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-09-20 02:18:09 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-09-20 02:18:09 +0300
commit6ed4ec3e0b1340f96b7c043ef51d1b33bbe85fde (patch)
treedc4d20fe6064752c0bd323187252c77e0a89144b
parent9868dae7fc0655bd7ce4a6887d4e6d487690eeed (diff)
Add latest changes from gitlab-org/gitlab@15-4-stable-eev15.4.0-rc42
-rw-r--r--.eslintrc.yml11
-rw-r--r--.gitignore2
-rw-r--r--.gitlab-ci.yml16
-rw-r--r--.gitlab/CODEOWNERS811
-rw-r--r--.gitlab/agents/review-apps/config.yaml1
-rw-r--r--.gitlab/ci/_skip.yml11
-rw-r--r--.gitlab/ci/build-images.gitlab-ci.yml19
-rw-r--r--.gitlab/ci/docs.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/frontend.gitlab-ci.yml46
-rw-r--r--.gitlab/ci/global.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/notify.gitlab-ci.yml20
-rw-r--r--.gitlab/ci/package-and-test/main.gitlab-ci.yml600
-rw-r--r--.gitlab/ci/package-and-test/rules.gitlab-ci.yml99
-rw-r--r--.gitlab/ci/package-and-test/variables.gitlab-ci.yml9
-rw-r--r--.gitlab/ci/qa.gitlab-ci.yml127
-rw-r--r--.gitlab/ci/rails.gitlab-ci.yml79
-rw-r--r--.gitlab/ci/review-apps/main.gitlab-ci.yml21
-rw-r--r--.gitlab/ci/review-apps/qa.gitlab-ci.yml129
-rw-r--r--.gitlab/ci/review-apps/rules.gitlab-ci.yml81
-rw-r--r--.gitlab/ci/review-apps/skip-qa.gitlab-ci.yml13
-rw-r--r--.gitlab/ci/review.gitlab-ci.yml60
-rw-r--r--.gitlab/ci/rules.gitlab-ci.yml292
-rw-r--r--.gitlab/ci/setup.gitlab-ci.yml76
-rw-r--r--.gitlab/ci/static-analysis.gitlab-ci.yml45
-rw-r--r--.gitlab/ci/vendored-gems.gitlab-ci.yml32
-rw-r--r--.gitlab/ci/workhorse.gitlab-ci.yml25
-rw-r--r--.gitlab/issue_templates/Audit Event Proposal.md6
-rw-r--r--.gitlab/issue_templates/Deprecations.md2
-rw-r--r--.gitlab/issue_templates/Doc_cleanup.md32
-rw-r--r--.gitlab/issue_templates/Service Ping reporting and monitoring.md6
-rw-r--r--.gitlab/merge_request_templates/Deprecations.md8
-rw-r--r--.gitlab/merge_request_templates/Quarantine End to End Test.md1
-rw-r--r--.gitlab/merge_request_templates/Removals.md5
-rw-r--r--.gitlab/merge_request_templates/Security Release.md4
-rw-r--r--.gitlab/secret-detection-ruleset.toml6
-rw-r--r--.markdownlint.yml3
-rw-r--r--.rubocop.yml22
-rw-r--r--.rubocop_todo/cop/user_admin.yml1
-rw-r--r--.rubocop_todo/database/multiple_databases.yml1
-rw-r--r--.rubocop_todo/gitlab/feature_available_usage.yml3
-rw-r--r--.rubocop_todo/gitlab/namespaced_class.yml5
-rw-r--r--.rubocop_todo/layout/argument_alignment.yml1
-rw-r--r--.rubocop_todo/layout/first_array_element_indentation.yml171
-rw-r--r--.rubocop_todo/layout/hash_alignment.yml383
-rw-r--r--.rubocop_todo/layout/line_length.yml14
-rw-r--r--.rubocop_todo/layout/space_in_lambda_literal.yml1
-rw-r--r--.rubocop_todo/layout/space_inside_block_braces.yml134
-rw-r--r--.rubocop_todo/migration/background_migration_base_class.yml1
-rw-r--r--.rubocop_todo/rails/application_controller.yml13
-rw-r--r--.rubocop_todo/rails/helper_instance_variable.yml2
-rw-r--r--.rubocop_todo/rails/index_with.yml1
-rw-r--r--.rubocop_todo/rails/squished_sql_heredocs.yml1
-rw-r--r--.rubocop_todo/rails/where_exists.yml1
-rw-r--r--.rubocop_todo/rspec/any_instance_of.yml2
-rw-r--r--.rubocop_todo/rspec/context_wording.yml5
-rw-r--r--.rubocop_todo/rspec/described_class.yml282
-rw-r--r--.rubocop_todo/rspec/empty_line_after_example_group.yml39
-rw-r--r--.rubocop_todo/rspec/expect_change.yml5
-rw-r--r--.rubocop_todo/rspec/expect_in_hook.yml10
-rw-r--r--.rubocop_todo/rspec/missing_example_group_argument.yml16
-rw-r--r--.rubocop_todo/rspec/predicate_matcher.yml1
-rw-r--r--.rubocop_todo/rspec/shared_examples.yml24
-rw-r--r--.rubocop_todo/rspec/verified_doubles.yml4
-rw-r--r--.rubocop_todo/style/bare_percent_literals.yml15
-rw-r--r--.rubocop_todo/style/class_and_module_children.yml3
-rw-r--r--.rubocop_todo/style/empty_else.yml2
-rw-r--r--.rubocop_todo/style/float_division.yml7
-rw-r--r--.rubocop_todo/style/format_string.yml1
-rw-r--r--.rubocop_todo/style/hash_as_last_array_item.yml1
-rw-r--r--.rubocop_todo/style/if_unless_modifier.yml1
-rw-r--r--.rubocop_todo/style/lambda.yml3
-rw-r--r--.rubocop_todo/style/next.yml10
-rw-r--r--.rubocop_todo/style/percent_literal_delimiters.yml1
-rw-r--r--.rubocop_todo/style/redundant_self.yml1
-rw-r--r--.rubocop_todo/style/string_concatenation.yml2
-rw-r--r--.rubocop_todo/style/symbol_proc.yml1
-rw-r--r--CHANGELOG.md59
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_ELASTICSEARCH_INDEXER_VERSION2
-rw-r--r--GITLAB_KAS_VERSION2
-rw-r--r--Gemfile71
-rw-r--r--Gemfile.checksum639
-rw-r--r--Gemfile.lock221
-rw-r--r--app/assets/javascripts/access_tokens/components/access_token_table_app.vue1
-rw-r--r--app/assets/javascripts/access_tokens/components/constants.js14
-rw-r--r--app/assets/javascripts/access_tokens/components/expires_at_field.vue29
-rw-r--r--app/assets/javascripts/access_tokens/components/new_access_token_app.vue10
-rw-r--r--app/assets/javascripts/access_tokens/index.js4
-rw-r--r--app/assets/javascripts/admin/application_settings/runner_token_expiration/components/expiration_interval_description.vue52
-rw-r--r--app/assets/javascripts/admin/application_settings/runner_token_expiration/components/expiration_intervals.vue123
-rw-r--r--app/assets/javascripts/admin/application_settings/runner_token_expiration/index.js32
-rw-r--r--app/assets/javascripts/admin/topics/components/merge_topics.vue141
-rw-r--r--app/assets/javascripts/admin/topics/components/topic_select.vue106
-rw-r--r--app/assets/javascripts/admin/topics/index.js32
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_form.vue2
-rw-r--r--app/assets/javascripts/analytics/shared/components/daterange.vue24
-rw-r--r--app/assets/javascripts/analytics/shared/constants.js3
-rw-r--r--app/assets/javascripts/analytics/shared/utils.js2
-rw-r--r--app/assets/javascripts/analytics/usage_trends/utils.js2
-rw-r--r--app/assets/javascripts/api.js1
-rw-r--r--app/assets/javascripts/api/harbor_registry.js49
-rw-r--r--app/assets/javascripts/api/integrations_api.js21
-rw-r--r--app/assets/javascripts/api/user_api.js6
-rw-r--r--app/assets/javascripts/autosave.js16
-rw-r--r--app/assets/javascripts/awards_handler.js2
-rw-r--r--app/assets/javascripts/batch_comments/components/preview_item.vue1
-rw-r--r--app/assets/javascripts/batch_comments/components/submit_dropdown.vue87
-rw-r--r--app/assets/javascripts/batch_comments/stores/modules/batch_comments/actions.js6
-rw-r--r--app/assets/javascripts/behaviors/copy_code.js1
-rw-r--r--app/assets/javascripts/behaviors/copy_to_clipboard.js8
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_math.js2
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js30
-rw-r--r--app/assets/javascripts/blob/3d_viewer/index.js7
-rw-r--r--app/assets/javascripts/blob/3d_viewer/mesh_object.js2
-rw-r--r--app/assets/javascripts/blob/notebook/notebook_viewer.vue6
-rw-r--r--app/assets/javascripts/blob/sketch/index.js41
-rw-r--r--app/assets/javascripts/blob/viewer/index.js1
-rw-r--r--app/assets/javascripts/blob_edit/blob_bundle.js3
-rw-r--r--app/assets/javascripts/boards/components/board_add_new_column_form.vue2
-rw-r--r--app/assets/javascripts/boards/components/board_blocked_icon.vue27
-rw-r--r--app/assets/javascripts/boards/components/board_card.vue19
-rw-r--r--app/assets/javascripts/boards/components/board_card_inner.vue47
-rw-r--r--app/assets/javascripts/boards/components/board_card_move_to_position.vue128
-rw-r--r--app/assets/javascripts/boards/components/board_column.vue4
-rw-r--r--app/assets/javascripts/boards/components/board_content.vue2
-rw-r--r--app/assets/javascripts/boards/components/board_content_sidebar.vue4
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue22
-rw-r--r--app/assets/javascripts/boards/components/board_list_header.vue16
-rw-r--r--app/assets/javascripts/boards/components/board_new_item.vue2
-rw-r--r--app/assets/javascripts/boards/components/issue_due_date.vue8
-rw-r--r--app/assets/javascripts/boards/components/issue_time_estimate.vue2
-rw-r--r--app/assets/javascripts/boards/components/item_count.vue4
-rw-r--r--app/assets/javascripts/boards/constants.js6
-rw-r--r--app/assets/javascripts/boards/filters/due_date_filters.js2
-rw-r--r--app/assets/javascripts/boards/graphql/board.fragment.graphql4
-rw-r--r--app/assets/javascripts/boards/graphql/board_blocking_epics.query.graphql17
-rw-r--r--app/assets/javascripts/boards/graphql/group_boards.query.graphql5
-rw-r--r--app/assets/javascripts/boards/graphql/group_recent_boards.query.graphql5
-rw-r--r--app/assets/javascripts/boards/graphql/project_boards.query.graphql5
-rw-r--r--app/assets/javascripts/boards/graphql/project_recent_boards.query.graphql5
-rw-r--r--app/assets/javascripts/boards/stores/actions.js28
-rw-r--r--app/assets/javascripts/boards/stores/mutation_types.js1
-rw-r--r--app/assets/javascripts/boards/stores/mutations.js41
-rw-r--r--app/assets/javascripts/boards/stores/state.js1
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue4
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_group_variables.vue4
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_project_variables.vue120
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue18
-rw-r--r--app/assets/javascripts/ci_variable_list/components/legacy_ci_variable_modal.vue2
-rw-r--r--app/assets/javascripts/ci_variable_list/constants.js10
-rw-r--r--app/assets/javascripts/ci_variable_list/graphql/mutations/client/add_project_environment.mutation.graphql3
-rw-r--r--app/assets/javascripts/ci_variable_list/graphql/mutations/project_add_variable.mutation.graphql30
-rw-r--r--app/assets/javascripts/ci_variable_list/graphql/mutations/project_delete_variable.mutation.graphql30
-rw-r--r--app/assets/javascripts/ci_variable_list/graphql/mutations/project_update_variable.mutation.graphql30
-rw-r--r--app/assets/javascripts/ci_variable_list/graphql/queries/project_environments.query.graphql11
-rw-r--r--app/assets/javascripts/ci_variable_list/graphql/queries/project_variables.query.graphql15
-rw-r--r--app/assets/javascripts/ci_variable_list/graphql/resolvers.js56
-rw-r--r--app/assets/javascripts/ci_variable_list/index.js9
-rw-r--r--app/assets/javascripts/clusters/agents/components/agent_integration_status_row.vue66
-rw-r--r--app/assets/javascripts/clusters/agents/components/integration_status.vue98
-rw-r--r--app/assets/javascripts/clusters/agents/components/show.vue19
-rw-r--r--app/assets/javascripts/clusters/agents/components/token_table.vue7
-rw-r--r--app/assets/javascripts/clusters/agents/constants.js22
-rw-r--r--app/assets/javascripts/clusters_list/clusters_util.js23
-rw-r--r--app/assets/javascripts/clusters_list/components/agents.vue34
-rw-r--r--app/assets/javascripts/code_navigation/utils/dom_utils.js1
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.vue2
-rw-r--r--app/assets/javascripts/confidential_merge_request/components/dropdown.vue2
-rw-r--r--app/assets/javascripts/content_editor/components/bubble_menus/bubble_menu.vue60
-rw-r--r--app/assets/javascripts/content_editor/components/bubble_menus/code_block.vue275
-rw-r--r--app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue276
-rw-r--r--app/assets/javascripts/content_editor/components/bubble_menus/formatting.vue122
-rw-r--r--app/assets/javascripts/content_editor/components/bubble_menus/formatting_bubble_menu.vue123
-rw-r--r--app/assets/javascripts/content_editor/components/bubble_menus/link.vue189
-rw-r--r--app/assets/javascripts/content_editor/components/bubble_menus/link_bubble_menu.vue198
-rw-r--r--app/assets/javascripts/content_editor/components/bubble_menus/media.vue288
-rw-r--r--app/assets/javascripts/content_editor/components/bubble_menus/media_bubble_menu.vue287
-rw-r--r--app/assets/javascripts/content_editor/components/content_editor.vue84
-rw-r--r--app/assets/javascripts/content_editor/components/content_editor_alert.vue17
-rw-r--r--app/assets/javascripts/content_editor/components/editor_state_observer.vue14
-rw-r--r--app/assets/javascripts/content_editor/components/loading_indicator.vue34
-rw-r--r--app/assets/javascripts/content_editor/components/toolbar_image_button.vue48
-rw-r--r--app/assets/javascripts/content_editor/components/toolbar_link_button.vue55
-rw-r--r--app/assets/javascripts/content_editor/components/toolbar_more_dropdown.vue13
-rw-r--r--app/assets/javascripts/content_editor/components/toolbar_table_button.vue24
-rw-r--r--app/assets/javascripts/content_editor/components/toolbar_text_style_dropdown.vue1
-rw-r--r--app/assets/javascripts/content_editor/components/top_toolbar.vue2
-rw-r--r--app/assets/javascripts/content_editor/constants/index.js8
-rw-r--r--app/assets/javascripts/content_editor/content_editor.stories.js2
-rw-r--r--app/assets/javascripts/content_editor/extensions/paste_markdown.js14
-rw-r--r--app/assets/javascripts/content_editor/extensions/sourcemap.js8
-rw-r--r--app/assets/javascripts/content_editor/services/content_editor.js41
-rw-r--r--app/assets/javascripts/content_editor/services/create_content_editor.js2
-rw-r--r--app/assets/javascripts/content_editor/services/markdown_serializer.js16
-rw-r--r--app/assets/javascripts/content_editor/services/remark_markdown_deserializer.js70
-rw-r--r--app/assets/javascripts/crm/constants.js4
-rw-r--r--app/assets/javascripts/crm/organizations/bundle.js18
-rw-r--r--app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations.query.graphql29
-rw-r--r--app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations_count_by_state.query.graphql11
-rw-r--r--app/assets/javascripts/crm/organizations/components/organization_form_wrapper.vue2
-rw-r--r--app/assets/javascripts/crm/organizations/components/organizations_root.vue225
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_table.vue4
-rw-r--r--app/assets/javascripts/cycle_analytics/components/total_time.vue2
-rw-r--r--app/assets/javascripts/cycle_analytics/components/value_stream_filters.vue5
-rw-r--r--app/assets/javascripts/cycle_analytics/store/getters.js2
-rw-r--r--app/assets/javascripts/deploy_keys/components/key.vue4
-rw-r--r--app/assets/javascripts/deploy_keys/components/keys_panel.vue4
-rw-r--r--app/assets/javascripts/deploy_keys/index.js4
-rw-r--r--app/assets/javascripts/deprecated_jquery_dropdown/render.js2
-rw-r--r--app/assets/javascripts/design_management/components/design_notes/design_discussion.vue5
-rw-r--r--app/assets/javascripts/design_management/components/design_notes/design_note.vue5
-rw-r--r--app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue40
-rw-r--r--app/assets/javascripts/design_management/components/list/item.vue2
-rw-r--r--app/assets/javascripts/design_management/components/upload/design_version_dropdown.vue4
-rw-r--r--app/assets/javascripts/design_management/pages/design/index.vue1
-rw-r--r--app/assets/javascripts/design_management/pages/index.vue12
-rw-r--r--app/assets/javascripts/diffs/components/app.vue19
-rw-r--r--app/assets/javascripts/diffs/components/compare_dropdown_layout.vue2
-rw-r--r--app/assets/javascripts/diffs/components/diff_code_quality.vue8
-rw-r--r--app/assets/javascripts/diffs/components/diff_content.vue4
-rw-r--r--app/assets/javascripts/diffs/components/diff_discussions.vue4
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue8
-rw-r--r--app/assets/javascripts/diffs/components/diff_gutter_avatars.vue8
-rw-r--r--app/assets/javascripts/diffs/components/diff_line.vue35
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_note_form.vue4
-rw-r--r--app/assets/javascripts/diffs/components/diff_row.vue6
-rw-r--r--app/assets/javascripts/diffs/components/diff_view.vue17
-rw-r--r--app/assets/javascripts/diffs/constants.js3
-rw-r--r--app/assets/javascripts/diffs/i18n.js2
-rw-r--r--app/assets/javascripts/diffs/index.js4
-rw-r--r--app/assets/javascripts/environments/components/deploy_board.vue4
-rw-r--r--app/assets/javascripts/environments/components/deployment.vue171
-rw-r--r--app/assets/javascripts/environments/components/new_environment_item.vue1
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_bundle.js4
-rw-r--r--app/assets/javascripts/environments/graphql/queries/deployment_details.query.graphql13
-rw-r--r--app/assets/javascripts/feature_flags/components/feature_flags.vue1
-rw-r--r--app/assets/javascripts/filterable_list.js1
-rw-r--r--app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue12
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_emoji.js1
-rw-r--r--app/assets/javascripts/filtered_search/droplab/drop_down.js4
-rw-r--r--app/assets/javascripts/filtered_search/droplab/hook_button.js1
-rw-r--r--app/assets/javascripts/filtered_search/droplab/hook_input.js1
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js5
-rw-r--r--app/assets/javascripts/filtered_search/visual_token_value.js2
-rw-r--r--app/assets/javascripts/flash.js2
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_list.vue2
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue9
-rw-r--r--app/assets/javascripts/google_cloud/databases/index.js11
-rw-r--r--app/assets/javascripts/google_cloud/databases/init_index.js11
-rw-r--r--app/assets/javascripts/google_cloud/databases/init_new.js11
-rw-r--r--app/assets/javascripts/google_cloud/databases/panel.vue38
-rw-r--r--app/assets/javascripts/google_tag_manager/index.js19
-rw-r--r--app/assets/javascripts/graphql_shared/fragments/issuable_timelogs.fragment.graphql1
-rw-r--r--app/assets/javascripts/graphql_shared/fragments/issue_time_tracking.fragment.graphql13
-rw-r--r--app/assets/javascripts/graphql_shared/fragments/merge_request_time_tracking.fragment.graphql13
-rw-r--r--app/assets/javascripts/graphql_shared/issuable_client.js93
-rw-r--r--app/assets/javascripts/graphql_shared/possible_types.json1
-rw-r--r--app/assets/javascripts/graphql_shared/queries/project_topics_search.query.graphql (renamed from app/assets/javascripts/projects/settings/topics/queries/project_topics_search.query.graphql)0
-rw-r--r--app/assets/javascripts/graphql_shared/queries/users_search.query.graphql14
-rw-r--r--app/assets/javascripts/groups/components/app.vue13
-rw-r--r--app/assets/javascripts/groups/components/group_item.vue22
-rw-r--r--app/assets/javascripts/groups/components/item_stats.vue8
-rw-r--r--app/assets/javascripts/groups/components/overview_tabs.vue103
-rw-r--r--app/assets/javascripts/groups/components/visibility_level_dropdown.vue48
-rw-r--r--app/assets/javascripts/groups/constants.js26
-rw-r--r--app/assets/javascripts/groups/index.js17
-rw-r--r--app/assets/javascripts/groups/init_overview_tabs.js78
-rw-r--r--app/assets/javascripts/groups/visibility_level.js24
-rw-r--r--app/assets/javascripts/header_search/constants.js25
-rw-r--r--app/assets/javascripts/header_search/store/actions.js29
-rw-r--r--app/assets/javascripts/header_search/store/getters.js18
-rw-r--r--app/assets/javascripts/header_search/store/mutations.js8
-rw-r--r--app/assets/javascripts/ide/components/editor_mode_dropdown.vue2
-rw-r--r--app/assets/javascripts/ide/components/ide_status_bar.vue4
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/index.vue4
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/modal.vue6
-rw-r--r--app/assets/javascripts/ide/index.js14
-rw-r--r--app/assets/javascripts/ide/init_gitlab_web_ide.js30
-rw-r--r--app/assets/javascripts/image_diff/helpers/badge_helper.js1
-rw-r--r--app/assets/javascripts/image_diff/helpers/comment_indicator_helper.js1
-rw-r--r--app/assets/javascripts/invite_members/components/invite_members_modal.vue31
-rw-r--r--app/assets/javascripts/invite_members/components/user_limit_notification.vue23
-rw-r--r--app/assets/javascripts/invite_members/constants.js11
-rw-r--r--app/assets/javascripts/invite_members/init_invite_members_modal.js9
-rw-r--r--app/assets/javascripts/issuable/components/issue_assignees.vue2
-rw-r--r--app/assets/javascripts/issuable/components/related_issuable_item.vue4
-rw-r--r--app/assets/javascripts/issuable/components/status_box.vue2
-rw-r--r--app/assets/javascripts/issuable/issuable_form.js71
-rw-r--r--app/assets/javascripts/issues/list/components/issues_list_app.vue52
-rw-r--r--app/assets/javascripts/issues/list/constants.js19
-rw-r--r--app/assets/javascripts/issues/related_merge_requests/components/related_merge_requests.vue4
-rw-r--r--app/assets/javascripts/issues/show/components/app.vue27
-rw-r--r--app/assets/javascripts/issues/show/components/delete_issue_modal.vue3
-rw-r--r--app/assets/javascripts/issues/show/components/description.vue10
-rw-r--r--app/assets/javascripts/issues/show/components/edit_actions.vue86
-rw-r--r--app/assets/javascripts/issues/show/components/edited.vue4
-rw-r--r--app/assets/javascripts/issues/show/components/fields/description.vue4
-rw-r--r--app/assets/javascripts/issues/show/components/form.vue17
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/constants.js11
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/create_timeline_event.vue31
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/edit_timeline_event.vue47
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/graphql/queries/edit_timeline_event.mutation.graphql13
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue242
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue94
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue66
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue27
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/utils.js11
-rw-r--r--app/assets/javascripts/issues/show/graphql.js2
-rw-r--r--app/assets/javascripts/issues/show/index.js2
-rw-r--r--app/assets/javascripts/issues/show/utils/update_description.js1
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/api.js64
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/components/app.vue6
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/components/compatibility_alert.vue15
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/components/sign_in_oauth_button.vue111
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/constants.js25
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/index.vue38
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/version_select_form.vue12
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/store/actions.js8
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/store/state.js8
-rw-r--r--app/assets/javascripts/jira_connect/subscriptions/utils.js41
-rw-r--r--app/assets/javascripts/jobs/components/empty_state.vue87
-rw-r--r--app/assets/javascripts/jobs/components/filtered_search/constants.js13
-rw-r--r--app/assets/javascripts/jobs/components/filtered_search/jobs_filtered_search.vue22
-rw-r--r--app/assets/javascripts/jobs/components/filtered_search/utils.js27
-rw-r--r--app/assets/javascripts/jobs/components/job/empty_state.vue99
-rw-r--r--app/assets/javascripts/jobs/components/job/environments_block.vue (renamed from app/assets/javascripts/jobs/components/environments_block.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/erased_block.vue (renamed from app/assets/javascripts/jobs/components/erased_block.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/job_app.vue328
-rw-r--r--app/assets/javascripts/jobs/components/job/job_log_controllers.vue (renamed from app/assets/javascripts/jobs/components/job_log_controllers.vue)0
-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.vue195
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/artifacts_block.vue (renamed from app/assets/javascripts/jobs/components/artifacts_block.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/commit_block.vue (renamed from app/assets/javascripts/jobs/components/commit_block.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/job_container_item.vue (renamed from app/assets/javascripts/jobs/components/job_container_item.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/job_retry_forward_deployment_modal.vue66
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/job_sidebar_retry_button.vue53
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/jobs_container.vue (renamed from app/assets/javascripts/jobs/components/jobs_container.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/legacy_sidebar_header.vue99
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/sidebar.vue149
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/sidebar_detail_row.vue (renamed from app/assets/javascripts/jobs/components/sidebar_detail_row.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/sidebar_header.vue102
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/sidebar_job_details_container.vue (renamed from app/assets/javascripts/jobs/components/sidebar_job_details_container.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/stages_dropdown.vue167
-rw-r--r--app/assets/javascripts/jobs/components/job/sidebar/trigger_block.vue (renamed from app/assets/javascripts/jobs/components/trigger_block.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/stuck_block.vue (renamed from app/assets/javascripts/jobs/components/stuck_block.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job/unmet_prerequisites_block.vue (renamed from app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue)0
-rw-r--r--app/assets/javascripts/jobs/components/job_app.vue328
-rw-r--r--app/assets/javascripts/jobs/components/job_retry_forward_deployment_modal.vue66
-rw-r--r--app/assets/javascripts/jobs/components/job_sidebar_retry_button.vue53
-rw-r--r--app/assets/javascripts/jobs/components/manual_variables_form.vue193
-rw-r--r--app/assets/javascripts/jobs/components/sidebar.vue213
-rw-r--r--app/assets/javascripts/jobs/components/stages_dropdown.vue167
-rw-r--r--app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql1
-rw-r--r--app/assets/javascripts/jobs/components/table/jobs_table_app.vue24
-rw-r--r--app/assets/javascripts/jobs/constants.js8
-rw-r--r--app/assets/javascripts/jobs/index.js2
-rw-r--r--app/assets/javascripts/labels/labels_select.js1
-rw-r--r--app/assets/javascripts/lib/dateformat.js60
-rw-r--r--app/assets/javascripts/lib/dompurify.js6
-rw-r--r--app/assets/javascripts/lib/gfm/constants.js10
-rw-r--r--app/assets/javascripts/lib/gfm/glfm_extensions/table_of_contents.js85
-rw-r--r--app/assets/javascripts/lib/gfm/index.js11
-rw-r--r--app/assets/javascripts/lib/gfm/mdast_to_hast_handlers/glfm_mdast_to_hast_handlers.js1
-rw-r--r--app/assets/javascripts/lib/mermaid.js1
-rw-r--r--app/assets/javascripts/lib/utils/datetime/date_calculation_utility.js26
-rw-r--r--app/assets/javascripts/lib/utils/datetime/date_format_utility.js2
-rw-r--r--app/assets/javascripts/lib/utils/datetime_range.js2
-rw-r--r--app/assets/javascripts/lib/utils/text_markdown.js10
-rw-r--r--app/assets/javascripts/lib/utils/text_utility.js1
-rw-r--r--app/assets/javascripts/linked_resources/index.js2
-rw-r--r--app/assets/javascripts/locale/sprintf.js2
-rw-r--r--app/assets/javascripts/members/components/modals/remove_member_modal.vue3
-rw-r--r--app/assets/javascripts/members/constants.js5
-rw-r--r--app/assets/javascripts/members/utils.js16
-rw-r--r--app/assets/javascripts/merge_request_tabs.js1
-rw-r--r--app/assets/javascripts/merge_requests/components/sticky_header.vue180
-rw-r--r--app/assets/javascripts/milestones/components/milestone_combobox.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue6
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard_header.vue3
-rw-r--r--app/assets/javascripts/monitoring/components/dashboards_dropdown.vue4
-rw-r--r--app/assets/javascripts/monitoring/components/refresh_button.vue4
-rw-r--r--app/assets/javascripts/monitoring/format_date.js2
-rw-r--r--app/assets/javascripts/mr_notes/index.js6
-rw-r--r--app/assets/javascripts/mr_notes/init_notes.js4
-rw-r--r--app/assets/javascripts/nav/components/top_nav_app.vue17
-rw-r--r--app/assets/javascripts/nav/components/top_nav_menu_sections.vue29
-rw-r--r--app/assets/javascripts/notebook/cells/code.vue13
-rw-r--r--app/assets/javascripts/notebook/cells/code/index.vue25
-rw-r--r--app/assets/javascripts/notebook/cells/markdown.vue2
-rw-r--r--app/assets/javascripts/notebook/cells/output/image.vue2
-rw-r--r--app/assets/javascripts/notebook/cells/output/index.vue6
-rw-r--r--app/assets/javascripts/notebook/index.vue6
-rw-r--r--app/assets/javascripts/notebook/lib/highlight.js5
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue18
-rw-r--r--app/assets/javascripts/notes/components/diff_discussion_header.vue8
-rw-r--r--app/assets/javascripts/notes/components/discussion_actions.vue1
-rw-r--r--app/assets/javascripts/notes/components/discussion_counter.vue93
-rw-r--r--app/assets/javascripts/notes/components/discussion_filter.vue109
-rw-r--r--app/assets/javascripts/notes/components/discussion_navigator.vue11
-rw-r--r--app/assets/javascripts/notes/components/note_actions.vue15
-rw-r--r--app/assets/javascripts/notes/components/note_actions/timeline_event_button.vue49
-rw-r--r--app/assets/javascripts/notes/components/note_body.vue18
-rw-r--r--app/assets/javascripts/notes/components/note_edited_text.vue4
-rw-r--r--app/assets/javascripts/notes/components/note_form.vue12
-rw-r--r--app/assets/javascripts/notes/components/note_header.vue26
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue25
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue18
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue36
-rw-r--r--app/assets/javascripts/notes/components/sidebar_subscription.vue2
-rw-r--r--app/assets/javascripts/notes/components/sort_discussion.vue76
-rw-r--r--app/assets/javascripts/notes/components/timeline_toggle.vue1
-rw-r--r--app/assets/javascripts/notes/constants.js2
-rw-r--r--app/assets/javascripts/notes/graphql/promote_timeline_event.mutation.graphql8
-rw-r--r--app/assets/javascripts/notes/index.js8
-rw-r--r--app/assets/javascripts/notes/mixins/discussion_navigation.js107
-rw-r--r--app/assets/javascripts/notes/sort_discussions.js17
-rw-r--r--app/assets/javascripts/notes/stores/actions.js55
-rw-r--r--app/assets/javascripts/notes/stores/getters.js7
-rw-r--r--app/assets/javascripts/notes/stores/modules/index.js1
-rw-r--r--app/assets/javascripts/notes/stores/mutation_types.js3
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js3
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/components/details/artifacts_list.vue95
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/components/details/artifacts_list_row.vue133
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/components/details/details_header.vue47
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/components/harbor_registry_breadcrumb.vue68
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/components/list/harbor_list_header.vue12
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/components/list/harbor_list_row.vue32
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/components/tags/tags_header.vue54
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/components/tags/tags_list.vue82
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/components/tags/tags_list_row.vue74
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/constants/common.js17
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/constants/details.js46
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/constants/list.js14
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/index.js43
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/mock_api.js200
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/pages/details.vue156
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/pages/harbor_tags.vue103
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/pages/list.vue169
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/router.js13
-rw-r--r--app/assets/javascripts/packages_and_registries/harbor_registry/utils.js84
-rw-r--r--app/assets/javascripts/packages_and_registries/infrastructure_registry/details/components/app.vue2
-rw-r--r--app/assets/javascripts/packages_and_registries/infrastructure_registry/details/components/package_files.vue4
-rw-r--r--app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list.vue2
-rw-r--r--app/assets/javascripts/packages_and_registries/infrastructure_registry/shared/package_list_row.vue2
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue19
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue4
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue2
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue2
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/constants.js4
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/pages/details.vue2
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/group/components/duplicates_settings.vue103
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/group/components/exceptions_input.vue79
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/group/components/generic_settings.vue26
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/group/components/maven_settings.vue26
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/group/components/packages_settings.vue129
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/group/components/settings_titles.vue26
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/group/constants.js7
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/project/components/cleanup_image_tags.vue112
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/project/components/container_expiration_policy.vue70
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/project/components/container_expiration_policy_form.vue32
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/project/components/registry_settings_app.vue39
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/project/constants.js7
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/project/registry_settings_bundle.js2
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/project/registry_settings_cleanup_tags_bundle.js41
-rw-r--r--app/assets/javascripts/packages_and_registries/shared/components/persisted_search.vue7
-rw-r--r--app/assets/javascripts/packages_and_registries/shared/components/settings_block.vue6
-rw-r--r--app/assets/javascripts/packages_and_registries/shared/constants/package_registry.js4
-rw-r--r--app/assets/javascripts/pages/admin/application_settings/ci_cd/index.js3
-rw-r--r--app/assets/javascripts/pages/admin/application_settings/payload_previewer.js2
-rw-r--r--app/assets/javascripts/pages/admin/jobs/index/index.js4
-rw-r--r--app/assets/javascripts/pages/admin/runners/edit/index.js4
-rw-r--r--app/assets/javascripts/pages/admin/topics/edit/index.js2
-rw-r--r--app/assets/javascripts/pages/admin/topics/index.js3
-rw-r--r--app/assets/javascripts/pages/dashboard/todos/index/todos.js6
-rw-r--r--app/assets/javascripts/pages/groups/details/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/runners/edit/index.js3
-rw-r--r--app/assets/javascripts/pages/groups/settings/repository/create_deploy_token/index.js6
-rw-r--r--app/assets/javascripts/pages/groups/show/index.js2
-rw-r--r--app/assets/javascripts/pages/profiles/keys/index.js3
-rw-r--r--app/assets/javascripts/pages/profiles/show/emoji_menu.js19
-rw-r--r--app/assets/javascripts/pages/profiles/show/index.js96
-rw-r--r--app/assets/javascripts/pages/profiles/two_factor_auths/index.js5
-rw-r--r--app/assets/javascripts/pages/projects/blob/show/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue112
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/components/project_namespace.vue136
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/index.js7
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/queries/search_forkable_namespaces.query.graphql13
-rw-r--r--app/assets/javascripts/pages/projects/google_cloud/databases/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/google_cloud/databases/index/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/google_cloud/databases/new/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue2
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/edit/index.js69
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/edit/update_form.js23
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js8
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/show/index.js30
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue2
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js4
-rw-r--r--app/assets/javascripts/pages/projects/project.js2
-rw-r--r--app/assets/javascripts/pages/projects/settings/merge_requests/index.js10
-rw-r--r--app/assets/javascripts/pages/projects/settings/packages_and_registries/cleanup_tags/index.js5
-rw-r--r--app/assets/javascripts/pages/projects/settings/repository/create_deploy_token/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue189
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/constants.js17
-rw-r--r--app/assets/javascripts/pages/shared/wikis/components/wiki_content.vue2
-rw-r--r--app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue81
-rw-r--r--app/assets/javascripts/pages/users/activity_calendar.js2
-rw-r--r--app/assets/javascripts/pages/users/user_overview_block.js1
-rw-r--r--app/assets/javascripts/pdf/index.vue4
-rw-r--r--app/assets/javascripts/persistent_user_callout.js4
-rw-r--r--app/assets/javascripts/persistent_user_callouts.js3
-rw-r--r--app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue2
-rw-r--r--app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue31
-rw-r--r--app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue2
-rw-r--r--app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue16
-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.vue2
-rw-r--r--app/assets/javascripts/pipeline_new/index.js75
-rw-r--r--app/assets/javascripts/pipeline_wizard/components/editor.vue12
-rw-r--r--app/assets/javascripts/pipeline_wizard/components/wrapper.vue63
-rw-r--r--app/assets/javascripts/pipeline_wizard/pipeline_wizard.vue4
-rw-r--r--app/assets/javascripts/pipeline_wizard/templates/pages.yml1
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue1
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue39
-rw-r--r--app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/graph/stage_column_component.vue12
-rw-r--r--app/assets/javascripts/pipelines/components/header_component.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/jobs_shared/job_name_component.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/performance_insights_modal.vue171
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_graph/stage_name.vue12
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_mini_graph/accessors/linked_pipelines_accessors.js14
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_mini_graph/job_item.vue190
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_mini_graph/linked_pipelines_mini_list.vue132
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue103
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stage.vue186
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stages.vue55
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/job_item.vue190
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue54
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipeline_multi_actions.vue7
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue9
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue182
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stop_modal.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue33
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue6
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines_filtered_search.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines_manual_actions.vue8
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines_status_badge.vue10
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue42
-rw-r--r--app/assets/javascripts/pipelines/constants.js6
-rw-r--r--app/assets/javascripts/pipelines/graphql/queries/get_performance_insights.query.graphql28
-rw-r--r--app/assets/javascripts/pipelines/graphql/queries/get_pipeline_jobs.query.graphql1
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_header.js4
-rw-r--r--app/assets/javascripts/pipelines/pipeline_tabs.js9
-rw-r--r--app/assets/javascripts/pipelines/utils.js21
-rw-r--r--app/assets/javascripts/profile/account/index.js4
-rw-r--r--app/assets/javascripts/profile/profile.js24
-rw-r--r--app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue36
-rw-r--r--app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue2
-rw-r--r--app/assets/javascripts/projects/project_visibility.js11
-rw-r--r--app/assets/javascripts/projects/settings/repository/branch_rules/app.vue52
-rw-r--r--app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue61
-rw-r--r--app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql10
-rw-r--r--app/assets/javascripts/projects/settings/repository/branch_rules/mount_branch_rules.js17
-rw-r--r--app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue2
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue32
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue64
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/components/service_desk_template_dropdown.vue2
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/index.js2
-rw-r--r--app/assets/javascripts/projects/star.js3
-rw-r--r--app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue4
-rw-r--r--app/assets/javascripts/related_issues/components/related_issuable_input.vue4
-rw-r--r--app/assets/javascripts/related_issues/components/related_issues_block.vue17
-rw-r--r--app/assets/javascripts/related_issues/components/related_issues_root.vue2
-rw-r--r--app/assets/javascripts/related_issues/constants.js9
-rw-r--r--app/assets/javascripts/releases/components/evidence_block.vue2
-rw-r--r--app/assets/javascripts/releases/stores/modules/edit_new/actions.js50
-rw-r--r--app/assets/javascripts/releases/stores/modules/edit_new/getters.js5
-rw-r--r--app/assets/javascripts/releases/stores/modules/edit_new/mutations.js3
-rw-r--r--app/assets/javascripts/releases/stores/modules/edit_new/state.js1
-rw-r--r--app/assets/javascripts/repository/components/blob_content_viewer.vue28
-rw-r--r--app/assets/javascripts/repository/components/blob_controls.vue2
-rw-r--r--app/assets/javascripts/repository/components/breadcrumbs.vue8
-rw-r--r--app/assets/javascripts/repository/components/last_commit.vue3
-rw-r--r--app/assets/javascripts/repository/components/table/index.vue5
-rw-r--r--app/assets/javascripts/repository/components/table/row.vue4
-rw-r--r--app/assets/javascripts/repository/log_tree.js3
-rw-r--r--app/assets/javascripts/repository/queries/blob_info.query.graphql66
-rw-r--r--app/assets/javascripts/repository/queries/project_info.query.graphql14
-rw-r--r--app/assets/javascripts/repository/utils/commit.js4
-rw-r--r--app/assets/javascripts/rest_api.js1
-rw-r--r--app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue73
-rw-r--r--app/assets/javascripts/runner/admin_runner_edit/index.js33
-rw-r--r--app/assets/javascripts/runner/admin_runners/admin_runners_app.vue4
-rw-r--r--app/assets/javascripts/runner/components/cells/runner_stacked_summary_cell.vue112
-rw-r--r--app/assets/javascripts/runner/components/cells/runner_status_cell.vue3
-rw-r--r--app/assets/javascripts/runner/components/cells/runner_summary_cell.vue71
-rw-r--r--app/assets/javascripts/runner/components/cells/runner_summary_field.vue33
-rw-r--r--app/assets/javascripts/runner/components/runner_detail.vue9
-rw-r--r--app/assets/javascripts/runner/components/runner_details.vue46
-rw-r--r--app/assets/javascripts/runner/components/runner_header.vue46
-rw-r--r--app/assets/javascripts/runner/components/runner_list.vue49
-rw-r--r--app/assets/javascripts/runner/components/runner_name.vue4
-rw-r--r--app/assets/javascripts/runner/components/runner_paused_badge.vue12
-rw-r--r--app/assets/javascripts/runner/components/runner_projects.vue41
-rw-r--r--app/assets/javascripts/runner/components/runner_stacked_layout_banner.vue58
-rw-r--r--app/assets/javascripts/runner/components/runner_status_badge.vue26
-rw-r--r--app/assets/javascripts/runner/components/runner_tags.vue2
-rw-r--r--app/assets/javascripts/runner/components/runner_type_badge.vue21
-rw-r--r--app/assets/javascripts/runner/components/search_tokens/paused_token_config.js6
-rw-r--r--app/assets/javascripts/runner/components/search_tokens/status_token_config.js14
-rw-r--r--app/assets/javascripts/runner/components/stat/runner_stats.vue22
-rw-r--r--app/assets/javascripts/runner/constants.js16
-rw-r--r--app/assets/javascripts/runner/graphql/list/list_item_shared.fragment.graphql1
-rw-r--r--app/assets/javascripts/runner/graphql/show/runner_details_shared.fragment.graphql1
-rw-r--r--app/assets/javascripts/runner/graphql/show/runner_projects.query.graphql3
-rw-r--r--app/assets/javascripts/runner/group_runner_show/index.js3
-rw-r--r--app/assets/javascripts/runner/group_runners/group_runners_app.vue8
-rw-r--r--app/assets/javascripts/runner/runner_edit/index.js33
-rw-r--r--app/assets/javascripts/runner/runner_edit/runner_edit_app.vue73
-rw-r--r--app/assets/javascripts/runner/utils.js11
-rw-r--r--app/assets/javascripts/search/index.js4
-rw-r--r--app/assets/javascripts/search/topbar/components/searchable_dropdown.vue4
-rw-r--r--app/assets/javascripts/search/topbar/components/searchable_dropdown_item.vue4
-rw-r--r--app/assets/javascripts/search/under_topbar/index.js31
-rw-r--r--app/assets/javascripts/security_configuration/components/constants.js2
-rw-r--r--app/assets/javascripts/set_status_modal/constants.js14
-rw-r--r--app/assets/javascripts/set_status_modal/set_status_form.vue231
-rw-r--r--app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue219
-rw-r--r--app/assets/javascripts/set_status_modal/user_profile_set_status_wrapper.vue100
-rw-r--r--app/assets/javascripts/set_status_modal/utils.js5
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue5
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue7
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/copy_email_to_clipboard.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/incidents/escalation_status.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/lock/edit_form.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/participants/participants.vue8
-rw-r--r--app/assets/javascripts/sidebar/components/participants/sidebar_participants_widget.vue5
-rw-r--r--app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue80
-rw-r--r--app/assets/javascripts/sidebar/components/sidebar_editable_item.vue18
-rw-r--r--app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue24
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/graphql/cache_update.js20
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/graphql/mutations/delete_timelog.mutation.graphql12
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/report.vue9
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue33
-rw-r--r--app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue10
-rw-r--r--app/assets/javascripts/sidebar/constants.js20
-rw-r--r--app/assets/javascripts/sidebar/graphql.js29
-rw-r--r--app/assets/javascripts/sidebar/mount_milestone_sidebar.js6
-rw-r--r--app/assets/javascripts/sidebar/mount_sidebar.js11
-rw-r--r--app/assets/javascripts/sidebar/queries/issue_time_tracking.query.graphql6
-rw-r--r--app/assets/javascripts/sidebar/queries/merge_request_time_tracking.query.graphql6
-rw-r--r--app/assets/javascripts/snippets/components/show.vue4
-rw-r--r--app/assets/javascripts/snippets/constants.js20
-rw-r--r--app/assets/javascripts/snippets/index.js8
-rw-r--r--app/assets/javascripts/snippets/utils/blob.js4
-rw-r--r--app/assets/javascripts/surveys/merge_request_experience/app.vue157
-rw-r--r--app/assets/javascripts/token_access/components/token_access.vue38
-rw-r--r--app/assets/javascripts/user_popovers.js1
-rw-r--r--app/assets/javascripts/validators/input_validator.js2
-rw-r--r--app/assets/javascripts/visibility_level/constants.js24
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/action_buttons.vue8
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/added_commit_message.vue12
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_info.vue8
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue24
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/extensions/child_content.vue5
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/extensions/status_icon.vue30
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_icon.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue30
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue30
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/state_container.vue71
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/merge_checks_failed.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue26
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue8
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue23
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue25
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue56
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue20
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue8
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue14
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue8
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue1
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/sha_mismatch.vue2
-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.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/app.vue7
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/widget.vue115
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_section.vue35
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/constants.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/extensions/test_report/index.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mixins/mr_widget_pipeline.js10
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue57
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql1
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js7
-rw-r--r--app/assets/javascripts/vue_shared/components/actions_button.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/ci_badge_link.vue20
-rw-r--r--app/assets/javascripts/vue_shared/components/code_block.stories.js18
-rw-r--r--app/assets/javascripts/vue_shared/components/code_block.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/code_block_highlighted.stories.js18
-rw-r--r--app/assets/javascripts/vue_shared/components/code_block_highlighted.vue72
-rw-r--r--app/assets/javascripts/vue_shared/components/color_select_dropdown/dropdown_contents_color_view.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.stories.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_lib.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.stories.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.stories.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/header.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/suggestions.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/toolbar.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/system_note.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/pagination_bar/pagination_bar.stories.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/project_avatar.stories.js5
-rw-r--r--app/assets/javascripts/vue_shared/components/project_selector/project_list_item.stories.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/registry/persisted_dropdown_selection.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/runner_instructions/runner_instructions_modal.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/settings/settings_block.stories.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/constants.js1
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/plugins/wrap_comments.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/split_button.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/timezone_dropdown.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.stories.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/upload_dropzone/upload_dropzone.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image_new.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/user_deletion_obstacles/user_deletion_obstacles_list.stories.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/user_popover/constants.js13
-rw-r--r--app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue116
-rw-r--r--app/assets/javascripts/vue_shared/components/user_select/user_select.vue2
-rw-r--r--app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue6
-rw-r--r--app/assets/javascripts/vue_shared/new_namespace/components/legacy_container.vue1
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/graphql/queries/security_report_merge_request_download_paths.query.graphql1
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/graphql/queries/security_report_pipeline_download_paths.query.graphql1
-rw-r--r--app/assets/javascripts/webpack_non_compiled_placeholder.js1
-rw-r--r--app/assets/javascripts/work_items/components/item_title.vue7
-rw-r--r--app/assets/javascripts/work_items/components/work_item_actions.vue30
-rw-r--r--app/assets/javascripts/work_items/components/work_item_assignees.vue84
-rw-r--r--app/assets/javascripts/work_items/components/work_item_description.vue15
-rw-r--r--app/assets/javascripts/work_items/components/work_item_detail.vue86
-rw-r--r--app/assets/javascripts/work_items/components/work_item_due_date.vue257
-rw-r--r--app/assets/javascripts/work_items/components/work_item_information.vue14
-rw-r--r--app/assets/javascripts/work_items/components/work_item_labels.vue9
-rw-r--r--app/assets/javascripts/work_items/components/work_item_links/index.js9
-rw-r--r--app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue109
-rw-r--r--app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue121
-rw-r--r--app/assets/javascripts/work_items/components/work_item_state.vue7
-rw-r--r--app/assets/javascripts/work_items/components/work_item_title.vue9
-rw-r--r--app/assets/javascripts/work_items/components/work_item_type_icon.vue20
-rw-r--r--app/assets/javascripts/work_items/components/work_item_weight.vue162
-rw-r--r--app/assets/javascripts/work_items/constants.js30
-rw-r--r--app/assets/javascripts/work_items/graphql/create_work_item.mutation.graphql2
-rw-r--r--app/assets/javascripts/work_items/graphql/create_work_item_from_task.mutation.graphql2
-rw-r--r--app/assets/javascripts/work_items/graphql/local_update_work_item.mutation.graphql2
-rw-r--r--app/assets/javascripts/work_items/graphql/provider.js77
-rw-r--r--app/assets/javascripts/work_items/graphql/update_work_item.mutation.graphql2
-rw-r--r--app/assets/javascripts/work_items/graphql/update_work_item_task.mutation.graphql2
-rw-r--r--app/assets/javascripts/work_items/graphql/update_work_item_widgets.mutation.graphql10
-rw-r--r--app/assets/javascripts/work_items/graphql/work_item.fragment.graphql37
-rw-r--r--app/assets/javascripts/work_items/graphql/work_item.query.graphql2
-rw-r--r--app/assets/javascripts/work_items/graphql/work_item_dates.subscription.graphql13
-rw-r--r--app/assets/javascripts/work_items/graphql/work_item_links.query.graphql4
-rw-r--r--app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql36
-rw-r--r--app/assets/javascripts/work_items/index.js4
-rw-r--r--app/assets/javascripts/work_items/pages/create_work_item.vue21
-rw-r--r--app/assets/javascripts/work_items/pages/work_item_root.vue17
-rw-r--r--app/assets/stylesheets/_page_specific_files.scss2
-rw-r--r--app/assets/stylesheets/components/upload_dropzone/upload_dropzone.scss8
-rw-r--r--app/assets/stylesheets/framework/contextual_sidebar.scss4
-rw-r--r--app/assets/stylesheets/framework/diffs.scss31
-rw-r--r--app/assets/stylesheets/framework/files.scss9
-rw-r--r--app/assets/stylesheets/framework/header.scss14
-rw-r--r--app/assets/stylesheets/framework/highlight.scss3
-rw-r--r--app/assets/stylesheets/framework/layout.scss10
-rw-r--r--app/assets/stylesheets/framework/markdown_area.scss1
-rw-r--r--app/assets/stylesheets/framework/secondary_navigation_elements.scss10
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss2
-rw-r--r--app/assets/stylesheets/framework/typography.scss7
-rw-r--r--app/assets/stylesheets/framework/variables.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/boards.scss249
-rw-r--r--app/assets/stylesheets/page_bundles/editor.scss202
-rw-r--r--app/assets/stylesheets/page_bundles/group.scss23
-rw-r--r--app/assets/stylesheets/page_bundles/issues_show.scss69
-rw-r--r--app/assets/stylesheets/page_bundles/merge_requests.scss87
-rw-r--r--app/assets/stylesheets/page_bundles/pipeline_schedules.scss8
-rw-r--r--app/assets/stylesheets/page_bundles/profiles/preferences.scss83
-rw-r--r--app/assets/stylesheets/page_bundles/reports.scss4
-rw-r--r--app/assets/stylesheets/page_bundles/todos.scss32
-rw-r--r--app/assets/stylesheets/page_bundles/work_items.scss36
-rw-r--r--app/assets/stylesheets/pages/commits.scss6
-rw-r--r--app/assets/stylesheets/pages/editor.scss204
-rw-r--r--app/assets/stylesheets/pages/issuable.scss55
-rw-r--r--app/assets/stylesheets/pages/issues.scss18
-rw-r--r--app/assets/stylesheets/pages/login.scss20
-rw-r--r--app/assets/stylesheets/pages/note_form.scss6
-rw-r--r--app/assets/stylesheets/pages/notes.scss15
-rw-r--r--app/assets/stylesheets/pages/profiles/preferences.scss81
-rw-r--r--app/assets/stylesheets/pages/search.scss12
-rw-r--r--app/assets/stylesheets/pages/settings.scss6
-rw-r--r--app/assets/stylesheets/startup/startup-dark.scss39
-rw-r--r--app/assets/stylesheets/startup/startup-general.scss26
-rw-r--r--app/assets/stylesheets/startup/startup-signin.scss458
-rw-r--r--app/assets/stylesheets/themes/_dark.scss2
-rw-r--r--app/assets/stylesheets/themes/dark_mode_overrides.scss22
-rw-r--r--app/assets/stylesheets/themes/theme_blue.scss1
-rw-r--r--app/assets/stylesheets/themes/theme_gray.scss1
-rw-r--r--app/assets/stylesheets/themes/theme_green.scss1
-rw-r--r--app/assets/stylesheets/themes/theme_helper.scss66
-rw-r--r--app/assets/stylesheets/themes/theme_indigo.scss1
-rw-r--r--app/assets/stylesheets/themes/theme_light_blue.scss1
-rw-r--r--app/assets/stylesheets/themes/theme_light_gray.scss1
-rw-r--r--app/assets/stylesheets/themes/theme_light_green.scss1
-rw-r--r--app/assets/stylesheets/themes/theme_light_indigo.scss1
-rw-r--r--app/assets/stylesheets/themes/theme_light_red.scss1
-rw-r--r--app/assets/stylesheets/themes/theme_red.scss1
-rw-r--r--app/assets/stylesheets/utilities.scss10
-rw-r--r--app/components/layouts/horizontal_section_component.haml10
-rw-r--r--app/components/layouts/horizontal_section_component.rb22
-rw-r--r--app/components/pajamas/badge_component.html.haml6
-rw-r--r--app/components/pajamas/badge_component.rb72
-rw-r--r--app/components/pajamas/button_component.rb2
-rw-r--r--app/controllers/abuse_reports_controller.rb5
-rw-r--r--app/controllers/acme_challenges_controller.rb2
-rw-r--r--app/controllers/admin/application_settings_controller.rb28
-rw-r--r--app/controllers/admin/applications_controller.rb11
-rw-r--r--app/controllers/admin/broadcast_messages_controller.rb19
-rw-r--r--app/controllers/admin/cohorts_controller.rb17
-rw-r--r--app/controllers/admin/dashboard_controller.rb9
-rw-r--r--app/controllers/admin/hook_logs_controller.rb37
-rw-r--r--app/controllers/admin/hooks_controller.rb4
-rw-r--r--app/controllers/admin/plan_limits_controller.rb39
-rw-r--r--app/controllers/admin/runners_controller.rb10
-rw-r--r--app/controllers/admin/spam_logs_controller.rb2
-rw-r--r--app/controllers/admin/topics_controller.rb12
-rw-r--r--app/controllers/admin/users_controller.rb2
-rw-r--r--app/controllers/boards/issues_controller.rb8
-rw-r--r--app/controllers/chaos_controller.rb2
-rw-r--r--app/controllers/concerns/accepts_pending_invitations.rb7
-rw-r--r--app/controllers/concerns/dependency_proxy/group_access.rb2
-rw-r--r--app/controllers/concerns/harbor/access.rb2
-rw-r--r--app/controllers/concerns/integrations/hooks_execution.rb95
-rw-r--r--app/controllers/concerns/issuable_actions.rb5
-rw-r--r--app/controllers/concerns/membership_actions.rb6
-rw-r--r--app/controllers/concerns/packages_access.rb2
-rw-r--r--app/controllers/concerns/product_analytics_tracking.rb12
-rw-r--r--app/controllers/concerns/verifies_with_email.rb59
-rw-r--r--app/controllers/concerns/web_hooks/hook_actions.rb85
-rw-r--r--app/controllers/concerns/web_hooks/hook_execution_notice.rb20
-rw-r--r--app/controllers/concerns/web_hooks/hook_log_actions.rb44
-rw-r--r--app/controllers/groups/observability_controller.rb45
-rw-r--r--app/controllers/groups/runners_controller.rb7
-rw-r--r--app/controllers/groups/settings/applications_controller.rb12
-rw-r--r--app/controllers/groups/settings/repository_controller.rb16
-rw-r--r--app/controllers/groups_controller.rb7
-rw-r--r--app/controllers/health_controller.rb10
-rw-r--r--app/controllers/help_controller.rb23
-rw-r--r--app/controllers/ide_controller.rb1
-rw-r--r--app/controllers/import/github_controller.rb2
-rw-r--r--app/controllers/jira_connect/oauth_callbacks_controller.rb2
-rw-r--r--app/controllers/jira_connect/subscriptions_controller.rb11
-rw-r--r--app/controllers/jwt_controller.rb6
-rw-r--r--app/controllers/metrics_controller.rb2
-rw-r--r--app/controllers/oauth/applications_controller.rb11
-rw-r--r--app/controllers/profiles/personal_access_tokens_controller.rb2
-rw-r--r--app/controllers/profiles_controller.rb2
-rw-r--r--app/controllers/projects/blame_controller.rb5
-rw-r--r--app/controllers/projects/cycle_analytics_controller.rb16
-rw-r--r--app/controllers/projects/environments_controller.rb34
-rw-r--r--app/controllers/projects/google_cloud/base_controller.rb28
-rw-r--r--app/controllers/projects/google_cloud/configuration_controller.rb2
-rw-r--r--app/controllers/projects/google_cloud/databases_controller.rb129
-rw-r--r--app/controllers/projects/google_cloud/deployments_controller.rb12
-rw-r--r--app/controllers/projects/google_cloud/gcp_regions_controller.rb6
-rw-r--r--app/controllers/projects/google_cloud/revoke_oauth_controller.rb4
-rw-r--r--app/controllers/projects/google_cloud/service_accounts_controller.rb14
-rw-r--r--app/controllers/projects/graphs_controller.rb16
-rw-r--r--app/controllers/projects/hook_logs_controller.rb27
-rw-r--r--app/controllers/projects/hooks_controller.rb4
-rw-r--r--app/controllers/projects/incidents_controller.rb3
-rw-r--r--app/controllers/projects/integrations/shimos_controller.rb2
-rw-r--r--app/controllers/projects/issues_controller.rb26
-rw-r--r--app/controllers/projects/jobs_controller.rb9
-rw-r--r--app/controllers/projects/merge_requests/diffs_controller.rb17
-rw-r--r--app/controllers/projects/merge_requests/drafts_controller.rb30
-rw-r--r--app/controllers/projects/merge_requests_controller.rb24
-rw-r--r--app/controllers/projects/packages/package_files_controller.rb1
-rw-r--r--app/controllers/projects/pipelines/tests_controller.rb3
-rw-r--r--app/controllers/projects/pipelines_controller.rb25
-rw-r--r--app/controllers/projects/runners_controller.rb6
-rw-r--r--app/controllers/projects/settings/integration_hook_logs_controller.rb10
-rw-r--r--app/controllers/projects/settings/integrations_controller.rb13
-rw-r--r--app/controllers/projects/settings/merge_requests_controller.rb67
-rw-r--r--app/controllers/projects/settings/repository_controller.rb5
-rw-r--r--app/controllers/projects/uploads_controller.rb10
-rw-r--r--app/controllers/projects_controller.rb25
-rw-r--r--app/controllers/registrations_controller.rb22
-rw-r--r--app/controllers/repositories/git_http_client_controller.rb6
-rw-r--r--app/controllers/repositories/git_http_controller.rb2
-rw-r--r--app/controllers/search_controller.rb18
-rw-r--r--app/experiments/combined_registration_experiment.rb16
-rw-r--r--app/finders/context_commits_finder.rb11
-rw-r--r--app/finders/crm/organizations_finder.rb16
-rw-r--r--app/finders/database/batched_background_migrations_finder.rb27
-rw-r--r--app/finders/deployments_finder.rb47
-rw-r--r--app/finders/environments/environments_finder.rb7
-rw-r--r--app/finders/group_members_finder.rb2
-rw-r--r--app/finders/groups/accepting_group_transfers_finder.rb69
-rw-r--r--app/finders/groups/accepting_project_transfers_finder.rb4
-rw-r--r--app/finders/groups/base.rb17
-rw-r--r--app/finders/groups/user_groups_finder.rb12
-rw-r--r--app/finders/groups_finder.rb36
-rw-r--r--app/finders/incident_management/timeline_events_finder.rb2
-rw-r--r--app/finders/issuable_finder.rb29
-rw-r--r--app/finders/issues_finder.rb4
-rw-r--r--app/finders/merge_requests/by_approvals_finder.rb4
-rw-r--r--app/finders/merge_requests_finder.rb28
-rw-r--r--app/finders/merge_requests_finder/params.rb6
-rw-r--r--app/finders/projects_finder.rb4
-rw-r--r--app/finders/user_groups_counter.rb6
-rw-r--r--app/graphql/graphql_triggers.rb2
-rw-r--r--app/graphql/mutations/boards/issues/issue_move_list.rb28
-rw-r--r--app/graphql/mutations/ci/job/artifacts_destroy.rb38
-rw-r--r--app/graphql/mutations/ci/job_artifact/destroy.rb39
-rw-r--r--app/graphql/mutations/ci/runner/bulk_delete.rb4
-rw-r--r--app/graphql/mutations/ci/runner/update.rb46
-rw-r--r--app/graphql/mutations/custom_emoji/create.rb4
-rw-r--r--app/graphql/mutations/custom_emoji/destroy.rb4
-rw-r--r--app/graphql/mutations/dependency_proxy/group_settings/update.rb5
-rw-r--r--app/graphql/mutations/incident_management/timeline_event/promote_from_note.rb9
-rw-r--r--app/graphql/mutations/releases/create.rb2
-rw-r--r--app/graphql/mutations/todos/restore_many.rb4
-rw-r--r--app/graphql/queries/repository/blob_info.query.graphql62
-rw-r--r--app/graphql/resolvers/ci/job_token_scope_resolver.rb4
-rw-r--r--app/graphql/resolvers/ci/runner_jobs_resolver.rb10
-rw-r--r--app/graphql/resolvers/ci/runner_owner_project_resolver.rb15
-rw-r--r--app/graphql/resolvers/ci/runner_projects_resolver.rb63
-rw-r--r--app/graphql/resolvers/ci/test_suite_resolver.rb3
-rw-r--r--app/graphql/resolvers/concerns/issue_resolver_arguments.rb58
-rw-r--r--app/graphql/resolvers/concerns/looks_ahead.rb8
-rw-r--r--app/graphql/resolvers/concerns/project_search_arguments.rb36
-rw-r--r--app/graphql/resolvers/concerns/search_arguments.rb39
-rw-r--r--app/graphql/resolvers/crm/organization_state_counts_resolver.rb26
-rw-r--r--app/graphql/resolvers/crm/organizations_resolver.rb6
-rw-r--r--app/graphql/resolvers/deployment_resolver.rb20
-rw-r--r--app/graphql/resolvers/deployments_resolver.rb39
-rw-r--r--app/graphql/resolvers/environments/last_deployment_resolver.rb44
-rw-r--r--app/graphql/resolvers/environments_resolver.rb4
-rw-r--r--app/graphql/resolvers/group_packages_resolver.rb10
-rw-r--r--app/graphql/resolvers/members_resolver.rb4
-rw-r--r--app/graphql/resolvers/package_details_resolver.rb10
-rw-r--r--app/graphql/resolvers/project_jobs_resolver.rb10
-rw-r--r--app/graphql/resolvers/projects/branch_rules_resolver.rb15
-rw-r--r--app/graphql/resolvers/projects_resolver.rb32
-rw-r--r--app/graphql/resolvers/work_items_resolver.rb48
-rw-r--r--app/graphql/types/base_field.rb31
-rw-r--r--app/graphql/types/branch_protections/base_access_level_type.rb22
-rw-r--r--app/graphql/types/branch_protections/merge_access_level_type.rb11
-rw-r--r--app/graphql/types/branch_protections/push_access_level_type.rb11
-rw-r--r--app/graphql/types/branch_rules/branch_protection_type.rb29
-rw-r--r--app/graphql/types/ci/config_variable_type.rb22
-rw-r--r--app/graphql/types/ci/group_variable_connection_type.rb17
-rw-r--r--app/graphql/types/ci/group_variable_type.rb17
-rw-r--r--app/graphql/types/ci/instance_variable_type.rb28
-rw-r--r--app/graphql/types/ci/job_artifact_type.rb9
-rw-r--r--app/graphql/types/ci/job_type.rb6
-rw-r--r--app/graphql/types/ci/manual_variable_type.rb12
-rw-r--r--app/graphql/types/ci/project_variable_connection_type.rb17
-rw-r--r--app/graphql/types/ci/project_variable_type.rb13
-rw-r--r--app/graphql/types/ci/runner_membership_filter_enum.rb8
-rw-r--r--app/graphql/types/ci/runner_type.rb22
-rw-r--r--app/graphql/types/ci/variable_interface.rb24
-rw-r--r--app/graphql/types/clusters/agent_type.rb2
-rw-r--r--app/graphql/types/customer_relations/contact_sort_enum.rb4
-rw-r--r--app/graphql/types/customer_relations/organization_sort_enum.rb21
-rw-r--r--app/graphql/types/customer_relations/organization_state_counts_type.rb24
-rw-r--r--app/graphql/types/customer_relations/organization_state_enum.rb8
-rw-r--r--app/graphql/types/deployment_details_type.rb15
-rw-r--r--app/graphql/types/deployment_status_enum.rb14
-rw-r--r--app/graphql/types/deployment_tag_type.rb20
-rw-r--r--app/graphql/types/deployment_type.rb69
-rw-r--r--app/graphql/types/deployments_order_by_input_type.rb24
-rw-r--r--app/graphql/types/environment_type.rb41
-rw-r--r--app/graphql/types/group_type.rb14
-rw-r--r--app/graphql/types/member_sort_enum.rb13
-rw-r--r--app/graphql/types/merge_request_type.rb9
-rw-r--r--app/graphql/types/merge_requests/detailed_merge_status_enum.rb3
-rw-r--r--app/graphql/types/mutation_type.rb6
-rw-r--r--app/graphql/types/packages/package_details_type.rb2
-rw-r--r--app/graphql/types/project_type.rb479
-rw-r--r--app/graphql/types/projects/branch_rule_type.rb33
-rw-r--r--app/graphql/types/query_type.rb2
-rw-r--r--app/graphql/types/sort_direction_enum.rb11
-rw-r--r--app/graphql/types/subscription_type.rb7
-rw-r--r--app/graphql/types/timelog_type.rb2
-rw-r--r--app/graphql/types/work_items/widgets/description_type.rb14
-rw-r--r--app/helpers/application_settings_helper.rb22
-rw-r--r--app/helpers/badges_helper.rb66
-rw-r--r--app/helpers/blob_helper.rb26
-rw-r--r--app/helpers/ci/builds_helper.rb2
-rw-r--r--app/helpers/ci/jobs_helper.rb2
-rw-r--r--app/helpers/ci/runners_helper.rb1
-rw-r--r--app/helpers/deploy_tokens_helper.rb8
-rw-r--r--app/helpers/diff_helper.rb12
-rw-r--r--app/helpers/dropdowns_helper.rb5
-rw-r--r--app/helpers/form_helper.rb4
-rw-r--r--app/helpers/groups_helper.rb9
-rw-r--r--app/helpers/ide_helper.rb3
-rw-r--r--app/helpers/issuables_helper.rb3
-rw-r--r--app/helpers/javascript_helper.rb7
-rw-r--r--app/helpers/jira_connect_helper.rb14
-rw-r--r--app/helpers/kerberos_helper.rb13
-rw-r--r--app/helpers/kerberos_spnego_helper.rb13
-rw-r--r--app/helpers/labels_helper.rb2
-rw-r--r--app/helpers/learn_gitlab_helper.rb25
-rw-r--r--app/helpers/merge_requests_helper.rb20
-rw-r--r--app/helpers/nav/new_dropdown_helper.rb2
-rw-r--r--app/helpers/nav/top_nav_helper.rb101
-rw-r--r--app/helpers/notify_helper.rb11
-rw-r--r--app/helpers/packages_helper.rb4
-rw-r--r--app/helpers/page_layout_helper.rb4
-rw-r--r--app/helpers/profiles_helper.rb2
-rw-r--r--app/helpers/projects/google_cloud/cloudsql_helper.rb55
-rw-r--r--app/helpers/projects/pages_helper.rb11
-rw-r--r--app/helpers/projects/pipeline_helper.rb1
-rw-r--r--app/helpers/projects_helper.rb12
-rw-r--r--app/helpers/search_helper.rb40
-rw-r--r--app/helpers/sorting_helper.rb4
-rw-r--r--app/helpers/sorting_titles_values_helper.rb16
-rw-r--r--app/helpers/storage_helper.rb115
-rw-r--r--app/helpers/system_note_helper.rb6
-rw-r--r--app/helpers/timeboxes_helper.rb11
-rw-r--r--app/helpers/todos_helper.rb10
-rw-r--r--app/helpers/users/callouts_helper.rb20
-rw-r--r--app/helpers/users_helper.rb13
-rw-r--r--app/helpers/web_hooks/web_hooks_helper.rb5
-rw-r--r--app/mailers/abuse_report_mailer.rb2
-rw-r--r--app/mailers/application_mailer.rb19
-rw-r--r--app/mailers/email_rejection_mailer.rb2
-rw-r--r--app/mailers/emails/admin_notification.rb4
-rw-r--r--app/mailers/emails/groups.rb2
-rw-r--r--app/mailers/emails/identity_verification.rb2
-rw-r--r--app/mailers/emails/in_product_marketing.rb2
-rw-r--r--app/mailers/emails/members.rb2
-rw-r--r--app/mailers/emails/pages_domains.rb10
-rw-r--r--app/mailers/emails/profile.rb20
-rw-r--r--app/mailers/emails/projects.rb25
-rw-r--r--app/mailers/emails/releases.rb2
-rw-r--r--app/mailers/emails/remote_mirrors.rb2
-rw-r--r--app/mailers/notify.rb14
-rw-r--r--app/mailers/previews/notify_preview.rb18
-rw-r--r--app/mailers/repository_check_mailer.rb2
-rw-r--r--app/models/active_session.rb8
-rw-r--r--app/models/application_setting.rb17
-rw-r--r--app/models/ci/bridge.rb8
-rw-r--r--app/models/ci/build.rb137
-rw-r--r--app/models/ci/build_metadata.rb3
-rw-r--r--app/models/ci/freeze_period_status.rb28
-rw-r--r--app/models/ci/job_artifact.rb40
-rw-r--r--app/models/ci/job_token/scope.rb12
-rw-r--r--app/models/ci/namespace_mirror.rb14
-rw-r--r--app/models/ci/partition.rb6
-rw-r--r--app/models/ci/pipeline.rb81
-rw-r--r--app/models/ci/pipeline_artifact.rb6
-rw-r--r--app/models/ci/pipeline_variable.rb5
-rw-r--r--app/models/ci/processable.rb1
-rw-r--r--app/models/ci/runner.rb17
-rw-r--r--app/models/ci/stage.rb21
-rw-r--r--app/models/ci/trigger.rb6
-rw-r--r--app/models/clusters/applications/ingress.rb4
-rw-r--r--app/models/commit.rb16
-rw-r--r--app/models/commit_status.rb10
-rw-r--r--app/models/concerns/approvable.rb62
-rw-r--r--app/models/concerns/approvable_base.rb61
-rw-r--r--app/models/concerns/ci/artifactable.rb11
-rw-r--r--app/models/concerns/ci/has_deployment_name.rb15
-rw-r--r--app/models/concerns/ci/lockable.rb20
-rw-r--r--app/models/concerns/ci/metadatable.rb2
-rw-r--r--app/models/concerns/ci/partitionable.rb47
-rw-r--r--app/models/concerns/ci/track_environment_usage.rb31
-rw-r--r--app/models/concerns/counter_attribute.rb20
-rw-r--r--app/models/concerns/enums/ci/commit_status.rb6
-rw-r--r--app/models/concerns/enums/internal_id.rb3
-rw-r--r--app/models/concerns/from_set_operator.rb25
-rw-r--r--app/models/concerns/integrations/slack_mattermost_notifier.rb14
-rw-r--r--app/models/concerns/merge_request_reviewer_state.rb11
-rw-r--r--app/models/concerns/pg_full_text_searchable.rb1
-rw-r--r--app/models/concerns/project_features_compatibility.rb4
-rw-r--r--app/models/concerns/sortable.rb27
-rw-r--r--app/models/container_repository.rb9
-rw-r--r--app/models/customer_relations/contact.rb45
-rw-r--r--app/models/customer_relations/organization.rb33
-rw-r--r--app/models/deployment.rb25
-rw-r--r--app/models/environment.rb27
-rw-r--r--app/models/environment_status.rb2
-rw-r--r--app/models/error_tracking/project_error_tracking_setting.rb16
-rw-r--r--app/models/group.rb85
-rw-r--r--app/models/group_group_link.rb4
-rw-r--r--app/models/hooks/web_hook_log.rb2
-rw-r--r--app/models/incident_management/timeline_event.rb2
-rw-r--r--app/models/integration.rb6
-rw-r--r--app/models/integrations/datadog.rb26
-rw-r--r--app/models/integrations/discord.rb46
-rw-r--r--app/models/integrations/hangouts_chat.rb25
-rw-r--r--app/models/integrations/harbor.rb4
-rw-r--r--app/models/integrations/shimo.rb2
-rw-r--r--app/models/internal_id.rb5
-rw-r--r--app/models/issue.rb43
-rw-r--r--app/models/jira_connect_installation.rb6
-rw-r--r--app/models/loose_foreign_keys/deleted_record.rb4
-rw-r--r--app/models/member.rb101
-rw-r--r--app/models/merge_request.rb116
-rw-r--r--app/models/merge_request/predictions.rb7
-rw-r--r--app/models/merge_request_assignee.rb5
-rw-r--r--app/models/merge_request_reviewer.rb2
-rw-r--r--app/models/ml/candidate.rb13
-rw-r--r--app/models/ml/experiment.rb26
-rw-r--r--app/models/namespace.rb22
-rw-r--r--app/models/namespace_setting.rb15
-rw-r--r--app/models/namespaces/traversal/linear.rb10
-rw-r--r--app/models/namespaces/traversal/linear_scopes.rb29
-rw-r--r--app/models/note.rb7
-rw-r--r--app/models/notification_recipient.rb10
-rw-r--r--app/models/oauth_access_token.rb9
-rw-r--r--app/models/onboarding/completion.rb70
-rw-r--r--app/models/onboarding/learn_gitlab.rb38
-rw-r--r--app/models/onboarding/progress.rb118
-rw-r--r--app/models/onboarding_progress.rb114
-rw-r--r--app/models/packages/package.rb43
-rw-r--r--app/models/packages/policies/group.rb15
-rw-r--r--app/models/packages/policies/project.rb15
-rw-r--r--app/models/packages/rpm.rb8
-rw-r--r--app/models/packages/rpm/metadatum.rb51
-rw-r--r--app/models/pages_domain.rb12
-rw-r--r--app/models/personal_access_token.rb2
-rw-r--r--app/models/pool_repository.rb4
-rw-r--r--app/models/preloaders/environments/deployment_preloader.rb10
-rw-r--r--app/models/preloaders/group_policy_preloader.rb2
-rw-r--r--app/models/preloaders/project_policy_preloader.rb23
-rw-r--r--app/models/preloaders/project_root_ancestor_preloader.rb37
-rw-r--r--app/models/preloaders/users_max_access_level_in_projects_preloader.rb2
-rw-r--r--app/models/project.rb121
-rw-r--r--app/models/project_feature.rb1
-rw-r--r--app/models/project_setting.rb11
-rw-r--r--app/models/project_statistics.rb34
-rw-r--r--app/models/projects/build_artifacts_size_refresh.rb11
-rw-r--r--app/models/projects/topic.rb8
-rw-r--r--app/models/protected_branch.rb4
-rw-r--r--app/models/repository.rb28
-rw-r--r--app/models/resource_state_event.rb7
-rw-r--r--app/models/resource_timebox_event.rb6
-rw-r--r--app/models/route.rb18
-rw-r--r--app/models/snippet.rb10
-rw-r--r--app/models/snippet_repository.rb2
-rw-r--r--app/models/system_note_metadata.rb1
-rw-r--r--app/models/todo.rb9
-rw-r--r--app/models/user.rb99
-rw-r--r--app/models/user_status.rb4
-rw-r--r--app/models/users/callout.rb14
-rw-r--r--app/models/users/credit_card_validation.rb6
-rw-r--r--app/models/users/ghost_user_migration.rb12
-rw-r--r--app/models/users/group_callout.rb8
-rw-r--r--app/models/users/namespace_callout.rb8
-rw-r--r--app/models/users/project_callout.rb4
-rw-r--r--app/models/users_star_project.rb2
-rw-r--r--app/models/wiki.rb96
-rw-r--r--app/models/work_item.rb4
-rw-r--r--app/models/work_items/widgets/description.rb8
-rw-r--r--app/policies/ci/build_policy.rb8
-rw-r--r--app/policies/ci/job_artifact_policy.rb7
-rw-r--r--app/policies/ci/runner_policy.rb52
-rw-r--r--app/policies/group_policy.rb22
-rw-r--r--app/policies/issuable_policy.rb5
-rw-r--r--app/policies/packages/package_policy.rb2
-rw-r--r--app/policies/packages/policies/group_policy.rb27
-rw-r--r--app/policies/packages/policies/project_policy.rb54
-rw-r--r--app/policies/project_policy.rb8
-rw-r--r--app/policies/protected_branch_access_policy.rb5
-rw-r--r--app/policies/protected_branch_policy.rb1
-rw-r--r--app/presenters/ci/pipeline_presenter.rb2
-rw-r--r--app/presenters/commit_status_presenter.rb7
-rw-r--r--app/presenters/deployments/deployment_presenter.rb17
-rw-r--r--app/presenters/project_presenter.rb2
-rw-r--r--app/serializers/access_token_entity_base.rb8
-rw-r--r--app/serializers/environment_serializer.rb3
-rw-r--r--app/serializers/group_access_token_entity.rb6
-rw-r--r--app/serializers/impersonation_access_token_entity.rb11
-rw-r--r--app/serializers/impersonation_access_token_serializer.rb7
-rw-r--r--app/serializers/import/provider_repo_serializer.rb2
-rw-r--r--app/serializers/member_user_entity.rb4
-rw-r--r--app/serializers/merge_request_noteable_entity.rb15
-rw-r--r--app/serializers/merge_request_user_entity.rb12
-rw-r--r--app/serializers/personal_access_token_entity.rb2
-rw-r--r--app/serializers/project_access_token_entity.rb6
-rw-r--r--app/serializers/request_aware_entity.rb2
-rw-r--r--app/services/alert_management/process_prometheus_alert_service.rb5
-rw-r--r--app/services/auth/container_registry_authentication_service.rb1
-rw-r--r--app/services/authorized_project_update/find_records_due_for_refresh_service.rb32
-rw-r--r--app/services/boards/base_item_move_service.rb22
-rw-r--r--app/services/boards/issues/move_service.rb4
-rw-r--r--app/services/bulk_imports/file_download_service.rb83
-rw-r--r--app/services/bulk_imports/relation_export_service.rb2
-rw-r--r--app/services/bulk_imports/tree_export_service.rb8
-rw-r--r--app/services/ci/after_requeue_job_service.rb36
-rw-r--r--app/services/ci/archive_trace_service.rb2
-rw-r--r--app/services/ci/build_erase_service.rb49
-rw-r--r--app/services/ci/build_report_result_service.rb3
-rw-r--r--app/services/ci/compare_reports_base_service.rb9
-rw-r--r--app/services/ci/create_downstream_pipeline_service.rb15
-rw-r--r--app/services/ci/create_pipeline_service.rb1
-rw-r--r--app/services/ci/delete_objects_service.rb4
-rw-r--r--app/services/ci/expire_pipeline_cache_service.rb2
-rw-r--r--app/services/ci/generate_coverage_reports_service.rb2
-rw-r--r--app/services/ci/job_artifacts/create_service.rb2
-rw-r--r--app/services/ci/job_artifacts/delete_service.rb32
-rw-r--r--app/services/ci/job_artifacts/track_artifact_report_service.rb23
-rw-r--r--app/services/ci/pipeline_artifacts/coverage_report_service.rb8
-rw-r--r--app/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service.rb20
-rw-r--r--app/services/ci/pipelines/add_job_service.rb2
-rw-r--r--app/services/ci/queue/pending_builds_strategy.rb6
-rw-r--r--app/services/ci/register_job_service.rb2
-rw-r--r--app/services/ci/resource_groups/assign_resource_from_resource_group_service.rb6
-rw-r--r--app/services/ci/runners/set_runner_associated_projects_service.rb69
-rw-r--r--app/services/ci/runners/update_runner_service.rb9
-rw-r--r--app/services/ci/stuck_builds/drop_helpers.rb2
-rw-r--r--app/services/ci/test_failure_history_service.rb4
-rw-r--r--app/services/ci/unlock_artifacts_service.rb17
-rw-r--r--app/services/commits/create_service.rb2
-rw-r--r--app/services/concerns/alert_management/alert_processing.rb4
-rw-r--r--app/services/concerns/ci/downstream_pipeline_helpers.rb3
-rw-r--r--app/services/concerns/ci/job_token_scope/edit_scope_validations.rb4
-rw-r--r--app/services/concerns/projects/container_repository/gitlab/timeoutable.rb27
-rw-r--r--app/services/container_expiration_policies/cleanup_service.rb2
-rw-r--r--app/services/deployments/update_environment_service.rb14
-rw-r--r--app/services/design_management/copy_design_collection/copy_service.rb4
-rw-r--r--app/services/design_management/delete_designs_service.rb3
-rw-r--r--app/services/design_management/runs_design_actions.rb2
-rw-r--r--app/services/design_management/save_designs_service.rb6
-rw-r--r--app/services/environments/stop_service.rb15
-rw-r--r--app/services/files/multi_service.rb2
-rw-r--r--app/services/google_cloud/create_cloudsql_instance_service.rb2
-rw-r--r--app/services/google_cloud/enable_cloudsql_service.rb2
-rw-r--r--app/services/google_cloud/fetch_google_ip_list_service.rb89
-rw-r--r--app/services/groups/create_service.rb2
-rw-r--r--app/services/import/github_service.rb8
-rw-r--r--app/services/issuable_base_service.rb6
-rw-r--r--app/services/issuable_links/create_service.rb8
-rw-r--r--app/services/issues/base_service.rb3
-rw-r--r--app/services/issues/close_service.rb9
-rw-r--r--app/services/issues/export_csv_service.rb10
-rw-r--r--app/services/issues/relative_position_rebalancing_service.rb2
-rw-r--r--app/services/issues/reopen_service.rb7
-rw-r--r--app/services/labels/transfer_service.rb6
-rw-r--r--app/services/members/update_service.rb9
-rw-r--r--app/services/merge_requests/after_create_service.rb2
-rw-r--r--app/services/merge_requests/approval_service.rb41
-rw-r--r--app/services/merge_requests/base_service.rb43
-rw-r--r--app/services/merge_requests/ff_merge_service.rb30
-rw-r--r--app/services/merge_requests/handle_assignees_change_service.rb2
-rw-r--r--app/services/merge_requests/merge_service.rb23
-rw-r--r--app/services/merge_requests/mergeability/detailed_merge_status_service.rb63
-rw-r--r--app/services/merge_requests/mergeability/logger.rb103
-rw-r--r--app/services/merge_requests/mergeability/run_checks_service.rb13
-rw-r--r--app/services/merge_requests/refresh_service.rb1
-rw-r--r--app/services/merge_requests/update_assignees_service.rb2
-rw-r--r--app/services/merge_requests/update_service.rb14
-rw-r--r--app/services/milestones/transfer_service.rb5
-rw-r--r--app/services/namespaces/in_product_marketing_emails_service.rb2
-rw-r--r--app/services/notification_recipients/builder/base.rb52
-rw-r--r--app/services/onboarding/progress_service.rb33
-rw-r--r--app/services/onboarding_progress_service.rb31
-rw-r--r--app/services/packages/conan/search_service.rb2
-rw-r--r--app/services/packages/debian/generate_distribution_service.rb1
-rw-r--r--app/services/packages/debian/process_changes_service.rb16
-rw-r--r--app/services/packages/rpm/repository_metadata/base_builder.rb20
-rw-r--r--app/services/packages/rpm/repository_metadata/build_filelist_xml.rb14
-rw-r--r--app/services/packages/rpm/repository_metadata/build_other_xml.rb14
-rw-r--r--app/services/packages/rpm/repository_metadata/build_primary_xml.rb15
-rw-r--r--app/services/packages/rpm/repository_metadata/build_repomd_xml.rb59
-rw-r--r--app/services/packages/rubygems/dependency_resolver_service.rb5
-rw-r--r--app/services/post_receive_service.rb2
-rw-r--r--app/services/projects/alerting/notify_service.rb17
-rw-r--r--app/services/projects/blame_service.rb17
-rw-r--r--app/services/projects/container_repository/base_container_repository_service.rb17
-rw-r--r--app/services/projects/container_repository/cleanup_tags_base_service.rb119
-rw-r--r--app/services/projects/container_repository/cleanup_tags_service.rb161
-rw-r--r--app/services/projects/container_repository/gitlab/cleanup_tags_service.rb81
-rw-r--r--app/services/projects/container_repository/gitlab/delete_tags_service.rb15
-rw-r--r--app/services/projects/create_service.rb17
-rw-r--r--app/services/projects/destroy_service.rb26
-rw-r--r--app/services/projects/prometheus/alerts/notify_service.rb20
-rw-r--r--app/services/projects/update_pages_service.rb11
-rw-r--r--app/services/releases/create_service.rb3
-rw-r--r--app/services/resource_events/change_labels_service.rb5
-rw-r--r--app/services/service_ping/submit_service.rb78
-rw-r--r--app/services/service_response.rb26
-rw-r--r--app/services/snippets/base_service.rb9
-rw-r--r--app/services/snippets/bulk_destroy_service.rb4
-rw-r--r--app/services/snippets/create_service.rb3
-rw-r--r--app/services/snippets/update_service.rb5
-rw-r--r--app/services/spam/spam_action_service.rb9
-rw-r--r--app/services/spam/spam_constants.rb1
-rw-r--r--app/services/spam/spam_verdict_service.rb10
-rw-r--r--app/services/system_notes/issuables_service.rb56
-rw-r--r--app/services/system_notes/time_tracking_service.rb14
-rw-r--r--app/services/topics/merge_service.rb13
-rw-r--r--app/services/users/authorized_build_service.rb2
-rw-r--r--app/services/users/destroy_service.rb51
-rw-r--r--app/services/users/email_verification/base_service.rb27
-rw-r--r--app/services/users/email_verification/generate_token_service.rb21
-rw-r--r--app/services/users/email_verification/validate_token_service.rb78
-rw-r--r--app/services/users/migrate_records_to_ghost_user_in_batches_service.rb26
-rw-r--r--app/services/users/migrate_records_to_ghost_user_service.rb111
-rw-r--r--app/uploaders/object_storage/cdn.rb46
-rw-r--r--app/uploaders/object_storage/cdn/google_cdn.rb71
-rw-r--r--app/uploaders/object_storage/cdn/google_ip_cache.rb60
-rw-r--r--app/uploaders/packages/package_file_uploader.rb4
-rw-r--r--app/validators/json_schemas/merge_request_predictions_suggested_reviewers.json10
-rw-r--r--app/views/abuse_reports/new.html.haml6
-rw-r--r--app/views/admin/application_settings/_abuse.html.haml6
-rw-r--r--app/views/admin/application_settings/_account_and_limit.html.haml15
-rw-r--r--app/views/admin/application_settings/_ci_cd.html.haml12
-rw-r--r--app/views/admin/application_settings/_default_branch.html.haml2
-rw-r--r--app/views/admin/application_settings/_diff_limits.html.haml2
-rw-r--r--app/views/admin/application_settings/_eks.html.haml2
-rw-r--r--app/views/admin/application_settings/_email.html.haml2
-rw-r--r--app/views/admin/application_settings/_error_tracking.html.haml2
-rw-r--r--app/views/admin/application_settings/_external_authorization_service_form.html.haml2
-rw-r--r--app/views/admin/application_settings/_floc.html.haml11
-rw-r--r--app/views/admin/application_settings/_git_lfs_limits.html.haml2
-rw-r--r--app/views/admin/application_settings/_gitaly.html.haml2
-rw-r--r--app/views/admin/application_settings/_gitpod.html.haml2
-rw-r--r--app/views/admin/application_settings/_grafana.html.haml2
-rw-r--r--app/views/admin/application_settings/_help_page.html.haml2
-rw-r--r--app/views/admin/application_settings/_import_export_limits.html.haml2
-rw-r--r--app/views/admin/application_settings/_invitation_flow_enforcement.html.haml8
-rw-r--r--app/views/admin/application_settings/_ip_limits.html.haml2
-rw-r--r--app/views/admin/application_settings/_issue_limits.html.haml2
-rw-r--r--app/views/admin/application_settings/_jira_connect_application_key.html.haml2
-rw-r--r--app/views/admin/application_settings/_kroki.html.haml2
-rw-r--r--app/views/admin/application_settings/_localization.html.haml2
-rw-r--r--app/views/admin/application_settings/_mailgun.html.haml2
-rw-r--r--app/views/admin/application_settings/_network_rate_limits.html.haml2
-rw-r--r--app/views/admin/application_settings/_note_limits.html.haml2
-rw-r--r--app/views/admin/application_settings/_outbound.html.haml4
-rw-r--r--app/views/admin/application_settings/_package_registry.html.haml2
-rw-r--r--app/views/admin/application_settings/_pages.html.haml9
-rw-r--r--app/views/admin/application_settings/_performance.html.haml2
-rw-r--r--app/views/admin/application_settings/_performance_bar.html.haml2
-rw-r--r--app/views/admin/application_settings/_pipeline_limits.html.haml2
-rw-r--r--app/views/admin/application_settings/_plantuml.html.haml2
-rw-r--r--app/views/admin/application_settings/_prometheus.html.haml6
-rw-r--r--app/views/admin/application_settings/_protected_paths.html.haml2
-rw-r--r--app/views/admin/application_settings/_realtime.html.haml2
-rw-r--r--app/views/admin/application_settings/_registry.html.haml2
-rw-r--r--app/views/admin/application_settings/_repository_check.html.haml2
-rw-r--r--app/views/admin/application_settings/_repository_mirrors_form.html.haml2
-rw-r--r--app/views/admin/application_settings/_repository_static_objects.html.haml2
-rw-r--r--app/views/admin/application_settings/_repository_storage.html.haml2
-rw-r--r--app/views/admin/application_settings/_runner_registrars_form.html.haml2
-rw-r--r--app/views/admin/application_settings/_search_limits.html.haml2
-rw-r--r--app/views/admin/application_settings/_sentry.html.haml2
-rw-r--r--app/views/admin/application_settings/_sidekiq_job_limits.html.haml2
-rw-r--r--app/views/admin/application_settings/_signin.html.haml2
-rw-r--r--app/views/admin/application_settings/_signup.html.haml2
-rw-r--r--app/views/admin/application_settings/_snowplow.html.haml2
-rw-r--r--app/views/admin/application_settings/_sourcegraph.html.haml2
-rw-r--r--app/views/admin/application_settings/_spam.html.haml2
-rw-r--r--app/views/admin/application_settings/_terminal.html.haml2
-rw-r--r--app/views/admin/application_settings/_terms.html.haml2
-rw-r--r--app/views/admin/application_settings/_third_party_offers.html.haml2
-rw-r--r--app/views/admin/application_settings/_usage.html.haml2
-rw-r--r--app/views/admin/application_settings/_users_api_limits.html.haml2
-rw-r--r--app/views/admin/application_settings/_visibility_and_access.html.haml2
-rw-r--r--app/views/admin/application_settings/_whats_new.html.haml2
-rw-r--r--app/views/admin/application_settings/appearances/_form.html.haml2
-rw-r--r--app/views/admin/application_settings/general.html.haml4
-rw-r--r--app/views/admin/application_settings/metrics_and_profiling.html.haml2
-rw-r--r--app/views/admin/applications/_form.html.haml2
-rw-r--r--app/views/admin/background_migrations/index.html.haml2
-rw-r--r--app/views/admin/broadcast_messages/_form.html.haml2
-rw-r--r--app/views/admin/groups/_form.html.haml66
-rw-r--r--app/views/admin/hooks/_form.html.haml2
-rw-r--r--app/views/admin/identities/_form.html.haml2
-rw-r--r--app/views/admin/projects/show.html.haml229
-rw-r--r--app/views/admin/sessions/_new_base.html.haml2
-rw-r--r--app/views/admin/sessions/_signin_box.html.haml2
-rw-r--r--app/views/admin/spam_logs/_spam_log.html.haml12
-rw-r--r--app/views/admin/topics/_form.html.haml2
-rw-r--r--app/views/admin/topics/index.html.haml22
-rw-r--r--app/views/admin/users/_form.html.haml2
-rw-r--r--app/views/award_emoji/_awards_block.html.haml2
-rw-r--r--app/views/clusters/clusters/_gitlab_integration_form.html.haml2
-rw-r--r--app/views/dashboard/_activities.html.haml2
-rw-r--r--app/views/dashboard/milestones/index.html.haml6
-rw-r--r--app/views/dashboard/projects/_blank_state_welcome.html.haml11
-rw-r--r--app/views/dashboard/todos/_todo.html.haml8
-rw-r--r--app/views/dashboard/todos/index.html.haml2
-rw-r--r--app/views/devise/sessions/_new_base.html.haml4
-rw-r--r--app/views/devise/sessions/new.html.haml7
-rw-r--r--app/views/devise/sessions/successful_verification.haml2
-rw-r--r--app/views/devise/shared/_omniauth_box.html.haml11
-rw-r--r--app/views/devise/shared/_signin_box.html.haml2
-rw-r--r--app/views/devise/shared/_signup_box.html.haml8
-rw-r--r--app/views/devise/shared/_signup_omniauth_provider_list.haml2
-rw-r--r--app/views/devise/shared/_tab_single.html.haml2
-rw-r--r--app/views/devise/shared/_tabs_ldap.html.haml4
-rw-r--r--app/views/devise/shared/_terms_of_service_notice.html.haml18
-rw-r--r--app/views/groups/_activities.html.haml2
-rw-r--r--app/views/groups/_home_panel.html.haml8
-rw-r--r--app/views/groups/_new_group_fields.html.haml20
-rw-r--r--app/views/groups/crm/organizations/index.html.haml2
-rw-r--r--app/views/groups/harbor/repositories/index.html.haml5
-rw-r--r--app/views/groups/milestones/_form.html.haml2
-rw-r--r--app/views/groups/observability/index.html.haml2
-rw-r--r--app/views/groups/runners/edit.html.haml15
-rw-r--r--app/views/groups/runners/index.html.haml2
-rw-r--r--app/views/groups/settings/_advanced.html.haml2
-rw-r--r--app/views/groups/settings/_general.html.haml2
-rw-r--r--app/views/groups/settings/_permissions.html.haml2
-rw-r--r--app/views/groups/settings/ci_cd/_auto_devops_form.html.haml2
-rw-r--r--app/views/groups/settings/packages_and_registries/show.html.haml4
-rw-r--r--app/views/groups/settings/repository/_default_branch.html.haml2
-rw-r--r--app/views/groups/settings/repository/show.html.haml10
-rw-r--r--app/views/groups/show.html.haml53
-rw-r--r--app/views/help/drawers.html.haml2
-rw-r--r--app/views/jira_connect/subscriptions/index.html.haml2
-rw-r--r--app/views/layouts/_google_tag_manager_head.html.haml15
-rw-r--r--app/views/layouts/_page.html.haml1
-rw-r--r--app/views/layouts/devise.html.haml2
-rw-r--r--app/views/layouts/fullscreen.html.haml18
-rw-r--r--app/views/layouts/group.html.haml5
-rw-r--r--app/views/layouts/header/_current_user_dropdown.html.haml2
-rw-r--r--app/views/layouts/header/_default.html.haml28
-rw-r--r--app/views/layouts/header/_storage_enforcement_banner.html.haml15
-rw-r--r--app/views/layouts/nav/_top_nav.html.haml7
-rw-r--r--app/views/layouts/nav/sidebar/_admin.html.haml2
-rw-r--r--app/views/layouts/profile.html.haml2
-rw-r--r--app/views/layouts/project.html.haml4
-rw-r--r--app/views/notify/_failed_builds.html.haml2
-rw-r--r--app/views/notify/_successful_pipeline.html.haml15
-rw-r--r--app/views/notify/approved_merge_request_email.html.haml22
-rw-r--r--app/views/notify/autodevops_disabled_email.text.erb2
-rw-r--r--app/views/notify/change_in_merge_request_draft_status_email.html.haml8
-rw-r--r--app/views/notify/change_in_merge_request_draft_status_email.text.erb6
-rw-r--r--app/views/notify/import_issues_csv_email.html.haml14
-rw-r--r--app/views/notify/new_gpg_key_email.html.haml11
-rw-r--r--app/views/notify/new_mention_in_issue_email.html.haml2
-rw-r--r--app/views/notify/new_mention_in_merge_request_email.html.haml2
-rw-r--r--app/views/notify/new_ssh_key_email.html.haml14
-rw-r--r--app/views/notify/new_user_email.html.haml16
-rw-r--r--app/views/notify/pipeline_failed_email.text.erb2
-rw-r--r--app/views/notify/pipeline_fixed_email.html.haml2
-rw-r--r--app/views/notify/push_to_merge_request_email.html.haml13
-rw-r--r--app/views/notify/remote_mirror_update_failed_email.html.haml18
-rw-r--r--app/views/notify/removed_milestone_issue_email.html.haml2
-rw-r--r--app/views/notify/removed_milestone_merge_request_email.html.haml2
-rw-r--r--app/views/notify/repository_push_email.html.haml29
-rw-r--r--app/views/notify/resolved_all_discussions_email.html.haml3
-rw-r--r--app/views/notify/send_admin_notification.html.haml4
-rw-r--r--app/views/notify/unapproved_merge_request_email.html.haml22
-rw-r--r--app/views/profiles/_email_settings.html.haml4
-rw-r--r--app/views/profiles/active_sessions/index.html.haml7
-rw-r--r--app/views/profiles/emails/index.html.haml28
-rw-r--r--app/views/profiles/gpg_keys/_form.html.haml2
-rw-r--r--app/views/profiles/keys/_form.html.haml10
-rw-r--r--app/views/profiles/keys/_key.html.haml2
-rw-r--r--app/views/profiles/keys/_key_details.html.haml2
-rw-r--r--app/views/profiles/notifications/_email_settings.html.haml2
-rw-r--r--app/views/profiles/notifications/show.html.haml2
-rw-r--r--app/views/profiles/passwords/edit.html.haml2
-rw-r--r--app/views/profiles/passwords/new.html.haml2
-rw-r--r--app/views/profiles/preferences/show.html.haml5
-rw-r--r--app/views/profiles/show.html.haml41
-rw-r--r--app/views/profiles/two_factor_auths/show.html.haml2
-rw-r--r--app/views/projects/_activity.html.haml2
-rw-r--r--app/views/projects/_commit_button.html.haml5
-rw-r--r--app/views/projects/_errors.html.haml2
-rw-r--r--app/views/projects/_home_panel.html.haml6
-rw-r--r--app/views/projects/_service_desk_settings.html.haml3
-rw-r--r--app/views/projects/_stat_anchor_list.html.haml2
-rw-r--r--app/views/projects/activity.html.haml2
-rw-r--r--app/views/projects/blame/show.html.haml16
-rw-r--r--app/views/projects/blob/_header.html.haml5
-rw-r--r--app/views/projects/blob/edit.html.haml1
-rw-r--r--app/views/projects/blob/new.html.haml1
-rw-r--r--app/views/projects/blob/show.html.haml1
-rw-r--r--app/views/projects/branch_rules/_show.html.haml2
-rw-r--r--app/views/projects/branches/new.html.haml7
-rw-r--r--app/views/projects/buttons/_clone.html.haml4
-rw-r--r--app/views/projects/ci/builds/_build.html.haml2
-rw-r--r--app/views/projects/ci/pipeline_editor/show.html.haml1
-rw-r--r--app/views/projects/commits/show.html.haml2
-rw-r--r--app/views/projects/default_branch/_show.html.haml2
-rw-r--r--app/views/projects/edit.html.haml26
-rw-r--r--app/views/projects/forks/new.html.haml2
-rw-r--r--app/views/projects/google_cloud/databases/cloudsql_form.html.haml9
-rw-r--r--app/views/projects/google_cloud/gcp_regions/index.html.haml2
-rw-r--r--app/views/projects/harbor/repositories/index.html.haml5
-rw-r--r--app/views/projects/issues/_discussion.html.haml3
-rw-r--r--app/views/projects/issues/_related_branches.html.haml2
-rw-r--r--app/views/projects/issues/_work_item_links.html.haml2
-rw-r--r--app/views/projects/labels/index.html.haml1
-rw-r--r--app/views/projects/merge_requests/_awards_block.html.haml2
-rw-r--r--app/views/projects/merge_requests/_close_reopen_draft_report_toggle.html.haml18
-rw-r--r--app/views/projects/merge_requests/_widget.html.haml2
-rw-r--r--app/views/projects/merge_requests/creations/_new_compare.html.haml8
-rw-r--r--app/views/projects/merge_requests/show.html.haml22
-rw-r--r--app/views/projects/milestones/_form.html.haml2
-rw-r--r--app/views/projects/mirrors/_mirror_repos.html.haml40
-rw-r--r--app/views/projects/mirrors/_mirror_repos_list.html.haml47
-rw-r--r--app/views/projects/pages/_header.html.haml2
-rw-r--r--app/views/projects/pages/new.html.haml2
-rw-r--r--app/views/projects/pipeline_schedules/_form.html.haml2
-rw-r--r--app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml4
-rw-r--r--app/views/projects/pipelines/_with_tabs.html.haml2
-rw-r--r--app/views/projects/project_members/index.html.haml1
-rw-r--r--app/views/projects/project_templates/_template.html.haml2
-rw-r--r--app/views/projects/protected_branches/_create_protected_branch.html.haml12
-rw-r--r--app/views/projects/protected_branches/shared/_branches_list.html.haml2
-rw-r--r--app/views/projects/protected_branches/shared/_create_protected_branch.html.haml2
-rw-r--r--app/views/projects/protected_branches/shared/_protected_branch.html.haml2
-rw-r--r--app/views/projects/protected_tags/shared/_create_protected_tag.html.haml2
-rw-r--r--app/views/projects/settings/ci_cd/_autodevops_form.html.haml2
-rw-r--r--app/views/projects/settings/ci_cd/_form.html.haml2
-rw-r--r--app/views/projects/settings/ci_cd/show.html.haml3
-rw-r--r--app/views/projects/settings/merge_requests/show.html.haml18
-rw-r--r--app/views/projects/settings/operations/show.html.haml13
-rw-r--r--app/views/projects/settings/packages_and_registries/cleanup_tags.html.haml4
-rw-r--r--app/views/projects/settings/packages_and_registries/show.html.haml4
-rw-r--r--app/views/projects/show.html.haml1
-rw-r--r--app/views/projects/tags/index.html.haml2
-rw-r--r--app/views/projects/tags/new.html.haml22
-rw-r--r--app/views/projects/triggers/_form.html.haml2
-rw-r--r--app/views/projects/usage_quotas/index.html.haml2
-rw-r--r--app/views/search/_results_status.html.haml2
-rw-r--r--app/views/shared/_email_with_badge.html.haml5
-rw-r--r--app/views/shared/_file_highlight.html.haml3
-rw-r--r--app/views/shared/_integration_settings.html.haml2
-rw-r--r--app/views/shared/_md_preview.html.haml6
-rw-r--r--app/views/shared/access_tokens/_created_container.html.haml2
-rw-r--r--app/views/shared/access_tokens/_form.html.haml2
-rw-r--r--app/views/shared/boards/_show.html.haml2
-rw-r--r--app/views/shared/deploy_keys/_form.html.haml2
-rw-r--r--app/views/shared/deploy_keys/_project_group_form.html.haml2
-rw-r--r--app/views/shared/deploy_tokens/_index.html.haml7
-rw-r--r--app/views/shared/doorkeeper/applications/_form.html.haml2
-rw-r--r--app/views/shared/doorkeeper/applications/_show.html.haml9
-rw-r--r--app/views/shared/groups/_group.html.haml2
-rw-r--r--app/views/shared/groups/_visibility_level.html.haml3
-rw-r--r--app/views/shared/hook_logs/_recent_deliveries_table.html.haml2
-rw-r--r--app/views/shared/issuable/_feed_buttons.html.haml2
-rw-r--r--app/views/shared/issuable/_form.html.haml2
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml11
-rw-r--r--app/views/shared/issuable/_sidebar_assignees.html.haml2
-rw-r--r--app/views/shared/issuable/_sidebar_reviewers.html.haml2
-rw-r--r--app/views/shared/issuable/form/_branch_chooser.html.haml12
-rw-r--r--app/views/shared/issuable/form/_merge_params.html.haml8
-rw-r--r--app/views/shared/issuable/form/_metadata_issuable_assignee.html.haml2
-rw-r--r--app/views/shared/issue_type/_emoji_block.html.haml3
-rw-r--r--app/views/shared/labels/_form.html.haml2
-rw-r--r--app/views/shared/milestones/_milestone.html.haml3
-rw-r--r--app/views/shared/notes/_hints.html.haml2
-rw-r--r--app/views/shared/projects/_search_form.html.haml2
-rw-r--r--app/views/shared/runners/_form.html.haml2
-rw-r--r--app/views/shared/web_hooks/_form.html.haml2
-rw-r--r--app/views/shared/web_hooks/_index.html.haml2
-rw-r--r--app/views/shared/web_hooks/_web_hook_disabled_alert.html.haml13
-rw-r--r--app/views/shared/wikis/_form.html.haml2
-rw-r--r--app/views/shared/wikis/_wiki_content.html.haml2
-rw-r--r--app/views/shared/wikis/git_error.html.haml2
-rw-r--r--app/views/shared/wikis/show.html.haml2
-rw-r--r--app/views/users/show.html.haml2
-rw-r--r--app/workers/all_queues.yml81
-rw-r--r--app/workers/analytics/usage_trends/counter_job_worker.rb25
-rw-r--r--app/workers/ci/build_finished_worker.rb4
-rw-r--r--app/workers/ci/job_artifacts/track_artifact_report_worker.rb23
-rw-r--r--app/workers/ci/pipeline_artifacts/coverage_report_worker.rb2
-rw-r--r--app/workers/cleanup_container_repository_worker.rb2
-rw-r--r--app/workers/flush_counter_increments_worker.rb1
-rw-r--r--app/workers/gitlab/github_import/advance_stage_worker.rb2
-rw-r--r--app/workers/gitlab/github_import/import_protected_branch_worker.rb23
-rw-r--r--app/workers/gitlab/github_import/import_release_attachments_worker.rb21
-rw-r--r--app/workers/gitlab/github_import/stage/import_attachments_worker.rb58
-rw-r--r--app/workers/gitlab/github_import/stage/import_notes_worker.rb6
-rw-r--r--app/workers/gitlab/github_import/stage/import_protected_branches_worker.rb45
-rw-r--r--app/workers/gitlab/jira_import/import_issue_worker.rb3
-rw-r--r--app/workers/gitlab_service_ping_worker.rb15
-rw-r--r--app/workers/google_cloud/create_cloudsql_instance_worker.rb23
-rw-r--r--app/workers/google_cloud/fetch_google_ip_list_worker.rb17
-rw-r--r--app/workers/groups/update_two_factor_requirement_for_members_worker.rb22
-rw-r--r--app/workers/issues/close_worker.rb50
-rw-r--r--app/workers/namespaces/onboarding_issue_created_worker.rb2
-rw-r--r--app/workers/namespaces/onboarding_pipeline_created_worker.rb2
-rw-r--r--app/workers/namespaces/onboarding_progress_worker.rb2
-rw-r--r--app/workers/namespaces/onboarding_user_added_worker.rb2
-rw-r--r--app/workers/namespaces/process_sync_events_worker.rb2
-rw-r--r--app/workers/object_storage/migrate_uploads_worker.rb57
-rw-r--r--app/workers/process_commit_worker.rb39
-rw-r--r--app/workers/projects/inactive_projects_deletion_cron_worker.rb2
-rw-r--r--app/workers/projects/process_sync_events_worker.rb2
-rw-r--r--app/workers/ssh_keys/expired_notification_worker.rb27
-rw-r--r--app/workers/users/migrate_records_to_ghost_user_in_batches_worker.rb22
-rw-r--r--config/application.rb3
-rw-r--r--config/audit_events/types/type_schema.json74
-rw-r--r--config/dependency_decisions.yml6
-rw-r--r--config/environments/development.rb4
-rw-r--r--config/environments/test.rb2
-rw-r--r--config/events/1662373051_Projects__GoogleCloud__ConfigurationController_error_invalid_user.yml26
-rw-r--r--config/events/1662373057_Projects__GoogleCloud__ConfigurationController_error_google_oauth2_not_enabled.yml26
-rw-r--r--config/events/1662373062_Projects__GoogleCloud__ConfigurationController_error_feature_flag_not_enabled.yml26
-rw-r--r--config/events/1662373069_Projects__GoogleCloud__ConfigurationController_render_page.yml26
-rw-r--r--config/events/1662373075_Projects__GoogleCloud__ServiceAccountsController_error_invalid_user.yml26
-rw-r--r--config/events/1662373081_Projects__GoogleCloud__ServiceAccountsController_error_google_oauth2_not_enabled.yml26
-rw-r--r--config/events/1662373087_Projects__GoogleCloud__ServiceAccountsController_error_feature_flag_not_enabled.yml26
-rw-r--r--config/events/1662373092_Projects__GoogleCloud__ServiceAccountsController_render_form.yml26
-rw-r--r--config/events/1662373098_Projects__GoogleCloud__ServiceAccountsController_error_no_gcp_projects.yml26
-rw-r--r--config/events/1662373103_Projects__GoogleCloud__ServiceAccountsController_create_service_account.yml26
-rw-r--r--config/events/1662373109_Projects__GoogleCloud__ServiceAccountsController_error_google_api.yml26
-rw-r--r--config/events/1662373114_Projects__GoogleCloud__GcpRegionsController_error_invalid_user.yml26
-rw-r--r--config/events/1662373120_Projects__GoogleCloud__GcpRegionsController_error_google_oauth2_not_enabled.yml26
-rw-r--r--config/events/1662373125_Projects__GoogleCloud__GcpRegionsController_error_feature_flag_not_enabled.yml26
-rw-r--r--config/events/1662373131_Projects__GoogleCloud__GcpRegionsController_render_form.yml26
-rw-r--r--config/events/1662373136_Projects__GoogleCloud__GcpRegionsController_configure_region.yml26
-rw-r--r--config/events/1662373142_Projects__GoogleCloud__GcpRegionsController_error_create.yml26
-rw-r--r--config/events/1662373147_Projects__GoogleCloud__RevokeOauthController_error_invalid_user.yml26
-rw-r--r--config/events/1662373153_Projects__GoogleCloud__RevokeOauthController_error_google_oauth2_not_enabled.yml26
-rw-r--r--config/events/1662373158_Projects__GoogleCloud__RevokeOauthController_error_feature_flag_not_enabled.yml26
-rw-r--r--config/events/1662373164_Projects__GoogleCloud__RevokeOauthController_revoke_oauth.yml26
-rw-r--r--config/events/1662373170_Projects__GoogleCloud__DeploymentsController_error_invalid_user.yml26
-rw-r--r--config/events/1662373175_Projects__GoogleCloud__DeploymentsController_error_google_oauth2_not_enabled.yml26
-rw-r--r--config/events/1662373181_Projects__GoogleCloud__DeploymentsController_error_feature_flag_not_enabled.yml26
-rw-r--r--config/events/1662373187_Projects__GoogleCloud__DeploymentsController_render_page.yml26
-rw-r--r--config/events/1662373192_Projects__GoogleCloud__DeploymentsController_generate_cloudrun_pipeline.yml26
-rw-r--r--config/events/1662373198_Projects__GoogleCloud__DeploymentsController_error_enable_cloudrun_services.yml26
-rw-r--r--config/events/1662373204_Projects__GoogleCloud__DeploymentsController_error_generate_cloudrun_pipeline.yml26
-rw-r--r--config/events/1662373209_Projects__GoogleCloud__DeploymentsController_error_google_api.yml26
-rw-r--r--config/events/1662373215_Projects__GoogleCloud__DatabasesController_error_invalid_user.yml26
-rw-r--r--config/events/1662373220_Projects__GoogleCloud__DatabasesController_error_google_oauth2_not_enabled.yml26
-rw-r--r--config/events/1662373226_Projects__GoogleCloud__DatabasesController_error_feature_flag_not_enabled.yml26
-rw-r--r--config/events/1662373232_Projects__GoogleCloud__DatabasesController_render_page.yml26
-rw-r--r--config/events/1662373237_Projects__GoogleCloud__DatabasesController_render_cloudsql_form.yml26
-rw-r--r--config/events/1662373243_Projects__GoogleCloud__DatabasesController_create_cloudsql_instance.yml26
-rw-r--r--config/events/1662373249_Projects__GoogleCloud__DatabasesController_error_enable_cloudsql_services.yml26
-rw-r--r--config/events/1662373254_Projects__GoogleCloud__DatabasesController_error_create_cloudsql_instance.yml26
-rw-r--r--config/feature_categories.yml3
-rw-r--r--config/feature_flags/development/add_timing_to_certain_cache_actions.yml8
-rw-r--r--config/feature_flags/development/always_async_project_authorizations_refresh.yml2
-rw-r--r--config/feature_flags/development/api_caching_branches.yml8
-rw-r--r--config/feature_flags/development/arkose_labs_signup_challenge.yml8
-rw-r--r--config/feature_flags/development/async_after_approval.yml8
-rw-r--r--config/feature_flags/development/auto_ban_user_on_excessive_projects_download.yml8
-rw-r--r--config/feature_flags/development/auto_ban_user_on_namespace_excessive_projects_download.yml8
-rw-r--r--config/feature_flags/development/block_emails_with_failures.yml8
-rw-r--r--config/feature_flags/development/block_weak_passwords.yml8
-rw-r--r--config/feature_flags/development/bypass_batch_pop_queueing_for_merge_trains.yml8
-rw-r--r--config/feature_flags/development/cache_issue_sums.yml8
-rw-r--r--config/feature_flags/development/ci_docker_image_pull_policy.yml8
-rw-r--r--config/feature_flags/development/ci_limit_active_jobs_early.yml8
-rw-r--r--config/feature_flags/development/ci_limit_complete_hierarchy_size.yml8
-rw-r--r--config/feature_flags/development/ci_new_public_oss_cost_factor.yml8
-rw-r--r--config/feature_flags/development/ci_project_pipeline_config_refactoring.yml8
-rw-r--r--config/feature_flags/development/ci_requeue_with_dag_object_hierarchy.yml8
-rw-r--r--config/feature_flags/development/ci_stop_expanding_file_vars_for_runners.yml2
-rw-r--r--config/feature_flags/development/ci_update_unlocked_pipeline_artifacts.yml8
-rw-r--r--config/feature_flags/development/ci_variable_for_group_gitlab_deploy_token.yml8
-rw-r--r--config/feature_flags/development/ci_variables_refactoring_to_variable.yml8
-rw-r--r--config/feature_flags/development/container_registry_legacy_authentication_for_deploy_tokens.yml8
-rw-r--r--config/feature_flags/development/contribution_analytics_optimized_base_query.yml2
-rw-r--r--config/feature_flags/development/convert_diff_to_utf8_with_replacement_symbol.yml8
-rw-r--r--config/feature_flags/development/cube_api_proxy.yml8
-rw-r--r--config/feature_flags/development/datadog_integration_logs_collection.yml8
-rw-r--r--config/feature_flags/development/detect_cross_database_modification.yml8
-rw-r--r--config/feature_flags/development/dora_configuration.yml8
-rw-r--r--config/feature_flags/development/draft_quick_action_non_toggle.yml8
-rw-r--r--config/feature_flags/development/epic_widget_edit_confirmation.yml8
-rw-r--r--config/feature_flags/development/error_tracking_sentry_limit.yml8
-rw-r--r--config/feature_flags/development/escape_gitaly_refs.yml8
-rw-r--r--config/feature_flags/development/etag_merge_request_diff_batches.yml8
-rw-r--r--config/feature_flags/development/execute_build_hooks_inline.yml8
-rw-r--r--config/feature_flags/development/extract_mr_diff_deletions.yml8
-rw-r--r--config/feature_flags/development/gitlab_shell_jwt_token.yml8
-rw-r--r--config/feature_flags/development/global_search_custom_slis.yml8
-rw-r--r--config/feature_flags/development/global_search_error_rate_sli.yml8
-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_ip_restrictions_allow_global.yml8
-rw-r--r--config/feature_flags/development/group_level_protected_environment_settings_permission.yml8
-rw-r--r--config/feature_flags/development/group_overview_tabs_vue.yml8
-rw-r--r--config/feature_flags/development/hash_oauth_secrets.yml8
-rw-r--r--config/feature_flags/development/highlight_diffs_renewable_expiration.yml8
-rw-r--r--config/feature_flags/development/highlight_diffs_short_renewable_expiration.yml8
-rw-r--r--config/feature_flags/development/improved_mergeability_checks.yml8
-rw-r--r--config/feature_flags/development/inactive_projects_deletion.yml8
-rw-r--r--config/feature_flags/development/include_groups_from_group_shares_in_group_transfer_locations.yml8
-rw-r--r--config/feature_flags/development/include_groups_from_group_shares_in_project_transfer_locations.yml8
-rw-r--r--config/feature_flags/development/incubation_5mp_google_cloud.yml2
-rw-r--r--config/feature_flags/development/invitation_flow_enforcement_setting.yml8
-rw-r--r--config/feature_flags/development/markdown_dollar_math.yml8
-rw-r--r--config/feature_flags/development/maven_central_request_forwarding.yml8
-rw-r--r--config/feature_flags/development/mergeability_checks_logger.yml8
-rw-r--r--config/feature_flags/development/ml_experiment_tracking.yml8
-rw-r--r--config/feature_flags/development/mr_attention_requests.yml8
-rw-r--r--config/feature_flags/development/new_navbar_layout.yml8
-rw-r--r--config/feature_flags/development/observability_group_tab.yml8
-rw-r--r--config/feature_flags/development/order_builds_for_group_runner.yml8
-rw-r--r--config/feature_flags/development/prevent_outdated_deployment_jobs.yml8
-rw-r--r--config/feature_flags/development/process_issue_closure_in_background.yml8
-rw-r--r--config/feature_flags/development/product_intelligence_database_event_tracking.yml1
-rw-r--r--config/feature_flags/development/query_analyzer_gitlab_schema_metrics.yml8
-rw-r--r--config/feature_flags/development/read_package_policy_rule.yml8
-rw-r--r--config/feature_flags/development/rebalance_issues.yml8
-rw-r--r--config/feature_flags/development/rely_on_protected_branches_cache.yml8
-rw-r--r--config/feature_flags/development/remove_extra_primary_submenu_options.yml8
-rw-r--r--config/feature_flags/development/remove_user_attributes_groups.yml8
-rw-r--r--config/feature_flags/development/remove_user_attributes_projects.yml8
-rw-r--r--config/feature_flags/development/report_artifact_build_completed_metrics_on_build_completion.yml8
-rw-r--r--config/feature_flags/development/restyle_login_page.yml2
-rw-r--r--config/feature_flags/development/rpm_packages.yml8
-rw-r--r--config/feature_flags/development/run_pipeline_graphql.yml8
-rw-r--r--config/feature_flags/development/s3_omit_multipart_urls.yml8
-rw-r--r--config/feature_flags/development/seat_count_alerts.yml8
-rw-r--r--config/feature_flags/development/shimo_integration.yml8
-rw-r--r--config/feature_flags/development/skip_checking_namespace_in_query.yml8
-rw-r--r--config/feature_flags/development/tag_list_keyset_pagination.yml8
-rw-r--r--config/feature_flags/development/track_agent_users_using_ci_tunnel.yml8
-rw-r--r--config/feature_flags/development/track_work_items_activity.yml2
-rw-r--r--config/feature_flags/development/traversal_ids_btree.yml8
-rw-r--r--config/feature_flags/development/ultimate_feature_removal_banner.yml8
-rw-r--r--config/feature_flags/development/usage_data_ci_i_testing_test_report_uploaded.yml8
-rw-r--r--config/feature_flags/development/usage_data_i_code_review_user_gitlab_cli_api_request.yml8
-rw-r--r--config/feature_flags/development/usage_data_i_code_review_user_jetbrains_api_request.yml8
-rw-r--r--config/feature_flags/development/usage_quotas_for_all_editions.yml8
-rw-r--r--config/feature_flags/development/use_gitaly_pagination_for_refs.yml8
-rw-r--r--config/feature_flags/development/use_pipeline_wizard_for_pages.yml4
-rw-r--r--config/feature_flags/development/use_vsa_aggregated_tables.yml8
-rw-r--r--config/feature_flags/development/user_destroy_with_limited_execution_time_worker.yml8
-rw-r--r--config/feature_flags/development/vscode_web_ide.yml8
-rw-r--r--config/feature_flags/development/webui_members_inherited_users.yml8
-rw-r--r--config/feature_flags/development/wiki_find_page_with_normal_repository_rpcs.yml8
-rw-r--r--config/feature_flags/experiment/combined_registration.yml8
-rw-r--r--config/feature_flags/ops/ci_partitioning_analyze_queries.yml8
-rw-r--r--config/feature_flags/ops/database_async_index_destruction.yml8
-rw-r--r--config/feature_flags/ops/detect_cross_database_modification.yml7
-rw-r--r--config/feature_flags/ops/github_importer_attachments_import.yml8
-rw-r--r--config/feature_flags/ops/increase_branch_cache_expiry.yml8
-rw-r--r--config/feature_flags/ops/query_analyzer_gitlab_schema_metrics.yml7
-rw-r--r--config/feature_flags/undefined/gitaly_simplify_find_local_branches_response.yml8
-rw-r--r--config/gitlab.yml.example16
-rw-r--r--config/helpers/evaluate_module_from_source.js37
-rw-r--r--config/initializers/01_secret_token.rb7
-rw-r--r--config/initializers/1_settings.rb49
-rw-r--r--config/initializers/7_redis.rb15
-rw-r--r--config/initializers/active_record_keyset_pagination.rb26
-rw-r--r--config/initializers/doorkeeper.rb4
-rw-r--r--config/initializers/excon.rb8
-rw-r--r--config/initializers/gitlab_experiment.rb2
-rw-r--r--config/initializers/load_balancing.rb12
-rw-r--r--config/initializers/lookbook.rb14
-rw-r--r--config/initializers/microsoft_graph_mailer.rb14
-rw-r--r--config/initializers/omniauth.rb11
-rw-r--r--config/initializers/postgres_partitioning.rb67
-rw-r--r--config/initializers/sidekiq.rb1
-rw-r--r--config/initializers/sidekiq_cluster.rb29
-rw-r--r--config/initializers/wikicloth_redos_patch.rb41
-rw-r--r--config/initializers/wikicloth_ruby_3_patch.rb272
-rw-r--r--config/initializers/zz_metrics.rb4
-rw-r--r--config/initializers_before_autoloader/000_inflections.rb1
-rw-r--r--config/initializers_before_autoloader/002_sidekiq.rb2
-rw-r--r--config/metrics/aggregates/code_review.yml16
-rw-r--r--config/metrics/counts_28d/20210216174910_analytics_unique_visits_for_any_target_monthly.yml1
-rw-r--r--config/metrics/counts_28d/20210216184454_code_review_total_unique_counts_monthly.yml148
-rwxr-xr-xconfig/metrics/counts_28d/20210216184559_ci_templates_total_unique_counts_monthly.yml9
-rw-r--r--config/metrics/counts_28d/20210216184850_deploy_token_packages_total_unique_counts_monthly.yml4
-rw-r--r--config/metrics/counts_28d/20210216184937_user_packages_total_unique_counts_monthly.yml4
-rw-r--r--config/metrics/counts_28d/20220131143209_i_quickactions_attention_monthly.yml4
-rw-r--r--config/metrics/counts_28d/20220131153230_i_quickactions_remove_attention_monthly.yml4
-rw-r--r--config/metrics/counts_28d/20220504150641_count_notes_in_ipynb_diff_commit_monthly.yml27
-rw-r--r--config/metrics/counts_28d/20220504150641_count_notes_in_ipynb_diff_monthly.yml27
-rw-r--r--config/metrics/counts_28d/20220504150641_count_notes_in_ipynb_diff_mr_monthly.yml27
-rw-r--r--config/metrics/counts_28d/20220504150641_count_users_with_notes_in_ipynb_diff_commit_monthly.yml27
-rw-r--r--config/metrics/counts_28d/20220504150641_count_users_with_notes_in_ipynb_diff_monthly.yml27
-rw-r--r--config/metrics/counts_28d/20220504150641_count_users_with_notes_in_ipynb_diff_mr_monthly.yml27
-rw-r--r--config/metrics/counts_28d/20220531145023_p_ci_templates_katalon_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220627133135_i_code_review_submit_review_approve_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220627134108_i_code_review_submit_review_comment_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220816202928_bulk_import_entities_group_failed.yml26
-rw-r--r--config/metrics/counts_28d/20220816202928_bulk_import_entities_group_finished.yml26
-rw-r--r--config/metrics/counts_28d/20220816202928_bulk_import_entities_group_timeout.yml26
-rw-r--r--config/metrics/counts_28d/20220823125645_bulk_import_entities_project_timeout.yml26
-rw-r--r--config/metrics/counts_28d/20220823125924_bulk_import_entities_project_failed.yml26
-rw-r--r--config/metrics/counts_28d/20220823130209_bulk_import_entities_project_finished.yml26
-rw-r--r--config/metrics/counts_28d/20220825142533_i_testing_test_report_uploaded_monthly.yml26
-rw-r--r--config/metrics/counts_28d/20220825232557_count_user_auth.yml24
-rw-r--r--config/metrics/counts_28d/20220830104453_i_code_review_merge_request_widget_license_compliance_view_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220830104500_i_code_review_merge_request_widget_license_compliance_full_report_clicked_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220830104507_i_code_review_merge_request_widget_license_compliance_expand_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220830104514_i_code_review_merge_request_widget_license_compliance_expand_success_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220830104521_i_code_review_merge_request_widget_license_compliance_expand_warning_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220830104528_i_code_review_merge_request_widget_license_compliance_expand_failed_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220905210112_users_visiting_environments_pages_monthly.yml26
-rw-r--r--config/metrics/counts_28d/20220907080630_i_quickactions_timeline_monthly.yml27
-rw-r--r--config/metrics/counts_28d/20220907084347_p_ci_templates_implicit_security_sast_iac_monthly.yml26
-rw-r--r--config/metrics/counts_28d/20220907102714_p_ci_templates_implicit_jobs_sast_iac_monthly.yml26
-rw-r--r--config/metrics/counts_28d/20220907202807_p_ci_templates_jobs_dependency_scanning_latest_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220907212005_p_ci_templates_security_container_scanning_latest_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220907215635_p_ci_templates_jobs_license_scanning_latest_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220912161240_p_ci_templates_implicit_jobs_dependency_scanning_latest_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220912162308_p_ci_templates_implicit_jobs_license_scanning_latest_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220912162752_p_ci_templates_implicit_security_container_scanning_latest_monthly.yml25
-rw-r--r--config/metrics/counts_7d/20210216184452_code_review_total_unique_counts_weekly.yml172
-rwxr-xr-xconfig/metrics/counts_7d/20210216184557_ci_templates_total_unique_counts_weekly.yml4
-rw-r--r--config/metrics/counts_7d/20210216184848_deploy_token_packages_total_unique_counts_weekly.yml4
-rw-r--r--config/metrics/counts_7d/20210216184935_user_packages_total_unique_counts_weekly.yml4
-rw-r--r--config/metrics/counts_7d/20220131143201_i_quickactions_attention_weekly.yml4
-rw-r--r--config/metrics/counts_7d/20220131153223_i_quickactions_remove_attention_weekly.yml4
-rw-r--r--config/metrics/counts_7d/20220504150641_count_notes_in_ipynb_diff_commit_weekly.yml27
-rw-r--r--config/metrics/counts_7d/20220504150641_count_notes_in_ipynb_diff_mr_weekly.yml27
-rw-r--r--config/metrics/counts_7d/20220504150641_count_notes_in_ipynb_diff_weekly.yml27
-rw-r--r--config/metrics/counts_7d/20220504150641_count_users_with_notes_in_ipynb_diff_commit_weekly.yml27
-rw-r--r--config/metrics/counts_7d/20220504150641_count_users_with_notes_in_ipynb_diff_mr_weekly.yml27
-rw-r--r--config/metrics/counts_7d/20220504150641_count_users_with_notes_in_ipynb_diff_weekly.yml27
-rw-r--r--config/metrics/counts_7d/20220531145014_p_ci_templates_katalon_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220627133128_i_code_review_submit_review_approve_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220627134100_i_code_review_submit_review_comment_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220825142528_i_testing_test_report_uploaded_weekly.yml26
-rw-r--r--config/metrics/counts_7d/20220830104410_i_code_review_merge_request_widget_license_compliance_view_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220830104418_i_code_review_merge_request_widget_license_compliance_full_report_clicked_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220830104424_i_code_review_merge_request_widget_license_compliance_expand_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220830104431_i_code_review_merge_request_widget_license_compliance_expand_success_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220830104438_i_code_review_merge_request_widget_license_compliance_expand_warning_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220830104446_i_code_review_merge_request_widget_license_compliance_expand_failed_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220907080626_i_quickactions_timeline_weekly.yml27
-rw-r--r--config/metrics/counts_7d/20220907084343_p_ci_templates_implicit_security_sast_iac_weekly.yml26
-rw-r--r--config/metrics/counts_7d/20220907102710_p_ci_templates_implicit_jobs_sast_iac_weekly.yml26
-rw-r--r--config/metrics/counts_7d/20220907202801_p_ci_templates_jobs_dependency_scanning_latest_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220907211959_p_ci_templates_security_container_scanning_latest_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220907215629_p_ci_templates_jobs_license_scanning_latest_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220912161233_p_ci_templates_implicit_jobs_dependency_scanning_latest_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220912162301_p_ci_templates_implicit_jobs_license_scanning_latest_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220912162745_p_ci_templates_implicit_security_container_scanning_latest_weekly.yml25
-rw-r--r--config/metrics/counts_all/20210216175043_merge_request_create.yml4
-rw-r--r--config/metrics/counts_all/20210216180242_web_ide_commits.yml5
-rw-r--r--config/metrics/counts_all/20210216180244_web_ide_views.yml5
-rw-r--r--config/metrics/counts_all/20210216180246_web_ide_merge_requests.yml5
-rw-r--r--config/metrics/counts_all/20210216180248_web_ide_previews.yml5
-rw-r--r--config/metrics/counts_all/20210216180250_web_ide_terminals.yml5
-rw-r--r--config/metrics/counts_all/20210216180252_web_ide_pipelines.yml5
-rw-r--r--config/metrics/counts_all/20210216180740_design_management_designs_create.yml4
-rw-r--r--config/metrics/counts_all/20210216180741_design_management_designs_update.yml4
-rw-r--r--config/metrics/counts_all/20210216180743_design_management_designs_delete.yml4
-rw-r--r--config/metrics/counts_all/20210216182006_source_code_pushes.yml2
-rw-r--r--config/metrics/counts_all/20210216182855_package_events_i_package_composer_delete_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182857_package_events_i_package_composer_pull_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182859_package_events_i_package_composer_push_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182901_package_events_i_package_conan_delete_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182903_package_events_i_package_conan_pull_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182905_package_events_i_package_conan_push_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182913_package_events_i_package_debian_delete_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182915_package_events_i_package_debian_pull_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182919_package_events_i_package_delete_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182921_package_events_i_package_delete_package_by_deploy_token.yml4
-rw-r--r--config/metrics/counts_all/20210216182923_package_events_i_package_delete_package_by_guest.yml4
-rw-r--r--config/metrics/counts_all/20210216182925_package_events_i_package_delete_package_by_user.yml4
-rw-r--r--config/metrics/counts_all/20210216182927_package_events_i_package_generic_delete_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182929_package_events_i_package_generic_pull_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182931_package_events_i_package_generic_push_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182933_package_events_i_package_golang_delete_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182934_package_events_i_package_golang_pull_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182936_package_events_i_package_golang_push_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182938_package_events_i_package_maven_delete_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182940_package_events_i_package_maven_pull_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182942_package_events_i_package_maven_push_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182944_package_events_i_package_npm_delete_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182946_package_events_i_package_npm_pull_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182948_package_events_i_package_npm_push_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182950_package_events_i_package_nuget_delete_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182952_package_events_i_package_nuget_pull_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182954_package_events_i_package_nuget_push_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182956_package_events_i_package_pull_package.yml4
-rw-r--r--config/metrics/counts_all/20210216182958_package_events_i_package_pull_package_by_deploy_token.yml4
-rw-r--r--config/metrics/counts_all/20210216183000_package_events_i_package_pull_package_by_guest.yml4
-rw-r--r--config/metrics/counts_all/20210216183002_package_events_i_package_pull_package_by_user.yml4
-rw-r--r--config/metrics/counts_all/20210216183004_package_events_i_package_push_package.yml4
-rw-r--r--config/metrics/counts_all/20210216183005_package_events_i_package_push_package_by_deploy_token.yml4
-rw-r--r--config/metrics/counts_all/20210216183007_package_events_i_package_push_package_by_guest.yml4
-rw-r--r--config/metrics/counts_all/20210216183009_package_events_i_package_push_package_by_user.yml4
-rw-r--r--config/metrics/counts_all/20210216183011_package_events_i_package_pypi_delete_package.yml4
-rw-r--r--config/metrics/counts_all/20210216183013_package_events_i_package_pypi_pull_package.yml4
-rw-r--r--config/metrics/counts_all/20210216183015_package_events_i_package_pypi_push_package.yml4
-rw-r--r--config/metrics/counts_all/20210303153000_package_events_i_package_rubygems_delete_package.yml4
-rw-r--r--config/metrics/counts_all/20210303153002_package_events_i_package_rubygems_pull_package.yml4
-rw-r--r--config/metrics/counts_all/20210303153004_package_events_i_package_rubygems_push_package.yml4
-rw-r--r--config/metrics/counts_all/20210410012200_package_events_i_package_terraform_module_delete_package.yml4
-rw-r--r--config/metrics/counts_all/20210410012202_package_events_i_package_terraform_module_pull_package.yml4
-rw-r--r--config/metrics/counts_all/20210410012204_package_events_i_package_terraform_module_push_package.yml4
-rw-r--r--config/metrics/counts_all/20210517073546_package_events_i_package_helm_pull_package.yml4
-rw-r--r--config/metrics/counts_all/20210625095025_package_events_i_package_helm_push_package.yml4
-rw-r--r--config/metrics/counts_all/20210709191135_package_events_i_package_nuget_pull_symbol_package.yml4
-rw-r--r--config/metrics/counts_all/20210709191829_package_events_i_package_nuget_push_symbol_package.yml4
-rw-r--r--config/metrics/counts_all/20210709210941_package_events_i_package_pull_symbol_package.yml4
-rw-r--r--config/metrics/counts_all/20210709211058_package_events_i_package_pull_symbol_package_by_deploy_token.yml4
-rw-r--r--config/metrics/counts_all/20210709211248_package_events_i_package_pull_symbol_package_by_guest.yml4
-rw-r--r--config/metrics/counts_all/20210709211341_package_events_i_package_pull_symbol_package_by_user.yml4
-rw-r--r--config/metrics/counts_all/20210709211439_package_events_i_package_push_symbol_package.yml4
-rw-r--r--config/metrics/counts_all/20210709211636_package_events_i_package_push_symbol_package_by_deploy_token.yml4
-rw-r--r--config/metrics/counts_all/20210709211731_package_events_i_package_push_symbol_package_by_guest.yml4
-rw-r--r--config/metrics/counts_all/20210709211831_package_events_i_package_push_symbol_package_by_user.yml4
-rw-r--r--config/metrics/counts_all/20210723075525_diff_searches.yml6
-rw-r--r--config/metrics/counts_all/20220122022215_web_ide_previews_success.yml5
-rw-r--r--config/metrics/counts_all/20220314362302_service_usage_data_download_payload.yml4
-rw-r--r--config/metrics/counts_all/20220825115210_i_merge_request_widget_license_compliance_count_view.yml24
-rw-r--r--config/metrics/counts_all/20220825115217_i_merge_request_widget_license_compliance_count_full_report_clicked.yml24
-rw-r--r--config/metrics/counts_all/20220825115224_i_merge_request_widget_license_compliance_count_expand.yml24
-rw-r--r--config/metrics/counts_all/20220825115230_i_merge_request_widget_license_compliance_count_expand_success.yml24
-rw-r--r--config/metrics/counts_all/20220825115236_i_merge_request_widget_license_compliance_count_expand_warning.yml24
-rw-r--r--config/metrics/counts_all/20220825115242_i_merge_request_widget_license_compliance_count_expand_failed.yml24
-rw-r--r--config/metrics/counts_all/20220825232556_count_user_auth.yml23
-rw-r--r--config/no_todos_messages.yml11
-rw-r--r--config/object_store_settings.rb17
-rw-r--r--config/plugins/graphql_known_operations_plugin.js19
-rw-r--r--config/routes.rb7
-rw-r--r--config/routes/group.rb4
-rw-r--r--config/routes/help.rb1
-rw-r--r--config/routes/project.rb5
-rw-r--r--config/routes/uploads.rb7
-rw-r--r--config/settings.rb6
-rw-r--r--config/sidekiq_queues.yml14
-rw-r--r--config/weak_password_digests.yml4550
-rw-r--r--config/webpack.config.js36
-rw-r--r--danger/Dangerfile-bundle_size33
-rw-r--r--danger/config_files/Dangerfile3
-rw-r--r--danger/database/Dangerfile5
-rw-r--r--danger/plugins/config_files.rb10
-rw-r--r--data/deprecations/14-10-manual-iteration-management.yml34
-rw-r--r--data/deprecations/14-5-certificate-based-integration-with-kubernetes-saas.yml6
-rw-r--r--data/deprecations/14-5-certificate-based-integration-with-kubernetes.yml6
-rw-r--r--data/deprecations/15-3-vulnerabilityFindingDismiss-mutation.yml23
-rw-r--r--data/deprecations/15-4-confidence-field-in-graphql.yml14
-rw-r--r--data/deprecations/15-4-create-deprecation-draft-quick-action-toggle.yml12
-rw-r--r--data/deprecations/15-4-cs-docker-variables.yml11
-rw-r--r--data/deprecations/15-4-deprecate-bundled-grafana.yml17
-rw-r--r--data/deprecations/15-4-non-expiring-access-tokens.yml25
-rw-r--r--data/deprecations/15-4-starboard-directive.yml11
-rw-r--r--data/deprecations/16-0-security_report_schemas_v14-x-x.yml18
-rw-r--r--data/deprecations/templates/_deprecation_template.md.erb2
-rw-r--r--data/deprecations/templates/example.yml15
-rw-r--r--data/removals/15_0/15-0-remove-background-upload-object-storage.yml5
-rw-r--r--data/removals/15_3/15-3-vulnerability-report-state-sort.yml25
-rw-r--r--data/removals/15_3/15-3-vulnerability-report-tool-sort.yml26
-rw-r--r--data/removals/15_3/removal_debian9.yml6
-rw-r--r--data/removals/15_4/15-4-sast-analyzer-consolidation.yml30
-rw-r--r--data/removals/templates/example.yml2
-rw-r--r--db/docs/analytics_cycle_analytics_aggregations.yml2
-rw-r--r--db/docs/analytics_cycle_analytics_group_value_streams.yml2
-rw-r--r--db/docs/analytics_cycle_analytics_issue_stage_events.yml2
-rw-r--r--db/docs/analytics_cycle_analytics_merge_request_stage_events.yml2
-rw-r--r--db/docs/analytics_cycle_analytics_project_stages.yml2
-rw-r--r--db/docs/analytics_cycle_analytics_stage_event_hashes.yml2
-rw-r--r--db/docs/analytics_devops_adoption_segments.yml2
-rw-r--r--db/docs/analytics_devops_adoption_snapshots.yml2
-rw-r--r--db/docs/analytics_language_trend_repository_languages.yml4
-rw-r--r--db/docs/analytics_usage_trends_measurements.yml2
-rw-r--r--db/docs/ci_partitions.yml9
-rw-r--r--db/docs/conversational_development_index_metrics.yml2
-rw-r--r--db/docs/dora_configurations.yml9
-rw-r--r--db/docs/dora_daily_metrics.yml4
-rw-r--r--db/docs/events.yml4
-rw-r--r--db/docs/fork_networks.yml4
-rw-r--r--db/docs/ghost_user_migrations.yml9
-rw-r--r--db/docs/insights.yml2
-rw-r--r--db/docs/issue_metrics.yml2
-rw-r--r--db/docs/merge_request_metrics.yml2
-rw-r--r--db/docs/merge_request_predictions.yml9
-rw-r--r--db/docs/onboarding_progresses.yml2
-rw-r--r--db/docs/packages_rpm_metadata.yml9
-rw-r--r--db/docs/protected_environment_deploy_access_levels.yml2
-rw-r--r--db/docs/sbom_vulnerable_component_versions.yml11
-rw-r--r--db/docs/spam_logs.yml6
-rw-r--r--db/docs/vulnerability_advisories.yml11
-rw-r--r--db/fixtures/development/044_add_security_training_providers.rb5
-rw-r--r--db/fixtures/development/14_pipelines.rb6
-rw-r--r--db/fixtures/development/17_cycle_analytics.rb2
-rw-r--r--db/fixtures/development/24_forks.rb2
-rw-r--r--db/fixtures/development/33_triage_ops.rb66
-rw-r--r--db/fixtures/development/98_gitlab_instance_administration_project.rb5
-rw-r--r--db/fixtures/production/004_add_security_training_providers.rb5
-rw-r--r--db/migrate/20220406193806_add_maven_package_requests_forwarding_to_application_settings.rb13
-rw-r--r--db/migrate/20220603125200_add_show_diff_preview_in_email_to_namespace_settings.rb9
-rw-r--r--db/migrate/20220711142148_add_invitation_flow_enforcement_to_application_settings.rb10
-rw-r--r--db/migrate/20220726171440_create_ghost_user_migrations.rb12
-rw-r--r--db/migrate/20220726171450_add_user_fk_to_ghost_user_migrations.rb15
-rw-r--r--db/migrate/20220803004853_add_auto_ban_user_to_namespace_settings.rb10
-rw-r--r--db/migrate/20220803235114_add_auto_ban_user_to_application_settings.rb8
-rw-r--r--db/migrate/20220815152905_create_vulnerability_advisories.rb25
-rw-r--r--db/migrate/20220816135816_create_sbom_vulnerable_component_versions.rb18
-rw-r--r--db/migrate/20220817122907_re_add_show_diff_preview_in_email_to_project_settings.rb9
-rw-r--r--db/migrate/20220818095225_add_max_pages_custom_domains_per_project.rb20
-rw-r--r--db/migrate/20220818132108_add_deleted_on_to_ml_experiments.rb7
-rw-r--r--db/migrate/20220819153725_add_vulnerability_advisory_foreign_key_to_sbom_vulnerable_component_versions.rb19
-rw-r--r--db/migrate/20220819162852_add_sbom_component_version_foreign_key_to_sbom_vulnerable_component_versions.rb19
-rw-r--r--db/migrate/20220822102651_add_namespace_id_to_broadcast_message.rb9
-rw-r--r--db/migrate/20220822103638_add_index_and_foreign_key_to_broadcast_message.rb17
-rw-r--r--db/migrate/20220824175648_limit_namespaces_sync_triggers_to_traversal_ids_update.rb35
-rw-r--r--db/migrate/20220824194103_remove_existing_work_item_type_backfill_migrations.rb22
-rw-r--r--db/migrate/20220825105631_add_cube_api_key_to_application_settings.rb11
-rw-r--r--db/migrate/20220825134827_remove_not_null_constraint_for_confidence_columns.rb15
-rw-r--r--db/migrate/20220828094411_add_rpm_max_file_size_to_plan_limits.rb9
-rw-r--r--db/migrate/20220828131848_create_packages_rpm_metadata.rb30
-rw-r--r--db/migrate/20220829183356_replace_index_on_credit_card_validations.rb20
-rw-r--r--db/migrate/20220830082928_add_text_limit_to_cube_api_base_url.rb13
-rw-r--r--db/migrate/20220830114228_create_dora_configuration_table.rb16
-rw-r--r--db/migrate/20220831182105_add_constraints_view.rb32
-rw-r--r--db/migrate/20220901035722_add_temp_project_member_index.rb16
-rw-r--r--db/migrate/20220901124637_add_last_downloaded_at_to_packages.rb7
-rw-r--r--db/migrate/20220901131828_add_environments_project_name_lower_pattern_ops_index.rb15
-rw-r--r--db/migrate/20220901212027_add_merge_request_id_to_environments.rb7
-rw-r--r--db/migrate/20220902065314_create_ci_partitions.rb9
-rw-r--r--db/migrate/20220902065316_create_default_partition_record.rb21
-rw-r--r--db/migrate/20220902065317_add_partition_id_to_ci_builds.rb11
-rw-r--r--db/migrate/20220902065558_add_partition_id_to_ci_builds_metadata.rb9
-rw-r--r--db/migrate/20220902065611_add_partition_id_to_ci_job_artifacts.rb9
-rw-r--r--db/migrate/20220902065623_add_partition_id_to_ci_pipelines.rb9
-rw-r--r--db/migrate/20220902065635_add_partition_id_to_ci_stages.rb9
-rw-r--r--db/migrate/20220902065647_add_partition_id_to_ci_pipeline_variables.rb9
-rw-r--r--db/migrate/20220902165931_index_evironments_on_merge_request_id.rb15
-rw-r--r--db/migrate/20220902170131_add_fk_constraint_to_environments_merge_request_id.rb13
-rw-r--r--db/migrate/20220906093857_add_column_branch_filter_strategy_to_web_hooks.rb7
-rw-r--r--db/migrate/20220906155105_add_start_time_and_end_time_and_status_to_ml_candidates.rb9
-rw-r--r--db/migrate/20220906204832_add_locked_to_ci_pipeline_artifacts.rb20
-rw-r--r--db/migrate/20220907124320_add_internal_to_notes.rb9
-rw-r--r--db/migrate/20220909091410_add_dismissal_reason_to_vulnerability_state_transitions.rb7
-rw-r--r--db/migrate/20220909094752_add_free_user_cap_over_limt_notified_at_to_namespace_details.rb20
-rw-r--r--db/migrate/20220909113809_add_environments_project_name_lower_pattern_ops_state_index.rb15
-rw-r--r--db/migrate/20220912180807_add_epoch_column_to_rpm_metadata.rb7
-rw-r--r--db/migrate/20220913082930_rename_iterations_cadences_last_run_date_to_next_run_date.rb13
-rw-r--r--db/migrate/20220914005141_change_namespace_id_not_null_in_members.rb13
-rw-r--r--db/migrate/20220914010233_change_members_namespace_foreign_key_on_delete_constraint.rb30
-rw-r--r--db/migrate/20220915140802_create_merge_request_predictions.rb20
-rw-r--r--db/post_migrate/20210812013042_remove_duplicate_project_authorizations.rb3
-rw-r--r--db/post_migrate/20220601110011_schedule_remove_self_managed_wiki_notes.rb35
-rw-r--r--db/post_migrate/20220606054503_add_tmp_index_job_artifacts_id_and_expire_at.rb29
-rw-r--r--db/post_migrate/20220606080509_fix_incorrect_job_artifacts_expire_at.rb31
-rw-r--r--db/post_migrate/20220615154500_schedule_backfill_cluster_agents_has_vulnerabilities.rb30
-rw-r--r--db/post_migrate/20220616171355_update_vulnerabilities_project_id_id_active_cis_index.rb24
-rw-r--r--db/post_migrate/20220706145113_backfill_namespace_id_on_issues.rb28
-rw-r--r--db/post_migrate/20220707192420_remove_tmp_idx_merge_requests_draft_and_status.rb18
-rw-r--r--db/post_migrate/20220726225114_remove_tmp_index_group_membership_namespace_id_column.rb17
-rw-r--r--db/post_migrate/20220801155858_schedule_disable_legacy_open_source_licence_for_recent_public_projects.rb33
-rw-r--r--db/post_migrate/20220809002011_schedule_destroy_invalid_group_members.rb30
-rw-r--r--db/post_migrate/20220809214730_add_note_metadata_temp_index_on_id_where_task.rb15
-rw-r--r--db/post_migrate/20220809223215_change_task_system_note_wording_to_checklist_item.rb28
-rw-r--r--db/post_migrate/20220815061621_rename_web_hooks_service_id_to_integration_id.rb13
-rw-r--r--db/post_migrate/20220816075638_drop_uuid_and_id_index_from_security_findings.rb15
-rw-r--r--db/post_migrate/20220816163444_update_start_date_for_iterations_cadences.rb53
-rw-r--r--db/post_migrate/20220820221036_update_tmp_non_migrated_index_on_container_repositories.rb25
-rw-r--r--db/post_migrate/20220822071909_remove_other_role_from_user_details.rb9
-rw-r--r--db/post_migrate/20220822090656_drop_build_coverage_regex_from_project.rb13
-rw-r--r--db/post_migrate/20220822094804_add_issues_authorization_index.rb15
-rw-r--r--db/post_migrate/20220823084747_prepare_removal_partial_trigram_indexes_for_issues.rb16
-rw-r--r--db/post_migrate/20220824114218_add_tmp_index_approval_merge_request_rules.rb19
-rw-r--r--db/post_migrate/20220825061250_drop_tmp_index_todos_attention_request_action_idx.rb18
-rw-r--r--db/post_migrate/20220825142324_replace_issues_authorization_index.rb18
-rw-r--r--db/post_migrate/20220826165048_drop_temporary_job_trace_index.rb13
-rw-r--r--db/post_migrate/20220826175058_fully_remove_temporary_job_trace_index.rb15
-rw-r--r--db/post_migrate/20220830051704_add_temporary_index_for_orphaned_invited_members.rb21
-rw-r--r--db/post_migrate/20220830061704_orphaned_invited_members_cleanup.rb29
-rw-r--r--db/post_migrate/20220830071704_remove_temporary_index_for_orphaned_invited_members.rb21
-rw-r--r--db/post_migrate/20220830172142_reschedule_issue_work_item_type_id_backfill.rb40
-rw-r--r--db/post_migrate/20220831021358_add_index_on_issue_health_status.rb16
-rw-r--r--db/post_migrate/20220831132802_delete_approval_rules_for_vulnerability.rb42
-rw-r--r--db/post_migrate/20220901035725_schedule_destroy_invalid_project_members.rb28
-rw-r--r--db/post_migrate/20220901071310_add_tmp_index_user_callouts_on_attention_request_feature_names.rb18
-rw-r--r--db/post_migrate/20220901071355_cleanup_attention_request_user_callouts.rb18
-rw-r--r--db/post_migrate/20220901073300_remove_partial_trigram_indexes_for_issues.rb25
-rw-r--r--db/post_migrate/20220901184106_add_not_null_to_board_group_recent_visits.rb17
-rw-r--r--db/post_migrate/20220901184246_add_not_null_to_board_project_recent_visits.rb17
-rw-r--r--db/post_migrate/20220902111016_delete_null_records_from_board_group_recent_visits.rb15
-rw-r--r--db/post_migrate/20220902111038_delete_null_records_from_board_project_recent_visits.rb15
-rw-r--r--db/post_migrate/20220902204048_move_security_findings_table_to_gitlab_partitions_dynamic_schema.rb232
-rw-r--r--db/post_migrate/20220904173342_validate_not_null_constraint_board_group_recent_visits.rb15
-rw-r--r--db/post_migrate/20220904173430_validate_not_null_constraint_board_project_recent_visits.rb15
-rw-r--r--db/post_migrate/20220905090300_add_tmp_index_merge_request_reviewers_attention_request_state.rb18
-rw-r--r--db/post_migrate/20220905090339_reset_attention_requested_merge_requests_reviewers_state_to_unreviewed.rb30
-rw-r--r--db/post_migrate/20220905112710_add_async_index_to_todos_to_cover_pending_query.rb16
-rw-r--r--db/post_migrate/20220906074449_schedule_disable_legacy_open_source_license_for_projects_less_than_one_mb.rb33
-rw-r--r--db/post_migrate/20220906212931_add_partial_index_for_ci_pipeline_artifacts_unlocked_with_expire_at.rb17
-rw-r--r--db/post_migrate/20220908125146_remove_free_user_cap_remediation_worker.rb11
-rw-r--r--db/post_migrate/20220909114220_drop_environments_project_name_lower_pattern_ops_index.rb16
-rw-r--r--db/post_migrate/20220912085047_add_index_to_todos_pending_query.rb16
-rw-r--r--db/post_migrate/20220912110433_prepare_create_internal_notes_index_on_id.rb13
-rw-r--r--db/post_migrate/20220913030552_add_tmp_index_system_note_metadata_on_attention_request_actions.rb17
-rw-r--r--db/post_migrate/20220913030624_cleanup_attention_request_related_system_notes.rb30
-rw-r--r--db/post_migrate/20220913082728_drop_index_cadence_create_iterations_automation.rb21
-rw-r--r--db/post_migrate/20220913083015_clean_up_rename_iterations_cadences_last_run_date_to_next_run_date.rb13
-rw-r--r--db/post_migrate/20220914093408_add_unique_id_partition_id_index_to_ci_build_metadata.rb14
-rw-r--r--db/post_migrate/20220915103831_add_unique_build_id_partition_id_index_to_ci_build_metadata.rb14
-rw-r--r--db/post_migrate/20220916112841_remove_unused_aggregation_columns.rb59
-rw-r--r--db/schema_migrations/202204061938061
-rw-r--r--db/schema_migrations/202206011100111
-rw-r--r--db/schema_migrations/202206031252001
-rw-r--r--db/schema_migrations/202206060545031
-rw-r--r--db/schema_migrations/202206060805091
-rw-r--r--db/schema_migrations/202206151545001
-rw-r--r--db/schema_migrations/202206161713551
-rw-r--r--db/schema_migrations/202207061451131
-rw-r--r--db/schema_migrations/202207071924201
-rw-r--r--db/schema_migrations/202207111421481
-rw-r--r--db/schema_migrations/202207261714401
-rw-r--r--db/schema_migrations/202207261714501
-rw-r--r--db/schema_migrations/202207262251141
-rw-r--r--db/schema_migrations/202208011558581
-rw-r--r--db/schema_migrations/202208030048531
-rw-r--r--db/schema_migrations/202208032351141
-rw-r--r--db/schema_migrations/202208090020111
-rw-r--r--db/schema_migrations/202208092147301
-rw-r--r--db/schema_migrations/202208092232151
-rw-r--r--db/schema_migrations/202208150616211
-rw-r--r--db/schema_migrations/202208151529051
-rw-r--r--db/schema_migrations/202208160756381
-rw-r--r--db/schema_migrations/202208161358161
-rw-r--r--db/schema_migrations/202208161634441
-rw-r--r--db/schema_migrations/202208171229071
-rw-r--r--db/schema_migrations/202208180952251
-rw-r--r--db/schema_migrations/202208181321081
-rw-r--r--db/schema_migrations/202208191537251
-rw-r--r--db/schema_migrations/202208191628521
-rw-r--r--db/schema_migrations/202208202210361
-rw-r--r--db/schema_migrations/202208220719091
-rw-r--r--db/schema_migrations/202208220906561
-rw-r--r--db/schema_migrations/202208220948041
-rw-r--r--db/schema_migrations/202208221026511
-rw-r--r--db/schema_migrations/202208221036381
-rw-r--r--db/schema_migrations/202208230847471
-rw-r--r--db/schema_migrations/202208241142181
-rw-r--r--db/schema_migrations/202208241756481
-rw-r--r--db/schema_migrations/202208241941031
-rw-r--r--db/schema_migrations/202208250612501
-rw-r--r--db/schema_migrations/202208251056311
-rw-r--r--db/schema_migrations/202208251348271
-rw-r--r--db/schema_migrations/202208251423241
-rw-r--r--db/schema_migrations/202208261650481
-rw-r--r--db/schema_migrations/202208261750581
-rw-r--r--db/schema_migrations/202208280944111
-rw-r--r--db/schema_migrations/202208281318481
-rw-r--r--db/schema_migrations/202208291833561
-rw-r--r--db/schema_migrations/202208300517041
-rw-r--r--db/schema_migrations/202208300617041
-rw-r--r--db/schema_migrations/202208300717041
-rw-r--r--db/schema_migrations/202208300829281
-rw-r--r--db/schema_migrations/202208301142281
-rw-r--r--db/schema_migrations/202208301721421
-rw-r--r--db/schema_migrations/202208310213581
-rw-r--r--db/schema_migrations/202208311328021
-rw-r--r--db/schema_migrations/202208311821051
-rw-r--r--db/schema_migrations/202209010357221
-rw-r--r--db/schema_migrations/202209010357251
-rw-r--r--db/schema_migrations/202209010713101
-rw-r--r--db/schema_migrations/202209010713551
-rw-r--r--db/schema_migrations/202209010733001
-rw-r--r--db/schema_migrations/202209011246371
-rw-r--r--db/schema_migrations/202209011318281
-rw-r--r--db/schema_migrations/202209011841061
-rw-r--r--db/schema_migrations/202209011842461
-rw-r--r--db/schema_migrations/202209012120271
-rw-r--r--db/schema_migrations/202209020653141
-rw-r--r--db/schema_migrations/202209020653161
-rw-r--r--db/schema_migrations/202209020653171
-rw-r--r--db/schema_migrations/202209020655581
-rw-r--r--db/schema_migrations/202209020656111
-rw-r--r--db/schema_migrations/202209020656231
-rw-r--r--db/schema_migrations/202209020656351
-rw-r--r--db/schema_migrations/202209020656471
-rw-r--r--db/schema_migrations/202209021110161
-rw-r--r--db/schema_migrations/202209021110381
-rw-r--r--db/schema_migrations/202209021659311
-rw-r--r--db/schema_migrations/202209021701311
-rw-r--r--db/schema_migrations/202209022040481
-rw-r--r--db/schema_migrations/202209041733421
-rw-r--r--db/schema_migrations/202209041734301
-rw-r--r--db/schema_migrations/202209050903001
-rw-r--r--db/schema_migrations/202209050903391
-rw-r--r--db/schema_migrations/202209051127101
-rw-r--r--db/schema_migrations/202209060744491
-rw-r--r--db/schema_migrations/202209060938571
-rw-r--r--db/schema_migrations/202209061551051
-rw-r--r--db/schema_migrations/202209062048321
-rw-r--r--db/schema_migrations/202209062129311
-rw-r--r--db/schema_migrations/202209071243201
-rw-r--r--db/schema_migrations/202209081251461
-rw-r--r--db/schema_migrations/202209090914101
-rw-r--r--db/schema_migrations/202209090947521
-rw-r--r--db/schema_migrations/202209091138091
-rw-r--r--db/schema_migrations/202209091142201
-rw-r--r--db/schema_migrations/202209120850471
-rw-r--r--db/schema_migrations/202209121104331
-rw-r--r--db/schema_migrations/202209121808071
-rw-r--r--db/schema_migrations/202209130305521
-rw-r--r--db/schema_migrations/202209130306241
-rw-r--r--db/schema_migrations/202209130827281
-rw-r--r--db/schema_migrations/202209130829301
-rw-r--r--db/schema_migrations/202209130830151
-rw-r--r--db/schema_migrations/202209140051411
-rw-r--r--db/schema_migrations/202209140102331
-rw-r--r--db/schema_migrations/202209140934081
-rw-r--r--db/schema_migrations/202209151038311
-rw-r--r--db/schema_migrations/202209151408021
-rw-r--r--db/schema_migrations/202209161128411
-rw-r--r--db/structure.sql460
-rw-r--r--doc/.vale/gitlab/British.yml3
-rw-r--r--doc/.vale/gitlab/InternalLinkExtension.yml4
-rw-r--r--doc/.vale/gitlab/Markdown_emoji.yml13
-rw-r--r--doc/administration/application_settings_cache.md8
-rw-r--r--doc/administration/audit_event_streaming.md42
-rw-r--r--doc/administration/audit_events.md8
-rw-r--r--doc/administration/audit_reports.md2
-rw-r--r--doc/administration/auditor_users.md2
-rw-r--r--doc/administration/auth/atlassian.md5
-rw-r--r--doc/administration/auth/index.md28
-rw-r--r--doc/administration/auth/jwt.md5
-rw-r--r--doc/administration/auth/ldap/index.md2
-rw-r--r--doc/administration/auth/ldap/ldap-troubleshooting.md131
-rw-r--r--doc/administration/auth/ldap/ldap_synchronization.md10
-rw-r--r--doc/administration/auth/oidc.md2
-rw-r--r--doc/administration/compliance.md2
-rw-r--r--doc/administration/environment_variables.md1
-rw-r--r--doc/administration/external_pipeline_validation.md12
-rw-r--r--doc/administration/feature_flags.md5
-rw-r--r--doc/administration/geo/disaster_recovery/background_verification.md8
-rw-r--r--doc/administration/geo/disaster_recovery/bring_primary_back.md2
-rw-r--r--doc/administration/geo/disaster_recovery/index.md4
-rw-r--r--doc/administration/geo/disaster_recovery/planned_failover.md10
-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.md8
-rw-r--r--doc/administration/geo/index.md6
-rw-r--r--doc/administration/geo/replication/configuration.md14
-rw-r--r--doc/administration/geo/replication/container_registry.md8
-rw-r--r--doc/administration/geo/replication/disable_geo.md2
-rw-r--r--doc/administration/geo/replication/object_storage.md2
-rw-r--r--doc/administration/geo/replication/remove_geo_site.md2
-rw-r--r--doc/administration/geo/replication/troubleshooting.md190
-rw-r--r--doc/administration/geo/replication/tuning.md2
-rw-r--r--doc/administration/geo/replication/version_specific_upgrades.md15
-rw-r--r--doc/administration/geo/secondary_proxy/index.md14
-rw-r--r--doc/administration/gitaly/configure_gitaly.md7
-rw-r--r--doc/administration/gitaly/index.md23
-rw-r--r--doc/administration/gitaly/praefect.md17
-rw-r--r--doc/administration/gitaly/recovery.md38
-rw-r--r--doc/administration/gitaly/troubleshooting.md2
-rw-r--r--doc/administration/housekeeping.md2
-rw-r--r--doc/administration/inactive_project_deletion.md14
-rw-r--r--doc/administration/index.md2
-rw-r--r--doc/administration/instance_limits.md24
-rw-r--r--doc/administration/integration/kroki.md2
-rw-r--r--doc/administration/integration/mailgun.md2
-rw-r--r--doc/administration/integration/plantuml.md2
-rw-r--r--doc/administration/integration/terminal.md6
-rw-r--r--doc/administration/job_artifacts.md15
-rw-r--r--doc/administration/job_logs.md4
-rw-r--r--doc/administration/load_balancer.md30
-rw-r--r--doc/administration/maintenance_mode/index.md6
-rw-r--r--doc/administration/monitoring/gitlab_self_monitoring_project/index.md6
-rw-r--r--doc/administration/monitoring/performance/gitlab_configuration.md2
-rw-r--r--doc/administration/monitoring/performance/grafana_configuration.md6
-rw-r--r--doc/administration/monitoring/performance/performance_bar.md2
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md6
-rw-r--r--doc/administration/monitoring/prometheus/index.md5
-rw-r--r--doc/administration/nfs.md30
-rw-r--r--doc/administration/object_storage.md10
-rw-r--r--doc/administration/operations/fast_ssh_key_lookup.md4
-rw-r--r--doc/administration/operations/index.md2
-rw-r--r--doc/administration/operations/puma.md6
-rw-r--r--doc/administration/operations/rails_console.md53
-rw-r--r--doc/administration/operations/ssh_certificates.md2
-rw-r--r--doc/administration/package_information/supported_os.md1
-rw-r--r--doc/administration/packages/container_registry.md12
-rw-r--r--doc/administration/pages/index.md52
-rw-r--r--doc/administration/pages/source.md7
-rw-r--r--doc/administration/polling.md2
-rw-r--r--doc/administration/raketasks/maintenance.md18
-rw-r--r--doc/administration/raketasks/project_import_export.md2
-rw-r--r--doc/administration/raketasks/storage.md2
-rw-r--r--doc/administration/raketasks/uploads/migrate.md3
-rw-r--r--doc/administration/redis/replication_and_failover.md2
-rw-r--r--doc/administration/reference_architectures/10k_users.md189
-rw-r--r--doc/administration/reference_architectures/1k_users.md10
-rw-r--r--doc/administration/reference_architectures/25k_users.md198
-rw-r--r--doc/administration/reference_architectures/2k_users.md173
-rw-r--r--doc/administration/reference_architectures/3k_users.md186
-rw-r--r--doc/administration/reference_architectures/50k_users.md193
-rw-r--r--doc/administration/reference_architectures/5k_users.md179
-rw-r--r--doc/administration/repository_checks.md16
-rw-r--r--doc/administration/repository_storage_paths.md2
-rw-r--r--doc/administration/repository_storage_types.md2
-rw-r--r--doc/administration/restart_gitlab.md2
-rw-r--r--doc/administration/server_hooks.md37
-rw-r--r--doc/administration/sidekiq/extra_sidekiq_processes.md2
-rw-r--r--doc/administration/static_objects_external_storage.md2
-rw-r--r--doc/administration/system_hooks.md2
-rw-r--r--doc/administration/terraform_state.md4
-rw-r--r--doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md166
-rw-r--r--doc/administration/troubleshooting/linux_cheat_sheet.md2
-rw-r--r--doc/administration/troubleshooting/ssl.md12
-rw-r--r--doc/administration/troubleshooting/tracing_correlation_id.md2
-rw-r--r--doc/administration/user_settings.md13
-rw-r--r--doc/administration/whats-new.md46
-rw-r--r--doc/api/api_resources.md12
-rw-r--r--doc/api/audit_events.md2
-rw-r--r--doc/api/container_registry.md5
-rw-r--r--doc/api/custom_attributes.md4
-rw-r--r--doc/api/deployments.md9
-rw-r--r--doc/api/discussions.md32
-rw-r--r--doc/api/dora/metrics.md3
-rw-r--r--doc/api/environments.md6
-rw-r--r--doc/api/events.md2
-rw-r--r--doc/api/graphql/audit_report.md4
-rw-r--r--doc/api/graphql/getting_started.md11
-rw-r--r--doc/api/graphql/reference/index.md669
-rw-r--r--doc/api/graphql/users_example.md4
-rw-r--r--doc/api/group_protected_environments.md177
-rw-r--r--doc/api/group_repository_storage_moves.md5
-rw-r--r--doc/api/groups.md146
-rw-r--r--doc/api/integrations.md30
-rw-r--r--doc/api/issues.md54
-rw-r--r--doc/api/job_artifacts.md4
-rw-r--r--doc/api/jobs.md25
-rw-r--r--doc/api/members.md28
-rw-r--r--doc/api/merge_request_approvals.md203
-rw-r--r--doc/api/merge_request_context_commits.md32
-rw-r--r--doc/api/merge_requests.md554
-rw-r--r--doc/api/metadata.md4
-rw-r--r--doc/api/oauth2.md45
-rw-r--r--doc/api/packages.md1
-rw-r--r--doc/api/packages/conan.md2
-rw-r--r--doc/api/packages/debian.md67
-rw-r--r--doc/api/packages/terraform-modules.md6
-rw-r--r--doc/api/personal_access_tokens.md10
-rw-r--r--doc/api/pipeline_triggers.md2
-rw-r--r--doc/api/pipelines.md4
-rw-r--r--doc/api/product_analytics.md67
-rw-r--r--doc/api/project_level_variables.md57
-rw-r--r--doc/api/project_templates.md16
-rw-r--r--doc/api/projects.md71
-rw-r--r--doc/api/protected_environments.md237
-rw-r--r--doc/api/releases/index.md2
-rw-r--r--doc/api/repositories.md2
-rw-r--r--doc/api/resource_label_events.md2
-rw-r--r--doc/api/resource_state_events.md106
-rw-r--r--doc/api/settings.md12
-rw-r--r--doc/api/status_checks.md2
-rw-r--r--doc/api/system_hooks.md2
-rw-r--r--doc/api/tags.md12
-rw-r--r--doc/api/topics.md42
-rw-r--r--doc/api/version.md4
-rw-r--r--doc/api/vulnerabilities.md2
-rw-r--r--doc/api/vulnerability_exports.md2
-rw-r--r--doc/api/vulnerability_findings.md2
-rw-r--r--doc/architecture/blueprints/_template.md142
-rw-r--r--doc/architecture/blueprints/ci_data_decay/index.md6
-rw-r--r--doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md202
-rw-r--r--doc/architecture/blueprints/ci_pipeline_components/index.md209
-rw-r--r--doc/architecture/blueprints/ci_scale/index.md8
-rw-r--r--doc/architecture/blueprints/cloud_native_build_logs/index.md6
-rw-r--r--doc/architecture/blueprints/cloud_native_gitlab_pages/index.md6
-rw-r--r--doc/architecture/blueprints/database_scaling/size-limits.md2
-rw-r--r--doc/architecture/blueprints/feature_flags_development/index.md6
-rw-r--r--doc/architecture/blueprints/graphql_api/index.md4
-rw-r--r--doc/architecture/blueprints/object_storage/index.md12
-rw-r--r--doc/architecture/blueprints/pods/index.md162
-rw-r--r--doc/architecture/blueprints/rate_limiting/index.md411
-rw-r--r--doc/architecture/blueprints/runner_scaling/index.md4
-rw-r--r--doc/ci/caching/index.md23
-rw-r--r--doc/ci/ci_cd_for_external_repos/bitbucket_integration.md3
-rw-r--r--doc/ci/ci_cd_for_external_repos/github_integration.md6
-rw-r--r--doc/ci/ci_cd_for_external_repos/index.md13
-rw-r--r--doc/ci/cloud_deployment/ecs/deploy_to_aws_ecs.md2
-rw-r--r--doc/ci/cloud_deployment/index.md12
-rw-r--r--doc/ci/cloud_services/azure/index.md157
-rw-r--r--doc/ci/cloud_services/index.md12
-rw-r--r--doc/ci/docker/using_docker_build.md4
-rw-r--r--doc/ci/docker/using_docker_images.md2
-rw-r--r--doc/ci/docker/using_kaniko.md37
-rw-r--r--doc/ci/enable_or_disable_ci.md4
-rw-r--r--doc/ci/environments/deployment_approvals.md7
-rw-r--r--doc/ci/environments/deployment_safety.md6
-rw-r--r--doc/ci/environments/environments_dashboard.md2
-rw-r--r--doc/ci/environments/index.md16
-rw-r--r--doc/ci/environments/protected_environments.md8
-rw-r--r--doc/ci/examples/deployment/index.md6
-rw-r--r--doc/ci/examples/end_to_end_testing_webdriverio/index.md1
-rw-r--r--doc/ci/examples/php.md2
-rw-r--r--doc/ci/examples/semantic-release.md2
-rw-r--r--doc/ci/index.md60
-rw-r--r--doc/ci/interactive_web_terminal/index.md6
-rw-r--r--doc/ci/jobs/ci_job_token.md9
-rw-r--r--doc/ci/jobs/index.md2
-rw-r--r--doc/ci/jobs/job_control.md38
-rw-r--r--doc/ci/lint.md4
-rw-r--r--doc/ci/migration/circleci.md3
-rw-r--r--doc/ci/migration/jenkins.md2
-rw-r--r--doc/ci/mobile_devops.md42
-rw-r--r--doc/ci/pipeline_editor/index.md32
-rw-r--r--doc/ci/pipelines/cicd_minutes.md25
-rw-r--r--doc/ci/pipelines/downstream_pipelines.md647
-rw-r--r--doc/ci/pipelines/img/downstream_pipeline_actions.pngbin13406 -> 0 bytes
-rw-r--r--doc/ci/pipelines/index.md98
-rw-r--r--doc/ci/pipelines/job_artifacts.md64
-rw-r--r--doc/ci/pipelines/merge_request_pipelines.md25
-rw-r--r--doc/ci/pipelines/merge_trains.md9
-rw-r--r--doc/ci/pipelines/merged_results_pipelines.md7
-rw-r--r--doc/ci/pipelines/multi_project_pipelines.md422
-rw-r--r--doc/ci/pipelines/parent_child_pipelines.md231
-rw-r--r--doc/ci/pipelines/pipeline_architectures.md8
-rw-r--r--doc/ci/pipelines/pipeline_efficiency.md2
-rw-r--r--doc/ci/pipelines/schedules.md8
-rw-r--r--doc/ci/pipelines/settings.md22
-rw-r--r--doc/ci/quick_start/index.md1
-rw-r--r--doc/ci/resource_groups/index.md6
-rw-r--r--doc/ci/review_apps/index.md2
-rw-r--r--doc/ci/runners/configure_runners.md48
-rw-r--r--doc/ci/runners/index.md4
-rw-r--r--doc/ci/runners/runners_scope.md22
-rw-r--r--doc/ci/runners/saas/linux_saas_runner.md105
-rw-r--r--doc/ci/secure_files/index.md8
-rw-r--r--doc/ci/services/index.md9
-rw-r--r--doc/ci/testing/unit_test_reports.md2
-rw-r--r--doc/ci/triggers/index.md9
-rw-r--r--doc/ci/troubleshooting.md44
-rw-r--r--doc/ci/variables/index.md26
-rw-r--r--doc/ci/variables/predefined_variables.md7
-rw-r--r--doc/ci/variables/where_variables_can_be_used.md3
-rw-r--r--doc/ci/yaml/artifacts_reports.md67
-rw-r--r--doc/ci/yaml/index.md180
-rw-r--r--doc/ci/yaml/script.md2
-rw-r--r--doc/ci/yaml/workflow.md2
-rw-r--r--doc/cloud_seed/index.md49
-rw-r--r--doc/development/api_graphql_styleguide.md92
-rw-r--r--doc/development/api_styleguide.md6
-rw-r--r--doc/development/application_secrets.md1
-rw-r--r--doc/development/application_slis/index.md14
-rw-r--r--doc/development/architecture.md2
-rw-r--r--doc/development/audit_event_guide/index.md44
-rw-r--r--doc/development/auto_devops.md2
-rw-r--r--doc/development/backend/create_source_code_be/index.md3
-rw-r--r--doc/development/backend/ruby_style_guide.md114
-rw-r--r--doc/development/build_test_package.md2
-rw-r--r--doc/development/changelog.md2
-rw-r--r--doc/development/cicd/index.md2
-rw-r--r--doc/development/cicd/pipeline_wizard.md15
-rw-r--r--doc/development/cicd/templates.md5
-rw-r--r--doc/development/code_intelligence/index.md2
-rw-r--r--doc/development/code_review.md142
-rw-r--r--doc/development/contributing/index.md12
-rw-r--r--doc/development/contributing/merge_request_workflow.md104
-rw-r--r--doc/development/contributing/style_guides.md37
-rw-r--r--doc/development/database/add_foreign_key_to_existing_column.md2
-rw-r--r--doc/development/database/adding_database_indexes.md16
-rw-r--r--doc/development/database/batched_background_migrations.md6
-rw-r--r--doc/development/database/ci_mirrored_tables.md4
-rw-r--r--doc/development/database/client_side_connection_pool.md4
-rw-r--r--doc/development/database/database_debugging.md2
-rw-r--r--doc/development/database/loose_foreign_keys.md2
-rw-r--r--doc/development/database/multiple_databases.md2
-rw-r--r--doc/development/database/not_null_constraints.md9
-rw-r--r--doc/development/database/ordering_table_columns.md2
-rw-r--r--doc/development/database/strings_and_the_text_data_type.md2
-rw-r--r--doc/development/database/understanding_explain_plans.md18
-rw-r--r--doc/development/database_review.md14
-rw-r--r--doc/development/deprecation_guidelines/index.md13
-rw-r--r--doc/development/development_processes.md2
-rw-r--r--doc/development/distributed_tracing.md8
-rw-r--r--doc/development/documentation/feature_flags.md3
-rw-r--r--doc/development/documentation/index.md49
-rw-r--r--doc/development/documentation/redirects.md168
-rw-r--r--doc/development/documentation/restful_api_styleguide.md40
-rw-r--r--doc/development/documentation/review_apps.md2
-rw-r--r--doc/development/documentation/site_architecture/deployment_process.md9
-rw-r--r--doc/development/documentation/site_architecture/index.md2
-rw-r--r--doc/development/documentation/structure.md398
-rw-r--r--doc/development/documentation/styleguide/index.md50
-rw-r--r--doc/development/documentation/styleguide/word_list.md44
-rw-r--r--doc/development/documentation/testing.md2
-rw-r--r--doc/development/documentation/topic_types/concept.md46
-rw-r--r--doc/development/documentation/topic_types/index.md130
-rw-r--r--doc/development/documentation/topic_types/reference.md32
-rw-r--r--doc/development/documentation/topic_types/task.md65
-rw-r--r--doc/development/documentation/topic_types/troubleshooting.md56
-rw-r--r--doc/development/documentation/versions.md9
-rw-r--r--doc/development/ee_features.md12
-rw-r--r--doc/development/elasticsearch.md8
-rw-r--r--doc/development/emails.md6
-rw-r--r--doc/development/experiment_guide/index.md2
-rw-r--r--doc/development/export_csv.md4
-rw-r--r--doc/development/fe_guide/graphql.md4
-rw-r--r--doc/development/fe_guide/index.md3
-rw-r--r--doc/development/fe_guide/storybook.md10
-rw-r--r--doc/development/fe_guide/style/vue.md12
-rw-r--r--doc/development/fe_guide/tooling.md4
-rw-r--r--doc/development/fe_guide/troubleshooting.md2
-rw-r--r--doc/development/fe_guide/view_component.md39
-rw-r--r--doc/development/fe_guide/vue.md16
-rw-r--r--doc/development/fe_guide/vuex.md4
-rw-r--r--doc/development/fe_guide/widgets.md6
-rw-r--r--doc/development/feature_development.md1
-rw-r--r--doc/development/feature_flags/controls.md4
-rw-r--r--doc/development/feature_flags/index.md14
-rw-r--r--doc/development/fips_compliance.md4
-rw-r--r--doc/development/gemfile.md23
-rw-r--r--doc/development/geo.md5
-rw-r--r--doc/development/geo/proxying.md2
-rw-r--r--doc/development/git_object_deduplication.md4
-rw-r--r--doc/development/github_importer.md40
-rw-r--r--doc/development/gitlab_flavored_markdown/specification_guide/index.md143
-rw-r--r--doc/development/go_guide/dependencies.md4
-rw-r--r--doc/development/go_guide/index.md6
-rw-r--r--doc/development/import_export.md23
-rw-r--r--doc/development/import_project.md16
-rw-r--r--doc/development/integrations/jenkins.md2
-rw-r--r--doc/development/integrations/secure.md32
-rw-r--r--doc/development/internal_api/index.md2
-rw-r--r--doc/development/internal_users.md2
-rw-r--r--doc/development/lfs.md4
-rw-r--r--doc/development/logging.md4
-rw-r--r--doc/development/merge_request_concepts/index.md28
-rw-r--r--doc/development/merge_request_performance_guidelines.md2
-rw-r--r--doc/development/migration_style_guide.md2
-rw-r--r--doc/development/newlines_styleguide.md111
-rw-r--r--doc/development/packages/new_format_development.md4
-rw-r--r--doc/development/permissions.md36
-rw-r--r--doc/development/pipelines.md29
-rw-r--r--doc/development/policies.md2
-rw-r--r--doc/development/rails_update.md2
-rw-r--r--doc/development/rake_tasks.md14
-rw-r--r--doc/development/real_time.md2
-rw-r--r--doc/development/redis/new_redis_instance.md2
-rw-r--r--doc/development/reusing_abstractions.md25
-rw-r--r--doc/development/routing.md2
-rw-r--r--doc/development/ruby3_gotchas.md37
-rw-r--r--doc/development/scalability.md18
-rw-r--r--doc/development/sec/index.md69
-rw-r--r--doc/development/secure_coding_guidelines.md2
-rw-r--r--doc/development/service_ping/implement.md15
-rw-r--r--doc/development/service_ping/index.md17
-rw-r--r--doc/development/service_ping/metrics_instrumentation.md26
-rw-r--r--doc/development/service_ping/troubleshooting.md6
-rw-r--r--doc/development/shell_scripting_guide/index.md2
-rw-r--r--doc/development/sidekiq/compatibility_across_updates.md6
-rw-r--r--doc/development/sidekiq/idempotent_jobs.md2
-rw-r--r--doc/development/snowplow/index.md2
-rw-r--r--doc/development/snowplow/troubleshooting.md8
-rw-r--r--doc/development/sql.md4
-rw-r--r--doc/development/testing_guide/best_practices.md82
-rw-r--r--doc/development/testing_guide/end_to_end/best_practices.md6
-rw-r--r--doc/development/testing_guide/end_to_end/feature_flags.md14
-rw-r--r--doc/development/testing_guide/end_to_end/index.md70
-rw-r--r--doc/development/testing_guide/end_to_end/rspec_metadata_tests.md13
-rw-r--r--doc/development/testing_guide/flaky_tests.md2
-rw-r--r--doc/development/testing_guide/frontend_testing.md17
-rw-r--r--doc/development/testing_guide/index.md2
-rw-r--r--doc/development/testing_guide/testing_migrations_guide.md2
-rw-r--r--doc/development/value_stream_analytics.md178
-rw-r--r--doc/development/value_stream_analytics/value_stream_analytics_aggregated_backend.md41
-rw-r--r--doc/development/work_items.md32
-rw-r--r--doc/development/workhorse/index.md2
-rw-r--r--doc/gitlab-basics/start-using-git.md20
-rw-r--r--doc/install/aws/manual_install_aws.md4
-rw-r--r--doc/install/azure/index.md6
-rw-r--r--doc/install/cloud_native/index.md54
-rw-r--r--doc/install/docker.md48
-rw-r--r--doc/install/google_cloud_platform/index.md6
-rw-r--r--doc/install/index.md46
-rw-r--r--doc/install/install_methods.md51
-rw-r--r--doc/install/installation.md4
-rw-r--r--doc/install/migrate/compare_sm_to_saas.md124
-rw-r--r--doc/install/requirements.md33
-rw-r--r--doc/integration/advanced_search/elasticsearch.md24
-rw-r--r--doc/integration/advanced_search/elasticsearch_troubleshooting.md65
-rw-r--r--doc/integration/akismet.md6
-rw-r--r--doc/integration/cas.md6
-rw-r--r--doc/integration/datadog.md2
-rw-r--r--doc/integration/ding_talk.md4
-rw-r--r--doc/integration/facebook.md5
-rw-r--r--doc/integration/gitpod.md2
-rw-r--r--doc/integration/google.md7
-rw-r--r--doc/integration/jenkins.md4
-rw-r--r--doc/integration/jira/configure.md2
-rw-r--r--doc/integration/jira/development_panel.md9
-rw-r--r--doc/integration/jira/dvcs.md4
-rw-r--r--doc/integration/jira/issues.md7
-rw-r--r--doc/integration/kerberos.md38
-rw-r--r--doc/integration/mattermost/index.md25
-rw-r--r--doc/integration/oauth_provider.md19
-rw-r--r--doc/integration/omniauth.md32
-rw-r--r--doc/integration/recaptcha.md6
-rw-r--r--doc/integration/salesforce.md7
-rw-r--r--doc/integration/saml.md142
-rw-r--r--doc/integration/sourcegraph.md2
-rw-r--r--doc/integration/twitter.md5
-rw-r--r--doc/operations/error_tracking.md7
-rw-r--r--doc/operations/feature_flags.md16
-rw-r--r--doc/operations/incident_management/alerts.md6
-rw-r--r--doc/operations/incident_management/escalation_policies.md6
-rw-r--r--doc/operations/incident_management/incident_timeline_events.md94
-rw-r--r--doc/operations/incident_management/incidents.md88
-rw-r--r--doc/operations/incident_management/linked_resources.md24
-rw-r--r--doc/operations/incident_management/oncall_schedules.md12
-rw-r--r--doc/operations/incident_management/paging.md2
-rw-r--r--doc/operations/incident_management/status_page.md4
-rw-r--r--doc/operations/metrics/alerts.md10
-rw-r--r--doc/operations/metrics/embed_grafana.md2
-rw-r--r--doc/operations/product_analytics.md80
-rw-r--r--doc/policy/maintenance.md11
-rw-r--r--doc/raketasks/backup_gitlab.md66
-rw-r--r--doc/raketasks/backup_restore.md24
-rw-r--r--doc/raketasks/restore_gitlab.md16
-rw-r--r--doc/security/img/allowlist_v13_0.pngbin16076 -> 0 bytes
-rw-r--r--doc/security/img/outbound_requests_section_v12_2.pngbin6303 -> 0 bytes
-rw-r--r--doc/security/information_exclusivity.md2
-rw-r--r--doc/security/password_length_limits.md2
-rw-r--r--doc/security/ssh_keys_restrictions.md2
-rw-r--r--doc/security/token_overview.md27
-rw-r--r--doc/security/two_factor_authentication.md4
-rw-r--r--doc/security/unlock_user.md2
-rw-r--r--doc/security/user_email_confirmation.md2
-rw-r--r--doc/security/user_file_uploads.md2
-rw-r--r--doc/security/webhooks.md134
-rw-r--r--doc/subscriptions/gitlab_com/index.md12
-rw-r--r--doc/subscriptions/gitlab_dedicated/index.md8
-rw-r--r--doc/subscriptions/index.md10
-rw-r--r--doc/subscriptions/self_managed/index.md8
-rw-r--r--doc/topics/application_development_platform/index.md70
-rw-r--r--doc/topics/autodevops/cloud_deployments/auto_devops_with_ecs.md2
-rw-r--r--doc/topics/autodevops/cloud_deployments/auto_devops_with_gke.md2
-rw-r--r--doc/topics/autodevops/customize.md3
-rw-r--r--doc/topics/autodevops/index.md92
-rw-r--r--doc/topics/autodevops/prepare_deployment.md2
-rw-r--r--doc/topics/autodevops/requirements.md4
-rw-r--r--doc/topics/autodevops/stages.md2
-rw-r--r--doc/topics/autodevops/troubleshooting.md6
-rw-r--r--doc/topics/build_your_application.md2
-rw-r--r--doc/topics/git/cherry_picking.md4
-rw-r--r--doc/topics/git/index.md2
-rw-r--r--doc/topics/git/lfs/migrate_to_git_lfs.md2
-rw-r--r--doc/topics/git/numerous_undo_possibilities_in_git/index.md2
-rw-r--r--doc/topics/offline/quick_start_guide.md2
-rw-r--r--doc/topics/release_your_application.md6
-rw-r--r--doc/tutorials/make_your_first_git_commit.md6
-rw-r--r--doc/tutorials/move_personal_project_to_a_group.md8
-rw-r--r--doc/update/deprecations.md173
-rw-r--r--doc/update/index.md69
-rw-r--r--doc/update/package/index.md7
-rw-r--r--doc/update/plan_your_upgrade.md5
-rw-r--r--doc/update/removals.md47
-rw-r--r--doc/update/upgrading_from_source.md2
-rw-r--r--doc/update/zero_downtime.md43
-rw-r--r--doc/user/admin_area/analytics/dev_ops_reports.md2
-rw-r--r--doc/user/admin_area/analytics/index.md2
-rw-r--r--doc/user/admin_area/analytics/usage_trends.md2
-rw-r--r--doc/user/admin_area/appearance.md6
-rw-r--r--doc/user/admin_area/broadcast_messages.md6
-rw-r--r--doc/user/admin_area/credentials_inventory.md2
-rw-r--r--doc/user/admin_area/custom_project_templates.md2
-rw-r--r--doc/user/admin_area/diff_limits.md2
-rw-r--r--doc/user/admin_area/email_from_gitlab.md2
-rw-r--r--doc/user/admin_area/geo_sites.md4
-rw-r--r--doc/user/admin_area/index.md44
-rw-r--r--doc/user/admin_area/license.md2
-rw-r--r--doc/user/admin_area/license_file.md6
-rw-r--r--doc/user/admin_area/merge_requests_approvals.md2
-rw-r--r--doc/user/admin_area/moderate_users.md30
-rw-r--r--doc/user/admin_area/monitoring/background_migrations.md3
-rw-r--r--doc/user/admin_area/reporting/git_abuse_rate_limit.md2
-rw-r--r--doc/user/admin_area/reporting/spamcheck.md2
-rw-r--r--doc/user/admin_area/review_abuse_reports.md6
-rw-r--r--doc/user/admin_area/settings/account_and_limit_settings.md36
-rw-r--r--doc/user/admin_area/settings/continuous_integration.md48
-rw-r--r--doc/user/admin_area/settings/deprecated_api_rate_limits.md2
-rw-r--r--doc/user/admin_area/settings/email.md12
-rw-r--r--doc/user/admin_area/settings/external_authorization.md2
-rw-r--r--doc/user/admin_area/settings/files_api_rate_limits.md2
-rw-r--r--doc/user/admin_area/settings/floc.md6
-rw-r--r--doc/user/admin_area/settings/git_lfs_rate_limits.md2
-rw-r--r--doc/user/admin_area/settings/gitaly_timeouts.md2
-rw-r--r--doc/user/admin_area/settings/help_page.md13
-rw-r--r--doc/user/admin_area/settings/import_export_rate_limits.md2
-rw-r--r--doc/user/admin_area/settings/incident_management_rate_limits.md2
-rw-r--r--doc/user/admin_area/settings/index.md4
-rw-r--r--doc/user/admin_area/settings/instance_template_repository.md2
-rw-r--r--doc/user/admin_area/settings/package_registry_rate_limits.md4
-rw-r--r--doc/user/admin_area/settings/project_integration_management.md6
-rw-r--r--doc/user/admin_area/settings/push_event_activities_limit.md2
-rw-r--r--doc/user/admin_area/settings/rate_limit_on_issues_creation.md2
-rw-r--r--doc/user/admin_area/settings/rate_limit_on_notes_creation.md2
-rw-r--r--doc/user/admin_area/settings/rate_limit_on_pipelines_creation.md4
-rw-r--r--doc/user/admin_area/settings/rate_limit_on_users_api.md2
-rw-r--r--doc/user/admin_area/settings/rate_limits_on_raw_endpoints.md2
-rw-r--r--doc/user/admin_area/settings/sidekiq_job_limits.md2
-rw-r--r--doc/user/admin_area/settings/sign_in_restrictions.md24
-rw-r--r--doc/user/admin_area/settings/sign_up_restrictions.md14
-rw-r--r--doc/user/admin_area/settings/terms.md2
-rw-r--r--doc/user/admin_area/settings/third_party_offers.md2
-rw-r--r--doc/user/admin_area/settings/usage_statistics.md8
-rw-r--r--doc/user/admin_area/settings/user_and_ip_rate_limits.md10
-rw-r--r--doc/user/admin_area/settings/visibility_and_access_controls.md33
-rw-r--r--doc/user/admin_area/user_cohorts.md2
-rw-r--r--doc/user/analytics/ci_cd_analytics.md12
-rw-r--r--doc/user/analytics/code_review_analytics.md57
-rw-r--r--doc/user/analytics/img/code_review_analytics_v13_11.pngbin37179 -> 0 bytes
-rw-r--r--doc/user/analytics/issue_analytics.md2
-rw-r--r--doc/user/analytics/merge_request_analytics.md6
-rw-r--r--doc/user/analytics/productivity_analytics.md6
-rw-r--r--doc/user/analytics/repository_analytics.md2
-rw-r--r--doc/user/analytics/value_stream_analytics.md23
-rw-r--r--doc/user/application_security/api_fuzzing/index.md653
-rw-r--r--doc/user/application_security/configuration/index.md4
-rw-r--r--doc/user/application_security/container_scanning/index.md71
-rw-r--r--doc/user/application_security/coverage_fuzzing/index.md10
-rw-r--r--doc/user/application_security/cve_id_request.md2
-rw-r--r--doc/user/application_security/dast/checks/16.7.md2
-rw-r--r--doc/user/application_security/dast/checks/209.1.md10
-rw-r--r--doc/user/application_security/dast/dast_troubleshooting.md2
-rw-r--r--doc/user/application_security/dast/index.md66
-rw-r--r--doc/user/application_security/dast_api/index.md659
-rw-r--r--doc/user/application_security/dependency_scanning/analyzers.md8
-rw-r--r--doc/user/application_security/dependency_scanning/index.md16
-rw-r--r--doc/user/application_security/generate_test_vulnerabilities/index.md2
-rw-r--r--doc/user/application_security/get-started-security.md27
-rw-r--r--doc/user/application_security/iac_scanning/index.md2
-rw-r--r--doc/user/application_security/index.md27
-rw-r--r--doc/user/application_security/offline_deployments/index.md6
-rw-r--r--doc/user/application_security/policies/index.md12
-rw-r--r--doc/user/application_security/policies/scan-execution-policies.md33
-rw-r--r--doc/user/application_security/policies/scan-result-policies.md4
-rw-r--r--doc/user/application_security/sast/analyzers.md38
-rw-r--r--doc/user/application_security/sast/index.md64
-rw-r--r--doc/user/application_security/secret_detection/index.md543
-rw-r--r--doc/user/application_security/security_dashboard/index.md28
-rw-r--r--doc/user/application_security/vulnerabilities/index.md20
-rw-r--r--doc/user/application_security/vulnerabilities/severities.md2
-rw-r--r--doc/user/application_security/vulnerability_report/index.md13
-rw-r--r--doc/user/application_security/vulnerability_report/pipeline.md24
-rw-r--r--doc/user/clusters/agent/ci_cd_workflow.md6
-rw-r--r--doc/user/clusters/agent/gitops.md13
-rw-r--r--doc/user/clusters/agent/gitops/helm.md112
-rw-r--r--doc/user/clusters/agent/index.md36
-rw-r--r--doc/user/clusters/agent/install/index.md6
-rw-r--r--doc/user/clusters/agent/vulnerabilities.md11
-rw-r--r--doc/user/clusters/agent/work_with_agent.md9
-rw-r--r--doc/user/clusters/environments.md2
-rw-r--r--doc/user/clusters/management_project.md2
-rw-r--r--doc/user/clusters/management_project_template.md7
-rw-r--r--doc/user/clusters/migrating_from_gma_to_project_template.md2
-rw-r--r--doc/user/compliance/compliance_report/index.md8
-rw-r--r--doc/user/compliance/index.md2
-rw-r--r--doc/user/compliance/license_compliance/img/denied_licenses_v13_3.pngbin29503 -> 0 bytes
-rw-r--r--doc/user/compliance/license_compliance/img/denied_licenses_v15_3.pngbin0 -> 39570 bytes
-rw-r--r--doc/user/compliance/license_compliance/index.md32
-rw-r--r--doc/user/crm/index.md20
-rw-r--r--doc/user/discussions/img/create-new-issue_v15.pngbin5672 -> 0 bytes
-rw-r--r--doc/user/discussions/img/create_new_issue_v15_4.pngbin0 -> 11883 bytes
-rw-r--r--doc/user/discussions/img/unresolved_threads_v15.pngbin2793 -> 0 bytes
-rw-r--r--doc/user/discussions/img/unresolved_threads_v15_4.pngbin0 -> 3692 bytes
-rw-r--r--doc/user/discussions/index.md24
-rw-r--r--doc/user/free_user_limit.md13
-rw-r--r--doc/user/gitlab_com/index.md18
-rw-r--r--doc/user/group/access_and_permissions.md72
-rw-r--r--doc/user/group/clusters/index.md4
-rw-r--r--doc/user/group/contribution_analytics/index.md2
-rw-r--r--doc/user/group/devops_adoption/index.md4
-rw-r--r--doc/user/group/epics/epic_boards.md42
-rw-r--r--doc/user/group/epics/manage_epics.md4
-rw-r--r--doc/user/group/import/index.md22
-rw-r--r--doc/user/group/insights/index.md43
-rw-r--r--doc/user/group/issues_analytics/index.md2
-rw-r--r--doc/user/group/iterations/index.md90
-rw-r--r--doc/user/group/manage.md94
-rw-r--r--doc/user/group/repositories_analytics/index.md4
-rw-r--r--doc/user/group/saml_sso/example_saml_config.md15
-rw-r--r--doc/user/group/saml_sso/group_sync.md19
-rw-r--r--doc/user/group/saml_sso/img/scim_token_v13_3.pngbin56821 -> 0 bytes
-rw-r--r--doc/user/group/saml_sso/index.md204
-rw-r--r--doc/user/group/saml_sso/scim_setup.md158
-rw-r--r--doc/user/group/saml_sso/troubleshooting.md249
-rw-r--r--doc/user/group/settings/group_access_tokens.md23
-rw-r--r--doc/user/group/settings/import_export.md6
-rw-r--r--doc/user/group/subgroups/index.md12
-rw-r--r--doc/user/group/value_stream_analytics/index.md38
-rw-r--r--doc/user/infrastructure/clusters/connect/index.md6
-rw-r--r--doc/user/infrastructure/clusters/connect/new_civo_cluster.md11
-rw-r--r--doc/user/infrastructure/clusters/connect/new_eks_cluster.md4
-rw-r--r--doc/user/infrastructure/clusters/connect/new_gke_cluster.md4
-rw-r--r--doc/user/infrastructure/clusters/manage/management_project_applications/certmanager.md4
-rw-r--r--doc/user/infrastructure/iac/index.md2
-rw-r--r--doc/user/infrastructure/iac/terraform_state.md6
-rw-r--r--doc/user/infrastructure/iac/troubleshooting.md8
-rw-r--r--doc/user/instance/clusters/index.md2
-rw-r--r--doc/user/markdown.md219
-rw-r--r--doc/user/namespace/index.md18
-rw-r--r--doc/user/operations_dashboard/index.md2
-rw-r--r--doc/user/packages/composer_repository/index.md5
-rw-r--r--doc/user/packages/conan_repository/index.md3
-rw-r--r--doc/user/packages/container_registry/index.md29
-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.md6
-rw-r--r--doc/user/packages/debian_repository/index.md40
-rw-r--r--doc/user/packages/dependency_proxy/index.md12
-rw-r--r--doc/user/packages/dependency_proxy/reduce_dependency_proxy_storage.md2
-rw-r--r--doc/user/packages/generic_packages/index.md4
-rw-r--r--doc/user/packages/harbor_container_registry/index.md69
-rw-r--r--doc/user/packages/infrastructure_registry/index.md8
-rw-r--r--doc/user/packages/maven_repository/index.md77
-rw-r--r--doc/user/packages/npm_registry/index.md3
-rw-r--r--doc/user/packages/nuget_repository/index.md1
-rw-r--r--doc/user/packages/package_registry/index.md6
-rw-r--r--doc/user/packages/package_registry/reduce_package_registry_storage.md26
-rw-r--r--doc/user/packages/pypi_repository/index.md2
-rw-r--r--doc/user/packages/rubygems_registry/index.md2
-rw-r--r--doc/user/packages/terraform_module_registry/index.md2
-rw-r--r--doc/user/permissions.md45
-rw-r--r--doc/user/profile/account/create_accounts.md2
-rw-r--r--doc/user/profile/account/delete_account.md2
-rw-r--r--doc/user/profile/index.md8
-rw-r--r--doc/user/profile/notifications.md4
-rw-r--r--doc/user/profile/personal_access_tokens.md9
-rw-r--r--doc/user/profile/preferences.md7
-rw-r--r--doc/user/project/badges.md8
-rw-r--r--doc/user/project/canary_deployments.md4
-rw-r--r--doc/user/project/clusters/add_eks_clusters.md4
-rw-r--r--doc/user/project/clusters/add_existing_cluster.md2
-rw-r--r--doc/user/project/clusters/add_gke_clusters.md4
-rw-r--r--doc/user/project/clusters/add_remove_clusters.md2
-rw-r--r--doc/user/project/clusters/index.md2
-rw-r--r--doc/user/project/code_intelligence.md15
-rw-r--r--doc/user/project/deploy_boards.md2
-rw-r--r--doc/user/project/deploy_keys/index.md10
-rw-r--r--doc/user/project/deploy_tokens/index.md7
-rw-r--r--doc/user/project/description_templates.md15
-rw-r--r--doc/user/project/file_lock.md2
-rw-r--r--doc/user/project/git_attributes.md2
-rw-r--r--doc/user/project/import/clearcase.md2
-rw-r--r--doc/user/project/import/github.md11
-rw-r--r--doc/user/project/import/gitlab_com.md37
-rw-r--r--doc/user/project/import/img/gitlab_importer.pngbin12864 -> 0 bytes
-rw-r--r--doc/user/project/import/img/gitlab_new_project_page_v12_2.pngbin66644 -> 0 bytes
-rw-r--r--doc/user/project/import/index.md11
-rw-r--r--doc/user/project/import/repo_by_url.md3
-rw-r--r--doc/user/project/import/svn.md211
-rw-r--r--doc/user/project/insights/index.md8
-rw-r--r--doc/user/project/integrations/asana.md2
-rw-r--r--doc/user/project/integrations/bamboo.md2
-rw-r--r--doc/user/project/integrations/bugzilla.md2
-rw-r--r--doc/user/project/integrations/custom_issue_tracker.md2
-rw-r--r--doc/user/project/integrations/discord_notifications.md2
-rw-r--r--doc/user/project/integrations/emails_on_push.md2
-rw-r--r--doc/user/project/integrations/ewm.md2
-rw-r--r--doc/user/project/integrations/github.md4
-rw-r--r--doc/user/project/integrations/gitlab_slack_application.md1
-rw-r--r--doc/user/project/integrations/hangouts_chat.md23
-rw-r--r--doc/user/project/integrations/harbor.md12
-rw-r--r--doc/user/project/integrations/img/mattermost_add_slash_command.pngbin9265 -> 0 bytes
-rw-r--r--doc/user/project/integrations/img/mattermost_bot_auth.pngbin8669 -> 0 bytes
-rw-r--r--doc/user/project/integrations/img/mattermost_bot_available_commands.pngbin4642 -> 0 bytes
-rw-r--r--doc/user/project/integrations/img/mattermost_gitlab_token.pngbin3673 -> 0 bytes
-rw-r--r--doc/user/project/integrations/index.md3
-rw-r--r--doc/user/project/integrations/irker.md2
-rw-r--r--doc/user/project/integrations/mattermost.md2
-rw-r--r--doc/user/project/integrations/mattermost_slash_commands.md167
-rw-r--r--doc/user/project/integrations/microsoft_teams.md2
-rw-r--r--doc/user/project/integrations/pivotal_tracker.md2
-rw-r--r--doc/user/project/integrations/prometheus.md4
-rw-r--r--doc/user/project/integrations/pumble.md6
-rw-r--r--doc/user/project/integrations/redmine.md2
-rw-r--r--doc/user/project/integrations/servicenow.md2
-rw-r--r--doc/user/project/integrations/shimo.md34
-rw-r--r--doc/user/project/integrations/slack.md2
-rw-r--r--doc/user/project/integrations/slack_slash_commands.md2
-rw-r--r--doc/user/project/integrations/unify_circuit.md2
-rw-r--r--doc/user/project/integrations/webex_teams.md2
-rw-r--r--doc/user/project/integrations/youtrack.md2
-rw-r--r--doc/user/project/issue_board.md174
-rw-r--r--doc/user/project/issues/associate_zoom_meeting.md7
-rw-r--r--doc/user/project/issues/confidential_issues.md13
-rw-r--r--doc/user/project/issues/img/confidential_issues_create.pngbin8185 -> 0 bytes
-rw-r--r--doc/user/project/issues/img/confidential_issues_create_v15_4.pngbin0 -> 13023 bytes
-rw-r--r--doc/user/project/issues/img/confidential_issues_system_notes.pngbin4214 -> 0 bytes
-rw-r--r--doc/user/project/issues/img/confidential_issues_system_notes_v15_4.pngbin0 -> 4289 bytes
-rw-r--r--doc/user/project/issues/img/issue_weight_v13_11.pngbin14914 -> 0 bytes
-rw-r--r--doc/user/project/issues/img/related_issue_block_v15_3.pngbin28910 -> 10699 bytes
-rw-r--r--doc/user/project/issues/img/related_issues_add_v15_3.pngbin24947 -> 9286 bytes
-rw-r--r--doc/user/project/issues/issue_weight.md59
-rw-r--r--doc/user/project/issues/managing_issues.md48
-rw-r--r--doc/user/project/issues/related_issues.md4
-rw-r--r--doc/user/project/labels.md22
-rw-r--r--doc/user/project/members/index.md16
-rw-r--r--doc/user/project/members/share_project_with_groups.md3
-rw-r--r--doc/user/project/merge_requests/approvals/rules.md31
-rw-r--r--doc/user/project/merge_requests/approvals/settings.md41
-rw-r--r--doc/user/project/merge_requests/cherry_pick_changes.md154
-rw-r--r--doc/user/project/merge_requests/commit_templates.md4
-rw-r--r--doc/user/project/merge_requests/creating_merge_requests.md10
-rw-r--r--doc/user/project/merge_requests/csv_export.md4
-rw-r--r--doc/user/project/merge_requests/dependencies.md163
-rw-r--r--doc/user/project/merge_requests/drafts.md4
-rw-r--r--doc/user/project/merge_requests/getting_started.md2
-rw-r--r--doc/user/project/merge_requests/img/cancel-mwps_v15_4.pngbin0 -> 12914 bytes
-rw-r--r--doc/user/project/merge_requests/img/cherry_pick_changes_commit.pngbin13568 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/img/cherry_pick_changes_mr.pngbin7214 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/img/cherry_pick_mr_timeline_v12_9.pngbin29557 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/img/cherry_pick_mr_timeline_v15_4.pngbin0 -> 7678 bytes
-rw-r--r--doc/user/project/merge_requests/img/cherry_pick_v15_4.pngbin0 -> 10187 bytes
-rw-r--r--doc/user/project/merge_requests/img/dependencies_edit_v12_4.pngbin18741 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/img/dependencies_view_v15_3.pngbin0 -> 18558 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_enable.pngbin10186 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_status.pngbin21397 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/img/mwps_v15_4.pngbin0 -> 11146 bytes
-rw-r--r--doc/user/project/merge_requests/index.md12
-rw-r--r--doc/user/project/merge_requests/merge_request_dependencies.md144
-rw-r--r--doc/user/project/merge_requests/merge_when_pipeline_succeeds.md186
-rw-r--r--doc/user/project/merge_requests/methods/index.md7
-rw-r--r--doc/user/project/merge_requests/revert_changes.md92
-rw-r--r--doc/user/project/merge_requests/reviews/img/mr_summary_comment_v15_3.pngbin49186 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/reviews/img/mr_summary_comment_v15_4.pngbin0 -> 61841 bytes
-rw-r--r--doc/user/project/merge_requests/reviews/index.md12
-rw-r--r--doc/user/project/merge_requests/squash_and_merge.md5
-rw-r--r--doc/user/project/merge_requests/status_checks.md7
-rw-r--r--doc/user/project/milestones/index.md112
-rw-r--r--doc/user/project/pages/custom_domains_ssl_tls_certification/index.md29
-rw-r--r--doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md6
-rw-r--r--doc/user/project/pages/getting_started/pages_ci_cd_template.md2
-rw-r--r--doc/user/project/pages/getting_started/pages_from_scratch.md6
-rw-r--r--doc/user/project/pages/getting_started/pages_ui.md58
-rw-r--r--doc/user/project/pages/index.md11
-rw-r--r--doc/user/project/pages/introduction.md1
-rw-r--r--doc/user/project/pages/public_folder.md153
-rw-r--r--doc/user/project/protected_branches.md22
-rw-r--r--doc/user/project/protected_tags.md2
-rw-r--r--doc/user/project/push_options.md2
-rw-r--r--doc/user/project/quick_actions.md7
-rw-r--r--doc/user/project/releases/index.md22
-rw-r--r--doc/user/project/releases/release_cicd_examples.md73
-rw-r--r--doc/user/project/releases/release_cli.md2
-rw-r--r--doc/user/project/repository/branches/default.md8
-rw-r--r--doc/user/project/repository/branches/index.md3
-rw-r--r--doc/user/project/repository/gpg_signed_commits/index.md4
-rw-r--r--doc/user/project/repository/index.md10
-rw-r--r--doc/user/project/repository/managing_large_repositories.md6
-rw-r--r--doc/user/project/repository/mirror/bidirectional.md2
-rw-r--r--doc/user/project/repository/mirror/index.md8
-rw-r--r--doc/user/project/repository/mirror/pull.md2
-rw-r--r--doc/user/project/repository/mirror/push.md2
-rw-r--r--doc/user/project/repository/push_rules.md4
-rw-r--r--doc/user/project/repository/reducing_the_repo_size_using_git.md2
-rw-r--r--doc/user/project/service_desk.md18
-rw-r--r--doc/user/project/settings/import_export.md47
-rw-r--r--doc/user/project/settings/index.md46
-rw-r--r--doc/user/project/settings/project_access_tokens.md16
-rw-r--r--doc/user/project/time_tracking.md2
-rw-r--r--doc/user/project/web_ide/index.md2
-rw-r--r--doc/user/project/wiki/group.md4
-rw-r--r--doc/user/project/wiki/index.md24
-rw-r--r--doc/user/project/working_with_projects.md79
-rw-r--r--doc/user/public_access.md12
-rw-r--r--doc/user/search/advanced_search.md54
-rw-r--r--doc/user/search/index.md4
-rw-r--r--doc/user/snippets.md16
-rw-r--r--doc/user/ssh.md8
-rw-r--r--doc/user/tasks.md85
-rw-r--r--doc/user/usage_quotas.md48
-rw-r--r--glfm_specification/example_snapshots/examples_index.yml1404
-rw-r--r--glfm_specification/example_snapshots/html.yml1515
-rw-r--r--glfm_specification/example_snapshots/markdown.yml1412
-rw-r--r--glfm_specification/example_snapshots/prosemirror_json.yml1606
-rw-r--r--glfm_specification/input/gitlab_flavored_markdown/glfm_canonical_examples.txt208
-rw-r--r--glfm_specification/input/gitlab_flavored_markdown/glfm_example_metadata.yml14
-rw-r--r--glfm_specification/input/gitlab_flavored_markdown/glfm_example_normalizations.yml16
-rw-r--r--glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml42
-rw-r--r--glfm_specification/output/spec.txt208
-rw-r--r--lib/api/admin/batched_background_migrations.rb108
-rw-r--r--lib/api/api.rb5
-rw-r--r--lib/api/branches.rb37
-rw-r--r--lib/api/ci/job_artifacts.rb2
-rw-r--r--lib/api/ci/jobs.rb7
-rw-r--r--lib/api/ci/runners.rb15
-rw-r--r--lib/api/composer_packages.rb5
-rw-r--r--lib/api/concerns/packages/conan_endpoints.rb8
-rw-r--r--lib/api/concerns/packages/debian_package_endpoints.rb123
-rw-r--r--lib/api/debian_group_packages.rb2
-rw-r--r--lib/api/debian_project_packages.rb2
-rw-r--r--lib/api/entities/batched_background_migration.rb14
-rw-r--r--lib/api/entities/ci/job_basic.rb6
-rw-r--r--lib/api/entities/ci/job_request/image.rb2
-rw-r--r--lib/api/entities/ci/job_request/service.rb2
-rw-r--r--lib/api/entities/merge_request_reviewer.rb1
-rw-r--r--lib/api/entities/ml/mlflow/experiment.rb28
-rw-r--r--lib/api/entities/ml/mlflow/new_experiment.rb19
-rw-r--r--lib/api/entities/ml/mlflow/run.rb16
-rw-r--r--lib/api/entities/ml/mlflow/run_info.rb27
-rw-r--r--lib/api/entities/ml/mlflow/update_run.rb19
-rw-r--r--lib/api/entities/package.rb1
-rw-r--r--lib/api/entities/personal_access_token_with_details.rb13
-rw-r--r--lib/api/entities/user_safe.rb6
-rw-r--r--lib/api/generic_packages.rb2
-rw-r--r--lib/api/groups.rb21
-rw-r--r--lib/api/helm_packages.rb2
-rw-r--r--lib/api/helpers.rb29
-rw-r--r--lib/api/helpers/groups_helpers.rb3
-rw-r--r--lib/api/helpers/packages/conan/api_helpers.rb6
-rw-r--r--lib/api/helpers/packages/dependency_proxy_helpers.rb20
-rw-r--r--lib/api/helpers/packages_helpers.rb7
-rw-r--r--lib/api/helpers/personal_access_tokens_helpers.rb35
-rw-r--r--lib/api/helpers/projects_helpers.rb5
-rw-r--r--lib/api/helpers/resource_events_helpers.rb17
-rw-r--r--lib/api/helpers/resource_label_events_helpers.rb18
-rw-r--r--lib/api/integrations/slack/events.rb40
-rw-r--r--lib/api/integrations/slack/events/url_verification.rb22
-rw-r--r--lib/api/integrations/slack/request.rb51
-rw-r--r--lib/api/internal/base.rb12
-rw-r--r--lib/api/maven_packages.rb79
-rw-r--r--lib/api/members.rb1
-rw-r--r--lib/api/merge_requests.rb25
-rw-r--r--lib/api/ml/mlflow.rb171
-rw-r--r--lib/api/namespaces.rb2
-rw-r--r--lib/api/npm_project_packages.rb2
-rw-r--r--lib/api/nuget_project_packages.rb2
-rw-r--r--lib/api/personal_access_tokens.rb34
-rw-r--r--lib/api/personal_access_tokens/self_revocation.rb26
-rw-r--r--lib/api/projects.rb18
-rw-r--r--lib/api/pypi_packages.rb4
-rw-r--r--lib/api/releases.rb66
-rw-r--r--lib/api/resource_label_events.rb12
-rw-r--r--lib/api/resource_state_events.rb30
-rw-r--r--lib/api/rpm_project_packages.rb63
-rw-r--r--lib/api/rubygem_packages.rb4
-rw-r--r--lib/api/search.rb17
-rw-r--r--lib/api/settings.rb1
-rw-r--r--lib/api/tags.rb4
-rw-r--r--lib/api/topics.rb20
-rw-r--r--lib/api/users.rb5
-rw-r--r--lib/banzai/filter/blockquote_fence_filter.rb6
-rw-r--r--lib/banzai/filter/kroki_filter.rb11
-rw-r--r--lib/banzai/filter/math_filter.rb100
-rw-r--r--lib/banzai/filter/plantuml_filter.rb3
-rw-r--r--lib/banzai/filter/references/reference_filter.rb12
-rw-r--r--lib/banzai/pipeline/markup_pipeline.rb4
-rw-r--r--lib/bulk_imports/file_downloads/filename_fetch.rb46
-rw-r--r--lib/bulk_imports/file_downloads/validations.rb58
-rw-r--r--lib/container_registry/gitlab_api_client.rb2
-rw-r--r--lib/container_registry/tag.rb13
-rw-r--r--lib/error_tracking/sentry_client.rb20
-rw-r--r--lib/error_tracking/sentry_client/issue.rb12
-rw-r--r--lib/event_filter.rb25
-rw-r--r--lib/generators/gitlab/usage_metric/templates/instrumentation_class_spec.rb.template4
-rw-r--r--lib/gitlab/abuse.rb6
-rw-r--r--lib/gitlab/access.rb20
-rw-r--r--lib/gitlab/alert_management/payload/base.rb4
-rw-r--r--lib/gitlab/alert_management/payload/generic.rb9
-rw-r--r--lib/gitlab/analytics/cycle_analytics/request_params.rb5
-rw-r--r--lib/gitlab/analytics/date_filler.rb112
-rw-r--r--lib/gitlab/application_rate_limiter.rb79
-rw-r--r--lib/gitlab/application_rate_limiter/increment_per_action.rb6
-rw-r--r--lib/gitlab/application_rate_limiter/increment_per_actioned_resource.rb8
-rw-r--r--lib/gitlab/audit/auditor.rb2
-rw-r--r--lib/gitlab/audit/type/definition.rb122
-rw-r--r--lib/gitlab/auth/ldap/config.rb14
-rw-r--r--lib/gitlab/auth/o_auth/auth_hash.rb4
-rw-r--r--lib/gitlab/auth/o_auth/provider.rb18
-rw-r--r--lib/gitlab/auth/o_auth/user.rb10
-rw-r--r--lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp.rb2
-rw-r--r--lib/gitlab/auth/user_access_denied_reason.rb2
-rw-r--r--lib/gitlab/background_migration/backfill_cluster_agents_has_vulnerabilities.rb31
-rw-r--r--lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_with_corrected_regex.rb1
-rw-r--r--lib/gitlab/background_migration/backfill_project_feature_package_registry_access_level.rb2
-rw-r--r--lib/gitlab/background_migration/backfill_project_namespace_on_issues.rb25
-rw-r--r--lib/gitlab/background_migration/backfill_project_repositories.rb4
-rw-r--r--lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent.rb12
-rw-r--r--lib/gitlab/background_migration/backfill_work_item_type_id_for_issues.rb32
-rw-r--r--lib/gitlab/background_migration/batching_strategies/backfill_issue_work_item_type_batching_strategy.rb19
-rw-r--r--lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy.rb13
-rw-r--r--lib/gitlab/background_migration/batching_strategies/backfill_vulnerability_reads_cluster_agent_batching_strategy.rb11
-rw-r--r--lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy.rb9
-rw-r--r--lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy.rb16
-rw-r--r--lib/gitlab/background_migration/batching_strategies/remove_backfilled_job_artifacts_expire_at_batching_strategy.rb12
-rw-r--r--lib/gitlab/background_migration/delete_approval_rules_with_vulnerability.rb16
-rw-r--r--lib/gitlab/background_migration/destroy_invalid_group_members.rb23
-rw-r--r--lib/gitlab/background_migration/destroy_invalid_project_members.rb25
-rw-r--r--lib/gitlab/background_migration/disable_legacy_open_source_licence_for_recent_public_projects.rb29
-rw-r--r--lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_one_mb.rb21
-rw-r--r--lib/gitlab/background_migration/mailers/unconfirm_mailer.rb2
-rw-r--r--lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb2
-rw-r--r--lib/gitlab/background_migration/remove_backfilled_job_artifacts_expire_at.rb43
-rw-r--r--lib/gitlab/background_migration/remove_self_managed_wiki_notes.rb16
-rw-r--r--lib/gitlab/background_migration/rename_task_system_note_to_checklist_item.rb28
-rw-r--r--lib/gitlab/background_migration/set_correct_vulnerability_state.rb7
-rw-r--r--lib/gitlab/base_doorkeeper_controller.rb2
-rw-r--r--lib/gitlab/cache/helpers.rb75
-rw-r--r--lib/gitlab/ci/ansi2html.rb10
-rw-r--r--lib/gitlab/ci/ansi2json/parser.rb10
-rw-r--r--lib/gitlab/ci/build/artifacts/adapters/zip_stream.rb61
-rw-r--r--lib/gitlab/ci/build/context/build.rb13
-rw-r--r--lib/gitlab/ci/build/rules/rule/clause/exists.rb2
-rw-r--r--lib/gitlab/ci/config/entry/current_variables.rb49
-rw-r--r--lib/gitlab/ci/config/entry/environment.rb2
-rw-r--r--lib/gitlab/ci/config/entry/image.rb7
-rw-r--r--lib/gitlab/ci/config/entry/imageable.rb5
-rw-r--r--lib/gitlab/ci/config/entry/legacy_variables.rb46
-rw-r--r--lib/gitlab/ci/config/entry/processable.rb4
-rw-r--r--lib/gitlab/ci/config/entry/root.rb3
-rw-r--r--lib/gitlab/ci/config/entry/service.rb9
-rw-r--r--lib/gitlab/ci/config/entry/variable.rb98
-rw-r--r--lib/gitlab/ci/config/entry/variables.rb40
-rw-r--r--lib/gitlab/ci/jwt_v2.rb2
-rw-r--r--lib/gitlab/ci/parsers/sbom/cyclonedx.rb14
-rw-r--r--lib/gitlab/ci/parsers/sbom/source/dependency_scanning.rb10
-rw-r--r--lib/gitlab/ci/parsers/security/common.rb24
-rw-r--r--lib/gitlab/ci/parsers/security/validators/schema_validator.rb16
-rw-r--r--lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/cluster-image-scanning-report-format.json977
-rw-r--r--lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/container-scanning-report-format.json911
-rw-r--r--lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/coverage-fuzzing-report-format.json874
-rw-r--r--lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/dast-report-format.json1287
-rw-r--r--lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/dependency-scanning-report-format.json968
-rw-r--r--lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/sast-report-format.json869
-rw-r--r--lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/secret-detection-report-format.json892
-rw-r--r--lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/cluster-image-scanning-report-format.json946
-rw-r--r--lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/container-scanning-report-format.json880
-rw-r--r--lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/coverage-fuzzing-report-format.json836
-rw-r--r--lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/dast-report-format.json1241
-rw-r--r--lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/dependency-scanning-report-format.json944
-rw-r--r--lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/sast-report-format.json831
-rw-r--r--lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/secret-detection-report-format.json854
-rw-r--r--lib/gitlab/ci/parsers/test/junit.rb4
-rw-r--r--lib/gitlab/ci/pipeline/chain/assign_partition.rb31
-rw-r--r--lib/gitlab/ci/pipeline/chain/command.rb12
-rw-r--r--lib/gitlab/ci/pipeline/chain/config/content.rb23
-rw-r--r--lib/gitlab/ci/pipeline/chain/config/content/source.rb1
-rw-r--r--lib/gitlab/ci/pipeline/chain/ensure_environments.rb4
-rw-r--r--lib/gitlab/ci/pipeline/chain/validate/external.rb9
-rw-r--r--lib/gitlab/ci/pipeline/seed/build.rb4
-rw-r--r--lib/gitlab/ci/pipeline/seed/environment.rb16
-rw-r--r--lib/gitlab/ci/pipeline/seed/stage.rb3
-rw-r--r--lib/gitlab/ci/processable_object_hierarchy.rb33
-rw-r--r--lib/gitlab/ci/project_config.rb52
-rw-r--r--lib/gitlab/ci/project_config/auto_devops.rb28
-rw-r--r--lib/gitlab/ci/project_config/bridge.rb19
-rw-r--r--lib/gitlab/ci/project_config/external_project.rb45
-rw-r--r--lib/gitlab/ci/project_config/parameter.rb21
-rw-r--r--lib/gitlab/ci/project_config/remote.rb21
-rw-r--r--lib/gitlab/ci/project_config/repository.rb32
-rw-r--r--lib/gitlab/ci/project_config/source.rb41
-rw-r--r--lib/gitlab/ci/reports/coverage_report_generator.rb2
-rw-r--r--lib/gitlab/ci/reports/sbom/component.rb8
-rw-r--r--lib/gitlab/ci/reports/sbom/report.rb4
-rw-r--r--lib/gitlab/ci/reports/sbom/source.rb8
-rw-r--r--lib/gitlab/ci/reports/security/scanner.rb4
-rw-r--r--lib/gitlab/ci/status/build/failed.rb4
-rw-r--r--lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Jobs/Dependency-Scanning.latest.gitlab-ci.yml244
-rw-r--r--lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Jobs/License-Scanning.latest.gitlab-ci.yml48
-rw-r--r--lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml58
-rw-r--r--lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml90
-rw-r--r--lib/gitlab/ci/templates/Katalon.gitlab-ci.yml65
-rw-r--r--lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml6
-rw-r--r--lib/gitlab/ci/templates/Security/Container-Scanning.latest.gitlab-ci.yml68
-rw-r--r--lib/gitlab/ci/templates/Terraform/Base.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/npm.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/trace.rb11
-rw-r--r--lib/gitlab/ci/variables/builder.rb4
-rw-r--r--lib/gitlab/ci/variables/helpers.rb28
-rw-r--r--lib/gitlab/ci/yaml_processor/feature_flags.rb27
-rw-r--r--lib/gitlab/ci/yaml_processor/result.rb10
-rw-r--r--lib/gitlab/cleanup/personal_access_tokens.rb105
-rw-r--r--lib/gitlab/closing_issue_extractor.rb6
-rw-r--r--lib/gitlab/cluster/lifecycle_events.rb5
-rw-r--r--lib/gitlab/config/entry/composable_hash.rb10
-rw-r--r--lib/gitlab/config/entry/validators.rb13
-rw-r--r--lib/gitlab/container_repository/tags/cache.rb4
-rw-r--r--lib/gitlab/data_builder/build.rb2
-rw-r--r--lib/gitlab/data_builder/pipeline.rb5
-rw-r--r--lib/gitlab/database.rb3
-rw-r--r--lib/gitlab/database/background_migration/batched_migration.rb14
-rw-r--r--lib/gitlab/database/background_migration/health_status.rb2
-rw-r--r--lib/gitlab/database/batch_average_counter.rb103
-rw-r--r--lib/gitlab/database/batch_count.rb6
-rw-r--r--lib/gitlab/database/batch_counter.rb31
-rw-r--r--lib/gitlab/database/gitlab_schemas.yml8
-rw-r--r--lib/gitlab/database/lock_writes_manager.rb45
-rw-r--r--lib/gitlab/database/migration_helpers.rb15
-rw-r--r--lib/gitlab/database/migrations/base_background_runner.rb2
-rw-r--r--lib/gitlab/database/migrations/test_background_runner.rb1
-rw-r--r--lib/gitlab/database/migrations/test_batched_background_runner.rb75
-rw-r--r--lib/gitlab/database/partitioning.rb12
-rw-r--r--lib/gitlab/database/partitioning/convert_table_to_first_list_partition.rb214
-rw-r--r--lib/gitlab/database/partitioning/partition_manager.rb25
-rw-r--r--lib/gitlab/database/partitioning/single_numeric_list_partition.rb20
-rw-r--r--lib/gitlab/database/partitioning/sliding_list_strategy.rb17
-rw-r--r--lib/gitlab/database/partitioning_migration_helpers/index_helpers.rb34
-rw-r--r--lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb48
-rw-r--r--lib/gitlab/database/postgres_constraint.rb29
-rw-r--r--lib/gitlab/database/query_analyzers/ci/partitioning_analyzer.rb41
-rw-r--r--lib/gitlab/database/query_analyzers/gitlab_schemas_metrics.rb2
-rw-r--r--lib/gitlab/database/query_analyzers/prevent_cross_database_modification.rb2
-rw-r--r--lib/gitlab/database/reflection.rb2
-rw-r--r--lib/gitlab/database/reindexing.rb2
-rw-r--r--lib/gitlab/database/tables_sorted_by_foreign_keys.rb41
-rw-r--r--lib/gitlab/database/tables_truncate.rb96
-rw-r--r--lib/gitlab/database_importers/security/training_providers/importer.rb42
-rw-r--r--lib/gitlab/diff/file_collection/compare.rb4
-rw-r--r--lib/gitlab/diff/highlight_cache.rb29
-rw-r--r--lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512.rb28
-rw-r--r--lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512.rb38
-rw-r--r--lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512.rb30
-rw-r--r--lib/gitlab/email/attachment_uploader.rb4
-rw-r--r--lib/gitlab/email/message/in_product_marketing/team.rb14
-rw-r--r--lib/gitlab/email/message/repository_push.rb6
-rw-r--r--lib/gitlab/emoji.rb12
-rw-r--r--lib/gitlab/encoding_helper.rb24
-rw-r--r--lib/gitlab/etag_caching/middleware.rb12
-rw-r--r--lib/gitlab/etag_caching/store.rb4
-rw-r--r--lib/gitlab/experimentation.rb6
-rw-r--r--lib/gitlab/external_authorization/cache.rb6
-rw-r--r--lib/gitlab/fogbugz_import/importer.rb26
-rw-r--r--lib/gitlab/gfm/reference_rewriter.rb7
-rw-r--r--lib/gitlab/git.rb2
-rw-r--r--lib/gitlab/git/diff.rb1
-rw-r--r--lib/gitlab/git/repository.rb61
-rw-r--r--lib/gitlab/git/tag.rb2
-rw-r--r--lib/gitlab/git/wiki.rb24
-rw-r--r--lib/gitlab/git/wiki_page.rb34
-rw-r--r--lib/gitlab/gitaly_client/commit_service.rb29
-rw-r--r--lib/gitlab/gitaly_client/operation_service.rb22
-rw-r--r--lib/gitlab/gitaly_client/ref_service.rb32
-rw-r--r--lib/gitlab/gitaly_client/server_service.rb13
-rw-r--r--lib/gitlab/github_import/attachments_downloader.rb65
-rw-r--r--lib/gitlab/github_import/client.rb16
-rw-r--r--lib/gitlab/github_import/importer/events/base_importer.rb13
-rw-r--r--lib/gitlab/github_import/importer/events/changed_assignee.rb20
-rw-r--r--lib/gitlab/github_import/importer/events/changed_label.rb7
-rw-r--r--lib/gitlab/github_import/importer/events/changed_milestone.rb7
-rw-r--r--lib/gitlab/github_import/importer/events/changed_reviewer.rb54
-rw-r--r--lib/gitlab/github_import/importer/events/closed.rb9
-rw-r--r--lib/gitlab/github_import/importer/events/cross_referenced.rb2
-rw-r--r--lib/gitlab/github_import/importer/events/renamed.rb2
-rw-r--r--lib/gitlab/github_import/importer/events/reopened.rb9
-rw-r--r--lib/gitlab/github_import/importer/issue_event_importer.rb6
-rw-r--r--lib/gitlab/github_import/importer/protected_branch_importer.rb48
-rw-r--r--lib/gitlab/github_import/importer/protected_branches_importer.rb52
-rw-r--r--lib/gitlab/github_import/importer/release_attachments_importer.rb58
-rw-r--r--lib/gitlab/github_import/importer/releases_attachments_importer.rb59
-rw-r--r--lib/gitlab/github_import/importer/repository_importer.rb4
-rw-r--r--lib/gitlab/github_import/importer/single_endpoint_issue_events_importer.rb26
-rw-r--r--lib/gitlab/github_import/markdown_text.rb31
-rw-r--r--lib/gitlab/github_import/parallel_scheduling.rb8
-rw-r--r--lib/gitlab/github_import/representation/expose_attribute.rb4
-rw-r--r--lib/gitlab/github_import/representation/issue_event.rb9
-rw-r--r--lib/gitlab/github_import/representation/protected_branch.rb46
-rw-r--r--lib/gitlab/github_import/representation/release_attachments.rb44
-rw-r--r--lib/gitlab/github_import/sequential_importer.rb1
-rw-r--r--lib/gitlab/github_import/single_endpoint_notes_importing.rb28
-rw-r--r--lib/gitlab/github_import/user_finder.rb6
-rw-r--r--lib/gitlab/gon_helper.rb1
-rw-r--r--lib/gitlab/graphql/errors.rb1
-rw-r--r--lib/gitlab/graphql/limit/field_call_count.rb32
-rw-r--r--lib/gitlab/graphql/pagination/keyset/connection.rb28
-rw-r--r--lib/gitlab/graphql/pagination/keyset/last_items.rb25
-rw-r--r--lib/gitlab/graphql/type_name_deprecations.rb3
-rw-r--r--lib/gitlab/harbor/query.rb2
-rw-r--r--lib/gitlab/health_checks/gitaly_check.rb26
-rw-r--r--lib/gitlab/health_checks/redis.rb16
-rw-r--r--lib/gitlab/health_checks/redis/cache_check.rb11
-rw-r--r--lib/gitlab/health_checks/redis/queues_check.rb11
-rw-r--r--lib/gitlab/health_checks/redis/rate_limiting_check.rb11
-rw-r--r--lib/gitlab/health_checks/redis/redis_abstract_check.rb4
-rw-r--r--lib/gitlab/health_checks/redis/redis_check.rb38
-rw-r--r--lib/gitlab/health_checks/redis/sessions_check.rb11
-rw-r--r--lib/gitlab/health_checks/redis/shared_state_check.rb11
-rw-r--r--lib/gitlab/health_checks/redis/trace_chunks_check.rb11
-rw-r--r--lib/gitlab/hook_data/project_member_builder.rb20
-rw-r--r--lib/gitlab/i18n.rb18
-rw-r--r--lib/gitlab/import_export/attributes_finder.rb4
-rw-r--r--lib/gitlab/import_export/base/relation_factory.rb2
-rw-r--r--lib/gitlab/import_export/base/relation_object_saver.rb13
-rw-r--r--lib/gitlab/import_export/group/import_export.yml40
-rw-r--r--lib/gitlab/import_export/group/legacy_import_export.yml38
-rw-r--r--lib/gitlab/import_export/group/legacy_tree_restorer.rb22
-rw-r--r--lib/gitlab/import_export/group/relation_factory.rb16
-rw-r--r--lib/gitlab/import_export/group/relation_tree_restorer.rb4
-rw-r--r--lib/gitlab/import_export/group/tree_saver.rb3
-rw-r--r--lib/gitlab/import_export/json/streaming_serializer.rb44
-rw-r--r--lib/gitlab/import_export/legacy_relation_tree_saver.rb8
-rw-r--r--lib/gitlab/import_export/members_mapper.rb2
-rw-r--r--lib/gitlab/import_export/project/import_export.yml110
-rw-r--r--lib/gitlab/import_export/project/import_task.rb4
-rw-r--r--lib/gitlab/import_export/project/object_builder.rb14
-rw-r--r--lib/gitlab/import_export/project/relation_saver.rb3
-rw-r--r--lib/gitlab/import_export/project/relation_tree_restorer.rb2
-rw-r--r--lib/gitlab/import_export/project/tree_saver.rb3
-rw-r--r--lib/gitlab/instrumentation/redis.rb21
-rw-r--r--lib/gitlab/instrumentation/redis_base.rb14
-rw-r--r--lib/gitlab/instrumentation/redis_interceptor.rb57
-rw-r--r--lib/gitlab/issuable/clone/copy_resource_events_service.rb8
-rw-r--r--lib/gitlab/jira_import.rb5
-rw-r--r--lib/gitlab/kubernetes.rb8
-rw-r--r--lib/gitlab/legacy_github_import/client.rb14
-rw-r--r--lib/gitlab/legacy_github_import/project_creator.rb10
-rw-r--r--lib/gitlab/mailgun/webhook_processors/failure_logger.rb7
-rw-r--r--lib/gitlab/manifest_import/metadata.rb6
-rw-r--r--lib/gitlab/marginalia/comment.rb2
-rw-r--r--lib/gitlab/markdown_cache/redis/store.rb14
-rw-r--r--lib/gitlab/memory/jemalloc.rb10
-rw-r--r--lib/gitlab/memory/reports/jemalloc_stats.rb9
-rw-r--r--lib/gitlab/memory/watchdog.rb139
-rw-r--r--lib/gitlab/metrics/dashboard/stages/grafana_formatter.rb10
-rw-r--r--lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics.rb18
-rw-r--r--lib/gitlab/metrics/dashboard/validator/client.rb4
-rw-r--r--lib/gitlab/metrics/exporter/metrics_middleware.rb4
-rw-r--r--lib/gitlab/metrics/global_search_slis.rb114
-rw-r--r--lib/gitlab/metrics/samplers/puma_sampler.rb14
-rw-r--r--lib/gitlab/metrics/samplers/ruby_sampler.rb18
-rw-r--r--lib/gitlab/metrics/system.rb16
-rw-r--r--lib/gitlab/nav/top_nav_menu_builder.rb14
-rw-r--r--lib/gitlab/nav/top_nav_menu_header.rb14
-rw-r--r--lib/gitlab/nav/top_nav_menu_item.rb1
-rw-r--r--lib/gitlab/nav/top_nav_view_model_builder.rb7
-rw-r--r--lib/gitlab/no_cache_headers.rb4
-rw-r--r--lib/gitlab/pagination/gitaly_keyset_pager.rb4
-rw-r--r--lib/gitlab/pagination/keyset/column_order_definition.rb8
-rw-r--r--lib/gitlab/patch/sidekiq_cron_poller.rb21
-rw-r--r--lib/gitlab/quick_actions/issue_actions.rb63
-rw-r--r--lib/gitlab/quick_actions/merge_request_actions.rb39
-rw-r--r--lib/gitlab/quick_actions/timeline_text_and_date_time_separator.rb58
-rw-r--r--lib/gitlab/reactive_cache_set_cache.rb6
-rw-r--r--lib/gitlab/redis.rb19
-rw-r--r--lib/gitlab/redis/cache.rb2
-rw-r--r--lib/gitlab/redis/multi_store.rb2
-rw-r--r--lib/gitlab/repository_hash_cache.rb6
-rw-r--r--lib/gitlab/repository_set_cache.rb14
-rw-r--r--lib/gitlab/request_forgery_protection.rb2
-rw-r--r--lib/gitlab/seeder.rb42
-rw-r--r--lib/gitlab/seeders/ci/daily_build_group_report_result.rb47
-rw-r--r--lib/gitlab/set_cache.rb12
-rw-r--r--lib/gitlab/shell.rb4
-rw-r--r--lib/gitlab/sidekiq_daemon/memory_killer.rb6
-rw-r--r--lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb10
-rw-r--r--lib/gitlab/sidekiq_middleware/server_metrics.rb26
-rw-r--r--lib/gitlab/sidekiq_versioning.rb6
-rw-r--r--lib/gitlab/slash_commands/presenters/base.rb20
-rw-r--r--lib/gitlab/spamcheck/client.rb49
-rw-r--r--lib/gitlab/subscription_portal.rb5
-rw-r--r--lib/gitlab/template/gitignore_template.rb2
-rw-r--r--lib/gitlab/tracking.rb2
-rw-r--r--lib/gitlab/tree_summary.rb4
-rw-r--r--lib/gitlab/uploads/migration_helper.rb38
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric.rb11
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/count_user_auth_metric.rb17
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/redis_metric.rb26
-rw-r--r--lib/gitlab/usage_data.rb6
-rw-r--r--lib/gitlab/usage_data_counters.rb19
-rw-r--r--lib/gitlab/usage_data_counters/base_counter.rb4
-rw-r--r--lib/gitlab/usage_data_counters/hll_redis_counter.rb9
-rw-r--r--lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb69
-rw-r--r--lib/gitlab/usage_data_counters/known_events/ci_templates.yml28
-rw-r--r--lib/gitlab/usage_data_counters/known_events/code_review_events.yml85
-rw-r--r--lib/gitlab/usage_data_counters/known_events/common.yml15
-rw-r--r--lib/gitlab/usage_data_counters/known_events/ecosystem.yml8
-rw-r--r--lib/gitlab/usage_data_counters/known_events/epic_board_events.yml19
-rw-r--r--lib/gitlab/usage_data_counters/known_events/kubernetes_agent.yml1
-rw-r--r--lib/gitlab/usage_data_counters/known_events/quickactions.yml12
-rw-r--r--lib/gitlab/usage_data_counters/merge_request_activity_unique_counter.rb10
-rw-r--r--lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb2
-rw-r--r--lib/gitlab/utils/deep_size.rb4
-rw-r--r--lib/gitlab/utils/execution_tracker.rb25
-rw-r--r--lib/gitlab/view/presenter/base.rb7
-rw-r--r--lib/gitlab/visibility_level.rb8
-rw-r--r--lib/gitlab/web_hooks/recursion_detection.rb6
-rw-r--r--lib/gitlab/workhorse.rb5
-rw-r--r--lib/gitlab_edition.rb15
-rw-r--r--lib/google_api/cloud_platform/client.rb2
-rw-r--r--lib/learn_gitlab/onboarding.rb69
-rw-r--r--lib/learn_gitlab/project.rb38
-rw-r--r--lib/object_storage/direct_upload.rb2
-rw-r--r--lib/omni_auth/strategies/bitbucket.rb2
-rw-r--r--lib/peek/views/redis_detailed.rb6
-rw-r--r--lib/product_analytics/event_params.rb68
-rw-r--r--lib/security/weak_passwords.rb88
-rw-r--r--lib/sidebars/groups/menus/observability_menu.rb34
-rw-r--r--lib/sidebars/groups/menus/packages_registries_menu.rb6
-rw-r--r--lib/sidebars/groups/menus/settings_menu.rb31
-rw-r--r--lib/sidebars/groups/panel.rb1
-rw-r--r--lib/sidebars/projects/menus/infrastructure_menu.rb12
-rw-r--r--lib/sidebars/projects/menus/learn_gitlab_menu.rb4
-rw-r--r--lib/sidebars/projects/menus/merge_requests_menu.rb4
-rw-r--r--lib/sidebars/projects/menus/monitor_menu.rb10
-rw-r--r--lib/sidebars/projects/menus/packages_registries_menu.rb9
-rw-r--r--lib/sidebars/projects/menus/settings_menu.rb16
-rw-r--r--lib/sidebars/projects/panel.rb3
-rw-r--r--lib/tasks/gitlab/assets.rake11
-rw-r--r--lib/tasks/gitlab/db/truncate_legacy_tables.rake31
-rw-r--r--lib/tasks/gitlab/db/validate_config.rake2
-rw-r--r--lib/tasks/gitlab/import_export/export.rake6
-rw-r--r--lib/tasks/gitlab/import_export/import.rake6
-rw-r--r--lib/tasks/gitlab/tw/codeowners.rake10
-rw-r--r--lib/tasks/gitlab/uploads/migrate.rake22
-rw-r--r--lib/tasks/gitlab/usage_data.rake22
-rw-r--r--lib/tasks/haml-lint.rake7
-rw-r--r--lib/tasks/rubocop.rake13
-rw-r--r--lib/tasks/tanuki_emoji.rake18
-rw-r--r--locale/am_ET/gitlab.po1308
-rw-r--r--locale/ar_SA/gitlab.po1356
-rw-r--r--locale/as_IN/gitlab.po1308
-rw-r--r--locale/az_AZ/gitlab.po1308
-rw-r--r--locale/ba_RU/gitlab.po1296
-rw-r--r--locale/bg/gitlab.po1308
-rw-r--r--locale/bn_BD/gitlab.po1308
-rw-r--r--locale/bn_IN/gitlab.po1308
-rw-r--r--locale/br_FR/gitlab.po1344
-rw-r--r--locale/bs_BA/gitlab.po1320
-rw-r--r--locale/ca_ES/gitlab.po1308
-rw-r--r--locale/cs_CZ/gitlab.po1332
-rw-r--r--locale/cy_GB/gitlab.po1356
-rw-r--r--locale/da_DK/gitlab.po1322
-rw-r--r--locale/de/gitlab.po1332
-rw-r--r--locale/el_GR/gitlab.po1308
-rw-r--r--locale/en_GB/gitlab.po1320
-rw-r--r--locale/eo/gitlab.po1308
-rw-r--r--locale/es/gitlab.po1350
-rw-r--r--locale/et_EE/gitlab.po1308
-rw-r--r--locale/fa_IR/gitlab.po1308
-rw-r--r--locale/fi_FI/gitlab.po1308
-rw-r--r--locale/fil_PH/gitlab.po1308
-rw-r--r--locale/fr/gitlab.po1318
-rw-r--r--locale/gitlab.pot1671
-rw-r--r--locale/gl_ES/gitlab.po1308
-rw-r--r--locale/he_IL/gitlab.po1332
-rw-r--r--locale/hi_IN/gitlab.po1308
-rw-r--r--locale/hr_HR/gitlab.po1320
-rw-r--r--locale/hu_HU/gitlab.po1308
-rw-r--r--locale/hy_AM/gitlab.po1308
-rw-r--r--locale/id_ID/gitlab.po1296
-rw-r--r--locale/ig_NG/gitlab.po1296
-rw-r--r--locale/is_IS/gitlab.po1308
-rw-r--r--locale/it/gitlab.po1308
-rw-r--r--locale/ja/gitlab.po1366
-rw-r--r--locale/ka_GE/gitlab.po1308
-rw-r--r--locale/kab/gitlab.po1308
-rw-r--r--locale/ko/gitlab.po3008
-rw-r--r--locale/ku_TR/gitlab.po1308
-rw-r--r--locale/ky_KG/gitlab.po1308
-rw-r--r--locale/lt_LT/gitlab.po1332
-rw-r--r--locale/mk_MK/gitlab.po1308
-rw-r--r--locale/ml_IN/gitlab.po1308
-rw-r--r--locale/mn_MN/gitlab.po1308
-rw-r--r--locale/nb_NO/gitlab.po1374
-rw-r--r--locale/nl_NL/gitlab.po1308
-rw-r--r--locale/or_IN/gitlab.po1308
-rw-r--r--locale/pa_IN/gitlab.po1308
-rw-r--r--locale/pl_PL/gitlab.po1332
-rw-r--r--locale/pt_BR/gitlab.po1690
-rw-r--r--locale/pt_PT/gitlab.po1308
-rw-r--r--locale/ro_RO/gitlab.po1468
-rw-r--r--locale/ru/gitlab.po1346
-rw-r--r--locale/si_LK/gitlab.po1318
-rw-r--r--locale/sk_SK/gitlab.po1332
-rw-r--r--locale/sl_SI/gitlab.po1332
-rw-r--r--locale/sq_AL/gitlab.po1308
-rw-r--r--locale/sr_CS/gitlab.po1320
-rw-r--r--locale/sr_SP/gitlab.po1320
-rw-r--r--locale/sv_SE/gitlab.po1308
-rw-r--r--locale/sw_KE/gitlab.po1308
-rw-r--r--locale/ta_IN/gitlab.po1308
-rw-r--r--locale/th_TH/gitlab.po1296
-rw-r--r--locale/tr_TR/gitlab.po1316
-rw-r--r--locale/uk/gitlab.po1722
-rw-r--r--locale/ur_PK/gitlab.po1308
-rw-r--r--locale/uz_UZ/gitlab.po1308
-rw-r--r--locale/vi_VN/gitlab.po1296
-rw-r--r--locale/zh_CN/gitlab.po1486
-rw-r--r--locale/zh_HK/gitlab.po1298
-rw-r--r--locale/zh_TW/gitlab.po1640
-rw-r--r--package.json41
-rw-r--r--qa/.confiner/master.yml2
-rw-r--r--qa/Gemfile2
-rw-r--r--qa/Gemfile.lock23
-rw-r--r--qa/README.md6
-rw-r--r--qa/Rakefile28
-rw-r--r--qa/lib/gitlab/page/admin/subscription.rb9
-rw-r--r--qa/qa.rb1
-rw-r--r--qa/qa/fixtures/auto_devops_rack/Dockerfile9
-rw-r--r--qa/qa/fixtures/auto_devops_rack/Gemfile5
-rw-r--r--qa/qa/fixtures/auto_devops_rack/Gemfile.lock15
-rw-r--r--qa/qa/fixtures/auto_devops_rack/Rakefile9
-rw-r--r--qa/qa/fixtures/auto_devops_rack/config.ru3
-rw-r--r--qa/qa/fixtures/package_managers/terraform/module_upload.yaml.erb21
-rw-r--r--qa/qa/flow/login.rb4
-rw-r--r--qa/qa/flow/merge_request.rb7
-rw-r--r--qa/qa/flow/pipeline.rb4
-rw-r--r--qa/qa/flow/project.rb4
-rw-r--r--qa/qa/flow/purchase.rb2
-rw-r--r--qa/qa/flow/saml.rb4
-rw-r--r--qa/qa/flow/settings.rb4
-rw-r--r--qa/qa/flow/sign_up.rb4
-rw-r--r--qa/qa/flow/user.rb4
-rw-r--r--qa/qa/flow/user_onboarding.rb4
-rw-r--r--qa/qa/page/base.rb2
-rw-r--r--qa/qa/page/component/access_tokens.rb8
-rw-r--r--qa/qa/page/component/ci_badge_link.rb4
-rw-r--r--qa/qa/page/component/content_editor.rb9
-rw-r--r--qa/qa/page/component/note.rb4
-rw-r--r--qa/qa/page/component/project/templates.rb2
-rw-r--r--qa/qa/page/component/web_ide/modal/create_new_file.rb2
-rw-r--r--qa/qa/page/component/wiki_page_form.rb4
-rw-r--r--qa/qa/page/file/shared/commit_message.rb4
-rw-r--r--qa/qa/page/file/show.rb5
-rw-r--r--qa/qa/page/group/members.rb4
-rw-r--r--qa/qa/page/group/menu.rb6
-rw-r--r--qa/qa/page/group/settings/package_registries.rb23
-rw-r--r--qa/qa/page/main/menu.rb33
-rw-r--r--qa/qa/page/merge_request/show.rb2
-rw-r--r--qa/qa/page/profile/ssh_keys.rb15
-rw-r--r--qa/qa/page/project/fork/new.rb29
-rw-r--r--qa/qa/page/project/infrastructure/kubernetes/index.rb9
-rw-r--r--qa/qa/page/project/job/show.rb6
-rw-r--r--qa/qa/page/project/monitor/metrics/show.rb8
-rw-r--r--qa/qa/page/project/new.rb2
-rw-r--r--qa/qa/page/project/packages/index.rb9
-rw-r--r--qa/qa/page/project/pipeline/new.rb2
-rw-r--r--qa/qa/page/project/settings/main.rb11
-rw-r--r--qa/qa/page/project/settings/merge_request.rb2
-rw-r--r--qa/qa/page/project/settings/mirroring_repositories.rb3
-rw-r--r--qa/qa/page/project/settings/pages.rb1
-rw-r--r--qa/qa/page/project/settings/protected_branches.rb12
-rw-r--r--qa/qa/page/project/show.rb8
-rw-r--r--qa/qa/page/project/sub_menus/packages.rb12
-rw-r--r--qa/qa/page/project/sub_menus/repository.rb8
-rw-r--r--qa/qa/page/project/sub_menus/settings.rb8
-rw-r--r--qa/qa/page/project/web_ide/edit.rb2
-rw-r--r--qa/qa/resource/base.rb24
-rw-r--r--qa/qa/resource/ci_variable.rb6
-rw-r--r--qa/qa/resource/clusters/agent.rb17
-rw-r--r--qa/qa/resource/clusters/agent_token.rb21
-rw-r--r--qa/qa/resource/fork.rb2
-rw-r--r--qa/qa/resource/group.rb5
-rw-r--r--qa/qa/resource/group_access_token.rb47
-rw-r--r--qa/qa/resource/group_base.rb4
-rw-r--r--qa/qa/resource/impersonation_token.rb2
-rw-r--r--qa/qa/resource/job.rb9
-rw-r--r--qa/qa/resource/kubernetes_cluster/project_cluster.rb50
-rw-r--r--qa/qa/resource/personal_access_token.rb42
-rw-r--r--qa/qa/resource/project_access_token.rb48
-rw-r--r--qa/qa/resource/runner.rb5
-rw-r--r--qa/qa/resource/sandbox.rb6
-rw-r--r--qa/qa/resource/user.rb4
-rw-r--r--qa/qa/runtime/browser.rb8
-rw-r--r--qa/qa/runtime/env.rb20
-rw-r--r--qa/qa/runtime/fixtures.rb2
-rw-r--r--qa/qa/runtime/user.rb5
-rw-r--r--qa/qa/scenario/test/instance/cloud_activation.rb13
-rw-r--r--qa/qa/scenario/test/instance/integrations.rb13
-rw-r--r--qa/qa/scenario/test/instance/jira.rb13
-rw-r--r--qa/qa/scenario/test/instance/large_setup.rb13
-rw-r--r--qa/qa/scenario/test/instance/metrics.rb13
-rw-r--r--qa/qa/scenario/test/instance/object_storage.rb13
-rw-r--r--qa/qa/scenario/test/instance/packages.rb13
-rw-r--r--qa/qa/scenario/test/instance/repository_storage.rb13
-rw-r--r--qa/qa/scenario/test/instance/review_blocking.rb16
-rw-r--r--qa/qa/scenario/test/instance/review_non_blocking.rb16
-rw-r--r--qa/qa/service/cluster_provider/gcloud.rb38
-rw-r--r--qa/qa/service/kubernetes_cluster.rb14
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb93
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb63
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/changing_repository_storage_spec.rb98
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb96
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/gitaly_mtls_spec.rb41
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/praefect_connectivity_spec.rb45
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb114
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb72
-rw-r--r--qa/qa/specs/features/api/12_systems/gitaly/praefect_repo_sync_spec.rb96
-rw-r--r--qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb9
-rw-r--r--qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb7
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/automatic_failover_and_recovery_spec.rb93
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/backend_node_recovery_spec.rb65
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/changing_repository_storage_spec.rb94
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb92
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/gitaly_mtls_spec.rb43
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/praefect_connectivity_spec.rb41
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/praefect_dataloss_spec.rb104
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/praefect_replication_queue_spec.rb71
-rw-r--r--qa/qa/specs/features/api/3_create/gitaly/praefect_repo_sync_spec.rb75
-rw-r--r--qa/qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb2
-rw-r--r--qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb2
-rw-r--r--qa/qa/specs/features/api/3_create/repository/files_spec.rb2
-rw-r--r--qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb16
-rw-r--r--qa/qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb2
-rw-r--r--qa/qa/specs/features/api/3_create/repository/storage_size_spec.rb2
-rw-r--r--qa/qa/specs/features/api/3_create/repository/tag_revision_trigger_prereceive_hook_spec.rb43
-rw-r--r--qa/qa/specs/features/api/4_verify/file_variable_spec.rb142
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/rebase_merge_request_spec.rb8
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/pages/new_static_page_spec.rb54
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/pages/pages_pipeline_spec.rb47
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/branch_with_unusual_name_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/file/file_with_unusual_name_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/license_detecton_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_to_canary_gitaly_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb72
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb21
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/infrastructure_registry/terraform_module_registry_spec.rb81
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/maven/maven_group_level_spec.rb8
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb160
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/kubernetes/kubernetes_integration_spec.rb38
-rw-r--r--qa/qa/specs/features/shared_examples/merge_with_code_owner_shared_examples.rb8
-rw-r--r--qa/qa/specs/helpers/context_selector.rb4
-rw-r--r--qa/qa/specs/runner.rb3
-rw-r--r--qa/qa/specs/spec_helper.rb4
-rw-r--r--qa/qa/support/api.rb23
-rw-r--r--qa/qa/support/json_formatter.rb1
-rw-r--r--qa/qa/support/matchers/eventually_matcher.rb1
-rw-r--r--qa/qa/tools/ci/ff_changes.rb66
-rw-r--r--qa/qa/tools/ci/helpers.rb50
-rw-r--r--qa/qa/tools/ci/non_empty_suites.rb98
-rw-r--r--qa/qa/tools/ci/qa_changes.rb116
-rw-r--r--qa/qa/tools/ci/test_results.rb78
-rw-r--r--qa/qa/tools/revoke_all_personal_access_tokens.rb44
-rw-r--r--qa/qa/tools/revoke_user_personal_access_tokens.rb94
-rw-r--r--qa/qa/tools/test_resources_handler.rb1
-rw-r--r--qa/spec/fixtures/ff/bulk_import_projects.yml8
-rw-r--r--qa/spec/resource/base_spec.rb22
-rw-r--r--qa/spec/resource/user_spec.rb9
-rw-r--r--qa/spec/specs/helpers/context_selector_spec.rb20
-rw-r--r--qa/spec/tools/ci/ff_changes_spec.rb51
-rw-r--r--qa/spec/tools/ci/non_empty_suites_spec.rb19
-rw-r--r--qa/spec/tools/ci/qa_changes_spec.rb87
-rw-r--r--qa/tasks/ci.rake61
-rw-r--r--qa/tasks/helpers/util.rb49
-rw-r--r--rubocop/check_graceful_task.rb83
-rw-r--r--rubocop/code_reuse_helpers.rb8
-rw-r--r--rubocop/cop/active_model_errors_direct_manipulation.rb10
-rw-r--r--rubocop/cop/active_record_association_reload.rb4
-rw-r--r--rubocop/cop/api/base.rb14
-rw-r--r--rubocop/cop/api/grape_array_missing_coerce.rb2
-rw-r--r--rubocop/cop/avoid_becomes.rb4
-rw-r--r--rubocop/cop/avoid_break_from_strong_memoize.rb2
-rw-r--r--rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers.rb4
-rw-r--r--rubocop/cop/avoid_return_from_blocks.rb2
-rw-r--r--rubocop/cop/avoid_route_redirect_leading_slash.rb14
-rw-r--r--rubocop/cop/ban_catch_throw.rb4
-rw-r--r--rubocop/cop/code_reuse/finder.rb2
-rw-r--r--rubocop/cop/code_reuse/presenter.rb2
-rw-r--r--rubocop/cop/code_reuse/serializer.rb2
-rw-r--r--rubocop/cop/code_reuse/service_class.rb2
-rw-r--r--rubocop/cop/code_reuse/worker.rb2
-rw-r--r--rubocop/cop/database/disable_referential_integrity.rb2
-rw-r--r--rubocop/cop/database/establish_connection.rb4
-rw-r--r--rubocop/cop/database/multiple_databases.rb4
-rw-r--r--rubocop/cop/database/rescue_query_canceled.rb2
-rw-r--r--rubocop/cop/database/rescue_statement_timeout.rb2
-rw-r--r--rubocop/cop/default_scope.rb4
-rw-r--r--rubocop/cop/destroy_all.rb4
-rw-r--r--rubocop/cop/file_decompression.rb4
-rw-r--r--rubocop/cop/filename_length.rb8
-rw-r--r--rubocop/cop/gitlab/avoid_feature_category_not_owned.rb4
-rw-r--r--rubocop/cop/gitlab/avoid_feature_get.rb30
-rw-r--r--rubocop/cop/gitlab/avoid_uploaded_file_from_params.rb4
-rw-r--r--rubocop/cop/gitlab/bulk_insert.rb4
-rw-r--r--rubocop/cop/gitlab/change_timezone.rb2
-rw-r--r--rubocop/cop/gitlab/const_get_inherit_false.rb10
-rw-r--r--rubocop/cop/gitlab/delegate_predicate_methods.rb2
-rw-r--r--rubocop/cop/gitlab/deprecate_track_redis_hll_event.rb4
-rw-r--r--rubocop/cop/gitlab/event_store_subscriber.rb2
-rw-r--r--rubocop/cop/gitlab/except.rb4
-rw-r--r--rubocop/cop/gitlab/feature_available_usage.rb7
-rw-r--r--rubocop/cop/gitlab/finder_with_find_by.rb14
-rw-r--r--rubocop/cop/gitlab/httparty.rb41
-rw-r--r--rubocop/cop/gitlab/intersect.rb4
-rw-r--r--rubocop/cop/gitlab/json.rb18
-rw-r--r--rubocop/cop/gitlab/keys_first_and_values_first.rb81
-rw-r--r--rubocop/cop/gitlab/mark_used_feature_flags.rb43
-rw-r--r--rubocop/cop/gitlab/module_with_instance_variables.rb6
-rw-r--r--rubocop/cop/gitlab/policy_rule_boolean.rb2
-rw-r--r--rubocop/cop/gitlab/predicate_memoization.rb2
-rw-r--r--rubocop/cop/gitlab/rails_logger.rb2
-rw-r--r--rubocop/cop/gitlab/union.rb4
-rw-r--r--rubocop/cop/graphql/authorize_types.rb4
-rw-r--r--rubocop/cop/graphql/descriptions.rb20
-rw-r--r--rubocop/cop/graphql/gid_expected_type.rb2
-rw-r--r--rubocop/cop/graphql/graphql_name_position.rb4
-rw-r--r--rubocop/cop/graphql/id_type.rb2
-rw-r--r--rubocop/cop/graphql/json_type.rb4
-rw-r--r--rubocop/cop/graphql/old_types.rb4
-rw-r--r--rubocop/cop/graphql/resolver_type.rb4
-rw-r--r--rubocop/cop/group_public_or_visible_to_user.rb4
-rw-r--r--rubocop/cop/ignored_columns.rb6
-rw-r--r--rubocop/cop/include_sidekiq_worker.rb12
-rw-r--r--rubocop/cop/inject_enterprise_edition_module.rb14
-rw-r--r--rubocop/cop/lint/last_keyword_argument.rb10
-rw-r--r--rubocop/cop/migration/add_column_with_default.rb4
-rw-r--r--rubocop/cop/migration/add_columns_to_wide_tables.rb4
-rw-r--r--rubocop/cop/migration/add_concurrent_foreign_key.rb4
-rw-r--r--rubocop/cop/migration/add_concurrent_index.rb10
-rw-r--r--rubocop/cop/migration/add_index.rb4
-rw-r--r--rubocop/cop/migration/add_limit_to_text_columns.rb6
-rw-r--r--rubocop/cop/migration/add_reference.rb6
-rw-r--r--rubocop/cop/migration/add_timestamps.rb4
-rw-r--r--rubocop/cop/migration/background_migration_base_class.rb4
-rw-r--r--rubocop/cop/migration/background_migration_record.rb6
-rw-r--r--rubocop/cop/migration/background_migrations.rb4
-rw-r--r--rubocop/cop/migration/complex_indexes_require_name.rb4
-rw-r--r--rubocop/cop/migration/create_table_with_foreign_keys.rb2
-rw-r--r--rubocop/cop/migration/datetime.rb6
-rw-r--r--rubocop/cop/migration/drop_table.rb4
-rw-r--r--rubocop/cop/migration/migration_record.rb4
-rw-r--r--rubocop/cop/migration/prevent_global_enable_lock_retries_with_disable_ddl_transaction.rb2
-rw-r--r--rubocop/cop/migration/prevent_index_creation.rb4
-rw-r--r--rubocop/cop/migration/prevent_strings.rb4
-rw-r--r--rubocop/cop/migration/refer_to_index_by_name.rb4
-rw-r--r--rubocop/cop/migration/remove_column.rb4
-rw-r--r--rubocop/cop/migration/remove_concurrent_index.rb8
-rw-r--r--rubocop/cop/migration/remove_index.rb4
-rw-r--r--rubocop/cop/migration/safer_boolean_column.rb4
-rw-r--r--rubocop/cop/migration/schedule_async.rb4
-rw-r--r--rubocop/cop/migration/sidekiq_queue_migrate.rb4
-rw-r--r--rubocop/cop/migration/timestamps.rb4
-rw-r--r--rubocop/cop/migration/update_column_in_batches.rb8
-rw-r--r--rubocop/cop/migration/versioned_migration_class.rb6
-rw-r--r--rubocop/cop/migration/with_lock_retries_disallowed_method.rb6
-rw-r--r--rubocop/cop/migration/with_lock_retries_with_change.rb8
-rw-r--r--rubocop/cop/performance/active_record_subtransaction_methods.rb4
-rw-r--r--rubocop/cop/performance/active_record_subtransactions.rb2
-rw-r--r--rubocop/cop/performance/ar_count_each.rb4
-rw-r--r--rubocop/cop/performance/ar_exists_and_present_blank.rb6
-rw-r--r--rubocop/cop/performance/readlines_each.rb11
-rw-r--r--rubocop/cop/prefer_class_methods_over_module.rb14
-rw-r--r--rubocop/cop/project_path_helper.rb18
-rw-r--r--rubocop/cop/put_group_routes_under_scope.rb2
-rw-r--r--rubocop/cop/put_project_routes_under_scope.rb2
-rw-r--r--rubocop/cop/qa/ambiguous_page_object_name.rb2
-rw-r--r--rubocop/cop/qa/element_with_pattern.rb2
-rw-r--r--rubocop/cop/qa/selector_usage.rb2
-rw-r--r--rubocop/cop/rspec/any_instance_of.rb29
-rw-r--r--rubocop/cop/rspec/be_success_matcher.rb10
-rw-r--r--rubocop/cop/rspec/env_assignment.rb10
-rw-r--r--rubocop/cop/rspec/expect_gitlab_tracking.rb2
-rw-r--r--rubocop/cop/rspec/factories_in_migration_specs.rb4
-rw-r--r--rubocop/cop/rspec/factory_bot/inline_association.rb10
-rw-r--r--rubocop/cop/rspec/have_gitlab_http_status.rb22
-rw-r--r--rubocop/cop/rspec/httparty_basic_auth.rb12
-rw-r--r--rubocop/cop/rspec/modify_sidekiq_middleware.rb10
-rw-r--r--rubocop/cop/rspec/timecop_freeze.rb10
-rw-r--r--rubocop/cop/rspec/timecop_travel.rb10
-rw-r--r--rubocop/cop/rspec/web_mock_enable.rb14
-rw-r--r--rubocop/cop/ruby_interpolation_in_translation.rb2
-rw-r--r--rubocop/cop/safe_params.rb4
-rw-r--r--rubocop/cop/scalability/bulk_perform_with_context.rb4
-rw-r--r--rubocop/cop/scalability/cron_worker_context.rb4
-rw-r--r--rubocop/cop/scalability/file_uploads.rb4
-rw-r--r--rubocop/cop/scalability/idempotent_worker.rb4
-rw-r--r--rubocop/cop/sidekiq_load_balancing/worker_data_consistency.rb4
-rw-r--r--rubocop/cop/sidekiq_options_queue.rb4
-rw-r--r--rubocop/cop/static_translation_definition.rb2
-rw-r--r--rubocop/cop/usage_data/distinct_count_by_large_foreign_key.rb4
-rw-r--r--rubocop/cop/usage_data/histogram_with_large_table.rb8
-rw-r--r--rubocop/cop/usage_data/instrumentation_superclass.rb2
-rw-r--r--rubocop/cop/usage_data/large_table.rb4
-rw-r--r--rubocop/cop/user_admin.rb4
-rw-r--r--rubocop/cop_todo.rb6
-rw-r--r--rubocop/formatter/graceful_formatter.rb109
-rw-r--r--rubocop/formatter/todo_formatter.rb24
-rwxr-xr-xscripts/build_assets_image8
-rwxr-xr-xscripts/bundle_size_review68
-rwxr-xr-xscripts/checkout-mr-source-sha7
-rwxr-xr-xscripts/determine-qa-tests106
-rw-r--r--scripts/frontend/startup_css/constants.js3
-rwxr-xr-xscripts/generate-e2e-pipeline39
-rwxr-xr-xscripts/glfm/run-snapshot-tests.sh6
-rw-r--r--scripts/lib/glfm/constants.rb8
-rw-r--r--scripts/lib/glfm/parse_examples.rb25
-rw-r--r--scripts/lib/glfm/render_static_html.rb113
-rw-r--r--scripts/lib/glfm/shared.rb34
-rw-r--r--scripts/lib/glfm/update_example_snapshots.rb218
-rwxr-xr-xscripts/lint-doc.sh2
-rwxr-xr-xscripts/rspec_check_order_dependence76
-rw-r--r--scripts/rspec_helpers.sh19
-rwxr-xr-xscripts/rubocop-parse73
-rwxr-xr-xscripts/static-analysis10
-rwxr-xr-xscripts/trigger-build.rb65
-rw-r--r--scripts/utils.sh6
-rwxr-xr-xscripts/verify-tff-mapping12
-rw-r--r--spec/benchmarks/banzai_benchmark.rb10
-rw-r--r--spec/components/layouts/horizontal_section_component_spec.rb88
-rw-r--r--spec/components/pajamas/badge_component_spec.rb148
-rw-r--r--spec/components/previews/layouts/horizontal_section_component_preview.rb22
-rw-r--r--spec/components/previews/pajamas/badge_component_preview.rb61
-rw-r--r--spec/config/metrics/aggregates/aggregated_metrics_spec.rb4
-rw-r--r--spec/config/object_store_settings_spec.rb69
-rw-r--r--spec/config/settings_spec.rb28
-rw-r--r--spec/controllers/admin/application_settings_controller_spec.rb13
-rw-r--r--spec/controllers/admin/applications_controller_spec.rb84
-rw-r--r--spec/controllers/admin/cohorts_controller_spec.rb12
-rw-r--r--spec/controllers/admin/runners_controller_spec.rb4
-rw-r--r--spec/controllers/admin/spam_logs_controller_spec.rb33
-rw-r--r--spec/controllers/admin/topics_controller_spec.rb2
-rw-r--r--spec/controllers/admin/users_controller_spec.rb164
-rw-r--r--spec/controllers/application_controller_spec.rb2
-rw-r--r--spec/controllers/concerns/continue_params_spec.rb2
-rw-r--r--spec/controllers/concerns/product_analytics_tracking_spec.rb2
-rw-r--r--spec/controllers/concerns/redis_tracking_spec.rb3
-rw-r--r--spec/controllers/confirmations_controller_spec.rb4
-rw-r--r--spec/controllers/graphql_controller_spec.rb9
-rw-r--r--spec/controllers/groups/group_members_controller_spec.rb19
-rw-r--r--spec/controllers/groups/labels_controller_spec.rb4
-rw-r--r--spec/controllers/groups/releases_controller_spec.rb4
-rw-r--r--spec/controllers/groups/settings/applications_controller_spec.rb84
-rw-r--r--spec/controllers/help_controller_spec.rb33
-rw-r--r--spec/controllers/import/bitbucket_controller_spec.rb8
-rw-r--r--spec/controllers/import/github_controller_spec.rb10
-rw-r--r--spec/controllers/import/manifest_controller_spec.rb2
-rw-r--r--spec/controllers/oauth/applications_controller_spec.rb27
-rw-r--r--spec/controllers/oauth/token_info_controller_spec.rb12
-rw-r--r--spec/controllers/omniauth_callbacks_controller_spec.rb2
-rw-r--r--spec/controllers/profiles/personal_access_tokens_controller_spec.rb16
-rw-r--r--spec/controllers/profiles_controller_spec.rb8
-rw-r--r--spec/controllers/projects/analytics/cycle_analytics/stages_controller_spec.rb1
-rw-r--r--spec/controllers/projects/artifacts_controller_spec.rb6
-rw-r--r--spec/controllers/projects/blame_controller_spec.rb2
-rw-r--r--spec/controllers/projects/ci/daily_build_group_report_results_controller_spec.rb74
-rw-r--r--spec/controllers/projects/ci/lints_controller_spec.rb9
-rw-r--r--spec/controllers/projects/cycle_analytics_controller_spec.rb12
-rw-r--r--spec/controllers/projects/deploy_keys_controller_spec.rb4
-rw-r--r--spec/controllers/projects/environments_controller_spec.rb126
-rw-r--r--spec/controllers/projects/feature_flags_controller_spec.rb6
-rw-r--r--spec/controllers/projects/grafana_api_controller_spec.rb4
-rw-r--r--spec/controllers/projects/graphs_controller_spec.rb15
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb136
-rw-r--r--spec/controllers/projects/labels_controller_spec.rb8
-rw-r--r--spec/controllers/projects/merge_requests/drafts_controller_spec.rb73
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb91
-rw-r--r--spec/controllers/projects/notes_controller_spec.rb2
-rw-r--r--spec/controllers/projects/pipeline_schedules_controller_spec.rb3
-rw-r--r--spec/controllers/projects/pipelines/tests_controller_spec.rb11
-rw-r--r--spec/controllers/projects/pipelines_controller_spec.rb87
-rw-r--r--spec/controllers/projects/project_members_controller_spec.rb32
-rw-r--r--spec/controllers/projects/registry/tags_controller_spec.rb2
-rw-r--r--spec/controllers/projects/releases_controller_spec.rb2
-rw-r--r--spec/controllers/projects/service_desk_controller_spec.rb5
-rw-r--r--spec/controllers/projects/settings/integrations_controller_spec.rb8
-rw-r--r--spec/controllers/projects/settings/merge_requests_controller_spec.rb52
-rw-r--r--spec/controllers/projects/tree_controller_spec.rb6
-rw-r--r--spec/controllers/projects_controller_spec.rb36
-rw-r--r--spec/controllers/registrations/welcome_controller_spec.rb4
-rw-r--r--spec/controllers/registrations_controller_spec.rb33
-rw-r--r--spec/controllers/repositories/git_http_controller_spec.rb4
-rw-r--r--spec/controllers/search_controller_spec.rb53
-rw-r--r--spec/controllers/snippets/notes_controller_spec.rb2
-rw-r--r--spec/db/development/add_security_training_providers_spec.rb9
-rw-r--r--spec/db/migration_spec.rb4
-rw-r--r--spec/db/production/add_security_training_providers_spec.rb9
-rw-r--r--spec/db/schema_spec.rb8
-rw-r--r--spec/dependencies/omniauth_saml_spec.rb2
-rw-r--r--spec/deprecation_toolkit_env.rb2
-rw-r--r--spec/experiments/application_experiment_spec.rb26
-rw-r--r--spec/factories/ci/build_trace_chunks.rb4
-rw-r--r--spec/factories/ci/builds.rb20
-rw-r--r--spec/factories/ci/job_artifacts.rb33
-rw-r--r--spec/factories/ci/pipeline_artifacts.rb2
-rw-r--r--spec/factories/ci/reports/sbom/components.rb19
-rw-r--r--spec/factories/ci/reports/sbom/sources.rb34
-rw-r--r--spec/factories/commit_statuses.rb13
-rw-r--r--spec/factories/design_management/designs.rb2
-rw-r--r--spec/factories/design_management/versions.rb4
-rw-r--r--spec/factories/emails.rb2
-rw-r--r--spec/factories/environments.rb2
-rw-r--r--spec/factories/external_pull_requests.rb2
-rw-r--r--spec/factories/generic_commit_statuses.rb8
-rw-r--r--spec/factories/gitlab/database/postgres_index.rb2
-rw-r--r--spec/factories/group_members.rb1
-rw-r--r--spec/factories/groups.rb4
-rw-r--r--spec/factories/ml/candidates.rb7
-rw-r--r--spec/factories/ml/experiments.rb8
-rw-r--r--spec/factories/namespaces/sync_events.rb7
-rw-r--r--spec/factories/onboarding/progresses.rb7
-rw-r--r--spec/factories/onboarding_progresses.rb7
-rw-r--r--spec/factories/packages/debian/component_file.rb8
-rw-r--r--spec/factories/packages/dependencies.rb4
-rw-r--r--spec/factories/packages/package_files.rb8
-rw-r--r--spec/factories/packages/package_tags.rb2
-rw-r--r--spec/factories/packages/packages.rb20
-rw-r--r--spec/factories/packages/rpm/metadata.rb12
-rw-r--r--spec/factories/project_members.rb1
-rw-r--r--spec/factories/projects.rb1
-rw-r--r--spec/factories/prometheus_alert.rb2
-rw-r--r--spec/factories/prometheus_metrics.rb2
-rw-r--r--spec/factories/protected_branches.rb52
-rw-r--r--spec/factories/users/ghost_user_migrations.rb9
-rw-r--r--spec/factories/work_items.rb4
-rw-r--r--spec/fast_spec_helper.rb10
-rw-r--r--spec/features/admin/admin_mode/workers_spec.rb54
-rw-r--r--spec/features/admin/admin_mode_spec.rb4
-rw-r--r--spec/features/admin/admin_runners_spec.rb75
-rw-r--r--spec/features/admin/admin_sees_background_migrations_spec.rb2
-rw-r--r--spec/features/admin/admin_settings_spec.rb35
-rw-r--r--spec/features/admin/users/user_spec.rb4
-rw-r--r--spec/features/clusters/create_agent_spec.rb9
-rw-r--r--spec/features/commits/user_view_commits_spec.rb57
-rw-r--r--spec/features/cycle_analytics_spec.rb4
-rw-r--r--spec/features/dashboard/datetime_on_tooltips_spec.rb2
-rw-r--r--spec/features/dashboard/milestones_spec.rb7
-rw-r--r--spec/features/dashboard/todos/todos_sorting_spec.rb2
-rw-r--r--spec/features/discussion_comments/issue_spec.rb1
-rw-r--r--spec/features/discussion_comments/merge_request_spec.rb1
-rw-r--r--spec/features/groups/group_runners_spec.rb52
-rw-r--r--spec/features/groups/navbar_spec.rb20
-rw-r--r--spec/features/groups/settings/packages_and_registries_spec.rb40
-rw-r--r--spec/features/groups/settings/user_searches_in_settings_spec.rb2
-rw-r--r--spec/features/groups_spec.rb71
-rw-r--r--spec/features/help_dropdown_spec.rb3
-rw-r--r--spec/features/ide/user_commits_changes_spec.rb2
-rw-r--r--spec/features/ide/user_opens_merge_request_spec.rb2
-rw-r--r--spec/features/ide_spec.rb26
-rw-r--r--spec/features/incidents/incident_timeline_events_spec.rb58
-rw-r--r--spec/features/incidents/user_uses_quick_actions_spec.rb26
-rw-r--r--spec/features/invites_spec.rb2
-rw-r--r--spec/features/issuables/markdown_references/jira_spec.rb2
-rw-r--r--spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb11
-rw-r--r--spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb2
-rw-r--r--spec/features/issues/issue_detail_spec.rb9
-rw-r--r--spec/features/issues/issue_sidebar_spec.rb6
-rw-r--r--spec/features/issues/resource_label_events_spec.rb1
-rw-r--r--spec/features/issues/user_creates_issue_spec.rb2
-rw-r--r--spec/features/issues/user_interacts_with_awards_spec.rb4
-rw-r--r--spec/features/issues/user_sees_empty_state_spec.rb4
-rw-r--r--spec/features/issues/user_sorts_issue_comments_spec.rb2
-rw-r--r--spec/features/jira_oauth_provider_authorize_spec.rb8
-rw-r--r--spec/features/markdown/markdown_spec.rb4
-rw-r--r--spec/features/merge_request/batch_comments_spec.rb2
-rw-r--r--spec/features/merge_request/user_comments_on_diff_spec.rb5
-rw-r--r--spec/features/merge_request/user_comments_on_merge_request_spec.rb1
-rw-r--r--spec/features/merge_request/user_creates_merge_request_spec.rb183
-rw-r--r--spec/features/merge_request/user_edits_merge_request_spec.rb11
-rw-r--r--spec/features/merge_request/user_manages_subscription_spec.rb10
-rw-r--r--spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb24
-rw-r--r--spec/features/merge_request/user_sees_deployment_widget_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_merge_widget_spec.rb2
-rw-r--r--spec/features/merge_request/user_uses_quick_actions_spec.rb2
-rw-r--r--spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb4
-rw-r--r--spec/features/monitor_sidebar_link_spec.rb102
-rw-r--r--spec/features/populate_new_pipeline_vars_with_params_spec.rb42
-rw-r--r--spec/features/profile_spec.rb38
-rw-r--r--spec/features/profiles/active_sessions_spec.rb2
-rw-r--r--spec/features/profiles/user_edit_profile_spec.rb21
-rw-r--r--spec/features/profiles/user_visits_profile_spec.rb57
-rw-r--r--spec/features/project_variables_spec.rb30
-rw-r--r--spec/features/projects/badges/coverage_spec.rb2
-rw-r--r--spec/features/projects/blobs/blame_spec.rb99
-rw-r--r--spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb4
-rw-r--r--spec/features/projects/branches/user_creates_branch_spec.rb133
-rw-r--r--spec/features/projects/commit/mini_pipeline_graph_spec.rb4
-rw-r--r--spec/features/projects/commits/multi_view_diff_spec.rb5
-rw-r--r--spec/features/projects/environments/environment_metrics_spec.rb6
-rw-r--r--spec/features/projects/environments/environment_spec.rb7
-rw-r--r--spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb5
-rw-r--r--spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb7
-rw-r--r--spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb2
-rw-r--r--spec/features/projects/files/user_edits_files_spec.rb17
-rw-r--r--spec/features/projects/fork_spec.rb5
-rw-r--r--spec/features/projects/jobs/user_browses_jobs_spec.rb7
-rw-r--r--spec/features/projects/milestones/milestones_sorting_spec.rb2
-rw-r--r--spec/features/projects/navbar_spec.rb4
-rw-r--r--spec/features/projects/new_project_spec.rb14
-rw-r--r--spec/features/projects/pipeline_schedules_spec.rb4
-rw-r--r--spec/features/projects/pipelines/legacy_pipeline_spec.rb48
-rw-r--r--spec/features/projects/pipelines/legacy_pipelines_spec.rb13
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb30
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb73
-rw-r--r--spec/features/projects/releases/user_creates_release_spec.rb7
-rw-r--r--spec/features/projects/settings/merge_requests_settings_spec.rb261
-rw-r--r--spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb81
-rw-r--r--spec/features/projects/settings/registry_settings_spec.rb44
-rw-r--r--spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb41
-rw-r--r--spec/features/projects/settings/visibility_settings_spec.rb20
-rw-r--r--spec/features/projects/settings/webhooks_settings_spec.rb6
-rw-r--r--spec/features/projects/show/user_interacts_with_stars_spec.rb38
-rw-r--r--spec/features/projects/show/user_sees_collaboration_links_spec.rb4
-rw-r--r--spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb11
-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/tree_show_spec.rb10
-rw-r--r--spec/features/projects/tree/upload_file_spec.rb2
-rw-r--r--spec/features/projects/user_sorts_projects_spec.rb4
-rw-r--r--spec/features/projects_spec.rb100
-rw-r--r--spec/features/runners_spec.rb98
-rw-r--r--spec/features/search/user_searches_for_code_spec.rb78
-rw-r--r--spec/features/search/user_uses_header_search_field_spec.rb1
-rw-r--r--spec/features/snippets/user_creates_snippet_spec.rb2
-rw-r--r--spec/features/tags/developer_creates_tag_spec.rb2
-rw-r--r--spec/features/user_opens_link_to_comment_spec.rb2
-rw-r--r--spec/features/users/email_verification_on_login_spec.rb35
-rw-r--r--spec/features/users/login_spec.rb4
-rw-r--r--spec/features/users/show_spec.rb4
-rw-r--r--spec/features/users/signup_spec.rb7
-rw-r--r--spec/features/work_items/work_item_children_spec.rb110
-rw-r--r--spec/finders/bulk_imports/entities_finder_spec.rb18
-rw-r--r--spec/finders/ci/daily_build_group_report_results_finder_spec.rb22
-rw-r--r--spec/finders/ci/jobs_finder_spec.rb2
-rw-r--r--spec/finders/ci/pipelines_for_merge_request_finder_spec.rb20
-rw-r--r--spec/finders/ci/runners_finder_spec.rb14
-rw-r--r--spec/finders/concerns/packages/finder_helper_spec.rb6
-rw-r--r--spec/finders/container_repositories_finder_spec.rb8
-rw-r--r--spec/finders/context_commits_finder_spec.rb26
-rw-r--r--spec/finders/crm/organizations_finder_spec.rb71
-rw-r--r--spec/finders/database/batched_background_migrations_finder_spec.rb27
-rw-r--r--spec/finders/deploy_tokens/tokens_finder_spec.rb48
-rw-r--r--spec/finders/deployments_finder_spec.rb42
-rw-r--r--spec/finders/design_management/versions_finder_spec.rb4
-rw-r--r--spec/finders/environments/environments_finder_spec.rb10
-rw-r--r--spec/finders/group_descendants_finder_spec.rb6
-rw-r--r--spec/finders/group_members_finder_spec.rb55
-rw-r--r--spec/finders/groups/accepting_group_transfers_finder_spec.rb135
-rw-r--r--spec/finders/groups/projects_requiring_authorizations_refresh/on_direct_membership_finder_spec.rb14
-rw-r--r--spec/finders/groups/projects_requiring_authorizations_refresh/on_transfer_finder_spec.rb6
-rw-r--r--spec/finders/groups_finder_spec.rb102
-rw-r--r--spec/finders/merge_requests_finder/params_spec.rb23
-rw-r--r--spec/finders/merge_requests_finder_spec.rb58
-rw-r--r--spec/finders/milestones_finder_spec.rb2
-rw-r--r--spec/finders/packages/group_packages_finder_spec.rb4
-rw-r--r--spec/finders/packages/npm/package_finder_spec.rb2
-rw-r--r--spec/finders/projects_finder_spec.rb10
-rw-r--r--spec/finders/template_finder_spec.rb8
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_noteable.json8
-rw-r--r--spec/fixtures/api/schemas/external_validation.json12
-rw-r--r--spec/fixtures/api/schemas/graphql/packages/package_details.json6
-rw-r--r--spec/fixtures/api/schemas/ml/get_experiment.json23
-rw-r--r--spec/fixtures/api/schemas/ml/run.json47
-rw-r--r--spec/fixtures/api/schemas/ml/update_run.json35
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/job.json6
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/packages/package.json7
-rw-r--r--spec/fixtures/blockquote_fence_after.md24
-rw-r--r--spec/fixtures/blockquote_fence_before.md24
-rw-r--r--spec/fixtures/cdn/google_cloud.json17
-rw-r--r--spec/fixtures/lib/generators/gitlab/usage_metric_generator/sample_metric_test.rb4
-rw-r--r--spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/100_files.zipbin15902 -> 0 bytes
-rw-r--r--spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/200_mb_decompressed.zipbin203718 -> 0 bytes
-rw-r--r--spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/multiple_files.zipbin332 -> 0 bytes
-rw-r--r--spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/single_file.zipbin177 -> 0 bytes
-rw-r--r--spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/with_directory.zipbin520 -> 0 bytes
-rw-r--r--spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/zipbomb.zipbin1042247 -> 0 bytes
-rw-r--r--spec/fixtures/lib/gitlab/import_export/complex/project.json74
-rw-r--r--spec/fixtures/lib/gitlab/import_export/complex/tree/project/issues.ndjson2
-rw-r--r--spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson2
-rw-r--r--spec/fixtures/lib/gitlab/import_export/group/project.json33
-rw-r--r--spec/fixtures/lib/gitlab/import_export/group/tree/project/issues.ndjson2
-rw-r--r--spec/fixtures/markdown.md.erb36
-rw-r--r--spec/fixtures/markdown/markdown_golden_master_examples.yml2
-rw-r--r--spec/fixtures/packages/debian/distribution/D-I-Packages2
-rw-r--r--spec/fixtures/packages/debian/distribution/OtherSHA2561
-rw-r--r--spec/fixtures/packages/debian/distribution/Sources2
-rw-r--r--spec/fixtures/packages/rpm/hello-0.0.1-1.fc29.x86_64.rpmbin0 -> 6604 bytes
-rw-r--r--spec/fixtures/security_reports/deprecated/gl-sast-report.json2
-rw-r--r--spec/fixtures/security_reports/feature-branch/gl-sast-report.json2
-rw-r--r--spec/fixtures/security_reports/feature-branch/gl-secret-detection-report.json2
-rw-r--r--spec/fixtures/security_reports/master/gl-common-scanning-report-names.json2
-rw-r--r--spec/fixtures/security_reports/master/gl-common-scanning-report-without-top-level-scanner.json50
-rw-r--r--spec/fixtures/security_reports/master/gl-common-scanning-report.json405
-rw-r--r--spec/fixtures/security_reports/master/gl-sast-missing-scanner.json2
-rw-r--r--spec/fixtures/security_reports/master/gl-sast-report-bandit.json2
-rw-r--r--spec/fixtures/security_reports/master/gl-sast-report-gosec.json2
-rw-r--r--spec/fixtures/security_reports/master/gl-sast-report-minimal.json2
-rw-r--r--spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-bandit.json2
-rw-r--r--spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-gosec.json2
-rw-r--r--spec/fixtures/security_reports/master/gl-sast-report.json2
-rw-r--r--spec/fixtures/security_reports/master/gl-secret-detection-report.json2
-rw-r--r--spec/frontend/__helpers__/datetime_helpers.js2
-rw-r--r--spec/frontend/__helpers__/dl_locator_helper.js13
-rw-r--r--spec/frontend/__helpers__/keep_alive_component_helper_spec.js6
-rw-r--r--spec/frontend/__helpers__/matchers/to_validate_json_schema_spec.js4
-rw-r--r--spec/frontend/__helpers__/shared_test_setup.js3
-rw-r--r--spec/frontend/__mocks__/sortablejs/index.js2
-rw-r--r--spec/frontend/access_tokens/components/access_token_table_app_spec.js15
-rw-r--r--spec/frontend/access_tokens/components/expires_at_field_spec.js16
-rw-r--r--spec/frontend/access_tokens/components/new_access_token_app_spec.js41
-rw-r--r--spec/frontend/access_tokens/index_spec.js2
-rw-r--r--spec/frontend/add_context_commits_modal/components/add_context_commits_modal_spec.js12
-rw-r--r--spec/frontend/add_context_commits_modal/components/review_tab_container_spec.js4
-rw-r--r--spec/frontend/admin/analytics/devops_score/components/devops_score_spec.js4
-rw-r--r--spec/frontend/admin/topics/components/topic_select_spec.js91
-rw-r--r--spec/frontend/alert_management/components/alert_management_empty_state_spec.js2
-rw-r--r--spec/frontend/alert_management/components/alert_management_list_wrapper_spec.js8
-rw-r--r--spec/frontend/alert_management/components/alert_management_table_spec.js6
-rw-r--r--spec/frontend/alerts_settings/components/alert_mapping_builder_spec.js12
-rw-r--r--spec/frontend/alerts_settings/components/alerts_form_spec.js2
-rw-r--r--spec/frontend/alerts_settings/components/alerts_integrations_list_spec.js10
-rw-r--r--spec/frontend/alerts_settings/components/alerts_settings_form_spec.js6
-rw-r--r--spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js18
-rw-r--r--spec/frontend/analytics/components/activity_chart_spec.js2
-rw-r--r--spec/frontend/analytics/shared/components/daterange_spec.js15
-rw-r--r--spec/frontend/analytics/shared/components/metric_popover_spec.js6
-rw-r--r--spec/frontend/analytics/shared/components/projects_dropdown_filter_spec.js6
-rw-r--r--spec/frontend/analytics/usage_trends/components/app_spec.js6
-rw-r--r--spec/frontend/analytics/usage_trends/components/usage_trends_count_chart_spec.js12
-rw-r--r--spec/frontend/analytics/usage_trends/components/users_chart_spec.js8
-rw-r--r--spec/frontend/analytics/usage_trends/utils_spec.js6
-rw-r--r--spec/frontend/api/harbor_registry_spec.js107
-rw-r--r--spec/frontend/artifacts_settings/components/keep_latest_artifact_checkbox_spec.js4
-rw-r--r--spec/frontend/authentication/two_factor_auth/components/recovery_codes_spec.js8
-rw-r--r--spec/frontend/authentication/two_factor_auth/index_spec.js2
-rw-r--r--spec/frontend/autosave_spec.js44
-rw-r--r--spec/frontend/badges/components/badge_settings_spec.js6
-rw-r--r--spec/frontend/batch_comments/components/diff_file_drafts_spec.js4
-rw-r--r--spec/frontend/batch_comments/components/draft_note_spec.js8
-rw-r--r--spec/frontend/batch_comments/components/preview_dropdown_spec.js2
-rw-r--r--spec/frontend/batch_comments/components/preview_item_spec.js2
-rw-r--r--spec/frontend/batch_comments/components/publish_dropdown_spec.js4
-rw-r--r--spec/frontend/batch_comments/components/review_bar_spec.js4
-rw-r--r--spec/frontend/batch_comments/components/submit_dropdown_spec.js35
-rw-r--r--spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js3
-rw-r--r--spec/frontend/behaviors/bind_in_out_spec.js6
-rw-r--r--spec/frontend/blob/sketch/index_spec.js22
-rw-r--r--spec/frontend/boards/board_card_inner_spec.js20
-rw-r--r--spec/frontend/boards/board_list_helper.js1
-rw-r--r--spec/frontend/boards/components/__snapshots__/board_blocked_icon_spec.js.snap2
-rw-r--r--spec/frontend/boards/components/board_blocked_icon_spec.js74
-rw-r--r--spec/frontend/boards/components/board_card_move_to_position_spec.js133
-rw-r--r--spec/frontend/boards/components/board_card_spec.js9
-rw-r--r--spec/frontend/boards/components/board_new_issue_spec.js2
-rw-r--r--spec/frontend/boards/components/issue_due_date_spec.js2
-rw-r--r--spec/frontend/boards/components/item_count_spec.js4
-rw-r--r--spec/frontend/boards/mock_data.js76
-rw-r--r--spec/frontend/boards/stores/actions_spec.js38
-rw-r--r--spec/frontend/boards/stores/mutations_spec.js25
-rw-r--r--spec/frontend/branches/components/divergence_graph_spec.js4
-rw-r--r--spec/frontend/captcha/captcha_modal_spec.js2
-rw-r--r--spec/frontend/cascading_settings/components/lock_popovers_spec.js4
-rw-r--r--spec/frontend/chronic_duration_spec.js2
-rw-r--r--spec/frontend/ci_lint/components/ci_lint_spec.js6
-rw-r--r--spec/frontend/ci_secure_files/components/secure_files_list_spec.js4
-rw-r--r--spec/frontend/ci_settings_pipeline_triggers/components/triggers_list_spec.js12
-rw-r--r--spec/frontend/ci_variable_list/components/ci_project_variables_spec.js215
-rw-r--r--spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js30
-rw-r--r--spec/frontend/ci_variable_list/components/ci_variable_popover_spec.js2
-rw-r--r--spec/frontend/ci_variable_list/components/legacy_ci_variable_modal_spec.js8
-rw-r--r--spec/frontend/ci_variable_list/mocks.js15
-rw-r--r--spec/frontend/ci_variable_list/store/mutations_spec.js2
-rw-r--r--spec/frontend/clusters/agents/components/agent_integration_status_row_spec.js96
-rw-r--r--spec/frontend/clusters/agents/components/integration_status_spec.js111
-rw-r--r--spec/frontend/clusters/agents/components/show_spec.js6
-rw-r--r--spec/frontend/clusters_list/components/agent_table_spec.js6
-rw-r--r--spec/frontend/clusters_list/components/agents_spec.js2
-rw-r--r--spec/frontend/clusters_list/components/ancestor_notice_spec.js2
-rw-r--r--spec/frontend/clusters_list/components/clusters_main_view_spec.js2
-rw-r--r--spec/frontend/clusters_list/components/clusters_spec.js2
-rw-r--r--spec/frontend/clusters_list/components/install_agent_modal_spec.js2
-rw-r--r--spec/frontend/clusters_list/components/node_error_help_text_spec.js2
-rw-r--r--spec/frontend/code_navigation/components/app_spec.js4
-rw-r--r--spec/frontend/code_navigation/components/popover_spec.js8
-rw-r--r--spec/frontend/code_navigation/utils/index_spec.js4
-rw-r--r--spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js216
-rw-r--r--spec/frontend/commit/commit_pipeline_status_component_spec.js4
-rw-r--r--spec/frontend/commit/mock_data.js211
-rw-r--r--spec/frontend/commit/pipelines/pipelines_table_spec.js27
-rw-r--r--spec/frontend/confidential_merge_request/components/dropdown_spec.js6
-rw-r--r--spec/frontend/content_editor/components/__snapshots__/toolbar_link_button_spec.js.snap62
-rw-r--r--spec/frontend/content_editor/components/bubble_menus/bubble_menu_spec.js126
-rw-r--r--spec/frontend/content_editor/components/bubble_menus/code_block_bubble_menu_spec.js296
-rw-r--r--spec/frontend/content_editor/components/bubble_menus/code_block_spec.js296
-rw-r--r--spec/frontend/content_editor/components/bubble_menus/formatting_bubble_menu_spec.js90
-rw-r--r--spec/frontend/content_editor/components/bubble_menus/formatting_spec.js87
-rw-r--r--spec/frontend/content_editor/components/bubble_menus/link_bubble_menu_spec.js305
-rw-r--r--spec/frontend/content_editor/components/bubble_menus/link_spec.js227
-rw-r--r--spec/frontend/content_editor/components/bubble_menus/media_bubble_menu_spec.js237
-rw-r--r--spec/frontend/content_editor/components/bubble_menus/media_spec.js234
-rw-r--r--spec/frontend/content_editor/components/content_editor_alert_spec.js25
-rw-r--r--spec/frontend/content_editor/components/content_editor_spec.js213
-rw-r--r--spec/frontend/content_editor/components/editor_state_observer_spec.js26
-rw-r--r--spec/frontend/content_editor/components/loading_indicator_spec.js46
-rw-r--r--spec/frontend/content_editor/components/toolbar_image_button_spec.js21
-rw-r--r--spec/frontend/content_editor/components/toolbar_link_button_spec.js18
-rw-r--r--spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js17
-rw-r--r--spec/frontend/content_editor/components/toolbar_table_button_spec.js14
-rw-r--r--spec/frontend/content_editor/components/toolbar_text_style_dropdown_spec.js4
-rw-r--r--spec/frontend/content_editor/components/wrappers/code_block_spec.js6
-rw-r--r--spec/frontend/content_editor/extensions/paste_markdown_spec.js21
-rw-r--r--spec/frontend/content_editor/remark_markdown_processing_spec.js73
-rw-r--r--spec/frontend/content_editor/render_html_and_json_for_all_examples.js6
-rw-r--r--spec/frontend/content_editor/services/content_editor_spec.js95
-rw-r--r--spec/frontend/content_editor/services/markdown_serializer_spec.js21
-rw-r--r--spec/frontend/crm/form_spec.js5
-rw-r--r--spec/frontend/crm/mock_data.js22
-rw-r--r--spec/frontend/crm/organizations_root_spec.js92
-rw-r--r--spec/frontend/cycle_analytics/__snapshots__/total_time_spec.js.snap12
-rw-r--r--spec/frontend/cycle_analytics/base_spec.js2
-rw-r--r--spec/frontend/cycle_analytics/path_navigation_spec.js12
-rw-r--r--spec/frontend/cycle_analytics/value_stream_metrics_spec.js2
-rw-r--r--spec/frontend/deploy_freeze/components/deploy_freeze_modal_spec.js2
-rw-r--r--spec/frontend/deploy_freeze/components/deploy_freeze_settings_spec.js4
-rw-r--r--spec/frontend/deploy_freeze/components/timezone_dropdown_spec.js6
-rw-r--r--spec/frontend/deprecated_jquery_dropdown_spec.js4
-rw-r--r--spec/frontend/design_management/components/design_notes/design_note_spec.js1
-rw-r--r--spec/frontend/design_management/components/design_notes/design_reply_form_spec.js40
-rw-r--r--spec/frontend/design_management/components/design_presentation_spec.js2
-rw-r--r--spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap4
-rw-r--r--spec/frontend/design_management/components/toolbar/index_spec.js2
-rw-r--r--spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap8
-rw-r--r--spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap4
-rw-r--r--spec/frontend/design_management/pages/index_spec.js2
-rw-r--r--spec/frontend/diffs/components/app_spec.js36
-rw-r--r--spec/frontend/diffs/components/collapsed_files_warning_spec.js4
-rw-r--r--spec/frontend/diffs/components/commit_item_spec.js6
-rw-r--r--spec/frontend/diffs/components/commit_widget_spec.js2
-rw-r--r--spec/frontend/diffs/components/compare_dropdown_layout_spec.js2
-rw-r--r--spec/frontend/diffs/components/diff_code_quality_spec.js3
-rw-r--r--spec/frontend/diffs/components/diff_comment_cell_spec.js8
-rw-r--r--spec/frontend/diffs/components/diff_content_spec.js12
-rw-r--r--spec/frontend/diffs/components/diff_discussion_reply_spec.js4
-rw-r--r--spec/frontend/diffs/components/diff_discussions_spec.js16
-rw-r--r--spec/frontend/diffs/components/diff_file_header_spec.js23
-rw-r--r--spec/frontend/diffs/components/diff_file_row_spec.js8
-rw-r--r--spec/frontend/diffs/components/diff_file_spec.js12
-rw-r--r--spec/frontend/diffs/components/diff_gutter_avatars_spec.js12
-rw-r--r--spec/frontend/diffs/components/diff_line_note_form_spec.js2
-rw-r--r--spec/frontend/diffs/components/diff_line_spec.js65
-rw-r--r--spec/frontend/diffs/components/diff_stats_spec.js2
-rw-r--r--spec/frontend/diffs/components/diff_view_spec.js21
-rw-r--r--spec/frontend/diffs/components/image_diff_overlay_spec.js2
-rw-r--r--spec/frontend/diffs/components/no_changes_spec.js2
-rw-r--r--spec/frontend/diffs/components/tree_list_spec.js4
-rw-r--r--spec/frontend/editor/components/source_editor_toolbar_spec.js2
-rw-r--r--spec/frontend/editor/source_editor_extension_spec.js2
-rw-r--r--spec/frontend/editor/source_editor_instance_spec.js6
-rw-r--r--spec/frontend/editor/source_editor_webide_ext_spec.js6
-rw-r--r--spec/frontend/emoji/components/category_spec.js10
-rw-r--r--spec/frontend/emoji/components/utils_spec.js4
-rw-r--r--spec/frontend/emoji/index_spec.js98
-rw-r--r--spec/frontend/environments/deployment_spec.js83
-rw-r--r--spec/frontend/environments/environment_table_spec.js2
-rw-r--r--spec/frontend/environments/environments_app_spec.js1
-rw-r--r--spec/frontend/environments/graphql/mock_data.js38
-rw-r--r--spec/frontend/environments/new_environment_item_spec.js2
-rw-r--r--spec/frontend/environments/new_environment_spec.js2
-rw-r--r--spec/frontend/error_tracking/components/error_tracking_list_spec.js6
-rw-r--r--spec/frontend/error_tracking/components/stacktrace_entry_spec.js4
-rw-r--r--spec/frontend/error_tracking_settings/components/app_spec.js10
-rw-r--r--spec/frontend/error_tracking_settings/components/project_dropdown_spec.js12
-rw-r--r--spec/frontend/feature_flags/components/environments_dropdown_spec.js2
-rw-r--r--spec/frontend/feature_flags/store/edit/actions_spec.js8
-rw-r--r--spec/frontend/feature_flags/store/index/actions_spec.js8
-rw-r--r--spec/frontend/feature_flags/store/new/actions_spec.js4
-rw-r--r--spec/frontend/filtered_search/components/recent_searches_dropdown_content_spec.js12
-rw-r--r--spec/frontend/filtered_search/droplab/drop_down_spec.js12
-rw-r--r--spec/frontend/fixtures/api_merge_requests.rb2
-rw-r--r--spec/frontend/fixtures/api_projects.rb2
-rw-r--r--spec/frontend/fixtures/application_settings.rb2
-rw-r--r--spec/frontend/fixtures/blob.rb2
-rw-r--r--spec/frontend/fixtures/branches.rb2
-rw-r--r--spec/frontend/fixtures/clusters.rb2
-rw-r--r--spec/frontend/fixtures/deploy_keys.rb8
-rw-r--r--spec/frontend/fixtures/groups.rb2
-rw-r--r--spec/frontend/fixtures/issues.rb2
-rw-r--r--spec/frontend/fixtures/jobs.rb2
-rw-r--r--spec/frontend/fixtures/labels.rb2
-rw-r--r--spec/frontend/fixtures/merge_requests.rb2
-rw-r--r--spec/frontend/fixtures/merge_requests_diffs.rb2
-rw-r--r--spec/frontend/fixtures/metrics_dashboard.rb2
-rw-r--r--spec/frontend/fixtures/pipeline_schedules.rb2
-rw-r--r--spec/frontend/fixtures/pipelines.rb2
-rw-r--r--spec/frontend/fixtures/projects.rb2
-rw-r--r--spec/frontend/fixtures/raw.rb2
-rw-r--r--spec/frontend/fixtures/search.rb69
-rw-r--r--spec/frontend/fixtures/snippet.rb2
-rw-r--r--spec/frontend/fixtures/startup_css.rb16
-rw-r--r--spec/frontend/fixtures/todos.rb2
-rw-r--r--spec/frontend/flash_spec.js2
-rw-r--r--spec/frontend/frequent_items/components/frequent_items_list_item_spec.js14
-rw-r--r--spec/frontend/frequent_items/components/frequent_items_list_spec.js10
-rw-r--r--spec/frontend/frequent_items/components/frequent_items_search_input_spec.js2
-rw-r--r--spec/frontend/frequent_items/utils_spec.js8
-rw-r--r--spec/frontend/google_cloud/databases/panel_spec.js17
-rw-r--r--spec/frontend/google_tag_manager/index_spec.js45
-rw-r--r--spec/frontend/groups/components/app_spec.js14
-rw-r--r--spec/frontend/groups/components/empty_state_spec.js2
-rw-r--r--spec/frontend/groups/components/group_item_spec.js34
-rw-r--r--spec/frontend/groups/components/groups_spec.js4
-rw-r--r--spec/frontend/groups/components/invite_members_banner_spec.js14
-rw-r--r--spec/frontend/groups/components/item_caret_spec.js4
-rw-r--r--spec/frontend/groups/components/item_stats_spec.js2
-rw-r--r--spec/frontend/groups/components/item_stats_value_spec.js2
-rw-r--r--spec/frontend/groups/components/item_type_icon_spec.js2
-rw-r--r--spec/frontend/groups/components/overview_tabs_spec.js187
-rw-r--r--spec/frontend/groups/components/visibility_level_dropdown_spec.js70
-rw-r--r--spec/frontend/header_search/init_spec.js10
-rw-r--r--spec/frontend/header_search/mock_data.js44
-rw-r--r--spec/frontend/header_search/store/actions_spec.js66
-rw-r--r--spec/frontend/header_search/store/getters_spec.js24
-rw-r--r--spec/frontend/ide/components/commit_sidebar/list_spec.js2
-rw-r--r--spec/frontend/ide/components/commit_sidebar/radio_group_spec.js8
-rw-r--r--spec/frontend/ide/components/preview/navigator_spec.js4
-rw-r--r--spec/frontend/ide/components/shared/tokened_input_spec.js2
-rw-r--r--spec/frontend/ide/init_gitlab_web_ide_spec.js62
-rw-r--r--spec/frontend/ide/stores/actions/file_spec.js2
-rw-r--r--spec/frontend/ide/stores/modules/commit/actions_spec.js2
-rw-r--r--spec/frontend/import_entities/components/group_dropdown_spec.js2
-rw-r--r--spec/frontend/import_entities/import_groups/components/import_table_spec.js18
-rw-r--r--spec/frontend/import_entities/import_groups/components/import_target_cell_spec.js4
-rw-r--r--spec/frontend/import_entities/import_projects/components/bitbucket_status_table_spec.js10
-rw-r--r--spec/frontend/import_entities/import_projects/components/import_projects_table_spec.js18
-rw-r--r--spec/frontend/import_entities/import_projects/components/provider_repo_table_row_spec.js16
-rw-r--r--spec/frontend/import_entities/import_projects/store/getters_spec.js4
-rw-r--r--spec/frontend/incidents/components/incidents_list_spec.js14
-rw-r--r--spec/frontend/incidents_settings/components/incidents_settings_tabs_spec.js6
-rw-r--r--spec/frontend/integrations/edit/components/trigger_fields_spec.js4
-rw-r--r--spec/frontend/invite_members/components/import_project_members_modal_spec.js2
-rw-r--r--spec/frontend/invite_members/components/invite_members_modal_spec.js32
-rw-r--r--spec/frontend/invite_members/components/user_limit_notification_spec.js37
-rw-r--r--spec/frontend/issuable/components/issue_assignees_spec.js2
-rw-r--r--spec/frontend/issuable/components/issue_milestone_spec.js2
-rw-r--r--spec/frontend/issuable/issuable_form_spec.js231
-rw-r--r--spec/frontend/issuable/related_issues/components/issue_token_spec.js6
-rw-r--r--spec/frontend/issuable/related_issues/components/related_issues_block_spec.js4
-rw-r--r--spec/frontend/issuable/related_issues/components/related_issues_list_spec.js4
-rw-r--r--spec/frontend/issues/create_merge_request_dropdown_spec.js2
-rw-r--r--spec/frontend/issues/list/components/issue_card_time_info_spec.js10
-rw-r--r--spec/frontend/issues/list/components/issues_list_app_spec.js29
-rw-r--r--spec/frontend/issues/list/components/jira_issues_import_status_app_spec.js10
-rw-r--r--spec/frontend/issues/new/components/title_suggestions_item_spec.js6
-rw-r--r--spec/frontend/issues/new/components/title_suggestions_spec.js2
-rw-r--r--spec/frontend/issues/related_merge_requests/components/related_merge_requests_spec.js4
-rw-r--r--spec/frontend/issues/show/components/app_spec.js2
-rw-r--r--spec/frontend/issues/show/components/description_spec.js56
-rw-r--r--spec/frontend/issues/show/components/edit_actions_spec.js82
-rw-r--r--spec/frontend/issues/show/components/fields/description_spec.js2
-rw-r--r--spec/frontend/issues/show/components/fields/title_spec.js2
-rw-r--r--spec/frontend/issues/show/components/header_actions_spec.js10
-rw-r--r--spec/frontend/issues/show/components/incidents/create_timeline_events_form_spec.js17
-rw-r--r--spec/frontend/issues/show/components/incidents/edit_timeline_event_spec.js44
-rw-r--r--spec/frontend/issues/show/components/incidents/highlight_bar_spec.js2
-rw-r--r--spec/frontend/issues/show/components/incidents/incident_tabs_spec.js8
-rw-r--r--spec/frontend/issues/show/components/incidents/mock_data.js42
-rw-r--r--spec/frontend/issues/show/components/incidents/timeline_events_form_spec.js40
-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.js157
-rw-r--r--spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js19
-rw-r--r--spec/frontend/issues/show/components/incidents/utils_spec.js6
-rw-r--r--spec/frontend/issues/show/components/pinned_links_spec.js2
-rw-r--r--spec/frontend/issues/show/components/sentry_error_stack_trace_spec.js8
-rw-r--r--spec/frontend/jira_connect/branches/components/new_branch_form_spec.js2
-rw-r--r--spec/frontend/jira_connect/subscriptions/api_spec.js118
-rw-r--r--spec/frontend/jira_connect/subscriptions/components/add_namespace_modal/groups_list_spec.js2
-rw-r--r--spec/frontend/jira_connect/subscriptions/components/app_spec.js21
-rw-r--r--spec/frontend/jira_connect/subscriptions/components/sign_in_oauth_button_spec.js87
-rw-r--r--spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_com_spec.js2
-rw-r--r--spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_multiversion/index_spec.js33
-rw-r--r--spec/frontend/jira_connect/subscriptions/store/actions_spec.js16
-rw-r--r--spec/frontend/jira_import/components/jira_import_app_spec.js10
-rw-r--r--spec/frontend/jira_import/components/jira_import_form_spec.js15
-rw-r--r--spec/frontend/jira_import/components/jira_import_progress_spec.js2
-rw-r--r--spec/frontend/jira_import/components/jira_import_setup_spec.js2
-rw-r--r--spec/frontend/jobs/components/artifacts_block_spec.js176
-rw-r--r--spec/frontend/jobs/components/commit_block_spec.js70
-rw-r--r--spec/frontend/jobs/components/empty_state_spec.js140
-rw-r--r--spec/frontend/jobs/components/environments_block_spec.js265
-rw-r--r--spec/frontend/jobs/components/erased_block_spec.js63
-rw-r--r--spec/frontend/jobs/components/filtered_search/jobs_filtered_search_spec.js34
-rw-r--r--spec/frontend/jobs/components/filtered_search/utils_spec.js19
-rw-r--r--spec/frontend/jobs/components/job/artifacts_block_spec.js176
-rw-r--r--spec/frontend/jobs/components/job/commit_block_spec.js70
-rw-r--r--spec/frontend/jobs/components/job/empty_state_spec.js140
-rw-r--r--spec/frontend/jobs/components/job/environments_block_spec.js265
-rw-r--r--spec/frontend/jobs/components/job/erased_block_spec.js63
-rw-r--r--spec/frontend/jobs/components/job/job_app_spec.js440
-rw-r--r--spec/frontend/jobs/components/job/job_container_item_spec.js98
-rw-r--r--spec/frontend/jobs/components/job/job_log_controllers_spec.js315
-rw-r--r--spec/frontend/jobs/components/job/job_retry_forward_deployment_modal_spec.js76
-rw-r--r--spec/frontend/jobs/components/job/job_sidebar_details_container_spec.js140
-rw-r--r--spec/frontend/jobs/components/job/job_sidebar_retry_button_spec.js69
-rw-r--r--spec/frontend/jobs/components/job/jobs_container_spec.js147
-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.js91
-rw-r--r--spec/frontend/jobs/components/job/manual_variables_form_spec.js156
-rw-r--r--spec/frontend/jobs/components/job/sidebar_detail_row_spec.js55
-rw-r--r--spec/frontend/jobs/components/job/sidebar_header_spec.js91
-rw-r--r--spec/frontend/jobs/components/job/sidebar_spec.js166
-rw-r--r--spec/frontend/jobs/components/job/stages_dropdown_spec.js192
-rw-r--r--spec/frontend/jobs/components/job/stuck_block_spec.js101
-rw-r--r--spec/frontend/jobs/components/job/trigger_block_spec.js85
-rw-r--r--spec/frontend/jobs/components/job/unmet_prerequisites_block_spec.js41
-rw-r--r--spec/frontend/jobs/components/job_app_spec.js440
-rw-r--r--spec/frontend/jobs/components/job_container_item_spec.js98
-rw-r--r--spec/frontend/jobs/components/job_log_controllers_spec.js315
-rw-r--r--spec/frontend/jobs/components/job_retry_forward_deployment_modal_spec.js76
-rw-r--r--spec/frontend/jobs/components/job_sidebar_details_container_spec.js140
-rw-r--r--spec/frontend/jobs/components/job_sidebar_retry_button_spec.js69
-rw-r--r--spec/frontend/jobs/components/jobs_container_spec.js147
-rw-r--r--spec/frontend/jobs/components/log/line_header_spec.js4
-rw-r--r--spec/frontend/jobs/components/log/line_spec.js2
-rw-r--r--spec/frontend/jobs/components/manual_variables_form_spec.js156
-rw-r--r--spec/frontend/jobs/components/sidebar_detail_row_spec.js55
-rw-r--r--spec/frontend/jobs/components/sidebar_spec.js227
-rw-r--r--spec/frontend/jobs/components/stages_dropdown_spec.js192
-rw-r--r--spec/frontend/jobs/components/stuck_block_spec.js101
-rw-r--r--spec/frontend/jobs/components/table/job_table_app_spec.js14
-rw-r--r--spec/frontend/jobs/components/trigger_block_spec.js85
-rw-r--r--spec/frontend/jobs/components/unmet_prerequisites_block_spec.js41
-rw-r--r--spec/frontend/jobs/store/actions_spec.js20
-rw-r--r--spec/frontend/jobs/store/mutations_spec.js4
-rw-r--r--spec/frontend/labels/components/delete_label_modal_spec.js2
-rw-r--r--spec/frontend/lib/dompurify_spec.js2
-rw-r--r--spec/frontend/lib/gfm/index_spec.js376
-rw-r--r--spec/frontend/lib/utils/apollo_startup_js_link_spec.js2
-rw-r--r--spec/frontend/lib/utils/common_utils_spec.js2
-rw-r--r--spec/frontend/lib/utils/datetime/date_calculation_utility_spec.js18
-rw-r--r--spec/frontend/lib/utils/finite_state_machine_spec.js4
-rw-r--r--spec/frontend/lib/utils/is_navigating_away_spec.js2
-rw-r--r--spec/frontend/lib/utils/navigation_utility_spec.js2
-rw-r--r--spec/frontend/lib/utils/poll_spec.js4
-rw-r--r--spec/frontend/lib/utils/text_markdown_spec.js27
-rw-r--r--spec/frontend/lib/utils/text_utility_spec.js35
-rw-r--r--spec/frontend/lib/utils/vuex_module_mappers_spec.js2
-rw-r--r--spec/frontend/locale/sprintf_spec.js18
-rw-r--r--spec/frontend/members/components/avatars/user_avatar_spec.js2
-rw-r--r--spec/frontend/members/mock_data.js1
-rw-r--r--spec/frontend/members/store/actions_spec.js4
-rw-r--r--spec/frontend/members/utils_spec.js16
-rw-r--r--spec/frontend/merge_conflicts/components/merge_conflict_resolver_app_spec.js4
-rw-r--r--spec/frontend/merge_conflicts/store/actions_spec.js14
-rw-r--r--spec/frontend/merge_request_tabs_spec.js2
-rw-r--r--spec/frontend/milestones/components/milestone_combobox_spec.js8
-rw-r--r--spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap4
-rw-r--r--spec/frontend/monitoring/components/charts/stacked_column_spec.js2
-rw-r--r--spec/frontend/monitoring/components/dashboard_panel_spec.js2
-rw-r--r--spec/frontend/monitoring/components/dashboard_spec.js10
-rw-r--r--spec/frontend/monitoring/components/dashboards_dropdown_spec.js3
-rw-r--r--spec/frontend/monitoring/components/duplicate_dashboard_form_spec.js2
-rw-r--r--spec/frontend/monitoring/components/duplicate_dashboard_modal_spec.js2
-rw-r--r--spec/frontend/monitoring/store/actions_spec.js2
-rw-r--r--spec/frontend/nav/components/top_nav_app_spec.js5
-rw-r--r--spec/frontend/nav/components/top_nav_dropdown_menu_spec.js2
-rw-r--r--spec/frontend/nav/components/top_nav_menu_item_spec.js2
-rw-r--r--spec/frontend/nav/components/top_nav_menu_sections_spec.js68
-rw-r--r--spec/frontend/nav/mock_data.js2
-rw-r--r--spec/frontend/notebook/cells/output/latex_spec.js2
-rw-r--r--spec/frontend/notebook/index_spec.js10
-rw-r--r--spec/frontend/notebook/lib/highlight_spec.js15
-rw-r--r--spec/frontend/notes/components/comment_form_spec.js8
-rw-r--r--spec/frontend/notes/components/discussion_counter_spec.js46
-rw-r--r--spec/frontend/notes/components/discussion_filter_spec.js81
-rw-r--r--spec/frontend/notes/components/discussion_notes_spec.js8
-rw-r--r--spec/frontend/notes/components/discussion_resolve_with_issue_button_spec.js2
-rw-r--r--spec/frontend/notes/components/multiline_comment_form_spec.js2
-rw-r--r--spec/frontend/notes/components/note_actions/timeline_event_button_spec.js35
-rw-r--r--spec/frontend/notes/components/note_body_spec.js10
-rw-r--r--spec/frontend/notes/components/note_form_spec.js12
-rw-r--r--spec/frontend/notes/components/note_header_spec.js30
-rw-r--r--spec/frontend/notes/components/noteable_discussion_spec.js2
-rw-r--r--spec/frontend/notes/components/noteable_note_spec.js2
-rw-r--r--spec/frontend/notes/components/sort_discussion_spec.js102
-rw-r--r--spec/frontend/notes/mixins/discussion_navigation_spec.js31
-rw-r--r--spec/frontend/notes/stores/actions_spec.js101
-rw-r--r--spec/frontend/notes/stores/getters_spec.js6
-rw-r--r--spec/frontend/notes/stores/mutation_spec.js2
-rw-r--r--spec/frontend/notifications/components/custom_notifications_modal_spec.js14
-rw-r--r--spec/frontend/notifications/components/notifications_dropdown_spec.js11
-rw-r--r--spec/frontend/operation_settings/components/metrics_settings_spec.js14
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/delete_button_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/delete_alert_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/details_header_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/partial_cleanup_alert_spec.js2
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/status_alert_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_loader_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/cleanup_status_spec.js2
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/cli_commands_spec.js85
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_row_spec.js2
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/registry_header_spec.js2
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/pages/details_spec.js20
-rw-r--r--spec/frontend/packages_and_registries/container_registry/explorer/pages/index_spec.js2
-rw-r--r--spec/frontend/packages_and_registries/harbor_registry/components/details/artifacts_list_row_spec.js143
-rw-r--r--spec/frontend/packages_and_registries/harbor_registry/components/details/artifacts_list_spec.js75
-rw-r--r--spec/frontend/packages_and_registries/harbor_registry/components/details/details_header_spec.js85
-rw-r--r--spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_header_spec.js9
-rw-r--r--spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_row_spec.js38
-rw-r--r--spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_spec.js10
-rw-r--r--spec/frontend/packages_and_registries/harbor_registry/components/tags/tags_header_spec.js52
-rw-r--r--spec/frontend/packages_and_registries/harbor_registry/components/tags/tags_list_row_spec.js75
-rw-r--r--spec/frontend/packages_and_registries/harbor_registry/components/tags/tags_list_spec.js66
-rw-r--r--spec/frontend/packages_and_registries/harbor_registry/mock_data.js269
-rw-r--r--spec/frontend/packages_and_registries/harbor_registry/pages/details_spec.js162
-rw-r--r--spec/frontend/packages_and_registries/harbor_registry/pages/index_spec.js2
-rw-r--r--spec/frontend/packages_and_registries/harbor_registry/pages/list_spec.js42
-rw-r--r--spec/frontend/packages_and_registries/harbor_registry/pages/tags_spec.js125
-rw-r--r--spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/app_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/package_files_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/package_history_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/infrastructure_title_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/packages_list_app_spec.js8
-rw-r--r--spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/packages_list_spec.js10
-rw-r--r--spec/frontend/packages_and_registries/infrastructure_registry/components/shared/__snapshots__/package_list_row_spec.js.snap2
-rw-r--r--spec/frontend/packages_and_registries/infrastructure_registry/components/shared/infrastructure_icon_and_name_spec.js2
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/details/nuget_installation_spec.js2
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap2
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/list/package_list_row_spec.js6
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js2
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/list/packages_title_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/list/tokens/package_type_token_spec.js8
-rw-r--r--spec/frontend/packages_and_registries/package_registry/pages/details_spec.js14
-rw-r--r--spec/frontend/packages_and_registries/settings/group/components/__snapshots__/settings_titles_spec.js.snap18
-rw-r--r--spec/frontend/packages_and_registries/settings/group/components/dependency_proxy_settings_spec.js2
-rw-r--r--spec/frontend/packages_and_registries/settings/group/components/duplicates_settings_spec.js143
-rw-r--r--spec/frontend/packages_and_registries/settings/group/components/exceptions_input_spec.js108
-rw-r--r--spec/frontend/packages_and_registries/settings/group/components/generic_settings_spec.js54
-rw-r--r--spec/frontend/packages_and_registries/settings/group/components/maven_settings_spec.js54
-rw-r--r--spec/frontend/packages_and_registries/settings/group/components/package_settings_spec.js115
-rw-r--r--spec/frontend/packages_and_registries/settings/group/components/settings_titles_spec.js35
-rw-r--r--spec/frontend/packages_and_registries/settings/project/settings/components/cleanup_image_tags_spec.js164
-rw-r--r--spec/frontend/packages_and_registries/settings/project/settings/components/container_expiration_policy_form_spec.js109
-rw-r--r--spec/frontend/packages_and_registries/settings/project/settings/components/container_expiration_policy_spec.js64
-rw-r--r--spec/frontend/packages_and_registries/settings/project/settings/components/expiration_dropdown_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/settings/project/settings/components/expiration_input_spec.js6
-rw-r--r--spec/frontend/packages_and_registries/settings/project/settings/components/expiration_run_text_spec.js4
-rw-r--r--spec/frontend/packages_and_registries/settings/project/settings/components/expiration_toggle_spec.js2
-rw-r--r--spec/frontend/packages_and_registries/settings/project/settings/components/packages_cleanup_policy_form_spec.js2
-rw-r--r--spec/frontend/packages_and_registries/settings/project/settings/components/registry_settings_app_spec.js96
-rw-r--r--spec/frontend/packages_and_registries/shared/components/cli_commands_spec.js85
-rw-r--r--spec/frontend/packages_and_registries/shared/components/package_icon_and_name_spec.js2
-rw-r--r--spec/frontend/pages/admin/application_settings/metrics_and_profiling/usage_statistics_spec.js2
-rw-r--r--spec/frontend/pages/import/bitbucket_server/components/bitbucket_server_status_table_spec.js4
-rw-r--r--spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js8
-rw-r--r--spec/frontend/pages/import/history/components/import_error_details_spec.js6
-rw-r--r--spec/frontend/pages/import/history/components/import_history_app_spec.js10
-rw-r--r--spec/frontend/pages/profiles/show/emoji_menu_spec.js115
-rw-r--r--spec/frontend/pages/projects/forks/new/components/fork_form_spec.js173
-rw-r--r--spec/frontend/pages/projects/forks/new/components/project_namespace_spec.js177
-rw-r--r--spec/frontend/pages/projects/graphs/code_coverage_spec.js8
-rw-r--r--spec/frontend/pages/projects/merge_requests/edit/update_form_spec.js59
-rw-r--r--spec/frontend/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js2
-rw-r--r--spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js223
-rw-r--r--spec/frontend/pages/shared/wikis/components/wiki_content_spec.js2
-rw-r--r--spec/frontend/pages/shared/wikis/components/wiki_form_spec.js50
-rw-r--r--spec/frontend/performance_bar/components/add_request_spec.js2
-rw-r--r--spec/frontend/performance_bar/components/detailed_metric_spec.js2
-rw-r--r--spec/frontend/persistent_user_callout_spec.js2
-rw-r--r--spec/frontend/pipeline_editor/components/commit/commit_form_spec.js2
-rw-r--r--spec/frontend/pipeline_editor/components/commit/commit_section_spec.js2
-rw-r--r--spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js2
-rw-r--r--spec/frontend/pipeline_editor/components/file-tree/container_spec.js2
-rw-r--r--spec/frontend/pipeline_editor/components/header/pipeline_editor_mini_graph_spec.js109
-rw-r--r--spec/frontend/pipeline_editor/components/header/pipline_editor_mini_graph_spec.js2
-rw-r--r--spec/frontend/pipeline_editor/components/lint/ci_lint_results_spec.js4
-rw-r--r--spec/frontend/pipeline_editor/components/lint/ci_lint_warnings_spec.js4
-rw-r--r--spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js2
-rw-r--r--spec/frontend/pipeline_editor/components/popovers/walkthrough_popover_spec.js2
-rw-r--r--spec/frontend/pipeline_editor/components/ui/editor_tab_spec.js2
-rw-r--r--spec/frontend/pipeline_editor/pipeline_editor_app_spec.js3
-rw-r--r--spec/frontend/pipeline_editor/pipeline_editor_home_spec.js2
-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.js12
-rw-r--r--spec/frontend/pipeline_new/components/refs_dropdown_spec.js4
-rw-r--r--spec/frontend/pipeline_wizard/components/commit_spec.js2
-rw-r--r--spec/frontend/pipeline_wizard/components/editor_spec.js11
-rw-r--r--spec/frontend/pipeline_wizard/components/input_wrapper_spec.js2
-rw-r--r--spec/frontend/pipeline_wizard/components/wrapper_spec.js125
-rw-r--r--spec/frontend/pipeline_wizard/mock/yaml.js2
-rw-r--r--spec/frontend/pipeline_wizard/pipeline_wizard_spec.js1
-rw-r--r--spec/frontend/pipelines/components/dag/dag_annotations_spec.js2
-rw-r--r--spec/frontend/pipelines/components/dag/dag_spec.js10
-rw-r--r--spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mini_list_spec.js176
-rw-r--r--spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mock_data.js407
-rw-r--r--spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_mini_graph_spec.js149
-rw-r--r--spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stage_spec.js260
-rw-r--r--spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stages_spec.js83
-rw-r--r--spec/frontend/pipelines/components/pipelines_filtered_search_spec.js20
-rw-r--r--spec/frontend/pipelines/components/pipelines_list/pipeline_mini_graph_spec.js83
-rw-r--r--spec/frontend/pipelines/components/pipelines_list/pipeline_stage_spec.js259
-rw-r--r--spec/frontend/pipelines/graph/action_component_spec.js2
-rw-r--r--spec/frontend/pipelines/graph/graph_component_spec.js8
-rw-r--r--spec/frontend/pipelines/graph/graph_component_wrapper_spec.js24
-rw-r--r--spec/frontend/pipelines/graph/graph_view_selector_spec.js61
-rw-r--r--spec/frontend/pipelines/graph/job_item_spec.js6
-rw-r--r--spec/frontend/pipelines/graph/job_name_component_spec.js2
-rw-r--r--spec/frontend/pipelines/graph/linked_pipeline_spec.js16
-rw-r--r--spec/frontend/pipelines/graph/linked_pipelines_column_spec.js4
-rw-r--r--spec/frontend/pipelines/graph/mock_data.js242
-rw-r--r--spec/frontend/pipelines/graph/stage_column_component_spec.js10
-rw-r--r--spec/frontend/pipelines/graph_shared/links_layer_spec.js2
-rw-r--r--spec/frontend/pipelines/header_component_spec.js6
-rw-r--r--spec/frontend/pipelines/performance_insights_modal_spec.js131
-rw-r--r--spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js2
-rw-r--r--spec/frontend/pipelines/pipeline_multi_actions_spec.js20
-rw-r--r--spec/frontend/pipelines/pipeline_url_spec.js71
-rw-r--r--spec/frontend/pipelines/pipelines_actions_spec.js24
-rw-r--r--spec/frontend/pipelines/pipelines_artifacts_spec.js5
-rw-r--r--spec/frontend/pipelines/pipelines_spec.js18
-rw-r--r--spec/frontend/pipelines/pipelines_table_spec.js72
-rw-r--r--spec/frontend/pipelines/test_reports/test_suite_table_spec.js8
-rw-r--r--spec/frontend/pipelines/test_reports/test_summary_table_spec.js2
-rw-r--r--spec/frontend/pipelines/time_ago_spec.js4
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js7
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_source_token_spec.js5
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_status_token_spec.js7
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_tag_name_token_spec.js7
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js7
-rw-r--r--spec/frontend/pipelines/utils_spec.js44
-rw-r--r--spec/frontend/popovers/components/popovers_spec.js10
-rw-r--r--spec/frontend/profile/account/components/delete_account_modal_spec.js2
-rw-r--r--spec/frontend/profile/account/components/update_username_spec.js6
-rw-r--r--spec/frontend/profile/preferences/components/integration_view_spec.js2
-rw-r--r--spec/frontend/profile/preferences/components/profile_preferences_spec.js4
-rw-r--r--spec/frontend/projects/commit/components/form_modal_spec.js8
-rw-r--r--spec/frontend/projects/commit/store/mutations_spec.js2
-rw-r--r--spec/frontend/projects/commits/components/author_select_spec.js10
-rw-r--r--spec/frontend/projects/compare/components/app_spec.js4
-rw-r--r--spec/frontend/projects/compare/components/repo_dropdown_spec.js4
-rw-r--r--spec/frontend/projects/compare/components/revision_card_spec.js4
-rw-r--r--spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js4
-rw-r--r--spec/frontend/projects/compare/components/revision_dropdown_spec.js6
-rw-r--r--spec/frontend/projects/components/project_delete_button_spec.js2
-rw-r--r--spec/frontend/projects/components/shared/delete_button_spec.js2
-rw-r--r--spec/frontend/projects/details/upload_button_spec.js6
-rw-r--r--spec/frontend/projects/pipelines/charts/components/app_spec.js17
-rw-r--r--spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js4
-rw-r--r--spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js10
-rw-r--r--spec/frontend/projects/settings/components/new_access_dropdown_spec.js8
-rw-r--r--spec/frontend/projects/settings/components/shared_runners_toggle_spec.js6
-rw-r--r--spec/frontend/projects/settings/repository/branch_rules/app_spec.js49
-rw-r--r--spec/frontend/projects/settings/repository/branch_rules/components/branch_rule_spec.js58
-rw-r--r--spec/frontend/projects/settings/repository/branch_rules/mock_data.js25
-rw-r--r--spec/frontend/projects/settings_service_desk/components/service_desk_root_spec.js40
-rw-r--r--spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js37
-rw-r--r--spec/frontend/projects/settings_service_desk/components/service_desk_template_dropdown_spec.js6
-rw-r--r--spec/frontend/ref/components/ref_selector_spec.js17
-rw-r--r--spec/frontend/related_issues/components/related_issuable_input_spec.js8
-rw-r--r--spec/frontend/releases/components/app_edit_new_spec.js4
-rw-r--r--spec/frontend/releases/components/app_show_spec.js4
-rw-r--r--spec/frontend/releases/components/asset_links_form_spec.js14
-rw-r--r--spec/frontend/releases/components/evidence_block_spec.js14
-rw-r--r--spec/frontend/releases/components/issuable_stats_spec.js8
-rw-r--r--spec/frontend/releases/components/release_block_assets_spec.js2
-rw-r--r--spec/frontend/releases/components/release_block_footer_spec.js12
-rw-r--r--spec/frontend/releases/components/release_block_header_spec.js2
-rw-r--r--spec/frontend/releases/components/release_block_milestone_info_spec.js8
-rw-r--r--spec/frontend/releases/components/release_block_spec.js6
-rw-r--r--spec/frontend/releases/components/release_skeleton_loader_spec.js2
-rw-r--r--spec/frontend/releases/components/tag_field_exsting_spec.js2
-rw-r--r--spec/frontend/releases/components/tag_field_new_spec.js6
-rw-r--r--spec/frontend/releases/components/tag_field_spec.js4
-rw-r--r--spec/frontend/releases/stores/modules/detail/actions_spec.js26
-rw-r--r--spec/frontend/releases/stores/modules/detail/getters_spec.js19
-rw-r--r--spec/frontend/releases/stores/modules/detail/mutations_spec.js7
-rw-r--r--spec/frontend/reports/accessibility_report/components/accessibility_issue_body_spec.js8
-rw-r--r--spec/frontend/reports/accessibility_report/grouped_accessibility_reports_app_spec.js2
-rw-r--r--spec/frontend/reports/codequality_report/components/codequality_issue_body_spec.js2
-rw-r--r--spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js2
-rw-r--r--spec/frontend/reports/components/grouped_issues_list_spec.js8
-rw-r--r--spec/frontend/reports/components/report_item_spec.js4
-rw-r--r--spec/frontend/reports/grouped_test_report/components/modal_spec.js4
-rw-r--r--spec/frontend/reports/grouped_test_report/store/actions_spec.js4
-rw-r--r--spec/frontend/reports/mock_data/new_failures_with_null_files_report.json40
-rw-r--r--spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap3
-rw-r--r--spec/frontend/repository/components/blob_button_group_spec.js10
-rw-r--r--spec/frontend/repository/components/blob_content_viewer_spec.js49
-rw-r--r--spec/frontend/repository/components/blob_viewers/csv_viewer_spec.js2
-rw-r--r--spec/frontend/repository/components/breadcrumbs_spec.js20
-rw-r--r--spec/frontend/repository/components/delete_blob_modal_spec.js2
-rw-r--r--spec/frontend/repository/components/last_commit_spec.js9
-rw-r--r--spec/frontend/repository/components/new_directory_modal_spec.js2
-rw-r--r--spec/frontend/repository/components/preview/index_spec.js2
-rw-r--r--spec/frontend/repository/components/table/index_spec.js39
-rw-r--r--spec/frontend/repository/components/upload_blob_modal_spec.js2
-rw-r--r--spec/frontend/repository/log_tree_spec.js8
-rw-r--r--spec/frontend/repository/mock_data.js1
-rw-r--r--spec/frontend/repository/utils/commit_spec.js2
-rw-r--r--spec/frontend/runner/admin_runner_edit/admin_runner_edit_app_spec.js113
-rw-r--r--spec/frontend/runner/admin_runner_show/admin_runner_show_app_spec.js2
-rw-r--r--spec/frontend/runner/admin_runners/admin_runners_app_spec.js30
-rw-r--r--spec/frontend/runner/components/cells/runner_stacked_summary_cell_spec.js164
-rw-r--r--spec/frontend/runner/components/cells/runner_status_cell_spec.js21
-rw-r--r--spec/frontend/runner/components/cells/runner_summary_cell_spec.js91
-rw-r--r--spec/frontend/runner/components/cells/runner_summary_field_spec.js49
-rw-r--r--spec/frontend/runner/components/runner_details_spec.js30
-rw-r--r--spec/frontend/runner/components/runner_header_spec.js6
-rw-r--r--spec/frontend/runner/components/runner_list_spec.js75
-rw-r--r--spec/frontend/runner/components/runner_paused_badge_spec.js5
-rw-r--r--spec/frontend/runner/components/runner_projects_spec.js65
-rw-r--r--spec/frontend/runner/components/runner_stacked_layout_banner_spec.js39
-rw-r--r--spec/frontend/runner/components/runner_status_badge_spec.js20
-rw-r--r--spec/frontend/runner/components/runner_tag_spec.js4
-rw-r--r--spec/frontend/runner/components/runner_tags_spec.js4
-rw-r--r--spec/frontend/runner/components/runner_type_badge_spec.js17
-rw-r--r--spec/frontend/runner/components/runner_type_tabs_spec.js2
-rw-r--r--spec/frontend/runner/components/runner_update_form_spec.js7
-rw-r--r--spec/frontend/runner/components/stat/runner_stats_spec.js33
-rw-r--r--spec/frontend/runner/group_runners/group_runners_app_spec.js55
-rw-r--r--spec/frontend/runner/runner_edit/runner_edit_app_spec.js114
-rw-r--r--spec/frontend/runner/utils_spec.js13
-rw-r--r--spec/frontend/search/sidebar/components/radio_filter_spec.js2
-rw-r--r--spec/frontend/search/sort/components/app_spec.js2
-rw-r--r--spec/frontend/set_status_modal/set_status_form_spec.js167
-rw-r--r--spec/frontend/set_status_modal/set_status_modal_wrapper_spec.js45
-rw-r--r--spec/frontend/set_status_modal/user_profile_set_status_wrapper_spec.js156
-rw-r--r--spec/frontend/set_status_modal/utils_spec.js3
-rw-r--r--spec/frontend/sidebar/assignee_title_spec.js4
-rw-r--r--spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js2
-rw-r--r--spec/frontend/sidebar/components/assignees/collapsed_assignee_list_spec.js2
-rw-r--r--spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js12
-rw-r--r--spec/frontend/sidebar/components/assignees/uncollapsed_assignee_list_spec.js8
-rw-r--r--spec/frontend/sidebar/components/assignees/user_name_with_status_spec.js2
-rw-r--r--spec/frontend/sidebar/components/confidential/sidebar_confidentiality_form_spec.js25
-rw-r--r--spec/frontend/sidebar/components/confidential/sidebar_confidentiality_widget_spec.js5
-rw-r--r--spec/frontend/sidebar/components/date/sidebar_inherit_date_spec.js12
-rw-r--r--spec/frontend/sidebar/components/reviewers/uncollapsed_reviewer_list_spec.js4
-rw-r--r--spec/frontend/sidebar/components/severity/sidebar_severity_spec.js2
-rw-r--r--spec/frontend/sidebar/components/sidebar_dropdown_widget_spec.js18
-rw-r--r--spec/frontend/sidebar/components/subscriptions/sidebar_subscriptions_widget_spec.js2
-rw-r--r--spec/frontend/sidebar/components/time_tracking/report_spec.js2
-rw-r--r--spec/frontend/sidebar/issuable_assignees_spec.js23
-rw-r--r--spec/frontend/sidebar/lock/issuable_lock_form_spec.js2
-rw-r--r--spec/frontend/sidebar/mock_data.js10
-rw-r--r--spec/frontend/sidebar/sidebar_mediator_spec.js2
-rw-r--r--spec/frontend/sidebar/sidebar_move_issue_spec.js11
-rw-r--r--spec/frontend/sidebar/todo_spec.js2
-rw-r--r--spec/frontend/snippets/components/edit_spec.js38
-rw-r--r--spec/frontend/snippets/components/show_spec.js22
-rw-r--r--spec/frontend/snippets/components/snippet_blob_actions_edit_spec.js2
-rw-r--r--spec/frontend/snippets/components/snippet_blob_view_spec.js4
-rw-r--r--spec/frontend/snippets/components/snippet_visibility_edit_spec.js20
-rw-r--r--spec/frontend/surveys/merge_request_performance/app_spec.js74
-rw-r--r--spec/frontend/terraform/components/states_table_spec.js2
-rw-r--r--spec/frontend/token_access/mock_data.js13
-rw-r--r--spec/frontend/token_access/token_access_spec.js28
-rw-r--r--spec/frontend/tooltips/components/tooltips_spec.js4
-rw-r--r--spec/frontend/user_lists/store/index/actions_spec.js4
-rw-r--r--spec/frontend/vue_merge_request_widget/components/added_commit_message_spec.js13
-rw-r--r--spec/frontend/vue_merge_request_widget/components/artifacts_list_spec.js6
-rw-r--r--spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_spec.js28
-rw-r--r--spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js75
-rw-r--r--spec/frontend/vue_merge_request_widget/components/mr_widget_status_icon_spec.js40
-rw-r--r--spec/frontend/vue_merge_request_widget/components/mr_widget_suggest_pipeline_spec.js2
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_auto_merge_enabled_spec.js.snap468
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_pipeline_failed_spec.js.snap24
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_archived_spec.js21
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_checking_spec.js22
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_closed_spec.js65
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_commit_message_dropdown_spec.js2
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_failed_to_merge_spec.js15
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_not_allowed_spec.js20
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked_spec.js19
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_failed_spec.js17
-rw-r--r--spec/frontend/vue_merge_request_widget/components/states/mr_widget_ready_to_merge_spec.js4
-rw-r--r--spec/frontend/vue_merge_request_widget/components/terraform/mr_widget_terraform_container_spec.js3
-rw-r--r--spec/frontend/vue_merge_request_widget/components/widget/app_spec.js4
-rw-r--r--spec/frontend/vue_merge_request_widget/components/widget/widget_content_section_spec.js39
-rw-r--r--spec/frontend/vue_merge_request_widget/components/widget/widget_spec.js174
-rw-r--r--spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js2
-rw-r--r--spec/frontend/vue_merge_request_widget/extensions/test_report/index_spec.js10
-rw-r--r--spec/frontend/vue_merge_request_widget/mr_widget_options_spec.js4
-rw-r--r--spec/frontend/vue_merge_request_widget/stores/artifacts_list/actions_spec.js4
-rw-r--r--spec/frontend/vue_shared/alert_details/alert_details_spec.js2
-rw-r--r--spec/frontend/vue_shared/alert_details/alert_metrics_spec.js2
-rw-r--r--spec/frontend/vue_shared/components/__snapshots__/code_block_spec.js.snap26
-rw-r--r--spec/frontend/vue_shared/components/ci_badge_link_spec.js22
-rw-r--r--spec/frontend/vue_shared/components/code_block_highlighted_spec.js65
-rw-r--r--spec/frontend/vue_shared/components/code_block_spec.js82
-rw-r--r--spec/frontend/vue_shared/components/diff_stats_dropdown_spec.js4
-rw-r--r--spec/frontend/vue_shared/components/gl_modal_vuex_spec.js4
-rw-r--r--spec/frontend/vue_shared/components/paginated_list_spec.js2
-rw-r--r--spec/frontend/vue_shared/components/registry/registry_search_spec.js2
-rw-r--r--spec/frontend/vue_shared/components/sidebar/labels_select_widget/mock_data.js2
-rw-r--r--spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js9
-rw-r--r--spec/frontend/vue_shared/components/upload_dropzone/__snapshots__/upload_dropzone_spec.js.snap14
-rw-r--r--spec/frontend/vue_shared/components/user_callout_dismisser_spec.js24
-rw-r--r--spec/frontend/vue_shared/components/user_popover/user_popover_spec.js41
-rw-r--r--spec/frontend/vue_shared/issuable/show/components/issuable_edit_form_spec.js6
-rw-r--r--spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js4
-rw-r--r--spec/frontend/work_items/components/item_title_spec.js2
-rw-r--r--spec/frontend/work_items/components/work_item_actions_spec.js48
-rw-r--r--spec/frontend/work_items/components/work_item_assignees_spec.js79
-rw-r--r--spec/frontend/work_items/components/work_item_description_spec.js14
-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.js522
-rw-r--r--spec/frontend/work_items/components/work_item_due_date_spec.js346
-rw-r--r--spec/frontend/work_items/components/work_item_information_spec.js9
-rw-r--r--spec/frontend/work_items/components/work_item_labels_spec.js6
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js122
-rw-r--r--spec/frontend/work_items/components/work_item_links/work_item_links_spec.js98
-rw-r--r--spec/frontend/work_items/components/work_item_state_spec.js5
-rw-r--r--spec/frontend/work_items/components/work_item_title_spec.js6
-rw-r--r--spec/frontend/work_items/components/work_item_type_icon_spec.js39
-rw-r--r--spec/frontend/work_items/components/work_item_weight_spec.js214
-rw-r--r--spec/frontend/work_items/mock_data.js258
-rw-r--r--spec/frontend/work_items/pages/create_work_item_spec.js4
-rw-r--r--spec/frontend/work_items/pages/work_item_detail_spec.js484
-rw-r--r--spec/frontend/work_items/router_spec.js39
-rw-r--r--spec/frontend/work_items_hierarchy/components/hierarchy_spec.js2
-rw-r--r--spec/frontend_integration/content_editor/content_editor_integration_spec.js111
-rw-r--r--spec/frontend_integration/ide/helpers/start.js4
-rw-r--r--spec/graphql/features/feature_flag_spec.rb52
-rw-r--r--spec/graphql/mutations/boards/issues/issue_move_list_spec.rb46
-rw-r--r--spec/graphql/mutations/ci/runner/update_spec.rb170
-rw-r--r--spec/graphql/mutations/commits/create_spec.rb289
-rw-r--r--spec/graphql/mutations/environments/canary_ingress/update_spec.rb11
-rw-r--r--spec/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb4
-rw-r--r--spec/graphql/mutations/merge_requests/create_spec.rb119
-rw-r--r--spec/graphql/mutations/releases/create_spec.rb2
-rw-r--r--spec/graphql/mutations/releases/update_spec.rb4
-rw-r--r--spec/graphql/resolvers/board_lists_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/ci/config_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/ci/group_runners_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/ci/job_token_scope_resolver_spec.rb8
-rw-r--r--spec/graphql/resolvers/ci/jobs_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/ci/runner_projects_resolver_spec.rb69
-rw-r--r--spec/graphql/resolvers/ci/runners_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/ci/test_suite_resolver_spec.rb11
-rw-r--r--spec/graphql/resolvers/container_repositories_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/container_repository_tags_resolver_spec.rb2
-rw-r--r--spec/graphql/resolvers/crm/organization_state_counts_resolver_spec.rb61
-rw-r--r--spec/graphql/resolvers/crm/organizations_resolver_spec.rb14
-rw-r--r--spec/graphql/resolvers/deployment_resolver_spec.rb28
-rw-r--r--spec/graphql/resolvers/deployments_resolver_spec.rb41
-rw-r--r--spec/graphql/resolvers/design_management/versions_resolver_spec.rb4
-rw-r--r--spec/graphql/resolvers/environments/last_deployment_resolver_spec.rb40
-rw-r--r--spec/graphql/resolvers/group_members_resolver_spec.rb4
-rw-r--r--spec/graphql/resolvers/issues_resolver_spec.rb52
-rw-r--r--spec/graphql/resolvers/project_members_resolver_spec.rb4
-rw-r--r--spec/graphql/resolvers/work_items_resolver_spec.rb56
-rw-r--r--spec/graphql/types/base_field_spec.rb99
-rw-r--r--spec/graphql/types/branch_protections/merge_access_level_type_spec.rb13
-rw-r--r--spec/graphql/types/branch_protections/push_access_level_type_spec.rb13
-rw-r--r--spec/graphql/types/branch_rule_type_spec.rb22
-rw-r--r--spec/graphql/types/branch_rules/branch_protection_type_spec.rb13
-rw-r--r--spec/graphql/types/ci/config_variable_type_spec.rb7
-rw-r--r--spec/graphql/types/ci/group_variable_connection_type_spec.rb11
-rw-r--r--spec/graphql/types/ci/instance_variable_type_spec.rb2
-rw-r--r--spec/graphql/types/ci/job_artifact_type_spec.rb2
-rw-r--r--spec/graphql/types/ci/job_token_scope_type_spec.rb4
-rw-r--r--spec/graphql/types/ci/job_type_spec.rb15
-rw-r--r--spec/graphql/types/ci/manual_variable_type_spec.rb2
-rw-r--r--spec/graphql/types/ci/project_variable_connection_type_spec.rb11
-rw-r--r--spec/graphql/types/ci/runner_architecture_type_spec.rb4
-rw-r--r--spec/graphql/types/ci/runner_platform_type_spec.rb6
-rw-r--r--spec/graphql/types/ci/variable_interface_spec.rb2
-rw-r--r--spec/graphql/types/clusters/agent_type_spec.rb2
-rw-r--r--spec/graphql/types/customer_relations/organization_sort_enum_spec.rb22
-rw-r--r--spec/graphql/types/customer_relations/organization_state_counts_type_spec.rb31
-rw-r--r--spec/graphql/types/deployment_details_type_spec.rb17
-rw-r--r--spec/graphql/types/deployment_type_spec.rb17
-rw-r--r--spec/graphql/types/detployment_tag_type_spec.rb15
-rw-r--r--spec/graphql/types/environment_type_spec.rb10
-rw-r--r--spec/graphql/types/group_type_spec.rb12
-rw-r--r--spec/graphql/types/merge_request_review_state_enum_spec.rb4
-rw-r--r--spec/graphql/types/metrics/dashboard_type_spec.rb4
-rw-r--r--spec/graphql/types/packages/composer/metadatum_type_spec.rb2
-rw-r--r--spec/graphql/types/packages/package_type_enum_spec.rb2
-rw-r--r--spec/graphql/types/project_type_spec.rb67
-rw-r--r--spec/graphql/types/subscription_type_spec.rb3
-rw-r--r--spec/graphql/types/timelog_type_spec.rb2
-rw-r--r--spec/graphql/types/user_merge_request_interaction_type_spec.rb3
-rw-r--r--spec/graphql/types/work_item_type_spec.rb4
-rw-r--r--spec/graphql/types/work_items/widgets/description_type_spec.rb2
-rw-r--r--spec/helpers/application_settings_helper_spec.rb70
-rw-r--r--spec/helpers/blob_helper_spec.rb2
-rw-r--r--spec/helpers/ci/builds_helper_spec.rb4
-rw-r--r--spec/helpers/ci/runners_helper_spec.rb37
-rw-r--r--spec/helpers/commits_helper_spec.rb2
-rw-r--r--spec/helpers/gitlab_script_tag_helper_spec.rb4
-rw-r--r--spec/helpers/groups_helper_spec.rb23
-rw-r--r--spec/helpers/issuables_helper_spec.rb4
-rw-r--r--spec/helpers/jira_connect_helper_spec.rb32
-rw-r--r--spec/helpers/learn_gitlab_helper_spec.rb14
-rw-r--r--spec/helpers/members_helper_spec.rb8
-rw-r--r--spec/helpers/nav/new_dropdown_helper_spec.rb2
-rw-r--r--spec/helpers/nav/top_nav_helper_spec.rb240
-rw-r--r--spec/helpers/notify_helper_spec.rb27
-rw-r--r--spec/helpers/page_layout_helper_spec.rb16
-rw-r--r--spec/helpers/profiles_helper_spec.rb2
-rw-r--r--spec/helpers/projects/google_cloud/cloudsql_helper_spec.rb25
-rw-r--r--spec/helpers/projects/pages_helper_spec.rb68
-rw-r--r--spec/helpers/projects/pipeline_helper_spec.rb8
-rw-r--r--spec/helpers/projects_helper_spec.rb36
-rw-r--r--spec/helpers/routing/pseudonymization_helper_spec.rb4
-rw-r--r--spec/helpers/search_helper_spec.rb2
-rw-r--r--spec/helpers/sorting_helper_spec.rb48
-rw-r--r--spec/helpers/storage_helper_spec.rb159
-rw-r--r--spec/helpers/tab_helper_spec.rb2
-rw-r--r--spec/helpers/todos_helper_spec.rb15
-rw-r--r--spec/helpers/users/callouts_helper_spec.rb4
-rw-r--r--spec/helpers/users_helper_spec.rb32
-rw-r--r--spec/helpers/wiki_helper_spec.rb8
-rw-r--r--spec/helpers/wiki_page_version_helper_spec.rb4
-rw-r--r--spec/initializers/00_rails_disable_joins_spec.rb6
-rw-r--r--spec/initializers/action_cable_subscription_adapter_identifier_spec.rb2
-rw-r--r--spec/initializers/carrierwave_patch_spec.rb6
-rw-r--r--spec/initializers/load_balancing_spec.rb100
-rw-r--r--spec/initializers/microsoft_graph_mailer_spec.rb56
-rw-r--r--spec/initializers/net_http_patch_spec.rb1
-rw-r--r--spec/initializers/settings_spec.rb36
-rw-r--r--spec/initializers/trusted_proxies_spec.rb2
-rw-r--r--spec/lib/api/entities/ci/job_request/image_spec.rb10
-rw-r--r--spec/lib/api/entities/ci/job_request/service_spec.rb8
-rw-r--r--spec/lib/api/entities/ml/mlflow/run_info_spec.rb65
-rw-r--r--spec/lib/api/entities/ml/mlflow/run_spec.rb21
-rw-r--r--spec/lib/api/entities/personal_access_token_with_details_spec.rb29
-rw-r--r--spec/lib/api/helpers/caching_spec.rb9
-rw-r--r--spec/lib/api/helpers/packages/dependency_proxy_helpers_spec.rb50
-rw-r--r--spec/lib/api/helpers/packages_helpers_spec.rb44
-rw-r--r--spec/lib/api/helpers/pagination_spec.rb2
-rw-r--r--spec/lib/api/helpers_spec.rb89
-rw-r--r--spec/lib/api/integrations/slack/events/url_verification_spec.rb11
-rw-r--r--spec/lib/backup/database_backup_error_spec.rb2
-rw-r--r--spec/lib/backup/gitaly_backup_spec.rb4
-rw-r--r--spec/lib/backup/task_spec.rb2
-rw-r--r--spec/lib/banzai/color_parser_spec.rb2
-rw-r--r--spec/lib/banzai/filter/blockquote_fence_filter_spec.rb4
-rw-r--r--spec/lib/banzai/filter/kroki_filter_spec.rb8
-rw-r--r--spec/lib/banzai/filter/math_filter_spec.rb298
-rw-r--r--spec/lib/banzai/filter/output_safety_spec.rb2
-rw-r--r--spec/lib/banzai/filter/plantuml_filter_spec.rb10
-rw-r--r--spec/lib/banzai/filter/repository_link_filter_spec.rb14
-rw-r--r--spec/lib/banzai/filter_array_spec.rb2
-rw-r--r--spec/lib/banzai/pipeline/pre_process_pipeline_spec.rb17
-rw-r--r--spec/lib/banzai/pipeline_spec.rb2
-rw-r--r--spec/lib/banzai/querying_spec.rb2
-rw-r--r--spec/lib/banzai/renderer_spec.rb2
-rw-r--r--spec/lib/bitbucket/collection_spec.rb2
-rw-r--r--spec/lib/bitbucket/page_spec.rb2
-rw-r--r--spec/lib/bitbucket/paginator_spec.rb2
-rw-r--r--spec/lib/bitbucket/representation/comment_spec.rb2
-rw-r--r--spec/lib/bitbucket/representation/issue_spec.rb2
-rw-r--r--spec/lib/bitbucket/representation/pull_request_comment_spec.rb2
-rw-r--r--spec/lib/bitbucket/representation/pull_request_spec.rb2
-rw-r--r--spec/lib/bitbucket/representation/repo_spec.rb2
-rw-r--r--spec/lib/bitbucket/representation/user_spec.rb2
-rw-r--r--spec/lib/bitbucket_server/page_spec.rb2
-rw-r--r--spec/lib/bulk_imports/file_downloads/filename_fetch_spec.rb16
-rw-r--r--spec/lib/bulk_imports/file_downloads/validations_spec.rb28
-rw-r--r--spec/lib/bulk_imports/pipeline/extracted_data_spec.rb2
-rw-r--r--spec/lib/bulk_imports/pipeline_spec.rb2
-rw-r--r--spec/lib/bulk_imports/projects/pipelines/merge_requests_pipeline_spec.rb51
-rw-r--r--spec/lib/bulk_imports/retry_pipeline_error_spec.rb2
-rw-r--r--spec/lib/constraints/jira_encoded_url_constrainer_spec.rb2
-rw-r--r--spec/lib/container_registry/gitlab_api_client_spec.rb4
-rw-r--r--spec/lib/container_registry/tag_spec.rb25
-rw-r--r--spec/lib/declarative_enum_spec.rb2
-rw-r--r--spec/lib/error_tracking/sentry_client/event_spec.rb5
-rw-r--r--spec/lib/error_tracking/sentry_client/issue_link_spec.rb7
-rw-r--r--spec/lib/error_tracking/sentry_client/issue_spec.rb28
-rw-r--r--spec/lib/error_tracking/sentry_client/projects_spec.rb5
-rw-r--r--spec/lib/error_tracking/sentry_client/repo_spec.rb3
-rw-r--r--spec/lib/error_tracking/sentry_client_spec.rb2
-rw-r--r--spec/lib/gitlab/alert_management/payload/base_spec.rb22
-rw-r--r--spec/lib/gitlab/alert_management/payload/generic_spec.rb36
-rw-r--r--spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb2
-rw-r--r--spec/lib/gitlab/analytics/date_filler_spec.rb136
-rw-r--r--spec/lib/gitlab/application_rate_limiter/base_strategy_spec.rb2
-rw-r--r--spec/lib/gitlab/asciidoc/html5_converter_spec.rb2
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb6
-rw-r--r--spec/lib/gitlab/audit/auditor_spec.rb9
-rw-r--r--spec/lib/gitlab/audit/null_target_spec.rb2
-rw-r--r--spec/lib/gitlab/audit/type/definition_spec.rb219
-rw-r--r--spec/lib/gitlab/auth/ldap/auth_hash_spec.rb14
-rw-r--r--spec/lib/gitlab/auth/ldap/config_spec.rb202
-rw-r--r--spec/lib/gitlab/auth/ldap/person_spec.rb12
-rw-r--r--spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb10
-rw-r--r--spec/lib/gitlab/auth/o_auth/provider_spec.rb18
-rw-r--r--spec/lib/gitlab/auth/otp/strategies/forti_token_cloud_spec.rb8
-rw-r--r--spec/lib/gitlab/background_migration/backfill_cluster_agents_has_vulnerabilities_spec.rb107
-rw-r--r--spec/lib/gitlab/background_migration/backfill_imported_issue_search_data_spec.rb8
-rw-r--r--spec/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification_spec.rb30
-rw-r--r--spec/lib/gitlab/background_migration/backfill_project_namespace_on_issues_spec.rb57
-rw-r--r--spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb42
-rw-r--r--spec/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent_spec.rb32
-rw-r--r--spec/lib/gitlab/background_migration/backfill_work_item_type_id_for_issues_spec.rb24
-rw-r--r--spec/lib/gitlab/background_migration/batching_strategies/backfill_issue_work_item_type_batching_strategy_spec.rb135
-rw-r--r--spec/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy_spec.rb133
-rw-r--r--spec/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy_spec.rb112
-rw-r--r--spec/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy_spec.rb23
-rw-r--r--spec/lib/gitlab/background_migration/batching_strategies/remove_backfilled_job_artifacts_expire_at_batching_strategy_spec.rb7
-rw-r--r--spec/lib/gitlab/background_migration/destroy_invalid_group_members_spec.rb89
-rw-r--r--spec/lib/gitlab/background_migration/destroy_invalid_project_members_spec.rb102
-rw-r--r--spec/lib/gitlab/background_migration/disable_legacy_open_source_licence_for_recent_public_projects_spec.rb114
-rw-r--r--spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_one_mb_spec.rb59
-rw-r--r--spec/lib/gitlab/background_migration/encrypt_integration_properties_spec.rb4
-rw-r--r--spec/lib/gitlab/background_migration/remove_backfilled_job_artifacts_expire_at_spec.rb92
-rw-r--r--spec/lib/gitlab/background_migration/remove_self_managed_wiki_notes_spec.rb31
-rw-r--r--spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb91
-rw-r--r--spec/lib/gitlab/background_migration/set_correct_vulnerability_state_spec.rb21
-rw-r--r--spec/lib/gitlab/bullet/exclusions_spec.rb1
-rw-r--r--spec/lib/gitlab/cache/helpers_spec.rb9
-rw-r--r--spec/lib/gitlab/changes_list_spec.rb2
-rw-r--r--spec/lib/gitlab/chat/responder/base_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/ansi2json/parser_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/ansi2json/result_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb86
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/path_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/build/policy/variables_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/build/policy_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/build/port_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb46
-rw-r--r--spec/lib/gitlab/ci/config/entry/environment_spec.rb10
-rw-r--r--spec/lib/gitlab/ci/config/entry/image_spec.rb27
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/config/entry/legacy_variables_spec.rb173
-rw-r--r--spec/lib/gitlab/ci/config/entry/port_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/processable_spec.rb32
-rw-r--r--spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/root_spec.rb149
-rw-r--r--spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/config/entry/service_spec.rb25
-rw-r--r--spec/lib/gitlab/ci/config/entry/variable_spec.rb212
-rw-r--r--spec/lib/gitlab/ci/config/entry/variables_spec.rb82
-rw-r--r--spec/lib/gitlab/ci/config/external/file/remote_spec.rb39
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/config/external/processor_spec.rb30
-rw-r--r--spec/lib/gitlab/ci/lint_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/mask_secret_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/parsers/security/common_spec.rb24
-rw-r--r--spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb273
-rw-r--r--spec/lib/gitlab/ci/parsers/test/junit_spec.rb8
-rw-r--r--spec/lib/gitlab/ci/parsers_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb47
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/command_spec.rb38
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb14
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb44
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb39
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb64
-rw-r--r--spec/lib/gitlab/ci/pipeline/duration_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/null_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/string_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/expression/lexeme/variable_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/metrics_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/build_spec.rb68
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb3
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb55
-rw-r--r--spec/lib/gitlab/ci/processable_object_hierarchy_spec.rb82
-rw-r--r--spec/lib/gitlab/ci/project_config/repository_spec.rb47
-rw-r--r--spec/lib/gitlab/ci/project_config/source_spec.rb23
-rw-r--r--spec/lib/gitlab/ci/project_config_spec.rb177
-rw-r--r--spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/coverage_report_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/component_spec.rb16
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/report_spec.rb26
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/reports_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/sbom/source_spec.rb16
-rw-r--r--spec/lib/gitlab/ci/reports/security/flag_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/security/link_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/security/scan_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/security/scanned_resource_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/terraform_reports_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/extended_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/group/factory_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/templates/katalon_gitlab_ci_yaml_spec.rb52
-rw-r--r--spec/lib/gitlab/ci/trace/archive_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/trace_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/variables/builder_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/variables/collection/sort_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/variables/helpers_spec.rb113
-rw-r--r--spec/lib/gitlab/ci/yaml_processor/dag_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/yaml_processor/feature_flags_spec.rb30
-rw-r--r--spec/lib/gitlab/ci/yaml_processor/result_spec.rb4
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb185
-rw-r--r--spec/lib/gitlab/ci_access_spec.rb2
-rw-r--r--spec/lib/gitlab/cleanup/personal_access_tokens_spec.rb168
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb22
-rw-r--r--spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb2
-rw-r--r--spec/lib/gitlab/config/entry/attributable_spec.rb2
-rw-r--r--spec/lib/gitlab/config/entry/composable_hash_spec.rb9
-rw-r--r--spec/lib/gitlab/config/entry/simplifiable_spec.rb2
-rw-r--r--spec/lib/gitlab/config/entry/undefined_spec.rb2
-rw-r--r--spec/lib/gitlab/config/entry/unspecified_spec.rb2
-rw-r--r--spec/lib/gitlab/container_repository/tags/cache_spec.rb12
-rw-r--r--spec/lib/gitlab/cross_project_access/check_collection_spec.rb2
-rw-r--r--spec/lib/gitlab/cross_project_access/check_info_spec.rb2
-rw-r--r--spec/lib/gitlab/cross_project_access/class_methods_spec.rb2
-rw-r--r--spec/lib/gitlab/cross_project_access_spec.rb2
-rw-r--r--spec/lib/gitlab/cycle_analytics/summary/value_spec.rb2
-rw-r--r--spec/lib/gitlab/data_builder/issuable_spec.rb2
-rw-r--r--spec/lib/gitlab/data_builder/note_spec.rb32
-rw-r--r--spec/lib/gitlab/database/background_migration/batched_migration_spec.rb68
-rw-r--r--spec/lib/gitlab/database/batch_average_counter_spec.rb107
-rw-r--r--spec/lib/gitlab/database/batch_count_spec.rb177
-rw-r--r--spec/lib/gitlab/database/lock_writes_manager_spec.rb84
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb2
-rw-r--r--spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb27
-rw-r--r--spec/lib/gitlab/database/partitioning/convert_table_to_first_list_partition_spec.rb246
-rw-r--r--spec/lib/gitlab/database/partitioning/partition_manager_spec.rb89
-rw-r--r--spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb15
-rw-r--r--spec/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table_spec.rb6
-rw-r--r--spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb27
-rw-r--r--spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb70
-rw-r--r--spec/lib/gitlab/database/partitioning_spec.rb73
-rw-r--r--spec/lib/gitlab/database/postgres_constraint_spec.rb123
-rw-r--r--spec/lib/gitlab/database/query_analyzers/ci/partitioning_analyzer_spec.rb78
-rw-r--r--spec/lib/gitlab/database/reindexing_spec.rb22
-rw-r--r--spec/lib/gitlab/database/tables_sorted_by_foreign_keys_spec.rb41
-rw-r--r--spec/lib/gitlab/database/tables_truncate_spec.rb257
-rw-r--r--spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb4
-rw-r--r--spec/lib/gitlab/database_spec.rb20
-rw-r--r--spec/lib/gitlab/dependency_linker/parser/gemfile_spec.rb2
-rw-r--r--spec/lib/gitlab/dependency_linker_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/file_collection_sorter_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/highlight_cache_spec.rb110
-rw-r--r--spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/inline_diff_marker_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/lines_unfolder_spec.rb16
-rw-r--r--spec/lib/gitlab/diff/position_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb3
-rw-r--r--spec/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512_spec.rb36
-rw-r--r--spec/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512_spec.rb45
-rw-r--r--spec/lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512_spec.rb36
-rw-r--r--spec/lib/gitlab/encoding_helper_spec.rb30
-rw-r--r--spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb8
-rw-r--r--spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb2
-rw-r--r--spec/lib/gitlab/etag_caching/middleware_spec.rb10
-rw-r--r--spec/lib/gitlab/etag_caching/router/graphql_spec.rb2
-rw-r--r--spec/lib/gitlab/experimentation/group_types_spec.rb2
-rw-r--r--spec/lib/gitlab/file_detector_spec.rb2
-rw-r--r--spec/lib/gitlab/file_markdown_link_builder_spec.rb2
-rw-r--r--spec/lib/gitlab/gfm/uploads_rewriter_spec.rb2
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb209
-rw-r--r--spec/lib/gitlab/git/branch_spec.rb2
-rw-r--r--spec/lib/gitlab/git/changed_path_spec.rb2
-rw-r--r--spec/lib/gitlab/git/changes_spec.rb2
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb70
-rw-r--r--spec/lib/gitlab/git/commit_stats_spec.rb16
-rw-r--r--spec/lib/gitlab/git/compare_spec.rb5
-rw-r--r--spec/lib/gitlab/git/conflict/file_spec.rb2
-rw-r--r--spec/lib/gitlab/git/conflict/parser_spec.rb2
-rw-r--r--spec/lib/gitlab/git/cross_repo_comparer_spec.rb2
-rw-r--r--spec/lib/gitlab/git/diff_collection_spec.rb2
-rw-r--r--spec/lib/gitlab/git/diff_spec.rb18
-rw-r--r--spec/lib/gitlab/git/gitmodules_parser_spec.rb2
-rw-r--r--spec/lib/gitlab/git/lfs_pointer_file_spec.rb2
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb613
-rw-r--r--spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb2
-rw-r--r--spec/lib/gitlab/git/tree_spec.rb2
-rw-r--r--spec/lib/gitlab/git/util_spec.rb2
-rw-r--r--spec/lib/gitlab/git/wiki_spec.rb63
-rw-r--r--spec/lib/gitlab/git_spec.rb12
-rw-r--r--spec/lib/gitlab/gitaly_client/commit_service_spec.rb19
-rw-r--r--spec/lib/gitlab/gitaly_client/diff_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/operation_service_spec.rb79
-rw-r--r--spec/lib/gitlab/gitaly_client/ref_service_spec.rb107
-rw-r--r--spec/lib/gitlab/gitaly_client/server_service_spec.rb42
-rw-r--r--spec/lib/gitlab/gitaly_client/util_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/attachments_downloader_spec.rb97
-rw-r--r--spec/lib/gitlab/github_import/client_spec.rb62
-rw-r--r--spec/lib/gitlab/github_import/importer/events/changed_assignee_spec.rb55
-rw-r--r--spec/lib/gitlab/github_import/importer/events/changed_label_spec.rb48
-rw-r--r--spec/lib/gitlab/github_import/importer/events/changed_milestone_spec.rb49
-rw-r--r--spec/lib/gitlab/github_import/importer/events/changed_reviewer_spec.rb101
-rw-r--r--spec/lib/gitlab/github_import/importer/events/closed_spec.rb80
-rw-r--r--spec/lib/gitlab/github_import/importer/events/cross_referenced_spec.rb95
-rw-r--r--spec/lib/gitlab/github_import/importer/events/renamed_spec.rb50
-rw-r--r--spec/lib/gitlab/github_import/importer/events/reopened_spec.rb63
-rw-r--r--spec/lib/gitlab/github_import/importer/issue_and_label_links_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/importer/issue_event_importer_spec.rb18
-rw-r--r--spec/lib/gitlab/github_import/importer/issue_events_importer_spec.rb4
-rw-r--r--spec/lib/gitlab/github_import/importer/protected_branch_importer_spec.rb91
-rw-r--r--spec/lib/gitlab/github_import/importer/protected_branches_importer_spec.rb225
-rw-r--r--spec/lib/gitlab/github_import/importer/release_attachments_importer_spec.rb57
-rw-r--r--spec/lib/gitlab/github_import/importer/releases_attachments_importer_spec.rb74
-rw-r--r--spec/lib/gitlab/github_import/importer/repository_importer_spec.rb8
-rw-r--r--spec/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer_spec.rb74
-rw-r--r--spec/lib/gitlab/github_import/markdown_text_spec.rb28
-rw-r--r--spec/lib/gitlab/github_import/parallel_scheduling_spec.rb4
-rw-r--r--spec/lib/gitlab/github_import/representation/diff_notes/suggestion_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/expose_attribute_spec.rb32
-rw-r--r--spec/lib/gitlab/github_import/representation/issue_event_spec.rb56
-rw-r--r--spec/lib/gitlab/github_import/representation/lfs_object_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/note_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/protected_branch_spec.rb51
-rw-r--r--spec/lib/gitlab/github_import/representation/pull_request_review_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/pull_request_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/release_attachments_spec.rb49
-rw-r--r--spec/lib/gitlab/github_import/representation/to_hash_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation/user_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/representation_spec.rb2
-rw-r--r--spec/lib/gitlab/github_import/user_finder_spec.rb12
-rw-r--r--spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb2
-rw-r--r--spec/lib/gitlab/graphql/limit/field_call_count_spec.rb66
-rw-r--r--spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb403
-rw-r--r--spec/lib/gitlab/graphql/pagination/keyset/last_items_spec.rb27
-rw-r--r--spec/lib/gitlab/health_checks/gitaly_check_spec.rb24
-rw-r--r--spec/lib/gitlab/health_checks/master_check_spec.rb2
-rw-r--r--spec/lib/gitlab/health_checks/probes/collection_spec.rb20
-rw-r--r--spec/lib/gitlab/health_checks/redis/cache_check_spec.rb8
-rw-r--r--spec/lib/gitlab/health_checks/redis/queues_check_spec.rb8
-rw-r--r--spec/lib/gitlab/health_checks/redis/rate_limiting_check_spec.rb8
-rw-r--r--spec/lib/gitlab/health_checks/redis/redis_check_spec.rb8
-rw-r--r--spec/lib/gitlab/health_checks/redis/sessions_check_spec.rb8
-rw-r--r--spec/lib/gitlab/health_checks/redis/shared_state_check_spec.rb8
-rw-r--r--spec/lib/gitlab/health_checks/redis/trace_chunks_check_spec.rb8
-rw-r--r--spec/lib/gitlab/health_checks/redis_spec.rb26
-rw-r--r--spec/lib/gitlab/i18n/metadata_entry_spec.rb2
-rw-r--r--spec/lib/gitlab/i18n/translation_entry_spec.rb2
-rw-r--r--spec/lib/gitlab/import/merge_request_creator_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml30
-rw-r--r--spec/lib/gitlab/import_export/attribute_cleaner_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/attributes_finder_spec.rb12
-rw-r--r--spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb25
-rw-r--r--spec/lib/gitlab/import_export/config_spec.rb6
-rw-r--r--spec/lib/gitlab/import_export/file_importer_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/group/object_builder_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb18
-rw-r--r--spec/lib/gitlab/import_export/json/legacy_writer_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb65
-rw-r--r--spec/lib/gitlab/import_export/legacy_relation_tree_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb18
-rw-r--r--spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb18
-rw-r--r--spec/lib/gitlab/import_export/project/tree_restorer_spec.rb16
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml29
-rw-r--r--spec/lib/gitlab/import_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/import_sources_spec.rb20
-rw-r--r--spec/lib/gitlab/incoming_email_spec.rb2
-rw-r--r--spec/lib/gitlab/insecure_key_fingerprint_spec.rb2
-rw-r--r--spec/lib/gitlab/instrumentation/redis_base_spec.rb19
-rw-r--r--spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb40
-rw-r--r--spec/lib/gitlab/instrumentation/redis_spec.rb12
-rw-r--r--spec/lib/gitlab/instrumentation_helper_spec.rb14
-rw-r--r--spec/lib/gitlab/internal_post_receive/response_spec.rb2
-rw-r--r--spec/lib/gitlab/jira/middleware_spec.rb4
-rw-r--r--spec/lib/gitlab/jira_import/metadata_collector_spec.rb2
-rw-r--r--spec/lib/gitlab/jira_import_spec.rb6
-rw-r--r--spec/lib/gitlab/kubernetes/helm/v2/certificate_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes/kubeconfig/entry/cluster_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes/kubeconfig/entry/context_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes/kubeconfig/entry/user_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes/kubeconfig/template_spec.rb2
-rw-r--r--spec/lib/gitlab/lazy_spec.rb2
-rw-r--r--spec/lib/gitlab/legacy_github_import/client_spec.rb24
-rw-r--r--spec/lib/gitlab/legacy_github_import/issuable_formatter_spec.rb2
-rw-r--r--spec/lib/gitlab/legacy_github_import/project_creator_spec.rb24
-rw-r--r--spec/lib/gitlab/loop_helpers_spec.rb2
-rw-r--r--spec/lib/gitlab/mailgun/webhook_processors/failure_logger_spec.rb47
-rw-r--r--spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb4
-rw-r--r--spec/lib/gitlab/markdown_cache/redis/extension_spec.rb8
-rw-r--r--spec/lib/gitlab/markup_helper_spec.rb2
-rw-r--r--spec/lib/gitlab/memory/jemalloc_spec.rb12
-rw-r--r--spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb23
-rw-r--r--spec/lib/gitlab/memory/watchdog_spec.rb318
-rw-r--r--spec/lib/gitlab/merge_requests/mergeability/results_store_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/dashboard/defaults_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb30
-rw-r--r--spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb38
-rw-r--r--spec/lib/gitlab/metrics/dashboard/validator_spec.rb24
-rw-r--r--spec/lib/gitlab/metrics/delta_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb22
-rw-r--r--spec/lib/gitlab/metrics/global_search_slis_spec.rb173
-rw-r--r--spec/lib/gitlab/metrics/rack_middleware_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb10
-rw-r--r--spec/lib/gitlab/metrics/subscribers/action_view_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/subscribers/active_record_spec.rb6
-rw-r--r--spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb4
-rw-r--r--spec/lib/gitlab/metrics/system_spec.rb37
-rw-r--r--spec/lib/gitlab/metrics/transaction_spec.rb2
-rw-r--r--spec/lib/gitlab/metrics/web_transaction_spec.rb4
-rw-r--r--spec/lib/gitlab/middleware/rack_multipart_tempfile_factory_spec.rb1
-rw-r--r--spec/lib/gitlab/middleware/release_env_spec.rb2
-rw-r--r--spec/lib/gitlab/middleware/sidekiq_web_static_spec.rb2
-rw-r--r--spec/lib/gitlab/namespaced_session_store_spec.rb2
-rw-r--r--spec/lib/gitlab/nav/top_nav_menu_header_spec.rb16
-rw-r--r--spec/lib/gitlab/nav/top_nav_menu_item_spec.rb4
-rw-r--r--spec/lib/gitlab/net_http_adapter_spec.rb2
-rw-r--r--spec/lib/gitlab/null_request_store_spec.rb2
-rw-r--r--spec/lib/gitlab/omniauth_initializer_spec.rb12
-rw-r--r--spec/lib/gitlab/pagination/keyset/column_order_definition_spec.rb29
-rw-r--r--spec/lib/gitlab/phabricator_import/representation/task_spec.rb2
-rw-r--r--spec/lib/gitlab/phabricator_import/representation/user_spec.rb2
-rw-r--r--spec/lib/gitlab/popen/runner_spec.rb2
-rw-r--r--spec/lib/gitlab/push_options_spec.rb2
-rw-r--r--spec/lib/gitlab/quick_actions/substitution_definition_spec.rb2
-rw-r--r--spec/lib/gitlab/quick_actions/timeline_text_and_date_time_separator_spec.rb94
-rw-r--r--spec/lib/gitlab/redis/boolean_spec.rb2
-rw-r--r--spec/lib/gitlab/redis/cache_spec.rb4
-rw-r--r--spec/lib/gitlab/redis/duplicate_jobs_spec.rb2
-rw-r--r--spec/lib/gitlab/redis/multi_store_spec.rb44
-rw-r--r--spec/lib/gitlab/redis/sidekiq_status_spec.rb2
-rw-r--r--spec/lib/gitlab/render_timeout_spec.rb2
-rw-r--r--spec/lib/gitlab/seeder_spec.rb40
-rw-r--r--spec/lib/gitlab/seeders/ci/daily_build_group_report_result_spec.rb43
-rw-r--r--spec/lib/gitlab/service_desk_email_spec.rb2
-rw-r--r--spec/lib/gitlab/session_spec.rb2
-rw-r--r--spec/lib/gitlab/setup_helper/workhorse_spec.rb2
-rw-r--r--spec/lib/gitlab/shell_spec.rb53
-rw-r--r--spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb4
-rw-r--r--spec/lib/gitlab/sidekiq_death_handler_spec.rb8
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb8
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb48
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/size_limiter/server_spec.rb2
-rw-r--r--spec/lib/gitlab/sidekiq_migrate_jobs_spec.rb8
-rw-r--r--spec/lib/gitlab/sidekiq_queue_spec.rb10
-rw-r--r--spec/lib/gitlab/sidekiq_signals_spec.rb2
-rw-r--r--spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb2
-rw-r--r--spec/lib/gitlab/sidekiq_versioning_spec.rb25
-rw-r--r--spec/lib/gitlab/slug/environment_spec.rb28
-rw-r--r--spec/lib/gitlab/spamcheck/client_spec.rb59
-rw-r--r--spec/lib/gitlab/string_placeholder_replacer_spec.rb2
-rw-r--r--spec/lib/gitlab/string_range_marker_spec.rb2
-rw-r--r--spec/lib/gitlab/string_regex_marker_spec.rb2
-rw-r--r--spec/lib/gitlab/subscription_portal_spec.rb15
-rw-r--r--spec/lib/gitlab/tcp_checker_spec.rb2
-rw-r--r--spec/lib/gitlab/tracking/incident_management_spec.rb2
-rw-r--r--spec/lib/gitlab/tracking_spec.rb44
-rw-r--r--spec/lib/gitlab/tree_summary_spec.rb16
-rw-r--r--spec/lib/gitlab/url_blockers/domain_allowlist_entry_spec.rb2
-rw-r--r--spec/lib/gitlab/url_blockers/ip_allowlist_entry_spec.rb2
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb66
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/count_user_auth_metric_spec.rb35
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb22
-rw-r--r--spec/lib/gitlab/usage_data/topology_spec.rb16
-rw-r--r--spec/lib/gitlab/usage_data_counters/base_counter_spec.rb2
-rw-r--r--spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb2
-rw-r--r--spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb68
-rw-r--r--spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb4
-rw-r--r--spec/lib/gitlab/usage_data_counters/note_counter_spec.rb2
-rw-r--r--spec/lib/gitlab/usage_data_counters_spec.rb2
-rw-r--r--spec/lib/gitlab/usage_data_metrics_spec.rb8
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb29
-rw-r--r--spec/lib/gitlab/utils/deep_size_spec.rb8
-rw-r--r--spec/lib/gitlab/utils/delegator_override_spec.rb1
-rw-r--r--spec/lib/gitlab/utils/execution_tracker_spec.rb24
-rw-r--r--spec/lib/gitlab/utils/json_size_estimator_spec.rb2
-rw-r--r--spec/lib/gitlab/utils/markdown_spec.rb2
-rw-r--r--spec/lib/gitlab/utils/merge_hash_spec.rb2
-rw-r--r--spec/lib/gitlab/utils/nokogiri_spec.rb3
-rw-r--r--spec/lib/gitlab/utils/sanitize_node_link_spec.rb4
-rw-r--r--spec/lib/gitlab/utils_spec.rb2
-rw-r--r--spec/lib/gitlab/web_ide/config/entry/terminal_spec.rb23
-rw-r--r--spec/lib/gitlab/word_diff/chunk_collection_spec.rb2
-rw-r--r--spec/lib/gitlab/word_diff/line_processor_spec.rb2
-rw-r--r--spec/lib/gitlab/word_diff/parser_spec.rb20
-rw-r--r--spec/lib/gitlab/word_diff/positions_counter_spec.rb2
-rw-r--r--spec/lib/gitlab/word_diff/segments/chunk_spec.rb2
-rw-r--r--spec/lib/gitlab/word_diff/segments/diff_hunk_spec.rb2
-rw-r--r--spec/lib/gitlab/word_diff/segments/newline_spec.rb2
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb2
-rw-r--r--spec/lib/gitlab_edition_spec.rb55
-rw-r--r--spec/lib/google_api/cloud_platform/client_spec.rb2
-rw-r--r--spec/lib/learn_gitlab/onboarding_spec.rb63
-rw-r--r--spec/lib/learn_gitlab/project_spec.rb67
-rw-r--r--spec/lib/marginalia_spec.rb20
-rw-r--r--spec/lib/microsoft_teams/activity_spec.rb2
-rw-r--r--spec/lib/object_storage/direct_upload_spec.rb98
-rw-r--r--spec/lib/omni_auth/strategies/bitbucket_spec.rb29
-rw-r--r--spec/lib/peek/views/redis_detailed_spec.rb22
-rw-r--r--spec/lib/prometheus/cleanup_multiproc_dir_service_spec.rb1
-rw-r--r--spec/lib/security/ci_configuration/sast_build_action_spec.rb58
-rw-r--r--spec/lib/security/report_schema_version_matcher_spec.rb2
-rw-r--r--spec/lib/security/weak_passwords_spec.rb112
-rw-r--r--spec/lib/sidebars/concerns/container_with_html_options_spec.rb2
-rw-r--r--spec/lib/sidebars/concerns/link_with_html_options_spec.rb2
-rw-r--r--spec/lib/sidebars/groups/menus/observability_menu_spec.rb43
-rw-r--r--spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/groups/menus/settings_menu_spec.rb2
-rw-r--r--spec/lib/sidebars/menu_item_spec.rb2
-rw-r--r--spec/lib/sidebars/projects/menus/deployments_menu_spec.rb36
-rw-r--r--spec/lib/sidebars/projects/menus/learn_gitlab_menu_spec.rb8
-rw-r--r--spec/lib/sidebars/projects/menus/monitor_menu_spec.rb25
-rw-r--r--spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb18
-rw-r--r--spec/lib/sidebars/projects/menus/settings_menu_spec.rb8
-rw-r--r--spec/lib/system_check/base_check_spec.rb2
-rw-r--r--spec/mailers/emails/pipelines_spec.rb4
-rw-r--r--spec/mailers/emails/service_desk_spec.rb2
-rw-r--r--spec/mailers/notify_spec.rb11
-rw-r--r--spec/mailers/previews_spec.rb44
-rw-r--r--spec/mailers/repository_check_mailer_spec.rb9
-rw-r--r--spec/migrations/20210804150320_create_base_work_item_types_spec.rb6
-rw-r--r--spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb2
-rw-r--r--spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb6
-rw-r--r--spec/migrations/20210910194952_update_report_type_for_existing_approval_project_rules_spec.rb2
-rw-r--r--spec/migrations/20211117084814_migrate_remaining_u2f_registrations_spec.rb12
-rw-r--r--spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb8
-rw-r--r--spec/migrations/20220601110011_schedule_remove_self_managed_wiki_notes_spec.rb43
-rw-r--r--spec/migrations/20220606080509_fix_incorrect_job_artifacts_expire_at_spec.rb42
-rw-r--r--spec/migrations/20220801155858_schedule_disable_legacy_open_source_licence_for_recent_public_projects_spec.rb62
-rw-r--r--spec/migrations/20220809002011_schedule_destroy_invalid_group_members_spec.rb31
-rw-r--r--spec/migrations/20220816163444_update_start_date_for_iterations_cadences_spec.rb73
-rw-r--r--spec/migrations/20220819153725_add_vulnerability_advisory_foreign_key_to_sbom_vulnerable_component_versions_spec.rb23
-rw-r--r--spec/migrations/20220819162852_add_sbom_component_version_foreign_key_to_sbom_vulnerable_component_versions_spec.rb23
-rw-r--r--spec/migrations/20220901035725_schedule_destroy_invalid_project_members_spec.rb31
-rw-r--r--spec/migrations/20220906074449_schedule_disable_legacy_open_source_license_for_projects_less_than_one_mb_spec.rb62
-rw-r--r--spec/migrations/20220913030624_cleanup_attention_request_related_system_notes_spec.rb26
-rw-r--r--spec/migrations/backfill_namespace_id_on_issues_spec.rb32
-rw-r--r--spec/migrations/change_task_system_note_wording_to_checklist_item_spec.rb32
-rw-r--r--spec/migrations/confirm_support_bot_user_spec.rb2
-rw-r--r--spec/migrations/move_security_findings_table_to_gitlab_partitions_dynamic_schema_spec.rb108
-rw-r--r--spec/migrations/orphaned_invited_members_cleanup_spec.rb46
-rw-r--r--spec/migrations/reschedule_issue_work_item_type_id_backfill_spec.rb54
-rw-r--r--spec/migrations/reset_job_token_scope_enabled_again_spec.rb4
-rw-r--r--spec/migrations/reset_job_token_scope_enabled_spec.rb4
-rw-r--r--spec/migrations/reset_severity_levels_to_new_default_spec.rb10
-rw-r--r--spec/migrations/schedule_backfill_cluster_agents_has_vulnerabilities_spec.rb24
-rw-r--r--spec/models/application_setting_spec.rb8
-rw-r--r--spec/models/ci/build_dependencies_spec.rb23
-rw-r--r--spec/models/ci/build_spec.rb441
-rw-r--r--spec/models/ci/freeze_period_status_spec.rb9
-rw-r--r--spec/models/ci/job_artifact_spec.rb132
-rw-r--r--spec/models/ci/namespace_mirror_spec.rb38
-rw-r--r--spec/models/ci/pipeline_artifact_spec.rb13
-rw-r--r--spec/models/ci/pipeline_spec.rb352
-rw-r--r--spec/models/ci/pipeline_variable_spec.rb21
-rw-r--r--spec/models/ci/processable_spec.rb20
-rw-r--r--spec/models/ci/runner_spec.rb188
-rw-r--r--spec/models/ci/stage_spec.rb29
-rw-r--r--spec/models/ci/trigger_spec.rb17
-rw-r--r--spec/models/clusters/platforms/kubernetes_spec.rb16
-rw-r--r--spec/models/commit_signatures/gpg_signature_spec.rb17
-rw-r--r--spec/models/commit_signatures/ssh_signature_spec.rb12
-rw-r--r--spec/models/commit_signatures/x509_commit_signature_spec.rb11
-rw-r--r--spec/models/commit_spec.rb16
-rw-r--r--spec/models/commit_status_spec.rb59
-rw-r--r--spec/models/concerns/approvable_base_spec.rb111
-rw-r--r--spec/models/concerns/approvable_spec.rb111
-rw-r--r--spec/models/concerns/ci/artifactable_spec.rb24
-rw-r--r--spec/models/concerns/ci/has_deployment_name_spec.rb34
-rw-r--r--spec/models/concerns/ci/track_environment_usage_spec.rb61
-rw-r--r--spec/models/concerns/counter_attribute_spec.rb10
-rw-r--r--spec/models/concerns/from_set_operator_spec.rb51
-rw-r--r--spec/models/concerns/pg_full_text_searchable_spec.rb1
-rw-r--r--spec/models/concerns/project_features_compatibility_spec.rb1
-rw-r--r--spec/models/concerns/reactive_caching_spec.rb6
-rw-r--r--spec/models/container_registry/event_spec.rb2
-rw-r--r--spec/models/customer_relations/organization_spec.rb36
-rw-r--r--spec/models/deployment_spec.rb101
-rw-r--r--spec/models/design_management/version_spec.rb2
-rw-r--r--spec/models/environment_spec.rb79
-rw-r--r--spec/models/error_tracking/project_error_tracking_setting_spec.rb106
-rw-r--r--spec/models/group_group_link_spec.rb11
-rw-r--r--spec/models/group_spec.rb343
-rw-r--r--spec/models/incident_management/timeline_event_spec.rb16
-rw-r--r--spec/models/integrations/chat_message/pipeline_message_spec.rb4
-rw-r--r--spec/models/integrations/datadog_spec.rb9
-rw-r--r--spec/models/integrations/discord_spec.rb4
-rw-r--r--spec/models/integrations/drone_ci_spec.rb4
-rw-r--r--spec/models/integrations/hangouts_chat_spec.rb171
-rw-r--r--spec/models/integrations/harbor_spec.rb6
-rw-r--r--spec/models/issue_spec.rb32
-rw-r--r--spec/models/jira_connect_installation_spec.rb26
-rw-r--r--spec/models/loose_foreign_keys/deleted_record_spec.rb4
-rw-r--r--spec/models/member_spec.rb12
-rw-r--r--spec/models/members/group_member_spec.rb7
-rw-r--r--spec/models/members/project_member_spec.rb2
-rw-r--r--spec/models/merge_request_assignee_spec.rb22
-rw-r--r--spec/models/merge_request_reviewer_spec.rb20
-rw-r--r--spec/models/merge_request_spec.rb95
-rw-r--r--spec/models/ml/candidate_spec.rb31
-rw-r--r--spec/models/ml/experiment_spec.rb51
-rw-r--r--spec/models/namespace_setting_spec.rb50
-rw-r--r--spec/models/namespace_spec.rb133
-rw-r--r--spec/models/namespaces/sync_event_spec.rb23
-rw-r--r--spec/models/note_spec.rb26
-rw-r--r--spec/models/notification_recipient_spec.rb50
-rw-r--r--spec/models/oauth_access_token_spec.rb17
-rw-r--r--spec/models/onboarding/completion_spec.rb59
-rw-r--r--spec/models/onboarding/learn_gitlab_spec.rb69
-rw-r--r--spec/models/onboarding/progress_spec.rb317
-rw-r--r--spec/models/onboarding_progress_spec.rb293
-rw-r--r--spec/models/operations/feature_flag_spec.rb14
-rw-r--r--spec/models/packages/package_spec.rb13
-rw-r--r--spec/models/packages/rpm/metadatum_spec.rb36
-rw-r--r--spec/models/pages_domain_spec.rb58
-rw-r--r--spec/models/personal_access_token_spec.rb75
-rw-r--r--spec/models/pool_repository_spec.rb9
-rw-r--r--spec/models/preloaders/project_policy_preloader_spec.rb55
-rw-r--r--spec/models/preloaders/project_root_ancestor_preloader_spec.rb99
-rw-r--r--spec/models/project_setting_spec.rb47
-rw-r--r--spec/models/project_spec.rb257
-rw-r--r--spec/models/project_statistics_spec.rb30
-rw-r--r--spec/models/projects/build_artifacts_size_refresh_spec.rb12
-rw-r--r--spec/models/protected_branch_spec.rb33
-rw-r--r--spec/models/remote_mirror_spec.rb8
-rw-r--r--spec/models/repository_spec.rb77
-rw-r--r--spec/models/resource_state_event_spec.rb42
-rw-r--r--spec/models/snippet_repository_spec.rb8
-rw-r--r--spec/models/spam_log_spec.rb34
-rw-r--r--spec/models/user_spec.rb121
-rw-r--r--spec/models/user_status_spec.rb8
-rw-r--r--spec/models/users/credit_card_validation_spec.rb23
-rw-r--r--spec/models/users/ghost_user_migration_spec.rb14
-rw-r--r--spec/models/users/merge_request_interaction_spec.rb3
-rw-r--r--spec/models/users_star_project_spec.rb7
-rw-r--r--spec/models/work_item_spec.rb21
-rw-r--r--spec/models/work_items/widgets/description_spec.rb43
-rw-r--r--spec/policies/ci/pipeline_schedule_policy_spec.rb2
-rw-r--r--spec/policies/ci/runner_policy_spec.rb160
-rw-r--r--spec/policies/clusters/agent_policy_spec.rb2
-rw-r--r--spec/policies/commit_policy_spec.rb107
-rw-r--r--spec/policies/group_member_policy_spec.rb6
-rw-r--r--spec/policies/group_policy_spec.rb98
-rw-r--r--spec/policies/issuable_policy_spec.rb54
-rw-r--r--spec/policies/issue_policy_spec.rb2
-rw-r--r--spec/policies/merge_request_policy_spec.rb374
-rw-r--r--spec/policies/packages/policies/group_policy_spec.rb79
-rw-r--r--spec/policies/packages/policies/project_policy_spec.rb164
-rw-r--r--spec/policies/project_policy_spec.rb158
-rw-r--r--spec/policies/protected_branch_access_policy_spec.rb31
-rw-r--r--spec/policies/protected_branch_policy_spec.rb44
-rw-r--r--spec/policies/terraform/state_policy_spec.rb2
-rw-r--r--spec/policies/terraform/state_version_policy_spec.rb2
-rw-r--r--spec/presenters/blobs/notebook_presenter_spec.rb10
-rw-r--r--spec/presenters/ci/pipeline_presenter_spec.rb2
-rw-r--r--spec/presenters/clusters/cluster_presenter_spec.rb26
-rw-r--r--spec/presenters/deployments/deployment_presenter_spec.rb15
-rw-r--r--spec/presenters/packages/composer/packages_presenter_spec.rb2
-rw-r--r--spec/presenters/packages/conan/package_presenter_spec.rb2
-rw-r--r--spec/presenters/packages/nuget/packages_metadata_presenter_spec.rb2
-rw-r--r--spec/presenters/project_presenter_spec.rb20
-rw-r--r--spec/requests/admin/hook_logs_controller_spec.rb15
-rw-r--r--spec/requests/api/admin/batched_background_migrations_spec.rb230
-rw-r--r--spec/requests/api/branches_spec.rb141
-rw-r--r--spec/requests/api/ci/job_artifacts_spec.rb12
-rw-r--r--spec/requests/api/ci/jobs_spec.rb26
-rw-r--r--spec/requests/api/ci/runner/jobs_request_post_spec.rb83
-rw-r--r--spec/requests/api/ci/runners_spec.rb38
-rw-r--r--spec/requests/api/commit_statuses_spec.rb20
-rw-r--r--spec/requests/api/commits_spec.rb616
-rw-r--r--spec/requests/api/conan_instance_packages_spec.rb6
-rw-r--r--spec/requests/api/conan_project_packages_spec.rb6
-rw-r--r--spec/requests/api/debian_group_packages_spec.rb40
-rw-r--r--spec/requests/api/debian_project_packages_spec.rb40
-rw-r--r--spec/requests/api/deployments_spec.rb9
-rw-r--r--spec/requests/api/feature_flags_spec.rb8
-rw-r--r--spec/requests/api/files_spec.rb715
-rw-r--r--spec/requests/api/generic_packages_spec.rb18
-rw-r--r--spec/requests/api/graphql/ci/config_spec.rb6
-rw-r--r--spec/requests/api/graphql/ci/config_variables_spec.rb93
-rw-r--r--spec/requests/api/graphql/ci/group_variables_spec.rb12
-rw-r--r--spec/requests/api/graphql/ci/instance_variables_spec.rb2
-rw-r--r--spec/requests/api/graphql/ci/jobs_spec.rb31
-rw-r--r--spec/requests/api/graphql/ci/project_variables_spec.rb4
-rw-r--r--spec/requests/api/graphql/ci/runner_spec.rb163
-rw-r--r--spec/requests/api/graphql/ci/runners_spec.rb18
-rw-r--r--spec/requests/api/graphql/custom_emoji_query_spec.rb12
-rw-r--r--spec/requests/api/graphql/environments/deployments_query_spec.rb487
-rw-r--r--spec/requests/api/graphql/group/group_members_spec.rb9
-rw-r--r--spec/requests/api/graphql/group/packages_spec.rb2
-rw-r--r--spec/requests/api/graphql/group/work_item_types_spec.rb2
-rw-r--r--spec/requests/api/graphql/mutations/boards/issues/issue_move_list_spec.rb14
-rw-r--r--spec/requests/api/graphql/mutations/branches/create_spec.rb89
-rw-r--r--spec/requests/api/graphql/mutations/ci/job/destroy_spec.rb54
-rw-r--r--spec/requests/api/graphql/mutations/ci/job_artifact/destroy_spec.rb47
-rw-r--r--spec/requests/api/graphql/mutations/custom_emoji/create_spec.rb14
-rw-r--r--spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb14
-rw-r--r--spec/requests/api/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb3
-rw-r--r--spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb10
-rw-r--r--spec/requests/api/graphql/mutations/releases/update_spec.rb11
-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.rb6
-rw-r--r--spec/requests/api/graphql/packages/nuget_spec.rb2
-rw-r--r--spec/requests/api/graphql/packages/package_spec.rb15
-rw-r--r--spec/requests/api/graphql/packages/pypi_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/branch_protections/merge_access_levels_spec.rb109
-rw-r--r--spec/requests/api/graphql/project/branch_protections/push_access_levels_spec.rb109
-rw-r--r--spec/requests/api/graphql/project/branch_rules/branch_protection_spec.rb60
-rw-r--r--spec/requests/api/graphql/project/branch_rules_spec.rb122
-rw-r--r--spec/requests/api/graphql/project/deployment_spec.rb51
-rw-r--r--spec/requests/api/graphql/project/environments_spec.rb133
-rw-r--r--spec/requests/api/graphql/project/issue/design_collection/version_spec.rb10
-rw-r--r--spec/requests/api/graphql/project/issues_spec.rb26
-rw-r--r--spec/requests/api/graphql/project/job_spec.rb54
-rw-r--r--spec/requests/api/graphql/project/merge_request_spec.rb20
-rw-r--r--spec/requests/api/graphql/project/pipeline_spec.rb2
-rw-r--r--spec/requests/api/graphql/project/terraform/state_spec.rb16
-rw-r--r--spec/requests/api/graphql/project/terraform/states_spec.rb18
-rw-r--r--spec/requests/api/graphql/project/work_items_spec.rb63
-rw-r--r--spec/requests/api/graphql/query_spec.rb4
-rw-r--r--spec/requests/api/graphql/work_item_spec.rb19
-rw-r--r--spec/requests/api/group_export_spec.rb1
-rw-r--r--spec/requests/api/groups_spec.rb136
-rw-r--r--spec/requests/api/import_github_spec.rb10
-rw-r--r--spec/requests/api/integrations/slack/events_spec.rb112
-rw-r--r--spec/requests/api/internal/base_spec.rb201
-rw-r--r--spec/requests/api/internal/lfs_spec.rb26
-rw-r--r--spec/requests/api/issues/get_group_issues_spec.rb8
-rw-r--r--spec/requests/api/markdown_snapshot_spec.rb4
-rw-r--r--spec/requests/api/maven_packages_spec.rb160
-rw-r--r--spec/requests/api/merge_requests_spec.rb187
-rw-r--r--spec/requests/api/ml/mlflow_spec.rb366
-rw-r--r--spec/requests/api/namespaces_spec.rb8
-rw-r--r--spec/requests/api/npm_project_packages_spec.rb3
-rw-r--r--spec/requests/api/personal_access_tokens/self_revocation_spec.rb69
-rw-r--r--spec/requests/api/personal_access_tokens_spec.rb58
-rw-r--r--spec/requests/api/project_attributes.yml2
-rw-r--r--spec/requests/api/project_import_spec.rb2
-rw-r--r--spec/requests/api/project_packages_spec.rb13
-rw-r--r--spec/requests/api/project_snippets_spec.rb3
-rw-r--r--spec/requests/api/projects_spec.rb190
-rw-r--r--spec/requests/api/releases_spec.rb218
-rw-r--r--spec/requests/api/resource_access_tokens_spec.rb60
-rw-r--r--spec/requests/api/resource_state_events_spec.rb83
-rw-r--r--spec/requests/api/rpm_project_packages_spec.rb250
-rw-r--r--spec/requests/api/search_spec.rb40
-rw-r--r--spec/requests/api/settings_spec.rb14
-rw-r--r--spec/requests/api/snippets_spec.rb2
-rw-r--r--spec/requests/api/suggestions_spec.rb15
-rw-r--r--spec/requests/api/tags_spec.rb78
-rw-r--r--spec/requests/api/topics_spec.rb62
-rw-r--r--spec/requests/api/unleash_spec.rb94
-rw-r--r--spec/requests/api/usage_data_queries_spec.rb32
-rw-r--r--spec/requests/api/usage_data_spec.rb4
-rw-r--r--spec/requests/api/users_spec.rb122
-rw-r--r--spec/requests/git_http_spec.rb4
-rw-r--r--spec/requests/groups/observability_controller_spec.rb190
-rw-r--r--spec/requests/health_controller_spec.rb22
-rw-r--r--spec/requests/jira_connect/oauth_callbacks_controller_spec.rb6
-rw-r--r--spec/requests/jira_connect/subscriptions_controller_spec.rb21
-rw-r--r--spec/requests/jwt_controller_spec.rb24
-rw-r--r--spec/requests/oauth_tokens_spec.rb11
-rw-r--r--spec/requests/openid_connect_spec.rb20
-rw-r--r--spec/requests/projects/environments_controller_spec.rb4
-rw-r--r--spec/requests/projects/google_cloud/configuration_controller_spec.rb44
-rw-r--r--spec/requests/projects/google_cloud/databases_controller_spec.rb221
-rw-r--r--spec/requests/projects/google_cloud/deployments_controller_spec.rb72
-rw-r--r--spec/requests/projects/google_cloud/gcp_regions_controller_spec.rb28
-rw-r--r--spec/requests/projects/google_cloud/revoke_oauth_controller_spec.rb14
-rw-r--r--spec/requests/projects/google_cloud/service_accounts_controller_spec.rb42
-rw-r--r--spec/requests/projects/hook_logs_controller_spec.rb19
-rw-r--r--spec/requests/projects/merge_requests/context_commit_diffs_spec.rb26
-rw-r--r--spec/requests/projects/merge_requests/diffs_spec.rb283
-rw-r--r--spec/requests/projects/merge_requests_discussions_spec.rb9
-rw-r--r--spec/requests/projects/packages/package_files_controller_spec.rb30
-rw-r--r--spec/requests/projects/settings/integration_hook_logs_controller_spec.rb20
-rw-r--r--spec/requests/verifies_with_email_spec.rb14
-rw-r--r--spec/routing/group_routing_spec.rb4
-rw-r--r--spec/routing/project_routing_spec.rb20
-rw-r--r--spec/routing/uploads_routing_spec.rb8
-rw-r--r--spec/rubocop/check_graceful_task_spec.rb125
-rw-r--r--spec/rubocop/code_reuse_helpers_spec.rb29
-rw-r--r--spec/rubocop/cop/active_model_errors_direct_manipulation_spec.rb4
-rw-r--r--spec/rubocop/cop/active_record_association_reload_spec.rb4
-rw-r--r--spec/rubocop/cop/api/base_spec.rb4
-rw-r--r--spec/rubocop/cop/api/grape_array_missing_coerce_spec.rb4
-rw-r--r--spec/rubocop/cop/avoid_becomes_spec.rb4
-rw-r--r--spec/rubocop/cop/avoid_break_from_strong_memoize_spec.rb4
-rw-r--r--spec/rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers_spec.rb4
-rw-r--r--spec/rubocop/cop/avoid_return_from_blocks_spec.rb4
-rw-r--r--spec/rubocop/cop/avoid_route_redirect_leading_slash_spec.rb4
-rw-r--r--spec/rubocop/cop/ban_catch_throw_spec.rb4
-rw-r--r--spec/rubocop/cop/code_reuse/finder_spec.rb4
-rw-r--r--spec/rubocop/cop/code_reuse/presenter_spec.rb4
-rw-r--r--spec/rubocop/cop/code_reuse/serializer_spec.rb4
-rw-r--r--spec/rubocop/cop/code_reuse/service_class_spec.rb4
-rw-r--r--spec/rubocop/cop/code_reuse/worker_spec.rb4
-rw-r--r--spec/rubocop/cop/database/disable_referential_integrity_spec.rb4
-rw-r--r--spec/rubocop/cop/database/establish_connection_spec.rb4
-rw-r--r--spec/rubocop/cop/database/multiple_databases_spec.rb4
-rw-r--r--spec/rubocop/cop/database/rescue_query_canceled_spec.rb4
-rw-r--r--spec/rubocop/cop/database/rescue_statement_timeout_spec.rb4
-rw-r--r--spec/rubocop/cop/default_scope_spec.rb4
-rw-r--r--spec/rubocop/cop/destroy_all_spec.rb10
-rw-r--r--spec/rubocop/cop/file_decompression_spec.rb4
-rw-r--r--spec/rubocop/cop/filename_length_spec.rb4
-rw-r--r--spec/rubocop/cop/gemspec/avoid_executing_git_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/avoid_feature_category_not_owned_spec.rb10
-rw-r--r--spec/rubocop/cop/gitlab/avoid_feature_get_spec.rb32
-rw-r--r--spec/rubocop/cop/gitlab/avoid_uploaded_file_from_params_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/bulk_insert_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/change_timezone_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/const_get_inherit_false_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/delegate_predicate_methods_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/deprecate_track_redis_hll_event_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/duplicate_spec_location_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/event_store_subscriber_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/except_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/feature_available_usage_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/finder_with_find_by_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/httparty_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/intersect_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/json_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/keys_first_and_values_first_spec.rb52
-rw-r--r--spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb18
-rw-r--r--spec/rubocop/cop/gitlab/module_with_instance_variables_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/namespaced_class_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/policy_rule_boolean_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/predicate_memoization_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/rails_logger_spec.rb4
-rw-r--r--spec/rubocop/cop/gitlab/union_spec.rb4
-rw-r--r--spec/rubocop/cop/graphql/authorize_types_spec.rb4
-rw-r--r--spec/rubocop/cop/graphql/descriptions_spec.rb4
-rw-r--r--spec/rubocop/cop/graphql/gid_expected_type_spec.rb4
-rw-r--r--spec/rubocop/cop/graphql/graphql_name_position_spec.rb4
-rw-r--r--spec/rubocop/cop/graphql/id_type_spec.rb4
-rw-r--r--spec/rubocop/cop/graphql/json_type_spec.rb4
-rw-r--r--spec/rubocop/cop/graphql/old_types_spec.rb4
-rw-r--r--spec/rubocop/cop/graphql/resolver_type_spec.rb4
-rw-r--r--spec/rubocop/cop/group_public_or_visible_to_user_spec.rb4
-rw-r--r--spec/rubocop/cop/ignored_columns_spec.rb4
-rw-r--r--spec/rubocop/cop/include_sidekiq_worker_spec.rb4
-rw-r--r--spec/rubocop/cop/inject_enterprise_edition_module_spec.rb4
-rw-r--r--spec/rubocop/cop/lint/last_keyword_argument_spec.rb12
-rw-r--r--spec/rubocop/cop/migration/add_column_with_default_spec.rb2
-rw-r--r--spec/rubocop/cop/migration/add_columns_to_wide_tables_spec.rb2
-rw-r--r--spec/rubocop/cop/migration/add_concurrent_foreign_key_spec.rb2
-rw-r--r--spec/rubocop/cop/migration/add_concurrent_index_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/add_index_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/add_limit_to_text_columns_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/add_reference_spec.rb2
-rw-r--r--spec/rubocop/cop/migration/add_timestamps_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/background_migration_base_class_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/background_migration_record_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/background_migrations_spec.rb2
-rw-r--r--spec/rubocop/cop/migration/complex_indexes_require_name_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/datetime_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/drop_table_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/migration_record_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/prevent_global_enable_lock_retries_with_disable_ddl_transaction_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/prevent_index_creation_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/prevent_strings_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/refer_to_index_by_name_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/remove_column_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/remove_concurrent_index_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/remove_index_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/safer_boolean_column_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/schedule_async_spec.rb2
-rw-r--r--spec/rubocop/cop/migration/sidekiq_queue_migrate_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/timestamps_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/update_column_in_batches_spec.rb2
-rw-r--r--spec/rubocop/cop/migration/versioned_migration_class_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/with_lock_retries_disallowed_method_spec.rb4
-rw-r--r--spec/rubocop/cop/migration/with_lock_retries_with_change_spec.rb4
-rw-r--r--spec/rubocop/cop/performance/active_record_subtransaction_methods_spec.rb4
-rw-r--r--spec/rubocop/cop/performance/active_record_subtransactions_spec.rb4
-rw-r--r--spec/rubocop/cop/performance/ar_count_each_spec.rb8
-rw-r--r--spec/rubocop/cop/performance/ar_exists_and_present_blank_spec.rb8
-rw-r--r--spec/rubocop/cop/performance/readlines_each_spec.rb4
-rw-r--r--spec/rubocop/cop/prefer_class_methods_over_module_spec.rb4
-rw-r--r--spec/rubocop/cop/project_path_helper_spec.rb4
-rw-r--r--spec/rubocop/cop/put_group_routes_under_scope_spec.rb4
-rw-r--r--spec/rubocop/cop/put_project_routes_under_scope_spec.rb4
-rw-r--r--spec/rubocop/cop/qa/ambiguous_page_object_name_spec.rb4
-rw-r--r--spec/rubocop/cop/qa/element_with_pattern_spec.rb4
-rw-r--r--spec/rubocop/cop/qa/selector_usage_spec.rb4
-rw-r--r--spec/rubocop/cop/rspec/any_instance_of_spec.rb4
-rw-r--r--spec/rubocop/cop/rspec/be_success_matcher_spec.rb4
-rw-r--r--spec/rubocop/cop/rspec/env_assignment_spec.rb4
-rw-r--r--spec/rubocop/cop/rspec/expect_gitlab_tracking_spec.rb4
-rw-r--r--spec/rubocop/cop/rspec/factories_in_migration_specs_spec.rb4
-rw-r--r--spec/rubocop/cop/rspec/factory_bot/inline_association_spec.rb4
-rw-r--r--spec/rubocop/cop/rspec/have_gitlab_http_status_spec.rb4
-rw-r--r--spec/rubocop/cop/rspec/htt_party_basic_auth_spec.rb4
-rw-r--r--spec/rubocop/cop/rspec/modify_sidekiq_middleware_spec.rb4
-rw-r--r--spec/rubocop/cop/rspec/timecop_freeze_spec.rb4
-rw-r--r--spec/rubocop/cop/rspec/timecop_travel_spec.rb4
-rw-r--r--spec/rubocop/cop/rspec/top_level_describe_path_spec.rb4
-rw-r--r--spec/rubocop/cop/rspec/web_mock_enable_spec.rb4
-rw-r--r--spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb4
-rw-r--r--spec/rubocop/cop/safe_params_spec.rb4
-rw-r--r--spec/rubocop/cop/scalability/bulk_perform_with_context_spec.rb4
-rw-r--r--spec/rubocop/cop/scalability/cron_worker_context_spec.rb4
-rw-r--r--spec/rubocop/cop/scalability/file_uploads_spec.rb4
-rw-r--r--spec/rubocop/cop/scalability/idempotent_worker_spec.rb4
-rw-r--r--spec/rubocop/cop/sidekiq_load_balancing/worker_data_consistency_spec.rb4
-rw-r--r--spec/rubocop/cop/sidekiq_options_queue_spec.rb4
-rw-r--r--spec/rubocop/cop/static_translation_definition_spec.rb4
-rw-r--r--spec/rubocop/cop/style/regexp_literal_mixed_preserve_spec.rb2
-rw-r--r--spec/rubocop/cop/usage_data/distinct_count_by_large_foreign_key_spec.rb4
-rw-r--r--spec/rubocop/cop/usage_data/histogram_with_large_table_spec.rb4
-rw-r--r--spec/rubocop/cop/usage_data/instrumentation_superclass_spec.rb4
-rw-r--r--spec/rubocop/cop/usage_data/large_table_spec.rb4
-rw-r--r--spec/rubocop/cop/user_admin_spec.rb4
-rw-r--r--spec/rubocop/cop_todo_spec.rb22
-rw-r--r--spec/rubocop/formatter/graceful_formatter_spec.rb239
-rw-r--r--spec/rubocop/formatter/todo_formatter_spec.rb96
-rw-r--r--spec/rubocop/qa_helpers_spec.rb2
-rw-r--r--spec/rubocop/todo_dir_spec.rb4
-rw-r--r--spec/rubocop_spec_helper.rb20
-rw-r--r--spec/scripts/changed-feature-flags_spec.rb1
-rw-r--r--spec/scripts/determine-qa-tests_spec.rb109
-rw-r--r--spec/scripts/lib/glfm/parse_examples_spec.rb331
-rw-r--r--spec/scripts/lib/glfm/shared_spec.rb18
-rw-r--r--spec/scripts/lib/glfm/update_example_snapshots_spec.rb500
-rw-r--r--spec/scripts/lib/glfm/update_specification_spec.rb21
-rw-r--r--spec/scripts/trigger-build_spec.rb286
-rw-r--r--spec/serializers/access_token_entity_base_spec.rb26
-rw-r--r--spec/serializers/ci/dag_pipeline_entity_spec.rb32
-rw-r--r--spec/serializers/ci/lint/job_entity_spec.rb2
-rw-r--r--spec/serializers/cluster_entity_spec.rb2
-rw-r--r--spec/serializers/container_repository_entity_spec.rb3
-rw-r--r--spec/serializers/deployment_entity_spec.rb3
-rw-r--r--spec/serializers/group_access_token_entity_spec.rb4
-rw-r--r--spec/serializers/impersonation_access_token_entity_spec.rb26
-rw-r--r--spec/serializers/impersonation_access_token_serializer_spec.rb20
-rw-r--r--spec/serializers/import/provider_repo_serializer_spec.rb2
-rw-r--r--spec/serializers/member_user_entity_spec.rb6
-rw-r--r--spec/serializers/merge_request_metrics_helper_spec.rb6
-rw-r--r--spec/serializers/pipeline_details_entity_spec.rb2
-rw-r--r--spec/serializers/project_access_token_entity_spec.rb4
-rw-r--r--spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb6
-rw-r--r--spec/services/bulk_imports/file_download_service_spec.rb2
-rw-r--r--spec/services/bulk_imports/tree_export_service_spec.rb4
-rw-r--r--spec/services/ci/after_requeue_job_service_spec.rb97
-rw-r--r--spec/services/ci/archive_trace_service_spec.rb9
-rw-r--r--spec/services/ci/build_erase_service_spec.rb88
-rw-r--r--spec/services/ci/compare_reports_base_service_spec.rb18
-rw-r--r--spec/services/ci/create_downstream_pipeline_service_spec.rb67
-rw-r--r--spec/services/ci/create_pipeline_service/artifacts_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/cache_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/custom_config_content_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/custom_yaml_tags_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/dry_run_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/environment_spec.rb48
-rw-r--r--spec/services/ci/create_pipeline_service/evaluate_runner_tags_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/include_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/logger_spec.rb8
-rw-r--r--spec/services/ci/create_pipeline_service/merge_requests_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/needs_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/parallel_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/parameter_content_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb4
-rw-r--r--spec/services/ci/create_pipeline_service/partitioning_spec.rb146
-rw-r--r--spec/services/ci/create_pipeline_service/pre_post_stages_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/rate_limit_spec.rb4
-rw-r--r--spec/services/ci/create_pipeline_service/rules_spec.rb2
-rw-r--r--spec/services/ci/create_pipeline_service/tags_spec.rb4
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb4
-rw-r--r--spec/services/ci/job_artifacts/create_service_spec.rb17
-rw-r--r--spec/services/ci/job_artifacts/delete_service_spec.rb41
-rw-r--r--spec/services/ci/job_artifacts/destroy_batch_service_spec.rb11
-rw-r--r--spec/services/ci/job_artifacts/track_artifact_report_service_spec.rb122
-rw-r--r--spec/services/ci/job_token_scope/add_project_service_spec.rb20
-rw-r--r--spec/services/ci/job_token_scope/remove_project_service_spec.rb20
-rw-r--r--spec/services/ci/list_config_variables_service_spec.rb4
-rw-r--r--spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb24
-rw-r--r--spec/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service_spec.rb24
-rw-r--r--spec/services/ci/pipeline_processing/atomic_processing_service/status_collection_spec.rb27
-rw-r--r--spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb2
-rw-r--r--spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_fail_and_retry_1.yml55
-rw-r--r--spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_fail_and_retry_2.yml63
-rw-r--r--spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_subsequent_manual_jobs.yml65
-rw-r--r--spec/services/ci/pipeline_schedule_service_spec.rb11
-rw-r--r--spec/services/ci/pipelines/add_job_service_spec.rb8
-rw-r--r--spec/services/ci/pipelines/hook_service_spec.rb2
-rw-r--r--spec/services/ci/play_manual_stage_service_spec.rb2
-rw-r--r--spec/services/ci/process_sync_events_service_spec.rb12
-rw-r--r--spec/services/ci/queue/pending_builds_strategy_spec.rb24
-rw-r--r--spec/services/ci/register_job_service_spec.rb8
-rw-r--r--spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb15
-rw-r--r--spec/services/ci/retry_job_service_spec.rb52
-rw-r--r--spec/services/ci/retry_pipeline_service_spec.rb121
-rw-r--r--spec/services/ci/runners/set_runner_associated_projects_service_spec.rb88
-rw-r--r--spec/services/ci/runners/update_runner_service_spec.rb80
-rw-r--r--spec/services/ci/unlock_artifacts_service_spec.rb34
-rw-r--r--spec/services/container_expiration_policies/cleanup_service_spec.rb2
-rw-r--r--spec/services/deployments/link_merge_requests_service_spec.rb4
-rw-r--r--spec/services/deployments/update_environment_service_spec.rb27
-rw-r--r--spec/services/design_management/delete_designs_service_spec.rb8
-rw-r--r--spec/services/design_management/save_designs_service_spec.rb16
-rw-r--r--spec/services/discussions/capture_diff_note_positions_service_spec.rb4
-rw-r--r--spec/services/environments/stop_service_spec.rb36
-rw-r--r--spec/services/event_create_service_spec.rb12
-rw-r--r--spec/services/git/branch_hooks_service_spec.rb2
-rw-r--r--spec/services/git/branch_push_service_spec.rb8
-rw-r--r--spec/services/git/wiki_push_service_spec.rb32
-rw-r--r--spec/services/google_cloud/enable_cloudsql_service_spec.rb22
-rw-r--r--spec/services/google_cloud/fetch_google_ip_list_service_spec.rb73
-rw-r--r--spec/services/groups/create_service_spec.rb4
-rw-r--r--spec/services/groups/destroy_service_spec.rb39
-rw-r--r--spec/services/groups/import_export/import_service_spec.rb28
-rw-r--r--spec/services/import/github_service_spec.rb10
-rw-r--r--spec/services/issuable/bulk_update_service_spec.rb2
-rw-r--r--spec/services/issues/build_service_spec.rb2
-rw-r--r--spec/services/issues/create_service_spec.rb60
-rw-r--r--spec/services/issues/relative_position_rebalancing_service_spec.rb15
-rw-r--r--spec/services/issues/update_service_spec.rb37
-rw-r--r--spec/services/members/create_service_spec.rb26
-rw-r--r--spec/services/merge_requests/approval_service_spec.rb73
-rw-r--r--spec/services/merge_requests/build_service_spec.rb24
-rw-r--r--spec/services/merge_requests/create_pipeline_service_spec.rb2
-rw-r--r--spec/services/merge_requests/create_service_spec.rb2
-rw-r--r--spec/services/merge_requests/ff_merge_service_spec.rb8
-rw-r--r--spec/services/merge_requests/handle_assignees_change_service_spec.rb2
-rw-r--r--spec/services/merge_requests/mergeability/detailed_merge_status_service_spec.rb97
-rw-r--r--spec/services/merge_requests/mergeability/logger_spec.rb121
-rw-r--r--spec/services/merge_requests/mergeability/run_checks_service_spec.rb10
-rw-r--r--spec/services/merge_requests/update_assignees_service_spec.rb43
-rw-r--r--spec/services/merge_requests/update_service_spec.rb111
-rw-r--r--spec/services/metrics/dashboard/clone_dashboard_service_spec.rb2
-rw-r--r--spec/services/notes/create_service_spec.rb10
-rw-r--r--spec/services/notes/destroy_service_spec.rb11
-rw-r--r--spec/services/onboarding/progress_service_spec.rb89
-rw-r--r--spec/services/onboarding_progress_service_spec.rb89
-rw-r--r--spec/services/packages/debian/parse_debian822_service_spec.rb2
-rw-r--r--spec/services/packages/debian/process_changes_service_spec.rb6
-rw-r--r--spec/services/packages/rpm/repository_metadata/base_builder_spec.rb22
-rw-r--r--spec/services/packages/rpm/repository_metadata/build_filelist_xml_spec.rb21
-rw-r--r--spec/services/packages/rpm/repository_metadata/build_other_xml_spec.rb21
-rw-r--r--spec/services/packages/rpm/repository_metadata/build_primary_xml_spec.rb21
-rw-r--r--spec/services/packages/rpm/repository_metadata/build_repomd_xml_spec.rb66
-rw-r--r--spec/services/post_receive_service_spec.rb3
-rw-r--r--spec/services/projects/blame_service_spec.rb12
-rw-r--r--spec/services/projects/container_repository/cleanup_tags_service_spec.rb456
-rw-r--r--spec/services/projects/container_repository/gitlab/cleanup_tags_service_spec.rb183
-rw-r--r--spec/services/projects/create_service_spec.rb26
-rw-r--r--spec/services/projects/destroy_service_spec.rb43
-rw-r--r--spec/services/projects/update_pages_service_spec.rb19
-rw-r--r--spec/services/protected_branches/cache_service_spec.rb10
-rw-r--r--spec/services/protected_branches/destroy_service_spec.rb2
-rw-r--r--spec/services/protected_branches/update_service_spec.rb2
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb57
-rw-r--r--spec/services/releases/create_service_spec.rb22
-rw-r--r--spec/services/resource_access_tokens/revoke_service_spec.rb33
-rw-r--r--spec/services/resource_events/change_labels_service_spec.rb19
-rw-r--r--spec/services/security/ci_configuration/sast_parser_service_spec.rb2
-rw-r--r--spec/services/service_ping/submit_service_ping_service_spec.rb151
-rw-r--r--spec/services/service_response_spec.rb15
-rw-r--r--spec/services/snippets/bulk_destroy_service_spec.rb4
-rw-r--r--spec/services/spam/spam_action_service_spec.rb78
-rw-r--r--spec/services/spam/spam_verdict_service_spec.rb78
-rw-r--r--spec/services/suggestions/apply_service_spec.rb53
-rw-r--r--spec/services/system_notes/time_tracking_service_spec.rb91
-rw-r--r--spec/services/topics/merge_service_spec.rb10
-rw-r--r--spec/services/users/destroy_service_spec.rb586
-rw-r--r--spec/services/users/email_verification/generate_token_service_spec.rb37
-rw-r--r--spec/services/users/email_verification/validate_token_service_spec.rb97
-rw-r--r--spec/services/users/migrate_records_to_ghost_user_in_batches_service_spec.rb31
-rw-r--r--spec/services/users/migrate_records_to_ghost_user_service_spec.rb259
-rw-r--r--spec/services/users/reject_service_spec.rb26
-rw-r--r--spec/services/work_items/update_service_spec.rb12
-rw-r--r--spec/services/work_items/widgets/description_service/update_service_spec.rb2
-rw-r--r--spec/spec_helper.rb40
-rw-r--r--spec/support/capybara.rb6
-rw-r--r--spec/support/database/without_check_constraint.rb52
-rw-r--r--spec/support/gitlab_stubs/gitlab_ci.yml3
-rw-r--r--spec/support/helpers/api_internal_base_helpers.rb16
-rw-r--r--spec/support/helpers/ci/template_helpers.rb4
-rw-r--r--spec/support/helpers/create_environments_helpers.rb4
-rw-r--r--spec/support/helpers/cycle_analytics_helpers.rb2
-rw-r--r--spec/support/helpers/database/partitioning_helpers.rb4
-rw-r--r--spec/support/helpers/gitlab_shell_helpers.rb14
-rw-r--r--spec/support/helpers/graphql_helpers.rb7
-rw-r--r--spec/support/helpers/html_escaped_helpers.rb24
-rw-r--r--spec/support/helpers/javascript_form_helper.rb7
-rw-r--r--spec/support/helpers/kubernetes_helpers.rb2
-rw-r--r--spec/support/helpers/login_helpers.rb2
-rw-r--r--spec/support/helpers/migrations_helpers/work_item_types_helper.rb8
-rw-r--r--spec/support/helpers/navbar_structure_helper.rb20
-rw-r--r--spec/support/helpers/seed_helper.rb4
-rw-r--r--spec/support/helpers/snowplow_helpers.rb4
-rw-r--r--spec/support/helpers/stub_configuration.rb4
-rw-r--r--spec/support/helpers/stub_object_storage.rb4
-rw-r--r--spec/support/helpers/test_env.rb163
-rw-r--r--spec/support/helpers/usage_data_helpers.rb43
-rw-r--r--spec/support/matchers/abort_matcher.rb19
-rw-r--r--spec/support/matchers/graphql_matchers.rb58
-rw-r--r--spec/support/matchers/markdown_matchers.rb12
-rw-r--r--spec/support/migrations_helpers/vulnerabilities_findings_helper.rb8
-rw-r--r--spec/support/redis.rb51
-rw-r--r--spec/support/redis/redis_helpers.rb34
-rw-r--r--spec/support/redis/redis_shared_examples.rb10
-rw-r--r--spec/support/rspec.rb26
-rw-r--r--spec/support/rspec_order.rb67
-rw-r--r--spec/support/rspec_order_todo.yml11150
-rw-r--r--spec/support/seed.rb9
-rw-r--r--spec/support/shared_contexts/bulk_imports_requests_shared_context.rb18
-rw-r--r--spec/support/shared_contexts/design_management_shared_contexts.rb18
-rw-r--r--spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb22
-rw-r--r--spec/support/shared_contexts/glfm/api_markdown_snapshot_shared_context.rb62
-rw-r--r--spec/support/shared_contexts/glfm/example_snapshot_fixtures.rb27
-rw-r--r--spec/support/shared_contexts/graphql/requests/packages_shared_context.rb4
-rw-r--r--spec/support/shared_contexts/markdown_golden_master_shared_examples.rb2
-rw-r--r--spec/support/shared_contexts/markdown_snapshot_shared_examples.rb65
-rw-r--r--spec/support/shared_contexts/navbar_structure_context.rb23
-rw-r--r--spec/support/shared_contexts/policies/project_policy_shared_context.rb13
-rw-r--r--spec/support/shared_contexts/projects/container_repository/cleanup_tags_service_shared_context.rb28
-rw-r--r--spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb2
-rw-r--r--spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb14
-rw-r--r--spec/support/shared_contexts/views/html_safe_render_shared_context.rb39
-rw-r--r--spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb25
-rw-r--r--spec/support/shared_examples/ci/edit_job_token_scope_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/controllers/concerns/web_hooks/integrations_hook_log_actions_shared_examples.rb47
-rw-r--r--spec/support/shared_examples/controllers/error_tracking_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/controllers/githubish_import_controller_shared_examples.rb27
-rw-r--r--spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb2
-rw-r--r--spec/support/shared_examples/features/board_sidebar_labels_examples.rb2
-rw-r--r--spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/features/content_editor_shared_examples.rb121
-rw-r--r--spec/support/shared_examples/features/deploy_token_shared_examples.rb21
-rw-r--r--spec/support/shared_examples/features/discussion_comments_shared_example.rb4
-rw-r--r--spec/support/shared_examples/features/manage_applications_shared_examples.rb92
-rw-r--r--spec/support/shared_examples/features/packages_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb12
-rw-r--r--spec/support/shared_examples/features/protected_branches_with_deploy_keys_examples.rb6
-rw-r--r--spec/support/shared_examples/features/rss_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/features/runners_shared_examples.rb44
-rw-r--r--spec/support/shared_examples/features/snippets_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/variable_list_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb11
-rw-r--r--spec/support/shared_examples/features/wiki/user_views_asciidoc_page_with_includes_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/finders/issues_finder_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/graphql/members_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/graphql/n_plus_one_query_examples.rb2
-rw-r--r--spec/support/shared_examples/graphql/resolvers/issuable_resolvers_shared_examples.rb99
-rw-r--r--spec/support/shared_examples/lib/banzai/filters/sanitization_filter_shared_examples.rb26
-rw-r--r--spec/support/shared_examples/lib/cache_helpers_shared_examples.rb139
-rw-r--r--spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb126
-rw-r--r--spec/support/shared_examples/lib/gitlab/cycle_analytics/deployment_metrics.rb2
-rw-r--r--spec/support/shared_examples/lib/gitlab/database/background_migration_job_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/lib/gitlab/diff_file_collections_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/lib/gitlab/sql/set_operator_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/lib/sentry/client_shared_examples.rb49
-rw-r--r--spec/support/shared_examples/models/chat_integration_shared_examples.rb51
-rw-r--r--spec/support/shared_examples/models/cluster_application_core_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb2
-rw-r--r--spec/support/shared_examples/models/concerns/counter_attribute_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/models/concerns/has_repository_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/label_note_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/members_notifications_shared_example.rb4
-rw-r--r--spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb12
-rw-r--r--spec/support/shared_examples/models/project_latest_successful_build_for_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/synthetic_note_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/models/update_project_statistics_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/models/wiki_shared_examples.rb162
-rw-r--r--spec/support/shared_examples/namespaces/traversal_scope_examples.rb16
-rw-r--r--spec/support/shared_examples/policies/project_policy_shared_examples.rb150
-rw-r--r--spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb263
-rw-r--r--spec/support/shared_examples/quick_actions/incident/timeline_quick_action_shared_examples.rb82
-rw-r--r--spec/support/shared_examples/quick_actions/issuable/time_tracking_quick_action_shared_examples.rb54
-rw-r--r--spec/support/shared_examples/quick_actions/merge_request/rebase_quick_action_shared_examples.rb11
-rw-r--r--spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb29
-rw-r--r--spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb1
-rw-r--r--spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb17
-rw-r--r--spec/support/shared_examples/requests/api/graphql/issuable_search_shared_examples.rb14
-rw-r--r--spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb7
-rw-r--r--spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/labels_api_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/requests/api/packages_shared_examples.rb7
-rw-r--r--spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb1
-rw-r--r--spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/api/resource_state_events_api_shared_examples.rb82
-rw-r--r--spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb1
-rw-r--r--spec/support/shared_examples/requests/api/snippets_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/applications_controller_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/requests/lfs_http_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/requests/projects/google_cloud/google_cloud_ff_examples.rb18
-rw-r--r--spec/support/shared_examples/requests/projects/google_cloud/google_cloud_role_examples.rb55
-rw-r--r--spec/support/shared_examples/requests/projects/google_cloud/google_oauth2_config_examples.rb22
-rw-r--r--spec/support/shared_examples/requests/projects/google_cloud/google_oauth2_token_examples.rb47
-rw-r--r--spec/support/shared_examples/requests/rack_attack_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/routing/resource_routing_shared_examples.rb12
-rw-r--r--spec/support/shared_examples/routing/wiki_routing_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/security_training_providers_importer.rb14
-rw-r--r--spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb38
-rw-r--r--spec/support/shared_examples/services/common_system_notes_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb20
-rw-r--r--spec/support/shared_examples/services/feature_flags/client_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/services/issuable/update_service_shared_examples.rb29
-rw-r--r--spec/support/shared_examples/services/issuable_links/create_links_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/issuable_links/destroyable_issuable_links_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/merge_request_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/onboarding_progress_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/packages_shared_examples.rb1
-rw-r--r--spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/services/snippets_shared_examples.rb5
-rw-r--r--spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/tasks/gitlab/uploads/migration_shared_examples.rb31
-rw-r--r--spec/support/shared_examples/uploaders/object_storage_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/users/migrate_records_to_ghost_user_service_shared_examples.rb39
-rw-r--r--spec/support_specs/database/without_check_constraint_spec.rb85
-rw-r--r--spec/support_specs/helpers/html_escaped_helpers_spec.rb43
-rw-r--r--spec/tasks/gitlab/db/truncate_legacy_tables_rake_spec.rb157
-rw-r--r--spec/tasks/gitlab/db/validate_config_rake_spec.rb2
-rw-r--r--spec/tasks/gitlab/snippets_rake_spec.rb2
-rw-r--r--spec/tasks/gitlab/uploads/migrate_rake_spec.rb150
-rw-r--r--spec/tasks/gitlab/usage_data_rake_spec.rb57
-rw-r--r--spec/tasks/rubocop_rake_spec.rb39
-rw-r--r--spec/tooling/danger/config_files_spec.rb91
-rw-r--r--spec/tooling/danger/datateam_spec.rb2
-rw-r--r--spec/tooling/danger/project_helper_spec.rb2
-rw-r--r--spec/uploaders/object_storage/cdn/google_cdn_spec.rb95
-rw-r--r--spec/uploaders/object_storage/cdn/google_ip_cache_spec.rb84
-rw-r--r--spec/uploaders/object_storage/cdn_spec.rb85
-rw-r--r--spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb4
-rw-r--r--spec/uploaders/packages/package_file_uploader_spec.rb71
-rw-r--r--spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb162
-rw-r--r--spec/validators/addressable_url_validator_spec.rb4
-rw-r--r--spec/views/admin/sessions/new.html.haml_spec.rb8
-rw-r--r--spec/views/dashboard/projects/_blank_state_welcome.html.haml_spec.rb62
-rw-r--r--spec/views/devise/sessions/new.html.haml_spec.rb4
-rw-r--r--spec/views/devise/shared/_signup_box.html.haml_spec.rb10
-rw-r--r--spec/views/groups/new.html.haml_spec.rb11
-rw-r--r--spec/views/groups/observability.html.haml_spec.rb18
-rw-r--r--spec/views/help/drawers.html.haml_spec.rb18
-rw-r--r--spec/views/help/instance_configuration.html.haml_spec.rb2
-rw-r--r--spec/views/layouts/_header_search.html.haml_spec.rb2
-rw-r--r--spec/views/layouts/_published_experiments.html.haml_spec.rb6
-rw-r--r--spec/views/layouts/fullscreen.html.haml_spec.rb41
-rw-r--r--spec/views/layouts/header/_new_dropdown.haml_spec.rb2
-rw-r--r--spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb6
-rw-r--r--spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb20
-rw-r--r--spec/views/notify/approved_merge_request_email.html.haml_spec.rb26
-rw-r--r--spec/views/notify/autodevops_disabled_email.text.erb_spec.rb2
-rw-r--r--spec/views/notify/change_in_merge_request_draft_status_email.html.haml_spec.rb17
-rw-r--r--spec/views/notify/change_in_merge_request_draft_status_email.text.erb_spec.rb14
-rw-r--r--spec/views/notify/import_issues_csv_email.html.haml_spec.rb61
-rw-r--r--spec/views/profiles/keys/_form.html.haml_spec.rb4
-rw-r--r--spec/views/profiles/preferences/show.html.haml_spec.rb4
-rw-r--r--spec/views/profiles/show.html.haml_spec.rb5
-rw-r--r--spec/views/projects/edit.html.haml_spec.rb56
-rw-r--r--spec/views/projects/imports/new.html.haml_spec.rb2
-rw-r--r--spec/views/projects/settings/merge_requests/show.html.haml_spec.rb78
-rw-r--r--spec/views/projects/tags/index.html.haml_spec.rb4
-rw-r--r--spec/views/shared/runners/_runner_details.html.haml_spec.rb4
-rw-r--r--spec/views/shared/web_hooks/_web_hook_disabled_alert.html.haml_spec.rb38
-rw-r--r--spec/workers/analytics/usage_trends/counter_job_worker_spec.rb34
-rw-r--r--spec/workers/bulk_imports/export_request_worker_spec.rb2
-rw-r--r--spec/workers/ci/build_finished_worker_spec.rb13
-rw-r--r--spec/workers/ci/job_artifacts/track_artifact_report_worker_spec.rb60
-rw-r--r--spec/workers/cleanup_container_repository_worker_spec.rb2
-rw-r--r--spec/workers/clusters/cleanup/project_namespace_worker_spec.rb2
-rw-r--r--spec/workers/concerns/application_worker_spec.rb2
-rw-r--r--spec/workers/concerns/cluster_agent_queue_spec.rb1
-rw-r--r--spec/workers/concerns/cluster_queue_spec.rb21
-rw-r--r--spec/workers/concerns/cronjob_queue_spec.rb4
-rw-r--r--spec/workers/concerns/gitlab/github_import/queue_spec.rb18
-rw-r--r--spec/workers/concerns/pipeline_background_queue_spec.rb21
-rw-r--r--spec/workers/concerns/pipeline_queue_spec.rb21
-rw-r--r--spec/workers/concerns/repository_check_queue_spec.rb4
-rw-r--r--spec/workers/concerns/waitable_worker_spec.rb3
-rw-r--r--spec/workers/disallow_two_factor_for_group_worker_spec.rb2
-rw-r--r--spec/workers/emails_on_push_worker_spec.rb2
-rw-r--r--spec/workers/every_sidekiq_worker_spec.rb6
-rw-r--r--spec/workers/gitlab/github_import/import_protected_branch_worker_spec.rb40
-rw-r--r--spec/workers/gitlab/github_import/import_release_attachments_worker_spec.rb48
-rw-r--r--spec/workers/gitlab/github_import/stage/import_attachments_worker_spec.rb49
-rw-r--r--spec/workers/gitlab/github_import/stage/import_notes_worker_spec.rb2
-rw-r--r--spec/workers/gitlab/github_import/stage/import_protected_branches_worker_spec.rb58
-rw-r--r--spec/workers/gitlab_service_ping_worker_spec.rb21
-rw-r--r--spec/workers/google_cloud/fetch_google_ip_list_worker_spec.rb15
-rw-r--r--spec/workers/groups/update_two_factor_requirement_for_members_worker_spec.rb39
-rw-r--r--spec/workers/issues/close_worker_spec.rb86
-rw-r--r--spec/workers/namespaces/onboarding_issue_created_worker_spec.rb4
-rw-r--r--spec/workers/namespaces/process_sync_events_worker_spec.rb24
-rw-r--r--spec/workers/packages/helm/extraction_worker_spec.rb2
-rw-r--r--spec/workers/pages_domain_ssl_renewal_cron_worker_spec.rb4
-rw-r--r--spec/workers/pages_worker_spec.rb2
-rw-r--r--spec/workers/process_commit_worker_spec.rb50
-rw-r--r--spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb92
-rw-r--r--spec/workers/projects/process_sync_events_worker_spec.rb8
-rw-r--r--spec/workers/purge_dependency_proxy_cache_worker_spec.rb4
-rw-r--r--spec/workers/releases/manage_evidence_worker_spec.rb2
-rw-r--r--spec/workers/remove_expired_members_worker_spec.rb23
-rw-r--r--spec/workers/repository_check/dispatch_worker_spec.rb4
-rw-r--r--spec/workers/ssh_keys/expired_notification_worker_spec.rb1
-rw-r--r--spec/workers/ssh_keys/expiring_soon_notification_worker_spec.rb1
-rw-r--r--spec/workers/users/deactivate_dormant_users_worker_spec.rb10
-rw-r--r--spec/workers/users/migrate_records_to_ghost_user_in_batches_worker_spec.rb53
-rw-r--r--storybook/config/preview.js7
-rw-r--r--storybook/package.json13
-rw-r--r--storybook/yarn.lock3552
-rw-r--r--tests.yml15
-rwxr-xr-xtooling/bin/find_changes10
-rwxr-xr-xtooling/bin/qa/run_qa_check45
-rw-r--r--tooling/config/CODEOWNERS.yml12
-rw-r--r--tooling/danger/config_files.rb43
-rw-r--r--tooling/danger/project_helper.rb4
-rw-r--r--vendor/gems/bundler-checksum/.gitlab-ci.yml28
-rw-r--r--vendor/gems/bundler-checksum/Gemfile5
-rw-r--r--vendor/gems/bundler-checksum/Gemfile.lock18
-rw-r--r--vendor/gems/bundler-checksum/LICENSE19
-rw-r--r--vendor/gems/bundler-checksum/README.md32
-rwxr-xr-xvendor/gems/bundler-checksum/bin/bundler-checksum6
-rw-r--r--vendor/gems/bundler-checksum/bundler-checksum.gemspec22
-rw-r--r--vendor/gems/bundler-checksum/lib/bundler-checksum.rb1
-rw-r--r--vendor/gems/bundler-checksum/lib/bundler/checksum.rb109
-rw-r--r--vendor/gems/bundler-checksum/lib/bundler/checksum/command.rb27
-rw-r--r--vendor/gems/bundler-checksum/lib/bundler/checksum/command/helper.rb28
-rw-r--r--vendor/gems/bundler-checksum/lib/bundler/checksum/command/init.rb66
-rw-r--r--vendor/gems/bundler-checksum/lib/bundler/checksum/command/verify.rb52
-rw-r--r--vendor/gems/bundler-checksum/lib/bundler/checksum/version.rb8
-rw-r--r--vendor/gems/bundler-checksum/test/project_with_checksum_lock/Gemfile11
-rw-r--r--vendor/gems/bundler-checksum/test/project_with_checksum_lock/Gemfile.checksum54
-rw-r--r--vendor/gems/bundler-checksum/test/project_with_checksum_lock/Gemfile.lock139
-rwxr-xr-xvendor/gems/bundler-checksum/test/project_with_checksum_lock/scripts/test15
-rw-r--r--vendor/gems/ipynbdiff/Gemfile.lock2
-rw-r--r--vendor/gems/ipynbdiff/ipynbdiff.gemspec2
-rw-r--r--vendor/gems/microsoft_graph_mailer/.gitlab-ci.yml32
-rw-r--r--vendor/gems/microsoft_graph_mailer/Gemfile5
-rw-r--r--vendor/gems/microsoft_graph_mailer/Gemfile.lock217
-rw-r--r--vendor/gems/microsoft_graph_mailer/LICENSE.txt21
-rw-r--r--vendor/gems/microsoft_graph_mailer/README.md104
-rw-r--r--vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer.rb16
-rw-r--r--vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer/client.rb51
-rw-r--r--vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer/delivery.rb49
-rw-r--r--vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer/railtie.rb11
-rw-r--r--vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer/version.rb5
-rw-r--r--vendor/gems/microsoft_graph_mailer/microsoft_graph_mailer.gemspec29
-rw-r--r--vendor/gems/microsoft_graph_mailer/spec/fixtures/attachments/gitlab.txt1
-rw-r--r--vendor/gems/microsoft_graph_mailer/spec/fixtures/attachments/gitlab_logo.pngbin0 -> 1528 bytes
-rw-r--r--vendor/gems/microsoft_graph_mailer/spec/lib/microsoft_graph_mailer/delivery_spec.rb128
-rw-r--r--vendor/gems/microsoft_graph_mailer/spec/lib/microsoft_graph_mailer/railtie_spec.rb140
-rw-r--r--vendor/gems/microsoft_graph_mailer/spec/lib/microsoft_graph_mailer_spec.rb29
-rw-r--r--vendor/gems/microsoft_graph_mailer/spec/spec_helper.rb60
-rw-r--r--vendor/gems/omniauth-azure-oauth2/Gemfile.lock13
-rw-r--r--vendor/gems/omniauth-azure-oauth2/lib/omniauth/strategies/azure_oauth2.rb4
-rw-r--r--vendor/gems/omniauth-azure-oauth2/omniauth-azure-oauth2.gemspec2
-rw-r--r--vendor/gems/omniauth-cas3/Gemfile.lock21
-rw-r--r--vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3.rb11
-rw-r--r--vendor/gems/omniauth-cas3/omniauth-cas3.gemspec2
-rw-r--r--vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3_spec.rb20
-rw-r--r--vendor/gems/omniauth-gitlab/Gemfile.lock11
-rw-r--r--vendor/gems/omniauth-gitlab/lib/omniauth/strategies/gitlab.rb8
-rw-r--r--vendor/gems/omniauth-gitlab/omniauth-gitlab.gemspec2
-rw-r--r--vendor/gems/omniauth-gitlab/spec/omniauth/strategies/gitlab_spec.rb22
-rw-r--r--vendor/gems/omniauth-google-oauth2/.gitlab-ci.yml28
-rw-r--r--vendor/gems/omniauth-google-oauth2/CHANGELOG.md354
-rw-r--r--vendor/gems/omniauth-google-oauth2/Gemfile5
-rw-r--r--vendor/gems/omniauth-google-oauth2/Gemfile.lock87
-rw-r--r--vendor/gems/omniauth-google-oauth2/README.md368
-rw-r--r--vendor/gems/omniauth-google-oauth2/Rakefile8
-rw-r--r--vendor/gems/omniauth-google-oauth2/examples/Gemfile8
-rw-r--r--vendor/gems/omniauth-google-oauth2/examples/config.ru120
-rw-r--r--vendor/gems/omniauth-google-oauth2/examples/omni_auth.rb37
-rw-r--r--vendor/gems/omniauth-google-oauth2/lib/omniauth-google-oauth2.rb3
-rw-r--r--vendor/gems/omniauth-google-oauth2/lib/omniauth/google_oauth2.rb3
-rw-r--r--vendor/gems/omniauth-google-oauth2/lib/omniauth/google_oauth2/version.rb7
-rw-r--r--vendor/gems/omniauth-google-oauth2/lib/omniauth/strategies/google_oauth2.rb254
-rw-r--r--vendor/gems/omniauth-google-oauth2/omniauth-google-oauth2.gemspec31
-rw-r--r--vendor/gems/omniauth-google-oauth2/spec/omniauth/strategies/google_oauth2_spec.rb850
-rw-r--r--vendor/gems/omniauth-google-oauth2/spec/spec_helper.rb4
-rw-r--r--vendor/gems/omniauth-salesforce/.gitlab-ci.yml28
-rwxr-xr-xvendor/gems/omniauth-salesforce/Gemfile12
-rw-r--r--vendor/gems/omniauth-salesforce/Gemfile.lock124
-rwxr-xr-xvendor/gems/omniauth-salesforce/Guardfile10
-rw-r--r--vendor/gems/omniauth-salesforce/LICENSE.md5
-rwxr-xr-xvendor/gems/omniauth-salesforce/README.md60
-rwxr-xr-xvendor/gems/omniauth-salesforce/Rakefile12
-rwxr-xr-xvendor/gems/omniauth-salesforce/lib/omniauth-salesforce.rb2
-rwxr-xr-xvendor/gems/omniauth-salesforce/lib/omniauth-salesforce/version.rb5
-rwxr-xr-xvendor/gems/omniauth-salesforce/lib/omniauth/strategies/salesforce.rb97
-rwxr-xr-xvendor/gems/omniauth-salesforce/omniauth-salesforce.gemspec24
-rwxr-xr-xvendor/gems/omniauth-salesforce/spec/omniauth/strategies/salesforce_spec.rb219
-rwxr-xr-xvendor/gems/omniauth-salesforce/spec/spec_helper.rb16
-rw-r--r--vendor/gems/omniauth_crowd/Gemfile.lock9
-rwxr-xr-xvendor/gems/omniauth_crowd/lib/omniauth/strategies/crowd.rb10
-rw-r--r--vendor/gems/omniauth_crowd/omniauth_crowd.gemspec2
-rwxr-xr-xvendor/gems/omniauth_crowd/spec/omniauth/strategies/crowd_spec.rb88
-rw-r--r--vendor/project_templates/cluster_management.tar.gzbin14184 -> 14040 bytes
-rw-r--r--workhorse/.gitignore1
-rw-r--r--workhorse/Makefile24
-rw-r--r--workhorse/gitaly_integration_test.go58
-rw-r--r--workhorse/go.mod20
-rw-r--r--workhorse/go.sum417
-rw-r--r--workhorse/internal/git/pktline.go59
-rw-r--r--workhorse/internal/git/pktline_test.go39
-rw-r--r--workhorse/internal/headers/content_headers.go10
-rw-r--r--workhorse/internal/lsif_transformer/parser/code_hover.go28
-rw-r--r--workhorse/internal/lsif_transformer/parser/code_hover_test.go48
-rw-r--r--workhorse/internal/redis/keywatcher.go230
-rw-r--r--workhorse/internal/redis/keywatcher_test.go103
-rw-r--r--workhorse/internal/senddata/contentprocessor/contentprocessor_test.go14
-rw-r--r--workhorse/internal/upload/destination/objectstore/uploader.go4
-rw-r--r--workhorse/internal/upstream/metrics_test.go2
-rw-r--r--workhorse/internal/upstream/routes.go46
-rw-r--r--workhorse/internal/upstream/upstream.go9
-rw-r--r--workhorse/internal/upstream/upstream_test.go4
-rw-r--r--workhorse/main.go7
-rw-r--r--workhorse/main_test.go4
-rw-r--r--workhorse/testdata/file.docxbin0 -> 6151 bytes
l---------workhorse/testdata/repo/group/test.git1
-rw-r--r--workhorse/upload_test.go11
-rw-r--r--yarn.lock440
6317 files changed, 218215 insertions, 90221 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml
index af2f1d88938..659ed2a0010 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -3,6 +3,7 @@ extends:
- plugin:@gitlab/i18n
- plugin:no-jquery/slim
- plugin:no-jquery/deprecated-3.4
+ - plugin:no-unsanitized/DOM
- ./tooling/eslint-config/conditionally_ignore.js
globals:
__webpack_public_path__: true
@@ -116,6 +117,14 @@ rules:
vue/multi-word-component-names: off
unicorn/prefer-dom-node-dataset:
- error
+ no-unsanitized/method:
+ - error
+ - escape:
+ methods: 'sanitize'
+ no-unsanitized/property:
+ - error
+ - escape:
+ methods: 'sanitize'
overrides:
- files:
- '{,ee/,jh/}spec/frontend*/**/*'
@@ -134,6 +143,8 @@ overrides:
message: 'Prefer explicit waitForPromises (or equivalent), or jest.runAllTimers (or equivalent) to vague setImmediate calls.'
- selector: ImportSpecifier[imported.name='GlSkeletonLoading']
message: 'Migrate to GlSkeletonLoader, or import GlDeprecatedSkeletonLoading.'
+ no-unsanitized/method: off
+ no-unsanitized/property: off
- files:
- 'config/**/*'
- 'scripts/**/*'
diff --git a/.gitignore b/.gitignore
index 0bd718f254b..234593b944e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -95,8 +95,6 @@ jsdoc/
webpack-dev-server.json
/.nvimrc
.solargraph.yml
-/tmp/matching_foss_tests.txt
-/tmp/matching_tests.txt
ee/changelogs/unreleased-ee
/sitespeed-result
tags.lock
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 882ae337c60..9c6b84d3931 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -35,17 +35,10 @@ workflow:
# they serve no purpose and will run anyway when the changes are merged.
- 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 merged result pipelines, set $QA_IMAGE, since $CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is only available for merged result pipelines.
- # AND
# 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:
- QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}"
RUBY_VERSION: "3.0"
- # For merged result pipelines, set $QA_IMAGE, since $CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is only available for merged result pipelines.
- - if: '($CI_MERGE_REQUEST_EVENT_TYPE == "merged_result" || $CI_MERGE_REQUEST_EVENT_TYPE == "merge_train")'
- variables:
- QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}"
# For merge requests running exclusively in Ruby 3.0
- if: '$CI_MERGE_REQUEST_LABELS =~ /pipeline:run-in-ruby3/'
variables:
@@ -74,7 +67,7 @@ variables:
RAILS_ENV: "test"
NODE_ENV: "test"
BUNDLE_WITHOUT: "production:development"
- BUNDLE_INSTALL_FLAGS: "--jobs=$(nproc) --retry=3 --quiet"
+ BUNDLE_INSTALL_FLAGS: "--jobs=$(nproc) --retry=3"
BUNDLE_FROZEN: "true"
# we override the max_old_space_size to prevent OOM errors
NODE_OPTIONS: --max_old_space_size=3584
@@ -97,6 +90,8 @@ variables:
RSPEC_PACKED_TESTS_MAPPING_PATH: crystalball/packed-mapping.json
RSPEC_PROFILING_FOLDER_PATH: rspec/profiling
FRONTEND_FIXTURES_MAPPING_PATH: crystalball/frontend_fixtures_mapping.json
+ RSPEC_CHANGED_FILES_PATH: rspec/changed_files.txt
+ RSPEC_MATCHING_TESTS_PATH: rspec/matching_tests.txt
RSPEC_LAST_RUN_RESULTS_FILE: rspec/rspec_last_run_results.txt
JUNIT_RESULT_FILE: rspec/junit_rspec.xml
JUNIT_RETRY_FILE: rspec/junit_rspec-retry.xml
@@ -122,11 +117,6 @@ variables:
BUILD_ASSETS_IMAGE: "true" # Set it to "false" to disable assets image building, used in `build-assets-image`
SIMPLECOV: "true"
- # For the default QA image, we use $CI_COMMIT_SHA as tag since it's always available and we override it for specific workflow.rules (see above)
- QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_COMMIT_SHA}"
- # Default latest tag for particular branch
- QA_IMAGE_BRANCH: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_COMMIT_REF_SLUG}"
-
REGISTRY_HOST: "registry.gitlab.com"
REGISTRY_GROUP: "gitlab-org"
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index 6019fe636a8..03fcfc4dd53 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -2,6 +2,24 @@
# project here: https://gitlab.com/gitlab-org/gitlab/-/project_members
# As described in https://docs.gitlab.com/ee/user/project/code_owners.html
+* @gitlab-org/maintainers/rails-backend @gitlab-org/maintainers/frontend @gitlab-org/maintainers/database @gl-quality/qe-maintainers @gitlab-org/delivery @gitlab-org/maintainers/cicd-templates @kwiebers @nolith @jacobvosmaer-gitlab
+
+CODEOWNERS @clefelhocz1 @timzallmann @cdu1 @wayne @dsatcher @sgoldstein @jeromezng @stanhu @susantacker @dianalogan @kpaizee @sselhorn
+docs/CODEOWNERS @clefelhocz1 @timzallmann @cdu1 @wayne @dsatcher @sgoldstein @jeromezng @stanhu @susantacker @dianalogan @kpaizee @sselhorn
+.gitlab/CODEOWNERS @clefelhocz1 @timzallmann @cdu1 @wayne @dsatcher @sgoldstein @jeromezng @stanhu @susantacker @dianalogan @kpaizee @sselhorn
+
+## Allows release tooling to update the Gitaly Version
+GITALY_SERVER_VERSION @project_278964_bot6 @gitlab-org/maintainers/rails-backend @gitlab-org/delivery
+
+## Files that are excluded from required approval
+/.gitlab/issue_templates/*.md
+/.gitlab/merge_request_templates/*.md
+/doc/*.md
+/doc/**/*.md
+/doc/**/*.png
+/data/deprecations/*.yml
+/data/removals/*.yml
+
^[Backend]
*.rb @gitlab-org/maintainers/rails-backend
*.rake @gitlab-org/maintainers/rails-backend
@@ -34,7 +52,6 @@
/.gitlab/ci/docs.gitlab-ci.yml @gl-quality/eng-prod @gl-docsteam
/.gitlab/ci/releases.gitlab-ci.yml @gl-quality/eng-prod @gitlab-org/delivery
/.gitlab/ci/reports.gitlab-ci.yml @gitlab-com/gl-security/appsec @gl-quality/eng-prod
-/.gitlab/CODEOWNERS @gl-quality/eng-prod
Dangerfile @gl-quality/eng-prod
/danger/ @gl-quality/eng-prod
/tooling/danger/ @gl-quality/eng-prod
@@ -57,11 +74,89 @@ Dangerfile @gl-quality/eng-prod
/ee/lib/ee/gitlab/auth/ldap/ @dblessing @mkozono
/lib/gitlab/auth/ldap/ @dblessing @mkozono
+^[Verify]
+/app/**/ci/ @gitlab-org/maintainers/cicd-verify
+/app/controllers/admin/jobs_controller.rb @gitlab-org/maintainers/cicd-verify
+/app/controllers/admin/runner_projects_controller.rb @gitlab-org/maintainers/cicd-verify
+/app/controllers/admin/runners_controller.rb @gitlab-org/maintainers/cicd-verify
+/app/controllers/projects/artifacts_controller.rb @gitlab-org/maintainers/cicd-verify
+/app/controllers/projects/build_artifacts_controller.rb @gitlab-org/maintainers/cicd-verify
+/app/controllers/projects/builds_controller.rb @gitlab-org/maintainers/cicd-verify
+/app/controllers/projects/jobs_controller.rb @gitlab-org/maintainers/cicd-verify
+/app/controllers/runner_setup_controller.rb @gitlab-org/maintainers/cicd-verify
+/app/controllers/projects/pipeline_schedules_controller.rb @gitlab-org/maintainers/cicd-verify
+/app/controllers/projects/pipelines_controller.rb @gitlab-org/maintainers/cicd-verify
+/app/controllers/projects/pipelines_settings_controller.rb @gitlab-org/maintainers/cicd-verify
+/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
+/app/models/generic_commit_status.rb @gitlab-org/maintainers/cicd-verify
+/app/models/namespace_ci_cd_setting.rb @gitlab-org/maintainers/cicd-verify
+/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/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
+/app/views/projects/pipeline_schedules/ @gitlab-org/maintainers/cicd-verify
+/app/views/projects/pipelines/ @gitlab-org/maintainers/cicd-verify
+/app/views/projects/triggers/ @gitlab-org/maintainers/cicd-verify
+/app/workers/build_hooks_worker.rb @gitlab-org/maintainers/cicd-verify
+/app/workers/build_queue_worker.rb @gitlab-org/maintainers/cicd-verify
+/app/workers/build_success_worker.rb @gitlab-org/maintainers/cicd-verify
+/app/workers/ci_platform_metrics_update_cron_worker.rb @gitlab-org/maintainers/cicd-verify
+/app/workers/create_pipeline_worker.rb @gitlab-org/maintainers/cicd-verify
+/app/workers/expire_build_artifacts_worker.rb @gitlab-org/maintainers/cicd-verify
+/app/workers/pipeline_hooks_worker.rb @gitlab-org/maintainers/cicd-verify
+/app/workers/pipeline_metrics_worker.rb @gitlab-org/maintainers/cicd-verify
+/app/workers/pipeline_notification_worker.rb @gitlab-org/maintainers/cicd-verify
+/app/workers/pipeline_process_worker.rb @gitlab-org/maintainers/cicd-verify
+/app/workers/pipeline_schedule_worker.rb @gitlab-org/maintainers/cicd-verify
+/app/workers/run_pipeline_schedule_worker.rb @gitlab-org/maintainers/cicd-verify
+/app/workers/stuck_ci_jobs_worker.rb @gitlab-org/maintainers/cicd-verify
+/app/workers/update_external_pull_requests_worker.rb @gitlab-org/maintainers/cicd-verify
+/lib/**/ci/ @gitlab-org/maintainers/cicd-verify
+/lib/api/commit_statuses.rb @gitlab-org/maintainers/cicd-verify
+/ee/app/**/ci/ @gitlab-org/maintainers/cicd-verify
+/ee/app/**/merge_trains/ @gitlab-org/maintainers/cicd-verify
+/ee/app/models/merge_train.rb @gitlab-org/maintainers/cicd-verify
+/ee/app/finders/merge_trains_finder.rb @gitlab-org/maintainers/cicd-verify
+/ee/app/services/auto_merge/add_to_merge_train_when_pipeline_succeeds_service.rb @gitlab-org/maintainers/cicd-verify
+/ee/app/services/auto_merge/merge_train_service.rb @gitlab-org/maintainers/cicd-verify
+/ee/app/services/system_notes/merge_train_service.rb @gitlab-org/maintainers/cicd-verify
+/ee/app/controllers/ee/admin/runners_controller.rb @gitlab-org/maintainers/cicd-verify
+/ee/app/controllers/ee/projects/pipelines_controller.rb @gitlab-org/maintainers/cicd-verify
+/ee/app/controllers/projects/pipelines/ @gitlab-org/maintainers/cicd-verify
+/ee/app/controllers/projects/subscriptions_controller.rb @gitlab-org/maintainers/cicd-verify
+/ee/app/models/merge_train.rb @gitlab-org/maintainers/cicd-verify
+/ee/app/helpers/ee/projects/pipeline_helper.rb @gitlab-org/maintainers/cicd-verify
+/ee/app/views/ci_minutes_usage_mailer/ @gitlab-org/maintainers/cicd-verify
+/ee/app/views/projects/pipelines/ @gitlab-org/maintainers/cicd-verify
+/ee/app/views/projects/settings/ci_cd/ @gitlab-org/maintainers/cicd-verify
+/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
+/**/javascripts/jobs/ @pburdette @jivanvl
+/**/javascripts/pipelines/ @pburdette @f_caplette @jivanvl @mfluharty @bsandlin @mgandres
+/app/assets/javascripts/pipeline_new/ @pburdette @f_caplette @jivanvl @mfluharty @bsandlin @mgandres
+/app/assets/javascripts/ci_lint/ @f_caplette @bsandlin @mgandres
+/app/assets/javascripts/ci_variable_list/ @pburdette @f_caplette @jivanvl @mfluharty @bsandlin @mgandres
+/app/assets/javascripts/pipeline_schedules/ @pburdette @jivanvl
+/app/assets/javascripts/pipeline_editor/ @f_caplette @bsandlin @mgandres
+/ee/app/assets/javascripts/ci_minutes_usage/ @pburdette @jivanvl
+/ee/app/assets/javascripts/usage_quotas/ci_minutes_usage/ @pburdette @jivanvl
+/ee/app/assets/javascripts/usage_quotas/pipelines/ @pburdette @jivanvl
+/ee/app/assets/javascripts/reports/ @mfluharty
+
^[Templates]
/lib/gitlab/ci/templates/ @gitlab-org/maintainers/cicd-templates
/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml @DylanGriffith @mayra-cabrera @tkuah
/lib/gitlab/ci/templates/Security/ @gonzoyumo @twoodham @sethgitlab @thiagocsf
-/lib/gitlab/ci/templates/Security/Container-Scanning.*.yml @gitlab-org/protect/container-security-backend
+/lib/gitlab/ci/templates/Security/Container-Scanning.*.yml @gitlab-org/govern/security-policies-backend
^[Project Alias]
/ee/app/models/project_alias.rb @patrickbajao
@@ -70,18 +165,18 @@ Dangerfile @gl-quality/eng-prod
# Secure & Threat Management ownership delineation
# https://about.gitlab.com/handbook/engineering/development/threat-management/delineate-secure-threat-management.html#technical-boundaries
^[Threat Insights]
-/app/finders/security/ @gitlab-org/secure/threat-insights-backend-team
-/app/models/vulnerability.rb @gitlab-org/secure/threat-insights-backend-team
-/ee/app/finders/security/ @gitlab-org/secure/threat-insights-backend-team
-/ee/app/models/security/ @gitlab-org/secure/threat-insights-backend-team
-/ee/app/models/vulnerabilities/ @gitlab-org/secure/threat-insights-backend-team
-/ee/app/policies/vulnerabilities/ @gitlab-org/secure/threat-insights-backend-team
-/ee/app/policies/vulnerability*.rb @gitlab-org/secure/threat-insights-backend-team
-/ee/app/presenters/projects/security/ @gitlab-org/secure/threat-insights-backend-team
-/ee/lib/api/vulnerabilit*.rb @gitlab-org/secure/threat-insights-backend-team
-/ee/spec/policies/vulnerabilities/ @gitlab-org/secure/threat-insights-backend-team
-/ee/spec/policies/vulnerability*.rb @gitlab-org/secure/threat-insights-backend-team
-/ee/spec/presenters/projects/security/ @gitlab-org/secure/threat-insights-backend-team
+/app/finders/security/ @gitlab-org/govern/threat-insights-backend-team
+/app/models/vulnerability.rb @gitlab-org/govern/threat-insights-backend-team
+/ee/app/finders/security/ @gitlab-org/govern/threat-insights-backend-team
+/ee/app/models/security/ @gitlab-org/govern/threat-insights-backend-team
+/ee/app/models/vulnerabilities/ @gitlab-org/govern/threat-insights-backend-team
+/ee/app/policies/vulnerabilities/ @gitlab-org/govern/threat-insights-backend-team
+/ee/app/policies/vulnerability*.rb @gitlab-org/govern/threat-insights-backend-team
+/ee/app/presenters/projects/security/ @gitlab-org/govern/threat-insights-backend-team
+/ee/lib/api/vulnerabilit*.rb @gitlab-org/govern/threat-insights-backend-team
+/ee/spec/policies/vulnerabilities/ @gitlab-org/govern/threat-insights-backend-team
+/ee/spec/policies/vulnerability*.rb @gitlab-org/govern/threat-insights-backend-team
+/ee/spec/presenters/projects/security/ @gitlab-org/govern/threat-insights-backend-team
^[Secure]
/ee/lib/gitlab/ci/parsers/license_compliance/ @gitlab-org/secure/composition-analysis-be
@@ -93,23 +188,23 @@ Dangerfile @gl-quality/eng-prod
/ee/app/services/app_sec/dast/ @gitlab-org/secure/dynamic-analysis-be
^[Container Security]
-/ee/app/views/projects/threat_monitoring/** @gitlab-org/protect/container-security-frontend
-/ee/app/views/projects/security/policies/** @gitlab-org/protect/container-security-frontend
-/ee/spec/views/projects/security/policies/** @gitlab-org/protect/container-security-frontend
-/ee/app/assets/javascripts/pages/projects/threat_monitoring/** @gitlab-org/protect/container-security-frontend
-/ee/app/assets/javascripts/threat_monitoring/** @gitlab-org/protect/container-security-frontend
-/ee/spec/frontend/threat_monitoring/** @gitlab-org/protect/container-security-frontend
+/ee/app/views/projects/threat_monitoring/** @gitlab-org/govern/security-policies-frontend
+/ee/app/views/projects/security/policies/** @gitlab-org/govern/security-policies-frontend
+/ee/spec/views/projects/security/policies/** @gitlab-org/govern/security-policies-frontend
+/ee/app/assets/javascripts/pages/projects/threat_monitoring/** @gitlab-org/govern/security-policies-frontend
+/ee/app/assets/javascripts/threat_monitoring/** @gitlab-org/govern/security-policies-frontend
+/ee/spec/frontend/threat_monitoring/** @gitlab-org/govern/security-policies-frontend
-/ee/app/controllers/projects/threat_monitoring_controller.rb @gitlab-org/protect/container-security-backend
-/ee/spec/controllers/projects/threat_monitoring_controller_spec.rb @gitlab-org/protect/container-security-backend
-/ee/app/controllers/projects/security/policies_controller.rb @gitlab-org/protect/container-security-backend
-/ee/spec/requests/projects/security/policies_controller_spec.rb @gitlab-org/protect/container-security-backend
-/ee/app/models/security/orchestration_policy_configuration.rb @gitlab-org/protect/container-security-backend
-/ee/spec/models/security/orchestration_policy_configuration_spec.rb @gitlab-org/protect/container-security-backend
-/app/models/clusters/applications/cilium.rb @gitlab-org/protect/container-security-backend
-/spec/models/clusters/applications/cilium_spec.rb @gitlab-org/protect/container-security-backend
-/ee/app/services/security/orchestration/** @gitlab-org/protect/container-security-backend
-/ee/spec/services/security/orchestration/** @gitlab-org/protect/container-security-backend
+/ee/app/controllers/projects/threat_monitoring_controller.rb @gitlab-org/govern/security-policies-backend
+/ee/spec/controllers/projects/threat_monitoring_controller_spec.rb @gitlab-org/govern/container-security-backend
+/ee/app/controllers/projects/security/policies_controller.rb @gitlab-org/govern/security-policies-backend
+/ee/spec/requests/projects/security/policies_controller_spec.rb @gitlab-org/govern/security-policies-backend
+/ee/app/models/security/orchestration_policy_configuration.rb @gitlab-org/govern/security-policies-backend
+/ee/spec/models/security/orchestration_policy_configuration_spec.rb @gitlab-org/govern/security-policies-backend
+/app/models/clusters/applications/cilium.rb @gitlab-org/govern/security-policies-backend
+/spec/models/clusters/applications/cilium_spec.rb @gitlab-org/govern/security-policies-backend
+/ee/app/services/security/orchestration/** @gitlab-org/govern/security-policies-backend
+/ee/spec/services/security/orchestration/** @gitlab-org/govern/security-policies-backend
^[Code Owners]
/ee/lib/gitlab/code_owners.rb @reprazent @kerrizor @garyh
@@ -156,18 +251,56 @@ Dangerfile @gl-quality/eng-prod
^[Growth Experiments]
/app/experiments/ @gitlab-org/growth/experiment-devs
+/spec/experiments/ @gitlab-org/growth/experiment-devs
/app/models/experiment.rb @gitlab-org/growth/experiment-devs
+/spec/models/experiment.rb @gitlab-org/growth/experiment-devs
/app/models/experiment_subject.rb @gitlab-org/growth/experiment-devs
+/spec/models/experiment_subject.rb @gitlab-org/growth/experiment-devs
/app/models/experiment_user.rb @gitlab-org/growth/experiment-devs
+/spec/models/experiment_user.rb @gitlab-org/growth/experiment-devs
/app/workers/experiments/ @gitlab-org/growth/experiment-devs
+/spec/workers/experiments/ @gitlab-org/growth/experiment-devs
+/config/initializers/gitlab_experiment.rb @gitlab-org/growth/experiment-devs
/config/feature_flags/experiment/ @gitlab-org/growth/experiment-devs
/ee/config/feature_flags/experiment/ @gitlab-org/growth/experiment-devs
/ee/lib/api/experiments.rb @gitlab-org/growth/experiment-devs
+/ee/spec/requests/api/experiments_spec.rb @gitlab-org/growth/experiment-devs
/ee/lib/ee/api/entities/experiment.rb @gitlab-org/growth/experiment-devs
+/ee/spec/lib/ee/api/entities/experiment_spec.rb @gitlab-org/growth/experiment-devs
/lib/gitlab/experimentation/ @gitlab-org/growth/experiment-devs
+/spec/lib/gitlab/experimentation/ @gitlab-org/growth/experiment-devs
/lib/gitlab/experimentation.rb @gitlab-org/growth/experiment-devs
+/spec/lib/gitlab/experimentation_spec.rb @gitlab-org/growth/experiment-devs
/lib/gitlab/experimentation_logger.rb @gitlab-org/growth/experiment-devs
-/ee/spec/requests/api/experiments_spec.rb @gitlab-org/growth/experiment-devs
+
+^[Growth]
+/ee/app/workers/onboarding/ @gitlab-org/growth/engineers
+/ee/spec/workers/onboarding/ @gitlab-org/growth/engineers
+/app/models/onboarding/ @gitlab-org/growth/engineers
+/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/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
+/spec/workers/namespaces/in_product_marketing_emails_worker_spec.rb @gitlab-org/growth/engineers
+/ee/app/workers/ee/namespaces/in_product_marketing_emails_worker.rb @gitlab-org/growth/engineers
+/ee/spec/workers/ee/namespaces/in_product_marketing_emails_worker_spec.rb @gitlab-org/growth/engineers
+/app/models/users/in_product_marketing_email.rb @gitlab-org/growth/engineers
+/spec/models/users/in_product_marketing_email_spec.rb @gitlab-org/growth/engineers
+/app/services/namespaces/in_product_marketing_emails_service.rb @gitlab-org/growth/engineers
+/spec/services/namespaces/in_product_marketing_emails_service_spec.rb @gitlab-org/growth/engineers
+/ee/app/services/ee/namespaces/in_product_marketing_emails_service.rb @gitlab-org/growth/engineers
+/ee/spec/services/namespaces/in_product_marketing_emails_service_spec.rb @gitlab-org/growth/engineers
+/app/workers/projects/record_target_platforms_worker.rb @gitlab-org/growth/engineers
+/spec/workers/projects/record_target_platforms_worker_spec.rb @gitlab-org/growth/engineers
+/ee/app/controllers/groups/feature_discovery_moments_controller.rb @gitlab-org/growth/engineers
+/ee/spec/requests/groups/feature_discovery_moments_spec.rb @gitlab-org/growth/engineers
^[Legal]
/config/dependency_decisions.yml @gitlab-org/legal-reviewers
@@ -200,10 +333,10 @@ ee/lib/ee/gitlab/checks/** @proglottis @toon @zj-gitlab
lib/gitlab/checks/** @proglottis @toon @zj-gitlab
^[Documentation Directories]
-.markdownlint.yml @marcel.amirault @eread @aqualls @dianalogan
-/doc/.markdownlint @marcel.amirault @eread @aqualls @dianalogan
+.markdownlint.yml @marcel.amirault @eread @aqualls @dianalogan @kpaizee
/doc/ @gl-docsteam
-/doc/.vale/ @marcel.amirault @eread @aqualls @dianalogan
+/doc/.markdownlint/ @marcel.amirault @eread @aqualls @dianalogan @kpaizee
+/doc/.vale/ @marcel.amirault @eread @aqualls @dianalogan @kpaizee
^[Documentation Pages]
/doc/administration/application_settings_cache.md @sselhorn
@@ -212,8 +345,9 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/administration/audit_reports.md @eread
/doc/administration/auditor_users.md @eread
/doc/administration/auth/ @eread
+/doc/administration/auth/ldap/ @eread
/doc/administration/cicd.md @marcel.amirault
-/doc/administration/clusters/kas.md @sselhorn
+/doc/administration/clusters/ @phillipwells
/doc/administration/compliance.md @eread
/doc/administration/configure.md @axil
/doc/administration/consul.md @axil
@@ -224,74 +358,81 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/administration/feature_flags.md @axil
/doc/administration/file_hooks.md @aqualls
/doc/administration/geo/ @axil
+/doc/administration/geo/disaster_recovery/ @axil
+/doc/administration/geo/disaster_recovery/runbooks/ @axil
+/doc/administration/geo/replication/ @axil
+/doc/administration/geo/secondary_proxy/ @axil
+/doc/administration/geo/setup/ @axil
/doc/administration/git_protocol.md @aqualls
/doc/administration/gitaly/ @eread
/doc/administration/housekeeping.md @axil
+/doc/administration/inactive_project_deletion.md @eread
/doc/administration/incoming_email.md @msedlakjakubowski
/doc/administration/index.md @axil
/doc/administration/instance_limits.md @axil
-/doc/administration/instance_review.md @kpaizee
+/doc/administration/instance_review.md @phillipwells
/doc/administration/integration/kroki.md @msedlakjakubowski
-/doc/administration/integration/mailgun.md @kpaizee
+/doc/administration/integration/mailgun.md @msedlakjakubowski
/doc/administration/integration/plantuml.md @aqualls
-/doc/administration/integration/terminal.md @kpaizee
+/doc/administration/integration/terminal.md @ashrafkhamis
/doc/administration/invalidate_markdown_cache.md @msedlakjakubowski
/doc/administration/issue_closing_pattern.md @aqualls
/doc/administration/job_artifacts.md @marcel.amirault
/doc/administration/job_logs.md @sselhorn
-/doc/administration/lfs/index.md @aqualls
+/doc/administration/lfs/ @aqualls
/doc/administration/libravatar.md @axil
/doc/administration/load_balancer.md @axil
/doc/administration/logs/index.md @msedlakjakubowski
-/doc/administration/maintenance_mode/index.md @axil
+/doc/administration/logs/log_parsing.md @axil
+/doc/administration/logs/tracing_correlation_id.md @axil
+/doc/administration/maintenance_mode/ @axil
/doc/administration/merge_request_diffs.md @aqualls
/doc/administration/monitoring/ @msedlakjakubowski
+/doc/administration/monitoring/gitlab_self_monitoring_project/ @msedlakjakubowski
+/doc/administration/monitoring/ip_allowlist.md @sselhorn
+/doc/administration/monitoring/performance/ @msedlakjakubowski
+/doc/administration/monitoring/prometheus/ @msedlakjakubowski
/doc/administration/monitoring/prometheus/index.md @axil
+/doc/administration/monitoring/prometheus/web_exporter.md @sselhorn
/doc/administration/nfs.md @axil
/doc/administration/object_storage.md @axil
/doc/administration/operations/ @axil
-/doc/administration/operations/moving_repositories.md @eread
/doc/administration/operations/fast_ssh_key_lookup.md @aqualls
-/doc/administration/operations/sidekiq_memory_killer.md @sselhorn
+/doc/administration/operations/moving_repositories.md @eread
/doc/administration/package_information/ @axil
/doc/administration/packages/ @claytoncornell
-/doc/administration/pages/index.md @aqualls
-/doc/administration/pages/source.md @aqualls
+/doc/administration/pages/ @aqualls
/doc/administration/polling.md @axil
/doc/administration/postgresql/ @aqualls
-/doc/administration/pseudonymizer.md @axil
/doc/administration/raketasks/ @axil
/doc/administration/raketasks/ldap.md @eread
/doc/administration/raketasks/praefect.md @eread
/doc/administration/read_only_gitlab.md @axil
/doc/administration/redis/ @axil
/doc/administration/reference_architectures/ @axil
-/doc/administration/reply_by_email_postfix_setup.md @axil
/doc/administration/reply_by_email.md @msedlakjakubowski
+/doc/administration/reply_by_email_postfix_setup.md @axil
/doc/administration/repository_checks.md @eread
/doc/administration/repository_storage_paths.md @eread
/doc/administration/repository_storage_types.md @eread
/doc/administration/restart_gitlab.md @axil
/doc/administration/server_hooks.md @eread
-/doc/administration/sidekiq_health_check.md @axil
-/doc/administration/sidekiq.md @axil
+/doc/administration/sidekiq/ @axil
/doc/administration/smime_signing_email.md @axil
-/doc/administration/snippets/index.md @aqualls
+/doc/administration/snippets/ @aqualls
/doc/administration/static_objects_external_storage.md @aqualls
-/doc/administration/system_hooks.md @kpaizee
-/doc/administration/terraform_state.md @sselhorn
+/doc/administration/system_hooks.md @ashrafkhamis
+/doc/administration/terraform_state.md @phillipwells
/doc/administration/timezone.md @axil
/doc/administration/troubleshooting/ @axil
-/doc/administration/troubleshooting/elasticsearch.md @sselhorn
/doc/administration/troubleshooting/postgresql.md @aqualls
/doc/administration/uploads.md @axil
/doc/administration/user_settings.md @eread
-/doc/administration/whats-new.md @kpaizee
-/doc/administration/wikis/index.md @aqualls
+/doc/administration/wikis/ @aqualls
/doc/api/access_requests.md @eread
/doc/api/admin_sidekiq_queues.md @axil
/doc/api/alert_management_alerts.md @msedlakjakubowski
-/doc/api/api_resources.md @kpaizee
+/doc/api/api_resources.md @ashrafkhamis
/doc/api/appearance.md @eread
/doc/api/applications.md @eread
/doc/api/audit_events.md @eread
@@ -299,41 +440,40 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/award_emoji.md @msedlakjakubowski
/doc/api/boards.md @msedlakjakubowski
/doc/api/branches.md @aqualls
-/doc/api/broadcast_messages.md @kpaizee
+/doc/api/broadcast_messages.md @phillipwells
/doc/api/bulk_imports.md @eread
-/doc/api/cluster_agents.md @sselhorn
+/doc/api/cluster_agents.md @phillipwells
/doc/api/commits.md @aqualls
/doc/api/container_registry.md @claytoncornell
-/doc/api/custom_attributes.md @kpaizee
+/doc/api/custom_attributes.md @ashrafkhamis
/doc/api/dependencies.md @rdickenson
/doc/api/dependency_proxy.md @claytoncornell
/doc/api/deploy_keys.md @rdickenson
/doc/api/deploy_tokens.md @rdickenson
/doc/api/deployments.md @rdickenson
/doc/api/discussions.md @aqualls
-/doc/api/dora/metrics.md @fneill
-/doc/api/dora4_project_analytics.md @fneill
+/doc/api/dora/ @fneill
/doc/api/environments.md @rdickenson
/doc/api/epic_issues.md @msedlakjakubowski
/doc/api/epic_links.md @msedlakjakubowski
/doc/api/epics.md @msedlakjakubowski
/doc/api/error_tracking.md @msedlakjakubowski
/doc/api/events.md @eread
-/doc/api/experiments.md @kpaizee
+/doc/api/experiments.md @phillipwells
/doc/api/feature_flag_specs.md @rdickenson
/doc/api/feature_flag_user_lists.md @rdickenson
/doc/api/feature_flags.md @rdickenson
/doc/api/features.md @rdickenson
/doc/api/freeze_periods.md @rdickenson
/doc/api/geo_nodes.md @axil
-/doc/api/graphql/ @kpaizee
+/doc/api/graphql/ @ashrafkhamis
/doc/api/graphql/custom_emoji.md @msedlakjakubowski
/doc/api/graphql/sample_issue_boards.md @msedlakjakubowski
/doc/api/group_access_tokens.md @eread
/doc/api/group_activity_analytics.md @fneill
/doc/api/group_badges.md @fneill
/doc/api/group_boards.md @msedlakjakubowski
-/doc/api/group_clusters.md @sselhorn
+/doc/api/group_clusters.md @phillipwells
/doc/api/group_import_export.md @eread
/doc/api/group_iterations.md @msedlakjakubowski
/doc/api/group_labels.md @msedlakjakubowski
@@ -346,29 +486,30 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/group_wikis.md @aqualls
/doc/api/groups.md @fneill
/doc/api/import.md @eread
-/doc/api/index.md @kpaizee
-/doc/api/instance_clusters.md @sselhorn
+/doc/api/index.md @ashrafkhamis
+/doc/api/instance_clusters.md @phillipwells
/doc/api/instance_level_ci_variables.md @marcel.amirault
-/doc/api/integrations.md @kpaizee
-/doc/api/invitations.md @kpaizee
+/doc/api/integrations.md @ashrafkhamis
+/doc/api/invitations.md @phillipwells
/doc/api/issue_links.md @msedlakjakubowski
-/doc/api/issues_statistics.md @msedlakjakubowski
/doc/api/issues.md @msedlakjakubowski
+/doc/api/issues_statistics.md @msedlakjakubowski
/doc/api/iterations.md @msedlakjakubowski
/doc/api/job_artifacts.md @marcel.amirault
/doc/api/jobs.md @marcel.amirault
/doc/api/keys.md @aqualls
/doc/api/labels.md @msedlakjakubowski
-/doc/api/license.md @kpaizee
+/doc/api/license.md @fneill
/doc/api/linked_epics.md @msedlakjakubowski
/doc/api/lint.md @marcel.amirault
-/doc/api/managed_licenses.md @kpaizee
-/doc/api/markdown.md @aqualls
+/doc/api/managed_licenses.md @fneill
+/doc/api/markdown.md @msedlakjakubowski
/doc/api/members.md @eread
/doc/api/merge_request_approvals.md @aqualls
/doc/api/merge_request_context_commits.md @aqualls
/doc/api/merge_requests.md @aqualls
/doc/api/merge_trains.md @marcel.amirault
+/doc/api/metadata.md @ashrafkhamis
/doc/api/metrics_dashboard_annotations.md @msedlakjakubowski
/doc/api/metrics_user_starred_dashboards.md @msedlakjakubowski
/doc/api/milestones.md @msedlakjakubowski
@@ -376,11 +517,11 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/notes.md @msedlakjakubowski
/doc/api/notification_settings.md @msedlakjakubowski
/doc/api/oauth2.md @eread
-/doc/api/openapi/openapi_interactive.md @kpaizee
+/doc/api/openapi/ @ashrafkhamis
/doc/api/packages.md @claytoncornell
/doc/api/packages/ @claytoncornell
-/doc/api/pages_domains.md @aqualls
/doc/api/pages.md @aqualls
+/doc/api/pages_domains.md @aqualls
/doc/api/personal_access_tokens.md @eread
/doc/api/pipeline_schedules.md @marcel.amirault
/doc/api/pipeline_triggers.md @marcel.amirault
@@ -389,7 +530,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/project_access_tokens.md @eread
/doc/api/project_aliases.md @aqualls
/doc/api/project_badges.md @aqualls
-/doc/api/project_clusters.md @sselhorn
+/doc/api/project_clusters.md @phillipwells
/doc/api/project_import_export.md @aqualls
/doc/api/project_level_variables.md @marcel.amirault
/doc/api/project_relations_export.md @eread
@@ -402,8 +543,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/protected_branches.md @aqualls
/doc/api/protected_environments.md @rdickenson
/doc/api/protected_tags.md @aqualls
-/doc/api/releases/index.md @rdickenson
-/doc/api/releases/links.md @rdickenson
+/doc/api/releases/ @rdickenson
/doc/api/remote_mirrors.md @aqualls
/doc/api/repositories.md @aqualls
/doc/api/repository_files.md @aqualls
@@ -425,7 +565,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/statistics.md @eread
/doc/api/status_checks.md @eread
/doc/api/suggestions.md @aqualls
-/doc/api/system_hooks.md @kpaizee
+/doc/api/system_hooks.md @ashrafkhamis
/doc/api/tags.md @aqualls
/doc/api/templates/dockerfiles.md @aqualls
/doc/api/templates/gitignores.md @aqualls
@@ -435,202 +575,244 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/api/topics.md @fneill
/doc/api/usage_data.md @claytoncornell
/doc/api/users.md @eread
-/doc/api/version.md @kpaizee
+/doc/api/version.md @ashrafkhamis
/doc/api/visual_review_discussions.md @marcel.amirault
/doc/api/vulnerabilities.md @claytoncornell
/doc/api/vulnerability_exports.md @claytoncornell
/doc/api/vulnerability_findings.md @claytoncornell
/doc/api/wikis.md @aqualls
-/doc/architecture/blueprints/container_registry_metadata_database/index.md @claytoncornell
+/doc/architecture/blueprints/container_registry_metadata_database/ @claytoncornell
/doc/architecture/blueprints/database/scalability/patterns/ @aqualls
-/doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md @sselhorn
-/doc/ci/caching/index.md @marcel.amirault
-/doc/ci/chatops/index.md @sselhorn
+/doc/architecture/blueprints/database_scaling/ @aqualls
+/doc/architecture/blueprints/gitlab_to_kubernetes_communication/ @phillipwells
+/doc/ci/ @marcel.amirault
+/doc/ci/caching/ @marcel.amirault
+/doc/ci/chatops/ @phillipwells
/doc/ci/ci_cd_for_external_repos/ @marcel.amirault
-/doc/ci/cloud_deployment/ecs/quick_start_guide.md @rdickenson
-/doc/ci/cloud_deployment/index.md @rdickenson
+/doc/ci/cloud_deployment/ @rdickenson
+/doc/ci/cloud_deployment/ecs/ @rdickenson
/doc/ci/cloud_services/ @marcel.amirault
-/doc/ci/directed_acyclic_graph/index.md @marcel.amirault
+/doc/ci/cloud_services/aws/ @marcel.amirault
+/doc/ci/cloud_services/azure/ @marcel.amirault
+/doc/ci/cloud_services/google_cloud/ @marcel.amirault
+/doc/ci/directed_acyclic_graph/ @marcel.amirault
/doc/ci/docker/index.md @marcel.amirault
/doc/ci/docker/using_docker_build.md @marcel.amirault
/doc/ci/docker/using_docker_images.md @sselhorn
/doc/ci/docker/using_kaniko.md @marcel.amirault
-/doc/ci/enable_or_disable_ci.md @marcel.amirault
/doc/ci/environments/ @rdickenson
-/doc/ci/examples/authenticating-with-hashicorp-vault/index.md @marcel.amirault
-/doc/ci/examples/deployment/composer-npm-deploy.md @rdickenson
-/doc/ci/examples/deployment/index.md @rdickenson
-/doc/ci/examples/end_to_end_testing_webdriverio/index.md @marcel.amirault
+/doc/ci/examples/authenticating-with-hashicorp-vault/ @marcel.amirault
+/doc/ci/examples/deployment/ @rdickenson
+/doc/ci/examples/end_to_end_testing_webdriverio/ @marcel.amirault
/doc/ci/examples/index.md @marcel.amirault
-/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md @marcel.amirault
+/doc/ci/examples/laravel_with_gitlab_and_envoy/ @marcel.amirault
/doc/ci/examples/php.md @marcel.amirault
/doc/ci/examples/semantic-release.md @claytoncornell
-/doc/ci/git_submodules.md @marcel.amirault
-/doc/ci/index.md @marcel.amirault
-/doc/ci/interactive_web_terminal/index.md @sselhorn
-/doc/ci/introduction/index.md @marcel.amirault
-/doc/ci/jobs/ci_job_token.md @marcel.amirault
-/doc/ci/jobs/index.md @marcel.amirault
-/doc/ci/jobs/job_control.md @marcel.amirault
-/doc/ci/large_repositories/index.md @sselhorn
-/doc/ci/lint.md @marcel.amirault
-/doc/ci/metrics_reports.md @marcel.amirault
-/doc/ci/migration/circleci.md @marcel.amirault
-/doc/ci/migration/jenkins.md @marcel.amirault
-/doc/ci/pipeline_editor/index.md @marcel.amirault
+/doc/ci/interactive_web_terminal/ @sselhorn
+/doc/ci/introduction/ @marcel.amirault
+/doc/ci/jobs/ @marcel.amirault
+/doc/ci/large_repositories/ @sselhorn
+/doc/ci/migration/ @marcel.amirault
+/doc/ci/pipeline_editor/ @marcel.amirault
/doc/ci/pipelines/ @marcel.amirault
-/doc/ci/quick_start/index.md @marcel.amirault
-/doc/ci/resource_groups/index.md @rdickenson
-/doc/ci/review_apps/index.md @marcel.amirault
+/doc/ci/quick_start/ @marcel.amirault
+/doc/ci/resource_groups/ @rdickenson
+/doc/ci/review_apps/ @marcel.amirault
/doc/ci/runners/ @sselhorn
-/doc/ci/secrets/index.md @marcel.amirault
-/doc/ci/secure_files/index.md @marcel.amirault
+/doc/ci/runners/saas/ @sselhorn
+/doc/ci/runners/saas/macos/ @sselhorn
+/doc/ci/secrets/ @marcel.amirault
+/doc/ci/secure_files/ @marcel.amirault
/doc/ci/services/ @sselhorn
-/doc/ci/ssh_keys/index.md @marcel.amirault
-/doc/ci/test_cases/index.md @msedlakjakubowski
-/doc/ci/triggers/index.md @marcel.amirault
-/doc/ci/troubleshooting.md @marcel.amirault
-/doc/ci/unit_test_reports.md @marcel.amirault
+/doc/ci/ssh_keys/ @marcel.amirault
+/doc/ci/test_cases/ @msedlakjakubowski
+/doc/ci/testing/ @marcel.amirault
+/doc/ci/testing/code_quality.md @rdickenson
+/doc/ci/triggers/ @marcel.amirault
/doc/ci/variables/ @marcel.amirault
/doc/ci/yaml/ @marcel.amirault
/doc/development/application_limits.md @axil
/doc/development/approval_rules.md @aqualls
-/doc/development/audit_event_guide/index.md @eread
-/doc/development/auto_devops.md @sselhorn
+/doc/development/audit_event_guide/ @eread
+/doc/development/auto_devops.md @phillipwells
+/doc/development/backend/ @sselhorn
/doc/development/backend/create_source_code_be/ @aqualls
-/doc/development/backend/ruby_style_guide.md @sselhorn
/doc/development/build_test_package.md @axil
/doc/development/bulk_import.md @eread
/doc/development/cached_queries.md @sselhorn
/doc/development/cascading_settings.md @eread
-/doc/development/chatops_on_gitlabcom.md @sselhorn
-/doc/development/cicd/cicd_reference_documentation_guide.md @marcel.amirault
-/doc/development/cicd/index.md @marcel.amirault
-/doc/development/cicd/schema.md @marcel.amirault
-/doc/development/cicd/templates.md @marcel.amirault
-/doc/development/code_intelligence/index.md @aqualls
+/doc/development/chatops_on_gitlabcom.md @phillipwells
+/doc/development/cicd/ @marcel.amirault
+/doc/development/code_intelligence/ @aqualls
/doc/development/contributing/ @sselhorn
-/doc/development/contributing/merge_request_workflow.md @aqualls
-/doc/development/database_review.md @aqualls
/doc/development/database/ @aqualls
+/doc/development/database/filtering_by_label.md @msedlakjakubowski
+/doc/development/database/multiple_databases.md @sselhorn
+/doc/development/database_review.md @aqualls
/doc/development/developing_with_solargraph.md @aqualls
+/doc/development/development_processes.md @sselhorn
/doc/development/diffs.md @aqualls
/doc/development/distributed_tracing.md @msedlakjakubowski
/doc/development/documentation/ @sselhorn
-/doc/development/documentation/index.md @dianalogan
-/doc/development/documentation/redirects.md @dianalogan
-/doc/development/documentation/review_apps.md @dianalogan
-/doc/development/documentation/testing.md @dianalogan
-/doc/development/elasticsearch.md @sselhorn
-/doc/development/experiment_guide/gitlab_experiment.md @kpaizee
-/doc/development/experiment_guide/index.md @kpaizee
+/doc/development/documentation/styleguide/ @sselhorn
+/doc/development/documentation/topic_types/ @sselhorn
+/doc/development/elasticsearch.md @ashrafkhamis
+/doc/development/experiment_guide/ @phillipwells
/doc/development/export_csv.md @eread
/doc/development/fe_guide/content_editor.md @aqualls
/doc/development/fe_guide/dark_mode.md @sselhorn
/doc/development/fe_guide/graphql.md @sselhorn
+/doc/development/fe_guide/merge_request_widget_extensions.md @aqualls
/doc/development/fe_guide/source_editor.md @aqualls
-/doc/development/feature_categorization/index.md @sselhorn
-/doc/development/feature_flags/controls.md @sselhorn
-/doc/development/feature_flags/index.md @sselhorn
-/doc/development/filtering_by_label.md @msedlakjakubowski
+/doc/development/fe_guide/view_component.md @rdickenson
+/doc/development/feature_categorization/ @sselhorn
+/doc/development/feature_development.md @sselhorn
+/doc/development/feature_flags/ @sselhorn
+/doc/development/fips_compliance.md @aqualls
/doc/development/geo.md @axil
-/doc/development/geo/framework.md @axil
+/doc/development/geo/ @axil
/doc/development/git_object_deduplication.md @eread
/doc/development/gitaly.md @eread
-/doc/development/gitlab_flavored_markdown/index.md @aqualls
-/doc/development/gitlab_flavored_markdown/specification_guide/index.md @aqualls
-/doc/development/graphql_guide/ @kpaizee
+/doc/development/gitlab_flavored_markdown/ @aqualls
+/doc/development/gitlab_flavored_markdown/specification_guide/ @aqualls
/doc/development/graphql_guide/batchloader.md @aqualls
-/doc/development/hash_indexes.md @aqualls
+/doc/development/graphql_guide/graphql_pro.md @ashrafkhamis
+/doc/development/graphql_guide/index.md @ashrafkhamis
+/doc/development/graphql_guide/monitoring.md @ashrafkhamis
+/doc/development/graphql_guide/pagination.md @ashrafkhamis
/doc/development/i18n/ @eread
/doc/development/image_scaling.md @sselhorn
/doc/development/import_export.md @eread
/doc/development/index.md @sselhorn
-/doc/development/integrations/ @kpaizee
-/doc/development/integrations/codesandbox.md @sselhorn
-/doc/development/integrations/secure_partner_integration.md @rdickenson
+/doc/development/integrations/ @ashrafkhamis
/doc/development/integrations/secure.md @claytoncornell
+/doc/development/integrations/secure_partner_integration.md @rdickenson
/doc/development/internal_api/ @aqualls
/doc/development/internal_users.md @sselhorn
/doc/development/issuable-like-models.md @msedlakjakubowski
/doc/development/issue_types.md @msedlakjakubowski
-/doc/development/kubernetes.md @sselhorn
+/doc/development/kubernetes.md @phillipwells
/doc/development/lfs.md @aqualls
-/doc/development/ee_features.md @fneill
/doc/development/logging.md @msedlakjakubowski
/doc/development/maintenance_mode.md @axil
+/doc/development/merge_request_concepts/ @aqualls
/doc/development/omnibus.md @axil
/doc/development/packages/ @claytoncornell
+/doc/development/pages/ @aqualls
/doc/development/permissions.md @eread
/doc/development/policies.md @eread
-/doc/development/product_qualified_lead_guide/index.md @kpaizee
+/doc/development/product_qualified_lead_guide/ @phillipwells
/doc/development/project_templates.md @fneill
/doc/development/prometheus_metrics.md @msedlakjakubowski
/doc/development/real_time.md @msedlakjakubowski
+/doc/development/sec/ @rdickenson
/doc/development/secure_coding_guidelines.md @sselhorn
/doc/development/service_ping/ @claytoncornell
/doc/development/snowplow/ @claytoncornell
/doc/development/spam_protection_and_captcha/ @eread
/doc/development/sql.md @aqualls
-/doc/development/testing_guide/best_practices.md @sselhorn
-/doc/development/testing_guide/end_to_end/best_practices.md @sselhorn
+/doc/development/testing_guide/ @sselhorn
+/doc/development/testing_guide/contract/ @sselhorn
+/doc/development/testing_guide/end_to_end/ @sselhorn
/doc/development/value_stream_analytics.md @fneill
-/doc/development/value_stream_analytics/value_stream_analytics_aggregated_backend.md @fneill
+/doc/development/value_stream_analytics/ @fneill
/doc/development/wikis.md @aqualls
-/doc/development/work_items_widgets.md @msedlakjakubowski
/doc/development/work_items.md @msedlakjakubowski
+/doc/development/work_items_widgets.md @msedlakjakubowski
/doc/development/workhorse/ @aqualls
-/doc/development/workspace/index.md @sselhorn
-/doc/downgrade_ee_to_ce/index.md @axil
+/doc/development/workspace/ @sselhorn
+/doc/downgrade_ee_to_ce/ @axil
+/doc/drawers/ @ashrafkhamis
/doc/gitlab-basics/ @aqualls
/doc/install/ @axil
-/doc/integration/ @kpaizee
-/doc/integration/advanced_search/ @sselhorn
-/doc/integration/elasticsearch.md @sselhorn
+/doc/install/aws/ @axil
+/doc/install/azure/ @axil
+/doc/install/cloud_native/ @axil
+/doc/install/google_cloud_platform/ @axil
+/doc/install/openshift_and_gitlab/ @axil
+/doc/integration/advanced_search/ @ashrafkhamis
+/doc/integration/akismet.md @ashrafkhamis
+/doc/integration/alicloud.md @eread
+/doc/integration/arkose.md @phillipwells
+/doc/integration/auth0.md @eread
+/doc/integration/azure.md @eread
+/doc/integration/bitbucket.md @eread
+/doc/integration/cas.md @eread
+/doc/integration/datadog.md @ashrafkhamis
+/doc/integration/ding_talk.md @eread
+/doc/integration/external-issue-tracker.md @ashrafkhamis
+/doc/integration/facebook.md @eread
+/doc/integration/github.md @eread
+/doc/integration/gitlab.md @eread
/doc/integration/gitpod.md @aqualls
+/doc/integration/gmail_action_buttons_for_gitlab.md @ashrafkhamis
+/doc/integration/google.md @eread
+/doc/integration/index.md @ashrafkhamis
+/doc/integration/jenkins.md @ashrafkhamis
+/doc/integration/jira/ @ashrafkhamis
/doc/integration/kerberos.md @eread
-/doc/integration/mattermost/index.md @axil
+/doc/integration/mattermost/ @axil
+/doc/integration/oauth2_generic.md @eread
/doc/integration/oauth_provider.md @eread
+/doc/integration/omniauth.md @eread
+/doc/integration/openid_connect_provider.md @eread
+/doc/integration/recaptcha.md @ashrafkhamis
+/doc/integration/salesforce.md @eread
/doc/integration/saml.md @eread
-/doc/integration/security_partners/index.md @rdickenson
+/doc/integration/security_partners/ @rdickenson
+/doc/integration/slash_commands.md @ashrafkhamis
/doc/integration/sourcegraph.md @aqualls
-/doc/integration/vault.md @sselhorn
-/doc/operations/ @msedlakjakubowski
+/doc/integration/trello_power_up.md @ashrafkhamis
+/doc/integration/twitter.md @eread
+/doc/integration/vault.md @phillipwells
+/doc/operations/error_tracking.md msedlakjakubowski
/doc/operations/feature_flags.md @rdickenson
+/doc/operations/incident_management/ @msedlakjakubowski
+/doc/operations/index.md @msedlakjakubowski
+/doc/operations/metrics/ @msedlakjakubowski
+/doc/operations/metrics/dashboards/ @msedlakjakubowski
/doc/operations/product_analytics.md @claytoncornell
-/doc/policy/alpha-beta-support.md @axil
-/doc/policy/maintenance.md @axil @gitlab-org/delivery
+/doc/operations/tracing.md @msedlakjakubowski
+/doc/policy/ @axil
/doc/raketasks/ @axil
/doc/raketasks/generate_sample_prometheus_data.md @msedlakjakubowski
/doc/raketasks/migrate_snippets.md @aqualls
/doc/raketasks/spdx.md @rdickenson
/doc/raketasks/x509_signatures.md @aqualls
/doc/security/ @eread
-/doc/subscriptions/ @sselhorn
-/doc/topics/authentication/index.md @eread
-/doc/topics/autodevops/ @sselhorn
+/doc/subscriptions/ @fneill
+/doc/subscriptions/gitlab_com/ @fneill
+/doc/subscriptions/gitlab_dedicated/ @axil
+/doc/subscriptions/self_managed/ @fneill
+/doc/topics/authentication/ @eread
+/doc/topics/autodevops/ @phillipwells
+/doc/topics/autodevops/cloud_deployments/ @phillipwells
/doc/topics/git/ @aqualls
+/doc/topics/git/how_to_install_git/ @aqualls
+/doc/topics/git/lfs/ @aqualls
+/doc/topics/git/numerous_undo_possibilities_in_git/ @aqualls
/doc/topics/gitlab_flow.md @aqualls
-/doc/topics/offline/index.md @axil
-/doc/topics/offline/quick_start_guide.md @axil
+/doc/topics/offline/ @axil
/doc/topics/plan_and_track.md @msedlakjakubowski
/doc/tutorials/ @kpaizee
/doc/update/ @axil
/doc/update/mysql_to_postgresql.md @aqualls
+/doc/update/package/ @axil
/doc/update/upgrading_postgresql_using_slony.md @aqualls
/doc/user/admin_area/analytics/ @fneill
-/doc/user/admin_area/broadcast_messages.md @kpaizee
+/doc/user/admin_area/broadcast_messages.md @phillipwells
/doc/user/admin_area/credentials_inventory.md @eread
/doc/user/admin_area/custom_project_templates.md @eread
/doc/user/admin_area/diff_limits.md @aqualls
/doc/user/admin_area/geo_sites.md @axil
/doc/user/admin_area/labels.md @msedlakjakubowski
-/doc/user/admin_area/license_file.md @sselhorn
-/doc/user/admin_area/license.md @sselhorn
+/doc/user/admin_area/license.md @fneill
+/doc/user/admin_area/license_file.md @fneill
/doc/user/admin_area/merge_requests_approvals.md @aqualls
/doc/user/admin_area/moderate_users.md @eread
/doc/user/admin_area/monitoring/background_migrations.md @aqualls
/doc/user/admin_area/monitoring/health_check.md @msedlakjakubowski
+/doc/user/admin_area/reporting/git_abuse_rate_limit.md @phillipwells
/doc/user/admin_area/reporting/spamcheck.md @axil
/doc/user/admin_area/review_abuse_reports.md @eread
/doc/user/admin_area/settings/account_and_limit_settings.md @aqualls
@@ -642,89 +824,120 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/admin_area/settings/git_lfs_rate_limits.md @aqualls
/doc/user/admin_area/settings/gitaly_timeouts.md @eread
/doc/user/admin_area/settings/import_export_rate_limits.md @eread
+/doc/user/admin_area/settings/incident_management_rate_limits.md @msedlakjakubowski
/doc/user/admin_area/settings/index.md @aqualls
/doc/user/admin_area/settings/instance_template_repository.md @aqualls
/doc/user/admin_area/settings/package_registry_rate_limits.md @claytoncornell
-/doc/user/admin_area/settings/project_integration_management.md @kpaizee
+/doc/user/admin_area/settings/project_integration_management.md @ashrafkhamis
/doc/user/admin_area/settings/push_event_activities_limit.md @aqualls
/doc/user/admin_area/settings/rate_limit_on_issues_creation.md @msedlakjakubowski
/doc/user/admin_area/settings/rate_limit_on_notes_creation.md @msedlakjakubowski
+/doc/user/admin_area/settings/rate_limit_on_pipelines_creation.md @marcel.amirault
+/doc/user/admin_area/settings/rate_limit_on_users_api.md @eread
/doc/user/admin_area/settings/third_party_offers.md @fneill
/doc/user/admin_area/settings/usage_statistics.md @claytoncornell
/doc/user/admin_area/settings/visibility_and_access_controls.md @aqualls
-/doc/user/analytics/ @fneill
/doc/user/analytics/ci_cd_analytics.md @rdickenson
-/doc/user/application_security/ @rdickenson
-/doc/user/application_security/cluster_image_scanning/index.md @claytoncornell
-/doc/user/application_security/container_scanning/index.md @claytoncornell
+/doc/user/analytics/ @fneill
+/doc/user/application_security/api_fuzzing/ @rdickenson
+/doc/user/application_security/configuration/ @rdickenson
+/doc/user/application_security/container_scanning/ @claytoncornell
+/doc/user/application_security/coverage_fuzzing/ @rdickenson
/doc/user/application_security/cve_id_request.md @claytoncornell
+/doc/user/application_security/dast/ @rdickenson
+/doc/user/application_security/dast/checks/ @rdickenson
+/doc/user/application_security/dast_api/ @rdickenson
+/doc/user/application_security/dependency_list/ @rdickenson
+/doc/user/application_security/dependency_scanning/ @rdickenson
+/doc/user/application_security/generate_test_vulnerabilities/ @claytoncornell
+/doc/user/application_security/iac_scanning/ @rdickenson
+/doc/user/application_security/index.md @rdickenson
+/doc/user/application_security/offline_deployments/ @rdickenson
/doc/user/application_security/policies/ @claytoncornell
-/doc/user/application_security/security_dashboard/index.md @claytoncornell
-/doc/user/application_security/vulnerabilities/index.md @claytoncornell
-/doc/user/application_security/vulnerabilities/severities.md @claytoncornell
-/doc/user/application_security/vulnerability_report/index.md @claytoncornell
+/doc/user/application_security/sast/ @rdickenson
+/doc/user/application_security/secret_detection/ @rdickenson
+/doc/user/application_security/security_dashboard/ @claytoncornell
+/doc/user/application_security/terminology/ @rdickenson
+/doc/user/application_security/vulnerabilities/ @claytoncornell
+/doc/user/application_security/vulnerability_report/ @claytoncornell
/doc/user/asciidoc.md @aqualls
/doc/user/award_emojis.md @msedlakjakubowski
-/doc/user/clusters/ @sselhorn
-/doc/user/compliance/compliance_report/index.md @eread
-/doc/user/compliance/index.md @eread
-/doc/user/compliance/license_compliance/index.md @rdickenson
-/doc/user/crm/index.md @msedlakjakubowski
-/doc/user/discussions/index.md @aqualls
+/doc/user/clusters/ @phillipwells
+/doc/user/clusters/agent/ @phillipwells
+/doc/user/clusters/agent/gitops/ @phillipwells
+/doc/user/clusters/agent/install/ @phillipwells
+/doc/user/clusters/create/ @phillipwells
+/doc/user/compliance/ @eread
+/doc/user/compliance/compliance_report/ @eread
+/doc/user/compliance/license_compliance/ @rdickenson
+/doc/user/crm/ @msedlakjakubowski
+/doc/user/discussions/ @aqualls
/doc/user/feature_flags.md @sselhorn
-/doc/user/group/clusters/index.md @sselhorn
-/doc/user/group/contribution_analytics/index.md @fneill
+/doc/user/free_user_limit.md @phillipwells
+/doc/user/group/access_and_permissions.md @fneill
+/doc/user/group/clusters/ @phillipwells
+/doc/user/group/contribution_analytics/ @fneill
/doc/user/group/custom_project_templates.md @eread
-/doc/user/group/devops_adoption/index.md @fneill
-/doc/user/group/epics/epic_boards.md @msedlakjakubowski
-/doc/user/group/epics/index.md @msedlakjakubowski
-/doc/user/group/epics/linked_epics.md @msedlakjakubowski
-/doc/user/group/epics/manage_epics.md @msedlakjakubowski
-/doc/user/group/import/index.md @eread
+/doc/user/group/devops_adoption/ @fneill
+/doc/user/group/epics/ @msedlakjakubowski
+/doc/user/group/import/ @eread
/doc/user/group/index.md @fneill
-/doc/user/group/insights/index.md @fneill
-/doc/user/group/issues_analytics/index.md @msedlakjakubowski
-/doc/user/group/iterations/index.md @msedlakjakubowski
-/doc/user/group/planning_hierarchy/index.md @msedlakjakubowski
-/doc/user/group/repositories_analytics/index.md @marcel.amirault
-/doc/user/group/roadmap/index.md @msedlakjakubowski
-/doc/user/group/saml_sso/group_managed_accounts.md @eread
-/doc/user/group/saml_sso/index.md @eread
-/doc/user/group/saml_sso/scim_setup.md @eread
-/doc/user/group/saml_sso/example_saml_config.md @eread
-/doc/user/group/settings/group_access_tokens.md @eread
-/doc/user/group/settings/import_export.md @eread
-/doc/user/group/subgroups/index.md @fneill
-/doc/user/group/value_stream_analytics/index.md @fneill
-/doc/user/infrastructure/clusters/ @sselhorn
-/doc/user/infrastructure/clusters/manage/management_project_applications/apparmor.md @claytoncornell
-/doc/user/infrastructure/clusters/manage/management_project_applications/cilium.md @claytoncornell
-/doc/user/infrastructure/clusters/manage/management_project_applications/elasticstack.md @msedlakjakubowski
-/doc/user/infrastructure/clusters/manage/management_project_applications/falco.md @claytoncornell
-/doc/user/infrastructure/clusters/manage/management_project_applications/fluentd.md @claytoncornell
-/doc/user/infrastructure/clusters/manage/management_project_applications/prometheus.md @msedlakjakubowski
+/doc/user/group/insights/ @fneill
+/doc/user/group/issues_analytics/ @msedlakjakubowski
+/doc/user/group/iterations/ @msedlakjakubowski
+/doc/user/group/manage.md @fneill
+/doc/user/group/planning_hierarchy/ @msedlakjakubowski
+/doc/user/group/repositories_analytics/ @marcel.amirault
+/doc/user/group/roadmap/ @msedlakjakubowski
+/doc/user/group/saml_sso/ @eread
+/doc/user/group/settings/ @eread
+/doc/user/group/subgroups/ @fneill
+/doc/user/group/value_stream_analytics/ @fneill
+/doc/user/infrastructure/ @phillipwells
+/doc/user/infrastructure/clusters/ @phillipwells
+/doc/user/infrastructure/clusters/connect/ @phillipwells
+/doc/user/infrastructure/clusters/deploy/ @phillipwells
+/doc/user/infrastructure/clusters/manage/ @phillipwells
+/doc/user/infrastructure/clusters/manage/management_project_applications/certmanager.md @phillipwells
+/doc/user/infrastructure/clusters/manage/management_project_applications/ingress.md @phillipwells
/doc/user/infrastructure/clusters/manage/management_project_applications/runner.md @sselhorn
-/doc/user/infrastructure/clusters/manage/management_project_applications/sentry.md @msedlakjakubowski
-/doc/user/infrastructure/clusters/manage/management_project_applications/vault.md @sselhorn
-/doc/user/infrastructure/iac/ @sselhorn
-/doc/user/infrastructure/index.md @sselhorn
+/doc/user/infrastructure/clusters/manage/management_project_applications/vault.md @phillipwells
+/doc/user/infrastructure/iac/ @phillipwells
/doc/user/markdown.md @aqualls
+/doc/user/namespace/ @fneill
/doc/user/packages/ @claytoncornell
-/doc/user/packages/infrastructure_registry/index.md @sselhorn
-/doc/user/packages/terraform_module_registry/index.md @sselhorn
+/doc/user/packages/composer_repository/ @claytoncornell
+/doc/user/packages/conan_repository/ @claytoncornell
+/doc/user/packages/container_registry/ @claytoncornell
+/doc/user/packages/debian_repository/ @claytoncornell
+/doc/user/packages/dependency_proxy/ @claytoncornell
+/doc/user/packages/generic_packages/ @claytoncornell
+/doc/user/packages/go_proxy/ @claytoncornell
+/doc/user/packages/helm_repository/ @claytoncornell
+/doc/user/packages/infrastructure_registry/ @phillipwells
+/doc/user/packages/maven_repository/ @claytoncornell
+/doc/user/packages/npm_registry/ @claytoncornell
+/doc/user/packages/nuget_repository/ @claytoncornell
+/doc/user/packages/package_registry/ @claytoncornell
+/doc/user/packages/pypi_repository/ @claytoncornell
+/doc/user/packages/rubygems_registry/ @claytoncornell
+/doc/user/packages/terraform_module_registry/ @phillipwells
+/doc/user/packages/workflows/ @claytoncornell
/doc/user/permissions.md @eread
-/doc/user/profile/ @eread
+/doc/user/profile/account/ @eread
+/doc/user/profile/index.md @eread
/doc/user/profile/notifications.md @msedlakjakubowski
+/doc/user/profile/personal_access_tokens.md @eread
+/doc/user/profile/unknown_sign_in_notification.md @eread
/doc/user/project/autocomplete_characters.md @aqualls
/doc/user/project/badges.md @aqualls
-/doc/user/project/clusters/ @sselhorn
-/doc/user/project/clusters/kubernetes_pod_logs.md @msedlakjakubowski
-/doc/user/project/clusters/protect/ @claytoncornell
+/doc/user/project/clusters/ @phillipwells
+/doc/user/project/clusters/runbooks/ @phillipwells
/doc/user/project/code_intelligence.md @aqualls
/doc/user/project/code_owners.md @aqualls
/doc/user/project/deploy_boards.md @rdickenson
-/doc/user/project/deploy_keys/index.md @rdickenson
-/doc/user/project/deploy_tokens/index.md @rdickenson
+/doc/user/project/deploy_keys/ @rdickenson
+/doc/user/project/deploy_tokens/ @rdickenson
/doc/user/project/description_templates.md @msedlakjakubowski
/doc/user/project/file_lock.md @aqualls
/doc/user/project/git_attributes.md @aqualls
@@ -732,58 +945,50 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/project/import/ @eread
/doc/user/project/import/jira.md @msedlakjakubowski
/doc/user/project/index.md @fneill
-/doc/user/project/integrations/ @kpaizee
-/doc/user/project/integrations/prometheus_library/ @msedlakjakubowski
+/doc/user/project/integrations/ @ashrafkhamis
/doc/user/project/integrations/prometheus.md @msedlakjakubowski
+/doc/user/project/integrations/prometheus_library/ @msedlakjakubowski
/doc/user/project/issue_board.md @msedlakjakubowski
/doc/user/project/issues/ @msedlakjakubowski
/doc/user/project/issues/csv_import.md @eread
-/doc/user/project/labels.md @msedlakjakubowski
-/doc/user/project/members/index.md @fneill
-/doc/user/project/members/share_project_with_groups.md @fneill
+/doc/user/project/members/ @fneill
/doc/user/project/merge_requests/ @aqualls
-/doc/user/project/merge_requests/accessibility_testing.md @marcel.amirault
-/doc/user/project/merge_requests/browser_performance_testing.md @marcel.amirault
-/doc/user/project/merge_requests/code_quality.md @rdickenson
+/doc/user/project/merge_requests/approvals/ @aqualls
/doc/user/project/merge_requests/csv_export.md @eread
-/doc/user/project/merge_requests/fail_fast_testing.md @marcel.amirault
-/doc/user/project/merge_requests/load_performance_testing.md @marcel.amirault
/doc/user/project/merge_requests/status_checks.md @eread
-/doc/user/project/merge_requests/test_coverage_visualization.md @marcel.amirault
-/doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md @marcel.amirault
-/doc/user/project/milestones/burndown_and_burnup_charts.md @msedlakjakubowski
-/doc/user/project/milestones/index.md @msedlakjakubowski
+/doc/user/project/milestones/ @msedlakjakubowski
/doc/user/project/pages/ @aqualls
+/doc/user/project/pages/custom_domains_ssl_tls_certification/ @aqualls
+/doc/user/project/pages/getting_started/ @aqualls
/doc/user/project/protected_branches.md @aqualls
/doc/user/project/protected_tags.md @aqualls
/doc/user/project/push_options.md @aqualls
/doc/user/project/quick_actions.md @msedlakjakubowski
-/doc/user/project/releases/index.md @rdickenson
-/doc/user/project/releases/release_cli.md @rdickenson
+/doc/user/project/releases/ @rdickenson
/doc/user/project/repository/ @aqualls
+/doc/user/project/repository/branches/ @aqualls
+/doc/user/project/repository/managing_large_repositories.md @axil
/doc/user/project/repository/reducing_the_repo_size_using_git.md @eread
-/doc/user/project/requirements/index.md @msedlakjakubowski
+/doc/user/project/requirements/ @msedlakjakubowski
/doc/user/project/service_desk.md @msedlakjakubowski
/doc/user/project/settings/import_export.md @eread
/doc/user/project/settings/index.md @fneill
/doc/user/project/settings/project_access_tokens.md @eread
/doc/user/project/time_tracking.md @msedlakjakubowski
-/doc/user/project/web_ide/index.md @aqualls
-/doc/user/project/wiki/group.md @aqualls
-/doc/user/project/wiki/index.md @aqualls
+/doc/user/project/web_ide/ @aqualls
+/doc/user/project/wiki/ @aqualls
/doc/user/project/working_with_projects.md @fneill
/doc/user/public_access.md @fneill
/doc/user/reserved_names.md @fneill
-/doc/user/search/advanced_search.md @sselhorn
-/doc/user/search/global_search/advanced_search_syntax.md @sselhorn
-/doc/user/search/index.md @sselhorn
+/doc/user/search/ @ashrafkhamis
+/doc/user/search/global_search/ @ashrafkhamis
/doc/user/shortcuts.md @aqualls
/doc/user/snippets.md @aqualls
/doc/user/ssh.md @eread
/doc/user/tasks.md @msedlakjakubowski
/doc/user/todos.md @msedlakjakubowski
-/doc/user/usage_quotas.md @sselhorn
-/doc/user/workspace/index.md @fneill
+/doc/user/usage_quotas.md @fneill
+/doc/user/workspace/ @fneill
[Authentication and Authorization]
/app/assets/javascripts/access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
@@ -853,6 +1058,14 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/app/models/token_with_iv.rb @gitlab-org/manage/authentication-and-authorization/approvers
/app/models/webauthn_registration.rb @gitlab-org/manage/authentication-and-authorization/approvers
/app/policies/personal_access_token_policy.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/serializers/group_access_token_entity.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/serializers/group_access_token_serializer.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/serializers/impersonation_access_token_entity.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/serializers/impersonation_access_token_serializer.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/serializers/personal_access_token_entity.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/serializers/personal_access_token_serializer.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/serializers/project_access_token_entity.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/serializers/project_access_token_serializer.rb @gitlab-org/manage/authentication-and-authorization/approvers
/app/services/access_token_validation_service.rb @gitlab-org/manage/authentication-and-authorization/approvers
/app/services/auth/ @gitlab-org/manage/authentication-and-authorization/approvers
/app/services/authorized_project_update/ @gitlab-org/manage/authentication-and-authorization/approvers
@@ -863,8 +1076,11 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/app/services/todos/destroy/unauthorized_features_service.rb @gitlab-org/manage/authentication-and-authorization/approvers
/app/services/users/authorized_build_service.rb @gitlab-org/manage/authentication-and-authorization/approvers
/app/services/users/authorized_create_service.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/services/users/email_verification/generate_token_service.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/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
@@ -909,20 +1125,6 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/app/workers/authorized_project_update/ @gitlab-org/manage/authentication-and-authorization/approvers
/app/workers/authorized_projects_worker.rb @gitlab-org/manage/authentication-and-authorization/approvers
/app/workers/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
-/config/feature_flags/development/access_token_pagination.yml @gitlab-org/manage/authentication-and-authorization/approvers
-/config/feature_flags/development/application_settings_tokens_optional_encryption.yml @gitlab-org/manage/authentication-and-authorization/approvers
-/config/feature_flags/development/enforce_auth_checks_on_uploads.yml @gitlab-org/manage/authentication-and-authorization/approvers
-/config/feature_flags/development/forti_authenticator.yml @gitlab-org/manage/authentication-and-authorization/approvers
-/config/feature_flags/development/forti_token_cloud.yml @gitlab-org/manage/authentication-and-authorization/approvers
-/config/feature_flags/development/groups_tokens_optional_encryption.yml @gitlab-org/manage/authentication-and-authorization/approvers
-/config/feature_flags/development/pbkdf2_password_encryption.yml @gitlab-org/manage/authentication-and-authorization/approvers
-/config/feature_flags/development/pbkdf2_password_encryption_write.yml @gitlab-org/manage/authentication-and-authorization/approvers
-/config/feature_flags/development/projects_tokens_optional_encryption.yml @gitlab-org/manage/authentication-and-authorization/approvers
-/config/feature_flags/development/skip_group_share_unlink_auth_refresh.yml @gitlab-org/manage/authentication-and-authorization/approvers
-/config/feature_flags/development/specialized_worker_for_group_lock_update_auth_recalculation.yml @gitlab-org/manage/authentication-and-authorization/approvers
-/config/feature_flags/development/update_oauth_registration_flow.yml @gitlab-org/manage/authentication-and-authorization/approvers
-/config/feature_flags/development/webauthn.yml @gitlab-org/manage/authentication-and-authorization/approvers
-/config/feature_flags/ops/block_password_auth_for_saml_users.yml @gitlab-org/manage/authentication-and-authorization/approvers
/config/initializers/01_secret_token.rb @gitlab-org/manage/authentication-and-authorization/approvers
/config/initializers/devise_dynamic_password_length_validation.rb @gitlab-org/manage/authentication-and-authorization/approvers
/config/initializers/devise_password_length.rb.example @gitlab-org/manage/authentication-and-authorization/approvers
@@ -936,6 +1138,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/ee/app/assets/javascripts/access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
/ee/app/assets/javascripts/audit_events/components/tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
/ee/app/assets/javascripts/audit_events/token_utils.js @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/assets/javascripts/batch_comments/ @gitlab-org/manage/authentication-and-authorization/approvers
/ee/app/assets/javascripts/groups/settings/components/ @gitlab-org/manage/authentication-and-authorization/approvers
/ee/app/assets/javascripts/pages/admin/application_settings/general/components/ @gitlab-org/manage/authentication-and-authorization/approvers
/ee/app/assets/javascripts/pages/groups/omniauth_callbacks/ @gitlab-org/manage/authentication-and-authorization/approvers
@@ -957,7 +1160,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/ee/app/controllers/groups/omniauth_callbacks_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers
/ee/app/controllers/groups/scim_oauth_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers
/ee/app/controllers/oauth/ @gitlab-org/manage/authentication-and-authorization/approvers
-/ee/app/controllers/omniauth_kerberos_spnego_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/controllers/omniauth_kerberos_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers
/ee/app/finders/auth/ @gitlab-org/manage/authentication-and-authorization/approvers
/ee/app/helpers/ee/access_tokens_helper.rb @gitlab-org/manage/authentication-and-authorization/approvers
/ee/app/helpers/ee/auth_helper.rb @gitlab-org/manage/authentication-and-authorization/approvers
@@ -1023,6 +1226,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/lib/gitlab/background_migration/update_users_where_two_factor_auth_required_from_group.rb @gitlab-org/manage/authentication-and-authorization/approvers
/lib/gitlab/chat_name_token.rb @gitlab-org/manage/authentication-and-authorization/approvers
/lib/gitlab/ci/pipeline/expression/token.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/cleanup/unused_personal_access_tokens.rb @gitlab-org/manage/authentication-and-authorization/approvers
/lib/gitlab/external_authorization/ @gitlab-org/manage/authentication-and-authorization/approvers
/lib/gitlab/external_authorization.rb @gitlab-org/manage/authentication-and-authorization/approvers
/lib/gitlab/grape_logging/loggers/token_logger.rb @gitlab-org/manage/authentication-and-authorization/approvers
@@ -1040,6 +1244,13 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/lib/tasks/gitlab/password.rake @gitlab-org/manage/authentication-and-authorization/approvers
/lib/tasks/tokens.rake @gitlab-org/manage/authentication-and-authorization/approvers
+[Manage::Workspace]
+lib/api/entities/basic_project_details.rb @gitlab-org/manage/manage-workspace/backend-approvers
+lib/api/entities/project_with_access.rb @gitlab-org/manage/manage-workspace/backend-approvers
+lib/api/entities/project_identity.rb @gitlab-org/manage/manage-workspace/backend-approvers
+lib/api/entities/project.rb @gitlab-org/manage/manage-workspace/backend-approvers
+ee/lib/ee/api/entities/project.rb @gitlab-org/manage/manage-workspace/backend-approvers
+
[Compliance]
/ee/app/services/audit_events/build_service.rb @gitlab-org/manage/compliance
/ee/spec/services/audit_events/custom_audit_event_service_spec.rb @gitlab-org/manage/compliance
@@ -1047,21 +1258,6 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/app/services/audit_event_service.rb @gitlab-org/manage/compliance
/app/services/concerns/audit_event_save_type.rb @gitlab-org/manage/compliance
/app/views/profiles/audit_log.html.haml @gitlab-org/manage/compliance
-/data/deprecations/14-3-repository-push-audit-events.yml @gitlab-org/manage/compliance
-/data/removals/15_0/removal_manage_repository_push_audit_event.yml @gitlab-org/manage/compliance
-/db/docs/audit_events.yml @gitlab-org/manage/compliance
-/db/docs/audit_events_external_audit_event_destinations.yml @gitlab-org/manage/compliance
-/db/docs/audit_events_streaming_headers.yml @gitlab-org/manage/compliance
-/db/migrate/20210819185500_create_external_audit_event_destinations_table.rb @gitlab-org/manage/compliance
-/db/migrate/20220524141800_create_audit_events_streaming_headers.rb @gitlab-org/manage/compliance
-/db/post_migrate/20210331105335_drop_non_partitioned_audit_events.rb @gitlab-org/manage/compliance
-/db/post_migrate/20220119094503_populate_audit_event_streaming_verification_token.rb @gitlab-org/manage/compliance
-/doc/administration/audit_event_streaming.md @gitlab-org/manage/compliance
-/doc/administration/audit_events.md @gitlab-org/manage/compliance
-/doc/administration/audit_reports.md @gitlab-org/manage/compliance
-/doc/administration/auditor_users.md @gitlab-org/manage/compliance
-/doc/api/audit_events.md @gitlab-org/manage/compliance
-/doc/api/graphql/audit_report.md @gitlab-org/manage/compliance
/ee/app/assets/javascripts/audit_events/components/audit_events_app.vue @gitlab-org/manage/compliance
/ee/app/assets/javascripts/audit_events/components/audit_events_export_button.vue @gitlab-org/manage/compliance
/ee/app/assets/javascripts/audit_events/components/audit_events_filter.vue @gitlab-org/manage/compliance
@@ -1119,58 +1315,5 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/ee/lib/ee/api/entities/audit_event.rb @gitlab-org/manage/compliance
/ee/lib/ee/audit/ @gitlab-org/manage/compliance
/ee/lib/ee/gitlab/audit/ @gitlab-org/manage/compliance
-/ee/spec/controllers/admin/audit_log_reports_controller_spec.rb @gitlab-org/manage/compliance
-/ee/spec/controllers/admin/audit_logs_controller_spec.rb @gitlab-org/manage/compliance
-/ee/spec/controllers/groups/audit_events_controller_spec.rb @gitlab-org/manage/compliance
-/ee/spec/controllers/projects/audit_events_controller_spec.rb @gitlab-org/manage/compliance
-/ee/spec/factories/audit_events/external_audit_event_destinations.rb @gitlab-org/manage/compliance
-/ee/spec/features/admin/admin_audit_logs_spec.rb @gitlab-org/manage/compliance
-/ee/spec/features/groups/audit_events_spec.rb @gitlab-org/manage/compliance
-/ee/spec/features/projects/audit_events_spec.rb @gitlab-org/manage/compliance
-/ee/spec/finders/audit_event_finder_spec.rb @gitlab-org/manage/compliance
-/ee/spec/fixtures/api/schemas/public_api/v4/audit_event.json @gitlab-org/manage/compliance
-/ee/spec/fixtures/api/schemas/public_api/v4/audit_events.json @gitlab-org/manage/compliance
-/ee/spec/frontend/audit_events/components/__snapshots__/ @gitlab-org/manage/compliance
-/ee/spec/frontend/audit_events/components/audit_events_app_spec.js @gitlab-org/manage/compliance
-/ee/spec/frontend/audit_events/components/audit_events_export_button_spec.js @gitlab-org/manage/compliance
-/ee/spec/frontend/audit_events/components/audit_events_filter_spec.js @gitlab-org/manage/compliance
-/ee/spec/frontend/audit_events/components/audit_events_logs_spec.js @gitlab-org/manage/compliance
-/ee/spec/frontend/audit_events/components/audit_events_stream_spec.js @gitlab-org/manage/compliance
-/ee/spec/frontend/audit_events/components/audit_events_table_spec.js @gitlab-org/manage/compliance
-/ee/spec/frontend/audit_events/components/tokens/shared/ @gitlab-org/manage/compliance
-/ee/spec/graphql/types/audit_events/exterrnal_audit_event_destination_type_spec.rb @gitlab-org/manage/compliance
-/ee/spec/helpers/audit_events_helper_spec.rb @gitlab-org/manage/compliance
-/ee/spec/lib/audit/external_status_check_changes_auditor_spec.rb @gitlab-org/manage/compliance
-/ee/spec/lib/audit/group_merge_request_approval_setting_changes_auditor_spec.rb @gitlab-org/manage/compliance
-/ee/spec/lib/audit/group_push_rules_changes_auditor_spec.rb @gitlab-org/manage/compliance
-/ee/spec/lib/ee/audit/ @gitlab-org/manage/compliance
-/ee/spec/lib/gitlab/audit/auditor_spec.rb @gitlab-org/manage/compliance
-/ee/spec/models/audit_events/external_audit_event_destination_spec.rb @gitlab-org/manage/compliance
-/ee/spec/models/concerns/auditable_spec.rb @gitlab-org/manage/compliance
-/ee/spec/models/ee/audit_event_spec.rb @gitlab-org/manage/compliance
-/ee/spec/presenters/audit_event_presenter_spec.rb @gitlab-org/manage/compliance
-/ee/spec/requests/admin/audit_events_spec.rb @gitlab-org/manage/compliance
-/ee/spec/requests/api/audit_events_spec.rb @gitlab-org/manage/compliance
-/ee/spec/requests/api/graphql/group/external_audit_event_destinations_spec.rb @gitlab-org/manage/compliance
-/ee/spec/requests/groups/audit_events_spec.rb @gitlab-org/manage/compliance
-/ee/spec/requests/projects/audit_events_spec.rb @gitlab-org/manage/compliance
-/ee/spec/serializers/audit_event_entity_spec.rb @gitlab-org/manage/compliance
-/ee/spec/serializers/audit_event_serializer_spec.rb @gitlab-org/manage/compliance
-/ee/spec/services/audit_event_service_spec.rb @gitlab-org/manage/compliance
-/ee/spec/support/shared_contexts/audit_event_not_licensed_shared_context.rb @gitlab-org/manage/compliance
-/ee/spec/support/shared_contexts/audit_event_queue_shared_context.rb @gitlab-org/manage/compliance
-/ee/spec/support/shared_examples/audit/ @gitlab-org/manage/compliance
-/ee/spec/support/shared_examples/features/audit_events_filter_shared_examples.rb @gitlab-org/manage/compliance
-/ee/spec/support/shared_examples/services/audit_event_logging_shared_examples.rb @gitlab-org/manage/compliance
-/ee/spec/workers/audit_events/audit_event_streaming_worker_spec.rb @gitlab-org/manage/compliance
/lib/gitlab/audit/auditor.rb @gitlab-org/manage/compliance
/lib/gitlab/audit_json_logger.rb @gitlab-org/manage/compliance
-/spec/factories/audit_events.rb @gitlab-org/manage/compliance
-/spec/lib/gitlab/audit/auditor_spec.rb @gitlab-org/manage/compliance
-/spec/migrations/populate_audit_event_streaming_verification_token_spec.rb @gitlab-org/manage/compliance
-/spec/models/audit_event_spec.rb @gitlab-org/manage/compliance
-/spec/services/audit_event_service_spec.rb @gitlab-org/manage/compliance
-/spec/services/concerns/audit_event_save_type_spec.rb @gitlab-org/manage/compliance
-/spec/support/shared_examples/sends_git_audit_streaming_event_shared_examples.rb @gitlab-org/manage/compliance
-/spec/views/profiles/audit_log.html.haml_spec.rb @gitlab-org/manage/compliance
-/vendor/project_templates/hipaa_audit_protocol.tar.gz @gitlab-org/manage/compliance
diff --git a/.gitlab/agents/review-apps/config.yaml b/.gitlab/agents/review-apps/config.yaml
new file mode 100644
index 00000000000..945b3a18d30
--- /dev/null
+++ b/.gitlab/agents/review-apps/config.yaml
@@ -0,0 +1 @@
+# This empty file is used for agent-based integration with Kubernetes
diff --git a/.gitlab/ci/_skip.yml b/.gitlab/ci/_skip.yml
new file mode 100644
index 00000000000..27a3ff5b836
--- /dev/null
+++ b/.gitlab/ci/_skip.yml
@@ -0,0 +1,11 @@
+# no-op pipeline template for skipping whole child pipeline execution
+
+no-op:
+ image: ${GITLAB_DEPENDENCY_PROXY}alpine:latest
+ stage: test
+ variables:
+ GIT_STRATEGY: none
+ script:
+ - echo "${SKIP_MESSAGE:-no-op run, nothing will be executed!}"
+ rules:
+ - when: always
diff --git a/.gitlab/ci/build-images.gitlab-ci.yml b/.gitlab/ci/build-images.gitlab-ci.yml
index 3b360d66b7f..1b041c9af38 100644
--- a/.gitlab/ci/build-images.gitlab-ci.yml
+++ b/.gitlab/ci/build-images.gitlab-ci.yml
@@ -2,15 +2,11 @@
extends: .use-kaniko
variables:
GIT_LFS_SKIP_SMUDGE: 1
- script:
- - scripts/checkout-mr-source-sha
retry: 2
# This image is used by:
# - The `review-qa-*` jobs
-# - The downstream `omnibus-gitlab-mirror` pipeline triggered by `package-and-qa` so that it doesn't have to rebuild it again.
-# The downstream `omnibus-gitlab-mirror` pipeline itself passes the image name to the `gitlab-qa-mirror` pipeline so that
-# it can use it instead of inferring an end-to-end imag from the GitLab image built by the downstream `omnibus-gitlab-mirror` pipeline.
+# - The `e2e:package-and-test` child pipeline test stage jobs
# See https://docs.gitlab.com/ee/development/testing_guide/end_to_end/index.html#testing-code-in-merge-requests for more details.
build-qa-image:
extends:
@@ -19,15 +15,23 @@ build-qa-image:
stage: build-images
needs: []
script:
- - !reference [.base-image-build, script]
+ # Tag with commit SHA by default
+ - export QA_IMAGE="${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_COMMIT_SHA}"
+ # For branches, tag with slugified branch name. For tags, use the tag directly
+ - export QA_IMAGE_BRANCH="${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_COMMIT_TAG:-$CI_COMMIT_REF_SLUG}"
+ # Auto-deploy tag format uses first 12 letters of commit SHA. Tag with that
+ # reference also
+ - export QA_IMAGE_FOR_AUTO_DEPLOY="${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_COMMIT_SHA:0:11}"
- echo $QA_IMAGE
- echo $QA_IMAGE_BRANCH
+ - echo $QA_IMAGE_FOR_AUTO_DEPLOY
- |
/kaniko/executor \
--context=${CI_PROJECT_DIR} \
--dockerfile=${CI_PROJECT_DIR}/qa/Dockerfile \
--destination=${QA_IMAGE} \
--destination=${QA_IMAGE_BRANCH} \
+ --destination=${QA_IMAGE_FOR_AUTO_DEPLOY} \
--build-arg=CHROME_VERSION=${CHROME_VERSION} \
--build-arg=DOCKER_VERSION=${DOCKER_VERSION} \
--build-arg=QA_BUILD_TARGET=${QA_BUILD_TARGET:-qa} \
@@ -35,7 +39,7 @@ 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 `package-and-qa` job): https://gitlab.com/gitlab-org/omnibus-gitlab/-/blob/dfd1ad475868fc84e91ab7b5706aa03e46dc3a86/.gitlab-ci.yml#L130
+# - 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
@@ -43,7 +47,6 @@ build-assets-image:
stage: build-images
needs: ["compile-production-assets"]
script:
- - !reference [.base-image-build, 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
diff --git a/.gitlab/ci/docs.gitlab-ci.yml b/.gitlab/ci/docs.gitlab-ci.yml
index 3af156e9bd0..7e157171183 100644
--- a/.gitlab/ci/docs.gitlab-ci.yml
+++ b/.gitlab/ci/docs.gitlab-ci.yml
@@ -44,7 +44,7 @@ docs-lint markdown:
- .default-retry
- .docs:rules:docs-lint
# When updating the image version here, update it in /scripts/lint-doc.sh too.
- image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-docs/lint-markdown:alpine-3.16-vale-2.17.0-markdownlint-0.31.1
+ image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-docs/lint-markdown:alpine-3.16-vale-2.20.1-markdownlint-0.32.2
stage: lint
needs: []
script:
diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml
index 1d0218f4bd7..3bd65b565e4 100644
--- a/.gitlab/ci/frontend.gitlab-ci.yml
+++ b/.gitlab/ci/frontend.gitlab-ci.yml
@@ -18,6 +18,7 @@
# Disable warnings in browserslist which can break on backports
# https://github.com/browserslist/browserslist/blob/a287ec6/node.js#L367-L384
BROWSERSLIST_IGNORE_OLD_DATA: "true"
+ WEBPACK_COMPILE_LOG_PATH: "tmp/webpack-output.log"
stage: prepare
script:
- *yarn-install
@@ -31,7 +32,6 @@ compile-production-assets:
variables:
NODE_ENV: "production"
RAILS_ENV: "production"
- WEBPACK_REPORT: "true"
artifacts:
name: webpack-report
expire_in: 31d
@@ -40,10 +40,9 @@ compile-production-assets:
# - 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
- public/assets/
- - webpack-report/
+ - "${WEBPACK_COMPILE_LOG_PATH}"
when: always
before_script:
- - scripts/checkout-mr-source-sha
- !reference [.default-before_script, before_script]
after_script:
- rm -f /etc/apt/sources.list.d/google*.list # We don't need to update Chrome here
@@ -57,6 +56,7 @@ compile-test-assets:
paths:
- public/assets/
- node_modules/@gitlab/svgs/dist/icons.json # app/helpers/icons_helper.rb uses this file
+ - "${WEBPACK_COMPILE_LOG_PATH}"
when: always
compile-test-assets as-if-foss:
@@ -213,7 +213,15 @@ jest minimal:
- !reference [jest, needs]
- "detect-tests"
script:
- - run_timed_command "yarn jest:ci:minimal"
+ - if [[ -s "$RSPEC_CHANGED_FILES_PATH" ]]; then run_timed_command "yarn jest:ci:minimal"; fi
+
+jest as-if-foss:
+ extends:
+ - .jest-base
+ - .frontend:rules:jest:as-if-foss
+ - .as-if-foss
+ needs: ["rspec-all frontend_fixture as-if-foss"]
+ parallel: 2
jest minimal as-if-foss:
extends:
@@ -224,7 +232,7 @@ jest minimal as-if-foss:
- "rspec-all frontend_fixture as-if-foss"
- "detect-tests"
script:
- - run_timed_command "yarn jest:ci:minimal"
+ - if [[ -s "$RSPEC_CHANGED_FILES_PATH" ]]; then run_timed_command "yarn jest:ci:minimal"; fi
jest-integration:
extends:
@@ -236,19 +244,11 @@ jest-integration:
- job: "rspec-all frontend_fixture"
- job: "graphql-schema-dump"
-jest-as-if-foss:
- extends:
- - .jest-base
- - .frontend:rules:default-frontend-jobs-as-if-foss
- - .as-if-foss
- needs: ["rspec-all frontend_fixture as-if-foss"]
- parallel: 2
-
coverage-frontend:
extends:
- .default-retry
- .yarn-cache
- - .frontend:rules:ee-mr-and-default-branch-only
+ - .frontend:rules:coverage-frontend
needs:
- job: "jest"
optional: true
@@ -260,7 +260,9 @@ coverage-frontend:
script:
- run_timed_command "yarn node scripts/frontend/merge_coverage_frontend.js"
# Removing the individual coverage results, as we just merged them.
- - rm -r coverage-frontend/jest-*
+ - if ls coverage-frontend/jest-* > /dev/null 2>&1; then
+ rm -r coverage-frontend/jest-*;
+ fi
coverage: '/^Statements\s*:\s*?(\d+(?:\.\d+)?)%/'
artifacts:
name: coverage-frontend
@@ -320,24 +322,20 @@ webpack-dev-server:
bundle-size-review:
extends:
- .default-retry
+ - .assets-compile-cache
- .frontend:rules:bundle-size-review
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:danger
stage: test
- needs: ["compile-production-assets"]
+ needs: []
script:
- - source scripts/utils.sh
- - mkdir -p bundle-size-review
- - cp webpack-report/index.html bundle-size-review/bundle-report.html
- - yarn global add https://gitlab.com/gitlab-org/frontend/playground/webpack-memory-metrics.git
- - |
- danger_id=$(echo -n ${DANGER_GITLAB_API_TOKEN} | md5sum | awk '{print $1}' | cut -c5-10)
- run_timed_command "danger --dangerfile=danger/Dangerfile-bundle_size --fail-on-errors=true --verbose --danger_id=bundle-size-review-${danger_id}"
+ - *yarn-install
+ - scripts/bundle_size_review
artifacts:
when: always
name: bundle-size-review
expire_in: 31d
paths:
- - bundle-size-review
+ - bundle-size-review/
.startup-css-check-base:
extends:
diff --git a/.gitlab/ci/global.gitlab-ci.yml b/.gitlab/ci/global.gitlab-ci.yml
index 9c92b11d4fd..7bdca62b68e 100644
--- a/.gitlab/ci/global.gitlab-ci.yml
+++ b/.gitlab/ci/global.gitlab-ci.yml
@@ -290,7 +290,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.2.0
+ - name: elasticsearch:8.3.3
variables:
POSTGRES_HOST_AUTH_METHOD: trust
PG_VERSION: "12"
diff --git a/.gitlab/ci/notify.gitlab-ci.yml b/.gitlab/ci/notify.gitlab-ci.yml
index a8c156c7dba..95318d5ce08 100644
--- a/.gitlab/ci/notify.gitlab-ci.yml
+++ b/.gitlab/ci/notify.gitlab-ci.yml
@@ -7,6 +7,8 @@
MERGE_REQUEST_URL: ${CI_MERGE_REQUEST_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}
before_script:
- apk update && apk add git curl bash
+ - echo "NOTIFY_CHANNEL is ${NOTIFY_CHANNEL}"
+ - echo "CI_PIPELINE_URL is ${CI_PIPELINE_URL}"
notify-update-gitaly:
extends:
@@ -16,11 +18,9 @@ notify-update-gitaly:
when: on_failure
allow_failure: true
variables:
- NOTIFY_CHANNEL: g_create_gitaly
+ NOTIFY_CHANNEL: g_gitaly
GITALY_UPDATE_BRANCH: release-tools/update-gitaly
script:
- - echo "NOTIFY_CHANNEL is ${NOTIFY_CHANNEL}"
- - echo "CI_PIPELINE_URL is ${CI_PIPELINE_URL}"
- scripts/slack ${NOTIFY_CHANNEL} "â˜ ï¸ \`${GITALY_UPDATE_BRANCH}\` failed! â˜ ï¸ See ${CI_PIPELINE_URL} (triggered from ${MERGE_REQUEST_URL})" ci_failing "GitLab QA Bot"
notify-security-pipeline:
@@ -30,7 +30,17 @@ notify-security-pipeline:
variables:
NOTIFY_CHANNEL: f_upcoming_release
script:
- - echo "NOTIFY_CHANNEL is ${NOTIFY_CHANNEL}"
- - echo "CI_PIPELINE_URL is ${CI_PIPELINE_URL}"
# <!subteam^S0127FU8PDE> mentions the `@release-managers` group
- scripts/slack ${NOTIFY_CHANNEL} "<!subteam^S0127FU8PDE> â˜ ï¸ Pipeline for merged result failed! â˜ ï¸ See ${CI_PIPELINE_URL} (triggered from ${MERGE_REQUEST_URL})" ci_failing "GitLab Release Tools Bot"
+
+notify-pipeline-failure:
+ extends:
+ - .notify-slack
+ rules:
+ - if: '$NOTIFY_PIPELINE_FAILURE_CHANNEL'
+ when: on_failure
+ allow_failure: true
+ variables:
+ NOTIFY_CHANNEL: "${NOTIFY_PIPELINE_FAILURE_CHANNEL}"
+ script:
+ - scripts/slack ${NOTIFY_CHANNEL} "⌠\`${CI_COMMIT_REF_NAME}\` pipeline failed! See ${CI_PIPELINE_URL}" ci_failing "notify-pipeline-failure"
diff --git a/.gitlab/ci/package-and-test/main.gitlab-ci.yml b/.gitlab/ci/package-and-test/main.gitlab-ci.yml
new file mode 100644
index 00000000000..d96da7744ab
--- /dev/null
+++ b/.gitlab/ci/package-and-test/main.gitlab-ci.yml
@@ -0,0 +1,600 @@
+# E2E tests pipeline loaded dynamically by script: scripts/generate-e2e-pipeline
+
+include:
+ - local: .gitlab/ci/global.gitlab-ci.yml
+ - local: .gitlab/ci/package-and-test/rules.gitlab-ci.yml
+ - local: .gitlab/ci/package-and-test/variables.gitlab-ci.yml
+ - project: gitlab-org/quality/pipeline-common
+ ref: 1.2.1
+ file:
+ - /ci/base.gitlab-ci.yml
+ - /ci/allure-report.yml
+ - /ci/knapsack-report.yml
+
+stages:
+ - test
+ - report
+ - notify
+
+# ==========================================
+# Templates
+# ==========================================
+.parallel:
+ parallel: 5
+ variables:
+ QA_KNAPSACK_REPORT_PATH: $CI_PROJECT_DIR/qa/knapsack
+
+.ruby-image:
+ image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3
+
+.bundle-install:
+ extends:
+ - .gitlab-qa-install
+ - .qa-cache
+ variables:
+ RUN_WITH_BUNDLE: "true" # installs and runs gitlab-qa via bundler
+ QA_PATH: qa
+
+.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 "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"
+ - echo "Running - '$QA_COMMAND'"
+ - eval "$QA_COMMAND"
+
+.qa:
+ extends:
+ - .qa-base
+ - .bundle-install
+ - .gitlab-qa-report
+ stage: test
+ tags:
+ - e2e
+ needs:
+ - trigger-omnibus
+ - download-knapsack-report
+ variables:
+ QA_GENERATE_ALLURE_REPORT: "true"
+ QA_CAN_TEST_PRAEFECT: "false"
+ QA_INTERCEPT_REQUESTS: "true"
+ QA_RUN_TYPE: e2e-package-and-test
+ TEST_LICENSE_MODE: $QA_TEST_LICENSE_MODE
+ EE_LICENSE: $QA_EE_LICENSE
+ GITHUB_ACCESS_TOKEN: $QA_GITHUB_ACCESS_TOKEN
+ GITLAB_QA_ADMIN_ACCESS_TOKEN: $QA_ADMIN_ACCESS_TOKEN
+
+# ==========================================
+# Prepare stage
+# ==========================================
+trigger-omnibus-env:
+ extends:
+ - .omnibus-env
+ - .rules:prepare
+ stage: .pre
+
+trigger-omnibus:
+ extends: .rules:prepare
+ stage: .pre
+ needs:
+ - trigger-omnibus-env
+ inherit:
+ variables: false
+ variables:
+ GITALY_SERVER_VERSION: $GITALY_SERVER_VERSION
+ GITLAB_ELASTICSEARCH_INDEXER_VERSION: $GITLAB_ELASTICSEARCH_INDEXER_VERSION
+ GITLAB_KAS_VERSION: $GITLAB_KAS_VERSION
+ GITLAB_METRICS_EXPORTER_VERSION: $GITLAB_METRICS_EXPORTER_VERSION
+ GITLAB_PAGES_VERSION: $GITLAB_PAGES_VERSION
+ GITLAB_SHELL_VERSION: $GITLAB_SHELL_VERSION
+ GITLAB_WORKHORSE_VERSION: $GITLAB_WORKHORSE_VERSION
+ GITLAB_VERSION: $CI_COMMIT_SHA
+ IMAGE_TAG: $CI_COMMIT_SHA
+ TOP_UPSTREAM_SOURCE_PROJECT: $CI_PROJECT_PATH
+ SECURITY_SOURCES: $SECURITY_SOURCES
+ CACHE_UPDATE: $OMNIBUS_GITLAB_CACHE_UPDATE
+ SKIP_QA_DOCKER: "true"
+ SKIP_QA_TEST: "true"
+ ee: "true"
+ trigger:
+ project: gitlab-org/build/omnibus-gitlab-mirror
+ strategy: depend
+
+download-knapsack-report:
+ extends:
+ - .bundle-install
+ - .ruby-image
+ - .rules:prepare
+ stage: .pre
+ script:
+ - bundle exec rake "knapsack:download[test]"
+ allow_failure: true
+ artifacts:
+ paths:
+ - qa/knapsack/ee-*.json
+ expire_in: 1 day
+
+# e2e test jobs run on separate runner which has separate cache setup
+cache-gems:
+ extends:
+ - .bundle-install
+ - .ruby-image
+ - .qa-cache-push
+ - .rules:prepare
+ stage: .pre
+ tags:
+ - e2e
+ script:
+ - echo "Populated qa cache"
+
+# ==========================================
+# Test stage
+# ==========================================
+
+# ------------------------------------------
+# Manual jobs
+# ------------------------------------------
+
+# Run manual quarantine job
+# this job requires passing QA_SCENARIO variable
+# and optionally QA_TESTS to run specific quarantined tests
+_ee:quarantine:
+ extends:
+ - .qa
+ - .rules:test:quarantine
+ needs:
+ - trigger-omnibus
+ stage: test
+ allow_failure: true
+ variables:
+ QA_RSPEC_TAGS: --tag quarantine
+
+# ------------------------------------------
+# FF changes
+# ------------------------------------------
+
+# Run specs with feature flags set to the opposite of the default state
+ee:instance-parallel-ff-inverse:
+ extends:
+ - .qa
+ - .parallel
+ variables:
+ QA_SCENARIO: Test::Instance::Image
+ QA_KNAPSACK_REPORT_NAME: ee-instance-parallel
+ GITLAB_QA_OPTS: --set-feature-flags $QA_FEATURE_FLAGS
+ rules:
+ - !reference [.rules:test:feature-flags-deleted, rules] # skip job when only change is ff deletion
+ - !reference [.rules:test:feature-flags-set, rules]
+
+# ------------------------------------------
+# Jobs with parallel variant
+# ------------------------------------------
+ee:instance:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Instance::Image
+ rules:
+ - !reference [.rules:test:qa-non-parallel, rules]
+ - if: $QA_SUITES =~ /Test::Instance::All/
+ee:instance-parallel:
+ extends:
+ - .parallel
+ - ee:instance
+ rules:
+ - !reference [.rules:test:feature-flags-set, rules] # always run instance-parallel to validate ff change
+ - !reference [.rules:test:qa-parallel, rules]
+ - if: $QA_SUITES =~ /Test::Instance::All/
+
+ee:praefect:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Integration::Praefect
+ QA_CAN_TEST_PRAEFECT: "true"
+ rules:
+ - !reference [.rules:test:qa-non-parallel, rules]
+ - if: $QA_SUITES =~ /Test::Instance::All/
+ee:praefect-parallel:
+ extends:
+ - .parallel
+ - ee:praefect
+ rules:
+ - !reference [.rules:test:qa-parallel, rules]
+ - if: $QA_SUITES =~ /Test::Instance::All/
+
+ee:relative-url:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Instance::RelativeUrl
+ rules:
+ - !reference [.rules:test:qa-non-parallel, rules]
+ - if: $QA_SUITES =~ /Test::Instance::All/
+ee:relative-url-parallel:
+ extends:
+ - .parallel
+ - ee:relative-url
+ rules:
+ - !reference [.rules:test:qa-parallel, rules]
+ - if: $QA_SUITES =~ /Test::Instance::All/
+
+ee:decomposition-single-db:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Instance::Image
+ GITLAB_QA_OPTS: --omnibus-config decomposition_single_db
+ rules:
+ - !reference [.rules:test:qa-non-parallel, rules]
+ - if: $QA_SUITES =~ /Test::Instance::All/
+ee:decomposition-single-db-parallel:
+ extends:
+ - .parallel
+ - ee:decomposition-single-db
+ rules:
+ - !reference [.rules:test:qa-parallel, rules]
+ - if: $QA_SUITES =~ /Test::Instance::All/
+
+ee:decomposition-multiple-db:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Instance::Image
+ GITLAB_QA_OPTS: --omnibus-config decomposition_multiple_db
+ rules:
+ - !reference [.rules:test:qa-non-parallel, rules]
+ - if: $QA_SUITES =~ /Test::Instance::All/
+ee:decomposition-multiple-db-parallel:
+ extends:
+ - .parallel
+ - ee:decomposition-multiple-db
+ rules:
+ - !reference [.rules:test:qa-parallel, rules]
+ - if: $QA_SUITES =~ /Test::Instance::All/
+
+ee:object-storage:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Instance::Image
+ QA_RSPEC_TAGS: --tag object_storage
+ GITLAB_QA_OPTS: --omnibus-config object_storage
+ rules:
+ - !reference [.rules:test:qa-non-parallel, rules]
+ - if: $QA_SUITES =~ /Test::Instance::ObjectStorage/
+ee:object-storage-parallel:
+ extends: ee:object-storage
+ parallel: 2
+ rules:
+ - !reference [.rules:test:qa-parallel, rules]
+ - if: $QA_SUITES =~ /Test::Instance::ObjectStorage/
+
+ee:object-storage-aws:
+ extends: ee:object-storage
+ variables:
+ AWS_S3_ACCESS_KEY: $QA_AWS_S3_ACCESS_KEY
+ AWS_S3_BUCKET_NAME: $QA_AWS_S3_BUCKET_NAME
+ AWS_S3_KEY_ID: $QA_AWS_S3_KEY_ID
+ AWS_S3_REGION: $QA_AWS_S3_REGION
+ GITLAB_QA_OPTS: --omnibus-config object_storage_aws
+ee:object-storage-aws-parallel:
+ extends: ee:object-storage-aws
+ parallel: 2
+ rules:
+ - !reference [ee:object-storage-parallel, rules]
+
+ee:object-storage-gcs:
+ extends: ee:object-storage
+ variables:
+ GCS_BUCKET_NAME: $QA_GCS_BUCKET_NAME
+ GOOGLE_PROJECT: $QA_GOOGLE_PROJECT
+ GOOGLE_JSON_KEY: $QA_GOOGLE_JSON_KEY
+ GOOGLE_CLIENT_EMAIL: $QA_GOOGLE_CLIENT_EMAIL
+ GITLAB_QA_OPTS: --omnibus-config object_storage_gcs
+ee:object-storage-gcs-parallel:
+ extends: ee:object-storage-gcs
+ parallel: 2
+ rules:
+ - !reference [ee:object-storage-parallel, rules]
+
+ee:packages:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Instance::Image
+ QA_RSPEC_TAGS: --tag packages
+ GITLAB_QA_OPTS: --omnibus-config packages
+ rules:
+ - !reference [.rules:test:qa-non-parallel, rules]
+ - if: $QA_SUITES =~ /Test::Instance::Packages/
+ee:packages-parallel:
+ extends: ee:packages
+ parallel: 2
+ rules:
+ - !reference [.rules:test:qa-parallel, rules]
+ - if: $QA_SUITES =~ /Test::Instance::Packages/
+
+# ------------------------------------------
+# Non parallel jobs
+# ------------------------------------------
+ee:update-minor:
+ extends:
+ - .qa
+ - .update-script
+ variables:
+ UPDATE_TYPE: minor
+ QA_RSPEC_TAGS: --tag smoke
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Instance::Smoke/
+
+ee:update-major:
+ extends:
+ - .qa
+ - .update-script
+ variables:
+ UPDATE_TYPE: major
+ QA_RSPEC_TAGS: --tag smoke
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Instance::Smoke/
+
+ee:gitaly-cluster:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Integration::GitalyCluster
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Integration::GitalyCluster/
+
+ee:group-saml:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Integration::GroupSAML
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Integration::GroupSAML/
+
+ee:instance-saml:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Integration::InstanceSAML
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Integration::InstanceSAML/
+
+ee:jira:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Integration::Jira
+ JIRA_ADMIN_USERNAME: $QA_JIRA_ADMIN_USERNAME
+ JIRA_ADMIN_PASSWORD: $QA_JIRA_ADMIN_PASSWORD
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Integration::Jira/
+
+ee:ldap-no-server:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Integration::LDAPNoServer
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Integration::LDAPNoServer/
+
+ee:ldap-tls:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Integration::LDAPTLS
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Integration::LDAPTLS/
+
+ee:ldap-no-tls:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Integration::LDAPNoTLS
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Integration::LDAPNoTLS/
+
+ee:mtls:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Integration::MTLS
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Integration::MTLS/
+
+ee:mattermost:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Integration::Mattermost
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Integration::Mattermost/
+
+ee:registry:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Integration::Registry
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Integration::Registry/
+
+ee:registry-with-cdn:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Integration::RegistryWithCDN
+ GCS_CDN_BUCKET_NAME: $QA_GCS_CDN_BUCKET_NAME
+ GOOGLE_CDN_LB: $QA_GOOGLE_CDN_LB
+ GOOGLE_CDN_JSON_KEY: $QA_GOOGLE_CDN_JSON_KEY
+ GOOGLE_CDN_SIGNURL_KEY: $QA_GOOGLE_CDN_SIGNURL_KEY
+ GOOGLE_CDN_SIGNURL_KEY_NAME: $QA_GOOGLE_CDN_SIGNURL_KEY_NAME
+ before_script:
+ - unset GITLAB_QA_ADMIN_ACCESS_TOKEN
+ - !reference [.qa, before_script]
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Integration::RegistryWithCDN/
+
+ee:repository-storage:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Instance::RepositoryStorage
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Instance::RepositoryStorage/
+
+ee:service-ping-disabled:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Integration::ServicePingDisabled
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Integration::ServicePingDisabled/
+
+ee:smtp:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Integration::SMTP
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Integration::SMTP/
+
+ee:cloud-activation:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Instance::Image
+ QA_RSPEC_TAGS: --tag cloud_activation
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Integration::CloudActivation/
+
+ee:large-setup:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Instance::Image
+ QA_RSPEC_TAGS: --tag can_use_large_setup
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Instance::LargeSetup/
+
+ee:metrics:
+ extends: .qa
+ variables:
+ QA_SCENARIO: Test::Integration::Metrics
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Instance::Metrics/
+
+ee:elasticsearch:
+ extends: .qa
+ variables:
+ QA_SCENARIO: "Test::Integration::Elasticsearch"
+ before_script:
+ - unset ELASTIC_URL # unset url which is globally defined in .gitlab-ci.yml
+ - !reference [.qa, before_script]
+ rules:
+ - !reference [.rules:test:qa, rules]
+ - if: $QA_SUITES =~ /Test::Integration::Elasticsearch/
+
+ee:registry-object-storage-tls:
+ extends: ee:object-storage-aws
+ variables:
+ QA_SCENARIO: Test::Integration::RegistryTLS
+ QA_RSPEC_TAGS: ""
+ GITLAB_TLS_CERTIFICATE: $QA_GITLAB_TLS_CERTIFICATE
+ GITLAB_QA_OPTS: --omnibus-config registry_object_storage
+
+# ==========================================
+# Post test stage
+# ==========================================
+e2e-test-report:
+ extends:
+ - .generate-allure-report-base
+ - .rules:report:allure-report
+ stage: report
+ variables:
+ GITLAB_AUTH_TOKEN: $GITLAB_QA_MR_ALLURE_REPORT_TOKEN
+ ALLURE_PROJECT_PATH: $CI_PROJECT_PATH
+ ALLURE_MERGE_REQUEST_IID: $CI_MERGE_REQUEST_IID
+ ALLURE_JOB_NAME: e2e-package-and-test
+ GIT_STRATEGY: none
+ artifacts: # save rspec results for displaying in parent pipeline
+ expire_in: 1 day
+ when: always
+ paths:
+ - gitlab-qa-run-*/**/rspec-*.xml
+
+upload-knapsack-report:
+ extends:
+ - .generate-knapsack-report-base
+ - .bundle-install
+ - .ruby-image
+ - .rules:report:process-results
+ stage: report
+ when: always
+
+relate-test-failures:
+ extends:
+ - .bundle-install
+ - .ruby-image
+ - .rules:report:process-results
+ stage: report
+ variables:
+ QA_FAILURES_REPORTING_PROJECT: gitlab-org/gitlab
+ QA_FAILURES_MAX_DIFF_RATIO: "0.15"
+ GITLAB_QA_ACCESS_TOKEN: $GITLAB_QA_PRODUCTION_ACCESS_TOKEN
+ when: on_failure
+ script:
+ - |
+ bundle exec gitlab-qa-report \
+ --relate-failure-issue "$CI_PROJECT_DIR/gitlab-qa-run-*/**/rspec-*.json" \
+ --project "$QA_FAILURES_REPORTING_PROJECT" \
+ --max-diff-ratio "$QA_FAILURES_MAX_DIFF_RATIO"
+
+generate-test-session:
+ extends:
+ - .bundle-install
+ - .ruby-image
+ - .rules:report:process-results
+ stage: report
+ variables:
+ QA_TESTCASE_SESSIONS_PROJECT: gitlab-org/quality/testcase-sessions
+ GITLAB_QA_ACCESS_TOKEN: $QA_TEST_SESSION_TOKEN
+ GITLAB_CI_API_TOKEN: $QA_GITLAB_CI_TOKEN
+ when: always
+ script:
+ - |
+ bundle exec gitlab-qa-report \
+ --generate-test-session "$CI_PROJECT_DIR/gitlab-qa-run-*/**/rspec-*.json" \
+ --project "$QA_TESTCASE_SESSIONS_PROJECT"
+ artifacts:
+ when: always
+ expire_in: 1d
+ paths:
+ - qa/REPORT_ISSUE_URL
+
+notify-slack:
+ extends:
+ - .notify-slack-qa
+ - .bundle-install
+ - .ruby-image
+ - .rules:report:process-results
+ stage: notify
+ variables:
+ ALLURE_JOB_NAME: e2e-package-and-test
+ SLACK_ICON_EMOJI: ci_failing
+ STATUS_SYM: ☠ï¸
+ STATUS: failed
+ when: on_failure
+ script:
+ - bundle exec gitlab-qa-report --prepare-stage-reports "$CI_PROJECT_DIR/gitlab-qa-run-*/**/rspec-*.xml" # generate summary
+ - !reference [.notify-slack-qa, script]
diff --git a/.gitlab/ci/package-and-test/rules.gitlab-ci.yml b/.gitlab/ci/package-and-test/rules.gitlab-ci.yml
new file mode 100644
index 00000000000..d866dec1cd0
--- /dev/null
+++ b/.gitlab/ci/package-and-test/rules.gitlab-ci.yml
@@ -0,0 +1,99 @@
+# Specific specs passed
+.specific-specs: &specific-specs
+ if: $QA_TESTS != ""
+
+# No specific specs passed
+.all-specs: &all-specs
+ if: $QA_TESTS == ""
+
+# FF changes
+.feature-flags-set: &feature-flags-set
+ if: $QA_FEATURE_FLAGS != ""
+
+# Only deleted feature flags
+.feature-flags-deleted: &feature-flags-deleted
+ if: $QA_FEATURE_FLAGS != "" && $QA_FEATURE_FLAGS !~ /enabled|disabled/
+
+# Manually trigger job on ff changes but with default ff state instead of inverted
+.feature-flags-set-manual: &feature-flags-set-manual
+ <<: *feature-flags-set
+ when: manual
+ allow_failure: true
+
+# QA framework changes present
+.qa-framework-changes: &qa-framework-changes
+ if: $QA_FRAMEWORK_CHANGES == "true"
+
+# Process test results (notify failure to slack, create test session report, relate test failures)
+.process-test-results: &process-test-results
+ if: $PROCESS_TEST_RESULTS == "true"
+
+# Selective test execution against omnibus instance have following execution scenarios:
+# * only e2e spec files changed - runs only changed specs
+# * qa framework changes - runs full test suite
+# * feature flag changed - runs full test suite with base gitlab instance configuration with both ff states
+# * quarantined e2e spec - skips execution of e2e tests by creating a no-op pipeline
+
+# ------------------------------------------
+# Prepare
+# ------------------------------------------
+.rules:prepare:
+ rules:
+ - when: always
+
+# ------------------------------------------
+# Test
+# ------------------------------------------
+.rules:test:quarantine:
+ rules:
+ - when: manual
+ variables:
+ QA_TESTS: ""
+
+.rules:test:feature-flags-set:
+ rules:
+ # unset specific specs if pipeline has feature flag changes and run full suite
+ - <<: *feature-flags-set
+ variables:
+ QA_TESTS: ""
+
+.rules:test:feature-flags-deleted:
+ rules:
+ - <<: *feature-flags-deleted
+ when: never
+
+# parallel and non parallel rules are used for jobs that require parallel execution and thus need to switch
+# between parallel and non parallel when only certain specs are executed
+.rules:test:qa-non-parallel:
+ rules:
+ # always run parallel with full suite when framework changes present or ff state changed
+ - <<: *qa-framework-changes
+ when: never
+ - <<: *all-specs
+ when: never
+ - <<: *feature-flags-set
+ when: never
+
+.rules:test:qa-parallel:
+ rules:
+ - *qa-framework-changes
+ - <<: *specific-specs
+ when: never
+ - *feature-flags-set-manual
+
+# general qa job rule for jobs without the need to run in parallel
+.rules:test:qa:
+ rules:
+ - *qa-framework-changes
+ - *feature-flags-set-manual
+
+# ------------------------------------------
+# Report
+# ------------------------------------------
+.rules:report:allure-report:
+ rules:
+ - when: always
+
+.rules:report:process-results:
+ rules:
+ - *process-test-results
diff --git a/.gitlab/ci/package-and-test/variables.gitlab-ci.yml b/.gitlab/ci/package-and-test/variables.gitlab-ci.yml
new file mode 100644
index 00000000000..324092c80f4
--- /dev/null
+++ b/.gitlab/ci/package-and-test/variables.gitlab-ci.yml
@@ -0,0 +1,9 @@
+# Default variables for package-and-test
+
+variables:
+ RELEASE: "${REGISTRY_HOST}/${REGISTRY_GROUP}/build/omnibus-gitlab-mirror/gitlab-ee:${CI_COMMIT_SHA}"
+ SKIP_REPORT_IN_ISSUES: "true"
+ OMNIBUS_GITLAB_CACHE_UPDATE: "false"
+ QA_LOG_LEVEL: "info"
+ QA_TESTS: ""
+ QA_FEATURE_FLAGS: ""
diff --git a/.gitlab/ci/qa.gitlab-ci.yml b/.gitlab/ci/qa.gitlab-ci.yml
index a71aac4225e..c5a182b055a 100644
--- a/.gitlab/ci/qa.gitlab-ci.yml
+++ b/.gitlab/ci/qa.gitlab-ci.yml
@@ -1,5 +1,5 @@
.qa-job-base:
- image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-2.7:bundler-2.3-chrome-103-docker-20.10.14
+ image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}
extends:
- .default-retry
- .qa-cache
@@ -57,7 +57,7 @@ qa:selectors-as-if-foss:
- .qa:rules:as-if-foss
- .as-if-foss
-update-qa-cache:
+qa:update-qa-cache:
extends:
- .qa-job-base
- .qa-cache-push
@@ -66,103 +66,40 @@ update-qa-cache:
script:
- echo "Cache has been updated and ready to be uploaded."
-populate-qa-tests-var:
+e2e:package-and-test:
extends:
- - .qa:rules:determine-qa-tests
- image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7-alpine
- stage: prepare
- script:
- - export QA_TESTS=$(scripts/determine-qa-tests --files $CHANGES_FILE --labels "$CI_MERGE_REQUEST_LABELS")
- - 'echo "QA_TESTS=$QA_TESTS" >> qa_tests_var.env'
- - 'echo "QA_TESTS: $QA_TESTS"'
- artifacts:
- expire_in: 2d
- reports:
- dotenv: qa_tests_var.env
- paths:
- - ${CHANGES_FILE}
- - qa_tests_var.env
- variables:
- CHANGES_FILE: tmp/changed_files.txt
- needs:
- - detect-tests
-
-.package-and-qa-base:
- image: ${GITLAB_DEPENDENCY_PROXY}ruby:${RUBY_VERSION}-alpine
+ - .qa:rules:package-and-test
stage: qa
- retry: 0
- before_script:
- - source scripts/utils.sh
- - install_gitlab_gem
- - tooling/bin/find_change_diffs ${CHANGES_DIFFS_DIR}
- script:
- - 'echo "QA_TESTS: $QA_TESTS"'
- - exit_code=0 && tooling/bin/qa/run_qa_check ${CHANGES_DIFFS_DIR} || exit_code=$?
- - echo $exit_code
- - |
- if [ $exit_code -eq 0 ]; then
- ./scripts/trigger-build.rb omnibus
- elif [ $exit_code -eq 1 ]; then
- exit 1
- else
- echo "Downstream jobs will not be triggered because run_qa_check exited with code: $exit_code"
- fi
- # These jobs often time out, so temporarily use private runners and a long timeout: https://gitlab.com/gitlab-org/gitlab/-/issues/238563
- tags:
- - prm
- timeout: 4h
needs:
- - job: build-qa-image
- artifacts: false
- - job: build-assets-image
- artifacts: false
- - job: populate-qa-tests-var
- - detect-tests
- artifacts:
- expire_in: 7d
- paths:
- - ${CHANGES_DIFFS_DIR}/*
+ - build-assets-image
+ - build-qa-image
+ - e2e-test-pipeline-generate
variables:
- CHANGES_DIFFS_DIR: tmp/diffs
- ALLURE_JOB_NAME: $CI_JOB_NAME
-
-.package-and-qa-ff-base:
- script:
- - |
- feature_flags=$(scripts/changed-feature-flags --files $CHANGES_DIFFS_DIR --state $QA_FF_STATE)
- if [[ $feature_flags ]]; then
- export GITLAB_QA_OPTIONS="--set-feature-flags $feature_flags"
- echo $GITLAB_QA_OPTIONS
- ./scripts/trigger-build.rb omnibus
- else
- echo "No changed feature flag found to test as $QA_FF_STATE."
- fi
-
-package-and-qa:
- extends:
- - .package-and-qa-base
- - .qa:rules:package-and-qa
+ SKIP_MESSAGE: Skipping package-and-test due to mr containing only quarantine changes!
+ trigger:
+ strategy: depend
+ include:
+ - artifact: package-and-test-pipeline.yml
+ job: e2e-test-pipeline-generate
-package-and-qa-ff-enabled:
+# Fetch child pipeline test results and store in parent pipeline
+# workaround until natively implemented: https://gitlab.com/groups/gitlab-org/-/epics/8205
+e2e:package-and-test-results:
+ image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3
extends:
- - .package-and-qa-base
- - .package-and-qa-ff-base
- - .qa:rules:package-and-qa:feature-flags
- variables:
- QA_FF_STATE: "enabled"
-
-package-and-qa-ff-disabled:
- extends:
- - .package-and-qa-base
- - .package-and-qa-ff-base
- - .qa:rules:package-and-qa:feature-flags
- variables:
- QA_FF_STATE: "disabled"
-
-package-and-qa-ff-deleted:
- extends:
- - .package-and-qa-base
- - .package-and-qa-ff-base
- - .qa:rules:package-and-qa:feature-flags
+ - .qa-job-base
+ - .qa:rules:package-and-test
+ stage: qa
+ needs:
+ - e2e:package-and-test
variables:
- QA_FF_STATE: "deleted"
+ COLORIZED_LOGS: "true"
+ QA_LOG_LEVEL: "debug"
+ when: always
+ allow_failure: true
+ script:
+ - bundle exec rake "ci:download_test_results[e2e:package-and-test,e2e-test-report,${CI_PROJECT_DIR}]"
+ artifacts:
+ when: always
+ reports:
+ junit: gitlab-qa-run-*/**/rspec-*.xml
diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml
index 50c86313d29..0f524f03188 100644
--- a/.gitlab/ci/rails.gitlab-ci.yml
+++ b/.gitlab/ci/rails.gitlab-ci.yml
@@ -8,11 +8,13 @@
.base-script:
script:
+ - source ./scripts/rspec_helpers.sh
# Only install knapsack after bundle install! Otherwise oddly some native
# gems could not be found under some circumstance. No idea why, hours wasted.
- run_timed_command "gem install knapsack --no-document"
- - run_timed_command "scripts/gitaly-test-spawn"
- - source ./scripts/rspec_helpers.sh
+ - echo -e "\e[0Ksection_start:`date +%s`:gitaly-test-spawn[collapsed=true]\r\e[0KStarting Gitaly"
+ - run_timed_command "scripts/gitaly-test-spawn" # Do not use 'bundle exec' here
+ - echo -e "\e[0Ksection_end:`date +%s`:gitaly-test-spawn\r\e[0K"
.minimal-rspec-tests:
variables:
@@ -129,28 +131,28 @@
############################
# rspec job parallel configs
.rspec-migration-parallel:
- parallel: 9
+ parallel: 12
.rspec-ee-migration-parallel:
- parallel: 3
+ parallel: 4
.rspec-unit-parallel:
- parallel: 22
+ parallel: 28
.rspec-ee-unit-parallel:
- parallel: 16
+ parallel: 18
.rspec-integration-parallel:
- parallel: 10
+ parallel: 12
.rspec-ee-integration-parallel:
- parallel: 4
+ parallel: 6
.rspec-system-parallel:
- parallel: 24
+ parallel: 28
.rspec-ee-system-parallel:
- parallel: 6
+ parallel: 10
# rspec job parallel configs
############################
@@ -165,6 +167,7 @@ setup-test-env:
variables:
SETUP_DB: "false"
script:
+ - echo $CI_MERGE_REQUEST_APPROVED
- source scripts/gitlab_workhorse_component_helpers.sh
- run_timed_command "download_and_extract_gitlab_workhorse_package" || true
- run_timed_command "scripts/setup-test-env"
@@ -175,14 +178,10 @@ setup-test-env:
artifacts:
expire_in: 7d
paths:
- - config/secrets.yml
- ${TMP_TEST_FOLDER}/gitaly/_build/bin/
- - ${TMP_TEST_FOLDER}/gitaly/_build/deps/git/install
- ${TMP_TEST_FOLDER}/gitaly/config.toml
- ${TMP_TEST_FOLDER}/gitaly/gitaly2.config.toml
- ${TMP_TEST_FOLDER}/gitaly/internal/
- - ${TMP_TEST_FOLDER}/gitaly/run/
- - ${TMP_TEST_FOLDER}/gitaly/run2/
- ${TMP_TEST_FOLDER}/gitaly/Makefile
- ${TMP_TEST_FOLDER}/gitaly/praefect.config.toml
- ${TMP_TEST_FOLDER}/gitaly/praefect-db.config.toml
@@ -379,51 +378,19 @@ db:migrate:reset single-db:
- .single-db
- .rails:rules:single-db
-db:migrate-from-previous-major-version:
- extends: .db-job-base
- variables:
- USE_BUNDLE_INSTALL: "false"
- SETUP_DB: "false"
- PROJECT_TO_CHECKOUT: "gitlab-foss"
- TAG_TO_CHECKOUT: "v14.10.2"
- before_script:
- - !reference [.default-before_script, before_script]
- - '[[ -d "ee/" ]] || export PROJECT_TO_CHECKOUT="gitlab"'
- - '[[ -d "ee/" ]] || export TAG_TO_CHECKOUT="${TAG_TO_CHECKOUT}-ee"'
- - retry 'git fetch https://gitlab.com/gitlab-org/$PROJECT_TO_CHECKOUT.git $TAG_TO_CHECKOUT'
- - git checkout -f FETCH_HEAD
- - SETUP_DB=false USE_BUNDLE_INSTALL=true ENABLE_BOOTSNAP=false bash scripts/prepare_build.sh
- - run_timed_command "ENABLE_BOOTSNAP=false bundle exec rake db:drop db:create db:structure:load db:migrate db:seed_fu"
- - git checkout -f $CI_COMMIT_SHA
- - SETUP_DB=false USE_BUNDLE_INSTALL=true bash scripts/prepare_build.sh
- script:
- - run_timed_command "scripts/db_tasks db:migrate"
-
-db:migrate-from-previous-major-version-single-db:
- extends:
- - db:migrate-from-previous-major-version
- - .single-db
- - .rails:rules:single-db
-
-.db:check-schema-base:
+db:check-schema:
extends:
+ - .db-job-base
- .rails:rules:ee-mr-and-default-branch-only
- variables:
- TAG_TO_CHECKOUT: "v14.7.0" # this version updated grpc to 1.42.0, which supports Ruby 2 & 3
script:
+ - run_timed_command "bundle exec rake db:drop db:create"
- run_timed_command "scripts/db_tasks db:migrate"
- - scripts/schema_changed.sh
- - scripts/validate_migration_timestamps
-
-db:check-schema:
- extends:
- - db:migrate-from-previous-major-version
- - .db:check-schema-base
db:check-schema-single-db:
extends:
- - db:migrate-from-previous-major-version-single-db
- - .db:check-schema-base
+ - db:check-schema
+ - .single-db
+ - .rails:rules:single-db
db:check-migrations:
extends:
@@ -624,7 +591,7 @@ rspec:feature-flags:
stage: post-test
needs:
- job: "feature-flags-usage"
- - job: "haml-lint foss"
+ - job: "haml-lint"
- job: "haml-lint ee"
optional: true
script:
@@ -970,7 +937,7 @@ rspec fail-fast:
needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets", "detect-tests"]
script:
- !reference [.base-script, script]
- - rspec_fail_fast tmp/matching_tests.txt "--tag ~quarantine"
+ - rspec_fail_fast "${RSPEC_MATCHING_TESTS_PATH}" "--tag ~quarantine"
artifacts:
expire_in: 7d
paths:
@@ -980,10 +947,10 @@ rspec foss-impact:
extends:
- .rspec-base-pg12-as-if-foss
- .rails:rules:rspec-foss-impact
- needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets as-if-foss", "detect-tests as-if-foss"]
+ needs: ["setup-test-env", "retrieve-tests-metadata", "compile-test-assets as-if-foss", "detect-tests"]
script:
- !reference [.base-script, script]
- - rspec_matched_foss_tests tmp/matching_foss_tests.txt "--tag ~quarantine"
+ - rspec_matched_foss_tests "${RSPEC_MATCHING_TESTS_PATH}" "--tag ~quarantine"
artifacts:
expire_in: 7d
paths:
diff --git a/.gitlab/ci/review-apps/main.gitlab-ci.yml b/.gitlab/ci/review-apps/main.gitlab-ci.yml
index 37ccecc0562..e28ffc82811 100644
--- a/.gitlab/ci/review-apps/main.gitlab-ci.yml
+++ b/.gitlab/ci/review-apps/main.gitlab-ci.yml
@@ -8,6 +8,7 @@ stages:
include:
- local: .gitlab/ci/global.gitlab-ci.yml
- local: .gitlab/ci/rules.gitlab-ci.yml
+ - local: .gitlab/ci/review-apps/rules.gitlab-ci.yml
- local: .gitlab/ci/review-apps/qa.gitlab-ci.yml
- local: .gitlab/ci/review-apps/dast.gitlab-ci.yml
@@ -94,6 +95,7 @@ review-deploy:
- export GITALY_VERSION=$(<GITALY_SERVER_VERSION)
- export GITLAB_WORKHORSE_VERSION=$(<GITLAB_WORKHORSE_VERSION)
- echo "${CI_ENVIRONMENT_URL}" > environment_url.txt
+ - echo "QA_GITLAB_URL=${CI_ENVIRONMENT_URL}" > environment.env
- *base-before_script
script:
- check_kube_domain
@@ -102,7 +104,6 @@ review-deploy:
- deploy || (display_deployment_debug && exit 1)
- verify_deploy || exit 1
- disable_sign_ups || (delete_release && exit 1)
- - create_sample_projects
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.
@@ -110,9 +111,27 @@ review-deploy:
artifacts:
paths:
- environment_url.txt
+ reports:
+ dotenv: environment.env
expire_in: 7 days
when: always
+review-deploy-sample-projects:
+ extends:
+ - .review-workflow-base
+ - .review:rules:review-deploy
+ stage: deploy
+ needs: ["review-deploy"]
+ 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
+ script:
+ - date
+ - create_sample_projects
+
.review-stop-base:
extends: .review-workflow-base
environment:
diff --git a/.gitlab/ci/review-apps/qa.gitlab-ci.yml b/.gitlab/ci/review-apps/qa.gitlab-ci.yml
index 631fe7fef30..21e6a8e42fb 100644
--- a/.gitlab/ci/review-apps/qa.gitlab-ci.yml
+++ b/.gitlab/ci/review-apps/qa.gitlab-ci.yml
@@ -1,6 +1,6 @@
include:
- project: gitlab-org/quality/pipeline-common
- ref: 0.13.0
+ ref: 1.2.2
file:
- /ci/allure-report.yml
- /ci/knapsack-report.yml
@@ -8,85 +8,57 @@ include:
.test-variables:
variables:
QA_GENERATE_ALLURE_REPORT: "true"
- COLORIZED_LOGS: "true"
+ QA_CAN_TEST_PRAEFECT: "false"
GITLAB_USERNAME: "root"
GITLAB_PASSWORD: "${REVIEW_APPS_ROOT_PASSWORD}"
GITLAB_ADMIN_USERNAME: "root"
GITLAB_ADMIN_PASSWORD: "${REVIEW_APPS_ROOT_PASSWORD}"
GITLAB_QA_ADMIN_ACCESS_TOKEN: "${REVIEW_APPS_ROOT_TOKEN}"
- GITHUB_ACCESS_TOKEN: "${REVIEW_APPS_QA_GITHUB_ACCESS_TOKEN}"
+ GITHUB_ACCESS_TOKEN: "${QA_GITHUB_ACCESS_TOKEN}"
.bundle-base:
extends:
- .qa-cache
image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3
before_script:
- - export QA_GITLAB_URL="$(cat environment_url.txt)"
- cd qa && bundle install
.review-qa-base:
+ image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3-git-2.33-lfs-2.9-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}-gcloud-383-kubectl-1.23
extends:
- .use-docker-in-docker
- .bundle-base
- .test-variables
- image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3-git-2.33-lfs-2.9-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}-gcloud-383-kubectl-1.23
stage: qa
needs:
- review-deploy
- download-knapsack-report
variables:
- DOCKER_HOST: tcp://docker:2376
- DOCKER_TLS_CERTDIR: /certs
- DOCKER_CERT_PATH: /certs/client
- DOCKER_TLS_VERIFY: 1
GIT_LFS_SKIP_SMUDGE: 1
WD_INSTALL_DIR: /usr/local/bin
- before_script:
- - scripts/checkout-mr-source-sha
- - !reference [.bundle-base, before_script]
+ RSPEC_REPORT_OPTS: --force-color --order random --format documentation --format RspecJunitFormatter --out tmp/rspec-${CI_JOB_ID}.xml
script:
- export EE_LICENSE="$(cat $REVIEW_APPS_EE_LICENSE_FILE)"
- - qa_run_status=0
+ - QA_COMMAND="bundle exec bin/qa ${QA_SCENARIO} ${QA_GITLAB_URL} -- ${QA_TESTS} ${RSPEC_REPORT_OPTS}"
+ - echo "Running - '${QA_COMMAND}'"
+ - eval "$QA_COMMAND"
+ after_script:
- |
- bundle exec rake "knapsack:rspec[\
- ${RSPEC_TAGS} \
- --tag ~orchestrated \
- --tag ~transient \
- --tag ~skip_signup_disabled \
- --tag ~requires_git_protocol_v2 \
- --tag ~requires_praefect \
- --force-color \
- --order random \
- --format documentation \
- --format RspecJunitFormatter --out tmp/rspec.xml \
- ]" || qa_run_status=$?
- - if [ ${qa_run_status} -ne 0 ]; then
- release_sha=$(echo "${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA:-${CI_COMMIT_SHA}}" | cut -c1-11);
- echo "Errors can be found at https://sentry.gitlab.net/gitlab/gitlab-review-apps/releases/${release_sha}/all-events/.";
- fi
- - exit ${qa_run_status}
+ echo "Sentry errors for the current review-app test run can be found via following url:"
+ echo "https://sentry.gitlab.net/gitlab/gitlab-review-apps/releases/$(echo "${CI_COMMIT_SHA}" | cut -c1-11)/all-events/."
artifacts:
paths:
- qa/tmp
reports:
- junit: qa/tmp/rspec.xml
+ junit: qa/tmp/rspec-*.xml
expire_in: 7 days
when: always
-.allure-report-base:
- extends: .generate-allure-report-base
- stage: post-qa
- variables:
- GITLAB_AUTH_TOKEN: $GITLAB_QA_MR_ALLURE_REPORT_TOKEN
- ALLURE_PROJECT_PATH: $CI_PROJECT_PATH
- ALLURE_MERGE_REQUEST_IID: $CI_MERGE_REQUEST_IID
- ALLURE_RESULTS_GLOB: qa/tmp/allure-results/*
-
# Store knapsack report as artifact so the same report is reused across all jobs
download-knapsack-report:
extends:
- .bundle-base
- - .review:rules:review-qa-reliable
+ - .rules:app-or-qa-framework-changes-or-review-scenarios
stage: prepare
script:
- bundle exec rake "knapsack:download[qa]"
@@ -99,30 +71,39 @@ download-knapsack-report:
review-qa-smoke:
extends:
- .review-qa-base
- - .review:rules:review-qa-smoke
- retry: 1
+ - .rules:qa-smoke
variables:
+ QA_SCENARIO: Test::Instance::Smoke
QA_RUN_TYPE: review-qa-smoke
- RSPEC_TAGS: --tag smoke
+ retry: 1
-review-qa-reliable:
+review-qa-blocking:
extends:
- .review-qa-base
- - .review:rules:review-qa-reliable
+ - .rules:qa-blocking
+ variables:
+ QA_SCENARIO: Test::Instance::ReviewBlocking
+ QA_RUN_TYPE: review-qa-blocking
retry: 1
+review-qa-blocking-parallel:
+ extends:
+ - review-qa-blocking
+ - .rules:qa-blocking-parallel
parallel: 10
- variables:
- QA_RUN_TYPE: review-qa-reliable
- RSPEC_TAGS: --tag reliable --tag sanity_feature_flags
-review-qa-all:
+review-qa-non-blocking:
extends:
- .review-qa-base
- - .review:rules:review-qa-all
- parallel: 5
+ - .rules:qa-non-blocking
variables:
- QA_RUN_TYPE: review-qa-all
- RSPEC_TAGS: --tag ~reliable --tag ~smoke --tag ~sanity_feature_flags
+ QA_SCENARIO: Test::Instance::ReviewNonBlocking
+ QA_RUN_TYPE: review-qa-non-blocking
+ allow_failure: true
+review-qa-non-blocking-parallel:
+ extends:
+ - review-qa-non-blocking
+ - .rules:qa-non-blocking-parallel
+ parallel: 5
review-performance:
extends:
@@ -150,27 +131,25 @@ review-performance:
performance: performance.json
expire_in: 31d
-# Generate single report for both smoke and reliable test jobs
-# Both job types are essentially the same:
-# * always executed
-# * always blocking
-allure-report-qa-blocking:
- extends:
- - .allure-report-base
- - .review:rules:review-qa-blocking-report
- needs:
- - review-qa-smoke
- - review-qa-reliable
- variables:
- ALLURE_JOB_NAME: review-qa-blocking
-
-allure-report-qa-all:
+e2e-test-report:
extends:
- - .allure-report-base
- - .review:rules:review-qa-all-report
- needs: ["review-qa-all"]
+ - .generate-allure-report-base
+ - .rules:app-or-qa-framework-changes-or-review-scenarios
+ stage: post-qa
variables:
- ALLURE_JOB_NAME: review-qa-all
+ ALLURE_JOB_NAME: e2e-review-qa
+ ALLURE_PROJECT_PATH: $CI_PROJECT_PATH
+ ALLURE_RESULTS_GLOB: qa/tmp/allure-results/*
+ ALLURE_MERGE_REQUEST_IID: $CI_MERGE_REQUEST_IID
+ GITLAB_AUTH_TOKEN: $GITLAB_QA_MR_ALLURE_REPORT_TOKEN
+ GIT_STRATEGY: none
+ allow_failure: true
+ when: always
+ artifacts: # re-save rspec results for displaying in parent pipeline
+ expire_in: 1 day
+ when: always
+ paths:
+ - qa/tmp/rspec-*.xml
upload-knapsack-report:
extends:
@@ -183,13 +162,13 @@ upload-knapsack-report:
delete-test-resources:
extends:
- .bundle-base
- - .review:rules:review-qa-cleanup
+ - .rules:app-or-qa-framework-changes-or-review-scenarios
stage: post-qa
variables:
QA_TEST_RESOURCES_FILE_PATTERN: $CI_PROJECT_DIR/qa/tmp/test-resources-*.json
GITLAB_QA_ACCESS_TOKEN: $REVIEW_APPS_ROOT_TOKEN
- COLORIZED_LOGS: "true"
script:
- export GITLAB_ADDRESS="$QA_GITLAB_URL"
- bundle exec rake "test_resources:delete[$QA_TEST_RESOURCES_FILE_PATTERN]"
allow_failure: true
+ when: always
diff --git a/.gitlab/ci/review-apps/rules.gitlab-ci.yml b/.gitlab/ci/review-apps/rules.gitlab-ci.yml
new file mode 100644
index 00000000000..56d3731bb56
--- /dev/null
+++ b/.gitlab/ci/review-apps/rules.gitlab-ci.yml
@@ -0,0 +1,81 @@
+# Specific specs passed
+.specific-specs: &specific-specs
+ if: $QA_TESTS != ""
+
+# No specific specs passed
+.all-specs: &all-specs
+ if: $QA_TESTS == ""
+
+# No specific specs in mr pipeline
+.all-specs-mr: &all-specs-mr
+ if: $CI_MERGE_REQUEST_IID && $QA_TESTS == ""
+ when: manual
+
+# Triggered by change pattern
+.app-changes: &app-changes
+ if: $APP_CHANGE_TRIGGER == "true"
+
+# QA framework changes present
+.qa-framework-changes: &qa-framework-changes
+ if: $QA_FRAMEWORK_CHANGES == "true"
+
+.never-when-qa-framework-changes-or-no-specific-specs:
+ - <<: *qa-framework-changes
+ when: never
+ - <<: *all-specs
+ when: never
+
+.never-when-specific-specs-always-when-qa-framework-changes:
+ - <<: *specific-specs
+ when: never
+ - *qa-framework-changes
+
+# ------------------------------------------
+# Test
+# ------------------------------------------
+.rules:qa-smoke:
+ rules:
+ # always trigger smoke suite if review pipeline got triggered by specific changes in application code
+ - <<: *app-changes
+ variables:
+ QA_TESTS: "" # unset QA_TESTS even if specific tests were inferred from stage label
+ - *qa-framework-changes
+ - if: $QA_SUITES =~ /Test::Instance::Smoke/
+
+.rules:qa-blocking:
+ rules:
+ - <<: *app-changes
+ when: never
+ - !reference [.never-when-qa-framework-changes-or-no-specific-specs]
+ - if: $QA_SUITES =~ /Test::Instance::ReviewBlocking/
+.rules:qa-blocking-parallel:
+ rules:
+ # always trigger blocking suite if review pipeline got triggered by specific changes in application code
+ - <<: *app-changes
+ variables:
+ QA_TESTS: "" # unset QA_TESTS even if specific tests were inferred from stage label
+ - !reference [.never-when-specific-specs-always-when-qa-framework-changes]
+ - if: $QA_SUITES =~ /Test::Instance::ReviewBlocking/
+
+.rules:qa-non-blocking:
+ rules:
+ - !reference [.never-when-qa-framework-changes-or-no-specific-specs]
+ - if: $QA_SUITES =~ /Test::Instance::ReviewNonBlocking/
+.rules:qa-non-blocking-parallel:
+ rules:
+ - !reference [.never-when-specific-specs-always-when-qa-framework-changes]
+ - *all-specs-mr # set full suite to manual when no specific specs passed in mr
+ - if: $QA_SUITES =~ /Test::Instance::ReviewNonBlocking/
+
+# ------------------------------------------
+# Prepare/Report
+# ------------------------------------------
+# if no rules for test execution are matched, pipeline will not have e2e test jobs
+# so we need to skip knapsack, allure and test resource deletion jobs as well
+.rules:app-or-qa-framework-changes-or-review-scenarios:
+ rules:
+ - *app-changes
+ - *qa-framework-changes
+ - if: $QA_SUITES =~ /Test::Instance::Smoke/
+ - if: $QA_SUITES =~ /Test::Instance::ReviewBlocking/
+ - if: $QA_SUITES =~ /Test::Instance::ReviewNonBlocking/
diff --git a/.gitlab/ci/review-apps/skip-qa.gitlab-ci.yml b/.gitlab/ci/review-apps/skip-qa.gitlab-ci.yml
deleted file mode 100644
index 1305673a4d8..00000000000
--- a/.gitlab/ci/review-apps/skip-qa.gitlab-ci.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-stages:
- - review
-
-include:
- - local: .gitlab/ci/global.gitlab-ci.yml
- - local: .gitlab/ci/rules.gitlab-ci.yml
-
-no-op:
- extends:
- - .review:rules:start-review-app-pipeline
- stage: review
- script:
- - echo "Skip Review App because the MR includes only quarantine changes"
diff --git a/.gitlab/ci/review.gitlab-ci.yml b/.gitlab/ci/review.gitlab-ci.yml
index 4f51409d6a8..46e62829394 100644
--- a/.gitlab/ci/review.gitlab-ci.yml
+++ b/.gitlab/ci/review.gitlab-ci.yml
@@ -23,42 +23,13 @@ review-cleanup:
- ruby -rrubygems scripts/review_apps/automated_cleanup.rb
- gcp_cleanup
-review-app-pipeline-generate:
- image: ${GITLAB_DEPENDENCY_PROXY}ruby:${RUBY_VERSION}
- stage: prepare
- extends:
- - .review:rules:start-review-app-pipeline
- artifacts:
- expire_in: 7d
- paths:
- - ${CHANGES_DIFFS_DIR}/*
- - review-app-pipeline.yml
- variables:
- CHANGES_DIFFS_DIR: tmp/diffs
- before_script:
- - source scripts/utils.sh
- - install_gitlab_gem
- - tooling/bin/find_change_diffs ${CHANGES_DIFFS_DIR}
- script:
- - exit_code=0 && tooling/bin/qa/run_qa_check ${CHANGES_DIFFS_DIR} || exit_code=$?
- - |
- if [ $exit_code -eq 0 ]; then
- echo "Review App will use the full pipeline"
- cp .gitlab/ci/review-apps/main.gitlab-ci.yml review-app-pipeline.yml
- elif [ $exit_code -eq 2 ]; then
- echo "Skip Review App because the MR includes only quarantine changes"
- cp .gitlab/ci/review-apps/skip-qa.gitlab-ci.yml review-app-pipeline.yml
- else
- exit $exit_code
- fi
-
start-review-app-pipeline:
extends:
- .review:rules:start-review-app-pipeline
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: review
needs:
- - review-app-pipeline-generate
+ - job: e2e-test-pipeline-generate
- job: build-assets-image
artifacts: false
# These variables are set in the pipeline schedules.
@@ -67,11 +38,36 @@ start-review-app-pipeline:
variables:
SCHEDULE_TYPE: $SCHEDULE_TYPE
DAST_RUN: $DAST_RUN
+ SKIP_MESSAGE: Skipping review-app due to mr containing only quarantine changes!
trigger:
+ strategy: depend
include:
- artifact: review-app-pipeline.yml
- job: review-app-pipeline-generate
- strategy: depend
+ job: e2e-test-pipeline-generate
+
+# Fetch child pipeline test results and store in parent pipeline
+# workaround until natively implemented: https://gitlab.com/groups/gitlab-org/-/epics/8205
+review-app-test-results:
+ image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3
+ stage: review
+ extends:
+ - .qa-cache
+ - .review:rules:start-review-app-pipeline
+ needs:
+ - start-review-app-pipeline
+ variables:
+ COLORIZED_LOGS: "true"
+ QA_LOG_LEVEL: "debug"
+ before_script:
+ - cd qa && bundle install
+ script:
+ - bundle exec rake "ci:download_test_results[start-review-app-pipeline,e2e-test-report,${CI_PROJECT_DIR}]"
+ when: always
+ allow_failure: true
+ artifacts:
+ when: always
+ reports:
+ junit: qa/tmp/rspec-*.xml
danger-review:
extends:
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index fcb853a7bd2..9dc2f5eff23 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -20,13 +20,7 @@
if: '$CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH || $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/ || $CI_COMMIT_REF_NAME =~ /^\d+-\d+-auto-deploy-\d+$/ || $CI_COMMIT_REF_NAME =~ /^security\// || $CI_MERGE_REQUEST_IID || $CI_COMMIT_TAG || $FORCE_GITLAB_CI'
.if-default-branch-refs: &if-default-branch-refs
- if: '$CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'
-
-.if-stable-branch-refs: &if-stable-branch-refs
- if: '$CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/'
-
-.if-default-branch-push: &if-default-branch-push
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "push"'
+ if: '$CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH && $CI_MERGE_REQUEST_IID == null'
.if-auto-deploy-branches: &if-auto-deploy-branches
if: '$CI_COMMIT_BRANCH =~ /^\d+-\d+-auto-deploy-\d+$/'
@@ -130,7 +124,7 @@
.if-dot-com-gitlab-org-and-security-merge-request-and-qa-tests-specified: &if-dot-com-gitlab-org-and-security-merge-request-and-qa-tests-specified
if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE =~ /^gitlab-org($|\/security$)/ && $CI_MERGE_REQUEST_IID && $QA_TESTS'
-.if-dot-com-gitlab-org-and-security-merge-request-manual-ff-package-and-qa: &if-dot-com-gitlab-org-and-security-merge-request-manual-ff-package-and-qa
+.if-dot-com-gitlab-org-and-security-merge-request-manual-ff-package-and-e2e: &if-dot-com-gitlab-org-and-security-merge-request-manual-ff-package-and-e2e
if: '$CI_SERVER_HOST == "gitlab.com" && $CI_PROJECT_NAMESPACE =~ /^gitlab-org($|\/security$)/ && $CI_MERGE_REQUEST_IID && $QA_MANUAL_FF_PACKAGE_AND_QA'
.if-dot-com-gitlab-org-and-security-tag: &if-dot-com-gitlab-org-and-security-tag
@@ -173,6 +167,9 @@
- ".gitlab/ci/frontend.gitlab-ci.yml"
- ".gitlab/ci/build-images.gitlab-ci.yml"
- ".gitlab/ci/qa.gitlab-ci.yml"
+ - ".gitlab/ci/package-and-test/*.yml"
+ - ".gitlab/ci/review-apps/qa.gitlab-ci.yml"
+ - ".gitlab/ci/review-apps/rules.gitlab-ci.yml"
.gitaly-patterns: &gitaly-patterns
- "GITALY_SERVER_VERSION"
@@ -261,7 +258,7 @@
- "Dockerfile.assets"
- "config/**/*.js"
- "vendor/assets/**/*"
- - "{app/assets,app/helpers,app/presenters,app/views,locale,public,spec/frontend,symbol}/**/*"
+ - "{app/assets,app/components,app/helpers,app/presenters,app/views,locale,public,spec/frontend,symbol}/**/*"
.controllers-patterns: &controllers-patterns
- "{,ee/,jh/}{app/controllers}/**/*"
@@ -281,7 +278,7 @@
- "Rakefile"
- "config.ru"
# List explicitly all the app/ dirs that are backend (i.e. all except app/assets).
- - "{,ee/,jh/}{app/channels,app/controllers,app/finders,app/graphql,app/helpers,app/mailers,app/models,app/policies,app/presenters,app/serializers,app/services,app/uploaders,app/validators,app/views,app/workers}/**/*"
+ - "{,ee/,jh/}{app/channels,app/components,app/controllers,app/finders,app/graphql,app/helpers,app/mailers,app/models,app/policies,app/presenters,app/serializers,app/services,app/uploaders,app/validators,app/views,app/workers}/**/*"
- "{,ee/,jh/}{bin,config,db,generator_templates,lib}/**/*"
- "{,ee/,jh/}spec/**/*"
# CI changes
@@ -296,7 +293,7 @@
- "{,jh/}Gemfile.lock"
- "GITLAB_ELASTICSEARCH_INDEXER_VERSION"
# List explicitly all the app/ dirs that are backend (i.e. all except app/assets).
- - "{,ee/,jh/}{app/channels,app/controllers,app/finders,app/graphql,app/helpers,app/mailers,app/models,app/policies,app/presenters,app/serializers,app/services,app/uploaders,app/validators,app/views,app/workers}/**/*"
+ - "{,ee/,jh/}{app/channels,app/components,app/controllers,app/finders,app/graphql,app/helpers,app/mailers,app/models,app/policies,app/presenters,app/serializers,app/services,app/uploaders,app/validators,app/views,app/workers}/**/*"
- "{,ee/,jh/}{bin,config,db,generator_templates,lib}/**/*"
- "{,ee/,jh/}spec/**/*"
@@ -309,6 +306,8 @@
- "{,ee/,jh/}{,spec/}lib/{,ee/,jh/}gitlab/background_migration{,_spec}.rb"
- "{,ee/,jh/}spec/support/helpers/database/**/*"
- "lib/gitlab/markdown_cache/active_record/**/*"
+ - "lib/api/admin/batched_background_migrations.rb"
+ - "spec/requests/api/admin/batched_background_migrations_spec.rb"
- "config/prometheus/common_metrics.yml" # Used by Gitlab::DatabaseImporters::CommonMetrics::Importer
- "{,ee/,jh/}app/models/project_statistics.rb" # Used to calculate sizes in migration specs
# Gitaly has interactions with background migrations: https://gitlab.com/gitlab-org/gitlab/-/issues/336538
@@ -333,7 +332,7 @@
.qa-patterns: &qa-patterns
- ".dockerignore"
- - "qa/**/*"
+ - "{,jh/}qa/**/*"
# Code patterns + .ci-patterns
.code-patterns: &code-patterns
@@ -411,7 +410,7 @@
- ".gitlab/ci/**/*"
# QA changes
- ".dockerignore"
- - "qa/**/*"
+ - "{,jh/}qa/**/*"
# Mapped patterns (see tests.yml)
- "data/whats_new/*.yml"
@@ -445,7 +444,7 @@
- "{,spec/}tooling/**/*"
# QA changes
- ".dockerignore"
- - "qa/**/*"
+ - "{,jh/}qa/**/*"
# Mapped patterns (see tests.yml)
- "data/whats_new/*.yml"
@@ -480,7 +479,7 @@
- "{,spec/}tooling/**/*"
# QA changes
- ".dockerignore"
- - "qa/**/*"
+ - "{,jh/}qa/**/*"
# Workhorse changes
- "GITLAB_WORKHORSE_VERSION"
- "workhorse/**/*"
@@ -491,9 +490,14 @@
.static-analysis-patterns: &static-analysis-patterns
- ".{codeclimate,eslintrc,haml-lint,haml-lint_todo}.yml"
- - ".rubocop.yml"
- - ".rubocop_todo.yml"
+
+.rubocop-patterns: &rubocop-patterns
+ - ".{rubocop,rubocop_todo}.yml"
- ".rubocop_todo/**/*.yml"
+ - "{,ee/,jh/}rubocop/**/*" # We might be changing custom cops
+ - "{,ee/,jh/}Gemfile.lock" # This should include gitlab-styles, rubocop itself, and any plugins we might be using
+ - "lib/gitlab_edition.rb" # This is required in RuboCop::CodeReuseHelpers
+ - ".gitlab/ci/static-analysis.gitlab-ci.yml"
.danger-patterns: &danger-patterns
- "Dangerfile"
@@ -528,6 +532,9 @@
rules:
- <<: *if-merge-request-approved
when: never
+ # Temporarily disabled minimal rspec jobs before and after approval because of https://gitlab.com/gitlab-org/gitlab/-/issues/373064.
+ - <<: *if-merge-request-not-approved
+ when: never
- <<: *if-automated-merge-request
when: never
- <<: *if-security-merge-request
@@ -549,8 +556,6 @@
changes: *backend-patterns
- <<: *if-security-merge-request
changes: *backend-patterns
- - <<: *if-merge-request-not-approved
- when: never
.rails:rules:as-if-foss-migration-unit-integration:minimal-default-rules:
rules:
@@ -580,8 +585,6 @@
changes: *code-backstage-patterns
- <<: *if-security-merge-request
changes: *code-backstage-patterns
- - <<: *if-merge-request-not-approved
- when: never
.rails:rules:system:minimal-default-rules:
rules:
@@ -628,14 +631,16 @@
rules:
- <<: *if-not-ee
when: never
+ - <<: *if-not-canonical-namespace
+ when: never
- <<: *if-merge-request-targeting-stable-branch
- <<: *if-merge-request-labels-run-review-app
- <<: *if-dot-com-gitlab-org-and-security-merge-request
changes: *ci-build-images-patterns
- <<: *if-dot-com-gitlab-org-and-security-merge-request
changes: *code-qa-patterns
- - <<: *if-dot-com-gitlab-org-default-branch
- changes: *code-qa-patterns
+ - <<: *if-auto-deploy-branches
+ - <<: *if-default-branch-or-tag
- <<: *if-dot-com-gitlab-org-schedule
- <<: *if-force-ci
@@ -714,6 +719,14 @@
##################
# Frontend rules #
##################
+
+.frontend:rules:minimal-default-rules:
+ rules:
+ - <<: *if-automated-merge-request
+ when: never
+ - <<: *if-security-merge-request
+ when: never
+
.frontend:rules:compile-production-assets:
rules:
- <<: *if-not-canonical-namespace
@@ -762,31 +775,28 @@
rules:
- !reference [".strict-ee-only-rules", rules]
- !reference [".frontend:rules:default-frontend-jobs-as-if-foss", rules]
- - !reference [".frontend:rules:jest:minimal:as-if-foss", rules]
+ - <<: *if-merge-request
+ changes: *frontend-patterns-for-as-if-foss
.frontend:rules:jest:
rules:
- <<: *if-fork-merge-request
when: never
- <<: *if-merge-request-labels-run-all-jest
- - <<: *if-default-refs
- changes: *core-frontend-patterns
- <<: *if-merge-request
- changes: *ci-patterns
+ changes: [".gitlab/ci/rules.gitlab-ci.yml", ".gitlab/ci/frontend.gitlab-ci.yml"]
- <<: *if-automated-merge-request
changes: *code-backstage-patterns
- <<: *if-security-merge-request
changes: *code-backstage-patterns
- - <<: *if-merge-request-not-approved
- when: never
- - <<: *if-default-refs
+ - <<: *if-default-branch-refs
changes: *code-backstage-patterns
.frontend:rules:jest:minimal:
rules:
- <<: *if-fork-merge-request
changes: *code-backstage-patterns
- - !reference [".rails:rules:minimal-default-rules", rules]
+ - !reference [".frontend:rules:minimal-default-rules", rules]
- <<: *if-merge-request-labels-run-all-jest
when: never
- changes: *core-frontend-patterns
@@ -797,30 +807,24 @@
- <<: *if-merge-request
changes: *code-backstage-patterns
-.frontend:rules:jest:minimal:as-if-foss:
+.frontend:rules:jest:as-if-foss:
rules:
- !reference [".strict-ee-only-rules", rules]
+ - <<: *if-merge-request-labels-as-if-foss
+ - <<: *if-merge-request-labels-run-all-jest
- <<: *if-security-merge-request
changes: *code-backstage-patterns
- when: never
- - <<: *if-merge-request-labels-as-if-foss
- when: never
+
+.frontend:rules:jest:minimal:as-if-foss:
+ rules:
+ - !reference [".strict-ee-only-rules", rules]
+ - !reference [".frontend:rules:minimal-default-rules", rules]
- <<: *if-merge-request-labels-run-all-jest
when: never
- - <<: *if-merge-request-labels-run-all-rspec
- when: never
- - <<: *if-merge-request
- changes: *startup-css-patterns
- when: never
- - <<: *if-merge-request
- changes: *ci-patterns
- when: never
- <<: *if-fork-merge-request
when: never
- <<: *if-merge-request
- changes: *core-frontend-patterns
- - <<: *if-merge-request
- changes: *code-backstage-patterns
+ changes: *frontend-patterns-for-as-if-foss
.frontend:rules:eslint-as-if-foss:
rules:
@@ -829,13 +833,14 @@
- <<: *if-merge-request
changes: *frontend-patterns-for-as-if-foss
-.frontend:rules:ee-mr-and-default-branch-only:
+.frontend:rules:coverage-frontend:
rules:
- <<: *if-not-ee
when: never
+ - <<: *if-merge-request-labels-pipeline-revert
+ when: never
- <<: *if-merge-request
changes: *code-backstage-patterns
- when: always
- <<: *if-default-branch-refs
changes: *code-backstage-patterns
@@ -859,6 +864,9 @@
rules:
- <<: *if-not-canonical-namespace
when: never
+ - <<: *if-default-branch-refs
+ changes: *frontend-build-patterns
+ allow_failure: true
- if: '$DANGER_GITLAB_API_TOKEN && $CI_MERGE_REQUEST_IID && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH'
changes: *frontend-build-patterns
allow_failure: true
@@ -907,7 +915,7 @@
- <<: *if-default-refs
changes: *qa-patterns
-.qa:rules:determine-qa-tests:
+.qa:rules:determine-e2e-tests:
rules:
- <<: *if-not-ee
when: never
@@ -921,14 +929,23 @@
- <<: *if-force-ci
allow_failure: true
-.qa:rules:package-and-qa:
+.qa:rules:package-and-test:
rules:
+ - <<: *if-not-canonical-namespace
+ when: never
- <<: *if-not-ee
when: never
- <<: *if-merge-request-labels-pipeline-revert
when: never
- <<: *if-merge-request-targeting-stable-branch
allow_failure: true
+ - <<: *if-dot-com-gitlab-org-and-security-merge-request-manual-ff-package-and-e2e
+ changes: *feature-flag-development-config-patterns
+ when: manual
+ allow_failure: true
+ - <<: *if-dot-com-gitlab-org-and-security-merge-request
+ changes: *feature-flag-development-config-patterns
+ allow_failure: true
- <<: *if-dot-com-gitlab-org-and-security-merge-request
changes: *nodejs-patterns
allow_failure: true
@@ -947,24 +964,14 @@
allow_failure: true
- <<: *if-dot-com-gitlab-org-schedule
allow_failure: true
+ variables:
+ SKIP_REPORT_IN_ISSUES: "false"
+ PROCESS_TEST_RESULTS: "true"
+ KNAPSACK_GENERATE_REPORT: "true"
- <<: *if-force-ci
when: manual
allow_failure: true
-.qa:rules:package-and-qa:feature-flags:
- rules:
- - <<: *if-not-ee
- when: never
- - <<: *if-merge-request-labels-pipeline-revert
- when: never
- - <<: *if-dot-com-gitlab-org-and-security-merge-request-manual-ff-package-and-qa
- changes: *feature-flag-development-config-patterns
- when: manual
- allow_failure: true
- - <<: *if-dot-com-gitlab-org-and-security-merge-request
- changes: *feature-flag-development-config-patterns
- allow_failure: true
-
###############
# Rails rules #
###############
@@ -1012,8 +1019,6 @@
changes: *db-patterns
- <<: *if-security-merge-request
changes: *db-patterns
- - <<: *if-merge-request-not-approved
- when: never
- changes: *db-patterns
.rails:rules:ee-and-foss-migration:minimal:
@@ -1117,8 +1122,6 @@
changes: *db-patterns
- <<: *if-security-merge-request
changes: *db-patterns
- - <<: *if-merge-request-not-approved
- when: never
- changes: *db-patterns
.rails:rules:ee-only-migration:minimal:
@@ -1208,8 +1211,6 @@
changes: *db-patterns
- <<: *if-security-merge-request
changes: *db-patterns
- - <<: *if-merge-request-not-approved
- when: never
.rails:rules:as-if-foss-migration:minimal:
rules:
@@ -1365,12 +1366,12 @@
rules:
- <<: *if-not-ee
when: never
+ - <<: *if-merge-request-labels-pipeline-revert
+ when: never
- <<: *if-merge-request
changes: *code-backstage-patterns
- when: always
- <<: *if-default-branch-schedule-maintenance
- <<: *if-merge-request-labels-run-all-rspec
- when: always
.rails:rules:rspec-undercoverage:
rules:
@@ -1410,6 +1411,8 @@
rules:
- <<: *if-not-ee
when: never
+ - <<: *if-merge-request-labels-pipeline-revert
+ when: never
- if: '$SKIP_FLAKY_TESTS_AUTOMATICALLY == "true" || $RETRY_FAILED_TESTS_IN_NEW_PROCESS == "true"'
changes: *code-backstage-patterns
when: always
@@ -1421,24 +1424,42 @@
# Static analysis rules #
#########################
-.static-analysis:rules:ee-and-foss:
+.static-analysis:rules:static-analysis:
rules:
- changes: *code-backstage-qa-patterns
- changes: *static-analysis-patterns
-.static-analysis:rules:ee-and-foss-qa:
+.static-analysis:rules:static-verification-with-database:
+ rules:
+ - changes: *code-backstage-qa-patterns
+
+.static-analysis:rules:rubocop:
+ rules:
+ - changes: *rubocop-patterns
+ variables:
+ RUN_ALL_RUBOCOP: "true"
+ - changes: *code-backstage-qa-patterns
+
+.static-analysis:rules:qa:metadata-lint:
rules:
- changes: *qa-patterns
+ - changes: [".gitlab/ci/static-analysis.gitlab-ci.yml"]
+
+.static-analysis:rules:haml-lint:
+ rules:
+ - changes: *rubocop-patterns
- changes: *static-analysis-patterns
+ - changes: *code-backstage-qa-patterns
-.static-analysis:rules:ee:
+.static-analysis:rules:haml-lint-ee:
rules:
- <<: *if-not-ee
when: never
- - changes: *code-backstage-qa-patterns
+ - changes: *rubocop-patterns
- changes: *static-analysis-patterns
+ - changes: *code-backstage-qa-patterns
-.static-analysis:rules:as-if-foss:
+.static-analysis:rules:static-analysis-as-if-foss:
rules:
- <<: *if-not-ee
when: never
@@ -1447,7 +1468,7 @@
- <<: *if-security-merge-request
changes: *code-backstage-qa-patterns
- <<: *if-merge-request
- changes: *ci-patterns
+ changes: [".gitlab/ci/static-analysis.gitlab-ci.yml"]
- <<: *if-merge-request
changes: *static-analysis-patterns
@@ -1461,6 +1482,12 @@
changes: ["vendor/gems/mail-smtp_pool/**/*"]
- <<: *if-merge-request-labels-run-all-rspec
+.vendor:rules:microsoft_graph_mailer:
+ rules:
+ - <<: *if-merge-request
+ changes: ["vendor/gems/microsoft_graph_mailer/**/*"]
+ - <<: *if-merge-request-labels-run-all-rspec
+
.vendor:rules:ipynbdiff:
rules:
- <<: *if-merge-request
@@ -1491,12 +1518,30 @@
changes: ["vendor/gems/omniauth-gitlab/**/*"]
- <<: *if-merge-request-labels-run-all-rspec
+.vendor:rules:omniauth-google-oauth2:
+ rules:
+ - <<: *if-merge-request
+ changes: ["vendor/gems/omniauth-google-oauth2/**/*"]
+ - <<: *if-merge-request-labels-run-all-rspec
+
+.vendor:rules:omniauth-salesforce:
+ rules:
+ - <<: *if-merge-request
+ changes: ["vendor/gems/omniauth-salesforce/**/*"]
+ - <<: *if-merge-request-labels-run-all-rspec
+
.vendor:rules:devise-pbkdf2-encryptable:
rules:
- <<: *if-merge-request
changes: ["vendor/gems/devise-pbkdf2-encryptable/**/*"]
- <<: *if-merge-request-labels-run-all-rspec
+.vendor:rules:bundler-checksum:
+ rules:
+ - <<: *if-merge-request
+ changes: ["vendor/gems/bundler-checksum/**/*"]
+ - <<: *if-merge-request-labels-run-all-rspec
+
##################
# Releases rules #
##################
@@ -1620,6 +1665,13 @@
################
# Review rules #
################
+.review-change-pattern: &review-change-pattern
+ APP_CHANGE_TRIGGER: "true"
+
+# The following rules needs to be the same as the one for .review:rules:review-cleanup
+# except that:
+# - most rules re automatic here (i.e. no `when: manual`) and not allowed to fail (i.e. no `allow_failure: true`) here
+# - several rules have `variables: *review-change-pattern` here
.review:rules:start-review-app-pipeline:
rules:
- <<: *if-not-ee
@@ -1631,12 +1683,16 @@
changes: *ci-review-patterns
- <<: *if-dot-com-gitlab-org-merge-request
changes: *frontend-build-patterns
+ variables: *review-change-pattern
- <<: *if-dot-com-gitlab-org-merge-request
changes: *controllers-patterns
+ variables: *review-change-pattern
- <<: *if-dot-com-gitlab-org-merge-request
changes: *models-patterns
+ variables: *review-change-pattern
- <<: *if-dot-com-gitlab-org-merge-request
changes: *lib-gitlab-patterns
+ variables: *review-change-pattern
- <<: *if-dot-com-gitlab-org-merge-request
changes: *qa-patterns
- <<: *if-dot-com-gitlab-org-merge-request
@@ -1670,54 +1726,44 @@
rules:
- when: on_success
-.review:rules:review-qa-smoke:
- rules:
- - when: on_success
-
-# If the needed job isn't allowed to fail, we need to use `when: always` in
-# order to keep the job always running after it.
-#
-# If the needed job is allowed to fail, we need to use both
-# `when: on_success` and `when: on_failure` in order to keep
-# the job always running after it.
-# Not that if the needed job has `when: on_success` we can use `when: always`
-# for the depending job.
-#
-# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76756
-
-.review:rules:review-qa-reliable:
- rules:
- - when: on_success
-
-# Since `review-qa-reliable` isn't allowed to fail, we need to use `when: always`for `review-qa-reliable-report`.
-.review:rules:review-qa-blocking-report:
- rules:
- - when: always
-
-.review:rules:review-qa-all:
- rules:
- - <<: *if-dot-com-gitlab-org-merge-request
- changes: *code-patterns
- when: manual
- allow_failure: true # manual jobs needs to be allowed to fail, otherwise they block the pipeline
- - when: on_success
- allow_failure: true
-
-# Since `review-qa-all` is allowed to fail (and potentially manual), we need to use `when: on_success` and `when: on_failure` for `review-qa-all-report`.
-.review:rules:review-qa-all-report:
- rules:
- - when: on_success
- - when: on_failure
-
-.review:rules:review-qa-cleanup:
- rules:
- - when: always
-
+# The following rules needs to be the same as the one for .review:rules:start-review-app-pipeline
+# except that:
+# - all rules have `when: manual` and `allow_failure: true` here
.review:rules:review-cleanup:
rules:
- <<: *if-not-ee
when: never
+ - <<: *if-merge-request-labels-pipeline-revert
+ when: never
+ - <<: *if-merge-request-labels-run-review-app
+ when: manual
+ allow_failure: true
+ - <<: *if-dot-com-gitlab-org-merge-request
+ changes: *ci-review-patterns
+ when: manual
+ allow_failure: true
+ - <<: *if-dot-com-gitlab-org-merge-request
+ changes: *frontend-build-patterns
+ when: manual
+ allow_failure: true
+ - <<: *if-dot-com-gitlab-org-merge-request
+ changes: *controllers-patterns
+ when: manual
+ allow_failure: true
+ - <<: *if-dot-com-gitlab-org-merge-request
+ changes: *models-patterns
+ when: manual
+ allow_failure: true
+ - <<: *if-dot-com-gitlab-org-merge-request
+ changes: *lib-gitlab-patterns
+ when: manual
+ allow_failure: true
- <<: *if-dot-com-gitlab-org-merge-request
+ changes: *qa-patterns
+ when: manual
+ allow_failure: true
+ - <<: *if-dot-com-gitlab-org-merge-request
+ changes: *code-patterns
when: manual
allow_failure: true
- <<: *if-dot-com-gitlab-org-schedule
@@ -1754,6 +1800,10 @@
when: never
- <<: *if-default-branch-or-tag
changes: *code-backstage-qa-patterns
+ - <<: *if-dot-com-gitlab-org-merge-request
+ changes: [".gitlab/ci/setup.gitlab-ci.yml"]
+ when: manual
+ allow_failure: true
.setup:rules:dont-interrupt-me:
rules:
diff --git a/.gitlab/ci/setup.gitlab-ci.yml b/.gitlab/ci/setup.gitlab-ci.yml
index 17113b1245c..4f3111de2bf 100644
--- a/.gitlab/ci/setup.gitlab-ci.yml
+++ b/.gitlab/ci/setup.gitlab-ci.yml
@@ -3,16 +3,20 @@
cache gems:
extends:
- .default-retry
- - .rails-cache
+ - .ruby-cache
- .default-before_script
- .setup:rules:cache-gems
- stage: test
- needs: ["setup-test-env"]
+ stage: prepare
+ needs: []
variables:
- BUNDLE_INSTALL_FLAGS: --with=production --with=development --with=test --jobs=2 --path=vendor --retry=3 --quiet
+ BUNDLE_WITHOUT: ""
+ BUNDLE_WITH: "production:development:test"
SETUP_DB: "false"
script:
- - bundle package --all --all-platforms
+ - echo -e "\e[0Ksection_start:`date +%s`:bundle-package[collapsed=true]\r\e[0KPackaging gems"
+ - bundle config set cache_all true
+ - run_timed_command "bundle package --all-platforms"
+ - echo -e "\e[0Ksection_end:`date +%s`:bundle-package\r\e[0K"
artifacts:
paths:
- vendor/cache
@@ -110,10 +114,13 @@ generate-frontend-fixtures-mapping:
paths:
- ${FRONTEND_FIXTURES_MAPPING_PATH}
-.detect-test-base:
+detect-tests:
+ extends: .rails:rules:detect-tests
image: ${GITLAB_DEPENDENCY_PROXY}ruby:${RUBY_VERSION}
needs: []
stage: prepare
+ variables:
+ RSPEC_TESTS_MAPPING_ENABLED: "true"
script:
- source ./scripts/utils.sh
- source ./scripts/rspec_helpers.sh
@@ -123,42 +130,23 @@ generate-frontend-fixtures-mapping:
- retrieve_frontend_fixtures_mapping
- |
if [ -n "$CI_MERGE_REQUEST_IID" ]; then
- tooling/bin/find_changes ${CHANGES_FILE};
- tooling/bin/find_tests ${CHANGES_FILE} ${MATCHED_TESTS_FILE};
- tooling/bin/find_changes ${CHANGES_FILE} ${MATCHED_TESTS_FILE} ${FRONTEND_FIXTURES_MAPPING_PATH};
- echo "Changed files: $(cat $CHANGES_FILE)";
- echo "Related rspec tests: $(cat $MATCHED_TESTS_FILE)";
+ mkdir -p $(dirname "$RSPEC_CHANGED_FILES_PATH")
+ tooling/bin/find_changes ${RSPEC_CHANGED_FILES_PATH};
+ tooling/bin/find_tests ${RSPEC_CHANGED_FILES_PATH} ${RSPEC_MATCHING_TESTS_PATH};
+ tooling/bin/find_changes ${RSPEC_CHANGED_FILES_PATH} ${RSPEC_MATCHING_TESTS_PATH} ${FRONTEND_FIXTURES_MAPPING_PATH};
+ echo "Changed files: $(cat $RSPEC_CHANGED_FILES_PATH)";
+ echo "Related rspec tests: $(cat $RSPEC_MATCHING_TESTS_PATH)";
fi
artifacts:
expire_in: 7d
paths:
- - ${CHANGES_FILE}
- - ${MATCHED_TESTS_FILE}
+ - ${RSPEC_CHANGED_FILES_PATH}
+ - ${RSPEC_MATCHING_TESTS_PATH}
- ${FRONTEND_FIXTURES_MAPPING_PATH}
-detect-tests:
- extends:
- - .detect-test-base
- - .rails:rules:detect-tests
- variables:
- RSPEC_TESTS_MAPPING_ENABLED: "true"
- CHANGES_FILE: tmp/changed_files.txt
- MATCHED_TESTS_FILE: tmp/matching_tests.txt
-
-detect-tests as-if-foss:
- extends:
- - .detect-test-base
- - .rails:rules:detect-tests
- - .as-if-foss
- variables:
- CHANGES_FILE: tmp/changed_foss_files.txt
- MATCHED_TESTS_FILE: tmp/matching_foss_tests.txt
- before_script:
- - '[ "$FOSS_ONLY" = "1" ] && rm -rf ee/ qa/spec/ee/ qa/qa/specs/features/ee/ qa/qa/ee/ qa/qa/ee.rb'
-
detect-previous-failed-tests:
extends:
- - .detect-test-base
+ - detect-tests
- .rails:rules:detect-previous-failed-tests
variables:
PREVIOUS_FAILED_TESTS_DIR: tmp/previous_failed_tests/
@@ -172,3 +160,23 @@ detect-previous-failed-tests:
expire_in: 7d
paths:
- ${PREVIOUS_FAILED_TESTS_DIR}
+
+e2e-test-pipeline-generate:
+ extends:
+ - .qa-job-base
+ - .minimal-job
+ - .qa:rules:determine-e2e-tests
+ stage: prepare
+ variables:
+ ENV_FILE: $CI_PROJECT_DIR/qa_tests_vars.env
+ OMNIBUS_PIPELINE_YML: package-and-test-pipeline.yml
+ REVIEW_PIPELINE_YML: review-app-pipeline.yml
+ COLORIZED_LOGS: "true"
+ script:
+ - bundle exec rake "ci:detect_changes[$ENV_FILE]"
+ - cd $CI_PROJECT_DIR && scripts/generate-e2e-pipeline
+ artifacts:
+ expire_in: 1 day
+ paths:
+ - $OMNIBUS_PIPELINE_YML
+ - $REVIEW_PIPELINE_YML
diff --git a/.gitlab/ci/static-analysis.gitlab-ci.yml b/.gitlab/ci/static-analysis.gitlab-ci.yml
index b4efd9e49bf..e845e7eb88b 100644
--- a/.gitlab/ci/static-analysis.gitlab-ci.yml
+++ b/.gitlab/ci/static-analysis.gitlab-ci.yml
@@ -19,13 +19,16 @@ update-static-analysis-cache:
- .shared:rules:update-cache
stage: prepare
script:
- - run_timed_command "bundle exec rubocop --parallel" # For the moment we only cache `tmp/rubocop_cache` so we don't need to run all the tasks.
+ # 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"
static-analysis:
extends:
- .static-analysis-base
- .static-analysis-cache
- - .static-analysis:rules:ee-and-foss
+ - .static-analysis:rules:static-analysis
parallel: 2
script:
- run_timed_command "retry yarn install --frozen-lockfile"
@@ -34,14 +37,14 @@ static-analysis:
static-analysis as-if-foss:
extends:
- static-analysis
- - .static-analysis:rules:as-if-foss
+ - .static-analysis:rules:static-analysis-as-if-foss
- .as-if-foss
static-verification-with-database:
extends:
- .static-analysis-base
- .rubocop-job-cache
- - .static-analysis:rules:ee-and-foss
+ - .static-analysis:rules:static-verification-with-database
- .use-pg12
script:
- bundle exec rake lint:static_verification_with_database
@@ -91,13 +94,13 @@ eslint as-if-foss:
- .as-if-foss
needs: ['generate-apollo-graphql-schema as-if-foss']
-haml-lint foss:
+haml-lint:
extends:
- .static-analysis-base
- .ruby-cache
- - .static-analysis:rules:ee-and-foss
+ - .static-analysis:rules:haml-lint
script:
- - run_timed_command "bin/rake 'haml_lint[app/views]'"
+ - run_timed_command "bundle exec haml-lint --parallel app/views"
artifacts:
expire_in: 31d
when: always
@@ -106,23 +109,37 @@ haml-lint foss:
haml-lint ee:
extends:
- - "haml-lint foss"
- - .static-analysis:rules:ee
+ - "haml-lint"
+ - .static-analysis:rules:haml-lint-ee
script:
- - run_timed_command "bin/rake 'haml_lint[ee/app/views]'"
+ - run_timed_command "bundle exec haml-lint --parallel ee/app/views"
rubocop:
extends:
- .static-analysis-base
- .rubocop-job-cache
- - .static-analysis:rules:ee-and-foss
+ - .static-analysis:rules:rubocop
+ needs:
+ - job: detect-tests
+ optional: true
script:
- - run_timed_command "bundle exec rubocop --parallel"
+ - |
+ # For non-merge request, or when RUN_ALL_RUBOCOP is 'true', run all RuboCop rules
+ if [ -z "${CI_MERGE_REQUEST_IID}" ] || [ "${RUN_ALL_RUBOCOP}" == "true" ]; then
+ # Silence cop offenses for rules with "grace period".
+ # 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"
+ else
+ cat ${RSPEC_CHANGED_FILES_PATH} | ruby -e 'puts $stdin.read.split(" ").select { |f| File.exist?(f) }.join(" ")' > tmp/rubocop_target_files.txt
+ run_timed_command "bundle exec rubocop --parallel --force-exclusion $(cat tmp/rubocop_target_files.txt)"
+ fi
qa:metadata-lint:
extends:
- .static-analysis-base
- - .static-analysis:rules:ee-and-foss-qa
+ - .static-analysis:rules:qa:metadata-lint
before_script:
- !reference [.default-before_script, before_script]
- cd qa/
@@ -149,7 +166,7 @@ feature-flags-usage:
extends:
- .static-analysis-base
- .rubocop-job-cache
- - .static-analysis:rules:ee-and-foss
+ - .static-analysis:rules:rubocop
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.
diff --git a/.gitlab/ci/vendored-gems.gitlab-ci.yml b/.gitlab/ci/vendored-gems.gitlab-ci.yml
index 4408a6e4624..577bd37ca9e 100644
--- a/.gitlab/ci/vendored-gems.gitlab-ci.yml
+++ b/.gitlab/ci/vendored-gems.gitlab-ci.yml
@@ -6,6 +6,14 @@ vendor mail-smtp_pool:
include: vendor/gems/mail-smtp_pool/.gitlab-ci.yml
strategy: depend
+vendor microsoft_graph_mailer:
+ extends:
+ - .vendor:rules:microsoft_graph_mailer
+ needs: []
+ trigger:
+ include: vendor/gems/microsoft_graph_mailer/.gitlab-ci.yml
+ strategy: depend
+
vendor ipynbdiff:
extends:
- .vendor:rules:ipynbdiff
@@ -46,6 +54,22 @@ vendor omniauth-gitlab:
include: vendor/gems/omniauth-gitlab/.gitlab-ci.yml
strategy: depend
+vendor omniauth-google-oauth2:
+ extends:
+ - .vendor:rules:omniauth-google-oauth2
+ needs: []
+ trigger:
+ include: vendor/gems/omniauth-google-oauth2/.gitlab-ci.yml
+ strategy: depend
+
+vendor omniauth-salesforce:
+ extends:
+ - .vendor:rules:omniauth-salesforce
+ needs: []
+ trigger:
+ include: vendor/gems/omniauth-salesforce/.gitlab-ci.yml
+ strategy: depend
+
vendor devise-pbkdf2-encryptable:
extends:
- .vendor:rules:devise-pbkdf2-encryptable
@@ -53,3 +77,11 @@ vendor devise-pbkdf2-encryptable:
trigger:
include: vendor/gems/devise-pbkdf2-encryptable/.gitlab-ci.yml
strategy: depend
+
+vendor bundler-checksum:
+ extends:
+ - .vendor:rules:bundler-checksum
+ needs: []
+ trigger:
+ include: vendor/gems/bundler-checksum/.gitlab-ci.yml
+ strategy: depend
diff --git a/.gitlab/ci/workhorse.gitlab-ci.yml b/.gitlab/ci/workhorse.gitlab-ci.yml
index ade2f65441f..efd37b2247b 100644
--- a/.gitlab/ci/workhorse.gitlab-ci.yml
+++ b/.gitlab/ci/workhorse.gitlab-ci.yml
@@ -9,23 +9,38 @@ workhorse:verify:
.workhorse:test:
extends: .workhorse:rules:workhorse
+ image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}-golang-${GO_VERSION}:git-2.36
variables:
GITALY_ADDRESS: "tcp://127.0.0.1:8075"
+ GO_VERSION: "1.17"
stage: test
needs:
- setup-test-env
- script:
+ before_script:
- go version
- apt-get update && apt-get -y install libimage-exiftool-perl
- scripts/gitaly-test-build
+ script:
- make -C workhorse test
-workhorse:test using go 1.17:
+workhorse:test go:
extends: .workhorse:test
- image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}-golang-1.17-git-2.31
+ parallel:
+ matrix:
+ - GO_VERSION: ["1.17", "1.18"]
+ script:
+ - make -C workhorse test-coverage
+ coverage: '/\d+.\d+%/'
+ artifacts:
+ paths:
+ - workhorse/coverage.html
-workhorse:test using go 1.17 with FIPS:
+workhorse:test fips:
extends: .workhorse:test
variables:
WORKHORSE_TEST_FIPS_ENABLED: 1
- image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:debian-${DEBIAN_VERSION}-ruby-${RUBY_VERSION}-golang-1.17-git-2.31
+
+workhorse:test race:
+ extends: .workhorse:test
+ script:
+ - make -C workhorse test-race
diff --git a/.gitlab/issue_templates/Audit Event Proposal.md b/.gitlab/issue_templates/Audit Event Proposal.md
index af32845bd9a..09b0a6761bc 100644
--- a/.gitlab/issue_templates/Audit Event Proposal.md
+++ b/.gitlab/issue_templates/Audit Event Proposal.md
@@ -1,4 +1,5 @@
<!-- Audit Event documentation: See https://docs.gitlab.com/ee/administration/audit_events.html -->
+<!-- Streaming Audit Event documentation: See https://docs.gitlab.com/ee/administration/audit_event_streaming.html -->
## Audit need
@@ -8,6 +9,11 @@
<!-- Describe the audit event you are proposing should be added, including any details of what should be captured, how, and why. -->
+### Streaming-only event or normal event?
+
+<!-- Should this event be a streaming-only audit event or also logged to GitLab's database? Consider the
+volume of data that will be generated by the event when answering this. -->
+
/label ~"Category:Audit Events"
/label ~"type::feature"
/label ~"group::compliance"
diff --git a/.gitlab/issue_templates/Deprecations.md b/.gitlab/issue_templates/Deprecations.md
index 3dfed1a1fc1..76c66a3a891 100644
--- a/.gitlab/issue_templates/Deprecations.md
+++ b/.gitlab/issue_templates/Deprecations.md
@@ -46,7 +46,7 @@ Which tier is this feature available in?
Please add links to the relevant merge requests.
- As soon as possible, but no later than the third milestone preceding the major release (for example, given the following release schedule: `14.8, 14.9, 14.10, 15.0` – `14.8` is the third milestone preceding the major release):
- - [ ] A [deprecation entry](https://about.gitlab.com/handbook/marketing/blog/release-posts/#creating-a-deprecation-entry) has been created so the deprecation will appear in release posts and on the [general deprecation page](https://docs.gitlab.com/ee/update/deprecations).
+ - [ ] A [deprecation announcement entry](https://about.gitlab.com/handbook/marketing/blog/release-posts/#creating-a-deprecation-announcement) has been created so the deprecation will appear in release posts and on the [general deprecation page](https://docs.gitlab.com/ee/update/deprecations).
- [ ] Documentation has been updated to mark the feature as [deprecated](https://docs.gitlab.com/ee/development/documentation/versions.html#deprecations-and-removals).
- [ ] On or before the major milestone: A [removal entry](https://about.gitlab.com/handbook/marketing/blog/release-posts/#removals) has been created so the removal will appear on the [removals by milestones](https://docs.gitlab.com/ee/update/removals) page and be announced in the release post.
- On the major milestone:
diff --git a/.gitlab/issue_templates/Doc_cleanup.md b/.gitlab/issue_templates/Doc_cleanup.md
index 58a51e15803..79cf2662b07 100644
--- a/.gitlab/issue_templates/Doc_cleanup.md
+++ b/.gitlab/issue_templates/Doc_cleanup.md
@@ -7,18 +7,27 @@
* feature development should use the Feature Request template.
-->
-If you are a community contributor:
+If you are a community contributor, **do not work on the issue if it is not assigned to you yet**.
-1. To work on an issue, type `@gl-docsteam I would like to work on this issue.`
- in a comment. A technical writer
- will assign the issue to you. Do not work on the issue before it is assigned to you.
- If someone has already chosen the issue, pick another or view docs [in the docs directory](https://gitlab.com/gitlab-org/gitlab/-/tree/master/doc)
+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. Create a merge request for the issue. 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. If you were not
- assigned the issue, do not create a merge request. It will not be accepted.
-1. Copy the link to this issue and add it to the merge request's description, which will link
- the merge request and the issue together.
+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.
If you notice things you'd like to fix that are not part of the issue, open separate merge requests for those issues.
@@ -37,6 +46,9 @@ Thank you again for contributing to the GitLab documentation!
* 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
diff --git a/.gitlab/issue_templates/Service Ping reporting and monitoring.md b/.gitlab/issue_templates/Service Ping reporting and monitoring.md
index 9a30f71e42b..baa384a8aa2 100644
--- a/.gitlab/issue_templates/Service Ping reporting and monitoring.md
+++ b/.gitlab/issue_templates/Service Ping reporting and monitoring.md
@@ -27,7 +27,7 @@ Broken metrics issues are marked with the ~"broken metric" label.
1. Note which bastion host machine was assigned. For example: `<username>@bastion-01-inf-gprd.c.gitlab-production.internal:~$` shows that you are connected to `bastion-01-inf-gprd.c.gitlab-production.internal`.
1. Create a named screen: `screen -S $USER-service-ping-$(date +%F)`.
1. Connect to the console host: `ssh $USER-rails@console-01-sv-gprd.c.gitlab-production.internal`.
-1. Run: `ServicePing::SubmitService.new.execute`.
+1. Run: `GitlabServicePingWorker.new.perform('triggered_from_cron' => false)`.
1. Press <kbd>Control</kbd>+<kbd>a</kbd> followed by <kbd>Control</kbd>+<kbd>d</kbd> to detach from the screen session.
1. Exit from the bastion: `exit`.
@@ -58,12 +58,12 @@ OR
## Service Ping process triggering (through a long-running SSH session)
1. Connect to the `gprd` Rails console.
-1. Run `SubmitUsagePingService.new.execute`. This process requires more than 30 hours to complete.
+1. Run `GitlabServicePingWorker.new.perform('triggered_from_cron' => false)`. This process requires more than 30 hours to complete.
1. Find the last payload in the `raw_usage_data` table: `RawUsageData.last.payload`.
1. Check the when the payload was sent: `RawUsageData.last.sent_at`.
```plaintext
-ServicePing::SubmitService.new.execute
+GitlabServicePingWorker.new.perform('triggered_from_cron' => false)
# Get the payload
RawUsageData.last.payload
diff --git a/.gitlab/merge_request_templates/Deprecations.md b/.gitlab/merge_request_templates/Deprecations.md
index fc803152efb..dcd5d79e0d0 100644
--- a/.gitlab/merge_request_templates/Deprecations.md
+++ b/.gitlab/merge_request_templates/Deprecations.md
@@ -30,8 +30,12 @@ Deprecation announcements can and should be created and merged into Docs at any
---
-Please review the [guidelines for deprecations](https://about.gitlab.com/handbook/marketing/blog/release-posts/#deprecations),
-as well as the process for [creating a deprecation entry](https://about.gitlab.com/handbook/marketing/blog/release-posts/#creating-a-deprecation-entry).
+Please review:
+
+- The definitions of ["Deprecation", "End of Support", and "Removal"](https://docs.gitlab.com/ee/development/deprecation_guidelines/#terminology).
+- The [guidelines for deprecations](https://about.gitlab.com/handbook/marketing/blog/release-posts/#deprecations).
+- The process for [creating a deprecation entry](https://about.gitlab.com/handbook/marketing/blog/release-posts/#creating-a-deprecation-entry).
+
They are frequently updated, and everyone should make sure they are aware of the current standards (PM, PMM, EM, and TW).
## EM/PM release post item checklist
diff --git a/.gitlab/merge_request_templates/Quarantine End to End Test.md b/.gitlab/merge_request_templates/Quarantine End to End Test.md
index a8d2378eee0..c088fde857a 100644
--- a/.gitlab/merge_request_templates/Quarantine End to End Test.md
+++ b/.gitlab/merge_request_templates/Quarantine End to End Test.md
@@ -26,7 +26,6 @@ the noise (due to constantly failing tests, flaky tests, and so on) so that new
- [ ] Dequarantine test check-list
- [ ] Follow the [Dequarantining Tests guide](https://about.gitlab.com/handbook/engineering/quality/quality-engineering/debugging-qa-test-failures/#dequarantining-tests).
- [ ] Confirm the test consistently passes on the target GitLab environment(s).
- - [ ] (Optionally) [Trigger a manual GitLab-QA pipeline](https://about.gitlab.com/handbook/engineering/quality/quality-engineering/tips-and-tricks/#running-gitlab-qa-pipeline-against-a-specific-gitlab-release) against a specific GitLab environment using the `RELEASE` variable from the `package-and-qa` job of the current merge request.
- [ ] To ensure a faster turnaround, ask in the `#quality` Slack channel for someone to review and merge the merge request, rather than assigning it directly.
<!-- Base labels. -->
diff --git a/.gitlab/merge_request_templates/Removals.md b/.gitlab/merge_request_templates/Removals.md
index 0b7f1efe006..4801af2a123 100644
--- a/.gitlab/merge_request_templates/Removals.md
+++ b/.gitlab/merge_request_templates/Removals.md
@@ -36,7 +36,10 @@ If the removal creates a [breaking change](https://about.gitlab.com/handbook/pro
---
-Please review the [guidelines for removals](https://about.gitlab.com/handbook/marketing/blog/release-posts/#removals).
+Please review:
+
+- The definitions of ["Deprecation", "End of Support", and "Removal"](https://docs.gitlab.com/ee/development/deprecation_guidelines/#terminology).
+- The [guidelines for removals](https://about.gitlab.com/handbook/marketing/blog/release-posts/#removals).
## EM/PM release post item checklist
diff --git a/.gitlab/merge_request_templates/Security Release.md b/.gitlab/merge_request_templates/Security Release.md
index f43bddaa46e..14130ca42c2 100644
--- a/.gitlab/merge_request_templates/Security Release.md
+++ b/.gitlab/merge_request_templates/Security Release.md
@@ -21,7 +21,7 @@ See [the general developer security release guidelines](https://gitlab.com/gitla
- [ ] Ensure it's approved according to our [Approval Guidelines].
- [ ] Ensure it's approved by an AppSec engineer.
- Please see the security release [Code reviews and Approvals] documentation for details on which AppSec team member to ping for approval.
- - Trigger the [`package-and-qa` build]. The docker image generated will be used by the AppSec engineer to validate the security vulnerability has been remediated.
+ - Trigger the [`e2e:package-and-test` job]. The docker image generated will be used by the AppSec engineer to validate the security vulnerability has been remediated.
- [ ] 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.
@@ -42,4 +42,4 @@ See [the general developer security release guidelines](https://gitlab.com/gitla
[Code reviews and Approvals]: (https://gitlab.com/gitlab-org/release/docs/blob/master/general/security/developer.md#code-reviews-and-approvals)
[Approval Guidelines]: https://docs.gitlab.com/ee/development/code_review.html#approval-guidelines
[Canonical repository]: https://gitlab.com/gitlab-org/gitlab
-[`package-and-qa` build]: https://docs.gitlab.com/ee/development/testing_guide/end_to_end/#using-the-package-and-qa-job
+[`e2e:package-and-test` job]: https://docs.gitlab.com/ee/development/testing_guide/end_to_end/#using-the-package-and-test-job
diff --git a/.gitlab/secret-detection-ruleset.toml b/.gitlab/secret-detection-ruleset.toml
new file mode 100644
index 00000000000..5fcde57cad7
--- /dev/null
+++ b/.gitlab/secret-detection-ruleset.toml
@@ -0,0 +1,6 @@
+[secrets]
+ [[secrets.ruleset]]
+ disable = true
+ [secrets.ruleset.identifier]
+ type = "gitleaks_rule_id"
+ value = "Password in URL"
diff --git a/.markdownlint.yml b/.markdownlint.yml
index a5f74890815..2ad24e5f754 100644
--- a/.markdownlint.yml
+++ b/.markdownlint.yml
@@ -23,7 +23,8 @@ first-line-h1: false
code-block-style:
style: "fenced"
emphasis-style: false
-strong-style: false
+link-fragments: false
+reference-links-images: false
proper-names:
names: [
"Akismet",
diff --git a/.rubocop.yml b/.rubocop.yml
index 8ddd5087bb7..7fe3fc35b66 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -44,7 +44,7 @@ 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: 30000
+ MaxFilesInCache: 35000
Metrics/ParameterLists:
Exclude:
@@ -566,21 +566,8 @@ Graphql/Descriptions:
RSpec/ImplicitSubject:
Enabled: false
-RSpec/DescribedClass:
- Enabled: false
-
-RSpec/SharedExamples:
- Enabled: false
-
-RSpec/EmptyLineAfterExampleGroup:
- Enabled: false
-
-RSpec/ReceiveNever:
- Enabled: false
-
-RSpec/MissingExampleGroupArgument:
- Enabled: false
-
+# Already covered by `RSpec::Configuration#on_potential_false_positives = :raise`.
+# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86429
RSpec/UnspecifiedException:
Enabled: false
@@ -612,9 +599,6 @@ Style/MultilineWhenThen:
Style/NumericPredicate:
EnforcedStyle: comparison
-Style/FloatDivision:
- Enabled: false
-
Cop/BanCatchThrow:
Enabled: true
diff --git a/.rubocop_todo/cop/user_admin.yml b/.rubocop_todo/cop/user_admin.yml
index c9eed57e74d..653865e3d43 100644
--- a/.rubocop_todo/cop/user_admin.yml
+++ b/.rubocop_todo/cop/user_admin.yml
@@ -52,7 +52,6 @@ Cop/UserAdmin:
- lib/api/entities/ci/runner_details.rb
- lib/api/groups.rb
- lib/api/helpers.rb
- - lib/api/personal_access_tokens.rb
- lib/api/users.rb
- lib/api/v3/github.rb
- lib/constraints/admin_constrainer.rb
diff --git a/.rubocop_todo/database/multiple_databases.yml b/.rubocop_todo/database/multiple_databases.yml
index 07d8ce12fa8..86db4e0c91f 100644
--- a/.rubocop_todo/database/multiple_databases.yml
+++ b/.rubocop_todo/database/multiple_databases.yml
@@ -7,7 +7,6 @@ Database/MultipleDatabases:
- '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'
- - 'lib/gitlab/background_migration/copy_ci_builds_columns_to_security_scans.rb'
- 'spec/db/schema_spec.rb'
- 'spec/initializers/database_config_spec.rb'
- 'spec/lib/gitlab/database_spec.rb'
diff --git a/.rubocop_todo/gitlab/feature_available_usage.yml b/.rubocop_todo/gitlab/feature_available_usage.yml
index 92aad8b990e..0daacdfe2b1 100644
--- a/.rubocop_todo/gitlab/feature_available_usage.yml
+++ b/.rubocop_todo/gitlab/feature_available_usage.yml
@@ -41,7 +41,7 @@ Gitlab/FeatureAvailableUsage:
- ee/app/helpers/ee/search_helper.rb
- ee/app/helpers/ee/tree_helper.rb
- ee/app/models/approval_state.rb
- - ee/app/models/concerns/approvable.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
@@ -140,6 +140,7 @@ Gitlab/FeatureAvailableUsage:
- 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
diff --git a/.rubocop_todo/gitlab/namespaced_class.yml b/.rubocop_todo/gitlab/namespaced_class.yml
index b79402ce5bf..230ef959153 100644
--- a/.rubocop_todo/gitlab/namespaced_class.yml
+++ b/.rubocop_todo/gitlab/namespaced_class.yml
@@ -252,7 +252,6 @@ Gitlab/NamespacedClass:
- 'app/models/notification_setting.rb'
- 'app/models/oauth_access_grant.rb'
- 'app/models/oauth_access_token.rb'
- - 'app/models/onboarding_progress.rb'
- 'app/models/out_of_context_discussion.rb'
- 'app/models/pages_deployment.rb'
- 'app/models/pages_domain.rb'
@@ -405,6 +404,7 @@ Gitlab/NamespacedClass:
- '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/release_policy.rb'
- 'app/policies/repository_policy.rb'
- 'app/policies/resource_label_event_policy.rb'
@@ -656,7 +656,6 @@ Gitlab/NamespacedClass:
- 'app/services/metrics_service.rb'
- 'app/services/note_summary.rb'
- 'app/services/notification_service.rb'
- - 'app/services/onboarding_progress_service.rb'
- 'app/services/post_receive_service.rb'
- 'app/services/preview_markdown_service.rb'
- 'app/services/push_event_payload_service.rb'
@@ -852,7 +851,7 @@ Gitlab/NamespacedClass:
- 'app/workers/x509_issuer_crl_check_worker.rb'
- 'ee/app/controllers/countries_controller.rb'
- 'ee/app/controllers/country_states_controller.rb'
- - 'ee/app/controllers/omniauth_kerberos_spnego_controller.rb'
+ - 'ee/app/controllers/omniauth_kerberos_controller.rb'
- 'ee/app/controllers/operations_controller.rb'
- 'ee/app/controllers/sitemap_controller.rb'
- 'ee/app/controllers/smartcard_controller.rb'
diff --git a/.rubocop_todo/layout/argument_alignment.yml b/.rubocop_todo/layout/argument_alignment.yml
index 1977bedd143..bd4cd386153 100644
--- a/.rubocop_todo/layout/argument_alignment.yml
+++ b/.rubocop_todo/layout/argument_alignment.yml
@@ -391,7 +391,6 @@ Layout/ArgumentAlignment:
- '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/pipeline_spec.rb'
- - 'ee/spec/features/uncompleted_learn_gitlab_link_spec.rb'
- 'ee/spec/finders/security/pipeline_vulnerabilities_finder_spec.rb'
- 'ee/spec/frontend/fixtures/search.rb'
- 'ee/spec/graphql/mutations/requirements_management/export_requirements_spec.rb'
diff --git a/.rubocop_todo/layout/first_array_element_indentation.yml b/.rubocop_todo/layout/first_array_element_indentation.yml
index 84e367e0514..5207d493044 100644
--- a/.rubocop_todo/layout/first_array_element_indentation.yml
+++ b/.rubocop_todo/layout/first_array_element_indentation.yml
@@ -2,157 +2,6 @@
# Cop supports --auto-correct.
Layout/FirstArrayElementIndentation:
Exclude:
- - 'app/controllers/abuse_reports_controller.rb'
- - 'app/controllers/admin/application_settings_controller.rb'
- - 'app/controllers/admin/broadcast_messages_controller.rb'
- - 'app/controllers/admin/plan_limits_controller.rb'
- - 'app/controllers/boards/issues_controller.rb'
- - 'app/controllers/groups_controller.rb'
- - 'app/controllers/projects/issues_controller.rb'
- - 'app/controllers/projects/merge_requests_controller.rb'
- - 'app/controllers/projects/pipelines_controller.rb'
- - 'app/controllers/projects_controller.rb'
- - 'app/finders/issuable_finder.rb'
- - 'app/finders/merge_requests/by_approvals_finder.rb'
- - 'app/finders/user_groups_counter.rb'
- - 'app/helpers/diff_helper.rb'
- - 'app/helpers/search_helper.rb'
- - 'app/models/ci/job_token/scope.rb'
- - 'app/models/container_repository.rb'
- - 'app/models/customer_relations/contact.rb'
- - 'app/models/customer_relations/organization.rb'
- - 'app/models/group.rb'
- - 'app/models/integration.rb'
- - 'app/models/internal_id.rb'
- - 'app/models/issue.rb'
- - 'app/models/member.rb'
- - 'app/models/merge_request.rb'
- - 'app/models/namespace.rb'
- - 'app/models/packages/package.rb'
- - 'app/models/project.rb'
- - 'app/models/projects/topic.rb'
- - 'app/models/todo.rb'
- - 'app/models/user.rb'
- - 'app/services/ci/delete_objects_service.rb'
- - 'app/services/labels/transfer_service.rb'
- - 'app/services/milestones/transfer_service.rb'
- - 'app/workers/ssh_keys/expired_notification_worker.rb'
- - 'config/initializers/postgres_partitioning.rb'
- - 'db/post_migrate/20210812013042_remove_duplicate_project_authorizations.rb'
- - 'ee/app/controllers/groups/settings/reporting_controller.rb'
- - 'ee/app/controllers/projects/vulnerability_feedback_controller.rb'
- - 'ee/app/finders/autocomplete/project_invited_groups_finder.rb'
- - 'ee/app/finders/geo/project_registry_finder.rb'
- - 'ee/app/finders/merge_requests/by_approvers_finder.rb'
- - 'ee/app/graphql/mutations/vulnerabilities/create.rb'
- - 'ee/app/helpers/ee/application_settings_helper.rb'
- - 'ee/app/helpers/ee/trial_helper.rb'
- - 'ee/app/models/analytics/devops_adoption/enabled_namespace.rb'
- - 'ee/app/models/ee/epic.rb'
- - 'ee/app/models/ee/project.rb'
- - 'ee/app/models/ee/user.rb'
- - 'ee/app/models/ee/vulnerability.rb'
- - 'ee/app/models/protected_environment.rb'
- - 'ee/app/models/vulnerabilities/read.rb'
- - 'ee/app/serializers/dashboard_environments_serializer.rb'
- - 'ee/app/services/app_sec/dast/profiles/update_service.rb'
- - 'ee/app/services/vulnerabilities/create_service_base.rb'
- - 'ee/lib/ee/api/helpers/award_emoji.rb'
- - 'ee/lib/ee/gitlab/geo_git_access.rb'
- - 'ee/lib/gitlab/contribution_analytics/data_collector.rb'
- - 'ee/lib/gitlab/elastic/helper.rb'
- - 'ee/lib/gitlab/sitemaps/url_extractor.rb'
- - 'ee/lib/tasks/gitlab/seed/metrics.rake'
- - 'ee/spec/controllers/admin/audit_log_reports_controller_spec.rb'
- - 'ee/spec/controllers/admin/licenses/usage_exports_controller_spec.rb'
- - 'ee/spec/controllers/groups/analytics/coverage_reports_controller_spec.rb'
- - 'ee/spec/controllers/groups/security/merge_commit_reports_controller_spec.rb'
- - 'ee/spec/controllers/projects/merge_requests_controller_spec.rb'
- - 'ee/spec/features/admin/admin_dev_ops_reports_spec.rb'
- - 'ee/spec/features/boards/boards_licensed_features_spec.rb'
- - 'ee/spec/features/groups/analytics/cycle_analytics/charts_spec.rb'
- - 'ee/spec/features/groups/group_roadmap_spec.rb'
- - 'ee/spec/finders/billed_users_finder_spec.rb'
- - 'ee/spec/finders/merge_requests/by_approvers_finder_spec.rb'
- - 'ee/spec/finders/security/pipeline_vulnerabilities_finder_spec.rb'
- - 'ee/spec/finders/security/scan_execution_policies_finder_spec.rb'
- - 'ee/spec/frontend/fixtures/dast_profiles.rb'
- - 'ee/spec/frontend/fixtures/search.rb'
- - 'ee/spec/graphql/mutations/incident_management/escalation_policy/create_spec.rb'
- - 'ee/spec/graphql/resolvers/dora_metrics_resolver_spec.rb'
- - 'ee/spec/graphql/resolvers/security_orchestration/scan_execution_policy_resolver_spec.rb'
- - 'ee/spec/graphql/resolvers/timebox_report_resolver_spec.rb'
- - 'ee/spec/graphql/types/ci/pipeline_type_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/helpers/trial_status_widget_helper_spec.rb'
- - 'ee/spec/lib/ee/gitlab/auth/ldap/access_levels_spec.rb'
- - 'ee/spec/lib/ee/gitlab/auth/ldap/sync/group_spec.rb'
- - 'ee/spec/lib/ee/gitlab/usage_data_spec.rb'
- - 'ee/spec/lib/gitlab/checks/diff_check_spec.rb'
- - 'ee/spec/lib/gitlab/ci/config/security_orchestration_policies/processor_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/yaml_processor_spec.rb'
- - 'ee/spec/lib/gitlab/graphql/aggregations/epics/epic_node_spec.rb'
- - 'ee/spec/lib/gitlab/graphql/aggregations/epics/lazy_links_aggregate_spec.rb'
- - 'ee/spec/lib/gitlab/graphql/aggregations/issues/lazy_links_aggregate_spec.rb'
- - 'ee/spec/lib/gitlab/graphql/loaders/bulk_epic_aggregate_loader_spec.rb'
- - 'ee/spec/lib/gitlab/usage/metrics/instrumentations/count_ci_builds_metric_spec.rb'
- - 'ee/spec/lib/gitlab/usage/metrics/instrumentations/count_users_creating_ci_builds_metric_spec.rb'
- - 'ee/spec/lib/gitlab/vulnerabilities/parser_spec.rb'
- - 'ee/spec/models/analytics/cycle_analytics/group_value_stream_spec.rb'
- - 'ee/spec/models/application_setting_spec.rb'
- - 'ee/spec/models/approval_state_spec.rb'
- - 'ee/spec/models/burndown_spec.rb'
- - 'ee/spec/models/concerns/ee/noteable_spec.rb'
- - 'ee/spec/models/concerns/geo/verification_state_spec.rb'
- - 'ee/spec/models/ee/iterations/cadence_spec.rb'
- - 'ee/spec/models/ee/namespace_spec.rb'
- - 'ee/spec/models/ee/release_spec.rb'
- - 'ee/spec/models/group_wiki_repository_spec.rb'
- - 'ee/spec/models/namespace_setting_spec.rb'
- - 'ee/spec/models/project_spec.rb'
- - 'ee/spec/models/requirements_management/test_report_spec.rb'
- - 'ee/spec/models/security/orchestration_policy_configuration_spec.rb'
- - 'ee/spec/models/security/orchestration_policy_rule_schedule_spec.rb'
- - 'ee/spec/models/security/scan_spec.rb'
- - 'ee/spec/models/security/training_provider_spec.rb'
- - 'ee/spec/models/snippet_repository_spec.rb'
- - 'ee/spec/policies/project_policy_spec.rb'
- - 'ee/spec/requests/admin/user_permission_exports_controller_spec.rb'
- - 'ee/spec/requests/api/analytics/project_deployment_frequency_spec.rb'
- - 'ee/spec/requests/api/experiments_spec.rb'
- - 'ee/spec/requests/api/graphql/analytics/devops_adoption/enabled_namespaces_spec.rb'
- - 'ee/spec/requests/api/graphql/group/epics_spec.rb'
- - 'ee/spec/requests/api/graphql/mutations/quality_management/test_cases/create_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/alert_management/payload_fields_spec.rb'
- - 'ee/spec/requests/api/graphql/project/incident_management/escalation_policy/rules_spec.rb'
- - 'ee/spec/requests/api/graphql/project/merge_requests_spec.rb'
- - 'ee/spec/requests/api/internal/kubernetes_spec.rb'
- - 'ee/spec/requests/api/ldap_group_links_spec.rb'
- - 'ee/spec/requests/api/members_spec.rb'
- - 'ee/spec/services/analytics/cycle_analytics/value_streams/update_service_spec.rb'
- - 'ee/spec/services/audit_events/export_csv_service_spec.rb'
- - 'ee/spec/services/gitlab_subscriptions/fetch_purchase_eligible_namespaces_service_spec.rb'
- - 'ee/spec/services/groups/seat_usage_export_service_spec.rb'
- - 'ee/spec/services/iterations/cadences/create_iterations_in_advance_service_spec.rb'
- - 'ee/spec/services/protected_environments/base_service_spec.rb'
- - 'ee/spec/services/search_service_spec.rb'
- - 'ee/spec/services/security/ingestion/tasks/hooks_execution_spec.rb'
- - 'ee/spec/services/security/security_orchestration_policies/process_scan_result_policy_service_spec.rb'
- - 'ee/spec/services/security/store_findings_metadata_service_spec.rb'
- - 'ee/spec/services/timebox_report_service_spec.rb'
- - 'ee/spec/services/user_permissions/export_service_spec.rb'
- - 'ee/spec/support/shared_examples/services/search_notes_shared_examples.rb'
- - 'ee/spec/workers/geo/scheduler/scheduler_worker_spec.rb'
- - 'lib/event_filter.rb'
- - 'lib/gitlab/database/migration_helpers.rb'
- - 'lib/gitlab/email/message/in_product_marketing/team.rb'
- 'lib/gitlab/email/message/in_product_marketing/trial.rb'
- 'lib/gitlab/email/message/in_product_marketing/verify.rb'
- 'lib/gitlab/import_export/base/relation_factory.rb'
@@ -195,26 +44,6 @@ Layout/FirstArrayElementIndentation:
- 'qa/qa/specs/features/ee/browser_ui/3_create/repository/code_owners_with_protected_branch_and_squashed_commits_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/4_verify/new_discussion_not_dropping_merge_trains_mr_spec.rb'
- 'spec/controllers/concerns/send_file_upload_spec.rb'
- - 'spec/controllers/graphql_controller_spec.rb'
- - 'spec/controllers/projects/ci/daily_build_group_report_results_controller_spec.rb'
- - 'spec/controllers/projects/ci/lints_controller_spec.rb'
- - 'spec/controllers/projects/pipelines/tests_controller_spec.rb'
- - 'spec/controllers/projects/pipelines_controller_spec.rb'
- - 'spec/deprecation_toolkit_env.rb'
- - 'spec/features/clusters/create_agent_spec.rb'
- - 'spec/finders/bulk_imports/entities_finder_spec.rb'
- - 'spec/finders/ci/daily_build_group_report_results_finder_spec.rb'
- - 'spec/finders/deploy_tokens/tokens_finder_spec.rb'
- - '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/frontend/fixtures/search.rb'
- - 'spec/graphql/mutations/commits/create_spec.rb'
- - 'spec/graphql/mutations/environments/canary_ingress/update_spec.rb'
- - 'spec/graphql/resolvers/ci/test_suite_resolver_spec.rb'
- - 'spec/graphql/types/ci/runner_architecture_type_spec.rb'
- - 'spec/graphql/types/ci/runner_platform_type_spec.rb'
- - 'spec/graphql/types/metrics/dashboard_type_spec.rb'
- - 'spec/graphql/types/packages/composer/metadatum_type_spec.rb'
- 'spec/graphql/types/packages/tag_type_spec.rb'
- 'spec/helpers/application_settings_helper_spec.rb'
- 'spec/helpers/commits_helper_spec.rb'
diff --git a/.rubocop_todo/layout/hash_alignment.yml b/.rubocop_todo/layout/hash_alignment.yml
index 4a98bcea706..62d877624a7 100644
--- a/.rubocop_todo/layout/hash_alignment.yml
+++ b/.rubocop_todo/layout/hash_alignment.yml
@@ -2,388 +2,5 @@
# Cop supports --auto-correct.
Layout/HashAlignment:
Exclude:
- - 'ee/lib/api/iterations.rb'
- - 'ee/lib/api/merge_trains.rb'
- - 'ee/lib/api/related_epic_links.rb'
- - 'ee/lib/api/vulnerability_exports.rb'
- - 'ee/lib/api/vulnerability_findings.rb'
- - 'ee/lib/ee/api/helpers/groups_helpers.rb'
- - 'ee/lib/ee/api/helpers/issues_helpers.rb'
- - 'ee/lib/ee/api/helpers/protected_branches_helpers.rb'
- - 'ee/lib/ee/api/merge_requests.rb'
- - 'ee/lib/ee/audit/project_setting_changes_auditor.rb'
- - 'ee/lib/ee/audit/protected_branches_changes_auditor.rb'
- - 'ee/lib/ee/banzai/filter/references/epic_reference_filter.rb'
- - 'ee/lib/ee/banzai/filter/references/vulnerability_reference_filter.rb'
- - 'ee/lib/ee/gitlab/application_rate_limiter.rb'
- - 'ee/lib/ee/gitlab/ci/pipeline/chain/validate/after_config.rb'
- - 'ee/lib/ee/gitlab/quick_actions/issue_actions.rb'
- - 'ee/lib/ee/gitlab/tracking.rb'
- - 'ee/lib/ee/gitlab/usage_data.rb'
- - 'ee/lib/elastic/latest/commit_config.rb'
- - 'ee/lib/elastic/latest/config.rb'
- - 'ee/lib/elastic/latest/merge_request_config.rb'
- - 'ee/lib/gem_extensions/elasticsearch/model/indexing/instance_methods.rb'
- - 'ee/lib/gitlab/auth/smartcard/certificate.rb'
- - 'ee/lib/gitlab/auth/smartcard/ldap_certificate.rb'
- - 'ee/lib/gitlab/ci/parsers/security/formatters/dependency_list.rb'
- - 'ee/lib/gitlab/elastic/helper.rb'
- - 'ee/lib/gitlab/elastic/indexer.rb'
- - 'ee/spec/controllers/ee/projects/variables_controller_spec.rb'
- - 'ee/spec/controllers/groups/epic_boards_controller_spec.rb'
- - 'ee/spec/controllers/groups/issues_controller_spec.rb'
- - 'ee/spec/controllers/projects/settings/operations_controller_spec.rb'
- - 'ee/spec/controllers/trials_controller_spec.rb'
- - 'ee/spec/factories/dependencies.rb'
- - 'ee/spec/factories/projects.rb'
- - 'ee/spec/features/billings/billing_plans_spec.rb'
- - 'ee/spec/features/groups/settings/protected_environments_spec.rb'
- - 'ee/spec/features/projects/environments/environments_spec.rb'
- - 'ee/spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb'
- - 'ee/spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb'
- - 'ee/spec/finders/epics_finder_spec.rb'
- - 'ee/spec/finders/merge_requests_finder_spec.rb'
- - 'ee/spec/frontend/fixtures/dast_profiles.rb'
- - 'ee/spec/graphql/ee/mutations/ci/runner/update_spec.rb'
- - 'ee/spec/graphql/ee/resolvers/namespace_projects_resolver_spec.rb'
- - 'ee/spec/graphql/resolvers/path_locks_resolver_spec.rb'
- - 'ee/spec/graphql/resolvers/security_orchestration/scan_execution_policy_resolver_spec.rb'
- - 'ee/spec/graphql/resolvers/security_report_summary_resolver_spec.rb'
- - 'ee/spec/graphql/resolvers/vulnerabilities/issue_links_resolver_spec.rb'
- - 'ee/spec/helpers/billing_plans_helper_spec.rb'
- - 'ee/spec/helpers/routing/pseudonymization_helper_spec.rb'
- - 'ee/spec/lib/ee/gitlab/auth/ldap/access_levels_spec.rb'
- - 'ee/spec/lib/ee/gitlab/auth/ldap/config_spec.rb'
- - 'ee/spec/lib/ee/gitlab/background_migration/backfill_project_statistics_container_repository_size_spec.rb'
- - 'ee/spec/lib/ee/gitlab/background_migration/delete_invalid_epic_issues_spec.rb'
- - 'ee/spec/lib/ee/gitlab/background_migration/fix_incorrect_max_seats_used_spec.rb'
- - 'ee/spec/lib/ee/gitlab/ci/config/entry/need_spec.rb'
- - 'ee/spec/lib/ee/gitlab/ci/matching/runner_matcher_spec.rb'
- - 'ee/spec/lib/ee/gitlab/elastic/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/ee/gitlab/usage_data_spec.rb'
- - 'ee/spec/lib/gitlab/auth/ldap/person_spec.rb'
- - 'ee/spec/lib/gitlab/auth/smartcard/certificate_spec.rb'
- - 'ee/spec/lib/gitlab/custom_file_templates_spec.rb'
- - 'ee/spec/lib/gitlab/elastic/client_spec.rb'
- - 'ee/spec/lib/gitlab/elastic/indexer_spec.rb'
- - 'ee/spec/lib/gitlab/insights/loader_spec.rb'
- - 'ee/spec/lib/gitlab/template/custom_templates_spec.rb'
- - 'ee/spec/models/application_setting_spec.rb'
- - 'ee/spec/models/approval_merge_request_rule_spec.rb'
- - 'ee/spec/models/member_spec.rb'
- - 'ee/spec/models/preloaders/environments/protected_environment_preloader_spec.rb'
- - 'ee/spec/models/protected_environments/approval_summary_spec.rb'
- - 'ee/spec/models/vulnerabilities/statistic_spec.rb'
- - 'ee/spec/requests/api/deployments_spec.rb'
- - 'ee/spec/requests/api/graphql/app_sec/fuzzing/coverage/corpus_type_spec.rb'
- - 'ee/spec/requests/api/graphql/group/epics_spec.rb'
- - 'ee/spec/requests/api/internal/base_spec.rb'
- - 'ee/spec/requests/api/issues_spec.rb'
- - 'ee/spec/requests/api/protected_environments_spec.rb'
- - 'ee/spec/requests/api/templates_spec.rb'
- - 'ee/spec/requests/ee/projects/environments_controller_spec.rb'
- - 'ee/spec/requests/ee/projects/service_desk_controller_spec.rb'
- - 'ee/spec/requests/projects/security/dast_site_profiles_controller_spec.rb'
- - 'ee/spec/requests/rack_attack_global_spec.rb'
- - 'ee/spec/serializers/integrations/zentao_serializers/issue_entity_spec.rb'
- - 'ee/spec/services/app_sec/dast/profiles/create_associations_service_spec.rb'
- - 'ee/spec/services/app_sec/dast/scan_configs/fetch_service_spec.rb'
- - 'ee/spec/services/arkose/blocked_users_report_service_spec.rb'
- - 'ee/spec/services/audit_events/protected_branch_audit_event_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/process_pipeline_service_spec.rb'
- - 'ee/spec/services/ci/retry_pipeline_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/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/status_page/trigger_publish_service_spec.rb'
- - 'ee/spec/services/status_page/unpublish_details_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/create_from_security_finding_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/findings/create_from_security_finding_service_spec.rb'
- - 'ee/spec/support/shared_examples/services/audit_event_logging_shared_examples.rb'
- - 'ee/spec/support/shared_examples/status_page/publish_shared_examples.rb'
- - 'ee/spec/support/shared_examples/status_page/reference_links_examples.rb'
- - 'ee/spec/workers/scan_security_report_secrets_worker_spec.rb'
- - 'lib/gitlab/abuse.rb'
- - 'lib/gitlab/access.rb'
- - 'lib/gitlab/application_rate_limiter.rb'
- - 'lib/gitlab/auth/ldap/config.rb'
- - 'lib/gitlab/auth/o_auth/auth_hash.rb'
- - 'lib/gitlab/auth/o_auth/provider.rb'
- - 'lib/gitlab/auth/o_auth/user.rb'
- - 'lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp.rb'
- - 'lib/gitlab/background_migration/backfill_project_repositories.rb'
- - 'lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb'
- - 'lib/gitlab/ci/ansi2html.rb'
- - 'lib/gitlab/ci/ansi2json/parser.rb'
- - 'lib/gitlab/ci/config/entry/processable.rb'
- - 'lib/gitlab/ci/jwt_v2.rb'
- - 'lib/gitlab/ci/pipeline/chain/validate/external.rb'
- - 'lib/gitlab/ci/reports/security/scanner.rb'
- - 'lib/gitlab/database/background_migration/health_status.rb'
- - 'lib/gitlab/database/reflection.rb'
- - 'lib/gitlab/diff/file_collection/compare.rb'
- - 'lib/gitlab/email/attachment_uploader.rb'
- - 'lib/gitlab/emoji.rb'
- - 'lib/gitlab/etag_caching/middleware.rb'
- - 'lib/gitlab/experimentation.rb'
- - 'lib/gitlab/fogbugz_import/importer.rb'
- - 'lib/gitlab/git/repository.rb'
- - 'lib/gitlab/gitaly_client/commit_service.rb'
- - 'lib/gitlab/gitaly_client/operation_service.rb'
- - 'lib/gitlab/hook_data/project_member_builder.rb'
- - 'lib/gitlab/import_export/group/legacy_tree_restorer.rb'
- - 'lib/gitlab/import_export/group/relation_factory.rb'
- - 'lib/gitlab/import_export/group/relation_tree_restorer.rb'
- - 'lib/gitlab/import_export/members_mapper.rb'
- - 'lib/gitlab/import_export/project/import_task.rb'
- - 'lib/gitlab/import_export/shared.rb'
- - 'lib/gitlab/issuable/clone/copy_resource_events_service.rb'
- - 'lib/gitlab/kubernetes.rb'
- - 'lib/gitlab/marginalia/comment.rb'
- - 'lib/gitlab/metrics/dashboard/stages/grafana_formatter.rb'
- - 'lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics.rb'
- - 'lib/gitlab/metrics/dashboard/validator/client.rb'
- - 'lib/gitlab/metrics/exporter/metrics_middleware.rb'
- - 'lib/gitlab/metrics/samplers/puma_sampler.rb'
- - 'lib/gitlab/metrics/samplers/ruby_sampler.rb'
- - 'lib/gitlab/no_cache_headers.rb'
- - 'lib/gitlab/sidekiq_daemon/memory_killer.rb'
- - 'lib/gitlab/sidekiq_middleware/server_metrics.rb'
- - 'lib/gitlab/slash_commands/presenters/base.rb'
- - 'lib/gitlab/template/gitignore_template.rb'
- - 'lib/gitlab/visibility_level.rb'
- - 'lib/product_analytics/event_params.rb'
- - 'lib/sidebars/projects/menus/infrastructure_menu.rb'
- - 'lib/tasks/gitlab/import_export/export.rake'
- - 'lib/tasks/gitlab/import_export/import.rake'
- - 'lib/tasks/tanuki_emoji.rake'
- - 'spec/controllers/concerns/product_analytics_tracking_spec.rb'
- - 'spec/controllers/concerns/redis_tracking_spec.rb'
- - 'spec/controllers/import/bitbucket_controller_spec.rb'
- - 'spec/controllers/oauth/token_info_controller_spec.rb'
- - 'spec/controllers/omniauth_callbacks_controller_spec.rb'
- - 'spec/controllers/projects/artifacts_controller_spec.rb'
- - 'spec/controllers/projects/feature_flags_controller_spec.rb'
- - 'spec/controllers/projects/grafana_api_controller_spec.rb'
- - 'spec/controllers/projects/merge_requests_controller_spec.rb'
- - 'spec/controllers/projects/pipeline_schedules_controller_spec.rb'
- - 'spec/controllers/projects/registry/tags_controller_spec.rb'
- - 'spec/controllers/projects/service_desk_controller_spec.rb'
- - 'spec/db/migration_spec.rb'
- - 'spec/factories/ci/builds.rb'
- - 'spec/factories/environments.rb'
- - 'spec/factories/groups.rb'
- - 'spec/features/dashboard/datetime_on_tooltips_spec.rb'
- - 'spec/features/dashboard/todos/todos_sorting_spec.rb'
- - 'spec/features/jira_oauth_provider_authorize_spec.rb'
- - 'spec/features/merge_request/user_sees_deployment_widget_spec.rb'
- - 'spec/features/profiles/active_sessions_spec.rb'
- - 'spec/features/projects/badges/coverage_spec.rb'
- - 'spec/features/projects/environments/environment_spec.rb'
- - 'spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb'
- - 'spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb'
- - 'spec/features/projects/jobs/user_browses_jobs_spec.rb'
- - 'spec/features/projects/milestones/milestones_sorting_spec.rb'
- - 'spec/features/projects/new_project_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/snippets/user_creates_snippet_spec.rb'
- - 'spec/features/users/email_verification_on_login_spec.rb'
- - 'spec/features/users/login_spec.rb'
- - 'spec/finders/ci/pipelines_for_merge_request_finder_spec.rb'
- - 'spec/finders/group_descendants_finder_spec.rb'
- - 'spec/finders/group_members_finder_spec.rb'
- - 'spec/finders/template_finder_spec.rb'
- - 'spec/graphql/mutations/releases/update_spec.rb'
- - 'spec/graphql/resolvers/board_lists_resolver_spec.rb'
- - 'spec/graphql/resolvers/ci/config_resolver_spec.rb'
- - 'spec/graphql/resolvers/ci/group_runners_resolver_spec.rb'
- - 'spec/graphql/resolvers/ci/jobs_resolver_spec.rb'
- - 'spec/graphql/resolvers/ci/runners_resolver_spec.rb'
- - 'spec/graphql/resolvers/container_repositories_resolver_spec.rb'
- - 'spec/graphql/resolvers/container_repository_tags_resolver_spec.rb'
- - 'spec/graphql/resolvers/design_management/versions_resolver_spec.rb'
- - 'spec/graphql/resolvers/work_items_resolver_spec.rb'
- - 'spec/helpers/ci/builds_helper_spec.rb'
- - 'spec/helpers/sorting_helper_spec.rb'
- - 'spec/helpers/storage_helper_spec.rb'
- - 'spec/helpers/wiki_helper_spec.rb'
- - 'spec/initializers/00_rails_disable_joins_spec.rb'
- - 'spec/lib/backup/gitaly_backup_spec.rb'
- - 'spec/lib/banzai/filter/repository_link_filter_spec.rb'
- - 'spec/lib/gitlab/asciidoc_spec.rb'
- - 'spec/lib/gitlab/auth/ldap/auth_hash_spec.rb'
- - 'spec/lib/gitlab/auth/ldap/config_spec.rb'
- - 'spec/lib/gitlab/auth/ldap/person_spec.rb'
- - 'spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb'
- - 'spec/lib/gitlab/auth/otp/strategies/forti_token_cloud_spec.rb'
- - 'spec/lib/gitlab/background_migration/backfill_imported_issue_search_data_spec.rb'
- - 'spec/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification_spec.rb'
- - 'spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb'
- - 'spec/lib/gitlab/background_migration/encrypt_integration_properties_spec.rb'
- - 'spec/lib/gitlab/ci/config/entry/port_spec.rb'
- - 'spec/lib/gitlab/ci/config/entry/root_spec.rb'
- - 'spec/lib/gitlab/ci/config/external/mapper_spec.rb'
- - 'spec/lib/gitlab/ci/lint_spec.rb'
- - 'spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb'
- - 'spec/lib/gitlab/ci/variables/builder_spec.rb'
- - 'spec/lib/gitlab/ci/yaml_processor_spec.rb'
- - 'spec/lib/gitlab/data_builder/issuable_spec.rb'
- - 'spec/lib/gitlab/data_builder/note_spec.rb'
- - 'spec/lib/gitlab/database/migration_helpers_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_importers/self_monitoring/project/create_service_spec.rb'
- - 'spec/lib/gitlab/diff/highlight_cache_spec.rb'
- - 'spec/lib/gitlab/diff/lines_unfolder_spec.rb'
- - 'spec/lib/gitlab/diff/position_spec.rb'
- - 'spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb'
- - 'spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb'
- - 'spec/lib/gitlab/etag_caching/middleware_spec.rb'
- - 'spec/lib/gitlab/etag_caching/router/graphql_spec.rb'
- - 'spec/lib/gitlab/git/repository_spec.rb'
- - 'spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb'
- - 'spec/lib/gitlab/import_export/attributes_finder_spec.rb'
- - 'spec/lib/gitlab/import_export/group/object_builder_spec.rb'
- - 'spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb'
- - 'spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb'
- - 'spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb'
- - 'spec/lib/gitlab/import_sources_spec.rb'
- - 'spec/lib/gitlab/instrumentation_helper_spec.rb'
- - 'spec/lib/gitlab/jira/middleware_spec.rb'
- - 'spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb'
- - 'spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb'
- - 'spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb'
- - 'spec/lib/gitlab/metrics/dashboard/validator_spec.rb'
- - 'spec/lib/gitlab/metrics/requests_rack_middleware_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/load_balancing_spec.rb'
- - 'spec/lib/gitlab/metrics/web_transaction_spec.rb'
- - 'spec/lib/gitlab/omniauth_initializer_spec.rb'
- - 'spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb'
- - 'spec/lib/gitlab/sidekiq_death_handler_spec.rb'
- - 'spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb'
- - 'spec/lib/gitlab/slug/environment_spec.rb'
- - 'spec/lib/gitlab/tracking_spec.rb'
- - 'spec/lib/gitlab/usage_data/topology_spec.rb'
- - 'spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb'
- - 'spec/lib/gitlab/usage_data_counters/note_counter_spec.rb'
- - 'spec/lib/gitlab/usage_data_spec.rb'
- - 'spec/lib/gitlab/utils_spec.rb'
- - 'spec/lib/gitlab/word_diff/parser_spec.rb'
- - 'spec/lib/marginalia_spec.rb'
- - 'spec/lib/security/ci_configuration/sast_build_action_spec.rb'
- - 'spec/mailers/emails/pipelines_spec.rb'
- - 'spec/migrations/20210804150320_create_base_work_item_types_spec.rb'
- - 'spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb'
- - 'spec/migrations/20211117084814_migrate_remaining_u2f_registrations_spec.rb'
- - 'spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb'
- - 'spec/models/ci/build_spec.rb'
- - 'spec/models/ci/pipeline_spec.rb'
- - 'spec/models/ci/processable_spec.rb'
- - 'spec/models/clusters/platforms/kubernetes_spec.rb'
- - 'spec/models/commit_status_spec.rb'
- - 'spec/models/container_registry/event_spec.rb'
- - 'spec/models/deployment_spec.rb'
- - 'spec/models/design_management/version_spec.rb'
- - 'spec/models/group_spec.rb'
- - 'spec/models/integrations/chat_message/pipeline_message_spec.rb'
- - 'spec/models/integrations/drone_ci_spec.rb'
- - 'spec/models/merge_request_spec.rb'
- - 'spec/models/namespace_spec.rb'
- - 'spec/models/operations/feature_flag_spec.rb'
- - 'spec/models/pages_domain_spec.rb'
- - 'spec/models/remote_mirror_spec.rb'
- - 'spec/models/repository_spec.rb'
- - 'spec/models/user_spec.rb'
- - 'spec/presenters/clusters/cluster_presenter_spec.rb'
- - 'spec/presenters/project_presenter_spec.rb'
- - 'spec/requests/api/ci/job_artifacts_spec.rb'
- - 'spec/requests/api/ci/jobs_spec.rb'
- - 'spec/requests/api/ci/runner/jobs_request_post_spec.rb'
- - 'spec/requests/api/feature_flags_spec.rb'
- - 'spec/requests/api/graphql/ci/config_spec.rb'
- - 'spec/requests/api/graphql/ci/group_variables_spec.rb'
- - 'spec/requests/api/graphql/ci/instance_variables_spec.rb'
- - 'spec/requests/api/graphql/ci/project_variables_spec.rb'
- - 'spec/requests/api/graphql/ci/runner_spec.rb'
- - 'spec/requests/api/graphql/ci/runners_spec.rb'
- - 'spec/requests/api/graphql/mutations/releases/update_spec.rb'
- - 'spec/requests/api/graphql/project/issue/design_collection/version_spec.rb'
- - 'spec/requests/api/graphql/project/terraform/state_spec.rb'
- - 'spec/requests/api/graphql/project/terraform/states_spec.rb'
- - 'spec/requests/api/graphql/query_spec.rb'
- - 'spec/requests/api/groups_spec.rb'
- - 'spec/requests/api/internal/base_spec.rb'
- - 'spec/requests/api/issues/get_group_issues_spec.rb'
- - 'spec/requests/api/projects_spec.rb'
- - 'spec/requests/api/suggestions_spec.rb'
- - 'spec/requests/api/unleash_spec.rb'
- - 'spec/requests/git_http_spec.rb'
- - 'spec/requests/oauth_tokens_spec.rb'
- - 'spec/requests/openid_connect_spec.rb'
- - 'spec/requests/projects/environments_controller_spec.rb'
- - 'spec/requests/projects/merge_requests_discussions_spec.rb'
- - 'spec/routing/project_routing_spec.rb'
- - 'spec/serializers/ci/lint/job_entity_spec.rb'
- - 'spec/serializers/container_repository_entity_spec.rb'
- - 'spec/serializers/deployment_entity_spec.rb'
- 'spec/serializers/environment_serializer_spec.rb'
- - 'spec/serializers/merge_request_metrics_helper_spec.rb'
- - 'spec/services/ci/create_downstream_pipeline_service_spec.rb'
- - 'spec/services/ci/create_pipeline_service/logger_spec.rb'
- - 'spec/services/ci/create_pipeline_service/tags_spec.rb'
- - 'spec/services/ci/job_artifacts/create_service_spec.rb'
- - 'spec/services/ci/retry_job_service_spec.rb'
- - 'spec/services/deployments/link_merge_requests_service_spec.rb'
- - 'spec/services/discussions/capture_diff_note_positions_service_spec.rb'
- - 'spec/services/environments/stop_service_spec.rb'
- - 'spec/services/event_create_service_spec.rb'
- - 'spec/services/groups/import_export/import_service_spec.rb'
- - 'spec/services/issuable/bulk_update_service_spec.rb'
- - 'spec/services/issues/create_service_spec.rb'
- - 'spec/services/merge_requests/build_service_spec.rb'
- - 'spec/services/merge_requests/create_service_spec.rb'
- - 'spec/services/merge_requests/update_service_spec.rb'
- - 'spec/services/metrics/dashboard/clone_dashboard_service_spec.rb'
- - 'spec/services/notes/create_service_spec.rb'
- - 'spec/services/notes/destroy_service_spec.rb'
- - 'spec/services/packages/debian/parse_debian822_service_spec.rb'
- - 'spec/services/projects/destroy_service_spec.rb'
- - 'spec/services/service_ping/submit_service_ping_service_spec.rb'
- - 'spec/services/suggestions/apply_service_spec.rb'
- - 'spec/services/work_items/widgets/description_service/update_service_spec.rb'
- - 'spec/support/helpers/create_environments_helpers.rb'
- - 'spec/support/helpers/kubernetes_helpers.rb'
- - 'spec/support/helpers/migrations_helpers/work_item_types_helper.rb'
- - 'spec/support/helpers/seed_helper.rb'
- - 'spec/support/helpers/stub_object_storage.rb'
- - 'spec/support/helpers/test_env.rb'
- - 'spec/support/helpers/usage_data_helpers.rb'
- - 'spec/support/migrations_helpers/vulnerabilities_findings_helper.rb'
- - 'spec/support/shared_contexts/bulk_imports_requests_shared_context.rb'
- - 'spec/support/shared_contexts/design_management_shared_contexts.rb'
- - 'spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb'
- - 'spec/support/shared_examples/finders/issues_finder_shared_examples.rb'
- - 'spec/support/shared_examples/graphql/members_shared_examples.rb'
- - 'spec/support/shared_examples/lib/banzai/filters/sanitization_filter_shared_examples.rb'
- - 'spec/support/shared_examples/lib/gitlab/database/background_migration_job_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/graphql/packages/group_and_project_packages_list_shared_examples.rb'
- - 'spec/support/shared_examples/routing/resource_routing_shared_examples.rb'
- - 'spec/support/shared_examples/routing/wiki_routing_shared_examples.rb'
- - 'spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb'
- - 'spec/tooling/danger/datateam_spec.rb'
- - 'spec/views/projects/tags/index.html.haml_spec.rb'
- - 'spec/workers/emails_on_push_worker_spec.rb'
- - 'spec/workers/pages_domain_ssl_renewal_cron_worker_spec.rb'
diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml
index 80c0f986552..01c83d7ac5d 100644
--- a/.rubocop_todo/layout/line_length.yml
+++ b/.rubocop_todo/layout/line_length.yml
@@ -1123,7 +1123,6 @@ Layout/LineLength:
- 'ee/app/controllers/concerns/ee/issuable_collections.rb'
- 'ee/app/controllers/concerns/group_invite_members.rb'
- 'ee/app/controllers/concerns/insights_actions.rb'
- - 'ee/app/controllers/concerns/registrations/create_project.rb'
- 'ee/app/controllers/ee/admin/dev_ops_report_controller.rb'
- 'ee/app/controllers/ee/admin/users_controller.rb'
- 'ee/app/controllers/ee/application_controller.rb'
@@ -1160,8 +1159,6 @@ Layout/LineLength:
- 'ee/app/controllers/projects/requirements_management/requirements_controller.rb'
- 'ee/app/controllers/projects/security/policies_controller.rb'
- 'ee/app/controllers/projects/security/vulnerabilities/notes_controller.rb'
- - 'ee/app/controllers/registrations/groups_controller.rb'
- - 'ee/app/controllers/registrations/groups_projects_controller.rb'
- 'ee/app/controllers/subscriptions_controller.rb'
- 'ee/app/controllers/trial_registrations_controller.rb'
- 'ee/app/controllers/trials_controller.rb'
@@ -1862,8 +1859,6 @@ Layout/LineLength:
- 'ee/spec/controllers/projects/subscriptions_controller_spec.rb'
- 'ee/spec/controllers/projects/vulnerability_feedback_controller_spec.rb'
- 'ee/spec/controllers/projects_controller_spec.rb'
- - 'ee/spec/controllers/registrations/groups_controller_spec.rb'
- - 'ee/spec/controllers/registrations/groups_projects_controller_spec.rb'
- 'ee/spec/controllers/registrations/welcome_controller_spec.rb'
- 'ee/spec/controllers/subscriptions/groups_controller_spec.rb'
- 'ee/spec/controllers/subscriptions_controller_spec.rb'
@@ -1979,13 +1974,11 @@ Layout/LineLength:
- 'ee/spec/features/projects_spec.rb'
- 'ee/spec/features/promotion_spec.rb'
- 'ee/spec/features/read_only_spec.rb'
- - 'ee/spec/features/registrations/combined_registration_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'
- 'ee/spec/features/trial_registrations/company_information_spec.rb'
- - 'ee/spec/features/uncompleted_learn_gitlab_link_spec.rb'
- 'ee/spec/features/users/login_spec.rb'
- 'ee/spec/finders/analytics/devops_adoption/enabled_namespaces_finder_spec.rb'
- 'ee/spec/finders/analytics/devops_adoption/snapshots_finder_spec.rb'
@@ -2960,8 +2953,6 @@ Layout/LineLength:
- 'ee/spec/support/shared_examples/controllers/analytics/cycle_analytics/shared_stage_shared_examples.rb'
- 'ee/spec/support/shared_examples/controllers/concerns/description_diff_actions_shared_examples.rb'
- 'ee/spec/support/shared_examples/controllers/projects/license_scanning_report_comparison_shared_examples.rb'
- - 'ee/spec/support/shared_examples/controllers/registrations/groups_controller_shared_examples.rb'
- - 'ee/spec/support/shared_examples/controllers/registrations/projects_controller_shared_examples.rb'
- 'ee/spec/support/shared_examples/features/epics_filtered_search_shared_examples.rb'
- 'ee/spec/support/shared_examples/features/over_free_user_limit_shared_examples.rb'
- 'ee/spec/support/shared_examples/features/protected_branches_access_control_shared_examples.rb'
@@ -3017,7 +3008,6 @@ Layout/LineLength:
- 'ee/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb'
- 'ee/spec/views/operations/environments.html.haml_spec.rb'
- 'ee/spec/views/projects/security/discover/show.html.haml_spec.rb'
- - 'ee/spec/views/registrations/groups_projects/new.html.haml_spec.rb'
- 'ee/spec/views/registrations/welcome/show.html.haml_spec.rb'
- 'ee/spec/views/shared/_mirror_status.html.haml_spec.rb'
- 'ee/spec/views/shared/_namespace_user_cap_reached_alert.html.haml_spec.rb'
@@ -3675,7 +3665,6 @@ Layout/LineLength:
- 'qa/qa/ee/page/project/secure/security_dashboard.rb'
- 'qa/qa/ee/page/project/secure/show.rb'
- 'qa/qa/ee/resource/license.rb'
- - 'qa/qa/fixtures/auto_devops_rack/config.ru'
- 'qa/qa/flow/sign_up.rb'
- 'qa/qa/git/repository.rb'
- 'qa/qa/page/base.rb'
@@ -5169,7 +5158,6 @@ Layout/LineLength:
- 'spec/lib/grafana/validator_spec.rb'
- 'spec/lib/kramdown/kramdown_spec.rb'
- 'spec/lib/kramdown/parser/atlassian_document_format_spec.rb'
- - 'spec/lib/learn_gitlab/project_spec.rb'
- 'spec/lib/mattermost/command_spec.rb'
- 'spec/lib/microsoft_teams/notifier_spec.rb'
- 'spec/lib/object_storage/config_spec.rb'
@@ -5292,7 +5280,6 @@ Layout/LineLength:
- 'spec/models/ci/instance_variable_spec.rb'
- 'spec/models/ci/job_artifact_spec.rb'
- 'spec/models/ci/job_token/scope_spec.rb'
- - 'spec/models/ci/namespace_mirror_spec.rb'
- 'spec/models/ci/pipeline_schedule_spec.rb'
- 'spec/models/ci/pipeline_spec.rb'
- 'spec/models/ci/processable_spec.rb'
@@ -5404,7 +5391,6 @@ Layout/LineLength:
- 'spec/models/namespace_statistics_spec.rb'
- 'spec/models/note_spec.rb'
- 'spec/models/notification_setting_spec.rb'
- - 'spec/models/onboarding_progress_spec.rb'
- 'spec/models/packages/composer/cache_file_spec.rb'
- 'spec/models/packages/composer/metadatum_spec.rb'
- 'spec/models/packages/conan/metadatum_spec.rb'
diff --git a/.rubocop_todo/layout/space_in_lambda_literal.yml b/.rubocop_todo/layout/space_in_lambda_literal.yml
index c88fdf5a3b0..9359939514b 100644
--- a/.rubocop_todo/layout/space_in_lambda_literal.yml
+++ b/.rubocop_todo/layout/space_in_lambda_literal.yml
@@ -96,7 +96,6 @@ Layout/SpaceInLambdaLiteral:
- 'app/models/namespace_statistics.rb'
- 'app/models/note.rb'
- 'app/models/note_diff_file.rb'
- - 'app/models/onboarding_progress.rb'
- 'app/models/operations/feature_flags/user_list.rb'
- 'app/models/packages/build_info.rb'
- 'app/models/packages/maven/metadatum.rb'
diff --git a/.rubocop_todo/layout/space_inside_block_braces.yml b/.rubocop_todo/layout/space_inside_block_braces.yml
deleted file mode 100644
index 4fdc308eaa5..00000000000
--- a/.rubocop_todo/layout/space_inside_block_braces.yml
+++ /dev/null
@@ -1,134 +0,0 @@
----
-# Cop supports --auto-correct.
-Layout/SpaceInsideBlockBraces:
- Exclude:
- - 'spec/config/settings_spec.rb'
- - 'spec/controllers/admin/application_settings_controller_spec.rb'
- - 'spec/controllers/application_controller_spec.rb'
- - 'spec/controllers/groups/labels_controller_spec.rb'
- - 'spec/controllers/groups/releases_controller_spec.rb'
- - 'spec/controllers/import/manifest_controller_spec.rb'
- - 'spec/controllers/projects/blame_controller_spec.rb'
- - 'spec/controllers/projects/deploy_keys_controller_spec.rb'
- - 'spec/controllers/projects/feature_flags_controller_spec.rb'
- - 'spec/controllers/projects/jobs_controller_spec.rb'
- - 'spec/controllers/projects/labels_controller_spec.rb'
- - 'spec/controllers/projects/notes_controller_spec.rb'
- - 'spec/controllers/projects/releases_controller_spec.rb'
- - 'spec/controllers/projects/tree_controller_spec.rb'
- - 'spec/controllers/registrations/welcome_controller_spec.rb'
- - 'spec/controllers/snippets/notes_controller_spec.rb'
- - 'spec/dependencies/omniauth_saml_spec.rb'
- - 'spec/experiments/application_experiment_spec.rb'
- - 'spec/factories/ci/build_trace_chunks.rb'
- - 'spec/factories/ci/job_artifacts.rb'
- - 'spec/factories/ci/pipeline_artifacts.rb'
- - 'spec/factories/commit_statuses.rb'
- - 'spec/factories/emails.rb'
- - 'spec/factories/external_pull_requests.rb'
- - 'spec/factories/gitlab/database/postgres_index.rb'
- - 'spec/factories/packages/dependencies.rb'
- - 'spec/factories/packages/package_tags.rb'
- - 'spec/factories/packages/packages.rb'
- - 'spec/factories/prometheus_alert.rb'
- - 'spec/factories/prometheus_metrics.rb'
- - 'spec/finders/ci/jobs_finder_spec.rb'
- - 'spec/finders/ci/runners_finder_spec.rb'
- - 'spec/finders/concerns/packages/finder_helper_spec.rb'
- - 'spec/finders/container_repositories_finder_spec.rb'
- - 'spec/finders/design_management/versions_finder_spec.rb'
- - 'spec/finders/milestones_finder_spec.rb'
- - 'spec/finders/packages/group_packages_finder_spec.rb'
- - 'spec/finders/packages/npm/package_finder_spec.rb'
- - 'spec/finders/projects_finder_spec.rb'
- - 'spec/frontend/fixtures/api_merge_requests.rb'
- - 'spec/frontend/fixtures/api_projects.rb'
- - 'spec/frontend/fixtures/application_settings.rb'
- - 'spec/frontend/fixtures/blob.rb'
- - 'spec/frontend/fixtures/branches.rb'
- - 'spec/frontend/fixtures/clusters.rb'
- - 'spec/frontend/fixtures/deploy_keys.rb'
- - 'spec/frontend/fixtures/groups.rb'
- - 'spec/frontend/fixtures/issues.rb'
- - 'spec/frontend/fixtures/jobs.rb'
- - 'spec/frontend/fixtures/labels.rb'
- - 'spec/frontend/fixtures/merge_requests.rb'
- - 'spec/frontend/fixtures/merge_requests_diffs.rb'
- - 'spec/frontend/fixtures/metrics_dashboard.rb'
- - 'spec/frontend/fixtures/pipeline_schedules.rb'
- - 'spec/frontend/fixtures/pipelines.rb'
- - 'spec/frontend/fixtures/projects.rb'
- - 'spec/frontend/fixtures/raw.rb'
- - 'spec/frontend/fixtures/snippet.rb'
- - 'spec/frontend/fixtures/todos.rb'
- - 'spec/helpers/application_settings_helper_spec.rb'
- - 'spec/helpers/blob_helper_spec.rb'
- - 'spec/helpers/gitlab_script_tag_helper_spec.rb'
- - 'spec/helpers/issuables_helper_spec.rb'
- - 'spec/helpers/projects/pipeline_helper_spec.rb'
- - 'spec/helpers/routing/pseudonymization_helper_spec.rb'
- - 'spec/helpers/search_helper_spec.rb'
- - 'spec/helpers/wiki_page_version_helper_spec.rb'
- - 'spec/initializers/carrierwave_patch_spec.rb'
- - 'spec/initializers/trusted_proxies_spec.rb'
- - 'spec/mailers/emails/service_desk_spec.rb'
- - 'spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb'
- - 'spec/migrations/20210910194952_update_report_type_for_existing_approval_project_rules_spec.rb'
- - 'spec/migrations/confirm_support_bot_user_spec.rb'
- - 'spec/migrations/reset_job_token_scope_enabled_again_spec.rb'
- - 'spec/migrations/reset_job_token_scope_enabled_spec.rb'
- - 'spec/migrations/reset_severity_levels_to_new_default_spec.rb'
- - 'spec/policies/clusters/agent_policy_spec.rb'
- - 'spec/policies/group_member_policy_spec.rb'
- - 'spec/policies/issue_policy_spec.rb'
- - 'spec/policies/project_policy_spec.rb'
- - 'spec/policies/terraform/state_policy_spec.rb'
- - 'spec/policies/terraform/state_version_policy_spec.rb'
- - 'spec/presenters/packages/composer/packages_presenter_spec.rb'
- - 'spec/presenters/packages/conan/package_presenter_spec.rb'
- - 'spec/presenters/packages/nuget/packages_metadata_presenter_spec.rb'
- - 'spec/presenters/project_presenter_spec.rb'
- - 'spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb'
- - 'spec/serializers/cluster_entity_spec.rb'
- - 'spec/serializers/import/provider_repo_serializer_spec.rb'
- - 'spec/support/helpers/cycle_analytics_helpers.rb'
- - 'spec/support/redis/redis_shared_examples.rb'
- - 'spec/support/shared_contexts/graphql/requests/packages_shared_context.rb'
- - 'spec/support/shared_contexts/markdown_golden_master_shared_examples.rb'
- - 'spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb'
- - 'spec/support/shared_examples/controllers/error_tracking_shared_examples.rb'
- - 'spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb'
- - 'spec/support/shared_examples/features/board_sidebar_labels_examples.rb'
- - 'spec/support/shared_examples/features/snippets_shared_examples.rb'
- - 'spec/support/shared_examples/features/wiki/user_views_asciidoc_page_with_includes_shared_examples.rb'
- - 'spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb'
- - 'spec/support/shared_examples/lib/gitlab/sql/set_operator_shared_examples.rb'
- - 'spec/support/shared_examples/models/cluster_application_core_shared_examples.rb'
- - 'spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb'
- - 'spec/support/shared_examples/models/concerns/has_repository_shared_examples.rb'
- - 'spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb'
- - 'spec/support/shared_examples/models/label_note_shared_examples.rb'
- - 'spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb'
- - 'spec/support/shared_examples/models/project_latest_successful_build_for_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/labels_api_shared_examples.rb'
- - 'spec/support/shared_examples/requests/api/repository_storage_moves_shared_examples.rb'
- - 'spec/support/shared_examples/requests/lfs_http_shared_examples.rb'
- - 'spec/support/shared_examples/services/alert_management/alert_processing/alert_firing_shared_examples.rb'
- - 'spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb'
- - 'spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb'
- - 'spec/support/shared_examples/services/merge_request_shared_examples.rb'
- - 'spec/support/shared_examples/uploaders/object_storage_shared_examples.rb'
- - 'spec/tasks/gitlab/snippets_rake_spec.rb'
- - 'spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb'
- - 'spec/validators/addressable_url_validator_spec.rb'
- - 'spec/views/help/instance_configuration.html.haml_spec.rb'
- - 'spec/views/layouts/_header_search.html.haml_spec.rb'
- - 'spec/views/layouts/_published_experiments.html.haml_spec.rb'
- - 'spec/views/shared/runners/_runner_details.html.haml_spec.rb'
- - 'spec/workers/bulk_imports/export_request_worker_spec.rb'
- - 'spec/workers/clusters/cleanup/project_namespace_worker_spec.rb'
- - 'spec/workers/packages/helm/extraction_worker_spec.rb'
- - 'spec/workers/pages_worker_spec.rb'
- - 'spec/workers/purge_dependency_proxy_cache_worker_spec.rb'
- - 'spec/workers/releases/manage_evidence_worker_spec.rb'
diff --git a/.rubocop_todo/migration/background_migration_base_class.yml b/.rubocop_todo/migration/background_migration_base_class.yml
index 495b4a51e56..9e42e85cce8 100644
--- a/.rubocop_todo/migration/background_migration_base_class.yml
+++ b/.rubocop_todo/migration/background_migration_base_class.yml
@@ -26,7 +26,6 @@ Migration/BackgroundMigrationBaseClass:
- '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/copy_ci_builds_columns_to_security_scans.rb'
- 'lib/gitlab/background_migration/create_security_setting.rb'
- 'lib/gitlab/background_migration/delete_orphaned_deployments.rb'
- 'lib/gitlab/background_migration/disable_expiration_policies_linked_to_no_container_images.rb'
diff --git a/.rubocop_todo/rails/application_controller.yml b/.rubocop_todo/rails/application_controller.yml
deleted file mode 100644
index d53fd3411d3..00000000000
--- a/.rubocop_todo/rails/application_controller.yml
+++ /dev/null
@@ -1,13 +0,0 @@
----
-# Cop supports --auto-correct.
-Rails/ApplicationController:
- Exclude:
- - 'app/controllers/acme_challenges_controller.rb'
- - 'app/controllers/chaos_controller.rb'
- - 'app/controllers/health_controller.rb'
- - 'app/controllers/metrics_controller.rb'
- - 'ee/app/controllers/oauth/geo_auth_controller.rb'
- - 'ee/spec/helpers/ee/integrations_helper_spec.rb'
- - 'lib/gitlab/base_doorkeeper_controller.rb'
- - 'lib/gitlab/request_forgery_protection.rb'
- - 'spec/controllers/concerns/continue_params_spec.rb'
diff --git a/.rubocop_todo/rails/helper_instance_variable.yml b/.rubocop_todo/rails/helper_instance_variable.yml
index 006e66ed0b2..f43ca39f47c 100644
--- a/.rubocop_todo/rails/helper_instance_variable.yml
+++ b/.rubocop_todo/rails/helper_instance_variable.yml
@@ -66,7 +66,7 @@ Rails/HelperInstanceVariable:
- 'ee/app/helpers/ee/groups/group_members_helper.rb'
- 'ee/app/helpers/ee/groups_helper.rb'
- 'ee/app/helpers/ee/integrations_helper.rb'
- - 'ee/app/helpers/ee/kerberos_spnego_helper.rb'
+ - 'ee/app/helpers/ee/kerberos_helper.rb'
- 'ee/app/helpers/ee/labels_helper.rb'
- 'ee/app/helpers/ee/lock_helper.rb'
- 'ee/app/helpers/ee/merge_requests_helper.rb'
diff --git a/.rubocop_todo/rails/index_with.yml b/.rubocop_todo/rails/index_with.yml
index 09339d3fd56..d8ccbd97f7c 100644
--- a/.rubocop_todo/rails/index_with.yml
+++ b/.rubocop_todo/rails/index_with.yml
@@ -43,7 +43,6 @@ Rails/IndexWith:
- '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/lib/learn_gitlab/onboarding_spec.rb'
- 'spec/models/event_spec.rb'
- 'spec/presenters/projects/security/configuration_presenter_spec.rb'
- 'spec/support/database/multiple_databases.rb'
diff --git a/.rubocop_todo/rails/squished_sql_heredocs.yml b/.rubocop_todo/rails/squished_sql_heredocs.yml
index 3696f661893..9dcb989f571 100644
--- a/.rubocop_todo/rails/squished_sql_heredocs.yml
+++ b/.rubocop_todo/rails/squished_sql_heredocs.yml
@@ -136,7 +136,6 @@ Rails/SquishedSQLHeredocs:
- 'lib/gitlab/background_migration/backfill_project_settings.rb'
- 'lib/gitlab/background_migration/backfill_projects_with_coverage.rb'
- 'lib/gitlab/background_migration/backfill_upvotes_count_on_issues.rb'
- - 'lib/gitlab/background_migration/copy_ci_builds_columns_to_security_scans.rb'
- 'lib/gitlab/background_migration/disable_expiration_policies_linked_to_no_container_images.rb'
- 'lib/gitlab/background_migration/encrypt_static_object_token.rb'
- 'lib/gitlab/background_migration/fix_duplicate_project_name_and_path.rb'
diff --git a/.rubocop_todo/rails/where_exists.yml b/.rubocop_todo/rails/where_exists.yml
index 00ff82d137e..77722549722 100644
--- a/.rubocop_todo/rails/where_exists.yml
+++ b/.rubocop_todo/rails/where_exists.yml
@@ -19,7 +19,6 @@ Rails/WhereExists:
- 'app/models/lfs_object.rb'
- 'app/models/merge_request_diff.rb'
- 'app/models/namespace.rb'
- - 'app/models/onboarding_progress.rb'
- 'app/models/project.rb'
- 'app/models/protected_branch/push_access_level.rb'
- 'app/services/projects/transfer_service.rb'
diff --git a/.rubocop_todo/rspec/any_instance_of.yml b/.rubocop_todo/rspec/any_instance_of.yml
index 2cc2478e530..e7855e84b28 100644
--- a/.rubocop_todo/rspec/any_instance_of.yml
+++ b/.rubocop_todo/rspec/any_instance_of.yml
@@ -73,7 +73,7 @@ RSpec/AnyInstanceOf:
- 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_spnego_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
diff --git a/.rubocop_todo/rspec/context_wording.yml b/.rubocop_todo/rspec/context_wording.yml
index 163533621a6..97480a0e0ba 100644
--- a/.rubocop_todo/rspec/context_wording.yml
+++ b/.rubocop_todo/rspec/context_wording.yml
@@ -64,7 +64,6 @@ RSpec/ContextWording:
- 'ee/spec/controllers/projects/settings/repository_controller_spec.rb'
- 'ee/spec/controllers/projects/vulnerability_feedback_controller_spec.rb'
- 'ee/spec/controllers/projects_controller_spec.rb'
- - 'ee/spec/controllers/registrations/groups_projects_controller_spec.rb'
- 'ee/spec/controllers/registrations/welcome_controller_spec.rb'
- 'ee/spec/controllers/repositories/git_http_controller_spec.rb'
- 'ee/spec/controllers/security/dashboard_controller_spec.rb'
@@ -921,8 +920,6 @@ RSpec/ContextWording:
- 'ee/spec/support/shared_examples/controllers/boards_actions_shared_examples.rb'
- 'ee/spec/support/shared_examples/controllers/cluster_metrics_shared_examples.rb'
- 'ee/spec/support/shared_examples/controllers/concerns/description_diff_actions_shared_examples.rb'
- - 'ee/spec/support/shared_examples/controllers/registrations/groups_controller_shared_examples.rb'
- - 'ee/spec/support/shared_examples/controllers/registrations/projects_controller_shared_examples.rb'
- 'ee/spec/support/shared_examples/features/insights_shared_examples.rb'
- 'ee/spec/support/shared_examples/features/password_complexity_shared_examples.rb'
- 'ee/spec/support/shared_examples/features/protected_branches_access_control_shared_examples.rb'
@@ -968,7 +965,6 @@ RSpec/ContextWording:
- '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/groups/new.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'
@@ -2470,7 +2466,6 @@ RSpec/ContextWording:
- 'spec/models/note_spec.rb'
- 'spec/models/notification_recipient_spec.rb'
- 'spec/models/notification_setting_spec.rb'
- - 'spec/models/onboarding_progress_spec.rb'
- 'spec/models/operations/feature_flag_spec.rb'
- 'spec/models/packages/conan/file_metadatum_spec.rb'
- 'spec/models/packages/debian/file_metadatum_spec.rb'
diff --git a/.rubocop_todo/rspec/described_class.yml b/.rubocop_todo/rspec/described_class.yml
new file mode 100644
index 00000000000..8304704985b
--- /dev/null
+++ b/.rubocop_todo/rspec/described_class.yml
@@ -0,0 +1,282 @@
+---
+# Cop supports --auto-correct.
+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'
+ - 'ee/spec/models/concerns/elastic/repository_spec.rb'
+ - 'ee/spec/models/concerns/elastic/snippet_spec.rb'
+ - 'ee/spec/models/dast_scanner_profile_spec.rb'
+ - 'ee/spec/models/dast_site_profile_spec.rb'
+ - 'ee/spec/models/ee/ci/job_artifact_spec.rb'
+ - 'ee/spec/models/ee/ci/pending_build_spec.rb'
+ - 'ee/spec/models/ee/ci/runner_spec.rb'
+ - 'ee/spec/models/ee/gpg_key_spec.rb'
+ - 'ee/spec/models/ee/group_spec.rb'
+ - 'ee/spec/models/ee/iteration_spec.rb'
+ - 'ee/spec/models/ee/merge_request_diff_spec.rb'
+ - 'ee/spec/models/ee/namespace/storage/notification_spec.rb'
+ - 'ee/spec/models/ee/vulnerability_spec.rb'
+ - 'ee/spec/models/epic_issue_spec.rb'
+ - 'ee/spec/models/epic_spec.rb'
+ - 'ee/spec/models/geo/container_repository_registry_spec.rb'
+ - 'ee/spec/models/geo/design_registry_spec.rb'
+ - 'ee/spec/models/geo/package_file_registry_spec.rb'
+ - 'ee/spec/models/geo/project_registry_spec.rb'
+ - 'ee/spec/models/geo/secondary_usage_data_spec.rb'
+ - 'ee/spec/models/incident_management/escalation_policy_spec.rb'
+ - 'ee/spec/models/issuable_metric_image_spec.rb'
+ - 'ee/spec/models/issue_spec.rb'
+ - 'ee/spec/models/license_spec.rb'
+ - 'ee/spec/models/merge_train_spec.rb'
+ - 'ee/spec/models/plan_spec.rb'
+ - 'ee/spec/models/project_import_state_spec.rb'
+ - 'ee/spec/models/project_spec.rb'
+ - 'ee/spec/models/release_highlight_spec.rb'
+ - 'ee/spec/models/requirements_management/requirement_spec.rb'
+ - 'ee/spec/models/requirements_management/test_report_spec.rb'
+ - 'ee/spec/models/resource_weight_event_spec.rb'
+ - 'ee/spec/models/uploads/local_spec.rb'
+ - 'ee/spec/models/vulnerabilities/finding_spec.rb'
+ - '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'
+ - 'ee/spec/services/security/ingestion/tasks/update_vulnerability_uuids_spec.rb'
+ - 'ee/spec/services/users/captcha_challenge_service_spec.rb'
+ - 'ee/spec/services/vulnerabilities/create_from_security_finding_service_spec.rb'
+ - 'ee/spec/workers/concerns/elastic/indexing_control_spec.rb'
+ - 'ee/spec/workers/elastic/migration_worker_spec.rb'
+ - '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'
+ - 'spec/controllers/concerns/confirm_email_warning_spec.rb'
+ - 'spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb'
+ - 'spec/controllers/concerns/enforces_admin_authentication_spec.rb'
+ - 'spec/controllers/concerns/graceful_timeout_handling_spec.rb'
+ - 'spec/controllers/concerns/group_tree_spec.rb'
+ - 'spec/controllers/concerns/metrics_dashboard_spec.rb'
+ - 'spec/controllers/concerns/product_analytics_tracking_spec.rb'
+ - 'spec/controllers/concerns/redirects_for_missing_path_on_tree_spec.rb'
+ - 'spec/controllers/concerns/redis_tracking_spec.rb'
+ - 'spec/controllers/concerns/renders_commits_spec.rb'
+ - 'spec/controllers/concerns/routable_actions_spec.rb'
+ - 'spec/controllers/concerns/sourcegraph_decorator_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/concerns/static_object_external_storage_spec.rb'
+ - 'spec/controllers/repositories/git_http_controller_spec.rb'
+ - 'spec/experiments/application_experiment_spec.rb'
+ - 'spec/experiments/concerns/project_commit_count_spec.rb'
+ - 'spec/frontend/fixtures/metrics_dashboard.rb'
+ - 'spec/frontend/fixtures/timezones.rb'
+ - 'spec/frontend/fixtures/u2f.rb'
+ - 'spec/frontend/fixtures/webauthn.rb'
+ - 'spec/graphql/gitlab_schema_spec.rb'
+ - 'spec/graphql/graphql_triggers_spec.rb'
+ - 'spec/graphql/types/global_id_type_spec.rb'
+ - 'spec/initializers/google_api_client_spec.rb'
+ - 'spec/lib/api/entities/project_spec.rb'
+ - 'spec/lib/api/helpers_spec.rb'
+ - 'spec/lib/bulk_imports/groups/stage_spec.rb'
+ - 'spec/lib/bulk_imports/projects/stage_spec.rb'
+ - 'spec/lib/expand_variables_spec.rb'
+ - 'spec/lib/feature_spec.rb'
+ - 'spec/lib/gitlab/application_context_spec.rb'
+ - 'spec/lib/gitlab/asciidoc/html5_converter_spec.rb'
+ - 'spec/lib/gitlab/buffered_io_spec.rb'
+ - 'spec/lib/gitlab/checks/snippet_check_spec.rb'
+ - 'spec/lib/gitlab/ci/tags/bulk_insert_spec.rb'
+ - 'spec/lib/gitlab/ci/trace/chunked_io_spec.rb'
+ - 'spec/lib/gitlab/ci/variables/collection/item_spec.rb'
+ - 'spec/lib/gitlab/ci/variables/collection/sort_spec.rb'
+ - 'spec/lib/gitlab/ci/variables/collection_spec.rb'
+ - 'spec/lib/gitlab/ci/yaml_processor_spec.rb'
+ - 'spec/lib/gitlab/config/entry/composable_array_spec.rb'
+ - 'spec/lib/gitlab/config/entry/composable_hash_spec.rb'
+ - 'spec/lib/gitlab/current_settings_spec.rb'
+ - 'spec/lib/gitlab/database/background_migration/health_status_spec.rb'
+ - 'spec/lib/gitlab/database/load_balancing/host_spec.rb'
+ - 'spec/lib/gitlab/database/load_balancing/primary_host_spec.rb'
+ - 'spec/lib/gitlab/database/migrations/lock_retry_mixin_spec.rb'
+ - 'spec/lib/gitlab/database/query_analyzers/gitlab_schemas_validate_connection_spec.rb'
+ - 'spec/lib/gitlab/database/reindexing_spec.rb'
+ - 'spec/lib/gitlab/database/similarity_score_spec.rb'
+ - 'spec/lib/gitlab/database_spec.rb'
+ - 'spec/lib/gitlab/email/handler_spec.rb'
+ - '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'
+ - 'spec/lib/gitlab/git/compare_spec.rb'
+ - 'spec/lib/gitlab/git/diff_collection_spec.rb'
+ - 'spec/lib/gitlab/git/repository_spec.rb'
+ - 'spec/lib/gitlab/git/tree_spec.rb'
+ - 'spec/lib/gitlab/git_access_snippet_spec.rb'
+ - 'spec/lib/gitlab/gl_repository/repo_type_spec.rb'
+ - 'spec/lib/gitlab/import_formatter_spec.rb'
+ - 'spec/lib/gitlab/inactive_projects_deletion_warning_tracker_spec.rb'
+ - 'spec/lib/gitlab/kubernetes/kube_client_spec.rb'
+ - 'spec/lib/gitlab/no_cache_headers_spec.rb'
+ - 'spec/lib/gitlab/pagination/keyset/order_spec.rb'
+ - 'spec/lib/gitlab/relative_positioning/range_spec.rb'
+ - 'spec/lib/gitlab/runtime_spec.rb'
+ - 'spec/lib/gitlab/search_context/controller_concern_spec.rb'
+ - 'spec/lib/gitlab/seeder_spec.rb'
+ - 'spec/lib/gitlab/serverless/service_spec.rb'
+ - 'spec/lib/gitlab/sidekiq_middleware/size_limiter/client_spec.rb'
+ - 'spec/lib/gitlab/suggestions/commit_message_spec.rb'
+ - 'spec/lib/gitlab/usage/metrics/aggregates/sources/redis_hll_spec.rb'
+ - 'spec/lib/gitlab/utils/measuring_spec.rb'
+ - 'spec/lib/gitlab/webpack/file_loader_spec.rb'
+ - 'spec/lib/gitlab/webpack/manifest_spec.rb'
+ - 'spec/lib/gitlab/x509/commit_spec.rb'
+ - 'spec/lib/gitlab/x509/signature_spec.rb'
+ - 'spec/lib/peek/views/active_record_spec.rb'
+ - 'spec/lib/service_ping/devops_report_spec.rb'
+ - 'spec/lib/sidebars/panel_spec.rb'
+ - 'spec/mailers/emails/service_desk_spec.rb'
+ - 'spec/metrics_server/metrics_server_spec.rb'
+ - 'spec/migrations/remove_records_without_group_from_webhooks_table_spec.rb'
+ - 'spec/models/active_session_spec.rb'
+ - 'spec/models/alert_management/alert_spec.rb'
+ - 'spec/models/application_record_spec.rb'
+ - 'spec/models/application_setting_spec.rb'
+ - 'spec/models/awareness_session_spec.rb'
+ - 'spec/models/broadcast_message_spec.rb'
+ - 'spec/models/chat_name_spec.rb'
+ - 'spec/models/ci/build_runner_session_spec.rb'
+ - 'spec/models/ci/build_spec.rb'
+ - 'spec/models/ci/group_variable_spec.rb'
+ - 'spec/models/ci/job_artifact_spec.rb'
+ - 'spec/models/ci/namespace_mirror_spec.rb'
+ - 'spec/models/ci/pipeline_artifact_spec.rb'
+ - 'spec/models/ci/pipeline_spec.rb'
+ - 'spec/models/ci/processable_spec.rb'
+ - 'spec/models/ci/ref_spec.rb'
+ - 'spec/models/ci/variable_spec.rb'
+ - 'spec/models/ci_platform_metric_spec.rb'
+ - 'spec/models/clusters/cluster_spec.rb'
+ - 'spec/models/commit_spec.rb'
+ - 'spec/models/commit_status_spec.rb'
+ - 'spec/models/concerns/blocks_unsafe_serialization_spec.rb'
+ - 'spec/models/concerns/bulk_insertable_associations_spec.rb'
+ - 'spec/models/concerns/counter_attribute_spec.rb'
+ - 'spec/models/concerns/has_user_type_spec.rb'
+ - 'spec/models/concerns/integrations/enable_ssl_verification_spec.rb'
+ - 'spec/models/concerns/integrations/reset_secret_fields_spec.rb'
+ - 'spec/models/concerns/issuable_spec.rb'
+ - 'spec/models/concerns/mentionable_spec.rb'
+ - 'spec/models/concerns/resolvable_note_spec.rb'
+ - 'spec/models/concerns/token_authenticatable_spec.rb'
+ - 'spec/models/customer_relations/contact_spec.rb'
+ - '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'
+ - 'spec/models/internal_id_spec.rb'
+ - 'spec/models/issue_assignee_spec.rb'
+ - 'spec/models/issue_spec.rb'
+ - 'spec/models/jira_import_state_spec.rb'
+ - 'spec/models/label_link_spec.rb'
+ - 'spec/models/lfs_objects_project_spec.rb'
+ - 'spec/models/loose_foreign_keys/deleted_record_spec.rb'
+ - 'spec/models/member_spec.rb'
+ - 'spec/models/merge_request_assignee_spec.rb'
+ - 'spec/models/merge_request_diff_commit_spec.rb'
+ - 'spec/models/merge_request_diff_file_spec.rb'
+ - 'spec/models/merge_request_spec.rb'
+ - 'spec/models/milestone_spec.rb'
+ - 'spec/models/namespace_spec.rb'
+ - 'spec/models/namespace_statistics_spec.rb'
+ - 'spec/models/oauth_access_token_spec.rb'
+ - 'spec/models/packages/dependency_spec.rb'
+ - 'spec/models/packages/maven/metadatum_spec.rb'
+ - 'spec/models/packages/package_spec.rb'
+ - 'spec/models/pages_deployment_spec.rb'
+ - 'spec/models/pages_domain_spec.rb'
+ - 'spec/models/performance_monitoring/prometheus_dashboard_spec.rb'
+ - 'spec/models/performance_monitoring/prometheus_metric_spec.rb'
+ - 'spec/models/performance_monitoring/prometheus_panel_group_spec.rb'
+ - 'spec/models/performance_monitoring/prometheus_panel_spec.rb'
+ - 'spec/models/plan_spec.rb'
+ - 'spec/models/postgresql/detached_partition_spec.rb'
+ - 'spec/models/preloaders/user_max_access_level_in_projects_preloader_spec.rb'
+ - 'spec/models/preloaders/users_max_access_level_in_projects_preloader_spec.rb'
+ - 'spec/models/project_feature_usage_spec.rb'
+ - 'spec/models/project_setting_spec.rb'
+ - 'spec/models/project_spec.rb'
+ - 'spec/models/projects/topic_spec.rb'
+ - 'spec/models/release_highlight_spec.rb'
+ - 'spec/models/release_spec.rb'
+ - 'spec/models/route_spec.rb'
+ - 'spec/models/todo_spec.rb'
+ - 'spec/models/u2f_registration_spec.rb'
+ - 'spec/models/user_custom_attribute_spec.rb'
+ - 'spec/models/user_preference_spec.rb'
+ - 'spec/models/user_spec.rb'
+ - 'spec/models/users/merge_request_interaction_spec.rb'
+ - 'spec/models/users_statistics_spec.rb'
+ - 'spec/models/wiki_directory_spec.rb'
+ - 'spec/models/work_items/type_spec.rb'
+ - 'spec/serializers/context_commits_diff_entity_spec.rb'
+ - 'spec/services/alert_management/alerts/todo/create_service_spec.rb'
+ - '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'
+ - 'spec/services/notification_service_spec.rb'
+ - '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'
+ - 'spec/services/webauthn/authenticate_service_spec.rb'
+ - 'spec/services/webauthn/register_service_spec.rb'
+ - 'spec/support_specs/helpers/active_record/query_recorder_spec.rb'
+ - 'spec/support_specs/helpers/graphql_helpers_spec.rb'
+ - 'spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb'
+ - 'spec/validators/html_safety_validator_spec.rb'
+ - 'spec/workers/bulk_imports/entity_worker_spec.rb'
+ - 'spec/workers/jira_connect/retry_request_worker_spec.rb'
diff --git a/.rubocop_todo/rspec/empty_line_after_example_group.yml b/.rubocop_todo/rspec/empty_line_after_example_group.yml
new file mode 100644
index 00000000000..80d60ee181e
--- /dev/null
+++ b/.rubocop_todo/rspec/empty_line_after_example_group.yml
@@ -0,0 +1,39 @@
+---
+# 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/expect_change.yml b/.rubocop_todo/rspec/expect_change.yml
index 03066626883..f8962515874 100644
--- a/.rubocop_todo/rspec/expect_change.yml
+++ b/.rubocop_todo/rspec/expect_change.yml
@@ -22,8 +22,6 @@ RSpec/ExpectChange:
- 'ee/spec/controllers/projects/repositories_controller_spec.rb'
- 'ee/spec/controllers/projects/security/vulnerabilities/notes_controller_spec.rb'
- 'ee/spec/controllers/projects_controller_spec.rb'
- - 'ee/spec/controllers/registrations/groups_controller_spec.rb'
- - 'ee/spec/controllers/registrations/groups_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/features/groups/group_settings_spec.rb'
@@ -245,7 +243,6 @@ RSpec/ExpectChange:
- 'ee/spec/services/vulnerability_feedback/create_service_spec.rb'
- 'ee/spec/services/vulnerability_feedback/destroy_service_spec.rb'
- 'ee/spec/support/shared_contexts/audit_event_not_licensed_shared_context.rb'
- - 'ee/spec/support/shared_examples/controllers/registrations/projects_controller_shared_examples.rb'
- 'ee/spec/support/shared_examples/graphql/mutations/update_health_status_shared_examples.rb'
- 'ee/spec/support/shared_examples/models/concerns/replicable_model_with_separate_table_shared_examples.rb'
- 'ee/spec/support/shared_examples/models/geo_verifiable_registry_shared_examples.rb'
@@ -431,6 +428,7 @@ RSpec/ExpectChange:
- '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'
@@ -491,6 +489,7 @@ RSpec/ExpectChange:
- 'spec/services/ci/create_downstream_pipeline_service_spec.rb'
- 'spec/services/ci/create_pipeline_service_spec.rb'
- 'spec/services/ci/destroy_pipeline_service_spec.rb'
+ - 'spec/services/ci/job_artifacts/delete_service_spec.rb'
- 'spec/services/ci/job_artifacts/update_unknown_locked_status_service_spec.rb'
- 'spec/services/ci/play_build_service_spec.rb'
- 'spec/services/ci/process_build_service_spec.rb'
diff --git a/.rubocop_todo/rspec/expect_in_hook.yml b/.rubocop_todo/rspec/expect_in_hook.yml
index d138ea806f8..1551f2dc637 100644
--- a/.rubocop_todo/rspec/expect_in_hook.yml
+++ b/.rubocop_todo/rspec/expect_in_hook.yml
@@ -6,8 +6,6 @@ RSpec/ExpectInHook:
- '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/registrations/groups_controller_spec.rb'
- - 'ee/spec/controllers/registrations/groups_projects_controller_spec.rb'
- 'ee/spec/controllers/subscriptions_controller_spec.rb'
- 'ee/spec/controllers/trials_controller_spec.rb'
- 'ee/spec/elastic/migrate/20220118150500_delete_orphaned_commits_spec.rb'
@@ -17,7 +15,6 @@ 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/combined_registration_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'
@@ -35,7 +32,7 @@ RSpec/ExpectInHook:
- 'ee/spec/helpers/ee/issues_helper_spec.rb'
- 'ee/spec/helpers/ee/projects/security/dast_configuration_helper_spec.rb'
- 'ee/spec/helpers/ee/welcome_helper_spec.rb'
- - 'ee/spec/helpers/kerberos_spnego_helper_spec.rb'
+ - 'ee/spec/helpers/kerberos_helper_spec.rb'
- 'ee/spec/helpers/vulnerabilities_helper_spec.rb'
- 'ee/spec/lib/ee/api/helpers/members_helpers_spec.rb'
- 'ee/spec/lib/ee/gitlab/auth/ldap/sync/group_spec.rb'
@@ -72,7 +69,7 @@ RSpec/ExpectInHook:
- 'ee/spec/requests/api/geo_spec.rb'
- 'ee/spec/requests/api/internal/base_spec.rb'
- 'ee/spec/requests/groups/analytics/devops_adoption_controller_spec.rb'
- - 'ee/spec/requests/omniauth_kerberos_spnego_spec.rb'
+ - 'ee/spec/requests/omniauth_kerberos_spec.rb'
- 'ee/spec/services/analytics/cycle_analytics/stages/update_service_spec.rb'
- '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'
@@ -103,7 +100,6 @@ RSpec/ExpectInHook:
- '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/controllers/registrations/projects_controller_shared_examples.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'
- 'ee/spec/support/shared_examples/models/concerns/verifiable_replicator_shared_examples.rb'
@@ -315,12 +311,10 @@ RSpec/ExpectInHook:
- 'spec/lib/gitlab/verify/uploads_spec.rb'
- 'spec/lib/gitlab/zentao/query_spec.rb'
- 'spec/lib/gitlab_spec.rb'
- - 'spec/lib/learn_gitlab/onboarding_spec.rb'
- 'spec/lib/omni_auth/strategies/jwt_spec.rb'
- 'spec/lib/prometheus/pid_provider_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/learn_gitlab_menu_spec.rb'
- 'spec/mailers/emails/service_desk_spec.rb'
- 'spec/metrics_server/metrics_server_spec.rb'
- 'spec/migrations/20210406144743_backfill_total_tuple_count_for_batched_migrations_spec.rb'
diff --git a/.rubocop_todo/rspec/missing_example_group_argument.yml b/.rubocop_todo/rspec/missing_example_group_argument.yml
new file mode 100644
index 00000000000..1506c9536a0
--- /dev/null
+++ b/.rubocop_todo/rspec/missing_example_group_argument.yml
@@ -0,0 +1,16 @@
+---
+RSpec/MissingExampleGroupArgument:
+ Exclude:
+ - 'ee/spec/controllers/groups/audit_events_controller_spec.rb'
+ - 'ee/spec/services/ee/notification_service_spec.rb'
+ - 'ee/spec/support/shared_examples/controllers/concerns/description_diff_actions_shared_examples.rb'
+ - 'spec/controllers/projects/issues_controller_spec.rb'
+ - 'spec/controllers/projects/merge_requests_controller_spec.rb'
+ - 'spec/factories/projects/ci_feature_usages.rb'
+ - 'spec/lib/gitlab/background_migration/backfill_jira_tracker_deployment_type2_spec.rb'
+ - 'spec/lib/gitlab/git_access_spec.rb'
+ - 'spec/policies/award_emoji_policy_spec.rb'
+ - 'spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb'
+ - 'spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb'
+ - 'spec/services/notification_service_spec.rb'
+ - 'spec/support/shared_examples/graphql/sorted_paginated_query_shared_examples.rb'
diff --git a/.rubocop_todo/rspec/predicate_matcher.yml b/.rubocop_todo/rspec/predicate_matcher.yml
index 7b31ca3622c..f3cfe93191d 100644
--- a/.rubocop_todo/rspec/predicate_matcher.yml
+++ b/.rubocop_todo/rspec/predicate_matcher.yml
@@ -340,6 +340,7 @@ RSpec/PredicateMatcher:
- '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'
diff --git a/.rubocop_todo/rspec/shared_examples.yml b/.rubocop_todo/rspec/shared_examples.yml
new file mode 100644
index 00000000000..612692bdb55
--- /dev/null
+++ b/.rubocop_todo/rspec/shared_examples.yml
@@ -0,0 +1,24 @@
+---
+# 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/verified_doubles.yml b/.rubocop_todo/rspec/verified_doubles.yml
index 70b54d5bc7f..a225dbf21ae 100644
--- a/.rubocop_todo/rspec/verified_doubles.yml
+++ b/.rubocop_todo/rspec/verified_doubles.yml
@@ -41,7 +41,7 @@ RSpec/VerifiedDoubles:
- 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_spnego_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
@@ -90,7 +90,6 @@ RSpec/VerifiedDoubles:
- 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/approvable_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
@@ -449,7 +448,6 @@ RSpec/VerifiedDoubles:
- 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/command_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
diff --git a/.rubocop_todo/style/bare_percent_literals.yml b/.rubocop_todo/style/bare_percent_literals.yml
index 104ead817d5..1a155e3cca0 100644
--- a/.rubocop_todo/style/bare_percent_literals.yml
+++ b/.rubocop_todo/style/bare_percent_literals.yml
@@ -1,16 +1,13 @@
---
# Cop supports --auto-correct.
Style/BarePercentLiterals:
- # Offense count: 220
- # Temporarily disabled due to too many offenses
- Enabled: false
+ Details: grace period
Exclude:
- 'app/models/commit.rb'
- 'app/models/concerns/storage/legacy_namespace.rb'
- 'app/models/integrations/datadog.rb'
- 'app/services/feature_flags/base_service.rb'
- 'app/services/repositories/base_service.rb'
- - 'app/services/repositories/destroy_service.rb'
- 'ee/app/services/jira/jql_builder_service.rb'
- 'ee/lib/ee/gitlab/checks/push_rules/file_size_check.rb'
- 'ee/spec/features/projects/environments/environments_spec.rb'
@@ -41,17 +38,15 @@ Style/BarePercentLiterals:
- 'qa/qa/ee/page/project/show.rb'
- 'qa/qa/ee/page/project/snippet/index.rb'
- 'qa/qa/ee/page/project/wiki/show.rb'
- - 'qa/qa/page/component/design_management.rb'
- 'qa/qa/page/component/select2.rb'
- 'qa/qa/page/element.rb'
- 'qa/qa/page/file/form.rb'
- 'qa/qa/page/project/web_ide/edit.rb'
- 'qa/qa/resource/events/project.rb'
- - 'qa/qa/resource/members.rb'
+ - 'qa/qa/resource/personal_access_token_cache.rb'
- 'qa/qa/specs/features/browser_ui/2_plan/email/trigger_email_notification_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/1_manage/group/group_saml_enforced_sso_new_account_spec.rb'
+ - 'qa/qa/specs/features/browser_ui/4_verify/pipeline/pipeline_with_image_pull_policy_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/3_create/repository/push_rules_spec.rb'
- - 'qa/qa/support/page/logging.rb'
- 'qa/spec/runtime/feature_spec.rb'
- 'scripts/regenerate-schema'
- 'scripts/trigger-build.rb'
@@ -79,6 +74,7 @@ Style/BarePercentLiterals:
- 'spec/lib/banzai/filter/references/label_reference_filter_spec.rb'
- 'spec/lib/banzai/filter/references/milestone_reference_filter_spec.rb'
- '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'
@@ -95,6 +91,7 @@ Style/BarePercentLiterals:
- 'spec/mailers/emails/releases_spec.rb'
- 'spec/mailers/emails/service_desk_spec.rb'
- 'spec/models/deployment_spec.rb'
+ - 'spec/models/incident_management/timeline_event_spec.rb'
- 'spec/models/integrations/drone_ci_spec.rb'
- 'spec/models/integrations/teamcity_spec.rb'
- 'spec/models/project_label_spec.rb'
@@ -102,6 +99,8 @@ Style/BarePercentLiterals:
- 'spec/requests/api/ci/job_artifacts_spec.rb'
- 'spec/requests/api/deployments_spec.rb'
- 'spec/requests/api/graphql/mutations/snippets/destroy_spec.rb'
+ - 'spec/requests/api/graphql/project/incident_management/timeline_events_spec.rb'
+ - 'spec/requests/projects/packages/package_files_controller_spec.rb'
- 'spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb'
- 'spec/services/prometheus/proxy_variable_substitution_service_spec.rb'
- 'spec/support/banzai/reference_filter_shared_examples.rb'
diff --git a/.rubocop_todo/style/class_and_module_children.yml b/.rubocop_todo/style/class_and_module_children.yml
index e0747f232a1..2674902c7ee 100644
--- a/.rubocop_todo/style/class_and_module_children.yml
+++ b/.rubocop_todo/style/class_and_module_children.yml
@@ -384,9 +384,6 @@ Style/ClassAndModuleChildren:
- 'ee/app/controllers/admin/push_rules_controller.rb'
- 'ee/app/controllers/admin/subscriptions_controller.rb'
- 'ee/app/controllers/admin/user_permission_exports_controller.rb'
- - 'ee/app/controllers/concerns/registrations/apply_trial.rb'
- - 'ee/app/controllers/concerns/registrations/create_group.rb'
- - 'ee/app/controllers/concerns/registrations/create_project.rb'
- 'ee/app/controllers/concerns/registrations/verification.rb'
- 'ee/app/controllers/ee/profiles/accounts_controller.rb'
- 'ee/app/controllers/ee/profiles/preferences_controller.rb'
diff --git a/.rubocop_todo/style/empty_else.yml b/.rubocop_todo/style/empty_else.yml
index f211d022dcd..07e42692f60 100644
--- a/.rubocop_todo/style/empty_else.yml
+++ b/.rubocop_todo/style/empty_else.yml
@@ -29,7 +29,7 @@ Style/EmptyElse:
- 'config/initializers/doorkeeper_openid_connect.rb'
- 'ee/app/controllers/admin/audit_logs_controller.rb'
- 'ee/app/controllers/ee/groups_controller.rb'
- - 'ee/app/helpers/ee/kerberos_spnego_helper.rb'
+ - 'ee/app/helpers/ee/kerberos_helper.rb'
- 'ee/app/helpers/ee/trial_helper.rb'
- 'ee/app/models/ee/audit_event.rb'
- 'ee/app/services/ee/users/update_service.rb'
diff --git a/.rubocop_todo/style/float_division.yml b/.rubocop_todo/style/float_division.yml
new file mode 100644
index 00000000000..7fd0cda469d
--- /dev/null
+++ b/.rubocop_todo/style/float_division.yml
@@ -0,0 +1,7 @@
+---
+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 bb95d8f7fe9..66c368a7a52 100644
--- a/.rubocop_todo/style/format_string.yml
+++ b/.rubocop_todo/style/format_string.yml
@@ -65,6 +65,7 @@ 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'
diff --git a/.rubocop_todo/style/hash_as_last_array_item.yml b/.rubocop_todo/style/hash_as_last_array_item.yml
index 384d2dc5fce..aa22e9ed82b 100644
--- a/.rubocop_todo/style/hash_as_last_array_item.yml
+++ b/.rubocop_todo/style/hash_as_last_array_item.yml
@@ -20,7 +20,6 @@ Style/HashAsLastArrayItem:
- '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/learn_gitlab_helper.rb'
- 'app/helpers/namespaces_helper.rb'
- 'app/models/customer_relations/contact.rb'
- 'app/models/customer_relations/organization.rb'
diff --git a/.rubocop_todo/style/if_unless_modifier.yml b/.rubocop_todo/style/if_unless_modifier.yml
index a58f71aee8f..cc21860d462 100644
--- a/.rubocop_todo/style/if_unless_modifier.yml
+++ b/.rubocop_todo/style/if_unless_modifier.yml
@@ -652,6 +652,7 @@ Style/IfUnlessModifier:
- 'ee/app/services/vulnerability_feedback/create_service.rb'
- 'ee/app/services/vulnerability_feedback/destroy_service.rb'
- 'ee/app/services/vulnerability_feedback_module/update_service.rb'
+ - 'ee/app/services/elastic/cluster_reindexing_service.rb'
- 'ee/app/validators/host_validator.rb'
- 'ee/app/validators/password/complexity_validator.rb'
- 'ee/app/workers/app_sec/dast/profile_schedule_worker.rb'
diff --git a/.rubocop_todo/style/lambda.yml b/.rubocop_todo/style/lambda.yml
index 5b898417d96..f733af601ec 100644
--- a/.rubocop_todo/style/lambda.yml
+++ b/.rubocop_todo/style/lambda.yml
@@ -77,7 +77,7 @@ Style/Lambda:
- 'app/models/note.rb'
- 'app/models/note_diff_file.rb'
- 'app/models/notification_setting.rb'
- - 'app/models/onboarding_progress.rb'
+ - 'app/models/onboarding/progress.rb'
- 'app/models/operations/feature_flags/user_list.rb'
- 'app/models/packages/package.rb'
- 'app/models/packages/package_file.rb'
@@ -217,7 +217,6 @@ Style/Lambda:
- 'lib/gitlab/sidekiq_signals.rb'
- 'lib/gitlab/utils/measuring.rb'
- 'lib/gitlab/visibility_level.rb'
- - 'qa/qa/fixtures/auto_devops_rack/config.ru'
- 'rubocop/cop/rspec/modify_sidekiq_middleware.rb'
- 'rubocop/cop/rspec/timecop_freeze.rb'
- 'rubocop/cop/rspec/timecop_travel.rb'
diff --git a/.rubocop_todo/style/next.yml b/.rubocop_todo/style/next.yml
index 4106cba955f..9570bd7b036 100644
--- a/.rubocop_todo/style/next.yml
+++ b/.rubocop_todo/style/next.yml
@@ -3,17 +3,7 @@
Style/Next:
Exclude:
- 'app/models/concerns/integrations/slack_mattermost_notifier.rb'
- - 'app/models/preloaders/environments/deployment_preloader.rb'
- - 'app/models/route.rb'
- - 'app/services/authorized_project_update/find_records_due_for_refresh_service.rb'
- 'app/validators/nested_attributes_duplicates_validator.rb'
- - 'config/initializers/01_secret_token.rb'
- - 'config/initializers/sidekiq_cluster.rb'
- - 'ee/app/controllers/groups/analytics/cycle_analytics/value_streams_controller.rb'
- - 'ee/app/services/app_sec/dast/profiles/create_associations_service.rb'
- - 'ee/app/services/elastic/cluster_reindexing_service.rb'
- - 'ee/app/services/gitlab_subscriptions/fetch_purchase_eligible_namespaces_service.rb'
- - 'ee/app/services/security/auto_fix_service.rb'
- 'ee/app/services/security/ingestion/tasks/update_vulnerability_uuids.rb'
- 'ee/db/fixtures/development/20_vulnerabilities.rb'
- 'ee/lib/ee/audit/protected_branches_changes_auditor.rb'
diff --git a/.rubocop_todo/style/percent_literal_delimiters.yml b/.rubocop_todo/style/percent_literal_delimiters.yml
index ae019079a8d..9989ae3f8b3 100644
--- a/.rubocop_todo/style/percent_literal_delimiters.yml
+++ b/.rubocop_todo/style/percent_literal_delimiters.yml
@@ -254,7 +254,6 @@ Style/PercentLiteralDelimiters:
- 'ee/app/mailers/previews/license_mailer_preview.rb'
- 'ee/app/models/app_sec/fuzzing/api/scan_profile.rb'
- 'ee/app/models/app_sec/fuzzing/coverage/corpus.rb'
- - 'ee/app/models/concerns/approvable.rb'
- 'ee/app/models/concerns/ee/issue_available_features.rb'
- 'ee/app/models/ee/audit_event.rb'
- 'ee/app/models/ee/description_version.rb'
diff --git a/.rubocop_todo/style/redundant_self.yml b/.rubocop_todo/style/redundant_self.yml
index 65f2ea327d7..8c688dc89c1 100644
--- a/.rubocop_todo/style/redundant_self.yml
+++ b/.rubocop_todo/style/redundant_self.yml
@@ -47,7 +47,6 @@ Style/RedundantSelf:
- 'app/models/commit_status.rb'
- 'app/models/compare.rb'
- 'app/models/concerns/after_commit_queue.rb'
- - 'app/models/concerns/approvable_base.rb'
- 'app/models/concerns/atomic_internal_id.rb'
- 'app/models/concerns/avatarable.rb'
- 'app/models/concerns/awardable.rb'
diff --git a/.rubocop_todo/style/string_concatenation.yml b/.rubocop_todo/style/string_concatenation.yml
index 2330683cc18..3dd708d2c49 100644
--- a/.rubocop_todo/style/string_concatenation.yml
+++ b/.rubocop_todo/style/string_concatenation.yml
@@ -74,7 +74,7 @@ Style/StringConcatenation:
- 'ee/lib/elastic/latest/git_class_proxy.rb'
- 'ee/lib/gitlab/elastic/search_results.rb'
- 'ee/lib/gitlab/geo/git_ssh_proxy.rb'
- - 'ee/lib/omni_auth/strategies/kerberos_spnego.rb'
+ - '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'
diff --git a/.rubocop_todo/style/symbol_proc.yml b/.rubocop_todo/style/symbol_proc.yml
index 75aab7c6116..bfb3867b127 100644
--- a/.rubocop_todo/style/symbol_proc.yml
+++ b/.rubocop_todo/style/symbol_proc.yml
@@ -239,7 +239,6 @@ Style/SymbolProc:
- 'spec/graphql/mutations/releases/create_spec.rb'
- 'spec/graphql/types/work_items/widget_type_enum_spec.rb'
- 'spec/helpers/instance_configuration_helper_spec.rb'
- - 'spec/helpers/learn_gitlab_helper_spec.rb'
- 'spec/helpers/members_helper_spec.rb'
- 'spec/lib/backup/gitaly_backup_spec.rb'
- 'spec/lib/gitlab/database/dynamic_model_helpers_spec.rb'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 387453ccc64..b8d3d239aad 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -645,6 +645,36 @@ entry.
- [Remove FF import_release_authors_from_github](gitlab-org/gitlab@c4d6871e4438a1626d688856903778623138f671) ([merge request](gitlab-org/gitlab!92686))
- [Remove unused feature](gitlab-org/gitlab@0ef95d341e4a15150d6ccb3d104ebbe064aa062a) ([merge request](gitlab-org/gitlab!92753))
+## 15.2.4 (2022-08-30)
+
+### Security (18 changes)
+
+- [No overriding methods for Sawyer class](gitlab-org/security/gitlab@fafcaf91c510ace5c3fc845197fa71d2ad8943cc) ([merge request](gitlab-org/security/gitlab!2755))
+- [Update Oj to v3.13.21](gitlab-org/security/gitlab@e14f62112f51315288f3f08108b59cf40ab5635e) ([merge request](gitlab-org/security/gitlab!2729))
+- [Bump yajl-ruby gem version](gitlab-org/security/gitlab@ad7469e802aff36989276bd77afcebf9bcb8a545) ([merge request](gitlab-org/security/gitlab!2689))
+- [Prevent long loops when generating suggested branch name](gitlab-org/security/gitlab@f8f1631a7751b40444debbd69188187c895d2ad6) ([merge request](gitlab-org/security/gitlab!2744))
+- [IDOR in Zentao integration issue show page](gitlab-org/security/gitlab@01004871400564e5b18a2efa4f6d87c8ca37db5c) ([merge request](gitlab-org/security/gitlab!2741))
+- [Patch VULNDB-255039 (potential Rack cache poisoning)](gitlab-org/security/gitlab@a951318f5870e8f35c742eab58132c63d6d36198) ([merge request](gitlab-org/security/gitlab!2694))
+- [HTML escape the label background color](gitlab-org/security/gitlab@de115e3b0896aa1504882d3230b5427506fee3e2) ([merge request](gitlab-org/security/gitlab!2720))
+- [Sandbox jupyter notebook HTML output](gitlab-org/security/gitlab@67aeba4ae4c95d2668d0428cb66d263ee4247b68) ([merge request](gitlab-org/security/gitlab!2711))
+- [Fix unauthorized GFM references in Incident Timeline](gitlab-org/security/gitlab@f091bc238efa1d669c1257aa146339f4b1134a0c) ([merge request](gitlab-org/security/gitlab!2708))
+- [Optimize handling repositories with huge trees](gitlab-org/security/gitlab@9969c2cabccef2367631498f38ab8d0b19cf9da3) ([merge request](gitlab-org/security/gitlab!2666))
+- [Parse commit trailers without using regexp](gitlab-org/security/gitlab@9bd64457525313a949f151fd27f2954ff71e399d) ([merge request](gitlab-org/security/gitlab!2700))
+- [Check for pathological markdown input](gitlab-org/security/gitlab@c05642874c38e4d914297ad788a07c42b77b6b1e) ([merge request](gitlab-org/security/gitlab!2732))
+- [Replaced smooshpack to fix the vulnerability in LivePreview](gitlab-org/security/gitlab@e48df65563c6c66fd6d89fb7bf626bdf8b465cc0) ([merge request](gitlab-org/security/gitlab!2662))
+- [Update package auth for group IP allowlist](gitlab-org/security/gitlab@eb7b9e646732cc3590e00d5694d5a662e71c9f99) ([merge request](gitlab-org/security/gitlab!2684))
+- [Don't show pipeline status](gitlab-org/security/gitlab@a5962d9ee7aec4f86a982f2d686a690806df6f15) ([merge request](gitlab-org/security/gitlab!2680))
+- [Sanitize img attributes in Banzai::Filter::ImageLinkFilter](gitlab-org/security/gitlab@ee68b29c2199e1c399a4d0065ed53c50592e54a0) ([merge request](gitlab-org/security/gitlab!2676))
+- [Validate description length for snippets](gitlab-org/security/gitlab@e9e4c3b3109590a5c12ecb2f25e4641dd408ce36) ([merge request](gitlab-org/security/gitlab!2703))
+- [Prevent brute force vuln for Git over HTTP(S) requests](gitlab-org/security/gitlab@aab24e532b8c0b9e8acc90e7954434519e19b908) ([merge request](gitlab-org/security/gitlab!2717))
+
+## 15.2.3 (2022-08-22)
+
+### Security (2 changes)
+
+- [Validate if values to be saved in Redis can be converted to string](gitlab-org/security/gitlab@427c7818b229fd45b10cb5de9ea6cc7c451dd4da) ([merge request](gitlab-org/security/gitlab!2724))
+- [Fix CSS selector used in specs](gitlab-org/security/gitlab@47bb40d097e2b05ecdbeebf6bdbe6eb9b6db1c7b) ([merge request](gitlab-org/security/gitlab!2727))
+
## 15.2.2 (2022-08-01)
### Fixed (6 changes)
@@ -1361,6 +1391,35 @@ entry.
- [Update GitLab Runner Helm Chart to 0.42.0](gitlab-org/gitlab@cc89200f498fe216864914c79b5b0d1d578edab3) ([merge request](gitlab-org/gitlab!90605))
- [Address database documentation Vale warningss](gitlab-org/gitlab@e5f9a089766bace046d3bbd760a2979865a4bbc0) by @cgives ([merge request](gitlab-org/gitlab!90093))
+## 15.1.6 (2022-08-30)
+
+### Security (17 changes)
+
+- [No overriding methods for Sawyer class](gitlab-org/security/gitlab@720a17d03791c298d193b2d49d322a5f259bb6f2) ([merge request](gitlab-org/security/gitlab!2756))
+- [Bump yajl-ruby gem version](gitlab-org/security/gitlab@acb8bee73354ddbd7a7a52e3d09c870d1cd99e27) ([merge request](gitlab-org/security/gitlab!2690))
+- [Prevent long loops when generating suggested branch name](gitlab-org/security/gitlab@e331ecf658de25901def2ea4a368104b82a0109c) ([merge request](gitlab-org/security/gitlab!2745))
+- [IDOR in Zentao integration issue show page](gitlab-org/security/gitlab@0a238baf6a1d4aa0bc834448aefaf756d594a7be) ([merge request](gitlab-org/security/gitlab!2742))
+- [Patch VULNDB-255039 (potential Rack cache poisoning)](gitlab-org/security/gitlab@1f5ecd95b3631c8352ff57cf4bee23d26aa51ecc) ([merge request](gitlab-org/security/gitlab!2695))
+- [HTML escape the label background color](gitlab-org/security/gitlab@470b75a53ea4383ea30de5a482d39b322f87dfa2) ([merge request](gitlab-org/security/gitlab!2721))
+- [Sandbox jupyter notebook HTML output](gitlab-org/security/gitlab@72089898a60de7f17c19a2fa9d4f1330d3052b52) ([merge request](gitlab-org/security/gitlab!2713))
+- [Fix unauthorized GFM references in Incident Timeline](gitlab-org/security/gitlab@c62408682ed47bc2e5f93585a5b4e92e8cfebf9f) ([merge request](gitlab-org/security/gitlab!2709))
+- [Optimize handling repositories with huge trees](gitlab-org/security/gitlab@396f20e019a9888d1645e9345a82fdf21153bf76) ([merge request](gitlab-org/security/gitlab!2667))
+- [Parse commit trailers without using regexp](gitlab-org/security/gitlab@b377a1ecbb37c5359b2c2a0ecfbd911654664700) ([merge request](gitlab-org/security/gitlab!2701))
+- [Check for pathological markdown input](gitlab-org/security/gitlab@e3a1376ec70d8d60f11a380cce6e0b3c35f68646) ([merge request](gitlab-org/security/gitlab!2731))
+- [Replaced smooshpack to fix the vulnerability in LivePreview](gitlab-org/security/gitlab@d520ffd2a5a75d33ac98c39cd2f2fe623b0e1115) ([merge request](gitlab-org/security/gitlab!2664))
+- [Update package auth for group IP allowlist](gitlab-org/security/gitlab@12bb8656bdaa9a7502c0a1b77c12fefb72677ba1) ([merge request](gitlab-org/security/gitlab!2685))
+- [Don't show pipeline status](gitlab-org/security/gitlab@7fb43f899f2342704bda81643f8375a126efc2ae) ([merge request](gitlab-org/security/gitlab!2679))
+- [Sanitize img attributes in Banzai::Filter::ImageLinkFilter](gitlab-org/security/gitlab@594fa5874fb7cc6b6588bbf8aff2f04b8acbbfd0) ([merge request](gitlab-org/security/gitlab!2677))
+- [Validate description length for snippets](gitlab-org/security/gitlab@94ae3d05741bc69b9307e5f58f0d61bf2566c21b) ([merge request](gitlab-org/security/gitlab!2704))
+- [Prevent brute force vuln for Git over HTTP(S) requests](gitlab-org/security/gitlab@7b76542e197ea72289c881c312b3a519c8b28e63) ([merge request](gitlab-org/security/gitlab!2718))
+
+## 15.1.5 (2022-08-22)
+
+### Security (2 changes)
+
+- [Validate if values to be saved in Redis can be converted to string](gitlab-org/security/gitlab@0bc82f875f78b6a2703b294b1a5a8d941000f9f2) ([merge request](gitlab-org/security/gitlab!2725))
+- [Fix CSS selector used in specs](gitlab-org/security/gitlab@a900e65b192a8415baa6bcd4050566543107ab1f) ([merge request](gitlab-org/security/gitlab!2728))
+
## 15.1.4 (2022-07-28)
### Security (18 changes)
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index e618248e56c..0b94efba568 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-15.3.3 \ No newline at end of file
+b4c1f29c487a41b2e69a31a99f6b0ac462c81ce4
diff --git a/GITLAB_ELASTICSEARCH_INDEXER_VERSION b/GITLAB_ELASTICSEARCH_INDEXER_VERSION
index cb2b00e4f7a..b5021469305 100644
--- a/GITLAB_ELASTICSEARCH_INDEXER_VERSION
+++ b/GITLAB_ELASTICSEARCH_INDEXER_VERSION
@@ -1 +1 @@
-3.0.1
+3.0.2
diff --git a/GITLAB_KAS_VERSION b/GITLAB_KAS_VERSION
index 5ff58dbcd68..c915b5db730 100644
--- a/GITLAB_KAS_VERSION
+++ b/GITLAB_KAS_VERSION
@@ -1 +1 @@
-15.3.0
+15.4.0
diff --git a/Gemfile b/Gemfile
index c8eca55525d..6631a889375 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,6 +2,8 @@
source 'https://rubygems.org'
+gem 'bundler-checksum', '~> 0.1.0', path: 'vendor/gems/bundler-checksum', require: false
+
gem 'rails', '~> 6.1.6.1'
gem 'bootsnap', '~> 1.13.0', require: false
@@ -11,7 +13,7 @@ gem 'responders', '~> 3.0'
gem 'sprockets', '~> 3.7.0'
-gem 'view_component', '~> 2.61'
+gem 'view_component', '~> 2.71.0'
# Default values for AR models
gem 'default_value_for', '~> 3.4.0'
@@ -29,32 +31,33 @@ gem 'marginalia', '~> 1.10.0'
gem 'declarative_policy', '~> 1.1.0'
# Authentication libraries
-gem 'devise', '~> 4.7.2'
+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 'rexml', '~> 3.2.5'
gem 'ruby-saml', '~> 1.13.0'
-gem 'omniauth', '~> 1.8'
+gem 'omniauth-rails_csrf_protection'
+gem 'omniauth', '~> 2.1.0'
gem 'omniauth-auth0', '~> 2.0.0'
gem 'omniauth-azure-activedirectory-v2', '~> 1.0'
-gem 'omniauth-azure-oauth2', '~> 0.0.9' # See vendor/gems/omniauth-azure-oauth2/README.md
+gem 'omniauth-azure-oauth2', '~> 0.0.9', path: 'vendor/gems/omniauth-azure-oauth2' # See gem README.md
gem 'omniauth-cas3', '~> 1.1.4', path: 'vendor/gems/omniauth-cas3' # See vendor/gems/omniauth-cas3/README.md
gem 'omniauth-dingtalk-oauth2', '~> 1.0'
gem 'omniauth-alicloud', '~> 1.0.1'
gem 'omniauth-facebook', '~> 4.0.0'
-gem 'omniauth-github', '~> 1.4'
+gem 'omniauth-github', '2.0.0'
gem 'omniauth-gitlab', '~> 4.0.0', path: 'vendor/gems/omniauth-gitlab' # See vendor/gems/omniauth-gitlab/README.md
-gem 'omniauth-google-oauth2', '~> 0.6.0'
+gem 'omniauth-google-oauth2', '~> 1.0.1', path: 'vendor/gems/omniauth-google-oauth2' # See gem README.md
gem 'omniauth-oauth2-generic', '~> 0.2.2'
-gem 'omniauth-saml', '~> 1.10'
+gem 'omniauth-saml', '~> 2.0.0'
gem 'omniauth-shibboleth', '~> 1.3.0'
gem 'omniauth-twitter', '~> 1.4'
gem 'omniauth_crowd', '~> 2.4.0', path: 'vendor/gems/omniauth_crowd' # See vendor/gems/omniauth_crowd/README.md
gem 'omniauth-authentiq', '~> 0.3.3'
-gem 'gitlab-omniauth-openid-connect', '~> 0.9.0', require: 'omniauth_openid_connect'
-gem 'omniauth-salesforce', '~> 1.0.5'
+gem 'gitlab-omniauth-openid-connect', '~> 0.10.0', require: 'omniauth_openid_connect'
+gem 'omniauth-salesforce', '~> 1.0.5', path: 'vendor/gems/omniauth-salesforce' # See gem README.md
gem 'omniauth-atlassian-oauth2', '~> 0.2.0'
gem 'rack-oauth2', '~> 1.21.2'
gem 'jwt', '~> 2.1.0'
@@ -164,21 +167,21 @@ gem 'rdoc', '~> 6.3.2'
gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1'
-gem 'asciidoctor', '~> 2.0.10'
+gem 'asciidoctor', '~> 2.0.17'
gem 'asciidoctor-include-ext', '~> 0.4.0', require: false
-gem 'asciidoctor-plantuml', '~> 0.0.12'
+gem 'asciidoctor-plantuml', '~> 0.0.16'
gem 'asciidoctor-kroki', '~> 0.5.0', require: false
gem 'rouge', '~> 3.30.0'
gem 'truncato', '~> 0.7.12'
gem 'bootstrap_form', '~> 4.2.0'
-gem 'nokogiri', '~> 1.13.0'
+gem 'nokogiri', '~> 1.13.8'
gem 'escape_utils', '~> 1.1'
# Calendar rendering
gem 'icalendar'
# Diffs
-gem 'diffy', '~> 3.3'
+gem 'diffy', '~> 3.4'
gem 'diff_match_patch', '~> 0.1.0'
# Application server
@@ -187,7 +190,7 @@ gem 'rack', '~> 2.2.4'
gem 'rack-timeout', '~> 0.6.0', require: 'rack/timeout/base'
group :puma do
- gem 'puma', '~> 5.6.4', require: false
+ gem 'puma', '~> 5.6.5', require: false
gem 'puma_worker_killer', '~> 0.3.1', require: false
gem 'sd_notify', '~> 0.1.0', require: false
end
@@ -199,8 +202,8 @@ gem 'state_machines-activerecord', '~> 0.8.0'
gem 'acts-as-taggable-on', '~> 9.0'
# Background jobs
-gem 'sidekiq', '~> 6.4'
-gem 'sidekiq-cron', '~> 1.2'
+gem 'sidekiq', '~> 6.4.0'
+gem 'sidekiq-cron', '~> 1.4.0'
gem 'redis-namespace', '~> 1.8.1'
gem 'gitlab-sidekiq-fetcher', '0.8.0', require: 'sidekiq-reliable-fetch'
@@ -233,7 +236,7 @@ gem 'js_regex', '~> 3.7'
gem 'device_detector'
# Redis
-gem 'redis', '~> 4.4.0'
+gem 'redis', '~> 4.7.0'
gem 'connection_pool', '~> 2.0'
# Redis session store
@@ -272,7 +275,9 @@ gem 'babosa', '~> 1.0.4'
gem 'loofah', '~> 2.18.0'
# Working with license
-gem 'licensee', '~> 9.14.1'
+# Detects the open source license the repository includes
+# This version needs to be in sync with gitlab-org/gitaly
+gem 'licensee', '~> 9.15'
# Detect and convert string character encoding
gem 'charlock_holmes', '~> 0.7.7'
@@ -318,9 +323,7 @@ gem 'premailer-rails', '~> 1.10.3'
# LabKit: Tracing and Correlation
gem 'gitlab-labkit', '~> 0.24.0'
-# Thrift is a dependency of gitlab-labkit, we want a version higher than 0.14.0
-# because of https://gitlab.com/gitlab-org/gitlab/-/issues/321900
-gem 'thrift', '>= 0.14.0'
+gem 'thrift', '>= 0.16.0'
# I18n
gem 'ruby_parser', '~> 3.15', require: false
@@ -338,22 +341,21 @@ gem 'peek', '~> 1.1'
gem 'snowplow-tracker', '~> 0.6.1'
# Metrics
-gem 'method_source', '~> 1.0', require: false
gem 'webrick', '~> 1.6.1', require: false
gem 'prometheus-client-mmap', '~> 0.16', require: 'prometheus/client'
gem 'warning', '~> 1.3.0'
group :development do
- gem 'lefthook', '~> 1.1.0', require: false
+ gem 'lefthook', '~> 1.1.1', require: false
gem 'rubocop'
- gem 'solargraph', '~> 0.45.0', require: false
+ gem 'solargraph', '~> 0.46.0', require: false
gem 'letter_opener_web', '~> 2.0.0'
- gem 'lookbook'
+ gem 'lookbook', '~> 1.0'
# Better errors handler
- gem 'better_errors', '~> 2.9.0'
+ gem 'better_errors', '~> 2.9.1'
gem 'sprite-factory', '~> 1.7'
end
@@ -380,12 +382,14 @@ group :development, :test do
gem 'spring', '~> 2.1.0'
gem 'spring-commands-rspec', '~> 1.0.4'
- gem 'gitlab-styles', '~> 7.1.0', require: false
+ gem 'gitlab-styles', '~> 8.0.0', require: false
- gem 'haml_lint', '~> 0.36.0', require: false
+ gem 'haml_lint', '~> 0.40.0', require: false
gem 'bundler-audit', '~> 0.7.0.1', require: false
+ # Benchmarking & profiling
gem 'benchmark-ips', '~> 2.3.0', require: false
+ gem 'benchmark-memory', '~> 0.1', require: false
gem 'knapsack', '~> 1.21.1'
gem 'crystalball', '~> 0.7.0', require: false
@@ -406,7 +410,7 @@ group :development, :test do
end
group :development, :test, :danger do
- gem 'gitlab-dangerfiles', '~> 3.5.0', require: false
+ gem 'gitlab-dangerfiles', '~> 3.5.2', require: false
end
group :development, :test, :coverage do
@@ -457,10 +461,9 @@ gem 'email_reply_trimmer', '~> 0.1'
gem 'html2text'
gem 'ruby-prof', '~> 1.3.0'
-gem 'stackprof', '~> 0.2.15', require: false
+gem 'stackprof', '~> 0.2.21', require: false
gem 'rbtrace', '~> 0.4', require: false
gem 'memory_profiler', '~> 0.9', require: false
-gem 'benchmark-memory', '~> 0.1', require: false
gem 'activerecord-explain-analyze', '~> 0.1', require: false
# OAuth
@@ -480,10 +483,10 @@ gem 'net-ntp'
gem 'ssh_data', '~> 1.3'
# Spamcheck GRPC protocol definitions
-gem 'spamcheck', '~> 0.1.0'
+gem 'spamcheck', '~> 1.0.0'
# Gitaly GRPC protocol definitions
-gem 'gitaly', '~> 15.3.0-rc3'
+gem 'gitaly', '~> 15.4.0-rc2'
# KAS GRPC protocol definitions
gem 'kas-grpc', '~> 0.0.2'
@@ -524,6 +527,8 @@ gem 'erubi', '~> 1.9.0'
gem 'mail', '= 2.7.1'
gem 'mail-smtp_pool', '~> 0.1.0', path: 'vendor/gems/mail-smtp_pool', require: false
+gem 'microsoft_graph_mailer', '~> 0.1.0', path: 'vendor/gems/microsoft_graph_mailer'
+
# File encryption
gem 'lockbox', '~> 0.6.2'
diff --git a/Gemfile.checksum b/Gemfile.checksum
new file mode 100644
index 00000000000..542a8d25fb6
--- /dev/null
+++ b/Gemfile.checksum
@@ -0,0 +1,639 @@
+[
+{"name":"RedCloth","version":"4.3.2","platform":"ruby","checksum":"1ee7bc55c8dcec92cf7741a2132a9a6cd19e4b884fbc1b3aca23e1a4fcd92d55"},
+{"name":"acme-client","version":"2.0.11","platform":"ruby","checksum":"edf6da9f3c5dbe3ab0c6738eb3b97978b7a60e3500445480d2a72fcc610089de"},
+{"name":"actioncable","version":"6.1.6.1","platform":"ruby","checksum":"11f079141cf032026881e4a79ae0cc93753351089c1b6ca1ed30a8a6a21f961b"},
+{"name":"actionmailbox","version":"6.1.6.1","platform":"ruby","checksum":"a4cc16fe634c9de4e22669fc4bf20d5b84f65039c7e3d7308c804b82726d03d2"},
+{"name":"actionmailer","version":"6.1.6.1","platform":"ruby","checksum":"13964bff4a75efd705304cb7aeb71380a4b11d404c7304b67f3bc3208cde12a7"},
+{"name":"actionpack","version":"6.1.6.1","platform":"ruby","checksum":"f3e0a82a62aa36fecadbacbb266e38338da032f18aaf97674f335671b420bdd4"},
+{"name":"actiontext","version":"6.1.6.1","platform":"ruby","checksum":"ff26b96769b6f4bdf3c0e74f613b232b2cdab7e46f1433c9cfa4fdcd081afac0"},
+{"name":"actionview","version":"6.1.6.1","platform":"ruby","checksum":"a87fc7d2c4fe9b6357492a3ee361be8169f3f319f47bf70fda1b1718b944d06b"},
+{"name":"activejob","version":"6.1.6.1","platform":"ruby","checksum":"9efee4499d31aaaab73b843a09564d4a2aabcd51c2088361a92e08766ab0db65"},
+{"name":"activemodel","version":"6.1.6.1","platform":"ruby","checksum":"239953365a7da4bcb9a3819b8ac2557a58a3ba89ddd36bee9bb3eca818e4a3e2"},
+{"name":"activerecord","version":"6.1.6.1","platform":"ruby","checksum":"82f74804ab34ea549fd593e5ced68c32426564786127d2de9b933ba78467d0b0"},
+{"name":"activerecord-explain-analyze","version":"0.1.0","platform":"ruby","checksum":"5debb11fe23f35b91953a80677d80ba9284ee737fd9d148c1d7603ce45217f7b"},
+{"name":"activestorage","version":"6.1.6.1","platform":"ruby","checksum":"3fbf4c355a69a46e14676004ad8e06245bdce7f96858e72782715218326aafc5"},
+{"name":"activesupport","version":"6.1.6.1","platform":"ruby","checksum":"5fc9fd6fe6f755e7523bb3aaf4370fb91a8416b39e3202939fd8bded4fec606d"},
+{"name":"acts-as-taggable-on","version":"9.0.0","platform":"ruby","checksum":"5a409be0eae125b7b02c1a7316264b40d4a583584a13d4ea4a6d82acdb351b86"},
+{"name":"addressable","version":"2.8.0","platform":"ruby","checksum":"f76d29d2d1f54b6c6a49aec58f9583b08d97e088c227a3fcba92f6c6531d5908"},
+{"name":"aes_key_wrap","version":"1.1.0","platform":"ruby","checksum":"b935f4756b37375895db45669e79dfcdc0f7901e12d4e08974d5540c8e0776a5"},
+{"name":"akismet","version":"3.0.0","platform":"ruby","checksum":"74991b8e3d3257eeea996b47069abb8da2006c84a144255123e8dffd1c86b230"},
+{"name":"android_key_attestation","version":"0.3.0","platform":"ruby","checksum":"467eb01a99d2bb48ef9cf24cc13712669d7056cba5a52d009554ff037560570b"},
+{"name":"apollo_upload_server","version":"2.1.0","platform":"ruby","checksum":"e5f3c9dda0c2ca775d007072742b98d517dfd91a667111fedbcdc94dfabd904e"},
+{"name":"asana","version":"0.10.13","platform":"ruby","checksum":"36d0d37f8dd6118a54580f1b80224875d7b6a9027598938e1722a508bfc2d7ac"},
+{"name":"asciidoctor","version":"2.0.17","platform":"ruby","checksum":"ed5b5e399e8d64994cc16f0983f993d6e33990909a8415b6fc8b786cdeb00f3d"},
+{"name":"asciidoctor-include-ext","version":"0.4.0","platform":"ruby","checksum":"406adb9d2fbfc25536609ca13b787ed704dc06a4e49d6709b83f3bad578f7878"},
+{"name":"asciidoctor-kroki","version":"0.5.0","platform":"ruby","checksum":"622c8b74796689bdaf8abdf89ad5295b11ce310e3d193e28f19e5baf58d45f12"},
+{"name":"asciidoctor-plantuml","version":"0.0.16","platform":"ruby","checksum":"407e47cd1186ded5ccc75f0c812e5524c26c571d542247c5132abb8f47bd1793"},
+{"name":"ast","version":"2.4.2","platform":"ruby","checksum":"1e280232e6a33754cde542bc5ef85520b74db2aac73ec14acef453784447cc12"},
+{"name":"atlassian-jwt","version":"0.2.0","platform":"ruby","checksum":"52e653e9d6062d7a740c3675b0e79fa08367927c6fc17f5476d1b6b3798c6eb2"},
+{"name":"attr_encrypted","version":"3.1.0","platform":"ruby","checksum":"4f0682604714ed4599cf00771ad27e82f0b51b0ed8644af51a43d21fbe129b59"},
+{"name":"attr_required","version":"1.0.1","platform":"ruby","checksum":"024e10393bd30901e1adf6769bd756b873a5ef7da60f86f8f11066116b5742bc"},
+{"name":"autoprefixer-rails","version":"10.2.5.1","platform":"ruby","checksum":"3711d67f1112361c7628847ac192d8aa6f3b8abe47527aee8a69dc8985e798ee"},
+{"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.600.0","platform":"ruby","checksum":"23592386dd0bb34c38fae2714eb1ab5c18fbef714f22b042815a92fdd51fa733"},
+{"name":"aws-sdk-cloudformation","version":"1.41.0","platform":"ruby","checksum":"31e47539719734413671edf9b1a31f8673fbf9688549f50c41affabbcb1c6b26"},
+{"name":"aws-sdk-core","version":"3.131.1","platform":"ruby","checksum":"481c602d682b61abccb4e9f5b64750907bb49758f6f31b3bec599819951a3f7a"},
+{"name":"aws-sdk-kms","version":"1.57.0","platform":"ruby","checksum":"ffd7dbb9b4251f29d4f508af761d0addd7035a346a88e3481cdb4dc548e51bd5"},
+{"name":"aws-sdk-s3","version":"1.114.0","platform":"ruby","checksum":"ce0f71df1a7b0fb1f88d40a70636ef1a9b08e69fb560694c5dab3f4ac7efcde4"},
+{"name":"aws-sigv4","version":"1.5.0","platform":"ruby","checksum":"3f81c08bacabec6cbc77ebbbac755ca6132a74a4a3279afbde64db83796ce776"},
+{"name":"azure-storage-blob","version":"2.0.3","platform":"ruby","checksum":"61b76118843c91776bd24bee22c74adafeb7c4bb3a858a325047dae3b59d0363"},
+{"name":"azure-storage-common","version":"2.0.4","platform":"ruby","checksum":"608f4daab0e06b583b73dcffd3246ea39e78056de31630286b0cf97af7d6956b"},
+{"name":"babosa","version":"1.0.4","platform":"ruby","checksum":"18dea450f595462ed7cb80595abd76b2e535db8c91b350f6c4b3d73986c5bc99"},
+{"name":"backport","version":"1.2.0","platform":"ruby","checksum":"912c7dfdd9ee4625d013ddfccb6205c3f92da69a8990f65c440e40f5b2fc7f75"},
+{"name":"base32","version":"0.3.2","platform":"ruby","checksum":"532e9b19c5dd1fce281df67fc93a803ebd5d26426a93f6dda6612769bc46fe2c"},
+{"name":"batch-loader","version":"2.0.1","platform":"ruby","checksum":"93f711df78d316ee0440a7a45daba4f5418d0ee2b5f58f60c9ea038424e7a89d"},
+{"name":"bcrypt","version":"3.1.16","platform":"java","checksum":"2925a1546fa8e85bdb1b10f1fc95c4e1ea15992ada16adea4af82b0978ed662c"},
+{"name":"bcrypt","version":"3.1.16","platform":"ruby","checksum":"0b8bf031ba81aa76c0f10c5a8dac779b6035d84b09af1dbb2b1a32a7e360210b"},
+{"name":"benchmark","version":"0.2.0","platform":"ruby","checksum":"5f7087b794613abdd3ac9c13f4351f65b164bcb15ced2ad29508e365f9b28c77"},
+{"name":"benchmark-ips","version":"2.3.0","platform":"ruby","checksum":"12443aa327d3129aa965244f79d7d5cb0f692f0f92ba7db76fba61526a40062e"},
+{"name":"benchmark-malloc","version":"0.2.0","platform":"ruby","checksum":"37c68f0435261634026f584d79956a35325a3027e3e6b4cc8d7575aa10537e6b"},
+{"name":"benchmark-memory","version":"0.1.2","platform":"ruby","checksum":"aa7bfe6776174d0ddefe6fb39945d88fff6d76eac165690188391d9acd441c87"},
+{"name":"benchmark-perf","version":"0.6.0","platform":"ruby","checksum":"fe2b01959f3de0f9dd34820d54ef881eb4f3589fccb7d17b63068ac92d7f9621"},
+{"name":"benchmark-trend","version":"0.4.0","platform":"ruby","checksum":"de5a02a9f443babefbbd97784759820decee8554a0c273d859c02a0990845d81"},
+{"name":"better_errors","version":"2.9.1","platform":"ruby","checksum":"39efc116ab04d6c4200052c5782936e4bd99906978d098992bce6bf81d054284"},
+{"name":"bindata","version":"2.4.10","platform":"ruby","checksum":"798b5e3ec00e9d562243076b819c16b1e226eb176d5b7b5cd21417bc3589981a"},
+{"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":"bootstrap_form","version":"4.2.0","platform":"ruby","checksum":"f578b3c900d2cf15fab641064d357318b29e285bd5fdf090f903727912889710"},
+{"name":"browser","version":"4.2.0","platform":"ruby","checksum":"fc194b422ea8b313f98443c6ec249ccf252e29007ce01bb99ebe828bd7fe3e60"},
+{"name":"builder","version":"3.2.4","platform":"ruby","checksum":"99caf08af60c8d7f3a6b004029c4c3c0bdaebced6c949165fe98f1db27fbbc10"},
+{"name":"bullet","version":"7.0.2","platform":"ruby","checksum":"4b7986b366f694bb05d5c1b4ea8ba949a99224d4511bf02f0c3944112f719c81"},
+{"name":"bundler-audit","version":"0.7.0.1","platform":"ruby","checksum":"12d853cb0b92fa8868abbb539414d7a33da9e48b792e2ff28271d36c8ace8912"},
+{"name":"byebug","version":"11.1.3","platform":"ruby","checksum":"2485944d2bb21283c593d562f9ae1019bf80002143cc3a255aaffd4e9cf4a35b"},
+{"name":"capybara","version":"3.35.3","platform":"ruby","checksum":"3389f8203b05175352b763f4d04c31b29ba606a96224649ac42ef967f56538ee"},
+{"name":"capybara-screenshot","version":"1.0.22","platform":"ruby","checksum":"f86040349a0df7f723123460d9456023f7d693068338991529f10f670fa420f5"},
+{"name":"carrierwave","version":"1.3.2","platform":"ruby","checksum":"3c42f3f3c2c83b29efe2b441d246829c42bc626ac3f6af1486c9a62d971fb114"},
+{"name":"cbor","version":"0.5.9.6","platform":"ruby","checksum":"434a147658dd1df24ec9e7b3297c1fd4f8a691c97d0e688b3049df8e728b2114"},
+{"name":"character_set","version":"1.4.1","platform":"java","checksum":"38b632136b40e02fecba2898497b07ac640cc121f17ac536eaf19873d50053d0"},
+{"name":"character_set","version":"1.4.1","platform":"ruby","checksum":"f71b1ac35b21c4c6f9f26b8a67c7eec8e10bdf0da17488ac7f8fae756d9f8062"},
+{"name":"charlock_holmes","version":"0.7.7","platform":"ruby","checksum":"1790eca3f661ffa6bbf5866c53c7191e4b8472626fc4997ff9dbe7c425e2cb43"},
+{"name":"chef-config","version":"16.10.17","platform":"ruby","checksum":"1f4961e4d6aa4df374f739c6f62ae1d2be03dcff1bd93e56d9c963b8a156747c"},
+{"name":"chef-utils","version":"16.10.17","platform":"ruby","checksum":"a74253da6aab8ff92c955549536bdecbc4d1ce8032c8201576f2a8ef4e8ed7b3"},
+{"name":"childprocess","version":"3.0.0","platform":"ruby","checksum":"4579a87cdc962de252eebf1482a4185fad383ae7dbe29a746ba2be8e261280c5"},
+{"name":"chunky_png","version":"1.3.5","platform":"ruby","checksum":"b6ab1011b2e79bcc973c92deee4110d071d5cd59ed950efcd0aba49a5f57c06d"},
+{"name":"citrus","version":"3.0.2","platform":"ruby","checksum":"4ec2412fc389ad186735f4baee1460f7900a8e130ffe3f216b30d4f9c684f650"},
+{"name":"claide","version":"1.1.0","platform":"ruby","checksum":"6d3c5c089dde904d96aa30e73306d0d4bd444b1accb9b3125ce14a3c0183f82e"},
+{"name":"claide-plugins","version":"0.9.2","platform":"ruby","checksum":"c7ea78bc067ab23bce8515497cdcdcb8f01c86dadfbe13c44644e382922c1c2e"},
+{"name":"coderay","version":"1.1.3","platform":"ruby","checksum":"dc530018a4684512f8f38143cd2a096c9f02a1fc2459edcfe534787a7fc77d4b"},
+{"name":"colored2","version":"3.1.2","platform":"ruby","checksum":"b13c2bd7eeae2cf7356a62501d398e72fde78780bd26aec6a979578293c28b4a"},
+{"name":"commonmarker","version":"0.23.4","platform":"ruby","checksum":"95d9cb050576376374a66d71a4feab3562e0955aab9d80a3e8606a5cf5e9c7ce"},
+{"name":"concurrent-ruby","version":"1.1.10","platform":"ruby","checksum":"244cb1ca0d91ec2c15ca2209507c39fb163336994428e16fbd3f465c87bd8e68"},
+{"name":"connection_pool","version":"2.2.5","platform":"ruby","checksum":"13a8fc3921ce4df8e04fb65f1037251decb08d74757b41163688bd1c1feccd39"},
+{"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":"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"},
+{"name":"crystalball","version":"0.7.0","platform":"ruby","checksum":"6e729f372a5071daec877adb40c5df4cb25fe21f350635e2a9624373fc151ef2"},
+{"name":"css_parser","version":"1.11.0","platform":"ruby","checksum":"568926c3193579446ad3e3f9d761c73e2918ee5b3b7757a1a49ec166c67d6de1"},
+{"name":"danger","version":"8.6.1","platform":"ruby","checksum":"d95eb58b41f68d3aaa9bbef697916b6b4d161a38819517c98562531be75cdfd8"},
+{"name":"danger-gitlab","version":"8.0.0","platform":"ruby","checksum":"497dd7d0f6513913de651019223d8058cf494df10acbd17de92b175dfa04a3a8"},
+{"name":"database_cleaner","version":"1.7.0","platform":"ruby","checksum":"bdf833c197afac7054015bcde2567c3834c366bbfe6a377c30151ca984b32016"},
+{"name":"dead_end","version":"3.1.1","platform":"ruby","checksum":"1011df7f7c0149be004e11cbbc37747760227c55305cd902fd3c06e1394b2f5b"},
+{"name":"deckar01-task_list","version":"2.3.1","platform":"ruby","checksum":"205a08c22a8f05c063d43c452aeada4214b58fdce1e55e92ec709e2cfb9e7ca1"},
+{"name":"declarative","version":"0.0.20","platform":"ruby","checksum":"8021dd6cb17ab2b61233c56903d3f5a259c5cf43c80ff332d447d395b17d9ff9"},
+{"name":"declarative-option","version":"0.1.0","platform":"ruby","checksum":"17508349f51c5631e5ad4158c29f78a4b2de618abffa066d76c11953705f91bc"},
+{"name":"declarative_policy","version":"1.1.0","platform":"ruby","checksum":"9af4cf299ade03f2bbf63908f2ce6a117d132fc714c39a128596667fb13331cb"},
+{"name":"default_value_for","version":"3.4.0","platform":"ruby","checksum":"35d2dc51675a6bedfa875778628d44b823e0d7336da9432519477174ebb0f40f"},
+{"name":"deprecation_toolkit","version":"1.5.1","platform":"ruby","checksum":"a8a1ab1a19ae40ea12560b65010e099f3459ebde390b76621ef0c21c516a04ba"},
+{"name":"derailed_benchmarks","version":"2.1.1","platform":"ruby","checksum":"25b0ba79dc52c715ea6cce35fd8afbbf84511ef1c7f2bbe1d8b7a30addc6aab3"},
+{"name":"device_detector","version":"1.0.0","platform":"ruby","checksum":"b800fb3150b00c23e87b6768011808ac1771fffaae74c3238ebaf2b782947a7d"},
+{"name":"devise","version":"4.8.1","platform":"ruby","checksum":"fdd48bbe79a89e7c1152236a70479842ede48bea4fa7f4f2d8da1f872559803e"},
+{"name":"devise-two-factor","version":"4.0.2","platform":"ruby","checksum":"6548d2696ed090d27046f888f4fa7380f151e0f823902d46fd9b91e7d0cac511"},
+{"name":"diff-lcs","version":"1.5.0","platform":"ruby","checksum":"49b934001c8c6aedb37ba19daec5c634da27b318a7a3c654ae979d6ba1929b67"},
+{"name":"diff_match_patch","version":"0.1.0","platform":"ruby","checksum":"b36057bfcfeaedf19dcb7b2c28c19ee625bd6ec6d0d182717d3ef22b3879c40e"},
+{"name":"diffy","version":"3.4.2","platform":"ruby","checksum":"36b42ffbe5138ddc56182107c24ad8d6b066ecfd2876829f391e3a4993d89ae1"},
+{"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":"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"},
+{"name":"dry-core","version":"0.5.0","platform":"ruby","checksum":"3af9777320a7c930766e2fe638df970c62145100f673caf7aae25ecc3a2c4493"},
+{"name":"dry-equalizer","version":"0.3.0","platform":"ruby","checksum":"866c9245b98280696ee7799f14e0c89d19d0afe890832aaf914649f9c0bc5867"},
+{"name":"dry-inflector","version":"0.2.0","platform":"ruby","checksum":"c7cf29c3dc9d961c115aac873ac39a4ff6988fae7f7871c473a9694c1f6fb39e"},
+{"name":"dry-logic","version":"1.1.0","platform":"ruby","checksum":"eca4b39084c9d22778144b7e4cf8db20e8bab7de6d89deb220d20a9fde60b69d"},
+{"name":"dry-types","version":"1.4.0","platform":"ruby","checksum":"68003bb0db3077fecd0270f4ae486a82ee76bab6d666fdc4e094380a67c9a1df"},
+{"name":"e2mmap","version":"0.1.0","platform":"ruby","checksum":"45ee6bba2d97a7d91ee0885774261feee87e28c598355df31e93b56196ec0f59"},
+{"name":"ecma-re-validator","version":"0.3.0","platform":"ruby","checksum":"66a95bd8c2b0641baf1fbf9bd355a0dcf13c82c6883f6f496a722420a8b6e0d7"},
+{"name":"ed25519","version":"1.3.0","platform":"java","checksum":"8e5d2f8a5325c7a463d61d1a48406ce54074c610f3dccd889e6532c9527a3894"},
+{"name":"ed25519","version":"1.3.0","platform":"ruby","checksum":"514a5584f84d39daac568a17ec93a4e7261e140c52c562ed8c382c18456e627d"},
+{"name":"elasticsearch","version":"7.13.3","platform":"ruby","checksum":"58b1ad787fafd41836388176dc09e914b2f6e0b257e73b8a51a704ba6bf75b41"},
+{"name":"elasticsearch-api","version":"7.13.3","platform":"ruby","checksum":"888f35c64c896db7909f1a56f6c383c45ad6b73c3231649b9c989e39b3d2ba2e"},
+{"name":"elasticsearch-model","version":"7.2.0","platform":"ruby","checksum":"2cc1810a45619223c43eff78c6112988f12d475d201523243007dccc6ef96cc7"},
+{"name":"elasticsearch-rails","version":"7.2.1","platform":"ruby","checksum":"0750dc0e956358d9a3a0912a8186c266ef19f8de0b178c61996ed1a6998156e4"},
+{"name":"elasticsearch-transport","version":"7.13.3","platform":"ruby","checksum":"ab8d0226652fb5b32923f172c1abfbc7464058b7de2d9dde3215c88d518c8e2e"},
+{"name":"email_reply_trimmer","version":"0.1.6","platform":"ruby","checksum":"9fede222ce660993e4e2e3dad282535ceb7914e246eb8302c19aa9e021f7326e"},
+{"name":"email_spec","version":"2.2.0","platform":"ruby","checksum":"60b7980580a835e7f676db60667f17a2d60e8e0e39c26d81cfc231805c544d79"},
+{"name":"encryptor","version":"3.0.0","platform":"ruby","checksum":"abf23f94ab4d864b8cea85b43f3432044a60001982cda7c33c1cd90da8db1969"},
+{"name":"erubi","version":"1.9.0","platform":"ruby","checksum":"7d84d6037396418c4ba30bc40ed7a0aec9beb567ce55bcecb12e8c0cb1ed9fdb"},
+{"name":"escape_utils","version":"1.2.1","platform":"ruby","checksum":"e5292fe8d7e12a9bcb4502d99e28fb602e4e1514690d98a1c4957f6f77b4b162"},
+{"name":"et-orbi","version":"1.2.7","platform":"ruby","checksum":"3b693d47f94a4060ccc07e60adda488759b1e8b9228a633ebbad842dfc245fb4"},
+{"name":"ethon","version":"0.15.0","platform":"ruby","checksum":"0809805a035bc10f54162ca99f15ded49e428e0488bcfe1c08c821e18261a74d"},
+{"name":"excon","version":"0.90.0","platform":"ruby","checksum":"01beac0f20652b12de95aef931f72bcb82ffb009e1c34c42a5cf5df93f4070ae"},
+{"name":"execjs","version":"2.8.1","platform":"ruby","checksum":"6d939919cfd81bcc4d6556f322c3995a70cfe4289ea0bd3b1f999b489c323088"},
+{"name":"expression_parser","version":"0.9.0","platform":"ruby","checksum":"2b56db3cffc48c3337f4f29f5bc2374c86e7ba29acb40269c74bb55af9f868a4"},
+{"name":"extended-markdown-filter","version":"0.6.0","platform":"ruby","checksum":"46844b5740b1703a0e0674e31a17c83d1244a3198abb3aae51cad1eb152eb19e"},
+{"name":"factory_bot","version":"6.2.0","platform":"ruby","checksum":"d181902cdda531cf6cef036001b3a700a7b5e04bac63976864530120b2ac7d13"},
+{"name":"factory_bot_rails","version":"6.2.0","platform":"ruby","checksum":"278b969666b078e76e1c972c501da9b1fac15e5b0ff328cc7ce400366164d0a1"},
+{"name":"faraday","version":"1.10.0","platform":"ruby","checksum":"a42158d5c1932c16fd483c512f7e0797b4916096bcf0eb5fb927a1c915a7ea02"},
+{"name":"faraday-cookie_jar","version":"0.0.7","platform":"ruby","checksum":"f3cbbe6f2de3d4028f00a67ae4196b99348a6dc3c065fdae6d3c7123fa8b1402"},
+{"name":"faraday-em_http","version":"1.0.0","platform":"ruby","checksum":"7a3d4c7079789121054f57e08cd4ef7e40ad1549b63101f38c7093a9d6c59689"},
+{"name":"faraday-em_synchrony","version":"1.0.0","platform":"ruby","checksum":"460dad1c30cc692d6e77d4c391ccadb4eca4854b315632cd7e560f74275cf9ed"},
+{"name":"faraday-excon","version":"1.1.0","platform":"ruby","checksum":"b055c842376734d7f74350fe8611542ae2000c5387348d9ba9708109d6e40940"},
+{"name":"faraday-http-cache","version":"2.4.0","platform":"ruby","checksum":"388f901d63bd5903b470c5696bc886ed94fab0c4206b25c3761e7b9bdbbf6c90"},
+{"name":"faraday-httpclient","version":"1.0.1","platform":"ruby","checksum":"4c8ff1f0973ff835be8d043ef16aaf54f47f25b7578f6d916deee8399a04d33b"},
+{"name":"faraday-multipart","version":"1.0.4","platform":"ruby","checksum":"9012021ab57790f7d712f590b48d5f948b19b43cfa11ca83e6459f06090b0725"},
+{"name":"faraday-net_http","version":"1.0.1","platform":"ruby","checksum":"3245ce406ebb77b40e17a77bfa66191dda04be2fd4e13a78d8a4305854d328ba"},
+{"name":"faraday-net_http_persistent","version":"1.2.0","platform":"ruby","checksum":"0b0cbc8f03dab943c3e1cc58d8b7beb142d9df068b39c718cd83e39260348335"},
+{"name":"faraday-patron","version":"1.0.0","platform":"ruby","checksum":"dc2cd7b340bb3cc8e36bcb9e6e7eff43d134b6d526d5f3429c7a7680ddd38fa7"},
+{"name":"faraday-rack","version":"1.0.0","platform":"ruby","checksum":"ef60ec969a2bb95b8dbf24400155aee64a00fc8ba6c6a4d3968562bcc92328c0"},
+{"name":"faraday-retry","version":"1.0.3","platform":"ruby","checksum":"add154f4f399243cbe070806ed41b96906942e7f5259bb1fe6daf2ec8f497194"},
+{"name":"faraday_middleware","version":"1.2.0","platform":"ruby","checksum":"ded15d574d50e92bd04448d5566913af5cb1a01b2fa311ceecc2464fa0ab88af"},
+{"name":"faraday_middleware-aws-sigv4","version":"0.3.0","platform":"ruby","checksum":"744654bd5b15539a54aed39b806e2dfb45aa47708fa1e6f6766fedcda6c262be"},
+{"name":"faraday_middleware-multi_json","version":"0.0.6","platform":"ruby","checksum":"38fc4dab7a78916ad09827d5a164aab62fbf2cb8b9de0507763de1f561d7a118"},
+{"name":"fast_blank","version":"1.0.0","platform":"ruby","checksum":"a67c93dbcb8c34ba40973688e4b600b640760503362f3aeb63b37ebe3d8d419b"},
+{"name":"fast_gettext","version":"2.1.0","platform":"ruby","checksum":"0d095dbc5a003b3caea13b9569288703086ae05aac2cc6d7b0881c8e0a07ae15"},
+{"name":"ffaker","version":"2.10.0","platform":"ruby","checksum":"4323b85b4bbdb0d932695b2ebffeb3645304215092e64e2a451630a9f5a5e507"},
+{"name":"ffi","version":"1.15.5","platform":"java","checksum":"610b9a993e67b812123cfedc1c45a639aa2f2455747af5443d54f98e092b1abe"},
+{"name":"ffi","version":"1.15.5","platform":"ruby","checksum":"6f2ed2fa68047962d6072b964420cba91d82ce6fa8ee251950c17fca6af3c2a0"},
+{"name":"ffi","version":"1.15.5","platform":"x64-mingw-ucrt","checksum":"3dea5573d11cd4c8a965e861052b0036098b239140caa0a98940c5c84eb5990d"},
+{"name":"ffi","version":"1.15.5","platform":"x64-mingw32","checksum":"9acc4e474bef25dbcf827b1e6fce4067e9a1eff74a0296d25f0af392fafcd096"},
+{"name":"ffi","version":"1.15.5","platform":"x86-mingw32","checksum":"6e52c39fdbbccc085ae05d489d1b835ce05dcdf2a4c88ba739e3b42afd2b5404"},
+{"name":"ffi-compiler","version":"1.0.1","platform":"ruby","checksum":"019f389b078a2fec9de7f4f65771095f80a447e34436b4588bcb629e2a564c30"},
+{"name":"ffi-yajl","version":"2.3.4","platform":"ruby","checksum":"a54b70a7539745701f79bb38602a3193527c89e939a9ae56096cb07c1fb0d5dd"},
+{"name":"ffi-yajl","version":"2.3.4","platform":"universal-java","checksum":"ac1acda7a7ebe900235222ddb4f47247f6480f06f480915d4e450f3b1ebe8713"},
+{"name":"filelock","version":"1.1.1","platform":"ruby","checksum":"5207258374370a75d5927b2eaf9b831732d224e48ce56c318e8308c7cdcabb20"},
+{"name":"find_a_port","version":"1.0.1","platform":"ruby","checksum":"605d6a84b5e6f138da2b06c87c5a4a0231e4fdc9b9a92022d9caa361f77d5ceb"},
+{"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.14.0","platform":"ruby","checksum":"07442dff8ee2a314413f812d6f6052e7d4a444540df84c193c135c1994114bbf"},
+{"name":"fog-core","version":"2.1.0","platform":"ruby","checksum":"53e5d793554d7080d015ef13cd44b54027e421d924d9dba4ce3d83f95f37eda9"},
+{"name":"fog-google","version":"1.15.0","platform":"ruby","checksum":"2f840780fbf2384718e961b05ef2fc522b4213bbda6f25b28c1bbd875ff0b306"},
+{"name":"fog-json","version":"1.2.0","platform":"ruby","checksum":"dd4f5ab362dbc72b687240bba9d2dd841d5dfe888a285797533f85c03ea548fe"},
+{"name":"fog-local","version":"0.6.0","platform":"ruby","checksum":"417473fe22a839af8f1388218d1843dbd09a5edfc8fcc59a893edb322ca5442d"},
+{"name":"fog-openstack","version":"1.0.8","platform":"ruby","checksum":"8f174ab5e5b1bc107c7da90cc7c47a24930e1566cd88ab4df447026ea8b63d9c"},
+{"name":"fog-rackspace","version":"0.1.1","platform":"ruby","checksum":"4a8c7a2432dd32321958c869f3b1b8190cf4eac292024e6ea267bc6040a44b78"},
+{"name":"fog-xml","version":"0.1.3","platform":"ruby","checksum":"5604c42649ebb0d8a31bd973aa000c2dd0127f1c1c4c174b69266a2e78e37410"},
+{"name":"formatador","version":"0.2.5","platform":"ruby","checksum":"80821869ddacb79e72870ff4bb1531efacd278c04f2df26bc6b4529ee13582bd"},
+{"name":"fugit","version":"1.2.3","platform":"ruby","checksum":"a0ba58211f4e69103531cc26d6d5c18f03a2d3989e7b8aa03d2d83f2181cb745"},
+{"name":"fuubar","version":"2.2.0","platform":"ruby","checksum":"9b0263c4074f39c68b37f1e4e69a7d3cfc7523c41bea43601235daa723179b4a"},
+{"name":"fuzzyurl","version":"0.9.0","platform":"ruby","checksum":"542efa80f2bcaadbdc402c2f0b572f2e335a1d53e375aecad68bbb3d86860c0f"},
+{"name":"gemoji","version":"3.0.1","platform":"ruby","checksum":"80553f2f4932a7a95fb1b3c7c63f7dd937e7c8c610164bbdea28fd06eba5f36d"},
+{"name":"get_process_mem","version":"0.2.7","platform":"ruby","checksum":"4afd3c3641dd6a817c09806c7d6d509d8a9984512ac38dea8b917426bbf77eba"},
+{"name":"gettext","version":"3.3.6","platform":"ruby","checksum":"ee6bbd1b2f833ee52d7797fa68acbfecc4726aec6b6280fd7eab92aa0190b413"},
+{"name":"gettext_i18n_rails","version":"1.8.0","platform":"ruby","checksum":"95e5cf8440b1e08705b27f2bccb56143272c5a7a0dabcf54ea1bd701140a496f"},
+{"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.4.0.pre.rc2","platform":"ruby","checksum":"48764528a730605a46f00cf86c7cfcb92d25f4f3d8cb9e09557baac3e9f3f8e3"},
+{"name":"github-markup","version":"1.7.0","platform":"ruby","checksum":"97eb27c70662d9cc1d5997cd6c99832026fae5d4913b5dce1ce6c9f65078e69d"},
+{"name":"gitlab","version":"4.16.1","platform":"ruby","checksum":"13fd7059cbdad5a1a21b15fa2cf9070b97d92e27f8c688581fe3d84dc038074f"},
+{"name":"gitlab-chronic","version":"0.10.5","platform":"ruby","checksum":"f80f18dc699b708870a80685243331290bc10cfeedb6b99c92219722f729c875"},
+{"name":"gitlab-dangerfiles","version":"3.5.2","platform":"ruby","checksum":"fae28a55b83b6c7f8298b9b1d90354ae73636729fd829ad58326bef46bd2f01f"},
+{"name":"gitlab-experiment","version":"0.7.1","platform":"ruby","checksum":"166dddb3aa83428bcaa93c35684ed01dc4d61f321fd2ae40b020806dc54a7824"},
+{"name":"gitlab-fog-azure-rm","version":"1.3.0","platform":"ruby","checksum":"2fef5317d6515f95f803099afa860fe3019ce6e1907bf49f66b5e06468a617b5"},
+{"name":"gitlab-labkit","version":"0.24.0","platform":"ruby","checksum":"8f16e5aa4e0a05be58958fe880bdd53c84b659a081ea9981d2b510922a4a0548"},
+{"name":"gitlab-license","version":"2.2.1","platform":"ruby","checksum":"39fcf6be8b2887df8afe01b5dcbae8d08b7c5d937ff56b0fb40484a8c4f02d30"},
+{"name":"gitlab-mail_room","version":"0.0.9","platform":"ruby","checksum":"6700374b5c0aa9d9ad4e711aeb677f0b7d415a6d01d3baa699efab25349d851c"},
+{"name":"gitlab-markup","version":"1.8.0","platform":"ruby","checksum":"fb629369dca5dd343e47ebf5fa2e0a0fc146012fc49c35eff5ca826ae4186c86"},
+{"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.8.0","platform":"ruby","checksum":"9c564caa2a958d44a8d78672dc23b2a206102d0223b41b77b58626a945e37362"},
+{"name":"gitlab-styles","version":"8.0.0","platform":"ruby","checksum":"ff77d8543b7093e387430ebdaa2adce9dc3caaa8b10ce9bf9b40242d5f7b66da"},
+{"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"},
+{"name":"gon","version":"6.4.0","platform":"ruby","checksum":"e3a618d659392890f1aa7db420f17c75fd7d35aeb5f8fe003697d02c4b88d2f0"},
+{"name":"google-api-client","version":"0.50.0","platform":"ruby","checksum":"3ae45e972f293f3a66e53950ecc0fd350d85d6347c06a430bb971bd1ae5ad617"},
+{"name":"google-cloud-env","version":"1.6.0","platform":"ruby","checksum":"6179acb946975892c7908748df5722a4ebadfc8cf5bb7b0d8d933ca67183fa15"},
+{"name":"google-protobuf","version":"3.21.5","platform":"java","checksum":"f63a511439c6350823790d1c8d903a7b536549bbc3eec21160aff3ee5594dcc3"},
+{"name":"google-protobuf","version":"3.21.5","platform":"ruby","checksum":"c94bee502390041d0f5c6fd4b776118656c5b54521c2a5c7957686de6ba95bfb"},
+{"name":"google-protobuf","version":"3.21.5","platform":"x64-mingw-ucrt","checksum":"cf17273c5d5d33fc4c4879ba8aa5973aa9e34f879d728c461facf5da24db7745"},
+{"name":"google-protobuf","version":"3.21.5","platform":"x64-mingw32","checksum":"677a4c0770446e0b08828da6b8c021469551639cfb2b489f3d3f32da435bd6e8"},
+{"name":"google-protobuf","version":"3.21.5","platform":"x86-linux","checksum":"cdaa147bbb7e5fd59adc2aba167b7965221013a39830494f616974e97db02a42"},
+{"name":"google-protobuf","version":"3.21.5","platform":"x86-mingw32","checksum":"eda2bd51324e855a1593aa0c805d1c99a95dcc5161bc61eb6e8afb5d4e7bcb75"},
+{"name":"google-protobuf","version":"3.21.5","platform":"x86_64-darwin","checksum":"4e2c937e5dc991a5675e4b78d6132e818862f19479013c2edc2455c76b54be53"},
+{"name":"google-protobuf","version":"3.21.5","platform":"x86_64-linux","checksum":"ec2dabea012d31dd99f450174be461f9323b4e97460af13cc3a4f9426c2b0fc0"},
+{"name":"googleapis-common-protos-types","version":"1.3.0","platform":"ruby","checksum":"c5411f3197cc3e02547ded1858303b1f830b4dc89c588c142ad6c8a231050671"},
+{"name":"googleauth","version":"0.14.0","platform":"ruby","checksum":"4659b563d5b2727e775ba9231e75485c1b55ac8fc319e0bf1bc87d5e9705a632"},
+{"name":"gpgme","version":"2.0.20","platform":"ruby","checksum":"fc194689cff40cd4ccafb3086031e930650b3efc15348bbfdf7a2f8b5a826f75"},
+{"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"},
+{"name":"grape_logging","version":"1.8.4","platform":"ruby","checksum":"efcc3e322dbd5d620a68f078733b7db043cf12680144cd03c982f14115c792d1"},
+{"name":"graphiql-rails","version":"1.8.0","platform":"ruby","checksum":"02e2c5098be2c6c29219a0e9b2910a2cd3c494301587a3199a7c4484d8038ed1"},
+{"name":"graphlient","version":"0.5.0","platform":"ruby","checksum":"0f2c9416142e50b6bd4edcd86fe6810f792951732c487f9061aee6d420e0f292"},
+{"name":"graphlyte","version":"1.0.0","platform":"ruby","checksum":"b5af4ab67dde6e961f00ea1c18f159f73b52ed11395bb4ece297fe628fa1804d"},
+{"name":"graphql","version":"1.13.12","platform":"ruby","checksum":"1d82666cf201193a8d0cb54cea38576b820418db4869b549f61a35f3a2d97ac3"},
+{"name":"graphql-client","version":"0.17.0","platform":"ruby","checksum":"5aaf02ce8f2dbc8e3ba05a7eaeb3ad9336762c4424c6093f4438fbb9490eeb5d"},
+{"name":"graphql-docs","version":"2.1.0","platform":"ruby","checksum":"7eb82402f8fda455104b2b60364e9ada145d79d3121a8f915790d49da38bb576"},
+{"name":"grpc","version":"1.42.0","platform":"ruby","checksum":"b3d2649e67c6a636544996843d9ec191699c54c1aca797dbfea4dff36c14584a"},
+{"name":"grpc","version":"1.42.0","platform":"x64-mingw32","checksum":"6aac1b6576134b0a83e000b1269f60d502eb24aee96c64e2658c3f24f8e32ac0"},
+{"name":"grpc","version":"1.42.0","platform":"x86-linux","checksum":"4aa50538aa929f1f3bcefb11c65ee1a1606b5aef838ea4d4e93c100b5f4263a5"},
+{"name":"grpc","version":"1.42.0","platform":"x86-mingw32","checksum":"eeb2a9381bea43fafe879b6ddaa011351a44d0894d48bdc965a07bcb67c6eb56"},
+{"name":"grpc","version":"1.42.0","platform":"x86_64-darwin","checksum":"20fa202d46d8a055628260622e98fb6439529fbac283f0552af620b909f78535"},
+{"name":"grpc","version":"1.42.0","platform":"x86_64-linux","checksum":"92e2ceb2aca335d5755163dd8030082091d5b0e63c117b1ca07051b66c53eb2e"},
+{"name":"gssapi","version":"1.2.0","platform":"ruby","checksum":"3d0e3222f36532c1ab3b7eaab171ad4245889edc5439a5483ff714999cb62349"},
+{"name":"guard","version":"2.16.2","platform":"ruby","checksum":"71ba7abaddecc8be91ab77bbaf78f767246603652ebbc7b976fda497ebdc8fbb"},
+{"name":"guard-compat","version":"1.2.1","platform":"ruby","checksum":"3ad21ab0070107f92edfd82610b5cdc2fb8e368851e72362ada9703443d646fe"},
+{"name":"guard-rspec","version":"4.7.3","platform":"ruby","checksum":"a47ba03cbd1e3c71e6ae8645cea97e203098a248aede507461a43e906e2f75ca"},
+{"name":"haml","version":"5.2.2","platform":"ruby","checksum":"6e759246556145642ef832d670fc06f9bd8539159a0e600847a00291dd7aae0c"},
+{"name":"haml_lint","version":"0.40.1","platform":"ruby","checksum":"b658322eb245399e40b19a27a341039c76aead5794bc622d469e877162e34802"},
+{"name":"hamlit","version":"2.15.0","platform":"java","checksum":"fda165464e59337ab7cda6304a66bfdb607bb7155f25566da19c9ee7b98e03d1"},
+{"name":"hamlit","version":"2.15.0","platform":"ruby","checksum":"d2e8505362338945fa309c68b2b8be07ebdc181200ec6021223567bf66dac38e"},
+{"name":"hana","version":"1.3.7","platform":"ruby","checksum":"5425db42d651fea08859811c29d20446f16af196308162894db208cac5ce9b0d"},
+{"name":"hangouts-chat","version":"0.0.5","platform":"ruby","checksum":"bdbeb6c6e4abc98f395cb273f53b39911b3aa9e248fbbf063242b021ced8b6b6"},
+{"name":"hashdiff","version":"1.0.1","platform":"ruby","checksum":"2cd4d04f5080314ecc8403c4e2e00dbaa282dff395e2d031bc16c8d501bdd6db"},
+{"name":"hashie","version":"4.1.0","platform":"ruby","checksum":"7890dcb9ec18a4b66acec797018c73824b89cef5eb8cda36e8e8501845e87a09"},
+{"name":"hashie-forbidden_attributes","version":"0.1.1","platform":"ruby","checksum":"3a6ed37f3a314e4fb1dd1e2df6eb7721bcadd023a30bc0b951b2b5285a790fb2"},
+{"name":"health_check","version":"3.0.0","platform":"ruby","checksum":"1b336c5c49036a993153e75c8d14e9742377fb9b7361c0a6af2e5fedb45b991f"},
+{"name":"heapy","version":"0.2.0","platform":"ruby","checksum":"74141e845d61ffc7c1e8bf8b127c8cf94544ec7a1181aec613288682543585ea"},
+{"name":"html-pipeline","version":"2.13.2","platform":"ruby","checksum":"a1de83f7bd2d3464f3a068e391b661983fc6099d194c8d9ceb91ace02dadb803"},
+{"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-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.16.4","platform":"ruby","checksum":"62c89d00f5e8933b2d397a49b57deb18ca18e16cb7d862ee4f53b73228dc3d81"},
+{"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":"icalendar","version":"2.4.1","platform":"ruby","checksum":"ade7384b3a78d302e01c0b93f66816b734c9abd85d1511b90200de2eee6d5ef7"},
+{"name":"imagen","version":"0.1.8","platform":"ruby","checksum":"fde7b727d4fe79c6bb5ac46c1f7184bf87a6d54df54d712ad2be039d2f93a162"},
+{"name":"invisible_captcha","version":"1.1.0","platform":"ruby","checksum":"3670294a998ab1430ff07cd1697d25c70e6367bdb0dff534df24a14fdee8b4d2"},
+{"name":"ipaddress","version":"0.8.3","platform":"ruby","checksum":"85640c4f9194c26937afc8c78e3074a8e7c97d5d1210358d1440f01034d006f5"},
+{"name":"jaeger-client","version":"1.1.0","platform":"ruby","checksum":"cb5e9b9bbee6ee8d6a82d03d947a5b04543d8c0a949c22e484254f18d8a458a8"},
+{"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":"js_regex","version":"3.7.0","platform":"ruby","checksum":"b13fac68c4416d1a5f21c3bab8a71b4530f424b7c4ff9f46d8e62b895dc05975"},
+{"name":"json","version":"2.5.1","platform":"java","checksum":"be284a0c4a9d0373e81b0d5dfe71ed5b18d0479f05970e60a77be89a2978ce6c"},
+{"name":"json","version":"2.5.1","platform":"ruby","checksum":"918d8c41dacb7cfdbe0c7bbd6014a5372f0cf1c454ca150e9f4010fe80cc3153"},
+{"name":"json-jwt","version":"1.13.0","platform":"ruby","checksum":"b9bded80ba687e59d199db365731494ee68214f27d0d7be5b635b9956b98eb5b"},
+{"name":"json_schemer","version":"0.2.18","platform":"ruby","checksum":"3362c21efbefdd12ce994e541a1e7fdb86fd267a6541dd8715e8a580fe3b6be6"},
+{"name":"jsonpath","version":"1.1.2","platform":"ruby","checksum":"6804124c244d04418218acb85b15c7caa79c592d7d6970195300428458946d3a"},
+{"name":"jwt","version":"2.1.0","platform":"ruby","checksum":"7e7e7ffc1a5ebce628ac7da428341c50615a3a10ac47bb74c22c1cba325613f0"},
+{"name":"kaminari","version":"1.2.1","platform":"ruby","checksum":"03b26388ebb732b708e40b4f1d858c4cbc58d7cb18d12eeb9364176f23c3b3ef"},
+{"name":"kaminari-actionview","version":"1.2.1","platform":"ruby","checksum":"31a3dc6955e7dff95f6133e1f4c03b9dec36d714b632330034ee51b33d0c1770"},
+{"name":"kaminari-activerecord","version":"1.2.1","platform":"ruby","checksum":"8327823ddf8a8e059ce10d9da0c784ef44d5323e9f6d53054e03d26876efc50a"},
+{"name":"kaminari-core","version":"1.2.1","platform":"ruby","checksum":"9ff2f0fa5a454731943a847bbd9960f0b9f859dd6910df5f40d6c9e049660440"},
+{"name":"kas-grpc","version":"0.0.2","platform":"ruby","checksum":"111ff7515952e939f491297ba4c69a218b72d9d0ef8e5bff80a5df6a56df9a16"},
+{"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.1.1","platform":"ruby","checksum":"fa7dcd2c55dc14f6f164f96cf1404e712be84a2ac256e75947213093e080d05b"},
+{"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":"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.18.0","platform":"ruby","checksum":"61975a247a6aeb8f09ac5a3430305451efc4525c0b9b79c05feaec35a8b9d5a3"},
+{"name":"lookbook","version":"1.0.3","platform":"ruby","checksum":"c53e130a37588e32f66be3b9418b1efcb51bef69946b276edd3b4348a71cbcd6"},
+{"name":"lru_redux","version":"1.1.0","platform":"ruby","checksum":"ee71d0ccab164c51de146c27b480a68b3631d5b4297b8ffe8eda1c72de87affb"},
+{"name":"lumberjack","version":"1.2.7","platform":"ruby","checksum":"a5c6aae6b4234f1420dbcd80b23e3bca0817bd239440dde097ebe3fa63c63b1f"},
+{"name":"mail","version":"2.7.1","platform":"ruby","checksum":"ec2a3d489f7510b90d8eaa3f6abaad7038cf1d663cdf8ee66d0214a0bdf99c03"},
+{"name":"marcel","version":"1.0.2","platform":"ruby","checksum":"a013b677ef46cbcb49fd5c59b3d35803d2ee04dd75d8bfdc43533fc5a31f7e4e"},
+{"name":"marginalia","version":"1.10.0","platform":"ruby","checksum":"68289091ee493e1a8f22369c245f250652a6538e497fdeef68cb2a2490830380"},
+{"name":"memoist","version":"0.16.2","platform":"ruby","checksum":"a52c53a3f25b5875151670b2f3fd44388633486dc0f09f9a7150ead1e3bf3c45"},
+{"name":"memory_profiler","version":"0.9.14","platform":"ruby","checksum":"de558cf6525d8d56d2c0ea465b1664517fbe45560f892dc7a898d3b8c2863b12"},
+{"name":"method_source","version":"1.0.0","platform":"ruby","checksum":"d779455a2b5666a079ce58577bfad8534f571af7cec8107f4dce328f0981dede"},
+{"name":"mime-types","version":"3.4.1","platform":"ruby","checksum":"6bcf8b0e656b6ae9977bdc1351ef211d0383252d2f759a59ef4bcf254542fc46"},
+{"name":"mime-types-data","version":"3.2022.0105","platform":"ruby","checksum":"d8c401ba9ea8b648b7145b90081789ec714e91fd625d82c5040079c5ea696f00"},
+{"name":"mini_histogram","version":"0.3.1","platform":"ruby","checksum":"6a114b504e4618b0e076cc672996036870f7cc6f16b8e5c25c0c637726d2dd94"},
+{"name":"mini_magick","version":"4.10.1","platform":"ruby","checksum":"e939d2c70c8002233fc6b1eecfe762f38a156d69ad31a87160205870be08f852"},
+{"name":"mini_mime","version":"1.1.2","platform":"ruby","checksum":"a54aec0cc7438a03a850adb00daca2bdb60747f839e28186994df057cea87151"},
+{"name":"mini_portile2","version":"2.8.0","platform":"ruby","checksum":"1e06b286ff19b73cfc9193cb3dd2bd80416f8262443564b25b23baea74a05765"},
+{"name":"minitest","version":"5.11.3","platform":"ruby","checksum":"78e18aa2c49c58e9bc53c54a0b900e87ad0a96394e92fbbfa58d3ff860a68f45"},
+{"name":"mixlib-cli","version":"2.1.8","platform":"ruby","checksum":"e6f27be34d580f6ed71731ca46b967e57793a627131c1f6e1ed2dad39ea3bdf9"},
+{"name":"mixlib-config","version":"3.0.9","platform":"ruby","checksum":"9867adab3ab547eb74a8efdc9dfab6bcc83d2802a571ff8af8d6e981ca8d53ab"},
+{"name":"mixlib-log","version":"3.0.9","platform":"ruby","checksum":"fd6ca2c8075f8085065dffcee0805c5b3f88d643d5c954acdc3282f463a9ad58"},
+{"name":"mixlib-shellout","version":"3.2.5","platform":"ruby","checksum":"121a54005e52b6596a945f7bfc95bbcbd7d8ee7685cb3736dd3cef5ff46029bd"},
+{"name":"mixlib-shellout","version":"3.2.5","platform":"universal-mingw32","checksum":"c40ef5f34a68eec5e0cad13482497f6c3898a30cff1747517f2169d4fa4055e0"},
+{"name":"ms_rest","version":"0.7.6","platform":"ruby","checksum":"272a1d592594c5c25edd2f3378c17ac9d840a29991572fb1bc7616d5565030ca"},
+{"name":"ms_rest_azure","version":"0.12.0","platform":"ruby","checksum":"3006060104629cf56f611bee9040023e461c748a7a97eaa601028b4cd03a231a"},
+{"name":"msgpack","version":"1.5.4","platform":"java","checksum":"05b3bd16a65dddc64c878634b7ecb9cd613569ca3dd6e480d7295626a0a3f562"},
+{"name":"msgpack","version":"1.5.4","platform":"ruby","checksum":"a53db320fba40f58c07c5b66ed9fd4d73cbe8eba4cb28fe9e3218444341a4e09"},
+{"name":"multi_json","version":"1.14.1","platform":"ruby","checksum":"d971296c0eacea289d31e4a7ab7ac5eda97262c62bbc8c110de4f5e36425c577"},
+{"name":"multi_xml","version":"0.6.0","platform":"ruby","checksum":"d24393cf958adb226db884b976b007914a89c53ad88718e25679d7008823ad52"},
+{"name":"multipart-post","version":"2.2.3","platform":"ruby","checksum":"462979de2971b8df33c2ee797fd497731617241f9dcd93960cc3caccb2dd13d8"},
+{"name":"murmurhash3","version":"0.1.6","platform":"java","checksum":"7e8677549a65b454362d8688885a21cca721a22c086d14e874c26c1e213946f0"},
+{"name":"murmurhash3","version":"0.1.6","platform":"ruby","checksum":"c78f06d1636665c92e2b3cc309e31036ca4cb8f47f1cdc080d4f964f4865fceb"},
+{"name":"mustermann","version":"1.1.1","platform":"ruby","checksum":"0a21cfe505869cce9ce17998db5260344e78df81ae857c07a62143fd30299531"},
+{"name":"mustermann-grape","version":"1.0.1","platform":"ruby","checksum":"00ce12b3df66be33ec4304aa9108fb9e1a0689f2a136c96b51c104684f5c5436"},
+{"name":"nap","version":"1.1.0","platform":"ruby","checksum":"949691660f9d041d75be611bb2a8d2fd559c467537deac241f4097d9b5eea576"},
+{"name":"nenv","version":"0.3.0","platform":"ruby","checksum":"d9de6d8fb7072228463bf61843159419c969edb34b3cef51832b516ae7972765"},
+{"name":"net-http-persistent","version":"4.0.1","platform":"ruby","checksum":"2752f4cce05fd1c45e0537c6f3a98fa5a4899efd5f88e63c104ed5f05cbddef9"},
+{"name":"net-ldap","version":"0.16.3","platform":"ruby","checksum":"db464d2f8236e5f8546c07abb51b08e0a62b77136b1c7f91fa0ec5fe2336908e"},
+{"name":"net-ntp","version":"2.1.3","platform":"ruby","checksum":"5bc73f4102bde0d1872bd3b293608ae99d9f5007d744f21919c6a565eda9267d"},
+{"name":"net-scp","version":"3.0.0","platform":"ruby","checksum":"8fc6c80365b95230c6bfc529dbea3893d2d81724855bfb01cbf385866e1c902c"},
+{"name":"net-ssh","version":"6.0.0","platform":"ruby","checksum":"6290ddcb232380cae79b772af924e12f57fe1dcd0f71254411dd21c04f7b13d0"},
+{"name":"netrc","version":"0.11.0","platform":"ruby","checksum":"de1ce33da8c99ab1d97871726cba75151113f117146becbe45aa85cb3dabee3f"},
+{"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.8","platform":"aarch64-linux","checksum":"d6b2c45a57738f12fe27783939fe1394e7049246288c7770d3b1fee7f49432a6"},
+{"name":"nokogiri","version":"1.13.8","platform":"arm64-darwin","checksum":"00217e48a6995e81dd83014325c0ea0b015023a8922c7bdb2ef1416aa87c1f43"},
+{"name":"nokogiri","version":"1.13.8","platform":"java","checksum":"9d04c616900e2b5118e501436ebb9bc48520d08f3695d012a314006e28082f72"},
+{"name":"nokogiri","version":"1.13.8","platform":"ruby","checksum":"79c279298b2f22fd4e760f49990c7930436bac1b1cfeff7bacff192f30edea3c"},
+{"name":"nokogiri","version":"1.13.8","platform":"x64-mingw-ucrt","checksum":"98f7dac7583f07a84ec3fcc01dc03a66fce10f412cd363fce7de749acdb2a42d"},
+{"name":"nokogiri","version":"1.13.8","platform":"x64-mingw32","checksum":"117a71b37f2e1d774a9f031d393e72d5d04b92af8036e0c1a8dd509c247b2013"},
+{"name":"nokogiri","version":"1.13.8","platform":"x86-linux","checksum":"6d04342456edfb8fbc041d0c2cf5a59baaa7aacdda414b2333100b02f85d441d"},
+{"name":"nokogiri","version":"1.13.8","platform":"x86-mingw32","checksum":"0529d558b4280a55bc7af500d3d4d590b7c059c814a0cea52e4e18cb30c25d15"},
+{"name":"nokogiri","version":"1.13.8","platform":"x86_64-darwin","checksum":"8966d79e687b271df87a4b240456597c43cd98584e3f783fc35de4f066486421"},
+{"name":"nokogiri","version":"1.13.8","platform":"x86_64-linux","checksum":"344f1bc66feac787e5b2053c6e9095d1f33605083e58ddf2b8d4eef257bccc5f"},
+{"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"},
+{"name":"oauth2","version":"2.0.3","platform":"ruby","checksum":"b343d8665a936b4223b335b38f8640134ce14e07c540b8d17e8bbac0b4e5c41b"},
+{"name":"octokit","version":"4.25.1","platform":"ruby","checksum":"c02092ee82dcdfe84db0e0ea630a70d32becc54245a4f0bacfd21c010df09b96"},
+{"name":"ohai","version":"16.10.6","platform":"ruby","checksum":"b835806e585faea4ac8346b68c722fb5fc29a29f73fd7e3a022f9073132dec22"},
+{"name":"oj","version":"3.13.21","platform":"ruby","checksum":"aef31a8dcc6f0b9b4bb5cc7ac6cc5272b2d851deb11a1804c2ed6b5501b50e46"},
+{"name":"omniauth","version":"2.1.0","platform":"ruby","checksum":"bff7234f5ec9323622b217c7f26d52f850de0b0e2b8c807c3358fc79fe572300"},
+{"name":"omniauth-alicloud","version":"1.0.1","platform":"ruby","checksum":"2f81ec0b8f98d2ab5e5d64555c75ace46582cf27c4ef4207aa59f15331dd697a"},
+{"name":"omniauth-atlassian-oauth2","version":"0.2.0","platform":"ruby","checksum":"eb07574a188ab8a03376ce288bce86bc2dd4a1382ffa5781cb5e2b7bc15d76c9"},
+{"name":"omniauth-auth0","version":"2.0.0","platform":"ruby","checksum":"823769be7883b45b2fa94367c2f6a17f7b3b1333986016089c016d45827da545"},
+{"name":"omniauth-authentiq","version":"0.3.3","platform":"ruby","checksum":"11b3791085a130782bf14b0088653beeb085638a9548d7110a57d3cbbb54fb4c"},
+{"name":"omniauth-azure-activedirectory-v2","version":"1.0.0","platform":"ruby","checksum":"edcdd77ace89ae3a0ed4b3d350b64a0c81b5a2435dff6f8eda8cbce15cbf69df"},
+{"name":"omniauth-dingtalk-oauth2","version":"1.0.1","platform":"ruby","checksum":"6545670f1c38344eaf960df9750c550a9534f790f888af088761a9e04269139b"},
+{"name":"omniauth-facebook","version":"4.0.0","platform":"ruby","checksum":"05ae3565c8fdb38df8dab04eb8ca854ea6c18e81591d3e6598ce101293a2f20f"},
+{"name":"omniauth-github","version":"2.0.0","platform":"ruby","checksum":"1ca26576125a97e27d3f8dc39cd98853d7382dd0fc04a40d3b9ec345ee378649"},
+{"name":"omniauth-oauth","version":"1.2.0","platform":"ruby","checksum":"e7a78658498dc83aa3f3be1a776425c0f06a60d45d9236dbe5e98e61fadf827b"},
+{"name":"omniauth-oauth2","version":"1.7.3","platform":"ruby","checksum":"3f5a8f99fa72e0f91d2abd7475ceb972a4ae67ed59e049f314c0c1bad81f4745"},
+{"name":"omniauth-oauth2-generic","version":"0.2.2","platform":"ruby","checksum":"e30814f6c472e04f3d9e4a3ddc03bc9a46f53f9333f8d443bf3ad43c6ebcdbd4"},
+{"name":"omniauth-rails_csrf_protection","version":"1.0.1","platform":"ruby","checksum":"fc546aeb7d43b7b9d7737051c380156e61c8f080b898cd4934d523eaa7e59acf"},
+{"name":"omniauth-saml","version":"2.0.0","platform":"ruby","checksum":"02594fd6630de26a9e65a2e64223e9ad32324fa97a6c7f1f22a1553ea3dd44c7"},
+{"name":"omniauth-shibboleth","version":"1.3.0","platform":"ruby","checksum":"b0bb725ced5cb76fbfc187ddbb8ad6864d0cd5df714cab36a528df8ee4b1d113"},
+{"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.0","platform":"ruby","checksum":"1cdcbd35c7977647d7583def720aed15111cd29b326b737aa0c72b38bff58b0f"},
+{"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"},
+{"name":"org-ruby","version":"0.9.12","platform":"ruby","checksum":"93cbec3a4470cb9dca6a4a98dc276a6434ea9d9e7bc2d42ea33c3aedd5d1c974"},
+{"name":"orm_adapter","version":"0.5.0","platform":"ruby","checksum":"aa5d0be5d540cbb46d3a93e88061f4ece6a25f6e97d6a47122beb84fe595e9b9"},
+{"name":"os","version":"1.1.1","platform":"ruby","checksum":"3db1fbc14ab8ea99b69ed8e353c894613e1b35e665fffb90414996cf8989d489"},
+{"name":"pact","version":"1.59.0","platform":"ruby","checksum":"6272cea35e4ee809493fadcba9800d4a24c262ef0778a0d1ba5d9a9b3f61fc59"},
+{"name":"pact-mock_service","version":"3.6.2","platform":"ruby","checksum":"cc91229484ae428b6eb3a6673c178046cbf6610ee6536ca6cbac060b6071f547"},
+{"name":"pact-support","version":"1.15.1","platform":"ruby","checksum":"c364596fe9fe78c4f93028013262d5d97867a680fa6acc35dda946447cdf1d1f"},
+{"name":"parallel","version":"1.22.1","platform":"ruby","checksum":"ebdf1f0c51f182df38522f70ba770214940bef998cdb6e00f36492b29699761f"},
+{"name":"parser","version":"3.1.2.1","platform":"ruby","checksum":"57e49821b52d5fe7baffaca44ed77e9754688c9bbc68443b5293a722fdb161e0"},
+{"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"},
+{"name":"pg","version":"1.4.1","platform":"ruby","checksum":"386bbffad223cce1dda903dad2c59268e41f3d183792506671ae526011ff0487"},
+{"name":"pg","version":"1.4.1","platform":"x64-mingw-ucrt","checksum":"de35769d4e7c25daa035f2dc33447e74711ab0dc8b73f685a846987e0080d030"},
+{"name":"pg","version":"1.4.1","platform":"x64-mingw32","checksum":"3457bf6bfdda7144097ef23d490a83980ba4572c78c58689aadaf58940a1989d"},
+{"name":"pg","version":"1.4.1","platform":"x86-mingw32","checksum":"323d09138b7bbfc6ae8eb427774d3639fc0e995983e65bb729527bf8e859fc29"},
+{"name":"pg_query","version":"2.1.3","platform":"ruby","checksum":"f3dd4b4c88c638eab48e9274f0dd88c584b60f8da58e3008b873192fe1e47001"},
+{"name":"plist","version":"3.6.0","platform":"ruby","checksum":"f468bcf6b72ec6d1585ed6744eb4817c1932a5bf91895ed056e69b7f12ca10f2"},
+{"name":"png_quantizator","version":"0.2.1","platform":"ruby","checksum":"6023d4d064125c3a7e02929c95b7320ed6ac0d7341f9e8de0c9ea6576ef3106b"},
+{"name":"po_to_json","version":"1.0.1","platform":"ruby","checksum":"6a7188aa6c42a22c9718f9b39062862ef7f3d8f6a7b4177cae058c3308b56af7"},
+{"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":"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"},
+{"name":"pry-rails","version":"0.3.9","platform":"ruby","checksum":"468662575abb6b67f4a9831219f99290d5eae7bf186e64dd810d0a3e4a8cc4b1"},
+{"name":"pry-shell","version":"0.5.1","platform":"ruby","checksum":"2b9000e30677acf5d66f55fa53d31934b7c069d9e0f738a0b84eed03a4ab677d"},
+{"name":"public_suffix","version":"4.0.7","platform":"ruby","checksum":"8be161e2421f8d45b0098c042c06486789731ea93dc3a896d30554ee38b573b8"},
+{"name":"puma","version":"5.6.5","platform":"java","checksum":"29d78fc2bc070b9db285a3334a890c3e0ece9bb369388065f0f340ccb1e57faf"},
+{"name":"puma","version":"5.6.5","platform":"ruby","checksum":"661029d15a115e9f6c0641a69c830ffd9f1b9ac63fcd0791d94ccd900e03f863"},
+{"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":"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"},
+{"name":"rack-cors","version":"1.1.1","platform":"ruby","checksum":"4702644ac6d63ebbddff372a3cd4cd573513287e3524b5a5415f678970057a4b"},
+{"name":"rack-oauth2","version":"1.21.2","platform":"ruby","checksum":"06fc157cad243ac11d8681c18139c4556a3f86b518f0fcb419e29512181a3ff2"},
+{"name":"rack-protection","version":"2.2.2","platform":"ruby","checksum":"fd41414dbabbec274af0bdb1f72a48504449de4d979782c9af38cbb5dfff3299"},
+{"name":"rack-proxy","version":"0.7.2","platform":"ruby","checksum":"89c74fd6d3e5a4db0ed7e0d8b9915dbc85360fd6f98f6c1f9a66479fe236f4b6"},
+{"name":"rack-test","version":"1.1.0","platform":"ruby","checksum":"154161f40f162b1c009a655b7b0c5de3a3102cc6d7d2e94b64e1f46ace800866"},
+{"name":"rack-timeout","version":"0.6.0","platform":"ruby","checksum":"6038d1cc31936394bd2c57bb16c17b31d2fd1d33ce928537cf0ef6f1f1905099"},
+{"name":"rails","version":"6.1.6.1","platform":"ruby","checksum":"17024921a3913fb341f584542b06adf6bb12977a8b92d5fce093c3996c963686"},
+{"name":"rails-controller-testing","version":"1.0.5","platform":"ruby","checksum":"741448db59366073e86fc965ba403f881c636b79a2c39a48d0486f2607182e94"},
+{"name":"rails-dom-testing","version":"2.0.3","platform":"ruby","checksum":"b140c4f39f6e609c8113137b9a60dfc2ecb89864e496f87f23a68b3b8f12d8d1"},
+{"name":"rails-html-sanitizer","version":"1.4.3","platform":"ruby","checksum":"2ebba6ad9a0b100f79fda853a46851e7664febe1728223f9734281e0d55940d6"},
+{"name":"rails-i18n","version":"7.0.3","platform":"ruby","checksum":"e3158e98c5332d129fd5131f171ac575eb30dbb8919b21595382b08850cf2bd3"},
+{"name":"railties","version":"6.1.6.1","platform":"ruby","checksum":"bafecdf2dcbe4ea44e1ab7081fd797aa87ae9bbcd0f3a4372b662a1b93949733"},
+{"name":"rainbow","version":"3.1.1","platform":"ruby","checksum":"039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a"},
+{"name":"rake","version":"13.0.6","platform":"ruby","checksum":"5ce4bf5037b4196c24ac62834d8db1ce175470391026bd9e557d669beeb19097"},
+{"name":"randexp","version":"0.1.7","platform":"ruby","checksum":"3026510ecf6a8e8642b9b96fa44bb41af6d24058023b7df77cf280f08e14e4c8"},
+{"name":"rash_alt","version":"0.4.12","platform":"ruby","checksum":"1d4a6dd5b8fdcecc6b777c0b924dbf31c125ddd8d805e72dc9359db8324e1607"},
+{"name":"rb-fsevent","version":"0.11.2","platform":"ruby","checksum":"43900b972e7301d6570f64b850a5aa67833ee7d87b458ee92805d56b7318aefe"},
+{"name":"rb-inotify","version":"0.10.1","platform":"ruby","checksum":"050062d4f31d307cca52c3f6a7f4b946df8de25fc4bd373e1a5142e41034a7ca"},
+{"name":"rbtrace","version":"0.4.14","platform":"ruby","checksum":"162bbf89cecabfc4f09c869b655f6f3a679c4870ebb7cbdcadf7393a81cc1769"},
+{"name":"rbtree","version":"0.4.4","platform":"ruby","checksum":"c1277a502a96fe8fd8656cb619db1ac87145df809ea4db35f7242b50bb161d5c"},
+{"name":"rchardet","version":"1.8.0","platform":"ruby","checksum":"693acd5253d5ade81a51940697955f6dd4bb2f0d245bda76a8e23deec70a52c7"},
+{"name":"rdoc","version":"6.3.2","platform":"ruby","checksum":"def4a720235c27d56c176ae73555e647eb04ea58a8bbaa927f8f9f79de7805a6"},
+{"name":"re2","version":"1.4.0","platform":"ruby","checksum":"5c07d2351be1159530e2b815aae499b6524942e79bf21d560fcff5b2fa20ea8f"},
+{"name":"recaptcha","version":"4.13.1","platform":"ruby","checksum":"dc6c2cb78afa87034358b7ba1c6f7175972b5709fdf7500e2550623e119e3788"},
+{"name":"recursive-open-struct","version":"1.1.3","platform":"ruby","checksum":"a3538a72552fcebcd0ada657bdff313641a4a5fbc482c08cfb9a65acb1c9de5a"},
+{"name":"redcarpet","version":"3.5.1","platform":"ruby","checksum":"717f64cb6ec11c8d9ec9b521ed26ca2eeda68b4fe1fc3388a641176dbd47732f"},
+{"name":"redis","version":"4.7.1","platform":"ruby","checksum":"ecb256d4e53ead3eca05bf394dd100e6a162c136f461fe752ddf5d35b64a2df6"},
+{"name":"redis-actionpack","version":"5.3.0","platform":"ruby","checksum":"3fb1ad0a8fd9d26a289c9399bb609dcaef38bf37711e6f677a53ca728fc19140"},
+{"name":"redis-namespace","version":"1.8.1","platform":"ruby","checksum":"c81707d693e5e754c690b4e1fa68ecfa8bd1028fbf306e533b3832e44e76c54c"},
+{"name":"redis-rack","version":"2.1.4","platform":"ruby","checksum":"0872eecb303e483c3863d6bd0d47323d230640d41c1a4ac4a2c7596ec0b1774c"},
+{"name":"redis-store","version":"1.9.1","platform":"ruby","checksum":"7b4c7438d46f7b7ce8f67fc0eda3a04fc67d32d28cf606cc98a5df4d2b77071d"},
+{"name":"regexp_parser","version":"2.5.0","platform":"ruby","checksum":"a076d2d35ab8d11feab5fecf8aa09ec6df68c2429810748cba079f7b021ecde5"},
+{"name":"regexp_property_values","version":"1.0.0","platform":"java","checksum":"5e26782b01241616855c4ee7bb8a62fce9387e484f2d3eaf04f2a0633708222e"},
+{"name":"regexp_property_values","version":"1.0.0","platform":"ruby","checksum":"162499dc0bba1e66d334273a059f207a61981cc8cc69d2ca743594e7886d080f"},
+{"name":"representable","version":"3.0.4","platform":"ruby","checksum":"07d43917dea4712ecebd19c1909e769deed863ad444d23ceb6461519e2cba962"},
+{"name":"request_store","version":"1.5.1","platform":"ruby","checksum":"07a204d161590789f2b1d27f9f0eadcdecd6d868cb2f03240250e1bc747df78e"},
+{"name":"responders","version":"3.0.0","platform":"ruby","checksum":"a267b281582802d04cf0968dbc7d60df0d48384915934b3bf9018f811dc3f7dc"},
+{"name":"rest-client","version":"2.1.0","platform":"ruby","checksum":"35a6400bdb14fae28596618e312776c158f7ebbb0ccad752ff4fa142bf2747e3"},
+{"name":"rest-client","version":"2.1.0","platform":"x64-mingw32","checksum":"7cd156496196d90b7d8f5b8de521ef67d8a9e03f06862da80b9b5912ab05a470"},
+{"name":"rest-client","version":"2.1.0","platform":"x86-mingw32","checksum":"fb543caf36cb555c05c6186aeb1273c6a1b059896e4cfd394e7269b20c40ca01"},
+{"name":"rest-client","version":"2.1.0","platform":"x86-mswin32","checksum":"a35a3bb8d16ca39d110a946a2c805267f98ce07a0ae890e4512a45eadea47a6e"},
+{"name":"retriable","version":"3.1.2","platform":"ruby","checksum":"0a5a5d0ca4ba61a76fb31a17ab8f7f80281beb040c329d34dfc137a1398688e0"},
+{"name":"reverse_markdown","version":"1.4.0","platform":"ruby","checksum":"a3305da1509ac8388fa84a28745621113e121383402a2e8e9350ba649034e870"},
+{"name":"rexml","version":"3.2.5","platform":"ruby","checksum":"a33c3bf95fda7983ec7f05054f3a985af41dbc25a0339843bd2479e93cabb123"},
+{"name":"rinku","version":"2.0.0","platform":"ruby","checksum":"3e695aaf9f24baba3af45823b5c427b58a624582132f18482320e2737f9f8a85"},
+{"name":"rotp","version":"6.2.0","platform":"ruby","checksum":"239a2eefba6f1bd4157b2c735d0f975598e0ef94823eea2f35d103d2e5cc0787"},
+{"name":"rouge","version":"3.30.0","platform":"ruby","checksum":"a3d353222aa72e49e2c86726c0bcfd719f82592f57d494474655f48e669eceb6"},
+{"name":"rqrcode","version":"0.7.0","platform":"ruby","checksum":"8b3a5cba9cc199ba2d781a7c767cb55679f29a3621aa0506a799cec3760d16a1"},
+{"name":"rqrcode-rails3","version":"0.1.7","platform":"ruby","checksum":"6f0582f26485123e5ed6f2a8a2871f00d86d353e0f58c8429a5a13212bcf48c4"},
+{"name":"rspec","version":"3.10.0","platform":"ruby","checksum":"b870b43d49ae4a4e063b94976d2742b0854ec10458c425d569b5556ee5898ab7"},
+{"name":"rspec-benchmark","version":"0.6.0","platform":"ruby","checksum":"1014adb57ec2599a2455c63884229f367a2fff6a63a77fd68ce5d804c83dd6cf"},
+{"name":"rspec-core","version":"3.10.1","platform":"ruby","checksum":"ac9abdc9577a3a34e9e92815603da8343931055ab4fba1c2a49de6dd3b749673"},
+{"name":"rspec-expectations","version":"3.10.1","platform":"ruby","checksum":"27acf5d5df13f8cc8f7158001ebf572513bcec3d45404ba76e0a8998895ce9eb"},
+{"name":"rspec-mocks","version":"3.10.3","platform":"ruby","checksum":"d2f6f3d8b7569b1e846703d164cb23e24c7f530d38217fc06da2beaf6024260a"},
+{"name":"rspec-parameterized","version":"0.5.0","platform":"ruby","checksum":"f163ac07b5edd1eeb13136480623db7020852c70cf0ad2fa98e31384ae162454"},
+{"name":"rspec-rails","version":"5.0.1","platform":"ruby","checksum":"c61e7f35db2266f83b3cc58a340fc3ec0bd6344818040430fd5ddc99775242de"},
+{"name":"rspec-retry","version":"0.6.1","platform":"ruby","checksum":"86b7e8513c5b0c713c2e28854f4d996deb8efa6304eef50f0ad68ee6c563d8da"},
+{"name":"rspec-support","version":"3.10.2","platform":"ruby","checksum":"74315f89069fbaf2a710e2117340373b77ee45eceb026de87e0cad9dd5154596"},
+{"name":"rspec_junit_formatter","version":"0.4.1","platform":"ruby","checksum":"3788f9b3fabc6284b93493cf4b3a80cba2b59b3a774b95f39dd7886d5faed6ab"},
+{"name":"rspec_profiling","version":"0.0.6","platform":"ruby","checksum":"7a45697f79dcec9a174a0e26703465f6bd52ee78e8d798741240bfcef38f6e6e"},
+{"name":"rubocop","version":"0.93.1","platform":"ruby","checksum":"73b44fbbe872edbd3f14487175b6369a0f48e952c155f305896ffa56c48b195e"},
+{"name":"rubocop-ast","version":"1.21.0","platform":"ruby","checksum":"8f5d98611343498602de2d41bc583aca71599daad16daeadaeeee60f134c9568"},
+{"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.9.2","platform":"ruby","checksum":"3373ad82b70189fa16b593b6237eb06186da669d468b7d6483bca64c0a844a05"},
+{"name":"rubocop-rails","version":"2.9.1","platform":"ruby","checksum":"2d8d113c3ae074c78c89cb706b4a08116d730bf92dbef148798498171435c540"},
+{"name":"rubocop-rspec","version":"1.44.1","platform":"ruby","checksum":"7b2238e7d6cf17a925a90992914f3cd8ecd68b65b31710d60a3f7f647f8a8b2a"},
+{"name":"ruby-fogbugz","version":"0.2.1","platform":"ruby","checksum":"15b2e7fe7e95b021a94ee6e9d8bb32fdad6ae44e820c2ce0dc312fe6e77d40ca"},
+{"name":"ruby-magic","version":"0.5.4","platform":"ruby","checksum":"2c17b185130d10a83791f63a40baa358c4b138af37da3f4dab53690121c421d5"},
+{"name":"ruby-prof","version":"1.3.1","platform":"ruby","checksum":"e735d20c92954e1fa2a4475539c99dfc8d0166b4cc6915ca309e8ee2dd19323c"},
+{"name":"ruby-prof","version":"1.3.1","platform":"x64-mingw32","checksum":"a97bb6ff0abb01131ecba88799222d492d9124e057535db8e5bd47119f2a58ba"},
+{"name":"ruby-progressbar","version":"1.11.0","platform":"ruby","checksum":"cc127db3866dc414ffccbf92928a241e585b3aa2b758a5563e74a6ee0f57d50a"},
+{"name":"ruby-saml","version":"1.13.0","platform":"ruby","checksum":"d31cbdf5fb8fdd6aa3187e48dba3085cfeb751af30276a5739aa3659a66f069c"},
+{"name":"ruby-statistics","version":"3.0.0","platform":"ruby","checksum":"610301370346931cb701e3a8d3d3e28eb65681162cae6066c0c11abf20efdc81"},
+{"name":"ruby2_keywords","version":"0.0.5","platform":"ruby","checksum":"ffd13740c573b7301cf7a2e61fc857b2a8e3d3aff32545d6f8300d8bae10e3ef"},
+{"name":"ruby_parser","version":"3.15.0","platform":"ruby","checksum":"53784028e46407d43b5a704f10f105db00241102ee4402d2a55755b5fa2ad42c"},
+{"name":"rubyntlm","version":"0.6.3","platform":"ruby","checksum":"5b321456dba3130351f7451f8669f1afa83a0d26fd63cdec285b7b88e667102d"},
+{"name":"rubypants","version":"0.2.0","platform":"ruby","checksum":"f07e38eac793655a0323fe91946081052341b9e69807026fcf102346589eedee"},
+{"name":"rubyzip","version":"2.3.2","platform":"ruby","checksum":"3f57e3935dc2255c414484fbf8d673b4909d8a6a57007ed754dde39342d2373f"},
+{"name":"rugged","version":"1.2.0","platform":"ruby","checksum":"111979962e75378209673ae262a8f43bd1971b5b5f96f4dfb77ca82b343604ed"},
+{"name":"safe_yaml","version":"1.0.4","platform":"ruby","checksum":"248193992ef1730a0c9ec579999ef2256a2b3a32a9bd9d708a1e12544a489ec2"},
+{"name":"safety_net_attestation","version":"0.4.0","platform":"ruby","checksum":"96be2d74e7ed26453a51894913449bea0e072f44490021545ac2d1c38b0718ce"},
+{"name":"sanitize","version":"6.0.0","platform":"ruby","checksum":"81795f985873f3bacee2eaaededeaafc3a29aafeaa9aff51e04b85a66bbf08ff"},
+{"name":"sass","version":"3.5.5","platform":"ruby","checksum":"1bb5431bc620ce29076728a4c8f7b4acb55066ed9df8cf5d57db6cda450d8080"},
+{"name":"sass-listen","version":"4.0.0","platform":"ruby","checksum":"ae9dcb76dd3e234329e5ba6e213f48e532c5a3e7b0b4d8a87f13aaca0cc18377"},
+{"name":"sassc","version":"2.4.0","platform":"ruby","checksum":"4c60a2b0a3b36685c83b80d5789401c2f678c1652e3288315a1551d811d9f83e"},
+{"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":"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"},
+{"name":"sentry-rails","version":"5.1.1","platform":"ruby","checksum":"906ef0a776ddc35884ab8b548856ba81c607e3fdee7c9c9f7c44efccc16a657f"},
+{"name":"sentry-raven","version":"3.1.2","platform":"ruby","checksum":"103d3b122958810d34898ce2e705bcf549ddb9d855a70ce9a3970ee2484f364a"},
+{"name":"sentry-ruby","version":"5.1.1","platform":"ruby","checksum":"4e49563b72c1c22ffe3a67e5024b856c364766146d01e270423fd494d8fcc125"},
+{"name":"sentry-ruby-core","version":"5.1.1","platform":"ruby","checksum":"152ed891ee78348da448a65237be92990f3e8b9e5b34bb39003eade48cca5d04"},
+{"name":"sentry-sidekiq","version":"5.1.1","platform":"ruby","checksum":"e4c3618577fba37f7a9fc3812013a7868d09e0e0a0970efc605e6e184079d1af"},
+{"name":"set","version":"1.0.1","platform":"ruby","checksum":"d169fe8df4738e9da1118199429a9cf1ce0ac5e8a3cacc481e2ed24d585419dd"},
+{"name":"settingslogic","version":"2.0.9","platform":"ruby","checksum":"5925a91d0d48dfb59a6e48ae2bb9c9b801fe6fab25a8e8d302ce8699d92f2ae6"},
+{"name":"sexp_processor","version":"4.15.1","platform":"ruby","checksum":"9291a0f2247f50d15068ee6965b67cd7b678b0d273e18adf3c0b2ea4a890125c"},
+{"name":"shellany","version":"0.0.1","platform":"ruby","checksum":"0e127a9132698766d7e752e82cdac8250b6adbd09e6c0a7fbbb6f61964fedee7"},
+{"name":"shoulda-matchers","version":"5.1.0","platform":"ruby","checksum":"a01d20589989e9653ab4a28c67d9db2b82bcf0a2496cf01d5e1a95a4aaaf5b07"},
+{"name":"sidekiq","version":"6.4.2","platform":"ruby","checksum":"0d3c05fecb5fbace5ff5efc63da707e02a9c4673fb8e33ceca10b5ec0e9f062c"},
+{"name":"sidekiq-cron","version":"1.4.0","platform":"ruby","checksum":"21612ade25ea79b4eeb8eacd7fb559a85a1abf6bf1da1aca1aa0079cafd3376d"},
+{"name":"sigdump","version":"0.2.4","platform":"ruby","checksum":"0bf2176e55c1a262788623fe5ea57caddd6ba2abebe5e349d9d5e7c3a3010ed7"},
+{"name":"signet","version":"0.17.0","platform":"ruby","checksum":"1d2831930dc28da32e34bec68cf7ded97ee2867b208f97c500ee293829cb0004"},
+{"name":"simple_po_parser","version":"1.1.6","platform":"ruby","checksum":"122687d44d3de516a0e69e2f383a4180f5015e8c5ed5a7f2258f2b376f64cbf3"},
+{"name":"simplecov","version":"0.21.2","platform":"ruby","checksum":"990db6aedb55086d6bf8874993ff1f796e4830abfa11937468ca502a0d013bc3"},
+{"name":"simplecov-cobertura","version":"1.3.1","platform":"ruby","checksum":"9b738110652421cd5a9f09f495cc1159533c02ac4a53e7fe8ae60934bca0331e"},
+{"name":"simplecov-html","version":"0.12.3","platform":"ruby","checksum":"4b1aad33259ffba8b29c6876c12db70e5750cb9df829486e4c6e5da4fa0aa07b"},
+{"name":"simplecov-lcov","version":"0.8.0","platform":"ruby","checksum":"0115f31cb7ef5ec4334f5d9382c67fd43de2e5270e21b65bfc693da82dd713c1"},
+{"name":"simplecov_json_formatter","version":"0.1.4","platform":"ruby","checksum":"529418fbe8de1713ac2b2d612aa3daa56d316975d307244399fa4838c601b428"},
+{"name":"sixarm_ruby_unaccent","version":"1.2.0","platform":"ruby","checksum":"0043a6077bdf2c4b03040152676a07f8bf77144f9b007b1960ee5c94d13a4384"},
+{"name":"slack-messenger","version":"2.3.4","platform":"ruby","checksum":"49c611d2be5b0f9c250a3a957b9cc09b9c07b81dacb9843642d87b6fa35609c1"},
+{"name":"snowplow-tracker","version":"0.6.1","platform":"ruby","checksum":"9cec52fd060619f4974b3dc1f7d9a2776c5e31b668a6ead53145b9780e312314"},
+{"name":"solargraph","version":"0.46.0","platform":"ruby","checksum":"1da9fd8c364501f18b0454e54506e7098bc38dae719219713fe5f246dfc91465"},
+{"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-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"},
+{"name":"sprockets-rails","version":"3.4.2","platform":"ruby","checksum":"36d6327757ccf7460a00d1d52b2d5ef0019a4670503046a129fa1fb1300931ad"},
+{"name":"sqlite3","version":"1.4.2","platform":"ruby","checksum":"e8b8ef3b0f75c18e1a7ee62c5678c827e99389e53fa55eb7a9a5f57459004a52"},
+{"name":"ssh_data","version":"1.3.0","platform":"ruby","checksum":"ec7c1e95a3aebeee412147998f4c147b4b05da6ed0aafda6083f9449318eaac0"},
+{"name":"ssrf_filter","version":"1.0.7","platform":"ruby","checksum":"80dc5728e5743201239e465c7e14290c7acf1de3b9f1f664579112429c79953a"},
+{"name":"stackprof","version":"0.2.21","platform":"ruby","checksum":"2b6406c55dc2e134b2789c4cc631d96e67da87821a166f4ae12f15bec5cff5ae"},
+{"name":"state_machines","version":"0.5.0","platform":"ruby","checksum":"23e6249d374a920b528dccade403518b4abbd83841a3e2c9ef13e6f1a009b102"},
+{"name":"state_machines-activemodel","version":"0.8.0","platform":"ruby","checksum":"e932dab190d4be044fb5f9cab01a3ea0b092c5f113d4676c6c0a0d49bf738d2c"},
+{"name":"state_machines-activerecord","version":"0.8.0","platform":"ruby","checksum":"072fb701b8ab03de0608297f6c55dc34ed096e556fa8f77e556f3c461c71aab6"},
+{"name":"strings","version":"0.2.1","platform":"ruby","checksum":"933293b3c95cf85b81eb44b3cf673e3087661ba739bbadfeadf442083158d6fb"},
+{"name":"strings-ansi","version":"0.2.0","platform":"ruby","checksum":"90262d760ea4a94cc2ae8d58205277a343409c288cbe7c29416b1826bd511c88"},
+{"name":"swd","version":"1.3.0","platform":"ruby","checksum":"bc382a19e1d36a95529b25152976db61b80376c3d486b21c8dd60ac2b5c06389"},
+{"name":"sync","version":"0.5.0","platform":"ruby","checksum":"668356cc07c59ac7ed9ecf34fec3929831f179c07adb1f3e1c3b7a1609a638fd"},
+{"name":"sys-filesystem","version":"1.4.3","platform":"ruby","checksum":"390919de89822ad6d3ba3daf694d720be9d83ed95cdf7adf54d4573c98b17421"},
+{"name":"sysexits","version":"1.2.0","platform":"ruby","checksum":"598241c4ae57baa403c125182dfdcc0d1ac4c0fb606dd47fbed57e4aaf795662"},
+{"name":"tanuki_emoji","version":"0.6.0","platform":"ruby","checksum":"4ce91aefed2d076b73fba3eff50e89660c3d25691787a9fe4c0dfabb4218c12a"},
+{"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":"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"},
+{"name":"text","version":"1.3.1","platform":"ruby","checksum":"2fbbbc82c1ce79c4195b13018a87cbb00d762bda39241bb3cdc32792759dd3f4"},
+{"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.10","platform":"ruby","checksum":"9b664f0e9ae2b500cfa00f9c65c34abc6ff1799cf0034a8c0a0412d520fac866"},
+{"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.0","platform":"ruby","checksum":"20b5ea997dc046358fd05f15d39636bd7946798591b9c5741cc41f69853c7894"},
+{"name":"toml-rb","version":"2.0.1","platform":"ruby","checksum":"5016c6c77ac72bca5fe67c372722bdfdd4479a6fe1a1c4ff2a486e247849b274"},
+{"name":"tomlrb","version":"1.3.0","platform":"ruby","checksum":"68666bf53fa70ba686a48a7435ce7e086f5227c58c4c993bd9792f4760f2a503"},
+{"name":"tpm-key_attestation","version":"0.9.0","platform":"ruby","checksum":"e469ad9111a68dab4d04596e1c0621d7c877c2e3e247f765af3c04f1adf2b8cd"},
+{"name":"train-core","version":"3.4.9","platform":"ruby","checksum":"d7ad8fa9a379c43a30baaaf1141af1cb28349d386c054f7fc81d169a625d6edd"},
+{"name":"truncato","version":"0.7.12","platform":"ruby","checksum":"fed9e8a04fa35fd1a64506cd2089761bae4adfe47e756c3ce98a5c43856c9c4c"},
+{"name":"tty-color","version":"0.6.0","platform":"ruby","checksum":"6f9c37ca3a4e2367fb2e6d09722762647d6f455c111f05b59f35730eeb24332a"},
+{"name":"tty-cursor","version":"0.7.1","platform":"ruby","checksum":"79534185e6a777888d88628b14b6a1fdf5154a603f285f80b1753e1908e0bf48"},
+{"name":"tty-markdown","version":"0.7.0","platform":"ruby","checksum":"251e8ef71f6a8bc91faa48aa49c341aff644321f493f94c030e571c5aa59e642"},
+{"name":"tty-prompt","version":"0.23.1","platform":"ruby","checksum":"fcdbce905238993f27eecfdf67597a636bc839d92192f6a0eef22b8166449ec8"},
+{"name":"tty-reader","version":"0.9.0","platform":"ruby","checksum":"c62972c985c0b1566f0e56743b6a7882f979d3dc32ff491ed490a076f899c2b1"},
+{"name":"tty-screen","version":"0.8.1","platform":"ruby","checksum":"6508657c38f32bdca64880abe201ce237d80c94146e1f9b911cba3c7823659a2"},
+{"name":"typhoeus","version":"1.4.0","platform":"ruby","checksum":"fff9880d5dc35950e7706cf132fd297f377c049101794be1cf01c95567f642d4"},
+{"name":"tzinfo","version":"2.0.5","platform":"ruby","checksum":"c5352fd901544d396745d013f46a04ae2ed081ce806d942099825b7c2b09a167"},
+{"name":"u2f","version":"0.2.1","platform":"ruby","checksum":"7907b163c00682ce94d82178154af2ec3930e50f342c3502d64929c6370c5553"},
+{"name":"uber","version":"0.1.0","platform":"ruby","checksum":"5beeb407ff807b5db994f82fa9ee07cfceaa561dad8af20be880bc67eba935dc"},
+{"name":"undercover","version":"0.4.4","platform":"ruby","checksum":"61c4cbe03a9de0764b07cceef82a63f9d8dbf4d8680ec017cee307927561f6a5"},
+{"name":"unf","version":"0.1.4","platform":"java","checksum":"49a5972ec0b3d091d3b0b2e00113f2f342b9b212f0db855eb30a629637f6d302"},
+{"name":"unf","version":"0.1.4","platform":"ruby","checksum":"4999517a531f2a955750f8831941891f6158498ec9b6cb1c81ce89388e63022e"},
+{"name":"unf_ext","version":"0.0.8.2","platform":"ruby","checksum":"90b9623ee359cc4878461c5d2eab7d3d3ce5801a680a9e7ac83b8040c5b742fa"},
+{"name":"unf_ext","version":"0.0.8.2","platform":"x64-mingw-ucrt","checksum":"580e3c1ca389f10ca17d13ddeb5dc3fbdc80ce2c2b8cdb15c08af3a3b45a60fc"},
+{"name":"unf_ext","version":"0.0.8.2","platform":"x64-mingw32","checksum":"f7e4c01774c91eb22e30d53dfc40ffbbb5a175f785c8f6f1be17ad96a0b29ed0"},
+{"name":"unf_ext","version":"0.0.8.2","platform":"x86-mingw32","checksum":"6d44c13c98924bebd15ebdd4ed196ead403a0770ac03304570873349fda2a208"},
+{"name":"unicode-display_width","version":"1.8.0","platform":"ruby","checksum":"0292132d364d59fcdd83f144910c48b3c8332b28a14c5c04bb093dd165600488"},
+{"name":"unicode_utils","version":"1.4.0","platform":"ruby","checksum":"b922d0cf2313b6b7136ada6645ce7154ffc86418ca07d53b058efe9eb72f2a40"},
+{"name":"uniform_notifier","version":"1.16.0","platform":"ruby","checksum":"99b39ee4a0864e3b49f375b5e5803eb26d35ed6eb1719c96407573a87bc4dbb5"},
+{"name":"unleash","version":"3.2.2","platform":"ruby","checksum":"0f6e56498de920de66a01bceffb93933693ade646bb853fc70eb16bd1026b93b"},
+{"name":"unparser","version":"0.6.0","platform":"ruby","checksum":"4afa0540583032d28a623e65f057809fdbed6dc84bd8a1de93262e1aa4618608"},
+{"name":"uri_template","version":"0.7.0","platform":"ruby","checksum":"312c8fe13700db86ac9d05ea997af3db03abdf50c65b1801d775bc7a695f185d"},
+{"name":"valid_email","version":"0.1.3","platform":"ruby","checksum":"b81452b51b64c4beb67913f68db52c20ecb4d73d45512f5b282ab4a3f4416570"},
+{"name":"validate_email","version":"0.1.6","platform":"ruby","checksum":"9dfe9016d527b17a8d3a6e95e4dc50a125400eef899d13d4cc2a254393f82ee4"},
+{"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.0.0","platform":"ruby","checksum":"929c93a4d46482bb3b0359980c7a5fb1b5a833548f1202a480b08a6f0a5f8f2f"},
+{"name":"version_sorter","version":"2.2.4","platform":"ruby","checksum":"7ad071609edfaa3cf28c42d83b1a03096e43512244ae5a9e2fce1404f7e06d41"},
+{"name":"view_component","version":"2.71.0","platform":"ruby","checksum":"c1880647800d9cfb03ff4ba92313db624a4a4b3d5753e137effe86e5f2b3662b"},
+{"name":"vmstat","version":"2.3.0","platform":"ruby","checksum":"ab5446a3e3bd0a9cdb9d9ac69a0bbd119c4f161d945a0846a519dd7018af656d"},
+{"name":"warden","version":"1.2.9","platform":"ruby","checksum":"46684f885d35a69dbb883deabf85a222c8e427a957804719e143005df7a1efd0"},
+{"name":"warning","version":"1.3.0","platform":"ruby","checksum":"23695a5d8e50bd5c46068931b529bee0b28e4982cbcefbe77d867800dde8069e"},
+{"name":"webauthn","version":"2.3.0","platform":"ruby","checksum":"96fbee59f4a45219f1dae96f467b693de144f871be9ec6ea357168624dacd89e"},
+{"name":"webfinger","version":"1.2.0","platform":"ruby","checksum":"7814ef1c85da47514f65c6e5ca14205fa9ce41ea2a70785e0c872842162852a2"},
+{"name":"webmock","version":"3.9.1","platform":"ruby","checksum":"bcf6822456b234fb1bed2b0a89bff31fe0641214b44f6ba4ced2b824cf31337d"},
+{"name":"webrick","version":"1.6.1","platform":"ruby","checksum":"0b4d1eab918f5f53333c690ad470825e51844ce9851e403a3fd47d6a84d9d67c"},
+{"name":"websocket-driver","version":"0.7.5","platform":"java","checksum":"fffa83aa188e9ac90e32a385832ec9d26acdf019538e1c7d703f2c8a323b39c8"},
+{"name":"websocket-driver","version":"0.7.5","platform":"ruby","checksum":"a280c3f44dcbb0323d58bc78dc49350c05d589ab7d13267fcff08d9d5ae76b28"},
+{"name":"websocket-extensions","version":"0.1.5","platform":"ruby","checksum":"1c6ba63092cda343eb53fc657110c71c754c56484aad42578495227d717a8241"},
+{"name":"wikicloth","version":"0.8.1","platform":"ruby","checksum":"7ac8a9ca0a948cf472851e521afc6c2a6b04a8f91ef1d824ba6a61ffbd60e6ca"},
+{"name":"wisper","version":"2.0.1","platform":"ruby","checksum":"ce17bc5c3a166f241a2e6613848b025c8146fce2defba505920c1d1f3f88fae6"},
+{"name":"with_env","version":"1.1.0","platform":"ruby","checksum":"50b3e4f0a6cda8f90d8a6bd87a6261f6c381429abafb161c4c69ad4a0cd0b6e4"},
+{"name":"wmi-lite","version":"1.0.5","platform":"ruby","checksum":"14efa710be3226e281a66ab93f7ebc92f5e0807029e02b9cf1d3f39d15d90d84"},
+{"name":"xml-simple","version":"1.1.9","platform":"ruby","checksum":"d21131e519c86f1a5bc2b6d2d57d46e6998e47f18ed249b25cad86433dbd695d"},
+{"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"}
+]
diff --git a/Gemfile.lock b/Gemfile.lock
index 218a66d8f74..9074b48c56b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,4 +1,10 @@
PATH
+ remote: vendor/gems/bundler-checksum
+ specs:
+ bundler-checksum (0.1.0)
+ bundler
+
+PATH
remote: vendor/gems/devise-pbkdf2-encryptable
specs:
devise-pbkdf2-encryptable (0.0.0)
@@ -14,7 +20,7 @@ PATH
remote: vendor/gems/ipynbdiff
specs:
ipynbdiff (0.4.7)
- diffy (~> 3.3)
+ diffy (~> 3.4)
oj (~> 3.13.16)
PATH
@@ -25,27 +31,58 @@ PATH
mail (~> 2.7)
PATH
+ remote: vendor/gems/microsoft_graph_mailer
+ specs:
+ microsoft_graph_mailer (0.1.0)
+ mail (~> 2.7)
+ oauth2 (>= 1.4.4, < 3)
+
+PATH
+ remote: vendor/gems/omniauth-azure-oauth2
+ specs:
+ omniauth-azure-oauth2 (0.0.10)
+ jwt (>= 1.0, < 3.0)
+ omniauth (~> 2.0)
+ omniauth-oauth2 (~> 1.4)
+
+PATH
remote: vendor/gems/omniauth-cas3
specs:
omniauth-cas3 (1.1.4)
addressable (~> 2.3)
nokogiri (~> 1.7, >= 1.7.1)
- omniauth (~> 1.2, < 3)
+ omniauth (~> 2.0)
PATH
remote: vendor/gems/omniauth-gitlab
specs:
omniauth-gitlab (4.0.0)
- omniauth (~> 1.0)
+ omniauth (~> 2.0)
+ omniauth-oauth2 (~> 1.7.1)
+
+PATH
+ remote: vendor/gems/omniauth-google-oauth2
+ specs:
+ omniauth-google-oauth2 (1.0.1)
+ jwt (>= 2.0)
+ oauth2 (~> 2.0)
+ omniauth (~> 2.0)
omniauth-oauth2 (~> 1.7.1)
PATH
+ remote: vendor/gems/omniauth-salesforce
+ specs:
+ omniauth-salesforce (1.0.5)
+ omniauth (~> 2.0)
+ omniauth-oauth2 (~> 1.0)
+
+PATH
remote: vendor/gems/omniauth_crowd
specs:
omniauth_crowd (2.4.0)
activesupport
nokogiri (>= 1.4.4)
- omniauth (~> 1.0, < 3)
+ omniauth (~> 2.0)
GEM
remote: https://rubygems.org/
@@ -131,13 +168,13 @@ GEM
faraday_middleware (~> 1.0)
faraday_middleware-multi_json (~> 0.0)
oauth2 (>= 1.4, < 3)
- asciidoctor (2.0.15)
+ asciidoctor (2.0.17)
asciidoctor-include-ext (0.4.0)
asciidoctor (>= 1.5.6, < 3.0.0)
asciidoctor-kroki (0.5.0)
asciidoctor (~> 2.0)
- asciidoctor-plantuml (0.0.12)
- asciidoctor (>= 1.5.6, < 3.0.0)
+ asciidoctor-plantuml (0.0.16)
+ asciidoctor (>= 2.0.17, < 3.0.0)
ast (2.4.2)
atlassian-jwt (0.2.0)
jwt (~> 2.1.0)
@@ -306,7 +343,7 @@ GEM
ruby-statistics (>= 2.1)
thor (>= 0.19, < 2)
device_detector (1.0.0)
- devise (4.7.3)
+ devise (4.8.1)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
@@ -320,7 +357,7 @@ GEM
rotp (~> 6.0)
diff-lcs (1.5.0)
diff_match_patch (0.1.0)
- diffy (3.3.0)
+ diffy (3.4.2)
discordrb-webhooks (3.4.2)
rest-client (>= 2.0.0)
docile (1.4.0)
@@ -377,7 +414,7 @@ GEM
encryptor (3.0.0)
erubi (1.9.0)
escape_utils (1.2.1)
- et-orbi (1.2.1)
+ et-orbi (1.2.7)
tzinfo
ethon (0.15.0)
ffi (>= 1.15.0)
@@ -486,7 +523,7 @@ GEM
fog-core
nokogiri (>= 1.5.11, < 2.0.0)
formatador (0.2.5)
- fugit (1.2.1)
+ fugit (1.2.3)
et-orbi (~> 1.1, >= 1.1.8)
raabro (~> 1.1)
fuubar (2.2.0)
@@ -508,7 +545,7 @@ GEM
rails (>= 3.2.0)
git (1.11.0)
rchardet (~> 1.8)
- gitaly (15.3.0.pre.rc3)
+ gitaly (15.4.0.pre.rc2)
grpc (~> 1.0)
github-markup (1.7.0)
gitlab (4.16.1)
@@ -516,7 +553,7 @@ GEM
terminal-table (~> 1.5, >= 1.5.1)
gitlab-chronic (0.10.5)
numerizer (~> 0.2)
- gitlab-dangerfiles (3.5.0)
+ gitlab-dangerfiles (3.5.2)
danger (>= 8.4.5)
danger-gitlab (>= 8.0.0)
rake
@@ -540,15 +577,15 @@ GEM
redis (> 3.0.0, < 5.0.0)
gitlab-license (2.2.1)
gitlab-mail_room (0.0.9)
- gitlab-markup (1.8.0)
+ gitlab-markup (1.8.1)
gitlab-net-dns (0.9.1)
- gitlab-omniauth-openid-connect (0.9.1)
+ gitlab-omniauth-openid-connect (0.10.0)
addressable (~> 2.7)
- omniauth (~> 1.9)
+ omniauth (>= 1.9, < 3)
openid_connect (~> 1.2)
gitlab-sidekiq-fetcher (0.8.0)
sidekiq (~> 6.1)
- gitlab-styles (7.1.0)
+ gitlab-styles (8.0.0)
rubocop (~> 0.91, >= 0.91.1)
rubocop-gitlab-security (~> 0.1.1)
rubocop-graphql (~> 0.10)
@@ -580,7 +617,7 @@ GEM
signet (~> 0.12)
google-cloud-env (1.6.0)
faraday (>= 0.17.3, < 3.0)
- google-protobuf (3.21.3)
+ google-protobuf (3.21.5)
googleapis-common-protos-types (1.3.0)
google-protobuf (~> 3.14)
googleauth (0.14.0)
@@ -649,10 +686,10 @@ GEM
guard (~> 2.1)
guard-compat (~> 1.1)
rspec (>= 2.99.0, < 4.0)
- haml (5.1.2)
+ haml (5.2.2)
temple (>= 0.8.0)
tilt
- haml_lint (0.36.0)
+ haml_lint (0.40.1)
haml (>= 4.0, < 5.3)
parallel (~> 1.10)
rainbow
@@ -757,7 +794,7 @@ GEM
rest-client (~> 2.0)
launchy (2.5.0)
addressable (~> 2.7)
- lefthook (1.1.0)
+ lefthook (1.1.1)
letter_opener (1.7.0)
launchy (~> 2.2)
letter_opener_web (2.0.0)
@@ -773,9 +810,9 @@ GEM
tomlrb (>= 1.3, < 2.1)
with_env (= 1.1.0)
xml-simple (~> 1.1.9)
- licensee (9.14.1)
+ licensee (9.15.2)
dotenv (~> 2.0)
- octokit (~> 4.17)
+ octokit (~> 4.20)
reverse_markdown (~> 1.0)
rugged (>= 0.24, < 2.0)
thor (>= 0.19, < 2.0)
@@ -792,9 +829,11 @@ GEM
loofah (2.18.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
- lookbook (0.9.3)
+ lookbook (1.0.3)
actioncable
+ css_parser
htmlbeautifier (~> 1.3)
+ htmlentities (~> 4.3.4)
listen (~> 3.0)
railties (>= 5.0)
redcarpet (~> 3.5)
@@ -888,9 +927,10 @@ GEM
train-core
wmi-lite (~> 1.0)
oj (3.13.21)
- omniauth (1.9.1)
+ omniauth (2.1.0)
hashie (>= 3.4.6)
- rack (>= 1.6.2, < 3)
+ rack (>= 2.2.3)
+ rack-protection
omniauth-alicloud (1.0.1)
omniauth-oauth2 (~> 1.7.1)
omniauth-atlassian-oauth2 (0.2.0)
@@ -903,21 +943,13 @@ GEM
omniauth-oauth2 (>= 1.5)
omniauth-azure-activedirectory-v2 (1.0.0)
omniauth-oauth2 (~> 1.7)
- omniauth-azure-oauth2 (0.0.10)
- jwt (>= 1.0, < 3.0)
- omniauth (~> 1.0)
- omniauth-oauth2 (~> 1.4)
omniauth-dingtalk-oauth2 (1.0.1)
omniauth-oauth2 (~> 1.7)
omniauth-facebook (4.0.0)
omniauth-oauth2 (~> 1.2)
- omniauth-github (1.4.0)
- omniauth (~> 1.5)
- omniauth-oauth2 (>= 1.4.0, < 2.0)
- omniauth-google-oauth2 (0.6.0)
- jwt (>= 2.0)
- omniauth (>= 1.1.1)
- omniauth-oauth2 (>= 1.5)
+ omniauth-github (2.0.0)
+ omniauth (~> 2.0)
+ omniauth-oauth2 (~> 1.7.1)
omniauth-oauth (1.2.0)
oauth
omniauth (>= 1.0, < 3)
@@ -926,12 +958,12 @@ GEM
omniauth (>= 1.9, < 3)
omniauth-oauth2-generic (0.2.2)
omniauth-oauth2 (~> 1.0)
- omniauth-salesforce (1.0.5)
- omniauth (~> 1.0)
- omniauth-oauth2 (~> 1.0)
- omniauth-saml (1.10.0)
- omniauth (~> 1.3, >= 1.3.2)
- ruby-saml (~> 1.7)
+ omniauth-rails_csrf_protection (1.0.1)
+ actionpack (>= 4.2)
+ omniauth (~> 2.0)
+ omniauth-saml (2.0.0)
+ omniauth (~> 2.0)
+ ruby-saml (~> 1.9)
omniauth-shibboleth (1.3.0)
omniauth (>= 1.0.0)
omniauth-twitter (1.4.0)
@@ -980,7 +1012,7 @@ GEM
rspec (>= 2.14)
term-ansicolor (~> 1.0)
parallel (1.22.1)
- parser (3.1.2.0)
+ parser (3.1.2.1)
ast (~> 2.4.1)
parslet (1.8.2)
pastel (0.8.0)
@@ -988,7 +1020,7 @@ GEM
peek (1.1.0)
railties (>= 4.0.0)
pg (1.4.1)
- pg_query (2.1.3)
+ pg_query (2.1.4)
google-protobuf (>= 3.19.2)
plist (3.6.0)
png_quantizator (0.2.1)
@@ -1019,13 +1051,13 @@ GEM
tty-markdown
tty-prompt
public_suffix (4.0.7)
- puma (5.6.4)
+ puma (5.6.5)
nio4r (~> 2.0)
puma_worker_killer (0.3.1)
get_process_mem (~> 0.2)
puma (>= 2.7)
pyu-ruby-sasl (0.0.3.3)
- raabro (1.1.6)
+ raabro (1.4.0)
racc (1.6.0)
rack (2.2.4)
rack-accept (0.4.5)
@@ -1040,6 +1072,8 @@ GEM
httpclient
json-jwt (>= 1.11.0)
rack (>= 2.1.0)
+ rack-protection (2.2.2)
+ rack
rack-proxy (0.7.2)
rack
rack-test (1.1.0)
@@ -1083,7 +1117,7 @@ GEM
randexp (0.1.7)
rash_alt (0.4.12)
hashie (>= 3.4)
- rb-fsevent (0.11.1)
+ rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
rbtrace (0.4.14)
@@ -1098,7 +1132,7 @@ GEM
json
recursive-open-struct (1.1.3)
redcarpet (3.5.1)
- redis (4.4.0)
+ redis (4.7.1)
redis-actionpack (5.3.0)
actionpack (>= 5, < 8)
redis-rack (>= 2.1.0, < 3)
@@ -1187,11 +1221,11 @@ GEM
rubocop-ast (>= 0.6.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 2.0)
- rubocop-ast (1.19.1)
+ rubocop-ast (1.21.0)
parser (>= 3.1.1.0)
rubocop-gitlab-security (0.1.1)
rubocop (>= 0.51)
- rubocop-graphql (0.14.3)
+ rubocop-graphql (0.14.6)
rubocop (>= 0.87, < 2)
rubocop-performance (1.9.2)
rubocop (>= 0.90.0, < 2.0)
@@ -1269,12 +1303,12 @@ GEM
shellany (0.0.1)
shoulda-matchers (5.1.0)
activesupport (>= 5.2.0)
- sidekiq (6.4.0)
+ sidekiq (6.4.2)
connection_pool (>= 2.2.2)
rack (~> 2.0)
redis (>= 4.2.0)
- sidekiq-cron (1.2.0)
- fugit (~> 1.1)
+ sidekiq-cron (1.4.0)
+ fugit (~> 1)
sidekiq (>= 4.2.1)
sigdump (0.2.4)
signet (0.17.0)
@@ -1296,7 +1330,7 @@ GEM
slack-messenger (2.3.4)
snowplow-tracker (0.6.1)
contracts (~> 0.7, <= 0.11)
- solargraph (0.45.0)
+ solargraph (0.46.0)
backport (~> 1.2)
benchmark
bundler (>= 1.17.2)
@@ -1314,7 +1348,7 @@ GEM
sorted_set (1.0.3)
rbtree
set (~> 1.0)
- spamcheck (0.1.0)
+ spamcheck (1.0.0)
grpc (~> 1.0)
spring (2.1.1)
spring-commands-rspec (1.0.4)
@@ -1330,7 +1364,7 @@ GEM
sqlite3 (1.4.2)
ssh_data (1.3.0)
ssrf_filter (1.0.7)
- stackprof (0.2.15)
+ stackprof (0.2.21)
state_machines (0.5.0)
state_machines-activemodel (0.8.0)
activemodel (>= 5.1)
@@ -1364,7 +1398,7 @@ GEM
faraday (~> 1.0)
text (1.3.1)
thor (1.2.1)
- thrift (0.14.0)
+ thrift (0.16.0)
tilt (2.0.10)
timecop (0.9.1)
timeliness (0.3.10)
@@ -1432,7 +1466,7 @@ GEM
validate_email (0.1.6)
activemodel (>= 3.0)
mail (>= 2.2.5)
- validate_url (1.0.13)
+ validate_url (1.0.15)
activemodel (>= 3.0.0)
public_suffix
validates_hostname (1.0.11)
@@ -1440,12 +1474,13 @@ GEM
activesupport (>= 3.0)
version_gem (1.0.0)
version_sorter (2.2.4)
- view_component (2.61.0)
+ view_component (2.71.0)
activesupport (>= 5.0.0, < 8.0)
+ concurrent-ruby (~> 1.0)
method_source (~> 1.0)
vmstat (2.3.0)
- warden (1.2.8)
- rack (>= 2.0.6)
+ warden (1.2.9)
+ rack (>= 2.0.9)
warning (1.3.0)
webauthn (2.3.0)
android_key_attestation (~> 0.3.0)
@@ -1495,10 +1530,10 @@ DEPENDENCIES
akismet (~> 3.0)
apollo_upload_server (~> 2.1.0)
asana (~> 0.10.13)
- asciidoctor (~> 2.0.10)
+ asciidoctor (~> 2.0.17)
asciidoctor-include-ext (~> 0.4.0)
asciidoctor-kroki (~> 0.5.0)
- asciidoctor-plantuml (~> 0.0.12)
+ asciidoctor-plantuml (~> 0.0.16)
atlassian-jwt (~> 0.2.0)
attr_encrypted (~> 3.1.0)
autoprefixer-rails (= 10.2.5.1)
@@ -1512,12 +1547,13 @@ DEPENDENCIES
bcrypt (~> 3.1, >= 3.1.14)
benchmark-ips (~> 2.3.0)
benchmark-memory (~> 0.1)
- better_errors (~> 2.9.0)
+ better_errors (~> 2.9.1)
bootsnap (~> 1.13.0)
bootstrap_form (~> 4.2.0)
browser (~> 4.2)
bullet (~> 7.0.2)
bundler-audit (~> 0.7.0.1)
+ bundler-checksum (~> 0.1.0)!
capybara (~> 3.35.3)
capybara-screenshot (~> 1.0.22)
carrierwave (~> 1.3)
@@ -1535,11 +1571,11 @@ DEPENDENCIES
deprecation_toolkit (~> 1.5.1)
derailed_benchmarks
device_detector
- devise (~> 4.7.2)
+ devise (~> 4.8.1)
devise-pbkdf2-encryptable (~> 0.0.0)!
devise-two-factor (~> 4.0.2)
diff_match_patch (~> 0.1.0)
- diffy (~> 3.3)
+ diffy (~> 3.4)
discordrb-webhooks (~> 3.4)
doorkeeper (~> 5.5.0.rc2)
doorkeeper-openid_connect (~> 1.7.5)
@@ -1573,10 +1609,10 @@ DEPENDENCIES
gettext (~> 3.3)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly (~> 15.3.0.pre.rc3)
+ gitaly (~> 15.4.0.pre.rc2)
github-markup (~> 1.7.0)
gitlab-chronic (~> 0.10.5)
- gitlab-dangerfiles (~> 3.5.0)
+ gitlab-dangerfiles (~> 3.5.2)
gitlab-experiment (~> 0.7.1)
gitlab-fog-azure-rm (~> 1.3.0)
gitlab-labkit (~> 0.24.0)
@@ -1584,9 +1620,9 @@ DEPENDENCIES
gitlab-mail_room (~> 0.0.9)
gitlab-markup (~> 1.8.0)
gitlab-net-dns (~> 0.9.1)
- gitlab-omniauth-openid-connect (~> 0.9.0)
+ gitlab-omniauth-openid-connect (~> 0.10.0)
gitlab-sidekiq-fetcher (= 0.8.0)
- gitlab-styles (~> 7.1.0)
+ gitlab-styles (~> 8.0.0)
gitlab_chronic_duration (~> 0.10.6.2)
gitlab_omniauth-ldap (~> 2.2.0)
gon (~> 6.4.0)
@@ -1605,7 +1641,7 @@ DEPENDENCIES
grpc (~> 1.42.0)
gssapi
guard-rspec
- haml_lint (~> 0.36.0)
+ haml_lint (~> 0.40.0)
hamlit (~> 2.15.0)
hangouts-chat (~> 0.0.5)
hashie
@@ -1628,46 +1664,47 @@ DEPENDENCIES
knapsack (~> 1.21.1)
kramdown (~> 2.3.1)
kubeclient (~> 4.9.3)
- lefthook (~> 1.1.0)
+ lefthook (~> 1.1.1)
letter_opener_web (~> 2.0.0)
license_finder (~> 7.0)
- licensee (~> 9.14.1)
+ licensee (~> 9.15)
lockbox (~> 0.6.2)
lograge (~> 0.5)
loofah (~> 2.18.0)
- lookbook
+ lookbook (~> 1.0)
lru_redux
mail (= 2.7.1)
mail-smtp_pool (~> 0.1.0)!
marginalia (~> 1.10.0)
memory_profiler (~> 0.9)
- method_source (~> 1.0)
+ microsoft_graph_mailer (~> 0.1.0)!
mini_magick (~> 4.10.1)
minitest (~> 5.11.0)
multi_json (~> 1.14.1)
net-ldap (~> 0.16.3)
net-ntp
- nokogiri (~> 1.13.0)
+ nokogiri (~> 1.13.8)
oauth2 (~> 2.0)
octokit (~> 4.15)
ohai (~> 16.10)
oj (~> 3.13.21)
- omniauth (~> 1.8)
+ omniauth (~> 2.1.0)
omniauth-alicloud (~> 1.0.1)
omniauth-atlassian-oauth2 (~> 0.2.0)
omniauth-auth0 (~> 2.0.0)
omniauth-authentiq (~> 0.3.3)
omniauth-azure-activedirectory-v2 (~> 1.0)
- omniauth-azure-oauth2 (~> 0.0.9)
+ omniauth-azure-oauth2 (~> 0.0.9)!
omniauth-cas3 (~> 1.1.4)!
omniauth-dingtalk-oauth2 (~> 1.0)
omniauth-facebook (~> 4.0.0)
- omniauth-github (~> 1.4)
+ omniauth-github (= 2.0.0)
omniauth-gitlab (~> 4.0.0)!
- omniauth-google-oauth2 (~> 0.6.0)
+ omniauth-google-oauth2 (~> 1.0.1)!
omniauth-oauth2-generic (~> 0.2.2)
- omniauth-salesforce (~> 1.0.5)
- omniauth-saml (~> 1.10)
+ omniauth-rails_csrf_protection
+ omniauth-salesforce (~> 1.0.5)!
+ omniauth-saml (~> 2.0.0)
omniauth-shibboleth (~> 1.3.0)
omniauth-twitter (~> 1.4)
omniauth_crowd (~> 2.4.0)!
@@ -1684,7 +1721,7 @@ DEPENDENCIES
pry-byebug
pry-rails (~> 0.3.9)
pry-shell (~> 0.5.1)
- puma (~> 5.6.4)
+ puma (~> 5.6.5)
puma_worker_killer (~> 0.3.1)
rack (~> 2.2.4)
rack-attack (~> 6.6.0)
@@ -1700,7 +1737,7 @@ DEPENDENCIES
rdoc (~> 6.3.2)
re2 (~> 1.4.0)
recaptcha (~> 4.11)
- redis (~> 4.4.0)
+ redis (~> 4.7.0)
redis-actionpack (~> 5.3.0)
redis-namespace (~> 1.8.1)
request_store (~> 1.5)
@@ -1735,8 +1772,8 @@ DEPENDENCIES
sentry-sidekiq (~> 5.1.1)
settingslogic (~> 2.0.9)
shoulda-matchers (~> 5.1.0)
- sidekiq (~> 6.4)
- sidekiq-cron (~> 1.2)
+ sidekiq (~> 6.4.0)
+ sidekiq-cron (~> 1.4.0)
sigdump (~> 0.2.4)
simple_po_parser (~> 1.1.6)
simplecov (~> 0.21)
@@ -1744,21 +1781,21 @@ DEPENDENCIES
simplecov-lcov (~> 0.8.0)
slack-messenger (~> 2.3.4)
snowplow-tracker (~> 0.6.1)
- solargraph (~> 0.45.0)
- spamcheck (~> 0.1.0)
+ solargraph (~> 0.46.0)
+ spamcheck (~> 1.0.0)
spring (~> 2.1.0)
spring-commands-rspec (~> 1.0.4)
sprite-factory (~> 1.7)
sprockets (~> 3.7.0)
ssh_data (~> 1.3)
- stackprof (~> 0.2.15)
+ stackprof (~> 0.2.21)
state_machines-activerecord (~> 0.8.0)
sys-filesystem (~> 1.4.3)
tanuki_emoji (~> 0.6)
terser (= 1.0.2)
test-prof (~> 1.0.7)
test_file_finder (~> 0.1.3)
- thrift (>= 0.14.0)
+ thrift (>= 0.16.0)
timecop (~> 0.9.1)
timfel-krb5-auth (~> 0.8)
toml-rb (~> 2.0)
@@ -1771,7 +1808,7 @@ DEPENDENCIES
valid_email (~> 0.1)
validates_hostname (~> 1.0.11)
version_sorter (~> 2.2.4)
- view_component (~> 2.61)
+ view_component (~> 2.71.0)
vmstat (~> 2.3.0)
warning (~> 1.3.0)
webauthn (~> 2.3)
diff --git a/app/assets/javascripts/access_tokens/components/access_token_table_app.vue b/app/assets/javascripts/access_tokens/components/access_token_table_app.vue
index 59f0e0dd17d..461b2dad479 100644
--- a/app/assets/javascripts/access_tokens/components/access_token_table_app.vue
+++ b/app/assets/javascripts/access_tokens/components/access_token_table_app.vue
@@ -140,6 +140,7 @@ export default {
<template #cell(action)="{ item: { revokePath } }">
<gl-button
+ v-if="revokePath"
category="tertiary"
:aria-label="$options.i18n.revokeButton"
:data-confirm="modalMessage"
diff --git a/app/assets/javascripts/access_tokens/components/constants.js b/app/assets/javascripts/access_tokens/components/constants.js
index 84e50bc099f..9cd7cb5bb3a 100644
--- a/app/assets/javascripts/access_tokens/components/constants.js
+++ b/app/assets/javascripts/access_tokens/components/constants.js
@@ -12,8 +12,6 @@ export const FIELDS = [
key: 'name',
label: __('Token name'),
sortable: true,
- tdClass: `gl-text-black-normal`,
- thClass: `gl-text-black-normal`,
},
{
formatter(scopes) {
@@ -22,40 +20,30 @@ export const FIELDS = [
key: 'scopes',
label: __('Scopes'),
sortable: true,
- tdClass: `gl-text-black-normal`,
- thClass: `gl-text-black-normal`,
},
{
key: 'createdAt',
label: s__('AccessTokens|Created'),
sortable: true,
- tdClass: `gl-text-black-normal`,
- thClass: `gl-text-black-normal`,
},
{
key: 'lastUsedAt',
label: __('Last Used'),
sortable: true,
- tdClass: `gl-text-black-normal`,
- thClass: `gl-text-black-normal`,
},
{
key: 'expiresAt',
label: __('Expires'),
sortable: true,
- tdClass: `gl-text-black-normal`,
- thClass: `gl-text-black-normal`,
},
{
key: 'role',
label: __('Role'),
- tdClass: `gl-text-black-normal`,
- thClass: `gl-text-black-normal`,
sortable: true,
},
{
key: 'action',
label: __('Action'),
- thClass: `gl-text-black-normal`,
+ tdClass: 'gl-py-3!',
},
];
diff --git a/app/assets/javascripts/access_tokens/components/expires_at_field.vue b/app/assets/javascripts/access_tokens/components/expires_at_field.vue
index 5516fd0daf6..38501d63d3a 100644
--- a/app/assets/javascripts/access_tokens/components/expires_at_field.vue
+++ b/app/assets/javascripts/access_tokens/components/expires_at_field.vue
@@ -16,6 +16,16 @@ export default {
import('ee_component/access_tokens/components/max_expiration_date_message.vue'),
},
props: {
+ defaultDateOffset: {
+ type: Number,
+ required: false,
+ default: 30,
+ },
+ description: {
+ type: String,
+ required: false,
+ default: null,
+ },
inputAttrs: {
type: Object,
required: false,
@@ -33,9 +43,15 @@ export default {
},
},
computed: {
- in30Days() {
- const today = new Date();
- return getDateInFuture(today, 30);
+ defaultDate() {
+ const defaultDate = getDateInFuture(new Date(), this.defaultDateOffset);
+ // The maximum date can be set by admins. If the maximum date is sooner
+ // than the default expiration date we use the maximum date as default
+ // expiration date.
+ if (this.maxDate && this.maxDate < defaultDate) {
+ return this.maxDate;
+ }
+ return defaultDate;
},
},
};
@@ -47,7 +63,7 @@ export default {
:target="null"
:min-date="minDate"
:max-date="maxDate"
- :default-date="in30Days"
+ :default-date="defaultDate"
show-clear-button
:input-name="inputAttrs.name"
:input-id="inputAttrs.id"
@@ -55,7 +71,10 @@ export default {
data-qa-selector="expiry_date_field"
/>
<template #description>
- <max-expiration-date-message :max-date="maxDate" />
+ <template v-if="description">
+ {{ description }}
+ </template>
+ <max-expiration-date-message v-else :max-date="maxDate" />
</template>
</gl-form-group>
</template>
diff --git a/app/assets/javascripts/access_tokens/components/new_access_token_app.vue b/app/assets/javascripts/access_tokens/components/new_access_token_app.vue
index e111ae91e5c..6b52bd84656 100644
--- a/app/assets/javascripts/access_tokens/components/new_access_token_app.vue
+++ b/app/assets/javascripts/access_tokens/components/new_access_token_app.vue
@@ -42,7 +42,6 @@ export default {
formInputGroupProps() {
return {
id: this.$options.tokenInputId,
- class: 'qa-created-access-token',
'data-qa-selector': 'created_access_token_field',
name: this.$options.tokenInputId,
};
@@ -82,7 +81,14 @@ export default {
this.infoAlert = createAlert({ message: this.alertInfoMessage, variant: VARIANT_INFO });
- this.form.reset();
+ // Selectively reset all input fields except for the date picker and submit.
+ // The form token creation is not controlled by Vue.
+ this.form.querySelectorAll('input[type=text]:not([id$=expires_at])').forEach((el) => {
+ el.value = '';
+ });
+ this.form.querySelectorAll('input[type=checkbox]').forEach((el) => {
+ el.checked = false;
+ });
},
},
};
diff --git a/app/assets/javascripts/access_tokens/index.js b/app/assets/javascripts/access_tokens/index.js
index 9801aa08e28..f0c1b415157 100644
--- a/app/assets/javascripts/access_tokens/index.js
+++ b/app/assets/javascripts/access_tokens/index.js
@@ -61,7 +61,7 @@ export const initExpiresAtField = () => {
}
const { expiresAt: inputAttrs } = parseRailsFormFields(el);
- const { minDate, maxDate } = el.dataset;
+ const { minDate, maxDate, defaultDateOffset, description } = el.dataset;
return new Vue({
el,
@@ -71,6 +71,8 @@ export const initExpiresAtField = () => {
inputAttrs,
minDate: minDate ? new Date(minDate) : undefined,
maxDate: maxDate ? new Date(maxDate) : undefined,
+ defaultDateOffset: defaultDateOffset ? Number(defaultDateOffset) : undefined,
+ description,
},
});
},
diff --git a/app/assets/javascripts/admin/application_settings/runner_token_expiration/components/expiration_interval_description.vue b/app/assets/javascripts/admin/application_settings/runner_token_expiration/components/expiration_interval_description.vue
new file mode 100644
index 00000000000..2f74b44625f
--- /dev/null
+++ b/app/assets/javascripts/admin/application_settings/runner_token_expiration/components/expiration_interval_description.vue
@@ -0,0 +1,52 @@
+<script>
+import { GlLink, GlSprintf } from '@gitlab/ui';
+import { helpPagePath } from '~/helpers/help_page_helper';
+import { s__ } from '~/locale';
+
+export default {
+ components: {
+ GlLink,
+ GlSprintf,
+ },
+ props: {
+ message: {
+ type: String,
+ required: true,
+ },
+ },
+ i18n: {
+ fieldHelpText: s__(
+ 'AdminSettings|If no unit is written, it defaults to seconds. For example, these are all equivalent: %{oneDayInSeconds}, %{oneDayInHoursHumanReadable}, or %{oneDayHumanReadable}. Minimum value is two hours. %{linkStart}Learn more.%{linkEnd}',
+ ),
+ },
+ computed: {
+ helpUrl() {
+ return helpPagePath('ci/runners/configure_runners', {
+ anchor: 'authentication-token-security',
+ });
+ },
+ },
+};
+</script>
+<template>
+ <p>
+ {{ message }}
+ <gl-sprintf :message="$options.i18n.fieldHelpText">
+ <template #oneDayInSeconds>
+ <!-- eslint-disable-next-line @gitlab/vue-require-i18n-strings -->
+ <code>86400</code>
+ </template>
+ <template #oneDayInHoursHumanReadable>
+ <!-- eslint-disable-next-line @gitlab/vue-require-i18n-strings -->
+ <code>24 hours</code>
+ </template>
+ <template #oneDayHumanReadable>
+ <!-- eslint-disable-next-line @gitlab/vue-require-i18n-strings -->
+ <code>1 day</code>
+ </template>
+ <template #link>
+ <gl-link :href="helpUrl" target="_blank">{{ __('Learn more.') }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </p>
+</template>
diff --git a/app/assets/javascripts/admin/application_settings/runner_token_expiration/components/expiration_intervals.vue b/app/assets/javascripts/admin/application_settings/runner_token_expiration/components/expiration_intervals.vue
new file mode 100644
index 00000000000..371a26d2664
--- /dev/null
+++ b/app/assets/javascripts/admin/application_settings/runner_token_expiration/components/expiration_intervals.vue
@@ -0,0 +1,123 @@
+<script>
+import { GlFormGroup } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import ChronicDurationInput from '~/vue_shared/components/chronic_duration_input.vue';
+import ExpirationIntervalDescription from './expiration_interval_description.vue';
+
+export default {
+ components: {
+ ChronicDurationInput,
+ ExpirationIntervalDescription,
+ GlFormGroup,
+ },
+ props: {
+ instanceRunnerExpirationInterval: {
+ type: Number,
+ required: false,
+ default: null,
+ },
+ groupRunnerExpirationInterval: {
+ type: Number,
+ required: false,
+ default: null,
+ },
+ projectRunnerExpirationInterval: {
+ type: Number,
+ required: false,
+ default: null,
+ },
+ },
+ data() {
+ return {
+ perInput: {
+ instance: {
+ value: this.instanceRunnerExpirationInterval,
+ valid: null,
+ feedback: '',
+ },
+ group: {
+ value: this.groupRunnerExpirationInterval,
+ valid: null,
+ feedback: '',
+ },
+ project: {
+ value: this.projectRunnerExpirationInterval,
+ valid: null,
+ feedback: '',
+ },
+ },
+ };
+ },
+ methods: {
+ updateValidity(obj, event) {
+ /* eslint-disable no-param-reassign */
+ obj.valid = event.valid;
+ obj.feedback = event.feedback;
+ /* eslint-enable no-param-reassign */
+ },
+ },
+ i18n: {
+ instanceRunnerTitle: s__('AdminSettings|Instance runners expiration'),
+ instanceRunnerDescription: s__(
+ 'AdminSettings|Set the expiration time of authentication tokens of newly registered instance runners. Authentication tokens are automatically reset at these intervals.',
+ ),
+ groupRunnerTitle: s__('AdminSettings|Group runners expiration'),
+ groupRunnerDescription: s__(
+ 'AdminSettings|Set the expiration time of authentication tokens of newly registered group runners.',
+ ),
+ projectRunnerTitle: s__('AdminSettings|Project runners expiration'),
+ projectRunnerDescription: s__(
+ 'AdminSettings|Set the expiration time of authentication tokens of newly registered project runners.',
+ ),
+ },
+};
+</script>
+<template>
+ <div>
+ <gl-form-group
+ :label="$options.i18n.instanceRunnerTitle"
+ :invalid-feedback="perInput.instance.feedback"
+ :state="perInput.instance.valid"
+ >
+ <template #description>
+ <expiration-interval-description :message="$options.i18n.instanceRunnerDescription" />
+ </template>
+ <chronic-duration-input
+ v-model="perInput.instance.value"
+ name="application_setting[runner_token_expiration_interval]"
+ :state="perInput.instance.valid"
+ @valid="updateValidity(perInput.instance, $event)"
+ />
+ </gl-form-group>
+ <gl-form-group
+ :label="$options.i18n.groupRunnerTitle"
+ :invalid-feedback="perInput.group.feedback"
+ :state="perInput.group.valid"
+ >
+ <template #description>
+ <expiration-interval-description :message="$options.i18n.groupRunnerDescription" />
+ </template>
+ <chronic-duration-input
+ v-model="perInput.group.value"
+ name="application_setting[group_runner_token_expiration_interval]"
+ :state="perInput.group.valid"
+ @valid="updateValidity(perInput.group, $event)"
+ />
+ </gl-form-group>
+ <gl-form-group
+ :label="$options.i18n.projectRunnerTitle"
+ :invalid-feedback="perInput.project.feedback"
+ :state="perInput.project.valid"
+ >
+ <template #description>
+ <expiration-interval-description :message="$options.i18n.projectRunnerDescription" />
+ </template>
+ <chronic-duration-input
+ v-model="perInput.project.value"
+ name="application_setting[project_runner_token_expiration_interval]"
+ :state="perInput.project.valid"
+ @valid="updateValidity(perInput.project, $event)"
+ />
+ </gl-form-group>
+ </div>
+</template>
diff --git a/app/assets/javascripts/admin/application_settings/runner_token_expiration/index.js b/app/assets/javascripts/admin/application_settings/runner_token_expiration/index.js
new file mode 100644
index 00000000000..79d7ff0451a
--- /dev/null
+++ b/app/assets/javascripts/admin/application_settings/runner_token_expiration/index.js
@@ -0,0 +1,32 @@
+import Vue from 'vue';
+import { parseInterval } from '~/runner/utils';
+import ExpirationIntervals from './components/expiration_intervals.vue';
+
+const initRunnerTokenExpirationIntervals = (selector = '#js-runner-token-expiration-intervals') => {
+ const el = document.querySelector(selector);
+
+ if (!el) {
+ return null;
+ }
+
+ const {
+ instanceRunnerTokenExpirationInterval,
+ groupRunnerTokenExpirationInterval,
+ projectRunnerTokenExpirationInterval,
+ } = el.dataset;
+
+ return new Vue({
+ el,
+ render(h) {
+ return h(ExpirationIntervals, {
+ props: {
+ instanceRunnerExpirationInterval: parseInterval(instanceRunnerTokenExpirationInterval),
+ groupRunnerExpirationInterval: parseInterval(groupRunnerTokenExpirationInterval),
+ projectRunnerExpirationInterval: parseInterval(projectRunnerTokenExpirationInterval),
+ },
+ });
+ },
+ });
+};
+
+export default initRunnerTokenExpirationIntervals;
diff --git a/app/assets/javascripts/admin/topics/components/merge_topics.vue b/app/assets/javascripts/admin/topics/components/merge_topics.vue
new file mode 100644
index 00000000000..921b762bbef
--- /dev/null
+++ b/app/assets/javascripts/admin/topics/components/merge_topics.vue
@@ -0,0 +1,141 @@
+<script>
+import { GlAlert, GlButton, GlModal, GlModalDirective, GlSprintf } from '@gitlab/ui';
+import { __, s__ } from '~/locale';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import csrf from '~/lib/utils/csrf';
+import TopicSelect from './topic_select.vue';
+
+export default {
+ components: {
+ GlAlert,
+ GlButton,
+ GlModal,
+ GlSprintf,
+ TopicSelect,
+ },
+ directives: {
+ GlModal: GlModalDirective,
+ },
+ inject: ['path'],
+ data() {
+ return {
+ sourceTopic: {},
+ targetTopic: {},
+ };
+ },
+ computed: {
+ sourceTopicId() {
+ return getIdFromGraphQLId(this.sourceTopic?.id);
+ },
+ targetTopicId() {
+ return getIdFromGraphQLId(this.targetTopic?.id);
+ },
+ validSelectedTopics() {
+ return (
+ Object.keys(this.sourceTopic).length &&
+ Object.keys(this.targetTopic).length &&
+ this.sourceTopic !== this.targetTopic
+ );
+ },
+ actionPrimary() {
+ return {
+ text: __('Merge'),
+ attributes: {
+ variant: 'danger',
+ disabled: !this.validSelectedTopics,
+ },
+ };
+ },
+ },
+ methods: {
+ selectSourceTopic(topic) {
+ this.sourceTopic = topic;
+ },
+ selectTargetTopic(topic) {
+ this.targetTopic = topic;
+ },
+ mergeTopics() {
+ this.$refs.mergeForm.submit();
+ },
+ },
+ i18n: {
+ title: s__('MergeTopics|Merge topics'),
+ body: s__(
+ 'MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic.',
+ ),
+ sourceTopic: s__('MergeTopics|Source topic'),
+ targetTopic: s__('MergeTopics|Target topic'),
+ warningTitle: s__('MergeTopics|Merging topics will cause the following:'),
+ warningBody: s__('MergeTopics|This action cannot be undone.'),
+ warningRemoveTopic: s__('MergeTopics|%{sourceTopic} will be removed'),
+ warningMoveProjects: s__('MergeTopics|All assigned projects will be moved to %{targetTopic}'),
+ },
+ modal: {
+ id: 'merge-topics',
+ actionSecondary: {
+ text: __('Cancel'),
+ attributes: {
+ variant: 'default',
+ },
+ },
+ },
+ csrf,
+};
+</script>
+<template>
+ <div class="gl-mr-3">
+ <gl-button v-gl-modal="$options.modal.id" category="secondary">{{
+ $options.i18n.title
+ }}</gl-button>
+ <gl-modal
+ :title="$options.i18n.title"
+ :action-primary="actionPrimary"
+ :action-secondary="$options.modal.actionSecondary"
+ :modal-id="$options.modal.id"
+ size="sm"
+ @primary="mergeTopics"
+ >
+ <p>{{ $options.i18n.body }}</p>
+ <topic-select
+ :selected-topic="sourceTopic"
+ :label-text="$options.i18n.sourceTopic"
+ @click="selectSourceTopic"
+ />
+ <topic-select
+ :selected-topic="targetTopic"
+ :label-text="$options.i18n.targetTopic"
+ @click="selectTargetTopic"
+ />
+ <gl-alert
+ v-if="validSelectedTopics"
+ :title="$options.i18n.warningTitle"
+ :dismissible="false"
+ variant="danger"
+ >
+ <ul>
+ <li>
+ <gl-sprintf :message="$options.i18n.warningRemoveTopic">
+ <template #sourceTopic>
+ <strong>{{ sourceTopic.name }}</strong>
+ </template>
+ </gl-sprintf>
+ </li>
+ <li>
+ <gl-sprintf :message="$options.i18n.warningMoveProjects">
+ <template #targetTopic>
+ <strong>{{ targetTopic.name }}</strong>
+ </template>
+ </gl-sprintf>
+ </li>
+ </ul>
+ {{ $options.i18n.warningBody }}
+ </gl-alert>
+ <form ref="mergeForm" method="post" :action="path">
+ <input type="hidden" name="_method" value="post" />
+ <input type="hidden" name="authenticity_token" :value="$options.csrf.token" />
+ <input type="hidden" name="source_topic_id" :value="sourceTopicId" />
+ <input type="hidden" name="target_topic_id" :value="targetTopicId" />
+ </form>
+ </gl-modal>
+ </div>
+</template>
diff --git a/app/assets/javascripts/admin/topics/components/topic_select.vue b/app/assets/javascripts/admin/topics/components/topic_select.vue
new file mode 100644
index 00000000000..8bf5be1afd1
--- /dev/null
+++ b/app/assets/javascripts/admin/topics/components/topic_select.vue
@@ -0,0 +1,106 @@
+<script>
+import {
+ GlAvatarLabeled,
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownText,
+ GlSearchBoxByType,
+} from '@gitlab/ui';
+import { s__ } from '~/locale';
+import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
+import searchProjectTopics from '~/graphql_shared/queries/project_topics_search.query.graphql';
+
+export default {
+ components: {
+ GlAvatarLabeled,
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownText,
+ GlSearchBoxByType,
+ },
+ props: {
+ selectedTopic: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ labelText: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+ apollo: {
+ topics: {
+ query: searchProjectTopics,
+ variables() {
+ return {
+ search: this.search,
+ };
+ },
+ update(data) {
+ return data.topics?.nodes || [];
+ },
+ debounce: 250,
+ },
+ },
+ data() {
+ return {
+ topics: [],
+ search: '',
+ };
+ },
+ computed: {
+ loading() {
+ return this.$apollo.queries.topics.loading;
+ },
+ isResultEmpty() {
+ return this.topics.length === 0;
+ },
+ dropdownText() {
+ if (Object.keys(this.selectedTopic).length) {
+ return this.selectedTopic.name;
+ }
+
+ return this.$options.i18n.dropdownText;
+ },
+ },
+ methods: {
+ selectTopic(topic) {
+ this.$emit('click', topic);
+ },
+ },
+ i18n: {
+ dropdownText: s__('TopicSelect|Select a topic'),
+ searchPlaceholder: s__('TopicSelect|Search topics'),
+ emptySearchResult: s__('TopicSelect|No matching results'),
+ },
+ AVATAR_SHAPE_OPTION_RECT,
+};
+</script>
+
+<template>
+ <div>
+ <label v-if="labelText">{{ labelText }}</label>
+ <gl-dropdown block :text="dropdownText">
+ <gl-search-box-by-type
+ v-model="search"
+ :is-loading="loading"
+ :placeholder="$options.i18n.searchPlaceholder"
+ />
+ <gl-dropdown-item v-for="topic in topics" :key="topic.id" @click="selectTopic(topic)">
+ <gl-avatar-labeled
+ :label="topic.title"
+ :sub-label="topic.name"
+ :src="topic.avatarUrl"
+ :entity-name="topic.name"
+ :size="32"
+ :shape="$options.AVATAR_SHAPE_OPTION_RECT"
+ />
+ </gl-dropdown-item>
+ <gl-dropdown-text v-if="isResultEmpty && !loading">
+ <span class="gl-text-gray-500">{{ $options.i18n.emptySearchResult }}</span>
+ </gl-dropdown-text>
+ </gl-dropdown>
+ </div>
+</template>
diff --git a/app/assets/javascripts/admin/topics/index.js b/app/assets/javascripts/admin/topics/index.js
index 09e9b20f220..d81690e8f4c 100644
--- a/app/assets/javascripts/admin/topics/index.js
+++ b/app/assets/javascripts/admin/topics/index.js
@@ -1,7 +1,20 @@
import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
+import showToast from '~/vue_shared/plugins/global_toast';
import RemoveAvatar from './components/remove_avatar.vue';
+import MergeTopics from './components/merge_topics.vue';
-export default () => {
+const toasts = document.querySelectorAll('.js-toast-message');
+toasts.forEach((toast) => showToast(toast.dataset.message));
+
+Vue.use(VueApollo);
+
+const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient(),
+});
+
+export const initRemoveAvatar = () => {
const el = document.querySelector('.js-remove-topic-avatar');
if (!el) {
@@ -21,3 +34,20 @@ export default () => {
},
});
};
+
+export const initMergeTopics = () => {
+ const el = document.querySelector('.js-merge-topics');
+
+ if (!el) return false;
+
+ const { path } = el.dataset;
+
+ return new Vue({
+ el,
+ apolloProvider,
+ provide: { path },
+ render(createElement) {
+ return createElement(MergeTopics);
+ },
+ });
+};
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_form.vue b/app/assets/javascripts/alerts_settings/components/alerts_form.vue
index 696e7f359d1..388d925196b 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_form.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_form.vue
@@ -109,7 +109,7 @@ export default {
v-for="template in templates"
:key="template.key"
data-qa-selector="incident_templates_item"
- :is-check-item="true"
+ is-check-item
:is-checked="isTemplateSelected(template.key)"
@click="selectIssueTemplate(template.key)"
>
diff --git a/app/assets/javascripts/analytics/shared/components/daterange.vue b/app/assets/javascripts/analytics/shared/components/daterange.vue
index 7df66d1b2be..92ccac59057 100644
--- a/app/assets/javascripts/analytics/shared/components/daterange.vue
+++ b/app/assets/javascripts/analytics/shared/components/daterange.vue
@@ -1,13 +1,10 @@
<script>
-import { GlDaterangePicker, GlSprintf } from '@gitlab/ui';
-import { getDayDifference } from '~/lib/utils/datetime_utility';
-import { __, sprintf } from '~/locale';
-import { OFFSET_DATE_BY_ONE } from '../constants';
+import { GlDaterangePicker } from '@gitlab/ui';
+import { n__, __, sprintf } from '~/locale';
export default {
components: {
GlDaterangePicker,
- GlSprintf,
},
props: {
show: {
@@ -69,9 +66,10 @@ export default {
this.$emit('change', { startDate, endDate });
},
},
- numberOfDays() {
- const dayDifference = getDayDifference(this.startDate, this.endDate);
- return this.includeSelectedDate ? dayDifference + OFFSET_DATE_BY_ONE : dayDifference;
+ },
+ methods: {
+ numberOfDays(daysSelected) {
+ return n__('1 day selected', '%d days selected', daysSelected);
},
},
};
@@ -83,7 +81,7 @@ export default {
>
<gl-daterange-picker
v-model="dateRange"
- class="d-flex flex-column flex-lg-row"
+ class="gl-display-flex gl-flex-direction-column gl-lg-flex-direction-row"
:default-start-date="startDate"
:default-end-date="endDate"
:default-min-date="minDate"
@@ -93,12 +91,12 @@ export default {
:tooltip="maxDateRangeTooltip"
theme="animate-picker"
start-picker-class="js-daterange-picker-from gl-display-flex gl-flex-direction-column gl-lg-flex-direction-row gl-lg-align-items-center gl-lg-mr-3 gl-mb-2 gl-lg-mb-0"
- end-picker-class="js-daterange-picker-to d-flex flex-column flex-lg-row align-items-lg-center gl-mb-2 gl-lg-mb-0"
+ end-picker-class="js-daterange-picker-to gl-display-flex gl-flex-direction-column gl-lg-flex-direction-row gl-lg-align-items-center gl-mb-2 gl-lg-mb-0"
label-class="gl-mb-2 gl-lg-mb-0"
>
- <gl-sprintf :message="n__('1 day selected', '%d days selected', numberOfDays)">
- <template #numberOfDays>{{ numberOfDays }}</template>
- </gl-sprintf>
+ <template #default="{ daysSelected }">
+ {{ numberOfDays(daysSelected) }}
+ </template>
</gl-daterange-picker>
</div>
</template>
diff --git a/app/assets/javascripts/analytics/shared/constants.js b/app/assets/javascripts/analytics/shared/constants.js
index e1bc59b36ef..c62736d55a8 100644
--- a/app/assets/javascripts/analytics/shared/constants.js
+++ b/app/assets/javascripts/analytics/shared/constants.js
@@ -1,8 +1,7 @@
-import { masks } from 'dateformat';
+import { masks } from '~/lib/dateformat';
import { s__ } from '~/locale';
export const DATE_RANGE_LIMIT = 180;
-export const OFFSET_DATE_BY_ONE = 1;
export const PROJECTS_PER_PAGE = 50;
const { isoDate, mediumDate } = masks;
diff --git a/app/assets/javascripts/analytics/shared/utils.js b/app/assets/javascripts/analytics/shared/utils.js
index 1887f2affc3..bc52e38fc81 100644
--- a/app/assets/javascripts/analytics/shared/utils.js
+++ b/app/assets/javascripts/analytics/shared/utils.js
@@ -1,5 +1,5 @@
-import dateFormat from 'dateformat';
import { hideFlash } from '~/flash';
+import dateFormat from '~/lib/dateformat';
import { slugify } from '~/lib/utils/text_utility';
import { urlQueryToFilter } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
import { dateFormats } from './constants';
diff --git a/app/assets/javascripts/analytics/usage_trends/utils.js b/app/assets/javascripts/analytics/usage_trends/utils.js
index 91907877ed6..9474d264363 100644
--- a/app/assets/javascripts/analytics/usage_trends/utils.js
+++ b/app/assets/javascripts/analytics/usage_trends/utils.js
@@ -1,5 +1,5 @@
-import { masks } from 'dateformat';
import { get } from 'lodash';
+import { masks } from '~/lib/dateformat';
import { formatDate } from '~/lib/utils/datetime_utility';
const { isoDate } = masks;
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 0c870a89760..b02dd9321b3 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -47,6 +47,7 @@ const Api = {
projectSharePath: '/api/:version/projects/:id/share',
projectMilestonesPath: '/api/:version/projects/:id/milestones',
projectIssuePath: '/api/:version/projects/:id/issues/:issue_iid',
+ projectCreateIssuePath: '/api/:version/projects/:id/issues',
mergeRequestsPath: '/api/:version/merge_requests',
groupLabelsPath: '/api/:version/groups/:namespace_path/labels',
issuableTemplatePath: '/:namespace_path/:project_path/templates/:type/:key',
diff --git a/app/assets/javascripts/api/harbor_registry.js b/app/assets/javascripts/api/harbor_registry.js
new file mode 100644
index 00000000000..eb241342567
--- /dev/null
+++ b/app/assets/javascripts/api/harbor_registry.js
@@ -0,0 +1,49 @@
+import axios from '~/lib/utils/axios_utils';
+import { buildApiUrl } from '~/api/api_utils';
+
+// the :request_path is loading API-like resources, not part of our REST API.
+// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82784#note_1077703806
+const HARBOR_REPOSITORIES_PATH = '/:request_path.json';
+const HARBOR_ARTIFACTS_PATH = '/:request_path/:repo_name/artifacts.json';
+const HARBOR_TAGS_PATH = '/:request_path/:repo_name/artifacts/:digest/tags.json';
+
+export function getHarborRepositoriesList({ requestPath, limit, page, sort, search = '' }) {
+ const url = buildApiUrl(HARBOR_REPOSITORIES_PATH).replace('/:request_path', requestPath);
+
+ return axios.get(url, {
+ params: {
+ limit,
+ page,
+ search,
+ sort,
+ },
+ });
+}
+
+export function getHarborArtifacts({ requestPath, repoName, limit, page, sort, search = '' }) {
+ const url = buildApiUrl(HARBOR_ARTIFACTS_PATH)
+ .replace('/:request_path', requestPath)
+ .replace(':repo_name', repoName);
+
+ return axios.get(url, {
+ params: {
+ limit,
+ page,
+ search,
+ sort,
+ },
+ });
+}
+
+export function getHarborTags({ requestPath, repoName, digest, page }) {
+ const url = buildApiUrl(HARBOR_TAGS_PATH)
+ .replace('/:request_path', requestPath)
+ .replace(':repo_name', repoName)
+ .replace(':digest', digest);
+
+ return axios.get(url, {
+ params: {
+ page,
+ },
+ });
+}
diff --git a/app/assets/javascripts/api/integrations_api.js b/app/assets/javascripts/api/integrations_api.js
deleted file mode 100644
index 692aae21a4f..00000000000
--- a/app/assets/javascripts/api/integrations_api.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import axios from '../lib/utils/axios_utils';
-import { buildApiUrl } from './api_utils';
-
-const JIRA_CONNECT_SUBSCRIPTIONS_PATH = '/api/:version/integrations/jira_connect/subscriptions';
-
-export function addJiraConnectSubscription(namespacePath, { jwt, accessToken }) {
- const url = buildApiUrl(JIRA_CONNECT_SUBSCRIPTIONS_PATH);
-
- return axios.post(
- url,
- {
- jwt,
- namespace_path: namespacePath,
- },
- {
- headers: {
- Authorization: `Bearer ${accessToken}`, // eslint-disable-line @gitlab/require-i18n-strings
- },
- },
- );
-}
diff --git a/app/assets/javascripts/api/user_api.js b/app/assets/javascripts/api/user_api.js
index c362253f52e..c743b18d572 100644
--- a/app/assets/javascripts/api/user_api.js
+++ b/app/assets/javascripts/api/user_api.js
@@ -12,7 +12,6 @@ const USER_PROJECTS_PATH = '/api/:version/users/:id/projects';
const USER_POST_STATUS_PATH = '/api/:version/user/status';
const USER_FOLLOW_PATH = '/api/:version/users/:id/follow';
const USER_UNFOLLOW_PATH = '/api/:version/users/:id/unfollow';
-const CURRENT_USER_PATH = '/api/:version/user';
export function getUsers(query, options) {
const url = buildApiUrl(USERS_PATH);
@@ -82,8 +81,3 @@ export function unfollowUser(userId) {
const url = buildApiUrl(USER_UNFOLLOW_PATH).replace(':id', encodeURIComponent(userId));
return axios.post(url);
}
-
-export function getCurrentUser(options) {
- const url = buildApiUrl(CURRENT_USER_PATH);
- return axios.get(url, { ...options });
-}
diff --git a/app/assets/javascripts/autosave.js b/app/assets/javascripts/autosave.js
index 8381dcec9c3..5ab66acaf80 100644
--- a/app/assets/javascripts/autosave.js
+++ b/app/assets/javascripts/autosave.js
@@ -5,7 +5,7 @@ import AccessorUtilities from './lib/utils/accessor';
export default class Autosave {
constructor(field, key, fallbackKey, lockVersion) {
this.field = field;
-
+ this.type = this.field.prop('type');
this.isLocalStorageAvailable = AccessorUtilities.canUseLocalStorage();
if (key.join != null) {
key = key.join('/');
@@ -22,11 +22,12 @@ export default class Autosave {
restore() {
if (!this.isLocalStorageAvailable) return;
if (!this.field.length) return;
-
const text = window.localStorage.getItem(this.key);
const fallbackText = window.localStorage.getItem(this.fallbackKey);
- if (text) {
+ if (this.type === 'checkbox') {
+ this.field.prop('checked', text || fallbackText);
+ } else if (text) {
this.field.val(text);
} else if (fallbackText) {
this.field.val(fallbackText);
@@ -49,17 +50,16 @@ export default class Autosave {
save() {
if (!this.field.length) return;
+ const value = this.type === 'checkbox' ? this.field.is(':checked') : this.field.val();
- const text = this.field.val();
-
- if (this.isLocalStorageAvailable && text) {
+ if (this.isLocalStorageAvailable && value) {
if (this.fallbackKey) {
- window.localStorage.setItem(this.fallbackKey, text);
+ window.localStorage.setItem(this.fallbackKey, value);
}
if (this.lockVersion !== undefined) {
window.localStorage.setItem(this.lockVersionKey, this.lockVersion);
}
- return window.localStorage.setItem(this.key, text);
+ return window.localStorage.setItem(this.key, value);
}
return this.reset();
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index a030797c698..a3ffb4df7b7 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -165,6 +165,7 @@ export class AwardsHandler {
`;
const targetEl = this.targetContainerEl ? this.targetContainerEl : document.body;
+ // eslint-disable-next-line no-unsanitized/method
targetEl.insertAdjacentHTML('beforeend', emojiMenuMarkup);
this.addRemainingEmojiMenuCategories();
@@ -198,6 +199,7 @@ export class AwardsHandler {
emojisInCategory,
);
requestAnimationFrame(() => {
+ // eslint-disable-next-line no-unsanitized/method
emojiContentElement.insertAdjacentHTML('beforeend', categoryMarkup);
resolve();
});
diff --git a/app/assets/javascripts/batch_comments/components/preview_item.vue b/app/assets/javascripts/batch_comments/components/preview_item.vue
index 0eb4e6e7709..71560c7de3a 100644
--- a/app/assets/javascripts/batch_comments/components/preview_item.vue
+++ b/app/assets/javascripts/batch_comments/components/preview_item.vue
@@ -67,6 +67,7 @@ export default {
},
content() {
const el = document.createElement('div');
+ // eslint-disable-next-line no-unsanitized/property
el.innerHTML = this.draft.note_html;
return el.textContent;
diff --git a/app/assets/javascripts/batch_comments/components/submit_dropdown.vue b/app/assets/javascripts/batch_comments/components/submit_dropdown.vue
index 54b9953270b..acc3cbe10a0 100644
--- a/app/assets/javascripts/batch_comments/components/submit_dropdown.vue
+++ b/app/assets/javascripts/batch_comments/components/submit_dropdown.vue
@@ -1,7 +1,16 @@
<script>
import $ from 'jquery';
-import { GlDropdown, GlButton, GlIcon, GlForm, GlFormGroup, GlLink } from '@gitlab/ui';
+import {
+ GlDropdown,
+ GlButton,
+ GlIcon,
+ GlForm,
+ GlFormGroup,
+ GlLink,
+ GlFormCheckbox,
+} from '@gitlab/ui';
import { mapGetters, mapActions } from 'vuex';
+import { createAlert } from '~/flash';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import { scrollToElement } from '~/lib/utils/common_utils';
import Autosave from '~/autosave';
@@ -15,29 +24,46 @@ export default {
GlForm,
GlFormGroup,
GlLink,
+ GlFormCheckbox,
MarkdownField,
+ ApprovalPassword: () => import('ee_component/batch_comments/components/approval_password.vue'),
},
data() {
return {
isSubmitting: false,
- note: '',
+ noteData: {
+ noteable_type: '',
+ noteable_id: '',
+ note: '',
+ approve: false,
+ approval_password: '',
+ },
};
},
computed: {
...mapGetters(['getNotesData', 'getNoteableData', 'noteableType', 'getCurrentUserLastNote']),
},
+ watch: {
+ 'noteData.approve': function noteDataApproveWatch() {
+ setTimeout(() => {
+ this.repositionDropdown();
+ });
+ },
+ },
mounted() {
this.autosave = new Autosave(
$(this.$refs.textarea),
`submit_review_dropdown/${this.getNoteableData.id}`,
);
+ this.noteData.noteable_type = this.noteableType;
+ this.noteData.noteable_id = this.getNoteableData.id;
// We override the Bootstrap Vue click outside behaviour
// to allow for clicking in the autocomplete dropdowns
// without this override the submit dropdown will close
// whenever a item in the autocomplete dropdown is clicked
- const originalClickOutHandler = this.$refs.dropdown.$refs.dropdown.clickOutHandler;
- this.$refs.dropdown.$refs.dropdown.clickOutHandler = (e) => {
+ const originalClickOutHandler = this.$refs.submitDropdown.$refs.dropdown.clickOutHandler;
+ this.$refs.submitDropdown.$refs.dropdown.clickOutHandler = (e) => {
if (!e.target.closest('.atwho-container')) {
originalClickOutHandler(e);
}
@@ -45,26 +71,32 @@ export default {
},
methods: {
...mapActions('batchComments', ['publishReview']),
+ repositionDropdown() {
+ this.$refs.submitDropdown?.$refs.dropdown?.updatePopper();
+ },
async submitReview() {
- const noteData = {
- noteable_type: this.noteableType,
- noteable_id: this.getNoteableData.id,
- note: this.note,
- };
-
this.isSubmitting = true;
- await this.publishReview(noteData);
+ try {
+ await this.publishReview(this.noteData);
+
+ this.autosave.reset();
- this.autosave.reset();
+ if (window.mrTabs && (this.noteData.note || this.noteData.approve)) {
+ if (this.noteData.note) {
+ window.location.hash = `note_${this.getCurrentUserLastNote.id}`;
+ }
- if (window.mrTabs && this.note) {
- window.location.hash = `note_${this.getCurrentUserLastNote.id}`;
- window.mrTabs.tabShown('show');
+ window.mrTabs.tabShown('show');
- setTimeout(() =>
- scrollToElement(document.getElementById(`note_${this.getCurrentUserLastNote.id}`)),
- );
+ setTimeout(() =>
+ scrollToElement(document.getElementById(`note_${this.getCurrentUserLastNote.id}`)),
+ );
+ }
+ } catch (e) {
+ if (e.data?.message) {
+ createAlert({ message: e.data.message, captureError: true });
+ }
}
this.isSubmitting = false;
@@ -79,8 +111,9 @@ export default {
<template>
<gl-dropdown
- ref="dropdown"
+ ref="submitDropdown"
right
+ dropup
class="submit-review-dropdown"
data-qa-selector="submit_review_dropdown"
variant="info"
@@ -110,7 +143,7 @@ export default {
<markdown-field
:is-submitting="isSubmitting"
:add-spacing-classes="false"
- :textarea-value="note"
+ :textarea-value="noteData.note"
:markdown-preview-path="getNoteableData.preview_note_path"
:markdown-docs-path="getNotesData.markdownDocsPath"
:quick-actions-docs-path="getNotesData.quickActionsDocsPath"
@@ -122,7 +155,7 @@ export default {
<textarea
id="review-note-body"
ref="textarea"
- v-model="note"
+ v-model="noteData.note"
dir="auto"
:disabled="isSubmitting"
name="review[note]"
@@ -139,6 +172,18 @@ export default {
</div>
</div>
</gl-form-group>
+ <template v-if="getNoteableData.current_user.can_approve">
+ <gl-form-checkbox v-model="noteData.approve" data-testid="approve_merge_request">
+ {{ __('Approve merge request') }}
+ </gl-form-checkbox>
+ <approval-password
+ v-if="getNoteableData.require_password_to_approve"
+ v-show="noteData.approve"
+ v-model="noteData.approval_password"
+ class="gl-mt-3"
+ data-testid="approve_password"
+ />
+ </template>
<div class="gl-display-flex gl-justify-content-start gl-mt-5">
<gl-button
:loading="isSubmitting"
diff --git a/app/assets/javascripts/batch_comments/stores/modules/batch_comments/actions.js b/app/assets/javascripts/batch_comments/stores/modules/batch_comments/actions.js
index a44b9827fe9..2b0aaa74e83 100644
--- a/app/assets/javascripts/batch_comments/stores/modules/batch_comments/actions.js
+++ b/app/assets/javascripts/batch_comments/stores/modules/batch_comments/actions.js
@@ -84,7 +84,11 @@ export const publishReview = ({ commit, dispatch, getters }, noteData = {}) => {
.publish(getters.getNotesData.draftsPublishPath, noteData)
.then(() => dispatch('updateDiscussionsAfterPublish'))
.then(() => commit(types.RECEIVE_PUBLISH_REVIEW_SUCCESS))
- .catch(() => commit(types.RECEIVE_PUBLISH_REVIEW_ERROR));
+ .catch((e) => {
+ commit(types.RECEIVE_PUBLISH_REVIEW_ERROR);
+
+ throw e.response;
+ });
};
export const updateDiscussionsAfterPublish = async ({ dispatch, getters, rootGetters }) => {
diff --git a/app/assets/javascripts/behaviors/copy_code.js b/app/assets/javascripts/behaviors/copy_code.js
index 6d2a4c245cc..a653769b60f 100644
--- a/app/assets/javascripts/behaviors/copy_code.js
+++ b/app/assets/javascripts/behaviors/copy_code.js
@@ -22,6 +22,7 @@ class CopyCodeButton extends HTMLElement {
'data-clipboard-target': `pre#${this.for}`,
});
+ // eslint-disable-next-line no-unsanitized/property
button.innerHTML = spriteIcon('copy-to-clipboard');
return button;
diff --git a/app/assets/javascripts/behaviors/copy_to_clipboard.js b/app/assets/javascripts/behaviors/copy_to_clipboard.js
index 07fd6dae76a..4b337dce8f3 100644
--- a/app/assets/javascripts/behaviors/copy_to_clipboard.js
+++ b/app/assets/javascripts/behaviors/copy_to_clipboard.js
@@ -102,8 +102,12 @@ export default function initCopyToClipboard() {
* @param {HTMLElement} btnElement
*/
export function clickCopyToClipboardButton(btnElement) {
- // Ensure the button has already been tooltip'd.
- add([btnElement], { show: true });
+ const { clipboardHandleTooltip = true } = btnElement.dataset;
+
+ if (parseBoolean(clipboardHandleTooltip)) {
+ // Ensure the button has already been tooltip'd.
+ add([btnElement], { show: true });
+ }
btnElement.click();
}
diff --git a/app/assets/javascripts/behaviors/markdown/render_math.js b/app/assets/javascripts/behaviors/markdown/render_math.js
index af7aac4cf36..ac41af4df7a 100644
--- a/app/assets/javascripts/behaviors/markdown/render_math.js
+++ b/app/assets/javascripts/behaviors/markdown/render_math.js
@@ -91,6 +91,7 @@ class SafeMathRenderer {
`;
if (!wrapperElement.classList.contains('lazy-alert-shown')) {
+ // eslint-disable-next-line no-unsanitized/property
wrapperElement.innerHTML = html;
wrapperElement.append(codeElement);
wrapperElement.classList.add('lazy-alert-shown');
@@ -111,6 +112,7 @@ class SafeMathRenderer {
}
try {
+ // eslint-disable-next-line no-unsanitized/property
displayContainer.innerHTML = this.katex.renderToString(text, {
displayMode: el.dataset.mathStyle === 'display',
throwOnError: true,
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
index 82229b5aa8f..97ba9e15c0f 100644
--- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
@@ -1,9 +1,11 @@
import $ from 'jquery';
+import ClipboardJS from 'clipboard';
import Mousetrap from 'mousetrap';
-import { clickCopyToClipboardButton } from '~/behaviors/copy_to_clipboard';
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 toast from '~/vue_shared/plugins/global_toast';
+import { s__ } from '~/locale';
import Sidebar from '~/right_sidebar';
import { CopyAsGFM } from '../markdown/copy_as_gfm';
import {
@@ -21,6 +23,15 @@ export default class ShortcutsIssuable extends Shortcuts {
constructor() {
super();
+ this.inMemoryButton = document.createElement('button');
+ this.clipboardInstance = new ClipboardJS(this.inMemoryButton);
+ this.clipboardInstance.on('success', () => {
+ toast(s__('GlobalShortcuts|Copied source branch name to clipboard.'));
+ });
+ this.clipboardInstance.on('error', () => {
+ toast(s__('GlobalShortcuts|Unable to copy the source branch name at this time.'));
+ });
+
Mousetrap.bind(keysFor(ISSUE_MR_CHANGE_ASSIGNEE), () =>
ShortcutsIssuable.openSidebarDropdown('assignee'),
);
@@ -32,7 +43,7 @@ export default class ShortcutsIssuable extends Shortcuts {
);
Mousetrap.bind(keysFor(ISSUABLE_COMMENT_OR_REPLY), ShortcutsIssuable.replyWithSelectedText);
Mousetrap.bind(keysFor(ISSUABLE_EDIT_DESCRIPTION), ShortcutsIssuable.editIssue);
- Mousetrap.bind(keysFor(MR_COPY_SOURCE_BRANCH_NAME), ShortcutsIssuable.copyBranchName);
+ Mousetrap.bind(keysFor(MR_COPY_SOURCE_BRANCH_NAME), () => this.copyBranchName());
/**
* We're attaching a global focus event listener on document for
@@ -153,17 +164,14 @@ export default class ShortcutsIssuable extends Shortcuts {
return false;
}
- static copyBranchName() {
- // There are two buttons - one that is shown when the sidebar
- // is expanded, and one that is shown when it's collapsed.
- const allCopyBtns = Array.from(document.querySelectorAll('.js-source-branch-copy'));
+ async copyBranchName() {
+ const button = document.querySelector('.js-source-branch-copy');
+ const branchName = button?.dataset.clipboardText;
- // Select whichever button is currently visible so that
- // the "Copied" tooltip is shown when a click is simulated.
- const visibleBtn = allCopyBtns.find(isElementVisible);
+ if (branchName) {
+ this.inMemoryButton.dataset.clipboardText = branchName;
- if (visibleBtn) {
- clickCopyToClipboardButton(visibleBtn);
+ this.inMemoryButton.dispatchEvent(new CustomEvent('click'));
}
}
}
diff --git a/app/assets/javascripts/blob/3d_viewer/index.js b/app/assets/javascripts/blob/3d_viewer/index.js
index d4efe409fef..2831c37838b 100644
--- a/app/assets/javascripts/blob/3d_viewer/index.js
+++ b/app/assets/javascripts/blob/3d_viewer/index.js
@@ -1,11 +1,8 @@
-import OrbitControlsClass from 'three-orbit-controls';
-import STLLoaderClass from 'three-stl-loader';
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
+import { STLLoader } from 'three/examples/jsm/loaders/STLLoader';
import * as THREE from 'three/build/three.module';
import MeshObject from './mesh_object';
-const STLLoader = STLLoaderClass(THREE);
-const OrbitControls = OrbitControlsClass(THREE);
-
export default class Renderer {
constructor(container) {
this.renderWrapper = this.render.bind(this);
diff --git a/app/assets/javascripts/blob/3d_viewer/mesh_object.js b/app/assets/javascripts/blob/3d_viewer/mesh_object.js
index c55a9ca8926..5322dc00e86 100644
--- a/app/assets/javascripts/blob/3d_viewer/mesh_object.js
+++ b/app/assets/javascripts/blob/3d_viewer/mesh_object.js
@@ -22,7 +22,7 @@ export default class MeshObject extends Mesh {
if (this.geometry.boundingSphere.radius > 4) {
const scale = 4 / this.geometry.boundingSphere.radius;
- this.geometry.applyMatrix(new Matrix4().makeScale(scale, scale, scale));
+ this.geometry.applyMatrix4(new Matrix4().makeScale(scale, scale, scale));
this.geometry.computeBoundingSphere();
this.position.x = -this.geometry.boundingSphere.center.x;
diff --git a/app/assets/javascripts/blob/notebook/notebook_viewer.vue b/app/assets/javascripts/blob/notebook/notebook_viewer.vue
index d2a841c88f1..dc1a9cb865a 100644
--- a/app/assets/javascripts/blob/notebook/notebook_viewer.vue
+++ b/app/assets/javascripts/blob/notebook/notebook_viewer.vue
@@ -1,11 +1,11 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils';
-import notebookLab from '~/notebook/index.vue';
+import NotebookLab from '~/notebook/index.vue';
export default {
components: {
- notebookLab,
+ NotebookLab,
GlLoadingIcon,
},
props: {
@@ -66,7 +66,7 @@ export default {
<div v-if="loading && !error" class="text-center loading">
<gl-loading-icon class="mt-5" size="lg" />
</div>
- <notebook-lab v-if="!loading && !error" :notebook="json" code-css-class="code white" />
+ <notebook-lab v-if="!loading && !error" :notebook="json" />
<p v-if="error" class="text-center">
<span v-if="loadError" ref="loadErrorMessage">{{
__('An error occurred while loading the file. Please try again later.')
diff --git a/app/assets/javascripts/blob/sketch/index.js b/app/assets/javascripts/blob/sketch/index.js
index a92161bbc1b..bb29224cda2 100644
--- a/app/assets/javascripts/blob/sketch/index.js
+++ b/app/assets/javascripts/blob/sketch/index.js
@@ -1,5 +1,5 @@
import JSZip from 'jszip';
-import JSZipUtils from 'jszip-utils';
+import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
export default class SketchLoader {
@@ -7,35 +7,28 @@ export default class SketchLoader {
this.container = container;
this.loadingIcon = this.container.querySelector('.js-loading-icon');
- this.load();
+ this.load().catch(() => {
+ this.error();
+ });
}
- load() {
- return this.getZipFile()
- .then((data) => JSZip.loadAsync(data))
- .then((asyncResult) => asyncResult.files['previews/preview.png'].async('uint8array'))
- .then((content) => {
- const url = window.URL || window.webkitURL;
- const blob = new Blob([new Uint8Array(content)], {
- type: 'image/png',
- });
- const previewUrl = url.createObjectURL(blob);
+ async load() {
+ const zipContents = await this.getZipContents();
+ const previewContents = await zipContents.files['previews/preview.png'].async('uint8array');
+
+ const blob = new Blob([previewContents], {
+ type: 'image/png',
+ });
- this.render(previewUrl);
- })
- .catch(this.error.bind(this));
+ this.render(window.URL.createObjectURL(blob));
}
- getZipFile() {
- return new Promise((resolve, reject) => {
- JSZipUtils.getBinaryContent(this.container.dataset.endpoint, (err, data) => {
- if (err) {
- reject(err);
- } else {
- resolve(data);
- }
- });
+ async getZipContents() {
+ const { data } = await axios.get(this.container.dataset.endpoint, {
+ responseType: 'arraybuffer',
});
+
+ return JSZip.loadAsync(data);
}
render(previewUrl) {
diff --git a/app/assets/javascripts/blob/viewer/index.js b/app/assets/javascripts/blob/viewer/index.js
index a0d4f7ef4f2..5ca3f131d99 100644
--- a/app/assets/javascripts/blob/viewer/index.js
+++ b/app/assets/javascripts/blob/viewer/index.js
@@ -45,6 +45,7 @@ const loadViewer = (viewerParam) => {
viewer.dataset.loading = 'true';
return axios.get(url).then(({ data }) => {
+ // eslint-disable-next-line no-unsanitized/property
viewer.innerHTML = data.html;
window.requestIdleCallback(() => {
diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js
index 425de914c17..d73e1cc43b0 100644
--- a/app/assets/javascripts/blob_edit/blob_bundle.js
+++ b/app/assets/javascripts/blob_edit/blob_bundle.js
@@ -63,6 +63,7 @@ export default () => {
const isMarkdown = editBlobForm.data('is-markdown');
const previewMarkdownPath = editBlobForm.data('previewMarkdownPath');
const commitButton = $('.js-commit-button');
+ const commitButtonLoading = $('.js-commit-button-loading');
const cancelLink = $('#cancel-changes');
import('./edit_blob')
@@ -88,6 +89,8 @@ export default () => {
});
commitButton.on('click', () => {
+ commitButton.addClass('gl-display-none');
+ commitButtonLoading.removeClass('gl-display-none');
window.onbeforeunload = null;
});
diff --git a/app/assets/javascripts/boards/components/board_add_new_column_form.vue b/app/assets/javascripts/boards/components/board_add_new_column_form.vue
index c4a2f83ab50..1899d42fa4d 100644
--- a/app/assets/javascripts/boards/components/board_add_new_column_form.vue
+++ b/app/assets/javascripts/boards/components/board_add_new_column_form.vue
@@ -102,7 +102,7 @@ export default {
data-qa-selector="board_add_new_list"
>
<div
- class="board-inner gl-display-flex gl-flex-direction-column gl-relative gl-h-full gl-rounded-base"
+ class="board-inner gl-display-flex gl-flex-direction-column gl-relative gl-h-full gl-rounded-base gl-bg-gray-50"
>
<h3 class="gl-font-size-h2 gl-px-5 gl-py-5 gl-m-0" data-testid="board-add-column-form-title">
{{ $options.i18n.newList }}
diff --git a/app/assets/javascripts/boards/components/board_blocked_icon.vue b/app/assets/javascripts/boards/components/board_blocked_icon.vue
index b81edb4dfe6..3f8a596abd8 100644
--- a/app/assets/javascripts/boards/components/board_blocked_icon.vue
+++ b/app/assets/javascripts/boards/components/board_blocked_icon.vue
@@ -1,7 +1,7 @@
<script>
import { GlIcon, GlLink, GlPopover, GlLoadingIcon } from '@gitlab/ui';
import { blockingIssuablesQueries, issuableTypes } from '~/boards/constants';
-import { TYPE_ISSUE } from '~/graphql_shared/constants';
+import { TYPE_ISSUE, TYPE_EPIC } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { truncate } from '~/lib/utils/text_utility';
import { __, n__, s__, sprintf } from '~/locale';
@@ -10,10 +10,12 @@ export default {
i18n: {
issuableType: {
[issuableTypes.issue]: __('issue'),
+ [issuableTypes.epic]: __('epic'),
},
},
graphQLIdType: {
[issuableTypes.issue]: TYPE_ISSUE,
+ [issuableTypes.epic]: TYPE_EPIC,
},
referenceFormatter: {
[issuableTypes.issue]: (r) => r.split('/')[1],
@@ -40,7 +42,7 @@ export default {
type: String,
required: true,
validator(value) {
- return [issuableTypes.issue].includes(value);
+ return [issuableTypes.issue, issuableTypes.epic].includes(value);
},
},
},
@@ -53,14 +55,21 @@ export default {
return blockingIssuablesQueries[this.issuableType].query;
},
variables() {
+ if (this.isEpic) {
+ return {
+ fullPath: this.item.group.fullPath,
+ iid: Number(this.item.iid),
+ };
+ }
return {
id: convertToGraphQLId(this.$options.graphQLIdType[this.issuableType], this.item.id),
};
},
update(data) {
this.skip = true;
+ const issuable = this.isEpic ? data?.group?.issuable : data?.issuable;
- return data?.issuable?.blockingIssuables?.nodes || [];
+ return issuable?.blockingIssuables?.nodes || [];
},
error(error) {
const message = sprintf(s__('Boards|Failed to fetch blocking %{issuableType}s'), {
@@ -77,13 +86,16 @@ export default {
};
},
computed: {
+ isEpic() {
+ return this.issuableType === issuableTypes.epic;
+ },
displayedIssuables() {
const { defaultDisplayLimit, referenceFormatter } = this.$options;
return this.blockingIssuables.slice(0, defaultDisplayLimit).map((i) => {
return {
...i,
title: truncate(i.title, this.$options.textTruncateWidth),
- reference: referenceFormatter[this.issuableType](i.reference),
+ reference: this.isEpic ? i.reference : referenceFormatter[this.issuableType](i.reference),
};
});
},
@@ -106,6 +118,9 @@ export default {
},
);
},
+ blockIcon() {
+ return this.issuableType === issuableTypes.issue ? 'issue-block' : 'entity-blocked';
+ },
glIconId() {
return `blocked-icon-${this.uniqueId}`;
},
@@ -153,8 +168,8 @@ export default {
<gl-icon
:id="glIconId"
ref="icon"
- name="issue-block"
- class="issue-blocked-icon gl-mr-2 gl-cursor-pointer"
+ :name="blockIcon"
+ class="issue-blocked-icon gl-mr-2 gl-cursor-pointer gl-text-red-500"
data-testid="issue-blocked-icon"
@mouseenter="handleMouseEnter"
/>
diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue
index 3638fdd2ca5..44c16324950 100644
--- a/app/assets/javascripts/boards/components/board_card.vue
+++ b/app/assets/javascripts/boards/components/board_card.vue
@@ -30,6 +30,11 @@ export default {
default: 0,
required: false,
},
+ showWorkItemTypeIcon: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
},
computed: {
...mapState(['selectedBoardItems', 'activeId']),
@@ -81,10 +86,10 @@ export default {
data-qa-selector="board_card"
:class="[
{
- 'multi-select': multiSelectVisible,
+ 'multi-select gl-bg-blue-50 gl-border-blue-200': multiSelectVisible,
'gl-cursor-grab': isDraggable,
'is-disabled': isDisabled,
- 'is-active': isActive,
+ 'is-active gl-bg-blue-50': isActive,
'gl-cursor-not-allowed gl-bg-gray-10': item.isLoading,
},
colorClass,
@@ -95,9 +100,15 @@ export default {
:data-item-path="item.referencePath"
:style="cardStyle"
data-testid="board_card"
- class="board-card gl-p-5 gl-rounded-base"
+ class="board-card gl-p-5 gl-rounded-base gl-line-height-normal gl-relative gl-mb-3"
@click="toggleIssue($event)"
>
- <board-card-inner :list="list" :item="item" :update-filters="true" />
+ <board-card-inner
+ :list="list"
+ :item="item"
+ :update-filters="true"
+ :index="index"
+ :show-work-item-type-icon="showWorkItemTypeIcon"
+ />
</li>
</template>
diff --git a/app/assets/javascripts/boards/components/board_card_inner.vue b/app/assets/javascripts/boards/components/board_card_inner.vue
index 8dc521317cd..92a623d65d4 100644
--- a/app/assets/javascripts/boards/components/board_card_inner.vue
+++ b/app/assets/javascripts/boards/components/board_card_inner.vue
@@ -15,6 +15,8 @@ import { updateHistory } from '~/lib/utils/url_utility';
import { sprintf, __, n__ } from '~/locale';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
+import BoardCardMoveToPosition from '~/boards/components/board_card_move_to_position.vue';
+import WorkItemTypeIcon from '~/work_items/components/work_item_type_icon.vue';
import { ListType } from '../constants';
import eventHub from '../eventhub';
import BoardBlockedIcon from './board_blocked_icon.vue';
@@ -34,6 +36,10 @@ export default {
IssueCardWeight: () => import('ee_component/boards/components/issue_card_weight.vue'),
BoardBlockedIcon,
GlSprintf,
+ BoardCardMoveToPosition,
+ WorkItemTypeIcon,
+ IssueHealthStatus: () =>
+ import('ee_component/related_items_tree/components/issue_health_status.vue'),
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -55,6 +61,15 @@ export default {
required: false,
default: false,
},
+ index: {
+ type: Number,
+ required: true,
+ },
+ showWorkItemTypeIcon: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -202,7 +217,7 @@ export default {
<template>
<div>
<div class="gl-display-flex" dir="auto">
- <h4 class="board-card-title gl-mb-0 gl-mt-0">
+ <h4 class="board-card-title gl-mb-0 gl-mt-0 gl-mr-3 gl-font-base gl-overflow-break-word">
<board-blocked-icon
v-if="item.blocked"
:item="item"
@@ -215,7 +230,7 @@ export default {
v-gl-tooltip
name="eye-slash"
:title="__('Confidential')"
- class="confidential-icon gl-mr-2"
+ class="confidential-icon gl-mr-2 gl-text-orange-500 gl-cursor-help"
:aria-label="__('Confidential')"
/>
<gl-icon
@@ -223,24 +238,25 @@ export default {
v-gl-tooltip
name="spam"
:title="__('This issue is hidden because its author has been banned')"
- class="gl-mr-2 hidden-icon"
+ class="gl-mr-2 hidden-icon gl-text-orange-500 gl-cursor-help"
data-testid="hidden-icon"
/>
<a
:href="item.path || item.webUrl || ''"
:title="item.title"
:class="{ 'gl-text-gray-400!': item.isLoading }"
- class="js-no-trigger"
+ class="js-no-trigger gl-text-body gl-hover-text-gray-900"
@mousemove.stop
>{{ item.title }}</a
>
</h4>
+ <board-card-move-to-position :item="item" :list="list" :index="index" />
</div>
<div v-if="showLabelFooter" class="board-card-labels gl-mt-2 gl-display-flex gl-flex-wrap">
<template v-for="label in orderedLabels">
<gl-label
:key="label.id"
- class="js-no-trigger"
+ class="js-no-trigger gl-mt-2 gl-mr-2"
:background-color="label.color"
:title="label.title"
:description="label.description"
@@ -260,9 +276,14 @@ export default {
<gl-loading-icon v-if="item.isLoading" size="lg" class="gl-mt-5" />
<span
v-if="item.referencePath"
- class="board-card-number gl-overflow-hidden gl-display-flex gl-mr-3 gl-mt-3"
+ class="board-card-number gl-overflow-hidden gl-display-flex gl-mr-3 gl-mt-3 gl-text-secondary"
:class="{ 'gl-font-base': isEpicBoard }"
>
+ <work-item-type-icon
+ v-if="showWorkItemTypeIcon"
+ :work-item-type="item.type"
+ show-tooltip-on-hover
+ />
<tooltip-on-truncate
v-if="showReferencePath"
:title="itemReferencePath"
@@ -321,7 +342,10 @@ export default {
</p>
</gl-tooltip>
- <span ref="countBadge" class="board-card-info gl-mr-0 gl-pr-0 gl-pl-3">
+ <span
+ ref="countBadge"
+ class="board-card-info gl-mr-0 gl-pr-0 gl-pl-3 gl-text-secondary gl-cursor-help"
+ >
<span v-if="allowSubEpics" class="gl-mr-3">
<gl-icon name="epic" />
{{ totalEpicsCount }}
@@ -339,7 +363,7 @@ export default {
<span
v-if="shouldRenderEpicProgress"
ref="progressBadge"
- class="board-card-info gl-pl-0"
+ class="board-card-info gl-pl-0 gl-text-secondary gl-cursor-help"
>
<span class="gl-mr-3" data-testid="epic-progress">
<gl-icon name="progress" />
@@ -359,10 +383,11 @@ export default {
:weight="item.weight"
@click="filterByWeight(item.weight)"
/>
+ <issue-health-status v-if="item.healthStatus" :health-status="item.healthStatus" />
</span>
</span>
</div>
- <div class="board-card-assignee gl-display-flex gl-gap-3">
+ <div class="board-card-assignee gl-display-flex gl-gap-3 gl-mb-n2">
<user-avatar-link
v-for="assignee in cappedAssignees"
:key="assignee.id"
@@ -370,7 +395,7 @@ export default {
:img-alt="avatarUrlTitle(assignee)"
:img-src="avatarUrl(assignee)"
:img-size="avatarSize"
- class="js-no-trigger"
+ class="js-no-trigger user-avatar-link"
tooltip-placement="bottom"
:enforce-gl-avatar="true"
>
@@ -384,7 +409,7 @@ export default {
v-if="shouldRenderCounter"
v-gl-tooltip
:title="assigneeCounterTooltip"
- class="avatar-counter"
+ class="avatar-counter gl-bg-gray-400 gl-cursor-help gl-font-weight-bold gl-ml-n4 gl-border-0 gl-line-height-24"
data-placement="bottom"
>{{ assigneeCounterLabel }}</span
>
diff --git a/app/assets/javascripts/boards/components/board_card_move_to_position.vue b/app/assets/javascripts/boards/components/board_card_move_to_position.vue
new file mode 100644
index 00000000000..ff938219475
--- /dev/null
+++ b/app/assets/javascripts/boards/components/board_card_move_to_position.vue
@@ -0,0 +1,128 @@
+<script>
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { mapActions, mapGetters, mapState } from 'vuex';
+import { s__ } from '~/locale';
+
+import Tracking from '~/tracking';
+
+export default {
+ i18n: {
+ moveToStartText: s__('Boards|Move to start of list'),
+ moveToEndText: s__('Boards|Move to end of list'),
+ },
+ name: 'BoardCardMoveToPosition',
+ components: {
+ GlDropdown,
+ GlDropdownItem,
+ },
+ mixins: [Tracking.mixin()],
+ props: {
+ item: {
+ type: Object,
+ required: true,
+ validator: (item) => ['id', 'iid', 'referencePath'].every((key) => item[key]),
+ },
+ list: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ index: {
+ type: Number,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapState(['pageInfoByListId']),
+ ...mapGetters(['getBoardItemsByList']),
+ tracking() {
+ return {
+ category: 'boards:list',
+ label: 'move_to_position',
+ property: `type_card`,
+ };
+ },
+ listItems() {
+ return this.getBoardItemsByList(this.list.id);
+ },
+ listHasNextPage() {
+ return this.pageInfoByListId[this.list.id]?.hasNextPage;
+ },
+ lengthOfListItemsInBoard() {
+ return this.listItems?.length;
+ },
+ itemIdentifier() {
+ return `${this.item.id}-${this.item.iid}-${this.index}`;
+ },
+ isFirstItemInList() {
+ return this.index === 0;
+ },
+ isLastItemInList() {
+ return this.index === this.lengthOfListItemsInBoard - 1;
+ },
+ },
+ methods: {
+ ...mapActions(['moveItem']),
+ moveToStart() {
+ this.track('click_toggle_button', {
+ label: 'move_to_start',
+ });
+ /** in case it is the first in the list don't call any action/mutation * */
+ if (this.isFirstItemInList) {
+ return;
+ }
+ this.moveToPosition({
+ positionInList: 0,
+ });
+ },
+ moveToEnd() {
+ this.track('click_toggle_button', {
+ label: 'move_to_end',
+ });
+ /** in case it is the last in the list don't call any action/mutation * */
+ if (this.isLastItemInList) {
+ return;
+ }
+ this.moveToPosition({
+ positionInList: -1,
+ });
+ },
+ moveToPosition({ positionInList }) {
+ this.moveItem({
+ itemId: this.item.id,
+ itemIid: this.item.iid,
+ itemPath: this.item.referencePath,
+ fromListId: this.list.id,
+ toListId: this.list.id,
+ positionInList,
+ atIndex: this.index,
+ allItemsLoadedInList: !this.listHasNextPage,
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-dropdown
+ ref="dropdown"
+ :key="itemIdentifier"
+ icon="ellipsis_v"
+ :text="s__('Boards|Move card')"
+ :text-sr-only="true"
+ class="move-to-position gl-display-block gl-mb-2 gl-ml-2 gl-mt-n3 gl-mr-n3"
+ category="tertiary"
+ :tabindex="index"
+ no-caret
+ @keydown.esc.native="$emit('hide')"
+ >
+ <div>
+ <gl-dropdown-item @click.stop="moveToStart">
+ {{ $options.i18n.moveToStartText }}
+ </gl-dropdown-item>
+ <gl-dropdown-item @click.stop="moveToEnd">
+ {{ $options.i18n.moveToEndText }}
+ </gl-dropdown-item>
+ </div>
+ </gl-dropdown>
+</template>
diff --git a/app/assets/javascripts/boards/components/board_column.vue b/app/assets/javascripts/boards/components/board_column.vue
index bcf5b12b209..8fc76c02e14 100644
--- a/app/assets/javascripts/boards/components/board_column.vue
+++ b/app/assets/javascripts/boards/components/board_column.vue
@@ -76,7 +76,7 @@ export default {
<div
:class="{
'is-draggable': isListDraggable,
- 'is-collapsed': list.collapsed,
+ 'is-collapsed gl-w-10': list.collapsed,
'board-type-assignee': list.listType === 'assignee',
}"
:data-list-id="list.id"
@@ -84,7 +84,7 @@ export default {
data-qa-selector="board_list"
>
<div
- class="board-inner gl-display-flex gl-flex-direction-column gl-relative gl-h-full gl-rounded-base"
+ class="board-inner gl-display-flex gl-flex-direction-column gl-relative gl-h-full gl-rounded-base gl-bg-gray-50"
:class="{ 'board-column-highlighted': highlighted }"
>
<board-list-header :list="list" :disabled="disabled" />
diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue
index 8868b9b2f3e..d99afa8455d 100644
--- a/app/assets/javascripts/boards/components/board_content.vue
+++ b/app/assets/javascripts/boards/components/board_content.vue
@@ -75,7 +75,7 @@ export default {
v-if="!isSwimlanesOn"
ref="list"
v-bind="draggableOptions"
- class="boards-list gl-w-full gl-py-5 gl-pr-3 gl-white-space-nowrap"
+ class="boards-list gl-w-full gl-py-5 gl-pr-3 gl-white-space-nowrap gl-overflow-x-scroll"
@end="moveList"
>
<board-column
diff --git a/app/assets/javascripts/boards/components/board_content_sidebar.vue b/app/assets/javascripts/boards/components/board_content_sidebar.vue
index d25169b5b9d..00b4e6c96a9 100644
--- a/app/assets/javascripts/boards/components/board_content_sidebar.vue
+++ b/app/assets/javascripts/boards/components/board_content_sidebar.vue
@@ -57,6 +57,9 @@ export default {
labelsFilterBasePath: {
default: '',
},
+ canUpdate: {
+ default: false,
+ },
},
inheritAttrs: false,
computed: {
@@ -163,6 +166,7 @@ export default {
:full-path="fullPath"
:initial-assignees="activeBoardItem.assignees"
:allow-multiple-assignees="multipleAssigneesFeatureAvailable"
+ :editable="canUpdate"
@assignees-updated="setAssignees"
/>
<sidebar-dropdown-widget
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index 66388f4eb43..edf1a5ee7e6 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -66,7 +66,7 @@ export default {
},
},
computed: {
- ...mapState(['pageInfoByListId', 'listsFlags', 'filterParams']),
+ ...mapState(['pageInfoByListId', 'listsFlags', 'filterParams', 'isUpdateIssueOrderInProgress']),
...mapGetters(['isEpicBoard']),
listItemsCount() {
return this.isEpicBoard ? this.list.epicsCount : this.boardList?.issuesCount;
@@ -132,6 +132,9 @@ export default {
return this.canMoveIssue ? options : {};
},
+ disableScrollingWhenMutationInProgress() {
+ return this.hasNextPage && this.isUpdateIssueOrderInProgress;
+ },
},
watch: {
boardItems() {
@@ -265,7 +268,7 @@ export default {
<template>
<div
v-show="!list.collapsed"
- class="board-list-component gl-relative gl-h-full gl-display-flex gl-flex-direction-column"
+ class="board-list-component gl-relative gl-h-full gl-display-flex gl-flex-direction-column gl-min-h-0"
data-qa-selector="board_list_cards_area"
>
<div
@@ -285,9 +288,13 @@ export default {
v-bind="treeRootOptions"
:data-board="list.id"
:data-board-type="list.listType"
- :class="{ 'bg-danger-100': boardItemsSizeExceedsMax }"
+ :class="{
+ 'bg-danger-100': boardItemsSizeExceedsMax,
+ 'gl-overflow-hidden': disableScrollingWhenMutationInProgress,
+ 'gl-overflow-y-auto': !disableScrollingWhenMutationInProgress,
+ }"
draggable=".board-card"
- class="board-list gl-w-full gl-h-full gl-list-style-none gl-mb-0 gl-p-3 gl-pt-0"
+ class="board-list gl-w-full gl-h-full gl-list-style-none gl-mb-0 gl-p-3 gl-pt-0 gl-overflow-x-hidden"
data-testid="tree-root-wrapper"
@start="handleDragOnStart"
@end="handleDragOnEnd"
@@ -301,9 +308,14 @@ export default {
:item="item"
:data-draggable-item-type="$options.draggableItemTypes.card"
:disabled="disabled"
+ :show-work-item-type-icon="!isEpicBoard"
/>
<gl-intersection-observer @appear="onReachingListBottom">
- <li v-if="showCount" class="board-list-count gl-text-center" data-issue-id="-1">
+ <li
+ v-if="showCount"
+ class="board-list-count gl-text-center gl-text-secondary gl-py-4"
+ data-issue-id="-1"
+ >
<gl-loading-icon
v-if="loadingMore"
size="sm"
diff --git a/app/assets/javascripts/boards/components/board_list_header.vue b/app/assets/javascripts/boards/components/board_list_header.vue
index e3012f5b36d..230fa4e1e0f 100644
--- a/app/assets/javascripts/boards/components/board_list_header.vue
+++ b/app/assets/javascripts/boards/components/board_list_header.vue
@@ -252,7 +252,7 @@ export default {
<header
:class="{
'gl-h-full': list.collapsed,
- 'board-inner gl-rounded-top-left-base gl-rounded-top-right-base': isSwimlanesHeader,
+ 'board-inner gl-rounded-top-left-base gl-rounded-top-right-base gl-bg-gray-50': isSwimlanesHeader,
}"
:style="headerStyle"
class="board-header gl-relative"
@@ -267,14 +267,15 @@ export default {
'gl-py-2': list.collapsed && isSwimlanesHeader,
'gl-flex-direction-column': list.collapsed,
}"
- class="board-title gl-m-0 gl-display-flex gl-align-items-center gl-font-base gl-px-3"
+ class="board-title gl-m-0 gl-display-flex gl-align-items-center gl-font-base gl-px-3 gl-h-9"
>
<gl-button
v-gl-tooltip.hover
:aria-label="chevronTooltip"
:title="chevronTooltip"
:icon="chevronIcon"
- class="board-title-caret no-drag gl-cursor-pointer"
+ class="board-title-caret no-drag gl-cursor-pointer gl-hover-bg-gray-50"
+ :class="{ 'gl-mt-1': list.collapsed, 'gl-mr-2': !list.collapsed }"
category="tertiary"
size="small"
data-testid="board-title-caret"
@@ -307,6 +308,7 @@ export default {
'gl-display-none': list.collapsed && isSwimlanesHeader,
'gl-flex-grow-0 gl-my-3 gl-mx-0': list.collapsed,
'gl-flex-grow-1': !list.collapsed,
+ 'gl-rotate-90': list.collapsed,
}"
>
<!-- EE start -->
@@ -324,7 +326,7 @@ export default {
<span
v-if="listType === 'assignee'"
v-show="!list.collapsed"
- class="gl-ml-2 gl-font-weight-normal gl-text-gray-500"
+ class="gl-ml-2 gl-font-weight-normal gl-text-secondary"
>
@{{ listAssignee }}
</span>
@@ -345,7 +347,7 @@ export default {
v-if="isSwimlanesHeader && list.collapsed"
ref="collapsedInfo"
aria-hidden="true"
- class="board-header-collapsed-info-icon gl-cursor-pointer gl-text-gray-500"
+ class="board-header-collapsed-info-icon gl-cursor-pointer gl-text-secondary gl-hover-text-gray-900"
>
<gl-icon name="information" />
</span>
@@ -369,14 +371,14 @@ export default {
<!-- EE end -->
<div
- class="issue-count-badge gl-display-inline-flex gl-pr-2 no-drag gl-text-gray-500"
+ class="issue-count-badge gl-display-inline-flex gl-pr-2 no-drag gl-text-secondary"
data-testid="issue-count-badge"
:class="{
'gl-display-none!': list.collapsed && isSwimlanesHeader,
'gl-p-0': list.collapsed,
}"
>
- <span class="gl-display-inline-flex">
+ <span class="gl-display-inline-flex" :class="{ 'gl-rotate-90': list.collapsed }">
<gl-tooltip :target="() => $refs.itemCount" :title="itemsTooltipLabel" />
<span ref="itemCount" class="gl-display-inline-flex gl-align-items-center">
<gl-icon class="gl-mr-2" :name="countIcon" :size="16" />
diff --git a/app/assets/javascripts/boards/components/board_new_item.vue b/app/assets/javascripts/boards/components/board_new_item.vue
index 600917683cd..084b7519d1f 100644
--- a/app/assets/javascripts/boards/components/board_new_item.vue
+++ b/app/assets/javascripts/boards/components/board_new_item.vue
@@ -69,7 +69,7 @@ export default {
</script>
<template>
- <div class="board-new-issue-form">
+ <div class="board-new-issue-form gl-z-index-3 gl-m-3">
<div class="board-card position-relative gl-p-5 rounded">
<gl-form @submit.prevent="handleFormSubmit" @reset="handleFormCancel">
<label :for="inputFieldId" class="gl-font-weight-bold">{{ __('Title') }}</label>
diff --git a/app/assets/javascripts/boards/components/issue_due_date.vue b/app/assets/javascripts/boards/components/issue_due_date.vue
index 73ec008c2b6..b09b1d48ca5 100644
--- a/app/assets/javascripts/boards/components/issue_due_date.vue
+++ b/app/assets/javascripts/boards/components/issue_due_date.vue
@@ -1,6 +1,6 @@
<script>
import { GlTooltip, GlIcon } from '@gitlab/ui';
-import dateFormat from 'dateformat';
+import dateFormat from '~/lib/dateformat';
import {
getDayDifference,
getTimeago,
@@ -85,7 +85,11 @@ export default {
<template>
<span>
- <span ref="issueDueDate" :class="cssClass" class="board-card-info card-number">
+ <span
+ ref="issueDueDate"
+ :class="cssClass"
+ class="board-card-info gl-mr-3 gl-text-secondary gl-cursor-help"
+ >
<gl-icon
:class="{ 'text-danger': isPastDue }"
class="board-card-info-icon gl-mr-2"
diff --git a/app/assets/javascripts/boards/components/issue_time_estimate.vue b/app/assets/javascripts/boards/components/issue_time_estimate.vue
index 9312db06efe..bc12717a92d 100644
--- a/app/assets/javascripts/boards/components/issue_time_estimate.vue
+++ b/app/assets/javascripts/boards/components/issue_time_estimate.vue
@@ -36,7 +36,7 @@ export default {
<template>
<span>
- <span ref="issueTimeEstimate" class="board-card-info card-number">
+ <span ref="issueTimeEstimate" class="board-card-info gl-mr-3 gl-text-secondary gl-cursor-help">
<gl-icon name="hourglass" class="board-card-info-icon gl-mr-2" />
<time class="board-card-info-text">{{ timeEstimate }}</time>
</span>
diff --git a/app/assets/javascripts/boards/components/item_count.vue b/app/assets/javascripts/boards/components/item_count.vue
index a11c23e5625..dab82abb646 100644
--- a/app/assets/javascripts/boards/components/item_count.vue
+++ b/app/assets/javascripts/boards/components/item_count.vue
@@ -30,7 +30,9 @@ export default {
{{ itemsSize }}
</span>
<span v-if="isMaxLimitSet" class="max-issue-size">
- {{ maxIssueCount }}
+ <!-- eslint-disable @gitlab/vue-require-i18n-strings -->
+ {{ `/ ${maxIssueCount}` }}
+ <!-- eslint-enable @gitlab/vue-require-i18n-strings -->
</span>
</div>
</template>
diff --git a/app/assets/javascripts/boards/constants.js b/app/assets/javascripts/boards/constants.js
index 0f290f566ba..ed22a375271 100644
--- a/app/assets/javascripts/boards/constants.js
+++ b/app/assets/javascripts/boards/constants.js
@@ -3,6 +3,7 @@ import { __ } from '~/locale';
import updateEpicSubscriptionMutation from '~/sidebar/queries/update_epic_subscription.mutation.graphql';
import updateEpicTitleMutation from '~/sidebar/queries/update_epic_title.mutation.graphql';
import boardBlockingIssuesQuery from './graphql/board_blocking_issues.query.graphql';
+import boardBlockingEpicsQuery from './graphql/board_blocking_epics.query.graphql';
import destroyBoardListMutation from './graphql/board_list_destroy.mutation.graphql';
import updateBoardListMutation from './graphql/board_list_update.mutation.graphql';
@@ -70,6 +71,9 @@ export const blockingIssuablesQueries = {
[issuableTypes.issue]: {
query: boardBlockingIssuesQuery,
},
+ [issuableTypes.epic]: {
+ query: boardBlockingEpicsQuery,
+ },
};
export const updateListQueries = {
@@ -146,3 +150,5 @@ export default {
BoardType,
ListType,
};
+
+export const DEFAULT_BOARD_LIST_ITEMS_SIZE = 10;
diff --git a/app/assets/javascripts/boards/filters/due_date_filters.js b/app/assets/javascripts/boards/filters/due_date_filters.js
index 1745ab3bab4..a452d32ef15 100644
--- a/app/assets/javascripts/boards/filters/due_date_filters.js
+++ b/app/assets/javascripts/boards/filters/due_date_filters.js
@@ -1,5 +1,5 @@
-import dateFormat from 'dateformat';
import Vue from 'vue';
+import dateFormat from '~/lib/dateformat';
Vue.filter('due-date', (value) => {
const date = new Date(value);
diff --git a/app/assets/javascripts/boards/graphql/board.fragment.graphql b/app/assets/javascripts/boards/graphql/board.fragment.graphql
deleted file mode 100644
index 872a4c4afbc..00000000000
--- a/app/assets/javascripts/boards/graphql/board.fragment.graphql
+++ /dev/null
@@ -1,4 +0,0 @@
-fragment BoardFragment on Board {
- id
- name
-}
diff --git a/app/assets/javascripts/boards/graphql/board_blocking_epics.query.graphql b/app/assets/javascripts/boards/graphql/board_blocking_epics.query.graphql
new file mode 100644
index 00000000000..071a6d7410f
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/board_blocking_epics.query.graphql
@@ -0,0 +1,17 @@
+query BoardBlockingEpics($fullPath: ID!, $iid: ID) {
+ group(fullPath: $fullPath) {
+ id
+ issuable: epic(iid: $iid) {
+ id
+ blockingIssuables: blockedByEpics {
+ nodes {
+ id
+ iid
+ title
+ reference(full: true)
+ webUrl
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/boards/graphql/group_boards.query.graphql b/app/assets/javascripts/boards/graphql/group_boards.query.graphql
index 0823c4f5a83..ce9f7bbfd2a 100644
--- a/app/assets/javascripts/boards/graphql/group_boards.query.graphql
+++ b/app/assets/javascripts/boards/graphql/group_boards.query.graphql
@@ -1,12 +1,11 @@
-#import "ee_else_ce/boards/graphql/board.fragment.graphql"
-
query group_boards($fullPath: ID!) {
group(fullPath: $fullPath) {
id
boards {
edges {
node {
- ...BoardFragment
+ id
+ name
}
}
}
diff --git a/app/assets/javascripts/boards/graphql/group_recent_boards.query.graphql b/app/assets/javascripts/boards/graphql/group_recent_boards.query.graphql
index 827c08486b1..b9fe778d4d4 100644
--- a/app/assets/javascripts/boards/graphql/group_recent_boards.query.graphql
+++ b/app/assets/javascripts/boards/graphql/group_recent_boards.query.graphql
@@ -1,12 +1,11 @@
-#import "ee_else_ce/boards/graphql/board.fragment.graphql"
-
query group_recent_boards($fullPath: ID!) {
group(fullPath: $fullPath) {
id
recentIssueBoards {
edges {
node {
- ...BoardFragment
+ id
+ name
}
}
}
diff --git a/app/assets/javascripts/boards/graphql/project_boards.query.graphql b/app/assets/javascripts/boards/graphql/project_boards.query.graphql
index b8879bc260c..770c246a95b 100644
--- a/app/assets/javascripts/boards/graphql/project_boards.query.graphql
+++ b/app/assets/javascripts/boards/graphql/project_boards.query.graphql
@@ -1,12 +1,11 @@
-#import "ee_else_ce/boards/graphql/board.fragment.graphql"
-
query project_boards($fullPath: ID!) {
project(fullPath: $fullPath) {
id
boards {
edges {
node {
- ...BoardFragment
+ id
+ name
}
}
}
diff --git a/app/assets/javascripts/boards/graphql/project_recent_boards.query.graphql b/app/assets/javascripts/boards/graphql/project_recent_boards.query.graphql
index 4d38e9b0498..c633107a409 100644
--- a/app/assets/javascripts/boards/graphql/project_recent_boards.query.graphql
+++ b/app/assets/javascripts/boards/graphql/project_recent_boards.query.graphql
@@ -1,12 +1,11 @@
-#import "ee_else_ce/boards/graphql/board.fragment.graphql"
-
query project_recent_boards($fullPath: ID!) {
project(fullPath: $fullPath) {
id
recentIssueBoards {
edges {
node {
- ...BoardFragment
+ id
+ name
}
}
}
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index 791182af806..c2e346da606 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -15,6 +15,7 @@ import {
FilterFields,
ListTypeTitles,
DraggableItemTypes,
+ DEFAULT_BOARD_LIST_ITEMS_SIZE,
} from 'ee_else_ce/boards/constants';
import {
formatIssueInput,
@@ -429,7 +430,7 @@ export default {
filters: filterParams,
isGroup: boardType === BoardType.group,
isProject: boardType === BoardType.project,
- first: 10,
+ first: DEFAULT_BOARD_LIST_ITEMS_SIZE,
after: fetchNext ? state.pageInfoByListId[listId].endCursor : undefined,
};
@@ -478,16 +479,25 @@ export default {
toListId,
moveBeforeId,
moveAfterId,
+ positionInList,
+ allItemsLoadedInList,
} = moveData;
commit(types.REMOVE_BOARD_ITEM_FROM_LIST, { itemId, listId: fromListId });
+ if (reordering && !allItemsLoadedInList && positionInList === -1) {
+ return;
+ }
+
if (reordering) {
commit(types.ADD_BOARD_ITEM_TO_LIST, {
itemId,
listId: toListId,
moveBeforeId,
moveAfterId,
+ positionInList,
+ atIndex: originalIndex,
+ allItemsLoadedInList,
});
return;
@@ -499,6 +509,7 @@ export default {
listId: toListId,
moveBeforeId,
moveAfterId,
+ positionInList,
});
}
@@ -552,7 +563,15 @@ export default {
updateIssueOrder: async ({ commit, dispatch, state }, { moveData, mutationVariables = {} }) => {
try {
- const { itemId, fromListId, toListId, moveBeforeId, moveAfterId, itemNotInToList } = moveData;
+ const {
+ itemId,
+ fromListId,
+ toListId,
+ moveBeforeId,
+ moveAfterId,
+ itemNotInToList,
+ positionInList,
+ } = moveData;
const {
fullBoardId,
filterParams,
@@ -561,6 +580,8 @@ export default {
},
} = state;
+ commit(types.MUTATE_ISSUE_IN_PROGRESS, true);
+
const { data } = await gqlClient.mutate({
mutation: issueMoveListMutation,
variables: {
@@ -571,6 +592,7 @@ export default {
toListId: getIdFromGraphQLId(toListId),
moveBeforeId: moveBeforeId ? getIdFromGraphQLId(moveBeforeId) : undefined,
moveAfterId: moveAfterId ? getIdFromGraphQLId(moveAfterId) : undefined,
+ positionInList,
// 'mutationVariables' allows EE code to pass in extra parameters.
...mutationVariables,
},
@@ -642,7 +664,9 @@ export default {
}
commit(types.MUTATE_ISSUE_SUCCESS, { issue: data.issueMoveList.issue });
+ commit(types.MUTATE_ISSUE_IN_PROGRESS, false);
} catch {
+ commit(types.MUTATE_ISSUE_IN_PROGRESS, false);
commit(
types.SET_ERROR,
s__('Boards|An error occurred while moving the issue. Please try again.'),
diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js
index 43268f21f96..0e496677b7b 100644
--- a/app/assets/javascripts/boards/stores/mutation_types.js
+++ b/app/assets/javascripts/boards/stores/mutation_types.js
@@ -44,3 +44,4 @@ export const ADD_LIST_TO_HIGHLIGHTED_LISTS = 'ADD_LIST_TO_HIGHLIGHTED_LISTS';
export const REMOVE_LIST_FROM_HIGHLIGHTED_LISTS = 'REMOVE_LIST_FROM_HIGHLIGHTED_LISTS';
export const RESET_BOARD_ITEM_SELECTION = 'RESET_BOARD_ITEM_SELECTION';
export const SET_ERROR = 'SET_ERROR';
+export const MUTATE_ISSUE_IN_PROGRESS = 'MUTATE_ISSUE_IN_PROGRESS';
diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js
index 26a98a645b3..44abb2030c7 100644
--- a/app/assets/javascripts/boards/stores/mutations.js
+++ b/app/assets/javascripts/boards/stores/mutations.js
@@ -20,17 +20,28 @@ export const removeItemFromList = ({ state, listId, itemId }) => {
updateListItemsCount({ state, listId, value: -1 });
};
-export const addItemToList = ({ state, listId, itemId, moveBeforeId, moveAfterId, atIndex }) => {
+export const addItemToList = ({
+ state,
+ listId,
+ itemId,
+ moveBeforeId,
+ moveAfterId,
+ atIndex,
+ positionInList,
+}) => {
const listIssues = state.boardItemsByListId[listId];
let newIndex = atIndex || 0;
+ const moveToStartOrLast = positionInList !== undefined;
if (moveBeforeId) {
newIndex = listIssues.indexOf(moveBeforeId) + 1;
} else if (moveAfterId) {
newIndex = listIssues.indexOf(moveAfterId);
+ } else if (moveToStartOrLast) {
+ newIndex = positionInList === -1 ? listIssues.length : 0;
}
listIssues.splice(newIndex, 0, itemId);
Vue.set(state.boardItemsByListId, listId, listIssues);
- updateListItemsCount({ state, listId, value: 1 });
+ updateListItemsCount({ state, listId, value: moveToStartOrLast ? 0 : 1 });
};
export default {
@@ -205,12 +216,34 @@ export default {
Vue.set(state.boardItems, issue.id, formatIssue(issue));
},
+ [mutationTypes.MUTATE_ISSUE_IN_PROGRESS](state, isLoading) {
+ state.isUpdateIssueOrderInProgress = isLoading;
+ },
+
[mutationTypes.ADD_BOARD_ITEM_TO_LIST]: (
state,
- { itemId, listId, moveBeforeId, moveAfterId, atIndex, inProgress = false },
+ {
+ itemId,
+ listId,
+ moveBeforeId,
+ moveAfterId,
+ atIndex,
+ positionInList,
+ allItemsLoadedInList,
+ inProgress = false,
+ },
) => {
Vue.set(state.listsFlags, listId, { ...state.listsFlags, addItemToListInProgress: inProgress });
- addItemToList({ state, listId, itemId, moveBeforeId, moveAfterId, atIndex });
+ addItemToList({
+ state,
+ listId,
+ itemId,
+ moveBeforeId,
+ moveAfterId,
+ atIndex,
+ positionInList,
+ allItemsLoadedInList,
+ });
},
[mutationTypes.REMOVE_BOARD_ITEM_FROM_LIST]: (state, { itemId, listId }) => {
diff --git a/app/assets/javascripts/boards/stores/state.js b/app/assets/javascripts/boards/stores/state.js
index b62c032b921..bf3f777ea7d 100644
--- a/app/assets/javascripts/boards/stores/state.js
+++ b/app/assets/javascripts/boards/stores/state.js
@@ -40,4 +40,5 @@ export default () => ({
},
// TODO: remove after ce/ee split of board_content.vue
isShowingEpicsSwimlanes: false,
+ isUpdateIssueOrderInProgress: false,
});
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue b/app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue
index 83bad9eb518..59ddf4b19d8 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue
@@ -11,11 +11,11 @@ import {
import addAdminVariable from '../graphql/mutations/admin_add_variable.mutation.graphql';
import deleteAdminVariable from '../graphql/mutations/admin_delete_variable.mutation.graphql';
import updateAdminVariable from '../graphql/mutations/admin_update_variable.mutation.graphql';
-import ciVariableSettings from './ci_variable_settings.vue';
+import CiVariableSettings from './ci_variable_settings.vue';
export default {
components: {
- ciVariableSettings,
+ CiVariableSettings,
},
inject: ['endpoint'],
data() {
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 3af83ffa8ed..3522243e3e7 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
@@ -14,11 +14,11 @@ import {
import addGroupVariable from '../graphql/mutations/group_add_variable.mutation.graphql';
import deleteGroupVariable from '../graphql/mutations/group_delete_variable.mutation.graphql';
import updateGroupVariable from '../graphql/mutations/group_update_variable.mutation.graphql';
-import ciVariableSettings from './ci_variable_settings.vue';
+import CiVariableSettings from './ci_variable_settings.vue';
export default {
components: {
- ciVariableSettings,
+ CiVariableSettings,
},
mixins: [glFeatureFlagsMixin()],
inject: ['endpoint', 'groupPath', 'groupId'],
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
new file mode 100644
index 00000000000..29db02a3c59
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/components/ci_project_variables.vue
@@ -0,0 +1,120 @@
+<script>
+import createFlash from '~/flash';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
+import getProjectEnvironments from '../graphql/queries/project_environments.query.graphql';
+import getProjectVariables from '../graphql/queries/project_variables.query.graphql';
+import { mapEnvironmentNames } from '../utils';
+import {
+ ADD_MUTATION_ACTION,
+ DELETE_MUTATION_ACTION,
+ GRAPHQL_PROJECT_TYPE,
+ UPDATE_MUTATION_ACTION,
+ environmentFetchErrorText,
+ genericMutationErrorText,
+ variableFetchErrorText,
+} from '../constants';
+import addProjectVariable from '../graphql/mutations/project_add_variable.mutation.graphql';
+import deleteProjectVariable from '../graphql/mutations/project_delete_variable.mutation.graphql';
+import updateProjectVariable from '../graphql/mutations/project_update_variable.mutation.graphql';
+import CiVariableSettings from './ci_variable_settings.vue';
+
+export default {
+ components: {
+ CiVariableSettings,
+ },
+ inject: ['endpoint', 'projectFullPath', 'projectId'],
+ data() {
+ return {
+ projectEnvironments: [],
+ projectVariables: [],
+ };
+ },
+ apollo: {
+ projectEnvironments: {
+ query: getProjectEnvironments,
+ variables() {
+ return {
+ fullPath: this.projectFullPath,
+ };
+ },
+ update(data) {
+ return mapEnvironmentNames(data?.project?.environments?.nodes);
+ },
+ error() {
+ createFlash({ message: environmentFetchErrorText });
+ },
+ },
+ projectVariables: {
+ query: getProjectVariables,
+ variables() {
+ return {
+ fullPath: this.projectFullPath,
+ };
+ },
+ update(data) {
+ return data?.project?.ciVariables?.nodes || [];
+ },
+ error() {
+ createFlash({ message: variableFetchErrorText });
+ },
+ },
+ },
+ computed: {
+ isLoading() {
+ return (
+ this.$apollo.queries.projectVariables.loading ||
+ this.$apollo.queries.projectEnvironments.loading
+ );
+ },
+ },
+ methods: {
+ addVariable(variable) {
+ this.variableMutation(ADD_MUTATION_ACTION, variable);
+ },
+ deleteVariable(variable) {
+ this.variableMutation(DELETE_MUTATION_ACTION, variable);
+ },
+ updateVariable(variable) {
+ this.variableMutation(UPDATE_MUTATION_ACTION, variable);
+ },
+ async variableMutation(mutationAction, variable) {
+ try {
+ const currentMutation = this.$options.mutationData[mutationAction];
+ const { data } = await this.$apollo.mutate({
+ mutation: currentMutation.action,
+ variables: {
+ endpoint: this.endpoint,
+ fullPath: this.projectFullPath,
+ projectId: convertToGraphQLId(GRAPHQL_PROJECT_TYPE, this.projectId),
+ variable,
+ },
+ });
+
+ const { errors } = data[currentMutation.name];
+ if (errors.length > 0) {
+ createFlash({ message: errors[0] });
+ }
+ } catch (e) {
+ createFlash({ message: genericMutationErrorText });
+ }
+ },
+ },
+ mutationData: {
+ [ADD_MUTATION_ACTION]: { action: addProjectVariable, name: 'addProjectVariable' },
+ [UPDATE_MUTATION_ACTION]: { action: updateProjectVariable, name: 'updateProjectVariable' },
+ [DELETE_MUTATION_ACTION]: { action: deleteProjectVariable, name: 'deleteProjectVariable' },
+ },
+};
+</script>
+
+<template>
+ <ci-variable-settings
+ :are-scoped-variables-available="true"
+ :environments="projectEnvironments"
+ :is-loading="isLoading"
+ :variables="projectVariables"
+ @add-variable="addVariable"
+ @delete-variable="deleteVariable"
+ @update-variable="updateVariable"
+ />
+</template>
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 5ba63de8c96..56c1804910a 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
@@ -108,7 +108,6 @@ export default {
return {
newEnvironments: [],
isTipDismissed: getCookie(AWS_TIP_DISMISSED_COOKIE_NAME) === 'true',
- typeOptions: variableOptions,
validationErrorEventProperty: '',
variable: { ...defaultVariableState, ...this.selectedVariable },
};
@@ -259,6 +258,7 @@ export default {
},
},
defaultScope: allEnvironments.text,
+ variableOptions,
};
</script>
@@ -277,6 +277,7 @@ export default {
v-model="variable.key"
:token-list="$options.tokenList"
:label-text="__('Key')"
+ data-testid="pipeline-form-ci-variable-key"
data-qa-selector="ci_variable_key_field"
/>
@@ -293,21 +294,26 @@ export default {
:state="variableValidationState"
rows="3"
max-rows="6"
+ data-testid="pipeline-form-ci-variable-value"
data-qa-selector="ci_variable_value_field"
class="gl-font-monospace!"
/>
</gl-form-group>
- <div class="d-flex">
- <gl-form-group :label="__('Type')" label-for="ci-variable-type" class="w-50 gl-mr-5">
+ <div class="gl-display-flex">
+ <gl-form-group :label="__('Type')" label-for="ci-variable-type" class="gl-w-half gl-mr-5">
<gl-form-select
id="ci-variable-type"
v-model="variable.variableType"
- :options="typeOptions"
+ :options="$options.variableOptions"
/>
</gl-form-group>
- <gl-form-group label-for="ci-variable-env" class="w-50" data-testid="environment-scope">
+ <gl-form-group
+ label-for="ci-variable-env"
+ class="gl-w-half"
+ data-testid="environment-scope"
+ >
<template #label>
{{ __('Environment scope') }}
<gl-link
@@ -380,7 +386,7 @@ export default {
data-testid="aws-guidance-tip"
@dismiss="dismissTip"
>
- <div class="gl-display-flex gl-flex-direction-row">
+ <div class="gl-display-flex gl-flex-direction-row gl-flex-wrap-wrap gl-md-flex-wrap-nowrap">
<div>
<p>
<gl-sprintf :message="$options.awsTipMessage">
diff --git a/app/assets/javascripts/ci_variable_list/components/legacy_ci_variable_modal.vue b/app/assets/javascripts/ci_variable_list/components/legacy_ci_variable_modal.vue
index cebb7eb85ac..1fbe52388c9 100644
--- a/app/assets/javascripts/ci_variable_list/components/legacy_ci_variable_modal.vue
+++ b/app/assets/javascripts/ci_variable_list/components/legacy_ci_variable_modal.vue
@@ -255,6 +255,7 @@ export default {
v-model="key"
:token-list="$options.tokenList"
:label-text="__('Key')"
+ data-testid="pipeline-form-ci-variable-key"
data-qa-selector="ci_variable_key_field"
/>
@@ -271,6 +272,7 @@ export default {
:state="variableValidationState"
rows="3"
max-rows="6"
+ data-testid="pipeline-form-ci-variable-value"
data-qa-selector="ci_variable_value_field"
class="gl-font-monospace!"
/>
diff --git a/app/assets/javascripts/ci_variable_list/constants.js b/app/assets/javascripts/ci_variable_list/constants.js
index 5d22974ffbb..e2dd28cdaa1 100644
--- a/app/assets/javascripts/ci_variable_list/constants.js
+++ b/app/assets/javascripts/ci_variable_list/constants.js
@@ -10,7 +10,7 @@ export const displayText = {
};
export const variableTypes = {
- variableType: 'ENV_VAR',
+ envType: 'ENV_VAR',
fileType: 'FILE',
};
@@ -29,13 +29,13 @@ export const allEnvironments = {
export const variableText = {
[types.variableType]: __('Variable'),
[types.fileType]: __('File'),
- [variableTypes.variableType]: __('Variable'),
+ [variableTypes.envType]: __('Variable'),
[variableTypes.fileType]: __('File'),
};
export const variableOptions = [
- { value: types.variableType, text: variableText[types.variableType] },
- { value: types.fileType, text: variableText[types.fileType] },
+ { value: variableTypes.envType, text: variableText[variableTypes.envType] },
+ { value: variableTypes.fileType, text: variableText[variableTypes.fileType] },
];
export const defaultVariableState = {
@@ -44,7 +44,7 @@ export const defaultVariableState = {
masked: false,
protected: false,
value: '',
- variableType: types.variableType,
+ variableType: variableTypes.envType,
};
// eslint-disable-next-line @gitlab/require-i18n-strings
diff --git a/app/assets/javascripts/ci_variable_list/graphql/mutations/client/add_project_environment.mutation.graphql b/app/assets/javascripts/ci_variable_list/graphql/mutations/client/add_project_environment.mutation.graphql
new file mode 100644
index 00000000000..45109762e80
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/graphql/mutations/client/add_project_environment.mutation.graphql
@@ -0,0 +1,3 @@
+mutation addProjectEnvironment($environment: CiEnvironment, $fullPath: ID!) {
+ addProjectEnvironment(environment: $environment, fullPath: $fullPath) @client
+}
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
new file mode 100644
index 00000000000..ab3a46da854
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/graphql/mutations/project_add_variable.mutation.graphql
@@ -0,0 +1,30 @@
+#import "~/ci_variable_list/graphql/fragments/ci_variable.fragment.graphql"
+
+mutation addProjectVariable(
+ $variable: CiVariable!
+ $endpoint: String!
+ $fullPath: ID!
+ $projectId: ID!
+) {
+ addProjectVariable(
+ variable: $variable
+ endpoint: $endpoint
+ fullPath: $fullPath
+ projectId: $projectId
+ ) @client {
+ project {
+ id
+ ciVariables {
+ nodes {
+ ...BaseCiVariable
+ ... on CiProjectVariable {
+ environmentScope
+ masked
+ protected
+ }
+ }
+ }
+ }
+ errors
+ }
+}
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
new file mode 100644
index 00000000000..e83dc9a5e5e
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/graphql/mutations/project_delete_variable.mutation.graphql
@@ -0,0 +1,30 @@
+#import "~/ci_variable_list/graphql/fragments/ci_variable.fragment.graphql"
+
+mutation deleteProjectVariable(
+ $variable: CiVariable!
+ $endpoint: String!
+ $fullPath: ID!
+ $projectId: ID!
+) {
+ deleteProjectVariable(
+ variable: $variable
+ endpoint: $endpoint
+ fullPath: $fullPath
+ projectId: $projectId
+ ) @client {
+ project {
+ id
+ ciVariables {
+ nodes {
+ ...BaseCiVariable
+ ... on CiProjectVariable {
+ environmentScope
+ masked
+ protected
+ }
+ }
+ }
+ }
+ errors
+ }
+}
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
new file mode 100644
index 00000000000..4788911431b
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/graphql/mutations/project_update_variable.mutation.graphql
@@ -0,0 +1,30 @@
+#import "~/ci_variable_list/graphql/fragments/ci_variable.fragment.graphql"
+
+mutation updateProjectVariable(
+ $variable: CiVariable!
+ $endpoint: String!
+ $fullPath: ID!
+ $projectId: ID!
+) {
+ updateProjectVariable(
+ variable: $variable
+ endpoint: $endpoint
+ fullPath: $fullPath
+ projectId: $projectId
+ ) @client {
+ project {
+ id
+ ciVariables {
+ nodes {
+ ...BaseCiVariable
+ ... on CiProjectVariable {
+ environmentScope
+ masked
+ protected
+ }
+ }
+ }
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/ci_variable_list/graphql/queries/project_environments.query.graphql b/app/assets/javascripts/ci_variable_list/graphql/queries/project_environments.query.graphql
new file mode 100644
index 00000000000..921e0ca25b9
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/graphql/queries/project_environments.query.graphql
@@ -0,0 +1,11 @@
+query getProjectEnvironments($fullPath: ID!) {
+ project(fullPath: $fullPath) {
+ id
+ environments {
+ nodes {
+ id
+ name
+ }
+ }
+ }
+}
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
new file mode 100644
index 00000000000..a60a50e4bc4
--- /dev/null
+++ b/app/assets/javascripts/ci_variable_list/graphql/queries/project_variables.query.graphql
@@ -0,0 +1,15 @@
+#import "~/ci_variable_list/graphql/fragments/ci_variable.fragment.graphql"
+
+query getProjectVariables($fullPath: ID!) {
+ project(fullPath: $fullPath) {
+ id
+ ciVariables {
+ nodes {
+ ...BaseCiVariable
+ environmentScope
+ masked
+ protected
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/ci_variable_list/graphql/resolvers.js b/app/assets/javascripts/ci_variable_list/graphql/resolvers.js
index be7e3f88cfd..c041531ae30 100644
--- a/app/assets/javascripts/ci_variable_list/graphql/resolvers.js
+++ b/app/assets/javascripts/ci_variable_list/graphql/resolvers.js
@@ -4,9 +4,16 @@ import {
convertObjectPropsToSnakeCase,
} from '../../lib/utils/common_utils';
import { getIdFromGraphQLId } from '../../graphql_shared/utils';
-import { GRAPHQL_GROUP_TYPE, groupString, instanceString } from '../constants';
-import getAdminVariables from './queries/variables.query.graphql';
+import {
+ GRAPHQL_GROUP_TYPE,
+ GRAPHQL_PROJECT_TYPE,
+ groupString,
+ instanceString,
+ projectString,
+} from '../constants';
+import getProjectVariables from './queries/project_variables.query.graphql';
import getGroupVariables from './queries/group_variables.query.graphql';
+import getAdminVariables from './queries/variables.query.graphql';
const prepareVariableForApi = ({ variable, destroy = false }) => {
return {
@@ -28,6 +35,20 @@ const mapVariableTypes = (variables = [], kind) => {
});
};
+const prepareProjectGraphQLResponse = ({ data, projectId, errors = [] }) => {
+ return {
+ errors,
+ project: {
+ __typename: GRAPHQL_PROJECT_TYPE,
+ id: projectId,
+ ciVariables: {
+ __typename: 'CiVariableConnection',
+ nodes: mapVariableTypes(data.variables, projectString),
+ },
+ },
+ };
+};
+
const prepareGroupGraphQLResponse = ({ data, groupId, errors = [] }) => {
return {
errors,
@@ -52,6 +73,28 @@ const prepareAdminGraphQLResponse = ({ data, errors = [] }) => {
};
};
+const callProjectEndpoint = async ({
+ endpoint,
+ fullPath,
+ variable,
+ projectId,
+ cache,
+ destroy = false,
+}) => {
+ try {
+ const { data } = await axios.patch(endpoint, {
+ variables_attributes: [prepareVariableForApi({ variable, destroy })],
+ });
+ return prepareProjectGraphQLResponse({ data, projectId });
+ } catch (e) {
+ return prepareProjectGraphQLResponse({
+ data: cache.readQuery({ query: getProjectVariables, variables: { fullPath } }),
+ projectId,
+ errors: [...e.response.data],
+ });
+ }
+};
+
const callGroupEndpoint = async ({
endpoint,
fullPath,
@@ -91,6 +134,15 @@ const callAdminEndpoint = async ({ endpoint, variable, cache, destroy = false })
export const resolvers = {
Mutation: {
+ addProjectVariable: async (_, { endpoint, fullPath, variable, projectId }, { cache }) => {
+ return callProjectEndpoint({ endpoint, fullPath, variable, projectId, cache });
+ },
+ updateProjectVariable: async (_, { endpoint, fullPath, variable, projectId }, { cache }) => {
+ return callProjectEndpoint({ endpoint, fullPath, variable, projectId, cache });
+ },
+ deleteProjectVariable: async (_, { endpoint, fullPath, variable, projectId }, { cache }) => {
+ return callProjectEndpoint({ endpoint, fullPath, variable, projectId, cache, destroy: true });
+ },
addGroupVariable: async (_, { endpoint, fullPath, variable, groupId }, { cache }) => {
return callGroupEndpoint({ endpoint, fullPath, variable, groupId, cache });
},
diff --git a/app/assets/javascripts/ci_variable_list/index.js b/app/assets/javascripts/ci_variable_list/index.js
index a74af8aed12..f5bdd4c7b1e 100644
--- a/app/assets/javascripts/ci_variable_list/index.js
+++ b/app/assets/javascripts/ci_variable_list/index.js
@@ -4,6 +4,7 @@ import createDefaultClient from '~/lib/graphql';
import { parseBoolean } from '~/lib/utils/common_utils';
import CiAdminVariables from './components/ci_admin_variables.vue';
import CiGroupVariables from './components/ci_group_variables.vue';
+import CiProjectVariables from './components/ci_project_variables.vue';
import LegacyCiVariableSettings from './components/legacy_ci_variable_settings.vue';
import { resolvers } from './graphql/resolvers';
import createStore from './store';
@@ -37,6 +38,8 @@ const mountCiVariableListApp = (containerEl) => {
if (parsedIsGroup) {
component = CiGroupVariables;
+ } else if (parsedIsProject) {
+ component = CiProjectVariables;
}
Vue.use(VueApollo);
@@ -77,7 +80,7 @@ const mountLegacyCiVariableListApp = (containerEl) => {
const {
endpoint,
projectId,
- group,
+ isGroup,
maskableRegex,
protectedByDefault,
awsLogoSvgPath,
@@ -89,13 +92,13 @@ const mountLegacyCiVariableListApp = (containerEl) => {
maskedEnvironmentVariablesLink,
environmentScopeLink,
} = containerEl.dataset;
- const isGroup = parseBoolean(group);
+ const parsedIsGroup = parseBoolean(isGroup);
const isProtectedByDefault = parseBoolean(protectedByDefault);
const store = createStore({
endpoint,
projectId,
- isGroup,
+ isGroup: parsedIsGroup,
maskableRegex,
isProtectedByDefault,
awsLogoSvgPath,
diff --git a/app/assets/javascripts/clusters/agents/components/agent_integration_status_row.vue b/app/assets/javascripts/clusters/agents/components/agent_integration_status_row.vue
new file mode 100644
index 00000000000..59de6df1e49
--- /dev/null
+++ b/app/assets/javascripts/clusters/agents/components/agent_integration_status_row.vue
@@ -0,0 +1,66 @@
+<script>
+import { GlLink, GlIcon, GlBadge } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+
+export default {
+ components: {
+ GlLink,
+ GlIcon,
+ GlBadge,
+ },
+ mixins: [glFeatureFlagMixin()],
+ i18n: {
+ premiumTitle: s__('ClusterAgents|Premium'),
+ },
+ props: {
+ text: {
+ required: true,
+ type: String,
+ },
+ icon: {
+ required: false,
+ type: String,
+ default: 'information',
+ },
+ iconClass: {
+ required: false,
+ type: String,
+ default: 'text-info',
+ },
+ helpUrl: {
+ required: false,
+ type: String,
+ default: null,
+ },
+ featureName: {
+ required: false,
+ type: String,
+ default: null,
+ },
+ },
+ computed: {
+ showPremiumBadge() {
+ return this.featureName && !this.glFeatures[this.featureName];
+ },
+ },
+};
+</script>
+
+<template>
+ <li class="gl-mb-3">
+ <gl-icon :name="icon" :size="16" :class="iconClass" class="gl-mr-2" />
+
+ <gl-link v-if="helpUrl" :href="helpUrl">{{ text }}</gl-link>
+ <span v-else>{{ text }}</span>
+
+ <gl-badge
+ v-if="showPremiumBadge"
+ size="md"
+ class="gl-ml-2 gl-vertical-align-middle"
+ icon="license"
+ variant="tier"
+ >{{ $options.i18n.premiumTitle }}</gl-badge
+ >
+ </li>
+</template>
diff --git a/app/assets/javascripts/clusters/agents/components/integration_status.vue b/app/assets/javascripts/clusters/agents/components/integration_status.vue
new file mode 100644
index 00000000000..68a77dfbc8e
--- /dev/null
+++ b/app/assets/javascripts/clusters/agents/components/integration_status.vue
@@ -0,0 +1,98 @@
+<script>
+import { GlCollapse, GlButton, GlIcon } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import { AGENT_STATUSES } from '~/clusters_list/constants';
+import { getAgentLastContact, getAgentStatus } from '~/clusters_list/clusters_util';
+import {
+ INTEGRATION_STATUS_VALID_TOKEN,
+ INTEGRATION_STATUS_NO_TOKEN,
+ INTEGRATION_STATUS_RESTRICTED_CI_CD,
+} from '../constants';
+import AgentIntegrationStatusRow from './agent_integration_status_row.vue';
+
+export default {
+ components: {
+ GlCollapse,
+ GlButton,
+ GlIcon,
+ AgentIntegrationStatusRow,
+ },
+ i18n: {
+ title: s__('ClusterAgents|Integration Status'),
+ },
+ AGENT_STATUSES,
+ props: {
+ tokens: {
+ required: true,
+ type: Array,
+ },
+ },
+ data() {
+ return {
+ isVisible: false,
+ };
+ },
+ computed: {
+ chevronIcon() {
+ return this.isVisible ? 'chevron-down' : 'chevron-right';
+ },
+ agentStatus() {
+ const lastContact = getAgentLastContact(this.tokens);
+ return getAgentStatus(lastContact);
+ },
+ integrationStatuses() {
+ const statuses = [];
+
+ if (this.agentStatus === 'active') {
+ statuses.push(INTEGRATION_STATUS_VALID_TOKEN);
+ }
+
+ if (!this.tokens.length) {
+ statuses.push(INTEGRATION_STATUS_NO_TOKEN);
+ }
+
+ statuses.push(INTEGRATION_STATUS_RESTRICTED_CI_CD);
+
+ return statuses;
+ },
+ },
+ methods: {
+ toggleCollapse() {
+ this.isVisible = !this.isVisible;
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-button
+ :icon="chevronIcon"
+ variant="link"
+ size="small"
+ class="gl-mr-3"
+ @click="toggleCollapse"
+ >
+ {{ $options.i18n.title }} </gl-button
+ ><span data-testid="agent-status">
+ <gl-icon
+ :name="$options.AGENT_STATUSES[agentStatus].icon"
+ :class="$options.AGENT_STATUSES[agentStatus].class"
+ class="gl-mr-2"
+ />{{ $options.AGENT_STATUSES[agentStatus].name }}
+ </span>
+ <gl-collapse v-model="isVisible" class="gl-ml-5 gl-mt-5">
+ <ul class="gl-list-style-none gl-pl-2 gl-mb-0">
+ <agent-integration-status-row
+ v-for="(status, index) in integrationStatuses"
+ :key="index"
+ :icon="status.icon"
+ :icon-class="status.iconClass"
+ :text="status.text"
+ :help-url="status.helpUrl"
+ :feature-name="status.featureName"
+ />
+ </ul>
+ </gl-collapse>
+ </div>
+</template>
diff --git a/app/assets/javascripts/clusters/agents/components/show.vue b/app/assets/javascripts/clusters/agents/components/show.vue
index e3de8339325..f1bd36b4a63 100644
--- a/app/assets/javascripts/clusters/agents/components/show.vue
+++ b/app/assets/javascripts/clusters/agents/components/show.vue
@@ -14,6 +14,7 @@ import { MAX_LIST_COUNT, TOKEN_STATUS_ACTIVE } from '../constants';
import getClusterAgentQuery from '../graphql/queries/get_cluster_agent.query.graphql';
import TokenTable from './token_table.vue';
import ActivityEvents from './activity_events_list.vue';
+import IntegrationStatus from './integration_status.vue';
export default {
i18n: {
@@ -51,6 +52,7 @@ export default {
TimeAgoTooltip,
TokenTable,
ActivityEvents,
+ IntegrationStatus,
},
inject: ['agentName', 'projectPath'],
data() {
@@ -105,11 +107,11 @@ export default {
<template>
<section>
- <h2>{{ agentName }}</h2>
+ <h1>{{ agentName }}</h1>
<gl-loading-icon v-if="isLoading && clusterAgent == null" size="lg" class="gl-m-3" />
- <div v-else-if="clusterAgent">
+ <template v-else-if="clusterAgent">
<p data-testid="cluster-agent-create-info">
<gl-sprintf :message="$options.i18n.installedInfo">
<template #name>
@@ -122,7 +124,16 @@ export default {
</gl-sprintf>
</p>
- <gl-tabs sync-active-tab-with-query-params lazy>
+ <integration-status
+ :tokens="tokens"
+ class="gl-py-5 gl-border-t-1 gl-border-t-solid gl-border-t-gray-100"
+ />
+
+ <gl-tabs
+ sync-active-tab-with-query-params
+ lazy
+ class="gl-border-t-1 gl-border-t-solid gl-border-t-gray-100"
+ >
<gl-tab :title="$options.i18n.activity" query-param-value="activity">
<activity-events :agent-name="agentName" :project-path="projectPath" />
</gl-tab>
@@ -151,7 +162,7 @@ export default {
</div>
</gl-tab>
</gl-tabs>
- </div>
+ </template>
<gl-alert v-else variant="danger" :dismissible="false">
{{ $options.i18n.loadingError }}
diff --git a/app/assets/javascripts/clusters/agents/components/token_table.vue b/app/assets/javascripts/clusters/agents/components/token_table.vue
index f74d66f6b8f..667d10e1753 100644
--- a/app/assets/javascripts/clusters/agents/components/token_table.vue
+++ b/app/assets/javascripts/clusters/agents/components/token_table.vue
@@ -44,36 +44,43 @@ export default {
},
computed: {
fields() {
+ const tdClass = 'gl-vertical-align-middle!';
return [
{
key: 'name',
label: this.$options.i18n.name,
tdAttr: { 'data-testid': 'agent-token-name' },
+ tdClass,
},
{
key: 'lastUsed',
label: this.$options.i18n.lastUsed,
tdAttr: { 'data-testid': 'agent-token-used' },
+ tdClass,
},
{
key: 'createdAt',
label: this.$options.i18n.dateCreated,
tdAttr: { 'data-testid': 'agent-token-created-time' },
+ tdClass,
},
{
key: 'createdBy',
label: this.$options.i18n.createdBy,
tdAttr: { 'data-testid': 'agent-token-created-user' },
+ tdClass,
},
{
key: 'description',
label: this.$options.i18n.description,
tdAttr: { 'data-testid': 'agent-token-description' },
+ tdClass,
},
{
key: 'actions',
label: '',
tdAttr: { 'data-testid': 'agent-token-revoke' },
+ tdClass,
},
];
},
diff --git a/app/assets/javascripts/clusters/agents/constants.js b/app/assets/javascripts/clusters/agents/constants.js
index 962fa243903..76af552181f 100644
--- a/app/assets/javascripts/clusters/agents/constants.js
+++ b/app/assets/javascripts/clusters/agents/constants.js
@@ -1,4 +1,5 @@
import { s__ } from '~/locale';
+import { helpPagePath } from '~/helpers/help_page_helper';
export const MAX_LIST_COUNT = 25;
@@ -46,3 +47,24 @@ export const EVENT_ACTIONS_CLICK = 'click_button';
export const TOKEN_NAME_LIMIT = 255;
export const REVOKE_TOKEN_MODAL_ID = 'revoke-token-%{tokenName}';
+
+export const INTEGRATION_STATUS_VALID_TOKEN = {
+ icon: 'status-success',
+ iconClass: 'text-success-500',
+ text: s__('ClusterAgents|Valid access token'),
+};
+export const INTEGRATION_STATUS_NO_TOKEN = {
+ icon: 'status-alert',
+ iconClass: 'text-danger-500',
+ text: s__('ClusterAgents|No agent access token'),
+};
+
+export const INTEGRATION_STATUS_RESTRICTED_CI_CD = {
+ icon: 'information',
+ iconClass: 'text-info',
+ text: s__('ClusterAgents|CI/CD workflow with restricted access'),
+ helpUrl: helpPagePath('user/clusters/agent/ci_cd_workflow', {
+ anchor: 'restrict-project-and-group-access-by-using-impersonation',
+ }),
+ featureName: 'clusterAgentsCiImpersonation',
+};
diff --git a/app/assets/javascripts/clusters_list/clusters_util.js b/app/assets/javascripts/clusters_list/clusters_util.js
index e2d01723dde..ee36a295513 100644
--- a/app/assets/javascripts/clusters_list/clusters_util.js
+++ b/app/assets/javascripts/clusters_list/clusters_util.js
@@ -1,3 +1,5 @@
+import { ACTIVE_CONNECTION_TIME } from './constants';
+
export function generateAgentRegistrationCommand({ name, token, version, address }) {
return `helm repo add gitlab https://charts.gitlab.io
helm repo update
@@ -12,3 +14,24 @@ helm upgrade --install ${name} gitlab/gitlab-agent \\
export function getAgentConfigPath(clusterAgentName) {
return `.gitlab/agents/${clusterAgentName}`;
}
+
+export function getAgentLastContact(tokens = []) {
+ let lastContact = null;
+ tokens.forEach((token) => {
+ const lastContactToDate = new Date(token.lastUsedAt).getTime();
+ if (lastContactToDate > lastContact) {
+ lastContact = lastContactToDate;
+ }
+ });
+ return lastContact;
+}
+
+export function getAgentStatus(lastContact) {
+ if (lastContact) {
+ const now = new Date().getTime();
+ const diff = now - lastContact;
+
+ return diff >= ACTIVE_CONNECTION_TIME ? 'inactive' : 'active';
+ }
+ return 'unused';
+}
diff --git a/app/assets/javascripts/clusters_list/components/agents.vue b/app/assets/javascripts/clusters_list/components/agents.vue
index 8a4a81d3e96..36f0f8e61ba 100644
--- a/app/assets/javascripts/clusters_list/components/agents.vue
+++ b/app/assets/javascripts/clusters_list/components/agents.vue
@@ -3,13 +3,9 @@ import { GlAlert, GlKeysetPagination, GlLoadingIcon, GlBanner } from '@gitlab/ui
import { s__ } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
-import {
- MAX_LIST_COUNT,
- ACTIVE_CONNECTION_TIME,
- AGENT_FEEDBACK_ISSUE,
- AGENT_FEEDBACK_KEY,
-} from '../constants';
+import { MAX_LIST_COUNT, AGENT_FEEDBACK_ISSUE, AGENT_FEEDBACK_KEY } from '../constants';
import getAgentsQuery from '../graphql/queries/get_agents.query.graphql';
+import { getAgentLastContact, getAgentStatus } from '../clusters_util';
import AgentEmptyState from './agent_empty_state.vue';
import AgentTable from './agent_table.vue';
@@ -88,8 +84,8 @@ export default {
if (list) {
list = list.map((agent) => {
const configFolder = this.folderList[agent.name];
- const lastContact = this.getLastContact(agent);
- const status = this.getStatus(lastContact);
+ const lastContact = getAgentLastContact(agent?.tokens?.nodes);
+ const status = getAgentStatus(lastContact);
return { ...agent, configFolder, lastContact, status };
});
}
@@ -141,28 +137,6 @@ export default {
});
}
},
- getLastContact(agent) {
- const tokens = agent?.tokens?.nodes;
- let lastContact = null;
- if (tokens?.length) {
- tokens.forEach((token) => {
- const lastContactToDate = new Date(token.lastUsedAt).getTime();
- if (lastContactToDate > lastContact) {
- lastContact = lastContactToDate;
- }
- });
- }
- return lastContact;
- },
- getStatus(lastContact) {
- if (lastContact) {
- const now = new Date().getTime();
- const diff = now - lastContact;
-
- return diff > ACTIVE_CONNECTION_TIME ? 'inactive' : 'active';
- }
- return 'unused';
- },
emitAgentsLoaded() {
const count = this.agents?.project?.clusterAgents?.count;
this.$emit('onAgentsLoad', count);
diff --git a/app/assets/javascripts/code_navigation/utils/dom_utils.js b/app/assets/javascripts/code_navigation/utils/dom_utils.js
index 1a65c1a64a2..90af31b715c 100644
--- a/app/assets/javascripts/code_navigation/utils/dom_utils.js
+++ b/app/assets/javascripts/code_navigation/utils/dom_utils.js
@@ -23,6 +23,7 @@ const wrapTextWithSpan = (el, text) => {
const wrapNodes = (text) => {
const wrapper = createSpan();
+ // eslint-disable-next-line no-unsanitized/property
wrapper.innerHTML = wrapSpacesWithSpans(text);
wrapper.childNodes.forEach((el) => wrapTextWithSpan(el, text));
return wrapper.childNodes;
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
index 95ee3a0d90e..6890d7f6f44 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.vue
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
@@ -293,7 +293,7 @@ export default {
</div>
<gl-modal
- v-if="canRenderPipelineButton"
+ v-if="canRenderPipelineButton || shouldRenderEmptyState"
:id="modalId"
ref="modal"
:modal-id="modalId"
diff --git a/app/assets/javascripts/confidential_merge_request/components/dropdown.vue b/app/assets/javascripts/confidential_merge_request/components/dropdown.vue
index 6bb654a434f..9cb7cd9607f 100644
--- a/app/assets/javascripts/confidential_merge_request/components/dropdown.vue
+++ b/app/assets/javascripts/confidential_merge_request/components/dropdown.vue
@@ -40,7 +40,7 @@ export default {
<gl-dropdown-item
v-for="project in projects"
:key="project.id"
- :is-check-item="true"
+ is-check-item
:is-checked="project.id === selectedProject.id"
@click="selectProject(project)"
>
diff --git a/app/assets/javascripts/content_editor/components/bubble_menus/bubble_menu.vue b/app/assets/javascripts/content_editor/components/bubble_menus/bubble_menu.vue
new file mode 100644
index 00000000000..3891274e35e
--- /dev/null
+++ b/app/assets/javascripts/content_editor/components/bubble_menus/bubble_menu.vue
@@ -0,0 +1,60 @@
+<script>
+import { BubbleMenuPlugin } from '@tiptap/extension-bubble-menu';
+
+export default {
+ name: 'BubbleMenu',
+ inject: ['tiptapEditor'],
+ props: {
+ pluginKey: {
+ type: String,
+ required: true,
+ },
+ shouldShow: {
+ type: Function,
+ required: true,
+ },
+ tippyOptions: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ },
+ data() {
+ return {
+ menuVisible: false,
+ };
+ },
+ async mounted() {
+ await this.$nextTick();
+
+ this.tiptapEditor.registerPlugin(
+ BubbleMenuPlugin({
+ pluginKey: this.pluginKey,
+ editor: this.tiptapEditor,
+ element: this.$el,
+ shouldShow: this.shouldShow,
+ tippyOptions: {
+ ...this.tippyOptions,
+ onShow: (...args) => {
+ this.$emit('show', ...args);
+ this.menuVisible = true;
+ },
+ onHidden: (...args) => {
+ this.$emit('hidden', ...args);
+ this.menuVisible = false;
+ },
+ },
+ }),
+ );
+ },
+
+ beforeDestroy() {
+ this.tiptapEditor.unregisterPlugin(this.pluginKey);
+ },
+};
+</script>
+<template>
+ <div>
+ <slot v-if="menuVisible"></slot>
+ </div>
+</template>
diff --git a/app/assets/javascripts/content_editor/components/bubble_menus/code_block.vue b/app/assets/javascripts/content_editor/components/bubble_menus/code_block.vue
deleted file mode 100644
index 6c0ac8e54d2..00000000000
--- a/app/assets/javascripts/content_editor/components/bubble_menus/code_block.vue
+++ /dev/null
@@ -1,275 +0,0 @@
-<script>
-import {
- GlDropdownForm,
- GlFormInput,
- GlDropdownDivider,
- GlButton,
- GlButtonGroup,
- GlDropdown,
- GlDropdownItem,
- GlSearchBoxByType,
- GlTooltipDirective as GlTooltip,
-} from '@gitlab/ui';
-import { BubbleMenu } from '@tiptap/vue-2';
-import { getParentByTagName } from '~/lib/utils/dom_utils';
-import codeBlockLanguageLoader from '../../services/code_block_language_loader';
-import CodeBlockHighlight from '../../extensions/code_block_highlight';
-import Diagram from '../../extensions/diagram';
-import Frontmatter from '../../extensions/frontmatter';
-import EditorStateObserver from '../editor_state_observer.vue';
-
-const CODE_BLOCK_NODE_TYPES = [CodeBlockHighlight.name, Diagram.name, Frontmatter.name];
-
-export default {
- components: {
- BubbleMenu,
- GlDropdownForm,
- GlFormInput,
- GlButton,
- GlButtonGroup,
- GlDropdown,
- GlDropdownItem,
- GlDropdownDivider,
- GlSearchBoxByType,
- EditorStateObserver,
- },
- directives: {
- GlTooltip,
- },
- inject: ['tiptapEditor', 'contentEditor'],
- data() {
- return {
- codeBlockType: undefined,
- filterTerm: '',
- filteredLanguages: [],
-
- showCustomLanguageInput: false,
- customLanguageType: '',
-
- selectedLanguage: {},
- isDiagram: false,
- showPreview: false,
- };
- },
- watch: {
- filterTerm: {
- handler(val) {
- this.filteredLanguages = codeBlockLanguageLoader.filterLanguages(val);
- },
- immediate: true,
- },
- },
- methods: {
- shouldShow: ({ editor }) => {
- return CODE_BLOCK_NODE_TYPES.some((type) => editor.isActive(type));
- },
-
- async updateCodeBlockInfoToState() {
- this.codeBlockType = CODE_BLOCK_NODE_TYPES.find((type) => this.tiptapEditor.isActive(type));
-
- if (!this.codeBlockType) return;
-
- const { language, isDiagram, showPreview } = this.tiptapEditor.getAttributes(
- this.codeBlockType,
- );
- this.selectedLanguage = codeBlockLanguageLoader.findOrCreateLanguageBySyntax(
- language,
- isDiagram,
- );
- this.isDiagram = isDiagram;
- this.showPreview = showPreview;
- },
-
- getCodeBlockText() {
- const { view } = this.tiptapEditor;
- const { from } = this.tiptapEditor.state.selection;
- const node = getParentByTagName(view.domAtPos(from).node, 'pre');
- return node?.textContent || '';
- },
-
- copyCodeBlockText() {
- navigator.clipboard.writeText(this.getCodeBlockText());
- },
-
- togglePreview() {
- this.showPreview = !this.showPreview;
- this.tiptapEditor.commands.updateAttributes(Diagram.name, { showPreview: this.showPreview });
- },
-
- async applyLanguage(language) {
- this.selectedLanguage = language;
-
- await codeBlockLanguageLoader.loadLanguage(language.syntax);
-
- this.tiptapEditor.commands.setCodeBlock({ language: this.selectedLanguage.syntax });
- },
-
- clearCustomLanguageForm() {
- this.showCustomLanguageInput = false;
- this.customLanguageType = '';
- },
-
- applyCustomLanguage() {
- this.showCustomLanguageInput = false;
-
- const language = codeBlockLanguageLoader.findOrCreateLanguageBySyntax(
- this.customLanguageType,
- );
-
- this.applyLanguage(language);
- },
-
- getReferenceClientRect() {
- const { view } = this.tiptapEditor;
- const { from } = this.tiptapEditor.state.selection;
- const node = getParentByTagName(view.domAtPos(from).node, 'pre');
- return node?.getBoundingClientRect() || new DOMRect(-1000, -1000, 0, 0);
- },
-
- deleteCodeBlock() {
- this.tiptapEditor.chain().focus().deleteNode(this.codeBlockType).run();
- },
- },
-};
-</script>
-<template>
- <bubble-menu
- data-testid="code-block-bubble-menu"
- class="gl-shadow gl-rounded-base gl-bg-white"
- :editor="tiptapEditor"
- plugin-key="bubbleMenuCodeBlock"
- :should-show="shouldShow"
- :tippy-options="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
- getReferenceClientRect,
- } /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
- >
- <editor-state-observer @transaction="updateCodeBlockInfoToState">
- <gl-button-group>
- <gl-dropdown
- category="tertiary"
- contenteditable="false"
- boundary="viewport"
- :text="selectedLanguage.label"
- @hide="clearCustomLanguageForm"
- >
- <template v-if="showCustomLanguageInput" #header>
- <div class="gl-relative">
- <gl-button
- v-gl-tooltip
- class="gl-absolute gl-mt-n3 gl-ml-2"
- variant="default"
- category="tertiary"
- size="medium"
- :aria-label="__('Go back')"
- :title="__('Go back')"
- 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!"
- >
- {{ __('Create custom type') }}
- </p>
- </div>
- </template>
- <template v-else #header>
- <gl-search-box-by-type
- v-model="filterTerm"
- :clear-button-title="__('Clear')"
- :placeholder="__('Search')"
- />
- </template>
-
- <template v-if="!showCustomLanguageInput" #highlighted-items>
- <gl-dropdown-item :key="selectedLanguage.syntax" is-check-item :is-checked="true">
- {{ selectedLanguage.label }}
- </gl-dropdown-item>
- </template>
-
- <template v-if="!showCustomLanguageInput" #default>
- <gl-dropdown-item
- v-for="language in filteredLanguages"
- v-show="selectedLanguage.syntax !== language.syntax"
- :key="language.syntax"
- @click="applyLanguage(language)"
- >
- {{ language.label }}
- </gl-dropdown-item>
- </template>
- <template v-else #default>
- <gl-dropdown-form @submit.prevent="applyCustomLanguage">
- <div class="gl-mx-4 gl-mt-2 gl-mb-3">
- <gl-form-input v-model="customLanguageType" :placeholder="__('Language type')" />
- </div>
- <gl-dropdown-divider />
- <div class="gl-mx-4 gl-mt-3 gl-display-flex gl-justify-content-end">
- <gl-button
- variant="default"
- size="medium"
- category="primary"
- class="gl-mr-2 gl-w-auto!"
- @click.prevent.stop="showCustomLanguageInput = false"
- >
- {{ __('Cancel') }}
- </gl-button>
- <gl-button
- variant="confirm"
- size="medium"
- category="primary"
- type="submit"
- class="gl-w-auto!"
- >
- {{ __('Apply') }}
- </gl-button>
- </div>
- </gl-dropdown-form>
- </template>
-
- <template v-if="!showCustomLanguageInput" #footer>
- <gl-dropdown-item
- data-testid="create-custom-type"
- @click.capture.native.stop="showCustomLanguageInput = true"
- >
- {{ __('Create custom type') }}
- </gl-dropdown-item>
- </template>
- </gl-dropdown>
- <gl-button
- v-gl-tooltip
- variant="default"
- category="tertiary"
- size="medium"
- data-testid="copy-code-block"
- :aria-label="__('Copy code')"
- :title="__('Copy code')"
- icon="copy-to-clipboard"
- @click="copyCodeBlockText"
- />
- <gl-button
- v-if="isDiagram"
- v-gl-tooltip
- variant="default"
- category="tertiary"
- size="medium"
- :class="{ active: showPreview }"
- data-testid="preview-diagram"
- :aria-label="__('Preview diagram')"
- :title="__('Preview diagram')"
- icon="eye"
- @click="togglePreview"
- />
- <gl-button
- v-gl-tooltip
- variant="default"
- category="tertiary"
- size="medium"
- data-testid="delete-code-block"
- :aria-label="__('Delete code block')"
- :title="__('Delete code block')"
- icon="remove"
- @click="deleteCodeBlock"
- />
- </gl-button-group>
- </editor-state-observer>
- </bubble-menu>
-</template>
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
new file mode 100644
index 00000000000..a9668ebdb69
--- /dev/null
+++ b/app/assets/javascripts/content_editor/components/bubble_menus/code_block_bubble_menu.vue
@@ -0,0 +1,276 @@
+<script>
+import {
+ GlDropdownForm,
+ GlFormInput,
+ GlDropdownDivider,
+ GlButton,
+ GlButtonGroup,
+ GlDropdown,
+ GlDropdownItem,
+ GlSearchBoxByType,
+ GlTooltipDirective as GlTooltip,
+} from '@gitlab/ui';
+import { getParentByTagName } from '~/lib/utils/dom_utils';
+import codeBlockLanguageLoader from '../../services/code_block_language_loader';
+import CodeBlockHighlight from '../../extensions/code_block_highlight';
+import Diagram from '../../extensions/diagram';
+import Frontmatter from '../../extensions/frontmatter';
+import EditorStateObserver from '../editor_state_observer.vue';
+import BubbleMenu from './bubble_menu.vue';
+
+const CODE_BLOCK_NODE_TYPES = [CodeBlockHighlight.name, Diagram.name, Frontmatter.name];
+
+export default {
+ components: {
+ BubbleMenu,
+ GlDropdownForm,
+ GlFormInput,
+ GlButton,
+ GlButtonGroup,
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownDivider,
+ GlSearchBoxByType,
+ EditorStateObserver,
+ },
+ directives: {
+ GlTooltip,
+ },
+ inject: ['tiptapEditor', 'contentEditor'],
+ data() {
+ return {
+ codeBlockType: undefined,
+ filterTerm: '',
+ filteredLanguages: [],
+
+ showCustomLanguageInput: false,
+ customLanguageType: '',
+
+ selectedLanguage: {},
+ isDiagram: false,
+ showPreview: false,
+ };
+ },
+ watch: {
+ filterTerm: {
+ handler(val) {
+ this.filteredLanguages = codeBlockLanguageLoader.filterLanguages(val);
+ },
+ immediate: true,
+ },
+ },
+ methods: {
+ shouldShow: ({ editor }) => {
+ return CODE_BLOCK_NODE_TYPES.some((type) => editor.isActive(type));
+ },
+
+ async updateCodeBlockInfoToState() {
+ this.codeBlockType = CODE_BLOCK_NODE_TYPES.find((type) => this.tiptapEditor.isActive(type));
+
+ if (!this.codeBlockType) return;
+
+ const { language, isDiagram, showPreview } = this.tiptapEditor.getAttributes(
+ this.codeBlockType,
+ );
+ this.selectedLanguage = codeBlockLanguageLoader.findOrCreateLanguageBySyntax(
+ language,
+ isDiagram,
+ );
+ this.isDiagram = isDiagram;
+ this.showPreview = showPreview;
+ },
+
+ getCodeBlockText() {
+ const { view } = this.tiptapEditor;
+ const { from } = this.tiptapEditor.state.selection;
+ const node = getParentByTagName(view.domAtPos(from).node, 'pre');
+ return node?.textContent || '';
+ },
+
+ copyCodeBlockText() {
+ navigator.clipboard.writeText(this.getCodeBlockText());
+ },
+
+ togglePreview() {
+ this.showPreview = !this.showPreview;
+ this.tiptapEditor.commands.updateAttributes(Diagram.name, { showPreview: this.showPreview });
+ },
+
+ async applyLanguage(language) {
+ this.selectedLanguage = language;
+
+ await codeBlockLanguageLoader.loadLanguage(language.syntax);
+
+ this.tiptapEditor.commands.setCodeBlock({ language: this.selectedLanguage.syntax });
+ },
+
+ clearCustomLanguageForm() {
+ this.showCustomLanguageInput = false;
+ this.customLanguageType = '';
+ },
+
+ applyCustomLanguage() {
+ this.showCustomLanguageInput = false;
+
+ const language = codeBlockLanguageLoader.findOrCreateLanguageBySyntax(
+ this.customLanguageType,
+ );
+
+ this.applyLanguage(language);
+ },
+
+ getReferenceClientRect() {
+ const { view } = this.tiptapEditor;
+ const { from } = this.tiptapEditor.state.selection;
+ const node = getParentByTagName(view.domAtPos(from).node, 'pre');
+ return node?.getBoundingClientRect() || new DOMRect(-1000, -1000, 0, 0);
+ },
+
+ deleteCodeBlock() {
+ this.tiptapEditor.chain().focus().deleteNode(this.codeBlockType).run();
+ },
+
+ tippyOptions() {
+ return { getReferenceClientRect: this.getReferenceClientRect.bind(this) };
+ },
+ },
+};
+</script>
+<template>
+ <bubble-menu
+ data-testid="code-block-bubble-menu"
+ class="gl-shadow gl-rounded-base gl-bg-white"
+ plugin-key="bubbleMenuCodeBlock"
+ :should-show="shouldShow"
+ :tippy-options="tippyOptions()"
+ >
+ <editor-state-observer @transaction="updateCodeBlockInfoToState">
+ <gl-button-group>
+ <gl-dropdown
+ category="tertiary"
+ contenteditable="false"
+ boundary="viewport"
+ :text="selectedLanguage.label"
+ @hide="clearCustomLanguageForm"
+ >
+ <template v-if="showCustomLanguageInput" #header>
+ <div class="gl-relative">
+ <gl-button
+ v-gl-tooltip
+ class="gl-absolute gl-mt-n3 gl-ml-2"
+ variant="default"
+ category="tertiary"
+ size="medium"
+ :aria-label="__('Go back')"
+ :title="__('Go back')"
+ 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!"
+ >
+ {{ __('Create custom type') }}
+ </p>
+ </div>
+ </template>
+ <template v-else #header>
+ <gl-search-box-by-type
+ v-model="filterTerm"
+ :clear-button-title="__('Clear')"
+ :placeholder="__('Search')"
+ />
+ </template>
+
+ <template v-if="!showCustomLanguageInput" #highlighted-items>
+ <gl-dropdown-item :key="selectedLanguage.syntax" is-check-item is-checked>
+ {{ selectedLanguage.label }}
+ </gl-dropdown-item>
+ </template>
+
+ <template v-if="!showCustomLanguageInput" #default>
+ <gl-dropdown-item
+ v-for="language in filteredLanguages"
+ v-show="selectedLanguage.syntax !== language.syntax"
+ :key="language.syntax"
+ @click="applyLanguage(language)"
+ >
+ {{ language.label }}
+ </gl-dropdown-item>
+ </template>
+ <template v-else #default>
+ <gl-dropdown-form @submit.prevent="applyCustomLanguage">
+ <div class="gl-mx-4 gl-mt-2 gl-mb-3">
+ <gl-form-input v-model="customLanguageType" :placeholder="__('Language type')" />
+ </div>
+ <gl-dropdown-divider />
+ <div class="gl-mx-4 gl-mt-3 gl-display-flex gl-justify-content-end">
+ <gl-button
+ variant="default"
+ size="medium"
+ category="primary"
+ class="gl-mr-2 gl-w-auto!"
+ @click.prevent.stop="showCustomLanguageInput = false"
+ >
+ {{ __('Cancel') }}
+ </gl-button>
+ <gl-button
+ variant="confirm"
+ size="medium"
+ category="primary"
+ type="submit"
+ class="gl-w-auto!"
+ >
+ {{ __('Apply') }}
+ </gl-button>
+ </div>
+ </gl-dropdown-form>
+ </template>
+
+ <template v-if="!showCustomLanguageInput" #footer>
+ <gl-dropdown-item
+ data-testid="create-custom-type"
+ @click.capture.native.stop="showCustomLanguageInput = true"
+ >
+ {{ __('Create custom type') }}
+ </gl-dropdown-item>
+ </template>
+ </gl-dropdown>
+ <gl-button
+ v-gl-tooltip
+ variant="default"
+ category="tertiary"
+ size="medium"
+ data-testid="copy-code-block"
+ :aria-label="__('Copy code')"
+ :title="__('Copy code')"
+ icon="copy-to-clipboard"
+ @click="copyCodeBlockText"
+ />
+ <gl-button
+ v-if="isDiagram"
+ v-gl-tooltip
+ variant="default"
+ category="tertiary"
+ size="medium"
+ :class="{ active: showPreview }"
+ data-testid="preview-diagram"
+ :aria-label="__('Preview diagram')"
+ :title="__('Preview diagram')"
+ icon="eye"
+ @click="togglePreview"
+ />
+ <gl-button
+ v-gl-tooltip
+ variant="default"
+ category="tertiary"
+ size="medium"
+ data-testid="delete-code-block"
+ :aria-label="__('Delete code block')"
+ :title="__('Delete code block')"
+ icon="remove"
+ @click="deleteCodeBlock"
+ />
+ </gl-button-group>
+ </editor-state-observer>
+ </bubble-menu>
+</template>
diff --git a/app/assets/javascripts/content_editor/components/bubble_menus/formatting.vue b/app/assets/javascripts/content_editor/components/bubble_menus/formatting.vue
deleted file mode 100644
index 05ca7fd75c3..00000000000
--- a/app/assets/javascripts/content_editor/components/bubble_menus/formatting.vue
+++ /dev/null
@@ -1,122 +0,0 @@
-<script>
-import { GlButtonGroup } from '@gitlab/ui';
-import { BubbleMenu } from '@tiptap/vue-2';
-import { BUBBLE_MENU_TRACKING_ACTION } from '../../constants';
-import trackUIControl from '../../services/track_ui_control';
-import Paragraph from '../../extensions/paragraph';
-import Heading from '../../extensions/heading';
-import Audio from '../../extensions/audio';
-import Video from '../../extensions/video';
-import Image from '../../extensions/image';
-import ToolbarButton from '../toolbar_button.vue';
-
-export default {
- components: {
- BubbleMenu,
- GlButtonGroup,
- ToolbarButton,
- },
- inject: ['tiptapEditor'],
- methods: {
- trackToolbarControlExecution({ contentType, value }) {
- trackUIControl({ action: BUBBLE_MENU_TRACKING_ACTION, property: contentType, value });
- },
-
- shouldShow: ({ editor, from, to }) => {
- if (from === to) return false;
-
- const includes = [Paragraph.name, Heading.name];
- const excludes = [Image.name, Audio.name, Video.name];
-
- return (
- includes.some((type) => editor.isActive(type)) &&
- !excludes.some((type) => editor.isActive(type))
- );
- },
- },
-};
-</script>
-<template>
- <bubble-menu
- data-testid="formatting-bubble-menu"
- class="gl-shadow gl-rounded-base gl-bg-white"
- :editor="tiptapEditor"
- :should-show="shouldShow"
- >
- <gl-button-group>
- <toolbar-button
- data-testid="bold"
- content-type="bold"
- icon-name="bold"
- editor-command="toggleBold"
- category="tertiary"
- size="medium"
- :label="__('Bold text')"
- @execute="trackToolbarControlExecution"
- />
- <toolbar-button
- data-testid="italic"
- content-type="italic"
- icon-name="italic"
- editor-command="toggleItalic"
- category="tertiary"
- size="medium"
- :label="__('Italic text')"
- @execute="trackToolbarControlExecution"
- />
- <toolbar-button
- data-testid="strike"
- content-type="strike"
- icon-name="strikethrough"
- editor-command="toggleStrike"
- category="tertiary"
- size="medium"
- :label="__('Strikethrough')"
- @execute="trackToolbarControlExecution"
- />
- <toolbar-button
- data-testid="code"
- content-type="code"
- icon-name="code"
- editor-command="toggleCode"
- category="tertiary"
- size="medium"
- :label="__('Code')"
- @execute="trackToolbarControlExecution"
- />
- <toolbar-button
- data-testid="superscript"
- content-type="superscript"
- icon-name="superscript"
- editor-command="toggleSuperscript"
- category="tertiary"
- size="medium"
- :label="__('Superscript')"
- @execute="trackToolbarControlExecution"
- />
- <toolbar-button
- data-testid="subscript"
- content-type="subscript"
- icon-name="subscript"
- editor-command="toggleSubscript"
- category="tertiary"
- size="medium"
- :label="__('Subscript')"
- @execute="trackToolbarControlExecution"
- />
- <toolbar-button
- data-testid="link"
- content-type="link"
- icon-name="link"
- editor-command="toggleLink"
- :editor-command-params="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
- href: '',
- } /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
- category="tertiary"
- size="medium"
- :label="__('Insert link')"
- @execute="trackToolbarControlExecution"
- />
- </gl-button-group>
- </bubble-menu>
-</template>
diff --git a/app/assets/javascripts/content_editor/components/bubble_menus/formatting_bubble_menu.vue b/app/assets/javascripts/content_editor/components/bubble_menus/formatting_bubble_menu.vue
new file mode 100644
index 00000000000..327b0967229
--- /dev/null
+++ b/app/assets/javascripts/content_editor/components/bubble_menus/formatting_bubble_menu.vue
@@ -0,0 +1,123 @@
+<script>
+import { GlButtonGroup } from '@gitlab/ui';
+import { BUBBLE_MENU_TRACKING_ACTION } from '../../constants';
+import trackUIControl from '../../services/track_ui_control';
+import Paragraph from '../../extensions/paragraph';
+import Heading from '../../extensions/heading';
+import Audio from '../../extensions/audio';
+import Video from '../../extensions/video';
+import Image from '../../extensions/image';
+import ToolbarButton from '../toolbar_button.vue';
+import BubbleMenu from './bubble_menu.vue';
+
+export default {
+ components: {
+ BubbleMenu,
+ GlButtonGroup,
+ ToolbarButton,
+ },
+ inject: ['tiptapEditor'],
+ methods: {
+ trackToolbarControlExecution({ contentType, value }) {
+ trackUIControl({ action: BUBBLE_MENU_TRACKING_ACTION, property: contentType, value });
+ },
+
+ shouldShow: ({ editor, from, to }) => {
+ if (from === to) return false;
+
+ const includes = [Paragraph.name, Heading.name];
+ const excludes = [Image.name, Audio.name, Video.name];
+
+ return (
+ includes.some((type) => editor.isActive(type)) &&
+ !excludes.some((type) => editor.isActive(type))
+ );
+ },
+ },
+ toggleLinkCommandParams: {
+ href: '',
+ },
+};
+</script>
+<template>
+ <bubble-menu
+ data-testid="formatting-bubble-menu"
+ class="gl-shadow gl-rounded-base gl-bg-white"
+ :should-show="shouldShow"
+ :plugin-key="'formatting'"
+ >
+ <gl-button-group>
+ <toolbar-button
+ data-testid="bold"
+ content-type="bold"
+ icon-name="bold"
+ editor-command="toggleBold"
+ category="tertiary"
+ size="medium"
+ :label="__('Bold text')"
+ @execute="trackToolbarControlExecution"
+ />
+ <toolbar-button
+ data-testid="italic"
+ content-type="italic"
+ icon-name="italic"
+ editor-command="toggleItalic"
+ category="tertiary"
+ size="medium"
+ :label="__('Italic text')"
+ @execute="trackToolbarControlExecution"
+ />
+ <toolbar-button
+ data-testid="strike"
+ content-type="strike"
+ icon-name="strikethrough"
+ editor-command="toggleStrike"
+ category="tertiary"
+ size="medium"
+ :label="__('Strikethrough')"
+ @execute="trackToolbarControlExecution"
+ />
+ <toolbar-button
+ data-testid="code"
+ content-type="code"
+ icon-name="code"
+ editor-command="toggleCode"
+ category="tertiary"
+ size="medium"
+ :label="__('Code')"
+ @execute="trackToolbarControlExecution"
+ />
+ <toolbar-button
+ data-testid="superscript"
+ content-type="superscript"
+ icon-name="superscript"
+ editor-command="toggleSuperscript"
+ category="tertiary"
+ size="medium"
+ :label="__('Superscript')"
+ @execute="trackToolbarControlExecution"
+ />
+ <toolbar-button
+ data-testid="subscript"
+ content-type="subscript"
+ icon-name="subscript"
+ editor-command="toggleSubscript"
+ category="tertiary"
+ size="medium"
+ :label="__('Subscript')"
+ @execute="trackToolbarControlExecution"
+ />
+ <toolbar-button
+ data-testid="link"
+ content-type="link"
+ icon-name="link"
+ editor-command="toggleLink"
+ :editor-command-params="$options.toggleLinkCommandParams"
+ category="tertiary"
+ size="medium"
+ :label="__('Insert link')"
+ @execute="trackToolbarControlExecution"
+ />
+ </gl-button-group>
+ </bubble-menu>
+</template>
diff --git a/app/assets/javascripts/content_editor/components/bubble_menus/link.vue b/app/assets/javascripts/content_editor/components/bubble_menus/link.vue
deleted file mode 100644
index dae0bc63b5a..00000000000
--- a/app/assets/javascripts/content_editor/components/bubble_menus/link.vue
+++ /dev/null
@@ -1,189 +0,0 @@
-<script>
-import {
- GlLink,
- GlForm,
- GlFormGroup,
- GlFormInput,
- GlButton,
- GlButtonGroup,
- GlTooltipDirective as GlTooltip,
-} from '@gitlab/ui';
-import { BubbleMenu } from '@tiptap/vue-2';
-import Link from '../../extensions/link';
-import EditorStateObserver from '../editor_state_observer.vue';
-
-export default {
- components: {
- BubbleMenu,
- GlForm,
- GlFormGroup,
- GlFormInput,
- GlLink,
- GlButton,
- GlButtonGroup,
- EditorStateObserver,
- },
- directives: {
- GlTooltip,
- },
- inject: ['tiptapEditor', 'contentEditor'],
- data() {
- return {
- linkHref: undefined,
- linkCanonicalSrc: undefined,
- linkTitle: undefined,
-
- isEditing: false,
- };
- },
- watch: {
- linkCanonicalSrc(value) {
- if (!value) this.isEditing = true;
- },
- },
- methods: {
- shouldShow() {
- const shouldShow = this.tiptapEditor.isActive(Link.name);
-
- if (!shouldShow) this.isEditing = false;
-
- return shouldShow;
- },
-
- startEditingLink() {
- // select the entire link
- this.tiptapEditor.chain().focus().extendMarkRange(Link.name).run();
-
- this.isEditing = true;
- },
-
- async endEditingLink() {
- this.isEditing = false;
-
- this.linkHref = await this.contentEditor.resolveUrl(this.linkCanonicalSrc);
-
- if (!this.linkCanonicalSrc && !this.linkHref) {
- this.removeLink();
- }
- },
-
- cancelEditingLink() {
- this.endEditingLink();
- this.updateLinkToState();
- },
-
- async saveEditedLink() {
- if (!this.linkCanonicalSrc) {
- this.removeLink();
- } else {
- this.tiptapEditor
- .chain()
- .focus()
- .extendMarkRange(Link.name)
- .updateAttributes(Link.name, {
- href: this.linkCanonicalSrc,
- canonicalSrc: this.linkCanonicalSrc,
- title: this.linkTitle,
- })
- .run();
- }
-
- this.endEditingLink();
- },
-
- updateLinkToState() {
- if (!this.tiptapEditor.isActive(Link.name)) return;
-
- const { href, title, canonicalSrc } = this.tiptapEditor.getAttributes(Link.name);
-
- this.linkTitle = title;
- this.linkHref = href;
- this.linkCanonicalSrc = canonicalSrc || href;
- },
-
- copyLinkHref() {
- navigator.clipboard.writeText(this.linkCanonicalSrc);
- },
-
- removeLink() {
- this.tiptapEditor.chain().focus().extendMarkRange(Link.name).unsetLink().run();
- },
- },
-};
-</script>
-<template>
- <bubble-menu
- data-testid="link-bubble-menu"
- class="gl-shadow gl-rounded-base gl-bg-white"
- :editor="tiptapEditor"
- plugin-key="bubbleMenuLink"
- :should-show="() => shouldShow()"
- :tippy-options="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
- placement: 'bottom',
- } /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
- >
- <editor-state-observer @transaction="updateLinkToState">
- <gl-button-group v-if="!isEditing" class="gl-display-flex gl-align-items-center">
- <gl-link
- v-gl-tooltip
- :href="linkHref"
- :aria-label="linkCanonicalSrc"
- :title="linkCanonicalSrc"
- target="_blank"
- class="gl-px-3 gl-overflow-hidden gl-white-space-nowrap gl-text-overflow-ellipsis"
- >
- {{ linkCanonicalSrc }}
- </gl-link>
- <gl-button
- v-gl-tooltip
- variant="default"
- category="tertiary"
- size="medium"
- data-testid="copy-link-url"
- :aria-label="__('Copy link URL')"
- :title="__('Copy link URL')"
- icon="copy-to-clipboard"
- @click="copyLinkHref"
- />
- <gl-button
- v-gl-tooltip
- variant="default"
- category="tertiary"
- size="medium"
- data-testid="edit-link"
- :aria-label="__('Edit link')"
- :title="__('Edit link')"
- icon="pencil"
- @click="startEditingLink"
- />
- <gl-button
- v-gl-tooltip
- variant="default"
- category="tertiary"
- size="medium"
- data-testid="remove-link"
- :aria-label="__('Remove link')"
- :title="__('Remove link')"
- icon="unlink"
- @click="removeLink"
- />
- </gl-button-group>
- <gl-form v-else class="bubble-menu-form gl-p-4 gl-w-100" @submit.prevent="saveEditedLink">
- <gl-form-group :label="__('URL')" label-for="link-href">
- <gl-form-input id="link-href" v-model="linkCanonicalSrc" data-testid="link-href" />
- </gl-form-group>
- <gl-form-group :label="__('Title')" label-for="link-title">
- <gl-form-input id="link-title" v-model="linkTitle" data-testid="link-title" />
- </gl-form-group>
- <div class="gl-display-flex gl-justify-content-end">
- <gl-button class="gl-mr-3" data-testid="cancel-link" @click="cancelEditingLink">
- {{ __('Cancel') }}
- </gl-button>
- <gl-button variant="confirm" type="submit">
- {{ __('Apply') }}
- </gl-button>
- </div>
- </gl-form>
- </editor-state-observer>
- </bubble-menu>
-</template>
diff --git a/app/assets/javascripts/content_editor/components/bubble_menus/link_bubble_menu.vue b/app/assets/javascripts/content_editor/components/bubble_menus/link_bubble_menu.vue
new file mode 100644
index 00000000000..a4713eb3275
--- /dev/null
+++ b/app/assets/javascripts/content_editor/components/bubble_menus/link_bubble_menu.vue
@@ -0,0 +1,198 @@
+<script>
+import {
+ GlLink,
+ GlForm,
+ GlFormGroup,
+ GlFormInput,
+ GlButton,
+ GlButtonGroup,
+ GlTooltipDirective as GlTooltip,
+} from '@gitlab/ui';
+import Link from '../../extensions/link';
+import EditorStateObserver from '../editor_state_observer.vue';
+import BubbleMenu from './bubble_menu.vue';
+
+export default {
+ components: {
+ BubbleMenu,
+ GlForm,
+ GlFormGroup,
+ GlFormInput,
+ GlLink,
+ GlButton,
+ GlButtonGroup,
+ EditorStateObserver,
+ },
+ directives: {
+ GlTooltip,
+ },
+ inject: ['tiptapEditor', 'contentEditor'],
+ data() {
+ return {
+ linkHref: undefined,
+ linkCanonicalSrc: undefined,
+ linkTitle: undefined,
+
+ isEditing: false,
+ };
+ },
+ methods: {
+ shouldShow() {
+ return this.tiptapEditor.isActive(Link.name);
+ },
+
+ startEditingLink() {
+ // select the entire link
+ this.tiptapEditor.chain().focus().extendMarkRange(Link.name).run();
+
+ this.isEditing = true;
+ },
+
+ async endEditingLink() {
+ this.isEditing = false;
+
+ this.linkHref = await this.contentEditor.resolveUrl(this.linkCanonicalSrc);
+
+ if (!this.linkCanonicalSrc && !this.linkHref) {
+ this.removeLink();
+ }
+ },
+
+ cancelEditingLink() {
+ this.endEditingLink();
+ this.updateLinkToState();
+ },
+
+ async saveEditedLink() {
+ if (!this.linkCanonicalSrc) {
+ this.removeLink();
+ } else {
+ this.tiptapEditor
+ .chain()
+ .focus()
+ .extendMarkRange(Link.name)
+ .updateAttributes(Link.name, {
+ href: this.linkCanonicalSrc,
+ canonicalSrc: this.linkCanonicalSrc,
+ title: this.linkTitle,
+ })
+ .run();
+ }
+
+ this.endEditingLink();
+ },
+
+ updateLinkToState() {
+ const editor = this.tiptapEditor;
+
+ const { href, title, canonicalSrc } = editor.getAttributes(Link.name);
+
+ if (
+ canonicalSrc === this.linkCanonicalSrc &&
+ href === this.linkHref &&
+ title === this.linkTitle
+ ) {
+ return;
+ }
+
+ this.linkTitle = title;
+ this.linkHref = href;
+ this.linkCanonicalSrc = canonicalSrc || href;
+
+ this.isEditing = !this.linkCanonicalSrc;
+ },
+
+ copyLinkHref() {
+ navigator.clipboard.writeText(this.linkCanonicalSrc);
+ },
+
+ removeLink() {
+ this.tiptapEditor.chain().focus().extendMarkRange(Link.name).unsetLink().run();
+ },
+
+ resetBubbleMenuState() {
+ this.linkTitle = undefined;
+ this.linkHref = undefined;
+ this.linkCanonicalSrc = undefined;
+ },
+ },
+ tippyOptions: {
+ placement: 'bottom',
+ },
+};
+</script>
+<template>
+ <bubble-menu
+ data-testid="link-bubble-menu"
+ class="gl-shadow gl-rounded-base gl-bg-white"
+ plugin-key="bubbleMenuLink"
+ :should-show="shouldShow"
+ :tippy-options="$options.tippyOptions"
+ @show="updateLinkToState"
+ @hidden="resetBubbleMenuState"
+ >
+ <editor-state-observer @selectionUpdate="updateLinkToState">
+ <gl-button-group v-if="!isEditing" class="gl-display-flex gl-align-items-center">
+ <gl-link
+ v-gl-tooltip
+ :href="linkHref"
+ :aria-label="linkCanonicalSrc"
+ :title="linkCanonicalSrc"
+ target="_blank"
+ class="gl-px-3 gl-overflow-hidden gl-white-space-nowrap gl-text-overflow-ellipsis"
+ >
+ {{ linkCanonicalSrc }}
+ </gl-link>
+ <gl-button
+ v-gl-tooltip
+ variant="default"
+ category="tertiary"
+ size="medium"
+ data-testid="copy-link-url"
+ :aria-label="__('Copy link URL')"
+ :title="__('Copy link URL')"
+ icon="copy-to-clipboard"
+ @click="copyLinkHref"
+ />
+ <gl-button
+ v-gl-tooltip
+ variant="default"
+ category="tertiary"
+ size="medium"
+ data-testid="edit-link"
+ :aria-label="__('Edit link')"
+ :title="__('Edit link')"
+ icon="pencil"
+ @click="startEditingLink"
+ />
+ <gl-button
+ v-gl-tooltip
+ variant="default"
+ category="tertiary"
+ size="medium"
+ data-testid="remove-link"
+ :aria-label="__('Remove link')"
+ :title="__('Remove link')"
+ icon="unlink"
+ @click="removeLink"
+ />
+ </gl-button-group>
+ <gl-form v-else class="bubble-menu-form gl-p-4 gl-w-100" @submit.prevent="saveEditedLink">
+ <gl-form-group :label="__('URL')" label-for="link-href">
+ <gl-form-input id="link-href" v-model="linkCanonicalSrc" data-testid="link-href" />
+ </gl-form-group>
+ <gl-form-group :label="__('Title')" label-for="link-title">
+ <gl-form-input id="link-title" v-model="linkTitle" data-testid="link-title" />
+ </gl-form-group>
+ <div class="gl-display-flex gl-justify-content-end">
+ <gl-button class="gl-mr-3" data-testid="cancel-link" @click="cancelEditingLink">
+ {{ __('Cancel') }}
+ </gl-button>
+ <gl-button variant="confirm" type="submit">
+ {{ __('Apply') }}
+ </gl-button>
+ </div>
+ </gl-form>
+ </editor-state-observer>
+ </bubble-menu>
+</template>
diff --git a/app/assets/javascripts/content_editor/components/bubble_menus/media.vue b/app/assets/javascripts/content_editor/components/bubble_menus/media.vue
deleted file mode 100644
index a36a860c440..00000000000
--- a/app/assets/javascripts/content_editor/components/bubble_menus/media.vue
+++ /dev/null
@@ -1,288 +0,0 @@
-<script>
-import {
- GlLink,
- GlForm,
- GlFormGroup,
- GlFormInput,
- GlLoadingIcon,
- GlButton,
- GlButtonGroup,
- GlTooltipDirective as GlTooltip,
-} from '@gitlab/ui';
-import { BubbleMenu } from '@tiptap/vue-2';
-import { __ } from '~/locale';
-import Audio from '../../extensions/audio';
-import Image from '../../extensions/image';
-import Video from '../../extensions/video';
-import EditorStateObserver from '../editor_state_observer.vue';
-import { acceptedMimes } from '../../services/upload_helpers';
-
-const MEDIA_TYPES = [Audio.name, Image.name, Video.name];
-
-export default {
- i18n: {
- copySourceLabels: {
- [Audio.name]: __('Copy audio URL'),
- [Image.name]: __('Copy image URL'),
- [Video.name]: __('Copy video URL'),
- },
- editLabels: {
- [Audio.name]: __('Edit audio description'),
- [Image.name]: __('Edit image description'),
- [Video.name]: __('Edit video description'),
- },
- replaceLabels: {
- [Audio.name]: __('Replace audio'),
- [Image.name]: __('Replace image'),
- [Video.name]: __('Replace video'),
- },
- deleteLabels: {
- [Audio.name]: __('Delete audio'),
- [Image.name]: __('Delete image'),
- [Video.name]: __('Delete video'),
- },
- },
- components: {
- BubbleMenu,
- GlForm,
- GlFormGroup,
- GlFormInput,
- GlLink,
- GlLoadingIcon,
- GlButton,
- GlButtonGroup,
- EditorStateObserver,
- },
- directives: {
- GlTooltip,
- },
- inject: ['tiptapEditor', 'contentEditor'],
- data() {
- return {
- mediaType: undefined,
- mediaSrc: undefined,
- mediaCanonicalSrc: undefined,
- mediaAlt: undefined,
- mediaTitle: undefined,
-
- isEditing: false,
- isUpdating: false,
- isUploading: false,
- };
- },
- computed: {
- copySourceLabel() {
- return this.$options.i18n.copySourceLabels[this.mediaType];
- },
- editLabel() {
- return this.$options.i18n.editLabels[this.mediaType];
- },
- replaceLabel() {
- return this.$options.i18n.replaceLabels[this.mediaType];
- },
- deleteLabel() {
- return this.$options.i18n.deleteLabels[this.mediaType];
- },
- showProgressIndicator() {
- return this.isUploading || this.isUpdating;
- },
- },
- methods: {
- shouldShow() {
- const shouldShow = MEDIA_TYPES.some((type) => this.tiptapEditor.isActive(type));
-
- if (!shouldShow) this.isEditing = false;
-
- return shouldShow;
- },
-
- startEditingMedia() {
- this.isEditing = true;
- },
-
- endEditingMedia() {
- this.isEditing = false;
-
- this.updateMediaInfoToState();
- },
-
- cancelEditingMedia() {
- this.endEditingMedia();
- this.updateMediaInfoToState();
- },
-
- async saveEditedMedia() {
- this.isUpdating = true;
-
- this.mediaSrc = await this.contentEditor.resolveUrl(this.mediaCanonicalSrc);
-
- const position = this.tiptapEditor.state.selection.from;
-
- this.tiptapEditor
- .chain()
- .focus()
- .updateAttributes(this.mediaType, {
- src: this.mediaSrc,
- alt: this.mediaAlt,
- canonicalSrc: this.mediaCanonicalSrc,
- title: this.mediaTitle,
- })
- .run();
-
- this.tiptapEditor.commands.setNodeSelection(position);
-
- this.endEditingMedia();
-
- this.isUpdating = false;
- },
-
- async updateMediaInfoToState() {
- this.mediaType = MEDIA_TYPES.find((type) => this.tiptapEditor.isActive(type));
-
- if (!this.mediaType) return;
-
- this.isUpdating = true;
-
- const { src, title, alt, canonicalSrc, uploading } = this.tiptapEditor.getAttributes(
- this.mediaType,
- );
-
- this.mediaTitle = title;
- this.mediaAlt = alt;
- this.mediaCanonicalSrc = canonicalSrc || src;
- this.isUploading = uploading;
- this.mediaSrc = await this.contentEditor.resolveUrl(this.mediaCanonicalSrc);
-
- this.isUpdating = false;
- },
-
- replaceMedia() {
- this.$refs.fileSelector.click();
- },
-
- onFileSelect(e) {
- this.tiptapEditor
- .chain()
- .focus()
- .deleteSelection()
- .uploadAttachment({
- file: e.target.files[0],
- })
- .run();
-
- this.$refs.fileSelector.value = '';
- },
-
- copyMediaSrc() {
- navigator.clipboard.writeText(this.mediaCanonicalSrc);
- },
-
- deleteMedia() {
- this.tiptapEditor.chain().focus().deleteSelection().run();
- },
- },
-
- acceptedMimes,
-};
-</script>
-<template>
- <bubble-menu
- data-testid="media-bubble-menu"
- class="gl-shadow gl-rounded-base gl-bg-white"
- :editor="tiptapEditor"
- plugin-key="bubbleMenuMedia"
- :should-show="() => shouldShow()"
- >
- <editor-state-observer @transaction="updateMediaInfoToState">
- <gl-button-group v-if="!isEditing" class="gl-display-flex gl-align-items-center">
- <gl-loading-icon v-if="showProgressIndicator" class="gl-pl-4 gl-pr-3" />
- <input
- ref="fileSelector"
- type="file"
- name="content_editor_image"
- :accept="$options.acceptedMimes[mediaType]"
- class="gl-display-none"
- data-qa-selector="file_upload_field"
- @change="onFileSelect"
- />
-
- <gl-link
- v-if="!showProgressIndicator"
- v-gl-tooltip
- :href="mediaSrc"
- :aria-label="mediaCanonicalSrc"
- :title="mediaCanonicalSrc"
- target="_blank"
- class="gl-px-3 gl-overflow-hidden gl-white-space-nowrap gl-text-overflow-ellipsis"
- >
- {{ mediaCanonicalSrc }}
- </gl-link>
- <gl-button
- v-gl-tooltip
- variant="default"
- category="tertiary"
- size="medium"
- data-testid="copy-media-src"
- :aria-label="copySourceLabel"
- :title="copySourceLabel"
- icon="copy-to-clipboard"
- @click="copyMediaSrc"
- />
- <gl-button
- v-if="!showProgressIndicator"
- v-gl-tooltip
- variant="default"
- category="tertiary"
- size="medium"
- data-testid="edit-media"
- :aria-label="editLabel"
- :title="editLabel"
- icon="pencil"
- @click="startEditingMedia"
- />
- <gl-button
- v-gl-tooltip
- variant="default"
- category="tertiary"
- size="medium"
- data-testid="replace-media"
- :aria-label="replaceLabel"
- :title="replaceLabel"
- icon="upload"
- @click="replaceMedia"
- />
- <gl-button
- v-gl-tooltip
- variant="default"
- category="tertiary"
- size="medium"
- data-testid="delete-media"
- :aria-label="deleteLabel"
- :title="deleteLabel"
- icon="remove"
- @click="deleteMedia"
- />
- </gl-button-group>
- <gl-form v-else class="bubble-menu-form gl-p-4 gl-w-100" @submit.prevent="saveEditedMedia">
- <gl-form-group :label="__('URL')" label-for="media-src">
- <gl-form-input id="media-src" v-model="mediaCanonicalSrc" data-testid="media-src" />
- </gl-form-group>
- <gl-form-group :label="__('Description (alt text)')" label-for="media-alt">
- <gl-form-input id="media-alt" v-model="mediaAlt" data-testid="media-alt" />
- </gl-form-group>
- <gl-form-group :label="__('Title')" label-for="media-title">
- <gl-form-input id="media-title" v-model="mediaTitle" data-testid="media-title" />
- </gl-form-group>
- <div class="gl-display-flex gl-justify-content-end">
- <gl-button
- class="gl-mr-3"
- data-testid="cancel-editing-media"
- @click="cancelEditingMedia"
- >{{ __('Cancel') }}</gl-button
- >
- <gl-button variant="confirm" type="submit">{{ __('Apply') }}</gl-button>
- </div>
- </gl-form>
- </editor-state-observer>
- </bubble-menu>
-</template>
diff --git a/app/assets/javascripts/content_editor/components/bubble_menus/media_bubble_menu.vue b/app/assets/javascripts/content_editor/components/bubble_menus/media_bubble_menu.vue
new file mode 100644
index 00000000000..310bb1be81f
--- /dev/null
+++ b/app/assets/javascripts/content_editor/components/bubble_menus/media_bubble_menu.vue
@@ -0,0 +1,287 @@
+<script>
+import {
+ GlLink,
+ GlForm,
+ GlFormGroup,
+ GlFormInput,
+ GlLoadingIcon,
+ GlButton,
+ GlButtonGroup,
+ GlTooltipDirective as GlTooltip,
+} from '@gitlab/ui';
+import { __ } from '~/locale';
+import Audio from '../../extensions/audio';
+import Image from '../../extensions/image';
+import Video from '../../extensions/video';
+import EditorStateObserver from '../editor_state_observer.vue';
+import { acceptedMimes } from '../../services/upload_helpers';
+import BubbleMenu from './bubble_menu.vue';
+
+const MEDIA_TYPES = [Audio.name, Image.name, Video.name];
+
+export default {
+ i18n: {
+ copySourceLabels: {
+ [Audio.name]: __('Copy audio URL'),
+ [Image.name]: __('Copy image URL'),
+ [Video.name]: __('Copy video URL'),
+ },
+ editLabels: {
+ [Audio.name]: __('Edit audio description'),
+ [Image.name]: __('Edit image description'),
+ [Video.name]: __('Edit video description'),
+ },
+ replaceLabels: {
+ [Audio.name]: __('Replace audio'),
+ [Image.name]: __('Replace image'),
+ [Video.name]: __('Replace video'),
+ },
+ deleteLabels: {
+ [Audio.name]: __('Delete audio'),
+ [Image.name]: __('Delete image'),
+ [Video.name]: __('Delete video'),
+ },
+ },
+ components: {
+ BubbleMenu,
+ GlForm,
+ GlFormGroup,
+ GlFormInput,
+ GlLink,
+ GlLoadingIcon,
+ GlButton,
+ GlButtonGroup,
+ EditorStateObserver,
+ },
+ directives: {
+ GlTooltip,
+ },
+ inject: ['tiptapEditor', 'contentEditor'],
+ data() {
+ return {
+ mediaType: undefined,
+ mediaSrc: undefined,
+ mediaCanonicalSrc: undefined,
+ mediaAlt: undefined,
+ mediaTitle: undefined,
+
+ isEditing: false,
+ isUpdating: false,
+ isUploading: false,
+ };
+ },
+ computed: {
+ copySourceLabel() {
+ return this.$options.i18n.copySourceLabels[this.mediaType];
+ },
+ editLabel() {
+ return this.$options.i18n.editLabels[this.mediaType];
+ },
+ replaceLabel() {
+ return this.$options.i18n.replaceLabels[this.mediaType];
+ },
+ deleteLabel() {
+ return this.$options.i18n.deleteLabels[this.mediaType];
+ },
+ showProgressIndicator() {
+ return this.isUploading || this.isUpdating;
+ },
+ },
+ methods: {
+ shouldShow() {
+ const shouldShow = MEDIA_TYPES.some((type) => this.tiptapEditor.isActive(type));
+
+ if (!shouldShow) this.isEditing = false;
+
+ return shouldShow;
+ },
+
+ startEditingMedia() {
+ this.isEditing = true;
+ },
+
+ endEditingMedia() {
+ this.isEditing = false;
+
+ this.updateMediaInfoToState();
+ },
+
+ cancelEditingMedia() {
+ this.endEditingMedia();
+ this.updateMediaInfoToState();
+ },
+
+ async saveEditedMedia() {
+ this.isUpdating = true;
+
+ this.mediaSrc = await this.contentEditor.resolveUrl(this.mediaCanonicalSrc);
+
+ const position = this.tiptapEditor.state.selection.from;
+
+ this.tiptapEditor
+ .chain()
+ .focus()
+ .updateAttributes(this.mediaType, {
+ src: this.mediaSrc,
+ alt: this.mediaAlt,
+ canonicalSrc: this.mediaCanonicalSrc,
+ title: this.mediaTitle,
+ })
+ .run();
+
+ this.tiptapEditor.commands.setNodeSelection(position);
+
+ this.endEditingMedia();
+
+ this.isUpdating = false;
+ },
+
+ async updateMediaInfoToState() {
+ this.mediaType = MEDIA_TYPES.find((type) => this.tiptapEditor.isActive(type));
+
+ if (!this.mediaType) return;
+
+ this.isUpdating = true;
+
+ const { src, title, alt, canonicalSrc, uploading } = this.tiptapEditor.getAttributes(
+ this.mediaType,
+ );
+
+ this.mediaTitle = title;
+ this.mediaAlt = alt;
+ this.mediaCanonicalSrc = canonicalSrc || src;
+ this.isUploading = uploading;
+ this.mediaSrc = await this.contentEditor.resolveUrl(this.mediaCanonicalSrc);
+
+ this.isUpdating = false;
+ },
+
+ replaceMedia() {
+ this.$refs.fileSelector.click();
+ },
+
+ onFileSelect(e) {
+ this.tiptapEditor
+ .chain()
+ .focus()
+ .deleteSelection()
+ .uploadAttachment({
+ file: e.target.files[0],
+ })
+ .run();
+
+ this.$refs.fileSelector.value = '';
+ },
+
+ copyMediaSrc() {
+ navigator.clipboard.writeText(this.mediaCanonicalSrc);
+ },
+
+ deleteMedia() {
+ this.tiptapEditor.chain().focus().deleteSelection().run();
+ },
+ },
+
+ acceptedMimes,
+};
+</script>
+<template>
+ <bubble-menu
+ data-testid="media-bubble-menu"
+ class="gl-shadow gl-rounded-base gl-bg-white"
+ plugin-key="bubbleMenuMedia"
+ :should-show="shouldShow"
+ >
+ <editor-state-observer @transaction="updateMediaInfoToState">
+ <gl-button-group v-if="!isEditing" class="gl-display-flex gl-align-items-center">
+ <gl-loading-icon v-if="showProgressIndicator" class="gl-pl-4 gl-pr-3" />
+ <input
+ ref="fileSelector"
+ type="file"
+ name="content_editor_image"
+ :accept="$options.acceptedMimes[mediaType]"
+ class="gl-display-none"
+ data-qa-selector="file_upload_field"
+ @change="onFileSelect"
+ />
+
+ <gl-link
+ v-if="!showProgressIndicator"
+ v-gl-tooltip
+ :href="mediaSrc"
+ :aria-label="mediaCanonicalSrc"
+ :title="mediaCanonicalSrc"
+ target="_blank"
+ class="gl-px-3 gl-overflow-hidden gl-white-space-nowrap gl-text-overflow-ellipsis"
+ >
+ {{ mediaCanonicalSrc }}
+ </gl-link>
+ <gl-button
+ v-gl-tooltip
+ variant="default"
+ category="tertiary"
+ size="medium"
+ data-testid="copy-media-src"
+ :aria-label="copySourceLabel"
+ :title="copySourceLabel"
+ icon="copy-to-clipboard"
+ @click="copyMediaSrc"
+ />
+ <gl-button
+ v-if="!showProgressIndicator"
+ v-gl-tooltip
+ variant="default"
+ category="tertiary"
+ size="medium"
+ data-testid="edit-media"
+ :aria-label="editLabel"
+ :title="editLabel"
+ icon="pencil"
+ @click="startEditingMedia"
+ />
+ <gl-button
+ v-gl-tooltip
+ variant="default"
+ category="tertiary"
+ size="medium"
+ data-testid="replace-media"
+ :aria-label="replaceLabel"
+ :title="replaceLabel"
+ icon="upload"
+ @click="replaceMedia"
+ />
+ <gl-button
+ v-gl-tooltip
+ variant="default"
+ category="tertiary"
+ size="medium"
+ data-testid="delete-media"
+ :aria-label="deleteLabel"
+ :title="deleteLabel"
+ icon="remove"
+ @click="deleteMedia"
+ />
+ </gl-button-group>
+ <gl-form v-else class="bubble-menu-form gl-p-4 gl-w-100" @submit.prevent="saveEditedMedia">
+ <gl-form-group :label="__('URL')" label-for="media-src">
+ <gl-form-input id="media-src" v-model="mediaCanonicalSrc" data-testid="media-src" />
+ </gl-form-group>
+ <gl-form-group :label="__('Description (alt text)')" label-for="media-alt">
+ <gl-form-input id="media-alt" v-model="mediaAlt" data-testid="media-alt" />
+ </gl-form-group>
+ <gl-form-group :label="__('Title')" label-for="media-title">
+ <gl-form-input id="media-title" v-model="mediaTitle" data-testid="media-title" />
+ </gl-form-group>
+ <div class="gl-display-flex gl-justify-content-end">
+ <gl-button
+ class="gl-mr-3"
+ data-testid="cancel-editing-media"
+ @click="cancelEditingMedia"
+ >{{ __('Cancel') }}</gl-button
+ >
+ <gl-button variant="confirm" type="submit">{{ __('Apply') }}</gl-button>
+ </div>
+ </gl-form>
+ </editor-state-observer>
+ </bubble-menu>
+</template>
diff --git a/app/assets/javascripts/content_editor/components/content_editor.vue b/app/assets/javascripts/content_editor/components/content_editor.vue
index c3c881d9135..659c447e861 100644
--- a/app/assets/javascripts/content_editor/components/content_editor.vue
+++ b/app/assets/javascripts/content_editor/components/content_editor.vue
@@ -1,13 +1,16 @@
<script>
import { EditorContent as TiptapEditorContent } from '@tiptap/vue-2';
+import { __ } from '~/locale';
+import { VARIANT_DANGER } from '~/flash';
import { createContentEditor } from '../services/create_content_editor';
+import { ALERT_EVENT } from '../constants';
import ContentEditorAlert from './content_editor_alert.vue';
import ContentEditorProvider from './content_editor_provider.vue';
import EditorStateObserver from './editor_state_observer.vue';
-import FormattingBubbleMenu from './bubble_menus/formatting.vue';
-import CodeBlockBubbleMenu from './bubble_menus/code_block.vue';
-import LinkBubbleMenu from './bubble_menus/link.vue';
-import MediaBubbleMenu from './bubble_menus/media.vue';
+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 LoadingIndicator from './loading_indicator.vue';
@@ -43,12 +46,26 @@ export default {
required: false,
default: () => {},
},
+ markdown: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
data() {
return {
focused: false,
+ isLoading: false,
+ latestMarkdown: null,
};
},
+ watch: {
+ markdown(markdown) {
+ if (markdown !== this.latestMarkdown) {
+ this.setSerializedContent(markdown);
+ }
+ },
+ },
created() {
const { renderMarkdown, uploadsPath, extensions, serializerConfig } = this;
@@ -61,21 +78,61 @@ export default {
});
},
mounted() {
- this.$emit('initialized', this.contentEditor);
+ this.$emit('initialized');
+ this.setSerializedContent(this.markdown);
},
beforeDestroy() {
this.contentEditor.dispose();
},
methods: {
+ async setSerializedContent(markdown) {
+ this.notifyLoading();
+
+ try {
+ await this.contentEditor.setSerializedContent(markdown);
+ this.contentEditor.setEditable(true);
+ this.notifyLoadingSuccess();
+ this.latestMarkdown = markdown;
+ } catch {
+ this.contentEditor.eventHub.$emit(ALERT_EVENT, {
+ message: __(
+ 'An error occurred while trying to render the content editor. Please try again.',
+ ),
+ variant: VARIANT_DANGER,
+ actionLabel: __('Retry'),
+ action: () => {
+ this.setSerializedContent(markdown);
+ },
+ });
+ this.contentEditor.setEditable(false);
+ this.notifyLoadingError();
+ }
+ },
focus() {
this.focused = true;
},
blur() {
this.focused = false;
},
+ notifyLoading() {
+ this.isLoading = true;
+ this.$emit('loading');
+ },
+ notifyLoadingSuccess() {
+ this.isLoading = false;
+ this.$emit('loadingSuccess');
+ },
+ notifyLoadingError(error) {
+ this.isLoading = false;
+ this.$emit('loadingError', error);
+ },
notifyChange() {
+ this.latestMarkdown = this.contentEditor.getSerializedContent();
+
this.$emit('change', {
empty: this.contentEditor.empty,
+ changed: this.contentEditor.changed,
+ markdown: this.latestMarkdown,
});
},
},
@@ -84,14 +141,7 @@ export default {
<template>
<content-editor-provider :content-editor="contentEditor">
<div>
- <editor-state-observer
- @docUpdate="notifyChange"
- @focus="focus"
- @blur="blur"
- @loading="$emit('loading')"
- @loadingSuccess="$emit('loadingSuccess')"
- @loadingError="$emit('loadingError')"
- />
+ <editor-state-observer @docUpdate="notifyChange" @focus="focus" @blur="blur" />
<content-editor-alert />
<div
data-testid="content-editor"
@@ -105,8 +155,12 @@ export default {
<code-block-bubble-menu />
<link-bubble-menu />
<media-bubble-menu />
- <tiptap-editor-content class="md" :editor="contentEditor.tiptapEditor" />
- <loading-indicator />
+ <tiptap-editor-content
+ class="md"
+ data-testid="content_editor_editablebox"
+ :editor="contentEditor.tiptapEditor"
+ />
+ <loading-indicator v-if="isLoading" />
</div>
</div>
</div>
diff --git a/app/assets/javascripts/content_editor/components/content_editor_alert.vue b/app/assets/javascripts/content_editor/components/content_editor_alert.vue
index c6737da1d77..87eff2451ec 100644
--- a/app/assets/javascripts/content_editor/components/content_editor_alert.vue
+++ b/app/assets/javascripts/content_editor/components/content_editor_alert.vue
@@ -14,19 +14,32 @@ export default {
};
},
methods: {
- displayAlert({ message, variant }) {
+ displayAlert({ message, variant, action, actionLabel }) {
this.message = message;
this.variant = variant;
+ this.action = action;
+ this.actionLabel = actionLabel;
},
dismissAlert() {
this.message = null;
},
+ primaryAction() {
+ this.dismissAlert();
+ this.action?.();
+ },
},
};
</script>
<template>
<editor-state-observer @alert="displayAlert">
- <gl-alert v-if="message" class="gl-mb-6" :variant="variant" @dismiss="dismissAlert">
+ <gl-alert
+ v-if="message"
+ class="gl-mb-6"
+ :variant="variant"
+ :primary-button-text="actionLabel"
+ @dismiss="dismissAlert"
+ @primaryAction="primaryAction"
+ >
{{ message }}
</gl-alert>
</editor-state-observer>
diff --git a/app/assets/javascripts/content_editor/components/editor_state_observer.vue b/app/assets/javascripts/content_editor/components/editor_state_observer.vue
index 252f69f7a5d..41c3771bf41 100644
--- a/app/assets/javascripts/content_editor/components/editor_state_observer.vue
+++ b/app/assets/javascripts/content_editor/components/editor_state_observer.vue
@@ -1,11 +1,6 @@
<script>
import { debounce } from 'lodash';
-import {
- LOADING_CONTENT_EVENT,
- LOADING_SUCCESS_EVENT,
- LOADING_ERROR_EVENT,
- ALERT_EVENT,
-} from '../constants';
+import { ALERT_EVENT } from '../constants';
export const tiptapToComponentMap = {
update: 'docUpdate',
@@ -15,12 +10,7 @@ export const tiptapToComponentMap = {
blur: 'blur',
};
-export const eventHubEvents = [
- ALERT_EVENT,
- LOADING_CONTENT_EVENT,
- LOADING_SUCCESS_EVENT,
- LOADING_ERROR_EVENT,
-];
+export const eventHubEvents = [ALERT_EVENT];
const getComponentEventName = (tiptapEventName) => tiptapToComponentMap[tiptapEventName];
diff --git a/app/assets/javascripts/content_editor/components/loading_indicator.vue b/app/assets/javascripts/content_editor/components/loading_indicator.vue
index 7bc953e0dc3..e2af6cabddb 100644
--- a/app/assets/javascripts/content_editor/components/loading_indicator.vue
+++ b/app/assets/javascripts/content_editor/components/loading_indicator.vue
@@ -1,40 +1,18 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
-import EditorStateObserver from './editor_state_observer.vue';
export default {
components: {
GlLoadingIcon,
- EditorStateObserver,
- },
- data() {
- return {
- isLoading: false,
- };
- },
- methods: {
- displayLoadingIndicator() {
- this.isLoading = true;
- },
- hideLoadingIndicator() {
- this.isLoading = false;
- },
},
};
</script>
<template>
- <editor-state-observer
- @loading="displayLoadingIndicator"
- @loadingSuccess="hideLoadingIndicator"
- @loadingError="hideLoadingIndicator"
+ <div
+ data-testid="content-editor-loading-indicator"
+ class="gl-w-full gl-display-flex gl-justify-content-center gl-align-items-center gl-absolute gl-top-0 gl-bottom-0"
>
- <div
- v-if="isLoading"
- data-testid="content-editor-loading-indicator"
- class="gl-w-full gl-display-flex gl-justify-content-center gl-align-items-center gl-absolute gl-top-0 gl-bottom-0"
- >
- <div class="gl-bg-white gl-absolute gl-w-full gl-h-full gl-opacity-3"></div>
- <gl-loading-icon size="lg" />
- </div>
- </editor-state-observer>
+ <div class="gl-bg-white gl-absolute gl-w-full gl-h-full gl-opacity-3"></div>
+ <gl-loading-icon size="lg" />
+ </div>
</template>
diff --git a/app/assets/javascripts/content_editor/components/toolbar_image_button.vue b/app/assets/javascripts/content_editor/components/toolbar_image_button.vue
index 649e23c29aa..8ed4dfce6de 100644
--- a/app/assets/javascripts/content_editor/components/toolbar_image_button.vue
+++ b/app/assets/javascripts/content_editor/components/toolbar_image_button.vue
@@ -71,27 +71,31 @@ export default {
};
</script>
<template>
- <gl-dropdown
- v-gl-tooltip
- :aria-label="__('Insert image')"
- :title="__('Insert image')"
- size="small"
- category="tertiary"
- icon="media"
- @hidden="resetFields()"
- >
- <gl-dropdown-form class="gl-px-3!">
- <gl-form-input-group v-model="imgSrc" :placeholder="__('Image URL')">
- <template #append>
- <gl-button variant="confirm" @click="insertImage">{{ __('Insert') }}</gl-button>
- </template>
- </gl-form-input-group>
- </gl-dropdown-form>
- <gl-dropdown-divider />
- <gl-dropdown-item @click="openFileUpload">
- {{ __('Upload image') }}
- </gl-dropdown-item>
-
+ <span class="gl-display-inline-flex">
+ <gl-dropdown
+ v-gl-tooltip
+ :text="__('Insert image')"
+ :title="__('Insert image')"
+ size="small"
+ category="tertiary"
+ icon="media"
+ lazy
+ text-sr-only
+ data-testid="insert-image-toolbar-button"
+ @hidden="resetFields()"
+ >
+ <gl-dropdown-form class="gl-px-3!">
+ <gl-form-input-group v-model="imgSrc" :placeholder="__('Image URL')">
+ <template #append>
+ <gl-button variant="confirm" @click="insertImage">{{ __('Insert') }}</gl-button>
+ </template>
+ </gl-form-input-group>
+ </gl-dropdown-form>
+ <gl-dropdown-divider />
+ <gl-dropdown-item @click="openFileUpload">
+ {{ __('Upload image') }}
+ </gl-dropdown-item>
+ </gl-dropdown>
<input
ref="fileSelector"
type="file"
@@ -101,5 +105,5 @@ export default {
data-qa-selector="file_upload_field"
@change="onFileSelect"
/>
- </gl-dropdown>
+ </span>
</template>
diff --git a/app/assets/javascripts/content_editor/components/toolbar_link_button.vue b/app/assets/javascripts/content_editor/components/toolbar_link_button.vue
index ff525e52873..4fb1e8ce16f 100644
--- a/app/assets/javascripts/content_editor/components/toolbar_link_button.vue
+++ b/app/assets/javascripts/content_editor/components/toolbar_link_button.vue
@@ -89,31 +89,34 @@ export default {
</script>
<template>
<editor-state-observer @transaction="updateLinkState">
- <gl-dropdown
- v-gl-tooltip
- :aria-label="__('Insert link')"
- :title="__('Insert link')"
- :toggle-class="{ active: isActive }"
- size="small"
- category="tertiary"
- icon="link"
- @show="selectLink()"
- >
- <gl-dropdown-form class="gl-px-3!">
- <gl-form-input-group v-model="linkHref" :placeholder="__('Link URL')">
- <template #append>
- <gl-button variant="confirm" @click="updateLink">{{ __('Apply') }}</gl-button>
- </template>
- </gl-form-input-group>
- </gl-dropdown-form>
- <gl-dropdown-divider />
- <gl-dropdown-item v-if="isActive" @click="removeLink">
- {{ __('Remove link') }}
- </gl-dropdown-item>
- <gl-dropdown-item v-else @click="openFileUpload">
- {{ __('Upload file') }}
- </gl-dropdown-item>
-
+ <span class="gl-display-inline-flex">
+ <gl-dropdown
+ v-gl-tooltip
+ :title="__('Insert link')"
+ :text="__('Insert link')"
+ :toggle-class="{ active: isActive }"
+ size="small"
+ category="tertiary"
+ icon="link"
+ text-sr-only
+ lazy
+ @show="selectLink()"
+ >
+ <gl-dropdown-form class="gl-px-3!">
+ <gl-form-input-group v-model="linkHref" :placeholder="__('Link URL')">
+ <template #append>
+ <gl-button variant="confirm" @click="updateLink">{{ __('Apply') }}</gl-button>
+ </template>
+ </gl-form-input-group>
+ </gl-dropdown-form>
+ <gl-dropdown-divider />
+ <gl-dropdown-item v-if="isActive" @click="removeLink">
+ {{ __('Remove link') }}
+ </gl-dropdown-item>
+ <gl-dropdown-item v-else @click="openFileUpload">
+ {{ __('Upload file') }}
+ </gl-dropdown-item>
+ </gl-dropdown>
<input
ref="fileSelector"
type="file"
@@ -121,6 +124,6 @@ export default {
class="gl-display-none"
@change="onFileSelect"
/>
- </gl-dropdown>
+ </span>
</editor-state-observer>
</template>
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 9ad739e7358..6bb122153ef 100644
--- a/app/assets/javascripts/content_editor/components/toolbar_more_dropdown.vue
+++ b/app/assets/javascripts/content_editor/components/toolbar_more_dropdown.vue
@@ -46,7 +46,18 @@ export default {
};
</script>
<template>
- <gl-dropdown size="small" category="tertiary" icon="plus" class="content-editor-dropdown" right>
+ <gl-dropdown
+ v-gl-tooltip
+ size="small"
+ category="tertiary"
+ icon="plus"
+ :text="__('More')"
+ :title="__('More')"
+ text-sr-only
+ class="content-editor-dropdown"
+ right
+ lazy
+ >
<gl-dropdown-item @click="insert('codeBlock')">
{{ __('Code block') }}
</gl-dropdown-item>
diff --git a/app/assets/javascripts/content_editor/components/toolbar_table_button.vue b/app/assets/javascripts/content_editor/components/toolbar_table_button.vue
index 18928acef3c..4b1929e1a20 100644
--- a/app/assets/javascripts/content_editor/components/toolbar_table_button.vue
+++ b/app/assets/javascripts/content_editor/components/toolbar_table_button.vue
@@ -1,5 +1,11 @@
<script>
-import { GlDropdown, GlDropdownDivider, GlDropdownForm, GlButton } from '@gitlab/ui';
+import {
+ GlDropdown,
+ GlDropdownDivider,
+ GlDropdownForm,
+ GlButton,
+ GlTooltipDirective as GlTooltip,
+} from '@gitlab/ui';
import { __, sprintf } from '~/locale';
import { clamp } from '../services/utils';
@@ -17,6 +23,9 @@ export default {
GlDropdownDivider,
GlDropdownForm,
},
+ directives: {
+ GlTooltip,
+ },
inject: ['tiptapEditor'],
data() {
return {
@@ -62,7 +71,18 @@ export default {
};
</script>
<template>
- <gl-dropdown size="small" category="tertiary" icon="table" class="content-editor-dropdown" right>
+ <gl-dropdown
+ v-gl-tooltip
+ size="small"
+ category="tertiary"
+ icon="table"
+ :title="__('Insert table')"
+ :text="__('Insert table')"
+ class="content-editor-dropdown"
+ right
+ text-sr-only
+ lazy
+ >
<gl-dropdown-form class="gl-px-3!">
<div v-for="r of list(maxRows)" :key="r" class="gl-display-flex">
<gl-button
diff --git a/app/assets/javascripts/content_editor/components/toolbar_text_style_dropdown.vue b/app/assets/javascripts/content_editor/components/toolbar_text_style_dropdown.vue
index 13728d4001d..2bf32a70cd1 100644
--- a/app/assets/javascripts/content_editor/components/toolbar_text_style_dropdown.vue
+++ b/app/assets/javascripts/content_editor/components/toolbar_text_style_dropdown.vue
@@ -64,6 +64,7 @@ export default {
data-qa-selector="text_style_dropdown"
:disabled="!activeItem"
:text="activeItemLabel"
+ lazy
>
<gl-dropdown-item
v-for="(item, index) in $options.items"
diff --git a/app/assets/javascripts/content_editor/components/top_toolbar.vue b/app/assets/javascripts/content_editor/components/top_toolbar.vue
index 1030ebbf838..460368b6a11 100644
--- a/app/assets/javascripts/content_editor/components/top_toolbar.vue
+++ b/app/assets/javascripts/content_editor/components/top_toolbar.vue
@@ -25,7 +25,7 @@ export default {
</script>
<template>
<div
- class="gl-display-flex gl-flex-wrap gl-pb-3 gl-pt-0 gl-border-b-solid gl-border-b-1 gl-border-b-gray-200"
+ 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"
diff --git a/app/assets/javascripts/content_editor/constants/index.js b/app/assets/javascripts/content_editor/constants/index.js
index a39a243ec6b..564cca23afa 100644
--- a/app/assets/javascripts/content_editor/constants/index.js
+++ b/app/assets/javascripts/content_editor/constants/index.js
@@ -58,3 +58,11 @@ export const EXTENSION_PRIORITY_LOWER = 75;
*/
export const EXTENSION_PRIORITY_DEFAULT = 100;
export const EXTENSION_PRIORITY_HIGHEST = 200;
+
+/**
+ * See lib/gitlab/file_type_detection.rb
+ */
+export const SAFE_VIDEO_EXT = ['mp4', 'm4v', 'mov', 'webm', 'ogv'];
+export const SAFE_AUDIO_EXT = ['mp3', 'oga', 'ogg', 'spx', 'wav'];
+
+export const DIAGRAM_LANGUAGES = ['plantuml', 'mermaid'];
diff --git a/app/assets/javascripts/content_editor/content_editor.stories.js b/app/assets/javascripts/content_editor/content_editor.stories.js
index 9329bbcb2c7..2d4226ccd33 100644
--- a/app/assets/javascripts/content_editor/content_editor.stories.js
+++ b/app/assets/javascripts/content_editor/content_editor.stories.js
@@ -2,7 +2,7 @@ import { ContentEditor } from './index';
export default {
component: ContentEditor,
- title: 'content_editor/components/content_editor',
+ title: 'content_editor/content_editor',
};
const Template = (_, { argTypes }) => ({
diff --git a/app/assets/javascripts/content_editor/extensions/paste_markdown.js b/app/assets/javascripts/content_editor/extensions/paste_markdown.js
index f87e4d8d1dd..848c4c12a9a 100644
--- a/app/assets/javascripts/content_editor/extensions/paste_markdown.js
+++ b/app/assets/javascripts/content_editor/extensions/paste_markdown.js
@@ -3,13 +3,7 @@ import { Plugin, PluginKey } from 'prosemirror-state';
import { __ } from '~/locale';
import { VARIANT_DANGER } from '~/flash';
import createMarkdownDeserializer from '../services/gl_api_markdown_deserializer';
-import {
- ALERT_EVENT,
- LOADING_CONTENT_EVENT,
- LOADING_SUCCESS_EVENT,
- LOADING_ERROR_EVENT,
- EXTENSION_PRIORITY_HIGHEST,
-} from '../constants';
+import { ALERT_EVENT, EXTENSION_PRIORITY_HIGHEST } from '../constants';
import CodeBlockHighlight from './code_block_highlight';
import Diagram from './diagram';
import Frontmatter from './frontmatter';
@@ -34,10 +28,8 @@ export default Extension.create({
const { renderMarkdown, eventHub } = options;
const deserializer = createMarkdownDeserializer({ render: renderMarkdown });
- eventHub.$emit(LOADING_CONTENT_EVENT);
-
deserializer
- .deserialize({ schema: editor.schema, content: markdown })
+ .deserialize({ schema: editor.schema, markdown })
.then(({ document }) => {
if (!document) {
return;
@@ -48,14 +40,12 @@ export default Extension.create({
tr.replaceWith(selection.from - 1, selection.to, document.content);
view.dispatch(tr);
- eventHub.$emit(LOADING_SUCCESS_EVENT);
})
.catch(() => {
eventHub.$emit(ALERT_EVENT, {
message: __('An error occurred while pasting text in the editor. Please try again.'),
variant: VARIANT_DANGER,
});
- eventHub.$emit(LOADING_ERROR_EVENT);
});
return true;
diff --git a/app/assets/javascripts/content_editor/extensions/sourcemap.js b/app/assets/javascripts/content_editor/extensions/sourcemap.js
index f9de71f601b..54d69d83188 100644
--- a/app/assets/javascripts/content_editor/extensions/sourcemap.js
+++ b/app/assets/javascripts/content_editor/extensions/sourcemap.js
@@ -1,9 +1,11 @@
import { Extension } from '@tiptap/core';
+import Audio from './audio';
import Blockquote from './blockquote';
import Bold from './bold';
import BulletList from './bullet_list';
import Code from './code';
import CodeBlockHighlight from './code_block_highlight';
+import Diagram from './diagram';
import FootnoteReference from './footnote_reference';
import FootnoteDefinition from './footnote_definition';
import Frontmatter from './frontmatter';
@@ -25,17 +27,21 @@ import Table from './table';
import TableCell from './table_cell';
import TableHeader from './table_header';
import TableRow from './table_row';
+import TableOfContents from './table_of_contents';
+import Video from './video';
export default Extension.create({
addGlobalAttributes() {
return [
{
types: [
+ Audio.name,
Bold.name,
Blockquote.name,
BulletList.name,
Code.name,
CodeBlockHighlight.name,
+ Diagram.name,
FootnoteReference.name,
FootnoteDefinition.name,
Frontmatter.name,
@@ -56,6 +62,8 @@ export default Extension.create({
TableCell.name,
TableHeader.name,
TableRow.name,
+ TableOfContents.name,
+ Video.name,
...HTMLNodes.map((htmlNode) => htmlNode.name),
],
attributes: {
diff --git a/app/assets/javascripts/content_editor/services/content_editor.js b/app/assets/javascripts/content_editor/services/content_editor.js
index 75d8581890f..514ab9699bc 100644
--- a/app/assets/javascripts/content_editor/services/content_editor.js
+++ b/app/assets/javascripts/content_editor/services/content_editor.js
@@ -1,5 +1,3 @@
-import { LOADING_CONTENT_EVENT, LOADING_SUCCESS_EVENT, LOADING_ERROR_EVENT } from '../constants';
-
/* eslint-disable no-underscore-dangle */
export class ContentEditor {
constructor({ tiptapEditor, serializer, deserializer, assetResolver, eventHub }) {
@@ -20,14 +18,19 @@ export class ContentEditor {
}
get changed() {
- return this._pristineDoc?.eq(this.tiptapEditor.state.doc);
+ if (!this._pristineDoc) {
+ return !this.empty;
+ }
+
+ return !this._pristineDoc.eq(this.tiptapEditor.state.doc);
}
get empty() {
- const doc = this.tiptapEditor?.state.doc;
+ return this.tiptapEditor.isEmpty;
+ }
- // Makes sure the document has more than one empty paragraph
- return doc.childCount === 0 || (doc.childCount === 1 && doc.child(0).childCount === 0);
+ get editable() {
+ return this.tiptapEditor.isEditable;
}
dispose() {
@@ -55,24 +58,22 @@ export class ContentEditor {
return this._assetResolver.renderDiagram(code, language);
}
+ setEditable(editable = true) {
+ this._tiptapEditor.setOptions({
+ editable,
+ });
+ }
+
async setSerializedContent(serializedContent) {
- const { _tiptapEditor: editor, _eventHub: eventHub } = this;
+ const { _tiptapEditor: editor } = this;
const { doc, tr } = editor.state;
- try {
- eventHub.$emit(LOADING_CONTENT_EVENT);
- const { document } = await this.deserialize(serializedContent);
-
- if (document) {
- this._pristineDoc = document;
- tr.replaceWith(0, doc.content.size, document).setMeta('preventUpdate', true);
- editor.view.dispatch(tr);
- }
+ const { document } = await this.deserialize(serializedContent);
- eventHub.$emit(LOADING_SUCCESS_EVENT);
- } catch (e) {
- eventHub.$emit(LOADING_ERROR_EVENT, e);
- throw e;
+ if (document) {
+ this._pristineDoc = document;
+ tr.replaceWith(0, doc.content.size, document).setMeta('preventUpdate', true);
+ editor.view.dispatch(tr);
}
}
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 7a289df94ea..5ed7f3dc23d 100644
--- a/app/assets/javascripts/content_editor/services/create_content_editor.js
+++ b/app/assets/javascripts/content_editor/services/create_content_editor.js
@@ -127,7 +127,7 @@ export const createContentEditor = ({
MathInline,
OrderedList,
Paragraph,
- PasteMarkdown,
+ PasteMarkdown.configure({ eventHub, renderMarkdown }),
Reference,
ReferenceDefinition,
Sourcemap,
diff --git a/app/assets/javascripts/content_editor/services/markdown_serializer.js b/app/assets/javascripts/content_editor/services/markdown_serializer.js
index 472a0a4815b..ba0cad6c91c 100644
--- a/app/assets/javascripts/content_editor/services/markdown_serializer.js
+++ b/app/assets/javascripts/content_editor/services/markdown_serializer.js
@@ -108,7 +108,10 @@ const defaultSerializerConfig = {
},
nodes: {
- [Audio.name]: renderPlayable,
+ [Audio.name]: preserveUnchanged({
+ render: renderPlayable,
+ inline: true,
+ }),
[Blockquote.name]: preserveUnchanged((state, node) => {
if (node.attrs.multiline) {
state.write('>>>');
@@ -123,7 +126,7 @@ const defaultSerializerConfig = {
}),
[BulletList.name]: preserveUnchanged(renderBulletList),
[CodeBlockHighlight.name]: preserveUnchanged(renderCodeBlock),
- [Diagram.name]: renderCodeBlock,
+ [Diagram.name]: preserveUnchanged(renderCodeBlock),
[DescriptionList.name]: renderHTMLNode('dl', true),
[DescriptionItem.name]: (state, node, parent, index) => {
if (index === 1) state.ensureNewLine();
@@ -203,10 +206,10 @@ const defaultSerializerConfig = {
},
overwriteSourcePreservationStrategy: true,
}),
- [TableOfContents.name]: (state, node) => {
+ [TableOfContents.name]: preserveUnchanged((state, node) => {
state.write('[[_TOC_]]');
state.closeBlock(node);
- },
+ }),
[Table.name]: preserveUnchanged(renderTable),
[TableCell.name]: renderTableCell,
[TableHeader.name]: renderTableCell,
@@ -220,7 +223,10 @@ const defaultSerializerConfig = {
else renderBulletList(state, node);
}),
[Text.name]: defaultMarkdownSerializer.nodes.text,
- [Video.name]: renderPlayable,
+ [Video.name]: preserveUnchanged({
+ render: renderPlayable,
+ inline: true,
+ }),
[WordBreak.name]: (state) => state.write('<wbr>'),
...HTMLNodes.reduce((serializers, htmlNode) => {
return {
diff --git a/app/assets/javascripts/content_editor/services/remark_markdown_deserializer.js b/app/assets/javascripts/content_editor/services/remark_markdown_deserializer.js
index 8a15633708f..ca290efca11 100644
--- a/app/assets/javascripts/content_editor/services/remark_markdown_deserializer.js
+++ b/app/assets/javascripts/content_editor/services/remark_markdown_deserializer.js
@@ -1,7 +1,10 @@
import { render } from '~/lib/gfm';
import { isValidAttribute } from '~/lib/dompurify';
+import { SAFE_AUDIO_EXT, SAFE_VIDEO_EXT, DIAGRAM_LANGUAGES } from '../constants';
import { createProseMirrorDocFromMdastTree } from './hast_to_prosemirror_converter';
+const ALL_AUDIO_VIDEO_EXT = [...SAFE_AUDIO_EXT, ...SAFE_VIDEO_EXT];
+
const wrappableTags = ['img', 'br', 'code', 'i', 'em', 'b', 'strong', 'a', 'strike', 's', 'del'];
const isTaskItem = (hastNode) => {
@@ -17,6 +20,32 @@ const getTableCellAttrs = (hastNode) => ({
rowspan: parseInt(hastNode.properties.rowSpan, 10) || 1,
});
+const getMediaAttrs = (hastNode) => ({
+ src: hastNode.properties.src,
+ canonicalSrc: hastNode.properties.identifier ?? hastNode.properties.src,
+ isReference: hastNode.properties.isReference === 'true',
+ title: hastNode.properties.title,
+ alt: hastNode.properties.alt,
+});
+
+const isMediaTag = (hastNode) => hastNode.tagName === 'img' && Boolean(hastNode.properties);
+
+const extractMediaFileExtension = (url) => {
+ try {
+ const parsedUrl = new URL(url, window.location.origin);
+
+ return /\.(\w+)$/.exec(parsedUrl.pathname)?.[1] ?? null;
+ } catch {
+ return null;
+ }
+};
+
+const isCodeBlock = (hastNode) => hastNode.tagName === 'codeblock';
+
+const isDiagramCodeBlock = (hastNode) => DIAGRAM_LANGUAGES.includes(hastNode.properties?.language);
+
+const getCodeBlockAttrs = (hastNode) => ({ language: hastNode.properties.language });
+
const factorySpecs = {
blockquote: { type: 'block', selector: 'blockquote' },
paragraph: { type: 'block', selector: 'p' },
@@ -45,8 +74,13 @@ const factorySpecs = {
},
codeBlock: {
type: 'block',
- selector: 'codeblock',
- getAttrs: (hastNode) => ({ ...hastNode.properties }),
+ selector: (hastNode) => isCodeBlock(hastNode) && !isDiagramCodeBlock(hastNode),
+ getAttrs: getCodeBlockAttrs,
+ },
+ diagram: {
+ type: 'block',
+ selector: (hastNode) => isCodeBlock(hastNode) && isDiagramCodeBlock(hastNode),
+ getAttrs: getCodeBlockAttrs,
},
horizontalRule: {
type: 'block',
@@ -121,16 +155,26 @@ const factorySpecs = {
selector: 'pre',
wrapInParagraph: true,
},
+ audio: {
+ type: 'inline',
+ selector: (hastNode) =>
+ isMediaTag(hastNode) &&
+ SAFE_AUDIO_EXT.includes(extractMediaFileExtension(hastNode.properties.src)),
+ getAttrs: getMediaAttrs,
+ },
image: {
type: 'inline',
- selector: 'img',
- getAttrs: (hastNode) => ({
- src: hastNode.properties.src,
- canonicalSrc: hastNode.properties.identifier ?? hastNode.properties.src,
- isReference: hastNode.properties.isReference === 'true',
- title: hastNode.properties.title,
- alt: hastNode.properties.alt,
- }),
+ selector: (hastNode) =>
+ isMediaTag(hastNode) &&
+ !ALL_AUDIO_VIDEO_EXT.includes(extractMediaFileExtension(hastNode.properties.src)),
+ getAttrs: getMediaAttrs,
+ },
+ video: {
+ type: 'inline',
+ selector: (hastNode) =>
+ isMediaTag(hastNode) &&
+ SAFE_VIDEO_EXT.includes(extractMediaFileExtension(hastNode.properties.src)),
+ getAttrs: getMediaAttrs,
},
hardBreak: {
type: 'inline',
@@ -193,6 +237,11 @@ const factorySpecs = {
language: hastNode.properties.language,
}),
},
+
+ tableOfContents: {
+ type: 'block',
+ selector: 'tableofcontents',
+ },
};
const SANITIZE_ALLOWLIST = ['level', 'identifier', 'numeric', 'language', 'url', 'isReference'];
@@ -250,6 +299,7 @@ export default () => {
'yaml',
'toml',
'json',
+ 'tableOfContents',
],
});
diff --git a/app/assets/javascripts/crm/constants.js b/app/assets/javascripts/crm/constants.js
index 815289e075e..832efa90956 100644
--- a/app/assets/javascripts/crm/constants.js
+++ b/app/assets/javascripts/crm/constants.js
@@ -5,3 +5,7 @@ export const trackViewsOptions = {
category: 'Customer Relations' /* eslint-disable-line @gitlab/require-i18n-strings */,
action: 'view_contacts_list',
};
+export const organizationTrackViewsOptions = {
+ category: 'Customer Relations' /* eslint-disable-line @gitlab/require-i18n-strings */,
+ action: 'view_organizations_list',
+};
diff --git a/app/assets/javascripts/crm/organizations/bundle.js b/app/assets/javascripts/crm/organizations/bundle.js
index 828d7cd426c..5897810a384 100644
--- a/app/assets/javascripts/crm/organizations/bundle.js
+++ b/app/assets/javascripts/crm/organizations/bundle.js
@@ -3,6 +3,7 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import VueRouter from 'vue-router';
import createDefaultClient from '~/lib/graphql';
+import { parseBoolean } from '~/lib/utils/common_utils';
import CrmOrganizationsRoot from './components/organizations_root.vue';
import routes from './routes';
@@ -21,7 +22,14 @@ export default () => {
return false;
}
- const { basePath, canAdminCrmOrganization, groupFullPath, groupId, groupIssuesPath } = el.dataset;
+ const {
+ basePath,
+ canAdminCrmOrganization,
+ groupFullPath,
+ groupId,
+ groupIssuesPath,
+ textQuery,
+ } = el.dataset;
const router = new VueRouter({
base: basePath,
@@ -33,7 +41,13 @@ export default () => {
el,
router,
apolloProvider,
- provide: { canAdminCrmOrganization, groupFullPath, groupId, groupIssuesPath },
+ provide: {
+ canAdminCrmOrganization: parseBoolean(canAdminCrmOrganization),
+ groupFullPath,
+ groupId,
+ groupIssuesPath,
+ textQuery,
+ },
render(createElement) {
return createElement(CrmOrganizationsRoot);
},
diff --git a/app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations.query.graphql b/app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations.query.graphql
index 97b75091cac..1bdcd9ba352 100644
--- a/app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations.query.graphql
+++ b/app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations.query.graphql
@@ -1,12 +1,37 @@
#import "./crm_organization_fields.fragment.graphql"
-query organizations($groupFullPath: ID!) {
+query organizations(
+ $groupFullPath: ID!
+ $state: CustomerRelationsOrganizationState
+ $searchTerm: String
+ $sort: OrganizationSort
+ $firstPageSize: Int
+ $lastPageSize: Int
+ $prevPageCursor: String = ""
+ $nextPageCursor: String = ""
+ $ids: [CustomerRelationsOrganizationID!]
+) {
group(fullPath: $groupFullPath) {
id
- organizations {
+ organizations(
+ state: $state
+ search: $searchTerm
+ sort: $sort
+ first: $firstPageSize
+ last: $lastPageSize
+ after: $nextPageCursor
+ before: $prevPageCursor
+ ids: $ids
+ ) {
nodes {
...OrganizationFragment
}
+ pageInfo {
+ hasNextPage
+ endCursor
+ hasPreviousPage
+ startCursor
+ }
}
}
}
diff --git a/app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations_count_by_state.query.graphql b/app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations_count_by_state.query.graphql
new file mode 100644
index 00000000000..fb6064e171f
--- /dev/null
+++ b/app/assets/javascripts/crm/organizations/components/graphql/get_group_organizations_count_by_state.query.graphql
@@ -0,0 +1,11 @@
+query organizationsCountByState($groupFullPath: ID!, $searchTerm: String) {
+ group(fullPath: $groupFullPath) {
+ __typename
+ id
+ organizationStateCounts(search: $searchTerm) {
+ all
+ active
+ inactive
+ }
+ }
+}
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 5fd0294b0ea..32900d45f22 100644
--- a/app/assets/javascripts/crm/organizations/components/organization_form_wrapper.vue
+++ b/app/assets/javascripts/crm/organizations/components/organization_form_wrapper.vue
@@ -36,7 +36,7 @@ export default {
getQuery() {
return {
query: getGroupOrganizationsQuery,
- variables: { groupFullPath: this.groupFullPath },
+ variables: { groupFullPath: this.groupFullPath, ids: [this.organizationGraphQLId] },
};
},
title() {
diff --git a/app/assets/javascripts/crm/organizations/components/organizations_root.vue b/app/assets/javascripts/crm/organizations/components/organizations_root.vue
index a165dd68603..155c8f00537 100644
--- a/app/assets/javascripts/crm/organizations/components/organizations_root.vue
+++ b/app/assets/javascripts/crm/organizations/components/organizations_root.vue
@@ -1,36 +1,54 @@
<script>
-import { GlAlert, GlButton, GlLoadingIcon, GlTable, GlTooltipDirective } from '@gitlab/ui';
-import { parseBoolean } from '~/lib/utils/common_utils';
+import { GlButton, GlLoadingIcon, GlTable, GlTooltipDirective } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { EDIT_ROUTE_NAME, NEW_ROUTE_NAME } from '../../constants';
+import PaginatedTableWithSearchAndTabs from '~/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue';
+import {
+ bodyTrClass,
+ initialPaginationState,
+} from '~/vue_shared/components/paginated_table_with_search_and_tabs/constants';
+import { convertToSnakeCase } from '~/lib/utils/text_utility';
+import { EDIT_ROUTE_NAME, NEW_ROUTE_NAME, organizationTrackViewsOptions } from '../../constants';
import getGroupOrganizationsQuery from './graphql/get_group_organizations.query.graphql';
+import getGroupOrganizationsCountByStateQuery from './graphql/get_group_organizations_count_by_state.query.graphql';
export default {
components: {
- GlAlert,
GlButton,
GlLoadingIcon,
GlTable,
+ PaginatedTableWithSearchAndTabs,
},
directives: {
GlTooltip: GlTooltipDirective,
},
- inject: ['canAdminCrmOrganization', 'groupFullPath', 'groupIssuesPath'],
+ inject: ['canAdminCrmOrganization', 'groupFullPath', 'groupIssuesPath', 'textQuery'],
data() {
return {
- organizations: [],
+ organizations: { list: [] },
+ organizationsCount: {},
error: false,
+ filteredByStatus: '',
+ pagination: initialPaginationState,
+ statusFilter: 'all',
+ searchTerm: this.textQuery,
+ sort: 'NAME_ASC',
+ sortDesc: false,
};
},
apollo: {
organizations: {
- query() {
- return getGroupOrganizationsQuery;
- },
+ query: getGroupOrganizationsQuery,
variables() {
return {
groupFullPath: this.groupFullPath,
+ searchTerm: this.searchTerm,
+ state: this.statusFilter,
+ sort: this.sort,
+ firstPageSize: this.pagination.firstPageSize,
+ lastPageSize: this.pagination.lastPageSize,
+ prevPageCursor: this.pagination.prevPageCursor,
+ nextPageCursor: this.pagination.nextPageCursor,
};
},
update(data) {
@@ -40,19 +58,52 @@ export default {
this.error = true;
},
},
+ organizationsCount: {
+ query: getGroupOrganizationsCountByStateQuery,
+ variables() {
+ return {
+ groupFullPath: this.groupFullPath,
+ searchTerm: this.searchTerm,
+ };
+ },
+ update(data) {
+ return data?.group?.organizationStateCounts;
+ },
+ error() {
+ this.error = true;
+ },
+ },
},
computed: {
isLoading() {
return this.$apollo.queries.organizations.loading;
},
- canAdmin() {
- return parseBoolean(this.canAdminCrmOrganization);
+ tbodyTrClass() {
+ return {
+ [bodyTrClass]: !this.loading && !this.isEmpty,
+ };
},
},
methods: {
+ errorAlertDismissed() {
+ this.error = false;
+ },
extractOrganizations(data) {
const organizations = data?.group?.organizations?.nodes || [];
- return organizations.slice().sort((a, b) => a.name.localeCompare(b.name));
+ const pageInfo = data?.group?.organizations?.pageInfo || {};
+ return {
+ list: organizations,
+ pageInfo,
+ };
+ },
+ fetchSortedData({ sortBy, sortDesc }) {
+ const sortingColumn = convertToSnakeCase(sortBy).toUpperCase();
+ const sortingDirection = sortDesc ? 'DESC' : 'ASC';
+ this.pagination = initialPaginationState;
+ this.sort = `${sortingColumn}_${sortingDirection}`;
+ },
+ filtersChanged({ searchTerm }) {
+ this.searchTerm = searchTerm;
},
getIssuesPath(path, value) {
return `${path}?crm_organization_id=${value}`;
@@ -60,6 +111,13 @@ export default {
getEditRoute(id) {
return { name: this.$options.EDIT_ROUTE_NAME, params: { id } };
},
+ pageChanged(pagination) {
+ this.pagination = pagination;
+ },
+ statusChanged({ filters, status }) {
+ this.statusFilter = filters;
+ this.filteredByStatus = status;
+ },
},
fields: [
{ key: 'name', sortable: true },
@@ -83,60 +141,113 @@ export default {
},
EDIT_ROUTE_NAME,
NEW_ROUTE_NAME,
+ statusTabs: [
+ {
+ title: __('Active'),
+ status: 'ACTIVE',
+ filters: 'active',
+ },
+ {
+ title: __('Inactive'),
+ status: 'INACTIVE',
+ filters: 'inactive',
+ },
+ {
+ title: __('All'),
+ status: 'ALL',
+ filters: 'all',
+ },
+ ],
+ organizationTrackViewsOptions,
+ emptyArray: [],
};
</script>
<template>
<div>
- <gl-alert v-if="error" variant="danger" class="gl-mt-6" @dismiss="error = false">
- {{ $options.i18n.errorText }}
- </gl-alert>
- <div
- class="gl-display-flex gl-align-items-baseline gl-flex-direction-row gl-justify-content-space-between gl-mt-6"
+ <paginated-table-with-search-and-tabs
+ :show-items="true"
+ :show-error-msg="false"
+ :i18n="$options.i18n"
+ :items="organizations.list"
+ :page-info="organizations.pageInfo"
+ :items-count="organizationsCount"
+ :status-tabs="$options.statusTabs"
+ :track-views-options="$options.organizationTrackViewsOptions"
+ :filter-search-tokens="$options.emptyArray"
+ filter-search-key="organizations"
+ @page-changed="pageChanged"
+ @tabs-changed="statusChanged"
+ @filters-changed="filtersChanged"
+ @error-alert-dismissed="errorAlertDismissed"
>
- <h2 class="gl-font-size-h2 gl-my-0">
- {{ $options.i18n.title }}
- </h2>
- <div
- v-if="canAdmin"
- class="gl-display-none gl-md-display-flex gl-align-items-center gl-justify-content-end"
- >
- <router-link :to="{ name: $options.NEW_ROUTE_NAME }">
- <gl-button variant="confirm" data-testid="new-organization-button">
+ <template #header-actions>
+ <router-link v-if="canAdminCrmOrganization" :to="{ name: $options.NEW_ROUTE_NAME }">
+ <gl-button
+ class="gl-my-3 gl-mr-5"
+ variant="confirm"
+ data-testid="new-organization-button"
+ >
{{ $options.i18n.newOrganization }}
</gl-button>
</router-link>
- </div>
- </div>
- <router-view />
- <gl-loading-icon v-if="isLoading" class="gl-mt-5" size="lg" />
- <gl-table
- v-else
- class="gl-mt-5"
- :items="organizations"
- :fields="$options.fields"
- :empty-text="$options.i18n.emptyText"
- show-empty
- >
- <template #cell(id)="{ value: id }">
- <gl-button
- v-gl-tooltip.hover.bottom="$options.i18n.issuesButtonLabel"
- class="gl-mr-3"
- data-testid="issues-link"
- icon="issues"
- :aria-label="$options.i18n.issuesButtonLabel"
- :href="getIssuesPath(groupIssuesPath, id)"
- />
- <router-link :to="getEditRoute(id)">
- <gl-button
- v-if="canAdmin"
- v-gl-tooltip.hover.bottom="$options.i18n.editButtonLabel"
- data-testid="edit-organization-button"
- icon="pencil"
- :aria-label="$options.i18n.editButtonLabel"
- />
- </router-link>
</template>
- </gl-table>
+
+ <template #title>
+ {{ $options.i18n.title }}
+ </template>
+
+ <template #table>
+ <gl-table
+ :items="organizations.list"
+ :fields="$options.fields"
+ :busy="isLoading"
+ stacked="md"
+ :tbody-tr-class="tbodyTrClass"
+ sort-direction="asc"
+ :sort-desc.sync="sortDesc"
+ sort-by="createdAt"
+ show-empty
+ no-local-sorting
+ sort-icon-left
+ fixed
+ @sort-changed="fetchSortedData"
+ >
+ <template #cell(id)="{ value: id }">
+ <gl-button
+ v-gl-tooltip.hover.bottom="$options.i18n.issuesButtonLabel"
+ class="gl-mr-3"
+ data-testid="issues-link"
+ icon="issues"
+ :aria-label="$options.i18n.issuesButtonLabel"
+ :href="getIssuesPath(groupIssuesPath, id)"
+ />
+ <router-link :to="getEditRoute(id)">
+ <gl-button
+ v-if="canAdminCrmOrganization"
+ v-gl-tooltip.hover.bottom="$options.i18n.editButtonLabel"
+ data-testid="edit-organization-button"
+ icon="pencil"
+ :aria-label="$options.i18n.editButtonLabel"
+ />
+ </router-link>
+ </template>
+
+ <template #table-busy>
+ <gl-loading-icon size="lg" color="dark" class="mt-3" />
+ </template>
+
+ <template #empty>
+ <span v-if="error">
+ {{ $options.i18n.errorText }}
+ </span>
+ <span v-else>
+ {{ $options.i18n.emptyText }}
+ </span>
+ </template>
+ </gl-table>
+ </template>
+ </paginated-table-with-search-and-tabs>
+ <router-view />
</div>
</template>
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_table.vue b/app/assets/javascripts/cycle_analytics/components/stage_table.vue
index 85a40b89b77..f1fdffd4b72 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_table.vue
+++ b/app/assets/javascripts/cycle_analytics/components/stage_table.vue
@@ -246,9 +246,7 @@ export default {
</p>
<p class="gl-m-0">
<span data-testid="vsa-stage-event-build-author-and-date">
- <gl-link class="gl-text-black-normal build-date" :href="item.url">{{
- item.date
- }}</gl-link>
+ <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"
diff --git a/app/assets/javascripts/cycle_analytics/components/total_time.vue b/app/assets/javascripts/cycle_analytics/components/total_time.vue
index a5a90a56974..725952c3518 100644
--- a/app/assets/javascripts/cycle_analytics/components/total_time.vue
+++ b/app/assets/javascripts/cycle_analytics/components/total_time.vue
@@ -52,7 +52,7 @@ export default {
};
</script>
<template>
- <span class="total-time">
+ <span>
<template v-if="hasData">
{{ calculatedTime.duration }} <span>{{ calculatedTime.units }}</span>
</template>
diff --git a/app/assets/javascripts/cycle_analytics/components/value_stream_filters.vue b/app/assets/javascripts/cycle_analytics/components/value_stream_filters.vue
index f686cd0db95..17decb6b448 100644
--- a/app/assets/javascripts/cycle_analytics/components/value_stream_filters.vue
+++ b/app/assets/javascripts/cycle_analytics/components/value_stream_filters.vue
@@ -57,6 +57,10 @@ export default {
includeSubgroups: true,
};
},
+ currentDate() {
+ const now = new Date();
+ return new Date(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
+ },
},
multiProjectSelect: true,
maxDateRange: DATE_RANGE_LIMIT,
@@ -93,6 +97,7 @@ export default {
v-if="hasDateRangeFilter"
:start-date="startDate"
:end-date="endDate"
+ :max-date="currentDate"
:max-date-range="$options.maxDateRange"
:include-selected-date="true"
class="js-daterange-picker"
diff --git a/app/assets/javascripts/cycle_analytics/store/getters.js b/app/assets/javascripts/cycle_analytics/store/getters.js
index 6fe353405d4..83068cabf0f 100644
--- a/app/assets/javascripts/cycle_analytics/store/getters.js
+++ b/app/assets/javascripts/cycle_analytics/store/getters.js
@@ -1,5 +1,5 @@
-import dateFormat from 'dateformat';
import { dateFormats } from '~/analytics/shared/constants';
+import dateFormat from '~/lib/dateformat';
import { filterToQueryObject } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
import { PAGINATION_TYPE } from '../constants';
import { transformStagesForPathNavigation, filterStagesByHiddenStatus } from '../utils';
diff --git a/app/assets/javascripts/deploy_keys/components/key.vue b/app/assets/javascripts/deploy_keys/components/key.vue
index d811bb3b0bf..c9097b9384f 100644
--- a/app/assets/javascripts/deploy_keys/components/key.vue
+++ b/app/assets/javascripts/deploy_keys/components/key.vue
@@ -4,11 +4,11 @@ import { head, tail } from 'lodash';
import { s__, sprintf } from '~/locale';
import timeagoMixin from '~/vue_shared/mixins/timeago';
-import actionBtn from './action_btn.vue';
+import ActionBtn from './action_btn.vue';
export default {
components: {
- actionBtn,
+ ActionBtn,
GlButton,
GlIcon,
GlLink,
diff --git a/app/assets/javascripts/deploy_keys/components/keys_panel.vue b/app/assets/javascripts/deploy_keys/components/keys_panel.vue
index d71f4f5507f..77ec1ef590f 100644
--- a/app/assets/javascripts/deploy_keys/components/keys_panel.vue
+++ b/app/assets/javascripts/deploy_keys/components/keys_panel.vue
@@ -1,9 +1,9 @@
<script>
-import deployKey from './key.vue';
+import DeployKey from './key.vue';
export default {
components: {
- deployKey,
+ DeployKey,
},
props: {
keys: {
diff --git a/app/assets/javascripts/deploy_keys/index.js b/app/assets/javascripts/deploy_keys/index.js
index 6e439be42ae..83601d5b2e3 100644
--- a/app/assets/javascripts/deploy_keys/index.js
+++ b/app/assets/javascripts/deploy_keys/index.js
@@ -1,11 +1,11 @@
import Vue from 'vue';
-import deployKeysApp from './components/app.vue';
+import DeployKeysApp from './components/app.vue';
export default () =>
new Vue({
el: document.getElementById('js-deploy-keys'),
components: {
- deployKeysApp,
+ DeployKeysApp,
},
data() {
return {
diff --git a/app/assets/javascripts/deprecated_jquery_dropdown/render.js b/app/assets/javascripts/deprecated_jquery_dropdown/render.js
index f10c2d82b61..0f612989bb4 100644
--- a/app/assets/javascripts/deprecated_jquery_dropdown/render.js
+++ b/app/assets/javascripts/deprecated_jquery_dropdown/render.js
@@ -13,6 +13,7 @@ const renderersByType = {
},
header(element, data) {
element.classList.add('dropdown-header');
+ // eslint-disable-next-line no-unsanitized/property
element.innerHTML = data.content;
return element;
@@ -122,6 +123,7 @@ function assignTextToLink(el, data, options) {
const text = getLinkText(data, options);
if (options.icon || options.highlight) {
+ // eslint-disable-next-line no-unsanitized/property
el.innerHTML = text;
} else {
el.textContent = text;
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 ac00af2ab34..124780df8a5 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
@@ -174,6 +174,7 @@ export default {
this.$emit('open-form', this.discussion.id);
this.isFormRendered = true;
},
+
toggleResolvedStatus() {
this.isResolving = true;
@@ -234,6 +235,7 @@ export default {
:note="firstNote"
:markdown-preview-path="markdownPreviewPath"
:is-resolving="isResolving"
+ :noteable-id="noteableId"
:class="{ 'gl-bg-blue-50': isDiscussionActive }"
@error="$emit('update-note-error', $event)"
>
@@ -276,6 +278,7 @@ export default {
:note="note"
:markdown-preview-path="markdownPreviewPath"
:is-resolving="isResolving"
+ :noteable-id="noteableId"
:class="{ 'gl-bg-blue-50': isDiscussionActive }"
@error="$emit('update-note-error', $event)"
/>
@@ -307,6 +310,8 @@ export default {
v-model="discussionComment"
:is-saving="loading"
:markdown-preview-path="markdownPreviewPath"
+ :noteable-id="noteableId"
+ :discussion-id="discussion.id"
@submit-form="mutate"
@cancel-form="hideForm"
>
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 5fb5989e11a..e629f74ba02 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
@@ -45,6 +45,10 @@ export default {
required: false,
default: '',
},
+ noteableId: {
+ type: String,
+ required: true,
+ },
},
data() {
return {
@@ -160,6 +164,7 @@ export default {
:is-saving="loading"
:markdown-preview-path="markdownPreviewPath"
:is-new-comment="false"
+ :noteable-id="noteableId"
class="gl-mt-5"
@submit-form="mutate"
@cancel-form="hideForm"
diff --git a/app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue b/app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue
index 1b6458668f5..4faeba3983b 100644
--- a/app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue
+++ b/app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue
@@ -1,7 +1,11 @@
<script>
import { GlButton, GlModal } from '@gitlab/ui';
+import $ from 'jquery';
import { helpPagePath } from '~/helpers/help_page_helper';
import { s__ } from '~/locale';
+import Autosave from '~/autosave';
+import { isLoggedIn } from '~/lib/utils/common_utils';
+import { getIdFromGraphQLId, isGid } from '~/graphql_shared/utils';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
export default {
@@ -30,10 +34,20 @@ export default {
required: false,
default: true,
},
+ noteableId: {
+ type: String,
+ required: true,
+ },
+ discussionId: {
+ type: String,
+ required: false,
+ default: 'new',
+ },
},
data() {
return {
formText: this.value,
+ isLoggedIn: isLoggedIn(),
};
},
computed: {
@@ -64,13 +78,19 @@ export default {
markdownDocsPath() {
return helpPagePath('user/markdown');
},
+ shortDiscussionId() {
+ return isGid(this.discussionId) ? getIdFromGraphQLId(this.discussionId) : this.discussionId;
+ },
},
mounted() {
this.focusInput();
},
methods: {
submitForm() {
- if (this.hasValue) this.$emit('submit-form');
+ if (this.hasValue) {
+ this.$emit('submit-form');
+ this.autosaveDiscussion.reset();
+ }
},
cancelComment() {
if (this.hasValue && this.formText !== this.value) {
@@ -79,8 +99,22 @@ export default {
this.$emit('cancel-form');
}
},
+ confirmCancelCommentModal() {
+ this.$emit('cancel-form');
+ this.autosaveDiscussion.reset();
+ },
focusInput() {
this.$refs.textarea.focus();
+ this.initAutosaveComment();
+ },
+ initAutosaveComment() {
+ if (this.isLoggedIn) {
+ this.autosaveDiscussion = new Autosave($(this.$refs.textarea), [
+ s__('DesignManagement|Discussion'),
+ getIdFromGraphQLId(this.noteableId),
+ this.shortDiscussionId,
+ ]);
+ }
},
},
};
@@ -124,7 +158,7 @@ export default {
type="submit"
data-track-action="click_button"
data-qa-selector="save_comment_button"
- @click="$emit('submit-form')"
+ @click="submitForm"
>
{{ buttonText }}
</gl-button>
@@ -144,7 +178,7 @@ export default {
:ok-title="modalSettings.okTitle"
:cancel-title="modalSettings.cancelTitle"
modal-id="cancel-comment-modal"
- @ok="$emit('cancel-form')"
+ @ok="confirmCancelCommentModal"
>{{ modalSettings.content }}
</gl-modal>
</form>
diff --git a/app/assets/javascripts/design_management/components/list/item.vue b/app/assets/javascripts/design_management/components/list/item.vue
index 3092b8554ac..1e36aa686a4 100644
--- a/app/assets/javascripts/design_management/components/list/item.vue
+++ b/app/assets/javascripts/design_management/components/list/item.vue
@@ -128,7 +128,7 @@ export default {
params: { id: filename },
query: $route.query,
}"
- class="card gl-cursor-pointer text-plain js-design-list-item design-list-item design-list-item-new"
+ class="card gl-cursor-pointer text-plain js-design-list-item design-list-item design-list-item-new gl-mb-0"
>
<div
class="card-body gl-p-0 gl-display-flex gl-align-items-center gl-justify-content-center gl-overflow-hidden gl-relative"
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 816d7ac7abf..f10545faea6 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
@@ -73,8 +73,8 @@ export default {
<gl-dropdown-item
v-for="(version, index) in allVersions"
:key="version.id"
- :is-check-item="true"
- :is-check-centered="true"
+ is-check-item
+ is-check-centered
:is-checked="findVersionId(version.id) === currentVersionId"
:avatar-url="getAvatarUrl(version)"
@click="routeToVersion(version.id)"
diff --git a/app/assets/javascripts/design_management/pages/design/index.vue b/app/assets/javascripts/design_management/pages/design/index.vue
index 1825ce7f092..228ad637b9e 100644
--- a/app/assets/javascripts/design_management/pages/design/index.vue
+++ b/app/assets/javascripts/design_management/pages/design/index.vue
@@ -418,6 +418,7 @@ export default {
v-model="comment"
:is-saving="loading"
:markdown-preview-path="markdownPreviewPath"
+ :noteable-id="design.id"
@submit-form="mutate"
@cancel-form="closeCommentForm"
/> </apollo-mutation
diff --git a/app/assets/javascripts/design_management/pages/index.vue b/app/assets/javascripts/design_management/pages/index.vue
index 91e35ad3764..07f7a19f7d4 100644
--- a/app/assets/javascripts/design_management/pages/index.vue
+++ b/app/assets/javascripts/design_management/pages/index.vue
@@ -135,7 +135,7 @@ export default {
designDropzoneWrapperClass() {
return this.isDesignListEmpty
? 'col-12'
- : 'gl-flex-direction-column col-md-6 col-lg-3 gl-mb-3';
+ : 'gl-flex-direction-column col-md-6 col-lg-3 gl-mt-5';
},
},
mounted() {
@@ -364,15 +364,15 @@ export default {
data-testid="design-toolbar-wrapper"
>
<div
- class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-w-full gl-flex-wrap"
+ class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-w-full gl-flex-wrap gl-gap-3"
>
- <div class="gl-display-flex gl-align-items-center gl-my-2">
+ <div class="gl-display-flex gl-align-items-center">
<span class="gl-font-weight-bold gl-mr-3">{{ s__('DesignManagement|Designs') }}</span>
<design-version-dropdown />
</div>
<div
v-show="hasDesigns"
- class="gl-display-flex gl-align-items-center gl-my-2"
+ class="gl-display-flex gl-align-items-center"
data-testid="design-selector-toolbar"
>
<gl-button
@@ -413,7 +413,7 @@ export default {
</div>
</div>
</header>
- <div class="gl-mt-6">
+ <div>
<gl-loading-icon v-if="isLoading" size="lg" />
<gl-alert v-else-if="error" variant="danger" :dismissible="false">
{{ __('An error occurred while loading designs. Please try again.') }}
@@ -449,7 +449,7 @@ export default {
<li
v-for="design in designs"
:key="design.id"
- class="col-md-6 col-lg-3 gl-mb-3 gl-bg-transparent gl-shadow-none js-design-tile"
+ class="col-md-6 col-lg-3 gl-mt-5 gl-bg-transparent gl-shadow-none js-design-tile"
>
<design-dropzone
:display-as-card="hasDesigns"
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 530f3a3a7f7..f5c0776ca35 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -331,6 +331,8 @@ export default {
mrReviews: this.rehydratedMrReviews,
});
+ this.interfaceWithDOM();
+
if (this.endpointCodequality) {
this.setCodequalityEndpoint(this.endpointCodequality);
}
@@ -445,6 +447,16 @@ export default {
notesEventHub.$off('refetchDiffData', this.refetchDiffData);
notesEventHub.$off('fetchDiffData', this.fetchData);
},
+ interfaceWithDOM() {
+ this.diffsTab = document.querySelector('.js-diffs-tab');
+ },
+ updateChangesTabCount() {
+ const badge = this.diffsTab.querySelector('.gl-badge');
+
+ if (this.diffsTab && badge) {
+ badge.textContent = this.diffFilesLength;
+ }
+ },
navigateToDiffFileNumber(number) {
this.navigateToDiffFileIndex(number - 1);
},
@@ -461,7 +473,11 @@ export default {
this.fetchDiffFilesMeta()
.then(({ real_size }) => {
this.diffFilesLength = parseInt(real_size, 10);
- if (toggleTree) this.setTreeDisplay();
+ if (toggleTree) {
+ this.setTreeDisplay();
+ }
+
+ this.updateChangesTabCount();
})
.catch(() => {
createFlash({
@@ -641,6 +657,7 @@ export default {
<div
v-if="renderFileTree"
:style="{ width: `${treeWidth}px` }"
+ :class="{ 'is-sidebar-moved': glFeatures.movedMrSidebar }"
class="diff-tree-list js-diff-tree-list gl-px-5"
>
<panel-resizer
diff --git a/app/assets/javascripts/diffs/components/compare_dropdown_layout.vue b/app/assets/javascripts/diffs/components/compare_dropdown_layout.vue
index fd219a7d00f..4501988ee4f 100644
--- a/app/assets/javascripts/diffs/components/compare_dropdown_layout.vue
+++ b/app/assets/javascripts/diffs/components/compare_dropdown_layout.vue
@@ -37,7 +37,7 @@ export default {
:class="{
'is-active': version.selected,
}"
- :is-check-item="true"
+ is-check-item
:is-checked="version.selected"
:href="version.href"
>
diff --git a/app/assets/javascripts/diffs/components/diff_code_quality.vue b/app/assets/javascripts/diffs/components/diff_code_quality.vue
index f339b108a11..8498724740f 100644
--- a/app/assets/javascripts/diffs/components/diff_code_quality.vue
+++ b/app/assets/javascripts/diffs/components/diff_code_quality.vue
@@ -5,10 +5,6 @@ import { SEVERITY_CLASSES, SEVERITY_ICONS } from '~/reports/codequality_report/c
export default {
components: { GlButton, GlIcon },
props: {
- line: {
- type: Number,
- required: true,
- },
codeQuality: {
type: Array,
required: true,
@@ -33,7 +29,7 @@ export default {
<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"
+ 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"
>
<gl-icon
:size="12"
@@ -50,7 +46,7 @@ export default {
size="small"
icon="close"
class="gl-absolute gl-right-2 gl-top-2"
- @click="$emit('hideCodeQualityFindings', line)"
+ @click="$emit('hideCodeQualityFindings')"
/>
</div>
</template>
diff --git a/app/assets/javascripts/diffs/components/diff_content.vue b/app/assets/javascripts/diffs/components/diff_content.vue
index 70071a3ff53..d7b63d205dc 100644
--- a/app/assets/javascripts/diffs/components/diff_content.vue
+++ b/app/assets/javascripts/diffs/components/diff_content.vue
@@ -11,7 +11,7 @@ import NoPreviewViewer from '~/vue_shared/components/diff_viewer/viewers/no_prev
import NotDiffableViewer from '~/vue_shared/components/diff_viewer/viewers/not_diffable.vue';
import NoteForm from '~/notes/components/note_form.vue';
import eventHub from '~/notes/event_hub';
-import userAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
+import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import { IMAGE_DIFF_POSITION_TYPE } from '../constants';
import { getDiffMode } from '../store/utils';
import DiffDiscussions from './diff_discussions.vue';
@@ -28,7 +28,7 @@ export default {
ImageDiffOverlay,
NotDiffableViewer,
NoPreviewViewer,
- userAvatarLink,
+ UserAvatarLink,
DiffFileDrafts,
},
mixins: [diffLineNoteFormMixin, draftCommentsMixin],
diff --git a/app/assets/javascripts/diffs/components/diff_discussions.vue b/app/assets/javascripts/diffs/components/diff_discussions.vue
index b39b50c4cdc..25d3bda147b 100644
--- a/app/assets/javascripts/diffs/components/diff_discussions.vue
+++ b/app/assets/javascripts/diffs/components/diff_discussions.vue
@@ -2,11 +2,11 @@
import { GlIcon } from '@gitlab/ui';
import { mapActions } from 'vuex';
import DesignNotePin from '~/vue_shared/components/design_management/design_note_pin.vue';
-import noteableDiscussion from '~/notes/components/noteable_discussion.vue';
+import NoteableDiscussion from '~/notes/components/noteable_discussion.vue';
export default {
components: {
- noteableDiscussion,
+ NoteableDiscussion,
GlIcon,
DesignNotePin,
},
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index 07316f9433a..705b43a222d 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -19,6 +19,7 @@ import { scrollToElement } from '~/lib/utils/common_utils';
import { truncateSha } from '~/lib/utils/text_utility';
import { __, s__, sprintf } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { DIFF_FILE_AUTOMATIC_COLLAPSE } from '../constants';
import { DIFF_FILE_HEADER } from '../i18n';
@@ -45,7 +46,7 @@ export default {
GlTooltip: GlTooltipDirective,
SafeHtml: GlSafeHtmlDirective,
},
- mixins: [IdState({ idProp: (vm) => vm.diffFile.file_hash })],
+ mixins: [IdState({ idProp: (vm) => vm.diffFile.file_hash }), glFeatureFlagsMixin()],
i18n: {
...DIFF_FILE_HEADER,
compareButtonLabel: __('Compare submodule commit revisions'),
@@ -276,7 +277,10 @@ export default {
<template>
<div
ref="header"
- :class="{ 'gl-z-dropdown-menu!': idState.moreActionsShown }"
+ :class="{
+ 'gl-z-dropdown-menu!': idState.moreActionsShown,
+ 'is-sidebar-moved': glFeatures.movedMrSidebar,
+ }"
class="js-file-title file-title file-title-flex-parent"
data-qa-selector="file_title_container"
:data-qa-file-name="filePath"
diff --git a/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue b/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue
index a077c8ae3af..8553bdd3020 100644
--- a/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue
+++ b/app/assets/javascripts/diffs/components/diff_gutter_avatars.vue
@@ -4,6 +4,7 @@ import { truncate } from '~/lib/utils/text_utility';
import { n__ } from '~/locale';
import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
import { COUNT_OF_AVATARS_IN_GUTTER, LENGTH_OF_AVATAR_TOOLTIP } from '../constants';
+import { HIDE_COMMENTS } from '../i18n';
export default {
components: {
@@ -55,6 +56,9 @@ export default {
return `${noteData.author.name}: ${note}`;
},
},
+ i18n: {
+ HIDE_COMMENTS,
+ },
};
</script>
@@ -62,8 +66,10 @@ export default {
<div class="diff-comment-avatar-holders">
<button
v-if="discussionsExpanded"
+ v-gl-tooltip
+ :title="$options.i18n.HIDE_COMMENTS"
type="button"
- :aria-label="__('Show comments')"
+ :aria-label="$options.i18n.HIDE_COMMENTS"
class="diff-notes-collapse js-diff-comment-avatar js-diff-comment-button"
@click="$emit('toggleLineDiscussions')"
>
diff --git a/app/assets/javascripts/diffs/components/diff_line.vue b/app/assets/javascripts/diffs/components/diff_line.vue
new file mode 100644
index 00000000000..448272549d3
--- /dev/null
+++ b/app/assets/javascripts/diffs/components/diff_line.vue
@@ -0,0 +1,35 @@
+<script>
+import DiffCodeQuality from './diff_code_quality.vue';
+
+export default {
+ components: {
+ DiffCodeQuality,
+ },
+ props: {
+ line: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ parsedCodeQuality() {
+ return (this.line.left ?? this.line.right)?.codequality;
+ },
+ codeQualityLineNumber() {
+ return this.parsedCodeQuality[0].line;
+ },
+ },
+ methods: {
+ hideCodeQualityFindings() {
+ this.$emit('hideCodeQualityFindings', this.codeQualityLineNumber);
+ },
+ },
+};
+</script>
+
+<template>
+ <diff-code-quality
+ :code-quality="parsedCodeQuality"
+ @hideCodeQualityFindings="hideCodeQualityFindings"
+ />
+</template>
diff --git a/app/assets/javascripts/diffs/components/diff_line_note_form.vue b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
index 467a0f8d2db..f63ab1bb067 100644
--- a/app/assets/javascripts/diffs/components/diff_line_note_form.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
@@ -7,7 +7,7 @@ import { ignoreWhilePending } from '~/lib/utils/ignore_while_pending';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import MultilineCommentForm from '~/notes/components/multiline_comment_form.vue';
import { commentLineOptions, formatLineRange } from '~/notes/components/multiline_comment_utils';
-import noteForm from '~/notes/components/note_form.vue';
+import NoteForm from '~/notes/components/note_form.vue';
import autosave from '~/notes/mixins/autosave';
import {
DIFF_NOTE_TYPE,
@@ -18,7 +18,7 @@ import {
export default {
components: {
- noteForm,
+ NoteForm,
MultilineCommentForm,
},
mixins: [autosave, diffLineNoteFormMixin, glFeatureFlagsMixin()],
diff --git a/app/assets/javascripts/diffs/components/diff_row.vue b/app/assets/javascripts/diffs/components/diff_row.vue
index 63c5aedd7ce..e5695c4390f 100644
--- a/app/assets/javascripts/diffs/components/diff_row.vue
+++ b/app/assets/javascripts/diffs/components/diff_row.vue
@@ -64,6 +64,11 @@ export default {
type: Function,
required: true,
},
+ codeQualityExpanded: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
classNameMap: memoize(
(props) => {
@@ -272,6 +277,7 @@ export default {
<component
:is="$options.CodeQualityGutterIcon"
v-if="$options.showCodequalityLeft(props)"
+ :code-quality-expanded="props.codeQualityExpanded"
:codequality="props.line.left.codequality"
:file-path="props.filePath"
@showCodeQualityFindings="
diff --git a/app/assets/javascripts/diffs/components/diff_view.vue b/app/assets/javascripts/diffs/components/diff_view.vue
index ea94df1ad5b..91bf3283379 100644
--- a/app/assets/javascripts/diffs/components/diff_view.vue
+++ b/app/assets/javascripts/diffs/components/diff_view.vue
@@ -9,7 +9,7 @@ import { getCommentedLines } from '~/notes/components/multiline_comment_utils';
import { hide } from '~/tooltips';
import { pickDirection } from '../utils/diff_line';
import DiffCommentCell from './diff_comment_cell.vue';
-import DiffCodeQuality from './diff_code_quality.vue';
+import DiffLine from './diff_line.vue';
import DiffExpansionCell from './diff_expansion_cell.vue';
import DiffRow from './diff_row.vue';
import { isHighlighted } from './diff_row_utils';
@@ -18,8 +18,8 @@ export default {
components: {
DiffExpansionCell,
DiffRow,
+ DiffLine,
DiffCommentCell,
- DiffCodeQuality,
DraftNote,
},
directives: {
@@ -96,10 +96,6 @@ export default {
}
this.idState.dragStart = line;
},
- parseCodeQuality(line) {
- return (line.left ?? line.right)?.codequality;
- },
-
hideCodeQualityFindings(line) {
const index = this.codeQualityExpandedLines.indexOf(line);
if (index > -1) {
@@ -179,7 +175,7 @@ export default {
);
},
getCodeQualityLine(line) {
- return this.parseCodeQuality(line)?.[0]?.line;
+ return (line.left ?? line.right)?.codequality?.[0]?.line;
},
},
userColorScheme: window.gon.user_color_scheme,
@@ -234,6 +230,7 @@ export default {
:is-commented="index >= commentedLines.startLine && index <= commentedLines.endLine"
:inline="inline"
:index="index"
+ :code-quality-expanded="codeQualityExpandedLines.includes(getCodeQualityLine(line))"
:is-highlighted="isHighlighted(line)"
:file-line-coverage="fileLineCoverage"
:coverage-loaded="coverageLoaded"
@@ -248,15 +245,13 @@ export default {
@startdragging="onStartDragging"
@stopdragging="onStopDragging"
/>
-
- <diff-code-quality
+ <diff-line
v-if="
glFeatures.refactorCodeQualityInlineFindings &&
codeQualityExpandedLines.includes(getCodeQualityLine(line))
"
:key="line.line_code"
- :line="getCodeQualityLine(line)"
- :code-quality="parseCodeQuality(line)"
+ :line="line"
@hideCodeQualityFindings="hideCodeQualityFindings"
/>
<div
diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js
index 1cc96ef3d54..6c0c9c4e1d0 100644
--- a/app/assets/javascripts/diffs/constants.js
+++ b/app/assets/javascripts/diffs/constants.js
@@ -71,15 +71,12 @@ export const DIFF_FILE_MANUAL_COLLAPSE = 'manual';
export const STATE_IDLING = 'idle';
export const STATE_LOADING = 'loading';
export const STATE_ERRORED = 'errored';
-export const STATE_PENDING_REVIEW = 'pending_comments';
// State machine transitions
export const TRANSITION_LOAD_START = 'LOAD_START';
export const TRANSITION_LOAD_ERROR = 'LOAD_ERROR';
export const TRANSITION_LOAD_SUCCEED = 'LOAD_SUCCEED';
export const TRANSITION_ACKNOWLEDGE_ERROR = 'ACKNOWLEDGE_ERROR';
-export const TRANSITION_HAS_PENDING_REVIEW = 'PENDING_REVIEW';
-export const TRANSITION_NO_REVIEW = 'NO_REVIEW';
export const RENAMED_DIFF_TRANSITIONS = {
[`${STATE_IDLING}:${TRANSITION_LOAD_START}`]: STATE_LOADING,
diff --git a/app/assets/javascripts/diffs/i18n.js b/app/assets/javascripts/diffs/i18n.js
index e617890af2e..f7f4aad3ad0 100644
--- a/app/assets/javascripts/diffs/i18n.js
+++ b/app/assets/javascripts/diffs/i18n.js
@@ -47,3 +47,5 @@ export const CONFLICT_TEXT = {
'Conflict: This file was added both in the source and target branches, but with different contents.',
),
};
+
+export const HIDE_COMMENTS = __('Hide comments');
diff --git a/app/assets/javascripts/diffs/index.js b/app/assets/javascripts/diffs/index.js
index 1691da34c6d..b4ff5e4f250 100644
--- a/app/assets/javascripts/diffs/index.js
+++ b/app/assets/javascripts/diffs/index.js
@@ -3,7 +3,7 @@ import { mapActions, mapState, mapGetters } from 'vuex';
import { getCookie, parseBoolean, removeCookie } from '~/lib/utils/common_utils';
import eventHub from '../notes/event_hub';
-import diffsApp from './components/app.vue';
+import DiffsApp from './components/app.vue';
import { TREE_LIST_STORAGE_KEY, DIFF_WHITESPACE_COOKIE_NAME } from './constants';
import { getReviewsForMergeRequest } from './utils/file_reviews';
@@ -14,7 +14,7 @@ export default function initDiffsApp(store) {
el: '#js-diffs-app',
name: 'MergeRequestDiffs',
components: {
- diffsApp,
+ DiffsApp,
},
store,
data() {
diff --git a/app/assets/javascripts/environments/components/deploy_board.vue b/app/assets/javascripts/environments/components/deploy_board.vue
index 7a2c9a8600e..f22a0705b3d 100644
--- a/app/assets/javascripts/environments/components/deploy_board.vue
+++ b/app/assets/javascripts/environments/components/deploy_board.vue
@@ -20,13 +20,13 @@ import {
} from '@gitlab/ui';
import { isEmpty } from 'lodash';
import { s__, n__ } from '~/locale';
-import instanceComponent from '~/vue_shared/components/deployment_instance.vue';
+import InstanceComponent from '~/vue_shared/components/deployment_instance.vue';
import { STATUS_MAP, CANARY_STATUS } from '../constants';
import CanaryIngress from './canary_ingress.vue';
export default {
components: {
- instanceComponent,
+ InstanceComponent,
CanaryIngress,
GlIcon,
GlLoadingIcon,
diff --git a/app/assets/javascripts/environments/components/deployment.vue b/app/assets/javascripts/environments/components/deployment.vue
index 19284b26d51..3475b38c8c9 100644
--- a/app/assets/javascripts/environments/components/deployment.vue
+++ b/app/assets/javascripts/environments/components/deployment.vue
@@ -1,17 +1,17 @@
<script>
import {
GlBadge,
- GlButton,
- GlCollapse,
GlIcon,
GlLink,
+ GlLoadingIcon,
GlTooltipDirective as GlTooltip,
GlTruncate,
} from '@gitlab/ui';
-import { GlBreakpointInstance } from '@gitlab/ui/dist/utils';
import { __, s__ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import createFlash from '~/flash';
+import deploymentDetails from '../graphql/queries/deployment_details.query.graphql';
import DeploymentStatusBadge from './deployment_status_badge.vue';
import Commit from './commit.vue';
@@ -21,16 +21,16 @@ export default {
Commit,
DeploymentStatusBadge,
GlBadge,
- GlButton,
- GlCollapse,
GlIcon,
GlLink,
GlTruncate,
+ GlLoadingIcon,
TimeAgoTooltip,
},
directives: {
GlTooltip,
},
+ inject: ['projectPath'],
props: {
deployment: {
type: Object,
@@ -41,9 +41,11 @@ export default {
default: false,
required: false,
},
- },
- data() {
- return { visible: false };
+ visible: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
},
computed: {
status() {
@@ -52,26 +54,21 @@ export default {
iid() {
return this.deployment?.iid;
},
+ isTag() {
+ return this.deployment?.tag;
+ },
shortSha() {
return this.commit?.shortId;
},
createdAt() {
return this.deployment?.createdAt;
},
- isMobile() {
- return !GlBreakpointInstance.isDesktop();
- },
- detailsButton() {
- return this.visible
- ? { text: this.$options.i18n.hideDetails, icon: 'expand-up' }
- : { text: this.$options.i18n.showDetails, icon: 'expand-down' };
- },
- detailsButtonClasses() {
- return this.isMobile ? 'gl-sr-only' : '';
- },
commit() {
return this.deployment?.commit;
},
+ commitPath() {
+ return this.commit?.commitPath;
+ },
user() {
return this.deployment?.user;
},
@@ -90,9 +87,6 @@ export default {
jobPath() {
return this.deployable?.buildPath;
},
- refLabel() {
- return this.deployment?.tag ? this.$options.i18n.tag : this.$options.i18n.branch;
- },
ref() {
return this.deployment?.ref;
},
@@ -105,10 +99,35 @@ export default {
needsApproval() {
return this.deployment.pendingApprovalCount > 0;
},
+ hasTags() {
+ return this.tags?.length > 0;
+ },
+ displayTags() {
+ return this.tags?.slice(0, 5);
+ },
},
- methods: {
- toggleCollapse() {
- this.visible = !this.visible;
+ apollo: {
+ tags: {
+ query: deploymentDetails,
+ variables() {
+ return {
+ projectPath: this.projectPath,
+ iid: this.deployment.iid,
+ };
+ },
+ update(data) {
+ return data?.project?.deployment?.tags;
+ },
+ error(error) {
+ createFlash({
+ message: this.$options.i18n.LOAD_ERROR_MESSAGE,
+ captureError: true,
+ error,
+ });
+ },
+ skip() {
+ return !this.visible;
+ },
},
},
i18n: {
@@ -116,14 +135,12 @@ export default {
deploymentId: s__('Deployment|Deployment ID'),
copyButton: __('Copy commit SHA'),
commitSha: __('Commit SHA'),
- showDetails: __('Show details'),
- hideDetails: __('Hide details'),
triggerer: s__('Deployment|Triggerer'),
needsApproval: s__('Deployment|Needs Approval'),
job: __('Job'),
api: __('API'),
branch: __('Branch'),
- tag: __('Tag'),
+ tags: __('Tags'),
},
headerClasses: [
'gl-display-flex',
@@ -179,7 +196,9 @@ export default {
class="gl-font-monospace gl-display-flex gl-align-items-center"
>
<gl-icon ref="deployment-commit-icon" name="commit" class="gl-mr-2" />
- <span v-gl-tooltip :title="$options.i18n.commitSha">{{ shortSha }}</span>
+ <gl-link v-gl-tooltip :title="$options.i18n.commitSha" :href="commitPath">
+ {{ shortSha }}
+ </gl-link>
<clipboard-button
:text="shortSha"
category="tertiary"
@@ -195,54 +214,66 @@ export default {
</time-ago-tooltip>
</div>
</div>
- <gl-button
- ref="details-toggle"
- category="tertiary"
- :icon="detailsButton.icon"
- :button-text-classes="detailsButtonClasses"
- @click="toggleCollapse"
- >
- {{ detailsButton.text }}
- </gl-button>
</div>
<commit v-if="commit" :commit="commit" class="gl-mt-3" />
<div class="gl-mt-3"><slot name="approval"></slot></div>
- <gl-collapse :visible="visible">
+ <div
+ class="gl-display-flex gl-md-align-items-center gl-mt-5 gl-flex-direction-column gl-md-flex-direction-row gl-pr-4 gl-md-pr-0"
+ >
+ <div v-if="user" class="gl-display-flex gl-flex-direction-column gl-md-max-w-15p">
+ <span class="gl-text-gray-500">{{ $options.i18n.triggerer }}</span>
+ <gl-link :href="userPath" class="gl-font-monospace gl-mt-3">
+ <gl-truncate :text="username" with-tooltip />
+ </gl-link>
+ </div>
<div
- class="gl-display-flex gl-md-align-items-center gl-mt-5 gl-flex-direction-column gl-md-flex-direction-row gl-pr-4 gl-md-pr-0"
+ class="gl-display-flex gl-flex-direction-column gl-md-pl-7 gl-md-max-w-15p gl-mt-4 gl-md-mt-0"
>
- <div v-if="user" class="gl-display-flex gl-flex-direction-column gl-md-max-w-15p">
- <span class="gl-text-gray-500">{{ $options.i18n.triggerer }}</span>
- <gl-link :href="userPath" class="gl-font-monospace gl-mt-3">
- <gl-truncate :text="username" with-tooltip />
- </gl-link>
- </div>
- <div
- class="gl-display-flex gl-flex-direction-column gl-md-pl-7 gl-md-max-w-15p gl-mt-4 gl-md-mt-0"
- >
- <span class="gl-text-gray-500" :class="{ 'gl-ml-3': !deployable }">
- {{ $options.i18n.job }}
- </span>
- <gl-link v-if="jobPath" :href="jobPath" class="gl-font-monospace gl-mt-3">
- <gl-truncate :text="jobName" with-tooltip position="middle" />
- </gl-link>
- <span v-else-if="jobName" class="gl-font-monospace gl-mt-3">
- <gl-truncate :text="jobName" with-tooltip position="middle" />
- </span>
- <gl-badge v-else class="gl-font-monospace gl-mt-3" variant="info">
- {{ $options.i18n.api }}
- </gl-badge>
- </div>
- <div
- v-if="ref"
- class="gl-display-flex gl-flex-direction-column gl-md-pl-7 gl-md-max-w-15p gl-mt-4 gl-md-mt-0"
- >
- <span class="gl-text-gray-500">{{ refLabel }}</span>
- <gl-link :href="refPath" class="gl-font-monospace gl-mt-3">
- <gl-truncate :text="refName" with-tooltip />
+ <span class="gl-text-gray-500" :class="{ 'gl-ml-3': !deployable }">
+ {{ $options.i18n.job }}
+ </span>
+ <gl-link v-if="jobPath" :href="jobPath" class="gl-font-monospace gl-mt-3">
+ <gl-truncate :text="jobName" with-tooltip position="middle" />
+ </gl-link>
+ <span v-else-if="jobName" class="gl-font-monospace gl-mt-3">
+ <gl-truncate :text="jobName" with-tooltip position="middle" />
+ </span>
+ <gl-badge v-else class="gl-font-monospace gl-mt-3" variant="info">
+ {{ $options.i18n.api }}
+ </gl-badge>
+ </div>
+ <div
+ v-if="ref && !isTag"
+ class="gl-display-flex gl-flex-direction-column gl-md-pl-7 gl-md-max-w-15p gl-mt-4 gl-md-mt-0"
+ >
+ <span class="gl-text-gray-500">{{ $options.i18n.branch }}</span>
+ <gl-link :href="refPath" class="gl-font-monospace gl-mt-3">
+ <gl-truncate :text="refName" with-tooltip />
+ </gl-link>
+ </div>
+ <div
+ v-if="hasTags || $apollo.queries.tags.loading"
+ class="gl-display-flex gl-flex-direction-column gl-md-pl-7 gl-md-max-w-15p gl-mt-4 gl-md-mt-0"
+ >
+ <span class="gl-text-gray-500">{{ $options.i18n.tags }}</span>
+ <gl-loading-icon
+ v-if="$apollo.queries.tags.loading"
+ class="gl-font-monospace gl-mt-3"
+ size="sm"
+ inline
+ />
+ <div v-if="hasTags" class="gl-display-flex gl-flex-direction-row">
+ <gl-link
+ v-for="(tag, ndx) in displayTags"
+ :key="tag.name"
+ :href="tag.path"
+ class="gl-font-monospace gl-mt-3 gl-mr-3"
+ >
+ {{ tag.name }}<span v-if="ndx + 1 < tags.length">, </span>
</gl-link>
+ <div v-if="tags.length > 5" class="gl-font-monospace gl-mt-3 gl-mr-3">...</div>
</div>
</div>
- </gl-collapse>
+ </div>
</div>
</template>
diff --git a/app/assets/javascripts/environments/components/new_environment_item.vue b/app/assets/javascripts/environments/components/new_environment_item.vue
index 75bd473497b..9a100e0199e 100644
--- a/app/assets/javascripts/environments/components/new_environment_item.vue
+++ b/app/assets/javascripts/environments/components/new_environment_item.vue
@@ -310,6 +310,7 @@ export default {
<div v-if="lastDeployment" :class="$options.deploymentClasses">
<deployment
:deployment="lastDeployment"
+ :visible="visible"
:class="{ 'gl-ml-7': inFolder }"
latest
class="gl-pl-4"
diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js b/app/assets/javascripts/environments/folder/environments_folder_bundle.js
index 4e5fe511f8a..1a32de30de0 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js
+++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import Translate from '~/vue_shared/translate';
-import environmentsFolderApp from './environments_folder_view.vue';
+import EnvironmentsFolderApp from './environments_folder_view.vue';
Vue.use(Translate);
Vue.use(VueApollo);
@@ -17,7 +17,7 @@ export default () => {
return new Vue({
el,
components: {
- environmentsFolderApp,
+ EnvironmentsFolderApp,
},
apolloProvider,
provide: {
diff --git a/app/assets/javascripts/environments/graphql/queries/deployment_details.query.graphql b/app/assets/javascripts/environments/graphql/queries/deployment_details.query.graphql
new file mode 100644
index 00000000000..baed777bd07
--- /dev/null
+++ b/app/assets/javascripts/environments/graphql/queries/deployment_details.query.graphql
@@ -0,0 +1,13 @@
+query getDeploymentDetails($projectPath: ID!, $iid: ID!) {
+ project(fullPath: $projectPath) {
+ id
+ deployment(iid: $iid) {
+ id
+ iid
+ tags {
+ name
+ path
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/feature_flags/components/feature_flags.vue b/app/assets/javascripts/feature_flags/components/feature_flags.vue
index 645c2456c6e..93510870915 100644
--- a/app/assets/javascripts/feature_flags/components/feature_flags.vue
+++ b/app/assets/javascripts/feature_flags/components/feature_flags.vue
@@ -163,7 +163,6 @@ export default {
v-gl-modal="'configure-feature-flags'"
variant="confirm"
category="secondary"
- data-qa-selector="configure_feature_flags_button"
data-testid="ff-configure-button"
class="gl-mb-3"
>
diff --git a/app/assets/javascripts/filterable_list.js b/app/assets/javascripts/filterable_list.js
index a8670caf5b2..a6781cffaec 100644
--- a/app/assets/javascripts/filterable_list.js
+++ b/app/assets/javascripts/filterable_list.js
@@ -81,6 +81,7 @@ export default class FilterableList {
onFilterSuccess(response, queryData) {
if (response.data.html) {
+ // eslint-disable-next-line no-unsanitized/property
this.listHolderElement.innerHTML = response.data.html;
}
diff --git a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue
index 4c2f55fd174..679c8caffdb 100644
--- a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue
+++ b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue
@@ -69,14 +69,18 @@ export default {
</script>
<template>
<div>
- <div v-if="!isLocalStorageAvailable" ref="localStorageNote" class="dropdown-info-note">
+ <div
+ v-if="!isLocalStorageAvailable"
+ data-testid="local-storage-note"
+ class="dropdown-info-note"
+ >
{{ __('This feature requires local storage to be enabled') }}
</div>
<ul v-else-if="hasItems">
<li
v-for="(item, index) in processedItems"
- ref="dropdownItem"
:key="`processed-items-${index}`"
+ data-testid="dropdown-item"
>
<button
type="button"
@@ -100,7 +104,7 @@ export default {
<li class="divider"></li>
<li>
<button
- ref="clearButton"
+ data-testid="clear-button"
type="button"
class="filtered-search-history-clear-button"
@click="onRequestClearRecentSearches($event)"
@@ -109,7 +113,7 @@ export default {
</button>
</li>
</ul>
- <div v-else ref="dropdownNote" class="dropdown-info-note">
+ <div v-else data-testid="dropdown-note" class="dropdown-info-note">
{{ __("You don't have any recent searches") }}
</div>
</div>
diff --git a/app/assets/javascripts/filtered_search/dropdown_emoji.js b/app/assets/javascripts/filtered_search/dropdown_emoji.js
index 5adc074b3ce..aeea66bf51c 100644
--- a/app/assets/javascripts/filtered_search/dropdown_emoji.js
+++ b/app/assets/javascripts/filtered_search/dropdown_emoji.js
@@ -75,6 +75,7 @@ export default class DropdownEmoji extends FilteredSearchDropdown {
const name = valueElement.innerText;
const emojiTag = this.glEmojiTag(name);
const emojiElement = dropdownItem.querySelector('gl-emoji');
+ // eslint-disable-next-line no-unsanitized/property
emojiElement.outerHTML = emojiTag;
}
});
diff --git a/app/assets/javascripts/filtered_search/droplab/drop_down.js b/app/assets/javascripts/filtered_search/droplab/drop_down.js
index 398a7b26677..e7edc678773 100644
--- a/app/assets/javascripts/filtered_search/droplab/drop_down.js
+++ b/app/assets/javascripts/filtered_search/droplab/drop_down.js
@@ -107,7 +107,7 @@ class DropDown {
}
const renderableList = this.list.querySelector('ul[data-dynamic]') || this.list;
-
+ // eslint-disable-next-line no-unsanitized/property
renderableList.innerHTML = children.join('');
const listEvent = new CustomEvent('render.dl', {
@@ -121,7 +121,7 @@ class DropDown {
renderChildren(data) {
const html = utils.template(this.templateString, data);
const template = document.createElement('div');
-
+ // eslint-disable-next-line no-unsanitized/property
template.innerHTML = html;
DropDown.setImagesSrc(template);
template.firstChild.style.display = data.droplab_hidden ? 'none' : 'block';
diff --git a/app/assets/javascripts/filtered_search/droplab/hook_button.js b/app/assets/javascripts/filtered_search/droplab/hook_button.js
index c51d6167fa3..805905e7750 100644
--- a/app/assets/javascripts/filtered_search/droplab/hook_button.js
+++ b/app/assets/javascripts/filtered_search/droplab/hook_button.js
@@ -42,6 +42,7 @@ class HookButton extends Hook {
}
restoreInitialState() {
+ // eslint-disable-next-line no-unsanitized/property
this.list.list.innerHTML = this.list.initialState;
}
diff --git a/app/assets/javascripts/filtered_search/droplab/hook_input.js b/app/assets/javascripts/filtered_search/droplab/hook_input.js
index c523dae347f..32dfe0372bb 100644
--- a/app/assets/javascripts/filtered_search/droplab/hook_input.js
+++ b/app/assets/javascripts/filtered_search/droplab/hook_input.js
@@ -97,6 +97,7 @@ class HookInput extends Hook {
}
restoreInitialState() {
+ // eslint-disable-next-line no-unsanitized/property
this.list.list.innerHTML = this.list.initialState;
}
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 7143cb50ea6..0c01220a7be 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
@@ -122,6 +122,7 @@ export default class FilteredSearchVisualTokens {
const hasOperator = Boolean(operator);
if (value) {
+ // eslint-disable-next-line no-unsanitized/property
li.innerHTML = FilteredSearchVisualTokens.createVisualTokenElementHTML({
canEdit,
uppercaseTokenName,
@@ -138,6 +139,7 @@ export default class FilteredSearchVisualTokens {
operatorHTML = '<div class="operator"></div>';
}
+ // eslint-disable-next-line no-unsanitized/property
li.innerHTML = nameHTML + operatorHTML;
}
@@ -160,6 +162,8 @@ export default class FilteredSearchVisualTokens {
if (!isLastVisualTokenValid && lastVisualToken.classList.contains('filtered-search-token')) {
const name = FilteredSearchVisualTokens.getLastTokenPartial();
const operator = FilteredSearchVisualTokens.getLastTokenOperator();
+
+ // eslint-disable-next-line no-unsanitized/property
lastVisualToken.innerHTML = FilteredSearchVisualTokens.createVisualTokenElementHTML({
hasOperator: Boolean(operator),
});
@@ -293,6 +297,7 @@ export default class FilteredSearchVisualTokens {
const button = lastVisualToken.querySelector('.selectable');
const valueContainer = lastVisualToken.querySelector('.value-container');
button.removeChild(valueContainer);
+ // eslint-disable-next-line no-unsanitized/property
lastVisualToken.innerHTML = button.innerHTML;
} else if (operator) {
lastVisualToken.removeChild(operator);
diff --git a/app/assets/javascripts/filtered_search/visual_token_value.js b/app/assets/javascripts/filtered_search/visual_token_value.js
index 707add10009..0d144398531 100644
--- a/app/assets/javascripts/filtered_search/visual_token_value.js
+++ b/app/assets/javascripts/filtered_search/visual_token_value.js
@@ -47,6 +47,7 @@ export default class VisualTokenValue {
/* eslint-disable no-param-reassign */
tokenValueContainer.dataset.originalValue = tokenValue;
+ // eslint-disable-next-line no-unsanitized/property
tokenValueElement.innerHTML = `
<img class="avatar s20" src="${user.avatar_url}" alt="">
${escape(user.name)}
@@ -152,6 +153,7 @@ export default class VisualTokenValue {
}
container.dataset.originalValue = value;
+ // eslint-disable-next-line no-unsanitized/property
element.innerHTML = Emoji.glEmojiTag(value);
});
}
diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js
index 5a47e76d597..edf83a33812 100644
--- a/app/assets/javascripts/flash.js
+++ b/app/assets/javascripts/flash.js
@@ -236,11 +236,13 @@ const createFlash = function createFlash({
if (!flashContainer) return null;
+ // eslint-disable-next-line no-unsanitized/property
flashContainer.innerHTML = createFlashEl(message, type);
const flashEl = flashContainer.querySelector(`.flash-${type}`);
if (actionConfig) {
+ // eslint-disable-next-line no-unsanitized/method
flashEl.insertAdjacentHTML('beforeend', createAction(actionConfig));
if (actionConfig.clickHandler) {
diff --git a/app/assets/javascripts/frequent_items/components/frequent_items_list.vue b/app/assets/javascripts/frequent_items/components/frequent_items_list.vue
index 1da0b88c9e9..c0bfcf9c4a9 100644
--- a/app/assets/javascripts/frequent_items/components/frequent_items_list.vue
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_list.vue
@@ -58,7 +58,7 @@ export default {
<template>
<div class="frequent-items-list-container">
- <ul ref="frequentItemsList" class="list-unstyled">
+ <ul data-testid="frequent-items-list" class="list-unstyled">
<li
v-if="isListEmpty"
:class="{ 'section-failure': isFetchFailed }"
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 9fb69a3cae3..33ab1d5cd7f 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
@@ -79,16 +79,19 @@ export default {
:project-name="itemName"
aria-hidden="true"
/>
- <div ref="frequentItemsItemMetadataContainer" class="frequent-items-item-metadata-container">
+ <div
+ data-testid="frequent-items-item-metadata-container"
+ class="frequent-items-item-metadata-container"
+ >
<div
- ref="frequentItemsItemTitle"
v-safe-html="highlightedItemName"
+ data-testid="frequent-items-item-title"
:title="itemName"
class="frequent-items-item-title"
></div>
<div
v-if="namespace"
- ref="frequentItemsItemNamespace"
+ data-testid="frequent-items-item-namespace"
:title="namespace"
class="frequent-items-item-namespace"
>
diff --git a/app/assets/javascripts/google_cloud/databases/index.js b/app/assets/javascripts/google_cloud/databases/index.js
deleted file mode 100644
index e240a1116e8..00000000000
--- a/app/assets/javascripts/google_cloud/databases/index.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import Vue from 'vue';
-import Panel from './panel.vue';
-
-export default (containerId = '#js-google-cloud-databases') => {
- const element = document.querySelector(containerId);
- const { ...attrs } = JSON.parse(element.getAttribute('data'));
- return new Vue({
- el: element,
- render: (createElement) => createElement(Panel, { attrs }),
- });
-};
diff --git a/app/assets/javascripts/google_cloud/databases/init_index.js b/app/assets/javascripts/google_cloud/databases/init_index.js
new file mode 100644
index 00000000000..931143833cb
--- /dev/null
+++ b/app/assets/javascripts/google_cloud/databases/init_index.js
@@ -0,0 +1,11 @@
+import Vue from 'vue';
+import Panel from './panel.vue';
+
+export default () => {
+ const element = document.querySelector('#js-google-cloud-databases');
+ const attrs = JSON.parse(element.getAttribute('data'));
+ return new Vue({
+ el: element,
+ render: (createElement) => createElement(Panel, { attrs }),
+ });
+};
diff --git a/app/assets/javascripts/google_cloud/databases/init_new.js b/app/assets/javascripts/google_cloud/databases/init_new.js
new file mode 100644
index 00000000000..3feb2dc2f98
--- /dev/null
+++ b/app/assets/javascripts/google_cloud/databases/init_new.js
@@ -0,0 +1,11 @@
+import Vue from 'vue';
+import Form from './cloudsql/create_instance_form.vue';
+
+export default () => {
+ const element = document.querySelector('#js-google-cloud-databases-cloudsql-form');
+ const attrs = JSON.parse(element.getAttribute('data'));
+ return new Vue({
+ el: element,
+ render: (createElement) => createElement(Form, { attrs }),
+ });
+};
diff --git a/app/assets/javascripts/google_cloud/databases/panel.vue b/app/assets/javascripts/google_cloud/databases/panel.vue
index e2f18c286a5..8b91c508871 100644
--- a/app/assets/javascripts/google_cloud/databases/panel.vue
+++ b/app/assets/javascripts/google_cloud/databases/panel.vue
@@ -1,11 +1,15 @@
<script>
import GoogleCloudMenu from '../components/google_cloud_menu.vue';
import IncubationBanner from '../components/incubation_banner.vue';
+import InstanceTable from './cloudsql/instance_table.vue';
+import ServiceTable from './service_table.vue';
export default {
components: {
IncubationBanner,
+ InstanceTable,
GoogleCloudMenu,
+ ServiceTable,
},
props: {
configurationUrl: {
@@ -20,6 +24,26 @@ export default {
type: String,
required: true,
},
+ cloudsqlPostgresUrl: {
+ type: String,
+ required: true,
+ },
+ cloudsqlMysqlUrl: {
+ type: String,
+ required: true,
+ },
+ cloudsqlSqlserverUrl: {
+ type: String,
+ required: true,
+ },
+ cloudsqlInstances: {
+ type: Array,
+ required: true,
+ },
+ emptyIllustrationUrl: {
+ type: String,
+ required: true,
+ },
},
};
</script>
@@ -34,5 +58,19 @@ export default {
:deployments-url="deploymentsUrl"
:databases-url="databasesUrl"
/>
+
+ <service-table
+ alloydb-postgres-url="#"
+ :cloudsql-mysql-url="cloudsqlMysqlUrl"
+ :cloudsql-postgres-url="cloudsqlPostgresUrl"
+ :cloudsql-sqlserver-url="cloudsqlSqlserverUrl"
+ firestore-url="#"
+ memorystore-redis-url="#"
+ />
+
+ <instance-table
+ :cloudsql-instances="cloudsqlInstances"
+ :empty-illustration-url="emptyIllustrationUrl"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/google_tag_manager/index.js b/app/assets/javascripts/google_tag_manager/index.js
index c8204f397ff..5b0bcfa963b 100644
--- a/app/assets/javascripts/google_tag_manager/index.js
+++ b/app/assets/javascripts/google_tag_manager/index.js
@@ -140,17 +140,6 @@ export const trackSaasTrialGroup = () => {
});
};
-export const trackSaasTrialProject = () => {
- if (!isSupported()) {
- return;
- }
-
- const form = document.getElementById('new_project');
- form.addEventListener('submit', () => {
- pushEvent('saasTrialProject');
- });
-};
-
export const trackProjectImport = () => {
if (!isSupported()) {
return;
@@ -290,3 +279,11 @@ export const trackCombinedGroupProjectForm = () => {
pushEvent('combinedGroupProjectFormSubmit');
});
};
+
+export const trackCompanyForm = (aboutYourCompanyType) => {
+ if (!isSupported()) {
+ return;
+ }
+
+ pushEvent('aboutYourCompanyFormSubmit', { aboutYourCompanyType });
+};
diff --git a/app/assets/javascripts/graphql_shared/fragments/issuable_timelogs.fragment.graphql b/app/assets/javascripts/graphql_shared/fragments/issuable_timelogs.fragment.graphql
index fb771d7ec8a..45dbfb30704 100644
--- a/app/assets/javascripts/graphql_shared/fragments/issuable_timelogs.fragment.graphql
+++ b/app/assets/javascripts/graphql_shared/fragments/issuable_timelogs.fragment.graphql
@@ -1,4 +1,5 @@
fragment TimelogFragment on Timelog {
+ __typename
id
timeSpent
user {
diff --git a/app/assets/javascripts/graphql_shared/fragments/issue_time_tracking.fragment.graphql b/app/assets/javascripts/graphql_shared/fragments/issue_time_tracking.fragment.graphql
new file mode 100644
index 00000000000..dbe6ad9f059
--- /dev/null
+++ b/app/assets/javascripts/graphql_shared/fragments/issue_time_tracking.fragment.graphql
@@ -0,0 +1,13 @@
+#import "~/graphql_shared/fragments/issuable_timelogs.fragment.graphql"
+
+fragment IssueTimeTrackingFragment on Issue {
+ __typename
+ id
+ humanTotalTimeSpent
+ totalTimeSpent
+ timelogs {
+ nodes {
+ ...TimelogFragment
+ }
+ }
+}
diff --git a/app/assets/javascripts/graphql_shared/fragments/merge_request_time_tracking.fragment.graphql b/app/assets/javascripts/graphql_shared/fragments/merge_request_time_tracking.fragment.graphql
new file mode 100644
index 00000000000..68d3c02cf2e
--- /dev/null
+++ b/app/assets/javascripts/graphql_shared/fragments/merge_request_time_tracking.fragment.graphql
@@ -0,0 +1,13 @@
+#import "~/graphql_shared/fragments/issuable_timelogs.fragment.graphql"
+
+fragment MergeRequestTimeTrackingFragment on MergeRequest {
+ __typename
+ id
+ humanTotalTimeSpent
+ totalTimeSpent
+ timelogs {
+ nodes {
+ ...TimelogFragment
+ }
+ }
+}
diff --git a/app/assets/javascripts/graphql_shared/issuable_client.js b/app/assets/javascripts/graphql_shared/issuable_client.js
new file mode 100644
index 00000000000..e86103c332b
--- /dev/null
+++ b/app/assets/javascripts/graphql_shared/issuable_client.js
@@ -0,0 +1,93 @@
+import produce from 'immer';
+import VueApollo from 'vue-apollo';
+import { concatPagination } from '@apollo/client/utilities';
+import getIssueStateQuery from '~/issues/show/queries/get_issue_state.query.graphql';
+import createDefaultClient from '~/lib/graphql';
+import typeDefs from '~/work_items/graphql/typedefs.graphql';
+import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
+import { WIDGET_TYPE_LABELS } from '~/work_items/constants';
+
+export const temporaryConfig = {
+ typeDefs,
+ cacheConfig: {
+ possibleTypes: {
+ LocalWorkItemWidget: ['LocalWorkItemLabels'],
+ },
+ typePolicies: {
+ Project: {
+ fields: {
+ projectMembers: {
+ keyArgs: ['fullPath', 'search', 'relations', 'first'],
+ },
+ },
+ },
+ WorkItem: {
+ fields: {
+ mockWidgets: {
+ read(widgets) {
+ return (
+ widgets || [
+ {
+ __typename: 'LocalWorkItemLabels',
+ type: WIDGET_TYPE_LABELS,
+ allowScopedLabels: true,
+ nodes: [],
+ },
+ ]
+ );
+ },
+ },
+ widgets: {
+ merge(_, incoming) {
+ return incoming;
+ },
+ },
+ },
+ },
+ MemberInterfaceConnection: {
+ fields: {
+ nodes: concatPagination(),
+ },
+ },
+ },
+ },
+};
+
+export const resolvers = {
+ Mutation: {
+ updateIssueState: (_, { issueType = undefined, isDirty = false }, { cache }) => {
+ const sourceData = cache.readQuery({ query: getIssueStateQuery });
+ const data = produce(sourceData, (draftData) => {
+ draftData.issueState = { issueType, isDirty };
+ });
+ cache.writeQuery({ query: getIssueStateQuery, data });
+ },
+ localUpdateWorkItem(_, { input }, { cache }) {
+ const sourceData = cache.readQuery({
+ query: workItemQuery,
+ variables: { id: input.id },
+ });
+
+ const data = produce(sourceData, (draftData) => {
+ if (input.labels) {
+ const labelsWidget = draftData.workItem.mockWidgets.find(
+ (widget) => widget.type === WIDGET_TYPE_LABELS,
+ );
+ labelsWidget.nodes = [...input.labels];
+ }
+ });
+
+ cache.writeQuery({
+ query: workItemQuery,
+ variables: { id: input.id },
+ data,
+ });
+ },
+ },
+};
+
+export const defaultClient = createDefaultClient(resolvers, temporaryConfig);
+
+export const apolloProvider = new VueApollo({
+ defaultClient,
+});
diff --git a/app/assets/javascripts/graphql_shared/possible_types.json b/app/assets/javascripts/graphql_shared/possible_types.json
index eac325f184f..72dbf9e7b7b 100644
--- a/app/assets/javascripts/graphql_shared/possible_types.json
+++ b/app/assets/javascripts/graphql_shared/possible_types.json
@@ -140,6 +140,7 @@
"WorkItemWidgetAssignees",
"WorkItemWidgetDescription",
"WorkItemWidgetHierarchy",
+ "WorkItemWidgetIteration",
"WorkItemWidgetLabels",
"WorkItemWidgetStartAndDueDate",
"WorkItemWidgetVerificationStatus",
diff --git a/app/assets/javascripts/projects/settings/topics/queries/project_topics_search.query.graphql b/app/assets/javascripts/graphql_shared/queries/project_topics_search.query.graphql
index 0c0a874d950..0c0a874d950 100644
--- a/app/assets/javascripts/projects/settings/topics/queries/project_topics_search.query.graphql
+++ b/app/assets/javascripts/graphql_shared/queries/project_topics_search.query.graphql
diff --git a/app/assets/javascripts/graphql_shared/queries/users_search.query.graphql b/app/assets/javascripts/graphql_shared/queries/users_search.query.graphql
index bb34e4032f4..f64c4276deb 100644
--- a/app/assets/javascripts/graphql_shared/queries/users_search.query.graphql
+++ b/app/assets/javascripts/graphql_shared/queries/users_search.query.graphql
@@ -1,10 +1,20 @@
#import "../fragments/user.fragment.graphql"
#import "~/graphql_shared/fragments/user_availability.fragment.graphql"
-query projectUsersSearch($search: String!, $fullPath: ID!) {
+query projectUsersSearch($search: String!, $fullPath: ID!, $after: String, $first: Int) {
workspace: project(fullPath: $fullPath) {
id
- users: projectMembers(search: $search, relations: [DIRECT, INHERITED, INVITED_GROUPS]) {
+ users: projectMembers(
+ search: $search
+ relations: [DIRECT, INHERITED, INVITED_GROUPS]
+ first: $first
+ after: $after
+ ) {
+ pageInfo {
+ hasNextPage
+ endCursor
+ startCursor
+ }
nodes {
id
user {
diff --git a/app/assets/javascripts/groups/components/app.vue b/app/assets/javascripts/groups/components/app.vue
index cd5521c599e..0bd7371d39b 100644
--- a/app/assets/javascripts/groups/components/app.vue
+++ b/app/assets/javascripts/groups/components/app.vue
@@ -17,11 +17,6 @@ export default {
GlLoadingIcon,
EmptyState,
},
- inject: {
- renderEmptyState: {
- default: false,
- },
- },
props: {
action: {
type: String,
@@ -45,6 +40,11 @@ export default {
type: Boolean,
required: true,
},
+ renderEmptyState: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -224,6 +224,9 @@ export default {
},
showLegacyEmptyState() {
const { containerEl } = this;
+
+ if (!containerEl) return;
+
const contentListEl = containerEl.querySelector(CONTENT_LIST_CLASS);
const emptyStateEl = containerEl.querySelector('.empty-state');
diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue
index 2f182b86d2c..961af800971 100644
--- a/app/assets/javascripts/groups/components/group_item.vue
+++ b/app/assets/javascripts/groups/components/group_item.vue
@@ -16,15 +16,15 @@ import UserAccessRoleBadge from '~/vue_shared/components/user_access_role_badge.
import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
import { helpPagePath } from '~/helpers/help_page_helper';
import { __ } from '~/locale';
-import { VISIBILITY_LEVELS_ENUM } from '~/visibility_level/constants';
+import { VISIBILITY_LEVELS_STRING_TO_INTEGER } from '~/visibility_level/constants';
import { VISIBILITY_TYPE_ICON, GROUP_VISIBILITY_TYPE, ITEM_TYPE } from '../constants';
import eventHub from '../event_hub';
-import itemActions from './item_actions.vue';
-import itemCaret from './item_caret.vue';
-import itemStats from './item_stats.vue';
-import itemTypeIcon from './item_type_icon.vue';
+import ItemActions from './item_actions.vue';
+import ItemCaret from './item_caret.vue';
+import ItemStats from './item_stats.vue';
+import ItemTypeIcon from './item_type_icon.vue';
export default {
directives: {
@@ -41,10 +41,10 @@ export default {
GlPopover,
GlLink,
UserAccessRoleBadge,
- itemCaret,
- itemTypeIcon,
- itemActions,
- itemStats,
+ ItemCaret,
+ ItemTypeIcon,
+ ItemActions,
+ ItemStats,
},
inject: ['currentGroupVisibility'],
props: {
@@ -111,8 +111,8 @@ export default {
shouldShowVisibilityWarning() {
return (
this.action === 'shared' &&
- VISIBILITY_LEVELS_ENUM[this.group.visibility] >
- VISIBILITY_LEVELS_ENUM[this.currentGroupVisibility]
+ VISIBILITY_LEVELS_STRING_TO_INTEGER[this.group.visibility] >
+ VISIBILITY_LEVELS_STRING_TO_INTEGER[this.currentGroupVisibility]
);
},
},
diff --git a/app/assets/javascripts/groups/components/item_stats.vue b/app/assets/javascripts/groups/components/item_stats.vue
index 2aa812250a0..a4c163b0a81 100644
--- a/app/assets/javascripts/groups/components/item_stats.vue
+++ b/app/assets/javascripts/groups/components/item_stats.vue
@@ -1,19 +1,19 @@
<script>
import { GlBadge } from '@gitlab/ui';
import isProjectPendingRemoval from 'ee_else_ce/groups/mixins/is_project_pending_removal';
-import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import {
ITEM_TYPE,
VISIBILITY_TYPE_ICON,
GROUP_VISIBILITY_TYPE,
PROJECT_VISIBILITY_TYPE,
} from '../constants';
-import itemStatsValue from './item_stats_value.vue';
+import ItemStatsValue from './item_stats_value.vue';
export default {
components: {
- timeAgoTooltip,
- itemStatsValue,
+ TimeAgoTooltip,
+ ItemStatsValue,
GlBadge,
},
mixins: [isProjectPendingRemoval],
diff --git a/app/assets/javascripts/groups/components/overview_tabs.vue b/app/assets/javascripts/groups/components/overview_tabs.vue
new file mode 100644
index 00000000000..325e42af0f8
--- /dev/null
+++ b/app/assets/javascripts/groups/components/overview_tabs.vue
@@ -0,0 +1,103 @@
+<script>
+import { GlTabs, GlTab } from '@gitlab/ui';
+import { isString } from 'lodash';
+import { __ } from '~/locale';
+import GroupsStore from '../store/groups_store';
+import GroupsService from '../service/groups_service';
+import {
+ ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
+ ACTIVE_TAB_SHARED,
+ ACTIVE_TAB_ARCHIVED,
+} from '../constants';
+import GroupsApp from './app.vue';
+
+export default {
+ components: { GlTabs, GlTab, GroupsApp },
+ inject: ['endpoints'],
+ data() {
+ return {
+ tabs: [
+ {
+ title: this.$options.i18n[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS],
+ key: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
+ renderEmptyState: true,
+ lazy: false,
+ service: new GroupsService(this.endpoints[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS]),
+ store: new GroupsStore({ showSchemaMarkup: true }),
+ },
+ {
+ title: this.$options.i18n[ACTIVE_TAB_SHARED],
+ key: ACTIVE_TAB_SHARED,
+ renderEmptyState: false,
+ lazy: true,
+ service: new GroupsService(this.endpoints[ACTIVE_TAB_SHARED]),
+ store: new GroupsStore(),
+ },
+ {
+ title: this.$options.i18n[ACTIVE_TAB_ARCHIVED],
+ key: ACTIVE_TAB_ARCHIVED,
+ renderEmptyState: false,
+ lazy: true,
+ service: new GroupsService(this.endpoints[ACTIVE_TAB_ARCHIVED]),
+ store: new GroupsStore(),
+ },
+ ],
+ activeTabIndex: 0,
+ };
+ },
+ mounted() {
+ const activeTabIndex = this.tabs.findIndex((tab) => tab.key === this.$route.name);
+
+ if (activeTabIndex === -1) {
+ return;
+ }
+
+ this.activeTabIndex = activeTabIndex;
+ },
+ methods: {
+ handleTabInput(tabIndex) {
+ if (tabIndex === this.activeTabIndex) {
+ return;
+ }
+
+ this.activeTabIndex = tabIndex;
+
+ const tab = this.tabs[tabIndex];
+ tab.lazy = false;
+
+ // Vue router will convert `/` to `%2F` if you pass a string as a param
+ // If you pass an array as a param it will concatenate them with a `/`
+ // This makes sure we are always passing an array for the group param
+ const groupParam = isString(this.$route.params.group)
+ ? this.$route.params.group.split('/')
+ : this.$route.params.group;
+
+ this.$router.push({ name: tab.key, params: { group: groupParam } });
+ },
+ },
+ i18n: {
+ [ACTIVE_TAB_SUBGROUPS_AND_PROJECTS]: __('Subgroups and projects'),
+ [ACTIVE_TAB_SHARED]: __('Shared projects'),
+ [ACTIVE_TAB_ARCHIVED]: __('Archived projects'),
+ },
+};
+</script>
+
+<template>
+ <gl-tabs content-class="gl-pt-0" :value="activeTabIndex" @input="handleTabInput">
+ <gl-tab
+ v-for="{ key, title, renderEmptyState, 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"
+ />
+ </gl-tab>
+ </gl-tabs>
+</template>
diff --git a/app/assets/javascripts/groups/components/visibility_level_dropdown.vue b/app/assets/javascripts/groups/components/visibility_level_dropdown.vue
deleted file mode 100644
index 0933045fc38..00000000000
--- a/app/assets/javascripts/groups/components/visibility_level_dropdown.vue
+++ /dev/null
@@ -1,48 +0,0 @@
-<script>
-import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
-
-export default {
- components: {
- GlDropdown,
- GlDropdownItem,
- },
- props: {
- visibilityLevelOptions: {
- type: Array,
- required: true,
- },
- defaultLevel: {
- type: Number,
- required: true,
- },
- },
- data() {
- return {
- selectedOption: this.getDefaultOption(),
- };
- },
- methods: {
- getDefaultOption() {
- return this.visibilityLevelOptions.find((option) => option.level === this.defaultLevel);
- },
- onClick(option) {
- this.selectedOption = option;
- },
- },
-};
-</script>
-<template>
- <div>
- <input type="hidden" name="group[visibility_level]" :value="selectedOption.level" />
- <gl-dropdown :text="selectedOption.label" class="gl-w-full" menu-class="gl-w-full! gl-mb-0">
- <gl-dropdown-item
- v-for="option in visibilityLevelOptions"
- :key="option.level"
- :secondary-text="option.description"
- @click="onClick(option)"
- >
- <div class="gl-font-weight-bold gl-mb-1">{{ option.label }}</div>
- </gl-dropdown-item>
- </gl-dropdown>
- </div>
-</template>
diff --git a/app/assets/javascripts/groups/constants.js b/app/assets/javascripts/groups/constants.js
index 0d09ad9442b..223c2975c11 100644
--- a/app/assets/javascripts/groups/constants.js
+++ b/app/assets/javascripts/groups/constants.js
@@ -1,8 +1,8 @@
import { __, s__ } from '~/locale';
import {
- VISIBILITY_LEVEL_PRIVATE,
- VISIBILITY_LEVEL_INTERNAL,
- VISIBILITY_LEVEL_PUBLIC,
+ VISIBILITY_LEVEL_PRIVATE_STRING,
+ VISIBILITY_LEVEL_INTERNAL_STRING,
+ VISIBILITY_LEVEL_PUBLIC_STRING,
} from '~/visibility_level/constants';
export const MAX_CHILDREN_COUNT = 20;
@@ -34,29 +34,31 @@ export const ITEM_TYPE = {
};
export const GROUP_VISIBILITY_TYPE = {
- [VISIBILITY_LEVEL_PUBLIC]: __(
+ [VISIBILITY_LEVEL_PUBLIC_STRING]: __(
'Public - The group and any public projects can be viewed without any authentication.',
),
- [VISIBILITY_LEVEL_INTERNAL]: __(
+ [VISIBILITY_LEVEL_INTERNAL_STRING]: __(
'Internal - The group and any internal projects can be viewed by any logged in user except external users.',
),
- [VISIBILITY_LEVEL_PRIVATE]: __(
+ [VISIBILITY_LEVEL_PRIVATE_STRING]: __(
'Private - The group and its projects can only be viewed by members.',
),
};
export const PROJECT_VISIBILITY_TYPE = {
- [VISIBILITY_LEVEL_PUBLIC]: __('Public - The project can be accessed without any authentication.'),
- [VISIBILITY_LEVEL_INTERNAL]: __(
+ [VISIBILITY_LEVEL_PUBLIC_STRING]: __(
+ 'Public - The project can be accessed without any authentication.',
+ ),
+ [VISIBILITY_LEVEL_INTERNAL_STRING]: __(
'Internal - The project can be accessed by any logged in user except external users.',
),
- [VISIBILITY_LEVEL_PRIVATE]: __(
+ [VISIBILITY_LEVEL_PRIVATE_STRING]: __(
'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.',
),
};
export const VISIBILITY_TYPE_ICON = {
- [VISIBILITY_LEVEL_PUBLIC]: 'earth',
- [VISIBILITY_LEVEL_INTERNAL]: 'shield',
- [VISIBILITY_LEVEL_PRIVATE]: 'lock',
+ [VISIBILITY_LEVEL_PUBLIC_STRING]: 'earth',
+ [VISIBILITY_LEVEL_INTERNAL_STRING]: 'shield',
+ [VISIBILITY_LEVEL_PRIVATE_STRING]: 'lock',
};
diff --git a/app/assets/javascripts/groups/index.js b/app/assets/javascripts/groups/index.js
index a502fcd31ad..c3bf3f28509 100644
--- a/app/assets/javascripts/groups/index.js
+++ b/app/assets/javascripts/groups/index.js
@@ -4,9 +4,9 @@ import { parseBoolean } from '~/lib/utils/common_utils';
import UserCallout from '~/user_callout';
import Translate from '../vue_shared/translate';
-import groupsApp from './components/app.vue';
-import groupFolderComponent from './components/group_folder.vue';
-import groupItemComponent from './components/group_item.vue';
+import GroupsApp from './components/app.vue';
+import GroupFolderComponent from './components/group_folder.vue';
+import GroupItemComponent from './components/group_item.vue';
import { GROUPS_LIST_HOLDER_CLASS, CONTENT_LIST_CLASS } from './constants';
import GroupFilterableList from './groups_filterable_list';
import GroupsService from './service/groups_service';
@@ -33,8 +33,8 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => {
dataEl = containerEl.querySelector(CONTENT_LIST_CLASS);
}
- Vue.component('GroupFolder', groupFolderComponent);
- Vue.component('GroupItem', groupItemComponent);
+ Vue.component('GroupFolder', GroupFolderComponent);
+ Vue.component('GroupItem', GroupItemComponent);
Vue.use(GlToast);
@@ -42,7 +42,7 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => {
new Vue({
el,
components: {
- groupsApp,
+ GroupsApp,
},
provide() {
const {
@@ -52,7 +52,6 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => {
newSubgroupIllustration,
newProjectIllustration,
emptySubgroupIllustration,
- renderEmptyState,
canCreateSubgroups,
canCreateProjects,
currentGroupVisibility,
@@ -65,7 +64,6 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => {
newSubgroupIllustration,
newProjectIllustration,
emptySubgroupIllustration,
- renderEmptyState: parseBoolean(renderEmptyState),
canCreateSubgroups: parseBoolean(canCreateSubgroups),
canCreateProjects: parseBoolean(canCreateProjects),
currentGroupVisibility,
@@ -75,6 +73,7 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => {
const { dataset } = dataEl || this.$options.el;
const hideProjects = parseBoolean(dataset.hideProjects);
const showSchemaMarkup = parseBoolean(dataset.showSchemaMarkup);
+ const renderEmptyState = parseBoolean(dataset.renderEmptyState);
const service = new GroupsService(endpoint || dataset.endpoint);
const store = new GroupsStore({ hideProjects, showSchemaMarkup });
@@ -83,6 +82,7 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => {
store,
service,
hideProjects,
+ renderEmptyState,
loading: true,
containerId,
};
@@ -119,6 +119,7 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => {
store: this.store,
service: this.service,
hideProjects: this.hideProjects,
+ renderEmptyState: this.renderEmptyState,
containerId: this.containerId,
},
});
diff --git a/app/assets/javascripts/groups/init_overview_tabs.js b/app/assets/javascripts/groups/init_overview_tabs.js
new file mode 100644
index 00000000000..4fa3682c729
--- /dev/null
+++ b/app/assets/javascripts/groups/init_overview_tabs.js
@@ -0,0 +1,78 @@
+import Vue from 'vue';
+import VueRouter from 'vue-router';
+import { GlToast } from '@gitlab/ui';
+import { parseBoolean } from '~/lib/utils/common_utils';
+import GroupFolder from './components/group_folder.vue';
+import GroupItem from './components/group_item.vue';
+import {
+ ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
+ ACTIVE_TAB_SHARED,
+ ACTIVE_TAB_ARCHIVED,
+} from './constants';
+import OverviewTabs from './components/overview_tabs.vue';
+
+export const createRouter = () => {
+ const routes = [
+ { name: ACTIVE_TAB_SHARED, path: '/groups/:group*/-/shared' },
+ { name: ACTIVE_TAB_ARCHIVED, path: '/groups/:group*/-/archived' },
+ { name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, path: '/:group*' },
+ ];
+
+ const router = new VueRouter({
+ routes,
+ mode: 'history',
+ base: '/',
+ });
+
+ return router;
+};
+
+export const initGroupOverviewTabs = () => {
+ const el = document.getElementById('js-group-overview-tabs');
+
+ if (!el) return false;
+
+ Vue.component('GroupFolder', GroupFolder);
+ Vue.component('GroupItem', GroupItem);
+ Vue.use(GlToast);
+ Vue.use(VueRouter);
+
+ const router = createRouter();
+
+ const {
+ newSubgroupPath,
+ newProjectPath,
+ newSubgroupIllustration,
+ newProjectIllustration,
+ emptySubgroupIllustration,
+ canCreateSubgroups,
+ canCreateProjects,
+ currentGroupVisibility,
+ subgroupsAndProjectsEndpoint,
+ sharedProjectsEndpoint,
+ archivedProjectsEndpoint,
+ } = el.dataset;
+
+ return new Vue({
+ el,
+ router,
+ provide: {
+ newSubgroupPath,
+ newProjectPath,
+ newSubgroupIllustration,
+ newProjectIllustration,
+ emptySubgroupIllustration,
+ canCreateSubgroups: parseBoolean(canCreateSubgroups),
+ canCreateProjects: parseBoolean(canCreateProjects),
+ currentGroupVisibility,
+ endpoints: {
+ [ACTIVE_TAB_SUBGROUPS_AND_PROJECTS]: subgroupsAndProjectsEndpoint,
+ [ACTIVE_TAB_SHARED]: sharedProjectsEndpoint,
+ [ACTIVE_TAB_ARCHIVED]: archivedProjectsEndpoint,
+ },
+ },
+ render(createElement) {
+ return createElement(OverviewTabs);
+ },
+ });
+};
diff --git a/app/assets/javascripts/groups/visibility_level.js b/app/assets/javascripts/groups/visibility_level.js
deleted file mode 100644
index d570b5e65ac..00000000000
--- a/app/assets/javascripts/groups/visibility_level.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import Vue from 'vue';
-import VisibilityLevelDropdown from './components/visibility_level_dropdown.vue';
-
-export default () => {
- const el = document.querySelector('.js-visibility-level-dropdown');
-
- if (!el) {
- return null;
- }
-
- const { visibilityLevelOptions, defaultLevel } = el.dataset;
-
- return new Vue({
- el,
- render(createElement) {
- return createElement(VisibilityLevelDropdown, {
- props: {
- visibilityLevelOptions: JSON.parse(visibilityLevelOptions),
- defaultLevel: Number(defaultLevel),
- },
- });
- },
- });
-};
diff --git a/app/assets/javascripts/header_search/constants.js b/app/assets/javascripts/header_search/constants.js
index 3a20fb0216d..332ccee510f 100644
--- a/app/assets/javascripts/header_search/constants.js
+++ b/app/assets/javascripts/header_search/constants.js
@@ -26,11 +26,17 @@ export const GROUPS_CATEGORY = s__('GlobalSearch|Groups');
export const PROJECTS_CATEGORY = s__('GlobalSearch|Projects');
-export const ISSUES_CATEGORY = 'Recent issues';
+export const ISSUES_CATEGORY = s__('GlobalSearch|Recent issues');
-export const MERGE_REQUEST_CATEGORY = 'Recent merge requests';
+export const MERGE_REQUEST_CATEGORY = s__('GlobalSearch|Recent merge requests');
-export const RECENT_EPICS_CATEGORY = 'Recent epics';
+export const RECENT_EPICS_CATEGORY = s__('GlobalSearch|Recent epics');
+
+export const IN_THIS_PROJECT_CATEGORY = s__('GlobalSearch|In this project');
+
+export const SETTINGS_CATEGORY = s__('GlobalSearch|Settings');
+
+export const HELP_CATEGORY = s__('GlobalSearch|Help');
export const LARGE_AVATAR_PX = 32;
@@ -55,3 +61,16 @@ export const HEADER_INIT_EVENTS = ['input', 'focus'];
export const IS_SEARCHING = 'is-searching';
export const IS_FOCUSED = 'is-focused';
export const IS_NOT_FOCUSED = 'is-not-focused';
+
+export const DROPDOWN_ORDER = [
+ MERGE_REQUEST_CATEGORY,
+ ISSUES_CATEGORY,
+ RECENT_EPICS_CATEGORY,
+ GROUPS_CATEGORY,
+ PROJECTS_CATEGORY,
+ IN_THIS_PROJECT_CATEGORY,
+ SETTINGS_CATEGORY,
+ HELP_CATEGORY,
+];
+
+export const FETCH_TYPES = ['generic', 'search'];
diff --git a/app/assets/javascripts/header_search/store/actions.js b/app/assets/javascripts/header_search/store/actions.js
index 3a86dcca409..a0f9e594506 100644
--- a/app/assets/javascripts/header_search/store/actions.js
+++ b/app/assets/javascripts/header_search/store/actions.js
@@ -1,10 +1,26 @@
+import { omitBy, isNil } from 'lodash';
+import { objectToQuery } from '~/lib/utils/url_utility';
import axios from '~/lib/utils/axios_utils';
+import { FETCH_TYPES } from '../constants';
import * as types from './mutation_types';
-export const fetchAutocompleteOptions = ({ commit, getters }) => {
- commit(types.REQUEST_AUTOCOMPLETE);
+export const autocompleteQuery = ({ state, fetchType }) => {
+ const query = omitBy(
+ {
+ term: state.search,
+ project_id: state.searchContext?.project?.id,
+ project_ref: state.searchContext?.ref,
+ filter: fetchType,
+ },
+ isNil,
+ );
+
+ return `${state.autocompletePath}?${objectToQuery(query)}`;
+};
+
+const doFetch = ({ commit, state, fetchType }) => {
return axios
- .get(getters.autocompleteQuery)
+ .get(autocompleteQuery({ state, fetchType }))
.then(({ data }) => {
commit(types.RECEIVE_AUTOCOMPLETE_SUCCESS, data);
})
@@ -13,6 +29,13 @@ export const fetchAutocompleteOptions = ({ commit, getters }) => {
});
};
+export const fetchAutocompleteOptions = ({ commit, state }) => {
+ commit(types.REQUEST_AUTOCOMPLETE);
+ const promises = FETCH_TYPES.map((fetchType) => doFetch({ commit, state, fetchType }));
+
+ return Promise.all(promises);
+};
+
export const clearAutocomplete = ({ commit }) => {
commit(types.CLEAR_AUTOCOMPLETE);
};
diff --git a/app/assets/javascripts/header_search/store/getters.js b/app/assets/javascripts/header_search/store/getters.js
index da7bccd35c0..3da9d2cd961 100644
--- a/app/assets/javascripts/header_search/store/getters.js
+++ b/app/assets/javascripts/header_search/store/getters.js
@@ -14,6 +14,7 @@ import {
PROJECTS_CATEGORY,
GROUPS_CATEGORY,
SEARCH_SHORTCUTS_MIN_CHARACTERS,
+ DROPDOWN_ORDER,
} from '../constants';
export const searchQuery = (state) => {
@@ -34,19 +35,6 @@ export const searchQuery = (state) => {
return `${state.searchPath}?${objectToQuery(query)}`;
};
-export const autocompleteQuery = (state) => {
- const query = omitBy(
- {
- term: state.search,
- project_id: state.searchContext?.project?.id,
- project_ref: state.searchContext?.ref,
- },
- isNil,
- );
-
- return `${state.autocompletePath}?${objectToQuery(query)}`;
-};
-
export const scopedIssuesPath = (state) => {
return (
state.searchContext?.project_metadata?.issues_path ||
@@ -197,7 +185,9 @@ export const autocompleteGroupedSearchOptions = (state) => {
}
});
- return results;
+ return results.sort(
+ (a, b) => DROPDOWN_ORDER.indexOf(a.category) - DROPDOWN_ORDER.indexOf(b.category),
+ );
};
export const searchOptions = (state, getters) => {
diff --git a/app/assets/javascripts/header_search/store/mutations.js b/app/assets/javascripts/header_search/store/mutations.js
index 92948bec515..19b4d4ec330 100644
--- a/app/assets/javascripts/header_search/store/mutations.js
+++ b/app/assets/javascripts/header_search/store/mutations.js
@@ -8,9 +8,11 @@ export default {
},
[types.RECEIVE_AUTOCOMPLETE_SUCCESS](state, data) {
state.loading = false;
- state.autocompleteOptions = data.map((d, i) => {
- return { html_id: `autocomplete-${d.category}-${i}`, ...d };
- });
+ state.autocompleteOptions = [...state.autocompleteOptions].concat(
+ data.map((d, i) => {
+ return { html_id: `autocomplete-${d.category}-${i}`, ...d };
+ }),
+ );
state.autocompleteError = false;
},
[types.RECEIVE_AUTOCOMPLETE_ERROR](state) {
diff --git a/app/assets/javascripts/ide/components/editor_mode_dropdown.vue b/app/assets/javascripts/ide/components/editor_mode_dropdown.vue
index 52593aabfea..d40aab8ee4f 100644
--- a/app/assets/javascripts/ide/components/editor_mode_dropdown.vue
+++ b/app/assets/javascripts/ide/components/editor_mode_dropdown.vue
@@ -50,7 +50,7 @@ export default {
<gl-dropdown-item
v-for="mode in modeDropdownItems"
:key="mode.viewerType"
- :is-check-item="true"
+ is-check-item
:is-checked="viewer === mode.viewerType"
@click="changeMode(mode.viewerType)"
>
diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue
index e0b7ac9b1e1..8962bb76926 100644
--- a/app/assets/javascripts/ide/components/ide_status_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_status_bar.vue
@@ -4,7 +4,7 @@ import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { mapActions, mapState, mapGetters } from 'vuex';
import timeAgoMixin from '~/vue_shared/mixins/timeago';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
-import userAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
+import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
import { rightSidebarViews } from '../constants';
import IdeStatusList from './ide_status_list.vue';
import IdeStatusMr from './ide_status_mr.vue';
@@ -12,7 +12,7 @@ import IdeStatusMr from './ide_status_mr.vue';
export default {
components: {
GlIcon,
- userAvatarImage,
+ UserAvatarImage,
CiIcon,
IdeStatusList,
IdeStatusMr,
diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue
index 87b60eca73c..9a529bdcee1 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/index.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue
@@ -4,12 +4,12 @@ import { mapActions } from 'vuex';
import { modalTypes } from '../../constants';
import ItemButton from './button.vue';
import NewModal from './modal.vue';
-import upload from './upload.vue';
+import Upload from './upload.vue';
export default {
components: {
GlIcon,
- upload,
+ Upload,
ItemButton,
NewModal,
},
diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
index d6207d4a557..9684bf8f18c 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
@@ -176,7 +176,11 @@ export default {
:placeholder="placeholder"
/>
</form>
- <ul v-if="isCreatingNewFile" class="file-templates gl-mt-3 list-inline qa-template-list">
+ <ul
+ v-if="isCreatingNewFile"
+ class="file-templates gl-mt-3 list-inline"
+ data-qa-selector="template_list_content"
+ >
<li v-for="(template, index) in templateTypes" :key="index" class="list-inline-item">
<gl-button
variant="dashed"
diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js
index df643675357..10e9f6a9488 100644
--- a/app/assets/javascripts/ide/index.js
+++ b/app/assets/javascripts/ide/index.js
@@ -8,6 +8,7 @@ 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';
@@ -34,7 +35,7 @@ Vue.use(PerformancePlugin, {
* @param {extendStoreCallback} options.extendStore -
* Function that receives the default store and returns an extended one.
*/
-export const initIde = (el, options = {}) => {
+export const initLegacyWebIDE = (el, options = {}) => {
if (!el) return null;
const { rootComponent = ide, extendStore = identity } = options;
@@ -93,8 +94,15 @@ export const initIde = (el, options = {}) => {
*/
export function startIde(options) {
const ideElement = document.getElementById('ide');
- if (ideElement) {
+
+ if (!ideElement) {
+ return;
+ }
+
+ if (gon.features?.vscodeWebIde) {
+ initGitlabWebIDE(ideElement);
+ } else {
resetServiceWorkersPublicPath();
- initIde(ideElement, options);
+ initLegacyWebIDE(ideElement, options);
}
}
diff --git a/app/assets/javascripts/ide/init_gitlab_web_ide.js b/app/assets/javascripts/ide/init_gitlab_web_ide.js
new file mode 100644
index 00000000000..a061da38d4f
--- /dev/null
+++ b/app/assets/javascripts/ide/init_gitlab_web_ide.js
@@ -0,0 +1,30 @@
+import { cleanTrailingSlash } from './stores/utils';
+
+export const initGitlabWebIDE = async (el) => {
+ const { start } = await import('@gitlab/web-ide');
+
+ const { gitlab_url: gitlabUrl } = window.gon;
+ const baseUrl = new URL(process.env.GITLAB_WEB_IDE_PUBLIC_PATH, window.location.origin);
+
+ // what: Pull what we need from the element. We will replace it soon.
+ const { path_with_namespace: projectPath } = JSON.parse(el.dataset.project);
+ const { cspNonce: nonce, branchName: ref } = el.dataset;
+
+ // 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');
+
+ el.replaceWith(newEl);
+
+ // what: Trigger start on our new mounting element
+ await start(newEl, {
+ baseUrl: cleanTrailingSlash(baseUrl.href),
+ projectPath,
+ gitlabUrl,
+ ref,
+ nonce,
+ });
+};
diff --git a/app/assets/javascripts/image_diff/helpers/badge_helper.js b/app/assets/javascripts/image_diff/helpers/badge_helper.js
index 5ff00394e3b..35d8ec32bdf 100644
--- a/app/assets/javascripts/image_diff/helpers/badge_helper.js
+++ b/app/assets/javascripts/image_diff/helpers/badge_helper.js
@@ -30,6 +30,7 @@ export function addImageBadge(containerEl, { coordinate, badgeText, noteId }) {
export function addImageCommentBadge(containerEl, { coordinate, noteId }) {
const buttonEl = createImageBadge(noteId, coordinate, ['image-comment-badge']);
+ // eslint-disable-next-line no-unsanitized/property
buttonEl.innerHTML = spriteIcon('image-comment-dark');
containerEl.appendChild(buttonEl);
diff --git a/app/assets/javascripts/image_diff/helpers/comment_indicator_helper.js b/app/assets/javascripts/image_diff/helpers/comment_indicator_helper.js
index deaef686f59..2b5cb70737f 100644
--- a/app/assets/javascripts/image_diff/helpers/comment_indicator_helper.js
+++ b/app/assets/javascripts/image_diff/helpers/comment_indicator_helper.js
@@ -8,6 +8,7 @@ export function addCommentIndicator(containerEl, { x, y }) {
buttonEl.style.left = `${x}px`;
buttonEl.style.top = `${y}px`;
+ // eslint-disable-next-line no-unsanitized/property
buttonEl.innerHTML = spriteIcon('image-comment-dark');
containerEl.appendChild(buttonEl);
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 87f1ed31a7f..a334f5e4bf7 100644
--- a/app/assets/javascripts/invite_members/components/invite_members_modal.vue
+++ b/app/assets/javascripts/invite_members/components/invite_members_modal.vue
@@ -118,6 +118,7 @@ export default {
selectedAccessLevel: undefined,
errorsLimit: 2,
isErrorsSectionExpanded: false,
+ emptyInvitesError: false,
};
},
computed: {
@@ -133,8 +134,8 @@ export default {
labelIntroText() {
return this.$options.labels[this.inviteTo][this.mode].introText;
},
- inviteDisabled() {
- return this.newUsersToInvite.length === 0;
+ isEmptyInvites() {
+ return Boolean(this.newUsersToInvite.length);
},
hasInvalidMembers() {
return !isEmpty(this.invalidMembers);
@@ -219,6 +220,18 @@ export default {
});
},
},
+ 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) {
+ return;
+ }
+
+ this.clearEmptyInviteError();
+ },
+ },
+ },
mounted() {
eventHub.$on('openModal', (options) => {
this.openModal(options);
@@ -260,10 +273,19 @@ export default {
const tracking = new ExperimentTracking(experimentName);
tracking.event(eventName);
},
+ showEmptyInvitesError() {
+ this.invalidFeedbackMessage = this.$options.labels.emptyInvitesErrorText;
+ this.emptyInvitesError = true;
+ },
sendInvite({ accessLevel, expiresAt }) {
this.isLoading = true;
this.clearValidation();
+ if (!this.isEmptyInvites) {
+ this.showEmptyInvitesError();
+ return;
+ }
+
const [usersToInviteByEmail, usersToAddById] = this.partitionNewUsersToInvite();
const apiAddByInvite = this.isProject
@@ -338,6 +360,10 @@ export default {
this.invalidFeedbackMessage = '';
this.invalidMembers = {};
},
+ clearEmptyInviteError() {
+ this.invalidFeedbackMessage = '';
+ this.emptyInvitesError = false;
+ },
removeToken(token) {
delete this.invalidMembers[memberName(token)];
this.invalidMembers = { ...this.invalidMembers };
@@ -360,7 +386,6 @@ export default {
:label-intro-text="labelIntroText"
:label-search-field="$options.labels.searchField"
:form-group-description="formGroupDescription"
- :submit-disabled="inviteDisabled"
:invalid-feedback-message="invalidFeedbackMessage"
:is-loading="isLoading"
:new-users-to-invite="newUsersToInvite"
diff --git a/app/assets/javascripts/invite_members/components/user_limit_notification.vue b/app/assets/javascripts/invite_members/components/user_limit_notification.vue
index 6c9b1f8e6d0..c3d9d959ef6 100644
--- a/app/assets/javascripts/invite_members/components/user_limit_notification.vue
+++ b/app/assets/javascripts/invite_members/components/user_limit_notification.vue
@@ -8,8 +8,6 @@ import {
REACHED_LIMIT_MESSAGE,
REACHED_LIMIT_UPGRADE_SUGGESTION_MESSAGE,
CLOSE_TO_LIMIT_MESSAGE,
- CLOSE_TO_LIMIT_MESSAGE_PERSONAL_NAMESPACE,
- DANGER_ALERT_TITLE_PERSONAL_NAMESPACE,
} from '../constants';
export default {
@@ -52,13 +50,6 @@ export default {
});
},
dangerAlertTitle() {
- if (this.usersLimitDataset.userNamespace) {
- return sprintf(DANGER_ALERT_TITLE_PERSONAL_NAMESPACE, {
- count: this.freeUsersLimit,
- members: this.pluralMembers(this.freeUsersLimit),
- });
- }
-
return sprintf(DANGER_ALERT_TITLE, {
count: this.freeUsersLimit,
members: this.pluralMembers(this.freeUsersLimit),
@@ -71,20 +62,9 @@ export default {
title() {
return this.reachedLimit ? this.dangerAlertTitle : this.warningAlertTitle;
},
- reachedLimitMessage() {
- if (this.usersLimitDataset.userNamespace) {
- return this.$options.i18n.reachedLimitMessage;
- }
-
- return this.$options.i18n.reachedLimitUpgradeSuggestionMessage;
- },
message() {
if (this.reachedLimit) {
- return this.reachedLimitMessage;
- }
-
- if (this.usersLimitDataset.userNamespace) {
- return this.$options.i18n.closeToLimitMessagePersonalNamespace;
+ return this.$options.i18n.reachedLimitUpgradeSuggestionMessage;
}
return this.$options.i18n.closeToLimitMessage;
@@ -99,7 +79,6 @@ export default {
reachedLimitMessage: REACHED_LIMIT_MESSAGE,
reachedLimitUpgradeSuggestionMessage: REACHED_LIMIT_UPGRADE_SUGGESTION_MESSAGE,
closeToLimitMessage: CLOSE_TO_LIMIT_MESSAGE,
- closeToLimitMessagePersonalNamespace: CLOSE_TO_LIMIT_MESSAGE_PERSONAL_NAMESPACE,
},
};
</script>
diff --git a/app/assets/javascripts/invite_members/constants.js b/app/assets/javascripts/invite_members/constants.js
index 1ceb63e2146..f502e1ea369 100644
--- a/app/assets/javascripts/invite_members/constants.js
+++ b/app/assets/javascripts/invite_members/constants.js
@@ -81,6 +81,9 @@ 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 MEMBER_MODAL_LABELS = {
modal: {
@@ -119,6 +122,7 @@ export const MEMBER_MODAL_LABELS = {
memberErrorListText: MEMBER_ERROR_LIST_TEXT,
collapsedErrors: COLLAPSED_ERRORS,
expandedErrors: EXPANDED_ERRORS,
+ emptyInvitesErrorText: EMPTY_INVITES_ERROR_TEXT,
};
export const GROUP_MODAL_LABELS = {
@@ -146,10 +150,6 @@ export const DANGER_ALERT_TITLE = s__(
"InviteMembersModal|You've reached your %{count} %{members} limit for %{name}",
);
-export const DANGER_ALERT_TITLE_PERSONAL_NAMESPACE = s__(
- "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects",
-);
-
export const REACHED_LIMIT_MESSAGE = s__(
'InviteMembersModal|You cannot add more members, but you can remove members who no longer need access.',
);
@@ -163,6 +163,3 @@ export const REACHED_LIMIT_UPGRADE_SUGGESTION_MESSAGE = REACHED_LIMIT_MESSAGE.co
export const CLOSE_TO_LIMIT_MESSAGE = s__(
'InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier.',
);
-export const CLOSE_TO_LIMIT_MESSAGE_PERSONAL_NAMESPACE = s__(
- 'InviteMembersModal|To make more space, you can remove members who no longer need access.',
-);
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 6e2c0ecb5bb..a4be3f205a3 100644
--- a/app/assets/javascripts/invite_members/init_invite_members_modal.js
+++ b/app/assets/javascripts/invite_members/init_invite_members_modal.js
@@ -20,8 +20,6 @@ export default (function initInviteMembersModal() {
return false;
}
- const usersLimitDataset = JSON.parse(el.dataset.usersLimitDataset || '{}');
-
inviteMembersModal = new Vue({
el,
name: 'InviteMembersModalRoot',
@@ -40,10 +38,9 @@ export default (function initInviteMembersModal() {
projects: JSON.parse(el.dataset.projects || '[]'),
usersFilter: el.dataset.usersFilter,
filterId: parseInt(el.dataset.filterId, 10),
- usersLimitDataset: convertObjectPropsToCamelCase({
- ...usersLimitDataset,
- user_namespace: parseBoolean(usersLimitDataset.user_namespace),
- }),
+ usersLimitDataset: convertObjectPropsToCamelCase(
+ JSON.parse(el.dataset.usersLimitDataset || '{}'),
+ ),
},
}),
});
diff --git a/app/assets/javascripts/issuable/components/issue_assignees.vue b/app/assets/javascripts/issuable/components/issue_assignees.vue
index 5955f31fc70..21f35690f6d 100644
--- a/app/assets/javascripts/issuable/components/issue_assignees.vue
+++ b/app/assets/javascripts/issuable/components/issue_assignees.vue
@@ -91,7 +91,7 @@ export default {
data-qa-selector="assignee_link"
>
<span class="js-assignee-tooltip">
- <span class="bold d-block">{{ __('Assignee') }}</span> {{ assignee.name }}
+ <span class="bold d-block">{{ s__('Label|Assignee') }}</span> {{ assignee.name }}
<span v-if="assignee.username" class="text-white-50">@{{ assignee.username }}</span>
</span>
</user-avatar-link>
diff --git a/app/assets/javascripts/issuable/components/related_issuable_item.vue b/app/assets/javascripts/issuable/components/related_issuable_item.vue
index 667c712d3be..8894e8f63b8 100644
--- a/app/assets/javascripts/issuable/components/related_issuable_item.vue
+++ b/app/assets/javascripts/issuable/components/related_issuable_item.vue
@@ -11,6 +11,7 @@ import {
import IssueDueDate from '~/boards/components/issue_due_date.vue';
import { TYPE_WORK_ITEM } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
+import { isMetaKey } from '~/lib/utils/common_utils';
import { setUrlParams, updateHistory } from '~/lib/utils/url_utility';
import { sprintf } from '~/locale';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
@@ -80,6 +81,9 @@ export default {
methods: {
handleTitleClick(event) {
if (this.workItemType === 'TASK') {
+ if (isMetaKey(event)) {
+ return;
+ }
event.preventDefault();
this.$refs.modal.show();
this.updateWorkItemIdUrlQuery(this.idKey);
diff --git a/app/assets/javascripts/issuable/components/status_box.vue b/app/assets/javascripts/issuable/components/status_box.vue
index d72ee5c6757..6c4ffc44444 100644
--- a/app/assets/javascripts/issuable/components/status_box.vue
+++ b/app/assets/javascripts/issuable/components/status_box.vue
@@ -65,7 +65,7 @@ export default {
data() {
if (!this.iid) return { state: this.initialState };
- if (this.initialState) {
+ if (this.initialState && !badgeState.state) {
badgeState.state = this.initialState;
}
diff --git a/app/assets/javascripts/issuable/issuable_form.js b/app/assets/javascripts/issuable/issuable_form.js
index cc2608b5c62..81bf7ca6ccc 100644
--- a/app/assets/javascripts/issuable/issuable_form.js
+++ b/app/assets/javascripts/issuable/issuable_form.js
@@ -39,12 +39,26 @@ function format(searchTerm, isFallbackKey = false) {
return formattedQuery;
}
+function getSearchTerm(newIssuePath) {
+ const { search, pathname } = document.location;
+ return newIssuePath === pathname ? '' : format(search);
+}
+
function getFallbackKey() {
const searchTerm = format(document.location.search, true);
return ['autosave', document.location.pathname, searchTerm].join('/');
}
export default class IssuableForm {
+ static addAutosave(map, id, $input, searchTerm, fallbackKey) {
+ if ($input.length) {
+ map.set(
+ id,
+ new Autosave($input, [document.location.pathname, searchTerm, id], `${fallbackKey}=${id}`),
+ );
+ }
+ }
+
constructor(form) {
if (form.length === 0) {
return;
@@ -72,14 +86,15 @@ export default class IssuableForm {
this.reviewersSelect = new UsersSelect(undefined, '.js-reviewer-search');
this.zenMode = new ZenMode();
- this.newIssuePath = form[0].getAttribute(DATA_ISSUES_NEW_PATH);
+ this.searchTerm = getSearchTerm(form[0].getAttribute(DATA_ISSUES_NEW_PATH));
+ this.fallbackKey = getFallbackKey();
this.titleField = this.form.find('input[name*="[title]"]');
this.descriptionField = this.form.find('textarea[name*="[description]"]');
if (!(this.titleField.length && this.descriptionField.length)) {
return;
}
- this.initAutosave();
+ this.autosaves = this.initAutosave();
this.form.on('submit', this.handleSubmit);
this.form.on('click', '.btn-cancel, .js-reset-autosave', this.resetAutosave);
this.form.find('.js-unwrap-on-load').unwrap();
@@ -95,7 +110,10 @@ export default class IssuableForm {
container: $issuableDueDate.parent().get(0),
parse: (dateString) => parsePikadayDate(dateString),
toString: (date) => pikadayToString(date),
- onSelect: (dateText) => $issuableDueDate.val(calendar.toString(dateText)),
+ onSelect: (dateText) => {
+ $issuableDueDate.val(calendar.toString(dateText));
+ if (this.autosaves.has('due_date')) this.autosaves.get('due_date').save();
+ },
firstDay: gon.first_day_of_week,
});
calendar.setDate(parsePikadayDate($issuableDueDate.val()));
@@ -109,21 +127,37 @@ export default class IssuableForm {
}
initAutosave() {
- const { search, pathname } = document.location;
- const searchTerm = this.newIssuePath === pathname ? '' : format(search);
- const fallbackKey = getFallbackKey();
-
- this.autosave = new Autosave(
- this.titleField,
- [document.location.pathname, searchTerm, 'title'],
- `${fallbackKey}=title`,
+ const autosaveMap = new Map();
+ IssuableForm.addAutosave(
+ autosaveMap,
+ 'title',
+ this.form.find('input[name*="[title]"]'),
+ this.searchTerm,
+ this.fallbackKey,
);
-
- return new Autosave(
- this.descriptionField,
- [document.location.pathname, searchTerm, 'description'],
- `${fallbackKey}=description`,
+ IssuableForm.addAutosave(
+ autosaveMap,
+ 'description',
+ this.form.find('textarea[name*="[description]"]'),
+ this.searchTerm,
+ this.fallbackKey,
+ );
+ IssuableForm.addAutosave(
+ autosaveMap,
+ 'confidential',
+ this.form.find('input:checkbox[name*="[confidential]"]'),
+ this.searchTerm,
+ this.fallbackKey,
);
+ IssuableForm.addAutosave(
+ autosaveMap,
+ 'due_date',
+ this.form.find('input[name*="[due_date]"]'),
+ this.searchTerm,
+ this.fallbackKey,
+ );
+
+ return autosaveMap;
}
handleSubmit() {
@@ -131,8 +165,9 @@ export default class IssuableForm {
}
resetAutosave() {
- this.titleField.data('autosave').reset();
- return this.descriptionField.data('autosave').reset();
+ this.autosaves.forEach((autosaveItem) => {
+ autosaveItem?.reset();
+ });
}
initWip() {
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 11911adb401..0b424d105b9 100644
--- a/app/assets/javascripts/issues/list/components/issues_list_app.vue
+++ b/app/assets/javascripts/issues/list/components/issues_list_app.vue
@@ -24,6 +24,7 @@ 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,
OPERATOR_IS_ONLY,
@@ -37,6 +38,7 @@ import {
TOKEN_TITLE_ORGANIZATION,
TOKEN_TITLE_RELEASE,
TOKEN_TITLE_TYPE,
+ FILTERED_SEARCH_TERM,
} 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';
@@ -462,6 +464,12 @@ export default {
page_before: this.pageParams.beforeCursor ?? undefined,
};
},
+ issuesHelpPagePath() {
+ return helpPagePath('user/project/issues/index');
+ },
+ shouldDisableSomeFilters() {
+ return this.isAnonymousSearchDisabled && !this.isSignedIn;
+ },
},
watch: {
$route(newValue, oldValue) {
@@ -578,13 +586,9 @@ export default {
this.issuesError = null;
},
handleFilter(filter) {
- if (this.isAnonymousSearchDisabled && !this.isSignedIn) {
- this.showAnonymousSearchingMessage();
- return;
- }
+ this.setFilterTokens(filter);
this.pageParams = getInitialPageParams(this.pageSize);
- this.filterTokens = filter;
this.$router.push({ query: this.urlParams });
},
@@ -674,6 +678,28 @@ export default {
Sentry.captureException(error);
});
},
+ setFilterTokens(filtersArg) {
+ const filters = this.removeDisabledSearchTerms(filtersArg);
+
+ this.filterTokens = filters;
+
+ // If we filtered something out, let's show a warning message
+ if (filters.length < filtersArg.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;
+ },
showAnonymousSearchingMessage() {
createFlash({
message: this.$options.i18n.anonymousSearchingMessage,
@@ -720,17 +746,9 @@ export default {
sortKey = defaultSortKey;
}
- const isSearchDisabled =
- this.isAnonymousSearchDisabled &&
- !this.isSignedIn &&
- window.location.search.includes('search=');
-
- if (isSearchDisabled) {
- this.showAnonymousSearchingMessage();
- }
-
this.exportCsvPathWithQuery = this.getExportCsvPathWithQuery();
- this.filterTokens = isSearchDisabled ? [] : getFilterTokens(window.location.search);
+ this.setFilterTokens(getFilterTokens(window.location.search));
+
this.pageParams = getInitialPageParams(
this.pageSize,
isPositiveInteger(firstPageSize) ? parseInt(firstPageSize, 10) : undefined,
@@ -899,7 +917,9 @@ export default {
<template v-else-if="isSignedIn">
<gl-empty-state :title="$options.i18n.noIssuesSignedInTitle" :svg-path="emptyStateSvgPath">
<template #description>
- <p>{{ $options.i18n.noIssuesSignedInDescription }}</p>
+ <gl-link :href="issuesHelpPagePath" target="_blank">{{
+ $options.i18n.noIssuesSignedInDescription
+ }}</gl-link>
<p v-if="canCreateProjects">
<strong>{{ $options.i18n.noGroupIssuesSignedInDescription }}</strong>
</p>
diff --git a/app/assets/javascripts/issues/list/constants.js b/app/assets/javascripts/issues/list/constants.js
index 38fe4c33792..27738d7a3e6 100644
--- a/app/assets/javascripts/issues/list/constants.js
+++ b/app/assets/javascripts/issues/list/constants.js
@@ -41,12 +41,8 @@ export const i18n = {
),
noOpenIssuesDescription: __('To keep this project going, create a new issue'),
noOpenIssuesTitle: __('There are no open issues'),
- noIssuesSignedInDescription: __(
- 'Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable.',
- ),
- noIssuesSignedInTitle: __(
- 'The Issue Tracker is the place to add things that need to be improved or solved in a project',
- ),
+ noIssuesSignedInDescription: __('Learn more about issues.'),
+ noIssuesSignedInTitle: __('Use issues to collaborate on ideas, solve problems, and plan work'),
noIssuesSignedOutButtonText: __('Register / Sign In'),
noIssuesSignedOutDescription: __(
'The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project.',
@@ -151,6 +147,7 @@ export const TOKEN_TYPE_EPIC = 'epic_id';
export const TOKEN_TYPE_WEIGHT = 'weight';
export const TOKEN_TYPE_CONTACT = 'crm_contact';
export const TOKEN_TYPE_ORGANIZATION = 'crm_organization';
+export const TOKEN_TYPE_HEALTH = 'health_status';
export const TYPE_TOKEN_TASK_OPTION = { icon: 'task-done', title: 'task', value: 'task' };
@@ -327,6 +324,16 @@ export const filters = {
},
},
},
+ [TOKEN_TYPE_HEALTH]: {
+ [API_PARAM]: {
+ [NORMAL_FILTER]: 'healthStatus',
+ },
+ [URL_PARAM]: {
+ [OPERATOR_IS]: {
+ [NORMAL_FILTER]: 'health_status',
+ },
+ },
+ },
[TOKEN_TYPE_CONTACT]: {
[API_PARAM]: {
[NORMAL_FILTER]: 'crmContactId',
diff --git a/app/assets/javascripts/issues/related_merge_requests/components/related_merge_requests.vue b/app/assets/javascripts/issues/related_merge_requests/components/related_merge_requests.vue
index a5cba3daafa..149049247fb 100644
--- a/app/assets/javascripts/issues/related_merge_requests/components/related_merge_requests.vue
+++ b/app/assets/javascripts/issues/related_merge_requests/components/related_merge_requests.vue
@@ -65,7 +65,7 @@ export default {
<template>
<div v-if="isFetchingMergeRequests || (!isFetchingMergeRequests && totalCount)">
- <div class="card card-slim gl-mt-5">
+ <div class="card card-slim gl-mt-5 gl-mb-0">
<div class="card-header gl-bg-gray-10">
<div
class="card-title gl-relative gl-display-flex gl-align-items-center gl-line-height-20 gl-font-weight-bold gl-m-0"
@@ -112,7 +112,7 @@ export default {
</div>
<div
v-if="hasClosingMergeRequest && !isFetchingMergeRequests"
- class="issue-closed-by-widget second-block"
+ class="issue-closed-by-widget second-block gl-mt-3"
>
{{ closingMergeRequestsText }}
</div>
diff --git a/app/assets/javascripts/issues/show/components/app.vue b/app/assets/javascripts/issues/show/components/app.vue
index c664135f30e..0daf77e03dc 100644
--- a/app/assets/javascripts/issues/show/components/app.vue
+++ b/app/assets/javascripts/issues/show/components/app.vue
@@ -17,11 +17,11 @@ import eventHub from '../event_hub';
import getIssueStateQuery from '../queries/get_issue_state.query.graphql';
import Service from '../services/index';
import Store from '../stores';
-import descriptionComponent from './description.vue';
-import editedComponent from './edited.vue';
-import formComponent from './form.vue';
+import DescriptionComponent from './description.vue';
+import EditedComponent from './edited.vue';
+import FormComponent from './form.vue';
import PinnedLinks from './pinned_links.vue';
-import titleComponent from './title.vue';
+import TitleComponent from './title.vue';
export default {
WorkspaceType,
@@ -29,9 +29,9 @@ export default {
GlIcon,
GlBadge,
GlIntersectionObserver,
- titleComponent,
- editedComponent,
- formComponent,
+ TitleComponent,
+ EditedComponent,
+ FormComponent,
PinnedLinks,
ConfidentialityBadge,
},
@@ -51,20 +51,11 @@ export default {
required: true,
type: Boolean,
},
- canDestroy: {
- required: true,
- type: Boolean,
- },
showInlineEditButton: {
type: Boolean,
required: false,
default: true,
},
- showDeleteButton: {
- type: Boolean,
- required: false,
- default: true,
- },
enableAutocomplete: {
type: Boolean,
required: false,
@@ -181,7 +172,7 @@ export default {
type: Object,
required: false,
default: () => {
- return descriptionComponent;
+ return DescriptionComponent;
},
},
showTitleBorder: {
@@ -494,14 +485,12 @@ export default {
:endpoint="endpoint"
:form-state="formState"
:initial-description-text="initialDescriptionText"
- :can-destroy="canDestroy"
:issuable-templates="issuableTemplates"
:markdown-docs-path="markdownDocsPath"
:markdown-preview-path="markdownPreviewPath"
:project-path="projectPath"
:project-id="projectId"
:project-namespace="projectNamespace"
- :show-delete-button="showDeleteButton"
:can-attach-file="canAttachFile"
:enable-autocomplete="enableAutocomplete"
:issuable-type="issuableType"
diff --git a/app/assets/javascripts/issues/show/components/delete_issue_modal.vue b/app/assets/javascripts/issues/show/components/delete_issue_modal.vue
index 47b09bd6aa0..f86ee11e64b 100644
--- a/app/assets/javascripts/issues/show/components/delete_issue_modal.vue
+++ b/app/assets/javascripts/issues/show/components/delete_issue_modal.vue
@@ -13,7 +13,8 @@ export default {
props: {
issuePath: {
type: String,
- required: true,
+ required: false,
+ default: '',
},
issueType: {
type: String,
diff --git a/app/assets/javascripts/issues/show/components/description.vue b/app/assets/javascripts/issues/show/components/description.vue
index a6747d67611..5c2a154362f 100644
--- a/app/assets/javascripts/issues/show/components/description.vue
+++ b/app/assets/javascripts/issues/show/components/description.vue
@@ -7,6 +7,7 @@ import { getIdFromGraphQLId, convertToGraphQLId } from '~/graphql_shared/utils';
import { TYPE_WORK_ITEM } from '~/graphql_shared/constants';
import createFlash from '~/flash';
import { IssuableType } from '~/issues/constants';
+import { isMetaKey } from '~/lib/utils/common_utils';
import { isPositiveInteger } from '~/lib/utils/number_utils';
import { getParameterByName, setUrlParams, updateHistory } from '~/lib/utils/url_utility';
import { __, s__, sprintf } from '~/locale';
@@ -20,6 +21,8 @@ import createWorkItemFromTaskMutation from '~/work_items/graphql/create_work_ite
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
import {
+ sprintfWorkItem,
+ I18N_WORK_ITEM_ERROR_CREATING,
TRACKING_CATEGORY_SHOW,
TASK_TYPE_NAME,
WIDGET_TYPE_DESCRIPTION,
@@ -226,6 +229,7 @@ export default {
},
createDragIconElement() {
const container = document.createElement('div');
+ // eslint-disable-next-line no-unsanitized/property
container.innerHTML = `<svg class="drag-icon s14 gl-icon gl-cursor-grab gl-visibility-hidden" role="img" aria-hidden="true">
<use href="${gon.sprite_icons}#drag-vertical"></use>
</svg>`;
@@ -330,6 +334,9 @@ export default {
this.addHoverListeners(taskLink, workItemId);
taskLink.classList.add('gl-link');
taskLink.addEventListener('click', (e) => {
+ if (isMetaKey(e)) {
+ return;
+ }
e.preventDefault();
this.openWorkItemDetailModal(taskLink);
this.workItemId = workItemId;
@@ -358,6 +365,7 @@ export default {
);
button.id = `js-task-button-${index}`;
this.taskButtons.push(button.id);
+ // eslint-disable-next-line no-unsanitized/property
button.innerHTML = `
<svg data-testid="ellipsis_v-icon" role="img" aria-hidden="true" class="dropdown-icon gl-icon s14">
<use href="${gon.sprite_icons}#doc-new"></use>
@@ -460,7 +468,7 @@ export default {
this.openWorkItemDetailModal(el);
} catch (error) {
createFlash({
- message: s__('WorkItem|Something went wrong when creating a work item. Please try again'),
+ message: sprintfWorkItem(I18N_WORK_ITEM_ERROR_CREATING, workItemTypes.TASK),
error,
captureError: true,
});
diff --git a/app/assets/javascripts/issues/show/components/edit_actions.vue b/app/assets/javascripts/issues/show/components/edit_actions.vue
index 358b53bd131..120034b8d67 100644
--- a/app/assets/javascripts/issues/show/components/edit_actions.vue
+++ b/app/assets/javascripts/issues/show/components/edit_actions.vue
@@ -1,12 +1,10 @@
<script>
-import { GlButton, GlModalDirective } from '@gitlab/ui';
-import { uniqueId } from 'lodash';
-import { __, sprintf } from '~/locale';
+import { GlButton } from '@gitlab/ui';
+import { __ } from '~/locale';
import Tracking from '~/tracking';
import eventHub from '../event_hub';
import updateMixin from '../mixins/update';
import getIssueStateQuery from '../queries/get_issue_state.query.graphql';
-import DeleteIssueModal from './delete_issue_modal.vue';
const issuableTypes = {
issue: __('Issue'),
@@ -18,18 +16,10 @@ const trackingMixin = Tracking.mixin({ label: 'delete_issue' });
export default {
components: {
- DeleteIssueModal,
GlButton,
},
- directives: {
- GlModal: GlModalDirective,
- },
mixins: [trackingMixin, updateMixin],
props: {
- canDestroy: {
- type: Boolean,
- required: true,
- },
endpoint: {
required: true,
type: String,
@@ -38,11 +28,6 @@ export default {
type: Object,
required: true,
},
- showDeleteButton: {
- type: Boolean,
- required: false,
- default: true,
- },
issuableType: {
type: String,
required: true,
@@ -53,7 +38,6 @@ export default {
deleteLoading: false,
skipApollo: false,
issueState: {},
- modalId: uniqueId('delete-issuable-modal-'),
};
},
apollo: {
@@ -68,17 +52,9 @@ export default {
},
},
computed: {
- deleteIssuableButtonText() {
- return sprintf(__('Delete %{issuableType}'), {
- issuableType: this.typeToShow.toLowerCase(),
- });
- },
isSubmitEnabled() {
return this.formState.title.trim() !== '';
},
- shouldShowDeleteButton() {
- return this.canDestroy && this.showDeleteButton && this.typeToShow;
- },
typeToShow() {
const { issueState, issuableType } = this;
const type = issueState.issueType ?? issuableType;
@@ -89,52 +65,26 @@ export default {
closeForm() {
eventHub.$emit('close.form');
},
- deleteIssuable() {
- this.deleteLoading = true;
- eventHub.$emit('delete.issuable');
- },
},
};
</script>
<template>
- <div class="gl-mt-3 gl-mb-3 gl-display-flex gl-justify-content-space-between">
- <div>
- <gl-button
- :loading="formState.updateLoading"
- :disabled="formState.updateLoading || !isSubmitEnabled"
- category="primary"
- variant="confirm"
- class="gl-mr-3"
- data-testid="issuable-save-button"
- type="submit"
- @click.prevent="updateIssuable"
- >
- {{ __('Save changes') }}
- </gl-button>
- <gl-button data-testid="issuable-cancel-button" @click="closeForm">
- {{ __('Cancel') }}
- </gl-button>
- </div>
- <div v-if="shouldShowDeleteButton">
- <gl-button
- v-gl-modal="modalId"
- :loading="deleteLoading"
- :disabled="deleteLoading"
- category="secondary"
- variant="danger"
- data-testid="issuable-delete-button"
- @click="track('click_button')"
- >
- {{ deleteIssuableButtonText }}
- </gl-button>
- <delete-issue-modal
- :issue-path="endpoint"
- :issue-type="typeToShow"
- :modal-id="modalId"
- :title="deleteIssuableButtonText"
- @delete="deleteIssuable"
- />
- </div>
+ <div class="gl-mt-3 gl-mb-3 gl-display-flex">
+ <gl-button
+ :loading="formState.updateLoading"
+ :disabled="formState.updateLoading || !isSubmitEnabled"
+ category="primary"
+ variant="confirm"
+ class="gl-mr-3"
+ data-testid="issuable-save-button"
+ type="submit"
+ @click.prevent="updateIssuable"
+ >
+ {{ __('Save changes') }}
+ </gl-button>
+ <gl-button data-testid="issuable-cancel-button" @click="closeForm">
+ {{ __('Cancel') }}
+ </gl-button>
</div>
</template>
diff --git a/app/assets/javascripts/issues/show/components/edited.vue b/app/assets/javascripts/issues/show/components/edited.vue
index 41cc3964055..4c5f783cd66 100644
--- a/app/assets/javascripts/issues/show/components/edited.vue
+++ b/app/assets/javascripts/issues/show/components/edited.vue
@@ -1,10 +1,10 @@
<script>
/* eslint-disable @gitlab/vue-require-i18n-strings */
-import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
export default {
components: {
- timeAgoTooltip,
+ TimeAgoTooltip,
},
props: {
updatedAt: {
diff --git a/app/assets/javascripts/issues/show/components/fields/description.vue b/app/assets/javascripts/issues/show/components/fields/description.vue
index f45af47374a..c2ab7c4f298 100644
--- a/app/assets/javascripts/issues/show/components/fields/description.vue
+++ b/app/assets/javascripts/issues/show/components/fields/description.vue
@@ -1,11 +1,11 @@
<script>
-import markdownField from '~/vue_shared/components/markdown/field.vue';
+import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import { helpPagePath } from '~/helpers/help_page_helper';
import updateMixin from '../../mixins/update';
export default {
components: {
- markdownField,
+ MarkdownField,
},
mixins: [updateMixin],
props: {
diff --git a/app/assets/javascripts/issues/show/components/form.vue b/app/assets/javascripts/issues/show/components/form.vue
index e2c12edf46d..f479c8ae78d 100644
--- a/app/assets/javascripts/issues/show/components/form.vue
+++ b/app/assets/javascripts/issues/show/components/form.vue
@@ -22,10 +22,6 @@ export default {
LockedWarning,
},
props: {
- canDestroy: {
- type: Boolean,
- required: true,
- },
endpoint: {
type: String,
required: true,
@@ -63,11 +59,6 @@ export default {
type: String,
required: true,
},
- showDeleteButton: {
- type: Boolean,
- required: false,
- default: true,
- },
canAttachFile: {
type: Boolean,
required: false,
@@ -231,12 +222,6 @@ export default {
:enable-autocomplete="enableAutocomplete"
/>
- <edit-actions
- :endpoint="endpoint"
- :form-state="formState"
- :can-destroy="canDestroy"
- :show-delete-button="showDeleteButton"
- :issuable-type="issuableType"
- />
+ <edit-actions :endpoint="endpoint" :form-state="formState" :issuable-type="issuableType" />
</form>
</template>
diff --git a/app/assets/javascripts/issues/show/components/incidents/constants.js b/app/assets/javascripts/issues/show/components/incidents/constants.js
index 77d13fe085a..aa7b9805b5f 100644
--- a/app/assets/javascripts/issues/show/components/incidents/constants.js
+++ b/app/assets/javascripts/issues/show/components/incidents/constants.js
@@ -26,4 +26,15 @@ export const timelineListI18n = Object.freeze({
'Incident|Something went wrong while deleting the incident timeline event.',
),
deleteModal: s__('Incident|Are you sure you want to delete this event?'),
+ editError: s__('Incident|Error updating incident timeline event: %{error}'),
+ editErrorGeneric: s__(
+ 'Incident|Something went wrong while updating the incident timeline event.',
+ ),
+});
+
+export const timelineItemI18n = Object.freeze({
+ delete: __('Delete'),
+ edit: __('Edit'),
+ moreActions: __('More actions'),
+ timeUTC: __('%{time} UTC'),
});
diff --git a/app/assets/javascripts/issues/show/components/incidents/create_timeline_event.vue b/app/assets/javascripts/issues/show/components/incidents/create_timeline_event.vue
index c902895702e..6bb72e82778 100644
--- a/app/assets/javascripts/issues/show/components/incidents/create_timeline_event.vue
+++ b/app/assets/javascripts/issues/show/components/incidents/create_timeline_event.vue
@@ -1,6 +1,7 @@
<script>
import { produce } from 'immer';
import { sortBy } from 'lodash';
+import { GlIcon } from '@gitlab/ui';
import { sprintf } from '~/locale';
import { createAlert } from '~/flash';
import { convertToGraphQLId } from '~/graphql_shared/utils';
@@ -16,6 +17,7 @@ export default {
i18n: timelineFormI18n,
components: {
TimelineEventsForm,
+ GlIcon,
},
inject: ['fullPath', 'issuableId'],
props: {
@@ -31,9 +33,6 @@ export default {
clearForm() {
this.$refs.eventForm.clear();
},
- focusDate() {
- this.$refs.eventForm.focusDate();
- },
updateCache(store, { data }) {
const { timelineEvent: event, errors } = data?.timelineEventCreate || {};
@@ -107,11 +106,23 @@ export default {
</script>
<template>
- <timeline-events-form
- ref="eventForm"
- :is-event-processed="createTimelineEventActive"
- :has-timeline-events="hasTimelineEvents"
- @save-event="createIncidentTimelineEvent"
- @cancel="$emit('hide-new-timeline-events-form')"
- />
+ <div
+ class="create-timeline-event gl-relative gl-display-flex gl-align-items-start"
+ :class="{ 'timeline-entry-vertical-line': hasTimelineEvents }"
+ >
+ <div
+ v-if="hasTimelineEvents"
+ class="gl-display-flex gl-align-items-center gl-justify-content-center gl-align-self-start 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-z-index-1"
+ >
+ <gl-icon name="comment" class="note-icon" />
+ </div>
+ <timeline-events-form
+ ref="eventForm"
+ :class="{ 'gl-border-gray-50 gl-border-t': hasTimelineEvents }"
+ :is-event-processed="createTimelineEventActive"
+ show-save-and-add
+ @save-event="createIncidentTimelineEvent"
+ @cancel="$emit('hide-new-timeline-events-form')"
+ />
+ </div>
</template>
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
new file mode 100644
index 00000000000..60fa8cb949b
--- /dev/null
+++ b/app/assets/javascripts/issues/show/components/incidents/edit_timeline_event.vue
@@ -0,0 +1,47 @@
+<script>
+import { GlIcon } from '@gitlab/ui';
+import TimelineEventsForm from './timeline_events_form.vue';
+
+export default {
+ name: 'EditTimelineEvent',
+ components: {
+ TimelineEventsForm,
+ GlIcon,
+ },
+ props: {
+ event: {
+ type: Object,
+ required: true,
+ validator: (item) => ['occurredAt', 'note'].every((key) => item[key]),
+ },
+ editTimelineEventActive: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ methods: {
+ saveEvent(eventDetails) {
+ this.$emit('handle-save-edit', { ...eventDetails, id: this.event.id }, false);
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="gl-relative gl-display-flex gl-align-items-center">
+ <div
+ class="gl-display-flex gl-align-items-center gl-justify-content-center gl-align-self-start 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-z-index-1"
+ >
+ <gl-icon name="comment" class="note-icon" />
+ </div>
+ <timeline-events-form
+ ref="eventForm"
+ class="timeline-event-border"
+ :is-event-processed="editTimelineEventActive"
+ :previous-occurred-at="event.occurredAt"
+ :previous-note="event.note"
+ @save-event="saveEvent"
+ @cancel="$emit('hide-edit')"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/edit_timeline_event.mutation.graphql b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/edit_timeline_event.mutation.graphql
new file mode 100644
index 00000000000..54f036268cc
--- /dev/null
+++ b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/edit_timeline_event.mutation.graphql
@@ -0,0 +1,13 @@
+mutation UpdateTimelineEvent($input: TimelineEventUpdateInput!) {
+ timelineEventUpdate(input: $input) {
+ timelineEvent {
+ id
+ note
+ noteHtml
+ action
+ occurredAt
+ createdAt
+ }
+ errors
+ }
+}
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 0d84fabb1be..b7ae18372ab 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,9 +1,9 @@
<script>
-import { GlDatepicker, GlFormInput, GlFormGroup, GlButton, GlIcon } from '@gitlab/ui';
+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 { timelineFormI18n } from './constants';
-import { getUtcShiftedDateNow } from './utils';
+import { getUtcShiftedDate } from './utils';
export default {
name: 'TimelineEventsForm',
@@ -15,6 +15,7 @@ export default {
'task-list',
'collapsible-section',
'table',
+ 'attach-file',
'full-screen',
],
components: {
@@ -23,175 +24,168 @@ export default {
GlFormInput,
GlFormGroup,
GlButton,
- GlIcon,
},
i18n: timelineFormI18n,
directives: {
autofocusonshow,
},
props: {
- hasTimelineEvents: {
+ showSaveAndAdd: {
type: Boolean,
- required: true,
+ required: false,
+ default: false,
},
isEventProcessed: {
type: Boolean,
required: true,
},
+ previousOccurredAt: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ previousNote: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
data() {
- // if occurredAt is undefined, returns "now" in UTC
- const placeholderDate = getUtcShiftedDateNow();
+ // if occurredAt is null, returns "now" in UTC
+ const placeholderDate = getUtcShiftedDate(this.previousOccurredAt);
return {
- timelineText: '',
+ timelineText: this.previousNote,
placeholderDate,
hourPickerInput: placeholderDate.getHours(),
minutePickerInput: placeholderDate.getMinutes(),
- datepickerTextInput: null,
+ datePickerInput: placeholderDate,
};
},
computed: {
- occurredAt() {
- const [years, months, days] = this.datepickerTextInput.split('-');
+ occurredAtString() {
+ const year = this.datePickerInput.getFullYear();
+ const month = this.datePickerInput.getMonth();
+ const day = this.datePickerInput.getDate();
+
const utcDate = new Date(
- Date.UTC(years, months - 1, days, this.hourPickerInput, this.minutePickerInput),
+ Date.UTC(year, month, day, this.hourPickerInput, this.minutePickerInput),
);
return utcDate.toISOString();
},
},
+ mounted() {
+ this.focusDate();
+ },
methods: {
clear() {
- const utcShiftedDateNow = getUtcShiftedDateNow();
- this.placeholderDate = utcShiftedDateNow;
- this.hourPickerInput = utcShiftedDateNow.getHours();
- this.minutePickerInput = utcShiftedDateNow.getMinutes();
+ const newPlaceholderDate = getUtcShiftedDate();
+ this.datePickerInput = newPlaceholderDate;
+ this.hourPickerInput = newPlaceholderDate.getHours();
+ this.minutePickerInput = newPlaceholderDate.getMinutes();
this.timelineText = '';
},
focusDate() {
- this.$refs.datepicker.$el.focus();
+ this.$refs.datepicker.$el.querySelector('input').focus();
},
handleSave(addAnotherEvent) {
- const eventDetails = {
+ const event = {
note: this.timelineText,
- occurredAt: this.occurredAt,
+ occurredAt: this.occurredAtString,
};
- this.$emit('save-event', eventDetails, addAnotherEvent);
+ this.$emit('save-event', event, addAnotherEvent);
},
},
};
</script>
<template>
- <div
- class="gl-relative gl-display-flex gl-align-items-center"
- :class="{ 'timeline-entry-vertical-line': hasTimelineEvents }"
- >
- <div
- v-if="hasTimelineEvents"
- class="gl-display-flex gl-align-items-center gl-justify-content-center gl-align-self-start 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-z-index-1"
- >
- <gl-icon name="comment" class="note-icon" />
- </div>
- <form class="gl-flex-grow-1 gl-border-gray-50" :class="{ 'gl-border-t': hasTimelineEvents }">
- <div
- class="gl-display-flex gl-flex-direction-column gl-sm-flex-direction-row datetime-picker"
- >
- <gl-form-group :label="__('Date')" class="gl-mt-5 gl-mr-5">
- <gl-datepicker id="incident-date" #default="{ formattedDate }" v-model="placeholderDate">
+ <form class="gl-flex-grow-1 gl-border-gray-50">
+ <div class="gl-display-flex gl-flex-direction-column gl-sm-flex-direction-row">
+ <gl-form-group :label="__('Date')" class="gl-mt-5 gl-mr-5">
+ <gl-datepicker id="incident-date" ref="datepicker" v-model="datePickerInput" />
+ </gl-form-group>
+ <div class="gl-display-flex gl-mt-5">
+ <gl-form-group :label="__('Time')">
+ <div class="gl-display-flex">
+ <label label-for="timeline-input-hours" class="sr-only"></label>
<gl-form-input
- id="incident-date"
- ref="datepicker"
- v-model="datepickerTextInput"
- data-testid="input-datepicker"
- class="gl-datepicker-input gl-pr-7!"
- :value="formattedDate"
- :placeholder="__('YYYY-MM-DD')"
- @keydown.enter="onKeydown"
+ id="timeline-input-hours"
+ v-model="hourPickerInput"
+ data-testid="input-hours"
+ size="xs"
+ type="number"
+ min="00"
+ max="23"
/>
- </gl-datepicker>
- </gl-form-group>
- <div class="gl-display-flex gl-mt-5">
- <gl-form-group :label="__('Time')">
- <div class="gl-display-flex">
- <label label-for="timeline-input-hours" class="sr-only"></label>
- <gl-form-input
- id="timeline-input-hours"
- v-model="hourPickerInput"
- data-testid="input-hours"
- size="xs"
- type="number"
- min="00"
- max="23"
- />
- <label label-for="timeline-input-minutes" class="sr-only"></label>
- <gl-form-input
- id="timeline-input-minutes"
- v-model="minutePickerInput"
- class="gl-ml-3"
- data-testid="input-minutes"
- size="xs"
- type="number"
- min="00"
- max="59"
- />
- </div>
- </gl-form-group>
- <p class="gl-ml-3 gl-align-self-end gl-line-height-32">{{ __('UTC') }}</p>
- </div>
- </div>
- <div class="common-note-form">
- <gl-form-group class="gl-mb-3" :label="$options.i18n.areaLabel">
- <markdown-field
- :can-attach-file="false"
- :add-spacing-classes="false"
- :show-comment-tool-bar="false"
- :textarea-value="timelineText"
- :restricted-tool-bar-items="$options.restrictedToolBarItems"
- markdown-docs-path=""
- :enable-preview="false"
- class="bordered-box gl-mt-0"
- >
- <template #textarea>
- <textarea
- v-model="timelineText"
- class="note-textarea js-gfm-input js-autosize markdown-area"
- data-testid="input-note"
- dir="auto"
- data-supports-quick-actions="false"
- :aria-label="$options.i18n.description"
- :placeholder="$options.i18n.areaPlaceholder"
- >
- </textarea>
- </template>
- </markdown-field>
+ <label label-for="timeline-input-minutes" class="sr-only"></label>
+ <gl-form-input
+ id="timeline-input-minutes"
+ v-model="minutePickerInput"
+ class="gl-ml-3"
+ data-testid="input-minutes"
+ size="xs"
+ type="number"
+ min="00"
+ max="59"
+ />
+ </div>
</gl-form-group>
+ <p class="gl-ml-3 gl-align-self-end gl-line-height-32">{{ __('UTC') }}</p>
</div>
- <gl-form-group class="gl-mb-0">
- <gl-button
- variant="confirm"
- category="primary"
- class="gl-mr-3"
- :loading="isEventProcessed"
- @click="handleSave(false)"
- >
- {{ $options.i18n.save }}
- </gl-button>
- <gl-button
- variant="confirm"
- category="secondary"
- class="gl-mr-3 gl-ml-n2"
- :loading="isEventProcessed"
- @click="handleSave(true)"
+ </div>
+ <div class="common-note-form">
+ <gl-form-group class="gl-mb-3" :label="$options.i18n.areaLabel">
+ <markdown-field
+ :can-attach-file="false"
+ :add-spacing-classes="false"
+ :show-comment-tool-bar="false"
+ :textarea-value="timelineText"
+ :restricted-tool-bar-items="$options.restrictedToolBarItems"
+ markdown-docs-path=""
+ :enable-preview="false"
+ class="bordered-box gl-mt-0"
>
- {{ $options.i18n.saveAndAdd }}
- </gl-button>
- <gl-button class="gl-ml-n2" :disabled="isEventProcessed" @click="$emit('cancel')">
- {{ $options.i18n.cancel }}
- </gl-button>
- <div class="gl-border-b gl-pt-5"></div>
+ <template #textarea>
+ <textarea
+ v-model="timelineText"
+ class="note-textarea js-gfm-input js-autosize markdown-area"
+ data-testid="input-note"
+ dir="auto"
+ data-supports-quick-actions="false"
+ :aria-label="$options.i18n.description"
+ :placeholder="$options.i18n.areaPlaceholder"
+ >
+ </textarea>
+ </template>
+ </markdown-field>
</gl-form-group>
- </form>
- </div>
+ </div>
+ <gl-form-group class="gl-mb-0">
+ <gl-button
+ variant="confirm"
+ category="primary"
+ class="gl-mr-3"
+ :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"
+ :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="timeline-event-bottom-border"></div>
+ </gl-form-group>
+ </form>
</template>
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 6175c9969ec..cbf3c387fa3 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,25 +1,13 @@
<script>
-import {
- GlButton,
- GlDropdown,
- GlDropdownItem,
- GlIcon,
- GlSafeHtmlDirective,
- GlSprintf,
-} from '@gitlab/ui';
+import { GlDropdown, GlDropdownItem, GlIcon, GlSafeHtmlDirective, GlSprintf } from '@gitlab/ui';
import { formatDate } from '~/lib/utils/datetime_utility';
-import { __ } from '~/locale';
+import { timelineItemI18n } from './constants';
import { getEventIcon } from './utils';
export default {
name: 'IncidentTimelineEventListItem',
- i18n: {
- delete: __('Delete'),
- moreActions: __('More actions'),
- timeUTC: __('%{time} UTC'),
- },
+ i18n: timelineItemI18n,
components: {
- GlButton,
GlDropdown,
GlDropdownItem,
GlIcon,
@@ -28,12 +16,8 @@ export default {
directives: {
SafeHtml: GlSafeHtmlDirective,
},
- inject: ['canUpdate'],
+ inject: ['canUpdateTimelineEvent'],
props: {
- isLastItem: {
- type: Boolean,
- required: true,
- },
occurredAt: {
type: String,
required: true,
@@ -58,43 +42,41 @@ export default {
};
</script>
<template>
- <li
- class="timeline-entry timeline-entry-vertical-line note system-note note-wrapper gl-my-2! gl-pr-0!"
- >
- <div class="gl-display-flex gl-align-items-center">
- <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-n2 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 class="gl-display-flex gl-align-items-start">
+ <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>
+ <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>
</div>
- <div
- class="timeline-event-note gl-w-full gl-display-flex gl-flex-direction-row"
- :class="{ 'gl-pb-3 gl-border-gray-50 gl-border-1 gl-border-b-solid': !isLastItem }"
- data-testid="event-text-container"
+ <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
>
- <div>
- <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>
- </div>
- <gl-dropdown
- v-if="canUpdate"
- right
- class="event-note-actions gl-ml-auto gl-align-self-center"
- icon="ellipsis_v"
- text-sr-only
- :text="$options.i18n.moreActions"
- category="tertiary"
- no-caret
- >
- <gl-dropdown-item @click="$emit('delete')">
- <gl-button>{{ $options.i18n.delete }}</gl-button>
- </gl-dropdown-item>
- </gl-dropdown>
- </div>
+ <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>
- </li>
+ </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 80ac1c372cd..321b7ccc14a 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
@@ -5,7 +5,9 @@ import { sprintf } from '~/locale';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
import { ignoreWhilePending } from '~/lib/utils/ignore_while_pending';
import IncidentTimelineEventItem from './timeline_events_item.vue';
+import EditTimelineEvent from './edit_timeline_event.vue';
import deleteTimelineEvent from './graphql/queries/delete_timeline_event.mutation.graphql';
+import editTimelineEvent from './graphql/queries/edit_timeline_event.mutation.graphql';
import { timelineListI18n } from './constants';
export default {
@@ -13,6 +15,7 @@ export default {
i18n: timelineListI18n,
components: {
IncidentTimelineEventItem,
+ EditTimelineEvent,
},
props: {
timelineEventLoading: {
@@ -26,6 +29,9 @@ export default {
default: () => [],
},
},
+ data() {
+ return { eventToEdit: null, editTimelineEventActive: false };
+ },
computed: {
dateGroupedEvents() {
const groupedEvents = new Map();
@@ -44,11 +50,12 @@ export default {
},
},
methods: {
- isLastItem(groups, groupIndex, events, eventIndex) {
- if (groupIndex < groups.size - 1) {
- return false;
- }
- return eventIndex === events.length - 1;
+ handleEditSelection(event) {
+ this.eventToEdit = event.id;
+ this.$emit('hide-new-incident-timeline-event-form');
+ },
+ hideEdit() {
+ this.eventToEdit = null;
},
handleDelete: ignoreWhilePending(async function handleDelete(event) {
const msg = this.$options.i18n.deleteModal;
@@ -85,6 +92,38 @@ export default {
createAlert({ message: this.$options.i18n.deleteErrorGeneric, captureError: true, error });
}
}),
+ handleSaveEdit(eventDetails) {
+ this.editTimelineEventActive = true;
+ return this.$apollo
+ .mutate({
+ mutation: editTimelineEvent,
+ variables: {
+ input: {
+ id: eventDetails.id,
+ note: eventDetails.note,
+ occurredAt: eventDetails.occurredAt,
+ },
+ },
+ })
+ .then(({ data }) => {
+ this.editTimelineEventActive = false;
+ const errors = data.timelineEventUpdate?.errors;
+ if (errors.length) {
+ createAlert({
+ message: sprintf(this.$options.i18n.editError, { error: errors.join('. ') }, false),
+ });
+ } else {
+ this.hideEdit();
+ }
+ })
+ .catch((error) => {
+ createAlert({
+ message: this.$options.i18n.editErrorGeneric,
+ captureError: true,
+ error,
+ });
+ });
+ },
},
};
</script>
@@ -92,9 +131,10 @@ export default {
<template>
<div class="issuable-discussion incident-timeline-events">
<div
- v-for="([eventDate, events], groupIndex) in dateGroupedEvents"
+ v-for="[eventDate, events] in dateGroupedEvents"
:key="eventDate"
data-testid="timeline-group"
+ class="timeline-group"
>
<div class="gl-pb-3 gl-border-gray-50 gl-border-1 gl-border-b-solid">
<strong class="gl-font-size-h2" data-testid="event-date">{{ eventDate }}</strong>
@@ -103,15 +143,25 @@ export default {
<li
v-for="(event, eventIndex) in events"
:key="eventIndex"
- class="timeline-entry-vertical-line note system-note note-wrapper gl-my-2! gl-pr-0!"
+ class="timeline-entry-vertical-line timeline-entry note system-note note-wrapper gl-my-2! gl-pr-0!"
>
+ <edit-timeline-event
+ v-if="eventToEdit === event.id"
+ :key="`edit-${event.id}`"
+ ref="eventForm"
+ :event="event"
+ :edit-timeline-event-active="editTimelineEventActive"
+ @handle-save-edit="handleSaveEdit"
+ @hide-edit="hideEdit()"
+ />
<incident-timeline-event-item
+ v-else
:key="event.id"
:action="event.action"
:occurred-at="event.occurredAt"
:note-html="event.noteHtml"
- :is-last-item="isLastItem(dateGroupedEvents, groupIndex, events, eventIndex)"
@delete="handleDelete(event)"
+ @edit="handleEditSelection(event)"
/>
</li>
</ul>
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 7c2a7878c58..5f70d9acac9 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
@@ -3,10 +3,10 @@ import { GlButton, GlEmptyState, GlLoadingIcon, GlTab } from '@gitlab/ui';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { TYPE_ISSUE } from '~/graphql_shared/constants';
import { fetchPolicies } from '~/lib/graphql';
+import notesEventHub from '~/notes/event_hub';
import getTimelineEvents from './graphql/queries/get_timeline_events.query.graphql';
import { displayAndLogError } from './utils';
import { timelineTabI18n } from './constants';
-
import CreateTimelineEvent from './create_timeline_event.vue';
import IncidentTimelineEventsList from './timeline_events_list.vue';
@@ -20,7 +20,7 @@ export default {
IncidentTimelineEventsList,
},
i18n: timelineTabI18n,
- inject: ['canUpdate', 'fullPath', 'issuableId'],
+ inject: ['canUpdateTimelineEvent', 'fullPath', 'issuableId'],
data() {
return {
isEventFormVisible: false,
@@ -56,15 +56,21 @@ export default {
return !this.timelineEventLoading && !this.hasTimelineEvents;
},
},
+ mounted() {
+ notesEventHub.$on('comment-promoted-to-timeline-event', this.refreshTimelineEvents);
+ },
+ destroyed() {
+ notesEventHub.$off('comment-promoted-to-timeline-event', this.refreshTimelineEvents);
+ },
methods: {
+ refreshTimelineEvents() {
+ this.$apollo.queries.timelineEvents.refetch();
+ },
hideEventForm() {
this.isEventFormVisible = false;
},
- async showEventForm() {
- this.$refs.createEventForm.clearForm();
+ showEventForm() {
this.isEventFormVisible = true;
- await this.$nextTick();
- this.$refs.createEventForm.focusDate();
},
},
};
@@ -85,14 +91,19 @@ export default {
@hide-new-timeline-events-form="hideEventForm"
/>
<create-timeline-event
- v-show="isEventFormVisible"
+ v-if="isEventFormVisible"
ref="createEventForm"
:has-timeline-events="hasTimelineEvents"
class="timeline-event-note timeline-event-note-form"
:class="{ 'gl-pl-0': !hasTimelineEvents }"
@hide-new-timeline-events-form="hideEventForm"
/>
- <gl-button v-if="canUpdate" variant="default" class="gl-mb-3 gl-mt-7" @click="showEventForm">
+ <gl-button
+ v-if="canUpdateTimelineEvent"
+ variant="default"
+ class="gl-mb-3 gl-mt-7"
+ @click="showEventForm"
+ >
{{ $options.i18n.addEventButton }}
</gl-button>
</gl-tab>
diff --git a/app/assets/javascripts/issues/show/components/incidents/utils.js b/app/assets/javascripts/issues/show/components/incidents/utils.js
index cf790a11b67..5a009debd75 100644
--- a/app/assets/javascripts/issues/show/components/incidents/utils.js
+++ b/app/assets/javascripts/issues/show/components/incidents/utils.js
@@ -21,13 +21,14 @@ export const getEventIcon = (actionName) => {
};
/**
- * Returns a date shifted by the current timezone offset. Allows
- * date.getHours() and similar to return UTC values.
- *
+ * Returns a date shifted by the current timezone offset set to now
+ * by default but can accept an existing date as an ISO date string
+ * @param {string} ISOString
* @returns {Date}
*/
-export const getUtcShiftedDateNow = () => {
- const date = new Date();
+export const getUtcShiftedDate = (ISOString = null) => {
+ const date = ISOString ? new Date(ISOString) : new Date();
date.setMinutes(date.getMinutes() + date.getTimezoneOffset());
+
return date;
};
diff --git a/app/assets/javascripts/issues/show/graphql.js b/app/assets/javascripts/issues/show/graphql.js
index 5b8630f7d63..deee034f9d1 100644
--- a/app/assets/javascripts/issues/show/graphql.js
+++ b/app/assets/javascripts/issues/show/graphql.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
-import { defaultClient } from '~/sidebar/graphql';
+import { defaultClient } from '~/graphql_shared/issuable_client';
Vue.use(VueApollo);
diff --git a/app/assets/javascripts/issues/show/index.js b/app/assets/javascripts/issues/show/index.js
index 459a3804837..e5eed9f6b79 100644
--- a/app/assets/javascripts/issues/show/index.js
+++ b/app/assets/javascripts/issues/show/index.js
@@ -32,6 +32,7 @@ export function initIncidentApp(issueData = {}) {
const {
canCreateIncident,
canUpdate,
+ canUpdateTimelineEvent,
iid,
issuableId,
projectNamespace,
@@ -51,6 +52,7 @@ export function initIncidentApp(issueData = {}) {
provide: {
issueType: INCIDENT_TYPE,
canCreateIncident,
+ canUpdateTimelineEvent,
canUpdate,
fullPath,
iid,
diff --git a/app/assets/javascripts/issues/show/utils/update_description.js b/app/assets/javascripts/issues/show/utils/update_description.js
index c5811290e61..aeb547b9194 100644
--- a/app/assets/javascripts/issues/show/utils/update_description.js
+++ b/app/assets/javascripts/issues/show/utils/update_description.js
@@ -13,6 +13,7 @@ const updateDescription = (descriptionHtml = '', details) => {
}
const placeholder = document.createElement('div');
+ // eslint-disable-next-line no-unsanitized/property
placeholder.innerHTML = descriptionHtml;
const newDetails = placeholder.getElementsByTagName('details');
diff --git a/app/assets/javascripts/jira_connect/subscriptions/api.js b/app/assets/javascripts/jira_connect/subscriptions/api.js
index de67703356f..c79d7002111 100644
--- a/app/assets/javascripts/jira_connect/subscriptions/api.js
+++ b/app/assets/javascripts/jira_connect/subscriptions/api.js
@@ -1,10 +1,25 @@
import axios from 'axios';
+import { buildApiUrl } from '~/api/api_utils';
+
+import { GITLAB_COM_BASE_PATH } from '~/jira_connect/subscriptions/constants';
import { getJwt } from './utils';
+const CURRENT_USER_PATH = '/api/:version/user';
+const JIRA_CONNECT_SUBSCRIPTIONS_PATH = '/api/:version/integrations/jira_connect/subscriptions';
+const JIRA_CONNECT_INSTALLATIONS_PATH = '/-/jira_connect/installations';
+const JIRA_CONNECT_OAUTH_APPLICATION_ID_PATH = '/-/jira_connect/oauth_application_id';
+
+// This export is only used for testing purposes
+export const axiosInstance = axios.create();
+
+export const setApiBaseURL = (baseURL = null) => {
+ axiosInstance.defaults.baseURL = baseURL;
+};
+
export const addSubscription = async (addPath, namespace) => {
const jwt = await getJwt();
- return axios.post(addPath, {
+ return axiosInstance.post(addPath, {
jwt,
namespace_path: namespace,
});
@@ -13,7 +28,7 @@ export const addSubscription = async (addPath, namespace) => {
export const removeSubscription = async (removePath) => {
const jwt = await getJwt();
- return axios.delete(removePath, {
+ return axiosInstance.delete(removePath, {
params: {
jwt,
},
@@ -21,7 +36,7 @@ export const removeSubscription = async (removePath) => {
};
export const fetchGroups = async (groupsPath, { page, perPage, search }) => {
- return axios.get(groupsPath, {
+ return axiosInstance.get(groupsPath, {
params: {
page,
per_page: perPage,
@@ -33,9 +48,50 @@ export const fetchGroups = async (groupsPath, { page, perPage, search }) => {
export const fetchSubscriptions = async (subscriptionsPath) => {
const jwt = await getJwt();
- return axios.get(subscriptionsPath, {
+ return axiosInstance.get(subscriptionsPath, {
params: {
jwt,
},
});
};
+
+export const getCurrentUser = (options) => {
+ const url = buildApiUrl(CURRENT_USER_PATH);
+ return axiosInstance.get(url, { ...options });
+};
+
+export const addJiraConnectSubscription = (namespacePath, { jwt, accessToken }) => {
+ const url = buildApiUrl(JIRA_CONNECT_SUBSCRIPTIONS_PATH);
+
+ return axiosInstance.post(
+ url,
+ {
+ jwt,
+ namespace_path: namespacePath,
+ },
+ {
+ headers: {
+ Authorization: `Bearer ${accessToken}`,
+ },
+ },
+ );
+};
+
+export const updateInstallation = async (instanceUrl) => {
+ const jwt = await getJwt();
+
+ return axiosInstance.put(JIRA_CONNECT_INSTALLATIONS_PATH, {
+ jwt,
+ installation: {
+ instance_url: instanceUrl === GITLAB_COM_BASE_PATH ? null : instanceUrl,
+ },
+ });
+};
+
+export const fetchOAuthApplicationId = () => {
+ return axiosInstance.get(JIRA_CONNECT_OAUTH_APPLICATION_ID_PATH);
+};
+
+export const fetchOAuthToken = (oauthTokenPath, data = {}) => {
+ return axiosInstance.post(oauthTokenPath, data);
+};
diff --git a/app/assets/javascripts/jira_connect/subscriptions/components/app.vue b/app/assets/javascripts/jira_connect/subscriptions/components/app.vue
index 66aea60c5b5..22a6c0751f4 100644
--- a/app/assets/javascripts/jira_connect/subscriptions/components/app.vue
+++ b/app/assets/javascripts/jira_connect/subscriptions/components/app.vue
@@ -83,7 +83,7 @@ export default {
* if the jiraConnectOauth flag is enabled.
*/
fetchSubscriptionsOauth() {
- if (!this.isOauthEnabled) return;
+ if (!this.isOauthEnabled || !this.userSignedIn) return;
this.fetchSubscriptions(this.subscriptionsPath);
},
@@ -146,12 +146,12 @@ export default {
<div class="gl-layout-w-limited gl-mx-auto gl-px-5 gl-mb-7">
<sign-in-page
- v-if="!userSignedIn"
+ v-show="!userSignedIn"
:has-subscriptions="hasSubscriptions"
@sign-in-oauth="onSignInOauth"
@error="onSignInError"
/>
- <subscriptions-page v-else :has-subscriptions="hasSubscriptions" />
+ <subscriptions-page v-if="userSignedIn" :has-subscriptions="hasSubscriptions" />
</div>
</div>
</main>
diff --git a/app/assets/javascripts/jira_connect/subscriptions/components/compatibility_alert.vue b/app/assets/javascripts/jira_connect/subscriptions/components/compatibility_alert.vue
index c5b56535247..9b50681515e 100644
--- a/app/assets/javascripts/jira_connect/subscriptions/components/compatibility_alert.vue
+++ b/app/assets/javascripts/jira_connect/subscriptions/components/compatibility_alert.vue
@@ -3,6 +3,7 @@ import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
import { s__ } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
const COMPATIBILITY_ALERT_STATE_KEY = 'compatibility_alert_dismissed';
@@ -14,6 +15,7 @@ export default {
GlLink,
LocalStorageSync,
},
+ mixins: [glFeatureFlagMixin()],
data() {
return {
alertDismissed: false,
@@ -23,6 +25,14 @@ export default {
shouldShowAlert() {
return !this.alertDismissed;
},
+ isOauthSelfManagedEnabled() {
+ return this.glFeatures.jiraConnectOauth && this.glFeatures.jiraConnectOauthSelfManaged;
+ },
+ alertBody() {
+ return this.isOauthSelfManagedEnabled
+ ? this.$options.i18n.body
+ : this.$options.i18n.bodyDotCom;
+ },
},
methods: {
dismissAlert() {
@@ -32,6 +42,9 @@ export default {
i18n: {
title: s__('Integrations|Known limitations'),
body: s__(
+ 'Integrations|Adding a namespace only works in browsers that allow cross-site cookies. %{linkStart}Learn more%{linkEnd}.',
+ ),
+ bodyDotCom: s__(
'Integrations|This integration only works with GitLab.com. Adding a namespace only works in browsers that allow cross-site cookies. %{linkStart}Learn more%{linkEnd}.',
),
},
@@ -50,7 +63,7 @@ export default {
:title="$options.i18n.title"
@dismiss="dismissAlert"
>
- <gl-sprintf :message="$options.i18n.body">
+ <gl-sprintf :message="alertBody">
<template #link="{ content }">
<gl-link :href="$options.DOCS_LINK_URL" target="_blank">{{ content }}</gl-link>
</template>
diff --git a/app/assets/javascripts/jira_connect/subscriptions/components/sign_in_oauth_button.vue b/app/assets/javascripts/jira_connect/subscriptions/components/sign_in_oauth_button.vue
index ad3e70bcb5f..4cf3a1a0279 100644
--- a/app/assets/javascripts/jira_connect/subscriptions/components/sign_in_oauth_button.vue
+++ b/app/assets/javascripts/jira_connect/subscriptions/components/sign_in_oauth_button.vue
@@ -1,30 +1,53 @@
<script>
import { mapActions, mapMutations } from 'vuex';
import { GlButton } from '@gitlab/ui';
-import axios from '~/lib/utils/axios_utils';
+import { sprintf } from '~/locale';
+
import {
I18N_DEFAULT_SIGN_IN_BUTTON_TEXT,
+ I18N_CUSTOM_SIGN_IN_BUTTON_TEXT,
+ I18N_OAUTH_APPLICATION_ID_ERROR_MESSAGE,
+ I18N_OAUTH_FAILED_TITLE,
+ I18N_OAUTH_FAILED_MESSAGE,
+ OAUTH_SELF_MANAGED_DOC_LINK,
OAUTH_WINDOW_OPTIONS,
PKCE_CODE_CHALLENGE_DIGEST_ALGORITHM,
} from '~/jira_connect/subscriptions/constants';
+import { fetchOAuthApplicationId, fetchOAuthToken } from '~/jira_connect/subscriptions/api';
import { setUrlParams } from '~/lib/utils/url_utility';
import AccessorUtilities from '~/lib/utils/accessor';
import { createCodeVerifier, createCodeChallenge } from '../pkce';
-import { SET_ACCESS_TOKEN } from '../store/mutation_types';
+import { SET_ACCESS_TOKEN, SET_ALERT } from '../store/mutation_types';
export default {
components: {
GlButton,
},
inject: ['oauthMetadata'],
+ props: {
+ gitlabBasePath: {
+ type: String,
+ required: false,
+ default: undefined,
+ },
+ },
data() {
return {
- token: null,
loading: false,
codeVerifier: null,
+ clientId: null,
canUseCrypto: AccessorUtilities.canUseCrypto(),
};
},
+ computed: {
+ buttonText() {
+ if (!this.gitlabBasePath) {
+ return I18N_DEFAULT_SIGN_IN_BUTTON_TEXT;
+ }
+
+ return sprintf(I18N_CUSTOM_SIGN_IN_BUTTON_TEXT, { url: this.gitlabBasePath });
+ },
+ },
created() {
window.addEventListener('message', this.handleWindowMessage);
},
@@ -35,30 +58,72 @@ export default {
...mapActions(['loadCurrentUser']),
...mapMutations({
setAccessToken: SET_ACCESS_TOKEN,
+ setAlert: SET_ALERT,
}),
- async startOAuthFlow() {
- this.loading = true;
-
+ async fetchOauthClientId() {
+ const {
+ data: { application_id: clientId },
+ } = await fetchOAuthApplicationId();
+ return clientId;
+ },
+ async getOauthAuthorizeURL() {
// Generate state necessary for PKCE OAuth flow
this.codeVerifier = createCodeVerifier();
const codeChallenge = await createCodeChallenge(this.codeVerifier);
+ try {
+ this.clientId = this.gitlabBasePath
+ ? await this.fetchOauthClientId()
+ : this.oauthMetadata?.oauth_token_payload?.client_id;
+ } catch {
+ throw new Error(I18N_OAUTH_APPLICATION_ID_ERROR_MESSAGE);
+ }
// Build the initial OAuth authorization URL
const { oauth_authorize_url: oauthAuthorizeURL } = this.oauthMetadata;
-
- const oauthAuthorizeURLWithChallenge = setUrlParams(
- {
- code_challenge: codeChallenge,
- code_challenge_method: PKCE_CODE_CHALLENGE_DIGEST_ALGORITHM.short,
- },
- oauthAuthorizeURL,
+ const oauthAuthorizeURLWithChallenge = new URL(
+ setUrlParams(
+ {
+ code_challenge: codeChallenge,
+ code_challenge_method: PKCE_CODE_CHALLENGE_DIGEST_ALGORITHM.short,
+ client_id: this.clientId,
+ },
+ oauthAuthorizeURL,
+ ),
);
- window.open(
- oauthAuthorizeURLWithChallenge,
- this.$options.i18n.defaultButtonText,
- OAUTH_WINDOW_OPTIONS,
- );
+ // Rebase URL on the specified GitLab base path (if specified).
+ if (this.gitlabBasePath) {
+ const gitlabBasePathURL = new URL(this.gitlabBasePath);
+ oauthAuthorizeURLWithChallenge.hostname = gitlabBasePathURL.hostname;
+ oauthAuthorizeURLWithChallenge.pathname = `${
+ gitlabBasePathURL.pathname === '/' ? '' : gitlabBasePathURL.pathname
+ }${oauthAuthorizeURLWithChallenge.pathname}`;
+ }
+
+ return oauthAuthorizeURLWithChallenge.toString();
+ },
+ async startOAuthFlow() {
+ try {
+ this.loading = true;
+ const oauthAuthorizeURL = await this.getOauthAuthorizeURL();
+
+ window.open(oauthAuthorizeURL, I18N_DEFAULT_SIGN_IN_BUTTON_TEXT, OAUTH_WINDOW_OPTIONS);
+ } catch (e) {
+ if (e.message) {
+ this.setAlert({
+ message: e.message,
+ variant: 'danger',
+ });
+ } else {
+ this.setAlert({
+ linkUrl: OAUTH_SELF_MANAGED_DOC_LINK,
+ title: I18N_OAUTH_FAILED_TITLE,
+ message: this.gitlabBasePath ? I18N_OAUTH_FAILED_MESSAGE : '',
+ variant: 'danger',
+ });
+ }
+ this.loading = false;
+ }
},
async handleWindowMessage(event) {
if (window.origin !== event.origin) {
@@ -94,20 +159,18 @@ export default {
async getOAuthToken(code) {
const {
oauth_token_payload: oauthTokenPayload,
- oauth_token_url: oauthTokenURL,
+ oauth_token_path: oauthTokenPath,
} = this.oauthMetadata;
- const { data } = await axios.post(oauthTokenURL, {
+ const { data } = await fetchOAuthToken(oauthTokenPath, {
...oauthTokenPayload,
code,
code_verifier: this.codeVerifier,
+ client_id: this.clientId,
});
return data.access_token;
},
},
- i18n: {
- defaultButtonText: I18N_DEFAULT_SIGN_IN_BUTTON_TEXT,
- },
};
</script>
<template>
@@ -119,7 +182,7 @@ export default {
@click="startOAuthFlow"
>
<slot>
- {{ $options.i18n.defaultButtonText }}
+ {{ buttonText }}
</slot>
</gl-button>
</template>
diff --git a/app/assets/javascripts/jira_connect/subscriptions/constants.js b/app/assets/javascripts/jira_connect/subscriptions/constants.js
index 8faafb1b0d0..fc365746b54 100644
--- a/app/assets/javascripts/jira_connect/subscriptions/constants.js
+++ b/app/assets/javascripts/jira_connect/subscriptions/constants.js
@@ -3,11 +3,13 @@ import { helpPagePath } from '~/helpers/help_page_helper';
export const DEFAULT_GROUPS_PER_PAGE = 10;
export const ALERT_LOCALSTORAGE_KEY = 'gitlab_alert';
+export const BASE_URL_LOCALSTORAGE_KEY = 'gitlab_base_url';
export const MINIMUM_SEARCH_TERM_LENGTH = 3;
export const ADD_NAMESPACE_MODAL_ID = 'add-namespace-modal';
export const I18N_DEFAULT_SIGN_IN_BUTTON_TEXT = s__('Integrations|Sign in to GitLab');
+export const I18N_CUSTOM_SIGN_IN_BUTTON_TEXT = s__('Integrations|Sign in to %{url}');
export const I18N_DEFAULT_SIGN_IN_ERROR_MESSAGE = s__('Integrations|Failed to sign in to GitLab.');
export const I18N_DEFAULT_SUBSCRIPTIONS_ERROR_MESSAGE = s__(
'Integrations|Failed to load subscriptions.',
@@ -18,13 +20,28 @@ export const I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_TITLE = s__(
export const I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_MESSAGE = s__(
'Integrations|You should now see GitLab.com activity inside your Jira Cloud issues. %{linkStart}Learn more%{linkEnd}',
);
-export const INTEGRATIONS_DOC_LINK = helpPagePath('integration/jira_development_panel', {
- anchor: 'use-the-integration',
-});
-
export const I18N_ADD_SUBSCRIPTIONS_ERROR_MESSAGE = s__(
'Integrations|Failed to link namespace. Please try again.',
);
+export const I18N_UPDATE_INSTALLATION_ERROR_MESSAGE = s__(
+ 'Integrations|Failed to update GitLab version. Please try again.',
+);
+export const I18N_OAUTH_APPLICATION_ID_ERROR_MESSAGE = s__(
+ 'Integrations|Failed to load Jira Connect Application ID. Please try again.',
+);
+export const I18N_OAUTH_FAILED_TITLE = s__('Integrations|Failed to sign in to GitLab.');
+export const I18N_OAUTH_FAILED_MESSAGE = s__(
+ 'Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}.',
+);
+
+export const INTEGRATIONS_DOC_LINK = helpPagePath('integration/jira/development_panel', {
+ 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',
+});
+
+export const GITLAB_COM_BASE_PATH = 'https://gitlab.com';
const OAUTH_WINDOW_SIZE = 800;
export const OAUTH_WINDOW_OPTIONS = [
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 4f5aa4c255c..5ff75e19425 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
@@ -1,6 +1,13 @@
<script>
+import { mapMutations } from 'vuex';
import { GlButton } from '@gitlab/ui';
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 { SET_ALERT } from '~/jira_connect/subscriptions/store/mutation_types';
+
import SignInOauthButton from '../../../components/sign_in_oauth_button.vue';
import VersionSelectForm from './version_select_form.vue';
@@ -14,6 +21,7 @@ export default {
data() {
return {
gitlabBasePath: null,
+ loadingVersionSelect: false,
};
},
computed: {
@@ -26,12 +34,32 @@ export default {
: this.$options.i18n.versionSelectSubtitle;
},
},
+ mounted() {
+ this.gitlabBasePath = retrieveBaseUrl();
+ setApiBaseURL(this.gitlabBasePath);
+ },
methods: {
+ ...mapMutations({
+ setAlert: SET_ALERT,
+ }),
resetGitlabBasePath() {
this.gitlabBasePath = null;
+ setApiBaseURL();
},
onVersionSelect(gitlabBasePath) {
- this.gitlabBasePath = gitlabBasePath;
+ this.loadingVersionSelect = true;
+ updateInstallation(gitlabBasePath)
+ .then(() => {
+ persistBaseUrl(gitlabBasePath);
+ reloadPage();
+ })
+ .catch(() => {
+ this.setAlert({
+ message: I18N_UPDATE_INSTALLATION_ERROR_MESSAGE,
+ variant: 'danger',
+ });
+ this.loadingVersionSelect = false;
+ });
},
onSignInError() {
this.$emit('error');
@@ -53,11 +81,17 @@ export default {
<p data-testid="subtitle">{{ subtitle }}</p>
</div>
- <version-select-form v-if="!hasSelectedVersion" class="gl-mt-7" @submit="onVersionSelect" />
+ <version-select-form
+ v-if="!hasSelectedVersion"
+ class="gl-mt-7"
+ :loading="loadingVersionSelect"
+ @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"
/>
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 0fa745ed7e3..6b32225ed11 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
@@ -9,13 +9,14 @@ import {
} from '@gitlab/ui';
import { __, s__ } from '~/locale';
+import { GITLAB_COM_BASE_PATH } from '~/jira_connect/subscriptions/constants';
+
const RADIO_OPTIONS = {
saas: 'saas',
selfManaged: 'selfManaged',
};
const DEFAULT_RADIO_OPTION = RADIO_OPTIONS.saas;
-const GITLAB_COM_BASE_PATH = 'https://gitlab.com';
export default {
name: 'VersionSelectForm',
@@ -27,6 +28,13 @@ export default {
GlFormRadio,
GlButton,
},
+ props: {
+ loading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
data() {
return {
selected: DEFAULT_RADIO_OPTION,
@@ -82,7 +90,7 @@ export default {
</gl-form-group>
<div class="gl-display-flex gl-justify-content-end">
- <gl-button variant="confirm" type="submit">{{ __('Save') }}</gl-button>
+ <gl-button variant="confirm" type="submit" :loading="loading">{{ __('Save') }}</gl-button>
</div>
</gl-form>
</template>
diff --git a/app/assets/javascripts/jira_connect/subscriptions/store/actions.js b/app/assets/javascripts/jira_connect/subscriptions/store/actions.js
index 4a83ee8671d..fff34e1d75d 100644
--- a/app/assets/javascripts/jira_connect/subscriptions/store/actions.js
+++ b/app/assets/javascripts/jira_connect/subscriptions/store/actions.js
@@ -1,6 +1,8 @@
-import { fetchSubscriptions as fetchSubscriptionsREST } from '~/jira_connect/subscriptions/api';
-import { getCurrentUser } from '~/rest_api';
-import { addJiraConnectSubscription } from '~/api/integrations_api';
+import {
+ fetchSubscriptions as fetchSubscriptionsREST,
+ getCurrentUser,
+ addJiraConnectSubscription,
+} from '~/jira_connect/subscriptions/api';
import {
I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_TITLE,
I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_MESSAGE,
diff --git a/app/assets/javascripts/jira_connect/subscriptions/store/state.js b/app/assets/javascripts/jira_connect/subscriptions/store/state.js
index 03a83f18b4c..82a8517b511 100644
--- a/app/assets/javascripts/jira_connect/subscriptions/store/state.js
+++ b/app/assets/javascripts/jira_connect/subscriptions/store/state.js
@@ -1,4 +1,8 @@
-export default function createState({ subscriptions = [], subscriptionsLoading = false } = {}) {
+export default function createState({
+ subscriptions = [],
+ subscriptionsLoading = false,
+ currentUser = null,
+} = {}) {
return {
alert: undefined,
@@ -9,7 +13,7 @@ export default function createState({ subscriptions = [], subscriptionsLoading =
addSubscriptionLoading: false,
addSubscriptionError: false,
- currentUser: null,
+ currentUser,
currentUserError: null,
accessToken: null,
diff --git a/app/assets/javascripts/jira_connect/subscriptions/utils.js b/app/assets/javascripts/jira_connect/subscriptions/utils.js
index b2d03a1fbba..6db8b62d692 100644
--- a/app/assets/javascripts/jira_connect/subscriptions/utils.js
+++ b/app/assets/javascripts/jira_connect/subscriptions/utils.js
@@ -1,32 +1,45 @@
import AccessorUtilities from '~/lib/utils/accessor';
import { objectToQuery } from '~/lib/utils/url_utility';
-import { ALERT_LOCALSTORAGE_KEY } from './constants';
+import { ALERT_LOCALSTORAGE_KEY, BASE_URL_LOCALSTORAGE_KEY } from './constants';
const isFunction = (fn) => typeof fn === 'function';
+const { canUseLocalStorage } = AccessorUtilities;
+
+const persistToStorage = (key, payload) => {
+ localStorage.setItem(key, payload);
+};
+
+const retrieveFromStorage = (key) => {
+ return localStorage.getItem(key);
+};
+
+const removeFromStorage = (key) => {
+ localStorage.removeItem(key);
+};
/**
* Persist alert data to localStorage.
*/
export const persistAlert = ({ title, message, linkUrl, variant } = {}) => {
- if (!AccessorUtilities.canUseLocalStorage()) {
+ if (!canUseLocalStorage()) {
return;
}
const payload = JSON.stringify({ title, message, linkUrl, variant });
- localStorage.setItem(ALERT_LOCALSTORAGE_KEY, payload);
+ persistToStorage(ALERT_LOCALSTORAGE_KEY, payload);
};
/**
* Return alert data from localStorage.
*/
export const retrieveAlert = () => {
- if (!AccessorUtilities.canUseLocalStorage()) {
+ if (!canUseLocalStorage()) {
return null;
}
- const initialAlertJSON = localStorage.getItem(ALERT_LOCALSTORAGE_KEY);
+ const initialAlertJSON = retrieveFromStorage(ALERT_LOCALSTORAGE_KEY);
// immediately clean up
- localStorage.removeItem(ALERT_LOCALSTORAGE_KEY);
+ removeFromStorage(ALERT_LOCALSTORAGE_KEY);
if (!initialAlertJSON) {
return null;
@@ -35,6 +48,22 @@ export const retrieveAlert = () => {
return JSON.parse(initialAlertJSON);
};
+export const persistBaseUrl = (baseUrl) => {
+ if (!canUseLocalStorage()) {
+ return;
+ }
+
+ persistToStorage(BASE_URL_LOCALSTORAGE_KEY, baseUrl);
+};
+
+export const retrieveBaseUrl = () => {
+ if (!canUseLocalStorage()) {
+ return null;
+ }
+
+ return retrieveFromStorage(BASE_URL_LOCALSTORAGE_KEY);
+};
+
export const getJwt = () => {
return new Promise((resolve) => {
if (isFunction(AP?.context?.getToken)) {
diff --git a/app/assets/javascripts/jobs/components/empty_state.vue b/app/assets/javascripts/jobs/components/empty_state.vue
deleted file mode 100644
index e31c13f40b0..00000000000
--- a/app/assets/javascripts/jobs/components/empty_state.vue
+++ /dev/null
@@ -1,87 +0,0 @@
-<script>
-import { GlLink } from '@gitlab/ui';
-import ManualVariablesForm from './manual_variables_form.vue';
-
-export default {
- components: {
- GlLink,
- ManualVariablesForm,
- },
- props: {
- illustrationPath: {
- type: String,
- required: true,
- },
- illustrationSizeClass: {
- type: String,
- required: true,
- },
- title: {
- type: String,
- required: true,
- },
- content: {
- type: String,
- required: false,
- default: null,
- },
- playable: {
- type: Boolean,
- required: true,
- default: false,
- },
- scheduled: {
- type: Boolean,
- required: false,
- default: false,
- },
- 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'))
- );
- },
- },
- },
- computed: {
- shouldRenderManualVariables() {
- return this.playable && !this.scheduled;
- },
- },
-};
-</script>
-<template>
- <div class="row empty-state">
- <div class="col-12">
- <div :class="illustrationSizeClass" class="svg-content">
- <img :src="illustrationPath" />
- </div>
- </div>
-
- <div class="col-12">
- <div class="text-content">
- <h4 class="text-center" data-testid="job-empty-state-title">{{ title }}</h4>
-
- <p v-if="content" data-testid="job-empty-state-content">{{ content }}</p>
- </div>
- <manual-variables-form v-if="shouldRenderManualVariables" :action="action" />
- <div class="text-content">
- <div v-if="action && !shouldRenderManualVariables" class="text-center">
- <gl-link
- :href="action.path"
- :data-method="action.method"
- class="btn gl-button btn-confirm gl-text-decoration-none!"
- data-testid="job-empty-state-action"
- >{{ action.button_title }}</gl-link
- >
- </div>
- </div>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/jobs/components/filtered_search/constants.js b/app/assets/javascripts/jobs/components/filtered_search/constants.js
new file mode 100644
index 00000000000..0daba892375
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/filtered_search/constants.js
@@ -0,0 +1,13 @@
+export const jobStatusValues = [
+ 'CANCELED',
+ 'CREATED',
+ 'FAILED',
+ 'MANUAL',
+ 'SUCCESS',
+ 'PENDING',
+ 'PREPARING',
+ 'RUNNING',
+ 'SCHEDULED',
+ 'SKIPPED',
+ 'WAITING_FOR_RESOURCE',
+];
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 fe7b7428c6e..e498a735898 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
@@ -11,6 +11,13 @@ export default {
components: {
GlFilteredSearch,
},
+ props: {
+ queryString: {
+ type: Object,
+ required: false,
+ default: null,
+ },
+ },
computed: {
tokens() {
return [
@@ -24,6 +31,20 @@ export default {
},
];
},
+ filteredSearchValue() {
+ if (this.queryString?.statuses) {
+ return [
+ {
+ type: 'status',
+ value: {
+ data: this.queryString?.statuses,
+ operator: '=',
+ },
+ },
+ ];
+ }
+ return [];
+ },
},
methods: {
onSubmit(filters) {
@@ -37,6 +58,7 @@ export default {
<gl-filtered-search
:placeholder="s__('Jobs|Filter jobs')"
:available-tokens="tokens"
+ :value="filteredSearchValue"
@submit="onSubmit"
/>
</template>
diff --git a/app/assets/javascripts/jobs/components/filtered_search/utils.js b/app/assets/javascripts/jobs/components/filtered_search/utils.js
new file mode 100644
index 00000000000..696cd8d4706
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/filtered_search/utils.js
@@ -0,0 +1,27 @@
+import { jobStatusValues } from './constants';
+
+// validates query string used for filtered search
+// on jobs table to ensure GraphQL query is called correctly
+export const validateQueryString = (queryStringObj) => {
+ // currently only one token is supported `statuses`
+ // this code will need to be expanded as more tokens
+ // are introduced
+
+ const filters = Object.keys(queryStringObj);
+
+ if (filters.includes('statuses')) {
+ const queryStringStatus = {
+ statuses: queryStringObj.statuses.toUpperCase(),
+ };
+
+ const found = jobStatusValues.find((status) => status === queryStringStatus.statuses);
+
+ if (found) {
+ return queryStringStatus;
+ }
+
+ return null;
+ }
+
+ return null;
+};
diff --git a/app/assets/javascripts/jobs/components/job/empty_state.vue b/app/assets/javascripts/jobs/components/job/empty_state.vue
new file mode 100644
index 00000000000..65b9600e664
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job/empty_state.vue
@@ -0,0 +1,99 @@
+<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,
+ required: true,
+ },
+ illustrationSizeClass: {
+ type: String,
+ required: true,
+ },
+ title: {
+ type: String,
+ required: true,
+ },
+ content: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ playable: {
+ type: Boolean,
+ required: true,
+ default: false,
+ },
+ scheduled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ 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'))
+ );
+ },
+ },
+ },
+ computed: {
+ isGraphQL() {
+ return this.glFeatures?.graphqlJobApp;
+ },
+ shouldRenderManualVariables() {
+ return this.playable && !this.scheduled;
+ },
+ },
+};
+</script>
+<template>
+ <div class="row empty-state">
+ <div class="col-12">
+ <div :class="illustrationSizeClass" class="svg-content">
+ <img :src="illustrationPath" />
+ </div>
+ </div>
+
+ <div class="col-12">
+ <div class="text-content">
+ <h4 class="text-center" data-testid="job-empty-state-title">{{ title }}</h4>
+
+ <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">
+ <gl-link
+ :href="action.path"
+ :data-method="action.method"
+ class="btn gl-button btn-confirm gl-text-decoration-none!"
+ data-testid="job-empty-state-action"
+ >{{ action.button_title }}</gl-link
+ >
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/components/environments_block.vue b/app/assets/javascripts/jobs/components/job/environments_block.vue
index 4046e1ade82..4046e1ade82 100644
--- a/app/assets/javascripts/jobs/components/environments_block.vue
+++ b/app/assets/javascripts/jobs/components/job/environments_block.vue
diff --git a/app/assets/javascripts/jobs/components/erased_block.vue b/app/assets/javascripts/jobs/components/job/erased_block.vue
index a815689659e..a815689659e 100644
--- a/app/assets/javascripts/jobs/components/erased_block.vue
+++ b/app/assets/javascripts/jobs/components/job/erased_block.vue
diff --git a/app/assets/javascripts/jobs/components/job/job_app.vue b/app/assets/javascripts/jobs/components/job/job_app.vue
new file mode 100644
index 00000000000..81b65d175a7
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job/job_app.vue
@@ -0,0 +1,328 @@
+<script>
+import { GlLoadingIcon, GlIcon, GlSafeHtmlDirective as SafeHtml, 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 { isScrolledToBottom } from '~/lib/utils/scroll_utils';
+import { __, sprintf } from '~/locale';
+import CiHeader from '~/vue_shared/components/header_ci_component.vue';
+import delayedJobMixin from '~/jobs/mixins/delayed_job_mixin';
+import Log from '~/jobs/components/log/log.vue';
+import EmptyState from './empty_state.vue';
+import EnvironmentsBlock from './environments_block.vue';
+import ErasedBlock from './erased_block.vue';
+import LogTopBar from './job_log_controllers.vue';
+import StuckBlock from './stuck_block.vue';
+import UnmetPrerequisitesBlock from './unmet_prerequisites_block.vue';
+import Sidebar from './sidebar/sidebar.vue';
+
+export default {
+ name: 'JobPageApp',
+ components: {
+ CiHeader,
+ EmptyState,
+ EnvironmentsBlock,
+ ErasedBlock,
+ GlIcon,
+ Log,
+ LogTopBar,
+ StuckBlock,
+ UnmetPrerequisitesBlock,
+ Sidebar,
+ GlLoadingIcon,
+ SharedRunner: () => import('ee_component/jobs/components/shared_runner_limit_block.vue'),
+ GlAlert,
+ },
+ directives: {
+ SafeHtml,
+ },
+ mixins: [delayedJobMixin],
+ props: {
+ artifactHelpUrl: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ runnerSettingsUrl: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ deploymentHelpUrl: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ terminalPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ projectPath: {
+ type: String,
+ required: true,
+ },
+ subscriptionsMoreMinutesUrl: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+ data() {
+ return {
+ searchResults: [],
+ };
+ },
+ computed: {
+ ...mapState([
+ 'isLoading',
+ 'job',
+ 'isSidebarOpen',
+ 'jobLog',
+ 'isJobLogComplete',
+ 'jobLogSize',
+ 'isJobLogSizeVisible',
+ 'isScrollBottomDisabled',
+ 'isScrollTopDisabled',
+ 'isScrolledToBottomBeforeReceivingJobLog',
+ 'hasError',
+ 'selectedStage',
+ ]),
+ ...mapGetters([
+ 'headerTime',
+ 'hasUnmetPrerequisitesFailure',
+ 'shouldRenderCalloutMessage',
+ 'shouldRenderTriggeredLabel',
+ 'hasEnvironment',
+ 'shouldRenderSharedRunnerLimitWarning',
+ 'hasJobLog',
+ 'emptyStateIllustration',
+ 'isScrollingDown',
+ 'emptyStateAction',
+ 'hasOfflineRunnersForProject',
+ ]),
+
+ shouldRenderContent() {
+ return !this.isLoading && !this.hasError;
+ },
+
+ emptyStateTitle() {
+ const { emptyStateIllustration, remainingTime } = this;
+ const { title } = emptyStateIllustration;
+
+ if (this.isDelayedJob) {
+ return sprintf(title, { remainingTime });
+ }
+
+ return title;
+ },
+
+ shouldRenderHeaderCallout() {
+ return this.shouldRenderCalloutMessage && !this.hasUnmetPrerequisitesFailure;
+ },
+
+ itemName() {
+ return sprintf(__('Job %{jobName}'), { jobName: this.job.name });
+ },
+ },
+ watch: {
+ // Once the job log is loaded,
+ // fetch the stages for the dropdown on the sidebar
+ job(newVal, oldVal) {
+ if (isEmpty(oldVal) && !isEmpty(newVal.pipeline)) {
+ const stages = this.job.pipeline.details.stages || [];
+
+ const defaultStage = stages.find((stage) => stage && stage.name === this.selectedStage);
+
+ if (defaultStage) {
+ this.fetchJobsForStage(defaultStage);
+ }
+ }
+ },
+ },
+ created() {
+ this.throttled = throttle(this.toggleScrollButtons, 100);
+
+ window.addEventListener('resize', this.onResize);
+ window.addEventListener('scroll', this.updateScroll);
+ },
+ mounted() {
+ this.updateSidebar();
+ },
+ beforeDestroy() {
+ this.stopPollingJobLog();
+ this.stopPolling();
+ window.removeEventListener('resize', this.onResize);
+ window.removeEventListener('scroll', this.updateScroll);
+ },
+ methods: {
+ ...mapActions([
+ 'fetchJobsForStage',
+ 'hideSidebar',
+ 'showSidebar',
+ 'toggleSidebar',
+ 'scrollBottom',
+ 'scrollTop',
+ 'stopPollingJobLog',
+ 'stopPolling',
+ 'toggleScrollButtons',
+ 'toggleScrollAnimation',
+ ]),
+ onResize() {
+ this.updateSidebar();
+ this.updateScroll();
+ },
+ updateSidebar() {
+ const breakpoint = bp.getBreakpointSize();
+ if (breakpoint === 'xs' || breakpoint === 'sm') {
+ this.hideSidebar();
+ } else if (!this.isSidebarOpen) {
+ this.showSidebar();
+ }
+ },
+ updateScroll() {
+ if (!isScrolledToBottom()) {
+ this.toggleScrollAnimation(false);
+ } else if (this.isScrollingDown) {
+ this.toggleScrollAnimation(true);
+ }
+
+ this.throttled();
+ },
+ setSearchResults(searchResults) {
+ this.searchResults = searchResults;
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <gl-loading-icon v-if="isLoading" size="lg" class="gl-mt-6" />
+
+ <template v-else-if="shouldRenderContent">
+ <div class="build-page" data-testid="job-content">
+ <!-- Header Section -->
+ <header>
+ <div class="build-header top-area">
+ <ci-header
+ :status="job.status"
+ :time="headerTime"
+ :user="job.user"
+ :has-sidebar-button="true"
+ :should-render-triggered-label="shouldRenderTriggeredLabel"
+ :item-name="itemName"
+ @clickedSidebarButton="toggleSidebar"
+ />
+ </div>
+ <gl-alert
+ v-if="shouldRenderHeaderCallout"
+ variant="danger"
+ class="gl-mt-3"
+ :dismissible="false"
+ >
+ <div v-safe-html="job.callout_message"></div>
+ </gl-alert>
+ </header>
+ <!-- EO Header Section -->
+
+ <!-- Body Section -->
+ <stuck-block
+ v-if="job.stuck"
+ :has-offline-runners-for-project="hasOfflineRunnersForProject"
+ :tags="job.tags"
+ :runners-path="runnerSettingsUrl"
+ />
+
+ <unmet-prerequisites-block
+ v-if="hasUnmetPrerequisitesFailure"
+ :help-path="deploymentHelpUrl"
+ />
+
+ <shared-runner
+ v-if="shouldRenderSharedRunnerLimitWarning"
+ :quota-used="job.runners.quota.used"
+ :quota-limit="job.runners.quota.limit"
+ :project-path="projectPath"
+ :subscriptions-more-minutes-url="subscriptionsMoreMinutesUrl"
+ />
+
+ <environments-block
+ v-if="hasEnvironment"
+ :deployment-status="job.deployment_status"
+ :deployment-cluster="job.deployment_cluster"
+ :icon-status="job.status"
+ />
+
+ <erased-block
+ v-if="job.erased_at"
+ data-testid="job-erased-block"
+ :user="job.erased_by"
+ :erased-at="job.erased_at"
+ />
+
+ <div
+ v-if="job.archived"
+ class="gl-mt-3 gl-py-2 gl-px-3 gl-align-items-center gl-z-index-1 gl-m-auto archived-job"
+ :class="{ 'sticky-top gl-border-bottom-0': hasJobLog }"
+ data-testid="archived-job"
+ >
+ <gl-icon name="lock" class="gl-vertical-align-bottom" />
+ {{ __('This job is archived. Only the complete pipeline can be retried.') }}
+ </div>
+ <!-- job log -->
+ <div
+ v-if="hasJobLog"
+ 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"
+ :raw-path="job.raw_path"
+ :is-scroll-bottom-disabled="isScrollBottomDisabled"
+ :is-scroll-top-disabled="isScrollTopDisabled"
+ :is-job-log-size-visible="isJobLogSizeVisible"
+ :is-scrolling-down="isScrollingDown"
+ :is-complete="isJobLogComplete"
+ :job-log="jobLog"
+ @scrollJobLogTop="scrollTop"
+ @scrollJobLogBottom="scrollBottom"
+ @searchResults="setSearchResults"
+ />
+ <log :job-log="jobLog" :is-complete="isJobLogComplete" :search-results="searchResults" />
+ </div>
+ <!-- EO job log -->
+
+ <!-- empty state -->
+ <empty-state
+ v-if="!hasJobLog"
+ :illustration-path="emptyStateIllustration.image"
+ :illustration-size-class="emptyStateIllustration.size"
+ :title="emptyStateTitle"
+ :content="emptyStateIllustration.content"
+ :action="emptyStateAction"
+ :playable="job.playable"
+ :scheduled="job.scheduled"
+ />
+ <!-- EO empty state -->
+
+ <!-- EO Body Section -->
+ </div>
+ </template>
+
+ <sidebar
+ v-if="shouldRenderContent"
+ :class="{
+ 'right-sidebar-expanded': isSidebarOpen,
+ 'right-sidebar-collapsed': !isSidebarOpen,
+ }"
+ :erase-path="job.erase_path"
+ :artifact-help-url="artifactHelpUrl"
+ data-testid="job-sidebar"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/components/job_log_controllers.vue b/app/assets/javascripts/jobs/components/job/job_log_controllers.vue
index e9809ac661b..e9809ac661b 100644
--- a/app/assets/javascripts/jobs/components/job_log_controllers.vue
+++ b/app/assets/javascripts/jobs/components/job/job_log_controllers.vue
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
new file mode 100644
index 00000000000..1898e02c94e
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job/legacy_manual_variables_form.vue
@@ -0,0 +1,192 @@
+<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
new file mode 100644
index 00000000000..2f97301979c
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job/manual_variables_form.vue
@@ -0,0 +1,195 @@
+<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';
+
+// 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.
+
+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/artifacts_block.vue b/app/assets/javascripts/jobs/components/job/sidebar/artifacts_block.vue
index 2018942a7e8..2018942a7e8 100644
--- a/app/assets/javascripts/jobs/components/artifacts_block.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/artifacts_block.vue
diff --git a/app/assets/javascripts/jobs/components/commit_block.vue b/app/assets/javascripts/jobs/components/job/sidebar/commit_block.vue
index 7f25ca8a94d..7f25ca8a94d 100644
--- a/app/assets/javascripts/jobs/components/commit_block.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/commit_block.vue
diff --git a/app/assets/javascripts/jobs/components/job_container_item.vue b/app/assets/javascripts/jobs/components/job/sidebar/job_container_item.vue
index 097ab3b4cf6..097ab3b4cf6 100644
--- a/app/assets/javascripts/jobs/components/job_container_item.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/job_container_item.vue
diff --git a/app/assets/javascripts/jobs/components/job/sidebar/job_retry_forward_deployment_modal.vue b/app/assets/javascripts/jobs/components/job/sidebar/job_retry_forward_deployment_modal.vue
new file mode 100644
index 00000000000..913924cc7b1
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job/sidebar/job_retry_forward_deployment_modal.vue
@@ -0,0 +1,66 @@
+<script>
+import { GlLink, GlModal } from '@gitlab/ui';
+import { JOB_RETRY_FORWARD_DEPLOYMENT_MODAL } from '~/jobs/constants';
+
+export default {
+ name: 'JobRetryForwardDeploymentModal',
+ components: {
+ GlLink,
+ GlModal,
+ },
+ i18n: {
+ ...JOB_RETRY_FORWARD_DEPLOYMENT_MODAL,
+ },
+ inject: {
+ retryOutdatedJobDocsUrl: {
+ default: '',
+ },
+ },
+ props: {
+ modalId: {
+ type: String,
+ required: true,
+ },
+ href: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ primaryProps: {
+ text: this.$options.i18n.primaryText,
+ attributes: [
+ {
+ 'data-method': 'post',
+ 'data-testid': 'retry-button-modal',
+ href: this.href,
+ variant: 'danger',
+ },
+ ],
+ },
+ cancelProps: {
+ text: this.$options.i18n.cancel,
+ attributes: [{ category: 'secondary', variant: 'default' }],
+ },
+ };
+ },
+};
+</script>
+
+<template>
+ <gl-modal
+ :action-cancel="cancelProps"
+ :action-primary="primaryProps"
+ :modal-id="modalId"
+ :title="$options.i18n.title"
+ >
+ <p>
+ {{ $options.i18n.info }}
+ <gl-link v-if="retryOutdatedJobDocsUrl" :href="retryOutdatedJobDocsUrl" target="_blank">
+ {{ $options.i18n.moreInfo }}
+ </gl-link>
+ </p>
+ <p>{{ $options.i18n.areYouSure }}</p>
+ </gl-modal>
+</template>
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
new file mode 100644
index 00000000000..dd620977f0c
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job/sidebar/job_sidebar_retry_button.vue
@@ -0,0 +1,53 @@
+<script>
+import { GlButton, 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,
+ },
+ components: {
+ GlButton,
+ },
+ directives: {
+ GlModal: GlModalDirective,
+ },
+ props: {
+ modalId: {
+ type: String,
+ required: true,
+ },
+ href: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapGetters(['hasForwardDeploymentFailure']),
+ },
+};
+</script>
+<template>
+ <gl-button
+ v-if="hasForwardDeploymentFailure"
+ v-gl-modal="modalId"
+ :aria-label="$options.i18n.retryLabel"
+ category="primary"
+ variant="confirm"
+ icon="retry"
+ data-testid="retry-job-button"
+ />
+
+ <gl-button
+ v-else
+ :href="href"
+ :aria-label="$options.i18n.retryLabel"
+ category="primary"
+ variant="confirm"
+ icon="retry"
+ data-method="post"
+ data-testid="retry-job-link"
+ />
+</template>
diff --git a/app/assets/javascripts/jobs/components/jobs_container.vue b/app/assets/javascripts/jobs/components/job/sidebar/jobs_container.vue
index df64b6422c7..df64b6422c7 100644
--- a/app/assets/javascripts/jobs/components/jobs_container.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/jobs_container.vue
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
new file mode 100644
index 00000000000..263b2d141c9
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job/sidebar/legacy_sidebar_header.vue
@@ -0,0 +1,99 @@
+<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';
+ },
+ },
+ 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="$options.i18n.retryJobButtonLabel"
+ :aria-label="$options.i18n.retryJobButtonLabel"
+ :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
new file mode 100644
index 00000000000..b0db48df01f
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job/sidebar/sidebar.vue
@@ -0,0 +1,149 @@
+<script>
+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 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';
+
+export default {
+ name: 'JobSidebar',
+ i18n: {
+ ...JOB_SIDEBAR_COPY,
+ },
+ borderTopClass: ['gl-border-t-solid', 'gl-border-t-1', 'gl-border-t-gray-100'],
+ forwardDeploymentFailureModalId,
+ components: {
+ ArtifactsBlock,
+ CommitBlock,
+ GlButton,
+ GlIcon,
+ 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']),
+ ...mapState(['job', 'stages', 'jobs', 'selectedStage']),
+ hasArtifact() {
+ // the artifact object will always have a locked property
+ return Object.keys(this.job.artifact).length > 1;
+ },
+ hasTriggers() {
+ return !isEmpty(this.job.trigger);
+ },
+ isGraphQL() {
+ return this.glFeatures?.graphqlJobApp;
+ },
+ commit() {
+ return this.job?.pipeline?.commit || {};
+ },
+ shouldShowJobRetryForwardDeploymentModal() {
+ return this.job.retry_path && this.hasForwardDeploymentFailure;
+ },
+ },
+ methods: {
+ ...mapActions(['fetchJobsForStage']),
+ },
+};
+</script>
+<template>
+ <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" />
+ <div
+ v-if="job.terminal_path || job.new_issue_path"
+ class="gl-py-5"
+ :class="$options.borderTopClass"
+ >
+ <gl-button
+ v-if="job.new_issue_path"
+ :href="job.new_issue_path"
+ category="secondary"
+ variant="confirm"
+ data-testid="job-new-issue"
+ >
+ {{ $options.i18n.newIssue }}
+ </gl-button>
+ <gl-button
+ v-if="job.terminal_path"
+ :href="job.terminal_path"
+ target="_blank"
+ data-testid="terminal-link"
+ >
+ {{ $options.i18n.debug }}
+ <gl-icon name="external-link" />
+ </gl-button>
+ </div>
+
+ <job-sidebar-details-container class="gl-py-5" :class="$options.borderTopClass" />
+
+ <artifacts-block
+ v-if="hasArtifact"
+ class="gl-py-5"
+ :class="$options.borderTopClass"
+ :artifact="job.artifact"
+ :help-url="artifactHelpUrl"
+ />
+
+ <trigger-block
+ v-if="hasTriggers"
+ class="gl-py-5"
+ :class="$options.borderTopClass"
+ :trigger="job.trigger"
+ />
+
+ <commit-block
+ :commit="commit"
+ class="gl-py-5"
+ :class="$options.borderTopClass"
+ :merge-request="job.merge_request"
+ />
+
+ <stages-dropdown
+ v-if="job.pipeline"
+ class="gl-py-5"
+ :class="$options.borderTopClass"
+ :pipeline="job.pipeline"
+ :selected-stage="selectedStage"
+ :stages="stages"
+ @requestSidebarStageDropdown="fetchJobsForStage"
+ />
+ </div>
+
+ <jobs-container v-if="jobs.length" :job-id="job.id" :jobs="jobs" />
+ </div>
+ <job-retry-forward-deployment-modal
+ v-if="shouldShowJobRetryForwardDeploymentModal"
+ :modal-id="$options.forwardDeploymentFailureModalId"
+ :href="job.retry_path"
+ />
+ </aside>
+</template>
diff --git a/app/assets/javascripts/jobs/components/sidebar_detail_row.vue b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_detail_row.vue
index 05567328660..05567328660 100644
--- a/app/assets/javascripts/jobs/components/sidebar_detail_row.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_detail_row.vue
diff --git a/app/assets/javascripts/jobs/components/job/sidebar/sidebar_header.vue b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_header.vue
new file mode 100644
index 00000000000..523710598bf
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_header.vue
@@ -0,0 +1,102 @@
+<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';
+
+// 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: {
+ ...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';
+ },
+ },
+ 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="$options.i18n.retryJobButtonLabel"
+ :aria-label="$options.i18n.retryJobButtonLabel"
+ :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/sidebar_job_details_container.vue b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_job_details_container.vue
index 3b1509e5be5..3b1509e5be5 100644
--- a/app/assets/javascripts/jobs/components/sidebar_job_details_container.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/sidebar_job_details_container.vue
diff --git a/app/assets/javascripts/jobs/components/job/sidebar/stages_dropdown.vue b/app/assets/javascripts/jobs/components/job/sidebar/stages_dropdown.vue
new file mode 100644
index 00000000000..e3afe9b7c67
--- /dev/null
+++ b/app/assets/javascripts/jobs/components/job/sidebar/stages_dropdown.vue
@@ -0,0 +1,167 @@
+<script>
+import { GlLink, GlDropdown, GlDropdownItem, GlSprintf } from '@gitlab/ui';
+import { isEmpty } from 'lodash';
+import Mousetrap from 'mousetrap';
+import { s__ } from '~/locale';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import { clickCopyToClipboardButton } from '~/behaviors/copy_to_clipboard';
+import { keysFor, MR_COPY_SOURCE_BRANCH_NAME } from '~/behaviors/shortcuts/keybindings';
+
+export default {
+ components: {
+ CiIcon,
+ ClipboardButton,
+ GlDropdown,
+ GlDropdownItem,
+ GlLink,
+ GlSprintf,
+ },
+ props: {
+ pipeline: {
+ type: Object,
+ required: true,
+ },
+ stages: {
+ type: Array,
+ required: true,
+ },
+ selectedStage: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ hasRef() {
+ return !isEmpty(this.pipeline.ref);
+ },
+ isTriggeredByMergeRequest() {
+ return Boolean(this.pipeline.merge_request);
+ },
+ isMergeRequestPipeline() {
+ return Boolean(this.pipeline.flags && this.pipeline.flags.merge_request_pipeline);
+ },
+ pipelineInfo() {
+ if (!this.hasRef) {
+ return s__('Job|%{boldStart}Pipeline%{boldEnd} %{id}');
+ } else if (!this.isTriggeredByMergeRequest) {
+ return s__('Job|%{boldStart}Pipeline%{boldEnd} %{id} for %{ref}');
+ } else if (!this.isMergeRequestPipeline) {
+ return s__('Job|%{boldStart}Pipeline%{boldEnd} %{id} for %{mrId} with %{source}');
+ }
+
+ return s__(
+ 'Job|%{boldStart}Pipeline%{boldEnd} %{id} for %{mrId} with %{source} into %{target}',
+ );
+ },
+ },
+ mounted() {
+ Mousetrap.bind(keysFor(MR_COPY_SOURCE_BRANCH_NAME), this.handleKeyboardCopy);
+ },
+ beforeDestroy() {
+ Mousetrap.unbind(keysFor(MR_COPY_SOURCE_BRANCH_NAME));
+ },
+ methods: {
+ onStageClick(stage) {
+ this.$emit('requestSidebarStageDropdown', stage);
+ },
+ handleKeyboardCopy() {
+ let button;
+
+ if (!this.hasRef) {
+ return;
+ } else if (!this.isTriggeredByMergeRequest) {
+ button = this.$refs['copy-source-ref-link'];
+ } else {
+ button = this.$refs['copy-source-branch-link'];
+ }
+
+ clickCopyToClipboardButton(button.$el);
+ },
+ },
+};
+</script>
+<template>
+ <div class="dropdown">
+ <div class="js-pipeline-info" data-testid="pipeline-info">
+ <ci-icon :status="pipeline.details.status" />
+ <gl-sprintf :message="pipelineInfo">
+ <template #bold="{ content }">
+ <span class="font-weight-bold">{{ content }}</span>
+ </template>
+ <template #id>
+ <gl-link
+ :href="pipeline.path"
+ class="js-pipeline-path link-commit"
+ data-testid="pipeline-path"
+ data-qa-selector="pipeline_path"
+ >#{{ pipeline.id }}</gl-link
+ >
+ </template>
+ <template #mrId>
+ <gl-link
+ :href="pipeline.merge_request.path"
+ class="link-commit ref-name"
+ data-testid="mr-link"
+ >!{{ pipeline.merge_request.iid }}</gl-link
+ >
+ </template>
+ <template #ref>
+ <gl-link
+ :href="pipeline.ref.path"
+ class="link-commit ref-name"
+ data-testid="source-ref-link"
+ >{{ pipeline.ref.name }}</gl-link
+ ><clipboard-button
+ ref="copy-source-ref-link"
+ :text="pipeline.ref.name"
+ :title="__('Copy reference')"
+ category="tertiary"
+ size="small"
+ data-testid="copy-source-ref-link"
+ />
+ </template>
+ <template #source>
+ <gl-link
+ :href="pipeline.merge_request.source_branch_path"
+ class="link-commit ref-name"
+ data-testid="source-branch-link"
+ >{{ pipeline.merge_request.source_branch }}</gl-link
+ ><clipboard-button
+ ref="copy-source-branch-link"
+ :text="pipeline.merge_request.source_branch"
+ :title="__('Copy branch name')"
+ category="tertiary"
+ size="small"
+ data-testid="copy-source-branch-link"
+ />
+ </template>
+ <template #target>
+ <gl-link
+ :href="pipeline.merge_request.target_branch_path"
+ class="link-commit ref-name"
+ data-testid="target-branch-link"
+ >{{ pipeline.merge_request.target_branch }}</gl-link
+ ><clipboard-button
+ :text="pipeline.merge_request.target_branch"
+ :title="__('Copy branch name')"
+ category="tertiary"
+ size="small"
+ data-testid="copy-target-branch-link"
+ />
+ </template>
+ </gl-sprintf>
+ </div>
+
+ <gl-dropdown :text="selectedStage" class="js-selected-stage gl-w-full gl-mt-3">
+ <gl-dropdown-item
+ v-for="stage in stages"
+ :key="stage.name"
+ class="js-stage-item stage-item"
+ @click="onStageClick(stage)"
+ >
+ {{ stage.name }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+ </div>
+</template>
diff --git a/app/assets/javascripts/jobs/components/trigger_block.vue b/app/assets/javascripts/jobs/components/job/sidebar/trigger_block.vue
index 1afc1c9a595..1afc1c9a595 100644
--- a/app/assets/javascripts/jobs/components/trigger_block.vue
+++ b/app/assets/javascripts/jobs/components/job/sidebar/trigger_block.vue
diff --git a/app/assets/javascripts/jobs/components/stuck_block.vue b/app/assets/javascripts/jobs/components/job/stuck_block.vue
index d7a26d22406..d7a26d22406 100644
--- a/app/assets/javascripts/jobs/components/stuck_block.vue
+++ b/app/assets/javascripts/jobs/components/job/stuck_block.vue
diff --git a/app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue b/app/assets/javascripts/jobs/components/job/unmet_prerequisites_block.vue
index c9747ca9f02..c9747ca9f02 100644
--- a/app/assets/javascripts/jobs/components/unmet_prerequisites_block.vue
+++ b/app/assets/javascripts/jobs/components/job/unmet_prerequisites_block.vue
diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job_app.vue
deleted file mode 100644
index d5ee3423d70..00000000000
--- a/app/assets/javascripts/jobs/components/job_app.vue
+++ /dev/null
@@ -1,328 +0,0 @@
-<script>
-import { GlLoadingIcon, GlIcon, GlSafeHtmlDirective as SafeHtml, 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 { isScrolledToBottom } from '~/lib/utils/scroll_utils';
-import { __, sprintf } from '~/locale';
-import CiHeader from '~/vue_shared/components/header_ci_component.vue';
-import delayedJobMixin from '../mixins/delayed_job_mixin';
-import EmptyState from './empty_state.vue';
-import EnvironmentsBlock from './environments_block.vue';
-import ErasedBlock from './erased_block.vue';
-import LogTopBar from './job_log_controllers.vue';
-import Log from './log/log.vue';
-import Sidebar from './sidebar.vue';
-import StuckBlock from './stuck_block.vue';
-import UnmetPrerequisitesBlock from './unmet_prerequisites_block.vue';
-
-export default {
- name: 'JobPageApp',
- components: {
- CiHeader,
- EmptyState,
- EnvironmentsBlock,
- ErasedBlock,
- GlIcon,
- Log,
- LogTopBar,
- StuckBlock,
- UnmetPrerequisitesBlock,
- Sidebar,
- GlLoadingIcon,
- SharedRunner: () => import('ee_component/jobs/components/shared_runner_limit_block.vue'),
- GlAlert,
- },
- directives: {
- SafeHtml,
- },
- mixins: [delayedJobMixin],
- props: {
- artifactHelpUrl: {
- type: String,
- required: false,
- default: '',
- },
- runnerSettingsUrl: {
- type: String,
- required: false,
- default: null,
- },
- deploymentHelpUrl: {
- type: String,
- required: false,
- default: null,
- },
- terminalPath: {
- type: String,
- required: false,
- default: null,
- },
- projectPath: {
- type: String,
- required: true,
- },
- subscriptionsMoreMinutesUrl: {
- type: String,
- required: false,
- default: null,
- },
- },
- data() {
- return {
- searchResults: [],
- };
- },
- computed: {
- ...mapState([
- 'isLoading',
- 'job',
- 'isSidebarOpen',
- 'jobLog',
- 'isJobLogComplete',
- 'jobLogSize',
- 'isJobLogSizeVisible',
- 'isScrollBottomDisabled',
- 'isScrollTopDisabled',
- 'isScrolledToBottomBeforeReceivingJobLog',
- 'hasError',
- 'selectedStage',
- ]),
- ...mapGetters([
- 'headerTime',
- 'hasUnmetPrerequisitesFailure',
- 'shouldRenderCalloutMessage',
- 'shouldRenderTriggeredLabel',
- 'hasEnvironment',
- 'shouldRenderSharedRunnerLimitWarning',
- 'hasJobLog',
- 'emptyStateIllustration',
- 'isScrollingDown',
- 'emptyStateAction',
- 'hasOfflineRunnersForProject',
- ]),
-
- shouldRenderContent() {
- return !this.isLoading && !this.hasError;
- },
-
- emptyStateTitle() {
- const { emptyStateIllustration, remainingTime } = this;
- const { title } = emptyStateIllustration;
-
- if (this.isDelayedJob) {
- return sprintf(title, { remainingTime });
- }
-
- return title;
- },
-
- shouldRenderHeaderCallout() {
- return this.shouldRenderCalloutMessage && !this.hasUnmetPrerequisitesFailure;
- },
-
- itemName() {
- return sprintf(__('Job %{jobName}'), { jobName: this.job.name });
- },
- },
- watch: {
- // Once the job log is loaded,
- // fetch the stages for the dropdown on the sidebar
- job(newVal, oldVal) {
- if (isEmpty(oldVal) && !isEmpty(newVal.pipeline)) {
- const stages = this.job.pipeline.details.stages || [];
-
- const defaultStage = stages.find((stage) => stage && stage.name === this.selectedStage);
-
- if (defaultStage) {
- this.fetchJobsForStage(defaultStage);
- }
- }
- },
- },
- created() {
- this.throttled = throttle(this.toggleScrollButtons, 100);
-
- window.addEventListener('resize', this.onResize);
- window.addEventListener('scroll', this.updateScroll);
- },
- mounted() {
- this.updateSidebar();
- },
- beforeDestroy() {
- this.stopPollingJobLog();
- this.stopPolling();
- window.removeEventListener('resize', this.onResize);
- window.removeEventListener('scroll', this.updateScroll);
- },
- methods: {
- ...mapActions([
- 'fetchJobsForStage',
- 'hideSidebar',
- 'showSidebar',
- 'toggleSidebar',
- 'scrollBottom',
- 'scrollTop',
- 'stopPollingJobLog',
- 'stopPolling',
- 'toggleScrollButtons',
- 'toggleScrollAnimation',
- ]),
- onResize() {
- this.updateSidebar();
- this.updateScroll();
- },
- updateSidebar() {
- const breakpoint = bp.getBreakpointSize();
- if (breakpoint === 'xs' || breakpoint === 'sm') {
- this.hideSidebar();
- } else if (!this.isSidebarOpen) {
- this.showSidebar();
- }
- },
- updateScroll() {
- if (!isScrolledToBottom()) {
- this.toggleScrollAnimation(false);
- } else if (this.isScrollingDown) {
- this.toggleScrollAnimation(true);
- }
-
- this.throttled();
- },
- setSearchResults(searchResults) {
- this.searchResults = searchResults;
- },
- },
-};
-</script>
-<template>
- <div>
- <gl-loading-icon v-if="isLoading" size="lg" class="qa-loading-animation gl-mt-6" />
-
- <template v-else-if="shouldRenderContent">
- <div class="build-page" data-testid="job-content">
- <!-- Header Section -->
- <header>
- <div class="build-header top-area">
- <ci-header
- :status="job.status"
- :time="headerTime"
- :user="job.user"
- :has-sidebar-button="true"
- :should-render-triggered-label="shouldRenderTriggeredLabel"
- :item-name="itemName"
- @clickedSidebarButton="toggleSidebar"
- />
- </div>
- <gl-alert
- v-if="shouldRenderHeaderCallout"
- variant="danger"
- class="gl-mt-3"
- :dismissible="false"
- >
- <div v-safe-html="job.callout_message"></div>
- </gl-alert>
- </header>
- <!-- EO Header Section -->
-
- <!-- Body Section -->
- <stuck-block
- v-if="job.stuck"
- :has-offline-runners-for-project="hasOfflineRunnersForProject"
- :tags="job.tags"
- :runners-path="runnerSettingsUrl"
- />
-
- <unmet-prerequisites-block
- v-if="hasUnmetPrerequisitesFailure"
- :help-path="deploymentHelpUrl"
- />
-
- <shared-runner
- v-if="shouldRenderSharedRunnerLimitWarning"
- :quota-used="job.runners.quota.used"
- :quota-limit="job.runners.quota.limit"
- :project-path="projectPath"
- :subscriptions-more-minutes-url="subscriptionsMoreMinutesUrl"
- />
-
- <environments-block
- v-if="hasEnvironment"
- :deployment-status="job.deployment_status"
- :deployment-cluster="job.deployment_cluster"
- :icon-status="job.status"
- />
-
- <erased-block
- v-if="job.erased_at"
- data-testid="job-erased-block"
- :user="job.erased_by"
- :erased-at="job.erased_at"
- />
-
- <div
- v-if="job.archived"
- class="gl-mt-3 gl-py-2 gl-px-3 gl-align-items-center gl-z-index-1 gl-m-auto archived-job"
- :class="{ 'sticky-top gl-border-bottom-0': hasJobLog }"
- data-testid="archived-job"
- >
- <gl-icon name="lock" class="gl-vertical-align-bottom" />
- {{ __('This job is archived. Only the complete pipeline can be retried.') }}
- </div>
- <!-- job log -->
- <div
- v-if="hasJobLog"
- 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"
- :raw-path="job.raw_path"
- :is-scroll-bottom-disabled="isScrollBottomDisabled"
- :is-scroll-top-disabled="isScrollTopDisabled"
- :is-job-log-size-visible="isJobLogSizeVisible"
- :is-scrolling-down="isScrollingDown"
- :is-complete="isJobLogComplete"
- :job-log="jobLog"
- @scrollJobLogTop="scrollTop"
- @scrollJobLogBottom="scrollBottom"
- @searchResults="setSearchResults"
- />
- <log :job-log="jobLog" :is-complete="isJobLogComplete" :search-results="searchResults" />
- </div>
- <!-- EO job log -->
-
- <!-- empty state -->
- <empty-state
- v-if="!hasJobLog"
- :illustration-path="emptyStateIllustration.image"
- :illustration-size-class="emptyStateIllustration.size"
- :title="emptyStateTitle"
- :content="emptyStateIllustration.content"
- :action="emptyStateAction"
- :playable="job.playable"
- :scheduled="job.scheduled"
- />
- <!-- EO empty state -->
-
- <!-- EO Body Section -->
- </div>
- </template>
-
- <sidebar
- v-if="shouldRenderContent"
- :class="{
- 'right-sidebar-expanded': isSidebarOpen,
- 'right-sidebar-collapsed': !isSidebarOpen,
- }"
- :erase-path="job.erase_path"
- :artifact-help-url="artifactHelpUrl"
- data-testid="job-sidebar"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/jobs/components/job_retry_forward_deployment_modal.vue b/app/assets/javascripts/jobs/components/job_retry_forward_deployment_modal.vue
deleted file mode 100644
index e83ed6c6332..00000000000
--- a/app/assets/javascripts/jobs/components/job_retry_forward_deployment_modal.vue
+++ /dev/null
@@ -1,66 +0,0 @@
-<script>
-import { GlLink, GlModal } from '@gitlab/ui';
-import { JOB_RETRY_FORWARD_DEPLOYMENT_MODAL } from '../constants';
-
-export default {
- name: 'JobRetryForwardDeploymentModal',
- components: {
- GlLink,
- GlModal,
- },
- i18n: {
- ...JOB_RETRY_FORWARD_DEPLOYMENT_MODAL,
- },
- inject: {
- retryOutdatedJobDocsUrl: {
- default: '',
- },
- },
- props: {
- modalId: {
- type: String,
- required: true,
- },
- href: {
- type: String,
- required: true,
- },
- },
- data() {
- return {
- primaryProps: {
- text: this.$options.i18n.primaryText,
- attributes: [
- {
- 'data-method': 'post',
- 'data-testid': 'retry-button-modal',
- href: this.href,
- variant: 'danger',
- },
- ],
- },
- cancelProps: {
- text: this.$options.i18n.cancel,
- attributes: [{ category: 'secondary', variant: 'default' }],
- },
- };
- },
-};
-</script>
-
-<template>
- <gl-modal
- :action-cancel="cancelProps"
- :action-primary="primaryProps"
- :modal-id="modalId"
- :title="$options.i18n.title"
- >
- <p>
- {{ $options.i18n.info }}
- <gl-link v-if="retryOutdatedJobDocsUrl" :href="retryOutdatedJobDocsUrl" target="_blank">
- {{ $options.i18n.moreInfo }}
- </gl-link>
- </p>
- <p>{{ $options.i18n.areYouSure }}</p>
- </gl-modal>
-</template>
diff --git a/app/assets/javascripts/jobs/components/job_sidebar_retry_button.vue b/app/assets/javascripts/jobs/components/job_sidebar_retry_button.vue
deleted file mode 100644
index a7bf365d35c..00000000000
--- a/app/assets/javascripts/jobs/components/job_sidebar_retry_button.vue
+++ /dev/null
@@ -1,53 +0,0 @@
-<script>
-import { GlButton, GlModalDirective } from '@gitlab/ui';
-import { mapGetters } from 'vuex';
-import { JOB_SIDEBAR } from '../constants';
-
-export default {
- name: 'JobSidebarRetryButton',
- i18n: {
- retryLabel: JOB_SIDEBAR.retry,
- },
- components: {
- GlButton,
- },
- directives: {
- GlModal: GlModalDirective,
- },
- props: {
- modalId: {
- type: String,
- required: true,
- },
- href: {
- type: String,
- required: true,
- },
- },
- computed: {
- ...mapGetters(['hasForwardDeploymentFailure']),
- },
-};
-</script>
-<template>
- <gl-button
- v-if="hasForwardDeploymentFailure"
- v-gl-modal="modalId"
- :aria-label="$options.i18n.retryLabel"
- category="primary"
- variant="confirm"
- icon="retry"
- data-testid="retry-job-button"
- />
-
- <gl-button
- v-else
- :href="href"
- :aria-label="$options.i18n.retryLabel"
- category="primary"
- variant="confirm"
- icon="retry"
- data-method="post"
- data-testid="retry-job-link"
- />
-</template>
diff --git a/app/assets/javascripts/jobs/components/manual_variables_form.vue b/app/assets/javascripts/jobs/components/manual_variables_form.vue
deleted file mode 100644
index 07ef4f054b4..00000000000
--- a/app/assets/javascripts/jobs/components/manual_variables_form.vue
+++ /dev/null
@@ -1,193 +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']),
- canRemove(index) {
- return index < this.variables.length - 1;
- },
- addEmptyVariable() {
- const lastVar = this.variables[this.variables.length - 1];
-
- if (lastVar.key === '') {
- return;
- }
-
- this.variables.push({
- key: '',
- secret_value: '',
- id: uniqueId(),
- });
- },
- deleteVariable(id) {
- this.variables.splice(
- this.variables.findIndex((el) => el.id === id),
- 1,
- );
- },
- 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="`${$options.inputTypes.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="`${$options.inputTypes.value}-${variable.id}`"
- v-model="variable.secretValue"
- :placeholder="$options.i18n.valuePlaceholder"
- data-testid="ci-variable-value"
- />
- </gl-form-input-group>
-
- <!-- delete variable button placeholder to not break flex layout -->
- <div
- v-if="!canRemove(index)"
- class="gl-w-7 gl-mr-3"
- data-testid="delete-variable-btn-placeholder"
- ></div>
-
- <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)"
- />
- </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/sidebar.vue b/app/assets/javascripts/jobs/components/sidebar.vue
deleted file mode 100644
index a42e45ee7e4..00000000000
--- a/app/assets/javascripts/jobs/components/sidebar.vue
+++ /dev/null
@@ -1,213 +0,0 @@
-<script>
-import { GlButton, GlIcon, GlTooltipDirective } from '@gitlab/ui';
-import { isEmpty } from 'lodash';
-import { mapActions, mapGetters, mapState } from 'vuex';
-import { s__ } from '~/locale';
-import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
-import { JOB_SIDEBAR } from '../constants';
-import ArtifactsBlock from './artifacts_block.vue';
-import CommitBlock from './commit_block.vue';
-import JobRetryForwardDeploymentModal from './job_retry_forward_deployment_modal.vue';
-import JobSidebarRetryButton from './job_sidebar_retry_button.vue';
-import JobsContainer from './jobs_container.vue';
-import JobSidebarDetailsContainer from './sidebar_job_details_container.vue';
-import StagesDropdown from './stages_dropdown.vue';
-import TriggerBlock from './trigger_block.vue';
-
-export const forwardDeploymentFailureModalId = 'forward-deployment-failure';
-
-export default {
- name: 'JobSidebar',
- i18n: {
- eraseLogButtonLabel: s__('Job|Erase job log and artifacts'),
- eraseLogConfirmText: s__('Job|Are you sure you want to erase this job log and artifacts?'),
- cancelJobButtonLabel: s__('Job|Cancel'),
- retryJobButtonLabel: s__('Job|Retry'),
- ...JOB_SIDEBAR,
- },
- borderTopClass: ['gl-border-t-solid', 'gl-border-t-1', 'gl-border-t-gray-100'],
- forwardDeploymentFailureModalId,
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- components: {
- ArtifactsBlock,
- CommitBlock,
- GlButton,
- GlIcon,
- JobsContainer,
- JobSidebarRetryButton,
- JobRetryForwardDeploymentModal,
- JobSidebarDetailsContainer,
- StagesDropdown,
- TooltipOnTruncate,
- TriggerBlock,
- },
- props: {
- artifactHelpUrl: {
- type: String,
- required: false,
- default: '',
- },
- erasePath: {
- type: String,
- required: false,
- default: null,
- },
- },
- computed: {
- ...mapGetters(['hasForwardDeploymentFailure']),
- ...mapState(['job', 'stages', 'jobs', 'selectedStage']),
- retryButtonCategory() {
- return this.job.status && this.job.recoverable ? 'primary' : 'secondary';
- },
- hasArtifact() {
- // the artifact object will always have a locked property
- return Object.keys(this.job.artifact).length > 1;
- },
- hasTriggers() {
- return !isEmpty(this.job.trigger);
- },
- hasStages() {
- return this.job?.pipeline?.stages?.length > 0;
- },
- commit() {
- return this.job?.pipeline?.commit || {};
- },
- shouldShowJobRetryForwardDeploymentModal() {
- return this.job.retry_path && this.hasForwardDeploymentFailure;
- },
- },
- methods: {
- ...mapActions(['fetchJobsForStage', 'toggleSidebar']),
- },
-};
-</script>
-<template>
- <aside class="right-sidebar build-sidebar" data-offset-top="101" data-spy="affix">
- <div class="sidebar-container">
- <div class="blocks-container">
- <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="$options.i18n.retryJobButtonLabel"
- :aria-label="$options.i18n.retryJobButtonLabel"
- :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"
- />
- </div>
-
- <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
- v-if="job.terminal_path || job.new_issue_path"
- class="gl-py-5"
- :class="$options.borderTopClass"
- >
- <gl-button
- v-if="job.new_issue_path"
- :href="job.new_issue_path"
- category="secondary"
- variant="confirm"
- data-testid="job-new-issue"
- >
- {{ $options.i18n.newIssue }}
- </gl-button>
- <gl-button
- v-if="job.terminal_path"
- :href="job.terminal_path"
- target="_blank"
- data-testid="terminal-link"
- >
- {{ $options.i18n.debug }}
- <gl-icon name="external-link" />
- </gl-button>
- </div>
-
- <job-sidebar-details-container class="gl-py-5" :class="$options.borderTopClass" />
-
- <artifacts-block
- v-if="hasArtifact"
- class="gl-py-5"
- :class="$options.borderTopClass"
- :artifact="job.artifact"
- :help-url="artifactHelpUrl"
- />
-
- <trigger-block
- v-if="hasTriggers"
- class="gl-py-5"
- :class="$options.borderTopClass"
- :trigger="job.trigger"
- />
-
- <commit-block
- :commit="commit"
- class="gl-py-5"
- :class="$options.borderTopClass"
- :merge-request="job.merge_request"
- />
-
- <stages-dropdown
- v-if="job.pipeline"
- class="gl-py-5"
- :class="$options.borderTopClass"
- :pipeline="job.pipeline"
- :selected-stage="selectedStage"
- :stages="stages"
- @requestSidebarStageDropdown="fetchJobsForStage"
- />
- </div>
-
- <jobs-container v-if="jobs.length" :job-id="job.id" :jobs="jobs" />
- </div>
- <job-retry-forward-deployment-modal
- v-if="shouldShowJobRetryForwardDeploymentModal"
- :modal-id="$options.forwardDeploymentFailureModalId"
- :href="job.retry_path"
- />
- </aside>
-</template>
diff --git a/app/assets/javascripts/jobs/components/stages_dropdown.vue b/app/assets/javascripts/jobs/components/stages_dropdown.vue
deleted file mode 100644
index 7c4811b2d6f..00000000000
--- a/app/assets/javascripts/jobs/components/stages_dropdown.vue
+++ /dev/null
@@ -1,167 +0,0 @@
-<script>
-import { GlLink, GlDropdown, GlDropdownItem, GlSprintf } from '@gitlab/ui';
-import { isEmpty } from 'lodash';
-import Mousetrap from 'mousetrap';
-import { s__ } from '~/locale';
-import CiIcon from '~/vue_shared/components/ci_icon.vue';
-import clipboardButton from '~/vue_shared/components/clipboard_button.vue';
-import { clickCopyToClipboardButton } from '~/behaviors/copy_to_clipboard';
-import { keysFor, MR_COPY_SOURCE_BRANCH_NAME } from '~/behaviors/shortcuts/keybindings';
-
-export default {
- components: {
- CiIcon,
- clipboardButton,
- GlDropdown,
- GlDropdownItem,
- GlLink,
- GlSprintf,
- },
- props: {
- pipeline: {
- type: Object,
- required: true,
- },
- stages: {
- type: Array,
- required: true,
- },
- selectedStage: {
- type: String,
- required: true,
- },
- },
- computed: {
- hasRef() {
- return !isEmpty(this.pipeline.ref);
- },
- isTriggeredByMergeRequest() {
- return Boolean(this.pipeline.merge_request);
- },
- isMergeRequestPipeline() {
- return Boolean(this.pipeline.flags && this.pipeline.flags.merge_request_pipeline);
- },
- pipelineInfo() {
- if (!this.hasRef) {
- return s__('Job|%{boldStart}Pipeline%{boldEnd} %{id}');
- } else if (!this.isTriggeredByMergeRequest) {
- return s__('Job|%{boldStart}Pipeline%{boldEnd} %{id} for %{ref}');
- } else if (!this.isMergeRequestPipeline) {
- return s__('Job|%{boldStart}Pipeline%{boldEnd} %{id} for %{mrId} with %{source}');
- }
-
- return s__(
- 'Job|%{boldStart}Pipeline%{boldEnd} %{id} for %{mrId} with %{source} into %{target}',
- );
- },
- },
- mounted() {
- Mousetrap.bind(keysFor(MR_COPY_SOURCE_BRANCH_NAME), this.handleKeyboardCopy);
- },
- beforeDestroy() {
- Mousetrap.unbind(keysFor(MR_COPY_SOURCE_BRANCH_NAME));
- },
- methods: {
- onStageClick(stage) {
- this.$emit('requestSidebarStageDropdown', stage);
- },
- handleKeyboardCopy() {
- let button;
-
- if (!this.hasRef) {
- return;
- } else if (!this.isTriggeredByMergeRequest) {
- button = this.$refs['copy-source-ref-link'];
- } else {
- button = this.$refs['copy-source-branch-link'];
- }
-
- clickCopyToClipboardButton(button.$el);
- },
- },
-};
-</script>
-<template>
- <div class="dropdown">
- <div class="js-pipeline-info" data-testid="pipeline-info">
- <ci-icon :status="pipeline.details.status" />
- <gl-sprintf :message="pipelineInfo">
- <template #bold="{ content }">
- <span class="font-weight-bold">{{ content }}</span>
- </template>
- <template #id>
- <gl-link
- :href="pipeline.path"
- class="js-pipeline-path link-commit"
- data-testid="pipeline-path"
- data-qa-selector="pipeline_path"
- >#{{ pipeline.id }}</gl-link
- >
- </template>
- <template #mrId>
- <gl-link
- :href="pipeline.merge_request.path"
- class="link-commit ref-name"
- data-testid="mr-link"
- >!{{ pipeline.merge_request.iid }}</gl-link
- >
- </template>
- <template #ref>
- <gl-link
- :href="pipeline.ref.path"
- class="link-commit ref-name"
- data-testid="source-ref-link"
- >{{ pipeline.ref.name }}</gl-link
- ><clipboard-button
- ref="copy-source-ref-link"
- :text="pipeline.ref.name"
- :title="__('Copy reference')"
- category="tertiary"
- size="small"
- data-testid="copy-source-ref-link"
- />
- </template>
- <template #source>
- <gl-link
- :href="pipeline.merge_request.source_branch_path"
- class="link-commit ref-name"
- data-testid="source-branch-link"
- >{{ pipeline.merge_request.source_branch }}</gl-link
- ><clipboard-button
- ref="copy-source-branch-link"
- :text="pipeline.merge_request.source_branch"
- :title="__('Copy branch name')"
- category="tertiary"
- size="small"
- data-testid="copy-source-branch-link"
- />
- </template>
- <template #target>
- <gl-link
- :href="pipeline.merge_request.target_branch_path"
- class="link-commit ref-name"
- data-testid="target-branch-link"
- >{{ pipeline.merge_request.target_branch }}</gl-link
- ><clipboard-button
- :text="pipeline.merge_request.target_branch"
- :title="__('Copy branch name')"
- category="tertiary"
- size="small"
- data-testid="copy-target-branch-link"
- />
- </template>
- </gl-sprintf>
- </div>
-
- <gl-dropdown :text="selectedStage" class="js-selected-stage gl-w-full gl-mt-3">
- <gl-dropdown-item
- v-for="stage in stages"
- :key="stage.name"
- class="js-stage-item stage-item"
- @click="onStageClick(stage)"
- >
- {{ stage.name }}
- </gl-dropdown-item>
- </gl-dropdown>
- </div>
-</template>
diff --git a/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql b/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql
index 98b51e8c2c4..851be211b25 100644
--- a/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql
+++ b/app/assets/javascripts/jobs/components/table/graphql/queries/get_jobs.query.graphql
@@ -11,6 +11,7 @@ query getJobs($fullPath: ID!, $after: String, $first: Int = 30, $statuses: [CiJo
}
nodes {
artifacts {
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
nodes {
downloadPath
fileType
diff --git a/app/assets/javascripts/jobs/components/table/jobs_table_app.vue b/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
index c2f460cb647..0a4757d11a8 100644
--- a/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
+++ b/app/assets/javascripts/jobs/components/table/jobs_table_app.vue
@@ -2,7 +2,9 @@
import { GlAlert, GlSkeletonLoader, GlIntersectionObserver, GlLoadingIcon } from '@gitlab/ui';
import { __ } from '~/locale';
import createFlash from '~/flash';
+import { setUrlParams, updateHistory, queryToObject } from '~/lib/utils/url_utility';
import JobsFilteredSearch from '../filtered_search/jobs_filtered_search.vue';
+import { validateQueryString } from '../filtered_search/utils';
import GetJobs from './graphql/queries/get_jobs.query.graphql';
import JobsTable from './jobs_table.vue';
import JobsTableEmptyState from './jobs_table_empty_state.vue';
@@ -37,6 +39,7 @@ export default {
variables() {
return {
fullPath: this.fullPath,
+ ...this.validatedQueryString,
};
},
update(data) {
@@ -95,6 +98,11 @@ export default {
jobsCount() {
return this.jobs.count;
},
+ validatedQueryString() {
+ const queryStringObject = queryToObject(window.location.search);
+
+ return validateQueryString(queryStringObject);
+ },
},
watch: {
// this watcher ensures that the count on the all tab
@@ -133,6 +141,10 @@ export default {
}
if (filter.type === 'status') {
+ updateHistory({
+ url: setUrlParams({ statuses: filter.value.data }, window.location.href, true),
+ });
+
this.$apollo.queries.jobs.refetch({ statuses: filter.value.data });
}
});
@@ -171,12 +183,12 @@ export default {
:loading="loading"
@fetchJobsByStatus="fetchJobsByStatus"
/>
-
- <jobs-filtered-search
- v-if="showFilteredSearch"
- :class="$options.filterSearchBoxStyles"
- @filterJobsBySearch="filterJobsBySearch"
- />
+ <div v-if="showFilteredSearch" :class="$options.filterSearchBoxStyles">
+ <jobs-filtered-search
+ :query-string="validatedQueryString"
+ @filterJobsBySearch="filterJobsBySearch"
+ />
+ </div>
<div v-if="showSkeletonLoader" class="gl-mt-5">
<gl-skeleton-loader :width="1248" :height="73">
diff --git a/app/assets/javascripts/jobs/constants.js b/app/assets/javascripts/jobs/constants.js
index 3040d4e2379..50ee7bd20dd 100644
--- a/app/assets/javascripts/jobs/constants.js
+++ b/app/assets/javascripts/jobs/constants.js
@@ -3,11 +3,17 @@ import { __, s__ } from '~/locale';
const cancel = __('Cancel');
const moreInfo = __('More information');
-export const JOB_SIDEBAR = {
+export const forwardDeploymentFailureModalId = 'forward-deployment-failure';
+
+export const JOB_SIDEBAR_COPY = {
cancel,
+ cancelJobButtonLabel: s__('Job|Cancel'),
debug: __('Debug'),
+ 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'),
toggleSidebar: __('Toggle Sidebar'),
};
diff --git a/app/assets/javascripts/jobs/index.js b/app/assets/javascripts/jobs/index.js
index 5c63ad96ad0..9dd47f4046c 100644
--- a/app/assets/javascripts/jobs/index.js
+++ b/app/assets/javascripts/jobs/index.js
@@ -1,6 +1,6 @@
import { GlToast } from '@gitlab/ui';
import Vue from 'vue';
-import JobApp from './components/job_app.vue';
+import JobApp from './components/job/job_app.vue';
import createStore from './store';
Vue.use(GlToast);
diff --git a/app/assets/javascripts/labels/labels_select.js b/app/assets/javascripts/labels/labels_select.js
index 3e5396c5bd8..51fedac339b 100644
--- a/app/assets/javascripts/labels/labels_select.js
+++ b/app/assets/javascripts/labels/labels_select.js
@@ -247,6 +247,7 @@ export default class LabelsSelect {
}
linkEl.className = selectedClass.join(' ');
+ // eslint-disable-next-line no-unsanitized/property
linkEl.innerHTML = `${colorEl} ${escape(label.title)}`;
const listItemEl = document.createElement('li');
diff --git a/app/assets/javascripts/lib/dateformat.js b/app/assets/javascripts/lib/dateformat.js
new file mode 100644
index 00000000000..1fd95dd03ab
--- /dev/null
+++ b/app/assets/javascripts/lib/dateformat.js
@@ -0,0 +1,60 @@
+import dateFormat, { i18n, masks } from 'dateformat';
+import { s__, __ } from '~/locale';
+
+i18n.dayNames = [
+ __('Sun'),
+ __('Mon'),
+ __('Tue'),
+ __('Wed'),
+ __('Thu'),
+ __('Fri'),
+ __('Sat'),
+ __('Sunday'),
+ __('Monday'),
+ __('Tuesday'),
+ __('Wednesday'),
+ __('Thursday'),
+ __('Friday'),
+ __('Saturday'),
+];
+
+i18n.monthNames = [
+ __('Jan'),
+ __('Feb'),
+ __('Mar'),
+ __('Apr'),
+ __('May'),
+ __('Jun'),
+ __('Jul'),
+ __('Aug'),
+ __('Sep'),
+ __('Oct'),
+ __('Nov'),
+ __('Dec'),
+ __('January'),
+ __('February'),
+ __('March'),
+ __('April'),
+ __('May'),
+ __('June'),
+ __('July'),
+ __('August'),
+ __('September'),
+ __('October'),
+ __('November'),
+ __('December'),
+];
+
+i18n.timeNames = [
+ s__('Time|a'),
+ s__('Time|p'),
+ s__('Time|am'),
+ s__('Time|pm'),
+ s__('Time|A'),
+ s__('Time|P'),
+ s__('Time|AM'),
+ s__('Time|PM'),
+];
+
+export { masks };
+export default dateFormat;
diff --git a/app/assets/javascripts/lib/dompurify.js b/app/assets/javascripts/lib/dompurify.js
index 3e28ca2a0f7..6f24590f9e7 100644
--- a/app/assets/javascripts/lib/dompurify.js
+++ b/app/assets/javascripts/lib/dompurify.js
@@ -1,6 +1,8 @@
-import { sanitize as dompurifySanitize, addHook } from 'dompurify';
+import DOMPurify from 'dompurify';
import { getNormalizedURL, getBaseURL, relativePathToAbsolute } from '~/lib/utils/url_utility';
+const { sanitize: dompurifySanitize, addHook, isValidAttribute } = DOMPurify;
+
const defaultConfig = {
// Safely allow SVG <use> tags
ADD_TAGS: ['use', 'gl-emoji', 'copy-code'],
@@ -94,4 +96,4 @@ addHook('afterSanitizeAttributes', (node) => {
export const sanitize = (val, config) => dompurifySanitize(val, { ...defaultConfig, ...config });
-export { isValidAttribute } from 'dompurify';
+export { isValidAttribute };
diff --git a/app/assets/javascripts/lib/gfm/constants.js b/app/assets/javascripts/lib/gfm/constants.js
new file mode 100644
index 00000000000..eaabeb2a767
--- /dev/null
+++ b/app/assets/javascripts/lib/gfm/constants.js
@@ -0,0 +1,10 @@
+export const TABLE_OF_CONTENTS_DOUBLE_BRACKET_OPEN_TOKEN = '[[';
+export const TABLE_OF_CONTENTS_DOUBLE_BRACKET_MIDDLE_TOKEN = 'TOC';
+export const TABLE_OF_CONTENTS_DOUBLE_BRACKET_CLOSE_TOKEN = ']]';
+export const TABLE_OF_CONTENTS_SINGLE_BRACKET_TOKEN = '[TOC]';
+
+export const MDAST_TEXT_NODE = 'text';
+export const MDAST_EMPHASIS_NODE = 'emphasis';
+export const MDAST_PARAGRAPH_NODE = 'paragraph';
+
+export const GLFM_TABLE_OF_CONTENTS_NODE = 'tableOfContents';
diff --git a/app/assets/javascripts/lib/gfm/glfm_extensions/table_of_contents.js b/app/assets/javascripts/lib/gfm/glfm_extensions/table_of_contents.js
new file mode 100644
index 00000000000..4d2484a657a
--- /dev/null
+++ b/app/assets/javascripts/lib/gfm/glfm_extensions/table_of_contents.js
@@ -0,0 +1,85 @@
+import { first, last } from 'lodash';
+import { u } from 'unist-builder';
+import { visitParents, SKIP, CONTINUE } from 'unist-util-visit-parents';
+import {
+ TABLE_OF_CONTENTS_DOUBLE_BRACKET_CLOSE_TOKEN,
+ TABLE_OF_CONTENTS_DOUBLE_BRACKET_MIDDLE_TOKEN,
+ TABLE_OF_CONTENTS_DOUBLE_BRACKET_OPEN_TOKEN,
+ TABLE_OF_CONTENTS_SINGLE_BRACKET_TOKEN,
+ MDAST_TEXT_NODE,
+ MDAST_EMPHASIS_NODE,
+ MDAST_PARAGRAPH_NODE,
+ GLFM_TABLE_OF_CONTENTS_NODE,
+} from '../constants';
+
+const isTOCTextNode = ({ type, value }) =>
+ type === MDAST_TEXT_NODE && value === TABLE_OF_CONTENTS_DOUBLE_BRACKET_MIDDLE_TOKEN;
+
+const isTOCEmphasisNode = ({ type, children }) =>
+ type === MDAST_EMPHASIS_NODE && children.length === 1 && isTOCTextNode(first(children));
+
+const isTOCDoubleSquareBracketOpenTokenTextNode = ({ type, value }) =>
+ type === MDAST_TEXT_NODE && value.trim() === TABLE_OF_CONTENTS_DOUBLE_BRACKET_OPEN_TOKEN;
+
+const isTOCDoubleSquareBracketCloseTokenTextNode = ({ type, value }) =>
+ type === MDAST_TEXT_NODE && value.trim() === TABLE_OF_CONTENTS_DOUBLE_BRACKET_CLOSE_TOKEN;
+
+/*
+ * Detects table of contents declaration with syntax [[_TOC_]]
+ */
+const isTableOfContentsDoubleSquareBracketSyntax = ({ children }) => {
+ if (children.length !== 3) {
+ return false;
+ }
+
+ const [firstChild, middleChild, lastChild] = children;
+
+ return (
+ isTOCDoubleSquareBracketOpenTokenTextNode(firstChild) &&
+ isTOCEmphasisNode(middleChild) &&
+ isTOCDoubleSquareBracketCloseTokenTextNode(lastChild)
+ );
+};
+
+/*
+ * Detects table of contents declaration with syntax [TOC]
+ */
+const isTableOfContentsSingleSquareBracketSyntax = ({ children }) => {
+ if (children.length !== 1) {
+ return false;
+ }
+
+ const [firstChild] = children;
+ const { type, value } = firstChild;
+
+ return type === MDAST_TEXT_NODE && value.trim() === TABLE_OF_CONTENTS_SINGLE_BRACKET_TOKEN;
+};
+
+const isTableOfContentsNode = (node) =>
+ node.type === MDAST_PARAGRAPH_NODE &&
+ (isTableOfContentsDoubleSquareBracketSyntax(node) ||
+ isTableOfContentsSingleSquareBracketSyntax(node));
+
+export default () => {
+ return (tree) => {
+ visitParents(tree, (node, ancestors) => {
+ const parent = last(ancestors);
+
+ if (!parent) {
+ return CONTINUE;
+ }
+
+ if (isTableOfContentsNode(node)) {
+ const index = parent.children.indexOf(node);
+
+ parent.children[index] = u(GLFM_TABLE_OF_CONTENTS_NODE, {
+ position: node.position,
+ });
+ }
+
+ return SKIP;
+ });
+
+ return tree;
+ };
+};
diff --git a/app/assets/javascripts/lib/gfm/index.js b/app/assets/javascripts/lib/gfm/index.js
index eaf653e9924..fad73f93c1a 100644
--- a/app/assets/javascripts/lib/gfm/index.js
+++ b/app/assets/javascripts/lib/gfm/index.js
@@ -6,6 +6,8 @@ import remarkFrontmatter from 'remark-frontmatter';
import remarkGfm from 'remark-gfm';
import remarkRehype, { all } from 'remark-rehype';
import rehypeRaw from 'rehype-raw';
+import glfmTableOfContents from './glfm_extensions/table_of_contents';
+import * as glfmMdastToHastHandlers from './mdast_to_hast_handlers/glfm_mdast_to_hast_handlers';
const skipFrontmatterHandler = (language) => (h, node) =>
h(node.position, 'frontmatter', { language }, [{ type: 'text', value: node.value }]);
@@ -65,19 +67,22 @@ const skipRenderingHandlers = {
all(h, node),
);
},
+ tableOfContents: (h, node) => h(node.position, 'tableOfContents'),
toml: skipFrontmatterHandler('toml'),
yaml: skipFrontmatterHandler('yaml'),
json: skipFrontmatterHandler('json'),
};
-const createParser = ({ skipRendering = [] }) => {
+const createParser = ({ skipRendering }) => {
return unified()
.use(remarkParse)
.use(remarkGfm)
.use(remarkFrontmatter, ['yaml', 'toml', { type: 'json', marker: ';' }])
+ .use(glfmTableOfContents)
.use(remarkRehype, {
allowDangerousHtml: true,
handlers: {
+ ...glfmMdastToHastHandlers,
...pick(skipRenderingHandlers, skipRendering),
},
})
@@ -99,13 +104,13 @@ const compilerFactory = (renderer) =>
* tree in any desired representation
*
* @param {String} params.markdown Markdown to parse
- * @param {(tree: MDast -> any)} params.renderer A function that accepts mdast
+ * @param {Function} params.renderer A function that accepts mdast
* AST tree and returns an object of any type that represents the result of
* rendering the tree. See the references below to for more information
* about MDast.
*
* MDastTree documentation https://github.com/syntax-tree/mdast
- * @returns {Promise<any>} Returns a promise with the result of rendering
+ * @returns {Promise} Returns a promise with the result of rendering
* the MDast tree
*/
export const render = async ({ markdown, renderer, skipRendering = [] }) => {
diff --git a/app/assets/javascripts/lib/gfm/mdast_to_hast_handlers/glfm_mdast_to_hast_handlers.js b/app/assets/javascripts/lib/gfm/mdast_to_hast_handlers/glfm_mdast_to_hast_handlers.js
new file mode 100644
index 00000000000..91b09e69405
--- /dev/null
+++ b/app/assets/javascripts/lib/gfm/mdast_to_hast_handlers/glfm_mdast_to_hast_handlers.js
@@ -0,0 +1 @@
+export const tableOfContents = (h, node) => h(node.position, 'nav');
diff --git a/app/assets/javascripts/lib/mermaid.js b/app/assets/javascripts/lib/mermaid.js
index d621c9ddf9e..c72561ce69d 100644
--- a/app/assets/javascripts/lib/mermaid.js
+++ b/app/assets/javascripts/lib/mermaid.js
@@ -9,6 +9,7 @@ const setIframeRenderedSize = (h, w) => {
const drawDiagram = (source) => {
const element = document.getElementById('app');
const insertSvg = (svgCode) => {
+ // eslint-disable-next-line no-unsanitized/property
element.innerHTML = svgCode;
const height = parseInt(element.firstElementChild.getAttribute('height'), 10);
diff --git a/app/assets/javascripts/lib/utils/datetime/date_calculation_utility.js b/app/assets/javascripts/lib/utils/datetime/date_calculation_utility.js
index 4e7086e62c5..6c5d4ecc901 100644
--- a/app/assets/javascripts/lib/utils/datetime/date_calculation_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime/date_calculation_utility.js
@@ -142,9 +142,16 @@ export const dayInQuarter = (date, quarter) => {
export const millisecondsPerDay = 1000 * 60 * 60 * 24;
-export const getDayDifference = (a, b) => {
- const date1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
- const date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
+/**
+ * Calculates the number of days between 2 specified dates, excluding the current date
+ *
+ * @param {Date} startDate the earlier date that we will substract from the end date
+ * @param {Date} endDate the last date in the range
+ * @return {Number} number of days in between
+ */
+export const getDayDifference = (startDate, endDate) => {
+ const date1 = Date.UTC(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
+ const date2 = Date.UTC(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
return Math.floor((date2 - date1) / millisecondsPerDay);
};
@@ -208,6 +215,19 @@ export const newDateAsLocaleTime = (date) => {
return new Date(`${date}${suffix}`);
};
+/**
+ * Takes a Date object (where timezone could be GMT or EST) and
+ * returns a Date object with the same date but in UTC.
+ *
+ * @param {Date} date A Date object
+ * @returns {Date|null} A Date object with the same date but in UTC
+ */
+export const getDateWithUTC = (date) => {
+ return date instanceof Date
+ ? new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()))
+ : null;
+};
+
export const beginOfDayTime = 'T00:00:00Z';
export const endOfDayTime = 'T23:59:59Z';
diff --git a/app/assets/javascripts/lib/utils/datetime/date_format_utility.js b/app/assets/javascripts/lib/utils/datetime/date_format_utility.js
index 830f4604382..d07abb72210 100644
--- a/app/assets/javascripts/lib/utils/datetime/date_format_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime/date_format_utility.js
@@ -1,5 +1,5 @@
-import dateFormat from 'dateformat';
import { isString, mapValues, reduce, isDate, unescape } from 'lodash';
+import dateFormat from '~/lib/dateformat';
import { roundToNearestHalf } from '~/lib/utils/common_utils';
import { sanitize } from '~/lib/dompurify';
import { s__, n__, __, sprintf } from '~/locale';
diff --git a/app/assets/javascripts/lib/utils/datetime_range.js b/app/assets/javascripts/lib/utils/datetime_range.js
index 840cc4600fe..548f5a438df 100644
--- a/app/assets/javascripts/lib/utils/datetime_range.js
+++ b/app/assets/javascripts/lib/utils/datetime_range.js
@@ -1,5 +1,5 @@
-import dateformat from 'dateformat';
import { pick, omit, isEqual, isEmpty } from 'lodash';
+import dateformat from '~/lib/dateformat';
import { DATETIME_RANGE_TYPES } from './constants';
import { secondsToMilliseconds } from './datetime_utility';
diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js
index 9f4e12a3010..48be8af3ff6 100644
--- a/app/assets/javascripts/lib/utils/text_markdown.js
+++ b/app/assets/javascripts/lib/utils/text_markdown.js
@@ -263,10 +263,12 @@ export function insertMarkdownText({
if (tag === LINK_TAG_PATTERN) {
if (URL) {
try {
- new URL(selected); // eslint-disable-line no-new
- // valid url
- tag = '[text]({text})';
- select = 'text';
+ const url = new URL(selected);
+
+ if (url.origin !== 'null' || url.origin === null) {
+ tag = '[text]({text})';
+ select = 'text';
+ }
} catch (e) {
// ignore - no valid url
}
diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js
index 7b00995b2e5..59645d50e29 100644
--- a/app/assets/javascripts/lib/utils/text_utility.js
+++ b/app/assets/javascripts/lib/utils/text_utility.js
@@ -119,6 +119,7 @@ const getAverageCharWidth = memoize(function getAverageCharWidth(options = {}) {
div.style.left = -1000;
div.style.top = -1000;
+ // eslint-disable-next-line no-unsanitized/property
div.innerHTML = chars;
document.body.appendChild(div);
diff --git a/app/assets/javascripts/linked_resources/index.js b/app/assets/javascripts/linked_resources/index.js
index 6d799d30b4b..f1d3026c2f1 100644
--- a/app/assets/javascripts/linked_resources/index.js
+++ b/app/assets/javascripts/linked_resources/index.js
@@ -22,7 +22,7 @@ export default function initLinkedResources() {
name: 'LinkedResourcesRoot',
apolloProvider,
components: {
- resourceLinksBlock: ResourceLinksBlock,
+ ResourceLinksBlock,
},
render: (createElement) =>
createElement('resource-links-block', {
diff --git a/app/assets/javascripts/locale/sprintf.js b/app/assets/javascripts/locale/sprintf.js
index e1749331d90..c8c6b51f374 100644
--- a/app/assets/javascripts/locale/sprintf.js
+++ b/app/assets/javascripts/locale/sprintf.js
@@ -14,6 +14,8 @@ import { escape } from 'lodash';
export default (input, parameters, escapeParameters = true) => {
let output = input;
+ output = output.replace(/%+/g, '%');
+
if (parameters) {
const mappedParameters = new Map(Object.entries(parameters));
diff --git a/app/assets/javascripts/members/components/modals/remove_member_modal.vue b/app/assets/javascripts/members/components/modals/remove_member_modal.vue
index b82fb0030ff..1bb1f90302c 100644
--- a/app/assets/javascripts/members/components/modals/remove_member_modal.vue
+++ b/app/assets/javascripts/members/components/modals/remove_member_modal.vue
@@ -88,7 +88,8 @@ export default {
:action-primary="actionPrimary"
:title="actionText"
:visible="removeMemberModalVisible"
- data-qa-selector="remove_member_modal_content"
+ data-qa-selector="remove_member_modal"
+ data-testid="remove-member-modal-content"
@primary="submitForm"
@hide="hideRemoveMemberModal"
>
diff --git a/app/assets/javascripts/members/constants.js b/app/assets/javascripts/members/constants.js
index 93d113d1afe..3135ec602be 100644
--- a/app/assets/javascripts/members/constants.js
+++ b/app/assets/javascripts/members/constants.js
@@ -196,3 +196,8 @@ export const MEMBER_ACCESS_LEVEL_PROPERTY_NAME = 'access_level';
export const GROUP_LINK_BASE_PROPERTY_NAME = 'group_link';
export const GROUP_LINK_ACCESS_LEVEL_PROPERTY_NAME = 'group_access';
+
+export const I18N_USER_YOU = __("It's you");
+export const I18N_USER_BLOCKED = __('Blocked');
+export const I18N_USER_BOT = __('Bot');
+export const I188N_USER_2FA = __('2FA');
diff --git a/app/assets/javascripts/members/utils.js b/app/assets/javascripts/members/utils.js
index 7ec083646e9..0da44b7d468 100644
--- a/app/assets/javascripts/members/utils.js
+++ b/app/assets/javascripts/members/utils.js
@@ -1,28 +1,36 @@
import { isUndefined } from 'lodash';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { getParameterByName, setUrlParams } from '~/lib/utils/url_utility';
-import { __ } from '~/locale';
import {
FIELDS,
DEFAULT_SORT,
GROUP_LINK_BASE_PROPERTY_NAME,
GROUP_LINK_ACCESS_LEVEL_PROPERTY_NAME,
+ I18N_USER_YOU,
+ I18N_USER_BLOCKED,
+ I18N_USER_BOT,
+ I188N_USER_2FA,
} from './constants';
export const generateBadges = ({ member, isCurrentUser, canManageMembers }) => [
{
show: isCurrentUser,
- text: __("It's you"),
+ text: I18N_USER_YOU,
variant: 'success',
},
{
show: member.user?.blocked,
- text: __('Blocked'),
+ text: I18N_USER_BLOCKED,
variant: 'danger',
},
{
+ show: member.user?.isBot,
+ text: I18N_USER_BOT,
+ variant: 'muted',
+ },
+ {
show: member.user?.twoFactorEnabled && (canManageMembers || isCurrentUser),
- text: __('2FA'),
+ text: I188N_USER_2FA,
variant: 'info',
},
];
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index ed2e6a5af58..0b53a8ede64 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -434,6 +434,7 @@ export default class MergeRequestTabs {
.get(`${source}.json`)
.then(({ data }) => {
const commitsDiv = document.querySelector('div#commits');
+ // eslint-disable-next-line no-unsanitized/property
commitsDiv.innerHTML = data.html;
localTimeAgo(commitsDiv.querySelectorAll('.js-timeago'));
this.commitsLoaded = true;
diff --git a/app/assets/javascripts/merge_requests/components/sticky_header.vue b/app/assets/javascripts/merge_requests/components/sticky_header.vue
new file mode 100644
index 00000000000..f067982fce1
--- /dev/null
+++ b/app/assets/javascripts/merge_requests/components/sticky_header.vue
@@ -0,0 +1,180 @@
+<script>
+import {
+ GlIntersectionObserver,
+ GlLink,
+ GlSprintf,
+ GlBadge,
+ GlSafeHtmlDirective,
+} from '@gitlab/ui';
+import { mapGetters, mapState } from 'vuex';
+import { TYPE_MERGE_REQUEST } from '~/graphql_shared/constants';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import { isLoggedIn } from '~/lib/utils/common_utils';
+import StatusBox from '~/issuable/components/status_box.vue';
+import DiscussionCounter from '~/notes/components/discussion_counter.vue';
+import TodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+
+export default {
+ components: {
+ GlIntersectionObserver,
+ GlLink,
+ GlSprintf,
+ GlBadge,
+ StatusBox,
+ DiscussionCounter,
+ TodoWidget,
+ ClipboardButton,
+ },
+ directives: {
+ SafeHtml: GlSafeHtmlDirective,
+ },
+ mixins: [glFeatureFlagsMixin()],
+ inject: {
+ projectPath: { default: null },
+ title: { default: '' },
+ tabs: { default: () => [] },
+ isFluidLayout: { default: false },
+ },
+ data() {
+ return {
+ isStickyHeaderVisible: false,
+ discussionCounter: 0,
+ };
+ },
+ computed: {
+ ...mapGetters(['getNoteableData', 'discussionTabCounter']),
+ ...mapState({
+ activeTab: (state) => state.page.activeTab,
+ doneFetchingBatchDiscussions: (state) => state.notes.doneFetchingBatchDiscussions,
+ }),
+ issuableId() {
+ return convertToGraphQLId(TYPE_MERGE_REQUEST, this.getNoteableData.id);
+ },
+ issuableIid() {
+ return `${this.getNoteableData.iid}`;
+ },
+ isSignedIn() {
+ return isLoggedIn();
+ },
+ },
+ watch: {
+ discussionTabCounter(val) {
+ if (this.glFeatures.paginatedMrDiscussions) {
+ if (this.doneFetchingBatchDiscussions) {
+ this.discussionCounter = val;
+ }
+ } else {
+ this.discussionCounter = val;
+ }
+ },
+ },
+ methods: {
+ setStickyHeaderVisible(val) {
+ this.isStickyHeaderVisible = val;
+ },
+ visitTab(e) {
+ window.mrTabs?.clickTab(e);
+ },
+ },
+ safeHtmlConfig: {
+ ADD_TAGS: ['gl-emoji'],
+ },
+};
+</script>
+
+<template>
+ <gl-intersection-observer
+ @appear="setStickyHeaderVisible(false)"
+ @disappear="setStickyHeaderVisible(true)"
+ >
+ <div
+ v-if="isStickyHeaderVisible"
+ class="issue-sticky-header merge-request-sticky-header gl-fixed gl-bg-white gl-border-1 gl-border-b-solid gl-border-b-gray-100 gl-pt-3 gl-display-none gl-md-display-block"
+ >
+ <div
+ class="issue-sticky-header-text gl-display-flex gl-flex-direction-column gl-align-items-center gl-mx-auto gl-px-5"
+ :class="{ 'gl-max-w-container-xl': !isFluidLayout }"
+ >
+ <div class="gl-w-full gl-display-flex gl-align-items-center">
+ <status-box :initial-state="getNoteableData.state" issuable-type="merge_request" />
+ <p
+ v-safe-html:[$options.safeHtmlConfig]="title"
+ class="gl-display-none gl-lg-display-block gl-font-weight-bold gl-overflow-hidden gl-white-space-nowrap gl-text-overflow-ellipsis gl-my-0 gl-mr-4"
+ ></p>
+ <div class="gl-display-flex gl-align-items-center">
+ <gl-sprintf :message="__('%{source} %{copyButton} into %{target}')">
+ <template #copyButton>
+ <clipboard-button
+ :text="getNoteableData.source_branch"
+ :title="__('Copy branch name')"
+ size="small"
+ category="tertiary"
+ tooltip-placement="bottom"
+ class="gl-m-0! gl-mx-1! js-source-branch-copy"
+ />
+ </template>
+ <template #source>
+ <gl-link
+ :title="getNoteableData.source_branch"
+ :href="getNoteableData.source_branch_path"
+ class="gl-text-blue-500! gl-font-monospace gl-bg-blue-50 gl-rounded-base gl-font-sm gl-px-2 gl-text-truncate gl-max-w-26"
+ >
+ {{ getNoteableData.source_branch }}
+ </gl-link>
+ </template>
+ <template #target>
+ <gl-link
+ :title="getNoteableData.target_branch"
+ :href="getNoteableData.target_branch_path"
+ class="gl-text-blue-500! gl-font-monospace gl-bg-blue-50 gl-rounded-base gl-font-sm gl-px-2 gl-text-truncate gl-max-w-26 gl-ml-2"
+ >
+ {{ getNoteableData.target_branch }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </div>
+ </div>
+ <div class="gl-w-full gl-display-flex">
+ <ul
+ class="merge-request-tabs nav-tabs nav nav-links gl-display-flex gl-flex-nowrap gl-m-0 gl-p-0 gl-border-b-0"
+ >
+ <li
+ v-for="(tab, index) in tabs"
+ :key="tab[0]"
+ :class="{ active: activeTab === tab[0] }"
+ >
+ <gl-link
+ :href="tab[2]"
+ :data-action="tab[0]"
+ class="gl-outline-0! gl-py-4!"
+ @click="visitTab"
+ >
+ {{ tab[1] }}
+ <gl-badge variant="muted" size="sm">
+ <template v-if="index === 0 && discussionCounter !== 0">
+ {{ discussionCounter }}
+ </template>
+ <template v-else>
+ {{ tab[3] }}
+ </template>
+ </gl-badge>
+ </gl-link>
+ </li>
+ </ul>
+ <div class="gl-display-none gl-lg-display-flex gl-align-items-center gl-ml-auto">
+ <discussion-counter blocks-merge hide-options />
+ <todo-widget
+ v-if="isSignedIn"
+ :issuable-id="issuableId"
+ :issuable-iid="issuableIid"
+ :full-path="projectPath"
+ issuable-type="merge_request"
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ </gl-intersection-observer>
+</template>
diff --git a/app/assets/javascripts/milestones/components/milestone_combobox.vue b/app/assets/javascripts/milestones/components/milestone_combobox.vue
index 59d2a2b29b3..5c3b969655b 100644
--- a/app/assets/javascripts/milestones/components/milestone_combobox.vue
+++ b/app/assets/javascripts/milestones/components/milestone_combobox.vue
@@ -243,7 +243,7 @@ export default {
v-for="(item, idx) in extraLinks"
:key="idx"
:href="item.url"
- :is-check-item="true"
+ is-check-item
data-testid="milestone-combobox-extra-links"
>
{{ item.text }}
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index 250d4b3c55f..e3fcdf716d4 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -391,7 +391,11 @@ export default {
};
</script>
<template>
- <div class="prometheus-graphs" data-qa-selector="prometheus_graphs">
+ <div
+ class="prometheus-graphs"
+ data-qa-selector="prometheus_graphs_content"
+ data-testid="prometheus-graphs"
+ >
<div>
<gl-alert
v-if="!isDeprecationNoticeDismissed"
diff --git a/app/assets/javascripts/monitoring/components/dashboard_header.vue b/app/assets/javascripts/monitoring/components/dashboard_header.vue
index 3338635bf96..90d2498ac19 100644
--- a/app/assets/javascripts/monitoring/components/dashboard_header.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard_header.vue
@@ -189,6 +189,7 @@ export default {
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"
:text="environmentDropdownText"
@@ -202,7 +203,7 @@ export default {
<gl-dropdown-item
v-for="environment in filteredEnvironments"
:key="environment.id"
- :is-check-item="true"
+ is-check-item
:is-checked="environment.name === currentEnvironmentName"
:href="getEnvironmentPath(environment.id)"
>
diff --git a/app/assets/javascripts/monitoring/components/dashboards_dropdown.vue b/app/assets/javascripts/monitoring/components/dashboards_dropdown.vue
index 568c66cf152..7fae684315c 100644
--- a/app/assets/javascripts/monitoring/components/dashboards_dropdown.vue
+++ b/app/assets/javascripts/monitoring/components/dashboards_dropdown.vue
@@ -86,7 +86,7 @@ export default {
<gl-dropdown-item
v-for="dashboard in starredDashboards"
:key="dashboard.path"
- :is-check-item="true"
+ is-check-item
:is-checked="dashboard.path === selectedDashboardPath"
@click="selectDashboard(dashboard)"
>
@@ -105,7 +105,7 @@ export default {
<gl-dropdown-item
v-for="dashboard in nonStarredDashboards"
:key="dashboard.path"
- :is-check-item="true"
+ is-check-item
:is-checked="dashboard.path === selectedDashboardPath"
@click="selectDashboard(dashboard)"
>
diff --git a/app/assets/javascripts/monitoring/components/refresh_button.vue b/app/assets/javascripts/monitoring/components/refresh_button.vue
index 0b80043a92c..544fe10f26e 100644
--- a/app/assets/javascripts/monitoring/components/refresh_button.vue
+++ b/app/assets/javascripts/monitoring/components/refresh_button.vue
@@ -163,7 +163,7 @@ export default {
:text="dropdownText"
>
<gl-dropdown-item
- :is-check-item="true"
+ is-check-item
:is-checked="refreshInterval === null"
@click="removeRefreshInterval()"
>{{ __('Off') }}</gl-dropdown-item
@@ -172,7 +172,7 @@ export default {
<gl-dropdown-item
v-for="(option, i) in $options.refreshIntervals"
:key="i"
- :is-check-item="true"
+ is-check-item
:is-checked="isChecked(option)"
@click="setRefreshInterval(option)"
>{{ option.label }}</gl-dropdown-item
diff --git a/app/assets/javascripts/monitoring/format_date.js b/app/assets/javascripts/monitoring/format_date.js
index c7bc626eb11..f20fea48084 100644
--- a/app/assets/javascripts/monitoring/format_date.js
+++ b/app/assets/javascripts/monitoring/format_date.js
@@ -1,4 +1,4 @@
-import dateFormat from 'dateformat';
+import dateFormat from '~/lib/dateformat';
export const timezones = {
/**
diff --git a/app/assets/javascripts/mr_notes/index.js b/app/assets/javascripts/mr_notes/index.js
index 7424c011052..297420bf94d 100644
--- a/app/assets/javascripts/mr_notes/index.js
+++ b/app/assets/javascripts/mr_notes/index.js
@@ -5,9 +5,8 @@ import initRevertCommitModal from '~/projects/commit/init_revert_commit_modal';
import initDiffsApp from '../diffs';
import { resetServiceWorkersPublicPath } from '../lib/utils/webpack';
import MergeRequest from '../merge_request';
-import discussionCounter from '../notes/components/discussion_counter.vue';
+import DiscussionCounter from '../notes/components/discussion_counter.vue';
import initDiscussionFilters from '../notes/discussion_filters';
-import initSortDiscussions from '../notes/sort_discussions';
import initNotesApp from './init_notes';
export default function initMrNotes() {
@@ -38,7 +37,7 @@ export default function initMrNotes() {
el,
name: 'DiscussionCounter',
components: {
- discussionCounter,
+ DiscussionCounter,
},
store,
render(createElement) {
@@ -52,6 +51,5 @@ export default function initMrNotes() {
}
initDiscussionFilters(store);
- initSortDiscussions(store);
});
}
diff --git a/app/assets/javascripts/mr_notes/init_notes.js b/app/assets/javascripts/mr_notes/init_notes.js
index cf24d18c7b6..e4a7a7bd9fc 100644
--- a/app/assets/javascripts/mr_notes/init_notes.js
+++ b/app/assets/javascripts/mr_notes/init_notes.js
@@ -4,7 +4,7 @@ import { mapActions, mapState, mapGetters } from 'vuex';
import { parseBoolean } from '~/lib/utils/common_utils';
import store from '~/mr_notes/stores';
import discussionNavigator from '../notes/components/discussion_navigator.vue';
-import notesApp from '../notes/components/notes_app.vue';
+import NotesApp from '../notes/components/notes_app.vue';
import initWidget from '../vue_merge_request_widget';
export default () => {
@@ -13,7 +13,7 @@ export default () => {
el: '#js-vue-mr-discussions',
name: 'MergeRequestDiscussions',
components: {
- notesApp,
+ NotesApp,
},
store,
data() {
diff --git a/app/assets/javascripts/nav/components/top_nav_app.vue b/app/assets/javascripts/nav/components/top_nav_app.vue
index 08a2c6952c8..ca6e6567f74 100644
--- a/app/assets/javascripts/nav/components/top_nav_app.vue
+++ b/app/assets/javascripts/nav/components/top_nav_app.vue
@@ -1,14 +1,18 @@
<script>
-import { GlNav, GlNavItemDropdown, GlDropdownForm } from '@gitlab/ui';
+import { GlNav, GlIcon, GlNavItemDropdown, GlDropdownForm, GlTooltipDirective } from '@gitlab/ui';
import TopNavDropdownMenu from './top_nav_dropdown_menu.vue';
export default {
components: {
+ GlIcon,
GlNav,
GlNavItemDropdown,
GlDropdownForm,
TopNavDropdownMenu,
},
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
props: {
navData: {
type: Object,
@@ -21,15 +25,20 @@ export default {
<template>
<gl-nav class="navbar-sub-nav">
<gl-nav-item-dropdown
- :text="navData.activeTitle"
+ v-gl-tooltip.bottom="navData.menuTooltip"
data-qa-selector="navbar_dropdown"
- :data-qa-title="navData.activeTitle"
- icon="hamburger"
+ data-qa-title="Menu"
menu-class="gl-mt-3! gl-max-w-none! gl-max-h-none! gl-sm-w-auto! js-top-nav-dropdown-menu"
toggle-class="top-nav-toggle js-top-nav-dropdown-toggle gl-px-3!"
no-flip
no-caret
>
+ <template #button-content>
+ <gl-icon name="hamburger" />
+ <span v-if="navData.menuTitle" class="gl-ml-3">
+ {{ navData.menuTitle }}
+ </span>
+ </template>
<gl-dropdown-form>
<top-nav-dropdown-menu
:primary="navData.primary"
diff --git a/app/assets/javascripts/nav/components/top_nav_menu_sections.vue b/app/assets/javascripts/nav/components/top_nav_menu_sections.vue
index b8555df53df..97e63c7324e 100644
--- a/app/assets/javascripts/nav/components/top_nav_menu_sections.vue
+++ b/app/assets/javascripts/nav/components/top_nav_menu_sections.vue
@@ -49,15 +49,26 @@ export default {
:class="getMenuSectionClasses(sectionIndex)"
data-testid="menu-section"
>
- <top-nav-menu-item
- v-for="(menuItem, menuItemIndex) in menuItems"
- :key="menuItem.id"
- :menu-item="menuItem"
- data-testid="menu-item"
- class="gl-w-full"
- :class="{ 'gl-mt-1': menuItemIndex > 0 }"
- @click="onClick(menuItem)"
- />
+ <template v-for="(menuItem, menuItemIndex) in menuItems">
+ <strong
+ v-if="menuItem.type == 'header'"
+ :key="menuItem.title"
+ class="gl-px-4 gl-py-2 gl-text-gray-900 gl-display-block"
+ :class="{ 'gl-pt-3!': menuItemIndex > 0 }"
+ data-testid="menu-header"
+ >
+ {{ menuItem.title }}
+ </strong>
+ <top-nav-menu-item
+ v-else
+ :key="menuItem.id"
+ :menu-item="menuItem"
+ data-testid="menu-item"
+ class="gl-w-full"
+ :class="{ 'gl-mt-1': menuItemIndex > 0 }"
+ @click="onClick(menuItem)"
+ />
+ </template>
</div>
</div>
</template>
diff --git a/app/assets/javascripts/notebook/cells/code.vue b/app/assets/javascripts/notebook/cells/code.vue
index f5a6f3a9817..bc1bab62553 100644
--- a/app/assets/javascripts/notebook/cells/code.vue
+++ b/app/assets/javascripts/notebook/cells/code.vue
@@ -13,11 +13,6 @@ export default {
type: Object,
required: true,
},
- codeCssClass: {
- type: String,
- required: false,
- default: '',
- },
},
computed: {
rawInputCode() {
@@ -39,18 +34,12 @@ export default {
<template>
<div class="cell">
- <code-output
- :raw-code="rawInputCode"
- :count="cell.execution_count"
- :code-css-class="codeCssClass"
- type="input"
- />
+ <code-output :raw-code="rawInputCode" :count="cell.execution_count" type="input" />
<output-cell
v-if="hasOutput"
:count="cell.execution_count"
:outputs="outputs"
:metadata="cell.metadata"
- :code-css-class="codeCssClass"
/>
</div>
</template>
diff --git a/app/assets/javascripts/notebook/cells/code/index.vue b/app/assets/javascripts/notebook/cells/code/index.vue
index e1ef9aa6d79..64e801a7516 100644
--- a/app/assets/javascripts/notebook/cells/code/index.vue
+++ b/app/assets/javascripts/notebook/cells/code/index.vue
@@ -1,10 +1,11 @@
<script>
-import Prism from '../../lib/highlight';
+import CodeBlockHighlighted from '~/vue_shared/components/code_block_highlighted.vue';
import Prompt from '../prompt.vue';
export default {
name: 'CodeOutput',
components: {
+ CodeBlockHighlighted,
Prompt,
},
props: {
@@ -13,11 +14,6 @@ export default {
required: false,
default: 0,
},
- codeCssClass: {
- type: String,
- required: false,
- default: '',
- },
type: {
type: String,
required: true,
@@ -41,22 +37,21 @@ export default {
return type.charAt(0).toUpperCase() + type.slice(1);
},
- cellCssClass() {
- return {
- [this.codeCssClass]: true,
- 'jupyter-notebook-scrolled': this.metadata.scrolled,
- };
+ maxHeight() {
+ return this.metadata.scrolled ? '20rem' : 'initial';
},
},
- mounted() {
- Prism.highlightElement(this.$refs.code);
- },
};
</script>
<template>
<div :class="type">
<prompt :type="promptType" :count="count" />
- <pre ref="code" :class="cellCssClass" class="language-python" v-text="code"></pre>
+ <code-block-highlighted
+ language="python"
+ :code="code"
+ :max-height="maxHeight"
+ class="gl-border"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/notebook/cells/markdown.vue b/app/assets/javascripts/notebook/cells/markdown.vue
index 8351ae7ced6..127e046b5a9 100644
--- a/app/assets/javascripts/notebook/cells/markdown.vue
+++ b/app/assets/javascripts/notebook/cells/markdown.vue
@@ -137,7 +137,7 @@ marked.setOptions({
export default {
components: {
- prompt: Prompt,
+ Prompt,
},
directives: {
SafeHtml,
diff --git a/app/assets/javascripts/notebook/cells/output/image.vue b/app/assets/javascripts/notebook/cells/output/image.vue
index 065f5def83c..da7d83539d3 100644
--- a/app/assets/javascripts/notebook/cells/output/image.vue
+++ b/app/assets/javascripts/notebook/cells/output/image.vue
@@ -3,7 +3,7 @@ import Prompt from '../prompt.vue';
export default {
components: {
- prompt: Prompt,
+ Prompt,
},
props: {
count: {
diff --git a/app/assets/javascripts/notebook/cells/output/index.vue b/app/assets/javascripts/notebook/cells/output/index.vue
index 5f7ef4a4377..88d01ffa659 100644
--- a/app/assets/javascripts/notebook/cells/output/index.vue
+++ b/app/assets/javascripts/notebook/cells/output/index.vue
@@ -6,11 +6,6 @@ import LatexOutput from './latex.vue';
export default {
props: {
- codeCssClass: {
- type: String,
- required: false,
- default: '',
- },
count: {
type: Number,
required: false,
@@ -96,7 +91,6 @@ export default {
:index="index"
:raw-code="rawCode(output)"
:metadata="metadata"
- :code-css-class="codeCssClass"
/>
</div>
</template>
diff --git a/app/assets/javascripts/notebook/index.vue b/app/assets/javascripts/notebook/index.vue
index 44dc1856e49..df9694b7cd8 100644
--- a/app/assets/javascripts/notebook/index.vue
+++ b/app/assets/javascripts/notebook/index.vue
@@ -11,11 +11,6 @@ export default {
type: Object,
required: true,
},
- codeCssClass: {
- type: String,
- required: false,
- default: '',
- },
},
computed: {
cells() {
@@ -52,7 +47,6 @@ export default {
v-for="(cell, index) in cells"
:key="index"
:cell="cell"
- :code-css-class="codeCssClass"
/>
</div>
</template>
diff --git a/app/assets/javascripts/notebook/lib/highlight.js b/app/assets/javascripts/notebook/lib/highlight.js
deleted file mode 100644
index 313aeecbd51..00000000000
--- a/app/assets/javascripts/notebook/lib/highlight.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import Prism from 'prismjs';
-import 'prismjs/components/prism-python';
-import 'prismjs/themes/prism.css';
-
-export default Prism;
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
index bd5945a951b..bf35d5c3b25 100644
--- a/app/assets/javascripts/notes/components/comment_form.vue
+++ b/app/assets/javascripts/notes/components/comment_form.vue
@@ -14,7 +14,7 @@ import {
slugifyWithUnderscore,
} from '~/lib/utils/text_utility';
import { sprintf } from '~/locale';
-import markdownField from '~/vue_shared/components/markdown/field.vue';
+import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
@@ -25,8 +25,8 @@ import { COMMENT_FORM } from '../i18n';
import issuableStateMixin from '../mixins/issuable_state';
import CommentFieldLayout from './comment_field_layout.vue';
import CommentTypeDropdown from './comment_type_dropdown.vue';
-import discussionLockedWidget from './discussion_locked_widget.vue';
-import noteSignedOutWidget from './note_signed_out_widget.vue';
+import DiscussionLockedWidget from './discussion_locked_widget.vue';
+import NoteSignedOutWidget from './note_signed_out_widget.vue';
const { UNPROCESSABLE_ENTITY } = httpStatusCodes;
@@ -34,9 +34,9 @@ export default {
name: 'CommentForm',
i18n: COMMENT_FORM,
components: {
- noteSignedOutWidget,
- discussionLockedWidget,
- markdownField,
+ NoteSignedOutWidget,
+ DiscussionLockedWidget,
+ MarkdownField,
GlAlert,
GlButton,
TimelineEntryItem,
@@ -214,11 +214,7 @@ export default {
note: {
noteable_type: this.noteableType,
noteable_id: this.getNoteableData.id,
- // Internal notes were identified as `confidential`
- // before we decided to treat them as _internal_
- // so now until API is updated we need to use `confidential`
- // in request payload.
- confidential: this.noteIsInternal,
+ internal: this.noteIsInternal,
note: this.note,
},
merge_request_diff_head_sha: this.getNoteableData.diff_head_sha,
diff --git a/app/assets/javascripts/notes/components/diff_discussion_header.vue b/app/assets/javascripts/notes/components/diff_discussion_header.vue
index 3cf47f42e0c..1b1923a90f7 100644
--- a/app/assets/javascripts/notes/components/diff_discussion_header.vue
+++ b/app/assets/javascripts/notes/components/diff_discussion_header.vue
@@ -4,16 +4,16 @@ import { escape } from 'lodash';
import { mapActions } from 'vuex';
import { truncateSha } from '~/lib/utils/text_utility';
import { s__, __, sprintf } from '~/locale';
-import noteEditedText from './note_edited_text.vue';
-import noteHeader from './note_header.vue';
+import NoteEditedText from './note_edited_text.vue';
+import NoteHeader from './note_header.vue';
export default {
name: 'DiffDiscussionHeader',
components: {
GlAvatar,
GlAvatarLink,
- noteEditedText,
- noteHeader,
+ NoteEditedText,
+ NoteHeader,
},
directives: {
SafeHtml,
diff --git a/app/assets/javascripts/notes/components/discussion_actions.vue b/app/assets/javascripts/notes/components/discussion_actions.vue
index 6f0745d4fb0..dcbf4a0e5d3 100644
--- a/app/assets/javascripts/notes/components/discussion_actions.vue
+++ b/app/assets/javascripts/notes/components/discussion_actions.vue
@@ -59,6 +59,7 @@ export default {
<resolve-discussion-button
v-if="discussion.resolvable"
data-qa-selector="resolve_discussion_button"
+ data-testid="resolve-discussion-button"
:is-resolving="isResolving"
:button-title="resolveButtonTitle"
@onClick="$emit('resolve')"
diff --git a/app/assets/javascripts/notes/components/discussion_counter.vue b/app/assets/javascripts/notes/components/discussion_counter.vue
index eedcb0c09d4..6521b86edbb 100644
--- a/app/assets/javascripts/notes/components/discussion_counter.vue
+++ b/app/assets/javascripts/notes/components/discussion_counter.vue
@@ -1,7 +1,16 @@
<script>
-import { GlTooltipDirective, GlButton, GlButtonGroup } from '@gitlab/ui';
+import {
+ GlTooltipDirective,
+ GlButton,
+ GlButtonGroup,
+ GlDropdown,
+ GlDropdownItem,
+ GlIcon,
+} from '@gitlab/ui';
import { mapGetters, mapActions } from 'vuex';
+import { throttle } from 'lodash';
import { __ } from '~/locale';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import discussionNavigation from '../mixins/discussion_navigation';
export default {
@@ -11,14 +20,23 @@ export default {
components: {
GlButton,
GlButtonGroup,
+ GlDropdown,
+ GlDropdownItem,
+ GlIcon,
},
- mixins: [discussionNavigation],
+ mixins: [glFeatureFlagsMixin(), discussionNavigation],
props: {
blocksMerge: {
type: Boolean,
required: true,
},
},
+ data() {
+ return {
+ jumpNext: throttle(this.jumpToNextDiscussion, 500),
+ jumpPrevious: throttle(this.jumpToPreviousDiscussion, 500),
+ };
+ },
computed: {
...mapGetters([
'getNoteableData',
@@ -54,27 +72,44 @@ export default {
<template>
<div
v-if="resolvableDiscussionsCount > 0"
+ id="discussionCounter"
ref="discussionCounter"
class="gl-display-flex discussions-counter"
>
<div
- class="gl-display-flex gl-align-items-center gl-pl-4 gl-rounded-base gl-mr-3"
+ class="gl-display-flex gl-align-items-center gl-pl-4 gl-rounded-base gl-mr-3 gl-min-h-7"
:class="{
'gl-bg-orange-50': blocksMerge && !allResolved,
'gl-bg-gray-50': !blocksMerge || allResolved,
- 'gl-pr-4': allResolved,
'gl-pr-2': !allResolved,
}"
data-testid="discussions-counter-text"
>
<template v-if="allResolved">
{{ __('All threads resolved!') }}
+ <gl-dropdown
+ size="small"
+ category="tertiary"
+ right
+ toggle-class="btn-icon"
+ class="gl-pt-0! gl-px-2 gl-h-full gl-ml-2"
+ >
+ <template #button-content>
+ <gl-icon name="ellipsis_v" class="mr-0" />
+ </template>
+ <gl-dropdown-item
+ data-testid="toggle-all-discussions-btn"
+ @click="handleExpandDiscussions"
+ >
+ {{ toggleThreadsLabel }}
+ </gl-dropdown-item>
+ </gl-dropdown>
</template>
<template v-else>
{{ n__('%d unresolved thread', '%d unresolved threads', unresolvedDiscussionsCount) }}
<gl-button-group class="gl-ml-3">
<gl-button
- v-gl-tooltip.hover
+ v-gl-tooltip:discussionCounter.hover.bottom
:title="__('Go to previous unresolved thread')"
:aria-label="__('Go to previous unresolved thread')"
class="discussion-previous-btn gl-rounded-base! gl-px-2!"
@@ -83,10 +118,10 @@ export default {
data-track-property="click_previous_unresolved_thread_top"
icon="chevron-lg-up"
category="tertiary"
- @click="jumpToPreviousDiscussion"
+ @click="jumpPrevious"
/>
<gl-button
- v-gl-tooltip.hover
+ v-gl-tooltip:discussionCounter.hover.bottom
:title="__('Go to next unresolved thread')"
:aria-label="__('Go to next unresolved thread')"
class="discussion-next-btn gl-rounded-base! gl-px-2!"
@@ -95,29 +130,33 @@ export default {
data-track-property="click_next_unresolved_thread_top"
icon="chevron-lg-down"
category="tertiary"
- @click="jumpToNextDiscussion"
+ @click="jumpNext"
/>
+ <gl-dropdown
+ size="small"
+ category="tertiary"
+ right
+ toggle-class="btn-icon"
+ class="gl-pt-0! gl-px-2"
+ >
+ <template #button-content>
+ <gl-icon name="ellipsis_v" class="mr-0" />
+ </template>
+ <gl-dropdown-item
+ data-testid="toggle-all-discussions-btn"
+ @click="handleExpandDiscussions"
+ >
+ {{ toggleThreadsLabel }}
+ </gl-dropdown-item>
+ <gl-dropdown-item
+ v-if="resolveAllDiscussionsIssuePath && !allResolved"
+ :href="resolveAllDiscussionsIssuePath"
+ >
+ {{ __('Create issue to resolve all threads') }}
+ </gl-dropdown-item>
+ </gl-dropdown>
</gl-button-group>
</template>
</div>
- <gl-button-group>
- <gl-button
- v-gl-tooltip
- :title="toggleThreadsLabel"
- :aria-label="toggleThreadsLabel"
- class="toggle-all-discussions-btn"
- :icon="allExpanded ? 'collapse' : 'expand'"
- @click="handleExpandDiscussions"
- />
- <gl-button
- v-if="resolveAllDiscussionsIssuePath && !allResolved"
- v-gl-tooltip
- :href="resolveAllDiscussionsIssuePath"
- :title="__('Create issue to resolve all threads')"
- :aria-label="__('Create issue to resolve all threads')"
- class="new-issue-for-discussion discussion-create-issue-btn"
- icon="issue-new"
- />
- </gl-button-group>
</div>
</template>
diff --git a/app/assets/javascripts/notes/components/discussion_filter.vue b/app/assets/javascripts/notes/components/discussion_filter.vue
index 15887c2738d..8a42fb6bd85 100644
--- a/app/assets/javascripts/notes/components/discussion_filter.vue
+++ b/app/assets/javascripts/notes/components/discussion_filter.vue
@@ -2,6 +2,9 @@
import { GlDropdown, GlDropdownItem, GlDropdownDivider } from '@gitlab/ui';
import { mapGetters, mapActions } from 'vuex';
import { getLocationHash, doesHashExistInUrl } from '~/lib/utils/url_utility';
+import { __ } from '~/locale';
+import Tracking from '~/tracking';
+import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import {
DISCUSSION_FILTERS_DEFAULT_VALUE,
HISTORY_ONLY_FILTER_VALUE,
@@ -9,15 +12,25 @@ import {
DISCUSSION_TAB_LABEL,
DISCUSSION_FILTER_TYPES,
NOTE_UNDERSCORE,
+ ASC,
+ DESC,
} from '../constants';
import notesEventHub from '../event_hub';
+const SORT_OPTIONS = [
+ { key: DESC, text: __('Newest first'), cls: 'js-newest-first' },
+ { key: ASC, text: __('Oldest first'), cls: 'js-oldest-first' },
+];
+
export default {
+ SORT_OPTIONS,
components: {
GlDropdown,
GlDropdownItem,
GlDropdownDivider,
+ LocalStorageSync,
},
+ mixins: [Tracking.mixin()],
props: {
filters: {
type: Array,
@@ -39,11 +52,24 @@ export default {
};
},
computed: {
- ...mapGetters(['getNotesDataByProp', 'timelineEnabled', 'isLoading']),
+ ...mapGetters([
+ 'getNotesDataByProp',
+ 'timelineEnabled',
+ 'isLoading',
+ 'sortDirection',
+ 'persistSortOrder',
+ 'noteableType',
+ ]),
currentFilter() {
if (!this.currentValue) return this.filters[0];
return this.filters.find((filter) => filter.value === this.currentValue);
},
+ selectedSortOption() {
+ return SORT_OPTIONS.find(({ key }) => this.sortDirection === key);
+ },
+ sortStorageKey() {
+ return `sort_direction_${this.noteableType.toLowerCase()}`;
+ },
},
created() {
if (window.mrTabs) {
@@ -69,6 +95,7 @@ export default {
'setCommentsDisabled',
'setTargetNoteHash',
'setTimelineView',
+ 'setDiscussionSortDirection',
]),
selectFilter(value, persistFilter = true) {
const filter = parseInt(value, 10);
@@ -108,31 +135,73 @@ export default {
}
return DISCUSSION_FILTER_TYPES.HISTORY;
},
+ fetchSortedDiscussions(direction) {
+ if (this.isSortDropdownItemActive(direction)) {
+ return;
+ }
+
+ this.setDiscussionSortDirection({ direction });
+ this.track('change_discussion_sort_direction', { property: direction });
+ },
+ isSortDropdownItemActive(sortDir) {
+ return sortDir === this.sortDirection;
+ },
},
};
</script>
<template>
- <gl-dropdown
+ <div
v-if="displayFilters"
- id="discussion-filter-dropdown"
- class="full-width-mobile discussion-filter-container js-discussion-filter-container"
- data-qa-selector="discussion_filter_dropdown"
- :text="currentFilter.title"
- :disabled="isLoading"
+ id="discussion-preferences"
+ data-testid="discussion-preferences"
+ class="gl-display-inline-block gl-vertical-align-bottom full-width-mobile"
>
- <div v-for="filter in filters" :key="filter.value" class="dropdown-item-wrapper">
- <gl-dropdown-item
- :is-check-item="true"
- :is-checked="filter.value === currentValue"
- :class="{ 'is-active': filter.value === currentValue }"
- :data-filter-type="filterType(filter.value)"
- data-qa-selector="filter_menu_item"
- @click.prevent="selectFilter(filter.value)"
+ <local-storage-sync
+ :value="sortDirection"
+ :storage-key="sortStorageKey"
+ :persist="persistSortOrder"
+ as-string
+ @input="setDiscussionSortDirection({ direction: $event })"
+ />
+ <gl-dropdown
+ id="discussion-preferences-dropdown"
+ class="full-width-mobile"
+ data-qa-selector="discussion_preferences_dropdown"
+ text="Sort or filter"
+ :disabled="isLoading"
+ right
+ >
+ <div id="discussion-sort">
+ <gl-dropdown-item
+ v-for="{ text, key, cls } in $options.SORT_OPTIONS"
+ :key="text"
+ :class="cls"
+ is-check-item
+ :is-checked="isSortDropdownItemActive(key)"
+ @click="fetchSortedDiscussions(key)"
+ >
+ {{ text }}
+ </gl-dropdown-item>
+ </div>
+ <gl-dropdown-divider />
+ <div
+ id="discussion-filter"
+ class="discussion-filter-container js-discussion-filter-container"
>
- {{ filter.title }}
- </gl-dropdown-item>
- <gl-dropdown-divider v-if="filter.value === defaultValue" />
- </div>
- </gl-dropdown>
+ <gl-dropdown-item
+ v-for="filter in filters"
+ :key="filter.value"
+ is-check-item
+ :is-checked="filter.value === currentValue"
+ :class="{ 'is-active': filter.value === currentValue }"
+ :data-filter-type="filterType(filter.value)"
+ data-qa-selector="filter_menu_item"
+ @click.prevent="selectFilter(filter.value)"
+ >
+ {{ filter.title }}
+ </gl-dropdown-item>
+ </div>
+ </gl-dropdown>
+ </div>
</template>
diff --git a/app/assets/javascripts/notes/components/discussion_navigator.vue b/app/assets/javascripts/notes/components/discussion_navigator.vue
index c1e39f31bbb..03bdc7a2cc6 100644
--- a/app/assets/javascripts/notes/components/discussion_navigator.vue
+++ b/app/assets/javascripts/notes/components/discussion_navigator.vue
@@ -1,6 +1,7 @@
<script>
/* global Mousetrap */
import 'mousetrap';
+import { throttle } from 'lodash';
import {
keysFor,
MR_NEXT_UNRESOLVED_DISCUSSION,
@@ -11,12 +12,18 @@ import discussionNavigation from '~/notes/mixins/discussion_navigation';
export default {
mixins: [discussionNavigation],
+ data() {
+ return {
+ jumpToNext: throttle(() => this.jumpToNextDiscussion({ behavior: 'auto' }), 200),
+ jumpToPrevious: throttle(() => this.jumpToPreviousDiscussion({ behavior: 'auto' }), 200),
+ };
+ },
created() {
eventHub.$on('jumpToFirstUnresolvedDiscussion', this.jumpToFirstUnresolvedDiscussion);
},
mounted() {
- Mousetrap.bind(keysFor(MR_NEXT_UNRESOLVED_DISCUSSION), this.jumpToNextDiscussion);
- Mousetrap.bind(keysFor(MR_PREVIOUS_UNRESOLVED_DISCUSSION), this.jumpToPreviousDiscussion);
+ Mousetrap.bind(keysFor(MR_NEXT_UNRESOLVED_DISCUSSION), this.jumpToNext);
+ Mousetrap.bind(keysFor(MR_PREVIOUS_UNRESOLVED_DISCUSSION), this.jumpToPrevious);
},
beforeDestroy() {
Mousetrap.unbind(keysFor(MR_NEXT_UNRESOLVED_DISCUSSION));
diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue
index c7f293a219a..9806f8e5dc2 100644
--- a/app/assets/javascripts/notes/components/note_actions.vue
+++ b/app/assets/javascripts/notes/components/note_actions.vue
@@ -1,6 +1,6 @@
<script>
import { GlTooltipDirective, GlIcon, GlButton, GlDropdownItem } from '@gitlab/ui';
-import { mapActions, mapGetters } from 'vuex';
+import { mapActions, mapGetters, mapState } from 'vuex';
import Api from '~/api';
import resolvedStatusMixin from '~/batch_comments/mixins/resolved_status';
import createFlash from '~/flash';
@@ -11,6 +11,7 @@ import UserAccessRoleBadge from '~/vue_shared/components/user_access_role_badge.
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { splitCamelCase } from '~/lib/utils/text_utility';
import ReplyButton from './note_actions/reply_button.vue';
+import TimelineEventButton from './note_actions/timeline_event_button.vue';
export default {
i18n: {
@@ -23,6 +24,7 @@ export default {
components: {
GlIcon,
ReplyButton,
+ TimelineEventButton,
GlButton,
GlDropdownItem,
UserAccessRoleBadge,
@@ -133,7 +135,8 @@ export default {
},
},
computed: {
- ...mapGetters(['getUserDataByProp', 'getNoteableData']),
+ ...mapState(['isPromoteCommentToTimelineEventInProgress']),
+ ...mapGetters(['getUserDataByProp', 'getNoteableData', 'canUserAddIncidentTimelineEvents']),
shouldShowActionsDropdown() {
return this.currentUserId && (this.canEdit || this.canReportAsAbuse);
},
@@ -199,7 +202,7 @@ export default {
},
},
methods: {
- ...mapActions(['toggleAwardRequest']),
+ ...mapActions(['toggleAwardRequest', 'promoteCommentToTimelineEvent']),
onEdit() {
this.$emit('handleEdit');
},
@@ -292,6 +295,12 @@ export default {
class="line-resolve-btn note-action-button"
@click="onResolve"
/>
+ <timeline-event-button
+ v-if="canUserAddIncidentTimelineEvents"
+ :note-id="noteId"
+ :is-promotion-in-progress="isPromoteCommentToTimelineEventInProgress"
+ @click-promote-comment-to-event="promoteCommentToTimelineEvent"
+ />
<emoji-picker
v-if="canAwardEmoji"
toggle-class="note-action-button note-emoji-button btn-icon btn-default-tertiary"
diff --git a/app/assets/javascripts/notes/components/note_actions/timeline_event_button.vue b/app/assets/javascripts/notes/components/note_actions/timeline_event_button.vue
new file mode 100644
index 00000000000..4dd0c968282
--- /dev/null
+++ b/app/assets/javascripts/notes/components/note_actions/timeline_event_button.vue
@@ -0,0 +1,49 @@
+<script>
+import { GlTooltipDirective, GlButton } from '@gitlab/ui';
+import { __ } from '~/locale';
+
+export default {
+ i18n: {
+ buttonText: __('Add comment to incident timeline'),
+ addError: __('Error promoting the note to timeline event: %{error}'),
+ addGenericError: __('Something went wrong while promoting the note to timeline event.'),
+ },
+ components: {
+ GlButton,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ noteId: {
+ type: [String, Number],
+ required: true,
+ },
+ isPromotionInProgress: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ methods: {
+ handleButtonClick() {
+ this.$emit('click-promote-comment-to-event', {
+ noteId: this.noteId,
+ addError: this.$options.i18n.addError,
+ addGenericError: this.$options.i18n.addGenericError,
+ });
+ },
+ },
+};
+</script>
+<template>
+ <span v-gl-tooltip :title="$options.i18n.buttonText">
+ <gl-button
+ category="tertiary"
+ icon="clock"
+ :aria-label="$options.i18n.buttonText"
+ :disabled="isPromotionInProgress"
+ @click="handleButtonClick"
+ />
+ </span>
+</template>
diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue
index f1c41eea428..82c125b79ce 100644
--- a/app/assets/javascripts/notes/components/note_body.vue
+++ b/app/assets/javascripts/notes/components/note_body.vue
@@ -8,17 +8,17 @@ import { __ } from '~/locale';
import '~/behaviors/markdown/render_gfm';
import Suggestions from '~/vue_shared/components/markdown/suggestions.vue';
import autosave from '../mixins/autosave';
-import noteAttachment from './note_attachment.vue';
-import noteAwardsList from './note_awards_list.vue';
-import noteEditedText from './note_edited_text.vue';
-import noteForm from './note_form.vue';
+import NoteAttachment from './note_attachment.vue';
+import NoteAwardsList from './note_awards_list.vue';
+import NoteEditedText from './note_edited_text.vue';
+import NoteForm from './note_form.vue';
export default {
components: {
- noteEditedText,
- noteAwardsList,
- noteAttachment,
- noteForm,
+ NoteEditedText,
+ NoteAwardsList,
+ NoteAttachment,
+ NoteForm,
Suggestions,
},
directives: {
@@ -71,7 +71,7 @@ export default {
return this.note.note;
},
saveButtonTitle() {
- return this.note.confidential ? __('Save internal note') : __('Save comment');
+ return this.note.internal ? __('Save internal note') : __('Save comment');
},
hasSuggestion() {
return this.note.suggestions && this.note.suggestions.length;
diff --git a/app/assets/javascripts/notes/components/note_edited_text.vue b/app/assets/javascripts/notes/components/note_edited_text.vue
index 03cbdf45ddd..e0c3ed0c67a 100644
--- a/app/assets/javascripts/notes/components/note_edited_text.vue
+++ b/app/assets/javascripts/notes/components/note_edited_text.vue
@@ -1,11 +1,11 @@
<script>
/* eslint-disable @gitlab/vue-require-i18n-strings */
-import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
export default {
name: 'EditedNoteText',
components: {
- timeAgoTooltip,
+ TimeAgoTooltip,
},
props: {
actionText: {
diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue
index 30579a8eb0d..b6ede10d02b 100644
--- a/app/assets/javascripts/notes/components/note_form.vue
+++ b/app/assets/javascripts/notes/components/note_form.vue
@@ -4,7 +4,7 @@ import { mapGetters, mapActions, mapState } from 'vuex';
import { getDraft, updateDraft } from '~/lib/utils/autosave';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
-import markdownField from '~/vue_shared/components/markdown/field.vue';
+import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import eventHub from '../event_hub';
import issuableStateMixin from '../mixins/issuable_state';
import resolvable from '../mixins/resolvable';
@@ -15,7 +15,7 @@ export default {
i18n: COMMENT_FORM,
name: 'NoteForm',
components: {
- markdownField,
+ MarkdownField,
CommentFieldLayout,
GlButton,
GlSprintf,
@@ -136,7 +136,7 @@ export default {
);
},
textareaPlaceholder() {
- return this.discussionNote?.confidential
+ return this.discussionNote?.internal
? this.$options.i18n.bodyPlaceholderInternal
: this.$options.i18n.bodyPlaceholder;
},
@@ -331,7 +331,7 @@ export default {
<form :data-line-code="lineCode" class="edit-note common-note-form js-quick-submit gfm-form">
<comment-field-layout
:noteable-data="getNoteableData"
- :is-internal-note="discussion.confidential"
+ :is-internal-note="discussion.internal"
>
<markdown-field
:markdown-preview-path="markdownPreviewPath"
@@ -423,7 +423,7 @@ export default {
category="primary"
variant="confirm"
data-qa-selector="reply_comment_button"
- class="gl-mr-3 js-vue-issue-save js-comment-button"
+ class="gl-sm-mr-3 gl-xs-mb-3 js-vue-issue-save js-comment-button"
@click="handleUpdate()"
>
{{ saveButtonTitle }}
@@ -432,7 +432,7 @@ export default {
v-if="discussion.resolvable"
category="secondary"
variant="default"
- class="gl-mr-3 js-comment-resolve-button"
+ class="gl-sm-mr-3 gl-xs-mb-3 js-comment-resolve-button"
@click.prevent="handleUpdate(true)"
>
{{ resolveButtonTitle }}
diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue
index 9917249f0db..f700802d6bc 100644
--- a/app/assets/javascripts/notes/components/note_header.vue
+++ b/app/assets/javascripts/notes/components/note_header.vue
@@ -8,13 +8,14 @@ import {
} from '@gitlab/ui';
import { mapActions } from 'vuex';
import { __, s__ } from '~/locale';
-import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
components: {
- timeAgoTooltip,
+ TimeAgoTooltip,
GitlabTeamMemberBadge: () =>
import('ee_component/vue_shared/components/user_avatar/badges/gitlab_team_member_badge.vue'),
GlIcon,
@@ -26,6 +27,7 @@ export default {
SafeHtml,
GlTooltip: GlTooltipDirective,
},
+ mixins: [glFeatureFlagsMixin()],
props: {
author: {
type: Object,
@@ -183,22 +185,35 @@ export default {
:data-user-id="author.id"
:data-username="author.username"
>
- <slot name="note-header-info"></slot>
+ <span
+ v-if="glFeatures.removeUserAttributesProjects || glFeatures.removeUserAttributesGroups"
+ class="note-header-author-name gl-font-weight-bold"
+ >
+ {{ authorName }}
+ </span>
<user-name-with-status
+ v-else
:name="authorName"
:availability="userAvailability(author)"
container-classes="note-header-author-name gl-font-weight-bold"
/>
</a>
<span
- v-if="authorStatus"
+ v-if="
+ authorStatus &&
+ !glFeatures.removeUserAttributesProjects &&
+ !glFeatures.removeUserAttributesGroups
+ "
ref="authorStatus"
v-safe-html:[$options.safeHtmlConfig]="authorStatus"
v-on="
authorStatusHasTooltip ? { mouseenter: removeEmojiTitle, mouseleave: addEmojiTitle } : {}
"
></span>
- <span class="text-nowrap author-username">
+ <span
+ v-if="!glFeatures.removeUserAttributesProjects && !glFeatures.removeUserAttributesGroups"
+ class="text-nowrap author-username"
+ >
<a
ref="authorUsernameLink"
class="author-username-link"
@@ -207,6 +222,7 @@ export default {
@mouseleave="handleUsernameMouseLeave"
><span class="note-headline-light">@{{ author.username }}</span>
</a>
+ <slot name="note-header-info"></slot>
<gitlab-team-member-badge v-if="author && author.is_gitlab_employee" />
</span>
</template>
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index c5d174ed890..afa5e39d8b0 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -10,25 +10,25 @@ import { ignoreWhilePending } from '~/lib/utils/ignore_while_pending';
import { s__, __, sprintf } from '~/locale';
import diffLineNoteFormMixin from '~/notes/mixins/diff_line_note_form';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
-import userAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
+import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import eventHub from '../event_hub';
import noteable from '../mixins/noteable';
import resolvable from '../mixins/resolvable';
-import diffDiscussionHeader from './diff_discussion_header.vue';
-import diffWithNote from './diff_with_note.vue';
+import DiffDiscussionHeader from './diff_discussion_header.vue';
+import DiffWithNote from './diff_with_note.vue';
import DiscussionActions from './discussion_actions.vue';
import DiscussionNotes from './discussion_notes.vue';
-import noteForm from './note_form.vue';
-import noteSignedOutWidget from './note_signed_out_widget.vue';
+import NoteForm from './note_form.vue';
+import NoteSignedOutWidget from './note_signed_out_widget.vue';
export default {
name: 'NoteableDiscussion',
components: {
GlIcon,
- userAvatarLink,
- diffDiscussionHeader,
- noteSignedOutWidget,
- noteForm,
+ UserAvatarLink,
+ DiffDiscussionHeader,
+ NoteSignedOutWidget,
+ NoteForm,
DraftNote,
TimelineEntryItem,
DiscussionNotes,
@@ -96,7 +96,7 @@ export default {
return isLoggedIn();
},
commentType() {
- return this.discussion.confidential ? __('internal note') : __('comment');
+ return this.discussion.internal ? __('internal note') : __('comment');
},
autosaveKey() {
return getDiscussionReplyKey(this.firstNote.noteable_type, this.discussion.id);
@@ -108,7 +108,7 @@ export default {
return this.discussion.notes.slice(0, 1)[0];
},
saveButtonTitle() {
- return this.discussion.confidential ? __('Reply internally') : __('Reply');
+ return this.discussion.internal ? __('Reply internally') : __('Reply');
},
shouldShowJumpToNextDiscussion() {
return this.showJumpToNextDiscussion(this.discussionsByDiffOrder ? 'diff' : 'discussion');
@@ -120,7 +120,7 @@ export default {
return !this.shouldRenderDiffs;
},
wrapperComponent() {
- return this.shouldRenderDiffs ? diffWithNote : 'div';
+ return this.shouldRenderDiffs ? DiffWithNote : 'div';
},
wrapperComponentProps() {
if (this.shouldRenderDiffs) {
@@ -269,6 +269,7 @@ export default {
<div class="timeline-content">
<div
:data-discussion-id="discussion.id"
+ :data-discussion-resolved="discussion.resolved"
class="discussion js-discussion-container"
data-qa-selector="discussion_content"
>
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index 875cfff74fe..e51969f95c7 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -22,16 +22,16 @@ import {
commentLineOptions,
formatLineRange,
} from './multiline_comment_utils';
-import noteActions from './note_actions.vue';
+import NoteActions from './note_actions.vue';
import NoteBody from './note_body.vue';
-import noteHeader from './note_header.vue';
+import NoteHeader from './note_header.vue';
export default {
name: 'NoteableNote',
components: {
GlSprintf,
- noteHeader,
- noteActions,
+ NoteHeader,
+ NoteActions,
NoteBody,
TimelineEntryItem,
GlAvatarLink,
@@ -109,7 +109,7 @@ export default {
return this.note.author;
},
commentType() {
- return this.note.confidential ? __('internal note') : __('comment');
+ return this.note.internal ? __('internal note') : __('comment');
},
classNameBindings() {
return {
@@ -259,7 +259,7 @@ export default {
});
const confirmed = await confirmAction(msg, {
primaryBtnVariant: 'danger',
- primaryBtnText: this.note.confidential ? __('Delete internal note') : __('Delete comment'),
+ primaryBtnText: this.note.internal ? __('Delete internal note') : __('Delete comment'),
});
if (confirmed) {
@@ -406,7 +406,7 @@ export default {
<template>
<timeline-entry-item
:id="noteAnchorId"
- :class="{ ...classNameBindings, 'internal-note': note.confidential }"
+ :class="{ ...classNameBindings, 'internal-note': note.internal }"
:data-award-url="note.toggle_award_path"
:data-note-id="note.id"
class="note note-wrapper"
@@ -440,7 +440,7 @@ export default {
</gl-avatar-link>
</div>
- <div v-else class="gl-float-left gl-pl-3 gl-mr-3 gl-md-pl-2 gl-md-pr-2">
+ <div v-else class="gl-float-left gl-pl-3 gl-md-pl-2">
<gl-avatar-link :href="author.path">
<gl-avatar
:src="author.avatar_url"
@@ -459,7 +459,7 @@ export default {
:author="author"
:created-at="note.created_at"
:note-id="note.id"
- :is-internal-note="note.confidential"
+ :is-internal-note="note.internal"
:noteable-type="noteableType"
>
<template #note-header-info>
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index 754c2917182..37bc8bad305 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -6,34 +6,34 @@ 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 DraftNote from '~/batch_comments/components/draft_note.vue';
import { getLocationHash, doesHashExistInUrl } 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';
-import systemNote from '~/vue_shared/components/notes/system_note.vue';
+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';
+import SystemNote from '~/vue_shared/components/notes/system_note.vue';
import * as constants from '../constants';
import eventHub from '../event_hub';
-import commentForm from './comment_form.vue';
-import discussionFilterNote from './discussion_filter_note.vue';
-import noteableDiscussion from './noteable_discussion.vue';
-import noteableNote from './noteable_note.vue';
+import CommentForm from './comment_form.vue';
+import DiscussionFilterNote from './discussion_filter_note.vue';
+import NoteableDiscussion from './noteable_discussion.vue';
+import NoteableNote from './noteable_note.vue';
import SidebarSubscription from './sidebar_subscription.vue';
export default {
name: 'NotesApp',
components: {
- noteableNote,
- noteableDiscussion,
- systemNote,
- commentForm,
- placeholderNote,
- placeholderSystemNote,
- skeletonLoadingContainer,
- discussionFilterNote,
+ NoteableNote,
+ NoteableDiscussion,
+ SystemNote,
+ CommentForm,
+ PlaceholderNote,
+ PlaceholderSystemNote,
+ SkeletonLoadingContainer,
+ DiscussionFilterNote,
OrderedLayout,
SidebarSubscription,
- draftNote,
+ DraftNote,
TimelineEntryItem,
},
mixins: [glFeatureFlagsMixin()],
diff --git a/app/assets/javascripts/notes/components/sidebar_subscription.vue b/app/assets/javascripts/notes/components/sidebar_subscription.vue
index 52dadc7b4c3..9fc11ff65d5 100644
--- a/app/assets/javascripts/notes/components/sidebar_subscription.vue
+++ b/app/assets/javascripts/notes/components/sidebar_subscription.vue
@@ -3,7 +3,7 @@ import { mapActions } from 'vuex';
import { IssuableType } from '~/issues/constants';
import { fetchPolicies } from '~/lib/graphql';
import { confidentialityQueries } from '~/sidebar/constants';
-import { defaultClient as gqlClient } from '~/sidebar/graphql';
+import { defaultClient as gqlClient } from '~/graphql_shared/issuable_client';
export default {
props: {
diff --git a/app/assets/javascripts/notes/components/sort_discussion.vue b/app/assets/javascripts/notes/components/sort_discussion.vue
deleted file mode 100644
index bcc5d12b7c8..00000000000
--- a/app/assets/javascripts/notes/components/sort_discussion.vue
+++ /dev/null
@@ -1,76 +0,0 @@
-<script>
-import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
-import { mapActions, mapGetters } from 'vuex';
-import { __ } from '~/locale';
-import Tracking from '~/tracking';
-import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
-import { ASC, DESC } from '../constants';
-
-const SORT_OPTIONS = [
- { key: DESC, text: __('Newest first'), cls: 'js-newest-first' },
- { key: ASC, text: __('Oldest first'), cls: 'js-oldest-first' },
-];
-
-export default {
- SORT_OPTIONS,
- components: {
- GlDropdown,
- GlDropdownItem,
- LocalStorageSync,
- },
- mixins: [Tracking.mixin()],
- computed: {
- ...mapGetters(['sortDirection', 'persistSortOrder', 'noteableType']),
- selectedOption() {
- return SORT_OPTIONS.find(({ key }) => this.sortDirection === key);
- },
- dropdownText() {
- return this.selectedOption.text;
- },
- storageKey() {
- return `sort_direction_${this.noteableType.toLowerCase()}`;
- },
- },
- methods: {
- ...mapActions(['setDiscussionSortDirection']),
- fetchSortedDiscussions(direction) {
- if (this.isDropdownItemActive(direction)) {
- return;
- }
-
- this.setDiscussionSortDirection({ direction });
- this.track('change_discussion_sort_direction', { property: direction });
- },
- isDropdownItemActive(sortDir) {
- return sortDir === this.sortDirection;
- },
- },
-};
-</script>
-
-<template>
- <div
- data-testid="sort-discussion-filter"
- class="gl-mr-3 gl-display-inline-block gl-vertical-align-bottom full-width-mobile"
- >
- <local-storage-sync
- :value="sortDirection"
- :storage-key="storageKey"
- :persist="persistSortOrder"
- as-string
- @input="setDiscussionSortDirection({ direction: $event })"
- />
- <gl-dropdown :text="dropdownText" class="js-dropdown-text full-width-mobile">
- <gl-dropdown-item
- v-for="{ text, key, cls } in $options.SORT_OPTIONS"
- :key="key"
- :class="cls"
- :is-check-item="true"
- :is-checked="isDropdownItemActive(key)"
- @click="fetchSortedDiscussions(key)"
- >
- {{ text }}
- </gl-dropdown-item>
- </gl-dropdown>
- </div>
-</template>
diff --git a/app/assets/javascripts/notes/components/timeline_toggle.vue b/app/assets/javascripts/notes/components/timeline_toggle.vue
index e4d89f54652..8632eea5d8e 100644
--- a/app/assets/javascripts/notes/components/timeline_toggle.vue
+++ b/app/assets/javascripts/notes/components/timeline_toggle.vue
@@ -53,7 +53,6 @@ export default {
:selected="timelineEnabled"
:title="tooltip"
:aria-label="tooltip"
- class="gl-mr-3"
@click="toggleTimeline"
/>
</template>
diff --git a/app/assets/javascripts/notes/constants.js b/app/assets/javascripts/notes/constants.js
index a5f459c8910..88f438975f6 100644
--- a/app/assets/javascripts/notes/constants.js
+++ b/app/assets/javascripts/notes/constants.js
@@ -13,6 +13,7 @@ export const MERGED = 'merged';
export const ISSUE_NOTEABLE_TYPE = 'Issue';
export const EPIC_NOTEABLE_TYPE = 'Epic';
export const MERGE_REQUEST_NOTEABLE_TYPE = 'MergeRequest';
+export const INCIDENT_NOTEABLE_TYPE = 'INCIDENT'; // TODO: check if value can be converted to `Incident`
export const UNRESOLVE_NOTE_METHOD_NAME = 'delete';
export const RESOLVE_NOTE_METHOD_NAME = 'post';
export const DESCRIPTION_TYPE = 'changed the description';
@@ -31,6 +32,7 @@ export const NOTEABLE_TYPE_MAPPING = {
Issue: ISSUE_NOTEABLE_TYPE,
MergeRequest: MERGE_REQUEST_NOTEABLE_TYPE,
Epic: EPIC_NOTEABLE_TYPE,
+ Incident: INCIDENT_NOTEABLE_TYPE,
};
export const DISCUSSION_FILTER_TYPES = {
diff --git a/app/assets/javascripts/notes/graphql/promote_timeline_event.mutation.graphql b/app/assets/javascripts/notes/graphql/promote_timeline_event.mutation.graphql
new file mode 100644
index 00000000000..c9df9cfd6d3
--- /dev/null
+++ b/app/assets/javascripts/notes/graphql/promote_timeline_event.mutation.graphql
@@ -0,0 +1,8 @@
+mutation PromoteTimelineEvent($input: TimelineEventPromoteFromNoteInput!) {
+ timelineEventPromoteFromNote(input: $input) {
+ timelineEvent {
+ id
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js
index 19fa484d659..054a5bd36e2 100644
--- a/app/assets/javascripts/notes/index.js
+++ b/app/assets/javascripts/notes/index.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
-import notesApp from './components/notes_app.vue';
+import { parseBoolean } from '~/lib/utils/common_utils';
+import NotesApp from './components/notes_app.vue';
import initDiscussionFilters from './discussion_filters';
-import initSortDiscussions from './sort_discussions';
import { store } from './stores';
import initTimelineToggle from './timeline';
@@ -16,7 +16,7 @@ export default () => {
el,
name: 'NotesRoot',
components: {
- notesApp,
+ NotesApp,
},
store,
data() {
@@ -40,6 +40,7 @@ export default () => {
username: parsedUserData.username,
avatar_url: parsedUserData.avatar_path || parsedUserData.avatar_url,
path: parsedUserData.path,
+ can_add_timeline_events: parseBoolean(notesDataset.canAddTimelineEvents),
};
}
@@ -61,6 +62,5 @@ export default () => {
});
initDiscussionFilters(store);
- initSortDiscussions(store);
initTimelineToggle(store);
};
diff --git a/app/assets/javascripts/notes/mixins/discussion_navigation.js b/app/assets/javascripts/notes/mixins/discussion_navigation.js
index 45df91796fc..db5f9ebf3f0 100644
--- a/app/assets/javascripts/notes/mixins/discussion_navigation.js
+++ b/app/assets/javascripts/notes/mixins/discussion_navigation.js
@@ -1,5 +1,5 @@
import { mapGetters, mapActions, mapState } from 'vuex';
-import { scrollToElementWithContext, scrollToElement } from '~/lib/utils/common_utils';
+import { scrollToElementWithContext, scrollToElement, contentTop } from '~/lib/utils/common_utils';
import { updateHistory } from '~/lib/utils/url_utility';
import eventHub from '../event_hub';
@@ -7,13 +7,14 @@ import eventHub from '../event_hub';
* @param {string} selector
* @returns {boolean}
*/
-function scrollTo(selector, { withoutContext = false } = {}) {
+function scrollTo(selector, { withoutContext = false, offset = 0 } = {}) {
const el = document.querySelector(selector);
const scrollFunction = withoutContext ? scrollToElement : scrollToElementWithContext;
if (el) {
scrollFunction(el, {
behavior: 'auto',
+ offset,
});
return true;
}
@@ -67,7 +68,10 @@ function diffsJump({ expandDiscussion }, id, firstNoteId) {
function discussionJump({ expandDiscussion }, id) {
const selector = `div.discussion[data-discussion-id="${id}"]`;
expandDiscussion({ discussionId: id });
- return scrollTo(selector, { withoutContext: true });
+ return scrollTo(selector, {
+ withoutContext: true,
+ offset: window.gon?.features?.movedMrSidebar ? -28 : 0,
+ });
}
/**
@@ -94,8 +98,6 @@ function jumpToDiscussion(self, discussion) {
if (activeTab === 'diffs' && isDiffDiscussion) {
diffsJump(self, id, firstNoteId);
- } else if (activeTab === 'show') {
- discussionJump(self, id);
} else {
switchToDiscussionsTabAndJumpTo(self, id);
}
@@ -105,11 +107,10 @@ function jumpToDiscussion(self, discussion) {
/**
* @param {object} self Component instance with mixin applied
* @param {function} fn Which function used to get the target discussion's id
- * @param {string} [discussionId=this.currentDiscussionId] Current discussion id, will be null if discussions have not been traversed yet
*/
-function handleDiscussionJump(self, fn, discussionId = self.currentDiscussionId) {
+function handleDiscussionJump(self, fn) {
const isDiffView = window.mrTabs.currentAction === 'diffs';
- const targetId = fn(discussionId, isDiffView);
+ const targetId = fn(self.currentDiscussionId, isDiffView);
const discussion = self.getDiscussion(targetId);
const discussionFilePath = discussion?.diff_file?.file_path;
@@ -127,6 +128,70 @@ function handleDiscussionJump(self, fn, discussionId = self.currentDiscussionId)
});
}
+function getAllDiscussionElements() {
+ return Array.from(
+ document.querySelectorAll('[data-discussion-id]:not([data-discussion-resolved])'),
+ );
+}
+
+function hasReachedPageEnd() {
+ return document.body.scrollHeight <= Math.ceil(window.scrollY + window.innerHeight);
+}
+
+function findNextClosestVisibleDiscussion(discussionElements) {
+ const offsetHeight = contentTop();
+ let isActive;
+ const index = discussionElements.findIndex((element) => {
+ const { y } = element.getBoundingClientRect();
+ const visibleHorizontalOffset = Math.ceil(y) - offsetHeight;
+ // handle rect rounding errors
+ isActive = visibleHorizontalOffset < 2;
+ return visibleHorizontalOffset >= 0;
+ });
+ return [discussionElements[index], index, isActive];
+}
+
+function getNextDiscussion() {
+ const discussionElements = getAllDiscussionElements();
+ const firstDiscussion = discussionElements[0];
+ if (hasReachedPageEnd()) {
+ return firstDiscussion;
+ }
+ const [nextClosestDiscussion, index, isActive] = findNextClosestVisibleDiscussion(
+ discussionElements,
+ );
+ if (nextClosestDiscussion && !isActive) {
+ return nextClosestDiscussion;
+ }
+ const nextDiscussion = discussionElements[index + 1];
+ if (!nextClosestDiscussion || !nextDiscussion) {
+ return firstDiscussion;
+ }
+ return nextDiscussion;
+}
+
+function getPreviousDiscussion() {
+ const discussionElements = getAllDiscussionElements();
+ const lastDiscussion = discussionElements[discussionElements.length - 1];
+ const [, index] = findNextClosestVisibleDiscussion(discussionElements);
+ const previousDiscussion = discussionElements[index - 1];
+ if (previousDiscussion) {
+ return previousDiscussion;
+ }
+ return lastDiscussion;
+}
+
+function handleJumpForBothPages(getDiscussion, ctx, fn, scrollOptions) {
+ if (window.mrTabs.currentAction !== 'show') {
+ handleDiscussionJump(ctx, fn);
+ } else {
+ const discussion = getDiscussion();
+ const id = discussion.dataset.discussionId;
+ ctx.expandDiscussion({ discussionId: id });
+ scrollToElement(discussion, scrollOptions);
+ }
+}
+
export default {
computed: {
...mapGetters([
@@ -142,12 +207,22 @@ export default {
...mapActions(['expandDiscussion', 'setCurrentDiscussionId']),
...mapActions('diffs', ['scrollToFile']),
- jumpToNextDiscussion() {
- handleDiscussionJump(this, this.nextUnresolvedDiscussionId);
+ jumpToNextDiscussion(scrollOptions) {
+ handleJumpForBothPages(
+ getNextDiscussion,
+ this,
+ this.nextUnresolvedDiscussionId,
+ scrollOptions,
+ );
},
- jumpToPreviousDiscussion() {
- handleDiscussionJump(this, this.previousUnresolvedDiscussionId);
+ jumpToPreviousDiscussion(scrollOptions) {
+ handleJumpForBothPages(
+ getPreviousDiscussion,
+ this,
+ this.previousUnresolvedDiscussionId,
+ scrollOptions,
+ );
},
jumpToFirstUnresolvedDiscussion() {
@@ -157,13 +232,5 @@ export default {
})
.catch(() => {});
},
-
- /**
- * Go to the next discussion from the given discussionId
- * @param {String} discussionId The id we are jumping from
- */
- jumpToNextRelativeDiscussion(discussionId) {
- handleDiscussionJump(this, this.nextUnresolvedDiscussionId, discussionId);
- },
},
};
diff --git a/app/assets/javascripts/notes/sort_discussions.js b/app/assets/javascripts/notes/sort_discussions.js
deleted file mode 100644
index ca8df880fe4..00000000000
--- a/app/assets/javascripts/notes/sort_discussions.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import Vue from 'vue';
-import SortDiscussion from './components/sort_discussion.vue';
-
-export default (store) => {
- const el = document.getElementById('js-vue-sort-issue-discussions');
-
- if (!el) return null;
-
- return new Vue({
- el,
- name: 'SortDiscussionRoot',
- store,
- render(createElement) {
- return createElement(SortDiscussion);
- },
- });
-};
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index 82417c9134b..fcef26d720c 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -6,6 +6,7 @@ import createFlash 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';
@@ -18,6 +19,12 @@ import sidebarTimeTrackingEventHub from '~/sidebar/event_hub';
import TaskList from '~/task_list';
import mrWidgetEventHub from '~/vue_merge_request_widget/event_hub';
import SidebarStore from '~/sidebar/stores/sidebar_store';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
+import { TYPE_NOTE } from '~/graphql_shared/constants';
+import notesEventHub from '../event_hub';
+
+import promoteTimelineEvent from '../graphql/promote_timeline_event.mutation.graphql';
+
import * as constants from '../constants';
import * as types from './mutation_types';
import * as utils from './utils';
@@ -226,6 +233,54 @@ export const updateOrCreateNotes = ({ commit, state, getters, dispatch }, notes)
});
};
+export const promoteCommentToTimelineEvent = (
+ { commit },
+ { noteId, addError, addGenericError },
+) => {
+ commit(types.SET_PROMOTE_COMMENT_TO_TIMELINE_PROGRESS, true); // Set loading state
+ return utils.gqClient
+ .mutate({
+ mutation: promoteTimelineEvent,
+ variables: {
+ input: {
+ noteId: convertToGraphQLId(TYPE_NOTE, noteId),
+ },
+ },
+ })
+ .then(({ data = {} }) => {
+ const errors = data.timelineEventPromoteFromNote?.errors;
+ if (errors.length) {
+ const errorMessage = sprintf(addError, {
+ error: errors.join('. '),
+ });
+ throw new Error(errorMessage);
+ } else {
+ notesEventHub.$emit('comment-promoted-to-timeline-event');
+ toast(__('Comment added to the timeline.'));
+ }
+ })
+ .catch((error) => {
+ const message = error.message || addGenericError;
+
+ let captureError = false;
+ let errorObj = null;
+
+ if (message === addGenericError) {
+ captureError = true;
+ errorObj = error;
+ }
+
+ createFlash({
+ message,
+ captureError,
+ error: errorObj,
+ });
+ })
+ .finally(() => {
+ commit(types.SET_PROMOTE_COMMENT_TO_TIMELINE_PROGRESS, false); // Revert loading state
+ });
+};
+
export const replyToDiscussion = (
{ commit, state, getters, dispatch },
{ endpoint, data: reply },
diff --git a/app/assets/javascripts/notes/stores/getters.js b/app/assets/javascripts/notes/stores/getters.js
index 1fe82d96435..6876220f75c 100644
--- a/app/assets/javascripts/notes/stores/getters.js
+++ b/app/assets/javascripts/notes/stores/getters.js
@@ -93,6 +93,13 @@ export const getUserDataByProp = (state) => (prop) => state.userData && state.us
export const descriptionVersions = (state) => state.descriptionVersions;
+export const canUserAddIncidentTimelineEvents = (state) => {
+ return (
+ state.userData.can_add_timeline_events &&
+ state.noteableData.type === constants.NOTEABLE_TYPE_MAPPING.Incident
+ );
+};
+
export const notesById = (state) =>
state.discussions.reduce((acc, note) => {
note.notes.every((n) => Object.assign(acc, { [n.id]: n }));
diff --git a/app/assets/javascripts/notes/stores/modules/index.js b/app/assets/javascripts/notes/stores/modules/index.js
index f779aad5679..7ba1f470b05 100644
--- a/app/assets/javascripts/notes/stores/modules/index.js
+++ b/app/assets/javascripts/notes/stores/modules/index.js
@@ -30,6 +30,7 @@ export default () => ({
isNotesFetched: false,
isLoading: true,
isLoadingDescriptionVersion: false,
+ isPromoteCommentToTimelineEventInProgress: false,
// holds endpoints and permissions provided through haml
notesData: {
diff --git a/app/assets/javascripts/notes/stores/mutation_types.js b/app/assets/javascripts/notes/stores/mutation_types.js
index e28a7bc5cdd..42df6bc0980 100644
--- a/app/assets/javascripts/notes/stores/mutation_types.js
+++ b/app/assets/javascripts/notes/stores/mutation_types.js
@@ -57,3 +57,6 @@ export const RECEIVE_DESCRIPTION_VERSION_ERROR = 'RECEIVE_DESCRIPTION_VERSION_ER
export const REQUEST_DELETE_DESCRIPTION_VERSION = 'REQUEST_DELETE_DESCRIPTION_VERSION';
export const RECEIVE_DELETE_DESCRIPTION_VERSION = 'RECEIVE_DELETE_DESCRIPTION_VERSION';
export const RECEIVE_DELETE_DESCRIPTION_VERSION_ERROR = 'RECEIVE_DELETE_DESCRIPTION_VERSION_ERROR';
+
+// Incidents
+export const SET_PROMOTE_COMMENT_TO_TIMELINE_PROGRESS = 'SET_PROMOTE_COMMENT_TO_TIMELINE_PROGRESS';
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index 0823eacf1b7..83c15c12eac 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -425,4 +425,7 @@ export default {
[types.SET_DONE_FETCHING_BATCH_DISCUSSIONS](state, value) {
state.doneFetchingBatchDiscussions = value;
},
+ [types.SET_PROMOTE_COMMENT_TO_TIMELINE_PROGRESS](state, value) {
+ state.isPromoteCommentToTimelineEventInProgress = value;
+ },
};
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/components/details/artifacts_list.vue b/app/assets/javascripts/packages_and_registries/harbor_registry/components/details/artifacts_list.vue
new file mode 100644
index 00000000000..b55204de875
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/components/details/artifacts_list.vue
@@ -0,0 +1,95 @@
+<script>
+import { GlEmptyState } from '@gitlab/ui';
+import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue';
+import TagsLoader from '~/packages_and_registries/shared/components/tags_loader.vue';
+import {
+ NO_ARTIFACTS_TITLE,
+ NO_TAGS_MATCHING_FILTERS_TITLE,
+ NO_TAGS_MATCHING_FILTERS_DESCRIPTION,
+} from '~/packages_and_registries/harbor_registry/constants';
+import ArtifactsListRow from '~/packages_and_registries/harbor_registry/components/details/artifacts_list_row.vue';
+
+export default {
+ name: 'TagsList',
+ components: {
+ GlEmptyState,
+ ArtifactsListRow,
+ TagsLoader,
+ RegistryList,
+ },
+ inject: ['noContainersImage'],
+ props: {
+ artifacts: {
+ type: Array,
+ required: true,
+ },
+ filter: {
+ type: String,
+ required: true,
+ },
+ pageInfo: {
+ type: Object,
+ required: true,
+ },
+ isLoading: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
+ },
+ data() {
+ return {
+ tags: [],
+ tagsPageInfo: {},
+ };
+ },
+ computed: {
+ hasNoTags() {
+ return this.artifacts.length === 0;
+ },
+ emptyStateTitle() {
+ return this.filter ? NO_TAGS_MATCHING_FILTERS_TITLE : NO_ARTIFACTS_TITLE;
+ },
+ emptyStateDescription() {
+ return this.filter ? NO_TAGS_MATCHING_FILTERS_DESCRIPTION : '';
+ },
+ },
+ methods: {
+ fetchNextPage() {
+ this.$emit('next-page');
+ },
+ fetchPreviousPage() {
+ this.$emit('prev-page');
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <tags-loader v-if="isLoading" />
+ <template v-else>
+ <gl-empty-state
+ v-if="hasNoTags"
+ :title="emptyStateTitle"
+ :svg-path="noContainersImage"
+ :description="emptyStateDescription"
+ class="gl-mx-auto gl-my-0"
+ />
+ <template v-else>
+ <registry-list
+ :pagination="pageInfo"
+ :items="artifacts"
+ :hidden-delete="true"
+ id-property="name"
+ @prev-page="fetchPreviousPage"
+ @next-page="fetchNextPage"
+ >
+ <template #default="{ item }">
+ <artifacts-list-row :artifact="item" />
+ </template>
+ </registry-list>
+ </template>
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/components/details/artifacts_list_row.vue b/app/assets/javascripts/packages_and_registries/harbor_registry/components/details/artifacts_list_row.vue
new file mode 100644
index 00000000000..b489f126f75
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/components/details/artifacts_list_row.vue
@@ -0,0 +1,133 @@
+<script>
+import { GlTooltipDirective, GlSprintf, GlIcon } from '@gitlab/ui';
+import { numberToHumanSize } from '~/lib/utils/number_utils';
+import { n__ } from '~/locale';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import ListItem from '~/vue_shared/components/registry/list_item.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import {
+ DIGEST_LABEL,
+ CREATED_AT_LABEL,
+ NOT_AVAILABLE_TEXT,
+ NOT_AVAILABLE_SIZE,
+} from '~/packages_and_registries/harbor_registry/constants';
+import { artifactPullCommand } from '~/packages_and_registries/harbor_registry/utils';
+
+export default {
+ name: 'TagsListRow',
+ components: {
+ GlSprintf,
+ GlIcon,
+ ListItem,
+ ClipboardButton,
+ TimeAgoTooltip,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ inject: ['repositoryUrl', 'harborIntegrationProjectName'],
+ props: {
+ artifact: {
+ type: Object,
+ required: true,
+ },
+ },
+ i18n: {
+ digestLabel: DIGEST_LABEL,
+ createdAtLabel: CREATED_AT_LABEL,
+ },
+ computed: {
+ formattedSize() {
+ return this.artifact.size
+ ? numberToHumanSize(Number(this.artifact.size))
+ : NOT_AVAILABLE_SIZE;
+ },
+ tagsCountText() {
+ const count = this.artifact?.tags.length ? this.artifact?.tags.length : 0;
+
+ return n__('%d tag', '%d tags', count);
+ },
+ shortDigest() {
+ // remove sha256: from the string, and show only the first 7 char
+ const PREFIX_LENGTH = 'sha256:'.length;
+ const DIGEST_LENGTH = 7;
+ return (
+ this.artifact.digest?.substring(PREFIX_LENGTH, PREFIX_LENGTH + DIGEST_LENGTH) ??
+ NOT_AVAILABLE_TEXT
+ );
+ },
+ getPullCommand() {
+ if (this.artifact?.digest) {
+ const { image } = this.$route.params;
+ return artifactPullCommand({
+ digest: this.artifact.digest,
+ imageName: image,
+ repositoryUrl: this.repositoryUrl,
+ harborProjectName: this.harborIntegrationProjectName,
+ });
+ }
+
+ return '';
+ },
+ linkTo() {
+ const { project, image } = this.$route.params;
+
+ return { name: 'tags', params: { project, image, digest: this.artifact.digest } };
+ },
+ },
+};
+</script>
+
+<template>
+ <list-item v-bind="$attrs">
+ <template #left-primary>
+ <div class="gl-display-flex gl-align-items-center">
+ <router-link
+ class="gl-text-body gl-font-weight-bold gl-word-break-all"
+ data-testid="name"
+ :to="linkTo"
+ >
+ {{ artifact.digest }}
+ </router-link>
+ <clipboard-button
+ v-if="getPullCommand"
+ :title="getPullCommand"
+ :text="getPullCommand"
+ category="tertiary"
+ />
+ </div>
+ </template>
+
+ <template #left-secondary>
+ <span class="gl-mr-3" data-testid="size">
+ {{ formattedSize }}
+ </span>
+ <span id="tagsCount" class="gl-display-flex gl-align-items-center" data-testid="tags-count">
+ <gl-icon name="tag" class="gl-mr-2" />
+ {{ tagsCountText }}
+ </span>
+ </template>
+ <template #right-primary>
+ <span data-testid="time">
+ <gl-sprintf :message="$options.i18n.createdAtLabel">
+ <template #timeInfo>
+ <time-ago-tooltip :time="artifact.pushTime" />
+ </template>
+ </gl-sprintf>
+ </span>
+ </template>
+ <template #right-secondary>
+ <span data-testid="digest">
+ <gl-sprintf :message="$options.i18n.digestLabel">
+ <template #imageId>{{ shortDigest }}</template>
+ </gl-sprintf>
+ </span>
+ <clipboard-button
+ v-if="artifact.digest"
+ :title="artifact.digest"
+ :text="artifact.digest"
+ category="tertiary"
+ />
+ </template>
+ </list-item>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/components/details/details_header.vue b/app/assets/javascripts/packages_and_registries/harbor_registry/components/details/details_header.vue
new file mode 100644
index 00000000000..bfb097601d5
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/components/details/details_header.vue
@@ -0,0 +1,47 @@
+<script>
+import { isEmpty } from 'lodash';
+import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
+import TitleArea from '~/vue_shared/components/registry/title_area.vue';
+import timeagoMixin from '~/vue_shared/mixins/timeago';
+import {
+ ROOT_IMAGE_TEXT,
+ EMPTY_ARTIFACTS_LABEL,
+ artifactsLabel,
+} from '~/packages_and_registries/harbor_registry/constants/index';
+
+export default {
+ name: 'DetailsHeader',
+ components: { TitleArea, MetadataItem },
+ mixins: [timeagoMixin],
+ props: {
+ imagesDetail: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ artifactCountText() {
+ if (isEmpty(this.imagesDetail)) {
+ return EMPTY_ARTIFACTS_LABEL;
+ }
+ return artifactsLabel(this.imagesDetail.artifactCount);
+ },
+ repositoryFullName() {
+ return this.imagesDetail.name || ROOT_IMAGE_TEXT;
+ },
+ },
+};
+</script>
+
+<template>
+ <title-area>
+ <template #title>
+ <span data-testid="title">
+ {{ repositoryFullName }}
+ </span>
+ </template>
+ <template #metadata-tags-count>
+ <metadata-item icon="package" :text="artifactCountText" data-testid="artifacts-count" />
+ </template>
+ </title-area>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/components/harbor_registry_breadcrumb.vue b/app/assets/javascripts/packages_and_registries/harbor_registry/components/harbor_registry_breadcrumb.vue
new file mode 100644
index 00000000000..ac1df5cf93f
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/components/harbor_registry_breadcrumb.vue
@@ -0,0 +1,68 @@
+<script>
+// Since app/assets/javascripts/packages_and_registries/shared/components/registry_breadcrumb.vue
+// can only handle two levels of breadcrumbs, but we have three levels here.
+// So we extended the registry_breadcrumb.vue component with harbor_registry_breadcrumb.vue to support multiple levels of breadcrumbs
+import { GlBreadcrumb, GlIcon } from '@gitlab/ui';
+import { isArray, last } from 'lodash';
+
+export default {
+ components: {
+ GlBreadcrumb,
+ GlIcon,
+ },
+ computed: {
+ rootRoute() {
+ return this.$router.options.routes.find((r) => r.meta.root);
+ },
+ isRootRoute() {
+ return this.$route.name === this.rootRoute.name;
+ },
+ currentRoute() {
+ const currentName = this.$route.meta.nameGenerator();
+ const currentHref = this.$route.meta.hrefGenerator();
+ let routeInfoList = [
+ {
+ text: currentName,
+ to: currentHref,
+ },
+ ];
+
+ if (isArray(currentName) && isArray(currentHref)) {
+ routeInfoList = currentName.map((name, index) => {
+ return {
+ text: name,
+ to: currentHref[index],
+ };
+ });
+ }
+
+ return routeInfoList;
+ },
+ isLoaded() {
+ return this.isRootRoute || last(this.currentRoute).text;
+ },
+ allCrumbs() {
+ let crumbs = [
+ {
+ text: this.rootRoute.meta.nameGenerator(),
+ to: this.rootRoute.path,
+ },
+ ];
+ if (!this.isRootRoute) {
+ crumbs = crumbs.concat(this.currentRoute);
+ }
+ return crumbs;
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-breadcrumb :key="isLoaded" :items="allCrumbs">
+ <template #separator>
+ <span class="gl-mx-n5">
+ <gl-icon name="chevron-lg-right" :size="8" />
+ </span>
+ </template>
+ </gl-breadcrumb>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/components/list/harbor_list_header.vue b/app/assets/javascripts/packages_and_registries/harbor_registry/components/list/harbor_list_header.vue
index 086b9c73d75..db66ebef937 100644
--- a/app/assets/javascripts/packages_and_registries/harbor_registry/components/list/harbor_list_header.vue
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/components/list/harbor_list_header.vue
@@ -5,6 +5,7 @@ import {
HARBOR_REGISTRY_TITLE,
LIST_INTRO_TEXT,
imagesCountInfoText,
+ HARBOR_REGISTRY_HELP_PAGE_PATH,
} from '~/packages_and_registries/harbor_registry/constants';
import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
@@ -20,11 +21,6 @@ export default {
default: 0,
required: false,
},
- helpPagePath: {
- type: String,
- default: '',
- required: false,
- },
metadataLoading: {
type: Boolean,
required: false,
@@ -32,7 +28,7 @@ export default {
},
},
i18n: {
- HARBOR_REGISTRY_TITLE,
+ harborRegistryTitle: HARBOR_REGISTRY_TITLE,
},
computed: {
imagesCountText() {
@@ -40,7 +36,7 @@ export default {
return sprintf(pluralisedString, { count: this.imagesCount });
},
infoMessages() {
- return [{ text: LIST_INTRO_TEXT, link: this.helpPagePath }];
+ return [{ text: LIST_INTRO_TEXT, link: HARBOR_REGISTRY_HELP_PAGE_PATH }];
},
},
};
@@ -48,7 +44,7 @@ export default {
<template>
<title-area
- :title="$options.i18n.HARBOR_REGISTRY_TITLE"
+ :title="$options.i18n.harborRegistryTitle"
:info-messages="infoMessages"
:metadata-loading="metadataLoading"
>
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/components/list/harbor_list_row.vue b/app/assets/javascripts/packages_and_registries/harbor_registry/components/list/harbor_list_row.vue
index 258472fe16e..bfe0c250dd9 100644
--- a/app/assets/javascripts/packages_and_registries/harbor_registry/components/list/harbor_list_row.vue
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/components/list/harbor_list_row.vue
@@ -1,15 +1,14 @@
<script>
-import { GlIcon, GlSprintf, GlSkeletonLoader } from '@gitlab/ui';
+import { GlIcon, GlSkeletonLoader } from '@gitlab/ui';
import { n__ } from '~/locale';
-
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import ListItem from '~/vue_shared/components/registry/list_item.vue';
+import { getNameFromParams } from '~/packages_and_registries/harbor_registry/utils';
export default {
name: 'HarborListRow',
components: {
ClipboardButton,
- GlSprintf,
GlIcon,
ListItem,
GlSkeletonLoader,
@@ -26,19 +25,18 @@ export default {
},
},
computed: {
- id() {
- return this.item.id;
+ linkTo() {
+ const { projectName, imageName } = getNameFromParams(this.item.name);
+
+ return { name: 'details', params: { project: projectName, image: imageName } };
},
artifactCountText() {
return n__(
- 'HarborRegistry|%{count} Tag',
- 'HarborRegistry|%{count} Tags',
+ 'HarborRegistry|%d artifact',
+ 'HarborRegistry|%d artifacts',
this.item.artifactCount,
);
},
- imageName() {
- return this.item.name;
- },
},
};
</script>
@@ -50,9 +48,9 @@ export default {
class="gl-text-body gl-font-weight-bold"
data-testid="details-link"
data-qa-selector="registry_image_content"
- :to="{ name: 'details', params: { id } }"
+ :to="linkTo"
>
- {{ imageName }}
+ {{ item.name }}
</router-link>
<clipboard-button
v-if="item.location"
@@ -63,13 +61,9 @@ export default {
</template>
<template #left-secondary>
<template v-if="!metadataLoading">
- <span class="gl-display-flex gl-align-items-center" data-testid="tags-count">
- <gl-icon name="tag" class="gl-mr-2" />
- <gl-sprintf :message="artifactCountText">
- <template #count>
- {{ item.artifactCount }}
- </template>
- </gl-sprintf>
+ <span class="gl-display-flex gl-align-items-center" data-testid="artifacts-count">
+ <gl-icon name="package" class="gl-mr-2" />
+ {{ artifactCountText }}
</span>
</template>
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/components/tags/tags_header.vue b/app/assets/javascripts/packages_and_registries/harbor_registry/components/tags/tags_header.vue
new file mode 100644
index 00000000000..e7f6989c49f
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/components/tags/tags_header.vue
@@ -0,0 +1,54 @@
+<script>
+import { isEmpty } from 'lodash';
+import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
+import TitleArea from '~/vue_shared/components/registry/title_area.vue';
+import timeagoMixin from '~/vue_shared/mixins/timeago';
+import {
+ EMPTY_TAG_LABEL,
+ tagsCountText,
+} from '~/packages_and_registries/harbor_registry/constants';
+
+export default {
+ name: 'TagsHeader',
+ components: {
+ TitleArea,
+ MetadataItem,
+ },
+ mixins: [timeagoMixin],
+ props: {
+ artifactDetail: {
+ type: Object,
+ required: true,
+ },
+ pageInfo: {
+ type: Object,
+ required: true,
+ },
+ tagsLoading: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ computed: {
+ tagCountText() {
+ if (isEmpty(this.pageInfo)) {
+ return EMPTY_TAG_LABEL;
+ }
+ return tagsCountText(this.pageInfo.total);
+ },
+ },
+};
+</script>
+
+<template>
+ <title-area :metadata-loading="tagsLoading">
+ <template #title>
+ <span class="gl-word-break-all" data-testid="title">
+ {{ artifactDetail.digest }}
+ </span>
+ </template>
+ <template #metadata-tags-count>
+ <metadata-item icon="tag" :text="tagCountText" data-testid="tags-count" />
+ </template>
+ </title-area>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/components/tags/tags_list.vue b/app/assets/javascripts/packages_and_registries/harbor_registry/components/tags/tags_list.vue
new file mode 100644
index 00000000000..b34d3a950c0
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/components/tags/tags_list.vue
@@ -0,0 +1,82 @@
+<script>
+import { GlEmptyState } from '@gitlab/ui';
+import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue';
+import TagsLoader from '~/packages_and_registries/shared/components/tags_loader.vue';
+import TagsListRow from '~/packages_and_registries/harbor_registry/components/tags/tags_list_row.vue';
+import {
+ NO_ARTIFACTS_TITLE,
+ NO_TAGS_MATCHING_FILTERS_TITLE,
+ NO_TAGS_MATCHING_FILTERS_DESCRIPTION,
+} from '~/packages_and_registries/harbor_registry/constants';
+
+export default {
+ name: 'TagsList',
+ components: {
+ GlEmptyState,
+ TagsLoader,
+ TagsListRow,
+ RegistryList,
+ },
+ inject: ['noContainersImage'],
+ props: {
+ tags: {
+ type: Array,
+ required: true,
+ },
+ pageInfo: {
+ type: Object,
+ required: true,
+ },
+ isLoading: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
+ },
+ computed: {
+ hasNoTags() {
+ return this.tags.length === 0;
+ },
+ emptyStateTitle() {
+ return this.filter ? NO_TAGS_MATCHING_FILTERS_TITLE : NO_ARTIFACTS_TITLE;
+ },
+ emptyStateDescription() {
+ return this.filter ? NO_TAGS_MATCHING_FILTERS_DESCRIPTION : '';
+ },
+ },
+ methods: {
+ fetchNextPage() {
+ this.$emit('next-page');
+ },
+ fetchPreviousPage() {
+ this.$emit('prev-page');
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <tags-loader v-if="isLoading" />
+ <gl-empty-state
+ v-else-if="hasNoTags"
+ :title="emptyStateTitle"
+ :svg-path="noContainersImage"
+ :description="emptyStateDescription"
+ class="gl-mx-auto gl-my-0"
+ />
+ <registry-list
+ v-else
+ :pagination="pageInfo"
+ :items="tags"
+ hidden-delete
+ id-property="name"
+ @prev-page="fetchPreviousPage"
+ @next-page="fetchNextPage"
+ >
+ <template #default="{ item }">
+ <tags-list-row :tag="item" />
+ </template>
+ </registry-list>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/components/tags/tags_list_row.vue b/app/assets/javascripts/packages_and_registries/harbor_registry/components/tags/tags_list_row.vue
new file mode 100644
index 00000000000..63e046c1abc
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/components/tags/tags_list_row.vue
@@ -0,0 +1,74 @@
+<script>
+import { GlSprintf } from '@gitlab/ui';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import ListItem from '~/vue_shared/components/registry/list_item.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import { CREATED_AT_LABEL } from '~/packages_and_registries/harbor_registry/constants';
+import { tagPullCommand } from '~/packages_and_registries/harbor_registry/utils';
+
+export default {
+ name: 'TagsListRow',
+ components: {
+ GlSprintf,
+ ListItem,
+ ClipboardButton,
+ TimeAgoTooltip,
+ },
+ inject: ['harborIntegrationProjectName', 'repositoryUrl'],
+ props: {
+ tag: {
+ type: Object,
+ required: true,
+ },
+ },
+ i18n: {
+ createdAtLabel: CREATED_AT_LABEL,
+ },
+ methods: {
+ getPullCommand(tagName) {
+ if (tagName) {
+ const { image } = this.$route.params;
+
+ return tagPullCommand({
+ imageName: image,
+ tag: tagName,
+ repositoryUrl: this.repositoryUrl,
+ harborProjectName: this.harborIntegrationProjectName,
+ });
+ }
+
+ return '';
+ },
+ },
+};
+</script>
+
+<template>
+ <list-item v-bind="$attrs">
+ <template #left-primary>
+ <div class="gl-display-flex gl-align-items-center">
+ <div
+ data-testid="name"
+ class="gl-text-overflow-ellipsis gl-overflow-hidden gl-white-space-nowrap"
+ >
+ {{ tag.name }}
+ </div>
+ <clipboard-button
+ :title="getPullCommand(tag.name)"
+ :text="getPullCommand(tag.name)"
+ category="tertiary"
+ />
+ </div>
+ </template>
+
+ <template #right-primary>
+ <span data-testid="time">
+ <gl-sprintf :message="$options.i18n.createdAtLabel">
+ <template #timeInfo>
+ <time-ago-tooltip :time="tag.pushTime" />
+ </template>
+ </gl-sprintf>
+ </span>
+ </template>
+ </list-item>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/constants/common.js b/app/assets/javascripts/packages_and_registries/harbor_registry/constants/common.js
index a7891821755..7f3c3da02b0 100644
--- a/app/assets/javascripts/packages_and_registries/harbor_registry/constants/common.js
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/constants/common.js
@@ -1,4 +1,5 @@
import { s__, __ } from '~/locale';
+import { helpPagePath } from '~/helpers/help_page_helper';
export const ROOT_IMAGE_TEXT = s__('HarborRegistry|Root image');
export const NAME_SORT_FIELD = { orderBy: 'NAME', label: __('Name') };
@@ -16,14 +17,8 @@ export const SORT_FIELD_MAPPING = {
CREATED: CREATED_SORT_FIELD_KEY,
};
-/* eslint-disable @gitlab/require-i18n-strings */
-export const dockerBuildCommand = (repositoryUrl) => {
- return `docker build -t ${repositoryUrl} .`;
-};
-export const dockerPushCommand = (repositoryUrl) => {
- return `docker push ${repositoryUrl}`;
-};
-export const dockerLoginCommand = (registryHostUrlWithPort) => {
- return `docker login ${registryHostUrlWithPort}`;
-};
-/* eslint-enable @gitlab/require-i18n-strings */
+export const DEFAULT_PER_PAGE = 10;
+
+export const HARBOR_REGISTRY_HELP_PAGE_PATH = helpPagePath(
+ 'user/packages/harbor_container_registry/index',
+);
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/constants/details.js b/app/assets/javascripts/packages_and_registries/harbor_registry/constants/details.js
index b62c51bd208..5b4b85ec31e 100644
--- a/app/assets/javascripts/packages_and_registries/harbor_registry/constants/details.js
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/constants/details.js
@@ -1,22 +1,10 @@
-import { s__, __ } from '~/locale';
+import { s__, __, n__ } from '~/locale';
-export const UPDATED_AT = s__('HarborRegistry|Last updated %{time}');
-
-export const MISSING_OR_DELETED_IMAGE_TITLE = s__(
- 'HarborRegistry|The image repository could not be found.',
-);
-
-export const MISSING_OR_DELETED_IMAGE_MESSAGE = s__(
- 'HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page.',
+export const FETCH_ARTIFACT_LIST_ERROR_MESSAGE = s__(
+ 'HarborRegistry|Something went wrong while fetching the artifact list.',
);
-export const NO_TAGS_TITLE = s__('HarborRegistry|This image has no active tags');
-
-export const NO_TAGS_MESSAGE = s__(
- `HarborRegistry|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.`,
-);
+export const NO_ARTIFACTS_TITLE = s__('HarborRegistry|This image has no artifacts');
export const NO_TAGS_MATCHING_FILTERS_TITLE = s__('HarborRegistry|The filter returned no results');
@@ -26,14 +14,24 @@ export const NO_TAGS_MATCHING_FILTERS_DESCRIPTION = s__(
export const DIGEST_LABEL = s__('HarborRegistry|Digest: %{imageId}');
export const CREATED_AT_LABEL = s__('HarborRegistry|Published %{timeInfo}');
-export const PUBLISHED_DETAILS_ROW_TEXT = s__(
- 'HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}',
-);
-export const MANIFEST_DETAILS_ROW_TEST = s__('HarborRegistry|Manifest digest: %{digest}');
-export const CONFIGURATION_DETAILS_ROW_TEST = s__('HarborRegistry|Configuration digest: %{digest}');
-export const MISSING_MANIFEST_WARNING_TOOLTIP = s__(
- 'HarborRegistry|Invalid tag: missing manifest digest',
-);
export const NOT_AVAILABLE_TEXT = __('Not applicable.');
export const NOT_AVAILABLE_SIZE = __('0 bytes');
+
+export const TOKEN_TYPE_TAG_NAME = 'tag_name';
+
+export const FETCH_TAGS_ERROR_MESSAGE = s__(
+ 'HarborRegistry|Something went wrong while fetching the tags.',
+);
+
+export const TAG_LABEL = s__('HarborRegistry|Tag');
+export const EMPTY_TAG_LABEL = s__('HarborRegistry|-- tags');
+
+export const EMPTY_ARTIFACTS_LABEL = s__('HarborRegistry|-- artifacts');
+export const artifactsLabel = (count) => {
+ return n__('%d artifact', '%d artifacts', count);
+};
+
+export const tagsCountText = (count) => {
+ return n__('%d tag', '%d tags', count);
+};
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/constants/list.js b/app/assets/javascripts/packages_and_registries/harbor_registry/constants/list.js
index a6cd59918ff..33950993125 100644
--- a/app/assets/javascripts/packages_and_registries/harbor_registry/constants/list.js
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/constants/list.js
@@ -7,8 +7,13 @@ export const HARBOR_REGISTRY_TITLE = s__('HarborRegistry|Harbor Registry');
export const CONNECTION_ERROR_TITLE = s__('HarborRegistry|Harbor connection error');
export const CONNECTION_ERROR_MESSAGE = s__(
- `HarborRegistry|We are having trouble connecting to the Harbor Registry. Please try refreshing the page. If this error persists, please review %{docLinkStart}the troubleshooting documentation%{docLinkEnd}.`,
+ `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}.`,
);
+
+export const FETCH_IMAGES_LIST_ERROR_MESSAGE = s__(
+ 'HarborRegistry|Something went wrong while fetching the repository list.',
+);
+
export const LIST_INTRO_TEXT = s__(
`HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}`,
);
@@ -26,6 +31,13 @@ export const EMPTY_RESULT_MESSAGE = s__(
'HarborRegistry|To widen your search, change or remove the filters above.',
);
+export const EMPTY_IMAGES_TITLE = s__(
+ 'HarborRegistry|There are no harbor images stored for this project',
+);
+export const EMPTY_IMAGES_MESSAGE = s__(
+ 'HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images.',
+);
+
export const SORT_FIELDS = [
{ orderBy: 'UPDATED', label: __('Updated') },
{ orderBy: 'CREATED', label: __('Created') },
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/index.js b/app/assets/javascripts/packages_and_registries/harbor_registry/index.js
index ecfefead61a..6185e4c7bc6 100644
--- a/app/assets/javascripts/packages_and_registries/harbor_registry/index.js
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/index.js
@@ -3,14 +3,8 @@ import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import PerformancePlugin from '~/performance/vue_performance_plugin';
import Translate from '~/vue_shared/translate';
-import RegistryBreadcrumb from '~/packages_and_registries/shared/components/registry_breadcrumb.vue';
+import RegistryBreadcrumb from '~/packages_and_registries/harbor_registry/components/harbor_registry_breadcrumb.vue';
import { renderBreadcrumb } from '~/packages_and_registries/shared/utils';
-import { helpPagePath } from '~/helpers/help_page_helper';
-import {
- dockerBuildCommand,
- dockerPushCommand,
- dockerLoginCommand,
-} from '~/packages_and_registries/harbor_registry/constants';
import createRouter from './router';
import HarborRegistryExplorer from './pages/index.vue';
@@ -35,13 +29,27 @@ export default (id) => {
return null;
}
- const { endpoint, connectionError, invalidPathError, isGroupPage, ...config } = el.dataset;
+ const {
+ endpoint,
+ connectionError,
+ invalidPathError,
+ isGroupPage,
+ noContainersImage,
+ containersErrorImage,
+ repositoryUrl,
+ harborIntegrationProjectName,
+ projectName,
+ } = el.dataset;
const breadCrumbState = Vue.observable({
name: '',
+ href: '',
updateName(value) {
this.name = value;
},
+ updateHref(value) {
+ this.href = value;
+ },
});
const router = createRouter(endpoint, breadCrumbState);
@@ -53,16 +61,15 @@ export default (id) => {
provide() {
return {
breadCrumbState,
- config: {
- ...config,
- connectionError: parseBoolean(connectionError),
- invalidPathError: parseBoolean(invalidPathError),
- isGroupPage: parseBoolean(isGroupPage),
- helpPagePath: helpPagePath('user/packages/container_registry/index'),
- },
- dockerBuildCommand: dockerBuildCommand(config.repositoryUrl),
- dockerPushCommand: dockerPushCommand(config.repositoryUrl),
- dockerLoginCommand: dockerLoginCommand(config.registryHostUrlWithPort),
+ endpoint,
+ connectionError: parseBoolean(connectionError),
+ invalidPathError: parseBoolean(invalidPathError),
+ isGroupPage: parseBoolean(isGroupPage),
+ repositoryUrl,
+ harborIntegrationProjectName,
+ projectName,
+ containersErrorImage,
+ noContainersImage,
};
},
render(createElement) {
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/mock_api.js b/app/assets/javascripts/packages_and_registries/harbor_registry/mock_api.js
deleted file mode 100644
index 50c7df1483c..00000000000
--- a/app/assets/javascripts/packages_and_registries/harbor_registry/mock_api.js
+++ /dev/null
@@ -1,200 +0,0 @@
-const mockRequestFn = (mockData) => {
- return new Promise((resolve) => {
- setTimeout(() => {
- resolve(mockData);
- }, 2000);
- });
-};
-export const harborListResponse = () => {
- const harborListResponseData = {
- repositories: [
- {
- artifactCount: 1,
- creationTime: '2022-03-02T06:35:53.205Z',
- id: 25,
- name: 'shao/flinkx',
- projectId: 21,
- pullCount: 0,
- updateTime: '2022-03-02T06:35:53.205Z',
- location: 'demo.harbor.com/gitlab-cn/build/cng-images/gitlab-kas',
- },
- {
- artifactCount: 1,
- creationTime: '2022-03-02T06:35:53.205Z',
- id: 26,
- name: 'shao/flinkx1',
- projectId: 21,
- pullCount: 0,
- updateTime: '2022-03-02T06:35:53.205Z',
- location: 'demo.harbor.com/gitlab-cn/build/cng-images/gitlab-kas',
- },
- {
- artifactCount: 1,
- creationTime: '2022-03-02T06:35:53.205Z',
- id: 27,
- name: 'shao/flinkx2',
- projectId: 21,
- pullCount: 0,
- updateTime: '2022-03-02T06:35:53.205Z',
- location: 'demo.harbor.com/gitlab-cn/build/cng-images/gitlab-kas',
- },
- ],
- totalCount: 3,
- pageInfo: {
- hasNextPage: false,
- hasPreviousPage: false,
- },
- };
-
- return mockRequestFn(harborListResponseData);
-};
-
-export const getHarborRegistryImageDetail = () => {
- const harborRegistryImageDetailData = {
- artifactCount: 1,
- creationTime: '2022-03-02T06:35:53.205Z',
- id: 25,
- name: 'shao/flinkx',
- projectId: 21,
- pullCount: 0,
- updateTime: '2022-03-02T06:35:53.205Z',
- location: 'demo.harbor.com/gitlab-cn/build/cng-images/gitlab-kas',
- tagsCount: 10,
- };
-
- return mockRequestFn(harborRegistryImageDetailData);
-};
-
-export const harborTagsResponse = () => {
- const harborTagsResponseData = {
- tags: [
- {
- digest: 'sha256:7f386a1844faf341353e1c20f2f39f11f397604fedc475435d13f756eeb235d1',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:02310e655103823920157bc4410ea361dc638bc2cda59667d2cb1f2a988e264c',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:02310e655103823920157bc4410ea361dc638bc2cda59667d2cb1f2a988e264c',
- name: '02310e655103823920157bc4410ea361dc638bc2cda59667d2cb1f2a988e264c',
- revision: 'f53bde3d44699e04e11cf15fb415046a0913e2623d878d89bc21adb2cbda5255',
- shortRevision: 'f53bde3d4',
- createdAt: '2022-03-02T23:59:05+00:00',
- totalSize: '6623124',
- },
- {
- digest: 'sha256:4554416b84c4568fe93086620b637064ed029737aabe7308b96d50e3d9d92ed7',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:02deb4dddf177212b50e883d5e4f6c03731fad1a18cd27261736cd9dbba79160',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:02deb4dddf177212b50e883d5e4f6c03731fad1a18cd27261736cd9dbba79160',
- name: '02deb4dddf177212b50e883d5e4f6c03731fad1a18cd27261736cd9dbba79160',
- revision: 'e1fe52d8bab66d71bd54a6b8784d3b9edbc68adbd6ea87f5fa44d9974144ef9e',
- shortRevision: 'e1fe52d8b',
- createdAt: '2022-02-10T01:09:56+00:00',
- totalSize: '920760',
- },
- {
- digest: 'sha256:14f37b60e52b9ce0e9f8f7094b311d265384798592f783487c30aaa3d58e6345',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:03bc5971bab1e849ba52a20a31e7273053f22b2ddb1d04bd6b77d53a2635727a',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:03bc5971bab1e849ba52a20a31e7273053f22b2ddb1d04bd6b77d53a2635727a',
- name: '03bc5971bab1e849ba52a20a31e7273053f22b2ddb1d04bd6b77d53a2635727a',
- revision: 'c72770c6eb93c421bc496964b4bffc742b1ec2e642cdab876be7afda1856029f',
- shortRevision: 'c72770c6e',
- createdAt: '2021-12-22T04:48:48+00:00',
- totalSize: '48609053',
- },
- {
- digest: 'sha256:e925e3b8277ea23f387ed5fba5e78280cfac7cfb261a78cf046becf7b6a3faae',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:03f495bc5714bff78bb14293320d336afdf47fd47ddff0c3c5f09f8da86d5d19',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:03f495bc5714bff78bb14293320d336afdf47fd47ddff0c3c5f09f8da86d5d19',
- name: '03f495bc5714bff78bb14293320d336afdf47fd47ddff0c3c5f09f8da86d5d19',
- revision: '1ac2a43194f4e15166abdf3f26e6ec92215240490b9cac834d63de1a3d87494a',
- shortRevision: '1ac2a4319',
- createdAt: '2022-03-09T11:02:27+00:00',
- totalSize: '35141894',
- },
- {
- digest: 'sha256:7d8303fd5c077787a8c879f8f66b69e2b5605f48ccd3f286e236fb0749fcc1ca',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:05a4e58231e54b70aab2d6f22ba4fbe10e48aa4ddcbfef11c5662241c2ae4fda',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:05a4e58231e54b70aab2d6f22ba4fbe10e48aa4ddcbfef11c5662241c2ae4fda',
- name: '05a4e58231e54b70aab2d6f22ba4fbe10e48aa4ddcbfef11c5662241c2ae4fda',
- revision: 'cf8fee086701016e1a84e6824f0c896951fef4cce9d4745459558b87eec3232c',
- shortRevision: 'cf8fee086',
- createdAt: '2022-01-21T11:31:43+00:00',
- totalSize: '48716070',
- },
- {
- digest: 'sha256:b33611cefe20e4a41a6e0dce356a5d7ef3c177ea7536a58652f5b3a4f2f83549',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:093d2746876997723541aec8b88687a4cdb3b5bbb0279c5089b7891317741a9a',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:093d2746876997723541aec8b88687a4cdb3b5bbb0279c5089b7891317741a9a',
- name: '093d2746876997723541aec8b88687a4cdb3b5bbb0279c5089b7891317741a9a',
- revision: '1a4b48198b13d55242c5164e64d41c4e9f75b5d9506bc6e0efc1534dd0dd1f15',
- shortRevision: '1a4b48198',
- createdAt: '2022-01-21T11:31:51+00:00',
- totalSize: '6623127',
- },
- {
- digest: 'sha256:d25c3c020e2dbd4711a67b9fe308f4cbb7b0bb21815e722a02f91c570dc5d519',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:09698b3fae81dfd6e02554dbc82930f304a6356c8f541c80e8598a42aed985f7',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:09698b3fae81dfd6e02554dbc82930f304a6356c8f541c80e8598a42aed985f7',
- name: '09698b3fae81dfd6e02554dbc82930f304a6356c8f541c80e8598a42aed985f7',
- revision: '03e2e2777dde01c30469ee8c710973dd08a7a4f70494d7dc1583c24b525d7f61',
- shortRevision: '03e2e2777',
- createdAt: '2022-03-02T23:58:20+00:00',
- totalSize: '911377',
- },
- {
- digest: 'sha256:fb760e4d2184e9e8e39d6917534d4610fe01009734698a5653b2de1391ba28f4',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:09b830c3eaf80d547f3b523d8e242a2c411085c349dab86c520f36c7b7644f95',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:09b830c3eaf80d547f3b523d8e242a2c411085c349dab86c520f36c7b7644f95',
- name: '09b830c3eaf80d547f3b523d8e242a2c411085c349dab86c520f36c7b7644f95',
- revision: '350e78d60646bf6967244448c6aaa14d21ecb9a0c6cf87e9ff0361cbe59b9012',
- shortRevision: '350e78d60',
- createdAt: '2022-01-19T13:49:14+00:00',
- totalSize: '48710241',
- },
- {
- digest: 'sha256:407250f380cea92729cbc038c420e74900f53b852e11edc6404fe75a0fd2c402',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:0d03504a17b467eafc8c96bde70af26c74bd459a32b7eb2dd189dd6b3c121557',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:0d03504a17b467eafc8c96bde70af26c74bd459a32b7eb2dd189dd6b3c121557',
- name: '0d03504a17b467eafc8c96bde70af26c74bd459a32b7eb2dd189dd6b3c121557',
- revision: '76038370b7f3904364891457c4a6a234897255e6b9f45d0a852bf3a7e5257e18',
- shortRevision: '76038370b',
- createdAt: '2022-01-24T12:56:22+00:00',
- totalSize: '280065',
- },
- {
- digest: 'sha256:ada87f25218542951ce6720c27f3d0758e90c2540bd129f5cfb9e15b31e07b07',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:0eb20a4a7cac2ebea821d420b3279654fe550fd8502f1785c1927aa84e5949eb',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:0eb20a4a7cac2ebea821d420b3279654fe550fd8502f1785c1927aa84e5949eb',
- name: '0eb20a4a7cac2ebea821d420b3279654fe550fd8502f1785c1927aa84e5949eb',
- revision: '3d4b49a7bbb36c48bb721f4d0e76e7950bec3878ee29cdfdd6da39f575d6d37f',
- shortRevision: '3d4b49a7b',
- createdAt: '2022-02-17T17:37:52+00:00',
- totalSize: '48655767',
- },
- ],
- totalCount: 10,
- pageInfo: {
- hasNextPage: false,
- hasPreviousPage: true,
- },
- };
-
- return mockRequestFn(harborTagsResponseData);
-};
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 e69de29bb2d..c6ab746b9f4 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
@@ -0,0 +1,156 @@
+<script>
+import { GlFilteredSearchToken } from '@gitlab/ui';
+import {
+ NAME_SORT_FIELD,
+ ROOT_IMAGE_TEXT,
+ DEFAULT_PER_PAGE,
+ FETCH_ARTIFACT_LIST_ERROR_MESSAGE,
+ 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 { createAlert } from '~/flash';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import TagsLoader from '~/packages_and_registries/shared/components/tags_loader.vue';
+import ArtifactsList from '~/packages_and_registries/harbor_registry/components/details/artifacts_list.vue';
+import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue';
+import DetailsHeader from '~/packages_and_registries/harbor_registry/components/details/details_header.vue';
+import {
+ extractSortingDetail,
+ parseFilter,
+ formatPagination,
+} from '~/packages_and_registries/harbor_registry/utils';
+import { getHarborArtifacts } from '~/rest_api';
+
+export default {
+ name: 'HarborDetailsPage',
+ components: {
+ ArtifactsList,
+ TagsLoader,
+ DetailsHeader,
+ PersistedSearch,
+ },
+ inject: ['endpoint', 'breadCrumbState'],
+ searchConfig: { nameSortFields: [NAME_SORT_FIELD] },
+ tokens: [
+ {
+ type: TOKEN_TYPE_TAG_NAME,
+ icon: 'tag',
+ title: TAG_LABEL,
+ unique: true,
+ token: GlFilteredSearchToken,
+ operators: OPERATOR_IS_ONLY,
+ },
+ ],
+ data() {
+ return {
+ artifactsList: [],
+ pageInfo: {},
+ mutationLoading: false,
+ deleteAlertType: null,
+ isLoading: true,
+ filterString: '',
+ sorting: null,
+ };
+ },
+ computed: {
+ currentPage() {
+ return this.pageInfo.page || 1;
+ },
+ imagesDetail() {
+ return {
+ name: this.fullName,
+ artifactCount: this.pageInfo?.total || 0,
+ };
+ },
+ fullName() {
+ const { project, image } = this.$route.params;
+
+ if (project && image) {
+ return `${project}/${image}`;
+ }
+ return '';
+ },
+ },
+ mounted() {
+ this.updateBreadcrumb();
+ },
+ methods: {
+ updateBreadcrumb() {
+ const name = this.fullName || ROOT_IMAGE_TEXT;
+ this.breadCrumbState.updateName(name);
+ this.breadCrumbState.updateHref(this.$route.path);
+ },
+ handleSearchUpdate({ sort, filters }) {
+ this.sorting = sort;
+ this.filterString = parseFilter(filters, 'digest');
+
+ this.fetchArtifacts(1);
+ },
+ fetchPrevPage() {
+ const prevPageNum = this.currentPage - 1;
+ this.fetchArtifacts(prevPageNum);
+ },
+ fetchNextPage() {
+ const nextPageNum = this.currentPage + 1;
+ this.fetchArtifacts(nextPageNum);
+ },
+ fetchArtifacts(requestPage) {
+ this.isLoading = true;
+
+ const { orderBy, sort } = extractSortingDetail(this.sorting);
+ const sortOptions = `${orderBy} ${sort}`;
+
+ const { image } = this.$route.params;
+
+ const params = {
+ requestPath: this.endpoint,
+ repoName: image,
+ limit: DEFAULT_PER_PAGE,
+ page: requestPage,
+ sort: sortOptions,
+ search: this.filterString,
+ };
+
+ getHarborArtifacts(params)
+ .then((res) => {
+ this.pageInfo = formatPagination(res.headers);
+
+ this.artifactsList = (res?.data || []).map((artifact) => {
+ return convertObjectPropsToCamelCase(artifact);
+ });
+ })
+ .catch(() => {
+ createAlert({ message: FETCH_ARTIFACT_LIST_ERROR_MESSAGE });
+ })
+ .finally(() => {
+ this.isLoading = false;
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="gl-my-3">
+ <details-header :images-detail="imagesDetail" />
+ <persisted-search
+ class="gl-mb-5"
+ :sortable-fields="$options.searchConfig.nameSortFields"
+ :default-order="$options.searchConfig.nameSortFields[0].orderBy"
+ default-sort="asc"
+ :tokens="$options.tokens"
+ @update="handleSearchUpdate"
+ />
+ <tags-loader v-if="isLoading" />
+ <artifacts-list
+ v-else
+ :filter="filterString"
+ :is-loading="isLoading"
+ :artifacts="artifactsList"
+ :page-info="pageInfo"
+ @prev-page="fetchPrevPage"
+ @next-page="fetchNextPage"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/pages/harbor_tags.vue b/app/assets/javascripts/packages_and_registries/harbor_registry/pages/harbor_tags.vue
new file mode 100644
index 00000000000..1323d347d10
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/pages/harbor_tags.vue
@@ -0,0 +1,103 @@
+<script>
+import TagsHeader from '~/packages_and_registries/harbor_registry/components/tags/tags_header.vue';
+import TagsList from '~/packages_and_registries/harbor_registry/components/tags/tags_list.vue';
+import { getHarborTags } from '~/rest_api';
+import { FETCH_TAGS_ERROR_MESSAGE } from '~/packages_and_registries/harbor_registry/constants';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import { createAlert } from '~/flash';
+import { formatPagination } from '~/packages_and_registries/harbor_registry/utils';
+
+export default {
+ name: 'HarborTagsPage',
+ components: {
+ TagsHeader,
+ TagsList,
+ },
+ inject: ['endpoint', 'breadCrumbState'],
+ data() {
+ return {
+ tagsLoading: false,
+ pageInfo: {},
+ tags: [],
+ };
+ },
+ computed: {
+ currentPage() {
+ return this.pageInfo?.page || 1;
+ },
+ artifactDetail() {
+ const { project, image, digest } = this.$route.params;
+
+ return {
+ project,
+ image,
+ digest,
+ };
+ },
+ },
+ mounted() {
+ this.updateBreadcrumb();
+ this.fetchTagsData();
+ },
+ methods: {
+ updateBreadcrumb() {
+ const artifactPath = `${this.artifactDetail.project}/${this.artifactDetail.image}`;
+ const nameList = [artifactPath, this.artifactDetail.digest];
+ const hrefList = [`/${artifactPath}`, this.$route.path];
+
+ this.breadCrumbState.updateName(nameList);
+ this.breadCrumbState.updateHref(hrefList);
+ },
+ fetchPrevPage() {
+ const prevPageNum = this.currentPage - 1;
+ this.fetchTagsData(prevPageNum);
+ },
+ fetchNextPage() {
+ const nextPageNum = this.currentPage + 1;
+ this.fetchTagsData(nextPageNum);
+ },
+ fetchTagsData(requestPage) {
+ this.tagsLoading = true;
+
+ const params = {
+ page: requestPage,
+ requestPath: this.endpoint,
+ repoName: this.artifactDetail.image,
+ digest: this.artifactDetail.digest,
+ };
+
+ getHarborTags(params)
+ .then((res) => {
+ this.pageInfo = formatPagination(res.headers);
+
+ this.tags = (res?.data || []).map((tagInfo) => {
+ return convertObjectPropsToCamelCase(tagInfo);
+ });
+ })
+ .catch(() => {
+ createAlert({ message: FETCH_TAGS_ERROR_MESSAGE });
+ })
+ .finally(() => {
+ this.tagsLoading = false;
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <tags-header
+ :artifact-detail="artifactDetail"
+ :page-info="pageInfo"
+ :tags-loading="tagsLoading"
+ />
+ <tags-list
+ :tags="tags"
+ :is-loading="tagsLoading"
+ :page-info="pageInfo"
+ @prev-page="fetchPrevPage"
+ @next-page="fetchNextPage"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/pages/list.vue b/app/assets/javascripts/packages_and_registries/harbor_registry/pages/list.vue
index 9c69059c968..931a99649cb 100644
--- a/app/assets/javascripts/packages_and_registries/harbor_registry/pages/list.vue
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/pages/list.vue
@@ -1,19 +1,32 @@
<script>
import { GlEmptyState, GlSprintf, GlLink, GlSkeletonLoader } from '@gitlab/ui';
-import { escape } from 'lodash';
import HarborListHeader from '~/packages_and_registries/harbor_registry/components/list/harbor_list_header.vue';
import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue';
import HarborList from '~/packages_and_registries/harbor_registry/components/list/harbor_list.vue';
-import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import {
+ extractSortingDetail,
+ formatPagination,
+ parseFilter,
+ dockerBuildCommand,
+ dockerPushCommand,
+ dockerLoginCommand,
+} from '~/packages_and_registries/harbor_registry/utils';
+import { createAlert } from '~/flash';
import {
SORT_FIELDS,
CONNECTION_ERROR_TITLE,
CONNECTION_ERROR_MESSAGE,
EMPTY_RESULT_TITLE,
EMPTY_RESULT_MESSAGE,
+ DEFAULT_PER_PAGE,
+ FETCH_IMAGES_LIST_ERROR_MESSAGE,
+ EMPTY_IMAGES_TITLE,
+ EMPTY_IMAGES_MESSAGE,
+ HARBOR_REGISTRY_HELP_PAGE_PATH,
} from '~/packages_and_registries/harbor_registry/constants';
import Tracking from '~/tracking';
-import { harborListResponse } from '../mock_api';
+import { getHarborRepositoriesList } from '~/rest_api';
export default {
name: 'HarborListPage',
@@ -31,19 +44,28 @@ export default {
),
},
mixins: [Tracking.mixin()],
- inject: ['config', 'dockerBuildCommand', 'dockerPushCommand', 'dockerLoginCommand'],
+ inject: [
+ 'endpoint',
+ 'repositoryUrl',
+ 'harborIntegrationProjectName',
+ 'projectName',
+ 'isGroupPage',
+ 'connectionError',
+ 'invalidPathError',
+ 'containersErrorImage',
+ 'noContainersImage',
+ ],
loader: {
repeat: 10,
width: 1000,
height: 40,
},
i18n: {
- CONNECTION_ERROR_TITLE,
- CONNECTION_ERROR_MESSAGE,
- EMPTY_RESULT_TITLE,
- EMPTY_RESULT_MESSAGE,
+ connectionErrorTitle: CONNECTION_ERROR_TITLE,
+ connectionErrorMessage: CONNECTION_ERROR_MESSAGE,
},
searchConfig: SORT_FIELDS,
+ helpPagePath: HARBOR_REGISTRY_HELP_PAGE_PATH,
data() {
return {
images: [],
@@ -56,42 +78,81 @@ export default {
};
},
computed: {
+ dockerCommand() {
+ return {
+ build: dockerBuildCommand({
+ repositoryUrl: this.repositoryUrl,
+ harborProjectName: this.harborIntegrationProjectName,
+ projectName: this.projectName,
+ }),
+ push: dockerPushCommand({
+ repositoryUrl: this.repositoryUrl,
+ harborProjectName: this.harborIntegrationProjectName,
+ projectName: this.projectName,
+ }),
+ login: dockerLoginCommand(this.repositoryUrl),
+ };
+ },
showCommands() {
- return !this.isLoading && !this.config?.isGroupPage && this.images?.length;
+ return !this.isLoading && !this.isGroupPage && this.images?.length;
},
showConnectionError() {
- return this.config.connectionError || this.config.invalidPathError;
+ return this.connectionError || this.invalidPathError;
+ },
+ currentPage() {
+ return this.pageInfo.page || 1;
+ },
+ emptyStateTexts() {
+ return {
+ title: this.name ? EMPTY_RESULT_TITLE : EMPTY_IMAGES_TITLE,
+ message: this.name ? EMPTY_RESULT_MESSAGE : EMPTY_IMAGES_MESSAGE,
+ };
},
},
methods: {
- fetchHarborImages() {
- // TODO: Waiting for harbor api integration to finish: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82777
+ fetchHarborImages(requestPage) {
this.isLoading = true;
- harborListResponse()
+ const { orderBy, sort } = extractSortingDetail(this.sorting);
+ const sortOptions = `${orderBy} ${sort}`;
+
+ const params = {
+ requestPath: this.endpoint,
+ limit: DEFAULT_PER_PAGE,
+ search: this.name,
+ page: requestPage,
+ sort: sortOptions,
+ };
+
+ getHarborRepositoriesList(params)
.then((res) => {
- this.images = res?.repositories || [];
- this.totalCount = res?.totalCount || 0;
- this.pageInfo = res?.pageInfo || {};
+ this.images = (res?.data || []).map((item) => {
+ return convertObjectPropsToCamelCase(item);
+ });
+ const pagination = formatPagination(res.headers);
+
+ this.totalCount = pagination?.total || 0;
+ this.pageInfo = pagination;
+
this.isLoading = false;
})
- .catch(() => {});
+ .catch(() => {
+ createAlert({ message: FETCH_IMAGES_LIST_ERROR_MESSAGE });
+ });
},
handleSearchUpdate({ sort, filters }) {
this.sorting = sort;
+ this.name = parseFilter(filters, 'name');
- const search = filters.find((i) => i.type === FILTERED_SEARCH_TERM);
- this.name = escape(search?.value?.data);
-
- this.fetchHarborImages();
+ this.fetchHarborImages(1);
},
fetchPrevPage() {
- // TODO: Waiting for harbor api integration to finish: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82777
- this.fetchHarborImages();
+ const prevPageNum = this.currentPage - 1;
+ this.fetchHarborImages(prevPageNum);
},
fetchNextPage() {
- // TODO: Waiting for harbor api integration to finish: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82777
- this.fetchHarborImages();
+ const nextPageNum = this.currentPage + 1;
+ this.fetchHarborImages(nextPageNum);
},
},
};
@@ -101,14 +162,14 @@ export default {
<div>
<gl-empty-state
v-if="showConnectionError"
- :title="$options.i18n.CONNECTION_ERROR_TITLE"
- :svg-path="config.containersErrorImage"
+ :title="$options.i18n.connectionErrorTitle"
+ :svg-path="containersErrorImage"
>
<template #description>
<p>
- <gl-sprintf :message="$options.i18n.CONNECTION_ERROR_MESSAGE">
+ <gl-sprintf :message="$options.i18n.connectionErrorMessage">
<template #docLink="{ content }">
- <gl-link :href="`${config.helpPagePath}#docker-connection-error`" target="_blank">
+ <gl-link :href="$options.helpPagePath" target="_blank">
{{ content }}
</gl-link>
</template>
@@ -117,17 +178,13 @@ export default {
</template>
</gl-empty-state>
<template v-else>
- <harbor-list-header
- :metadata-loading="isLoading"
- :images-count="totalCount"
- :help-page-path="config.helpPagePath"
- >
+ <harbor-list-header :metadata-loading="isLoading" :images-count="totalCount">
<template #commands>
<cli-commands
v-if="showCommands"
- :docker-build-command="dockerBuildCommand"
- :docker-push-command="dockerPushCommand"
- :docker-login-command="dockerLoginCommand"
+ :docker-build-command="dockerCommand.build"
+ :docker-push-command="dockerCommand.push"
+ :docker-login-command="dockerCommand.login"
/>
</template>
</harbor-list-header>
@@ -152,26 +209,24 @@ export default {
</gl-skeleton-loader>
</div>
<template v-else>
- <template v-if="images.length > 0 || name">
- <harbor-list
- v-if="images.length"
- :images="images"
- :meta-data-loading="isLoading"
- :page-info="pageInfo"
- @prev-page="fetchPrevPage"
- @next-page="fetchNextPage"
- />
- <gl-empty-state
- v-else
- :svg-path="config.noContainersImage"
- data-testid="emptySearch"
- :title="$options.i18n.EMPTY_RESULT_TITLE"
- >
- <template #description>
- {{ $options.i18n.EMPTY_RESULT_MESSAGE }}
- </template>
- </gl-empty-state>
- </template>
+ <harbor-list
+ v-if="images.length"
+ :images="images"
+ :metadata-loading="isLoading"
+ :page-info="pageInfo"
+ @prev-page="fetchPrevPage"
+ @next-page="fetchNextPage"
+ />
+ <gl-empty-state
+ v-else
+ :svg-path="noContainersImage"
+ data-testid="emptySearch"
+ :title="emptyStateTexts.title"
+ >
+ <template #description>
+ {{ emptyStateTexts.message }}
+ </template>
+ </gl-empty-state>
</template>
</template>
</div>
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/router.js b/app/assets/javascripts/packages_and_registries/harbor_registry/router.js
index 572dd382be3..5a792e30c62 100644
--- a/app/assets/javascripts/packages_and_registries/harbor_registry/router.js
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/router.js
@@ -3,6 +3,7 @@ import VueRouter from 'vue-router';
import { HARBOR_REGISTRY_TITLE } from './constants/index';
import List from './pages/list.vue';
import Details from './pages/details.vue';
+import HarborTags from './pages/harbor_tags.vue';
Vue.use(VueRouter);
@@ -22,10 +23,20 @@ export default function createRouter(base, breadCrumbState) {
},
{
name: 'details',
- path: '/:id',
+ path: '/:project/:image',
component: Details,
meta: {
nameGenerator: () => breadCrumbState.name,
+ hrefGenerator: () => breadCrumbState.href,
+ },
+ },
+ {
+ name: 'tags',
+ path: '/:project/:image/:digest',
+ component: HarborTags,
+ meta: {
+ nameGenerator: () => breadCrumbState.name,
+ hrefGenerator: () => breadCrumbState.href,
},
},
],
diff --git a/app/assets/javascripts/packages_and_registries/harbor_registry/utils.js b/app/assets/javascripts/packages_and_registries/harbor_registry/utils.js
new file mode 100644
index 00000000000..13df303cffe
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/harbor_registry/utils.js
@@ -0,0 +1,84 @@
+import { isFinite } from 'lodash';
+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';
+
+export const extractSortingDetail = (parsedSorting = '') => {
+ const [orderBy, sortOrder] = parsedSorting.split('_');
+ if (orderBy && sortOrder) {
+ return {
+ orderBy: SORT_FIELD_MAPPING[orderBy],
+ sort: sortOrder.toLowerCase(),
+ };
+ }
+
+ return {
+ orderBy: '',
+ sort: '',
+ };
+};
+
+export const parseFilter = (filters = [], defaultPrefix = '') => {
+ /* eslint-disable @gitlab/require-i18n-strings */
+ const prefixMap = {
+ [FILTERED_SEARCH_TERM]: `${defaultPrefix}=`,
+ [TOKEN_TYPE_TAG_NAME]: 'tags=',
+ };
+ /* eslint-enable @gitlab/require-i18n-strings */
+ const filterList = [];
+ filters.forEach((i) => {
+ if (i.value?.data) {
+ const filterVal = i.value?.data;
+ const prefix = prefixMap[i.type];
+ const filterString = `${prefix}${filterVal}`;
+
+ filterList.push(filterString);
+ }
+ });
+
+ return filterList.join(',');
+};
+
+export const getNameFromParams = (fullName) => {
+ const names = fullName.split('/');
+ return {
+ projectName: names[0] || '',
+ imageName: names[1] || '',
+ };
+};
+
+export const formatPagination = (headers) => {
+ const pagination = parseIntPagination(normalizeHeaders(headers)) || {};
+
+ if (pagination.nextPage || pagination.previousPage) {
+ pagination.hasNextPage = isFinite(pagination.nextPage);
+ pagination.hasPreviousPage = isFinite(pagination.previousPage);
+ }
+
+ return pagination;
+};
+
+/* eslint-disable @gitlab/require-i18n-strings */
+export const dockerBuildCommand = ({ repositoryUrl, harborProjectName, projectName = '' }) => {
+ return `docker build -t ${repositoryUrl}/${harborProjectName}/${projectName} .`;
+};
+
+export const dockerPushCommand = ({ repositoryUrl, harborProjectName, projectName = '' }) => {
+ return `docker push ${repositoryUrl}/${harborProjectName}/${projectName}`;
+};
+
+export const dockerLoginCommand = (repositoryUrl) => {
+ return `docker login ${repositoryUrl}`;
+};
+
+export const artifactPullCommand = ({ repositoryUrl, harborProjectName, imageName, digest }) => {
+ return `docker pull ${repositoryUrl}/${harborProjectName}/${imageName}@${digest}`;
+};
+
+export const tagPullCommand = ({ repositoryUrl, harborProjectName, imageName, tag }) => {
+ return `docker pull ${repositoryUrl}/${harborProjectName}/${imageName}:${tag}`;
+};
+/* eslint-enable @gitlab/require-i18n-strings */
diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/details/components/app.vue b/app/assets/javascripts/packages_and_registries/infrastructure_registry/details/components/app.vue
index 425fb4596fd..fd099ee4e69 100644
--- a/app/assets/javascripts/packages_and_registries/infrastructure_registry/details/components/app.vue
+++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/details/components/app.vue
@@ -114,7 +114,7 @@ export default {
deleteModalContent: s__(
`PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?`,
),
- deleteFileModalTitle: s__(`PackageRegistry|Delete Package File`),
+ deleteFileModalTitle: s__(`PackageRegistry|Delete package asset`),
deleteFileModalContent: s__(
`PackageRegistry|You are about to delete %{filename}. This is a destructive action that may render your package unusable. Are you sure?`,
),
diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/details/components/package_files.vue b/app/assets/javascripts/packages_and_registries/infrastructure_registry/details/components/package_files.vue
index 28bfb82093c..e45b88bc6d5 100644
--- a/app/assets/javascripts/packages_and_registries/infrastructure_registry/details/components/package_files.vue
+++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/details/components/package_files.vue
@@ -84,14 +84,14 @@ export default {
},
},
i18n: {
- deleteFile: __('Delete file'),
+ deleteFile: __('Delete asset'),
},
};
</script>
<template>
<div>
- <h3 class="gl-font-lg gl-mt-5">{{ __('Files') }}</h3>
+ <h3 class="gl-font-lg gl-mt-5">{{ __('Assets') }}</h3>
<gl-table
:fields="filesTableHeaderFields"
:items="filesTableRows"
diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list.vue b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list.vue
index a465fea0b74..dab4a051d0c 100644
--- a/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list.vue
+++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/list/components/packages_list.vue
@@ -98,7 +98,7 @@ export default {
</div>
<template v-else>
- <div data-qa-selector="packages-table">
+ <div data-testid="packages-table">
<packages-list-row
v-for="packageEntity in list"
:key="packageEntity.id"
diff --git a/app/assets/javascripts/packages_and_registries/infrastructure_registry/shared/package_list_row.vue b/app/assets/javascripts/packages_and_registries/infrastructure_registry/shared/package_list_row.vue
index 3c6b8344c34..cc52235eaf3 100644
--- a/app/assets/javascripts/packages_and_registries/infrastructure_registry/shared/package_list_row.vue
+++ b/app/assets/javascripts/packages_and_registries/infrastructure_registry/shared/package_list_row.vue
@@ -78,7 +78,7 @@ export default {
</script>
<template>
- <list-item data-qa-selector="package_row" :disabled="disabledRow">
+ <list-item data-testid="package-row" :disabled="disabledRow">
<template #left-primary>
<div class="gl-display-flex gl-align-items-center gl-mr-3 gl-min-w-0">
<gl-link
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue
index 122d444e859..f581469b12b 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/installation_commands.vue
@@ -14,16 +14,17 @@ import NpmInstallation from './npm_installation.vue';
import NugetInstallation from './nuget_installation.vue';
import PypiInstallation from './pypi_installation.vue';
+const components = {
+ [PACKAGE_TYPE_CONAN]: ConanInstallation,
+ [PACKAGE_TYPE_MAVEN]: MavenInstallation,
+ [PACKAGE_TYPE_NPM]: NpmInstallation,
+ [PACKAGE_TYPE_NUGET]: NugetInstallation,
+ [PACKAGE_TYPE_PYPI]: PypiInstallation,
+ [PACKAGE_TYPE_COMPOSER]: ComposerInstallation,
+};
+
export default {
name: 'InstallationCommands',
- components: {
- [PACKAGE_TYPE_CONAN]: ConanInstallation,
- [PACKAGE_TYPE_MAVEN]: MavenInstallation,
- [PACKAGE_TYPE_NPM]: NpmInstallation,
- [PACKAGE_TYPE_NUGET]: NugetInstallation,
- [PACKAGE_TYPE_PYPI]: PypiInstallation,
- [PACKAGE_TYPE_COMPOSER]: ComposerInstallation,
- },
props: {
packageEntity: {
type: Object,
@@ -32,7 +33,7 @@ export default {
},
computed: {
installationComponent() {
- return this.$options.components[this.packageEntity.packageType];
+ return components[this.packageEntity.packageType];
},
},
};
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue
index b872294d2cf..8eb8654cddd 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/details/package_files.vue
@@ -139,7 +139,7 @@ export default {
},
},
i18n: {
- deleteFile: __('Delete file'),
+ deleteFile: __('Delete asset'),
deleteSelected: s__('PackageRegistry|Delete selected'),
moreActionsText: __('More actions'),
},
@@ -149,7 +149,7 @@ export default {
<template>
<div class="gl-pt-6">
<div class="gl-display-flex gl-align-items-center gl-justify-content-space-between">
- <h3 class="gl-font-lg gl-mt-5">{{ __('Files') }}</h3>
+ <h3 class="gl-font-lg gl-mt-5">{{ __('Assets') }}</h3>
<gl-button
v-if="canDelete"
:disabled="isLoading || !areFilesSelected"
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 04faff1a75b..7a000aca0f2 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
@@ -90,7 +90,7 @@ export default {
</script>
<template>
- <list-item data-qa-selector="package_row">
+ <list-item data-testid="package-row">
<template #left-primary>
<div class="gl-display-flex gl-align-items-center gl-mr-3 gl-min-w-0">
<router-link
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue
index a6ac2eb1b2b..e84f181e9b2 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue
@@ -151,7 +151,7 @@ export default {
@primaryAction="showConfirmationModal"
>{{ $options.i18n.errorMessageBodyAlert }}</gl-alert
>
- <div data-qa-selector="packages-table">
+ <div data-testid="packages-table">
<packages-list-row
v-for="packageEntity in list"
:key="packageEntity.id"
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/constants.js b/app/assets/javascripts/packages_and_registries/package_registry/constants.js
index 5b2a347a4ee..06a04ee248a 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/constants.js
+++ b/app/assets/javascripts/packages_and_registries/package_registry/constants.js
@@ -79,10 +79,10 @@ export const TRACKING_LABEL_PACKAGE_HISTORY = 'package_history';
export const SHOW_DELETE_SUCCESS_ALERT = 'showSuccessDeleteAlert';
export const DELETE_PACKAGE_FILE_ERROR_MESSAGE = s__(
- 'PackageRegistry|Something went wrong while deleting the package file.',
+ 'PackageRegistry|Something went wrong while deleting the package asset.',
);
export const DELETE_PACKAGE_FILE_SUCCESS_MESSAGE = s__(
- 'PackageRegistry|Package file deleted successfully',
+ 'PackageRegistry|Package asset deleted successfully',
);
export const DELETE_PACKAGE_FILES_ERROR_MESSAGE = s__(
'PackageRegistry|Something went wrong while deleting the package assets.',
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 e83962bb608..c10fc914d56 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
@@ -256,7 +256,7 @@ export default {
deleteModalContent: s__(
`PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?`,
),
- deleteFileModalTitle: s__(`PackageRegistry|Delete Package File`),
+ deleteFileModalTitle: s__(`PackageRegistry|Delete package asset`),
deleteFileModalContent: s__(
`PackageRegistry|You are about to delete %{filename}. This is a destructive action that may render your package unusable. Are you sure?`,
),
diff --git a/app/assets/javascripts/packages_and_registries/settings/group/components/duplicates_settings.vue b/app/assets/javascripts/packages_and_registries/settings/group/components/duplicates_settings.vue
deleted file mode 100644
index 51a97aead49..00000000000
--- a/app/assets/javascripts/packages_and_registries/settings/group/components/duplicates_settings.vue
+++ /dev/null
@@ -1,103 +0,0 @@
-<script>
-import { GlToggle, GlFormGroup, GlFormInput } from '@gitlab/ui';
-import { isEqual } from 'lodash';
-
-import {
- DUPLICATES_TOGGLE_LABEL,
- DUPLICATES_SETTING_EXCEPTION_TITLE,
- DUPLICATES_SETTINGS_EXCEPTION_LEGEND,
-} from '~/packages_and_registries/settings/group/constants';
-
-export default {
- name: 'DuplicatesSettings',
- i18n: {
- DUPLICATES_TOGGLE_LABEL,
- DUPLICATES_SETTING_EXCEPTION_TITLE,
- DUPLICATES_SETTINGS_EXCEPTION_LEGEND,
- },
- components: {
- GlToggle,
- GlFormGroup,
- GlFormInput,
- },
- props: {
- loading: {
- type: Boolean,
- required: false,
- default: false,
- },
- duplicatesAllowed: {
- type: Boolean,
- default: false,
- required: false,
- },
- duplicateExceptionRegex: {
- type: String,
- default: '',
- required: false,
- },
- duplicateExceptionRegexError: {
- type: String,
- default: '',
- required: false,
- },
- modelNames: {
- type: Object,
- required: true,
- validator(value) {
- return isEqual(Object.keys(value), ['allowed', 'exception']);
- },
- },
- toggleQaSelector: {
- type: String,
- required: false,
- default: null,
- },
- labelQaSelector: {
- type: String,
- required: false,
- default: null,
- },
- },
- computed: {
- isExceptionRegexValid() {
- return !this.duplicateExceptionRegexError;
- },
- },
- methods: {
- update(type, value) {
- this.$emit('update', { [type]: value });
- },
- },
-};
-</script>
-
-<template>
- <form>
- <gl-toggle
- :data-qa-selector="toggleQaSelector"
- :label="$options.i18n.DUPLICATES_TOGGLE_LABEL"
- :value="!duplicatesAllowed"
- :disabled="loading"
- @change="update(modelNames.allowed, !$event)"
- />
- <gl-form-group
- v-if="!duplicatesAllowed"
- class="gl-mt-4"
- :label="$options.i18n.DUPLICATES_SETTING_EXCEPTION_TITLE"
- label-size="sm"
- :state="isExceptionRegexValid"
- :invalid-feedback="duplicateExceptionRegexError"
- :description="$options.i18n.DUPLICATES_SETTINGS_EXCEPTION_LEGEND"
- label-for="maven-duplicated-settings-regex-input"
- >
- <gl-form-input
- id="maven-duplicated-settings-regex-input"
- :disabled="loading"
- size="lg"
- :value="duplicateExceptionRegex"
- @change="update(modelNames.exception, $event)"
- />
- </gl-form-group>
- </form>
-</template>
diff --git a/app/assets/javascripts/packages_and_registries/settings/group/components/exceptions_input.vue b/app/assets/javascripts/packages_and_registries/settings/group/components/exceptions_input.vue
new file mode 100644
index 00000000000..9ac1673dbf3
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/settings/group/components/exceptions_input.vue
@@ -0,0 +1,79 @@
+<script>
+import { GlFormGroup, GlFormInput } from '@gitlab/ui';
+
+import {
+ DUPLICATES_SETTING_EXCEPTION_TITLE,
+ DUPLICATES_SETTINGS_EXCEPTION_LEGEND,
+} from '~/packages_and_registries/settings/group/constants';
+
+export default {
+ name: 'ExceptionsInput',
+ i18n: {
+ DUPLICATES_SETTING_EXCEPTION_TITLE,
+ DUPLICATES_SETTINGS_EXCEPTION_LEGEND,
+ },
+ components: {
+ GlFormGroup,
+ GlFormInput,
+ },
+ props: {
+ loading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ duplicatesAllowed: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
+ duplicateExceptionRegex: {
+ type: String,
+ default: '',
+ required: false,
+ },
+ duplicateExceptionRegexError: {
+ type: String,
+ default: '',
+ required: false,
+ },
+ id: {
+ type: String,
+ required: true,
+ },
+ name: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ isExceptionRegexValid() {
+ return !this.duplicateExceptionRegexError;
+ },
+ },
+ methods: {
+ update(type, value) {
+ this.$emit('update', { [type]: value });
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-form-group
+ class="gl-mb-0"
+ :label="$options.i18n.DUPLICATES_SETTING_EXCEPTION_TITLE"
+ label-sr-only
+ :invalid-feedback="duplicateExceptionRegexError"
+ :label-for="id"
+ >
+ <gl-form-input
+ :id="id"
+ :disabled="duplicatesAllowed || loading"
+ size="lg"
+ :value="duplicateExceptionRegex"
+ :state="isExceptionRegexValid"
+ @change="update(name, $event)"
+ />
+ </gl-form-group>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/settings/group/components/generic_settings.vue b/app/assets/javascripts/packages_and_registries/settings/group/components/generic_settings.vue
deleted file mode 100644
index e5f63fe8d0d..00000000000
--- a/app/assets/javascripts/packages_and_registries/settings/group/components/generic_settings.vue
+++ /dev/null
@@ -1,26 +0,0 @@
-<script>
-import { s__ } from '~/locale';
-import SettingsTitles from '~/packages_and_registries/settings/group/components/settings_titles.vue';
-
-export default {
- name: 'GenericSettings',
- components: {
- SettingsTitles,
- },
- i18n: {
- title: s__('PackageRegistry|Generic'),
- subTitle: s__('PackageRegistry|Settings for Generic packages'),
- },
- modelNames: {
- allowed: 'genericDuplicatesAllowed',
- exception: 'genericDuplicateExceptionRegex',
- },
-};
-</script>
-
-<template>
- <div>
- <settings-titles :title="$options.i18n.title" :sub-title="$options.i18n.subTitle" />
- <slot :model-names="$options.modelNames"></slot>
- </div>
-</template>
diff --git a/app/assets/javascripts/packages_and_registries/settings/group/components/maven_settings.vue b/app/assets/javascripts/packages_and_registries/settings/group/components/maven_settings.vue
deleted file mode 100644
index a1cbd695f34..00000000000
--- a/app/assets/javascripts/packages_and_registries/settings/group/components/maven_settings.vue
+++ /dev/null
@@ -1,26 +0,0 @@
-<script>
-import { s__ } from '~/locale';
-import SettingsTitles from '~/packages_and_registries/settings/group/components/settings_titles.vue';
-
-export default {
- name: 'MavenSettings',
- components: {
- SettingsTitles,
- },
- i18n: {
- title: s__('PackageRegistry|Maven'),
- subTitle: s__('PackageRegistry|Settings for Maven packages'),
- },
- modelNames: {
- allowed: 'mavenDuplicatesAllowed',
- exception: 'mavenDuplicateExceptionRegex',
- },
-};
-</script>
-
-<template>
- <div>
- <settings-titles :title="$options.i18n.title" :sub-title="$options.i18n.subTitle" />
- <slot :model-names="$options.modelNames"></slot>
- </div>
-</template>
diff --git a/app/assets/javascripts/packages_and_registries/settings/group/components/packages_settings.vue b/app/assets/javascripts/packages_and_registries/settings/group/components/packages_settings.vue
index abb9f02d290..de087a8fcc5 100644
--- a/app/assets/javascripts/packages_and_registries/settings/group/components/packages_settings.vue
+++ b/app/assets/javascripts/packages_and_registries/settings/group/components/packages_settings.vue
@@ -1,27 +1,50 @@
<script>
-import DuplicatesSettings from '~/packages_and_registries/settings/group/components/duplicates_settings.vue';
-import GenericSettings from '~/packages_and_registries/settings/group/components/generic_settings.vue';
-import MavenSettings from '~/packages_and_registries/settings/group/components/maven_settings.vue';
+import { GlTableLite, GlToggle } from '@gitlab/ui';
import {
+ GENERIC_PACKAGE_FORMAT,
+ MAVEN_PACKAGE_FORMAT,
+ PACKAGE_FORMATS_TABLE_HEADER,
PACKAGE_SETTINGS_HEADER,
PACKAGE_SETTINGS_DESCRIPTION,
+ DUPLICATES_SETTING_EXCEPTION_TITLE,
+ DUPLICATES_TOGGLE_LABEL,
} from '~/packages_and_registries/settings/group/constants';
import updateNamespacePackageSettings from '~/packages_and_registries/settings/group/graphql/mutations/update_group_packages_settings.mutation.graphql';
import { updateGroupPackageSettings } from '~/packages_and_registries/settings/group/graphql/utils/cache_update';
import { updateGroupPackagesSettingsOptimisticResponse } from '~/packages_and_registries/settings/group/graphql/utils/optimistic_responses';
import SettingsBlock from '~/packages_and_registries/shared/components/settings_block.vue';
+import ExceptionsInput from '~/packages_and_registries/settings/group/components/exceptions_input.vue';
export default {
name: 'PackageSettings',
i18n: {
PACKAGE_SETTINGS_HEADER,
PACKAGE_SETTINGS_DESCRIPTION,
+ DUPLICATES_SETTING_EXCEPTION_TITLE,
+ DUPLICATES_TOGGLE_LABEL,
},
+ tableHeaderFields: [
+ {
+ key: 'packageFormat',
+ label: PACKAGE_FORMATS_TABLE_HEADER,
+ thClass: 'gl-bg-gray-10!',
+ },
+ {
+ key: 'allowDuplicates',
+ label: DUPLICATES_TOGGLE_LABEL,
+ thClass: 'gl-bg-gray-10!',
+ },
+ {
+ key: 'exceptions',
+ label: DUPLICATES_SETTING_EXCEPTION_TITLE,
+ thClass: 'gl-bg-gray-10!',
+ },
+ ],
components: {
SettingsBlock,
- MavenSettings,
- GenericSettings,
- DuplicatesSettings,
+ GlTableLite,
+ GlToggle,
+ ExceptionsInput,
},
inject: ['groupPath'],
props: {
@@ -40,6 +63,37 @@ export default {
errors: {},
};
},
+ computed: {
+ tableRows() {
+ return [
+ {
+ id: 'maven-duplicated-settings-regex-input',
+ format: MAVEN_PACKAGE_FORMAT,
+ duplicatesAllowed: this.packageSettings.mavenDuplicatesAllowed,
+ duplicateExceptionRegex: this.packageSettings.mavenDuplicateExceptionRegex,
+ duplicateExceptionRegexError: this.errors.mavenDuplicateExceptionRegex,
+ modelNames: {
+ allowed: 'mavenDuplicatesAllowed',
+ exception: 'mavenDuplicateExceptionRegex',
+ },
+ testid: 'maven-settings',
+ dataQaSelector: 'allow_duplicates_toggle',
+ },
+ {
+ id: 'generic-duplicated-settings-regex-input',
+ format: GENERIC_PACKAGE_FORMAT,
+ duplicatesAllowed: this.packageSettings.genericDuplicatesAllowed,
+ duplicateExceptionRegex: this.packageSettings.genericDuplicateExceptionRegex,
+ duplicateExceptionRegexError: this.errors.genericDuplicateExceptionRegex,
+ modelNames: {
+ allowed: 'genericDuplicatesAllowed',
+ exception: 'genericDuplicateExceptionRegex',
+ },
+ testid: 'generic-settings',
+ },
+ ];
+ },
+ },
methods: {
async updateSettings(payload) {
this.errors = {};
@@ -79,6 +133,9 @@ export default {
this.$emit('error');
}
},
+ update(type, value) {
+ this.updateSettings({ [type]: value });
+ },
},
};
</script>
@@ -92,32 +149,40 @@ export default {
</span>
</template>
<template #default>
- <maven-settings data-testid="maven-settings">
- <template #default="{ modelNames }">
- <duplicates-settings
- :duplicates-allowed="packageSettings.mavenDuplicatesAllowed"
- :duplicate-exception-regex="packageSettings.mavenDuplicateExceptionRegex"
- :duplicate-exception-regex-error="errors.mavenDuplicateExceptionRegex"
- :model-names="modelNames"
- :loading="isLoading"
- toggle-qa-selector="reject_duplicates_toggle"
- label-qa-selector="reject_duplicates_label"
- @update="updateSettings"
- />
- </template>
- </maven-settings>
- <generic-settings class="gl-mt-6" data-testid="generic-settings">
- <template #default="{ modelNames }">
- <duplicates-settings
- :duplicates-allowed="packageSettings.genericDuplicatesAllowed"
- :duplicate-exception-regex="packageSettings.genericDuplicateExceptionRegex"
- :duplicate-exception-regex-error="errors.genericDuplicateExceptionRegex"
- :model-names="modelNames"
- :loading="isLoading"
- @update="updateSettings"
- />
- </template>
- </generic-settings>
+ <form>
+ <gl-table-lite
+ :fields="$options.tableHeaderFields"
+ :items="tableRows"
+ stacked="sm"
+ :tbody-tr-attr="(item) => ({ 'data-testid': item.testid })"
+ >
+ <template #cell(packageFormat)="{ item }">
+ <span class="gl-md-pt-3">{{ item.format }}</span>
+ </template>
+ <template #cell(allowDuplicates)="{ item }">
+ <gl-toggle
+ :data-qa-selector="item.dataQaSelector"
+ :label="$options.i18n.DUPLICATES_TOGGLE_LABEL"
+ :value="item.duplicatesAllowed"
+ :disabled="isLoading"
+ label-position="hidden"
+ class="gl-align-items-flex-end gl-sm-align-items-flex-start"
+ @change="update(item.modelNames.allowed, $event)"
+ />
+ </template>
+ <template #cell(exceptions)="{ item }">
+ <exceptions-input
+ :id="item.id"
+ :duplicates-allowed="item.duplicatesAllowed"
+ :duplicate-exception-regex="item.duplicateExceptionRegex"
+ :duplicate-exception-regex-error="item.duplicateExceptionRegexError"
+ :name="item.modelNames.exception"
+ :loading="isLoading"
+ @update="updateSettings"
+ />
+ </template>
+ </gl-table-lite>
+ </form>
</template>
</settings-block>
</template>
diff --git a/app/assets/javascripts/packages_and_registries/settings/group/components/settings_titles.vue b/app/assets/javascripts/packages_and_registries/settings/group/components/settings_titles.vue
deleted file mode 100644
index 1e93875c1e3..00000000000
--- a/app/assets/javascripts/packages_and_registries/settings/group/components/settings_titles.vue
+++ /dev/null
@@ -1,26 +0,0 @@
-<script>
-export default {
- name: 'SettingsTitle',
- props: {
- title: {
- type: String,
- required: true,
- },
- subTitle: {
- type: String,
- required: false,
- default: '',
- },
- },
-};
-</script>
-
-<template>
- <div>
- <h5 class="gl-border-b-solid gl-border-b-1 gl-border-gray-200 gl-pb-3">
- {{ title }}
- </h5>
- <p v-if="subTitle">{{ subTitle }}</p>
- <slot></slot>
- </div>
-</template>
diff --git a/app/assets/javascripts/packages_and_registries/settings/group/constants.js b/app/assets/javascripts/packages_and_registries/settings/group/constants.js
index 34764663892..2dd6d3f76f6 100644
--- a/app/assets/javascripts/packages_and_registries/settings/group/constants.js
+++ b/app/assets/javascripts/packages_and_registries/settings/group/constants.js
@@ -5,10 +5,11 @@ export const PACKAGE_SETTINGS_HEADER = s__('PackageRegistry|Duplicate packages')
export const PACKAGE_SETTINGS_DESCRIPTION = s__(
'PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing.',
);
+export const PACKAGE_FORMATS_TABLE_HEADER = s__('PackageRegistry|Package formats');
+export const MAVEN_PACKAGE_FORMAT = s__('PackageRegistry|Maven');
+export const GENERIC_PACKAGE_FORMAT = s__('PackageRegistry|Generic');
-export const DUPLICATES_TOGGLE_LABEL = s__(
- 'PackageRegistry|Reject packages with the same name and version',
-);
+export const DUPLICATES_TOGGLE_LABEL = s__('PackageRegistry|Allow duplicates');
export const DUPLICATES_SETTING_EXCEPTION_TITLE = __('Exceptions');
export const DUPLICATES_SETTINGS_EXCEPTION_LEGEND = s__(
'PackageRegistry|Publish packages if their name or version matches this regex.',
diff --git a/app/assets/javascripts/packages_and_registries/settings/project/components/cleanup_image_tags.vue b/app/assets/javascripts/packages_and_registries/settings/project/components/cleanup_image_tags.vue
new file mode 100644
index 00000000000..72e68aca070
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/settings/project/components/cleanup_image_tags.vue
@@ -0,0 +1,112 @@
+<script>
+import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
+import { isEqual, get, isEmpty } from 'lodash';
+import {
+ CONTAINER_CLEANUP_POLICY_TITLE,
+ CONTAINER_CLEANUP_POLICY_DESCRIPTION,
+ FETCH_SETTINGS_ERROR_MESSAGE,
+ UNAVAILABLE_FEATURE_TITLE,
+ UNAVAILABLE_FEATURE_INTRO_TEXT,
+ UNAVAILABLE_USER_FEATURE_TEXT,
+ UNAVAILABLE_ADMIN_FEATURE_TEXT,
+} from '~/packages_and_registries/settings/project/constants';
+import expirationPolicyQuery from '~/packages_and_registries/settings/project/graphql/queries/get_expiration_policy.query.graphql';
+
+import ContainerExpirationPolicyForm from './container_expiration_policy_form.vue';
+
+export default {
+ components: {
+ GlAlert,
+ GlSprintf,
+ GlLink,
+ ContainerExpirationPolicyForm,
+ },
+ inject: ['projectPath', 'isAdmin', 'adminSettingsPath', 'enableHistoricEntries', 'helpPagePath'],
+ i18n: {
+ CONTAINER_CLEANUP_POLICY_TITLE,
+ CONTAINER_CLEANUP_POLICY_DESCRIPTION,
+ UNAVAILABLE_FEATURE_TITLE,
+ UNAVAILABLE_FEATURE_INTRO_TEXT,
+ FETCH_SETTINGS_ERROR_MESSAGE,
+ },
+ apollo: {
+ containerExpirationPolicy: {
+ query: expirationPolicyQuery,
+ variables() {
+ return {
+ projectPath: this.projectPath,
+ };
+ },
+ update: (data) => data.project?.containerExpirationPolicy,
+ result({ data }) {
+ this.workingCopy = { ...get(data, 'project.containerExpirationPolicy', {}) };
+ },
+ error(e) {
+ this.fetchSettingsError = e;
+ },
+ },
+ },
+ data() {
+ return {
+ fetchSettingsError: false,
+ containerExpirationPolicy: null,
+ workingCopy: {},
+ };
+ },
+ computed: {
+ isEnabled() {
+ return this.containerExpirationPolicy || this.enableHistoricEntries;
+ },
+ showDisabledFormMessage() {
+ return !this.isEnabled && !this.fetchSettingsError;
+ },
+ unavailableFeatureMessage() {
+ return this.isAdmin ? UNAVAILABLE_ADMIN_FEATURE_TEXT : UNAVAILABLE_USER_FEATURE_TEXT;
+ },
+ isEdited() {
+ if (isEmpty(this.containerExpirationPolicy) && isEmpty(this.workingCopy)) {
+ return false;
+ }
+ return !isEqual(this.containerExpirationPolicy, this.workingCopy);
+ },
+ },
+};
+</script>
+
+<template>
+ <div data-testid="container-expiration-policy-project-settings">
+ <h4 data-testid="title">{{ $options.i18n.CONTAINER_CLEANUP_POLICY_TITLE }}</h4>
+ <p data-testid="description">
+ <gl-sprintf :message="$options.i18n.CONTAINER_CLEANUP_POLICY_DESCRIPTION">
+ <template #link="{ content }">
+ <gl-link :href="helpPagePath">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </p>
+ <container-expiration-policy-form
+ v-if="isEnabled"
+ v-model="workingCopy"
+ :is-loading="$apollo.queries.containerExpirationPolicy.loading"
+ :is-edited="isEdited"
+ />
+ <template v-else>
+ <gl-alert
+ v-if="showDisabledFormMessage"
+ :dismissible="false"
+ :title="$options.i18n.UNAVAILABLE_FEATURE_TITLE"
+ variant="tip"
+ >
+ {{ $options.i18n.UNAVAILABLE_FEATURE_INTRO_TEXT }}
+
+ <gl-sprintf :message="unavailableFeatureMessage">
+ <template #link="{ content }">
+ <gl-link :href="adminSettingsPath">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </gl-alert>
+ <gl-alert v-else-if="fetchSettingsError" variant="warning" :dismissible="false">
+ <gl-sprintf :message="$options.i18n.FETCH_SETTINGS_ERROR_MESSAGE" />
+ </gl-alert>
+ </template>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/settings/project/components/container_expiration_policy.vue b/app/assets/javascripts/packages_and_registries/settings/project/components/container_expiration_policy.vue
index 1c44d2bc38b..b003b6aeb6b 100644
--- a/app/assets/javascripts/packages_and_registries/settings/project/components/container_expiration_policy.vue
+++ b/app/assets/javascripts/packages_and_registries/settings/project/components/container_expiration_policy.vue
@@ -1,9 +1,11 @@
<script>
-import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
-import { isEqual, get, isEmpty } from 'lodash';
+import { GlAlert, GlSprintf, GlLink, GlCard, GlButton } from '@gitlab/ui';
import {
CONTAINER_CLEANUP_POLICY_TITLE,
CONTAINER_CLEANUP_POLICY_DESCRIPTION,
+ CONTAINER_CLEANUP_POLICY_EDIT_RULES,
+ CONTAINER_CLEANUP_POLICY_RULES_DESCRIPTION,
+ CONTAINER_CLEANUP_POLICY_SET_RULES,
FETCH_SETTINGS_ERROR_MESSAGE,
UNAVAILABLE_FEATURE_TITLE,
UNAVAILABLE_FEATURE_INTRO_TEXT,
@@ -13,20 +15,29 @@ import {
import expirationPolicyQuery from '~/packages_and_registries/settings/project/graphql/queries/get_expiration_policy.query.graphql';
import SettingsBlock from '~/packages_and_registries/shared/components/settings_block.vue';
-import ContainerExpirationPolicyForm from './container_expiration_policy_form.vue';
-
export default {
components: {
SettingsBlock,
GlAlert,
GlSprintf,
GlLink,
- ContainerExpirationPolicyForm,
+ GlCard,
+ GlButton,
},
- inject: ['projectPath', 'isAdmin', 'adminSettingsPath', 'enableHistoricEntries', 'helpPagePath'],
+ inject: [
+ 'projectPath',
+ 'isAdmin',
+ 'adminSettingsPath',
+ 'enableHistoricEntries',
+ 'helpPagePath',
+ 'cleanupSettingsPath',
+ ],
i18n: {
CONTAINER_CLEANUP_POLICY_TITLE,
CONTAINER_CLEANUP_POLICY_DESCRIPTION,
+ CONTAINER_CLEANUP_POLICY_EDIT_RULES,
+ CONTAINER_CLEANUP_POLICY_RULES_DESCRIPTION,
+ CONTAINER_CLEANUP_POLICY_SET_RULES,
UNAVAILABLE_FEATURE_TITLE,
UNAVAILABLE_FEATURE_INTRO_TEXT,
FETCH_SETTINGS_ERROR_MESSAGE,
@@ -40,9 +51,6 @@ export default {
};
},
update: (data) => data.project?.containerExpirationPolicy,
- result({ data }) {
- this.workingCopy = { ...get(data, 'project.containerExpirationPolicy', {}) };
- },
error(e) {
this.fetchSettingsError = e;
},
@@ -52,29 +60,25 @@ export default {
return {
fetchSettingsError: false,
containerExpirationPolicy: null,
- workingCopy: {},
};
},
computed: {
- isDisabled() {
- return !(this.containerExpirationPolicy || this.enableHistoricEntries);
+ isCleanupEnabled() {
+ return this.containerExpirationPolicy?.enabled ?? false;
+ },
+ isEnabled() {
+ return this.containerExpirationPolicy || this.enableHistoricEntries;
},
showDisabledFormMessage() {
- return this.isDisabled && !this.fetchSettingsError;
+ return !this.isEnabled && !this.fetchSettingsError;
},
unavailableFeatureMessage() {
return this.isAdmin ? UNAVAILABLE_ADMIN_FEATURE_TEXT : UNAVAILABLE_USER_FEATURE_TEXT;
},
- isEdited() {
- if (isEmpty(this.containerExpirationPolicy) && isEmpty(this.workingCopy)) {
- return false;
- }
- return !isEqual(this.containerExpirationPolicy, this.workingCopy);
- },
- },
- methods: {
- restoreOriginal() {
- this.workingCopy = { ...this.containerExpirationPolicy };
+ cleanupRulesButtonText() {
+ return this.isCleanupEnabled
+ ? this.$options.i18n.CONTAINER_CLEANUP_POLICY_EDIT_RULES
+ : this.$options.i18n.CONTAINER_CLEANUP_POLICY_SET_RULES;
},
},
};
@@ -93,13 +97,19 @@ export default {
</span>
</template>
<template #default>
- <container-expiration-policy-form
- v-if="!isDisabled"
- v-model="workingCopy"
- :is-loading="$apollo.queries.containerExpirationPolicy.loading"
- :is-edited="isEdited"
- @reset="restoreOriginal"
- />
+ <gl-card v-if="isEnabled">
+ <p data-testid="description">
+ {{ $options.i18n.CONTAINER_CLEANUP_POLICY_RULES_DESCRIPTION }}
+ </p>
+ <gl-button
+ data-testid="rules-button"
+ :href="cleanupSettingsPath"
+ category="secondary"
+ variant="confirm"
+ >
+ {{ cleanupRulesButtonText }}
+ </gl-button>
+ </gl-card>
<template v-else>
<gl-alert
v-if="showDisabledFormMessage"
diff --git a/app/assets/javascripts/packages_and_registries/settings/project/components/container_expiration_policy_form.vue b/app/assets/javascripts/packages_and_registries/settings/project/components/container_expiration_policy_form.vue
index ae2d5f4fbc5..11d8732426d 100644
--- a/app/assets/javascripts/packages_and_registries/settings/project/components/container_expiration_policy_form.vue
+++ b/app/assets/javascripts/packages_and_registries/settings/project/components/container_expiration_policy_form.vue
@@ -1,8 +1,9 @@
<script>
import { GlCard, GlButton, GlSprintf } from '@gitlab/ui';
+import { objectToQuery, visitUrl } from '~/lib/utils/url_utility';
import {
UPDATE_SETTINGS_ERROR_MESSAGE,
- UPDATE_SETTINGS_SUCCESS_MESSAGE,
+ SHOW_SETUP_SUCCESS_ALERT,
SET_CLEANUP_POLICY_BUTTON,
KEEP_HEADER_TEXT,
KEEP_INFO_TEXT,
@@ -37,7 +38,7 @@ export default {
ExpirationRunText,
},
mixins: [Tracking.mixin()],
- inject: ['projectPath'],
+ inject: ['projectPath', 'projectSettingsPath'],
props: {
value: {
type: Object,
@@ -95,10 +96,10 @@ export default {
return Object.values(this.localErrors).every((error) => error);
},
isSubmitButtonDisabled() {
- return !this.fieldsAreValid || this.showLoadingIcon;
+ return !this.isEdited || !this.fieldsAreValid || this.showLoadingIcon;
},
isCancelButtonDisabled() {
- return !this.isEdited || this.isLoading || this.mutationLoading;
+ return this.isLoading || this.mutationLoading;
},
isFieldDisabled() {
return this.showLoadingIcon || !this.value.enabled;
@@ -119,12 +120,6 @@ export default {
findDefaultOption(option) {
return this.value[option] || this.$options.formOptions[option].find((f) => f.default)?.key;
},
- reset() {
- this.track('reset_form');
- this.apiErrors = {};
- this.localErrors = {};
- this.$emit('reset');
- },
setApiErrors(response) {
this.apiErrors = response.graphQLErrors.reduce((acc, curr) => {
curr.extensions.problems.forEach((item) => {
@@ -168,7 +163,7 @@ export default {
const customError = this.encapsulateError('nameRegex', errorMessage);
throw customError;
} else {
- this.$toast.show(UPDATE_SETTINGS_SUCCESS_MESSAGE);
+ this.navigateToSettingsWithSuccessAlert();
}
})
.catch((error) => {
@@ -183,12 +178,17 @@ export default {
this.$emit('input', { ...this.value, [model]: newValue });
this.apiErrors[model] = undefined;
},
+ navigateToSettingsWithSuccessAlert() {
+ const alertQuery = objectToQuery({ [SHOW_SETUP_SUCCESS_ALERT]: true });
+
+ visitUrl(`${this.projectSettingsPath}?${alertQuery}`);
+ },
},
};
</script>
<template>
- <form ref="form-element" @submit.prevent="submit" @reset.prevent="reset">
+ <form @submit.prevent="submit">
<expiration-toggle
:value="prefilledForm.enabled"
:disabled="showLoadingIcon"
@@ -199,7 +199,7 @@ export default {
<div class="gl-display-flex gl-mt-7">
<expiration-dropdown
- v-model="prefilledForm.cadence"
+ :value="prefilledForm.cadence"
:disabled="isFieldDisabled"
:form-options="$options.formOptions.cadence"
:label="$options.i18n.CADENCE_LABEL"
@@ -231,7 +231,7 @@ export default {
</gl-sprintf>
</p>
<expiration-dropdown
- v-model="prefilledForm.keepN"
+ :value="prefilledForm.keepN"
:disabled="isFieldDisabled"
:form-options="$options.formOptions.keepN"
:label="$options.i18n.KEEP_N_LABEL"
@@ -270,7 +270,7 @@ export default {
</gl-sprintf>
</p>
<expiration-dropdown
- v-model="prefilledForm.olderThan"
+ :value="prefilledForm.olderThan"
:disabled="isFieldDisabled"
:form-options="$options.formOptions.olderThan"
:label="$options.i18n.EXPIRATION_SCHEDULE_LABEL"
@@ -306,7 +306,7 @@ export default {
</gl-button>
<gl-button
data-testid="cancel-button"
- type="reset"
+ :href="projectSettingsPath"
:disabled="isCancelButtonDisabled"
class="gl-mr-4"
>
diff --git a/app/assets/javascripts/packages_and_registries/settings/project/components/registry_settings_app.vue b/app/assets/javascripts/packages_and_registries/settings/project/components/registry_settings_app.vue
index 710cfe7b1eb..2c1368262f2 100644
--- a/app/assets/javascripts/packages_and_registries/settings/project/components/registry_settings_app.vue
+++ b/app/assets/javascripts/packages_and_registries/settings/project/components/registry_settings_app.vue
@@ -1,18 +1,57 @@
<script>
+import { GlAlert } from '@gitlab/ui';
+import { historyReplaceState } from '~/lib/utils/common_utils';
+import { getParameterByName } from '~/lib/utils/url_utility';
+import {
+ SHOW_SETUP_SUCCESS_ALERT,
+ UPDATE_SETTINGS_SUCCESS_MESSAGE,
+} from '~/packages_and_registries/settings/project/constants';
import ContainerExpirationPolicy from './container_expiration_policy.vue';
import PackagesCleanupPolicy from './packages_cleanup_policy.vue';
export default {
components: {
ContainerExpirationPolicy,
+ GlAlert,
PackagesCleanupPolicy,
},
inject: ['showContainerRegistrySettings', 'showPackageRegistrySettings'],
+ i18n: {
+ UPDATE_SETTINGS_SUCCESS_MESSAGE,
+ },
+ data() {
+ return {
+ showAlert: false,
+ };
+ },
+ mounted() {
+ this.checkAlert();
+ },
+ methods: {
+ checkAlert() {
+ const showAlert = getParameterByName(SHOW_SETUP_SUCCESS_ALERT);
+
+ if (showAlert) {
+ this.showAlert = true;
+ const cleanUrl = window.location.href.split('?')[0];
+ historyReplaceState(cleanUrl);
+ }
+ },
+ },
};
</script>
<template>
<div>
+ <gl-alert
+ v-if="showAlert"
+ variant="success"
+ class="gl-mt-5"
+ dismissible
+ @dismiss="showAlert = false"
+ >
+ {{ $options.i18n.UPDATE_SETTINGS_SUCCESS_MESSAGE }}
+ </gl-alert>
<packages-cleanup-policy v-if="showPackageRegistrySettings" />
<container-expiration-policy v-if="showContainerRegistrySettings" />
</div>
diff --git a/app/assets/javascripts/packages_and_registries/settings/project/constants.js b/app/assets/javascripts/packages_and_registries/settings/project/constants.js
index fcb4a8ee297..a9b47cbd343 100644
--- a/app/assets/javascripts/packages_and_registries/settings/project/constants.js
+++ b/app/assets/javascripts/packages_and_registries/settings/project/constants.js
@@ -4,6 +4,13 @@ export const CONTAINER_CLEANUP_POLICY_TITLE = s__(`ContainerRegistry|Clean up im
export const CONTAINER_CLEANUP_POLICY_DESCRIPTION = s__(
`ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}`,
);
+export const CONTAINER_CLEANUP_POLICY_RULES_DESCRIPTION = s__(
+ 'ContainerRegistry|Set rules to automatically remove unused packages to save storage space.',
+);
+export const CONTAINER_CLEANUP_POLICY_EDIT_RULES = s__('ContainerRegistry|Edit cleanup rules');
+export const CONTAINER_CLEANUP_POLICY_SET_RULES = s__('ContainerRegistry|Set cleanup rules');
+export const SHOW_SETUP_SUCCESS_ALERT = 'showSetupSuccessAlert';
+
export const SET_CLEANUP_POLICY_BUTTON = __('Save changes');
export const UNAVAILABLE_FEATURE_TITLE = s__(
`ContainerRegistry|Cleanup policy for tags is disabled`,
diff --git a/app/assets/javascripts/packages_and_registries/settings/project/registry_settings_bundle.js b/app/assets/javascripts/packages_and_registries/settings/project/registry_settings_bundle.js
index daf1da6eac8..57c8d07e620 100644
--- a/app/assets/javascripts/packages_and_registries/settings/project/registry_settings_bundle.js
+++ b/app/assets/javascripts/packages_and_registries/settings/project/registry_settings_bundle.js
@@ -18,6 +18,7 @@ export default () => {
enableHistoricEntries,
projectPath,
adminSettingsPath,
+ cleanupSettingsPath,
tagsRegexHelpPagePath,
helpPagePath,
showContainerRegistrySettings,
@@ -34,6 +35,7 @@ export default () => {
enableHistoricEntries: parseBoolean(enableHistoricEntries),
projectPath,
adminSettingsPath,
+ cleanupSettingsPath,
tagsRegexHelpPagePath,
helpPagePath,
showContainerRegistrySettings: parseBoolean(showContainerRegistrySettings),
diff --git a/app/assets/javascripts/packages_and_registries/settings/project/registry_settings_cleanup_tags_bundle.js b/app/assets/javascripts/packages_and_registries/settings/project/registry_settings_cleanup_tags_bundle.js
new file mode 100644
index 00000000000..b1401c448a1
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/settings/project/registry_settings_cleanup_tags_bundle.js
@@ -0,0 +1,41 @@
+import { GlToast } from '@gitlab/ui';
+import Vue from 'vue';
+import { parseBoolean } from '~/lib/utils/common_utils';
+import Translate from '~/vue_shared/translate';
+import CleanupImageTags from './components/cleanup_image_tags.vue';
+import { apolloProvider } from './graphql/index';
+
+Vue.use(GlToast);
+Vue.use(Translate);
+
+export default () => {
+ const el = document.getElementById('js-registry-settings-cleanup-image-tags');
+ if (!el) {
+ return null;
+ }
+ const {
+ isAdmin,
+ enableHistoricEntries,
+ projectPath,
+ adminSettingsPath,
+ projectSettingsPath,
+ tagsRegexHelpPagePath,
+ helpPagePath,
+ } = el.dataset;
+ return new Vue({
+ el,
+ apolloProvider,
+ provide: {
+ isAdmin: parseBoolean(isAdmin),
+ enableHistoricEntries: parseBoolean(enableHistoricEntries),
+ projectPath,
+ adminSettingsPath,
+ projectSettingsPath,
+ tagsRegexHelpPagePath,
+ helpPagePath,
+ },
+ render(createElement) {
+ return createElement(CleanupImageTags, {});
+ },
+ });
+};
diff --git a/app/assets/javascripts/packages_and_registries/shared/components/persisted_search.vue b/app/assets/javascripts/packages_and_registries/shared/components/persisted_search.vue
index b2b1d2c8212..363304c20ce 100644
--- a/app/assets/javascripts/packages_and_registries/shared/components/persisted_search.vue
+++ b/app/assets/javascripts/packages_and_registries/shared/components/persisted_search.vue
@@ -18,6 +18,11 @@ export default {
type: String,
required: true,
},
+ tokens: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
data() {
return {
@@ -68,7 +73,7 @@ export default {
v-if="mountRegistrySearch"
:filters="filters"
:sorting="sorting"
- :tokens="$options.tokens"
+ :tokens="tokens"
:sortable-fields="sortableFields"
@sorting:changed="updateSortingAndEmitUpdate"
@filter:changed="updateFilters"
diff --git a/app/assets/javascripts/packages_and_registries/shared/components/settings_block.vue b/app/assets/javascripts/packages_and_registries/shared/components/settings_block.vue
index 0458b914b58..7740924b058 100644
--- a/app/assets/javascripts/packages_and_registries/shared/components/settings_block.vue
+++ b/app/assets/javascripts/packages_and_registries/shared/components/settings_block.vue
@@ -1,7 +1,7 @@
<template>
<section class="settings gl-py-7">
- <div class="gl-lg-display-flex gl-gap-6">
- <div class="gl-lg-w-40p gl-pr-10 gl-flex-shrink-0">
+ <div class="row">
+ <div class="col-lg-4">
<h4>
<slot name="title"></slot>
</h4>
@@ -9,7 +9,7 @@
<slot name="description"></slot>
</p>
</div>
- <div class="gl-pt-3 gl-flex-grow-1">
+ <div class="col-lg-8 gl-pt-3">
<slot></slot>
</div>
</div>
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 6744e821565..7fd440d0b27 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
@@ -33,10 +33,10 @@ export const DELETE_PACKAGE_ERROR_MESSAGE = s__(
'PackageRegistry|Something went wrong while deleting the package.',
);
export const DELETE_PACKAGE_FILE_ERROR_MESSAGE = s__(
- 'PackageRegistry|Something went wrong while deleting the package file.',
+ 'PackageRegistry|Something went wrong while deleting the package asset.',
);
export const DELETE_PACKAGE_FILE_SUCCESS_MESSAGE = s__(
- 'PackageRegistry|Package file deleted successfully',
+ 'PackageRegistry|Package asset deleted successfully',
);
export const PACKAGE_ERROR_STATUS = 'error';
diff --git a/app/assets/javascripts/pages/admin/application_settings/ci_cd/index.js b/app/assets/javascripts/pages/admin/application_settings/ci_cd/index.js
new file mode 100644
index 00000000000..9b6fba9876e
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/application_settings/ci_cd/index.js
@@ -0,0 +1,3 @@
+import initRunnerTokenExpirationIntervals from '~/admin/application_settings/runner_token_expiration/index';
+
+initRunnerTokenExpirationIntervals();
diff --git a/app/assets/javascripts/pages/admin/application_settings/payload_previewer.js b/app/assets/javascripts/pages/admin/application_settings/payload_previewer.js
index 84027203783..616005565c4 100644
--- a/app/assets/javascripts/pages/admin/application_settings/payload_previewer.js
+++ b/app/assets/javascripts/pages/admin/application_settings/payload_previewer.js
@@ -63,6 +63,8 @@ export default class PayloadPreviewer {
insertPayload(data) {
this.isInserted = true;
+
+ // eslint-disable-next-line no-unsanitized/property
this.getContainer().innerHTML = data;
this.showPayload();
}
diff --git a/app/assets/javascripts/pages/admin/jobs/index/index.js b/app/assets/javascripts/pages/admin/jobs/index/index.js
index a4d89889d57..4cd312b403c 100644
--- a/app/assets/javascripts/pages/admin/jobs/index/index.js
+++ b/app/assets/javascripts/pages/admin/jobs/index/index.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import { BV_SHOW_MODAL } from '~/lib/utils/constants';
import Translate from '~/vue_shared/translate';
-import stopJobsModal from './components/stop_jobs_modal.vue';
+import StopJobsModal from './components/stop_jobs_modal.vue';
Vue.use(Translate);
@@ -14,7 +14,7 @@ function initJobs() {
new Vue({
el: `#js-${modalId}`,
components: {
- stopJobsModal,
+ StopJobsModal,
},
mounted() {
stopJobsButton.classList.remove('disabled');
diff --git a/app/assets/javascripts/pages/admin/runners/edit/index.js b/app/assets/javascripts/pages/admin/runners/edit/index.js
index ddf135a2732..03d31f49a99 100644
--- a/app/assets/javascripts/pages/admin/runners/edit/index.js
+++ b/app/assets/javascripts/pages/admin/runners/edit/index.js
@@ -1,3 +1,3 @@
-import { initAdminRunnerEdit } from '~/runner/admin_runner_edit';
+import { initRunnerEdit } from '~/runner/runner_edit';
-initAdminRunnerEdit();
+initRunnerEdit('#js-admin-runner-edit');
diff --git a/app/assets/javascripts/pages/admin/topics/edit/index.js b/app/assets/javascripts/pages/admin/topics/edit/index.js
index f5e6d044865..b2cbd52fb27 100644
--- a/app/assets/javascripts/pages/admin/topics/edit/index.js
+++ b/app/assets/javascripts/pages/admin/topics/edit/index.js
@@ -2,7 +2,7 @@ import $ from 'jquery';
import GLForm from '~/gl_form';
import initFilePickers from '~/file_pickers';
import ZenMode from '~/zen_mode';
-import initRemoveAvatar from '~/admin/topics';
+import { initRemoveAvatar } from '~/admin/topics';
new GLForm($('.js-project-topic-form')); // eslint-disable-line no-new
initFilePickers();
diff --git a/app/assets/javascripts/pages/admin/topics/index.js b/app/assets/javascripts/pages/admin/topics/index.js
new file mode 100644
index 00000000000..ec0e11660d2
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/topics/index.js
@@ -0,0 +1,3 @@
+import { initMergeTopics } from '~/admin/topics';
+
+initMergeTopics();
diff --git a/app/assets/javascripts/pages/dashboard/todos/index/todos.js b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
index e45a40bd44c..b6f42a27002 100644
--- a/app/assets/javascripts/pages/dashboard/todos/index/todos.js
+++ b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
@@ -140,10 +140,10 @@ export default class Todos {
restoreBtn.classList.add('hidden');
doneBtn.classList.remove('hidden');
} else if (target === doneBtn) {
- row.classList.add('done-reversible');
+ row.classList.add('done-reversible', 'gl-bg-gray-50', 'gl-border-gray-100');
restoreBtn.classList.remove('hidden');
} else if (target === restoreBtn) {
- row.classList.remove('done-reversible');
+ row.classList.remove('done-reversible', 'gl-bg-gray-50', 'gl-border-gray-100');
doneBtn.classList.remove('hidden');
} else {
row.parentNode.removeChild(row);
@@ -200,9 +200,11 @@ export default class Todos {
});
document.dispatchEvent(event);
+ // eslint-disable-next-line no-unsanitized/property
document.querySelector('.js-todos-pending .js-todos-badge').innerHTML = addDelimiter(
data.count,
);
+ // eslint-disable-next-line no-unsanitized/property
document.querySelector('.js-todos-done .js-todos-badge').innerHTML = addDelimiter(
data.done_count,
);
diff --git a/app/assets/javascripts/pages/groups/details/index.js b/app/assets/javascripts/pages/groups/details/index.js
index 0417134f2a7..92490368b15 100644
--- a/app/assets/javascripts/pages/groups/details/index.js
+++ b/app/assets/javascripts/pages/groups/details/index.js
@@ -1,3 +1,5 @@
+import { initGroupOverviewTabs } from '~/groups/init_overview_tabs';
import initGroupDetails from '../shared/group_details';
initGroupDetails('details');
+initGroupOverviewTabs();
diff --git a/app/assets/javascripts/pages/groups/runners/edit/index.js b/app/assets/javascripts/pages/groups/runners/edit/index.js
new file mode 100644
index 00000000000..febb0026b67
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/runners/edit/index.js
@@ -0,0 +1,3 @@
+import { initRunnerEdit } from '~/runner/runner_edit';
+
+initRunnerEdit('#js-group-runner-edit');
diff --git a/app/assets/javascripts/pages/groups/settings/repository/create_deploy_token/index.js b/app/assets/javascripts/pages/groups/settings/repository/create_deploy_token/index.js
new file mode 100644
index 00000000000..1943704ac3d
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/settings/repository/create_deploy_token/index.js
@@ -0,0 +1,6 @@
+// This "page" is only rendered as response to the create_deploy_token form.
+// It shows the secret token to the user one time, but is otherwise identical
+// with the Settings/Repository page.
+//
+// This is why we just import the other page's JavaScript here.
+import '../show/index';
diff --git a/app/assets/javascripts/pages/groups/show/index.js b/app/assets/javascripts/pages/groups/show/index.js
index e4a84dd5eec..161fca83a58 100644
--- a/app/assets/javascripts/pages/groups/show/index.js
+++ b/app/assets/javascripts/pages/groups/show/index.js
@@ -1,5 +1,7 @@
import leaveByUrl from '~/namespaces/leave_by_url';
+import { initGroupOverviewTabs } from '~/groups/init_overview_tabs';
import initGroupDetails from '../shared/group_details';
leaveByUrl('group');
initGroupDetails();
+initGroupOverviewTabs();
diff --git a/app/assets/javascripts/pages/profiles/keys/index.js b/app/assets/javascripts/pages/profiles/keys/index.js
index 6b12604c76b..28b1aa02dfa 100644
--- a/app/assets/javascripts/pages/profiles/keys/index.js
+++ b/app/assets/javascripts/pages/profiles/keys/index.js
@@ -1,5 +1,6 @@
import initConfirmModal from '~/confirm_modal';
import AddSshKeyValidation from '~/profile/add_ssh_key_validation';
+import { initExpiresAtField } from '~/access_tokens/index';
initConfirmModal();
@@ -23,3 +24,5 @@ function initSshKeyValidation() {
}
initSshKeyValidation();
+
+initExpiresAtField();
diff --git a/app/assets/javascripts/pages/profiles/show/emoji_menu.js b/app/assets/javascripts/pages/profiles/show/emoji_menu.js
deleted file mode 100644
index 286c1f1e929..00000000000
--- a/app/assets/javascripts/pages/profiles/show/emoji_menu.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import '~/commons/bootstrap';
-import { AwardsHandler } from '~/awards_handler';
-
-class EmojiMenu extends AwardsHandler {
- constructor(emoji, toggleButtonSelector, menuClass, selectEmojiCallback) {
- super(emoji);
-
- this.selectEmojiCallback = selectEmojiCallback;
- this.toggleButtonSelector = toggleButtonSelector;
- this.menuClass = menuClass;
- }
-
- postEmoji($emojiButton, awardUrl, selectedEmoji, callback) {
- this.selectEmojiCallback(selectedEmoji, this.emoji.glEmojiTag(selectedEmoji));
- callback();
- }
-}
-
-export default EmojiMenu;
diff --git a/app/assets/javascripts/pages/profiles/show/index.js b/app/assets/javascripts/pages/profiles/show/index.js
index 226ef4c4e23..96ea7329e6e 100644
--- a/app/assets/javascripts/pages/profiles/show/index.js
+++ b/app/assets/javascripts/pages/profiles/show/index.js
@@ -1,88 +1,18 @@
import emojiRegex from 'emoji-regex';
-import $ from 'jquery';
-import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
-import * as Emoji from '~/emoji';
-import createFlash from '~/flash';
import { __ } from '~/locale';
-import EmojiMenu from './emoji_menu';
+import { initSetStatusForm } from '~/profile/profile';
-const defaultStatusEmoji = 'speech_balloon';
-const toggleEmojiMenuButtonSelector = '.js-toggle-emoji-menu';
-const toggleEmojiMenuButton = document.querySelector(toggleEmojiMenuButtonSelector);
-const statusEmojiField = document.getElementById('js-status-emoji-field');
-const statusMessageField = document.getElementById('js-status-message-field');
-
-const toggleNoEmojiPlaceholder = (isVisible) => {
- const placeholderElement = document.getElementById('js-no-emoji-placeholder');
- placeholderElement.classList.toggle('hidden', !isVisible);
-};
-
-const findStatusEmoji = () => toggleEmojiMenuButton.querySelector('gl-emoji');
-const removeStatusEmoji = () => {
- const statusEmoji = findStatusEmoji();
- if (statusEmoji) {
- statusEmoji.remove();
- }
-};
-
-const selectEmojiCallback = (emoji, emojiTag) => {
- statusEmojiField.value = emoji;
- toggleNoEmojiPlaceholder(false);
- removeStatusEmoji();
- toggleEmojiMenuButton.innerHTML += emojiTag;
-};
-
-const clearEmojiButton = document.getElementById('js-clear-user-status-button');
-clearEmojiButton.addEventListener('click', () => {
- statusEmojiField.value = '';
- statusMessageField.value = '';
- removeStatusEmoji();
- toggleNoEmojiPlaceholder(true);
-});
-
-const emojiAutocomplete = new GfmAutoComplete();
-emojiAutocomplete.setup($(statusMessageField), { emojis: true });
+initSetStatusForm();
const userNameInput = document.getElementById('user_name');
-userNameInput.addEventListener('input', () => {
- const EMOJI_REGEX = emojiRegex();
- if (EMOJI_REGEX.test(userNameInput.value)) {
- // set field to invalid so it gets detected by GlFieldErrors
- userNameInput.setCustomValidity(__('Invalid field'));
- } else {
- userNameInput.setCustomValidity('');
- }
-});
-
-Emoji.initEmojiMap()
- .then(() => {
- const emojiMenu = new EmojiMenu(
- Emoji,
- toggleEmojiMenuButtonSelector,
- 'js-status-emoji-menu',
- selectEmojiCallback,
- );
- emojiMenu.bindEvents();
-
- const defaultEmojiTag = Emoji.glEmojiTag(defaultStatusEmoji);
- statusMessageField.addEventListener('input', () => {
- const hasStatusMessage = statusMessageField.value.trim() !== '';
- const statusEmoji = findStatusEmoji();
- if (hasStatusMessage && statusEmoji) {
- return;
- }
-
- if (hasStatusMessage) {
- toggleNoEmojiPlaceholder(false);
- toggleEmojiMenuButton.innerHTML += defaultEmojiTag;
- } else if (statusEmoji.dataset.name === defaultStatusEmoji) {
- toggleNoEmojiPlaceholder(true);
- removeStatusEmoji();
- }
- });
- })
- .catch(() =>
- createFlash({
- message: __('Failed to load emoji list.'),
- }),
- );
+if (userNameInput) {
+ userNameInput.addEventListener('input', () => {
+ const EMOJI_REGEX = emojiRegex();
+ if (EMOJI_REGEX.test(userNameInput.value)) {
+ // set field to invalid so it gets detected by GlFieldErrors
+ userNameInput.setCustomValidity(__('Invalid field'));
+ } else {
+ userNameInput.setCustomValidity('');
+ }
+ });
+}
diff --git a/app/assets/javascripts/pages/profiles/two_factor_auths/index.js b/app/assets/javascripts/pages/profiles/two_factor_auths/index.js
index 49fdf5bb6b5..96c4d0e0670 100644
--- a/app/assets/javascripts/pages/profiles/two_factor_auths/index.js
+++ b/app/assets/javascripts/pages/profiles/two_factor_auths/index.js
@@ -8,7 +8,10 @@ const skippable = twoFactorNode ? parseBoolean(twoFactorNode.dataset.twoFactorSk
if (skippable) {
const button = `<br/><a class="btn gl-button btn-sm btn-confirm gl-mt-3" data-qa-selector="configure_it_later_button" data-method="patch" href="${twoFactorNode.dataset.two_factor_skip_url}">Configure it later</a>`;
const flashAlert = document.querySelector('.flash-alert');
- if (flashAlert) flashAlert.insertAdjacentHTML('beforeend', button);
+ if (flashAlert) {
+ // eslint-disable-next-line no-unsanitized/method
+ flashAlert.insertAdjacentHTML('beforeend', button);
+ }
}
mount2faRegistration();
diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js
index 740fdb8a96a..e45f9a10294 100644
--- a/app/assets/javascripts/pages/projects/blob/show/index.js
+++ b/app/assets/javascripts/pages/projects/blob/show/index.js
@@ -9,7 +9,7 @@ import GpgBadges from '~/gpg_badges';
import createDefaultClient from '~/lib/graphql';
import initBlob from '~/pages/projects/init_blob';
import initWebIdeLink from '~/pages/projects/shared/web_ide_link';
-import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
+import CommitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
import BlobContentViewer from '~/repository/components/blob_content_viewer.vue';
import '~/sourcegraph/load';
import createStore from '~/code_navigation/store';
@@ -64,7 +64,7 @@ if (statusLink) {
new Vue({
el: CommitPipelineStatusEl,
components: {
- commitPipelineStatus,
+ CommitPipelineStatus,
},
render(createElement) {
return createElement('commit-pipeline-status', {
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 f92a40e057f..b415e36bf09 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
@@ -3,15 +3,12 @@ import {
GlIcon,
GlLink,
GlForm,
- GlFormInputGroup,
- GlInputGroupText,
GlFormInput,
GlFormGroup,
GlFormTextarea,
GlButton,
GlFormRadio,
GlFormRadioGroup,
- GlFormSelect,
} from '@gitlab/ui';
import { kebabCase } from 'lodash';
import { buildApiUrl } from '~/api/api_utils';
@@ -21,16 +18,13 @@ import csrf from '~/lib/utils/csrf';
import { redirectTo } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
import validation from '~/vue_shared/directives/validation';
-
-const PRIVATE_VISIBILITY = 'private';
-const INTERNAL_VISIBILITY = 'internal';
-const PUBLIC_VISIBILITY = 'public';
-
-const VISIBILITY_LEVEL = {
- [PRIVATE_VISIBILITY]: 0,
- [INTERNAL_VISIBILITY]: 10,
- [PUBLIC_VISIBILITY]: 20,
-};
+import {
+ VISIBILITY_LEVEL_PRIVATE_STRING,
+ VISIBILITY_LEVEL_INTERNAL_STRING,
+ VISIBILITY_LEVEL_PUBLIC_STRING,
+ VISIBILITY_LEVELS_STRING_TO_INTEGER,
+} from '~/visibility_level/constants';
+import ProjectNamespace from './project_namespace.vue';
const initFormField = ({ value, required = true, skipValidation = false }) => ({
value,
@@ -39,28 +33,18 @@ const initFormField = ({ value, required = true, skipValidation = false }) => ({
feedback: null,
});
-function sortNamespaces(namespaces) {
- if (!namespaces || !namespaces?.length) {
- return namespaces;
- }
-
- return namespaces.sort((a, b) => a.full_name.localeCompare(b.full_name));
-}
-
export default {
components: {
GlForm,
GlIcon,
GlLink,
GlButton,
- GlFormInputGroup,
- GlInputGroupText,
GlFormInput,
GlFormTextarea,
GlFormGroup,
GlFormRadio,
GlFormRadioGroup,
- GlFormSelect,
+ ProjectNamespace,
},
directives: {
validation: validation(),
@@ -72,9 +56,6 @@ export default {
visibilityHelpPath: {
default: '',
},
- endpoint: {
- default: '',
- },
projectFullPath: {
default: '',
},
@@ -96,6 +77,9 @@ export default {
restrictedVisibilityLevels: {
default: [],
},
+ namespaceId: {
+ default: '',
+ },
},
data() {
const form = {
@@ -117,20 +101,17 @@ export default {
};
return {
isSaving: false,
- namespaces: [],
form,
};
},
computed: {
- projectUrl() {
- return `${gon.gitlab_url}/`;
- },
projectVisibilityLevel() {
- return VISIBILITY_LEVEL[this.projectVisibility];
+ return VISIBILITY_LEVELS_STRING_TO_INTEGER[this.projectVisibility];
},
namespaceVisibilityLevel() {
- const visibility = this.form.fields.namespace.value?.visibility || PUBLIC_VISIBILITY;
- return VISIBILITY_LEVEL[visibility];
+ 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);
@@ -139,7 +120,7 @@ export default {
return new Set(this.restrictedVisibilityLevels);
},
allowedVisibilityLevels() {
- const allowedLevels = Object.entries(VISIBILITY_LEVEL).reduce(
+ const allowedLevels = Object.entries(VISIBILITY_LEVELS_STRING_TO_INTEGER).reduce(
(levels, [levelName, levelValue]) => {
if (
!this.restrictedVisibilityLevelsSet.has(levelValue) &&
@@ -153,7 +134,7 @@ export default {
);
if (!allowedLevels.length) {
- return [PRIVATE_VISIBILITY];
+ return [VISIBILITY_LEVEL_PRIVATE_STRING];
}
return allowedLevels;
@@ -162,58 +143,56 @@ export default {
return [
{
text: s__('ForkProject|Private'),
- value: PRIVATE_VISIBILITY,
+ value: VISIBILITY_LEVEL_PRIVATE_STRING,
icon: 'lock',
help: s__(
'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.',
),
- disabled: this.isVisibilityLevelDisabled(PRIVATE_VISIBILITY),
+ disabled: this.isVisibilityLevelDisabled(VISIBILITY_LEVEL_PRIVATE_STRING),
},
{
text: s__('ForkProject|Internal'),
- value: INTERNAL_VISIBILITY,
+ value: VISIBILITY_LEVEL_INTERNAL_STRING,
icon: 'shield',
help: s__('ForkProject|The project can be accessed by any logged in user.'),
- disabled: this.isVisibilityLevelDisabled(INTERNAL_VISIBILITY),
+ disabled: this.isVisibilityLevelDisabled(VISIBILITY_LEVEL_INTERNAL_STRING),
},
{
text: s__('ForkProject|Public'),
- value: PUBLIC_VISIBILITY,
+ value: VISIBILITY_LEVEL_PUBLIC_STRING,
icon: 'earth',
help: s__('ForkProject|The project can be accessed without any authentication.'),
- disabled: this.isVisibilityLevelDisabled(PUBLIC_VISIBILITY),
+ disabled: this.isVisibilityLevelDisabled(VISIBILITY_LEVEL_PUBLIC_STRING),
},
];
},
},
watch: {
// eslint-disable-next-line func-names
- 'form.fields.namespace.value': function () {
- this.form.fields.visibility.value =
- this.restrictedVisibilityLevels.length !== 0 ? null : PRIVATE_VISIBILITY;
- },
- // eslint-disable-next-line func-names
'form.fields.name.value': function (newVal) {
this.form.fields.slug.value = kebabCase(newVal);
},
},
- mounted() {
- this.fetchNamespaces();
- },
methods: {
- async fetchNamespaces() {
- const { data } = await axios.get(this.endpoint);
- this.namespaces = sortNamespaces(data.namespaces);
- },
isVisibilityLevelDisabled(visibility) {
return !this.allowedVisibilityLevels.includes(visibility);
},
getInitialVisibilityValue() {
return this.restrictedVisibilityLevels.length !== 0 ? null : 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;
+ },
async onSubmit() {
this.form.showValidation = true;
+ if (!this.form.fields.namespace.value) {
+ this.form.fields.namespace.state = false;
+ }
+
if (!this.form.state) {
return;
}
@@ -282,30 +261,7 @@ export default {
:state="form.fields.namespace.state"
:invalid-feedback="s__('ForkProject|Please select a namespace')"
>
- <gl-form-input-group>
- <template #prepend>
- <gl-input-group-text>
- {{ projectUrl }}
- </gl-input-group-text>
- </template>
- <gl-form-select
- id="fork-url"
- v-model="form.fields.namespace.value"
- v-validation:[form.showValidation]
- name="namespace"
- data-testid="fork-url-input"
- data-qa-selector="fork_namespace_dropdown"
- :state="form.fields.namespace.state"
- required
- >
- <template #first>
- <option :value="null" disabled>{{ s__('ForkProject|Select a namespace') }}</option>
- </template>
- <option v-for="namespace in namespaces" :key="namespace.id" :value="namespace">
- {{ namespace.full_name }}
- </option>
- </gl-form-select>
- </gl-form-input-group>
+ <project-namespace @select="setNamespace" />
</gl-form-group>
</div>
<div class="gl-flex-basis-half">
diff --git a/app/assets/javascripts/pages/projects/forks/new/components/project_namespace.vue b/app/assets/javascripts/pages/projects/forks/new/components/project_namespace.vue
new file mode 100644
index 00000000000..2b3055ecd66
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/forks/new/components/project_namespace.vue
@@ -0,0 +1,136 @@
+<script>
+import {
+ GlButton,
+ GlButtonGroup,
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownText,
+ GlDropdownSectionHeader,
+ GlSearchBoxByType,
+ GlTruncate,
+} from '@gitlab/ui';
+import createFlash from '~/flash';
+import { MINIMUM_SEARCH_LENGTH } from '~/graphql_shared/constants';
+import { s__ } from '~/locale';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants';
+import searchForkableNamespaces from '../queries/search_forkable_namespaces.query.graphql';
+
+export default {
+ components: {
+ GlButton,
+ GlButtonGroup,
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownText,
+ GlDropdownSectionHeader,
+ GlSearchBoxByType,
+ GlTruncate,
+ },
+ apollo: {
+ project: {
+ query: searchForkableNamespaces,
+ variables() {
+ return {
+ projectPath: this.projectFullPath,
+ search: this.search,
+ };
+ },
+ skip() {
+ const { length } = this.search;
+ return length > 0 && length < MINIMUM_SEARCH_LENGTH;
+ },
+ error(error) {
+ createFlash({
+ message: s__(
+ 'ForkProject|Something went wrong while loading data. Please refresh the page to try again.',
+ ),
+ captureError: true,
+ error,
+ });
+ },
+ debounce: DEBOUNCE_DELAY,
+ },
+ },
+ inject: ['projectFullPath'],
+ data() {
+ return {
+ project: {},
+ search: '',
+ selectedNamespace: null,
+ };
+ },
+ computed: {
+ rootUrl() {
+ return `${gon.gitlab_url}/`;
+ },
+ namespaces() {
+ return this.project.forkTargets?.nodes || [];
+ },
+ hasMatches() {
+ return this.namespaces.length;
+ },
+ dropdownText() {
+ return this.selectedNamespace?.fullPath || s__('ForkProject|Select a namespace');
+ },
+ },
+ methods: {
+ handleDropdownShown() {
+ this.$refs.search.focusInput();
+ },
+ setNamespace(namespace) {
+ const id = getIdFromGraphQLId(namespace.id);
+
+ this.$emit('select', {
+ id,
+ name: namespace.name,
+ visibility: namespace.visibility,
+ });
+
+ this.selectedNamespace = { id, fullPath: namespace.fullPath };
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-button-group class="gl-w-full">
+ <gl-button class="gl-text-truncate gl-flex-grow-0! gl-max-w-34" label :title="rootUrl">{{
+ rootUrl
+ }}</gl-button>
+
+ <gl-dropdown
+ class="gl-flex-grow-1"
+ toggle-class="gl-rounded-top-right-base! gl-rounded-bottom-right-base! gl-w-20"
+ data-qa-selector="select_namespace_dropdown"
+ data-testid="select_namespace_dropdown"
+ no-flip
+ @shown="handleDropdownShown"
+ >
+ <template #button-text>
+ <gl-truncate :text="dropdownText" position="start" with-tooltip />
+ </template>
+ <gl-search-box-by-type
+ ref="search"
+ v-model.trim="search"
+ :is-loading="$apollo.queries.project.loading"
+ data-qa-selector="select_namespace_dropdown_search_field"
+ data-testid="select_namespace_dropdown_search_field"
+ />
+ <template v-if="!$apollo.queries.project.loading">
+ <template v-if="hasMatches">
+ <gl-dropdown-section-header>{{ __('Namespaces') }}</gl-dropdown-section-header>
+ <gl-dropdown-item
+ v-for="namespace of namespaces"
+ :key="namespace.id"
+ data-qa-selector="select_namespace_dropdown_item"
+ @click="setNamespace(namespace)"
+ >
+ {{ namespace.fullPath }}
+ </gl-dropdown-item>
+ </template>
+ <gl-dropdown-text v-else>{{ __('No matches found') }}</gl-dropdown-text>
+ </template>
+ </gl-dropdown>
+ </gl-button-group>
+</template>
diff --git a/app/assets/javascripts/pages/projects/forks/new/index.js b/app/assets/javascripts/pages/projects/forks/new/index.js
index cbf74f755e7..d3a5ce5390f 100644
--- a/app/assets/javascripts/pages/projects/forks/new/index.js
+++ b/app/assets/javascripts/pages/projects/forks/new/index.js
@@ -1,4 +1,6 @@
import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
import App from './components/app.vue';
const mountElement = document.getElementById('fork-groups-mount-element');
@@ -17,9 +19,14 @@ const {
restrictedVisibilityLevels,
} = mountElement.dataset;
+Vue.use(VueApollo);
+
// eslint-disable-next-line no-new
new Vue({
el: mountElement,
+ apolloProvider: new VueApollo({
+ defaultClient: createDefaultClient(),
+ }),
provide: {
newGroupPath,
visibilityHelpPath,
diff --git a/app/assets/javascripts/pages/projects/forks/new/queries/search_forkable_namespaces.query.graphql b/app/assets/javascripts/pages/projects/forks/new/queries/search_forkable_namespaces.query.graphql
new file mode 100644
index 00000000000..089b57815bd
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/forks/new/queries/search_forkable_namespaces.query.graphql
@@ -0,0 +1,13 @@
+query searchForkableNamespaces($projectPath: ID!, $search: String) {
+ project(fullPath: $projectPath) {
+ id
+ forkTargets(search: $search) {
+ nodes {
+ id
+ fullPath
+ name
+ visibility
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/pages/projects/google_cloud/databases/index.js b/app/assets/javascripts/pages/projects/google_cloud/databases/index.js
deleted file mode 100644
index 5482324f1cd..00000000000
--- a/app/assets/javascripts/pages/projects/google_cloud/databases/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import init from '~/google_cloud/databases/index';
-
-init();
diff --git a/app/assets/javascripts/pages/projects/google_cloud/databases/index/index.js b/app/assets/javascripts/pages/projects/google_cloud/databases/index/index.js
new file mode 100644
index 00000000000..e1dc0116707
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/google_cloud/databases/index/index.js
@@ -0,0 +1,3 @@
+import init from '~/google_cloud/databases/init_index';
+
+init();
diff --git a/app/assets/javascripts/pages/projects/google_cloud/databases/new/index.js b/app/assets/javascripts/pages/projects/google_cloud/databases/new/index.js
new file mode 100644
index 00000000000..698e788789b
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/google_cloud/databases/new/index.js
@@ -0,0 +1,3 @@
+import init from '~/google_cloud/databases/init_new';
+
+init();
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 d7e68484143..08d24344ffc 100644
--- a/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue
+++ b/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue
@@ -180,7 +180,7 @@ export default {
v-for="({ group_name }, index) in dailyCoverageData"
:key="index"
:value="group_name"
- :is-check-item="true"
+ is-check-item
:is-checked="index === selectedCoverageIndex"
@click="setSelectedCoverage(index)"
>
diff --git a/app/assets/javascripts/pages/projects/merge_requests/edit/index.js b/app/assets/javascripts/pages/projects/merge_requests/edit/index.js
index ec21d8c84e0..5179d1b31ab 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/edit/index.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/edit/index.js
@@ -1,5 +1,74 @@
+import createFlash from '~/flash';
+import axios from '~/lib/utils/axios_utils';
+import { __ } from '~/locale';
+
+import { GitLabDropdown } from '~/deprecated_jquery_dropdown/gl_dropdown';
+
import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request';
import initCheckFormState from './check_form_state';
+import initFormUpdate from './update_form';
+
+function initTargetBranchSelector() {
+ const targetBranch = document.querySelector('.js-target-branch');
+ const { selected, fieldName, refsUrl } = targetBranch?.dataset ?? {};
+ const formField = document.querySelector(`input[name="${fieldName}"]`);
+
+ if (targetBranch && refsUrl && formField) {
+ /* eslint-disable-next-line no-new */
+ new GitLabDropdown(targetBranch, {
+ selectable: true,
+ filterable: true,
+ filterRemote: Boolean(refsUrl),
+ filterInput: 'input[type="search"]',
+ data(term, callback) {
+ const params = {
+ search: term,
+ };
+
+ axios
+ .get(refsUrl, {
+ params,
+ })
+ .then(({ data }) => {
+ callback(data);
+ })
+ .catch(() =>
+ createFlash({
+ message: __('Error fetching branches'),
+ }),
+ );
+ },
+ renderRow(branch) {
+ const item = document.createElement('li');
+ const link = document.createElement('a');
+
+ link.setAttribute('href', '#');
+ link.dataset.branch = branch;
+ link.classList.toggle('is-active', branch === selected);
+ link.textContent = branch;
+
+ item.appendChild(link);
+
+ return item;
+ },
+ id(obj, $el) {
+ return $el.data('id');
+ },
+ toggleLabel(obj, $el) {
+ return $el.text().trim();
+ },
+ clicked({ $el, e }) {
+ e.preventDefault();
+
+ const branchName = $el[0].dataset.branch;
+
+ formField.setAttribute('value', branchName);
+ },
+ });
+ }
+}
initMergeRequest();
+initFormUpdate();
initCheckFormState();
+initTargetBranchSelector();
diff --git a/app/assets/javascripts/pages/projects/merge_requests/edit/update_form.js b/app/assets/javascripts/pages/projects/merge_requests/edit/update_form.js
new file mode 100644
index 00000000000..3bb64f741e7
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/merge_requests/edit/update_form.js
@@ -0,0 +1,23 @@
+const findForm = () => document.querySelector('.merge-request-form');
+
+const removeHiddenCheckbox = (node) => {
+ const checkboxWrapper = node.closest('.form-check');
+ const hiddenCheckbox = checkboxWrapper.querySelector('input[type="hidden"]');
+ hiddenCheckbox.remove();
+};
+
+export default () => {
+ const updateCheckboxes = () => {
+ const checkboxes = document.querySelectorAll('.js-form-update');
+
+ if (!checkboxes.length) return;
+
+ checkboxes.forEach((checkbox) => {
+ if (checkbox.checked) {
+ removeHiddenCheckbox(checkbox);
+ }
+ });
+ };
+
+ findForm().addEventListener('submit', () => updateCheckboxes());
+};
diff --git a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js
index 2db804e1ad8..30734f0b698 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request_show.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
+import { s__ } from '~/locale';
import ShortcutsIssuable from '~/behaviors/shortcuts/shortcuts_issuable';
import { initPipelineCountListener } from '~/commit/pipelines/utils';
import { initIssuableSidebar } from '~/issuable';
@@ -10,6 +11,7 @@ import ZenMode from '~/zen_mode';
import initAwardsApp from '~/emoji/awards_app';
import MrWidgetHowToMergeModal from '~/vue_merge_request_widget/components/mr_widget_how_to_merge_modal.vue';
import { initMrExperienceSurvey } from '~/surveys/merge_request_experience';
+import toast from '~/vue_shared/plugins/global_toast';
import getStateQuery from './queries/get_state.query.graphql';
export default function initMergeRequestShow() {
@@ -65,4 +67,10 @@ export default function initMergeRequestShow() {
});
},
});
+
+ const copyReferenceButton = document.querySelector('.js-copy-reference');
+
+ copyReferenceButton?.addEventListener('click', () => {
+ toast(s__('MergeRequests|Reference copied'));
+ });
}
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 7f49eb60c5c..cc5c393ff8c 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/show/index.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/show/index.js
@@ -1,9 +1,14 @@
+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';
initMrNotes();
initShow();
@@ -12,4 +17,29 @@ 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);
+ },
+ });
+ }
});
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue
index 37e8a316ee4..b3ad50f395b 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue
@@ -29,7 +29,7 @@ export default {
</script>
<template>
<div v-if="!calloutDismissed" class="pipeline-schedules-user-callout user-callout">
- <div class="bordered-box landing content-block" data-testid="innerContent">
+ <div class="bordered-box landing content-block gl-p-5!" data-testid="innerContent">
<gl-button
category="tertiary"
icon="close"
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js
index 5dae812bbcb..eae721771de 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js
@@ -6,7 +6,7 @@ import { REF_TYPE_BRANCHES, REF_TYPE_TAGS } from '~/ref/constants';
import setupNativeFormVariableList from '~/ci_variable_list/native_form_variable_list';
import GlFieldErrors from '~/gl_field_errors';
import Translate from '~/vue_shared/translate';
-import intervalPatternInput from './components/interval_pattern_input.vue';
+import IntervalPatternInput from './components/interval_pattern_input.vue';
import TimezoneDropdown from './components/timezone_dropdown';
Vue.use(Translate);
@@ -19,7 +19,7 @@ function initIntervalPatternInput() {
return new Vue({
el: intervalPatternMount,
components: {
- intervalPatternInput,
+ IntervalPatternInput,
},
render(createElement) {
return createElement('interval-pattern-input', {
diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js
index 032e2410233..ccabaad5b2e 100644
--- a/app/assets/javascripts/pages/projects/project.js
+++ b/app/assets/javascripts/pages/projects/project.js
@@ -141,7 +141,7 @@ export default class Project {
if (doesPathContainRef) {
// We are ignoring the url containing the ref portion
// and plucking the thereafter portion to reconstructure the url that is correct
- const targetPath = splitPathAfterRefPortion?.slice(1).split('#')[0];
+ const targetPath = splitPathAfterRefPortion?.slice(1).split('#')[0].split('?')[0];
selectedUrl.searchParams.set('path', targetPath);
selectedUrl.hash = window.location.hash;
}
diff --git a/app/assets/javascripts/pages/projects/settings/merge_requests/index.js b/app/assets/javascripts/pages/projects/settings/merge_requests/index.js
new file mode 100644
index 00000000000..739e666644c
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/settings/merge_requests/index.js
@@ -0,0 +1,10 @@
+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
+new UserCallout({ className: 'js-mr-approval-callout' });
diff --git a/app/assets/javascripts/pages/projects/settings/packages_and_registries/cleanup_tags/index.js b/app/assets/javascripts/pages/projects/settings/packages_and_registries/cleanup_tags/index.js
new file mode 100644
index 00000000000..acd5d3febff
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/settings/packages_and_registries/cleanup_tags/index.js
@@ -0,0 +1,5 @@
+import registrySettingsCleanupTagsApp from '~/packages_and_registries/settings/project/registry_settings_cleanup_tags_bundle';
+import initSettingsPanels from '~/settings_panels';
+
+registrySettingsCleanupTagsApp();
+initSettingsPanels();
diff --git a/app/assets/javascripts/pages/projects/settings/repository/create_deploy_token/index.js b/app/assets/javascripts/pages/projects/settings/repository/create_deploy_token/index.js
index 1dc238b56b4..6a7c6028c95 100644
--- a/app/assets/javascripts/pages/projects/settings/repository/create_deploy_token/index.js
+++ b/app/assets/javascripts/pages/projects/settings/repository/create_deploy_token/index.js
@@ -1,3 +1 @@
-import initForm from '../form';
-
-initForm();
+import '../show/index';
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 c7c331c7de5..a82f485bf44 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
@@ -5,7 +5,11 @@ import settingsMixin from 'ee_else_ce/pages/projects/shared/permissions/mixins/s
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { __, s__ } from '~/locale';
import {
- visibilityOptions,
+ VISIBILITY_LEVEL_PRIVATE_INTEGER,
+ VISIBILITY_LEVEL_INTERNAL_INTEGER,
+ VISIBILITY_LEVEL_PUBLIC_INTEGER,
+} from '~/visibility_level/constants';
+import {
visibilityLevelDescriptions,
featureAccessLevelMembers,
featureAccessLevelEveryone,
@@ -14,8 +18,8 @@ import {
featureAccessLevelDescriptions,
} from '../constants';
import { toggleHiddenClassBySelector } from '../external';
-import projectFeatureSetting from './project_feature_setting.vue';
-import projectSettingRow from './project_setting_row.vue';
+import ProjectFeatureSetting from './project_feature_setting.vue';
+import ProjectSettingRow from './project_setting_row.vue';
const FEATURE_ACCESS_LEVEL_ANONYMOUS = [30, s__('ProjectSettings|Everyone')];
@@ -33,6 +37,11 @@ export default {
environmentsHelpText: s__(
'ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access.',
),
+ featureFlagsLabel: s__('ProjectSettings|Feature flags'),
+ featureFlagsHelpText: s__(
+ 'ProjectSettings|Roll out new features without redeploying with feature flags.',
+ ),
+ monitorLabel: s__('ProjectSettings|Monitor'),
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.',
),
@@ -45,6 +54,10 @@ export default {
ciCdLabel: __('CI/CD'),
repositoryLabel: s__('ProjectSettings|Repository'),
requirementsLabel: s__('ProjectSettings|Requirements'),
+ releasesLabel: s__('ProjectSettings|Releases'),
+ releasesHelpText: s__(
+ 'ProjectSettings|Combine git tags with release notes, release evidence, and assets to create a release.',
+ ),
securityAndComplianceLabel: s__('ProjectSettings|Security & Compliance'),
snippetsLabel: s__('ProjectSettings|Snippets'),
wikiLabel: s__('ProjectSettings|Wiki'),
@@ -54,10 +67,13 @@ export default {
),
confirmButtonText: __('Save changes'),
},
+ VISIBILITY_LEVEL_PRIVATE_INTEGER,
+ VISIBILITY_LEVEL_INTERNAL_INTEGER,
+ VISIBILITY_LEVEL_PUBLIC_INTEGER,
components: {
- projectFeatureSetting,
- projectSettingRow,
+ ProjectFeatureSetting,
+ ProjectSettingRow,
GlButton,
GlIcon,
GlSprintf,
@@ -65,7 +81,7 @@ export default {
GlFormCheckbox,
GlToggle,
ConfirmDanger,
- otherProjectSettings: () =>
+ OtherProjectSettings: () =>
import(
'jh_component/pages/projects/shared/permissions/components/other_project_settings.vue'
),
@@ -96,9 +112,9 @@ export default {
type: Array,
required: false,
default: () => [
- visibilityOptions.PRIVATE,
- visibilityOptions.INTERNAL,
- visibilityOptions.PUBLIC,
+ VISIBILITY_LEVEL_PRIVATE_INTEGER,
+ VISIBILITY_LEVEL_INTERNAL_INTEGER,
+ VISIBILITY_LEVEL_PUBLIC_INTEGER,
],
},
lfsAvailable: {
@@ -131,6 +147,21 @@ export default {
required: false,
default: '',
},
+ environmentsHelpPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ featureFlagsHelpPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ releasesHelpPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
lfsHelpPath: {
type: String,
required: false,
@@ -197,8 +228,7 @@ export default {
},
data() {
const defaults = {
- visibilityOptions,
- visibilityLevel: visibilityOptions.PUBLIC,
+ visibilityLevel: VISIBILITY_LEVEL_PUBLIC_INTEGER,
issuesAccessLevel: featureAccessLevel.EVERYONE,
repositoryAccessLevel: featureAccessLevel.EVERYONE,
forkingAccessLevel: featureAccessLevel.EVERYONE,
@@ -214,6 +244,9 @@ export default {
securityAndComplianceAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
operationsAccessLevel: featureAccessLevel.EVERYONE,
environmentsAccessLevel: featureAccessLevel.EVERYONE,
+ featureFlagsAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
+ releasesAccessLevel: featureAccessLevel.EVERYONE,
+ monitorAccessLevel: featureAccessLevel.EVERYONE,
containerRegistryAccessLevel: featureAccessLevel.EVERYONE,
warnAboutPotentiallyUnwantedCharacters: true,
lfsEnabled: true,
@@ -234,7 +267,7 @@ export default {
computed: {
featureAccessLevelOptions() {
const options = [featureAccessLevelMembers];
- if (this.visibilityLevel !== visibilityOptions.PRIVATE) {
+ if (this.visibilityLevel !== VISIBILITY_LEVEL_PRIVATE_INTEGER) {
options.push(featureAccessLevelEveryone);
}
return options;
@@ -246,18 +279,12 @@ export default {
);
},
- operationsFeatureAccessLevelOptions() {
- return this.featureAccessLevelOptions.filter(
- ([value]) => value <= this.operationsAccessLevel,
- );
- },
-
packageRegistryFeatureAccessLevelOptions() {
const options = [FEATURE_ACCESS_LEVEL_ANONYMOUS];
- if (this.visibilityLevel === visibilityOptions.PRIVATE) {
+ if (this.visibilityLevel === VISIBILITY_LEVEL_PRIVATE_INTEGER) {
options.unshift(featureAccessLevelMembers);
- } else if (this.visibilityLevel === visibilityOptions.INTERNAL) {
+ } else if (this.visibilityLevel === VISIBILITY_LEVEL_INTERNAL_INTEGER) {
options.unshift(featureAccessLevelEveryone);
}
@@ -268,15 +295,15 @@ export default {
const options = [featureAccessLevelMembers];
if (this.pagesAccessControlForced) {
- if (this.visibilityLevel === visibilityOptions.INTERNAL) {
+ if (this.visibilityLevel === VISIBILITY_LEVEL_INTERNAL_INTEGER) {
options.push(featureAccessLevelEveryone);
}
} else {
- if (this.visibilityLevel !== visibilityOptions.PRIVATE) {
+ if (this.visibilityLevel !== VISIBILITY_LEVEL_PRIVATE_INTEGER) {
options.push(featureAccessLevelEveryone);
}
- if (this.visibilityLevel !== visibilityOptions.PUBLIC) {
+ if (this.visibilityLevel !== VISIBILITY_LEVEL_PUBLIC_INTEGER) {
options.push(FEATURE_ACCESS_LEVEL_ANONYMOUS);
}
}
@@ -290,6 +317,11 @@ export default {
environmentsEnabled() {
return this.environmentsAccessLevel > featureAccessLevel.NOT_ENABLED;
},
+
+ monitorEnabled() {
+ return this.monitorAccessLevel > featureAccessLevel.NOT_ENABLED;
+ },
+
repositoryEnabled() {
return this.repositoryAccessLevel > featureAccessLevel.NOT_ENABLED;
},
@@ -300,13 +332,13 @@ export default {
showContainerRegistryPublicNote() {
return (
- this.visibilityLevel === visibilityOptions.PUBLIC &&
+ this.visibilityLevel === VISIBILITY_LEVEL_PUBLIC_INTEGER &&
this.containerRegistryAccessLevel === featureAccessLevel.EVERYONE
);
},
repositoryHelpText() {
- if (this.visibilityLevel === visibilityOptions.PRIVATE) {
+ if (this.visibilityLevel === VISIBILITY_LEVEL_PRIVATE_INTEGER) {
return s__('ProjectSettings|View and edit files in this project.');
}
@@ -315,7 +347,7 @@ export default {
);
},
cveIdRequestIsDisabled() {
- return this.visibilityLevel !== visibilityOptions.PUBLIC;
+ return this.visibilityLevel !== VISIBILITY_LEVEL_PUBLIC_INTEGER;
},
isVisibilityReduced() {
return (
@@ -329,11 +361,19 @@ export default {
splitOperationsEnabled() {
return this.glFeatures.splitOperationsVisibilityPermissions;
},
+ monitorOperationsFeatureAccessLevelOptions() {
+ if (this.splitOperationsEnabled) {
+ return this.featureAccessLevelOptions.filter(([value]) => value <= this.monitorAccessLevel);
+ }
+ return this.featureAccessLevelOptions.filter(
+ ([value]) => value <= this.operationsAccessLevel,
+ );
+ },
},
watch: {
visibilityLevel(value, oldValue) {
- if (value === visibilityOptions.PRIVATE) {
+ if (value === VISIBILITY_LEVEL_PRIVATE_INTEGER) {
// when private, features are restricted to "only team members"
this.issuesAccessLevel = Math.min(
featureAccessLevel.PROJECT_MEMBERS,
@@ -355,7 +395,7 @@ export default {
if (
this.packageRegistryAccessLevel === featureAccessLevel.EVERYONE ||
(this.packageRegistryAccessLevel > featureAccessLevel.EVERYONE &&
- oldValue === visibilityOptions.PUBLIC)
+ oldValue === VISIBILITY_LEVEL_PUBLIC_INTEGER)
) {
this.packageRegistryAccessLevel = featureAccessLevel.PROJECT_MEMBERS;
}
@@ -389,6 +429,18 @@ export default {
featureAccessLevel.PROJECT_MEMBERS,
this.environmentsAccessLevel,
);
+ this.featureFlagsAccessLevel = Math.min(
+ featureAccessLevel.PROJECT_MEMBERS,
+ this.featureFlagsAccessLevel,
+ );
+ this.releasesAccessLevel = Math.min(
+ featureAccessLevel.PROJECT_MEMBERS,
+ this.releasesAccessLevel,
+ );
+ this.monitorAccessLevel = Math.min(
+ featureAccessLevel.PROJECT_MEMBERS,
+ this.monitorAccessLevel,
+ );
this.containerRegistryAccessLevel = Math.min(
featureAccessLevel.PROJECT_MEMBERS,
this.containerRegistryAccessLevel,
@@ -398,7 +450,7 @@ export default {
this.pagesAccessLevel = featureAccessLevel.PROJECT_MEMBERS;
}
this.highlightChanges();
- } else if (oldValue === visibilityOptions.PRIVATE) {
+ } else if (oldValue === VISIBILITY_LEVEL_PRIVATE_INTEGER) {
// if changing away from private, make enabled features more permissive
if (this.issuesAccessLevel > featureAccessLevel.NOT_ENABLED)
this.issuesAccessLevel = featureAccessLevel.EVERYONE;
@@ -432,19 +484,21 @@ export default {
this.operationsAccessLevel = featureAccessLevel.EVERYONE;
if (this.environmentsAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
this.environmentsAccessLevel = featureAccessLevel.EVERYONE;
+ if (this.monitorAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
+ this.monitorAccessLevel = featureAccessLevel.EVERYONE;
if (this.containerRegistryAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
this.containerRegistryAccessLevel = featureAccessLevel.EVERYONE;
this.highlightChanges();
} else if (this.packageRegistryAccessLevelEnabled) {
if (
- value === visibilityOptions.PUBLIC &&
+ value === VISIBILITY_LEVEL_PUBLIC_INTEGER &&
this.packageRegistryAccessLevel === featureAccessLevel.EVERYONE
) {
// eslint-disable-next-line prefer-destructuring
this.packageRegistryAccessLevel = FEATURE_ACCESS_LEVEL_ANONYMOUS[0];
} else if (
- value === visibilityOptions.INTERNAL &&
+ value === VISIBILITY_LEVEL_INTERNAL_INTEGER &&
this.packageRegistryAccessLevel === FEATURE_ACCESS_LEVEL_ANONYMOUS[0]
) {
this.packageRegistryAccessLevel = featureAccessLevel.EVERYONE;
@@ -467,6 +521,16 @@ export default {
},
operationsAccessLevel(value, oldValue) {
+ this.updateSubFeatureAccessLevel(value, oldValue);
+ },
+
+ monitorAccessLevel(value, oldValue) {
+ this.updateSubFeatureAccessLevel(value, oldValue);
+ },
+ },
+
+ methods: {
+ updateSubFeatureAccessLevel(value, oldValue) {
if (value < oldValue) {
// sub-features cannot have more permissive access level
this.metricsDashboardAccessLevel = Math.min(this.metricsDashboardAccessLevel, value);
@@ -474,9 +538,7 @@ export default {
this.metricsDashboardAccessLevel = value;
}
},
- },
- methods: {
highlightChanges() {
this.highlightChangesClass = true;
this.$nextTick(() => {
@@ -514,20 +576,20 @@ export default {
data-qa-selector="project_visibility_dropdown"
>
<option
- :value="visibilityOptions.PRIVATE"
- :disabled="!visibilityAllowed(visibilityOptions.PRIVATE)"
+ :value="$options.VISIBILITY_LEVEL_PRIVATE_INTEGER"
+ :disabled="!visibilityAllowed($options.VISIBILITY_LEVEL_PRIVATE_INTEGER)"
>
{{ s__('ProjectSettings|Private') }}
</option>
<option
- :value="visibilityOptions.INTERNAL"
- :disabled="!visibilityAllowed(visibilityOptions.INTERNAL)"
+ :value="$options.VISIBILITY_LEVEL_INTERNAL_INTEGER"
+ :disabled="!visibilityAllowed($options.VISIBILITY_LEVEL_INTERNAL_INTEGER)"
>
{{ s__('ProjectSettings|Internal') }}
</option>
<option
- :value="visibilityOptions.PUBLIC"
- :disabled="!visibilityAllowed(visibilityOptions.PUBLIC)"
+ :value="$options.VISIBILITY_LEVEL_PUBLIC_INTEGER"
+ :disabled="!visibilityAllowed($options.VISIBILITY_LEVEL_PUBLIC_INTEGER)"
>
{{ s__('ProjectSettings|Public') }}
</option>
@@ -558,7 +620,7 @@ export default {
<div class="gl-mt-4">
<strong class="gl-display-block">{{ s__('ProjectSettings|Additional options') }}</strong>
<label
- v-if="visibilityLevel !== visibilityOptions.PRIVATE"
+ v-if="visibilityLevel !== $options.VISIBILITY_LEVEL_PRIVATE_INTEGER"
class="gl-line-height-28 gl-font-weight-normal gl-mb-0"
>
<input
@@ -570,7 +632,7 @@ export default {
{{ s__('ProjectSettings|Users can request access') }}
</label>
<label
- v-if="visibilityLevel !== visibilityOptions.PUBLIC"
+ v-if="visibilityLevel !== $options.VISIBILITY_LEVEL_PUBLIC_INTEGER"
class="gl-line-height-28 gl-font-weight-normal gl-display-block gl-mb-0"
>
<input
@@ -847,6 +909,22 @@ 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.')
+ "
+ >
+ <project-feature-setting
+ v-model="monitorAccessLevel"
+ :label="$options.i18n.monitorLabel"
+ :options="featureAccessLevelOptions"
+ 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="
@@ -869,7 +947,7 @@ export default {
<project-feature-setting
v-model="metricsDashboardAccessLevel"
:show-toggle="false"
- :options="operationsFeatureAccessLevelOptions"
+ :options="monitorOperationsFeatureAccessLevelOptions"
name="project[project_feature_attributes][metrics_dashboard_access_level]"
/>
</project-setting-row>
@@ -879,6 +957,7 @@ export default {
ref="environments-settings"
:label="$options.i18n.environmentsLabel"
:help-text="$options.i18n.environmentsHelpText"
+ :help-path="environmentsHelpPath"
>
<project-feature-setting
v-model="environmentsAccessLevel"
@@ -887,6 +966,32 @@ export default {
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"
+ :options="featureAccessLevelOptions"
+ name="project[project_feature_attributes][feature_flags_access_level]"
+ />
+ </project-setting-row>
+ <project-setting-row
+ ref="releases-settings"
+ :label="$options.i18n.releasesLabel"
+ :help-text="$options.i18n.releasesHelpText"
+ :help-path="releasesHelpPath"
+ >
+ <project-feature-setting
+ v-model="releasesAccessLevel"
+ :label="$options.i18n.releasesLabel"
+ :options="featureAccessLevelOptions"
+ name="project[project_feature_attributes][releases_access_level]"
+ />
+ </project-setting-row>
</template>
</div>
<project-setting-row v-if="canDisableEmails" ref="email-settings" class="mb-3">
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/constants.js b/app/assets/javascripts/pages/projects/shared/permissions/constants.js
index cfca9d400e3..4c687859344 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/constants.js
+++ b/app/assets/javascripts/pages/projects/shared/permissions/constants.js
@@ -1,17 +1,16 @@
import { s__, __ } from '~/locale';
-
-export const visibilityOptions = {
- PRIVATE: 0,
- INTERNAL: 10,
- PUBLIC: 20,
-};
+import {
+ VISIBILITY_LEVEL_PRIVATE_INTEGER,
+ VISIBILITY_LEVEL_INTERNAL_INTEGER,
+ VISIBILITY_LEVEL_PUBLIC_INTEGER,
+} from '~/visibility_level/constants';
export const visibilityLevelDescriptions = {
- [visibilityOptions.PRIVATE]: __(
+ [VISIBILITY_LEVEL_PRIVATE_INTEGER]: __(
`Only accessible by %{membersPageLinkStart}project members%{membersPageLinkEnd}. Membership must be explicitly granted to each user.`,
),
- [visibilityOptions.INTERNAL]: __('Accessible by any user who is logged in.'),
- [visibilityOptions.PUBLIC]: __('Accessible by anyone, regardless of authentication.'),
+ [VISIBILITY_LEVEL_INTERNAL_INTEGER]: __('Accessible by any user who is logged in.'),
+ [VISIBILITY_LEVEL_PUBLIC_INTEGER]: __('Accessible by anyone, regardless of authentication.'),
};
export const featureAccessLevel = {
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 e92f386a29e..10b95fd6f3c 100644
--- a/app/assets/javascripts/pages/shared/wikis/components/wiki_content.vue
+++ b/app/assets/javascripts/pages/shared/wikis/components/wiki_content.vue
@@ -87,7 +87,7 @@ export default {
v-else-if="!loadingContentFailed && !isLoadingContent"
ref="content"
data-qa-selector="wiki_page_content"
- data-testid="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>
diff --git a/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue b/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue
index 9d7d9e376cf..9acc1cb62a1 100644
--- a/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue
+++ b/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue
@@ -5,7 +5,6 @@ import {
GlLink,
GlButton,
GlSprintf,
- GlAlert,
GlFormGroup,
GlFormInput,
GlFormSelect,
@@ -59,14 +58,6 @@ export default {
label: s__('WikiPage|Content'),
placeholder: s__('WikiPage|Write your content or drag files here…'),
},
- contentEditor: {
- renderFailed: {
- message: s__(
- 'WikiPage|An error occurred while trying to render the content editor. Please try again later.',
- ),
- primaryAction: s__('WikiPage|Retry'),
- },
- },
linksHelpText: s__(
'WikiPage|To link to a (new) page, simply type %{linkExample}. More examples are in the %{linkStart}documentation%{linkEnd}.',
),
@@ -88,7 +79,6 @@ export default {
{ text: s__('Wiki Page|Rich text'), value: 'richText' },
],
components: {
- GlAlert,
GlIcon,
GlForm,
GlFormGroup,
@@ -115,14 +105,12 @@ export default {
content: this.pageInfo.content || '',
commitMessage: '',
isDirty: false,
- contentEditorRenderFailed: false,
contentEditorEmpty: false,
switchEditingControlDisabled: false,
};
},
computed: {
noContent() {
- if (this.isContentEditorActive) return this.contentEditorEmpty;
return !this.content.trim();
},
csrfToken() {
@@ -145,11 +133,6 @@ export default {
linkExample() {
return MARKDOWN_LINK_TEXT[this.format];
},
- toggleEditingModeButtonText() {
- return this.isContentEditorActive
- ? this.$options.i18n.editSourceButtonText
- : this.$options.i18n.editRichTextButtonText;
- },
submitButtonText() {
return this.pageInfo.persisted
? this.$options.i18n.submitButton.existingPage
@@ -177,7 +160,7 @@ export default {
return !this.isContentEditorActive;
},
disableSubmitButton() {
- return this.noContent || !this.title || this.contentEditorRenderFailed;
+ return this.noContent || !this.title;
},
isContentEditorActive() {
return this.isMarkdownFormat && this.useContentEditor;
@@ -201,23 +184,14 @@ export default {
.then(({ data }) => data.body);
},
- toggleEditingMode(editingMode) {
+ setEditingMode(editingMode) {
this.editingMode = editingMode;
- if (!this.useContentEditor && this.contentEditor) {
- this.content = this.contentEditor.getSerializedContent();
- }
- },
-
- setEditingMode(value) {
- this.editingMode = value;
},
async handleFormSubmit(e) {
e.preventDefault();
if (this.useContentEditor) {
- this.content = this.contentEditor.getSerializedContent();
-
this.trackFormSubmit();
}
@@ -235,30 +209,10 @@ export default {
this.isDirty = true;
},
- async loadInitialContent(contentEditor) {
- this.contentEditor = contentEditor;
-
- try {
- await this.contentEditor.setSerializedContent(this.content);
- this.trackContentEditorLoaded();
- } catch (e) {
- this.contentEditorRenderFailed = true;
- }
- },
-
- async retryInitContentEditor() {
- try {
- this.contentEditorRenderFailed = false;
- await this.contentEditor.setSerializedContent(this.content);
- } catch (e) {
- this.contentEditorRenderFailed = true;
- }
- },
-
- handleContentEditorChange({ empty }) {
+ handleContentEditorChange({ empty, markdown, changed }) {
this.contentEditorEmpty = empty;
- // TODO: Implement a precise mechanism to detect changes in the Content
- this.isDirty = true;
+ this.isDirty = changed;
+ this.content = markdown;
},
onPageUnload(event) {
@@ -320,17 +274,6 @@ export default {
class="wiki-form common-note-form gl-mt-3 js-quick-submit"
@submit="handleFormSubmit"
>
- <gl-alert
- v-if="isContentEditorActive && contentEditorRenderFailed"
- class="gl-mb-6"
- :dismissible="false"
- variant="danger"
- :primary-button-text="$options.i18n.contentEditor.renderFailed.primaryAction"
- @primaryAction="retryInitContentEditor"
- >
- {{ $options.i18n.contentEditor.renderFailed.message }}
- </gl-alert>
-
<input :value="csrfToken" type="hidden" name="authenticity_token" />
<input v-if="pageInfo.persisted" type="hidden" name="_method" value="put" />
<input
@@ -350,7 +293,6 @@ export default {
{{ $options.i18n.title.helpText.learnMore }}
</gl-link>
</template>
-
<gl-form-input
id="wiki_title"
v-model="title"
@@ -395,7 +337,7 @@ export default {
:checked="editingMode"
:options="$options.switchEditingControlOptions"
:disabled="switchEditingControlDisabled"
- @input="toggleEditingMode"
+ @input="setEditingMode"
/>
</div>
<local-storage-sync
@@ -436,13 +378,20 @@ export default {
<content-editor
:render-markdown="renderMarkdown"
:uploads-path="pageInfo.uploadsPath"
- @initialized="loadInitialContent"
+ :markdown="content"
+ @initialized="trackContentEditorLoaded"
@change="handleContentEditorChange"
@loading="disableSwitchEditingControl"
@loadingSuccess="enableSwitchEditingControl"
@loadingError="enableSwitchEditingControl"
/>
- <input id="wiki_content" v-model.trim="content" type="hidden" name="wiki[content]" />
+ <input
+ id="wiki_content"
+ v-model.trim="content"
+ type="hidden"
+ name="wiki[content]"
+ data-qa-selector="wiki_hidden_content"
+ />
</div>
<div class="clearfix"></div>
diff --git a/app/assets/javascripts/pages/users/activity_calendar.js b/app/assets/javascripts/pages/users/activity_calendar.js
index 94506d33b33..9e0af426f6e 100644
--- a/app/assets/javascripts/pages/users/activity_calendar.js
+++ b/app/assets/javascripts/pages/users/activity_calendar.js
@@ -1,8 +1,8 @@
import { select } from 'd3-selection';
-import dateFormat from 'dateformat';
import $ from 'jquery';
import { last } from 'lodash';
import createFlash from '~/flash';
+import dateFormat from '~/lib/dateformat';
import axios from '~/lib/utils/axios_utils';
import { getDayName, getDayDifference } from '~/lib/utils/datetime_utility';
import { formatDate } from '~/lib/utils/datetime/date_format_utility';
diff --git a/app/assets/javascripts/pages/users/user_overview_block.js b/app/assets/javascripts/pages/users/user_overview_block.js
index a7c3c9d104d..8d2d66d812e 100644
--- a/app/assets/javascripts/pages/users/user_overview_block.js
+++ b/app/assets/javascripts/pages/users/user_overview_block.js
@@ -33,6 +33,7 @@ export default class UserOverviewBlock {
const containerEl = document.querySelector(this.container);
const contentList = containerEl.querySelector('.overview-content-list');
+ // eslint-disable-next-line no-unsanitized/property
contentList.innerHTML += html;
const loadingEl = containerEl.querySelector('.loading');
diff --git a/app/assets/javascripts/pdf/index.vue b/app/assets/javascripts/pdf/index.vue
index 644eccc0232..ddc880db227 100644
--- a/app/assets/javascripts/pdf/index.vue
+++ b/app/assets/javascripts/pdf/index.vue
@@ -2,10 +2,10 @@
import pdfjsLib from 'pdfjs-dist/build/pdf';
import workerSrc from 'pdfjs-dist/build/pdf.worker.min';
-import page from './page/index.vue';
+import Page from './page/index.vue';
export default {
- components: { page },
+ components: { Page },
props: {
pdf: {
type: [String, Uint8Array],
diff --git a/app/assets/javascripts/persistent_user_callout.js b/app/assets/javascripts/persistent_user_callout.js
index 9cea89f4990..7e331bdd91d 100644
--- a/app/assets/javascripts/persistent_user_callout.js
+++ b/app/assets/javascripts/persistent_user_callout.js
@@ -7,12 +7,13 @@ const DEFERRED_LINK_CLASS = 'deferred-link';
export default class PersistentUserCallout {
constructor(container, options = container.dataset) {
- const { dismissEndpoint, featureId, groupId, namespaceId, deferLinks } = options;
+ const { dismissEndpoint, featureId, groupId, namespaceId, projectId, deferLinks } = options;
this.container = container;
this.dismissEndpoint = dismissEndpoint;
this.featureId = featureId;
this.groupId = groupId;
this.namespaceId = namespaceId;
+ this.projectId = projectId;
this.deferLinks = parseBoolean(deferLinks);
this.closeButtons = this.container.querySelectorAll('.js-close');
@@ -58,6 +59,7 @@ export default class PersistentUserCallout {
feature_name: this.featureId,
group_id: this.groupId,
namespace_id: this.namespaceId,
+ project_id: this.projectId,
})
.then(() => {
this.container.remove();
diff --git a/app/assets/javascripts/persistent_user_callouts.js b/app/assets/javascripts/persistent_user_callouts.js
index ead512e3574..2580cbcb944 100644
--- a/app/assets/javascripts/persistent_user_callouts.js
+++ b/app/assets/javascripts/persistent_user_callouts.js
@@ -17,6 +17,9 @@ const PERSISTENT_USER_CALLOUTS = [
'.js-submit-license-usage-data-banner',
'.js-project-usage-limitations-callout',
'.js-namespace-storage-alert',
+ '.js-web-hook-disabled-callout',
+ '.js-merge-request-settings-callout',
+ '.js-ultimate-feature-removal-banner',
];
const initCallouts = () => {
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
index 4398ba67d47..1f8ddae3696 100644
--- a/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue
+++ b/app/assets/javascripts/pipeline_editor/components/file_nav/branch_switcher.vue
@@ -237,7 +237,7 @@ export default {
v-for="branch in availableBranches"
:key="branch"
:is-checked="currentBranch === branch"
- :is-check-item="true"
+ is-check-item
data-qa-selector="branch_menu_item_button"
@click="selectBranch(branch)"
>
diff --git a/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue b/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue
index 7beabcfe403..feadc60a22a 100644
--- a/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue
+++ b/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue
@@ -1,6 +1,6 @@
<script>
import { __ } from '~/locale';
-import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_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 '../../constants';
@@ -10,8 +10,6 @@ export default {
},
components: {
PipelineMiniGraph,
- LinkedPipelinesMiniList: () =>
- import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'),
},
inject: ['projectFullPath'],
props: {
@@ -47,9 +45,6 @@ export default {
downstreamPipelines() {
return this.linkedPipelines?.downstream?.nodes || [];
},
- hasDownstreamPipelines() {
- return this.downstreamPipelines.length > 0;
- },
hasPipelineStages() {
return this.pipelineStages.length > 0;
},
@@ -87,23 +82,11 @@ export default {
</script>
<template>
- <div
+ <pipeline-mini-graph
v-if="hasPipelineStages"
- class="gl-align-items-center gl-display-inline-flex gl-flex-wrap stage-cell gl-mr-5"
- >
- <linked-pipelines-mini-list
- v-if="upstreamPipeline"
- :triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
- upstreamPipeline,
- ] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
- data-testid="pipeline-editor-mini-graph-upstream"
- />
- <pipeline-mini-graph :stages="pipelineStages" />
- <linked-pipelines-mini-list
- v-if="hasDownstreamPipelines"
- :triggered="downstreamPipelines"
- :pipeline-path="pipelinePath"
- data-testid="pipeline-editor-mini-graph-downstream"
- />
- </div>
+ :downstream-pipelines="downstreamPipelines"
+ :pipeline-path="pipelinePath"
+ :stages="pipelineStages"
+ :upstream-pipeline="upstreamPipeline"
+ />
</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
index 4b9c98135ec..137dfca68d6 100644
--- a/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue
+++ b/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue
@@ -174,7 +174,7 @@ export default {
<div class="gl-display-flex gl-flex-wrap">
<pipeline-editor-mini-graph :pipeline="pipeline" v-on="$listeners" />
<gl-button
- class="gl-mt-2 gl-md-mt-0"
+ class="gl-ml-3"
category="secondary"
variant="confirm"
:href="status.detailsPath"
diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
index 3fd31edec2c..548769eb214 100644
--- a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
+++ b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue
@@ -47,6 +47,7 @@ export default {
currentCiFileContent: '',
failureType: null,
failureReasons: [],
+ hasBranchLoaded: false,
initialCiFileContent: '',
isFetchingCommitSha: false,
isLintUnavailable: false,
@@ -234,7 +235,7 @@ export default {
return this.lastCommittedContent !== this.currentCiFileContent;
},
isBlobContentLoading() {
- return this.$apollo.queries.initialCiFileContent.loading;
+ return !this.hasBranchLoaded || this.$apollo.queries.initialCiFileContent.loading;
},
isCiConfigDataLoading() {
return this.$apollo.queries.ciConfigData.loading;
@@ -243,7 +244,7 @@ export default {
return this.currentCiFileContent === '';
},
shouldSkipBlobContentQuery() {
- return this.isNewCiConfigFile || this.lastCommittedContent || !this.currentBranch;
+ return this.isNewCiConfigFile || this.lastCommittedContent || !this.hasBranchLoaded;
},
shouldSkipCiConfigQuery() {
return !this.currentCiFileContent || !this.commitSha;
@@ -264,6 +265,17 @@ export default {
},
},
watch: {
+ currentBranch: {
+ immediate: true,
+ handler(branch) {
+ // currentBranch is a client query so it starts off undefined. In the index.js,
+ // write to the apollo cache. Once that operation is done, we can safely do operations
+ // that require the branch to have loaded.
+ if (branch) {
+ this.hasBranchLoaded = true;
+ }
+ },
+ },
isEmpty(flag) {
if (flag) {
this.setAppStatus(EDITOR_APP_STATUS_EMPTY);
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
new file mode 100644
index 00000000000..529ec4897b4
--- /dev/null
+++ b/app/assets/javascripts/pipeline_new/components/legacy_pipeline_new_form.vue
@@ -0,0 +1,490 @@
+<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-ml-n3 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 9378b67b915..529ec4897b4 100644
--- a/app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue
+++ b/app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue
@@ -282,7 +282,7 @@ export default {
const descriptions = {};
Object.entries(data).forEach(([key, { value, description }]) => {
- if (description !== null) {
+ if (description) {
params[key] = value;
descriptions[key] = description;
}
diff --git a/app/assets/javascripts/pipeline_new/index.js b/app/assets/javascripts/pipeline_new/index.js
index 927eeb5e144..e3f363f4ada 100644
--- a/app/assets/javascripts/pipeline_new/index.js
+++ b/app/assets/javascripts/pipeline_new/index.js
@@ -1,27 +1,72 @@
import Vue from 'vue';
+import LegacyPipelineNewForm from './components/legacy_pipeline_new_form.vue';
import PipelineNewForm from './components/pipeline_new_form.vue';
-export default () => {
- const el = document.getElementById('js-new-pipeline');
+const mountLegacyPipelineNewForm = (el) => {
const {
// provide/inject
projectRefsEndpoint,
// props
- projectId,
- pipelinesPath,
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
+ projectRefsEndpoint,
+
+ // props
+ configVariablesPath,
+ defaultBranch,
fileParam,
- settingsLink,
maxWarnings,
+ pipelinesPath,
+ projectId,
+ refParam,
+ settingsLink,
+ varParam,
} = el.dataset;
const variableParams = JSON.parse(varParam);
const fileParams = JSON.parse(fileParam);
+ // TODO: add apolloProvider
+
return new Vue({
el,
provide: {
@@ -30,17 +75,27 @@ export default () => {
render(createElement) {
return createElement(PipelineNewForm, {
props: {
- projectId,
- pipelinesPath,
configVariablesPath,
defaultBranch,
- refParam,
- variableParams,
fileParams,
- settingsLink,
maxWarnings: Number(maxWarnings),
+ pipelinesPath,
+ projectId,
+ refParam,
+ settingsLink,
+ variableParams,
},
});
},
});
};
+
+export default () => {
+ const el = document.getElementById('js-new-pipeline');
+
+ if (gon.features?.runPipelineGraphql) {
+ mountPipelineNewForm(el);
+ } else {
+ mountLegacyPipelineNewForm(el);
+ }
+};
diff --git a/app/assets/javascripts/pipeline_wizard/components/editor.vue b/app/assets/javascripts/pipeline_wizard/components/editor.vue
index 41611233f71..0c063241173 100644
--- a/app/assets/javascripts/pipeline_wizard/components/editor.vue
+++ b/app/assets/javascripts/pipeline_wizard/components/editor.vue
@@ -27,7 +27,7 @@ export default {
data() {
return {
editor: null,
- isUpdating: false,
+ isFocused: false,
yamlEditorExtension: null,
};
},
@@ -60,19 +60,23 @@ export default {
this.editor.onDidChangeModelContent(
debounce(() => this.handleChange(), CONTENT_UPDATE_DEBOUNCE),
);
+ this.editor.onDidFocusEditorText(() => {
+ this.isFocused = true;
+ });
+ this.editor.onDidBlurEditorText(() => {
+ this.isFocused = false;
+ });
this.updateEditorContent();
this.emitValue();
},
methods: {
async updateEditorContent() {
- this.isUpdating = true;
this.editor.setDoc(this.doc);
- this.isUpdating = false;
this.requestHighlight(this.highlight);
},
handleChange() {
this.emitValue();
- if (!this.isUpdating) {
+ if (this.isFocused) {
this.handleTouch();
}
},
diff --git a/app/assets/javascripts/pipeline_wizard/components/wrapper.vue b/app/assets/javascripts/pipeline_wizard/components/wrapper.vue
index 0fe87bcee7b..adeb4ae598b 100644
--- a/app/assets/javascripts/pipeline_wizard/components/wrapper.vue
+++ b/app/assets/javascripts/pipeline_wizard/components/wrapper.vue
@@ -5,6 +5,7 @@ import { uniqueId } from 'lodash';
import { merge } from '~/lib/utils/yaml';
import { __ } from '~/locale';
import { isValidStepSeq } from '~/pipeline_wizard/validators';
+import Tracking from '~/tracking';
import YamlEditor from './editor.vue';
import WizardStep from './step.vue';
import CommitStep from './commit.vue';
@@ -16,6 +17,8 @@ export const i18n = {
YAML-file for you to add to your repository`),
};
+const trackingMixin = Tracking.mixin();
+
export default {
name: 'PipelineWizardWrapper',
i18n,
@@ -25,6 +28,7 @@ export default {
WizardStep,
CommitStep,
},
+ mixins: [trackingMixin],
props: {
steps: {
type: Object,
@@ -43,6 +47,11 @@ export default {
type: String,
required: true,
},
+ templateId: {
+ type: String,
+ required: false,
+ default: null,
+ },
},
data() {
return {
@@ -77,6 +86,11 @@ export default {
template: this.steps.get(i).get('template', true),
}));
},
+ tracking() {
+ return {
+ category: `pipeline_wizard:${this.templateId}`,
+ };
+ },
},
watch: {
isLastStep(value) {
@@ -84,9 +98,6 @@ export default {
},
},
methods: {
- getStep(index) {
- return this.steps.get(index);
- },
resetHighlight() {
this.highlightPath = null;
},
@@ -106,6 +117,43 @@ export default {
});
return doc;
},
+ onBack() {
+ this.currentStepIndex -= 1;
+ this.track('click_button', {
+ property: 'back',
+ label: 'pipeline_wizard_navigation',
+ extra: {
+ fromStep: this.currentStepIndex + 1,
+ toStep: this.currentStepIndex,
+ },
+ });
+ },
+ onNext() {
+ this.currentStepIndex += 1;
+ this.track('click_button', {
+ property: 'next',
+ label: 'pipeline_wizard_navigation',
+ extra: {
+ fromStep: this.currentStepIndex - 1,
+ toStep: this.currentStepIndex,
+ },
+ });
+ },
+ onDone() {
+ this.$emit('done');
+ this.track('click_button', {
+ label: 'pipeline_wizard_commit',
+ property: 'commit',
+ });
+ },
+ onEditorTouched() {
+ this.track('edit', {
+ label: 'pipeline_wizard_editor_interaction',
+ extra: {
+ currentStep: this.currentStepIndex,
+ },
+ });
+ },
},
};
</script>
@@ -127,8 +175,8 @@ export default {
:file-content="pipelineBlob"
:filename="filename"
:project-path="projectPath"
- @back="currentStepIndex--"
- @done="$emit('done')"
+ @back="onBack"
+ @done="onDone"
/>
<wizard-step
v-for="(step, i) in stepList"
@@ -141,8 +189,8 @@ export default {
:highlight.sync="highlightPath"
:inputs="step.inputs"
:template="step.template"
- @back="currentStepIndex--"
- @next="currentStepIndex++"
+ @back="onBack"
+ @next="onNext"
@update:compiled="onUpdate"
/>
</section>
@@ -162,6 +210,7 @@ export default {
:highlight="highlightPath"
class="gl-w-full"
@update:yaml="onEditorUpdate"
+ @touch.once="onEditorTouched"
/>
<div
v-if="showPlaceholder"
diff --git a/app/assets/javascripts/pipeline_wizard/pipeline_wizard.vue b/app/assets/javascripts/pipeline_wizard/pipeline_wizard.vue
index 79b1507ad0e..5a93de3b1be 100644
--- a/app/assets/javascripts/pipeline_wizard/pipeline_wizard.vue
+++ b/app/assets/javascripts/pipeline_wizard/pipeline_wizard.vue
@@ -42,6 +42,9 @@ export default {
steps() {
return this.parsedTemplate?.get('steps');
},
+ templateId() {
+ return this.parsedTemplate?.get('id');
+ },
},
};
</script>
@@ -60,6 +63,7 @@ export default {
:filename="filename"
:project-path="projectPath"
:steps="steps"
+ :template-id="templateId"
@done="$emit('done')"
/>
</div>
diff --git a/app/assets/javascripts/pipeline_wizard/templates/pages.yml b/app/assets/javascripts/pipeline_wizard/templates/pages.yml
index cd2242b1ba7..9d7936f2f5a 100644
--- a/app/assets/javascripts/pipeline_wizard/templates/pages.yml
+++ b/app/assets/javascripts/pipeline_wizard/templates/pages.yml
@@ -1,3 +1,4 @@
+id: gitlab/pages
title: Get started with Pages
description: "GitLab Pages lets you deploy static websites in minutes. All you
need is a .gitlab-ci.yml file. Follow the below steps to
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
index 31a34ab4fb5..1a05710a13e 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
@@ -170,7 +170,7 @@ export default {
ref="mainPipelineContainer"
class="gl-display-flex gl-position-relative gl-bg-gray-10 gl-white-space-nowrap"
:class="{
- 'gl-pipeline-min-h gl-py-5 gl-overflow-auto gl-border-t-solid gl-border-t-1 gl-border-gray-100': !isLinkedPipeline,
+ 'gl-pipeline-min-h gl-py-5 gl-overflow-auto': !isLinkedPipeline,
}"
>
<linked-graph-wrapper>
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 14872c34afb..f822e2c0874 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component_wrapper.vue
@@ -281,7 +281,6 @@ export default {
:type="graphViewType"
:show-links="showLinks"
:tip-previously-dismissed="hoverTipPreviouslyDismissed"
- :is-pipeline-complete="pipeline.complete"
@dismissHoverTip="handleTipDismissal"
@updateViewType="updateViewType"
@updateShowLinksState="updateShowLinksState"
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue b/app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue
index a8c5d85f4ed..6d8c35f4482 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_view_selector.vue
@@ -1,33 +1,19 @@
<script>
-import {
- GlAlert,
- GlButton,
- GlButtonGroup,
- GlLoadingIcon,
- GlToggle,
- GlModalDirective,
-} from '@gitlab/ui';
+import { GlAlert, GlButton, GlButtonGroup, GlLoadingIcon, GlToggle } from '@gitlab/ui';
import { __, s__ } from '~/locale';
-import Tracking from '~/tracking';
-import PerformanceInsightsModal from '../performance_insights_modal.vue';
-import { performanceModalId } from '../../constants';
import { STAGE_VIEW, LAYER_VIEW } from './constants';
export default {
name: 'GraphViewSelector',
- performanceModalId,
+
components: {
GlAlert,
GlButton,
GlButtonGroup,
GlLoadingIcon,
GlToggle,
- PerformanceInsightsModal,
- },
- directives: {
- GlModal: GlModalDirective,
},
- mixins: [Tracking.mixin()],
+
props: {
showLinks: {
type: Boolean,
@@ -41,10 +27,6 @@ export default {
type: String,
required: true,
},
- isPipelineComplete: {
- type: Boolean,
- required: true,
- },
},
data() {
return {
@@ -59,7 +41,6 @@ export default {
hoverTipText: __('Tip: Hover over a job to see the jobs it depends on to run.'),
linksLabelText: s__('GraphViewType|Show dependencies'),
viewLabelText: __('Group jobs by'),
- performanceBtnText: __('Performance insights'),
},
views: {
[STAGE_VIEW]: {
@@ -150,9 +131,6 @@ export default {
this.$emit('updateShowLinksState', val);
});
},
- trackInsightsClick() {
- this.track('click_insights_button', { label: 'performance_insights' });
- },
},
};
</script>
@@ -178,15 +156,6 @@ export default {
</gl-button>
</gl-button-group>
- <gl-button
- v-if="isPipelineComplete"
- v-gl-modal="$options.performanceModalId"
- data-testid="pipeline-insights-btn"
- @click="trackInsightsClick"
- >
- {{ $options.i18n.performanceBtnText }}
- </gl-button>
-
<div v-if="showLinksToggle" class="gl-display-flex gl-align-items-center">
<gl-toggle
v-model="showLinksActive"
@@ -202,7 +171,5 @@ export default {
<gl-alert v-if="showTip" class="gl-my-5" variant="tip" @dismiss="dismissTip">
{{ $options.i18n.hoverTipText }}
</gl-alert>
-
- <performance-insights-modal />
</div>
</template>
diff --git a/app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue b/app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue
index 8d764fad0c5..02d0c07ea54 100644
--- a/app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue
+++ b/app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue
@@ -82,7 +82,9 @@ export default {
:stage-name="stageName"
/>
- <div class="gl-font-weight-100 gl-font-size-lg gl-ml-n4">{{ group.size }}</div>
+ <div class="gl-font-weight-100 gl-font-size-lg gl-ml-n4 gl-align-self-center">
+ {{ group.size }}
+ </div>
</div>
</button>
diff --git a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
index 6ab4eb58977..4aec28295bd 100644
--- a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
@@ -1,5 +1,5 @@
<script>
-import { capitalize, escape, isEmpty } from 'lodash';
+import { escape, isEmpty } from 'lodash';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { reportToSentry } from '../../utils';
import MainGraphWrapper from '../graph_shared/main_graph_wrapper.vue';
@@ -64,8 +64,7 @@ export default {
},
},
jobClasses: [
- 'gl-py-3',
- 'gl-px-4',
+ 'gl-p-3',
'gl-border-gray-100',
'gl-border-solid',
'gl-border-1',
@@ -92,9 +91,6 @@ export default {
columnSpacingClass() {
return this.isStageView ? 'gl-px-6' : 'gl-px-9';
},
- formattedTitle() {
- return capitalize(escape(this.name));
- },
hasAction() {
return !isEmpty(this.action);
},
@@ -141,8 +137,8 @@ export default {
class="gl-display-flex gl-justify-content-space-between gl-relative"
:class="$options.titleClasses"
>
- <span :title="formattedTitle" class="gl-text-truncate gl-pr-3 gl-w-85p">
- {{ formattedTitle }}
+ <span :title="name" class="gl-text-truncate gl-pr-3 gl-w-85p">
+ {{ name }}
</span>
<action-component
v-if="hasAction && canUpdatePipeline"
diff --git a/app/assets/javascripts/pipelines/components/header_component.vue b/app/assets/javascripts/pipelines/components/header_component.vue
index fabae62fc45..a36d5d9b58f 100644
--- a/app/assets/javascripts/pipelines/components/header_component.vue
+++ b/app/assets/javascripts/pipelines/components/header_component.vue
@@ -9,7 +9,7 @@ import {
} from '@gitlab/ui';
import { setUrlFragment, redirectTo } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
-import ciHeader from '~/vue_shared/components/header_ci_component.vue';
+import CiHeader from '~/vue_shared/components/header_ci_component.vue';
import {
LOAD_FAILURE,
POST_FAILURE,
@@ -33,7 +33,7 @@ export default {
pipelineRetry: 'pipelineRetry',
finishedStatuses: ['FAILED', 'SUCCESS', 'CANCELED'],
components: {
- ciHeader,
+ CiHeader,
GlAlert,
GlButton,
GlLoadingIcon,
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 70d1a5c08cc..f4fc6893520 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
@@ -1,5 +1,5 @@
<script>
-import ciIcon from '~/vue_shared/components/ci_icon.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
/**
* Component that renders both the CI icon status and the job name.
@@ -9,7 +9,7 @@ import ciIcon from '~/vue_shared/components/ci_icon.vue';
*/
export default {
components: {
- ciIcon,
+ CiIcon,
},
props: {
name: {
diff --git a/app/assets/javascripts/pipelines/components/performance_insights_modal.vue b/app/assets/javascripts/pipelines/components/performance_insights_modal.vue
deleted file mode 100644
index fdbf0ca19bc..00000000000
--- a/app/assets/javascripts/pipelines/components/performance_insights_modal.vue
+++ /dev/null
@@ -1,171 +0,0 @@
-<script>
-import { GlAlert, GlCard, GlLink, GlLoadingIcon, GlModal } from '@gitlab/ui';
-import { __, s__ } from '~/locale';
-import { humanizeTimeInterval } from '~/lib/utils/datetime_utility';
-import HelpPopover from '~/vue_shared/components/help_popover.vue';
-import getPerformanceInsightsQuery from '../graphql/queries/get_performance_insights.query.graphql';
-import { performanceModalId } from '../constants';
-import { calculateJobStats, calculateSlowestFiveJobs } from '../utils';
-
-export default {
- name: 'PerformanceInsightsModal',
- i18n: {
- queuedCardHeader: s__('Pipeline|Longest queued job'),
- queuedCardHelp: s__(
- 'Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner',
- ),
- executedCardHeader: s__('Pipeline|Last executed job'),
- executedCardHelp: s__(
- 'Pipeline|The last executed job is the last job to start in the pipeline.',
- ),
- viewDependency: s__('Pipeline|View dependency'),
- slowJobsTitle: s__('Pipeline|Five slowest jobs'),
- feeback: __('Feedback issue'),
- insightsLimit: s__('Pipeline|Only able to show first 100 results'),
- },
- modal: {
- title: s__('Pipeline|Performance insights'),
- actionCancel: {
- text: __('Close'),
- attributes: {
- variant: 'confirm',
- },
- },
- },
- performanceModalId,
- components: {
- GlAlert,
- GlCard,
- GlLink,
- GlModal,
- GlLoadingIcon,
- HelpPopover,
- },
- inject: {
- pipelineIid: {
- default: '',
- },
- pipelineProjectPath: {
- default: '',
- },
- },
- apollo: {
- jobs: {
- query: getPerformanceInsightsQuery,
- variables() {
- return {
- fullPath: this.pipelineProjectPath,
- iid: this.pipelineIid,
- };
- },
- update(data) {
- return data.project?.pipeline?.jobs;
- },
- },
- },
- data() {
- return {
- jobs: null,
- };
- },
- computed: {
- longestQueuedJob() {
- return calculateJobStats(this.jobs, 'queuedDuration');
- },
- lastExecutedJob() {
- return calculateJobStats(this.jobs, 'startedAt');
- },
- slowestFiveJobs() {
- return calculateSlowestFiveJobs(this.jobs);
- },
- queuedDurationDisplay() {
- return humanizeTimeInterval(this.longestQueuedJob.queuedDuration);
- },
- showLimitMessage() {
- return this.jobs.pageInfo.hasNextPage;
- },
- },
-};
-</script>
-
-<template>
- <gl-modal
- :modal-id="$options.performanceModalId"
- :title="$options.modal.title"
- :action-cancel="$options.modal.actionCancel"
- >
- <gl-loading-icon v-if="$apollo.queries.jobs.loading" size="lg" />
-
- <template v-else>
- <gl-alert class="gl-mb-4" :dismissible="false">
- <p v-if="showLimitMessage" data-testid="limit-alert-text">
- {{ $options.i18n.insightsLimit }}
- </p>
- <gl-link href="https://gitlab.com/gitlab-org/gitlab/-/issues/365902" class="gl-mt-5">
- {{ $options.i18n.feeback }}
- </gl-link>
- </gl-alert>
-
- <div class="gl-display-flex gl-justify-content-space-between gl-mt-2 gl-mb-7">
- <gl-card class="gl-w-half gl-mr-7 gl-text-center">
- <template #header>
- <span class="gl-font-weight-bold">{{ $options.i18n.queuedCardHeader }}</span>
- <help-popover>
- {{ $options.i18n.queuedCardHelp }}
- </help-popover>
- </template>
- <div class="gl-display-flex gl-flex-direction-column">
- <span
- class="gl-font-weight-bold gl-font-size-h2 gl-mb-2"
- data-testid="insights-queued-card-data"
- >
- {{ queuedDurationDisplay }}
- </span>
- <gl-link
- :href="longestQueuedJob.detailedStatus.detailsPath"
- data-testid="insights-queued-card-link"
- >
- {{ longestQueuedJob.name }}
- </gl-link>
- </div>
- </gl-card>
- <gl-card class="gl-w-half gl-text-center" data-testid="insights-executed-card">
- <template #header>
- <span class="gl-font-weight-bold">{{ $options.i18n.executedCardHeader }}</span>
- <help-popover>
- {{ $options.i18n.executedCardHelp }}
- </help-popover>
- </template>
- <div class="gl-display-flex gl-flex-direction-column">
- <span
- class="gl-font-weight-bold gl-font-size-h2 gl-mb-2"
- data-testid="insights-executed-card-data"
- >
- {{ lastExecutedJob.name }}
- </span>
- <gl-link
- :href="lastExecutedJob.detailedStatus.detailsPath"
- data-testid="insights-executed-card-link"
- >
- {{ $options.i18n.viewDependency }}
- </gl-link>
- </div>
- </gl-card>
- </div>
-
- <div class="gl-mt-7">
- <span class="gl-font-weight-bold">{{ $options.i18n.slowJobsTitle }}</span>
- <div
- v-for="job in slowestFiveJobs"
- :key="job.name"
- class="gl-display-flex gl-justify-content-space-between gl-mb-3 gl-mt-3 gl-p-4 gl-border-t-1 gl-border-t-solid gl-border-b-0 gl-border-b-solid gl-border-gray-100"
- >
- <span data-testid="insights-slow-job-stage">{{ job.stage.name }}</span>
- <gl-link :href="job.detailedStatus.detailsPath" data-testid="insights-slow-job-link">{{
- job.name
- }}</gl-link>
- </div>
- </div>
- </template>
- </gl-modal>
-</template>
diff --git a/app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue b/app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue
index 793e343a02a..3f1d7255a2b 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_graph/job_pill.vue
@@ -1,9 +1,9 @@
<script>
-import tooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
+import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
export default {
components: {
- tooltipOnTruncate,
+ TooltipOnTruncate,
},
props: {
jobName: {
diff --git a/app/assets/javascripts/pipelines/components/pipeline_graph/stage_name.vue b/app/assets/javascripts/pipelines/components/pipeline_graph/stage_name.vue
index e485b38ce11..600832b7633 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_graph/stage_name.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_graph/stage_name.vue
@@ -1,10 +1,9 @@
<script>
-import { capitalize, escape } from 'lodash';
-import tooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
+import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
export default {
components: {
- tooltipOnTruncate,
+ TooltipOnTruncate,
},
props: {
stageName: {
@@ -12,17 +11,12 @@ export default {
required: true,
},
},
- computed: {
- formattedTitle() {
- return capitalize(escape(this.stageName));
- },
- },
};
</script>
<template>
<tooltip-on-truncate :title="stageName" truncate-target="child" placement="top">
<div class="gl-py-2 gl-text-truncate gl-font-weight-bold gl-w-20">
- {{ formattedTitle }}
+ {{ stageName }}
</div>
</tooltip-on-truncate>
</template>
diff --git a/app/assets/javascripts/pipelines/components/pipeline_mini_graph/accessors/linked_pipelines_accessors.js b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/accessors/linked_pipelines_accessors.js
new file mode 100644
index 00000000000..1ca9e35c008
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/accessors/linked_pipelines_accessors.js
@@ -0,0 +1,14 @@
+import { get } from 'lodash';
+
+export const accessors = {
+ rest: {
+ detailedStatus: ['details', 'status'],
+ },
+ graphql: {
+ detailedStatus: 'detailedStatus',
+ },
+};
+
+export const accessValue = (pipeline, dataMethod, path) => {
+ return get(pipeline, accessors[dataMethod][path]);
+};
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
new file mode 100644
index 00000000000..211c5f117c7
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/job_item.vue
@@ -0,0 +1,190 @@
+<script>
+import { GlTooltipDirective, GlLink } from '@gitlab/ui';
+import delayedJobMixin from '~/jobs/mixins/delayed_job_mixin';
+import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
+import { sprintf } from '~/locale';
+import { reportToSentry } from '../../utils';
+import ActionComponent from '../jobs_shared/action_component.vue';
+import JobNameComponent from '../jobs_shared/job_name_component.vue';
+
+/**
+ * Renders the badge for the pipeline graph and the job's dropdown.
+ *
+ * The following object should be provided as `job`:
+ *
+ * {
+ * "id": 4256,
+ * "name": "test",
+ * "status": {
+ * "icon": "status_success",
+ * "text": "passed",
+ * "label": "passed",
+ * "group": "success",
+ * "tooltip": "passed",
+ * "details_path": "/root/ci-mock/builds/4256",
+ * "action": {
+ * "icon": "retry",
+ * "title": "Retry",
+ * "path": "/root/ci-mock/builds/4256/retry",
+ * "method": "post"
+ * }
+ * }
+ * }
+ */
+
+export default {
+ hoverClass: 'gl-shadow-x0-y0-b3-s1-blue-500',
+ components: {
+ ActionComponent,
+ JobNameComponent,
+ GlLink,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ mixins: [delayedJobMixin],
+ props: {
+ job: {
+ type: Object,
+ required: true,
+ },
+ cssClassJobName: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ dropdownLength: {
+ type: Number,
+ required: false,
+ default: Infinity,
+ },
+ jobHovered: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ pipelineExpanded: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ pipelineId: {
+ type: Number,
+ required: false,
+ default: -1,
+ },
+ },
+ computed: {
+ boundary() {
+ return this.dropdownLength === 1 ? 'viewport' : 'scrollParent';
+ },
+ detailsPath() {
+ return this.status.details_path;
+ },
+ hasDetails() {
+ return this.status.has_details;
+ },
+ status() {
+ return this.job && this.job.status ? this.job.status : {};
+ },
+ tooltipText() {
+ const textBuilder = [];
+ const { name: jobName } = this.job;
+
+ if (jobName) {
+ textBuilder.push(jobName);
+ }
+
+ const { tooltip: statusTooltip } = this.status;
+ if (jobName && statusTooltip) {
+ textBuilder.push('-');
+ }
+
+ if (statusTooltip) {
+ if (this.isDelayedJob) {
+ textBuilder.push(sprintf(statusTooltip, { remainingTime: this.remainingTime }));
+ } else {
+ textBuilder.push(statusTooltip);
+ }
+ }
+
+ return textBuilder.join(' ');
+ },
+ /**
+ * Verifies if the provided job has an action path
+ *
+ * @return {Boolean}
+ */
+ hasAction() {
+ return this.job.status && this.job.status.action && this.job.status.action.path;
+ },
+ relatedDownstreamHovered() {
+ return this.job.name === this.jobHovered;
+ },
+ relatedDownstreamExpanded() {
+ return this.job.name === this.pipelineExpanded.jobName && this.pipelineExpanded.expanded;
+ },
+ jobClasses() {
+ return this.relatedDownstreamHovered || this.relatedDownstreamExpanded
+ ? `${this.$options.hoverClass} ${this.cssClassJobName}`
+ : this.cssClassJobName;
+ },
+ },
+ errorCaptured(err, _vm, info) {
+ reportToSentry('pipelines_job_item', `pipelines_job_item error: ${err}, info: ${info}`);
+ },
+ methods: {
+ hideTooltips() {
+ this.$root.$emit(BV_HIDE_TOOLTIP);
+ },
+ pipelineActionRequestComplete() {
+ this.$emit('pipelineActionRequestComplete');
+ },
+ },
+};
+</script>
+<template>
+ <div
+ class="ci-job-component gl-display-flex gl-align-items-center gl-justify-content-space-between"
+ data-qa-selector="job_item_container"
+ >
+ <gl-link
+ v-if="hasDetails"
+ v-gl-tooltip="{
+ boundary: 'viewport',
+ placement: 'bottom',
+ customClass: 'gl-pointer-events-none',
+ }"
+ :href="detailsPath"
+ :title="tooltipText"
+ :class="jobClasses"
+ class="js-pipeline-graph-job-link menu-item gl-text-gray-900 gl-active-text-decoration-none gl-focus-text-decoration-none gl-hover-text-decoration-none"
+ data-testid="job-with-link"
+ @click.stop="hideTooltips"
+ @mouseout="hideTooltips"
+ >
+ <job-name-component :name="job.name" :status="job.status" :icon-size="24" />
+ </gl-link>
+
+ <div
+ v-else
+ v-gl-tooltip="{ boundary, placement: 'bottom', customClass: 'gl-pointer-events-none' }"
+ :title="tooltipText"
+ :class="jobClasses"
+ class="js-job-component-tooltip non-details-job-component menu-item"
+ data-testid="job-without-link"
+ @mouseout="hideTooltips"
+ >
+ <job-name-component :name="job.name" :status="job.status" :icon-size="24" />
+ </div>
+
+ <action-component
+ v-if="hasAction"
+ :tooltip-text="status.action.title"
+ :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/linked_pipelines_mini_list.vue b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/linked_pipelines_mini_list.vue
new file mode 100644
index 00000000000..a5c6dc98694
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/linked_pipelines_mini_list.vue
@@ -0,0 +1,132 @@
+<script>
+import { GlTooltipDirective } from '@gitlab/ui';
+import { sprintf, s__ } from '~/locale';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import { accessValue } from './accessors/linked_pipelines_accessors';
+/**
+ * Renders the upstream/downstream portions of the pipeline mini graph.
+ */
+export default {
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ components: {
+ CiIcon,
+ },
+ inject: {
+ dataMethod: {
+ default: 'rest',
+ },
+ },
+ props: {
+ triggeredBy: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ triggered: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ pipelinePath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ data() {
+ return {
+ maxRenderedPipelines: 3,
+ };
+ },
+ computed: {
+ // Exactly one of these (triggeredBy and triggered) must be truthy. Never both. Never neither.
+ isUpstream() {
+ return Boolean(this.triggeredBy.length) && !this.triggered.length;
+ },
+ isDownstream() {
+ return !this.triggeredBy.length && Boolean(this.triggered.length);
+ },
+ linkedPipelines() {
+ return this.isUpstream ? this.triggeredBy : this.triggered;
+ },
+ totalPipelineCount() {
+ return this.linkedPipelines.length;
+ },
+ linkedPipelinesTrimmed() {
+ return this.totalPipelineCount > this.maxRenderedPipelines
+ ? this.linkedPipelines.slice(0, this.maxRenderedPipelines)
+ : this.linkedPipelines;
+ },
+ shouldRenderCounter() {
+ return this.isDownstream && this.linkedPipelines.length > this.maxRenderedPipelines;
+ },
+ counterLabel() {
+ return `+${this.linkedPipelines.length - this.maxRenderedPipelines}`;
+ },
+ counterTooltipText() {
+ return sprintf(s__('LinkedPipelines|%{counterLabel} more downstream pipelines'), {
+ counterLabel: this.counterLabel,
+ });
+ },
+ },
+ methods: {
+ pipelineTooltipText(pipeline) {
+ const { label } = accessValue(pipeline, this.dataMethod, 'detailedStatus');
+
+ return `${pipeline.project.name} - ${label}`;
+ },
+ pipelineStatus(pipeline) {
+ // detailedStatus is graphQL, details.status is REST
+ return pipeline?.detailedStatus || pipeline?.details?.status;
+ },
+ triggerButtonClass(pipeline) {
+ const { group } = accessValue(pipeline, this.dataMethod, 'detailedStatus');
+
+ return `ci-status-icon-${group}`;
+ },
+ },
+};
+</script>
+
+<template>
+ <span
+ v-if="linkedPipelines"
+ :class="{
+ 'is-upstream': isUpstream,
+ 'is-downstream': isDownstream,
+ }"
+ class="linked-pipeline-mini-list gl-display-inline gl-vertical-align-middle"
+ >
+ <a
+ v-for="pipeline in linkedPipelinesTrimmed"
+ :key="pipeline.id"
+ v-gl-tooltip="{ title: pipelineTooltipText(pipeline) }"
+ :href="pipeline.path"
+ :class="triggerButtonClass(pipeline)"
+ class="linked-pipeline-mini-item gl-display-inline-block gl-h-6 gl-mr-2 gl-my-2 gl-rounded-full gl-vertical-align-middle"
+ data-testid="linked-pipeline-mini-item"
+ >
+ <ci-icon
+ is-borderless
+ is-interactive
+ css-classes="gl-rounded-full"
+ :size="24"
+ :status="pipelineStatus(pipeline)"
+ class="gl-align-items-center gl-border gl-display-inline-flex"
+ />
+ </a>
+
+ <a
+ v-if="shouldRenderCounter"
+ v-gl-tooltip="{ title: counterTooltipText }"
+ :title="counterTooltipText"
+ :href="pipelinePath"
+ class="gl-align-items-center gl-bg-gray-50 gl-display-inline-flex gl-font-sm gl-h-6 gl-justify-content-center gl-rounded-pill gl-text-decoration-none gl-text-gray-500 gl-w-7 linked-pipelines-counter linked-pipeline-mini-item"
+ data-testid="linked-pipeline-counter"
+ >
+ {{ counterLabel }}
+ </a>
+ </span>
+</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
new file mode 100644
index 00000000000..993fa121d89
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue
@@ -0,0 +1,103 @@
+<script>
+import { GlIcon } from '@gitlab/ui';
+import PipelineStages from './pipeline_stages.vue';
+import LinkedPipelinesMiniList from './linked_pipelines_mini_list.vue';
+/**
+ * Renders the pipeline mini graph.
+ */
+export default {
+ components: {
+ GlIcon,
+ LinkedPipelinesMiniList,
+ PipelineStages,
+ },
+ arrowStyles: [
+ 'arrow-icon gl-display-inline-block gl-mx-1 gl-text-gray-500 gl-vertical-align-middle!',
+ ],
+ props: {
+ downstreamPipelines: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ isMergeTrain: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ pipelinePath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ stages: {
+ type: Array,
+ required: true,
+ default: () => [],
+ },
+ stagesClass: {
+ type: [Array, Object, String],
+ required: false,
+ default: '',
+ },
+ updateDropdown: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ upstreamPipeline: {
+ type: Object,
+ required: false,
+ default: () => {},
+ },
+ },
+ computed: {
+ hasDownstreamPipelines() {
+ return Boolean(this.downstreamPipelines.length);
+ },
+ },
+ methods: {
+ onPipelineActionRequestComplete() {
+ this.$emit('pipelineActionRequestComplete');
+ },
+ },
+};
+</script>
+<template>
+ <div class="stage-cell" data-testid="pipeline-mini-graph">
+ <linked-pipelines-mini-list
+ v-if="upstreamPipeline"
+ :triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
+ upstreamPipeline,
+ ] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
+ data-testid="pipeline-mini-graph-upstream"
+ />
+ <gl-icon
+ v-if="upstreamPipeline"
+ :class="$options.arrowStyles"
+ name="long-arrow"
+ data-testid="upstream-arrow-icon"
+ />
+ <pipeline-stages
+ :is-merge-train="isMergeTrain"
+ :stages="stages"
+ :update-dropdown="updateDropdown"
+ :stages-class="stagesClass"
+ data-testid="pipeline-stages"
+ @pipelineActionRequestComplete="onPipelineActionRequestComplete"
+ @miniGraphStageClick="$emit('miniGraphStageClick')"
+ />
+ <gl-icon
+ v-if="hasDownstreamPipelines"
+ :class="$options.arrowStyles"
+ name="long-arrow"
+ data-testid="downstream-arrow-icon"
+ />
+ <linked-pipelines-mini-list
+ v-if="hasDownstreamPipelines"
+ :triggered="downstreamPipelines"
+ :pipeline-path="pipelinePath"
+ data-testid="pipeline-mini-graph-downstream"
+ />
+ </div>
+</template>
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
new file mode 100644
index 00000000000..a68797a7235
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stage.vue
@@ -0,0 +1,186 @@
+<script>
+/**
+ * Renders each stage of the pipeline mini graph.
+ *
+ * Given the provided endpoint will make a request to
+ * fetch the dropdown data when the stage is clicked.
+ *
+ * Request is made inside this component to make it reusable between:
+ * 1. Pipelines main table
+ * 2. Pipelines table in commit and Merge request views
+ * 3. Merge request widget
+ * 4. Commit widget
+ */
+
+import { GlDropdown, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import createFlash from '~/flash';
+import axios from '~/lib/utils/axios_utils';
+import { __, sprintf } from '~/locale';
+import eventHub from '../../event_hub';
+import JobItem from './job_item.vue';
+
+export default {
+ i18n: {
+ stage: __('Stage:'),
+ loadingText: __('Loading, please wait.'),
+ },
+ dropdownPopperOpts: {
+ placement: 'bottom',
+ },
+ components: {
+ CiIcon,
+ GlLoadingIcon,
+ GlDropdown,
+ JobItem,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ stage: {
+ type: Object,
+ required: true,
+ },
+ updateDropdown: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ isMergeTrain: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ isDropdownOpen: false,
+ isLoading: false,
+ dropdownContent: [],
+ stageName: '',
+ };
+ },
+ watch: {
+ updateDropdown() {
+ if (this.updateDropdown && this.isDropdownOpen && !this.isLoading) {
+ this.fetchJobs();
+ }
+ },
+ },
+ methods: {
+ onHideDropdown() {
+ this.isDropdownOpen = false;
+ },
+ onShowDropdown() {
+ eventHub.$emit('clickedDropdown');
+ this.isDropdownOpen = true;
+ this.isLoading = true;
+ this.fetchJobs();
+
+ // used for tracking and is separate from event hub
+ // to avoid complexity with mixin
+ this.$emit('miniGraphStageClick');
+ },
+ fetchJobs() {
+ axios
+ .get(this.stage.dropdown_path)
+ .then(({ data }) => {
+ this.dropdownContent = data.latest_statuses;
+ this.stageName = data.name;
+ this.isLoading = false;
+ })
+ .catch(() => {
+ this.$refs.dropdown.hide();
+ this.isLoading = false;
+
+ createFlash({
+ message: __('Something went wrong on our end.'),
+ });
+ });
+ },
+ 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 });
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-dropdown
+ ref="dropdown"
+ v-gl-tooltip.hover.ds0
+ v-gl-tooltip="stage.title"
+ data-testid="mini-pipeline-graph-dropdown"
+ variant="link"
+ :aria-label="stageAriaLabel(stage.title)"
+ :lazy="true"
+ :popper-opts="$options.dropdownPopperOpts"
+ :toggle-class="['gl-rounded-full!']"
+ menu-class="mini-pipeline-graph-dropdown-menu"
+ @hide="onHideDropdown"
+ @show="onShowDropdown"
+ >
+ <template #button-content>
+ <ci-icon
+ is-borderless
+ is-interactive
+ css-classes="gl-rounded-full"
+ :is-active="isDropdownOpen"
+ :size="24"
+ :status="stage.status"
+ class="gl-align-items-center gl-border gl-display-inline-flex gl-z-index-1"
+ />
+ </template>
+ <div
+ v-if="isLoading"
+ class="gl-display-flex gl-justify-content-center gl-p-2"
+ data-testid="pipeline-stage-loading-state"
+ >
+ <gl-loading-icon size="sm" class="gl-mr-3" />
+ <p class="gl-mb-0">{{ $options.i18n.loadingText }}</p>
+ </div>
+ <ul
+ v-else
+ class="js-builds-dropdown-list scrollable-menu"
+ data-testid="mini-pipeline-graph-dropdown-menu-list"
+ >
+ <div
+ class="gl-align-items-center gl-border-b gl-display-flex gl-font-weight-bold gl-justify-content-center gl-pb-3"
+ >
+ <span class="gl-mr-1">{{ $options.i18n.stage }}</span>
+ <span data-testid="pipeline-stage-dropdown-menu-title">{{ stageName }}</span>
+ </div>
+ <li v-for="job in dropdownContent" :key="job.id">
+ <job-item
+ :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">
+ <hr role="separator" aria-orientation="horizontal" class="dropdown-divider" />
+ </li>
+ <li>
+ <div
+ class="gl-display-flex gl-align-items-center"
+ data-testid="warning-message-merge-trains"
+ >
+ <div class="menu-item gl-font-sm gl-text-gray-300!">
+ {{ s__('Pipeline|Merge train pipeline jobs can not be retried') }}
+ </div>
+ </div>
+ </li>
+ </template>
+ </ul>
+ </gl-dropdown>
+</template>
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
new file mode 100644
index 00000000000..e965dc5e6b0
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/pipeline_mini_graph/pipeline_stages.vue
@@ -0,0 +1,55 @@
+<script>
+import PipelineStage from './pipeline_stage.vue';
+/**
+ * Renders the pipeline stages portion of the pipeline mini graph.
+ */
+export default {
+ components: {
+ PipelineStage,
+ },
+ props: {
+ stages: {
+ type: Array,
+ required: true,
+ },
+ updateDropdown: {
+ type: Boolean,
+ 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>
+ <div data-testid="pipeline-stages" class="gl-display-inline gl-vertical-align-middle">
+ <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"
+ >
+ <pipeline-stage
+ :stage="stage"
+ :update-dropdown="updateDropdown"
+ :is-merge-train="isMergeTrain"
+ @pipelineActionRequestComplete="onPipelineActionRequestComplete"
+ @miniGraphStageClick="$emit('miniGraphStageClick')"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/job_item.vue b/app/assets/javascripts/pipelines/components/pipelines_list/job_item.vue
deleted file mode 100644
index 670fa398536..00000000000
--- a/app/assets/javascripts/pipelines/components/pipelines_list/job_item.vue
+++ /dev/null
@@ -1,190 +0,0 @@
-<script>
-import { GlTooltipDirective, GlLink } from '@gitlab/ui';
-import delayedJobMixin from '~/jobs/mixins/delayed_job_mixin';
-import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
-import { sprintf } from '~/locale';
-import { reportToSentry } from '../../utils';
-import ActionComponent from '../jobs_shared/action_component.vue';
-import JobNameComponent from '../jobs_shared/job_name_component.vue';
-
-/**
- * Renders the badge for the pipeline graph and the job's dropdown.
- *
- * The following object should be provided as `job`:
- *
- * {
- * "id": 4256,
- * "name": "test",
- * "status": {
- * "icon": "status_success",
- * "text": "passed",
- * "label": "passed",
- * "group": "success",
- * "tooltip": "passed",
- * "details_path": "/root/ci-mock/builds/4256",
- * "action": {
- * "icon": "retry",
- * "title": "Retry",
- * "path": "/root/ci-mock/builds/4256/retry",
- * "method": "post"
- * }
- * }
- * }
- */
-
-export default {
- hoverClass: 'gl-shadow-x0-y0-b3-s1-blue-500',
- components: {
- ActionComponent,
- JobNameComponent,
- GlLink,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- mixins: [delayedJobMixin],
- props: {
- job: {
- type: Object,
- required: true,
- },
- cssClassJobName: {
- type: String,
- required: false,
- default: '',
- },
- dropdownLength: {
- type: Number,
- required: false,
- default: Infinity,
- },
- jobHovered: {
- type: String,
- required: false,
- default: '',
- },
- pipelineExpanded: {
- type: Object,
- required: false,
- default: () => ({}),
- },
- pipelineId: {
- type: Number,
- required: false,
- default: -1,
- },
- },
- computed: {
- boundary() {
- return this.dropdownLength === 1 ? 'viewport' : 'scrollParent';
- },
- detailsPath() {
- return this.status.details_path;
- },
- hasDetails() {
- return this.status.has_details;
- },
- status() {
- return this.job && this.job.status ? this.job.status : {};
- },
- tooltipText() {
- const textBuilder = [];
- const { name: jobName } = this.job;
-
- if (jobName) {
- textBuilder.push(jobName);
- }
-
- const { tooltip: statusTooltip } = this.status;
- if (jobName && statusTooltip) {
- textBuilder.push('-');
- }
-
- if (statusTooltip) {
- if (this.isDelayedJob) {
- textBuilder.push(sprintf(statusTooltip, { remainingTime: this.remainingTime }));
- } else {
- textBuilder.push(statusTooltip);
- }
- }
-
- return textBuilder.join(' ');
- },
- /**
- * Verifies if the provided job has an action path
- *
- * @return {Boolean}
- */
- hasAction() {
- return this.job.status && this.job.status.action && this.job.status.action.path;
- },
- relatedDownstreamHovered() {
- return this.job.name === this.jobHovered;
- },
- relatedDownstreamExpanded() {
- return this.job.name === this.pipelineExpanded.jobName && this.pipelineExpanded.expanded;
- },
- jobClasses() {
- return this.relatedDownstreamHovered || this.relatedDownstreamExpanded
- ? `${this.$options.hoverClass} ${this.cssClassJobName}`
- : this.cssClassJobName;
- },
- },
- errorCaptured(err, _vm, info) {
- reportToSentry('pipelines_job_item', `pipelines_job_item error: ${err}, info: ${info}`);
- },
- methods: {
- hideTooltips() {
- this.$root.$emit(BV_HIDE_TOOLTIP);
- },
- pipelineActionRequestComplete() {
- this.$emit('pipelineActionRequestComplete');
- },
- },
-};
-</script>
-<template>
- <div
- class="ci-job-component gl-display-flex gl-align-items-center gl-justify-content-space-between"
- data-qa-selector="job_item_container"
- >
- <gl-link
- v-if="hasDetails"
- v-gl-tooltip="{
- boundary: 'viewport',
- placement: 'bottom',
- customClass: 'gl-pointer-events-none',
- }"
- :href="detailsPath"
- :title="tooltipText"
- :class="jobClasses"
- class="js-pipeline-graph-job-link qa-job-link menu-item gl-text-gray-900 gl-active-text-decoration-none gl-focus-text-decoration-none gl-hover-text-decoration-none"
- data-testid="job-with-link"
- @click.stop="hideTooltips"
- @mouseout="hideTooltips"
- >
- <job-name-component :name="job.name" :status="job.status" :icon-size="24" />
- </gl-link>
-
- <div
- v-else
- v-gl-tooltip="{ boundary, placement: 'bottom', customClass: 'gl-pointer-events-none' }"
- :title="tooltipText"
- :class="jobClasses"
- class="js-job-component-tooltip non-details-job-component menu-item"
- data-testid="job-without-link"
- @mouseout="hideTooltips"
- >
- <job-name-component :name="job.name" :status="job.status" :icon-size="24" />
- </div>
-
- <action-component
- v-if="hasAction"
- :tooltip-text="status.action.title"
- :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/pipelines_list/pipeline_mini_graph.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue
deleted file mode 100644
index 05cb2ebb769..00000000000
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue
+++ /dev/null
@@ -1,54 +0,0 @@
-<script>
-import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue';
-/**
- * Renders the pipeline mini graph.
- */
-export default {
- components: {
- PipelineStage,
- },
- props: {
- stages: {
- type: Array,
- required: true,
- },
- updateDropdown: {
- type: Boolean,
- 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>
- <div data-testid="pipeline-mini-graph" class="gl-display-inline gl-vertical-align-middle">
- <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"
- >
- <pipeline-stage
- :stage="stage"
- :update-dropdown="updateDropdown"
- :is-merge-train="isMergeTrain"
- @pipelineActionRequestComplete="onPipelineActionRequestComplete"
- />
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_multi_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_multi_actions.vue
index 05a1ceface3..2d2f649f651 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_multi_actions.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_multi_actions.vue
@@ -10,6 +10,8 @@ import {
import fuzzaldrinPlus from 'fuzzaldrin-plus';
import axios from '~/lib/utils/axios_utils';
import { __, s__ } from '~/locale';
+import Tracking from '~/tracking';
+import { TRACKING_CATEGORIES } from '../../constants';
export const i18n = {
downloadArtifacts: __('Download artifacts'),
@@ -29,6 +31,7 @@ export default {
GlSearchBoxByType,
GlLoadingIcon,
},
+ mixins: [Tracking.mixin()],
inject: {
artifactsEndpoint: {
default: '',
@@ -60,6 +63,10 @@ export default {
},
methods: {
fetchArtifacts() {
+ // refactor tracking based on action once this dropdown supports
+ // actions other than artifacts
+ this.track('click_artifacts_dropdown', { label: TRACKING_CATEGORIES.table });
+
this.isLoading = true;
// Replace the placeholder with the ID of the pipeline we are viewing
const endpoint = this.artifactsEndpoint.replace(
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue
index 7a08dacb824..dd62ffb27f7 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_operations.vue
@@ -1,7 +1,8 @@
<script>
import { GlButton, GlTooltipDirective, GlModalDirective } from '@gitlab/ui';
+import Tracking from '~/tracking';
import eventHub from '../../event_hub';
-import { BUTTON_TOOLTIP_RETRY, BUTTON_TOOLTIP_CANCEL } from '../../constants';
+import { BUTTON_TOOLTIP_RETRY, BUTTON_TOOLTIP_CANCEL, TRACKING_CATEGORIES } from '../../constants';
import PipelineMultiActions from './pipeline_multi_actions.vue';
import PipelinesManualActions from './pipelines_manual_actions.vue';
@@ -17,6 +18,7 @@ export default {
PipelineMultiActions,
PipelinesManualActions,
},
+ mixins: [Tracking.mixin()],
props: {
pipeline: {
type: Object,
@@ -52,6 +54,7 @@ export default {
},
methods: {
handleCancelClick() {
+ this.trackClick('click_cancel_button');
eventHub.$emit('openConfirmationModal', {
pipeline: this.pipeline,
endpoint: this.pipeline.cancel_path,
@@ -59,8 +62,12 @@ export default {
},
handleRetryClick() {
this.isRetrying = true;
+ this.trackClick('click_retry_button');
eventHub.$emit('retryPipeline', this.pipeline.retry_path);
},
+ trackClick(action) {
+ this.track(action, { label: TRACKING_CATEGORIES.table });
+ },
},
};
</script>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue
deleted file mode 100644
index d7e55d36ff6..00000000000
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue
+++ /dev/null
@@ -1,182 +0,0 @@
-<script>
-/**
- * Renders each stage of the pipeline mini graph.
- *
- * Given the provided endpoint will make a request to
- * fetch the dropdown data when the stage is clicked.
- *
- * Request is made inside this component to make it reusable between:
- * 1. Pipelines main table
- * 2. Pipelines table in commit and Merge request views
- * 3. Merge request widget
- * 4. Commit widget
- */
-
-import { GlDropdown, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
-import CiIcon from '~/vue_shared/components/ci_icon.vue';
-import createFlash from '~/flash';
-import axios from '~/lib/utils/axios_utils';
-import { __, sprintf } from '~/locale';
-import eventHub from '../../event_hub';
-import JobItem from './job_item.vue';
-
-export default {
- i18n: {
- stage: __('Stage:'),
- loadingText: __('Loading, please wait.'),
- },
- dropdownPopperOpts: {
- placement: 'bottom',
- },
- components: {
- CiIcon,
- GlLoadingIcon,
- GlDropdown,
- JobItem,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- props: {
- stage: {
- type: Object,
- required: true,
- },
- updateDropdown: {
- type: Boolean,
- required: false,
- default: false,
- },
- isMergeTrain: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- data() {
- return {
- isDropdownOpen: false,
- isLoading: false,
- dropdownContent: [],
- stageName: '',
- };
- },
- watch: {
- updateDropdown() {
- if (this.updateDropdown && this.isDropdownOpen && !this.isLoading) {
- this.fetchJobs();
- }
- },
- },
- methods: {
- onHideDropdown() {
- this.isDropdownOpen = false;
- },
- onShowDropdown() {
- eventHub.$emit('clickedDropdown');
- this.isDropdownOpen = true;
- this.isLoading = true;
- this.fetchJobs();
- },
- fetchJobs() {
- axios
- .get(this.stage.dropdown_path)
- .then(({ data }) => {
- this.dropdownContent = data.latest_statuses;
- this.stageName = data.name;
- this.isLoading = false;
- })
- .catch(() => {
- this.$refs.dropdown.hide();
- this.isLoading = false;
-
- createFlash({
- message: __('Something went wrong on our end.'),
- });
- });
- },
- 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 });
- },
- },
-};
-</script>
-
-<template>
- <gl-dropdown
- ref="dropdown"
- v-gl-tooltip.hover.ds0
- v-gl-tooltip="stage.title"
- data-testid="mini-pipeline-graph-dropdown"
- variant="link"
- :aria-label="stageAriaLabel(stage.title)"
- :lazy="true"
- :popper-opts="$options.dropdownPopperOpts"
- :toggle-class="['gl-rounded-full!']"
- menu-class="mini-pipeline-graph-dropdown-menu"
- @hide="onHideDropdown"
- @show="onShowDropdown"
- >
- <template #button-content>
- <ci-icon
- is-borderless
- is-interactive
- css-classes="gl-rounded-full"
- :is-active="isDropdownOpen"
- :size="24"
- :status="stage.status"
- class="gl-align-items-center gl-border gl-display-inline-flex gl-z-index-1"
- />
- </template>
- <div
- v-if="isLoading"
- class="gl-display-flex gl-justify-content-center gl-p-2"
- data-testid="pipeline-stage-loading-state"
- >
- <gl-loading-icon size="sm" class="gl-mr-3" />
- <p class="gl-mb-0">{{ $options.i18n.loadingText }}</p>
- </div>
- <ul
- v-else
- class="js-builds-dropdown-list scrollable-menu"
- data-testid="mini-pipeline-graph-dropdown-menu-list"
- >
- <div
- class="gl-align-items-center gl-border-b gl-display-flex gl-font-weight-bold gl-justify-content-center gl-pb-3"
- >
- <span class="gl-mr-1">{{ $options.i18n.stage }}</span>
- <span data-testid="pipeline-stage-dropdown-menu-title">{{ stageName }}</span>
- </div>
- <li v-for="job in dropdownContent" :key="job.id">
- <job-item
- :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">
- <hr role="separator" aria-orientation="horizontal" class="dropdown-divider" />
- </li>
- <li>
- <div
- class="gl-display-flex gl-align-items-center"
- data-testid="warning-message-merge-trains"
- >
- <div class="menu-item gl-font-sm gl-text-gray-300!">
- {{ s__('Pipeline|Merge train pipeline jobs can not be retried') }}
- </div>
- </div>
- </li>
- </template>
- </ul>
- </gl-dropdown>
-</template>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stop_modal.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stop_modal.vue
index ef21673115e..eb70b5fbb7a 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stop_modal.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stop_modal.vue
@@ -83,9 +83,7 @@ export default {
<span class="font-weight-bold">{{ __('Pipeline') }}</span>
- <a :href="pipeline.path" class="js-pipeline-path link-commit qa-pipeline-path"
- >#{{ pipeline.id }}</a
- >
+ <a :href="pipeline.path" class="js-pipeline-path link-commit">#{{ pipeline.id }}</a>
<template v-if="hasRef">
{{ __('from') }}
<a :href="pipeline.ref.path" class="link-commit ref-name">{{ pipeline.ref.name }}</a>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue
index 09d588aaafd..39d41415456 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_url.vue
@@ -1,9 +1,10 @@
<script>
import { GlIcon, GlLink, GlTooltipDirective } from '@gitlab/ui';
import { __ } from '~/locale';
+import Tracking from '~/tracking';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
-import { ICONS } from '../../constants';
+import { ICONS, TRACKING_CATEGORIES } from '../../constants';
import PipelineLabels from './pipeline_labels.vue';
export default {
@@ -17,6 +18,7 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
+ mixins: [Tracking.mixin()],
props: {
pipeline: {
type: Object,
@@ -114,6 +116,11 @@ export default {
return this.pipeline?.commit?.title;
},
},
+ methods: {
+ trackClick(action) {
+ this.track(action, { label: TRACKING_CATEGORIES.table });
+ },
+ },
};
</script>
<template>
@@ -125,6 +132,7 @@ export default {
:href="commitUrl"
class="commit-row-message gl-text-gray-900"
data-testid="commit-title"
+ @click="trackClick('click_commit_title')"
>{{ commitTitle }}</gl-link
>
</tooltip-on-truncate>
@@ -137,6 +145,7 @@ export default {
class="gl-text-decoration-underline gl-text-blue-600! gl-mr-3"
data-testid="pipeline-url-link"
data-qa-selector="pipeline_url_link"
+ @click="trackClick('click_pipeline_id')"
>#{{ pipeline[pipelineKey] }}</gl-link
>
<!--Commit row-->
@@ -154,11 +163,17 @@ export default {
:href="mergeRequestRef.path"
class="ref-name gl-mr-3"
data-testid="merge-request-ref"
+ @click="trackClick('click_mr_ref')"
>{{ mergeRequestRef.iid }}</gl-link
>
- <gl-link v-else :href="refUrl" class="ref-name gl-mr-3" data-testid="commit-ref-name">{{
- commitRef.name
- }}</gl-link>
+ <gl-link
+ v-else
+ :href="refUrl"
+ class="ref-name gl-mr-3"
+ data-testid="commit-ref-name"
+ @click="trackClick('click_commit_name')"
+ >{{ commitRef.name }}</gl-link
+ >
</tooltip-on-truncate>
<gl-icon
v-gl-tooltip
@@ -167,9 +182,13 @@ export default {
:title="__('Commit')"
data-testid="commit-icon"
/>
- <gl-link :href="commitUrl" class="commit-sha mr-0" data-testid="commit-short-sha">{{
- commitShortSha
- }}</gl-link>
+ <gl-link
+ :href="commitUrl"
+ class="commit-sha mr-0"
+ data-testid="commit-short-sha"
+ @click="trackClick('click_commit_sha')"
+ >{{ commitShortSha }}</gl-link
+ >
<user-avatar-link
v-if="commitAuthor"
:link-href="commitAuthor.path"
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
index 485e338f639..f9022be888a 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines.vue
@@ -4,6 +4,7 @@ import { isEqual } from 'lodash';
import createFlash from '~/flash';
import { getParameterByName } from '~/lib/utils/url_utility';
import { __, s__ } from '~/locale';
+import Tracking from '~/tracking';
import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
import {
@@ -11,6 +12,7 @@ import {
RAW_TEXT_WARNING,
FILTER_TAG_IDENTIFIER,
PipelineKeyOptions,
+ TRACKING_CATEGORIES,
} from '../../constants';
import PipelinesMixin from '../../mixins/pipelines_mixin';
import PipelinesService from '../../services/pipelines_service';
@@ -35,7 +37,7 @@ export default {
PipelinesTableComponent,
TablePagination,
},
- mixins: [PipelinesMixin],
+ mixins: [PipelinesMixin, Tracking.mixin()],
props: {
store: {
type: Object,
@@ -246,6 +248,8 @@ export default {
params = this.onChangeWithFilter(params);
this.updateContent(params);
+
+ this.track('click_filter_tabs', { label: TRACKING_CATEGORIES.tabs });
},
successCallback(resp) {
// Because we are polling & the user is interacting verify if the response received
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 4d28545a035..af089aebbbe 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
@@ -2,7 +2,9 @@
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 { TRACKING_CATEGORIES } from '../../constants';
import PipelineBranchNameToken from './tokens/pipeline_branch_name_token.vue';
import PipelineSourceToken from './tokens/pipeline_source_token.vue';
import PipelineStatusToken from './tokens/pipeline_status_token.vue';
@@ -19,6 +21,7 @@ export default {
components: {
GlFilteredSearch,
},
+ mixins: [Tracking.mixin()],
props: {
projectId: {
type: String,
@@ -110,6 +113,7 @@ export default {
},
methods: {
onSubmit(filters) {
+ this.track('click_filtered_search', { label: TRACKING_CATEGORIES.search });
this.$emit('filterPipelines', filters);
},
},
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_manual_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_manual_actions.vue
index 47fffa8a6b2..16a747f6165 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_manual_actions.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_manual_actions.vue
@@ -4,8 +4,10 @@ import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
import { s__, __, sprintf } from '~/locale';
+import Tracking from '~/tracking';
import GlCountdown from '~/vue_shared/components/gl_countdown.vue';
import eventHub from '../../event_hub';
+import { TRACKING_CATEGORIES } from '../../constants';
export default {
directives: {
@@ -17,6 +19,7 @@ export default {
GlDropdownItem,
GlIcon,
},
+ mixins: [Tracking.mixin()],
props: {
actions: {
type: Array,
@@ -66,7 +69,6 @@ export default {
createFlash({ message: __('An error occurred while making the request.') });
});
},
-
isActionDisabled(action) {
if (action.playable === undefined) {
return false;
@@ -74,6 +76,9 @@ export default {
return !action.playable;
},
+ trackClick() {
+ this.track('click_manual_actions', { label: TRACKING_CATEGORIES.table });
+ },
},
};
</script>
@@ -86,6 +91,7 @@ export default {
right
lazy
icon="play"
+ @shown="trackClick"
>
<gl-dropdown-item
v-for="action in actions"
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_status_badge.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_status_badge.vue
index e765a8cd86c..936ae4da1ec 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_status_badge.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_status_badge.vue
@@ -1,6 +1,7 @@
<script>
-import { CHILD_VIEW } from '~/pipelines/constants';
+import { CHILD_VIEW, TRACKING_CATEGORIES } from '~/pipelines/constants';
import CiBadge from '~/vue_shared/components/ci_badge_link.vue';
+import Tracking from '~/tracking';
import PipelinesTimeago from './time_ago.vue';
export default {
@@ -8,6 +9,7 @@ export default {
CiBadge,
PipelinesTimeago,
},
+ mixins: [Tracking.mixin()],
props: {
pipeline: {
type: Object,
@@ -26,6 +28,11 @@ export default {
return this.viewType === CHILD_VIEW;
},
},
+ methods: {
+ trackClick() {
+ this.track('click_ci_status_badge', { label: TRACKING_CATEGORIES.table });
+ },
+ },
};
</script>
@@ -37,6 +44,7 @@ export default {
:show-text="!isChildView"
:icon-classes="'gl-vertical-align-middle!'"
data-qa-selector="pipeline_commit_status"
+ @ciStatusBadgeClick="trackClick"
/>
<pipelines-timeago class="gl-mt-3" :pipeline="pipeline" />
</div>
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 53da98434b0..f6e46c090d3 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
@@ -1,8 +1,10 @@
<script>
import { GlTableLite, GlTooltipDirective } from '@gitlab/ui';
import { s__, __ } from '~/locale';
+import Tracking from '~/tracking';
+import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
import eventHub from '../../event_hub';
-import PipelineMiniGraph from './pipeline_mini_graph.vue';
+import { TRACKING_CATEGORIES } from '../../constants';
import PipelineOperations from './pipeline_operations.vue';
import PipelineStopModal from './pipeline_stop_modal.vue';
import PipelineTriggerer from './pipeline_triggerer.vue';
@@ -17,8 +19,6 @@ const DEFAULT_TH_CLASSES =
export default {
components: {
GlTableLite,
- LinkedPipelinesMiniList: () =>
- import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'),
PipelineMiniGraph,
PipelineOperations,
PipelinesStatusBadge,
@@ -70,6 +70,7 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
+ mixins: [Tracking.mixin()],
props: {
pipelines: {
type: Array,
@@ -126,6 +127,9 @@ export default {
onPipelineActionRequestComplete() {
eventHub.$emit('refreshPipelinesTable');
},
+ trackPipelineMiniGraph() {
+ this.track('click_minigraph', { label: TRACKING_CATEGORIES.table });
+ },
},
TBODY_TR_ATTR: {
'data-testid': 'pipeline-table-row',
@@ -169,29 +173,15 @@ export default {
</template>
<template #cell(stages)="{ item }">
- <div class="stage-cell">
- <!-- This empty div should be removed, see https://gitlab.com/gitlab-org/gitlab/-/issues/323488 -->
- <div></div>
- <linked-pipelines-mini-list
- v-if="item.triggered_by"
- :triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
- item.triggered_by,
- ] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
- data-testid="mini-graph-upstream"
- />
- <pipeline-mini-graph
- v-if="item.details && item.details.stages && item.details.stages.length > 0"
- :stages="item.details.stages"
- :update-dropdown="updateGraphDropdown"
- @pipelineActionRequestComplete="onPipelineActionRequestComplete"
- />
- <linked-pipelines-mini-list
- v-if="item.triggered.length"
- :triggered="item.triggered"
- :pipeline-path="item.path"
- data-testid="mini-graph-downstream"
- />
- </div>
+ <pipeline-mini-graph
+ :downstream-pipelines="item.triggered"
+ :pipeline-path="item.path"
+ :stages="item.details.stages"
+ :update-dropdown="updateGraphDropdown"
+ :upstream-pipeline="item.triggered_by"
+ @pipelineActionRequestComplete="onPipelineActionRequestComplete"
+ @miniGraphStageClick="trackPipelineMiniGraph"
+ />
</template>
<template #cell(actions)="{ item }">
diff --git a/app/assets/javascripts/pipelines/constants.js b/app/assets/javascripts/pipelines/constants.js
index 7b38f870cb6..327633dcb1a 100644
--- a/app/assets/javascripts/pipelines/constants.js
+++ b/app/assets/javascripts/pipelines/constants.js
@@ -110,4 +110,8 @@ export const DEFAULT_FIELDS = [
},
];
-export const performanceModalId = 'performanceInsightsModal';
+export const TRACKING_CATEGORIES = {
+ table: 'pipelines_table_component',
+ tabs: 'pipelines_filter_tabs',
+ search: 'pipelines_filtered_search',
+};
diff --git a/app/assets/javascripts/pipelines/graphql/queries/get_performance_insights.query.graphql b/app/assets/javascripts/pipelines/graphql/queries/get_performance_insights.query.graphql
deleted file mode 100644
index 25e990c8934..00000000000
--- a/app/assets/javascripts/pipelines/graphql/queries/get_performance_insights.query.graphql
+++ /dev/null
@@ -1,28 +0,0 @@
-query getPerformanceInsightsData($fullPath: ID!, $iid: ID!) {
- project(fullPath: $fullPath) {
- id
- pipeline(iid: $iid) {
- id
- jobs {
- pageInfo {
- hasNextPage
- }
- nodes {
- id
- duration
- detailedStatus {
- id
- detailsPath
- }
- name
- stage {
- id
- name
- }
- startedAt
- queuedDuration
- }
- }
- }
- }
-}
diff --git a/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_jobs.query.graphql b/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_jobs.query.graphql
index 641ec7a3cf6..b0f875160d4 100644
--- a/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_jobs.query.graphql
+++ b/app/assets/javascripts/pipelines/graphql/queries/get_pipeline_jobs.query.graphql
@@ -11,6 +11,7 @@ query getPipelineJobs($fullPath: ID!, $iid: ID!, $after: String) {
}
nodes {
artifacts {
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
nodes {
downloadPath
fileType
diff --git a/app/assets/javascripts/pipelines/pipeline_details_header.js b/app/assets/javascripts/pipelines/pipeline_details_header.js
index 2fedd7e7a98..c9e60756407 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_header.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_header.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
-import pipelineHeader from './components/header_component.vue';
+import PipelineHeader from './components/header_component.vue';
Vue.use(VueApollo);
@@ -16,7 +16,7 @@ export const createPipelineHeaderApp = (elSelector, apolloProvider, graphqlResou
new Vue({
el,
components: {
- pipelineHeader,
+ PipelineHeader,
},
apolloProvider,
provide: {
diff --git a/app/assets/javascripts/pipelines/pipeline_tabs.js b/app/assets/javascripts/pipelines/pipeline_tabs.js
index 7051d356089..508f188c229 100644
--- a/app/assets/javascripts/pipelines/pipeline_tabs.js
+++ b/app/assets/javascripts/pipelines/pipeline_tabs.js
@@ -20,6 +20,8 @@ export const createAppOptions = (selector, apolloProvider) => {
const {
canGenerateCodequalityReports,
codequalityReportDownloadPath,
+ codequalityBlobPath,
+ codequalityProjectPath,
downloadablePathForReportType,
exposeSecurityDashboard,
exposeLicenseScanningData,
@@ -40,9 +42,12 @@ export const createAppOptions = (selector, apolloProvider) => {
hasTestReport,
emptyStateImagePath,
artifactsExpiredImagePath,
+ isFullCodequalityReportAvailable,
testsCount,
} = dataset;
+ // TODO remove projectPath variable once https://gitlab.com/gitlab-org/gitlab/-/issues/371641 is resolved
+ const projectPath = fullPath;
const defaultTabValue = getPipelineDefaultTab(window.location.href);
return {
@@ -63,6 +68,10 @@ export const createAppOptions = (selector, apolloProvider) => {
provide: {
canGenerateCodequalityReports: parseBoolean(canGenerateCodequalityReports),
codequalityReportDownloadPath,
+ codequalityBlobPath,
+ codequalityProjectPath,
+ isFullCodequalityReportAvailable: parseBoolean(isFullCodequalityReportAvailable),
+ projectPath,
defaultTabValue,
downloadablePathForReportType,
exposeSecurityDashboard: parseBoolean(exposeSecurityDashboard),
diff --git a/app/assets/javascripts/pipelines/utils.js b/app/assets/javascripts/pipelines/utils.js
index 83e00b80426..588d15495ab 100644
--- a/app/assets/javascripts/pipelines/utils.js
+++ b/app/assets/javascripts/pipelines/utils.js
@@ -153,24 +153,3 @@ export const getPipelineDefaultTab = (url) => {
return null;
};
-
-export const calculateJobStats = (jobs, sortField) => {
- const jobNodes = [...jobs.nodes];
-
- const sorted = jobNodes.sort((a, b) => {
- return b[sortField] - a[sortField];
- });
-
- return sorted[0];
-};
-
-export const calculateSlowestFiveJobs = (jobs) => {
- const jobNodes = [...jobs.nodes];
- const limit = 5;
-
- return jobNodes
- .sort((a, b) => {
- return b.duration - a.duration;
- })
- .slice(0, limit);
-};
diff --git a/app/assets/javascripts/profile/account/index.js b/app/assets/javascripts/profile/account/index.js
index f208280af27..2d31cf772e3 100644
--- a/app/assets/javascripts/profile/account/index.js
+++ b/app/assets/javascripts/profile/account/index.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import { BV_SHOW_MODAL } from '~/lib/utils/constants';
import Translate from '~/vue_shared/translate';
-import deleteAccountModal from './components/delete_account_modal.vue';
+import DeleteAccountModal from './components/delete_account_modal.vue';
import UpdateUsername from './components/update_username.vue';
export default () => {
@@ -27,7 +27,7 @@ export default () => {
new Vue({
el: deleteAccountModalEl,
components: {
- deleteAccountModal,
+ DeleteAccountModal,
},
mounted() {
deleteAccountButton.disabled = false;
diff --git a/app/assets/javascripts/profile/profile.js b/app/assets/javascripts/profile/profile.js
index 064bcf8e4c4..af5beeb686c 100644
--- a/app/assets/javascripts/profile/profile.js
+++ b/app/assets/javascripts/profile/profile.js
@@ -1,11 +1,14 @@
import $ from 'jquery';
+import Vue from 'vue';
import { VARIANT_DANGER, VARIANT_INFO, createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { parseBoolean } from '~/lib/utils/common_utils';
+import { parseRailsFormFields } from '~/lib/utils/forms';
import { Rails } from '~/lib/utils/rails_ujs';
import TimezoneDropdown, {
formatTimezone,
} from '~/pages/projects/pipeline_schedules/shared/components/timezone_dropdown';
+import UserProfileSetStatusWrapper from '~/set_status_modal/user_profile_set_status_wrapper.vue';
export default class Profile {
constructor({ form } = {}) {
@@ -116,3 +119,24 @@ export default class Profile {
}
}
}
+
+export const initSetStatusForm = () => {
+ const el = document.getElementById('js-user-profile-set-status-form');
+
+ if (!el) {
+ return null;
+ }
+
+ const fields = parseRailsFormFields(el);
+
+ return new Vue({
+ el,
+ name: 'UserProfileStatusForm',
+ provide: {
+ fields,
+ },
+ render(h) {
+ return h(UserProfileSetStatusWrapper);
+ },
+ });
+};
diff --git a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
index 1cdf26b76b7..4505dd1f85c 100644
--- a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
+++ b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
@@ -2,11 +2,11 @@
import { GlLoadingIcon } from '@gitlab/ui';
import createFlash from '~/flash';
import { __ } from '~/locale';
-import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue';
import {
getQueryHeaders,
toggleQueryPollingByVisibility,
} from '~/pipelines/components/graph/utils';
+import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
import { formatStages } from '../utils';
import getLinkedPipelinesQuery from '../graphql/queries/get_linked_pipelines.query.graphql';
import getPipelineStagesQuery from '../graphql/queries/get_pipeline_stages.query.graphql';
@@ -21,8 +21,6 @@ export default {
components: {
GlLoadingIcon,
PipelineMiniGraph,
- LinkedPipelinesMiniList: () =>
- import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'),
},
inject: {
fullPath: {
@@ -92,12 +90,12 @@ export default {
};
},
computed: {
- hasDownstream() {
- return this.pipeline?.downstream?.nodes.length > 0;
- },
downstreamPipelines() {
return this.pipeline?.downstream?.nodes;
},
+ pipelinePath() {
+ return this.pipeline?.path ?? '';
+ },
upstreamPipeline() {
return this.pipeline?.upstream;
},
@@ -128,23 +126,13 @@ export default {
<template>
<div class="gl-pt-2">
<gl-loading-icon v-if="$apollo.queries.pipeline.loading" />
- <div v-else class="gl-align-items-center gl-display-flex">
- <linked-pipelines-mini-list
- v-if="upstreamPipeline"
- :triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
- upstreamPipeline,
- ] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
- data-testid="commit-box-mini-graph-upstream"
- />
-
- <pipeline-mini-graph :stages="formattedStages" data-testid="commit-box-mini-graph" />
-
- <linked-pipelines-mini-list
- v-if="hasDownstream"
- :triggered="downstreamPipelines"
- :pipeline-path="pipeline.path"
- data-testid="commit-box-mini-graph-downstream"
- />
- </div>
+ <pipeline-mini-graph
+ v-else
+ data-testid="commit-box-pipeline-mini-graph"
+ :downstream-pipelines="downstreamPipelines"
+ :pipeline-path="pipelinePath"
+ :stages="formattedStages"
+ :upstream-pipeline="upstreamPipeline"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue b/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue
index ecd2288eb2f..06d96ef7bef 100644
--- a/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue
+++ b/app/assets/javascripts/projects/pipelines/charts/components/pipeline_charts.vue
@@ -1,7 +1,7 @@
<script>
import { GlAlert, GlSkeletonLoader } from '@gitlab/ui';
import { GlColumnChart } from '@gitlab/ui/dist/charts';
-import dateFormat from 'dateformat';
+import dateFormat from '~/lib/dateformat';
import { getDateInPast } from '~/lib/utils/datetime_utility';
import { __, s__, sprintf } from '~/locale';
import CiCdAnalyticsCharts from '~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue';
diff --git a/app/assets/javascripts/projects/project_visibility.js b/app/assets/javascripts/projects/project_visibility.js
index b8ac17a01f2..84b8936c17f 100644
--- a/app/assets/javascripts/projects/project_visibility.js
+++ b/app/assets/javascripts/projects/project_visibility.js
@@ -1,13 +1,7 @@
import { escape } from 'lodash';
import { __, sprintf } from '~/locale';
import eventHub from '~/projects/new/event_hub';
-
-// Values are from lib/gitlab/visibility_level.rb
-const visibilityLevel = {
- private: 0,
- internal: 10,
- public: 20,
-};
+import { VISIBILITY_LEVELS_STRING_TO_INTEGER } from '~/visibility_level/constants';
function setVisibilityOptions({ name, visibility, showPath, editPath }) {
document.querySelectorAll('.visibility-level-setting .gl-form-radio').forEach((option) => {
@@ -19,13 +13,14 @@ function setVisibilityOptions({ name, visibility, showPath, editPath }) {
const optionInput = option.querySelector('input[type=radio]');
const optionValue = optionInput ? parseInt(optionInput.value, 10) : 0;
- if (visibilityLevel[visibility] < optionValue) {
+ if (VISIBILITY_LEVELS_STRING_TO_INTEGER[visibility] < optionValue) {
option.classList.add('disabled');
optionInput.disabled = true;
const reason = option.querySelector('.option-disabled-reason');
if (reason) {
const optionTitle = option.querySelector('.js-visibility-level-radio span');
const optionName = optionTitle ? optionTitle.innerText.toLowerCase() : '';
+ // eslint-disable-next-line no-unsanitized/property
reason.innerHTML = sprintf(
__(
'This project cannot be %{visibilityLevel} because the visibility of %{openShowLink}%{name}%{closeShowLink} is %{visibility}. To make this project %{visibilityLevel}, you must first %{openEditLink}change the visibility%{closeEditLink} of the parent group.',
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 ada951f6867..e8eaf0a70b2 100644
--- a/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue
+++ b/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue
@@ -1,16 +1,58 @@
<script>
-import { __ } from '~/locale';
+import { s__ } from '~/locale';
+import createFlash from '~/flash';
+import branchRulesQuery from './graphql/queries/branch_rules.query.graphql';
+import BranchRule from './components/branch_rule.vue';
+
+export const i18n = {
+ queryError: s__(
+ 'ProtectedBranch|An error occurred while loading branch rules. Please try again.',
+ ),
+ emptyState: s__(
+ 'ProtectedBranch|Protected branches, merge request approvals, and status checks will appear here once configured.',
+ ),
+};
export default {
name: 'BranchRules',
- i18n: { heading: __('Branch') },
+ i18n,
+ components: {
+ BranchRule,
+ },
+ apollo: {
+ branchRules: {
+ query: branchRulesQuery,
+ variables() {
+ return {
+ projectPath: this.projectPath,
+ };
+ },
+ update(data) {
+ return data.project?.branchRules?.nodes || [];
+ },
+ error() {
+ createFlash({ message: this.$options.i18n.queryError });
+ },
+ },
+ },
+ props: {
+ projectPath: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ branchRules: [],
+ };
+ },
};
</script>
<template>
- <div>
- <strong>{{ $options.i18n.heading }}</strong>
+ <div class="settings-content">
+ <branch-rule v-for="rule in branchRules" :key="rule.name" :name="rule.name" />
- <!-- TODO - List branch rules (https://gitlab.com/gitlab-org/gitlab/-/issues/362217) -->
+ <span v-if="!branchRules.length" data-testid="empty">{{ $options.i18n.emptyState }}</span>
</div>
</template>
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
new file mode 100644
index 00000000000..68750318029
--- /dev/null
+++ b/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue
@@ -0,0 +1,61 @@
+<script>
+import { GlBadge } from '@gitlab/ui';
+import { s__ } from '~/locale';
+
+export const i18n = {
+ defaultLabel: s__('BranchRules|default'),
+ protectedLabel: s__('BranchRules|protected'),
+};
+
+export default {
+ name: 'BranchRule',
+ i18n,
+ components: {
+ GlBadge,
+ },
+ props: {
+ name: {
+ type: String,
+ required: true,
+ },
+ isDefault: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ isProtected: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ approvalDetails: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ },
+ computed: {
+ hasApprovalDetails() {
+ return this.approvalDetails && this.approvalDetails.length;
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="gl-border-b gl-pt-5 gl-pb-5">
+ <strong class="gl-font-monospace">{{ name }}</strong>
+
+ <gl-badge v-if="isDefault" variant="info" size="sm" class="gl-ml-2">{{
+ $options.i18n.defaultLabel
+ }}</gl-badge>
+
+ <gl-badge v-if="isProtected" variant="success" size="sm" class="gl-ml-2">{{
+ $options.i18n.protectedLabel
+ }}</gl-badge>
+
+ <ul v-if="hasApprovalDetails" class="gl-pl-6 gl-mt-2 gl-mb-0 gl-text-gray-500">
+ <li v-for="(detail, index) in approvalDetails" :key="index">{{ detail }}</li>
+ </ul>
+ </div>
+</template>
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
new file mode 100644
index 00000000000..104a0c25a80
--- /dev/null
+++ b/app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql
@@ -0,0 +1,10 @@
+query getBranchRules($projectPath: ID!) {
+ project(fullPath: $projectPath) {
+ id
+ branchRules {
+ nodes {
+ name
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/projects/settings/repository/branch_rules/mount_branch_rules.js b/app/assets/javascripts/projects/settings/repository/branch_rules/mount_branch_rules.js
index abe0b93081e..35322e2e466 100644
--- a/app/assets/javascripts/projects/settings/repository/branch_rules/mount_branch_rules.js
+++ b/app/assets/javascripts/projects/settings/repository/branch_rules/mount_branch_rules.js
@@ -1,13 +1,28 @@
import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
import BranchRulesApp from '~/projects/settings/repository/branch_rules/app.vue';
+Vue.use(VueApollo);
+
+const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient(),
+});
+
export default function mountBranchRules(el) {
if (!el) return null;
+ const { projectPath } = el.dataset;
+
return new Vue({
el,
+ apolloProvider,
render(createElement) {
- return createElement(BranchRulesApp);
+ return createElement(BranchRulesApp, {
+ props: {
+ projectPath,
+ },
+ });
},
});
}
diff --git a/app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue b/app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue
index 9c8de9bef2d..3d553e71f71 100644
--- a/app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue
+++ b/app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue
@@ -2,7 +2,7 @@
import { GlTokenSelector, GlAvatarLabeled } from '@gitlab/ui';
import { s__ } from '~/locale';
import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
-import searchProjectTopics from '../queries/project_topics_search.query.graphql';
+import searchProjectTopics from '~/graphql_shared/queries/project_topics_search.query.graphql';
export default {
components: {
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 14c8c53dd19..71ff3e892b1 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,12 +1,18 @@
<script>
-import { GlAlert, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlAlert, GlSprintf, GlLink, GlSafeHtmlDirective } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils';
+import { helpPagePath } from '~/helpers/help_page_helper';
import { __, sprintf } from '~/locale';
import ServiceDeskSetting from './service_desk_setting.vue';
export default {
+ customEmailHelpPath: helpPagePath('/user/project/service_desk.html', {
+ anchor: 'using-a-custom-email-address',
+ }),
components: {
GlAlert,
+ GlSprintf,
+ GlLink,
ServiceDeskSetting,
},
directives: {
@@ -43,6 +49,9 @@ export default {
templates: {
default: [],
},
+ publicProject: {
+ default: false,
+ },
},
data() {
return {
@@ -127,6 +136,27 @@ export default {
<template>
<div>
+ <gl-alert
+ v-if="publicProject && isEnabled"
+ class="mb-3"
+ variant="warning"
+ data-testid="public-project-alert"
+ :dismissible="false"
+ >
+ <gl-sprintf
+ :message="
+ __(
+ '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}',
+ )
+ "
+ >
+ <template #link="{ content }">
+ <gl-link :href="$options.customEmailHelpPath" target="_blank">
+ {{ content }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </gl-alert>
<gl-alert v-if="isAlertShowing" class="mb-3" :variant="alertVariant" @dismiss="onDismiss">
<span v-safe-html="alertMessage"></span>
</gl-alert>
diff --git a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue
index 8a9a0b541f3..452e7a4fd21 100644
--- a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue
+++ b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue
@@ -176,7 +176,7 @@ export default {
</template>
</gl-form-input-group>
<template v-if="email && hasCustomEmail" #description>
- <span class="gl-mt-2 d-inline-block">
+ <span class="gl-mt-2 gl-display-inline-block">
<gl-sprintf :message="__('Emails sent to %{email} are also supported.')">
<template #email>
<code>{{ incomingEmail }}</code>
@@ -190,7 +190,11 @@ export default {
</template>
</gl-form-group>
- <gl-form-group :label="__('Email address suffix')" :state="!projectKeyError">
+ <gl-form-group
+ :label="__('Email address suffix')"
+ :state="!projectKeyError"
+ data-testid="suffix-form-group"
+ >
<gl-form-input
v-if="hasProjectKeySupport"
id="service-desk-project-suffix"
@@ -216,22 +220,24 @@ export default {
</gl-sprintf>
</template>
<template v-else #description>
- <gl-sprintf
- :message="
- __(
- 'To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}',
- )
- "
- >
- <template #link="{ content }">
- <gl-link
- :href="customEmailAddressHelpUrl"
- target="_blank"
- class="gl-text-blue-600 font-size-inherit"
- >{{ content }}
- </gl-link>
- </template>
- </gl-sprintf>
+ <span class="gl-text-gray-900">
+ <gl-sprintf
+ :message="
+ __(
+ 'To add a custom suffix, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}',
+ )
+ "
+ >
+ <template #link="{ content }">
+ <gl-link
+ :href="customEmailAddressHelpUrl"
+ target="_blank"
+ class="gl-text-blue-600 font-size-inherit"
+ >{{ content }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </span>
</template>
<template v-if="hasProjectKeySupport && projectKeyError" #invalid-feedback>
@@ -266,7 +272,27 @@ export default {
/>
<template v-if="hasProjectKeySupport" #description>
- {{ __('Emails sent from Service Desk have this name.') }}
+ {{ __('Name to be used as the sender for emails from Service Desk.') }}
+ </template>
+ <template v-else #description>
+ <span class="gl-text-gray-900">
+ <gl-sprintf
+ :message="
+ __(
+ 'To add display name, set up a Service Desk email address. %{linkStart}Learn more.%{linkEnd}',
+ )
+ "
+ >
+ <template #link="{ content }">
+ <gl-link
+ :href="customEmailAddressHelpUrl"
+ target="_blank"
+ class="gl-text-blue-600 font-size-inherit"
+ >{{ content }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </span>
</template>
</gl-form-group>
diff --git a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_template_dropdown.vue b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_template_dropdown.vue
index bdd9f940d79..315f0743b53 100644
--- a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_template_dropdown.vue
+++ b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_template_dropdown.vue
@@ -100,7 +100,7 @@ export default {
<gl-dropdown-item
v-for="template in item"
:key="template.key"
- :is-check-item="true"
+ is-check-item
:is-checked="
template.project_id === selectedFileTemplateProjectId &&
template.name === selectedTemplate
diff --git a/app/assets/javascripts/projects/settings_service_desk/index.js b/app/assets/javascripts/projects/settings_service_desk/index.js
index e14cdee17ce..26435a5fac9 100644
--- a/app/assets/javascripts/projects/settings_service_desk/index.js
+++ b/app/assets/javascripts/projects/settings_service_desk/index.js
@@ -20,6 +20,7 @@ export default () => {
selectedTemplate,
selectedFileTemplateProjectId,
templates,
+ publicProject,
} = el.dataset;
return new Vue({
@@ -35,6 +36,7 @@ export default () => {
selectedTemplate,
selectedFileTemplateProjectId: parseInt(selectedFileTemplateProjectId, 10) || null,
templates: JSON.parse(templates),
+ publicProject: parseBoolean(publicProject),
},
render: (createElement) => createElement(ServiceDeskRoot),
});
diff --git a/app/assets/javascripts/projects/star.js b/app/assets/javascripts/projects/star.js
index 5bbace11b15..e063064663b 100644
--- a/app/assets/javascripts/projects/star.js
+++ b/app/assets/javascripts/projects/star.js
@@ -22,11 +22,14 @@ export default class Star {
starSpan.classList.remove('starred');
starSpan.textContent = s__('StarProject|Star');
starIcon.remove();
+ // eslint-disable-next-line no-unsanitized/method
starSpan.insertAdjacentHTML('beforebegin', spriteIcon('star-o', iconClasses));
} else {
starSpan.classList.add('starred');
starSpan.textContent = __('Unstar');
starIcon.remove();
+
+ // eslint-disable-next-line no-unsanitized/method
starSpan.insertAdjacentHTML('beforebegin', spriteIcon('star', iconClasses));
}
})
diff --git a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
index a79da00de43..6b14ebadacc 100644
--- a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
+++ b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
@@ -4,7 +4,7 @@ import Visibility from 'visibilityjs';
import createFlash from '~/flash';
import Poll from '~/lib/utils/poll';
import { __, s__, sprintf } from '~/locale';
-import ciIcon from '~/vue_shared/components/ci_icon.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
import CommitPipelineService from '../services/commit_pipeline_service';
export default {
@@ -12,7 +12,7 @@ export default {
GlTooltip: GlTooltipDirective,
},
components: {
- ciIcon,
+ CiIcon,
GlLoadingIcon,
},
props: {
diff --git a/app/assets/javascripts/related_issues/components/related_issuable_input.vue b/app/assets/javascripts/related_issues/components/related_issuable_input.vue
index 270d4632a54..09ecad2d90e 100644
--- a/app/assets/javascripts/related_issues/components/related_issuable_input.vue
+++ b/app/assets/javascripts/related_issues/components/related_issuable_input.vue
@@ -7,14 +7,14 @@ import {
inputPlaceholderTextMap,
issuableTypesMap,
} from '../constants';
-import issueToken from './issue_token.vue';
+import IssueToken from './issue_token.vue';
const SPACE_FACTOR = 1;
export default {
name: 'RelatedIssuableInput',
components: {
- issueToken,
+ IssueToken,
},
props: {
inputId: {
diff --git a/app/assets/javascripts/related_issues/components/related_issues_block.vue b/app/assets/javascripts/related_issues/components/related_issues_block.vue
index 5b4a6d1fe0d..53f2dbbbbd7 100644
--- a/app/assets/javascripts/related_issues/components/related_issues_block.vue
+++ b/app/assets/javascripts/related_issues/components/related_issues_block.vue
@@ -3,7 +3,6 @@ import { GlLink, GlIcon, GlButton } from '@gitlab/ui';
import { __ } from '~/locale';
import {
issuableIconMap,
- issuableQaClassMap,
linkedIssueTypesMap,
linkedIssueTypesTextMap,
issuablesBlockHeaderTextMap,
@@ -142,9 +141,6 @@ export default {
issuableTypeIcon() {
return issuableIconMap[this.issuableType];
},
- qaClass() {
- return issuableQaClassMap[this.issuableType];
- },
toggleIcon() {
return this.isOpen ? 'chevron-lg-up' : 'chevron-lg-down';
},
@@ -166,11 +162,15 @@ export default {
</script>
<template>
- <div id="related-issues" class="related-issues-block gl-mt-5">
- <div class="card card-slim gl-overflow-hidden">
+ <div id="related-issues" class="related-issues-block">
+ <div class="card card-slim gl-overflow-hidden gl-mt-5 gl-mb-0">
<div
- :class="{ 'panel-empty-heading border-bottom-0': !hasBody, 'gl-border-b-0': !isOpen }"
- class="gl-display-flex gl-justify-content-space-between gl-line-height-24 gl-py-3 gl-px-5 gl-bg-gray-10 gl-border-b-1 gl-border-b-solid gl-border-b-gray-100"
+ :class="{
+ 'panel-empty-heading border-bottom-0': !hasBody,
+ 'gl-border-b-1': isOpen,
+ 'gl-border-b-0': !isOpen,
+ }"
+ class="gl-display-flex gl-justify-content-space-between gl-line-height-24 gl-py-3 gl-px-5 gl-bg-gray-10 gl-border-b-solid gl-border-b-gray-100"
>
<h3 class="card-title h5 gl-my-0 gl-display-flex gl-align-items-center gl-flex-grow-1">
<gl-link
@@ -205,7 +205,6 @@ export default {
data-qa-selector="related_issues_plus_button"
data-testid="related-issues-plus-button"
:aria-label="addIssuableButtonText"
- :class="qaClass"
class="gl-ml-3"
@click="addButtonClick"
>
diff --git a/app/assets/javascripts/related_issues/components/related_issues_root.vue b/app/assets/javascripts/related_issues/components/related_issues_root.vue
index cad5037d7e4..ae40232df6f 100644
--- a/app/assets/javascripts/related_issues/components/related_issues_root.vue
+++ b/app/assets/javascripts/related_issues/components/related_issues_root.vue
@@ -40,7 +40,7 @@ import RelatedIssuesBlock from './related_issues_block.vue';
export default {
name: 'RelatedIssuesRoot',
components: {
- relatedIssuesBlock: RelatedIssuesBlock,
+ RelatedIssuesBlock,
},
props: {
endpoint: {
diff --git a/app/assets/javascripts/related_issues/constants.js b/app/assets/javascripts/related_issues/constants.js
index 23ea93cd258..4eb054ccb5c 100644
--- a/app/assets/javascripts/related_issues/constants.js
+++ b/app/assets/javascripts/related_issues/constants.js
@@ -99,15 +99,6 @@ export const issuableIconMap = {
[issuableTypesMap.EPIC]: 'epic',
};
-/**
- * These are used to map issuableType to the correct QA class.
- * Since these are never used for any display purposes, don't wrap
- * them inside i18n functions.
- */
-export const issuableQaClassMap = {
- [issuableTypesMap.EPIC]: 'qa-add-epics-button',
-};
-
export const PathIdSeparator = {
Epic: '&',
Issue: '#',
diff --git a/app/assets/javascripts/releases/components/evidence_block.vue b/app/assets/javascripts/releases/components/evidence_block.vue
index 78831ceefe9..6d415471b14 100644
--- a/app/assets/javascripts/releases/components/evidence_block.vue
+++ b/app/assets/javascripts/releases/components/evidence_block.vue
@@ -1,6 +1,6 @@
<script>
import { GlLink, GlTooltipDirective, GlIcon } from '@gitlab/ui';
-import dateFormat from 'dateformat';
+import dateFormat from '~/lib/dateformat';
import { getTimeago } from '~/lib/utils/datetime_utility';
import { truncateSha } from '~/lib/utils/text_utility';
import { __, sprintf } from '~/locale';
diff --git a/app/assets/javascripts/releases/stores/modules/edit_new/actions.js b/app/assets/javascripts/releases/stores/modules/edit_new/actions.js
index a71a8125d65..669e5928143 100644
--- a/app/assets/javascripts/releases/stores/modules/edit_new/actions.js
+++ b/app/assets/javascripts/releases/stores/modules/edit_new/actions.js
@@ -16,6 +16,8 @@ import {
import * as types from './mutation_types';
+class GraphQLError extends Error {}
+
export const initializeRelease = ({ commit, dispatch, state }) => {
if (state.isExistingRelease) {
// When editing an existing release,
@@ -110,35 +112,35 @@ export const saveRelease = ({ commit, dispatch, state }) => {
*
* @param {Object} gqlResponse The response object returned by the GraphQL client
* @param {String} mutationName The name of the mutation that was executed
- * @param {String} messageIfError An message to build into the error object if something went wrong
*/
-const checkForErrorsAsData = (gqlResponse, mutationName, messageIfError) => {
+const checkForErrorsAsData = (gqlResponse, mutationName) => {
const allErrors = gqlResponse.data[mutationName].errors;
if (allErrors.length > 0) {
- const allErrorMessages = JSON.stringify(allErrors);
- throw new Error(`${messageIfError}: ${allErrorMessages}`);
+ throw new GraphQLError(allErrors[0]);
}
};
-export const createRelease = async ({ commit, dispatch, state, getters }) => {
+export const createRelease = async ({ commit, dispatch, getters }) => {
try {
const response = await gqClient.mutate({
mutation: createReleaseMutation,
variables: getters.releaseCreateMutatationVariables,
});
- checkForErrorsAsData(
- response,
- 'releaseCreate',
- `Something went wrong while creating a new release with projectPath "${state.projectPath}" and tagName "${state.release.tagName}"`,
- );
+ checkForErrorsAsData(response, 'releaseCreate');
dispatch('receiveSaveReleaseSuccess', response.data.releaseCreate.release.links.selfUrl);
} catch (error) {
commit(types.RECEIVE_SAVE_RELEASE_ERROR, error);
- createFlash({
- message: s__('Release|Something went wrong while creating a new release.'),
- });
+ if (error instanceof GraphQLError) {
+ createFlash({
+ message: error.message,
+ });
+ } else {
+ createFlash({
+ message: s__('Release|Something went wrong while creating a new release.'),
+ });
+ }
}
};
@@ -146,7 +148,7 @@ export const createRelease = async ({ commit, dispatch, state, getters }) => {
* Deletes a single release link.
* Throws an error if any network or validation errors occur.
*/
-const deleteReleaseLinks = async ({ state, id }) => {
+const deleteReleaseLinks = async ({ id }) => {
const deleteResponse = await gqClient.mutate({
mutation: deleteReleaseAssetLinkMutation,
variables: {
@@ -154,11 +156,7 @@ const deleteReleaseLinks = async ({ state, id }) => {
},
});
- checkForErrorsAsData(
- deleteResponse,
- 'releaseAssetLinkDelete',
- `Something went wrong while deleting release asset link for release with projectPath "${state.projectPath}", tagName "${state.tagName}", and link id "${id}"`,
- );
+ checkForErrorsAsData(deleteResponse, 'releaseAssetLinkDelete');
};
/**
@@ -180,11 +178,7 @@ const createReleaseLink = async ({ state, link }) => {
},
});
- checkForErrorsAsData(
- createResponse,
- 'releaseAssetLinkCreate',
- `Something went wrong while creating a release asset link for release with projectPath "${state.projectPath}" and tagName "${state.tagName}"`,
- );
+ checkForErrorsAsData(createResponse, 'releaseAssetLinkCreate');
};
export const updateRelease = async ({ commit, dispatch, state, getters }) => {
@@ -210,11 +204,7 @@ export const updateRelease = async ({ commit, dispatch, state, getters }) => {
variables: getters.releaseUpdateMutatationVariables,
});
- checkForErrorsAsData(
- updateReleaseResponse,
- 'releaseUpdate',
- `Something went wrong while updating release with projectPath "${state.projectPath}" and tagName "${state.tagName}"`,
- );
+ checkForErrorsAsData(updateReleaseResponse, 'releaseUpdate');
// Delete all links currently associated with this Release
await Promise.all(
@@ -266,7 +256,7 @@ export const deleteRelease = ({ commit, getters, dispatch, state }) => {
mutation: deleteReleaseMutation,
variables: getters.releaseDeleteMutationVariables,
})
- .then((response) => checkForErrorsAsData(response, 'releaseDelete', ''))
+ .then((response) => checkForErrorsAsData(response, 'releaseDelete'))
.then(() => {
window.sessionStorage.setItem(
deleteReleaseSessionKey(state.projectPath),
diff --git a/app/assets/javascripts/releases/stores/modules/edit_new/getters.js b/app/assets/javascripts/releases/stores/modules/edit_new/getters.js
index 62d6bd42d51..ccca9ca8250 100644
--- a/app/assets/javascripts/releases/stores/modules/edit_new/getters.js
+++ b/app/assets/javascripts/releases/stores/modules/edit_new/getters.js
@@ -130,7 +130,7 @@ export const releaseUpdateMutatationVariables = (state, getters) => {
projectPath: state.projectPath,
tagName: state.release.tagName,
name,
- releasedAt: state.release.releasedAt,
+ releasedAt: getters.releasedAtChanged ? state.release.releasedAt : null,
description: state.includeTagNotes
? getters.formattedReleaseNotes
: state.release.description,
@@ -167,3 +167,6 @@ export const formattedReleaseNotes = ({ includeTagNotes, release: { description
includeTagNotes && tagNotes
? `${description}\n\n### ${s__('Releases|Tag message')}\n\n${tagNotes}\n`
: description;
+
+export const releasedAtChanged = ({ originalReleasedAt, release }) =>
+ originalReleasedAt !== release.releasedAt;
diff --git a/app/assets/javascripts/releases/stores/modules/edit_new/mutations.js b/app/assets/javascripts/releases/stores/modules/edit_new/mutations.js
index ea794f91f66..34361f84a5a 100644
--- a/app/assets/javascripts/releases/stores/modules/edit_new/mutations.js
+++ b/app/assets/javascripts/releases/stores/modules/edit_new/mutations.js
@@ -14,7 +14,7 @@ export default {
description: '',
milestones: [],
groupMilestones: [],
- releasedAt: new Date(),
+ releasedAt: state.originalReleasedAt,
assets: {
links: [],
},
@@ -29,6 +29,7 @@ export default {
state.isFetchingRelease = false;
state.release = data;
state.originalRelease = Object.freeze(cloneDeep(state.release));
+ state.originalReleasedAt = state.originalRelease.releasedAt;
},
[types.RECEIVE_RELEASE_ERROR](state, error) {
state.fetchError = error;
diff --git a/app/assets/javascripts/releases/stores/modules/edit_new/state.js b/app/assets/javascripts/releases/stores/modules/edit_new/state.js
index cb447cf9aaf..11a2f9df59b 100644
--- a/app/assets/javascripts/releases/stores/modules/edit_new/state.js
+++ b/app/assets/javascripts/releases/stores/modules/edit_new/state.js
@@ -61,4 +61,5 @@ export default ({
tagNotes: '',
includeTagNotes: false,
existingRelease: null,
+ originalReleasedAt: new Date(),
});
diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue
index 78572f11f6f..902077ba3e4 100644
--- a/app/assets/javascripts/repository/components/blob_content_viewer.vue
+++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue
@@ -13,9 +13,10 @@ import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import WebIdeLink from '~/vue_shared/components/web_ide_link.vue';
import CodeIntelligence from '~/code_navigation/components/app.vue';
import LineHighlighter from '~/blob/line_highlighter';
+import blobInfoQuery from 'shared_queries/repository/blob_info.query.graphql';
import addBlameLink from '~/blob/blob_blame_link';
+import projectInfoQuery from '../queries/project_info.query.graphql';
import getRefMixin from '../mixins/get_ref';
-import blobInfoQuery from '../queries/blob_info.query.graphql';
import userInfoQuery from '../queries/user_info.query.graphql';
import applicationInfoQuery from '../queries/application_info.query.graphql';
import { DEFAULT_BLOB_INFO, TEXT_FILE_TYPE, LFS_STORAGE, LEGACY_FILE_TYPES } from '../constants';
@@ -41,6 +42,21 @@ export default {
},
},
apollo: {
+ projectInfo: {
+ query: projectInfoQuery,
+ variables() {
+ return {
+ projectPath: this.projectPath,
+ };
+ },
+ error() {
+ this.displayError();
+ },
+ update({ project }) {
+ this.pathLocks = project.pathLocks || DEFAULT_BLOB_INFO.pathLocks;
+ this.userPermissions = project.userPermissions;
+ },
+ },
gitpodEnabled: {
query: applicationInfoQuery,
error() {
@@ -121,6 +137,8 @@ export default {
gitpodEnabled: DEFAULT_BLOB_INFO.gitpodEnabled,
currentUser: DEFAULT_BLOB_INFO.currentUser,
useFallback: false,
+ pathLocks: DEFAULT_BLOB_INFO.pathLocks,
+ userPermissions: DEFAULT_BLOB_INFO.userPermissions,
};
},
computed: {
@@ -163,7 +181,7 @@ export default {
);
},
canLock() {
- const { pushCode, downloadCode } = this.project.userPermissions;
+ const { pushCode, downloadCode } = this.userPermissions;
const currentUsername = window.gon?.current_username;
if (this.pathLockedByUser && this.pathLockedByUser.username !== currentUsername) {
@@ -173,12 +191,12 @@ export default {
return pushCode && downloadCode;
},
pathLockedByUser() {
- const pathLock = this.project?.pathLocks?.nodes.find((node) => node.path === this.path);
+ const pathLock = this.pathLocks?.nodes.find((node) => node.path === this.path);
return pathLock ? pathLock.user : null;
},
showForkSuggestion() {
- const { createMergeRequestIn, forkProject } = this.project.userPermissions;
+ const { createMergeRequestIn, forkProject } = this.userPermissions;
const { canModifyBlob } = this.blobInfo;
return this.isLoggedIn && !canModifyBlob && createMergeRequestIn && forkProject;
@@ -338,7 +356,7 @@ export default {
:name="blobInfo.name"
:replace-path="blobInfo.replacePath"
:delete-path="blobInfo.webPath"
- :can-push-code="project.userPermissions.pushCode"
+ :can-push-code="userPermissions.pushCode"
:can-push-to-branch="blobInfo.canCurrentUserPushToBranch"
:empty-repo="project.repository.empty"
:project-path="projectPath"
diff --git a/app/assets/javascripts/repository/components/blob_controls.vue b/app/assets/javascripts/repository/components/blob_controls.vue
index 3223ed92fe2..fb1227f0df9 100644
--- a/app/assets/javascripts/repository/components/blob_controls.vue
+++ b/app/assets/javascripts/repository/components/blob_controls.vue
@@ -90,7 +90,7 @@ export default {
</script>
<template>
- <div v-if="showBlobControls">
+ <div v-if="showBlobControls" class="gl-display-flex gl-gap-3">
<gl-button data-testid="find" :href="blobInfo.findFilePath" :class="$options.buttonClassList">
{{ $options.i18n.findFile }}
</gl-button>
diff --git a/app/assets/javascripts/repository/components/breadcrumbs.vue b/app/assets/javascripts/repository/components/breadcrumbs.vue
index 20888db80a9..46dee9db69a 100644
--- a/app/assets/javascripts/repository/components/breadcrumbs.vue
+++ b/app/assets/javascripts/repository/components/breadcrumbs.vue
@@ -191,7 +191,7 @@ export default {
href: `${this.newBlobPath}/${
this.currentPath ? encodeURIComponent(this.currentPath) : ''
}`,
- class: 'qa-new-file-option',
+ 'data-qa-selector': 'new_file_menu_item',
},
text: __('New file'),
},
@@ -300,7 +300,11 @@ export default {
</router-link>
</li>
<li v-if="renderAddToTreeDropdown" class="breadcrumb-item">
- <gl-dropdown toggle-class="add-to-tree qa-add-to-tree gl-ml-2">
+ <gl-dropdown
+ toggle-class="add-to-tree gl-ml-2"
+ data-testid="add-to-tree"
+ data-qa-selector="add_to_tree_dropdown"
+ >
<template #button-content>
<span class="sr-only">{{ __('Add to tree') }}</span>
<gl-icon name="plus" :size="16" class="float-left" />
diff --git a/app/assets/javascripts/repository/components/last_commit.vue b/app/assets/javascripts/repository/components/last_commit.vue
index 7f408485326..22fe3fe440e 100644
--- a/app/assets/javascripts/repository/components/last_commit.vue
+++ b/app/assets/javascripts/repository/components/last_commit.vue
@@ -135,7 +135,7 @@ export default {
<div
class="commit-detail flex-list gl-display-flex gl-justify-content-space-between gl-align-items-flex-start gl-flex-grow-1 gl-min-w-0"
>
- <div class="commit-content qa-commit-content">
+ <div class="commit-content" data-qa-selector="commit_content">
<gl-link
v-safe-html:[$options.safeHtmlConfig]="commit.titleHtml"
:href="commit.webPath"
@@ -148,6 +148,7 @@ export default {
:class="{ open: showDescription }"
:title="__('Toggle commit description')"
:aria-label="__('Toggle commit description')"
+ :selected="showDescription"
class="text-expander gl-vertical-align-bottom!"
icon="ellipsis_h"
@click="toggleShowDescription"
diff --git a/app/assets/javascripts/repository/components/table/index.vue b/app/assets/javascripts/repository/components/table/index.vue
index 1f6b5e98122..99eb167172b 100644
--- a/app/assets/javascripts/repository/components/table/index.vue
+++ b/app/assets/javascripts/repository/components/table/index.vue
@@ -2,6 +2,7 @@
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';
import projectPathQuery from '../../queries/project_path.query.graphql';
import TableHeader from './header.vue';
@@ -108,7 +109,9 @@ export default {
return {};
}
- return this.commits.find((commitEntry) => commitEntry.fileName === fileName);
+ 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 99b7395d6e7..c8cd64b5311 100644
--- a/app/assets/javascripts/repository/components/table/row.vue
+++ b/app/assets/javascripts/repository/components/table/row.vue
@@ -17,8 +17,8 @@ import { TREE_PAGE_SIZE, ROW_APPEAR_DELAY } from '~/repository/constants';
import FileIcon from '~/vue_shared/components/file_icon.vue';
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 blobInfoQuery from '../../queries/blob_info.query.graphql';
import commitQuery from '../../queries/commit.query.graphql';
export default {
@@ -244,7 +244,7 @@ export default {
/><span class="position-relative">{{ fullPath }}</span>
</component>
<!-- eslint-disable @gitlab/vue-require-i18n-strings -->
- <gl-badge v-if="lfsOid" variant="muted" size="sm" class="ml-1" data-qa-selector="label-lfs"
+ <gl-badge v-if="lfsOid" variant="muted" size="sm" class="ml-1" data-testid="label-lfs"
>LFS</gl-badge
>
<!-- eslint-enable @gitlab/vue-require-i18n-strings -->
diff --git a/app/assets/javascripts/repository/log_tree.js b/app/assets/javascripts/repository/log_tree.js
index 9345a8406e3..a5bcd9e6b5e 100644
--- a/app/assets/javascripts/repository/log_tree.js
+++ b/app/assets/javascripts/repository/log_tree.js
@@ -1,6 +1,7 @@
import produce from 'immer';
import { normalizeData } from 'ee_else_ce/repository/utils/commit';
import axios from '~/lib/utils/axios_utils';
+import { joinPaths } from '~/lib/utils/url_utility';
import commitsQuery from './queries/commits.query.graphql';
import projectPathQuery from './queries/project_path.query.graphql';
import refQuery from './queries/ref.query.graphql';
@@ -16,7 +17,7 @@ function setNextOffset(offset) {
}
export function resolveCommit(commits, path, { resolve, entry }) {
- const commit = commits.find((c) => c.filePath === `${path}/${entry.name}`);
+ const commit = commits.find((c) => c.filePath === joinPaths(path, entry.name));
if (commit) {
resolve(commit);
diff --git a/app/assets/javascripts/repository/queries/blob_info.query.graphql b/app/assets/javascripts/repository/queries/blob_info.query.graphql
deleted file mode 100644
index 45a7793e559..00000000000
--- a/app/assets/javascripts/repository/queries/blob_info.query.graphql
+++ /dev/null
@@ -1,66 +0,0 @@
-#import "ee_else_ce/repository/queries/path_locks.fragment.graphql"
-
-query getBlobInfo(
- $projectPath: ID!
- $filePath: String!
- $ref: String!
- $shouldFetchRawText: Boolean!
-) {
- project(fullPath: $projectPath) {
- userPermissions {
- pushCode
- downloadCode
- createMergeRequestIn
- forkProject
- }
- ...ProjectPathLocksFragment
- repository {
- empty
- blobs(paths: [$filePath], ref: $ref) {
- nodes {
- id
- webPath
- name
- size
- rawSize
- rawTextBlob @include(if: $shouldFetchRawText)
- fileType
- language
- path
- blamePath
- editBlobPath
- gitpodBlobUrl
- ideEditPath
- forkAndEditPath
- ideForkAndEditPath
- codeNavigationPath
- projectBlobPathRoot
- forkAndViewPath
- environmentFormattedExternalUrl
- environmentExternalUrlForRouteMap
- canModifyBlob
- canCurrentUserPushToBranch
- archived
- storedExternally
- externalStorage
- externalStorageUrl
- rawPath
- replacePath
- pipelineEditorPath
- simpleViewer {
- fileType
- tooLarge
- type
- renderError
- }
- richViewer {
- fileType
- tooLarge
- type
- renderError
- }
- }
- }
- }
- }
-}
diff --git a/app/assets/javascripts/repository/queries/project_info.query.graphql b/app/assets/javascripts/repository/queries/project_info.query.graphql
new file mode 100644
index 00000000000..7a380d25bb1
--- /dev/null
+++ b/app/assets/javascripts/repository/queries/project_info.query.graphql
@@ -0,0 +1,14 @@
+#import "ee_else_ce/repository/queries/path_locks.fragment.graphql"
+
+query getProjectInfo($projectPath: ID!) {
+ project(fullPath: $projectPath) {
+ id
+ userPermissions {
+ pushCode
+ downloadCode
+ createMergeRequestIn
+ forkProject
+ }
+ ...ProjectPathLocksFragment
+ }
+}
diff --git a/app/assets/javascripts/repository/utils/commit.js b/app/assets/javascripts/repository/utils/commit.js
index 878b4fdd71a..247e30d20fc 100644
--- a/app/assets/javascripts/repository/utils/commit.js
+++ b/app/assets/javascripts/repository/utils/commit.js
@@ -1,3 +1,5 @@
+import { joinPaths } from '~/lib/utils/url_utility';
+
export function normalizeData(data, path, extra = () => {}) {
return data.map((d) => ({
sha: d.commit.id,
@@ -6,7 +8,7 @@ export function normalizeData(data, path, extra = () => {}) {
committedDate: d.commit.committed_date,
commitPath: d.commit_path,
fileName: d.file_name,
- filePath: `${path}/${d.file_name}`,
+ filePath: joinPaths(path, d.file_name),
__typename: 'LogTreeCommit',
...extra(d),
}));
diff --git a/app/assets/javascripts/rest_api.js b/app/assets/javascripts/rest_api.js
index 0b6c5063129..7b5babdd3a6 100644
--- a/app/assets/javascripts/rest_api.js
+++ b/app/assets/javascripts/rest_api.js
@@ -6,6 +6,7 @@ export * from './api/bulk_imports_api';
export * from './api/namespaces_api';
export * from './api/tags_api';
export * from './api/alert_management_alerts_api';
+export * from './api/harbor_registry';
// Note: It's not possible to spy on methods imported from this file in
// Jest tests.
diff --git a/app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue b/app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue
deleted file mode 100644
index 40787cf72da..00000000000
--- a/app/assets/javascripts/runner/admin_runner_edit/admin_runner_edit_app.vue
+++ /dev/null
@@ -1,73 +0,0 @@
-<script>
-import { createAlert } from '~/flash';
-import { TYPE_CI_RUNNER } from '~/graphql_shared/constants';
-import { convertToGraphQLId } from '~/graphql_shared/utils';
-import RunnerHeader from '../components/runner_header.vue';
-import RunnerUpdateForm from '../components/runner_update_form.vue';
-import { I18N_FETCH_ERROR } from '../constants';
-import runnerFormQuery from '../graphql/edit/runner_form.query.graphql';
-import { captureException } from '../sentry_utils';
-
-export default {
- name: 'AdminRunnerEditApp',
- components: {
- RunnerHeader,
- RunnerUpdateForm,
- },
- props: {
- runnerId: {
- type: String,
- required: true,
- },
- runnerPath: {
- type: String,
- required: false,
- default: null,
- },
- },
- data() {
- return {
- runner: null,
- };
- },
- apollo: {
- runner: {
- query: runnerFormQuery,
- variables() {
- return {
- id: convertToGraphQLId(TYPE_CI_RUNNER, this.runnerId),
- };
- },
- error(error) {
- createAlert({ message: I18N_FETCH_ERROR });
-
- this.reportToSentry(error);
- },
- },
- },
- computed: {
- loading() {
- return this.$apollo.queries.runner.loading;
- },
- },
- errorCaptured(error) {
- this.reportToSentry(error);
- },
- methods: {
- reportToSentry(error) {
- captureException({ error, component: this.$options.name });
- },
- },
-};
-</script>
-<template>
- <div>
- <runner-header v-if="runner" :runner="runner" />
- <runner-update-form
- :loading="loading"
- :runner="runner"
- :runner-path="runnerPath"
- class="gl-my-5"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/runner/admin_runner_edit/index.js b/app/assets/javascripts/runner/admin_runner_edit/index.js
deleted file mode 100644
index a2ac5731a62..00000000000
--- a/app/assets/javascripts/runner/admin_runner_edit/index.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import createDefaultClient from '~/lib/graphql';
-import AdminRunnerEditApp from './admin_runner_edit_app.vue';
-
-Vue.use(VueApollo);
-
-export const initAdminRunnerEdit = (selector = '#js-admin-runner-edit') => {
- const el = document.querySelector(selector);
-
- if (!el) {
- return null;
- }
-
- const { runnerId, runnerPath } = el.dataset;
-
- const apolloProvider = new VueApollo({
- defaultClient: createDefaultClient(),
- });
-
- return new Vue({
- el,
- apolloProvider,
- render(h) {
- return h(AdminRunnerEditApp, {
- props: {
- runnerId,
- runnerPath,
- },
- });
- },
- });
-};
diff --git a/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue b/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue
index 777a332333d..f5620876783 100644
--- a/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue
+++ b/app/assets/javascripts/runner/admin_runners/admin_runners_app.vue
@@ -15,6 +15,7 @@ import allRunnersQuery from 'ee_else_ce/runner/graphql/list/all_runners.query.gr
import allRunnersCountQuery from 'ee_else_ce/runner/graphql/list/all_runners_count.query.graphql';
import RegistrationDropdown from '../components/registration/registration_dropdown.vue';
+import RunnerStackedLayoutBanner from '../components/runner_stacked_layout_banner.vue';
import RunnerFilteredSearchBar from '../components/runner_filtered_search_bar.vue';
import RunnerBulkDelete from '../components/runner_bulk_delete.vue';
import RunnerBulkDeleteCheckbox from '../components/runner_bulk_delete_checkbox.vue';
@@ -37,6 +38,7 @@ export default {
components: {
GlLink,
RegistrationDropdown,
+ RunnerStackedLayoutBanner,
RunnerFilteredSearchBar,
RunnerBulkDelete,
RunnerBulkDeleteCheckbox,
@@ -169,6 +171,8 @@ export default {
</script>
<template>
<div>
+ <runner-stacked-layout-banner />
+
<div
class="gl-display-flex gl-align-items-center gl-flex-direction-column-reverse gl-md-flex-direction-row gl-mt-3 gl-md-mt-0"
>
diff --git a/app/assets/javascripts/runner/components/cells/runner_stacked_summary_cell.vue b/app/assets/javascripts/runner/components/cells/runner_stacked_summary_cell.vue
new file mode 100644
index 00000000000..e5d49eb7c8e
--- /dev/null
+++ b/app/assets/javascripts/runner/components/cells/runner_stacked_summary_cell.vue
@@ -0,0 +1,112 @@
+<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/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/runner/components/cells/runner_status_cell.vue b/app/assets/javascripts/runner/components/cells/runner_status_cell.vue
index a48db9f8ac8..eb98d4ae2fb 100644
--- a/app/assets/javascripts/runner/components/cells/runner_status_cell.vue
+++ b/app/assets/javascripts/runner/components/cells/runner_status_cell.vue
@@ -32,17 +32,14 @@ export default {
<div>
<runner-status-badge
:runner="runner"
- size="sm"
class="gl-display-inline-block gl-max-w-full gl-text-truncate"
/>
<runner-upgrade-status-badge
:runner="runner"
- size="sm"
class="gl-display-inline-block gl-max-w-full gl-text-truncate"
/>
<runner-paused-badge
v-if="paused"
- size="sm"
class="gl-display-inline-block gl-max-w-full gl-text-truncate"
/>
</div>
diff --git a/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue b/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue
deleted file mode 100644
index 1cd098d6713..00000000000
--- a/app/assets/javascripts/runner/components/cells/runner_summary_cell.vue
+++ /dev/null
@@ -1,71 +0,0 @@
-<script>
-import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
-
-import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
-import RunnerName from '../runner_name.vue';
-import RunnerTypeBadge from '../runner_type_badge.vue';
-
-import { I18N_LOCKED_RUNNER_DESCRIPTION } from '../../constants';
-
-export default {
- components: {
- GlIcon,
- TooltipOnTruncate,
- RunnerName,
- RunnerTypeBadge,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- props: {
- runner: {
- type: Object,
- required: true,
- },
- },
- computed: {
- runnerType() {
- return this.runner.runnerType;
- },
- locked() {
- return this.runner.locked;
- },
- description() {
- return this.runner.description;
- },
- ipAddress() {
- return this.runner.ipAddress;
- },
- },
- i18n: {
- I18N_LOCKED_RUNNER_DESCRIPTION,
- },
-};
-</script>
-
-<template>
- <div>
- <slot :runner="runner" name="runner-name">
- <runner-name :runner="runner" />
- </slot>
-
- <runner-type-badge :type="runnerType" size="sm" />
- <gl-icon
- v-if="locked"
- v-gl-tooltip
- :title="$options.i18n.I18N_LOCKED_RUNNER_DESCRIPTION"
- name="lock"
- />
- <tooltip-on-truncate class="gl-display-block gl-text-truncate" :title="description">
- {{ description }}
- </tooltip-on-truncate>
- <tooltip-on-truncate
- v-if="ipAddress"
- class="gl-display-block gl-text-truncate"
- :title="ipAddress"
- >
- <span class="gl-md-display-none gl-lg-display-inline">{{ __('IP Address') }}</span>
- <strong>{{ ipAddress }}</strong>
- </tooltip-on-truncate>
- </div>
-</template>
diff --git a/app/assets/javascripts/runner/components/cells/runner_summary_field.vue b/app/assets/javascripts/runner/components/cells/runner_summary_field.vue
new file mode 100644
index 00000000000..1bbbd55089a
--- /dev/null
+++ b/app/assets/javascripts/runner/components/cells/runner_summary_field.vue
@@ -0,0 +1,33 @@
+<script>
+import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlIcon,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ icon: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ tooltip: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+};
+</script>
+
+<template>
+ <div v-gl-tooltip="tooltip" class="gl-display-inline-block gl-text-secondary gl-my-2 gl-mr-2">
+ <gl-icon v-if="icon" :name="icon" />
+ <!-- display tooltip as a label for screen readers -->
+ <span class="gl-sr-only">{{ tooltip }}</span>
+ <slot></slot>
+ </div>
+</template>
diff --git a/app/assets/javascripts/runner/components/runner_detail.vue b/app/assets/javascripts/runner/components/runner_detail.vue
index 584f77b7648..c260670b517 100644
--- a/app/assets/javascripts/runner/components/runner_detail.vue
+++ b/app/assets/javascripts/runner/components/runner_detail.vue
@@ -21,7 +21,8 @@ export default {
props: {
label: {
type: String,
- required: true,
+ default: null,
+ required: false,
},
value: {
type: String,
@@ -39,7 +40,11 @@ export default {
<template>
<div class="gl-display-contents">
- <dt class="gl-mb-5 gl-mr-6 gl-max-w-26">{{ label }}</dt>
+ <dt class="gl-mb-5 gl-mr-6 gl-max-w-26">
+ <template v-if="label || $scopedSlots.label">
+ <slot name="label">{{ label }}</slot>
+ </template>
+ </dt>
<dd class="gl-mb-5">
<template v-if="value || $scopedSlots.value">
<slot name="value">{{ value }}</slot>
diff --git a/app/assets/javascripts/runner/components/runner_details.vue b/app/assets/javascripts/runner/components/runner_details.vue
index d5222f39b81..79f934764c6 100644
--- a/app/assets/javascripts/runner/components/runner_details.vue
+++ b/app/assets/javascripts/runner/components/runner_details.vue
@@ -1,7 +1,10 @@
<script>
-import { GlIntersperse } from '@gitlab/ui';
+import { GlIntersperse, GlLink } from '@gitlab/ui';
+import { helpPagePath } from '~/helpers/help_page_helper';
import { s__ } from '~/locale';
+import HelpPopover from '~/vue_shared/components/help_popover.vue';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
import { ACCESS_LEVEL_REF_PROTECTED, GROUP_TYPE, PROJECT_TYPE } from '../constants';
import RunnerDetail from './runner_detail.vue';
@@ -12,6 +15,8 @@ import RunnerTags from './runner_tags.vue';
export default {
components: {
GlIntersperse,
+ GlLink,
+ HelpPopover,
RunnerDetail,
RunnerMaintenanceNoteDetail: () =>
import('ee_component/runner/components/runner_maintenance_note_detail.vue'),
@@ -24,6 +29,7 @@ export default {
RunnerTags,
TimeAgo,
},
+ mixins: [glFeatureFlagMixin()],
props: {
runner: {
type: Object,
@@ -60,6 +66,16 @@ export default {
isProjectRunner() {
return this.runner?.runnerType === PROJECT_TYPE;
},
+ tokenExpirationHelpPopoverOptions() {
+ return {
+ title: s__('Runners|Runner authentication token expiration'),
+ };
+ },
+ tokenExpirationHelpUrl() {
+ return helpPagePath('ci/runners/configure_runners', {
+ anchor: 'authentication-token-security',
+ });
+ },
},
ACCESS_LEVEL_REF_PROTECTED,
};
@@ -101,6 +117,34 @@ export default {
</template>
</runner-detail>
<runner-detail :label="s__('Runners|Maximum job timeout')" :value="maximumTimeout" />
+ <runner-detail
+ v-if="glFeatures.enforceRunnerTokenExpiresAt"
+ :empty-value="s__('Runners|Never expires')"
+ >
+ <template #label>
+ {{ s__('Runners|Token expiry') }}
+ <help-popover :options="tokenExpirationHelpPopoverOptions">
+ <p>
+ {{
+ s__(
+ 'Runners|Runner authentication tokens will expire based on a set interval. They will automatically rotate once expired.',
+ )
+ }}
+ </p>
+ <p class="gl-mb-0">
+ <gl-link
+ :href="tokenExpirationHelpUrl"
+ target="_blank"
+ class="gl-reset-font-size"
+ >{{ __('Learn more') }}</gl-link
+ >
+ </p>
+ </help-popover>
+ </template>
+ <template v-if="runner.tokenExpiresAt" #value>
+ <time-ago :time="runner.tokenExpiresAt" />
+ </template>
+ </runner-detail>
<runner-detail :label="s__('Runners|Tags')">
<template v-if="tagList.length" #value>
<runner-tags class="gl-vertical-align-middle" :tag-list="tagList" size="sm" />
diff --git a/app/assets/javascripts/runner/components/runner_header.vue b/app/assets/javascripts/runner/components/runner_header.vue
index abc07cec1ad..874c234ca4c 100644
--- a/app/assets/javascripts/runner/components/runner_header.vue
+++ b/app/assets/javascripts/runner/components/runner_header.vue
@@ -38,31 +38,33 @@ export default {
</script>
<template>
<div
- class="gl-display-flex gl-align-items-center gl-py-5 gl-border-b-1 gl-border-b-solid gl-border-b-gray-100"
+ class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-gap-3 gl-flex-wrap gl-py-5 gl-border-b-1 gl-border-b-solid gl-border-b-gray-100"
>
- <div>
+ <div class="gl-display-flex gl-align-items-flex-start gl-gap-3 gl-flex-wrap">
<runner-status-badge :runner="runner" />
<runner-type-badge v-if="runner" :type="runner.runnerType" />
- <template v-if="runner.createdAt">
- <gl-sprintf :message="__('%{runner} created %{timeago}')">
- <template #runner>
- <strong>{{ heading }}</strong>
- <gl-icon
- v-if="runner.locked"
- v-gl-tooltip="$options.I18N_LOCKED_RUNNER_DESCRIPTION"
- name="lock"
- :aria-label="$options.I18N_LOCKED_RUNNER_DESCRIPTION"
- />
- </template>
- <template #timeago>
- <time-ago :time="runner.createdAt" />
- </template>
- </gl-sprintf>
- </template>
- <template v-else>
- <strong>{{ heading }}</strong>
- </template>
+ <span>
+ <template v-if="runner.createdAt">
+ <gl-sprintf :message="__('%{runner} created %{timeago}')">
+ <template #runner>
+ <strong>{{ heading }}</strong>
+ <gl-icon
+ v-if="runner.locked"
+ v-gl-tooltip="$options.I18N_LOCKED_RUNNER_DESCRIPTION"
+ name="lock"
+ :aria-label="$options.I18N_LOCKED_RUNNER_DESCRIPTION"
+ />
+ </template>
+ <template #timeago>
+ <time-ago :time="runner.createdAt" />
+ </template>
+ </gl-sprintf>
+ </template>
+ <template v-else>
+ <strong>{{ heading }}</strong>
+ </template>
+ </span>
</div>
- <div class="gl-ml-auto gl-flex-shrink-0"><slot name="actions"></slot></div>
+ <div class="gl-display-flex gl-gap-3 gl-flex-wrap"><slot name="actions"></slot></div>
</div>
</template>
diff --git a/app/assets/javascripts/runner/components/runner_list.vue b/app/assets/javascripts/runner/components/runner_list.vue
index 2e406f71792..26f1f3ce08c 100644
--- a/app/assets/javascripts/runner/components/runner_list.vue
+++ b/app/assets/javascripts/runner/components/runner_list.vue
@@ -1,24 +1,17 @@
<script>
import { GlFormCheckbox, GlTableLite, GlTooltipDirective, GlSkeletonLoader } from '@gitlab/ui';
-import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { __, s__ } from '~/locale';
-import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
+import { s__ } from '~/locale';
import checkedRunnerIdsQuery from '../graphql/list/checked_runner_ids.query.graphql';
import { formatJobCount, tableField } from '../utils';
-import RunnerSummaryCell from './cells/runner_summary_cell.vue';
+import RunnerStackedSummaryCell from './cells/runner_stacked_summary_cell.vue';
import RunnerStatusPopover from './runner_status_popover.vue';
import RunnerStatusCell from './cells/runner_status_cell.vue';
-import RunnerTags from './runner_tags.vue';
const defaultFields = [
tableField({ key: 'status', label: s__('Runners|Status'), thClasses: ['gl-w-15p'] }),
- tableField({ key: 'summary', label: s__('Runners|Runner'), thClasses: ['gl-lg-w-25p'] }),
- tableField({ key: 'version', label: __('Version') }),
- tableField({ key: 'jobCount', label: __('Jobs') }),
- tableField({ key: 'tagList', label: __('Tags'), thClasses: ['gl-lg-w-25p'] }),
- tableField({ key: 'contactedAt', label: __('Last contact') }),
- tableField({ key: 'actions', label: '' }),
+ tableField({ key: 'summary', label: s__('Runners|Runner') }),
+ tableField({ key: 'actions', label: '', thClasses: ['gl-w-15p'] }),
];
export default {
@@ -26,11 +19,8 @@ export default {
GlFormCheckbox,
GlTableLite,
GlSkeletonLoader,
- TooltipOnTruncate,
- TimeAgo,
RunnerStatusPopover,
- RunnerSummaryCell,
- RunnerTags,
+ RunnerStackedSummaryCell,
RunnerStatusCell,
},
directives: {
@@ -74,6 +64,8 @@ export default {
};
},
fields() {
+ const fields = defaultFields;
+
if (this.checkable) {
const checkboxField = tableField({
key: 'checkbox',
@@ -81,9 +73,9 @@ export default {
thClasses: ['gl-w-9'],
tdClass: ['gl-text-center'],
});
- return [checkboxField, ...defaultFields];
+ return [checkboxField, ...fields];
}
- return defaultFields;
+ return fields;
},
},
methods: {
@@ -141,30 +133,11 @@ export default {
</template>
<template #cell(summary)="{ item, index }">
- <runner-summary-cell :runner="item">
+ <runner-stacked-summary-cell :runner="item">
<template #runner-name="{ runner }">
<slot name="runner-name" :runner="runner" :index="index"></slot>
</template>
- </runner-summary-cell>
- </template>
-
- <template #cell(version)="{ item: { version } }">
- <tooltip-on-truncate class="gl-display-block gl-text-truncate" :title="version">
- {{ version }}
- </tooltip-on-truncate>
- </template>
-
- <template #cell(jobCount)="{ item: { jobCount } }">
- {{ formatJobCount(jobCount) }}
- </template>
-
- <template #cell(tagList)="{ item: { tagList } }">
- <runner-tags :tag-list="tagList" size="sm" />
- </template>
-
- <template #cell(contactedAt)="{ item: { contactedAt } }">
- <time-ago v-if="contactedAt" :time="contactedAt" />
- <template v-else>{{ __('Never') }}</template>
+ </runner-stacked-summary-cell>
</template>
<template #cell(actions)="{ item }">
diff --git a/app/assets/javascripts/runner/components/runner_name.vue b/app/assets/javascripts/runner/components/runner_name.vue
index 8e495125e03..d4ecfd2d776 100644
--- a/app/assets/javascripts/runner/components/runner_name.vue
+++ b/app/assets/javascripts/runner/components/runner_name.vue
@@ -14,5 +14,7 @@ export default {
};
</script>
<template>
- <span>#{{ getIdFromGraphQLId(runner.id) }} ({{ runner.shortSha }})</span>
+ <span class="gl-font-weight-bold gl-vertical-align-middle"
+ >#{{ getIdFromGraphQLId(runner.id) }} ({{ runner.shortSha }})</span
+ >
</template>
diff --git a/app/assets/javascripts/runner/components/runner_paused_badge.vue b/app/assets/javascripts/runner/components/runner_paused_badge.vue
index 27618290ce6..00fd84a48d8 100644
--- a/app/assets/javascripts/runner/components/runner_paused_badge.vue
+++ b/app/assets/javascripts/runner/components/runner_paused_badge.vue
@@ -1,6 +1,6 @@
<script>
import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
-import { I18N_PAUSED_DESCRIPTION } from '../constants';
+import { I18N_PAUSED, I18N_PAUSED_DESCRIPTION } from '../constants';
export default {
components: {
@@ -9,11 +9,17 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
+ I18N_PAUSED,
I18N_PAUSED_DESCRIPTION,
};
</script>
<template>
- <gl-badge v-gl-tooltip="$options.I18N_PAUSED_DESCRIPTION" variant="danger" v-bind="$attrs">
- {{ s__('Runners|paused') }}
+ <gl-badge
+ v-gl-tooltip="$options.I18N_PAUSED_DESCRIPTION"
+ variant="warning"
+ icon="status-paused"
+ v-bind="$attrs"
+ >
+ {{ $options.I18N_PAUSED }}
</gl-badge>
</template>
diff --git a/app/assets/javascripts/runner/components/runner_projects.vue b/app/assets/javascripts/runner/components/runner_projects.vue
index 2c1d2fc2b10..84008e8eee8 100644
--- a/app/assets/javascripts/runner/components/runner_projects.vue
+++ b/app/assets/javascripts/runner/components/runner_projects.vue
@@ -1,11 +1,13 @@
<script>
-import { GlSkeletonLoader } from '@gitlab/ui';
+import { GlSearchBoxByType, GlSkeletonLoader } from '@gitlab/ui';
import { sprintf, formatNumber } from '~/locale';
import { createAlert } from '~/flash';
import runnerProjectsQuery from '../graphql/show/runner_projects.query.graphql';
import {
I18N_ASSIGNED_PROJECTS,
- I18N_NONE,
+ I18N_CLEAR_FILTER_PROJECTS,
+ I18N_FILTER_PROJECTS,
+ I18N_NO_PROJECTS_FOUND,
I18N_FETCH_ERROR,
RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
} from '../constants';
@@ -14,9 +16,12 @@ import { captureException } from '../sentry_utils';
import RunnerAssignedItem from './runner_assigned_item.vue';
import RunnerPagination from './runner_pagination.vue';
+const SHORT_SEARCH_LENGTH = 3;
+
export default {
name: 'RunnerProjects',
components: {
+ GlSearchBoxByType,
GlSkeletonLoader,
RunnerAssignedItem,
RunnerPagination,
@@ -35,6 +40,7 @@ export default {
pageInfo: {},
count: 0,
},
+ search: '',
pagination: {},
};
},
@@ -61,9 +67,10 @@ export default {
},
computed: {
variables() {
- const { id } = this.runner;
+ const { search, runner } = this;
return {
- id,
+ id: runner.id,
+ search: search.length >= SHORT_SEARCH_LENGTH ? search : '',
...getPaginationVariables(this.pagination, RUNNER_DETAILS_PROJECTS_PAGE_SIZE),
};
},
@@ -80,22 +87,38 @@ export default {
isOwner(projectId) {
return projectId === this.projects.ownerProjectId;
},
+ onSearchInput(search) {
+ this.search = search;
+ this.pagination = {};
+ },
onPaginationInput(value) {
this.pagination = value;
},
},
- I18N_NONE,
+ RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
+ I18N_CLEAR_FILTER_PROJECTS,
+ I18N_FILTER_PROJECTS,
+ I18N_NO_PROJECTS_FOUND,
};
</script>
<template>
<div class="gl-border-t-gray-100 gl-border-t-1 gl-border-t-solid">
- <h3 class="gl-font-lg gl-mt-5 gl-mb-0">
+ <h3 class="gl-font-lg gl-mt-5">
{{ heading }}
</h3>
+ <gl-search-box-by-type
+ :is-loading="loading"
+ :clear-button-title="$options.I18N_CLEAR_FILTER_PROJECTS"
+ :placeholder="$options.I18N_FILTER_PROJECTS"
+ debounce="500"
+ class="gl-w-28"
+ :value="search"
+ @input="onSearchInput"
+ />
- <div v-if="loading" class="gl-py-5">
- <gl-skeleton-loader />
+ <div v-if="!projects.items.length && loading" class="gl-py-5">
+ <gl-skeleton-loader v-for="i in $options.RUNNER_DETAILS_PROJECTS_PAGE_SIZE" :key="i" />
</div>
<template v-else-if="projects.items.length">
<runner-assigned-item
@@ -110,7 +133,7 @@ export default {
:is-owner="isOwner(project.id)"
/>
</template>
- <span v-else class="gl-text-gray-500">{{ $options.I18N_NONE }}</span>
+ <div v-else class="gl-py-5 gl-text-gray-500">{{ $options.I18N_NO_PROJECTS_FOUND }}</div>
<runner-pagination
:disabled="loading"
diff --git a/app/assets/javascripts/runner/components/runner_stacked_layout_banner.vue b/app/assets/javascripts/runner/components/runner_stacked_layout_banner.vue
new file mode 100644
index 00000000000..e3a9a9fd8a4
--- /dev/null
+++ b/app/assets/javascripts/runner/components/runner_stacked_layout_banner.vue
@@ -0,0 +1,58 @@
+<script>
+import allChangesCommittedSvg from '@gitlab/svgs/dist/illustrations/multi-editor_all_changes_committed_empty.svg';
+import { GlBanner } from '@gitlab/ui';
+
+import { s__ } from '~/locale';
+import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
+
+const I18N_TITLE = s__("Runners|We've made some changes and want your feedback");
+const I18N_DESCRIPTION = s__(
+ "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!",
+);
+const I18N_LINK = s__('Runners|Add your feedback in the issue');
+
+// use a data url instead getting it from via HTML data-* attributes to simplify removal of this feature flag
+const ILLUSTRATION_URL = `data:image/svg+xml;utf8,${encodeURIComponent(allChangesCommittedSvg)}`;
+const ISSUE_URL = 'https://gitlab.com/gitlab-org/gitlab/-/issues/371621';
+const STORAGE_KEY = 'runner_list_stacked_layout_feedback_dismissed';
+
+export default {
+ components: {
+ GlBanner,
+ LocalStorageSync,
+ },
+ data() {
+ return {
+ isDismissed: false,
+ };
+ },
+ methods: {
+ onClose() {
+ this.isDismissed = true;
+ },
+ },
+ I18N_TITLE,
+ I18N_DESCRIPTION,
+ I18N_LINK,
+ ILLUSTRATION_URL,
+ ISSUE_URL,
+ STORAGE_KEY,
+};
+</script>
+
+<template>
+ <div>
+ <local-storage-sync v-model="isDismissed" :storage-key="$options.STORAGE_KEY" />
+ <gl-banner
+ v-if="!isDismissed"
+ :svg-path="$options.ILLUSTRATION_URL"
+ :title="$options.I18N_TITLE"
+ :button-text="$options.I18N_LINK"
+ :button-link="$options.ISSUE_URL"
+ class="gl-my-5"
+ @close="onClose"
+ >
+ <p>{{ $options.I18N_DESCRIPTION }}</p>
+ </gl-banner>
+ </div>
+</template>
diff --git a/app/assets/javascripts/runner/components/runner_status_badge.vue b/app/assets/javascripts/runner/components/runner_status_badge.vue
index 073d0a49f59..d084408781e 100644
--- a/app/assets/javascripts/runner/components/runner_status_badge.vue
+++ b/app/assets/javascripts/runner/components/runner_status_badge.vue
@@ -1,8 +1,12 @@
<script>
import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
-import { __, s__, sprintf } from '~/locale';
+import { __, sprintf } from '~/locale';
import { getTimeago } from '~/lib/utils/datetime_utility';
import {
+ I18N_STATUS_ONLINE,
+ I18N_STATUS_NEVER_CONTACTED,
+ I18N_STATUS_OFFLINE,
+ I18N_STATUS_STALE,
I18N_ONLINE_TIMEAGO_TOOLTIP,
I18N_NEVER_CONTACTED_TOOLTIP,
I18N_OFFLINE_TIMEAGO_TOOLTIP,
@@ -39,26 +43,30 @@ export default {
switch (this.runner?.status) {
case STATUS_ONLINE:
return {
+ icon: 'status-active',
variant: 'success',
- label: s__('Runners|online'),
+ label: I18N_STATUS_ONLINE,
tooltip: this.timeAgoTooltip(I18N_ONLINE_TIMEAGO_TOOLTIP),
};
case STATUS_NEVER_CONTACTED:
return {
+ icon: 'time-out',
variant: 'muted',
- label: s__('Runners|never contacted'),
+ label: I18N_STATUS_NEVER_CONTACTED,
tooltip: I18N_NEVER_CONTACTED_TOOLTIP,
};
case STATUS_OFFLINE:
return {
+ icon: 'time-out',
variant: 'muted',
- label: s__('Runners|offline'),
+ label: I18N_STATUS_OFFLINE,
tooltip: this.timeAgoTooltip(I18N_OFFLINE_TIMEAGO_TOOLTIP),
};
case STATUS_STALE:
return {
+ icon: 'time-out',
variant: 'warning',
- label: s__('Runners|stale'),
+ label: I18N_STATUS_STALE,
// runner may have contacted (or not) and be stale: consider both cases.
tooltip: this.runner.contactedAt
? this.timeAgoTooltip(I18N_STALE_TIMEAGO_TOOLTIP)
@@ -77,7 +85,13 @@ export default {
};
</script>
<template>
- <gl-badge v-if="badge" v-gl-tooltip="badge.tooltip" :variant="badge.variant" v-bind="$attrs">
+ <gl-badge
+ v-if="badge"
+ v-gl-tooltip="badge.tooltip"
+ :variant="badge.variant"
+ :icon="badge.icon"
+ v-bind="$attrs"
+ >
{{ badge.label }}
</gl-badge>
</template>
diff --git a/app/assets/javascripts/runner/components/runner_tags.vue b/app/assets/javascripts/runner/components/runner_tags.vue
index 797d2a35b2c..38e566f9f53 100644
--- a/app/assets/javascripts/runner/components/runner_tags.vue
+++ b/app/assets/javascripts/runner/components/runner_tags.vue
@@ -20,7 +20,7 @@ export default {
};
</script>
<template>
- <span>
+ <span v-if="tagList && tagList.length">
<runner-tag
v-for="tag in tagList"
:key="tag"
diff --git a/app/assets/javascripts/runner/components/runner_type_badge.vue b/app/assets/javascripts/runner/components/runner_type_badge.vue
index b885dcefdcb..f568f914004 100644
--- a/app/assets/javascripts/runner/components/runner_type_badge.vue
+++ b/app/assets/javascripts/runner/components/runner_type_badge.vue
@@ -1,26 +1,31 @@
<script>
import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
-import { s__ } from '~/locale';
import {
INSTANCE_TYPE,
GROUP_TYPE,
PROJECT_TYPE,
+ I18N_INSTANCE_TYPE,
I18N_INSTANCE_RUNNER_DESCRIPTION,
+ I18N_GROUP_TYPE,
I18N_GROUP_RUNNER_DESCRIPTION,
+ I18N_PROJECT_TYPE,
I18N_PROJECT_RUNNER_DESCRIPTION,
} from '../constants';
const BADGE_DATA = {
[INSTANCE_TYPE]: {
- text: s__('Runners|shared'),
+ icon: 'users',
+ text: I18N_INSTANCE_TYPE,
tooltip: I18N_INSTANCE_RUNNER_DESCRIPTION,
},
[GROUP_TYPE]: {
- text: s__('Runners|group'),
+ icon: 'group',
+ text: I18N_GROUP_TYPE,
tooltip: I18N_GROUP_RUNNER_DESCRIPTION,
},
[PROJECT_TYPE]: {
- text: s__('Runners|specific'),
+ icon: 'project',
+ text: I18N_PROJECT_TYPE,
tooltip: I18N_PROJECT_RUNNER_DESCRIPTION,
},
};
@@ -50,7 +55,13 @@ export default {
};
</script>
<template>
- <gl-badge v-if="badge" v-gl-tooltip="badge.tooltip" variant="info" v-bind="$attrs">
+ <gl-badge
+ v-if="badge"
+ v-gl-tooltip="badge.tooltip"
+ variant="muted"
+ :icon="badge.icon"
+ v-bind="$attrs"
+ >
{{ badge.text }}
</gl-badge>
</template>
diff --git a/app/assets/javascripts/runner/components/search_tokens/paused_token_config.js b/app/assets/javascripts/runner/components/search_tokens/paused_token_config.js
index c1ad5da3ab9..97ee8ec3eef 100644
--- a/app/assets/javascripts/runner/components/search_tokens/paused_token_config.js
+++ b/app/assets/javascripts/runner/components/search_tokens/paused_token_config.js
@@ -1,7 +1,7 @@
-import { __, s__ } from '~/locale';
+import { __ } from '~/locale';
import { OPERATOR_IS_ONLY } 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 } from '../../constants';
+import { PARAM_KEY_PAUSED, I18N_PAUSED } from '../../constants';
const options = [
{ value: 'true', title: __('Yes') },
@@ -10,7 +10,7 @@ const options = [
export const pausedTokenConfig = {
icon: 'pause',
- title: s__('Runners|Paused'),
+ title: I18N_PAUSED,
type: PARAM_KEY_PAUSED,
token: BaseToken,
unique: true,
diff --git a/app/assets/javascripts/runner/components/search_tokens/status_token_config.js b/app/assets/javascripts/runner/components/search_tokens/status_token_config.js
index 9e6f63d3f7c..f5c42d120fb 100644
--- a/app/assets/javascripts/runner/components/search_tokens/status_token_config.js
+++ b/app/assets/javascripts/runner/components/search_tokens/status_token_config.js
@@ -1,7 +1,11 @@
-import { __, s__ } from '~/locale';
+import { __ } from '~/locale';
import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
import {
+ I18N_STATUS_ONLINE,
+ I18N_STATUS_NEVER_CONTACTED,
+ I18N_STATUS_OFFLINE,
+ I18N_STATUS_STALE,
STATUS_ONLINE,
STATUS_OFFLINE,
STATUS_NEVER_CONTACTED,
@@ -10,10 +14,10 @@ import {
} from '../../constants';
const options = [
- { value: STATUS_ONLINE, title: s__('Runners|Online') },
- { value: STATUS_OFFLINE, title: s__('Runners|Offline') },
- { value: STATUS_NEVER_CONTACTED, title: s__('Runners|Never contacted') },
- { value: STATUS_STALE, title: s__('Runners|Stale') },
+ { value: STATUS_ONLINE, title: I18N_STATUS_ONLINE },
+ { value: STATUS_OFFLINE, title: I18N_STATUS_OFFLINE },
+ { value: STATUS_NEVER_CONTACTED, title: I18N_STATUS_NEVER_CONTACTED },
+ { value: STATUS_STALE, title: I18N_STATUS_STALE },
];
export const statusTokenConfig = {
diff --git a/app/assets/javascripts/runner/components/stat/runner_stats.vue b/app/assets/javascripts/runner/components/stat/runner_stats.vue
index 93e54ebe7f4..4df59f5a0c9 100644
--- a/app/assets/javascripts/runner/components/stat/runner_stats.vue
+++ b/app/assets/javascripts/runner/components/stat/runner_stats.vue
@@ -1,7 +1,13 @@
<script>
-import { s__ } from '~/locale';
import RunnerSingleStat from '~/runner/components/stat/runner_single_stat.vue';
-import { STATUS_ONLINE, STATUS_OFFLINE, STATUS_STALE } from '../../constants';
+import {
+ I18N_STATUS_ONLINE,
+ I18N_STATUS_OFFLINE,
+ I18N_STATUS_STALE,
+ STATUS_ONLINE,
+ STATUS_OFFLINE,
+ STATUS_STALE,
+} from '../../constants';
export default {
components: {
@@ -29,8 +35,8 @@ export default {
skip: this.statusCountSkip(STATUS_ONLINE),
variables: { ...this.variables, status: STATUS_ONLINE },
variant: 'success',
- title: s__('Runners|Online runners'),
- metaText: s__('Runners|online'),
+ title: I18N_STATUS_ONLINE,
+ metaIcon: 'status-active',
},
},
{
@@ -39,8 +45,8 @@ export default {
skip: this.statusCountSkip(STATUS_OFFLINE),
variables: { ...this.variables, status: STATUS_OFFLINE },
variant: 'muted',
- title: s__('Runners|Offline runners'),
- metaText: s__('Runners|offline'),
+ title: I18N_STATUS_OFFLINE,
+ metaIcon: 'status-waiting',
},
},
{
@@ -49,8 +55,8 @@ export default {
skip: this.statusCountSkip(STATUS_STALE),
variables: { ...this.variables, status: STATUS_STALE },
variant: 'warning',
- title: s__('Runners|Stale runners'),
- metaText: s__('Runners|stale'),
+ title: I18N_STATUS_STALE,
+ metaIcon: 'time-out',
},
},
];
diff --git a/app/assets/javascripts/runner/constants.js b/app/assets/javascripts/runner/constants.js
index ed1afcbf691..3009577599f 100644
--- a/app/assets/javascripts/runner/constants.js
+++ b/app/assets/javascripts/runner/constants.js
@@ -23,6 +23,12 @@ export const I18N_GROUP_RUNNER_DESCRIPTION = s__(
);
export const I18N_PROJECT_RUNNER_DESCRIPTION = s__('Runners|Associated with one or more projects');
+// Status
+export const I18N_STATUS_ONLINE = s__('Runners|Online');
+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');
+
// Status help popover
export const I18N_STATUS_POPOVER_TITLE = s__('Runners|Runner statuses');
@@ -62,6 +68,7 @@ export const I18N_STALE_NEVER_CONTACTED_TOOLTIP = s__(
export const I18N_EDIT = __('Edit');
export const I18N_PAUSE = __('Pause');
+export const I18N_PAUSED = s__('Runners|Paused');
export const I18N_PAUSE_TOOLTIP = s__('Runners|Pause from accepting jobs');
export const I18N_PAUSED_DESCRIPTION = s__('Runners|Not accepting jobs');
@@ -77,20 +84,27 @@ export const I18N_DELETE_DISABLED_UNKNOWN_REASON = s__(
);
export const I18N_DELETED_TOAST = s__('Runners|Runner %{name} was deleted');
+// List
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.',
);
+export const I18N_VERSION_LABEL = s__('Runners|Version %{version}');
+export const I18N_LAST_CONTACT_LABEL = s__('Runners|Last contact: %{timeAgo}');
+export const I18N_CREATED_AT_LABEL = s__('Runners|Created %{timeAgo}');
// Runner details
export const I18N_DETAILS = s__('Runners|Details');
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');
export const I18N_NONE = __('None');
export const I18N_NO_JOBS_FOUND = s__('Runners|This runner has not run any jobs.');
+export const I18N_NO_PROJECTS_FOUND = __('No projects found');
// Styles
-export const RUNNER_TAG_BADGE_VARIANT = 'neutral';
+export const RUNNER_TAG_BADGE_VARIANT = 'info';
export const RUNNER_TAG_BG_CLASS = 'gl-bg-blue-100';
// Filtered search parameter names
diff --git a/app/assets/javascripts/runner/graphql/list/list_item_shared.fragment.graphql b/app/assets/javascripts/runner/graphql/list/list_item_shared.fragment.graphql
index ce23bddb898..a12ba7a751a 100644
--- a/app/assets/javascripts/runner/graphql/list/list_item_shared.fragment.graphql
+++ b/app/assets/javascripts/runner/graphql/list/list_item_shared.fragment.graphql
@@ -9,6 +9,7 @@ fragment ListItemShared on CiRunner {
locked
jobCount
tagList
+ createdAt
contactedAt
status(legacyMode: null)
userPermissions {
diff --git a/app/assets/javascripts/runner/graphql/show/runner_details_shared.fragment.graphql b/app/assets/javascripts/runner/graphql/show/runner_details_shared.fragment.graphql
index 499c0156770..b5689ff7687 100644
--- a/app/assets/javascripts/runner/graphql/show/runner_details_shared.fragment.graphql
+++ b/app/assets/javascripts/runner/graphql/show/runner_details_shared.fragment.graphql
@@ -17,6 +17,7 @@ fragment RunnerDetailsShared on CiRunner {
createdAt
status(legacyMode: null)
contactedAt
+ tokenExpiresAt
version
editAdminUrl
userPermissions {
diff --git a/app/assets/javascripts/runner/graphql/show/runner_projects.query.graphql b/app/assets/javascripts/runner/graphql/show/runner_projects.query.graphql
index acc4a641565..e42648b3079 100644
--- a/app/assets/javascripts/runner/graphql/show/runner_projects.query.graphql
+++ b/app/assets/javascripts/runner/graphql/show/runner_projects.query.graphql
@@ -2,6 +2,7 @@
query getRunnerProjects(
$id: CiRunnerID!
+ $search: String
$first: Int
$last: Int
$before: String
@@ -13,7 +14,7 @@ query getRunnerProjects(
id
}
projectCount
- projects(first: $first, last: $last, before: $before, after: $after) {
+ projects(search: $search, first: $first, last: $last, before: $before, after: $after) {
nodes {
id
avatarUrl
diff --git a/app/assets/javascripts/runner/group_runner_show/index.js b/app/assets/javascripts/runner/group_runner_show/index.js
index 62a0dab9211..e75f337b38e 100644
--- a/app/assets/javascripts/runner/group_runner_show/index.js
+++ b/app/assets/javascripts/runner/group_runner_show/index.js
@@ -1,11 +1,14 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
+import { showAlertFromLocalStorage } from '../local_storage_alert/show_alert_from_local_storage';
import GroupRunnerShowApp from './group_runner_show_app.vue';
Vue.use(VueApollo);
export const initGroupRunnerShow = (selector = '#js-group-runner-show') => {
+ showAlertFromLocalStorage();
+
const el = document.querySelector(selector);
if (!el) {
diff --git a/app/assets/javascripts/runner/group_runners/group_runners_app.vue b/app/assets/javascripts/runner/group_runners/group_runners_app.vue
index a82411a2120..70826a6bfa1 100644
--- a/app/assets/javascripts/runner/group_runners/group_runners_app.vue
+++ b/app/assets/javascripts/runner/group_runners/group_runners_app.vue
@@ -13,6 +13,7 @@ import {
import groupRunnersQuery from 'ee_else_ce/runner/graphql/list/group_runners.query.graphql';
import RegistrationDropdown from '../components/registration/registration_dropdown.vue';
+import RunnerStackedLayoutBanner from '../components/runner_stacked_layout_banner.vue';
import RunnerFilteredSearchBar from '../components/runner_filtered_search_bar.vue';
import RunnerList from '../components/runner_list.vue';
import RunnerListEmptyState from '../components/runner_list_empty_state.vue';
@@ -37,6 +38,7 @@ export default {
components: {
GlLink,
RegistrationDropdown,
+ RunnerStackedLayoutBanner,
RunnerFilteredSearchBar,
RunnerList,
RunnerListEmptyState,
@@ -50,7 +52,8 @@ export default {
props: {
registrationToken: {
type: String,
- required: true,
+ required: false,
+ default: null,
},
groupFullPath: {
type: String,
@@ -178,6 +181,8 @@ export default {
<template>
<div>
+ <runner-stacked-layout-banner />
+
<div class="gl-display-flex gl-align-items-center">
<runner-type-tabs
ref="runner-type-tabs"
@@ -191,6 +196,7 @@ export default {
/>
<registration-dropdown
+ v-if="registrationToken"
class="gl-ml-auto"
:registration-token="registrationToken"
:type="$options.GROUP_TYPE"
diff --git a/app/assets/javascripts/runner/runner_edit/index.js b/app/assets/javascripts/runner/runner_edit/index.js
new file mode 100644
index 00000000000..5b2ddb8f68e
--- /dev/null
+++ b/app/assets/javascripts/runner/runner_edit/index.js
@@ -0,0 +1,33 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
+import RunnerEditApp from './runner_edit_app.vue';
+
+Vue.use(VueApollo);
+
+export const initRunnerEdit = (selector) => {
+ const el = document.querySelector(selector);
+
+ if (!el) {
+ return null;
+ }
+
+ const { runnerId, runnerPath } = el.dataset;
+
+ const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient(),
+ });
+
+ return new Vue({
+ el,
+ apolloProvider,
+ render(h) {
+ return h(RunnerEditApp, {
+ props: {
+ runnerId,
+ runnerPath,
+ },
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/runner/runner_edit/runner_edit_app.vue b/app/assets/javascripts/runner/runner_edit/runner_edit_app.vue
new file mode 100644
index 00000000000..879162916a9
--- /dev/null
+++ b/app/assets/javascripts/runner/runner_edit/runner_edit_app.vue
@@ -0,0 +1,73 @@
+<script>
+import { createAlert } from '~/flash';
+import { TYPE_CI_RUNNER } from '~/graphql_shared/constants';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
+import RunnerHeader from '../components/runner_header.vue';
+import RunnerUpdateForm from '../components/runner_update_form.vue';
+import { I18N_FETCH_ERROR } from '../constants';
+import runnerFormQuery from '../graphql/edit/runner_form.query.graphql';
+import { captureException } from '../sentry_utils';
+
+export default {
+ name: 'RunnerEditApp',
+ components: {
+ RunnerHeader,
+ RunnerUpdateForm,
+ },
+ props: {
+ runnerId: {
+ type: String,
+ required: true,
+ },
+ runnerPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+ data() {
+ return {
+ runner: null,
+ };
+ },
+ apollo: {
+ runner: {
+ query: runnerFormQuery,
+ variables() {
+ return {
+ id: convertToGraphQLId(TYPE_CI_RUNNER, this.runnerId),
+ };
+ },
+ error(error) {
+ createAlert({ message: I18N_FETCH_ERROR });
+
+ this.reportToSentry(error);
+ },
+ },
+ },
+ computed: {
+ loading() {
+ return this.$apollo.queries.runner.loading;
+ },
+ },
+ errorCaptured(error) {
+ this.reportToSentry(error);
+ },
+ methods: {
+ reportToSentry(error) {
+ captureException({ error, component: this.$options.name });
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <runner-header v-if="runner" :runner="runner" />
+ <runner-update-form
+ :loading="loading"
+ :runner="runner"
+ :runner-path="runnerPath"
+ class="gl-my-5"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/runner/utils.js b/app/assets/javascripts/runner/utils.js
index cb2917a92fd..1ca0a9e86b5 100644
--- a/app/assets/javascripts/runner/utils.js
+++ b/app/assets/javascripts/runner/utils.js
@@ -70,3 +70,14 @@ export const getPaginationVariables = (pagination, pageSize = 10) => {
// Get the first N items
return { first: pageSize };
};
+
+/**
+ * Turns a server-provided interval integer represented as a string into an
+ * integer that the frontend can use.
+ *
+ * @param {String} interval - String to convert
+ * @returns Parsed integer
+ */
+export const parseInterval = (interval) => {
+ return typeof interval === 'string' ? parseInt(interval, 10) : null;
+};
diff --git a/app/assets/javascripts/search/index.js b/app/assets/javascripts/search/index.js
index d9d4056466a..446ab7f433c 100644
--- a/app/assets/javascripts/search/index.js
+++ b/app/assets/javascripts/search/index.js
@@ -1,11 +1,11 @@
import setHighlightClass from 'ee_else_ce/search/highlight_blob_search_result';
import { queryToObject } from '~/lib/utils/url_utility';
-import Project from '~/pages/projects/project';
import refreshCounts from '~/pages/search/show/refresh_counts';
import { initSidebar } from './sidebar';
import { initSearchSort } from './sort';
import createStore from './store';
import { initTopbar } from './topbar';
+import { initBlobRefSwitcher } from './under_topbar';
export const initSearchApp = () => {
const query = queryToObject(window.location.search);
@@ -18,5 +18,5 @@ export const initSearchApp = () => {
setHighlightClass(query.search); // Code Highlighting
refreshCounts(); // Other Scope Tab Counts
- Project.initRefSwitcher(); // Code Search Branch Picker
+ initBlobRefSwitcher(); // Code Search Branch Picker
};
diff --git a/app/assets/javascripts/search/topbar/components/searchable_dropdown.vue b/app/assets/javascripts/search/topbar/components/searchable_dropdown.vue
index 5653cddda60..ff639d538b3 100644
--- a/app/assets/javascripts/search/topbar/components/searchable_dropdown.vue
+++ b/app/assets/javascripts/search/topbar/components/searchable_dropdown.vue
@@ -144,9 +144,9 @@ export default {
/>
<gl-dropdown-item
class="gl-border-b-solid gl-border-b-gray-100 gl-border-b-1 gl-pb-2! gl-mb-2"
- :is-check-item="true"
+ is-check-item
:is-checked="isSelected($options.ANY_OPTION)"
- :is-check-centered="true"
+ is-check-centered
@click="updateDropdown($options.ANY_OPTION)"
>
<span data-testid="item-title">{{ $options.ANY_OPTION.name }}</span>
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 a4254a355a2..70156142365 100644
--- a/app/assets/javascripts/search/topbar/components/searchable_dropdown_item.vue
+++ b/app/assets/javascripts/search/topbar/components/searchable_dropdown_item.vue
@@ -53,9 +53,9 @@ export default {
<template>
<gl-dropdown-item
- :is-check-item="true"
+ is-check-item
:is-checked="isSelected"
- :is-check-centered="true"
+ is-check-centered
@click="$emit('change', item)"
>
<div class="gl-display-flex gl-align-items-center">
diff --git a/app/assets/javascripts/search/under_topbar/index.js b/app/assets/javascripts/search/under_topbar/index.js
new file mode 100644
index 00000000000..8e50c6655dd
--- /dev/null
+++ b/app/assets/javascripts/search/under_topbar/index.js
@@ -0,0 +1,31 @@
+import Vue from 'vue';
+import Translate from '~/vue_shared/translate';
+import RefSelector from '~/ref/components/ref_selector.vue';
+import { setUrlParams, visitUrl } from '~/lib/utils/url_utility';
+
+Vue.use(Translate);
+
+export const initBlobRefSwitcher = () => {
+ const el = document.getElementById('js-blob-ref-switcher');
+
+ if (!el) return false;
+
+ const { projectId, ref, fieldName } = el.dataset;
+
+ return new Vue({
+ el,
+ render(createElement) {
+ return createElement(RefSelector, {
+ props: {
+ projectId,
+ value: ref,
+ },
+ on: {
+ input(selected) {
+ visitUrl(setUrlParams({ [fieldName]: selected }));
+ },
+ },
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/security_configuration/components/constants.js b/app/assets/javascripts/security_configuration/components/constants.js
index 5a9ef832e05..77216408c39 100644
--- a/app/assets/javascripts/security_configuration/components/constants.js
+++ b/app/assets/javascripts/security_configuration/components/constants.js
@@ -157,7 +157,7 @@ export const SCANNER_NAMES_MAP = {
COVERAGE_FUZZING: COVERAGE_FUZZING_NAME,
SECRET_DETECTION: SECRET_DETECTION_NAME,
DEPENDENCY_SCANNING: DEPENDENCY_SCANNING_NAME,
- GENERIC: s__('ciReport|Manually Added'),
+ GENERIC: s__('ciReport|Manually added'),
};
export const securityFeatures = [
diff --git a/app/assets/javascripts/set_status_modal/constants.js b/app/assets/javascripts/set_status_modal/constants.js
new file mode 100644
index 00000000000..53e64db1497
--- /dev/null
+++ b/app/assets/javascripts/set_status_modal/constants.js
@@ -0,0 +1,14 @@
+import { timeRanges } from '~/vue_shared/constants';
+import { __ } from '~/locale';
+
+export const NEVER_TIME_RANGE = {
+ label: __('Never'),
+ name: 'never',
+};
+
+export const TIME_RANGES_WITH_NEVER = [NEVER_TIME_RANGE, ...timeRanges];
+
+export const AVAILABILITY_STATUS = {
+ BUSY: 'busy',
+ NOT_SET: 'not_set',
+};
diff --git a/app/assets/javascripts/set_status_modal/set_status_form.vue b/app/assets/javascripts/set_status_modal/set_status_form.vue
new file mode 100644
index 00000000000..7f9a30b7ff1
--- /dev/null
+++ b/app/assets/javascripts/set_status_modal/set_status_form.vue
@@ -0,0 +1,231 @@
+<script>
+import {
+ GlButton,
+ GlTooltipDirective,
+ GlIcon,
+ GlFormCheckbox,
+ GlFormInput,
+ GlFormInputGroup,
+ GlDropdown,
+ GlDropdownItem,
+ GlSprintf,
+ GlFormGroup,
+ GlSafeHtmlDirective,
+} from '@gitlab/ui';
+import $ from 'jquery';
+import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
+import * as Emoji from '~/emoji';
+import { s__ } from '~/locale';
+import { TIME_RANGES_WITH_NEVER, AVAILABILITY_STATUS } from './constants';
+
+export default {
+ components: {
+ GlButton,
+ GlIcon,
+ GlFormCheckbox,
+ GlFormInput,
+ GlFormInputGroup,
+ GlDropdown,
+ GlDropdownItem,
+ GlSprintf,
+ GlFormGroup,
+ EmojiPicker: () => import('~/emoji/components/picker.vue'),
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ SafeHtml: GlSafeHtmlDirective,
+ },
+ props: {
+ defaultEmoji: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ emoji: {
+ type: String,
+ required: true,
+ },
+ message: {
+ type: String,
+ required: true,
+ },
+ availability: {
+ type: Boolean,
+ required: true,
+ },
+ clearStatusAfter: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ currentClearStatusAfter: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ data() {
+ return {
+ defaultEmojiTag: '',
+ emojiTag: '',
+ };
+ },
+ computed: {
+ isCustomEmoji() {
+ return this.emoji !== this.defaultEmoji;
+ },
+ isDirty() {
+ return Boolean(this.message.length || this.isCustomEmoji);
+ },
+ noEmoji() {
+ return this.emojiTag === '';
+ },
+ },
+ mounted() {
+ this.setupEmojiListAndAutocomplete();
+ },
+ methods: {
+ async setupEmojiListAndAutocomplete() {
+ const emojiAutocomplete = new GfmAutoComplete();
+ emojiAutocomplete.setup($(this.$refs.statusMessageField.$el), { emojis: true });
+
+ if (this.emoji) {
+ this.emojiTag = Emoji.glEmojiTag(this.emoji);
+ }
+ this.defaultEmojiTag = Emoji.glEmojiTag(this.defaultEmoji);
+
+ this.setDefaultEmoji();
+ },
+ setDefaultEmoji() {
+ const { emojiTag } = this;
+ const hasStatusMessage = Boolean(this.message.length);
+ if (hasStatusMessage && emojiTag) {
+ return;
+ }
+
+ if (hasStatusMessage) {
+ this.emojiTag = this.defaultEmojiTag;
+ } else if (emojiTag === this.defaultEmojiTag) {
+ this.clearEmoji();
+ }
+ },
+ handleEmojiClick(emoji) {
+ this.$emit('emoji-click', emoji);
+
+ this.emojiTag = Emoji.glEmojiTag(emoji);
+ },
+ clearEmoji() {
+ if (this.emojiTag) {
+ this.emojiTag = '';
+ }
+ },
+ clearStatusInputs() {
+ this.$emit('emoji-click', '');
+ this.$emit('message-input', '');
+ this.clearEmoji();
+ },
+ },
+ TIME_RANGES_WITH_NEVER,
+ AVAILABILITY_STATUS,
+ safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
+ i18n: {
+ statusMessagePlaceholder: s__(`SetStatusModal|What's your status?`),
+ clearStatusButtonLabel: s__('SetStatusModal|Clear status'),
+ availabilityCheckboxLabel: s__('SetStatusModal|Busy'),
+ availabilityCheckboxHelpText: s__(
+ 'SetStatusModal|An indicator appears next to your name and avatar',
+ ),
+ clearStatusAfterDropdownLabel: s__('SetStatusModal|Clear status after'),
+ clearStatusAfterMessage: s__('SetStatusModal|Your status resets on %{date}.'),
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-form-input-group class="gl-mb-5">
+ <gl-form-input
+ ref="statusMessageField"
+ :value="message"
+ :placeholder="$options.i18n.statusMessagePlaceholder"
+ @keyup="setDefaultEmoji"
+ @input="$emit('message-input', $event)"
+ @keyup.enter.prevent
+ />
+ <template #prepend>
+ <emoji-picker
+ dropdown-class="gl-h-full"
+ toggle-class="btn emoji-menu-toggle-button gl-px-4! gl-rounded-top-right-none! gl-rounded-bottom-right-none!"
+ boundary="viewport"
+ :right="false"
+ @click="handleEmojiClick"
+ >
+ <template #button-content>
+ <span
+ v-if="noEmoji"
+ class="no-emoji-placeholder position-relative"
+ data-testid="no-emoji-placeholder"
+ >
+ <gl-icon name="slight-smile" class="award-control-icon-neutral" />
+ <gl-icon name="smiley" class="award-control-icon-positive" />
+ <gl-icon name="smile" class="award-control-icon-super-positive" />
+ </span>
+ <span v-else>
+ <span
+ v-safe-html:[$options.safeHtmlConfig]="emojiTag"
+ data-testid="selected-emoji"
+ ></span>
+ </span>
+ </template>
+ </emoji-picker>
+ </template>
+ <template v-if="isDirty" #append>
+ <gl-button
+ v-gl-tooltip.bottom
+ :title="$options.i18n.clearStatusButtonLabel"
+ :aria-label="$options.i18n.clearStatusButtonLabel"
+ icon="close"
+ class="js-clear-user-status-button"
+ @click="clearStatusInputs"
+ />
+ </template>
+ </gl-form-input-group>
+
+ <gl-form-checkbox
+ :checked="availability"
+ class="gl-mb-5"
+ data-testid="user-availability-checkbox"
+ @input="$emit('availability-input', $event)"
+ >
+ {{ $options.i18n.availabilityCheckboxLabel }}
+ <template #help>
+ {{ $options.i18n.availabilityCheckboxHelpText }}
+ </template>
+ </gl-form-checkbox>
+
+ <gl-form-group :label="$options.i18n.clearStatusAfterDropdownLabel" class="gl-mb-0">
+ <gl-dropdown
+ block
+ :text="clearStatusAfter.label"
+ data-testid="clear-status-at-dropdown"
+ toggle-class="gl-mb-0 gl-form-input-md"
+ >
+ <gl-dropdown-item
+ v-for="after in $options.TIME_RANGES_WITH_NEVER"
+ :key="after.name"
+ :data-testid="after.name"
+ @click="$emit('clear-status-after-click', after)"
+ >{{ after.label }}</gl-dropdown-item
+ >
+ </gl-dropdown>
+
+ <template v-if="currentClearStatusAfter.length" #description>
+ <span data-testid="clear-status-at-message">
+ <gl-sprintf :message="$options.i18n.clearStatusAfterMessage">
+ <template #date>{{ currentClearStatusAfter }}</template>
+ </gl-sprintf>
+ </span>
+ </template>
+ </gl-form-group>
+ </div>
+</template>
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 2cdec8fc481..80b1cb8c4d5 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,55 +1,21 @@
<script>
-import {
- GlButton,
- GlToast,
- GlModal,
- GlTooltipDirective,
- GlIcon,
- GlFormCheckbox,
- GlFormInput,
- GlFormInputGroup,
- GlDropdown,
- GlDropdownItem,
- GlSafeHtmlDirective,
-} from '@gitlab/ui';
-import $ from 'jquery';
+import { GlToast, GlTooltipDirective, GlSafeHtmlDirective, GlModal } from '@gitlab/ui';
import Vue from 'vue';
-import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
-import * as Emoji from '~/emoji';
import createFlash from '~/flash';
import { BV_SHOW_MODAL, BV_HIDE_MODAL } from '~/lib/utils/constants';
-import { __, s__, sprintf } from '~/locale';
+import { s__ } from '~/locale';
import { updateUserStatus } from '~/rest_api';
-import { timeRanges } from '~/vue_shared/constants';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { isUserBusy } from './utils';
-
-export const AVAILABILITY_STATUS = {
- BUSY: 'busy',
- NOT_SET: 'not_set',
-};
+import { NEVER_TIME_RANGE, AVAILABILITY_STATUS } from './constants';
+import SetStatusForm from './set_status_form.vue';
Vue.use(GlToast);
-const statusTimeRanges = [
- {
- label: __('Never'),
- name: 'never',
- },
- ...timeRanges,
-];
-
export default {
components: {
- GlButton,
- GlIcon,
GlModal,
- GlFormCheckbox,
- GlFormInput,
- GlFormInputGroup,
- GlDropdown,
- GlDropdownItem,
- EmojiPicker: () => import('~/emoji/components/picker.vue'),
+ SetStatusForm,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -85,26 +51,12 @@ export default {
return {
defaultEmojiTag: '',
emoji: this.currentEmoji,
- emojiMenu: null,
- emojiTag: '',
message: this.currentMessage,
modalId: 'set-user-status-modal',
- noEmoji: true,
availability: isUserBusy(this.currentAvailability),
- clearStatusAfter: statusTimeRanges[0],
- clearStatusAfterMessage: sprintf(s__('SetStatusModal|Your status resets on %{date}.'), {
- date: this.currentClearStatusAfter,
- }),
+ clearStatusAfter: NEVER_TIME_RANGE,
};
},
- computed: {
- isCustomEmoji() {
- return this.emoji !== this.defaultEmoji;
- },
- isDirty() {
- return Boolean(this.message.length || this.isCustomEmoji);
- },
- },
mounted() {
this.$root.$emit(BV_SHOW_MODAL, this.modalId);
},
@@ -112,62 +64,10 @@ export default {
closeModal() {
this.$root.$emit(BV_HIDE_MODAL, this.modalId);
},
- setupEmojiListAndAutocomplete() {
- const emojiAutocomplete = new GfmAutoComplete();
- emojiAutocomplete.setup($(this.$refs.statusMessageField), { emojis: true });
-
- Emoji.initEmojiMap()
- .then(() => {
- if (this.emoji) {
- this.emojiTag = Emoji.glEmojiTag(this.emoji);
- }
- this.noEmoji = this.emoji === '';
- this.defaultEmojiTag = Emoji.glEmojiTag(this.defaultEmoji);
-
- this.setDefaultEmoji();
- })
- .catch(() =>
- createFlash({
- message: __('Failed to load emoji list.'),
- }),
- );
- },
- setDefaultEmoji() {
- const { emojiTag } = this;
- const hasStatusMessage = Boolean(this.message.length);
- if (hasStatusMessage && emojiTag) {
- return;
- }
-
- if (hasStatusMessage) {
- this.noEmoji = false;
- this.emojiTag = this.defaultEmojiTag;
- } else if (emojiTag === this.defaultEmojiTag) {
- this.noEmoji = true;
- this.clearEmoji();
- }
- },
- setEmoji(emoji) {
- this.emoji = emoji;
- this.noEmoji = false;
- this.clearEmoji();
-
- this.emojiTag = Emoji.glEmojiTag(this.emoji);
- },
- clearEmoji() {
- if (this.emojiTag) {
- this.emojiTag = '';
- }
- },
- clearStatusInputs() {
- this.emoji = '';
- this.message = '';
- this.noEmoji = true;
- this.clearEmoji();
- },
removeStatus() {
this.availability = false;
- this.clearStatusInputs();
+ this.emoji = '';
+ this.message = '';
this.setStatus();
},
setStatus() {
@@ -178,7 +78,7 @@ export default {
message,
availability: availability ? AVAILABILITY_STATUS.BUSY : AVAILABILITY_STATUS.NOT_SET,
clearStatusAfter:
- clearStatusAfter.label === statusTimeRanges[0].label ? null : clearStatusAfter.shortcut,
+ clearStatusAfter.label === NEVER_TIME_RANGE.label ? null : clearStatusAfter.shortcut,
})
.then(this.onUpdateSuccess)
.catch(this.onUpdateFail);
@@ -197,11 +97,19 @@ export default {
this.closeModal();
},
- setClearStatusAfter(after) {
+ handleMessageInput(value) {
+ this.message = value;
+ },
+ handleEmojiClick(emoji) {
+ this.emoji = emoji;
+ },
+ handleClearStatusAfterClick(after) {
this.clearStatusAfter = after;
},
+ handleAvailabilityInput(value) {
+ this.availability = value;
+ },
},
- statusTimeRanges,
safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
actionPrimary: { text: s__('SetStatusModal|Set status') },
actionSecondary: { text: s__('SetStatusModal|Remove status') },
@@ -215,85 +123,20 @@ export default {
:action-primary="$options.actionPrimary"
:action-secondary="$options.actionSecondary"
modal-class="set-user-status-modal"
- @shown="setupEmojiListAndAutocomplete"
@primary="setStatus"
@secondary="removeStatus"
>
- <input v-model="emoji" class="js-status-emoji-field" type="hidden" name="user[status][emoji]" />
- <gl-form-input-group class="gl-mb-5">
- <gl-form-input
- ref="statusMessageField"
- v-model="message"
- :placeholder="s__(`SetStatusModal|What's your status?`)"
- class="js-status-message-field"
- name="user[status][message]"
- @keyup="setDefaultEmoji"
- @keyup.enter.prevent
- />
- <template #prepend>
- <emoji-picker
- dropdown-class="gl-h-full"
- toggle-class="btn emoji-menu-toggle-button gl-px-4! gl-rounded-top-right-none! gl-rounded-bottom-right-none!"
- boundary="viewport"
- :right="false"
- @click="setEmoji"
- >
- <template #button-content>
- <span v-safe-html:[$options.safeHtmlConfig]="emojiTag"></span>
- <span
- v-show="noEmoji"
- class="js-no-emoji-placeholder no-emoji-placeholder position-relative"
- >
- <gl-icon name="slight-smile" class="award-control-icon-neutral" />
- <gl-icon name="smiley" class="award-control-icon-positive" />
- <gl-icon name="smile" class="award-control-icon-super-positive" />
- </span>
- </template>
- </emoji-picker>
- </template>
- <template v-if="isDirty" #append>
- <gl-button
- v-gl-tooltip.bottom
- :title="s__('SetStatusModal|Clear status')"
- :aria-label="s__('SetStatusModal|Clear status')"
- icon="close"
- class="js-clear-user-status-button"
- @click="clearStatusInputs"
- />
- </template>
- </gl-form-input-group>
-
- <gl-form-checkbox
- v-model="availability"
- class="gl-mb-5"
- data-testid="user-availability-checkbox"
- >
- {{ s__('SetStatusModal|Busy') }}
- <template #help>
- {{ s__('SetStatusModal|An indicator appears next to your name and avatar') }}
- </template>
- </gl-form-checkbox>
-
- <div class="form-group">
- <div class="gl-display-flex gl-align-items-baseline">
- <span class="gl-mr-3">{{ s__('SetStatusModal|Clear status after') }}</span>
- <gl-dropdown :text="clearStatusAfter.label" data-testid="clear-status-at-dropdown">
- <gl-dropdown-item
- v-for="after in $options.statusTimeRanges"
- :key="after.name"
- :data-testid="after.name"
- @click="setClearStatusAfter(after)"
- >{{ after.label }}</gl-dropdown-item
- >
- </gl-dropdown>
- </div>
- <div
- v-if="currentClearStatusAfter.length"
- class="gl-mt-3 gl-text-gray-400 gl-font-sm"
- data-testid="clear-status-at-message"
- >
- {{ clearStatusAfterMessage }}
- </div>
- </div>
+ <set-status-form
+ :default-emoji="defaultEmoji"
+ :emoji="emoji"
+ :message="message"
+ :availability="availability"
+ :clear-status-after="clearStatusAfter"
+ :current-clear-status-after="currentClearStatusAfter"
+ @message-input="handleMessageInput"
+ @emoji-click="handleEmojiClick"
+ @clear-status-after-click="handleClearStatusAfterClick"
+ @availability-input="handleAvailabilityInput"
+ />
</gl-modal>
</template>
diff --git a/app/assets/javascripts/set_status_modal/user_profile_set_status_wrapper.vue b/app/assets/javascripts/set_status_modal/user_profile_set_status_wrapper.vue
new file mode 100644
index 00000000000..c709611e13d
--- /dev/null
+++ b/app/assets/javascripts/set_status_modal/user_profile_set_status_wrapper.vue
@@ -0,0 +1,100 @@
+<script>
+import { secondsToMilliseconds } from '~/lib/utils/datetime_utility';
+import dateFormat from '~/lib/dateformat';
+import SetStatusForm from './set_status_form.vue';
+import { isUserBusy } from './utils';
+import { NEVER_TIME_RANGE, AVAILABILITY_STATUS } from './constants';
+
+export default {
+ components: { SetStatusForm },
+ inject: ['fields'],
+ data() {
+ return {
+ emoji: this.fields.emoji.value,
+ message: this.fields.message.value,
+ availability: isUserBusy(this.fields.availability.value),
+ clearStatusAfter: NEVER_TIME_RANGE,
+ currentClearStatusAfter: this.fields.clearStatusAfter.value,
+ };
+ },
+ computed: {
+ clearStatusAfterInputValue() {
+ return this.clearStatusAfter.label === NEVER_TIME_RANGE.label
+ ? null
+ : this.clearStatusAfter.shortcut;
+ },
+ availabilityInputValue() {
+ return this.availability
+ ? this.$options.AVAILABILITY_STATUS.BUSY
+ : this.$options.AVAILABILITY_STATUS.NOT_SET;
+ },
+ },
+ mounted() {
+ this.$options.formEl = document.querySelector('form.js-edit-user');
+
+ if (!this.$options.formEl) return;
+
+ this.$options.formEl.addEventListener('ajax:success', this.handleFormSuccess);
+ },
+ beforeDestroy() {
+ if (!this.$options.formEl) return;
+
+ this.$options.formEl.removeEventListener('ajax:success', this.handleFormSuccess);
+ },
+ methods: {
+ handleMessageInput(value) {
+ this.message = value;
+ },
+ handleEmojiClick(emoji) {
+ this.emoji = emoji;
+ },
+ handleClearStatusAfterClick(after) {
+ this.clearStatusAfter = after;
+ },
+ handleAvailabilityInput(value) {
+ this.availability = value;
+ },
+ handleFormSuccess() {
+ if (!this.clearStatusAfter?.duration?.seconds) {
+ this.currentClearStatusAfter = '';
+
+ return;
+ }
+
+ const now = new Date();
+ const currentClearStatusAfterDate = new Date(
+ now.getTime() + secondsToMilliseconds(this.clearStatusAfter.duration.seconds),
+ );
+
+ this.currentClearStatusAfter = dateFormat(
+ currentClearStatusAfterDate,
+ "UTC:yyyy-mm-dd HH:MM:ss 'UTC'",
+ );
+ this.clearStatusAfter = NEVER_TIME_RANGE;
+ },
+ },
+ AVAILABILITY_STATUS,
+ formEl: null,
+};
+</script>
+
+<template>
+ <div>
+ <input :value="emoji" type="hidden" :name="fields.emoji.name" />
+ <input :value="message" type="hidden" :name="fields.message.name" />
+ <input :value="availabilityInputValue" type="hidden" :name="fields.availability.name" />
+ <input :value="clearStatusAfterInputValue" type="hidden" :name="fields.clearStatusAfter.name" />
+ <set-status-form
+ default-emoji="speech_balloon"
+ :emoji="emoji"
+ :message="message"
+ :availability="availability"
+ :clear-status-after="clearStatusAfter"
+ :current-clear-status-after="currentClearStatusAfter"
+ @message-input="handleMessageInput"
+ @emoji-click="handleEmojiClick"
+ @clear-status-after-click="handleClearStatusAfterClick"
+ @availability-input="handleAvailabilityInput"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/set_status_modal/utils.js b/app/assets/javascripts/set_status_modal/utils.js
index e17d95adb25..950091195d2 100644
--- a/app/assets/javascripts/set_status_modal/utils.js
+++ b/app/assets/javascripts/set_status_modal/utils.js
@@ -1,7 +1,4 @@
-export const AVAILABILITY_STATUS = {
- BUSY: 'busy',
- NOT_SET: 'not_set',
-};
+import { AVAILABILITY_STATUS } from './constants';
export const isUserBusy = (status = '') =>
Boolean(status.length && status.toLowerCase().trim() === AVAILABILITY_STATUS.BUSY);
diff --git a/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue b/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue
index a94dd128a1a..4408ebb881b 100644
--- a/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue
@@ -23,6 +23,10 @@ export default {
required: false,
default: false,
},
+ editable: {
+ type: Boolean,
+ required: true,
+ },
},
computed: {
assigneesText() {
@@ -43,7 +47,7 @@ export default {
data-testid="none"
>
<span> {{ __('None') }}</span>
- <template v-if="signedIn">
+ <template v-if="signedIn && editable">
<span class="gl-ml-2">-</span>
<gl-button
data-testid="assign-yourself"
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 5c432ca0e03..26fda2a823c 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
@@ -72,6 +72,10 @@ export default {
type: Boolean,
required: true,
},
+ editable: {
+ type: Boolean,
+ required: true,
+ },
},
data() {
return {
@@ -252,6 +256,7 @@ export default {
:users="assignees"
:issuable-type="issuableType"
:signed-in="signedIn"
+ :editable="editable"
@assign-self="assignSelf"
@expand-widget="expandWidget"
/>
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 336c291d4f1..c44ce8b0057 100644
--- a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue
@@ -88,10 +88,7 @@ export default {
.then(
({
data: {
- issuableSetConfidential: {
- issuable: { confidential },
- errors,
- },
+ issuableSetConfidential: { errors },
},
}) => {
if (errors.length) {
@@ -99,7 +96,7 @@ export default {
message: errors[0],
});
} else {
- this.$emit('closeForm', { confidential });
+ this.$emit('closeForm');
}
},
)
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 eec083f23f3..f234c5ea3c9 100644
--- a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue
@@ -95,10 +95,10 @@ export default {
confidentialWidget.setConfidentiality = null;
},
methods: {
- closeForm({ confidential } = {}) {
+ closeForm() {
this.$refs.editable.collapse();
this.$el.dispatchEvent(hideDropdownEvent);
- this.$emit('closeForm', { confidential });
+ this.$emit('closeForm');
},
// synchronizing the quick action with the sidebar widget
// this is a temporary solution until we have confidentiality real-time updates
diff --git a/app/assets/javascripts/sidebar/components/copy_email_to_clipboard.vue b/app/assets/javascripts/sidebar/components/copy_email_to_clipboard.vue
index 8528ad56ddb..fd652583f76 100644
--- a/app/assets/javascripts/sidebar/components/copy_email_to_clipboard.vue
+++ b/app/assets/javascripts/sidebar/components/copy_email_to_clipboard.vue
@@ -16,7 +16,7 @@ export default {
<template>
<copyable-field
- data-qa-selector="copy-forward-email"
+ data-testid="copy-forward-email"
:name="s__('RightSidebar|Issue email')"
:clipboard-tooltip-text="s__('RightSidebar|Copy email address')"
:value="issueEmailAddress"
diff --git a/app/assets/javascripts/sidebar/components/incidents/escalation_status.vue b/app/assets/javascripts/sidebar/components/incidents/escalation_status.vue
index aeaac76cff4..9c41db98c63 100644
--- a/app/assets/javascripts/sidebar/components/incidents/escalation_status.vue
+++ b/app/assets/javascripts/sidebar/components/incidents/escalation_status.vue
@@ -62,7 +62,7 @@ export default {
v-for="status in $options.STATUS_LIST"
:key="status"
data-testid="status-dropdown-item"
- :is-check-item="true"
+ is-check-item
:is-checked="status === value"
@click="$emit('input', status)"
>
diff --git a/app/assets/javascripts/sidebar/components/lock/edit_form.vue b/app/assets/javascripts/sidebar/components/lock/edit_form.vue
index 65b51169420..c9e651370f9 100644
--- a/app/assets/javascripts/sidebar/components/lock/edit_form.vue
+++ b/app/assets/javascripts/sidebar/components/lock/edit_form.vue
@@ -1,10 +1,10 @@
<script>
import { GlSprintf } from '@gitlab/ui';
-import editFormButtons from './edit_form_buttons.vue';
+import EditFormButtons from './edit_form_buttons.vue';
export default {
components: {
- editFormButtons,
+ EditFormButtons,
GlSprintf,
},
props: {
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 5f1808ff4da..286bd50f6dd 100644
--- a/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
+++ b/app/assets/javascripts/sidebar/components/lock/issuable_lock_form.vue
@@ -6,7 +6,7 @@ import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import createFlash from '~/flash';
import eventHub from '~/sidebar/event_hub';
import toast from '~/vue_shared/plugins/global_toast';
-import editForm from './edit_form.vue';
+import EditForm from './edit_form.vue';
export default {
issue: 'issue',
@@ -23,7 +23,7 @@ export default {
displayText: __('Unlocked'),
},
components: {
- editForm,
+ EditForm,
GlIcon,
},
directives: {
diff --git a/app/assets/javascripts/sidebar/components/participants/participants.vue b/app/assets/javascripts/sidebar/components/participants/participants.vue
index b8804de653f..2f25c2fd4b0 100644
--- a/app/assets/javascripts/sidebar/components/participants/participants.vue
+++ b/app/assets/javascripts/sidebar/components/participants/participants.vue
@@ -1,7 +1,7 @@
<script>
import { GlButton, GlIcon, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
import { __, n__, sprintf } from '~/locale';
-import userAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
+import UserAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
export default {
directives: {
@@ -11,7 +11,7 @@ export default {
GlButton,
GlIcon,
GlLoadingIcon,
- userAvatarImage,
+ UserAvatarImage,
},
props: {
loading: {
@@ -27,7 +27,7 @@ export default {
numberOfLessParticipants: {
type: Number,
required: false,
- default: 7,
+ default: 8,
},
showParticipantLabel: {
type: Boolean,
@@ -123,7 +123,7 @@ export default {
:size="24"
:tooltip-text="participant.name"
:img-alt="participant.name"
- css-classes="avatar-inline"
+ css-classes="gl-mr-0!"
tooltip-placement="bottom"
/>
</a>
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 a09138a708b..46a04725a49 100644
--- a/app/assets/javascripts/sidebar/components/participants/sidebar_participants_widget.vue
+++ b/app/assets/javascripts/sidebar/components/participants/sidebar_participants_widget.vue
@@ -49,6 +49,9 @@ export default {
error,
});
},
+ context: {
+ isSingleRequest: true,
+ },
},
},
computed: {
@@ -68,7 +71,7 @@ export default {
<participants
:loading="isLoading"
:participants="participants"
- :number-of-less-participants="7"
+ :number-of-less-participants="8"
:lazy="false"
class="block participants"
@toggleSidebar="toggleSidebar"
diff --git a/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue b/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue
index bf4ba715f85..a562df4ecd6 100644
--- a/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue
+++ b/app/assets/javascripts/sidebar/components/severity/sidebar_severity.vue
@@ -179,7 +179,7 @@ export default {
v-for="option in severitiesList"
:key="option.value"
data-testid="severityDropdownItem"
- :is-check-item="true"
+ is-check-item
:is-checked="option.value === severity"
@click="updateSeverity(option.value)"
>
diff --git a/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue b/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
index 3d8a2cd847c..6c615109bb8 100644
--- a/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
+++ b/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
@@ -9,6 +9,8 @@ import {
GlLoadingIcon,
GlIcon,
GlTooltipDirective,
+ GlPopover,
+ GlButton,
} from '@gitlab/ui';
import { kebabCase, snakeCase } from 'lodash';
import createFlash from '~/flash';
@@ -17,6 +19,7 @@ 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,
Tracking,
@@ -47,7 +50,10 @@ export default {
GlSearchBoxByType,
GlIcon,
GlLoadingIcon,
+ GlPopover,
+ GlButton,
},
+ mixins: [glFeatureFlagMixin()],
inject: {
isClassicSidebar: {
default: false,
@@ -66,6 +72,7 @@ export default {
},
},
},
+
props: {
issuableAttribute: {
type: String,
@@ -111,6 +118,10 @@ export default {
};
},
update(data) {
+ if (this.glFeatures?.epicWidgetEditConfirmation && this.isEpic) {
+ this.hasCurrentAttribute = data?.workspace?.issuable.hasEpic;
+ }
+
return data?.workspace?.issuable.attribute;
},
error(error) {
@@ -179,6 +190,8 @@ export default {
updating: false,
selectedTitle: null,
currentAttribute: null,
+ hasCurrentAttribute: false,
+ editConfirmation: false,
attributesList: [],
tracking: {
event: Tracking.editEvent,
@@ -228,6 +241,15 @@ export default {
snake: snakeCase(this.issuableAttribute),
};
},
+ shouldShowConfirmationPopover() {
+ if (!this.glFeatures?.epicWidgetEditConfirmation) {
+ return false;
+ }
+
+ return this.isEpic && this.currentAttribute === null && this.hasCurrentAttribute
+ ? !this.editConfirmation
+ : false;
+ },
},
methods: {
updateAttribute(attributeId) {
@@ -299,6 +321,17 @@ export default {
setFocus() {
this.$refs.search.focusInput();
},
+ handlePopoverClose() {
+ this.$refs.popover.$emit('close');
+ },
+ handlePopoverConfirm(cb) {
+ this.editConfirmation = true;
+ this.handlePopoverClose();
+ setTimeout(cb, 0);
+ },
+ handleEditConfirmation() {
+ this.$refs.popover.$emit('open');
+ },
},
};
</script>
@@ -308,10 +341,13 @@ export default {
ref="editable"
:title="attributeTypeTitle"
:data-testid="`${formatIssuableAttribute.kebab}-edit`"
+ :button-id="`${formatIssuableAttribute.kebab}-edit`"
:tracking="tracking"
+ :should-show-confirmation-popover="shouldShowConfirmationPopover"
:loading="updating || loading"
@open="handleOpen"
@close="handleClose"
+ @edit-confirm="handleEditConfirmation"
>
<template #collapsed>
<slot name="value-collapsed" :current-attribute="currentAttribute">
@@ -332,6 +368,10 @@ export default {
:class="isClassicSidebar ? 'hide-collapsed' : 'gl-mt-3'"
>
<span v-if="updating">{{ selectedTitle }}</span>
+ <template v-else-if="!currentAttribute && hasCurrentAttribute">
+ <gl-icon name="warning" class="gl-text-orange-500" />
+ <span class="gl-text-gray-500">{{ i18n.noPermissionToView }}</span>
+ </template>
<span v-else-if="!currentAttribute" class="gl-text-gray-500">
{{ $options.i18n.none }}
</span>
@@ -344,6 +384,7 @@ export default {
>
<gl-link
v-gl-tooltip="tooltipText"
+ class="gl-reset-color gl-hover-text-blue-800"
:href="attributeUrl"
:data-qa-selector="`${formatIssuableAttribute.snake}_link`"
>
@@ -353,7 +394,40 @@ export default {
</slot>
</div>
</template>
- <template #default>
+ <template v-if="shouldShowConfirmationPopover" #default="{ toggle }">
+ <gl-popover
+ ref="popover"
+ :target="`${formatIssuableAttribute.kebab}-edit`"
+ placement="bottomleft"
+ boundary="viewport"
+ triggers="click"
+ >
+ <div class="gl-mb-4 gl-font-base">
+ {{ i18n.editConfirmation }}
+ </div>
+ <div class="gl-display-flex gl-align-items-center">
+ <gl-button
+ size="small"
+ variant="confirm"
+ category="primary"
+ data-testid="confirm-edit-cta"
+ @click.prevent="() => handlePopoverConfirm(toggle)"
+ >{{ i18n.editConfirmationCta }}</gl-button
+ >
+ <gl-button
+ class="gl-ml-auto"
+ size="small"
+ name="cancel"
+ variant="default"
+ category="primary"
+ data-testid="confirm-edit-cancel"
+ @click.prevent="handlePopoverClose"
+ >{{ i18n.editConfirmationCancel }}</gl-button
+ >
+ </div>
+ </gl-popover>
+ </template>
+ <template v-else #default>
<gl-dropdown
ref="newDropdown"
lazy
@@ -368,7 +442,7 @@ export default {
<gl-search-box-by-type ref="search" v-model="searchTerm" />
<gl-dropdown-item
:data-testid="`no-${formatIssuableAttribute.kebab}-item`"
- :is-check-item="true"
+ is-check-item
:is-checked="isAttributeChecked($options.noAttributeId)"
@click="updateAttribute($options.noAttributeId)"
>
@@ -395,7 +469,7 @@ export default {
<gl-dropdown-item
v-for="attrItem in attributesList"
:key="attrItem.id"
- :is-check-item="true"
+ is-check-item
:is-checked="isAttributeChecked(attrItem.id)"
:data-testid="`${formatIssuableAttribute.kebab}-items`"
@click="updateAttribute(attrItem.id)"
diff --git a/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue b/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue
index 7551b181a58..cc88812c7b0 100644
--- a/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue
+++ b/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue
@@ -14,6 +14,11 @@ export default {
},
},
props: {
+ buttonId: {
+ type: String,
+ required: false,
+ default: '',
+ },
title: {
type: String,
required: false,
@@ -48,6 +53,11 @@ export default {
required: false,
default: true,
},
+ shouldShowConfirmationPopover: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -97,6 +107,11 @@ export default {
window.removeEventListener('keyup', this.collapseOnEscape);
},
toggle({ emitEvent = true } = {}) {
+ if (this.shouldShowConfirmationPopover) {
+ this.$emit('edit-confirm');
+ return;
+ }
+
if (this.edit) {
this.collapse({ emitEvent });
} else {
@@ -132,6 +147,7 @@ export default {
<slot name="collapsed-right"></slot>
<gl-button
v-if="canUpdate && !initialLoading && canEdit"
+ :id="buttonId"
category="tertiary"
size="small"
class="gl-text-gray-900! gl-ml-auto hide-collapsed gl-mr-n2 shortcut-sidebar-dropdown-toggle"
@@ -151,7 +167,7 @@ export default {
<slot name="collapsed">{{ __('None') }}</slot>
</div>
<div v-show="edit" data-testid="expanded-content" :class="{ 'gl-mt-3': !isClassicSidebar }">
- <slot :edit="edit"></slot>
+ <slot :edit="edit" :toggle="toggle"></slot>
</div>
</template>
</div>
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 7662d645dd9..e5bee4df9b8 100644
--- a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
+++ b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue
@@ -181,18 +181,18 @@ export default {
</script>
<template>
- <li v-if="isMergeRequest" class="gl-new-dropdown-item">
- <button type="button" class="dropdown-item" @click="toggleSubscribed">
- <span class="gl-new-dropdown-item-text-wrapper">
- <template v-if="subscribed">
- {{ __('Turn off notifications') }}
- </template>
- <template v-else>
- {{ __('Turn on notifications') }}
- </template>
- </span>
- </button>
- </li>
+ <div v-if="isMergeRequest" class="gl-new-dropdown-item">
+ <div class="gl-px-5 gl-pb-2 gl-pt-1">
+ <gl-toggle
+ :value="subscribed"
+ :label="__('Notifications')"
+ class="merge-request-notification-toggle"
+ label-position="left"
+ data-testid="notifications-toggle"
+ @change="toggleSubscribed"
+ />
+ </div>
+ </div>
<sidebar-editable-item
v-else
ref="editable"
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/graphql/cache_update.js b/app/assets/javascripts/sidebar/components/time_tracking/graphql/cache_update.js
deleted file mode 100644
index 70177d84b1b..00000000000
--- a/app/assets/javascripts/sidebar/components/time_tracking/graphql/cache_update.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import produce from 'immer';
-
-export function removeTimelogFromStore(store, deletedTimelogId, query, variables) {
- const sourceData = store.readQuery({
- query,
- variables,
- });
-
- const data = produce(sourceData, (draftData) => {
- draftData.issuable.timelogs.nodes = draftData.issuable.timelogs.nodes.filter(
- ({ id }) => id !== deletedTimelogId,
- );
- });
-
- store.writeQuery({
- query,
- variables,
- data,
- });
-}
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/graphql/mutations/delete_timelog.mutation.graphql b/app/assets/javascripts/sidebar/components/time_tracking/graphql/mutations/delete_timelog.mutation.graphql
index 17bbad1acb1..6e916893b5a 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/graphql/mutations/delete_timelog.mutation.graphql
+++ b/app/assets/javascripts/sidebar/components/time_tracking/graphql/mutations/delete_timelog.mutation.graphql
@@ -1,5 +1,17 @@
+#import "~/graphql_shared/fragments/issue_time_tracking.fragment.graphql"
+#import "~/graphql_shared/fragments/merge_request_time_tracking.fragment.graphql"
+
mutation deleteTimelog($input: TimelogDeleteInput!) {
timelogDelete(input: $input) {
errors
+ timelog {
+ id
+ issue {
+ ...IssueTimeTrackingFragment
+ }
+ mergeRequest {
+ ...MergeRequestTimeTrackingFragment
+ }
+ }
}
}
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/report.vue b/app/assets/javascripts/sidebar/components/time_tracking/report.vue
index 79ef5a32474..d751816bd94 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/report.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/report.vue
@@ -7,7 +7,6 @@ import { formatDate, parseSeconds, stringifyTime } from '~/lib/utils/datetime_ut
import { __, s__ } from '~/locale';
import { timelogQueries } from '~/sidebar/constants';
import deleteTimelogMutation from './graphql/mutations/delete_timelog.mutation.graphql';
-import { removeTimelogFromStore } from './graphql/cache_update';
const TIME_DATE_FORMAT = 'mmmm d, yyyy, HH:MM ("UTC:" o)';
@@ -99,14 +98,6 @@ export default {
.mutate({
mutation: deleteTimelogMutation,
variables: { input: { id: timelogId } },
- update: (store) => {
- removeTimelogFromStore(
- store,
- timelogId,
- timelogQueries[this.issuableType].query,
- this.getQueryVariables(),
- );
- },
})
.then(({ data }) => {
if (data.timelogDelete?.errors?.length) {
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 e39d9f9fb49..13981c477c6 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
@@ -1,8 +1,16 @@
<script>
-import { GlIcon, GlLink, GlModal, GlButton, GlModalDirective, GlLoadingIcon } from '@gitlab/ui';
+import {
+ GlIcon,
+ GlLink,
+ GlModal,
+ GlButton,
+ GlModalDirective,
+ GlLoadingIcon,
+ GlTooltipDirective,
+} from '@gitlab/ui';
import { IssuableType } from '~/issues/constants';
import { s__, __ } from '~/locale';
-import { timeTrackingQueries } from '~/sidebar/constants';
+import { HOW_TO_TRACK_TIME, timeTrackingQueries } from '~/sidebar/constants';
import eventHub from '../../event_hub';
import TimeTrackingCollapsedState from './collapsed_state.vue';
@@ -31,6 +39,7 @@ export default {
},
directives: {
GlModal: GlModalDirective,
+ GlTooltip: GlTooltipDirective,
},
inject: {
issuableType: {
@@ -162,6 +171,12 @@ export default {
this.issuableId
);
},
+ timeTrackingIconTitle() {
+ return this.showHelpState ? '' : HOW_TO_TRACK_TIME;
+ },
+ timeTrackingIconName() {
+ return this.showHelpState ? 'close' : 'question-o';
+ },
},
watch: {
/**
@@ -188,11 +203,7 @@ export default {
</script>
<template>
- <div
- v-cloak
- class="time-tracker time-tracking-component-wrap sidebar-help-wrap"
- data-testid="time-tracker"
- >
+ <div v-cloak class="time-tracker sidebar-help-wrap" data-testid="time-tracker">
<time-tracking-collapsed-state
v-if="showCollapsed"
:show-comparison-state="showComparisonState"
@@ -216,7 +227,12 @@ export default {
class="gl-ml-auto"
@click="toggleHelpState(!showHelpState)"
>
- <gl-icon :name="showHelpState ? 'close' : 'question-o'" class="gl-text-gray-900!" />
+ <gl-icon
+ v-gl-tooltip.left
+ :title="timeTrackingIconTitle"
+ :name="timeTrackingIconName"
+ class="gl-text-gray-900!"
+ />
</gl-button>
</div>
<div v-if="!isTimeTrackingInfoLoading" class="hide-collapsed">
@@ -252,7 +268,6 @@ export default {
size="lg"
:title="__('Time tracking report')"
:hide-footer="true"
- @hide="refresh"
>
<time-tracking-report :limit-to-hours="limitToHours" :issuable-id="issuableId" />
</gl-modal>
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 482b9343e70..42e16aae312 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
@@ -6,6 +6,7 @@ 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';
const trackingMixin = Tracking.mixin();
@@ -19,7 +20,7 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
- mixins: [trackingMixin],
+ mixins: [glFeatureFlagsMixin(), trackingMixin],
inject: {
isClassicSidebar: {
default: false,
@@ -81,6 +82,9 @@ export default {
},
},
computed: {
+ isMergeRequest() {
+ return this.glFeatures.movedMrSidebar && this.issuableType === 'merge_request';
+ },
todoIdQuery() {
return todoQueries[this.issuableType].query;
},
@@ -183,12 +187,12 @@ export default {
:issuable-id="issuableId"
:is-todo="hasTodo"
:loading="isLoading"
- size="small"
+ :size="isMergeRequest ? 'medium' : 'small'"
class="hide-collapsed"
@click.stop.prevent="toggleTodo"
/>
<gl-button
- v-if="isClassicSidebar"
+ v-if="isClassicSidebar && !isMergeRequest"
v-gl-tooltip.left.viewport
:title="tootltipTitle"
category="tertiary"
diff --git a/app/assets/javascripts/sidebar/constants.js b/app/assets/javascripts/sidebar/constants.js
index 989dc574bc3..60cb4cff727 100644
--- a/app/assets/javascripts/sidebar/constants.js
+++ b/app/assets/javascripts/sidebar/constants.js
@@ -1,4 +1,4 @@
-import { s__, sprintf } from '~/locale';
+import { s__, __, sprintf } from '~/locale';
import updateIssueLabelsMutation from '~/boards/graphql/issue_set_labels.mutation.graphql';
import userSearchQuery from '~/graphql_shared/queries/users_search.query.graphql';
import userSearchWithMRPermissionsQuery from '~/graphql_shared/queries/users_search_with_mr_permissions.graphql';
@@ -313,8 +313,26 @@ export function dropdowni18nText(issuableAttribute, issuableType) {
),
{ issuableAttribute, issuableType },
),
+ noPermissionToView: sprintf(
+ s__("DropdownWidget|You don't have permission to view this %{issuableAttribute}."),
+ { issuableAttribute },
+ ),
+ editConfirmation: sprintf(
+ s__(
+ 'DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it.',
+ ),
+ {
+ issuableAttribute,
+ },
+ ),
+ editConfirmationCta: sprintf(s__('DropdownWidget|Edit %{issuableAttribute}'), {
+ issuableAttribute,
+ }),
+ editConfirmationCancel: s__('DropdownWidget|Cancel'),
};
}
export const escalationStatusQuery = getEscalationStatusQuery;
export const escalationStatusMutation = updateEscalationStatusMutation;
+
+export const HOW_TO_TRACK_TIME = __('How to track time');
diff --git a/app/assets/javascripts/sidebar/graphql.js b/app/assets/javascripts/sidebar/graphql.js
deleted file mode 100644
index 127e3a3c610..00000000000
--- a/app/assets/javascripts/sidebar/graphql.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import produce from 'immer';
-import VueApollo from 'vue-apollo';
-import getIssueStateQuery from '~/issues/show/queries/get_issue_state.query.graphql';
-import createDefaultClient from '~/lib/graphql';
-import { temporaryConfig, resolvers as workItemResolvers } from '~/work_items/graphql/provider';
-
-const resolvers = {
- Mutation: {
- updateIssueState: (_, { issueType = undefined, isDirty = false }, { cache }) => {
- const sourceData = cache.readQuery({ query: getIssueStateQuery });
- const data = produce(sourceData, (draftData) => {
- draftData.issueState = { issueType, isDirty };
- });
- cache.writeQuery({ query: getIssueStateQuery, data });
- },
- ...workItemResolvers.Mutation,
- },
-};
-
-export const defaultClient = createDefaultClient(
- resolvers,
- // should be removed with the rollout of work item assignees FF
- // https://gitlab.com/gitlab-org/gitlab/-/issues/363030
- temporaryConfig,
-);
-
-export const apolloProvider = new VueApollo({
- defaultClient,
-});
diff --git a/app/assets/javascripts/sidebar/mount_milestone_sidebar.js b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js
index 2aacce2fb00..cc5de5e4083 100644
--- a/app/assets/javascripts/sidebar/mount_milestone_sidebar.js
+++ b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import { IssuableType } from '~/issues/constants';
import { parseBoolean } from '~/lib/utils/common_utils';
-import timeTracker from './components/time_tracking/time_tracker.vue';
+import TimeTracker from './components/time_tracking/time_tracker.vue';
export default class SidebarMilestone {
constructor() {
@@ -23,13 +23,13 @@ export default class SidebarMilestone {
el,
name: 'SidebarMilestoneRoot',
components: {
- timeTracker,
+ TimeTracker,
},
provide: {
issuableType: IssuableType.Milestone,
},
render: (createElement) =>
- createElement('timeTracker', {
+ createElement('time-tracker', {
props: {
limitToHours: parseBoolean(limitToHours),
issuableIid: iid.toString(),
diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js
index fec4d0e346d..1cb3c30b9e0 100644
--- a/app/assets/javascripts/sidebar/mount_sidebar.js
+++ b/app/assets/javascripts/sidebar/mount_sidebar.js
@@ -22,7 +22,7 @@ import SidebarParticipantsWidget from '~/sidebar/components/participants/sidebar
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 '~/sidebar/graphql';
+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';
@@ -39,7 +39,6 @@ import SidebarTimeTracking from './components/time_tracking/sidebar_time_trackin
import { IssuableAttributeType } from './constants';
import SidebarMoveIssue from './lib/sidebar_move_issue';
import CrmContacts from './components/crm_contacts/crm_contacts.vue';
-import SidebarEventHub from './event_hub';
Vue.use(Translate);
Vue.use(VueApollo);
@@ -163,6 +162,7 @@ function mountAssigneesComponent() {
issuableType,
issuableId: id,
allowMultipleAssignees: !el.dataset.maxAssignees,
+ editable,
},
scopedSlots: {
collapsed: ({ users }) =>
@@ -360,13 +360,6 @@ function mountConfidentialComponent() {
? IssuableType.Issue
: IssuableType.MergeRequest,
},
- on: {
- closeForm({ confidential }) {
- if (confidential !== undefined) {
- SidebarEventHub.$emit('confidentialityUpdated', confidential);
- }
- },
- },
}),
});
}
diff --git a/app/assets/javascripts/sidebar/queries/issue_time_tracking.query.graphql b/app/assets/javascripts/sidebar/queries/issue_time_tracking.query.graphql
index f4d0e9b5deb..41d45b486e8 100644
--- a/app/assets/javascripts/sidebar/queries/issue_time_tracking.query.graphql
+++ b/app/assets/javascripts/sidebar/queries/issue_time_tracking.query.graphql
@@ -1,12 +1,12 @@
+#import "~/graphql_shared/fragments/issue_time_tracking.fragment.graphql"
+
query issueTimeTracking($fullPath: ID!, $iid: String) {
workspace: project(fullPath: $fullPath) {
id
issuable: issue(iid: $iid) {
- id
+ ...IssueTimeTrackingFragment
humanTimeEstimate
- humanTotalTimeSpent
timeEstimate
- totalTimeSpent
}
}
}
diff --git a/app/assets/javascripts/sidebar/queries/merge_request_time_tracking.query.graphql b/app/assets/javascripts/sidebar/queries/merge_request_time_tracking.query.graphql
index 5d05cb2f34c..12ef78a6453 100644
--- a/app/assets/javascripts/sidebar/queries/merge_request_time_tracking.query.graphql
+++ b/app/assets/javascripts/sidebar/queries/merge_request_time_tracking.query.graphql
@@ -1,12 +1,12 @@
+#import "~/graphql_shared/fragments/merge_request_time_tracking.fragment.graphql"
+
query mergeRequestTimeTracking($fullPath: ID!, $iid: String!) {
workspace: project(fullPath: $fullPath) {
id
issuable: mergeRequest(iid: $iid) {
- id
+ ...MergeRequestTimeTrackingFragment
humanTimeEstimate
- humanTotalTimeSpent
timeEstimate
- totalTimeSpent
}
}
}
diff --git a/app/assets/javascripts/snippets/components/show.vue b/app/assets/javascripts/snippets/components/show.vue
index ee8b00c1f5d..853293e5eb6 100644
--- a/app/assets/javascripts/snippets/components/show.vue
+++ b/app/assets/javascripts/snippets/components/show.vue
@@ -6,7 +6,7 @@ import {
SNIPPET_MEASURE_BLOBS_CONTENT,
} from '~/performance/constants';
import { performanceMarkAndMeasure } from '~/performance/utils';
-import { SNIPPET_VISIBILITY_PUBLIC } from '~/snippets/constants';
+import { VISIBILITY_LEVEL_PUBLIC_STRING } from '~/visibility_level/constants';
import CloneDropdownButton from '~/vue_shared/components/clone_dropdown.vue';
import { getSnippetMixin } from '../mixins/snippets';
@@ -31,7 +31,7 @@ export default {
mixins: [getSnippetMixin],
computed: {
embeddable() {
- return this.snippet.visibilityLevel === SNIPPET_VISIBILITY_PUBLIC;
+ return this.snippet.visibilityLevel === VISIBILITY_LEVEL_PUBLIC_STRING;
},
canBeCloned() {
return Boolean(this.snippet.sshUrlToRepo || this.snippet.httpUrlToRepo);
diff --git a/app/assets/javascripts/snippets/constants.js b/app/assets/javascripts/snippets/constants.js
index 2a9ecbc27dc..84a940ed1f8 100644
--- a/app/assets/javascripts/snippets/constants.js
+++ b/app/assets/javascripts/snippets/constants.js
@@ -1,22 +1,23 @@
import { __ } from '~/locale';
-
-export const SNIPPET_VISIBILITY_PRIVATE = 'private';
-export const SNIPPET_VISIBILITY_INTERNAL = 'internal';
-export const SNIPPET_VISIBILITY_PUBLIC = 'public';
+import {
+ VISIBILITY_LEVEL_PRIVATE_STRING,
+ VISIBILITY_LEVEL_INTERNAL_STRING,
+ VISIBILITY_LEVEL_PUBLIC_STRING,
+} from '~/visibility_level/constants';
export const SNIPPET_VISIBILITY = {
- [SNIPPET_VISIBILITY_PRIVATE]: {
+ [VISIBILITY_LEVEL_PRIVATE_STRING]: {
label: __('Private'),
icon: 'lock',
description: __('The snippet is visible only to me.'),
description_project: __('The snippet is visible only to project members.'),
},
- [SNIPPET_VISIBILITY_INTERNAL]: {
+ [VISIBILITY_LEVEL_INTERNAL_STRING]: {
label: __('Internal'),
icon: 'shield',
description: __('The snippet is visible to any logged in user except external users.'),
},
- [SNIPPET_VISIBILITY_PUBLIC]: {
+ [VISIBILITY_LEVEL_PUBLIC_STRING]: {
label: __('Public'),
icon: 'earth',
description: __('The snippet can be accessed without any authentication.'),
@@ -34,11 +35,6 @@ export const SNIPPET_BLOB_ACTION_DELETE = 'delete';
export const SNIPPET_MAX_BLOBS = 10;
-export const SNIPPET_LEVELS_MAP = {
- 0: SNIPPET_VISIBILITY_PRIVATE,
- 10: SNIPPET_VISIBILITY_INTERNAL,
- 20: SNIPPET_VISIBILITY_PUBLIC,
-};
export const SNIPPET_LEVELS_RESTRICTED = __(
'Other visibility settings have been disabled by the administrator.',
);
diff --git a/app/assets/javascripts/snippets/index.js b/app/assets/javascripts/snippets/index.js
index 21f38c4d8c9..89dd5e586fb 100644
--- a/app/assets/javascripts/snippets/index.js
+++ b/app/assets/javascripts/snippets/index.js
@@ -2,7 +2,10 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
-import { SNIPPET_LEVELS_MAP, SNIPPET_VISIBILITY_PRIVATE } from '~/snippets/constants';
+import {
+ VISIBILITY_LEVEL_PRIVATE_STRING,
+ VISIBILITY_LEVELS_INTEGER_TO_STRING,
+} from '~/visibility_level/constants';
import Translate from '~/vue_shared/translate';
Vue.use(VueApollo);
@@ -36,7 +39,8 @@ export default function appFactory(el, Component) {
apolloProvider,
provide: {
visibilityLevels: JSON.parse(visibilityLevels),
- selectedLevel: SNIPPET_LEVELS_MAP[selectedLevel] ?? SNIPPET_VISIBILITY_PRIVATE,
+ selectedLevel:
+ VISIBILITY_LEVELS_INTEGER_TO_STRING[selectedLevel] ?? VISIBILITY_LEVEL_PRIVATE_STRING,
multipleLevelsRestricted: 'multipleLevelsRestricted' in el.dataset,
reportAbusePath,
canReportSpam,
diff --git a/app/assets/javascripts/snippets/utils/blob.js b/app/assets/javascripts/snippets/utils/blob.js
index 2a3f590a803..a228d6111ce 100644
--- a/app/assets/javascripts/snippets/utils/blob.js
+++ b/app/assets/javascripts/snippets/utils/blob.js
@@ -1,12 +1,12 @@
import { uniqueId } from 'lodash';
import { SNIPPET_MARK_BLOBS_CONTENT, SNIPPET_MEASURE_BLOBS_CONTENT } from '~/performance/constants';
import { performanceMarkAndMeasure } from '~/performance/utils';
+import { VISIBILITY_LEVELS_INTEGER_TO_STRING } from '~/visibility_level/constants';
import {
SNIPPET_BLOB_ACTION_CREATE,
SNIPPET_BLOB_ACTION_UPDATE,
SNIPPET_BLOB_ACTION_MOVE,
SNIPPET_BLOB_ACTION_DELETE,
- SNIPPET_LEVELS_MAP,
SNIPPET_VISIBILITY,
} from '../constants';
@@ -72,7 +72,7 @@ export const diffAll = (blobs, origBlobs) => {
export const defaultSnippetVisibilityLevels = (arr) => {
if (Array.isArray(arr)) {
return arr.map((l) => {
- const translatedLevel = SNIPPET_LEVELS_MAP[l];
+ const translatedLevel = VISIBILITY_LEVELS_INTEGER_TO_STRING[l];
return {
value: translatedLevel,
...SNIPPET_VISIBILITY[translatedLevel],
diff --git a/app/assets/javascripts/surveys/merge_request_experience/app.vue b/app/assets/javascripts/surveys/merge_request_experience/app.vue
index 4e4ef49b1c6..df114c27908 100644
--- a/app/assets/javascripts/surveys/merge_request_experience/app.vue
+++ b/app/assets/javascripts/surveys/merge_request_experience/app.vue
@@ -19,6 +19,8 @@ const steps = [
},
];
+const MR_RENDER_LS_KEY = 'mr_survey_rendered';
+
export default {
name: 'MergeRequestExperienceSurveyApp',
components: {
@@ -68,9 +70,20 @@ export default {
onQueryLoaded({ shouldShowCallout }) {
this.visible = shouldShowCallout;
if (!this.visible) this.$emit('close');
+ else if (!localStorage?.getItem(MR_RENDER_LS_KEY)) {
+ this.track('survey:mr_experience', {
+ label: 'render',
+ extra: {
+ accountAge: this.accountAge,
+ },
+ });
+ localStorage?.setItem(MR_RENDER_LS_KEY, '1');
+ }
},
onRate(event) {
+ this.$refs.dismisser?.dismiss();
this.$emit('rate');
+ localStorage?.removeItem(MR_RENDER_LS_KEY);
this.track('survey:mr_experience', {
label: this.step.label,
value: event,
@@ -87,8 +100,18 @@ export default {
},
handleKeyup(e) {
if (e.key !== 'Escape') return;
- this.$emit('close');
+ this.dismiss();
+ },
+ dismiss() {
this.$refs.dismisser?.dismiss();
+ this.$emit('close');
+ this.track('survey:mr_experience', {
+ label: 'dismiss',
+ extra: {
+ accountAge: this.accountAge,
+ },
+ });
+ localStorage?.removeItem(MR_RENDER_LS_KEY);
},
},
};
@@ -100,79 +123,71 @@ export default {
feature-name="mr_experience_survey"
@queryResult.once="onQueryLoaded"
>
- <template #default="{ dismiss }">
- <aside
- class="mr-experience-survey-wrapper gl-fixed gl-bottom-0 gl-right-0 gl-p-5"
- :aria-label="$options.i18n.survey"
- >
- <transition name="survey-slide-up">
+ <aside
+ class="mr-experience-survey-wrapper gl-fixed gl-bottom-0 gl-right-0 gl-p-5"
+ :aria-label="$options.i18n.survey"
+ >
+ <transition name="survey-slide-up">
+ <div
+ v-if="visible"
+ class="mr-experience-survey-body gl-relative gl-display-flex gl-flex-direction-column gl-bg-white gl-p-5 gl-border gl-rounded-base"
+ >
+ <gl-button
+ v-tooltip="$options.i18n.close"
+ :aria-label="$options.i18n.close"
+ variant="default"
+ category="tertiary"
+ class="gl-top-4 gl-right-3 gl-absolute"
+ icon="close"
+ @click="dismiss"
+ />
<div
- v-if="visible"
- class="mr-experience-survey-body gl-relative gl-display-flex gl-flex-direction-column gl-bg-white gl-p-5 gl-border gl-rounded-base"
+ v-if="stepIndex === 0"
+ class="mr-experience-survey-legal gl-border-t gl-mt-5 gl-pt-3 gl-text-gray-500 gl-font-sm"
+ role="note"
>
- <gl-button
- v-tooltip="$options.i18n.close"
- :aria-label="$options.i18n.close"
- variant="default"
- category="tertiary"
- class="gl-top-4 gl-right-3 gl-absolute"
- icon="close"
- @click="
- dismiss();
- $emit('close');
- "
- />
- <div
- v-if="stepIndex === 0"
- class="mr-experience-survey-legal gl-border-t gl-mt-5 gl-pt-3 gl-text-gray-500 gl-font-sm"
- role="note"
- >
- <p class="gl-m-0">
- <gl-sprintf :message="$options.i18n.legal">
- <template #link="{ content }">
- <a
- class="gl-text-decoration-underline gl-text-gray-500"
- href="https://about.gitlab.com/privacy/"
- target="_blank"
- rel="noreferrer nofollow"
- v-text="content"
- ></a>
- </template>
- </gl-sprintf>
- </p>
- </div>
- <div class="gl-relative">
- <div class="gl-absolute">
- <div
- v-safe-html="$options.gitlabLogo"
- aria-hidden="true"
- class="mr-experience-survey-logo"
- ></div>
- </div>
+ <p class="gl-m-0">
+ <gl-sprintf :message="$options.i18n.legal">
+ <template #link="{ content }">
+ <a
+ class="gl-text-decoration-underline gl-text-gray-500"
+ href="https://about.gitlab.com/privacy/"
+ target="_blank"
+ rel="noreferrer nofollow"
+ v-text="content"
+ ></a>
+ </template>
+ </gl-sprintf>
+ </p>
+ </div>
+ <div class="gl-relative">
+ <div class="gl-absolute">
+ <div
+ v-safe-html="$options.gitlabLogo"
+ aria-hidden="true"
+ class="mr-experience-survey-logo"
+ ></div>
</div>
- <section v-if="step">
- <p id="mr_survey_question" ref="question" class="gl-m-0 gl-px-7">
- <gl-sprintf :message="step.question">
- <template #strong="{ content }">
- <strong>{{ content }}</strong>
- </template>
- </gl-sprintf>
- </p>
- <satisfaction-rate
- aria-labelledby="mr_survey_question"
- class="gl-mt-5"
- @rate="
- dismiss();
- onRate($event);
- "
- />
- </section>
- <section v-else class="gl-px-7">
- {{ $options.i18n.thanks }}
- </section>
</div>
- </transition>
- </aside>
- </template>
+ <section v-if="step">
+ <p id="mr_survey_question" ref="question" class="gl-m-0 gl-px-7">
+ <gl-sprintf :message="step.question">
+ <template #strong="{ content }">
+ <strong>{{ content }}</strong>
+ </template>
+ </gl-sprintf>
+ </p>
+ <satisfaction-rate
+ aria-labelledby="mr_survey_question"
+ class="gl-mt-5"
+ @rate="onRate"
+ />
+ </section>
+ <section v-else class="gl-px-7">
+ {{ $options.i18n.thanks }}
+ </section>
+ </div>
+ </transition>
+ </aside>
</user-callout-dismisser>
</template>
diff --git a/app/assets/javascripts/token_access/components/token_access.vue b/app/assets/javascripts/token_access/components/token_access.vue
index de8cd856bf7..363a9d58d65 100644
--- a/app/assets/javascripts/token_access/components/token_access.vue
+++ b/app/assets/javascripts/token_access/components/token_access.vue
@@ -1,7 +1,16 @@
<script>
-import { GlButton, GlCard, GlFormInput, GlLoadingIcon, GlToggle } from '@gitlab/ui';
+import {
+ GlButton,
+ GlCard,
+ GlFormInput,
+ GlLink,
+ GlLoadingIcon,
+ GlSprintf,
+ GlToggle,
+} from '@gitlab/ui';
import createFlash from '~/flash';
import { __, s__ } from '~/locale';
+import { helpPagePath } from '~/helpers/help_page_helper';
import addProjectCIJobTokenScopeMutation from '../graphql/mutations/add_project_ci_job_token_scope.mutation.graphql';
import removeProjectCIJobTokenScopeMutation from '../graphql/mutations/remove_project_ci_job_token_scope.mutation.graphql';
import updateCIJobTokenScopeMutation from '../graphql/mutations/update_ci_job_token_scope.mutation.graphql';
@@ -13,7 +22,7 @@ export default {
i18n: {
toggleLabelTitle: s__('CICD|Limit CI_JOB_TOKEN access'),
toggleHelpText: s__(
- `CICD|Select projects that can be accessed by API requests authenticated with this project's CI_JOB_TOKEN CI/CD variable.`,
+ `CICD|Select the projects that can be accessed by API requests authenticated with this project's CI_JOB_TOKEN CI/CD variable. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API. %{linkStart}Learn more.%{linkEnd}`,
),
cardHeaderTitle: s__('CICD|Add an existing project to the scope'),
addProject: __('Add project'),
@@ -26,7 +35,9 @@ export default {
GlButton,
GlCard,
GlFormInput,
+ GlLink,
GlLoadingIcon,
+ GlSprintf,
GlToggle,
TokenProjectsTable,
},
@@ -76,6 +87,9 @@ export default {
isProjectPathEmpty() {
return this.targetProjectPath === '';
},
+ ciJobTokenHelpPage() {
+ return helpPagePath('ci/jobs/ci_job_token');
+ },
},
methods: {
async updateCIJobTokenScope() {
@@ -99,10 +113,6 @@ export default {
}
} catch (error) {
createFlash({ message: error });
- } finally {
- if (this.jobTokenScopeEnabled) {
- this.getProjects();
- }
}
},
async addProject() {
@@ -172,10 +182,20 @@ export default {
<gl-toggle
v-model="jobTokenScopeEnabled"
:label="$options.i18n.toggleLabelTitle"
- :help="$options.i18n.toggleHelpText"
@change="updateCIJobTokenScope"
- />
- <div v-if="jobTokenScopeEnabled" data-testid="token-section">
+ >
+ <template #help>
+ <gl-sprintf :message="$options.i18n.toggleHelpText">
+ <template #link="{ content }">
+ <gl-link :href="ciJobTokenHelpPage" class="inline-link" target="_blank">
+ {{ content }}
+ </gl-link>
+ </template>
+ </gl-sprintf>
+ </template>
+ </gl-toggle>
+
+ <div data-testid="token-section">
<gl-card class="gl-mt-5">
<template #header>
<h5 class="gl-my-0">{{ $options.i18n.cardHeaderTitle }}</h5>
diff --git a/app/assets/javascripts/user_popovers.js b/app/assets/javascripts/user_popovers.js
index c2892fb8dac..ee5d6a22fc3 100644
--- a/app/assets/javascripts/user_popovers.js
+++ b/app/assets/javascripts/user_popovers.js
@@ -46,6 +46,7 @@ const populateUserInfo = (user) => {
pronouns: userData.pronouns,
localTime: userData.local_time,
isFollowed: userData.is_followed,
+ state: userData.state,
loaded: true,
});
}
diff --git a/app/assets/javascripts/validators/input_validator.js b/app/assets/javascripts/validators/input_validator.js
index f37373977b8..b799976a0ba 100644
--- a/app/assets/javascripts/validators/input_validator.js
+++ b/app/assets/javascripts/validators/input_validator.js
@@ -19,6 +19,7 @@ export default class InputValidator {
setValidationMessage() {
if (this.invalidInput) {
this.inputDomElement.setCustomValidity(this.errorMessage);
+ // eslint-disable-next-line no-unsanitized/property
this.inputErrorMessage.innerHTML = this.errorMessage;
} else {
this.resetValidationMessage();
@@ -28,6 +29,7 @@ export default class InputValidator {
resetValidationMessage() {
if (this.inputDomElement.validationMessage === this.errorMessage) {
this.inputDomElement.setCustomValidity('');
+ // eslint-disable-next-line no-unsanitized/property
this.inputErrorMessage.innerHTML = this.inputDomElement.title;
}
}
diff --git a/app/assets/javascripts/visibility_level/constants.js b/app/assets/javascripts/visibility_level/constants.js
index 65f0eceae55..77736fb6ef5 100644
--- a/app/assets/javascripts/visibility_level/constants.js
+++ b/app/assets/javascripts/visibility_level/constants.js
@@ -1,10 +1,20 @@
-export const VISIBILITY_LEVEL_PRIVATE = 'private';
-export const VISIBILITY_LEVEL_INTERNAL = 'internal';
-export const VISIBILITY_LEVEL_PUBLIC = 'public';
+export const VISIBILITY_LEVEL_PRIVATE_STRING = 'private';
+export const VISIBILITY_LEVEL_INTERNAL_STRING = 'internal';
+export const VISIBILITY_LEVEL_PUBLIC_STRING = 'public';
+
+export const VISIBILITY_LEVEL_PRIVATE_INTEGER = 0;
+export const VISIBILITY_LEVEL_INTERNAL_INTEGER = 10;
+export const VISIBILITY_LEVEL_PUBLIC_INTEGER = 20;
// Matches `lib/gitlab/visibility_level.rb`
-export const VISIBILITY_LEVELS_ENUM = {
- [VISIBILITY_LEVEL_PRIVATE]: 0,
- [VISIBILITY_LEVEL_INTERNAL]: 10,
- [VISIBILITY_LEVEL_PUBLIC]: 20,
+export const VISIBILITY_LEVELS_STRING_TO_INTEGER = {
+ [VISIBILITY_LEVEL_PRIVATE_STRING]: VISIBILITY_LEVEL_PRIVATE_INTEGER,
+ [VISIBILITY_LEVEL_INTERNAL_STRING]: VISIBILITY_LEVEL_INTERNAL_INTEGER,
+ [VISIBILITY_LEVEL_PUBLIC_STRING]: VISIBILITY_LEVEL_PUBLIC_INTEGER,
+};
+
+export const VISIBILITY_LEVELS_INTEGER_TO_STRING = {
+ [VISIBILITY_LEVEL_PRIVATE_INTEGER]: VISIBILITY_LEVEL_PRIVATE_STRING,
+ [VISIBILITY_LEVEL_INTERNAL_INTEGER]: VISIBILITY_LEVEL_INTERNAL_STRING,
+ [VISIBILITY_LEVEL_PUBLIC_INTEGER]: VISIBILITY_LEVEL_PUBLIC_STRING,
};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/action_buttons.vue b/app/assets/javascripts/vue_merge_request_widget/components/action_buttons.vue
index 38f40e8a3c8..30a0e7c383c 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/action_buttons.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/action_buttons.vue
@@ -63,6 +63,12 @@ export default {
return btn.tooltipText;
},
+ actionButtonQaSelector(btn) {
+ if (btn.dataQaSelector) {
+ return btn.dataQaSelector;
+ }
+ return 'mr_widget_extension_actions_button';
+ },
},
};
</script>
@@ -105,7 +111,7 @@ export default {
:target="btn.target"
:class="[{ 'gl-mr-3': index !== tertiaryButtons.length - 1 }, btn.class]"
:data-clipboard-text="btn.dataClipboardText"
- :data-qa-selector="btn.dataQaSelector"
+ :data-qa-selector="actionButtonQaSelector(btn)"
:data-method="btn.dataMethod"
:icon="btn.icon"
:data-testid="btn.testId || 'extension-actions-button'"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/added_commit_message.vue b/app/assets/javascripts/vue_merge_request_widget/components/added_commit_message.vue
index 254b280bf14..f377a185879 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/added_commit_message.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/added_commit_message.vue
@@ -1,5 +1,5 @@
<script>
-import { GlSprintf } from '@gitlab/ui';
+import { GlSprintf, GlLink } from '@gitlab/ui';
import { escape } from 'lodash';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { n__, s__, sprintf } from '~/locale';
@@ -9,6 +9,7 @@ const mergeCommitCount = s__('mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} m
export default {
components: {
GlSprintf,
+ GlLink,
},
mixins: [glFeatureFlagMixin()],
props: {
@@ -40,6 +41,11 @@ export default {
required: false,
default: '',
},
+ mergeCommitPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
computed: {
isMerged() {
@@ -124,7 +130,9 @@ export default {
</template>
</template>
<template #mergeCommitSha>
- <span class="label-branch">{{ mergeCommitSha }}</span>
+ <gl-link :href="mergeCommitPath" class="label-branch" data-testid="merge-commit-sha">{{
+ mergeCommitSha
+ }}</gl-link>
</template>
</gl-sprintf>
</span>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue
index b1c4f7c5a7c..d7255eb6ad2 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue
@@ -103,7 +103,7 @@ export default {
<span v-if="approvalLeftMessage">{{ message }}</span>
<span v-else class="gl-font-weight-bold">{{ message }}</span>
<user-avatar-list
- class="gl-display-inline-block gl-vertical-align-middle"
+ class="gl-display-inline-block gl-vertical-align-middle gl-pt-1"
:img-size="24"
:items="approvers"
/>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_info.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_info.vue
index e115710b5d1..30098f7619a 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_info.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment/deployment_info.vue
@@ -74,16 +74,12 @@ export default {
<div class="js-deployment-info deployment-info">
<template v-if="hasDeploymentMeta">
<span>{{ deployedText }}</span>
- <tooltip-on-truncate
- :title="deployment.name"
- truncate-target="child"
- class="deploy-link label-truncate"
- >
+ <tooltip-on-truncate :title="deployment.name" truncate-target="child" class="label-truncate">
<gl-link
:href="deployment.url"
target="_blank"
rel="noopener noreferrer nofollow"
- class="js-deploy-meta gl-font-sm"
+ class="js-deploy-meta gl-font-sm gl-pb-1"
>
{{ deployment.name }}
</gl-link>
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 414c5bf9691..300e2a672cb 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
@@ -13,6 +13,7 @@ import Poll from '~/lib/utils/poll';
import { normalizeHeaders } from '~/lib/utils/common_utils';
import { EXTENSION_ICON_CLASS, EXTENSION_ICONS } from '../../constants';
import Actions from '../action_buttons.vue';
+import StateContainer from '../state_container.vue';
import StatusIcon from './status_icon.vue';
import ChildContent from './child_content.vue';
import { createTelemetryHub } from './telemetry';
@@ -36,6 +37,7 @@ export default {
ChildContent,
DynamicScroller,
DynamicScrollerItem,
+ StateContainer,
},
directives: {
SafeHtml: GlSafeHtmlDirective,
@@ -307,19 +309,20 @@ export default {
</script>
<template>
- <section class="media-section" data-testid="widget-extension">
- <div
+ <section
+ class="media-section"
+ data-testid="widget-extension"
+ data-qa-selector="mr_widget_extension"
+ >
+ <state-container
+ :mr="mr"
+ :status="statusIconName"
+ :is-loading="isLoadingSummary"
:class="{ 'gl-cursor-pointer': isCollapsible }"
- class="media gl-p-5"
+ class="gl-p-5"
@mousedown="onRowMouseDown"
@mouseup="onRowMouseUp"
>
- <status-icon
- :level="1"
- :name="$options.label || $options.name"
- :is-loading="isLoadingSummary"
- :icon-name="statusIconName"
- />
<div
class="media-body gl-display-flex gl-flex-direction-row! gl-align-self-center"
data-testid="widget-extension-top-level"
@@ -352,12 +355,13 @@ export default {
:icon="isCollapsed ? 'chevron-lg-down' : 'chevron-lg-up'"
category="tertiary"
data-testid="toggle-button"
+ data-qa-selector="toggle_button"
size="small"
@click="toggleCollapsed"
/>
</div>
</div>
- </div>
+ </state-container>
<div
v-if="!isCollapsed"
class="mr-widget-grouped-section gl-relative"
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 1eccc7de660..52c9f047b76 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
@@ -62,7 +62,9 @@ export default {
<strong v-else v-safe-html="generateText(data.header)"></strong>
</div>
<div class="gl-display-flex">
- <status-icon v-if="data.icon" :icon-name="data.icon.name" :size="12" class="gl-pl-0" />
+ <div v-if="data.icon" class="report-block-child-icon gl-display-flex">
+ <status-icon :icon-name="data.icon.name" :size="12" class="gl-m-auto" />
+ </div>
<div class="gl-w-full">
<div class="gl-display-flex gl-flex-nowrap">
<div class="gl-flex-wrap gl-display-flex gl-w-full">
@@ -109,6 +111,7 @@ export default {
:modal-id="modalId"
:level="3"
data-testid="child-content"
+ data-qa-selector="child_content"
@clickedAction="onClickedAction"
/>
</li>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/status_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/extensions/status_icon.vue
index dc748ba44f2..f9d0986d60d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/status_icon.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/status_icon.vue
@@ -49,18 +49,28 @@ export default {
<div
:class="[
$options.EXTENSION_ICON_CLASS[iconName],
- { 'mr-widget-extension-icon gl-w-6': !isLoading && level === 1 },
+ { 'gl-w-6': !isLoading && level === 1 },
{ 'gl-p-2': isLoading || level === 1 },
]"
- class="gl-rounded-full gl-mr-3 gl-relative gl-p-2"
+ class="gl-mr-3 gl-p-2"
>
- <gl-loading-icon v-if="isLoading" size="sm" inline class="gl-display-block" />
- <gl-icon
- v-else
- :name="$options.EXTENSION_ICON_NAMES[iconName]"
- :size="size"
- :aria-label="iconAriaLabel"
- class="gl-display-block"
- />
+ <div
+ class="gl-rounded-full gl-relative gl-display-flex"
+ :class="{ 'mr-widget-extension-icon': !isLoading && level === 1 }"
+ >
+ <div class="gl-absolute gl-top-half gl-left-50p gl-translate-x-n50 gl-display-flex gl-m-auto">
+ <div class="gl-display-flex gl-m-auto gl-translate-y-n50">
+ <gl-loading-icon v-if="isLoading" size="md" inline />
+ <gl-icon
+ v-else
+ :name="$options.EXTENSION_ICON_NAMES[iconName]"
+ :size="size"
+ :aria-label="iconAriaLabel"
+ :data-qa-selector="`status_${iconName}_icon`"
+ class="gl-display-block"
+ />
+ </div>
+ </div>
+ </div>
</div>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js b/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js
index bc84459e298..d67ff11f297 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/telemetry.js
@@ -24,7 +24,7 @@ const nonStandardEvents = {
},
issues: {
uniqueUser: {
- expand: ['i_testing_load_performance_widget_total'],
+ expand: ['i_testing_issues_widget_total'],
},
counter: {},
},
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_icon.vue
index 437342bf438..0c36e1ccd7f 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_icon.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_icon.vue
@@ -13,7 +13,7 @@ export default {
</script>
<template>
- <div class="circle-icon-container gl-mr-3 align-self-start">
+ <div class="circle-icon-container gl-mr-3 align-self-start gl-mt-2">
<gl-icon :name="name" :size="24" />
</div>
</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 1e1a2049414..fe69e96bd87 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
@@ -9,11 +9,10 @@ import {
GlTooltipDirective,
GlSafeHtmlDirective,
} from '@gitlab/ui';
-import mrWidgetPipelineMixin from 'ee_else_ce/vue_merge_request_widget/mixins/mr_widget_pipeline';
import { s__, n__ } from '~/locale';
-import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue';
-import PipelineArtifacts from '~/pipelines/components/pipelines_list/pipelines_artifacts.vue';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import PipelineArtifacts from '~/pipelines/components/pipelines_list/pipelines_artifacts.vue';
+import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
import { MT_MERGE_STRATEGY } from '../constants';
@@ -31,14 +30,11 @@ export default {
PipelineMiniGraph,
TimeAgoTooltip,
TooltipOnTruncate,
- LinkedPipelinesMiniList: () =>
- import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'),
},
directives: {
GlTooltip: GlTooltipDirective,
SafeHtml: GlSafeHtmlDirective,
},
- mixins: [mrWidgetPipelineMixin],
props: {
pipeline: {
type: Object,
@@ -172,7 +168,7 @@ export default {
</p>
</template>
<template v-else-if="!hasPipeline">
- <gl-loading-icon size="lg" />
+ <gl-loading-icon size="md" />
<p
class="gl-flex-grow-1 gl-display-flex gl-ml-3 gl-mb-0"
data-testid="monitoring-pipeline-message"
@@ -276,17 +272,15 @@ export default {
</div>
</div>
<div>
- <span class="gl-align-items-center gl-display-inline-flex mr-widget-pipeline-graph">
- <span class="gl-align-items-center gl-display-inline-flex gl-flex-wrap stage-cell">
- <linked-pipelines-mini-list v-if="triggeredBy.length" :triggered-by="triggeredBy" />
- <pipeline-mini-graph
- v-if="hasStages"
- stages-class="mr-widget-pipeline-stages"
- :stages="pipeline.details.stages"
- :is-merge-train="isMergeTrain"
- />
- </span>
- <linked-pipelines-mini-list v-if="triggered.length" :triggered="triggered" />
+ <span class="gl-align-items-center gl-display-inline-flex">
+ <pipeline-mini-graph
+ v-if="pipeline.details.stages"
+ :downstream-pipelines="pipeline.triggered"
+ :is-merge-train="isMergeTrain"
+ :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>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
index 5b8acb4ebf8..3239285e53e 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_status_icon.vue
@@ -1,11 +1,11 @@
<script>
-import { GlLoadingIcon } from '@gitlab/ui';
-import ciIcon from '~/vue_shared/components/ci_icon.vue';
+import { GlIcon } from '@gitlab/ui';
+import StatusIcon from './extensions/status_icon.vue';
export default {
components: {
- ciIcon,
- GlLoadingIcon,
+ StatusIcon,
+ GlIcon,
},
props: {
status: {
@@ -17,22 +17,20 @@ export default {
isLoading() {
return this.status === 'loading';
},
- statusObj() {
- return {
- group: this.status,
- icon: `status_${this.status}`,
- };
- },
},
};
</script>
<template>
- <div class="gl-display-flex gl-align-self-start">
- <div class="square s24 h-auto d-flex-center gl-mr-3">
- <div v-if="isLoading" class="mr-widget-icon gl-display-inline-flex">
- <gl-loading-icon size="md" class="mr-loading-icon gl-display-inline-flex" />
- </div>
- <ci-icon v-else :status="statusObj" :size="24" />
+ <div class="gl-w-6 gl-h-6 gl-display-flex gl-align-self-start gl-mr-3">
+ <div class="gl-display-flex gl-m-auto">
+ <gl-icon v-if="status === 'merged'" name="merge" :size="16" class="gl-text-blue-500" />
+ <gl-icon
+ v-else-if="status === 'closed'"
+ name="merge-request-close"
+ :size="16"
+ class="gl-text-red-500"
+ />
+ <status-icon v-else :is-loading="isLoading" :icon-name="status" :level="1" class="gl-m-0!" />
</div>
</div>
</template>
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 4a5a03fb598..822c5a68093 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
@@ -1,13 +1,23 @@
<script>
+import { GlButton, GlTooltipDirective } from '@gitlab/ui';
+import { __ } from '~/locale';
import StatusIcon from './mr_widget_status_icon.vue';
import Actions from './action_buttons.vue';
export default {
components: {
+ GlButton,
StatusIcon,
Actions,
},
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
props: {
+ mr: {
+ type: Object,
+ required: true,
+ },
isLoading: {
type: Boolean,
required: false,
@@ -24,30 +34,67 @@ export default {
default: () => [],
},
},
+ i18n: {
+ expandDetailsTooltip: __('Expand merge details'),
+ collapseDetailsTooltip: __('Collapse merge details'),
+ },
+ computed: {
+ wrapperClasses() {
+ if (this.status === 'merged') return 'gl-bg-blue-50';
+ if (this.status === 'closed') return 'gl-bg-red-50';
+ return null;
+ },
+ },
};
</script>
<template>
- <div class="mr-widget-body media">
+ <div class="mr-widget-body media" :class="wrapperClasses" v-on="$listeners">
<div v-if="isLoading" class="gl-w-full mr-conflict-loader">
- <slot name="loading"></slot>
+ <slot name="loading">
+ <div class="gl-display-flex">
+ <status-icon status="loading" />
+ <div class="media-body">
+ <slot></slot>
+ </div>
+ </div>
+ </slot>
</div>
<template v-else>
<slot name="icon">
<status-icon :status="status" />
</slot>
- <div
- :class="{ 'gl-display-flex': actions.length, 'gl-md-display-flex': !actions.length }"
- class="media-body"
- >
- <slot></slot>
+ <div class="gl-display-flex gl-w-full">
+ <div
+ :class="{ 'gl-display-flex': actions.length, 'gl-md-display-flex': !actions.length }"
+ class="media-body"
+ >
+ <slot></slot>
+ <div
+ :class="{ 'gl-flex-direction-column-reverse': !actions.length }"
+ class="gl-display-flex gl-md-display-block gl-font-size-0 gl-ml-auto"
+ >
+ <slot name="actions">
+ <actions v-if="actions.length" :tertiary-buttons="actions" />
+ </slot>
+ </div>
+ </div>
<div
- :class="{ 'gl-flex-direction-column-reverse': !actions.length }"
- class="gl-display-flex gl-md-display-block gl-font-size-0 gl-ml-auto gl-mt-1"
+ class="gl-md-display-none gl-border-l-1 gl-border-l-solid gl-border-gray-100 gl-ml-3 gl-pl-3 gl-h-6 gl-mt-1"
>
- <slot name="actions">
- <actions v-if="actions.length" :tertiary-buttons="actions" />
- </slot>
+ <gl-button
+ v-gl-tooltip
+ :title="
+ mr.mergeDetailsCollapsed
+ ? $options.i18n.expandDetailsTooltip
+ : $options.i18n.collapseDetailsTooltip
+ "
+ :icon="mr.mergeDetailsCollapsed ? 'chevron-lg-down' : 'chevron-lg-up'"
+ category="tertiary"
+ size="small"
+ class="gl-vertical-align-top"
+ @click="() => mr.toggleMergeDetails()"
+ />
</div>
</div>
</template>
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 a45823823f0..e2a9caf5419 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
@@ -34,7 +34,7 @@ export default {
<template>
<div class="mr-widget-body media gl-flex-wrap">
- <status-icon status="warning" />
+ <status-icon status="failed" />
<p class="media-body gl-m-0! gl-font-weight-bold gl-text-black-normal!">
{{ failedText }}
</p>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue
index f74826f95d3..79e878431ed 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_archived.vue
@@ -1,22 +1,24 @@
<script>
-import statusIcon from '../mr_widget_status_icon.vue';
+import StateContainer from '../state_container.vue';
export default {
name: 'MRWidgetArchived',
components: {
- statusIcon,
+ StateContainer,
+ },
+ props: {
+ mr: {
+ type: Object,
+ required: true,
+ },
},
};
</script>
+
<template>
- <div class="mr-widget-body media">
- <div class="space-children">
- <status-icon status="warning" show-disabled-button />
- </div>
- <div class="media-body">
- <span class="gl-ml-0! gl-text-body! bold">
- {{ s__('mrWidget|Merge unavailable: merge requests are read-only on archived projects.') }}
- </span>
- </div>
- </div>
+ <state-container :mr="mr" status="failed">
+ <span class="gl-font-weight-bold">
+ {{ s__('mrWidget|Merge unavailable: merge requests are read-only on archived projects.') }}
+ </span>
+ </state-container>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue
index 690acc9a6dc..3c6c2a44e70 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue
@@ -1,5 +1,5 @@
<script>
-import { GlSkeletonLoader, GlIcon, GlSprintf } from '@gitlab/ui';
+import { GlSkeletonLoader, GlSprintf } from '@gitlab/ui';
import autoMergeMixin from 'ee_else_ce/vue_merge_request_widget/mixins/auto_merge';
import autoMergeEnabledQuery from 'ee_else_ce/vue_merge_request_widget/queries/states/auto_merge_enabled.query.graphql';
import createFlash from '~/flash';
@@ -28,7 +28,6 @@ export default {
components: {
MrWidgetAuthor,
GlSkeletonLoader,
- GlIcon,
GlSprintf,
StateContainer,
},
@@ -151,7 +150,7 @@ export default {
};
</script>
<template>
- <state-container status="scheduled" :is-loading="loading" :actions="actions">
+ <state-container :mr="mr" status="scheduled" :is-loading="loading" :actions="actions">
<template #loading>
<gl-skeleton-loader :width="334" :height="30">
<rect x="0" y="3" width="24" height="24" rx="4" />
@@ -168,8 +167,5 @@ export default {
</gl-sprintf>
</h4>
</template>
- <template v-if="!loading" #icon>
- <gl-icon name="status_scheduled" :size="24" class="gl-text-blue-500 gl-mr-3 gl-mt-1" />
- </template>
</state-container>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
index b0cda85f361..39c56cbb93d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
@@ -58,8 +58,8 @@ export default {
};
</script>
<template>
- <state-container status="warning" :actions="actions">
- <span class="bold gl-ml-0!">
+ <state-container :mr="mr" status="failed" :actions="actions">
+ <span class="gl-font-weight-bold">
<template v-if="mergeError">{{ mergeError }}</template>
{{ s__('mrWidget|This merge request failed to be merged automatically') }}
</span>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
index e2d87d8d536..922075516f3 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
@@ -1,20 +1,23 @@
<script>
-import statusIcon from '../mr_widget_status_icon.vue';
+import StateContainer from '../state_container.vue';
export default {
name: 'MRWidgetChecking',
components: {
- statusIcon,
+ StateContainer,
+ },
+ props: {
+ mr: {
+ type: Object,
+ required: true,
+ },
},
};
</script>
<template>
- <div class="mr-widget-body media">
- <status-icon :show-disabled-button="true" status="loading" />
- <div class="media-body space-children">
- <span class="gl-ml-0! gl-text-body! bold">
- {{ s__('mrWidget|Checking if merge request can be merged…') }}
- </span>
- </div>
- </div>
+ <state-container :mr="mr" status="loading">
+ <span class="gl-font-weight-bold">
+ {{ s__('mrWidget|Checking if merge request can be merged…') }}
+ </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 61f7d26f51e..806f8f939a6 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,16 +1,14 @@
<script>
import MrWidgetAuthorTime from '../mr_widget_author_time.vue';
-import statusIcon from '../mr_widget_status_icon.vue';
+import StateContainer from '../state_container.vue';
export default {
name: 'MRWidgetClosed',
components: {
MrWidgetAuthorTime,
- statusIcon,
+ StateContainer,
},
props: {
- /* TODO: This is providing all store and service down when it
- only needs metrics and targetBranch */
mr: {
type: Object,
required: true,
@@ -19,15 +17,12 @@ export default {
};
</script>
<template>
- <div class="mr-widget-body media">
- <status-icon status="warning" />
- <div class="media-body">
- <mr-widget-author-time
- :action-text="s__('mrWidget|Closed by')"
- :author="mr.metrics.closedBy"
- :date-title="mr.metrics.closedAt"
- :date-readable="mr.metrics.readableClosedAt"
- />
- </div>
- </div>
+ <state-container :mr="mr" status="closed">
+ <mr-widget-author-time
+ :action-text="s__('mrWidget|Closed by')"
+ :author="mr.metrics.closedBy"
+ :date-title="mr.metrics.closedAt"
+ :date-readable="mr.metrics.readableClosedAt"
+ />
+ </state-container>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue
index 8abd915b93e..d60d3cfc9ea 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue
@@ -86,7 +86,7 @@ export default {
};
</script>
<template>
- <state-container status="warning" :is-loading="isLoading">
+ <state-container :mr="mr" status="failed" :is-loading="isLoading">
<template #loading>
<gl-skeleton-loader :width="334" :height="30">
<rect x="0" y="7" width="150" height="16" rx="4" />
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue
index 18103ac4a0e..8a7f15d8d1a 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue
@@ -1,16 +1,14 @@
<script>
-import { GlButton } from '@gitlab/ui';
import { stripHtml } from '~/lib/utils/text_utility';
import { sprintf, s__, n__ } from '~/locale';
import eventHub from '../../event_hub';
-import statusIcon from '../mr_widget_status_icon.vue';
+import StateContainer from '../state_container.vue';
export default {
name: 'MRWidgetFailedToMerge',
components: {
- GlButton,
- statusIcon,
+ StateContainer,
},
props: {
@@ -47,6 +45,16 @@ export default {
this.timer,
);
},
+ actions() {
+ return [
+ {
+ text: s__('mrWidget|Refresh now'),
+ onClick: () => this.refresh(),
+ testId: 'merge-request-failed-refresh-button',
+ dataQaSelector: 'merge_request_error_content',
+ },
+ ];
+ },
},
mounted() {
@@ -87,30 +95,18 @@ export default {
};
</script>
<template>
- <div class="mr-widget-body media">
- <template v-if="isRefreshing">
- <status-icon status="loading" />
- <span class="media-body bold js-refresh-label"> {{ s__('mrWidget|Refreshing now') }} </span>
- </template>
- <template v-else>
- <status-icon :show-disabled-button="true" status="warning" />
- <div class="media-body space-children">
- <span class="bold">
- <span v-if="mr.mergeError" class="has-error-message" data-testid="merge-error">
- {{ mergeError }}
- </span>
- <span v-else> {{ s__('mrWidget|Merge failed.') }} </span>
- <span :class="{ 'has-custom-error': mr.mergeError }"> {{ timerText }} </span>
- </span>
- <gl-button
- size="small"
- data-testid="merge-request-failed-refresh-button"
- data-qa-selector="merge_request_error_content"
- @click="refresh"
- >
- {{ s__('mrWidget|Refresh now') }}
- </gl-button>
- </div>
- </template>
- </div>
+ <state-container v-if="isRefreshing" :mr="mr" status="loading">
+ <span class="gl-font-weight-bold">
+ {{ s__('mrWidget|Refreshing now') }}
+ </span>
+ </state-container>
+ <state-container v-else :mr="mr" status="failed" :actions="actions">
+ <span class="gl-font-weight-bold">
+ <span v-if="mr.mergeError" class="has-error-message" data-testid="merge-error">
+ {{ mergeError }}
+ </span>
+ <span v-else> {{ s__('mrWidget|Merge failed.') }} </span>
+ <span :class="{ 'has-custom-error': mr.mergeError }"> {{ timerText }} </span>
+ </span>
+ </state-container>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
index 4416123cd51..e9298b0c856 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
@@ -1,5 +1,5 @@
<script>
-import { GlTooltipDirective, GlIcon } from '@gitlab/ui';
+import { GlTooltipDirective } from '@gitlab/ui';
import api from '~/api';
import createFlash from '~/flash';
import { s__, __ } from '~/locale';
@@ -16,7 +16,6 @@ export default {
},
components: {
MrWidgetAuthorTime,
- GlIcon,
StateContainer,
},
props: {
@@ -49,18 +48,6 @@ export default {
const { sourceBranchRemoved, isRemovingSourceBranch } = this.mr;
return !sourceBranchRemoved && (isRemovingSourceBranch || this.isMakingRequest);
},
- shouldShowMergedButtons() {
- const {
- canRevertInCurrentMR,
- canCherryPickInCurrentMR,
- revertInForkPath,
- cherryPickInForkPath,
- } = this.mr;
-
- return (
- canRevertInCurrentMR || canCherryPickInCurrentMR || revertInForkPath || cherryPickInForkPath
- );
- },
revertTitle() {
return s__('mrWidget|Revert this merge request in a new merge request');
},
@@ -163,10 +150,7 @@ export default {
};
</script>
<template>
- <state-container :actions="actions">
- <template #icon>
- <gl-icon name="merge" :size="24" class="gl-text-blue-500 gl-mr-3 gl-mt-1" />
- </template>
+ <state-container :mr="mr" :actions="actions" status="merged">
<mr-widget-author-time
:action-text="s__('mrWidget|Merged by')"
:author="mr.metrics.mergedBy"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue
index c7574a41bb8..51ac2576f75 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merging.vue
@@ -4,7 +4,7 @@ import simplePoll from '~/lib/utils/simple_poll';
import MergeRequest from '~/merge_request';
import eventHub from '../../event_hub';
import { MERGE_ACTIVE_STATUS_PHRASES, STATE_MACHINE } from '../../constants';
-import statusIcon from '../mr_widget_status_icon.vue';
+import StatusIcon from '../mr_widget_status_icon.vue';
const { transitions } = STATE_MACHINE;
const { MERGE_FAILURE } = transitions;
@@ -12,7 +12,7 @@ const { MERGE_FAILURE } = transitions;
export default {
name: 'MRWidgetMerging',
components: {
- statusIcon,
+ StatusIcon,
},
props: {
mr: {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue
index 659d12d1160..214d1b49732 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue
@@ -9,7 +9,7 @@ import {
MR_WIDGET_MISSING_BRANCH_RESTORE,
MR_WIDGET_MISSING_BRANCH_MANUALCLI,
} from '../../i18n';
-import statusIcon from '../mr_widget_status_icon.vue';
+import StatusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetMissingBranch',
@@ -19,7 +19,7 @@ export default {
components: {
GlIcon,
GlSprintf,
- statusIcon,
+ StatusIcon,
},
mixins: [glFeatureFlagMixin(), mergeRequestQueryVariablesMixin],
apollo: {
@@ -71,10 +71,10 @@ export default {
</script>
<template>
<div class="mr-widget-body media">
- <status-icon :show-disabled-button="true" status="warning" />
+ <status-icon :show-disabled-button="true" status="failed" />
<div class="media-body space-children">
- <span class="gl-ml-0! gl-text-body! bold js-branch-text" data-testid="widget-content">
+ <span class="gl-font-weight-bold js-branch-text" data-testid="widget-content">
<gl-sprintf :message="warning">
<template #code="{ content }">
<code>{{ content }}</code>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue
index c203d2824fa..d837551a813 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue
@@ -11,9 +11,9 @@ export default {
<template>
<div class="mr-widget-body media">
- <status-icon :show-disabled-button="true" status="success" />
+ <status-icon status="success" />
<div class="media-body space-children">
- <span class="bold">
+ <span class="gl-font-weight-bold">
{{
s__(`mrWidget|Ready to be merged automatically.
Ask someone with write access to this repository to merge this request`)
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue
index e99ee59b877..13920daca15 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue
@@ -12,9 +12,9 @@ export default {
</script>
<template>
<div class="mr-widget-body media">
- <status-icon :show-disabled-button="true" status="warning" />
+ <status-icon status="failed" />
<div class="media-body space-children">
- <span class="gl-ml-0! gl-text-body! bold">
+ <span class="gl-font-weight-bold">
{{
s__(
`mrWidget|Merge blocked: pipeline must succeed. It's waiting for a manual action to continue.`,
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
index 6c5fc916799..37c8d5d15f3 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
@@ -81,16 +81,19 @@ export default {
return 'loading';
}
if (!this.canPushToSourceBranch && !this.rebaseInProgress) {
- return 'warning';
+ return 'failed';
}
return 'success';
},
- showDisabledButton() {
- return ['failed', 'loading'].includes(this.status);
- },
fastForwardMergeText() {
return __('Merge blocked: the source branch must be rebased onto the target branch.');
},
+ showRebaseWithoutPipeline() {
+ return (
+ !this.mr.onlyAllowMergeIfPipelineSucceeds ||
+ (this.mr.onlyAllowMergeIfPipelineSucceeds && this.mr.allowMergeOnSkippedPipeline)
+ );
+ },
},
methods: {
rebase({ skipCi = false } = {}) {
@@ -149,7 +152,7 @@ export default {
};
</script>
<template>
- <state-container :status="status" :is-loading="isLoading">
+ <state-container :mr="mr" :status="status" :is-loading="isLoading">
<template #loading>
<gl-skeleton-loader :width="334" :height="30">
<rect x="0" y="3" width="24" height="24" rx="4" />
@@ -192,6 +195,7 @@ export default {
</template>
<template v-if="!isLoading" #actions>
<gl-button
+ v-if="showRebaseWithoutPipeline"
:loading="isMakingRequest"
variant="confirm"
size="small"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue
index d507e5f232b..3cbd171a035 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue
@@ -2,14 +2,14 @@
import { GlLink, GlSprintf } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { s__ } from '~/locale';
-import statusIcon from '../mr_widget_status_icon.vue';
+import StatusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'PipelineFailed',
components: {
GlLink,
GlSprintf,
- statusIcon,
+ StatusIcon,
},
computed: {
troubleshootingDocsPath() {
@@ -26,9 +26,9 @@ export default {
<template>
<div class="mr-widget-body media">
- <status-icon :show-disabled-button="true" status="warning" />
+ <status-icon status="failed" />
<div class="media-body space-children">
- <span class="gl-ml-0! gl-text-body! bold">
+ <span class="gl-font-weight-bold">
<gl-sprintf :message="$options.i18n.failedMessage">
<template #link="{ content }">
<gl-link :href="troubleshootingDocsPath" target="_blank">
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 d2c85b14999..78430abcfe9 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
@@ -680,6 +680,7 @@ export default {
:is-fast-forward-enabled="!shouldShowMergeEdit"
:commits-count="commitsCount"
:target-branch="stateData.targetBranch"
+ :merge-commit-path="mr.mergeCommitPath"
/>
</li>
<li v-if="mr.state !== 'closed'" class="gl-line-height-normal">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/sha_mismatch.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/sha_mismatch.vue
index d149f5208fc..27919f90cc3 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/sha_mismatch.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/sha_mismatch.vue
@@ -22,7 +22,7 @@ export default {
</script>
<template>
- <state-container status="warning">
+ <state-container :mr="mr" status="failed">
<span
class="gl-font-weight-bold gl-md-mr-3 gl-flex-grow-1 gl-ml-0! gl-text-body!"
data-qa-selector="head_mismatch_content"
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 035d62eaa59..8f2e4eb2131 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
@@ -24,7 +24,7 @@ export default {
</script>
<template>
- <state-container status="warning">
+ <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!"
>
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 cf7f83c014a..0458e9dfaf5 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
@@ -163,7 +163,7 @@ export default {
</script>
<template>
- <state-container status="warning">
+ <state-container :mr="mr" status="failed">
<span class="gl-font-weight-bold gl-ml-0! gl-text-body! gl-flex-grow-1">
{{ __("Merge blocked: merge request must be marked as ready. It's still marked as draft.") }}
</span>
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 f1c1bde256f..2f52ac70833 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
@@ -15,7 +15,12 @@ export default {
</script>
<template>
- <section role="region" :aria-label="__('Merge request reports')" data-testid="mr-widget-app">
+ <section
+ v-if="widgets.length"
+ role="region"
+ :aria-label="__('Merge request reports')"
+ data-testid="mr-widget-app"
+ >
<component
:is="widget"
v-for="(widget, index) in widgets"
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 9c8819327e6..c9fc2dde0bd 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,21 +1,32 @@
<script>
+import { GlButton, GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { normalizeHeaders } from '~/lib/utils/common_utils';
-import { __ } from '~/locale';
+import { sprintf, __ } from '~/locale';
import Poll from '~/lib/utils/poll';
import StatusIcon from '../extensions/status_icon.vue';
-import { EXTENSION_ICON_NAMES } from '../../constants';
+import ActionButtons from '../action_buttons.vue';
+import { EXTENSION_ICONS } from '../../constants';
+import ContentSection from './widget_content_section.vue';
const FETCH_TYPE_COLLAPSED = 'collapsed';
+const FETCH_TYPE_EXPANDED = 'expanded';
export default {
components: {
+ ActionButtons,
StatusIcon,
+ GlButton,
+ GlLoadingIcon,
+ ContentSection,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
},
props: {
/**
* @param {value.collapsed} Object
- * @param {value.extended} Object
+ * @param {value.expanded} Object
*/
value: {
type: Object,
@@ -35,7 +46,7 @@ export default {
type: Function,
required: true,
},
- fetchExtendedData: {
+ fetchExpandedData: {
type: Function,
required: false,
default: undefined,
@@ -61,7 +72,16 @@ export default {
type: String,
default: 'neutral',
required: false,
- validator: (value) => Object.keys(EXTENSION_ICON_NAMES).indexOf(value) > -1,
+ validator: (value) => Object.keys(EXTENSION_ICONS).indexOf(value) > -1,
+ },
+ isCollapsible: {
+ type: Boolean,
+ required: true,
+ },
+ actionButtons: {
+ type: Array,
+ required: false,
+ default: () => [],
},
widgetName: {
type: String,
@@ -70,10 +90,22 @@ export default {
},
data() {
return {
+ isExpandedForTheFirstTime: true,
+ isCollapsed: true,
isLoading: false,
- error: null,
+ isLoadingExpandedContent: false,
+ summaryError: null,
+ contentError: null,
};
},
+ computed: {
+ collapseButtonLabel() {
+ return sprintf(this.isCollapsed ? __('Show details') : __('Hide details'));
+ },
+ summaryStatusIcon() {
+ return this.summaryError ? this.$options.failedStatusIcon : this.statusIconName;
+ },
+ },
watch: {
isLoading(newValue) {
this.$emit('is-loading', newValue);
@@ -85,12 +117,36 @@ export default {
try {
await this.fetch(this.fetchCollapsedData, FETCH_TYPE_COLLAPSED);
} catch {
- this.error = this.errorText;
+ this.summaryError = this.errorText;
}
this.isLoading = false;
},
methods: {
+ toggleCollapsed() {
+ this.isCollapsed = !this.isCollapsed;
+
+ if (this.isExpandedForTheFirstTime && typeof this.fetchExpandedData === 'function') {
+ this.isExpandedForTheFirstTime = false;
+ this.fetchExpandedContent();
+ }
+ },
+ async fetchExpandedContent() {
+ this.isLoadingExpandedContent = true;
+ this.contentError = null;
+
+ try {
+ await this.fetch(this.fetchExpandedData, FETCH_TYPE_EXPANDED);
+ } catch {
+ this.contentError = this.errorText;
+
+ // Reset these values so that we allow refetching
+ this.isExpandedForTheFirstTime = true;
+ this.isCollapsed = true;
+ }
+
+ this.isLoadingExpandedContent = false;
+ },
fetch(handler, dataType) {
const requests = this.multiPolling ? handler() : [handler];
@@ -125,6 +181,7 @@ export default {
});
},
},
+ failedStatusIcon: EXTENSION_ICONS.failed,
};
</script>
@@ -135,24 +192,58 @@ export default {
:level="1"
:name="widgetName"
:is-loading="isLoading"
- :icon-name="statusIconName"
+ :icon-name="summaryStatusIcon"
/>
<div
class="media-body gl-display-flex gl-flex-direction-row! gl-align-self-center"
data-testid="widget-extension-top-level"
>
<div class="gl-flex-grow-1" data-testid="widget-extension-top-level-summary">
- <slot name="summary">{{ isLoading ? loadingText : summary }}</slot>
+ <span v-if="summaryError">{{ summaryError }}</span>
+ <slot v-else name="summary">{{ isLoading ? loadingText : summary }}</slot>
+ </div>
+ <action-buttons
+ v-if="actionButtons.length > 0"
+ :widget="widgetName"
+ :tertiary-buttons="actionButtons"
+ />
+ <div
+ v-if="isCollapsible"
+ class="gl-border-l-1 gl-border-l-solid gl-border-gray-100 gl-ml-3 gl-pl-3 gl-h-6"
+ >
+ <gl-button
+ v-gl-tooltip
+ :title="collapseButtonLabel"
+ :aria-expanded="`${!isCollapsed}`"
+ :aria-label="collapseButtonLabel"
+ :icon="isCollapsed ? 'chevron-lg-down' : 'chevron-lg-up'"
+ category="tertiary"
+ data-testid="toggle-button"
+ size="small"
+ @click="toggleCollapsed"
+ />
</div>
- <!-- actions will go here -->
- <!-- toggle button will go here -->
</div>
</div>
<div
+ v-if="!isCollapsed || contentError"
class="mr-widget-grouped-section gl-relative"
data-testid="widget-extension-collapsed-section"
>
- <slot name="content">{{ content }}</slot>
+ <div v-if="isLoadingExpandedContent" class="report-block-container gl-text-center">
+ <gl-loading-icon size="sm" inline /> {{ __('Loading...') }}
+ </div>
+ <content-section
+ v-else-if="contentError"
+ class="report-block-container"
+ :status-icon-name="$options.failedStatusIcon"
+ :widget-name="widgetName"
+ >
+ {{ contentError }}
+ </content-section>
+ <slot v-else name="content">
+ {{ content }}
+ </slot>
</div>
</section>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_section.vue b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_section.vue
new file mode 100644
index 00000000000..61e3744b5dc
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/widget/widget_content_section.vue
@@ -0,0 +1,35 @@
+<script>
+import { EXTENSION_ICONS } from '../../constants';
+import StatusIcon from '../extensions/status_icon.vue';
+
+export default {
+ components: {
+ StatusIcon,
+ },
+ props: {
+ statusIconName: {
+ type: String,
+ default: '',
+ required: false,
+ validator: (value) => value === '' || Object.keys(EXTENSION_ICONS).includes(value),
+ },
+ widgetName: {
+ type: String,
+ required: true,
+ },
+ },
+};
+</script>
+<template>
+ <div class="gl-px-7">
+ <div class="gl-pl-4 gl-display-flex">
+ <status-icon
+ v-if="statusIconName"
+ :level="2"
+ :name="widgetName"
+ :icon-name="statusIconName"
+ />
+ <slot name="default"></slot>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/constants.js b/app/assets/javascripts/vue_merge_request_widget/constants.js
index c148a35209f..be4e34ffff0 100644
--- a/app/assets/javascripts/vue_merge_request_widget/constants.js
+++ b/app/assets/javascripts/vue_merge_request_widget/constants.js
@@ -140,6 +140,7 @@ export const EXTENSION_ICON_NAMES = {
neutral: 'status-neutral',
error: 'status-alert',
notice: 'status-alert',
+ scheduled: 'status-scheduled',
severityCritical: 'severity-critical',
severityHigh: 'severity-high',
severityMedium: 'severity-medium',
@@ -155,6 +156,7 @@ export const EXTENSION_ICON_CLASS = {
neutral: 'gl-text-gray-400',
error: 'gl-text-red-500',
notice: 'gl-text-gray-500',
+ scheduled: 'gl-text-blue-500',
severityCritical: 'gl-text-red-800',
severityHigh: 'gl-text-red-600',
severityMedium: 'gl-text-orange-400',
diff --git a/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/index.js b/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/index.js
index c74445a5b80..97b9b59e2c3 100644
--- a/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/index.js
+++ b/app/assets/javascripts/vue_merge_request_widget/extensions/test_report/index.js
@@ -32,7 +32,7 @@ export default {
});
});
- return fileNames.join(' ');
+ return fileNames.join(' ').trim();
},
summary(data) {
if (data.parsingInProgress) {
diff --git a/app/assets/javascripts/vue_merge_request_widget/mixins/mr_widget_pipeline.js b/app/assets/javascripts/vue_merge_request_widget/mixins/mr_widget_pipeline.js
deleted file mode 100644
index 7b77d7475bc..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/mixins/mr_widget_pipeline.js
+++ /dev/null
@@ -1,10 +0,0 @@
-export default {
- computed: {
- triggered() {
- return [];
- },
- triggeredBy() {
- return [];
- },
- },
-};
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 1e25143e15c..c8a2a8d119b 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
@@ -59,28 +59,28 @@ export default {
Loading,
ExtensionsContainer,
WidgetContainer,
- 'mr-widget-suggest-pipeline': WidgetSuggestPipeline,
+ MrWidgetSuggestPipeline: WidgetSuggestPipeline,
MrWidgetPipelineContainer,
MrWidgetAlertMessage,
- 'mr-widget-merged': MergedState,
- 'mr-widget-closed': ClosedState,
- 'mr-widget-merging': MergingState,
- 'mr-widget-failed-to-merge': FailedToMerge,
- 'mr-widget-wip': WorkInProgressState,
- 'mr-widget-archived': ArchivedState,
- 'mr-widget-conflicts': ConflictsState,
- 'mr-widget-nothing-to-merge': NothingToMergeState,
- 'mr-widget-not-allowed': NotAllowedState,
- 'mr-widget-missing-branch': MissingBranchState,
- 'mr-widget-ready-to-merge': () => import('./components/states/new_ready_to_merge.vue'),
- 'sha-mismatch': ShaMismatch,
- 'mr-widget-checking': CheckingState,
- 'mr-widget-unresolved-discussions': UnresolvedDiscussionsState,
- 'mr-widget-pipeline-blocked': PipelineBlockedState,
- 'mr-widget-pipeline-failed': PipelineFailedState,
+ MrWidgetMerged: MergedState,
+ MrWidgetClosed: ClosedState,
+ MrWidgetMerging: MergingState,
+ MrWidgetFailedToMerge: FailedToMerge,
+ MrWidgetWip: WorkInProgressState,
+ MrWidgetArchived: ArchivedState,
+ MrWidgetConflicts: ConflictsState,
+ MrWidgetNothingToMerge: NothingToMergeState,
+ MrWidgetNotAllowed: NotAllowedState,
+ MrWidgetMissingBranch: MissingBranchState,
+ MrWidgetReadyToMerge: () => import('./components/states/new_ready_to_merge.vue'),
+ ShaMismatch,
+ MrWidgetChecking: CheckingState,
+ MrWidgetUnresolvedDiscussions: UnresolvedDiscussionsState,
+ MrWidgetPipelineBlocked: PipelineBlockedState,
+ MrWidgetPipelineFailed: PipelineFailedState,
MrWidgetAutoMergeEnabled,
- 'mr-widget-auto-merge-failed': AutoMergeFailed,
- 'mr-widget-rebase': RebaseState,
+ MrWidgetAutoMergeFailed: AutoMergeFailed,
+ MrWidgetRebase: RebaseState,
SourceBranchRemovalStatus,
GroupedCodequalityReportsApp: () =>
import('../reports/codequality_report/grouped_codequality_reports_app.vue'),
@@ -230,6 +230,11 @@ export default {
shouldShowCodeQualityExtension() {
return window.gon?.features?.refactorCodeQualityExtension;
},
+ shouldShowMergeDetails() {
+ if (this.mr.state === 'readyToMerge') return true;
+
+ return !this.mr.mergeDetailsCollapsed;
+ },
},
watch: {
'mr.machineValue': {
@@ -318,6 +323,12 @@ export default {
this.initPolling();
this.bindEventHubListeners();
eventHub.$on('mr.discussion.updated', this.checkStatus);
+
+ window.addEventListener('resize', () => {
+ if (window.innerWidth >= 768) {
+ this.mr.toggleMergeDetails(false);
+ }
+ });
},
getServiceEndpoints(store) {
return {
@@ -428,6 +439,7 @@ export default {
.then((res) => {
if (res.data) {
const el = document.createElement('div');
+ // eslint-disable-next-line no-unsanitized/property
el.innerHTML = res.data;
document.body.appendChild(el);
document.dispatchEvent(new CustomEvent('merged:UpdateActions'));
@@ -620,7 +632,12 @@ export default {
<div class="mr-widget-section" data-qa-selector="mr_widget_content">
<component :is="componentName" :mr="mr" :service="service" />
- <ready-to-merge v-if="mr.commitsCount" :mr="mr" :service="service" />
+ <ready-to-merge
+ v-if="mr.commitsCount"
+ v-show="shouldShowMergeDetails"
+ :mr="mr"
+ :service="service"
+ />
</div>
</div>
<mr-widget-pipeline-container
diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql
index 981c667f27a..eac72ffb2f2 100644
--- a/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql
+++ b/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql
@@ -3,6 +3,7 @@ query getState($projectPath: ID!, $iid: String!) {
id
archived
onlyAllowMergeIfPipelineSucceeds
+ allowMergeOnSkippedPipeline
mergeRequest(iid: $iid) {
id
autoMergeEnabled
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 146cf7e11a7..e6ff586892f 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
@@ -28,6 +28,7 @@ export default class MergeRequestStore {
this.stateMachine = machine(STATE_MACHINE.definition);
this.machineValue = this.stateMachine.value;
+ this.mergeDetailsCollapsed = window.innerWidth < 768;
this.setPaths(data);
@@ -168,6 +169,7 @@ export default class MergeRequestStore {
this.mergeError = data.merge_error;
this.mergeStatus = data.merge_status;
this.onlyAllowMergeIfPipelineSucceeds = data.only_allow_merge_if_pipeline_succeeds || false;
+ this.allowMergeOnSkippedPipeline = data.allow_merge_on_skipped_pipeline || false;
this.projectArchived = data.project_archived;
this.isSHAMismatch = this.sha !== data.diff_head_sha;
this.shouldBeRebased = Boolean(data.should_be_rebased);
@@ -195,6 +197,7 @@ export default class MergeRequestStore {
this.projectArchived = project.archived;
this.onlyAllowMergeIfPipelineSucceeds = project.onlyAllowMergeIfPipelineSucceeds;
+ this.allowMergeOnSkippedPipeline = project.allowMergeOnSkippedPipeline;
this.autoMergeEnabled = mergeRequest.autoMergeEnabled;
this.canBeMerged = mergeRequest.mergeStatus === 'can_be_merged';
@@ -403,4 +406,8 @@ export default class MergeRequestStore {
this.transitionStateMachine(transitionOptions);
}
+
+ toggleMergeDetails(val = !this.mergeDetailsCollapsed) {
+ this.mergeDetailsCollapsed = val;
+ }
}
diff --git a/app/assets/javascripts/vue_shared/components/actions_button.vue b/app/assets/javascripts/vue_shared/components/actions_button.vue
index 6db18afe51c..c6c22f9c61f 100644
--- a/app/assets/javascripts/vue_shared/components/actions_button.vue
+++ b/app/assets/javascripts/vue_shared/components/actions_button.vue
@@ -77,7 +77,7 @@ export default {
<template v-for="(action, index) in actions">
<gl-dropdown-item
:key="action.key"
- :is-check-item="true"
+ is-check-item
:is-checked="action.key === selectedAction.key"
:secondary-text="action.secondaryText"
:data-qa-selector="`${action.key}_menu_item`"
diff --git a/app/assets/javascripts/vue_shared/components/ci_badge_link.vue b/app/assets/javascripts/vue_shared/components/ci_badge_link.vue
index 5de71c35be9..84bd6bca601 100644
--- a/app/assets/javascripts/vue_shared/components/ci_badge_link.vue
+++ b/app/assets/javascripts/vue_shared/components/ci_badge_link.vue
@@ -1,5 +1,6 @@
<script>
import { GlTooltipDirective } from '@gitlab/ui';
+import { visitUrl } from '~/lib/utils/url_utility';
import CiIcon from './ci_icon.vue';
/**
* Renders CI Badge link with CI icon and status text based on
@@ -57,13 +58,28 @@ export default {
},
cssClass() {
const className = this.status.group;
- return className ? `ci-status ci-${className} qa-status-badge` : 'ci-status qa-status-badge';
+ return className ? `ci-status ci-${className}` : 'ci-status';
+ },
+ },
+ methods: {
+ navigateToPipeline() {
+ visitUrl(this.detailsPath);
+
+ // event used for tracking
+ this.$emit('ciStatusBadgeClick');
},
},
};
</script>
<template>
- <a v-gl-tooltip :href="detailsPath" :class="cssClass" :title="title">
+ <a
+ v-gl-tooltip
+ :class="cssClass"
+ class="gl-cursor-pointer"
+ :title="title"
+ data-qa-selector="status_badge_link"
+ @click="navigateToPipeline"
+ >
<ci-icon :status="status" :css-classes="iconClasses" />
<template v-if="showText">
diff --git a/app/assets/javascripts/vue_shared/components/code_block.stories.js b/app/assets/javascripts/vue_shared/components/code_block.stories.js
new file mode 100644
index 00000000000..e02a346c1de
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/code_block.stories.js
@@ -0,0 +1,18 @@
+import CodeBlock from './code_block.vue';
+
+export default {
+ component: CodeBlock,
+ title: 'vue_shared/code_block',
+};
+
+const Template = (args, { argTypes }) => ({
+ components: { CodeBlock },
+ props: Object.keys(argTypes),
+ template: '<code-block v-bind="$props" />',
+});
+
+export const Default = Template.bind({});
+Default.args = {
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ code: `git commit -a "Message"\ngit push`,
+};
diff --git a/app/assets/javascripts/vue_shared/components/code_block.vue b/app/assets/javascripts/vue_shared/components/code_block.vue
index 9856f35c7f6..4a69845d3a4 100644
--- a/app/assets/javascripts/vue_shared/components/code_block.vue
+++ b/app/assets/javascripts/vue_shared/components/code_block.vue
@@ -4,7 +4,8 @@ export default {
props: {
code: {
type: String,
- required: true,
+ required: false,
+ default: '',
},
maxHeight: {
type: String,
@@ -32,5 +33,5 @@ export default {
class="code-block rounded code"
:class="$options.userColorScheme"
:style="styleObject"
- ><code class="d-block">{{ code }}</code></pre>
+ ><slot><code class="d-block">{{ code }}</code></slot></pre>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/code_block_highlighted.stories.js b/app/assets/javascripts/vue_shared/components/code_block_highlighted.stories.js
new file mode 100644
index 00000000000..bf81a811d16
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/code_block_highlighted.stories.js
@@ -0,0 +1,18 @@
+import CodeBlockHighlighted from './code_block_highlighted.vue';
+
+export default {
+ component: CodeBlockHighlighted,
+ title: 'vue_shared/code_block_highlighted',
+};
+
+const Template = (args, { argTypes }) => ({
+ components: { CodeBlockHighlighted },
+ props: Object.keys(argTypes),
+ template: '<code-block-highlighted v-bind="$props" />',
+});
+
+export const Default = Template.bind({});
+Default.args = {
+ code: `const foo = 1;\nconsole.log(foo + ' yay')`,
+ language: 'javascript',
+};
diff --git a/app/assets/javascripts/vue_shared/components/code_block_highlighted.vue b/app/assets/javascripts/vue_shared/components/code_block_highlighted.vue
new file mode 100644
index 00000000000..65b08b608e8
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/code_block_highlighted.vue
@@ -0,0 +1,72 @@
+<script>
+import { GlSafeHtmlDirective } from '@gitlab/ui';
+
+import languageLoader from '~/content_editor/services/highlight_js_language_loader';
+import CodeBlock from './code_block.vue';
+
+export default {
+ name: 'CodeBlockHighlighted',
+ directives: {
+ SafeHtml: GlSafeHtmlDirective,
+ },
+ components: {
+ CodeBlock,
+ },
+ props: {
+ code: {
+ type: String,
+ required: true,
+ },
+ language: {
+ type: String,
+ required: true,
+ },
+ maxHeight: {
+ type: String,
+ required: false,
+ default: 'initial',
+ },
+ },
+ data() {
+ return {
+ hljs: null,
+ languageLoaded: false,
+ };
+ },
+ computed: {
+ highlighted() {
+ if (this.hljs && this.languageLoaded) {
+ return this.hljs.highlight(this.code, { language: this.language }).value;
+ }
+
+ return this.code;
+ },
+ },
+ async mounted() {
+ this.hljs = await this.loadHighlightJS();
+ if (this.language) {
+ await this.loadLanguage();
+ }
+ },
+ methods: {
+ async loadLanguage() {
+ try {
+ const { default: languageDefinition } = await languageLoader[this.language]();
+
+ this.hljs.registerLanguage(this.language, languageDefinition);
+ this.languageLoaded = true;
+ } catch (e) {
+ this.$emit('error', e);
+ }
+ },
+ loadHighlightJS() {
+ return import('highlight.js/lib/core');
+ },
+ },
+};
+</script>
+<template>
+ <code-block :max-height="maxHeight" class="highlight">
+ <span v-safe-html="highlighted"></span>
+ </code-block>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/color_select_dropdown/dropdown_contents_color_view.vue b/app/assets/javascripts/vue_shared/components/color_select_dropdown/dropdown_contents_color_view.vue
index 91906388049..22f3c35b9c3 100644
--- a/app/assets/javascripts/vue_shared/components/color_select_dropdown/dropdown_contents_color_view.vue
+++ b/app/assets/javascripts/vue_shared/components/color_select_dropdown/dropdown_contents_color_view.vue
@@ -42,8 +42,8 @@ export default {
v-for="color in colors"
:key="color.color"
:is-checked="isColorSelected(color)"
- :is-check-centered="true"
- :is-check-item="true"
+ is-check-centered
+ is-check-item
@click.native.capture.stop="handleColorClick(color)"
>
<color-item :color="color.color" :title="color.title" />
diff --git a/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.stories.js b/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.stories.js
index 8481280f25f..7ecc309db52 100644
--- a/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.stories.js
+++ b/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.stories.js
@@ -3,7 +3,7 @@ import ConfirmDanger from './confirm_danger.vue';
export default {
component: ConfirmDanger,
- title: 'vue_shared/components/modals/confirm_danger_modal',
+ title: 'vue_shared/modals/confirm_danger_modal',
};
const Template = (args, { argTypes }) => ({
diff --git a/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_lib.js b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_lib.js
index aec67a18a05..38b1a587b34 100644
--- a/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_lib.js
+++ b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker_lib.js
@@ -1,4 +1,4 @@
-import dateformat from 'dateformat';
+import dateformat from '~/lib/dateformat';
import { __ } from '~/locale';
/**
diff --git a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.stories.js b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.stories.js
index eeed5e9dc3a..8256d953466 100644
--- a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.stories.js
+++ b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.stories.js
@@ -5,7 +5,7 @@ import DropdownWidget from './dropdown_widget.vue';
export default {
component: DropdownWidget,
- title: 'vue_shared/components/dropdown/dropdown_widget/dropdown_widget',
+ title: 'vue_shared/dropdown/dropdown_widget/dropdown_widget',
};
const Template = (args, { argTypes }) => ({
diff --git a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue
index 840911dc99c..faa50a50c69 100644
--- a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue
+++ b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue
@@ -149,8 +149,8 @@ export default {
v-for="option in presetOptions"
:key="option.id"
:is-checked="isSelected(option)"
- :is-check-centered="true"
- :is-check-item="true"
+ is-check-centered
+ is-check-item
@click.native.capture.stop="selectOption(option)"
>
<slot name="preset-item" :item="option">
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 5d7f4ae2a01..ffe09634a3b 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
@@ -46,7 +46,7 @@ export const SortDirection = {
export const FILTERED_SEARCH_LABELS = 'labels';
export const FILTERED_SEARCH_TERM = 'filtered-search-term';
-export const TOKEN_TITLE_ASSIGNEE = __('Assignee');
+export const TOKEN_TITLE_ASSIGNEE = s__('SearchToken|Assignee');
export const TOKEN_TITLE_AUTHOR = __('Author');
export const TOKEN_TITLE_CONFIDENTIAL = __('Confidential');
export const TOKEN_TITLE_CONTACT = s__('Crm|Contact');
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 33d507dad57..e311df6e66f 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
@@ -369,7 +369,7 @@ export default {
<gl-dropdown-item
v-for="sortBy in sortOptions"
:key="sortBy.id"
- :is-check-item="true"
+ is-check-item
:is-checked="sortBy.id === selectedSortOption.id"
@click="handleSortOptionClick(sortBy)"
>{{ sortBy.title }}</gl-dropdown-item
diff --git a/app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.stories.js b/app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.stories.js
index cdd7a074f34..377f1e7c136 100644
--- a/app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.stories.js
+++ b/app/assets/javascripts/vue_shared/components/form/input_copy_toggle_visibility.stories.js
@@ -2,7 +2,7 @@ import InputCopyToggleVisibility from './input_copy_toggle_visibility.vue';
export default {
component: InputCopyToggleVisibility,
- title: 'vue_shared/components/form/input_copy_toggle_visibility',
+ title: 'vue_shared/form/input_copy_toggle_visibility',
};
const defaultProps = {
diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue
index 1d1b65aa1af..458dfe0ed23 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/header.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue
@@ -182,7 +182,7 @@ export default {
<div class="md-header">
<gl-tabs content-class="gl-display-none">
<gl-tab
- title-link-class="gl-pt-3 gl-px-3 js-md-write-button"
+ title-link-class="gl-py-4 gl-px-3 js-md-write-button"
:title="$options.i18n.writeTabTitle"
:active="!previewMarkdown"
data-testid="write-tab"
@@ -190,7 +190,7 @@ export default {
/>
<gl-tab
v-if="enablePreview"
- title-link-class="gl-pt-3 gl-px-3 js-md-preview-button"
+ title-link-class="gl-py-4 gl-px-3 js-md-preview-button"
:title="$options.i18n.previewTabTitle"
:active="previewMarkdown"
data-testid="preview-tab"
@@ -201,7 +201,7 @@ export default {
<div
data-testid="md-header-toolbar"
:class="{ 'gl-display-none!': previewMarkdown }"
- class="md-header-toolbar gl-ml-auto gl-pb-3 gl-justify-content-center"
+ class="md-header-toolbar gl-ml-auto gl-py-2 gl-justify-content-center"
>
<template v-if="canSuggest">
<toolbar-button
diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
index de3eda6b04f..9b81444fc04 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
@@ -163,6 +163,7 @@ export default {
// resets the container HTML (replaces it with the updated noteHTML)
// calls `renderSuggestions` once the updated noteHTML is added to the DOM
+ // eslint-disable-next-line no-unsanitized/property
this.$refs.container.innerHTML = this.noteHtml;
this.isRendered = false;
this.renderSuggestions();
diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
index aa325862f06..b5640e12541 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
@@ -72,7 +72,7 @@ export default {
</gl-sprintf>
</template>
</div>
- <span v-if="canAttachFile" class="uploading-container">
+ <span v-if="canAttachFile" class="uploading-container gl-line-height-32">
<span class="uploading-progress-container hide">
<gl-icon name="paperclip" />
<span class="attaching-file-message"></span>
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 3593ea16968..7e99f1b01b2 100644
--- a/app/assets/javascripts/vue_shared/components/notes/system_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/system_note.vue
@@ -29,7 +29,7 @@ import descriptionVersionHistoryMixin from 'ee_else_ce/notes/mixins/description_
import '~/behaviors/markdown/render_gfm';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
-import noteHeader from '~/notes/components/note_header.vue';
+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 TimelineEntryItem from './timeline_entry_item.vue';
@@ -43,7 +43,7 @@ export default {
name: 'SystemNote',
components: {
GlIcon,
- noteHeader,
+ NoteHeader,
TimelineEntryItem,
GlButton,
GlSkeletonLoader,
diff --git a/app/assets/javascripts/vue_shared/components/pagination_bar/pagination_bar.stories.js b/app/assets/javascripts/vue_shared/components/pagination_bar/pagination_bar.stories.js
index e31446f4bb8..f16afc77164 100644
--- a/app/assets/javascripts/vue_shared/components/pagination_bar/pagination_bar.stories.js
+++ b/app/assets/javascripts/vue_shared/components/pagination_bar/pagination_bar.stories.js
@@ -3,7 +3,7 @@ import PaginationBar from './pagination_bar.vue';
export default {
component: PaginationBar,
- title: 'vue_shared/components/pagination_bar/pagination_bar',
+ title: 'vue_shared/pagination_bar/pagination_bar',
};
const Template = (args, { argTypes }) => ({
diff --git a/app/assets/javascripts/vue_shared/components/project_avatar.stories.js b/app/assets/javascripts/vue_shared/components/project_avatar.stories.js
index 110c6c73bad..bfb30c74cb8 100644
--- a/app/assets/javascripts/vue_shared/components/project_avatar.stories.js
+++ b/app/assets/javascripts/vue_shared/components/project_avatar.stories.js
@@ -2,7 +2,7 @@ import ProjectAvatar from './project_avatar.vue';
export default {
component: ProjectAvatar,
- title: 'vue_shared/components/project_avatar',
+ title: 'vue_shared/project_avatar',
};
const Template = (args, { argTypes }) => ({
@@ -13,8 +13,7 @@ const Template = (args, { argTypes }) => ({
export const Default = Template.bind({});
Default.args = {
- projectAvatarUrl:
- 'https://gitlab.com/uploads/-/system/project/avatar/278964/logo-extra-whitespace.png?width=64',
+ projectAvatarUrl: 'https://gitlab.com/uploads/-/system/project/avatar/278964/project_avatar.png',
projectName: 'GitLab',
};
diff --git a/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.stories.js b/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.stories.js
index 9700117a3da..4021e23a3f6 100644
--- a/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.stories.js
+++ b/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.stories.js
@@ -2,7 +2,7 @@ import ProjectListItem from './project_list_item.vue';
export default {
component: ProjectListItem,
- title: 'vue_shared/components/project_selector/project_list_item',
+ title: 'vue_shared/project_selector/project_list_item',
};
const Template = (args, { argTypes }) => ({
diff --git a/app/assets/javascripts/vue_shared/components/registry/persisted_dropdown_selection.vue b/app/assets/javascripts/vue_shared/components/registry/persisted_dropdown_selection.vue
index 43a8e241d77..32d7cdad568 100644
--- a/app/assets/javascripts/vue_shared/components/registry/persisted_dropdown_selection.vue
+++ b/app/assets/javascripts/vue_shared/components/registry/persisted_dropdown_selection.vue
@@ -49,7 +49,7 @@ export default {
v-for="option in parsedOptions"
:key="option.value"
:is-checked="option.selected"
- :is-check-item="true"
+ is-check-item
@click="setSelected(option.value)"
>
{{ option.label }}
diff --git a/app/assets/javascripts/vue_shared/components/runner_instructions/runner_instructions_modal.vue b/app/assets/javascripts/vue_shared/components/runner_instructions/runner_instructions_modal.vue
index bfaf3b92c34..c5d3704ead9 100644
--- a/app/assets/javascripts/vue_shared/components/runner_instructions/runner_instructions_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/runner_instructions/runner_instructions_modal.vue
@@ -253,7 +253,7 @@ export default {
<gl-dropdown-item
v-for="architecture in architectures"
:key="architecture.name"
- :is-check-item="true"
+ is-check-item
:is-checked="selectedArchitecture === architecture.name"
data-testid="architecture-dropdown-item"
@click="selectArchitecture(architecture.name)"
diff --git a/app/assets/javascripts/vue_shared/components/settings/settings_block.stories.js b/app/assets/javascripts/vue_shared/components/settings/settings_block.stories.js
index 5242743ad30..53e4a08e486 100644
--- a/app/assets/javascripts/vue_shared/components/settings/settings_block.stories.js
+++ b/app/assets/javascripts/vue_shared/components/settings/settings_block.stories.js
@@ -2,7 +2,7 @@ import SettingsBlock from './settings_block.vue';
export default {
component: SettingsBlock,
- title: 'vue_shared/components/settings/settings_block',
+ title: 'vue_shared/settings/settings_block',
};
const Template = (args, { argTypes }) => ({
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue b/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue
index dfa2ca2d20c..0f5560ff628 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue
@@ -180,7 +180,7 @@ export default {
<gl-dropdown-item
v-for="project in projects"
:key="project.id"
- :is-check-item="true"
+ is-check-item
:is-checked="isSelectedProject(project)"
@click.stop.prevent="handleProjectSelect(project)"
>{{ project.name_with_namespace }}</gl-dropdown-item
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
index f595e635f2c..8d3d4d5f86a 100644
--- 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
@@ -154,8 +154,8 @@ export default {
v-for="(label, index) in visibleLabels"
:key="label.id"
:is-checked="isLabelSelected(label)"
- :is-check-centered="true"
- :is-check-item="true"
+ is-check-centered
+ is-check-item
:active="shouldHighlightFirstItem && index === 0"
active-class="is-focused"
data-testid="labels-list"
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue
index aaddab43e2a..154a8e866d0 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue
@@ -80,6 +80,7 @@ export default {
v-if="!showDropdownContentsCreateView"
ref="searchInput"
:value="searchKey"
+ :placeholder="__('Search labels')"
:disabled="labelsFetchInProgress"
data-qa-selector="dropdown_input_field"
data-testid="dropdown-input-field"
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js
index 294e5bd9f90..8a2bab4cb9a 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js
+++ b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js
@@ -4,7 +4,7 @@ import TodoButton from './todo_button.vue';
export default {
component: TodoButton,
- title: 'vue_shared/components/sidebar/todo_toggle/todo_button',
+ title: 'vue_shared/sidebar/todo_toggle/todo_button',
};
const Template = (args, { argTypes }) => ({
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/constants.js b/app/assets/javascripts/vue_shared/components/source_viewer/constants.js
index cc930d67fa4..30f57f506a6 100644
--- a/app/assets/javascripts/vue_shared/components/source_viewer/constants.js
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/constants.js
@@ -81,6 +81,7 @@ export const ROUGE_TO_HLJS_LANGUAGE_MAP = {
protobuf: 'protobuf',
puppet: 'puppet',
python: 'python',
+ python3: 'python',
q: 'q',
qml: 'qml',
r: 'r',
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/plugins/wrap_comments.js b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/wrap_comments.js
index 5be92af5b55..8b52df83fdf 100644
--- a/app/assets/javascripts/vue_shared/components/source_viewer/plugins/wrap_comments.js
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/wrap_comments.js
@@ -3,6 +3,8 @@ import { HLJS_COMMENT_SELECTOR } from '../constants';
const createWrapper = (content) => {
const span = document.createElement('span');
span.className = HLJS_COMMENT_SELECTOR;
+
+ // eslint-disable-next-line no-unsanitized/property
span.innerHTML = content;
return span.outerHTML;
};
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 f471db24889..9c6c12eac7d 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
@@ -42,7 +42,7 @@ export default {
return {
languageDefinition: null,
content: this.blob.rawTextBlob,
- language: ROUGE_TO_HLJS_LANGUAGE_MAP[this.blob.language],
+ language: ROUGE_TO_HLJS_LANGUAGE_MAP[this.blob.language?.toLowerCase()],
hljs: null,
firstChunk: null,
chunks: {},
@@ -62,7 +62,7 @@ export default {
const supportedLanguages = Object.keys(languageLoader);
return (
!supportedLanguages.includes(this.language) &&
- !supportedLanguages.includes(this.blob.language)
+ !supportedLanguages.includes(this.blob.language?.toLowerCase())
);
},
},
diff --git a/app/assets/javascripts/vue_shared/components/split_button.vue b/app/assets/javascripts/vue_shared/components/split_button.vue
index 994fa68fb1a..c0aef42b0f2 100644
--- a/app/assets/javascripts/vue_shared/components/split_button.vue
+++ b/app/assets/javascripts/vue_shared/components/split_button.vue
@@ -68,7 +68,7 @@ export default {
<template v-for="(item, itemIndex) in actionItems">
<gl-dropdown-item
:key="item.eventName"
- :is-check-item="true"
+ is-check-item
:is-checked="selectedItem === item"
@click="changeSelectedItem(item)"
>
diff --git a/app/assets/javascripts/vue_shared/components/timezone_dropdown.vue b/app/assets/javascripts/vue_shared/components/timezone_dropdown.vue
index 42334d80eec..ce65266cbc9 100644
--- a/app/assets/javascripts/vue_shared/components/timezone_dropdown.vue
+++ b/app/assets/javascripts/vue_shared/components/timezone_dropdown.vue
@@ -72,7 +72,7 @@ export default {
v-for="timezone in filteredResults"
:key="timezone.formattedTimezone"
:is-checked="isSelected(timezone)"
- :is-check-item="true"
+ is-check-item
@click="selectTimezone(timezone)"
>
{{ timezone.formattedTimezone }}
diff --git a/app/assets/javascripts/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.stories.js b/app/assets/javascripts/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.stories.js
index f27901a30a9..e621442e601 100644
--- a/app/assets/javascripts/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.stories.js
+++ b/app/assets/javascripts/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.stories.js
@@ -5,7 +5,7 @@ const defaultWidth = '250px';
export default {
component: TooltipOnTruncate,
- title: 'vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue',
+ title: 'vue_shared/tooltip_on_truncate/tooltip_on_truncate.vue',
};
const createStory = ({ ...options }) => {
diff --git a/app/assets/javascripts/vue_shared/components/upload_dropzone/upload_dropzone.vue b/app/assets/javascripts/vue_shared/components/upload_dropzone/upload_dropzone.vue
index 424cab20c7e..a001b6bdf24 100644
--- a/app/assets/javascripts/vue_shared/components/upload_dropzone/upload_dropzone.vue
+++ b/app/assets/javascripts/vue_shared/components/upload_dropzone/upload_dropzone.vue
@@ -149,7 +149,7 @@ export default {
>
<slot>
<button
- class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4"
+ class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4 gl-mb-0"
type="button"
@click="openFileUpload"
>
diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image_new.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image_new.vue
index cd610314292..6bd66981860 100644
--- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image_new.vue
+++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image_new.vue
@@ -90,9 +90,8 @@ export default {
</script>
<template>
- <span>
+ <span ref="userAvatar">
<gl-avatar
- ref="userAvatar"
:class="{
lazy: lazy,
[cssClasses]: true,
@@ -108,7 +107,7 @@ export default {
tooltipText ||
$slots.default /* eslint-disable-line @gitlab/vue-prefer-dollar-scopedslots */
"
- :target="() => $refs.userAvatar.$el"
+ :target="() => $refs.userAvatar"
:placement="tooltipPlacement"
boundary="window"
>
diff --git a/app/assets/javascripts/vue_shared/components/user_deletion_obstacles/user_deletion_obstacles_list.stories.js b/app/assets/javascripts/vue_shared/components/user_deletion_obstacles/user_deletion_obstacles_list.stories.js
index d2030c14029..1f0f4cde234 100644
--- a/app/assets/javascripts/vue_shared/components/user_deletion_obstacles/user_deletion_obstacles_list.stories.js
+++ b/app/assets/javascripts/vue_shared/components/user_deletion_obstacles/user_deletion_obstacles_list.stories.js
@@ -5,7 +5,7 @@ import UserDeletionObstaclesList from './user_deletion_obstacles_list.vue';
export default {
component: UserDeletionObstaclesList,
- title: 'vue_shared/components/user_deletion_obstacles/user_deletion_obstacles_list',
+ title: 'vue_shared/user_deletion_obstacles/user_deletion_obstacles_list',
};
const Template = (args, { argTypes }) => ({
diff --git a/app/assets/javascripts/vue_shared/components/user_popover/constants.js b/app/assets/javascripts/vue_shared/components/user_popover/constants.js
index 1d49aefd297..bcbe72b4b4f 100644
--- a/app/assets/javascripts/vue_shared/components/user_popover/constants.js
+++ b/app/assets/javascripts/vue_shared/components/user_popover/constants.js
@@ -1 +1,14 @@
+import { __ } from '~/locale';
+
export const USER_POPOVER_DELAY = 200;
+export const I18N_ERROR_FOLLOW = __(
+ 'An error occurred while trying to follow this user, please try again.',
+);
+export const I18N_ERROR_UNFOLLOW = __(
+ 'An error occurred while trying to unfollow this user, please try again.',
+);
+export const I18N_USER_BLOCKED = __('User is blocked');
+export const I18N_USER_BUSY = __('Busy');
+export const I18N_USER_LEARN = __('Learn more about %{name}');
+export const I18N_USER_FOLLOW = __('Follow');
+export const I18N_USER_UNFOLLOW = __('Unfollow');
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 2b9804796ae..4b39a8e45bb 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
@@ -9,23 +9,31 @@ import {
GlButton,
GlAvatarLabeled,
} from '@gitlab/ui';
-import { __ } from '~/locale';
import { glEmojiTag } from '~/emoji';
import createFlash from '~/flash';
import { followUser, unfollowUser } from '~/rest_api';
import { isUserBusy } from '~/set_status_modal/utils';
import Tracking from '~/tracking';
-import { USER_POPOVER_DELAY } from './constants';
+import {
+ I18N_ERROR_FOLLOW,
+ I18N_ERROR_UNFOLLOW,
+ I18N_USER_BLOCKED,
+ I18N_USER_BUSY,
+ I18N_USER_LEARN,
+ I18N_USER_FOLLOW,
+ I18N_USER_UNFOLLOW,
+ USER_POPOVER_DELAY,
+} from './constants';
const MAX_SKELETON_LINES = 4;
export default {
name: 'UserPopover',
maxSkeletonLines: MAX_SKELETON_LINES,
+ I18N_USER_BLOCKED,
+ I18N_USER_BUSY,
+ I18N_USER_LEARN,
USER_POPOVER_DELAY,
- i18n: {
- busy: __('Busy'),
- },
components: {
GlIcon,
GlLink,
@@ -94,7 +102,7 @@ export default {
toggleFollowButtonText() {
if (this.toggleFollowLoading) return null;
- return this.user?.isFollowed ? __('Unfollow') : __('Follow');
+ return this.user?.isFollowed ? I18N_USER_UNFOLLOW : I18N_USER_FOLLOW;
},
toggleFollowButtonVariant() {
return this.user?.isFollowed ? 'default' : 'confirm';
@@ -102,6 +110,9 @@ export default {
hasPronouns() {
return Boolean(this.user?.pronouns?.trim());
},
+ isBlocked() {
+ return this.user?.state === 'blocked';
+ },
isBusy() {
return isUserBusy(this.availabilityStatus);
},
@@ -129,7 +140,7 @@ export default {
this.$emit('follow');
} catch (error) {
createFlash({
- message: __('An error occurred while trying to follow this user, please try again.'),
+ message: I18N_ERROR_FOLLOW,
error,
captureError: true,
});
@@ -149,7 +160,7 @@ export default {
this.$emit('unfollow');
} catch (error) {
createFlash({
- message: __('An error occurred while trying to unfollow this user, please try again.'),
+ message: I18N_ERROR_UNFOLLOW,
error,
captureError: true,
});
@@ -189,16 +200,21 @@ export default {
:label="user.name"
:sub-label="username"
>
- <gl-button
- v-if="shouldRenderToggleFollowButton"
- class="gl-mt-3 gl-align-self-start"
- :variant="toggleFollowButtonVariant"
- :loading="toggleFollowLoading"
- size="small"
- data-testid="toggle-follow-button"
- @click="toggleFollow"
- >{{ toggleFollowButtonText }}</gl-button
- >
+ <template v-if="isBlocked">
+ <span class="gl-mt-4 gl-font-style-italic">{{ $options.I18N_USER_BLOCKED }}</span>
+ </template>
+ <template v-else>
+ <gl-button
+ v-if="shouldRenderToggleFollowButton"
+ class="gl-mt-3 gl-align-self-start"
+ :variant="toggleFollowButtonVariant"
+ :loading="toggleFollowLoading"
+ size="small"
+ data-testid="toggle-follow-button"
+ @click="toggleFollow"
+ >{{ toggleFollowButtonText }}</gl-button
+ >
+ </template>
<template #meta>
<span
@@ -208,7 +224,7 @@ export default {
>({{ user.pronouns }})</span
>
<span v-if="isBusy" class="gl-text-gray-500 gl-font-sm gl-font-weight-normal gl-p-1"
- >({{ $options.i18n.busy }})</span
+ >({{ $options.I18N_USER_BUSY }})</span
>
</template>
</gl-avatar-labeled>
@@ -223,39 +239,41 @@ export default {
/>
</template>
<template v-else>
- <div class="gl-text-gray-500">
- <div v-if="user.bio" class="gl-display-flex gl-mb-2">
- <gl-icon name="profile" class="gl-flex-shrink-0" />
- <span ref="bio" class="gl-ml-2">{{ user.bio }}</span>
+ <template v-if="!isBlocked">
+ <div class="gl-text-gray-500">
+ <div v-if="user.bio" class="gl-display-flex gl-mb-2">
+ <gl-icon name="profile" class="gl-flex-shrink-0" />
+ <span ref="bio" class="gl-ml-2">{{ user.bio }}</span>
+ </div>
+ <div v-if="user.workInformation" class="gl-display-flex gl-mb-2">
+ <gl-icon name="work" class="gl-flex-shrink-0" />
+ <span ref="workInformation" class="gl-ml-2">{{ user.workInformation }}</span>
+ </div>
+ <div v-if="user.location" class="gl-display-flex gl-mb-2">
+ <gl-icon name="location" class="gl-flex-shrink-0" />
+ <span class="gl-ml-2">{{ user.location }}</span>
+ </div>
+ <div
+ v-if="user.localTime && !user.bot"
+ class="gl-display-flex gl-mb-2"
+ data-testid="user-popover-local-time"
+ >
+ <gl-icon name="clock" class="gl-flex-shrink-0" />
+ <span class="gl-ml-2">{{ user.localTime }}</span>
+ </div>
</div>
- <div v-if="user.workInformation" class="gl-display-flex gl-mb-2">
- <gl-icon name="work" class="gl-flex-shrink-0" />
- <span ref="workInformation" class="gl-ml-2">{{ user.workInformation }}</span>
+ <div v-if="statusHtml" class="gl-mb-2" data-testid="user-popover-status">
+ <span v-safe-html:[$options.safeHtmlConfig]="statusHtml"></span>
</div>
- <div v-if="user.location" class="gl-display-flex gl-mb-2">
- <gl-icon name="location" class="gl-flex-shrink-0" />
- <span class="gl-ml-2">{{ user.location }}</span>
+ <div v-if="user.bot && user.websiteUrl" class="gl-text-blue-500">
+ <gl-icon name="question" />
+ <gl-link data-testid="user-popover-bot-docs-link" :href="user.websiteUrl">
+ <gl-sprintf :message="$options.I18N_USER_LEARN">
+ <template #name>{{ user.name }}</template>
+ </gl-sprintf>
+ </gl-link>
</div>
- <div
- v-if="user.localTime && !user.bot"
- class="gl-display-flex gl-mb-2"
- data-testid="user-popover-local-time"
- >
- <gl-icon name="clock" class="gl-flex-shrink-0" />
- <span class="gl-ml-2">{{ user.localTime }}</span>
- </div>
- </div>
- <div v-if="statusHtml" class="gl-mb-2" data-testid="user-popover-status">
- <span v-safe-html:[$options.safeHtmlConfig]="statusHtml"></span>
- </div>
- <div v-if="user.bot && user.websiteUrl" class="gl-text-blue-500">
- <gl-icon name="question" />
- <gl-link data-testid="user-popover-bot-docs-link" :href="user.websiteUrl">
- <gl-sprintf :message="__('Learn more about %{username}')">
- <template #username>{{ user.name }}</template>
- </gl-sprintf>
- </gl-link>
- </div>
+ </template>
</template>
</div>
</gl-popover>
diff --git a/app/assets/javascripts/vue_shared/components/user_select/user_select.vue b/app/assets/javascripts/vue_shared/components/user_select/user_select.vue
index 43a590c2367..3180bd0d283 100644
--- a/app/assets/javascripts/vue_shared/components/user_select/user_select.vue
+++ b/app/assets/javascripts/vue_shared/components/user_select/user_select.vue
@@ -320,7 +320,7 @@ export default {
<gl-dropdown-item
v-if="isSearchEmpty"
:is-checked="selectedIsEmpty"
- :is-check-centered="true"
+ is-check-centered
data-testid="unassign"
@click.native.capture.stop="$emit('input', [])"
>
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 38083327593..7e735f358eb 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
@@ -232,7 +232,11 @@ export default {
</span>
</div>
<div class="issuable-info">
- <work-item-type-icon v-if="showWorkItemTypeIcon" :work-item-type="issuable.type" />
+ <work-item-type-icon
+ v-if="showWorkItemTypeIcon"
+ :work-item-type="issuable.type"
+ show-tooltip-on-hover
+ />
<slot v-if="hasSlotContents('reference')" name="reference"></slot>
<span v-else data-testid="issuable-reference" class="issuable-reference">
{{ reference }}
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 d2fc2c66924..e42720bf1db 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
@@ -10,6 +10,7 @@ export default {
mounted() {
const legacyEntry = document.querySelector(this.selector);
if (legacyEntry.tagName === 'TEMPLATE') {
+ // eslint-disable-next-line no-unsanitized/property
this.$el.innerHTML = legacyEntry.innerHTML;
} else {
this.source = legacyEntry.parentNode;
diff --git a/app/assets/javascripts/vue_shared/security_reports/graphql/queries/security_report_merge_request_download_paths.query.graphql b/app/assets/javascripts/vue_shared/security_reports/graphql/queries/security_report_merge_request_download_paths.query.graphql
index 2e80db30e9a..6a83669d206 100644
--- a/app/assets/javascripts/vue_shared/security_reports/graphql/queries/security_report_merge_request_download_paths.query.graphql
+++ b/app/assets/javascripts/vue_shared/security_reports/graphql/queries/security_report_merge_request_download_paths.query.graphql
@@ -14,6 +14,7 @@ query securityReportDownloadPaths(
id
name
artifacts {
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
nodes {
downloadPath
fileType
diff --git a/app/assets/javascripts/vue_shared/security_reports/graphql/queries/security_report_pipeline_download_paths.query.graphql b/app/assets/javascripts/vue_shared/security_reports/graphql/queries/security_report_pipeline_download_paths.query.graphql
index e4f0c392b91..1f1e56a5876 100644
--- a/app/assets/javascripts/vue_shared/security_reports/graphql/queries/security_report_pipeline_download_paths.query.graphql
+++ b/app/assets/javascripts/vue_shared/security_reports/graphql/queries/security_report_pipeline_download_paths.query.graphql
@@ -4,6 +4,7 @@ query getPipelineCorpuses($projectPath: ID!, $iid: ID, $reportTypes: [SecurityRe
project(fullPath: $projectPath) {
id
pipeline(iid: $iid) {
+ # eslint-disable-next-line @graphql-eslint/require-id-when-available
...JobArtifacts
}
}
diff --git a/app/assets/javascripts/webpack_non_compiled_placeholder.js b/app/assets/javascripts/webpack_non_compiled_placeholder.js
index af671e72129..c1baa7b8dd3 100644
--- a/app/assets/javascripts/webpack_non_compiled_placeholder.js
+++ b/app/assets/javascripts/webpack_non_compiled_placeholder.js
@@ -20,6 +20,7 @@ const reloadMessage = LIVE_RELOAD
? 'You have live_reload enabled, the page will reload automatically when complete.'
: 'You have live_reload disabled, the page will reload automatically in a few seconds.';
+// eslint-disable-next-line no-unsanitized/property
div.innerHTML = `
<!-- https://github.com/webpack/media/blob/master/logo/icon-square-big.svg -->
<svg height="50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 1200">
diff --git a/app/assets/javascripts/work_items/components/item_title.vue b/app/assets/javascripts/work_items/components/item_title.vue
index 551ebbadb21..b2c8b7ae1db 100644
--- a/app/assets/javascripts/work_items/components/item_title.vue
+++ b/app/assets/javascripts/work_items/components/item_title.vue
@@ -39,14 +39,14 @@ export default {
:class="{ 'gl-cursor-text': disabled }"
aria-labelledby="item-title"
>
- <div
+ <span
id="item-title"
ref="titleEl"
role="textbox"
:aria-label="__('Title')"
:data-placeholder="placeholder"
:contenteditable="!disabled"
- class="gl-px-4 gl-py-3 gl-ml-n4 gl-border gl-border-white gl-rounded-base"
+ class="gl-px-4 gl-py-3 gl-ml-n4 gl-border gl-border-white gl-rounded-base gl-display-block"
:class="{ 'gl-hover-border-gray-200 gl-pseudo-placeholder': !disabled }"
@blur="handleBlur"
@keyup="handleInput"
@@ -55,8 +55,7 @@ export default {
@keydown.meta.u.prevent
@keydown.ctrl.b.prevent
@keydown.meta.b.prevent
+ >{{ title }}</span
>
- {{ title }}
- </div>
</h2>
</template>
diff --git a/app/assets/javascripts/work_items/components/work_item_actions.vue b/app/assets/javascripts/work_items/components/work_item_actions.vue
index 2753c3fa388..9f9d94ec3c2 100644
--- a/app/assets/javascripts/work_items/components/work_item_actions.vue
+++ b/app/assets/javascripts/work_items/components/work_item_actions.vue
@@ -8,10 +8,14 @@ import {
} from '@gitlab/ui';
import { s__ } from '~/locale';
import Tracking from '~/tracking';
+import {
+ sprintfWorkItem,
+ I18N_WORK_ITEM_DELETE,
+ I18N_WORK_ITEM_ARE_YOU_SURE_DELETE,
+} from '../constants';
export default {
i18n: {
- deleteTask: s__('WorkItem|Delete task'),
enableTaskConfidentiality: s__('WorkItem|Turn on confidentiality'),
disableTaskConfidentiality: s__('WorkItem|Turn off confidentiality'),
},
@@ -31,6 +35,11 @@ export default {
required: false,
default: null,
},
+ workItemType: {
+ type: String,
+ required: false,
+ default: null,
+ },
canUpdate: {
type: Boolean,
required: false,
@@ -53,6 +62,14 @@ export default {
},
},
emits: ['deleteWorkItem', 'toggleWorkItemConfidentiality'],
+ computed: {
+ i18n() {
+ return {
+ deleteWorkItem: sprintfWorkItem(I18N_WORK_ITEM_DELETE, this.workItemType),
+ areYouSureDelete: sprintfWorkItem(I18N_WORK_ITEM_ARE_YOU_SURE_DELETE, this.workItemType),
+ };
+ },
+ },
methods: {
handleToggleWorkItemConfidentiality() {
this.track('click_toggle_work_item_confidentiality');
@@ -75,6 +92,7 @@ export default {
<div>
<gl-dropdown
icon="ellipsis_v"
+ data-testid="work-item-actions-dropdown"
text-sr-only
:text="__('More actions')"
category="tertiary"
@@ -97,20 +115,18 @@ export default {
v-if="canDelete"
v-gl-modal="'work-item-confirm-delete'"
data-testid="delete-action"
- >{{ $options.i18n.deleteTask }}</gl-dropdown-item
+ >{{ i18n.deleteWorkItem }}</gl-dropdown-item
>
</gl-dropdown>
<gl-modal
modal-id="work-item-confirm-delete"
- :title="$options.i18n.deleteWorkItem"
- :ok-title="$options.i18n.deleteWorkItem"
+ :title="i18n.deleteWorkItem"
+ :ok-title="i18n.deleteWorkItem"
ok-variant="danger"
@ok="handleDeleteWorkItem"
@hide="handleCancelDeleteWorkItem"
>
- {{
- s__('WorkItem|Are you sure you want to delete the task? This action cannot be reversed.')
- }}
+ {{ i18n.areYouSureDelete }}
</gl-modal>
</div>
</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 7342f215b5e..4585426edaa 100644
--- a/app/assets/javascripts/work_items/components/work_item_assignees.vue
+++ b/app/assets/javascripts/work_items/components/work_item_assignees.vue
@@ -8,6 +8,7 @@ import {
GlButton,
GlDropdownItem,
GlDropdownDivider,
+ GlIntersectionObserver,
} from '@gitlab/ui';
import { debounce } from 'lodash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
@@ -19,7 +20,7 @@ import Tracking from '~/tracking';
import SidebarParticipant from '~/sidebar/components/assignees/sidebar_participant.vue';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import updateWorkItemMutation from '../graphql/update_work_item.mutation.graphql';
-import { i18n, TRACKING_CATEGORY_SHOW } from '../constants';
+import { i18n, TRACKING_CATEGORY_SHOW, DEFAULT_PAGE_SIZE_ASSIGNEES } from '../constants';
function isTokenSelectorElement(el) {
return (
@@ -50,9 +51,9 @@ export default {
InviteMembersTrigger,
GlDropdownItem,
GlDropdownDivider,
+ GlIntersectionObserver,
},
mixins: [Tracking.mixin()],
- inject: ['fullPath'],
props: {
workItemId: {
type: String,
@@ -80,6 +81,10 @@ export default {
required: false,
default: false,
},
+ fullPath: {
+ type: String,
+ required: true,
+ },
},
data() {
return {
@@ -87,12 +92,15 @@ export default {
searchStarted: false,
localAssignees: this.assignees.map(addClass),
searchKey: '',
- searchUsers: [],
+ users: {
+ nodes: [],
+ },
currentUser: null,
+ isLoadingMore: false,
};
},
apollo: {
- searchUsers: {
+ users: {
query() {
return userSearchQuery;
},
@@ -100,13 +108,14 @@ export default {
return {
fullPath: this.fullPath,
search: this.searchKey,
+ first: DEFAULT_PAGE_SIZE_ASSIGNEES,
};
},
skip() {
return !this.searchStarted;
},
update(data) {
- return data.workspace?.users?.nodes.map((node) => addClass({ ...node, ...node.user }));
+ return data.workspace?.users;
},
error() {
this.$emit('error', i18n.fetchError);
@@ -117,6 +126,12 @@ export default {
},
},
computed: {
+ searchUsers() {
+ return this.users.nodes.map((node) => addClass({ ...node, ...node.user }));
+ },
+ pageInfo() {
+ return this.users.pageInfo;
+ },
tracking() {
return {
category: TRACKING_CATEGORY_SHOW,
@@ -131,7 +146,7 @@ export default {
return !this.isEditing ? 'gl-shadow-none!' : '';
},
isLoadingUsers() {
- return this.$apollo.queries.searchUsers.loading;
+ return this.$apollo.queries.users.loading;
},
assigneeText() {
return n__('WorkItem|Assignee', 'WorkItem|Assignees', this.localAssignees.length);
@@ -159,6 +174,12 @@ export default {
assigneeIds() {
return this.localAssignees.map(({ id }) => id);
},
+ hasNextPage() {
+ return this.pageInfo?.hasNextPage;
+ },
+ showIntersectionSkeletonLoader() {
+ return this.isLoadingMore && this.dropdownItems.length;
+ },
},
watch: {
assignees: {
@@ -221,6 +242,16 @@ export default {
this.isEditing = true;
this.searchStarted = true;
},
+ async fetchMoreAssignees() {
+ this.isLoadingMore = true;
+ await this.$apollo.queries.users.fetchMore({
+ variables: {
+ after: this.pageInfo.endCursor,
+ first: DEFAULT_PAGE_SIZE_ASSIGNEES,
+ },
+ });
+ this.isLoadingMore = false;
+ },
async focusTokenSelector() {
this.handleFocus();
await this.$nextTick();
@@ -263,7 +294,7 @@ export default {
</script>
<template>
- <div class="form-row gl-mb-5 work-item-assignees gl-relative">
+ <div class="form-row gl-mb-5 work-item-assignees gl-relative gl-flex-nowrap">
<span
class="gl-font-weight-bold col-lg-2 col-3 gl-pt-2 min-w-fit-content gl-overflow-wrap-break"
data-testid="assignees-title"
@@ -275,7 +306,7 @@ export default {
:container-class="containerClass"
:class="{ 'gl-hover-border-gray-200': canUpdate }"
:dropdown-items="dropdownItems"
- :loading="isLoadingUsers"
+ :loading="isLoadingUsers && !isLoadingMore"
:view-only="!canUpdate"
:allow-clear-all="isEditing"
class="assignees-selector gl-flex-grow-1 gl-border gl-border-white gl-rounded-base col-9 gl-align-self-start gl-px-0! gl-mx-2"
@@ -326,17 +357,32 @@ export default {
<rect width="280" height="20" x="10" y="130" rx="4" />
</gl-skeleton-loader>
</template>
- <template v-if="canInviteMembers" #dropdown-footer>
- <gl-dropdown-divider />
- <gl-dropdown-item @click="closeDropdown">
- <invite-members-trigger
- :display-text="__('Invite members')"
- trigger-element="side-nav"
- icon="plus"
- trigger-source="work-item-assignees-dropdown"
- classes="gl-display-block gl-text-body! gl-hover-text-decoration-none gl-pb-2"
- />
- </gl-dropdown-item>
+ <template #dropdown-footer>
+ <gl-intersection-observer
+ v-if="hasNextPage && !isLoadingUsers"
+ @appear="fetchMoreAssignees"
+ />
+ <gl-skeleton-loader
+ v-if="showIntersectionSkeletonLoader"
+ :height="100"
+ data-testid="next-page-loading"
+ class="gl-text-center gl-py-3"
+ >
+ <rect width="380" height="20" x="10" y="15" rx="4" />
+ <rect width="280" height="20" x="10" y="50" rx="4" />
+ </gl-skeleton-loader>
+ <div v-if="canInviteMembers">
+ <gl-dropdown-divider />
+ <gl-dropdown-item @click="closeDropdown">
+ <invite-members-trigger
+ :display-text="__('Invite members')"
+ trigger-element="side-nav"
+ icon="plus"
+ trigger-source="work-item-assignees-dropdown"
+ classes="gl-display-block gl-text-body! gl-hover-text-decoration-none gl-pb-2"
+ />
+ </gl-dropdown-item>
+ </div>
</template>
</gl-token-selector>
</div>
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 cf59789ce2d..c2e4a50fe31 100644
--- a/app/assets/javascripts/work_items/components/work_item_description.vue
+++ b/app/assets/javascripts/work_items/components/work_item_description.vue
@@ -8,7 +8,7 @@ import { __, s__ } from '~/locale';
import Tracking from '~/tracking';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import workItemQuery from '../graphql/work_item.query.graphql';
-import updateWorkItemWidgetsMutation from '../graphql/update_work_item_widgets.mutation.graphql';
+import updateWorkItemMutation from '../graphql/update_work_item.mutation.graphql';
import { i18n, TRACKING_CATEGORY_SHOW, WIDGET_TYPE_DESCRIPTION } from '../constants';
export default {
@@ -21,12 +21,15 @@ export default {
MarkdownField,
},
mixins: [Tracking.mixin()],
- inject: ['fullPath'],
props: {
workItemId: {
type: String,
required: true,
},
+ fullPath: {
+ type: String,
+ required: true,
+ },
},
markdownDocsPath: helpPagePath('user/markdown'),
data() {
@@ -139,9 +142,9 @@ export default {
this.track('updated_description');
const {
- data: { workItemUpdateWidgets },
+ data: { workItemUpdate },
} = await this.$apollo.mutate({
- mutation: updateWorkItemWidgetsMutation,
+ mutation: updateWorkItemMutation,
variables: {
input: {
id: this.workItem.id,
@@ -152,8 +155,8 @@ export default {
},
});
- if (workItemUpdateWidgets.errors?.length) {
- throw new Error(workItemUpdateWidgets.errors[0]);
+ if (workItemUpdate.errors?.length) {
+ throw new Error(workItemUpdate.errors[0]);
}
this.isEditing = false;
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 a5580c14a7a..3d25df9fcb8 100644
--- a/app/assets/javascripts/work_items/components/work_item_detail.vue
+++ b/app/assets/javascripts/work_items/components/work_item_detail.vue
@@ -16,12 +16,14 @@ import {
WIDGET_TYPE_ASSIGNEES,
WIDGET_TYPE_LABELS,
WIDGET_TYPE_DESCRIPTION,
+ WIDGET_TYPE_START_AND_DUE_DATE,
WIDGET_TYPE_WEIGHT,
WIDGET_TYPE_HIERARCHY,
WORK_ITEM_VIEWED_STORAGE_KEY,
} from '../constants';
import workItemQuery from '../graphql/work_item.query.graphql';
+import workItemDatesSubscription from '../graphql/work_item_dates.subscription.graphql';
import workItemTitleSubscription from '../graphql/work_item_title.subscription.graphql';
import updateWorkItemMutation from '../graphql/update_work_item.mutation.graphql';
import updateWorkItemTaskMutation from '../graphql/update_work_item_task.mutation.graphql';
@@ -30,9 +32,9 @@ import WorkItemActions from './work_item_actions.vue';
import WorkItemState from './work_item_state.vue';
import WorkItemTitle from './work_item_title.vue';
import WorkItemDescription from './work_item_description.vue';
+import WorkItemDueDate from './work_item_due_date.vue';
import WorkItemAssignees from './work_item_assignees.vue';
import WorkItemLabels from './work_item_labels.vue';
-import WorkItemWeight from './work_item_weight.vue';
import WorkItemInformation from './work_item_information.vue';
export default {
@@ -50,10 +52,11 @@ export default {
WorkItemAssignees,
WorkItemActions,
WorkItemDescription,
+ WorkItemDueDate,
WorkItemLabels,
WorkItemTitle,
WorkItemState,
- WorkItemWeight,
+ WorkItemWeight: () => import('ee_component/work_items/components/work_item_weight.vue'),
WorkItemInformation,
LocalStorageSync,
WorkItemTypeIcon,
@@ -98,14 +101,36 @@ export default {
error() {
this.error = this.$options.i18n.fetchError;
},
- subscribeToMore: {
- document: workItemTitleSubscription,
- variables() {
- return {
- issuableId: this.workItemId,
- };
- },
+ result() {
+ if (!this.isModal) {
+ const path = this.workItem.project?.fullPath
+ ? ` · ${this.workItem.project.fullPath}`
+ : '';
+
+ document.title = `${this.workItem.title} · ${this.workItem?.workItemType?.name}${path}`;
+ }
},
+ subscribeToMore: [
+ {
+ document: workItemTitleSubscription,
+ variables() {
+ return {
+ issuableId: this.workItemId,
+ };
+ },
+ },
+ {
+ document: workItemDatesSubscription,
+ variables() {
+ return {
+ issuableId: this.workItemId,
+ };
+ },
+ skip() {
+ return !this.workItemDueDate;
+ },
+ },
+ ],
},
},
computed: {
@@ -121,6 +146,9 @@ export default {
canDelete() {
return this.workItem?.userPermissions?.deleteWorkItem;
},
+ fullPath() {
+ return this.workItem?.project.fullPath;
+ },
workItemsMvc2Enabled() {
return this.glFeatures.workItemsMvc2;
},
@@ -133,6 +161,11 @@ export default {
workItemLabels() {
return this.workItem?.mockWidgets?.find((widget) => widget.type === WIDGET_TYPE_LABELS);
},
+ workItemDueDate() {
+ return this.workItem?.widgets?.find(
+ (widget) => widget.type === WIDGET_TYPE_START_AND_DUE_DATE,
+ );
+ },
workItemWeight() {
return this.workItem?.widgets?.find((widget) => widget.type === WIDGET_TYPE_WEIGHT);
},
@@ -276,11 +309,12 @@ export default {
<work-item-actions
v-if="canUpdate || canDelete"
:work-item-id="workItem.id"
+ :work-item-type="workItemType"
:can-delete="canDelete"
:can-update="canUpdate"
:is-confidential="workItem.confidential"
:is-parent-confidential="parentWorkItemConfidentiality"
- @deleteWorkItem="$emit('deleteWorkItem')"
+ @deleteWorkItem="$emit('deleteWorkItem', workItemType)"
@toggleWorkItemConfidentiality="toggleConfidentiality"
@error="error = $event"
/>
@@ -317,21 +351,32 @@ export default {
:can-update="canUpdate"
@error="error = $event"
/>
+ <work-item-assignees
+ v-if="workItemAssignees"
+ :can-update="canUpdate"
+ :work-item-id="workItem.id"
+ :assignees="workItemAssignees.assignees.nodes"
+ :allows-multiple-assignees="workItemAssignees.allowsMultipleAssignees"
+ :work-item-type="workItemType"
+ :can-invite-members="workItemAssignees.canInviteMembers"
+ :full-path="fullPath"
+ @error="error = $event"
+ />
<template v-if="workItemsMvc2Enabled">
- <work-item-assignees
- v-if="workItemAssignees"
- :can-update="canUpdate"
- :work-item-id="workItem.id"
- :assignees="workItemAssignees.assignees.nodes"
- :allows-multiple-assignees="workItemAssignees.allowsMultipleAssignees"
- :work-item-type="workItemType"
- :can-invite-members="workItemAssignees.canInviteMembers"
- @error="error = $event"
- />
<work-item-labels
v-if="workItemLabels"
:work-item-id="workItem.id"
:can-update="canUpdate"
+ :full-path="fullPath"
+ @error="error = $event"
+ />
+ <work-item-due-date
+ v-if="workItemDueDate"
+ :can-update="canUpdate"
+ :due-date="workItemDueDate.dueDate"
+ :start-date="workItemDueDate.startDate"
+ :work-item-id="workItem.id"
+ :work-item-type="workItemType"
@error="error = $event"
/>
</template>
@@ -347,6 +392,7 @@ export default {
<work-item-description
v-if="hasDescriptionWidget"
:work-item-id="workItem.id"
+ :full-path="fullPath"
class="gl-pt-5"
@error="error = $event"
/>
diff --git a/app/assets/javascripts/work_items/components/work_item_due_date.vue b/app/assets/javascripts/work_items/components/work_item_due_date.vue
new file mode 100644
index 00000000000..05f8fa8f5e1
--- /dev/null
+++ b/app/assets/javascripts/work_items/components/work_item_due_date.vue
@@ -0,0 +1,257 @@
+<script>
+import { GlButton, GlDatepicker, GlFormGroup } from '@gitlab/ui';
+import * as Sentry from '@sentry/browser';
+import { getDateWithUTC, newDateAsLocaleTime } from '~/lib/utils/datetime/date_calculation_utility';
+import { s__ } from '~/locale';
+import Tracking from '~/tracking';
+import {
+ I18N_WORK_ITEM_ERROR_UPDATING,
+ sprintfWorkItem,
+ TRACKING_CATEGORY_SHOW,
+} from '~/work_items/constants';
+import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
+
+const nullObjectDate = new Date(0);
+
+export default {
+ i18n: {
+ addDueDate: s__('WorkItem|Add due date'),
+ addStartDate: s__('WorkItem|Add start date'),
+ dates: s__('WorkItem|Dates'),
+ dueDate: s__('WorkItem|Due date'),
+ none: s__('WorkItem|None'),
+ startDate: s__('WorkItem|Start date'),
+ },
+ dueDateInputId: 'due-date-input',
+ startDateInputId: 'start-date-input',
+ components: {
+ GlButton,
+ GlDatepicker,
+ GlFormGroup,
+ },
+ mixins: [Tracking.mixin()],
+ props: {
+ canUpdate: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ dueDate: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ startDate: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ workItemId: {
+ type: String,
+ required: true,
+ },
+ workItemType: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ dirtyDueDate: null,
+ dirtyStartDate: null,
+ isUpdating: false,
+ showDueDateInput: false,
+ showStartDateInput: false,
+ };
+ },
+ computed: {
+ datesUnchanged() {
+ const dirtyDueDate = this.dirtyDueDate || nullObjectDate;
+ const dirtyStartDate = this.dirtyStartDate || nullObjectDate;
+ const dueDate = this.dueDate ? newDateAsLocaleTime(this.dueDate) : nullObjectDate;
+ const startDate = this.startDate ? newDateAsLocaleTime(this.startDate) : nullObjectDate;
+ return (
+ dirtyDueDate.getTime() === dueDate.getTime() &&
+ dirtyStartDate.getTime() === startDate.getTime()
+ );
+ },
+ isDatepickerDisabled() {
+ return !this.canUpdate || this.isUpdating;
+ },
+ isReadonlyWithOnlyDueDate() {
+ return !this.canUpdate && this.dueDate && !this.startDate;
+ },
+ isReadonlyWithOnlyStartDate() {
+ return !this.canUpdate && !this.dueDate && this.startDate;
+ },
+ isReadonlyWithNoDates() {
+ return !this.canUpdate && !this.dueDate && !this.startDate;
+ },
+ labelClass() {
+ return this.isReadonlyWithNoDates ? 'gl-align-self-center gl-pb-0!' : 'gl-mt-3 gl-pb-0!';
+ },
+ showDueDateButton() {
+ return this.canUpdate && !this.showDueDateInput;
+ },
+ showStartDateButton() {
+ return this.canUpdate && !this.showStartDateInput;
+ },
+ tracking() {
+ return {
+ category: TRACKING_CATEGORY_SHOW,
+ label: 'item_dates',
+ property: `type_${this.workItemType}`,
+ };
+ },
+ },
+ watch: {
+ dueDate: {
+ handler(newDueDate) {
+ this.dirtyDueDate = newDateAsLocaleTime(newDueDate);
+ this.showDueDateInput = Boolean(newDueDate);
+ },
+ immediate: true,
+ },
+ startDate: {
+ handler(newStartDate) {
+ this.dirtyStartDate = newDateAsLocaleTime(newStartDate);
+ this.showStartDateInput = Boolean(newStartDate);
+ },
+ immediate: true,
+ },
+ },
+ methods: {
+ clearDueDatePicker() {
+ this.dirtyDueDate = null;
+ this.showDueDateInput = false;
+ this.updateDates();
+ },
+ clearStartDatePicker() {
+ this.dirtyStartDate = null;
+ this.showStartDateInput = false;
+ this.updateDates();
+ },
+ async clickShowDueDate() {
+ this.showDueDateInput = true;
+ await this.$nextTick();
+ this.$refs.dueDatePicker.calendar.show();
+ },
+ async clickShowStartDate() {
+ this.showStartDateInput = true;
+ await this.$nextTick();
+ this.$refs.startDatePicker.calendar.show();
+ },
+ handleStartDateInput() {
+ if (this.dirtyDueDate && this.dirtyStartDate > this.dirtyDueDate) {
+ this.dirtyDueDate = this.dirtyStartDate;
+ this.clickShowDueDate();
+ return;
+ }
+
+ this.updateDates();
+ },
+ updateDates() {
+ if (!this.canUpdate || this.datesUnchanged) {
+ return;
+ }
+
+ this.track('updated_dates');
+
+ this.isUpdating = true;
+
+ this.$apollo
+ .mutate({
+ mutation: updateWorkItemMutation,
+ variables: {
+ input: {
+ id: this.workItemId,
+ startAndDueDateWidget: {
+ dueDate: getDateWithUTC(this.dirtyDueDate),
+ startDate: getDateWithUTC(this.dirtyStartDate),
+ },
+ },
+ },
+ })
+ .then(({ data }) => {
+ if (data.workItemUpdate.errors.length) {
+ throw new Error(data.workItemUpdate.errors.join('; '));
+ }
+ })
+ .catch((error) => {
+ const message = sprintfWorkItem(I18N_WORK_ITEM_ERROR_UPDATING, this.workItemType);
+ this.$emit('error', message);
+ Sentry.captureException(error);
+ })
+ .finally(() => {
+ this.isUpdating = false;
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-form-group
+ class="work-item-due-date"
+ :label="$options.i18n.dates"
+ :label-class="labelClass"
+ label-cols="3"
+ label-cols-lg="2"
+ >
+ <span v-if="isReadonlyWithNoDates" class="gl-text-gray-400 gl-ml-4">
+ {{ $options.i18n.none }}
+ </span>
+ <div v-else class="gl-display-flex gl-flex-wrap gl-gap-5">
+ <gl-form-group
+ class="gl-display-flex gl-align-items-center gl-m-0"
+ :class="{ 'gl-ml-n3': isReadonlyWithOnlyDueDate }"
+ :label="$options.i18n.startDate"
+ :label-for="$options.startDateInputId"
+ :label-sr-only="!showStartDateInput"
+ label-class="gl-flex-shrink-0 gl-text-secondary gl-font-weight-normal! gl-pb-0! gl-ml-4 gl-mr-3"
+ >
+ <gl-datepicker
+ v-if="showStartDateInput"
+ ref="startDatePicker"
+ v-model="dirtyStartDate"
+ container="body"
+ :disabled="isDatepickerDisabled"
+ :input-id="$options.startDateInputId"
+ show-clear-button
+ :target="null"
+ @clear="clearStartDatePicker"
+ @close="handleStartDateInput"
+ />
+ <gl-button v-if="showStartDateButton" category="tertiary" @click="clickShowStartDate">
+ {{ $options.i18n.addStartDate }}
+ </gl-button>
+ </gl-form-group>
+ <gl-form-group
+ v-if="!isReadonlyWithOnlyStartDate"
+ class="gl-display-flex gl-align-items-center gl-m-0"
+ :class="{ 'gl-ml-n3': isReadonlyWithOnlyDueDate }"
+ :label="$options.i18n.dueDate"
+ :label-for="$options.dueDateInputId"
+ :label-sr-only="!showDueDateInput"
+ label-class="gl-flex-shrink-0 gl-text-secondary gl-font-weight-normal! gl-pb-0! gl-ml-4 gl-mr-3"
+ >
+ <gl-datepicker
+ v-if="showDueDateInput"
+ ref="dueDatePicker"
+ v-model="dirtyDueDate"
+ container="body"
+ :disabled="isDatepickerDisabled"
+ :input-id="$options.dueDateInputId"
+ :min-date="dirtyStartDate"
+ show-clear-button
+ :target="null"
+ @clear="clearDueDatePicker"
+ @close="updateDates"
+ />
+ <gl-button v-if="showDueDateButton" category="tertiary" @click="clickShowDueDate">
+ {{ $options.i18n.addDueDate }}
+ </gl-button>
+ </gl-form-group>
+ </div>
+ </gl-form-group>
+</template>
diff --git a/app/assets/javascripts/work_items/components/work_item_information.vue b/app/assets/javascripts/work_items/components/work_item_information.vue
index 2ff7ba169ea..ce75cc98a75 100644
--- a/app/assets/javascripts/work_items/components/work_item_information.vue
+++ b/app/assets/javascripts/work_items/components/work_item_information.vue
@@ -5,16 +5,14 @@ import { helpPagePath } from '~/helpers/help_page_helper';
export default {
i18n: {
- learnTasksButtonText: s__('WorkItem|Learn about tasks'),
- workItemsText: s__('WorkItem|work items'),
+ learnTasksLinkText: s__('WorkItem|Learn about tasks.'),
tasksInformationTitle: s__('WorkItem|Introducing tasks'),
tasksInformationBody: s__(
- 'WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon.',
+ 'WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}',
),
},
helpPageLinks: {
tasksDocLinkPath: helpPagePath('user/tasks'),
- workItemsLinkPath: helpPagePath(`development/work_items`),
},
components: {
GlAlert,
@@ -38,16 +36,14 @@ export default {
v-if="showInfoBanner"
variant="tip"
:title="$options.i18n.tasksInformationTitle"
- :primary-button-link="$options.helpPageLinks.tasksDocLinkPath"
- :primary-button-text="$options.i18n.learnTasksButtonText"
data-testid="work-item-information"
class="gl-mt-3"
@dismiss="$emit('work-item-banner-dismissed')"
>
<gl-sprintf :message="$options.i18n.tasksInformationBody">
- <template #workItemsLink>
- <gl-link :href="$options.helpPageLinks.workItemsLinkPath">{{
- $options.i18n.workItemsText
+ <template #learnMoreLink>
+ <gl-link :href="$options.helpPageLinks.tasksDocLinkPath">{{
+ $options.i18n.learnTasksLinkText
}}</gl-link>
</template>
></gl-sprintf
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 e73488bbd70..b8b5198be57 100644
--- a/app/assets/javascripts/work_items/components/work_item_labels.vue
+++ b/app/assets/javascripts/work_items/components/work_item_labels.vue
@@ -31,7 +31,6 @@ export default {
LabelItem,
},
mixins: [Tracking.mixin()],
- inject: ['fullPath'],
props: {
workItemId: {
type: String,
@@ -41,6 +40,10 @@ export default {
type: Boolean,
required: true,
},
+ fullPath: {
+ type: String,
+ required: true,
+ },
},
data() {
return {
@@ -189,7 +192,7 @@ export default {
</script>
<template>
- <div class="form-row gl-mb-5 work-item-labels gl-relative">
+ <div class="form-row gl-mb-5 work-item-labels gl-relative gl-flex-nowrap">
<span
class="gl-font-weight-bold gl-mt-2 col-lg-2 col-3 gl-pt-2 min-w-fit-content gl-overflow-wrap-break"
data-testid="labels-title"
@@ -216,7 +219,7 @@ export default {
class="add-labels gl-min-w-fit-content gl-display-flex gl-align-items-center gl-text-gray-400 gl-pr-4 gl-top-2"
data-testid="empty-state"
>
- <span v-if="canUpdate" class="gl-ml-2">{{ __('Select labels') }}</span>
+ <span v-if="canUpdate" class="gl-ml-2">{{ __('Add labels') }}</span>
<span v-else class="gl-ml-2">{{ __('None') }}</span>
</div>
</template>
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 86f03583ea3..8f31b07b6a3 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
@@ -1,6 +1,6 @@
import Vue from 'vue';
import { GlToast } from '@gitlab/ui';
-import { createApolloProvider } from '../../graphql/provider';
+import { apolloProvider } from '~/graphql_shared/issuable_client';
import WorkItemLinks from './work_item_links.vue';
Vue.use(GlToast);
@@ -16,18 +16,19 @@ export default function initWorkItemLinks() {
return;
}
- const { projectPath, wiHasIssueWeightsFeature } = workItemLinksRoot.dataset;
+ const { projectPath, wiHasIssueWeightsFeature, iid } = workItemLinksRoot.dataset;
// eslint-disable-next-line no-new
new Vue({
el: workItemLinksRoot,
name: 'WorkItemLinksRoot',
- apolloProvider: createApolloProvider(),
+ apolloProvider,
components: {
- workItemLinks: WorkItemLinks,
+ WorkItemLinks,
},
provide: {
projectPath,
+ iid,
fullPath: projectPath,
hasIssueWeightsFeature: wiHasIssueWeightsFeature,
},
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
new file mode 100644
index 00000000000..34874908f9b
--- /dev/null
+++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_link_child.vue
@@ -0,0 +1,109 @@
+<script>
+import { GlButton, GlIcon, GlTooltipDirective } from '@gitlab/ui';
+
+import { __ } from '~/locale';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import RichTimestampTooltip from '~/vue_shared/components/rich_timestamp_tooltip.vue';
+
+import { STATE_OPEN } from '../../constants';
+import WorkItemLinksMenu from './work_item_links_menu.vue';
+
+export default {
+ components: {
+ GlButton,
+ GlIcon,
+ RichTimestampTooltip,
+ WorkItemLinksMenu,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ projectPath: {
+ type: String,
+ required: true,
+ },
+ canUpdate: {
+ type: Boolean,
+ required: true,
+ },
+ issuableGid: {
+ type: String,
+ required: true,
+ },
+ childItem: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ isItemOpen() {
+ return this.childItem.state === STATE_OPEN;
+ },
+ iconClass() {
+ return this.isItemOpen ? 'gl-text-green-500' : 'gl-text-blue-500';
+ },
+ iconName() {
+ return this.isItemOpen ? 'issue-open-m' : 'issue-close';
+ },
+ stateTimestamp() {
+ return this.isItemOpen ? this.childItem.createdAt : this.childItem.closedAt;
+ },
+ stateTimestampTypeText() {
+ return this.isItemOpen ? __('Created') : __('Closed');
+ },
+ childPath() {
+ return `/${this.projectPath}/-/work_items/${getIdFromGraphQLId(this.childItem.id)}`;
+ },
+ },
+};
+</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
+ 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('remove', childItem.id)"
+ />
+ </div>
+ </div>
+</template>
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 534ebabee08..840fd910272 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
@@ -5,22 +5,17 @@ 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 issueConfidentialQuery from '~/sidebar/queries/issue_confidential.query.graphql';
import { isMetaKey } from '~/lib/utils/common_utils';
import { setUrlParams, updateHistory } from '~/lib/utils/url_utility';
-import SidebarEventHub from '~/sidebar/event_hub';
-import {
- STATE_OPEN,
- WIDGET_ICONS,
- WORK_ITEM_STATUS_TEXT,
- WIDGET_TYPE_HIERARCHY,
-} from '../../constants';
+import { WIDGET_ICONS, WORK_ITEM_STATUS_TEXT, WIDGET_TYPE_HIERARCHY } from '../../constants';
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 WorkItemDetailModal from '../work_item_detail_modal.vue';
+import WorkItemLinkChild from './work_item_link_child.vue';
import WorkItemLinksForm from './work_item_links_form.vue';
-import WorkItemLinksMenu from './work_item_links_menu.vue';
export default {
components: {
@@ -28,14 +23,14 @@ export default {
GlIcon,
GlAlert,
GlLoadingIcon,
+ WorkItemLinkChild,
WorkItemLinksForm,
- WorkItemLinksMenu,
WorkItemDetailModal,
},
directives: {
GlTooltip: GlTooltipDirective,
},
- inject: ['projectPath'],
+ inject: ['projectPath', 'iid'],
props: {
workItemId: {
type: String,
@@ -63,6 +58,18 @@ export default {
this.error = e.message || this.$options.i18n.fetchError;
},
},
+ parentIssue: {
+ query: issueConfidentialQuery,
+ variables() {
+ return {
+ fullPath: this.projectPath,
+ iid: String(this.iid),
+ };
+ },
+ update(data) {
+ return data.workspace?.issuable;
+ },
+ },
},
data() {
return {
@@ -72,9 +79,13 @@ export default {
activeToast: null,
prefetchedWorkItem: null,
error: undefined,
+ parentIssue: null,
};
},
computed: {
+ confidential() {
+ return this.parentIssue?.confidential || this.workItem?.confidential || false;
+ },
children() {
return (
this.workItem?.widgets.find((widget) => widget.type === WIDGET_TYPE_HIERARCHY)?.children
@@ -84,9 +95,6 @@ export default {
canUpdate() {
return this.workItem?.userPermissions.updateWorkItem || false;
},
- confidential() {
- return this.workItem?.confidential || false;
- },
// Only used for children for now but should be extended later to support parents and siblings
isChildrenEmpty() {
return this.children?.length === 0;
@@ -95,9 +103,7 @@ export default {
return this.isOpen ? 'chevron-lg-up' : 'chevron-lg-down';
},
toggleLabel() {
- return this.isOpen
- ? s__('WorkItem|Collapse child items')
- : s__('WorkItem|Expand child items');
+ return this.isOpen ? s__('WorkItem|Collapse tasks') : s__('WorkItem|Expand tasks');
},
issuableGid() {
return this.issuableId ? convertToGraphQLId(TYPE_WORK_ITEM, this.issuableId) : null;
@@ -112,22 +118,7 @@ export default {
return this.isLoading && this.children.length === 0 ? '...' : this.children.length;
},
},
- mounted() {
- SidebarEventHub.$on('confidentialityUpdated', this.refetchWorkItems);
- },
- destroyed() {
- SidebarEventHub.$off('confidentialityUpdated', this.refetchWorkItems);
- },
methods: {
- refetchWorkItems() {
- this.$apollo.queries.workItem.refetch();
- },
- iconClass(state) {
- return state === STATE_OPEN ? 'gl-text-green-500' : 'gl-text-blue-500';
- },
- iconName(state) {
- return state === STATE_OPEN ? 'issue-open-m' : 'issue-close';
- },
toggle() {
this.isOpen = !this.isOpen;
},
@@ -169,9 +160,6 @@ export default {
replace: true,
});
},
- childPath(childItemId) {
- return `/${this.projectPath}/-/work_items/${getIdFromGraphQLId(childItemId)}`;
- },
toggleChildFromCache(workItem, childId, store) {
const sourceData = store.readQuery({
query: getWorkItemLinksQuery,
@@ -242,14 +230,12 @@ export default {
},
},
i18n: {
- title: s__('WorkItem|Child items'),
- fetchError: s__(
- 'WorkItem|Something went wrong when fetching the items list. Please refresh this page.',
- ),
+ title: s__('WorkItem|Tasks'),
+ fetchError: s__('WorkItem|Something went wrong when fetching tasks. Please refresh this page.'),
emptyStateMessage: s__(
- 'WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!',
+ 'WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts.',
),
- addChildButtonLabel: s__('WorkItem|Add a task'),
+ addChildButtonLabel: s__('WorkItem|Add'),
},
WIDGET_TYPE_TASK_ICON: WIDGET_ICONS.TASK,
WORK_ITEM_STATUS_TEXT,
@@ -257,7 +243,10 @@ export default {
</script>
<template>
- <div class="gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100 gl-bg-gray-10">
+ <div
+ class="gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100 gl-bg-gray-10 gl-mt-5"
+ data-testid="work-item-links"
+ >
<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 }"
@@ -319,48 +308,18 @@ export default {
@cancel="hideAddForm"
@addWorkItemChild="addChild"
/>
- <div
+ <work-item-link-child
v-for="child in children"
:key="child.id"
- 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">
- <gl-icon
- :name="iconName(child.state)"
- class="gl-mr-3"
- :class="iconClass(child.state)"
- />
- <gl-icon
- v-if="child.confidential"
- v-gl-tooltip.top
- name="eye-slash"
- class="gl-mr-2 gl-text-orange-500"
- data-testid="confidential-icon"
- :title="__('Confidential')"
- />
- <gl-button
- :href="childPath(child.id)"
- category="tertiary"
- variant="link"
- class="gl-text-truncate gl-max-w-80 gl-text-black-normal!"
- @click="openChild(child.id, $event)"
- @mouseover="prefetchWorkItem(child.id)"
- @mouseout="clearPrefetching"
- >
- {{ child.title }}
- </gl-button>
- </div>
- <div class="gl-ml-0 gl-sm-ml-auto! gl-display-inline-flex gl-align-items-center">
- <work-item-links-menu
- v-if="canUpdate"
- :work-item-id="child.id"
- :parent-work-item-id="issuableGid"
- data-testid="links-menu"
- @removeChild="removeChild(child.id)"
- />
- </div>
- </div>
+ :project-path="projectPath"
+ :can-update="canUpdate"
+ :issuable-gid="issuableGid"
+ :child-item="child"
+ @click="openChild"
+ @mouseover="prefetchWorkItem"
+ @mouseout="clearPrefetching"
+ @remove="removeChild"
+ />
<work-item-detail-modal
ref="modal"
:work-item-id="activeChildId"
diff --git a/app/assets/javascripts/work_items/components/work_item_state.vue b/app/assets/javascripts/work_items/components/work_item_state.vue
index 080d4025cc3..3880ae25c8c 100644
--- a/app/assets/javascripts/work_items/components/work_item_state.vue
+++ b/app/assets/javascripts/work_items/components/work_item_state.vue
@@ -2,7 +2,8 @@
import * as Sentry from '@sentry/browser';
import Tracking from '~/tracking';
import {
- i18n,
+ sprintfWorkItem,
+ I18N_WORK_ITEM_ERROR_UPDATING,
STATE_OPEN,
STATE_CLOSED,
STATE_EVENT_CLOSE,
@@ -93,7 +94,9 @@ export default {
throw new Error(errors[0]);
}
} catch (error) {
- this.$emit('error', i18n.updateError);
+ const msg = sprintfWorkItem(I18N_WORK_ITEM_ERROR_UPDATING, this.workItemType);
+
+ this.$emit('error', msg);
Sentry.captureException(error);
}
diff --git a/app/assets/javascripts/work_items/components/work_item_title.vue b/app/assets/javascripts/work_items/components/work_item_title.vue
index cd5cc3894f6..c52a6854fad 100644
--- a/app/assets/javascripts/work_items/components/work_item_title.vue
+++ b/app/assets/javascripts/work_items/components/work_item_title.vue
@@ -1,7 +1,11 @@
<script>
import * as Sentry from '@sentry/browser';
import Tracking from '~/tracking';
-import { i18n, TRACKING_CATEGORY_SHOW } from '../constants';
+import {
+ sprintfWorkItem,
+ I18N_WORK_ITEM_ERROR_UPDATING,
+ TRACKING_CATEGORY_SHOW,
+} from '../constants';
import { getUpdateWorkItemMutation } from './update_work_item';
import ItemTitle from './item_title.vue';
@@ -78,7 +82,8 @@ export default {
throw new Error(errors[0]);
}
} catch (error) {
- this.$emit('error', i18n.updateError);
+ const msg = sprintfWorkItem(I18N_WORK_ITEM_ERROR_UPDATING, this.workItemType);
+ this.$emit('error', msg);
Sentry.captureException(error);
}
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 fd914fa350b..31e75663055 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
@@ -1,11 +1,14 @@
<script>
-import { GlIcon } from '@gitlab/ui';
+import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { WORK_ITEMS_TYPE_MAP } from '../constants';
export default {
components: {
GlIcon,
},
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
props: {
workItemType: {
type: String,
@@ -22,6 +25,11 @@ export default {
required: false,
default: '',
},
+ showTooltipOnHover: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
computed: {
iconName() {
@@ -32,13 +40,21 @@ export default {
workItemTypeName() {
return WORK_ITEMS_TYPE_MAP[this.workItemType]?.name;
},
+ workItemTooltipTitle() {
+ return this.showTooltipOnHover ? this.workItemTypeName : '';
+ },
},
};
</script>
<template>
<span>
- <gl-icon :name="iconName" class="gl-mr-2" />
+ <gl-icon
+ v-gl-tooltip.hover="showTooltipOnHover"
+ :name="iconName"
+ :title="workItemTooltipTitle"
+ class="gl-mr-2 gl-text-gray-500"
+ />
<span v-if="workItemTypeName" :class="{ 'gl-sr-only': !showText }">{{ workItemTypeName }}</span>
</span>
</template>
diff --git a/app/assets/javascripts/work_items/components/work_item_weight.vue b/app/assets/javascripts/work_items/components/work_item_weight.vue
deleted file mode 100644
index b0ad7c97bb1..00000000000
--- a/app/assets/javascripts/work_items/components/work_item_weight.vue
+++ /dev/null
@@ -1,162 +0,0 @@
-<script>
-import { GlForm, GlFormGroup, GlFormInput } from '@gitlab/ui';
-import * as Sentry from '@sentry/browser';
-import { __ } from '~/locale';
-import Tracking from '~/tracking';
-import { i18n, TRACKING_CATEGORY_SHOW } from '../constants';
-import updateWorkItemMutation from '../graphql/update_work_item.mutation.graphql';
-
-/* eslint-disable @gitlab/require-i18n-strings */
-const allowedKeys = [
- 'Alt',
- 'ArrowDown',
- 'ArrowLeft',
- 'ArrowRight',
- 'ArrowUp',
- 'Backspace',
- 'Control',
- 'Delete',
- 'End',
- 'Enter',
- 'Home',
- 'Meta',
- 'PageDown',
- 'PageUp',
- 'Tab',
- '0',
- '1',
- '2',
- '3',
- '4',
- '5',
- '6',
- '7',
- '8',
- '9',
-];
-/* eslint-enable @gitlab/require-i18n-strings */
-
-export default {
- inputId: 'weight-widget-input',
- components: {
- GlForm,
- GlFormGroup,
- GlFormInput,
- },
- mixins: [Tracking.mixin()],
- inject: ['hasIssueWeightsFeature'],
- props: {
- canUpdate: {
- type: Boolean,
- required: false,
- default: false,
- },
- weight: {
- type: Number,
- required: false,
- default: undefined,
- },
- workItemId: {
- type: String,
- required: true,
- },
- workItemType: {
- type: String,
- required: true,
- },
- },
- data() {
- return {
- isEditing: false,
- };
- },
- computed: {
- placeholder() {
- return this.canUpdate && this.isEditing ? __('Enter a number') : __('None');
- },
- tracking() {
- return {
- category: TRACKING_CATEGORY_SHOW,
- label: 'item_weight',
- property: `type_${this.workItemType}`,
- };
- },
- type() {
- return this.canUpdate && this.isEditing ? 'number' : 'text';
- },
- },
- methods: {
- blurInput() {
- this.$refs.input.$el.blur();
- },
- handleFocus() {
- this.isEditing = true;
- },
- handleKeydown(event) {
- if (!allowedKeys.includes(event.key)) {
- event.preventDefault();
- }
- },
- updateWeight(event) {
- if (!this.canUpdate) return;
- this.isEditing = false;
-
- const weight = Number(event.target.value);
- if (this.weight === weight) {
- return;
- }
-
- this.track('updated_weight');
- this.$apollo
- .mutate({
- mutation: updateWorkItemMutation,
- variables: {
- input: {
- id: this.workItemId,
- weightWidget: {
- weight: event.target.value === '' ? null : weight,
- },
- },
- },
- })
- .then(({ data }) => {
- if (data.workItemUpdate.errors.length) {
- throw new Error(data.workItemUpdate.errors.join('\n'));
- }
- })
- .catch((error) => {
- this.$emit('error', i18n.updateError);
- Sentry.captureException(error);
- });
- },
- },
-};
-</script>
-
-<template>
- <gl-form v-if="hasIssueWeightsFeature" @submit.prevent="blurInput">
- <gl-form-group
- class="gl-align-items-center"
- :label="__('Weight')"
- :label-for="$options.inputId"
- label-class="gl-pb-0! gl-overflow-wrap-break"
- label-cols="3"
- label-cols-lg="2"
- >
- <gl-form-input
- :id="$options.inputId"
- ref="input"
- min="0"
- :placeholder="placeholder"
- :readonly="!canUpdate"
- size="sm"
- :type="type"
- :value="weight"
- @blur="updateWeight"
- @focus="handleFocus"
- @keydown="handleKeydown"
- @keydown.exact.esc.stop="blurInput"
- />
- </gl-form-group>
- </gl-form>
-</template>
diff --git a/app/assets/javascripts/work_items/constants.js b/app/assets/javascripts/work_items/constants.js
index a2aea3cd327..78219e62d01 100644
--- a/app/assets/javascripts/work_items/constants.js
+++ b/app/assets/javascripts/work_items/constants.js
@@ -1,4 +1,5 @@
-import { s__ } from '~/locale';
+import { s__, sprintf } from '~/locale';
+import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
export const STATE_OPEN = 'OPEN';
export const STATE_CLOSED = 'CLOSED';
@@ -13,6 +14,7 @@ export const TASK_TYPE_NAME = 'Task';
export const WIDGET_TYPE_ASSIGNEES = 'ASSIGNEES';
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_HIERARCHY = 'HIERARCHY';
export const WORK_ITEM_VIEWED_STORAGE_KEY = 'gl-show-work-item-banner';
@@ -31,6 +33,30 @@ export const i18n = {
),
};
+export const I18N_WORK_ITEM_ERROR_CREATING = s__(
+ 'WorkItem|Something went wrong when creating %{workItemType}. Please try again.',
+);
+export const I18N_WORK_ITEM_ERROR_UPDATING = s__(
+ 'WorkItem|Something went wrong while updating the %{workItemType}. Please try again.',
+);
+export const I18N_WORK_ITEM_ERROR_DELETING = s__(
+ 'WorkItem|Something went wrong when deleting the %{workItemType}. Please try again.',
+);
+export const I18N_WORK_ITEM_DELETE = s__('WorkItem|Delete %{workItemType}');
+export const I18N_WORK_ITEM_ARE_YOU_SURE_DELETE = s__(
+ 'WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed.',
+);
+export const I18N_WORK_ITEM_DELETED = s__('WorkItem|%{workItemType} deleted');
+
+export const sprintfWorkItem = (msg, workItemTypeArg) => {
+ const workItemType = workItemTypeArg || s__('WorkItem|Work item');
+ return capitalizeFirstCharacter(
+ sprintf(msg, {
+ workItemType: workItemType.toLocaleLowerCase(),
+ }),
+ );
+};
+
export const WIDGET_ICONS = {
TASK: 'issue-type-task',
};
@@ -62,3 +88,5 @@ export const WORK_ITEMS_TYPE_MAP = {
name: s__('WorkItem|Requirements'),
},
};
+
+export const DEFAULT_PAGE_SIZE_ASSIGNEES = 10;
diff --git a/app/assets/javascripts/work_items/graphql/create_work_item.mutation.graphql b/app/assets/javascripts/work_items/graphql/create_work_item.mutation.graphql
index 4cc23fa0071..1228c876a55 100644
--- a/app/assets/javascripts/work_items/graphql/create_work_item.mutation.graphql
+++ b/app/assets/javascripts/work_items/graphql/create_work_item.mutation.graphql
@@ -1,4 +1,4 @@
-#import "ee_else_ce/work_items/graphql/work_item.fragment.graphql"
+#import "./work_item.fragment.graphql"
mutation createWorkItem($input: WorkItemCreateInput!) {
workItemCreate(input: $input) {
diff --git a/app/assets/javascripts/work_items/graphql/create_work_item_from_task.mutation.graphql b/app/assets/javascripts/work_items/graphql/create_work_item_from_task.mutation.graphql
index 1f98cd4fa2b..ccfe62cc585 100644
--- a/app/assets/javascripts/work_items/graphql/create_work_item_from_task.mutation.graphql
+++ b/app/assets/javascripts/work_items/graphql/create_work_item_from_task.mutation.graphql
@@ -1,4 +1,4 @@
-#import "ee_else_ce/work_items/graphql/work_item.fragment.graphql"
+#import "./work_item.fragment.graphql"
mutation workItemCreateFromTask($input: WorkItemCreateFromTaskInput!) {
workItemCreateFromTask(input: $input) {
diff --git a/app/assets/javascripts/work_items/graphql/local_update_work_item.mutation.graphql b/app/assets/javascripts/work_items/graphql/local_update_work_item.mutation.graphql
index 790b8e60b6a..43c92cf89ec 100644
--- a/app/assets/javascripts/work_items/graphql/local_update_work_item.mutation.graphql
+++ b/app/assets/javascripts/work_items/graphql/local_update_work_item.mutation.graphql
@@ -1,4 +1,4 @@
-#import "ee_else_ce/work_items/graphql/work_item.fragment.graphql"
+#import "./work_item.fragment.graphql"
mutation localUpdateWorkItem($input: LocalUpdateWorkItemInput) {
localUpdateWorkItem(input: $input) @client {
diff --git a/app/assets/javascripts/work_items/graphql/provider.js b/app/assets/javascripts/work_items/graphql/provider.js
deleted file mode 100644
index b70c06fddea..00000000000
--- a/app/assets/javascripts/work_items/graphql/provider.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import produce from 'immer';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import createDefaultClient from '~/lib/graphql';
-import { WIDGET_TYPE_LABELS } from '../constants';
-import typeDefs from './typedefs.graphql';
-import workItemQuery from './work_item.query.graphql';
-
-export const temporaryConfig = {
- typeDefs,
- cacheConfig: {
- possibleTypes: {
- LocalWorkItemWidget: ['LocalWorkItemLabels'],
- },
- typePolicies: {
- WorkItem: {
- fields: {
- mockWidgets: {
- read(widgets) {
- return (
- widgets || [
- {
- __typename: 'LocalWorkItemLabels',
- type: WIDGET_TYPE_LABELS,
- allowScopedLabels: true,
- nodes: [],
- },
- ]
- );
- },
- },
- widgets: {
- merge(_, incoming) {
- return incoming;
- },
- },
- },
- },
- },
- },
-};
-
-export const resolvers = {
- Mutation: {
- localUpdateWorkItem(_, { input }, { cache }) {
- const sourceData = cache.readQuery({
- query: workItemQuery,
- variables: { id: input.id },
- });
-
- const data = produce(sourceData, (draftData) => {
- if (input.labels) {
- const labelsWidget = draftData.workItem.mockWidgets.find(
- (widget) => widget.type === WIDGET_TYPE_LABELS,
- );
- labelsWidget.nodes = [...input.labels];
- }
- });
-
- cache.writeQuery({
- query: workItemQuery,
- variables: { id: input.id },
- data,
- });
- },
- },
-};
-
-export function createApolloProvider() {
- Vue.use(VueApollo);
-
- const defaultClient = createDefaultClient(resolvers, temporaryConfig);
-
- return new VueApollo({
- defaultClient,
- });
-}
diff --git a/app/assets/javascripts/work_items/graphql/update_work_item.mutation.graphql b/app/assets/javascripts/work_items/graphql/update_work_item.mutation.graphql
index 0a887fcfc00..25eb8099251 100644
--- a/app/assets/javascripts/work_items/graphql/update_work_item.mutation.graphql
+++ b/app/assets/javascripts/work_items/graphql/update_work_item.mutation.graphql
@@ -1,4 +1,4 @@
-#import "ee_else_ce/work_items/graphql/work_item.fragment.graphql"
+#import "./work_item.fragment.graphql"
mutation workItemUpdate($input: WorkItemUpdateInput!) {
workItemUpdate(input: $input) {
diff --git a/app/assets/javascripts/work_items/graphql/update_work_item_task.mutation.graphql b/app/assets/javascripts/work_items/graphql/update_work_item_task.mutation.graphql
index fad5a9fa5bc..ad861a60d15 100644
--- a/app/assets/javascripts/work_items/graphql/update_work_item_task.mutation.graphql
+++ b/app/assets/javascripts/work_items/graphql/update_work_item_task.mutation.graphql
@@ -1,4 +1,4 @@
-#import "ee_else_ce/work_items/graphql/work_item.fragment.graphql"
+#import "./work_item.fragment.graphql"
mutation workItemUpdateTask($input: WorkItemUpdateTaskInput!) {
workItemUpdate: workItemUpdateTask(input: $input) {
diff --git a/app/assets/javascripts/work_items/graphql/update_work_item_widgets.mutation.graphql b/app/assets/javascripts/work_items/graphql/update_work_item_widgets.mutation.graphql
deleted file mode 100644
index 6a94c96b347..00000000000
--- a/app/assets/javascripts/work_items/graphql/update_work_item_widgets.mutation.graphql
+++ /dev/null
@@ -1,10 +0,0 @@
-#import "ee_else_ce/work_items/graphql/work_item.fragment.graphql"
-
-mutation workItemUpdateWidgets($input: WorkItemUpdateWidgetsInput!) {
- workItemUpdateWidgets(input: $input) {
- workItem {
- ...WorkItem
- }
- errors
- }
-}
diff --git a/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql b/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql
index e8ef27ec778..f4c77ed2ec0 100644
--- a/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql
+++ b/app/assets/javascripts/work_items/graphql/work_item.fragment.graphql
@@ -1,4 +1,5 @@
#import "~/graphql_shared/fragments/user.fragment.graphql"
+#import "ee_else_ce/work_items/graphql/work_item_widgets.fragment.graphql"
fragment WorkItem on WorkItem {
id
@@ -6,6 +7,12 @@ fragment WorkItem on WorkItem {
state
description
confidential
+ createdAt
+ closedAt
+ project {
+ id
+ fullPath
+ }
workItemType {
id
name
@@ -16,34 +23,6 @@ fragment WorkItem on WorkItem {
updateWorkItem
}
widgets {
- ... on WorkItemWidgetDescription {
- type
- description
- descriptionHtml
- }
- ... on WorkItemWidgetAssignees {
- type
- allowsMultipleAssignees
- canInviteMembers
- assignees {
- nodes {
- ...User
- }
- }
- }
- ... on WorkItemWidgetHierarchy {
- type
- parent {
- id
- iid
- title
- confidential
- }
- children {
- nodes {
- id
- }
- }
- }
+ ...WorkItemWidgets
}
}
diff --git a/app/assets/javascripts/work_items/graphql/work_item.query.graphql b/app/assets/javascripts/work_items/graphql/work_item.query.graphql
index a9f7b714551..276061af193 100644
--- a/app/assets/javascripts/work_items/graphql/work_item.query.graphql
+++ b/app/assets/javascripts/work_items/graphql/work_item.query.graphql
@@ -1,5 +1,5 @@
#import "~/graphql_shared/fragments/label.fragment.graphql"
-#import "ee_else_ce/work_items/graphql/work_item.fragment.graphql"
+#import "./work_item.fragment.graphql"
query workItem($id: WorkItemID!) {
workItem(id: $id) {
diff --git a/app/assets/javascripts/work_items/graphql/work_item_dates.subscription.graphql b/app/assets/javascripts/work_items/graphql/work_item_dates.subscription.graphql
new file mode 100644
index 00000000000..7e045fdf431
--- /dev/null
+++ b/app/assets/javascripts/work_items/graphql/work_item_dates.subscription.graphql
@@ -0,0 +1,13 @@
+subscription issuableDatesUpdated($issuableId: IssuableID!) {
+ issuableDatesUpdated(issuableId: $issuableId) {
+ ... on WorkItem {
+ id
+ widgets {
+ ... on WorkItemWidgetStartAndDueDate {
+ dueDate
+ startDate
+ }
+ }
+ }
+ }
+}
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 df62ca1c143..7b63d9c7ca3 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
@@ -1,4 +1,4 @@
-query workItemQuery($id: WorkItemID!) {
+query workItemLinksQuery($id: WorkItemID!) {
workItem(id: $id) {
id
workItemType {
@@ -26,6 +26,8 @@ query workItemQuery($id: WorkItemID!) {
}
title
state
+ createdAt
+ closedAt
}
}
}
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
new file mode 100644
index 00000000000..3005069f59a
--- /dev/null
+++ b/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql
@@ -0,0 +1,36 @@
+fragment WorkItemWidgets on WorkItemWidget {
+ ... on WorkItemWidgetDescription {
+ type
+ description
+ descriptionHtml
+ }
+ ... on WorkItemWidgetAssignees {
+ type
+ allowsMultipleAssignees
+ canInviteMembers
+ assignees {
+ nodes {
+ ...User
+ }
+ }
+ }
+ ... on WorkItemWidgetStartAndDueDate {
+ type
+ dueDate
+ startDate
+ }
+ ... on WorkItemWidgetHierarchy {
+ type
+ parent {
+ id
+ iid
+ title
+ confidential
+ }
+ children {
+ nodes {
+ id
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/work_items/index.js b/app/assets/javascripts/work_items/index.js
index 6437df597b4..bb4c7052238 100644
--- a/app/assets/javascripts/work_items/index.js
+++ b/app/assets/javascripts/work_items/index.js
@@ -1,8 +1,8 @@
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
+import { apolloProvider } from '~/graphql_shared/issuable_client';
import App from './components/app.vue';
import { createRouter } from './router';
-import { createApolloProvider } from './graphql/provider';
export const initWorkItemsRoot = () => {
const el = document.querySelector('#js-work-items');
@@ -12,7 +12,7 @@ export const initWorkItemsRoot = () => {
el,
name: 'WorkItemsRoot',
router: createRouter(el.dataset.fullPath),
- apolloProvider: createApolloProvider(),
+ apolloProvider,
provide: {
fullPath,
hasIssueWeightsFeature: parseBoolean(hasIssueWeightsFeature),
diff --git a/app/assets/javascripts/work_items/pages/create_work_item.vue b/app/assets/javascripts/work_items/pages/create_work_item.vue
index 482da5419c6..3b7257591e2 100644
--- a/app/assets/javascripts/work_items/pages/create_work_item.vue
+++ b/app/assets/javascripts/work_items/pages/create_work_item.vue
@@ -1,7 +1,9 @@
<script>
import { GlButton, GlAlert, GlLoadingIcon, GlFormSelect } from '@gitlab/ui';
-import { s__ } from '~/locale';
+import { getPreferredLocales, s__ } from '~/locale';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
+import { sprintfWorkItem, I18N_WORK_ITEM_ERROR_CREATING } from '../constants';
import workItemQuery from '../graphql/work_item.query.graphql';
import createWorkItemMutation from '../graphql/create_work_item.mutation.graphql';
import createWorkItemFromTaskMutation from '../graphql/create_work_item_from_task.mutation.graphql';
@@ -10,7 +12,6 @@ import projectWorkItemTypesQuery from '../graphql/project_work_item_types.query.
import ItemTitle from '../components/item_title.vue';
export default {
- createErrorText: s__('WorkItem|Something went wrong when creating a task. Please try again'),
fetchTypesErrorText: s__(
'WorkItem|Something went wrong when fetching work item types. Please try again',
),
@@ -69,7 +70,7 @@ export default {
update(data) {
return data.workspace?.workItemTypes?.nodes.map((node) => ({
value: node.id,
- text: node.name,
+ text: capitalizeFirstCharacter(node.name.toLocaleLowerCase(getPreferredLocales()[0])),
}));
},
error() {
@@ -78,15 +79,19 @@ export default {
},
},
computed: {
- dropdownButtonText() {
- return this.selectedWorkItemType?.name || s__('WorkItem|Type');
- },
formOptions() {
return [{ value: null, text: s__('WorkItem|Select type') }, ...this.workItemTypes];
},
isButtonDisabled() {
return this.title.trim().length === 0 || !this.selectedWorkItemType;
},
+ createErrorText() {
+ const workItemType = this.workItemTypes.find(
+ (item) => item.value === this.selectedWorkItemType,
+ )?.text;
+
+ return sprintfWorkItem(I18N_WORK_ITEM_ERROR_CREATING, workItemType);
+ },
},
methods: {
async createWorkItem() {
@@ -128,7 +133,7 @@ export default {
} = response;
this.$router.push({ name: 'workItem', params: { id: `${getIdFromGraphQLId(id)}` } });
} catch {
- this.error = this.$options.createErrorText;
+ this.error = this.createErrorText;
}
},
async createWorkItemFromTask() {
@@ -150,7 +155,7 @@ export default {
});
this.$emit('onCreate', data.workItemCreateFromTask.workItem.descriptionHtml);
} catch {
- this.error = this.$options.createErrorText;
+ this.error = this.createErrorText;
}
},
handleTitleInput(title) {
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 e9840889bdb..a2cacd8bd7a 100644
--- a/app/assets/javascripts/work_items/pages/work_item_root.vue
+++ b/app/assets/javascripts/work_items/pages/work_item_root.vue
@@ -3,9 +3,13 @@ import { GlAlert } from '@gitlab/ui';
import { TYPE_WORK_ITEM } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { visitUrl } from '~/lib/utils/url_utility';
-import { s__ } from '~/locale';
import ZenMode from '~/zen_mode';
import WorkItemDetail from '../components/work_item_detail.vue';
+import {
+ sprintfWorkItem,
+ I18N_WORK_ITEM_ERROR_DELETING,
+ I18N_WORK_ITEM_DELETED,
+} from '../constants';
import deleteWorkItemMutation from '../graphql/delete_work_item.mutation.graphql';
export default {
@@ -34,7 +38,7 @@ export default {
this.ZenMode = new ZenMode();
},
methods: {
- deleteWorkItem() {
+ deleteWorkItem(workItemType) {
this.$apollo
.mutate({
mutation: deleteWorkItemMutation,
@@ -53,13 +57,12 @@ export default {
throw new Error(workItemDelete.errors[0]);
}
- this.$toast.show(s__('WorkItem|Work item deleted'));
+ const msg = sprintfWorkItem(I18N_WORK_ITEM_DELETED, workItemType);
+ this.$toast.show(msg);
visitUrl(this.issuesListPath);
})
.catch((e) => {
- this.error =
- e.message ||
- s__('WorkItem|Something went wrong when deleting the work item. Please try again.');
+ this.error = e.message || sprintfWorkItem(I18N_WORK_ITEM_ERROR_DELETING, workItemType);
});
},
},
@@ -69,6 +72,6 @@ export default {
<template>
<div>
<gl-alert v-if="error" variant="danger" @dismiss="error = ''">{{ error }}</gl-alert>
- <work-item-detail :work-item-id="gid" @deleteWorkItem="deleteWorkItem" />
+ <work-item-detail :work-item-id="gid" @deleteWorkItem="deleteWorkItem($event)" />
</div>
</template>
diff --git a/app/assets/stylesheets/_page_specific_files.scss b/app/assets/stylesheets/_page_specific_files.scss
index 004dc22c9b8..9e81e1d4771 100644
--- a/app/assets/stylesheets/_page_specific_files.scss
+++ b/app/assets/stylesheets/_page_specific_files.scss
@@ -4,7 +4,6 @@
@import './pages/commits';
@import './pages/deploy_keys';
@import './pages/detail_page';
-@import './pages/editor';
@import './pages/environment_logs';
@import './pages/events';
@import './pages/groups';
@@ -21,7 +20,6 @@
@import './pages/notifications';
@import './pages/pipelines';
@import './pages/profile';
-@import './pages/profiles/preferences';
@import './pages/projects';
@import './pages/prometheus';
@import './pages/registry';
diff --git a/app/assets/stylesheets/components/upload_dropzone/upload_dropzone.scss b/app/assets/stylesheets/components/upload_dropzone/upload_dropzone.scss
index f6be241d644..324c23022ca 100644
--- a/app/assets/stylesheets/components/upload_dropzone/upload_dropzone.scss
+++ b/app/assets/stylesheets/components/upload_dropzone/upload_dropzone.scss
@@ -7,14 +7,14 @@
@return $string;
}
-@mixin dropzone-background($stroke-color, $stroke-width: 4, $stroke-linecap: 'butt') {
- background-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='8' ry='8' stroke='#{encodecolor($stroke-color)}' stroke-width='#{$stroke-width}' stroke-dasharray='6%2c4' stroke-dashoffset='0' stroke-linecap='#{encodecolor($stroke-linecap)}'/%3e%3c/svg%3e");
+@mixin dropzone-background($stroke-color, $stroke-width: 4) {
+ background-image: url("data:image/svg+xml, %3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='#{$border-radius-default}' ry='#{$border-radius-default}' stroke='#{encodecolor($stroke-color)}' stroke-width='#{$stroke-width}' stroke-dasharray='6%2c4' stroke-dashoffset='0' stroke-linecap='butt' /%3e %3c/svg%3e");
}
.upload-dropzone-border {
border: 0;
- @include dropzone-background($gray-400, 2, 'round');
- border-radius: 8px;
+ @include dropzone-background($gray-400, 2);
+ border-radius: $border-radius-default;
}
.upload-dropzone-card {
diff --git a/app/assets/stylesheets/framework/contextual_sidebar.scss b/app/assets/stylesheets/framework/contextual_sidebar.scss
index ad0036df607..34c7ffa58fe 100644
--- a/app/assets/stylesheets/framework/contextual_sidebar.scss
+++ b/app/assets/stylesheets/framework/contextual_sidebar.scss
@@ -149,7 +149,7 @@
margin: $sidebar-top-item-tb-margin $sidebar-top-item-lr-margin;
&:hover {
- background-color: $indigo-900-alpha-008;
+ background-color: $nav-active-bg;
}
}
@@ -275,7 +275,7 @@
&:not(.fly-out-top-item) {
> a:not(.has-sub-items) {
- background-color: $indigo-900-alpha-008;
+ background-color: $nav-active-bg;
}
}
}
diff --git a/app/assets/stylesheets/framework/diffs.scss b/app/assets/stylesheets/framework/diffs.scss
index 904d041fdc9..8d1fb5eb55f 100644
--- a/app/assets/stylesheets/framework/diffs.scss
+++ b/app/assets/stylesheets/framework/diffs.scss
@@ -34,19 +34,28 @@
@media (min-width: map-get($grid-breakpoints, md)) {
// The `+11` is to ensure the file header border shows when scrolled -
// the bottom of the compare-versions header and the top of the file header
- $mr-file-header-top: calc(#{$header-height} + #{$mr-tabs-height});
+ --initial-top: calc(#{$header-height} + #{$mr-tabs-height});
+ --top: var(--initial-top);
position: -webkit-sticky;
position: sticky;
- top: $mr-file-header-top;
+ top: var(--top);
z-index: 120;
+ &.is-sidebar-moved {
+ --initial-top: calc(#{$header-height} + #{$mr-tabs-height + 28px});
+ }
+
.with-system-header & {
- top: calc(#{$mr-file-header-top} + #{$system-header-height});
+ --top: calc(var(--initial-top) + #{$system-header-height});
}
.with-system-header.with-performance-bar & {
- top: calc(#{$mr-file-header-top} + #{$system-header-height} + #{$performance-bar-height});
+ --top: calc(var(--initial-top) + #{$system-header-height} + #{$performance-bar-height});
+ }
+
+ .with-performance-bar & {
+ top: calc(var(--initial-top) + #{$performance-bar-height});
}
&::before {
@@ -60,10 +69,6 @@
pointer-events: none;
}
- .with-performance-bar & {
- top: calc(#{$mr-file-header-top} + #{$performance-bar-height});
- }
-
&.is-commit {
top: calc(#{$header-height} + #{$commit-stat-summary-height});
@@ -788,11 +793,13 @@ table.code {
}
.diff-comments-more-count,
-.diff-notes-collapse {
+.diff-notes-collapse,
+.diff-codequality-collapse {
@include avatar-counter(50%);
}
-.diff-notes-collapse {
+.diff-notes-collapse,
+.diff-codequality-collapse {
border: 0;
border-radius: 50%;
padding: 0;
@@ -977,7 +984,8 @@ table.code {
position: relative;
}
- .diff-notes-collapse {
+ .diff-notes-collapse,
+ .diff-codequality-collapse {
position: absolute;
left: -12px;
}
@@ -1107,6 +1115,7 @@ table.code {
}
.diff-notes-collapse,
+ .diff-codequality-collapse,
.note,
.discussion-reply-holder {
display: none;
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index b980d7fdaa7..07516275e58 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -39,8 +39,8 @@
.file-title {
position: relative;
- background-color: $gray-light;
- border-bottom: 1px solid $border-color;
+ background-color: var(--gray-10, $gray-10);
+ border-bottom: 1px solid var(--border-color, $border-color);
margin: 0;
text-align: left;
padding: 10px $gl-padding;
@@ -471,11 +471,6 @@ span.idiff {
}
}
-.jupyter-notebook-scrolled {
- overflow-y: auto;
- max-height: 20rem;
-}
-
#js-openapi-viewer {
pre.version,
code {
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 37f92d3cf3d..ed41d10f3b2 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -48,7 +48,7 @@
opacity: 0;
}
- a {
+ a:not(.canary-badge) {
display: flex;
align-items: center;
padding: 2px 8px;
@@ -61,10 +61,6 @@
}
}
- .canary-badge {
- margin-left: -8px;
- }
-
.project-item-select {
right: auto;
left: 0;
@@ -564,15 +560,11 @@
}
.frequent-items-list-item-container > a:hover {
- background-color: $nav-active-bg;
+ background-color: $nav-active-bg !important;
}
}
.top-nav-toggle {
- .dropdown-icon {
- @include gl-mr-3;
- }
-
.dropdown-chevron {
top: 0;
}
@@ -581,7 +573,7 @@
.top-nav-menu-item {
&.active,
&:hover {
- background-color: $nav-active-bg;
+ background-color: $nav-active-bg !important;
}
.gl-icon {
diff --git a/app/assets/stylesheets/framework/highlight.scss b/app/assets/stylesheets/framework/highlight.scss
index ab426f388c6..a63ce66e681 100644
--- a/app/assets/stylesheets/framework/highlight.scss
+++ b/app/assets/stylesheets/framework/highlight.scss
@@ -31,8 +31,7 @@
width: 100%;
padding-left: 10px;
padding-right: 10px;
- white-space: break-spaces;
- word-break: break-word;
+ white-space: pre;
&:empty::before {
content: '\200b';
diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss
index fb05f8575ef..02b76b89482 100644
--- a/app/assets/stylesheets/framework/layout.scss
+++ b/app/assets/stylesheets/framework/layout.scss
@@ -7,9 +7,6 @@ html {
}
body {
- // Improves readability for dyslexic users; supported only in Chrome/Safari so far
- text-decoration-skip: ink;
-
&.navless {
background-color: $white !important;
}
@@ -139,6 +136,13 @@ body {
}
}
+.gl--flex-full {
+ @include gl-display-flex;
+ @include gl-align-items-stretch;
+ @include gl-overflow-hidden;
+}
+
+
.with-performance-bar .layout-page {
margin-top: calc(#{$header-height} + #{$performance-bar-height});
}
diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
index 7522f791b8e..b623f18c4ae 100644
--- a/app/assets/stylesheets/framework/markdown_area.scss
+++ b/app/assets/stylesheets/framework/markdown_area.scss
@@ -104,7 +104,6 @@
li.md-header-toolbar {
margin-left: auto;
visibility: hidden;
- padding-bottom: $gl-padding-8;
&.active {
visibility: visible;
diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
index 92ca8654287..7e0a601223d 100644
--- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss
+++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
@@ -13,9 +13,9 @@
a,
button {
- padding: $gl-padding-8;
- font-size: 14px;
- line-height: 28px;
+ padding: $gl-spacing-scale-5 $gl-spacing-scale-4;
+ font-size: $gl-font-size;
+ line-height: $gl-line-height-16;
color: $gl-text-color-secondary;
border: 0;
white-space: nowrap;
@@ -88,10 +88,6 @@
float: left;
}
- li a {
- padding: 16px 15px 11px;
- }
-
/* Small devices (phones, 768px and lower) */
@include media-breakpoint-down(sm) {
width: 100%;
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index ae0f18753ad..7878e08e549 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -208,7 +208,7 @@
position: relative;
top: -3px;
padding: $gl-padding-4 0;
- background-color: $gray-light;
+ background-color: $body-bg;
&.opened {
color: $green-500;
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 031f5dc45ca..e79fb843967 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -333,7 +333,7 @@
font-size: 13px;
line-height: 1.6em;
overflow-x: auto;
- border-radius: 2px;
+ border-radius: $border-radius-default;
// Multi-line code blocks should scroll horizontally
code {
@@ -427,10 +427,11 @@
padding-inline-start: 28px;
margin-inline-start: 0 !important;
+ > p > input.task-list-item-checkbox,
> input.task-list-item-checkbox {
position: absolute;
- inset-inline-start: 8px;
- top: 5px;
+ inset-inline-start: $gl-padding-8;
+ inset-block-start: 5px;
}
}
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index e9ad930ef2b..bd32a817d5d 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -364,7 +364,7 @@ $well-expand-item: #e8f2f7 !default;
$well-inner-border: #eef0f2 !default;
$well-light-border: #f1f1f1;
$well-light-text-color: #5b6169;
-$nav-active-bg: var(--nav-active-bg, rgba($black, 0.08)) !important;
+$nav-active-bg: rgba($black, 0.08);
/*
* Text
diff --git a/app/assets/stylesheets/page_bundles/boards.scss b/app/assets/stylesheets/page_bundles/boards.scss
index 197073412e8..d45bc865da5 100644
--- a/app/assets/stylesheets/page_bundles/boards.scss
+++ b/app/assets/stylesheets/page_bundles/boards.scss
@@ -5,34 +5,6 @@
pointer-events: none;
}
-.dropdown-projects {
- .dropdown-content {
- max-height: 200px;
- }
-}
-
-.issue-board-dropdown-content {
- margin: 0;
- padding: $gl-padding-4 $gl-padding $gl-padding;
- border-bottom: 0;
- color: var(--gray-500, $gray-500);
-}
-
-[data-page$='epic_boards:index'],
-[data-page$='epic_boards:show'],
-.issue-boards-page {
- .content-wrapper {
- padding-bottom: 0;
- }
-}
-
-[data-page$='epic_boards:index'],
-[data-page$='epic_boards:show'] {
- .filtered-search-wrapper {
- display: none !important;
- }
-}
-
.boards-app {
@include media-breakpoint-up(sm) {
transition: width $gl-transition-duration-medium;
@@ -87,33 +59,7 @@
width: 400px;
}
- .board-title-caret {
- border-radius: $border-radius-default;
- line-height: $gl-spacing-scale-5;
-
- &.btn svg {
- top: 0;
- }
-
- &:hover {
- background-color: var(--gray-50, $gray-50);
- transition: background-color 0.1s linear;
- }
- }
-
- &:not(.is-collapsed) {
- .board-title-caret {
- margin-right: $gl-padding-4;
- }
- }
-
&.is-collapsed {
- width: 50px;
-
- .board-title-caret {
- margin-top: 1px;
- }
-
.board-title-text > span,
.issue-count-badge > span {
height: 16px;
@@ -124,17 +70,11 @@
// rotated element has square dimensions so it won't overlap with its siblings.
margin: calc(50% - 8px) 0;
- transform: rotate(90deg);
transform-origin: center;
}
}
}
-.board-inner {
- font-size: $issue-boards-font-size;
- background: var(--gray-50, $gray-50);
-}
-
// to highlight columns we have animated pulse of box-shadow
// we don't want to actually animate the box-shadow property
// because that causes costly repaints. Instead we can add a
@@ -169,103 +109,45 @@
}
}
-.board-title {
- height: 3rem;
-
- .max-issue-size::before {
- content: '/';
- }
-}
-
-.board-list-component {
- min-height: 0; // firefox fix
-}
-
-.board-list {
- overflow-y: auto;
- overflow-x: hidden;
-}
-
-.board-list-loading {
- margin-top: 10px;
- font-size: (26px / $issue-boards-font-size) * 1em;
-}
-
.board-card {
background: var(--gray-10, $white);
box-shadow: 0 1px 2px rgba(var(--black, $black), 0.1);
- line-height: $gl-padding;
- list-style: none;
- position: relative;
-
- &:not(:last-child) {
- margin-bottom: $gl-padding-8;
- }
-
- &.is-active,
- &.is-active .board-card-assignee:hover a {
- background-color: var(--blue-50, $blue-50);
- }
-
- &.multi-select {
- border-color: var(--blue-200, $blue-200);
- background-color: var(--blue-50, $blue-50);
- }
-
- &.sortable-chosen {
- box-shadow: 0 2px 4px 0 rgba($black, 0.16);
- }
- .gl-label {
- margin-top: 4px;
- margin-right: 4px;
+ &:last-child {
+ @include gl-mb-0;
}
- .confidential-icon,
- .hidden-icon {
- color: var(--orange-500, $orange-500);
- cursor: help;
+ .move-to-position {
+ visibility: hidden;
}
- .issue-blocked-icon {
- color: var(--red-500, $red-500);
+ &:hover .move-to-position {
+ visibility: visible;
}
- @include media-breakpoint-down(md) {
- padding: $gl-padding-8;
+ @include media-breakpoint-down(sm) {
+ .move-to-position {
+ visibility: visible;
+ }
}
}
.board-card-title {
- @include overflow-break-word();
- font-size: 1em;
+ width: 95%;
a {
- color: var(--gray-900, $gray-900);
- }
-
- @include media-breakpoint-down(md) {
- font-size: $label-font-size;
+ @include media-breakpoint-down(md) {
+ font-size: $gl-font-size-sm;
+ }
}
}
.board-card-assignee {
- margin-top: -$gl-padding-4;
- margin-bottom: -$gl-padding-4;
-
.avatar-counter {
- vertical-align: middle;
- line-height: $gl-padding-24;
min-width: $gl-padding-24;
height: $gl-padding-24;
border-radius: $gl-padding-24;
- background-color: var(--gray-400, $gray-400);
font-size: $gl-font-size-xs;
- cursor: help;
- font-weight: $gl-font-weight-bold;
- margin-left: -$gl-padding-4;
- border: 0;
- padding: 0 $gl-padding-4;
@include media-breakpoint-down(md) {
min-width: auto;
@@ -275,12 +157,8 @@
}
}
- img {
- vertical-align: top;
- }
-
.user-avatar-link:not(:only-child) {
- margin-left: -$gl-padding-4;
+ margin-left: -$gl-padding;
&:nth-of-type(1) {
z-index: 2;
@@ -299,89 +177,26 @@
}
@include media-breakpoint-down(md) {
- margin-top: 0;
- margin-bottom: 0;
+ margin-bottom: 0 !important;
}
}
.board-card-number {
- font-size: $gl-font-size-xs;
- color: var(--gray-500, $gray-500);
-
- @include media-breakpoint-up(md) {
- font-size: $label-font-size;
+ @include media-breakpoint-down(md) {
+ font-size: $gl-font-size-sm;
}
}
.board-list-count {
- padding: 10px 0;
- color: var(--gray-500, $gray-500);
font-size: 13px;
}
-.board-new-issue-form {
- z-index: 4;
- margin: 5px;
-}
-
-.right-sidebar.boards-sidebar {
- .gutter-toggle {
- bottom: 15px;
- width: 22px;
- padding-left: $gl-padding-32;
-
- svg {
- position: absolute;
- top: 50%;
- right: 0;
- margin-top: (-11px / 2);
- height: $gl-font-size-12;
- width: $gl-font-size-12;
- }
- }
-
- .issuable-header-text {
- @include overflow-break-word();
- padding-right: 35px;
- }
-}
-
-.right-sidebar.right-sidebar-expanded {
- &.boards-sidebar-slide-enter-active,
- &.boards-sidebar-slide-leave-active {
- transition: width $gl-transition-duration-medium, padding $gl-transition-duration-medium;
- }
-
- &.boards-sidebar-slide-enter,
- &.boards-sidebar-slide-leave-active {
- width: 0;
- padding-left: 0;
- padding-right: 0;
- }
-}
-
.board-card-info {
- color: var(--gray-500, $gray-500);
- white-space: nowrap;
- margin-right: $gl-padding-8;
-
- &:not(.board-card-weight) {
- cursor: help;
- }
-
- &.board-card-weight {
- color: var(--gray-500, $gray-500);
- cursor: pointer;
-
- &:hover {
- color: initial;
- text-decoration: underline;
- }
+ &.board-card-weight:hover {
+ color: initial;
}
.board-card-info-icon {
- color: var(--gray-500, $gray-500);
- margin-right: $gl-padding-4;
vertical-align: text-top;
}
@@ -394,15 +209,6 @@
cursor: help;
}
-.board-labels-toggle-wrapper,
-.board-swimlanes-toggle-wrapper {
- /**
- * Make the wrapper the same height as a button so it aligns properly when the
- * filtered-search-box input element increases in size on Linux smaller breakpoints
- */
- height: $input-height;
-}
-
.issue-boards-content:not(.breadcrumbs) {
isolation: isolate;
}
@@ -422,7 +228,6 @@
.boards-list {
height: calc(100vh - #{$issue-boards-filter-height});
- overflow-x: scroll;
}
.boards-sidebar {
@@ -433,15 +238,7 @@
.boards-sidebar {
.sidebar-collapsed-icon {
- display: none;
- }
-
- .gl-drawer-header {
- align-items: flex-start;
- }
-
- .labels-select-wrapper.is-embedded .labels-select-wrapper.is-embedded {
- width: auto;
+ @include gl-display-none;
}
.show.dropdown .dropdown-menu {
@@ -449,10 +246,6 @@
}
}
-.board-header-collapsed-info-icon:hover {
- color: var(--gray-900, $gray-900);
-}
-
.board-card-skeleton {
height: 110px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
diff --git a/app/assets/stylesheets/page_bundles/editor.scss b/app/assets/stylesheets/page_bundles/editor.scss
new file mode 100644
index 00000000000..b7b698b2128
--- /dev/null
+++ b/app/assets/stylesheets/page_bundles/editor.scss
@@ -0,0 +1,202 @@
+@import 'page_bundles/mixins_and_variables_and_functions';
+
+.file-editor {
+ .nav-links {
+ border-top: 1px solid var(--border-color, $border-color);
+ border-right: 1px solid var(--border-color, $border-color);
+ border-left: 1px solid var(--border-color, $border-color);
+ border-bottom: 0;
+ border-radius: $border-radius-small $border-radius-small 0 0;
+ background: var(--gray-50, $gray-50);
+ }
+
+ #editor,
+ .editor {
+ @include gl-border-0;
+ @include gl-m-0;
+ @include gl-p-0;
+ @include gl-relative;
+ @include gl-w-full;
+ height: 500px;
+
+ .editor-loading-content {
+ @include gl-h-full;
+ @include gl-border-0;
+ }
+ }
+
+ .cancel-btn {
+ color: $red-600;
+
+ &:hover {
+ color: $red-600;
+ }
+ }
+
+ .file-title {
+ @include gl-font-monospace;
+ }
+
+ .editor-ref {
+ background: var(--gray-10, $gray-10);
+ padding-right: $gl-padding;
+ border-right: 1px solid var(--border-color, $border-color);
+ display: block;
+ float: left;
+ margin-right: 10px;
+ max-width: 250px;
+ }
+
+ .new-file-name,
+ .new-file-path {
+ display: inline-block;
+ max-width: 250px;
+ float: left;
+
+ @media(max-width: map-get($grid-breakpoints, lg)-1) {
+ width: 180px;
+ }
+
+ @media (max-width: 1360px) {
+ width: auto;
+ }
+ }
+
+ .file-buttons {
+ flex: 1;
+ }
+
+ .soft-wrap-toggle {
+ font-family: $regular-font;
+ margin-left: $gl-padding-8;
+
+ .soft-wrap {
+ display: inline-flex;
+ }
+
+ .no-wrap {
+ display: none;
+ }
+
+ &.soft-wrap-active {
+ .soft-wrap {
+ display: none;
+ }
+
+ .no-wrap {
+ display: inline-flex;
+ }
+ }
+ }
+}
+
+
+@include media-breakpoint-down(md) {
+ .file-editor {
+ .file-title {
+ display: block;
+ }
+
+ .new-file-name,
+ .new-file-path {
+ max-width: none;
+ width: 100%;
+ margin-top: $gl-padding-8;
+ }
+
+ .file-buttons {
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+
+ .md-header-toolbar {
+ margin: $gl-padding 0;
+ }
+
+ .soft-wrap-toggle {
+ width: 100%;
+ margin-left: 0;
+ }
+
+ @media(max-width: map-get($grid-breakpoints, md)-1) {
+ clear: both;
+ }
+ }
+ }
+}
+
+.blob-new-page-title,
+.blob-edit-page-title {
+ margin: 19px 0 21px;
+ vertical-align: top;
+ display: inline-block;
+
+ @media(max-width: map-get($grid-breakpoints, lg)-1) {
+ display: block;
+ margin: 19px 0 12px;
+ }
+}
+
+.template-selectors-menu {
+ display: flex;
+ vertical-align: top;
+
+ @media(max-width: map-get($grid-breakpoints, lg)-1) {
+ display: inline-block;
+ width: 100%;
+ padding: 0;
+ border-left: 0;
+ }
+}
+
+.template-selector-dropdowns-wrap {
+ display: flex;
+ vertical-align: top;
+
+ @media(max-width: map-get($grid-breakpoints, lg)-1) {
+ display: block;
+ width: 100%;
+ margin: 0 0 16px;
+ }
+
+ .license-selector,
+ .gitignore-selector,
+ .gitlab-ci-yml-selector,
+ .dockerfile-selector,
+ .template-type-selector,
+ .metrics-dashboard-selector {
+ display: inline-block;
+ vertical-align: top;
+ font-family: $regular_font;
+ margin: 0 8px 0 0;
+
+ @media(max-width: map-get($grid-breakpoints, lg)-1) {
+ display: block;
+ width: 100%;
+ margin: 5px 0;
+ }
+
+ .dropdown {
+ line-height: 21px;
+ }
+
+ .dropdown-menu-toggle {
+ width: 200px;
+ vertical-align: top;
+
+ @media (max-width: map-get($grid-breakpoints, xl)-1) {
+ width: auto;
+ }
+
+ @media(max-width: map-get($grid-breakpoints, lg)-1) {
+ display: block;
+ width: 100%;
+ margin: 5px 0;
+ }
+ }
+ }
+}
+
+.popover.suggest-gitlab-ci-yml {
+ z-index: $header-zindex - 1;
+}
diff --git a/app/assets/stylesheets/page_bundles/group.scss b/app/assets/stylesheets/page_bundles/group.scss
index 71dbb855103..5086cdbf9bc 100644
--- a/app/assets/stylesheets/page_bundles/group.scss
+++ b/app/assets/stylesheets/page_bundles/group.scss
@@ -1,35 +1,16 @@
@import 'page_bundles/mixins_and_variables_and_functions';
.group-home-panel {
- margin-top: $gl-padding;
- margin-bottom: $gl-padding;
-
.home-panel-avatar {
width: $home-panel-title-row-height;
height: $home-panel-title-row-height;
- flex-shrink: 0;
flex-basis: $home-panel-title-row-height;
}
.home-panel-title {
- font-size: 20px;
- line-height: $gl-line-height-24;
- font-weight: bold;
-
.icon {
vertical-align: -1px;
}
-
- .home-panel-topic-list {
- font-size: $gl-font-size;
- font-weight: $gl-font-weight-normal;
-
- .icon {
- position: relative;
- top: 3px;
- margin-right: $gl-padding-4;
- }
- }
}
.home-panel-title-row {
@@ -52,7 +33,7 @@
line-height: $gl-font-size-large;
}
- .home-panel-topic-list,
+
.home-panel-metadata {
font-size: $gl-font-size-small;
}
@@ -60,8 +41,6 @@
}
.home-panel-metadata {
- font-weight: normal;
- font-size: 14px;
line-height: $gl-btn-line-height;
}
diff --git a/app/assets/stylesheets/page_bundles/issues_show.scss b/app/assets/stylesheets/page_bundles/issues_show.scss
index c664e0a734e..26d694f7421 100644
--- a/app/assets/stylesheets/page_bundles/issues_show.scss
+++ b/app/assets/stylesheets/page_bundles/issues_show.scss
@@ -1,77 +1,24 @@
@import 'mixins_and_variables_and_functions';
.description {
- ul,
- ol {
- /* We're changing list-style-position to inside because the default of
- * outside doesn't move negative margin to the left of the bullet. */
- list-style-position: inside;
- }
-
li {
position: relative;
- /* In the browser, the li element comes after (to the right of) the bullet point, so hovering
- * over the left of the bullet point doesn't trigger a row hover. To trigger hovering on the
- * left, we're applying negative margin here to shift the li element left. */
- margin-inline-start: -1rem;
- padding-inline-start: 2.5rem;
+ margin-inline-start: 2.25rem;
.drag-icon {
position: absolute;
inset-block-start: 0.3rem;
- inset-inline-start: 1rem;
- }
-
- /* The inside bullet aligns itself to the bottom, which we see when text to the right of
- * a multi-line list item wraps. We fix this by aligning it to the top, and excluding
- * other elements. Targeting ::marker doesn't seem to work, instead we exclude custom elements
- * or anything with a class */
- > *:not(gl-emoji, code, [class]) {
- vertical-align: top;
- }
-
- /* The inside bullet is treated like an element inside the li element, so when we have a
- * multi-paragraph list item, the text doesn't start on the right of the bullet because
- * it is a block level p element. We make it inline to fix this. */
- > p:first-of-type {
- display: inline-block;
- max-width: calc(100% - 1.5rem);
- }
-
- /* We fix the other paragraphs not indenting to the
- * right of the bullet due to the inside bullet. */
- p ~ a,
- p ~ blockquote,
- p ~ code,
- p ~ details,
- p ~ dl,
- p ~ h1,
- p ~ h2,
- p ~ h3,
- p ~ h4,
- p ~ h5,
- p ~ h6,
- p ~ hr,
- p ~ ol,
- p ~ p,
- p ~ table:not(.code), /* We need :not(.code) to override typography.scss */
- p ~ ul,
- p ~ .markdown-code-block {
- margin-inline-start: 1rem;
+ inset-inline-start: -2.3rem;
+ padding-inline-end: 1rem;
+ width: 2rem;
}
}
- ul.task-list {
- > li.task-list-item {
- /* We're using !important to override the same selector in typography.scss */
- margin-inline-start: -1rem !important;
- padding-inline-start: 2.5rem;
+ ul.task-list > li.task-list-item {
+ margin-inline-start: 0.5rem !important; /* Override typography.scss */
- > input.task-list-item-checkbox {
- position: static;
- vertical-align: middle;
- margin-block-start: -2px;
- }
+ > .drag-icon {
+ inset-inline-start: -0.6rem;
}
}
}
diff --git a/app/assets/stylesheets/page_bundles/merge_requests.scss b/app/assets/stylesheets/page_bundles/merge_requests.scss
index b7a75884425..463c8f74342 100644
--- a/app/assets/stylesheets/page_bundles/merge_requests.scss
+++ b/app/assets/stylesheets/page_bundles/merge_requests.scss
@@ -41,17 +41,21 @@ $tabs-holder-z-index: 250;
// If they don't match, the file tree and the diff files stick
// to the top at different heights, which is a bad-looking defect
$diff-file-header-top: 11px;
- $top-pos: calc(#{$header-height} + #{$mr-tabs-height} + #{$diff-file-header-top});
+ --initial-pos: calc(#{$header-height} + #{$mr-tabs-height} + #{$diff-file-header-top});
+ --top-pos: var(--initial-pos);
position: -webkit-sticky;
position: sticky;
- // Unitless zero values are not allowed in calculations
- top: calc(#{$top-pos} + var(--system-header-height, 0px) + var(--performance-bar-height, 0px));
- max-height: calc(100vh - #{$top-pos} - var(--system-header-height, 0px) - var(--performance-bar-height, 0px) - var(--review-bar-height, 0px));
+ top: var(--top-pos);
+ max-height: calc(100vh - var(--top-pos) - var(--system-header-height, 0px) - var(--performance-bar-height, 0px) - var(--review-bar-height, 0px));
.drag-handle {
bottom: 16px;
}
+
+ &.is-sidebar-moved {
+ --top-pos: calc(var(--initial-pos) + 26px);
+ }
}
.tree-list-holder {
@@ -196,12 +200,8 @@ $tabs-holder-z-index: 250;
background-color: var(--gray-50, $gray-50);
}
-.mr-conflict-loader {
- max-width: 334px;
-
- > svg {
- vertical-align: middle;
- }
+.mr-widget-body-loading svg {
+ vertical-align: middle;
}
.mr-info-list {
@@ -398,12 +398,6 @@ $tabs-holder-z-index: 250;
display: block;
}
- .mr-widget-pipeline-graph {
- .dropdown-menu {
- z-index: $zindex-dropdown-menu;
- }
- }
-
.normal {
flex: 1;
flex-basis: auto;
@@ -440,7 +434,7 @@ $tabs-holder-z-index: 250;
.mr-widget-body {
&:not(.mr-widget-body-line-height-1) {
- line-height: 28px;
+ line-height: 24px;
}
@include clearfix;
@@ -475,12 +469,6 @@ $tabs-holder-z-index: 250;
margin: 0 0 0 10px;
}
- .bold {
- font-weight: $gl-font-weight-bold;
- color: var(--gray-600, $gray-600);
- margin-left: 10px;
- }
-
.state-label {
font-weight: $gl-font-weight-bold;
padding-right: 10px;
@@ -490,11 +478,6 @@ $tabs-holder-z-index: 250;
color: var(--red-500, $red-500);
}
- .spacing,
- .bold {
- vertical-align: middle;
- }
-
.dropdown-menu {
li a {
padding: 5px;
@@ -621,8 +604,8 @@ $tabs-holder-z-index: 250;
.mr-widget-extension-icon::before {
@include gl-content-empty;
@include gl-absolute;
- @include gl-left-0;
- @include gl-top-0;
+ @include gl-left-50p;
+ @include gl-top-half;
@include gl-opacity-3;
@include gl-border-solid;
@include gl-border-4;
@@ -630,24 +613,20 @@ $tabs-holder-z-index: 250;
width: 24px;
height: 24px;
+ transform: translate(-50%, -50%);
}
.mr-widget-extension-icon::after {
@include gl-content-empty;
@include gl-absolute;
@include gl-rounded-full;
+ @include gl-left-50p;
+ @include gl-top-half;
- top: 4px;
- left: 4px;
width: 16px;
height: 16px;
- border: 4px solid currentColor;
-}
-
-.mr-widget-extension-icon svg {
- position: relative;
- top: 2px;
- left: 2px;
+ border: 4px solid;
+ transform: translate(-50%, -50%);
}
.mr-widget-heading {
@@ -777,6 +756,7 @@ $tabs-holder-z-index: 250;
&.show .dropdown-menu {
width: calc(100vw - 20px);
max-width: 650px;
+ max-height: calc(100vh - 50px);
.gl-new-dropdown-inner {
max-height: none !important;
@@ -818,8 +798,7 @@ $tabs-holder-z-index: 250;
}
.md-preview-holder {
- max-height: 180px;
- height: 180px;
+ max-height: 172px;
}
}
@@ -840,3 +819,29 @@ $tabs-holder-z-index: 250;
}
}
}
+
+.merge-request-sticky-header {
+ z-index: 204;
+ box-shadow: 0 1px 2px $issue-boards-card-shadow;
+ --width: calc(100% - #{$contextual-sidebar-width});
+
+ @include media-breakpoint-down(lg) {
+ --width: calc(100% - #{$contextual-sidebar-collapsed-width});
+ }
+}
+
+.detail-page-header-actions {
+ .gl-toggle {
+ @include gl-ml-auto;
+ }
+}
+
+.page-with-icon-sidebar .issue-sticky-header {
+ --width: calc(100% - #{$contextual-sidebar-collapsed-width});
+}
+
+.merge-request-notification-toggle {
+ .gl-toggle-label {
+ @include gl-font-weight-normal;
+ }
+}
diff --git a/app/assets/stylesheets/page_bundles/pipeline_schedules.scss b/app/assets/stylesheets/page_bundles/pipeline_schedules.scss
index 412971253ca..0c73bece035 100644
--- a/app/assets/stylesheets/page_bundles/pipeline_schedules.scss
+++ b/app/assets/stylesheets/page_bundles/pipeline_schedules.scss
@@ -28,13 +28,6 @@
.pipeline-schedule-table-row {
.branch-name-cell {
max-width: 300px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
-
- .next-run-cell {
- color: var(--gray-500, $gray-500);
}
a {
@@ -50,7 +43,6 @@
.bordered-box.content-block {
border: 1px solid var(--border-color, $border-color);
background-color: transparent;
- padding: $gl-spacing-scale-5;
}
}
diff --git a/app/assets/stylesheets/page_bundles/profiles/preferences.scss b/app/assets/stylesheets/page_bundles/profiles/preferences.scss
new file mode 100644
index 00000000000..c9c78a70163
--- /dev/null
+++ b/app/assets/stylesheets/page_bundles/profiles/preferences.scss
@@ -0,0 +1,83 @@
+@import 'page_bundles/mixins_and_variables_and_functions';
+
+.application-theme {
+ $ui-gray-bg: #303030;
+ $ui-light-gray-bg: #f0f0f0;
+ $ui-dark-mode-bg: #1f1f1f;
+
+ .preview {
+ font-size: 0;
+ height: 48px;
+ border-radius: 4px;
+ min-width: 112px;
+ margin-bottom: $gl-padding-8;
+
+ &.ui-indigo {
+ background-color: $indigo-900;
+ }
+
+ &.ui-light-indigo {
+ background-color: $indigo-700;
+ }
+
+ &.ui-blue {
+ background-color: $theme-blue-900;
+ }
+
+ &.ui-light-blue {
+ background-color: $theme-light-blue-700;
+ }
+
+ &.ui-green {
+ background-color: $theme-green-900;
+ }
+
+ &.ui-light-green {
+ background-color: $theme-light-green-700;
+ }
+
+ &.ui-red {
+ background-color: $theme-red-900;
+ }
+
+ &.ui-light-red {
+ background-color: $theme-light-red-700;
+ }
+
+ &.ui-gray {
+ background-color: $ui-gray-bg;
+ border: solid 1px $border-color;
+ }
+
+ &.ui-light-gray {
+ background-color: $ui-light-gray-bg;
+ }
+
+ &.gl-dark {
+ background-color: $ui-dark-mode-bg;
+ border: solid 1px $border-color;
+ }
+ }
+
+ .preview-row {
+ display: block;
+ }
+}
+
+.syntax-theme {
+ label {
+ margin-right: $gl-padding-32;
+ margin-bottom: $gl-padding;
+ text-align: center;
+
+ .preview {
+ margin-bottom: 10px;
+ width: 160px;
+
+ img {
+ border-radius: 4px;
+ max-width: 100%;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/page_bundles/reports.scss b/app/assets/stylesheets/page_bundles/reports.scss
index d0748779f47..03c9fc7508d 100644
--- a/app/assets/stylesheets/page_bundles/reports.scss
+++ b/app/assets/stylesheets/page_bundles/reports.scss
@@ -16,6 +16,10 @@
line-height: 20px;
}
+.report-block-child-icon {
+ height: 20px;
+}
+
.report-block-list {
list-style: none;
padding: 0 1px;
diff --git a/app/assets/stylesheets/page_bundles/todos.scss b/app/assets/stylesheets/page_bundles/todos.scss
index e7813e3b56e..3eacf98688e 100644
--- a/app/assets/stylesheets/page_bundles/todos.scss
+++ b/app/assets/stylesheets/page_bundles/todos.scss
@@ -9,12 +9,6 @@
// workaround because we cannot use border-collapse
border-top: 1px solid transparent;
- &:hover {
- background-color: var(--blue-50, $blue-50);
- border-color: var(--blue-200, $blue-200);
- cursor: pointer;
- }
-
// overwrite border style of .content-list
&:last-child {
border-bottom: 1px solid transparent;
@@ -26,8 +20,6 @@
&.todo-pending.done-reversible {
&:hover {
- border-color: var(--border-color, $border-color);
- background-color: var(--gray-50, $gray-50);
border-top: 1px solid transparent;
.todo-avatar,
@@ -40,20 +32,12 @@
.todo-item {
opacity: 0.2;
}
-
- .btn {
- background-color: var(--gray-50, $gray-50);
- }
}
}
.todo-item {
@include transition(opacity);
- .status-box {
- line-height: inherit;
- }
-
.todo-label,
.todo-project {
a {
@@ -66,22 +50,6 @@
color: var(--gl-text-color, $gl-text-color);
}
- pre {
- border: 0;
- background: var(--gray-50, $gray-50);
- border-radius: 0;
- color: var(--gray-500, $gray-500);
- margin: 0 20px;
- overflow: hidden;
- }
-
- .note-image-attach {
- margin-top: 4px;
- margin-left: 0;
- max-width: 200px;
- float: none;
- }
-
.gl-label-scoped {
--label-inset-border: inset 0 0 0 1px currentColor;
}
diff --git a/app/assets/stylesheets/page_bundles/work_items.scss b/app/assets/stylesheets/page_bundles/work_items.scss
index 9220fa82b46..d0fc011dde7 100644
--- a/app/assets/stylesheets/page_bundles/work_items.scss
+++ b/app/assets/stylesheets/page_bundles/work_items.scss
@@ -7,7 +7,7 @@
#weight-widget-input:not(:hover, :focus),
#weight-widget-input[readonly] {
- box-shadow: inset 0 0 0 $gl-border-size-1 var(--white, $white);
+ box-shadow: none;
}
#weight-widget-input[readonly] {
@@ -19,8 +19,38 @@
display: none;
}
- .assignees-selector:hover .assign-myself {
- display: block;
+ @include media-breakpoint-up(sm) {
+ .assignees-selector:hover .assign-myself {
+ display: block;
+ }
+ }
+}
+
+.work-item-due-date {
+ .gl-datepicker-input.gl-form-input.form-control {
+ width: 10rem;
+
+ &:not(:focus, :hover) {
+ box-shadow: none;
+
+ ~ .gl-datepicker-actions {
+ display: none;
+ }
+ }
+
+ &[disabled] {
+ background-color: var(--white, $white);
+ box-shadow: none;
+
+ ~ .gl-datepicker-actions {
+ display: none;
+ }
+ }
+ }
+
+ .gl-datepicker-actions:focus,
+ .gl-datepicker-actions:hover {
+ display: flex !important;
}
}
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index c96d8ecc782..19318d87731 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -33,12 +33,6 @@
height: 22px;
}
}
-
- .mr-widget-pipeline-graph {
- .dropdown-menu {
- margin-top: 11px;
- }
- }
}
.branch-info .commit-icon {
diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss
deleted file mode 100644
index c177d0b74a2..00000000000
--- a/app/assets/stylesheets/pages/editor.scss
+++ /dev/null
@@ -1,204 +0,0 @@
-.file-editor {
- .nav-links {
- border-top: 1px solid $border-color;
- border-right: 1px solid $border-color;
- border-left: 1px solid $border-color;
- border-bottom: 0;
- border-radius: $border-radius-small $border-radius-small 0 0;
- background: $gray-normal;
- }
-
- #editor,
- .editor {
- @include gl-border-0;
- @include gl-m-0;
- @include gl-p-0;
- @include gl-relative;
- @include gl-w-full;
- height: 500px;
-
- .editor-loading-content {
- @include gl-h-full;
- @include gl-border-0;
- }
- }
-
- .ace_gutter-cell {
- background-color: $gray-light;
- }
-
- .cancel-btn {
- color: $red-600;
-
- &:hover {
- color: $red-600;
- }
- }
-
- .file-title {
- @include gl-font-monospace;
- }
-
- .editor-ref {
- background: $gray-light;
- padding-right: $gl-padding;
- border-right: 1px solid $border-color;
- display: block;
- float: left;
- margin-right: 10px;
- max-width: 250px;
- }
-
- .new-file-name,
- .new-file-path {
- display: inline-block;
- max-width: 250px;
- float: left;
-
- @media(max-width: map-get($grid-breakpoints, lg)-1) {
- width: 180px;
- }
-
- @media (max-width: 1360px) {
- width: auto;
- }
- }
-
- .file-buttons {
- flex: 1;
- }
-
- .soft-wrap-toggle {
- font-family: $regular-font;
- margin-left: $gl-padding-8;
-
- .soft-wrap {
- display: inline-flex;
- }
-
- .no-wrap {
- display: none;
- }
-
- &.soft-wrap-active {
- .soft-wrap {
- display: none;
- }
-
- .no-wrap {
- display: inline-flex;
- }
- }
- }
-}
-
-
-@include media-breakpoint-down(md) {
- .file-editor {
- .file-title {
- display: block;
- }
-
- .new-file-name,
- .new-file-path {
- max-width: none;
- width: 100%;
- margin-top: $gl-padding-8;
- }
-
- .file-buttons {
- display: flex;
- flex-direction: column;
- width: 100%;
-
- .md-header-toolbar {
- margin: $gl-padding 0;
- }
-
- .soft-wrap-toggle {
- width: 100%;
- margin-left: 0;
- }
-
- @media(max-width: map-get($grid-breakpoints, md)-1) {
- clear: both;
- }
- }
- }
-}
-
-.blob-new-page-title,
-.blob-edit-page-title {
- margin: 19px 0 21px;
- vertical-align: top;
- display: inline-block;
-
- @media(max-width: map-get($grid-breakpoints, lg)-1) {
- display: block;
- margin: 19px 0 12px;
- }
-}
-
-.template-selectors-menu {
- display: flex;
- vertical-align: top;
-
- @media(max-width: map-get($grid-breakpoints, lg)-1) {
- display: inline-block;
- width: 100%;
- padding: 0;
- border-left: 0;
- }
-}
-
-.template-selector-dropdowns-wrap {
- display: flex;
- vertical-align: top;
-
- @media(max-width: map-get($grid-breakpoints, lg)-1) {
- display: block;
- width: 100%;
- margin: 0 0 16px;
- }
-
- .license-selector,
- .gitignore-selector,
- .gitlab-ci-yml-selector,
- .dockerfile-selector,
- .template-type-selector,
- .metrics-dashboard-selector {
- display: inline-block;
- vertical-align: top;
- font-family: $regular_font;
- margin: 0 8px 0 0;
-
- @media(max-width: map-get($grid-breakpoints, lg)-1) {
- display: block;
- width: 100%;
- margin: 5px 0;
- }
-
- .dropdown {
- line-height: 21px;
- }
-
- .dropdown-menu-toggle {
- width: 200px;
- vertical-align: top;
-
- @media (max-width: map-get($grid-breakpoints, xl)-1) {
- width: auto;
- }
-
- @media(max-width: map-get($grid-breakpoints, lg)-1) {
- display: block;
- width: 100%;
- margin: 5px 0;
- }
- }
- }
-}
-
-.popover.suggest-gitlab-ci-yml {
- z-index: $header-zindex - 1;
-}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 69797c6b303..85205f4d5ac 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -297,7 +297,7 @@
padding: 0;
.issuable-context-form {
- --initial-top: calc(#{$header-height} + #{$mr-tabs-height});
+ --initial-top: calc(#{$header-height} + 76px);
--top: var(--initial-top);
@include gl-sticky;
@@ -613,7 +613,7 @@
}
.participants-author {
- &:nth-of-type(7n) {
+ &:nth-of-type(8n) {
padding-right: 0;
}
@@ -962,40 +962,26 @@
border-left: 2px solid $gray-50;
position: absolute;
left: 39px;
- height: 80%;
+ height: calc(100% + #{$gl-spacing-scale-5});
+ top: -#{$gl-spacing-scale-5};
}
- &:first-child::before,
- &:last-child::after {
+ &:first-child::before {
content: none;
}
&:first-child {
&::after {
- top: 50%;
+ top: $gl-spacing-scale-5;
+ height: calc(100% + #{$gl-spacing-scale-5});
}
}
- &:last-child {
+ &:last-child,
+ &.create-timeline-event {
&::before {
- bottom: 50%;
- }
- }
-
- &:not(:first-child):not(:last-child) {
- &::before {
- top: -10%;
- }
-
- &::after {
- bottom: -10%;
- }
- }
-
- &.timeline-event-note-form {
- &::before {
- top: -15% !important; // Override default positioning
- height: 20%;
+ top: - #{$gl-spacing-scale-5} !important; // Override default positioning
+ @include gl-h-8;
}
&::after {
@@ -1007,3 +993,22 @@
.timeline-event-note-form {
padding-left: 20px;
}
+
+.timeline-entry:not(:last-child) {
+ .timeline-event-border {
+ @include gl-pb-5;
+ @include gl-border-gray-50;
+ @include gl-border-1;
+ @include gl-border-b-solid;
+ }
+}
+
+.timeline-group:last-child {
+ .timeline-entry:last-child,
+ .create-timeline-event {
+ .timeline-event-bottom-border {
+ @include gl-border-b;
+ @include gl-pt-5;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index a151c28fe93..843daec8cda 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -259,13 +259,15 @@ ul.related-merge-requests > li gl-emoji {
}
.issue-sticky-header {
+ --width: 100%;
+
@include gl-left-0;
- @include gl-w-full;
+ width: var(--width);
top: $header-height;
// collapsed right sidebar
@include media-breakpoint-up(sm) {
- width: calc(100% - #{$gutter-collapsed-width});
+ --width: calc(100% - #{$gutter-collapsed-width});
}
}
@@ -283,12 +285,12 @@ ul.related-merge-requests > li gl-emoji {
// collapsed left sidebar + collapsed right sidebar
.issue-sticky-header {
left: $contextual-sidebar-collapsed-width;
- width: calc(100% - #{$contextual-sidebar-collapsed-width} - #{$gutter-collapsed-width});
+ --width: calc(100% - #{$contextual-sidebar-collapsed-width} - #{$gutter-collapsed-width});
}
// collapsed left sidebar + expanded right sidebar
.right-sidebar-expanded .issue-sticky-header {
- width: calc(100% - #{$contextual-sidebar-collapsed-width} - #{$gutter-width});
+ --width: calc(100% - #{$contextual-sidebar-collapsed-width} - #{$gutter-width});
}
}
@@ -296,23 +298,23 @@ ul.related-merge-requests > li gl-emoji {
// expanded left sidebar + collapsed right sidebar
.issue-sticky-header {
left: $contextual-sidebar-width;
- width: calc(100% - #{$contextual-sidebar-width} - #{$gutter-collapsed-width});
+ --width: calc(100% - #{$contextual-sidebar-width} - #{$gutter-collapsed-width});
}
// collapsed left sidebar + collapsed right sidebar
.page-with-icon-sidebar .issue-sticky-header {
left: $contextual-sidebar-collapsed-width;
- width: calc(100% - #{$contextual-sidebar-collapsed-width} - #{$gutter-collapsed-width});
+ --width: calc(100% - #{$contextual-sidebar-collapsed-width} - #{$gutter-collapsed-width});
}
// expanded left sidebar + expanded right sidebar
.right-sidebar-expanded .issue-sticky-header {
- width: calc(100% - #{$contextual-sidebar-width} - #{$gutter-width});
+ --width: calc(100% - #{$contextual-sidebar-width} - #{$gutter-width});
}
// collapsed left sidebar + expanded right sidebar
.right-sidebar-expanded.page-with-icon-sidebar .issue-sticky-header {
- width: calc(100% - #{$contextual-sidebar-collapsed-width} - #{$gutter-width});
+ --width: calc(100% - #{$contextual-sidebar-collapsed-width} - #{$gutter-width});
}
}
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index 1beb9f05b6c..d4ad6da7f4d 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -1,3 +1,4 @@
+@import 'framework/variables';
/* Login Page */
.login-page {
.container {
@@ -41,6 +42,13 @@
font-size: 13px;
}
+ .signin-text {
+ p {
+ margin-bottom: 0;
+ line-height: 1.5;
+ }
+ }
+
.borderless {
.login-box,
.omniauth-container {
@@ -118,6 +126,18 @@
}
.new-session-tabs {
+ &.nav-links-unboxed {
+ border-color: transparent;
+ box-shadow: none;
+
+ .nav-item {
+ border-left: 0;
+ border-right: 0;
+ border-bottom: 1px solid $gray-100;
+ background-color: transparent;
+ }
+ }
+
display: flex;
box-shadow: 0 0 0 1px $border-color;
border-top-right-radius: $border-radius-default;
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 9692becef4f..cb77c31d59a 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -48,7 +48,7 @@
.common-note-form {
.md-area {
- padding: $gl-padding-8 $gl-padding;
+ padding: 0 $gl-padding;
border: 1px solid $border-color;
border-radius: $border-radius-base;
transition: border-color ease-in-out 0.15s,
@@ -305,7 +305,6 @@ table {
}
.comment-toolbar {
- padding-top: $gl-padding-top;
color: $gl-text-color-secondary;
border-top: 1px solid $border-color;
}
@@ -336,8 +335,7 @@ table {
.toolbar-text {
font-size: 14px;
- line-height: 16px;
- margin-top: 2px;
+ line-height: $gl-spacing-scale-7;
@include media-breakpoint-up(md) {
float: left;
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index db07f16dfd0..fc1b78bf730 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -164,7 +164,7 @@ $system-note-svg-size: 16px;
}
.note-body {
- padding: $gl-padding-4;
+ padding: $gl-padding-4 $gl-padding-4 $gl-padding-4 $gl-padding-8;
overflow-x: auto;
overflow-y: hidden;
@@ -305,7 +305,7 @@ $system-note-svg-size: 16px;
height: $system-note-icon-size;
border: 1px solid $gray-10;
border-radius: $system-note-icon-size;
- margin: -6px 20px 0 0;
+ margin: -6px 0 0;
svg {
width: $system-note-svg-size;
@@ -334,10 +334,14 @@ $system-note-svg-size: 16px;
border-radius: 0;
@media (min-width: map-get($grid-breakpoints, md)) {
- top: calc(#{$mr-tabs-height} + #{$header-height});
+ --initial-top: calc(#{$header-height} + #{$mr-tabs-height});
+
+ &.is-sidebar-moved {
+ --initial-top: calc(#{$header-height} + #{$mr-tabs-height + 28px});
+ }
.with-performance-bar & {
- top: 123px;
+ --top: 123px;
}
}
@@ -551,6 +555,7 @@ $system-note-svg-size: 16px;
.note-header {
display: flex;
justify-content: space-between;
+ align-items: center;
flex-wrap: wrap;
> .note-header-info,
@@ -581,7 +586,7 @@ $system-note-svg-size: 16px;
.note-header-info {
min-width: 0;
- padding-left: $gl-padding-4;
+ padding-left: $gl-padding-8;
&.discussion {
padding-bottom: 0;
diff --git a/app/assets/stylesheets/pages/profiles/preferences.scss b/app/assets/stylesheets/pages/profiles/preferences.scss
deleted file mode 100644
index c7d7aacceec..00000000000
--- a/app/assets/stylesheets/pages/profiles/preferences.scss
+++ /dev/null
@@ -1,81 +0,0 @@
-.application-theme {
- $ui-gray-bg: #303030;
- $ui-light-gray-bg: #f0f0f0;
- $ui-dark-mode-bg: #1f1f1f;
-
- .preview {
- font-size: 0;
- height: 48px;
- border-radius: 4px;
- min-width: 112px;
- margin-bottom: $gl-padding-8;
-
- &.ui-indigo {
- background-color: $indigo-900;
- }
-
- &.ui-light-indigo {
- background-color: $indigo-700;
- }
-
- &.ui-blue {
- background-color: $theme-blue-900;
- }
-
- &.ui-light-blue {
- background-color: $theme-light-blue-700;
- }
-
- &.ui-green {
- background-color: $theme-green-900;
- }
-
- &.ui-light-green {
- background-color: $theme-light-green-700;
- }
-
- &.ui-red {
- background-color: $theme-red-900;
- }
-
- &.ui-light-red {
- background-color: $theme-light-red-700;
- }
-
- &.ui-gray {
- background-color: $ui-gray-bg;
- border: solid 1px $border-color;
- }
-
- &.ui-light-gray {
- background-color: $ui-light-gray-bg;
- }
-
- &.gl-dark {
- background-color: $ui-dark-mode-bg;
- border: solid 1px $border-color;
- }
- }
-
- .preview-row {
- display: block;
- }
-}
-
-.syntax-theme {
- label {
- margin-right: $gl-padding-32;
- margin-bottom: $gl-padding;
- text-align: center;
-
- .preview {
- margin-bottom: 10px;
- width: 160px;
-
- img {
- border-radius: 4px;
- max-width: 100%;
- }
- }
- }
-}
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index 6c909b8d9fa..e8f71c8a21c 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -49,7 +49,7 @@ input[type='checkbox']:hover {
}
&.header-search-is-active {
- .navbar-collapse {
+ .global-search-container {
flex-grow: 1;
}
@@ -59,12 +59,6 @@ input[type='checkbox']:hover {
overflow: hidden;
}
}
-
- @include media-breakpoint-up(xl) {
- .navbar-nav {
- padding-left: 1rem;
- }
- }
}
}
@@ -383,6 +377,10 @@ input[type='checkbox']:hover {
.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 {
diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss
index 935595d1b3b..56acf6de828 100644
--- a/app/assets/stylesheets/pages/settings.scss
+++ b/app/assets/stylesheets/pages/settings.scss
@@ -349,3 +349,9 @@
}
}
}
+
+.gl-md-flex-wrap-nowrap.gl-md-flex-wrap-nowrap {
+ @include gl-media-breakpoint-up(md) {
+ @include gl-flex-wrap-nowrap;
+ }
+}
diff --git a/app/assets/stylesheets/startup/startup-dark.scss b/app/assets/stylesheets/startup/startup-dark.scss
index ffe4d5dde9d..0450b3d9a44 100644
--- a/app/assets/stylesheets/startup/startup-dark.scss
+++ b/app/assets/stylesheets/startup/startup-dark.scss
@@ -10,7 +10,6 @@ body.gl-dark {
--gray-50: #303030;
--gray-100: #404040;
--gray-200: #525252;
- --gray-600: #bfbfbf;
--gray-700: #dbdbdb;
--gray-900: #fafafa;
--green-100: #0d532a;
@@ -18,7 +17,6 @@ body.gl-dark {
--gl-text-color: #fafafa;
--border-color: #4f4f4f;
--black: #fff;
- --nav-active-bg: rgba(255, 255, 255, 0.08);
}
:root {
--white: #333;
@@ -332,9 +330,6 @@ kbd kbd {
color: #fff;
background-color: #c17d10;
}
-.bg-transparent {
- background-color: transparent !important;
-}
.rounded-circle {
border-radius: 50% !important;
}
@@ -459,7 +454,7 @@ a.gl-badge.badge-warning:active {
.gl-form-input:disabled,
.gl-form-input.form-control:disabled {
cursor: not-allowed;
- color: #868686;
+ color: #999;
}
.gl-form-input::placeholder,
.gl-form-input.form-control::placeholder {
@@ -594,9 +589,6 @@ svg {
html {
overflow-y: scroll;
}
-body {
- text-decoration-skip: ink;
-}
.btn {
border-radius: 4px;
font-size: 0.875rem;
@@ -815,20 +807,17 @@ kbd {
.navbar-gitlab .header-content .title img {
height: 24px;
}
-.navbar-gitlab .header-content .title a {
+.navbar-gitlab .header-content .title a:not(.canary-badge) {
display: flex;
align-items: center;
padding: 2px 8px;
margin: 4px 2px 4px -8px;
border-radius: 4px;
}
-.navbar-gitlab .header-content .title a:active {
+.navbar-gitlab .header-content .title a:not(.canary-badge):active {
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.6), 0 0 0 3px #1068bf;
outline: none;
}
-.navbar-gitlab .header-content .title .canary-badge {
- margin-left: -8px;
-}
.navbar-gitlab .header-content .navbar-collapse > ul.nav > li:not(.d-none) {
margin: 0 2px;
}
@@ -1012,9 +1001,6 @@ kbd {
visibility: hidden;
top: 3px;
}
-.top-nav-toggle .dropdown-icon {
- margin-right: 0.5rem;
-}
.tanuki-logo .tanuki {
fill: #e24329;
}
@@ -1137,7 +1123,7 @@ kbd {
font-weight: 600;
}
.nav-sidebar li.active:not(.fly-out-top-item) > a:not(.has-sub-items) {
- background-color: rgba(41, 41, 97, 0.08);
+ background-color: rgba(255, 255, 255, 0.08);
}
.nav-sidebar ul {
padding-left: 0;
@@ -1790,7 +1776,6 @@ body.gl-dark {
--white: #333;
--black: #fff;
--svg-status-bg: #333;
- --nav-active-bg: rgba(255, 255, 255, 0.08);
}
.nav-sidebar,
.toggle-sidebar-button,
@@ -1802,15 +1787,6 @@ body.gl-dark {
.avatar {
background: rgba(255, 255, 255, 0.04);
}
-.nav-sidebar li a {
- color: var(--gray-600);
-}
-.nav-sidebar li.active {
- box-shadow: none;
-}
-.nav-sidebar li.active:not(.fly-out-top-item) > a:not(.has-sub-items) {
- background-color: var(--nav-active-bg);
-}
body.gl-dark {
--gl-theme-accent: #868686;
}
@@ -2038,7 +2014,6 @@ body.gl-dark {
--white: #333;
--black: #fff;
--svg-status-bg: #333;
- --nav-active-bg: rgba(255, 255, 255, 0.08);
}
.tab-width-8 {
tab-size: 8;
@@ -2113,6 +2088,12 @@ body.gl-dark {
.gl-pt-0 {
padding-top: 0;
}
+.gl-mr-auto {
+ margin-right: auto;
+}
+.gl-mr-3 {
+ margin-right: 0.5rem;
+}
.gl-ml-n2 {
margin-left: -0.25rem;
}
diff --git a/app/assets/stylesheets/startup/startup-general.scss b/app/assets/stylesheets/startup/startup-general.scss
index 00ca98bfd27..356fb58b4c8 100644
--- a/app/assets/stylesheets/startup/startup-general.scss
+++ b/app/assets/stylesheets/startup/startup-general.scss
@@ -311,9 +311,6 @@ kbd kbd {
color: #fff;
background-color: #ab6100;
}
-.bg-transparent {
- background-color: transparent !important;
-}
.rounded-circle {
border-radius: 50% !important;
}
@@ -438,7 +435,7 @@ a.gl-badge.badge-warning:active {
.gl-form-input:disabled,
.gl-form-input.form-control:disabled {
cursor: not-allowed;
- color: #868686;
+ color: #666;
}
.gl-form-input::placeholder,
.gl-form-input.form-control::placeholder {
@@ -573,9 +570,6 @@ svg {
html {
overflow-y: scroll;
}
-body {
- text-decoration-skip: ink;
-}
.btn {
border-radius: 4px;
font-size: 0.875rem;
@@ -794,20 +788,17 @@ kbd {
.navbar-gitlab .header-content .title img {
height: 24px;
}
-.navbar-gitlab .header-content .title a {
+.navbar-gitlab .header-content .title a:not(.canary-badge) {
display: flex;
align-items: center;
padding: 2px 8px;
margin: 4px 2px 4px -8px;
border-radius: 4px;
}
-.navbar-gitlab .header-content .title a:active {
+.navbar-gitlab .header-content .title a:not(.canary-badge):active {
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.6), 0 0 0 3px #63a6e9;
outline: none;
}
-.navbar-gitlab .header-content .title .canary-badge {
- margin-left: -8px;
-}
.navbar-gitlab .header-content .navbar-collapse > ul.nav > li:not(.d-none) {
margin: 0 2px;
}
@@ -991,9 +982,6 @@ kbd {
visibility: hidden;
top: 3px;
}
-.top-nav-toggle .dropdown-icon {
- margin-right: 0.5rem;
-}
.tanuki-logo .tanuki {
fill: #e24329;
}
@@ -1116,7 +1104,7 @@ kbd {
font-weight: 600;
}
.nav-sidebar li.active:not(.fly-out-top-item) > a:not(.has-sub-items) {
- background-color: rgba(41, 41, 97, 0.08);
+ background-color: rgba(0, 0, 0, 0.08);
}
.nav-sidebar ul {
padding-left: 0;
@@ -1751,6 +1739,12 @@ svg.s16 {
.gl-pt-0 {
padding-top: 0;
}
+.gl-mr-auto {
+ margin-right: auto;
+}
+.gl-mr-3 {
+ margin-right: 0.5rem;
+}
.gl-ml-n2 {
margin-left: -0.25rem;
}
diff --git a/app/assets/stylesheets/startup/startup-signin.scss b/app/assets/stylesheets/startup/startup-signin.scss
index c0e2d8d44d4..edc579f48f6 100644
--- a/app/assets/stylesheets/startup/startup-signin.scss
+++ b/app/assets/stylesheets/startup/startup-signin.scss
@@ -11,6 +11,9 @@ html {
font-family: sans-serif;
line-height: 1.15;
}
+header {
+ display: block;
+}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
@@ -28,7 +31,8 @@ hr {
height: 0;
overflow: visible;
}
-h1 {
+h1,
+h3 {
margin-top: 0;
margin-bottom: 0.25rem;
}
@@ -49,26 +53,49 @@ img {
vertical-align: middle;
border-style: none;
}
+svg {
+ overflow: hidden;
+ vertical-align: middle;
+}
label {
display: inline-block;
margin-bottom: 0.5rem;
}
-input {
+button {
+ border-radius: 0;
+}
+input,
+button {
margin: 0;
font-family: inherit;
font-size: inherit;
line-height: inherit;
}
+button,
input {
overflow: visible;
}
+button {
+ text-transform: none;
+}
+[role="button"] {
+ cursor: pointer;
+}
+button:not(:disabled),
+[type="button"]:not(:disabled),
[type="submit"]:not(:disabled) {
cursor: pointer;
}
+button::-moz-focus-inner,
+[type="button"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
padding: 0;
border-style: none;
}
+input[type="checkbox"] {
+ box-sizing: border-box;
+ padding: 0;
+}
fieldset {
min-width: 0;
padding: 0;
@@ -78,7 +105,8 @@ fieldset {
[hidden] {
display: none !important;
}
-h1 {
+h1,
+h3 {
margin-bottom: 0.25rem;
font-weight: 600;
line-height: 1.2;
@@ -87,6 +115,9 @@ h1 {
h1 {
font-size: 2.1875rem;
}
+h3 {
+ font-size: 1.53125rem;
+}
hr {
margin-top: 0.5rem;
margin-bottom: 0.5rem;
@@ -120,23 +151,42 @@ hr {
max-width: 1140px;
}
}
-.col-sm-12,
-.col {
+.row {
+ display: flex;
+ flex-wrap: wrap;
+ margin-right: -15px;
+ margin-left: -15px;
+}
+.col-md-6,
+.col-sm-12 {
position: relative;
width: 100%;
padding-right: 15px;
padding-left: 15px;
}
-.col {
- flex-basis: 0;
- flex-grow: 1;
- max-width: 100%;
+.order-1 {
+ order: 1;
+}
+.order-12 {
+ order: 12;
}
@media (min-width: 576px) {
.col-sm-12 {
flex: 0 0 100%;
max-width: 100%;
}
+ .order-sm-1 {
+ order: 1;
+ }
+ .order-sm-12 {
+ order: 12;
+ }
+}
+@media (min-width: 768px) {
+ .col-md-6 {
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
}
.form-control {
display: block;
@@ -169,16 +219,6 @@ hr {
.form-group {
margin-bottom: 1rem;
}
-.form-row {
- display: flex;
- flex-wrap: wrap;
- margin-right: -5px;
- margin-left: -5px;
-}
-.form-row > .col {
- padding-right: 5px;
- padding-left: 5px;
-}
.btn {
display: inline-block;
font-weight: 400;
@@ -204,6 +244,137 @@ hr {
fieldset:disabled a.btn {
pointer-events: none;
}
+.btn-block {
+ display: block;
+ width: 100%;
+}
+.btn-block + .btn-block {
+ margin-top: 0.5rem;
+}
+input.btn-block[type="submit"],
+input.btn-block[type="button"] {
+ width: 100%;
+}
+.custom-control {
+ position: relative;
+ z-index: 1;
+ display: block;
+ min-height: 1.5rem;
+ padding-left: 1.5rem;
+ color-adjust: exact;
+}
+.custom-control-input {
+ position: absolute;
+ left: 0;
+ z-index: -1;
+ width: 1rem;
+ height: 1.25rem;
+ opacity: 0;
+}
+.custom-control-input:checked ~ .custom-control-label::before {
+ color: #fff;
+ border-color: #007bff;
+ background-color: #007bff;
+}
+.custom-control-input:not(:disabled):active ~ .custom-control-label::before {
+ color: #fff;
+ background-color: #b3d7ff;
+ border-color: #b3d7ff;
+}
+.custom-control-input:disabled ~ .custom-control-label {
+ color: #5e5e5e;
+}
+.custom-control-input:disabled ~ .custom-control-label::before {
+ background-color: #fafafa;
+}
+.custom-control-label {
+ position: relative;
+ margin-bottom: 0;
+ vertical-align: top;
+}
+.custom-control-label::before {
+ position: absolute;
+ top: 0.25rem;
+ left: -1.5rem;
+ display: block;
+ width: 1rem;
+ height: 1rem;
+ pointer-events: none;
+ content: "";
+ background-color: #fff;
+ border: #666 solid 1px;
+}
+.custom-control-label::after {
+ position: absolute;
+ top: 0.25rem;
+ left: -1.5rem;
+ display: block;
+ width: 1rem;
+ height: 1rem;
+ content: "";
+ background: no-repeat 50% / 50% 50%;
+}
+.custom-checkbox .custom-control-label::before {
+ border-radius: 0.25rem;
+}
+.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e");
+}
+.custom-checkbox
+ .custom-control-input:indeterminate
+ ~ .custom-control-label::before {
+ border-color: #007bff;
+ background-color: #007bff;
+}
+.custom-checkbox
+ .custom-control-input:indeterminate
+ ~ .custom-control-label::after {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e");
+}
+.custom-checkbox
+ .custom-control-input:disabled:checked
+ ~ .custom-control-label::before {
+ background-color: rgba(0, 123, 255, 0.5);
+}
+.custom-checkbox
+ .custom-control-input:disabled:indeterminate
+ ~ .custom-control-label::before {
+ background-color: rgba(0, 123, 255, 0.5);
+}
+@media (prefers-reduced-motion: reduce) {
+}
+.tab-content > .tab-pane {
+ display: none;
+}
+.tab-content > .active {
+ display: block;
+}
+.navbar {
+ position: relative;
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0.25rem 0.5rem;
+}
+.navbar .container {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ justify-content: space-between;
+}
+.clearfix::after {
+ display: block;
+ clear: both;
+ content: "";
+}
+.fixed-top {
+ position: fixed;
+ top: 0;
+ right: 0;
+ left: 0;
+ z-index: 1030;
+}
.mt-3 {
margin-top: 1rem !important;
}
@@ -213,8 +384,8 @@ fieldset:disabled a.btn {
.text-nowrap {
white-space: nowrap !important;
}
-.text-center {
- text-align: center !important;
+.font-weight-normal {
+ font-weight: 400 !important;
}
.gl-form-input,
.gl-form-input.form-control {
@@ -245,19 +416,109 @@ fieldset:disabled a.btn {
.gl-form-input:disabled,
.gl-form-input.form-control:disabled {
cursor: not-allowed;
- color: #868686;
+ color: #666;
}
.gl-form-input::placeholder,
.gl-form-input.form-control::placeholder {
color: #868686;
}
+.gl-form-checkbox {
+ font-size: 0.875rem;
+ line-height: 1rem;
+ color: #303030;
+}
+.gl-form-checkbox .custom-control-input:disabled,
+.gl-form-checkbox .custom-control-input:disabled ~ .custom-control-label {
+ cursor: not-allowed;
+ color: #868686;
+}
+.gl-form-checkbox.custom-control .custom-control-input ~ .custom-control-label {
+ cursor: pointer;
+}
+.gl-form-checkbox.custom-control
+ .custom-control-input
+ ~ .custom-control-label::before,
+.gl-form-checkbox.custom-control
+ .custom-control-input
+ ~ .custom-control-label::after {
+ top: 0;
+}
+.gl-form-checkbox.custom-control
+ .custom-control-input
+ ~ .custom-control-label::before {
+ background-color: #fff;
+ border-color: #868686;
+}
+.gl-form-checkbox.custom-control
+ .custom-control-input:checked
+ ~ .custom-control-label::before {
+ background-color: #1f75cb;
+ border-color: #1f75cb;
+}
+.gl-form-checkbox.custom-control
+ .custom-control-input[type="checkbox"]:checked
+ ~ .custom-control-label::after,
+.gl-form-checkbox.custom-control
+ .custom-control-input[type="checkbox"]:indeterminate
+ ~ .custom-control-label::after {
+ background: none;
+ background-color: #fff;
+ mask-repeat: no-repeat;
+ mask-position: center center;
+}
+.gl-form-checkbox.custom-control
+ .custom-control-input[type="checkbox"]:checked
+ ~ .custom-control-label::after {
+ mask-image: url('data:image/svg+xml,%3Csvg width="8" height="7" viewBox="0 0 8 7" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M1 3.05299L2.99123 5L7 1" stroke="white" stroke-width="2"/%3E%3C/svg%3E%0A');
+}
+.gl-form-checkbox.custom-control
+ .custom-control-input[type="checkbox"]:indeterminate
+ ~ .custom-control-label::after {
+ mask-image: url('data:image/svg+xml,%3Csvg width="8" height="2" viewBox="0 0 8 2" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M0 1L8 1" stroke="white" stroke-width="2"/%3E%3C/svg%3E%0A');
+}
+.gl-form-checkbox.custom-control.custom-checkbox
+ .custom-control-input:indeterminate
+ ~ .custom-control-label::before {
+ background-color: #1f75cb;
+ border-color: #1f75cb;
+}
+.gl-form-checkbox.custom-control
+ .custom-control-input:disabled
+ ~ .custom-control-label {
+ cursor: not-allowed;
+}
+.gl-form-checkbox.custom-control
+ .custom-control-input:disabled
+ ~ .custom-control-label::before {
+ background-color: #f0f0f0;
+ border-color: #dbdbdb;
+ pointer-events: auto;
+}
+.gl-form-checkbox.custom-control
+ .custom-control-input:checked:disabled
+ ~ .custom-control-label::before,
+.gl-form-checkbox.custom-control
+ .custom-control-input:indeterminate:disabled
+ ~ .custom-control-label::before {
+ background-color: #dbdbdb;
+ border-color: #dbdbdb;
+}
+.gl-form-checkbox.custom-control
+ .custom-control-input:checked:disabled
+ ~ .custom-control-label::after,
+.gl-form-checkbox.custom-control
+ .custom-control-input:indeterminate:disabled
+ ~ .custom-control-label::after {
+ background-color: #5e5e5e;
+}
.gl-button {
display: inline-flex;
}
.gl-button:not(.btn-link):active {
text-decoration: none;
}
-.gl-button.gl-button {
+.gl-button.gl-button,
+.gl-button.gl-button.btn-block {
border-width: 0;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
@@ -273,7 +534,8 @@ fieldset:disabled a.btn {
font-size: 0.875rem;
border-radius: 0.25rem;
}
-.gl-button.gl-button .gl-button-text {
+.gl-button.gl-button .gl-button-text,
+.gl-button.gl-button.btn-block .gl-button-text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -282,29 +544,39 @@ fieldset:disabled a.btn {
margin-top: -1px;
margin-bottom: -1px;
}
-.gl-button.gl-button .gl-button-icon {
+.gl-button.gl-button .gl-button-icon,
+.gl-button.gl-button.btn-block .gl-button-icon {
height: 1rem;
width: 1rem;
flex-shrink: 0;
margin-right: 0.25rem;
top: auto;
}
-.gl-button.gl-button.btn-default {
+.gl-button.gl-button.btn-default,
+.gl-button.gl-button.btn-block.btn-default {
background-color: #fff;
}
-.gl-button.gl-button.btn-default:active {
+.gl-button.gl-button.btn-default:active,
+.gl-button.gl-button.btn-default.active,
+.gl-button.gl-button.btn-block.btn-default:active,
+.gl-button.gl-button.btn-block.btn-default.active {
box-shadow: inset 0 0 0 1px #5e5e5e, 0 0 0 1px #fff, 0 0 0 3px #428fdc;
outline: none;
background-color: #dbdbdb;
}
-.gl-button.gl-button.btn-confirm {
+.gl-button.gl-button.btn-confirm,
+.gl-button.gl-button.btn-block.btn-confirm {
color: #fff;
}
-.gl-button.gl-button.btn-confirm {
+.gl-button.gl-button.btn-confirm,
+.gl-button.gl-button.btn-block.btn-confirm {
background-color: #1f75cb;
box-shadow: inset 0 0 0 1px #1068bf;
}
-.gl-button.gl-button.btn-confirm:active {
+.gl-button.gl-button.btn-confirm:active,
+.gl-button.gl-button.btn-confirm.active,
+.gl-button.gl-button.btn-block.btn-confirm:active,
+.gl-button.gl-button.btn-block.btn-confirm.active {
box-shadow: inset 0 0 0 1px #033464, 0 0 0 1px #fff, 0 0 0 3px #428fdc;
outline: none;
background-color: #0b5cad;
@@ -312,10 +584,14 @@ fieldset:disabled a.btn {
body {
font-size: 0.875rem;
}
-[type="submit"] {
+button,
+html [type="button"],
+[type="submit"],
+[role="button"] {
cursor: pointer;
}
-h1 {
+h1,
+h3 {
margin-top: 20px;
margin-bottom: 10px;
}
@@ -325,6 +601,9 @@ a {
hr {
overflow: hidden;
}
+svg {
+ vertical-align: baseline;
+}
.form-control {
font-size: 0.875rem;
}
@@ -332,15 +611,9 @@ hr {
display: none !important;
visibility: hidden !important;
}
-.hide {
- display: none;
-}
html {
overflow-y: scroll;
}
-body {
- text-decoration-skip: ink;
-}
body.navless {
background-color: #fff !important;
}
@@ -375,13 +648,34 @@ body.navless {
background-color: #f0f0f0;
box-shadow: none;
}
-.btn:active {
+.btn:active,
+.btn.active {
background-color: #eaeaea;
border-color: #e3e3e3;
color: #303030;
}
-.light {
- color: #303030;
+.btn svg {
+ height: 15px;
+ width: 15px;
+}
+.btn svg:not(:last-child) {
+ margin-right: 5px;
+}
+.btn-block {
+ width: 100%;
+ margin: 0;
+ margin-bottom: 1rem;
+}
+.btn-block.btn {
+ padding: 6px 0;
+}
+.tab-content {
+ overflow: visible;
+}
+@media (max-width: 767.98px) {
+ .tab-content {
+ isolation: isolate;
+ }
}
hr {
margin: 1.5rem 0;
@@ -419,6 +713,9 @@ input {
label {
font-weight: 600;
}
+label.custom-control-label {
+ font-weight: 400;
+}
label.label-bold {
font-weight: 600;
}
@@ -432,8 +729,25 @@ label.label-bold {
.gl-show-field-errors .form-control:not(textarea) {
height: 34px;
}
-.gl-show-field-errors .gl-field-hint {
- color: #303030;
+.navbar-empty {
+ justify-content: center;
+ height: var(--header-height, 48px);
+ background: #fff;
+ border-bottom: 1px solid #dbdbdb;
+}
+.navbar-empty .tanuki-logo,
+.navbar-empty .brand-header-logo {
+ max-height: 100%;
+}
+.tanuki-logo .tanuki {
+ fill: #e24329;
+}
+.tanuki-logo .left-cheek,
+.tanuki-logo .right-cheek {
+ fill: #fc6d26;
+}
+.tanuki-logo .chin {
+ fill: #fca326;
}
input::-moz-placeholder {
color: #868686;
@@ -445,6 +759,9 @@ input::-ms-input-placeholder {
input:-ms-input-placeholder {
color: #868686;
}
+svg {
+ fill: currentColor;
+}
.login-page .container {
max-width: 960px;
}
@@ -477,6 +794,10 @@ input:-ms-input-placeholder {
.login-page p {
font-size: 13px;
}
+.login-page .signin-text p {
+ margin-bottom: 0;
+ line-height: 1.5;
+}
.login-page .borderless .login-box,
.login-page .borderless .omniauth-container {
box-shadow: none;
@@ -549,6 +870,16 @@ input:-ms-input-placeholder {
border-top-right-radius: 4px;
border-top-left-radius: 4px;
}
+.login-page .new-session-tabs.nav-links-unboxed {
+ border-color: transparent;
+ box-shadow: none;
+}
+.login-page .new-session-tabs.nav-links-unboxed .nav-item {
+ border-left: 0;
+ border-right: 0;
+ border-bottom: 1px solid #dbdbdb;
+ background-color: transparent;
+}
.login-page .new-session-tabs.custom-provider-tabs {
flex-wrap: wrap;
}
@@ -648,14 +979,20 @@ input:-ms-input-placeholder {
}
}
-.gl-text-green-600 {
- color: #217645;
+.gl-display-flex {
+ display: flex;
}
-.gl-text-red-500 {
- color: #dd2b0e;
+.gl-display-inline-block {
+ display: inline-block;
}
-.gl-display-block {
- display: block;
+.gl-flex-wrap {
+ flex-wrap: wrap;
+}
+.gl-justify-content-center {
+ justify-content: center;
+}
+.gl-float-right {
+ float: right;
}
.gl-w-10 {
width: 3.5rem;
@@ -674,14 +1011,18 @@ input:-ms-input-placeholder {
width: 100%;
}
}
-.gl-p-4 {
- padding: 0.75rem;
+.gl-p-5 {
+ padding: 1rem;
+}
+.gl-px-5 {
+ padding-left: 1rem;
+ padding-right: 1rem;
}
.gl-pt-5 {
padding-top: 1rem;
}
-.gl-mt-2 {
- margin-top: 0.25rem;
+.gl-mt-3 {
+ margin-top: 0.5rem;
}
.gl-mt-5 {
margin-top: 1rem;
@@ -701,15 +1042,17 @@ input:-ms-input-placeholder {
.gl-mb-3 {
margin-bottom: 0.5rem;
}
-.gl-mb-5 {
- margin-bottom: 1rem;
-}
.gl-ml-auto {
margin-left: auto;
}
.gl-ml-2 {
margin-left: 0.25rem;
}
+@media (min-width: 576px) {
+ .gl-sm-mt-0 {
+ margin-top: 0;
+ }
+}
.gl-text-center {
text-align: center;
}
@@ -719,6 +1062,9 @@ input:-ms-input-placeholder {
.gl-font-weight-normal {
font-weight: 400;
}
+.gl-font-weight-bold {
+ font-weight: 600;
+}
@import "startup/cloaking";
@include cloak-startup-scss(none);
diff --git a/app/assets/stylesheets/themes/_dark.scss b/app/assets/stylesheets/themes/_dark.scss
index eeb4604f32a..4b74e449e06 100644
--- a/app/assets/stylesheets/themes/_dark.scss
+++ b/app/assets/stylesheets/themes/_dark.scss
@@ -101,7 +101,6 @@ $white-dark: #444;
$theme-indigo-50: #1a1a40;
$border-color: #4f4f4f;
-$nav-active-bg: rgba(255, 255, 255, 0.08);
:root {
color-scheme: dark;
@@ -206,7 +205,6 @@ body.gl-dark {
--black: #{$black};
--svg-status-bg: #{$white};
- --nav-active-bg: #{$nav-active-bg};
.gl-button.gl-button,
.gl-button.gl-button.btn-block {
diff --git a/app/assets/stylesheets/themes/dark_mode_overrides.scss b/app/assets/stylesheets/themes/dark_mode_overrides.scss
index 92740aaf89e..e1ba2a69420 100644
--- a/app/assets/stylesheets/themes/dark_mode_overrides.scss
+++ b/app/assets/stylesheets/themes/dark_mode_overrides.scss
@@ -60,26 +60,6 @@
}
.nav-sidebar {
- li {
- a {
- color: var(--gray-600);
- }
-
- > a:hover {
- background-color: var(--nav-active-bg);
- }
-
- &.active {
- box-shadow: none;
-
- &:not(.fly-out-top-item) {
- > a:not(.has-sub-items) {
- background-color: var(--nav-active-bg);
- }
- }
- }
- }
-
.sidebar-sub-level-items.fly-out-list {
box-shadow: none;
border: 1px solid $border-color;
@@ -92,7 +72,7 @@ aside.right-sidebar:not(.right-sidebar-merge-requests) {
}
body.gl-dark {
- @include gitlab-theme($gray-900, $gray-400, $gray-500, $gray-900, $gray-900, $white);
+ @include gitlab-theme($gray-900, $gray-400, $gray-500, $gray-900, $white);
.terms {
.logo-text {
diff --git a/app/assets/stylesheets/themes/theme_blue.scss b/app/assets/stylesheets/themes/theme_blue.scss
index 817557f37cd..90122cec31f 100644
--- a/app/assets/stylesheets/themes/theme_blue.scss
+++ b/app/assets/stylesheets/themes/theme_blue.scss
@@ -6,7 +6,6 @@ body {
$theme-blue-200,
$theme-blue-500,
$theme-blue-700,
- $gray-900,
$theme-blue-900,
$white
);
diff --git a/app/assets/stylesheets/themes/theme_gray.scss b/app/assets/stylesheets/themes/theme_gray.scss
index 75b111f90c7..a6cdfb36a7c 100644
--- a/app/assets/stylesheets/themes/theme_gray.scss
+++ b/app/assets/stylesheets/themes/theme_gray.scss
@@ -7,7 +7,6 @@ body {
$gray-300,
$gray-500,
$gray-900,
- $gray-900,
$white
);
}
diff --git a/app/assets/stylesheets/themes/theme_green.scss b/app/assets/stylesheets/themes/theme_green.scss
index 7e387e97452..0300f261d64 100644
--- a/app/assets/stylesheets/themes/theme_green.scss
+++ b/app/assets/stylesheets/themes/theme_green.scss
@@ -6,7 +6,6 @@ body {
$theme-green-200,
$theme-green-500,
$theme-green-700,
- $gray-900,
$theme-green-900,
$white
);
diff --git a/app/assets/stylesheets/themes/theme_helper.scss b/app/assets/stylesheets/themes/theme_helper.scss
index 042e21cebd6..d644d8acc98 100644
--- a/app/assets/stylesheets/themes/theme_helper.scss
+++ b/app/assets/stylesheets/themes/theme_helper.scss
@@ -6,18 +6,22 @@
$search-and-nav-links,
$accent,
$border-and-box-shadow,
- $sidebar-text,
- $nav-svg-color,
- $color-alternate
+ $navbar-theme-color,
+ $navbar-theme-contrast-color
) {
// Set custom properties
--gl-theme-accent: #{$accent};
+ $search-and-nav-links-a20: rgba($search-and-nav-links, 0.2);
+ $search-and-nav-links-a30: rgba($search-and-nav-links, 0.3);
+ $search-and-nav-links-a40: rgba($search-and-nav-links, 0.4);
+ $search-and-nav-links-a80: rgba($search-and-nav-links, 0.8);
+
// Header
.navbar-gitlab {
- background-color: $nav-svg-color;
+ background-color: $navbar-theme-color;
.navbar-collapse {
color: $search-and-nav-links;
@@ -37,7 +41,7 @@
> button {
&:hover,
&:focus {
- background-color: rgba($search-and-nav-links, 0.2);
+ background-color: $search-and-nav-links-a20;
}
}
@@ -45,13 +49,13 @@
&.dropdown.show {
> a,
> button {
- color: $nav-svg-color;
- background-color: $color-alternate;
+ color: $navbar-theme-color;
+ background-color: $navbar-theme-contrast-color;
}
}
&.line-separator {
- border-left: 1px solid rgba($search-and-nav-links, 0.2);
+ border-left: 1px solid $search-and-nav-links-a20;
}
}
}
@@ -65,12 +69,12 @@
color: $search-and-nav-links;
&.header-search-new {
- color: $sidebar-text;
+ color: $gray-900;
}
> a {
.notification-dot {
- border: 2px solid $nav-svg-color;
+ border: 2px solid $navbar-theme-color;
}
&.header-help-dropdown-toggle {
@@ -88,7 +92,7 @@
&:hover,
&:focus {
@include media-breakpoint-up(sm) {
- background-color: rgba($search-and-nav-links, 0.2);
+ background-color: $search-and-nav-links-a20;
}
svg {
@@ -97,7 +101,7 @@
.notification-dot {
will-change: border-color, background-color;
- border-color: adjust-color($nav-svg-color, $red: 33, $green: 33, $blue: 33);
+ border-color: adjust-color($navbar-theme-color, $red: 33, $green: 33, $blue: 33);
}
&.header-help-dropdown-toggle .notification-dot {
@@ -108,12 +112,12 @@
&.active > a,
&.dropdown.show > a {
- color: $nav-svg-color;
- background-color: $color-alternate;
+ color: $navbar-theme-color;
+ background-color: $navbar-theme-contrast-color;
&:hover {
svg {
- fill: $nav-svg-color;
+ fill: $navbar-theme-color;
}
}
@@ -123,7 +127,7 @@
&.header-help-dropdown-toggle {
.notification-dot {
- background-color: $nav-svg-color;
+ background-color: $navbar-theme-color;
}
}
}
@@ -131,7 +135,7 @@
.impersonated-user,
.impersonated-user:hover {
svg {
- fill: $nav-svg-color;
+ fill: $navbar-theme-color;
}
}
}
@@ -142,30 +146,30 @@
> a {
&:hover,
&:focus {
- background-color: rgba($search-and-nav-links, 0.2);
+ background-color: $search-and-nav-links-a20;
}
}
}
.header-search {
- background-color: rgba($search-and-nav-links, 0.2) !important;
+ background-color: $search-and-nav-links-a20 !important;
border-radius: $border-radius-default;
&:hover {
- background-color: rgba($search-and-nav-links, 0.3) !important;
+ background-color: $search-and-nav-links-a30 !important;
}
svg.gl-search-box-by-type-search-icon {
- color: rgba($search-and-nav-links, 0.8);
+ color: $search-and-nav-links-a80;
}
input {
background-color: transparent;
- color: rgba($search-and-nav-links, 0.8);
- box-shadow: inset 0 0 0 1px rgba($search-and-nav-links, 0.4);
+ color: $search-and-nav-links-a80;
+ box-shadow: inset 0 0 0 1px $search-and-nav-links-a40;
&::placeholder {
- color: rgba($search-and-nav-links, 0.8);
+ color: $search-and-nav-links-a80;
}
&:focus,
@@ -178,27 +182,27 @@
.keyboard-shortcut-helper {
color: $search-and-nav-links;
- background-color: rgba($search-and-nav-links, 0.2);
+ background-color: $search-and-nav-links-a20;
}
}
.search {
form {
- background-color: rgba($search-and-nav-links, 0.2);
+ background-color: $search-and-nav-links-a20;
&:hover {
- background-color: rgba($search-and-nav-links, 0.3);
+ background-color: $search-and-nav-links-a30;
}
}
.search-input::placeholder {
- color: rgba($search-and-nav-links, 0.8);
+ color: $search-and-nav-links-a80;
}
.search-input-wrap {
.search-icon,
.clear-icon {
- fill: rgba($search-and-nav-links, 0.8);
+ fill: $search-and-nav-links-a80;
}
}
@@ -209,7 +213,7 @@
.search-input-wrap {
.search-icon {
- fill: rgba($search-and-nav-links, 0.8);
+ fill: $search-and-nav-links-a80;
}
}
}
@@ -217,7 +221,7 @@
// Sidebar
.nav-sidebar li.active > a {
- color: $sidebar-text;
+ color: $gray-900;
}
.nav-sidebar {
diff --git a/app/assets/stylesheets/themes/theme_indigo.scss b/app/assets/stylesheets/themes/theme_indigo.scss
index 3bf6cfea650..5a27a9cfdc5 100644
--- a/app/assets/stylesheets/themes/theme_indigo.scss
+++ b/app/assets/stylesheets/themes/theme_indigo.scss
@@ -6,7 +6,6 @@ body {
$indigo-200,
$indigo-500,
$indigo-700,
- $gray-900,
$indigo-900,
$white
);
diff --git a/app/assets/stylesheets/themes/theme_light_blue.scss b/app/assets/stylesheets/themes/theme_light_blue.scss
index 771a84911b3..7cb0d98802e 100644
--- a/app/assets/stylesheets/themes/theme_light_blue.scss
+++ b/app/assets/stylesheets/themes/theme_light_blue.scss
@@ -6,7 +6,6 @@ body {
$theme-light-blue-200,
$theme-light-blue-500,
$theme-light-blue-500,
- $gray-900,
$theme-light-blue-700,
$white
);
diff --git a/app/assets/stylesheets/themes/theme_light_gray.scss b/app/assets/stylesheets/themes/theme_light_gray.scss
index ad19438d79a..a0cbec9a92b 100644
--- a/app/assets/stylesheets/themes/theme_light_gray.scss
+++ b/app/assets/stylesheets/themes/theme_light_gray.scss
@@ -6,7 +6,6 @@ body {
$gray-500,
$gray-700,
$gray-500,
- $gray-900,
$gray-50,
$gray-500
);
diff --git a/app/assets/stylesheets/themes/theme_light_green.scss b/app/assets/stylesheets/themes/theme_light_green.scss
index 8c991a7bfb3..797279cc37b 100644
--- a/app/assets/stylesheets/themes/theme_light_green.scss
+++ b/app/assets/stylesheets/themes/theme_light_green.scss
@@ -6,7 +6,6 @@ body {
$theme-green-200,
$theme-green-500,
$theme-green-500,
- $gray-900,
$theme-light-green-700,
$white
);
diff --git a/app/assets/stylesheets/themes/theme_light_indigo.scss b/app/assets/stylesheets/themes/theme_light_indigo.scss
index 6c220e0459a..3632c5ad45a 100644
--- a/app/assets/stylesheets/themes/theme_light_indigo.scss
+++ b/app/assets/stylesheets/themes/theme_light_indigo.scss
@@ -6,7 +6,6 @@ body {
$indigo-200,
$indigo-500,
$indigo-500,
- $gray-900,
$indigo-700,
$white
);
diff --git a/app/assets/stylesheets/themes/theme_light_red.scss b/app/assets/stylesheets/themes/theme_light_red.scss
index e1a715293b4..6c10d9178f1 100644
--- a/app/assets/stylesheets/themes/theme_light_red.scss
+++ b/app/assets/stylesheets/themes/theme_light_red.scss
@@ -6,7 +6,6 @@ body {
$theme-light-red-200,
$theme-light-red-500,
$theme-light-red-500,
- $gray-900,
$theme-light-red-700,
$white
);
diff --git a/app/assets/stylesheets/themes/theme_red.scss b/app/assets/stylesheets/themes/theme_red.scss
index 19fd150727d..140e27de6e2 100644
--- a/app/assets/stylesheets/themes/theme_red.scss
+++ b/app/assets/stylesheets/themes/theme_red.scss
@@ -6,7 +6,6 @@ body {
$theme-red-200,
$theme-red-500,
$theme-red-700,
- $gray-900,
$theme-red-900,
$white
);
diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss
index 6bd05f90f26..bdb8f758137 100644
--- a/app/assets/stylesheets/utilities.scss
+++ b/app/assets/stylesheets/utilities.scss
@@ -370,3 +370,13 @@ to @gitlab/ui by https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1709
.gl-flex-flow-row-wrap {
flex-flow: row wrap;
}
+
+/*
+ * The below style will be moved to @gitlab/ui by
+ * https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1963
+ */
+.gl-gap-y-3 {
+ > * + * {
+ margin-top: $gl-spacing-scale-3;
+ }
+}
diff --git a/app/components/layouts/horizontal_section_component.haml b/app/components/layouts/horizontal_section_component.haml
new file mode 100644
index 00000000000..4b5b4f1d0df
--- /dev/null
+++ b/app/components/layouts/horizontal_section_component.haml
@@ -0,0 +1,10 @@
+%div{ formatted_options }
+ .row
+ .col-lg-4
+ %h4.gl-mt-0
+ = title
+ - if description?
+ %p
+ = description
+ .col-lg-8
+ = body
diff --git a/app/components/layouts/horizontal_section_component.rb b/app/components/layouts/horizontal_section_component.rb
new file mode 100644
index 00000000000..48c960f17d9
--- /dev/null
+++ b/app/components/layouts/horizontal_section_component.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Layouts
+ class HorizontalSectionComponent < ViewComponent::Base
+ # @param [Boolean] border
+ # @param [Hash] options
+ def initialize(border: true, options: {})
+ @border = border
+ @options = options
+ end
+
+ private
+
+ renders_one :title
+ renders_one :description
+ renders_one :body
+
+ def formatted_options
+ @options.merge({ class: [('gl-border-b' if @border), @options[:class]].flatten.compact })
+ end
+ end
+end
diff --git a/app/components/pajamas/badge_component.html.haml b/app/components/pajamas/badge_component.html.haml
new file mode 100644
index 00000000000..eaadc681f0e
--- /dev/null
+++ b/app/components/pajamas/badge_component.html.haml
@@ -0,0 +1,6 @@
+- if link?
+ %a{ href: @href, **html_options }><
+ = badge_content
+- else
+ %span{ **html_options }><
+ = badge_content
diff --git a/app/components/pajamas/badge_component.rb b/app/components/pajamas/badge_component.rb
new file mode 100644
index 00000000000..244064b0e1e
--- /dev/null
+++ b/app/components/pajamas/badge_component.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+module Pajamas
+ class BadgeComponent < Pajamas::Component
+ def initialize(
+ text = nil,
+ icon: nil,
+ icon_classes: [],
+ icon_only: false,
+ href: nil,
+ size: :md,
+ variant: :muted,
+ **html_options
+ )
+ @text = text.presence
+ @icon = icon.to_s.presence
+ @icon_classes = Array.wrap(icon_classes)
+ @icon_only = @icon && icon_only
+ @href = href.presence
+ @size = filter_attribute(size.to_sym, SIZE_OPTIONS, default: :md)
+ @variant = filter_attribute(variant.to_sym, VARIANT_OPTIONS, default: :muted)
+ @html_options = html_options
+ end
+
+ private
+
+ SIZE_OPTIONS = [:sm, :md, :lg].freeze
+ VARIANT_OPTIONS = [:muted, :neutral, :info, :success, :warning, :danger].freeze
+
+ delegate :sprite_icon, to: :helpers
+
+ def badge_classes
+ ["gl-badge", "badge", "badge-pill", "badge-#{@variant}", @size.to_s]
+ end
+
+ def icon_classes
+ classes = %w[gl-icon gl-badge-icon] + @icon_classes
+ classes.push("gl-mr-2") unless icon_only?
+ classes.join(" ")
+ end
+
+ def icon_only?
+ @icon_only
+ end
+
+ def link?
+ @href.present?
+ end
+
+ # Determines the rendered text content.
+ # The content slot takes presedence over the text param.
+ def text
+ content || @text
+ end
+
+ def badge_content
+ if icon_only?
+ sprite_icon(@icon, css_class: icon_classes)
+ elsif @icon.present?
+ sprite_icon(@icon, css_class: icon_classes) + text
+ else
+ text
+ end
+ end
+
+ def html_options
+ options = format_options(options: @html_options, css_classes: badge_classes)
+ options.merge!({ aria: { label: text }, role: "img" }) if icon_only?
+ options
+ end
+ end
+end
diff --git a/app/components/pajamas/button_component.rb b/app/components/pajamas/button_component.rb
index 4233e446d5b..b2dd798b718 100644
--- a/app/components/pajamas/button_component.rb
+++ b/app/components/pajamas/button_component.rb
@@ -112,7 +112,7 @@ module Pajamas
def base_attributes
attributes = {}
- attributes['disabled'] = '' if @disabled || @loading
+ attributes['disabled'] = 'disabled' if @disabled || @loading
attributes['aria-disabled'] = true if @disabled || @loading
attributes['type'] = @type unless @href
diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb
index 206a5b11e4b..0de2115d4d6 100644
--- a/app/controllers/abuse_reports_controller.rb
+++ b/app/controllers/abuse_reports_controller.rb
@@ -30,10 +30,7 @@ class AbuseReportsController < ApplicationController
private
def report_params
- params.require(:abuse_report).permit(%i(
- message
- user_id
- ))
+ params.require(:abuse_report).permit(:message, :user_id)
end
# rubocop: disable CodeReuse/ActiveRecord
diff --git a/app/controllers/acme_challenges_controller.rb b/app/controllers/acme_challenges_controller.rb
index 67a39d8870b..4a7706db94e 100644
--- a/app/controllers/acme_challenges_controller.rb
+++ b/app/controllers/acme_challenges_controller.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
+# rubocop:disable Rails/ApplicationController
class AcmeChallengesController < ActionController::Base
def show
if acme_order
@@ -15,3 +16,4 @@ class AcmeChallengesController < ActionController::Base
@acme_order ||= PagesDomainAcmeOrder.find_by_domain_and_token(params[:domain], params[:token])
end
end
+# rubocop:enable Rails/ApplicationController
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 6f21b123eb0..b75a7c4a2dd 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -18,23 +18,23 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
end
feature_category :not_owned, [ # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned
- :general, :reporting, :metrics_and_profiling, :network,
- :preferences, :update, :reset_health_check_token
- ]
+ :general, :reporting, :metrics_and_profiling, :network,
+ :preferences, :update, :reset_health_check_token
+ ]
feature_category :metrics, [
- :create_self_monitoring_project,
- :status_create_self_monitoring_project,
- :delete_self_monitoring_project,
- :status_delete_self_monitoring_project
- ]
+ :create_self_monitoring_project,
+ :status_create_self_monitoring_project,
+ :delete_self_monitoring_project,
+ :status_delete_self_monitoring_project
+ ]
urgency :low, [
- :create_self_monitoring_project,
- :status_create_self_monitoring_project,
- :delete_self_monitoring_project,
- :status_delete_self_monitoring_project,
- :reset_error_tracking_access_token
- ]
+ :create_self_monitoring_project,
+ :status_create_self_monitoring_project,
+ :delete_self_monitoring_project,
+ :status_delete_self_monitoring_project,
+ :reset_error_tracking_access_token
+ ]
feature_category :source_code_management, [:repository, :clear_repository_check_states]
feature_category :continuous_integration, [:ci_cd, :reset_registration_token]
diff --git a/app/controllers/admin/applications_controller.rb b/app/controllers/admin/applications_controller.rb
index b0d7c8cb8f2..d66b3cb4366 100644
--- a/app/controllers/admin/applications_controller.rb
+++ b/app/controllers/admin/applications_controller.rb
@@ -14,7 +14,7 @@ class Admin::ApplicationsController < Admin::ApplicationController
end
def show
- @created = get_created_session
+ @created = get_created_session if Feature.disabled?('hash_oauth_secrets')
end
def new
@@ -30,9 +30,14 @@ class Admin::ApplicationsController < Admin::ApplicationController
if @application.persisted?
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
- set_created_session
+ if Feature.enabled?('hash_oauth_secrets')
+ @created = true
+ render :show
+ else
+ set_created_session
- redirect_to admin_application_url(@application)
+ redirect_to admin_application_url(@application)
+ end
else
render :new
end
diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb
index a53e832329f..251ba9e29f2 100644
--- a/app/controllers/admin/broadcast_messages_controller.rb
+++ b/app/controllers/admin/broadcast_messages_controller.rb
@@ -57,14 +57,15 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController
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: [])
+ 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
end
diff --git a/app/controllers/admin/cohorts_controller.rb b/app/controllers/admin/cohorts_controller.rb
index 468a1077694..ce3d769f35e 100644
--- a/app/controllers/admin/cohorts_controller.rb
+++ b/app/controllers/admin/cohorts_controller.rb
@@ -1,15 +1,20 @@
# frozen_string_literal: true
class Admin::CohortsController < Admin::ApplicationController
- include RedisTracking
+ include ProductAnalyticsTracking
feature_category :devops_reports
urgency :low
+ track_custom_event :index,
+ name: 'i_analytics_cohorts',
+ action: 'perform_analytics_usage_action',
+ label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly',
+ destinations: %i[redis_hll snowplow]
+
def index
@cohorts = load_cohorts
- track_cohorts_visit
end
private
@@ -22,7 +27,11 @@ class Admin::CohortsController < Admin::ApplicationController
CohortsSerializer.new.represent(cohorts_results)
end
- def track_cohorts_visit
- track_unique_redis_hll_event('i_analytics_cohorts') if trackable_html_request?
+ def tracking_namespace_source
+ nil
+ end
+
+ def tracking_project_source
+ nil
end
end
diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb
index 8fe106249c3..37dde065e70 100644
--- a/app/controllers/admin/dashboard_controller.rb
+++ b/app/controllers/admin/dashboard_controller.rb
@@ -14,14 +14,7 @@ class Admin::DashboardController < Admin::ApplicationController
@groups = Group.order_id_desc.with_route.limit(10)
@notices = Gitlab::ConfigChecker::PumaRuggedChecker.check
@notices += Gitlab::ConfigChecker::ExternalDatabaseChecker.check
- @redis_versions = [
- Gitlab::Redis::Queues,
- Gitlab::Redis::SharedState,
- Gitlab::Redis::Cache,
- Gitlab::Redis::TraceChunks,
- Gitlab::Redis::RateLimiting,
- Gitlab::Redis::Sessions
- ].map(&:version).uniq
+ @redis_versions = Gitlab::Redis::ALL_CLASSES.map(&:version).uniq
end
def stats
diff --git a/app/controllers/admin/hook_logs_controller.rb b/app/controllers/admin/hook_logs_controller.rb
index aa13673095d..a283d3abb0b 100644
--- a/app/controllers/admin/hook_logs_controller.rb
+++ b/app/controllers/admin/hook_logs_controller.rb
@@ -1,34 +1,17 @@
# frozen_string_literal: true
-class Admin::HookLogsController < Admin::ApplicationController
- include ::Integrations::HooksExecution
+module Admin
+ class HookLogsController < Admin::ApplicationController
+ include WebHooks::HookLogActions
- before_action :hook, only: [:show, :retry]
- before_action :hook_log, only: [:show, :retry]
+ private
- respond_to :html
+ def hook
+ @hook ||= SystemHook.find(params[:hook_id])
+ end
- feature_category :integrations
- urgency :low, [:retry]
-
- def show
- end
-
- def retry
- result = hook.execute(hook_log.request_data, hook_log.trigger)
-
- set_hook_execution_notice(result)
-
- redirect_to edit_admin_hook_path(@hook)
- end
-
- private
-
- def hook
- @hook ||= SystemHook.find(params[:hook_id])
- end
-
- def hook_log
- @hook_log ||= hook.web_hook_logs.find(params[:id])
+ def after_retry_redirect_path
+ edit_admin_hook_path(hook)
+ end
end
end
diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb
index 810801d4209..1dc6c68d8ca 100644
--- a/app/controllers/admin/hooks_controller.rb
+++ b/app/controllers/admin/hooks_controller.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class Admin::HooksController < Admin::ApplicationController
- include ::Integrations::HooksExecution
+ include ::WebHooks::HookActions
before_action :hook_logs, only: :edit
@@ -27,7 +27,7 @@ class Admin::HooksController < Admin::ApplicationController
end
def hook_logs
- @hook_logs ||= hook.web_hook_logs.recent.page(params[:page])
+ @hook_logs ||= hook.web_hook_logs.recent.page(params[:page]).without_count
end
def hook_param_names
diff --git a/app/controllers/admin/plan_limits_controller.rb b/app/controllers/admin/plan_limits_controller.rb
index 7bfbabe8dfc..2cebc059830 100644
--- a/app/controllers/admin/plan_limits_controller.rb
+++ b/app/controllers/admin/plan_limits_controller.rb
@@ -28,24 +28,25 @@ class Admin::PlanLimitsController < Admin::ApplicationController
end
def plan_limits_params
- params.require(:plan_limits).permit(%i[
- plan_id
- conan_max_file_size
- helm_max_file_size
- maven_max_file_size
- npm_max_file_size
- nuget_max_file_size
- pypi_max_file_size
- terraform_module_max_file_size
- generic_packages_max_file_size
- ci_pipeline_size
- ci_active_jobs
- ci_active_pipelines
- ci_project_subscriptions
- ci_pipeline_schedules
- ci_needs_size_limit
- ci_registered_group_runners
- ci_registered_project_runners
- ])
+ params.require(:plan_limits)
+ .permit(%i[
+ plan_id
+ conan_max_file_size
+ helm_max_file_size
+ maven_max_file_size
+ npm_max_file_size
+ nuget_max_file_size
+ pypi_max_file_size
+ terraform_module_max_file_size
+ generic_packages_max_file_size
+ ci_pipeline_size
+ ci_active_jobs
+ ci_active_pipelines
+ ci_project_subscriptions
+ ci_pipeline_schedules
+ ci_needs_size_limit
+ ci_registered_group_runners
+ ci_registered_project_runners
+ ])
end
end
diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb
index 24d7bd9ca7b..a0f72f5e58c 100644
--- a/app/controllers/admin/runners_controller.rb
+++ b/app/controllers/admin/runners_controller.rb
@@ -8,6 +8,10 @@ class Admin::RunnersController < Admin::ApplicationController
push_frontend_feature_flag(:admin_runners_bulk_delete)
end
+ before_action only: [:show] do
+ push_frontend_feature_flag(:enforce_runner_token_expires_at)
+ end
+
feature_category :runner
urgency :low
@@ -22,7 +26,7 @@ class Admin::RunnersController < Admin::ApplicationController
end
def update
- if Ci::Runners::UpdateRunnerService.new(@runner).update(runner_params)
+ if Ci::Runners::UpdateRunnerService.new(@runner).execute(runner_params).success?
respond_to do |format|
format.html { redirect_to edit_admin_runner_path(@runner) }
end
@@ -39,7 +43,7 @@ class Admin::RunnersController < Admin::ApplicationController
end
def resume
- if Ci::Runners::UpdateRunnerService.new(@runner).update(active: true)
+ if Ci::Runners::UpdateRunnerService.new(@runner).execute(active: true).success?
redirect_to admin_runners_path, notice: _('Runner was successfully updated.')
else
redirect_to admin_runners_path, alert: _('Runner was not updated.')
@@ -47,7 +51,7 @@ class Admin::RunnersController < Admin::ApplicationController
end
def pause
- if Ci::Runners::UpdateRunnerService.new(@runner).update(active: false)
+ if Ci::Runners::UpdateRunnerService.new(@runner).execute(active: false).success?
redirect_to admin_runners_path, notice: _('Runner was successfully updated.')
else
redirect_to admin_runners_path, alert: _('Runner was not updated.')
diff --git a/app/controllers/admin/spam_logs_controller.rb b/app/controllers/admin/spam_logs_controller.rb
index e4e866a8b60..3a55fc4b951 100644
--- a/app/controllers/admin/spam_logs_controller.rb
+++ b/app/controllers/admin/spam_logs_controller.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class Admin::SpamLogsController < Admin::ApplicationController
- feature_category :not_owned # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned
+ feature_category :instance_resiliency
# rubocop: disable CodeReuse/ActiveRecord
def index
diff --git a/app/controllers/admin/topics_controller.rb b/app/controllers/admin/topics_controller.rb
index 69bcfdf4791..e97ead12f71 100644
--- a/app/controllers/admin/topics_controller.rb
+++ b/app/controllers/admin/topics_controller.rb
@@ -49,16 +49,12 @@ class Admin::TopicsController < Admin::ApplicationController
source_topic = Projects::Topic.find(merge_params[:source_topic_id])
target_topic = Projects::Topic.find(merge_params[:target_topic_id])
- begin
- ::Topics::MergeService.new(source_topic, target_topic).execute
- rescue ArgumentError => e
- return render status: :bad_request, json: { type: :alert, message: e.message }
- end
+ response = ::Topics::MergeService.new(source_topic, target_topic).execute
+ return render status: :bad_request, json: { type: :alert, message: response.message } if response.error?
message = _('Topic %{source_topic} was successfully merged into topic %{target_topic}.')
- redirect_to admin_topics_path,
- status: :found,
- notice: message % { source_topic: source_topic.name, target_topic: target_topic.name }
+ flash[:toast] = message % { source_topic: source_topic.name, target_topic: target_topic.name }
+ redirect_to admin_topics_path, status: :found
end
private
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 5cc0c8f3970..1a57d271271 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -105,7 +105,7 @@ class Admin::UsersController < Admin::ApplicationController
return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user cannot be deactivated")) if user.blocked?
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: ::User::MINIMUM_INACTIVE_DAYS }) 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 }) unless user.can_be_deactivated?
user.deactivate
redirect_back_or_admin_user(notice: _("Successfully deactivated"))
diff --git a/app/controllers/boards/issues_controller.rb b/app/controllers/boards/issues_controller.rb
index 11377df7a10..5028544795c 100644
--- a/app/controllers/boards/issues_controller.rb
+++ b/app/controllers/boards/issues_controller.rb
@@ -77,10 +77,10 @@ module Boards
:milestone,
:assignees,
project: [
- :route,
- {
- namespace: [:route]
- }
+ :route,
+ {
+ namespace: [:route]
+ }
],
labels: [:priorities],
notes: [:award_emoji, :author]
diff --git a/app/controllers/chaos_controller.rb b/app/controllers/chaos_controller.rb
index 4e5af1945a4..6139168d29f 100644
--- a/app/controllers/chaos_controller.rb
+++ b/app/controllers/chaos_controller.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
+# rubocop:disable Rails/ApplicationController
class ChaosController < ActionController::Base
before_action :validate_chaos_secret, unless: :development_or_test?
@@ -93,3 +94,4 @@ class ChaosController < ActionController::Base
Rails.env.development? || Rails.env.test?
end
end
+# rubocop:enable Rails/ApplicationController
diff --git a/app/controllers/concerns/accepts_pending_invitations.rb b/app/controllers/concerns/accepts_pending_invitations.rb
index 53dec698fa0..1723058c217 100644
--- a/app/controllers/concerns/accepts_pending_invitations.rb
+++ b/app/controllers/concerns/accepts_pending_invitations.rb
@@ -8,7 +8,6 @@ module AcceptsPendingInvitations
if user.pending_invitations.load.any?
user.accept_pending_invitations!
- clear_stored_location_for(user: user)
after_pending_invitations_hook
end
end
@@ -16,10 +15,4 @@ module AcceptsPendingInvitations
def after_pending_invitations_hook
# no-op
end
-
- def clear_stored_location_for(user:)
- session_key = stored_location_key_for(user)
-
- session.delete(session_key)
- end
end
diff --git a/app/controllers/concerns/dependency_proxy/group_access.rb b/app/controllers/concerns/dependency_proxy/group_access.rb
index 45392625e45..e9fb2563e42 100644
--- a/app/controllers/concerns/dependency_proxy/group_access.rb
+++ b/app/controllers/concerns/dependency_proxy/group_access.rb
@@ -20,3 +20,5 @@ module DependencyProxy
end
end
end
+
+DependencyProxy::GroupAccess.prepend_mod_with('DependencyProxy::GroupAccess')
diff --git a/app/controllers/concerns/harbor/access.rb b/app/controllers/concerns/harbor/access.rb
index 70de72f15fc..211566aeda7 100644
--- a/app/controllers/concerns/harbor/access.rb
+++ b/app/controllers/concerns/harbor/access.rb
@@ -17,7 +17,7 @@ module Harbor
private
def harbor_registry_enabled!
- render_404 unless Feature.enabled?(:harbor_registry_integration)
+ render_404 unless Feature.enabled?(:harbor_registry_integration, defined?(group) ? group : project)
end
def authorize_read_harbor_registry!
diff --git a/app/controllers/concerns/integrations/hooks_execution.rb b/app/controllers/concerns/integrations/hooks_execution.rb
deleted file mode 100644
index fb26840168f..00000000000
--- a/app/controllers/concerns/integrations/hooks_execution.rb
+++ /dev/null
@@ -1,95 +0,0 @@
-# frozen_string_literal: true
-
-module Integrations::HooksExecution
- extend ActiveSupport::Concern
-
- included do
- attr_writer :hooks, :hook
- end
-
- def index
- self.hooks = relation.select(&:persisted?)
- self.hook = relation.new
- end
-
- def create
- self.hook = relation.new(hook_params)
- hook.save
-
- unless hook.valid?
- self.hooks = relation.select(&:persisted?)
- flash[:alert] = hook.errors.full_messages.join.html_safe
- end
-
- redirect_to action: :index
- end
-
- def update
- if hook.update(hook_params)
- flash[:notice] = _('Hook was successfully updated.')
- redirect_to action: :index
- else
- render 'edit'
- end
- end
-
- def destroy
- destroy_hook(hook)
-
- redirect_to action: :index, status: :found
- end
-
- def edit
- redirect_to(action: :index) unless hook
- end
-
- private
-
- def hook_params
- permitted = hook_param_names + trigger_values
- permitted << { url_variables: [:key, :value] }
-
- ps = params.require(:hook).permit(*permitted).to_h
-
- ps[:url_variables] = ps[:url_variables].to_h { [_1[:key], _1[:value].presence] } if ps.key?(:url_variables)
-
- if action_name == 'update' && ps.key?(:url_variables)
- supplied = ps[:url_variables]
- ps[:url_variables] = hook.url_variables.merge(supplied).compact
- end
-
- ps
- end
-
- def hook_param_names
- %i[enable_ssl_verification token url push_events_branch_filter]
- end
-
- def destroy_hook(hook)
- result = WebHooks::DestroyService.new(current_user).execute(hook)
-
- if result[:status] == :success
- flash[:notice] =
- if result[:async]
- _("%{hook_type} was scheduled for deletion") % { hook_type: hook.model_name.human }
- else
- _("%{hook_type} was deleted") % { hook_type: hook.model_name.human }
- end
- else
- flash[:alert] = result[:message]
- end
- end
-
- def set_hook_execution_notice(result)
- http_status = result[:http_status]
- message = result[:message]
-
- if http_status && http_status >= 200 && http_status < 400
- flash[:notice] = "Hook executed successfully: HTTP #{http_status}"
- elsif http_status
- flash[:alert] = "Hook executed successfully but returned HTTP #{http_status} #{message}"
- else
- flash[:alert] = "Hook execution failed: #{message}"
- end
- end
-end
diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb
index f1d80e37674..7c3401a7e90 100644
--- a/app/controllers/concerns/issuable_actions.rb
+++ b/app/controllers/concerns/issuable_actions.rb
@@ -193,7 +193,10 @@ module IssuableActions
end
def render_cached_discussions(discussions, serializer, cache_context)
- render_cached(discussions, with: serializer, cache_context: -> (_) { cache_context }, context: self)
+ render_cached(discussions,
+ with: serializer,
+ cache_context: -> (_) { cache_context },
+ context: self)
end
def paginated_discussions
diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb
index fb11bece79c..8a67b62f28b 100644
--- a/app/controllers/concerns/membership_actions.rb
+++ b/app/controllers/concerns/membership_actions.rb
@@ -150,7 +150,11 @@ module MembershipActions
when 'only'
[:inherited]
else
- [:inherited, :direct]
+ if Feature.enabled?(:webui_members_inherited_users, current_user)
+ [:inherited, :direct, :shared_from_groups]
+ else
+ [:inherited, :direct]
+ end
end
end
end
diff --git a/app/controllers/concerns/packages_access.rb b/app/controllers/concerns/packages_access.rb
index 6df2e064bb2..a7d16a5bc88 100644
--- a/app/controllers/concerns/packages_access.rb
+++ b/app/controllers/concerns/packages_access.rb
@@ -15,6 +15,6 @@ module PackagesAccess
end
def verify_read_package!
- authorize_read_package!(project)
+ access_denied! unless can?(current_user, :read_package, project&.packages_policy_subject)
end
end
diff --git a/app/controllers/concerns/product_analytics_tracking.rb b/app/controllers/concerns/product_analytics_tracking.rb
index 260b433cc6f..8e936782e5a 100644
--- a/app/controllers/concerns/product_analytics_tracking.rb
+++ b/app/controllers/concerns/product_analytics_tracking.rb
@@ -66,7 +66,17 @@ module ProductAnalyticsTracking
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
+ 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
}
Feature.enabled?(events_to_ff[event.to_sym], tracking_namespace_source)
diff --git a/app/controllers/concerns/verifies_with_email.rb b/app/controllers/concerns/verifies_with_email.rb
index 1a3e7136481..782cae53c3f 100644
--- a/app/controllers/concerns/verifies_with_email.rb
+++ b/app/controllers/concerns/verifies_with_email.rb
@@ -7,11 +7,9 @@ module VerifiesWithEmail
extend ActiveSupport::Concern
include ActionView::Helpers::DateHelper
- TOKEN_LENGTH = 6
- TOKEN_VALID_FOR_MINUTES = 60
-
included do
prepend_before_action :verify_with_email, only: :create, unless: -> { two_factor_enabled? }
+ skip_before_action :required_signup_info, only: :successful_verification
end
def verify_with_email
@@ -76,7 +74,8 @@ module VerifiesWithEmail
def send_verification_instructions(user)
return if send_rate_limited?(user)
- raw_token, encrypted_token = generate_token
+ service = Users::EmailVerification::GenerateTokenService.new(attr: :unlock_token)
+ raw_token, encrypted_token = service.execute
user.unlock_token = encrypted_token
user.lock_access!({ send_instructions: false })
send_verification_instructions_email(user, raw_token)
@@ -88,27 +87,20 @@ module VerifiesWithEmail
Notify.verification_instructions_email(
user.id,
token: token,
- expires_in: TOKEN_VALID_FOR_MINUTES).deliver_later
+ expires_in: Users::EmailVerification::ValidateTokenService::TOKEN_VALID_FOR_MINUTES).deliver_later
log_verification(user, :instructions_sent)
end
def verify_token(user, token)
- return handle_verification_failure(user, :rate_limited) if verification_rate_limited?(user)
- return handle_verification_failure(user, :invalid) unless valid_token?(user, token)
- return handle_verification_failure(user, :expired) if expired_token?(user)
-
- handle_verification_success(user)
- end
-
- def generate_token
- raw_token = SecureRandom.random_number(10**TOKEN_LENGTH).to_s.rjust(TOKEN_LENGTH, '0')
- encrypted_token = digest_token(raw_token)
- [raw_token, encrypted_token]
- end
+ service = Users::EmailVerification::ValidateTokenService.new(attr: :unlock_token, user: user, token: token)
+ result = service.execute
- def digest_token(token)
- Devise.token_generator.digest(User, :unlock_token, token)
+ if result[:status] == :success
+ handle_verification_success(user)
+ else
+ handle_verification_failure(user, result[:reason], result[:message])
+ end
end
def render_sign_in_rate_limited
@@ -122,44 +114,17 @@ module VerifiesWithEmail
distance_of_time_in_words(interval_in_seconds)
end
- def verification_rate_limited?(user)
- Gitlab::ApplicationRateLimiter.throttled?(:email_verification, scope: user.unlock_token)
- end
-
def send_rate_limited?(user)
Gitlab::ApplicationRateLimiter.throttled?(:email_verification_code_send, scope: user)
end
- def expired_token?(user)
- user.locked_at < (Time.current - TOKEN_VALID_FOR_MINUTES.minutes)
- end
-
- def valid_token?(user, token)
- user.unlock_token == digest_token(token)
- end
-
- def handle_verification_failure(user, reason)
- message = case reason
- when :rate_limited
- s_("IdentityVerification|You've reached the maximum amount of tries. "\
- 'Wait %{interval} or resend a new code and try again.') % { interval: email_verification_interval }
- when :expired
- s_('IdentityVerification|The code has expired. Resend a new code and try again.')
- when :invalid
- s_('IdentityVerification|The code is incorrect. Enter it again, or resend a new code.')
- end
-
+ def handle_verification_failure(user, reason, message)
user.errors.add(:base, message)
log_verification(user, :failed_attempt, reason)
prompt_for_email_verification(user)
end
- def email_verification_interval
- interval_in_seconds = Gitlab::ApplicationRateLimiter.rate_limits[:email_verification][:interval]
- distance_of_time_in_words(interval_in_seconds)
- end
-
def handle_verification_success(user)
user.unlock_access!
log_verification(user, :successful)
diff --git a/app/controllers/concerns/web_hooks/hook_actions.rb b/app/controllers/concerns/web_hooks/hook_actions.rb
new file mode 100644
index 00000000000..ea11f13c7ef
--- /dev/null
+++ b/app/controllers/concerns/web_hooks/hook_actions.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+module WebHooks
+ module HookActions
+ extend ActiveSupport::Concern
+ include HookExecutionNotice
+
+ included do
+ attr_writer :hooks, :hook
+ end
+
+ def index
+ self.hooks = relation.select(&:persisted?)
+ self.hook = relation.new
+ end
+
+ def create
+ self.hook = relation.new(hook_params)
+ hook.save
+
+ unless hook.valid?
+ self.hooks = relation.select(&:persisted?)
+ flash[:alert] = hook.errors.full_messages.join.html_safe
+ end
+
+ redirect_to action: :index
+ end
+
+ def update
+ if hook.update(hook_params)
+ flash[:notice] = _('Hook was successfully updated.')
+ redirect_to action: :index
+ else
+ render 'edit'
+ end
+ end
+
+ def destroy
+ destroy_hook(hook)
+
+ redirect_to action: :index, status: :found
+ end
+
+ def edit
+ redirect_to(action: :index) unless hook
+ end
+
+ private
+
+ def hook_params
+ permitted = hook_param_names + trigger_values
+ permitted << { url_variables: [:key, :value] }
+
+ ps = params.require(:hook).permit(*permitted).to_h
+
+ ps[:url_variables] = ps[:url_variables].to_h { [_1[:key], _1[:value].presence] } if ps.key?(:url_variables)
+
+ if action_name == 'update' && ps.key?(:url_variables)
+ supplied = ps[:url_variables]
+ ps[:url_variables] = hook.url_variables.merge(supplied).compact
+ end
+
+ ps
+ end
+
+ def hook_param_names
+ %i[enable_ssl_verification token url push_events_branch_filter]
+ 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
+ else
+ flash[:alert] = result[:message]
+ end
+ end
+ end
+end
diff --git a/app/controllers/concerns/web_hooks/hook_execution_notice.rb b/app/controllers/concerns/web_hooks/hook_execution_notice.rb
new file mode 100644
index 00000000000..d651313b30d
--- /dev/null
+++ b/app/controllers/concerns/web_hooks/hook_execution_notice.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module WebHooks
+ module HookExecutionNotice
+ private
+
+ def set_hook_execution_notice(result)
+ http_status = result[:http_status]
+ message = result[:message]
+
+ if http_status && http_status >= 200 && http_status < 400
+ flash[:notice] = "Hook executed successfully: HTTP #{http_status}"
+ elsif http_status
+ flash[:alert] = "Hook executed successfully but returned HTTP #{http_status} #{message}"
+ else
+ flash[:alert] = "Hook execution failed: #{message}"
+ end
+ end
+ end
+end
diff --git a/app/controllers/concerns/web_hooks/hook_log_actions.rb b/app/controllers/concerns/web_hooks/hook_log_actions.rb
new file mode 100644
index 00000000000..f3378d7c857
--- /dev/null
+++ b/app/controllers/concerns/web_hooks/hook_log_actions.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module WebHooks
+ module HookLogActions
+ extend ActiveSupport::Concern
+ include HookExecutionNotice
+
+ included do
+ before_action :hook, only: [:show, :retry]
+ before_action :hook_log, only: [:show, :retry]
+
+ respond_to :html
+
+ feature_category :integrations
+ urgency :low, [:retry]
+ end
+
+ def show
+ hide_search_settings
+ end
+
+ def retry
+ execute_hook
+ redirect_to after_retry_redirect_path
+ end
+
+ private
+
+ # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ def hook_log
+ @hook_log ||= hook.web_hook_logs.find(params[:id])
+ end
+ # rubocop:enable Gitlab/ModuleWithInstanceVariables
+
+ def execute_hook
+ result = hook.execute(hook_log.request_data, hook_log.trigger)
+ set_hook_execution_notice(result)
+ end
+
+ def hide_search_settings
+ @hide_search_settings ||= true
+ end
+ end
+end
diff --git a/app/controllers/groups/observability_controller.rb b/app/controllers/groups/observability_controller.rb
new file mode 100644
index 00000000000..5b6503494c4
--- /dev/null
+++ b/app/controllers/groups/observability_controller.rb
@@ -0,0 +1,45 @@
+# 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']
+
+ # 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) | [ObservabilityController.observability_url, "'self'"]
+
+ p.frame_src(*frame_src_values)
+ end
+
+ before_action :check_observability_allowed, only: :index
+
+ def index
+ # Format: https://observe.gitlab.com/-/GROUP_ID
+ @observability_iframe_src = "#{ObservabilityController.observability_url}/-/#{@group.id}"
+
+ # Uncomment below for testing with local GDK
+ # @observability_iframe_src = "#{ObservabilityController.observability_url}/9970?groupId=14485840"
+
+ render layout: 'group', locals: { base_layout: 'layouts/fullscreen' }
+ end
+
+ private
+
+ def self.observability_url
+ return ENV['OVERRIDE_OBSERVABILITY_URL'] if ENV['OVERRIDE_OBSERVABILITY_URL']
+ # TODO Make observability URL configurable https://gitlab.com/gitlab-org/opstrace/opstrace-ui/-/issues/80
+ return "https://staging.observe.gitlab.com" if Gitlab.staging?
+
+ "https://observe.gitlab.com"
+ end
+
+ def check_observability_allowed
+ return render_404 unless self.class.observability_url.present?
+
+ render_404 unless can?(current_user, :read_observability, @group)
+ end
+ end
+end
diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb
index aeb54527c69..652f12e34ba 100644
--- a/app/controllers/groups/runners_controller.rb
+++ b/app/controllers/groups/runners_controller.rb
@@ -5,12 +5,17 @@ class Groups::RunnersController < Groups::ApplicationController
before_action :authorize_admin_group_runners!, only: [:edit, :update, :destroy, :pause, :resume]
before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show]
+ before_action only: [:show] do
+ push_frontend_feature_flag(:enforce_runner_token_expires_at)
+ end
+
feature_category :runner
urgency :low
def index
finder = Ci::RunnersFinder.new(current_user: current_user, params: { group: @group })
@group_runners_limited_count = finder.execute.except(:limit, :offset).page.total_count_with_limit(:all, limit: 1000)
+ @group_runner_registration_token = @group.runners_token if can?(current_user, :register_group_runners, group)
Gitlab::Tracking.event(self.class.name, 'index', user: current_user, namespace: @group)
end
@@ -22,7 +27,7 @@ class Groups::RunnersController < Groups::ApplicationController
end
def update
- if Ci::Runners::UpdateRunnerService.new(@runner).update(runner_params)
+ if Ci::Runners::UpdateRunnerService.new(@runner).execute(runner_params).success?
redirect_to group_runner_path(@group, @runner), notice: _('Runner was successfully updated.')
else
render 'edit'
diff --git a/app/controllers/groups/settings/applications_controller.rb b/app/controllers/groups/settings/applications_controller.rb
index bfe61696e0f..3557d485422 100644
--- a/app/controllers/groups/settings/applications_controller.rb
+++ b/app/controllers/groups/settings/applications_controller.rb
@@ -16,7 +16,7 @@ module Groups
end
def show
- @created = get_created_session
+ @created = get_created_session if Feature.disabled?('hash_oauth_secrets')
end
def edit
@@ -28,9 +28,15 @@ module Groups
if @application.persisted?
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
- set_created_session
+ if Feature.enabled?('hash_oauth_secrets')
- redirect_to group_settings_application_url(@group, @application)
+ @created = true
+ render :show
+ else
+ set_created_session
+
+ redirect_to group_settings_application_url(@group, @application)
+ end
else
set_index_vars
render :index
diff --git a/app/controllers/groups/settings/repository_controller.rb b/app/controllers/groups/settings/repository_controller.rb
index b0431c31179..cb62ea2a543 100644
--- a/app/controllers/groups/settings/repository_controller.rb
+++ b/app/controllers/groups/settings/repository_controller.rb
@@ -5,8 +5,9 @@ module Groups
class RepositoryController < Groups::ApplicationController
layout 'group_settings'
skip_cross_project_access_check :show
- before_action :authorize_create_deploy_token!
- before_action :define_deploy_token_variables
+ before_action :authorize_create_deploy_token!, only: :create_deploy_token
+ before_action :authorize_access!, only: :show
+ before_action :define_deploy_token_variables, if: -> { can?(current_user, :create_deploy_token, @group) }
before_action do
push_frontend_feature_flag(:ajax_new_deploy_token, @group)
end
@@ -16,13 +17,13 @@ module Groups
def create_deploy_token
result = Groups::DeployTokens::CreateService.new(@group, current_user, deploy_token_params).execute
- @new_deploy_token = result[:deploy_token]
if result[:status] == :success
+ @created_deploy_token = result[:deploy_token]
respond_to do |format|
format.json do
# IMPORTANT: It's a security risk to expose the token value more than just once here!
- json = API::Entities::DeployTokenWithToken.represent(@new_deploy_token).as_json
+ json = API::Entities::DeployTokenWithToken.represent(@created_deploy_token).as_json
render json: json, status: result[:http_status]
end
format.html do
@@ -31,6 +32,7 @@ module Groups
end
end
else
+ @new_deploy_token = result[:deploy_token]
respond_to do |format|
format.json { render json: { message: result[:message] }, status: result[:http_status] }
format.html do
@@ -43,6 +45,10 @@ module Groups
private
+ def authorize_access!
+ authorize_admin_group!
+ end
+
def define_deploy_token_variables
@deploy_tokens = @group.deploy_tokens.active
@@ -55,3 +61,5 @@ module Groups
end
end
end
+
+Groups::Settings::RepositoryController.prepend_mod
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 32b187c3260..9316204d89c 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -49,9 +49,9 @@ class GroupsController < Groups::ApplicationController
layout :determine_layout
feature_category :subgroups, [
- :index, :new, :create, :show, :edit, :update,
- :destroy, :details, :transfer, :activity
- ]
+ :index, :new, :create, :show, :edit, :update,
+ :destroy, :details, :transfer, :activity
+ ]
feature_category :team_planning, [:issues, :issues_calendar, :preview_markdown]
feature_category :code_review, [:merge_requests, :unfoldered_environment_names]
@@ -276,6 +276,7 @@ class GroupsController < Groups::ApplicationController
:avatar,
:description,
:emails_disabled,
+ :show_diff_preview_in_email,
:mentions_disabled,
:lfs_enabled,
:name,
diff --git a/app/controllers/health_controller.rb b/app/controllers/health_controller.rb
index 071378f266e..5fac7c0d663 100644
--- a/app/controllers/health_controller.rb
+++ b/app/controllers/health_controller.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
+# rubocop:disable Rails/ApplicationController
class HealthController < ActionController::Base
protect_from_forgery with: :exception, prepend: true
include RequiresWhitelistedMonitoringClient
@@ -11,13 +12,7 @@ class HealthController < ActionController::Base
ALL_CHECKS = [
*CHECKS,
Gitlab::HealthChecks::DbCheck,
- Gitlab::HealthChecks::Redis::RedisCheck,
- Gitlab::HealthChecks::Redis::CacheCheck,
- Gitlab::HealthChecks::Redis::QueuesCheck,
- Gitlab::HealthChecks::Redis::SharedStateCheck,
- Gitlab::HealthChecks::Redis::TraceChunksCheck,
- Gitlab::HealthChecks::Redis::RateLimitingCheck,
- Gitlab::HealthChecks::Redis::SessionsCheck,
+ *Gitlab::HealthChecks::Redis::ALL_INSTANCE_CHECKS,
Gitlab::HealthChecks::GitalyCheck
].freeze
@@ -45,3 +40,4 @@ class HealthController < ActionController::Base
render json: result.json, status: result.http_status
end
end
+# rubocop:enable Rails/ApplicationController
diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb
index 1508531828d..9635e476510 100644
--- a/app/controllers/help_controller.rb
+++ b/app/controllers/help_controller.rb
@@ -12,8 +12,7 @@ class HelpController < ApplicationController
YAML_FRONT_MATTER_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m.freeze
def index
- # Remove YAML frontmatter so that it doesn't look weird
- @help_index = File.read(path_to_doc('index.md')).sub(YAML_FRONT_MATTER_REGEXP, '')
+ @help_index = get_markdown_without_frontmatter(path_to_doc('index.md'))
# Prefix Markdown links with `help/` unless they are external links.
# '//' not necessarily part of URL, e.g., mailto:mail@example.com
@@ -59,8 +58,25 @@ class HelpController < ApplicationController
@instance_configuration = InstanceConfiguration.new
end
+ def drawers
+ @clean_path = Rack::Utils.clean_path_info(params[:markdown_file])
+ @path = path_to_doc("#{@clean_path}.md")
+
+ if File.exist?(@path)
+ render :drawers, formats: :html, layout: false
+ else
+ head :not_found
+ end
+ end
+
private
+ # Remove YAML frontmatter so that it doesn't look weird
+ helper_method :get_markdown_without_frontmatter
+ def get_markdown_without_frontmatter(path)
+ File.read(path).gsub(YAML_FRONT_MATTER_REGEXP, '')
+ end
+
def redirect_to_documentation_website?
Gitlab::UrlSanitizer.valid_web?(documentation_url)
end
@@ -100,8 +116,7 @@ class HelpController < ApplicationController
path = path_to_doc("#{@path}.md")
if File.exist?(path)
- # Remove YAML frontmatter so that it doesn't look weird
- @markdown = File.read(path).gsub(YAML_FRONT_MATTER_REGEXP, '')
+ @markdown = get_markdown_without_frontmatter(path)
render :show, formats: :html
else
diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb
index 9fcb8385312..58a985cbc46 100644
--- a/app/controllers/ide_controller.rb
+++ b/app/controllers/ide_controller.rb
@@ -13,6 +13,7 @@ class IdeController < ApplicationController
push_frontend_feature_flag(:build_service_proxy)
push_frontend_feature_flag(:schema_linting)
push_frontend_feature_flag(:reject_unsigned_commits_by_gitlab)
+ push_frontend_feature_flag(:vscode_web_ide, current_user)
define_index_vars
end
diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb
index 9cc58ce542c..8a3e6809736 100644
--- a/app/controllers/import/github_controller.rb
+++ b/app/controllers/import/github_controller.rb
@@ -130,7 +130,7 @@ class Import::GithubController < Import::BaseController
if sanitized_filter_param
client.search_repos_by_name(sanitized_filter_param, pagination_options)[:items]
else
- client.octokit.repos(nil, pagination_options)
+ client.repos(pagination_options)
end
else
filtered(client.repos)
diff --git a/app/controllers/jira_connect/oauth_callbacks_controller.rb b/app/controllers/jira_connect/oauth_callbacks_controller.rb
index f603a563402..e1a47a12b6d 100644
--- a/app/controllers/jira_connect/oauth_callbacks_controller.rb
+++ b/app/controllers/jira_connect/oauth_callbacks_controller.rb
@@ -7,5 +7,7 @@
class JiraConnect::OauthCallbacksController < ApplicationController
feature_category :integrations
+ skip_before_action :authenticate_user!
+
def index; end
end
diff --git a/app/controllers/jira_connect/subscriptions_controller.rb b/app/controllers/jira_connect/subscriptions_controller.rb
index 623113f8413..9305f46c39e 100644
--- a/app/controllers/jira_connect/subscriptions_controller.rb
+++ b/app/controllers/jira_connect/subscriptions_controller.rb
@@ -64,10 +64,12 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController
private
def allow_self_managed_content_security_policy
+ return unless Feature.enabled?(:jira_connect_oauth_self_managed)
+
return unless current_jira_installation.instance_url?
request.content_security_policy.directives['connect-src'] ||= []
- request.content_security_policy.directives['connect-src'] << Gitlab::Utils.append_path(current_jira_installation.instance_url, '/-/jira_connect/oauth_application_ids')
+ request.content_security_policy.directives['connect-src'].concat(allowed_instance_connect_src)
end
def create_service
@@ -77,4 +79,11 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController
def allow_rendering_in_iframe
response.headers.delete('X-Frame-Options')
end
+
+ def allowed_instance_connect_src
+ [
+ Gitlab::Utils.append_path(current_jira_installation.instance_url, '/-/jira_connect/'),
+ Gitlab::Utils.append_path(current_jira_installation.instance_url, '/api/')
+ ]
+ end
end
diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb
index 84f5632854b..7211eebdb4b 100644
--- a/app/controllers/jwt_controller.rb
+++ b/app/controllers/jwt_controller.rb
@@ -78,7 +78,11 @@ class JwtController < ApplicationController
end
def additional_params
- { scopes: scopes_param, deploy_token: @authentication_result.deploy_token }.compact
+ {
+ scopes: scopes_param,
+ deploy_token: @authentication_result.deploy_token,
+ auth_type: @authentication_result.type
+ }.compact
end
# We have to parse scope here, because Docker Client does not send an array of scopes,
diff --git a/app/controllers/metrics_controller.rb b/app/controllers/metrics_controller.rb
index a0c307a0a03..bfd6181a940 100644
--- a/app/controllers/metrics_controller.rb
+++ b/app/controllers/metrics_controller.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
+# rubocop:disable Rails/ApplicationController
class MetricsController < ActionController::Base
include RequiresWhitelistedMonitoringClient
@@ -34,3 +35,4 @@ class MetricsController < ActionController::Base
)
end
end
+# rubocop:enable Rails/ApplicationController
diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb
index a996bad3fac..ff466fd5fbb 100644
--- a/app/controllers/oauth/applications_controller.rb
+++ b/app/controllers/oauth/applications_controller.rb
@@ -25,7 +25,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
end
def show
- @created = get_created_session
+ @created = get_created_session if Feature.disabled?('hash_oauth_secrets')
end
def create
@@ -34,9 +34,14 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
if @application.persisted?
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
- set_created_session
+ if Feature.enabled?('hash_oauth_secrets')
+ @created = true
+ render :show
+ else
+ set_created_session
- redirect_to oauth_application_url(@application)
+ redirect_to oauth_application_url(@application)
+ end
else
set_index_vars
render :index
diff --git a/app/controllers/profiles/personal_access_tokens_controller.rb b/app/controllers/profiles/personal_access_tokens_controller.rb
index 07d786ab060..8ed67c26f19 100644
--- a/app/controllers/profiles/personal_access_tokens_controller.rb
+++ b/app/controllers/profiles/personal_access_tokens_controller.rb
@@ -65,7 +65,7 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
add_pagination_headers(tokens)
end
- ::API::Entities::PersonalAccessTokenWithDetails.represent(tokens)
+ ::PersonalAccessTokenSerializer.new.represent(tokens)
end
def add_pagination_headers(relation)
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index dd1ac526b89..e3704b77adc 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -137,7 +137,7 @@ class ProfilesController < Profiles::ApplicationController
:pronouns,
:pronunciation,
:validation_password,
- status: [:emoji, :message, :availability]
+ status: [:emoji, :message, :availability, :clear_status_after]
]
end
diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb
index 5bfda526fb0..2a20c67a23d 100644
--- a/app/controllers/projects/blame_controller.rb
+++ b/app/controllers/projects/blame_controller.rb
@@ -23,11 +23,10 @@ class Projects::BlameController < Projects::ApplicationController
environment_params[:find_latest] = true
@environment = ::Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last
- blame_service = Projects::BlameService.new(@blob, @commit, params.permit(:page))
+ blame_service = Projects::BlameService.new(@blob, @commit, params.permit(:page, :no_pagination))
@blame = Gitlab::View::Presenter::Factory.new(blame_service.blame, project: @project, path: @path, page: blame_service.page).fabricate!
-
- render locals: { blame_pagination: blame_service.pagination }
+ @blame_pagination = blame_service.pagination
end
end
diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb
index 6160dafb177..63c1378ad11 100644
--- a/app/controllers/projects/cycle_analytics_controller.rb
+++ b/app/controllers/projects/cycle_analytics_controller.rb
@@ -5,13 +5,17 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController
include ActionView::Helpers::TextHelper
include CycleAnalyticsParams
include GracefulTimeoutHandling
- include RedisTracking
+ include ProductAnalyticsTracking
extend ::Gitlab::Utils::Override
before_action :authorize_read_cycle_analytics!
before_action :load_value_stream, only: :show
- track_redis_hll_event :show, name: 'p_analytics_valuestream'
+ track_custom_event :show,
+ name: 'p_analytics_valuestream',
+ action: 'perform_analytics_usage_action',
+ label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly',
+ destinations: %i[redis_hll snowplow]
feature_category :planning_analytics
urgency :low
@@ -54,4 +58,12 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController
permissions: @cycle_analytics.permissions(user: current_user)
}
end
+
+ def tracking_namespace_source
+ project.namespace
+ end
+
+ def tracking_project_source
+ project
+ end
end
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index 7ef9fd9daed..4f037cc843e 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -5,7 +5,10 @@ class Projects::EnvironmentsController < Projects::ApplicationController
# into app/controllers/projects/metrics_dashboard_controller.rb
# See https://gitlab.com/gitlab-org/gitlab/-/issues/226002 for more details.
+ MIN_SEARCH_LENGTH = 3
+
include MetricsDashboard
+ include ProductAnalyticsTracking
layout 'project'
@@ -26,6 +29,18 @@ class Projects::EnvironmentsController < Projects::ApplicationController
before_action :expire_etag_cache, only: [:index], unless: -> { request.format.json? }
after_action :expire_etag_cache, only: [:cancel_auto_stop]
+ track_event :index,
+ :folder,
+ :show,
+ :new,
+ :edit,
+ :create,
+ :update,
+ :stop,
+ :cancel_auto_stop,
+ :terminal,
+ name: 'users_visiting_environments_pages'
+
feature_category :continuous_delivery
urgency :low
@@ -35,12 +50,10 @@ class Projects::EnvironmentsController < Projects::ApplicationController
respond_to do |format|
format.html
format.json do
- @environments = project.environments
- .with_state(params[:scope] || :available)
+ @environments = search_environments.with_state(params[:scope] || :available)
+ environments_count_by_state = search_environments.count_by_state
Gitlab::PollingInterval.set_header(response, interval: 3_000)
- environments_count_by_state = project.environments.count_by_state
-
render json: {
environments: serialize_environments(request, response, params[:nested]),
review_app: serialize_review_app,
@@ -59,7 +72,8 @@ class Projects::EnvironmentsController < Projects::ApplicationController
respond_to do |format|
format.html
format.json do
- folder_environments = project.environments.where(environment_type: params[:id])
+ folder_environments = search_environments(type: params[:id])
+
@environments = folder_environments.with_state(params[:scope] || :available)
.order(:name)
@@ -236,6 +250,16 @@ class Projects::EnvironmentsController < Projects::ApplicationController
@environment ||= project.environments.find(params[:id])
end
+ def search_environments(type: nil)
+ search = params[:search] if params[:search] && params[:search].length >= MIN_SEARCH_LENGTH
+
+ @search_environments ||=
+ Environments::EnvironmentsFinder.new(project,
+ current_user,
+ type: type,
+ search: search).execute
+ end
+
def metrics_params
params.require([:start, :end])
end
diff --git a/app/controllers/projects/google_cloud/base_controller.rb b/app/controllers/projects/google_cloud/base_controller.rb
index d1eb86c5e49..dfb73821b0f 100644
--- a/app/controllers/projects/google_cloud/base_controller.rb
+++ b/app/controllers/projects/google_cloud/base_controller.rb
@@ -12,7 +12,7 @@ class Projects::GoogleCloud::BaseController < Projects::ApplicationController
def admin_project_google_cloud!
unless can?(current_user, :admin_project_google_cloud, project)
- track_event('admin_project_google_cloud!', 'error_access_denied', 'invalid_user')
+ track_event(:error_invalid_user)
access_denied!
end
end
@@ -20,11 +20,7 @@ class Projects::GoogleCloud::BaseController < Projects::ApplicationController
def google_oauth2_enabled!
config = Gitlab::Auth::OAuth::Provider.config_for('google_oauth2')
if config.app_id.blank? || config.app_secret.blank?
- track_event(
- 'google_oauth2_enabled!',
- 'error_access_denied',
- { reason: 'google_oauth2_not_configured', config: config }
- )
+ track_event(:error_google_oauth2_not_enabled)
access_denied! 'This GitLab instance not configured for Google Oauth2.'
end
end
@@ -35,7 +31,7 @@ class Projects::GoogleCloud::BaseController < Projects::ApplicationController
enabled_for_project = Feature.enabled?(:incubation_5mp_google_cloud, project)
feature_is_enabled = enabled_for_user || enabled_for_group || enabled_for_project
unless feature_is_enabled
- track_event('feature_flag_enabled!', 'error_access_denied', 'feature_flag_not_enabled')
+ track_event(:error_feature_flag_not_enabled)
access_denied!
end
end
@@ -69,16 +65,14 @@ class Projects::GoogleCloud::BaseController < Projects::ApplicationController
session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at]
end
- def track_event(action, label, property)
- options = { label: label, project: project, user: current_user }
-
- if property.is_a?(String)
- options[:property] = property
- else
- options[:extra] = property
- end
-
- Gitlab::Tracking.event('Projects::GoogleCloud', action, **options)
+ def track_event(action, label = nil)
+ Gitlab::Tracking.event(
+ self.class.name,
+ action.to_s,
+ label: label,
+ project: project,
+ user: current_user
+ )
end
def gcp_projects
diff --git a/app/controllers/projects/google_cloud/configuration_controller.rb b/app/controllers/projects/google_cloud/configuration_controller.rb
index 8d252c35031..06a6674d578 100644
--- a/app/controllers/projects/google_cloud/configuration_controller.rb
+++ b/app/controllers/projects/google_cloud/configuration_controller.rb
@@ -16,7 +16,7 @@ module Projects
revokeOauthUrl: revoke_oauth_url
}
@js_data = js_data.to_json
- track_event('configuration#index', 'success', js_data)
+ track_event(:render_page)
end
private
diff --git a/app/controllers/projects/google_cloud/databases_controller.rb b/app/controllers/projects/google_cloud/databases_controller.rb
index 7b1cf6e5ce1..8f7554f248b 100644
--- a/app/controllers/projects/google_cloud/databases_controller.rb
+++ b/app/controllers/projects/google_cloud/databases_controller.rb
@@ -3,14 +3,139 @@
module Projects
module GoogleCloud
class DatabasesController < Projects::GoogleCloud::BaseController
+ before_action :validate_gcp_token!
+ before_action :validate_product, only: :new
+
def index
js_data = {
configurationUrl: project_google_cloud_configuration_path(project),
deploymentsUrl: project_google_cloud_deployments_path(project),
- databasesUrl: project_google_cloud_databases_path(project)
+ databasesUrl: project_google_cloud_databases_path(project),
+ cloudsqlPostgresUrl: new_project_google_cloud_database_path(project, :postgres),
+ cloudsqlMysqlUrl: new_project_google_cloud_database_path(project, :mysql),
+ cloudsqlSqlserverUrl: new_project_google_cloud_database_path(project, :sqlserver),
+ cloudsqlInstances: ::GoogleCloud::GetCloudsqlInstancesService.new(project).execute,
+ emptyIllustrationUrl: ActionController::Base.helpers.image_path('illustrations/pipelines_empty.svg')
}
@js_data = js_data.to_json
- track_event('databases#index', 'success', js_data)
+
+ track_event(:render_page)
+ end
+
+ def new
+ product = permitted_params[:product].to_sym
+
+ @title = title(product)
+
+ @js_data = {
+ gcpProjects: gcp_projects,
+ refs: refs,
+ cancelPath: project_google_cloud_databases_path(project),
+ formTitle: form_title(product),
+ formDescription: description(product),
+ databaseVersions: Projects::GoogleCloud::CloudsqlHelper::VERSIONS[product],
+ tiers: Projects::GoogleCloud::CloudsqlHelper::TIERS
+ }.to_json
+
+ track_event(:render_form)
+ render template: 'projects/google_cloud/databases/cloudsql_form', formats: :html
+ end
+
+ def create
+ enable_response = ::GoogleCloud::EnableCloudsqlService
+ .new(project, current_user, enable_service_params)
+ .execute
+
+ if enable_response[:status] == :error
+ track_event(:error_enable_cloudsql_services)
+ flash[:error] = error_message(enable_response[:message])
+ else
+ permitted_params = params.permit(:gcp_project, :ref, :database_version, :tier)
+ create_response = ::GoogleCloud::CreateCloudsqlInstanceService
+ .new(project, current_user, create_service_params(permitted_params))
+ .execute
+
+ if create_response[:status] == :error
+ track_event(:error_create_cloudsql_instance)
+ flash[:warning] = error_message(create_response[:message])
+ else
+ track_event(:create_cloudsql_instance, permitted_params.to_s)
+ flash[:notice] = success_message
+ end
+ end
+
+ redirect_to project_google_cloud_databases_path(project)
+ end
+
+ private
+
+ def enable_service_params
+ { google_oauth2_token: token_in_session }
+ end
+
+ def create_service_params(permitted_params)
+ {
+ google_oauth2_token: token_in_session,
+ gcp_project_id: permitted_params[:gcp_project],
+ environment_name: permitted_params[:ref],
+ database_version: permitted_params[:database_version],
+ tier: permitted_params[:tier]
+ }
+ end
+
+ def error_message(message)
+ format(s_("CloudSeed|Google Cloud Error - %{message}"), message: message)
+ end
+
+ def success_message
+ s_('CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes.')
+ end
+
+ def validate_product
+ not_found unless permitted_params[:product].in?(%w[postgres mysql sqlserver])
+ end
+
+ def permitted_params
+ params.permit(:product)
+ end
+
+ def title(product)
+ case product
+ when :postgres
+ s_('CloudSeed|Create Postgres Instance')
+ when :mysql
+ s_('CloudSeed|Create MySQL Instance')
+ else
+ s_('CloudSeed|Create MySQL Instance')
+ end
+ end
+
+ def form_title(product)
+ case product
+ when :postgres
+ s_('CloudSeed|Cloud SQL for Postgres')
+ when :mysql
+ s_('CloudSeed|Cloud SQL for MySQL')
+ else
+ s_('CloudSeed|Cloud SQL for SQL Server')
+ end
+ end
+
+ def description(product)
+ case product
+ when :postgres
+ s_('CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. '\
+ 'Google handles replication, patch management, and database management '\
+ 'to ensure availability and performance.')
+ when :mysql
+ s_('Cloud SQL instances are fully managed, relational MySQL databases. '\
+ 'Google handles replication, patch management, and database management '\
+ 'to ensure availability and performance.')
+ else
+ s_('Cloud SQL instances are fully managed, relational SQL Server databases. ' \
+ 'Google handles replication, patch management, and database management ' \
+ 'to ensure availability and performance.')
+ end
end
end
end
diff --git a/app/controllers/projects/google_cloud/deployments_controller.rb b/app/controllers/projects/google_cloud/deployments_controller.rb
index 1ac4697a63f..f6cc8d5eafb 100644
--- a/app/controllers/projects/google_cloud/deployments_controller.rb
+++ b/app/controllers/projects/google_cloud/deployments_controller.rb
@@ -12,7 +12,7 @@ class Projects::GoogleCloud::DeploymentsController < Projects::GoogleCloud::Base
enableCloudStorageUrl: project_google_cloud_deployments_cloud_storage_path(project)
}
@js_data = js_data.to_json
- track_event('deployments#index', 'success', js_data)
+ track_event(:render_page)
end
def cloud_run
@@ -21,7 +21,7 @@ class Projects::GoogleCloud::DeploymentsController < Projects::GoogleCloud::Base
.new(project, current_user, params).execute
if enable_cloud_run_response[:status] == :error
- track_event('deployments#cloud_run', 'error_enable_cloud_run', enable_cloud_run_response)
+ track_event(:error_enable_services)
flash[:error] = enable_cloud_run_response[:message]
redirect_to project_google_cloud_deployments_path(project)
else
@@ -30,17 +30,17 @@ class Projects::GoogleCloud::DeploymentsController < Projects::GoogleCloud::Base
.new(project, current_user, params).execute
if generate_pipeline_response[:status] == :error
- track_event('deployments#cloud_run', 'error_generate_pipeline', generate_pipeline_response)
+ track_event(:error_generate_cloudrun_pipeline)
flash[:error] = 'Failed to generate pipeline'
redirect_to project_google_cloud_deployments_path(project)
else
cloud_run_mr_params = cloud_run_mr_params(generate_pipeline_response[:branch_name])
- track_event('deployments#cloud_run', 'success', cloud_run_mr_params)
+ track_event(:generate_cloudrun_pipeline)
redirect_to project_new_merge_request_path(project, merge_request: cloud_run_mr_params)
end
end
- rescue Google::Apis::ClientError, Google::Apis::ServerError, Google::Apis::AuthorizationError => e
- track_event('deployments#cloud_run', 'error_gcp', e)
+ rescue Google::Apis::Error => e
+ track_event(:error_google_api)
flash[:warning] = _('Google Cloud Error - %{error}') % { error: e }
redirect_to project_google_cloud_deployments_path(project)
end
diff --git a/app/controllers/projects/google_cloud/gcp_regions_controller.rb b/app/controllers/projects/google_cloud/gcp_regions_controller.rb
index 39f33624804..2f0bc05030f 100644
--- a/app/controllers/projects/google_cloud/gcp_regions_controller.rb
+++ b/app/controllers/projects/google_cloud/gcp_regions_controller.rb
@@ -15,13 +15,13 @@ class Projects::GoogleCloud::GcpRegionsController < Projects::GoogleCloud::BaseC
cancelPath: project_google_cloud_configuration_path(project)
}
@js_data = js_data.to_json
- track_event('gcp_regions#index', 'success', js_data)
+ track_event(:render_form)
end
def create
permitted_params = params.permit(:ref, :gcp_region)
- response = GoogleCloud::GcpRegionAddOrReplaceService.new(project).execute(permitted_params[:ref], permitted_params[:gcp_region])
- track_event('gcp_regions#create', 'success', response)
+ GoogleCloud::GcpRegionAddOrReplaceService.new(project).execute(permitted_params[:ref], permitted_params[:gcp_region])
+ track_event(:configure_region)
redirect_to project_google_cloud_configuration_path(project), notice: _('GCP region configured')
end
end
diff --git a/app/controllers/projects/google_cloud/revoke_oauth_controller.rb b/app/controllers/projects/google_cloud/revoke_oauth_controller.rb
index 1a9a2daf4f2..dbf91806722 100644
--- a/app/controllers/projects/google_cloud/revoke_oauth_controller.rb
+++ b/app/controllers/projects/google_cloud/revoke_oauth_controller.rb
@@ -9,10 +9,10 @@ class Projects::GoogleCloud::RevokeOauthController < Projects::GoogleCloud::Base
if response.success?
redirect_message = { notice: s_('GoogleCloud|Google OAuth2 token revocation requested') }
- track_event('revoke_oauth#create', 'success', response.to_json)
+ track_event(:revoke_oauth)
else
redirect_message = { alert: s_('GoogleCloud|Google OAuth2 token revocation request failed') }
- track_event('revoke_oauth#create', 'error', response.to_json)
+ track_event(:error)
end
session.delete(GoogleApi::CloudPlatform::Client.session_key_for_token)
diff --git a/app/controllers/projects/google_cloud/service_accounts_controller.rb b/app/controllers/projects/google_cloud/service_accounts_controller.rb
index 7f25054177e..89d624764df 100644
--- a/app/controllers/projects/google_cloud/service_accounts_controller.rb
+++ b/app/controllers/projects/google_cloud/service_accounts_controller.rb
@@ -5,7 +5,7 @@ class Projects::GoogleCloud::ServiceAccountsController < Projects::GoogleCloud::
def index
if gcp_projects.empty?
- track_event('service_accounts#index', 'error_form', 'no_gcp_projects')
+ track_event(:error_no_gcp_projects)
flash[:warning] = _('No Google Cloud projects - You need at least one Google Cloud project')
redirect_to project_google_cloud_configuration_path(project)
else
@@ -16,10 +16,10 @@ class Projects::GoogleCloud::ServiceAccountsController < Projects::GoogleCloud::
}
@js_data = js_data.to_json
- track_event('service_accounts#index', 'success', js_data)
+ track_event(:render_form)
end
- rescue Google::Apis::ClientError, Google::Apis::ServerError, Google::Apis::AuthorizationError => e
- track_event('service_accounts#index', 'error_gcp', e)
+ rescue Google::Apis::Error => e
+ track_event(:error_google_api)
flash[:warning] = _('Google Cloud Error - %{error}') % { error: e }
redirect_to project_google_cloud_configuration_path(project)
end
@@ -35,10 +35,10 @@ class Projects::GoogleCloud::ServiceAccountsController < Projects::GoogleCloud::
environment_name: permitted_params[:ref]
).execute
- track_event('service_accounts#create', 'success', response)
+ track_event(:create_service_account)
redirect_to project_google_cloud_configuration_path(project), notice: response.message
- rescue Google::Apis::ClientError, Google::Apis::ServerError, Google::Apis::AuthorizationError => e
- track_event('service_accounts#create', 'error_gcp', e)
+ rescue Google::Apis::Error => e
+ track_event(:error_google_api)
flash[:warning] = _('Google Cloud Error - %{error}') % { error: e }
redirect_to project_google_cloud_configuration_path(project)
end
diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb
index 63309cce1e5..47557133ac8 100644
--- a/app/controllers/projects/graphs_controller.rb
+++ b/app/controllers/projects/graphs_controller.rb
@@ -2,14 +2,18 @@
class Projects::GraphsController < Projects::ApplicationController
include ExtractsPath
- include RedisTracking
+ include ProductAnalyticsTracking
# Authorize
before_action :require_non_empty_project
before_action :assign_ref_vars
before_action :authorize_read_repository_graphs!
- track_redis_hll_event :charts, name: 'p_analytics_repo'
+ track_custom_event :charts,
+ name: 'p_analytics_repo',
+ action: 'perform_analytics_usage_action',
+ label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly',
+ destinations: %i[redis_hll snowplow]
feature_category :source_code_management, [:show, :commits, :languages, :charts]
urgency :low, [:show]
@@ -102,6 +106,14 @@ class Projects::GraphsController < Projects::ApplicationController
render json: @log.to_json
end
+
+ def tracking_namespace_source
+ project.namespace
+ end
+
+ def tracking_project_source
+ project
+ end
end
Projects::GraphsController.prepend_mod
diff --git a/app/controllers/projects/hook_logs_controller.rb b/app/controllers/projects/hook_logs_controller.rb
index 0ca3d71f728..3ab4c34737d 100644
--- a/app/controllers/projects/hook_logs_controller.rb
+++ b/app/controllers/projects/hook_logs_controller.rb
@@ -1,40 +1,19 @@
# frozen_string_literal: true
class Projects::HookLogsController < Projects::ApplicationController
- include ::Integrations::HooksExecution
-
before_action :authorize_admin_project!
- before_action :hook, only: [:show, :retry]
- before_action :hook_log, only: [:show, :retry]
-
- respond_to :html
+ include WebHooks::HookLogActions
layout 'project_settings'
- feature_category :integrations
- urgency :low, [:retry]
-
- def show
- end
-
- def retry
- execute_hook
- redirect_to edit_project_hook_path(@project, @hook)
- end
-
private
- def execute_hook
- result = hook.execute(hook_log.request_data, hook_log.trigger)
- set_hook_execution_notice(result)
- end
-
def hook
@hook ||= @project.hooks.find(params[:hook_id])
end
- def hook_log
- @hook_log ||= hook.web_hook_logs.find(params[:id])
+ def after_retry_redirect_path
+ edit_project_hook_path(@project, hook)
end
end
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index 50f388324f1..22b6bf6faf0 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class Projects::HooksController < Projects::ApplicationController
- include ::Integrations::HooksExecution
+ include ::WebHooks::HookActions
# Authorize
before_action :authorize_admin_project!
@@ -35,7 +35,7 @@ class Projects::HooksController < Projects::ApplicationController
end
def hook_logs
- @hook_logs ||= hook.web_hook_logs.recent.page(params[:page])
+ @hook_logs ||= hook.web_hook_logs.recent.page(params[:page]).without_count
end
def trigger_values
diff --git a/app/controllers/projects/incidents_controller.rb b/app/controllers/projects/incidents_controller.rb
index 36b52533e78..cbf0c756e1e 100644
--- a/app/controllers/projects/incidents_controller.rb
+++ b/app/controllers/projects/incidents_controller.rb
@@ -11,6 +11,7 @@ class Projects::IncidentsController < Projects::ApplicationController
push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?)
push_force_frontend_feature_flag(:work_items_mvc_2, @project&.work_items_mvc_2_feature_flag_enabled?)
push_frontend_feature_flag(:work_items_hierarchy, @project)
+ push_frontend_feature_flag(:remove_user_attributes_projects, @project)
end
feature_category :incident_management
@@ -28,7 +29,7 @@ class Projects::IncidentsController < Projects::ApplicationController
.inc_relations_for_view
.iid_in(params[:id])
.without_order
- .first
+ .take # rubocop:disable CodeReuse/ActiveRecord
end
end
diff --git a/app/controllers/projects/integrations/shimos_controller.rb b/app/controllers/projects/integrations/shimos_controller.rb
index 827dbb8f3f9..6c8313d0805 100644
--- a/app/controllers/projects/integrations/shimos_controller.rb
+++ b/app/controllers/projects/integrations/shimos_controller.rb
@@ -12,7 +12,7 @@ module Projects
private
def ensure_renderable
- render_404 unless Feature.enabled?(:shimo_integration, project) && project.has_shimo? && project.shimo_integration&.render?
+ render_404 unless project.has_shimo? && project.shimo_integration&.render?
end
end
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index d19db2b11ab..800a7df2566 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -42,6 +42,7 @@ class Projects::IssuesController < Projects::ApplicationController
before_action do
push_frontend_feature_flag(:incident_timeline, project)
+ push_frontend_feature_flag(:remove_user_attributes_projects, project)
end
before_action only: [:index, :show] do
@@ -53,6 +54,7 @@ class Projects::IssuesController < Projects::ApplicationController
push_frontend_feature_flag(:realtime_labels, project)
push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?)
push_frontend_feature_flag(:work_items_hierarchy, project)
+ push_frontend_feature_flag(:epic_widget_edit_confirmation, project)
push_force_frontend_feature_flag(:work_items_create_from_markdown, project&.work_items_create_from_markdown_feature_flag_enabled?)
end
@@ -63,19 +65,19 @@ class Projects::IssuesController < Projects::ApplicationController
alias_method :designs, :show
feature_category :team_planning, [
- :index, :calendar, :show, :new, :create, :edit, :update,
- :destroy, :move, :reorder, :designs, :toggle_subscription,
- :discussions, :bulk_update, :realtime_changes,
- :toggle_award_emoji, :mark_as_spam, :related_branches,
- :can_create_branch, :create_merge_request
- ]
+ :index, :calendar, :show, :new, :create, :edit, :update,
+ :destroy, :move, :reorder, :designs, :toggle_subscription,
+ :discussions, :bulk_update, :realtime_changes,
+ :toggle_award_emoji, :mark_as_spam, :related_branches,
+ :can_create_branch, :create_merge_request
+ ]
urgency :low, [
- :index, :calendar, :show, :new, :create, :edit, :update,
- :destroy, :move, :reorder, :designs, :toggle_subscription,
- :discussions, :bulk_update, :realtime_changes,
- :toggle_award_emoji, :mark_as_spam, :related_branches,
- :can_create_branch, :create_merge_request
- ]
+ :index, :calendar, :show, :new, :create, :edit, :update,
+ :destroy, :move, :reorder, :designs, :toggle_subscription,
+ :discussions, :bulk_update, :realtime_changes,
+ :toggle_award_emoji, :mark_as_spam, :related_branches,
+ :can_create_branch, :create_merge_request
+ ]
feature_category :service_desk, [:service_desk]
urgency :low, [:service_desk]
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index 7878ace5015..557ac566733 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -20,6 +20,9 @@ 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'
@@ -120,11 +123,13 @@ class Projects::JobsController < Projects::ApplicationController
end
def erase
- if @build.erase(erased_by: current_user)
+ service_response = Ci::BuildEraseService.new(@build, current_user).execute
+
+ if service_response.success?
redirect_to project_job_path(project, @build),
notice: _("Job has been successfully erased!")
else
- respond_422
+ head service_response.http_status
end
end
diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb
index 279fd4c457e..a68c2ffa06d 100644
--- a/app/controllers/projects/merge_requests/diffs_controller.rb
+++ b/app/controllers/projects/merge_requests/diffs_controller.rb
@@ -29,6 +29,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
render_diffs
end
+ # rubocop: disable Metrics/AbcSize
def diffs_batch
diff_options_hash = diff_options
diff_options_hash[:paths] = params[:paths] if params[:paths]
@@ -61,21 +62,11 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
options[:allow_tree_conflicts]
]
- if Feature.enabled?(:etag_merge_request_diff_batches, @merge_request.project)
- return unless stale?(etag: [cache_context + diff_options_hash.fetch(:paths, []), diffs])
- end
+ return unless stale?(etag: [cache_context + diff_options_hash.fetch(:paths, []), diffs])
- if diff_options_hash[:paths].blank?
- render_cached(
- diffs,
- with: PaginatedDiffSerializer.new(current_user: current_user),
- cache_context: -> (_) { [Digest::SHA256.hexdigest(cache_context.to_s)] },
- **options
- )
- else
- render json: PaginatedDiffSerializer.new(current_user: current_user).represent(diffs, options)
- end
+ render json: PaginatedDiffSerializer.new(current_user: current_user).represent(diffs, options)
end
+ # rubocop: enable Metrics/AbcSize
def diffs_metadata
diffs = @compare.diffs(diff_options)
diff --git a/app/controllers/projects/merge_requests/drafts_controller.rb b/app/controllers/projects/merge_requests/drafts_controller.rb
index ff6b6bfaf27..74bb3ad1a63 100644
--- a/app/controllers/projects/merge_requests/drafts_controller.rb
+++ b/app/controllers/projects/merge_requests/drafts_controller.rb
@@ -49,8 +49,24 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli
def publish
result = DraftNotes::PublishService.new(merge_request, current_user).execute(draft_note(allow_nil: true))
- if Feature.enabled?(:mr_review_submit_comment, @project) && create_note_params[:note]
- Notes::CreateService.new(@project, current_user, create_note_params).execute
+ if Feature.enabled?(:mr_review_submit_comment, @project)
+ if create_note_params[:note]
+ ::Notes::CreateService.new(@project, current_user, create_note_params).execute
+
+ merge_request_activity_counter.track_submit_review_comment(user: current_user)
+ end
+
+ if Gitlab::Utils.to_boolean(approve_params[:approve])
+ unless merge_request.approved_by?(current_user)
+ success = ::MergeRequests::ApprovalService.new(project: @project, current_user: current_user, params: approve_params).execute(merge_request)
+
+ unless success
+ return render json: { message: _('An error occurred while approving, please try again.') }, status: :internal_server_error
+ end
+ end
+
+ merge_request_activity_counter.track_submit_review_approve(user: current_user)
+ end
end
if result[:status] == :success
@@ -115,6 +131,10 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli
end
end
+ def approve_params
+ params.permit(:approve)
+ end
+
def prepare_notes_for_rendering(notes)
return [] unless notes
@@ -147,4 +167,10 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli
def authorize_create_note!
access_denied! unless can?(current_user, :create_note, merge_request)
end
+
+ def merge_request_activity_counter
+ Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter
+ end
end
+
+Projects::MergeRequests::DraftsController.prepend_mod
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 870c57fd6f3..5a212e9a152 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -45,6 +45,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:paginated_mr_discussions, project)
push_frontend_feature_flag(:mr_review_submit_comment, project)
push_frontend_feature_flag(:mr_experience_survey, project)
+ push_frontend_feature_flag(:remove_user_attributes_projects, @project)
end
before_action do
@@ -56,11 +57,11 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
after_action :log_merge_request_show, only: [:show]
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
- ]
+ :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
+ ]
feature_category :code_testing, [:test_reports, :coverage_reports]
feature_category :code_quality, [:codequality_reports, :codequality_mr_diff_reports]
@@ -219,7 +220,13 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
def context_commits
# Get commits from repository
# or from cache if already merged
- commits = ContextCommitsFinder.new(project, @merge_request, { search: params[:search], limit: params[:limit], offset: params[:offset] }).execute
+ commits = ContextCommitsFinder.new(project, @merge_request, {
+ search: params[:search],
+ author: params[:author],
+ committed_before: convert_date_to_epoch(params[:committed_before]),
+ committed_after: convert_date_to_epoch(params[:committed_after]),
+ limit: params[:limit]
+ }).execute
render json: CommitEntity.represent(commits, { type: :full, request: merge_request })
end
@@ -552,6 +559,11 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
diffs_metadata_project_json_merge_request_path(project, merge_request, 'json', params)
end
+
+ def convert_date_to_epoch(date)
+ Date.strptime(date, "%Y-%m-%d")&.to_time&.to_i if date
+ rescue Date::Error, TypeError
+ end
end
Projects::MergeRequestsController.prepend_mod_with('Projects::MergeRequestsController')
diff --git a/app/controllers/projects/packages/package_files_controller.rb b/app/controllers/projects/packages/package_files_controller.rb
index 32aadb4fcf4..1aa91ee1189 100644
--- a/app/controllers/projects/packages/package_files_controller.rb
+++ b/app/controllers/projects/packages/package_files_controller.rb
@@ -11,6 +11,7 @@ module Projects
def download
package_file = project.package_files.find(params[:id])
+ package_file.package.touch_last_downloaded_at
send_upload(package_file.file, attachment: package_file.file_name)
end
end
diff --git a/app/controllers/projects/pipelines/tests_controller.rb b/app/controllers/projects/pipelines/tests_controller.rb
index 8ac370b1bd4..d77cf095a4f 100644
--- a/app/controllers/projects/pipelines/tests_controller.rb
+++ b/app/controllers/projects/pipelines/tests_controller.rb
@@ -51,7 +51,8 @@ module Projects
def test_suite
suite = builds.sum do |build|
- build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
+ test_report = build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
+ test_report.get_suite(build.test_suite_name)
end
Gitlab::Ci::Reports::TestFailureHistory.new(suite.failed.values, project).load!
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index b2aa1d9f4ca..2a8f7171f9c 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -3,6 +3,7 @@
class Projects::PipelinesController < Projects::ApplicationController
include ::Gitlab::Utils::StrongMemoize
include RedisTracking
+ include ProductAnalyticsTracking
include ProjectStatsRefreshConflictsGuard
include ZuoraCSP
@@ -25,6 +26,7 @@ class Projects::PipelinesController < Projects::ApplicationController
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
@@ -32,8 +34,11 @@ class Projects::PipelinesController < Projects::ApplicationController
around_action :allow_gitaly_ref_name_caching, only: [:index, :show]
- # Will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/345074
- track_redis_hll_event :charts, name: 'p_analytics_pipelines'
+ track_custom_event :charts,
+ name: 'p_analytics_pipelines',
+ action: 'perform_analytics_usage_action',
+ label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly',
+ destinations: %i[redis_hll snowplow]
track_redis_hll_event :charts, name: 'p_analytics_ci_cd_pipelines', if: -> { should_track_ci_cd_pipelines? }
track_redis_hll_event :charts, name: 'p_analytics_ci_cd_deployment_frequency', if: -> { should_track_ci_cd_deployment_frequency? }
@@ -46,10 +51,10 @@ class Projects::PipelinesController < Projects::ApplicationController
POLLING_INTERVAL = 10_000
feature_category :continuous_integration, [
- :charts, :show, :config_variables, :stage, :cancel, :retry,
- :builds, :dag, :failures, :status,
- :index, :create, :new, :destroy
- ]
+ :charts, :show, :config_variables, :stage, :cancel, :retry,
+ :builds, :dag, :failures, :status,
+ :index, :create, :new, :destroy
+ ]
feature_category :code_testing, [:test_report]
feature_category :build_artifacts, [:downloadable_artifacts]
@@ -371,6 +376,14 @@ class Projects::PipelinesController < Projects::ApplicationController
def should_track_ci_cd_change_failure_rate?
params[:chart] == 'change-failure-rate'
end
+
+ def tracking_namespace_source
+ project.namespace
+ end
+
+ def tracking_project_source
+ project
+ end
end
Projects::PipelinesController.prepend_mod_with('Projects::PipelinesController')
diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb
index ba9576795ec..ee12b85b3a4 100644
--- a/app/controllers/projects/runners_controller.rb
+++ b/app/controllers/projects/runners_controller.rb
@@ -15,7 +15,7 @@ class Projects::RunnersController < Projects::ApplicationController
end
def update
- if Ci::Runners::UpdateRunnerService.new(@runner).update(runner_params)
+ if Ci::Runners::UpdateRunnerService.new(@runner).execute(runner_params).success?
redirect_to project_runner_path(@project, @runner), notice: _('Runner was successfully updated.')
else
render 'edit'
@@ -31,7 +31,7 @@ class Projects::RunnersController < Projects::ApplicationController
end
def resume
- if Ci::Runners::UpdateRunnerService.new(@runner).update(active: true)
+ if Ci::Runners::UpdateRunnerService.new(@runner).execute(active: true).success?
redirect_to project_runners_path(@project), notice: _('Runner was successfully updated.')
else
redirect_to project_runners_path(@project), alert: _('Runner was not updated.')
@@ -39,7 +39,7 @@ class Projects::RunnersController < Projects::ApplicationController
end
def pause
- if Ci::Runners::UpdateRunnerService.new(@runner).update(active: false)
+ if Ci::Runners::UpdateRunnerService.new(@runner).execute(active: false).success?
redirect_to project_runners_path(@project), notice: _('Runner was successfully updated.')
else
redirect_to project_runners_path(@project), alert: _('Runner was not updated.')
diff --git a/app/controllers/projects/settings/integration_hook_logs_controller.rb b/app/controllers/projects/settings/integration_hook_logs_controller.rb
index 1e42fbce4c4..3a921ecad0d 100644
--- a/app/controllers/projects/settings/integration_hook_logs_controller.rb
+++ b/app/controllers/projects/settings/integration_hook_logs_controller.rb
@@ -7,13 +7,13 @@ module Projects
before_action :integration, only: [:show, :retry]
- def retry
- execute_hook
- redirect_to edit_project_settings_integration_path(@project, @integration)
- end
-
private
+ override :after_retry_redirect_path
+ def after_retry_redirect_path
+ edit_project_settings_integration_path(@project, @integration)
+ end
+
def integration
@integration ||= @project.find_or_initialize_integration(params[:integration_id])
end
diff --git a/app/controllers/projects/settings/integrations_controller.rb b/app/controllers/projects/settings/integrations_controller.rb
index 03ef434456f..2bbcd9fe20c 100644
--- a/app/controllers/projects/settings/integrations_controller.rb
+++ b/app/controllers/projects/settings/integrations_controller.rb
@@ -11,7 +11,7 @@ module Projects
before_action :integration, only: [:edit, :update, :test]
before_action :default_integration, only: [:edit, :update]
before_action :web_hook_logs, only: [:edit, :update]
- before_action -> { check_rate_limit!(:project_testing_integration, scope: [@project, current_user]) }, only: :test
+ before_action -> { check_test_rate_limit! }, only: :test
respond_to :html
@@ -124,7 +124,7 @@ module Projects
def web_hook_logs
return unless integration.try(:service_hook).present?
- @web_hook_logs ||= integration.service_hook.web_hook_logs.recent.page(params[:page])
+ @web_hook_logs ||= integration.service_hook.web_hook_logs.recent.page(params[:page]).without_count
end
def ensure_integration_enabled
@@ -140,6 +140,15 @@ module Projects
def use_inherited_settings?(attributes)
default_integration && attributes[:inherit_from_id] == default_integration.id.to_s
end
+
+ def check_test_rate_limit!
+ check_rate_limit!(:project_testing_integration, scope: [@project, current_user]) do
+ render json: {
+ error: true,
+ message: _('This endpoint has been requested too many times. Try again later.')
+ }, status: :ok
+ end
+ end
end
end
end
diff --git a/app/controllers/projects/settings/merge_requests_controller.rb b/app/controllers/projects/settings/merge_requests_controller.rb
new file mode 100644
index 00000000000..93e10695767
--- /dev/null
+++ b/app/controllers/projects/settings/merge_requests_controller.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+module Projects
+ module Settings
+ class MergeRequestsController < Projects::ApplicationController
+ layout 'project_settings'
+
+ before_action :merge_requests_enabled?
+ before_action :present_project, only: [:edit]
+ before_action :authorize_admin_project!
+
+ feature_category :code_review
+
+ def update
+ result = ::Projects::UpdateService.new(@project, current_user, project_params).execute
+
+ if result[:status] == :success
+ flash[:notice] = format(_("Project '%{project_name}' was successfully updated."), project_name: @project.name)
+ redirect_to project_settings_merge_requests_path(@project)
+ else
+ # Refresh the repo in case anything changed
+ @repository = @project.repository.reset
+
+ flash[:alert] = result[:message]
+ @project.reset
+ render 'show'
+ end
+ end
+
+ private
+
+ def merge_requests_enabled?
+ render_404 unless @project.merge_requests_enabled?
+ end
+
+ def project_params
+ params.require(:project)
+ .permit(project_params_attributes)
+ end
+
+ def project_setting_attributes
+ %i[
+ squash_option
+ allow_editing_commit_messages
+ mr_default_target_self
+ ]
+ end
+
+ def project_params_attributes
+ [
+ :allow_merge_on_skipped_pipeline,
+ :resolve_outdated_diff_discussions,
+ :only_allow_merge_if_all_discussions_are_resolved,
+ :only_allow_merge_if_pipeline_succeeds,
+ :printing_merge_request_link_enabled,
+ :remove_source_branch_after_merge,
+ :merge_method,
+ :merge_commit_template_or_default,
+ :squash_commit_template_or_default,
+ :suggestion_commit_message
+ ] + [project_setting_attributes: project_setting_attributes]
+ end
+ end
+ end
+end
+
+Projects::Settings::MergeRequestsController.prepend_mod_with('Projects::Settings::MergeRequestsController')
diff --git a/app/controllers/projects/settings/repository_controller.rb b/app/controllers/projects/settings/repository_controller.rb
index a178b8f7aa3..43c6451577a 100644
--- a/app/controllers/projects/settings/repository_controller.rb
+++ b/app/controllers/projects/settings/repository_controller.rb
@@ -34,13 +34,13 @@ module Projects
def create_deploy_token
result = Projects::DeployTokens::CreateService.new(@project, current_user, deploy_token_params).execute
- @new_deploy_token = result[:deploy_token]
if result[:status] == :success
+ @created_deploy_token = result[:deploy_token]
respond_to do |format|
format.json do
# IMPORTANT: It's a security risk to expose the token value more than just once here!
- json = API::Entities::DeployTokenWithToken.represent(@new_deploy_token).as_json
+ json = API::Entities::DeployTokenWithToken.represent(@created_deploy_token).as_json
render json: json, status: result[:http_status]
end
format.html do
@@ -49,6 +49,7 @@ module Projects
end
end
else
+ @new_deploy_token = result[:deploy_token]
respond_to do |format|
format.json { render json: { message: result[:message] }, status: result[:http_status] }
format.html do
diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb
index a364668ea5f..8f4987a07f6 100644
--- a/app/controllers/projects/uploads_controller.rb
+++ b/app/controllers/projects/uploads_controller.rb
@@ -35,14 +35,4 @@ class Projects::UploadsController < Projects::ApplicationController
Project.find_by_full_path("#{namespace}/#{id}")
end
-
- # Overrides ApplicationController#build_canonical_path since there are
- # multiple routes that match project uploads:
- # https://gitlab.com/gitlab-org/gitlab/issues/196396
- def build_canonical_path(project)
- return super unless action_name == 'show'
- return super unless params[:secret] && params[:filename]
-
- show_namespace_project_uploads_url(project.namespace.to_param, project.to_param, params[:secret], params[:filename])
- end
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 8a6bcb4b3fc..5ceedbc1e01 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -12,6 +12,8 @@ class ProjectsController < Projects::ApplicationController
include SourcegraphDecorator
include PlanningHierarchy
+ REFS_LIMIT = 100
+
prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) }
around_action :allow_gitaly_ref_name_caching, only: [:index, :show]
@@ -54,9 +56,9 @@ class ProjectsController < Projects::ApplicationController
layout :determine_layout
feature_category :projects, [
- :index, :show, :new, :create, :edit, :update, :transfer,
- :destroy, :archive, :unarchive, :toggle_star, :activity
- ]
+ :index, :show, :new, :create, :edit, :update, :transfer,
+ :destroy, :archive, :unarchive, :toggle_star, :activity
+ ]
feature_category :source_code_management, [:remove_fork, :housekeeping, :refs]
feature_category :team_planning, [:preview_markdown, :new_issuable_address]
@@ -309,6 +311,8 @@ class ProjectsController < Projects::ApplicationController
find_tags = true
find_commits = true
+ use_gitaly_pagination = Feature.enabled?(:use_gitaly_pagination_for_refs, @project)
+
unless find_refs.nil?
find_branches = find_refs.include?('branches')
find_tags = find_refs.include?('tags')
@@ -318,13 +322,21 @@ class ProjectsController < Projects::ApplicationController
options = {}
if find_branches
- branches = BranchesFinder.new(@repository, refs_params).execute.take(100).map(&:name)
+ branches = BranchesFinder.new(@repository, refs_params.merge(per_page: REFS_LIMIT))
+ .execute(gitaly_pagination: use_gitaly_pagination)
+ .take(REFS_LIMIT)
+ .map(&:name)
+
options['Branches'] = branches
end
if find_tags && @repository.tag_count.nonzero?
- tags = TagsFinder.new(@repository, refs_params).execute
- options['Tags'] = tags.take(100).map(&:name)
+ tags = TagsFinder.new(@repository, refs_params.merge(per_page: REFS_LIMIT))
+ .execute(gitaly_pagination: use_gitaly_pagination)
+ .take(REFS_LIMIT)
+ .map(&:name)
+
+ options['Tags'] = tags
end
# If reference is commit id - we should add it to branch/tag selectbox
@@ -430,6 +442,7 @@ class ProjectsController < Projects::ApplicationController
if Feature.enabled?(:split_operations_visibility_permissions, project)
%i[
environments_access_level feature_flags_access_level releases_access_level
+ monitor_access_level
]
else
%i[operations_access_level]
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index 33d2c482795..0bd266bb490 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -32,6 +32,7 @@ class RegistrationsController < Devise::RegistrationsController
def create
set_user_state
+ token = set_custom_confirmation_token
super do |new_user|
accept_pending_invitations if new_user.persisted?
@@ -39,6 +40,7 @@ class RegistrationsController < Devise::RegistrationsController
persist_accepted_terms_if_required(new_user)
set_role_required(new_user)
track_experiment_event(new_user)
+ send_custom_confirmation_instructions(new_user, token)
if pending_approval?
NotificationService.new.new_instance_access_request(new_user)
@@ -118,8 +120,10 @@ class RegistrationsController < Devise::RegistrationsController
def after_inactive_sign_up_path_for(resource)
Gitlab::AppLogger.info(user_created_message)
return new_user_session_path(anchor: 'login-pane') if resource.blocked_pending_approval?
+ return dashboard_projects_path if Feature.enabled?(:soft_email_confirmation)
+ return identity_verification_redirect_path if custom_confirmation_enabled?(resource)
- Feature.enabled?(:soft_email_confirmation) ? dashboard_projects_path : users_almost_there_path(email: resource.email)
+ users_almost_there_path(email: resource.email)
end
private
@@ -236,6 +240,22 @@ class RegistrationsController < Devise::RegistrationsController
# signing up and becoming users
experiment(:logged_out_marketing_header, actor: new_user).track(:signed_up) if new_user.persisted?
end
+
+ def identity_verification_redirect_path
+ # overridden by EE module
+ end
+
+ def custom_confirmation_enabled?(resource)
+ # overridden by EE module
+ end
+
+ def set_custom_confirmation_token
+ # overridden by EE module
+ end
+
+ def send_custom_confirmation_instructions(user, token)
+ # overridden by EE module
+ end
end
RegistrationsController.prepend_mod_with('RegistrationsController')
diff --git a/app/controllers/repositories/git_http_client_controller.rb b/app/controllers/repositories/git_http_client_controller.rb
index fbf5d82a45b..a5ca17db113 100644
--- a/app/controllers/repositories/git_http_client_controller.rb
+++ b/app/controllers/repositories/git_http_client_controller.rb
@@ -3,7 +3,7 @@
module Repositories
class GitHttpClientController < Repositories::ApplicationController
include ActionController::HttpAuthentication::Basic
- include KerberosSpnegoHelper
+ include KerberosHelper
include Gitlab::Utils::StrongMemoize
attr_reader :authentication_result, :redirected_path
@@ -49,7 +49,7 @@ module Repositories
if handle_basic_authentication(login, password)
return # Allow access
end
- elsif allow_kerberos_spnego_auth? && spnego_provided?
+ elsif allow_kerberos_auth? && spnego_provided?
kerberos_user = find_kerberos_user
if kerberos_user
@@ -91,7 +91,7 @@ module Repositories
def send_challenges
challenges = []
challenges << 'Basic realm="GitLab"' if allow_basic_auth?
- challenges << spnego_challenge if allow_kerberos_spnego_auth?
+ challenges << spnego_challenge if allow_kerberos_auth?
headers['Www-Authenticate'] = challenges.join("\n") if challenges.any?
end
diff --git a/app/controllers/repositories/git_http_controller.rb b/app/controllers/repositories/git_http_controller.rb
index c3c6a51239d..144ec4c0de9 100644
--- a/app/controllers/repositories/git_http_controller.rb
+++ b/app/controllers/repositories/git_http_controller.rb
@@ -83,7 +83,7 @@ module Repositories
return if Gitlab::Database.read_only?
return unless repo_type.project?
- OnboardingProgressService.async(project.namespace_id).execute(action: :git_pull)
+ Onboarding::ProgressService.async(project.namespace_id).execute(action: :git_pull)
return if Feature.enabled?(:disable_git_http_fetch_writes)
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index 5843e13c7cd..9f87ad6aaf6 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -57,7 +57,25 @@ class SearchController < ApplicationController
@search_highlight = @search_service.search_highlight
end
+ Gitlab::Metrics::GlobalSearchSlis.record_apdex(
+ elapsed: @global_search_duration_s,
+ search_type: @search_type,
+ search_level: @search_level,
+ search_scope: @scope
+ )
+
increment_search_counters
+ ensure
+ if @search_type
+ # If we raise an error somewhere in the @global_search_duration_s benchmark block, we will end up here
+ # with a 200 status code, but an empty @global_search_duration_s.
+ Gitlab::Metrics::GlobalSearchSlis.record_error_rate(
+ error: @global_search_duration_s.nil? || (status < 200 || status >= 400),
+ search_type: @search_type,
+ search_level: @search_level,
+ search_scope: @scope
+ )
+ end
end
def count
diff --git a/app/experiments/combined_registration_experiment.rb b/app/experiments/combined_registration_experiment.rb
deleted file mode 100644
index 38295cec0d3..00000000000
--- a/app/experiments/combined_registration_experiment.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-class CombinedRegistrationExperiment < ApplicationExperiment
- include Rails.application.routes.url_helpers
-
- control { new_users_sign_up_group_path }
- candidate { new_users_sign_up_groups_project_path }
-
- def key_for(source, _ = nil)
- super(source, 'force_company_trial')
- end
-
- def redirect_path
- run
- end
-end
diff --git a/app/finders/context_commits_finder.rb b/app/finders/context_commits_finder.rb
index d623854ada4..4a45817cc61 100644
--- a/app/finders/context_commits_finder.rb
+++ b/app/finders/context_commits_finder.rb
@@ -5,8 +5,10 @@ class ContextCommitsFinder
@project = project
@merge_request = merge_request
@search = params[:search]
+ @author = params[:author]
+ @committed_before = params[:committed_before]
+ @committed_after = params[:committed_after]
@limit = (params[:limit] || 40).to_i
- @offset = (params[:offset] || 0).to_i
end
def execute
@@ -16,13 +18,13 @@ class ContextCommitsFinder
private
- attr_reader :project, :merge_request, :search, :limit, :offset
+ attr_reader :project, :merge_request, :search, :author, :committed_before, :committed_after, :limit
def init_collection
if search.present?
search_commits
else
- project.repository.commits(merge_request.target_branch, { limit: limit, offset: offset })
+ project.repository.commits(merge_request.target_branch, { limit: limit })
end
end
@@ -41,7 +43,8 @@ class ContextCommitsFinder
commits = [commit_by_sha] if commit_by_sha
end
else
- commits = project.repository.find_commits_by_message(search, merge_request.target_branch, nil, 20)
+ commits = project.repository.list_commits_by(search, merge_request.target_branch,
+ author: author, before: committed_before, after: committed_after, limit: limit)
end
commits
diff --git a/app/finders/crm/organizations_finder.rb b/app/finders/crm/organizations_finder.rb
index 5a8ab148ef3..69f72235c71 100644
--- a/app/finders/crm/organizations_finder.rb
+++ b/app/finders/crm/organizations_finder.rb
@@ -16,6 +16,11 @@ module Crm
attr_reader :params, :current_user
+ def self.counts_by_state(current_user, params = {})
+ params = params.merge(sort: nil)
+ new(current_user, params).execute.counts_by_state
+ end
+
def initialize(current_user, params = {})
@current_user = current_user
@params = params
@@ -28,11 +33,20 @@ module Crm
organizations = by_ids(organizations)
organizations = by_search(organizations)
organizations = by_state(organizations)
- organizations.sort_by_name
+ sort_organizations(organizations)
end
private
+ def sort_organizations(organizations)
+ return organizations.sort_by_name unless @params.key?(:sort)
+ return organizations if @params[:sort].nil?
+
+ field = @params[:sort][:field]
+ direction = @params[:sort][:direction]
+ organizations.sort_by_field(field, direction)
+ end
+
def root_group
strong_memoize(:root_group) do
group = params[:group]&.root_ancestor
diff --git a/app/finders/database/batched_background_migrations_finder.rb b/app/finders/database/batched_background_migrations_finder.rb
new file mode 100644
index 00000000000..866acd47238
--- /dev/null
+++ b/app/finders/database/batched_background_migrations_finder.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Database
+ class BatchedBackgroundMigrationsFinder
+ RETURNED_MIGRATIONS = 20
+
+ def initialize(connection:)
+ @connection = connection
+ end
+
+ def execute
+ batched_migration_class.ordered_by_created_at_desc.for_gitlab_schema(schema).limit(RETURNED_MIGRATIONS)
+ end
+
+ private
+
+ attr_accessor :connection
+
+ def batched_migration_class
+ Gitlab::Database::BackgroundMigration::BatchedMigration
+ end
+
+ def schema
+ Gitlab::Database.gitlab_schemas_for_connection(connection)
+ end
+ end
+end
diff --git a/app/finders/deployments_finder.rb b/app/finders/deployments_finder.rb
index 04b82ee04ec..5b2139cb941 100644
--- a/app/finders/deployments_finder.rb
+++ b/app/finders/deployments_finder.rb
@@ -9,8 +9,8 @@
# updated_before: DateTime
# finished_after: DateTime
# finished_before: DateTime
-# environment: String
-# status: String (see Deployment.statuses)
+# environment: String (name) or Integer (ID)
+# status: String or Array<String> (see Deployment.statuses)
# order_by: String (see ALLOWED_SORT_VALUES constant)
# sort: String (asc | desc)
class DeploymentsFinder
@@ -33,6 +33,7 @@ class DeploymentsFinder
def initialize(params = {})
@params = params
+ @params[:status] = Array(@params[:status]).map(&:to_s) if @params[:status]
validate!
end
@@ -68,16 +69,25 @@ class DeploymentsFinder
raise error if raise_for_inefficient_updated_at_query?
end
- if (filter_by_finished_at? && !order_by_finished_at?) || (!filter_by_finished_at? && order_by_finished_at?)
- raise InefficientQueryError, '`finished_at` filter and `finished_at` sorting must be paired'
+ if filter_by_finished_at? && !order_by_finished_at?
+ raise InefficientQueryError, '`finished_at` filter requires `finished_at` sort.'
+ end
+
+ if order_by_finished_at? && !(filter_by_finished_at? || filter_by_finished_statuses?)
+ raise InefficientQueryError,
+ '`finished_at` sort requires `finished_at` filter or a filter with at least one of the finished statuses.'
end
if filter_by_finished_at? && !filter_by_successful_deployment?
raise InefficientQueryError, '`finished_at` filter must be combined with `success` status filter.'
end
- if params[:environment].present? && !params[:project].present?
- raise InefficientQueryError, '`environment` filter must be combined with `project` scope.'
+ if filter_by_environment_name? && !params[:project].present?
+ raise InefficientQueryError, '`environment` name filter must be combined with `project` scope.'
+ end
+
+ if filter_by_finished_statuses? && filter_by_upcoming_statuses?
+ raise InefficientQueryError, 'finished statuses and upcoming statuses must be separately queried.'
end
end
@@ -86,6 +96,8 @@ class DeploymentsFinder
params[:project].deployments
elsif params[:group].present?
::Deployment.for_projects(params[:group].all_projects)
+ elsif filter_by_environment_id?
+ ::Deployment.for_environment(params[:environment])
else
::Deployment.none
end
@@ -112,7 +124,7 @@ class DeploymentsFinder
end
def by_environment(items)
- if params[:project].present? && params[:environment].present?
+ if params[:project].present? && filter_by_environment_name?
items.for_environment_name(params[:project], params[:environment])
else
items
@@ -122,7 +134,7 @@ class DeploymentsFinder
def by_status(items)
return items unless params[:status].present?
- unless Deployment.statuses.key?(params[:status])
+ unless Deployment.statuses.keys.intersection(params[:status]) == params[:status]
raise ArgumentError, "The deployment status #{params[:status]} is invalid"
end
@@ -165,7 +177,23 @@ class DeploymentsFinder
end
def filter_by_successful_deployment?
- params[:status].to_s == 'success'
+ params[:status].present? && params[:status].count == 1 && params[:status].first.to_s == 'success'
+ end
+
+ def filter_by_finished_statuses?
+ params[:status].present? && Deployment::FINISHED_STATUSES.map(&:to_s).intersection(params[:status]).any?
+ end
+
+ def filter_by_upcoming_statuses?
+ params[:status].present? && Deployment::UPCOMING_STATUSES.map(&:to_s).intersection(params[:status]).any?
+ end
+
+ def filter_by_environment_name?
+ params[:environment].present? && params[:environment].is_a?(String)
+ end
+
+ def filter_by_environment_id?
+ params[:environment].present? && params[:environment].is_a?(Integer)
end
def order_by_updated_at?
@@ -183,6 +211,7 @@ class DeploymentsFinder
environment: [],
deployable: {
job_artifacts: [],
+ user: [],
pipeline: {
project: {
route: [],
diff --git a/app/finders/environments/environments_finder.rb b/app/finders/environments/environments_finder.rb
index 46c49f096c6..f2dcba04349 100644
--- a/app/finders/environments/environments_finder.rb
+++ b/app/finders/environments/environments_finder.rb
@@ -14,6 +14,7 @@ module Environments
def execute
environments = project.environments
+ environments = by_type(environments)
environments = by_name(environments)
environments = by_search(environments)
environments = by_ids(environments)
@@ -24,6 +25,12 @@ module Environments
private
+ def by_type(environments)
+ return environments unless params[:type].present?
+
+ environments.for_type(params[:type])
+ end
+
def by_name(environments)
if params[:name].present?
environments.for_name(params[:name])
diff --git a/app/finders/group_members_finder.rb b/app/finders/group_members_finder.rb
index 048e25046da..4688d561897 100644
--- a/app/finders/group_members_finder.rb
+++ b/app/finders/group_members_finder.rb
@@ -47,7 +47,7 @@ class GroupMembersFinder < UnionFinder
related_groups << Group.by_id(group.id) if include_relations&.include?(:direct)
related_groups << group.ancestors if include_relations&.include?(:inherited)
related_groups << group.descendants if include_relations&.include?(:descendants)
- related_groups << group.shared_with_groups.public_or_visible_to_user(user) if include_relations&.include?(:shared_from_groups)
+ related_groups << Group.shared_into_ancestors(group).public_or_visible_to_user(user) if include_relations&.include?(:shared_from_groups)
find_union(related_groups, Group)
end
diff --git a/app/finders/groups/accepting_group_transfers_finder.rb b/app/finders/groups/accepting_group_transfers_finder.rb
new file mode 100644
index 00000000000..df67f940d20
--- /dev/null
+++ b/app/finders/groups/accepting_group_transfers_finder.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+module Groups
+ class AcceptingGroupTransfersFinder < Base
+ include Gitlab::Utils::StrongMemoize
+
+ def initialize(current_user, group_to_be_transferred, params = {})
+ @current_user = current_user
+ @group_to_be_transferred = group_to_be_transferred
+ @params = params
+ end
+
+ def execute
+ return Group.none unless can_transfer_group?
+
+ items = if Feature.enabled?(:include_groups_from_group_shares_in_group_transfer_locations)
+ find_all_groups
+ else
+ find_groups
+ end
+
+ items = by_search(items)
+
+ sort(items)
+ end
+
+ private
+
+ attr_reader :current_user, :group_to_be_transferred, :params
+
+ def find_groups
+ GroupsFinder.new( # rubocop: disable CodeReuse/Finder
+ current_user,
+ min_access_level: Gitlab::Access::OWNER,
+ exclude_group_ids: exclude_groups
+ ).execute.without_order
+ end
+
+ def find_all_groups
+ ::Namespace.from_union(
+ [
+ find_groups,
+ groups_originating_from_group_shares_with_owner_access
+ ]
+ )
+ end
+
+ def groups_originating_from_group_shares_with_owner_access
+ GroupGroupLink
+ .with_owner_access
+ .groups_accessible_via(
+ current_user.owned_groups.select(:id)
+ ).id_not_in(exclude_groups)
+ end
+
+ def exclude_groups
+ strong_memoize(:exclude_groups) do
+ exclude_groups = group_to_be_transferred.self_and_descendants.pluck_primary_key
+ exclude_groups << group_to_be_transferred.parent_id if group_to_be_transferred.parent_id
+
+ exclude_groups
+ end
+ end
+
+ def can_transfer_group?
+ Ability.allowed?(current_user, :admin_group, group_to_be_transferred)
+ end
+ end
+end
diff --git a/app/finders/groups/accepting_project_transfers_finder.rb b/app/finders/groups/accepting_project_transfers_finder.rb
index 09d3c430641..a3f58a78eca 100644
--- a/app/finders/groups/accepting_project_transfers_finder.rb
+++ b/app/finders/groups/accepting_project_transfers_finder.rb
@@ -7,10 +7,6 @@ module Groups
end
def execute
- if Feature.disabled?(:include_groups_from_group_shares_in_project_transfer_locations)
- return current_user.manageable_groups
- end
-
groups_accepting_project_transfers =
[
current_user.manageable_groups,
diff --git a/app/finders/groups/base.rb b/app/finders/groups/base.rb
new file mode 100644
index 00000000000..d7f56b1a7a6
--- /dev/null
+++ b/app/finders/groups/base.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Groups
+ class Base
+ private
+
+ def sort(items)
+ items.order(Group.arel_table[:path].asc, Group.arel_table[:id].asc) # rubocop: disable CodeReuse/ActiveRecord
+ end
+
+ def by_search(items)
+ return items if params[:search].blank?
+
+ items.search(params[:search], include_parents: true)
+ end
+ end
+end
diff --git a/app/finders/groups/user_groups_finder.rb b/app/finders/groups/user_groups_finder.rb
index bda8b7cc1e0..b58c1323b1f 100644
--- a/app/finders/groups/user_groups_finder.rb
+++ b/app/finders/groups/user_groups_finder.rb
@@ -13,7 +13,7 @@
#
# Initially created to filter user groups and descendants where the user can create projects
module Groups
- class UserGroupsFinder
+ class UserGroupsFinder < Base
def initialize(current_user, target_user, params = {})
@current_user = current_user
@target_user = target_user
@@ -34,16 +34,6 @@ module Groups
attr_reader :current_user, :target_user, :params
- def sort(items)
- items.order(Group.arel_table[:path].asc, Group.arel_table[:id].asc) # rubocop: disable CodeReuse/ActiveRecord
- end
-
- def by_search(items)
- return items if params[:search].blank?
-
- items.search(params[:search], include_parents: true)
- end
-
def by_permission_scope
if permission_scope_create_projects?
target_user.manageable_groups(include_groups_with_developer_maintainer_access: true)
diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb
index 9a8bc74f435..61d79885001 100644
--- a/app/finders/groups_finder.rb
+++ b/app/finders/groups_finder.rb
@@ -15,6 +15,7 @@
# exclude_group_ids: array of integers
# include_parent_descendants: boolean (defaults to false) - includes descendant groups when
# filtering by parent. The parent param must be present.
+# include_ancestors: boolean (defaults to true)
#
# Users with full private access can see all groups. The `owned` and `parent`
# params can be used to restrict the groups that are returned.
@@ -52,15 +53,7 @@ class GroupsFinder < UnionFinder
return [Group.all] if current_user&.can_read_all_resources? && all_available?
groups = []
-
- if current_user
- if Feature.enabled?(:use_traversal_ids_groups_finder, current_user)
- groups << current_user.authorized_groups.self_and_ancestors
- groups << current_user.groups.self_and_descendants
- else
- groups << Gitlab::ObjectHierarchy.new(groups_for_ancestors, groups_for_descendants).all_objects
- end
- end
+ groups = get_groups_for_user if current_user
groups << Group.unscoped.public_to_user(current_user) if include_public_groups?
groups << Group.none if groups.empty?
@@ -136,4 +129,29 @@ class GroupsFinder < UnionFinder
def min_access_level?
current_user && params[:min_access_level].present?
end
+
+ def include_ancestors?
+ params.fetch(:include_ancestors, true)
+ end
+
+ def get_groups_for_user
+ groups = []
+
+ if Feature.enabled?(:use_traversal_ids_groups_finder, current_user)
+ groups << if include_ancestors?
+ current_user.authorized_groups.self_and_ancestors
+ else
+ current_user.authorized_groups
+ end
+
+ groups << current_user.groups.self_and_descendants
+ elsif include_ancestors?
+ groups << Gitlab::ObjectHierarchy.new(groups_for_ancestors, groups_for_descendants).all_objects
+ else
+ groups << current_user.authorized_groups
+ groups << Gitlab::ObjectHierarchy.new(groups_for_descendants).base_and_descendants
+ end
+
+ groups
+ end
end
diff --git a/app/finders/incident_management/timeline_events_finder.rb b/app/finders/incident_management/timeline_events_finder.rb
index 09de46bb79f..aaf3133236a 100644
--- a/app/finders/incident_management/timeline_events_finder.rb
+++ b/app/finders/incident_management/timeline_events_finder.rb
@@ -31,7 +31,7 @@ module IncidentManagement
end
def sort(collection)
- collection.order_occurred_at_asc
+ collection.order_occurred_at_asc_id_asc
end
end
end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 1088d53c9a0..9f331d381aa 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -46,8 +46,7 @@ class IssuableFinder
requires_cross_project_access unless: -> { params.project? }
- FULL_TEXT_SEARCH_TERM_PATTERN = '[\u0000-\u218F]*'
- FULL_TEXT_SEARCH_TERM_REGEX = /\A#{FULL_TEXT_SEARCH_TERM_PATTERN}\z/.freeze
+ FULL_TEXT_SEARCH_TERM_REGEX = /\A[\p{ASCII}|\p{Latin}]+\z/.freeze
NEGATABLE_PARAMS_HELPER_KEYS = %i[project_id scope status include_subgroups].freeze
attr_accessor :current_user, :params
@@ -59,19 +58,19 @@ class IssuableFinder
class << self
def scalar_params
@scalar_params ||= %i[
- assignee_id
- assignee_username
- author_id
- author_username
- crm_contact_id
- crm_organization_id
- label_name
- milestone_title
- release_tag
- my_reaction_emoji
- search
- in
- ]
+ assignee_id
+ assignee_username
+ author_id
+ author_username
+ crm_contact_id
+ crm_organization_id
+ label_name
+ milestone_title
+ release_tag
+ my_reaction_emoji
+ search
+ in
+ ]
end
def array_params
diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb
index 663dda73a6a..9f96abcd4e5 100644
--- a/app/finders/issues_finder.rb
+++ b/app/finders/issues_finder.rb
@@ -60,10 +60,10 @@ class IssuesFinder < IssuableFinder
# count of issues assigned to the user for the header bar.
return issues.all if current_user && assignee_filter.includes_user?(current_user)
- return issues.where('issues.confidential IS NOT TRUE') if params.user_cannot_see_confidential_issues?
+ return issues.public_only if params.user_cannot_see_confidential_issues?
issues.where('
- issues.confidential IS NOT TRUE
+ issues.confidential = FALSE
OR (issues.confidential = TRUE
AND (issues.author_id = :user_id
OR EXISTS (SELECT TRUE FROM issue_assignees WHERE user_id = :user_id AND issue_id = issues.id)
diff --git a/app/finders/merge_requests/by_approvals_finder.rb b/app/finders/merge_requests/by_approvals_finder.rb
index 94f13468327..8b2e9aa8df1 100644
--- a/app/finders/merge_requests/by_approvals_finder.rb
+++ b/app/finders/merge_requests/by_approvals_finder.rb
@@ -71,9 +71,7 @@ module MergeRequests
#
# @param [ActiveRecord::Relation] items the activerecord relation
def with_any_approvals(items)
- items.select_from_union([
- items.with_approvals
- ])
+ items.select_from_union([items.with_approvals])
end
# Merge requests approved by given usernames
diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb
index 06feefb9059..ffa912afd1e 100644
--- a/app/finders/merge_requests_finder.rb
+++ b/app/finders/merge_requests_finder.rb
@@ -30,6 +30,8 @@
# updated_before: datetime
#
class MergeRequestsFinder < IssuableFinder
+ extend ::Gitlab::Utils::Override
+
include MergedAtFilter
def self.scalar_params
@@ -44,8 +46,7 @@ class MergeRequestsFinder < IssuableFinder
:reviewer_id,
:reviewer_username,
:target_branch,
- :wip,
- :attention
+ :wip
]
end
@@ -70,7 +71,6 @@ class MergeRequestsFinder < IssuableFinder
items = by_approvals(items)
items = by_deployments(items)
items = by_reviewer(items)
- items = by_attention(items)
by_source_project_id(items)
end
@@ -84,6 +84,16 @@ class MergeRequestsFinder < IssuableFinder
private
+ override :sort
+ def sort(items)
+ items = super(items)
+
+ return items unless use_grouping_columns?
+
+ grouping_columns = klass.grouping_columns(params[:sort])
+ items.group(grouping_columns) # rubocop:disable CodeReuse/ActiveRecord
+ end
+
def by_commit(items)
return items unless params[:commit_sha].presence
@@ -220,18 +230,18 @@ class MergeRequestsFinder < IssuableFinder
end
end
- def by_attention(items)
- return items unless params.attention?
-
- items.attention(params.attention)
- end
-
def parse_datetime(input)
# To work around http://www.ruby-lang.org/en/news/2021/11/15/date-parsing-method-regexp-dos-cve-2021-41817/
DateTime.parse(input.byteslice(0, 128)) if input
rescue Date::Error
nil
end
+
+ def use_grouping_columns?
+ return false unless params[:sort].present?
+
+ params[:approved_by_usernames].present? || params[:approved_by_ids].present?
+ end
end
MergeRequestsFinder.prepend_mod_with('MergeRequestsFinder')
diff --git a/app/finders/merge_requests_finder/params.rb b/app/finders/merge_requests_finder/params.rb
index 1c6a425c8af..e44e96054d3 100644
--- a/app/finders/merge_requests_finder/params.rb
+++ b/app/finders/merge_requests_finder/params.rb
@@ -21,11 +21,5 @@ class MergeRequestsFinder
end
end
end
-
- def attention
- strong_memoize(:attention) do
- User.find_by_username(params[:attention])
- end
- end
end
end
diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb
index 6b8dcd61d29..6bfe730ebc9 100644
--- a/app/finders/projects_finder.rb
+++ b/app/finders/projects_finder.rb
@@ -119,9 +119,9 @@ class ProjectsFinder < UnionFinder
# This is an optimization - surprisingly PostgreSQL does not optimize
# for this.
#
- # If the default visiblity level and desired visiblity level filter cancels
+ # If the default visibility level and desired visibility level filter cancels
# each other out, don't use the SQL clause for visibility level in
- # `Project.public_or_visible_to_user`. In fact, this then becames equivalent
+ # `Project.public_or_visible_to_user`. In fact, this then becomes equivalent
# to just authorized projects for the user.
#
# E.g.
diff --git a/app/finders/user_groups_counter.rb b/app/finders/user_groups_counter.rb
index 7dbc8502be2..e8e552510cd 100644
--- a/app/finders/user_groups_counter.rb
+++ b/app/finders/user_groups_counter.rb
@@ -8,9 +8,9 @@ class UserGroupsCounter
def execute
Namespace.unscoped do
Namespace.from_union([
- groups,
- project_groups
- ]).group(:user_id).count # rubocop: disable CodeReuse/ActiveRecord
+ groups,
+ project_groups
+ ]).group(:user_id).count # rubocop: disable CodeReuse/ActiveRecord
end
end
diff --git a/app/graphql/graphql_triggers.rb b/app/graphql/graphql_triggers.rb
index b39875b83a9..8086d8c02a4 100644
--- a/app/graphql/graphql_triggers.rb
+++ b/app/graphql/graphql_triggers.rb
@@ -21,3 +21,5 @@ module GraphqlTriggers
GitlabSchema.subscriptions.trigger('issuableDatesUpdated', { issuable_id: issuable.to_gid }, issuable)
end
end
+
+GraphqlTriggers.prepend_mod
diff --git a/app/graphql/mutations/boards/issues/issue_move_list.rb b/app/graphql/mutations/boards/issues/issue_move_list.rb
index 14fe9714f99..e9cae80e5f9 100644
--- a/app/graphql/mutations/boards/issues/issue_move_list.rb
+++ b/app/graphql/mutations/boards/issues/issue_move_list.rb
@@ -38,10 +38,16 @@ module Mutations
required: false,
description: 'ID of issue that should be placed after the current issue.'
+ argument :position_in_list, GraphQL::Types::Int,
+ required: false,
+ description: "Position of issue within the board list. Positions start at 0. "\
+ "Use #{::Boards::Issues::MoveService::LIST_END_POSITION} to move to the end of the list."
+
def ready?(**args)
if move_arguments(args).blank?
raise Gitlab::Graphql::Errors::ArgumentError,
- 'At least one of the arguments fromListId, toListId, afterId or beforeId is required'
+ 'At least one of the arguments ' \
+ 'fromListId, toListId, positionInList, moveAfterId, or moveBeforeId is required'
end
if move_list_arguments(args).one?
@@ -49,6 +55,24 @@ module Mutations
'Both fromListId and toListId must be present'
end
+ if args[:position_in_list].present?
+ if move_list_arguments(args).empty?
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ 'Both fromListId and toListId are required when positionInList is given'
+ end
+
+ if args[:move_before_id].present? || args[:move_after_id].present?
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ 'positionInList is mutually exclusive with any of moveBeforeId or moveAfterId'
+ end
+
+ if args[:position_in_list] != ::Boards::Issues::MoveService::LIST_END_POSITION &&
+ args[:position_in_list] < 0
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ "positionInList must be >= 0 or #{::Boards::Issues::MoveService::LIST_END_POSITION}"
+ end
+ end
+
super
end
@@ -77,7 +101,7 @@ module Mutations
end
def move_arguments(args)
- args.slice(:from_list_id, :to_list_id, :move_after_id, :move_before_id)
+ args.slice(:from_list_id, :to_list_id, :position_in_list, :move_after_id, :move_before_id)
end
def error_for(result)
diff --git a/app/graphql/mutations/ci/job/artifacts_destroy.rb b/app/graphql/mutations/ci/job/artifacts_destroy.rb
new file mode 100644
index 00000000000..c27ab9c4d89
--- /dev/null
+++ b/app/graphql/mutations/ci/job/artifacts_destroy.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Ci
+ module Job
+ class ArtifactsDestroy < Base
+ graphql_name 'JobArtifactsDestroy'
+
+ authorize :destroy_artifacts
+
+ field :job,
+ Types::Ci::JobType,
+ null: true,
+ description: 'Job with artifacts to be deleted.'
+
+ field :destroyed_artifacts_count,
+ GraphQL::Types::Int,
+ null: false,
+ description: 'Number of artifacts deleted.'
+
+ def find_object(id: )
+ GlobalID::Locator.locate(id)
+ end
+
+ def resolve(id:)
+ job = authorized_find!(id: id)
+
+ result = ::Ci::JobArtifacts::DestroyBatchService.new(job.job_artifacts, pick_up_at: Time.current).execute
+ {
+ job: job,
+ destroyed_artifacts_count: result[:destroyed_artifacts_count],
+ errors: Array(result[:errors])
+ }
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/ci/job_artifact/destroy.rb b/app/graphql/mutations/ci/job_artifact/destroy.rb
new file mode 100644
index 00000000000..47b3535d631
--- /dev/null
+++ b/app/graphql/mutations/ci/job_artifact/destroy.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Ci
+ module JobArtifact
+ class Destroy < BaseMutation
+ graphql_name 'ArtifactDestroy'
+
+ authorize :destroy_artifacts
+
+ ArtifactID = ::Types::GlobalIDType[::Ci::JobArtifact]
+
+ argument :id,
+ ArtifactID,
+ required: true,
+ description: 'ID of the artifact to delete.'
+
+ field :artifact,
+ Types::Ci::JobArtifactType,
+ null: true,
+ description: 'Deleted artifact.'
+
+ def find_object(id: )
+ GlobalID::Locator.locate(id)
+ end
+
+ def resolve(id:)
+ artifact = authorized_find!(id: id)
+
+ if artifact.destroy
+ { errors: [] }
+ else
+ { errors: artifact.errors.full_messages }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/mutations/ci/runner/bulk_delete.rb b/app/graphql/mutations/ci/runner/bulk_delete.rb
index 4c1c2967799..4265099d28e 100644
--- a/app/graphql/mutations/ci/runner/bulk_delete.rb
+++ b/app/graphql/mutations/ci/runner/bulk_delete.rb
@@ -40,9 +40,7 @@ module Mutations
private
def model_ids_of(ids)
- ids.map do |gid|
- gid.model_id.to_i
- end.compact
+ ids.filter_map { |gid| gid.model_id.to_i }
end
def find_all_runners_by_ids(ids)
diff --git a/app/graphql/mutations/ci/runner/update.rb b/app/graphql/mutations/ci/runner/update.rb
index 1c6cf6989bf..f98138646be 100644
--- a/app/graphql/mutations/ci/runner/update.rb
+++ b/app/graphql/mutations/ci/runner/update.rb
@@ -48,8 +48,13 @@ module Mutations
description: 'Indicates the runner is able to run untagged jobs.'
argument :tag_list, [GraphQL::Types::String],
- required: false,
- description: 'Tags associated with the runner.'
+ required: false,
+ description: 'Tags associated with the runner.'
+
+ 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 } }
field :runner,
Types::Ci::RunnerType,
@@ -59,16 +64,47 @@ module Mutations
def resolve(id:, **runner_attrs)
runner = authorized_find!(id)
- unless ::Ci::Runners::UpdateRunnerService.new(runner).update(runner_attrs)
- return { runner: nil, errors: runner.errors.full_messages }
+ associated_projects_ids = runner_attrs.delete(:associated_projects)
+
+ response = { runner: runner, errors: [] }
+ ::Ci::Runner.transaction do
+ associate_runner_projects(response, runner, associated_projects_ids) if associated_projects_ids.present?
+ update_runner(response, runner, runner_attrs)
end
- { runner: runner, errors: [] }
+ response
end
def find_object(id)
GitlabSchema.find_by_gid(id)
end
+
+ private
+
+ def associate_runner_projects(response, runner, associated_project_ids)
+ unless runner.project_type?
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ "associatedProjects must not be specified for '#{runner.runner_type}' scope"
+ end
+
+ result = ::Ci::Runners::SetRunnerAssociatedProjectsService.new(
+ runner: runner,
+ current_user: current_user,
+ project_ids: associated_project_ids
+ ).execute
+ return if result.success?
+
+ response[:errors] = result.errors
+ raise ActiveRecord::Rollback
+ end
+
+ def update_runner(response, runner, attrs)
+ result = ::Ci::Runners::UpdateRunnerService.new(runner).execute(attrs)
+ return if result.success?
+
+ response[:errors] = result.errors
+ raise ActiveRecord::Rollback
+ end
end
end
end
diff --git a/app/graphql/mutations/custom_emoji/create.rb b/app/graphql/mutations/custom_emoji/create.rb
index 269ea6c9999..535ff44a7fd 100644
--- a/app/graphql/mutations/custom_emoji/create.rb
+++ b/app/graphql/mutations/custom_emoji/create.rb
@@ -28,6 +28,10 @@ module Mutations
description: 'Location of the emoji file.'
def resolve(group_path:, **args)
+ if Feature.disabled?(:custom_emoji)
+ raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Custom emoji feature is disabled'
+ end
+
group = authorized_find!(group_path: group_path)
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/37911#note_444682238
args[:external] = true
diff --git a/app/graphql/mutations/custom_emoji/destroy.rb b/app/graphql/mutations/custom_emoji/destroy.rb
index 863b8152cc7..64e3f2ed7d3 100644
--- a/app/graphql/mutations/custom_emoji/destroy.rb
+++ b/app/graphql/mutations/custom_emoji/destroy.rb
@@ -17,6 +17,10 @@ module Mutations
description: 'Global ID of the custom emoji to destroy.'
def resolve(id:)
+ if Feature.disabled?(:custom_emoji)
+ raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Custom emoji feature is disabled'
+ end
+
custom_emoji = authorized_find!(id: id)
custom_emoji.destroy!
diff --git a/app/graphql/mutations/dependency_proxy/group_settings/update.rb b/app/graphql/mutations/dependency_proxy/group_settings/update.rb
index 65c919db3c3..6be07edd883 100644
--- a/app/graphql/mutations/dependency_proxy/group_settings/update.rb
+++ b/app/graphql/mutations/dependency_proxy/group_settings/update.rb
@@ -8,6 +8,11 @@ module Mutations
include Mutations::ResolvesGroup
+ description 'These settings can be adjusted by the group Owner or Maintainer. However, in GitLab 16.0, we ' \
+ 'will be limiting this to the Owner role. ' \
+ '[GitLab-#364441](https://gitlab.com/gitlab-org/gitlab/-/issues/364441) proposes making ' \
+ 'this change to match the permissions level in the user interface.'
+
authorize :admin_dependency_proxy
argument :group_path,
diff --git a/app/graphql/mutations/incident_management/timeline_event/promote_from_note.rb b/app/graphql/mutations/incident_management/timeline_event/promote_from_note.rb
index 31ae29d896b..bb1da9278ff 100644
--- a/app/graphql/mutations/incident_management/timeline_event/promote_from_note.rb
+++ b/app/graphql/mutations/incident_management/timeline_event/promote_from_note.rb
@@ -6,6 +6,8 @@ module Mutations
class PromoteFromNote < Base
graphql_name 'TimelineEventPromoteFromNote'
+ include NotesHelper
+
argument :note_id, Types::GlobalIDType[::Note],
required: true,
description: 'Note ID from which the timeline event promoted.'
@@ -20,7 +22,7 @@ module Mutations
incident,
current_user,
promoted_from_note: note,
- note: note.note,
+ note: build_note_string(note),
occurred_at: note.created_at,
editable: true
).execute
@@ -38,6 +40,11 @@ module Mutations
super
end
+ def build_note_string(note)
+ commented = _('commented')
+ "@#{note.author.username} [#{commented}](#{noteable_note_url(note)}): '#{note.note}'"
+ end
+
def raise_noteable_not_incident!
raise_resource_not_available_error! 'Note does not belong to an incident'
end
diff --git a/app/graphql/mutations/releases/create.rb b/app/graphql/mutations/releases/create.rb
index 70a0e71c869..ba1fa8d446c 100644
--- a/app/graphql/mutations/releases/create.rb
+++ b/app/graphql/mutations/releases/create.rb
@@ -32,7 +32,7 @@ module Mutations
argument :released_at, Types::TimeType,
required: false,
- description: 'Date and time for the release. Defaults to the current date and time.'
+ description: 'Date and time for the release. Defaults to the current time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). Only provide this field if creating an upcoming or historical release.'
argument :milestones, [GraphQL::Types::String],
required: false,
diff --git a/app/graphql/mutations/todos/restore_many.rb b/app/graphql/mutations/todos/restore_many.rb
index fe0ad6df65b..20913a9e7da 100644
--- a/app/graphql/mutations/todos/restore_many.rb
+++ b/app/graphql/mutations/todos/restore_many.rb
@@ -32,9 +32,7 @@ module Mutations
private
def model_ids_of(ids)
- ids.map do |gid|
- gid.model_id.to_i
- end.compact
+ ids.filter_map { |gid| gid.model_id.to_i }
end
def raise_too_many_todos_requested_error
diff --git a/app/graphql/queries/repository/blob_info.query.graphql b/app/graphql/queries/repository/blob_info.query.graphql
new file mode 100644
index 00000000000..fd463436ed4
--- /dev/null
+++ b/app/graphql/queries/repository/blob_info.query.graphql
@@ -0,0 +1,62 @@
+query getBlobInfo(
+ $projectPath: ID!
+ $filePath: String!
+ $ref: String!
+ $shouldFetchRawText: Boolean!
+) {
+ project(fullPath: $projectPath) {
+ __typename
+ id
+ repository {
+ __typename
+ empty
+ blobs(paths: [$filePath], ref: $ref) {
+ __typename
+ nodes {
+ __typename
+ id
+ webPath
+ name
+ size
+ rawSize
+ rawTextBlob @include(if: $shouldFetchRawText)
+ fileType
+ language
+ path
+ blamePath
+ editBlobPath
+ gitpodBlobUrl
+ ideEditPath
+ forkAndEditPath
+ ideForkAndEditPath
+ codeNavigationPath
+ projectBlobPathRoot
+ forkAndViewPath
+ environmentFormattedExternalUrl
+ environmentExternalUrlForRouteMap
+ canModifyBlob
+ canCurrentUserPushToBranch
+ archived
+ storedExternally
+ externalStorage
+ externalStorageUrl
+ rawPath
+ replacePath
+ pipelineEditorPath
+ simpleViewer {
+ fileType
+ tooLarge
+ type
+ renderError
+ }
+ richViewer {
+ fileType
+ tooLarge
+ type
+ renderError
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/graphql/resolvers/ci/job_token_scope_resolver.rb b/app/graphql/resolvers/ci/job_token_scope_resolver.rb
index ca76a7b94fc..7c6aedad1d6 100644
--- a/app/graphql/resolvers/ci/job_token_scope_resolver.rb
+++ b/app/graphql/resolvers/ci/job_token_scope_resolver.rb
@@ -6,14 +6,12 @@ module Resolvers
include Gitlab::Graphql::Authorize::AuthorizeResource
authorize :admin_project
- description 'Container for resources that can be accessed by a CI job token from the current project. Null if job token scope setting is disabled.'
+ description 'Container for resources that can be accessed by a CI job token from the current project.'
type ::Types::Ci::JobTokenScopeType, null: true
def resolve
authorize!(object)
- return unless object.ci_job_token_scope_enabled?
-
::Ci::JobToken::Scope.new(object)
end
end
diff --git a/app/graphql/resolvers/ci/runner_jobs_resolver.rb b/app/graphql/resolvers/ci/runner_jobs_resolver.rb
index 2f6ca09d031..de00aadaea8 100644
--- a/app/graphql/resolvers/ci/runner_jobs_resolver.rb
+++ b/app/graphql/resolvers/ci/runner_jobs_resolver.rb
@@ -9,6 +9,7 @@ module Resolvers
type ::Types::Ci::JobType.connection_type, null: true
authorize :read_builds
authorizes_object!
+ extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1
argument :statuses, [::Types::Ci::JobStatusEnum],
required: false,
@@ -16,15 +17,6 @@ module Resolvers
alias_method :runner, :object
- def ready?(**args)
- context[self.class] ||= { executions: 0 }
- context[self.class][:executions] += 1
-
- raise GraphQL::ExecutionError, "Jobs can be requested for only one runner at a time" if context[self.class][:executions] > 1
-
- super
- end
-
def resolve_with_lookahead(statuses: nil)
jobs = ::Ci::JobsFinder.new(current_user: current_user, runner: runner, params: { scope: statuses }).execute
diff --git a/app/graphql/resolvers/ci/runner_owner_project_resolver.rb b/app/graphql/resolvers/ci/runner_owner_project_resolver.rb
index 14b5f8f90eb..da8fab93619 100644
--- a/app/graphql/resolvers/ci/runner_owner_project_resolver.rb
+++ b/app/graphql/resolvers/ci/runner_owner_project_resolver.rb
@@ -9,7 +9,7 @@ module Resolvers
alias_method :runner, :object
- def resolve_with_lookahead(**args)
+ def resolve_with_lookahead(**_args)
resolve_owner
end
@@ -19,6 +19,8 @@ module Resolvers
}
end
+ private
+
def filtered_preloads
selection = lookahead
@@ -27,8 +29,6 @@ module Resolvers
end
end
- private
-
def resolve_owner
return unless runner.project_type?
@@ -48,14 +48,13 @@ module Resolvers
.transform_values { |runner_projects| runner_projects.first.project_id }
project_ids = owner_project_id_by_runner_id.values.uniq
- all_preloads = unconditional_includes + filtered_preloads
- owner_relation = Project.all
- owner_relation = owner_relation.preload(*all_preloads) if all_preloads.any?
- projects = owner_relation.where(id: project_ids).index_by(&:id)
+ projects = Project.where(id: project_ids)
+ Preloaders::ProjectPolicyPreloader.new(projects, current_user).execute
+ projects_by_id = projects.index_by(&:id)
runner_ids.each do |runner_id|
owner_project_id = owner_project_id_by_runner_id[runner_id]
- loader.call(runner_id, projects[owner_project_id])
+ loader.call(runner_id, projects_by_id[owner_project_id])
end
# rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/graphql/resolvers/ci/runner_projects_resolver.rb b/app/graphql/resolvers/ci/runner_projects_resolver.rb
new file mode 100644
index 00000000000..ca3b4ebb797
--- /dev/null
+++ b/app/graphql/resolvers/ci/runner_projects_resolver.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Ci
+ class RunnerProjectsResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+ include LooksAhead
+ include ProjectSearchArguments
+
+ type Types::ProjectType.connection_type, null: true
+ authorize :read_runner
+ authorizes_object!
+
+ alias_method :runner, :object
+
+ argument :sort, GraphQL::Types::String,
+ required: false,
+ default_value: 'id_asc', # TODO: Remove in %16.0 and move :sort to ProjectSearchArguments, see https://gitlab.com/gitlab-org/gitlab/-/issues/372117
+ deprecated: {
+ reason: 'Default sort order will change in 16.0. ' \
+ 'Specify `"id_asc"` if query results\' order is important',
+ milestone: '15.4'
+ },
+ description: "Sort order of results. Format: '<field_name>_<sort_direction>', " \
+ "for example: 'id_desc' or 'name_asc'"
+
+ def resolve_with_lookahead(**args)
+ return unless runner.project_type?
+
+ # rubocop:disable CodeReuse/ActiveRecord
+ BatchLoader::GraphQL.for(runner.id).batch(key: :runner_projects) do |runner_ids, loader|
+ plucked_runner_and_project_ids = ::Ci::RunnerProject
+ .select(:runner_id, :project_id)
+ .where(runner_id: runner_ids)
+ .pluck(:runner_id, :project_id)
+
+ project_ids = plucked_runner_and_project_ids.collect { |_runner_id, project_id| project_id }.uniq
+ projects = ProjectsFinder
+ .new(current_user: current_user,
+ params: project_finder_params(args),
+ project_ids_relation: project_ids)
+ .execute
+ Preloaders::ProjectPolicyPreloader.new(projects, current_user).execute
+ projects_by_id = projects.index_by(&:id)
+
+ # In plucked_runner_and_project_ids, first() represents the runner ID, and second() the project ID,
+ # so let's group the project IDs by runner ID
+ runner_project_ids_by_runner_id =
+ plucked_runner_and_project_ids
+ .group_by(&:first)
+ .transform_values { |values| values.map(&:second).filter_map { |project_id| projects_by_id[project_id] } }
+
+ runner_ids.each do |runner_id|
+ runner_projects = runner_project_ids_by_runner_id[runner_id] || []
+
+ loader.call(runner_id, runner_projects)
+ end
+ end
+ # rubocop:enable CodeReuse/ActiveRecord
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/ci/test_suite_resolver.rb b/app/graphql/resolvers/ci/test_suite_resolver.rb
index f758e217b47..a2d3af9c664 100644
--- a/app/graphql/resolvers/ci/test_suite_resolver.rb
+++ b/app/graphql/resolvers/ci/test_suite_resolver.rb
@@ -28,7 +28,8 @@ module Resolvers
def load_test_suite_data(builds)
suite = builds.sum do |build|
- build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
+ test_report = build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
+ test_report.get_suite(build.test_suite_name)
end
Gitlab::Ci::Reports::TestFailureHistory.new(suite.failed.values, pipeline.project).load!
diff --git a/app/graphql/resolvers/concerns/issue_resolver_arguments.rb b/app/graphql/resolvers/concerns/issue_resolver_arguments.rb
index fe213936f55..8295bd58388 100644
--- a/app/graphql/resolvers/concerns/issue_resolver_arguments.rb
+++ b/app/graphql/resolvers/concerns/issue_resolver_arguments.rb
@@ -76,24 +76,11 @@ module IssueResolverArguments
end
def resolve_with_lookahead(**args)
- # The project could have been loaded in batch by `BatchLoader`.
- # At this point we need the `id` of the project to query for issues, so
- # make sure it's loaded and not `nil` before continuing.
- parent = object.respond_to?(:sync) ? object.sync : object
- return Issue.none if parent.nil?
-
- # Will need to be made group & namespace aware with
- # https://gitlab.com/gitlab-org/gitlab-foss/issues/54520
- args[:not] = args[:not].to_h if args[:not].present?
- args[:iids] ||= [args.delete(:iid)].compact if args[:iid]
- args[:attempt_project_search_optimizations] = true if args[:search].present?
+ return Issue.none if resource_parent.nil?
- prepare_assignee_username_params(args)
- prepare_release_tag_params(args)
+ finder = IssuesFinder.new(current_user, prepare_finder_params(args))
- finder = IssuesFinder.new(current_user, args)
-
- continue_issue_resolve(parent, finder, **args)
+ continue_issue_resolve(resource_parent, finder, **args)
end
def ready?(**args)
@@ -103,7 +90,6 @@ module IssueResolverArguments
params_not_mutually_exclusive(args, mutually_exclusive_milestone_args)
params_not_mutually_exclusive(args.fetch(:not, {}), mutually_exclusive_milestone_args)
params_not_mutually_exclusive(args, mutually_exclusive_release_tag_args)
- validate_anonymous_search_access! if args[:search].present?
super
end
@@ -128,6 +114,17 @@ module IssueResolverArguments
private
+ def prepare_finder_params(args)
+ params = super(args)
+ params[:iids] ||= [params.delete(:iid)].compact if params[:iid]
+ params[:attempt_project_search_optimizations] = true if params[:search].present?
+
+ prepare_assignee_username_params(params)
+ prepare_release_tag_params(params)
+
+ params
+ end
+
def prepare_release_tag_params(args)
release_tag_wildcard = args.delete(:release_tag_wildcard_id)
return if release_tag_wildcard.blank?
@@ -135,20 +132,13 @@ module IssueResolverArguments
args[:release_tag] ||= release_tag_wildcard
end
- def mutually_exclusive_release_tag_args
- [:release_tag, :release_tag_wildcard_id]
- end
-
def prepare_assignee_username_params(args)
args[:assignee_username] = args.delete(:assignee_usernames) if args[:assignee_usernames].present?
args[:not][:assignee_username] = args[:not].delete(:assignee_usernames) if args.dig(:not, :assignee_usernames).present?
end
- def params_not_mutually_exclusive(args, mutually_exclusive_args)
- if args.slice(*mutually_exclusive_args).compact.size > 1
- arg_str = mutually_exclusive_args.map { |x| x.to_s.camelize(:lower) }.join(', ')
- raise ::Gitlab::Graphql::Errors::ArgumentError, "only one of [#{arg_str}] arguments is allowed at the same time."
- end
+ def mutually_exclusive_release_tag_args
+ [:release_tag, :release_tag_wildcard_id]
end
def mutually_exclusive_milestone_args
@@ -158,4 +148,20 @@ module IssueResolverArguments
def mutually_exclusive_assignee_username_args
[:assignee_usernames, :assignee_username]
end
+
+ def params_not_mutually_exclusive(args, mutually_exclusive_args)
+ if args.slice(*mutually_exclusive_args).compact.size > 1
+ arg_str = mutually_exclusive_args.map { |x| x.to_s.camelize(:lower) }.join(', ')
+ raise ::Gitlab::Graphql::Errors::ArgumentError, "only one of [#{arg_str}] arguments is allowed at the same time."
+ end
+ end
+
+ def resource_parent
+ # The project could have been loaded in batch by `BatchLoader`.
+ # At this point we need the `id` of the project to query for issues, so
+ # make sure it's loaded and not `nil` before continuing.
+ strong_memoize(:resource_parent) do
+ object.respond_to?(:sync) ? object.sync : object
+ end
+ end
end
diff --git a/app/graphql/resolvers/concerns/looks_ahead.rb b/app/graphql/resolvers/concerns/looks_ahead.rb
index 644b2a11460..b548dc1e175 100644
--- a/app/graphql/resolvers/concerns/looks_ahead.rb
+++ b/app/graphql/resolvers/concerns/looks_ahead.rb
@@ -33,10 +33,14 @@ module LooksAhead
end
def filtered_preloads
- selection = node_selection
+ nodes = node_selection
+
+ return [] unless nodes
+
+ selected_fields = nodes.selections.map(&:name)
preloads.each.flat_map do |name, requirements|
- selection&.selects?(name) ? requirements : []
+ selected_fields.include?(name) ? requirements : []
end
end
diff --git a/app/graphql/resolvers/concerns/project_search_arguments.rb b/app/graphql/resolvers/concerns/project_search_arguments.rb
new file mode 100644
index 00000000000..7e03963f412
--- /dev/null
+++ b/app/graphql/resolvers/concerns/project_search_arguments.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module ProjectSearchArguments
+ extend ActiveSupport::Concern
+
+ included do
+ argument :membership, GraphQL::Types::Boolean,
+ required: false,
+ description: 'Return only projects that the current user is a member of.'
+
+ argument :search, GraphQL::Types::String,
+ required: false,
+ description: 'Search query, which can be for the project name, a path, or a description.'
+
+ argument :search_namespaces, GraphQL::Types::Boolean,
+ required: false,
+ description: 'Include namespace in project search.'
+
+ argument :topics, type: [GraphQL::Types::String],
+ required: false,
+ description: 'Filter projects by topics.'
+ end
+
+ private
+
+ def project_finder_params(params)
+ {
+ without_deleted: true,
+ non_public: params[:membership],
+ search: params[:search],
+ search_namespaces: params[:search_namespaces],
+ sort: params[:sort],
+ topic: params[:topics]
+ }.compact
+ end
+end
diff --git a/app/graphql/resolvers/concerns/search_arguments.rb b/app/graphql/resolvers/concerns/search_arguments.rb
index 7f480f9d0b6..95c6dbf7497 100644
--- a/app/graphql/resolvers/concerns/search_arguments.rb
+++ b/app/graphql/resolvers/concerns/search_arguments.rb
@@ -7,12 +7,49 @@ module SearchArguments
argument :search, GraphQL::Types::String,
required: false,
description: 'Search query for title or description.'
+ argument :in, [Types::IssuableSearchableFieldEnum],
+ required: false,
+ description: <<~DESC
+ Specify the fields to perform the search in.
+ Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'
+ DESC
+ end
+
+ def ready?(**args)
+ validate_search_in_params!(args)
+ validate_anonymous_search_access!(args)
+
+ super
end
- def validate_anonymous_search_access!
+ private
+
+ def validate_anonymous_search_access!(args)
+ return unless args[:search].present?
return if current_user.present? || Feature.disabled?(:disable_anonymous_search, type: :ops)
raise ::Gitlab::Graphql::Errors::ArgumentError,
"User must be authenticated to include the `search` argument."
end
+
+ def validate_search_in_params!(args)
+ return unless args[:in].present? && args[:search].blank?
+
+ raise Gitlab::Graphql::Errors::ArgumentError,
+ '`search` should be present when including the `in` argument'
+ end
+
+ def prepare_finder_params(args)
+ prepare_search_params(args)
+ end
+
+ def prepare_search_params(args)
+ return args unless args[:search].present?
+
+ parent_type = resource_parent.is_a?(Project) ? :project : :group
+ args[:"attempt_#{parent_type}_search_optimizations"] = true
+ args[:in] = args[:in].join(',') if args[:in].present?
+
+ args
+ end
end
diff --git a/app/graphql/resolvers/crm/organization_state_counts_resolver.rb b/app/graphql/resolvers/crm/organization_state_counts_resolver.rb
new file mode 100644
index 00000000000..c16a4bd24ea
--- /dev/null
+++ b/app/graphql/resolvers/crm/organization_state_counts_resolver.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Crm
+ class OrganizationStateCountsResolver < BaseResolver
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ authorize :read_crm_organization
+ authorizes_object!
+
+ type Types::CustomerRelations::OrganizationStateCountsType, null: true
+
+ argument :search, GraphQL::Types::String,
+ required: false,
+ description: 'Search term to find organizations with.'
+
+ argument :state, Types::CustomerRelations::OrganizationStateEnum,
+ required: false,
+ description: 'State of the organizations to search for.'
+
+ def resolve(**args)
+ ::Crm::OrganizationsFinder.counts_by_state(context[:current_user], args.merge({ group: object }))
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/crm/organizations_resolver.rb b/app/graphql/resolvers/crm/organizations_resolver.rb
index ca0a908ee22..719834f406d 100644
--- a/app/graphql/resolvers/crm/organizations_resolver.rb
+++ b/app/graphql/resolvers/crm/organizations_resolver.rb
@@ -10,6 +10,11 @@ module Resolvers
type Types::CustomerRelations::OrganizationType, null: true
+ argument :sort, Types::CustomerRelations::OrganizationSortEnum,
+ description: 'Criteria to sort organizations by.',
+ required: false,
+ default_value: { field: 'name', direction: :asc }
+
argument :search, GraphQL::Types::String,
required: false,
description: 'Search term used to find organizations with.'
@@ -24,6 +29,7 @@ module Resolvers
def resolve(**args)
args[:ids] = resolve_ids(args.delete(:ids))
+ args.delete(:state) if args[:state] == :all
::Crm::OrganizationsFinder.new(current_user, { group: group }.merge(args)).execute
end
diff --git a/app/graphql/resolvers/deployment_resolver.rb b/app/graphql/resolvers/deployment_resolver.rb
new file mode 100644
index 00000000000..7d9ce0f023c
--- /dev/null
+++ b/app/graphql/resolvers/deployment_resolver.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class DeploymentResolver < BaseResolver
+ argument :iid,
+ GraphQL::Types::ID,
+ required: true,
+ description: 'Project-level internal ID of the Deployment.'
+
+ type Types::DeploymentType, null: true
+
+ alias_method :project, :object
+
+ def resolve(iid:)
+ return unless project.present? && project.is_a?(::Project)
+
+ Deployment.for_iid(project, iid)
+ end
+ end
+end
diff --git a/app/graphql/resolvers/deployments_resolver.rb b/app/graphql/resolvers/deployments_resolver.rb
new file mode 100644
index 00000000000..341d23c2ccb
--- /dev/null
+++ b/app/graphql/resolvers/deployments_resolver.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class DeploymentsResolver < BaseResolver
+ argument :statuses, [Types::DeploymentStatusEnum],
+ description: 'Statuses of the deployments.',
+ required: false,
+ as: :status
+
+ argument :order_by, Types::DeploymentsOrderByInputType,
+ description: 'Order by a specified field.',
+ required: false
+
+ type Types::DeploymentType, null: true
+
+ alias_method :environment, :object
+
+ def resolve(**args)
+ return unless environment.present? && environment.is_a?(::Environment)
+
+ args = transform_args_for_finder(**args)
+
+ # GraphQL BatchLoader shouldn't be used here because pagination query will be inefficient
+ # that fetches thousands of rows before limiting and offsetting.
+ DeploymentsFinder.new(environment: environment.id, **args).execute
+ end
+
+ private
+
+ def transform_args_for_finder(**args)
+ if (order_by = args.delete(:order_by))
+ order_by = order_by.to_h.map { |k, v| { order_by: k.to_s, sort: v } }.first
+ args.merge!(order_by)
+ end
+
+ args
+ end
+ end
+end
diff --git a/app/graphql/resolvers/environments/last_deployment_resolver.rb b/app/graphql/resolvers/environments/last_deployment_resolver.rb
new file mode 100644
index 00000000000..76f80112673
--- /dev/null
+++ b/app/graphql/resolvers/environments/last_deployment_resolver.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Environments
+ class LastDeploymentResolver < BaseResolver
+ argument :status,
+ Types::DeploymentStatusEnum,
+ required: true,
+ description: 'Status of the Deployment.'
+
+ type Types::DeploymentType, null: true
+
+ def resolve(status:)
+ return unless object.present? && object.is_a?(::Environment)
+
+ validate!(status)
+
+ find_last_deployment(status)
+ end
+
+ private
+
+ def find_last_deployment(status)
+ BatchLoader::GraphQL.for(object).batch(key: status) do |environments, loader, args|
+ association_name = "last_#{args[:key]}_deployment".to_sym
+
+ Preloaders::Environments::DeploymentPreloader.new(environments)
+ .execute_with_union(association_name, {})
+
+ environments.each do |environment|
+ loader.call(environment, environment.public_send(association_name)) # rubocop:disable GitlabSecurity/PublicSend
+ end
+ end
+ end
+
+ def validate!(status)
+ unless Deployment::FINISHED_STATUSES.include?(status.to_sym) ||
+ Deployment::UPCOMING_STATUSES.include?(status.to_sym)
+ raise Gitlab::Graphql::Errors::ArgumentError, "\"#{status}\" status is not supported."
+ end
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/environments_resolver.rb b/app/graphql/resolvers/environments_resolver.rb
index 934c1ba2738..f265e2183d0 100644
--- a/app/graphql/resolvers/environments_resolver.rb
+++ b/app/graphql/resolvers/environments_resolver.rb
@@ -21,8 +21,8 @@ module Resolvers
def resolve(**args)
return unless project.present?
- Environments::EnvironmentsFinder.new(project, context[:current_user], args).execute
- rescue Environments::EnvironmentsFinder::InvalidStatesError => e
+ ::Environments::EnvironmentsFinder.new(project, context[:current_user], args).execute
+ rescue ::Environments::EnvironmentsFinder::InvalidStatesError => e
raise Gitlab::Graphql::Errors::ArgumentError, e.message
end
end
diff --git a/app/graphql/resolvers/group_packages_resolver.rb b/app/graphql/resolvers/group_packages_resolver.rb
index b48e0b75190..e6a6abb39dd 100644
--- a/app/graphql/resolvers/group_packages_resolver.rb
+++ b/app/graphql/resolvers/group_packages_resolver.rb
@@ -5,6 +5,8 @@ module Resolvers
class GroupPackagesResolver < PackagesBaseResolver
# The GraphQL type is defined in the extended class
+ extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1
+
argument :sort, Types::Packages::PackageGroupSortEnum,
description: 'Sort packages by this criteria.',
required: false,
@@ -15,14 +17,6 @@ module Resolvers
project_path_asc: { order_by: 'project_path', sort: 'asc' }
}).freeze
- def ready?(**args)
- context[self.class] ||= { executions: 0 }
- context[self.class][:executions] += 1
- raise GraphQL::ExecutionError, "Packages can be requested only for one group at a time" if context[self.class][:executions] > 1
-
- super
- end
-
def resolve(sort:, **filters)
return unless packages_available?
diff --git a/app/graphql/resolvers/members_resolver.rb b/app/graphql/resolvers/members_resolver.rb
index 827db54134a..3d7894fdd6a 100644
--- a/app/graphql/resolvers/members_resolver.rb
+++ b/app/graphql/resolvers/members_resolver.rb
@@ -11,6 +11,10 @@ module Resolvers
required: false,
description: 'Search query.'
+ argument :sort, ::Types::MemberSortEnum,
+ required: false,
+ description: 'sort query.'
+
def resolve_with_lookahead(**args)
authorize!(object)
diff --git a/app/graphql/resolvers/package_details_resolver.rb b/app/graphql/resolvers/package_details_resolver.rb
index 705d3900cd2..b77c6b1112b 100644
--- a/app/graphql/resolvers/package_details_resolver.rb
+++ b/app/graphql/resolvers/package_details_resolver.rb
@@ -2,20 +2,14 @@
module Resolvers
class PackageDetailsResolver < BaseResolver
+ extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1
+
type ::Types::Packages::PackageDetailsType, null: true
argument :id, ::Types::GlobalIDType[::Packages::Package],
required: true,
description: 'Global ID of the package.'
- def ready?(**args)
- context[self.class] ||= { executions: 0 }
- context[self.class][:executions] += 1
- raise GraphQL::ExecutionError, "Package details can be requested only for one package at a time" if context[self.class][:executions] > 1
-
- super
- end
-
def resolve(id:)
GitlabSchema.find_by_gid(id)
end
diff --git a/app/graphql/resolvers/project_jobs_resolver.rb b/app/graphql/resolvers/project_jobs_resolver.rb
index b09158d475d..4d13a4a3fae 100644
--- a/app/graphql/resolvers/project_jobs_resolver.rb
+++ b/app/graphql/resolvers/project_jobs_resolver.rb
@@ -8,6 +8,7 @@ module Resolvers
type ::Types::Ci::JobType.connection_type, null: true
authorize :read_build
authorizes_object!
+ extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1
argument :statuses, [::Types::Ci::JobStatusEnum],
required: false,
@@ -15,15 +16,6 @@ module Resolvers
alias_method :project, :object
- def ready?(**args)
- context[self.class] ||= { executions: 0 }
- context[self.class][:executions] += 1
-
- raise GraphQL::ExecutionError, "Jobs can be requested for only one project at a time" if context[self.class][:executions] > 1
-
- super
- end
-
def resolve_with_lookahead(statuses: nil)
jobs = ::Ci::JobsFinder.new(current_user: current_user, project: project, params: { scope: statuses }).execute
diff --git a/app/graphql/resolvers/projects/branch_rules_resolver.rb b/app/graphql/resolvers/projects/branch_rules_resolver.rb
new file mode 100644
index 00000000000..6c8b416bcea
--- /dev/null
+++ b/app/graphql/resolvers/projects/branch_rules_resolver.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Resolvers
+ module Projects
+ class BranchRulesResolver < BaseResolver
+ type Types::Projects::BranchRuleType.connection_type, null: false
+
+ alias_method :project, :object
+
+ def resolve(**args)
+ project.protected_branches
+ end
+ end
+ end
+end
diff --git a/app/graphql/resolvers/projects_resolver.rb b/app/graphql/resolvers/projects_resolver.rb
index facf8ffe36f..4d1e1b867da 100644
--- a/app/graphql/resolvers/projects_resolver.rb
+++ b/app/graphql/resolvers/projects_resolver.rb
@@ -2,31 +2,18 @@
module Resolvers
class ProjectsResolver < BaseResolver
- type Types::ProjectType, null: true
-
- argument :membership, GraphQL::Types::Boolean,
- required: false,
- description: 'Limit projects that the current user is a member of.'
+ include ProjectSearchArguments
- argument :search, GraphQL::Types::String,
- required: false,
- description: 'Search query for project name, path, or description.'
+ type Types::ProjectType, null: true
argument :ids, [GraphQL::Types::ID],
required: false,
description: 'Filter projects by IDs.'
- argument :search_namespaces, GraphQL::Types::Boolean,
- required: false,
- description: 'Include namespace in project search.'
-
argument :sort, GraphQL::Types::String,
required: false,
- description: 'Sort order of results.'
-
- argument :topics, type: [GraphQL::Types::String],
- required: false,
- description: 'Filters projects by topics.'
+ description: "Sort order of results. Format: '<field_name>_<sort_direction>', " \
+ "for example: 'id_desc' or 'name_asc'"
def resolve(**args)
ProjectsFinder
@@ -36,17 +23,6 @@ module Resolvers
private
- def project_finder_params(params)
- {
- without_deleted: true,
- non_public: params[:membership],
- search: params[:search],
- search_namespaces: params[:search_namespaces],
- sort: params[:sort],
- topic: params[:topics]
- }.compact
- end
-
def parse_gids(gids)
gids&.map { |gid| GitlabSchema.parse_gid(gid, expected_type: ::Project).model_id }
end
diff --git a/app/graphql/resolvers/work_items_resolver.rb b/app/graphql/resolvers/work_items_resolver.rb
index 055984db3cb..9c7931a4edb 100644
--- a/app/graphql/resolvers/work_items_resolver.rb
+++ b/app/graphql/resolvers/work_items_resolver.rb
@@ -26,27 +26,31 @@ module Resolvers
required: false
def resolve_with_lookahead(**args)
- # The project could have been loaded in batch by `BatchLoader`.
- # At this point we need the `id` of the project to query for issues, so
- # make sure it's loaded and not `nil` before continuing.
- parent = object.respond_to?(:sync) ? object.sync : object
- return WorkItem.none if parent.nil? || !parent.work_items_feature_flag_enabled?
+ return WorkItem.none if resource_parent.nil? || !resource_parent.work_items_feature_flag_enabled?
- args[:iids] ||= [args.delete(:iid)].compact if args[:iid]
- args[:attempt_project_search_optimizations] = true if args[:search].present?
+ finder = ::WorkItems::WorkItemsFinder.new(current_user, prepare_finder_params(args))
- finder = ::WorkItems::WorkItemsFinder.new(current_user, args)
-
- Gitlab::Graphql::Loaders::IssuableLoader.new(parent, finder).batching_find_all { |q| apply_lookahead(q) }
+ Gitlab::Graphql::Loaders::IssuableLoader.new(resource_parent, finder).batching_find_all { |q| apply_lookahead(q) }
end
- def ready?(**args)
- validate_anonymous_search_access! if args[:search].present?
+ private
- super
+ def preloads
+ {
+ last_edited_by: :last_edited_by
+ }
end
- private
+ # Allows to apply lookahead for fields
+ # selected from WidgetInterface
+ override :node_selection
+ def node_selection
+ selected_fields = super
+
+ return unless selected_fields
+
+ selected_fields.selection(:widgets)
+ end
def unconditional_includes
[
@@ -56,6 +60,22 @@ module Resolvers
:author
]
end
+
+ def prepare_finder_params(args)
+ params = super(args)
+ params[:iids] ||= [params.delete(:iid)].compact if params[:iid]
+
+ params
+ end
+
+ def resource_parent
+ # The project could have been loaded in batch by `BatchLoader`.
+ # At this point we need the `id` of the project to query for work items, so
+ # make sure it's loaded and not `nil` before continuing.
+ strong_memoize(:resource_parent) do
+ object.respond_to?(:sync) ? object.sync : object
+ end
+ end
end
end
diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb
index 1c43432594a..6f64e5b5053 100644
--- a/app/graphql/types/base_field.rb
+++ b/app/graphql/types/base_field.rb
@@ -17,8 +17,6 @@ module Types
@requires_argument = !!kwargs.delete(:requires_argument)
@authorize = Array.wrap(kwargs.delete(:authorize))
kwargs[:complexity] = field_complexity(kwargs[:resolver_class], kwargs[:complexity])
- @feature_flag = kwargs[:_deprecated_feature_flag]
- kwargs = check_feature_flag(kwargs)
@deprecation = gitlab_deprecation(kwargs)
after_connection_extensions = kwargs.delete(:late_extensions) || []
@@ -91,16 +89,8 @@ module Types
@constant_complexity
end
- def visible?(context)
- return false if feature_flag.present? && !Feature.enabled?(feature_flag)
-
- super
- end
-
private
- attr_reader :feature_flag
-
def field_authorized?(object, ctx)
object = object.node if object.is_a?(GraphQL::Pagination::Connection::Edge)
@@ -123,27 +113,6 @@ module Types
@authorization ||= ::Gitlab::Graphql::Authorize::ObjectAuthorization.new(@authorize)
end
- def feature_documentation_message(key, description)
- message_parts = ["#{description} Available only when feature flag `#{key}` is enabled."]
-
- message_parts << if Feature::Definition.has_definition?(key) && Feature::Definition.default_enabled?(key)
- "This flag is enabled by default."
- else
- "This flag is disabled by default, because the feature is experimental and is subject to change without notice."
- end
-
- message_parts.join(' ')
- end
-
- def check_feature_flag(args)
- ff = args.delete(:_deprecated_feature_flag)
- return args unless ff.present?
-
- args[:description] = feature_documentation_message(ff, args[:description])
-
- args
- end
-
def field_complexity(resolver_class, current)
return current if current.present? && current > 0
diff --git a/app/graphql/types/branch_protections/base_access_level_type.rb b/app/graphql/types/branch_protections/base_access_level_type.rb
new file mode 100644
index 00000000000..472733a6bc5
--- /dev/null
+++ b/app/graphql/types/branch_protections/base_access_level_type.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Types
+ module BranchProtections
+ class BaseAccessLevelType < Types::BaseObject
+ authorize :read_protected_branch
+
+ field :access_level,
+ type: GraphQL::Types::Int,
+ null: false,
+ description: 'GitLab::Access level.'
+
+ field :access_level_description,
+ type: GraphQL::Types::String,
+ null: false,
+ description: 'Human readable representation for this access level.',
+ hash_key: 'humanize'
+ end
+ end
+end
+
+Types::BranchProtections::BaseAccessLevelType.prepend_mod
diff --git a/app/graphql/types/branch_protections/merge_access_level_type.rb b/app/graphql/types/branch_protections/merge_access_level_type.rb
new file mode 100644
index 00000000000..85295e1ba25
--- /dev/null
+++ b/app/graphql/types/branch_protections/merge_access_level_type.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Types
+ module BranchProtections
+ class MergeAccessLevelType < BaseAccessLevelType # rubocop:disable Graphql/AuthorizeTypes
+ graphql_name 'MergeAccessLevel'
+ description 'Represents the merge access level of a branch protection.'
+ accepts ::ProtectedBranch::MergeAccessLevel
+ end
+ end
+end
diff --git a/app/graphql/types/branch_protections/push_access_level_type.rb b/app/graphql/types/branch_protections/push_access_level_type.rb
new file mode 100644
index 00000000000..bfbdc4edbea
--- /dev/null
+++ b/app/graphql/types/branch_protections/push_access_level_type.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Types
+ module BranchProtections
+ class PushAccessLevelType < BaseAccessLevelType # rubocop:disable Graphql/AuthorizeTypes
+ graphql_name 'PushAccessLevel'
+ description 'Represents the push access level of a branch protection.'
+ accepts ::ProtectedBranch::PushAccessLevel
+ end
+ end
+end
diff --git a/app/graphql/types/branch_rules/branch_protection_type.rb b/app/graphql/types/branch_rules/branch_protection_type.rb
new file mode 100644
index 00000000000..4177a6f92a1
--- /dev/null
+++ b/app/graphql/types/branch_rules/branch_protection_type.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Types
+ module BranchRules
+ class BranchProtectionType < BaseObject
+ graphql_name 'BranchProtection'
+ description 'Branch protection details for a branch rule.'
+ accepts ::ProtectedBranch
+ authorize :read_protected_branch
+
+ field :merge_access_levels,
+ type: Types::BranchProtections::MergeAccessLevelType.connection_type,
+ null: true,
+ description: 'Details about who can merge when this branch is the source branch.'
+
+ field :push_access_levels,
+ type: Types::BranchProtections::PushAccessLevelType.connection_type,
+ null: true,
+ description: 'Details about who can push when this branch is the source branch.'
+
+ field :allow_force_push,
+ type: GraphQL::Types::Boolean,
+ null: false,
+ description: 'Toggle force push to the branch for users with write access.'
+ end
+ end
+end
+
+Types::BranchRules::BranchProtectionType.prepend_mod_with('Types::BranchRules::BranchProtectionType')
diff --git a/app/graphql/types/ci/config_variable_type.rb b/app/graphql/types/ci/config_variable_type.rb
new file mode 100644
index 00000000000..87ae026c2c1
--- /dev/null
+++ b/app/graphql/types/ci/config_variable_type.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ class ConfigVariableType < BaseObject # rubocop:disable Graphql/AuthorizeTypes
+ graphql_name 'CiConfigVariable'
+ description 'CI/CD config variables.'
+
+ field :key, GraphQL::Types::String,
+ null: true,
+ description: 'Name of the variable.'
+
+ field :description, GraphQL::Types::String,
+ null: true,
+ description: 'Description for the CI/CD config variable.'
+
+ field :value, GraphQL::Types::String,
+ null: true,
+ description: 'Value of the variable.'
+ end
+ end
+end
diff --git a/app/graphql/types/ci/group_variable_connection_type.rb b/app/graphql/types/ci/group_variable_connection_type.rb
new file mode 100644
index 00000000000..1f55dde6697
--- /dev/null
+++ b/app/graphql/types/ci/group_variable_connection_type.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ class GroupVariableConnectionType < GraphQL::Types::Relay::BaseConnection
+ field :limit, GraphQL::Types::Int,
+ null: false,
+ description: 'Maximum amount of group CI/CD variables.'
+
+ def limit
+ ::Plan.default.actual_limits.group_ci_variables
+ end
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/ci/group_variable_type.rb b/app/graphql/types/ci/group_variable_type.rb
index 3322f741342..f9ed54f0d10 100644
--- a/app/graphql/types/ci/group_variable_type.rb
+++ b/app/graphql/types/ci/group_variable_type.rb
@@ -7,19 +7,20 @@ module Types
graphql_name 'CiGroupVariable'
description 'CI/CD variables for a group.'
+ connection_type_class(Types::Ci::GroupVariableConnectionType)
implements(VariableInterface)
field :environment_scope, GraphQL::Types::String,
- null: true,
- description: 'Scope defining the environments that can use the variable.'
-
- field :protected, GraphQL::Types::Boolean,
- null: true,
- description: 'Indicates whether the variable is protected.'
+ null: true,
+ description: 'Scope defining the environments that can use the variable.'
field :masked, GraphQL::Types::Boolean,
- null: true,
- description: 'Indicates whether the variable is masked.'
+ null: true,
+ description: 'Indicates whether the variable is masked.'
+
+ field :protected, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates whether the variable is protected.'
end
end
end
diff --git a/app/graphql/types/ci/instance_variable_type.rb b/app/graphql/types/ci/instance_variable_type.rb
index f564a2f59a0..7ffc52deb73 100644
--- a/app/graphql/types/ci/instance_variable_type.rb
+++ b/app/graphql/types/ci/instance_variable_type.rb
@@ -9,21 +9,29 @@ module Types
implements(VariableInterface)
+ field :id, GraphQL::Types::ID,
+ null: false,
+ description: 'ID of the variable.'
+
field :environment_scope, GraphQL::Types::String,
- null: true,
- deprecated: {
- reason: 'No longer used, only available for GroupVariableType and ProjectVariableType',
- milestone: '15.3'
- },
- description: 'Scope defining the environments that can use the variable.'
+ null: true,
+ deprecated: {
+ reason: 'No longer used, only available for GroupVariableType and ProjectVariableType',
+ milestone: '15.3'
+ },
+ description: 'Scope defining the environments that can use the variable.'
field :protected, GraphQL::Types::Boolean,
- null: true,
- description: 'Indicates whether the variable is protected.'
+ null: true,
+ description: 'Indicates whether the variable is protected.'
field :masked, GraphQL::Types::Boolean,
- null: true,
- description: 'Indicates whether the variable is masked.'
+ null: true,
+ description: 'Indicates whether the variable is masked.'
+
+ field :raw, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates whether the variable is raw.'
def environment_scope
nil
diff --git a/app/graphql/types/ci/job_artifact_type.rb b/app/graphql/types/ci/job_artifact_type.rb
index a6ab445702c..6346d50de3a 100644
--- a/app/graphql/types/ci/job_artifact_type.rb
+++ b/app/graphql/types/ci/job_artifact_type.rb
@@ -6,6 +6,9 @@ module Types
class JobArtifactType < BaseObject
graphql_name 'CiJobArtifact'
+ field :id, Types::GlobalIDType[::Ci::JobArtifact], null: false,
+ description: 'ID of the artifact.'
+
field :download_path, GraphQL::Types::String, null: true,
description: "URL for downloading the artifact's file."
@@ -16,6 +19,12 @@ module Types
description: 'File name of the artifact.',
method: :filename
+ field :size, GraphQL::Types::Int, null: false,
+ description: 'Size of the artifact in bytes.'
+
+ field :expire_at, Types::TimeType, null: true,
+ description: 'Expiry date of the artifact.'
+
def download_path
::Gitlab::Routing.url_helpers.download_project_job_artifacts_path(
object.project,
diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb
index 4ea9a016e74..ab6103d9469 100644
--- a/app/graphql/types/ci/job_type.rb
+++ b/app/graphql/types/ci/job_type.rb
@@ -92,6 +92,8 @@ module Types
description: 'Indicates the job is stuck.'
field :triggered, GraphQL::Types::Boolean, null: true,
description: 'Whether the job was triggered.'
+ field :web_path, GraphQL::Types::String, null: true,
+ description: 'Web path of the job.'
def kind
return ::Ci::Build unless [::Ci::Build, ::Ci::Bridge].include?(object.class)
@@ -181,6 +183,10 @@ module Types
::Gitlab::Routing.url_helpers.project_commits_path(object.project, ref_name)
end
+ def web_path
+ ::Gitlab::Routing.url_helpers.project_job_path(object.project, object)
+ end
+
def coverage
object&.coverage
end
diff --git a/app/graphql/types/ci/manual_variable_type.rb b/app/graphql/types/ci/manual_variable_type.rb
index d6f59c1d249..ed92a6645b4 100644
--- a/app/graphql/types/ci/manual_variable_type.rb
+++ b/app/graphql/types/ci/manual_variable_type.rb
@@ -10,12 +10,12 @@ module Types
implements(VariableInterface)
field :environment_scope, GraphQL::Types::String,
- null: true,
- deprecated: {
- reason: 'No longer used, only available for GroupVariableType and ProjectVariableType',
- milestone: '15.3'
- },
- description: 'Scope defining the environments that can use the variable.'
+ null: true,
+ deprecated: {
+ reason: 'No longer used, only available for GroupVariableType and ProjectVariableType',
+ milestone: '15.3'
+ },
+ description: 'Scope defining the environments that can use the variable.'
def environment_scope
nil
diff --git a/app/graphql/types/ci/project_variable_connection_type.rb b/app/graphql/types/ci/project_variable_connection_type.rb
new file mode 100644
index 00000000000..c3cdc425f10
--- /dev/null
+++ b/app/graphql/types/ci/project_variable_connection_type.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ class ProjectVariableConnectionType < GraphQL::Types::Relay::BaseConnection
+ field :limit, GraphQL::Types::Int,
+ null: false,
+ description: 'Maximum amount of project CI/CD variables.'
+
+ def limit
+ ::Plan.default.actual_limits.project_ci_variables
+ end
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/graphql/types/ci/project_variable_type.rb b/app/graphql/types/ci/project_variable_type.rb
index 625bb7fd4b1..2a5375045e5 100644
--- a/app/graphql/types/ci/project_variable_type.rb
+++ b/app/graphql/types/ci/project_variable_type.rb
@@ -7,19 +7,20 @@ module Types
graphql_name 'CiProjectVariable'
description 'CI/CD variables for a project.'
+ connection_type_class(Types::Ci::ProjectVariableConnectionType)
implements(VariableInterface)
field :environment_scope, GraphQL::Types::String,
- null: true,
- description: 'Scope defining the environments that can use the variable.'
+ null: true,
+ description: 'Scope defining the environments that can use the variable.'
field :protected, GraphQL::Types::Boolean,
- null: true,
- description: 'Indicates whether the variable is protected.'
+ null: true,
+ description: 'Indicates whether the variable is protected.'
field :masked, GraphQL::Types::Boolean,
- null: true,
- description: 'Indicates whether the variable is masked.'
+ null: true,
+ description: 'Indicates whether the variable is masked.'
end
end
end
diff --git a/app/graphql/types/ci/runner_membership_filter_enum.rb b/app/graphql/types/ci/runner_membership_filter_enum.rb
index 2e1051b2151..4fd7e0749b0 100644
--- a/app/graphql/types/ci/runner_membership_filter_enum.rb
+++ b/app/graphql/types/ci/runner_membership_filter_enum.rb
@@ -3,15 +3,17 @@
module Types
module Ci
class RunnerMembershipFilterEnum < BaseEnum
- graphql_name 'RunnerMembershipFilter'
- description 'Values for filtering runners in namespaces.'
+ graphql_name 'CiRunnerMembershipFilter'
+ description 'Values for filtering runners in namespaces. ' \
+ 'The previous type name `RunnerMembershipFilter` was deprecated in 15.4.'
value 'DIRECT',
description: "Include runners that have a direct relationship.",
value: :direct
value 'DESCENDANTS',
- description: "Include runners that have either a direct relationship or a relationship with descendants. These can be project runners or group runners (in the case where group is queried).",
+ description: "Include runners that have either a direct or inherited relationship. " \
+ "These runners can be specific to a project or a group.",
value: :descendants
end
end
diff --git a/app/graphql/types/ci/runner_type.rb b/app/graphql/types/ci/runner_type.rb
index 0afb61d2b64..a9c76974850 100644
--- a/app/graphql/types/ci/runner_type.rb
+++ b/app/graphql/types/ci/runner_type.rb
@@ -52,7 +52,7 @@ module Types
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 :jobs, ::Types::Ci::JobType.connection_type, null: true,
- description: 'Jobs assigned to the runner.',
+ 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,
@@ -63,8 +63,11 @@ module Types
description: 'Indicates the runner is paused and not available to run jobs.'
field :project_count, GraphQL::Types::Int, null: true,
description: 'Number of projects that the runner is associated with.'
- field :projects, ::Types::ProjectType.connection_type, null: true,
- description: 'Projects the runner is associated with. For project runners only.'
+ field :projects,
+ ::Types::ProjectType.connection_type,
+ null: true,
+ resolver: ::Resolvers::Ci::RunnerProjectsResolver,
+ description: 'Find projects the runner is associated with. For project runners only.'
field :revision, GraphQL::Types::String, null: true,
description: 'Revision of the runner.'
field :run_untagged, GraphQL::Types::Boolean, null: false,
@@ -131,12 +134,6 @@ module Types
batched_owners(::Ci::RunnerNamespace, Group, :runner_groups, :namespace_id)
end
- def projects
- return unless runner.project_type?
-
- batched_owners(::Ci::RunnerProject, Project, :runner_projects, :project_id)
- end
-
private
def can_admin_runners?
@@ -159,19 +156,12 @@ module Types
owner_ids = runner_owner_ids_by_runner_id.values.flatten.uniq
owners = assoc_type.where(id: owner_ids).index_by(&:id)
- # Preload projects namespaces to avoid N+1 queries when checking the `read_project` policy for each
- preload_projects_namespaces(owners.values) if assoc_type == Project
-
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
-
- def preload_projects_namespaces(_projects)
- # overridden in EE
- end
end
end
end
diff --git a/app/graphql/types/ci/variable_interface.rb b/app/graphql/types/ci/variable_interface.rb
index 82c9ba7121c..ec68d3c987c 100644
--- a/app/graphql/types/ci/variable_interface.rb
+++ b/app/graphql/types/ci/variable_interface.rb
@@ -8,24 +8,24 @@ module Types
graphql_name 'CiVariable'
field :id, GraphQL::Types::ID,
- null: false,
- description: 'ID of the variable.'
+ null: false,
+ description: 'ID of the variable.'
field :key, GraphQL::Types::String,
- null: true,
- description: 'Name of the variable.'
+ null: true,
+ description: 'Name of the variable.'
+
+ field :raw, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates whether the variable is raw.'
field :value, GraphQL::Types::String,
- null: true,
- description: 'Value of the variable.'
+ null: true,
+ description: 'Value of the variable.'
field :variable_type, ::Types::Ci::VariableTypeEnum,
- null: true,
- description: 'Type of the variable.'
-
- field :raw, GraphQL::Types::Boolean,
- null: true,
- description: 'Indicates whether the variable is raw.'
+ null: true,
+ description: 'Type of the variable.'
end
end
end
diff --git a/app/graphql/types/clusters/agent_type.rb b/app/graphql/types/clusters/agent_type.rb
index 546252b2285..5d7b8815cde 100644
--- a/app/graphql/types/clusters/agent_type.rb
+++ b/app/graphql/types/clusters/agent_type.rb
@@ -71,3 +71,5 @@ module Types
end
end
end
+
+Types::Clusters::AgentType.prepend_mod
diff --git a/app/graphql/types/customer_relations/contact_sort_enum.rb b/app/graphql/types/customer_relations/contact_sort_enum.rb
index 221dedacb6a..bb11d741368 100644
--- a/app/graphql/types/customer_relations/contact_sort_enum.rb
+++ b/app/graphql/types/customer_relations/contact_sort_enum.rb
@@ -11,10 +11,10 @@ module Types
sortable_fields.each do |field|
value "#{field.upcase.tr(' ', '_')}_ASC",
value: { field: field.downcase.tr(' ', '_'), direction: :asc },
- description: "#{field} by ascending order."
+ description: "#{field} in ascending order."
value "#{field.upcase.tr(' ', '_')}_DESC",
value: { field: field.downcase.tr(' ', '_'), direction: :desc },
- description: "#{field} by descending order."
+ description: "#{field} in descending order."
end
end
end
diff --git a/app/graphql/types/customer_relations/organization_sort_enum.rb b/app/graphql/types/customer_relations/organization_sort_enum.rb
new file mode 100644
index 00000000000..742a5a4fa99
--- /dev/null
+++ b/app/graphql/types/customer_relations/organization_sort_enum.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Types
+ module CustomerRelations
+ class OrganizationSortEnum < SortEnum
+ graphql_name 'OrganizationSort'
+ description 'Values for sorting organizations'
+
+ sortable_fields = ['Name', 'Description', 'Default Rate']
+
+ sortable_fields.each do |field|
+ value "#{field.upcase.tr(' ', '_')}_ASC",
+ value: { field: field.downcase.tr(' ', '_'), direction: :asc },
+ description: "#{field} in ascending order."
+ value "#{field.upcase.tr(' ', '_')}_DESC",
+ value: { field: field.downcase.tr(' ', '_'), direction: :desc },
+ description: "#{field} in descending order."
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/customer_relations/organization_state_counts_type.rb b/app/graphql/types/customer_relations/organization_state_counts_type.rb
new file mode 100644
index 00000000000..7d813209a8e
--- /dev/null
+++ b/app/graphql/types/customer_relations/organization_state_counts_type.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Types
+ module CustomerRelations
+ # `object` is a hash. Authorization is performed by OrganizationStateCountsResolver
+ class OrganizationStateCountsType < Types::BaseObject # rubocop:disable Graphql/AuthorizeTypes
+ graphql_name 'OrganizationStateCounts'
+ description 'Represents the total number of organizations for the represented states.'
+
+ AVAILABLE_STATES = ::CustomerRelations::Organization.states.keys.push('all').freeze
+
+ AVAILABLE_STATES.each do |state|
+ field state,
+ GraphQL::Types::Int,
+ null: true,
+ description: "Number of organizations with state `#{state.upcase}`"
+ end
+
+ def all
+ object.values.sum
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/customer_relations/organization_state_enum.rb b/app/graphql/types/customer_relations/organization_state_enum.rb
index ecdd7d092ad..84bbbbc90fc 100644
--- a/app/graphql/types/customer_relations/organization_state_enum.rb
+++ b/app/graphql/types/customer_relations/organization_state_enum.rb
@@ -5,12 +5,16 @@ module Types
class OrganizationStateEnum < BaseEnum
graphql_name 'CustomerRelationsOrganizationState'
+ value 'all',
+ description: "All available organizations.",
+ value: :all
+
value 'active',
- description: "Active organization.",
+ description: "Active organizations.",
value: :active
value 'inactive',
- description: "Inactive organization.",
+ description: "Inactive organizations.",
value: :inactive
end
end
diff --git a/app/graphql/types/deployment_details_type.rb b/app/graphql/types/deployment_details_type.rb
new file mode 100644
index 00000000000..f8ba0cb1b24
--- /dev/null
+++ b/app/graphql/types/deployment_details_type.rb
@@ -0,0 +1,15 @@
+# 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
diff --git a/app/graphql/types/deployment_status_enum.rb b/app/graphql/types/deployment_status_enum.rb
new file mode 100644
index 00000000000..7ef69d3f1c1
--- /dev/null
+++ b/app/graphql/types/deployment_status_enum.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Types
+ class DeploymentStatusEnum < BaseEnum
+ graphql_name 'DeploymentStatus'
+ description 'All deployment statuses.'
+
+ ::Deployment.statuses.each_key do |status|
+ value status.upcase,
+ description: "A deployment that is #{status.tr('_', ' ')}.",
+ value: status
+ end
+ end
+end
diff --git a/app/graphql/types/deployment_tag_type.rb b/app/graphql/types/deployment_tag_type.rb
new file mode 100644
index 00000000000..bc3597404a2
--- /dev/null
+++ b/app/graphql/types/deployment_tag_type.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Types
+ # DeploymentTagType is a hash, authorized by the deployment
+ # rubocop:disable Graphql/AuthorizeTypes
+ class DeploymentTagType < BaseObject
+ graphql_name 'DeploymentTag'
+ description 'Tags for a given deployment'
+
+ field :name,
+ GraphQL::Types::String,
+ description: 'Name of this git tag.'
+
+ field :path,
+ GraphQL::Types::String,
+ description: 'Path for this tag.',
+ hash_key: :path
+ end
+ # rubocop:enable Graphql/AuthorizeTypes
+end
diff --git a/app/graphql/types/deployment_type.rb b/app/graphql/types/deployment_type.rb
new file mode 100644
index 00000000000..70a3a4cb574
--- /dev/null
+++ b/app/graphql/types/deployment_type.rb
@@ -0,0 +1,69 @@
+# 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'
+
+ present_using Deployments::DeploymentPresenter
+
+ authorize :read_deployment
+
+ field :id,
+ GraphQL::Types::ID,
+ description: 'Global ID of the deployment.'
+
+ field :iid,
+ GraphQL::Types::ID,
+ description: 'Project-level internal ID of the deployment.'
+
+ field :ref,
+ GraphQL::Types::String,
+ description: 'Git-Ref that the deployment ran on.'
+
+ field :tag,
+ GraphQL::Types::Boolean,
+ description: 'True or false if the deployment ran on a Git-tag.'
+
+ field :sha,
+ GraphQL::Types::String,
+ description: 'Git-SHA that the deployment ran on.'
+
+ field :created_at,
+ Types::TimeType,
+ description: 'When the deployment record was created.'
+
+ field :updated_at,
+ Types::TimeType,
+ description: 'When the deployment record was updated.'
+
+ field :finished_at,
+ Types::TimeType,
+ description: 'When the deployment finished.'
+
+ field :status,
+ Types::DeploymentStatusEnum,
+ description: 'Status of the deployment.'
+
+ field :commit,
+ Types::CommitType,
+ description: 'Commit details of the deployment.',
+ calls_gitaly: true
+
+ field :job,
+ Types::Ci::JobType,
+ description: 'Pipeline job of the deployment.',
+ method: :build
+
+ field :triggerer,
+ Types::UserType,
+ description: 'User who executed the deployment.',
+ method: :deployed_by
+ end
+end
diff --git a/app/graphql/types/deployments_order_by_input_type.rb b/app/graphql/types/deployments_order_by_input_type.rb
new file mode 100644
index 00000000000..a87fef9fe8a
--- /dev/null
+++ b/app/graphql/types/deployments_order_by_input_type.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Types
+ class DeploymentsOrderByInputType < BaseInputObject
+ graphql_name 'DeploymentsOrderByInput'
+ description 'Values for ordering deployments by a specific field'
+
+ argument :created_at,
+ Types::SortDirectionEnum,
+ required: false,
+ description: 'Order by Created time.'
+
+ argument :finished_at,
+ Types::SortDirectionEnum,
+ required: false,
+ description: 'Order by Finished time.'
+
+ def prepare
+ raise GraphQL::ExecutionError, 'orderBy parameter must contain one key-value pair.' unless to_h.size == 1
+
+ super
+ end
+ end
+end
diff --git a/app/graphql/types/environment_type.rb b/app/graphql/types/environment_type.rb
index 2a7076cc3c9..eb4e7b1dabf 100644
--- a/app/graphql/types/environment_type.rb
+++ b/app/graphql/types/environment_type.rb
@@ -21,6 +21,30 @@ module Types
field :path, GraphQL::Types::String, null: false,
description: 'Path to the environment.'
+ field :slug, GraphQL::Types::String,
+ description: 'Slug of the environment.'
+
+ field :external_url, GraphQL::Types::String, null: true,
+ description: 'External URL of the environment.'
+
+ field :created_at, Types::TimeType,
+ description: 'When the environment was created.'
+
+ field :updated_at, Types::TimeType,
+ description: 'When the environment was updated.'
+
+ field :auto_stop_at, Types::TimeType,
+ description: 'When the environment is going to be stopped automatically.'
+
+ field :auto_delete_at, Types::TimeType,
+ description: 'When the environment is going to be deleted automatically.'
+
+ field :tier, Types::DeploymentTierEnum,
+ description: 'Deployment tier of the environment.'
+
+ field :environment_type, GraphQL::Types::String,
+ description: 'Folder name of the environment.'
+
field :metrics_dashboard, Types::Metrics::DashboardType, null: true,
description: 'Metrics dashboard schema for the environment.',
resolver: Resolvers::Metrics::DashboardResolver
@@ -29,5 +53,22 @@ module Types
Types::AlertManagement::AlertType,
null: true,
description: 'Most severe open alert for the environment. If multiple alerts have equal severity, the most recent is returned.'
+
+ field :deployments,
+ Types::DeploymentType.connection_type,
+ null: true,
+ description: 'Deployments of the environment. This field can only be resolved for one project in any single request.',
+ resolver: Resolvers::DeploymentsResolver do
+ extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1
+ end
+
+ field :last_deployment,
+ Types::DeploymentType,
+ description: 'Last deployment of the environment.',
+ resolver: Resolvers::Environments::LastDeploymentResolver
+
+ def tier
+ object.tier.to_sym
+ end
end
end
diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb
index 235a2bc2a34..45357de5502 100644
--- a/app/graphql/types/group_type.rb
+++ b/app/graphql/types/group_type.rb
@@ -22,7 +22,7 @@ module Types
type: Types::CustomEmojiType.connection_type,
null: true,
description: 'Custom emoji within this namespace.',
- _deprecated_feature_flag: :custom_emoji
+ alpha: { milestone: '13.6' }
field :share_with_group_lock,
type: GraphQL::Types::Boolean,
@@ -134,7 +134,7 @@ module Types
description: 'Number of container repositories in the group.'
field :packages,
- description: 'Packages of the group.',
+ description: 'Packages of the group. This field can only be resolved for one group in any single request.',
resolver: Resolvers::GroupPackagesResolver
field :dependency_proxy_setting,
@@ -212,6 +212,12 @@ module Types
description: "Find organizations of this group.",
resolver: Resolvers::Crm::OrganizationsResolver
+ field :organization_state_counts,
+ Types::CustomerRelations::OrganizationStateCountsType,
+ null: true,
+ description: 'Counts of organizations by status for the group.',
+ resolver: Resolvers::Crm::OrganizationStateCountsResolver
+
field :contacts, Types::CustomerRelations::ContactType.connection_type,
null: true,
description: "Find contacts of this group.",
@@ -272,6 +278,10 @@ module Types
group.dependency_proxy_setting || group.create_dependency_proxy_setting
end
+ def custom_emoji
+ object.custom_emoji if Feature.enabled?(:custom_emoji)
+ end
+
private
def group
diff --git a/app/graphql/types/member_sort_enum.rb b/app/graphql/types/member_sort_enum.rb
new file mode 100644
index 00000000000..f3291dda13b
--- /dev/null
+++ b/app/graphql/types/member_sort_enum.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Types
+ class MemberSortEnum < SortEnum
+ graphql_name 'MemberSort'
+ description 'Values for sorting members'
+
+ value 'ACCESS_LEVEL_ASC', 'Access level ascending order.', value: :access_level_asc
+ value 'ACCESS_LEVEL_DESC', 'Access level descending order.', value: :access_level_desc
+ value 'USER_FULL_NAME_ASC', "User's full name ascending order.", value: :name_asc
+ value 'USER_FULL_NAME_DESC', "User's full name descending order.", value: :name_desc
+ end
+end
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index d88653f2f8c..399dcc8e03d 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -94,9 +94,10 @@ module Types
method: :public_merge_status, null: true,
description: 'Merge status of the merge request.'
- field :detailed_merge_status, ::Types::MergeRequests::DetailedMergeStatusEnum, method: :detailed_merge_status, null: true,
+ field :detailed_merge_status, ::Types::MergeRequests::DetailedMergeStatusEnum, null: true,
calls_gitaly: true,
- description: 'Detailed merge status of the merge request.', alpha: { milestone: '15.3' }
+ description: 'Detailed merge status of the merge request.',
+ alpha: { milestone: '15.3' }
field :mergeable_discussions_state, GraphQL::Types::Boolean, null: true,
calls_gitaly: true,
@@ -280,6 +281,10 @@ module Types
def merge_user
object.metrics&.merged_by || object.merge_user
end
+
+ def detailed_merge_status
+ ::MergeRequests::Mergeability::DetailedMergeStatusService.new(merge_request: object).execute
+ end
end
end
diff --git a/app/graphql/types/merge_requests/detailed_merge_status_enum.rb b/app/graphql/types/merge_requests/detailed_merge_status_enum.rb
index 58104159303..3de6296154d 100644
--- a/app/graphql/types/merge_requests/detailed_merge_status_enum.rb
+++ b/app/graphql/types/merge_requests/detailed_merge_status_enum.rb
@@ -21,6 +21,9 @@ module Types
value 'CI_MUST_PASS',
value: :ci_must_pass,
description: 'Pipeline must succeed before merging.'
+ value 'CI_STILL_RUNNING',
+ value: :ci_still_running,
+ description: 'Pipeline is still running.'
value 'DISCUSSIONS_NOT_RESOLVED',
value: :discussions_not_resolved,
description: 'Discussions must be resolved before merging.'
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index 499c2e786bf..ea833b35085 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -37,8 +37,8 @@ module Types
mount_mutation Mutations::Clusters::AgentTokens::Create
mount_mutation Mutations::Clusters::AgentTokens::Revoke
mount_mutation Mutations::Commits::Create, calls_gitaly: true
- mount_mutation Mutations::CustomEmoji::Create, _deprecated_feature_flag: :custom_emoji
- mount_mutation Mutations::CustomEmoji::Destroy, _deprecated_feature_flag: :custom_emoji
+ mount_mutation Mutations::CustomEmoji::Create, alpha: { milestone: '13.6' }
+ mount_mutation Mutations::CustomEmoji::Destroy, alpha: { milestone: '13.6' }
mount_mutation Mutations::CustomerRelations::Contacts::Create
mount_mutation Mutations::CustomerRelations::Contacts::Update
mount_mutation Mutations::CustomerRelations::Organizations::Create
@@ -120,10 +120,12 @@ module Types
milestone: '15.0'
}
mount_mutation Mutations::Ci::ProjectCiCdSettingsUpdate
+ mount_mutation Mutations::Ci::Job::ArtifactsDestroy
mount_mutation Mutations::Ci::Job::Play
mount_mutation Mutations::Ci::Job::Retry
mount_mutation Mutations::Ci::Job::Cancel
mount_mutation Mutations::Ci::Job::Unschedule
+ mount_mutation Mutations::Ci::JobArtifact::Destroy
mount_mutation Mutations::Ci::JobTokenScope::AddProject
mount_mutation Mutations::Ci::JobTokenScope::RemoveProject
mount_mutation Mutations::Ci::Runner::Update
diff --git a/app/graphql/types/packages/package_details_type.rb b/app/graphql/types/packages/package_details_type.rb
index 0413177ef14..6c0d955ed77 100644
--- a/app/graphql/types/packages/package_details_type.rb
+++ b/app/graphql/types/packages/package_details_type.rb
@@ -26,6 +26,8 @@ module Types
field :pypi_setup_url, GraphQL::Types::String, null: true, description: 'Url of the PyPi project setup endpoint.'
field :pypi_url, GraphQL::Types::String, null: true, description: 'Url of the PyPi project endpoint.'
+ field :last_downloaded_at, Types::TimeType, null: true, description: 'Last time that a file of this package was downloaded.'
+
def versions
object.versions
end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index ecc6c9d7811..f43f5c27dac 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -10,119 +10,204 @@ module Types
expose_permissions Types::PermissionTypes::Project
- field :id, GraphQL::Types::ID, null: false,
- description: 'ID of the project.'
-
- field :ci_config_path_or_default, GraphQL::Types::String, null: false,
- description: 'Path of the CI configuration file.'
- field :full_path, GraphQL::Types::ID, null: false,
- description: 'Full path of the project.'
- field :path, GraphQL::Types::String, null: false,
- description: 'Path of the project.'
-
- field :sast_ci_configuration, Types::CiConfiguration::Sast::Type, null: true,
- calls_gitaly: true,
- description: 'SAST CI configuration for the project.'
-
- field :name, GraphQL::Types::String, null: false,
- description: 'Name of the project (without namespace).'
- field :name_with_namespace, GraphQL::Types::String, null: false,
- description: 'Full name of the project with its namespace.'
-
- field :description, GraphQL::Types::String, null: true,
- description: 'Short description of the project.'
-
- field :tag_list, GraphQL::Types::String, null: true,
- deprecated: { reason: 'Use `topics`', milestone: '13.12' },
- description: 'List of project topics (not Git tags).', method: :topic_list
-
- field :topics, [GraphQL::Types::String], null: true,
- description: 'List of project topics.', method: :topic_list
-
- field :http_url_to_repo, GraphQL::Types::String, null: true,
- description: 'URL to connect to the project via HTTPS.'
- field :ssh_url_to_repo, GraphQL::Types::String, null: true,
- description: 'URL to connect to the project via SSH.'
- field :web_url, GraphQL::Types::String, null: true,
- description: 'Web URL of the project.'
-
- field :forks_count, GraphQL::Types::Int, null: false, calls_gitaly: true, # 4 times
- description: 'Number of times the project has been forked.'
- field :star_count, GraphQL::Types::Int, null: false,
- description: 'Number of times the project has been starred.'
-
- field :created_at, Types::TimeType, null: true,
- description: 'Timestamp of the project creation.'
- field :last_activity_at, Types::TimeType, null: true,
- description: 'Timestamp of the project last activity.'
-
- field :archived, GraphQL::Types::Boolean, null: true,
- description: 'Indicates the archived status of the project.'
-
- field :visibility, GraphQL::Types::String, null: true,
- description: 'Visibility of the project.'
-
- field :lfs_enabled, GraphQL::Types::Boolean, null: true,
- description: 'Indicates if the project has Large File Storage (LFS) enabled.'
- field :merge_requests_ff_only_enabled, GraphQL::Types::Boolean, null: true,
- description: 'Indicates if no merge commits should be created and all merges should instead be fast-forwarded, which means that merging is only allowed if the branch could be fast-forwarded.'
- field :shared_runners_enabled, GraphQL::Types::Boolean, null: true,
- description: 'Indicates if shared runners are enabled for the project.'
-
- field :service_desk_enabled, GraphQL::Types::Boolean, null: true,
- description: 'Indicates if the project has Service Desk enabled.'
-
- field :service_desk_address, GraphQL::Types::String, null: true,
- description: 'E-mail address of the Service Desk.'
-
- field :avatar_url, GraphQL::Types::String, null: true, calls_gitaly: true,
- description: 'URL to avatar image file of the project.'
-
- field :jobs_enabled, GraphQL::Types::Boolean, null: true,
- description: 'Indicates if CI/CD pipeline jobs are enabled for the current user.'
-
- field :public_jobs, GraphQL::Types::Boolean, method: :public_builds, null: true,
- description: 'Indicates if there is public access to pipelines and job details of the project, including output logs and artifacts.'
-
- field :open_issues_count, GraphQL::Types::Int, null: true,
- description: 'Number of open issues for the project.'
-
- field :allow_merge_on_skipped_pipeline, GraphQL::Types::Boolean, null: true,
- description: 'If `only_allow_merge_if_pipeline_succeeds` is true, indicates if merge requests of the project can also be merged with skipped jobs.'
- field :autoclose_referenced_issues, GraphQL::Types::Boolean, null: true,
- description: 'Indicates if issues referenced by merge requests and commits within the default branch are closed automatically.'
- field :import_status, GraphQL::Types::String, null: true,
- description: 'Status of import background job of the project.'
- field :jira_import_status, GraphQL::Types::String, null: true,
- description: 'Status of Jira import background job of the project.'
- field :only_allow_merge_if_all_discussions_are_resolved, GraphQL::Types::Boolean, null: true,
- description: 'Indicates if merge requests of the project can only be merged when all the discussions are resolved.'
- field :only_allow_merge_if_pipeline_succeeds, GraphQL::Types::Boolean, null: true,
- description: 'Indicates if merge requests of the project can only be merged with successful jobs.'
- field :printing_merge_request_link_enabled, GraphQL::Types::Boolean, null: true,
- description: 'Indicates if a link to create or view a merge request should display after a push to Git repositories of the project from the command line.'
- field :remove_source_branch_after_merge, GraphQL::Types::Boolean, null: true,
- description: 'Indicates if `Delete source branch` option should be enabled by default for all new merge requests of the project.'
- field :request_access_enabled, GraphQL::Types::Boolean, null: true,
- description: 'Indicates if users can request member access to the project.'
- field :squash_read_only, GraphQL::Types::Boolean, null: false, method: :squash_readonly?,
- description: 'Indicates if `squashReadOnly` is enabled.'
- field :suggestion_commit_message, GraphQL::Types::String, null: true,
- description: 'Commit message used to apply merge request suggestions.'
+ field :id, GraphQL::Types::ID,
+ null: false,
+ description: 'ID of the project.'
+
+ field :ci_config_path_or_default, GraphQL::Types::String,
+ null: false,
+ description: 'Path of the CI configuration file.'
+
+ field :ci_config_variables, [Types::Ci::ConfigVariableType],
+ null: true,
+ calls_gitaly: true,
+ authorize: :create_pipeline,
+ alpha: { milestone: '15.3' },
+ description: 'CI/CD config variable.' do
+ argument :sha, GraphQL::Types::String,
+ required: true,
+ description: 'Sha.'
+ end
+
+ field :full_path, GraphQL::Types::ID,
+ null: false,
+ description: 'Full path of the project.'
+
+ field :path, GraphQL::Types::String,
+ null: false,
+ description: 'Path of the project.'
+
+ field :sast_ci_configuration, Types::CiConfiguration::Sast::Type,
+ null: true,
+ calls_gitaly: true,
+ description: 'SAST CI configuration for the project.'
+
+ field :name, GraphQL::Types::String,
+ null: false,
+ description: 'Name of the project (without namespace).'
+
+ field :name_with_namespace, GraphQL::Types::String,
+ null: false,
+ description: 'Full name of the project with its namespace.'
+
+ field :description, GraphQL::Types::String,
+ null: true,
+ description: 'Short description of the project.'
+
+ field :tag_list, GraphQL::Types::String,
+ null: true,
+ deprecated: { reason: 'Use `topics`', milestone: '13.12' },
+ description: 'List of project topics (not Git tags).',
+ method: :topic_list
+
+ field :topics, [GraphQL::Types::String],
+ null: true,
+ description: 'List of project topics.',
+ method: :topic_list
+
+ field :http_url_to_repo, GraphQL::Types::String,
+ null: true,
+ description: 'URL to connect to the project via HTTPS.'
+
+ field :ssh_url_to_repo, GraphQL::Types::String,
+ null: true,
+ description: 'URL to connect to the project via SSH.'
+
+ field :web_url, GraphQL::Types::String,
+ null: true,
+ description: 'Web URL of the project.'
+
+ field :forks_count, GraphQL::Types::Int,
+ null: false,
+ calls_gitaly: true, # 4 times
+ description: 'Number of times the project has been forked.'
+
+ field :star_count, GraphQL::Types::Int,
+ null: false,
+ description: 'Number of times the project has been starred.'
+
+ field :created_at, Types::TimeType,
+ null: true,
+ description: 'Timestamp of the project creation.'
+
+ field :last_activity_at, Types::TimeType,
+ null: true,
+ description: 'Timestamp of the project last activity.'
+
+ field :archived, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates the archived status of the project.'
+
+ field :visibility, GraphQL::Types::String,
+ null: true,
+ description: 'Visibility of the project.'
+
+ field :lfs_enabled, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates if the project has Large File Storage (LFS) enabled.'
+
+ field :merge_requests_ff_only_enabled, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates if no merge commits should be created and all merges should instead be ' \
+ 'fast-forwarded, which means that merging is only allowed if the branch could be fast-forwarded.'
+
+ field :shared_runners_enabled, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates if shared runners are enabled for the project.'
+
+ field :service_desk_enabled, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates if the project has Service Desk enabled.'
+
+ field :service_desk_address, GraphQL::Types::String,
+ null: true,
+ description: 'E-mail address of the Service Desk.'
+
+ field :avatar_url, GraphQL::Types::String,
+ null: true,
+ calls_gitaly: true,
+ description: 'URL to avatar image file of the project.'
+
+ field :jobs_enabled, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates if CI/CD pipeline jobs are enabled for the current user.'
+
+ field :public_jobs, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates if there is public access to pipelines and job details of the project, ' \
+ 'including output logs and artifacts.',
+ method: :public_builds
+
+ field :open_issues_count, GraphQL::Types::Int,
+ null: true,
+ description: 'Number of open issues for the project.'
+
+ field :allow_merge_on_skipped_pipeline, GraphQL::Types::Boolean,
+ null: true,
+ description: 'If `only_allow_merge_if_pipeline_succeeds` is true, indicates if merge requests of ' \
+ 'the project can also be merged with skipped jobs.'
+
+ field :autoclose_referenced_issues, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates if issues referenced by merge requests and commits within the default branch ' \
+ 'are closed automatically.'
+
+ field :import_status, GraphQL::Types::String,
+ null: true,
+ description: 'Status of import background job of the project.'
+
+ field :jira_import_status, GraphQL::Types::String,
+ null: true,
+ description: 'Status of Jira import background job of the project.'
+
+ field :only_allow_merge_if_all_discussions_are_resolved, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates if merge requests of the project can only be merged when all the discussions are resolved.'
+
+ field :only_allow_merge_if_pipeline_succeeds, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates if merge requests of the project can only be merged with successful jobs.'
+
+ field :printing_merge_request_link_enabled, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates if a link to create or view a merge request should display after a push to Git ' \
+ 'repositories of the project from the command line.'
+
+ field :remove_source_branch_after_merge, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates if `Delete source branch` option should be enabled by default for all ' \
+ 'new merge requests of the project.'
+
+ field :request_access_enabled, GraphQL::Types::Boolean,
+ null: true,
+ description: 'Indicates if users can request member access to the project.'
+
+ field :squash_read_only, GraphQL::Types::Boolean,
+ null: false,
+ description: 'Indicates if `squashReadOnly` is enabled.',
+ method: :squash_readonly?
+
+ field :suggestion_commit_message, GraphQL::Types::String,
+ null: true,
+ description: 'Commit message used to apply merge request suggestions.'
# No, the quotes are not a typo. Used to get around circular dependencies.
# See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27536#note_871009675
- field :group, 'Types::GroupType', null: true,
- description: 'Group of the project.'
- field :namespace, Types::NamespaceType, null: true,
- description: 'Namespace of the project.'
+ field :group, 'Types::GroupType',
+ null: true,
+ description: 'Group of the project.'
+
+ field :namespace, Types::NamespaceType,
+ null: true,
+ description: 'Namespace of the project.'
field :statistics, Types::ProjectStatisticsType,
null: true,
description: 'Statistics of the project.'
- field :repository, Types::RepositoryType, null: true,
- description: 'Git repository of the project.'
+ field :repository, Types::RepositoryType,
+ null: true,
+ description: 'Git repository of the project.'
field :merge_requests,
Types::MergeRequestType.connection_type,
@@ -159,9 +244,10 @@ module Types
extras: [:lookahead],
resolver: Resolvers::IssueStatusCountsResolver
- field :milestones, Types::MilestoneType.connection_type, null: true,
- description: 'Milestones of the project.',
- resolver: Resolvers::ProjectMilestonesResolver
+ field :milestones, Types::MilestoneType.connection_type,
+ null: true,
+ description: 'Milestones of the project.',
+ resolver: Resolvers::ProjectMilestonesResolver
field :project_members,
description: 'Members of the project.',
@@ -179,6 +265,12 @@ module Types
description: 'A single environment of the project.',
resolver: Resolvers::EnvironmentsResolver.single
+ field :deployment,
+ Types::DeploymentDetailsType,
+ null: true,
+ description: 'Details of the deployment of the project.',
+ resolver: Resolvers::DeploymentResolver.single
+
field :issue,
Types::IssueType,
null: true,
@@ -201,164 +293,150 @@ module Types
description: 'Jobs of a project. This field can only be resolved for one project in any single request.',
resolver: Resolvers::ProjectJobsResolver
+ field :job,
+ type: Types::Ci::JobType,
+ null: true,
+ authorize: :read_build,
+ description: 'One job belonging to the project, selected by ID.' do
+ argument :id, Types::GlobalIDType[::CommitStatus],
+ required: true,
+ description: 'ID of the job.'
+ end
+
field :pipelines,
null: true,
description: 'Build pipelines of the project.',
extras: [:lookahead],
resolver: Resolvers::ProjectPipelinesResolver
- field :pipeline,
- Types::Ci::PipelineType,
+ field :pipeline, Types::Ci::PipelineType,
null: true,
description: 'Build pipeline of the project.',
extras: [:lookahead],
resolver: Resolvers::ProjectPipelineResolver
- field :pipeline_counts,
- Types::Ci::PipelineCountsType,
+ field :pipeline_counts, Types::Ci::PipelineCountsType,
null: true,
description: 'Build pipeline counts of the project.',
resolver: Resolvers::Ci::ProjectPipelineCountsResolver
- field :ci_variables,
- Types::Ci::ProjectVariableType.connection_type,
+ field :ci_variables, Types::Ci::ProjectVariableType.connection_type,
null: true,
description: "List of the project's CI/CD variables.",
authorize: :admin_build,
method: :variables
- field :ci_cd_settings,
- Types::Ci::CiCdSettingType,
+ field :ci_cd_settings, Types::Ci::CiCdSettingType,
null: true,
description: 'CI/CD settings for the project.'
- field :sentry_detailed_error,
- Types::ErrorTracking::SentryDetailedErrorType,
+ field :sentry_detailed_error, Types::ErrorTracking::SentryDetailedErrorType,
null: true,
description: 'Detailed version of a Sentry error on the project.',
resolver: Resolvers::ErrorTracking::SentryDetailedErrorResolver
- field :grafana_integration,
- Types::GrafanaIntegrationType,
+ field :grafana_integration, Types::GrafanaIntegrationType,
null: true,
description: 'Grafana integration details for the project.',
resolver: Resolvers::Projects::GrafanaIntegrationResolver
- field :snippets,
- Types::SnippetType.connection_type,
+ field :snippets, Types::SnippetType.connection_type,
null: true,
description: 'Snippets of the project.',
resolver: Resolvers::Projects::SnippetsResolver
- field :sentry_errors,
- Types::ErrorTracking::SentryErrorCollectionType,
+ field :sentry_errors, Types::ErrorTracking::SentryErrorCollectionType,
null: true,
description: 'Paginated collection of Sentry errors on the project.',
resolver: Resolvers::ErrorTracking::SentryErrorCollectionResolver
- field :boards,
- Types::BoardType.connection_type,
+ field :boards, Types::BoardType.connection_type,
null: true,
description: 'Boards of the project.',
max_page_size: 2000,
resolver: Resolvers::BoardsResolver
- field :recent_issue_boards,
- Types::BoardType.connection_type,
+ field :recent_issue_boards, Types::BoardType.connection_type,
null: true,
description: 'List of recently visited boards of the project. Maximum size is 4.',
resolver: Resolvers::RecentBoardsResolver
- field :board,
- Types::BoardType,
+ field :board, Types::BoardType,
null: true,
description: 'A single board of the project.',
resolver: Resolvers::BoardResolver
- field :jira_imports,
- Types::JiraImportType.connection_type,
+ field :jira_imports, Types::JiraImportType.connection_type,
null: true,
description: 'Jira imports into the project.'
- field :services,
- Types::Projects::ServiceType.connection_type,
+ field :services, Types::Projects::ServiceType.connection_type,
null: true,
description: 'Project services.',
resolver: Resolvers::Projects::ServicesResolver
- field :alert_management_alerts,
- Types::AlertManagement::AlertType.connection_type,
+ field :alert_management_alerts, Types::AlertManagement::AlertType.connection_type,
null: true,
description: 'Alert Management alerts of the project.',
extras: [:lookahead],
resolver: Resolvers::AlertManagement::AlertResolver
- field :alert_management_alert,
- Types::AlertManagement::AlertType,
+ field :alert_management_alert, Types::AlertManagement::AlertType,
null: true,
description: 'A single Alert Management alert of the project.',
resolver: Resolvers::AlertManagement::AlertResolver.single
- field :alert_management_alert_status_counts,
- Types::AlertManagement::AlertStatusCountsType,
+ field :alert_management_alert_status_counts, Types::AlertManagement::AlertStatusCountsType,
null: true,
description: 'Counts of alerts by status for the project.',
resolver: Resolvers::AlertManagement::AlertStatusCountsResolver
- field :alert_management_integrations,
- Types::AlertManagement::IntegrationType.connection_type,
+ field :alert_management_integrations, Types::AlertManagement::IntegrationType.connection_type,
null: true,
description: 'Integrations which can receive alerts for the project.',
resolver: Resolvers::AlertManagement::IntegrationsResolver
- field :alert_management_http_integrations,
- Types::AlertManagement::HttpIntegrationType.connection_type,
+ field :alert_management_http_integrations, Types::AlertManagement::HttpIntegrationType.connection_type,
null: true,
description: 'HTTP Integrations which can receive alerts for the project.',
resolver: Resolvers::AlertManagement::HttpIntegrationsResolver
- field :incident_management_timeline_events,
- Types::IncidentManagement::TimelineEventType.connection_type,
+ field :incident_management_timeline_events, Types::IncidentManagement::TimelineEventType.connection_type,
null: true,
description: 'Incident Management Timeline events associated with the incident.',
extras: [:lookahead],
resolver: Resolvers::IncidentManagement::TimelineEventsResolver
- field :incident_management_timeline_event,
- Types::IncidentManagement::TimelineEventType,
+ field :incident_management_timeline_event, Types::IncidentManagement::TimelineEventType,
null: true,
description: 'Incident Management Timeline event associated with the incident.',
resolver: Resolvers::IncidentManagement::TimelineEventsResolver.single
- field :releases,
- Types::ReleaseType.connection_type,
+ field :releases, Types::ReleaseType.connection_type,
null: true,
description: 'Releases of the project.',
resolver: Resolvers::ReleasesResolver
- field :release,
- Types::ReleaseType,
+ field :release, Types::ReleaseType,
null: true,
description: 'A single release of the project.',
resolver: Resolvers::ReleasesResolver.single,
authorize: :read_release
- field :container_expiration_policy,
- Types::ContainerExpirationPolicyType,
+ field :container_expiration_policy, Types::ContainerExpirationPolicyType,
null: true,
description: 'Container expiration policy of the project.'
- field :container_repositories,
- Types::ContainerRepositoryType.connection_type,
+ field :container_repositories, Types::ContainerRepositoryType.connection_type,
null: true,
description: 'Container repositories of the project.',
resolver: Resolvers::ContainerRepositoriesResolver
- field :container_repositories_count, GraphQL::Types::Int, null: false,
- description: 'Number of container repositories in the project.'
+ field :container_repositories_count, GraphQL::Types::Int,
+ null: false,
+ description: 'Number of container repositories in the project.'
- field :label,
- Types::LabelType,
+ field :label, Types::LabelType,
null: true,
description: 'Label available on this project.' do
argument :title, GraphQL::Types::String,
@@ -366,68 +444,63 @@ module Types
description: 'Title of the label.'
end
- field :terraform_state,
- Types::Terraform::StateType,
+ field :terraform_state, Types::Terraform::StateType,
null: true,
description: 'Find a single Terraform state by name.',
resolver: Resolvers::Terraform::StatesResolver.single
- field :terraform_states,
- Types::Terraform::StateType.connection_type,
+ field :terraform_states, Types::Terraform::StateType.connection_type,
null: true,
description: 'Terraform states associated with the project.',
resolver: Resolvers::Terraform::StatesResolver
- field :pipeline_analytics, Types::Ci::AnalyticsType, null: true,
- description: 'Pipeline analytics.',
- resolver: Resolvers::ProjectPipelineStatisticsResolver
+ field :pipeline_analytics, Types::Ci::AnalyticsType,
+ null: true,
+ description: 'Pipeline analytics.',
+ resolver: Resolvers::ProjectPipelineStatisticsResolver
- field :ci_template, Types::Ci::TemplateType, null: true,
- description: 'Find a single CI/CD template by name.',
- resolver: Resolvers::Ci::TemplateResolver
+ field :ci_template, Types::Ci::TemplateType,
+ null: true,
+ description: 'Find a single CI/CD template by name.',
+ resolver: Resolvers::Ci::TemplateResolver
- field :ci_job_token_scope, Types::Ci::JobTokenScopeType, null: true,
- description: 'The CI Job Tokens scope of access.',
- resolver: Resolvers::Ci::JobTokenScopeResolver
+ field :ci_job_token_scope, Types::Ci::JobTokenScopeType,
+ null: true,
+ description: 'The CI Job Tokens scope of access.',
+ resolver: Resolvers::Ci::JobTokenScopeResolver
- field :timelogs,
- Types::TimelogType.connection_type, null: true,
- description: 'Time logged on issues and merge requests in the project.',
- extras: [:lookahead],
- complexity: 5,
- resolver: ::Resolvers::TimelogResolver
+ field :timelogs, Types::TimelogType.connection_type,
+ null: true,
+ description: 'Time logged on issues and merge requests in the project.',
+ extras: [:lookahead],
+ complexity: 5,
+ resolver: ::Resolvers::TimelogResolver
- field :agent_configurations,
- ::Types::Kas::AgentConfigurationType.connection_type,
+ field :agent_configurations, ::Types::Kas::AgentConfigurationType.connection_type,
null: true,
description: 'Agent configurations defined by the project',
resolver: ::Resolvers::Kas::AgentConfigurationsResolver
- field :cluster_agent,
- ::Types::Clusters::AgentType,
+ field :cluster_agent, ::Types::Clusters::AgentType,
null: true,
description: 'Find a single cluster agent by name.',
resolver: ::Resolvers::Clusters::AgentsResolver.single
- field :cluster_agents,
- ::Types::Clusters::AgentType.connection_type,
+ field :cluster_agents, ::Types::Clusters::AgentType.connection_type,
extras: [:lookahead],
null: true,
description: 'Cluster agents associated with the project.',
resolver: ::Resolvers::Clusters::AgentsResolver
- field :merge_commit_template,
- GraphQL::Types::String,
+ field :merge_commit_template, GraphQL::Types::String,
null: true,
description: 'Template used to create merge commit message in merge requests.'
- field :squash_commit_template,
- GraphQL::Types::String,
+ field :squash_commit_template, GraphQL::Types::String,
null: true,
description: 'Template used to create squash commit message in merge requests.'
- field :labels,
- Types::LabelType.connection_type,
+ field :labels, Types::LabelType.connection_type,
null: true,
description: 'Labels available on this project.',
resolver: Resolvers::LabelsResolver
@@ -438,8 +511,7 @@ module Types
' Returns `null` if `work_items` feature flag is disabled.' \
' This flag is disabled by default, because the feature is experimental and is subject to change without notice.'
- field :timelog_categories,
- Types::TimeTracking::TimelogCategoryType.connection_type,
+ field :timelog_categories, Types::TimeTracking::TimelogCategoryType.connection_type,
null: true,
description: "Timelog categories for the project.",
alpha: { milestone: '15.3' }
@@ -448,6 +520,12 @@ module Types
resolver: Resolvers::Projects::ForkTargetsResolver,
description: 'Namespaces in which the current user can fork the project into.'
+ field :branch_rules,
+ Types::Projects::BranchRuleType.connection_type,
+ null: true,
+ description: "Branch rules configured for the project.",
+ resolver: Resolvers::Projects::BranchRulesResolver
+
def timelog_categories
object.project_namespace.timelog_categories if Feature.enabled?(:timelog_categories)
end
@@ -498,6 +576,21 @@ module Types
project.container_repositories.size
end
+ def ci_config_variables(sha:)
+ result = ::Ci::ListConfigVariablesService.new(object, context[:current_user]).execute(sha)
+
+ return if result.nil?
+
+ result.map do |var_key, var_config|
+ { key: var_key, **var_config }
+ end
+ end
+
+ def job(id:)
+ object.commit_statuses.find(id.model_id)
+ rescue ActiveRecord::RecordNotFound
+ end
+
def sast_ci_configuration
return unless Ability.allowed?(current_user, :download_code, object)
diff --git a/app/graphql/types/projects/branch_rule_type.rb b/app/graphql/types/projects/branch_rule_type.rb
new file mode 100644
index 00000000000..866cff0f439
--- /dev/null
+++ b/app/graphql/types/projects/branch_rule_type.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Types
+ module Projects
+ class BranchRuleType < BaseObject
+ graphql_name 'BranchRule'
+ description 'List of branch rules for a project, grouped by branch name.'
+ accepts ::ProtectedBranch
+ authorize :read_protected_branch
+
+ field :name,
+ type: GraphQL::Types::String,
+ null: false,
+ description: 'Branch name, with wildcards, for the branch rules.'
+
+ field :branch_protection,
+ type: Types::BranchRules::BranchProtectionType,
+ null: false,
+ description: 'Branch protections configured for this branch rule.',
+ method: :itself
+
+ field :created_at,
+ Types::TimeType,
+ null: false,
+ description: 'Timestamp of when the branch rule was created.'
+
+ field :updated_at,
+ Types::TimeType,
+ null: false,
+ description: 'Timestamp of when the branch rule was last updated.'
+ end
+ end
+end
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index 84355390ea0..78463a1804a 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.',
+ description: 'Find a package. This field can only be resolved for one query in any single request.',
resolver: Resolvers::PackageDetailsResolver
field :user, Types::UserType,
diff --git a/app/graphql/types/sort_direction_enum.rb b/app/graphql/types/sort_direction_enum.rb
new file mode 100644
index 00000000000..28dba1abfb6
--- /dev/null
+++ b/app/graphql/types/sort_direction_enum.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Types
+ class SortDirectionEnum < BaseEnum
+ graphql_name 'SortDirectionEnum'
+ description 'Values for sort direction'
+
+ value 'ASC', 'Ascending order.', value: 'asc'
+ value 'DESC', 'Descending order.', value: 'desc'
+ end
+end
diff --git a/app/graphql/types/subscription_type.rb b/app/graphql/types/subscription_type.rb
index 9b5f028a857..ef701bbfc10 100644
--- a/app/graphql/types/subscription_type.rb
+++ b/app/graphql/types/subscription_type.rb
@@ -18,5 +18,12 @@ module Types
field :issuable_dates_updated, subscription: Subscriptions::IssuableUpdated, null: true,
description: 'Triggered when the due date or start date of an issuable is updated.'
+
+ field :merge_request_reviewers_updated,
+ subscription: Subscriptions::IssuableUpdated,
+ null: true,
+ description: 'Triggered when the reviewers of a merge request are updated.'
end
end
+
+Types::SubscriptionType.prepend_mod
diff --git a/app/graphql/types/timelog_type.rb b/app/graphql/types/timelog_type.rb
index c3fb9b77927..3856e1aa3b3 100644
--- a/app/graphql/types/timelog_type.rb
+++ b/app/graphql/types/timelog_type.rb
@@ -4,7 +4,7 @@ module Types
class TimelogType < BaseObject
graphql_name 'Timelog'
- authorize :read_issue
+ authorize :read_issuable
expose_permissions Types::PermissionTypes::Timelog
diff --git a/app/graphql/types/work_items/widgets/description_type.rb b/app/graphql/types/work_items/widgets/description_type.rb
index 4c365a67bfd..4861f7f46d8 100644
--- a/app/graphql/types/work_items/widgets/description_type.rb
+++ b/app/graphql/types/work_items/widgets/description_type.rb
@@ -13,8 +13,18 @@ module Types
implements Types::WorkItems::WidgetInterface
field :description, GraphQL::Types::String,
- null: true,
- description: 'Description of the work item.'
+ null: true,
+ description: 'Description of the work item.'
+ field :edited, GraphQL::Types::Boolean,
+ null: false,
+ description: 'Whether the description has been edited since the work item was created.',
+ method: :edited?
+ field :last_edited_at, Types::TimeType,
+ null: true,
+ description: 'Timestamp of when the work item\'s description was last edited.'
+ field :last_edited_by, Types::UserType,
+ null: true,
+ description: 'User that made the last edit to the work item\'s description.'
markdown_field :description_html, null: true do |resolved_object|
resolved_object.work_item
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 321a6e9395e..ddc682bc08a 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -7,6 +7,7 @@ module ApplicationSettingsHelper
:gravatar_enabled?,
:password_authentication_enabled_for_web?,
:akismet_enabled?,
+ :spam_check_endpoint_enabled?,
to: :'Gitlab::CurrentSettings.current_application_settings'
def user_oauth_applications?
@@ -60,6 +61,10 @@ module ApplicationSettingsHelper
all_protocols_enabled? || Gitlab::CurrentSettings.enabled_git_access_protocol == 'http'
end
+ def anti_spam_service_enabled?
+ akismet_enabled? || spam_check_endpoint_enabled?
+ end
+
def enabled_protocol_button(container, protocol)
case protocol
when 'ssh'
@@ -278,6 +283,7 @@ module ApplicationSettingsHelper
:max_export_size,
:max_import_size,
:max_pages_size,
+ :max_pages_custom_domains_per_project,
:max_yaml_size_bytes,
:max_yaml_depth,
:metrics_method_call_threshold,
@@ -434,12 +440,24 @@ module ApplicationSettingsHelper
:runner_token_expiration_interval,
:group_runner_token_expiration_interval,
:project_runner_token_expiration_interval,
- :pipeline_limit_per_project_user_sha
+ :pipeline_limit_per_project_user_sha,
+ :invitation_flow_enforcement
].tap do |settings|
- settings << :deactivate_dormant_users unless Gitlab.com?
+ next if Gitlab.com?
+
+ settings << :deactivate_dormant_users
+ settings << :deactivate_dormant_users_period
end
end
+ def runner_token_expiration_interval_attributes
+ {
+ instance_runner_token_expiration_interval: @application_setting.runner_token_expiration_interval,
+ group_runner_token_expiration_interval: @application_setting.group_runner_token_expiration_interval,
+ project_runner_token_expiration_interval: @application_setting.project_runner_token_expiration_interval
+ }
+ end
+
def external_authorization_service_attributes
[
:external_auth_client_cert,
diff --git a/app/helpers/badges_helper.rb b/app/helpers/badges_helper.rb
index d48eae26a90..069c15433a5 100644
--- a/app/helpers/badges_helper.rb
+++ b/app/helpers/badges_helper.rb
@@ -1,25 +1,6 @@
# frozen_string_literal: true
module BadgesHelper
- VARIANT_CLASSES = {
- muted: "badge-muted",
- neutral: "badge-neutral",
- info: "badge-info",
- success: "badge-success",
- warning: "badge-warning",
- danger: "badge-danger"
- }.tap { |hash| hash.default = hash.fetch(:muted) }.freeze
-
- SIZE_CLASSES = {
- sm: "sm",
- md: "md",
- lg: "lg"
- }.tap { |hash| hash.default = hash.fetch(:md) }.freeze
-
- GL_BADGE_CLASSES = %w[gl-badge badge badge-pill].freeze
-
- GL_ICON_CLASSES = %w[gl-icon gl-badge-icon].freeze
-
# Creates a GitLab UI badge.
#
# Examples:
@@ -53,47 +34,16 @@ module BadgesHelper
#
# See also https://gitlab-org.gitlab.io/gitlab-ui/?path=/story/base-badge--default.
def gl_badge_tag(*args, &block)
+ # Merge the options and html_options hashes if both are present,
+ # because the badge component wants a flat list of keyword args.
+ args.compact!
+ hashes, params = args.partition { |a| a.is_a? Hash }
+ options_hash = hashes.reduce({}, :merge)
+
if block
- build_gl_badge_tag(capture(&block), *args)
+ render Pajamas::BadgeComponent.new(**options_hash), &block
else
- build_gl_badge_tag(*args)
+ render Pajamas::BadgeComponent.new(*params, **options_hash)
end
end
-
- private
-
- def build_gl_badge_tag(content, options = nil, html_options = nil)
- options ||= {}
- html_options ||= {}
-
- icon_only = options[:icon_only]
- variant_class = VARIANT_CLASSES[options.fetch(:variant, :muted)]
- size_class = SIZE_CLASSES[options.fetch(:size, :md)]
- icon_classes = GL_ICON_CLASSES.dup << options.fetch(:icon_classes, nil)
-
- html_options = html_options.merge(
- class: [
- *GL_BADGE_CLASSES,
- variant_class,
- size_class,
- *html_options[:class]
- ]
- )
-
- if icon_only
- html_options['aria-label'] = content
- html_options['role'] = 'img'
- end
-
- if options[:icon]
- icon_classes << "gl-mr-2" unless icon_only
- icon = sprite_icon(options[:icon], css_class: icon_classes.join(' '))
-
- content = icon_only ? icon : icon + content
- end
-
- tag = html_options[:href].nil? ? :span : :a
-
- content_tag(tag, content, html_options)
- end
end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 2c84da4862a..6c09e15f56f 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -92,32 +92,6 @@ module BlobHelper
end
end
- def replace_blob_link(project = @project, ref = @ref, path = @path, blob:)
- modify_file_button(
- project,
- ref,
- path,
- blob: blob,
- label: _("Replace"),
- action: "replace",
- btn_class: "default",
- modal_type: "upload"
- )
- end
-
- def delete_blob_link(project = @project, ref = @ref, path = @path, blob:)
- modify_file_button(
- project,
- ref,
- path,
- blob: blob,
- label: _("Delete"),
- action: "delete",
- btn_class: "default",
- modal_type: "remove"
- )
- end
-
def can_modify_blob?(blob, project = @project, ref = @ref)
!blob.stored_externally? && can_edit_tree?(project, ref)
end
diff --git a/app/helpers/ci/builds_helper.rb b/app/helpers/ci/builds_helper.rb
index b4a2cf7bb1e..afd0af18ba7 100644
--- a/app/helpers/ci/builds_helper.rb
+++ b/app/helpers/ci/builds_helper.rb
@@ -25,7 +25,7 @@ module Ci
{
page_path: project_job_path(@project, @build),
build_status: @build.status,
- build_stage: @build.stage,
+ build_stage: @build.stage_name,
log_state: ''
}
end
diff --git a/app/helpers/ci/jobs_helper.rb b/app/helpers/ci/jobs_helper.rb
index 6d63151769f..7b8290ac9ef 100644
--- a/app/helpers/ci/jobs_helper.rb
+++ b/app/helpers/ci/jobs_helper.rb
@@ -11,7 +11,7 @@ module Ci
"runner_settings_url" => project_runners_path(@build.project, anchor: 'js-runners-settings'),
"page_path" => project_job_path(@project, @build),
"build_status" => @build.status,
- "build_stage" => @build.stage,
+ "build_stage" => @build.stage_name,
"log_state" => '',
"build_options" => javascript_build_options,
"retry_outdated_job_docs_url" => help_page_path('ci/pipelines/settings', anchor: 'retry-outdated-jobs')
diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb
index 852eaeca5e3..0de84c0d61f 100644
--- a/app/helpers/ci/runners_helper.rb
+++ b/app/helpers/ci/runners_helper.rb
@@ -84,7 +84,6 @@ module Ci
def group_runners_data_attributes(group)
{
- registration_token: group.runners_token,
group_id: group.id,
group_full_path: group.full_path,
runner_install_help_page: 'https://docs.gitlab.com/runner/install/',
diff --git a/app/helpers/deploy_tokens_helper.rb b/app/helpers/deploy_tokens_helper.rb
index 560d2fcd29f..597823cdac7 100644
--- a/app/helpers/deploy_tokens_helper.rb
+++ b/app/helpers/deploy_tokens_helper.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: true
module DeployTokensHelper
- def expand_deploy_tokens_section?(deploy_token)
- deploy_token.persisted? ||
- deploy_token.errors.present? ||
+ def expand_deploy_tokens_section?(new_deploy_token, created_deploy_token)
+ created_deploy_token ||
+ new_deploy_token.errors.present? ||
Rails.env.test?
end
@@ -14,7 +14,7 @@ module DeployTokensHelper
def packages_registry_enabled?(group_or_project)
Gitlab.config.packages.enabled &&
- can?(current_user, :read_package, group_or_project)
+ can?(current_user, :read_package, group_or_project&.packages_policy_subject)
end
def deploy_token_revoke_button_data(token:, group_or_project:)
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index 457502347ee..5c3b9d4b5ab 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -140,12 +140,12 @@ module DiffHelper
if compare_url
link_text = [
- _('Compare'),
- ' ',
- content_tag(:span, Commit.truncate_sha(diff_file.old_blob.id), class: 'commit-sha'),
- '...',
- content_tag(:span, Commit.truncate_sha(diff_file.blob.id), class: 'commit-sha')
- ].join('').html_safe
+ _('Compare'),
+ ' ',
+ content_tag(:span, Commit.truncate_sha(diff_file.old_blob.id), class: 'commit-sha'),
+ '...',
+ content_tag(:span, Commit.truncate_sha(diff_file.blob.id), class: 'commit-sha')
+ ].join('').html_safe
tooltip = _('Compare submodule commit revisions')
link = content_tag(:span, link_to(link_text, compare_url, class: 'btn gl-button has-tooltip', title: tooltip), class: 'submodule-compare')
diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb
index a910d3d7c9d..62e66b9a3ea 100644
--- a/app/helpers/dropdowns_helper.rb
+++ b/app/helpers/dropdowns_helper.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
module DropdownsHelper
+ # rubocop:disable Metrics/CyclomaticComplexity
def dropdown_tag(toggle_text, options: {}, &block)
content_tag :div, class: "dropdown #{options[:wrapper_class] if options.key?(:wrapper_class)}" do
data_attr = { toggle: "dropdown" }
@@ -16,7 +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] = { qa_selector: "#{options[:dropdown_qa_selector]}" } if options[:dropdown_qa_selector]
+ 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]
dropdown_output << content_tag(:div, content_tag_options) do
output = []
@@ -46,6 +48,7 @@ module DropdownsHelper
dropdown_output.html_safe
end
end
+ # rubocop:enable Metrics/CyclomaticComplexity
def dropdown_toggle(toggle_text, data_attr, options = {})
default_label = data_attr[:default_label]
diff --git a/app/helpers/form_helper.rb b/app/helpers/form_helper.rb
index f74eeeb8c6a..f2e24f54391 100644
--- a/app/helpers/form_helper.rb
+++ b/app/helpers/form_helper.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module FormHelper
- def form_errors(model, type: 'form', truncate: [], pajamas_alert: true)
+ def form_errors(model, type: 'form', truncate: [])
errors = model.errors
return unless errors.any?
@@ -64,7 +64,7 @@ module FormHelper
field_name: "#{issuable_type}[assignee_ids][]",
default_label: _('Unassigned'),
'max-select': 1,
- 'dropdown-header': _('Assignee'),
+ 'dropdown-header': s_('SearchToken|Assignee'),
multi_select: true,
'input-meta': 'name',
'always-show-selectbox': true,
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index bb92792de2d..f77bd6621f9 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -172,6 +172,15 @@ module GroupsHelper
}
end
+ def group_overview_tabs_app_data(group)
+ {
+ subgroups_and_projects_endpoint: group_children_path(group, format: :json),
+ shared_projects_endpoint: group_shared_projects_path(group, format: :json),
+ archived_projects_endpoint: group_children_path(group, format: :json, archived: 'only'),
+ current_group_visibility: group.visibility
+ }.merge(subgroups_and_projects_list_app_data(group))
+ end
+
def enabled_git_access_protocol_options_for_group
case ::Gitlab::CurrentSettings.enabled_git_access_protocol
when nil, ""
diff --git a/app/helpers/ide_helper.rb b/app/helpers/ide_helper.rb
index 4b463b9971d..ec1327cf7ae 100644
--- a/app/helpers/ide_helper.rb
+++ b/app/helpers/ide_helper.rb
@@ -24,7 +24,8 @@ module IdeHelper
'web-terminal-svg-path' => image_path('illustrations/web-ide_promotion.svg'),
'web-terminal-help-path' => help_page_path('user/project/web_ide/index.md', anchor: 'interactive-web-terminals-for-the-web-ide'),
'web-terminal-config-help-path' => help_page_path('user/project/web_ide/index.md', anchor: 'web-ide-configuration-file'),
- 'web-terminal-runners-help-path' => help_page_path('user/project/web_ide/index.md', anchor: 'runner-configuration')
+ 'web-terminal-runners-help-path' => help_page_path('user/project/web_ide/index.md', anchor: 'runner-configuration'),
+ 'csp-nonce' => content_security_policy_nonce
}
end
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 8fd004233e2..96daf398243 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -156,7 +156,7 @@ module IssuablesHelper
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'), class: 'gl-mr-2', aria: { hidden: 'true' })
+ 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 << 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) }
else
output << s_('IssuableStatus|Created %{created_at} by').html_safe % { created_at: time_ago_with_tooltip(issuable.created_at) }
@@ -240,6 +240,7 @@ module IssuablesHelper
updateEndpoint: "#{issuable_path(issuable)}.json",
canUpdate: can?(current_user, :"update_#{issuable.to_ability_name}", issuable),
canDestroy: can?(current_user, :"destroy_#{issuable.to_ability_name}", issuable),
+ canUpdateTimelineEvent: can?(current_user, :admin_incident_management_timeline_event, issuable),
issuableRef: issuable.to_reference,
markdownPreviewPath: preview_markdown_path(parent, target_type: issuable.model_name, target_id: issuable.iid),
markdownDocsPath: help_page_path('user/markdown'),
diff --git a/app/helpers/javascript_helper.rb b/app/helpers/javascript_helper.rb
deleted file mode 100644
index 7cb6da26236..00000000000
--- a/app/helpers/javascript_helper.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-module JavascriptHelper
- def page_specific_javascript_tag(js)
- javascript_include_tag asset_path(js)
- end
-end
diff --git a/app/helpers/jira_connect_helper.rb b/app/helpers/jira_connect_helper.rb
index 4ddfb0224d1..0971fdae8dd 100644
--- a/app/helpers/jira_connect_helper.rb
+++ b/app/helpers/jira_connect_helper.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module JiraConnectHelper
- def jira_connect_app_data(subscriptions)
+ def jira_connect_app_data(subscriptions, installation)
skip_groups = subscriptions.map(&:namespace_id)
{
@@ -11,14 +11,16 @@ module JiraConnectHelper
subscriptions_path: jira_connect_subscriptions_path(format: :json),
users_path: current_user ? nil : jira_connect_users_path, # users_path is used to determine if user is signed in
gitlab_user_path: current_user ? user_path(current_user) : nil,
- oauth_metadata: Feature.enabled?(:jira_connect_oauth, current_user) ? jira_connect_oauth_data.to_json : nil
+ oauth_metadata: Feature.enabled?(:jira_connect_oauth, current_user) ? jira_connect_oauth_data(installation).to_json : nil
}
end
private
- def jira_connect_oauth_data
- oauth_authorize_url = oauth_authorization_url(
+ def jira_connect_oauth_data(installation)
+ oauth_instance_url = installation.oauth_authorization_url
+
+ oauth_authorize_path = oauth_authorization_path(
client_id: Gitlab::CurrentSettings.jira_connect_application_key,
response_type: 'code',
scope: 'api',
@@ -27,8 +29,8 @@ module JiraConnectHelper
)
{
- oauth_authorize_url: oauth_authorize_url,
- oauth_token_url: oauth_token_url,
+ oauth_authorize_url: Gitlab::Utils.append_path(oauth_instance_url, oauth_authorize_path),
+ oauth_token_path: oauth_token_path,
state: oauth_state,
oauth_token_payload: {
grant_type: :authorization_code,
diff --git a/app/helpers/kerberos_helper.rb b/app/helpers/kerberos_helper.rb
new file mode 100644
index 00000000000..31166772367
--- /dev/null
+++ b/app/helpers/kerberos_helper.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module KerberosHelper
+ def allow_basic_auth?
+ true # different behavior in GitLab Enterprise Edition
+ end
+
+ def allow_kerberos_auth?
+ false # different behavior in GitLab Enterprise Edition
+ end
+end
+
+KerberosHelper.prepend_mod_with('KerberosHelper')
diff --git a/app/helpers/kerberos_spnego_helper.rb b/app/helpers/kerberos_spnego_helper.rb
deleted file mode 100644
index 0f6812bc31b..00000000000
--- a/app/helpers/kerberos_spnego_helper.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-
-module KerberosSpnegoHelper
- def allow_basic_auth?
- true # different behavior in GitLab Enterprise Edition
- end
-
- def allow_kerberos_spnego_auth?
- false # different behavior in GitLab Enterprise Edition
- end
-end
-
-KerberosSpnegoHelper.prepend_mod_with('KerberosSpnegoHelper')
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index e865db128c1..0123eb68c9a 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -87,7 +87,7 @@ module LabelsHelper
'#013220' => s_('SuggestedColors|Dark green'),
'#6699cc' => s_('SuggestedColors|Blue-gray'),
'#0000ff' => s_('SuggestedColors|Blue'),
- '#e6e6fa' => s_('SuggestedColors|Lavendar'),
+ '#e6e6fa' => s_('SuggestedColors|Lavender'),
'#9400d3' => s_('SuggestedColors|Dark violet'),
'#330066' => s_('SuggestedColors|Deep violet'),
'#808080' => s_('SuggestedColors|Gray'),
diff --git a/app/helpers/learn_gitlab_helper.rb b/app/helpers/learn_gitlab_helper.rb
index 421cf84f98c..a07922e451a 100644
--- a/app/helpers/learn_gitlab_helper.rb
+++ b/app/helpers/learn_gitlab_helper.rb
@@ -21,8 +21,8 @@ module LearnGitlabHelper
end
def learn_gitlab_onboarding_available?(project)
- OnboardingProgress.onboarding?(project.namespace) &&
- LearnGitlab::Project.new(current_user).available?
+ Onboarding::Progress.onboarding?(project.namespace) &&
+ Onboarding::LearnGitlab.new(current_user).available?
end
private
@@ -33,10 +33,12 @@ module LearnGitlabHelper
action_urls(project).to_h do |action, url|
[
action,
- url: url,
- completed: attributes[OnboardingProgress.column_name(action)].present?,
- svg: image_path("learn_gitlab/#{action}.svg"),
- enabled: true
+ {
+ url: url,
+ completed: attributes[Onboarding::Progress.column_name(action)].present?,
+ svg: image_path("learn_gitlab/#{action}.svg"),
+ enabled: true
+ }
]
end
end
@@ -70,11 +72,14 @@ module LearnGitlabHelper
end
def action_issue_urls
- LearnGitlab::Onboarding::ACTION_ISSUE_IDS.transform_values { |id| project_issue_url(learn_gitlab_project, id) }
+ Onboarding::Completion::ACTION_ISSUE_IDS.transform_values do |id|
+ project_issue_url(learn_gitlab_project, id)
+ end
end
def deploy_section_action_urls(project)
- experiment(:security_actions_continuous_onboarding,
+ experiment(
+ :security_actions_continuous_onboarding,
namespace: project.namespace,
user: current_user,
sticky_to: current_user
@@ -91,11 +96,11 @@ module LearnGitlabHelper
end
def learn_gitlab_project
- @learn_gitlab_project ||= LearnGitlab::Project.new(current_user).project
+ @learn_gitlab_project ||= Onboarding::LearnGitlab.new(current_user).project
end
def onboarding_progress(project)
- OnboardingProgress.find_by(namespace: project.namespace) # rubocop: disable CodeReuse/ActiveRecord
+ Onboarding::Progress.find_by(namespace: project.namespace) # rubocop: disable CodeReuse/ActiveRecord
end
end
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index 4581da4a063..45ded6e35d8 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -256,6 +256,26 @@ module MergeRequestsHelper
def moved_mr_sidebar_enabled?
Feature.enabled?(:moved_mr_sidebar, @project) && defined?(@merge_request)
end
+
+ def sticky_header_data
+ data = {
+ iid: @merge_request.iid,
+ projectPath: @project.full_path,
+ title: markdown_field(@merge_request, :title),
+ isFluidLayout: fluid_layout.to_s,
+ tabs: [
+ ['show', _('Overview'), project_merge_request_path(@project, @merge_request), @merge_request.related_notes.user.count],
+ ['commits', _('Commits'), commits_project_merge_request_path(@project, @merge_request), @commits_count],
+ ['diffs', _('Changes'), diffs_project_merge_request_path(@project, @merge_request), @diffs_count]
+ ]
+ }
+
+ if @project.builds_enabled?
+ data[:tabs].insert(2, ['pipelines', _('Pipelines'), pipelines_project_merge_request_path(@project, @merge_request), @number_of_pipelines])
+ end
+
+ data
+ end
end
MergeRequestsHelper.prepend_mod_with('MergeRequestsHelper')
diff --git a/app/helpers/nav/new_dropdown_helper.rb b/app/helpers/nav/new_dropdown_helper.rb
index dc7d8049556..b017c9a81d1 100644
--- a/app/helpers/nav/new_dropdown_helper.rb
+++ b/app/helpers/nav/new_dropdown_helper.rb
@@ -135,7 +135,7 @@ module Nav
id: 'general_new_group',
title: _('New group'),
href: new_group_path,
- data: { track_action: 'click_link_new_group', track_label: 'plus_menu_dropdown' }
+ data: { track_action: 'click_link_new_group', track_label: 'plus_menu_dropdown', qa_selector: 'global_new_group_link' }
)
)
end
diff --git a/app/helpers/nav/top_nav_helper.rb b/app/helpers/nav/top_nav_helper.rb
index efec6f2d0d8..32d3f4aebb4 100644
--- a/app/helpers/nav/top_nav_helper.rb
+++ b/app/helpers/nav/top_nav_helper.rb
@@ -48,6 +48,13 @@ module Nav
private
+ def top_nav_localized_headers
+ {
+ explore: s_('TopNav|Explore'),
+ switch_to: s_('TopNav|Switch to')
+ }.freeze
+ end
+
def build_base_view_model(builder:, project:, group:)
if current_user
build_view_model(builder: builder, project: project, group: group)
@@ -60,6 +67,7 @@ module Nav
# These come from `app/views/layouts/nav/_explore.html.ham`
if explore_nav_link?(:projects)
builder.add_primary_menu_item_with_shortcut(
+ header: top_nav_localized_headers[:explore],
href: explore_root_path,
active: nav == 'project' || active_nav_link?(path: %w[dashboard#show root#show projects#trending projects#starred projects#index]),
**projects_menu_item_attrs
@@ -68,6 +76,7 @@ module Nav
if explore_nav_link?(:groups)
builder.add_primary_menu_item_with_shortcut(
+ header: top_nav_localized_headers[:explore],
href: explore_groups_path,
active: nav == 'group' || active_nav_link?(controller: [:groups, 'groups/milestones', 'groups/group_members']),
**groups_menu_item_attrs
@@ -76,6 +85,7 @@ module Nav
if explore_nav_link?(:snippets)
builder.add_primary_menu_item_with_shortcut(
+ header: top_nav_localized_headers[:explore],
active: active_nav_link?(controller: :snippets),
href: explore_snippets_path,
**snippets_menu_item_attrs
@@ -89,6 +99,7 @@ module Nav
current_item = project ? current_project(project: project) : {}
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" },
@@ -103,6 +114,7 @@ module Nav
current_item = group ? current_group(group: group) : {}
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" },
@@ -116,6 +128,7 @@ module Nav
if dashboard_nav_link?(:milestones)
builder.add_primary_menu_item_with_shortcut(
id: 'milestones',
+ header: top_nav_localized_headers[:explore],
title: _('Milestones'),
href: dashboard_milestones_path,
active: active_nav_link?(controller: 'dashboard/milestones'),
@@ -127,6 +140,7 @@ module Nav
if dashboard_nav_link?(:snippets)
builder.add_primary_menu_item_with_shortcut(
+ header: top_nav_localized_headers[:explore],
active: active_nav_link?(controller: 'dashboard/snippets'),
data: { qa_selector: 'snippets_link', **menu_data_tracking_attrs('snippets') },
href: dashboard_snippets_path,
@@ -137,6 +151,7 @@ module Nav
if dashboard_nav_link?(:activity)
builder.add_primary_menu_item_with_shortcut(
id: 'activity',
+ header: top_nav_localized_headers[:explore],
title: _('Activity'),
href: activity_dashboard_path,
active: active_nav_link?(path: 'dashboard#activity'),
@@ -266,52 +281,74 @@ module Nav
end
def projects_submenu_items(builder:)
- # These project links come from `app/views/layouts/nav/projects_dropdown/_show.html.haml`
- [
- { id: 'your', title: _('Your projects'), href: dashboard_projects_path },
- { id: 'starred', title: _('Starred projects'), href: starred_dashboard_projects_path },
- { id: 'explore', title: _('Explore projects'), href: explore_root_path },
- { id: 'topics', title: _('Explore topics'), href: topics_explore_projects_path }
- ].each do |item|
+ if Feature.enabled?(:remove_extra_primary_submenu_options)
+ title = _('View all projects')
+
builder.add_primary_menu_item(
- **item,
- data: { qa_selector: 'menu_item_link', qa_title: item[:title], **menu_data_tracking_attrs(item[:title]) }
+ id: 'your',
+ title: title,
+ href: dashboard_projects_path,
+ data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
)
- end
+ else
+ # These project links come from `app/views/layouts/nav/projects_dropdown/_show.html.haml`
+ [
+ { id: 'your', title: _('Your projects'), href: dashboard_projects_path },
+ { id: 'starred', title: _('Starred projects'), href: starred_dashboard_projects_path },
+ { id: 'explore', title: _('Explore projects'), href: explore_root_path },
+ { id: 'topics', title: _('Explore topics'), href: topics_explore_projects_path }
+ ].each do |item|
+ builder.add_primary_menu_item(
+ **item,
+ data: { qa_selector: 'menu_item_link', qa_title: item[:title], **menu_data_tracking_attrs(item[:title]) }
+ )
+ end
- title = _('Create new project')
+ title = _('Create new project')
- builder.add_secondary_menu_item(
- id: 'create',
- title: title,
- href: new_project_path,
- data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
- )
+ builder.add_secondary_menu_item(
+ id: 'create',
+ title: title,
+ href: new_project_path,
+ data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
+ )
+ end
end
def groups_submenu
# These group links come from `app/views/layouts/nav/groups_dropdown/_show.html.haml`
builder = ::Gitlab::Nav::TopNavMenuBuilder.new
- [
- { id: 'your', title: _('Your groups'), href: dashboard_groups_path },
- { id: 'explore', title: _('Explore groups'), href: explore_groups_path }
- ].each do |item|
- builder.add_primary_menu_item(
- **item,
- data: { qa_selector: 'menu_item_link', qa_title: item[:title], **menu_data_tracking_attrs(item[:title]) }
- )
- end
+ if Feature.enabled?(:remove_extra_primary_submenu_options)
+ title = _('View all groups')
- if current_user.can_create_group?
- title = _('Create group')
-
- builder.add_secondary_menu_item(
- id: 'create',
+ builder.add_primary_menu_item(
+ id: 'your',
title: title,
- href: new_group_path,
+ href: dashboard_groups_path,
data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
)
+ else
+ [
+ { id: 'your', title: _('Your groups'), href: dashboard_groups_path },
+ { id: 'explore', title: _('Explore groups'), href: explore_groups_path }
+ ].each do |item|
+ builder.add_primary_menu_item(
+ **item,
+ data: { qa_selector: 'menu_item_link', qa_title: item[:title], **menu_data_tracking_attrs(item[:title]) }
+ )
+ end
+
+ if current_user.can_create_group?
+ title = _('Create group')
+
+ builder.add_secondary_menu_item(
+ id: 'create',
+ title: title,
+ href: new_group_path,
+ data: { qa_selector: 'menu_item_link', qa_title: title, **menu_data_tracking_attrs(title) }
+ )
+ end
end
builder.build
diff --git a/app/helpers/notify_helper.rb b/app/helpers/notify_helper.rb
index c0ba93f4a30..b7ab1c2e2d1 100644
--- a/app/helpers/notify_helper.rb
+++ b/app/helpers/notify_helper.rb
@@ -20,4 +20,15 @@ module NotifyHelper
(source.description || default_description).truncate(200, separator: ' ')
end
+
+ def merge_request_hash_param(merge_request, reviewer)
+ {
+ mr_highlight: '<span style="font-weight: 600;color:#333333;">'.html_safe,
+ highlight_end: '</span>'.html_safe,
+ mr_link: link_to(merge_request.to_reference, merge_request_url(merge_request), style: "font-weight: 600;color:#3777b0;text-decoration:none").html_safe,
+ reviewer_highlight: '<span>'.html_safe,
+ reviewer_avatar: content_tag(:img, nil, height: "24", src: avatar_icon_for_user(reviewer, 24, only_path: false), style: "border-radius:12px;margin:-7px 0 -7px 3px;", width: "24", alt: "Avatar", class: "avatar").html_safe,
+ reviewer_link: link_to(reviewer.name, user_url(reviewer), style: "color:#333333;text-decoration:none;", class: "muted").html_safe
+ }
+ end
end
diff --git a/app/helpers/packages_helper.rb b/app/helpers/packages_helper.rb
index b52357bc891..f9ec20bdd01 100644
--- a/app/helpers/packages_helper.rb
+++ b/app/helpers/packages_helper.rb
@@ -73,6 +73,7 @@ module PackagesHelper
older_than_options: older_than_options.to_json,
is_admin: current_user&.admin.to_s,
admin_settings_path: ci_cd_admin_application_settings_path(anchor: 'js-registry-settings'),
+ project_settings_path: project_settings_packages_and_registries_path(@project),
enable_historic_entries: container_expiration_policies_historic_entry_enabled?.to_s,
help_page_path: help_page_path('user/packages/container_registry/reduce_container_registry_storage', anchor: 'cleanup-policy'),
show_cleanup_policy_link: show_cleanup_policy_link(@project).to_s,
@@ -83,7 +84,8 @@ module PackagesHelper
def settings_data
cleanup_settings_data.merge(
show_container_registry_settings: show_container_registry_settings(@project).to_s,
- show_package_registry_settings: show_package_registry_settings(@project).to_s
+ show_package_registry_settings: show_package_registry_settings(@project).to_s,
+ cleanup_settings_path: cleanup_image_tags_project_settings_packages_and_registries_path(@project)
)
end
end
diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb
index 0c057a29bec..c0665463706 100644
--- a/app/helpers/page_layout_helper.rb
+++ b/app/helpers/page_layout_helper.rb
@@ -150,6 +150,10 @@ module PageLayoutHelper
css_class.join(' ')
end
+ def full_content_class
+ "#{container_class} #{@content_class}" # rubocop:disable Rails/HelperInstanceVariable
+ end
+
def page_itemtype(itemtype = nil)
if itemtype
@page_itemtype = { itemscope: true, itemtype: itemtype }
diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb
index 104026ff21e..bfe39bbc211 100644
--- a/app/helpers/profiles_helper.rb
+++ b/app/helpers/profiles_helper.rb
@@ -53,7 +53,7 @@ module ProfilesHelper
# Overridden in EE::ProfilesHelper#ssh_key_expires_field_description
def ssh_key_expires_field_description
- s_('Profiles|Key becomes invalid on this date.')
+ s_('Profiles|Optional but recommended. If set, key becomes invalid on the specified date.')
end
# Overridden in EE::ProfilesHelper#ssh_key_expiration_policy_enabled?
diff --git a/app/helpers/projects/google_cloud/cloudsql_helper.rb b/app/helpers/projects/google_cloud/cloudsql_helper.rb
new file mode 100644
index 00000000000..0c24254d9b4
--- /dev/null
+++ b/app/helpers/projects/google_cloud/cloudsql_helper.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+module Projects
+ module GoogleCloud
+ module CloudsqlHelper
+ # Sources:
+ # - https://cloud.google.com/sql/docs/postgres/instance-settings
+ # - https://cloud.google.com/sql/docs/mysql/instance-settings
+ # - https://cloud.google.com/sql/docs/sqlserver/instance-settings
+
+ TIERS = [
+ { value: 'db-custom-1-3840', label: '1 vCPU, 3840 MB RAM - Standard' },
+ { value: 'db-custom-2-7680', label: '2 vCPU, 7680 MB RAM - Standard' },
+ { value: 'db-custom-2-13312', label: '2 vCPU, 13312 MB RAM - High memory' },
+ { value: 'db-custom-4-15360', label: '4 vCPU, 15360 MB RAM - Standard' },
+ { value: 'db-custom-4-26624', label: '4 vCPU, 26624 MB RAM - High memory' },
+ { value: 'db-custom-8-30720', label: '8 vCPU, 30720 MB RAM - Standard' },
+ { value: 'db-custom-8-53248', label: '8 vCPU, 53248 MB RAM - High memory' },
+ { value: 'db-custom-16-61440', label: '16 vCPU, 61440 MB RAM - Standard' },
+ { value: 'db-custom-16-106496', label: '16 vCPU, 106496 MB RAM - High memory' },
+ { value: 'db-custom-32-122880', label: '32 vCPU, 122880 MB RAM - Standard' },
+ { value: 'db-custom-32-212992', label: '32 vCPU, 212992 MB RAM - High memory' },
+ { value: 'db-custom-64-245760', label: '64 vCPU, 245760 MB RAM - Standard' },
+ { value: 'db-custom-64-425984', label: '64 vCPU, 425984 MB RAM - High memory' },
+ { value: 'db-custom-96-368640', label: '96 vCPU, 368640 MB RAM - Standard' },
+ { value: 'db-custom-96-638976', label: '96 vCPU, 638976 MB RAM - High memory' }
+ ].freeze
+
+ VERSIONS = {
+ postgres: [
+ { value: 'POSTGRES_14', label: 'PostgreSQL 14' },
+ { value: 'POSTGRES_13', label: 'PostgreSQL 13' },
+ { value: 'POSTGRES_12', label: 'PostgreSQL 12' },
+ { value: 'POSTGRES_11', label: 'PostgreSQL 11' },
+ { value: 'POSTGRES_10', label: 'PostgreSQL 10' },
+ { value: 'POSTGRES_9_6', label: 'PostgreSQL 9.6' }
+ ],
+ mysql: [
+ { value: 'MYSQL_8_0', label: 'MySQL 8' },
+ { value: 'MYSQL_5_7', label: 'MySQL 5.7' },
+ { value: 'MYSQL_5_6', label: 'MySQL 5.6' }
+ ],
+ sqlserver: [
+ { value: 'SQLSERVER_2017_STANDARD', label: 'SQL Server 2017 Standard' },
+ { value: 'SQLSERVER_2017_ENTERPRISE', label: 'SQL Server 2017 Enterprise' },
+ { value: 'SQLSERVER_2017_EXPRESS', label: 'SQL Server 2017 Express' },
+ { value: 'SQLSERVER_2017_WEB', label: 'SQL Server 2017 Web' },
+ { value: 'SQLSERVER_2019_STANDARD', label: 'SQL Server 2019 Standard' },
+ { value: 'SQLSERVER_2019_ENTERPRISE', label: 'SQL Server 2019 Enterprise' },
+ { value: 'SQLSERVER_2019_EXPRESS', label: 'SQL Server 2019 Express' },
+ { value: 'SQLSERVER_2019_WEB', label: 'SQL Server 2019 Web' }
+ ]
+ }.freeze
+ end
+ end
+end
diff --git a/app/helpers/projects/pages_helper.rb b/app/helpers/projects/pages_helper.rb
new file mode 100644
index 00000000000..f46c11db1db
--- /dev/null
+++ b/app/helpers/projects/pages_helper.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Projects
+ module PagesHelper
+ def can_create_pages_custom_domains?(current_user, project)
+ current_user.can?(:update_pages, project) &&
+ (Gitlab.config.pages.external_http || Gitlab.config.pages.external_https) &&
+ project.can_create_custom_domains?
+ end
+ end
+end
diff --git a/app/helpers/projects/pipeline_helper.rb b/app/helpers/projects/pipeline_helper.rb
index 5f2a9f7bf21..c72beb4d722 100644
--- a/app/helpers/projects/pipeline_helper.rb
+++ b/app/helpers/projects/pipeline_helper.rb
@@ -6,7 +6,6 @@ module Projects
def js_pipeline_tabs_data(project, pipeline, _user)
{
- can_generate_codequality_reports: pipeline.can_generate_codequality_reports?.to_json,
failed_jobs_count: pipeline.failed_builds.count,
failed_jobs_summary: prepare_failed_jobs_summary_data(pipeline.failed_builds),
full_path: project.full_path,
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index dfc270adf8b..e760fad7be9 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -172,6 +172,7 @@ module ProjectsHelper
def project_list_cache_key(project, pipeline_status: true)
key = [
+ project.star_count,
project.route.cache_key,
project.cache_key,
project.last_activity_date,
@@ -389,7 +390,10 @@ module ProjectsHelper
pagesAccessControlForced: ::Gitlab::Pages.access_control_is_forced?,
pagesHelpPath: help_page_path('user/project/pages/introduction', anchor: 'gitlab-pages-access-control'),
issuesHelpPath: help_page_path('user/project/issues/index'),
- membersPagePath: project_project_members_path(project)
+ membersPagePath: project_project_members_path(project),
+ environmentsHelpPath: help_page_path('ci/environments/index'),
+ featureFlagsHelpPath: help_page_path('operations/feature_flags'),
+ releasesHelpPath: help_page_path('user/project/releases/index')
}
end
@@ -437,7 +441,6 @@ module ProjectsHelper
def show_inactive_project_deletion_banner?(project)
return false unless project.present? && project.saved?
return false unless delete_inactive_projects?
- return false unless Feature.enabled?(:inactive_projects_deletion, project.root_namespace)
project.inactive?
end
@@ -452,9 +455,9 @@ module ProjectsHelper
def clusters_deprecation_alert_message
if has_active_license?
- s_('ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support.')
+ s_('ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support.')
else
- s_('ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}.')
+ s_('ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}.')
end
end
@@ -635,6 +638,7 @@ module ProjectsHelper
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?,
enforceAuthChecksOnUploads: project.enforce_auth_checks_on_uploads?,
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index dc53be330fe..b16235893ae 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -239,26 +239,26 @@ module SearchHelper
if can?(current_user, :download_code, @project)
result.concat([
- { category: "In this project", label: _("Files"), url: project_tree_path(@project, ref) },
- { category: "In this project", label: _("Commits"), url: project_commits_path(@project, ref) }
- ])
+ { category: "In this project", label: _("Files"), url: project_tree_path(@project, ref) },
+ { category: "In this project", label: _("Commits"), url: project_commits_path(@project, ref) }
+ ])
end
if can?(current_user, :read_repository_graphs, @project)
result.concat([
- { category: "In this project", label: _("Network"), url: project_network_path(@project, ref) },
- { category: "In this project", label: _("Graph"), url: project_graph_path(@project, ref) }
- ])
+ { category: "In this project", label: _("Network"), url: project_network_path(@project, ref) },
+ { category: "In this project", label: _("Graph"), url: project_graph_path(@project, ref) }
+ ])
end
result.concat([
- { category: "In this project", label: _("Issues"), url: project_issues_path(@project) },
- { category: "In this project", label: _("Merge requests"), url: project_merge_requests_path(@project) },
- { category: "In this project", label: _("Milestones"), url: project_milestones_path(@project) },
- { category: "In this project", label: _("Snippets"), url: project_snippets_path(@project) },
- { category: "In this project", label: _("Members"), url: project_project_members_path(@project) },
- { category: "In this project", label: _("Wiki"), url: project_wikis_path(@project) }
- ])
+ { category: "In this project", label: _("Issues"), url: project_issues_path(@project) },
+ { category: "In this project", label: _("Merge requests"), url: project_merge_requests_path(@project) },
+ { category: "In this project", label: _("Milestones"), url: project_milestones_path(@project) },
+ { category: "In this project", label: _("Snippets"), url: project_snippets_path(@project) },
+ { category: "In this project", label: _("Members"), url: project_project_members_path(@project) },
+ { category: "In this project", label: _("Wiki"), url: project_wikis_path(@project) }
+ ])
if can?(current_user, :read_feature_flag, @project)
result << { category: "In this project", label: _("Feature Flags"), url: project_feature_flags_path(@project) }
@@ -294,13 +294,13 @@ module SearchHelper
return [] unless issue && Ability.allowed?(current_user, :read_issue, issue)
[
- {
- category: 'In this project',
- id: issue.id,
- label: search_result_sanitize("#{issue.title} (#{issue.to_reference})"),
- url: issue_path(issue),
- avatar_url: issue.project.avatar_url || ''
- }
+ {
+ category: 'In this project',
+ id: issue.id,
+ label: search_result_sanitize("#{issue.title} (#{issue.to_reference})"),
+ url: issue_path(issue),
+ avatar_url: issue.project.avatar_url || ''
+ }
]
end
diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb
index 58f0af883f5..a711f36fe05 100644
--- a/app/helpers/sorting_helper.rb
+++ b/app/helpers/sorting_helper.rb
@@ -157,7 +157,9 @@ module SortingHelper
{
sort_value_name => sort_title_name,
sort_value_oldest_updated => sort_title_oldest_updated,
- sort_value_recently_updated => sort_title_recently_updated
+ sort_value_recently_updated => sort_title_recently_updated,
+ sort_value_version_desc => sort_title_version_desc,
+ sort_value_version_asc => sort_title_version_asc
}
end
diff --git a/app/helpers/sorting_titles_values_helper.rb b/app/helpers/sorting_titles_values_helper.rb
index 4dfa7689110..b49cb617d80 100644
--- a/app/helpers/sorting_titles_values_helper.rb
+++ b/app/helpers/sorting_titles_values_helper.rb
@@ -86,6 +86,14 @@ module SortingTitlesValuesHelper
s_('SortOptions|Name, descending')
end
+ def sort_title_version_desc
+ s_('SortOptions|Latest version')
+ end
+
+ def sort_title_version_asc
+ s_('SortOptions|Oldest version')
+ end
+
def sort_title_oldest_activity
s_('SortOptions|Oldest updated')
end
@@ -275,6 +283,14 @@ module SortingTitlesValuesHelper
'updated_asc'
end
+ def sort_value_version_asc
+ 'version_asc'
+ end
+
+ def sort_value_version_desc
+ 'version_desc'
+ end
+
def sort_value_popularity
'popularity'
end
diff --git a/app/helpers/storage_helper.rb b/app/helpers/storage_helper.rb
index 9e516d726c1..a60143db739 100644
--- a/app/helpers/storage_helper.rb
+++ b/app/helpers/storage_helper.rb
@@ -23,119 +23,4 @@ module StorageHelper
_("Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / Pipeline Artifacts: %{counter_pipeline_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}") % counters
end
-
- def storage_enforcement_banner_info(context)
- root_ancestor = context.root_ancestor
-
- return unless should_show_storage_enforcement_banner?(context, current_user, root_ancestor)
-
- text_args = storage_enforcement_banner_text_args(root_ancestor, context)
-
- text_paragraph_2 = if root_ancestor.user_namespace?
- html_escape_once(s_("UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. " \
- "View and manage your usage from %{strong_start}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} " \
- "about how to reduce your storage.")).html_safe % text_args[:p2]
- else
- html_escape_once(s_("UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. " \
- "Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}" \
- )).html_safe % text_args[:p2]
- end
-
- {
- text_paragraph_1: html_escape_once(s_("UsageQuota|Effective %{storage_enforcement_date}, namespace storage limits will apply " \
- "to the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}" \
- "View the %{rollout_link_start}rollout schedule for this change%{link_end}.")).html_safe % text_args[:p1],
- text_paragraph_2: text_paragraph_2,
- text_paragraph_3: html_escape_once(s_("UsageQuota|See our %{faq_link_start}FAQ%{link_end} for more information.")).html_safe % text_args[:p3],
- variant: 'warning',
- namespace_id: root_ancestor.id,
- callouts_path: root_ancestor.user_namespace? ? callouts_path : group_callouts_path,
- callouts_feature_name: storage_enforcement_banner_user_callouts_feature_name(root_ancestor)
- }
- end
-
- private
-
- def should_show_storage_enforcement_banner?(context, current_user, root_ancestor)
- return false unless user_allowed_storage_enforcement_banner?(context, current_user, root_ancestor)
- return false if root_ancestor.paid?
- return false unless future_enforcement_date?(root_ancestor)
- return false if user_dismissed_storage_enforcement_banner?(root_ancestor)
-
- ::Feature.enabled?(:namespace_storage_limit_show_preenforcement_banner, root_ancestor)
- end
-
- def user_allowed_storage_enforcement_banner?(context, current_user, root_ancestor)
- return can?(current_user, :maintainer_access, context) unless context.respond_to?(:user_namespace?) && context.user_namespace?
-
- can?(current_user, :owner_access, context)
- end
-
- def storage_enforcement_banner_text_args(root_ancestor, context)
- strong_tags = {
- strong_start: "<strong>".html_safe,
- strong_end: "</strong>".html_safe
- }
-
- extra_message = if context.is_a?(Project)
- html_escape_once(s_("UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "))
- .html_safe % strong_tags.merge(context_name: context.name)
- elsif !context.root?
- html_escape_once(s_("UsageQuota|The %{strong_start}%{context_name}%{strong_end} group will be affected by this. "))
- .html_safe % strong_tags.merge(context_name: context.name)
- else
- ''
- end
-
- {
- p1: {
- storage_enforcement_date: root_ancestor.storage_enforcement_date,
- namespace_name: root_ancestor.name,
- extra_message: extra_message,
- rollout_link_start: '<a href="%{url}" >'.html_safe % { url: help_page_path('user/usage_quotas', anchor: 'namespace-storage-limit-enforcement-schedule') },
- link_end: "</a>".html_safe
- }.merge(strong_tags),
- p2: {
- used_storage: storage_counter(root_ancestor.root_storage_statistics&.storage_size || 0),
- docs_link_start: '<a href="%{url}" >'.html_safe % { url: help_page_path('user/usage_quotas', anchor: 'manage-your-storage-usage') },
- link_end: "</a>".html_safe
- }.merge(strong_tags),
- p3: {
- faq_link_start: '<a href="%{url}" >'.html_safe % { url: "#{Gitlab::Saas.about_pricing_url}faq-efficient-free-tier/#storage-limits-on-gitlab-saas-free-tier" },
- link_end: "</a>".html_safe
- }
- }
- end
-
- def storage_enforcement_banner_user_callouts_feature_name(namespace)
- "storage_enforcement_banner_#{storage_enforcement_banner_threshold(namespace)}_enforcement_threshold"
- end
-
- def storage_enforcement_banner_threshold(namespace)
- days_to_enforcement_date = (namespace.storage_enforcement_date - Date.today)
-
- return :first if days_to_enforcement_date > 30
- return :second if days_to_enforcement_date > 15 && days_to_enforcement_date <= 30
- return :third if days_to_enforcement_date > 7 && days_to_enforcement_date <= 15
- return :fourth if days_to_enforcement_date >= 0 && days_to_enforcement_date <= 7
- end
-
- def user_dismissed_storage_enforcement_banner?(namespace)
- return false unless current_user
-
- if namespace.user_namespace?
- current_user.dismissed_callout?(feature_name: storage_enforcement_banner_user_callouts_feature_name(namespace))
- else
- current_user.dismissed_callout_for_group?(
- feature_name: storage_enforcement_banner_user_callouts_feature_name(namespace),
- group: namespace
- )
- end
- end
-
- def future_enforcement_date?(namespace)
- return true if ::Feature.enabled?(:namespace_storage_limit_bypass_date_check, namespace)
-
- namespace.storage_enforcement_date.present? && namespace.storage_enforcement_date >= Date.today
- end
end
diff --git a/app/helpers/system_note_helper.rb b/app/helpers/system_note_helper.rb
index a957c9ce9e0..3e5f63796b2 100644
--- a/app/helpers/system_note_helper.rb
+++ b/app/helpers/system_note_helper.rb
@@ -45,7 +45,11 @@ module SystemNoteHelper
'attention_requested' => 'user',
'attention_request_removed' => 'user',
'contact' => 'users',
- 'timeline_event' => 'clock'
+ 'timeline_event' => 'clock',
+ 'relate_to_child' => 'link',
+ 'unrelate_from_child' => 'link',
+ 'relate_to_parent' => 'link',
+ 'unrelate_from_parent' => 'link'
}.freeze
def system_note_icon_name(note)
diff --git a/app/helpers/timeboxes_helper.rb b/app/helpers/timeboxes_helper.rb
index 39993bbfb44..11d09a79dcf 100644
--- a/app/helpers/timeboxes_helper.rb
+++ b/app/helpers/timeboxes_helper.rb
@@ -172,18 +172,19 @@ module TimeboxesHelper
def timebox_date_range(timebox)
if timebox.start_date && timebox.due_date
- "#{timebox.start_date.to_s(:medium)}–#{timebox.due_date.to_s(:medium)}"
+ s_("DateRange|%{start_date}–%{end_date}") % { start_date: l(timebox.start_date, format: Date::DATE_FORMATS[:medium]),
+ end_date: l(timebox.due_date, format: Date::DATE_FORMATS[:medium]) }
elsif timebox.due_date
if timebox.due_date.past?
- _("expired on %{timebox_due_date}") % { timebox_due_date: timebox.due_date.to_s(:medium) }
+ _("expired on %{timebox_due_date}") % { timebox_due_date: l(timebox.due_date, format: Date::DATE_FORMATS[:medium]) }
else
- _("expires on %{timebox_due_date}") % { timebox_due_date: timebox.due_date.to_s(:medium) }
+ _("expires on %{timebox_due_date}") % { timebox_due_date: l(timebox.due_date, format: Date::DATE_FORMATS[:medium]) }
end
elsif timebox.start_date
if timebox.start_date.past?
- _("started on %{timebox_start_date}") % { timebox_start_date: timebox.start_date.to_s(:medium) }
+ _("started on %{timebox_start_date}") % { timebox_start_date: l(timebox.start_date, format: Date::DATE_FORMATS[:medium]) }
else
- _("starts on %{timebox_start_date}") % { timebox_start_date: timebox.start_date.to_s(:medium) }
+ _("starts on %{timebox_start_date}") % { timebox_start_date: l(timebox.start_date, format: Date::DATE_FORMATS[:medium]) }
end
end
end
diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb
index 5977f51cab1..ecf29c41100 100644
--- a/app/helpers/todos_helper.rb
+++ b/app/helpers/todos_helper.rb
@@ -142,6 +142,16 @@ module TodosHelper
todos_filter_params.values.none?
end
+ def no_todos_messages
+ [
+ s_('Todos|Good job! Looks like you don\'t have anything left on your To-Do List'),
+ s_('Todos|Isn\'t an empty To-Do List beautiful?'),
+ s_('Todos|Give yourself a pat on the back!'),
+ s_('Todos|Nothing left to do. High five!'),
+ s_('Todos|Henceforth, you shall be known as "To-Do Destroyer"')
+ ]
+ end
+
def todos_filter_path(options = {})
without = options.delete(:without)
diff --git a/app/helpers/users/callouts_helper.rb b/app/helpers/users/callouts_helper.rb
index 3dd6b3f4a80..d8baa185370 100644
--- a/app/helpers/users/callouts_helper.rb
+++ b/app/helpers/users/callouts_helper.rb
@@ -10,8 +10,10 @@ module Users
REGISTRATION_ENABLED_CALLOUT = 'registration_enabled_callout'
UNFINISHED_TAG_CLEANUP_CALLOUT = 'unfinished_tag_cleanup_callout'
SECURITY_NEWSLETTER_CALLOUT = 'security_newsletter_callout'
+ MERGE_REQUEST_SETTINGS_MOVED_CALLOUT = 'merge_request_settings_moved_callout'
REGISTRATION_ENABLED_CALLOUT_ALLOWED_CONTROLLER_PATHS = [/^root/, /^dashboard\S*/, /^admin\S*/].freeze
WEB_HOOK_DISABLED = 'web_hook_disabled'
+ ULTIMATE_FEATURE_REMOVAL_BANNER = 'ultimate_feature_removal_banner'
def show_gke_cluster_integration_callout?(project)
active_nav_link?(controller: sidebar_operations_paths) &&
@@ -71,18 +73,28 @@ module Users
last_failure = DateTime.parse(last_failure) if last_failure
- user_dismissed?(WEB_HOOK_DISABLED, last_failure, namespace: project.namespace)
+ user_dismissed?(WEB_HOOK_DISABLED, last_failure, project: project)
+ end
+
+ def show_merge_request_settings_callout?
+ !user_dismissed?(MERGE_REQUEST_SETTINGS_MOVED_CALLOUT)
+ end
+
+ def ultimate_feature_removal_banner_dismissed?(project)
+ return false unless project
+
+ user_dismissed?(ULTIMATE_FEATURE_REMOVAL_BANNER, project: project)
end
private
- def user_dismissed?(feature_name, ignore_dismissal_earlier_than = nil, namespace: nil)
+ def user_dismissed?(feature_name, ignore_dismissal_earlier_than = nil, project: nil)
return false unless current_user
query = { feature_name: feature_name, ignore_dismissal_earlier_than: ignore_dismissal_earlier_than }
- if namespace
- current_user.dismissed_callout_for_namespace?(namespace: namespace, **query)
+ if project
+ current_user.dismissed_callout_for_project?(project: project, **query)
else
current_user.dismissed_callout?(**query)
end
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index cae2addea9c..271fa47dd97 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -18,10 +18,11 @@ module UsersHelper
return _('We also use email for avatar detection if no avatar is uploaded.') unless user.unconfirmed_email.present?
confirmation_link = link_to _('Resend confirmation e-mail'), user_confirmation_path(user: { email: user.unconfirmed_email }), method: :post
-
- h(_('Please click the link in the confirmation email before continuing. It was sent to ')) +
- content_tag(:strong) { user.unconfirmed_email } + h('.') +
- content_tag(:p) { confirmation_link }
+ h(_('Please click the link in the confirmation email before continuing. It was sent to %{html_tag_strong_start}%{email}%{html_tag_strong_end}.')) % {
+ html_tag_strong_start: '<strong>'.html_safe,
+ html_tag_strong_end: '</strong>'.html_safe,
+ email: user.unconfirmed_email
+ } + content_tag(:p) { confirmation_link }
end
def profile_tabs
@@ -93,6 +94,7 @@ module UsersHelper
[].tap do |badges|
badges << blocked_user_badge(user) if user.blocked?
badges << { text: s_('AdminUsers|Admin'), variant: 'success' } if user.admin?
+ badges << { text: s_('AdminUsers|Bot'), variant: 'muted' } if user.bot?
badges << { text: s_('AdminUsers|External'), variant: 'secondary' } if user.external?
badges << { text: s_("AdminUsers|It's you!"), variant: 'muted' } if current_user == user
badges << { text: s_("AdminUsers|Locked"), variant: 'warning' } if user.access_locked?
@@ -197,6 +199,9 @@ module UsersHelper
banned_badge = { text: s_('AdminUsers|Banned'), variant: 'danger' }
return banned_badge if user.banned?
+ ldap_blocked_badge = { text: s_('AdminUsers|LDAP Blocked'), variant: 'danger' }
+ return ldap_blocked_badge if user.ldap_blocked?
+
{ text: s_('AdminUsers|Blocked'), variant: 'danger' }
end
diff --git a/app/helpers/web_hooks/web_hooks_helper.rb b/app/helpers/web_hooks/web_hooks_helper.rb
index 95122750c2f..e95b90c69ef 100644
--- a/app/helpers/web_hooks/web_hooks_helper.rb
+++ b/app/helpers/web_hooks/web_hooks_helper.rb
@@ -5,6 +5,7 @@ module WebHooks
EXPIRY_TTL = 1.hour
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)
@@ -23,5 +24,9 @@ module WebHooks
ProjectHook.for_projects(project).disabled.exists?
end
end
+
+ def project_hook_page?
+ current_controller?('projects/hooks') || current_controller?('projects/hook_logs')
+ end
end
end
diff --git a/app/mailers/abuse_report_mailer.rb b/app/mailers/abuse_report_mailer.rb
index 1fa85064c57..1bf7deec542 100644
--- a/app/mailers/abuse_report_mailer.rb
+++ b/app/mailers/abuse_report_mailer.rb
@@ -10,7 +10,7 @@ class AbuseReportMailer < ApplicationMailer
@abuse_report = AbuseReport.find(abuse_report_id)
- mail(
+ mail_with_locale(
to: Gitlab::CurrentSettings.abuse_notification_email,
subject: "#{@abuse_report.user.name} (#{@abuse_report.user.username}) was reported for abuse"
)
diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb
index 94ed83a7d4a..bb8d20b8301 100644
--- a/app/mailers/application_mailer.rb
+++ b/app/mailers/application_mailer.rb
@@ -34,4 +34,23 @@ class ApplicationMailer < ActionMailer::Base
address.display_name = Gitlab.config.gitlab.email_display_name
address
end
+
+ def mail_with_locale(headers = {}, &block)
+ locale = recipient_locale headers
+
+ Gitlab::I18n.with_locale(locale) do
+ mail(headers, &block)
+ end
+ end
+
+ def recipient_locale(headers = {})
+ to = Array(headers[:to])
+ locale = I18n.locale
+ locale = preferred_language_by_email(to.first) if to.one?
+ locale
+ end
+
+ def preferred_language_by_email(email)
+ User.find_by_any_email(email)&.preferred_language || I18n.locale
+ end
end
diff --git a/app/mailers/email_rejection_mailer.rb b/app/mailers/email_rejection_mailer.rb
index 25721658285..f681aa67a77 100644
--- a/app/mailers/email_rejection_mailer.rb
+++ b/app/mailers/email_rejection_mailer.rb
@@ -22,6 +22,6 @@ class EmailRejectionMailer < ApplicationMailer
headers['Reply-To'] = @original_message.to.first if can_retry
- mail(headers)
+ mail_with_locale(headers)
end
end
diff --git a/app/mailers/emails/admin_notification.rb b/app/mailers/emails/admin_notification.rb
index 3766b4447d1..5c5497d8eb5 100644
--- a/app/mailers/emails/admin_notification.rb
+++ b/app/mailers/emails/admin_notification.rb
@@ -7,13 +7,13 @@ module Emails
email = user.notification_email_or_default
@unsubscribe_url = unsubscribe_url(email: Base64.urlsafe_encode64(email))
@body = body
- mail to: email, subject: subject
+ mail_with_locale to: email, subject: subject
end
def send_unsubscribed_notification(user_id)
user = User.find(user_id)
email = user.notification_email_or_default
- mail to: email, subject: "Unsubscribed from GitLab administrator notifications"
+ mail_with_locale to: email, subject: "Unsubscribed from GitLab administrator notifications"
end
end
end
diff --git a/app/mailers/emails/groups.rb b/app/mailers/emails/groups.rb
index 07812a01202..3c9bf41c208 100644
--- a/app/mailers/emails/groups.rb
+++ b/app/mailers/emails/groups.rb
@@ -13,7 +13,7 @@ module Emails
def group_email(current_user, group, subj, errors: nil)
@group = group
@errors = errors
- mail(to: current_user.notification_email_for(@group), subject: subject(subj))
+ mail_with_locale(to: current_user.notification_email_for(@group), subject: subject(subj))
end
end
end
diff --git a/app/mailers/emails/identity_verification.rb b/app/mailers/emails/identity_verification.rb
index 2fc8cae06fe..e3089fdef9b 100644
--- a/app/mailers/emails/identity_verification.rb
+++ b/app/mailers/emails/identity_verification.rb
@@ -13,3 +13,5 @@ module Emails
end
end
end
+
+Emails::IdentityVerification.prepend_mod
diff --git a/app/mailers/emails/in_product_marketing.rb b/app/mailers/emails/in_product_marketing.rb
index 1b46d4841b0..972c1da065a 100644
--- a/app/mailers/emails/in_product_marketing.rb
+++ b/app/mailers/emails/in_product_marketing.rb
@@ -31,7 +31,7 @@ module Emails
def mail_to(to:, subject:)
custom_headers = Gitlab.com? ? CUSTOM_HEADERS : {}
- mail(to: to, subject: subject, **custom_headers) do |format|
+ mail_with_locale(to: to, subject: subject, **custom_headers) do |format|
format.html do
@message.format = :html
diff --git a/app/mailers/emails/members.rb b/app/mailers/emails/members.rb
index c885e41671c..33c955f94ee 100644
--- a/app/mailers/emails/members.rb
+++ b/app/mailers/emails/members.rb
@@ -61,7 +61,7 @@ module Emails
Gitlab::Tracking.event(self.class.name, 'invite_email_sent', label: 'invite_email', property: member_id.to_s)
- mail(to: member.invite_email, subject: invite_email_subject, **invite_email_headers) do |format|
+ mail_with_locale(to: member.invite_email, subject: invite_email_subject, **invite_email_headers) do |format|
format.html { render layout: 'unknown_user_mailer' }
format.text { render layout: 'unknown_user_mailer' }
end
diff --git a/app/mailers/emails/pages_domains.rb b/app/mailers/emails/pages_domains.rb
index 6c3dcf8746b..a6e9da18689 100644
--- a/app/mailers/emails/pages_domains.rb
+++ b/app/mailers/emails/pages_domains.rb
@@ -6,7 +6,7 @@ module Emails
@domain = domain
@project = domain.project
- mail(
+ mail_with_locale(
to: recipient.notification_email_for(@project.group),
subject: subject("GitLab Pages domain '#{domain.domain}' has been enabled")
)
@@ -16,7 +16,7 @@ module Emails
@domain = domain
@project = domain.project
- mail(
+ mail_with_locale(
to: recipient.notification_email_for(@project.group),
subject: subject("GitLab Pages domain '#{domain.domain}' has been disabled")
)
@@ -26,7 +26,7 @@ module Emails
@domain = domain
@project = domain.project
- mail(
+ mail_with_locale(
to: recipient.notification_email_for(@project.group),
subject: subject("Verification succeeded for GitLab Pages domain '#{domain.domain}'")
)
@@ -36,7 +36,7 @@ module Emails
@domain = domain
@project = domain.project
- mail(
+ mail_with_locale(
to: recipient.notification_email_for(@project.group),
subject: subject("ACTION REQUIRED: Verification failed for GitLab Pages domain '#{domain.domain}'")
)
@@ -47,7 +47,7 @@ module Emails
@project = domain.project
subject_text = _("ACTION REQUIRED: Something went wrong while obtaining the Let's Encrypt certificate for GitLab Pages domain '%{domain}'") % { domain: domain.domain }
- mail(
+ mail_with_locale(
to: recipient.notification_email_for(@project.group),
subject: subject(subject_text)
)
diff --git a/app/mailers/emails/profile.rb b/app/mailers/emails/profile.rb
index 81f082b9680..8fe471a48f2 100644
--- a/app/mailers/emails/profile.rb
+++ b/app/mailers/emails/profile.rb
@@ -6,7 +6,7 @@ module Emails
@current_user = @user = User.find(user_id)
@target_url = user_url(@user)
@token = token
- mail(to: @user.notification_email_or_default, subject: subject("Account was created for you"))
+ mail_with_locale(to: @user.notification_email_or_default, subject: subject("Account was created for you"))
end
def instance_access_request_email(user, recipient)
@@ -42,7 +42,7 @@ module Emails
@current_user = @user = @key.user
@target_url = user_url(@user)
- mail(to: @user.notification_email_or_default, subject: subject("SSH key was added to your account"))
+ mail_with_locale(to: @user.notification_email_or_default, subject: subject("SSH key was added to your account"))
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -54,7 +54,7 @@ module Emails
@current_user = @user = @gpg_key.user
@target_url = user_url(@user)
- mail(to: @user.notification_email_or_default, subject: subject("GPG key was added to your account"))
+ mail_with_locale(to: @user.notification_email_or_default, subject: subject("GPG key was added to your account"))
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -66,7 +66,7 @@ module Emails
@token_name = token_name
Gitlab::I18n.with_locale(@user.preferred_language) do
- mail(to: @user.notification_email_or_default, subject: subject(_("A new personal access token has been created")))
+ mail_with_locale(to: @user.notification_email_or_default, subject: subject(_("A new personal access token has been created")))
end
end
@@ -79,7 +79,7 @@ module Emails
@days_to_expire = PersonalAccessToken::DAYS_TO_EXPIRE
Gitlab::I18n.with_locale(@user.preferred_language) do
- mail(to: @user.notification_email_or_default, subject: subject(_("Your personal access tokens will expire in %{days_to_expire} days or less") % { days_to_expire: @days_to_expire }))
+ mail_with_locale(to: @user.notification_email_or_default, subject: subject(_("Your personal access tokens will expire in %{days_to_expire} days or less") % { days_to_expire: @days_to_expire }))
end
end
@@ -90,7 +90,7 @@ module Emails
@target_url = profile_personal_access_tokens_url
Gitlab::I18n.with_locale(@user.preferred_language) do
- mail(to: @user.notification_email_or_default, subject: subject(_("Your personal access token has expired")))
+ mail_with_locale(to: @user.notification_email_or_default, subject: subject(_("Your personal access token has expired")))
end
end
@@ -102,7 +102,7 @@ module Emails
@target_url = profile_keys_url
Gitlab::I18n.with_locale(@user.preferred_language) do
- mail(to: @user.notification_email_or_default, subject: subject(_("Your SSH key has expired")))
+ mail_with_locale(to: @user.notification_email_or_default, subject: subject(_("Your SSH key has expired")))
end
end
@@ -114,7 +114,7 @@ module Emails
@target_url = profile_keys_url
Gitlab::I18n.with_locale(@user.preferred_language) do
- mail(to: @user.notification_email_or_default, subject: subject(_("Your SSH key is expiring soon.")))
+ mail_with_locale(to: @user.notification_email_or_default, subject: subject(_("Your SSH key is expiring soon.")))
end
end
@@ -137,7 +137,7 @@ module Emails
@user = user
Gitlab::I18n.with_locale(@user.preferred_language) do
- mail(to: @user.notification_email_or_default, subject: subject(_("Two-factor authentication disabled")))
+ mail_with_locale(to: @user.notification_email_or_default, subject: subject(_("Two-factor authentication disabled")))
end
end
@@ -148,7 +148,7 @@ module Emails
@email = email
Gitlab::I18n.with_locale(@user.preferred_language) do
- mail(to: @user.notification_email_or_default, subject: subject(_("New email address added")))
+ mail_with_locale(to: @user.notification_email_or_default, subject: subject(_("New email address added")))
end
end
end
diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb
index 5b8471abb0f..4bb624c27e9 100644
--- a/app/mailers/emails/projects.rb
+++ b/app/mailers/emails/projects.rb
@@ -7,28 +7,29 @@ module Emails
@project = Project.find project_id
@target_url = project_url(@project)
@old_path_with_namespace = old_path_with_namespace
- mail(to: @user.notification_email_for(@project.group),
- subject: subject("Project was moved"))
+ mail_with_locale(to: @user.notification_email_for(@project.group),
+ subject: subject("Project was moved"))
end
def project_was_exported_email(current_user, project)
@project = project
- mail(to: current_user.notification_email_for(project.group),
- subject: subject("Project was exported"))
+ mail_with_locale(to: current_user.notification_email_for(project.group),
+ subject: subject("Project was exported"))
end
def project_was_not_exported_email(current_user, project, errors)
@project = project
@errors = errors
- mail(to: current_user.notification_email_for(@project.group),
- subject: subject("Project export error"))
+ mail_with_locale(to: current_user.notification_email_for(@project.group),
+ subject: subject("Project export error"))
end
def repository_cleanup_success_email(project, user)
@project = project
@user = user
- mail(to: user.notification_email_for(project.group), subject: subject("Project cleanup has completed"))
+ mail_with_locale(to: user.notification_email_for(project.group),
+ subject: subject("Project cleanup has completed"))
end
def repository_cleanup_failure_email(project, user, error)
@@ -36,7 +37,7 @@ module Emails
@user = user
@error = error
- mail(to: user.notification_email_for(project.group), subject: subject("Project cleanup failure"))
+ mail_with_locale(to: user.notification_email_for(project.group), subject: subject("Project cleanup failure"))
end
def repository_push_email(project_id, opts = {})
@@ -51,9 +52,9 @@ module Emails
add_project_headers
headers['X-GitLab-Author'] = @message.author_username
- mail(from: sender(@message.author_id, send_from_user_email: @message.send_from_committer_email?),
- reply_to: @message.reply_to,
- subject: @message.subject)
+ mail_with_locale(from: sender(@message.author_id, send_from_user_email: @message.send_from_committer_email?),
+ reply_to: @message.reply_to,
+ subject: @message.subject)
end
def prometheus_alert_fired_email(project, user, alert)
@@ -65,7 +66,7 @@ module Emails
add_alert_headers
subject_text = "Alert: #{@alert.email_title}"
- mail(to: user.notification_email_for(@project.group), subject: subject(subject_text))
+ mail_with_locale(to: user.notification_email_for(@project.group), subject: subject(subject_text))
end
def inactive_project_deletion_warning_email(project, user, deletion_date)
diff --git a/app/mailers/emails/releases.rb b/app/mailers/emails/releases.rb
index 4875abafe8d..8fe93f59662 100644
--- a/app/mailers/emails/releases.rb
+++ b/app/mailers/emails/releases.rb
@@ -11,7 +11,7 @@ module Emails
)
@recipient = User.find(user_id)
- mail(
+ mail_with_locale(
to: @recipient.notification_email_for(@project.group),
subject: subject(release_email_subject)
)
diff --git a/app/mailers/emails/remote_mirrors.rb b/app/mailers/emails/remote_mirrors.rb
index 9cde53918b9..791ab7103b4 100644
--- a/app/mailers/emails/remote_mirrors.rb
+++ b/app/mailers/emails/remote_mirrors.rb
@@ -7,7 +7,7 @@ module Emails
@project = @remote_mirror.project
user = User.find(recipient_id)
- mail(to: user.notification_email_for(@project.group), subject: subject('Remote mirror update failed'))
+ mail_with_locale(to: user.notification_email_for(@project.group), subject: subject('Remote mirror update failed'))
end
end
end
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index ed7681e595f..5a3fc70832c 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -38,11 +38,11 @@ class Notify < ApplicationMailer
helper InProductMarketingHelper
def test_email(recipient_email, subject, body)
- mail(to: recipient_email,
- subject: subject,
- body: body.html_safe,
- content_type: 'text/html'
- )
+ mail_with_locale(to: recipient_email,
+ subject: subject,
+ body: body.html_safe,
+ content_type: 'text/html'
+ )
end
# Splits "gitlab.corp.company.com" up into "gitlab.corp.company.com",
@@ -139,7 +139,7 @@ class Notify < ApplicationMailer
@reply_by_email = true
end
- mail(headers)
+ mail_with_locale(headers)
end
# `model` is used on EE code
@@ -225,7 +225,7 @@ class Notify < ApplicationMailer
end
def email_with_layout(to:, subject:, layout: 'mailer')
- mail(to: to, subject: subject) do |format|
+ mail_with_locale(to: to, subject: subject) do |format|
format.html { render layout: layout }
format.text { render layout: layout }
end
diff --git a/app/mailers/previews/notify_preview.rb b/app/mailers/previews/notify_preview.rb
index be8d96012cc..15b6fec3548 100644
--- a/app/mailers/previews/notify_preview.rb
+++ b/app/mailers/previews/notify_preview.rb
@@ -60,8 +60,12 @@ class NotifyPreview < ActionMailer::Preview
end
end
+ def user_cap_reached
+ Notify.user_cap_reached(user.id).message
+ end
+
def new_mention_in_merge_request_email
- Notify.new_mention_in_merge_request_email(user.id, issue.id, user.id).message
+ Notify.new_mention_in_merge_request_email(user.id, merge_request.id, user.id).message
end
def closed_issue_email
@@ -97,7 +101,7 @@ class NotifyPreview < ActionMailer::Preview
end
def closed_merge_request_email
- Notify.closed_merge_request_email(user.id, issue.id, user.id).message
+ Notify.closed_merge_request_email(user.id, merge_request.id, user.id).message
end
def merge_request_status_email
@@ -205,14 +209,6 @@ class NotifyPreview < ActionMailer::Preview
Notify.inactive_project_deletion_warning_email(project, user, '2022-04-22').message
end
- def user_auto_banned_instance_email
- ::Notify.user_auto_banned_email(user.id, user.id, max_project_downloads: 5, within_seconds: 600).message
- end
-
- def user_auto_banned_namespace_email
- ::Notify.user_auto_banned_email(user.id, user.id, max_project_downloads: 5, within_seconds: 600, group: group).message
- end
-
def verification_instructions_email
Notify.verification_instructions_email(user.id, token: '123456', expires_in: 60).message
end
@@ -220,7 +216,7 @@ class NotifyPreview < ActionMailer::Preview
private
def project
- @project ||= Project.find_by_full_path('gitlab-org/gitlab-test')
+ @project ||= Project.first
end
def issue
diff --git a/app/mailers/repository_check_mailer.rb b/app/mailers/repository_check_mailer.rb
index b8f990f26c8..17c36c19955 100644
--- a/app/mailers/repository_check_mailer.rb
+++ b/app/mailers/repository_check_mailer.rb
@@ -14,7 +14,7 @@ class RepositoryCheckMailer < ApplicationMailer
"#{failed_count} projects failed their last repository check"
end
- mail(
+ mail_with_locale(
to: User.admins.active.pluck(:email),
subject: "GitLab Admin | #{@message}"
)
diff --git a/app/models/active_session.rb b/app/models/active_session.rb
index 9f634e70ff4..7dbc95c251b 100644
--- a/app/models/active_session.rb
+++ b/app/models/active_session.rb
@@ -83,21 +83,21 @@ class ActiveSession
is_impersonated: request.session[:impersonator_id].present?
)
- redis.pipelined do
- redis.setex(
+ redis.pipelined do |pipeline|
+ pipeline.setex(
key_name(user.id, session_private_id),
expiry,
active_user_session.dump
)
# Deprecated legacy format - temporary to support mixed deployments
- redis.setex(
+ pipeline.setex(
key_name_v1(user.id, session_private_id),
expiry,
Marshal.dump(active_user_session)
)
- redis.sadd(
+ pipeline.sadd(
lookup_key_name(user.id),
session_private_id
)
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 579f2c38ae6..edb9a2053b1 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -10,11 +10,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 %i[max_package_files_for_package_destruction], 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 :pseudonymizer_enabled, remove_with: '15.1', remove_after: '2022-06-22'
- ignore_column :enforce_ssh_key_expiration, remove_with: '15.2', remove_after: '2022-07-22'
- ignore_column :enforce_pat_expiration, remove_with: '15.2', remove_after: '2022-07-22'
INSTANCE_REVIEW_MIN_USERS = 50
GRAFANA_URL_ERROR_MESSAGE = 'Please check your Grafana URL setting in ' \
@@ -221,6 +217,10 @@ class ApplicationSetting < ApplicationRecord
numericality: { only_integer: true, greater_than_or_equal_to: 0,
less_than: ::Gitlab::Pages::MAX_SIZE / 1.megabyte }
+ validates :max_pages_custom_domains_per_project,
+ presence: true,
+ numericality: { only_integer: true, greater_than_or_equal_to: 0 }
+
validates :jobs_per_stage_page_size,
presence: true,
numericality: { only_integer: true, greater_than_or_equal_to: 0 }
@@ -406,6 +406,10 @@ class ApplicationSetting < ApplicationRecord
validates :invisible_captcha_enabled,
inclusion: { in: [true, false], message: _('must be a boolean value') }
+ validates :invitation_flow_enforcement,
+ allow_nil: false,
+ inclusion: { in: [true, false], message: _('must be a boolean value') }
+
Gitlab::SSHPublicKey.supported_types.each do |type|
validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type }
end
@@ -621,6 +625,10 @@ class ApplicationSetting < ApplicationRecord
validates :inactive_projects_send_warning_email_after_months,
numericality: { only_integer: true, greater_than: 0, less_than: :inactive_projects_delete_after_months }
+ validates :cube_api_base_url,
+ addressable_url: { allow_localhost: true, allow_local_network: false },
+ allow_blank: true
+
attr_encrypted :asset_proxy_secret_key,
mode: :per_attribute_iv,
key: Settings.attr_encrypted_db_key_base_truncated,
@@ -658,6 +666,7 @@ class ApplicationSetting < ApplicationRecord
attr_encrypted :database_grafana_api_key, encryption_options_base_32_aes_256_gcm.merge(encode: false, encode_iv: false)
attr_encrypted :arkose_labs_public_api_key, encryption_options_base_32_aes_256_gcm.merge(encode: false, encode_iv: false)
attr_encrypted :arkose_labs_private_api_key, encryption_options_base_32_aes_256_gcm.merge(encode: false, encode_iv: false)
+ attr_encrypted :cube_api_key, encryption_options_base_32_aes_256_gcm
validates :disable_feed_token,
inclusion: { in: [true, false], message: _('must be a boolean value') }
diff --git a/app/models/ci/bridge.rb b/app/models/ci/bridge.rb
index 3fda8693a58..323d759510e 100644
--- a/app/models/ci/bridge.rb
+++ b/app/models/ci/bridge.rb
@@ -75,9 +75,9 @@ module Ci
def self.clone_accessors
%i[pipeline project ref tag options name
- allow_failure stage stage_id stage_idx
+ allow_failure stage stage_idx
yaml_variables when description needs_attributes
- scheduling_type].freeze
+ scheduling_type ci_stage partition_id].freeze
end
def inherit_status_from_downstream!(pipeline)
@@ -183,6 +183,10 @@ module Ci
false
end
+ def prevent_rollback_deployment?
+ false
+ end
+
def expanded_environment_name
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index bf8817e6e78..4e58f877217 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -11,7 +11,7 @@ module Ci
include Presentable
include Importable
include Ci::HasRef
- include HasDeploymentName
+ include Ci::TrackEnvironmentUsage
extend ::Gitlab::Utils::Override
@@ -34,7 +34,7 @@ module Ci
DEPLOYMENT_NAMES = %w[deploy release rollout].freeze
- has_one :deployment, as: :deployable, class_name: 'Deployment'
+ has_one :deployment, as: :deployable, class_name: 'Deployment', inverse_of: :deployable
has_one :pending_state, class_name: 'Ci::BuildPendingState', inverse_of: :build
has_one :queuing_entry, class_name: 'Ci::PendingBuild', foreign_key: :build_id
has_one :runtime_metadata, class_name: 'Ci::RunningBuild', foreign_key: :build_id
@@ -194,7 +194,7 @@ module Ci
after_save :stick_build_if_status_changed
after_create unless: :importing? do |build|
- run_after_commit { build.feature_flagged_execute_hooks }
+ run_after_commit { build.execute_hooks }
end
class << self
@@ -214,10 +214,11 @@ module Ci
def clone_accessors
%i[pipeline project ref tag options name
- allow_failure stage stage_id stage_idx trigger_request
+ allow_failure stage stage_idx trigger_request
yaml_variables when environment coverage_regex
description tag_list protected needs_attributes
- job_variables_attributes resource_group scheduling_type].freeze
+ job_variables_attributes resource_group scheduling_type
+ ci_stage partition_id].freeze
end
end
@@ -285,7 +286,7 @@ module Ci
build.run_after_commit do
BuildQueueWorker.perform_async(id)
- build.feature_flagged_execute_hooks
+ build.execute_hooks
end
end
@@ -313,7 +314,7 @@ module Ci
build.run_after_commit do
build.ensure_persistent_ref
- build.feature_flagged_execute_hooks
+ build.execute_hooks
end
end
@@ -442,6 +443,15 @@ module Ci
manual? && starts_environment? && deployment&.blocked?
end
+ def prevent_rollback_deployment?
+ strong_memoize(:prevent_rollback_deployment) do
+ Feature.enabled?(:prevent_outdated_deployment_jobs, project) &&
+ starts_environment? &&
+ project.ci_forward_deployment_enabled? &&
+ deployment&.older_than_last_successful_deployment?
+ end
+ end
+
def schedulable?
self.when == 'delayed' && options[:start_in].present?
end
@@ -703,25 +713,7 @@ module Ci
end
def has_test_reports?
- job_artifacts.test_reports.exists?
- end
-
- def has_old_trace?
- old_trace.present?
- end
-
- def trace=(data)
- raise NotImplementedError
- end
-
- def old_trace
- read_attribute(:trace)
- end
-
- def erase_old_trace!
- return unless has_old_trace?
-
- update_column(:trace, nil)
+ job_artifacts.of_report_type(:test).exists?
end
def ensure_trace_metadata!
@@ -780,14 +772,6 @@ module Ci
pending? && !any_runners_online?
end
- def feature_flagged_execute_hooks
- if Feature.enabled?(:execute_build_hooks_inline, project)
- execute_hooks
- else
- BuildHooksWorker.perform_async(self)
- end
- end
-
def execute_hooks
return unless project
return if user&.blocked?
@@ -823,41 +807,6 @@ module Ci
end
end
- def erase_erasable_artifacts!
- if project.refreshing_build_artifacts_size?
- Gitlab::ProjectStatsRefreshConflictsLogger.warn_artifact_deletion_during_stats_refresh(
- method: 'Ci::Build#erase_erasable_artifacts!',
- project_id: project_id
- )
- end
-
- destroyed_artifacts = job_artifacts.erasable.destroy_all # rubocop: disable Cop/DestroyAll
-
- Gitlab::Ci::Artifacts::Logger.log_deleted(destroyed_artifacts, 'Ci::Build#erase_erasable_artifacts!')
-
- destroyed_artifacts
- end
-
- def erase(opts = {})
- return false unless erasable?
-
- if project.refreshing_build_artifacts_size?
- Gitlab::ProjectStatsRefreshConflictsLogger.warn_artifact_deletion_during_stats_refresh(
- method: 'Ci::Build#erase',
- project_id: project_id
- )
- end
-
- # TODO: We should use DestroyBatchService here
- # See https://gitlab.com/gitlab-org/gitlab/-/issues/369132
- destroyed_artifacts = job_artifacts.destroy_all # rubocop: disable Cop/DestroyAll
-
- Gitlab::Ci::Artifacts::Logger.log_deleted(destroyed_artifacts, 'Ci::Build#erase')
-
- erase_trace!
- update_erased!(opts[:erased_by])
- end
-
def erasable?
complete? && (artifacts? || has_job_artifacts? || has_trace?)
end
@@ -1004,15 +953,11 @@ module Ci
end
def collect_test_reports!(test_reports)
- test_reports.get_suite(test_suite_name).tap do |test_suite|
- each_report(Ci::JobArtifact.file_types_for_report(:test)) do |file_type, blob|
- Gitlab::Ci::Parsers.fabricate!(file_type).parse!(
- blob,
- test_suite,
- job: self
- )
- end
+ each_report(Ci::JobArtifact.file_types_for_report(:test)) do |file_type, blob|
+ Gitlab::Ci::Parsers.fabricate!(file_type).parse!(blob, test_reports, job: self)
end
+
+ test_reports
end
def collect_accessibility_reports!(accessibility_report)
@@ -1154,18 +1099,6 @@ module Ci
.include?(exit_code)
end
- def track_deployment_usage
- Gitlab::Utils::UsageData.track_usage_event('ci_users_executing_deployment_job', user_id) if user_id.present? && count_user_deployment?
- end
-
- def track_verify_usage
- Gitlab::Utils::UsageData.track_usage_event('ci_users_executing_verify_environment_job', user_id) if user_id.present? && count_user_verification?
- end
-
- def count_user_verification?
- has_environment? && environment_action == 'verify'
- end
-
def each_report(report_types)
job_artifacts_for_types(report_types).each do |report_artifact|
report_artifact.each_blob do |blob|
@@ -1189,6 +1122,14 @@ module Ci
job_artifacts.map(&:file_type)
end
+ def test_suite_name
+ if matrix_build?
+ name
+ else
+ group_name
+ end
+ end
+
protected
def run_status_commit_hooks!
@@ -1199,14 +1140,6 @@ module Ci
private
- def test_suite_name
- if matrix_build?
- name
- else
- group_name
- end
- end
-
def matrix_build?
options.dig(:parallel, :matrix).present?
end
@@ -1245,14 +1178,6 @@ module Ci
job_artifacts.select { |artifact| artifact.file_type.in?(report_types) }
end
- def erase_trace!
- trace.erase!
- end
-
- def update_erased!(user = nil)
- self.update(erased_by: user, erased_at: Time.current, artifacts_expire_at: nil)
- end
-
def environment_url
options&.dig(:environment, :url) || persisted_environment&.external_url
end
@@ -1298,7 +1223,7 @@ module Ci
end
def observe_report_types
- return unless ::Gitlab.com? && Feature.enabled?(:report_artifact_build_completed_metrics_on_build_completion)
+ return unless ::Gitlab.com?
report_types = options&.dig(:artifacts, :reports)&.keys || []
diff --git a/app/models/ci/build_metadata.rb b/app/models/ci/build_metadata.rb
index 5fc21ba3f28..3bdf2f90acb 100644
--- a/app/models/ci/build_metadata.rb
+++ b/app/models/ci/build_metadata.rb
@@ -9,7 +9,6 @@ module Ci
include Presentable
include ChronicDurationAttribute
include Gitlab::Utils::StrongMemoize
- include IgnorableColumns
self.table_name = 'ci_builds_metadata'
@@ -39,8 +38,6 @@ module Ci
job_timeout_source: 4
}
- ignore_columns :runner_features, remove_with: '15.1', remove_after: '2022-05-22'
-
def update_timeout_state
timeout = timeout_with_highest_precedence
diff --git a/app/models/ci/freeze_period_status.rb b/app/models/ci/freeze_period_status.rb
index befa935e750..e810bb3f229 100644
--- a/app/models/ci/freeze_period_status.rb
+++ b/app/models/ci/freeze_period_status.rb
@@ -13,32 +13,16 @@ module Ci
end
def within_freeze_period?(period)
- # previous_freeze_end, ..., previous_freeze_start, ..., NOW, ..., next_freeze_end, ..., next_freeze_start
- # Current time is within a freeze period if
- # it falls between a previous freeze start and next freeze end
- start_freeze = Gitlab::Ci::CronParser.new(period.freeze_start, period.cron_timezone)
- end_freeze = Gitlab::Ci::CronParser.new(period.freeze_end, period.cron_timezone)
-
- previous_freeze_start = previous_time(start_freeze)
- previous_freeze_end = previous_time(end_freeze)
- next_freeze_start = next_time(start_freeze)
- next_freeze_end = next_time(end_freeze)
-
- previous_freeze_end < previous_freeze_start &&
- previous_freeze_start <= time_zone_now &&
- time_zone_now <= next_freeze_end &&
- next_freeze_end < next_freeze_start
- end
+ 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)
- private
+ start_freeze = start_freeze_cron.previous_time_from(time_zone_now)
+ end_freeze = end_freeze_cron.next_time_from(start_freeze)
- def previous_time(cron_parser)
- cron_parser.previous_time_from(time_zone_now)
+ start_freeze <= time_zone_now && time_zone_now <= end_freeze
end
- def next_time(cron_parser)
- cron_parser.next_time_from(time_zone_now)
- end
+ private
def time_zone_now
@time_zone_now ||= Time.zone.now
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index 71d33f0bb63..922806a21c3 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -2,6 +2,7 @@
module Ci
class JobArtifact < Ci::ApplicationRecord
+ include Ci::Partitionable
include IgnorableColumns
include AfterCommitQueue
include ObjectStorage::BackgroundMove
@@ -9,6 +10,7 @@ module Ci
include UsageStatistics
include Sortable
include Artifactable
+ include Lockable
include FileStoreMounter
include EachBatch
include Gitlab::Utils::StrongMemoize
@@ -22,8 +24,7 @@ module Ci
accessibility: %w[accessibility],
coverage: %w[cobertura],
codequality: %w[codequality],
- terraform: %w[terraform],
- sbom: %w[cyclonedx]
+ terraform: %w[terraform]
}.freeze
DEFAULT_FILE_NAMES = {
@@ -54,7 +55,7 @@ module Ci
requirements: 'requirements.json',
coverage_fuzzing: 'gl-coverage-fuzzing.json',
api_fuzzing: 'gl-api-fuzzing-report.json',
- cyclonedx: 'gl-sbom.cdx.zip'
+ cyclonedx: 'gl-sbom.cdx.json'
}.freeze
INTERNAL_TYPES = {
@@ -72,6 +73,7 @@ module Ci
cobertura: :gzip,
cluster_applications: :gzip, # DEPRECATED: https://gitlab.com/gitlab-org/gitlab/-/issues/361094
lsif: :zip,
+ cyclonedx: :gzip,
# Security reports and license scanning reports are raw artifacts
# because they used to be fetched by the frontend, but this is not the case anymore.
@@ -94,8 +96,7 @@ module Ci
terraform: :raw,
requirements: :raw,
coverage_fuzzing: :raw,
- api_fuzzing: :raw,
- cyclonedx: :zip
+ api_fuzzing: :raw
}.freeze
DOWNLOADABLE_TYPES = %w[
@@ -134,14 +135,16 @@ module Ci
mount_file_store_uploader JobArtifactUploader, skip_store_file: true
+ before_save :set_size, if: :file_changed?
after_save :store_file_in_transaction!, unless: :store_after_commit?
after_commit :store_file_after_transaction!, on: [:create, :update], if: :store_after_commit?
+ validates :job, presence: true
validates :file_format, presence: true, unless: :trace?, on: :create
validate :validate_file_format!, unless: :trace?, on: :create
- before_save :set_size, if: :file_changed?
update_project_statistics project_statistics_name: :build_artifacts_size
+ partitionable scope: :job
scope :not_expired, -> { where('expire_at IS NULL OR expire_at > ?', Time.current) }
scope :for_sha, ->(sha, project_id) { joins(job: :pipeline).where(ci_pipelines: { sha: sha, project_id: project_id }) }
@@ -160,12 +163,6 @@ module Ci
where(file_type: types)
end
- REPORT_FILE_TYPES.each do |report_type, file_types|
- scope "#{report_type}_reports", -> do
- with_file_types(file_types)
- end
- end
-
scope :all_reports, -> do
with_file_types(REPORT_TYPES.keys.map(&:to_s))
end
@@ -229,25 +226,20 @@ module Ci
hashed_path: 2
}
- # `locked` will be populated from the source of truth on Ci::Pipeline
- # in order to clean up expired job artifacts in a performant way.
- # The values should be the same as `Ci::Pipeline.lockeds` with the
- # additional value of `unknown` to indicate rows that have not
- # yet been populated from the parent Ci::Pipeline
- enum locked: {
- unlocked: 0,
- artifacts_locked: 1,
- unknown: 2
- }, _prefix: :artifact
-
def validate_file_format!
unless TYPE_AND_FORMAT_PAIRS[self.file_type&.to_sym] == self.file_format&.to_sym
errors.add(:base, _('Invalid file format with specified file type'))
end
end
+ def self.of_report_type(report_type)
+ file_types = file_types_for_report(report_type)
+
+ with_file_types(file_types)
+ end
+
def self.file_types_for_report(report_type)
- REPORT_FILE_TYPES.fetch(report_type)
+ REPORT_FILE_TYPES.fetch(report_type) { raise ArgumentError, "Unrecognized report type: #{report_type}" }
end
def self.associated_file_types_for(file_type)
diff --git a/app/models/ci/job_token/scope.rb b/app/models/ci/job_token/scope.rb
index 3a5765aa00c..26a49d6a730 100644
--- a/app/models/ci/job_token/scope.rb
+++ b/app/models/ci/job_token/scope.rb
@@ -30,10 +30,7 @@ module Ci
end
def all_projects
- Project.from_union([
- Project.id_in(source_project),
- Project.id_in(target_project_ids)
- ], remove_duplicates: false)
+ Project.from_union(target_projects, remove_duplicates: false)
end
private
@@ -41,6 +38,13 @@ module Ci
def target_project_ids
Ci::JobToken::ProjectScopeLink.from_project(source_project).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/namespace_mirror.rb b/app/models/ci/namespace_mirror.rb
index e8f08db597f..5ea51fbe0a7 100644
--- a/app/models/ci/namespace_mirror.rb
+++ b/app/models/ci/namespace_mirror.rb
@@ -43,20 +43,6 @@ module Ci
upsert({ namespace_id: event.namespace_id, traversal_ids: traversal_ids },
unique_by: :namespace_id)
-
- # It won't be necessary once we remove `sync_traversal_ids`.
- # More info: https://gitlab.com/gitlab-org/gitlab/-/issues/347541
- sync_children_namespaces!(event.namespace_id, traversal_ids)
- end
-
- private
-
- def sync_children_namespaces!(namespace_id, traversal_ids)
- by_group_and_descendants(namespace_id)
- .where.not(namespace_id: namespace_id)
- .update_all(
- "traversal_ids = ARRAY[#{sanitize_sql(traversal_ids.join(','))}]::int[] || traversal_ids[array_position(traversal_ids, #{sanitize_sql(namespace_id)}) + 1:]"
- )
end
end
end
diff --git a/app/models/ci/partition.rb b/app/models/ci/partition.rb
new file mode 100644
index 00000000000..d773038df01
--- /dev/null
+++ b/app/models/ci/partition.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module Ci
+ class Partition < Ci::ApplicationRecord
+ end
+end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index a94330270e2..1e328c3c573 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -2,6 +2,7 @@
module Ci
class Pipeline < Ci::ApplicationRecord
+ include Ci::Partitionable
include Ci::HasStatus
include Importable
include AfterCommitQueue
@@ -31,7 +32,7 @@ module Ci
sha_attribute :source_sha
sha_attribute :target_sha
-
+ partitionable scope: ->(_) { Ci::Pipeline.current_partition_value }
# Ci::CreatePipelineService returns Ci::Pipeline so this is the only place
# where we can pass additional information from the service. This accessor
# is used for storing the processed metadata for linting purposes.
@@ -296,6 +297,12 @@ module Ci
end
end
+ after_transition any => ::Ci::Pipeline.completed_statuses do |pipeline|
+ pipeline.run_after_commit do
+ ::Ci::JobArtifacts::TrackArtifactReportWorker.perform_async(pipeline.id)
+ end
+ end
+
after_transition any => ::Ci::Pipeline.stopped_statuses do |pipeline|
pipeline.run_after_commit do
pipeline.persistent_ref.delete
@@ -422,6 +429,10 @@ module Ci
end
def self.jobs_count_in_alive_pipelines
+ created_after(24.hours.ago).alive.joins(:statuses).count
+ end
+
+ def self.builds_count_in_alive_pipelines
created_after(24.hours.ago).alive.joins(:builds).count
end
@@ -472,8 +483,12 @@ module Ci
@auto_devops_pipelines_completed_total ||= Gitlab::Metrics.counter(:auto_devops_pipelines_completed_total, 'Number of completed auto devops pipelines')
end
+ def self.current_partition_value
+ 100
+ end
+
def uses_needs?
- builds.where(scheduling_type: :dag).any?
+ processables.where(scheduling_type: :dag).any?
end
def stages_count
@@ -605,7 +620,7 @@ module Ci
if cascade_to_children
# cancel any bridges that could spin up new child pipelines
- cancel_jobs(bridges_in_self_and_descendants.cancelable, retries: retries, auto_canceled_by_pipeline_id: auto_canceled_by_pipeline_id)
+ cancel_jobs(bridges_in_self_and_project_descendants.cancelable, retries: retries, auto_canceled_by_pipeline_id: auto_canceled_by_pipeline_id)
cancel_children(auto_canceled_by_pipeline_id: auto_canceled_by_pipeline_id, execute_async: execute_async)
end
end
@@ -937,26 +952,26 @@ module Ci
).base_and_descendants.select(:id)
end
- def build_with_artifacts_in_self_and_descendants(name)
- builds_in_self_and_descendants
+ def build_with_artifacts_in_self_and_project_descendants(name)
+ builds_in_self_and_project_descendants
.ordered_by_pipeline # find job in hierarchical order
.with_downloadable_artifacts
.find_by_name(name)
end
- def builds_in_self_and_descendants
- Ci::Build.latest.where(pipeline: self_and_descendants)
+ def builds_in_self_and_project_descendants
+ Ci::Build.latest.where(pipeline: self_and_project_descendants)
end
- def bridges_in_self_and_descendants
- Ci::Bridge.latest.where(pipeline: self_and_descendants)
+ def bridges_in_self_and_project_descendants
+ Ci::Bridge.latest.where(pipeline: self_and_project_descendants)
end
- def environments_in_self_and_descendants(deployment_status: nil)
+ def environments_in_self_and_project_descendants(deployment_status: nil)
# We limit to 100 unique environments for application safety.
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/340781#note_699114700
expanded_environment_names =
- builds_in_self_and_descendants.joins(:metadata)
+ builds_in_self_and_project_descendants.joins(:metadata)
.where.not('ci_builds_metadata.expanded_environment_name' => nil)
.distinct('ci_builds_metadata.expanded_environment_name')
.limit(100)
@@ -971,17 +986,22 @@ module Ci
end
# With multi-project and parent-child pipelines
- def all_pipelines_in_hierarchy
+ def self_and_downstreams
+ object_hierarchy.base_and_descendants
+ end
+
+ # With multi-project and parent-child pipelines
+ def upstream_and_all_downstreams
object_hierarchy.all_objects
end
# With only parent-child pipelines
- def self_and_ancestors
+ def self_and_project_ancestors
object_hierarchy(project_condition: :same).base_and_ancestors
end
# With only parent-child pipelines
- def self_and_descendants
+ def self_and_project_descendants
object_hierarchy(project_condition: :same).base_and_descendants
end
@@ -990,8 +1010,8 @@ module Ci
object_hierarchy(project_condition: :same).descendants
end
- def self_and_descendants_complete?
- self_and_descendants.all?(&:complete?)
+ def self_and_project_descendants_complete?
+ self_and_project_descendants.all?(&:complete?)
end
# Follow the parent-child relationships and return the top-level parent
@@ -1006,7 +1026,12 @@ module Ci
# Follow the upstream pipeline relationships, regardless of multi-project or
# parent-child, and return the top-level ancestor.
def upstream_root
- object_hierarchy.base_and_ancestors(hierarchy_order: :desc).first
+ @upstream_root ||= object_hierarchy.base_and_ancestors(hierarchy_order: :desc).first
+ end
+
+ # Applies to all parent-child and multi-project pipelines
+ def complete_hierarchy_count
+ upstream_root.self_and_downstreams.count
end
def bridge_triggered?
@@ -1052,11 +1077,11 @@ module Ci
end
def latest_test_report_builds
- latest_report_builds(Ci::JobArtifact.test_reports).preload(:project, :metadata)
+ latest_report_builds(Ci::JobArtifact.of_report_type(:test)).preload(:project, :metadata)
end
- def latest_report_builds_in_self_and_descendants(reports_scope = ::Ci::JobArtifact.all_reports)
- builds_in_self_and_descendants.with_artifacts(reports_scope)
+ def latest_report_builds_in_self_and_project_descendants(reports_scope = ::Ci::JobArtifact.all_reports)
+ builds_in_self_and_project_descendants.with_artifacts(reports_scope)
end
def builds_with_coverage
@@ -1068,10 +1093,14 @@ module Ci
end
def has_reports?(reports_scope)
+ latest_report_builds(reports_scope).exists?
+ end
+
+ def complete_and_has_reports?(reports_scope)
if Feature.enabled?(:mr_show_reports_immediately, project, type: :development)
latest_report_builds(reports_scope).exists?
else
- complete? && latest_report_builds(reports_scope).exists?
+ complete? && has_reports?(reports_scope)
end
end
@@ -1084,7 +1113,7 @@ module Ci
end
def can_generate_codequality_reports?
- has_reports?(Ci::JobArtifact.codequality_reports)
+ complete_and_has_reports?(Ci::JobArtifact.of_report_type(:codequality))
end
def test_report_summary
@@ -1103,7 +1132,7 @@ module Ci
def accessibility_reports
Gitlab::Ci::Reports::AccessibilityReports.new.tap do |accessibility_reports|
- latest_report_builds(Ci::JobArtifact.accessibility_reports).each do |build|
+ latest_report_builds(Ci::JobArtifact.of_report_type(:accessibility)).each do |build|
build.collect_accessibility_reports!(accessibility_reports)
end
end
@@ -1111,7 +1140,7 @@ module Ci
def codequality_reports
Gitlab::Ci::Reports::CodequalityReports.new.tap do |codequality_reports|
- latest_report_builds(Ci::JobArtifact.codequality_reports).each do |build|
+ latest_report_builds(Ci::JobArtifact.of_report_type(:codequality)).each do |build|
build.collect_codequality_reports!(codequality_reports)
end
end
@@ -1119,7 +1148,7 @@ module Ci
def terraform_reports
::Gitlab::Ci::Reports::TerraformReports.new.tap do |terraform_reports|
- latest_report_builds(::Ci::JobArtifact.terraform_reports).each do |build|
+ latest_report_builds(::Ci::JobArtifact.of_report_type(:terraform)).each do |build|
build.collect_terraform_reports!(terraform_reports)
end
end
@@ -1307,7 +1336,7 @@ module Ci
def has_test_reports?
strong_memoize(:has_test_reports) do
- has_reports?(::Ci::JobArtifact.test_reports)
+ has_reports?(::Ci::JobArtifact.of_report_type(:test))
end
end
diff --git a/app/models/ci/pipeline_artifact.rb b/app/models/ci/pipeline_artifact.rb
index cdc3d69f754..6d22a875aab 100644
--- a/app/models/ci/pipeline_artifact.rb
+++ b/app/models/ci/pipeline_artifact.rb
@@ -7,6 +7,7 @@ module Ci
include UpdateProjectStatistics
include Artifactable
include FileStoreMounter
+ include Lockable
include Presentable
FILE_SIZE_LIMIT = 10.megabytes.freeze
@@ -52,7 +53,7 @@ module Ci
find_by(file_type: file_type)
end
- def create_or_replace_for_pipeline!(pipeline:, file_type:, file:, size:)
+ def create_or_replace_for_pipeline!(pipeline:, file_type:, file:, size:, locked: :unknown)
transaction do
pipeline.pipeline_artifacts.find_by_file_type(file_type)&.destroy!
@@ -62,7 +63,8 @@ module Ci
size: size,
file: file,
file_format: REPORT_TYPES[file_type],
- expire_at: EXPIRATION_DATE.from_now
+ expire_at: EXPIRATION_DATE.from_now,
+ locked: locked
)
end
rescue ActiveRecord::ActiveRecordError => err
diff --git a/app/models/ci/pipeline_variable.rb b/app/models/ci/pipeline_variable.rb
index 3dca77af051..6e4418bc360 100644
--- a/app/models/ci/pipeline_variable.rb
+++ b/app/models/ci/pipeline_variable.rb
@@ -2,13 +2,16 @@
module Ci
class PipelineVariable < Ci::ApplicationRecord
+ include Ci::Partitionable
include Ci::HasVariable
belongs_to :pipeline
+ partitionable scope: :pipeline
+
alias_attribute :secret_value, :value
- validates :key, presence: true
+ validates :key, :pipeline, presence: true
def hook_attrs
{ key: key, value: value }
diff --git a/app/models/ci/processable.rb b/app/models/ci/processable.rb
index a2ff49077be..09dc9d4bce1 100644
--- a/app/models/ci/processable.rb
+++ b/app/models/ci/processable.rb
@@ -3,6 +3,7 @@
module Ci
class Processable < ::CommitStatus
include Gitlab::Utils::StrongMemoize
+ include FromUnion
extend ::Gitlab::Utils::Override
has_one :resource, class_name: 'Ci::Resource', foreign_key: 'build_id', inverse_of: :processable
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 6c3754d84d0..28d9edcc135 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -8,15 +8,12 @@ module Ci
include ChronicDurationAttribute
include FromUnion
include TokenAuthenticatable
- include IgnorableColumns
include FeatureGate
include Gitlab::Utils::StrongMemoize
include TaggableQueries
include Presentable
include EachBatch
- ignore_column :semver, remove_with: '15.4', remove_after: '2022-08-22'
-
add_authentication_token_field :token, encrypted: :optional, expires_at: :compute_token_expiration, expiration_enforced?: :token_expiration_enforced?
enum access_level: {
@@ -351,6 +348,12 @@ module Ci
end
end
+ def owner_project
+ return unless project_type?
+
+ runner_projects.order(:id).first.project
+ end
+
def belongs_to_one_project?
runner_projects.count == 1
end
@@ -359,14 +362,6 @@ module Ci
runner_projects.limit(2).count(:all) > 1
end
- def assigned_to_group?
- runner_namespaces.any?
- end
-
- def assigned_to_project?
- runner_projects.any?
- end
-
def match_build_if_online?(build)
active? && online? && matches_build?(build)
end
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index f03d1e96a4b..46a9e3f6494 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -2,22 +2,31 @@
module Ci
class Stage < Ci::ApplicationRecord
+ include Ci::Partitionable
include Importable
include Ci::HasStatus
include Gitlab::OptimisticLocking
include Presentable
+ partitionable scope: :pipeline
+
enum status: Ci::HasStatus::STATUSES_ENUM
belongs_to :project
belongs_to :pipeline
- has_many :statuses, class_name: 'CommitStatus', foreign_key: :stage_id
- has_many :latest_statuses, -> { ordered.latest }, class_name: 'CommitStatus', foreign_key: :stage_id
- has_many :retried_statuses, -> { ordered.retried }, class_name: 'CommitStatus', foreign_key: :stage_id
- has_many :processables, class_name: 'Ci::Processable', foreign_key: :stage_id
- has_many :builds, foreign_key: :stage_id
- has_many :bridges, foreign_key: :stage_id
+ has_many :statuses, class_name: 'CommitStatus', foreign_key: :stage_id, inverse_of: :ci_stage
+ has_many :latest_statuses, -> { ordered.latest },
+ class_name: 'CommitStatus',
+ foreign_key: :stage_id,
+ inverse_of: :ci_stage
+ has_many :retried_statuses, -> { ordered.retried },
+ class_name: 'CommitStatus',
+ foreign_key: :stage_id,
+ inverse_of: :ci_stage
+ has_many :processables, class_name: 'Ci::Processable', foreign_key: :stage_id, inverse_of: :ci_stage
+ has_many :builds, foreign_key: :stage_id, inverse_of: :ci_stage
+ has_many :bridges, foreign_key: :stage_id, inverse_of: :ci_stage
scope :ordered, -> { order(position: :asc) }
scope :in_pipelines, ->(pipelines) { where(pipeline: pipelines) }
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index c4db4754c52..1092b9c9564 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -6,6 +6,8 @@ module Ci
include Limitable
include IgnorableColumns
+ TRIGGER_TOKEN_PREFIX = 'glptt-'
+
ignore_column :ref, remove_with: '15.4', remove_after: '2022-08-22'
self.limit_name = 'pipeline_triggers'
@@ -22,7 +24,7 @@ module Ci
before_validation :set_default_values
def set_default_values
- self.token = SecureRandom.hex(15) if self.token.blank?
+ self.token = "#{TRIGGER_TOKEN_PREFIX}#{SecureRandom.hex(20)}" if self.token.blank?
end
def last_trigger_request
@@ -34,7 +36,7 @@ module Ci
end
def short_token
- token[0...4] if token.present?
+ token.delete_prefix(TRIGGER_TOKEN_PREFIX)[0...4] if token.present?
end
def can_access_project?
diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb
index 3a8c314efe4..27550616002 100644
--- a/app/models/clusters/applications/ingress.rb
+++ b/app/models/clusters/applications/ingress.rb
@@ -16,14 +16,10 @@ module Clusters
include ::Clusters::Concerns::ApplicationData
include AfterCommitQueue
include UsageStatistics
- include IgnorableColumns
default_value_for :ingress_type, :nginx
default_value_for :version, VERSION
- ignore_column :modsecurity_enabled, remove_with: '14.2', remove_after: '2021-07-22'
- ignore_column :modsecurity_mode, remove_with: '14.2', remove_after: '2021-07-22'
-
enum ingress_type: {
nginx: 1
}
diff --git a/app/models/commit.rb b/app/models/commit.rb
index bd60f02b532..54de45ebba7 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -133,6 +133,22 @@ class Commit
def parent_class
::Project
end
+
+ def build_from_sidekiq_hash(project, hash)
+ hash = hash.dup
+ date_suffix = '_date'
+
+ # When processing Sidekiq payloads various timestamps are stored as Strings.
+ # Commit in turn expects Time-like instances upon input, so we have to
+ # manually parse these values.
+ hash.each do |key, value|
+ if key.to_s.end_with?(date_suffix) && value.is_a?(String)
+ hash[key] = Time.zone.parse(value)
+ end
+ end
+
+ from_hash(hash, project)
+ end
end
attr_accessor :raw
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index afe4927ee73..05a258e6e26 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
class CommitStatus < Ci::ApplicationRecord
+ include Ci::Partitionable
include Ci::HasStatus
include Importable
include AfterCommitQueue
@@ -11,13 +12,14 @@ class CommitStatus < Ci::ApplicationRecord
include IgnorableColumns
self.table_name = 'ci_builds'
-
- ignore_column :token, remove_with: '15.4', remove_after: '2022-08-22'
+ partitionable scope: :pipeline
+ ignore_column :trace, remove_with: '15.6', remove_after: '2022-10-22'
belongs_to :user
belongs_to :project
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id
belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline'
+ belongs_to :ci_stage, class_name: 'Ci::Stage', foreign_key: :stage_id
has_many :needs, class_name: 'Ci::BuildNeed', foreign_key: :build_id, inverse_of: :build
@@ -318,6 +320,10 @@ class CommitStatus < Ci::ApplicationRecord
Gitlab::EtagCaching::Store.new.touch(job_path)
end
+ def stage_name
+ ci_stage&.name
+ end
+
private
def unrecoverable_failure?
diff --git a/app/models/concerns/approvable.rb b/app/models/concerns/approvable.rb
new file mode 100644
index 00000000000..1566c53217d
--- /dev/null
+++ b/app/models/concerns/approvable.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+module Approvable
+ extend ActiveSupport::Concern
+ include FromUnion
+
+ included do
+ has_many :approvals, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
+ has_many :approved_by_users, through: :approvals, source: :user
+
+ scope :without_approvals, -> { left_outer_joins(:approvals).where(approvals: { id: nil }) }
+ scope :with_approvals, -> { joins(:approvals) }
+ scope :approved_by_users_with_ids, -> (*user_ids) do
+ with_approvals
+ .merge(Approval.with_user)
+ .where(users: { id: user_ids })
+ .group(:id)
+ .having("COUNT(users.id) = ?", user_ids.size)
+ end
+ scope :approved_by_users_with_usernames, -> (*usernames) do
+ with_approvals
+ .merge(Approval.with_user)
+ .where(users: { username: usernames })
+ .group(:id)
+ .having("COUNT(users.id) = ?", usernames.size)
+ end
+
+ scope :not_approved_by_users_with_usernames, -> (usernames) do
+ users = User.where(username: usernames).select(:id)
+ app_table = Approval.arel_table
+
+ where(
+ Approval.where(approvals: { user_id: users })
+ .where(app_table[:merge_request_id].eq(arel_table[:id]))
+ .select('true')
+ .arel.exists.not
+ )
+ end
+ end
+
+ class_methods do
+ def select_from_union(relations)
+ where(id: from_union(relations))
+ end
+ end
+
+ def approved_by?(user)
+ return false unless user
+
+ approvals.where(user: user).any?
+ end
+
+ def can_be_approved_by?(user)
+ user && !approved_by?(user) && user.can?(:approve_merge_request, self)
+ end
+
+ def can_be_unapproved_by?(user)
+ user && approved_by?(user) && user.can?(:approve_merge_request, self)
+ end
+end
+
+Approvable.prepend_mod
diff --git a/app/models/concerns/approvable_base.rb b/app/models/concerns/approvable_base.rb
deleted file mode 100644
index 8240f9bd6ea..00000000000
--- a/app/models/concerns/approvable_base.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-# frozen_string_literal: true
-
-module ApprovableBase
- extend ActiveSupport::Concern
- include FromUnion
-
- included do
- has_many :approvals, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
- has_many :approved_by_users, through: :approvals, source: :user
-
- scope :without_approvals, -> { left_outer_joins(:approvals).where(approvals: { id: nil }) }
- scope :with_approvals, -> { joins(:approvals) }
- scope :approved_by_users_with_ids, -> (*user_ids) do
- with_approvals
- .merge(Approval.with_user)
- .where(users: { id: user_ids })
- .group(:id)
- .having("COUNT(users.id) = ?", user_ids.size)
- end
- scope :approved_by_users_with_usernames, -> (*usernames) do
- with_approvals
- .merge(Approval.with_user)
- .where(users: { username: usernames })
- .group(:id)
- .having("COUNT(users.id) = ?", usernames.size)
- end
-
- scope :not_approved_by_users_with_usernames, -> (usernames) do
- users = User.where(username: usernames).select(:id)
- self_table = self.arel_table
- app_table = Approval.arel_table
-
- where(
- Approval.where(approvals: { user_id: users })
- .where(app_table[:merge_request_id].eq(self_table[:id]))
- .select('true')
- .arel.exists.not
- )
- end
- end
-
- class_methods do
- def select_from_union(relations)
- where(id: from_union(relations))
- end
- end
-
- def approved_by?(user)
- return false unless user
-
- approved_by_users.include?(user)
- end
-
- def can_be_approved_by?(user)
- user && !approved_by?(user) && user.can?(:approve_merge_request, self)
- end
-
- def can_be_unapproved_by?(user)
- user && approved_by?(user) && user.can?(:approve_merge_request, self)
- end
-end
diff --git a/app/models/concerns/ci/artifactable.rb b/app/models/concerns/ci/artifactable.rb
index ee8e98ec1bf..3fdbd6a8789 100644
--- a/app/models/concerns/ci/artifactable.rb
+++ b/app/models/concerns/ci/artifactable.rb
@@ -10,8 +10,17 @@ module Ci
STORE_COLUMN = :file_store
NotSupportedAdapterError = Class.new(StandardError)
FILE_FORMAT_ADAPTERS = {
+ # While zip is a streamable file format, performing streaming
+ # reads requires that each entry in the zip has certain headers
+ # present at the front of the entry. These headers are OPTIONAL
+ # according to the file format specification. GitLab Runner uses
+ # Go's `archive/zip` to create zip archives, which does not include
+ # these headers. Go maintainers have expressed that they don't intend
+ # to support them: https://github.com/golang/go/issues/23301#issuecomment-363240781
+ #
+ # If you need GitLab to be able to read Artifactables, store them in
+ # raw or gzip format instead of zip.
gzip: Gitlab::Ci::Build::Artifacts::Adapters::GzipStream,
- zip: Gitlab::Ci::Build::Artifacts::Adapters::ZipStream,
raw: Gitlab::Ci::Build::Artifacts::Adapters::RawStream
}.freeze
diff --git a/app/models/concerns/ci/has_deployment_name.rb b/app/models/concerns/ci/has_deployment_name.rb
deleted file mode 100644
index 887653e846e..00000000000
--- a/app/models/concerns/ci/has_deployment_name.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-module Ci
- module HasDeploymentName
- extend ActiveSupport::Concern
-
- def count_user_deployment?
- deployment_name?
- end
-
- def deployment_name?
- self.class::DEPLOYMENT_NAMES.any? { |n| name.downcase.include?(n) }
- end
- end
-end
diff --git a/app/models/concerns/ci/lockable.rb b/app/models/concerns/ci/lockable.rb
new file mode 100644
index 00000000000..31ba93775e2
--- /dev/null
+++ b/app/models/concerns/ci/lockable.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Ci
+ module Lockable
+ extend ActiveSupport::Concern
+
+ included do
+ # `locked` will be populated from the source of truth on Ci::Pipeline
+ # in order to clean up expired job artifacts in a performant way.
+ # The values should be the same as `Ci::Pipeline.lockeds` with the
+ # additional value of `unknown` to indicate rows that have not
+ # yet been populated from the parent Ci::Pipeline
+ enum locked: {
+ unlocked: 0,
+ artifacts_locked: 1,
+ unknown: 2
+ }, _prefix: :artifact
+ end
+ end
+end
diff --git a/app/models/concerns/ci/metadatable.rb b/app/models/concerns/ci/metadatable.rb
index 8c3a05c23f0..71b26b70bbf 100644
--- a/app/models/concerns/ci/metadatable.rb
+++ b/app/models/concerns/ci/metadatable.rb
@@ -34,7 +34,7 @@ module Ci
end
def ensure_metadata
- metadata || build_metadata(project: project)
+ metadata || build_metadata(project: project, partition_id: partition_id)
end
def degenerated?
diff --git a/app/models/concerns/ci/partitionable.rb b/app/models/concerns/ci/partitionable.rb
new file mode 100644
index 00000000000..710ee1ba64f
--- /dev/null
+++ b/app/models/concerns/ci/partitionable.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+module Ci
+ ##
+ # This module implements a way to set the `partion_id` value on a dependent
+ # resource from a parent record.
+ # Usage:
+ #
+ # class PipelineVariable < Ci::ApplicationRecord
+ # include Ci::Partitionable
+ #
+ # belongs_to :pipeline
+ # partitionable scope: :pipeline
+ # # Or
+ # partitionable scope: ->(record) { record.partition_value }
+ #
+ #
+ module Partitionable
+ extend ActiveSupport::Concern
+ include ::Gitlab::Utils::StrongMemoize
+
+ included do
+ before_validation :set_partition_id, on: :create
+ validates :partition_id, presence: true
+
+ def set_partition_id
+ return if partition_id_changed? && partition_id.present?
+ return unless partition_scope_value
+
+ self.partition_id = partition_scope_value
+ end
+ end
+
+ class_methods do
+ private
+
+ def partitionable(scope:)
+ define_method(:partition_scope_value) do
+ strong_memoize(:partition_scope_value) do
+ record = scope.to_proc.call(self)
+ record.respond_to?(:partition_id) ? record.partition_id : record
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/models/concerns/ci/track_environment_usage.rb b/app/models/concerns/ci/track_environment_usage.rb
new file mode 100644
index 00000000000..45d9cdeeb59
--- /dev/null
+++ b/app/models/concerns/ci/track_environment_usage.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Ci
+ module TrackEnvironmentUsage
+ extend ActiveSupport::Concern
+
+ def track_deployment_usage
+ return unless user_id.present? && count_user_deployment?
+
+ Gitlab::Utils::UsageData.track_usage_event('ci_users_executing_deployment_job', user_id)
+ end
+
+ def track_verify_environment_usage
+ return unless user_id.present? && verifies_environment?
+
+ Gitlab::Utils::UsageData.track_usage_event('ci_users_executing_verify_environment_job', user_id)
+ end
+
+ def verifies_environment?
+ has_environment? && environment_action == 'verify'
+ end
+
+ def count_user_deployment?
+ deployment_name?
+ end
+
+ def deployment_name?
+ self.class::DEPLOYMENT_NAMES.any? { |n| name.downcase.include?(n) }
+ end
+ end
+end
diff --git a/app/models/concerns/counter_attribute.rb b/app/models/concerns/counter_attribute.rb
index 65cf3246d11..64d178b7507 100644
--- a/app/models/concerns/counter_attribute.rb
+++ b/app/models/concerns/counter_attribute.rb
@@ -65,6 +65,10 @@ module CounterAttribute
def counter_attribute_after_flush(&callback)
after_flush_callbacks << callback
end
+
+ def counter_attribute_enabled?(attribute)
+ counter_attributes.include?(attribute)
+ end
end
# This method must only be called by FlushCounterIncrementsWorker
@@ -103,16 +107,14 @@ module CounterAttribute
end
def delayed_increment_counter(attribute, increment)
+ raise ArgumentError, "#{attribute} is not a counter attribute" unless counter_attribute_enabled?(attribute)
+
return if increment == 0
run_after_commit_or_now do
- if counter_attribute_enabled?(attribute)
- increment_counter(attribute, increment)
+ increment_counter(attribute, increment)
- FlushCounterIncrementsWorker.perform_in(WORKER_DELAY, self.class.name, self.id, attribute)
- else
- legacy_increment!(attribute, increment)
- end
+ FlushCounterIncrementsWorker.perform_in(WORKER_DELAY, self.class.name, self.id, attribute)
end
true
@@ -157,7 +159,7 @@ module CounterAttribute
end
def counter_attribute_enabled?(attribute)
- self.class.counter_attributes.include?(attribute)
+ self.class.counter_attribute_enabled?(attribute)
end
private
@@ -168,10 +170,6 @@ module CounterAttribute
end
end
- def legacy_increment!(attribute, increment)
- increment!(attribute, increment)
- end
-
def unsafe_update_counters(id, increments)
self.class.update_counters(id, increments)
end
diff --git a/app/models/concerns/enums/ci/commit_status.rb b/app/models/concerns/enums/ci/commit_status.rb
index ecb120d8013..9de2da5aac3 100644
--- a/app/models/concerns/enums/ci/commit_status.rb
+++ b/app/models/concerns/enums/ci/commit_status.rb
@@ -19,7 +19,7 @@ module Enums
unmet_prerequisites: 10,
scheduler_failure: 11,
data_integrity_failure: 12,
- forward_deployment_failure: 13,
+ forward_deployment_failure: 13, # Deprecated in favor of failed_outdated_deployment_job.
user_blocked: 14,
project_deleted: 15,
ci_quota_exceeded: 16,
@@ -29,6 +29,7 @@ module Enums
builds_disabled: 20,
environment_creation_failure: 21,
deployment_rejected: 22,
+ failed_outdated_deployment_job: 23,
protected_environment_failure: 1_000,
insufficient_bridge_permissions: 1_001,
downstream_bridge_project_not_found: 1_002,
@@ -39,7 +40,8 @@ module Enums
downstream_pipeline_creation_failed: 1_007,
secrets_provider_not_found: 1_008,
reached_max_descendant_pipelines_depth: 1_009,
- ip_restriction_failure: 1_010
+ ip_restriction_failure: 1_010,
+ reached_max_pipeline_hierarchy_size: 1_011
}
end
end
diff --git a/app/models/concerns/enums/internal_id.rb b/app/models/concerns/enums/internal_id.rb
index 71c86bab136..a8227363a22 100644
--- a/app/models/concerns/enums/internal_id.rb
+++ b/app/models/concerns/enums/internal_id.rb
@@ -16,7 +16,8 @@ module Enums
alert_management_alerts: 8,
sprints: 9, # iterations
design_management_designs: 10,
- incident_management_oncall_schedules: 11
+ incident_management_oncall_schedules: 11,
+ ml_experiments: 12
}
end
end
diff --git a/app/models/concerns/from_set_operator.rb b/app/models/concerns/from_set_operator.rb
index ce3a83e9fa1..56b788eb1ab 100644
--- a/app/models/concerns/from_set_operator.rb
+++ b/app/models/concerns/from_set_operator.rb
@@ -10,7 +10,9 @@ module FromSetOperator
raise "Trying to redefine method '#{method(method_name)}'" if methods.include?(method_name)
- define_method(method_name) do |members, remove_duplicates: true, remove_order: true, alias_as: table_name|
+ define_method(method_name) do |*members, remove_duplicates: true, remove_order: true, alias_as: table_name|
+ members = flatten_ar_array(members)
+
operator_sql =
if members.any?
operator.new(members, remove_duplicates: remove_duplicates, remove_order: remove_order).to_sql
@@ -20,5 +22,26 @@ module FromSetOperator
from(Arel.sql("(#{operator_sql}) #{alias_as}"))
end
+
+ # Array#flatten with ActiveRecord::Relation items will load the ActiveRecord::Relation.
+ # Therefore we need to roll our own flatten method.
+ unless method_defined?(:flatten_ar_array) # rubocop:disable Style/GuardClause
+ define_method :flatten_ar_array do |ary|
+ arrays = ary.dup
+ result = []
+
+ until arrays.empty?
+ item = arrays.shift
+ if item.is_a?(Array)
+ arrays.concat(item.dup)
+ else
+ result.push(item)
+ end
+ end
+
+ result
+ end
+ private :flatten_ar_array
+ end
end
end
diff --git a/app/models/concerns/integrations/slack_mattermost_notifier.rb b/app/models/concerns/integrations/slack_mattermost_notifier.rb
index 142e62bb501..1ecddc015ab 100644
--- a/app/models/concerns/integrations/slack_mattermost_notifier.rb
+++ b/app/models/concerns/integrations/slack_mattermost_notifier.rb
@@ -21,13 +21,13 @@ module Integrations
)
responses.each do |response|
- unless response.success?
- log_error('SlackMattermostNotifier HTTP error response',
- request_host: response.request.uri.host,
- response_code: response.code,
- response_body: response.body
- )
- end
+ next if response.success?
+
+ log_error('SlackMattermostNotifier HTTP error response',
+ request_host: response.request.uri.host,
+ response_code: response.code,
+ response_body: response.body
+ )
end
end
diff --git a/app/models/concerns/merge_request_reviewer_state.rb b/app/models/concerns/merge_request_reviewer_state.rb
index 18ec1c253e1..412b1da55da 100644
--- a/app/models/concerns/merge_request_reviewer_state.rb
+++ b/app/models/concerns/merge_request_reviewer_state.rb
@@ -6,20 +6,11 @@ module MergeRequestReviewerState
included do
enum state: {
unreviewed: 0,
- reviewed: 1,
- attention_requested: 2
+ reviewed: 1
}
validates :state,
presence: true,
inclusion: { in: self.states.keys }
-
- belongs_to :updated_state_by, class_name: 'User', foreign_key: :updated_state_by_user_id
-
- def attention_requested_by
- return unless attention_requested?
-
- updated_state_by
- end
end
end
diff --git a/app/models/concerns/pg_full_text_searchable.rb b/app/models/concerns/pg_full_text_searchable.rb
index 813827478da..335fcec2611 100644
--- a/app/models/concerns/pg_full_text_searchable.rb
+++ b/app/models/concerns/pg_full_text_searchable.rb
@@ -108,6 +108,7 @@ module PgFullTextSearchable
# This fixes an inconsistency with how to_tsvector and websearch_to_tsquery process URLs
# See https://gitlab.com/gitlab-org/gitlab/-/issues/354784#note_905431920
search_term = remove_url_scheme(search_term)
+ search_term = ActiveSupport::Inflector.transliterate(search_term)
joins(:search_data).where(
Arel::Nodes::InfixOperation.new(
diff --git a/app/models/concerns/project_features_compatibility.rb b/app/models/concerns/project_features_compatibility.rb
index 7613691bc2e..2976b6f02a7 100644
--- a/app/models/concerns/project_features_compatibility.rb
+++ b/app/models/concerns/project_features_compatibility.rb
@@ -86,6 +86,10 @@ module ProjectFeaturesCompatibility
write_feature_attribute_string(:operations_access_level, value)
end
+ def monitor_access_level=(value)
+ write_feature_attribute_string(:monitor_access_level, value)
+ end
+
def security_and_compliance_access_level=(value)
write_feature_attribute_string(:security_and_compliance_access_level, value)
end
diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb
index 65fb62a814f..eccb004b503 100644
--- a/app/models/concerns/sortable.rb
+++ b/app/models/concerns/sortable.rb
@@ -43,6 +43,33 @@ module Sortable
}
end
+ def build_keyset_order_on_joined_column(scope:, attribute_name:, column:, direction:, nullable:)
+ reversed_direction = direction == :asc ? :desc : :asc
+
+ # rubocop: disable GitlabSecurity/PublicSend
+ order = ::Gitlab::Pagination::Keyset::Order.build(
+ [
+ ::Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: attribute_name,
+ column_expression: column,
+ order_expression: column.send(direction).send(nullable),
+ reversed_order_expression: column.send(reversed_direction).send(nullable),
+ order_direction: direction,
+ distinct: false,
+ add_to_projections: true,
+ nullable: nullable
+ ),
+ ::Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'id',
+ order_expression: arel_table['id'].desc
+ )
+ ]
+ )
+ # rubocop: enable GitlabSecurity/PublicSend
+
+ order.apply_cursor_conditions(scope).reorder(order)
+ end
+
private
def highest_label_priority(target_type_column: nil, target_type: nil, target_column:, project_column:, excluded_labels: [])
diff --git a/app/models/container_repository.rb b/app/models/container_repository.rb
index e10452c1081..14520b2da26 100644
--- a/app/models/container_repository.rb
+++ b/app/models/container_repository.rb
@@ -263,10 +263,10 @@ class ContainerRepository < ApplicationRecord
.with_migration_import_started_at_nil_or_before(before_timestamp)
union = ::Gitlab::SQL::Union.new([
- stale_pre_importing,
- stale_pre_import_done,
- stale_importing
- ])
+ stale_pre_importing,
+ stale_pre_import_done,
+ stale_importing
+ ])
from("(#{union.to_sql}) #{ContainerRepository.table_name}")
end
@@ -598,6 +598,7 @@ class ContainerRepository < ApplicationRecord
tags_response_body.map do |raw_tag|
tag = ContainerRegistry::Tag.new(self, raw_tag['name'])
tag.force_created_at_from_iso8601(raw_tag['created_at'])
+ tag.updated_at = raw_tag['updated_at']
tag
end
end
diff --git a/app/models/customer_relations/contact.rb b/app/models/customer_relations/contact.rb
index f6455da890b..16c741d340f 100644
--- a/app/models/customer_relations/contact.rb
+++ b/app/models/customer_relations/contact.rb
@@ -79,22 +79,23 @@ class CustomerRelations::Contact < ApplicationRecord
end
def self.sort_by_name
- order(Gitlab::Pagination::Keyset::Order.build([
- Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: 'last_name',
- order_expression: arel_table[:last_name].asc,
- distinct: false
- ),
- Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: 'first_name',
- order_expression: arel_table[:first_name].asc,
- distinct: false
- ),
- Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: 'id',
- order_expression: arel_table[:id].asc
- )
- ]))
+ order(Gitlab::Pagination::Keyset::Order.build(
+ [
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'last_name',
+ order_expression: arel_table[:last_name].asc,
+ distinct: false
+ ),
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'first_name',
+ order_expression: arel_table[:first_name].asc,
+ distinct: false
+ ),
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'id',
+ order_expression: arel_table[:id].asc
+ )
+ ]))
end
def self.find_ids_by_emails(group, emails)
@@ -117,22 +118,14 @@ class CustomerRelations::Contact < ApplicationRecord
JOIN #{table_name} AS new_contacts ON new_contacts.group_id = :old_group_id AND LOWER(new_contacts.email) = LOWER(existing_contacts.email)
WHERE existing_contacts.group_id = :new_group_id AND contact_id = existing_contacts.id
SQL
- connection.execute(sanitize_sql([
- update_query,
- old_group_id: group.root_ancestor.id,
- new_group_id: group.id
- ]))
+ connection.execute(sanitize_sql([update_query, old_group_id: group.root_ancestor.id, new_group_id: group.id]))
dupes_query = <<~SQL
DELETE FROM #{table_name} AS existing_contacts
USING #{table_name} AS new_contacts
WHERE existing_contacts.group_id = :new_group_id AND new_contacts.group_id = :old_group_id AND LOWER(new_contacts.email) = LOWER(existing_contacts.email)
SQL
- connection.execute(sanitize_sql([
- dupes_query,
- old_group_id: group.root_ancestor.id,
- new_group_id: group.id
- ]))
+ connection.execute(sanitize_sql([dupes_query, old_group_id: group.root_ancestor.id, new_group_id: group.id]))
where(group: group).update_all(group_id: group.root_ancestor.id)
end
diff --git a/app/models/customer_relations/organization.rb b/app/models/customer_relations/organization.rb
index 705e84250c9..5eda9b4bf15 100644
--- a/app/models/customer_relations/organization.rb
+++ b/app/models/customer_relations/organization.rb
@@ -23,6 +23,9 @@ class CustomerRelations::Organization < ApplicationRecord
validates :description, length: { maximum: 1024 }
validate :validate_root_group
+ scope :order_scope_asc, ->(field) { order(arel_table[field].asc.nulls_last) }
+ scope :order_scope_desc, ->(field) { order(arel_table[field].desc.nulls_last) }
+
# Searches for organizations with a matching name or description.
#
# This method uses ILIKE on PostgreSQL
@@ -38,6 +41,14 @@ class CustomerRelations::Organization < ApplicationRecord
where(state: state)
end
+ def self.sort_by_field(field, direction)
+ if direction == :asc
+ order_scope_asc(field)
+ else
+ order_scope_desc(field)
+ end
+ end
+
def self.sort_by_name
order(name: :asc)
end
@@ -55,28 +66,30 @@ class CustomerRelations::Organization < ApplicationRecord
JOIN #{table_name} AS new_organizations ON new_organizations.group_id = :old_group_id AND LOWER(new_organizations.name) = LOWER(existing_organizations.name)
WHERE existing_organizations.group_id = :new_group_id AND organization_id = existing_organizations.id
SQL
- connection.execute(sanitize_sql([
- update_query,
- old_group_id: group.root_ancestor.id,
- new_group_id: group.id
- ]))
+ connection.execute(sanitize_sql([update_query, old_group_id: group.root_ancestor.id, new_group_id: group.id]))
dupes_query = <<~SQL
DELETE FROM #{table_name} AS existing_organizations
USING #{table_name} AS new_organizations
WHERE existing_organizations.group_id = :new_group_id AND new_organizations.group_id = :old_group_id AND LOWER(new_organizations.name) = LOWER(existing_organizations.name)
SQL
- connection.execute(sanitize_sql([
- dupes_query,
- old_group_id: group.root_ancestor.id,
- new_group_id: group.id
- ]))
+ connection.execute(sanitize_sql([dupes_query, old_group_id: group.root_ancestor.id, new_group_id: group.id]))
where(group: group).update_all(group_id: group.root_ancestor.id)
end
+ def self.counts_by_state
+ default_state_counts.merge(group(:state).count)
+ end
+
private
+ def self.default_state_counts
+ states.keys.each_with_object({}) do |key, memo|
+ memo[key] = 0
+ end
+ end
+
def validate_root_group
return if group&.root?
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index a3213a59bed..dafcbc593be 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -18,7 +18,7 @@ class Deployment < ApplicationRecord
belongs_to :environment, optional: false
belongs_to :cluster, class_name: 'Clusters::Cluster', optional: true
belongs_to :user
- belongs_to :deployable, polymorphic: true, optional: true # rubocop:disable Cop/PolymorphicAssociations
+ belongs_to :deployable, polymorphic: true, optional: true, inverse_of: :deployment # rubocop:disable Cop/PolymorphicAssociations
has_many :deployment_merge_requests
has_many :merge_requests,
@@ -36,6 +36,7 @@ class Deployment < ApplicationRecord
delegate :name, to: :environment, prefix: true
delegate :kubernetes_namespace, to: :deployment_cluster, allow_nil: true
+ scope :for_iid, -> (project, iid) { where(project: project, iid: iid) }
scope :for_environment, -> (environment) { where(environment_id: environment) }
scope :for_environment_name, -> (project, name) do
where('deployments.environment_id = (?)',
@@ -58,9 +59,11 @@ class Deployment < ApplicationRecord
scope :finished_before, ->(date) { where('finished_at < ?', date) }
scope :ordered, -> { order(finished_at: :desc) }
+ scope :ordered_as_upcoming, -> { order(id: :desc) }
VISIBLE_STATUSES = %i[running success failed canceled blocked].freeze
FINISHED_STATUSES = %i[success failed canceled].freeze
+ UPCOMING_STATUSES = %i[created blocked running].freeze
state_machine :status, initial: :created do
event :run do
@@ -220,6 +223,10 @@ class Deployment < ApplicationRecord
Ci::Build.where(id: deployable_ids)
end
+ def build
+ deployable if deployable.is_a?(::Ci::Build)
+ end
+
class << self
##
# FastDestroyAll concerns
@@ -310,6 +317,16 @@ class Deployment < ApplicationRecord
project.repository.ancestor?(ancestor_sha, sha)
end
+ def older_than_last_successful_deployment?
+ last_deployment_id = environment.last_deployment&.id
+
+ return false unless last_deployment_id.present?
+
+ return false if self.id == last_deployment_id
+
+ self.id < last_deployment_id
+ end
+
def update_merge_request_metrics!
return unless environment.production? && success?
@@ -436,6 +453,12 @@ class Deployment < ApplicationRecord
deployable.environment_tier_from_options
end
+ # default tag limit is 100, 0 means no limit
+ def tags(limit: 100)
+ project.repository.tag_names_contains(sha, limit: limit)
+ end
+ strong_memoize_attr :tags
+
private
def update_status!(status)
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 1950431446b..4b98cd02e3b 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -13,6 +13,7 @@ class Environment < ApplicationRecord
self.reactive_cache_work_type = :external_dependency
belongs_to :project, optional: false
+ belongs_to :merge_request, optional: true
use_fast_destroy :all_deployments
nullify_if_blank :external_url
@@ -30,6 +31,16 @@ class Environment < ApplicationRecord
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'
+ Deployment::FINISHED_STATUSES.each do |status|
+ has_one :"last_#{status}_deployment", -> { 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 },
+ 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
@@ -58,6 +69,7 @@ class Environment < ApplicationRecord
allow_nil: true
validate :safe_external_url
+ validate :merge_request_not_changed
delegate :manual_actions, :other_manual_actions, to: :last_deployment, allow_nil: true
delegate :auto_rollback_enabled?, to: :project
@@ -84,11 +96,12 @@ 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(arel_table[:name].matches("#{sanitize_sql_like query}%")).limit(limit)
+ where('LOWER(environments.name) LIKE LOWER(?) || \'%\'', sanitize_sql_like(query)).limit(limit)
end
scope :for_project, -> (project) { where(project_id: project) }
scope :for_tier, -> (tier) { where(tier: tier).where.not(tier: nil) }
+ scope :for_type, -> (type) { where(environment_type: type) }
scope :unfoldered, -> { where(environment_type: nil) }
scope :with_rank, -> do
select('environments.*, rank() OVER (PARTITION BY project_id ORDER BY id DESC)')
@@ -431,9 +444,13 @@ class Environment < ApplicationRecord
return unless value
parser = ::Gitlab::Ci::Build::DurationParser.new(value)
- return if parser.seconds_from_now.nil?
+
+ return if parser.seconds_from_now.nil? && auto_stop_at.nil?
self.auto_stop_at = parser.seconds_from_now
+ rescue ChronicDuration::DurationParseError => ex
+ Gitlab::ErrorTracking.track_exception(ex, project_id: self.project_id, environment_id: self.id)
+ raise ex
end
def rollout_status
@@ -509,6 +526,12 @@ class Environment < ApplicationRecord
self.tier ||= guess_tier
end
+ def merge_request_not_changed
+ if merge_request_id_changed? && persisted?
+ errors.add(:merge_request, 'merge_request cannot be changed')
+ end
+ end
+
# Guessing the tier of the environment if it's not explicitly specified by users.
# See https://en.wikipedia.org/wiki/Deployment_environment for industry standard deployment environments
def guess_tier
diff --git a/app/models/environment_status.rb b/app/models/environment_status.rb
index 43b2c7899a1..d06d0a99948 100644
--- a/app/models/environment_status.rb
+++ b/app/models/environment_status.rb
@@ -100,7 +100,7 @@ class EnvironmentStatus
def self.build_environments_status(mr, user, pipeline)
return [] unless pipeline
- pipeline.environments_in_self_and_descendants.includes(:project).available.map do |environment|
+ pipeline.environments_in_self_and_project_descendants.includes(:project).available.map do |environment|
next unless Ability.allowed?(user, :read_environment, environment)
EnvironmentStatus.new(pipeline.project, environment, mr, pipeline.sha)
diff --git a/app/models/error_tracking/project_error_tracking_setting.rb b/app/models/error_tracking/project_error_tracking_setting.rb
index 4953f24755c..12d73ef0d72 100644
--- a/app/models/error_tracking/project_error_tracking_setting.rb
+++ b/app/models/error_tracking/project_error_tracking_setting.rb
@@ -23,6 +23,7 @@ module ErrorTracking
self.reactive_cache_key = ->(setting) { [setting.class.model_name.singular, setting.project_id] }
self.reactive_cache_work_type = :external_dependency
+ self.reactive_cache_hard_limit = ErrorTracking::SentryClient::RESPONSE_SIZE_LIMIT
self.table_name = 'project_error_tracking_settings'
@@ -103,9 +104,18 @@ module ErrorTracking
api_host
end
+ def sentry_response_limit_enabled?
+ Feature.enabled?(:error_tracking_sentry_limit, project)
+ end
+
+ def reactive_cache_limit_enabled?
+ sentry_response_limit_enabled?
+ end
+
def sentry_client
strong_memoize(:sentry_client) do
- ::ErrorTracking::SentryClient.new(api_url, token)
+ ::ErrorTracking::SentryClient
+ .new(api_url, token, validate_size_guarded_by_feature_flag: sentry_response_limit_enabled?)
end
end
@@ -127,14 +137,14 @@ module ErrorTracking
def issue_details(opts = {})
with_reactive_cache('issue_details', opts.stringify_keys) do |result|
- ensure_issue_belongs_to_project!(result[:issue].project_id)
+ ensure_issue_belongs_to_project!(result[:issue].project_id) if result[:issue]
result
end
end
def issue_latest_event(opts = {})
with_reactive_cache('issue_latest_event', opts.stringify_keys) do |result|
- ensure_issue_belongs_to_project!(result[:latest_event].project_id)
+ ensure_issue_belongs_to_project!(result[:latest_event].project_id) if result[:latest_event]
result
end
end
diff --git a/app/models/group.rb b/app/models/group.rb
index 55455d85531..1445e71b0bc 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -153,7 +153,7 @@ class Group < Namespace
after_create :post_create_hook
after_destroy :post_destroy_hook
- after_save :update_two_factor_requirement
+ after_commit :update_two_factor_requirement
after_update :path_changed_hook, if: :saved_change_to_path?
after_create -> { create_or_load_association(:group_feature) }
@@ -186,6 +186,27 @@ class Group < Namespace
where(project_creation_level: permitted_levels)
end
+ scope :shared_into_ancestors, -> (group) do
+ joins(:shared_group_links)
+ .where(group_group_links: { shared_group_id: group.self_and_ancestors })
+ end
+
+ # WARNING: This method should never be used on its own
+ # please do make sure the number of rows you are filtering is small
+ # enough for this query
+ #
+ # It's a replacement for `public_or_visible_to_user` that correctly
+ # supports subgroup permissions
+ scope :accessible_to_user, -> (user) do
+ if user
+ Preloaders::GroupPolicyPreloader.new(self, user).execute
+
+ select { |group| user.can?(:read_group, group) }
+ else
+ public_to_user
+ end
+ end
+
class << self
def sort_by_attribute(method)
if method == 'storage_size_desc'
@@ -614,11 +635,11 @@ class Group < Namespace
# 4. They belong to an ancestor group
def direct_and_indirect_users
User.from_union([
- User
- .where(id: direct_and_indirect_members.select(:user_id))
- .reorder(nil),
- project_users_with_descendants
- ])
+ User
+ .where(id: direct_and_indirect_members.select(:user_id))
+ .reorder(nil),
+ project_users_with_descendants
+ ])
end
# Returns all users (also inactive) that are members of the group because:
@@ -628,11 +649,11 @@ class Group < Namespace
# 4. They belong to an ancestor group
def direct_and_indirect_users_with_inactive
User.from_union([
- User
- .where(id: direct_and_indirect_members_with_inactive.select(:user_id))
- .reorder(nil),
- project_users_with_descendants
- ])
+ User
+ .where(id: direct_and_indirect_members_with_inactive.select(:user_id))
+ .reorder(nil),
+ project_users_with_descendants
+ ])
end
def users_count
@@ -672,14 +693,6 @@ class Group < Namespace
}
end
- def ci_variables_for(ref, project, environment: nil)
- cache_key = "ci_variables_for:group:#{self&.id}:project:#{project&.id}:ref:#{ref}:environment:#{environment}"
-
- ::Gitlab::SafeRequestStore.fetch(cache_key) do
- uncached_ci_variables_for(ref, project, environment: environment)
- end
- end
-
def member(user)
if group_members.loaded?
group_members.find { |gm| gm.user_id == user.id }
@@ -890,6 +903,18 @@ class Group < Namespace
end
end
+ def packages_policy_subject
+ if Feature.enabled?(:read_package_policy_rule, self)
+ ::Packages::Policies::Group.new(self)
+ else
+ self
+ end
+ end
+
+ def update_two_factor_requirement_for_members
+ direct_and_indirect_members.find_each(&:update_two_factor_requirement)
+ end
+
private
def feature_flag_enabled_for_self_or_ancestor?(feature_flag)
@@ -912,7 +937,7 @@ class Group < Namespace
def update_two_factor_requirement
return unless saved_change_to_require_two_factor_authentication? || saved_change_to_two_factor_grace_period?
- direct_and_indirect_members.find_each(&:update_two_factor_requirement)
+ Groups::UpdateTwoFactorRequirementForMembersWorker.perform_async(self.id)
end
def path_changed_hook
@@ -1031,26 +1056,6 @@ class Group < Namespace
def enable_shared_runners!
update!(shared_runners_enabled: true)
end
-
- def uncached_ci_variables_for(ref, project, environment: nil)
- list_of_ids = if root_ancestor.use_traversal_ids?
- [self] + ancestors(hierarchy_order: :asc)
- else
- [self] + ancestors
- end
-
- variables = Ci::GroupVariable.where(group: list_of_ids)
- variables = variables.unprotected unless project.protected_for?(ref)
-
- variables = if environment
- variables.on_environment(environment)
- else
- variables.where(environment_scope: '*')
- end
-
- variables = variables.group_by(&:group_id)
- list_of_ids.reverse.flat_map { |group| variables[group.id] }.compact
- end
end
Group.prepend_mod_with('Group')
diff --git a/app/models/group_group_link.rb b/app/models/group_group_link.rb
index 8dd245a6ab5..7005c8593bd 100644
--- a/app/models/group_group_link.rb
+++ b/app/models/group_group_link.rb
@@ -19,6 +19,10 @@ class GroupGroupLink < ApplicationRecord
where(group_access: [Gitlab::Access::OWNER, Gitlab::Access::MAINTAINER])
end
+ scope :with_owner_access, -> do
+ where(group_access: [Gitlab::Access::OWNER])
+ end
+
scope :groups_accessible_via, -> (shared_with_group_ids) do
links = where(shared_with_group_id: shared_with_group_ids)
# a group share also gives you access to the descendants of the group being shared,
diff --git a/app/models/hooks/web_hook_log.rb b/app/models/hooks/web_hook_log.rb
index 24e5f193a32..3fc3f193f19 100644
--- a/app/models/hooks/web_hook_log.rb
+++ b/app/models/hooks/web_hook_log.rb
@@ -25,7 +25,7 @@ class WebHookLog < ApplicationRecord
before_save :redact_author_email
def self.recent
- where('created_at >= ?', 2.days.ago.beginning_of_day)
+ where(created_at: 2.days.ago.beginning_of_day..Time.zone.now)
.order(created_at: :desc)
end
diff --git a/app/models/incident_management/timeline_event.rb b/app/models/incident_management/timeline_event.rb
index d30d6906e14..dd0d3c6585d 100644
--- a/app/models/incident_management/timeline_event.rb
+++ b/app/models/incident_management/timeline_event.rb
@@ -20,6 +20,6 @@ module IncidentManagement
validates :action, presence: true, length: { maximum: 128 }
validates :note, :note_html, presence: true, length: { maximum: 10_000 }
- scope :order_occurred_at_asc, -> { reorder(occurred_at: :asc) }
+ scope :order_occurred_at_asc_id_asc, -> { reorder(occurred_at: :asc, id: :asc) }
end
end
diff --git a/app/models/integration.rb b/app/models/integration.rb
index 6d755016380..aecf9529a14 100644
--- a/app/models/integration.rb
+++ b/app/models/integration.rb
@@ -401,9 +401,9 @@ class Integration < ApplicationRecord
.or(where(type: integration.type, instance: true)).select(:id)
from_union([
- where(type: integration.type, inherit_from_id: inherit_from_ids, group: integration.group.descendants),
- where(type: integration.type, inherit_from_id: inherit_from_ids, project: Project.in_namespace(integration.group.self_and_descendants))
- ])
+ where(type: integration.type, inherit_from_id: inherit_from_ids, group: integration.group.descendants),
+ where(type: integration.type, inherit_from_id: inherit_from_ids, project: Project.in_namespace(integration.group.self_and_descendants))
+ ])
end
def activated?
diff --git a/app/models/integrations/datadog.rb b/app/models/integrations/datadog.rb
index bb0fb6b9079..4479725a33b 100644
--- a/app/models/integrations/datadog.rb
+++ b/app/models/integrations/datadog.rb
@@ -10,7 +10,7 @@ module Integrations
URL_API_KEYS_DOCS = "https://docs.#{DEFAULT_DOMAIN}/account_management/api-app-keys/"
SUPPORTED_EVENTS = %w[
- pipeline job
+ pipeline job archive_trace
].freeze
TAG_KEY_VALUE_RE = %r{\A [\w-]+ : .*\S.* \z}x.freeze
@@ -38,14 +38,6 @@ module Integrations
SUPPORTED_EVENTS
end
- def supported_events
- events = super
-
- return events + ['archive_trace'] if Feature.enabled?(:datadog_integration_logs_collection, parent)
-
- events
- end
-
def self.default_test_event
'pipeline'
end
@@ -77,7 +69,7 @@ module Integrations
end
def fields
- f = [
+ [
{
type: 'text',
name: 'datadog_site',
@@ -110,21 +102,15 @@ module Integrations
linkClose: '</a>'.html_safe
},
required: true
- }
- ]
-
- if Feature.enabled?(:datadog_integration_logs_collection, parent)
- f.append({
+ },
+ {
type: 'checkbox',
name: 'archive_trace_events',
title: s_('Logs'),
checkbox_label: s_('Enable logs collection'),
help: s_('When enabled, job logs are collected by Datadog and displayed along with pipeline execution traces.'),
required: false
- })
- end
-
- f += [
+ },
{
type: 'text',
name: 'datadog_service',
@@ -161,8 +147,6 @@ module Integrations
}
}
]
-
- f
end
override :hook_url
diff --git a/app/models/integrations/discord.rb b/app/models/integrations/discord.rb
index ec8a12e4760..d0389b82410 100644
--- a/app/models/integrations/discord.rb
+++ b/app/models/integrations/discord.rb
@@ -6,6 +6,24 @@ module Integrations
class Discord < BaseChatNotification
ATTACHMENT_REGEX = /: (?<entry>.*?)\n - (?<name>.*)\n*/.freeze
+ undef :notify_only_broken_pipelines
+
+ field :webhook,
+ section: SECTION_TYPE_CONNECTION,
+ placeholder: 'https://discordapp.com/api/webhooks/…',
+ help: 'URL to the webhook for the Discord channel.',
+ required: true
+
+ field :notify_only_broken_pipelines,
+ type: 'checkbox',
+ section: SECTION_TYPE_CONFIGURATION
+
+ field :branches_to_be_notified,
+ type: 'select',
+ section: SECTION_TYPE_CONFIGURATION,
+ title: -> { s_('Integrations|Branches for which notifications are to be sent') },
+ choices: -> { branch_choices }
+
def title
s_("DiscordService|Discord Notifications")
end
@@ -18,6 +36,10 @@ module Integrations
"discord"
end
+ def fields
+ self.class.fields + build_event_channels
+ end
+
def help
docs_link = ActionController::Base.helpers.link_to _('How do I set up this service?'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/discord_notifications'), target: '_blank', rel: 'noopener noreferrer'
s_('Send notifications about project events to a Discord channel. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
@@ -31,30 +53,6 @@ module Integrations
%w[push issue confidential_issue merge_request note confidential_note tag_push pipeline wiki_page]
end
- def default_fields
- [
- {
- type: 'text',
- section: SECTION_TYPE_CONNECTION,
- name: 'webhook',
- placeholder: 'https://discordapp.com/api/webhooks/…',
- help: 'URL to the webhook for the Discord channel.'
- },
- {
- type: 'checkbox',
- section: SECTION_TYPE_CONFIGURATION,
- name: 'notify_only_broken_pipelines'
- },
- {
- type: 'select',
- section: SECTION_TYPE_CONFIGURATION,
- name: 'branches_to_be_notified',
- title: s_('Integrations|Branches for which notifications are to be sent'),
- choices: self.class.branch_choices
- }
- ]
- end
-
def sections
[
{
diff --git a/app/models/integrations/hangouts_chat.rb b/app/models/integrations/hangouts_chat.rb
index df112ad6ca8..6e7f31aa030 100644
--- a/app/models/integrations/hangouts_chat.rb
+++ b/app/models/integrations/hangouts_chat.rb
@@ -47,8 +47,31 @@ module Integrations
private
def notify(message, opts)
+ url = webhook.dup
+
+ key = parse_thread_key(message)
+ url = Gitlab::Utils.add_url_parameters(url, { threadKey: key }) if key
+
simple_text = parse_simple_text_message(message)
- ::HangoutsChat::Sender.new(webhook).simple(simple_text)
+ ::HangoutsChat::Sender.new(url).simple(simple_text)
+ end
+
+ # Returns an appropriate key for threading messages in google chat
+ def parse_thread_key(message)
+ case message
+ when Integrations::ChatMessage::NoteMessage
+ message.target
+ when Integrations::ChatMessage::IssueMessage
+ "issue #{Issue.reference_prefix}#{message.issue_iid}"
+ when Integrations::ChatMessage::MergeMessage
+ "merge request #{MergeRequest.reference_prefix}#{message.merge_request_iid}"
+ when Integrations::ChatMessage::PushMessage
+ "push #{message.project_name}_#{message.ref}"
+ when Integrations::ChatMessage::PipelineMessage
+ "pipeline #{message.pipeline_id}"
+ when Integrations::ChatMessage::WikiPageMessage
+ "wiki_page #{message.wiki_page_url}"
+ end
end
def parse_simple_text_message(message)
diff --git a/app/models/integrations/harbor.rb b/app/models/integrations/harbor.rb
index 03913a71d47..58eabcfd378 100644
--- a/app/models/integrations/harbor.rb
+++ b/app/models/integrations/harbor.rb
@@ -24,6 +24,10 @@ module Integrations
s_("HarborIntegration|After the Harbor integration is activated, global variables '$HARBOR_USERNAME', '$HARBOR_HOST', '$HARBOR_OCI', '$HARBOR_PASSWORD', '$HARBOR_URL' and '$HARBOR_PROJECT' will be created for CI/CD use.")
end
+ def hostname
+ Gitlab::Utils.parse_url(url).hostname
+ end
+
class << self
def to_param
name.demodulize.downcase
diff --git a/app/models/integrations/shimo.rb b/app/models/integrations/shimo.rb
index 8bc296e0320..f5b6595fff2 100644
--- a/app/models/integrations/shimo.rb
+++ b/app/models/integrations/shimo.rb
@@ -9,8 +9,6 @@ module Integrations
required: true
def render?
- return false unless Feature.enabled?(:shimo_integration, project)
-
valid? && activated?
end
diff --git a/app/models/internal_id.rb b/app/models/internal_id.rb
index b502d5e354d..d141061062a 100644
--- a/app/models/internal_id.rb
+++ b/app/models/internal_id.rb
@@ -143,10 +143,7 @@ class InternalId < ApplicationRecord
def track_greatest(new_value)
InternalId.internal_id_transactions_increment(operation: :track_greatest, usage: usage)
- function = Arel::Nodes::NamedFunction.new('GREATEST', [
- arel_table[:last_value],
- new_value.to_i
- ])
+ function = Arel::Nodes::NamedFunction.new('GREATEST', [arel_table[:last_value], new_value.to_i])
next_iid = update_record!(subject, scope, usage, function)
return next_iid if next_iid
diff --git a/app/models/issue.rb b/app/models/issue.rb
index df8ee34b3c3..153747c75df 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -254,31 +254,6 @@ class Issue < ApplicationRecord
alias_method :with_state, :with_state_id
alias_method :with_states, :with_state_ids
- def build_keyset_order_on_joined_column(scope:, attribute_name:, column:, direction:, nullable:)
- reversed_direction = direction == :asc ? :desc : :asc
-
- # rubocop: disable GitlabSecurity/PublicSend
- order = ::Gitlab::Pagination::Keyset::Order.build([
- ::Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: attribute_name,
- column_expression: column,
- order_expression: column.send(direction).send(nullable),
- reversed_order_expression: column.send(reversed_direction).send(nullable),
- order_direction: direction,
- distinct: false,
- add_to_projections: true,
- nullable: nullable
- ),
- ::Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: 'id',
- order_expression: arel_table['id'].desc
- )
- ])
- # rubocop: enable GitlabSecurity/PublicSend
-
- order.apply_cursor_conditions(scope).order(order)
- end
-
override :order_upvotes_desc
def order_upvotes_desc
reorder(upvotes_count: :desc)
@@ -293,16 +268,6 @@ class Issue < ApplicationRecord
def pg_full_text_search(search_term)
super.where('issue_search_data.project_id = issues.project_id')
end
-
- override :full_search
- def full_search(query, matched_columns: nil, use_minimum_char_limit: true)
- return super if query.match?(IssuableFinder::FULL_TEXT_SEARCH_TERM_REGEX)
-
- super.where(
- 'issues.title NOT SIMILAR TO :pattern OR issues.description NOT SIMILAR TO :pattern',
- pattern: IssuableFinder::FULL_TEXT_SEARCH_TERM_PATTERN
- )
- end
end
def next_object_by_relative_position(ignoring: nil, order: :asc)
@@ -406,8 +371,6 @@ class Issue < ApplicationRecord
attribute_name: 'relative_position',
column_expression: arel_table[:relative_position],
order_expression: Issue.arel_table[:relative_position].asc.nulls_last,
- reversed_order_expression: Issue.arel_table[:relative_position].desc.nulls_last,
- order_direction: :asc,
nullable: :nulls_last,
distinct: false
)
@@ -695,11 +658,11 @@ class Issue < ApplicationRecord
return unless persisted?
if confidential? && WorkItems::ParentLink.has_public_children?(id)
- errors.add(:confidential, _('confidential parent can not be used if there are non-confidential children.'))
+ errors.add(:base, _('A confidential issue cannot have a parent that already has non-confidential children.'))
end
if !confidential? && WorkItems::ParentLink.has_confidential_parent?(id)
- errors.add(:confidential, _('associated parent is confidential and can not have non-confidential children.'))
+ errors.add(:base, _('A non-confidential issue cannot have a confidential parent.'))
end
end
@@ -722,7 +685,7 @@ class Issue < ApplicationRecord
end
def record_create_action
- Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_created_action(author: author)
+ Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_created_action(author: author, project: project)
end
# Returns `true` if this Issue is visible to everybody.
diff --git a/app/models/jira_connect_installation.rb b/app/models/jira_connect_installation.rb
index 8befe9a9230..0a2d3ba0749 100644
--- a/app/models/jira_connect_installation.rb
+++ b/app/models/jira_connect_installation.rb
@@ -24,4 +24,10 @@ class JiraConnectInstallation < ApplicationRecord
def client
Atlassian::JiraConnect::Client.new(base_url, shared_secret)
end
+
+ def oauth_authorization_url
+ return Gitlab.config.gitlab.url if instance_url.blank? || Feature.disabled?(:jira_connect_oauth_self_managed)
+
+ instance_url
+ end
end
diff --git a/app/models/loose_foreign_keys/deleted_record.rb b/app/models/loose_foreign_keys/deleted_record.rb
index 94444f4b6d3..f28e8f81b40 100644
--- a/app/models/loose_foreign_keys/deleted_record.rb
+++ b/app/models/loose_foreign_keys/deleted_record.rb
@@ -12,7 +12,7 @@ class LooseForeignKeys::DeletedRecord < Gitlab::Database::SharedModel
next_partition_if: -> (active_partition) do
oldest_record_in_partition = LooseForeignKeys::DeletedRecord
.select(:id, :created_at)
- .for_partition(active_partition)
+ .for_partition(active_partition.value)
.order(:id)
.limit(1)
.take
@@ -22,7 +22,7 @@ class LooseForeignKeys::DeletedRecord < Gitlab::Database::SharedModel
end,
detach_partition_if: -> (partition) do
!LooseForeignKeys::DeletedRecord
- .for_partition(partition)
+ .for_partition(partition.value)
.status_pending
.exists?
end
diff --git a/app/models/member.rb b/app/models/member.rb
index 0cd1e022617..c5351d5447b 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -60,6 +60,7 @@ class Member < ApplicationRecord
if: :project_bot?
validate :access_level_inclusion
validate :validate_member_role_access_level
+ validate :validate_access_level_locked_for_member_role, on: :update
scope :with_invited_user_state, -> do
joins('LEFT JOIN users as invited_user ON invited_user.email = members.invite_email')
@@ -73,10 +74,7 @@ class Member < ApplicationRecord
projects = source.root_ancestor.all_projects
project_members = Member.default_scoped.where(source: projects).select(*Member.cached_column_list)
- Member.default_scoped.from_union([
- group_members,
- project_members
- ]).merge(self)
+ Member.default_scoped.from_union([group_members, project_members]).merge(self)
end
scope :excluding_users, ->(user_ids) do
@@ -186,14 +184,85 @@ class Member < ApplicationRecord
unscoped.from(distinct_members, :members)
end
- scope :order_name_asc, -> { left_join_users.reorder(User.arel_table[:name].asc.nulls_last) }
- scope :order_name_desc, -> { left_join_users.reorder(User.arel_table[:name].desc.nulls_last) }
- scope :order_recent_sign_in, -> { left_join_users.reorder(User.arel_table[:last_sign_in_at].desc.nulls_last) }
- scope :order_oldest_sign_in, -> { left_join_users.reorder(User.arel_table[:last_sign_in_at].asc.nulls_last) }
- scope :order_recent_last_activity, -> { left_join_users.reorder(User.arel_table[:last_activity_on].desc.nulls_last) }
- scope :order_oldest_last_activity, -> { left_join_users.reorder(User.arel_table[:last_activity_on].asc.nulls_first) }
- scope :order_recent_created_user, -> { left_join_users.reorder(User.arel_table[:created_at].desc.nulls_last) }
- scope :order_oldest_created_user, -> { left_join_users.reorder(User.arel_table[:created_at].asc.nulls_first) }
+ scope :order_name_asc, -> do
+ build_keyset_order_on_joined_column(
+ scope: left_join_users,
+ attribute_name: 'member_user_full_name',
+ column: User.arel_table[:name],
+ direction: :asc,
+ nullable: :nulls_last
+ )
+ end
+
+ scope :order_name_desc, -> do
+ build_keyset_order_on_joined_column(
+ scope: left_join_users,
+ attribute_name: 'member_user_full_name',
+ column: User.arel_table[:name],
+ direction: :desc,
+ nullable: :nulls_last
+ )
+ end
+
+ scope :order_oldest_sign_in, -> do
+ build_keyset_order_on_joined_column(
+ scope: left_join_users,
+ attribute_name: 'member_user_last_sign_in_at',
+ column: User.arel_table[:last_sign_in_at],
+ direction: :asc,
+ nullable: :nulls_last
+ )
+ end
+
+ scope :order_recent_sign_in, -> do
+ build_keyset_order_on_joined_column(
+ scope: left_join_users,
+ attribute_name: 'member_user_last_sign_in_at',
+ column: User.arel_table[:last_sign_in_at],
+ direction: :desc,
+ nullable: :nulls_last
+ )
+ end
+
+ scope :order_oldest_last_activity, -> do
+ build_keyset_order_on_joined_column(
+ scope: left_join_users,
+ attribute_name: 'member_user_last_activity_on',
+ column: User.arel_table[:last_activity_on],
+ direction: :asc,
+ nullable: :nulls_first
+ )
+ end
+
+ scope :order_recent_last_activity, -> do
+ build_keyset_order_on_joined_column(
+ scope: left_join_users,
+ attribute_name: 'member_user_last_activity_on',
+ column: User.arel_table[:last_activity_on],
+ direction: :desc,
+ nullable: :nulls_last
+ )
+ end
+
+ scope :order_oldest_created_user, -> do
+ build_keyset_order_on_joined_column(
+ scope: left_join_users,
+ attribute_name: 'member_user_created_at',
+ column: User.arel_table[:created_at],
+ direction: :asc,
+ nullable: :nulls_first
+ )
+ end
+
+ scope :order_recent_created_user, -> do
+ build_keyset_order_on_joined_column(
+ scope: left_join_users,
+ attribute_name: 'member_user_created_at',
+ column: User.arel_table[:created_at],
+ direction: :desc,
+ nullable: :nulls_last
+ )
+ end
scope :on_project_and_ancestors, ->(project) { where(source: [project] + project.ancestors) }
@@ -438,6 +507,14 @@ class Member < ApplicationRecord
end
end
+ def validate_access_level_locked_for_member_role
+ return unless member_role_id
+
+ if access_level_changed?
+ errors.add(:access_level, _("cannot be changed since member is associated with a custom role"))
+ end
+ end
+
def send_invite
# override in subclass
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 3c06e1aa983..a57cb97e936 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -20,7 +20,7 @@ class MergeRequest < ApplicationRecord
include IgnorableColumns
include MilestoneEventable
include StateEventable
- include ApprovableBase
+ include Approvable
include IdInOrdered
include Todoable
@@ -67,6 +67,8 @@ class MergeRequest < ApplicationRecord
has_one :merge_head_diff,
-> { merge_head }, inverse_of: :merge_request, class_name: 'MergeRequestDiff'
has_one :cleanup_schedule, inverse_of: :merge_request
+ has_one :predictions, inverse_of: :merge_request
+ delegate :suggested_reviewers, to: :predictions
belongs_to :latest_merge_request_diff, class_name: 'MergeRequestDiff'
manual_inverse_association :latest_merge_request_diff, :merge_request
@@ -116,6 +118,7 @@ class MergeRequest < ApplicationRecord
has_many :draft_notes
has_many :reviews, inverse_of: :merge_request
+ has_many :created_environments, class_name: 'Environment', foreign_key: :merge_request_id, inverse_of: :merge_request
KNOWN_MERGE_PARAMS = [
:auto_merge_strategy,
@@ -343,23 +346,24 @@ class MergeRequest < ApplicationRecord
column_expression = MergeRequest::Metrics.arel_table[metric]
column_expression_with_direction = direction == 'ASC' ? column_expression.asc : column_expression.desc
- order = Gitlab::Pagination::Keyset::Order.build([
- Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: "merge_request_metrics_#{metric}",
- column_expression: column_expression,
- order_expression: column_expression_with_direction.nulls_last,
- reversed_order_expression: column_expression_with_direction.reverse.nulls_first,
- order_direction: direction,
- nullable: :nulls_last,
- distinct: false,
- add_to_projections: true
- ),
- Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: 'merge_request_metrics_id',
- order_expression: MergeRequest::Metrics.arel_table[:id].desc,
- add_to_projections: true
- )
- ])
+ order = Gitlab::Pagination::Keyset::Order.build(
+ [
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: "merge_request_metrics_#{metric}",
+ column_expression: column_expression,
+ order_expression: column_expression_with_direction.nulls_last,
+ reversed_order_expression: column_expression_with_direction.reverse.nulls_first,
+ order_direction: direction,
+ nullable: :nulls_last,
+ distinct: false,
+ add_to_projections: true
+ ),
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'merge_request_metrics_id',
+ order_expression: MergeRequest::Metrics.arel_table[:id].desc,
+ add_to_projections: true
+ )
+ ])
order.apply_cursor_conditions(join_metrics).order(order)
end
@@ -417,17 +421,6 @@ class MergeRequest < ApplicationRecord
)
end
- scope :attention, ->(user) do
- # rubocop: disable Gitlab/Union
- union = Gitlab::SQL::Union.new([
- MergeRequestReviewer.select(:merge_request_id).where(user_id: user.id, state: MergeRequestReviewer.states[:attention_requested]),
- MergeRequestAssignee.select(:merge_request_id).where(user_id: user.id, state: MergeRequestAssignee.states[:attention_requested])
- ])
- # rubocop: enable Gitlab/Union
-
- with(Gitlab::SQL::CTE.new(:reviewers_and_assignees, union).to_arel).where('merge_requests.id in (select merge_request_id from reviewers_and_assignees)')
- end
-
def self.total_time_to_merge
join_metrics
.merge(MergeRequest::Metrics.with_valid_time_to_merge)
@@ -1187,41 +1180,13 @@ class MergeRequest < ApplicationRecord
]
end
- def detailed_merge_status
- if cannot_be_merged_rechecking? || preparing? || checking?
- return :checking
- elsif unchecked?
- return :unchecked
- end
-
- checks = execute_merge_checks
-
- if checks.success?
- :mergeable
- else
- checks.failure_reason
- end
- end
-
- # rubocop: disable CodeReuse/ServiceClass
def mergeable_state?(skip_ci_check: false, skip_discussions_check: false)
- if Feature.enabled?(:improved_mergeability_checks, self.project)
- additional_checks = execute_merge_checks(params: {
- skip_ci_check: skip_ci_check,
- skip_discussions_check: skip_discussions_check
- })
- additional_checks.execute.success?
- else
- return false unless open?
- return false if draft?
- return false if broken?
- return false unless skip_discussions_check || mergeable_discussions_state?
- return false unless skip_ci_check || mergeable_ci_state?
-
- true
- end
+ additional_checks = execute_merge_checks(params: {
+ skip_ci_check: skip_ci_check,
+ skip_discussions_check: skip_discussions_check
+ })
+ additional_checks.success?
end
- # rubocop: enable CodeReuse/ServiceClass
def ff_merge_possible?
project.repository.ancestor?(target_branch_sha, diff_head_sha)
@@ -1318,7 +1283,6 @@ class MergeRequest < ApplicationRecord
# running `ReferenceExtractor` on each of them separately.
# This optimization does not apply to issues from external sources.
def cache_merge_request_closes_issues!(current_user = self.author)
- return unless project.issues_enabled?
return if closed? || merged?
transaction do
@@ -1489,7 +1453,7 @@ class MergeRequest < ApplicationRecord
end
def environments_in_head_pipeline(deployment_status: nil)
- actual_head_pipeline&.environments_in_self_and_descendants(deployment_status: deployment_status) || Environment.none
+ actual_head_pipeline&.environments_in_self_and_project_descendants(deployment_status: deployment_status) || Environment.none
end
def fetch_ref!
@@ -1589,7 +1553,7 @@ class MergeRequest < ApplicationRecord
end
def has_test_reports?
- actual_head_pipeline&.has_reports?(Ci::JobArtifact.test_reports)
+ actual_head_pipeline&.complete_and_has_reports?(Ci::JobArtifact.of_report_type(:test))
end
def predefined_variables
@@ -1619,7 +1583,7 @@ class MergeRequest < ApplicationRecord
end
def has_accessibility_reports?
- actual_head_pipeline.present? && actual_head_pipeline.has_reports?(Ci::JobArtifact.accessibility_reports)
+ actual_head_pipeline.present? && actual_head_pipeline.complete_and_has_reports?(Ci::JobArtifact.of_report_type(:accessibility))
end
def has_coverage_reports?
@@ -1627,7 +1591,7 @@ class MergeRequest < ApplicationRecord
end
def has_terraform_reports?
- actual_head_pipeline&.has_reports?(Ci::JobArtifact.terraform_reports)
+ actual_head_pipeline&.complete_and_has_reports?(Ci::JobArtifact.of_report_type(:terraform))
end
def compare_accessibility_reports
@@ -1667,7 +1631,7 @@ class MergeRequest < ApplicationRecord
end
def has_codequality_reports?
- actual_head_pipeline&.has_reports?(Ci::JobArtifact.codequality_reports)
+ actual_head_pipeline&.complete_and_has_reports?(Ci::JobArtifact.of_report_type(:codequality))
end
def compare_codequality_reports
@@ -1717,11 +1681,11 @@ class MergeRequest < ApplicationRecord
end
def has_sast_reports?
- !!actual_head_pipeline&.has_reports?(::Ci::JobArtifact.sast_reports)
+ !!actual_head_pipeline&.complete_and_has_reports?(::Ci::JobArtifact.of_report_type(:sast))
end
def has_secret_detection_reports?
- !!actual_head_pipeline&.has_reports?(::Ci::JobArtifact.secret_detection_reports)
+ !!actual_head_pipeline&.complete_and_has_reports?(::Ci::JobArtifact.of_report_type(:secret_detection))
end
def compare_sast_reports(current_user)
@@ -2019,6 +1983,12 @@ class MergeRequest < ApplicationRecord
false # Overridden in EE
end
+ def execute_merge_checks(params: {})
+ # rubocop: disable CodeReuse/ServiceClass
+ MergeRequests::Mergeability::RunChecksService.new(merge_request: self, params: params).execute
+ # rubocop: enable CodeReuse/ServiceClass
+ end
+
private
attr_accessor :skip_fetch_ref
@@ -2072,12 +2042,6 @@ class MergeRequest < ApplicationRecord
def report_type_enabled?(report_type)
!!actual_head_pipeline&.batch_lookup_report_artifact_for_file_type(report_type)
end
-
- def execute_merge_checks(params: {})
- # rubocop: disable CodeReuse/ServiceClass
- MergeRequests::Mergeability::RunChecksService.new(merge_request: self, params: params).execute
- # rubocop: enable CodeReuse/ServiceClass
- end
end
MergeRequest.prepend_mod_with('MergeRequest')
diff --git a/app/models/merge_request/predictions.rb b/app/models/merge_request/predictions.rb
new file mode 100644
index 00000000000..ef9e00b5f74
--- /dev/null
+++ b/app/models/merge_request/predictions.rb
@@ -0,0 +1,7 @@
+# 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_assignee.rb b/app/models/merge_request_assignee.rb
index fd8e5860040..be3a1d42eac 100644
--- a/app/models/merge_request_assignee.rb
+++ b/app/models/merge_request_assignee.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
class MergeRequestAssignee < ApplicationRecord
- include MergeRequestReviewerState
+ include IgnorableColumns
+ ignore_column %i[state updated_state_by_user_id], remove_with: '15.6', remove_after: '2022-10-22'
belongs_to :merge_request, touch: true
belongs_to :assignee, class_name: "User", foreign_key: :user_id, inverse_of: :merge_request_assignees
@@ -11,6 +12,6 @@ class MergeRequestAssignee < ApplicationRecord
scope :in_projects, ->(project_ids) { joins(:merge_request).where(merge_requests: { target_project_id: project_ids }) }
def cache_key
- [model_name.cache_key, id, state, assignee.cache_key]
+ [model_name.cache_key, id, assignee.cache_key]
end
end
diff --git a/app/models/merge_request_reviewer.rb b/app/models/merge_request_reviewer.rb
index 4abf0fa09f0..4b5b71481d3 100644
--- a/app/models/merge_request_reviewer.rb
+++ b/app/models/merge_request_reviewer.rb
@@ -2,6 +2,8 @@
class MergeRequestReviewer < ApplicationRecord
include MergeRequestReviewerState
+ include IgnorableColumns
+ ignore_column :updated_state_by_user_id, remove_with: '15.6', remove_after: '2022-10-22'
belongs_to :merge_request
belongs_to :reviewer, class_name: 'User', foreign_key: :user_id, inverse_of: :merge_request_reviewers
diff --git a/app/models/ml/candidate.rb b/app/models/ml/candidate.rb
index e181217f01c..29e1ba88528 100644
--- a/app/models/ml/candidate.rb
+++ b/app/models/ml/candidate.rb
@@ -2,11 +2,24 @@
module Ml
class Candidate < ApplicationRecord
+ enum status: { running: 0, scheduled: 1, finished: 2, failed: 3, killed: 4 }
+
validates :iid, :experiment, presence: true
+ validates :status, inclusion: { in: statuses.keys }
belongs_to :experiment, class_name: 'Ml::Experiment'
belongs_to :user
has_many :metrics, class_name: 'Ml::CandidateMetric'
has_many :params, class_name: 'Ml::CandidateParam'
+
+ default_value_for(:iid) { SecureRandom.uuid }
+
+ class << self
+ def with_project_id_and_iid(project_id, iid)
+ return unless project_id.present? && iid.present?
+
+ joins(:experiment).find_by(experiment: { project_id: project_id }, iid: iid)
+ end
+ end
end
end
diff --git a/app/models/ml/experiment.rb b/app/models/ml/experiment.rb
index 7ef9c70ba7e..e4e9baac4c8 100644
--- a/app/models/ml/experiment.rb
+++ b/app/models/ml/experiment.rb
@@ -2,11 +2,33 @@
module Ml
class Experiment < ApplicationRecord
- validates :name, :iid, :project, presence: true
- validates :iid, :name, uniqueness: { scope: :project, message: "should be unique in the project" }
+ include AtomicInternalId
+
+ validates :name, :project, presence: true
+ validates :name, uniqueness: { scope: :project, message: "should be unique in the project" }
belongs_to :project
belongs_to :user
has_many :candidates, class_name: 'Ml::Candidate'
+
+ has_internal_id :iid, scope: :project
+
+ def artifact_location
+ 'not_implemented'
+ end
+
+ class << self
+ def by_project_id_and_iid(project_id, iid)
+ find_by(project_id: project_id, iid: iid)
+ end
+
+ def by_project_id_and_name(project_id, name)
+ find_by(project_id: project_id, name: name)
+ end
+
+ def has_record?(project_id, name)
+ where(project_id: project_id, name: name).exists?
+ end
+ end
end
end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 06f49f16d66..0ffd5c446d3 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -43,6 +43,8 @@ class Namespace < ApplicationRecord
# The first date in https://docs.gitlab.com/ee/user/usage_quotas.html#namespace-storage-limit-enforcement-schedule
# Determines when we start enforcing namespace storage
MIN_STORAGE_ENFORCEMENT_DATE = Date.new(2022, 10, 19)
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/367531
+ MIN_STORAGE_ENFORCEMENT_USAGE = 5.gigabytes
cache_markdown_field :description, pipeline: :description
@@ -59,7 +61,7 @@ class Namespace < ApplicationRecord
has_many :runner_namespaces, inverse_of: :namespace, class_name: 'Ci::RunnerNamespace'
has_many :runners, through: :runner_namespaces, source: :runner, class_name: 'Ci::Runner'
has_many :pending_builds, class_name: 'Ci::PendingBuild'
- has_one :onboarding_progress
+ has_one :onboarding_progress, class_name: 'Onboarding::Progress'
# This should _not_ be `inverse_of: :namespace`, because that would also set
# `user.namespace` when this user creates a group with themselves as `owner`.
@@ -126,8 +128,9 @@ class Namespace < ApplicationRecord
delegate :avatar_url, to: :owner, allow_nil: true
delegate :prevent_sharing_groups_outside_hierarchy, :prevent_sharing_groups_outside_hierarchy=,
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
- after_save :schedule_sync_event_worker, if: -> { saved_change_to_id? || saved_change_to_parent_id? }
after_save :reload_namespace_details
after_commit :refresh_access_of_projects_invited_groups, on: :update, if: -> { previous_changes.key?('share_with_group_lock') }
@@ -136,6 +139,7 @@ class Namespace < ApplicationRecord
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_sync_traversal_ids :schedule_sync_event_worker # custom callback defined in Namespaces::Traversal::Linear
# Legacy Storage specific hooks
@@ -172,13 +176,17 @@ class Namespace < ApplicationRecord
end
scope :sorted_by_similarity_and_parent_id_desc, -> (search) do
- order_expression = Gitlab::Database::SimilarityScore.build_expression(search: search, rules: [
- { column: arel_table["path"], multiplier: 1 },
- { column: arel_table["name"], multiplier: 0.7 }
- ])
+ order_expression = Gitlab::Database::SimilarityScore.build_expression(
+ search: search,
+ rules: [
+ { column: arel_table["path"], multiplier: 1 },
+ { column: arel_table["name"], multiplier: 0.7 }
+ ])
reorder(order_expression.desc, Namespace.arel_table['parent_id'].desc.nulls_last, Namespace.arel_table['id'].desc)
end
+ scope :with_shared_runners_enabled, -> { where(shared_runners_enabled: true) }
+
# Make sure that the name is same as strong_memoize name in root_ancestor
# method
attr_writer :root_ancestor, :emails_disabled_memoized
@@ -362,7 +370,7 @@ class Namespace < ApplicationRecord
end
def any_project_with_shared_runners_enabled?
- projects.with_shared_runners.any?
+ projects.with_shared_runners_enabled.any?
end
def user_ids_for_project_authorizations
diff --git a/app/models/namespace_setting.rb b/app/models/namespace_setting.rb
index 595e34821af..6a87fba57ac 100644
--- a/app/models/namespace_setting.rb
+++ b/app/models/namespace_setting.rb
@@ -4,6 +4,11 @@ class NamespaceSetting < ApplicationRecord
include CascadingNamespaceSettingAttribute
include Sanitizable
include ChronicDurationAttribute
+ include IgnorableColumns
+
+ ignore_columns %i[exclude_from_free_user_cap include_for_free_user_cap_preview],
+ remove_with: '15.5',
+ remove_after: '2022-09-23'
cascading_attr :delayed_project_removal
@@ -53,8 +58,18 @@ class NamespaceSetting < ApplicationRecord
namespace.root_ancestor.prevent_sharing_groups_outside_hierarchy
end
+ def show_diff_preview_in_email?
+ return show_diff_preview_in_email unless namespace.has_parent?
+
+ all_ancestors_allow_diff_preview_in_email?
+ end
+
private
+ def all_ancestors_allow_diff_preview_in_email?
+ !self.class.where(namespace_id: namespace.self_and_ancestors, show_diff_preview_in_email: false).exists?
+ end
+
def normalize_default_branch_name
self.default_branch_name = default_branch_name.presence
end
diff --git a/app/models/namespaces/traversal/linear.rb b/app/models/namespaces/traversal/linear.rb
index 687fa6a5334..16a9c20dfdc 100644
--- a/app/models/namespaces/traversal/linear.rb
+++ b/app/models/namespaces/traversal/linear.rb
@@ -47,6 +47,8 @@ module Namespaces
# This uses rails internal before_commit API to sync traversal_ids on namespace create, right before transaction is committed.
# This helps reduce the time during which the root namespace record is locked to ensure updated traversal_ids are valid
before_commit :sync_traversal_ids, on: [:create]
+
+ define_model_callbacks :sync_traversal_ids
end
class_methods do
@@ -208,10 +210,12 @@ module Namespaces
#
# NOTE: self.traversal_ids will be stale. Reload for a fresh record.
def sync_traversal_ids
- # Clear any previously memoized root_ancestor as our ancestors have changed.
- clear_memoization(:root_ancestor)
+ run_callbacks :sync_traversal_ids do
+ # Clear any previously memoized root_ancestor as our ancestors have changed.
+ clear_memoization(:root_ancestor)
- Namespace::TraversalHierarchy.for_namespace(self).sync_traversal_ids!
+ Namespace::TraversalHierarchy.for_namespace(self).sync_traversal_ids!
+ end
end
# Lock the root of the hierarchy we just left, and lock the root of the hierarchy
diff --git a/app/models/namespaces/traversal/linear_scopes.rb b/app/models/namespaces/traversal/linear_scopes.rb
index 81ac026d7ff..843de9bce33 100644
--- a/app/models/namespaces/traversal/linear_scopes.rb
+++ b/app/models/namespaces/traversal/linear_scopes.rb
@@ -41,24 +41,13 @@ module Namespaces
def self_and_descendants(include_self: true)
return super unless use_traversal_ids_for_descendants_scopes?
- if Feature.enabled?(:traversal_ids_btree)
- self_and_descendants_with_comparison_operators(include_self: include_self)
- else
- records = self_and_descendants_with_duplicates_with_array_operator(include_self: include_self)
- distinct = records.select('DISTINCT on(namespaces.id) namespaces.*')
- distinct.normal_select
- end
+ self_and_descendants_with_comparison_operators(include_self: include_self)
end
def self_and_descendant_ids(include_self: true)
return super unless use_traversal_ids_for_descendants_scopes?
- if Feature.enabled?(:traversal_ids_btree)
- self_and_descendants_with_comparison_operators(include_self: include_self).as_ids
- else
- self_and_descendants_with_duplicates_with_array_operator(include_self: include_self)
- .select('DISTINCT namespaces.id')
- end
+ self_and_descendants(include_self: include_self).as_ids
end
def self_and_hierarchy
@@ -181,20 +170,6 @@ module Namespaces
Arel::Nodes::NamedFunction.new('unnest', args)
end
- def self_and_descendants_with_duplicates_with_array_operator(include_self: true)
- base_ids = select(:id)
-
- records = unscoped
- .from("namespaces, (#{base_ids.to_sql}) base")
- .where('namespaces.traversal_ids @> ARRAY[base.id]')
-
- if include_self
- records
- else
- records.where('namespaces.id <> base.id')
- end
- end
-
def superset_cte(base_name)
superset_sql = <<~SQL
SELECT d1.traversal_ids
diff --git a/app/models/note.rb b/app/models/note.rb
index 1715f7cdc3b..daac489757b 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -164,6 +164,9 @@ class Note < ApplicationRecord
scope :like_note_or_capitalized_note, ->(text) { where('(note LIKE ? OR note LIKE ?)', text, text.capitalize) }
before_validation :nullify_blank_type, :nullify_blank_line_code
+ # Syncs `confidential` with `internal` as we rename the column.
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/367923
+ before_create :set_internal_flag
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?
@@ -813,6 +816,10 @@ class Note < ApplicationRecord
def noteable_can_have_confidential_note?
for_issue?
end
+
+ def set_internal_flag
+ self.internal = confidential if confidential
+ end
end
Note.prepend_mod_with('Note')
diff --git a/app/models/notification_recipient.rb b/app/models/notification_recipient.rb
index b3eaed154e2..caa24377791 100644
--- a/app/models/notification_recipient.rb
+++ b/app/models/notification_recipient.rb
@@ -38,6 +38,7 @@ class NotificationRecipient
return !unsubscribed? if @type == :subscription
return false unless suitable_notification_level?
+ return false if email_blocked?
# check this last because it's expensive
# nobody should receive notifications if they've specifically unsubscribed
@@ -95,6 +96,15 @@ class NotificationRecipient
end
end
+ def email_blocked?
+ return false if Feature.disabled?(:block_emails_with_failures)
+
+ recipient_email = user.notification_email_for(@group)
+
+ Gitlab::ApplicationRateLimiter.peek(:permanent_email_failure, scope: recipient_email) ||
+ Gitlab::ApplicationRateLimiter.peek(:temporary_email_failure, scope: recipient_email)
+ end
+
def has_access?
DeclarativePolicy.subject_scope do
break false unless user.can?(:receive_notifications)
diff --git a/app/models/oauth_access_token.rb b/app/models/oauth_access_token.rb
index 7d71e15d3c5..eac99e8d441 100644
--- a/app/models/oauth_access_token.rb
+++ b/app/models/oauth_access_token.rb
@@ -26,4 +26,13 @@ class OauthAccessToken < Doorkeeper::AccessToken
super
end
+
+ # Override Doorkeeper::AccessToken.matching_token_for since we
+ # have `reuse_access_tokens` disabled and we also hash tokens.
+ # This ensures we don't accidentally return a hashed token value.
+ def self.matching_token_for(application, resource_owner, scopes)
+ return if Feature.enabled?(:hash_oauth_tokens)
+
+ super
+ end
end
diff --git a/app/models/onboarding/completion.rb b/app/models/onboarding/completion.rb
new file mode 100644
index 00000000000..49fdb102209
--- /dev/null
+++ b/app/models/onboarding/completion.rb
@@ -0,0 +1,70 @@
+# frozen_string_literal: true
+
+module Onboarding
+ class Completion
+ include Gitlab::Utils::StrongMemoize
+ include Gitlab::Experiment::Dsl
+
+ ACTION_ISSUE_IDS = {
+ pipeline_created: 7,
+ trial_started: 2,
+ required_mr_approvals_enabled: 11,
+ code_owners_enabled: 10
+ }.freeze
+
+ ACTION_PATHS = [
+ :issue_created,
+ :git_write,
+ :merge_request_created,
+ :user_added
+ ].freeze
+
+ def initialize(namespace, current_user = nil)
+ @namespace = namespace
+ @current_user = current_user
+ end
+
+ def percentage
+ return 0 unless onboarding_progress
+
+ attributes = onboarding_progress.attributes.symbolize_keys
+
+ total_actions = action_columns.count
+ completed_actions = action_columns.count { |column| attributes[column].present? }
+
+ (completed_actions.to_f / total_actions * 100).round
+ end
+
+ private
+
+ def onboarding_progress
+ strong_memoize(:onboarding_progress) do
+ ::Onboarding::Progress.find_by(namespace: namespace)
+ end
+ end
+
+ def action_columns
+ strong_memoize(:action_columns) do
+ tracked_actions.map { |action_key| ::Onboarding::Progress.column_name(action_key) }
+ end
+ end
+
+ def tracked_actions
+ ACTION_ISSUE_IDS.keys + ACTION_PATHS + deploy_section_tracked_actions
+ end
+
+ def deploy_section_tracked_actions
+ experiment(
+ :security_actions_continuous_onboarding,
+ namespace: namespace,
+ user: current_user,
+ sticky_to: current_user
+ ) do |e|
+ e.control { [:security_scan_enabled] }
+ e.candidate { [:license_scanning_run, :secure_dependency_scanning_run, :secure_dast_run] }
+ end.run
+ end
+
+ attr_reader :namespace, :current_user
+ end
+end
diff --git a/app/models/onboarding/learn_gitlab.rb b/app/models/onboarding/learn_gitlab.rb
new file mode 100644
index 00000000000..d7a189ed6e2
--- /dev/null
+++ b/app/models/onboarding/learn_gitlab.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+module Onboarding
+ class LearnGitlab
+ PROJECT_NAME = 'Learn GitLab'
+ PROJECT_NAME_ULTIMATE_TRIAL = 'Learn GitLab - Ultimate trial'
+ BOARD_NAME = 'GitLab onboarding'
+ LABEL_NAME = 'Novice'
+
+ def initialize(current_user)
+ @current_user = current_user
+ end
+
+ def available?
+ project && board && label
+ end
+
+ def project
+ @project ||= current_user.projects.find_by_name([PROJECT_NAME, PROJECT_NAME_ULTIMATE_TRIAL])
+ end
+
+ def board
+ return unless project
+
+ @board ||= project.boards.find_by_name(BOARD_NAME)
+ end
+
+ def label
+ return unless project
+
+ @label ||= project.labels.find_by_name(LABEL_NAME)
+ end
+
+ private
+
+ attr_reader :current_user
+ end
+end
diff --git a/app/models/onboarding/progress.rb b/app/models/onboarding/progress.rb
new file mode 100644
index 00000000000..ecc78418256
--- /dev/null
+++ b/app/models/onboarding/progress.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+
+module Onboarding
+ class Progress < ApplicationRecord
+ self.table_name = 'onboarding_progresses'
+
+ belongs_to :namespace, optional: false
+
+ validate :namespace_is_root_namespace
+
+ ACTIONS = [
+ :git_pull,
+ :git_write,
+ :merge_request_created,
+ :pipeline_created,
+ :user_added,
+ :trial_started,
+ :subscription_created,
+ :required_mr_approvals_enabled,
+ :code_owners_enabled,
+ :scoped_label_created,
+ :security_scan_enabled,
+ :issue_created,
+ :issue_auto_closed,
+ :repository_imported,
+ :repository_mirrored,
+ :secure_dependency_scanning_run,
+ :secure_container_scanning_run,
+ :secure_dast_run,
+ :secure_secret_detection_run,
+ :secure_coverage_fuzzing_run,
+ :secure_api_fuzzing_run,
+ :secure_cluster_image_scanning_run,
+ :license_scanning_run
+ ].freeze
+
+ scope :incomplete_actions, ->(actions) do
+ Array.wrap(actions).inject(self) { |scope, action| scope.where(column_name(action) => nil) }
+ end
+
+ scope :completed_actions, ->(actions) do
+ Array.wrap(actions).inject(self) { |scope, action| scope.where.not(column_name(action) => nil) }
+ end
+
+ scope :completed_actions_with_latest_in_range, ->(actions, range) do
+ actions = Array(actions)
+ if actions.size == 1
+ where(column_name(actions[0]) => range)
+ else
+ action_columns = actions.map { |action| arel_table[column_name(action)] }
+ completed_actions(actions).where(Arel::Nodes::NamedFunction.new('GREATEST', action_columns).between(range))
+ end
+ end
+
+ class << self
+ def onboard(namespace)
+ return unless root_namespace?(namespace)
+
+ create(namespace: namespace)
+ end
+
+ def onboarding?(namespace)
+ where(namespace: namespace).any?
+ end
+
+ def register(namespace, actions)
+ actions = Array(actions)
+ return unless root_namespace?(namespace) && actions.difference(ACTIONS).empty?
+
+ onboarding_progress = find_by(namespace: namespace)
+ return unless onboarding_progress
+
+ now = Time.current
+ nil_actions = actions.select { |action| onboarding_progress[column_name(action)].nil? }
+ return if nil_actions.empty?
+
+ updates = nil_actions.inject({}) { |sum, action| sum.merge!({ column_name(action) => now }) }
+ onboarding_progress.update!(updates)
+ end
+
+ def completed?(namespace, action)
+ return unless root_namespace?(namespace) && ACTIONS.include?(action)
+
+ action_column = column_name(action)
+ where(namespace: namespace).where.not(action_column => nil).exists?
+ end
+
+ def not_completed?(namespace_id, action)
+ return unless ACTIONS.include?(action)
+
+ action_column = column_name(action)
+ exists?(namespace_id: namespace_id, action_column => nil)
+ end
+
+ def column_name(action)
+ :"#{action}_at"
+ end
+
+ private
+
+ def root_namespace?(namespace)
+ namespace&.root?
+ end
+ end
+
+ def number_of_completed_actions
+ attributes.extract!(*ACTIONS.map { |action| self.class.column_name(action).to_s }).compact!.size
+ end
+
+ private
+
+ def namespace_is_root_namespace
+ return unless namespace
+
+ errors.add(:namespace, _('must be a root namespace')) if namespace.has_parent?
+ end
+ end
+end
diff --git a/app/models/onboarding_progress.rb b/app/models/onboarding_progress.rb
deleted file mode 100644
index e5851c5cfc5..00000000000
--- a/app/models/onboarding_progress.rb
+++ /dev/null
@@ -1,114 +0,0 @@
-# frozen_string_literal: true
-
-class OnboardingProgress < ApplicationRecord
- belongs_to :namespace, optional: false
-
- validate :namespace_is_root_namespace
-
- ACTIONS = [
- :git_pull,
- :git_write,
- :merge_request_created,
- :pipeline_created,
- :user_added,
- :trial_started,
- :subscription_created,
- :required_mr_approvals_enabled,
- :code_owners_enabled,
- :scoped_label_created,
- :security_scan_enabled,
- :issue_created,
- :issue_auto_closed,
- :repository_imported,
- :repository_mirrored,
- :secure_dependency_scanning_run,
- :secure_container_scanning_run,
- :secure_dast_run,
- :secure_secret_detection_run,
- :secure_coverage_fuzzing_run,
- :secure_api_fuzzing_run,
- :secure_cluster_image_scanning_run,
- :license_scanning_run
- ].freeze
-
- scope :incomplete_actions, -> (actions) do
- Array.wrap(actions).inject(self) { |scope, action| scope.where(column_name(action) => nil) }
- end
-
- scope :completed_actions, -> (actions) do
- Array.wrap(actions).inject(self) { |scope, action| scope.where.not(column_name(action) => nil) }
- end
-
- scope :completed_actions_with_latest_in_range, -> (actions, range) do
- actions = Array(actions)
- if actions.size == 1
- where(column_name(actions[0]) => range)
- else
- action_columns = actions.map { |action| arel_table[column_name(action)] }
- completed_actions(actions).where(Arel::Nodes::NamedFunction.new('GREATEST', action_columns).between(range))
- end
- end
-
- class << self
- def onboard(namespace)
- return unless root_namespace?(namespace)
-
- create(namespace: namespace)
- end
-
- def onboarding?(namespace)
- where(namespace: namespace).any?
- end
-
- def register(namespace, actions)
- actions = Array(actions)
- return unless root_namespace?(namespace) && actions.difference(ACTIONS).empty?
-
- onboarding_progress = find_by(namespace: namespace)
- return unless onboarding_progress
-
- now = Time.current
- nil_actions = actions.select { |action| onboarding_progress[column_name(action)].nil? }
- return if nil_actions.empty?
-
- updates = nil_actions.inject({}) { |sum, action| sum.merge!({ column_name(action) => now }) }
- onboarding_progress.update!(updates)
- end
-
- def completed?(namespace, action)
- return unless root_namespace?(namespace) && ACTIONS.include?(action)
-
- action_column = column_name(action)
- where(namespace: namespace).where.not(action_column => nil).exists?
- end
-
- def not_completed?(namespace_id, action)
- return unless ACTIONS.include?(action)
-
- action_column = column_name(action)
- where(namespace_id: namespace_id).where(action_column => nil).exists?
- end
-
- def column_name(action)
- :"#{action}_at"
- end
-
- private
-
- def root_namespace?(namespace)
- namespace && namespace.root?
- end
- end
-
- def number_of_completed_actions
- attributes.extract!(*ACTIONS.map { |action| self.class.column_name(action).to_s }).compact!.size
- end
-
- private
-
- def namespace_is_root_namespace
- return unless namespace
-
- errors.add(:namespace, _('must be a root namespace')) if namespace.has_parent?
- end
-end
diff --git a/app/models/packages/package.rb b/app/models/packages/package.rb
index afd55b4f143..b4c09d99bb0 100644
--- a/app/models/packages/package.rb
+++ b/app/models/packages/package.rb
@@ -22,7 +22,8 @@ class Packages::Package < ApplicationRecord
debian: 9,
rubygems: 10,
helm: 11,
- terraform_module: 12
+ terraform_module: 12,
+ rpm: 13
}
enum status: { default: 0, hidden: 1, processing: 2, error: 3, pending_destruction: 4 }
@@ -43,6 +44,7 @@ class Packages::Package < ApplicationRecord
has_one :nuget_metadatum, inverse_of: :package, class_name: 'Packages::Nuget::Metadatum'
has_one :composer_metadatum, inverse_of: :package, class_name: 'Packages::Composer::Metadatum'
has_one :rubygems_metadatum, inverse_of: :package, class_name: 'Packages::Rubygems::Metadatum'
+ has_one :rpm_metadatum, inverse_of: :package, class_name: 'Packages::Rpm::Metadatum'
has_one :npm_metadatum, inverse_of: :package, class_name: 'Packages::Npm::Metadatum'
has_many :build_infos, inverse_of: :package
has_many :pipelines, through: :build_infos, disable_joins: true
@@ -242,22 +244,23 @@ class Packages::Package < ApplicationRecord
reverse_order_direction = direction == :asc ? desc_order_expression : asc_order_expression
arel_order_classes = ::Gitlab::Pagination::Keyset::ColumnOrderDefinition::AREL_ORDER_CLASSES.invert
- ::Gitlab::Pagination::Keyset::Order.build([
- ::Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: "#{join_table}_#{column_name}",
- column_expression: join_class.arel_table[column_name],
- order_expression: order_direction,
- reversed_order_expression: reverse_order_direction,
- order_direction: direction,
- distinct: false,
- add_to_projections: true
- ),
- ::Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: 'id',
- order_expression: arel_order_classes[direction].new(Packages::Package.arel_table[:id]),
- add_to_projections: true
- )
- ])
+ ::Gitlab::Pagination::Keyset::Order.build(
+ [
+ ::Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: "#{join_table}_#{column_name}",
+ column_expression: join_class.arel_table[column_name],
+ order_expression: order_direction,
+ reversed_order_expression: reverse_order_direction,
+ order_direction: direction,
+ distinct: false,
+ add_to_projections: true
+ ),
+ ::Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'id',
+ order_expression: arel_order_classes[direction].new(Packages::Package.arel_table[:id]),
+ add_to_projections: true
+ )
+ ])
end
def versions
@@ -330,6 +333,12 @@ class Packages::Package < ApplicationRecord
name.gsub(/#{Gitlab::Regex::Packages::PYPI_NORMALIZED_NAME_REGEX_STRING}/o, '-').downcase
end
+ def touch_last_downloaded_at
+ ::Gitlab::Database::LoadBalancing::Session.without_sticky_writes do
+ update_column(:last_downloaded_at, Time.zone.now)
+ end
+ end
+
private
def composer_tag_version?
diff --git a/app/models/packages/policies/group.rb b/app/models/packages/policies/group.rb
new file mode 100644
index 00000000000..66cd361f2ed
--- /dev/null
+++ b/app/models/packages/policies/group.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Packages
+ module Policies
+ class Group
+ attr_accessor :group
+
+ delegate_missing_to :group
+
+ def initialize(group)
+ @group = group
+ end
+ end
+ end
+end
diff --git a/app/models/packages/policies/project.rb b/app/models/packages/policies/project.rb
new file mode 100644
index 00000000000..a5c6703be42
--- /dev/null
+++ b/app/models/packages/policies/project.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+module Packages
+ module Policies
+ class Project
+ attr_accessor :project
+
+ delegate_missing_to :project
+
+ def initialize(project)
+ @project = project
+ end
+ end
+ end
+end
diff --git a/app/models/packages/rpm.rb b/app/models/packages/rpm.rb
new file mode 100644
index 00000000000..fc66e7ec5c8
--- /dev/null
+++ b/app/models/packages/rpm.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+module Packages
+ module Rpm
+ def self.table_name_prefix
+ 'packages_rpm_'
+ end
+ end
+end
diff --git a/app/models/packages/rpm/metadatum.rb b/app/models/packages/rpm/metadatum.rb
new file mode 100644
index 00000000000..07361995a12
--- /dev/null
+++ b/app/models/packages/rpm/metadatum.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+module Packages
+ module Rpm
+ class Metadatum < ApplicationRecord
+ self.primary_key = :package_id
+
+ belongs_to :package, -> { where(package_type: :rpm) }, inverse_of: :rpm_metadatum
+
+ validates :package, presence: true
+
+ validates :epoch,
+ presence: true,
+ numericality: { only_integer: true, greater_than_or_equal_to: 0 }
+
+ validates :release,
+ presence: true,
+ length: { maximum: 128 }
+
+ validates :summary,
+ presence: true,
+ length: { maximum: 1000 }
+
+ validates :description,
+ presence: true,
+ length: { maximum: 5000 }
+
+ validates :arch,
+ presence: true,
+ length: { maximum: 255 }
+
+ validates :license,
+ allow_nil: true,
+ length: { maximum: 1000 }
+
+ validates :url,
+ allow_nil: true,
+ length: { maximum: 1000 }
+
+ validate :rpm_package_type
+
+ private
+
+ def rpm_package_type
+ return if package&.rpm?
+
+ errors.add(:base, _('Package type must be RPM'))
+ end
+ end
+ end
+end
diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb
index 2e25839c47f..16d5492a65e 100644
--- a/app/models/pages_domain.rb
+++ b/app/models/pages_domain.rb
@@ -33,6 +33,7 @@ class PagesDomain < ApplicationRecord
validate :validate_pages_domain
validate :validate_matching_key, if: ->(domain) { domain.certificate.present? || domain.key.present? }
validate :validate_intermediates, if: ->(domain) { domain.certificate.present? && domain.certificate_changed? }
+ validate :validate_custom_domain_count_per_project, on: :create
default_value_for(:auto_ssl_enabled, allows_nil: false) { ::Gitlab::LetsEncrypt.enabled? }
default_value_for :scope, allows_nil: false, value: :project
@@ -57,6 +58,7 @@ class PagesDomain < ApplicationRecord
where(verified_at.eq(nil).or(enabled_until.eq(nil).or(enabled_until.lt(threshold))))
end
+ scope :verified, -> { where.not(verified_at: nil) }
scope :need_auto_ssl_renewal, -> do
enabled_and_not_failed = where(auto_ssl_enabled: true, auto_ssl_failed: false)
@@ -224,6 +226,16 @@ class PagesDomain < ApplicationRecord
self.auto_ssl_failed = false
end
+ def validate_custom_domain_count_per_project
+ return unless project
+
+ unless project.can_create_custom_domains?
+ self.errors.add(
+ :base,
+ _("This project reached the limit of custom domains. (Max %d)") % Gitlab::CurrentSettings.max_pages_custom_domains_per_project)
+ end
+ end
+
private
def pages_deployed?
diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb
index 7e6e366f8da..9ed25c56ed6 100644
--- a/app/models/personal_access_token.rb
+++ b/app/models/personal_access_token.rb
@@ -24,6 +24,8 @@ class PersonalAccessToken < ApplicationRecord
scope :expiring_and_not_notified, ->(date) { where(["revoked = false AND expire_notification_delivered = false AND expires_at >= CURRENT_DATE AND expires_at <= ?", date]) }
scope :expired_today_and_not_notified, -> { where(["revoked = false AND expires_at = CURRENT_DATE AND after_expiry_notification_delivered = false"]) }
scope :inactive, -> { where("revoked = true OR expires_at < CURRENT_DATE") }
+ scope :created_before, -> (date) { where("personal_access_tokens.created_at < :date", date: date) }
+ scope :last_used_before_or_unused, -> (date) { where("personal_access_tokens.created_at < :date AND (last_used_at < :date OR last_used_at IS NULL)", date: date) }
scope :with_impersonation, -> { where(impersonation: true) }
scope :without_impersonation, -> { where(impersonation: false) }
scope :revoked, -> { where(revoked: true) }
diff --git a/app/models/pool_repository.rb b/app/models/pool_repository.rb
index 3461104ae35..f22a63ee980 100644
--- a/app/models/pool_repository.rb
+++ b/app/models/pool_repository.rb
@@ -81,8 +81,8 @@ class PoolRepository < ApplicationRecord
object_pool.link(repository.raw)
end
- def unlink_repository(repository)
- repository.disconnect_alternates
+ def unlink_repository(repository, disconnect: true)
+ repository.disconnect_alternates if disconnect
if member_projects.where.not(id: repository.project.id).exists?
true
diff --git a/app/models/preloaders/environments/deployment_preloader.rb b/app/models/preloaders/environments/deployment_preloader.rb
index 251d1837f19..84aa7bc834f 100644
--- a/app/models/preloaders/environments/deployment_preloader.rb
+++ b/app/models/preloaders/environments/deployment_preloader.rb
@@ -41,11 +41,11 @@ module Preloaders
environment.association(association_name).target = associated_deployment
environment.association(association_name).loaded!
- if associated_deployment
- # `last?` in DeploymentEntity requires this environment to be loaded
- associated_deployment.association(:environment).target = environment
- associated_deployment.association(:environment).loaded!
- end
+ next unless associated_deployment
+
+ # `last?` in DeploymentEntity requires this environment to be loaded
+ associated_deployment.association(:environment).target = environment
+ associated_deployment.association(:environment).loaded!
end
end
end
diff --git a/app/models/preloaders/group_policy_preloader.rb b/app/models/preloaders/group_policy_preloader.rb
index 44030140ce3..23632a9b6c2 100644
--- a/app/models/preloaders/group_policy_preloader.rb
+++ b/app/models/preloaders/group_policy_preloader.rb
@@ -17,4 +17,4 @@ module Preloaders
end
end
-Preloaders::GroupPolicyPreloader.prepend_mod_with('Preloaders::GroupPolicyPreloader')
+Preloaders::GroupPolicyPreloader.prepend_mod
diff --git a/app/models/preloaders/project_policy_preloader.rb b/app/models/preloaders/project_policy_preloader.rb
new file mode 100644
index 00000000000..fe9db3464c7
--- /dev/null
+++ b/app/models/preloaders/project_policy_preloader.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Preloaders
+ class ProjectPolicyPreloader
+ def initialize(projects, current_user)
+ @projects = projects
+ @current_user = current_user
+ end
+
+ def execute
+ return if projects.is_a?(ActiveRecord::NullRelation)
+
+ ActiveRecord::Associations::Preloader.new.preload(projects, { group: :route, namespace: :owner })
+ ::Preloaders::UserMaxAccessLevelInProjectsPreloader.new(projects, current_user).execute
+ end
+
+ private
+
+ attr_reader :projects, :current_user
+ end
+end
+
+Preloaders::ProjectPolicyPreloader.prepend_mod
diff --git a/app/models/preloaders/project_root_ancestor_preloader.rb b/app/models/preloaders/project_root_ancestor_preloader.rb
new file mode 100644
index 00000000000..8d04e71774c
--- /dev/null
+++ b/app/models/preloaders/project_root_ancestor_preloader.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Preloaders
+ class ProjectRootAncestorPreloader
+ def initialize(projects, namespace_sti_name = :namespace, root_ancestor_preloads = [])
+ @projects = projects
+ @namespace_sti_name = namespace_sti_name
+ @root_ancestor_preloads = root_ancestor_preloads
+ end
+
+ def execute
+ return if @projects.is_a?(ActiveRecord::NullRelation)
+ return unless ::Feature.enabled?(:use_traversal_ids)
+
+ root_query = Namespace.joins("INNER JOIN (#{join_sql}) as root_query ON root_query.root_id = namespaces.id")
+ .select('namespaces.*, root_query.id as source_id')
+
+ root_query = root_query.preload(*@root_ancestor_preloads) if @root_ancestor_preloads.any?
+
+ root_ancestors_by_id = root_query.group_by(&:source_id)
+
+ ActiveRecord::Associations::Preloader.new.preload(@projects, :namespace)
+ @projects.each do |project|
+ project.namespace.root_ancestor = root_ancestors_by_id[project.id]&.first
+ end
+ end
+
+ private
+
+ def join_sql
+ @projects
+ .joins(@namespace_sti_name)
+ .select('projects.id, namespaces.traversal_ids[1] as root_id')
+ .to_sql
+ end
+ end
+end
diff --git a/app/models/preloaders/users_max_access_level_in_projects_preloader.rb b/app/models/preloaders/users_max_access_level_in_projects_preloader.rb
index 99a31a620c5..f32184f168d 100644
--- a/app/models/preloaders/users_max_access_level_in_projects_preloader.rb
+++ b/app/models/preloaders/users_max_access_level_in_projects_preloader.rb
@@ -51,4 +51,4 @@ module Preloaders
end
end
-# Preloaders::UsersMaxAccessLevelInProjectsPreloader.prepend_mod
+Preloaders::UsersMaxAccessLevelInProjectsPreloader.prepend_mod
diff --git a/app/models/project.rb b/app/models/project.rb
index 0c49cc24a8d..c5fad189f87 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -46,13 +46,9 @@ class Project < ApplicationRecord
extend Gitlab::ConfigHelper
- ignore_columns :container_registry_enabled, remove_after: '2021-09-22', remove_with: '14.4'
-
BoardLimitExceeded = Class.new(StandardError)
ExportLimitExceeded = Class.new(StandardError)
- ignore_columns :mirror_last_update_at, :mirror_last_successful_update_at, remove_after: '2021-09-22', remove_with: '14.4'
- ignore_columns :pull_mirror_branch_prefix, remove_after: '2021-09-22', remove_with: '14.4'
ignore_columns :build_coverage_regex, remove_after: '2022-10-22', remove_with: '15.5'
STATISTICS_ATTRIBUTE = 'repositories_count'
@@ -123,6 +119,7 @@ class Project < ApplicationRecord
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?
@@ -453,7 +450,7 @@ class Project < ApplicationRecord
:metrics_dashboard_access_level, :analytics_access_level,
:operations_access_level, :security_and_compliance_access_level,
:container_registry_access_level, :environments_access_level, :feature_flags_access_level,
- :releases_access_level,
+ :monitor_access_level, :releases_access_level,
to: :project_feature, allow_nil: true
delegate :show_default_award_emojis, :show_default_award_emojis=,
@@ -461,6 +458,9 @@ class Project < ApplicationRecord
:warn_about_potentially_unwanted_characters, :warn_about_potentially_unwanted_characters=,
to: :project_setting, allow_nil: true
+ delegate :show_diff_preview_in_email, :show_diff_preview_in_email=, :show_diff_preview_in_email?,
+ to: :project_setting
+
delegate :squash_always?, :squash_never?, :squash_enabled_by_default?, :squash_readonly?, to: :project_setting
delegate :squash_option, :squash_option=, to: :project_setting
delegate :mr_default_target_self, :mr_default_target_self=, to: :project_setting
@@ -565,26 +565,29 @@ class Project < ApplicationRecord
scope :projects_order_id_desc, -> { reorder(self.arel_table['id'].desc) }
scope :sorted_by_similarity_desc, -> (search, include_in_select: false) do
- order_expression = Gitlab::Database::SimilarityScore.build_expression(search: search, rules: [
- { column: arel_table["path"], multiplier: 1 },
- { column: arel_table["name"], multiplier: 0.7 },
- { column: arel_table["description"], multiplier: 0.2 }
- ])
-
- order = Gitlab::Pagination::Keyset::Order.build([
- Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: 'similarity',
- column_expression: order_expression,
- order_expression: order_expression.desc,
- order_direction: :desc,
- distinct: false,
- add_to_projections: true
- ),
- Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: 'id',
- order_expression: Project.arel_table[:id].desc
- )
- ])
+ order_expression = Gitlab::Database::SimilarityScore.build_expression(
+ search: search,
+ rules: [
+ { column: arel_table["path"], multiplier: 1 },
+ { column: arel_table["name"], multiplier: 0.7 },
+ { column: arel_table["description"], multiplier: 0.2 }
+ ])
+
+ order = Gitlab::Pagination::Keyset::Order.build(
+ [
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'similarity',
+ column_expression: order_expression,
+ order_expression: order_expression.desc,
+ order_direction: :desc,
+ distinct: false,
+ add_to_projections: true
+ ),
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'id',
+ order_expression: Project.arel_table[:id].desc
+ )
+ ])
order.apply_cursor_conditions(reorder(order))
end
@@ -611,7 +614,7 @@ class Project < ApplicationRecord
scope :include_integration, -> (integration_association_name) { includes(integration_association_name) }
scope :with_integration, -> (integration_class) { joins(:integrations).merge(integration_class.all) }
scope :with_active_integration, -> (integration_class) { with_integration(integration_class).merge(integration_class.active) }
- scope :with_shared_runners, -> { where(shared_runners_enabled: true) }
+ scope :with_shared_runners_enabled, -> { where(shared_runners_enabled: true) }
scope :inside_path, ->(path) do
# We need routes alias rs for JOIN so it does not conflict with
# includes(:route) which we use in ProjectsFinder.
@@ -1163,7 +1166,7 @@ class Project < ApplicationRecord
latest_pipeline = ci_pipelines.latest_successful_for_ref(ref)
return unless latest_pipeline
- latest_pipeline.build_with_artifacts_in_self_and_descendants(job_name)
+ latest_pipeline.build_with_artifacts_in_self_and_project_descendants(job_name)
end
def latest_successful_build_for_sha(job_name, sha)
@@ -1172,7 +1175,7 @@ class Project < ApplicationRecord
latest_pipeline = ci_pipelines.latest_successful_for_sha(sha)
return unless latest_pipeline
- latest_pipeline.build_with_artifacts_in_self_and_descendants(job_name)
+ latest_pipeline.build_with_artifacts_in_self_and_project_descendants(job_name)
end
def latest_successful_build_for_ref!(job_name, ref = default_branch)
@@ -1564,9 +1567,7 @@ class Project < ApplicationRecord
end
def disabled_integrations
- disabled_integrations = []
- disabled_integrations << 'shimo' unless Feature.enabled?(:shimo_integration, self)
- disabled_integrations
+ []
end
def find_or_initialize_integration(name)
@@ -2369,28 +2370,6 @@ class Project < ApplicationRecord
.first
end
- def ci_variables_for(ref:, environment: nil)
- cache_key = "ci_variables_for:project:#{self&.id}:ref:#{ref}:environment:#{environment}"
-
- ::Gitlab::SafeRequestStore.fetch(cache_key) do
- uncached_ci_variables_for(ref: ref, environment: environment)
- end
- end
-
- def uncached_ci_variables_for(ref:, environment: nil)
- result = if protected_for?(ref)
- variables
- else
- variables.unprotected
- end
-
- if environment
- result.on_environment(environment)
- else
- result.where(environment_scope: '*')
- end
- end
-
def protected_for?(ref)
raise Repository::AmbiguousRefError if repository.ambiguous_ref?(ref)
@@ -2582,10 +2561,7 @@ class Project < ApplicationRecord
def badges
return project_badges unless group
- Badge.from_union([
- project_badges,
- GroupBadge.where(group: group.self_and_ancestors)
- ])
+ Badge.from_union([project_badges, GroupBadge.where(group: group.self_and_ancestors)])
end
def merge_requests_allowing_push_to_user(user)
@@ -2631,11 +2607,7 @@ class Project < ApplicationRecord
def gitlab_deploy_token
strong_memoize(:gitlab_deploy_token) do
- if Feature.enabled?(:ci_variable_for_group_gitlab_deploy_token, self)
- deploy_tokens.gitlab_deploy_token || group&.gitlab_deploy_token
- else
- deploy_tokens.gitlab_deploy_token
- end
+ deploy_tokens.gitlab_deploy_token || group&.gitlab_deploy_token
end
end
@@ -2693,7 +2665,12 @@ class Project < ApplicationRecord
end
def leave_pool_repository
- pool_repository&.unlink_repository(repository) && update_column(:pool_repository_id, nil)
+ return if pool_repository.blank?
+
+ # Disconnecting the repository can be expensive, so let's skip it if
+ # this repository is being deleted anyway.
+ pool_repository.unlink_repository(repository, disconnect: !pending_delete?)
+ update_column(:pool_repository_id, nil)
end
def link_pool_repository
@@ -3045,10 +3022,24 @@ class Project < ApplicationRecord
licensed_feature_available?(:security_training)
end
+ def packages_policy_subject
+ if Feature.enabled?(:read_package_policy_rule, group)
+ ::Packages::Policies::Project.new(self)
+ else
+ self
+ end
+ end
+
def destroy_deployment_by_id(deployment_id)
deployments.where(id: deployment_id).fast_destroy_all
end
+ def can_create_custom_domains?
+ return true if Gitlab::CurrentSettings.max_pages_custom_domains_per_project == 0
+
+ pages_domains.count < Gitlab::CurrentSettings.max_pages_custom_domains_per_project
+ end
+
private
# overridden in EE
@@ -3300,6 +3291,10 @@ class Project < ApplicationRecord
end
end
+ def remove_leading_spaces_on_name
+ name&.lstrip!
+ end
+
def set_package_registry_access_level
return if !project_feature || project_feature.package_registry_access_level_changed?
diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb
index 8623e477c06..dad8aaf0625 100644
--- a/app/models/project_feature.rb
+++ b/app/models/project_feature.rb
@@ -17,6 +17,7 @@ class ProjectFeature < ApplicationRecord
pages
metrics_dashboard
analytics
+ monitor
operations
security_and_compliance
container_registry
diff --git a/app/models/project_setting.rb b/app/models/project_setting.rb
index 59d2e3deb4f..f5c346eda30 100644
--- a/app/models/project_setting.rb
+++ b/app/models/project_setting.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class ProjectSetting < ApplicationRecord
+ include ::Gitlab::Utils::StrongMemoize
+
ALLOWED_TARGET_PLATFORMS = %w(ios osx tvos watchos android).freeze
belongs_to :project, inverse_of: :project_setting
@@ -47,6 +49,15 @@ class ProjectSetting < ApplicationRecord
end
end
+ def show_diff_preview_in_email?
+ if project.group
+ super && project.group&.show_diff_preview_in_email?
+ else
+ !!super
+ end
+ end
+ strong_memoize_attr :show_diff_preview_in_email
+
private
def validates_mr_default_target_self
diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb
index a0af1b47d01..a91e0291438 100644
--- a/app/models/project_statistics.rb
+++ b/app/models/project_statistics.rb
@@ -11,9 +11,10 @@ class ProjectStatistics < ApplicationRecord
default_value_for :snippets_size, 0
counter_attribute :build_artifacts_size
- counter_attribute :storage_size
counter_attribute_after_flush do |project_statistic|
+ project_statistic.refresh_storage_size!
+
Namespaces::ScheduleAggregationWorker.perform_async(project_statistic.namespace_id)
end
@@ -21,7 +22,6 @@ class ProjectStatistics < ApplicationRecord
COLUMNS_TO_REFRESH = [:repository_size, :wiki_size, :lfs_objects_size, :commit_count, :snippets_size, :uploads_size, :container_registry_size].freeze
INCREMENTABLE_COLUMNS = {
- build_artifacts_size: %i[storage_size],
packages_size: %i[storage_size],
pipeline_artifacts_size: %i[storage_size],
snippets_size: %i[storage_size]
@@ -109,21 +109,25 @@ class ProjectStatistics < ApplicationRecord
self.storage_size = storage_size
end
- # Since this incremental update method does not call update_storage_size above,
- # we have to update the storage_size here as additional column.
- # Additional columns are updated depending on key => [columns], which allows
- # to update statistics which are and also those which aren't included in storage_size
- # or any other additional summary column in the future.
+ def refresh_storage_size!
+ update_storage_size
+ save!
+ end
+
+ # Since this incremental update method does not call update_storage_size above through before_save,
+ # 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
+ #
+ # 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_COLUMNS.key?(key)
+ raise ArgumentError, "Cannot increment attribute: #{key}" unless incrementable_attribute?(key)
return if amount == 0
project.statistics.try do |project_statistics|
- if project_statistics.counter_attribute_enabled?(key)
- statistics_to_increment = [key] + INCREMENTABLE_COLUMNS[key].to_a
- statistics_to_increment.each do |statistic|
- project_statistics.delayed_increment_counter(statistic, amount)
- end
+ if counter_attribute_enabled?(key)
+ project_statistics.delayed_increment_counter(key, amount)
else
legacy_increment_statistic(project, key, amount)
end
@@ -149,6 +153,10 @@ class ProjectStatistics < ApplicationRecord
update_all(updates.join(', '))
end
+ def self.incrementable_attribute?(key)
+ INCREMENTABLE_COLUMNS.key?(key) || counter_attribute_enabled?(key)
+ end
+
private
def schedule_namespace_aggregation_worker
diff --git a/app/models/projects/build_artifacts_size_refresh.rb b/app/models/projects/build_artifacts_size_refresh.rb
index dee4afdefa6..e66e1d5b42f 100644
--- a/app/models/projects/build_artifacts_size_refresh.rb
+++ b/app/models/projects/build_artifacts_size_refresh.rb
@@ -2,6 +2,7 @@
module Projects
class BuildArtifactsSizeRefresh < ApplicationRecord
+ include AfterCommitQueue
include BulkInsertSafe
STALE_WINDOW = 2.hours
@@ -52,6 +53,8 @@ module Projects
scope :remaining, -> { with_state(:created, :pending).or(stale) }
scope :processing_queue, -> { remaining.order(state: :desc) }
+ after_destroy :schedule_namespace_aggregation_worker
+
def self.enqueue_refresh(projects)
now = Time.zone.now
@@ -93,5 +96,13 @@ module Projects
def started?
!created?
end
+
+ private
+
+ def schedule_namespace_aggregation_worker
+ run_after_commit do
+ Namespaces::ScheduleAggregationWorker.perform_async(project.namespace_id)
+ end
+ end
end
end
diff --git a/app/models/projects/topic.rb b/app/models/projects/topic.rb
index b0f138714a0..3155eede2bd 100644
--- a/app/models/projects/topic.rb
+++ b/app/models/projects/topic.rb
@@ -18,9 +18,11 @@ module Projects
scope :without_assigned_projects, -> { where(total_projects_count: 0) }
scope :order_by_non_private_projects_count, -> { order(non_private_projects_count: :desc).order(id: :asc) }
scope :reorder_by_similarity, -> (search) do
- order_expression = Gitlab::Database::SimilarityScore.build_expression(search: search, rules: [
- { column: arel_table['name'] }
- ])
+ order_expression = Gitlab::Database::SimilarityScore.build_expression(
+ search: search,
+ rules: [
+ { column: arel_table['name'] }
+ ])
reorder(order_expression.desc, arel_table['non_private_projects_count'].desc, arel_table['id'])
end
diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb
index 76c277e4b86..b3a918d8952 100644
--- a/app/models/protected_branch.rb
+++ b/app/models/protected_branch.rb
@@ -25,10 +25,12 @@ class ProtectedBranch < ApplicationRecord
end
# Check if branch name is marked as protected in the system
- def self.protected?(project, ref_name, dry_run: true)
+ def self.protected?(project, ref_name)
return true if project.empty_repo? && project.default_branch_protected?
return false if ref_name.blank?
+ dry_run = Feature.disabled?(:rely_on_protected_branches_cache, project)
+
new_cache_result = new_cache(project, ref_name, dry_run: dry_run)
return new_cache_result unless new_cache_result.nil?
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 26c3b01a46e..ee1bea0e8d2 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -194,6 +194,18 @@ class Repository
CommitCollection.new(container, commits, ref)
end
+ def list_commits_by(query, ref, author: nil, before: nil, after: nil, limit: 1000)
+ return [] unless exists?
+ return [] unless has_visible_content?
+ return [] unless query.present? && ref.present?
+
+ commits = raw_repository.list_commits_by(
+ query, ref, author: author, before: before, after: after, limit: limit).map do |c|
+ commit(c)
+ end
+ CommitCollection.new(container, commits, ref)
+ end
+
def find_branch(name)
raw_repository.find_branch(name)
end
@@ -779,8 +791,8 @@ class Repository
raw_repository.branch_names_contains_sha(sha)
end
- def tag_names_contains(sha)
- raw_repository.tag_names_contains_sha(sha)
+ def tag_names_contains(sha, limit: 0)
+ raw_repository.tag_names_contains_sha(sha, limit: limit)
end
def local_branches
@@ -796,7 +808,7 @@ class Repository
def create_dir(user, path, **options)
options[:actions] = [{ action: :create_dir, file_path: path }]
- multi_action(user, **options)
+ commit_files(user, **options)
end
def create_file(user, path, content, **options)
@@ -808,7 +820,7 @@ class Repository
options[:actions].push({ action: :chmod, file_path: path, execute_filemode: execute_filemode })
end
- multi_action(user, **options)
+ commit_files(user, **options)
end
def update_file(user, path, content, **options)
@@ -823,13 +835,13 @@ class Repository
options[:actions].push({ action: :chmod, file_path: path, execute_filemode: execute_filemode })
end
- multi_action(user, **options)
+ commit_files(user, **options)
end
def delete_file(user, path, **options)
options[:actions] = [{ action: :delete, file_path: path }]
- multi_action(user, **options)
+ commit_files(user, **options)
end
def with_cache_hooks
@@ -843,14 +855,14 @@ class Repository
result.newrev
end
- def multi_action(user, **options)
+ def commit_files(user, **options)
start_project = options.delete(:start_project)
if start_project
options[:start_repository] = start_project.repository.raw_repository
end
- with_cache_hooks { raw.multi_action(user, **options) }
+ with_cache_hooks { raw.commit_files(user, **options) }
end
def merge(user, source_sha, merge_request, message)
diff --git a/app/models/resource_state_event.rb b/app/models/resource_state_event.rb
index 689a9d8a8ae..6ebb9d5f176 100644
--- a/app/models/resource_state_event.rb
+++ b/app/models/resource_state_event.rb
@@ -3,8 +3,9 @@
class ResourceStateEvent < ResourceEvent
include IssueResourceEvent
include MergeRequestResourceEvent
+ include Importable
- validate :exactly_one_issuable
+ validate :exactly_one_issuable, unless: :importing?
belongs_to :source_merge_request, class_name: 'MergeRequest', foreign_key: :source_merge_request_id
@@ -32,9 +33,9 @@ class ResourceStateEvent < ResourceEvent
case state
when 'closed'
- issue_usage_counter.track_issue_closed_action(author: user)
+ issue_usage_counter.track_issue_closed_action(author: user, project: issue.project)
when 'reopened'
- issue_usage_counter.track_issue_reopened_action(author: user)
+ issue_usage_counter.track_issue_reopened_action(author: user, project: issue.project)
else
# no-op, nothing to do, not a state we're tracking
end
diff --git a/app/models/resource_timebox_event.rb b/app/models/resource_timebox_event.rb
index db87ff09159..26bf2a225d4 100644
--- a/app/models/resource_timebox_event.rb
+++ b/app/models/resource_timebox_event.rb
@@ -5,8 +5,9 @@ class ResourceTimeboxEvent < ResourceEvent
include IssueResourceEvent
include MergeRequestResourceEvent
+ include Importable
- validate :exactly_one_issuable
+ validate :exactly_one_issuable, unless: :importing?
enum action: {
add: 1,
@@ -34,7 +35,8 @@ class ResourceTimeboxEvent < ResourceEvent
case self
when ResourceMilestoneEvent
- Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_milestone_changed_action(author: user)
+ Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_milestone_changed_action(author: user,
+ project: issue.project)
else
# no-op
end
diff --git a/app/models/route.rb b/app/models/route.rb
index 2f6b0a8e8f1..f2fe1664f9e 100644
--- a/app/models/route.rb
+++ b/app/models/route.rb
@@ -39,17 +39,17 @@ class Route < ApplicationRecord
attributes[:name] = route.name.sub(name_before_last_save, name)
end
- if attributes.present?
- old_path = route.path
+ next if attributes.empty?
- # Callbacks must be run manually
- route.update_columns(attributes.merge(updated_at: Time.current))
+ old_path = route.path
- # We are not calling route.delete_conflicting_redirects here, in hopes
- # of avoiding deadlocks. The parent (self, in this method) already
- # called it, which deletes conflicts for all descendants.
- route.create_redirect(old_path) if attributes[:path]
- end
+ # Callbacks must be run manually
+ route.update_columns(attributes.merge(updated_at: Time.current))
+
+ # We are not calling route.delete_conflicting_redirects here, in hopes
+ # of avoiding deadlocks. The parent (self, in this method) already
+ # called it, which deletes conflicts for all descendants.
+ route.create_redirect(old_path) if attributes[:path]
end
end
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index 943d09d983b..9b7c37dd23e 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -84,7 +84,7 @@ class Snippet < ApplicationRecord
participant :notes_with_associations
attr_spammable :title, spam_title: true
- attr_spammable :content, spam_description: true
+ attr_spammable :description, spam_description: true
attr_encrypted :secret_token,
key: Settings.attr_encrypted_db_key_base_truncated,
@@ -269,13 +269,7 @@ class Snippet < ApplicationRecord
def check_for_spam?(user:)
visibility_level_changed?(to: Snippet::PUBLIC) ||
- (public? && (title_changed? || content_changed?))
- end
-
- # snippets are the biggest sources of spam
- override :allow_possible_spam?
- def allow_possible_spam?
- false
+ (public? && (title_changed? || description_changed?))
end
def spammable_entity_type
diff --git a/app/models/snippet_repository.rb b/app/models/snippet_repository.rb
index 5ac159d9615..a959ad4d548 100644
--- a/app/models/snippet_repository.rb
+++ b/app/models/snippet_repository.rb
@@ -31,7 +31,7 @@ class SnippetRepository < ApplicationRecord
options[:actions] = transform_file_entries(files)
- capture_git_error { repository.multi_action(user, **options) }
+ capture_git_error { repository.commit_files(user, **options) }
ensure
Gitlab::ExclusiveLease.cancel(lease_key, uuid)
end
diff --git a/app/models/system_note_metadata.rb b/app/models/system_note_metadata.rb
index cc389dbe3f4..4e86036952b 100644
--- a/app/models/system_note_metadata.rb
+++ b/app/models/system_note_metadata.rb
@@ -25,6 +25,7 @@ class SystemNoteMetadata < ApplicationRecord
tag due_date start_date_or_due_date pinned_embed cherry_pick health_status approved unapproved
status alert_issue_added relate unrelate new_alert_added severity
attention_requested attention_request_removed contact timeline_event
+ issue_type relate_to_child unrelate_from_child relate_to_parent unrelate_from_parent
].freeze
validates :note, presence: true, unless: :importing?
diff --git a/app/models/todo.rb b/app/models/todo.rb
index d165e60e4c3..634fa9e7eda 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -96,10 +96,11 @@ class Todo < ApplicationRecord
def for_group_ids_and_descendants(group_ids)
groups = Group.groups_including_descendants_by(group_ids)
- from_union([
- for_project(Project.for_group(groups)),
- for_group(groups)
- ])
+ from_union(
+ [
+ for_project(Project.for_group(groups)),
+ for_group(groups)
+ ])
end
# Returns `true` if the current user has any todos for the given target with the optional given state.
diff --git a/app/models/user.rb b/app/models/user.rb
index afee2d70844..8825c18ea48 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -92,7 +92,6 @@ class User < ApplicationRecord
include ForcedEmailConfirmation
include RequireEmailVerification
- MINIMUM_INACTIVE_DAYS = 90
MINIMUM_DAYS_CREATED = 7
# Override Devise::Models::Trackable#update_tracked_fields!
@@ -262,6 +261,7 @@ class User < ApplicationRecord
presence: true,
numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: Gitlab::Database::MAX_INT_VALUE }
validates :username, presence: true
+ validate :check_password_weakness, if: :encrypted_password_changed?
validates :namespace, presence: true
validate :namespace_move_dir_allowed, if: :username_changed?
@@ -488,7 +488,7 @@ class User < ApplicationRecord
scope :order_oldest_sign_in, -> { reorder(arel_table[:current_sign_in_at].asc.nulls_last) }
scope :order_recent_last_activity, -> { reorder(arel_table[:last_activity_on].desc.nulls_last, arel_table[:id].asc) }
scope :order_oldest_last_activity, -> { reorder(arel_table[:last_activity_on].asc.nulls_first, arel_table[:id].desc) }
- scope :dormant, -> { with_state(:active).human_or_service_user.where('last_activity_on <= ?', MINIMUM_INACTIVE_DAYS.day.ago.to_date) }
+ scope :dormant, -> { with_state(:active).human_or_service_user.where('last_activity_on <= ?', Gitlab::CurrentSettings.deactivate_dormant_users_period.day.ago.to_date) }
scope :with_no_activity, -> { with_state(:active).human_or_service_user.where(last_activity_on: nil).where('created_at <= ?', MINIMUM_DAYS_CREATED.day.ago.to_date) }
scope :by_provider_and_extern_uid, ->(provider, extern_uid) { joins(:identities).merge(Identity.with_extern_uid(provider, extern_uid)) }
scope :by_ids_or_usernames, -> (ids, usernames) { where(username: usernames).or(where(id: ids)) }
@@ -697,28 +697,29 @@ class User < ApplicationRecord
scope = options[:with_private_emails] ? with_primary_or_secondary_email(query) : with_public_email(query)
scope = scope.or(search_by_name_or_username(query, use_minimum_char_limit: options[:use_minimum_char_limit]))
- order = Gitlab::Pagination::Keyset::Order.build([
- Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: 'users_match_priority',
- order_expression: sanitized_order_sql.asc,
- add_to_projections: true,
- distinct: false
- ),
- Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: 'users_name',
- order_expression: arel_table[:name].asc,
- add_to_projections: true,
- nullable: :not_nullable,
- distinct: false
- ),
- Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: 'users_id',
- order_expression: arel_table[:id].asc,
- add_to_projections: true,
- nullable: :not_nullable,
- distinct: true
- )
- ])
+ order = Gitlab::Pagination::Keyset::Order.build(
+ [
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'users_match_priority',
+ order_expression: sanitized_order_sql.asc,
+ add_to_projections: true,
+ distinct: false
+ ),
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'users_name',
+ order_expression: arel_table[:name].asc,
+ add_to_projections: true,
+ nullable: :not_nullable,
+ distinct: false
+ ),
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'users_id',
+ order_expression: arel_table[:id].asc,
+ add_to_projections: true,
+ nullable: :not_nullable,
+ distinct: true
+ )
+ ])
scope.reorder(order)
end
@@ -1358,10 +1359,11 @@ class User < ApplicationRecord
end
def accessible_deploy_keys
- DeployKey.from_union([
- DeployKey.where(id: project_deploy_keys.select(:deploy_key_id)),
- DeployKey.are_public
- ])
+ DeployKey.from_union(
+ [
+ DeployKey.where(id: project_deploy_keys.select(:deploy_key_id)),
+ DeployKey.are_public
+ ])
end
def created_by
@@ -1662,10 +1664,11 @@ class User < ApplicationRecord
strong_memoize(:forkable_namespaces) do
personal_namespace = Namespace.where(id: namespace_id)
- Namespace.from_union([
- manageable_groups(include_groups_with_developer_maintainer_access: true),
- personal_namespace
- ])
+ Namespace.from_union(
+ [
+ manageable_groups(include_groups_with_developer_maintainer_access: true),
+ personal_namespace
+ ])
end
end
@@ -2072,6 +2075,7 @@ class User < ApplicationRecord
callout_dismissed?(callout, ignore_dismissal_earlier_than)
end
+ # Deprecated: do not use. See: https://gitlab.com/gitlab-org/gitlab/-/issues/371017
def dismissed_callout_for_namespace?(feature_name:, namespace:, ignore_dismissal_earlier_than: nil)
source_feature_name = "#{feature_name}_#{namespace.id}"
callout = namespace_callouts_by_feature_name[source_feature_name]
@@ -2151,10 +2155,6 @@ class User < ApplicationRecord
end
end
- def mr_attention_requests_enabled?
- Feature.enabled?(:mr_attention_requests, self)
- end
-
def account_age_in_days
(Date.current - created_at.to_date).to_i
end
@@ -2247,10 +2247,11 @@ class User < ApplicationRecord
end
def authorized_groups_without_shared_membership
- Group.from_union([
- groups.select(*Namespace.cached_column_list),
- authorized_projects.joins(:namespace).select(*Namespace.cached_column_list)
- ])
+ Group.from_union(
+ [
+ groups.select(*Namespace.cached_column_list),
+ authorized_projects.joins(:namespace).select(*Namespace.cached_column_list)
+ ])
end
def authorized_groups_with_shared_membership
@@ -2260,10 +2261,10 @@ class User < ApplicationRecord
Group
.with(cte.to_arel)
.from_union([
- Group.from(cte_alias),
- Group.joins(:shared_with_group_links)
- .where(group_group_links: { shared_with_group_id: Group.from(cte_alias) })
- ])
+ Group.from(cte_alias),
+ Group.joins(:shared_with_group_links)
+ .where(group_group_links: { shared_with_group_id: Group.from(cte_alias) })
+ ])
end
def default_private_profile_to_false
@@ -2314,6 +2315,14 @@ class User < ApplicationRecord
errors.add(:username, _('ending with a reserved file extension is not allowed.'))
end
+ def check_password_weakness
+ if Feature.enabled?(:block_weak_passwords) &&
+ password.present? &&
+ Security::WeakPasswords.weak_for_user?(password, self)
+ errors.add(:password, _('must not contain commonly used combinations of words and letters'))
+ end
+ end
+
def groups_with_developer_maintainer_project_access
project_creation_levels = [::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS]
@@ -2325,7 +2334,7 @@ class User < ApplicationRecord
end
def no_recent_activity?
- last_active_at.to_i <= MINIMUM_INACTIVE_DAYS.days.ago.to_i
+ last_active_at.to_i <= Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_i
end
def update_highest_role?
diff --git a/app/models/user_status.rb b/app/models/user_status.rb
index dee976a4497..0c66f465356 100644
--- a/app/models/user_status.rb
+++ b/app/models/user_status.rb
@@ -29,6 +29,10 @@ class UserStatus < ApplicationRecord
cache_markdown_field :message, pipeline: :emoji
+ def clear_status_after
+ clear_status_at
+ end
+
def clear_status_after=(value)
self.clear_status_at = CLEAR_STATUS_QUICK_OPTIONS[value]&.from_now
end
diff --git a/app/models/users/callout.rb b/app/models/users/callout.rb
index 7b5c7fef7ba..03841ee48fa 100644
--- a/app/models/users/callout.rb
+++ b/app/models/users/callout.rb
@@ -43,12 +43,11 @@ module Users
verification_reminder: 40, # EE-only
ci_deprecation_warning_for_types_keyword: 41,
security_training_feature_promotion: 42, # EE-only
- storage_enforcement_banner_first_enforcement_threshold: 43,
- storage_enforcement_banner_second_enforcement_threshold: 44,
- storage_enforcement_banner_third_enforcement_threshold: 45,
- storage_enforcement_banner_fourth_enforcement_threshold: 46,
- attention_requests_top_nav: 47,
- attention_requests_side_nav: 48,
+ storage_enforcement_banner_first_enforcement_threshold: 43, # EE-only
+ storage_enforcement_banner_second_enforcement_threshold: 44, # EE-only
+ storage_enforcement_banner_third_enforcement_threshold: 45, # EE-only
+ storage_enforcement_banner_fourth_enforcement_threshold: 46, # EE-only
+ # 47 and 48 were removed with https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95446
# 49 was removed with https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91533
# because the banner was no longer relevant.
# Records will be migrated with https://gitlab.com/gitlab-org/gitlab/-/issues/367293
@@ -61,7 +60,8 @@ module Users
namespace_storage_limit_banner_warning_threshold: 56, # EE-only
namespace_storage_limit_banner_alert_threshold: 57, # EE-only
namespace_storage_limit_banner_error_threshold: 58, # EE-only
- project_quality_summary_feedback: 59 # EE-only
+ project_quality_summary_feedback: 59, # EE-only
+ merge_request_settings_moved_callout: 60
}
validates :feature_name,
diff --git a/app/models/users/credit_card_validation.rb b/app/models/users/credit_card_validation.rb
index 998a5deb0fd..272f31aa9ce 100644
--- a/app/models/users/credit_card_validation.rb
+++ b/app/models/users/credit_card_validation.rb
@@ -21,5 +21,11 @@ module Users
network: network
).order(credit_card_validated_at: :desc).includes(:user)
end
+
+ def similar_holder_names_count
+ return 0 unless holder_name
+
+ self.class.where('lower(holder_name) = lower(:value)', value: holder_name).count
+ end
end
end
diff --git a/app/models/users/ghost_user_migration.rb b/app/models/users/ghost_user_migration.rb
new file mode 100644
index 00000000000..1d93498e88b
--- /dev/null
+++ b/app/models/users/ghost_user_migration.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Users
+ class GhostUserMigration < ApplicationRecord
+ self.table_name = 'ghost_user_migrations'
+
+ belongs_to :user
+ belongs_to :initiator_user, class_name: 'User'
+
+ validates :user_id, presence: true
+ end
+end
diff --git a/app/models/users/group_callout.rb b/app/models/users/group_callout.rb
index 70498ae83e0..3e3e424e9c9 100644
--- a/app/models/users/group_callout.rb
+++ b/app/models/users/group_callout.rb
@@ -11,10 +11,10 @@ module Users
enum feature_name: {
invite_members_banner: 1,
approaching_seat_count_threshold: 2, # EE-only
- storage_enforcement_banner_first_enforcement_threshold: 3,
- storage_enforcement_banner_second_enforcement_threshold: 4,
- storage_enforcement_banner_third_enforcement_threshold: 5,
- storage_enforcement_banner_fourth_enforcement_threshold: 6,
+ storage_enforcement_banner_first_enforcement_threshold: 3, # EE-only
+ storage_enforcement_banner_second_enforcement_threshold: 4, # EE-only
+ storage_enforcement_banner_third_enforcement_threshold: 5, # EE-only
+ storage_enforcement_banner_fourth_enforcement_threshold: 6, # EE-only
preview_user_over_limit_free_plan_alert: 7, # EE-only
user_reached_limit_free_plan_alert: 8, # EE-only
free_group_limited_alert: 9, # EE-only
diff --git a/app/models/users/namespace_callout.rb b/app/models/users/namespace_callout.rb
index a20a196a4ef..4e655a96b57 100644
--- a/app/models/users/namespace_callout.rb
+++ b/app/models/users/namespace_callout.rb
@@ -11,10 +11,10 @@ module Users
enum feature_name: {
invite_members_banner: 1,
approaching_seat_count_threshold: 2, # EE-only
- storage_enforcement_banner_first_enforcement_threshold: 3,
- storage_enforcement_banner_second_enforcement_threshold: 4,
- storage_enforcement_banner_third_enforcement_threshold: 5,
- storage_enforcement_banner_fourth_enforcement_threshold: 6,
+ storage_enforcement_banner_first_enforcement_threshold: 3, # EE-only
+ storage_enforcement_banner_second_enforcement_threshold: 4, # EE-only
+ storage_enforcement_banner_third_enforcement_threshold: 5, # EE-only
+ storage_enforcement_banner_fourth_enforcement_threshold: 6, # EE-only
preview_user_over_limit_free_plan_alert: 7, # EE-only
user_reached_limit_free_plan_alert: 8, # EE-only
web_hook_disabled: 9
diff --git a/app/models/users/project_callout.rb b/app/models/users/project_callout.rb
index ddc5f8fb4de..98dacbe394a 100644
--- a/app/models/users/project_callout.rb
+++ b/app/models/users/project_callout.rb
@@ -9,7 +9,9 @@ module Users
belongs_to :project
enum feature_name: {
- awaiting_members_banner: 1 # EE-only
+ awaiting_members_banner: 1, # EE-only
+ web_hook_disabled: 2,
+ ultimate_feature_removal_banner: 3
}
validates :project, presence: true
diff --git a/app/models/users_star_project.rb b/app/models/users_star_project.rb
index 1549c099a64..9a514b82506 100644
--- a/app/models/users_star_project.rb
+++ b/app/models/users_star_project.rb
@@ -3,7 +3,7 @@
class UsersStarProject < ApplicationRecord
include Sortable
- belongs_to :project, counter_cache: :star_count, touch: true
+ belongs_to :project, counter_cache: :star_count
belongs_to :user
validates :user, presence: true
diff --git a/app/models/wiki.rb b/app/models/wiki.rb
index d28a73b644f..fac79a8194a 100644
--- a/app/models/wiki.rb
+++ b/app/models/wiki.rb
@@ -103,6 +103,17 @@ class Wiki
def find_by_id(container_id)
container_class.find_by_id(container_id)&.wiki
end
+
+ def sluggified_full_path(title, extension)
+ sluggified_title(title) + '.' + extension
+ end
+
+ def sluggified_title(title)
+ title = Gitlab::EncodingHelper.encode_utf8_no_detect(title)
+ title = File.expand_path(title, '/')
+ title = Pathname.new(title).relative_path_from('/').to_s
+ title.tr(' ', '-')
+ end
end
def initialize(container, user = nil)
@@ -206,10 +217,11 @@ class Wiki
#
# Returns an initialized WikiPage instance or nil
def find_page(title, version = nil, load_content: true)
- page_title, page_dir = page_title_and_dir(title)
-
- if page = wiki.page(title: page_title, version: version, dir: page_dir, load_content: load_content)
- WikiPage.new(self, page)
+ if find_page_with_repository_rpcs?
+ create_wiki_repository unless repository_exists?
+ find_page_with_repository_rpcs(title, version, load_content: load_content)
+ else
+ find_page_with_legacy_wiki_service(title, version, load_content: load_content)
end
end
@@ -419,19 +431,83 @@ class Wiki
end
def sluggified_full_path(title, extension)
- sluggified_title(title) + '.' + extension
+ self.class.sluggified_full_path(title, extension)
end
def sluggified_title(title)
- utf8_encoded_title = Gitlab::EncodingHelper.encode_utf8_no_detect(title)
+ self.class.sluggified_title(title)
+ end
- sanitized_title(utf8_encoded_title).tr(' ', '-')
+ def canonicalize_filename(filename)
+ Gitlab::Git::Wiki::GollumSlug.canonicalize_filename(filename)
end
- def sanitized_title(title)
- clean_absolute_path = File.expand_path(title, '/')
+ def find_page_with_legacy_wiki_service(title, version, load_content: false)
+ page_title, page_dir = page_title_and_dir(title)
+
+ if page = wiki.page(title: page_title, version: version, dir: page_dir, load_content: load_content)
+ WikiPage.new(self, page)
+ end
+ end
+
+ def find_matched_file(title, version)
+ escaped_path = RE2::Regexp.escape(sluggified_title(title))
+ # We could not use ALLOWED_EXTENSIONS_REGEX constant or similar regexp with
+ # Regexp.union. The result combination complicated modifiers:
+ # /(?i-mx:md|mkdn?|mdown|markdown)|(?i-mx:rdoc).../
+ # Regexp used by Gitaly is Go's Regexp package. It does not support those
+ # features. So, we have to compose another more-friendly regexp to pass to
+ # Gitaly side.
+ extension_regexp = Wiki::MARKUPS.map { |_, format| format[:extension_regex].source }.join("|")
+ path_regexp = Gitlab::EncodingHelper.encode_utf8_no_detect("(?i)^#{escaped_path}\\.(#{extension_regexp})$")
+
+ matched_files = repository.search_files_by_regexp(path_regexp, version)
+ return if matched_files.blank?
+
+ Gitlab::EncodingHelper.encode_utf8_no_detect(matched_files.first)
+ end
+
+ def find_page_format(path)
+ ext = File.extname(path).downcase[1..]
+ MARKUPS.find { |_, markup| markup[:extension_regex].match?(ext) }&.first
+ end
+
+ def check_page_historical(path, commit)
+ repository.last_commit_for_path('HEAD', path).id != commit.id
+ end
+
+ def find_page_with_repository_rpcs(title, version, load_content: true)
+ version = version.presence || 'HEAD'
+ path = find_matched_file(title, version)
+ return if path.blank?
+
+ blob_options = load_content ? {} : { limit: 0 }
+ blob = repository.blob_at(version, path, **blob_options)
+ commit = repository.commit(blob.commit_id)
+ format = find_page_format(path)
+
+ page = Gitlab::Git::WikiPage.new(
+ url_path: sluggified_title(path.sub(/\.[^.]+\z/, "")),
+ title: canonicalize_filename(path),
+ format: format,
+ path: sluggified_title(path),
+ raw_data: blob.data,
+ name: canonicalize_filename(path),
+ historical: version == 'HEAD' ? false : check_page_historical(path, commit),
+ version: Gitlab::Git::WikiPageVersion.new(commit, format)
+ )
+ WikiPage.new(self, page)
+ end
+
+ def find_page_with_repository_rpcs?
+ group =
+ if container.is_a?(::Group)
+ container
+ else
+ container.group
+ end
- Pathname.new(clean_absolute_path).relative_path_from('/').to_s
+ Feature.enabled?(:wiki_find_page_with_normal_repository_rpcs, group, type: :development)
end
end
diff --git a/app/models/work_item.rb b/app/models/work_item.rb
index 451359c1f85..05e45fa5b29 100644
--- a/app/models/work_item.rb
+++ b/app/models/work_item.rb
@@ -37,11 +37,11 @@ class WorkItem < Issue
override :parent_link_confidentiality
def parent_link_confidentiality
if confidential? && work_item_children.public_only.exists?
- errors.add(:confidential, _('confidential parent can not be used if there are non-confidential children.'))
+ errors.add(:base, _('A confidential work item cannot have a parent that already has non-confidential children.'))
end
if !confidential? && work_item_parent&.confidential?
- errors.add(:confidential, _('associated parent is confidential and can not have non-confidential children.'))
+ errors.add(:base, _('A non-confidential work item cannot have a confidential parent.'))
end
end
diff --git a/app/models/work_items/widgets/description.rb b/app/models/work_items/widgets/description.rb
index 1e84d172bef..ec3b7957c79 100644
--- a/app/models/work_items/widgets/description.rb
+++ b/app/models/work_items/widgets/description.rb
@@ -3,7 +3,13 @@
module WorkItems
module Widgets
class Description < Base
- delegate :description, to: :work_item
+ delegate :description, :edited?, :last_edited_at, to: :work_item
+
+ def last_edited_by
+ return unless work_item.edited?
+
+ work_item.last_edited_by
+ end
end
end
end
diff --git a/app/policies/ci/build_policy.rb b/app/policies/ci/build_policy.rb
index f377ff85b5e..b657b569e3e 100644
--- a/app/policies/ci/build_policy.rb
+++ b/app/policies/ci/build_policy.rb
@@ -2,6 +2,8 @@
module Ci
class BuildPolicy < CommitStatusPolicy
+ delegate { @subject.project }
+
condition(:protected_ref) do
access = ::Gitlab::UserAccess.new(@user, container: @subject.project)
@@ -25,6 +27,10 @@ module Ci
false
end
+ condition(:prevent_rollback) do
+ @subject.prevent_rollback_deployment?
+ end
+
condition(:owner_of_job) do
@subject.triggered_by?(@user)
end
@@ -71,7 +77,7 @@ module Ci
# Authorizing the user to access to protected entities.
# There is a "jailbreak" mode to exceptionally bypass the authorization,
# however, you should NEVER allow it, rather suspect it's a wrong feature/product design.
- rule { ~can?(:jailbreak) & (archived | protected_ref | protected_environment) }.policy do
+ rule { ~can?(:jailbreak) & (archived | protected_ref | protected_environment | prevent_rollback) }.policy do
prevent :update_build
prevent :update_commit_status
prevent :erase_build
diff --git a/app/policies/ci/job_artifact_policy.rb b/app/policies/ci/job_artifact_policy.rb
new file mode 100644
index 00000000000..e25c7311565
--- /dev/null
+++ b/app/policies/ci/job_artifact_policy.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module Ci
+ class JobArtifactPolicy < BasePolicy
+ delegate { @subject.job.project }
+ end
+end
diff --git a/app/policies/ci/runner_policy.rb b/app/policies/ci/runner_policy.rb
index 8a99f4d1a3e..a52dac446ea 100644
--- a/app/policies/ci/runner_policy.rb
+++ b/app/policies/ci/runner_policy.rb
@@ -9,19 +9,65 @@ module Ci
@user.owns_runner?(@subject)
end
- condition(:belongs_to_multiple_projects) do
+ with_options scope: :subject, score: 0
+ condition(:is_instance_runner) do
+ @subject.instance_type?
+ end
+
+ with_options scope: :subject, score: 0
+ condition(:is_group_runner) do
+ @subject.group_type?
+ end
+
+ with_options scope: :user, score: 5
+ condition(:any_developer_groups_inheriting_shared_runners) do
+ @user.developer_groups.with_shared_runners_enabled.any?
+ end
+
+ with_options scope: :user, score: 5
+ condition(:any_developer_projects_inheriting_shared_runners) do
+ @user.authorized_projects(Gitlab::Access::DEVELOPER).with_shared_runners_enabled.any?
+ end
+
+ with_options score: 10
+ condition(:any_associated_projects_in_group_runner_inheriting_group_runners) do
+ # Check if any projects where user is a developer are inheriting group runners
+ @subject.groups&.any? do |group|
+ group.all_projects
+ .with_group_runners_enabled
+ .visible_to_user_and_access_level(@user, Gitlab::Access::DEVELOPER)
+ .exists?
+ end
+ end
+
+ condition(:belongs_to_multiple_projects, scope: :subject) do
@subject.belongs_to_more_than_one_project?
end
rule { anonymous }.prevent_all
- rule { admin }.policy do
+ rule { admin | owned_runner }.policy do
enable :read_builds
end
rule { admin | owned_runner }.policy do
- enable :assign_runner
enable :read_runner
+ end
+
+ rule { is_instance_runner & any_developer_groups_inheriting_shared_runners }.policy do
+ enable :read_runner
+ end
+
+ rule { is_instance_runner & any_developer_projects_inheriting_shared_runners }.policy do
+ enable :read_runner
+ end
+
+ rule { is_group_runner & any_associated_projects_in_group_runner_inheriting_group_runners }.policy do
+ enable :read_runner
+ end
+
+ rule { admin | owned_runner }.policy do
+ enable :assign_runner
enable :update_runner
enable :delete_runner
end
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index 44393539327..96da0518dc0 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -59,6 +59,10 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
access_level(for_any_session: true) >= GroupMember::GUEST || valid_dependency_proxy_deploy_token
end
+ condition(:observability_enabled) do
+ Feature.enabled?(:observability_group_tab, @subject)
+ end
+
desc "Deploy token with read_package_registry scope"
condition(:read_package_registry_deploy_token) do
@user.is_a?(DeployToken) && @user.groups.include?(@subject) && @user.read_package_registry
@@ -82,10 +86,6 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
Feature.disabled?(:runner_registration_control) || Gitlab::CurrentSettings.valid_runner_registrars.include?('group')
end
- condition(:change_prevent_sharing_groups_outside_hierarchy_available) do
- change_prevent_sharing_groups_outside_hierarchy_available?
- end
-
rule { can?(:read_group) & design_management_enabled }.policy do
enable :read_design_activity
end
@@ -196,6 +196,8 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
enable :set_note_created_at
enable :set_emails_disabled
+ enable :change_prevent_sharing_groups_outside_hierarchy
+ enable :set_show_diff_preview_in_email
enable :change_new_user_signups_cap
enable :update_default_branch_protection
enable :create_deploy_token
@@ -204,10 +206,6 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
enable :owner_access
end
- rule { owner & change_prevent_sharing_groups_outside_hierarchy_available }.policy do
- enable :change_prevent_sharing_groups_outside_hierarchy
- end
-
rule { can?(:read_nested_project_resources) }.policy do
enable :read_group_activity
enable :read_group_issues
@@ -299,6 +297,10 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
enable :destroy_resource_access_tokens
end
+ rule { can?(:developer_access) & observability_enabled }.policy do
+ enable :read_observability
+ end
+
def access_level(for_any_session: false)
return GroupMember::NO_ACCESS if @user.nil?
return GroupMember::NO_ACCESS unless user_is_user?
@@ -335,10 +337,6 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
def valid_dependency_proxy_deploy_token
@user.is_a?(DeployToken) && @user&.valid_for_dependency_proxy? && @user&.has_access_to_group?(@subject)
end
-
- def change_prevent_sharing_groups_outside_hierarchy_available?
- true
- end
end
GroupPolicy.prepend_mod_with('GroupPolicy')
diff --git a/app/policies/issuable_policy.rb b/app/policies/issuable_policy.rb
index 3c5e1020c8a..e5913bab726 100644
--- a/app/policies/issuable_policy.rb
+++ b/app/policies/issuable_policy.rb
@@ -5,6 +5,7 @@ class IssuablePolicy < BasePolicy
condition(:locked, scope: :subject, score: 0) { @subject.discussion_locked? }
condition(:is_project_member) { @user && @subject.project && @subject.project.team.member?(@user) }
+ condition(:can_read_issuable) { can?(:"read_#{@subject.to_ability_name}") }
desc "User is the assignee or author"
condition(:assignee_or_author) do
@@ -48,6 +49,10 @@ class IssuablePolicy < BasePolicy
rule { can?(:reporter_access) }.policy do
enable :create_timelog
end
+
+ rule { can_read_issuable }.policy do
+ enable :read_issuable
+ end
end
IssuablePolicy.prepend_mod_with('IssuablePolicy')
diff --git a/app/policies/packages/package_policy.rb b/app/policies/packages/package_policy.rb
index 8eef280c640..829d62a6430 100644
--- a/app/policies/packages/package_policy.rb
+++ b/app/policies/packages/package_policy.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
module Packages
class PackagePolicy < BasePolicy
- delegate { @subject.project }
+ delegate { @subject.project&.packages_policy_subject }
end
end
diff --git a/app/policies/packages/policies/group_policy.rb b/app/policies/packages/policies/group_policy.rb
new file mode 100644
index 00000000000..32dbcb1b65b
--- /dev/null
+++ b/app/policies/packages/policies/group_policy.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Packages
+ module Policies
+ class GroupPolicy < BasePolicy
+ delegate(:group) { @subject.group }
+
+ overrides(:read_package)
+
+ rule { group.public_group }.policy do
+ enable :read_package
+ end
+
+ rule { group.reporter }.policy do
+ enable :read_package
+ end
+
+ rule { group.read_package_registry_deploy_token }.policy do
+ enable :read_package
+ end
+
+ rule { group.write_package_registry_deploy_token }.policy do
+ enable :read_package
+ end
+ end
+ end
+end
diff --git a/app/policies/packages/policies/project_policy.rb b/app/policies/packages/policies/project_policy.rb
new file mode 100644
index 00000000000..c754d24349a
--- /dev/null
+++ b/app/policies/packages/policies/project_policy.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module Packages
+ module Policies
+ class ProjectPolicy < BasePolicy
+ delegate(:project) { @subject.project }
+
+ overrides(:read_package)
+
+ condition(:package_registry_access_level_feature_flag_enabled, scope: :subject) do
+ ::Feature.enabled?(:package_registry_access_level, @subject)
+ end
+
+ condition(:packages_enabled_for_everyone, scope: :subject) do
+ @subject.package_registry_access_level == ProjectFeature::PUBLIC
+ end
+
+ # This rule can be removed if the `package_registry_access_level` feature flag is removed.
+ # Reason: If the feature flag is globally enabled, this rule will never be executed.
+ rule { anonymous & ~project.public_project & ~package_registry_access_level_feature_flag_enabled }.prevent_all
+
+ # This rule can be removed if the `package_registry_access_level` feature flag is removed.
+ # Reason: If the feature flag is globally enabled, this rule will never be executed.
+ rule do
+ ~project.public_project & ~project.internal_access &
+ ~project.project_allowed_for_job_token & ~package_registry_access_level_feature_flag_enabled
+ end.prevent_all
+
+ rule { project.packages_disabled }.policy do
+ prevent(:read_package)
+ end
+
+ rule { can?(:reporter_access) }.policy do
+ enable :read_package
+ end
+
+ rule { can?(:public_access) }.policy do
+ enable :read_package
+ end
+
+ rule { project.read_package_registry_deploy_token }.policy do
+ enable :read_package
+ end
+
+ rule { project.write_package_registry_deploy_token }.policy do
+ enable :read_package
+ end
+
+ rule { package_registry_access_level_feature_flag_enabled & packages_enabled_for_everyone }.policy do
+ enable :read_package
+ end
+ end
+ end
+end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index f4f7275a78a..fb162d03955 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -208,6 +208,7 @@ class ProjectPolicy < BasePolicy
metrics_dashboard
analytics
operations
+ monitor
security_and_compliance
environments
feature_flags
@@ -267,6 +268,7 @@ class ProjectPolicy < BasePolicy
enable :set_note_created_at
enable :set_emails_disabled
enable :set_show_default_award_emojis
+ enable :set_show_diff_preview_in_email
enable :set_warn_about_potentially_unwanted_characters
enable :register_project_runners
@@ -401,6 +403,12 @@ class ProjectPolicy < BasePolicy
prevent(*create_read_update_admin_destroy(:release))
end
+ rule { split_operations_visibility_permissions & 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 { can?(:metrics_dashboard) }.policy do
enable :read_prometheus
enable :read_deployment
diff --git a/app/policies/protected_branch_access_policy.rb b/app/policies/protected_branch_access_policy.rb
new file mode 100644
index 00000000000..4f33af89d2a
--- /dev/null
+++ b/app/policies/protected_branch_access_policy.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class ProtectedBranchAccessPolicy < BasePolicy
+ delegate { @subject.protected_branch }
+end
diff --git a/app/policies/protected_branch_policy.rb b/app/policies/protected_branch_policy.rb
index 8ad06653e5c..2be96ea7f24 100644
--- a/app/policies/protected_branch_policy.rb
+++ b/app/policies/protected_branch_policy.rb
@@ -4,6 +4,7 @@ class ProtectedBranchPolicy < BasePolicy
delegate { @subject.project }
rule { can?(:admin_project) }.policy do
+ enable :read_protected_branch
enable :create_protected_branch
enable :update_protected_branch
enable :destroy_protected_branch
diff --git a/app/presenters/ci/pipeline_presenter.rb b/app/presenters/ci/pipeline_presenter.rb
index 887980430f4..32a7d205f46 100644
--- a/app/presenters/ci/pipeline_presenter.rb
+++ b/app/presenters/ci/pipeline_presenter.rb
@@ -49,7 +49,7 @@ module Ci
{
merge_train: s_('Pipeline|Merge train pipeline'),
merged_result: s_('Pipeline|Merged result pipeline'),
- detached: s_('Pipeline|Detached merge request pipeline')
+ detached: s_('Pipeline|Merge request pipeline')
}.freeze
end
diff --git a/app/presenters/commit_status_presenter.rb b/app/presenters/commit_status_presenter.rb
index 815a4da25ab..059d6d06be2 100644
--- a/app/presenters/commit_status_presenter.rb
+++ b/app/presenters/commit_status_presenter.rb
@@ -26,6 +26,7 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
downstream_pipeline_creation_failed: 'The downstream pipeline could not be created',
secrets_provider_not_found: 'The secrets provider can not be found',
reached_max_descendant_pipelines_depth: 'You reached the maximum depth of child pipelines',
+ reached_max_pipeline_hierarchy_size: 'The downstream pipeline tree is too large',
project_deleted: 'The job belongs to a deleted project',
user_blocked: 'The user who created this job is blocked',
ci_quota_exceeded: 'No more CI minutes available',
@@ -34,11 +35,13 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
builds_disabled: 'The CI/CD is disabled for this project',
environment_creation_failure: 'This job could not be executed because it would create an environment with an invalid parameter.',
deployment_rejected: 'This deployment job was rejected.',
- ip_restriction_failure: "This job could not be executed because group IP address restrictions are enabled, and the runner's IP address is not in the allowed range."
+ ip_restriction_failure: "This job could not be executed because group IP address restrictions are enabled, and the runner's IP address is not in the allowed range.",
+ failed_outdated_deployment_job: 'The deployment job is older than the latest deployment, and therefore failed.'
}.freeze
TROUBLESHOOTING_DOC = {
- environment_creation_failure: { path: 'ci/environments/index', anchor: 'a-deployment-job-failed-with-this-job-could-not-be-executed-because-it-would-create-an-environment-with-an-invalid-parameter-error' }
+ environment_creation_failure: { path: 'ci/environments/index', anchor: 'a-deployment-job-failed-with-this-job-could-not-be-executed-because-it-would-create-an-environment-with-an-invalid-parameter-error' },
+ failed_outdated_deployment_job: { path: 'ci/environments/deployment_safety', anchor: 'skip-outdated-deployment-jobs' }
}.freeze
private_constant :CALLOUT_FAILURE_MESSAGES
diff --git a/app/presenters/deployments/deployment_presenter.rb b/app/presenters/deployments/deployment_presenter.rb
new file mode 100644
index 00000000000..5ef6fcff974
--- /dev/null
+++ b/app/presenters/deployments/deployment_presenter.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Deployments
+ class DeploymentPresenter < Gitlab::View::Presenter::Delegated
+ presents ::Deployment, as: :deployment
+
+ delegator_override :tags
+ def tags
+ super.map do |tag|
+ {
+ name: tag,
+ path: "tags/#{tag}"
+ }
+ end
+ end
+ end
+end
diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb
index 209f016dc6b..0be13197343 100644
--- a/app/presenters/project_presenter.rb
+++ b/app/presenters/project_presenter.rb
@@ -317,6 +317,8 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
end
def autodevops_anchor_data(show_auto_devops_callout: false)
+ return unless project.feature_available?(:builds, current_user)
+
if current_user && can?(current_user, :admin_pipeline, project) && repository.gitlab_ci_yml.blank? && !show_auto_devops_callout
if auto_devops_enabled?
AnchorData.new(false,
diff --git a/app/serializers/access_token_entity_base.rb b/app/serializers/access_token_entity_base.rb
new file mode 100644
index 00000000000..db22dbf1302
--- /dev/null
+++ b/app/serializers/access_token_entity_base.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+# rubocop: disable Gitlab/NamespacedClass
+class AccessTokenEntityBase < API::Entities::PersonalAccessToken
+ expose :expired?, as: :expired
+ expose :expires_soon?, as: :expires_soon
+end
+# rubocop: enable Gitlab/NamespacedClass
diff --git a/app/serializers/environment_serializer.rb b/app/serializers/environment_serializer.rb
index 6363d6276a7..22839ba3099 100644
--- a/app/serializers/environment_serializer.rb
+++ b/app/serializers/environment_serializer.rb
@@ -96,8 +96,7 @@ class EnvironmentSerializer < BaseSerializer
scheduled_actions: [:metadata],
latest_successful_builds: []
},
- project: project_associations,
- deployment: []
+ project: project_associations
}
}
end
diff --git a/app/serializers/group_access_token_entity.rb b/app/serializers/group_access_token_entity.rb
index e832eef1188..ab1fbb8ab46 100644
--- a/app/serializers/group_access_token_entity.rb
+++ b/app/serializers/group_access_token_entity.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
# rubocop: disable Gitlab/NamespacedClass
-class GroupAccessTokenEntity < API::Entities::PersonalAccessToken
+class GroupAccessTokenEntity < AccessTokenEntityBase
include Gitlab::Routing
expose :revoke_path do |token, options|
@@ -14,13 +14,13 @@ class GroupAccessTokenEntity < API::Entities::PersonalAccessToken
group_id: group.path)
end
- expose :access_level do |token, options|
+ expose :role do |token, options|
group = options.fetch(:group)
next unless group
next unless token.user
- group.member(token.user)&.access_level
+ group.member(token.user)&.human_access
end
end
# rubocop: enable Gitlab/NamespacedClass
diff --git a/app/serializers/impersonation_access_token_entity.rb b/app/serializers/impersonation_access_token_entity.rb
new file mode 100644
index 00000000000..b4ed62a890d
--- /dev/null
+++ b/app/serializers/impersonation_access_token_entity.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+# rubocop: disable Gitlab/NamespacedClass
+class ImpersonationAccessTokenEntity < AccessTokenEntityBase
+ include Gitlab::Routing
+
+ expose :revoke_path do |token, _options|
+ revoke_admin_user_impersonation_token_path(token.user, token)
+ end
+end
+# rubocop: enable Gitlab/NamespacedClass
diff --git a/app/serializers/impersonation_access_token_serializer.rb b/app/serializers/impersonation_access_token_serializer.rb
new file mode 100644
index 00000000000..d3ea5ceb305
--- /dev/null
+++ b/app/serializers/impersonation_access_token_serializer.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+# rubocop: disable Gitlab/NamespacedClass
+class ImpersonationAccessTokenSerializer < BaseSerializer
+ entity ImpersonationAccessTokenEntity
+end
+# rubocop: enable Gitlab/NamespacedClass
diff --git a/app/serializers/import/provider_repo_serializer.rb b/app/serializers/import/provider_repo_serializer.rb
index edd1a260146..d5d29603989 100644
--- a/app/serializers/import/provider_repo_serializer.rb
+++ b/app/serializers/import/provider_repo_serializer.rb
@@ -23,3 +23,5 @@ class Import::ProviderRepoSerializer < BaseSerializer
super(repo, opts, entity)
end
end
+
+Import::ProviderRepoSerializer.prepend_mod
diff --git a/app/serializers/member_user_entity.rb b/app/serializers/member_user_entity.rb
index 6a01c5bb297..73cb9a4a798 100644
--- a/app/serializers/member_user_entity.rb
+++ b/app/serializers/member_user_entity.rb
@@ -16,6 +16,10 @@ class MemberUserEntity < UserEntity
user.blocked?
end
+ expose :is_bot do |user|
+ user.bot?
+ end
+
expose :two_factor_enabled, if: -> (user) { current_user_can_manage_members? || current_user?(user) } do |user|
user.two_factor_enabled?
end
diff --git a/app/serializers/merge_request_noteable_entity.rb b/app/serializers/merge_request_noteable_entity.rb
index f8c8e3538da..29bd26c3a15 100644
--- a/app/serializers/merge_request_noteable_entity.rb
+++ b/app/serializers/merge_request_noteable_entity.rb
@@ -10,6 +10,15 @@ class MergeRequestNoteableEntity < IssuableEntity
expose :state
expose :source_branch
expose :target_branch
+
+ expose :source_branch_path, if: -> (merge_request) { merge_request.source_project } do |merge_request|
+ project_tree_path(merge_request.source_project, merge_request.source_branch)
+ end
+
+ expose :target_branch_path, if: -> (merge_request) { merge_request.source_project } do |merge_request|
+ project_tree_path(merge_request.source_project, merge_request.target_branch)
+ end
+
expose :diff_head_sha
expose :create_note_path do |merge_request|
@@ -40,6 +49,10 @@ class MergeRequestNoteableEntity < IssuableEntity
expose :can_update do |merge_request|
can?(current_user, :update_merge_request, merge_request)
end
+
+ expose :can_approve do |merge_request|
+ merge_request.can_be_approved_by?(current_user)
+ end
end
expose :locked_discussion_docs_path, if: -> (merge_request) { merge_request.discussion_locked? } do |merge_request|
@@ -65,3 +78,5 @@ class MergeRequestNoteableEntity < IssuableEntity
@presenters[merge_request] ||= MergeRequestPresenter.new(merge_request, current_user: current_user) # rubocop: disable CodeReuse/Presenter
end
end
+
+MergeRequestNoteableEntity.prepend_mod_with('MergeRequestNoteableEntity')
diff --git a/app/serializers/merge_request_user_entity.rb b/app/serializers/merge_request_user_entity.rb
index 2e875af6531..36825d14062 100644
--- a/app/serializers/merge_request_user_entity.rb
+++ b/app/serializers/merge_request_user_entity.rb
@@ -17,7 +17,7 @@ class MergeRequestUserEntity < ::API::Entities::UserBasic
end
expose :reviewed, if: satisfies(:present?, :allows_reviewers?) do |user, options|
- find_reviewer_or_assignee(user, options)&.reviewed?
+ options[:merge_request].find_reviewer(user)&.reviewed?
end
expose :approved, if: satisfies(:present?) do |user, options|
@@ -25,16 +25,6 @@ class MergeRequestUserEntity < ::API::Entities::UserBasic
# makes one query per merge request, whereas #approved_by? makes one per user
options[:merge_request].approvals.any? { |app| app.user_id == user.id }
end
-
- private
-
- def find_reviewer_or_assignee(user, options)
- if options[:type] == :reviewers
- options[:merge_request].find_reviewer(user)
- else
- options[:merge_request].find_assignee(user)
- end
- end
end
MergeRequestUserEntity.prepend_mod_with('MergeRequestUserEntity')
diff --git a/app/serializers/personal_access_token_entity.rb b/app/serializers/personal_access_token_entity.rb
index acd06fecd12..49dcdf12a6f 100644
--- a/app/serializers/personal_access_token_entity.rb
+++ b/app/serializers/personal_access_token_entity.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
# rubocop: disable Gitlab/NamespacedClass
-class PersonalAccessTokenEntity < API::Entities::PersonalAccessToken
+class PersonalAccessTokenEntity < AccessTokenEntityBase
include Gitlab::Routing
expose :revoke_path do |token, options|
diff --git a/app/serializers/project_access_token_entity.rb b/app/serializers/project_access_token_entity.rb
index b317057c952..52bb7b05d4e 100644
--- a/app/serializers/project_access_token_entity.rb
+++ b/app/serializers/project_access_token_entity.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
# rubocop: disable Gitlab/NamespacedClass
-class ProjectAccessTokenEntity < API::Entities::PersonalAccessToken
+class ProjectAccessTokenEntity < AccessTokenEntityBase
include Gitlab::Routing
expose :revoke_path do |token, options|
@@ -15,13 +15,13 @@ class ProjectAccessTokenEntity < API::Entities::PersonalAccessToken
project_id: project.path)
end
- expose :access_level do |token, options|
+ expose :role do |token, options|
project = options.fetch(:project)
next unless project
next unless token.user
- project.member(token.user)&.access_level
+ project.member(token.user)&.human_access
end
end
# rubocop: enable Gitlab/NamespacedClass
diff --git a/app/serializers/request_aware_entity.rb b/app/serializers/request_aware_entity.rb
index 1524c1291d8..04caba43cf4 100644
--- a/app/serializers/request_aware_entity.rb
+++ b/app/serializers/request_aware_entity.rb
@@ -10,6 +10,6 @@ module RequestAwareEntity
end
def request
- options.fetch(:request)
+ options.fetch(:request, nil)
end
end
diff --git a/app/services/alert_management/process_prometheus_alert_service.rb b/app/services/alert_management/process_prometheus_alert_service.rb
index 1b377a3d367..e0594247975 100644
--- a/app/services/alert_management/process_prometheus_alert_service.rb
+++ b/app/services/alert_management/process_prometheus_alert_service.rb
@@ -36,10 +36,5 @@ module AlertManagement
)
end
end
-
- override :resolving_alert?
- def resolving_alert?
- incoming_payload.resolved?
- end
end
end
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index e806bef46fe..509c2d4d544 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -83,6 +83,7 @@ module Auth
token.audience = params[:service]
token.subject = current_user.try(:username)
token.expire_time = self.class.token_expire_at
+ token[:auth_type] = params[:auth_type]
token[:access] = accesses.compact
end
end
diff --git a/app/services/authorized_project_update/find_records_due_for_refresh_service.rb b/app/services/authorized_project_update/find_records_due_for_refresh_service.rb
index 3a2251f15cc..dd696da0447 100644
--- a/app/services/authorized_project_update/find_records_due_for_refresh_service.rb
+++ b/app/services/authorized_project_update/find_records_due_for_refresh_service.rb
@@ -28,31 +28,33 @@ module AuthorizedProjectUpdate
current.except!(*projects_with_duplicates)
remove |= current.each_with_object([]) do |(project_id, row), array|
+ next if fresh[project_id] && fresh[project_id] == row.access_level
+
# rows not in the new list or with a different access level should be
# removed.
- if !fresh[project_id] || fresh[project_id] != row.access_level
- if incorrect_auth_found_callback
- incorrect_auth_found_callback.call(project_id, row.access_level)
- end
- array << row.project_id
+ if incorrect_auth_found_callback
+ incorrect_auth_found_callback.call(project_id, row.access_level)
end
+
+ array << row.project_id
end
add = fresh.each_with_object([]) do |(project_id, level), array|
+ next if current[project_id] && current[project_id].access_level == level
+
# rows not in the old list or with a different access level should be
# added.
- if !current[project_id] || current[project_id].access_level != level
- if missing_auth_found_callback
- missing_auth_found_callback.call(project_id, level)
- end
-
- array << {
- user_id: user.id,
- project_id: project_id,
- access_level: level
- }
+
+ if missing_auth_found_callback
+ missing_auth_found_callback.call(project_id, level)
end
+
+ array << {
+ user_id: user.id,
+ project_id: project_id,
+ access_level: level
+ }
end
[remove, add]
diff --git a/app/services/boards/base_item_move_service.rb b/app/services/boards/base_item_move_service.rb
index 9d711d83fd2..c9da889c536 100644
--- a/app/services/boards/base_item_move_service.rb
+++ b/app/services/boards/base_item_move_service.rb
@@ -2,6 +2,8 @@
module Boards
class BaseItemMoveService < Boards::BaseService
+ LIST_END_POSITION = -1
+
def execute(issuable)
issuable_modification_params = issuable_params(issuable)
return if issuable_modification_params.empty?
@@ -32,7 +34,13 @@ module Boards
)
end
- reposition_ids = move_between_ids(params)
+ move_params = if params[:position_in_list].present?
+ move_params_from_list_position(params[:position_in_list])
+ else
+ params
+ end
+
+ reposition_ids = move_between_ids(move_params)
attrs.merge!(reposition_params(reposition_ids)) if reposition_ids
attrs
@@ -90,6 +98,18 @@ module Boards
::Label.ids_on_board(board.id)
end
+ def move_params_from_list_position(position)
+ if position == LIST_END_POSITION
+ { move_before_id: moving_to_list_items_relation.reverse_order.pick(:id), move_after_id: nil }
+ else
+ item_at_position = moving_to_list_items_relation.offset(position).pick(:id) # rubocop: disable CodeReuse/ActiveRecord
+
+ return move_params_from_list_position(LIST_END_POSITION) if item_at_position.nil?
+
+ { move_before_id: nil, move_after_id: item_at_position }
+ end
+ end
+
def move_between_ids(move_params)
ids = [move_params[:move_before_id], move_params[:move_after_id]]
.map(&:to_i)
diff --git a/app/services/boards/issues/move_service.rb b/app/services/boards/issues/move_service.rb
index 90226b9d4e0..4de4d7c8f69 100644
--- a/app/services/boards/issues/move_service.rb
+++ b/app/services/boards/issues/move_service.rb
@@ -54,6 +54,10 @@ module Boards
def update(issue, issue_modification_params)
::Issues::UpdateService.new(project: issue.project, current_user: current_user, params: issue_modification_params).execute(issue)
end
+
+ def moving_to_list_items_relation
+ Boards::Issues::ListService.new(board.resource_parent, current_user, board_id: board.id, id: moving_to_list.id).execute
+ end
end
end
end
diff --git a/app/services/bulk_imports/file_download_service.rb b/app/services/bulk_imports/file_download_service.rb
index a2c8ba5b1cd..45f1350df92 100644
--- a/app/services/bulk_imports/file_download_service.rb
+++ b/app/services/bulk_imports/file_download_service.rb
@@ -10,10 +10,11 @@
# @param filename [String] Name of the file to download, if known. Use remote filename if none given.
module BulkImports
class FileDownloadService
+ include ::BulkImports::FileDownloads::FilenameFetch
+ include ::BulkImports::FileDownloads::Validations
+
ServiceError = Class.new(StandardError)
- REMOTE_FILENAME_PATTERN = %r{filename="(?<filename>[^"]+)"}.freeze
- FILENAME_SIZE_LIMIT = 255 # chars before the extension
DEFAULT_FILE_SIZE_LIMIT = 5.gigabytes
DEFAULT_ALLOWED_CONTENT_TYPES = %w(application/gzip application/octet-stream).freeze
@@ -74,6 +75,10 @@ module BulkImports
raise e
end
+ def raise_error(message)
+ raise ServiceError, message
+ end
+
def http_client
@http_client ||= BulkImports::Clients::HTTP.new(
url: configuration.url,
@@ -85,24 +90,20 @@ module BulkImports
::Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services?
end
- def headers
- @headers ||= http_client.head(relative_url).headers
- end
-
- def validate_filepath
- Gitlab::Utils.check_path_traversal!(filepath)
+ 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
- def validate_symlink
- if File.lstat(filepath).symlink?
- File.delete(filepath)
+ def filepath
+ @filepath ||= File.join(@tmpdir, filename)
+ end
- raise(ServiceError, 'Invalid downloaded file')
- end
+ def filename
+ @filename.presence || remote_filename
end
def validate_url
@@ -113,61 +114,5 @@ module BulkImports
schemes: %w(http https)
)
end
-
- def validate_content_length
- validate_size!(headers['content-length'])
- end
-
- def validate_size!(size)
- if size.blank?
- raise ServiceError, 'Missing content-length header'
- elsif size.to_i > file_size_limit
- raise ServiceError, "File size %{size} exceeds limit of %{limit}" % {
- size: ActiveSupport::NumberHelper.number_to_human_size(size),
- limit: ActiveSupport::NumberHelper.number_to_human_size(file_size_limit)
- }
- end
- end
-
- def validate_content_type
- content_type = headers['content-type']
-
- raise(ServiceError, 'Invalid content type') if content_type.blank? || allowed_content_types.exclude?(content_type)
- end
-
- def filepath
- @filepath ||= File.join(@tmpdir, filename)
- end
-
- def filename
- @filename.presence || remote_filename
- end
-
- # Fetch the remote filename information from the request content-disposition header
- # - Raises if the filename does not exist
- # - If the filename is longer then 255 chars truncate it
- # to be a total of 255 chars (with the extension)
- def remote_filename
- @remote_filename ||=
- headers['content-disposition'].to_s
- .match(REMOTE_FILENAME_PATTERN) # matches the filename pattern
- .then { |match| match&.named_captures || {} } # ensures the match is a hash
- .fetch('filename') # fetches the 'filename' key or raise KeyError
- .then(&File.method(:basename)) # Ensures to remove path from the filename (../ for instance)
- .then(&method(:ensure_filename_size)) # Ensures the filename is within the FILENAME_SIZE_LIMIT
- rescue KeyError
- raise ServiceError, 'Remote filename not provided in content-disposition header'
- end
-
- def ensure_filename_size(filename)
- if filename.length <= FILENAME_SIZE_LIMIT
- filename
- else
- extname = File.extname(filename)
- basename = File.basename(filename, extname)[0, FILENAME_SIZE_LIMIT]
-
- "#{basename}#{extname}"
- end
- end
end
end
diff --git a/app/services/bulk_imports/relation_export_service.rb b/app/services/bulk_imports/relation_export_service.rb
index c43f0d8cb4f..b1efa881180 100644
--- a/app/services/bulk_imports/relation_export_service.rb
+++ b/app/services/bulk_imports/relation_export_service.rb
@@ -65,7 +65,7 @@ module BulkImports
def export_service
@export_service ||= if config.tree_relation?(relation) || config.self_relation?(relation)
- TreeExportService.new(portable, config.export_path, relation)
+ TreeExportService.new(portable, config.export_path, relation, user)
elsif config.file_relation?(relation)
FileExportService.new(portable, config.export_path, relation)
else
diff --git a/app/services/bulk_imports/tree_export_service.rb b/app/services/bulk_imports/tree_export_service.rb
index 8e885e590d1..b6f094da558 100644
--- a/app/services/bulk_imports/tree_export_service.rb
+++ b/app/services/bulk_imports/tree_export_service.rb
@@ -2,11 +2,12 @@
module BulkImports
class TreeExportService
- def initialize(portable, export_path, relation)
+ def initialize(portable, export_path, relation, user)
@portable = portable
@export_path = export_path
@relation = relation
@config = FileTransfer.config_for(portable)
+ @user = user
end
def execute
@@ -27,7 +28,7 @@ module BulkImports
private
- attr_reader :export_path, :portable, :relation, :config
+ attr_reader :export_path, :portable, :relation, :config, :user
# rubocop: disable CodeReuse/Serializer
def serializer
@@ -35,7 +36,8 @@ module BulkImports
portable,
config.portable_tree,
json_writer,
- exportable_path: ''
+ exportable_path: '',
+ current_user: user
)
end
# rubocop: enable CodeReuse/Serializer
diff --git a/app/services/ci/after_requeue_job_service.rb b/app/services/ci/after_requeue_job_service.rb
index 1ae4639751b..634c547a623 100644
--- a/app/services/ci/after_requeue_job_service.rb
+++ b/app/services/ci/after_requeue_job_service.rb
@@ -21,9 +21,16 @@ module Ci
@processable.pipeline.reset_source_bridge!(current_user)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def dependent_jobs
+ return legacy_dependent_jobs unless ::Feature.enabled?(:ci_requeue_with_dag_object_hierarchy, project)
+
ordered_by_dag(
- stage_dependent_jobs.or(needs_dependent_jobs).ordered_by_stage
+ ::Ci::Processable
+ .from_union(needs_dependent_jobs, stage_dependent_jobs)
+ .skipped
+ .ordered_by_stage
+ .preload(:needs)
)
end
@@ -34,22 +41,37 @@ module Ci
end
def stage_dependent_jobs
- skipped_jobs.after_stage(@processable.stage_idx)
+ @processable.pipeline.processables.after_stage(@processable.stage_idx)
end
def needs_dependent_jobs
- skipped_jobs.scheduling_type_dag.with_needs([@processable.name])
+ ::Gitlab::Ci::ProcessableObjectHierarchy.new(
+ ::Ci::Processable.where(id: @processable.id)
+ ).descendants
end
- def skipped_jobs
- @skipped_jobs ||= @processable.pipeline.processables.skipped
+ def legacy_skipped_jobs
+ @legacy_skipped_jobs ||= @processable.pipeline.processables.skipped
+ end
+
+ def legacy_dependent_jobs
+ ordered_by_dag(
+ legacy_stage_dependent_jobs.or(legacy_needs_dependent_jobs).ordered_by_stage.preload(:needs)
+ )
+ end
+
+ def legacy_stage_dependent_jobs
+ legacy_skipped_jobs.after_stage(@processable.stage_idx)
+ end
+
+ def legacy_needs_dependent_jobs
+ legacy_skipped_jobs.scheduling_type_dag.with_needs([@processable.name])
end
- # rubocop: disable CodeReuse/ActiveRecord
def ordered_by_dag(jobs)
sorted_job_names = sort_jobs(jobs).each_with_index.to_h
- jobs.preload(:needs).group_by(&:stage_idx).flat_map do |_, stage_jobs|
+ jobs.group_by(&:stage_idx).flat_map do |_, stage_jobs|
stage_jobs.sort_by { |job| sorted_job_names.fetch(job.name) }
end
end
diff --git a/app/services/ci/archive_trace_service.rb b/app/services/ci/archive_trace_service.rb
index 9705a236d98..566346a4b09 100644
--- a/app/services/ci/archive_trace_service.rb
+++ b/app/services/ci/archive_trace_service.rb
@@ -27,7 +27,7 @@ module Ci
job.trace.archive!
job.remove_pending_state!
- if Feature.enabled?(:datadog_integration_logs_collection, job.project) && job.job_artifacts_trace.present?
+ if job.job_artifacts_trace.present?
job.project.execute_integrations(Gitlab::DataBuilder::ArchiveTrace.build(job), :archive_trace_hooks)
end
diff --git a/app/services/ci/build_erase_service.rb b/app/services/ci/build_erase_service.rb
new file mode 100644
index 00000000000..8a468e094eb
--- /dev/null
+++ b/app/services/ci/build_erase_service.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module Ci
+ class BuildEraseService
+ include BaseServiceUtility
+
+ def initialize(build, current_user)
+ @build = build
+ @current_user = current_user
+ end
+
+ def execute
+ unless build.erasable?
+ return ServiceResponse.error(message: _('Build cannot be erased'), http_status: :unprocessable_entity)
+ end
+
+ if build.project.refreshing_build_artifacts_size?
+ Gitlab::ProjectStatsRefreshConflictsLogger.warn_artifact_deletion_during_stats_refresh(
+ method: 'Ci::BuildEraseService#execute',
+ project_id: build.project_id
+ )
+ end
+
+ destroy_artifacts
+ erase_trace!
+ update_erased!
+
+ ServiceResponse.success(payload: build)
+ end
+
+ private
+
+ attr_reader :build, :current_user
+
+ def destroy_artifacts
+ # fix_expire_at is false because in this case we want to explicitly delete the job artifacts
+ # this flag is a workaround that will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/355833
+ Ci::JobArtifacts::DestroyBatchService.new(build.job_artifacts, fix_expire_at: false).execute
+ end
+
+ def erase_trace!
+ build.trace.erase!
+ end
+
+ def update_erased!
+ build.update(erased_by: current_user, erased_at: Time.current, artifacts_expire_at: nil)
+ end
+ end
+end
diff --git a/app/services/ci/build_report_result_service.rb b/app/services/ci/build_report_result_service.rb
index f9146b3677a..20a31322919 100644
--- a/app/services/ci/build_report_result_service.rb
+++ b/app/services/ci/build_report_result_service.rb
@@ -22,7 +22,8 @@ module Ci
private
def generate_test_suite_report(build)
- build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
+ test_report = build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
+ test_report.get_suite(build.test_suite_name)
end
def tests_params(test_suite)
diff --git a/app/services/ci/compare_reports_base_service.rb b/app/services/ci/compare_reports_base_service.rb
index 9aba3a50ec1..ee687706b53 100644
--- a/app/services/ci/compare_reports_base_service.rb
+++ b/app/services/ci/compare_reports_base_service.rb
@@ -8,6 +8,8 @@ module Ci
# issue: https://gitlab.com/gitlab-org/gitlab/issues/34224
class CompareReportsBaseService < ::BaseService
def execute(base_pipeline, head_pipeline)
+ return parsing_payload(base_pipeline, head_pipeline) if base_pipeline&.running?
+
base_report = get_report(base_pipeline)
head_report = get_report(head_pipeline)
comparer = build_comparer(base_report, head_report)
@@ -33,6 +35,13 @@ module Ci
protected
+ def parsing_payload(base_pipeline, head_pipeline)
+ {
+ status: :parsing,
+ key: key(base_pipeline, head_pipeline)
+ }
+ end
+
def build_comparer(base_report, head_report)
comparer_class.new(base_report, head_report)
end
diff --git a/app/services/ci/create_downstream_pipeline_service.rb b/app/services/ci/create_downstream_pipeline_service.rb
index b38b3b93353..25cc9045052 100644
--- a/app/services/ci/create_downstream_pipeline_service.rb
+++ b/app/services/ci/create_downstream_pipeline_service.rb
@@ -11,6 +11,7 @@ module Ci
DuplicateDownstreamPipelineError = Class.new(StandardError)
MAX_NESTED_CHILDREN = 2
+ MAX_HIERARCHY_SIZE = 1000
def execute(bridge)
@bridge = bridge
@@ -86,6 +87,11 @@ module Ci
return false
end
+ if Feature.enabled?(:ci_limit_complete_hierarchy_size) && pipeline_tree_too_large?
+ @bridge.drop!(:reached_max_pipeline_hierarchy_size)
+ return false
+ end
+
unless can_create_downstream_pipeline?(target_ref)
@bridge.drop!(:insufficient_bridge_permissions)
return false
@@ -137,10 +143,17 @@ module Ci
return false unless @bridge.triggers_child_pipeline?
# only applies to parent-child pipelines not multi-project
- ancestors_of_new_child = @bridge.pipeline.self_and_ancestors
+ ancestors_of_new_child = @bridge.pipeline.self_and_project_ancestors
ancestors_of_new_child.count > MAX_NESTED_CHILDREN
end
+ def pipeline_tree_too_large?
+ return false unless @bridge.triggers_downstream_pipeline?
+
+ # Applies to the entire pipeline tree across all projects
+ @bridge.pipeline.complete_hierarchy_count >= MAX_HIERARCHY_SIZE
+ end
+
def config_checksum(pipeline)
[pipeline.project_id, pipeline.ref, pipeline.source].hash
end
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index 02f25a82307..af175b8da1c 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -23,6 +23,7 @@ module Ci
Gitlab::Ci::Pipeline::Chain::RemoveUnwantedChatJobs,
Gitlab::Ci::Pipeline::Chain::SeedBlock,
Gitlab::Ci::Pipeline::Chain::EvaluateWorkflowRules,
+ Gitlab::Ci::Pipeline::Chain::AssignPartition,
Gitlab::Ci::Pipeline::Chain::Seed,
Gitlab::Ci::Pipeline::Chain::Limit::Size,
Gitlab::Ci::Pipeline::Chain::Limit::Deployments,
diff --git a/app/services/ci/delete_objects_service.rb b/app/services/ci/delete_objects_service.rb
index bac99abadc9..7a93d0e9665 100644
--- a/app/services/ci/delete_objects_service.rb
+++ b/app/services/ci/delete_objects_service.rb
@@ -27,9 +27,7 @@ module Ci
# `find_by_sql` performs a write in this case and we need to wrap it in
# a transaction to stick to the primary database.
Ci::DeletedObject.transaction do
- Ci::DeletedObject.find_by_sql([
- next_batch_sql, new_pick_up_at: RETRY_IN.from_now
- ])
+ Ci::DeletedObject.find_by_sql([next_batch_sql, new_pick_up_at: RETRY_IN.from_now])
end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/services/ci/expire_pipeline_cache_service.rb b/app/services/ci/expire_pipeline_cache_service.rb
index bf2355c447a..15597eb7209 100644
--- a/app/services/ci/expire_pipeline_cache_service.rb
+++ b/app/services/ci/expire_pipeline_cache_service.rb
@@ -86,7 +86,7 @@ module Ci
etag_paths << path
end
- pipeline.all_pipelines_in_hierarchy.includes(project: [:route, { namespace: :route }]).each do |relative_pipeline| # rubocop: disable CodeReuse/ActiveRecord
+ pipeline.upstream_and_all_downstreams.includes(project: [:route, { namespace: :route }]).each do |relative_pipeline| # rubocop: disable CodeReuse/ActiveRecord
etag_paths << project_pipeline_path(relative_pipeline.project, relative_pipeline)
etag_paths << graphql_pipeline_path(relative_pipeline)
etag_paths << graphql_pipeline_sha_path(relative_pipeline.sha)
diff --git a/app/services/ci/generate_coverage_reports_service.rb b/app/services/ci/generate_coverage_reports_service.rb
index 81f26e84ef8..8beecb79fd9 100644
--- a/app/services/ci/generate_coverage_reports_service.rb
+++ b/app/services/ci/generate_coverage_reports_service.rb
@@ -43,7 +43,7 @@ module Ci
end
def last_update_timestamp(pipeline_hierarchy)
- pipeline_hierarchy&.self_and_descendants&.maximum(:updated_at)
+ pipeline_hierarchy&.self_and_project_descendants&.maximum(:updated_at)
end
end
end
diff --git a/app/services/ci/job_artifacts/create_service.rb b/app/services/ci/job_artifacts/create_service.rb
index af56eb221d5..3dc097a8603 100644
--- a/app/services/ci/job_artifacts/create_service.rb
+++ b/app/services/ci/job_artifacts/create_service.rb
@@ -80,7 +80,7 @@ module Ci
Gitlab::CurrentSettings.current_application_settings.default_artifacts_expire_in
artifact_attributes = {
- job_id: job.id,
+ job: job,
project: project,
expire_in: expire_in
}
diff --git a/app/services/ci/job_artifacts/delete_service.rb b/app/services/ci/job_artifacts/delete_service.rb
new file mode 100644
index 00000000000..65cae03312e
--- /dev/null
+++ b/app/services/ci/job_artifacts/delete_service.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Ci
+ module JobArtifacts
+ class DeleteService
+ include BaseServiceUtility
+
+ def initialize(build)
+ @build = build
+ end
+
+ def execute
+ if build.project.refreshing_build_artifacts_size?
+ Gitlab::ProjectStatsRefreshConflictsLogger.warn_artifact_deletion_during_stats_refresh(
+ method: 'Ci::JobArtifacts::DeleteService#execute',
+ project_id: build.project_id
+ )
+ end
+
+ # fix_expire_at is false because in this case we want to explicitly delete the job artifacts
+ # this flag is a workaround that will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/355833
+ Ci::JobArtifacts::DestroyBatchService.new(build.job_artifacts.erasable, fix_expire_at: false).execute
+
+ ServiceResponse.success
+ end
+
+ private
+
+ attr_reader :build
+ end
+ end
+end
diff --git a/app/services/ci/job_artifacts/track_artifact_report_service.rb b/app/services/ci/job_artifacts/track_artifact_report_service.rb
new file mode 100644
index 00000000000..1be1d98394f
--- /dev/null
+++ b/app/services/ci/job_artifacts/track_artifact_report_service.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Ci
+ module JobArtifacts
+ class TrackArtifactReportService
+ include Gitlab::Utils::UsageData
+
+ REPORT_TRACKED = %i[test].freeze
+
+ def execute(pipeline)
+ REPORT_TRACKED.each do |report|
+ if pipeline.complete_and_has_reports?(Ci::JobArtifact.of_report_type(report))
+ track_usage_event(event_name(report), pipeline.user_id)
+ end
+ end
+ end
+
+ def event_name(report)
+ "i_testing_#{report}_report_uploaded"
+ end
+ end
+ end
+end
diff --git a/app/services/ci/pipeline_artifacts/coverage_report_service.rb b/app/services/ci/pipeline_artifacts/coverage_report_service.rb
index c11a8f7a0fd..99877603554 100644
--- a/app/services/ci/pipeline_artifacts/coverage_report_service.rb
+++ b/app/services/ci/pipeline_artifacts/coverage_report_service.rb
@@ -27,12 +27,18 @@ module Ci
end
def pipeline_artifact_params
- {
+ attributes = {
pipeline: pipeline,
file_type: :code_coverage,
file: carrierwave_file,
size: carrierwave_file['tempfile'].size
}
+
+ if ::Feature.enabled?(:ci_update_unlocked_pipeline_artifacts, pipeline.project)
+ attributes[:locked] = pipeline.locked
+ end
+
+ attributes
end
def carrierwave_file
diff --git a/app/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service.rb b/app/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service.rb
index d6865efac9f..aeb68a75f88 100644
--- a/app/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service.rb
+++ b/app/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service.rb
@@ -13,21 +13,31 @@ module Ci
return if pipeline.has_codequality_mr_diff_report?
return unless new_errors_introduced?
+ pipeline.pipeline_artifacts.create!(**artifact_attributes)
+ end
+
+ private
+
+ attr_reader :pipeline
+
+ def artifact_attributes
file = build_carrierwave_file!
- pipeline.pipeline_artifacts.create!(
+ attributes = {
project_id: pipeline.project_id,
file_type: :code_quality_mr_diff,
file_format: Ci::PipelineArtifact::REPORT_TYPES.fetch(:code_quality_mr_diff),
size: file["tempfile"].size,
file: file,
expire_at: Ci::PipelineArtifact::EXPIRATION_DATE.from_now
- )
- end
+ }
- private
+ if ::Feature.enabled?(:ci_update_unlocked_pipeline_artifacts, pipeline.project)
+ attributes[:locked] = pipeline.locked
+ end
- attr_reader :pipeline
+ attributes
+ end
def merge_requests
strong_memoize(:merge_requests) do
diff --git a/app/services/ci/pipelines/add_job_service.rb b/app/services/ci/pipelines/add_job_service.rb
index fc852bc3edd..dfbb37cf0dc 100644
--- a/app/services/ci/pipelines/add_job_service.rb
+++ b/app/services/ci/pipelines/add_job_service.rb
@@ -39,11 +39,13 @@ module Ci
job.pipeline = pipeline
job.project = pipeline.project
job.ref = pipeline.ref
+ job.partition_id = pipeline.partition_id
# update metadata since it might have been lazily initialised before this call
# metadata is present on `Ci::Processable`
if job.respond_to?(:metadata) && job.metadata
job.metadata.project = pipeline.project
+ job.metadata.partition_id = pipeline.partition_id
end
end
end
diff --git a/app/services/ci/queue/pending_builds_strategy.rb b/app/services/ci/queue/pending_builds_strategy.rb
index c8bdbba5e65..cfafe66d10b 100644
--- a/app/services/ci/queue/pending_builds_strategy.rb
+++ b/app/services/ci/queue/pending_builds_strategy.rb
@@ -19,7 +19,11 @@ module Ci
def builds_for_group_runner
return new_builds.none if runner.namespace_ids.empty?
- new_builds.where('ci_pending_builds.namespace_traversal_ids && ARRAY[?]::int[]', runner.namespace_ids)
+ new_builds_relation = new_builds.where('ci_pending_builds.namespace_traversal_ids && ARRAY[?]::int[]', runner.namespace_ids)
+
+ return order(new_builds_relation) if ::Feature.enabled?(:order_builds_for_group_runner)
+
+ new_builds_relation
end
def builds_matching_tag_ids(relation, ids)
diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb
index b357855db12..0bd4bf8cc86 100644
--- a/app/services/ci/register_job_service.rb
+++ b/app/services/ci/register_job_service.rb
@@ -287,7 +287,7 @@ module Ci
Gitlab::ErrorTracking.track_exception(ex,
build_id: build.id,
build_name: build.name,
- build_stage: build.stage,
+ build_stage: build.stage_name,
pipeline_id: build.pipeline_id,
project_id: build.project_id
)
diff --git a/app/services/ci/resource_groups/assign_resource_from_resource_group_service.rb b/app/services/ci/resource_groups/assign_resource_from_resource_group_service.rb
index dfd97498fc8..d7078200c14 100644
--- a/app/services/ci/resource_groups/assign_resource_from_resource_group_service.rb
+++ b/app/services/ci/resource_groups/assign_resource_from_resource_group_service.rb
@@ -9,8 +9,10 @@ module Ci
free_resources = resource_group.resources.free.count
- resource_group.upcoming_processables.take(free_resources).each do |processable|
- processable.enqueue_waiting_for_resource
+ resource_group.upcoming_processables.take(free_resources).each do |upcoming|
+ Gitlab::OptimisticLocking.retry_lock(upcoming, name: 'enqueue_waiting_for_resource') do |processable|
+ processable.enqueue_waiting_for_resource
+ end
end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/services/ci/runners/set_runner_associated_projects_service.rb b/app/services/ci/runners/set_runner_associated_projects_service.rb
new file mode 100644
index 00000000000..7930776749d
--- /dev/null
+++ b/app/services/ci/runners/set_runner_associated_projects_service.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+module Ci
+ module Runners
+ class SetRunnerAssociatedProjectsService
+ # @param [Ci::Runner] runner: the project runner to assign/unassign projects from
+ # @param [User] current_user: the user performing the operation
+ # @param [Array<Integer>] project_ids: the IDs of the associated projects to assign the runner to
+ def initialize(runner:, current_user:, project_ids:)
+ @runner = runner
+ @current_user = current_user
+ @project_ids = project_ids
+ end
+
+ def execute
+ unless current_user&.can?(:assign_runner, runner)
+ return ServiceResponse.error(message: 'user not allowed to assign runner', http_status: :forbidden)
+ end
+
+ return ServiceResponse.success if project_ids.blank?
+
+ set_associated_projects
+ end
+
+ private
+
+ def set_associated_projects
+ new_project_ids = [runner.owner_project.id] + project_ids
+
+ response = ServiceResponse.success
+ runner.transaction do
+ # rubocop:disable CodeReuse/ActiveRecord
+ current_project_ids = runner.projects.ids
+ # rubocop:enable CodeReuse/ActiveRecord
+
+ unless associate_new_projects(new_project_ids, current_project_ids)
+ response = ServiceResponse.error(message: 'failed to assign projects to runner')
+ raise ActiveRecord::Rollback, response.errors
+ end
+
+ unless disassociate_old_projects(new_project_ids, current_project_ids)
+ response = ServiceResponse.error(message: 'failed to destroy runner project')
+ raise ActiveRecord::Rollback, response.errors
+ end
+ end
+
+ response
+ end
+
+ def associate_new_projects(new_project_ids, current_project_ids)
+ missing_projects = Project.id_in(new_project_ids - current_project_ids)
+ missing_projects.all? { |project| runner.assign_to(project, current_user) }
+ end
+
+ def disassociate_old_projects(new_project_ids, current_project_ids)
+ projects_to_be_deleted = current_project_ids - new_project_ids
+ return true if projects_to_be_deleted.empty?
+
+ Ci::RunnerProject
+ .destroy_by(project_id: projects_to_be_deleted)
+ .all?(&:destroyed?)
+ end
+
+ attr_reader :runner, :current_user, :project_ids
+ end
+ end
+end
+
+Ci::Runners::SetRunnerAssociatedProjectsService.prepend_mod
diff --git a/app/services/ci/runners/update_runner_service.rb b/app/services/ci/runners/update_runner_service.rb
index 6cc080f81c2..bd01f52f396 100644
--- a/app/services/ci/runners/update_runner_service.rb
+++ b/app/services/ci/runners/update_runner_service.rb
@@ -9,11 +9,14 @@ module Ci
@runner = runner
end
- def update(params)
+ def execute(params)
params[:active] = !params.delete(:paused) if params.include?(:paused)
- runner.update(params).tap do |updated|
- runner.tick_runner_queue if updated
+ if runner.update(params)
+ runner.tick_runner_queue
+ ServiceResponse.success
+ else
+ ServiceResponse.error(message: runner.errors.full_messages)
end
end
end
diff --git a/app/services/ci/stuck_builds/drop_helpers.rb b/app/services/ci/stuck_builds/drop_helpers.rb
index dca50963883..f56c9aaeb55 100644
--- a/app/services/ci/stuck_builds/drop_helpers.rb
+++ b/app/services/ci/stuck_builds/drop_helpers.rb
@@ -48,7 +48,7 @@ module Ci
Gitlab::ErrorTracking.track_exception(ex,
build_id: build.id,
build_name: build.name,
- build_stage: build.stage,
+ build_stage: build.stage_name,
pipeline_id: build.pipeline_id,
project_id: build.project_id
)
diff --git a/app/services/ci/test_failure_history_service.rb b/app/services/ci/test_failure_history_service.rb
index 2214a6a2729..5a8072b2a0d 100644
--- a/app/services/ci/test_failure_history_service.rb
+++ b/app/services/ci/test_failure_history_service.rb
@@ -80,8 +80,8 @@ module Ci
end
def generate_test_suite!(build)
- # Returns an instance of Gitlab::Ci::Reports::TestSuite
- build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
+ test_report = build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new)
+ test_report.get_suite(build.test_suite_name)
end
def ci_unit_test_attrs(batch)
diff --git a/app/services/ci/unlock_artifacts_service.rb b/app/services/ci/unlock_artifacts_service.rb
index 30da31ba8ec..1fee31da4fc 100644
--- a/app/services/ci/unlock_artifacts_service.rb
+++ b/app/services/ci/unlock_artifacts_service.rb
@@ -7,9 +7,12 @@ module Ci
def execute(ci_ref, before_pipeline = nil)
results = {
unlocked_pipelines: 0,
- unlocked_job_artifacts: 0
+ unlocked_job_artifacts: 0,
+ unlocked_pipeline_artifacts: 0
}
+ unlock_pipeline_artifacts_enabled = ::Feature.enabled?(:ci_update_unlocked_pipeline_artifacts, ci_ref.project)
+
if ::Feature.enabled?(:ci_update_unlocked_job_artifacts, ci_ref.project)
loop do
unlocked_pipelines = []
@@ -18,6 +21,10 @@ module Ci
::Ci::Pipeline.transaction do
unlocked_pipelines = unlock_pipelines(ci_ref, before_pipeline)
unlocked_job_artifacts = unlock_job_artifacts(unlocked_pipelines)
+
+ if unlock_pipeline_artifacts_enabled
+ results[:unlocked_pipeline_artifacts] += unlock_pipeline_artifacts(unlocked_pipelines)
+ end
end
break if unlocked_pipelines.empty?
@@ -100,6 +107,14 @@ module Ci
)
end
+ # rubocop:disable CodeReuse/ActiveRecord
+ def unlock_pipeline_artifacts(pipelines)
+ return 0 if pipelines.empty?
+
+ ::Ci::PipelineArtifact.where(pipeline_id: pipelines.rows.flatten).update_all(locked: :unlocked)
+ end
+ # rubocop:enable CodeReuse/ActiveRecord
+
def unlock_pipelines(ci_ref, before_pipeline)
::Ci::Pipeline.connection.exec_query(unlock_pipelines_query(ci_ref, before_pipeline))
end
diff --git a/app/services/commits/create_service.rb b/app/services/commits/create_service.rb
index fc18420f6e4..a498d39d34e 100644
--- a/app/services/commits/create_service.rb
+++ b/app/services/commits/create_service.rb
@@ -66,7 +66,7 @@ module Commits
validate_on_branch!
validate_branch_existence!
- validate_new_branch_name! if different_branch?
+ validate_new_branch_name! if project.empty_repo? || different_branch?
end
def validate_permissions!
diff --git a/app/services/concerns/alert_management/alert_processing.rb b/app/services/concerns/alert_management/alert_processing.rb
index 8c6c7b15d28..9fe82507edd 100644
--- a/app/services/concerns/alert_management/alert_processing.rb
+++ b/app/services/concerns/alert_management/alert_processing.rb
@@ -113,7 +113,7 @@ module AlertManagement
end
def resolving_alert?
- incoming_payload.ends_at.present?
+ incoming_payload.resolved?
end
def notifying_alert?
@@ -121,7 +121,7 @@ module AlertManagement
end
def alert_source
- incoming_payload.monitoring_tool
+ incoming_payload.source
end
def logger
diff --git a/app/services/concerns/ci/downstream_pipeline_helpers.rb b/app/services/concerns/ci/downstream_pipeline_helpers.rb
index 39c0adb6e4e..26d7eb97151 100644
--- a/app/services/concerns/ci/downstream_pipeline_helpers.rb
+++ b/app/services/concerns/ci/downstream_pipeline_helpers.rb
@@ -5,7 +5,6 @@ module Ci
def log_downstream_pipeline_creation(downstream_pipeline)
return unless downstream_pipeline&.persisted?
- hierarchy_size = downstream_pipeline.all_pipelines_in_hierarchy.count
root_pipeline = downstream_pipeline.upstream_root
::Gitlab::AppLogger.info(
@@ -14,7 +13,7 @@ module Ci
root_pipeline_id: root_pipeline.id,
downstream_pipeline_id: downstream_pipeline.id,
downstream_pipeline_relationship: downstream_pipeline.parent_pipeline? ? :parent_child : :multi_project,
- hierarchy_size: hierarchy_size,
+ hierarchy_size: downstream_pipeline.complete_hierarchy_count,
root_pipeline_plan: root_pipeline.project.actual_plan_name,
root_pipeline_namespace_path: root_pipeline.project.namespace.full_path,
root_pipeline_project_path: root_pipeline.project.full_path
diff --git a/app/services/concerns/ci/job_token_scope/edit_scope_validations.rb b/app/services/concerns/ci/job_token_scope/edit_scope_validations.rb
index 23053975313..427aebf397e 100644
--- a/app/services/concerns/ci/job_token_scope/edit_scope_validations.rb
+++ b/app/services/concerns/ci/job_token_scope/edit_scope_validations.rb
@@ -9,10 +9,6 @@ module Ci
"not exist or you don't have permission to perform this action"
def validate_edit!(source_project, target_project, current_user)
- unless source_project.ci_job_token_scope_enabled?
- raise ValidationError, "Job token scope is disabled for this project"
- end
-
unless can?(current_user, :admin_project, source_project)
raise ValidationError, "Insufficient permissions to modify the job token scope"
end
diff --git a/app/services/concerns/projects/container_repository/gitlab/timeoutable.rb b/app/services/concerns/projects/container_repository/gitlab/timeoutable.rb
new file mode 100644
index 00000000000..095f5aa7cfa
--- /dev/null
+++ b/app/services/concerns/projects/container_repository/gitlab/timeoutable.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Projects
+ module ContainerRepository
+ module Gitlab
+ module Timeoutable
+ extend ActiveSupport::Concern
+
+ DISABLED_TIMEOUTS = [nil, 0].freeze
+
+ TimeoutError = Class.new(StandardError)
+
+ private
+
+ def timeout?(start_time)
+ return false if service_timeout.in?(DISABLED_TIMEOUTS)
+
+ (Time.zone.now - start_time) > service_timeout
+ end
+
+ def service_timeout
+ ::Gitlab::CurrentSettings.current_application_settings.container_registry_delete_tags_service_timeout
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/container_expiration_policies/cleanup_service.rb b/app/services/container_expiration_policies/cleanup_service.rb
index 34889e58127..1123b29f217 100644
--- a/app/services/container_expiration_policies/cleanup_service.rb
+++ b/app/services/container_expiration_policies/cleanup_service.rb
@@ -24,7 +24,7 @@ module ContainerExpirationPolicies
begin
service_result = Projects::ContainerRepository::CleanupTagsService
- .new(repository, nil, policy_params.merge('container_expiration_policy' => true))
+ .new(container_repository: repository, params: policy_params.merge('container_expiration_policy' => true))
.execute
rescue StandardError
repository.cleanup_unfinished!
diff --git a/app/services/deployments/update_environment_service.rb b/app/services/deployments/update_environment_service.rb
index 3cacedc7d6e..90a31ae9370 100644
--- a/app/services/deployments/update_environment_service.rb
+++ b/app/services/deployments/update_environment_service.rb
@@ -61,6 +61,12 @@ module Deployments
ExpandVariables.expand(environment_url, -> { variables.sort_and_expand_all })
end
+ def expanded_auto_stop_in
+ return unless auto_stop_in
+
+ ExpandVariables.expand(auto_stop_in, -> { variables.sort_and_expand_all })
+ end
+
def environment_url
environment_options[:url]
end
@@ -69,6 +75,10 @@ module Deployments
environment_options[:action] || 'start'
end
+ def auto_stop_in
+ deployable&.environment_auto_stop_in
+ end
+
def renew_external_url
if (url = expanded_environment_url)
environment.external_url = url
@@ -78,7 +88,9 @@ module Deployments
def renew_auto_stop_in
return unless deployable
- environment.auto_stop_in = deployable.environment_auto_stop_in
+ if (value = expanded_auto_stop_in)
+ environment.auto_stop_in = value
+ end
end
def renew_deployment_tier
diff --git a/app/services/design_management/copy_design_collection/copy_service.rb b/app/services/design_management/copy_design_collection/copy_service.rb
index 886077191ab..3bc30f62a81 100644
--- a/app/services/design_management/copy_design_collection/copy_service.rb
+++ b/app/services/design_management/copy_design_collection/copy_service.rb
@@ -143,7 +143,7 @@ module DesignManagement
gitaly_actions = version.actions.map do |action|
design = action.design
# Map the raw Action#event enum value to a Gitaly "action" for the
- # `Repository#multi_action` call.
+ # `Repository#commit_files` call.
gitaly_action_name = @event_enum_map[action.event_before_type_cast]
# `content` will be the LfsPointer file and not the design file,
# and can be nil for deletions.
@@ -157,7 +157,7 @@ module DesignManagement
}.compact
end
- sha = target_repository.multi_action(
+ sha = target_repository.commit_files(
git_user,
branch_name: temporary_branch,
message: commit_message(version),
diff --git a/app/services/design_management/delete_designs_service.rb b/app/services/design_management/delete_designs_service.rb
index 9ed03a994c4..921c904d8de 100644
--- a/app/services/design_management/delete_designs_service.rb
+++ b/app/services/design_management/delete_designs_service.rb
@@ -16,7 +16,8 @@ module DesignManagement
version = delete_designs!
EventCreateService.new.destroy_designs(designs, current_user)
- Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_designs_removed_action(author: current_user)
+ Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_designs_removed_action(author: current_user,
+ project: project)
TodosDestroyer::DestroyedDesignsWorker.perform_async(designs.map(&:id))
success(version: version)
diff --git a/app/services/design_management/runs_design_actions.rb b/app/services/design_management/runs_design_actions.rb
index ee6aa9286d3..267ed6bf29f 100644
--- a/app/services/design_management/runs_design_actions.rb
+++ b/app/services/design_management/runs_design_actions.rb
@@ -15,7 +15,7 @@ module DesignManagement
def run_actions(actions, skip_system_notes: false)
raise NoActions if actions.empty?
- sha = repository.multi_action(current_user,
+ sha = repository.commit_files(current_user,
branch_name: target_branch,
message: commit_message,
actions: actions.map(&:gitaly_action))
diff --git a/app/services/design_management/save_designs_service.rb b/app/services/design_management/save_designs_service.rb
index a1fce45434b..64537293e65 100644
--- a/app/services/design_management/save_designs_service.rb
+++ b/app/services/design_management/save_designs_service.rb
@@ -131,9 +131,11 @@ module DesignManagement
def track_usage_metrics(action)
if action == :update
- ::Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_designs_modified_action(author: current_user)
+ ::Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_designs_modified_action(author: current_user,
+ project: project)
else
- ::Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_designs_added_action(author: current_user)
+ ::Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_designs_added_action(author: current_user,
+ project: project)
end
::Gitlab::UsageDataCounters::DesignsCounter.count(action)
diff --git a/app/services/environments/stop_service.rb b/app/services/environments/stop_service.rb
index 75c878c9350..774e3ffe273 100644
--- a/app/services/environments/stop_service.rb
+++ b/app/services/environments/stop_service.rb
@@ -25,8 +25,19 @@ module Environments
def execute_for_merge_request_pipeline(merge_request)
return unless merge_request.actual_head_pipeline&.merge_request?
- merge_request.environments_in_head_pipeline(deployment_status: :success).each do |environment|
- execute(environment)
+ created_environments = merge_request.created_environments
+
+ if created_environments.any?
+ created_environments.each { |env| execute(env) }
+ else
+ environments_in_head_pipeline = merge_request.environments_in_head_pipeline(deployment_status: :success)
+ environments_in_head_pipeline.each { |env| execute(env) }
+
+ if environments_in_head_pipeline.any?
+ # If we don't see a message often, we'd be able to remove this path. (or likely in GitLab 16.0)
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/372965
+ Gitlab::AppJsonLogger.info(message: 'Running legacy dynamic environment stop logic', project_id: project.id)
+ end
end
end
diff --git a/app/services/files/multi_service.rb b/app/services/files/multi_service.rb
index 65af4dd5a28..dd09ecafb4f 100644
--- a/app/services/files/multi_service.rb
+++ b/app/services/files/multi_service.rb
@@ -38,7 +38,7 @@ module Files
end
def commit_actions!(actions)
- repository.multi_action(
+ repository.commit_files(
current_user,
message: @commit_message,
branch_name: @branch_name,
diff --git a/app/services/google_cloud/create_cloudsql_instance_service.rb b/app/services/google_cloud/create_cloudsql_instance_service.rb
index f7fca277c52..8d040c6c908 100644
--- a/app/services/google_cloud/create_cloudsql_instance_service.rb
+++ b/app/services/google_cloud/create_cloudsql_instance_service.rb
@@ -11,7 +11,7 @@ module GoogleCloud
trigger_instance_setup_worker
success
rescue Google::Apis::Error => err
- error(err.to_json)
+ error(err.message)
end
private
diff --git a/app/services/google_cloud/enable_cloudsql_service.rb b/app/services/google_cloud/enable_cloudsql_service.rb
index a466b2f3696..e4a411d0fab 100644
--- a/app/services/google_cloud/enable_cloudsql_service.rb
+++ b/app/services/google_cloud/enable_cloudsql_service.rb
@@ -12,6 +12,8 @@ module GoogleCloud
end
success({ gcp_project_ids: unique_gcp_project_ids })
+ rescue Google::Apis::Error => err
+ error(err.message)
end
private
diff --git a/app/services/google_cloud/fetch_google_ip_list_service.rb b/app/services/google_cloud/fetch_google_ip_list_service.rb
new file mode 100644
index 00000000000..f7739971603
--- /dev/null
+++ b/app/services/google_cloud/fetch_google_ip_list_service.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+module GoogleCloud
+ class FetchGoogleIpListService
+ include BaseServiceUtility
+
+ GOOGLE_IP_RANGES_URL = 'https://www.gstatic.com/ipranges/cloud.json'
+ RESPONSE_BODY_LIMIT = 1.megabyte
+ EXPECTED_CONTENT_TYPE = 'application/json'
+
+ IpListNotRetrievedError = Class.new(StandardError)
+
+ def execute
+ # Prevent too many workers from hitting the same HTTP endpoint
+ if ::Gitlab::ApplicationRateLimiter.throttled?(:fetch_google_ip_list, scope: nil)
+ return error("#{self.class} was rate limited")
+ end
+
+ subnets = fetch_and_update_cache!
+
+ Gitlab::AppJsonLogger.info(class: self.class.name,
+ message: 'Successfully retrieved Google IP list',
+ subnet_count: subnets.count)
+
+ success({ subnets: subnets })
+ rescue IpListNotRetrievedError => err
+ Gitlab::ErrorTracking.log_exception(err)
+ error('Google IP list not retrieved')
+ end
+
+ private
+
+ # Attempts to retrieve and parse the list of IPs from Google. Updates
+ # the internal cache so that the data is accessible.
+ #
+ # Returns an array of IPAddr objects consisting of subnets.
+ def fetch_and_update_cache!
+ parsed_response = fetch_google_ip_list
+
+ parse_google_prefixes(parsed_response).tap do |subnets|
+ ::ObjectStorage::CDN::GoogleIpCache.update!(subnets)
+ end
+ end
+
+ def fetch_google_ip_list
+ response = Gitlab::HTTP.get(GOOGLE_IP_RANGES_URL, follow_redirects: false, allow_local_requests: false)
+
+ validate_response!(response)
+
+ response.parsed_response
+ end
+
+ def validate_response!(response)
+ raise IpListNotRetrievedError, "response was #{response.code}" unless response.code == 200
+ raise IpListNotRetrievedError, "response was nil" unless response.body
+
+ parsed_response = response.parsed_response
+
+ unless response.content_type == EXPECTED_CONTENT_TYPE && parsed_response.is_a?(Hash)
+ raise IpListNotRetrievedError, "response was not JSON"
+ end
+
+ if response.body&.bytesize.to_i > RESPONSE_BODY_LIMIT
+ raise IpListNotRetrievedError, "response was too large: #{response.body.bytesize}"
+ end
+
+ prefixes = parsed_response['prefixes']
+
+ raise IpListNotRetrievedError, "JSON was type #{prefixes.class}, expected Array" unless prefixes.is_a?(Array)
+ raise IpListNotRetrievedError, "#{GOOGLE_IP_RANGES_URL} did not return any IP ranges" if prefixes.empty?
+
+ response.parsed_response
+ end
+
+ def parse_google_prefixes(parsed_response)
+ ranges = parsed_response['prefixes'].map do |prefix|
+ ip_range = prefix['ipv4Prefix'] || prefix['ipv6Prefix']
+
+ next unless ip_range
+
+ IPAddr.new(ip_range)
+ end.compact
+
+ raise IpListNotRetrievedError, "#{GOOGLE_IP_RANGES_URL} did not return any IP ranges" if ranges.empty?
+
+ ranges
+ end
+ end
+end
diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb
index 35716f7742a..d508865ef32 100644
--- a/app/services/groups/create_service.rb
+++ b/app/services/groups/create_service.rb
@@ -39,7 +39,7 @@ module Groups
if @group.save
@group.add_owner(current_user)
Integration.create_from_active_default_integrations(@group, :group_id)
- OnboardingProgress.onboard(@group)
+ Onboarding::Progress.onboard(@group)
end
end
diff --git a/app/services/import/github_service.rb b/app/services/import/github_service.rb
index ff5d5d2c4c1..53297d2412c 100644
--- a/app/services/import/github_service.rb
+++ b/app/services/import/github_service.rb
@@ -50,7 +50,7 @@ module Import
end
def project_name
- @project_name ||= params[:new_name].presence || repo.name
+ @project_name ||= params[:new_name].presence || repo[:name]
end
def namespace_path
@@ -66,13 +66,13 @@ module Import
end
def oversized?
- repository_size_limit > 0 && repo.size > repository_size_limit
+ repository_size_limit > 0 && repo[:size] > repository_size_limit
end
def oversize_error_message
_('"%{repository_name}" size (%{repository_size}) is larger than the limit of %{limit}.') % {
- repository_name: repo.name,
- repository_size: number_to_human_size(repo.size),
+ repository_name: repo[:name],
+ repository_size: number_to_human_size(repo[:size]),
limit: number_to_human_size(repository_size_limit)
}
end
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index acd6d45af7a..70ad97f8436 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -285,7 +285,7 @@ class IssuableBaseService < ::BaseProjectService
if issuable.changed? || params.present? || widget_params.present?
issuable.assign_attributes(allowed_update_params(params))
- if has_title_or_description_changed?(issuable)
+ if issuable.description_changed?
issuable.assign_attributes(last_edited_at: Time.current, last_edited_by: current_user)
end
@@ -398,10 +398,6 @@ class IssuableBaseService < ::BaseProjectService
update_task(issuable)
end
- def has_title_or_description_changed?(issuable)
- issuable.title_changed? || issuable.description_changed?
- end
-
def change_additional_attributes(issuable)
change_state(issuable)
change_subscription(issuable)
diff --git a/app/services/issuable_links/create_service.rb b/app/services/issuable_links/create_service.rb
index aca98596a02..2e9775af8c2 100644
--- a/app/services/issuable_links/create_service.rb
+++ b/app/services/issuable_links/create_service.rb
@@ -41,7 +41,7 @@ module IssuableLinks
set_link_type(link)
if link.changed? && link.save
- create_notes(referenced_issuable)
+ create_notes(link)
end
link
@@ -124,9 +124,9 @@ module IssuableLinks
:issue
end
- def create_notes(referenced_issuable)
- SystemNoteService.relate_issuable(issuable, referenced_issuable, current_user)
- SystemNoteService.relate_issuable(referenced_issuable, issuable, current_user)
+ def create_notes(issuable_link)
+ SystemNoteService.relate_issuable(issuable_link.source, issuable_link.target, current_user)
+ SystemNoteService.relate_issuable(issuable_link.target, issuable_link.source, current_user)
end
def linkable_issuables(objects)
diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb
index 61a95e49228..d75e74f3b19 100644
--- a/app/services/issues/base_service.rb
+++ b/app/services/issues/base_service.rb
@@ -28,9 +28,6 @@ module Issues
return if issue.relative_position.nil?
return if NO_REBALANCING_NEEDED.cover?(issue.relative_position)
- gates = [issue.project, issue.project.group].compact
- return unless gates.any? { |gate| Feature.enabled?(:rebalance_issues, gate) }
-
Issues::RebalancingWorker.perform_async(nil, *issue.project.self_or_root_group_ids)
end
diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb
index d08e4d12a92..da888386e0a 100644
--- a/app/services/issues/close_service.rb
+++ b/app/services/issues/close_service.rb
@@ -24,7 +24,7 @@ module Issues
return issue
end
- if perform_close(issue)
+ if issue.close(current_user)
event_service.close_issue(issue, current_user)
create_note(issue, closed_via) if system_note
@@ -40,7 +40,7 @@ module Issues
if closed_via.is_a?(MergeRequest)
store_first_mentioned_in_commit_at(issue, closed_via)
- OnboardingProgressService.new(project.namespace).execute(action: :issue_auto_closed)
+ Onboarding::ProgressService.new(project.namespace).execute(action: :issue_auto_closed)
end
delete_milestone_closed_issue_counter_cache(issue.milestone)
@@ -51,11 +51,6 @@ module Issues
private
- # Overridden on EE
- def perform_close(issue)
- issue.close(current_user)
- end
-
def can_close?(issue, skip_authorization: false)
skip_authorization || can?(current_user, :update_issue, issue) || issue.is_a?(ExternalIssue)
end
diff --git a/app/services/issues/export_csv_service.rb b/app/services/issues/export_csv_service.rb
index 6209127bd86..46e4b865dc3 100644
--- a/app/services/issues/export_csv_service.rb
+++ b/app/services/issues/export_csv_service.rb
@@ -5,20 +5,20 @@ module Issues
include Gitlab::Routing.url_helpers
include GitlabRoutingHelper
- def initialize(issuables_relation, project)
- super
+ def initialize(issuables_relation, project, user = nil)
+ super(issuables_relation, project)
@labels = @issuables.labels_hash.transform_values { |labels| labels.sort.join(',').presence }
end
- def email(user)
- Notify.issues_csv_email(user, project, csv_data, csv_builder.status).deliver_now
+ def email(mail_to_user)
+ Notify.issues_csv_email(mail_to_user, project, csv_data, csv_builder.status).deliver_now
end
private
def associations_to_preload
- %i(author assignees timelogs milestone project)
+ [:author, :assignees, :timelogs, :milestone, { project: { namespace: :route } }]
end
def header_to_value_hash
diff --git a/app/services/issues/relative_position_rebalancing_service.rb b/app/services/issues/relative_position_rebalancing_service.rb
index 23bb409f3cd..b5c10430e83 100644
--- a/app/services/issues/relative_position_rebalancing_service.rb
+++ b/app/services/issues/relative_position_rebalancing_service.rb
@@ -16,8 +16,6 @@ module Issues
end
def execute
- return unless Feature.enabled?(:rebalance_issues, root_namespace)
-
# Given can_start_rebalance? and track_new_running_rebalance are not atomic
# it can happen that we end up with more than Rebalancing::State::MAX_NUMBER_OF_CONCURRENT_REBALANCES running.
# Considering the number of allowed Rebalancing::State::MAX_NUMBER_OF_CONCURRENT_REBALANCES is small we should be ok,
diff --git a/app/services/issues/reopen_service.rb b/app/services/issues/reopen_service.rb
index e003ecacb3f..f4f81e9455a 100644
--- a/app/services/issues/reopen_service.rb
+++ b/app/services/issues/reopen_service.rb
@@ -5,7 +5,7 @@ module Issues
def execute(issue, skip_authorization: false)
return issue unless can_reopen?(issue, skip_authorization: skip_authorization)
- if perform_reopen(issue)
+ if issue.reopen
event_service.reopen_issue(issue, current_user)
create_note(issue, 'reopened')
notification_service.async.reopen_issue(issue, current_user)
@@ -22,11 +22,6 @@ module Issues
private
- # Overriden on EE
- def perform_reopen(issue)
- issue.reopen
- end
-
def can_reopen?(issue, skip_authorization: false)
skip_authorization || can?(current_user, :reopen_issue, issue)
end
diff --git a/app/services/labels/transfer_service.rb b/app/services/labels/transfer_service.rb
index 67163cb8122..a79e5b00232 100644
--- a/app/services/labels/transfer_service.rb
+++ b/app/services/labels/transfer_service.rb
@@ -40,9 +40,9 @@ module Labels
def labels_to_transfer
Label
.from_union([
- group_labels_applied_to_issues,
- group_labels_applied_to_merge_requests
- ])
+ group_labels_applied_to_issues,
+ group_labels_applied_to_merge_requests
+ ])
.reorder(nil)
.distinct
end
diff --git a/app/services/members/update_service.rb b/app/services/members/update_service.rb
index b4d1b80e5a3..8ef3e307519 100644
--- a/app/services/members/update_service.rb
+++ b/app/services/members/update_service.rb
@@ -7,6 +7,8 @@ module Members
raise Gitlab::Access::AccessDeniedError unless can?(current_user, action_member_permission(permission, member), member)
raise Gitlab::Access::AccessDeniedError if prevent_upgrade_to_owner?(member) || prevent_downgrade_from_owner?(member)
+ return success(member: member) if update_results_in_no_change?(member)
+
old_access_level = member.human_access
old_expiry = member.expires_at
@@ -26,6 +28,13 @@ module Members
private
+ def update_results_in_no_change?(member)
+ return false if params[:expires_at]&.to_date != member.expires_at
+ return false if params[:access_level] != member.access_level
+
+ true
+ end
+
def downgrading_to_guest?
params[:access_level] == Gitlab::Access::GUEST
end
diff --git a/app/services/merge_requests/after_create_service.rb b/app/services/merge_requests/after_create_service.rb
index 93a0d375b97..9d12eb80eb6 100644
--- a/app/services/merge_requests/after_create_service.rb
+++ b/app/services/merge_requests/after_create_service.rb
@@ -28,7 +28,7 @@ module MergeRequests
merge_request.diffs(include_stats: false).write_cache
merge_request.create_cross_references!(current_user)
- OnboardingProgressService.new(merge_request.target_project.namespace).execute(action: :merge_request_created)
+ Onboarding::ProgressService.new(merge_request.target_project.namespace).execute(action: :merge_request_created)
todo_service.new_merge_request(merge_request, current_user)
merge_request.cache_merge_request_closes_issues!(current_user)
diff --git a/app/services/merge_requests/approval_service.rb b/app/services/merge_requests/approval_service.rb
index dcc4cf4bb1e..64ae33c9b15 100644
--- a/app/services/merge_requests/approval_service.rb
+++ b/app/services/merge_requests/approval_service.rb
@@ -17,19 +17,11 @@ module MergeRequests
# utilizing the `Gitlab::EventStore`.
#
# Workers can subscribe to the `MergeRequests::ApprovedEvent`.
- if Feature.enabled?(:async_after_approval, project)
- Gitlab::EventStore.publish(
- MergeRequests::ApprovedEvent.new(
- data: { current_user_id: current_user.id, merge_request_id: merge_request.id }
- )
+ Gitlab::EventStore.publish(
+ MergeRequests::ApprovedEvent.new(
+ data: { current_user_id: current_user.id, merge_request_id: merge_request.id }
)
- else
- create_event(merge_request)
- stream_audit_event(merge_request)
- create_approval_note(merge_request)
- mark_pending_todos_as_done(merge_request)
- execute_approval_hooks(merge_request, current_user)
- end
+ )
success
end
@@ -37,7 +29,7 @@ module MergeRequests
private
def can_be_approved?(merge_request)
- current_user.can?(:approve_merge_request, merge_request)
+ merge_request.can_be_approved_by?(current_user)
end
def save_approval(approval)
@@ -49,29 +41,6 @@ module MergeRequests
def reset_approvals_cache(merge_request)
merge_request.approvals.reset
end
-
- def create_event(merge_request)
- event_service.approve_mr(merge_request, current_user)
- end
-
- def stream_audit_event(merge_request)
- # Defined in EE
- end
-
- def create_approval_note(merge_request)
- SystemNoteService.approve_mr(merge_request, current_user)
- end
-
- def mark_pending_todos_as_done(merge_request)
- todo_service.resolve_todos_for_target(merge_request, current_user)
- end
-
- def execute_approval_hooks(merge_request, current_user)
- # Only one approval is required for a merge request to be approved
- notification_service.async.approve_mr(merge_request, current_user)
-
- execute_hooks(merge_request, 'approved')
- end
end
end
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index bda8dc64ac0..6cefd9169f5 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -43,8 +43,6 @@ module MergeRequests
end
def handle_assignees_change(merge_request, old_assignees)
- bulk_update_assignees_state(merge_request, merge_request.assignees - old_assignees)
-
MergeRequests::HandleAssigneesChangeService
.new(project: project, current_user: current_user)
.async_execute(merge_request, old_assignees)
@@ -60,7 +58,6 @@ module MergeRequests
new_reviewers = merge_request.reviewers - old_reviewers
merge_request_activity_counter.track_users_review_requested(users: new_reviewers)
merge_request_activity_counter.track_reviewers_changed_action(user: current_user)
- bulk_update_reviewers_state(merge_request, new_reviewers)
end
def cleanup_environments(merge_request)
@@ -247,46 +244,6 @@ module MergeRequests
Milestones::MergeRequestsCountService.new(milestone).delete_cache
end
-
- def bulk_update_assignees_state(merge_request, new_assignees)
- return unless current_user.mr_attention_requests_enabled?
- return if new_assignees.empty?
-
- assignees_map = merge_request.merge_request_assignees_with(new_assignees).to_h do |assignee|
- state = if assignee.user_id == current_user&.id
- :unreviewed
- else
- merge_request.find_reviewer(assignee.assignee)&.state || :attention_requested
- end
-
- [
- assignee,
- { state: MergeRequestAssignee.states[state], updated_state_by_user_id: current_user.id }
- ]
- end
-
- ::Gitlab::Database::BulkUpdate.execute(%i[state updated_state_by_user_id], assignees_map)
- end
-
- def bulk_update_reviewers_state(merge_request, new_reviewers)
- return unless current_user.mr_attention_requests_enabled?
- return if new_reviewers.empty?
-
- reviewers_map = merge_request.merge_request_reviewers_with(new_reviewers).to_h do |reviewer|
- state = if reviewer.user_id == current_user&.id
- :unreviewed
- else
- merge_request.find_assignee(reviewer.reviewer)&.state || :attention_requested
- end
-
- [
- reviewer,
- { state: MergeRequestReviewer.states[state], updated_state_by_user_id: current_user.id }
- ]
- end
-
- ::Gitlab::Database::BulkUpdate.execute(%i[state updated_state_by_user_id], reviewers_map)
- end
end
end
diff --git a/app/services/merge_requests/ff_merge_service.rb b/app/services/merge_requests/ff_merge_service.rb
index c5640047899..6e1d1b6ad23 100644
--- a/app/services/merge_requests/ff_merge_service.rb
+++ b/app/services/merge_requests/ff_merge_service.rb
@@ -8,26 +8,22 @@ module MergeRequests
# Executed when you do fast-forward merge via GitLab UI
#
class FfMergeService < MergeRequests::MergeService
- private
+ extend ::Gitlab::Utils::Override
- def commit
- ff_merge = repository.ff_merge(current_user,
- source,
- merge_request.target_branch,
- merge_request: merge_request)
+ private
- if merge_request.squash_on_merge?
- merge_request.update_column(:squash_commit_sha, merge_request.in_progress_merge_commit_sha)
- end
+ override :execute_git_merge
+ def execute_git_merge
+ repository.ff_merge(current_user,
+ source,
+ merge_request.target_branch,
+ merge_request: merge_request)
+ end
- ff_merge
- rescue Gitlab::Git::PreReceiveError => e
- Gitlab::ErrorTracking.track_exception(e, pre_receive_message: e.raw_message, merge_request_id: merge_request&.id)
- raise MergeError, e.message
- rescue StandardError => e
- raise MergeError, "Something went wrong during merge: #{e.message}"
- ensure
- merge_request.update_and_mark_in_progress_merge_commit_sha(nil)
+ override :merge_success_data
+ def merge_success_data(commit_id)
+ # There is no merge commit to update, so this is just blank.
+ {}
end
end
end
diff --git a/app/services/merge_requests/handle_assignees_change_service.rb b/app/services/merge_requests/handle_assignees_change_service.rb
index 87cd6544406..51be4690af4 100644
--- a/app/services/merge_requests/handle_assignees_change_service.rb
+++ b/app/services/merge_requests/handle_assignees_change_service.rb
@@ -21,7 +21,7 @@ module MergeRequests
merge_request_activity_counter.track_users_assigned_to_mr(users: new_assignees)
merge_request_activity_counter.track_assignees_changed_action(user: current_user)
- execute_assignees_hooks(merge_request, old_assignees) if options[:execute_hooks]
+ execute_assignees_hooks(merge_request, old_assignees) if options['execute_hooks']
end
private
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index f51923b7035..6d31a29f5a7 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -92,15 +92,26 @@ module MergeRequests
raise_error(GENERIC_ERROR_MESSAGE)
end
- merge_request.update!(merge_commit_sha: commit_id)
+ update_merge_sha_metadata(commit_id)
+
+ commit_id
ensure
merge_request.update_and_mark_in_progress_merge_commit_sha(nil)
end
+ def update_merge_sha_metadata(commit_id)
+ data_to_update = merge_success_data(commit_id)
+ data_to_update[:squash_commit_sha] = source if merge_request.squash_on_merge?
+
+ merge_request.update!(**data_to_update) if data_to_update.present?
+ end
+
+ def merge_success_data(commit_id)
+ { merge_commit_sha: commit_id }
+ end
+
def try_merge
- repository.merge(current_user, source, merge_request, commit_message).tap do
- merge_request.update_column(:squash_commit_sha, source) if merge_request.squash_on_merge?
- end
+ execute_git_merge
rescue Gitlab::Git::PreReceiveError => e
raise MergeError,
"Something went wrong during merge pre-receive hook. #{e.message}".strip
@@ -109,6 +120,10 @@ module MergeRequests
raise_error(GENERIC_ERROR_MESSAGE)
end
+ def execute_git_merge
+ repository.merge(current_user, source, merge_request, commit_message)
+ end
+
def after_merge
log_info("Post merge started on JID #{merge_jid} with state #{state}")
MergeRequests::PostMergeService.new(project: project, current_user: current_user).execute(merge_request)
diff --git a/app/services/merge_requests/mergeability/detailed_merge_status_service.rb b/app/services/merge_requests/mergeability/detailed_merge_status_service.rb
new file mode 100644
index 00000000000..d25234183fd
--- /dev/null
+++ b/app/services/merge_requests/mergeability/detailed_merge_status_service.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+module MergeRequests
+ module Mergeability
+ class DetailedMergeStatusService
+ include ::Gitlab::Utils::StrongMemoize
+
+ def initialize(merge_request:)
+ @merge_request = merge_request
+ end
+
+ def execute
+ return :checking if checking?
+ return :unchecked if unchecked?
+
+ if check_results.success?
+
+ # If everything else is mergeable, but CI is not, the frontend expects two potential states to be returned
+ # See discussion: gitlab.com/gitlab-org/gitlab/-/merge_requests/96778#note_1093063523
+ if check_ci_results.success?
+ :mergeable
+ else
+ ci_check_failure_reason
+ end
+ else
+ check_results.failure_reason
+ end
+ end
+
+ private
+
+ attr_reader :merge_request, :checks, :ci_check
+
+ def checking?
+ merge_request.cannot_be_merged_rechecking? || merge_request.preparing? || merge_request.checking?
+ end
+
+ def unchecked?
+ merge_request.unchecked?
+ end
+
+ def check_results
+ strong_memoize(:check_results) do
+ merge_request.execute_merge_checks(params: { skip_ci_check: true })
+ end
+ end
+
+ def check_ci_results
+ strong_memoize(:check_ci_results) do
+ ::MergeRequests::Mergeability::CheckCiStatusService.new(merge_request: merge_request, params: {}).execute
+ end
+ end
+
+ def ci_check_failure_reason
+ if merge_request.actual_head_pipeline&.running?
+ :ci_still_running
+ else
+ check_ci_results.payload.fetch(:reason)
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/merge_requests/mergeability/logger.rb b/app/services/merge_requests/mergeability/logger.rb
new file mode 100644
index 00000000000..8b45d231e03
--- /dev/null
+++ b/app/services/merge_requests/mergeability/logger.rb
@@ -0,0 +1,103 @@
+# frozen_string_literal: true
+
+module MergeRequests
+ module Mergeability
+ class Logger
+ include Gitlab::Utils::StrongMemoize
+
+ def initialize(merge_request:, destination: Gitlab::AppJsonLogger)
+ @destination = destination
+ @merge_request = merge_request
+ end
+
+ def commit
+ return unless enabled?
+
+ commit_logs
+ end
+
+ def instrument(mergeability_name:)
+ raise ArgumentError, 'block not given' unless block_given?
+
+ return yield unless enabled?
+
+ op_start_db_counters = current_db_counter_payload
+ op_started_at = current_monotonic_time
+
+ result = yield
+
+ observe("mergeability.#{mergeability_name}.duration_s", current_monotonic_time - op_started_at)
+
+ observe_sql_counters(mergeability_name, op_start_db_counters, current_db_counter_payload)
+
+ result
+ end
+
+ private
+
+ attr_reader :destination, :merge_request
+
+ def observe(name, value)
+ return unless enabled?
+
+ observations[name.to_s].push(value)
+ end
+
+ def commit_logs
+ attributes = Gitlab::ApplicationContext.current.merge({
+ mergeability_project_id: merge_request.project.id
+ })
+
+ attributes[:mergeability_merge_request_id] = merge_request.id
+ attributes.merge!(observations_hash)
+ attributes.compact!
+ attributes.stringify_keys!
+
+ destination.info(attributes)
+ end
+
+ def observations_hash
+ transformed = observations.transform_values do |values|
+ next if values.empty?
+
+ {
+ 'values' => values
+ }
+ end.compact
+
+ transformed.each_with_object({}) do |key, hash|
+ key[1].each { |k, v| hash["#{key[0]}.#{k}"] = v }
+ end
+ end
+
+ def observations
+ strong_memoize(:observations) do
+ Hash.new { |hash, key| hash[key] = [] }
+ end
+ end
+
+ def observe_sql_counters(name, start_db_counters, end_db_counters)
+ end_db_counters.each do |key, value|
+ result = value - start_db_counters.fetch(key, 0)
+ next if result == 0
+
+ observe("mergeability.#{name}.#{key}", result)
+ end
+ end
+
+ def current_db_counter_payload
+ ::Gitlab::Metrics::Subscribers::ActiveRecord.db_counter_payload
+ end
+
+ def enabled?
+ strong_memoize(:enabled) do
+ ::Feature.enabled?(:mergeability_checks_logger, merge_request.project)
+ end
+ end
+
+ def current_monotonic_time
+ ::Gitlab::Metrics::System.monotonic_time
+ end
+ end
+ end
+end
diff --git a/app/services/merge_requests/mergeability/run_checks_service.rb b/app/services/merge_requests/mergeability/run_checks_service.rb
index 68f842b3322..7f205c8dd6c 100644
--- a/app/services/merge_requests/mergeability/run_checks_service.rb
+++ b/app/services/merge_requests/mergeability/run_checks_service.rb
@@ -15,12 +15,17 @@ module MergeRequests
next if check.skip?
- check_result = run_check(check)
+ check_result = logger.instrument(mergeability_name: check_class.to_s.demodulize.underscore) do
+ run_check(check)
+ end
+
result_hash << check_result
break result_hash if check_result.failed?
end
+ logger.commit
+
self
end
@@ -57,6 +62,12 @@ module MergeRequests
Gitlab::MergeRequests::Mergeability::ResultsStore.new(merge_request: merge_request)
end
end
+
+ def logger
+ strong_memoize(:logger) do
+ MergeRequests::Mergeability::Logger.new(merge_request: merge_request)
+ end
+ end
end
end
end
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index 5205d34baae..533d0052fb8 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -234,6 +234,7 @@ module MergeRequests
end
# Add comment about pushing new commits to merge requests and send nofitication emails
+ #
def notify_about_push(merge_request)
return unless @commits.present?
diff --git a/app/services/merge_requests/update_assignees_service.rb b/app/services/merge_requests/update_assignees_service.rb
index a6b0235c525..a13db52e34b 100644
--- a/app/services/merge_requests/update_assignees_service.rb
+++ b/app/services/merge_requests/update_assignees_service.rb
@@ -20,8 +20,6 @@ module MergeRequests
attrs = update_attrs.merge(assignee_ids: new_ids)
merge_request.update!(**attrs)
- bulk_update_assignees_state(merge_request, merge_request.assignees - old_assignees)
-
# Defer the more expensive operations (handle_assignee_changes) to the background
MergeRequests::HandleAssigneesChangeService
.new(project: project, current_user: current_user)
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 0902b5195a1..6d518edc88f 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -179,18 +179,16 @@ module MergeRequests
old_title_draft = MergeRequest.draft?(old_title)
new_title_draft = MergeRequest.draft?(new_title)
+ # notify the draft status changed. Added/removed message is handled in the
+ # email template itself, see `change_in_merge_request_draft_status_email` template.
+ notify_draft_status_changed(merge_request) if old_title_draft || new_title_draft
+
if !old_title_draft && new_title_draft
# Marked as Draft
- #
- merge_request_activity_counter
- .track_marked_as_draft_action(user: current_user)
+ merge_request_activity_counter.track_marked_as_draft_action(user: current_user)
elsif old_title_draft && !new_title_draft
# Unmarked as Draft
- #
- notify_draft_status_changed(merge_request)
-
- merge_request_activity_counter
- .track_unmarked_as_draft_action(user: current_user)
+ merge_request_activity_counter.track_unmarked_as_draft_action(user: current_user)
end
end
diff --git a/app/services/milestones/transfer_service.rb b/app/services/milestones/transfer_service.rb
index b9bd259ca8b..bbf6920f83b 100644
--- a/app/services/milestones/transfer_service.rb
+++ b/app/services/milestones/transfer_service.rb
@@ -35,10 +35,7 @@ module Milestones
# rubocop: disable CodeReuse/ActiveRecord
def milestones_to_transfer
- Milestone.from_union([
- group_milestones_applied_to_issues,
- group_milestones_applied_to_merge_requests
- ])
+ Milestone.from_union([group_milestones_applied_to_issues, group_milestones_applied_to_merge_requests])
.reorder(nil)
.distinct
end
diff --git a/app/services/namespaces/in_product_marketing_emails_service.rb b/app/services/namespaces/in_product_marketing_emails_service.rb
index c139b2e11dd..1ce7e4cae16 100644
--- a/app/services/namespaces/in_product_marketing_emails_service.rb
+++ b/app/services/namespaces/in_product_marketing_emails_service.rb
@@ -89,7 +89,7 @@ module Namespaces
end
def groups_for_track
- onboarding_progress_scope = OnboardingProgress
+ onboarding_progress_scope = Onboarding::Progress
.completed_actions_with_latest_in_range(completed_actions, range)
.incomplete_actions(incomplete_actions)
diff --git a/app/services/notification_recipients/builder/base.rb b/app/services/notification_recipients/builder/base.rb
index 0a7f25f1af3..3fabec29c0d 100644
--- a/app/services/notification_recipients/builder/base.rb
+++ b/app/services/notification_recipients/builder/base.rb
@@ -183,58 +183,6 @@ module NotificationRecipients
add_recipients(target.subscribers(project), :subscription, NotificationReason::SUBSCRIBED)
end
- # rubocop: disable CodeReuse/ActiveRecord
- def user_ids_notifiable_on(resource, notification_level = nil)
- return [] unless resource
-
- scope = resource.notification_settings
-
- if notification_level
- scope = scope.where(level: NotificationSetting.levels[notification_level])
- end
-
- scope.pluck(:user_id)
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- # Build a list of user_ids based on project notification settings
- def select_project_members_ids(global_setting, user_ids_global_level_watch)
- user_ids = user_ids_notifiable_on(project, :watch)
-
- # If project setting is global, add to watch list if global setting is watch
- user_ids + (global_setting & user_ids_global_level_watch)
- end
-
- # Build a list of user_ids based on group notification settings
- def select_group_members_ids(group, project_members, global_setting, user_ids_global_level_watch)
- uids = user_ids_notifiable_on(group, :watch)
-
- # Group setting is global, add to user_ids list if global setting is watch
- uids + (global_setting & user_ids_global_level_watch) - project_members
- end
-
- # rubocop: disable CodeReuse/ActiveRecord
- def user_ids_with_global_level_watch(ids)
- settings_with_global_level_of(:watch, ids).pluck(:user_id)
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- # rubocop: disable CodeReuse/ActiveRecord
- def user_ids_with_global_level_custom(ids, action)
- settings_with_global_level_of(:custom, ids).pluck(:user_id)
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- # rubocop: disable CodeReuse/ActiveRecord
- def settings_with_global_level_of(level, ids)
- NotificationSetting.where(
- user_id: ids,
- source_type: nil,
- level: NotificationSetting.levels[level]
- )
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
def add_labels_subscribers(labels: nil)
return unless target.respond_to? :labels
diff --git a/app/services/onboarding/progress_service.rb b/app/services/onboarding/progress_service.rb
new file mode 100644
index 00000000000..66f7f2bc33d
--- /dev/null
+++ b/app/services/onboarding/progress_service.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Onboarding
+ class ProgressService
+ class Async
+ attr_reader :namespace_id
+
+ def initialize(namespace_id)
+ @namespace_id = namespace_id
+ end
+
+ def execute(action:)
+ return unless Onboarding::Progress.not_completed?(namespace_id, action)
+
+ Namespaces::OnboardingProgressWorker.perform_async(namespace_id, action)
+ end
+ end
+
+ def self.async(namespace_id)
+ Async.new(namespace_id)
+ end
+
+ def initialize(namespace)
+ @namespace = namespace&.root_ancestor
+ end
+
+ def execute(action:)
+ return unless @namespace
+
+ Onboarding::Progress.register(@namespace, action)
+ end
+ end
+end
diff --git a/app/services/onboarding_progress_service.rb b/app/services/onboarding_progress_service.rb
deleted file mode 100644
index 6d44c0a61ea..00000000000
--- a/app/services/onboarding_progress_service.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-# frozen_string_literal: true
-
-class OnboardingProgressService
- class Async
- attr_reader :namespace_id
-
- def initialize(namespace_id)
- @namespace_id = namespace_id
- end
-
- def execute(action:)
- return unless OnboardingProgress.not_completed?(namespace_id, action)
-
- Namespaces::OnboardingProgressWorker.perform_async(namespace_id, action)
- end
- end
-
- def self.async(namespace_id)
- Async.new(namespace_id)
- end
-
- def initialize(namespace)
- @namespace = namespace&.root_ancestor
- end
-
- def execute(action:)
- return unless @namespace
-
- OnboardingProgress.register(@namespace, action)
- end
-end
diff --git a/app/services/packages/conan/search_service.rb b/app/services/packages/conan/search_service.rb
index 31ee9bea084..df22a895c00 100644
--- a/app/services/packages/conan/search_service.rb
+++ b/app/services/packages/conan/search_service.rb
@@ -44,7 +44,7 @@ module Packages
name, version, username, _ = query.split(%r{[@/]})
full_path = Packages::Conan::Metadatum.full_path_from(package_username: username)
project = Project.find_by_full_path(full_path)
- return unless Ability.allowed?(current_user, :read_package, project)
+ return unless Ability.allowed?(current_user, :read_package, project&.packages_policy_subject)
result = project.packages.with_name(name).with_version(version).order_created.last
[result&.conan_recipe].compact
diff --git a/app/services/packages/debian/generate_distribution_service.rb b/app/services/packages/debian/generate_distribution_service.rb
index 7db27f9234d..9b313202400 100644
--- a/app/services/packages/debian/generate_distribution_service.rb
+++ b/app/services/packages/debian/generate_distribution_service.rb
@@ -220,6 +220,7 @@ module Packages
valid_until_field,
rfc822_field('NotAutomatic', !@distribution.automatic, !@distribution.automatic),
rfc822_field('ButAutomaticUpgrades', @distribution.automatic_upgrades, !@distribution.automatic && @distribution.automatic_upgrades),
+ rfc822_field('Acquire-By-Hash', 'yes'),
rfc822_field('Architectures', @distribution.architectures.map { |architecture| architecture.name }.sort.join(' ')),
rfc822_field('Components', @distribution.components.map { |component| component.name }.sort.join(' ')),
rfc822_field('Description', @distribution.description)
diff --git a/app/services/packages/debian/process_changes_service.rb b/app/services/packages/debian/process_changes_service.rb
index b6e81012656..a29cbd3f65f 100644
--- a/app/services/packages/debian/process_changes_service.rb
+++ b/app/services/packages/debian/process_changes_service.rb
@@ -42,22 +42,30 @@ module Packages
def update_files_metadata
files.each do |filename, entry|
- entry.package_file.package = package
-
file_metadata = ::Packages::Debian::ExtractMetadataService.new(entry.package_file).execute
+ ::Packages::UpdatePackageFileService.new(entry.package_file, package_id: package.id)
+ .execute
+
+ # Force reload from database, as package has changed
+ entry.package_file.reload_package
+
entry.package_file.debian_file_metadatum.update!(
file_type: file_metadata[:file_type],
component: files[filename].component,
architecture: file_metadata[:architecture],
fields: file_metadata[:fields]
)
- entry.package_file.save!
end
end
def update_changes_metadata
- package_file.update!(package: package)
+ ::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: metadata[:file_type],
fields: metadata[:fields]
diff --git a/app/services/packages/rpm/repository_metadata/base_builder.rb b/app/services/packages/rpm/repository_metadata/base_builder.rb
new file mode 100644
index 00000000000..9d76336d764
--- /dev/null
+++ b/app/services/packages/rpm/repository_metadata/base_builder.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+module Packages
+ module Rpm
+ module RepositoryMetadata
+ class BaseBuilder
+ def execute
+ build_empty_structure
+ end
+
+ private
+
+ def build_empty_structure
+ Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
+ xml.public_send(self.class::ROOT_TAG, self.class::ROOT_ATTRIBUTES) # rubocop:disable GitlabSecurity/PublicSend
+ end.to_xml
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/packages/rpm/repository_metadata/build_filelist_xml.rb b/app/services/packages/rpm/repository_metadata/build_filelist_xml.rb
new file mode 100644
index 00000000000..01fb36f4b91
--- /dev/null
+++ b/app/services/packages/rpm/repository_metadata/build_filelist_xml.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+module Packages
+ module Rpm
+ module RepositoryMetadata
+ class BuildFilelistXml < ::Packages::Rpm::RepositoryMetadata::BaseBuilder
+ ROOT_TAG = 'filelists'
+ ROOT_ATTRIBUTES = {
+ xmlns: 'http://linux.duke.edu/metadata/filelists',
+ packages: '0'
+ }.freeze
+ end
+ end
+ end
+end
diff --git a/app/services/packages/rpm/repository_metadata/build_other_xml.rb b/app/services/packages/rpm/repository_metadata/build_other_xml.rb
new file mode 100644
index 00000000000..4bf61c901a3
--- /dev/null
+++ b/app/services/packages/rpm/repository_metadata/build_other_xml.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+module Packages
+ module Rpm
+ module RepositoryMetadata
+ class BuildOtherXml < ::Packages::Rpm::RepositoryMetadata::BaseBuilder
+ ROOT_TAG = 'otherdata'
+ ROOT_ATTRIBUTES = {
+ xmlns: 'http://linux.duke.edu/metadata/other',
+ packages: '0'
+ }.freeze
+ end
+ end
+ end
+end
diff --git a/app/services/packages/rpm/repository_metadata/build_primary_xml.rb b/app/services/packages/rpm/repository_metadata/build_primary_xml.rb
new file mode 100644
index 00000000000..affb41677c2
--- /dev/null
+++ b/app/services/packages/rpm/repository_metadata/build_primary_xml.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+module Packages
+ module Rpm
+ module RepositoryMetadata
+ class BuildPrimaryXml < ::Packages::Rpm::RepositoryMetadata::BaseBuilder
+ ROOT_TAG = 'metadata'
+ ROOT_ATTRIBUTES = {
+ xmlns: 'http://linux.duke.edu/metadata/common',
+ 'xmlns:rpm': 'http://linux.duke.edu/metadata/rpm',
+ packages: '0'
+ }.freeze
+ end
+ end
+ end
+end
diff --git a/app/services/packages/rpm/repository_metadata/build_repomd_xml.rb b/app/services/packages/rpm/repository_metadata/build_repomd_xml.rb
new file mode 100644
index 00000000000..c6cfd77815d
--- /dev/null
+++ b/app/services/packages/rpm/repository_metadata/build_repomd_xml.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+module Packages
+ module Rpm
+ module RepositoryMetadata
+ class BuildRepomdXml
+ attr_reader :data
+
+ ROOT_ATTRIBUTES = {
+ xmlns: 'http://linux.duke.edu/metadata/repo',
+ 'xmlns:rpm': 'http://linux.duke.edu/metadata/rpm'
+ }.freeze
+
+ # Expected `data` structure
+ #
+ # data = {
+ # filelists: {
+ # checksum: { type: "sha256", value: "123" },
+ # location: { href: "repodata/123-filelists.xml.gz" },
+ # ...
+ # },
+ # ...
+ # }
+ def initialize(data)
+ @data = data
+ end
+
+ def execute
+ build_repomd
+ end
+
+ private
+
+ def build_repomd
+ Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
+ xml.repomd(ROOT_ATTRIBUTES) do
+ xml.revision Time.now.to_i
+ build_data_info(xml)
+ end
+ end.to_xml
+ end
+
+ def build_data_info(xml)
+ data.each do |filename, info|
+ xml.data(type: filename) do
+ build_file_info(info, xml)
+ end
+ end
+ end
+
+ def build_file_info(info, xml)
+ info.each do |key, attributes|
+ value = attributes.delete(:value)
+ xml.public_send(key, value, attributes) # rubocop:disable GitlabSecurity/PublicSend
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/packages/rubygems/dependency_resolver_service.rb b/app/services/packages/rubygems/dependency_resolver_service.rb
index c44b26e2b92..839a7683632 100644
--- a/app/services/packages/rubygems/dependency_resolver_service.rb
+++ b/app/services/packages/rubygems/dependency_resolver_service.rb
@@ -8,7 +8,10 @@ module Packages
DEFAULT_PLATFORM = 'ruby'
def execute
- return ServiceResponse.error(message: "forbidden", http_status: :forbidden) unless Ability.allowed?(current_user, :read_package, project)
+ unless Ability.allowed?(current_user, :read_package, project&.packages_policy_subject)
+ return ServiceResponse.error(message: "forbidden", http_status: :forbidden)
+ end
+
return ServiceResponse.error(message: "#{gem_name} not found", http_status: :not_found) if packages.empty?
payload = packages.map do |package|
diff --git a/app/services/post_receive_service.rb b/app/services/post_receive_service.rb
index 15c978e6763..c376b4036f8 100644
--- a/app/services/post_receive_service.rb
+++ b/app/services/post_receive_service.rb
@@ -101,7 +101,7 @@ class PostReceiveService
def record_onboarding_progress
return unless project
- OnboardingProgressService.new(project.namespace).execute(action: :git_write)
+ Onboarding::ProgressService.new(project.namespace).execute(action: :git_write)
end
end
diff --git a/app/services/projects/alerting/notify_service.rb b/app/services/projects/alerting/notify_service.rb
index c21a61bcb52..9403c7bcfed 100644
--- a/app/services/projects/alerting/notify_service.rb
+++ b/app/services/projects/alerting/notify_service.rb
@@ -2,14 +2,13 @@
module Projects
module Alerting
- class NotifyService
+ class NotifyService < ::BaseProjectService
extend ::Gitlab::Utils::Override
include ::AlertManagement::AlertProcessing
include ::AlertManagement::Responses
- def initialize(project, payload)
- @project = project
- @payload = payload
+ def initialize(project, params)
+ super(project: project, params: params.to_h)
end
def execute(token, integration = nil)
@@ -29,15 +28,11 @@ module Projects
private
- attr_reader :project, :payload, :integration
+ attr_reader :integration
+ alias_method :payload, :params
def valid_payload_size?
- Gitlab::Utils::DeepSize.new(payload.to_h).valid?
- end
-
- override :alert_source
- def alert_source
- super || integration&.name || 'Generic Alert Endpoint'
+ Gitlab::Utils::DeepSize.new(params).valid?
end
def active_integration?
diff --git a/app/services/projects/blame_service.rb b/app/services/projects/blame_service.rb
index b324ea27360..57b913b04e6 100644
--- a/app/services/projects/blame_service.rb
+++ b/app/services/projects/blame_service.rb
@@ -10,6 +10,7 @@ module Projects
@blob = blob
@commit = commit
@page = extract_page(params)
+ @pagination_enabled = pagination_state(params)
end
attr_reader :page
@@ -19,7 +20,7 @@ module Projects
end
def pagination
- return unless pagination_enabled?
+ return unless pagination_enabled
Kaminari.paginate_array([], total_count: blob_lines_count, limit: per_page)
.tap { |pagination| pagination.max_paginates_per(per_page) }
@@ -28,10 +29,10 @@ module Projects
private
- attr_reader :blob, :commit
+ attr_reader :blob, :commit, :pagination_enabled
def blame_range
- return unless pagination_enabled?
+ return unless pagination_enabled
first_line = (page - 1) * per_page + 1
last_line = (first_line + per_page).to_i - 1
@@ -51,6 +52,12 @@ module Projects
PER_PAGE
end
+ def pagination_state(params)
+ return false if Gitlab::Utils.to_boolean(params[:no_pagination], default: false)
+
+ Feature.enabled?(:blame_page_pagination, commit.project)
+ end
+
def overlimit?(page)
page * per_page >= blob_lines_count + per_page
end
@@ -58,9 +65,5 @@ module Projects
def blob_lines_count
@blob_lines_count ||= blob.data.lines.count
end
-
- def pagination_enabled?
- Feature.enabled?(:blame_page_pagination, commit.project)
- end
end
end
diff --git a/app/services/projects/container_repository/base_container_repository_service.rb b/app/services/projects/container_repository/base_container_repository_service.rb
new file mode 100644
index 00000000000..d7539737e78
--- /dev/null
+++ b/app/services/projects/container_repository/base_container_repository_service.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Projects
+ module ContainerRepository
+ class BaseContainerRepositoryService < ::BaseContainerService
+ include ::Gitlab::Utils::StrongMemoize
+
+ alias_method :container_repository, :container
+
+ def initialize(container_repository:, current_user: nil, params: {})
+ super(container: container_repository, current_user: current_user, params: params)
+ end
+
+ delegate :project, to: :container_repository
+ end
+ end
+end
diff --git a/app/services/projects/container_repository/cleanup_tags_base_service.rb b/app/services/projects/container_repository/cleanup_tags_base_service.rb
new file mode 100644
index 00000000000..8ea4ae4830a
--- /dev/null
+++ b/app/services/projects/container_repository/cleanup_tags_base_service.rb
@@ -0,0 +1,119 @@
+# frozen_string_literal: true
+
+module Projects
+ module ContainerRepository
+ class CleanupTagsBaseService < BaseContainerRepositoryService
+ private
+
+ def filter_out_latest!(tags)
+ tags.reject!(&:latest?)
+ end
+
+ def filter_by_name!(tags)
+ regex_delete = ::Gitlab::UntrustedRegexp.new("\\A#{name_regex_delete || name_regex}\\z")
+ regex_retain = ::Gitlab::UntrustedRegexp.new("\\A#{name_regex_keep}\\z")
+
+ tags.select! do |tag|
+ # regex_retain will override any overlapping matches by regex_delete
+ regex_delete.match?(tag.name) && !regex_retain.match?(tag.name)
+ end
+ end
+
+ # Should return [tags_to_delete, tags_to_keep]
+ def partition_by_keep_n(tags)
+ return [tags, []] unless keep_n
+
+ tags = order_by_date_desc(tags)
+
+ tags.partition.with_index { |_, index| index >= keep_n_as_integer }
+ end
+
+ # Should return [tags_to_delete, tags_to_keep]
+ def partition_by_older_than(tags)
+ return [tags, []] unless older_than
+
+ older_than_timestamp = older_than_in_seconds.ago
+
+ tags.partition do |tag|
+ timestamp = pushed_at(tag)
+
+ timestamp && timestamp < older_than_timestamp
+ end
+ end
+
+ def order_by_date_desc(tags)
+ now = DateTime.current
+ tags.sort_by! { |tag| pushed_at(tag) || now }
+ .reverse!
+ end
+
+ def delete_tags(tags)
+ return success(deleted: []) unless tags.any?
+
+ service = Projects::ContainerRepository::DeleteTagsService.new(
+ project,
+ current_user,
+ tags: tags.map(&:name),
+ container_expiration_policy: container_expiration_policy
+ )
+
+ service.execute(container_repository)
+ end
+
+ def can_destroy?
+ return true if container_expiration_policy
+
+ can?(current_user, :destroy_container_image, project)
+ end
+
+ def valid_regex?
+ %w[name_regex_delete name_regex name_regex_keep].each do |param_name|
+ regex = params[param_name]
+ ::Gitlab::UntrustedRegexp.new(regex) unless regex.blank?
+ end
+ true
+ rescue RegexpError => e
+ ::Gitlab::ErrorTracking.log_exception(e, project_id: project.id)
+ false
+ end
+
+ def older_than
+ params['older_than']
+ end
+
+ def name_regex_delete
+ params['name_regex_delete']
+ end
+
+ def name_regex
+ params['name_regex']
+ end
+
+ def name_regex_keep
+ params['name_regex_keep']
+ end
+
+ def container_expiration_policy
+ params['container_expiration_policy']
+ end
+
+ def keep_n
+ params['keep_n']
+ end
+
+ def project
+ container_repository.project
+ end
+
+ def keep_n_as_integer
+ keep_n.to_i
+ end
+
+ def older_than_in_seconds
+ strong_memoize(:older_than_in_seconds) do
+ ChronicDuration.parse(older_than).seconds
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/projects/container_repository/cleanup_tags_service.rb b/app/services/projects/container_repository/cleanup_tags_service.rb
index 0a8e8e72766..285c3e252ef 100644
--- a/app/services/projects/container_repository/cleanup_tags_service.rb
+++ b/app/services/projects/container_repository/cleanup_tags_service.rb
@@ -2,39 +2,33 @@
module Projects
module ContainerRepository
- class CleanupTagsService
- include BaseServiceUtility
- include ::Gitlab::Utils::StrongMemoize
+ class CleanupTagsService < CleanupTagsBaseService
+ def initialize(container_repository:, current_user: nil, params: {})
+ super
- def initialize(container_repository, user = nil, params = {})
- @container_repository = container_repository
- @current_user = user
@params = params.dup
-
- @project = container_repository.project
- @tags = container_repository.tags
- tags_size = @tags.size
- @counts = {
- original_size: tags_size,
- cached_tags_count: 0
- }
+ @counts = { cached_tags_count: 0 }
end
def execute
return error('access denied') unless can_destroy?
return error('invalid regex') unless valid_regex?
- filter_out_latest
- filter_by_name
+ tags = container_repository.tags
+ @counts[:original_size] = tags.size
+
+ filter_out_latest!(tags)
+ filter_by_name!(tags)
+
+ tags = truncate(tags)
+ populate_from_cache(tags)
- truncate
- populate_from_cache
+ tags = filter_keep_n(tags)
+ tags = filter_by_older_than(tags)
- filter_keep_n
- filter_by_older_than
+ @counts[:before_delete_size] = tags.size
- delete_tags.merge(@counts).tap do |result|
- result[:before_delete_size] = @tags.size
+ delete_tags(tags).merge(@counts).tap do |result|
result[:deleted_size] = result[:deleted]&.size
result[:status] = :error if @counts[:before_truncate_size] != @counts[:after_truncate_size]
@@ -43,94 +37,45 @@ module Projects
private
- def delete_tags
- return success(deleted: []) unless @tags.any?
-
- service = Projects::ContainerRepository::DeleteTagsService.new(
- @project,
- @current_user,
- tags: @tags.map(&:name),
- container_expiration_policy: container_expiration_policy
- )
-
- service.execute(@container_repository)
- end
-
- def filter_out_latest
- @tags.reject!(&:latest?)
- end
-
- def order_by_date
- now = DateTime.current
- @tags.sort_by! { |tag| tag.created_at || now }
- .reverse!
- end
+ def filter_keep_n(tags)
+ tags, tags_to_keep = partition_by_keep_n(tags)
- def filter_by_name
- regex_delete = ::Gitlab::UntrustedRegexp.new("\\A#{name_regex_delete || name_regex}\\z")
- regex_retain = ::Gitlab::UntrustedRegexp.new("\\A#{name_regex_keep}\\z")
-
- @tags.select! do |tag|
- # regex_retain will override any overlapping matches by regex_delete
- regex_delete.match?(tag.name) && !regex_retain.match?(tag.name)
- end
- end
-
- def filter_keep_n
- return unless keep_n
+ cache_tags(tags_to_keep)
- order_by_date
- cache_tags(@tags.first(keep_n_as_integer))
- @tags = @tags.drop(keep_n_as_integer)
+ tags
end
- def filter_by_older_than
- return unless older_than
-
- older_than_timestamp = older_than_in_seconds.ago
-
- @tags, tags_to_keep = @tags.partition do |tag|
- tag.created_at && tag.created_at < older_than_timestamp
- end
+ def filter_by_older_than(tags)
+ tags, tags_to_keep = partition_by_older_than(tags)
cache_tags(tags_to_keep)
- end
- def can_destroy?
- return true if container_expiration_policy
-
- can?(@current_user, :destroy_container_image, @project)
+ tags
end
- def valid_regex?
- %w(name_regex_delete name_regex name_regex_keep).each do |param_name|
- regex = @params[param_name]
- ::Gitlab::UntrustedRegexp.new(regex) unless regex.blank?
- end
- true
- rescue RegexpError => e
- ::Gitlab::ErrorTracking.log_exception(e, project_id: @project.id)
- false
+ def pushed_at(tag)
+ tag.created_at
end
- def truncate
- @counts[:before_truncate_size] = @tags.size
- @counts[:after_truncate_size] = @tags.size
+ def truncate(tags)
+ @counts[:before_truncate_size] = tags.size
+ @counts[:after_truncate_size] = tags.size
- return if max_list_size == 0
+ return tags if max_list_size == 0
# truncate the list to make sure that after the #filter_keep_n
# execution, the resulting list will be max_list_size
truncated_size = max_list_size + keep_n_as_integer
- return if @tags.size <= truncated_size
+ return tags if tags.size <= truncated_size
- @tags = @tags.sample(truncated_size)
- @counts[:after_truncate_size] = @tags.size
+ tags = tags.sample(truncated_size)
+ @counts[:after_truncate_size] = tags.size
+ tags
end
- def populate_from_cache
- @counts[:cached_tags_count] = cache.populate(@tags) if caching_enabled?
+ def populate_from_cache(tags)
+ @counts[:cached_tags_count] = cache.populate(tags) if caching_enabled?
end
def cache_tags(tags)
@@ -139,7 +84,7 @@ module Projects
def cache
strong_memoize(:cache) do
- ::Gitlab::ContainerRepository::Tags::Cache.new(@container_repository)
+ ::Gitlab::ContainerRepository::Tags::Cache.new(container_repository)
end
end
@@ -153,40 +98,6 @@ module Projects
def max_list_size
::Gitlab::CurrentSettings.current_application_settings.container_registry_cleanup_tags_service_max_list_size.to_i
end
-
- def keep_n
- @params['keep_n']
- end
-
- def keep_n_as_integer
- keep_n.to_i
- end
-
- def older_than_in_seconds
- strong_memoize(:older_than_in_seconds) do
- ChronicDuration.parse(older_than).seconds
- end
- end
-
- def older_than
- @params['older_than']
- end
-
- def name_regex_delete
- @params['name_regex_delete']
- end
-
- def name_regex
- @params['name_regex']
- end
-
- def name_regex_keep
- @params['name_regex_keep']
- end
-
- def container_expiration_policy
- @params['container_expiration_policy']
- end
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
new file mode 100644
index 00000000000..81bb94c867a
--- /dev/null
+++ b/app/services/projects/container_repository/gitlab/cleanup_tags_service.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+module Projects
+ module ContainerRepository
+ module Gitlab
+ class CleanupTagsService < CleanupTagsBaseService
+ include ::Projects::ContainerRepository::Gitlab::Timeoutable
+
+ TAGS_PAGE_SIZE = 1000
+
+ def initialize(container_repository:, current_user: nil, params: {})
+ super
+ @params = params.dup
+ end
+
+ def execute
+ return error('access denied') unless can_destroy?
+ return error('invalid regex') unless valid_regex?
+
+ with_timeout do |start_time, result|
+ container_repository.each_tags_page(page_size: TAGS_PAGE_SIZE) do |tags|
+ execute_for_tags(tags, result)
+
+ raise TimeoutError if timeout?(start_time)
+ end
+ end
+ end
+
+ private
+
+ def execute_for_tags(tags, overall_result)
+ original_size = tags.size
+
+ filter_out_latest!(tags)
+ filter_by_name!(tags)
+
+ tags = filter_by_keep_n(tags)
+ tags = filter_by_older_than(tags)
+
+ overall_result[:before_delete_size] += tags.size
+ overall_result[:original_size] += original_size
+
+ result = delete_tags(tags)
+
+ overall_result[:deleted_size] += result[:deleted]&.size
+ overall_result[:deleted] += result[:deleted]
+ overall_result[:status] = result[:status] unless overall_result[:status] == :error
+ end
+
+ def with_timeout
+ result = {
+ original_size: 0,
+ before_delete_size: 0,
+ deleted_size: 0,
+ deleted: []
+ }
+
+ yield Time.zone.now, result
+
+ result
+ rescue TimeoutError
+ result[:status] = :error
+
+ result
+ end
+
+ def filter_by_keep_n(tags)
+ partition_by_keep_n(tags).first
+ end
+
+ def filter_by_older_than(tags)
+ partition_by_older_than(tags).first
+ end
+
+ def pushed_at(tag)
+ tag.updated_at || tag.created_at
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/projects/container_repository/gitlab/delete_tags_service.rb b/app/services/projects/container_repository/gitlab/delete_tags_service.rb
index 81cef554dec..530cf87c338 100644
--- a/app/services/projects/container_repository/gitlab/delete_tags_service.rb
+++ b/app/services/projects/container_repository/gitlab/delete_tags_service.rb
@@ -6,10 +6,7 @@ module Projects
class DeleteTagsService
include BaseServiceUtility
include ::Gitlab::Utils::StrongMemoize
-
- DISABLED_TIMEOUTS = [nil, 0].freeze
-
- TimeoutError = Class.new(StandardError)
+ include ::Projects::ContainerRepository::Gitlab::Timeoutable
def initialize(container_repository, tag_names)
@container_repository = container_repository
@@ -44,16 +41,6 @@ module Projects
@deleted_tags.any? ? success(deleted: @deleted_tags) : error('could not delete tags')
end
-
- def timeout?(start_time)
- return false if service_timeout.in?(DISABLED_TIMEOUTS)
-
- (Time.zone.now - start_time) > service_timeout
- end
-
- def service_timeout
- ::Gitlab::CurrentSettings.current_application_settings.container_registry_delete_tags_service_timeout
- end
end
end
end
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index 6381ee67ce7..c72f9b4b602 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -96,7 +96,7 @@ module Projects
log_info("#{current_user.name} created a new project \"#{@project.full_name}\"")
if @project.import?
- experiment(:combined_registration, user: current_user).track(:import_project)
+ Gitlab::Tracking.event(self.class.name, 'import_project', user: current_user)
else
# Skip writing the config for project imports/forks because it
# will always fail since the Git directory doesn't exist until
@@ -158,14 +158,25 @@ module Projects
priority: UserProjectAccessChangedService::LOW_PRIORITY
)
else
- @project.add_owner(@project.namespace.owner, current_user: current_user)
+ owner_user = @project.namespace.owner
+ owner_member = @project.add_owner(owner_user, current_user: current_user)
+
+ # There is a possibility that the sidekiq job to refresh the authorizations of the owner_user in this project
+ # isn't picked up (or finished) by the time the user is redirected to the newly created project's page.
+ # If that happens, the user will hit a 404. To avoid that scenario, we manually create a `project_authorizations` record for the user here.
+ if owner_member.persisted?
+ owner_user.project_authorizations.safe_find_or_create_by(
+ project: @project,
+ access_level: ProjectMember::OWNER
+ )
+ end
# During the process of adding a project owner, a check on permissions is made on the user which caches
# the max member access for that user on this project.
# Since that is `0` before the member is created - and we are still inside the request
# cycle when we need to do other operations that might check those permissions (e.g. write a commit)
# we need to purge that cache so that the updated permissions is fetched instead of using the outdated cached value of 0
# from before member creation
- @project.team.purge_member_access_cache_for_user_id(@project.namespace.owner.id)
+ @project.team.purge_member_access_cache_for_user_id(owner_user.id)
end
end
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index 06a44b07f9f..f1525ed9763 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -67,9 +67,9 @@ module Projects
end
def remove_snippets
- # We're setting the hard_delete param because we dont need to perform the access checks within the service since
+ # We're setting the skip_authorization param because we dont need to perform the access checks within the service since
# the user has enough access rights to remove the project and its resources.
- response = ::Snippets::BulkDestroyService.new(current_user, project.snippets).execute(hard_delete: true)
+ response = ::Snippets::BulkDestroyService.new(current_user, project.snippets).execute(skip_authorization: true)
if response.error?
log_error("Snippet deletion failed on #{project.full_path} with the following message: #{response.message}")
@@ -134,6 +134,8 @@ module Projects
destroy_ci_records!
destroy_mr_diff_relations!
+ destroy_merge_request_diffs! if ::Feature.enabled?(:extract_mr_diff_deletions)
+
# Rails attempts to load all related records into memory before
# destroying: https://github.com/rails/rails/issues/22510
# This ensures we delete records in batches.
@@ -158,10 +160,9 @@ module Projects
#
# rubocop: disable CodeReuse/ActiveRecord
def destroy_mr_diff_relations!
- mr_batch_size = 100
delete_batch_size = 1000
- project.merge_requests.each_batch(column: :iid, of: mr_batch_size) do |relation_ids|
+ project.merge_requests.each_batch(column: :iid, of: BATCH_SIZE) do |relation_ids|
[MergeRequestDiffCommit, MergeRequestDiffFile].each do |model|
loop do
inner_query = model
@@ -180,6 +181,23 @@ module Projects
end
# rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
+ def destroy_merge_request_diffs!
+ delete_batch_size = 1000
+
+ project.merge_requests.each_batch(column: :iid, of: BATCH_SIZE) do |relation|
+ loop do
+ deleted_rows = MergeRequestDiff
+ .where(merge_request: relation)
+ .limit(delete_batch_size)
+ .delete_all
+
+ break if deleted_rows == 0
+ end
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
def destroy_ci_records!
# Make sure to destroy this first just in case the project is undergoing stats refresh.
# This is to avoid logging the artifact deletion in Ci::JobArtifacts::DestroyBatchService.
diff --git a/app/services/projects/prometheus/alerts/notify_service.rb b/app/services/projects/prometheus/alerts/notify_service.rb
index 6265a74fad2..9f260345937 100644
--- a/app/services/projects/prometheus/alerts/notify_service.rb
+++ b/app/services/projects/prometheus/alerts/notify_service.rb
@@ -3,9 +3,8 @@
module Projects
module Prometheus
module Alerts
- class NotifyService
+ class NotifyService < ::BaseProjectService
include Gitlab::Utils::StrongMemoize
- include ::IncidentManagement::Settings
include ::AlertManagement::Responses
# This set of keys identifies a payload as a valid Prometheus
@@ -26,14 +25,13 @@ module Projects
# https://gitlab.com/gitlab-com/gl-infra/production/-/issues/6086
PROCESS_MAX_ALERTS = 100
- def initialize(project, payload)
- @project = project
- @payload = payload
+ def initialize(project, params)
+ super(project: project, params: params.to_h)
end
def execute(token, integration = nil)
return bad_request unless valid_payload_size?
- return unprocessable_entity unless self.class.processable?(payload)
+ return unprocessable_entity unless self.class.processable?(params)
return unauthorized unless valid_alert_manager_token?(token, integration)
truncate_alerts! if max_alerts_exceeded?
@@ -53,10 +51,8 @@ module Projects
private
- attr_reader :project, :payload
-
def valid_payload_size?
- Gitlab::Utils::DeepSize.new(payload.to_h).valid?
+ Gitlab::Utils::DeepSize.new(params).valid?
end
def max_alerts_exceeded?
@@ -75,11 +71,11 @@ module Projects
}
)
- payload['alerts'] = alerts.first(PROCESS_MAX_ALERTS)
+ params['alerts'] = alerts.first(PROCESS_MAX_ALERTS)
end
def alerts
- payload['alerts']
+ params['alerts']
end
def valid_alert_manager_token?(token, integration)
@@ -152,7 +148,7 @@ module Projects
def process_prometheus_alerts
alerts.map do |alert|
AlertManagement::ProcessPrometheusAlertService
- .new(project, alert.to_h)
+ .new(project, alert)
.execute
end
end
diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb
index dd1c2b94e18..bf90783fcbe 100644
--- a/app/services/projects/update_pages_service.rb
+++ b/app/services/projects/update_pages_service.rb
@@ -65,11 +65,20 @@ module Projects
def build_commit_status
GenericCommitStatus.new(
user: build.user,
- stage: 'deploy',
+ ci_stage: stage,
name: 'pages:deploy'
)
end
+ # rubocop: disable Performance/ActiveRecordSubtransactionMethods
+ def 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
+ end
+ end
+ # rubocop: enable Performance/ActiveRecordSubtransactionMethods
+
def create_pages_deployment(artifacts_path, build)
sha256 = build.job_artifacts_archive.file_sha256
File.open(artifacts_path) do |file|
diff --git a/app/services/releases/create_service.rb b/app/services/releases/create_service.rb
index 2588d2187a5..b7df201824a 100644
--- a/app/services/releases/create_service.rb
+++ b/app/services/releases/create_service.rb
@@ -4,6 +4,7 @@ module Releases
class CreateService < Releases::BaseService
def execute
return error('Access Denied', 403) unless allowed?
+ return error('You are not allowed to create this tag as it is protected.', 403) unless can_create_tag?
return error('Release already exists', 409) if release
return error("Milestone(s) not found: #{inexistent_milestones.join(', ')}", 400) if inexistent_milestones.any?
@@ -38,7 +39,7 @@ module Releases
end
def allowed?
- Ability.allowed?(current_user, :create_release, project) && can_create_tag?
+ Ability.allowed?(current_user, :create_release, project)
end
def can_create_tag?
diff --git a/app/services/resource_events/change_labels_service.rb b/app/services/resource_events/change_labels_service.rb
index 04f917ec8ef..7e176f95db0 100644
--- a/app/services/resource_events/change_labels_service.rb
+++ b/app/services/resource_events/change_labels_service.rb
@@ -29,7 +29,10 @@ module ResourceEvents
resource.expire_note_etag_cache
- Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_label_changed_action(author: user) if resource.is_a?(Issue)
+ return unless resource.is_a?(Issue)
+
+ Gitlab::UsageDataCounters::IssueActivityUniqueCounter.track_issue_label_changed_action(author: user,
+ project: resource.project)
end
private
diff --git a/app/services/service_ping/submit_service.rb b/app/services/service_ping/submit_service.rb
index 89cb14e6fff..7fd0fb10b4b 100644
--- a/app/services/service_ping/submit_service.rb
+++ b/app/services/service_ping/submit_service.rb
@@ -18,41 +18,20 @@ module ServicePing
def execute
return unless ServicePing::ServicePingSettings.product_intelligence_enabled?
- start = Time.current
- begin
- usage_data = payload || ServicePing::BuildPayload.new.execute
- response = submit_usage_data_payload(usage_data)
- rescue StandardError => e
- return unless Gitlab::CurrentSettings.usage_ping_enabled?
-
- error_payload = {
- time: Time.current,
- uuid: Gitlab::CurrentSettings.uuid,
- hostname: Gitlab.config.gitlab.host,
- version: Gitlab.version_info.to_s,
- message: "#{e.message.presence || e.class} at #{e.backtrace[0]}",
- elapsed: (Time.current - start).round(1)
- }
- submit_payload({ error: error_payload }, path: ERROR_PATH)
+ start_time = Time.current
- usage_data = payload || Gitlab::Usage::ServicePingReport.for(output: :all_metrics_values)
- response = submit_usage_data_payload(usage_data)
- end
+ begin
+ response = submit_usage_data_payload
- version_usage_data_id =
- response.dig('conv_index', 'usage_data_id') || response.dig('dev_ops_score', 'usage_data_id')
+ raise SubmissionError, "Unsuccessful response code: #{response.code}" unless response.success?
- unless version_usage_data_id.is_a?(Integer) && version_usage_data_id > 0
- raise SubmissionError, "Invalid usage_data_id in response: #{version_usage_data_id}"
- end
+ handle_response(response)
+ submit_metadata_payload
+ rescue StandardError => e
+ submit_error_payload(e, start_time)
- unless skip_db_write
- raw_usage_data = save_raw_usage_data(usage_data)
- raw_usage_data.update_version_metadata!(usage_data_id: version_usage_data_id)
- ServicePing::DevopsReport.new(response).execute
+ raise
end
-
- submit_payload(metadata(usage_data), path: METADATA_PATH)
end
private
@@ -90,14 +69,43 @@ module ServicePing
)
end
- def submit_usage_data_payload(usage_data)
- raise SubmissionError, 'Usage data is blank' if usage_data.blank?
+ def submit_usage_data_payload
+ raise SubmissionError, 'Usage data payload is blank' if payload.blank?
+
+ submit_payload(payload)
+ end
+
+ def handle_response(response)
+ version_usage_data_id =
+ response.dig('conv_index', 'usage_data_id') || response.dig('dev_ops_score', 'usage_data_id')
- response = submit_payload(usage_data)
+ unless version_usage_data_id.is_a?(Integer) && version_usage_data_id > 0
+ raise SubmissionError, "Invalid usage_data_id in response: #{version_usage_data_id}"
+ end
- raise SubmissionError, "Unsuccessful response code: #{response.code}" unless response.success?
+ return if skip_db_write
+
+ raw_usage_data = save_raw_usage_data(payload)
+ raw_usage_data.update_version_metadata!(usage_data_id: version_usage_data_id)
+ ServicePing::DevopsReport.new(response).execute
+ end
+
+ def submit_error_payload(error, start_time)
+ current_time = Time.current
+ error_payload = {
+ time: current_time,
+ uuid: Gitlab::CurrentSettings.uuid,
+ hostname: Gitlab.config.gitlab.host,
+ version: Gitlab.version_info.to_s,
+ message: "#{error.message.presence || error.class} at #{error.backtrace[0]}",
+ elapsed: (current_time - start_time).round(1)
+ }
+
+ submit_payload({ error: error_payload }, path: ERROR_PATH)
+ end
- response
+ def submit_metadata_payload
+ submit_payload(metadata(payload), path: METADATA_PATH)
end
def save_raw_usage_data(usage_data)
diff --git a/app/services/service_response.rb b/app/services/service_response.rb
index c7ab75a4426..848f90e7f25 100644
--- a/app/services/service_response.rb
+++ b/app/services/service_response.rb
@@ -2,20 +2,28 @@
class ServiceResponse
def self.success(message: nil, payload: {}, http_status: :ok)
- new(status: :success, message: message, payload: payload, http_status: http_status)
+ new(status: :success,
+ message: message,
+ payload: payload,
+ http_status: http_status)
end
- def self.error(message:, payload: {}, http_status: nil)
- new(status: :error, message: message, payload: payload, http_status: http_status)
+ def self.error(message:, payload: {}, http_status: nil, reason: nil)
+ new(status: :error,
+ message: message,
+ payload: payload,
+ http_status: http_status,
+ reason: reason)
end
- attr_reader :status, :message, :http_status, :payload
+ attr_reader :status, :message, :http_status, :payload, :reason
- def initialize(status:, message: nil, payload: {}, http_status: nil)
+ def initialize(status:, message: nil, payload: {}, http_status: nil, reason: nil)
self.status = status
self.message = message
self.payload = payload
self.http_status = http_status
+ self.reason = reason
end
def track_exception(as: StandardError, **extra_data)
@@ -41,7 +49,11 @@ class ServiceResponse
end
def to_h
- (payload || {}).merge(status: status, message: message, http_status: http_status)
+ (payload || {}).merge(
+ status: status,
+ message: message,
+ http_status: http_status,
+ reason: reason)
end
def success?
@@ -60,5 +72,5 @@ class ServiceResponse
private
- attr_writer :status, :message, :http_status, :payload
+ attr_writer :status, :message, :http_status, :payload, :reason
end
diff --git a/app/services/snippets/base_service.rb b/app/services/snippets/base_service.rb
index 1a04c4fcedd..42e62d65ee4 100644
--- a/app/services/snippets/base_service.rb
+++ b/app/services/snippets/base_service.rb
@@ -73,6 +73,15 @@ module Snippets
message
end
+ def file_paths_to_commit
+ paths = []
+ snippet_actions.to_commit_actions.each do |action|
+ paths << { path: action[:file_path] }
+ end
+
+ paths
+ end
+
def files_to_commit(snippet)
snippet_actions.to_commit_actions.presence || build_actions_from_params(snippet)
end
diff --git a/app/services/snippets/bulk_destroy_service.rb b/app/services/snippets/bulk_destroy_service.rb
index 6eab9fb320e..9c6e1c14051 100644
--- a/app/services/snippets/bulk_destroy_service.rb
+++ b/app/services/snippets/bulk_destroy_service.rb
@@ -14,10 +14,10 @@ module Snippets
@snippets = snippets
end
- def execute(options = {})
+ def execute(skip_authorization: false)
return ServiceResponse.success(message: 'No snippets found.') if snippets.empty?
- user_can_delete_snippets! unless options[:hard_delete]
+ user_can_delete_snippets! unless skip_authorization
attempt_delete_repositories!
snippets.destroy_all # rubocop: disable Cop/DestroyAll
diff --git a/app/services/snippets/create_service.rb b/app/services/snippets/create_service.rb
index 6d3b63de9fd..e0bab4cd6ad 100644
--- a/app/services/snippets/create_service.rb
+++ b/app/services/snippets/create_service.rb
@@ -24,7 +24,8 @@ module Snippets
spammable: @snippet,
spam_params: spam_params,
user: current_user,
- action: :create
+ action: :create,
+ extra_features: { files: file_paths_to_commit }
).execute
if save_and_commit
diff --git a/app/services/snippets/update_service.rb b/app/services/snippets/update_service.rb
index 76d5063c337..067680f2abc 100644
--- a/app/services/snippets/update_service.rb
+++ b/app/services/snippets/update_service.rb
@@ -23,11 +23,14 @@ module Snippets
update_snippet_attributes(snippet)
+ files = snippet.all_files.map { |f| { path: f } } + file_paths_to_commit
+
Spam::SpamActionService.new(
spammable: snippet,
spam_params: spam_params,
user: current_user,
- action: :update
+ action: :update,
+ extra_features: { files: files }
).execute
if save_and_commit(snippet)
diff --git a/app/services/spam/spam_action_service.rb b/app/services/spam/spam_action_service.rb
index 4fa9c0e4993..9c52e9f0cd3 100644
--- a/app/services/spam/spam_action_service.rb
+++ b/app/services/spam/spam_action_service.rb
@@ -4,11 +4,12 @@ module Spam
class SpamActionService
include SpamConstants
- def initialize(spammable:, spam_params:, user:, action:)
+ def initialize(spammable:, spam_params:, user:, action:, extra_features: {})
@target = spammable
@spam_params = spam_params
@user = user
@action = action
+ @extra_features = extra_features
end
# rubocop:disable Metrics/AbcSize
@@ -40,7 +41,7 @@ module Spam
private
- attr_reader :user, :action, :target, :spam_params, :spam_log
+ attr_reader :user, :action, :target, :spam_params, :spam_log, :extra_features
##
# In order to be proceed to the spam check process, the target must be
@@ -124,7 +125,9 @@ module Spam
SpamVerdictService.new(target: target,
user: user,
options: options,
- context: context)
+ context: context,
+ extra_features: extra_features
+ )
end
def noteable_type
diff --git a/app/services/spam/spam_constants.rb b/app/services/spam/spam_constants.rb
index d300525710c..9ac3bcf8a1d 100644
--- a/app/services/spam/spam_constants.rb
+++ b/app/services/spam/spam_constants.rb
@@ -2,6 +2,7 @@
module Spam
module SpamConstants
+ ERROR_TYPE = 'spamcheck'
BLOCK_USER = 'block'
DISALLOW = 'disallow'
CONDITIONAL_ALLOW = 'conditional_allow'
diff --git a/app/services/spam/spam_verdict_service.rb b/app/services/spam/spam_verdict_service.rb
index e73b2666c02..08634ec840c 100644
--- a/app/services/spam/spam_verdict_service.rb
+++ b/app/services/spam/spam_verdict_service.rb
@@ -5,11 +5,12 @@ module Spam
include AkismetMethods
include SpamConstants
- def initialize(user:, target:, options:, context: {})
+ def initialize(user:, target:, options:, context: {}, extra_features: {})
@target = target
@user = user
@options = options
@context = context
+ @extra_features = extra_features
end
def execute
@@ -61,7 +62,7 @@ module Spam
private
- attr_reader :user, :target, :options, :context
+ attr_reader :user, :target, :options, :context, :extra_features
def akismet_verdict
if akismet.spam?
@@ -75,7 +76,8 @@ module Spam
return unless Gitlab::CurrentSettings.spam_check_endpoint_enabled
begin
- result, attribs, _error = spamcheck_client.issue_spam?(spam_issue: target, user: user, context: context)
+ result, attribs, _error = spamcheck_client.spam?(spammable: target, user: user, context: context,
+ extra_features: extra_features)
# @TODO log if error is not nil https://gitlab.com/gitlab-org/gitlab/-/issues/329545
return [nil, attribs] unless result
@@ -83,7 +85,7 @@ module Spam
[result, attribs]
rescue StandardError => e
- Gitlab::ErrorTracking.log_exception(e)
+ Gitlab::ErrorTracking.log_exception(e, error: ERROR_TYPE)
# Default to ALLOW if any errors occur
[ALLOW, attribs, true]
diff --git a/app/services/system_notes/issuables_service.rb b/app/services/system_notes/issuables_service.rb
index 75903fde39e..7275a05d2ce 100644
--- a/app/services/system_notes/issuables_service.rb
+++ b/app/services/system_notes/issuables_service.rb
@@ -14,6 +14,13 @@ module SystemNotes
# See also the discussion in https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60700#note_612724683
USE_COMMIT_DATE_FOR_CROSS_REFERENCE_NOTE = false
+ def self.issuable_events
+ {
+ review_requested: s_('IssuableEvents|requested review from'),
+ review_request_removed: s_('IssuableEvents|removed review request for')
+ }.freeze
+ end
+
#
# noteable_ref - Referenced noteable object
#
@@ -26,7 +33,7 @@ module SystemNotes
issuable_type = noteable.to_ability_name.humanize(capitalize: false)
body = "marked this #{issuable_type} as related to #{noteable_ref.to_reference(noteable.resource_parent)}"
- issue_activity_counter.track_issue_related_action(author: author) if noteable.is_a?(Issue)
+ track_issue_event(:track_issue_related_action)
create_note(NoteSummary.new(noteable, project, author, body, action: 'relate'))
end
@@ -42,7 +49,7 @@ module SystemNotes
def unrelate_issuable(noteable_ref)
body = "removed the relation with #{noteable_ref.to_reference(noteable.resource_parent)}"
- issue_activity_counter.track_issue_unrelated_action(author: author) if noteable.is_a?(Issue)
+ track_issue_event(:track_issue_unrelated_action)
create_note(NoteSummary.new(noteable, project, author, body, action: 'unrelate'))
end
@@ -61,7 +68,7 @@ module SystemNotes
def change_assignee(assignee)
body = assignee.nil? ? 'removed assignee' : "assigned to #{assignee.to_reference}"
- issue_activity_counter.track_issue_assignee_changed_action(author: author) if noteable.is_a?(Issue)
+ track_issue_event(:track_issue_assignee_changed_action)
create_note(NoteSummary.new(noteable, project, author, body, action: 'assignee'))
end
@@ -93,7 +100,7 @@ module SystemNotes
body = text_parts.join(' and ')
- issue_activity_counter.track_issue_assignee_changed_action(author: author) if noteable.is_a?(Issue)
+ track_issue_event(:track_issue_assignee_changed_action)
create_note(NoteSummary.new(noteable, project, author, body, action: 'assignee'))
end
@@ -115,8 +122,8 @@ module SystemNotes
text_parts = []
Gitlab::I18n.with_default_locale do
- text_parts << "requested review from #{added_users.map(&:to_reference).to_sentence}" if added_users.any?
- text_parts << "removed review request for #{unassigned_users.map(&:to_reference).to_sentence}" if unassigned_users.any?
+ text_parts << "#{self.class.issuable_events[:review_requested]} #{added_users.map(&:to_reference).to_sentence}" if added_users.any?
+ text_parts << "#{self.class.issuable_events[:review_request_removed]} #{unassigned_users.map(&:to_reference).to_sentence}" if unassigned_users.any?
end
body = text_parts.join(' and ')
@@ -172,7 +179,7 @@ module SystemNotes
body = "changed title from **#{marked_old_title}** to **#{marked_new_title}**"
- issue_activity_counter.track_issue_title_changed_action(author: author) if noteable.is_a?(Issue)
+ track_issue_event(:track_issue_title_changed_action)
work_item_activity_counter.track_work_item_title_changed_action(author: author) if noteable.is_a?(WorkItem)
create_note(NoteSummary.new(noteable, project, author, body, action: 'title'))
@@ -210,7 +217,7 @@ module SystemNotes
def change_description
body = 'changed the description'
- issue_activity_counter.track_issue_description_changed_action(author: author) if noteable.is_a?(Issue)
+ track_issue_event(:track_issue_description_changed_action)
create_note(NoteSummary.new(noteable, project, author, body, action: 'description'))
end
@@ -246,6 +253,7 @@ module SystemNotes
)
else
track_cross_reference_action
+
created_at = mentioner.created_at if USE_COMMIT_DATE_FOR_CROSS_REFERENCE_NOTE && mentioner.is_a?(Commit)
create_note(NoteSummary.new(noteable, noteable.project, author, body, action: 'cross_reference', created_at: created_at))
end
@@ -280,7 +288,7 @@ module SystemNotes
status_label = new_task.complete? ? Taskable::COMPLETED : Taskable::INCOMPLETE
body = "marked the checklist item **#{new_task.source}** as #{status_label}"
- issue_activity_counter.track_issue_description_changed_action(author: author) if noteable.is_a?(Issue)
+ track_issue_event(:track_issue_description_changed_action)
create_note(NoteSummary.new(noteable, project, author, body, action: 'task'))
end
@@ -303,7 +311,7 @@ module SystemNotes
cross_reference = noteable_ref.to_reference(project)
body = "moved #{direction} #{cross_reference}"
- issue_activity_counter.track_issue_moved_action(author: author) if noteable.is_a?(Issue)
+ track_issue_event(:track_issue_moved_action)
create_note(NoteSummary.new(noteable, project, author, body, action: 'moved'))
end
@@ -327,9 +335,7 @@ module SystemNotes
cross_reference = noteable_ref.to_reference(project)
body = "cloned #{direction} #{cross_reference}"
- if noteable.is_a?(Issue) && direction == :to
- issue_activity_counter.track_issue_cloned_action(author: author, project: project)
- end
+ track_issue_event(:track_issue_cloned_action) if direction == :to
create_note(NoteSummary.new(noteable, project, author, body, action: 'cloned', created_at: created_at))
end
@@ -346,12 +352,12 @@ module SystemNotes
body = 'made the issue confidential'
action = 'confidential'
- issue_activity_counter.track_issue_made_confidential_action(author: author) if noteable.is_a?(Issue)
+ track_issue_event(:track_issue_made_confidential_action)
else
body = 'made the issue visible to everyone'
action = 'visible'
- issue_activity_counter.track_issue_made_visible_action(author: author) if noteable.is_a?(Issue)
+ track_issue_event(:track_issue_made_visible_action)
end
create_note(NoteSummary.new(noteable, project, author, body, action: action))
@@ -418,7 +424,7 @@ module SystemNotes
def mark_duplicate_issue(canonical_issue)
body = "marked this issue as a duplicate of #{canonical_issue.to_reference(project)}"
- issue_activity_counter.track_issue_marked_as_duplicate_action(author: author) if noteable.is_a?(Issue)
+ track_issue_event(:track_issue_marked_as_duplicate_action)
create_note(NoteSummary.new(noteable, project, author, body, action: 'duplicate'))
end
@@ -431,12 +437,10 @@ module SystemNotes
action = noteable.discussion_locked? ? 'locked' : 'unlocked'
body = "#{action} this #{noteable.class.to_s.titleize.downcase}"
- if noteable.is_a?(Issue)
- if action == 'locked'
- issue_activity_counter.track_issue_locked_action(author: author)
- else
- issue_activity_counter.track_issue_unlocked_action(author: author)
- end
+ if action == 'locked'
+ track_issue_event(:track_issue_locked_action)
+ else
+ track_issue_event(:track_issue_unlocked_action)
end
create_note(NoteSummary.new(noteable, project, author, body, action: action))
@@ -495,7 +499,7 @@ module SystemNotes
end
def track_cross_reference_action
- issue_activity_counter.track_issue_cross_referenced_action(author: author) if noteable.is_a?(Issue)
+ track_issue_event(:track_issue_cross_referenced_action)
end
def hierarchy_note_params(action, parent, child)
@@ -520,6 +524,12 @@ module SystemNotes
}
end
end
+
+ def track_issue_event(event_name)
+ return unless noteable.is_a?(Issue)
+
+ issue_activity_counter.public_send(event_name, author: author, project: project || noteable.project) # rubocop: disable GitlabSecurity/PublicSend
+ end
end
end
diff --git a/app/services/system_notes/time_tracking_service.rb b/app/services/system_notes/time_tracking_service.rb
index 68df52a03c7..c5bdbc6799e 100644
--- a/app/services/system_notes/time_tracking_service.rb
+++ b/app/services/system_notes/time_tracking_service.rb
@@ -21,7 +21,7 @@ module SystemNotes
# Using instance_of because WorkItem < Issue. We don't want to track work item updates as issue updates
if noteable.instance_of?(Issue) && changed_dates.key?('due_date')
- issue_activity_counter.track_issue_due_date_changed_action(author: author)
+ issue_activity_counter.track_issue_due_date_changed_action(author: author, project: project)
end
work_item_activity_counter.track_work_item_date_changed_action(author: author) if noteable.is_a?(WorkItem)
@@ -50,7 +50,9 @@ module SystemNotes
"changed time estimate to #{parsed_time}"
end
- issue_activity_counter.track_issue_time_estimate_changed_action(author: author) if noteable.is_a?(Issue)
+ if noteable.is_a?(Issue)
+ issue_activity_counter.track_issue_time_estimate_changed_action(author: author, project: project)
+ end
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
end
@@ -81,7 +83,9 @@ module SystemNotes
body = text_parts.join(' ')
end
- issue_activity_counter.track_issue_time_spent_changed_action(author: author) if noteable.is_a?(Issue)
+ if noteable.is_a?(Issue)
+ issue_activity_counter.track_issue_time_spent_changed_action(author: author, project: project)
+ end
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
end
@@ -107,7 +111,9 @@ module SystemNotes
text_parts << "at #{spent_at}" if spent_at && spent_at != DateTime.current.to_date
body = text_parts.join(' ')
- issue_activity_counter.track_issue_time_spent_changed_action(author: author) if noteable.is_a?(Issue)
+ if noteable.is_a?(Issue)
+ issue_activity_counter.track_issue_time_spent_changed_action(author: author, project: project)
+ end
create_note(NoteSummary.new(noteable, project, author, body, action: 'time_tracking'))
end
diff --git a/app/services/topics/merge_service.rb b/app/services/topics/merge_service.rb
index 0d256579fe0..58f3d5305b4 100644
--- a/app/services/topics/merge_service.rb
+++ b/app/services/topics/merge_service.rb
@@ -17,14 +17,21 @@ module Topics
refresh_target_topic_counters
delete_source_topic
end
+
+ ServiceResponse.success
+ rescue ArgumentError => e
+ ServiceResponse.error(message: e.message)
+ rescue StandardError => e
+ Gitlab::ErrorTracking.track_exception(e, source_topic_id: source_topic.id, target_topic_id: target_topic.id)
+ ServiceResponse.error(message: _('Topics could not be merged!'))
end
private
def validate_parameters!
- raise ArgumentError, 'The source topic is not a topic.' unless source_topic.is_a?(Projects::Topic)
- raise ArgumentError, 'The target topic is not a topic.' unless target_topic.is_a?(Projects::Topic)
- raise ArgumentError, 'The source topic and the target topic are identical.' if source_topic == target_topic
+ raise ArgumentError, _('The source topic is not a topic.') unless source_topic.is_a?(Projects::Topic)
+ raise ArgumentError, _('The target topic is not a topic.') unless target_topic.is_a?(Projects::Topic)
+ raise ArgumentError, _('The source topic and the target topic are identical.') if source_topic == target_topic
end
# rubocop: disable CodeReuse/ActiveRecord
diff --git a/app/services/users/authorized_build_service.rb b/app/services/users/authorized_build_service.rb
index eb2386198d3..5029105b087 100644
--- a/app/services/users/authorized_build_service.rb
+++ b/app/services/users/authorized_build_service.rb
@@ -16,3 +16,5 @@ module Users
end
end
end
+
+Users::AuthorizedBuildService.prepend_mod_with('Users::AuthorizedBuildService')
diff --git a/app/services/users/destroy_service.rb b/app/services/users/destroy_service.rb
index dfa9316889e..a378cb09854 100644
--- a/app/services/users/destroy_service.rb
+++ b/app/services/users/destroy_service.rb
@@ -23,6 +23,11 @@ module Users
# `hard_delete: true` implies `delete_solo_owned_groups: true`. To perform
# a hard deletion without destroying solo-owned groups, pass
# `delete_solo_owned_groups: false, hard_delete: true` in +options+.
+ #
+ # To make the service asynchronous, a new behaviour is being introduced
+ # behind the user_destroy_with_limited_execution_time_worker feature flag.
+ # Migrating the associated user records, and post-migration cleanup is
+ # handled by the Users::MigrateRecordsToGhostUserWorker cron worker.
def execute(user, options = {})
delete_solo_owned_groups = options.fetch(:delete_solo_owned_groups, options[:hard_delete])
@@ -35,12 +40,14 @@ module Users
return user
end
- # Calling all before/after_destroy hooks for the user because
- # there is no dependent: destroy in the relationship. And the removal
- # is done by a foreign_key. Otherwise they won't be called
- user.members.find_each { |member| member.run_callbacks(:destroy) }
+ user.block
+
+ # Load the records. Groups are unavailable after membership is destroyed.
+ solo_owned_groups = user.solo_owned_groups.load
+
+ user.members.each_batch { |batch| batch.destroy_all } # rubocop:disable Style/SymbolProc, Cop/DestroyAll
- user.solo_owned_groups.each do |group|
+ solo_owned_groups.each do |group|
Groups::DestroyService.new(group, current_user).execute
end
@@ -54,22 +61,32 @@ module Users
yield(user) if block_given?
- MigrateToGhostUserService.new(user).execute(hard_delete: options[:hard_delete])
+ hard_delete = options.fetch(:hard_delete, false)
- response = Snippets::BulkDestroyService.new(current_user, user.snippets).execute(options)
- raise DestroyError, response.message if response.error?
+ if Feature.enabled?(:user_destroy_with_limited_execution_time_worker)
+ Users::GhostUserMigration.create!(user: user,
+ initiator_user: current_user,
+ hard_delete: hard_delete)
- # Rails attempts to load all related records into memory before
- # destroying: https://github.com/rails/rails/issues/22510
- # This ensures we delete records in batches.
- user.destroy_dependent_associations_in_batches(exclude: [:snippets])
- user.nullify_dependent_associations_in_batches
+ else
+ MigrateToGhostUserService.new(user).execute(hard_delete: options[:hard_delete])
- # Destroy the namespace after destroying the user since certain methods may depend on the namespace existing
- user_data = user.destroy
- namespace.destroy
+ response = Snippets::BulkDestroyService.new(current_user, user.snippets)
+ .execute(skip_authorization: hard_delete)
+ raise DestroyError, response.message if response.error?
- user_data
+ # Rails attempts to load all related records into memory before
+ # destroying: https://github.com/rails/rails/issues/22510
+ # This ensures we delete records in batches.
+ user.destroy_dependent_associations_in_batches(exclude: [:snippets])
+ user.nullify_dependent_associations_in_batches
+
+ # Destroy the namespace after destroying the user since certain methods may depend on the namespace existing
+ user_data = user.destroy
+ namespace.destroy
+
+ user_data
+ end
end
end
end
diff --git a/app/services/users/email_verification/base_service.rb b/app/services/users/email_verification/base_service.rb
new file mode 100644
index 00000000000..3337beec195
--- /dev/null
+++ b/app/services/users/email_verification/base_service.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Users
+ module EmailVerification
+ class BaseService
+ VALID_ATTRS = %i[unlock_token confirmation_token].freeze
+
+ def initialize(attr:)
+ @attr = attr
+
+ validate_attr!
+ end
+
+ protected
+
+ attr_reader :attr, :token
+
+ def validate_attr!
+ raise ArgumentError, 'Invalid attribute' unless attr.in?(VALID_ATTRS)
+ end
+
+ def digest
+ Devise.token_generator.digest(User, attr, token)
+ end
+ end
+ end
+end
diff --git a/app/services/users/email_verification/generate_token_service.rb b/app/services/users/email_verification/generate_token_service.rb
new file mode 100644
index 00000000000..6f0237ce244
--- /dev/null
+++ b/app/services/users/email_verification/generate_token_service.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Users
+ module EmailVerification
+ class GenerateTokenService < EmailVerification::BaseService
+ TOKEN_LENGTH = 6
+
+ def execute
+ @token = generate_token
+
+ [token, digest]
+ end
+
+ private
+
+ def generate_token
+ SecureRandom.random_number(10**TOKEN_LENGTH).to_s.rjust(TOKEN_LENGTH, '0')
+ end
+ end
+ end
+end
diff --git a/app/services/users/email_verification/validate_token_service.rb b/app/services/users/email_verification/validate_token_service.rb
new file mode 100644
index 00000000000..b1b34e94f49
--- /dev/null
+++ b/app/services/users/email_verification/validate_token_service.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+module Users
+ module EmailVerification
+ class ValidateTokenService < EmailVerification::BaseService
+ include ActionView::Helpers::DateHelper
+
+ TOKEN_VALID_FOR_MINUTES = 60
+
+ def initialize(attr:, user:, token:)
+ super(attr: attr)
+
+ @user = user
+ @token = token
+ end
+
+ def execute
+ return failure(:rate_limited) if verification_rate_limited?
+ return failure(:invalid) unless valid?
+ return failure(:expired) if expired_token?
+
+ success
+ end
+
+ private
+
+ attr_reader :user
+
+ def verification_rate_limited?
+ Gitlab::ApplicationRateLimiter.throttled?(:email_verification, scope: user[attr])
+ end
+
+ def valid?
+ return false unless token.present?
+
+ Devise.secure_compare(user[attr], digest)
+ end
+
+ def expired_token?
+ generated_at = case attr
+ when :unlock_token then user.locked_at
+ when :confirmation_token then user.confirmation_sent_at
+ end
+
+ generated_at < TOKEN_VALID_FOR_MINUTES.minutes.ago
+ end
+
+ def success
+ { status: :success }
+ end
+
+ def failure(reason)
+ {
+ status: :failure,
+ reason: reason,
+ message: failure_message(reason)
+ }
+ end
+
+ def failure_message(reason)
+ case reason
+ when :rate_limited
+ format(s_("IdentityVerification|You've reached the maximum amount of tries. "\
+ 'Wait %{interval} or send a new code and try again.'), interval: email_verification_interval)
+ when :expired
+ s_('IdentityVerification|The code has expired. Send a new code and try again.')
+ when :invalid
+ s_('IdentityVerification|The code is incorrect. Enter it again, or send a new code.')
+ end
+ end
+
+ def email_verification_interval
+ interval_in_seconds = Gitlab::ApplicationRateLimiter.rate_limits[:email_verification][:interval]
+ distance_of_time_in_words(interval_in_seconds)
+ end
+ end
+ end
+end
diff --git a/app/services/users/migrate_records_to_ghost_user_in_batches_service.rb b/app/services/users/migrate_records_to_ghost_user_in_batches_service.rb
new file mode 100644
index 00000000000..7c4a5698ea9
--- /dev/null
+++ b/app/services/users/migrate_records_to_ghost_user_in_batches_service.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Users
+ class MigrateRecordsToGhostUserInBatchesService
+ def initialize
+ @execution_tracker = Gitlab::Utils::ExecutionTracker.new
+ end
+
+ def execute
+ Users::GhostUserMigration.find_each do |user_to_migrate|
+ break if execution_tracker.over_limit?
+
+ service = Users::MigrateRecordsToGhostUserService.new(user_to_migrate.user,
+ user_to_migrate.initiator_user,
+ execution_tracker)
+ service.execute(hard_delete: user_to_migrate.hard_delete)
+ end
+ rescue Gitlab::Utils::ExecutionTracker::ExecutionTimeOutError
+ # no-op
+ end
+
+ private
+
+ attr_reader :execution_tracker
+ end
+end
diff --git a/app/services/users/migrate_records_to_ghost_user_service.rb b/app/services/users/migrate_records_to_ghost_user_service.rb
new file mode 100644
index 00000000000..2d92aaed7da
--- /dev/null
+++ b/app/services/users/migrate_records_to_ghost_user_service.rb
@@ -0,0 +1,111 @@
+# frozen_string_literal: true
+
+# When a user is destroyed, some of their associated records are
+# moved to a "Ghost User", to prevent these associated records from
+# being destroyed.
+#
+# For example, all the issues/MRs a user has created are _not_ destroyed
+# when the user is destroyed.
+module Users
+ class MigrateRecordsToGhostUserService
+ extend ActiveSupport::Concern
+
+ DestroyError = Class.new(StandardError)
+
+ attr_reader :ghost_user, :user, :initiator_user, :hard_delete
+
+ def initialize(user, initiator_user, execution_tracker)
+ @user = user
+ @initiator_user = initiator_user
+ @execution_tracker = execution_tracker
+ @ghost_user = User.ghost
+ end
+
+ def execute(hard_delete: false)
+ @hard_delete = hard_delete
+
+ migrate_records
+ post_migrate_records
+ end
+
+ private
+
+ attr_reader :execution_tracker
+
+ def migrate_records
+ return if hard_delete
+
+ migrate_issues
+ migrate_merge_requests
+ migrate_notes
+ migrate_abuse_reports
+ migrate_award_emoji
+ migrate_snippets
+ migrate_reviews
+ end
+
+ def post_migrate_records
+ delete_snippets
+
+ # Rails attempts to load all related records into memory before
+ # destroying: https://github.com/rails/rails/issues/22510
+ # This ensures we delete records in batches.
+ user.destroy_dependent_associations_in_batches(exclude: [:snippets])
+ user.nullify_dependent_associations_in_batches
+
+ # Destroy the namespace after destroying the user since certain methods may depend on the namespace existing
+ user_data = user.destroy
+ user.namespace.destroy
+
+ user_data
+ end
+
+ def delete_snippets
+ response = Snippets::BulkDestroyService.new(initiator_user, user.snippets).execute(skip_authorization: true)
+ raise DestroyError, response.message if response.error?
+ end
+
+ def migrate_issues
+ batched_migrate(Issue, :author_id)
+ batched_migrate(Issue, :last_edited_by_id)
+ end
+
+ def migrate_merge_requests
+ batched_migrate(MergeRequest, :author_id)
+ batched_migrate(MergeRequest, :merge_user_id)
+ end
+
+ def migrate_notes
+ batched_migrate(Note, :author_id)
+ end
+
+ def migrate_abuse_reports
+ user.reported_abuse_reports.update_all(reporter_id: ghost_user.id)
+ end
+
+ def migrate_award_emoji
+ user.award_emoji.update_all(user_id: ghost_user.id)
+ end
+
+ def migrate_snippets
+ snippets = user.snippets.only_project_snippets
+ snippets.update_all(author_id: ghost_user.id)
+ end
+
+ def migrate_reviews
+ batched_migrate(Review, :author_id)
+ end
+
+ # rubocop:disable CodeReuse/ActiveRecord
+ def batched_migrate(base_scope, column, batch_size: 50)
+ loop do
+ update_count = base_scope.where(column => user.id).limit(batch_size).update_all(column => ghost_user.id)
+ break if update_count == 0
+ raise Gitlab::Utils::ExecutionTracker::ExecutionTimeOutError if execution_tracker.over_limit?
+ end
+ end
+ # rubocop:enable CodeReuse/ActiveRecord
+ end
+end
+
+Users::MigrateRecordsToGhostUserService.prepend_mod_with('Users::MigrateRecordsToGhostUserService')
diff --git a/app/uploaders/object_storage/cdn.rb b/app/uploaders/object_storage/cdn.rb
new file mode 100644
index 00000000000..0711ab0bd28
--- /dev/null
+++ b/app/uploaders/object_storage/cdn.rb
@@ -0,0 +1,46 @@
+# rubocop:disable Naming/FileName
+# frozen_string_literal: true
+
+require_relative 'cdn/google_cdn'
+
+module ObjectStorage
+ module CDN
+ module Concern
+ extend ActiveSupport::Concern
+
+ include Gitlab::Utils::StrongMemoize
+
+ def use_cdn?(request_ip)
+ return false unless cdn_options.is_a?(Hash) && cdn_options['provider']
+ return false unless cdn_provider
+
+ cdn_provider.use_cdn?(request_ip)
+ end
+
+ def cdn_signed_url
+ cdn_provider&.signed_url(path)
+ end
+
+ private
+
+ def cdn_provider
+ strong_memoize(:cdn_provider) do
+ provider = cdn_options['provider']&.downcase
+
+ next unless provider
+ next GoogleCDN.new(cdn_options) if provider == 'google'
+
+ raise "Unknown CDN provider: #{provider}"
+ end
+ end
+
+ def cdn_options
+ return {} unless options.object_store.key?('cdn')
+
+ options.object_store.cdn
+ end
+ end
+ end
+end
+
+# rubocop:enable Naming/FileName
diff --git a/app/uploaders/object_storage/cdn/google_cdn.rb b/app/uploaders/object_storage/cdn/google_cdn.rb
new file mode 100644
index 00000000000..ea7683f131c
--- /dev/null
+++ b/app/uploaders/object_storage/cdn/google_cdn.rb
@@ -0,0 +1,71 @@
+# rubocop:disable Naming/FileName
+# frozen_string_literal: true
+
+module ObjectStorage
+ module CDN
+ class GoogleCDN
+ include Gitlab::Utils::StrongMemoize
+
+ attr_reader :options
+
+ def initialize(options)
+ @options = HashWithIndifferentAccess.new(options.to_h)
+
+ GoogleIpCache.async_refresh unless GoogleIpCache.ready?
+ end
+
+ def use_cdn?(request_ip)
+ return false unless config_valid?
+
+ ip = IPAddr.new(request_ip)
+
+ return false if ip.private?
+
+ !GoogleIpCache.google_ip?(request_ip)
+ end
+
+ def signed_url(path, expiry: 10.minutes)
+ expiration = (Time.current + expiry).utc.to_i
+
+ uri = Addressable::URI.parse(cdn_url)
+ uri.path = path
+ uri.query = "Expires=#{expiration}&KeyName=#{key_name}"
+
+ signature = OpenSSL::HMAC.digest('SHA1', decoded_key, uri.to_s)
+ encoded_signature = Base64.urlsafe_encode64(signature)
+
+ uri.query += "&Signature=#{encoded_signature}"
+ uri.to_s
+ end
+
+ private
+
+ def config_valid?
+ [key_name, decoded_key, cdn_url].all?(&:present?)
+ end
+
+ def key_name
+ strong_memoize(:key_name) do
+ options['key_name']
+ end
+ end
+
+ def decoded_key
+ strong_memoize(:decoded_key) do
+ Base64.urlsafe_decode64(options['key']) if options['key']
+ rescue ArgumentError
+ Gitlab::ErrorTracking.log_exception(ArgumentError.new("Google CDN key is not base64-encoded"))
+ nil
+ end
+ end
+
+ def cdn_url
+ strong_memoize(:cdn_url) do
+ options['url']
+ end
+ end
+ end
+ end
+end
+
+# rubocop:enable Naming/FileName
diff --git a/app/uploaders/object_storage/cdn/google_ip_cache.rb b/app/uploaders/object_storage/cdn/google_ip_cache.rb
new file mode 100644
index 00000000000..35ec7ce0c6e
--- /dev/null
+++ b/app/uploaders/object_storage/cdn/google_ip_cache.rb
@@ -0,0 +1,60 @@
+# rubocop:disable Naming/FileName
+# frozen_string_literal: true
+
+module ObjectStorage
+ module CDN
+ class GoogleIpCache
+ GOOGLE_CDN_LIST_KEY = 'google_cdn_ip_list'
+ CACHE_EXPIRATION_TIME = 1.day
+
+ class << self
+ def update!(subnets)
+ caches.each { |cache| cache.write(GOOGLE_CDN_LIST_KEY, subnets) }
+ end
+
+ def ready?
+ caches.any? { |cache| cache.exist?(GOOGLE_CDN_LIST_KEY) }
+ end
+
+ def google_ip?(request_ip)
+ google_ip_ranges = cached_value(GOOGLE_CDN_LIST_KEY)
+
+ return false unless google_ip_ranges
+
+ google_ip_ranges.any? { |range| range.include?(request_ip) }
+ end
+
+ def async_refresh
+ ::GoogleCloud::FetchGoogleIpListWorker.perform_async
+ end
+
+ private
+
+ def caches
+ [l1_cache, l2_cache]
+ end
+
+ def l1_cache
+ Gitlab::ProcessMemoryCache.cache_backend
+ end
+
+ def l2_cache
+ Rails.cache
+ end
+
+ def cached_value(key)
+ l1_cache.fetch(key) do
+ result = l2_cache.fetch(key)
+
+ # Don't populate the L1 cache if we can't find the entry
+ break unless result
+
+ result
+ end
+ end
+ end
+ end
+ end
+end
+
+# rubocop:enable Naming/FileName
diff --git a/app/uploaders/packages/package_file_uploader.rb b/app/uploaders/packages/package_file_uploader.rb
index 4b6dbe5b358..9c0a88c9bf8 100644
--- a/app/uploaders/packages/package_file_uploader.rb
+++ b/app/uploaders/packages/package_file_uploader.rb
@@ -22,8 +22,6 @@ class Packages::PackageFileUploader < GitlabUploader
def dynamic_segment
raise ObjectNotReadyError, "Package model not ready" unless model.id
- package_segment = model.package.debian? ? 'debian' : model.package.id
-
- Gitlab::HashedPath.new('packages', package_segment, 'files', model.id, root_hash: model.package.project_id)
+ Gitlab::HashedPath.new('packages', model.package_id, 'files', model.id, root_hash: model.package.project_id)
end
end
diff --git a/app/validators/json_schemas/merge_request_predictions_suggested_reviewers.json b/app/validators/json_schemas/merge_request_predictions_suggested_reviewers.json
new file mode 100644
index 00000000000..70112d7e414
--- /dev/null
+++ b/app/validators/json_schemas/merge_request_predictions_suggested_reviewers.json
@@ -0,0 +1,10 @@
+{
+ "description": "Merge request predictions suggested reviewers",
+ "type": "object",
+ "properties": {
+ "top_n": { "type": "number" },
+ "version": { "type": "string" },
+ "changes": { "type": "array" }
+ },
+ "additionalProperties": true
+}
diff --git a/app/views/abuse_reports/new.html.haml b/app/views/abuse_reports/new.html.haml
index 258fdb4ad9a..aaa85e81bd4 100644
--- a/app/views/abuse_reports/new.html.haml
+++ b/app/views/abuse_reports/new.html.haml
@@ -6,8 +6,8 @@
%p
= _("A member of the abuse team will review your report as soon as possible.")
%hr
-= form_for @abuse_report, html: { class: 'js-quick-submit js-requires-input'} do |f|
- = form_errors(@abuse_report, pajamas_alert: true)
+= gitlab_ui_form_for @abuse_report, html: { class: 'js-quick-submit js-requires-input'} do |f|
+ = form_errors(@abuse_report)
= f.hidden_field :user_id
.form-group.row
@@ -25,4 +25,4 @@
= _("Explain the problem. If appropriate, provide a link to the relevant issue or comment.")
.form-actions
- = f.submit _("Send report"), class: "gl-button btn btn-confirm"
+ = f.submit _("Send report"), pajamas_button: true
diff --git a/app/views/admin/application_settings/_abuse.html.haml b/app/views/admin/application_settings/_abuse.html.haml
index fbadd26d0c0..1878db419f7 100644
--- a/app/views/admin/application_settings/_abuse.html.haml
+++ b/app/views/admin/application_settings/_abuse.html.haml
@@ -1,9 +1,9 @@
-= form_for @application_setting, url: reporting_admin_application_settings_path(anchor: 'js-abuse-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+= gitlab_ui_form_for @application_setting, url: reporting_admin_application_settings_path(anchor: 'js-abuse-settings'), html: { class: 'fieldset-form' } do |f|
+ = form_errors(@application_setting)
%fieldset
.form-group
= f.label :abuse_notification_email, _('Abuse reports notification email'), class: 'label-bold'
= f.text_field :abuse_notification_email, 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/_account_and_limit.html.haml b/app/views/admin/application_settings/_account_and_limit.html.haml
index e7204f635e6..c0e42f22119 100644
--- a/app/views/admin/application_settings/_account_and_limit.html.haml
+++ b/app/views/admin/application_settings/_account_and_limit.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-account-settings'), html: { class: 'fieldset-form', id: 'account-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
@@ -46,13 +46,19 @@
= 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/permissions', anchor: 'setting-new-users-to-external'), target: '_blank', rel: 'noopener noreferrer'
- unless Gitlab.com?
.form-group
= f.label :deactivate_dormant_users, _('Dormant users'), class: 'label-bold'
- dormant_users_help_link = help_page_path('user/admin_area/moderate_users', anchor: 'automatically-deactivate-dormant-users')
- dormant_users_help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: dormant_users_help_link }
- = f.gitlab_ui_checkbox_component :deactivate_dormant_users, _('Deactivate dormant users after 90 days of inactivity'), help_text: _('Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}').html_safe % { link_start: dormant_users_help_link_start, link_end: '</a>'.html_safe }
+ = f.gitlab_ui_checkbox_component :deactivate_dormant_users, _('Deactivate dormant users after a period of inactivity'), help_text: _('Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}').html_safe % { link_start: dormant_users_help_link_start, link_end: '</a>'.html_safe }
+ .form-group
+ = f.label :deactivate_dormant_users_period, _('Period of inactivity (days)'), class: 'label-light'
+ = f.number_field :deactivate_dormant_users_period, class: 'form-control gl-form-input', min: '1'
+ .form-text.text-muted
+ = _('Period of inactivity before deactivation.')
+
.form-group
= f.label :personal_access_token_prefix, _('Personal Access Token prefix'), class: 'label-light'
= f.text_field :personal_access_token_prefix, placeholder: _('Maximum 20 characters'), class: 'form-control gl-form-input'
@@ -60,6 +66,7 @@
= f.label :user_show_add_ssh_key_message, _('Prompt users to upload SSH keys'), class: 'label-bold'
= f.gitlab_ui_checkbox_component :user_show_add_ssh_key_message, _("Inform users without uploaded SSH keys that they can't push over SSH until one is added")
+ = render 'admin/application_settings/invitation_flow_enforcement', form: f
= render_if_exists 'admin/application_settings/updating_name_disabled_for_users', form: f
= render_if_exists 'admin/application_settings/availability_on_namespace_setting', form: f
- = f.submit _('Save changes'), class: 'gl-button btn btn-confirm qa-save-changes-button'
+ = f.submit _('Save changes'), class: 'qa-save-changes-button', pajamas_button: true
diff --git a/app/views/admin/application_settings/_ci_cd.html.haml b/app/views/admin/application_settings/_ci_cd.html.haml
index 77170761448..05aea2b343d 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, pajamas_alert: true )
+ = form_errors(@application_setting )
%fieldset
.form-group
@@ -53,8 +53,10 @@
= link_to sprite_icon('question-o'), help_page_path('ci/pipelines/settings', anchor: 'specify-a-custom-cicd-configuration-file'), target: '_blank', rel: 'noopener noreferrer'
.form-group
= f.gitlab_ui_checkbox_component :suggest_pipeline_enabled, s_('AdminSettings|Enable pipeline suggestion banner'), help_text: s_('AdminSettings|Display a banner on merge requests in projects with no pipelines to initiate steps to add a .gitlab-ci.yml file.')
+ - if Feature.enabled?(:enforce_runner_token_expires_at)
+ #js-runner-token-expiration-intervals{ data: runner_token_expiration_interval_attributes }
- = f.submit _('Save changes'), class: "gl-button btn btn-confirm"
+ = f.submit _('Save changes'), pajamas_button: true
.settings-content
%h4
@@ -71,8 +73,8 @@
.tab-content.gl-tab-content
- @plans.each_with_index do |plan, index|
.tab-pane{ :id => "plan#{index}", class: index == 0 ? 'active': '' }
- = form_for plan.actual_limits, url: admin_plan_limits_path(anchor: 'js-ci-cd-settings'), html: { class: 'fieldset-form' }, method: :post do |f|
- = form_errors(plan, pajamas_alert: true)
+ = gitlab_ui_form_for plan.actual_limits, url: admin_plan_limits_path(anchor: 'js-ci-cd-settings'), html: { class: 'fieldset-form' }, method: :post do |f|
+ = form_errors(plan)
%fieldset
= f.hidden_field(:plan_id, value: plan.id)
.form-group
@@ -99,4 +101,4 @@
.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'
- = f.submit s_('AdminSettings|Save %{name} limits').html_safe % { name: plan.name.capitalize }, class: 'btn gl-button btn-confirm'
+ = 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 f9b1aa22b7a..7be4bac02fd 100644
--- a/app/views/admin/application_settings/_default_branch.html.haml
+++ b/app/views/admin/application_settings/_default_branch.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: repository_admin_application_settings_path(anchor: 'js-default-branch-name'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
- fallback_branch_name = "<code>#{Gitlab::DefaultBranch.value}</code>"
diff --git a/app/views/admin/application_settings/_diff_limits.html.haml b/app/views/admin/application_settings/_diff_limits.html.haml
index 30165139711..2e8eb25b1d5 100644
--- a/app/views/admin/application_settings/_diff_limits.html.haml
+++ b/app/views/admin/application_settings/_diff_limits.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-merge-request-settings'), html: { class: 'fieldset-form', id: 'merge-request-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_eks.html.haml b/app/views/admin/application_settings/_eks.html.haml
index 68eb33d6552..0bb9be497d9 100644
--- a/app/views/admin/application_settings/_eks.html.haml
+++ b/app/views/admin/application_settings/_eks.html.haml
@@ -10,7 +10,7 @@
.settings-content
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-eks-settings'), html: { class: 'fieldset-form', id: 'eks-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_email.html.haml b/app/views/admin/application_settings/_email.html.haml
index 774c5665edd..fd65d4029f5 100644
--- a/app/views/admin/application_settings/_email.html.haml
+++ b/app/views/admin/application_settings/_email.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: preferences_admin_application_settings_path(anchor: 'js-email-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_error_tracking.html.haml b/app/views/admin/application_settings/_error_tracking.html.haml
index 2dcd9d0d2c0..5a8aba5784e 100644
--- a/app/views/admin/application_settings/_error_tracking.html.haml
+++ b/app/views/admin/application_settings/_error_tracking.html.haml
@@ -25,7 +25,7 @@
data: { confirm: _('Are you sure you want to reset the error tracking access token?') }
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-error-tracking-settings'), html: { class: 'fieldset-form', id: 'error-tracking-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true) if expanded
+ = form_errors(@application_setting) if expanded
%fieldset
.sub-section
diff --git a/app/views/admin/application_settings/_external_authorization_service_form.html.haml b/app/views/admin/application_settings/_external_authorization_service_form.html.haml
index f287dba9866..7919fde631f 100644
--- a/app/views/admin/application_settings/_external_authorization_service_form.html.haml
+++ b/app/views/admin/application_settings/_external_authorization_service_form.html.haml
@@ -10,7 +10,7 @@
.settings-content
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-external-auth-settings'), html: { class: 'fieldset-form', id: 'external-auth-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_floc.html.haml b/app/views/admin/application_settings/_floc.html.haml
index d63eb2bd09d..e56ba635890 100644
--- a/app/views/admin/application_settings/_floc.html.haml
+++ b/app/views/admin/application_settings/_floc.html.haml
@@ -3,19 +3,20 @@
%section.settings.no-animate#js-floc-settings{ class: ('expanded' if expanded) }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
- = s_('FloC|Federated Learning of Cohorts')
+ = s_('FloC|Federated Learning of Cohorts (FLoC)')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand')
%p
- = s_('FloC|Configure whether you want to participate in FloC.').html_safe
- = link_to sprite_icon('question-o'), 'https://github.com/WICG/floc', target: '_blank', rel: 'noopener noreferrer', class: 'has-tooltip', title: _('More information')
+ - floc_link_url = help_page_path('user/admin_area/settings/floc.md')
+ - floc_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: floc_link_url }
+ = html_escape(s_('FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}')) % { floc_link_start: floc_link_start, floc_link_end: '</a>'.html_safe }
.settings-content
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-floc-settings'), html: { class: 'fieldset-form', id: 'floc-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
= f.gitlab_ui_checkbox_component :floc_enabled,
- s_('FloC|Enable FloC (Federated Learning of Cohorts)')
+ s_('FloC|Participate in FLoC')
= f.submit _('Save changes'), class: 'gl-button btn btn-confirm'
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 7d47ca9a139..b8970a5bcf1 100644
--- a/app/views/admin/application_settings/_git_lfs_limits.html.haml
+++ b/app/views/admin/application_settings/_git_lfs_limits.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: network_admin_application_settings_path(anchor: 'js-git-lfs-limits-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
%h5
diff --git a/app/views/admin/application_settings/_gitaly.html.haml b/app/views/admin/application_settings/_gitaly.html.haml
index cc2c6dbcb03..ade6dac606a 100644
--- a/app/views/admin/application_settings/_gitaly.html.haml
+++ b/app/views/admin/application_settings/_gitaly.html.haml
@@ -1,5 +1,5 @@
= form_for @application_setting, url: preferences_admin_application_settings_path(anchor: 'js-gitaly-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_gitpod.html.haml b/app/views/admin/application_settings/_gitpod.html.haml
index cc1e3f968cb..df534f18bde 100644
--- a/app/views/admin/application_settings/_gitpod.html.haml
+++ b/app/views/admin/application_settings/_gitpod.html.haml
@@ -13,7 +13,7 @@
.settings-content
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-gitpod-settings'), html: { class: 'fieldset-form', id: 'gitpod-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_grafana.html.haml b/app/views/admin/application_settings/_grafana.html.haml
index f17f63c7df7..7f305b9ad9c 100644
--- a/app/views/admin/application_settings/_grafana.html.haml
+++ b/app/views/admin/application_settings/_grafana.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: metrics_and_profiling_admin_application_settings_path(anchor: 'js-grafana-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_help_page.html.haml b/app/views/admin/application_settings/_help_page.html.haml
index 08a4ebe5c71..21eb4caf579 100644
--- a/app/views/admin/application_settings/_help_page.html.haml
+++ b/app/views/admin/application_settings/_help_page.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: preferences_admin_application_settings_path(anchor: 'js-help-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
= render_if_exists 'admin/application_settings/help_text_setting', form: f
diff --git a/app/views/admin/application_settings/_import_export_limits.html.haml b/app/views/admin/application_settings/_import_export_limits.html.haml
index 4e774dd0a1e..bc4a1577f90 100644
--- a/app/views/admin/application_settings/_import_export_limits.html.haml
+++ b/app/views/admin/application_settings/_import_export_limits.html.haml
@@ -1,5 +1,5 @@
= form_for @application_setting, url: network_admin_application_settings_path(anchor: 'js-import-export-limits-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
= html_escape(_("Set any rate limit to %{code_open}0%{code_close} to disable the limit.")) % { code_open: '<code>'.html_safe, code_close: '</code>'.html_safe }
diff --git a/app/views/admin/application_settings/_invitation_flow_enforcement.html.haml b/app/views/admin/application_settings/_invitation_flow_enforcement.html.haml
new file mode 100644
index 00000000000..895662b38fd
--- /dev/null
+++ b/app/views/admin/application_settings/_invitation_flow_enforcement.html.haml
@@ -0,0 +1,8 @@
+- return unless ::Feature.enabled?(:invitation_flow_enforcement_setting)
+
+- form = local_assigns.fetch(:form)
+
+%fieldset.form-group.gl-form-group
+ %legend.col-form-label.col-form-label
+ = s_("AdminSettings|Enforce invitation flow for groups and projects")
+ = form.gitlab_ui_checkbox_component :invitation_flow_enforcement, s_("AdminSettings|Users and groups must accept the invitation before they're added to a group or project.")
diff --git a/app/views/admin/application_settings/_ip_limits.html.haml b/app/views/admin/application_settings/_ip_limits.html.haml
index 9a9038ef48e..4362ae9cb9b 100644
--- a/app/views/admin/application_settings/_ip_limits.html.haml
+++ b/app/views/admin/application_settings/_ip_limits.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: network_admin_application_settings_path(anchor: 'js-ip-limits-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
= _("Rate limits can help reduce request volume (like from crawlers or abusive bots).")
diff --git a/app/views/admin/application_settings/_issue_limits.html.haml b/app/views/admin/application_settings/_issue_limits.html.haml
index 64aca50cbe9..431e2a64c46 100644
--- a/app/views/admin/application_settings/_issue_limits.html.haml
+++ b/app/views/admin/application_settings/_issue_limits.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: network_admin_application_settings_path(anchor: 'js-issue-limits-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_jira_connect_application_key.html.haml b/app/views/admin/application_settings/_jira_connect_application_key.html.haml
index 68a82288573..e3df408cd4c 100644
--- a/app/views/admin/application_settings/_jira_connect_application_key.html.haml
+++ b/app/views/admin/application_settings/_jira_connect_application_key.html.haml
@@ -12,7 +12,7 @@
.settings-content
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-jira-connect-application-id-settings'), html: { class: 'fieldset-form', id: 'jira-connect-application-id-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_kroki.html.haml b/app/views/admin/application_settings/_kroki.html.haml
index c0ac924407f..4f5a313d7b7 100644
--- a/app/views/admin/application_settings/_kroki.html.haml
+++ b/app/views/admin/application_settings/_kroki.html.haml
@@ -10,7 +10,7 @@
= link_to _('Learn more.'), help_page_path('administration/integration/kroki.md'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-kroki-settings'), html: { class: 'fieldset-form', id: 'kroki-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true) if expanded
+ = form_errors(@application_setting) if expanded
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_localization.html.haml b/app/views/admin/application_settings/_localization.html.haml
index 0477f114bdf..a6ed48ef4fe 100644
--- a/app/views/admin/application_settings/_localization.html.haml
+++ b/app/views/admin/application_settings/_localization.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: preferences_admin_application_settings_path(anchor: 'js-localization-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_mailgun.html.haml b/app/views/admin/application_settings/_mailgun.html.haml
index cbe7e1c5bb6..1604419869c 100644
--- a/app/views/admin/application_settings/_mailgun.html.haml
+++ b/app/views/admin/application_settings/_mailgun.html.haml
@@ -9,7 +9,7 @@
= _('Configure the %{link} integration.').html_safe % { link: link_to(_('Mailgun events'), 'https://documentation.mailgun.com/en/latest/user_manual.html#webhooks', target: '_blank', rel: 'noopener noreferrer') }
.settings-content
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-mailgun-settings'), html: { class: 'fieldset-form', id: 'mailgun-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true) if expanded
+ = form_errors(@application_setting) if expanded
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_network_rate_limits.html.haml b/app/views/admin/application_settings/_network_rate_limits.html.haml
index 173e830c7da..f1857a9749a 100644
--- a/app/views/admin/application_settings/_network_rate_limits.html.haml
+++ b/app/views/admin/application_settings/_network_rate_limits.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: network_admin_application_settings_path(anchor: anchor), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
= _("Rate limits can help reduce request volume (like from crawlers or abusive bots).")
diff --git a/app/views/admin/application_settings/_note_limits.html.haml b/app/views/admin/application_settings/_note_limits.html.haml
index b783345b9df..40760b3c45e 100644
--- a/app/views/admin/application_settings/_note_limits.html.haml
+++ b/app/views/admin/application_settings/_note_limits.html.haml
@@ -1,5 +1,5 @@
= form_for @application_setting, url: network_admin_application_settings_path(anchor: 'js-note-limits-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_outbound.html.haml b/app/views/admin/application_settings/_outbound.html.haml
index 2d91b777a0b..bacfe056683 100644
--- a/app/views/admin/application_settings/_outbound.html.haml
+++ b/app/views/admin/application_settings/_outbound.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: network_admin_application_settings_path(anchor: 'js-outbound-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
@@ -15,7 +15,7 @@
= f.text_area :outbound_local_requests_allowlist_raw, placeholder: "example.com, 192.168.1.1, xn--itlab-j1a.com", class: 'form-control gl-form-input', rows: 8
%span.form-text.text-muted
= s_('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.')
- = link_to _('Learn more.'), help_page_path('security/webhooks.md', anchor: 'allowlist-for-local-requests'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to _('Learn more.'), help_page_path('security/webhooks.md', anchor: 'create-an-allowlist-for-local-requests'), target: '_blank', rel: 'noopener noreferrer'
.form-group
= f.gitlab_ui_checkbox_component :dns_rebinding_protection_enabled,
diff --git a/app/views/admin/application_settings/_package_registry.html.haml b/app/views/admin/application_settings/_package_registry.html.haml
index b31576b5c48..4bdfa5bfe83 100644
--- a/app/views/admin/application_settings/_package_registry.html.haml
+++ b/app/views/admin/application_settings/_package_registry.html.haml
@@ -26,7 +26,7 @@
- @plans.each_with_index do |plan, index|
.tab-pane{ :id => "plan#{index}", class: index == 0 ? 'active': '' }
= form_for plan.actual_limits, url: admin_plan_limits_path(anchor: 'js-package-settings'), html: { class: 'fieldset-form' }, method: :post do |f|
- = form_errors(plan, pajamas_alert: true)
+ = form_errors(plan)
%fieldset
= f.hidden_field(:plan_id, value: plan.id)
.form-group
diff --git a/app/views/admin/application_settings/_pages.html.haml b/app/views/admin/application_settings/_pages.html.haml
index 23b0d2d2092..cf43d3ddeca 100644
--- a/app/views/admin/application_settings/_pages.html.haml
+++ b/app/views/admin/application_settings/_pages.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: preferences_admin_application_settings_path(anchor: 'js-pages-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
@@ -22,6 +22,13 @@
- pages_link_url = help_page_path('administration/pages/index', anchor: 'set-maximum-size-of-gitlab-pages-site-in-a-project')
- pages_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: pages_link_url }
= s_('AdminSettings|Set the maximum size of GitLab Pages per project (0 for unlimited). %{link_start}Learn more.%{link_end}').html_safe % { link_start: pages_link_start, link_end: '</a>'.html_safe }
+ .form-group
+ = f.label :max_pages_custom_domains_per_project, s_('AdminSettings|Maximum number of custom domains per project'), class: 'label-bold'
+ = f.number_field :max_pages_custom_domains_per_project, class: 'form-control gl-form-input'
+ .form-text.text-muted
+ - pages_link_url = help_page_path('administration/pages/index', anchor: 'set-maximum-number-of-gitlab-pages-custom-domains-for-a-project')
+ - pages_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: pages_link_url }
+ = s_('AdminSettings|Set the maximum number of GitLab Pages custom domains per project (0 for unlimited). %{link_start}Learn more.%{link_end}').html_safe % { link_start: pages_link_start, link_end: '</a>'.html_safe }
%h5
= s_("AdminSettings|Configure Let's Encrypt")
%p
diff --git a/app/views/admin/application_settings/_performance.html.haml b/app/views/admin/application_settings/_performance.html.haml
index c87d166f8d9..e0ba8d93fbd 100644
--- a/app/views/admin/application_settings/_performance.html.haml
+++ b/app/views/admin/application_settings/_performance.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: network_admin_application_settings_path(anchor: 'js-performance-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_performance_bar.html.haml b/app/views/admin/application_settings/_performance_bar.html.haml
index a7f73edcf69..4e37c4c3c98 100644
--- a/app/views/admin/application_settings/_performance_bar.html.haml
+++ b/app/views/admin/application_settings/_performance_bar.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: metrics_and_profiling_admin_application_settings_path(anchor: 'js-performance-bar-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_pipeline_limits.html.haml b/app/views/admin/application_settings/_pipeline_limits.html.haml
index 3b33c41a924..e93823172db 100644
--- a/app/views/admin/application_settings/_pipeline_limits.html.haml
+++ b/app/views/admin/application_settings/_pipeline_limits.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: network_admin_application_settings_path(anchor: 'js-pipeline-limits-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_plantuml.html.haml b/app/views/admin/application_settings/_plantuml.html.haml
index 8be37ff1dda..5c86ce8dbfb 100644
--- a/app/views/admin/application_settings/_plantuml.html.haml
+++ b/app/views/admin/application_settings/_plantuml.html.haml
@@ -10,7 +10,7 @@
= link_to _('Learn more.'), help_page_path('administration/integration/plantuml.md'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-plantuml-settings'), html: { class: 'fieldset-form', id: 'plantuml-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true) if expanded
+ = form_errors(@application_setting) if expanded
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_prometheus.html.haml b/app/views/admin/application_settings/_prometheus.html.haml
index d8dffd6bc16..982531e9a2f 100644
--- a/app/views/admin/application_settings/_prometheus.html.haml
+++ b/app/views/admin/application_settings/_prometheus.html.haml
@@ -1,13 +1,13 @@
= gitlab_ui_form_for @application_setting, url: metrics_and_profiling_admin_application_settings_path(anchor: 'js-prometheus-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
- prometheus_help_link_url = help_page_path('administration/monitoring/prometheus/gitlab_metrics')
- prometheus_help_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: prometheus_help_link_url }
= f.gitlab_ui_checkbox_component :prometheus_metrics_enabled,
- _('Enable health and performance metrics endpoint'),
- help_text: s_('AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}').html_safe % { link_start: prometheus_help_link_start, link_end: '</a>'.html_safe }
+ _('Enable GitLab Prometheus metrics endpoint'),
+ help_text: s_('AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}.').html_safe % { link_start: prometheus_help_link_start, link_end: '</a>'.html_safe }
.form-text.gl-text-gray-500.gl-pl-6
- unless Gitlab::Metrics.metrics_folder_present?
- icon_link = link_to sprite_icon('question-o'), help_page_path('administration/monitoring/prometheus/gitlab_metrics', anchor: 'metrics-shared-directory'), target: '_blank', rel: 'noopener noreferrer'
diff --git a/app/views/admin/application_settings/_protected_paths.html.haml b/app/views/admin/application_settings/_protected_paths.html.haml
index 00da0f59be4..1f3f67c71c7 100644
--- a/app/views/admin/application_settings/_protected_paths.html.haml
+++ b/app/views/admin/application_settings/_protected_paths.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: network_admin_application_settings_path(anchor: 'js-protected-paths-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_realtime.html.haml b/app/views/admin/application_settings/_realtime.html.haml
index 66003f31104..6a7ec05d206 100644
--- a/app/views/admin/application_settings/_realtime.html.haml
+++ b/app/views/admin/application_settings/_realtime.html.haml
@@ -1,5 +1,5 @@
= form_for @application_setting, url: preferences_admin_application_settings_path(anchor: 'js-realtime-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_registry.html.haml b/app/views/admin/application_settings/_registry.html.haml
index db4d1cb323c..856db32e088 100644
--- a/app/views/admin/application_settings/_registry.html.haml
+++ b/app/views/admin/application_settings/_registry.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: ci_cd_admin_application_settings_path(anchor: 'js-registry-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_repository_check.html.haml b/app/views/admin/application_settings/_repository_check.html.haml
index 40d847f4949..ef8d3ccc8ab 100644
--- a/app/views/admin/application_settings/_repository_check.html.haml
+++ b/app/views/admin/application_settings/_repository_check.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: repository_admin_application_settings_path(anchor: 'js-repository-check-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.sub-section
diff --git a/app/views/admin/application_settings/_repository_mirrors_form.html.haml b/app/views/admin/application_settings/_repository_mirrors_form.html.haml
index 156a6bbcfa6..dad8d5f3fae 100644
--- a/app/views/admin/application_settings/_repository_mirrors_form.html.haml
+++ b/app/views/admin/application_settings/_repository_mirrors_form.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: repository_admin_application_settings_path(anchor: 'js-mirror-settings') do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
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 a8e109ce377..d962d050ebc 100644
--- a/app/views/admin/application_settings/_repository_static_objects.html.haml
+++ b/app/views/admin/application_settings/_repository_static_objects.html.haml
@@ -1,5 +1,5 @@
= 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, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_repository_storage.html.haml b/app/views/admin/application_settings/_repository_storage.html.haml
index ff10e4a8f77..9e7f2812d64 100644
--- a/app/views/admin/application_settings/_repository_storage.html.haml
+++ b/app/views/admin/application_settings/_repository_storage.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: repository_admin_application_settings_path(anchor: 'js-repository-storage-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.sub-section
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 7781db29bab..1d6051a06ea 100644
--- a/app/views/admin/application_settings/_runner_registrars_form.html.haml
+++ b/app/views/admin/application_settings/_runner_registrars_form.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: ci_cd_admin_application_settings_path(anchor: 'js-runner-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.gl-form-group
diff --git a/app/views/admin/application_settings/_search_limits.html.haml b/app/views/admin/application_settings/_search_limits.html.haml
index 93637b59d60..945c9397f0d 100644
--- a/app/views/admin/application_settings/_search_limits.html.haml
+++ b/app/views/admin/application_settings/_search_limits.html.haml
@@ -1,5 +1,5 @@
= 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, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_sentry.html.haml b/app/views/admin/application_settings/_sentry.html.haml
index ece8f50151a..cfd34f6ca15 100644
--- a/app/views/admin/application_settings/_sentry.html.haml
+++ b/app/views/admin/application_settings/_sentry.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: metrics_and_profiling_admin_application_settings_path(anchor: 'js-sentry-settings'), html: { class: 'fieldset-form', id: 'sentry-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%span.text-muted
= _('Changing any setting here requires an application restart')
diff --git a/app/views/admin/application_settings/_sidekiq_job_limits.html.haml b/app/views/admin/application_settings/_sidekiq_job_limits.html.haml
index a28e6e62e7f..eaf4bbf4702 100644
--- a/app/views/admin/application_settings/_sidekiq_job_limits.html.haml
+++ b/app/views/admin/application_settings/_sidekiq_job_limits.html.haml
@@ -1,5 +1,5 @@
= form_for @application_setting, url: preferences_admin_application_settings_path(anchor: 'js-sidekiq-job-limits-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_signin.html.haml b/app/views/admin/application_settings/_signin.html.haml
index 870bfbf4184..48f0b9b2c31 100644
--- a/app/views/admin/application_settings/_signin.html.haml
+++ b/app/views/admin/application_settings/_signin.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-signin-settings'), html: { class: 'fieldset-form', id: 'signin-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_signup.html.haml b/app/views/admin/application_settings/_signup.html.haml
index 2365daa2c70..fccf039533b 100644
--- a/app/views/admin/application_settings/_signup.html.haml
+++ b/app/views/admin/application_settings/_signup.html.haml
@@ -1,3 +1,3 @@
-= form_errors(@application_setting, pajamas_alert: true)
+= form_errors(@application_setting)
#js-signup-form{ data: signup_form_data }
diff --git a/app/views/admin/application_settings/_snowplow.html.haml b/app/views/admin/application_settings/_snowplow.html.haml
index d500194b742..8684b909853 100644
--- a/app/views/admin/application_settings/_snowplow.html.haml
+++ b/app/views/admin/application_settings/_snowplow.html.haml
@@ -10,7 +10,7 @@
= html_escape(_('Configure %{link} to track events. %{link_start}Learn more.%{link_end}')) % { link: link_to('Snowplow', 'https://snowplowanalytics.com/', target: '_blank', rel: 'noopener noreferrer').html_safe, link_start: link_start, link_end: '</a>'.html_safe }
.settings-content
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-snowplow-settings'), html: { class: 'fieldset-form', id: 'snowplow-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true) if expanded
+ = form_errors(@application_setting) if expanded
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_sourcegraph.html.haml b/app/views/admin/application_settings/_sourcegraph.html.haml
index 43ff2bc02f5..9e99b496ad0 100644
--- a/app/views/admin/application_settings/_sourcegraph.html.haml
+++ b/app/views/admin/application_settings/_sourcegraph.html.haml
@@ -17,7 +17,7 @@
.settings-content
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-sourcegraph-settings'), html: { class: 'fieldset-form', id: 'sourcegraph-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_spam.html.haml b/app/views/admin/application_settings/_spam.html.haml
index 7f3125d91ba..bb512940be2 100644
--- a/app/views/admin/application_settings/_spam.html.haml
+++ b/app/views/admin/application_settings/_spam.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: reporting_admin_application_settings_path(anchor: 'js-spam-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
%h5
diff --git a/app/views/admin/application_settings/_terminal.html.haml b/app/views/admin/application_settings/_terminal.html.haml
index 5703fbb463e..c53f63e124b 100644
--- a/app/views/admin/application_settings/_terminal.html.haml
+++ b/app/views/admin/application_settings/_terminal.html.haml
@@ -1,5 +1,5 @@
= 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, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_terms.html.haml b/app/views/admin/application_settings/_terms.html.haml
index c5387db59ef..a4b6e061c43 100644
--- a/app/views/admin/application_settings/_terms.html.haml
+++ b/app/views/admin/application_settings/_terms.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-terms-settings'), html: { class: 'fieldset-form', id: 'terms-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_third_party_offers.html.haml b/app/views/admin/application_settings/_third_party_offers.html.haml
index 397b47eefaa..20a60ac870a 100644
--- a/app/views/admin/application_settings/_third_party_offers.html.haml
+++ b/app/views/admin/application_settings/_third_party_offers.html.haml
@@ -9,7 +9,7 @@
= _('Control whether to display customer experience improvement content and third-party offers in GitLab.')
.settings-content
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-third-party-offers-settings'), html: { class: 'fieldset-form', id: 'third-party-offers-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true) if expanded
+ = form_errors(@application_setting) if expanded
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_usage.html.haml b/app/views/admin/application_settings/_usage.html.haml
index 7326a63f8c2..046b59dbd18 100644
--- a/app/views/admin/application_settings/_usage.html.haml
+++ b/app/views/admin/application_settings/_usage.html.haml
@@ -3,7 +3,7 @@
- link_end = '</a>'.html_safe
= gitlab_ui_form_for @application_setting, url: metrics_and_profiling_admin_application_settings_path(anchor: 'js-usage-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
diff --git a/app/views/admin/application_settings/_users_api_limits.html.haml b/app/views/admin/application_settings/_users_api_limits.html.haml
index f2edb81141d..3918c76b12c 100644
--- a/app/views/admin/application_settings/_users_api_limits.html.haml
+++ b/app/views/admin/application_settings/_users_api_limits.html.haml
@@ -1,5 +1,5 @@
= form_for @application_setting, url: network_admin_application_settings_path(anchor: 'js-users-api-limits-settings'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
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 d35fba7d3b2..b69b2f74d0d 100644
--- a/app/views/admin/application_settings/_visibility_and_access.html.haml
+++ b/app/views/admin/application_settings/_visibility_and_access.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-visibility-settings'), html: { class: 'fieldset-form', id: 'visibility-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
= render 'shared/project_creation_levels', f: f, method: :default_project_creation, legend: s_('ProjectCreationLevel|Default project creation protection')
diff --git a/app/views/admin/application_settings/_whats_new.html.haml b/app/views/admin/application_settings/_whats_new.html.haml
index d82bb1c94e4..3248969ca16 100644
--- a/app/views/admin/application_settings/_whats_new.html.haml
+++ b/app/views/admin/application_settings/_whats_new.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application_setting, url: preferences_admin_application_settings_path(anchor: 'js-whats-new-settings'), html: { class: 'fieldset-form whats-new-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
- whats_new_variants.each_key do |variant|
.gl-mb-4
diff --git a/app/views/admin/application_settings/appearances/_form.html.haml b/app/views/admin/application_settings/appearances/_form.html.haml
index 349e1dfde5d..a3bd8b52148 100644
--- a/app/views/admin/application_settings/appearances/_form.html.haml
+++ b/app/views/admin/application_settings/appearances/_form.html.haml
@@ -1,7 +1,7 @@
- parsed_with_gfm = (_("Content parsed with %{link}.") % { link: link_to('GitLab Flavored Markdown', help_page_path('user/markdown'), target: '_blank') }).html_safe
= gitlab_ui_form_for @appearance, url: admin_application_settings_appearances_path, html: { class: 'gl-mt-3' } do |f|
- = form_errors(@appearance, pajamas_alert: true)
+ = form_errors(@appearance)
.row
diff --git a/app/views/admin/application_settings/general.html.haml b/app/views/admin/application_settings/general.html.haml
index d7559fcd48b..cd63873a893 100644
--- a/app/views/admin/application_settings/general.html.haml
+++ b/app/views/admin/application_settings/general.html.haml
@@ -13,7 +13,7 @@
.settings-content
= render 'visibility_and_access'
-%section.settings.as-account-limit.no-animate#js-account-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'account_and_limit_settings_content' } }
+%section.settings.as-account-limit.no-animate#js-account-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'account_and_limit_settings_content', testid: 'account-limit' } }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= _('Account and limit')
@@ -94,7 +94,7 @@
= _('Manage Web IDE features.')
.settings-content
= gitlab_ui_form_for @application_setting, url: general_admin_application_settings_path(anchor: "js-web-ide-settings"), html: { class: 'fieldset-form', id: 'web-ide-settings' } do |f|
- = form_errors(@application_setting, pajamas_alert: true)
+ = form_errors(@application_setting)
%fieldset
.form-group
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 d4476bf838a..b79b189e9cf 100644
--- a/app/views/admin/application_settings/metrics_and_profiling.html.haml
+++ b/app/views/admin/application_settings/metrics_and_profiling.html.haml
@@ -11,7 +11,7 @@
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
- = _('Monitor the health and performance of GitLab with Prometheus.')
+ = _('Monitor GitLab with Prometheus.')
.settings-content
= render 'prometheus'
diff --git a/app/views/admin/applications/_form.html.haml b/app/views/admin/applications/_form.html.haml
index e0926221bcc..fd73d4c5671 100644
--- a/app/views/admin/applications/_form.html.haml
+++ b/app/views/admin/applications/_form.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for [:admin, @application], url: @url, html: {role: 'form'} do |f|
- = form_errors(application, pajamas_alert: true)
+ = form_errors(application)
= content_tag :div, class: 'form-group row' do
.col-sm-2.col-form-label
diff --git a/app/views/admin/background_migrations/index.html.haml b/app/views/admin/background_migrations/index.html.haml
index c8b195219ec..0f76fdce416 100644
--- a/app/views/admin/background_migrations/index.html.haml
+++ b/app/views/admin/background_migrations/index.html.haml
@@ -5,7 +5,7 @@
.gl-flex-grow-1
%h3= s_('BackgroundMigrations|Background Migrations')
%p.light.gl-mb-0
- - learnmore_link = help_page_path('development/database/batched_background_migrations')
+ - learnmore_link = help_page_path('user/admin_area/monitoring/background_migrations')
- learnmore_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: learnmore_link }
= html_escape(s_('BackgroundMigrations|Background migrations are used to perform data migrations whenever a migration exceeds the time limits in our guidelines. %{linkStart}Learn more%{linkEnd}')) % { linkStart: learnmore_link_start, linkEnd: '</a>'.html_safe }
diff --git a/app/views/admin/broadcast_messages/_form.html.haml b/app/views/admin/broadcast_messages/_form.html.haml
index 865b60a74b8..dfd3b87c674 100644
--- a/app/views/admin/broadcast_messages/_form.html.haml
+++ b/app/views/admin/broadcast_messages/_form.html.haml
@@ -2,7 +2,7 @@
= render 'preview'
= gitlab_ui_form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form js-quick-submit js-requires-input'} do |f|
- = form_errors(@broadcast_message, pajamas_alert: true)
+ = form_errors(@broadcast_message)
.form-group.row.mt-4
.col-sm-2.col-form-label
diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml
index a254690de72..69e9e4260b4 100644
--- a/app/views/admin/groups/_form.html.haml
+++ b/app/views/admin/groups/_form.html.haml
@@ -1,42 +1,34 @@
= gitlab_ui_form_for [:admin, @group] do |f|
- = form_errors(@group, pajamas_alert: true)
- .gl-border-b.gl-mb-6
- .row
- .col-lg-4
- %h4.gl-mt-0
- = _('Naming, visibility')
- %p
- = _('Update your group name, description, avatar, and visibility.')
- = link_to _('Learn more about groups.'), help_page_path('user/group/index')
- .col-lg-8
- = render 'shared/groups/group_name_and_path_fields', f: f
- = render 'shared/group_form_description', f: f
- .form-group.gl-form-group{ role: 'group' }
- = f.label :avatar, _("Group avatar"), class: 'gl-display-block col-form-label'
- = render 'shared/choose_avatar_button', f: f
- = render 'shared/old_visibility_level', f: f, visibility_level: visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group, with_label: false
+ = form_errors(@group)
+ = render ::Layouts::HorizontalSectionComponent.new(options: { class: 'gl-mb-6' }) do |c|
+ = c.title { _('Naming, visibility') }
+ = c.description do
+ = _('Update your group name, description, avatar, and visibility.')
+ = link_to _('Learn more about groups.'), help_page_path('user/group/index')
+ = c.body do
+ = render 'shared/groups/group_name_and_path_fields', f: f
+ = render 'shared/group_form_description', f: f
+ .form-group.gl-form-group{ role: 'group' }
+ = f.label :avatar, _("Group avatar"), class: 'gl-display-block col-form-label'
+ = render 'shared/choose_avatar_button', f: f
+ = render 'shared/old_visibility_level', f: f, visibility_level: visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group, with_label: false
- .gl-border-b.gl-pb-3.gl-mb-6
- .row
- .col-lg-4
- %h4.gl-mt-0
- = _('Permissions and group features')
- %p
- = _('Configure advanced permissions, Large File Storage, two-factor authentication, and CI/CD settings.')
- .col-lg-8
- = render_if_exists 'shared/old_repository_size_limit_setting', form: f, type: :group
- = render_if_exists 'admin/namespace_plan', f: f
- .form-group.gl-form-group{ role: 'group' }
- = render 'shared/allow_request_access', form: f
- = render 'groups/group_admin_settings', f: f
- = render_if_exists 'namespaces/shared_runners_minutes_settings', group: @group, form: f
- .gl-mb-3
- .row
- .col-lg-4
- %h4.gl-mt-0
- = _('Admin notes')
- .col-lg-8
- = render 'shared/admin/admin_note_form', f: f
+ = render ::Layouts::HorizontalSectionComponent.new(options: { class: 'gl-pb-3 gl-mb-6' }) do |c|
+ = c.title { _('Permissions and group features') }
+ = c.description do
+ = _('Configure advanced permissions, Large File Storage, two-factor authentication, and CI/CD settings.')
+ = c.body do
+ = render_if_exists 'shared/old_repository_size_limit_setting', form: f, type: :group
+ = render_if_exists 'admin/namespace_plan', f: f
+ .form-group.gl-form-group{ role: 'group' }
+ = render 'shared/allow_request_access', form: f
+ = render 'groups/group_admin_settings', f: f
+ = render_if_exists 'namespaces/shared_runners_minutes_settings', group: @group, form: f
+
+ = render ::Layouts::HorizontalSectionComponent.new(border: false, options: { class: 'gl-pb-3' }) do |c|
+ = c.title { _('Admin notes') }
+ = c.body do
+ = render 'shared/admin/admin_note_form', f: f
- if @group.new_record?
= render Pajamas::AlertComponent.new(dismissible: false) do |c|
diff --git a/app/views/admin/hooks/_form.html.haml b/app/views/admin/hooks/_form.html.haml
index cf3b6e6e0e0..a309e874317 100644
--- a/app/views/admin/hooks/_form.html.haml
+++ b/app/views/admin/hooks/_form.html.haml
@@ -1,4 +1,4 @@
-= form_errors(hook, pajamas_alert: true)
+= form_errors(hook)
.form-group
= form.label :url, _('URL'), class: 'label-bold'
diff --git a/app/views/admin/identities/_form.html.haml b/app/views/admin/identities/_form.html.haml
index 40c4d292e9d..ba7687db9c7 100644
--- a/app/views/admin/identities/_form.html.haml
+++ b/app/views/admin/identities/_form.html.haml
@@ -1,5 +1,5 @@
= form_for [:admin, @user, @identity], html: { class: 'fieldset-form' } do |f|
- = form_errors(@identity, pajamas_alert: true)
+ = form_errors(@identity)
.form-group.row
.col-sm-2.col-form-label
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index 6921c051361..eabb7e51227 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -23,119 +23,120 @@
= last_check_message.html_safe
.row
.col-md-6
- .card
- .card-header
+ = render Pajamas::CardComponent.new(card_options: { class: 'gl-mb-5' }, body_options: { class: 'gl-p-0' }) do |c|
+ - c.header do
= _('Project info:')
- %ul.content-list
- %li
- %span.light
- = _('Name:')
- %strong
- = link_to @project.name, project_path(@project)
- %li
- %span.light
- = _('Namespace:')
- %strong
- - if @project.namespace
- = link_to @project.namespace.human_name, [:admin, @project.personal? ? @project.namespace.owner : @project.group]
- - else
- = s_('ProjectSettings|Global')
- %li
- %span.light
- = _('Owned by:')
- %strong
- - if @project.owners.any?
- = safe_join(@project.owners.map { |owner| link_to(owner.name, [:admin, owner]) }, ", ".html_safe)
- - else
- = _('(deleted)')
-
- %li
- %span.light
- = _('Created by:')
- %strong
- = @project.creator.try(:name) || _('(deleted)')
-
- %li
- %span.light
- = _('Created on:')
- %strong
- = @project.created_at.to_s(:medium)
-
- %li
- %span.light
- = _('ID:')
- %strong
- = @project.id
-
- %li
- %span.light
- = _('http:')
- %strong
- = link_to @project.http_url_to_repo, project_path(@project)
- %li
- %span.light
- = _('ssh:')
- %strong
- = link_to @project.ssh_url_to_repo, project_path(@project)
- - if @project.repository.exists?
- %li
+ - c.body do
+ %ul.content-list
+ %li{ class: 'gl-px-5!' }
%span.light
- = _('Gitaly storage name:')
+ = _('Name:')
%strong
- = @project.repository.storage
- %li
+ = link_to @project.name, project_path(@project)
+ %li{ class: 'gl-px-5!' }
%span.light
- = _('Gitaly relative path:')
+ = _('Namespace:')
%strong
- = @project.repository.relative_path
-
- %li
- = render 'shared/storage_counter_statistics', storage_size: @project.statistics&.storage_size, storage_details: @project.statistics
-
- %li
+ - if @project.namespace
+ = link_to @project.namespace.human_name, [:admin, @project.personal? ? @project.namespace.owner : @project.group]
+ - else
+ = s_('ProjectSettings|Global')
+ %li{ class: 'gl-px-5!' }
%span.light
- = _('last commit:')
+ = _('Owned by:')
%strong
- = last_commit(@project)
+ - if @project.owners.any?
+ = safe_join(@project.owners.map { |owner| link_to(owner.name, [:admin, owner]) }, ", ".html_safe)
+ - else
+ = _('(deleted)')
- %li
+ %li{ class: 'gl-px-5!' }
%span.light
- = _('Git LFS status:')
+ = _('Created by:')
%strong
- = project_lfs_status(@project)
- = link_to sprite_icon('question-o'), help_page_path('topics/git/lfs/index')
- - else
- %li
- %span.light
- = _('repository:')
- %strong.cred
- = _('does not exist')
+ = @project.creator.try(:name) || _('(deleted)')
- - if @project.archived?
- %li
+ %li{ class: 'gl-px-5!' }
%span.light
- = _('archived:')
+ = _('Created on:')
%strong
- = _('project is read-only')
+ = @project.created_at.to_s(:medium)
- = render_if_exists "shared_runner_status", project: @project
+ %li{ class: 'gl-px-5!' }
+ %span.light
+ = _('ID:')
+ %strong
+ = @project.id
- %li
- %span.light
- = _('access:')
- %strong
- %span{ class: visibility_level_color(@project.visibility_level) }
- = visibility_level_icon(@project.visibility_level)
- = visibility_level_label(@project.visibility_level)
+ %li{ class: 'gl-px-5!' }
+ %span.light
+ = _('http:')
+ %strong
+ = link_to @project.http_url_to_repo, project_path(@project)
+ %li{ class: 'gl-px-5!' }
+ %span.light
+ = _('ssh:')
+ %strong
+ = link_to @project.ssh_url_to_repo, project_path(@project)
+ - if @project.repository.exists?
+ %li{ class: 'gl-px-5!' }
+ %span.light
+ = _('Gitaly storage name:')
+ %strong
+ = @project.repository.storage
+ %li{ class: 'gl-px-5!' }
+ %span.light
+ = _('Gitaly relative path:')
+ %strong
+ = @project.repository.relative_path
+
+ %li{ class: 'gl-px-5!' }
+ = render 'shared/storage_counter_statistics', storage_size: @project.statistics&.storage_size, storage_details: @project.statistics
+
+ %li{ class: 'gl-px-5!' }
+ %span.light
+ = _('last commit:')
+ %strong
+ = last_commit(@project)
+
+ %li{ class: 'gl-px-5!' }
+ %span.light
+ = _('Git LFS status:')
+ %strong
+ = project_lfs_status(@project)
+ = link_to sprite_icon('question-o'), help_page_path('topics/git/lfs/index')
+ - else
+ %li{ class: 'gl-px-5!' }
+ %span.light
+ = _('repository:')
+ %strong.cred
+ = _('does not exist')
+
+ - if @project.archived?
+ %li{ class: 'gl-px-5!' }
+ %span.light
+ = _('archived:')
+ %strong
+ = _('project is read-only')
+
+ = render_if_exists "admin/projects/shared_runner_status", project: @project
+
+ %li{ class: 'gl-px-5!' }
+ %span.light
+ = _('access:')
+ %strong
+ %span{ class: visibility_level_color(@project.visibility_level) }
+ = visibility_level_icon(@project.visibility_level)
+ = visibility_level_label(@project.visibility_level)
= render 'shared/custom_attributes', custom_attributes: @project.custom_attributes
= render_if_exists 'admin/projects/geo_status_widget', locals: { project: @project }
- .card
- .card-header
+ = render Pajamas::CardComponent.new(card_options: { class: 'gl-mb-5' }) do |c|
+ - c.header do
= s_('ProjectSettings|Transfer project')
- .card-body
+ - c.body do
= form_for @project, url: transfer_admin_project_path(@project), method: :put do |f|
.form-group.row
.col-sm-3.col-form-label
@@ -150,10 +151,10 @@
.offset-sm-3.col-sm-9
= f.submit _('Transfer'), class: 'gl-button btn btn-confirm'
- .card.repository-check
- .card-header
+ = render Pajamas::CardComponent.new(card_options: { class: 'gl-mb-5 repository-check' }) do |c|
+ - c.header do
= _("Repository check")
- .card-body
+ - c.body do
= form_for @project, url: repository_check_admin_project_path(@project), method: :post do |f|
.form-group
- if @project.last_repository_check_at.nil?
@@ -172,34 +173,36 @@
.col-md-6
- if @group
- .card
- .card-header
+ = render Pajamas::CardComponent.new(card_options: { class: 'gl-mb-5' }, body_options: { class: 'gl-p-0' }, footer_options: { class: 'gl-p-4' }) do |c|
+ - c.header do
%strong= @group.name
= _('group members')
= gl_badge_tag @group_members.size
= render 'shared/members/manage_access_button', path: group_group_members_path(@group)
- %ul.content-list.members-list
- = render partial: 'shared/members/member',
- collection: @group_members, as: :member,
- locals: { membership_source: @project,
- group: @group,
- current_user_is_group_owner: current_user_is_group_owner }
- .card-footer
+ - c.body do
+ %ul.content-list.members-list
+ = render partial: 'shared/members/member',
+ collection: @group_members, as: :member,
+ locals: { membership_source: @project,
+ group: @group,
+ current_user_is_group_owner: current_user_is_group_owner }
+ - c.footer do
= paginate @group_members, param_name: 'group_members_page', theme: 'gitlab'
= render 'shared/members/requests', membership_source: @project, group: @group, requesters: @requesters
- .card
- .card-header
+ = render Pajamas::CardComponent.new(card_options: { class: 'gl-mb-5' }, body_options: { class: 'gl-p-0' }, footer_options: { class: 'gl-p-4' }) do |c|
+ - c.header do
%strong= @project.name
= _('project members')
= gl_badge_tag @project.users.size
= render 'shared/members/manage_access_button', path: project_project_members_path(@project)
- %ul.content-list.project_members.members-list
- = render partial: 'shared/members/member',
- collection: @project_members, as: :member,
- locals: { membership_source: @project,
- group: @group,
- current_user_is_group_owner: current_user_is_group_owner }
- .card-footer
+ - c.body do
+ %ul.content-list.project_members.members-list
+ = render partial: 'shared/members/member',
+ collection: @project_members, as: :member,
+ locals: { membership_source: @project,
+ group: @group,
+ current_user_is_group_owner: current_user_is_group_owner }
+ - c.footer do
= paginate @project_members, param_name: 'project_members_page', theme: 'gitlab'
diff --git a/app/views/admin/sessions/_new_base.html.haml b/app/views/admin/sessions/_new_base.html.haml
index 65eb1358b40..b755b4a442c 100644
--- a/app/views/admin/sessions/_new_base.html.haml
+++ b/app/views/admin/sessions/_new_base.html.haml
@@ -1,7 +1,7 @@
= form_tag(admin_session_path, method: :post, class: 'new_user gl-show-field-errors', 'aria-live': 'assertive') do
.form-group
= label_tag :user_password, _('Password'), class: 'label-bold'
- = password_field_tag 'user[password]', nil, class: 'form-control', autocomplete: 'current-password', required: true, title: _('This field is required.'), data: { qa_selector: 'password_field' }
+ = password_field_tag 'user[password]', nil, class: 'form-control', autocomplete: 'current-password', required: true, title: _('This field is required.'), data: { qa_selector: 'password_field', testid: 'password-field' }
.submit-container.move-submit-down
= submit_tag _('Enter Admin Mode'), class: 'gl-button btn btn-confirm', data: { qa_selector: 'enter_admin_mode_button' }
diff --git a/app/views/admin/sessions/_signin_box.html.haml b/app/views/admin/sessions/_signin_box.html.haml
index 9372bae14c3..c7382266480 100644
--- a/app/views/admin/sessions/_signin_box.html.haml
+++ b/app/views/admin/sessions/_signin_box.html.haml
@@ -4,8 +4,6 @@
.login-body
= render 'devise/sessions/new_crowd'
- = render_if_exists 'devise/sessions/new_kerberos_tab'
-
- ldap_servers.each_with_index do |server, i|
.login-box.tab-pane{ id: "#{server['provider_name']}", role: 'tabpanel', class: active_when(i == 0 && form_based_auth_provider_has_active_class?(:ldapmain)) }
.login-body
diff --git a/app/views/admin/spam_logs/_spam_log.html.haml b/app/views/admin/spam_logs/_spam_log.html.haml
index cd6df5f30f3..2d0ea585735 100644
--- a/app/views/admin/spam_logs/_spam_log.html.haml
+++ b/app/views/admin/spam_logs/_spam_log.html.haml
@@ -26,11 +26,13 @@
= link_to _('Remove user'), admin_spam_log_path(spam_log, remove_user: true),
data: { confirm: _("USER %{user_name} WILL BE REMOVED! Are you sure?") % { user_name: user.name }, confirm_btn_variant: 'danger' }, aria: { label: _('Remove user') }, method: :delete, class: "gl-button btn btn-sm btn-danger"
%td
- - if spam_log.submitted_as_ham?
- .gl-button.btn.btn-default.btn-sm.disabled.gl-mb-3
- = _("Submitted as ham")
- - else
- = link_to _('Submit as ham'), mark_as_ham_admin_spam_log_path(spam_log), method: :post, class: 'gl-button btn btn-default btn-sm gl-mb-3'
+ -# TODO: Remove conditonal once spamcheck supports this https://gitlab.com/gitlab-com/gl-security/engineering-and-research/automation-team/spam/spamcheck/-/issues/190
+ - if akismet_enabled?
+ - if spam_log.submitted_as_ham?
+ .gl-button.btn.btn-default.btn-sm.disabled.gl-mb-3
+ = _("Submitted as ham")
+ - else
+ = link_to _('Submit as ham'), mark_as_ham_admin_spam_log_path(spam_log), method: :post, class: 'gl-button btn btn-default btn-sm gl-mb-3'
- if user && !user.blocked?
= link_to _('Block user'), block_admin_user_path(user), data: {confirm: _('USER WILL BE BLOCKED! Are you sure?')}, method: :put, class: "gl-button btn btn-default btn-sm gl-mb-3"
- else
diff --git a/app/views/admin/topics/_form.html.haml b/app/views/admin/topics/_form.html.haml
index 1c1bc61aef2..9b9d97950cc 100644
--- a/app/views/admin/topics/_form.html.haml
+++ b/app/views/admin/topics/_form.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @topic, url: url, html: { multipart: true, class: 'js-project-topic-form gl-show-field-errors common-note-form js-quick-submit js-requires-input' }, authenticity_token: true do |f|
- = form_errors(@topic, pajamas_alert: true)
+ = form_errors(@topic)
.form-group
= f.label :name do
diff --git a/app/views/admin/topics/index.html.haml b/app/views/admin/topics/index.html.haml
index 6485b8aa411..77823ed7058 100644
--- a/app/views/admin/topics/index.html.haml
+++ b/app/views/admin/topics/index.html.haml
@@ -1,16 +1,16 @@
- page_title _("Topics")
-= form_tag admin_topics_path, method: :get do |f|
- .gl-py-3.gl-display-flex.gl-flex-direction-column-reverse.gl-md-flex-direction-row.gl-border-b-solid.gl-border-gray-100.gl-border-b-1
- .gl-flex-grow-1.gl-mt-3.gl-md-mt-0
- .inline.gl-w-full.gl-md-w-auto
- - search = params.fetch(:search, nil)
- .search-field-holder
- = search_field_tag :search, search, class: "form-control gl-form-input search-text-input js-search-input", autofocus: true, spellcheck: false, placeholder: _('Search by name'), data: { qa_selector: 'topic_search_field' }
- = sprite_icon('search', css_class: 'search-icon')
- .nav-controls
- = link_to new_admin_topic_path, class: "gl-button btn btn-confirm gl-w-full gl-md-w-auto" do
- = _('New topic')
+.top-area
+ .nav-controls.gl-w-full.gl-mt-3.gl-mb-3
+ = form_tag admin_topics_path, method: :get do |f|
+ - search = params.fetch(:search, nil)
+ .search-field-holder
+ = search_field_tag :search, search, class: "form-control gl-form-input search-text-input js-search-input", autofocus: true, spellcheck: false, placeholder: _('Search by name'), data: { qa_selector: 'topic_search_field' }
+ = 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
+ = _('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 5ac15694922..47a761e608f 100644
--- a/app/views/admin/users/_form.html.haml
+++ b/app/views/admin/users/_form.html.haml
@@ -1,6 +1,6 @@
.user_new
= gitlab_ui_form_for [:admin, @user], html: { class: 'fieldset-form' } do |f|
- = form_errors(@user, pajamas_alert: true)
+ = form_errors(@user)
.gl-border-b.gl-pb-3.gl-mb-6
.row
diff --git a/app/views/award_emoji/_awards_block.html.haml b/app/views/award_emoji/_awards_block.html.haml
index 6cf414dc648..6ed46847482 100644
--- a/app/views/award_emoji/_awards_block.html.haml
+++ b/app/views/award_emoji/_awards_block.html.haml
@@ -1,7 +1,7 @@
- api_awards_path = local_assigns.fetch(:api_awards_path, nil)
- if api_awards_path
- .gl-display-flex.gl-flex-wrap
+ .gl-display-flex.gl-flex-wrap.gl-justify-content-space-between
#js-vue-awards-block{ data: { path: api_awards_path, can_award_emoji: can?(current_user, :award_emoji, awardable).to_s } }
= yield
- else
diff --git a/app/views/clusters/clusters/_gitlab_integration_form.html.haml b/app/views/clusters/clusters/_gitlab_integration_form.html.haml
index e0f5a984529..b6d6dcdd7a9 100644
--- a/app/views/clusters/clusters/_gitlab_integration_form.html.haml
+++ b/app/views/clusters/clusters/_gitlab_integration_form.html.haml
@@ -1,3 +1,3 @@
= form_for @cluster, url: clusterable.cluster_path(@cluster), as: :cluster, html: { class: 'js-cluster-details-form' } do |field|
- = form_errors(@cluster, pajamas_alert: true)
+ = form_errors(@cluster)
#js-cluster-details-form{ data: js_cluster_form_data(@cluster, can?(current_user, :update_cluster, @cluster)) }
diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml
index ed6cecdcc3d..4edb0f324dc 100644
--- a/app/views/dashboard/_activities.html.haml
+++ b/app/views/dashboard/_activities.html.haml
@@ -2,7 +2,7 @@
= 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: 'qa-rss-icon gl-icon')
+ = sprite_icon('rss', css_class: 'gl-icon')
.content_list
.loading
diff --git a/app/views/dashboard/milestones/index.html.haml b/app/views/dashboard/milestones/index.html.haml
index 39fbd9bc097..bc8e3e6ab69 100644
--- a/app/views/dashboard/milestones/index.html.haml
+++ b/app/views/dashboard/milestones/index.html.haml
@@ -9,7 +9,7 @@
- if current_user
.page-title-controls
= render 'shared/new_project_item_select',
- path: '-/milestones/new', label: 'New milestone',
+ path: '-/milestones/new', label: _('Milestone'),
include_groups: true, type: :milestones
- if @milestone_states.any? { |name, count| count > 0 }
@@ -23,7 +23,7 @@
- if current_user
.page-title-controls
= render 'shared/new_project_item_select',
- path: '-/milestones/new', label: 'New milestone',
+ path: '-/milestones/new', label: _('Milestone'),
include_groups: true, type: :milestones
- else
.milestones
@@ -36,5 +36,5 @@
- if current_user
.page-title-controls
= render 'shared/new_project_item_select',
- path: '-/milestones/new', label: 'New milestone',
+ path: '-/milestones/new', label: _('Milestone'),
include_groups: true, type: :milestones
diff --git a/app/views/dashboard/projects/_blank_state_welcome.html.haml b/app/views/dashboard/projects/_blank_state_welcome.html.haml
index 0658d548eab..a9a34af3f96 100644
--- a/app/views/dashboard/projects/_blank_state_welcome.html.haml
+++ b/app/views/dashboard/projects/_blank_state_welcome.html.haml
@@ -11,14 +11,9 @@
%p
= _('Projects are where you store your code, access issues, wiki and other features of GitLab.')
- else
- .blank-state.gl-display-flex.gl-align-items-center.gl-border-1.gl-border-solid.gl-border-gray-100.gl-rounded-base.gl-mb-5
- .blank-state-icon
- = custom_icon("add_new_project", size: 50)
- .blank-state-body.gl-sm-pl-0.gl-pl-6
- %h3.gl-font-size-h2.gl-mt-0
- = _('Create a project')
- %p
- = _('If you are added to a project, it will be displayed here.')
+ = render Pajamas::AlertComponent.new(variant: :info, alert_options: { class: 'gl-mb-5 gl-w-full' }) do |c|
+ = c.body do
+ = _("You see projects here when you're added to a group or project.").html_safe
- if current_user.can_create_group?
= link_to new_group_path, class: link_classes do
diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml
index 8d82116bf10..b4668b1e52a 100644
--- a/app/views/dashboard/todos/_todo.html.haml
+++ b/app/views/dashboard/todos/_todo.html.haml
@@ -1,4 +1,4 @@
-%li.todo{ class: "todo-#{todo.done? ? 'done' : 'pending'}", id: dom_id(todo), data: { url: todo_target_path(todo) } }
+%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)
@@ -49,13 +49,13 @@
.todo-actions.gl-ml-3
- if todo.pending?
- = link_to dashboard_todo_path(todo), method: :delete, class: 'gl-button btn btn-default btn-loading d-flex align-items-center js-done-todo', data: { href: dashboard_todo_path(todo) } do
+ = 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
= gl_loading_icon(inline: true)
= _('Done')
- = link_to restore_dashboard_todo_path(todo), method: :patch, class: 'gl-button btn btn-default btn-loading d-flex align-items-center js-undo-todo hidden', data: { href: restore_dashboard_todo_path(todo) } do
+ = 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
= gl_loading_icon(inline: true)
= _('Undo')
- else
- = link_to restore_dashboard_todo_path(todo), method: :patch, class: 'gl-button btn btn-default btn-loading d-flex align-items-center js-add-todo', data: { href: restore_dashboard_todo_path(todo) } do
+ = 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
= gl_loading_icon(inline: true)
= _('Add a to do')
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index 6bfe18fd3b2..deb1ac9e360 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -93,7 +93,7 @@
.text-content.gl-text-center
- if todos_filter_empty?
%h4
- = Gitlab.config.gitlab.no_todos_messages.sample
+ = no_todos_messages.sample
%p
= (s_("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.") % { strongStart: '<strong>', strongEnd: '</strong>', openIssuesLinkStart: "<a href=\"#{issues_dashboard_path}\">", openIssuesLinkEnd: '</a>', mergeRequestLinkStart: "<a href=\"#{merge_requests_dashboard_path}\">", mergeRequestLinkEnd: '</a>' }).html_safe
- else
diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml
index 5a322a8f89b..3aeb89979bb 100644
--- a/app/views/devise/sessions/_new_base.html.haml
+++ b/app/views/devise/sessions/_new_base.html.haml
@@ -1,10 +1,10 @@
-= gitlab_ui_form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: 'new_user gl-show-field-errors js-sign-in-form', aria: { live: 'assertive' }, data: { testid: 'sign-in-form' }}) do |f|
+= gitlab_ui_form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: 'new_user gl-show-field-errors js-arkose-labs-form', aria: { live: 'assertive' }, data: { testid: 'sign-in-form' }}) do |f|
.form-group.gl-px-5.gl-pt-5
= render_if_exists 'devise/sessions/new_base_user_login_label', form: f
= f.text_field :login, value: @invite_email, class: 'form-control gl-form-input top js-username-field', autofocus: 'autofocus', autocapitalize: 'off', autocorrect: 'off', required: true, title: _('This field is required.'), data: { qa_selector: 'login_field', testid: 'username-field' }
.form-group.gl-px-5
= f.label :password, class: "label-bold #{'gl-mb-1' if Feature.enabled?(:restyle_login_page, @project)}"
- = f.password_field :password, class: 'form-control gl-form-input bottom', autocomplete: 'current-password', required: true, title: _('This field is required.'), data: { qa_selector: 'password_field' }
+ = f.password_field :password, class: 'form-control gl-form-input bottom', autocomplete: 'current-password', required: true, title: _('This field is required.'), data: { qa_selector: 'password_field', testid: 'password-field' }
- if devise_mapping.rememberable?
.gl-px-5
.gl-display-inline-block
diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml
index f4db9ea5637..e0e0b82b596 100644
--- a/app/views/devise/sessions/new.html.haml
+++ b/app/views/devise/sessions/new.html.haml
@@ -17,10 +17,15 @@
%div
= _('No authentication methods configured.')
+ - if Feature.enabled?(:restyle_login_page, @project)
+ %p.gl-px-5
+ = html_escape(s_("SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}.")) % { link_start: "<a href='#{terms_path}' target='_blank' rel='noreferrer noopener'>".html_safe,
+ link_end: '</a>'.html_safe }
+
- if allow_signup?
%p{ class: "gl-mt-3 #{'gl-text-center' if Feature.enabled?(:restyle_login_page, @project)}" }
= _("Don't have an account yet?")
- = link_to _("Register now"), new_registration_path(:user, invite_email: @invite_email), data: { qa_selector: 'register_link' }, class: "#{'gl-font-weight-bold' if Feature.enabled?(:restyle_login_page, @project)} "
+ = link_to _("Register now"), new_registration_path(:user, invite_email: @invite_email), data: { qa_selector: 'register_link' }
- if omniauth_enabled? && devise_mapping.omniauthable? && button_based_providers_enabled?
.clearfix
= render 'devise/shared/omniauth_box'
diff --git a/app/views/devise/sessions/successful_verification.haml b/app/views/devise/sessions/successful_verification.haml
index 8af80fbdceb..59280cc13ca 100644
--- a/app/views/devise/sessions/successful_verification.haml
+++ b/app/views/devise/sessions/successful_verification.haml
@@ -8,4 +8,4 @@
%p.gl-pt-2
- redirect_url_start = '<a href="%{url}"">'.html_safe % { url: @redirect_url }
- redirect_url_end = '</a>'.html_safe
- = html_escape(s_("IdentityVerification|Your account has been successfully verified. You'll be redirected to your account in just a moment or %{redirect_url_start}click here%{redirect_url_end} to refresh.")) % { redirect_url_start: redirect_url_start, redirect_url_end: redirect_url_end }
+ = html_escape(s_("IdentityVerification|Your account has been successfully verified. You'll be redirected to your account in just a moment. You can also %{redirect_url_start}refresh the page%{redirect_url_end}.")) % { redirect_url_start: redirect_url_start, redirect_url_end: redirect_url_end }
diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml
index d67669352a6..d4f34a1cb3f 100644
--- a/app/views/devise/shared/_omniauth_box.html.haml
+++ b/app/views/devise/shared/_omniauth_box.html.haml
@@ -1,19 +1,20 @@
- hide_remember_me = local_assigns.fetch(:hide_remember_me, false)
-%div{ class: Feature.enabled?(:restyle_login_page, @project) ? 'omniauth-container gl-mt-5 gl-p-5 gl-text-center gl-w-90p gl-ml-auto gl-mr-auto' : 'omniauth-container gl-mt-5 gl-p-5' }
- %label{ class: Feature.enabled?(:restyle_login_page, @project) ? 'gl-font-weight-normal' : 'gl-font-weight-bold' }
+- restyle_login_page_enabled = Feature.enabled?(:restyle_login_page, @project)
+%div{ class: restyle_login_page_enabled ? 'omniauth-container gl-mt-5 gl-p-5 gl-text-center gl-w-90p gl-ml-auto gl-mr-auto' : 'omniauth-container gl-mt-5 gl-p-5' }
+ %label{ class: restyle_login_page_enabled ? 'gl-font-weight-normal' : 'gl-font-weight-bold' }
= _('Sign in with')
- providers = enabled_button_based_providers
- .gl-display-flex.gl-justify-content-between.gl-flex-wrap
+ .gl-display-flex.gl-flex-wrap{ class: restyle_login_page_enabled ? 'gl-justify-content-center' : 'gl-justify-content-between' }
- providers.each do |provider|
- has_icon = provider_has_icon?(provider)
- = button_to omniauth_authorize_path(:user, provider), id: "oauth-login-#{provider}", class: "btn gl-button btn-default gl-ml-2 gl-mr-2 gl-mb-2 js-oauth-login #{qa_class_for_provider(provider)} #{'gl-w-full' if Feature.disabled?(:restyle_login_page, @project)}", form: { class: 'gl-w-full gl-mb-3' } do
+ = button_to omniauth_authorize_path(:user, provider), id: "oauth-login-#{provider}", class: "btn gl-button btn-default gl-ml-2 gl-mr-2 gl-mb-2 js-oauth-login #{qa_class_for_provider(provider)} #{'gl-w-full' unless restyle_login_page_enabled}", form: { class: restyle_login_page_enabled ? 'gl-mb-3' : 'gl-w-full gl-mb-3' } do
- if has_icon
= provider_image_tag(provider)
%span.gl-button-text
= label_for_provider(provider)
- unless hide_remember_me
%fieldset
- %label{ class: Feature.enabled?(:restyle_login_page, @project) ? 'gl-font-weight-normal' : '' }
+ %label{ class: restyle_login_page_enabled ? 'gl-font-weight-normal' : '' }
= check_box_tag :remember_me, nil, false
%span
= _('Remember me')
diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml
index ff93449194a..60f1ff02e76 100644
--- a/app/views/devise/shared/_signin_box.html.haml
+++ b/app/views/devise/shared/_signin_box.html.haml
@@ -4,8 +4,6 @@
.login-body
= render 'devise/sessions/new_crowd'
- = render_if_exists 'devise/sessions/new_kerberos_tab'
-
- ldap_servers.each_with_index do |server, i|
.login-box.tab-pane{ id: "#{server['provider_name']}", role: 'tabpanel', class: active_when(i == 0 && form_based_auth_provider_has_active_class?(:ldapmain)) }
.login-body
diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml
index 991af1eea0c..b9c9c99bf1a 100644
--- a/app/views/devise/shared/_signup_box.html.haml
+++ b/app/views/devise/shared/_signup_box.html.haml
@@ -6,7 +6,7 @@
- if show_omniauth_providers && omniauth_providers_placement == :top
= render 'devise/shared/signup_omniauth_providers_top'
- = form_for(resource, as: "new_#{resource_name}", url: url, html: { class: 'new_user gl-show-field-errors', 'aria-live' => 'assertive' }) do |f|
+ = form_for(resource, as: "new_#{resource_name}", url: url, html: { class: 'new_user gl-show-field-errors js-arkose-labs-form', 'aria-live' => 'assertive' }, data: { testid: 'signup-form' }) do |f|
.devise-errors
= render 'devise/shared/error_messages', resource: resource
- if Gitlab::CurrentSettings.invisible_captcha_enabled
@@ -66,8 +66,12 @@
= render_if_exists 'shared/password_requirements_list'
= render_if_exists 'devise/shared/phone_verification', form: f
%div
- - if show_recaptcha_sign_up?
+
+ - if Feature.enabled?(:arkose_labs_signup_challenge)
+ = render_if_exists 'devise/registrations/arkose_labs'
+ - elsif show_recaptcha_sign_up?
= recaptcha_tags nonce: content_security_policy_nonce
+
.submit-container.gl-mt-5
= f.submit button_text, class: 'btn gl-button btn-confirm gl-display-block gl-w-full', data: { qa_selector: 'new_user_register_button' }
- if Gitlab::CurrentSettings.sign_in_text.present? && Feature.enabled?(:restyle_login_page, @project)
diff --git a/app/views/devise/shared/_signup_omniauth_provider_list.haml b/app/views/devise/shared/_signup_omniauth_provider_list.haml
index 8dc22674243..5c085555872 100644
--- a/app/views/devise/shared/_signup_omniauth_provider_list.haml
+++ b/app/views/devise/shared/_signup_omniauth_provider_list.haml
@@ -2,7 +2,7 @@
- if Feature.enabled?(:restyle_login_page, @project)
.gl-text-center.gl-pt-5
%label.gl-font-weight-normal
- = _("Create an account using:")
+ = _("Register with:")
.gl-text-center.gl-w-90p.gl-ml-auto.gl-mr-auto
- providers.each do |provider|
= link_to omniauth_authorize_path(:user, provider, register_omniauth_params), method: :post, class: "btn gl-button btn-default gl-ml-2 gl-mr-2 gl-mb-2 js-oauth-login #{qa_class_for_provider(provider)}", data: { provider: provider }, id: "oauth-login-#{provider}" do
diff --git a/app/views/devise/shared/_tab_single.html.haml b/app/views/devise/shared/_tab_single.html.haml
index 336954d00b0..b7ba8870df5 100644
--- a/app/views/devise/shared/_tab_single.html.haml
+++ b/app/views/devise/shared/_tab_single.html.haml
@@ -1,2 +1,2 @@
= gl_tabs_nav({ class: 'new-session-tabs gl-border-0' }) do
- = gl_tab_link_to tab_title, '#', { item_active: true, class: 'gl-cursor-default!', tab_class: 'gl-bg-transparent!', tabindex: '-1', data: { qa_selector: 'sign_in_tab' } }
+ = gl_tab_link_to tab_title, '#', { item_active: true, class: 'gl-cursor-default!', tab_class: 'gl-bg-transparent!', tabindex: '-1', data: { qa_selector: 'sign_in_tab', testid: 'sign-in-tab' } }
diff --git a/app/views/devise/shared/_tabs_ldap.html.haml b/app/views/devise/shared/_tabs_ldap.html.haml
index 0ef4a30d820..e81a5928983 100644
--- a/app/views/devise/shared/_tabs_ldap.html.haml
+++ b/app/views/devise/shared/_tabs_ldap.html.haml
@@ -1,14 +1,14 @@
- show_password_form = local_assigns.fetch(:show_password_form, password_authentication_enabled_for_web?)
- render_signup_link = local_assigns.fetch(:render_signup_link, true)
-%ul.nav-links.new-session-tabs.nav-tabs.nav{ class: ('custom-provider-tabs' if any_form_based_providers_enabled?) }
+%ul.nav-links.new-session-tabs.nav-tabs.nav{ class: "#{"custom-provider-tabs" if any_form_based_providers_enabled?} #{"nav-links-unboxed" if Feature.enabled?(:restyle_login_page, @project)}" }
- if crowd_enabled?
%li.nav-item
= link_to _("Crowd"), "#crowd", class: "nav-link #{active_when(form_based_auth_provider_has_active_class?(:crowd))}", 'data-toggle' => 'tab', role: 'tab'
= render_if_exists "devise/shared/kerberos_tab"
- ldap_servers.each_with_index do |server, i|
%li.nav-item
- = link_to server['label'], "##{server['provider_name']}", class: "nav-link #{active_when(i == 0 && form_based_auth_provider_has_active_class?(:ldapmain))}", data: { toggle: 'tab', qa_selector: 'ldap_tab' }, role: 'tab'
+ = link_to server['label'], "##{server['provider_name']}", class: "nav-link #{active_when(i == 0 && form_based_auth_provider_has_active_class?(:ldapmain))}", data: { toggle: 'tab', qa_selector: 'ldap_tab', testid: 'ldap-tab' }, role: 'tab'
= render_if_exists 'devise/shared/tab_smartcard'
diff --git a/app/views/devise/shared/_terms_of_service_notice.html.haml b/app/views/devise/shared/_terms_of_service_notice.html.haml
index 1c6dc1f2d5d..c19d64e789d 100644
--- a/app/views/devise/shared/_terms_of_service_notice.html.haml
+++ b/app/views/devise/shared/_terms_of_service_notice.html.haml
@@ -1,9 +1,17 @@
- return unless Gitlab::CurrentSettings.current_application_settings.enforce_terms?
%p.gl-text-gray-500.gl-mt-5.gl-mb-0
- - if Gitlab.com?
- = html_escape(s_("SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}")) % { button_text: button_text,
- link_start: "<a href='#{terms_path}' target='_blank' rel='noreferrer noopener'>".html_safe, link_end: '</a>'.html_safe }
+ - if Feature.enabled?(:restyle_login_page, @project)
+ - if Gitlab.com?
+ = html_escape(s_("SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}")) % { button_text: button_text,
+ link_start: "<a href='#{terms_path}' target='_blank' rel='noreferrer noopener'>".html_safe, link_end: '</a>'.html_safe }
+ - else
+ = html_escape(s_("SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}")) % { button_text: button_text,
+ link_start: "<a href='#{terms_path}' target='_blank' rel='noreferrer noopener'>".html_safe, link_end: '</a>'.html_safe }
- else
- = html_escape(s_("SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}")) % { button_text: button_text,
- link_start: "<a href='#{terms_path}' target='_blank' rel='noreferrer noopener'>".html_safe, link_end: '</a>'.html_safe }
+ - if Gitlab.com?
+ = html_escape(s_("SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}")) % { button_text: button_text,
+ link_start: "<a href='#{terms_path}' target='_blank' rel='noreferrer noopener'>".html_safe, link_end: '</a>'.html_safe }
+ - else
+ = html_escape(s_("SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}")) % { button_text: button_text,
+ link_start: "<a href='#{terms_path}' target='_blank' rel='noreferrer noopener'>".html_safe, link_end: '</a>'.html_safe }
diff --git a/app/views/groups/_activities.html.haml b/app/views/groups/_activities.html.haml
index 757c0a836f3..0d6c3e74ce8 100644
--- a/app/views/groups/_activities.html.haml
+++ b/app/views/groups/_activities.html.haml
@@ -2,7 +2,7 @@
= 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: 'qa-rss-icon gl-icon')
+ = sprite_icon('rss', css_class: 'gl-icon')
.content_list
.loading
diff --git a/app/views/groups/_home_panel.html.haml b/app/views/groups/_home_panel.html.haml
index 2911e9991f2..a82a2e41508 100644
--- a/app/views/groups/_home_panel.html.haml
+++ b/app/views/groups/_home_panel.html.haml
@@ -3,17 +3,17 @@
- emails_disabled = @group.emails_disabled?
.group-home-panel
- .row.mb-3
+ .row.my-3
.home-panel-title-row.col-md-12.col-lg-6.d-flex
.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-mt-3.gl-mb-2{ itemprop: 'name' }
+ %h1.home-panel-title.gl-font-size-h1.gl-mt-3.gl-mb-2{ itemprop: 'name' }
= @group.name
- %span.visibility-icon.text-secondary.gl-ml-2.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) }
+ %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'})
- .home-panel-metadata.text-secondary.gl-font-base.gl-font-weight-normal.gl-line-height-normal{ data: { qa_selector: 'group_id_content' }, itemprop: 'identifier' }
+ .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
= s_("GroupPage|Group ID: %{group_id}") % { group_id: @group.id }
diff --git a/app/views/groups/_new_group_fields.html.haml b/app/views/groups/_new_group_fields.html.haml
index 632884051f0..94b0b018084 100644
--- a/app/views/groups/_new_group_fields.html.haml
+++ b/app/views/groups/_new_group_fields.html.haml
@@ -1,18 +1,18 @@
- parent = @group.parent
- submit_label = parent ? s_('GroupsNew|Create subgroup') : s_('GroupsNew|Create group')
-= form_errors(@group, pajamas_alert: true)
+= form_errors(@group)
= render 'shared/groups/group_name_and_path_fields', f: f, autofocus: true, new_subgroup: !!parent
-- unless parent
- .row
- .form-group.gl-form-group.col-sm-12
- %label.label-bold
- = _('Visibility level')
- %p
- = _('Who will be able to see this group?')
- = link_to _('View the documentation'), help_page_path("user/public_access"), target: '_blank', rel: 'noopener noreferrer'
- = render 'shared/visibility_level', f: f, visibility_level: default_group_visibility, can_change_visibility_level: true, form_model: @group, with_label: false
+.row
+ .form-group.gl-form-group.col-sm-12
+ %label.label-bold
+ = _('Visibility level')
+ %p
+ = _('Who will be able to see this group?')
+ = link_to _('View the documentation'), help_page_path("user/public_access"), target: '_blank', rel: 'noopener noreferrer'
+ = render 'shared/visibility_level', f: f, visibility_level: default_group_visibility, can_change_visibility_level: true, form_model: @group, with_label: false
+- unless parent
- if Gitlab.config.mattermost.enabled
.row
= render 'create_chat_team', f: f
diff --git a/app/views/groups/crm/organizations/index.html.haml b/app/views/groups/crm/organizations/index.html.haml
index ff1ba678de0..f7702889eef 100644
--- a/app/views/groups/crm/organizations/index.html.haml
+++ b/app/views/groups/crm/organizations/index.html.haml
@@ -5,4 +5,4 @@
= content_for :after_content do
#js-crm-form-portal
-#js-crm-organizations-app{ data: { base_path: group_crm_organizations_path(@group), can_admin_crm_organization: can?(current_user, :admin_crm_organization, @group).to_s, group_full_path: @group.full_path, group_id: @group.id, group_issues_path: issues_group_path(@group) } }
+#js-crm-organizations-app{ data: { base_path: group_crm_organizations_path(@group), can_admin_crm_organization: can?(current_user, :admin_crm_organization, @group).to_s, group_full_path: @group.full_path, group_id: @group.id, group_issues_path: issues_group_path(@group), text_query: params[:search] } }
diff --git a/app/views/groups/harbor/repositories/index.html.haml b/app/views/groups/harbor/repositories/index.html.haml
index a8a52b2aba7..59ad29ccabd 100644
--- a/app/views/groups/harbor/repositories/index.html.haml
+++ b/app/views/groups/harbor/repositories/index.html.haml
@@ -4,8 +4,9 @@
#js-harbor-registry-list-group{ data: { endpoint: group_harbor_repositories_path(@group),
"no_containers_image" => image_path('illustrations/docker-empty-state.svg'),
"containers_error_image" => image_path('illustrations/docker-error-state.svg'),
- "repository_url" => 'demo.harbor.com/gitlab-cn/build/cng-images/gitlab-kas',
- "registry_host_url_with_port" => 'demo.harbor.com',
+ "repository_url" => @group.harbor_integration.hostname,
+ "harbor_integration_project_name" => @group.harbor_integration.project_name,
+ full_path: @group.full_path,
connection_error: (!!@connection_error).to_s,
invalid_path_error: (!!@invalid_path_error).to_s,
is_group_page: true.to_s } }
diff --git a/app/views/groups/milestones/_form.html.haml b/app/views/groups/milestones/_form.html.haml
index 9f13ad301bb..3864b30eb1e 100644
--- a/app/views/groups/milestones/_form.html.haml
+++ b/app/views/groups/milestones/_form.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for [@group, @milestone], html: { class: 'milestone-form common-note-form js-quick-submit js-requires-input' } do |f|
- = form_errors(@milestone, pajamas_alert: true)
+ = form_errors(@milestone)
.form-group.row
.col-form-label.col-sm-2
= f.label :title, _("Title")
diff --git a/app/views/groups/observability/index.html.haml b/app/views/groups/observability/index.html.haml
new file mode 100644
index 00000000000..582651c329b
--- /dev/null
+++ b/app/views/groups/observability/index.html.haml
@@ -0,0 +1,2 @@
+- page_title _("Observability")
+%iframe{ id: 'observability-ui-iframe', src: @observability_iframe_src, frameborder: 0, width: "100%", height: "100%" }
diff --git a/app/views/groups/runners/edit.html.haml b/app/views/groups/runners/edit.html.haml
index c5999317597..4bd550eaa47 100644
--- a/app/views/groups/runners/edit.html.haml
+++ b/app/views/groups/runners/edit.html.haml
@@ -1,14 +1,7 @@
+- runner_name = "##{@runner.id} (#{@runner.short_sha})"
- breadcrumb_title _('Edit')
-- page_title _('Edit'), "##{@runner.id} (#{@runner.short_sha})"
-
+- page_title _('Edit'), runner_name
- add_to_breadcrumbs _('Runners'), group_runners_path(@group)
-- add_to_breadcrumbs "#{@runner.short_sha}", group_runner_path(@group, @runner)
-
-
-%h1.page-title.gl-font-size-h-display
- = s_('Runners|Runner #%{runner_id}' % { runner_id: @runner.id })
- = render 'shared/runners/runner_type_badge', runner: @runner
-
-= render 'shared/runners/runner_type_alert', runner: @runner
+- add_to_breadcrumbs runner_name, group_runner_path(@group, @runner)
-= render 'shared/runners/form', runner: @runner, runner_form_url: group_runner_path(@group, @runner)
+#js-group-runner-edit{ data: {runner_id: @runner.id, runner_path: group_runner_path(@group, @runner) } }
diff --git a/app/views/groups/runners/index.html.haml b/app/views/groups/runners/index.html.haml
index a67a4f28c93..1146063969b 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 } ) }
+#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/_advanced.html.haml b/app/views/groups/settings/_advanced.html.haml
index 8fa8eeea3cd..21b1986bd34 100644
--- a/app/views/groups/settings/_advanced.html.haml
+++ b/app/views/groups/settings/_advanced.html.haml
@@ -4,7 +4,7 @@
.sub-section
%h4.warning-title= s_('GroupSettings|Change group URL')
= form_for @group, html: { multipart: true, class: 'gl-show-field-errors' }, authenticity_token: true do |f|
- = form_errors(@group, pajamas_alert: true)
+ = form_errors(@group)
.form-group
%p
= s_("GroupSettings|Changing a group's URL can have unintended side effects.")
diff --git a/app/views/groups/settings/_general.html.haml b/app/views/groups/settings/_general.html.haml
index 527791dfc04..be9d2c45885 100644
--- a/app/views/groups/settings/_general.html.haml
+++ b/app/views/groups/settings/_general.html.haml
@@ -1,6 +1,6 @@
= gitlab_ui_form_for @group, html: { multipart: true, class: 'gl-show-field-errors js-general-settings-form' }, authenticity_token: true do |f|
%input{ type: 'hidden', name: 'update_section', value: 'js-general-settings' }
- = form_errors(@group, pajamas_alert: true)
+ = form_errors(@group)
%fieldset
.row
diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml
index a60ab43f566..e35c0341ec0 100644
--- a/app/views/groups/settings/_permissions.html.haml
+++ b/app/views/groups/settings/_permissions.html.haml
@@ -1,6 +1,6 @@
= gitlab_ui_form_for @group, html: { multipart: true, class: 'gl-show-field-errors js-general-permissions-form' }, authenticity_token: true do |f|
%input{ type: 'hidden', name: 'update_section', value: 'js-permissions-settings' }
- = form_errors(@group, pajamas_alert: true)
+ = form_errors(@group)
%fieldset
%h5= _('Permissions')
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 3691c470ea7..a55ccd94974 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
@@ -1,5 +1,5 @@
= gitlab_ui_form_for group, url: update_auto_devops_group_settings_ci_cd_path(group), method: :patch do |f|
- = form_errors(group, pajamas_alert: true)
+ = form_errors(group)
%fieldset
.form-group
.card.gl-mb-3
diff --git a/app/views/groups/settings/packages_and_registries/show.html.haml b/app/views/groups/settings/packages_and_registries/show.html.haml
index 888419e463a..2861e696e31 100644
--- a/app/views/groups/settings/packages_and_registries/show.html.haml
+++ b/app/views/groups/settings/packages_and_registries/show.html.haml
@@ -1,5 +1,5 @@
-- breadcrumb_title _('Packages & Registries')
-- page_title _('Packages & Registries')
+- breadcrumb_title _('Package and registry settings')
+- page_title _('Package and registry settings')
- @content_class = 'limit-container-width' unless fluid_layout
%section#js-packages-and-registries-settings{ data: { group_path: @group.full_path,
diff --git a/app/views/groups/settings/repository/_default_branch.html.haml b/app/views/groups/settings/repository/_default_branch.html.haml
index cae33820a05..844a5f890a4 100644
--- a/app/views/groups/settings/repository/_default_branch.html.haml
+++ b/app/views/groups/settings/repository/_default_branch.html.haml
@@ -8,7 +8,7 @@
= s_('GroupSettings|Set the initial name and protections for the default branch of new repositories created in the group.')
.settings-content
= gitlab_ui_form_for @group, url: group_path(@group, anchor: 'js-default-branch-name'), html: { class: 'fieldset-form' } do |f|
- = form_errors(@group, pajamas_alert: true)
+ = form_errors(@group)
- fallback_branch_name = "<code>#{Gitlab::DefaultBranch.value(object: @group)}</code>"
%fieldset
diff --git a/app/views/groups/settings/repository/show.html.haml b/app/views/groups/settings/repository/show.html.haml
index d3b9117c05b..a15652b3179 100644
--- a/app/views/groups/settings/repository/show.html.haml
+++ b/app/views/groups/settings/repository/show.html.haml
@@ -2,7 +2,11 @@
- page_title _('Repository')
- @content_class = "limit-container-width" unless fluid_layout
-- deploy_token_description = s_('DeployTokens|Group deploy tokens allow access to the packages, repositories, and registry images within the group.')
+- if can?(current_user, :admin_group, @group)
+ - deploy_token_description = s_('DeployTokens|Group deploy tokens allow access to the packages, repositories, and registry images within the group.')
-= render "shared/deploy_tokens/index", group_or_project: @group, description: deploy_token_description
-= render "default_branch", group: @group
+ = render "shared/deploy_tokens/index", group_or_project: @group, description: deploy_token_description
+ = render "default_branch", group: @group
+
+- if can?(current_user, :change_push_rules, @group)
+ = render "push_rules"
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index d8da77dc5cc..f474f8fbd3b 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -33,33 +33,36 @@
= render_if_exists 'groups/group_activity_analytics', group: @group
-.groups-listing{ data: { endpoints: { default: group_children_path(@group, format: :json), shared: group_shared_projects_path(@group, format: :json) } } }
- .top-area.group-nav-container.justify-content-between
- .scrolling-tabs-container.inner-page-scroll-tabs
- .fade-left= sprite_icon('chevron-lg-left', size: 12)
- .fade-right= sprite_icon('chevron-lg-right', size: 12)
- -# `item_active` is set to `false` as the active state is set by `app/assets/javascripts/pages/groups/shared/group_details.js`
- -# TODO: Replace this approach in https://gitlab.com/gitlab-org/gitlab/-/issues/23466
- = gl_tabs_nav({ class: 'nav-links scrolling-tabs gl-display-flex gl-flex-grow-1 gl-flex-nowrap gl-border-0' }) do
- = gl_tab_link_to group_path, item_active: false, tab_class: 'js-subgroups_and_projects-tab', data: { target: 'div#subgroups_and_projects', action: 'subgroups_and_projects', toggle: 'tab' } do
- = _("Subgroups and projects")
- = gl_tab_link_to group_shared_path, item_active: false, tab_class: 'js-shared-tab', data: { target: 'div#shared', action: 'shared', toggle: 'tab' } do
- = _("Shared projects")
- = gl_tab_link_to group_archived_path, item_active: false, tab_class: 'js-archived-tab', data: { target: 'div#archived', action: 'archived', toggle: 'tab' } do
- = _("Archived projects")
+- if Feature.enabled?(:group_overview_tabs_vue, @group)
+ #js-group-overview-tabs{ data: group_overview_tabs_app_data(@group) }
+- else
+ .groups-listing{ data: { endpoints: { default: group_children_path(@group, format: :json), shared: group_shared_projects_path(@group, format: :json) } } }
+ .top-area.group-nav-container.justify-content-between
+ .scrolling-tabs-container.inner-page-scroll-tabs
+ .fade-left= sprite_icon('chevron-lg-left', size: 12)
+ .fade-right= sprite_icon('chevron-lg-right', size: 12)
+ -# `item_active` is set to `false` as the active state is set by `app/assets/javascripts/pages/groups/shared/group_details.js`
+ -# TODO: Replace this approach in https://gitlab.com/gitlab-org/gitlab/-/issues/23466
+ = gl_tabs_nav({ class: 'nav-links scrolling-tabs gl-display-flex gl-flex-grow-1 gl-flex-nowrap gl-border-0' }) do
+ = gl_tab_link_to group_path, item_active: false, tab_class: 'js-subgroups_and_projects-tab', data: { target: 'div#subgroups_and_projects', action: 'subgroups_and_projects', toggle: 'tab' } do
+ = _("Subgroups and projects")
+ = gl_tab_link_to group_shared_path, item_active: false, tab_class: 'js-shared-tab', data: { target: 'div#shared', action: 'shared', toggle: 'tab' } do
+ = _("Shared projects")
+ = gl_tab_link_to group_archived_path, item_active: false, tab_class: 'js-archived-tab', data: { target: 'div#archived', action: 'archived', toggle: 'tab' } do
+ = _("Archived projects")
- .nav-controls.d-block.d-md-flex
- .group-search
- = render "shared/groups/search_form"
+ .nav-controls.d-block.d-md-flex
+ .group-search
+ = render "shared/groups/search_form"
- = render "shared/groups/dropdown", options_hash: subgroups_sort_options_hash
+ = render "shared/groups/dropdown", options_hash: subgroups_sort_options_hash
- .tab-content
- #subgroups_and_projects.tab-pane
- = render "subgroups_and_projects", group: @group
+ .tab-content
+ #subgroups_and_projects.tab-pane
+ = render "subgroups_and_projects", group: @group
- #shared.tab-pane
- = render "shared_projects", group: @group
+ #shared.tab-pane
+ = render "shared_projects", group: @group
- #archived.tab-pane
- = render "archived_projects", group: @group
+ #archived.tab-pane
+ = render "archived_projects", group: @group
diff --git a/app/views/help/drawers.html.haml b/app/views/help/drawers.html.haml
new file mode 100644
index 00000000000..7c173eb7b07
--- /dev/null
+++ b/app/views/help/drawers.html.haml
@@ -0,0 +1,2 @@
+= cache(@clean_path, expires_in: 1.day) do
+ = markdown get_markdown_without_frontmatter(@path)
diff --git a/app/views/jira_connect/subscriptions/index.html.haml b/app/views/jira_connect/subscriptions/index.html.haml
index d4ced15b869..f66aa0840aa 100644
--- a/app/views/jira_connect/subscriptions/index.html.haml
+++ b/app/views/jira_connect/subscriptions/index.html.haml
@@ -1,4 +1,4 @@
-.js-jira-connect-app{ data: jira_connect_app_data(@subscriptions) }
+.js-jira-connect-app{ data: jira_connect_app_data(@subscriptions, @current_jira_installation) }
= webpack_bundle_tag 'performance_bar' if performance_bar_enabled?
= webpack_bundle_tag 'jira_connect_app'
diff --git a/app/views/layouts/_google_tag_manager_head.html.haml b/app/views/layouts/_google_tag_manager_head.html.haml
index f5c823465be..97e118aba93 100644
--- a/app/views/layouts/_google_tag_manager_head.html.haml
+++ b/app/views/layouts/_google_tag_manager_head.html.haml
@@ -6,19 +6,20 @@
function gtag(){dataLayer.push(arguments);}
gtag('consent', 'default', {
- 'analytics_storage': 'denied',
- 'ad_storage': 'denied',
- 'functionality_storage': 'denied',
- 'region': ['EU', 'UK', 'PE', 'RU'],
- 'wait_for_update': 500
- });
- gtag('consent', 'default', {
'analytics_storage': 'granted',
'ad_storage': 'granted',
'functionality_storage': 'granted',
'wait_for_update': 500
});
+ gtag('consent', 'default', {
+ 'analytics_storage': 'denied',
+ 'ad_storage': 'denied',
+ 'functionality_storage': 'denied',
+ 'region': ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL', 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE', 'IS', 'LI', 'NO', 'GB', 'PE', 'RU'],
+ 'wait_for_update': 500
+ });
+
- if Feature.enabled?(:gtm_nonce, type: :ops)
= javascript_tag nonce: content_security_policy_nonce do
:plain
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 59d4c81358d..014e26c7613 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -27,6 +27,7 @@
%div{ class: "#{(container_class unless @no_container)} #{@content_class}" }
%main.content{ id: "content-body", **page_itemtype }
= render "layouts/flash", extra_flash_class: 'limit-container-width'
+ = yield :after_flash_content
= yield :before_content
= yield
= yield :after_content
diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml
index 87a8b6dd870..6650e07be2a 100644
--- a/app/views/layouts/devise.html.haml
+++ b/app/views/layouts/devise.html.haml
@@ -18,10 +18,10 @@
= current_appearance&.title.presence || _('GitLab')
- if current_appearance&.description?
= brand_text
+ = render_if_exists 'layouts/devise_help_text'
.mb-3
.gl-w-half.gl-xs-w-full.gl-ml-auto.gl-mr-auto.bar
= yield
- = render_if_exists 'layouts/devise_help_text'
= render 'devise/shared/footer', footer_message: footer_message
diff --git a/app/views/layouts/fullscreen.html.haml b/app/views/layouts/fullscreen.html.haml
index 2a865aeda40..61a57240ed5 100644
--- a/app/views/layouts/fullscreen.html.haml
+++ b/app/views/layouts/fullscreen.html.haml
@@ -6,12 +6,16 @@
= header_message
= render partial: "layouts/header/default", locals: { project: @project, group: @group }
.mobile-overlay
- .alert-wrapper.hide-when-top-nav-responsive-open
- = render 'shared/outdated_browser'
- = render "layouts/broadcast"
- = yield :flash_message
- = render "layouts/flash"
- .content-wrapper.hide-when-top-nav-responsive-open{ id: "content-body", class: "d-flex flex-column align-items-stretch" }
- = yield
+ .hide-when-top-nav-responsive-open.gl--flex-full.gl-h-full{ class: nav ? ["layout-page", page_with_sidebar_class, "gl-mt-0!"]: '' }
+ - if defined?(nav) && nav
+ = render "layouts/nav/sidebar/#{nav}"
+ .gl--flex-full.gl-flex-direction-column.gl-w-full
+ .alert-wrapper
+ = render 'shared/outdated_browser'
+ = render "layouts/broadcast"
+ = yield :flash_message
+ = render "layouts/flash"
+ .content-wrapper{ id: "content-body", class: "d-flex flex-column align-items-stretch" }
+ = yield
= render "layouts/nav/top_nav_responsive", class: "gl-flex-grow-1 gl-overflow-y-auto"
= footer_message
diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml
index 67809cbc608..97c2b8bb7e3 100644
--- a/app/views/layouts/group.html.haml
+++ b/app/views/layouts/group.html.haml
@@ -4,9 +4,10 @@
- nav "group"
- display_subscription_banner!
- @left_sidebar = true
+- base_layout = local_assigns[:base_layout]
- content_for :flash_message do
- = render "layouts/header/storage_enforcement_banner", context: @group
+ = dispensable_render_if_exists "groups/storage_enforcement_alert", context: @group
= dispensable_render_if_exists "shared/namespace_storage_limit_alert", context: @group
- content_for :page_specific_javascripts do
@@ -15,4 +16,4 @@
:plain
window.uploads_path = "#{group_uploads_path(@group)}";
-= render template: "layouts/application"
+= render template: base_layout || "layouts/application"
diff --git a/app/views/layouts/header/_current_user_dropdown.html.haml b/app/views/layouts/header/_current_user_dropdown.html.haml
index 353f07c07c5..00e7a0567da 100644
--- a/app/views/layouts/header/_current_user_dropdown.html.haml
+++ b/app/views/layouts/header/_current_user_dropdown.html.haml
@@ -37,7 +37,7 @@
%li.d-md-none
= render 'shared/help_dropdown_forum_link'
%li.d-md-none
- = link_to _("Submit feedback"), "https://about.gitlab.com/submit-feedback"
+ = link_to _("Submit feedback"), Gitlab::Utils.append_path(promo_url, "submit-feedback")
- if current_user_menu?(:help) || current_user_menu?(:settings) || current_user_menu?(:profile)
%li.d-md-none
= render 'shared/user_dropdown_contributing_link'
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 783733bb313..a00c5c186cc 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -5,15 +5,15 @@
%a.gl-sr-only.gl-accessibility{ href: "#content-body" } Skip to content
.container-fluid
.header-content.js-header-content
- .title-container.hide-when-top-nav-responsive-open.gl-transition-medium.gl-display-flex.gl-align-items-stretch.gl-pt-0
+ .title-container.hide-when-top-nav-responsive-open.gl-transition-medium.gl-display-flex.gl-align-items-stretch.gl-pt-0.gl-mr-3
.title
%span.gl-sr-only GitLab
= link_to root_path, title: _('Dashboard'), id: 'logo', class: 'has-tooltip', **tracking_attrs('main_navigation', 'click_gitlab_logo_link', 'navigation') do
= brand_header_logo
+ .gl-display-flex.gl-align-items-center
- if Gitlab.com_and_canary?
- = link_to Gitlab::Saas.canary_toggle_com_url, class: 'canary-badge bg-transparent', data: { qa_selector: 'canary_badge_link' }, target: :_blank, rel: 'noopener noreferrer' do
- = gl_badge_tag({ variant: :success, size: :sm }) do
- = _('Next')
+ = gl_badge_tag({ variant: :success, size: :sm }, { href: Gitlab::Saas.canary_toggle_com_url, data: { qa_selector: 'canary_badge_link' }, target: :_blank, rel: 'noopener noreferrer', class: 'canary-badge' }) do
+ = _('Next')
- if current_user
.gl-display-none.gl-sm-display-block
@@ -28,11 +28,25 @@
.gl-display-none.gl-sm-display-block
= render "layouts/nav/top_nav"
- .navbar-collapse.gl-transition-medium.collapse
+ - if top_nav_show_search && Feature.enabled?(:new_navbar_layout)
+ .navbar-collapse.gl-transition-medium.collapse.gl-mr-auto.global-search-container.hide-when-top-nav-responsive-open
+ - search_menu_item = top_nav_search_menu_item_attrs
+ %ul.nav.navbar-nav.gl-w-full.gl-align-items-center
+ %li.nav-item.header-search-new.gl-display-none.gl-lg-display-block.gl-w-full
+ - unless current_controller?(:search)
+ - if Feature.enabled?(:new_header_search)
+ = render 'layouts/header_search'
+ - else
+ = render 'layouts/search'
+ %li.nav-item{ class: 'd-none d-sm-inline-block d-lg-none' }
+ = link_to search_menu_item.fetch(:href), title: search_menu_item.fetch(:title), aria: { label: search_menu_item.fetch(:title) }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = sprite_icon(search_menu_item.fetch(:icon))
+
+ .navbar-collapse.gl-transition-medium.collapse{ class: ('global-search-container' unless Feature.enabled?(:new_navbar_layout)) }
%ul.nav.navbar-nav.gl-w-full.gl-align-items-center.gl-justify-content-end
- if current_user
= render 'layouts/header/new_dropdown', class: 'gl-display-none gl-sm-display-block gl-white-space-nowrap gl-text-right'
- - if top_nav_show_search
+ - if top_nav_show_search && Feature.disabled?(:new_navbar_layout)
- search_menu_item = top_nav_search_menu_item_attrs
%li.nav-item.header-search-new.gl-display-none.gl-lg-display-block.gl-w-full
- unless current_controller?(:search)
@@ -116,7 +130,7 @@
= render "layouts/nav/top_nav"
- e.control {}
- if header_link?(:user_dropdown)
- %li.nav-item.header-user.js-nav-user-dropdown.dropdown{ data: { track_label: "profile_dropdown", track_action: "click_dropdown", track_value: "", qa_selector: 'user_menu' }, class: ('mr-0' if has_impersonation_link) }
+ %li.nav-item.header-user.js-nav-user-dropdown.dropdown{ data: { track_label: "profile_dropdown", track_action: "click_dropdown", track_value: "", qa_selector: 'user_menu', testid: 'user-menu' }, class: ('mr-0' if has_impersonation_link) }
= link_to current_user, class: user_dropdown_class, data: { toggle: "dropdown" } do
= image_tag avatar_icon_for_user(current_user, 23), width: 23, height: 23, class: "header-user-avatar qa-user-avatar", alt: current_user.name
= render_if_exists 'layouts/header/user_notification_dot', project: project, namespace: group
diff --git a/app/views/layouts/header/_storage_enforcement_banner.html.haml b/app/views/layouts/header/_storage_enforcement_banner.html.haml
deleted file mode 100644
index 1f7060f8235..00000000000
--- a/app/views/layouts/header/_storage_enforcement_banner.html.haml
+++ /dev/null
@@ -1,15 +0,0 @@
-- return unless current_user
-- context = local_assigns.fetch(:context)
-- banner_info = storage_enforcement_banner_info(context)
-- return unless banner_info.present?
-
-= render Pajamas::AlertComponent.new(variant: :warning,
- alert_options: { class: 'js-storage-enforcement-banner',
- data: { feature_id: banner_info[:callouts_feature_name],
- dismiss_endpoint: banner_info[:callouts_path],
- group_id: banner_info[:namespace_id],
- defer_links: "true" }}) do |c|
- = c.body do
- %p= banner_info[:text_paragraph_1]
- %p= banner_info[:text_paragraph_2]
- %p= banner_info[:text_paragraph_3]
diff --git a/app/views/layouts/nav/_top_nav.html.haml b/app/views/layouts/nav/_top_nav.html.haml
index 42119ddb291..aa1c462d2bf 100644
--- a/app/views/layouts/nav/_top_nav.html.haml
+++ b/app/views/layouts/nav/_top_nav.html.haml
@@ -1,9 +1,10 @@
- view_model = top_nav_view_model(project: @project, group: @group)
-%ul.list-unstyled.navbar-sub-nav#js-top-nav{ data: { view_model: view_model.to_json } }
+%ul.list-unstyled.nav.navbar-sub-nav#js-top-nav{ data: { view_model: view_model.to_json } }
%li
%a.top-nav-toggle{ href: '#', type: 'button', data: { toggle: "dropdown" } }
- = sprite_icon('hamburger', css_class: "dropdown-icon")
- = view_model[:activeTitle]
+ = sprite_icon('hamburger')
+ - if view_model[:menuTitle]
+ .gl-ml-3= view_model[:menuTitle]
.hidden
- view_model[:shortcuts].each do |shortcut|
diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml
index f3f79750643..56f333664df 100644
--- a/app/views/layouts/nav/sidebar/_admin.html.haml
+++ b/app/views/layouts/nav/sidebar/_admin.html.haml
@@ -175,7 +175,7 @@
%strong.fly-out-top-item-name
= _('Kubernetes')
- - if akismet_enabled?
+ - if anti_spam_service_enabled?
= nav_link(controller: :spam_logs) do
= link_to admin_spam_logs_path do
.nav-icon-container
diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml
index 1ec839ef642..1b6e78b7b3d 100644
--- a/app/views/layouts/profile.html.haml
+++ b/app/views/layouts/profile.html.haml
@@ -7,7 +7,7 @@
- enable_search_settings locals: { container_class: 'gl-my-5' }
- content_for :flash_message do
- = render "layouts/header/storage_enforcement_banner", context: current_user.namespace
+ = dispensable_render_if_exists "profiles/storage_enforcement_alert", context: current_user.namespace
= dispensable_render_if_exists "shared/namespace_storage_limit_alert", context: current_user.namespace
= render template: "layouts/application"
diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml
index 9503e874fd0..75d5e40011c 100644
--- a/app/views/layouts/project.html.haml
+++ b/app/views/layouts/project.html.haml
@@ -8,7 +8,7 @@
- @content_class = [@content_class, project_classes(@project)].compact.join(" ")
- content_for :flash_message do
- = render "layouts/header/storage_enforcement_banner", context: @project
+ = dispensable_render_if_exists "projects/storage_enforcement_alert", context: @project
= dispensable_render_if_exists "shared/namespace_storage_limit_alert", context: @project
- content_for :project_javascripts do
@@ -18,4 +18,6 @@
:plain
window.uploads_path = "#{project_uploads_path(project)}";
+= dispensable_render_if_exists "shared/web_hooks/web_hook_disabled_alert"
+
= render template: "layouts/application"
diff --git a/app/views/notify/_failed_builds.html.haml b/app/views/notify/_failed_builds.html.haml
index fc4a063f5a9..bb35bfffe46 100644
--- a/app/views/notify/_failed_builds.html.haml
+++ b/app/views/notify/_failed_builds.html.haml
@@ -18,6 +18,6 @@
%td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; color: #d22f57; font-weight: 500; font-size: 16px; vertical-align: middle; padding-right: 8px; line-height: 10px" }
%img{ alt: "✖", height: "10", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red.gif'), style: "display: block;", width: "10" }/
%td{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; color: #8c8c8c; font-weight: 500; font-size: 14px; vertical-align: middle;" }
- = build.stage
+ = build.stage_name
%td{ align: "right", style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; padding: 16px 0; color: #8c8c8c; font-weight: 500; font-size: 14px;" }
= render "notify/links/#{build.to_partial_path}", pipeline: pipeline, build: build
diff --git a/app/views/notify/_successful_pipeline.html.haml b/app/views/notify/_successful_pipeline.html.haml
index e77db14a9c5..88e0bbf6125 100644
--- a/app/views/notify/_successful_pipeline.html.haml
+++ b/app/views/notify/_successful_pipeline.html.haml
@@ -16,7 +16,8 @@
%table.table-info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
%tbody
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" }
+ = _('Project')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;" }
- namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
@@ -26,7 +27,8 @@
%a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
= @project.name
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
+ = _('Branch')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
@@ -37,7 +39,8 @@
%a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" }
= @pipeline.source_ref
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
+ = _('Commit')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:400;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
@@ -55,7 +58,8 @@
= @pipeline.git_commit_message.truncate(50)
- commit = @pipeline.commit
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit Author
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
+ = s_('Notify|Commit Author')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
@@ -71,7 +75,8 @@
= commit.author_name
- if commit.different_committer?
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Committed by
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
+ = s_('Notify|Committed by')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
diff --git a/app/views/notify/approved_merge_request_email.html.haml b/app/views/notify/approved_merge_request_email.html.haml
index 28da1182d49..0b20d4f3d3a 100644
--- a/app/views/notify/approved_merge_request_email.html.haml
+++ b/app/views/notify/approved_merge_request_email.html.haml
@@ -78,10 +78,11 @@
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;" }
%img{ alt: "✓", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-check-green-inverted.gif'), style: "display:block;", width: "13" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
- - if @merge_request.respond_to? :approvals_required
- %span Merge request was approved (#{@merge_request.approvals.count}/#{@merge_request.approvals_required})
- - else
- %span Merge request was approved
+ %span
+ - if @merge_request.respond_to? :approvals_required
+ = s_('Notify|Merge request was approved (%{approvals}/%{required_approvals})') % { approvals: @merge_request.approvals.count, required_approvals: @merge_request.approvals_required }
+ - else
+ = s_('Notify|Merge request was approved')
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
@@ -92,12 +93,7 @@
%tr{ style: 'width:100%;' }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;text-align:center;" }
%img{ src: image_url('mailers/approval/icon-merge-request-gray.gif'), style: "height:18px;width:18px;margin-bottom:-4px;", alt: "Merge request icon" }
- %span{ style: "font-weight: 600;color:#333333;" } Merge request
- %a{ href: merge_request_url(@merge_request), style: "font-weight: 600;color:#3777b0;text-decoration:none" }= @merge_request.to_reference
- %span was approved by
- %img.avatar{ height: "24", src: avatar_icon_for_user(@approved_by, 24, only_path: false), style: "border-radius:12px;margin:-7px 0 -7px 3px;", width: "24", alt: "Avatar" }/
- %a.muted{ href: user_url(@approved_by), style: "color:#333333;text-decoration:none;" }
- = @approved_by.name
+ = s_('Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{reviewer_highlight}was approved by%{highlight_end} %{reviewer_avatar} %{reviewer_link}').html_safe % merge_request_hash_param(@merge_request, @approved_by)
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
@@ -106,7 +102,7 @@
%table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
%tbody
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" }= _("Project")
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
- namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
@@ -116,7 +112,7 @@
%a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
= @project.name
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }= _("Branch")
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
@@ -127,7 +123,7 @@
%span.muted{ style: "color:#333333;text-decoration:none;" }
= @merge_request.source_branch
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Author
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }= _("Author")
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
diff --git a/app/views/notify/autodevops_disabled_email.text.erb b/app/views/notify/autodevops_disabled_email.text.erb
index c75857e96d7..da91ac67ff7 100644
--- a/app/views/notify/autodevops_disabled_email.text.erb
+++ b/app/views/notify/autodevops_disabled_email.text.erb
@@ -12,6 +12,6 @@ had <%= failed.size %> failed <%= 'build'.pluralize(failed.size) %>.
<% failed.each do |build| -%>
<%= render "notify/links/#{build.to_partial_path}", pipeline: @pipeline, build: build %>
- Stage: <%= build.stage %>
+ Stage: <%= build.stage_name %>
Name: <%= build.name %>
<% end -%>
diff --git a/app/views/notify/change_in_merge_request_draft_status_email.html.haml b/app/views/notify/change_in_merge_request_draft_status_email.html.haml
index 64ceb77e85c..21ea756cf06 100644
--- a/app/views/notify/change_in_merge_request_draft_status_email.html.haml
+++ b/app/views/notify/change_in_merge_request_draft_status_email.html.haml
@@ -1,2 +1,6 @@
-%p= html_escape(_('%{username} changed the draft status of merge request %{mr_link}')) % { username: link_to(@updated_by_user.name, user_url(@updated_by_user)),
- mr_link: merge_request_reference_link(@merge_request) }
+- if @merge_request.draft?
+ %p= html_escape(_('%{username} marked merge request %{mr_link} as draft')) % { username: link_to(@updated_by_user.name, user_url(@updated_by_user)),
+ mr_link: merge_request_reference_link(@merge_request) }
+- else
+ %p= html_escape(_('%{username} marked merge request %{mr_link} as ready')) % { username: link_to(@updated_by_user.name, user_url(@updated_by_user)),
+ mr_link: merge_request_reference_link(@merge_request) }
diff --git a/app/views/notify/change_in_merge_request_draft_status_email.text.erb b/app/views/notify/change_in_merge_request_draft_status_email.text.erb
index 4e2df2dff1d..8fe622f1e6b 100644
--- a/app/views/notify/change_in_merge_request_draft_status_email.text.erb
+++ b/app/views/notify/change_in_merge_request_draft_status_email.text.erb
@@ -1 +1,5 @@
-<%= "#{sanitize_name(@updated_by_user.name)} changed the draft status of merge request #{@merge_request.to_reference}" %>
+<% if @merge_request.draft? %>
+<%= _("#{sanitize_name(@updated_by_user.name)} marked merge request #{@merge_request.to_reference} as draft") %>
+<% else %>
+<%= _("#{sanitize_name(@updated_by_user.name)} marked merge request #{@merge_request.to_reference} as ready") %>
+<% end %>
diff --git a/app/views/notify/import_issues_csv_email.html.haml b/app/views/notify/import_issues_csv_email.html.haml
index f30d2b5f078..0008085025b 100644
--- a/app/views/notify/import_issues_csv_email.html.haml
+++ b/app/views/notify/import_issues_csv_email.html.haml
@@ -1,18 +1,18 @@
- text_style = 'font-size:16px; text-align:center; line-height:30px;'
%p{ style: text_style }
- Your CSV import for project
- %a{ href: project_url(@project), style: "color:#3777b0; text-decoration:none;" }
- = @project.full_name
- has been completed.
+ - project_link = link_to(@project.full_name, project_url(@project), style: "color:#3777b0; text-decoration:none;")
+ = s_('Notify|Your CSV import for project %{project_link} has been completed.').html_safe % { project_link: project_link }
%p{ style: text_style }
- #{pluralize(@results[:success], 'issue')} imported.
+ - issues = n_('%d issue', '%d issues', @results[:success]) % @results[:success]
+ = s_('Notify|%{issues} imported.') % { issues: issues }
- if @results[:error_lines].present?
%p{ style: text_style }
- Errors found on line #{'number'.pluralize(@results[:error_lines].size)}: #{@results[:error_lines].join(', ')}. Please check if these lines have an issue title.
+ = s_('Notify|Errors found on %{singular_or_plural_line}: %{error_lines}. Please check if these lines have an issue title.') % { singular_or_plural_line: n_('line', 'lines', @results[:error_lines].size),
+ error_lines: @results[:error_lines].join(', ') }
- if @results[:parse_error]
%p{ style: text_style }
- Error parsing CSV file. Please make sure it has the correct format: a delimited text file that uses a comma to separate values.
+ = s_('Notify|Error parsing CSV file. Please make sure it has the correct format: a delimited text file that uses a comma to separate values.')
diff --git a/app/views/notify/new_gpg_key_email.html.haml b/app/views/notify/new_gpg_key_email.html.haml
index b857705e01f..fca0dbd168a 100644
--- a/app/views/notify/new_gpg_key_email.html.haml
+++ b/app/views/notify/new_gpg_key_email.html.haml
@@ -1,10 +1,9 @@
%p
- Hi #{sanitize_name(@user.name)}!
+ = s_("Notify|Hi %{user}!") % { user: sanitize_name(@user.name) }
%p
- A new GPG key was added to your account:
+ = s_("Notify|A new GPG key was added to your account:")
%p
- Fingerprint:
- %code= @gpg_key.fingerprint
+ = s_("Notify|Fingerprint: %{fingerprint}").html_safe % { fingerprint: content_tag(:code, @gpg_key.fingerprint) }
%p
- If this key was added in error, you can remove it under
- = link_to "GPG Keys", profile_gpg_keys_url
+ - removal_link = link_to _("GPG Keys"), profile_gpg_keys_url
+ = s_("Notify|If this key was added in error, you can remove it under %{removal_link}").html_safe % { removal_link: removal_link }
diff --git a/app/views/notify/new_mention_in_issue_email.html.haml b/app/views/notify/new_mention_in_issue_email.html.haml
index 6b45ac265f7..3b2e36d118b 100644
--- a/app/views/notify/new_mention_in_issue_email.html.haml
+++ b/app/views/notify/new_mention_in_issue_email.html.haml
@@ -1,4 +1,4 @@
%p
- You have been mentioned in an issue.
+ = s_('Notify|You have been mentioned in an issue.')
= render template: 'notify/new_issue_email'
diff --git a/app/views/notify/new_mention_in_merge_request_email.html.haml b/app/views/notify/new_mention_in_merge_request_email.html.haml
index a28d944529f..e4588716d5c 100644
--- a/app/views/notify/new_mention_in_merge_request_email.html.haml
+++ b/app/views/notify/new_mention_in_merge_request_email.html.haml
@@ -1,4 +1,4 @@
%p
- You have been mentioned in merge request #{merge_request_reference_link(@merge_request)}
+ = (s_("Notify|You have been mentioned in merge request %{mr_link}") % { mr_link: merge_request_reference_link(@merge_request) }).html_safe
= render template: 'notify/new_merge_request_email'
diff --git a/app/views/notify/new_ssh_key_email.html.haml b/app/views/notify/new_ssh_key_email.html.haml
index d031842be95..38c6dfae411 100644
--- a/app/views/notify/new_ssh_key_email.html.haml
+++ b/app/views/notify/new_ssh_key_email.html.haml
@@ -1,10 +1,4 @@
-%p
- Hi #{sanitize_name(@user.name)}!
-%p
- A new public key was added to your account:
-%p
- title:
- %code= @key.title
-%p
- If this key was added in error, you can remove it under
- = link_to "SSH Keys", profile_keys_url
+- name = sanitize_name(@user.name)
+- key_title = html_escape(@key.title)
+= (s_("Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}") % { paragraph_start: '<p>'.html_safe,
+ paragraph_end: '</p>'.html_safe, name: name, key_title: content_tag(:code, key_title), removal_link: link_to(_("SSH Keys"), profile_keys_url) }).html_safe
diff --git a/app/views/notify/new_user_email.html.haml b/app/views/notify/new_user_email.html.haml
index ec135ae994f..11660126dc2 100644
--- a/app/views/notify/new_user_email.html.haml
+++ b/app/views/notify/new_user_email.html.haml
@@ -1,17 +1,19 @@
%p
- Hi #{sanitize_name(@user['name'])}!
+ = s_('Notify|Hi %{username}!') % {username: sanitize_name(@user['name'])}
%p
- if Gitlab::CurrentSettings.allow_signup?
- Your account has been created successfully.
+ = s_('Notify|Your account has been created successfully.')
- else
- The Administrator created an account for you. Now you are a member of the company GitLab application.
+ = s_('Notify|The Administrator created an account for you. Now you are a member of the company GitLab application.')
%p
- login..........................................
+ = s_('Notify|login..........................................')
%code= @user['email']
- if @user.created_by_id
%p
- = link_to "Click here to set your password", edit_password_url(@user, reset_password_token: @token)
+ = link_to s_('Notify|Click here to set your password'), edit_password_url(@user, reset_password_token: @token)
%p
- This link is valid for #{password_reset_token_valid_time}.
- After it expires, you can #{link_to("request a new one", new_user_password_url(user_email: @user.email))}.
+ = s_('Notify|This link is valid for %{password_reset_token_valid_time}.') % {password_reset_token_valid_time: password_reset_token_valid_time}
+ - a_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % {url: new_user_password_url(user_email: @user.email)}
+ - a_end = '</a>'.html_safe
+ = html_escape(s_('Notify|After it expires, you can %{a_start} request a new one %{a_end}.')) % {a_start: a_start, a_end: a_end}
diff --git a/app/views/notify/pipeline_failed_email.text.erb b/app/views/notify/pipeline_failed_email.text.erb
index 6ab74bcfb1a..c82b7a8dd2a 100644
--- a/app/views/notify/pipeline_failed_email.text.erb
+++ b/app/views/notify/pipeline_failed_email.text.erb
@@ -32,6 +32,6 @@ had <%= failed.size %> failed <%= 'job'.pluralize(failed.size) %>.
<% failed.each do |build| -%>
<%= render "notify/links/#{build.to_partial_path}", pipeline: @pipeline, build: build %>
-Stage: <%= build.stage %>
+Stage: <%= build.stage_name %>
Name: <%= build.name %>
<% end -%>
diff --git a/app/views/notify/pipeline_fixed_email.html.haml b/app/views/notify/pipeline_fixed_email.html.haml
index f2dbb3b20b7..33b83b104b1 100644
--- a/app/views/notify/pipeline_fixed_email.html.haml
+++ b/app/views/notify/pipeline_fixed_email.html.haml
@@ -1 +1 @@
-= render 'notify/successful_pipeline', title: "Pipeline has been fixed and ##{@pipeline.id} has passed!"
+= render 'notify/successful_pipeline', title: s_('Notify|Pipeline has been fixed and #%{pipeline_id} has passed!') % {pipeline_id: @pipeline.id}
diff --git a/app/views/notify/push_to_merge_request_email.html.haml b/app/views/notify/push_to_merge_request_email.html.haml
index 5197a1bdd08..16612cd43c5 100644
--- a/app/views/notify/push_to_merge_request_email.html.haml
+++ b/app/views/notify/push_to_merge_request_email.html.haml
@@ -1,7 +1,7 @@
%h3
- = sanitize_name(@updated_by_user.name)
- pushed new commits to merge request
- = merge_request_reference_link(@merge_request)
+ - updated_by_user_name = sanitize_name(@updated_by_user.name)
+ - mr_link = sanitize(merge_request_reference_link(@merge_request))
+ = s_('Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}').html_safe % {updated_by_user_name: updated_by_user_name, mr_link: mr_link}
- if @total_existing_commits_count > 0
%ul
@@ -13,8 +13,8 @@
= link_to(project_compare_url(@merge_request.target_project, from: @existing_commits.first[:short_id], to: @existing_commits.last[:short_id])) do
#{@existing_commits.first[:short_id]}...#{@existing_commits.last[:short_id]}
= precede '&nbsp;- ' do
- - commits_text = "#{@total_existing_commits_count} commit".pluralize(@total_existing_commits_count)
- #{commits_text} from branch `#{@merge_request.target_branch}`
+ - commits_text = n_("%d commit", "%d commits", @total_existing_commits_count) % @total_existing_commits_count
+ = s_('Notify|%{commits_text} from branch `%{target_branch}`') % {commits_text: commits_text, target_branch: @merge_request.target_branch}
- if @total_new_commits_count > 0
%ul
@@ -24,4 +24,5 @@
= precede ' - ' do
#{commit[:title]}
- if @total_stripped_new_commits_count > 0
- %li And #{@total_stripped_new_commits_count} more
+ %li
+ = s_('Notify|And %{total_stripped_new_commits_count} more') % {total_stripped_new_commits_count: @total_stripped_new_commits_count}
diff --git a/app/views/notify/remote_mirror_update_failed_email.html.haml b/app/views/notify/remote_mirror_update_failed_email.html.haml
index 4fb0a4c5a8a..db95398d2d6 100644
--- a/app/views/notify/remote_mirror_update_failed_email.html.haml
+++ b/app/views/notify/remote_mirror_update_failed_email.html.haml
@@ -6,7 +6,7 @@
%td{ style: "vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;line-height:1;" }
%img{ alt: "✖", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red-inverted.gif'), style: "display:block;", width: "13" }/
%td{ style: "vertical-align:middle;color:#ffffff;text-align:center;" }
- A remote mirror update has failed.
+ = s_('Notify|A remote mirror update has failed.')
%tr.spacer{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif;" }
%td{ style: "height:18px;font-size:18px;line-height:18px;" }
&nbsp;
@@ -15,7 +15,8 @@
%table.table-info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
%tbody{ style: "font-size:15px;line-height:1.4;color:#8c8c8c;" }
%tr
- %td{ style: "font-weight:300;padding:14px 0;margin:0;" } Project
+ %td{ style: "font-weight:300;padding:14px 0;margin:0;" }
+ = _('Project')
%td{ style: "font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;" }
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
%a.muted{ href: namespace_url, style: "color:#333333;text-decoration:none;" }
@@ -24,17 +25,20 @@
%a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
= @project.name
%tr
- %td{ style: "font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Remote mirror
+ %td{ style: "font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
+ = s_('Notify|Remote mirror')
%td{ style: "font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
= @remote_mirror.safe_url
%tr
- %td{ style: "font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Last update at
- %td{ style: "font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
- = @remote_mirror.last_update_at
+ - update_at_start = '<td style="font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;">'.html_safe
+ - update_at_mid = '</td><td style="font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;">'.html_safe
+ - update_at_end = '</td>'.html_safe
+ = html_escape(s_('Notify|%{update_at_start} Last update at %{update_at_mid} %{last_update_at} %{update_at_end}')) % {update_at_start: update_at_start, update_at_mid: update_at_mid, last_update_at: @remote_mirror.last_update_at, update_at_end: update_at_end}
+
%tr.table-warning{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif;" }
%td{ style: "border: 1px solid #ededed; border-bottom: 0; border-radius: 4px 4px 0 0; overflow: hidden; background-color: #fdf4f6; color: #d22852; font-size: 14px; line-height: 1.4; text-align: center; padding: 8px 16px;" }
- Logs may contain sensitive data. Please consider before forwarding this email.
+ = s_('Notify|Logs may contain sensitive data. Please consider before forwarding this email.')
%tr.section{ style: "font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif;" }
%td{ style: "padding: 0 16px; border: 1px solid #ededed; border-radius: 4px; overflow: hidden; border-top: 0; border-radius: 0 0 4px 4px;" }
%table.builds{ border: "0", cellpadding: "0", cellspacing: "0", style: "width: 100%; border-collapse: collapse;" }
diff --git a/app/views/notify/removed_milestone_issue_email.html.haml b/app/views/notify/removed_milestone_issue_email.html.haml
index 7e9205b6491..f411ea23832 100644
--- a/app/views/notify/removed_milestone_issue_email.html.haml
+++ b/app/views/notify/removed_milestone_issue_email.html.haml
@@ -1,2 +1,2 @@
%p
- Milestone removed
+ = s_('Notify|Milestone removed')
diff --git a/app/views/notify/removed_milestone_merge_request_email.html.haml b/app/views/notify/removed_milestone_merge_request_email.html.haml
index 7e9205b6491..f411ea23832 100644
--- a/app/views/notify/removed_milestone_merge_request_email.html.haml
+++ b/app/views/notify/removed_milestone_merge_request_email.html.haml
@@ -1,2 +1,2 @@
%p
- Milestone removed
+ = s_('Notify|Milestone removed')
diff --git a/app/views/notify/repository_push_email.html.haml b/app/views/notify/repository_push_email.html.haml
index 93806e6de8e..ee219914513 100644
--- a/app/views/notify/repository_push_email.html.haml
+++ b/app/views/notify/repository_push_email.html.haml
@@ -2,29 +2,30 @@
= stylesheet_link_tag 'mailers/highlighted_diff_email'
%h3
- #{@message.author_name} #{@message.action_name} #{@message.ref_type} #{@message.ref_name}
- at #{link_to(@message.project_name_with_namespace, project_url(@message.project))}
+ = s_('Notify|%{author_name} %{action_name} %{ref_type} %{ref_name} at %{project_link}').html_safe % {author_name: @message.author_name, action_name: @message.action_name, ref_type: @message.ref_type, ref_name: @message.ref_name, project_link: link_to(@message.project_name_with_namespace, strip_tags(project_url(@message.project)))}
- if @message.compare
- if @message.reverse_compare?
%p
- %strong WARNING:
- The push did not contain any new commits, but force pushed to delete the commits and changes below.
+ %strong
+ = _('WARNING:')
+ = s_('Notify|The push did not contain any new commits, but force pushed to delete the commits and changes below.')
%h4
- = @message.reverse_compare? ? "Deleted commits:" : "Commits:"
+ = @message.reverse_compare? ? _("Deleted commits:") : _("Commits:")
%ul
- @message.commits.each do |commit|
%li
%strong= link_to(commit.short_id, project_commit_url(@message.project, commit))
%div
- %span by #{commit.author_name}
- %i at #{commit.committed_date.to_s(:iso8601)}
+ = html_escape(s_('Notify|%{committed_by_start} by %{author_name} %{committed_by_end} %{committed_at_start} at %{committed_date} %{committed_at_end}')) % {committed_by_start: '<span>'.html_safe, author_name: commit.author_name, committed_by_end: '</span>'.html_safe, committed_at_start: '<i>'.html_safe, committed_date: commit.committed_date.to_s(:iso8601), committed_at_end: '</i>'.html_safe}
%pre.commit-message
= commit.safe_message
- %h4 #{pluralize @message.diffs_count, "changed file"}:
+ %h4
+ - changed_files = n_('%d changed file', '%d changed files', @message.diffs_count) % @message.diffs_count
+ = s_('Notify|%{changed_files}:') % {changed_files: changed_files}
%ul
- @message.diffs.each do |diff_file|
@@ -47,9 +48,11 @@
- unless @message.disable_diffs?
- if @message.compare_timeout
- %h5 The diff was not included because it is too large.
+ %h5
+ = s_('Notify|The diff was not included because it is too large.')
- else
- %h4 Changes:
+ %h4
+ = _('Changes:')
- @message.diffs.each do |diff_file|
- file_hash = hexdigest(diff_file.file_path)
%li{ id: file_hash }
@@ -57,7 +60,7 @@
- if diff_file.deleted_file?
%strong<
= diff_file.old_path
- deleted
+ = s_('deleted')
- elsif diff_file.renamed_file?
%strong<
= diff_file.old_path
@@ -68,7 +71,7 @@
%strong<
= diff_file.new_path
- if diff_file.too_large?
- The diff for this file was not included because it is too large.
+ = s_('Notify|The diff for this file was not included because it is too large.')
- else
%hr
- blob = diff_file.blob
@@ -76,5 +79,5 @@
%table.code.white
= render partial: "projects/diffs/email_line", collection: diff_file.highlighted_diff_lines, as: :line, locals: { diff_file: diff_file }
- else
- No preview for this file type
+ = s_('Notify|No preview for this file type')
%br
diff --git a/app/views/notify/resolved_all_discussions_email.html.haml b/app/views/notify/resolved_all_discussions_email.html.haml
index 209415e0aee..78dc21caf18 100644
--- a/app/views/notify/resolved_all_discussions_email.html.haml
+++ b/app/views/notify/resolved_all_discussions_email.html.haml
@@ -1,3 +1,2 @@
%p
- All discussions on merge request #{merge_request_reference_link(@merge_request)}
- were resolved by #{sanitize_name(@resolved_by.name)}
+ = s_('Notify|All discussions on merge request %{mr_link} were resolved by %{name}').html_safe % { mr_link: merge_request_reference_link(@merge_request), name: sanitize_name(@resolved_by.name) }
diff --git a/app/views/notify/send_admin_notification.html.haml b/app/views/notify/send_admin_notification.html.haml
index f7f1528f332..20c44df360c 100644
--- a/app/views/notify/send_admin_notification.html.haml
+++ b/app/views/notify/send_admin_notification.html.haml
@@ -3,5 +3,5 @@
\----
%p
- Don't want to receive updates from GitLab administrators?
- = link_to 'Unsubscribe', @unsubscribe_url
+ = s_("Notify|Don't want to receive updates from GitLab administrators?")
+ = link_to _('Unsubscribe'), @unsubscribe_url
diff --git a/app/views/notify/unapproved_merge_request_email.html.haml b/app/views/notify/unapproved_merge_request_email.html.haml
index 0b8fbe14228..94e2d0377aa 100644
--- a/app/views/notify/unapproved_merge_request_email.html.haml
+++ b/app/views/notify/unapproved_merge_request_email.html.haml
@@ -79,9 +79,11 @@
%img{ alt: "✗", height: "13", src: image_url('mailers/approval/icon-x-orange-inverted.gif'), style: "display:block;", width: "13" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
- if @merge_request.respond_to? :approvals_required
- %span Merge request was unapproved (#{@merge_request.approvals.count}/#{@merge_request.approvals_required})
+ %span
+ = s_('Notify|Merge request was unapproved (%{approvals_count}/%{approvals_required})') % {approvals_count: @merge_request.approvals.count, approvals_required: @merge_request.approvals_required}
- else
- %span Merge request was unapproved
+ %span
+ = s_('Notify|Merge request was unapproved')
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
@@ -92,12 +94,7 @@
%tr{ style: 'width:100%;' }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;text-align:center;" }
%img{ src: image_url('mailers/approval/icon-merge-request-gray.gif'), style: "height:18px;width:18px;margin-bottom:-4px;", alt: "Merge request icon" }
- %span{ style: "font-weight: 600;color:#333333;" } Merge request
- %a{ href: merge_request_url(@merge_request), style: "font-weight: 600;color:#3777b0;text-decoration:none" }= @merge_request.to_reference
- %span was unapproved by
- %img.avatar{ height: "24", src: avatar_icon_for_user(@unapproved_by, 24), style: "border-radius:12px;margin:-7px 0 -7px 3px;", width: "24", alt: "Avatar" }/
- %a.muted{ href: user_url(@unapproved_by), style: "color:#333333;text-decoration:none;" }
- = @unapproved_by.name
+ = s_('Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{reviewer_highlight}was unapproved by%{highlight_end} %{reviewer_avatar} %{reviewer_link}').html_safe % merge_request_hash_param(@merge_request, @unapproved_by)
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
@@ -106,7 +103,8 @@
%table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
%tbody
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" }
+ = _('Project')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
- namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
@@ -116,7 +114,8 @@
%a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
= @project.name
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
+ = _('Branch')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
@@ -127,7 +126,8 @@
%span.muted{ style: "color:#333333;text-decoration:none;" }
= @merge_request.source_branch
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Author
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
+ = _('Author')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
diff --git a/app/views/profiles/_email_settings.html.haml b/app/views/profiles/_email_settings.html.haml
index 0ca9acba2de..0fde7fd4f19 100644
--- a/app/views/profiles/_email_settings.html.haml
+++ b/app/views/profiles/_email_settings.html.haml
@@ -25,8 +25,8 @@
= s_("Profiles|This email will be displayed on your public profile.")
.form-group.gl-form-group
- - commit_email_link_url = help_page_path('user/profile/index', anchor: 'change-the-email-displayed-on-your-commits', target: '_blank')
- - commit_email_link_start = '<a href="%{url}">'.html_safe % { url: commit_email_link_url }
+ - commit_email_link_url = help_page_path('user/profile/index', anchor: 'change-the-email-displayed-on-your-commits')
+ - commit_email_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: commit_email_link_url }
- commit_email_docs_link = s_('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}').html_safe % { commit_email_link_start: commit_email_link_start, commit_email_link_end: '</a>'.html_safe }
= form.label :commit_email, s_('Profiles|Commit email')
.gl-md-form-input-lg
diff --git a/app/views/profiles/active_sessions/index.html.haml b/app/views/profiles/active_sessions/index.html.haml
index f444f236cfc..be835233528 100644
--- a/app/views/profiles/active_sessions/index.html.haml
+++ b/app/views/profiles/active_sessions/index.html.haml
@@ -10,6 +10,7 @@
.col-lg-8
.gl-mb-3
- .card.border-0
- %ul.list-group.list-group-flush
- = render partial: 'profiles/active_sessions/active_session', collection: @sessions
+ = render Pajamas::CardComponent.new(card_options: { class: 'gl-border-0' }, body_options: { class: 'gl-p-0' }) do |c|
+ - c.body do
+ %ul.list-group.list-group-flush
+ = render partial: 'profiles/active_sessions/active_session', collection: @sessions
diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml
index ef9e7512b57..1b8f0328a04 100644
--- a/app/views/profiles/emails/index.html.haml
+++ b/app/views/profiles/emails/index.html.haml
@@ -38,29 +38,29 @@
= render partial: 'shared/email_with_badge', locals: { email: @primary_email, verified: current_user.confirmed? }
%ul
%li= s_('Profiles|Primary email')
- - if @primary_email === current_user.commit_email_or_default
+ - if @primary_email == current_user.commit_email_or_default
%li= s_('Profiles|Commit email')
- - if @primary_email === current_user.public_email
+ - if @primary_email == current_user.public_email
%li= s_('Profiles|Public email')
- - if @primary_email === current_user.notification_email_or_default
+ - if @primary_email == current_user.notification_email_or_default
%li= s_('Profiles|Default notification email')
- @emails.reject(&:user_primary_email?).each do |email|
%li{ data: { qa_selector: 'email_row_content' } }
- .gl-display-flex.gl-justify-content-space-between{ style: 'flex-flow: wrap-reverse; row-gap: 0.5rem' }
+ .gl-display-flex.gl-justify-content-space-between.gl-flex-wrap.gl-gap-3
%div
= render partial: 'shared/email_with_badge', locals: { email: email.email, verified: email.confirmed? }
- .gl-ml-n3
+ %ul
+ - if email.email == current_user.commit_email_or_default
+ %li= s_('Profiles|Commit email')
+ - if email.email == current_user.public_email
+ %li= s_('Profiles|Public email')
+ - if email.email == current_user.notification_email_or_default
+ %li= s_('Profiles|Notification email')
+ .gl-display-flex.gl-justify-content-end.gl-align-items-flex-end.gl-flex-grow-1.gl-flex-wrap-wrap-reverse.gl-gap-3
- unless email.confirmed?
- confirm_title = "#{email.confirmation_sent_at ? _('Resend confirmation email') : _('Send confirmation email')}"
- = link_to confirm_title, resend_confirmation_instructions_profile_email_path(email), method: :put, class: 'gl-button btn btn-sm btn-default gl-ml-3'
+ = link_to confirm_title, resend_confirmation_instructions_profile_email_path(email), method: :put, class: 'gl-button btn btn-sm btn-default'
- = link_to profile_email_path(email), data: { confirm: _('Are you sure?'), qa_selector: 'delete_email_link'}, method: :delete, class: 'gl-button btn btn-sm btn-danger gl-ml-3' do
+ = link_to profile_email_path(email), data: { confirm: _('Are you sure?'), qa_selector: 'delete_email_link'}, method: :delete, class: 'gl-button btn btn-sm btn-danger' do
%span.sr-only= _('Remove')
= sprite_icon('remove')
- %ul
- - if email.email === current_user.commit_email_or_default
- %li= s_('Profiles|Commit email')
- - if email.email === current_user.public_email
- %li= s_('Profiles|Public email')
- - if email.email === current_user.notification_email_or_default
- %li= s_('Profiles|Notification email')
diff --git a/app/views/profiles/gpg_keys/_form.html.haml b/app/views/profiles/gpg_keys/_form.html.haml
index b3784faed28..9804a3b7735 100644
--- a/app/views/profiles/gpg_keys/_form.html.haml
+++ b/app/views/profiles/gpg_keys/_form.html.haml
@@ -1,6 +1,6 @@
%div
= form_for [:profile, @gpg_key], html: { class: 'js-requires-input' } do |f|
- = form_errors(@gpg_key, pajamas_alert: true)
+ = form_errors(@gpg_key)
.form-group
= f.label :key, s_('Profiles|Key'), class: 'label-bold'
diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml
index a749fbd1eec..6f7eb21b7e0 100644
--- a/app/views/profiles/keys/_form.html.haml
+++ b/app/views/profiles/keys/_form.html.haml
@@ -1,7 +1,7 @@
- max_date = ::Gitlab::CurrentSettings.max_ssh_key_lifetime_from_now.to_date if ssh_key_expiration_policy_enabled?
%div
= form_for [:profile, @key], html: { class: 'js-requires-input' } do |f|
- = form_errors(@key, pajamas_alert: true)
+ = form_errors(@key)
.form-group
= f.label :key, s_('Profiles|Key'), class: 'label-bold'
@@ -13,10 +13,12 @@
= f.text_field :title, class: "form-control gl-form-input input-lg qa-key-title-field", required: true, placeholder: s_('Profiles|Example: MacBook key')
%p.form-text.text-muted= s_('Profiles|Key titles are publicly visible.')
+ .form-row
.col.form-group
- = f.label :expires_at, s_('Profiles|Expiration date'), class: 'label-bold'
- = f.date_field :expires_at, class: "form-control input-lg", min: Date.tomorrow, max: max_date, data: { qa_selector: 'key_expiry_date_field' }
- %p.form-text.text-muted{ data: { qa_selector: 'key_expiry_date_field_description' } }= ssh_key_expires_field_description
+ .js-access-tokens-expires-at{ data: {min_date: Date.tomorrow, max_date: max_date, default_date_offset: 365, description: ssh_key_expires_field_description } }
+ = f.label :expires_at, s_('Profiles|Expiration date'), class: 'label-bold'
+ = f.text_field :expires_at, class: "gl-datepicker-input form-control gl-form-input", placeholder: 'YYYY-MM-DD', min: Date.tomorrow, max: max_date, data: { js_name: 'expiresAt' }
+ %p.form-text.text-muted= ssh_key_expires_field_description
.js-add-ssh-key-validation-warning.hide
.bs-callout.bs-callout-warning{ role: 'alert', aria_live: 'assertive' }
diff --git a/app/views/profiles/keys/_key.html.haml b/app/views/profiles/keys/_key.html.haml
index 178ed01c766..de4a19bdad7 100644
--- a/app/views/profiles/keys/_key.html.haml
+++ b/app/views/profiles/keys/_key.html.haml
@@ -16,7 +16,7 @@
%span.gl-text-truncate.gl-sm-ml-3
= key.fingerprint
- .gl-mt-3= s_('Profiles|Created%{time_ago}'.html_safe) % { time_ago: time_ago_with_tooltip(key.created_at, html_class: 'gl-ml-2')}
+ .gl-mt-3= html_escape(s_('Profiles|Created%{time_ago}')) % { time_ago: time_ago_with_tooltip(key.created_at, html_class: 'gl-ml-2').html_safe}
.key-list-item-dates
%span.last-used-at.gl-mr-3
diff --git a/app/views/profiles/keys/_key_details.html.haml b/app/views/profiles/keys/_key_details.html.haml
index 8f7ccadd108..8016d989ff1 100644
--- a/app/views/profiles/keys/_key_details.html.haml
+++ b/app/views/profiles/keys/_key_details.html.haml
@@ -19,7 +19,7 @@
%strong= @key.last_used_at.try(:to_s, :medium) || _('Never')
.col-md-8
- = form_errors(@key, type: 'key', pajamas_alert: true) unless @key.valid?
+ = form_errors(@key, type: 'key') unless @key.valid?
%pre.well-pre
= @key.key
.card
diff --git a/app/views/profiles/notifications/_email_settings.html.haml b/app/views/profiles/notifications/_email_settings.html.haml
index b4db99a8bd4..c4de33dcd9e 100644
--- a/app/views/profiles/notifications/_email_settings.html.haml
+++ b/app/views/profiles/notifications/_email_settings.html.haml
@@ -1,6 +1,6 @@
- form = local_assigns.fetch(:form)
.form-group
- = form.label :notification_email, class: "label-bold"
+ = form.label :notification_email, _('Notification Email'), class: "label-bold"
= form.select :notification_email, @user.public_verified_emails, { include_blank: _('Use primary email (%{email})') % { email: @user.email }, selected: @user.notification_email }, class: "select2", disabled: local_assigns.fetch(:email_change_disabled, nil)
.help-block
= local_assigns.fetch(:help_text, nil)
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index 26c9b2f0ee1..0f4b130a774 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -25,7 +25,7 @@
= gitlab_ui_form_for @user, url: profile_notifications_path, method: :put, html: { class: 'update-notifications gl-mt-3' } do |f|
= render_if_exists 'profiles/notifications/email_settings', form: f
- = label_tag :global_notification_level, "Global notification level", class: "label-bold"
+ = label_tag :global_notification_level, _('Global notification level'), class: "label-bold"
%br
.clearfix
.form-group.float-left.global-notification-setting
diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml
index 46ae602359f..257255eb4d7 100644
--- a/app/views/profiles/passwords/edit.html.haml
+++ b/app/views/profiles/passwords/edit.html.haml
@@ -15,7 +15,7 @@
- else
= _('Change your password or recover your current one')
= form_for @user, url: profile_password_path, method: :put, html: {class: "update-password"} do |f|
- = form_errors(@user, pajamas_alert: true)
+ = form_errors(@user)
- unless @user.password_automatically_set?
.form-group
diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml
index 5bcc92dcdfd..6f260eb4cc0 100644
--- a/app/views/profiles/passwords/new.html.haml
+++ b/app/views/profiles/passwords/new.html.haml
@@ -9,7 +9,7 @@
%br
= _('After a successful password update you will be redirected to login screen.')
- = form_errors(@user, pajamas_alert: true)
+ = form_errors(@user)
- unless @user.password_automatically_set?
.form-group.row
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index f8737a4e54a..e16108c5c22 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -1,4 +1,5 @@
- page_title _('Preferences')
+- add_page_specific_style 'page_bundles/profiles/preferences'
- @content_class = "limit-container-width" unless fluid_layout
- user_theme_id = Gitlab::Themes.for_user(@user).id
- user_color_schema_id = Gitlab::ColorSchemes.for_user(@user).id
@@ -77,10 +78,10 @@
= s_('Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout.').html_safe % { percentage: '100%' }
.form-group
= f.label :dashboard, class: 'label-bold' do
- = s_('Preferences|Homepage content')
+ = s_('Preferences|Dashboard')
= f.select :dashboard, dashboard_choices, {}, class: 'select2'
.form-text.text-muted
- = s_('Preferences|Choose what content you want to see on your homepage.')
+ = s_('Preferences|Choose what content you want to see by default on your dashboard.')
= render_if_exists 'profiles/preferences/group_overview_selector', f: f # EE-specific
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index a64968cdcbb..f38d6021b18 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -2,8 +2,6 @@
- page_title s_("Profiles|Edit Profile")
- @content_class = "limit-container-width" unless fluid_layout
- gravatar_link = link_to Gitlab.config.gravatar.host, 'https://' + Gitlab.config.gravatar.host
-- availability = availability_values
-- custom_emoji = @user.status&.customized?
= gitlab_ui_form_for @user, url: profile_path, method: :put, html: { multipart: true, class: 'edit-user js-edit-user gl-mt-3 js-quick-submit gl-show-field-errors js-password-prompt-form', remote: true }, authenticity_token: true do |f|
.row.js-search-settings-section
@@ -43,39 +41,12 @@
%h4.gl-mt-0= s_("Profiles|Current status")
%p= s_("Profiles|This emoji and message will appear on your profile and throughout the interface.")
.col-lg-8
- = f.fields_for :status, @user.status do |status_form|
- - emoji_button = render Pajamas::ButtonComponent.new(button_options: { title: s_("Profiles|Add status emoji"),
- class: 'js-toggle-emoji-menu emoji-menu-toggle-button has-tooltip' } ) do
- - if custom_emoji
- = emoji_icon(@user.status.emoji, class: 'gl-mr-0!')
- %span#js-no-emoji-placeholder.no-emoji-placeholder{ class: ('hidden' if custom_emoji) }
- = sprite_icon('slight-smile', css_class: 'award-control-icon-neutral')
- = sprite_icon('smiley', css_class: 'award-control-icon-positive')
- = sprite_icon('smile', css_class: 'award-control-icon-super-positive')
- - reset_message_button = render Pajamas::ButtonComponent.new(icon: 'close',
- button_options: { id: 'js-clear-user-status-button',
- class: 'has-tooltip',
- title: s_("Profiles|Clear status") } )
-
- = status_form.hidden_field :emoji, id: 'js-status-emoji-field'
- .form-group.gl-form-group
- = status_form.label :message, s_("Profiles|Your status")
- .input-group{ role: 'group' }
- .input-group-prepend
- = emoji_button
- = status_form.text_field :message,
- id: 'js-status-message-field',
- class: 'form-control gl-form-input input-lg',
- placeholder: s_("Profiles|What's your status?")
- .input-group-append
- = reset_message_button
- .form-group.gl-form-group
- = status_form.gitlab_ui_checkbox_component :availability,
- s_("Profiles|Busy"),
- help_text: s_('Profiles|An indicator appears next to your name and avatar.'),
- checkbox_options: { data: { testid: "user-availability-checkbox" } },
- checked_value: availability["busy"],
- unchecked_value: availability["not_set"]
+ #js-user-profile-set-status-form
+ = f.fields_for :status, @user.status do |status_form|
+ = status_form.hidden_field :emoji, data: { js_name: 'emoji' }
+ = status_form.hidden_field :message, data: { js_name: 'message' }
+ = status_form.hidden_field :availability, data: { js_name: 'availability' }
+ = status_form.hidden_field :clear_status_after, data: { js_name: 'clearStatusAfter' }
.col-lg-12
%hr
.row.user-time-preferences.js-search-settings-section
diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml
index c1eaa84e99d..855c73fd323 100644
--- a/app/views/profiles/two_factor_auths/show.html.haml
+++ b/app/views/profiles/two_factor_auths/show.html.haml
@@ -81,7 +81,7 @@
.col-lg-8
- registration = webauthn_enabled ? @webauthn_registration : @u2f_registration
- if registration.errors.present?
- = form_errors(registration, pajamas_alert: true)
+ = form_errors(registration)
- if webauthn_enabled
= render "authentication/register", target_path: create_webauthn_profile_two_factor_auth_path
- else
diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml
index 402affc7b0e..118f6fb1296 100644
--- a/app/views/projects/_activity.html.haml
+++ b/app/views/projects/_activity.html.haml
@@ -4,7 +4,7 @@
= render 'shared/event_filter'
.controls.gl-display-flex
= link_to project_path(@project, rss_url_options), title: s_("ProjectActivityRSS|Subscribe"), class: 'btn gl-button btn-default btn-icon d-none d-sm-inline-flex has-tooltip' do
- = sprite_icon('rss', css_class: 'qa-rss-icon gl-icon')
+ = sprite_icon('rss', css_class: 'gl-icon')
- if is_project_overview && can?(current_user, :download_code, @project)
.project-clone-holder.d-none.d-md-inline-flex.gl-ml-2
= render "projects/buttons/clone", dropdown_class: 'dropdown-menu-right'
diff --git a/app/views/projects/_commit_button.html.haml b/app/views/projects/_commit_button.html.haml
index 952c6daf415..9962e03995b 100644
--- a/app/views/projects/_commit_button.html.haml
+++ b/app/views/projects/_commit_button.html.haml
@@ -1,5 +1,8 @@
.form-actions.gl-display-flex
- = render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, button_options: { id: 'commit-changes', class: 'js-commit-button', data: { qa_selector: 'commit_button' } }) do
+ - submit_button_options = { type: :submit, variant: :confirm, button_options: { id: 'commit-changes', class: 'js-commit-button', data: { qa_selector: 'commit_button' } } }
+ = render Pajamas::ButtonComponent.new(**submit_button_options) do
+ = _('Commit changes')
+ = render Pajamas::ButtonComponent.new(loading: true, disabled: true, **submit_button_options.merge({ button_options: { class: 'js-commit-button-loading gl-display-none' } })) do
= _('Commit changes')
= render Pajamas::ButtonComponent.new(href: cancel_path, button_options: { class: 'gl-ml-3', id: 'cancel-changes', aria: { label: _('Discard changes') }, data: { confirm: leave_edit_message, confirm_btn_variant: "danger" } }) do
diff --git a/app/views/projects/_errors.html.haml b/app/views/projects/_errors.html.haml
index 5982aaf3622..2dba22d3be6 100644
--- a/app/views/projects/_errors.html.haml
+++ b/app/views/projects/_errors.html.haml
@@ -1 +1 @@
-= form_errors(@project, pajamas_alert: true)
+= form_errors(@project)
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index c220aa66c81..7ff58d12b9c 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -10,12 +10,12 @@
= 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.gl-line-height-24.gl-font-weight-bold{ data: { qa_selector: 'project_name_content' }, itemprop: 'name' }
+ %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.text-secondary.gl-ml-2.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@project) }
+ %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
- .home-panel-metadata.text-secondary.gl-font-base.gl-font-weight-normal.gl-line-height-normal{ data: { qa_selector: 'project_id_content' }, itemprop: 'identifier' }
+ .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
= s_('ProjectPage|Project ID: %{project_id}') % { project_id: @project.id }
diff --git a/app/views/projects/_service_desk_settings.html.haml b/app/views/projects/_service_desk_settings.html.haml
index cee3d9071b6..349cd88437f 100644
--- a/app/views/projects/_service_desk_settings.html.haml
+++ b/app/views/projects/_service_desk_settings.html.haml
@@ -17,6 +17,7 @@
selected_file_template_project_id: "#{@project.service_desk_setting&.file_template_project_id}",
outgoing_name: "#{@project.service_desk_setting&.outgoing_name}",
project_key: "#{@project.service_desk_setting&.project_key}",
- templates: available_service_desk_templates_for(@project) } }
+ templates: available_service_desk_templates_for(@project),
+ public_project: "#{@project.public?}" } }
- elsif show_callout?('promote_service_desk_dismissed')
= render 'shared/promotions/promote_servicedesk'
diff --git a/app/views/projects/_stat_anchor_list.html.haml b/app/views/projects/_stat_anchor_list.html.haml
index 4a21cb32c20..1409b28e735 100644
--- a/app/views/projects/_stat_anchor_list.html.haml
+++ b/app/views/projects/_stat_anchor_list.html.haml
@@ -2,7 +2,7 @@
- project_buttons = local_assigns.fetch(:project_buttons, false)
- return unless anchors.any?
-%ul.nav.gl-gap-3
+%ul.nav{ class: (project_buttons ? 'gl-gap-3' : 'gl-gap-5') }
- anchors.each do |anchor|
%li.nav-item
= link_to_if(anchor.link, anchor.label, anchor.link, stat_anchor_attrs(anchor)) do
diff --git a/app/views/projects/activity.html.haml b/app/views/projects/activity.html.haml
index 6a4760c3954..674b21b66b9 100644
--- a/app/views/projects/activity.html.haml
+++ b/app/views/projects/activity.html.haml
@@ -1,4 +1,6 @@
- page_title _("Activity")
+= render_if_exists 'shared/ultimate_feature_removal_banner', project: @project
+
= render 'projects/last_push'
= render 'projects/activity'
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index b44c773adff..f2c4fe017f2 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -1,9 +1,9 @@
- page_title _("Blame"), @blob.path, @ref
-#blob-content-holder.tree-holder
+#blob-content-holder.tree-holder{ data: { testid: 'blob-content-holder' } }
= render "projects/blob/breadcrumb", blob: @blob, blame: true
- .file-holder
+ .file-holder.gl-overflow-hidden
= render "projects/blob/header", blob: @blob, blame: true
.file-blame-legend
@@ -20,7 +20,7 @@
%span.legend-box.legend-box-9
%span.right-label Older
- .table-responsive.file-content.blame.code{ class: user_color_scheme, data: { qa_selector: 'blame_file_content' } }
+ .table-responsive.file-content.blame.code{ class: "#{user_color_scheme} gl-rounded-0!", data: { qa_selector: 'blame_file_content' } }
%table
- current_line = @blame.first_line
- @blame.groups.each do |blame_group|
@@ -59,5 +59,11 @@
- current_line += line_count
- - if blame_pagination
- = paginate(blame_pagination, theme: "gitlab")
+ - if @blame_pagination && @blame_pagination.total_pages > 1
+ .gl-display-flex.gl-justify-content-center.gl-flex-direction-column.gl-align-items-center.gl-p-3.gl-bg-gray-50.gl-border-t-solid.gl-border-t-1.gl-border-gray-100
+ = _('For faster browsing, not all history is shown.')
+ = render Pajamas::ButtonComponent.new(href: namespace_project_blame_path(namespace_id: @project.namespace, project_id: @project, id: @id, no_pagination: true), size: :small, button_options: { class: 'gl-mt-3' }) do |c|
+ = _('View entire blame')
+
+ - if @blame_pagination
+ = paginate(@blame_pagination, theme: "gitlab")
diff --git a/app/views/projects/blob/_header.html.haml b/app/views/projects/blob/_header.html.haml
index 8260aa0fb7e..9c07713b9f8 100644
--- a/app/views/projects/blob/_header.html.haml
+++ b/app/views/projects/blob/_header.html.haml
@@ -6,11 +6,6 @@
.file-actions.gl-display-flex.gl-align-items-center.gl-flex-wrap.gl-md-justify-content-end<
= render 'projects/blob/viewer_switcher', blob: blob unless blame
= render 'shared/web_ide_button', blob: blob
- .btn-group{ role: "group", class: ("gl-ml-3" if current_user) }>
- = render_if_exists 'projects/blob/header_file_locks_link'
- - if current_user
- = replace_blob_link(@project, @ref, @path, blob: blob)
- = delete_blob_link(@project, @ref, @path, blob: blob)
.btn-group.gl-ml-3{ role: "group" }
= copy_blob_source_button(blob) unless blame
= open_raw_blob_button(blob)
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index 220319d31b5..528999f5c89 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -2,6 +2,7 @@
- page_title _("Edit"), @blob.path, @ref
- content_for :prefetch_asset_tags do
- webpack_preload_asset_tag('monaco')
+- add_page_specific_style 'page_bundles/editor'
- if @conflict
= render Pajamas::AlertComponent.new(alert_options: { class: 'gl-mb-5 gl-mt-5' },
diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml
index 27f64104cf4..81b2715b228 100644
--- a/app/views/projects/blob/new.html.haml
+++ b/app/views/projects/blob/new.html.haml
@@ -1,5 +1,6 @@
- breadcrumb_title _("Repository")
- page_title _("New File"), @path.presence, @ref
+- add_page_specific_style 'page_bundles/editor'
%h1.page-title.blob-new-page-title.gl-font-size-h-display
= _('New file')
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index 16ecc1cc5a0..33b2229f5d1 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -3,6 +3,7 @@
- signatures_path = namespace_project_signatures_path(namespace_id: @project.namespace.full_path, project_id: @project.path, id: @last_commit, limit: 1)
- content_for :prefetch_asset_tags do
- webpack_preload_asset_tag('monaco', prefetch: true)
+- add_page_startup_graphql_call('repository/blob_info', { projectPath: @project.full_path, ref: current_ref, filePath: @blob.path, shouldFetchRawText: @blob.rendered_as_text? && !@blob.rich_viewer })
.js-signature-container{ data: { 'signatures-path': signatures_path } }
diff --git a/app/views/projects/branch_rules/_show.html.haml b/app/views/projects/branch_rules/_show.html.haml
index af0e656d301..46665fdb450 100644
--- a/app/views/projects/branch_rules/_show.html.haml
+++ b/app/views/projects/branch_rules/_show.html.haml
@@ -9,4 +9,4 @@
= _('Define rules for who can push, merge, and the required approvals for each branch.')
.settings-content.gl-pr-0
- #js-branch-rules
+ #js-branch-rules{ data: { project_path: @project.full_path } }
diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml
index f8bee5a69e9..63d0cf7145d 100644
--- a/app/views/projects/branches/new.html.haml
+++ b/app/views/projects/branches/new.html.haml
@@ -11,12 +11,12 @@
= form_tag namespace_project_branches_path, method: :post, id: "new-branch-form", class: "js-create-branch-form js-requires-input" do
.form-group.row
- = label_tag :branch_name, nil, class: 'col-form-label col-sm-2'
+ = label_tag :branch_name, _('Branch name'), class: 'col-form-label col-sm-2'
.col-sm-10
= text_field_tag :branch_name, params[:branch_name], required: true, autofocus: true, class: 'form-control js-branch-name monospace'
.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'
+ = label_tag :ref, _('Create from'), class: 'col-form-label col-sm-2'
.col-sm-10.create-from
.dropdown
= hidden_field_tag :ref, default_ref
@@ -24,7 +24,8 @@
.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'
- .form-text.text-muted Existing branch name, tag, or commit SHA
+ .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')
diff --git a/app/views/projects/buttons/_clone.html.haml b/app/views/projects/buttons/_clone.html.haml
index 5fd1c5cd403..10a6bc6b524 100644
--- a/app/views/projects/buttons/_clone.html.haml
+++ b/app/views/projects/buttons/_clone.html.haml
@@ -25,7 +25,8 @@
.input-group-append
= clipboard_button(target: '#http_project_clone', title: _("Copy URL"), class: "input-group-text gl-button btn btn-icon btn-default")
= render_if_exists 'projects/buttons/geo'
- %li.divider.mt-2
+ = render_if_exists 'projects/buttons/kerberos_clone_field'
+ %li.divider.mt-2
%li.pt-2.gl-new-dropdown-item
%label.label-bold{ class: 'gl-px-4!' }
= _('Open in your IDE')
@@ -51,4 +52,3 @@
%a.dropdown-item.open-with-link{ href: xcode_uri_to_repo(@project) }
.gl-new-dropdown-item-text-wrapper
= _("Xcode")
- = render_if_exists 'projects/buttons/kerberos_clone_field'
diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml
index bd096ed74f5..b48369322e4 100644
--- a/app/views/projects/ci/builds/_build.html.haml
+++ b/app/views/projects/ci/builds/_build.html.haml
@@ -82,7 +82,7 @@
- if stage
%td
- = job.stage
+ = job.stage_name
%td
- if job.duration
diff --git a/app/views/projects/ci/pipeline_editor/show.html.haml b/app/views/projects/ci/pipeline_editor/show.html.haml
index 18eac48d42a..bc352ff6c7d 100644
--- a/app/views/projects/ci/pipeline_editor/show.html.haml
+++ b/app/views/projects/ci/pipeline_editor/show.html.haml
@@ -1,4 +1,5 @@
- @force_fluid_layout = true
+- add_page_specific_style 'page_bundles/editor'
- add_page_specific_style 'page_bundles/pipelines'
- add_page_specific_style 'page_bundles/pipeline_editor'
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index 4007b657403..6b06584ea25 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -28,7 +28,7 @@
= 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
- = sprite_icon('rss', css_class: 'qa-rss-icon')
+ = sprite_icon('rss')
= render_if_exists 'projects/commits/mirror_status'
diff --git a/app/views/projects/default_branch/_show.html.haml b/app/views/projects/default_branch/_show.html.haml
index b1fb9c70a54..eba0f336f80 100644
--- a/app/views/projects/default_branch/_show.html.haml
+++ b/app/views/projects/default_branch/_show.html.haml
@@ -16,7 +16,7 @@
= _('A default branch cannot be chosen for an empty project.')
- else
.form-group
- = f.label :default_branch, "Default branch", class: 'label-bold'
+ = f.label :default_branch, _("Default branch"), class: 'label-bold'
= f.select(:default_branch, @project.repository.branch_names, {}, {class: 'select2 select-wide', data: { qa_selector: 'default_branch_dropdown' }})
.form-group
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index a7dd69a9607..70df995cdf3 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -5,6 +5,8 @@
- expanded = expanded_by_default?
- reduce_visibility_form_id = 'reduce-visibility-form'
+= render_if_exists 'shared/ultimate_feature_removal_banner', project: @project
+
%section.settings.general-settings.no-animate.expanded#js-general-settings
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Naming, topics, avatar')
@@ -26,23 +28,13 @@
%template.js-project-permissions-form-data{ type: "application/json" }= project_permissions_panel_data(@project).to_json.html_safe
.js-project-permissions-form{ data: visibility_confirm_modal_data(@project, reduce_visibility_form_id) }
-%section.rspec-merge-request-settings.settings.merge-requests-feature.no-animate#js-merge-request-settings{ class: [('expanded' if expanded), ('hidden' if @project.project_feature.send(:merge_requests_access_level) == 0)], data: { qa_selector: 'merge_request_settings_content' } }
- .settings-header
- %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Merge requests')
- = render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
- = expanded ? _('Collapse') : _('Expand')
- = render_if_exists 'projects/merge_request_settings_description_text'
-
- .settings-content
- = render_if_exists 'shared/promotions/promote_mr_features'
-
- = gitlab_ui_form_for @project, html: { multipart: true, class: "merge-request-settings-form js-mr-settings-form" }, authenticity_token: true do |f|
- %input{ name: 'update_section', type: 'hidden', value: 'js-merge-request-settings' }
- = render 'projects/merge_request_settings', form: f
- = f.submit _('Save changes'), class: "btn gl-button btn-confirm rspec-save-merge-request-changes", data: { qa_selector: 'save_merge_request_changes_button' }
-
-= render_if_exists 'projects/merge_request_approvals_settings', expanded: expanded
-
+- if show_merge_request_settings_callout?
+ %section.settings.expanded
+ = render Pajamas::AlertComponent.new(variant: :info,
+ title: _('Merge requests and approvals settings have moved.'),
+ alert_options: { class: 'js-merge-request-settings-callout gl-my-5', data: { feature_id: Users::CalloutsHelper::MERGE_REQUEST_SETTINGS_MOVED_CALLOUT, dismiss_endpoint: callouts_path, defer_links: 'true' } }) do |c|
+ = c.body do
+ = _('On the left sidebar, select %{merge_requests_link} to view them.').html_safe % { merge_requests_link: link_to('Settings > Merge requests', project_settings_merge_requests_path(@project)).html_safe }
%section.settings.no-animate{ class: ('expanded' if expanded), data: { qa_selector: 'badges_settings_content' } }
.settings-header
diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml
index 36347776ec9..a9913fe3d5e 100644
--- a/app/views/projects/forks/new.html.haml
+++ b/app/views/projects/forks/new.html.haml
@@ -3,7 +3,7 @@
#fork-groups-mount-element{ data: { fork_illustration: image_path('illustrations/project-create-new-sm.svg'),
endpoint: new_project_fork_path(@project, format: :json),
new_group_path: new_group_path,
- project_full_path: project_path(@project),
+ project_full_path: @project.full_path,
visibility_help_path: help_page_path("user/public_access"),
project_id: @project.id,
project_name: @project.name,
diff --git a/app/views/projects/google_cloud/databases/cloudsql_form.html.haml b/app/views/projects/google_cloud/databases/cloudsql_form.html.haml
new file mode 100644
index 00000000000..05838717b49
--- /dev/null
+++ b/app/views/projects/google_cloud/databases/cloudsql_form.html.haml
@@ -0,0 +1,9 @@
+- add_to_breadcrumbs _('Google Cloud'), project_google_cloud_path(@project)
+- add_to_breadcrumbs s_('CloudSeed|Databases'), project_google_cloud_databases_path(@project)
+- breadcrumb_title @title
+- page_title @title
+
+- @content_class = "limit-container-width" unless fluid_layout
+
+= form_tag project_google_cloud_databases_path(@project), method: 'post' do
+ #js-google-cloud-databases-cloudsql-form{ data: @js_data }
diff --git a/app/views/projects/google_cloud/gcp_regions/index.html.haml b/app/views/projects/google_cloud/gcp_regions/index.html.haml
index 36b5630611e..4cc218ff548 100644
--- a/app/views/projects/google_cloud/gcp_regions/index.html.haml
+++ b/app/views/projects/google_cloud/gcp_regions/index.html.haml
@@ -1,5 +1,5 @@
- add_to_breadcrumbs _('Google Cloud'), project_google_cloud_path(@project)
-- breadcrumb_title _('CloudSeed|Regions')
+- breadcrumb_title s_('CloudSeed|Regions')
- page_title s_('CloudSeed|Regions')
- @content_class = "limit-container-width" unless fluid_layout
diff --git a/app/views/projects/harbor/repositories/index.html.haml b/app/views/projects/harbor/repositories/index.html.haml
index 0fce3b7f8aa..e6f0e3e950c 100644
--- a/app/views/projects/harbor/repositories/index.html.haml
+++ b/app/views/projects/harbor/repositories/index.html.haml
@@ -4,8 +4,9 @@
#js-harbor-registry-list-project{ data: { endpoint: project_harbor_repositories_path(@project),
"no_containers_image" => image_path('illustrations/docker-empty-state.svg'),
"containers_error_image" => image_path('illustrations/docker-error-state.svg'),
- "repository_url" => 'demo.harbor.com/gitlab-cn/build/cng-images/gitlab-kas',
- "registry_host_url_with_port" => 'demo.harbor.com',
+ "repository_url" => @project.harbor_integration.hostname,
+ "harbor_integration_project_name" => @project.harbor_integration.project_name,
+ "project_name" => @project.name,
connection_error: (!!@connection_error).to_s,
invalid_path_error: (!!@invalid_path_error).to_s,
is_group_page: false.to_s, } }
diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml
index 16b795ee3c9..11b652cc818 100644
--- a/app/views/projects/issues/_discussion.html.haml
+++ b/app/views/projects/issues/_discussion.html.haml
@@ -7,4 +7,5 @@
noteable_data: serialize_issuable(@issue, with_blocking_issues: true),
noteable_type: 'Issue',
target_type: 'issue',
- current_user_data: UserSerializer.new.represent(current_user, {only_path: true}, CurrentUserEntity).to_json } }
+ current_user_data: UserSerializer.new.represent(current_user, {only_path: true}, CurrentUserEntity).to_json,
+ can_add_timeline_events: "#{can?(current_user, :admin_incident_management_timeline_event, @issue)}" } }
diff --git a/app/views/projects/issues/_related_branches.html.haml b/app/views/projects/issues/_related_branches.html.haml
index 310a0c1a61e..466eca2fdb0 100644
--- a/app/views/projects/issues/_related_branches.html.haml
+++ b/app/views/projects/issues/_related_branches.html.haml
@@ -1,7 +1,7 @@
- if @related_branches.any?
%h2.gl-font-lg
= pluralize(@related_branches.size, 'Related Branch')
- %ul.related-merge-requests.gl-pl-0
+ %ul.related-merge-requests.gl-pl-0.gl-mb-3
- @related_branches.each do |branch|
%li.gl-display-flex.gl-align-items-center
- if branch[:pipeline_status].present?
diff --git a/app/views/projects/issues/_work_item_links.html.haml b/app/views/projects/issues/_work_item_links.html.haml
index df2ffdd30ee..bc2136b89fb 100644
--- a/app/views/projects/issues/_work_item_links.html.haml
+++ b/app/views/projects/issues/_work_item_links.html.haml
@@ -1,2 +1,2 @@
- if Feature.enabled?(:work_items_hierarchy, @project)
- .js-work-item-links-root{ data: { issuable_id: @issue.id, project_path: @project.full_path, wi: work_items_index_data(@project) } }
+ .js-work-item-links-root{ data: { issuable_id: @issue.id, iid: @issue.iid, project_path: @project.full_path, wi: work_items_index_data(@project) } }
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index f7a02c521f5..f95689c0b1d 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -3,6 +3,7 @@
- search = params[:search]
- subscribed = params[:subscribed]
- labels_or_filters = @labels.exists? || @prioritized_labels.exists? || search.present? || subscribed.present?
+= render_if_exists 'shared/ultimate_feature_removal_banner', project: @project
- if labels_or_filters
#js-promote-label-modal
diff --git a/app/views/projects/merge_requests/_awards_block.html.haml b/app/views/projects/merge_requests/_awards_block.html.haml
index 80a58053ff7..64d35b4dfe6 100644
--- a/app/views/projects/merge_requests/_awards_block.html.haml
+++ b/app/views/projects/merge_requests/_awards_block.html.haml
@@ -1,5 +1,5 @@
.content-block.emoji-block.emoji-list-container.js-noteable-awards
= render 'award_emoji/awards_block', awardable: @merge_request, inline: true, api_awards_path: award_emoji_merge_request_api_path(@merge_request) do
- .ml-auto.gl-my-2
+ .gl-my-2.gl-xs-w-full
#js-vue-sort-issue-discussions
= render "projects/merge_requests/discussion_filter"
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 62cd8bd94e3..22571b11639 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
@@ -11,6 +11,10 @@
.gl-new-dropdown-inner
.gl-new-dropdown-contents
%ul
+ - if !@merge_request.merged? && current_user && moved_mr_sidebar_enabled?
+ %li.gl-new-dropdown-item.js-sidebar-subscriptions-entry-point
+ %li.gl-new-dropdown-divider
+ %hr.dropdown-divider
- if can?(current_user, :update_merge_request, @merge_request)
%li.gl-new-dropdown-item{ class: "gl-md-display-none!" }
= link_to edit_project_merge_request_path(@project, @merge_request), class: 'dropdown-item' do
@@ -32,17 +36,19 @@
.gl-new-dropdown-item-text-wrapper
= _('Reopen')
= display_issuable_type
+ - if moved_mr_sidebar_enabled?
+ %li.gl-new-dropdown-item#js-lock-entry-point
+ %li.gl-new-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
+ = _('Copy reference')
- unless current_controller?('conflicts')
- - if current_user && moved_mr_sidebar_enabled?
- - if !@merge_request.merged?
+ - unless issuable_author_is_current_user(@merge_request)
+ - if moved_mr_sidebar_enabled?
%li.gl-new-dropdown-divider
%hr.dropdown-divider
- %li.gl-new-dropdown-item.js-sidebar-subscriptions-entry-point
- - unless issuable_author_is_current_user(@merge_request)
%li.gl-new-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')
- - if moved_mr_sidebar_enabled?
- %li.gl-new-dropdown-item#js-lock-entry-point
diff --git a/app/views/projects/merge_requests/_widget.html.haml b/app/views/projects/merge_requests/_widget.html.haml
index 4e69dad2e12..783e3ac97c1 100644
--- a/app/views/projects/merge_requests/_widget.html.haml
+++ b/app/views/projects/merge_requests/_widget.html.haml
@@ -10,7 +10,7 @@
window.gl.mrWidgetData.squash_before_merge_help_path = '#{help_page_path("user/project/merge_requests/squash_and_merge")}';
window.gl.mrWidgetData.ci_troubleshooting_docs_path = '#{help_page_path('ci/troubleshooting.md')}';
window.gl.mrWidgetData.mr_troubleshooting_docs_path = '#{help_page_path('user/project/merge_requests/reviews/index.md', anchor: 'troubleshooting')}';
- window.gl.mrWidgetData.pipeline_must_succeed_docs_path = '#{help_page_path('user/project/merge_requests/merge_when_pipeline_succeeds.md', anchor: 'only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds')}';
+ window.gl.mrWidgetData.pipeline_must_succeed_docs_path = '#{help_page_path('user/project/merge_requests/merge_when_pipeline_succeeds.md', anchor: 'require-a-successful-pipeline-for-merge')}';
window.gl.mrWidgetData.security_approvals_help_page_path = '#{help_page_path('user/application_security/index.md', anchor: 'security-approvals-in-merge-requests')}';
window.gl.mrWidgetData.license_compliance_docs_path = '#{help_page_path('user/compliance/license_compliance/index.md', anchor: 'policies')}';
window.gl.mrWidgetData.eligible_approvers_docs_path = '#{help_page_path('user/project/merge_requests/approvals/rules.md', anchor: 'eligible-approvers')}';
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 cee8d2e92aa..17b1e5a757c 100644
--- a/app/views/projects/merge_requests/creations/_new_compare.html.haml
+++ b/app/views/projects/merge_requests/creations/_new_compare.html.haml
@@ -8,7 +8,7 @@
.col-lg-6
.card-new-merge-request
%h2.gl-font-size-h2
- Source branch
+ = _('Source branch')
.clearfix
.merge-request-select.dropdown
= f.hidden_field :source_project_id
@@ -38,7 +38,7 @@
.col-lg-6
.card-new-merge-request
%h2.gl-font-size-h2
- Target branch
+ = _('Target branch')
.clearfix
- projects = target_projects(@project)
.merge-request-select.dropdown
@@ -67,5 +67,5 @@
%ul.list-unstyled.mr_target_commit
- if @merge_request.errors.any?
- = form_errors(@merge_request, pajamas_alert: true)
- = f.submit 'Compare branches and continue', class: "gl-button btn btn-confirm mr-compare-btn gl-mt-4", data: { qa_selector: "compare_branches_button" }
+ = 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" }
diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml
index 78976be5dd7..d34848c801d 100644
--- a/app/views/projects/merge_requests/show.html.haml
+++ b/app/views/projects/merge_requests/show.html.haml
@@ -16,11 +16,13 @@
- add_page_startup_api_call @endpoint_metadata_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') }
+ .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
@@ -37,19 +39,19 @@
= 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", id: "diffs-tab", qa_selector: "diffs_tab" do
+ = 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 }
- - if Feature.enabled?(:moved_mr_sidebar, @project)
- .gl-ml-auto.gl-align-items-center.gl-display-none.gl-md-display-flex.js-expand-sidebar{ class: "gl-lg-display-none!" }
- = render Pajamas::ButtonComponent.new(size: 'small',
- icon: 'chevron-double-lg-left',
- button_options: { class: 'js-sidebar-toggle' }) do
- = _('Expand')
.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-issuable-todo{ 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
@@ -99,7 +101,7 @@
#js-review-bar
-- if Feature.enabled?(:mr_experience_survey, @project) && current_user
+- 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
diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index 0d56bf7793d..c11d5e7c9b6 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_form.html.haml
@@ -1,6 +1,6 @@
= gitlab_ui_form_for [@project, @milestone],
html: { class: 'milestone-form common-note-form js-quick-submit js-requires-input' } do |f|
- = form_errors(@milestone, pajamas_alert: true)
+ = form_errors(@milestone)
.form-group.row
.col-form-label.col-sm-2
= f.label :title, _('Title')
diff --git a/app/views/projects/mirrors/_mirror_repos.html.haml b/app/views/projects/mirrors/_mirror_repos.html.haml
index a90d5224d04..2ae7d300979 100644
--- a/app/views/projects/mirrors/_mirror_repos.html.haml
+++ b/app/views/projects/mirrors/_mirror_repos.html.haml
@@ -17,7 +17,7 @@
= gitlab_ui_form_for @project, url: project_mirror_path(@project), html: { class: 'gl-show-field-errors js-mirror-form', autocomplete: 'new-password', data: mirrors_form_data_attributes } do |f|
.panel.panel-default
.panel-body
- %div= form_errors(@project, pajamas_alert: true)
+ %div= form_errors(@project)
.form-group.has-feedback
= label_tag :url, _('Git repository URL'), class: 'label-light'
@@ -41,40 +41,4 @@
= c.body do
= _('Mirror settings are only available to GitLab administrators.')
- .panel.panel-default
- .table-responsive
- %table.table.push-pull-table
- %thead
- %tr
- %th
- = _('Mirrored repositories')
- = render_if_exists 'projects/mirrors/mirrored_repositories_count'
- %th= _('Direction')
- %th= _('Last update attempt')
- %th= _('Last successful update')
- %th
- %th
- %tbody.js-mirrors-table-body
- = render_if_exists 'projects/mirrors/table_pull_row'
- - @project.remote_mirrors.each_with_index do |mirror, index|
- - next if mirror.new_record?
- %tr.rspec-mirrored-repository-row{ class: ('bg-secondary' if mirror.disabled?), data: { qa_selector: 'mirrored_repository_row' } }
- %td{ data: { qa_selector: 'mirror_repository_url_cell' } }= mirror.safe_url || _('Invalid URL')
- %td= _('Push')
- %td
- = mirror.last_update_started_at.present? ? time_ago_with_tooltip(mirror.last_update_started_at) : _('Never')
- %td{ data: { qa_selector: 'mirror_last_update_at_cell' } }= mirror.last_update_at.present? ? time_ago_with_tooltip(mirror.last_update_at) : _('Never')
- %td
- - if mirror.disabled?
- = render 'projects/mirrors/disabled_mirror_badge'
- - if mirror.last_error.present?
- = gl_badge_tag _('Error'), { variant: :danger }, { data: { toggle: 'tooltip', html: 'true', qa_selector: 'mirror_error_badge' }, title: html_escape(mirror.last_error.try(:strip)) }
- %td.gl-display-flex
- - if mirror_settings_enabled
- .btn-group.mirror-actions-group{ role: 'group' }
- - if mirror.ssh_key_auth?
- = clipboard_button(text: mirror.ssh_public_key, class: 'gl-button btn btn-default btn-icon', title: _('Copy SSH public key'), qa_selector: 'copy_public_key_button')
- = render 'shared/remote_mirror_update_button', remote_mirror: mirror
- = render Pajamas::ButtonComponent.new(variant: :danger,
- icon: 'remove',
- button_options: { class: 'js-delete-mirror qa-delete-mirror rspec-delete-mirror', title: _('Remove'), data: { mirror_id: mirror.id, toggle: 'tooltip', container: 'body' } })
+ = render 'projects/mirrors/mirror_repos_list'
diff --git a/app/views/projects/mirrors/_mirror_repos_list.html.haml b/app/views/projects/mirrors/_mirror_repos_list.html.haml
new file mode 100644
index 00000000000..2dbcbd659c8
--- /dev/null
+++ b/app/views/projects/mirrors/_mirror_repos_list.html.haml
@@ -0,0 +1,47 @@
+- mirror_settings_enabled = can?(current_user, :admin_remote_mirror, @project)
+
+.panel.panel-default
+ .table-responsive
+ - if !@project.mirror? && @project.remote_mirrors.count == 0
+ .gl-card.gl-mt-5
+ .gl-card-header
+ %strong
+ = _('Mirrored repositories') + ' (0)'
+ .gl-card-body
+ = _('There are currently no mirrored repositories.')
+ - else
+ %table.table.push-pull-table
+ %thead
+ %tr
+ %th
+ = _('Mirrored repositories')
+ = render_if_exists 'projects/mirrors/mirrored_repositories_count'
+ %th= _('Direction')
+ %th= _('Last update attempt')
+ %th= _('Last successful update')
+ %th
+ %th
+ %tbody.js-mirrors-table-body
+ = render_if_exists 'projects/mirrors/table_pull_row'
+ - @project.remote_mirrors.each_with_index do |mirror, index|
+ - next if mirror.new_record?
+ %tr.rspec-mirrored-repository-row{ class: ('bg-secondary' if mirror.disabled?), data: { qa_selector: 'mirrored_repository_row' } }
+ %td{ data: { qa_selector: 'mirror_repository_url_cell' } }= mirror.safe_url || _('Invalid URL')
+ %td= _('Push')
+ %td
+ = mirror.last_update_started_at.present? ? time_ago_with_tooltip(mirror.last_update_started_at) : _('Never')
+ %td{ data: { qa_selector: 'mirror_last_update_at_cell' } }= mirror.last_update_at.present? ? time_ago_with_tooltip(mirror.last_update_at) : _('Never')
+ %td
+ - if mirror.disabled?
+ = render 'projects/mirrors/disabled_mirror_badge'
+ - if mirror.last_error.present?
+ = gl_badge_tag _('Error'), { variant: :danger }, { data: { toggle: 'tooltip', html: 'true', qa_selector: 'mirror_error_badge' }, title: html_escape(mirror.last_error.try(:strip)) }
+ %td.gl-display-flex
+ - if mirror_settings_enabled
+ .btn-group.mirror-actions-group{ role: 'group' }
+ - if mirror.ssh_key_auth?
+ = clipboard_button(text: mirror.ssh_public_key, class: 'gl-button btn btn-default btn-icon', title: _('Copy SSH public key'), qa_selector: 'copy_public_key_button')
+ = render 'shared/remote_mirror_update_button', remote_mirror: mirror
+ = render Pajamas::ButtonComponent.new(variant: :danger,
+ icon: 'remove',
+ button_options: { class: 'js-delete-mirror qa-delete-mirror rspec-delete-mirror', title: _('Remove'), data: { mirror_id: mirror.id, toggle: 'tooltip', container: 'body' } })
diff --git a/app/views/projects/pages/_header.html.haml b/app/views/projects/pages/_header.html.haml
index da35f2fdf09..cf51796e878 100644
--- a/app/views/projects/pages/_header.html.haml
+++ b/app/views/projects/pages/_header.html.haml
@@ -1,4 +1,4 @@
-- can_add_new_domain = can?(current_user, :update_pages, @project) && (Gitlab.config.pages.external_http || Gitlab.config.pages.external_https)
+- can_add_new_domain = can_create_pages_custom_domains?(current_user, @project)
%h1.page-title.gl-font-size-h-display.with-button
= s_('GitLabPages|Pages')
diff --git a/app/views/projects/pages/new.html.haml b/app/views/projects/pages/new.html.haml
index cdd52a933e9..5dea6b02e36 100644
--- a/app/views/projects/pages/new.html.haml
+++ b/app/views/projects/pages/new.html.haml
@@ -1,4 +1,4 @@
-- if Feature.enabled?(:use_pipeline_wizard_for_pages, @group)
+- if Feature.enabled?(:use_pipeline_wizard_for_pages, @project.group)
#js-pages{ data: @pipeline_wizard_data }
- else
diff --git a/app/views/projects/pipeline_schedules/_form.html.haml b/app/views/projects/pipeline_schedules/_form.html.haml
index d29030f992f..5d5ca2aaaf3 100644
--- a/app/views/projects/pipeline_schedules/_form.html.haml
+++ b/app/views/projects/pipeline_schedules/_form.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for [@project, @schedule], as: :schedule, html: { id: "new-pipeline-schedule-form", class: "js-pipeline-schedule-form pipeline-schedule-form" } do |f|
- = form_errors(@schedule, pajamas_alert: true)
+ = form_errors(@schedule)
.form-group.row
.col-md-9
= f.label :description, _('Description'), class: 'label-bold'
diff --git a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
index c36c3ae5adf..10dc74647b2 100644
--- a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
+++ b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
@@ -2,7 +2,7 @@
%tr.pipeline-schedule-table-row
%td
= pipeline_schedule.description
- %td.branch-name-cell
+ %td.branch-name-cell.gl-text-truncate
- if pipeline_schedule.for_tag?
= sprite_icon('tag', size: 12)
- else
@@ -17,7 +17,7 @@
%span ##{pipeline_schedule.last_pipeline.id}
- else
= s_("PipelineSchedules|None")
- %td.next-run-cell
+ %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
diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml
index a8ad53db8c2..e83547fd8f8 100644
--- a/app/views/projects/pipelines/_with_tabs.html.haml
+++ b/app/views/projects/pipelines/_with_tabs.html.haml
@@ -42,7 +42,7 @@
#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.has_reports?(Ci::JobArtifact.test_reports).to_s,
+ 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/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 37fe80d2aaf..34305d15eff 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -2,6 +2,7 @@
- page_title _("Members")
= render_if_exists 'projects/free_user_cap_alert', project: @project
+= render_if_exists 'shared/ultimate_feature_removal_banner', project: @project
.row.gl-mt-3
.col-lg-12
diff --git a/app/views/projects/project_templates/_template.html.haml b/app/views/projects/project_templates/_template.html.haml
index d0fdd3a729a..9dde86f77b4 100644
--- a/app/views/projects/project_templates/_template.html.haml
+++ b/app/views/projects/project_templates/_template.html.haml
@@ -1,4 +1,4 @@
-.template-option.d-flex.align-items-center{ data: { qa_selector: 'template_option_row' } }
+.template-option.d-flex.align-items-center{ data: { qa_selector: 'template_option_container' } }
.logo.gl-mr-3.px-1
= image_tag template.logo, size: 32, class: "btn-template-icon icon-#{template.name}"
.description
diff --git a/app/views/projects/protected_branches/_create_protected_branch.html.haml b/app/views/projects/protected_branches/_create_protected_branch.html.haml
index 20cd45be6da..34fe9a29068 100644
--- a/app/views/projects/protected_branches/_create_protected_branch.html.haml
+++ b/app/views/projects/protected_branches/_create_protected_branch.html.haml
@@ -1,14 +1,14 @@
- content_for :merge_access_levels do
.merge_access_levels-container
= dropdown_tag('Select',
- options: { toggle_class: 'js-allowed-to-merge qa-allowed-to-merge-select wide',
- dropdown_class: 'dropdown-menu-selectable qa-allowed-to-merge-dropdown rspec-allowed-to-merge-dropdown capitalize-header',
- data: { field_name: 'protected_branch[merge_access_levels_attributes][0][access_level]', input_id: 'merge_access_levels_attributes' }})
+ 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 qa-allowed-to-push-select js-multiselect wide",
- dropdown_class: 'dropdown-menu-selectable qa-allowed-to-push-dropdown rspec-allowed-to-push-dropdown capitalize-header',
- data: { field_name: 'protected_branch[push_access_levels_attributes][0][access_level]', input_id: 'push_access_levels_attributes' }})
+ 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/shared/_branches_list.html.haml b/app/views/projects/protected_branches/shared/_branches_list.html.haml
index 5964f2bfeda..64db51d5df2 100644
--- a/app/views/projects/protected_branches/shared/_branches_list.html.haml
+++ b/app/views/projects/protected_branches/shared/_branches_list.html.haml
@@ -1,4 +1,4 @@
-.protected-branches-list.js-protected-branches-list.qa-protected-branches-list
+.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 }
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
index 35770c32f9f..277cbf00034 100644
--- a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
+++ b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
@@ -4,7 +4,7 @@
- c.header do
= s_("ProtectedBranch|Protect a branch")
- c.body do
- = form_errors(@protected_branch, pajamas_alert: true)
+ = form_errors(@protected_branch)
.form-group.row
= f.label :name, s_('ProtectedBranch|Branch:'), class: 'col-sm-12'
.col-sm-12
diff --git a/app/views/projects/protected_branches/shared/_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_protected_branch.html.haml
index 6dd3b2e8d5e..098bd4a7eeb 100644
--- a/app/views/projects/protected_branches/shared/_protected_branch.html.haml
+++ b/app/views/projects/protected_branches/shared/_protected_branch.html.haml
@@ -1,6 +1,6 @@
- can_admin_project = can?(current_user, :admin_project, @project)
-%tr.qa-protected-branch.js-protected-branch-edit-form{ data: { url: namespace_project_protected_branch_path(@project.namespace, @project, protected_branch) } }
+%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
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 e257117a32e..ba0935fff7d 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
@@ -4,7 +4,7 @@
.card-header
= _('Protect a tag')
.card-body
- = form_errors(@protected_tag, pajamas_alert: true)
+ = form_errors(@protected_tag)
.form-group.row
= f.label :name, _('Tag:'), class: 'col-md-2 text-left text-md-right'
.col-md-10.protected-tags-dropdown
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 ea77bda0b0f..81526685bfc 100644
--- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
@@ -16,7 +16,7 @@
.row
.col-lg-12
= gitlab_ui_form_for @project, url: project_settings_ci_cd_path(@project, anchor: 'autodevops-settings') do |f|
- = form_errors(@project, pajamas_alert: true)
+ = form_errors(@project)
%fieldset.builds-feature.js-auto-devops-settings
.form-group
= f.fields_for :auto_devops_attributes, @auto_devops do |form|
diff --git a/app/views/projects/settings/ci_cd/_form.html.haml b/app/views/projects/settings/ci_cd/_form.html.haml
index 50e96528c0d..9419dacc16f 100644
--- a/app/views/projects/settings/ci_cd/_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_form.html.haml
@@ -6,7 +6,7 @@
.row.gl-mt-3
.col-lg-12
= gitlab_ui_form_for @project, url: project_settings_ci_cd_path(@project, anchor: 'js-general-pipeline-settings') do |f|
- = form_errors(@project, pajamas_alert: true)
+ = form_errors(@project)
%fieldset.builds-feature
.form-group
= f.gitlab_ui_checkbox_component :public_builds,
diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml
index dd9cc296d52..c1df7b88352 100644
--- a/app/views/projects/settings/ci_cd/show.html.haml
+++ b/app/views/projects/settings/ci_cd/show.html.haml
@@ -103,8 +103,7 @@
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand')
%p
- = _("Control which projects can be accessed by API requests authenticated with this project's CI_JOB_TOKEN CI/CD variable. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API.")
- = link_to _('Learn more'), help_page_path('ci/jobs/ci_job_token'), target: '_blank', rel: 'noopener noreferrer'
+ = _("Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects.")
.settings-content
= render 'ci/token_access/index'
diff --git a/app/views/projects/settings/merge_requests/show.html.haml b/app/views/projects/settings/merge_requests/show.html.haml
new file mode 100644
index 00000000000..886e276dea5
--- /dev/null
+++ b/app/views/projects/settings/merge_requests/show.html.haml
@@ -0,0 +1,18 @@
+- breadcrumb_title _('Merge requests')
+- page_title _('Merge requests')
+- @content_class = 'limit-container-width' unless fluid_layout
+
+%section.rspec-merge-request-settings.settings.merge-requests-feature.no-animate#js-merge-request-settings.expanded{ class: [('hidden' if @project.project_feature.send(:merge_requests_access_level) == 0)], data: { qa_selector: 'merge_request_settings_content' } }
+ .settings-header
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Merge requests')
+ = render_if_exists 'projects/merge_request_settings_description_text'
+
+ .settings-content
+ = render_if_exists 'shared/promotions/promote_mr_features'
+
+ = gitlab_ui_form_for @project, url: project_settings_merge_requests_path(@project), html: { multipart: true, class: "merge-request-settings-form js-mr-settings-form" }, authenticity_token: true do |f|
+ %input{ name: 'update_section', type: 'hidden', value: 'js-merge-request-settings' }
+ = render 'projects/merge_request_settings', form: f
+ = f.submit _('Save changes'), class: "btn gl-button btn-confirm rspec-save-merge-request-changes", data: { qa_selector: 'save_merge_request_changes_button' }
+
+= render_if_exists 'projects/settings/merge_requests/merge_request_approvals_settings', expanded: true
diff --git a/app/views/projects/settings/operations/show.html.haml b/app/views/projects/settings/operations/show.html.haml
index 87e3e03099c..90e0ccce8b4 100644
--- a/app/views/projects/settings/operations/show.html.haml
+++ b/app/views/projects/settings/operations/show.html.haml
@@ -2,19 +2,6 @@
- page_title _('Monitor Settings')
- breadcrumb_title _('Monitor Settings')
-= render Pajamas::AlertComponent.new(variant: :danger,
- dismissible: false,
- title: s_('Deprecations|Feature deprecation and removal')) do |c|
- = c.body do
- - removal_epic_link_url = 'https://gitlab.com/groups/gitlab-org/-/epics/7188'
- - removal_epic_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer" class="gl-link">'.html_safe % { url: removal_epic_link_url }
- - opstrace_link_url = 'https://gitlab.com/groups/gitlab-org/-/epics/6976'
- - opstrace_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer" class="gl-link">'.html_safe % { url: opstrace_link_url }
- - link_end = '</a>'.html_safe
- = html_escape(s_('Deprecations|The metrics feature was deprecated in GitLab 14.7.'))
- = html_escape(s_('Deprecations|The logs and tracing features were also deprecated in GitLab 14.7, and are %{removal_link_start} scheduled for removal %{link_end} in GitLab 15.0.')) % {removal_link_start: removal_epic_link_start, link_end: link_end }
- = html_escape(s_('Deprecations|For information on a possible replacement, %{opstrace_link_start} learn more about Opstrace %{link_end}.')) % {opstrace_link_start: opstrace_link_start, link_end: link_end }
-
= render 'projects/settings/operations/metrics_dashboard'
= render 'projects/settings/operations/error_tracking'
= render 'projects/settings/operations/alert_management'
diff --git a/app/views/projects/settings/packages_and_registries/cleanup_tags.html.haml b/app/views/projects/settings/packages_and_registries/cleanup_tags.html.haml
index 795544b75a2..5244587c16d 100644
--- a/app/views/projects/settings/packages_and_registries/cleanup_tags.html.haml
+++ b/app/views/projects/settings/packages_and_registries/cleanup_tags.html.haml
@@ -1,6 +1,6 @@
-- add_to_breadcrumbs _('Packages & Registries'), project_settings_packages_and_registries_path(@project)
+- add_to_breadcrumbs _('Package and registry settings'), project_settings_packages_and_registries_path(@project)
- breadcrumb_title s_('ContainerRegistry|Clean up image tags')
-- page_title s_('ContainerRegistry|Clean up image tags'), _('Packages & Registries')
+- page_title s_('ContainerRegistry|Clean up image tags'), _('Package and registry settings')
- @content_class = 'limit-container-width' unless fluid_layout
#js-registry-settings-cleanup-image-tags{ data: cleanup_settings_data }
diff --git a/app/views/projects/settings/packages_and_registries/show.html.haml b/app/views/projects/settings/packages_and_registries/show.html.haml
index d579981ebc0..e0ac07f5f31 100644
--- a/app/views/projects/settings/packages_and_registries/show.html.haml
+++ b/app/views/projects/settings/packages_and_registries/show.html.haml
@@ -1,5 +1,5 @@
-- breadcrumb_title _('Packages & Registries')
-- page_title _('Packages & Registries')
+- breadcrumb_title _('Package and registry settings')
+- page_title _('Package and registry settings')
- @content_class = 'limit-container-width' unless fluid_layout
#js-registry-settings{ data: settings_data }
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 1f529849b28..e9d1661a4f1 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -7,6 +7,7 @@
= auto_discovery_link_tag(:atom, project_path(@project, rss_url_options), title: "#{@project.name} activity")
= render_if_exists 'projects/free_user_cap_alert', project: @project
+= render_if_exists 'shared/ultimate_feature_removal_banner', project: @project
= render partial: 'flash_messages', locals: { project: @project }
= render 'clusters_deprecation_alert'
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index 2721f94134c..fda797f3228 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -10,7 +10,7 @@
.nav-controls
#js-tags-sort-dropdown{ data: { filter_tags_path: filter_tags_path(search: @search, sort: @sort), sort_options: tags_sort_options_hash.to_json } }
= link_to project_tags_path(@project, rss_url_options), title: _("Tags feed"), class: 'btn gl-button btn-default btn-icon has-tooltip gl-ml-auto' do
- = sprite_icon('rss', css_class: 'gl-icon qa-rss-icon')
+ = sprite_icon('rss', css_class: 'gl-icon')
- if can?(current_user, :admin_tag, @project)
= link_to new_project_tag_path(@project), class: 'btn gl-button btn-confirm', data: { qa_selector: "new_tag_button" } do
= s_('TagsPage|New tag')
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index 3b546888375..79fc1a64790 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -2,22 +2,21 @@
- default_ref = params[:ref] || @project.default_branch
- if @error
- = render Pajamas::AlertComponent.new(variant: :danger, dismissible: true, close_button_options: { class: 'gl-alert-dismiss' }) do |c|
+ = render Pajamas::AlertComponent.new(variant: :danger, dismissible: true ) do |c|
= c.body do
= @error
%h1.page-title.gl-font-size-h-display
= s_('TagsPage|New Tag')
-%hr
= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "common-note-form tag-form js-quick-submit js-requires-input" do
.form-group.row
- = label_tag :tag_name, nil, class: 'col-form-label col-sm-2'
- .col-sm-10
+ .col-sm-12
+ = 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
- = label_tag :ref, 'Create from', class: 'col-form-label col-sm-2'
- .col-sm-10.create-from
+ .col-sm-12.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
@@ -27,15 +26,14 @@
.form-text.text-muted
= s_('TagsPage|Existing branch name, tag, or commit SHA')
.form-group.row
- = label_tag :message, nil, class: 'col-form-label col-sm-2'
- .col-sm-10
+ .col-sm-12
+ = label_tag :message, nil
= text_area_tag :message, @message, required: false, class: 'form-control', rows: 5, data: { qa_selector: "tag_message_field" }
.form-text.text-muted
= tag_description_help_text
- %hr
.form-group.row
- = label_tag :release_description, s_('TagsPage|Release notes'), class: 'col-form-label col-sm-2'
- .col-sm-10
+ .col-sm-12
+ = label_tag :release_description, s_('TagsPage|Release notes'), class: 'gl-mb-0'
.form-text.mb-3
- link_start = '<a href="%{url}" rel="noopener noreferrer" target="_blank">'.html_safe
- releases_page_path = project_releases_path(@project)
@@ -49,7 +47,7 @@
= render layout: 'shared/md_preview', locals: { url: preview_markdown_path(@project), referenced_users: true } do
= render 'shared/zen', attr: :release_description, classes: 'note-textarea', placeholder: s_('TagsPage|Write your release notes or drag files here…'), current_text: @release_description, qa_selector: 'release_notes_field'
= render 'shared/notes/hints'
- .form-actions.gl-display-flex
+ .gl-display-flex
= render Pajamas::ButtonComponent.new(variant: :confirm, button_options: { class: 'gl-mr-3', data: { qa_selector: "create_tag_button" }, type: 'submit' }) do
= s_('TagsPage|Create tag')
= render Pajamas::ButtonComponent.new(href: project_tags_path(@project)) do
diff --git a/app/views/projects/triggers/_form.html.haml b/app/views/projects/triggers/_form.html.haml
index d24cfd61052..9043b8e60fc 100644
--- a/app/views/projects/triggers/_form.html.haml
+++ b/app/views/projects/triggers/_form.html.haml
@@ -1,5 +1,5 @@
= form_for [@project, @trigger], html: { class: 'gl-show-field-errors' } do |f|
- = form_errors(@trigger, pajamas_alert: true)
+ = form_errors(@trigger)
- if @trigger.token
.form-group
diff --git a/app/views/projects/usage_quotas/index.html.haml b/app/views/projects/usage_quotas/index.html.haml
index 3de9bce14d4..5e2217d3c9f 100644
--- a/app/views/projects/usage_quotas/index.html.haml
+++ b/app/views/projects/usage_quotas/index.html.haml
@@ -1,6 +1,6 @@
- page_title s_("UsageQuota|Usage")
-= render_if_exists 'namespaces/free_user_cap/projects/usage_quota_limitations_banner'
+= render_if_exists 'shared/ultimate_feature_removal_banner', project: @project
= render Pajamas::AlertComponent.new(title: _('Repository usage recalculation started'),
variant: :info,
diff --git a/app/views/search/_results_status.html.haml b/app/views/search/_results_status.html.haml
index dcfab046514..ef5e3e83103 100644
--- a/app/views/search/_results_status.html.haml
+++ b/app/views/search/_results_status.html.haml
@@ -13,7 +13,7 @@
- if search_service.scope == 'blobs'
= _("in")
.mx-md-1
- = render partial: "shared/ref_switcher", locals: { ref: repository_ref(search_service.project), form_path: request.fullpath, field_name: 'repository_ref' }
+ #js-blob-ref-switcher{ data: { "project-id" => search_service.project.id, "ref" => repository_ref(search_service.project), "field-name": "repository_ref" } }
= s_('SearchCodeResults|of %{link_to_project}').html_safe % { link_to_project: link_to_project }
- else
= _("in project %{link_to_project}").html_safe % { link_to_project: link_to_project }
diff --git a/app/views/shared/_email_with_badge.html.haml b/app/views/shared/_email_with_badge.html.haml
index 5d837657943..5013d8e439a 100644
--- a/app/views/shared/_email_with_badge.html.haml
+++ b/app/views/shared/_email_with_badge.html.haml
@@ -1,5 +1,6 @@
- variant = verified ? :success : :danger
- text = verified ? _('Verified') : _('Unverified')
-= email
-= gl_badge_tag text, { variant: variant }, { class: 'gl-ml-3' }
+%span.gl-mr-3
+ = email
+= gl_badge_tag text, { variant: variant }
diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml
index 130e73a069f..89be816fc76 100644
--- a/app/views/shared/_file_highlight.html.haml
+++ b/app/views/shared/_file_highlight.html.haml
@@ -1,6 +1,7 @@
#blob-content.file-content.code.js-syntax-highlight
- offset = defined?(first_line_number) ? first_line_number : 1
- - blame_path = project_blame_path(@project, tree_join(@ref, blob.path))
+ - 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?
- link = blob_link if defined?(blob_link)
diff --git a/app/views/shared/_integration_settings.html.haml b/app/views/shared/_integration_settings.html.haml
index d58be0f0f4a..84710b2ecc7 100644
--- a/app/views/shared/_integration_settings.html.haml
+++ b/app/views/shared/_integration_settings.html.haml
@@ -1,4 +1,4 @@
-= form_errors(integration, pajamas_alert: true)
+= form_errors(integration)
%div{ data: { testid: "integration-settings-form" } }
- if @default_integration
diff --git a/app/views/shared/_md_preview.html.haml b/app/views/shared/_md_preview.html.haml
index a49a0667d84..7314a7ddadc 100644
--- a/app/views/shared/_md_preview.html.haml
+++ b/app/views/shared/_md_preview.html.haml
@@ -11,13 +11,13 @@
.md-header
= gl_tabs_nav({ class: 'clearfix nav-links'}) do
%li.md-header-tab.active
- %button.js-md-write-button
+ %button.js-md-write-button{ class: 'gl-py-3!' }
= _("Write")
%li.md-header-tab
- %button.js-md-preview-button
+ %button.js-md-preview-button{ class: 'gl-py-3!' }
= _("Preview")
- %li.md-header-toolbar.active
+ %li.md-header-toolbar.active.gl-py-2
= render 'shared/blob/markdown_buttons', show_fullscreen_button: true
.md-write-holder
diff --git a/app/views/shared/access_tokens/_created_container.html.haml b/app/views/shared/access_tokens/_created_container.html.haml
index c5a18d98b89..c0aaa46e761 100644
--- a/app/views/shared/access_tokens/_created_container.html.haml
+++ b/app/views/shared/access_tokens/_created_container.html.haml
@@ -3,7 +3,7 @@
= _('Your new %{type}') % { type: type }
.form-group
.input-group
- = text_field_tag 'created-personal-access-token', new_token_value, readonly: true, class: 'qa-created-access-token form-control js-select-on-focus', 'aria-describedby' => 'created-token-help-block'
+ = text_field_tag 'created-personal-access-token', new_token_value, readonly: true, class: 'form-control js-select-on-focus', data: { qa_selector: 'created_access_token_field' }, 'aria-describedby' => 'created-token-help-block'
%span.input-group-append
= clipboard_button(text: new_token_value, title: _('Copy %{type}') % { type: type }, placement: 'left', class: 'input-group-text btn-default btn-clipboard')
%span#created-token-help-block.form-text.text-muted.text-danger
diff --git a/app/views/shared/access_tokens/_form.html.haml b/app/views/shared/access_tokens/_form.html.haml
index dd4d2ab46c1..0c88ac66b8b 100644
--- a/app/views/shared/access_tokens/_form.html.haml
+++ b/app/views/shared/access_tokens/_form.html.haml
@@ -13,7 +13,7 @@
= gitlab_ui_form_for token, as: prefix, url: path, method: :post, html: { id: 'js-new-access-token-form', class: 'js-requires-input' }, remote: ajax do |f|
- = form_errors(token, pajamas_alert: true)
+ = form_errors(token)
.row
.form-group.col
diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml
index c070baf02b1..c3835386d5a 100644
--- a/app/views/shared/boards/_show.html.haml
+++ b/app/views/shared/boards/_show.html.haml
@@ -1,7 +1,7 @@
- board = local_assigns.fetch(:board, nil)
- @no_breadcrumb_container = true
- @no_container = true
-- @content_wrapper_class = "#{@content_wrapper_class} gl-relative"
+- @content_wrapper_class = "#{@content_wrapper_class} gl-relative gl-pb-0"
- @content_class = "issue-boards-content js-focus-mode-board"
- is_epic_board = board.to_type == "EpicBoard"
- if is_epic_board
diff --git a/app/views/shared/deploy_keys/_form.html.haml b/app/views/shared/deploy_keys/_form.html.haml
index 38985319ca5..93f31629ca7 100644
--- a/app/views/shared/deploy_keys/_form.html.haml
+++ b/app/views/shared/deploy_keys/_form.html.haml
@@ -2,7 +2,7 @@
- deploy_key = local_assigns.fetch(:deploy_key)
- deploy_keys_project = deploy_key.deploy_keys_project_for(@project)
-= form_errors(deploy_key, pajamas_alert: true)
+= form_errors(deploy_key)
.form-group
= form.label :title, class: 'col-form-label col-sm-2'
diff --git a/app/views/shared/deploy_keys/_project_group_form.html.haml b/app/views/shared/deploy_keys/_project_group_form.html.haml
index 4bedce71c0f..d76ef8feb62 100644
--- a/app/views/shared/deploy_keys/_project_group_form.html.haml
+++ b/app/views/shared/deploy_keys/_project_group_form.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for [@project.namespace, @project, @deploy_keys.new_key], url: namespace_project_deploy_keys_path, html: { class: "js-requires-input container" } do |f|
- = form_errors(@deploy_keys.new_key, pajamas_alert: true)
+ = form_errors(@deploy_keys.new_key)
.form-group.row
= f.label :title, class: "label-bold"
= f.text_field :title, class: 'form-control gl-form-input', required: true, data: { qa_selector: 'deploy_key_title_field' }
diff --git a/app/views/shared/deploy_tokens/_index.html.haml b/app/views/shared/deploy_tokens/_index.html.haml
index aa4a3deaac4..79bf35e2726 100644
--- a/app/views/shared/deploy_tokens/_index.html.haml
+++ b/app/views/shared/deploy_tokens/_index.html.haml
@@ -1,4 +1,4 @@
-- expanded = expand_deploy_tokens_section?(@new_deploy_token)
+- expanded = expand_deploy_tokens_section?(@new_deploy_token, @created_deploy_token)
%section.settings.no-animate#js-deploy-tokens{ class: ('expanded' if expanded), data: { qa_selector: 'deploy_tokens_settings_content' } }
.settings-header
@@ -8,11 +8,10 @@
%p
= description
.settings-content
- - if @new_deploy_token.persisted?
- = render 'shared/deploy_tokens/new_deploy_token', deploy_token: @new_deploy_token
+ - if @created_deploy_token
+ = render 'shared/deploy_tokens/new_deploy_token', deploy_token: @created_deploy_token
%h5.gl-mt-0
= s_('DeployTokens|New deploy token')
= render 'shared/deploy_tokens/form', group_or_project: group_or_project, token: @new_deploy_token, presenter: @deploy_tokens
%hr
= render 'shared/deploy_tokens/table', group_or_project: group_or_project, active_tokens: @deploy_tokens
-
diff --git a/app/views/shared/doorkeeper/applications/_form.html.haml b/app/views/shared/doorkeeper/applications/_form.html.haml
index 9810754f52b..b40e2630011 100644
--- a/app/views/shared/doorkeeper/applications/_form.html.haml
+++ b/app/views/shared/doorkeeper/applications/_form.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for @application, url: url, html: { role: 'form', class: 'doorkeeper-app-form' } do |f|
- = form_errors(@application, pajamas_alert: true)
+ = form_errors(@application)
.form-group
= f.label :name, class: 'label-bold'
diff --git a/app/views/shared/doorkeeper/applications/_show.html.haml b/app/views/shared/doorkeeper/applications/_show.html.haml
index f533b5b5a4d..562b1aee4ca 100644
--- a/app/views/shared/doorkeeper/applications/_show.html.haml
+++ b/app/views/shared/doorkeeper/applications/_show.html.haml
@@ -15,7 +15,14 @@
%td
= _('Secret')
%td
- = clipboard_button(clipboard_text: @application.secret, button_text: _('Copy'), title: _("Copy secret"), class: "btn btn-default btn-md gl-button")
+ - if Feature.enabled?('hash_oauth_secrets')
+ - if @application.plaintext_secret
+ = clipboard_button(clipboard_text: @application.plaintext_secret, button_text: _('Copy'), title: _("Copy secret"), class: "btn btn-default btn-md gl-button")
+ %span= _('This is the only time the secret is accessible. Copy the secret and store it securely.')
+ - else
+ = _('The secret is only available when you first create the application.')
+ - else
+ = clipboard_button(clipboard_text: @application.secret, button_text: _('Copy'), title: _("Copy secret"), class: "btn btn-default btn-md gl-button")
%tr
%td
= _('Callback URL')
diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml
index 164773f9b60..f8304d5e44e 100644
--- a/app/views/shared/groups/_group.html.haml
+++ b/app/views/shared/groups/_group.html.haml
@@ -1,7 +1,7 @@
- user = local_assigns.fetch(:user, current_user)
- access = user&.max_member_access_for_group(group.id)
-%li.group-row.py-3.gl-align-items-center{ class: "gl-display-flex!#{' no-description' if group.description.blank?}" }
+%li.group-row.py-3.gl-align-items-center{ class: "gl-display-flex!" }
.avatar-container.rect-avatar.s40.gl-flex-shrink-0
= link_to group do
= group_icon(group, class: "avatar s40")
diff --git a/app/views/shared/groups/_visibility_level.html.haml b/app/views/shared/groups/_visibility_level.html.haml
deleted file mode 100644
index 1a13de9b76a..00000000000
--- a/app/views/shared/groups/_visibility_level.html.haml
+++ /dev/null
@@ -1,3 +0,0 @@
-= f.label :visibility_level, class: 'label-bold' do
- = _('Visibility level')
-.js-visibility-level-dropdown{ data: { visibility_level_options: visibility_level_options(@group).to_json, default_level: f.object.visibility_level } }
diff --git a/app/views/shared/hook_logs/_recent_deliveries_table.html.haml b/app/views/shared/hook_logs/_recent_deliveries_table.html.haml
index 3c91c2f6ab4..500eb29fa93 100644
--- a/app/views/shared/hook_logs/_recent_deliveries_table.html.haml
+++ b/app/views/shared/hook_logs/_recent_deliveries_table.html.haml
@@ -23,7 +23,7 @@
- if hook_logs.present?
- = paginate hook_logs, theme: 'gitlab'
+ = paginate_without_count hook_logs
- else
.gl-text-center.gl-mt-7
%h4= _('No webhook events')
diff --git a/app/views/shared/issuable/_feed_buttons.html.haml b/app/views/shared/issuable/_feed_buttons.html.haml
index f0e4b915ac8..69ff477d415 100644
--- a/app/views/shared/issuable/_feed_buttons.html.haml
+++ b/app/views/shared/issuable/_feed_buttons.html.haml
@@ -1,7 +1,7 @@
- show_calendar_button = local_assigns.fetch(:show_calendar_button, true)
= link_to safe_params.merge(rss_url_options), class: 'btn gl-button btn-default btn-icon has-tooltip', data: { container: 'body', testid: 'rss-feed-link' }, title: _('Subscribe to RSS feed') , 'aria-label': _('Subscribe to RSS feed') do
- = sprite_icon('rss', css_class: 'qa-rss-icon')
+ = sprite_icon('rss')
- if show_calendar_button
= link_to safe_params.merge(calendar_url_options), class: 'btn gl-button btn-default btn-icon has-tooltip', data: { container: 'body' }, title: _('Subscribe to calendar'), 'aria-label': _('Subscribe to calendar') do
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index ae8b266c092..53eb6f4c63b 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -3,7 +3,7 @@
- project = @target_project || @project
- presenter = local_assigns.fetch(:presenter, nil)
-= form_errors(issuable, pajamas_alert: true)
+= form_errors(issuable)
- if @conflict
= render Pajamas::AlertComponent.new(variant: :danger,
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 6da094924a0..f2ce0676a9a 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -20,13 +20,7 @@
.js-issuable-todo{ 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|
- - if signed_in && moved_sidebar_enabled
- .block.to-do
- .title.hide-collapsed.gl-font-weight-bold.gl-display-flex.gl-align-items-center.gl-justify-content-space-between.gl-mt-2{ class: 'gl-mb-0!' }
- = _('To-Do')
- .js-issuable-todo{ data: { project_path: issuable_sidebar[:project_full_path], iid: issuable_sidebar[:iid], id: issuable_sidebar[:id] } }
-
- .block.assignee{ class: "#{'gl-mt-3' if !signed_in && moved_sidebar_enabled}", data: { qa_selector: 'assignee_block_container' } }
+ .block.assignee.qa-assignee-block{ class: "#{'gl-mt-3' if !signed_in && moved_sidebar_enabled}", data: { qa_selector: 'assignee_block_container' } }
= render "shared/issuable/sidebar_assignees", issuable_sidebar: issuable_sidebar, assignees: assignees, signed_in: signed_in
- if issuable_sidebar[:supports_severity]
@@ -88,7 +82,8 @@
.js-sidebar-participants-entry-point
.block.with-sub-blocks
- #js-reference-entry-point
+ - if !moved_sidebar_enabled
+ #js-reference-entry-point
- if issuable_type == 'merge_request' && !moved_sidebar_enabled
.sub-block.js-sidebar-source-branch
.sidebar-collapsed-icon.js-dont-change-state
diff --git a/app/views/shared/issuable/_sidebar_assignees.html.haml b/app/views/shared/issuable/_sidebar_assignees.html.haml
index 2fd4c598580..e9b04579808 100644
--- a/app/views/shared/issuable/_sidebar_assignees.html.haml
+++ b/app/views/shared/issuable/_sidebar_assignees.html.haml
@@ -6,7 +6,7 @@
max_assignees: dropdown_options[:data][:"max-select"],
directly_invite_members: can_admin_project_member?(@project) } }
.title.hide-collapsed
- = _('Assignee')
+ = s_('Label|Assignee')
= gl_loading_icon(inline: true)
.js-sidebar-assignee-data.selectbox.hide-collapsed
diff --git a/app/views/shared/issuable/_sidebar_reviewers.html.haml b/app/views/shared/issuable/_sidebar_reviewers.html.haml
index cd976b88304..3f78f29ea24 100644
--- a/app/views/shared/issuable/_sidebar_reviewers.html.haml
+++ b/app/views/shared/issuable/_sidebar_reviewers.html.haml
@@ -2,7 +2,7 @@
#js-vue-sidebar-reviewers{ data: { field: issuable_type, signed_in: signed_in } }
.title.hide-collapsed
- = _('Reviewer')
+ = _('Reviewers')
= gl_loading_icon(inline: true)
.selectbox.hide-collapsed
diff --git a/app/views/shared/issuable/form/_branch_chooser.html.haml b/app/views/shared/issuable/form/_branch_chooser.html.haml
index 8ab002f755f..634e927f891 100644
--- a/app/views/shared/issuable/form/_branch_chooser.html.haml
+++ b/app/views/shared/issuable/form/_branch_chooser.html.haml
@@ -31,10 +31,14 @@
- if issuable.merged?
%code= target_title
- unless issuable.new_record? || issuable.merged?
- %span.dropdown.gl-ml-2.d-inline-block
- = form.hidden_field(:target_branch,
- { class: 'target_branch js-target-branch-select ref-name mw-xl',
- data: { placeholder: _('Select branch'), endpoint: refs_project_path(@project, sort: 'updated_desc', find: 'branches') }})
+ .merge-request-select.dropdown.gl-w-auto
+ = form.hidden_field :target_branch
+ = dropdown_toggle form.object.target_branch.presence || _("Select branch"), { toggle: "dropdown", 'field-name': "#{form.object_name}[target_branch]", 'refs-url': refs_project_path(@project, sort: 'updated_desc', find: 'branches'), selected: form.object.target_branch, default_text: _("Select branch") }, { toggle_class: "js-compare-dropdown js-target-branch monospace" }
+ .dropdown-menu.dropdown-menu-selectable.js-target-branch-dropdown.target_branch.ref-name.git-revision-dropdown
+ = dropdown_title(_("Select branch"))
+ = dropdown_filter(_("Search branches"))
+ = dropdown_content
+ = dropdown_loading
- if source_level < target_level
= render Pajamas::AlertComponent.new(variant: :warning, dismissible: false, alert_options: { class: 'gl-mb-4' }) do |c|
diff --git a/app/views/shared/issuable/form/_merge_params.html.haml b/app/views/shared/issuable/form/_merge_params.html.haml
index 5831460d59a..09086d3aa82 100644
--- a/app/views/shared/issuable/form/_merge_params.html.haml
+++ b/app/views/shared/issuable/form/_merge_params.html.haml
@@ -11,9 +11,9 @@
- if issuable.can_remove_source_branch?(current_user)
.form-check.gl-mb-3
= hidden_field_tag 'merge_request[force_remove_source_branch]', '0', id: nil
- = check_box_tag 'merge_request[force_remove_source_branch]', '1', issuable.force_remove_source_branch?, class: 'form-check-input'
+ = check_box_tag 'merge_request[force_remove_source_branch]', '1', issuable.force_remove_source_branch?, class: 'form-check-input js-form-update'
= label_tag 'merge_request[force_remove_source_branch]', class: 'form-check-label' do
- Delete source branch when merge request is accepted.
+ = _("Delete source branch when merge request is accepted.")
- if !project.squash_never?
.form-check
- if project.squash_always?
@@ -21,9 +21,9 @@
= check_box_tag 'merge_request[squash]', '1', project.squash_enabled_by_default?, class: 'form-check-input', disabled: 'true'
- else
= hidden_field_tag 'merge_request[squash]', '0', id: nil
- = check_box_tag 'merge_request[squash]', '1', issuable_squash_option?(issuable, project), class: 'form-check-input'
+ = check_box_tag 'merge_request[squash]', '1', issuable_squash_option?(issuable, project), class: 'form-check-input js-form-update'
= label_tag 'merge_request[squash]', class: 'form-check-label' do
- Squash commits when merge request is accepted.
+ = _("Squash commits when merge request is accepted.")
= link_to sprite_icon('question-o'), help_page_path('user/project/merge_requests/squash_and_merge'), target: '_blank', rel: 'noopener noreferrer'
- if project.squash_always?
.gl-text-gray-400
diff --git a/app/views/shared/issuable/form/_metadata_issuable_assignee.html.haml b/app/views/shared/issuable/form/_metadata_issuable_assignee.html.haml
index efecffbcc2e..bd9afc3ce69 100644
--- a/app/views/shared/issuable/form/_metadata_issuable_assignee.html.haml
+++ b/app/views/shared/issuable/form/_metadata_issuable_assignee.html.haml
@@ -1,4 +1,4 @@
-= form.label :assignee_id, issuable.allows_multiple_assignees? ? _('Assignees') : _('Assignee'), class: "col-12"
+= form.label :assignee_id, issuable.allows_multiple_assignees? ? _('Assignees') : s_('SearchToken|Assignee'), class: "col-12"
.col-12
.issuable-form-select-holder.selectbox
- issuable.assignees.each do |assignee|
diff --git a/app/views/shared/issue_type/_emoji_block.html.haml b/app/views/shared/issue_type/_emoji_block.html.haml
index d2c851a4e49..61e28f18d3b 100644
--- a/app/views/shared/issue_type/_emoji_block.html.haml
+++ b/app/views/shared/issue_type/_emoji_block.html.haml
@@ -4,8 +4,7 @@
.row.gl-m-0.gl-justify-content-space-between
.js-noteable-awards
= render 'award_emoji/awards_block', awardable: issuable, inline: true, api_awards_path: api_awards_path
- .new-branch-col.gl-my-2.gl-font-size-0
+ .new-branch-col.gl-display-flex.gl-my-2.gl-font-size-0.gl-gap-3
= render_if_exists "projects/issues/timeline_toggle", issuable: issuable
- #js-vue-sort-issue-discussions
#js-vue-discussion-filter{ data: { default_filter: current_user&.notes_filter_for(issuable), notes_filters: UserPreference.notes_filters.to_json } }
= render 'new_branch' if show_new_branch_button?
diff --git a/app/views/shared/labels/_form.html.haml b/app/views/shared/labels/_form.html.haml
index cf8bd23b153..e6d6d0998dc 100644
--- a/app/views/shared/labels/_form.html.haml
+++ b/app/views/shared/labels/_form.html.haml
@@ -1,5 +1,5 @@
= form_for @label, as: :label, url: url, html: { class: 'label-form js-quick-submit js-requires-input' } do |f|
- = form_errors(@label, pajamas_alert: true)
+ = form_errors(@label)
.form-group.row
.col-12
diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml
index 59d1537aa2b..01548325c83 100644
--- a/app/views/shared/milestones/_milestone.html.haml
+++ b/app/views/shared/milestones/_milestone.html.haml
@@ -43,7 +43,8 @@
- if milestone.merge_requests_enabled?
&middot;
= link_to pluralize(milestone.total_merge_requests_count, _('Merge request')), merge_requests_path
- .float-lg-right.light #{milestone.percent_complete}% complete
+ .float-lg-right.light
+ = format(s_('Milestone|%{percentage}%{percent} complete'), percentage: milestone.percent_complete, percent: '%')
.col-md-2
.milestone-actions.d-flex.justify-content-sm-start.justify-content-md-end
- if @project # if in milestones list on project level
diff --git a/app/views/shared/notes/_hints.html.haml b/app/views/shared/notes/_hints.html.haml
index 44740db5a00..fb000b9aab1 100644
--- a/app/views/shared/notes/_hints.html.haml
+++ b/app/views/shared/notes/_hints.html.haml
@@ -9,7 +9,7 @@
- else
= html_escape(s_('MarkdownToolbar|Supports %{markdownDocsLinkStart}Markdown%{markdownDocsLinkEnd}')) % { markdownDocsLinkStart: markdownLinkStart, markdownDocsLinkEnd: '</a>'.html_safe }
- if supports_file_upload
- %span.uploading-container
+ %span.uploading-container.gl-line-height-32
%span.uploading-progress-container.hide
= sprite_icon('paperclip', css_class: 'gl-icon gl-vertical-align-text-bottom')
%span.attaching-file-message
diff --git a/app/views/shared/projects/_search_form.html.haml b/app/views/shared/projects/_search_form.html.haml
index 51a5c9dd38f..a5170b199e8 100644
--- a/app/views/shared/projects/_search_form.html.haml
+++ b/app/views/shared/projects/_search_form.html.haml
@@ -1,5 +1,5 @@
- form_field_classes = local_assigns[:admin_view] || !Feature.enabled?(:project_list_filter_bar) ? 'input-short js-projects-list-filter' : ''
-- placeholder = local_assigns[:search_form_placeholder] ? search_form_placeholder : 'Filter by name...'
+- 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],
diff --git a/app/views/shared/runners/_form.html.haml b/app/views/shared/runners/_form.html.haml
index e0079a95cec..024b06fe97a 100644
--- a/app/views/shared/runners/_form.html.haml
+++ b/app/views/shared/runners/_form.html.haml
@@ -1,5 +1,5 @@
= gitlab_ui_form_for runner, url: runner_form_url do |f|
- = form_errors(runner, pajamas_alert: true)
+ = form_errors(runner)
.form-group.row
= label :active, _("Active"), class: 'col-form-label col-sm-2'
.col-sm-10
diff --git a/app/views/shared/web_hooks/_form.html.haml b/app/views/shared/web_hooks/_form.html.haml
index fe68244f1da..afe72767b9a 100644
--- a/app/views/shared/web_hooks/_form.html.haml
+++ b/app/views/shared/web_hooks/_form.html.haml
@@ -1,4 +1,4 @@
-= form_errors(hook, pajamas_alert: true)
+= form_errors(hook)
.form-group
= form.label :url, s_('Webhooks|URL'), class: 'label-bold'
diff --git a/app/views/shared/web_hooks/_index.html.haml b/app/views/shared/web_hooks/_index.html.haml
index 5d07b0f95ab..5ec82ad6702 100644
--- a/app/views/shared/web_hooks/_index.html.haml
+++ b/app/views/shared/web_hooks/_index.html.haml
@@ -1,5 +1,5 @@
%hr
-.card
+.card#webhooks-index
.card-header
%h5
= hook_class.underscore.humanize.titleize.pluralize
diff --git a/app/views/shared/web_hooks/_web_hook_disabled_alert.html.haml b/app/views/shared/web_hooks/_web_hook_disabled_alert.html.haml
new file mode 100644
index 00000000000..d9155b397b8
--- /dev/null
+++ b/app/views/shared/web_hooks/_web_hook_disabled_alert.html.haml
@@ -0,0 +1,13 @@
+- return unless show_project_hook_failed_callout?(project: @project)
+
+- content_for :after_flash_content do
+ = render Pajamas::AlertComponent.new(variant: :danger,
+ title: s_('Webhooks|Webhook disabled'),
+ alert_options: { class: 'gl-my-4 js-web-hook-disabled-callout',
+ data: { feature_id: Users::CalloutsHelper::WEB_HOOK_DISABLED, dismiss_endpoint: project_callouts_path, project_id: @project.id, defer_links: 'true'} }) do |c|
+ = c.body do
+ = s_('Webhooks|A webhook in this project was automatically disabled after being retried multiple times.')
+ = succeed '.' do
+ = link_to _('Learn more'), help_page_path('user/project/integrations/webhooks', anchor: 'troubleshoot-webhooks'), target: '_blank', rel: 'noopener noreferrer'
+ = c.actions do
+ = link_to s_('Webhooks|Go to webhooks'), project_hooks_path(@project, anchor: 'webhooks-index'), class: 'btn gl-alert-action btn-confirm gl-button'
diff --git a/app/views/shared/wikis/_form.html.haml b/app/views/shared/wikis/_form.html.haml
index 0d5e59919cb..34bedbd928a 100644
--- a/app/views/shared/wikis/_form.html.haml
+++ b/app/views/shared/wikis/_form.html.haml
@@ -1,6 +1,6 @@
- page_info = { last_commit_sha: @page.last_commit_sha, persisted: @page.persisted?, title: @page.title, content: @page.content || '', format: @page.format.to_s, uploads_path: uploads_path, path: wiki_page_path(@wiki, @page), wiki_path: wiki_path(@wiki), help_path: help_page_path('user/project/wiki/index'), markdown_help_path: help_page_path('user/markdown'), markdown_preview_path: wiki_page_path(@wiki, @page, action: :preview_markdown), create_path: wiki_path(@wiki, action: :create) }
.gl-mt-3
- = form_errors(@page, truncate: :title, pajamas_alert: true)
+ = form_errors(@page, truncate: :title)
#js-wiki-form{ data: { page_info: page_info.to_json, format_options: wiki_markup_hash_by_name_id.to_json } }
diff --git a/app/views/shared/wikis/_wiki_content.html.haml b/app/views/shared/wikis/_wiki_content.html.haml
index 42e8037bb0f..780e4c4746d 100644
--- a/app/views/shared/wikis/_wiki_content.html.haml
+++ b/app/views/shared/wikis/_wiki_content.html.haml
@@ -1,2 +1,2 @@
-.js-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki_page_content', tracking_context: wiki_page_tracking_context(@page).to_json } }
+.js-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki-page-content', tracking_context: wiki_page_tracking_context(@page).to_json } }
= render_wiki_content(@page)
diff --git a/app/views/shared/wikis/git_error.html.haml b/app/views/shared/wikis/git_error.html.haml
index dab3b940b9a..12eddb4a61e 100644
--- a/app/views/shared/wikis/git_error.html.haml
+++ b/app/views/shared/wikis/git_error.html.haml
@@ -9,6 +9,6 @@
.gl-mt-5.gl-mb-3
.gl-display-flex.gl-justify-content-space-between
%h2.gl-mt-0.gl-mb-5{ data: { qa_selector: 'wiki_page_title', testid: 'wiki_page_title' } }= @page ? @page.human_title : _('Failed to retrieve page')
- .js-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki_page_content' } }
+ .js-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki-page-content' } }
= _('The page could not be displayed because it timed out.')
= html_escape(_('You can view the source or %{linkStart}%{cloneIcon} clone the repository%{linkEnd}')) % { linkStart: "<a href=\"#{git_access_url}\">".html_safe, linkEnd: '</a>'.html_safe, cloneIcon: sprite_icon('download', css_class: 'gl-mr-2').html_safe }
diff --git a/app/views/shared/wikis/show.html.haml b/app/views/shared/wikis/show.html.haml
index 6591e8fae7b..3841113231c 100644
--- a/app/views/shared/wikis/show.html.haml
+++ b/app/views/shared/wikis/show.html.haml
@@ -27,6 +27,6 @@
- if can?(current_user, :create_wiki, @wiki.container) && @page.latest? && @valid_encoding
= link_to sprite_icon('pencil', css_class: 'gl-icon'), wiki_page_path(@wiki, @page, action: :edit), title: 'Edit', role: "button", class: 'btn gl-button btn-icon btn-default js-wiki-edit', data: { qa_selector: 'edit_page_button', testid: 'wiki_edit_button' }
- .js-async-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki_page_content', tracking_context: wiki_page_tracking_context(@page).to_json, get_wiki_content_url: wiki_page_render_api_endpoint(@page) } }
+ .js-async-wiki-page-content.md.gl-pt-2{ data: { qa_selector: 'wiki_page_content', testid: 'wiki-page-content', tracking_context: wiki_page_tracking_context(@page).to_json, get_wiki_content_url: wiki_page_render_api_endpoint(@page) } }
= render 'shared/wikis/sidebar'
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 25070138128..952023b3745 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -36,7 +36,7 @@
- if can?(current_user, :read_user_profile, @user)
= link_to user_path(@user, rss_url_options), class: link_classes + 'btn gl-button btn-default btn-icon has-tooltip',
title: s_('UserProfile|Subscribe'), data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
- = sprite_icon('rss', css_class: 'qa-rss-icon')
+ = sprite_icon('rss')
- if current_user && current_user.admin?
= link_to [:admin, @user], class: link_classes + 'btn gl-button btn-default btn-icon', title: s_('UserProfile|View user in admin area'),
data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 8bba5e36b52..9b282340d0a 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -786,6 +786,15 @@
:weight: 1
:idempotent: false
:tags: []
+- :name: cronjob:users_migrate_records_to_ghost_user_in_batches
+ :worker_name: Users::MigrateRecordsToGhostUserInBatchesWorker
+ :feature_category: :users
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: cronjob:x509_issuer_crl_check
:worker_name: X509IssuerCrlCheckWorker
:feature_category: :source_code_management
@@ -1056,6 +1065,15 @@
:weight: 1
:idempotent: false
:tags: []
+- :name: github_importer:github_import_import_protected_branch
+ :worker_name: Gitlab::GithubImport::ImportProtectedBranchWorker
+ :feature_category: :importers
+ :has_external_dependencies: true
+ :urgency: :low
+ :resource_boundary: :cpu
+ :weight: 1
+ :idempotent: false
+ :tags: []
- :name: github_importer:github_import_import_pull_request
:worker_name: Gitlab::GithubImport::ImportPullRequestWorker
:feature_category: :importers
@@ -1083,6 +1101,15 @@
:weight: 1
:idempotent: false
:tags: []
+- :name: github_importer:github_import_import_release_attachments
+ :worker_name: Gitlab::GithubImport::ImportReleaseAttachmentsWorker
+ :feature_category: :importers
+ :has_external_dependencies: true
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: false
+ :tags: []
- :name: github_importer:github_import_refresh_import_jid
:worker_name: Gitlab::GithubImport::RefreshImportJidWorker
:feature_category: :importers
@@ -1101,6 +1128,15 @@
:weight: 1
:idempotent: false
:tags: []
+- :name: github_importer:github_import_stage_import_attachments
+ :worker_name: Gitlab::GithubImport::Stage::ImportAttachmentsWorker
+ :feature_category: :importers
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: false
+ :tags: []
- :name: github_importer:github_import_stage_import_base_data
:worker_name: Gitlab::GithubImport::Stage::ImportBaseDataWorker
:feature_category: :importers
@@ -1146,6 +1182,15 @@
:weight: 1
:idempotent: false
:tags: []
+- :name: github_importer:github_import_stage_import_protected_branches
+ :worker_name: Gitlab::GithubImport::Stage::ImportProtectedBranchesWorker
+ :feature_category: :importers
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: false
+ :tags: []
- :name: github_importer:github_import_stage_import_pull_requests
:worker_name: Gitlab::GithubImport::Stage::ImportPullRequestsWorker
:feature_category: :importers
@@ -1578,6 +1623,15 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: pipeline_background:ci_job_artifacts_track_artifact_report
+ :worker_name: Ci::JobArtifacts::TrackArtifactReportWorker
+ :feature_category: :code_testing
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: pipeline_background:ci_pending_builds_update_group
:worker_name: Ci::PendingBuilds::UpdateGroupWorker
:feature_category: :continuous_integration
@@ -2379,6 +2433,15 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: google_cloud_fetch_google_ip_list
+ :worker_name: GoogleCloud::FetchGoogleIpListWorker
+ :feature_category: :build_artifacts
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: group_destroy
:worker_name: GroupDestroyWorker
:feature_category: :subgroups
@@ -2415,6 +2478,15 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: groups_update_two_factor_requirement_for_members
+ :worker_name: Groups::UpdateTwoFactorRequirementForMembersWorker
+ :feature_category: :authentication_and_authorization
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: import_issues_csv
:worker_name: ImportIssuesCsvWorker
:feature_category: :team_planning
@@ -2496,6 +2568,15 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: issues_close
+ :worker_name: Issues::CloseWorker
+ :feature_category: :source_code_management
+ :has_external_dependencies: false
+ :urgency: :high
+ :resource_boundary: :unknown
+ :weight: 2
+ :idempotent: true
+ :tags: []
- :name: issues_placement
:worker_name: Issues::PlacementWorker
:feature_category: :team_planning
diff --git a/app/workers/analytics/usage_trends/counter_job_worker.rb b/app/workers/analytics/usage_trends/counter_job_worker.rb
index b3a8f7dd3c2..e6de623f784 100644
--- a/app/workers/analytics/usage_trends/counter_job_worker.rb
+++ b/app/workers/analytics/usage_trends/counter_job_worker.rb
@@ -3,6 +3,8 @@
module Analytics
module UsageTrends
class CounterJobWorker
+ TIMEOUT = 250.seconds
+
extend ::Gitlab::Utils::Override
include ApplicationWorker
@@ -15,24 +17,27 @@ module Analytics
idempotent!
- def perform(measurement_identifier, min_id, max_id, recorded_at)
+ def perform(measurement_identifier, min_id, max_id, recorded_at, partial_results = nil)
query_scope = ::Analytics::UsageTrends::Measurement.identifier_query_mapping[measurement_identifier].call
- count = if min_id.nil? || max_id.nil? # table is empty
- 0
- else
- counter(query_scope, min_id, max_id)
- end
+ result = counter(query_scope, min_id, max_id, partial_results)
+
+ # If the batch counter timed out, schedule a job to continue counting later
+ if result[:status] == :timeout
+ return self.class.perform_async(measurement_identifier, result[:continue_from], max_id, recorded_at, result[:partial_results])
+ end
- return if count == Gitlab::Database::BatchCounter::FALLBACK
+ return if result[:status] != :completed
- UsageTrends::Measurement.insert_all([{ recorded_at: recorded_at, count: count, identifier: measurement_identifier }])
+ UsageTrends::Measurement.insert_all([{ recorded_at: recorded_at, count: result[:count], identifier: measurement_identifier }])
end
private
- def counter(query_scope, min_id, max_id)
- Gitlab::Database::BatchCount.batch_count(query_scope, start: min_id, finish: max_id)
+ def counter(query_scope, min_id, max_id, partial_results)
+ return { status: :completed, count: 0 } if min_id.nil? || max_id.nil? # table is empty
+
+ Gitlab::Database::BatchCount.batch_count_with_timeout(query_scope, start: min_id, finish: max_id, timeout: TIMEOUT, partial_results: partial_results)
end
end
end
diff --git a/app/workers/ci/build_finished_worker.rb b/app/workers/ci/build_finished_worker.rb
index 36a50735fed..7503ea3d800 100644
--- a/app/workers/ci/build_finished_worker.rb
+++ b/app/workers/ci/build_finished_worker.rb
@@ -36,10 +36,10 @@ module Ci
build.update_coverage
Ci::BuildReportResultService.new.execute(build)
- build.feature_flagged_execute_hooks
+ build.execute_hooks
ChatNotificationWorker.perform_async(build.id) if build.pipeline.chat?
build.track_deployment_usage
- build.track_verify_usage
+ build.track_verify_environment_usage
if build.failed? && !build.auto_retry_expected?
::Ci::MergeRequests::AddTodoWhenBuildFailsWorker.perform_async(build.id)
diff --git a/app/workers/ci/job_artifacts/track_artifact_report_worker.rb b/app/workers/ci/job_artifacts/track_artifact_report_worker.rb
new file mode 100644
index 00000000000..3df8c284ab3
--- /dev/null
+++ b/app/workers/ci/job_artifacts/track_artifact_report_worker.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Ci
+ module JobArtifacts
+ class TrackArtifactReportWorker
+ include ApplicationWorker
+
+ data_consistency :delayed
+
+ include PipelineBackgroundQueue
+
+ feature_category :code_testing
+
+ idempotent!
+
+ def perform(pipeline_id)
+ Ci::Pipeline.find_by_id(pipeline_id).try do |pipeline|
+ Ci::JobArtifacts::TrackArtifactReportService.new.execute(pipeline)
+ end
+ end
+ end
+ end
+end
diff --git a/app/workers/ci/pipeline_artifacts/coverage_report_worker.rb b/app/workers/ci/pipeline_artifacts/coverage_report_worker.rb
index 127eb3b6f44..53bed0fa9da 100644
--- a/app/workers/ci/pipeline_artifacts/coverage_report_worker.rb
+++ b/app/workers/ci/pipeline_artifacts/coverage_report_worker.rb
@@ -20,7 +20,7 @@ module Ci
return unless pipeline
pipeline.root_ancestor.try do |root_ancestor_pipeline|
- next unless root_ancestor_pipeline.self_and_descendants_complete?
+ next unless root_ancestor_pipeline.self_and_project_descendants_complete?
Ci::PipelineArtifacts::CoverageReportService.new(root_ancestor_pipeline).execute
end
diff --git a/app/workers/cleanup_container_repository_worker.rb b/app/workers/cleanup_container_repository_worker.rb
index 73501315575..3a506470743 100644
--- a/app/workers/cleanup_container_repository_worker.rb
+++ b/app/workers/cleanup_container_repository_worker.rb
@@ -24,7 +24,7 @@ class CleanupContainerRepositoryWorker
return unless valid?
Projects::ContainerRepository::CleanupTagsService
- .new(container_repository, current_user, params)
+ .new(container_repository: container_repository, current_user: current_user, params: params)
.execute
end
diff --git a/app/workers/flush_counter_increments_worker.rb b/app/workers/flush_counter_increments_worker.rb
index e21a7ee35e7..8c7e17587d0 100644
--- a/app/workers/flush_counter_increments_worker.rb
+++ b/app/workers/flush_counter_increments_worker.rb
@@ -11,6 +11,7 @@ class FlushCounterIncrementsWorker
data_consistency :always
sidekiq_options retry: 3
+ loggable_arguments 0, 2
# The increments in `ProjectStatistics` are owned by several teams depending
# on the counter
diff --git a/app/workers/gitlab/github_import/advance_stage_worker.rb b/app/workers/gitlab/github_import/advance_stage_worker.rb
index 70d18d8004c..fdf4ec6f396 100644
--- a/app/workers/gitlab/github_import/advance_stage_worker.rb
+++ b/app/workers/gitlab/github_import/advance_stage_worker.rb
@@ -25,6 +25,8 @@ module Gitlab
issues_and_diff_notes: Stage::ImportIssuesAndDiffNotesWorker,
issue_events: Stage::ImportIssueEventsWorker,
notes: Stage::ImportNotesWorker,
+ attachments: Stage::ImportAttachmentsWorker,
+ protected_branches: Stage::ImportProtectedBranchesWorker,
lfs_objects: Stage::ImportLfsObjectsWorker,
finish: Stage::FinishImportWorker
}.freeze
diff --git a/app/workers/gitlab/github_import/import_protected_branch_worker.rb b/app/workers/gitlab/github_import/import_protected_branch_worker.rb
new file mode 100644
index 00000000000..c083d8ee867
--- /dev/null
+++ b/app/workers/gitlab/github_import/import_protected_branch_worker.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ class ImportProtectedBranchWorker # rubocop:disable Scalability/IdempotentWorker
+ include ObjectImporter
+
+ worker_resource_boundary :cpu
+
+ def representation_class
+ Gitlab::GithubImport::Representation::ProtectedBranch
+ end
+
+ def importer_class
+ Importer::ProtectedBranchImporter
+ end
+
+ def object_type
+ :protected_branch
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/github_import/import_release_attachments_worker.rb b/app/workers/gitlab/github_import/import_release_attachments_worker.rb
new file mode 100644
index 00000000000..c6f45ec1d7c
--- /dev/null
+++ b/app/workers/gitlab/github_import/import_release_attachments_worker.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ class ImportReleaseAttachmentsWorker # rubocop:disable Scalability/IdempotentWorker
+ include ObjectImporter
+
+ def representation_class
+ Representation::ReleaseAttachments
+ end
+
+ def importer_class
+ Importer::ReleaseAttachmentsImporter
+ end
+
+ def object_type
+ :release_attachment
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/github_import/stage/import_attachments_worker.rb b/app/workers/gitlab/github_import/stage/import_attachments_worker.rb
new file mode 100644
index 00000000000..e9086dca503
--- /dev/null
+++ b/app/workers/gitlab/github_import/stage/import_attachments_worker.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ module Stage
+ class ImportAttachmentsWorker # rubocop:disable Scalability/IdempotentWorker
+ include ApplicationWorker
+
+ data_consistency :always
+
+ sidekiq_options retry: 5
+ include GithubImport::Queue
+ include StageMethods
+
+ # client - An instance of Gitlab::GithubImport::Client.
+ # project - An instance of Project.
+ def import(client, project)
+ return skip_to_next_stage(project) if feature_disabled?(project)
+
+ waiters = importers.each_with_object({}) do |importer, hash|
+ waiter = start_importer(project, importer, client)
+ hash[waiter.key] = waiter.jobs_remaining
+ end
+ move_to_next_stage(project, waiters)
+ end
+
+ private
+
+ # For future issue/mr/milestone/etc attachments importers
+ def importers
+ [::Gitlab::GithubImport::Importer::ReleasesAttachmentsImporter]
+ end
+
+ def start_importer(project, importer, client)
+ info(project.id, message: "starting importer", importer: importer.name)
+ importer.new(project, client).execute
+ end
+
+ def skip_to_next_stage(project)
+ info(project.id, message: "skipping importer", importer: 'Attachments')
+ move_to_next_stage(project)
+ end
+
+ def move_to_next_stage(project, waiters = {})
+ AdvanceStageWorker.perform_async(
+ project.id,
+ waiters,
+ :protected_branches
+ )
+ end
+
+ def feature_disabled?(project)
+ Feature.disabled?(:github_importer_attachments_import, project.group, type: :ops)
+ end
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/github_import/stage/import_notes_worker.rb b/app/workers/gitlab/github_import/stage/import_notes_worker.rb
index 167b3e147a3..b53e31ce40e 100644
--- a/app/workers/gitlab/github_import/stage/import_notes_worker.rb
+++ b/app/workers/gitlab/github_import/stage/import_notes_worker.rb
@@ -21,11 +21,7 @@ module Gitlab
hash[waiter.key] = waiter.jobs_remaining
end
- AdvanceStageWorker.perform_async(
- project.id,
- waiters,
- :lfs_objects
- )
+ AdvanceStageWorker.perform_async(project.id, waiters, :attachments)
end
def importers(project)
diff --git a/app/workers/gitlab/github_import/stage/import_protected_branches_worker.rb b/app/workers/gitlab/github_import/stage/import_protected_branches_worker.rb
new file mode 100644
index 00000000000..6d6dea10e64
--- /dev/null
+++ b/app/workers/gitlab/github_import/stage/import_protected_branches_worker.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ module Stage
+ class ImportProtectedBranchesWorker # rubocop:disable Scalability/IdempotentWorker
+ include ApplicationWorker
+
+ data_consistency :always
+
+ sidekiq_options retry: 3
+ include GithubImport::Queue
+ include StageMethods
+
+ # client - An instance of Gitlab::GithubImport::Client.
+ # project - An instance of Project.
+ def import(client, project)
+ info(project.id,
+ message: "starting importer",
+ importer: 'Importer::ProtectedBranchesImporter')
+ waiter = Importer::ProtectedBranchesImporter
+ .new(project, client)
+ .execute
+
+ project.import_state.refresh_jid_expiration
+
+ AdvanceStageWorker.perform_async(
+ project.id,
+ { waiter.key => waiter.jobs_remaining },
+ :lfs_objects
+ )
+ rescue StandardError => e
+ Gitlab::Import::ImportFailureService.track(
+ project_id: project.id,
+ error_source: self.class.name,
+ exception: e,
+ metrics: true
+ )
+
+ raise(e)
+ end
+ end
+ end
+ end
+end
diff --git a/app/workers/gitlab/jira_import/import_issue_worker.rb b/app/workers/gitlab/jira_import/import_issue_worker.rb
index 3824cc1f3ef..eabe988dfc2 100644
--- a/app/workers/gitlab/jira_import/import_issue_worker.rb
+++ b/app/workers/gitlab/jira_import/import_issue_worker.rb
@@ -15,8 +15,7 @@ module Gitlab
loggable_arguments 3
def perform(project_id, jira_issue_id, issue_attributes, waiter_key)
- issue_id = create_issue(issue_attributes, project_id)
- JiraImport.cache_issue_mapping(issue_id, jira_issue_id, project_id)
+ create_issue(issue_attributes, project_id)
rescue StandardError => ex
# Todo: Record jira issue id(or better jira issue key),
# so that we can report the list of failed to import issues to the user
diff --git a/app/workers/gitlab_service_ping_worker.rb b/app/workers/gitlab_service_ping_worker.rb
index a974667e5e0..b02e7318585 100644
--- a/app/workers/gitlab_service_ping_worker.rb
+++ b/app/workers/gitlab_service_ping_worker.rb
@@ -15,17 +15,24 @@ class GitlabServicePingWorker # rubocop:disable Scalability/IdempotentWorker
sidekiq_options retry: 3, dead: false
sidekiq_retry_in { |count| (count + 1) * 8.hours.to_i }
- def perform
- # Disable service ping for GitLab.com
+ def perform(options = {})
+ # Sidekiq does not support keyword arguments, so the args need to be
+ # passed the old pre-Ruby 2.0 way.
+ #
+ # See https://github.com/mperham/sidekiq/issues/2372
+ triggered_from_cron = options.fetch('triggered_from_cron', true)
+ skip_db_write = options.fetch('skip_db_write', false)
+
+ # Disable service ping for GitLab.com unless called manually
# See https://gitlab.com/gitlab-org/gitlab/-/issues/292929 for details
- return if Gitlab.com?
+ return if Gitlab.com? && triggered_from_cron
# Multiple Sidekiq workers could run this. We should only do this at most once a day.
in_lock(LEASE_KEY, ttl: LEASE_TIMEOUT) do
# Splay the request over a minute to avoid thundering herd problems.
sleep(rand(0.0..60.0).round(3))
- ServicePing::SubmitService.new(payload: usage_data).execute
+ ServicePing::SubmitService.new(payload: usage_data, skip_db_write: skip_db_write).execute
end
end
diff --git a/app/workers/google_cloud/create_cloudsql_instance_worker.rb b/app/workers/google_cloud/create_cloudsql_instance_worker.rb
index 3c15c59b8d9..8c4f4c83339 100644
--- a/app/workers/google_cloud/create_cloudsql_instance_worker.rb
+++ b/app/workers/google_cloud/create_cloudsql_instance_worker.rb
@@ -8,30 +8,15 @@ module GoogleCloud
feature_category :not_owned # rubocop:disable Gitlab/AvoidFeatureCategoryNotOwned
idempotent!
- def perform(user_id, project_id, options = {})
+ def perform(user_id, project_id, params = {})
user = User.find(user_id)
project = Project.find(project_id)
+ params = params.with_indifferent_access
- google_oauth2_token = options[:google_oauth2_token]
- gcp_project_id = options[:gcp_project_id]
- instance_name = options[:instance_name]
- database_version = options[:database_version]
- environment_name = options[:environment_name]
- is_protected = options[:is_protected]
-
- params = {
- google_oauth2_token: google_oauth2_token,
- gcp_project_id: gcp_project_id,
- instance_name: instance_name,
- database_version: database_version,
- environment_name: environment_name,
- is_protected: is_protected
- }
-
- response = GoogleCloud::SetupCloudsqlInstanceService.new(project, user, params).execute
+ response = ::GoogleCloud::SetupCloudsqlInstanceService.new(project, user, params).execute
if response[:status] == :error
- raise response[:message]
+ raise "Error SetupCloudsqlInstanceService: #{response.to_json}"
end
end
end
diff --git a/app/workers/google_cloud/fetch_google_ip_list_worker.rb b/app/workers/google_cloud/fetch_google_ip_list_worker.rb
new file mode 100644
index 00000000000..b14b4e735dc
--- /dev/null
+++ b/app/workers/google_cloud/fetch_google_ip_list_worker.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module GoogleCloud
+ class FetchGoogleIpListWorker
+ include ApplicationWorker
+
+ data_consistency :delayed
+ feature_category :build_artifacts
+ urgency :low
+ deduplicate :until_executing
+ idempotent!
+
+ def perform
+ GoogleCloud::FetchGoogleIpListService.new.execute
+ end
+ end
+end
diff --git a/app/workers/groups/update_two_factor_requirement_for_members_worker.rb b/app/workers/groups/update_two_factor_requirement_for_members_worker.rb
new file mode 100644
index 00000000000..ac1d3589516
--- /dev/null
+++ b/app/workers/groups/update_two_factor_requirement_for_members_worker.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+# Worker for updating two factor requirement for all group members
+module Groups
+ class UpdateTwoFactorRequirementForMembersWorker
+ include ApplicationWorker
+
+ data_consistency :always
+
+ idempotent!
+
+ feature_category :authentication_and_authorization
+
+ def perform(group_id)
+ group = Group.find_by_id(group_id)
+
+ return unless group
+
+ group.update_two_factor_requirement_for_members
+ end
+ end
+end
diff --git a/app/workers/issues/close_worker.rb b/app/workers/issues/close_worker.rb
new file mode 100644
index 00000000000..0d540ee8c4f
--- /dev/null
+++ b/app/workers/issues/close_worker.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Issues
+ class CloseWorker
+ include ApplicationWorker
+
+ data_consistency :always
+
+ sidekiq_options retry: 3
+
+ idempotent!
+ deduplicate :until_executed, including_scheduled: true
+ feature_category :source_code_management
+ urgency :high
+ weight 2
+
+ def perform(project_id, issue_id, issue_type, params = {})
+ project = Project.find_by_id(project_id)
+
+ unless project
+ logger.info(structured_payload(message: "Project not found.", project_id: project_id))
+ return
+ end
+
+ issue = case issue_type
+ when "ExternalIssue"
+ ExternalIssue.new(issue_id, project)
+ else
+ Issue.find_by_id(issue_id)
+ end
+
+ unless issue
+ logger.info(structured_payload(message: "Issue not found.", issue_id: issue_id))
+ return
+ end
+
+ author = User.find_by_id(params["closed_by"])
+
+ unless author
+ logger.info(structured_payload(message: "User not found.", user_id: params["closed_by"]))
+ return
+ end
+
+ commit = Commit.build_from_sidekiq_hash(project, params["commit_hash"])
+ service = Issues::CloseService.new(project: project, current_user: author)
+
+ service.execute(issue, commit: commit)
+ end
+ end
+end
diff --git a/app/workers/namespaces/onboarding_issue_created_worker.rb b/app/workers/namespaces/onboarding_issue_created_worker.rb
index aab5767e0f1..4f0cc71cd91 100644
--- a/app/workers/namespaces/onboarding_issue_created_worker.rb
+++ b/app/workers/namespaces/onboarding_issue_created_worker.rb
@@ -18,7 +18,7 @@ module Namespaces
namespace = Namespace.find_by_id(namespace_id)
return unless namespace
- OnboardingProgressService.new(namespace).execute(action: :issue_created)
+ Onboarding::ProgressService.new(namespace).execute(action: :issue_created)
end
end
end
diff --git a/app/workers/namespaces/onboarding_pipeline_created_worker.rb b/app/workers/namespaces/onboarding_pipeline_created_worker.rb
index 4172e286474..c3850880df0 100644
--- a/app/workers/namespaces/onboarding_pipeline_created_worker.rb
+++ b/app/workers/namespaces/onboarding_pipeline_created_worker.rb
@@ -18,7 +18,7 @@ module Namespaces
namespace = Namespace.find_by_id(namespace_id)
return unless namespace
- OnboardingProgressService.new(namespace).execute(action: :pipeline_created)
+ Onboarding::ProgressService.new(namespace).execute(action: :pipeline_created)
end
end
end
diff --git a/app/workers/namespaces/onboarding_progress_worker.rb b/app/workers/namespaces/onboarding_progress_worker.rb
index 77a31d85a9a..49629428459 100644
--- a/app/workers/namespaces/onboarding_progress_worker.rb
+++ b/app/workers/namespaces/onboarding_progress_worker.rb
@@ -19,7 +19,7 @@ module Namespaces
namespace = Namespace.find_by_id(namespace_id)
return unless namespace && action
- OnboardingProgressService.new(namespace).execute(action: action.to_sym)
+ Onboarding::ProgressService.new(namespace).execute(action: action.to_sym)
end
end
end
diff --git a/app/workers/namespaces/onboarding_user_added_worker.rb b/app/workers/namespaces/onboarding_user_added_worker.rb
index 4d17cf9a6e2..a1b349eedd3 100644
--- a/app/workers/namespaces/onboarding_user_added_worker.rb
+++ b/app/workers/namespaces/onboarding_user_added_worker.rb
@@ -15,7 +15,7 @@ module Namespaces
def perform(namespace_id)
namespace = Namespace.find(namespace_id)
- OnboardingProgressService.new(namespace).execute(action: :user_added)
+ Onboarding::ProgressService.new(namespace).execute(action: :user_added)
end
end
end
diff --git a/app/workers/namespaces/process_sync_events_worker.rb b/app/workers/namespaces/process_sync_events_worker.rb
index 2bf2a4a6ef8..d0124c69781 100644
--- a/app/workers/namespaces/process_sync_events_worker.rb
+++ b/app/workers/namespaces/process_sync_events_worker.rb
@@ -13,7 +13,7 @@ module Namespaces
urgency :high
idempotent!
- deduplicate :until_executing
+ deduplicate :until_executed, if_deduplicated: :reschedule_once
def perform
results = ::Ci::ProcessSyncEventsService.new(
diff --git a/app/workers/object_storage/migrate_uploads_worker.rb b/app/workers/object_storage/migrate_uploads_worker.rb
index b7d938e6b68..3e681c3f111 100644
--- a/app/workers/object_storage/migrate_uploads_worker.rb
+++ b/app/workers/object_storage/migrate_uploads_worker.rb
@@ -11,7 +11,7 @@ module ObjectStorage
include ObjectStorageQueue
feature_category :not_owned # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned
- loggable_arguments 0, 1, 2, 3
+ loggable_arguments 0
SanityCheckError = Class.new(StandardError)
@@ -67,41 +67,19 @@ module ObjectStorage
include Report
# rubocop: disable CodeReuse/ActiveRecord
- def self.enqueue!(uploads, model_class, mounted_as, to_store)
- sanity_check!(uploads, model_class, mounted_as)
-
- perform_async(uploads.ids, model_class.to_s, mounted_as, to_store)
+ def self.enqueue!(uploads, to_store)
+ perform_async(uploads.ids, to_store)
end
# rubocop: enable CodeReuse/ActiveRecord
- # We need to be sure all the uploads are for the same uploader and model type
- # and that the mount point exists if provided.
- #
- def self.sanity_check!(uploads, model_class, mounted_as)
- upload = uploads.first
- uploader_class = upload.uploader.constantize
- uploader_types = uploads.map(&:uploader).uniq
- model_types = uploads.map(&:model_type).uniq
- model_has_mount = mounted_as.nil? || model_class.uploaders[mounted_as] == uploader_class
-
- raise(SanityCheckError, _("Multiple uploaders found: %{uploader_types}") % { uploader_types: uploader_types }) unless uploader_types.count == 1
- raise(SanityCheckError, _("Multiple model types found: %{model_types}") % { model_types: model_types }) unless model_types.count == 1
- raise(SanityCheckError, _("Mount point %{mounted_as} not found in %{model_class}.") % { mounted_as: mounted_as, model_class: model_class }) unless model_has_mount
- end
-
# rubocop: disable CodeReuse/ActiveRecord
def perform(*args)
- args_check!(args)
-
- (ids, model_type, mounted_as, to_store) = args
+ ids, to_store = retrieve_applicable_args!(args)
- @model_class = model_type.constantize
- @mounted_as = mounted_as&.to_sym
@to_store = to_store
uploads = Upload.preload(:model).where(id: ids)
- sanity_check!(uploads)
results = migrate(uploads)
report!(results)
@@ -111,31 +89,22 @@ module ObjectStorage
end
# rubocop: enable CodeReuse/ActiveRecord
- def sanity_check!(uploads)
- self.class.sanity_check!(uploads, @model_class, @mounted_as)
- end
-
- def args_check!(args)
- return if args.count == 4
+ private
- case args.count
- when 3 then raise SanityCheckError, _("Job is missing the `model_type` argument.")
- else
- raise SanityCheckError, _("Job has wrong arguments format.")
- end
- end
+ def retrieve_applicable_args!(args)
+ return args if args.count == 2
+ return args.values_at(0, 3) if args.count == 4
- def build_uploaders(uploads)
- uploads.map { |upload| upload.retrieve_uploader(@mounted_as) }
+ raise SanityCheckError, _("Job has wrong arguments format.")
end
def migrate(uploads)
- build_uploaders(uploads).map(&method(:process_uploader))
+ uploads.map(&method(:process_upload))
end
- def process_uploader(uploader)
- MigrationResult.new(uploader.upload).tap do |result|
- uploader.migrate!(@to_store)
+ def process_upload(upload)
+ MigrationResult.new(upload).tap do |result|
+ upload.retrieve_uploader.migrate!(@to_store)
rescue StandardError => e
result.error = e
end
diff --git a/app/workers/process_commit_worker.rb b/app/workers/process_commit_worker.rb
index a4dfe11c394..cd6ce6eb28b 100644
--- a/app/workers/process_commit_worker.rb
+++ b/app/workers/process_commit_worker.rb
@@ -34,7 +34,7 @@ class ProcessCommitWorker
return unless user
- commit = build_commit(project, commit_hash)
+ commit = Commit.build_from_sidekiq_hash(project, commit_hash)
author = commit.author || user
process_commit_message(project, commit, user, author, default)
@@ -51,12 +51,22 @@ class ProcessCommitWorker
end
def close_issues(project, user, author, commit, issues)
- # We don't want to run permission related queries for every single issue,
- # therefore we use IssueCollection here and skip the authorization check in
- # Issues::CloseService#execute.
- IssueCollection.new(issues).updatable_by_user(user).each do |issue|
- Issues::CloseService.new(project: project, current_user: author)
- .close_issue(issue, closed_via: commit)
+ if Feature.enabled?(:process_issue_closure_in_background, project)
+ Issues::CloseWorker.bulk_perform_async_with_contexts(
+ issues,
+ arguments_proc: -> (issue) {
+ [project.id, issue.id, issue.class.to_s, { closed_by: author.id, commit_hash: commit.to_hash }]
+ },
+ context_proc: -> (issue) { { project: project } }
+ )
+ else
+ # We don't want to run permission related queries for every single issue,
+ # therefore we use IssueCollection here and skip the authorization check in
+ # Issues::CloseService#execute.
+ IssueCollection.new(issues).updatable_by_user(user).each do |issue|
+ Issues::CloseService.new(project: project, current_user: author)
+ .close_issue(issue, closed_via: commit)
+ end
end
end
@@ -75,19 +85,4 @@ class ProcessCommitWorker
.with_first_mention_not_earlier_than(commit.committed_date)
.update_all(first_mentioned_in_commit_at: commit.committed_date)
end
-
- def build_commit(project, hash)
- date_suffix = '_date'
-
- # When processing Sidekiq payloads various timestamps are stored as Strings.
- # Commit in turn expects Time-like instances upon input, so we have to
- # manually parse these values.
- hash.each do |key, value|
- if key.to_s.end_with?(date_suffix) && value.is_a?(String)
- hash[key] = Time.zone.parse(value)
- end
- end
-
- Commit.from_hash(hash, project)
- 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 a280c9203d6..ba6d44ec4a5 100644
--- a/app/workers/projects/inactive_projects_deletion_cron_worker.rb
+++ b/app/workers/projects/inactive_projects_deletion_cron_worker.rb
@@ -39,8 +39,6 @@ module Projects
raise TimeoutError
end
- next unless Feature.enabled?(:inactive_projects_deletion, project.root_namespace)
-
with_context(project: project, user: admin_user) do
deletion_warning_email_sent_on = notified_inactive_projects["project:#{project.id}"]
diff --git a/app/workers/projects/process_sync_events_worker.rb b/app/workers/projects/process_sync_events_worker.rb
index 57f3e3dee5e..4bbe1b65e5a 100644
--- a/app/workers/projects/process_sync_events_worker.rb
+++ b/app/workers/projects/process_sync_events_worker.rb
@@ -13,7 +13,7 @@ module Projects
urgency :high
idempotent!
- deduplicate :until_executing
+ deduplicate :until_executed, if_deduplicated: :reschedule_once
def perform
results = ::Ci::ProcessSyncEventsService.new(
diff --git a/app/workers/ssh_keys/expired_notification_worker.rb b/app/workers/ssh_keys/expired_notification_worker.rb
index dc1efce51ce..768579214c6 100644
--- a/app/workers/ssh_keys/expired_notification_worker.rb
+++ b/app/workers/ssh_keys/expired_notification_worker.rb
@@ -15,19 +15,20 @@ module SshKeys
# rubocop: disable CodeReuse/ActiveRecord
def perform
- order = Gitlab::Pagination::Keyset::Order.build([
- Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: 'expires_at_utc',
- order_expression: Arel.sql("date(expires_at AT TIME ZONE 'UTC')").asc,
- nullable: :not_nullable,
- distinct: false,
- add_to_projections: true
- ),
- Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: 'id',
- order_expression: Key.arel_table[:id].asc
- )
- ])
+ order = Gitlab::Pagination::Keyset::Order.build(
+ [
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'expires_at_utc',
+ order_expression: Arel.sql("date(expires_at AT TIME ZONE 'UTC')").asc,
+ nullable: :not_nullable,
+ distinct: false,
+ add_to_projections: true
+ ),
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: 'id',
+ order_expression: Key.arel_table[:id].asc
+ )
+ ])
scope = Key.expired_today_and_not_notified.order(order)
diff --git a/app/workers/users/migrate_records_to_ghost_user_in_batches_worker.rb b/app/workers/users/migrate_records_to_ghost_user_in_batches_worker.rb
new file mode 100644
index 00000000000..ddddfc106ae
--- /dev/null
+++ b/app/workers/users/migrate_records_to_ghost_user_in_batches_worker.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Users
+ class MigrateRecordsToGhostUserInBatchesWorker
+ include ApplicationWorker
+ include Gitlab::ExclusiveLeaseHelpers
+ include CronjobQueue # rubocop: disable Scalability/CronWorkerContext
+
+ sidekiq_options retry: false
+ feature_category :users
+ data_consistency :always
+ idempotent!
+
+ def perform
+ return unless Feature.enabled?(:user_destroy_with_limited_execution_time_worker)
+
+ in_lock(self.class.name.underscore, ttl: Gitlab::Utils::ExecutionTracker::MAX_RUNTIME, retries: 0) do
+ Users::MigrateRecordsToGhostUserInBatchesService.new.execute
+ end
+ end
+ end
+end
diff --git a/config/application.rb b/config/application.rb
index d28967f2966..03c8eadc4b0 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -264,6 +264,7 @@ module Gitlab
config.assets.precompile << "page_bundles/cycle_analytics.css"
config.assets.precompile << "page_bundles/dashboard_projects.css"
config.assets.precompile << "page_bundles/dev_ops_reports.css"
+ config.assets.precompile << "page_bundles/editor.css"
config.assets.precompile << "page_bundles/environments.css"
config.assets.precompile << "page_bundles/epics.css"
config.assets.precompile << "page_bundles/error_tracking_details.css"
@@ -292,10 +293,12 @@ module Gitlab
config.assets.precompile << "page_bundles/productivity_analytics.css"
config.assets.precompile << "page_bundles/profile.css"
config.assets.precompile << "page_bundles/profile_two_factor_auth.css"
+ config.assets.precompile << "page_bundles/profiles/preferences.css"
config.assets.precompile << "page_bundles/project.css"
config.assets.precompile << "page_bundles/projects_edit.css"
config.assets.precompile << "page_bundles/reports.css"
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/security_dashboard.css"
config.assets.precompile << "page_bundles/security_discover.css"
diff --git a/config/audit_events/types/type_schema.json b/config/audit_events/types/type_schema.json
new file mode 100644
index 00000000000..0d5d79bc4c4
--- /dev/null
+++ b/config/audit_events/types/type_schema.json
@@ -0,0 +1,74 @@
+{
+ "$schema": "http://json-schema.org/draft-06/schema#",
+ "$id": "https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/audit_events/types/type_schema.json",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Unique identifying name for the type of audit event"
+ },
+ "description": {
+ "type": "string",
+ "description": "A human-readable description of how this event is triggered"
+ },
+ "introduced_by_issue": {
+ "type": "string",
+ "format": "uri",
+ "description": "URL to GitLab issue that added this type of audit event",
+ "qt-uri-protocols": [
+ "https"
+ ]
+ },
+ "introduced_by_mr": {
+ "type": "string",
+ "format": "uri",
+ "description": "URL to GitLab merge request that added this type of audit event",
+ "qt-uri-protocols": [
+ "https"
+ ]
+ },
+ "group": {
+ "type": "string",
+ "description": "Name of the group that introduced this audit event. For example, manage::compliance"
+ },
+ "milestone": {
+ "type": "string",
+ "description": "Milestone that introduced this audit event type. For example, 15.8",
+ "pattern": "^[0-9]+\\.[0-9]+$"
+ },
+ "saved_to_database": {
+ "type": "boolean",
+ "description": "Indicate whether to persist events to database and JSON logs"
+ },
+ "streamed": {
+ "type": "boolean",
+ "description": "Indicate that events should be streamed to external services (if configured)"
+ }
+ },
+ "required": [
+ "description",
+ "group",
+ "introduced_by_issue",
+ "introduced_by_mr",
+ "milestone",
+ "name",
+ "saved_to_database",
+ "streamed"
+ ],
+ "not": {
+ "properties": {
+ "saved_to_database": {
+ "enum": [
+ false
+ ]
+ },
+ "streamed": {
+ "enum": [
+ false
+ ]
+ }
+ }
+ },
+ "title": "GitLabAuditEventType"
+}
diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml
index 22dd75fc64d..b8689c1c461 100644
--- a/config/dependency_decisions.yml
+++ b/config/dependency_decisions.yml
@@ -145,12 +145,6 @@
:versions: []
:when: 2017-04-05 10:38:46.275721000 Z
- - :approve
- - jszip-utils
- - :who: Phil Hughes
- :why: https://github.com/Stuk/jszip-utils/blob/master/LICENSE.markdown
- :versions: []
- :when: 2017-04-05 10:39:32.676232000 Z
-- - :approve
- pako
- :who: Phil Hughes
:why: https://github.com/nodeca/pako/blob/master/LICENSE
diff --git a/config/environments/development.rb b/config/environments/development.rb
index d475a2a6642..5e67ed71954 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -50,6 +50,8 @@ Rails.application.configure do
# Push preview path now to prevent FrozenError during view_component's initialzer
config.autoload_paths.push("#{config.root}/spec/components/previews")
+ config.lookbook.page_paths = ["#{config.root}/spec/components/docs"]
+
# Adds additional error checking when serving assets at runtime.
# Checks for improperly declared sprockets dependencies.
# Raises helpful error messages.
@@ -61,7 +63,7 @@ Rails.application.configure do
config.action_mailer.raise_delivery_errors = true
# Don't make a mess when bootstrapping a development environment
config.action_mailer.perform_deliveries = (ENV['BOOTSTRAP'] != '1')
- config.action_mailer.preview_path = "#{Rails.root}{/ee,}/app/mailers/previews"
+ config.action_mailer.preview_path = GitlabEdition.path_glob('app/mailers/previews')
config.eager_load = false
diff --git a/config/environments/test.rb b/config/environments/test.rb
index f4d3d2ddfda..41413c55ba4 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -48,6 +48,8 @@ Rails.application.configure do
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
+ config.action_mailer.preview_path = GitlabEdition.path_glob('app/mailers/previews')
+
# Print deprecation notices to the stderr
config.active_support.deprecation = :stderr
diff --git a/config/events/1662373051_Projects__GoogleCloud__ConfigurationController_error_invalid_user.yml b/config/events/1662373051_Projects__GoogleCloud__ConfigurationController_error_invalid_user.yml
new file mode 100644
index 00000000000..5a71e2df485
--- /dev/null
+++ b/config/events/1662373051_Projects__GoogleCloud__ConfigurationController_error_invalid_user.yml
@@ -0,0 +1,26 @@
+---
+description: Invalid or unauthorized user
+category: Projects::GoogleCloud::ConfigurationController
+action: error_invalid_user
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373057_Projects__GoogleCloud__ConfigurationController_error_google_oauth2_not_enabled.yml b/config/events/1662373057_Projects__GoogleCloud__ConfigurationController_error_google_oauth2_not_enabled.yml
new file mode 100644
index 00000000000..483225e0def
--- /dev/null
+++ b/config/events/1662373057_Projects__GoogleCloud__ConfigurationController_error_google_oauth2_not_enabled.yml
@@ -0,0 +1,26 @@
+---
+description: Google OAuth2 not enabled on GitLab instance
+category: Projects::GoogleCloud::ConfigurationController
+action: error_google_oauth2_not_enabled
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373062_Projects__GoogleCloud__ConfigurationController_error_feature_flag_not_enabled.yml b/config/events/1662373062_Projects__GoogleCloud__ConfigurationController_error_feature_flag_not_enabled.yml
new file mode 100644
index 00000000000..b24a326ab30
--- /dev/null
+++ b/config/events/1662373062_Projects__GoogleCloud__ConfigurationController_error_feature_flag_not_enabled.yml
@@ -0,0 +1,26 @@
+---
+description: Feature flag not enabled on the GitLab instance
+category: Projects::GoogleCloud::ConfigurationController
+action: error_feature_flag_not_enabled
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373069_Projects__GoogleCloud__ConfigurationController_render_page.yml b/config/events/1662373069_Projects__GoogleCloud__ConfigurationController_render_page.yml
new file mode 100644
index 00000000000..21083a7596b
--- /dev/null
+++ b/config/events/1662373069_Projects__GoogleCloud__ConfigurationController_render_page.yml
@@ -0,0 +1,26 @@
+---
+description: Configuration page rendered
+category: Projects::GoogleCloud::ConfigurationController
+action: render_page
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373075_Projects__GoogleCloud__ServiceAccountsController_error_invalid_user.yml b/config/events/1662373075_Projects__GoogleCloud__ServiceAccountsController_error_invalid_user.yml
new file mode 100644
index 00000000000..850b8e81c0b
--- /dev/null
+++ b/config/events/1662373075_Projects__GoogleCloud__ServiceAccountsController_error_invalid_user.yml
@@ -0,0 +1,26 @@
+---
+description: Invalid or unauthorized user
+category: Projects::GoogleCloud::ServiceAccountsController
+action: error_invalid_user
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373081_Projects__GoogleCloud__ServiceAccountsController_error_google_oauth2_not_enabled.yml b/config/events/1662373081_Projects__GoogleCloud__ServiceAccountsController_error_google_oauth2_not_enabled.yml
new file mode 100644
index 00000000000..726ba6af7aa
--- /dev/null
+++ b/config/events/1662373081_Projects__GoogleCloud__ServiceAccountsController_error_google_oauth2_not_enabled.yml
@@ -0,0 +1,26 @@
+---
+description: Google OAuth2 not enabled on GitLab instance
+category: Projects::GoogleCloud::ServiceAccountsController
+action: error_google_oauth2_not_enabled
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373087_Projects__GoogleCloud__ServiceAccountsController_error_feature_flag_not_enabled.yml b/config/events/1662373087_Projects__GoogleCloud__ServiceAccountsController_error_feature_flag_not_enabled.yml
new file mode 100644
index 00000000000..713e1a35584
--- /dev/null
+++ b/config/events/1662373087_Projects__GoogleCloud__ServiceAccountsController_error_feature_flag_not_enabled.yml
@@ -0,0 +1,26 @@
+---
+description: Feature flag not enabled on the GitLab instance
+category: Projects::GoogleCloud::ServiceAccountsController
+action: error_feature_flag_not_enabled
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373092_Projects__GoogleCloud__ServiceAccountsController_render_form.yml b/config/events/1662373092_Projects__GoogleCloud__ServiceAccountsController_render_form.yml
new file mode 100644
index 00000000000..55e0c87dd6c
--- /dev/null
+++ b/config/events/1662373092_Projects__GoogleCloud__ServiceAccountsController_render_form.yml
@@ -0,0 +1,26 @@
+---
+description: Service account form rendered
+category: Projects::GoogleCloud::ServiceAccountsController
+action: render_form
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373098_Projects__GoogleCloud__ServiceAccountsController_error_no_gcp_projects.yml b/config/events/1662373098_Projects__GoogleCloud__ServiceAccountsController_error_no_gcp_projects.yml
new file mode 100644
index 00000000000..a57df38aa6e
--- /dev/null
+++ b/config/events/1662373098_Projects__GoogleCloud__ServiceAccountsController_error_no_gcp_projects.yml
@@ -0,0 +1,26 @@
+---
+description: No GCP projects found for user
+category: Projects::GoogleCloud::ServiceAccountsController
+action: error_no_gcp_projects
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373103_Projects__GoogleCloud__ServiceAccountsController_create_service_account.yml b/config/events/1662373103_Projects__GoogleCloud__ServiceAccountsController_create_service_account.yml
new file mode 100644
index 00000000000..e147eaea44c
--- /dev/null
+++ b/config/events/1662373103_Projects__GoogleCloud__ServiceAccountsController_create_service_account.yml
@@ -0,0 +1,26 @@
+---
+description: Service account created
+category: Projects::GoogleCloud::ServiceAccountsController
+action: create_service_account
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373109_Projects__GoogleCloud__ServiceAccountsController_error_google_api.yml b/config/events/1662373109_Projects__GoogleCloud__ServiceAccountsController_error_google_api.yml
new file mode 100644
index 00000000000..f5404c0b318
--- /dev/null
+++ b/config/events/1662373109_Projects__GoogleCloud__ServiceAccountsController_error_google_api.yml
@@ -0,0 +1,26 @@
+---
+description: Google API error
+category: Projects::GoogleCloud::ServiceAccountsController
+action: error_google_api
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373114_Projects__GoogleCloud__GcpRegionsController_error_invalid_user.yml b/config/events/1662373114_Projects__GoogleCloud__GcpRegionsController_error_invalid_user.yml
new file mode 100644
index 00000000000..e190dc68e05
--- /dev/null
+++ b/config/events/1662373114_Projects__GoogleCloud__GcpRegionsController_error_invalid_user.yml
@@ -0,0 +1,26 @@
+---
+description: Invalid or unauthorized user
+category: Projects::GoogleCloud::GcpRegionsController
+action: error_invalid_user
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373120_Projects__GoogleCloud__GcpRegionsController_error_google_oauth2_not_enabled.yml b/config/events/1662373120_Projects__GoogleCloud__GcpRegionsController_error_google_oauth2_not_enabled.yml
new file mode 100644
index 00000000000..4ceb9567a31
--- /dev/null
+++ b/config/events/1662373120_Projects__GoogleCloud__GcpRegionsController_error_google_oauth2_not_enabled.yml
@@ -0,0 +1,26 @@
+---
+description: Google OAuth2 not enabled on GitLab instance
+category: Projects::GoogleCloud::GcpRegionsController
+action: error_google_oauth2_not_enabled
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373125_Projects__GoogleCloud__GcpRegionsController_error_feature_flag_not_enabled.yml b/config/events/1662373125_Projects__GoogleCloud__GcpRegionsController_error_feature_flag_not_enabled.yml
new file mode 100644
index 00000000000..c7b9c4ac2f6
--- /dev/null
+++ b/config/events/1662373125_Projects__GoogleCloud__GcpRegionsController_error_feature_flag_not_enabled.yml
@@ -0,0 +1,26 @@
+---
+description: Feature flag not enabled on the GitLab instance
+category: Projects::GoogleCloud::GcpRegionsController
+action: error_feature_flag_not_enabled
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373131_Projects__GoogleCloud__GcpRegionsController_render_form.yml b/config/events/1662373131_Projects__GoogleCloud__GcpRegionsController_render_form.yml
new file mode 100644
index 00000000000..227e0117e84
--- /dev/null
+++ b/config/events/1662373131_Projects__GoogleCloud__GcpRegionsController_render_form.yml
@@ -0,0 +1,26 @@
+---
+description: GCP regions configuration form rendered
+category: Projects::GoogleCloud::GcpRegionsController
+action: render_form
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373136_Projects__GoogleCloud__GcpRegionsController_configure_region.yml b/config/events/1662373136_Projects__GoogleCloud__GcpRegionsController_configure_region.yml
new file mode 100644
index 00000000000..f301c068188
--- /dev/null
+++ b/config/events/1662373136_Projects__GoogleCloud__GcpRegionsController_configure_region.yml
@@ -0,0 +1,26 @@
+---
+description: GCP region configured
+category: Projects::GoogleCloud::GcpRegionsController
+action: configure_region
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373142_Projects__GoogleCloud__GcpRegionsController_error_create.yml b/config/events/1662373142_Projects__GoogleCloud__GcpRegionsController_error_create.yml
new file mode 100644
index 00000000000..67bbc1a7465
--- /dev/null
+++ b/config/events/1662373142_Projects__GoogleCloud__GcpRegionsController_error_create.yml
@@ -0,0 +1,26 @@
+---
+description: Failed to configure GCP region
+category: Projects::GoogleCloud::GcpRegionsController
+action: error_create
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373147_Projects__GoogleCloud__RevokeOauthController_error_invalid_user.yml b/config/events/1662373147_Projects__GoogleCloud__RevokeOauthController_error_invalid_user.yml
new file mode 100644
index 00000000000..a316efda189
--- /dev/null
+++ b/config/events/1662373147_Projects__GoogleCloud__RevokeOauthController_error_invalid_user.yml
@@ -0,0 +1,26 @@
+---
+description: Invalid or unauthorized user
+category: Projects::GoogleCloud::RevokeOauthController
+action: error_invalid_user
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373153_Projects__GoogleCloud__RevokeOauthController_error_google_oauth2_not_enabled.yml b/config/events/1662373153_Projects__GoogleCloud__RevokeOauthController_error_google_oauth2_not_enabled.yml
new file mode 100644
index 00000000000..fc2bf9a5bcd
--- /dev/null
+++ b/config/events/1662373153_Projects__GoogleCloud__RevokeOauthController_error_google_oauth2_not_enabled.yml
@@ -0,0 +1,26 @@
+---
+description: Google OAuth2 not enabled on GitLab instance
+category: Projects::GoogleCloud::RevokeOauthController
+action: error_google_oauth2_not_enabled
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373158_Projects__GoogleCloud__RevokeOauthController_error_feature_flag_not_enabled.yml b/config/events/1662373158_Projects__GoogleCloud__RevokeOauthController_error_feature_flag_not_enabled.yml
new file mode 100644
index 00000000000..33fdb94c3d8
--- /dev/null
+++ b/config/events/1662373158_Projects__GoogleCloud__RevokeOauthController_error_feature_flag_not_enabled.yml
@@ -0,0 +1,26 @@
+---
+description: Feature flag not enabled on the GitLab instance
+category: Projects::GoogleCloud::RevokeOauthController
+action: error_feature_flag_not_enabled
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373164_Projects__GoogleCloud__RevokeOauthController_revoke_oauth.yml b/config/events/1662373164_Projects__GoogleCloud__RevokeOauthController_revoke_oauth.yml
new file mode 100644
index 00000000000..a621d57271a
--- /dev/null
+++ b/config/events/1662373164_Projects__GoogleCloud__RevokeOauthController_revoke_oauth.yml
@@ -0,0 +1,26 @@
+---
+description: OAuth token revoked
+category: Projects::GoogleCloud::RevokeOauthController
+action: revoke_oauth
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373170_Projects__GoogleCloud__DeploymentsController_error_invalid_user.yml b/config/events/1662373170_Projects__GoogleCloud__DeploymentsController_error_invalid_user.yml
new file mode 100644
index 00000000000..4543251dd08
--- /dev/null
+++ b/config/events/1662373170_Projects__GoogleCloud__DeploymentsController_error_invalid_user.yml
@@ -0,0 +1,26 @@
+---
+description: Invalid or unauthorized user
+category: Projects::GoogleCloud::DeploymentsController
+action: error_invalid_user
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373175_Projects__GoogleCloud__DeploymentsController_error_google_oauth2_not_enabled.yml b/config/events/1662373175_Projects__GoogleCloud__DeploymentsController_error_google_oauth2_not_enabled.yml
new file mode 100644
index 00000000000..119db94c828
--- /dev/null
+++ b/config/events/1662373175_Projects__GoogleCloud__DeploymentsController_error_google_oauth2_not_enabled.yml
@@ -0,0 +1,26 @@
+---
+description: Google OAuth2 not enabled on GitLab instance
+category: Projects::GoogleCloud::DeploymentsController
+action: error_google_oauth2_not_enabled
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373181_Projects__GoogleCloud__DeploymentsController_error_feature_flag_not_enabled.yml b/config/events/1662373181_Projects__GoogleCloud__DeploymentsController_error_feature_flag_not_enabled.yml
new file mode 100644
index 00000000000..1d4ba496e82
--- /dev/null
+++ b/config/events/1662373181_Projects__GoogleCloud__DeploymentsController_error_feature_flag_not_enabled.yml
@@ -0,0 +1,26 @@
+---
+description: Feature flag not enabled on the GitLab instance
+category: Projects::GoogleCloud::DeploymentsController
+action: error_feature_flag_not_enabled
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373187_Projects__GoogleCloud__DeploymentsController_render_page.yml b/config/events/1662373187_Projects__GoogleCloud__DeploymentsController_render_page.yml
new file mode 100644
index 00000000000..0335988d5c5
--- /dev/null
+++ b/config/events/1662373187_Projects__GoogleCloud__DeploymentsController_render_page.yml
@@ -0,0 +1,26 @@
+---
+description: Deployments page rendered
+category: Projects::GoogleCloud::DeploymentsController
+action: render_page
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373192_Projects__GoogleCloud__DeploymentsController_generate_cloudrun_pipeline.yml b/config/events/1662373192_Projects__GoogleCloud__DeploymentsController_generate_cloudrun_pipeline.yml
new file mode 100644
index 00000000000..8e3920015a2
--- /dev/null
+++ b/config/events/1662373192_Projects__GoogleCloud__DeploymentsController_generate_cloudrun_pipeline.yml
@@ -0,0 +1,26 @@
+---
+description: Cloud Run pipeline generated
+category: Projects::GoogleCloud::DeploymentsController
+action: generate_cloudrun_pipeline
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373198_Projects__GoogleCloud__DeploymentsController_error_enable_cloudrun_services.yml b/config/events/1662373198_Projects__GoogleCloud__DeploymentsController_error_enable_cloudrun_services.yml
new file mode 100644
index 00000000000..4a3fdd48a0d
--- /dev/null
+++ b/config/events/1662373198_Projects__GoogleCloud__DeploymentsController_error_enable_cloudrun_services.yml
@@ -0,0 +1,26 @@
+---
+description: Failed to enable Cloud Run services
+category: Projects::GoogleCloud::DeploymentsController
+action: error_enable_cloudrun_services
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373204_Projects__GoogleCloud__DeploymentsController_error_generate_cloudrun_pipeline.yml b/config/events/1662373204_Projects__GoogleCloud__DeploymentsController_error_generate_cloudrun_pipeline.yml
new file mode 100644
index 00000000000..ecf2ef4ae37
--- /dev/null
+++ b/config/events/1662373204_Projects__GoogleCloud__DeploymentsController_error_generate_cloudrun_pipeline.yml
@@ -0,0 +1,26 @@
+---
+description: Failed to enable Cloud Run services
+category: Projects::GoogleCloud::DeploymentsController
+action: error_generate_cloudrun_pipeline
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373209_Projects__GoogleCloud__DeploymentsController_error_google_api.yml b/config/events/1662373209_Projects__GoogleCloud__DeploymentsController_error_google_api.yml
new file mode 100644
index 00000000000..81e7a881b5a
--- /dev/null
+++ b/config/events/1662373209_Projects__GoogleCloud__DeploymentsController_error_google_api.yml
@@ -0,0 +1,26 @@
+---
+description: Google API error
+category: Projects::GoogleCloud::DeploymentsController
+action: error_google_api
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373215_Projects__GoogleCloud__DatabasesController_error_invalid_user.yml b/config/events/1662373215_Projects__GoogleCloud__DatabasesController_error_invalid_user.yml
new file mode 100644
index 00000000000..21734eb875f
--- /dev/null
+++ b/config/events/1662373215_Projects__GoogleCloud__DatabasesController_error_invalid_user.yml
@@ -0,0 +1,26 @@
+---
+description: Invalid or unauthorized user
+category: Projects::GoogleCloud::DatabasesController
+action: error_invalid_user
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373220_Projects__GoogleCloud__DatabasesController_error_google_oauth2_not_enabled.yml b/config/events/1662373220_Projects__GoogleCloud__DatabasesController_error_google_oauth2_not_enabled.yml
new file mode 100644
index 00000000000..b9a4e3f2c7d
--- /dev/null
+++ b/config/events/1662373220_Projects__GoogleCloud__DatabasesController_error_google_oauth2_not_enabled.yml
@@ -0,0 +1,26 @@
+---
+description: Google OAuth2 not enabled on GitLab instance
+category: Projects::GoogleCloud::DatabasesController
+action: error_google_oauth2_not_enabled
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373226_Projects__GoogleCloud__DatabasesController_error_feature_flag_not_enabled.yml b/config/events/1662373226_Projects__GoogleCloud__DatabasesController_error_feature_flag_not_enabled.yml
new file mode 100644
index 00000000000..04c03b87dd3
--- /dev/null
+++ b/config/events/1662373226_Projects__GoogleCloud__DatabasesController_error_feature_flag_not_enabled.yml
@@ -0,0 +1,26 @@
+---
+description: Feature flag not enabled on the GitLab instance
+category: Projects::GoogleCloud::DatabasesController
+action: error_feature_flag_not_enabled
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373232_Projects__GoogleCloud__DatabasesController_render_page.yml b/config/events/1662373232_Projects__GoogleCloud__DatabasesController_render_page.yml
new file mode 100644
index 00000000000..b5bf9853e44
--- /dev/null
+++ b/config/events/1662373232_Projects__GoogleCloud__DatabasesController_render_page.yml
@@ -0,0 +1,26 @@
+---
+description: Databases page rendered
+category: Projects::GoogleCloud::DatabasesController
+action: render_page
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373237_Projects__GoogleCloud__DatabasesController_render_cloudsql_form.yml b/config/events/1662373237_Projects__GoogleCloud__DatabasesController_render_cloudsql_form.yml
new file mode 100644
index 00000000000..5fab18d965d
--- /dev/null
+++ b/config/events/1662373237_Projects__GoogleCloud__DatabasesController_render_cloudsql_form.yml
@@ -0,0 +1,26 @@
+---
+description: Cloud SQL form rendered
+category: Projects::GoogleCloud::DatabasesController
+action: render_cloudsql_form
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373243_Projects__GoogleCloud__DatabasesController_create_cloudsql_instance.yml b/config/events/1662373243_Projects__GoogleCloud__DatabasesController_create_cloudsql_instance.yml
new file mode 100644
index 00000000000..3f5a2b5d8ba
--- /dev/null
+++ b/config/events/1662373243_Projects__GoogleCloud__DatabasesController_create_cloudsql_instance.yml
@@ -0,0 +1,26 @@
+---
+description: Cloud SQL instance created
+category: Projects::GoogleCloud::DatabasesController
+action: create_cloudsql_instance
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373249_Projects__GoogleCloud__DatabasesController_error_enable_cloudsql_services.yml b/config/events/1662373249_Projects__GoogleCloud__DatabasesController_error_enable_cloudsql_services.yml
new file mode 100644
index 00000000000..a6fb46df4b8
--- /dev/null
+++ b/config/events/1662373249_Projects__GoogleCloud__DatabasesController_error_enable_cloudsql_services.yml
@@ -0,0 +1,26 @@
+---
+description: Error enabling Cloud SQL services
+category: Projects::GoogleCloud::DatabasesController
+action: error_enable_cloudsql_services
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/events/1662373254_Projects__GoogleCloud__DatabasesController_error_create_cloudsql_instance.yml b/config/events/1662373254_Projects__GoogleCloud__DatabasesController_error_create_cloudsql_instance.yml
new file mode 100644
index 00000000000..df9e28fabf5
--- /dev/null
+++ b/config/events/1662373254_Projects__GoogleCloud__DatabasesController_error_create_cloudsql_instance.yml
@@ -0,0 +1,26 @@
+---
+description: Error creating Cloud SQL instance
+category: Projects::GoogleCloud::DatabasesController
+action: error_create_cloudsql_instance
+label_description:
+property_description:
+value_description:
+extra_properties:
+identifiers:
+- project
+- user
+- namespace
+product_section: google_cloud
+product_stage: configure
+product_group: group::incubation
+product_category: cloud_seed
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96683"
+distributions:
+- ce
+- ee
+tiers:
+- free
+- premium
+- ultimate
+
diff --git a/config/feature_categories.yml b/config/feature_categories.yml
index 9b5f3de3f75..ca27ece9196 100644
--- a/config/feature_categories.yml
+++ b/config/feature_categories.yml
@@ -13,7 +13,6 @@
- application_performance
- attack_emulation
- audit_events
-- audit_reports
- authentication_and_authorization
- auto_devops
- backup_restore
@@ -116,7 +115,7 @@
- secret_detection
- secrets_management
- security_benchmarking
-- security_orchestration
+- security_policy_management
- service_desk
- service_ping
- snippets
diff --git a/config/feature_flags/development/add_timing_to_certain_cache_actions.yml b/config/feature_flags/development/add_timing_to_certain_cache_actions.yml
new file mode 100644
index 00000000000..c03e49dae8d
--- /dev/null
+++ b/config/feature_flags/development/add_timing_to_certain_cache_actions.yml
@@ -0,0 +1,8 @@
+---
+name: add_timing_to_certain_cache_actions
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94966
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371657
+milestone: '15.4'
+type: development
+group: group::code review
+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
index 233be4d930e..f5ec2473af8 100644
--- a/config/feature_flags/development/always_async_project_authorizations_refresh.yml
+++ b/config/feature_flags/development/always_async_project_authorizations_refresh.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367683
milestone: '15.3'
type: development
group: group::workspace
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/api_caching_branches.yml b/config/feature_flags/development/api_caching_branches.yml
deleted file mode 100644
index 310d643529e..00000000000
--- a/config/feature_flags/development/api_caching_branches.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: api_caching_branches
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61157
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/330371
-milestone: '13.12'
-type: development
-group: group::source code
-default_enabled: false
diff --git a/config/feature_flags/development/arkose_labs_signup_challenge.yml b/config/feature_flags/development/arkose_labs_signup_challenge.yml
new file mode 100644
index 00000000000..8b40ce5d029
--- /dev/null
+++ b/config/feature_flags/development/arkose_labs_signup_challenge.yml
@@ -0,0 +1,8 @@
+---
+name: arkose_labs_signup_challenge
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95560
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/370932
+milestone: '15.4'
+type: development
+group: group::anti-abuse
+default_enabled: false
diff --git a/config/feature_flags/development/async_after_approval.yml b/config/feature_flags/development/async_after_approval.yml
deleted file mode 100644
index db53454b88f..00000000000
--- a/config/feature_flags/development/async_after_approval.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: async_after_approval
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92520
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/368098
-milestone: '15.3'
-type: development
-group: group::code review
-default_enabled: false
diff --git a/config/feature_flags/development/auto_ban_user_on_excessive_projects_download.yml b/config/feature_flags/development/auto_ban_user_on_excessive_projects_download.yml
deleted file mode 100644
index d3883086088..00000000000
--- a/config/feature_flags/development/auto_ban_user_on_excessive_projects_download.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: auto_ban_user_on_excessive_projects_download
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87872
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/364782
-milestone: '15.2'
-type: development
-group: group::anti-abuse
-default_enabled: false
diff --git a/config/feature_flags/development/auto_ban_user_on_namespace_excessive_projects_download.yml b/config/feature_flags/development/auto_ban_user_on_namespace_excessive_projects_download.yml
deleted file mode 100644
index 6e14a8dd62c..00000000000
--- a/config/feature_flags/development/auto_ban_user_on_namespace_excessive_projects_download.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: auto_ban_user_on_namespace_excessive_projects_download
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91343
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367611
-milestone: '15.3'
-type: development
-group: group::anti-abuse
-default_enabled: false
diff --git a/config/feature_flags/development/block_emails_with_failures.yml b/config/feature_flags/development/block_emails_with_failures.yml
new file mode 100644
index 00000000000..c06d49346b3
--- /dev/null
+++ b/config/feature_flags/development/block_emails_with_failures.yml
@@ -0,0 +1,8 @@
+---
+name: block_emails_with_failures
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96902
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373159
+milestone: '15.4'
+type: development
+group: group::project management
+default_enabled: false
diff --git a/config/feature_flags/development/block_weak_passwords.yml b/config/feature_flags/development/block_weak_passwords.yml
new file mode 100644
index 00000000000..aaa8c2cac38
--- /dev/null
+++ b/config/feature_flags/development/block_weak_passwords.yml
@@ -0,0 +1,8 @@
+---
+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/bypass_batch_pop_queueing_for_merge_trains.yml b/config/feature_flags/development/bypass_batch_pop_queueing_for_merge_trains.yml
new file mode 100644
index 00000000000..4517bd5360e
--- /dev/null
+++ b/config/feature_flags/development/bypass_batch_pop_queueing_for_merge_trains.yml
@@ -0,0 +1,8 @@
+---
+name: bypass_batch_pop_queueing_for_merge_trains
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96793
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372366
+milestone: '15.4'
+type: development
+group: group::scalability
+default_enabled: false
diff --git a/config/feature_flags/development/cache_issue_sums.yml b/config/feature_flags/development/cache_issue_sums.yml
new file mode 100644
index 00000000000..7b8bfc44ce2
--- /dev/null
+++ b/config/feature_flags/development/cache_issue_sums.yml
@@ -0,0 +1,8 @@
+---
+name: cache_issue_sums
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95048
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365940
+milestone: '15.4'
+type: development
+group: group::product planning
+default_enabled: false
diff --git a/config/feature_flags/development/ci_docker_image_pull_policy.yml b/config/feature_flags/development/ci_docker_image_pull_policy.yml
deleted file mode 100644
index 5bdcdf03d27..00000000000
--- a/config/feature_flags/development/ci_docker_image_pull_policy.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_docker_image_pull_policy
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85588
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/363186
-milestone: '15.1'
-type: development
-group: group::pipeline authoring
-default_enabled: true
diff --git a/config/feature_flags/development/ci_limit_active_jobs_early.yml b/config/feature_flags/development/ci_limit_active_jobs_early.yml
new file mode 100644
index 00000000000..b7dba0f81e9
--- /dev/null
+++ b/config/feature_flags/development/ci_limit_active_jobs_early.yml
@@ -0,0 +1,8 @@
+---
+name: ci_limit_active_jobs_early
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97700
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373284
+milestone: '15.4'
+type: development
+group: group::pipeline execution
+default_enabled: false
diff --git a/config/feature_flags/development/ci_limit_complete_hierarchy_size.yml b/config/feature_flags/development/ci_limit_complete_hierarchy_size.yml
new file mode 100644
index 00000000000..ad0dd85a25a
--- /dev/null
+++ b/config/feature_flags/development/ci_limit_complete_hierarchy_size.yml
@@ -0,0 +1,8 @@
+---
+name: ci_limit_complete_hierarchy_size
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95857
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373719
+milestone: '15.4'
+type: development
+group: group::pipeline execution
+default_enabled: false
diff --git a/config/feature_flags/development/ci_new_public_oss_cost_factor.yml b/config/feature_flags/development/ci_new_public_oss_cost_factor.yml
deleted file mode 100644
index 20ab9bd4509..00000000000
--- a/config/feature_flags/development/ci_new_public_oss_cost_factor.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_new_public_oss_cost_factor
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93046
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/369113
-milestone: '15.3'
-type: development
-group: group::pipeline execution
-default_enabled: false
diff --git a/config/feature_flags/development/ci_project_pipeline_config_refactoring.yml b/config/feature_flags/development/ci_project_pipeline_config_refactoring.yml
new file mode 100644
index 00000000000..0338b81caf7
--- /dev/null
+++ b/config/feature_flags/development/ci_project_pipeline_config_refactoring.yml
@@ -0,0 +1,8 @@
+---
+name: ci_project_pipeline_config_refactoring
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97240
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372867
+milestone: '15.4'
+type: development
+group: group::pipeline authoring
+default_enabled: false
diff --git a/config/feature_flags/development/ci_requeue_with_dag_object_hierarchy.yml b/config/feature_flags/development/ci_requeue_with_dag_object_hierarchy.yml
new file mode 100644
index 00000000000..5e27510629c
--- /dev/null
+++ b/config/feature_flags/development/ci_requeue_with_dag_object_hierarchy.yml
@@ -0,0 +1,8 @@
+---
+name: ci_requeue_with_dag_object_hierarchy
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97156
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373148
+milestone: '15.4'
+type: development
+group: group::pipeline authoring
+default_enabled: false
diff --git a/config/feature_flags/development/ci_stop_expanding_file_vars_for_runners.yml b/config/feature_flags/development/ci_stop_expanding_file_vars_for_runners.yml
index e9f378288d9..a78290b65d6 100644
--- a/config/feature_flags/development/ci_stop_expanding_file_vars_for_runners.yml
+++ b/config/feature_flags/development/ci_stop_expanding_file_vars_for_runners.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/369907
milestone: '15.3'
type: development
group: group::pipeline authoring
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/ci_update_unlocked_pipeline_artifacts.yml b/config/feature_flags/development/ci_update_unlocked_pipeline_artifacts.yml
new file mode 100644
index 00000000000..ffcd5352751
--- /dev/null
+++ b/config/feature_flags/development/ci_update_unlocked_pipeline_artifacts.yml
@@ -0,0 +1,8 @@
+---
+name: ci_update_unlocked_pipeline_artifacts
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97228
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372835
+milestone: '15.4'
+type: development
+group: group::pipeline insights
+default_enabled: false
diff --git a/config/feature_flags/development/ci_variable_for_group_gitlab_deploy_token.yml b/config/feature_flags/development/ci_variable_for_group_gitlab_deploy_token.yml
deleted file mode 100644
index 155a6c385fc..00000000000
--- a/config/feature_flags/development/ci_variable_for_group_gitlab_deploy_token.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_variable_for_group_gitlab_deploy_token
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88696
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/363621
-milestone: '15.1'
-type: development
-group: group::pipeline authoring
-default_enabled: true
diff --git a/config/feature_flags/development/ci_variables_refactoring_to_variable.yml b/config/feature_flags/development/ci_variables_refactoring_to_variable.yml
new file mode 100644
index 00000000000..131df28d104
--- /dev/null
+++ b/config/feature_flags/development/ci_variables_refactoring_to_variable.yml
@@ -0,0 +1,8 @@
+---
+name: ci_variables_refactoring_to_variable
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95390
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371559
+milestone: '15.4'
+type: development
+group: group::pipeline authoring
+default_enabled: false
diff --git a/config/feature_flags/development/container_registry_legacy_authentication_for_deploy_tokens.yml b/config/feature_flags/development/container_registry_legacy_authentication_for_deploy_tokens.yml
deleted file mode 100644
index fefc84ed0a0..00000000000
--- a/config/feature_flags/development/container_registry_legacy_authentication_for_deploy_tokens.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: container_registry_legacy_authentication_for_deploy_tokens
-introduced_by_url: https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests/2470
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365968
-milestone: '15.1'
-type: development
-group: group::package
-default_enabled: false
diff --git a/config/feature_flags/development/contribution_analytics_optimized_base_query.yml b/config/feature_flags/development/contribution_analytics_optimized_base_query.yml
index 0e8697fb206..05ee44a7ee2 100644
--- a/config/feature_flags/development/contribution_analytics_optimized_base_query.yml
+++ b/config/feature_flags/development/contribution_analytics_optimized_base_query.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367090
milestone: '15.2'
type: development
group: group::optimize
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/convert_diff_to_utf8_with_replacement_symbol.yml b/config/feature_flags/development/convert_diff_to_utf8_with_replacement_symbol.yml
deleted file mode 100644
index 1d0f64d9ac2..00000000000
--- a/config/feature_flags/development/convert_diff_to_utf8_with_replacement_symbol.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: convert_diff_to_utf8_with_replacement_symbol
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79996
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/354526
-milestone: '14.9'
-type: development
-group: group::code review
-default_enabled: false
diff --git a/config/feature_flags/development/cube_api_proxy.yml b/config/feature_flags/development/cube_api_proxy.yml
new file mode 100644
index 00000000000..06dcefb1303
--- /dev/null
+++ b/config/feature_flags/development/cube_api_proxy.yml
@@ -0,0 +1,8 @@
+---
+name: cube_api_proxy
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96250
+rollout_issue_url:
+milestone: '15.4'
+type: development
+group: group::product_analytics
+default_enabled: false
diff --git a/config/feature_flags/development/datadog_integration_logs_collection.yml b/config/feature_flags/development/datadog_integration_logs_collection.yml
deleted file mode 100644
index 3e65b70d0e6..00000000000
--- a/config/feature_flags/development/datadog_integration_logs_collection.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: datadog_integration_logs_collection
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74725
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/346339
-milestone: '14.8'
-type: development
-group: group::integrations
-default_enabled: true
diff --git a/config/feature_flags/development/detect_cross_database_modification.yml b/config/feature_flags/development/detect_cross_database_modification.yml
deleted file mode 100644
index 7f74e136291..00000000000
--- a/config/feature_flags/development/detect_cross_database_modification.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: detect_cross_database_modification
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73316
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/344620
-milestone: '14.5'
-type: development
-group: group::sharding
-default_enabled: false
diff --git a/config/feature_flags/development/dora_configuration.yml b/config/feature_flags/development/dora_configuration.yml
new file mode 100644
index 00000000000..38a050571d8
--- /dev/null
+++ b/config/feature_flags/development/dora_configuration.yml
@@ -0,0 +1,8 @@
+---
+name: dora_configuration
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96561"
+rollout_issue_url: "https://gitlab.com/gitlab-org/gitlab/-/issues/372545"
+milestone: '15.4'
+type: development
+group: group::optimize
+default_enabled: false
diff --git a/config/feature_flags/development/draft_quick_action_non_toggle.yml b/config/feature_flags/development/draft_quick_action_non_toggle.yml
new file mode 100644
index 00000000000..4d28b61f3bf
--- /dev/null
+++ b/config/feature_flags/development/draft_quick_action_non_toggle.yml
@@ -0,0 +1,8 @@
+---
+name: draft_quick_action_non_toggle
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92654
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/368610
+milestone: '15.4'
+type: development
+group: group::code review
+default_enabled: false
diff --git a/config/feature_flags/development/epic_widget_edit_confirmation.yml b/config/feature_flags/development/epic_widget_edit_confirmation.yml
new file mode 100644
index 00000000000..6c92ef44e2f
--- /dev/null
+++ b/config/feature_flags/development/epic_widget_edit_confirmation.yml
@@ -0,0 +1,8 @@
+---
+name: epic_widget_edit_confirmation
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96872
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372429
+milestone: '15.4'
+type: development
+group: group::product planning
+default_enabled: false
diff --git a/config/feature_flags/development/error_tracking_sentry_limit.yml b/config/feature_flags/development/error_tracking_sentry_limit.yml
new file mode 100644
index 00000000000..75a32fa2114
--- /dev/null
+++ b/config/feature_flags/development/error_tracking_sentry_limit.yml
@@ -0,0 +1,8 @@
+---
+name: error_tracking_sentry_limit
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84209
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372427
+milestone: '15.4'
+type: development
+group: group::observability
+default_enabled: false
diff --git a/config/feature_flags/development/escape_gitaly_refs.yml b/config/feature_flags/development/escape_gitaly_refs.yml
new file mode 100644
index 00000000000..b42cc4c07e5
--- /dev/null
+++ b/config/feature_flags/development/escape_gitaly_refs.yml
@@ -0,0 +1,8 @@
+---
+name: escape_gitaly_refs
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91058
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366437
+milestone: '15.2'
+type: development
+group: group::source code
+default_enabled: true
diff --git a/config/feature_flags/development/etag_merge_request_diff_batches.yml b/config/feature_flags/development/etag_merge_request_diff_batches.yml
deleted file mode 100644
index 8cd3ba8637a..00000000000
--- a/config/feature_flags/development/etag_merge_request_diff_batches.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: etag_merge_request_diff_batches
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93953
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/369488
-milestone: '15.3'
-type: development
-group: group::code review
-default_enabled: false
diff --git a/config/feature_flags/development/execute_build_hooks_inline.yml b/config/feature_flags/development/execute_build_hooks_inline.yml
deleted file mode 100644
index 0389fca3bb1..00000000000
--- a/config/feature_flags/development/execute_build_hooks_inline.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: execute_build_hooks_inline
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93665
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/370387
-milestone: '15.3'
-type: development
-group: group::integrations
-default_enabled: false
diff --git a/config/feature_flags/development/extract_mr_diff_deletions.yml b/config/feature_flags/development/extract_mr_diff_deletions.yml
new file mode 100644
index 00000000000..24067f95074
--- /dev/null
+++ b/config/feature_flags/development/extract_mr_diff_deletions.yml
@@ -0,0 +1,8 @@
+---
+name: extract_mr_diff_deletions
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96455
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372060
+milestone: '15.4'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/config/feature_flags/development/gitlab_shell_jwt_token.yml b/config/feature_flags/development/gitlab_shell_jwt_token.yml
new file mode 100644
index 00000000000..7cb6da2b49f
--- /dev/null
+++ b/config/feature_flags/development/gitlab_shell_jwt_token.yml
@@ -0,0 +1,8 @@
+---
+name: gitlab_shell_jwt_token
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86148
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/360808
+milestone: '15.3'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/config/feature_flags/development/global_search_custom_slis.yml b/config/feature_flags/development/global_search_custom_slis.yml
new file mode 100644
index 00000000000..6dd7cfb12f0
--- /dev/null
+++ b/config/feature_flags/development/global_search_custom_slis.yml
@@ -0,0 +1,8 @@
+---
+name: global_search_custom_slis
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95182
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372107
+milestone: '15.4'
+type: development
+group: group::application performance
+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
new file mode 100644
index 00000000000..d1637ad692c
--- /dev/null
+++ b/config/feature_flags/development/global_search_error_rate_sli.yml
@@ -0,0 +1,8 @@
+---
+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/graphql_job_app.yml b/config/feature_flags/development/graphql_job_app.yml
new file mode 100644
index 00000000000..a0f0cb71e17
--- /dev/null
+++ b/config/feature_flags/development/graphql_job_app.yml
@@ -0,0 +1,8 @@
+---
+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
new file mode 100644
index 00000000000..e289ad9af50
--- /dev/null
+++ b/config/feature_flags/development/graphql_keyset_pagination_without_next_page_query.yml
@@ -0,0 +1,8 @@
+---
+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: false
diff --git a/config/feature_flags/development/group_ip_restrictions_allow_global.yml b/config/feature_flags/development/group_ip_restrictions_allow_global.yml
deleted file mode 100644
index 87cfa5e8b1b..00000000000
--- a/config/feature_flags/development/group_ip_restrictions_allow_global.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: group_ip_restrictions_allow_global
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87579
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/362603
-milestone: '15.1'
-type: development
-group: group::source code
-default_enabled: false
diff --git a/config/feature_flags/development/group_level_protected_environment_settings_permission.yml b/config/feature_flags/development/group_level_protected_environment_settings_permission.yml
deleted file mode 100644
index bbcfe24ba72..00000000000
--- a/config/feature_flags/development/group_level_protected_environment_settings_permission.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: group_level_protected_environment_settings_permission
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92801
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/369873
-milestone: '15.3'
-type: development
-group: group::release
-default_enabled: true
diff --git a/config/feature_flags/development/group_overview_tabs_vue.yml b/config/feature_flags/development/group_overview_tabs_vue.yml
new file mode 100644
index 00000000000..4c54ab31b53
--- /dev/null
+++ b/config/feature_flags/development/group_overview_tabs_vue.yml
@@ -0,0 +1,8 @@
+---
+name: group_overview_tabs_vue
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95850
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/370872
+milestone: '15.4'
+type: development
+group: group::workspace
+default_enabled: false
diff --git a/config/feature_flags/development/hash_oauth_secrets.yml b/config/feature_flags/development/hash_oauth_secrets.yml
new file mode 100644
index 00000000000..7730d319bab
--- /dev/null
+++ b/config/feature_flags/development/hash_oauth_secrets.yml
@@ -0,0 +1,8 @@
+---
+name: hash_oauth_secrets
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96252
+rollout_issue_url:
+milestone: '15.4'
+type: development
+group: group::authentication and authorization
+default_enabled: false
diff --git a/config/feature_flags/development/highlight_diffs_renewable_expiration.yml b/config/feature_flags/development/highlight_diffs_renewable_expiration.yml
new file mode 100644
index 00000000000..80635b96240
--- /dev/null
+++ b/config/feature_flags/development/highlight_diffs_renewable_expiration.yml
@@ -0,0 +1,8 @@
+---
+name: highlight_diffs_renewable_expiration
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95356
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/370728
+milestone: '15.3'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/config/feature_flags/development/highlight_diffs_short_renewable_expiration.yml b/config/feature_flags/development/highlight_diffs_short_renewable_expiration.yml
new file mode 100644
index 00000000000..1f20678b4a6
--- /dev/null
+++ b/config/feature_flags/development/highlight_diffs_short_renewable_expiration.yml
@@ -0,0 +1,8 @@
+---
+name: highlight_diffs_short_renewable_expiration
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95356
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/370728
+milestone: '15.3'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/config/feature_flags/development/improved_mergeability_checks.yml b/config/feature_flags/development/improved_mergeability_checks.yml
deleted file mode 100644
index 83450ffa16f..00000000000
--- a/config/feature_flags/development/improved_mergeability_checks.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: improved_mergeability_checks
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68312
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342386
-milestone: '14.4'
-type: development
-group: group::code review
-default_enabled: false
diff --git a/config/feature_flags/development/inactive_projects_deletion.yml b/config/feature_flags/development/inactive_projects_deletion.yml
deleted file mode 100644
index e9bb91f62cc..00000000000
--- a/config/feature_flags/development/inactive_projects_deletion.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: inactive_projects_deletion
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/357968
-milestone: '15.0'
-type: development
-group: group::compliance
-default_enabled: false
diff --git a/config/feature_flags/development/include_groups_from_group_shares_in_group_transfer_locations.yml b/config/feature_flags/development/include_groups_from_group_shares_in_group_transfer_locations.yml
new file mode 100644
index 00000000000..f92a6b2b1b0
--- /dev/null
+++ b/config/feature_flags/development/include_groups_from_group_shares_in_group_transfer_locations.yml
@@ -0,0 +1,8 @@
+---
+name: include_groups_from_group_shares_in_group_transfer_locations
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96347
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371961
+milestone: '15.4'
+type: development
+group: group::workspace
+default_enabled: false
diff --git a/config/feature_flags/development/include_groups_from_group_shares_in_project_transfer_locations.yml b/config/feature_flags/development/include_groups_from_group_shares_in_project_transfer_locations.yml
deleted file mode 100644
index 3db60cfd2d7..00000000000
--- a/config/feature_flags/development/include_groups_from_group_shares_in_project_transfer_locations.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: include_groups_from_group_shares_in_project_transfer_locations
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90127
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366305
-milestone: '15.2'
-type: development
-group: group::workspace
-default_enabled: false
diff --git a/config/feature_flags/development/incubation_5mp_google_cloud.yml b/config/feature_flags/development/incubation_5mp_google_cloud.yml
index b687a656b40..1b3ba503f5d 100644
--- a/config/feature_flags/development/incubation_5mp_google_cloud.yml
+++ b/config/feature_flags/development/incubation_5mp_google_cloud.yml
@@ -1,7 +1,7 @@
---
name: incubation_5mp_google_cloud
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70715
-rollout_issue_url:
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371332
milestone: '14.3'
type: development
group: group::incubation
diff --git a/config/feature_flags/development/invitation_flow_enforcement_setting.yml b/config/feature_flags/development/invitation_flow_enforcement_setting.yml
new file mode 100644
index 00000000000..39da6c40bed
--- /dev/null
+++ b/config/feature_flags/development/invitation_flow_enforcement_setting.yml
@@ -0,0 +1,8 @@
+---
+name: invitation_flow_enforcement_setting
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92218
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367666
+milestone: '15.4'
+type: development
+group: group::workspace
+default_enabled: false
diff --git a/config/feature_flags/development/markdown_dollar_math.yml b/config/feature_flags/development/markdown_dollar_math.yml
new file mode 100644
index 00000000000..842837ea688
--- /dev/null
+++ b/config/feature_flags/development/markdown_dollar_math.yml
@@ -0,0 +1,8 @@
+---
+name: markdown_dollar_math
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94111
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371180
+milestone: '15.4'
+type: development
+group: group::project management
+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
new file mode 100644
index 00000000000..756a931b3a1
--- /dev/null
+++ b/config/feature_flags/development/maven_central_request_forwarding.yml
@@ -0,0 +1,8 @@
+---
+name: maven_central_request_forwarding
+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
+default_enabled: false
diff --git a/config/feature_flags/development/mergeability_checks_logger.yml b/config/feature_flags/development/mergeability_checks_logger.yml
new file mode 100644
index 00000000000..3476d6f2133
--- /dev/null
+++ b/config/feature_flags/development/mergeability_checks_logger.yml
@@ -0,0 +1,8 @@
+---
+name: mergeability_checks_logger
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96128
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371717
+milestone: '15.4'
+type: development
+group: group::code review
+default_enabled: false
diff --git a/config/feature_flags/development/ml_experiment_tracking.yml b/config/feature_flags/development/ml_experiment_tracking.yml
new file mode 100644
index 00000000000..2749cbc3fc1
--- /dev/null
+++ b/config/feature_flags/development/ml_experiment_tracking.yml
@@ -0,0 +1,8 @@
+---
+name: ml_experiment_tracking
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95689
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371669
+milestone: '15.4'
+type: development
+group: group::incubation
+default_enabled: false
diff --git a/config/feature_flags/development/mr_attention_requests.yml b/config/feature_flags/development/mr_attention_requests.yml
deleted file mode 100644
index b80300d1acf..00000000000
--- a/config/feature_flags/development/mr_attention_requests.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: mr_attention_requests
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72773
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343528
-milestone: '14.4'
-type: development
-group: group::code review
-default_enabled: false
diff --git a/config/feature_flags/development/new_navbar_layout.yml b/config/feature_flags/development/new_navbar_layout.yml
new file mode 100644
index 00000000000..2d212922fcc
--- /dev/null
+++ b/config/feature_flags/development/new_navbar_layout.yml
@@ -0,0 +1,8 @@
+---
+name: new_navbar_layout
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96853
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373078
+milestone: '15.4'
+type: development
+group: group::foundations
+default_enabled: true
diff --git a/config/feature_flags/development/observability_group_tab.yml b/config/feature_flags/development/observability_group_tab.yml
new file mode 100644
index 00000000000..b588a74e7d0
--- /dev/null
+++ b/config/feature_flags/development/observability_group_tab.yml
@@ -0,0 +1,8 @@
+---
+name: observability_group_tab
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96374
+rollout_issue_url:
+milestone: '15.3'
+type: development
+group: group::observability
+default_enabled: false
diff --git a/config/feature_flags/development/order_builds_for_group_runner.yml b/config/feature_flags/development/order_builds_for_group_runner.yml
new file mode 100644
index 00000000000..50f9a301ad6
--- /dev/null
+++ b/config/feature_flags/development/order_builds_for_group_runner.yml
@@ -0,0 +1,8 @@
+---
+name: order_builds_for_group_runner
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94815
+rollout_issue_url:
+milestone: '15.4'
+type: development
+group: group::pipeline execution
+default_enabled: true
diff --git a/config/feature_flags/development/prevent_outdated_deployment_jobs.yml b/config/feature_flags/development/prevent_outdated_deployment_jobs.yml
new file mode 100644
index 00000000000..5b92874dfd7
--- /dev/null
+++ b/config/feature_flags/development/prevent_outdated_deployment_jobs.yml
@@ -0,0 +1,8 @@
+---
+name: prevent_outdated_deployment_jobs
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97171
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/370628
+milestone: '15.4'
+type: development
+group: group::release
+default_enabled: false
diff --git a/config/feature_flags/development/process_issue_closure_in_background.yml b/config/feature_flags/development/process_issue_closure_in_background.yml
new file mode 100644
index 00000000000..6a97cbf888e
--- /dev/null
+++ b/config/feature_flags/development/process_issue_closure_in_background.yml
@@ -0,0 +1,8 @@
+---
+name: process_issue_closure_in_background
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94981
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371024
+milestone: '15.4'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/config/feature_flags/development/product_intelligence_database_event_tracking.yml b/config/feature_flags/development/product_intelligence_database_event_tracking.yml
index 83a65398e87..545cdc47f2b 100644
--- a/config/feature_flags/development/product_intelligence_database_event_tracking.yml
+++ b/config/feature_flags/development/product_intelligence_database_event_tracking.yml
@@ -2,7 +2,6 @@
name: product_intelligence_database_event_tracking
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92079
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/368976
-rollout_issue_url:
milestone: '15.3'
type: development
group: group::product intelligence
diff --git a/config/feature_flags/development/query_analyzer_gitlab_schema_metrics.yml b/config/feature_flags/development/query_analyzer_gitlab_schema_metrics.yml
deleted file mode 100644
index b784105368c..00000000000
--- a/config/feature_flags/development/query_analyzer_gitlab_schema_metrics.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: query_analyzer_gitlab_schema_metrics
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73839
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345034
-milestone: '14.5'
-type: development
-group: group::sharding
-default_enabled: false
diff --git a/config/feature_flags/development/read_package_policy_rule.yml b/config/feature_flags/development/read_package_policy_rule.yml
new file mode 100644
index 00000000000..151c5a8c0b5
--- /dev/null
+++ b/config/feature_flags/development/read_package_policy_rule.yml
@@ -0,0 +1,8 @@
+---
+name: read_package_policy_rule
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90963
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/366711
+milestone: '15.4'
+type: development
+group: group::package
+default_enabled: false
diff --git a/config/feature_flags/development/rebalance_issues.yml b/config/feature_flags/development/rebalance_issues.yml
deleted file mode 100644
index 5651b02b073..00000000000
--- a/config/feature_flags/development/rebalance_issues.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: rebalance_issues
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40124
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/239344
-milestone: '13.4'
-type: development
-group: group::project management
-default_enabled: true
diff --git a/config/feature_flags/development/rely_on_protected_branches_cache.yml b/config/feature_flags/development/rely_on_protected_branches_cache.yml
new file mode 100644
index 00000000000..5154d4cee08
--- /dev/null
+++ b/config/feature_flags/development/rely_on_protected_branches_cache.yml
@@ -0,0 +1,8 @@
+---
+name: rely_on_protected_branches_cache
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92937
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/370608
+milestone: '15.4'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/config/feature_flags/development/remove_extra_primary_submenu_options.yml b/config/feature_flags/development/remove_extra_primary_submenu_options.yml
new file mode 100644
index 00000000000..dda22c5d57e
--- /dev/null
+++ b/config/feature_flags/development/remove_extra_primary_submenu_options.yml
@@ -0,0 +1,8 @@
+---
+name: remove_extra_primary_submenu_options
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96931
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373078
+milestone: '15.4'
+type: development
+group: group::foundations
+default_enabled: true
diff --git a/config/feature_flags/development/remove_user_attributes_groups.yml b/config/feature_flags/development/remove_user_attributes_groups.yml
new file mode 100644
index 00000000000..069d83455f9
--- /dev/null
+++ b/config/feature_flags/development/remove_user_attributes_groups.yml
@@ -0,0 +1,8 @@
+---
+name: remove_user_attributes_groups
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97520
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372047
+milestone: '15.4'
+type: development
+group: group::code review
+default_enabled: false
diff --git a/config/feature_flags/development/remove_user_attributes_projects.yml b/config/feature_flags/development/remove_user_attributes_projects.yml
new file mode 100644
index 00000000000..fc1beb42a73
--- /dev/null
+++ b/config/feature_flags/development/remove_user_attributes_projects.yml
@@ -0,0 +1,8 @@
+---
+name: remove_user_attributes_projects
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97520
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372047
+milestone: '15.4'
+type: development
+group: group::code review
+default_enabled: false
diff --git a/config/feature_flags/development/report_artifact_build_completed_metrics_on_build_completion.yml b/config/feature_flags/development/report_artifact_build_completed_metrics_on_build_completion.yml
deleted file mode 100644
index 76b6c8c6b2f..00000000000
--- a/config/feature_flags/development/report_artifact_build_completed_metrics_on_build_completion.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: report_artifact_build_completed_metrics_on_build_completion
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80334
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/369500
-milestone: '15.3'
-type: development
-group: group::static analysis
-default_enabled: false
diff --git a/config/feature_flags/development/restyle_login_page.yml b/config/feature_flags/development/restyle_login_page.yml
index ed3ae3ef6ea..bfe99590e6e 100644
--- a/config/feature_flags/development/restyle_login_page.yml
+++ b/config/feature_flags/development/restyle_login_page.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/368387
milestone: '15.2'
type: development
group: group::authentication and authorization
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/rpm_packages.yml b/config/feature_flags/development/rpm_packages.yml
new file mode 100644
index 00000000000..a342f1203f7
--- /dev/null
+++ b/config/feature_flags/development/rpm_packages.yml
@@ -0,0 +1,8 @@
+---
+name: rpm_packages
+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
+default_enabled: false
diff --git a/config/feature_flags/development/run_pipeline_graphql.yml b/config/feature_flags/development/run_pipeline_graphql.yml
new file mode 100644
index 00000000000..78d8afbbee5
--- /dev/null
+++ b/config/feature_flags/development/run_pipeline_graphql.yml
@@ -0,0 +1,8 @@
+---
+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/s3_omit_multipart_urls.yml b/config/feature_flags/development/s3_omit_multipart_urls.yml
deleted file mode 100644
index 92d30601739..00000000000
--- a/config/feature_flags/development/s3_omit_multipart_urls.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: s3_omit_multipart_urls
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85306
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/359718
-milestone: '14.10'
-type: development
-group: group::package
-default_enabled: true
diff --git a/config/feature_flags/development/seat_count_alerts.yml b/config/feature_flags/development/seat_count_alerts.yml
deleted file mode 100644
index 9b2f3a2ef55..00000000000
--- a/config/feature_flags/development/seat_count_alerts.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: seat_count_alerts
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89204
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/362041
-milestone: '15.1'
-type: development
-group: group::purchase
-default_enabled: false
diff --git a/config/feature_flags/development/shimo_integration.yml b/config/feature_flags/development/shimo_integration.yml
deleted file mode 100644
index 28c0a7859bc..00000000000
--- a/config/feature_flags/development/shimo_integration.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: shimo_integration
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73129
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345356
-milestone: '14.5'
-type: development
-group: group::integrations
-default_enabled: false
diff --git a/config/feature_flags/development/skip_checking_namespace_in_query.yml b/config/feature_flags/development/skip_checking_namespace_in_query.yml
new file mode 100644
index 00000000000..2b9e3cbfe0b
--- /dev/null
+++ b/config/feature_flags/development/skip_checking_namespace_in_query.yml
@@ -0,0 +1,8 @@
+---
+name: skip_checking_namespace_in_query
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96559
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/370742
+milestone: '15.4'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/config/feature_flags/development/tag_list_keyset_pagination.yml b/config/feature_flags/development/tag_list_keyset_pagination.yml
deleted file mode 100644
index 52c21e22d9f..00000000000
--- a/config/feature_flags/development/tag_list_keyset_pagination.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: tag_list_keyset_pagination
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74239
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/345595
-milestone: '14.5'
-type: development
-group: group::source code
-default_enabled: true
diff --git a/config/feature_flags/development/track_agent_users_using_ci_tunnel.yml b/config/feature_flags/development/track_agent_users_using_ci_tunnel.yml
deleted file mode 100644
index 0a00babc2db..00000000000
--- a/config/feature_flags/development/track_agent_users_using_ci_tunnel.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: track_agent_users_using_ci_tunnel
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92300
-rollout_issue_url:
-milestone: '15.3'
-type: development
-group: group::configure
-default_enabled: false
diff --git a/config/feature_flags/development/track_work_items_activity.yml b/config/feature_flags/development/track_work_items_activity.yml
index e4614f2d5e2..3727bca1078 100644
--- a/config/feature_flags/development/track_work_items_activity.yml
+++ b/config/feature_flags/development/track_work_items_activity.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/352903
milestone: '14.9'
type: development
group: group::project management
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/traversal_ids_btree.yml b/config/feature_flags/development/traversal_ids_btree.yml
deleted file mode 100644
index aaecafe04ae..00000000000
--- a/config/feature_flags/development/traversal_ids_btree.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: traversal_ids_btree
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69535
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342871
-milestone: '14.5'
-type: development
-group: group::access
-default_enabled: false
diff --git a/config/feature_flags/development/ultimate_feature_removal_banner.yml b/config/feature_flags/development/ultimate_feature_removal_banner.yml
new file mode 100644
index 00000000000..14d7b4921c1
--- /dev/null
+++ b/config/feature_flags/development/ultimate_feature_removal_banner.yml
@@ -0,0 +1,8 @@
+---
+name: ultimate_feature_removal_banner
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94271
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371690
+milestone: '15.4'
+type: development
+group: group::workspace
+default_enabled: false
diff --git a/config/feature_flags/development/usage_data_ci_i_testing_test_report_uploaded.yml b/config/feature_flags/development/usage_data_ci_i_testing_test_report_uploaded.yml
new file mode 100644
index 00000000000..1635427485b
--- /dev/null
+++ b/config/feature_flags/development/usage_data_ci_i_testing_test_report_uploaded.yml
@@ -0,0 +1,8 @@
+---
+name: usage_data_ci_i_testing_test_report_uploaded
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95112
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339720
+milestone: '15.4'
+type: development
+group: group::pipeline insights
+default_enabled: false
diff --git a/config/feature_flags/development/usage_data_i_code_review_user_gitlab_cli_api_request.yml b/config/feature_flags/development/usage_data_i_code_review_user_gitlab_cli_api_request.yml
deleted file mode 100644
index 898c19a34a9..00000000000
--- a/config/feature_flags/development/usage_data_i_code_review_user_gitlab_cli_api_request.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: usage_data_i_code_review_user_gitlab_cli_api_request
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83464
-rollout_issue_url:
-milestone: '14.10'
-type: development
-group: group::code review
-default_enabled: true
diff --git a/config/feature_flags/development/usage_data_i_code_review_user_jetbrains_api_request.yml b/config/feature_flags/development/usage_data_i_code_review_user_jetbrains_api_request.yml
deleted file mode 100644
index 3ab01c78a28..00000000000
--- a/config/feature_flags/development/usage_data_i_code_review_user_jetbrains_api_request.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: usage_data_i_code_review_user_jetbrains_api_request
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78713
-rollout_issue_url:
-milestone: '14.8'
-type: development
-group: group::code review
-default_enabled: true
diff --git a/config/feature_flags/development/usage_quotas_for_all_editions.yml b/config/feature_flags/development/usage_quotas_for_all_editions.yml
new file mode 100644
index 00000000000..d4e4116542a
--- /dev/null
+++ b/config/feature_flags/development/usage_quotas_for_all_editions.yml
@@ -0,0 +1,8 @@
+---
+name: usage_quotas_for_all_editions
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96063
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371639
+milestone: '15.4'
+type: development
+group: group::utilization
+default_enabled: false
diff --git a/config/feature_flags/development/use_gitaly_pagination_for_refs.yml b/config/feature_flags/development/use_gitaly_pagination_for_refs.yml
new file mode 100644
index 00000000000..f44233e8d0b
--- /dev/null
+++ b/config/feature_flags/development/use_gitaly_pagination_for_refs.yml
@@ -0,0 +1,8 @@
+---
+name: use_gitaly_pagination_for_refs
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96448
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372049
+milestone: '15.4'
+type: development
+group: group::source code
+default_enabled: true
diff --git a/config/feature_flags/development/use_pipeline_wizard_for_pages.yml b/config/feature_flags/development/use_pipeline_wizard_for_pages.yml
index 10d4478934e..2de1b952f95 100644
--- a/config/feature_flags/development/use_pipeline_wizard_for_pages.yml
+++ b/config/feature_flags/development/use_pipeline_wizard_for_pages.yml
@@ -2,7 +2,7 @@
name: use_pipeline_wizard_for_pages
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78276
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/349095
-milestone: '15.3'
+milestone: '15.4'
type: development
group: group::incubation
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/use_vsa_aggregated_tables.yml b/config/feature_flags/development/use_vsa_aggregated_tables.yml
deleted file mode 100644
index 89c24181eb1..00000000000
--- a/config/feature_flags/development/use_vsa_aggregated_tables.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: use_vsa_aggregated_tables
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72978
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/343429
-milestone: '14.5'
-type: development
-group: group::optimize
-default_enabled: true
diff --git a/config/feature_flags/development/user_destroy_with_limited_execution_time_worker.yml b/config/feature_flags/development/user_destroy_with_limited_execution_time_worker.yml
new file mode 100644
index 00000000000..9eacfc019ac
--- /dev/null
+++ b/config/feature_flags/development/user_destroy_with_limited_execution_time_worker.yml
@@ -0,0 +1,8 @@
+---
+name: user_destroy_with_limited_execution_time_worker
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97141
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373138
+milestone: '15.4'
+type: development
+group: group::authentication and authorization
+default_enabled: false
diff --git a/config/feature_flags/development/vscode_web_ide.yml b/config/feature_flags/development/vscode_web_ide.yml
new file mode 100644
index 00000000000..3d29ae40e7c
--- /dev/null
+++ b/config/feature_flags/development/vscode_web_ide.yml
@@ -0,0 +1,8 @@
+---
+name: vscode_web_ide
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95169
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371084
+milestone: '15.4'
+type: development
+group: group::editor
+default_enabled: false
diff --git a/config/feature_flags/development/webui_members_inherited_users.yml b/config/feature_flags/development/webui_members_inherited_users.yml
new file mode 100644
index 00000000000..14704fd8341
--- /dev/null
+++ b/config/feature_flags/development/webui_members_inherited_users.yml
@@ -0,0 +1,8 @@
+---
+name: webui_members_inherited_users
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83214
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/364078
+milestone: '15.4'
+type: development
+group: group::workspace
+default_enabled: false
diff --git a/config/feature_flags/development/wiki_find_page_with_normal_repository_rpcs.yml b/config/feature_flags/development/wiki_find_page_with_normal_repository_rpcs.yml
new file mode 100644
index 00000000000..bad0578d50a
--- /dev/null
+++ b/config/feature_flags/development/wiki_find_page_with_normal_repository_rpcs.yml
@@ -0,0 +1,8 @@
+---
+name: wiki_find_page_with_normal_repository_rpcs
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95897
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371487
+milestone: '15.4'
+type: development
+group: group::gitaly
+default_enabled: false
diff --git a/config/feature_flags/experiment/combined_registration.yml b/config/feature_flags/experiment/combined_registration.yml
deleted file mode 100644
index 0b867353946..00000000000
--- a/config/feature_flags/experiment/combined_registration.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: combined_registration
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67614
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/285533
-milestone: '14.3'
-type: experiment
-group: group::acquisition
-default_enabled: false
diff --git a/config/feature_flags/ops/ci_partitioning_analyze_queries.yml b/config/feature_flags/ops/ci_partitioning_analyze_queries.yml
new file mode 100644
index 00000000000..59ca52278e9
--- /dev/null
+++ b/config/feature_flags/ops/ci_partitioning_analyze_queries.yml
@@ -0,0 +1,8 @@
+---
+name: ci_partitioning_analyze_queries
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97113
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372840
+milestone: '15.4'
+type: ops
+group: group::pipeline execution
+default_enabled: false
diff --git a/config/feature_flags/ops/database_async_index_destruction.yml b/config/feature_flags/ops/database_async_index_destruction.yml
deleted file mode 100644
index a100b3a40f2..00000000000
--- a/config/feature_flags/ops/database_async_index_destruction.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: database_async_index_destruction
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92328
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367964
-milestone: '15.3'
-type: ops
-group: group::database
-default_enabled: false
diff --git a/config/feature_flags/ops/detect_cross_database_modification.yml b/config/feature_flags/ops/detect_cross_database_modification.yml
new file mode 100644
index 00000000000..5f496118c4b
--- /dev/null
+++ b/config/feature_flags/ops/detect_cross_database_modification.yml
@@ -0,0 +1,7 @@
+---
+name: detect_cross_database_modification
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73316
+milestone: '14.5'
+type: ops
+group: group::sharding
+default_enabled: false
diff --git a/config/feature_flags/ops/github_importer_attachments_import.yml b/config/feature_flags/ops/github_importer_attachments_import.yml
new file mode 100644
index 00000000000..ec4fe144933
--- /dev/null
+++ b/config/feature_flags/ops/github_importer_attachments_import.yml
@@ -0,0 +1,8 @@
+---
+name: github_importer_attachments_import
+introduced_by_url:
+rollout_issue_url:
+milestone: '15.4'
+type: ops
+group: group::import
+default_enabled: false
diff --git a/config/feature_flags/ops/increase_branch_cache_expiry.yml b/config/feature_flags/ops/increase_branch_cache_expiry.yml
new file mode 100644
index 00000000000..61b9d5b9c42
--- /dev/null
+++ b/config/feature_flags/ops/increase_branch_cache_expiry.yml
@@ -0,0 +1,8 @@
+---
+name: increase_branch_cache_expiry
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96739
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372331
+milestone: '15.4'
+type: ops
+group: group::code review
+default_enabled: false
diff --git a/config/feature_flags/ops/query_analyzer_gitlab_schema_metrics.yml b/config/feature_flags/ops/query_analyzer_gitlab_schema_metrics.yml
new file mode 100644
index 00000000000..9793eebb014
--- /dev/null
+++ b/config/feature_flags/ops/query_analyzer_gitlab_schema_metrics.yml
@@ -0,0 +1,7 @@
+---
+name: query_analyzer_gitlab_schema_metrics
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73839
+milestone: '14.5'
+type: ops
+group: group::sharding
+default_enabled: false
diff --git a/config/feature_flags/undefined/gitaly_simplify_find_local_branches_response.yml b/config/feature_flags/undefined/gitaly_simplify_find_local_branches_response.yml
new file mode 100644
index 00000000000..c82f8ee26b7
--- /dev/null
+++ b/config/feature_flags/undefined/gitaly_simplify_find_local_branches_response.yml
@@ -0,0 +1,8 @@
+---
+name: gitaly_simplify_find_local_branches_response
+introduced_by_url: https://gitlab.com/gitlab-org/gitaly/-/merge_requests/4850
+rollout_issue_url: https://gitlab.com/gitlab-org/gitaly/-/issues/4452
+milestone: '15.4'
+type: undefined
+group: group::gitaly
+default_enabled: false
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 6861864999f..da950c54fbf 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -183,6 +183,22 @@ production: &base
# plaintext. This can be a security risk.
# display_initial_root_password: false
+ # Allows delivery of emails using Microsoft Graph API with OAuth 2.0 client credentials flow.
+ microsoft_graph_mailer:
+ enabled: false
+ # The unique identifier for the user. To use Microsoft Graph on behalf of the user.
+ # user_id: "YOUR-USER-ID"
+ # The directory tenant the application plans to operate against, in GUID or domain-name format.
+ # tenant: "YOUR-TENANT-ID"
+ # The application ID that's assigned to your app. You can find this information in the portal where you registered your app.
+ # client_id: "YOUR-CLIENT-ID"
+ # The client secret that you generated for your app in the app registration portal.
+ # client_secret: "YOUR-CLIENT-SECRET-ID"
+ # Defaults to "https://login.microsoftonline.com".
+ # azure_ad_endpoint:
+ # Defaults to "https://graph.microsoft.com".
+ # graph_endpoint:
+
## Reply by email
# Allow users to comment on issues and merge requests by replying to notification emails.
# For documentation on how to set this up, see https://docs.gitlab.com/ee/administration/reply_by_email.html
diff --git a/config/helpers/evaluate_module_from_source.js b/config/helpers/evaluate_module_from_source.js
new file mode 100644
index 00000000000..b55bbb82b63
--- /dev/null
+++ b/config/helpers/evaluate_module_from_source.js
@@ -0,0 +1,37 @@
+const vm = require('vm');
+
+/**
+ * This function uses Node's `vm` modules to evaluate the `module.exports` of a given source string
+ *
+ * Example:
+ *
+ * ```javascript
+ * const { exports: moduleExports } = evaluateModuleFromSource("const foo = 7;\n module.exports.bar = 10 + foo;");
+ *
+ * assert(moduleExports.bar === 17);
+ * ```
+ *
+ * @param {String} source to be evaluated using Node's `vm` modules
+ * @param {{ require: Function }} options used in the context during evaluation of the Node module
+ * @returns {{ exports: any }} exports added to the script's `module.exports` context
+ */
+const evaluateModuleFromSource = (source, { require } = {}) => {
+ const context = {
+ module: {
+ exports: {},
+ },
+ require,
+ };
+
+ try {
+ const script = new vm.Script(source);
+ script.runInNewContext(context);
+ } catch (e) {
+ console.error(e);
+ throw e;
+ }
+
+ return context.module;
+};
+
+module.exports = { evaluateModuleFromSource };
diff --git a/config/initializers/01_secret_token.rb b/config/initializers/01_secret_token.rb
index c1f03dfdb07..bb13869c963 100644
--- a/config/initializers/01_secret_token.rb
+++ b/config/initializers/01_secret_token.rb
@@ -65,11 +65,10 @@ end
def set_missing_keys(defaults)
defaults.stringify_keys.each_with_object({}) do |(key, default), missing|
- if Rails.application.secrets[key].blank?
- warn_missing_secret(key)
+ next if Rails.application.secrets[key].present?
- missing[key] = Rails.application.secrets[key] = default
- end
+ warn_missing_secret(key)
+ missing[key] = Rails.application.secrets[key] = default
end
end
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index b271cefadd9..3fc4b56f458 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -117,6 +117,27 @@ Settings.omniauth.cas3['session_duration'] ||= 8.hours
Settings.omniauth['session_tickets'] ||= Settingslogic.new({})
Settings.omniauth.session_tickets['cas3'] = 'ticket'
+# Handle backward compatibility with the renamed kerberos_spnego provider
+# https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96335#note_1094265436
+Gitlab.ee do
+ kerberos_spnego = Settings.omniauth.providers.find { |p| p.name == 'kerberos_spnego' }
+ if kerberos_spnego
+ Settings.omniauth.providers.delete_if { |p| p.name == 'kerberos' }
+ kerberos_spnego['name'] = 'kerberos'
+
+ omniauth_keys = %w(allow_single_sign_on auto_link_user external_providers sync_profile_from_provider allow_bypass_two_factor)
+ omniauth_keys.each do |key|
+ next unless Settings.omniauth[key].is_a?(Array)
+
+ Settings.omniauth[key].map! { |p| p == 'kerberos_spnego' ? 'kerberos' : p }
+ end
+
+ if Settings.omniauth['auto_sign_in_with_provider'] == 'kerberos_spnego'
+ Settings.omniauth['auto_sign_in_with_provider'] = 'kerberos'
+ end
+ end
+end
+
# Fill out omniauth-gitlab settings. It is needed for easy set up GHE or GH by just specifying url.
github_default_url = "https://github.com"
@@ -214,11 +235,11 @@ Settings.gitlab['import_sources'] ||= Gitlab::ImportSources.values
Settings.gitlab['trusted_proxies'] ||= []
Settings.gitlab['content_security_policy'] ||= {}
Settings.gitlab['allowed_hosts'] ||= []
-Settings.gitlab['no_todos_messages'] ||= YAML.load_file(Rails.root.join('config', 'no_todos_messages.yml'))
Settings.gitlab['impersonation_enabled'] ||= true if Settings.gitlab['impersonation_enabled'].nil?
Settings.gitlab['usage_ping_enabled'] = true if Settings.gitlab['usage_ping_enabled'].nil?
Settings.gitlab['max_request_duration_seconds'] ||= 57
Settings.gitlab['display_initial_root_password'] = false if Settings.gitlab['display_initial_root_password'].nil?
+Settings.gitlab['weak_passwords_digest_set'] ||= YAML.safe_load(File.open(Rails.root.join('config', 'weak_password_digests.yml')), permitted_classes: [String]).to_set.freeze
Gitlab.ee do
Settings.gitlab['mirror_max_delay'] ||= 300
@@ -547,7 +568,7 @@ Settings.cron_jobs['container_registry_migration_observer_worker'] ||= Settingsl
Settings.cron_jobs['container_registry_migration_observer_worker']['cron'] ||= '*/30 * * * *'
Settings.cron_jobs['container_registry_migration_observer_worker']['job_class'] = 'ContainerRegistry::Migration::ObserverWorker'
Settings.cron_jobs['container_registry_migration_enqueuer_worker'] ||= Settingslogic.new({})
-Settings.cron_jobs['container_registry_migration_enqueuer_worker']['cron'] ||= '45 */1 * * *'
+Settings.cron_jobs['container_registry_migration_enqueuer_worker']['cron'] ||= '15,45 */1 * * *'
Settings.cron_jobs['container_registry_migration_enqueuer_worker']['job_class'] = 'ContainerRegistry::Migration::EnqueuerWorker'
Settings.cron_jobs['image_ttl_group_policy_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['image_ttl_group_policy_worker']['cron'] ||= '40 0 * * *'
@@ -633,6 +654,9 @@ Settings.cron_jobs['loose_foreign_keys_cleanup_worker']['job_class'] = 'LooseFor
Settings.cron_jobs['ci_runner_versions_reconciliation_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['ci_runner_versions_reconciliation_worker']['cron'] ||= '@daily'
Settings.cron_jobs['ci_runner_versions_reconciliation_worker']['job_class'] = 'Ci::Runners::ReconcileExistingRunnerVersionsCronWorker'
+Settings.cron_jobs['users_migrate_records_to_ghost_user_in_batches_worker'] ||= Settingslogic.new({})
+Settings.cron_jobs['users_migrate_records_to_ghost_user_in_batches_worker']['cron'] ||= '*/1 * * * *'
+Settings.cron_jobs['users_migrate_records_to_ghost_user_in_batches_worker']['job_class'] = 'Users::MigrateRecordsToGhostUserInBatchesWorker'
Gitlab.ee do
Settings.cron_jobs['analytics_devops_adoption_create_all_snapshots_worker'] ||= Settingslogic.new({})
@@ -713,9 +737,6 @@ Gitlab.ee do
Settings.cron_jobs['ldap_sync_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['ldap_sync_worker']['cron'] ||= '30 1 * * *'
Settings.cron_jobs['ldap_sync_worker']['job_class'] = 'LdapSyncWorker'
- Settings.cron_jobs['free_user_cap_data_remediation'] ||= Settingslogic.new({})
- Settings.cron_jobs['free_user_cap_data_remediation']['cron'] ||= '17 6,10,14,18 * * *'
- Settings.cron_jobs['free_user_cap_data_remediation']['job_class'] = 'Namespaces::FreeUserCap::RemediationWorker'
Settings.cron_jobs['update_max_seats_used_for_gitlab_com_subscriptions_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['update_max_seats_used_for_gitlab_com_subscriptions_worker']['cron'] ||= '0 12 * * *'
Settings.cron_jobs['update_max_seats_used_for_gitlab_com_subscriptions_worker']['job_class'] = 'UpdateMaxSeatsUsedForGitlabComSubscriptionsWorker'
@@ -791,7 +812,7 @@ end
#
Settings['sidekiq'] ||= Settingslogic.new({})
Settings['sidekiq']['log_format'] ||= 'default'
-Settings['sidekiq']['routing_rules'] ||= []
+Settings['sidekiq']['routing_rules'] = Settings.__send__(:build_sidekiq_routing_rules, Settings['sidekiq']['routing_rules'])
#
# GitLab Shell
@@ -885,6 +906,18 @@ Settings['satellites'] ||= Settingslogic.new({})
Settings.satellites['path'] = Settings.absolute(Settings.satellites['path'] || "tmp/repo_satellites/")
#
+# Microsoft Graph Mailer
+#
+Settings['microsoft_graph_mailer'] ||= Settingslogic.new({})
+Settings.microsoft_graph_mailer['enabled'] = false if Settings.microsoft_graph_mailer['enabled'].nil?
+Settings.microsoft_graph_mailer['user_id'] ||= nil
+Settings.microsoft_graph_mailer['tenant'] ||= nil
+Settings.microsoft_graph_mailer['client_id'] ||= nil
+Settings.microsoft_graph_mailer['client_secret'] ||= nil
+Settings.microsoft_graph_mailer['azure_ad_endpoint'] ||= 'https://login.microsoftonline.com'
+Settings.microsoft_graph_mailer['graph_endpoint'] ||= 'https://graph.microsoft.com'
+
+#
# Kerberos
#
Gitlab.ee do
@@ -897,8 +930,8 @@ Gitlab.ee do
Settings.kerberos['https'] = Settings.gitlab.https if Settings.kerberos['https'].nil?
Settings.kerberos['port'] ||= Settings.kerberos.https ? 8443 : 8088
- if Settings.kerberos['enabled'] && !Settings.omniauth.providers.map(&:name).include?('kerberos_spnego')
- Settings.omniauth.providers << Settingslogic.new({ 'name' => 'kerberos_spnego' })
+ if Settings.kerberos['enabled'] && !Settings.omniauth.providers.map(&:name).include?('kerberos')
+ Settings.omniauth.providers << Settingslogic.new({ 'name' => 'kerberos' })
end
end
diff --git a/config/initializers/7_redis.rb b/config/initializers/7_redis.rb
index 415574e1ce1..1e2786db413 100644
--- a/config/initializers/7_redis.rb
+++ b/config/initializers/7_redis.rb
@@ -1,5 +1,9 @@
# frozen_string_literal: true
+require 'gitlab/redis'
+
+Redis.raise_deprecations = true unless Rails.env.production?
+
# We set the instance variable directly to suppress warnings.
# We cannot switch to the new behavior until we change all existing `redis.exists` calls to `redis.exists?`.
# Some gems also need to be updated
@@ -13,11 +17,6 @@ Redis::Client.prepend(Gitlab::Instrumentation::RedisInterceptor)
# 1. Sidekiq
# 2. Rails.cache
# 3. HTTP clients
-Gitlab::Redis::Cache.with { nil }
-Gitlab::Redis::Queues.with { nil }
-Gitlab::Redis::SharedState.with { nil }
-Gitlab::Redis::TraceChunks.with { nil }
-Gitlab::Redis::RateLimiting.with { nil }
-Gitlab::Redis::Sessions.with { nil }
-Gitlab::Redis::DuplicateJobs.with { nil }
-Gitlab::Redis::SidekiqStatus.with { nil }
+Gitlab::Redis::ALL_CLASSES.each do |redis_instance|
+ redis_instance.with { nil }
+end
diff --git a/config/initializers/active_record_keyset_pagination.rb b/config/initializers/active_record_keyset_pagination.rb
index f5692c95276..7f830cafd31 100644
--- a/config/initializers/active_record_keyset_pagination.rb
+++ b/config/initializers/active_record_keyset_pagination.rb
@@ -1,10 +1,36 @@
# frozen_string_literal: true
module PaginatorExtension
+ KEYSET_ORDER_PLACEHOLDER = Object.new
+
# This method loads the records for the requested page and returns a keyset paginator object.
def keyset_paginate(cursor: nil, per_page: 20, keyset_order_options: {})
Gitlab::Pagination::Keyset::Paginator.new(scope: self.dup, cursor: cursor, per_page: per_page, keyset_order_options: keyset_order_options)
end
+
+ # This modifies `reverse_sql_order` so that it is aware of Gitlab::Pagination::Keyset::Order which
+ # can reverse order clauses with NULLS LAST because we provide it a `reversed_order_expression`.
+ # This allows us to use `#last` on these relations.
+ #
+ # Overrides https://github.com/rails/rails/blob/v6.1.6.1/activerecord/lib/active_record/relation/query_methods.rb#L1331-L1358
+ def reverse_sql_order(order_query)
+ return super if order_query.empty?
+
+ keyset_order_values = []
+
+ order_query_without_keyset = order_query.flat_map do |o|
+ next o unless o.is_a?(Gitlab::Pagination::Keyset::Order)
+
+ keyset_order_values << o
+ KEYSET_ORDER_PLACEHOLDER
+ end
+
+ super(order_query_without_keyset).map do |o|
+ next o unless o == KEYSET_ORDER_PLACEHOLDER
+
+ keyset_order_values.shift.reversed_order
+ end
+ end
end
ActiveSupport.on_load(:active_record) do
diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb
index 867f3fd47cc..918b2767c4d 100644
--- a/config/initializers/doorkeeper.rb
+++ b/config/initializers/doorkeeper.rb
@@ -90,7 +90,9 @@ Doorkeeper.configure do
# Check out the wiki for more information on customization
access_token_methods :from_access_token_param, :from_bearer_authorization, :from_bearer_param
- hash_token_secrets using: '::Gitlab::DoorkeeperSecretStoring::Pbkdf2Sha512', fallback: :plain
+ hash_token_secrets using: '::Gitlab::DoorkeeperSecretStoring::Token::Pbkdf2Sha512', fallback: :plain
+
+ hash_application_secrets using: '::Gitlab::DoorkeeperSecretStoring::Secret::Pbkdf2Sha512', fallback: :plain
# Specify what grant flows are enabled in array of Strings. The valid
# strings and the flows they enable are:
diff --git a/config/initializers/excon.rb b/config/initializers/excon.rb
new file mode 100644
index 00000000000..132cb2ff15b
--- /dev/null
+++ b/config/initializers/excon.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+require 'openssl'
+
+# Excon ships its own bundled certs by default. Avoid confusion
+# by using the same set that GitLab uses.
+Excon.defaults[:ssl_ca_file] = OpenSSL::X509::DEFAULT_CERT_FILE
+Excon.defaults[:ssl_verify_peer] = true
diff --git a/config/initializers/gitlab_experiment.rb b/config/initializers/gitlab_experiment.rb
index a201a075f62..6d2795caf51 100644
--- a/config/initializers/gitlab_experiment.rb
+++ b/config/initializers/gitlab_experiment.rb
@@ -65,7 +65,7 @@ Gitlab::Experiment.configure do |config|
# permitted, and will be sent along using Gitlab::Tracking::StandardContext.
#
config.tracking_behavior = lambda do |action, event_args|
- Gitlab::Tracking.event(name, action.to_s, **event_args.merge(
+ Gitlab::Tracking.event(name, action, **event_args.merge(
context: (event_args[:context] || []) << SnowplowTracker::SelfDescribingJson.new(
'iglu:com.gitlab/gitlab_experiment/jsonschema/1-0-0', signature
)
diff --git a/config/initializers/load_balancing.rb b/config/initializers/load_balancing.rb
index 290481f7296..bcc62a6be5d 100644
--- a/config/initializers/load_balancing.rb
+++ b/config/initializers/load_balancing.rb
@@ -2,15 +2,6 @@
Gitlab::Application.configure do |config|
config.middleware.use(Gitlab::Database::LoadBalancing::RackMiddleware)
-
- # We need re-rerun the setup when code reloads in development
- config.reloader.to_prepare do
- if Rails.env.development? || Rails.env.test?
- Gitlab::Database::LoadBalancing.base_models.each do |model|
- Gitlab::Database::LoadBalancing::Setup.new(model).setup
- end
- end
- end
end
Gitlab::Database::LoadBalancing.base_models.each do |model|
@@ -23,8 +14,9 @@ Gitlab::Database::LoadBalancing.base_models.each do |model|
# information.
Gitlab::Database::LoadBalancing::Setup.new(model).setup
+ # We need re-rerun the setup when code reloads in development
Rails.application.reloader.to_prepare do
- if Rails.env.development?
+ if Rails.env.development? || Rails.env.test?
Gitlab::Database::LoadBalancing::Setup.new(model).setup
end
end
diff --git a/config/initializers/lookbook.rb b/config/initializers/lookbook.rb
deleted file mode 100644
index 4cb1b827286..00000000000
--- a/config/initializers/lookbook.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# frozen_string_literal: true
-
-if Rails.env.development?
- # :nocov: Lookbook is only available in development
- Lookbook::ApplicationController.class_eval do
- content_security_policy false
- end
-
- Rails.application.configure do
- config.lookbook.experimental_features = [:pages]
- config.lookbook.page_paths = ["#{config.root}/spec/components/docs"]
- end
- # :nocov:
-end
diff --git a/config/initializers/microsoft_graph_mailer.rb b/config/initializers/microsoft_graph_mailer.rb
new file mode 100644
index 00000000000..45fdef1c57d
--- /dev/null
+++ b/config/initializers/microsoft_graph_mailer.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+if Gitlab.config.microsoft_graph_mailer.enabled
+ ActionMailer::Base.delivery_method = :microsoft_graph
+
+ ActionMailer::Base.microsoft_graph_settings = {
+ user_id: Gitlab.config.microsoft_graph_mailer.user_id,
+ tenant: Gitlab.config.microsoft_graph_mailer.tenant,
+ client_id: Gitlab.config.microsoft_graph_mailer.client_id,
+ client_secret: Gitlab.config.microsoft_graph_mailer.client_secret,
+ azure_ad_endpoint: Gitlab.config.microsoft_graph_mailer.azure_ad_endpoint,
+ graph_endpoint: Gitlab.config.microsoft_graph_mailer.graph_endpoint
+ }
+end
diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb
index 38bd1034b36..2454b5ea818 100644
--- a/config/initializers/omniauth.rb
+++ b/config/initializers/omniauth.rb
@@ -11,6 +11,17 @@ if Gitlab::Auth::Ldap::Config.enabled?
end
end
+module OmniAuth
+ module Strategies
+ class AzureActivedirectoryV2
+ # override until https://github.com/RIPAGlobal/omniauth-azure-activedirectory-v2/pull/6 is merged
+ def callback_url
+ full_host + callback_path
+ end
+ end
+ end
+end
+
OmniAuth.config.full_host = Gitlab::OmniauthInitializer.full_host
OmniAuth.config.allowed_request_methods = [:post]
diff --git a/config/initializers/postgres_partitioning.rb b/config/initializers/postgres_partitioning.rb
index 4de6e706f16..e7f29ee1a84 100644
--- a/config/initializers/postgres_partitioning.rb
+++ b/config/initializers/postgres_partitioning.rb
@@ -1,43 +1,48 @@
# frozen_string_literal: true
-Gitlab::Database::Partitioning.register_models([
- AuditEvent,
- WebHookLog,
- LooseForeignKeys::DeletedRecord,
- Gitlab::Database::BackgroundMigration::BatchedJobTransitionLog
-])
+Gitlab::Database::Partitioning.register_models(
+ [
+ AuditEvent,
+ WebHookLog,
+ LooseForeignKeys::DeletedRecord,
+ Gitlab::Database::BackgroundMigration::BatchedJobTransitionLog
+ ])
if Gitlab.ee?
- Gitlab::Database::Partitioning.register_models([
- IncidentManagement::PendingEscalations::Alert,
- IncidentManagement::PendingEscalations::Issue
- ])
+ Gitlab::Database::Partitioning.register_models(
+ [
+ IncidentManagement::PendingEscalations::Alert,
+ IncidentManagement::PendingEscalations::Issue,
+ Security::Finding
+ ])
else
- Gitlab::Database::Partitioning.register_tables([
- {
- limit_connection_names: %i[main],
- table_name: 'incident_management_pending_alert_escalations',
- partitioned_column: :process_at, strategy: :monthly
- },
- {
- limit_connection_names: %i[main],
- table_name: 'incident_management_pending_issue_escalations',
- partitioned_column: :process_at, strategy: :monthly
- }
- ])
+ Gitlab::Database::Partitioning.register_tables(
+ [
+ {
+ limit_connection_names: %i[main],
+ table_name: 'incident_management_pending_alert_escalations',
+ partitioned_column: :process_at, strategy: :monthly
+ },
+ {
+ limit_connection_names: %i[main],
+ table_name: 'incident_management_pending_issue_escalations',
+ partitioned_column: :process_at, strategy: :monthly
+ }
+ ])
end
# The following tables are already defined as models
unless Gitlab.jh?
- Gitlab::Database::Partitioning.register_tables([
- # This should be synchronized with the following model:
- # https://jihulab.com/gitlab-cn/gitlab/-/blob/main-jh/jh/app/models/phone/verification_code.rb
- {
- limit_connection_names: %i[main],
- table_name: 'verification_codes',
- partitioned_column: :created_at, strategy: :monthly
- }
- ])
+ Gitlab::Database::Partitioning.register_tables(
+ [
+ # This should be synchronized with the following model:
+ # https://jihulab.com/gitlab-cn/gitlab/-/blob/main-jh/jh/app/models/phone/verification_code.rb
+ {
+ limit_connection_names: %i[main],
+ table_name: 'verification_codes',
+ partitioned_column: :created_at, strategy: :monthly
+ }
+ ])
end
Gitlab::Database::Partitioning.sync_partitions_ignore_db_error
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index 29df6da6ef1..262ef1dea21 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -127,3 +127,4 @@ end
Sidekiq::Scheduled::Poller.prepend Gitlab::Patch::SidekiqPoller
Sidekiq::Cron::Poller.prepend Gitlab::Patch::SidekiqPoller
+Sidekiq::Cron::Poller.prepend Gitlab::Patch::SidekiqCronPoller
diff --git a/config/initializers/sidekiq_cluster.rb b/config/initializers/sidekiq_cluster.rb
index 2f9c1de47eb..6fd598b3e25 100644
--- a/config/initializers/sidekiq_cluster.rb
+++ b/config/initializers/sidekiq_cluster.rb
@@ -9,22 +9,23 @@ if ENV['ENABLE_SIDEKIQ_CLUSTER']
loop do
sleep(5)
+ next if Process.ppid == parent
+
# In cluster mode it's possible that the master process is SIGKILL'd. In
# this case the parent PID changes and we need to terminate ourselves.
- if Process.ppid != parent
- Process.kill(:TERM, Process.pid)
-
- # Allow sidekiq to cleanly terminate and push any running jobs back
- # into the queue. We use the configured timeout and add a small
- # grace period
- sleep(Sidekiq.options[:timeout] + 5)
-
- # Signaling the Sidekiq Pgroup as KILL is not forwarded to
- # a possible child process. In Sidekiq Cluster, all child Sidekiq
- # processes are PGROUP leaders (each process has its own pgroup).
- Process.kill(:KILL, 0)
- break
- end
+
+ Process.kill(:TERM, Process.pid)
+
+ # Allow sidekiq to cleanly terminate and push any running jobs back
+ # into the queue. We use the configured timeout and add a small
+ # grace period
+ sleep(Sidekiq.options[:timeout] + 5)
+
+ # Signaling the Sidekiq Pgroup as KILL is not forwarded to
+ # a possible child process. In Sidekiq Cluster, all child Sidekiq
+ # processes are PGROUP leaders (each process has its own pgroup).
+ Process.kill(:KILL, 0)
+ break
end
end
end
diff --git a/config/initializers/wikicloth_redos_patch.rb b/config/initializers/wikicloth_redos_patch.rb
index 4ff545dd6f7..95901378891 100644
--- a/config/initializers/wikicloth_redos_patch.rb
+++ b/config/initializers/wikicloth_redos_patch.rb
@@ -1,3 +1,27 @@
+# This file contains code based on the wikicloth project:
+# https://github.com/nricciar/wikicloth
+#
+# Copyright (c) 2009 The wikicloth authors.
+#
+# 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.
+#
# frozen_string_literal: true
require 'wikicloth'
@@ -20,7 +44,10 @@ require 'digest/sha2'
# - https://gitlab.com/gitlab-org/gitlab/-/issues/361266
# Guard to ensure we remember to delete this patch if they ever release a new version of wikicloth
-raise 'New version of WikiCloth detected, please remove this patch' unless Gem::Version.new(WikiCloth::VERSION) == Gem::Version.new('0.8.1')
+unless Gem::Version.new(WikiCloth::VERSION) == Gem::Version.new('0.8.1')
+ raise 'New version of WikiCloth detected, please either update the version for this check, ' \
+ 'or remove this patch if no longer needed'
+end
# rubocop:disable Style/ClassAndModuleChildren
# rubocop:disable Layout/SpaceAroundEqualsInParameterDefault
@@ -43,6 +70,12 @@ raise 'New version of WikiCloth detected, please remove this patch' unless Gem::
# rubocop:disable Style/RegexpLiteralMixedPreserve
# rubocop:disable Style/RedundantRegexpCharacterClass
# rubocop:disable Performance/StringInclude
+# rubocop:disable Layout/LineLength
+# rubocop:disable Style/RedundantSelf
+# rubocop:disable Style/SymbolProc
+# rubocop:disable Layout/SpaceInsideParens
+# rubocop:disable Style/GuardClause
+# rubocop:disable Style/RedundantRegexpEscape
module WikiCloth
class WikiCloth
def render(opt={})
@@ -218,3 +251,9 @@ end
# rubocop:enable Style/RegexpLiteralMixedPreserve
# rubocop:enable Style/RedundantRegexpCharacterClass
# rubocop:enable Performance/StringInclude
+# rubocop:enable Layout/LineLength
+# rubocop:enable Style/RedundantSelf
+# rubocop:enable Style/SymbolProc
+# rubocop:enable Layout/SpaceInsideParens
+# rubocop:enable Style/GuardClause
+# rubocop:enable Style/RedundantRegexpEscape
diff --git a/config/initializers/wikicloth_ruby_3_patch.rb b/config/initializers/wikicloth_ruby_3_patch.rb
new file mode 100644
index 00000000000..d80383a125d
--- /dev/null
+++ b/config/initializers/wikicloth_ruby_3_patch.rb
@@ -0,0 +1,272 @@
+# This file contains code based on the wikicloth project:
+# https://github.com/nricciar/wikicloth
+#
+# Copyright (c) 2009 The wikicloth authors.
+#
+# 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.
+#
+# frozen_string_literal: true
+
+require 'wikicloth'
+require 'wikicloth/wiki_buffer/var'
+
+# Adds patch for changes in this PRs:
+#
+# https://github.com/nricciar/wikicloth/pull/110
+#
+# The maintainers are not releasing new versions, so we
+# need to patch it here.
+#
+# If they ever do release a version, then we can remove this file.
+#
+# See:
+# - https://gitlab.com/gitlab-org/gitlab/-/issues/372400
+
+# Guard to ensure we remember to delete this patch if they ever release a new version of wikicloth
+unless Gem::Version.new(WikiCloth::VERSION) == Gem::Version.new('0.8.1')
+ raise 'New version of WikiCloth detected, please either update the version for this check, ' \
+ 'or remove this patch if no longer needed'
+end
+
+# rubocop:disable Style/ClassAndModuleChildren
+# rubocop:disable Style/HashSyntax
+# rubocop:disable Layout/SpaceAfterComma
+# rubocop:disable Style/RescueStandardError
+# rubocop:disable Metrics/AbcSize
+# rubocop:disable Metrics/CyclomaticComplexity
+# rubocop:disable Metrics/PerceivedComplexity
+# rubocop:disable Cop/LineBreakAroundConditionalBlock
+# rubocop:disable Layout/EmptyLineAfterGuardClause
+# rubocop:disable Performance/ReverseEach
+# rubocop:disable Style/PerlBackrefs
+# rubocop:disable Style/RedundantRegexpCharacterClass
+# rubocop:disable Performance/StringInclude
+# rubocop:disable Style/IfUnlessModifier
+# rubocop:disable Layout/LineLength
+# rubocop:disable Lint/DeprecatedClassMethods
+# rubocop:disable Lint/UselessAssignment
+# rubocop:disable Lint/RedundantStringCoercion
+# rubocop:disable Style/StringLiteralsInInterpolation
+# rubocop:disable Lint/UriEscapeUnescape
+# rubocop:disable Style/For
+# rubocop:disable Style/SlicingWithRange
+# rubocop:disable Style/GuardClause
+# rubocop:disable Style/ZeroLengthPredicate
+# rubocop:disable Cop/LineBreakAfterGuardClauses
+# rubocop:disable Layout/MultilineHashBraceLayout
+module WikiCloth
+ class WikiCloth
+ class MathExtension < Extension
+ # <math>latex markup</math>
+ #
+ element 'math', :skip_html => true, :run_globals => false do |buffer|
+ blahtex_path = @options[:blahtex_path] || '/usr/bin/blahtex'
+ blahtex_png_path = @options[:blahtex_png_path] || '/tmp'
+ blahtex_options = @options[:blahtex_options] || '--texvc-compatible-commands --mathml-version-1-fonts --disallow-plane-1 --spacing strict'
+
+ if File.exists?(blahtex_path) && @options[:math_formatter] != :google
+ begin
+ # pass tex markup to blahtex
+ response = IO.popen("#{blahtex_path} #{blahtex_options} --png --mathml --png-directory #{blahtex_png_path}","w+") do |pipe|
+ pipe.write(buffer.element_content)
+ pipe.close_write
+ pipe.gets
+ end
+
+ xml_response = REXML::Document.new(response).root
+
+ if @options[:blahtex_html_prefix]
+ # render as embedded image
+ file_md5 = xml_response.elements["png/md5"].text
+ "<img src=\"#{File.join(@options[:blahtex_html_prefix],"#{file_md5}.png")}\" />"
+ else
+ # render as mathml
+ html = xml_response.elements["mathml/markup"].text
+ "<math xmlns=\"http://www.w3.org/1998/Math/MathML\">#{xml_response.elements["mathml/markup"].children.to_s}</math>"
+ end
+ rescue => err
+ # blahtex error
+ "<span class=\"error\">#{I18n.t("unable to parse mathml", :error => err)}</span>"
+ end
+ else
+ # if blahtex does not exist fallback to google charts api
+ # This is the patched line from:
+ # https://github.com/nricciar/wikicloth/pull/110/files#diff-f0cb4c400957bbdcc4c97d69d2aa7f48d8ba56c5943e484863f620605d7d17d4R37
+ encoded_string = URI.encode_www_form_component(buffer.element_content)
+ "<img src=\"https://chart.googleapis.com/chart?cht=tx&chl=#{encoded_string}\" />"
+ end
+ end
+ end
+
+ class WikiBuffer::Var < WikiBuffer
+ def default_functions(name,params)
+ case name
+ when "#if"
+ params.first.blank? ? params[2] : params[1]
+ when "#switch"
+ match = params.first
+ default = nil
+ for p in params[1..-1]
+ temp = p.split("=")
+ if p !~ /=/ && temp.length == 1 && p == params.last
+ return p
+ elsif temp.instance_of?(Array) && temp.length > 0
+ test = temp.first.strip
+ default = temp[1..-1].join("=").strip if test == "#default"
+ return temp[1..-1].join("=").strip if test == match || (test == "none" && match.blank?)
+ end
+ end
+ default.nil? ? "" : default
+ when "#expr"
+ begin
+ ExpressionParser::Parser.new.parse(params.first)
+ rescue RuntimeError
+ I18n.t('expression error', :error => $!)
+ end
+ when "#ifexpr"
+ val = false
+ begin
+ val = ExpressionParser::Parser.new.parse(params.first)
+ rescue RuntimeError
+ end
+ if val
+ params[1]
+ else
+ params[2]
+ end
+ when "#ifeq"
+ if params[0] =~ /^[0-9A-Fa-f]+$/ && params[1] =~ /^[0-9A-Fa-f]+$/
+ params[0].to_i == params[1].to_i ? params[2] : params[3]
+ else
+ params[0] == params[1] ? params[2] : params[3]
+ end
+ when "#len"
+ params.first.length
+ when "#sub"
+ params.first[params[1].to_i,params[2].to_i]
+ when "#pad"
+ case params[3]
+ when "right"
+ params[0].ljust(params[1].to_i,params[2])
+ when "center"
+ params[0].center(params[1].to_i,params[2])
+ else
+ params[0].rjust(params[1].to_i,params[2])
+ end
+ when "#iferror"
+ params.first =~ /error/ ? params[1] : params[2]
+ when "#capture"
+ @options[:params][params.first] = params[1]
+ ""
+ when "urlencode"
+ # This is the patched line from:
+ # https://github.com/nricciar/wikicloth/pull/110/files#diff-f262faf4fadb222cca87185be0fb65b3f49659abc840794cc83a736d41310fb1R170
+ URI.encode_www_form_component(params.first)
+ when "lc"
+ params.first.downcase
+ when "uc"
+ params.first.upcase
+ when "ucfirst"
+ params.first.capitalize
+ when "lcfirst"
+ params.first[0,1].downcase + params.first[1..-1]
+ when "anchorencode"
+ params.first.gsub(/\s+/,'_')
+ when "plural"
+ begin
+ expr_value = ExpressionParser::Parser.new.parse(params.first)
+ expr_value.to_i == 1 ? params[1] : params[2]
+ rescue RuntimeError
+ I18n.t('expression error', :error => $!)
+ end
+ when "ns"
+ values = {
+ "" => "", "0" => "",
+ "1" => localise_ns("Talk"), "talk" => localise_ns("Talk"),
+ "6" => localise_ns("File"), "file" => localise_ns("File"), "image" => localise_ns("File"),
+ "10" => localise_ns("Template"), "template" => localise_ns("Template"),
+ "14" => localise_ns("Category"), "category" => localise_ns("Category"),
+ "-1" => localise_ns("Special"), "special" => localise_ns("Special"),
+ "12" => localise_ns("Help"), "help" => localise_ns("Help"),
+ "-2" => localise_ns("Media"), "media" => localise_ns("Media") }
+
+ values[localise_ns(params.first,:en).gsub(/\s+/,'_').downcase]
+ when "#language"
+ WikiNamespaces.language_name(params.first)
+ when "#tag"
+ return "" if params.empty?
+ elem = Builder::XmlMarkup.new
+ return elem.tag!(params.first) if params.length == 1
+ return elem.tag!(params.first) { |e| e << params.last } if params.length == 2
+ tag_attrs = {}
+ params[1..-2].each do |attr|
+ tag_attrs[$1] = $2 if attr =~ /^\s*([\w]+)\s*=\s*"(.*)"\s*$/
+ end
+ elem.tag!(params.first,tag_attrs) { |e| e << params.last }
+ when "debug"
+ ret = nil
+ case params.first
+ when "param"
+ @options[:buffer].buffers.reverse.each do |b|
+ if b.instance_of?(WikiBuffer::HTMLElement) && b.element_name == "template"
+ ret = b.get_param(params[1])
+ end
+ end
+ ret
+ when "buffer"
+ ret = "<pre>"
+ buffer = @options[:buffer].buffers
+ buffer.each do |b|
+ ret += " --- #{b.class}"
+ ret += b.instance_of?(WikiBuffer::HTMLElement) ? " -- #{b.element_name}\n" : " -- #{b.data}\n"
+ end
+ "#{ret}</pre>"
+ end
+ end
+ end
+ end
+ end
+end
+# rubocop:enable Style/ClassAndModuleChildren
+# rubocop:enable Style/HashSyntax
+# rubocop:enable Layout/SpaceAfterComma
+# rubocop:enable Style/RescueStandardError
+# rubocop:enable Metrics/AbcSize
+# rubocop:enable Metrics/CyclomaticComplexity
+# rubocop:enable Metrics/PerceivedComplexity
+# rubocop:enable Cop/LineBreakAroundConditionalBlock
+# rubocop:enable Layout/EmptyLineAfterGuardClause
+# rubocop:enable Performance/ReverseEach
+# rubocop:enable Style/PerlBackrefs
+# rubocop:enable Style/RedundantRegexpCharacterClass
+# rubocop:enable Performance/StringInclude
+# rubocop:enable Style/IfUnlessModifier
+# rubocop:enable Layout/LineLength
+# rubocop:enable Lint/DeprecatedClassMethods
+# rubocop:enable Lint/UselessAssignment
+# rubocop:enable Lint/RedundantStringCoercion
+# rubocop:enable Style/StringLiteralsInInterpolation
+# rubocop:enable Lint/UriEscapeUnescape
+# rubocop:enable Style/For
+# rubocop:enable Style/SlicingWithRange
+# rubocop:enable Style/GuardClause
+# rubocop:enable Style/ZeroLengthPredicate
+# rubocop:enable Cop/LineBreakAfterGuardClauses
+# rubocop:enable Layout/MultilineHashBraceLayout
diff --git a/config/initializers/zz_metrics.rb b/config/initializers/zz_metrics.rb
index 5e6c1abdda6..940d8eed61f 100644
--- a/config/initializers/zz_metrics.rb
+++ b/config/initializers/zz_metrics.rb
@@ -17,7 +17,6 @@
if Gitlab::Metrics.enabled? && !Rails.env.test? && !(Rails.env.development? && defined?(Rails::Generators))
require 'pathname'
require 'connection_pool'
- require 'method_source'
# These are manually require'd so the classes are registered properly with
# ActiveSupport.
@@ -40,6 +39,9 @@ if Gitlab::Metrics.enabled? && !Rails.env.test? && !(Rails.env.development? && d
if Gitlab::Runtime.puma?
Gitlab::Metrics::RequestsRackMiddleware.initialize_metrics
+ Gitlab::Metrics::GlobalSearchSlis.initialize_slis!
+ elsif Gitlab.ee? && Gitlab::Runtime.sidekiq?
+ Gitlab::Metrics::GlobalSearchIndexingSlis.initialize_slis!
end
GC::Profiler.enable
diff --git a/config/initializers_before_autoloader/000_inflections.rb b/config/initializers_before_autoloader/000_inflections.rb
index 70c9ec0a0ba..dfd33938611 100644
--- a/config/initializers_before_autoloader/000_inflections.rb
+++ b/config/initializers_before_autoloader/000_inflections.rb
@@ -37,6 +37,7 @@ ActiveSupport::Inflector.inflections do |inflect|
vulnerabilities_feedback
vulnerability_feedback
)
+ inflect.acronym 'CDN'
inflect.acronym 'EE'
inflect.acronym 'JH'
inflect.acronym 'CSP'
diff --git a/config/initializers_before_autoloader/002_sidekiq.rb b/config/initializers_before_autoloader/002_sidekiq.rb
index 9ffcf39d6fb..929bdeda996 100644
--- a/config/initializers_before_autoloader/002_sidekiq.rb
+++ b/config/initializers_before_autoloader/002_sidekiq.rb
@@ -9,5 +9,5 @@
require 'sidekiq/web'
if Rails.env.development?
- Sidekiq.default_worker_options[:backtrace] = true
+ Sidekiq.default_job_options[:backtrace] = true
end
diff --git a/config/metrics/aggregates/code_review.yml b/config/metrics/aggregates/code_review.yml
index 2e1eab78fa3..e58707b20a5 100644
--- a/config/metrics/aggregates/code_review.yml
+++ b/config/metrics/aggregates/code_review.yml
@@ -116,6 +116,14 @@
- 'i_code_review_merge_request_widget_status_checks_expand_success'
- 'i_code_review_merge_request_widget_status_checks_expand_warning'
- 'i_code_review_merge_request_widget_status_checks_expand_failed'
+ - 'i_code_review_submit_review_approve'
+ - 'i_code_review_submit_review_comment'
+ - 'i_code_review_merge_request_widget_license_compliance_view'
+ - 'i_code_review_merge_request_widget_license_compliance_full_report_clicked'
+ - 'i_code_review_merge_request_widget_license_compliance_expand'
+ - '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'
- name: code_review_category_monthly_active_users
operator: OR
source: redis
@@ -220,6 +228,14 @@
- 'i_code_review_merge_request_widget_status_checks_expand_success'
- 'i_code_review_merge_request_widget_status_checks_expand_warning'
- 'i_code_review_merge_request_widget_status_checks_expand_failed'
+ - 'i_code_review_submit_review_approve'
+ - 'i_code_review_submit_review_comment'
+ - 'i_code_review_merge_request_widget_license_compliance_view'
+ - 'i_code_review_merge_request_widget_license_compliance_full_report_clicked'
+ - 'i_code_review_merge_request_widget_license_compliance_expand'
+ - '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'
- name: code_review_extension_category_monthly_active_users
operator: OR
source: redis
diff --git a/config/metrics/counts_28d/20210216174910_analytics_unique_visits_for_any_target_monthly.yml b/config/metrics/counts_28d/20210216174910_analytics_unique_visits_for_any_target_monthly.yml
index e766c555d3a..fbf4be136cb 100644
--- a/config/metrics/counts_28d/20210216174910_analytics_unique_visits_for_any_target_monthly.yml
+++ b/config/metrics/counts_28d/20210216174910_analytics_unique_visits_for_any_target_monthly.yml
@@ -48,7 +48,6 @@ tier:
- premium
- ultimate
performance_indicator_type:
-- smau
- gmau
- paid_gmau
milestone: "<13.9"
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 f2e72cb8fff..4d8e4409e73 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
@@ -13,62 +13,122 @@ data_source: redis_hll
instrumentation_class: RedisHLLMetric
options:
events:
+ - i_code_review_click_diff_view_setting
+ - i_code_review_click_file_browser_setting
+ - i_code_review_click_single_file_mode_setting
+ - i_code_review_click_whitespace_setting
+ - i_code_review_create_note_in_ipynb_diff
+ - i_code_review_create_note_in_ipynb_diff_commit
+ - i_code_review_create_note_in_ipynb_diff_mr
+ - i_code_review_diff_hide_whitespace
+ - i_code_review_diff_multiple_files
+ - i_code_review_diff_show_whitespace
+ - i_code_review_diff_single_file
+ - i_code_review_diff_view_inline
+ - i_code_review_diff_view_parallel
+ - i_code_review_edit_mr_desc
+ - i_code_review_edit_mr_title
+ - i_code_review_file_browser_list_view
+ - i_code_review_file_browser_tree_view
+ - i_code_review_merge_request_widget_accessibility_expand
+ - i_code_review_merge_request_widget_accessibility_expand_failed
+ - i_code_review_merge_request_widget_accessibility_expand_success
+ - i_code_review_merge_request_widget_accessibility_expand_warning
+ - i_code_review_merge_request_widget_accessibility_full_report_clicked
+ - i_code_review_merge_request_widget_accessibility_view
+ - i_code_review_merge_request_widget_code_quality_expand
+ - i_code_review_merge_request_widget_code_quality_expand_failed
+ - i_code_review_merge_request_widget_code_quality_expand_success
+ - i_code_review_merge_request_widget_code_quality_expand_warning
+ - i_code_review_merge_request_widget_code_quality_full_report_clicked
+ - i_code_review_merge_request_widget_code_quality_view
+ - i_code_review_merge_request_widget_metrics_expand
+ - i_code_review_merge_request_widget_metrics_expand_failed
+ - i_code_review_merge_request_widget_metrics_expand_success
+ - i_code_review_merge_request_widget_metrics_expand_warning
+ - i_code_review_merge_request_widget_metrics_full_report_clicked
+ - i_code_review_merge_request_widget_metrics_view
+ - i_code_review_merge_request_widget_status_checks_expand
+ - i_code_review_merge_request_widget_status_checks_expand_failed
+ - i_code_review_merge_request_widget_status_checks_expand_success
+ - i_code_review_merge_request_widget_status_checks_expand_warning
+ - i_code_review_merge_request_widget_status_checks_full_report_clicked
+ - i_code_review_merge_request_widget_status_checks_view
+ - i_code_review_merge_request_widget_terraform_expand
+ - i_code_review_merge_request_widget_terraform_expand_failed
+ - i_code_review_merge_request_widget_terraform_expand_success
+ - i_code_review_merge_request_widget_terraform_expand_warning
+ - i_code_review_merge_request_widget_terraform_full_report_clicked
+ - i_code_review_merge_request_widget_terraform_view
+ - i_code_review_merge_request_widget_test_summary_expand
+ - i_code_review_merge_request_widget_test_summary_expand_failed
+ - i_code_review_merge_request_widget_test_summary_expand_success
+ - i_code_review_merge_request_widget_test_summary_expand_warning
+ - i_code_review_merge_request_widget_test_summary_full_report_clicked
+ - i_code_review_merge_request_widget_test_summary_view
+ - i_code_review_merge_request_widget_license_compliance_expand
+ - i_code_review_merge_request_widget_license_compliance_expand_failed
+ - i_code_review_merge_request_widget_license_compliance_expand_success
+ - i_code_review_merge_request_widget_license_compliance_warning
+ - i_code_review_merge_request_widget_license_compliance_full_report_clicked
+ - i_code_review_merge_request_widget_license_compliance_view
- i_code_review_mr_diffs
- - i_code_review_user_single_file_diffs
- i_code_review_mr_single_file_diffs
- - i_code_review_user_toggled_task_item_status
- - i_code_review_user_create_mr
- - i_code_review_user_close_mr
- - i_code_review_user_reopen_mr
- - i_code_review_user_approve_mr
- - i_code_review_user_unapprove_mr
- - i_code_review_user_resolve_thread
- - i_code_review_user_unresolve_thread
- - i_code_review_edit_mr_title
- - i_code_review_edit_mr_desc
- - i_code_review_user_merge_mr
- - i_code_review_user_create_mr_comment
- - i_code_review_user_edit_mr_comment
- - i_code_review_user_remove_mr_comment
- - i_code_review_user_create_review_note
- - i_code_review_user_publish_review
- - i_code_review_user_create_multiline_mr_comment
- - i_code_review_user_edit_multiline_mr_comment
- - i_code_review_user_remove_multiline_mr_comment
+ - i_code_review_mr_with_invalid_approvers
+ - i_code_review_post_merge_click_cherry_pick
+ - i_code_review_post_merge_click_revert
+ - i_code_review_post_merge_delete_branch
+ - i_code_review_post_merge_submit_cherry_pick_modal
+ - i_code_review_post_merge_submit_revert_modal
+ - i_code_review_total_suggestions_added
+ - i_code_review_total_suggestions_applied
- i_code_review_user_add_suggestion
- i_code_review_user_apply_suggestion
- - i_code_review_user_assigned
- - i_code_review_user_marked_as_draft
- - i_code_review_user_unmarked_as_draft
- - i_code_review_user_review_requested
- i_code_review_user_approval_rule_added
- i_code_review_user_approval_rule_deleted
- i_code_review_user_approval_rule_edited
- - i_code_review_user_vs_code_api_request
- - i_code_review_user_create_mr_from_issue
- - i_code_review_user_mr_discussion_locked
- - i_code_review_user_mr_discussion_unlocked
- - i_code_review_user_time_estimate_changed
- - i_code_review_user_time_spent_changed
+ - i_code_review_user_approve_mr
+ - i_code_review_user_assigned
- i_code_review_user_assignees_changed
- - i_code_review_user_reviewers_changed
- - i_code_review_user_milestone_changed
+ - i_code_review_user_close_mr
+ - i_code_review_user_create_mr
+ - i_code_review_user_create_mr_comment
+ - i_code_review_user_create_mr_from_issue
+ - i_code_review_user_create_multiline_mr_comment
+ - i_code_review_user_create_note_in_ipynb_diff
+ - i_code_review_user_create_note_in_ipynb_diff_commit
+ - i_code_review_user_create_note_in_ipynb_diff_mr
+ - i_code_review_user_create_review_note
+ - i_code_review_user_edit_mr_comment
+ - i_code_review_user_edit_multiline_mr_comment
+ - i_code_review_user_gitlab_cli_api_request
+ - i_code_review_user_jetbrains_api_request
- i_code_review_user_labels_changed
- - i_code_review_click_diff_view_setting
- - i_code_review_click_single_file_mode_setting
- - i_code_review_click_file_browser_setting
- - i_code_review_click_whitespace_setting
- - i_code_review_diff_view_inline
- - i_code_review_diff_view_parallel
- - i_code_review_file_browser_tree_view
- - i_code_review_file_browser_list_view
- - i_code_review_diff_show_whitespace
- - i_code_review_diff_hide_whitespace
- - i_code_review_diff_single_file
- - i_code_review_diff_multiple_files
- i_code_review_user_load_conflict_ui
+ - i_code_review_user_marked_as_draft
+ - i_code_review_user_merge_mr
+ - i_code_review_user_milestone_changed
+ - i_code_review_user_mr_discussion_locked
+ - i_code_review_user_mr_discussion_unlocked
+ - i_code_review_user_publish_review
+ - i_code_review_user_remove_mr_comment
+ - i_code_review_user_remove_multiline_mr_comment
+ - i_code_review_user_reopen_mr
- i_code_review_user_resolve_conflict
+ - i_code_review_user_resolve_thread
+ - i_code_review_user_resolve_thread_in_issue
+ - i_code_review_user_review_requested
+ - i_code_review_user_reviewers_changed
- i_code_review_user_searches_diff
+ - i_code_review_user_single_file_diffs
+ - i_code_review_user_time_estimate_changed
+ - i_code_review_user_time_spent_changed
+ - i_code_review_user_toggled_task_item_status
+ - i_code_review_user_unapprove_mr
+ - i_code_review_user_unmarked_as_draft
+ - i_code_review_user_unresolve_thread
+ - i_code_review_user_vs_code_api_request
+ - i_code_review_widget_nothing_merge_click_new_file
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 a2a97eb7477..2c6b21b0f6f 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
@@ -51,6 +51,7 @@ options:
- p_ci_templates_security_dast_api
- p_ci_templates_security_dast_api_latest
- p_ci_templates_security_container_scanning
+ - p_ci_templates_security_container_scanning_latest
- p_ci_templates_security_dast_latest
- p_ci_templates_security_dependency_scanning
- p_ci_templates_security_api_fuzzing
@@ -94,7 +95,9 @@ options:
- p_ci_templates_jobs_code_intelligence
- p_ci_templates_jobs_code_quality
- p_ci_templates_jobs_dependency_scanning
+ - p_ci_templates_jobs_dependency_scanning_latest
- p_ci_templates_jobs_license_scanning
+ - p_ci_templates_jobs_license_scanning_latest
- p_ci_templates_jobs_deploy_ecs
- p_ci_templates_jobs_deploy_ec2
- p_ci_templates_jobs_deploy
@@ -140,7 +143,9 @@ options:
- p_ci_templates_implicit_jobs_code_intelligence
- p_ci_templates_implicit_jobs_code_quality
- p_ci_templates_implicit_jobs_dependency_scanning
+ - p_ci_templates_implicit_jobs_dependency_scanning_latest
- p_ci_templates_implicit_jobs_license_scanning
+ - p_ci_templates_implicit_jobs_license_scanning_latest
- p_ci_templates_implicit_jobs_deploy_ecs
- p_ci_templates_implicit_jobs_deploy_ec2
- p_ci_templates_implicit_auto_devops_deploy
@@ -151,6 +156,7 @@ options:
- p_ci_templates_implicit_jobs_browser_performance_testing_latest
- p_ci_templates_implicit_jobs_cf_provision
- p_ci_templates_implicit_jobs_build_latest
+ - p_ci_templates_implicit_jobs_sast_iac
- p_ci_templates_implicit_security_sast
- p_ci_templates_implicit_security_dast_runner_validation
- p_ci_templates_implicit_security_dast_on_demand_scan
@@ -162,16 +168,19 @@ options:
- p_ci_templates_implicit_security_dast_api
- p_ci_templates_implicit_security_dast_api_latest
- p_ci_templates_implicit_security_container_scanning
+ - p_ci_templates_implicit_security_container_scanning_latest
- p_ci_templates_implicit_security_dast_latest
- p_ci_templates_implicit_security_dependency_scanning
- p_ci_templates_implicit_security_api_fuzzing
- p_ci_templates_implicit_security_dast
- p_ci_templates_implicit_security_cluster_image_scanning
+ - p_ci_templates_implicit_security_sast_iac
- p_ci_templates_kaniko
- p_ci_templates_qualys_iac_security
- p_ci_templates_liquibase
- p_ci_templates_matlab
- p_ci_templates_themekit
+ - p_ci_templates_katalon
distribution:
- ce
- ee
diff --git a/config/metrics/counts_28d/20210216184850_deploy_token_packages_total_unique_counts_monthly.yml b/config/metrics/counts_28d/20210216184850_deploy_token_packages_total_unique_counts_monthly.yml
index 720165425fa..6b6c40495a6 100644
--- a/config/metrics/counts_28d/20210216184850_deploy_token_packages_total_unique_counts_monthly.yml
+++ b/config/metrics/counts_28d/20210216184850_deploy_token_packages_total_unique_counts_monthly.yml
@@ -16,17 +16,13 @@ options:
events:
- i_package_composer_deploy_token
- i_package_conan_deploy_token
- - i_package_container_deploy_token
- - i_package_debian_deploy_token
- i_package_generic_deploy_token
- - i_package_golang_deploy_token
- i_package_helm_deploy_token
- i_package_maven_deploy_token
- i_package_npm_deploy_token
- i_package_nuget_deploy_token
- i_package_pypi_deploy_token
- i_package_rubygems_deploy_token
- - i_package_tag_deploy_token
- i_package_terraform_module_deploy_token
distribution:
- ee
diff --git a/config/metrics/counts_28d/20210216184937_user_packages_total_unique_counts_monthly.yml b/config/metrics/counts_28d/20210216184937_user_packages_total_unique_counts_monthly.yml
index 52e2eec71c5..e758f06bea9 100644
--- a/config/metrics/counts_28d/20210216184937_user_packages_total_unique_counts_monthly.yml
+++ b/config/metrics/counts_28d/20210216184937_user_packages_total_unique_counts_monthly.yml
@@ -15,17 +15,13 @@ options:
events:
- i_package_composer_user
- i_package_conan_user
- - i_package_container_user
- - i_package_debian_user
- i_package_generic_user
- - i_package_golang_user
- i_package_helm_user
- i_package_maven_user
- i_package_npm_user
- i_package_nuget_user
- i_package_pypi_user
- i_package_rubygems_user
- - i_package_tag_user
- i_package_terraform_module_user
distribution:
- ee
diff --git a/config/metrics/counts_28d/20220131143209_i_quickactions_attention_monthly.yml b/config/metrics/counts_28d/20220131143209_i_quickactions_attention_monthly.yml
index f3fa80b4f25..7be45e79f51 100644
--- a/config/metrics/counts_28d/20220131143209_i_quickactions_attention_monthly.yml
+++ b/config/metrics/counts_28d/20220131143209_i_quickactions_attention_monthly.yml
@@ -6,7 +6,9 @@ product_stage: create
product_group: code_review
product_category: code_review
value_type: number
-status: active
+status: removed
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95241/
+milestone_removed: '15.4'
milestone: '14.8'
introduced_by_url:
time_frame: 28d
diff --git a/config/metrics/counts_28d/20220131153230_i_quickactions_remove_attention_monthly.yml b/config/metrics/counts_28d/20220131153230_i_quickactions_remove_attention_monthly.yml
index 773fc5564bb..2b002bc3790 100644
--- a/config/metrics/counts_28d/20220131153230_i_quickactions_remove_attention_monthly.yml
+++ b/config/metrics/counts_28d/20220131153230_i_quickactions_remove_attention_monthly.yml
@@ -6,7 +6,9 @@ product_stage: create
product_group: code_review
product_category: code_review
value_type: number
-status: active
+status: removed
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95241/
+milestone_removed: '15.4'
milestone: '14.8'
introduced_by_url:
time_frame: 28d
diff --git a/config/metrics/counts_28d/20220504150641_count_notes_in_ipynb_diff_commit_monthly.yml b/config/metrics/counts_28d/20220504150641_count_notes_in_ipynb_diff_commit_monthly.yml
new file mode 100644
index 00000000000..87ee4ff392b
--- /dev/null
+++ b/config/metrics/counts_28d/20220504150641_count_notes_in_ipynb_diff_commit_monthly.yml
@@ -0,0 +1,27 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_count_notes_in_ipynb_diff_commit_monthly
+name: "count_notes_in_ipynb_diff_commit_monthly"
+description: Monthly notes on ipynb commit diffs
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.0"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85398
+time_frame: 28d
+data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_create_note_in_ipynb_diff_commit
+data_category: Optional
+performance_indicator_type: []
+distribution:
+ - ce
+ - ee
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_28d/20220504150641_count_notes_in_ipynb_diff_monthly.yml b/config/metrics/counts_28d/20220504150641_count_notes_in_ipynb_diff_monthly.yml
new file mode 100644
index 00000000000..8a5a50572d8
--- /dev/null
+++ b/config/metrics/counts_28d/20220504150641_count_notes_in_ipynb_diff_monthly.yml
@@ -0,0 +1,27 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_count_notes_in_ipynb_diff_monthly
+name: "count_notes_in_ipynb_diff_monthly"
+description: Monthly notes on ipynb diffs
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.0"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85398
+time_frame: 28d
+data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_create_note_in_ipynb_diff
+data_category: Optional
+performance_indicator_type: []
+distribution:
+ - ce
+ - ee
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_28d/20220504150641_count_notes_in_ipynb_diff_mr_monthly.yml b/config/metrics/counts_28d/20220504150641_count_notes_in_ipynb_diff_mr_monthly.yml
new file mode 100644
index 00000000000..907c1459293
--- /dev/null
+++ b/config/metrics/counts_28d/20220504150641_count_notes_in_ipynb_diff_mr_monthly.yml
@@ -0,0 +1,27 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_count_notes_in_ipynb_diff_mr_monthly
+name: "count_notes_in_ipynb_diff_mr_monthly"
+description: Monthly notes on ipynb MR diffs
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.0"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85398
+time_frame: 28d
+data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_create_note_in_ipynb_diff_mr
+data_category: Optional
+performance_indicator_type: []
+distribution:
+ - ce
+ - ee
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_28d/20220504150641_count_users_with_notes_in_ipynb_diff_commit_monthly.yml b/config/metrics/counts_28d/20220504150641_count_users_with_notes_in_ipynb_diff_commit_monthly.yml
new file mode 100644
index 00000000000..e33149db20f
--- /dev/null
+++ b/config/metrics/counts_28d/20220504150641_count_users_with_notes_in_ipynb_diff_commit_monthly.yml
@@ -0,0 +1,27 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_count_users_with_notes_in_ipynb_diff_commit_monthly
+name: "count_users_with_notes_in_ipynb_diff_commit_monthly"
+description: Monthly unique users with notes on ipynb commit diffs
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.0"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85398
+time_frame: 28d
+data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_user_create_note_in_ipynb_diff_commit
+data_category: Optional
+performance_indicator_type: []
+distribution:
+ - ce
+ - ee
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_28d/20220504150641_count_users_with_notes_in_ipynb_diff_monthly.yml b/config/metrics/counts_28d/20220504150641_count_users_with_notes_in_ipynb_diff_monthly.yml
new file mode 100644
index 00000000000..b6f8e09039e
--- /dev/null
+++ b/config/metrics/counts_28d/20220504150641_count_users_with_notes_in_ipynb_diff_monthly.yml
@@ -0,0 +1,27 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_count_users_with_notes_in_ipynb_diff_monthly
+name: "count_users_with_notes_in_ipynb_diff_monthly"
+description: Monthly unique users with notes on ipynb diffs
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.0"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85398
+time_frame: 28d
+data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_user_create_note_in_ipynb_diff
+data_category: Optional
+performance_indicator_type: []
+distribution:
+ - ce
+ - ee
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_28d/20220504150641_count_users_with_notes_in_ipynb_diff_mr_monthly.yml b/config/metrics/counts_28d/20220504150641_count_users_with_notes_in_ipynb_diff_mr_monthly.yml
new file mode 100644
index 00000000000..732124e34f0
--- /dev/null
+++ b/config/metrics/counts_28d/20220504150641_count_users_with_notes_in_ipynb_diff_mr_monthly.yml
@@ -0,0 +1,27 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_count_users_with_notes_in_ipynb_diff_mr_monthly
+name: "count_users_with_notes_in_ipynb_diff_mr_monthly"
+description: Monthly unique users with notes on ipynb MR diffs
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.0"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85398
+time_frame: 28d
+data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_user_create_note_in_ipynb_diff_mr
+data_category: Optional
+performance_indicator_type: []
+distribution:
+ - ce
+ - ee
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_28d/20220531145023_p_ci_templates_katalon_monthly.yml b/config/metrics/counts_28d/20220531145023_p_ci_templates_katalon_monthly.yml
new file mode 100644
index 00000000000..abbc30a5d10
--- /dev/null
+++ b/config/metrics/counts_28d/20220531145023_p_ci_templates_katalon_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_katalon_monthly
+description: 'Monthly counts of times users have executed katalon_tests jobs'
+product_section: 'ops'
+product_stage: 'analytics'
+product_group: 'pipeline_authoring'
+product_category: 'pipeline_authoring'
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86484
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - p_ci_templates_katalon
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20220627133135_i_code_review_submit_review_approve_monthly.yml b/config/metrics/counts_28d/20220627133135_i_code_review_submit_review_approve_monthly.yml
new file mode 100644
index 00000000000..389b9d3ace5
--- /dev/null
+++ b/config/metrics/counts_28d/20220627133135_i_code_review_submit_review_approve_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_submit_review_approve_monthly
+description: Count of unique users per month who submit a review and approve
+product_stage: create
+product_group: code_review
+product_category: code_review
+product_section: dev
+value_type: number
+status: active
+milestone: '15.4'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91073
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_submit_review_approve
+distribution:
+ - ce
+ - ee
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_28d/20220627134108_i_code_review_submit_review_comment_monthly.yml b/config/metrics/counts_28d/20220627134108_i_code_review_submit_review_comment_monthly.yml
new file mode 100644
index 00000000000..1891191bdcd
--- /dev/null
+++ b/config/metrics/counts_28d/20220627134108_i_code_review_submit_review_comment_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_submit_review_comment_monthly
+description: Count of unique users per month who submit a review with a comment
+product_stage: create
+product_group: code_review
+product_category: code_review
+product_section: dev
+value_type: number
+status: active
+milestone: '15.4'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91073
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_submit_review_comment
+distribution:
+ - ce
+ - ee
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_28d/20220816202928_bulk_import_entities_group_failed.yml b/config/metrics/counts_28d/20220816202928_bulk_import_entities_group_failed.yml
new file mode 100644
index 00000000000..3b19ef26a6a
--- /dev/null
+++ b/config/metrics/counts_28d/20220816202928_bulk_import_entities_group_failed.yml
@@ -0,0 +1,26 @@
+---
+data_category: optional
+key_path: usage_activity_by_stage_monthly.manage.group_imports.gitlab_migration_failed
+description: Count of group entities with failed status in GitLab Migration
+product_section: dev
+product_stage: manage
+product_group: import
+product_category: importers
+value_type: number
+status: active
+time_frame: 28d
+data_source: database
+instrumentation_class: CountBulkImportsEntitiesMetric
+options:
+ source_type: group_entity
+ status: -1
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96062"
diff --git a/config/metrics/counts_28d/20220816202928_bulk_import_entities_group_finished.yml b/config/metrics/counts_28d/20220816202928_bulk_import_entities_group_finished.yml
new file mode 100644
index 00000000000..72af0e3e18b
--- /dev/null
+++ b/config/metrics/counts_28d/20220816202928_bulk_import_entities_group_finished.yml
@@ -0,0 +1,26 @@
+---
+data_category: optional
+key_path: usage_activity_by_stage_monthly.manage.group_imports.gitlab_migration_finished
+description: Count of group entities with finished status in GitLab Migration
+product_section: dev
+product_stage: manage
+product_group: import
+product_category: importers
+value_type: number
+status: active
+time_frame: 28d
+data_source: database
+instrumentation_class: CountBulkImportsEntitiesMetric
+options:
+ source_type: group_entity
+ status: 2
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96062"
diff --git a/config/metrics/counts_28d/20220816202928_bulk_import_entities_group_timeout.yml b/config/metrics/counts_28d/20220816202928_bulk_import_entities_group_timeout.yml
new file mode 100644
index 00000000000..079fc328d73
--- /dev/null
+++ b/config/metrics/counts_28d/20220816202928_bulk_import_entities_group_timeout.yml
@@ -0,0 +1,26 @@
+---
+data_category: optional
+key_path: usage_activity_by_stage_monthly.manage.group_imports.gitlab_migration_timeout
+description: Count of group entities with timeout status in GitLab Migration
+product_section: dev
+product_stage: manage
+product_group: import
+product_category: importers
+value_type: number
+status: active
+time_frame: 28d
+data_source: database
+instrumentation_class: CountBulkImportsEntitiesMetric
+options:
+ source_type: group_entity
+ status: 3
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96062"
diff --git a/config/metrics/counts_28d/20220823125645_bulk_import_entities_project_timeout.yml b/config/metrics/counts_28d/20220823125645_bulk_import_entities_project_timeout.yml
new file mode 100644
index 00000000000..a9ead88577d
--- /dev/null
+++ b/config/metrics/counts_28d/20220823125645_bulk_import_entities_project_timeout.yml
@@ -0,0 +1,26 @@
+---
+data_category: optional
+key_path: usage_activity_by_stage_monthly.manage.project_imports.gitlab_migration_timeout
+description: Count of project entities with timeout status in GitLab Migration
+product_section: dev
+product_stage: manage
+product_group: import
+product_category: importers
+value_type: number
+status: active
+time_frame: 28d
+data_source: database
+instrumentation_class: CountBulkImportsEntitiesMetric
+options:
+ source_type: project_entity
+ status: 3
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96062"
diff --git a/config/metrics/counts_28d/20220823125924_bulk_import_entities_project_failed.yml b/config/metrics/counts_28d/20220823125924_bulk_import_entities_project_failed.yml
new file mode 100644
index 00000000000..74c45c05641
--- /dev/null
+++ b/config/metrics/counts_28d/20220823125924_bulk_import_entities_project_failed.yml
@@ -0,0 +1,26 @@
+---
+data_category: optional
+key_path: usage_activity_by_stage_monthly.manage.project_imports.gitlab_migration_failed
+description: Count of project entities with failed status in GitLab Migration
+product_section: dev
+product_stage: manage
+product_group: import
+product_category: importers
+value_type: number
+status: active
+time_frame: 28d
+data_source: database
+instrumentation_class: CountBulkImportsEntitiesMetric
+options:
+ source_type: project_entity
+ status: -1
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96062"
diff --git a/config/metrics/counts_28d/20220823130209_bulk_import_entities_project_finished.yml b/config/metrics/counts_28d/20220823130209_bulk_import_entities_project_finished.yml
new file mode 100644
index 00000000000..228d6851020
--- /dev/null
+++ b/config/metrics/counts_28d/20220823130209_bulk_import_entities_project_finished.yml
@@ -0,0 +1,26 @@
+---
+data_category: optional
+key_path: usage_activity_by_stage_monthly.manage.project_imports.gitlab_migration_finished
+description: Count of project entities with finished status in GitLab Migration
+product_section: dev
+product_stage: manage
+product_group: import
+product_category: importers
+value_type: number
+status: active
+time_frame: 28d
+data_source: database
+instrumentation_class: CountBulkImportsEntitiesMetric
+options:
+ source_type: project_entity
+ status: 2
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96062"
diff --git a/config/metrics/counts_28d/20220825142533_i_testing_test_report_uploaded_monthly.yml b/config/metrics/counts_28d/20220825142533_i_testing_test_report_uploaded_monthly.yml
new file mode 100644
index 00000000000..341d3810318
--- /dev/null
+++ b/config/metrics/counts_28d/20220825142533_i_testing_test_report_uploaded_monthly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.testing.i_testing_test_report_uploaded_monthly
+description: "MAU of junit test reports uploaded by customers per pipeline"
+product_section: ops
+product_stage: verify
+product_group: pipeline_insights
+product_category: testing
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95112
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_testing_test_report_uploaded
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20220825232557_count_user_auth.yml b/config/metrics/counts_28d/20220825232557_count_user_auth.yml
new file mode 100644
index 00000000000..dbcd665d616
--- /dev/null
+++ b/config/metrics/counts_28d/20220825232557_count_user_auth.yml
@@ -0,0 +1,24 @@
+---
+key_path: usage_activity_by_stage_monthly.manage.count_user_auth
+description: Number of unique user logins
+product_section: dev
+product_stage: manage
+product_group: authentication_and_authorization
+product_category: system_access
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96321"
+time_frame: 28d
+data_source: database
+instrumentation_class: CountUserAuthMetric
+data_category: optional
+performance_indicator_type:
+- smau
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20220830104453_i_code_review_merge_request_widget_license_compliance_view_monthly.yml b/config/metrics/counts_28d/20220830104453_i_code_review_merge_request_widget_license_compliance_view_monthly.yml
new file mode 100644
index 00000000000..60567037303
--- /dev/null
+++ b/config/metrics/counts_28d/20220830104453_i_code_review_merge_request_widget_license_compliance_view_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_license_compliance_view_monthly
+description: The count of unique users (monthly) who were able to see the License Compliance widget extension
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96538"
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_license_compliance_view
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20220830104500_i_code_review_merge_request_widget_license_compliance_full_report_clicked_monthly.yml b/config/metrics/counts_28d/20220830104500_i_code_review_merge_request_widget_license_compliance_full_report_clicked_monthly.yml
new file mode 100644
index 00000000000..3b4c0012495
--- /dev/null
+++ b/config/metrics/counts_28d/20220830104500_i_code_review_merge_request_widget_license_compliance_full_report_clicked_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_license_compliance_full_report_clicked_monthly
+description: The count of unique users (monthly) who clicked the Full Report button on the License Compliance widget extension
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96538"
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_license_compliance_full_report_clicked
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20220830104507_i_code_review_merge_request_widget_license_compliance_expand_monthly.yml b/config/metrics/counts_28d/20220830104507_i_code_review_merge_request_widget_license_compliance_expand_monthly.yml
new file mode 100644
index 00000000000..7b0ec9d0518
--- /dev/null
+++ b/config/metrics/counts_28d/20220830104507_i_code_review_merge_request_widget_license_compliance_expand_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_license_compliance_expand_monthly
+description: The count of unique users (monthly) who expanded the License Compliance widget extension
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96538"
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_license_compliance_expand
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20220830104514_i_code_review_merge_request_widget_license_compliance_expand_success_monthly.yml b/config/metrics/counts_28d/20220830104514_i_code_review_merge_request_widget_license_compliance_expand_success_monthly.yml
new file mode 100644
index 00000000000..c6b7689c844
--- /dev/null
+++ b/config/metrics/counts_28d/20220830104514_i_code_review_merge_request_widget_license_compliance_expand_success_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_license_compliance_expand_success_monthly
+description: The count of unique users (monthly) who expanded the License Compliance 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.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96538"
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_license_compliance_expand_success
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20220830104521_i_code_review_merge_request_widget_license_compliance_expand_warning_monthly.yml b/config/metrics/counts_28d/20220830104521_i_code_review_merge_request_widget_license_compliance_expand_warning_monthly.yml
new file mode 100644
index 00000000000..5e164b5c3bf
--- /dev/null
+++ b/config/metrics/counts_28d/20220830104521_i_code_review_merge_request_widget_license_compliance_expand_warning_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_license_compliance_expand_warning_monthly
+description: The count of unique users (monthly) who expanded the License Compliance 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.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96538"
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_license_compliance_expand_warning
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20220830104528_i_code_review_merge_request_widget_license_compliance_expand_failed_monthly.yml b/config/metrics/counts_28d/20220830104528_i_code_review_merge_request_widget_license_compliance_expand_failed_monthly.yml
new file mode 100644
index 00000000000..69af6c70299
--- /dev/null
+++ b/config/metrics/counts_28d/20220830104528_i_code_review_merge_request_widget_license_compliance_expand_failed_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_license_compliance_expand_failed_monthly
+description: The count of unique users (monthly) who expanded the License Compliance 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.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96538"
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_license_compliance_expand_failed
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20220905210112_users_visiting_environments_pages_monthly.yml b/config/metrics/counts_28d/20220905210112_users_visiting_environments_pages_monthly.yml
new file mode 100644
index 00000000000..3eac5ca1c01
--- /dev/null
+++ b/config/metrics/counts_28d/20220905210112_users_visiting_environments_pages_monthly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.environments.users_visiting_environments_pages_monthly
+description: Monthly count of unique users visiting environments pages
+product_section: ops
+product_stage: release
+product_group: release
+product_category: environment_management
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97063
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+options:
+ events:
+ - users_visiting_environments_pages
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20220907080630_i_quickactions_timeline_monthly.yml b/config/metrics/counts_28d/20220907080630_i_quickactions_timeline_monthly.yml
new file mode 100644
index 00000000000..f3f9499e9d9
--- /dev/null
+++ b/config/metrics/counts_28d/20220907080630_i_quickactions_timeline_monthly.yml
@@ -0,0 +1,27 @@
+---
+key_path: redis_hll_counters.quickactions.i_quickactions_timeline_monthly
+name: quickactions_timeline_monthly
+description: Count of MAU using the `/timeline` quick action
+product_section: ops
+product_stage: monitor
+product_group: respond
+product_category: incident_management
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97020
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_quickactions_timeline
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20220907084347_p_ci_templates_implicit_security_sast_iac_monthly.yml b/config/metrics/counts_28d/20220907084347_p_ci_templates_implicit_security_sast_iac_monthly.yml
new file mode 100644
index 00000000000..2f32d5a3569
--- /dev/null
+++ b/config/metrics/counts_28d/20220907084347_p_ci_templates_implicit_security_sast_iac_monthly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_security_sast_iac_monthly
+description: Count of pipelines with implicit SAST runs using the stable SAST IaC template
+product_section: sec
+product_stage: secure
+product_group: "static_analysis"
+product_category: SAST
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86275
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_implicit_jobs_sast_iac
diff --git a/config/metrics/counts_28d/20220907102714_p_ci_templates_implicit_jobs_sast_iac_monthly.yml b/config/metrics/counts_28d/20220907102714_p_ci_templates_implicit_jobs_sast_iac_monthly.yml
new file mode 100644
index 00000000000..368c15653e9
--- /dev/null
+++ b/config/metrics/counts_28d/20220907102714_p_ci_templates_implicit_jobs_sast_iac_monthly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_sast_iac_monthly
+description: Count of pipelines with implicit SAST jobs using the stable SAST IaC template
+product_section: sec
+product_stage: secure
+product_group: "static_analysis"
+product_category: SAST
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86275
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_implicit_jobs_sast_iac
diff --git a/config/metrics/counts_28d/20220907202807_p_ci_templates_jobs_dependency_scanning_latest_monthly.yml b/config/metrics/counts_28d/20220907202807_p_ci_templates_jobs_dependency_scanning_latest_monthly.yml
new file mode 100644
index 00000000000..9cbfee008c3
--- /dev/null
+++ b/config/metrics/counts_28d/20220907202807_p_ci_templates_jobs_dependency_scanning_latest_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_dependency_scanning_latest_monthly
+description: Monthly counts for Dependency Scanning CI Latest template (Jobs folder)
+product_section: sec
+product_stage: secure
+product_group: composition_analysis
+product_category: dependency_scanning
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97323'
+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_dependency_scanning_latest
diff --git a/config/metrics/counts_28d/20220907212005_p_ci_templates_security_container_scanning_latest_monthly.yml b/config/metrics/counts_28d/20220907212005_p_ci_templates_security_container_scanning_latest_monthly.yml
new file mode 100644
index 00000000000..fbf263cd6bc
--- /dev/null
+++ b/config/metrics/counts_28d/20220907212005_p_ci_templates_security_container_scanning_latest_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_security_container_scanning_latest_monthly
+description: Monthly counts for Container Scanning CI Latest template (Security folder)
+product_section: sec
+product_stage: secure
+product_group: composition_analysis
+product_category: container_scanning
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97323'
+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_security_container_scanning_latest
diff --git a/config/metrics/counts_28d/20220907215635_p_ci_templates_jobs_license_scanning_latest_monthly.yml b/config/metrics/counts_28d/20220907215635_p_ci_templates_jobs_license_scanning_latest_monthly.yml
new file mode 100644
index 00000000000..b78e5bc65fa
--- /dev/null
+++ b/config/metrics/counts_28d/20220907215635_p_ci_templates_jobs_license_scanning_latest_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_license_scanning_latest_monthly
+description: Monthly counts for License Scanning CI Latest template (Jobs folder)
+product_section: sec
+product_stage: secure
+product_group: composition_analysis
+product_category: license_scanning
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97323'
+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_license_scanning_latest
diff --git a/config/metrics/counts_28d/20220912161240_p_ci_templates_implicit_jobs_dependency_scanning_latest_monthly.yml b/config/metrics/counts_28d/20220912161240_p_ci_templates_implicit_jobs_dependency_scanning_latest_monthly.yml
new file mode 100644
index 00000000000..a3a435fca09
--- /dev/null
+++ b/config/metrics/counts_28d/20220912161240_p_ci_templates_implicit_jobs_dependency_scanning_latest_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_dependency_scanning_latest_monthly
+description: Monthly counts for implicit Dependency Scanning CI Latest template (Jobs folder)
+product_section: sec
+product_stage: secure
+product_group: composition_analysis
+product_category: dependency_scanning
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97323'
+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_dependency_scanning_latest
diff --git a/config/metrics/counts_28d/20220912162308_p_ci_templates_implicit_jobs_license_scanning_latest_monthly.yml b/config/metrics/counts_28d/20220912162308_p_ci_templates_implicit_jobs_license_scanning_latest_monthly.yml
new file mode 100644
index 00000000000..acc48ff2a44
--- /dev/null
+++ b/config/metrics/counts_28d/20220912162308_p_ci_templates_implicit_jobs_license_scanning_latest_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_license_scanning_latest_monthly
+description: Monthly counts for implicit License Scanning CI Latest template (Jobs folder)
+product_section: sec
+product_stage: secure
+product_group: composition_analysis
+product_category: license_scanning
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97323'
+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_license_scanning_latest
diff --git a/config/metrics/counts_28d/20220912162752_p_ci_templates_implicit_security_container_scanning_latest_monthly.yml b/config/metrics/counts_28d/20220912162752_p_ci_templates_implicit_security_container_scanning_latest_monthly.yml
new file mode 100644
index 00000000000..2aebbc3a9eb
--- /dev/null
+++ b/config/metrics/counts_28d/20220912162752_p_ci_templates_implicit_security_container_scanning_latest_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_security_container_scanning_latest_monthly
+description: Monthly counts for implicit Container Scanning CI Latest template (Security folder)
+product_section: sec
+product_stage: secure
+product_group: composition_analysis
+product_category: container_scanning
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97323'
+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_security_container_scanning_latest
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 ab39318eb0d..60619deb786 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
@@ -13,62 +13,122 @@ data_source: redis_hll
instrumentation_class: RedisHLLMetric
options:
events:
- - i_code_review_mr_diffs
- - i_code_review_user_single_file_diffs
- - i_code_review_mr_single_file_diffs
- - i_code_review_user_toggled_task_item_status
- - i_code_review_user_create_mr
- - i_code_review_user_close_mr
- - i_code_review_user_reopen_mr
- - i_code_review_user_approve_mr
- - i_code_review_user_unapprove_mr
- - i_code_review_user_resolve_thread
- - i_code_review_user_unresolve_thread
- - i_code_review_edit_mr_title
- - i_code_review_edit_mr_desc
- - i_code_review_user_merge_mr
- - i_code_review_user_create_mr_comment
- - i_code_review_user_edit_mr_comment
- - i_code_review_user_remove_mr_comment
- - i_code_review_user_create_review_note
- - i_code_review_user_publish_review
- - i_code_review_user_create_multiline_mr_comment
- - i_code_review_user_edit_multiline_mr_comment
- - i_code_review_user_remove_multiline_mr_comment
- - i_code_review_user_add_suggestion
- - i_code_review_user_apply_suggestion
- - i_code_review_user_assigned
- - i_code_review_user_marked_as_draft
- - i_code_review_user_unmarked_as_draft
- - i_code_review_user_review_requested
- - i_code_review_user_approval_rule_added
- - i_code_review_user_approval_rule_deleted
- - i_code_review_user_approval_rule_edited
- - i_code_review_user_vs_code_api_request
- - i_code_review_user_create_mr_from_issue
- - i_code_review_user_mr_discussion_locked
- - i_code_review_user_mr_discussion_unlocked
- - i_code_review_user_time_estimate_changed
- - i_code_review_user_time_spent_changed
- - i_code_review_user_assignees_changed
- - i_code_review_user_reviewers_changed
- - i_code_review_user_milestone_changed
- - i_code_review_user_labels_changed
- - i_code_review_click_diff_view_setting
- - i_code_review_click_single_file_mode_setting
- - i_code_review_click_file_browser_setting
- - i_code_review_click_whitespace_setting
- - i_code_review_diff_view_inline
- - i_code_review_diff_view_parallel
- - i_code_review_file_browser_tree_view
- - i_code_review_file_browser_list_view
- - i_code_review_diff_show_whitespace
- - i_code_review_diff_hide_whitespace
- - i_code_review_diff_single_file
- - i_code_review_diff_multiple_files
- - i_code_review_user_load_conflict_ui
- - i_code_review_user_resolve_conflict
- - i_code_review_user_searches_diff
+ - i_code_review_click_diff_view_setting
+ - i_code_review_click_file_browser_setting
+ - i_code_review_click_single_file_mode_setting
+ - i_code_review_click_whitespace_setting
+ - i_code_review_create_note_in_ipynb_diff
+ - i_code_review_create_note_in_ipynb_diff_commit
+ - i_code_review_create_note_in_ipynb_diff_mr
+ - i_code_review_diff_hide_whitespace
+ - i_code_review_diff_multiple_files
+ - i_code_review_diff_show_whitespace
+ - i_code_review_diff_single_file
+ - i_code_review_diff_view_inline
+ - i_code_review_diff_view_parallel
+ - i_code_review_edit_mr_desc
+ - i_code_review_edit_mr_title
+ - i_code_review_file_browser_list_view
+ - i_code_review_file_browser_tree_view
+ - i_code_review_merge_request_widget_accessibility_expand
+ - i_code_review_merge_request_widget_accessibility_expand_failed
+ - i_code_review_merge_request_widget_accessibility_expand_success
+ - i_code_review_merge_request_widget_accessibility_expand_warning
+ - i_code_review_merge_request_widget_accessibility_full_report_clicked
+ - i_code_review_merge_request_widget_accessibility_view
+ - i_code_review_merge_request_widget_code_quality_expand
+ - i_code_review_merge_request_widget_code_quality_expand_failed
+ - i_code_review_merge_request_widget_code_quality_expand_success
+ - i_code_review_merge_request_widget_code_quality_expand_warning
+ - i_code_review_merge_request_widget_code_quality_full_report_clicked
+ - i_code_review_merge_request_widget_code_quality_view
+ - i_code_review_merge_request_widget_metrics_expand
+ - i_code_review_merge_request_widget_metrics_expand_failed
+ - i_code_review_merge_request_widget_metrics_expand_success
+ - i_code_review_merge_request_widget_metrics_expand_warning
+ - i_code_review_merge_request_widget_metrics_full_report_clicked
+ - i_code_review_merge_request_widget_metrics_view
+ - i_code_review_merge_request_widget_status_checks_expand
+ - i_code_review_merge_request_widget_status_checks_expand_failed
+ - i_code_review_merge_request_widget_status_checks_expand_success
+ - i_code_review_merge_request_widget_status_checks_expand_warning
+ - i_code_review_merge_request_widget_status_checks_full_report_clicked
+ - i_code_review_merge_request_widget_status_checks_view
+ - i_code_review_merge_request_widget_terraform_expand
+ - i_code_review_merge_request_widget_terraform_expand_failed
+ - i_code_review_merge_request_widget_terraform_expand_success
+ - i_code_review_merge_request_widget_terraform_expand_warning
+ - i_code_review_merge_request_widget_terraform_full_report_clicked
+ - i_code_review_merge_request_widget_terraform_view
+ - i_code_review_merge_request_widget_test_summary_expand
+ - i_code_review_merge_request_widget_test_summary_expand_failed
+ - i_code_review_merge_request_widget_test_summary_expand_success
+ - i_code_review_merge_request_widget_test_summary_expand_warning
+ - i_code_review_merge_request_widget_test_summary_full_report_clicked
+ - i_code_review_merge_request_widget_test_summary_view
+ - i_code_review_merge_request_widget_license_compliance_expand
+ - i_code_review_merge_request_widget_license_compliance_expand_failed
+ - i_code_review_merge_request_widget_license_compliance_expand_success
+ - i_code_review_merge_request_widget_license_compliance_warning
+ - i_code_review_merge_request_widget_license_compliance_full_report_clicked
+ - i_code_review_merge_request_widget_license_compliance_view
+ - i_code_review_mr_diffs
+ - i_code_review_mr_single_file_diffs
+ - i_code_review_mr_with_invalid_approvers
+ - i_code_review_post_merge_click_cherry_pick
+ - i_code_review_post_merge_click_revert
+ - i_code_review_post_merge_delete_branch
+ - i_code_review_post_merge_submit_cherry_pick_modal
+ - i_code_review_post_merge_submit_revert_modal
+ - i_code_review_total_suggestions_added
+ - i_code_review_total_suggestions_applied
+ - i_code_review_user_add_suggestion
+ - i_code_review_user_apply_suggestion
+ - i_code_review_user_approval_rule_added
+ - i_code_review_user_approval_rule_deleted
+ - i_code_review_user_approval_rule_edited
+ - i_code_review_user_approve_mr
+ - i_code_review_user_assigned
+ - i_code_review_user_assignees_changed
+ - i_code_review_user_close_mr
+ - i_code_review_user_create_mr
+ - i_code_review_user_create_mr_comment
+ - i_code_review_user_create_mr_from_issue
+ - i_code_review_user_create_multiline_mr_comment
+ - i_code_review_user_create_note_in_ipynb_diff
+ - i_code_review_user_create_note_in_ipynb_diff_commit
+ - i_code_review_user_create_note_in_ipynb_diff_mr
+ - i_code_review_user_create_review_note
+ - i_code_review_user_edit_mr_comment
+ - i_code_review_user_edit_multiline_mr_comment
+ - i_code_review_user_gitlab_cli_api_request
+ - i_code_review_user_jetbrains_api_request
+ - i_code_review_user_labels_changed
+ - i_code_review_user_load_conflict_ui
+ - i_code_review_user_marked_as_draft
+ - i_code_review_user_merge_mr
+ - i_code_review_user_milestone_changed
+ - i_code_review_user_mr_discussion_locked
+ - i_code_review_user_mr_discussion_unlocked
+ - i_code_review_user_publish_review
+ - i_code_review_user_remove_mr_comment
+ - i_code_review_user_remove_multiline_mr_comment
+ - i_code_review_user_reopen_mr
+ - i_code_review_user_resolve_conflict
+ - i_code_review_user_resolve_thread
+ - i_code_review_user_resolve_thread_in_issue
+ - i_code_review_user_review_requested
+ - i_code_review_user_reviewers_changed
+ - i_code_review_user_searches_diff
+ - i_code_review_user_single_file_diffs
+ - i_code_review_user_time_estimate_changed
+ - i_code_review_user_time_spent_changed
+ - i_code_review_user_toggled_task_item_status
+ - i_code_review_user_unapprove_mr
+ - i_code_review_user_unmarked_as_draft
+ - i_code_review_user_unresolve_thread
+ - i_code_review_user_vs_code_api_request
+ - i_code_review_widget_nothing_merge_click_new_file
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 aa25ab379b9..16186a412b8 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
@@ -51,6 +51,7 @@ options:
- p_ci_templates_security_dast_api
- p_ci_templates_security_dast_api_latest
- p_ci_templates_security_container_scanning
+ - p_ci_templates_security_container_scanning_latest
- p_ci_templates_security_dast_latest
- p_ci_templates_security_dependency_scanning
- p_ci_templates_security_api_fuzzing
@@ -94,7 +95,9 @@ options:
- p_ci_templates_jobs_code_intelligence
- p_ci_templates_jobs_code_quality
- p_ci_templates_jobs_dependency_scanning
+ - p_ci_templates_jobs_dependency_scanning_latest
- p_ci_templates_jobs_license_scanning
+ - p_ci_templates_jobs_license_scanning_latest
- p_ci_templates_jobs_deploy_ecs
- p_ci_templates_jobs_deploy_ec2
- p_ci_templates_jobs_deploy
@@ -172,6 +175,7 @@ options:
- p_ci_templates_liquibase
- p_ci_templates_matlab
- p_ci_templates_themekit
+ - p_ci_templates_katalon
distribution:
- ce
- ee
diff --git a/config/metrics/counts_7d/20210216184848_deploy_token_packages_total_unique_counts_weekly.yml b/config/metrics/counts_7d/20210216184848_deploy_token_packages_total_unique_counts_weekly.yml
index 671b8a1785a..28adf6d6d01 100644
--- a/config/metrics/counts_7d/20210216184848_deploy_token_packages_total_unique_counts_weekly.yml
+++ b/config/metrics/counts_7d/20210216184848_deploy_token_packages_total_unique_counts_weekly.yml
@@ -15,17 +15,13 @@ options:
events:
- i_package_composer_deploy_token
- i_package_conan_deploy_token
- - i_package_container_deploy_token
- - i_package_debian_deploy_token
- i_package_generic_deploy_token
- - i_package_golang_deploy_token
- i_package_helm_deploy_token
- i_package_maven_deploy_token
- i_package_npm_deploy_token
- i_package_nuget_deploy_token
- i_package_pypi_deploy_token
- i_package_rubygems_deploy_token
- - i_package_tag_deploy_token
- i_package_terraform_module_deploy_token
distribution:
- ee
diff --git a/config/metrics/counts_7d/20210216184935_user_packages_total_unique_counts_weekly.yml b/config/metrics/counts_7d/20210216184935_user_packages_total_unique_counts_weekly.yml
index 81181baa4ee..e7465b79667 100644
--- a/config/metrics/counts_7d/20210216184935_user_packages_total_unique_counts_weekly.yml
+++ b/config/metrics/counts_7d/20210216184935_user_packages_total_unique_counts_weekly.yml
@@ -15,17 +15,13 @@ options:
events:
- i_package_composer_user
- i_package_conan_user
- - i_package_container_user
- - i_package_debian_user
- i_package_generic_user
- - i_package_golang_user
- i_package_helm_user
- i_package_maven_user
- i_package_npm_user
- i_package_nuget_user
- i_package_pypi_user
- i_package_rubygems_user
- - i_package_tag_user
- i_package_terraform_module_user
distribution:
- ee
diff --git a/config/metrics/counts_7d/20220131143201_i_quickactions_attention_weekly.yml b/config/metrics/counts_7d/20220131143201_i_quickactions_attention_weekly.yml
index 3e47fca1265..ec725e331e9 100644
--- a/config/metrics/counts_7d/20220131143201_i_quickactions_attention_weekly.yml
+++ b/config/metrics/counts_7d/20220131143201_i_quickactions_attention_weekly.yml
@@ -6,7 +6,9 @@ product_stage: create
product_group: code_review
product_category: code_review
value_type: number
-status: active
+status: removed
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95241/
+milestone_removed: '15.4'
milestone: '14.8'
introduced_by_url:
time_frame: 7d
diff --git a/config/metrics/counts_7d/20220131153223_i_quickactions_remove_attention_weekly.yml b/config/metrics/counts_7d/20220131153223_i_quickactions_remove_attention_weekly.yml
index 315cac183d1..275776eb793 100644
--- a/config/metrics/counts_7d/20220131153223_i_quickactions_remove_attention_weekly.yml
+++ b/config/metrics/counts_7d/20220131153223_i_quickactions_remove_attention_weekly.yml
@@ -6,7 +6,9 @@ product_stage: create
product_group: code_review
product_category: code_review
value_type: number
-status: active
+status: removed
+removed_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95241/
+milestone_removed: '15.4'
milestone: '14.8'
introduced_by_url:
time_frame: 7d
diff --git a/config/metrics/counts_7d/20220504150641_count_notes_in_ipynb_diff_commit_weekly.yml b/config/metrics/counts_7d/20220504150641_count_notes_in_ipynb_diff_commit_weekly.yml
new file mode 100644
index 00000000000..b59129cea27
--- /dev/null
+++ b/config/metrics/counts_7d/20220504150641_count_notes_in_ipynb_diff_commit_weekly.yml
@@ -0,0 +1,27 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_count_notes_in_ipynb_diff_commit_weekly
+name: "count_notes_in_ipynb_diff_commit_weekly"
+description: Weekly notes on ipynb commit diffs
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.0"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85398
+time_frame: 7d
+data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_create_note_in_ipynb_diff_commit
+data_category: Optional
+performance_indicator_type: []
+distribution:
+ - ce
+ - ee
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20220504150641_count_notes_in_ipynb_diff_mr_weekly.yml b/config/metrics/counts_7d/20220504150641_count_notes_in_ipynb_diff_mr_weekly.yml
new file mode 100644
index 00000000000..30947b412fe
--- /dev/null
+++ b/config/metrics/counts_7d/20220504150641_count_notes_in_ipynb_diff_mr_weekly.yml
@@ -0,0 +1,27 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_count_notes_in_ipynb_diff_mr_weekly
+name: "count_notes_in_ipynb_diff_mr_weekly"
+description: Weekly notes on ipynb MR diffs
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.0"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85398
+time_frame: 7d
+data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_create_note_in_ipynb_diff_mr
+data_category: Optional
+performance_indicator_type: []
+distribution:
+ - ce
+ - ee
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20220504150641_count_notes_in_ipynb_diff_weekly.yml b/config/metrics/counts_7d/20220504150641_count_notes_in_ipynb_diff_weekly.yml
new file mode 100644
index 00000000000..d1f331ac1e1
--- /dev/null
+++ b/config/metrics/counts_7d/20220504150641_count_notes_in_ipynb_diff_weekly.yml
@@ -0,0 +1,27 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_count_notes_in_ipynb_diff_weekly
+name: "count_notes_in_ipynb_diff_weekly"
+description: Weekly notes on ipynb diffs
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.0"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85398
+time_frame: 7d
+data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_create_note_in_ipynb_diff
+data_category: Optional
+performance_indicator_type: []
+distribution:
+ - ce
+ - ee
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20220504150641_count_users_with_notes_in_ipynb_diff_commit_weekly.yml b/config/metrics/counts_7d/20220504150641_count_users_with_notes_in_ipynb_diff_commit_weekly.yml
new file mode 100644
index 00000000000..d57c100ec1e
--- /dev/null
+++ b/config/metrics/counts_7d/20220504150641_count_users_with_notes_in_ipynb_diff_commit_weekly.yml
@@ -0,0 +1,27 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_count_users_with_notes_in_ipynb_diff_commit_weekly
+name: "count_users_with_notes_in_ipynb_diff_commit_weekly"
+description: Weekly unique users with notes on ipynb commit diffs
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.0"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85398
+time_frame: 7d
+data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_user_create_note_in_ipynb_diff_commit
+data_category: Optional
+performance_indicator_type: []
+distribution:
+ - ce
+ - ee
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20220504150641_count_users_with_notes_in_ipynb_diff_mr_weekly.yml b/config/metrics/counts_7d/20220504150641_count_users_with_notes_in_ipynb_diff_mr_weekly.yml
new file mode 100644
index 00000000000..64d856782c2
--- /dev/null
+++ b/config/metrics/counts_7d/20220504150641_count_users_with_notes_in_ipynb_diff_mr_weekly.yml
@@ -0,0 +1,27 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_count_users_with_notes_in_ipynb_diff_mr_weekly
+name: "count_users_with_notes_in_ipynb_diff_mr_weekly"
+description: Weekly unique users with notes on ipynb MR diffs
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.0"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85398
+time_frame: 7d
+data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_user_create_note_in_ipynb_diff_mr
+data_category: Optional
+performance_indicator_type: []
+distribution:
+ - ce
+ - ee
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20220504150641_count_users_with_notes_in_ipynb_diff_weekly.yml b/config/metrics/counts_7d/20220504150641_count_users_with_notes_in_ipynb_diff_weekly.yml
new file mode 100644
index 00000000000..3d01988de75
--- /dev/null
+++ b/config/metrics/counts_7d/20220504150641_count_users_with_notes_in_ipynb_diff_weekly.yml
@@ -0,0 +1,27 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_count_users_with_notes_in_ipynb_diff_weekly
+name: "count_users_with_notes_in_ipynb_diff_weekly"
+description: Weekly unique users with notes on ipynb diffs
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.0"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85398
+time_frame: 7d
+data_source: redis_hll
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_user_create_note_in_ipynb_diff
+data_category: Optional
+performance_indicator_type: []
+distribution:
+ - ce
+ - ee
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20220531145014_p_ci_templates_katalon_weekly.yml b/config/metrics/counts_7d/20220531145014_p_ci_templates_katalon_weekly.yml
new file mode 100644
index 00000000000..f668646eacb
--- /dev/null
+++ b/config/metrics/counts_7d/20220531145014_p_ci_templates_katalon_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_katalon_weekly
+description: 'Weekly counts of times users have executed katalon_tests jobs'
+product_section: 'ops'
+product_stage: 'analytics'
+product_group: 'pipeline_authoring'
+product_category: 'pipeline_authoring'
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86484
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - p_ci_templates_katalon
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20220627133128_i_code_review_submit_review_approve_weekly.yml b/config/metrics/counts_7d/20220627133128_i_code_review_submit_review_approve_weekly.yml
new file mode 100644
index 00000000000..2275bf185d8
--- /dev/null
+++ b/config/metrics/counts_7d/20220627133128_i_code_review_submit_review_approve_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_submit_review_approve_weekly
+description: Count of unique users per week who submit a review and approve
+product_stage: create
+product_group: code_review
+product_category: code_review
+product_section: dev
+value_type: number
+status: active
+milestone: '15.4'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91073
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_submit_review_approve
+distribution:
+ - ce
+ - ee
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20220627134100_i_code_review_submit_review_comment_weekly.yml b/config/metrics/counts_7d/20220627134100_i_code_review_submit_review_comment_weekly.yml
new file mode 100644
index 00000000000..16029f05626
--- /dev/null
+++ b/config/metrics/counts_7d/20220627134100_i_code_review_submit_review_comment_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_submit_review_comment_weekly
+description: Count of unique users per week who submit a review with a comment
+product_stage: create
+product_group: code_review
+product_category: code_review
+product_section: dev
+value_type: number
+status: active
+milestone: '15.4'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91073
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_submit_review_comment
+distribution:
+ - ce
+ - ee
+tier:
+ - free
+ - premium
+ - ultimate
diff --git a/config/metrics/counts_7d/20220825142528_i_testing_test_report_uploaded_weekly.yml b/config/metrics/counts_7d/20220825142528_i_testing_test_report_uploaded_weekly.yml
new file mode 100644
index 00000000000..814a50554c1
--- /dev/null
+++ b/config/metrics/counts_7d/20220825142528_i_testing_test_report_uploaded_weekly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.testing.i_testing_test_report_uploaded_weekly
+description: "MAU of junit test reports uploaded by customers per pipeline"
+product_section: ops
+product_stage: verify
+product_group: pipeline_insights
+product_category: testing
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95112
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_testing_test_report_uploaded
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20220830104410_i_code_review_merge_request_widget_license_compliance_view_weekly.yml b/config/metrics/counts_7d/20220830104410_i_code_review_merge_request_widget_license_compliance_view_weekly.yml
new file mode 100644
index 00000000000..6e9415bdc42
--- /dev/null
+++ b/config/metrics/counts_7d/20220830104410_i_code_review_merge_request_widget_license_compliance_view_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_license_compliance_view_weekly
+description: The count of unique users (weekly) who were able to see the License Compliance widget extension
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96538"
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_license_compliance_view
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20220830104418_i_code_review_merge_request_widget_license_compliance_full_report_clicked_weekly.yml b/config/metrics/counts_7d/20220830104418_i_code_review_merge_request_widget_license_compliance_full_report_clicked_weekly.yml
new file mode 100644
index 00000000000..c3a8d7bfdfb
--- /dev/null
+++ b/config/metrics/counts_7d/20220830104418_i_code_review_merge_request_widget_license_compliance_full_report_clicked_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_license_compliance_full_report_clicked_weekly
+description: The count of unique users (weekly) who clicked the Full Report button on the License Compliance widget extension
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96538"
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_license_compliance_full_report_clicked
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20220830104424_i_code_review_merge_request_widget_license_compliance_expand_weekly.yml b/config/metrics/counts_7d/20220830104424_i_code_review_merge_request_widget_license_compliance_expand_weekly.yml
new file mode 100644
index 00000000000..69083c5ca2c
--- /dev/null
+++ b/config/metrics/counts_7d/20220830104424_i_code_review_merge_request_widget_license_compliance_expand_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_license_compliance_expand_weekly
+description: The count of unique users (weekly) who expanded the License Compliance widget extension
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96538"
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_license_compliance_expand
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20220830104431_i_code_review_merge_request_widget_license_compliance_expand_success_weekly.yml b/config/metrics/counts_7d/20220830104431_i_code_review_merge_request_widget_license_compliance_expand_success_weekly.yml
new file mode 100644
index 00000000000..d145ae31f38
--- /dev/null
+++ b/config/metrics/counts_7d/20220830104431_i_code_review_merge_request_widget_license_compliance_expand_success_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_license_compliance_expand_success_weekly
+description: The count of unique users (weekly) who expanded the License Compliance 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.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96538"
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_license_compliance_expand_success
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20220830104438_i_code_review_merge_request_widget_license_compliance_expand_warning_weekly.yml b/config/metrics/counts_7d/20220830104438_i_code_review_merge_request_widget_license_compliance_expand_warning_weekly.yml
new file mode 100644
index 00000000000..58c3d89497f
--- /dev/null
+++ b/config/metrics/counts_7d/20220830104438_i_code_review_merge_request_widget_license_compliance_expand_warning_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_license_compliance_expand_warning_weekly
+description: The count of unique users (weekly) who expanded the License Compliance 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.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96538"
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_license_compliance_expand_warning
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20220830104446_i_code_review_merge_request_widget_license_compliance_expand_failed_weekly.yml b/config/metrics/counts_7d/20220830104446_i_code_review_merge_request_widget_license_compliance_expand_failed_weekly.yml
new file mode 100644
index 00000000000..d8d9637f920
--- /dev/null
+++ b/config/metrics/counts_7d/20220830104446_i_code_review_merge_request_widget_license_compliance_expand_failed_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_license_compliance_expand_failed_weekly
+description: The count of unique users (weekly) who expanded the License Compliance 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.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96538"
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_license_compliance_expand_failed
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20220907080626_i_quickactions_timeline_weekly.yml b/config/metrics/counts_7d/20220907080626_i_quickactions_timeline_weekly.yml
new file mode 100644
index 00000000000..5b10a9a93d4
--- /dev/null
+++ b/config/metrics/counts_7d/20220907080626_i_quickactions_timeline_weekly.yml
@@ -0,0 +1,27 @@
+---
+key_path: redis_hll_counters.quickactions.i_quickactions_timeline_weekly
+name: quickactions_timeline_weekly
+description: Count of WAU using the `/timeline` quick action
+product_section: ops
+product_stage: monitor
+product_group: respond
+product_category: incident_management
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97020
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_quickactions_timeline
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20220907084343_p_ci_templates_implicit_security_sast_iac_weekly.yml b/config/metrics/counts_7d/20220907084343_p_ci_templates_implicit_security_sast_iac_weekly.yml
new file mode 100644
index 00000000000..c8e4c285492
--- /dev/null
+++ b/config/metrics/counts_7d/20220907084343_p_ci_templates_implicit_security_sast_iac_weekly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_security_sast_iac_weekly
+description: Count of pipelines with implicit SAST runs using the stable SAST IaC template
+product_section: sec
+product_stage: secure
+product_group: "static_analysis"
+product_category: SAST
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86275
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_implicit_jobs_sast_iac
diff --git a/config/metrics/counts_7d/20220907102710_p_ci_templates_implicit_jobs_sast_iac_weekly.yml b/config/metrics/counts_7d/20220907102710_p_ci_templates_implicit_jobs_sast_iac_weekly.yml
new file mode 100644
index 00000000000..faf4df4b772
--- /dev/null
+++ b/config/metrics/counts_7d/20220907102710_p_ci_templates_implicit_jobs_sast_iac_weekly.yml
@@ -0,0 +1,26 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_sast_iac_weekly
+description: Count of pipelines with implicit SAST jobs using the stable SAST IaC template
+product_section: sec
+product_stage: secure
+product_group: "static_analysis"
+product_category: SAST
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86275
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+options:
+ events:
+ - p_ci_templates_implicit_jobs_sast_iac
diff --git a/config/metrics/counts_7d/20220907202801_p_ci_templates_jobs_dependency_scanning_latest_weekly.yml b/config/metrics/counts_7d/20220907202801_p_ci_templates_jobs_dependency_scanning_latest_weekly.yml
new file mode 100644
index 00000000000..c2a9f72ec8b
--- /dev/null
+++ b/config/metrics/counts_7d/20220907202801_p_ci_templates_jobs_dependency_scanning_latest_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_dependency_scanning_latest_weekly
+description: Weekly counts for Dependency Scanning CI Latest template (Jobs folder)
+product_section: sec
+product_stage: secure
+product_group: composition_analysis
+product_category: dependency_scanning
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97323'
+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_dependency_scanning_latest
diff --git a/config/metrics/counts_7d/20220907211959_p_ci_templates_security_container_scanning_latest_weekly.yml b/config/metrics/counts_7d/20220907211959_p_ci_templates_security_container_scanning_latest_weekly.yml
new file mode 100644
index 00000000000..8e7c6e8cbaf
--- /dev/null
+++ b/config/metrics/counts_7d/20220907211959_p_ci_templates_security_container_scanning_latest_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_security_container_scanning_latest_weekly
+description: Weekly counts for Container Scanning CI Latest template (Security folder)
+product_section: sec
+product_stage: secure
+product_group: composition_analysis
+product_category: container_scanning
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97323'
+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_security_container_scanning_latest
diff --git a/config/metrics/counts_7d/20220907215629_p_ci_templates_jobs_license_scanning_latest_weekly.yml b/config/metrics/counts_7d/20220907215629_p_ci_templates_jobs_license_scanning_latest_weekly.yml
new file mode 100644
index 00000000000..f3cb628da5a
--- /dev/null
+++ b/config/metrics/counts_7d/20220907215629_p_ci_templates_jobs_license_scanning_latest_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_jobs_license_scanning_latest_weekly
+description: Weekly counts for License Scanning CI Latest template (Jobs folder)
+product_section: sec
+product_stage: secure
+product_group: composition_analysis
+product_category: license_scanning
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97323'
+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_license_scanning_latest
diff --git a/config/metrics/counts_7d/20220912161233_p_ci_templates_implicit_jobs_dependency_scanning_latest_weekly.yml b/config/metrics/counts_7d/20220912161233_p_ci_templates_implicit_jobs_dependency_scanning_latest_weekly.yml
new file mode 100644
index 00000000000..243d24bcf50
--- /dev/null
+++ b/config/metrics/counts_7d/20220912161233_p_ci_templates_implicit_jobs_dependency_scanning_latest_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_dependency_scanning_latest_weekly
+description: Weekly counts for implicit Dependency Scanning CI Latest template (Jobs folder)
+product_section: sec
+product_stage: secure
+product_group: composition_analysis
+product_category: dependency_scanning
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97323'
+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_dependency_scanning_latest
diff --git a/config/metrics/counts_7d/20220912162301_p_ci_templates_implicit_jobs_license_scanning_latest_weekly.yml b/config/metrics/counts_7d/20220912162301_p_ci_templates_implicit_jobs_license_scanning_latest_weekly.yml
new file mode 100644
index 00000000000..8fdd6c15c1d
--- /dev/null
+++ b/config/metrics/counts_7d/20220912162301_p_ci_templates_implicit_jobs_license_scanning_latest_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_jobs_license_scanning_latest_weekly
+description: Weekly counts for implicit License Scanning CI Latest template (Jobs folder)
+product_section: sec
+product_stage: secure
+product_group: composition_analysis
+product_category: license_scanning
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97323'
+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_license_scanning_latest
diff --git a/config/metrics/counts_7d/20220912162745_p_ci_templates_implicit_security_container_scanning_latest_weekly.yml b/config/metrics/counts_7d/20220912162745_p_ci_templates_implicit_security_container_scanning_latest_weekly.yml
new file mode 100644
index 00000000000..c3818fb819c
--- /dev/null
+++ b/config/metrics/counts_7d/20220912162745_p_ci_templates_implicit_security_container_scanning_latest_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.ci_templates.p_ci_templates_implicit_security_container_scanning_latest_weekly
+description: Weekly counts for implicit Container Scanning CI Latest template (Security folder)
+product_section: sec
+product_stage: secure
+product_group: composition_analysis
+product_category: container_scanning
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97323'
+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_security_container_scanning_latest
diff --git a/config/metrics/counts_all/20210216175043_merge_request_create.yml b/config/metrics/counts_all/20210216175043_merge_request_create.yml
index 3c2eb359f93..c71cfdcc3c0 100644
--- a/config/metrics/counts_all/20210216175043_merge_request_create.yml
+++ b/config/metrics/counts_all/20210216175043_merge_request_create.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: merge_request
+ event: create
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210216180242_web_ide_commits.yml b/config/metrics/counts_all/20210216180242_web_ide_commits.yml
index 3d1d416a7c2..f86b5bd5f84 100644
--- a/config/metrics/counts_all/20210216180242_web_ide_commits.yml
+++ b/config/metrics/counts_all/20210216180242_web_ide_commits.yml
@@ -10,6 +10,11 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: web_ide
+ event: commits_count
+ include_usage_prefix: false
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210216180244_web_ide_views.yml b/config/metrics/counts_all/20210216180244_web_ide_views.yml
index 7bc32b3dbc9..63149b86e0f 100644
--- a/config/metrics/counts_all/20210216180244_web_ide_views.yml
+++ b/config/metrics/counts_all/20210216180244_web_ide_views.yml
@@ -10,6 +10,11 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: web_ide
+ event: views_count
+ include_usage_prefix: false
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210216180246_web_ide_merge_requests.yml b/config/metrics/counts_all/20210216180246_web_ide_merge_requests.yml
index eb02d98dc85..f620447e615 100644
--- a/config/metrics/counts_all/20210216180246_web_ide_merge_requests.yml
+++ b/config/metrics/counts_all/20210216180246_web_ide_merge_requests.yml
@@ -10,6 +10,11 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: web_ide
+ event: merge_requests_count
+ include_usage_prefix: false
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210216180248_web_ide_previews.yml b/config/metrics/counts_all/20210216180248_web_ide_previews.yml
index 4d581fc7f7e..c785e95e105 100644
--- a/config/metrics/counts_all/20210216180248_web_ide_previews.yml
+++ b/config/metrics/counts_all/20210216180248_web_ide_previews.yml
@@ -10,6 +10,11 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: web_ide
+ event: previews_count
+ include_usage_prefix: false
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210216180250_web_ide_terminals.yml b/config/metrics/counts_all/20210216180250_web_ide_terminals.yml
index e8c1f425639..cd64a877341 100644
--- a/config/metrics/counts_all/20210216180250_web_ide_terminals.yml
+++ b/config/metrics/counts_all/20210216180250_web_ide_terminals.yml
@@ -10,6 +10,11 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: web_ide
+ event: terminals_count
+ include_usage_prefix: false
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210216180252_web_ide_pipelines.yml b/config/metrics/counts_all/20210216180252_web_ide_pipelines.yml
index ae891775bf9..bfd0b69401e 100644
--- a/config/metrics/counts_all/20210216180252_web_ide_pipelines.yml
+++ b/config/metrics/counts_all/20210216180252_web_ide_pipelines.yml
@@ -10,6 +10,11 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: web_ide
+ event: pipelines_count
+ include_usage_prefix: false
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210216180740_design_management_designs_create.yml b/config/metrics/counts_all/20210216180740_design_management_designs_create.yml
index 42f06ef563f..a6f04c56d56 100644
--- a/config/metrics/counts_all/20210216180740_design_management_designs_create.yml
+++ b/config/metrics/counts_all/20210216180740_design_management_designs_create.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: design_management_designs
+ event: create
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210216180741_design_management_designs_update.yml b/config/metrics/counts_all/20210216180741_design_management_designs_update.yml
index a4e526214aa..aedfed669a6 100644
--- a/config/metrics/counts_all/20210216180741_design_management_designs_update.yml
+++ b/config/metrics/counts_all/20210216180741_design_management_designs_update.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: design_management_designs
+ event: update
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210216180743_design_management_designs_delete.yml b/config/metrics/counts_all/20210216180743_design_management_designs_delete.yml
index 4eff2b1b0ff..0fee24ec9e2 100644
--- a/config/metrics/counts_all/20210216180743_design_management_designs_delete.yml
+++ b/config/metrics/counts_all/20210216180743_design_management_designs_delete.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: design_management_designs
+ event: delete
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210216182006_source_code_pushes.yml b/config/metrics/counts_all/20210216182006_source_code_pushes.yml
index 5c3c70f2496..3e4ef3ec76c 100644
--- a/config/metrics/counts_all/20210216182006_source_code_pushes.yml
+++ b/config/metrics/counts_all/20210216182006_source_code_pushes.yml
@@ -12,7 +12,7 @@ time_frame: all
data_source: redis
instrumentation_class: RedisMetric
options:
- counter_class: SourceCodeCounter
+ prefix: source_code
event: pushes
distribution:
- ce
diff --git a/config/metrics/counts_all/20210216182855_package_events_i_package_composer_delete_package.yml b/config/metrics/counts_all/20210216182855_package_events_i_package_composer_delete_package.yml
index 4db8a831afc..2db1ff225d8 100644
--- a/config/metrics/counts_all/20210216182855_package_events_i_package_composer_delete_package.yml
+++ b/config/metrics/counts_all/20210216182855_package_events_i_package_composer_delete_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_composer_delete_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182857_package_events_i_package_composer_pull_package.yml b/config/metrics/counts_all/20210216182857_package_events_i_package_composer_pull_package.yml
index cb1e984786c..81b381e7337 100644
--- a/config/metrics/counts_all/20210216182857_package_events_i_package_composer_pull_package.yml
+++ b/config/metrics/counts_all/20210216182857_package_events_i_package_composer_pull_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_composer_pull_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182859_package_events_i_package_composer_push_package.yml b/config/metrics/counts_all/20210216182859_package_events_i_package_composer_push_package.yml
index 0296f58dfb0..27e2b6ce535 100644
--- a/config/metrics/counts_all/20210216182859_package_events_i_package_composer_push_package.yml
+++ b/config/metrics/counts_all/20210216182859_package_events_i_package_composer_push_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_composer_push_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182901_package_events_i_package_conan_delete_package.yml b/config/metrics/counts_all/20210216182901_package_events_i_package_conan_delete_package.yml
index 7617489c5f4..90f0a59f58b 100644
--- a/config/metrics/counts_all/20210216182901_package_events_i_package_conan_delete_package.yml
+++ b/config/metrics/counts_all/20210216182901_package_events_i_package_conan_delete_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_conan_delete_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182903_package_events_i_package_conan_pull_package.yml b/config/metrics/counts_all/20210216182903_package_events_i_package_conan_pull_package.yml
index 125ef341824..be07d3a67c8 100644
--- a/config/metrics/counts_all/20210216182903_package_events_i_package_conan_pull_package.yml
+++ b/config/metrics/counts_all/20210216182903_package_events_i_package_conan_pull_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_conan_pull_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182905_package_events_i_package_conan_push_package.yml b/config/metrics/counts_all/20210216182905_package_events_i_package_conan_push_package.yml
index 3225658d982..deb3e1b4d0f 100644
--- a/config/metrics/counts_all/20210216182905_package_events_i_package_conan_push_package.yml
+++ b/config/metrics/counts_all/20210216182905_package_events_i_package_conan_push_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_conan_push_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182913_package_events_i_package_debian_delete_package.yml b/config/metrics/counts_all/20210216182913_package_events_i_package_debian_delete_package.yml
index 453f55a5e6a..e95a140aec9 100644
--- a/config/metrics/counts_all/20210216182913_package_events_i_package_debian_delete_package.yml
+++ b/config/metrics/counts_all/20210216182913_package_events_i_package_debian_delete_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_debian_delete_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182915_package_events_i_package_debian_pull_package.yml b/config/metrics/counts_all/20210216182915_package_events_i_package_debian_pull_package.yml
index 2949e819dff..f416d00ab4b 100644
--- a/config/metrics/counts_all/20210216182915_package_events_i_package_debian_pull_package.yml
+++ b/config/metrics/counts_all/20210216182915_package_events_i_package_debian_pull_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_debian_pull_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182919_package_events_i_package_delete_package.yml b/config/metrics/counts_all/20210216182919_package_events_i_package_delete_package.yml
index eeb5198e028..c5e999ae8f6 100644
--- a/config/metrics/counts_all/20210216182919_package_events_i_package_delete_package.yml
+++ b/config/metrics/counts_all/20210216182919_package_events_i_package_delete_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_delete_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182921_package_events_i_package_delete_package_by_deploy_token.yml b/config/metrics/counts_all/20210216182921_package_events_i_package_delete_package_by_deploy_token.yml
index 4d688af3908..7c160f02fee 100644
--- a/config/metrics/counts_all/20210216182921_package_events_i_package_delete_package_by_deploy_token.yml
+++ b/config/metrics/counts_all/20210216182921_package_events_i_package_delete_package_by_deploy_token.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_delete_package_by_deploy_token
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182923_package_events_i_package_delete_package_by_guest.yml b/config/metrics/counts_all/20210216182923_package_events_i_package_delete_package_by_guest.yml
index 9978595cb0a..b631d667ff0 100644
--- a/config/metrics/counts_all/20210216182923_package_events_i_package_delete_package_by_guest.yml
+++ b/config/metrics/counts_all/20210216182923_package_events_i_package_delete_package_by_guest.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_delete_package_by_guest
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182925_package_events_i_package_delete_package_by_user.yml b/config/metrics/counts_all/20210216182925_package_events_i_package_delete_package_by_user.yml
index 1e348c18943..3f62b79e9f1 100644
--- a/config/metrics/counts_all/20210216182925_package_events_i_package_delete_package_by_user.yml
+++ b/config/metrics/counts_all/20210216182925_package_events_i_package_delete_package_by_user.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_delete_package_by_user
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182927_package_events_i_package_generic_delete_package.yml b/config/metrics/counts_all/20210216182927_package_events_i_package_generic_delete_package.yml
index b88a0fe1622..82584ecd920 100644
--- a/config/metrics/counts_all/20210216182927_package_events_i_package_generic_delete_package.yml
+++ b/config/metrics/counts_all/20210216182927_package_events_i_package_generic_delete_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_generic_delete_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182929_package_events_i_package_generic_pull_package.yml b/config/metrics/counts_all/20210216182929_package_events_i_package_generic_pull_package.yml
index b2bcf1baffe..0eca0f41708 100644
--- a/config/metrics/counts_all/20210216182929_package_events_i_package_generic_pull_package.yml
+++ b/config/metrics/counts_all/20210216182929_package_events_i_package_generic_pull_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_generic_pull_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182931_package_events_i_package_generic_push_package.yml b/config/metrics/counts_all/20210216182931_package_events_i_package_generic_push_package.yml
index 54063d11b9f..1e3577820a4 100644
--- a/config/metrics/counts_all/20210216182931_package_events_i_package_generic_push_package.yml
+++ b/config/metrics/counts_all/20210216182931_package_events_i_package_generic_push_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_generic_push_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182933_package_events_i_package_golang_delete_package.yml b/config/metrics/counts_all/20210216182933_package_events_i_package_golang_delete_package.yml
index 0ea0007c525..8c0cfd62490 100644
--- a/config/metrics/counts_all/20210216182933_package_events_i_package_golang_delete_package.yml
+++ b/config/metrics/counts_all/20210216182933_package_events_i_package_golang_delete_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_golang_delete_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182934_package_events_i_package_golang_pull_package.yml b/config/metrics/counts_all/20210216182934_package_events_i_package_golang_pull_package.yml
index 1b68386e3fe..bd8b5ec54b7 100644
--- a/config/metrics/counts_all/20210216182934_package_events_i_package_golang_pull_package.yml
+++ b/config/metrics/counts_all/20210216182934_package_events_i_package_golang_pull_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_golang_pull_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182936_package_events_i_package_golang_push_package.yml b/config/metrics/counts_all/20210216182936_package_events_i_package_golang_push_package.yml
index be3f7974a44..0610ac36fab 100644
--- a/config/metrics/counts_all/20210216182936_package_events_i_package_golang_push_package.yml
+++ b/config/metrics/counts_all/20210216182936_package_events_i_package_golang_push_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_golang_push_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182938_package_events_i_package_maven_delete_package.yml b/config/metrics/counts_all/20210216182938_package_events_i_package_maven_delete_package.yml
index 676748442a5..1dfbdebfdc8 100644
--- a/config/metrics/counts_all/20210216182938_package_events_i_package_maven_delete_package.yml
+++ b/config/metrics/counts_all/20210216182938_package_events_i_package_maven_delete_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_maven_delete_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182940_package_events_i_package_maven_pull_package.yml b/config/metrics/counts_all/20210216182940_package_events_i_package_maven_pull_package.yml
index f035edcc74c..7e8891faa7a 100644
--- a/config/metrics/counts_all/20210216182940_package_events_i_package_maven_pull_package.yml
+++ b/config/metrics/counts_all/20210216182940_package_events_i_package_maven_pull_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_maven_pull_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182942_package_events_i_package_maven_push_package.yml b/config/metrics/counts_all/20210216182942_package_events_i_package_maven_push_package.yml
index 92521cc1fda..7685a116b9f 100644
--- a/config/metrics/counts_all/20210216182942_package_events_i_package_maven_push_package.yml
+++ b/config/metrics/counts_all/20210216182942_package_events_i_package_maven_push_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_maven_push_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182944_package_events_i_package_npm_delete_package.yml b/config/metrics/counts_all/20210216182944_package_events_i_package_npm_delete_package.yml
index 24188860b45..104576a8f32 100644
--- a/config/metrics/counts_all/20210216182944_package_events_i_package_npm_delete_package.yml
+++ b/config/metrics/counts_all/20210216182944_package_events_i_package_npm_delete_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_npm_delete_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182946_package_events_i_package_npm_pull_package.yml b/config/metrics/counts_all/20210216182946_package_events_i_package_npm_pull_package.yml
index 5dc94ab61fb..fe04fafc3c8 100644
--- a/config/metrics/counts_all/20210216182946_package_events_i_package_npm_pull_package.yml
+++ b/config/metrics/counts_all/20210216182946_package_events_i_package_npm_pull_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_npm_pull_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182948_package_events_i_package_npm_push_package.yml b/config/metrics/counts_all/20210216182948_package_events_i_package_npm_push_package.yml
index 0878ec9ade8..5f8ccc277f8 100644
--- a/config/metrics/counts_all/20210216182948_package_events_i_package_npm_push_package.yml
+++ b/config/metrics/counts_all/20210216182948_package_events_i_package_npm_push_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_npm_push_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182950_package_events_i_package_nuget_delete_package.yml b/config/metrics/counts_all/20210216182950_package_events_i_package_nuget_delete_package.yml
index bd45f8fd652..3570e509ce9 100644
--- a/config/metrics/counts_all/20210216182950_package_events_i_package_nuget_delete_package.yml
+++ b/config/metrics/counts_all/20210216182950_package_events_i_package_nuget_delete_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_nuget_delete_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182952_package_events_i_package_nuget_pull_package.yml b/config/metrics/counts_all/20210216182952_package_events_i_package_nuget_pull_package.yml
index e175f9ea64c..2eddb4af420 100644
--- a/config/metrics/counts_all/20210216182952_package_events_i_package_nuget_pull_package.yml
+++ b/config/metrics/counts_all/20210216182952_package_events_i_package_nuget_pull_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_nuget_pull_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182954_package_events_i_package_nuget_push_package.yml b/config/metrics/counts_all/20210216182954_package_events_i_package_nuget_push_package.yml
index 5e3c4d13177..02f45dad580 100644
--- a/config/metrics/counts_all/20210216182954_package_events_i_package_nuget_push_package.yml
+++ b/config/metrics/counts_all/20210216182954_package_events_i_package_nuget_push_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_nuget_push_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182956_package_events_i_package_pull_package.yml b/config/metrics/counts_all/20210216182956_package_events_i_package_pull_package.yml
index b059c11bf40..aa459c6db2c 100644
--- a/config/metrics/counts_all/20210216182956_package_events_i_package_pull_package.yml
+++ b/config/metrics/counts_all/20210216182956_package_events_i_package_pull_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_pull_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216182958_package_events_i_package_pull_package_by_deploy_token.yml b/config/metrics/counts_all/20210216182958_package_events_i_package_pull_package_by_deploy_token.yml
index 532546fd7f3..a527379fe00 100644
--- a/config/metrics/counts_all/20210216182958_package_events_i_package_pull_package_by_deploy_token.yml
+++ b/config/metrics/counts_all/20210216182958_package_events_i_package_pull_package_by_deploy_token.yml
@@ -11,6 +11,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_pull_package_by_deploy_token
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216183000_package_events_i_package_pull_package_by_guest.yml b/config/metrics/counts_all/20210216183000_package_events_i_package_pull_package_by_guest.yml
index d402d29612f..45dcd531484 100644
--- a/config/metrics/counts_all/20210216183000_package_events_i_package_pull_package_by_guest.yml
+++ b/config/metrics/counts_all/20210216183000_package_events_i_package_pull_package_by_guest.yml
@@ -11,6 +11,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_pull_package_by_guest
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216183002_package_events_i_package_pull_package_by_user.yml b/config/metrics/counts_all/20210216183002_package_events_i_package_pull_package_by_user.yml
index 21076556ce4..69983e77d5f 100644
--- a/config/metrics/counts_all/20210216183002_package_events_i_package_pull_package_by_user.yml
+++ b/config/metrics/counts_all/20210216183002_package_events_i_package_pull_package_by_user.yml
@@ -11,6 +11,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_pull_package_by_user
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216183004_package_events_i_package_push_package.yml b/config/metrics/counts_all/20210216183004_package_events_i_package_push_package.yml
index c9f52de010b..22c4969ad7d 100644
--- a/config/metrics/counts_all/20210216183004_package_events_i_package_push_package.yml
+++ b/config/metrics/counts_all/20210216183004_package_events_i_package_push_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_push_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216183005_package_events_i_package_push_package_by_deploy_token.yml b/config/metrics/counts_all/20210216183005_package_events_i_package_push_package_by_deploy_token.yml
index cf0c42dce28..252474155c1 100644
--- a/config/metrics/counts_all/20210216183005_package_events_i_package_push_package_by_deploy_token.yml
+++ b/config/metrics/counts_all/20210216183005_package_events_i_package_push_package_by_deploy_token.yml
@@ -11,6 +11,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_push_package_by_deploy_token
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216183007_package_events_i_package_push_package_by_guest.yml b/config/metrics/counts_all/20210216183007_package_events_i_package_push_package_by_guest.yml
index 92d31d03faa..8fa5f2b877c 100644
--- a/config/metrics/counts_all/20210216183007_package_events_i_package_push_package_by_guest.yml
+++ b/config/metrics/counts_all/20210216183007_package_events_i_package_push_package_by_guest.yml
@@ -11,6 +11,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_push_package_by_guest
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216183009_package_events_i_package_push_package_by_user.yml b/config/metrics/counts_all/20210216183009_package_events_i_package_push_package_by_user.yml
index 1a3b14e8a0b..42b0b0130ad 100644
--- a/config/metrics/counts_all/20210216183009_package_events_i_package_push_package_by_user.yml
+++ b/config/metrics/counts_all/20210216183009_package_events_i_package_push_package_by_user.yml
@@ -11,6 +11,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_push_package_by_user
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216183011_package_events_i_package_pypi_delete_package.yml b/config/metrics/counts_all/20210216183011_package_events_i_package_pypi_delete_package.yml
index 6c0e86090ae..d1c7f71e593 100644
--- a/config/metrics/counts_all/20210216183011_package_events_i_package_pypi_delete_package.yml
+++ b/config/metrics/counts_all/20210216183011_package_events_i_package_pypi_delete_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_pypi_delete_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216183013_package_events_i_package_pypi_pull_package.yml b/config/metrics/counts_all/20210216183013_package_events_i_package_pypi_pull_package.yml
index 2512818a4e1..e47231a30bd 100644
--- a/config/metrics/counts_all/20210216183013_package_events_i_package_pypi_pull_package.yml
+++ b/config/metrics/counts_all/20210216183013_package_events_i_package_pypi_pull_package.yml
@@ -11,6 +11,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_pypi_pull_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210216183015_package_events_i_package_pypi_push_package.yml b/config/metrics/counts_all/20210216183015_package_events_i_package_pypi_push_package.yml
index 4234dd52964..4edd359cc1e 100644
--- a/config/metrics/counts_all/20210216183015_package_events_i_package_pypi_push_package.yml
+++ b/config/metrics/counts_all/20210216183015_package_events_i_package_pypi_push_package.yml
@@ -10,6 +10,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_pypi_push_package
distribution:
- ee
- ce
diff --git a/config/metrics/counts_all/20210303153000_package_events_i_package_rubygems_delete_package.yml b/config/metrics/counts_all/20210303153000_package_events_i_package_rubygems_delete_package.yml
index 15f3003f5d6..2265eff7393 100644
--- a/config/metrics/counts_all/20210303153000_package_events_i_package_rubygems_delete_package.yml
+++ b/config/metrics/counts_all/20210303153000_package_events_i_package_rubygems_delete_package.yml
@@ -12,6 +12,10 @@ milestone: '13.10'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53480
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_rubygems_delete_package
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210303153002_package_events_i_package_rubygems_pull_package.yml b/config/metrics/counts_all/20210303153002_package_events_i_package_rubygems_pull_package.yml
index 5c825c76296..20af7e2a564 100644
--- a/config/metrics/counts_all/20210303153002_package_events_i_package_rubygems_pull_package.yml
+++ b/config/metrics/counts_all/20210303153002_package_events_i_package_rubygems_pull_package.yml
@@ -12,6 +12,10 @@ milestone: '13.10'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53480
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_rubygems_pull_package
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210303153004_package_events_i_package_rubygems_push_package.yml b/config/metrics/counts_all/20210303153004_package_events_i_package_rubygems_push_package.yml
index f8a8fcf8abd..6c7150b3f44 100644
--- a/config/metrics/counts_all/20210303153004_package_events_i_package_rubygems_push_package.yml
+++ b/config/metrics/counts_all/20210303153004_package_events_i_package_rubygems_push_package.yml
@@ -12,6 +12,10 @@ time_frame: all
milestone: '13.10'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53480
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_rubygems_push_package
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210410012200_package_events_i_package_terraform_module_delete_package.yml b/config/metrics/counts_all/20210410012200_package_events_i_package_terraform_module_delete_package.yml
index b914a60050d..2dd4576a0fc 100644
--- a/config/metrics/counts_all/20210410012200_package_events_i_package_terraform_module_delete_package.yml
+++ b/config/metrics/counts_all/20210410012200_package_events_i_package_terraform_module_delete_package.yml
@@ -12,6 +12,10 @@ milestone: '13.11'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55018
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_terraform_module_delete_package
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210410012202_package_events_i_package_terraform_module_pull_package.yml b/config/metrics/counts_all/20210410012202_package_events_i_package_terraform_module_pull_package.yml
index 85258f27878..21b36947f1a 100644
--- a/config/metrics/counts_all/20210410012202_package_events_i_package_terraform_module_pull_package.yml
+++ b/config/metrics/counts_all/20210410012202_package_events_i_package_terraform_module_pull_package.yml
@@ -12,6 +12,10 @@ milestone: '13.11'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55018
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_terraform_module_pull_package
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210410012204_package_events_i_package_terraform_module_push_package.yml b/config/metrics/counts_all/20210410012204_package_events_i_package_terraform_module_push_package.yml
index f8d0643ab3a..bd7a26f022b 100644
--- a/config/metrics/counts_all/20210410012204_package_events_i_package_terraform_module_push_package.yml
+++ b/config/metrics/counts_all/20210410012204_package_events_i_package_terraform_module_push_package.yml
@@ -12,6 +12,10 @@ milestone: '13.11'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55018
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_terraform_module_push_package
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210517073546_package_events_i_package_helm_pull_package.yml b/config/metrics/counts_all/20210517073546_package_events_i_package_helm_pull_package.yml
index 04cc4a17b45..f2bbac01b47 100644
--- a/config/metrics/counts_all/20210517073546_package_events_i_package_helm_pull_package.yml
+++ b/config/metrics/counts_all/20210517073546_package_events_i_package_helm_pull_package.yml
@@ -12,6 +12,10 @@ milestone: "14.0"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61014
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_helm_pull_package
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20210625095025_package_events_i_package_helm_push_package.yml b/config/metrics/counts_all/20210625095025_package_events_i_package_helm_push_package.yml
index c529f1ecb8a..5be5a703ad4 100644
--- a/config/metrics/counts_all/20210625095025_package_events_i_package_helm_push_package.yml
+++ b/config/metrics/counts_all/20210625095025_package_events_i_package_helm_push_package.yml
@@ -11,6 +11,10 @@ milestone: "14.1"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64814
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_helm_push_package
data_category: optional
distribution:
- ce
diff --git a/config/metrics/counts_all/20210709191135_package_events_i_package_nuget_pull_symbol_package.yml b/config/metrics/counts_all/20210709191135_package_events_i_package_nuget_pull_symbol_package.yml
index 164cdd477cb..9924df439b0 100644
--- a/config/metrics/counts_all/20210709191135_package_events_i_package_nuget_pull_symbol_package.yml
+++ b/config/metrics/counts_all/20210709191135_package_events_i_package_nuget_pull_symbol_package.yml
@@ -12,6 +12,10 @@ milestone: "14.1"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64554
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_nuget_pull_symbol_package
data_category: optional
distribution:
- ce
diff --git a/config/metrics/counts_all/20210709191829_package_events_i_package_nuget_push_symbol_package.yml b/config/metrics/counts_all/20210709191829_package_events_i_package_nuget_push_symbol_package.yml
index eb9576c864c..ecff5ad53e0 100644
--- a/config/metrics/counts_all/20210709191829_package_events_i_package_nuget_push_symbol_package.yml
+++ b/config/metrics/counts_all/20210709191829_package_events_i_package_nuget_push_symbol_package.yml
@@ -12,6 +12,10 @@ milestone: "14.1"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64554
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_nuget_push_symbol_package
data_category: optional
distribution:
- ce
diff --git a/config/metrics/counts_all/20210709210941_package_events_i_package_pull_symbol_package.yml b/config/metrics/counts_all/20210709210941_package_events_i_package_pull_symbol_package.yml
index 712eeb644cc..e7f3491aa09 100644
--- a/config/metrics/counts_all/20210709210941_package_events_i_package_pull_symbol_package.yml
+++ b/config/metrics/counts_all/20210709210941_package_events_i_package_pull_symbol_package.yml
@@ -12,6 +12,10 @@ milestone: "14.1"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64554
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_pull_symbol_package
data_category: optional
distribution:
- ce
diff --git a/config/metrics/counts_all/20210709211058_package_events_i_package_pull_symbol_package_by_deploy_token.yml b/config/metrics/counts_all/20210709211058_package_events_i_package_pull_symbol_package_by_deploy_token.yml
index d4ebb18d006..6aaf9bc2978 100644
--- a/config/metrics/counts_all/20210709211058_package_events_i_package_pull_symbol_package_by_deploy_token.yml
+++ b/config/metrics/counts_all/20210709211058_package_events_i_package_pull_symbol_package_by_deploy_token.yml
@@ -12,6 +12,10 @@ milestone: "14.1"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64554
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_pull_symbol_package_by_deploy_token
data_category: optional
distribution:
- ce
diff --git a/config/metrics/counts_all/20210709211248_package_events_i_package_pull_symbol_package_by_guest.yml b/config/metrics/counts_all/20210709211248_package_events_i_package_pull_symbol_package_by_guest.yml
index d84a218e88e..936e245c240 100644
--- a/config/metrics/counts_all/20210709211248_package_events_i_package_pull_symbol_package_by_guest.yml
+++ b/config/metrics/counts_all/20210709211248_package_events_i_package_pull_symbol_package_by_guest.yml
@@ -12,6 +12,10 @@ milestone: "14.1"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64554
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_pull_symbol_package_by_guest
data_category: optional
distribution:
- ce
diff --git a/config/metrics/counts_all/20210709211341_package_events_i_package_pull_symbol_package_by_user.yml b/config/metrics/counts_all/20210709211341_package_events_i_package_pull_symbol_package_by_user.yml
index b91dfa334a5..373c23a4016 100644
--- a/config/metrics/counts_all/20210709211341_package_events_i_package_pull_symbol_package_by_user.yml
+++ b/config/metrics/counts_all/20210709211341_package_events_i_package_pull_symbol_package_by_user.yml
@@ -12,6 +12,10 @@ milestone: "14.1"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64554
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_pull_symbol_package_by_user
data_category: optional
distribution:
- ce
diff --git a/config/metrics/counts_all/20210709211439_package_events_i_package_push_symbol_package.yml b/config/metrics/counts_all/20210709211439_package_events_i_package_push_symbol_package.yml
index a6335011ddd..e1c08134dec 100644
--- a/config/metrics/counts_all/20210709211439_package_events_i_package_push_symbol_package.yml
+++ b/config/metrics/counts_all/20210709211439_package_events_i_package_push_symbol_package.yml
@@ -12,6 +12,10 @@ milestone: "14.1"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64554
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_push_symbol_package
data_category: optional
distribution:
- ce
diff --git a/config/metrics/counts_all/20210709211636_package_events_i_package_push_symbol_package_by_deploy_token.yml b/config/metrics/counts_all/20210709211636_package_events_i_package_push_symbol_package_by_deploy_token.yml
index acc00f50744..2cdfc6f86d0 100644
--- a/config/metrics/counts_all/20210709211636_package_events_i_package_push_symbol_package_by_deploy_token.yml
+++ b/config/metrics/counts_all/20210709211636_package_events_i_package_push_symbol_package_by_deploy_token.yml
@@ -12,6 +12,10 @@ milestone: "14.1"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64554
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_push_symbol_package_by_deploy_token
data_category: optional
distribution:
- ce
diff --git a/config/metrics/counts_all/20210709211731_package_events_i_package_push_symbol_package_by_guest.yml b/config/metrics/counts_all/20210709211731_package_events_i_package_push_symbol_package_by_guest.yml
index 16635a5f6a9..69db39047c7 100644
--- a/config/metrics/counts_all/20210709211731_package_events_i_package_push_symbol_package_by_guest.yml
+++ b/config/metrics/counts_all/20210709211731_package_events_i_package_push_symbol_package_by_guest.yml
@@ -12,6 +12,10 @@ milestone: "14.1"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64554
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_push_symbol_package_by_guest
data_category: optional
distribution:
- ce
diff --git a/config/metrics/counts_all/20210709211831_package_events_i_package_push_symbol_package_by_user.yml b/config/metrics/counts_all/20210709211831_package_events_i_package_push_symbol_package_by_user.yml
index 653814c4bf0..d7fd850a24f 100644
--- a/config/metrics/counts_all/20210709211831_package_events_i_package_push_symbol_package_by_user.yml
+++ b/config/metrics/counts_all/20210709211831_package_events_i_package_push_symbol_package_by_user.yml
@@ -12,6 +12,10 @@ milestone: "14.1"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64554
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: package_events
+ event: i_package_push_symbol_package_by_user
data_category: optional
distribution:
- ce
diff --git a/config/metrics/counts_all/20210723075525_diff_searches.yml b/config/metrics/counts_all/20210723075525_diff_searches.yml
index 0cce12a8659..54deaba0406 100644
--- a/config/metrics/counts_all/20210723075525_diff_searches.yml
+++ b/config/metrics/counts_all/20210723075525_diff_searches.yml
@@ -1,4 +1,5 @@
---
+data_category: optional
key_path: counts.diff_searches
description: Total count of merge request diff searches
product_section: dev
@@ -11,7 +12,10 @@ milestone: '14.2'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66522
time_frame: all
data_source: redis
-data_category: optional
+instrumentation_class: RedisMetric
+options:
+ prefix: diff
+ event: searches
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20220122022215_web_ide_previews_success.yml b/config/metrics/counts_all/20220122022215_web_ide_previews_success.yml
index 203201e9174..e2d617dba16 100644
--- a/config/metrics/counts_all/20220122022215_web_ide_previews_success.yml
+++ b/config/metrics/counts_all/20220122022215_web_ide_previews_success.yml
@@ -10,6 +10,11 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: web_ide
+ event: previews_success_count
+ include_usage_prefix: false
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20220314362302_service_usage_data_download_payload.yml b/config/metrics/counts_all/20220314362302_service_usage_data_download_payload.yml
index 1b26202c5ef..5a330eea78b 100644
--- a/config/metrics/counts_all/20220314362302_service_usage_data_download_payload.yml
+++ b/config/metrics/counts_all/20220314362302_service_usage_data_download_payload.yml
@@ -11,6 +11,10 @@ value_type: number
status: active
time_frame: all
data_source: redis
+instrumentation_class: RedisMetric
+options:
+ prefix: service_usage_data
+ event: download_payload_click
distribution:
- ce
- ee
diff --git a/config/metrics/counts_all/20220825115210_i_merge_request_widget_license_compliance_count_view.yml b/config/metrics/counts_all/20220825115210_i_merge_request_widget_license_compliance_count_view.yml
new file mode 100644
index 00000000000..3542f4fd8ac
--- /dev/null
+++ b/config/metrics/counts_all/20220825115210_i_merge_request_widget_license_compliance_count_view.yml
@@ -0,0 +1,24 @@
+---
+key_path: counts.i_code_review_merge_request_widget_license_compliance_count_view
+description: Total number of times the License Compliance 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.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96538"
+time_frame: all
+data_source: redis
+data_category: optional
+options:
+ events:
+ - i_code_review_merge_request_widget_license_compliance_count_view
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20220825115217_i_merge_request_widget_license_compliance_count_full_report_clicked.yml b/config/metrics/counts_all/20220825115217_i_merge_request_widget_license_compliance_count_full_report_clicked.yml
new file mode 100644
index 00000000000..580d0d4dff3
--- /dev/null
+++ b/config/metrics/counts_all/20220825115217_i_merge_request_widget_license_compliance_count_full_report_clicked.yml
@@ -0,0 +1,24 @@
+---
+key_path: counts.i_code_review_merge_request_widget_license_compliance_count_full_report_clicked
+description: Total number of times the License Compliance 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.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96538"
+time_frame: all
+data_source: redis
+data_category: optional
+options:
+ events:
+ - i_code_review_merge_request_widget_license_compliance_count_full_report_clicked
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20220825115224_i_merge_request_widget_license_compliance_count_expand.yml b/config/metrics/counts_all/20220825115224_i_merge_request_widget_license_compliance_count_expand.yml
new file mode 100644
index 00000000000..1829e1c87c0
--- /dev/null
+++ b/config/metrics/counts_all/20220825115224_i_merge_request_widget_license_compliance_count_expand.yml
@@ -0,0 +1,24 @@
+---
+key_path: counts.i_code_review_merge_request_widget_license_compliance_count_expand
+description: Total number of times the License Compliance 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.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96538"
+time_frame: all
+data_source: redis
+data_category: optional
+options:
+ events:
+ - i_code_review_merge_request_widget_license_compliance_count_expand
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20220825115230_i_merge_request_widget_license_compliance_count_expand_success.yml b/config/metrics/counts_all/20220825115230_i_merge_request_widget_license_compliance_count_expand_success.yml
new file mode 100644
index 00000000000..4a8f35a7c5b
--- /dev/null
+++ b/config/metrics/counts_all/20220825115230_i_merge_request_widget_license_compliance_count_expand_success.yml
@@ -0,0 +1,24 @@
+---
+key_path: counts.i_code_review_merge_request_widget_license_compliance_count_expand_success
+description: Total number of times the License Compliance 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.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96538"
+time_frame: all
+data_source: redis
+data_category: optional
+options:
+ events:
+ - i_code_review_merge_request_widget_license_compliance_count_expand_success
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20220825115236_i_merge_request_widget_license_compliance_count_expand_warning.yml b/config/metrics/counts_all/20220825115236_i_merge_request_widget_license_compliance_count_expand_warning.yml
new file mode 100644
index 00000000000..1ebc20fca26
--- /dev/null
+++ b/config/metrics/counts_all/20220825115236_i_merge_request_widget_license_compliance_count_expand_warning.yml
@@ -0,0 +1,24 @@
+---
+key_path: counts.i_code_review_merge_request_widget_license_compliance_count_expand_warning
+description: Total number of times the License Compliance 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.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96538"
+time_frame: all
+data_source: redis
+data_category: optional
+options:
+ events:
+ - i_code_review_merge_request_widget_license_compliance_count_expand_warning
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20220825115242_i_merge_request_widget_license_compliance_count_expand_failed.yml b/config/metrics/counts_all/20220825115242_i_merge_request_widget_license_compliance_count_expand_failed.yml
new file mode 100644
index 00000000000..7fe8263a53e
--- /dev/null
+++ b/config/metrics/counts_all/20220825115242_i_merge_request_widget_license_compliance_count_expand_failed.yml
@@ -0,0 +1,24 @@
+---
+key_path: counts.i_code_review_merge_request_widget_license_compliance_count_expand_failed
+description: Total number of times the License Compliance 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.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96538"
+time_frame: all
+data_source: redis
+data_category: optional
+options:
+ events:
+ - i_code_review_merge_request_widget_license_compliance_count_expand_failed
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20220825232556_count_user_auth.yml b/config/metrics/counts_all/20220825232556_count_user_auth.yml
new file mode 100644
index 00000000000..623e0dbb7a4
--- /dev/null
+++ b/config/metrics/counts_all/20220825232556_count_user_auth.yml
@@ -0,0 +1,23 @@
+---
+key_path: usage_activity_by_stage.manage.count_user_auth
+description: Number of unique user logins
+product_section: dev
+product_stage: manage
+product_group: authentication_and_authorization
+product_category: system_access
+value_type: number
+status: active
+milestone: "15.4"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96321"
+time_frame: all
+data_source: database
+instrumentation_class: CountUserAuthMetric
+data_category: optional
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/no_todos_messages.yml b/config/no_todos_messages.yml
deleted file mode 100644
index d2076f235fd..00000000000
--- a/config/no_todos_messages.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-# When the todo list on the user's dashboard becomes empty, a random message
-# from the list below will be shown.
-#
-# If you come up with a fun one, please feel free to contribute it to GitLab!
-# https://about.gitlab.com/contributing/
----
-- Good job! Looks like you don't have anything left on your To-Do List
-- Isn't an empty To-Do List beautiful?
-- Give yourself a pat on the back!
-- Nothing left to do. High five!
-- Henceforth, you shall be known as "To-Do Destroyer"
diff --git a/config/object_store_settings.rb b/config/object_store_settings.rb
index 3280bc284ad..e55032d3987 100644
--- a/config/object_store_settings.rb
+++ b/config/object_store_settings.rb
@@ -16,10 +16,6 @@ class ObjectStoreSettings
# we don't need to raise an error in that case
ALLOWED_INCOMPLETE_TYPES = %w(pages).freeze
- # A fallback switch in case anyone gets a trouble with background upload removal
- # Epic: https://gitlab.com/groups/gitlab-com/gl-infra/-/epics/734
- LEGACY_BACKGROUND_UPLOADS_ENV = "GITLAB_LEGACY_BACKGROUND_UPLOADS"
-
attr_accessor :settings
# Legacy parser
@@ -30,13 +26,8 @@ class ObjectStoreSettings
object_store['remote_directory']
)
- if support_legacy_background_upload?(object_store_type)
- object_store['direct_upload'] = false
- object_store['background_upload'] = true
- else
- object_store['direct_upload'] = true
- object_store['background_upload'] = false
- end
+ 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'] ||= {}
@@ -46,10 +37,6 @@ class ObjectStoreSettings
object_store
end
- def self.support_legacy_background_upload?(object_store_type)
- ENV[LEGACY_BACKGROUND_UPLOADS_ENV].to_s.split(',').map(&:strip).include?(object_store_type)
- end
-
def self.split_bucket_prefix(bucket)
return [nil, nil] unless bucket.present?
diff --git a/config/plugins/graphql_known_operations_plugin.js b/config/plugins/graphql_known_operations_plugin.js
index 164b34c1dd1..c340849e084 100644
--- a/config/plugins/graphql_known_operations_plugin.js
+++ b/config/plugins/graphql_known_operations_plugin.js
@@ -1,9 +1,10 @@
/* eslint-disable no-underscore-dangle */
const yaml = require('js-yaml');
+const { evaluateModuleFromSource } = require('../helpers/evaluate_module_from_source');
+
const PLUGIN_NAME = 'GraphqlKnownOperationsPlugin';
const GRAPHQL_PATH_REGEX = /(query|mutation)\.graphql$/;
-const OPERATION_NAME_SOURCE_REGEX = /^\s*module\.exports.*oneQuery.*"(\w+)"/gm;
/**
* Returns whether a given webpack module is a "graphql" module
@@ -26,9 +27,19 @@ const getOperationNames = (module) => {
return [];
}
- const matches = originalSource.source().toString().matchAll(OPERATION_NAME_SOURCE_REGEX);
+ const { exports: moduleExports } = evaluateModuleFromSource(originalSource.source().toString(), {
+ // what: stub require(...) when evaluating the graphql module
+ // why: require(...) is used to fetch fragments. We only need operation metadata, so it's fine to stub these out.
+ require: () => ({ definitions: [] }),
+ });
+
+ const names = moduleExports.definitions
+ .filter((x) => ['query', 'mutation'].includes(x.operation))
+ .map((x) => x.name?.value)
+ // why: It's possible for operations to not have a name. That violates our eslint rule, but either way, let's ignore those here.
+ .filter(Boolean);
- return Array.from(matches).map((match) => match[1]);
+ return names;
};
const createFileContents = (knownOperations) => {
@@ -60,7 +71,7 @@ const onSucceedModule = ({ module, knownOperations }) => {
return;
}
- getOperationNames(module).forEach((x) => knownOperations.add(x));
+ getOperationNames(module).forEach((name) => knownOperations.add(name));
};
const onCompilerEmit = ({ compilation, knownOperations, filename }) => {
diff --git a/config/routes.rb b/config/routes.rb
index ddc7b77460c..704405bbcbd 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -70,8 +70,11 @@ InitializerConnections.with_disabled_database_connections do
Gitlab.ee do
resource :company, only: [:new, :create], controller: 'company'
- resources :groups, only: [:new, :create]
- resources :projects, only: [:new, :create]
+
+ # legacy - to be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/371996
+ get 'groups/new', to: redirect('users/sign_up/groups_projects/new')
+ get 'projects/new', to: redirect('users/sign_up/groups_projects/new')
+
resources :groups_projects, only: [:new, :create] do
collection do
post :import
diff --git a/config/routes/group.rb b/config/routes/group.rb
index 2a5931207b0..4a47b349665 100644
--- a/config/routes/group.rb
+++ b/config/routes/group.rb
@@ -121,8 +121,10 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
resource :dependency_proxy, only: [:show, :update]
resources :email_campaigns, only: :index
+ resources :observability, only: :index
+
namespace :harbor do
- resources :repositories, only: [:index] do
+ resources :repositories, only: [:index, :show], constraints: { id: %r{[a-zA-Z./:0-9_\-]+} } do
resources :artifacts, only: [:index] do
resources :tags, only: [:index]
end
diff --git a/config/routes/help.rb b/config/routes/help.rb
index 2a0aba8b632..54ad3d43081 100644
--- a/config/routes/help.rb
+++ b/config/routes/help.rb
@@ -3,4 +3,5 @@
get 'help' => 'help#index'
get 'help/shortcuts' => 'help#shortcuts'
get 'help/instance_configuration' => 'help#instance_configuration'
+get 'help/drawers/*markdown_file' => 'help#drawers'
get 'help/*path' => 'help#show', as: :help_page
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 53d9be13611..79ca13e3d8c 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -159,6 +159,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resource :packages_and_registries, only: [:show] do
get '/cleanup_image_tags', to: 'packages_and_registries#cleanup_tags'
end
+ resource :merge_requests, only: [:show, :update]
end
resources :usage_quotas, only: [:index]
@@ -312,7 +313,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get '/deployments/cloud_run', to: 'deployments#cloud_run'
get '/deployments/cloud_storage', to: 'deployments#cloud_storage'
- get '/databases', to: 'databases#index'
+ resources :databases, only: [:index, :create, :new], path_names: { new: 'new/:product' }
end
resources :environments, except: [:destroy] do
@@ -466,7 +467,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
namespace :harbor do
- resources :repositories, only: [:index, :show] do
+ resources :repositories, only: [:index, :show], constraints: { id: %r{[a-zA-Z./:0-9_\-]+} } do
resources :artifacts, only: [:index] do
resources :tags, only: [:index]
end
diff --git a/config/routes/uploads.rb b/config/routes/uploads.rb
index ba2e8493ef9..7b598e84975 100644
--- a/config/routes/uploads.rb
+++ b/config/routes/uploads.rb
@@ -22,13 +22,6 @@ scope path: :uploads do
constraints: { model: /appearance/, mounted_as: /logo|header_logo|favicon/, filename: /.+/ },
as: 'appearance_upload'
- # Project markdown uploads
- # DEPRECATED: Remove this in GitLab 13.0 because this is redundant to show_namespace_project_uploads
- # https://gitlab.com/gitlab-org/gitlab/issues/196396
- get ":namespace_id/:project_id/:secret/:filename",
- to: redirect("%{namespace_id}/%{project_id}/uploads/%{secret}/%{filename}"),
- constraints: { namespace_id: /[a-zA-Z.0-9_\-]+/, project_id: /[a-zA-Z.0-9_\-]+/, filename: %r{[^/]+} }, format: false, defaults: { format: nil }
-
# create uploads for models, snippets (notes) available for now
post ':model',
to: 'uploads#create',
diff --git a/config/settings.rb b/config/settings.rb
index 51d54817646..b242e970cc6 100644
--- a/config/settings.rb
+++ b/config/settings.rb
@@ -204,5 +204,11 @@ class Settings < Settingslogic
"#{minute} #{hour} * * #{day_of_week}"
end
+
+ # Route all jobs to 'default' queue. This setting is meant for self-managed instances use to keep things simple.
+ # See https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1491
+ def build_sidekiq_routing_rules(rules)
+ rules.nil? || rules&.empty? ? [['*', 'default']] : rules
+ end
end
end
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index 61e27f9aeb8..4527efe5a1c 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -199,8 +199,12 @@
- 2
- - gitlab_subscriptions_notify_seats_exceeded
- 1
+- - gitlab_subscriptions_trials_apply_trial
+ - 1
- - google_cloud_create_cloudsql_instance
- 1
+- - google_cloud_fetch_google_ip_list
+ - 1
- - group_destroy
- 1
- - group_export
@@ -221,6 +225,8 @@
- 1
- - groups_update_statistics
- 1
+- - groups_update_two_factor_requirement_for_members
+ - 1
- - hashed_storage
- 1
- - import_issues_csv
@@ -247,6 +253,8 @@
- 1
- - integrations_irker
- 1
+- - integrations_slack_event
+ - 1
- - invalid_gpg_signature_update
- 2
- - issuable_export_csv
@@ -255,6 +263,8 @@
- 1
- - issuables_clear_groups_issue_counter
- 1
+- - issues_close
+ - 2
- - issues_placement
- 2
- - issues_rebalancing
@@ -289,6 +299,8 @@
- 1
- - merge_requests_execute_approval_hooks
- 1
+- - merge_requests_fetch_suggested_reviewers
+ - 1
- - merge_requests_handle_assignees_change
- 1
- - merge_requests_resolve_todos
@@ -333,6 +345,8 @@
- 1
- - object_storage
- 1
+- - onboarding_create_learn_gitlab
+ - 1
- - package_cleanup
- 1
- - package_repositories
diff --git a/config/weak_password_digests.yml b/config/weak_password_digests.yml
new file mode 100644
index 00000000000..0ae8e4d8f1b
--- /dev/null
+++ b/config/weak_password_digests.yml
@@ -0,0 +1,4550 @@
+# This list defines a set of weak passwords which users cannot choose
+# when they set or change their password.
+#
+# Weak password sets are derived from dictionary words and previous
+# data breaches. These sets can contain words and phrases that
+# some may find offensive. For this reason the plaintext password is
+# obfuscated and stored as a SHA256 base64 digest.
+#
+# Administrators can configure a minimum password length of 8 or longer.
+# This list includes the weakest 500 passwords per password length from
+# 8-15 with a final bucket of 500 weak passwords 16 characters or longer
+# for a total of 4500 weak passwords.
+#
+# To refresh this list:
+# ```ruby
+# pwds = Array.new(9) { Array.new }
+# HTTParty.get("https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Common-Credentials/10-million-password-list-top-1000000.txt").split("\n").each do |weak_password|
+# next unless weak_password.length >= ApplicationSetting::DEFAULT_MINIMUM_PASSWORD_LENGTH
+# len = [weak_password.length-8, 8].min
+# next if pwds[len].include?(weak_password.downcase)
+# if pwds[len].length < 500
+# pwds[len] << weak_password.downcase
+# end
+# end
+# digests = pwds.flatten.collect { |pwd| Digest::SHA256.base64digest(pwd) }
+# File.write(path, digests.to_yaml)
+# ```
+#
+# MIT License
+#
+# Copyright (c) 2018 Daniel Miessler
+#
+# 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.
+---
+- XohImNooBHFR0OVvjcYpJ3NgPQ1qq73WKhHvch0VQtg=
+- 73l8gRjwLftklgfdXT+MdiMEjJwGPVMsyVxe16iYpk8=
+- oB7a2RwAq+e+W3K142v0zjxvJui84zQOujZWQoE6uLY=
+- Y4Lerx9dxueSt220pKe/K6RoiE4ACyXnko5iHif7I8s=
+- c80bFsT7gwYa0YoLKblkOmjUZAB1pGbcnlFoL4SoR/U=
+- BZoAGSWS1URLwMqtcgP5i1BjMuLPers11oTqm/fBjwg=
+- IDtwta6IOTIWG70L3tk1fnY+Y6/OmLFiML4z8LlMLMU=
+- nOjbkiqPSnq9hZre5wvYt6YzISZUh9pUz0vtamnrPhs=
+- qUGkxP0MAc3e9huL6WO/TB4rCBHAN84/GDX93272wiM=
+- 5K2TygesuNkIo6pB6SDqT0708m5/hs+CkcXbKJeApa4=
+- dPygMltf2zo0uttAolgc+9U0QYfo00MpUqWrwJKcEkY=
+- qpcwIVD86BFCXNhFNwKKWvvjfj8TYq1FpR1Gfhev3Jw=
+- AWIRSDBvyPt8K5XutcN+N1+Q21PPgxPqh8nDTAW34OU=
+- 7nmXbJOA1eM3/BwJXs6MjyL5HzBs7rFh+lH+zt4sS6E=
+- BOd7+Plcs+GjalnR6ThXxBGTDbZGtGwhigNS5DICPPI=
+- CcWH+ygsNCPwhn6xDDem/U46FXhl7797zP1RYh6ZQME=
+- 71EwYhTZpjYe4dW0UubSu3DcfruFv54Cw9R0f7V9a+w=
+- YV7X+xUEsMckopbXpp5sey+eosV8HYIGxa/fOS69/SU=
+- Ow/g00Lp+halxo27oz8uY8Ak9yqdTBzhAoVwEB1SKf8=
+- xUIsBSv717vZdk4EZ2iLYhk/7E+jKhsTryjRcI1YcOw=
+- hXOPj5p/GwS1MpxZDry55CWSXG0JhAicQ6Ai3k8ZwoE=
+- 0vEp3TBtHXpD/P4EfvDbsoJ19e1l9GNqKOLK43uB0JU=
+- quCgn/XJuSRiH4T+z/WYmEavkrImJLv8pQ6tuU1+Wus=
+- kX67M5ay/y4nt14/5CGx7cB7mY90NQRy86vFxmIKaNs=
+- TU8mNpFxmU86Rndu4tiElPuZVYAKW7YmHAFsS7nzC1Y=
+- CHdRg7Ds+di355EVIWeemvsLKHUIotEW8sx8d/vEUIg=
+- E6XCAuMg0L+bssbix884Cm995dOSUJ/uJguAnIk/8vk=
+- Na8l9aX9rGQB3UuvlJeUtZ24AWjWnIDn8s/YOZni/v8=
+- 8LE5JCtJfIpawzvxdq2J1XF2oERwjt5CYTmDzLl3mMM=
+- qxy3EvLcp1YQUWCAVQH01thlfZPUCxbu5Oy1/QSNJus=
+- cquZT6LrQmwFHvWcrWF3UL/gbXz2MRKF/3nBnDKv0jY=
+- xG2H8ftKXfbfhBAw/+MA6Cca90ti8lRcbBdZ0YkyZ10=
+- s9TNZBy7gSO34mmFTp/M3jo3IuiRodHpClF2izAA6v4=
+- JBP7NwmwWTnwTPLpL30Il/wllvmtC4qeqFXHv+uq6JI=
+- 4k35IAeMPdTn6NJELwDlyasqIxuzkY1lzFCQbknsrvQ=
+- "+THDCPxbYLQhwJlpkSg53/J3aVfZi40vkcVU7Y/ID3g="
+- OhINwVibsvDLAjso7HUyi+P8UzPvBwcoWzH0etJo39M=
+- YoYSuAPGR6fecPAwbGu8SJ2Ldeb/7xULcKFVOlp6u+s=
+- opRF/Zw8SDDcDJBSMOgNl2A7zwwSDOrov53QanFynYU=
+- 5dUD1FgiKcTJq+JQ5JZUdlyIXrSbtJ338YYbt5/HNG4=
+- 6c7nGrky/ehjM40Ivk3p3+OeoEm9r7NCzmWexUULaa4=
+- Yx/6ULWaAFF0Te4AoLkZTUSzV8yR/HWVYoqGfBM/IQI=
+- B6CjvReD3mMBb1UNocDkfIIl/qSw+gQP4FivYMLYWMc=
+- LUeVtaSwDrONGh25qQ/9jIui5Ae05R9CccHkghBksEs=
+- FxjCSxCuuAmeP8RJYKtpSat2omc1JFnyA+oQNr7DgsI=
+- cBAzriZBGQjzERTSkZD2DN2b8+cg4wP3k4gq9XY7aI8=
+- t/xuquG8fQcq8j/Q3bkbzbIcklIjGn0KapOASIPXGYs=
+- oXREVQ4sEnsC6hwZe8/6QiwhcTBA9T1cLKeSVBm8z38=
+- zhSE9QRB+ZOyEd43Kmlo48QkYEyYWCn5bzBQMjQ335k=
+- prcKilB0/BLtcNYdBf/6MKW+wSDtFAzwCbimMCMoNgs=
+- zQjExDFt8g2cMEUP53bc3kgQAp5kHN5SbFu//sH3cKM=
+- O4DTj3aGqLX45hrVYuwGmsFycy+02rlGQB8hpDhmmks=
+- TPDBASJ29GrzHkTS+7A6569W8DyZluuUUrmbPmJzaY4=
+- pwpZs5ga84W+ErKTIi8nhI+7sDh9BVBLAsfyAS0C5n8=
+- vNFkK8oWPaPJ0pA6vzZjp5/jKV3TGo5bEnBca9YmEds=
+- jumTjkuWClBUDxypKZ+sxaXzQtCEi0AsMi/RRZLkvDI=
+- go/rt2Ca/V5yjshD3y0R8LjNIWfUryrwLrNU+FA4UMI=
+- IUSlb3cobQy1ZIUBontauhv2YrlVQ1y9dWd5tci4PY8=
+- W+CIi74gh/li/uV0jZz1LjfkxqJK95Z1/34coKGxJzk=
+- jw4vduIrQ+KFUYmHfn3B4efZjCJsldskfNHVR5KDNKk=
+- MKmJr8gsCiETlXNZHeTl/zeZT30VBqms8rWZcAXCZJ8=
+- tzhG3VNZJ6yzn/yoXEX0EZfQOAsUd+NEigQbm5SiIrE=
+- UJTcrWGsqEra52CjpO9mvQAoShejkoYTLDtkHduth98=
+- EtRxON4MBh5w3QnMKSL2QrHgnnD5VuuNcjNA9Ha+CB8=
+- JpLUG6nymsncEklcPK/6R7cdXS+MX9iZyISm3meSLiw=
+- h0X4ZkCtLta4ov1CiEXfPaoc/Nup73TAz6pXys7DT1s=
+- T58QswTP6bKxH8sTh/aU4Y8I6jWMfp9WdDTTrWy9f8Q=
+- 6WBadACWcqM8XjOlomgIy30g+VGe/KJVUdt/q+V0AC4=
+- AYzD6LTDvlzJHwXrt0GuzHh+A660WkH8n3yBFN/19UI=
+- R+G6ZpowkVzat5J/tTnSG3UukTHOp0KCQrVy4gGMPUY=
+- rBOotTTfSijQ+7u2TP+Qm1sej63bTl6le7imQs6Zj2Y=
+- Q/95LXn3WJDTxRgXOXg4ib+e9Kk5fMjS/A6lQ85aMPc=
+- FCcr02HJmQYoyrTs8X8oEgsqQGVVYJkrGMekpcM3JBc=
+- N3l5EPR3/rmUMxIaq6rAa/dx6vnrpexTefqOaTs9MRc=
+- ejRbpeGJVYMfsfVDRDt4usWoI+641XR+j8ssVZGzExM=
+- cwVR9brUrwYE9mHn+Lguamw2TKGc4UAWYzPYbNyBTKY=
+- rR0KM9FHXinFd74j4p/fGeHfNmO3x+13JyvPMonxWV4=
+- UDDFvQAt6HE/712uvVl2IPXovOoxxgPczfzfUCpXzGA=
+- 0XEIdH6SJhP+Zr9bDsRQgB9RO5LpHXg48Lc5359oupg=
+- wKSUIUPocs0a4p/HWeBFJt4ukJrBcyc004VQopwuJRY=
+- iHmt3rGMaynouJlg8zECJHkvvgjpVaq8OVa9TynxvHI=
+- togj7e0LycfzMX1gGsJPasVjiVzujlor0spHWQb+JhU=
+- MSQzwoNJ9jxPOHlT/zNwRueUvqD5uev8sI6QBG3tnHY=
+- "+xK7blgCJXhzowHM3+1xQRVDpiiM1FPVpVJIIelwJy8="
+- "+V/J0lUxTe6+s+O+jHqSbKpOJBXGNyvvqo/W8Gm3xKM="
+- BqHYv3/CEN4j2VQKs1dErp2zEK+gr7q+lSkzPhdahQ8=
+- BU47MIcINw6gKdwuvRZGxJjVnXIDyeGkTPBITfmOWBo=
+- HEZzOclgoQernuX/Y7OACXl0Fslk+YiyJHEHc8w4eWo=
+- YnCAV/qMtFIC23ryM2IVaTTLwII3BX/PrI1MrWEPXI4=
+- 4KMuXPcJo2dUjKY67Cerh0o6a4yhXaMexJV6ErsGZlU=
+- i1kcwxPAdiF4DbqJ1OaYVmz/gsysJmdIOsL8zXNE4zA=
+- ZItKH9swBUbjWUUZW1FmZb+eRbaeM0cS418+oM+M150=
+- 3g3oKsajOAMATNxtdKipnNN5peBc2eiqFkxFbRsbYVg=
+- LmLI1gLOUcN91w891kpc7+l1ioiKfcHO6yf4KR22fnQ=
+- oIEaR7HimcgVKyRVy7U7aXYUiFPuNe9/b0Jyd5Jblrw=
+- "/S72r0GqVZ27SLaLixRwiFDvqhoaLwYa1tpndgFiiJc="
+- 44gHXaT6TpXam1YWYUX5aF0eSwjvx71eST2qL2cbScw=
+- WPh+Jl2X3hQQdtLncEWfUfILMhEhOaD33Ce314A0Zds=
+- gNcRQPdROUxHEr9G1rZav6AYSjVVPcLLG0FUb74q+ho=
+- Tcqw2CzLUD/qD296TWNECYHPJ1XZ+6VXM0iejICR/fU=
+- BcX27yG7Ar49ttBmxOgi4H73Wy+FJBMzp8ul3sdOLTM=
+- eeqTWBgYhGNxY/vWHAayIVD0KWNr3e2HvccAZ9sjlqQ=
+- tzlVMOTtVctqU3buhIH2A9GGec5MU7h7EUap7cEfzh4=
+- 8I9Eil56ncNhm7fBKfan1fxq8ALOoXrXHf3Bxo9NTg4=
+- 2TwJCiztHgBJMxPa9cF1Fx+wm2/1S22ahKapCXCcJic=
+- 0UpzEL31W4/+t7hGLR8eO8bHmn2AqQ5w6uTvNvIjhH8=
+- 75TUkrtI/TGT07r2uG9ZUPgEAlTy3acIj/QlcFhLAsM=
+- nacBdjdNmQSIXZrA8ml3Cmh0js/f9W27rnr3aNYU27Q=
+- 2S4GsyXSRCPdGHoBAZJBB6zos2c3mxLCuGCN34Q89DU=
+- 2G3G5gsGMuMPKnir6SLCOOkT3Vx092323JNfHsc6XfY=
+- HzzkBBWiCB+j7udfw5//jlbCInDRqXinJJtZLc69ILQ=
+- wdNcw0cf5QkgPWWz56U/yCM3rJrix5e4NmIfcG/jffg=
+- XS0863q+VSNEJ21H02qBdbeuslCpvwvwDoUM0j7PLkM=
+- 1N8uwlPclbNFW3exWGMCArFeXlCca9NBWSswGcYvv5A=
+- 4RGh732qhZckDtoK1IK6ma0JtPhMCTvegAY6I//yz94=
+- "/abPL5IPTh6SVvcBmq1t6+Ie8TNzFPqPDWo+uPSu9JQ="
+- lAnWiCb9uBlFt//EyIIeINphRkvitzi5EMg0vQbro5s=
+- gtMEK5QTeLyZS4wllvpp5DcL0stUAMTAQ8mW3Bl/2nw=
+- 5SY65+mMC24qCJAX7w0xTUxhP5N3pgJb1zyFLIwlv+A=
+- zqbghgVJb8+HGUb37o1fJr/jLu7I/f1hTRUtG9PmORw=
+- mNnVmpB1GcgPwI6boORQXxUsG7jb6UU44RrRn1zNv+A=
+- nG1AW7otskv70i/H/3Szm9nF6cbOZimcZRm+UX5u18Y=
+- TiBhPsOmNzL5hjC5Qp4WANR6CPBZSW05/9NT8MMDjzk=
+- 7yJmqlcwxxrEljbPq8h8twWiNx3Z06qoKVI1JE1VdN8=
+- y8RxymAJK75sYx8CvFc5my2j0a5JGlWhqpYKSy4Kx+g=
+- eQR5H5u0lqM1S/dWQeRf6zHogaAM68slCORb2UKm5DA=
+- AcAndtcpDpmcYK+EE5J98dOJaQqrjKwSUDBmz2LomfY=
+- 4+k7YL1yLO0loEH2Wv1OOW4rr+V+DD3gyLawqosFRQY=
+- 15mHBRolUqwYleBrl9EtjP5ZJIcvYdulnBVVK8yDL54=
+- 5pfFETzli3CLfB8x8EI8wCHQvzqs09Y4ZcU6X4F84ng=
+- uHDT44JwiNl4+8JgY5VUjfgKsAJ/v7+AajALe0+bvgE=
+- gU4q4DmDSNmof/kcA3Lct2GGui0lBLEjXI1TUL9S7do=
+- z9r7BFLVawPrYUbrkVKJVDFwnJY2O63dTiikc0J1DAo=
+- xzw6B7ZEsCYlGqZVMx7uA5S+pOe7HV8Bm/vL7rE+D4M=
+- 3VoTeIyAzxAD4jP4/91UFwUSF0LpIq7i0iFLqHe8NVE=
+- GWxWLLXnjoyxDGZGaGjKi0CtewKdjs74ULcXzr4FXrU=
+- fgcf2bAj7Y8YRYpzYToINPYiC9XMUDV7o0k8YECp6ow=
+- Lr2jl1ZykeZL6gV11UcV6kuozsJga/iy2pXfWN9nyo4=
+- 5JA0z0vJ6YXHCt9DRhdnkITjNUH3JsFWGCXtundV+4c=
+- RirsLKiIMkJINBaycyayJYua5NEWrvDFXDixgg0HNV4=
+- hUm5TNap0uW8eag5R3UzTQzwAw8amfYQ3Atvx0XaCTE=
+- uC5yO0YTIzdMHuwhHgZtx6HCEw/jbJBuS+xcxFEJwkg=
+- KPYVUVBRdnav3vXhJXQLBUym6ONlX8/E0pTs0kBlQ5I=
+- DES+n3lIlX2wc6HyTCZrB1CLEn7AMLMoJprQUtchOqc=
+- HbAEa4sZXuf0DjeWNIa69u13T4A+MgSdppVu6jq/Uyw=
+- ERSNj2rU1PJLGj6RsR9p2SWivQNAm7Tw9IbxPTEa6og=
+- sYa6H1M2bsJnRgsVQ5lrGwW5EtlRw2v2+2yg8UWSNVU=
+- 84pQdueThhLEpbe2Ada4rBeKMl0fC9A8fTYFdW4mzE0=
+- J8xplPwcAc5mWca93Km2nExqlBgGXmEsadEQs/exH4o=
+- qGGJNxM84aPD3Cj5zX4ef7Pe5NV8zHCMchoZUkZfXX8=
+- UUat4vPPenUXwWHf5HquBb70TKPLY7oxpwEmj0kokBg=
+- 5138ReaWV2ur3rztUzOBQstBOZTxgCR1KrHf4kZ2dRs=
+- sPYKBGni6iT9/EvbykaOVIGLxTh2R/l7Ks8Kbkssys8=
+- CkmS6kQrU+PcqGHerAmo1JhwBKhIMHmxKGEIDqSqG1I=
+- v7MBsmylWQxM10G+o3w21bTl+5LcSIDoyJRIv4KyuUw=
+- lJnmrv+y9ZmMzUj9VQoqdCVFdRh9cuX6DocH7aoRXeI=
+- hSmraJkBUmNXPfaD5Xqd4mnWDTj5pUkxUuH1PoxcJzA=
+- 9yN5Kc3ileoIEEuhZNuGwxTCbq77s/UBi5gyXf0U5zo=
+- BXugPWxEEEhj3HNh/kV4ll0YhzYPkKCJWILlimJI/IY=
+- "+huuuOb1wo8mmX9jzAi/CP8mMqWKV4uLlx3STVx9eGM="
+- Dh1ZMhp+sOO7lYQkV0fMc6nQPNnqLCTDmgcZEt+tszo=
+- vxXWzh1ef5zO38erJGjfTkwY21c0Rkk7GyTz0yU0uzQ=
+- Kgl4OxxHQpTRIrgrk7AcNwG8nk1ls65g2iEaQSEVKkM=
+- 0kJZvhNAfg0TIze9g5jumq/0OiScONC/IiQp8xHJyTk=
+- 5t6xCmVYZQrr9MTZX8R8cW37Lac1AYPBFAQcGa6b/NM=
+- EndTSM74Cesbe8tk8E8GhnIR7z6mtIpG46ap46nRtc0=
+- RLPFAD2TxfFiO/U+Jtq+QlPoq0pCMR43wzfon4+JEnQ=
+- J1G2b9Xeok2k7z8DptnT2xu1TAX5FlDdoFDGpRzukBk=
+- khF84R6YKUoiJwS9uqtoBU5H3lZ0CvBYvavX0hNax48=
+- yA9jAUxRcQbUac+7AgPxB3/qfYcrMEr/DQ3odYAsszo=
+- i9EGmCKdJmJ+sDmsIPRTe2LGaHzLKJJZC8ejaRZZ6JI=
+- PwjY+ttLZ/sFZiNWXtu8LHiAkdeP0ky8Rz/OMEPONHM=
+- D0iGg0JhSouA12Drke5Rrox3f4FAuvQKjEBgwq0s79Y=
+- M6fT2kdqMqwjez9gOhvmL60AKZ4NS1qNuNkTEE7exik=
+- RnQwkJH6TOBzohvuDE2ZRQXgoX4TYAugQ1pUAgijemw=
+- D/n5SPgMMSfDR3zsQobNuY6f/2opycPRKTRZh2q0V2c=
+- liMUt+8vcABzgMW2cA2u3GNDrGk46CQaRHE+chmTwKw=
+- AtMvKKiBtpZuf8/Vi2smGgzF9Q/aDkP2N8tvCHvfPtg=
+- "+L/3FWt51OprjXuFwxXHSp1/1uneuCnIKFhpNADLf0g="
+- jRZJhFOwPUQg2QNi1x5sNrsVZK7qU/NvIGdG58taUXU=
+- EdQEF5WWMdPSQg6M2HCYk8Ec16Tbc3r2Po1Wz6eGb4U=
+- ijiamrRHJgSCCrAW738K++trQk/B7ReUeaRWSHOaufM=
+- 9AX5XC9BAvmcK0GIY4lDyYuqNYy9/d+NuO6h482dqj8=
+- xsIwesAlq/7WgMtka8OMo8PW4CZioPL6oUPc/yImikk=
+- 39De4z8IL/a2DXbOBsRUSHFs/IwWNb8bC/2TkZDo3FE=
+- Xsdy86K1oYo8sRDc3d/NA+IDmYam6jX0DhiGDZa/wFA=
+- V/rIVy7qtfxZnHYMZZ9Yfnavml+jW1YHI3ct9fMwBL8=
+- eT2heuwm1T8I493P84wY4IAypjckaKSmcEo7qGpPwVU=
+- fc9Af6hKDgUZx5kRVMQUjeAkTXWJAgwNmELbnvrYIJQ=
+- QBywo8lXemrt4Tbm1dTJuHuVXvPJgBD/kTkKzQbUsqo=
+- 1QztQ1NZwfK6rfPJFJyin83WEOY5zWCCxSwtM7tKt0g=
+- MNDgNeSsoRSAhe0WN2nTiWmigfTQuU2nwx8vVkFrE7w=
+- NTednACeKQ1wK3Kr8/X5vbqn4Apj/De1F0/DglyAleM=
+- f1vP+g3oLpXKbbefuGnMZn498mmdbU7/gEAXt/VMN2w=
+- eqsGzZC276v7xYGSKkrS3fuiogoL5BIOtdDl5ypZ32Y=
+- yMeByWymKNFXkXHJTf3zpOFXWRyHvSPayGMwsgbYIwQ=
+- eCa5WLeccGJoAbiAQF61ERVX2tzrL+4rHtaaGO7Qxtw=
+- BX98sb4E5hShTf1YzgAB9G0qOh061oJ5kegGtZxkP0w=
+- "+nZvuEJ9H4/rywfz9fDq/36v5yxJmx98mJxZpxd66ZQ="
+- XpD31T+djMZiWtjxarwDHP8J13bHT/aNxpB61oD6/Uk=
+- SgekMQA0ECZoqGLy7H07p0FpN7L4XJCzglfPWxMJOww=
+- 4bYVryasnuk83dzqo8B9VBIw3Imkmu13KWewkA0HdGE=
+- 2BvFmn9I75jGSMaOYwnbNPD1F5eL9tln5qN7oxxT/80=
+- D0fdh83o+I4m3BkzJm0378MDqE3ARF3y//PK/nsToJE=
+- ckbSsrnOucu/Wwx2EvMli5eFb2vbV6edk1MI/L8IJw0=
+- HSVrXSC92L5X7R84gdfwa8ktoAk/A+704uYrQpXL5P4=
+- nFbMUbN0w7oYkhDVttS/V3kNNRyWxHwCGQ7PHkMGNas=
+- lUx6H4QJaZjm3xclRJdrD4NX2dVFwPX/oNfKg57oo4M=
+- BS2Z6o8xo3QbZyz3Bj1A7UwZNYGBrliq0LKarX88Syo=
+- HEQ8q7nboaSX0UzxEgM8VVxXWvd1q7hzhC5dvDFDS10=
+- tghuYTeo07XfQ5qEKihAhQcTYy/vGZ0K7bzDo26eDMw=
+- FX/FUSGrLFmFLDXXisqY2EAEfl2WaMQj5NOxRuFXeQE=
+- sNNRixq/tZGHgA3rVp3s3a5DT+heODoqCAIyWwSZTO0=
+- HPGKJDwlpWqZPIIH0RYanC3l80uVLTgnBLlNxeiIsQg=
+- KIejLgJlDUhYVHnQfYNxfZ3dulc3ELKtyPVG8+Vl8iw=
+- 0pmSf96i1afplKXI1P877oXFx3C+Gk9/o7m4G8IWvZA=
+- xYfe+b2vsyCeKNqdkPHwWwRWx6I7F9oWCHaWrQWe80E=
+- wOIaj/hRU96sgv5/CcDaGzvZCsCuIE541xSHU7Q2PAM=
+- McyWUPPdG8p/3NH0CkzRp396gvDTM74TL+w1AuydFRU=
+- LoRK1lHGuaacvo+Ie2p03TufxImq53dwHm4tDVI6DPs=
+- xcLlTenSgzqJYwbb4rq/DCoNcJHCXvuqCzE4rOykoFg=
+- NyaDNd1pMQRb3N+SYj/4GaZCRLU9DnRtQ4eXNJ1NpXg=
+- iatTCsREAAEWIaODBsYeUMb30GeTLzQbnk0A4PgnvWM=
+- vdZdzZ8jFJj6idrXmw5yY1NrkBJb1/KkkAxffiNVKhA=
+- kQN6GOw/hMfAceM49K1Qnh3S48K6mrobOS6BjpGrEy0=
+- AJ26flQEi1aivTclzfhSSCQsrBBF/9UBH8mbA+Gumjw=
+- lKLQCZYjPjk+VHo61J+oLv7qwSEKj98R3eNaI7czOsA=
+- cQw5BsqLVPhsngINmJeS0DuaDYkE17V7P1fnSrF0ZiU=
+- d2SId1UAc3ueEHLxs45QoclWHCpsz10+PKTsV1qGQZI=
+- QoVC5g2wkdj8UisDakKvjF5MvW3zpkO8HejxSMZfHhY=
+- K0pZ/sPnvMeOQIuQ+PZsbtQzc7V05CE825mYgXq8q2g=
+- HAnl15A1buyB0ZKGUpjhDP0TzJ8hkuA0BkJNEzJaLcE=
+- 1eNIGYQcTBHJ6DgUaw8Zs++KScTu/4ZDlrwtLwBLc6Y=
+- QLG7tURfwCGjEjFTefRjMoSFHhTR24P7BzD1iHLWAzs=
+- BCLULGibfoBG26LXBC6FX20h4ERkurFo/SY/WxD4k+0=
+- Ihs3/NtS0PfDm70L4hHbDhwAyl++zVeIeARjAmxrlks=
+- m/UBxXIW24AGmg3gC9iKgVgNSKZdttXHXlMQG0SdDR8=
+- 9MFKEEBbH9L3esd1GCf7xxIhH2Q6TlV/oUnzzbZLLD8=
+- 1Y2kHg9VFVdMMEXVogR/iLEny53hzLveK/V/I/mH1pw=
+- fyd0si8s4dAQD9VSYY3O3WWIrU6gFmWdnF4R2LcXKNs=
+- O0ddcmSkCz7BOYuovXmvvU9yrHA0TlDWDOzG5Irydlc=
+- hsErpnN9CHPDg0RfAdtMbGkVee/AEQ3UU3vDS39ePm0=
+- bC2QjUcm5wdFemj4YyuzXOqmC8wMCfTahnFcjg9+Glk=
+- GMy6GG2HV8IMvwXXqYssZPnxbrZOpKZGWbvFybezp/4=
+- YJYOZcd6oR2u1dYsYuNLUyaXX00p8Qk0hMxaPM8byuw=
+- Faz9x1/biIUUhyOM2EQsXsyODDGGjOn1Kk4jYbqJny8=
+- z5wcuJWEv4xBdqN8LJVKjcVgd9O6Ze5EAR5iq3xjzi0=
+- w0n4z7uIS0c10FTuFK1tWrI7DS+vpwgtDxz5v9q2c0I=
+- dFJvFsmE35sFeUmxm888fNEFBFPV0Qaac251P91N9Ec=
+- gJk/BUvy/dNwjaxYAFAS1RHPM19WB6PFC3F9+TyxdPY=
+- rJ/OSaxiY606tkkbzHeo9khbDhrIKJwN26LORQ5A10g=
+- pXYsvisJwBcAomukpjNcAwiOhxXWzTVPu5M8p/DYRpg=
+- Rkl+uhMMLxtiaULV5FJXvbBC2IcbtHha6TtRVenAoTc=
+- o1d7gDohajnBfZRUHWBUu+cv0R2ogy57KNkSd6JYgEg=
+- Gi0+PiJrjHLcy5IMWy9O9/zXxKWKnibETX0MqPZc3TI=
+- uGpwjrl4n9vU27qzhI2GWR9dpw9CDvYOObQFx327OT8=
+- 2b4ngOdoqNyGI4YiK3OaueS2iL49bbqnHGGJ6yfkJrc=
+- BvodNGAFWp1J65DAHkjWFtbJ8bGsgBElPw1Lp+HDDTM=
+- OgIlBjvqETJxtYV1hPduondZ3BTNObRHIFZAS80KJo4=
+- dPHD4qNm0qoIqU9+XW7b8yT1G3H+Cv9l7CwGzpdWioA=
+- m9ZCzfTNR1fwJ3CdZt8HeeNf4P5bqjnzACFAPKENSp8=
+- 01okfgdJGSDdfAz5o9Vxn2tTiRYCldmhY9/XsXHBzoY=
+- WbJhgjI1B6Xh4Gu8TkN3JbytADueTgWTzisQecVRmqM=
+- waHkqhA3uuFYbY0f0BtHLh0jaITp4ioEp5Mupqvq8I4=
+- zxqoId2rr6ahYhLro4BfvP+SxvyYHUOWfh/Khlf4Vxw=
+- 7uXLZgGADP/9G9f/YaBqLlImcQLD0YeJKguG7NQ0fr4=
+- r7afaFgtEeYFjlpP0V1uvIpnbtDOezLZjw1Q+FVAPa4=
+- twWaeskHzVll6/JU7CFtHRy1jrAgUWLUx+vQYsyJOls=
+- utYidNNFg6PGhl7tjo+9NH64mOmReD0suGIeDmXJMNw=
+- lWuvAkCSj1rHV2SBv7JJJuh4Sy6FujN+FWmmI4yRXhY=
+- a/vXCdX9wGGKtepxkT1VJGSx+tQ7L8NM8l6W89KpBNw=
+- "/MOiP8cjLMicfLDyPYd0/vtz19wqsi5qG2uLICtNzJE="
+- xtTE4yS8jptTLr9Mrq5aGza0OJoETNtZrKPo7KZIPz0=
+- saJNJyvWcIeaxHxujXOj9ZWUIs20iDZfSbRVp7V4ugE=
+- 1MNjAl+5W4hWO3Ksn5kUul4Etm1uazlZG5D//dl6X3U=
+- Xy/SsefvsA/D++MVrSi2laZ9KPPRzsAYLCgCS5U5Jzs=
+- S/vffr7iV/8GWLBNto/so3x7aO8/QZtDfv70PT9dhLY=
+- bSBYBuGdulMGoJ325bcgVzfzAb+vFQhuICAPNW65zO8=
+- invidvj6YgOQpuXpSYI/zwFWul3AQnd+a22Va60cdQg=
+- DD0S/ZOeepI4UxAPaLyfGEStr4RciAkcqhVzNsx82HQ=
+- 7LBwXINDuo3KV8IxfcI7quXwpvym6ja5aqaY7W241RU=
+- "+ojeAkNk2/zCeM4y/nlWrHwtBYU19+yejgF7jjt3+AU="
+- 5kf/5hM2FvCOniTLyu31DbqeFcFoUP0sDLeb1IcKpso=
+- 8hrf0ZmDcI04VZI5qP2ID7n5G4ZNB/56gLRHBopdCWA=
+- Wl7fM2pjXK/vqR98KB5Kg1coMTCMnkLy3+DVzmj7lZ4=
+- 1qOJFnjgW/hv7v+utIfwDqOUxlnK6fsVYRxMpQodOMU=
+- oOl/aF4qTm36Iy3xzU/fwvRgn0L1GXmK7DGFdAm18Ps=
+- Wcmuh8drSFEVnAlCVNuuvzLoB2krKdYl4Lk1H9Rr43s=
+- "/ymOTE+XXKolEU1SQWcCfdRydfS/fkFPzoe5k60S0Fg="
+- bMHv3wndNQF65j9ThYPC1L6O0VaamdLAvOiuavP9Wzk=
+- ZjazGBHuC79ZPRFSbOtlUmf+ltjapKFkkpZkQmlSw0g=
+- pOzvqfYFcbR+adVuIrKm+qvNKbdG42T062VanH+SeiQ=
+- 8aWDs/W+tNKbl41RwVPOfUeKvU9gQFNleVuAPP0/V1w=
+- KCdLOzNaIAFMDPvrk9YNdsee/gV6iRY4zq5yCbELBZg=
+- 2i2o5xyCsYquwOnc+BerCUgai1UGEGbwEbPjgYh4j2U=
+- WVSZ+IuwM9Dg2j4gtul1a3RYq2wixA+8OURk7PlhDyY=
+- "+c3gvqyCaOQw6xiXQiXYjbZJXEuzHB0/bddAe99clEM="
+- kHZUibS4KJNhJ4zwo07mP7gLidlQCSfuHCtD73i8JC0=
+- oLgUPFZQsVzKHN21bfRPetsi9/oxdBNOWqXvG25zZtM=
+- ThgSPg8GY1s8q3piDs9Lvv84Wi80zs2AjdIy+0ew5lU=
+- s0Fh3+ne1c4KtoCvV4FSRgflxI/teoumdjFBM13A2C4=
+- UiznBX/QUjrc1mctsku2cdCdH/ovHnyXwT5saK5vyxM=
+- IY03PCh1+siFrtNc1g0A/r/wu/YdKKeZw56BViOmGPw=
+- iwDvO6kjVKJ8TzVUSdFQUZGkDdgVU6w+B3+rH1tJAeQ=
+- ez91hIAIDVai0JIDiXukfLt7/c9MVWz/mcPDU3nQv6Y=
+- knTUnCe3WDpgoGQguCAHfjjGrXhfbfVLZn1CqppbMlw=
+- SgozmwxtBVOJd1KoQRWtyBx1gS4XQ+slGSWOUAD3Des=
+- eTfIkV+mtTBAe8ePPqfi3opvBrysAgUTvxQfPupmG0Q=
+- TkxW5KFfifBcL0xyYT2ioYyWZdTw1qzOFkFesG+b53Y=
+- 7gh0Fwt/bzK4wqyVc8Qo01tXUnCma3V8LAGF0r0JcY0=
+- GQaxsUwZa9aTUPlfSZJ46RoG9XmU5QFcv4YGeZ2gK18=
+- MTweyio5QcI9vNXzgE3e0Ax+kJdG1W4GWtdtCCACy1M=
+- GMMT43q8Dz5jMCq0BRMDA+Y3mpYcKw2uD/alcNSDPZM=
+- S2PDRecG0q37CtPldLO9sjB9Xp55sSIFO2CMnchkYvo=
+- BzB3hANf3Fyy4APLPZBaR4LQNIWLakaZvybJgEo3jK4=
+- B2qJwjF5zt/GEXH+QA7PAft2uaSKaPuC3QzWiNaE2QA=
+- GMxPCmYkESLIaBniiTWiddnL7Vxyq7i8jl22tlvONvk=
+- PJ/az2IFR6Djqq/G0ZM4xNQWsVa6MEl0ftJFFb8cHbs=
+- AASLpdc3s8cRfUzEZXtMd5WSTgLKSsdJrVtpmmJKalA=
+- i8QDQ7GHGupprm4rDB9BRePZo0ime880czP7Wmx7Am8=
+- GSwzyqw9ie1kf23FRBkWHCu/S1fRK7jFRuQdZEhZdXE=
+- RatblZGl9JPMviiZg9zVE9PxrQHfOubGbVcb6eCql/A=
+- 5Wa+Iolr4VH2ZKehWU0utH94XKLagmI+sh3GEYJDElQ=
+- 4rxRIkDkua6zxpXz2fKThfuHt1n6MUFCHV0pjD/R0bc=
+- WMlyL9P5PDAJeWOgFXwdcIi9S6Pn8vHeiNXqr+t9NVI=
+- libHREcXqreju91Qm8r6NadJHpR41CGzjlOaYh9pXt0=
+- wSnbi+iQS0CsIcnPXZ9cDiTvRV0denu/1wSfxtydJCk=
+- aCa1+y4/ZFNHwuUqN5+Tc3pgF4ku7feCWkyRcY5o25g=
+- "+QLe9IVcpl7dSZgxZxhqfZ6eozhgeO9ZzpKXDSkbK44="
+- 94mSyv02GheSmsFO64Elvm2yVNTEy0+5wun4SQNvaF8=
+- U1nuwTdj8dXEnuOWc5lE5bxMoRitaBoHYvfaeegl7dY=
+- "+dLBBq8o775fgyKCLe+qZQ6ArH4zWI81pfgLzJc4/5U="
+- PApXm70a10TVvHktEgoqpJ3YiEs/19QjoDx3SMeFQ4g=
+- VW+ctJiTImTdpKGuGz3jcuATQxZsVQHvYZrE8GjlZ9M=
+- s8S0B1CpchLomB5KxJTR7HcFPx6vTgk0wna3T8T4fEg=
+- D/NSr9256h5dnhB1aUxPWRQGuj7IdjTUqn81WX4cEGk=
+- 6uXX1Bj6QY1Cpm5F64sLeFgOQ6Ub9ZPrWlVezS/79FA=
+- g93OQoeXobkoD+u/q8I5lxw2rr588xevnXhfcE0nwYg=
+- zYQepcKHbj8ttru6dgtI1o8YBDA5Vr52tNcwFVv9FmU=
+- "+Vm+D+Yo6siKR+vcGEX9F0eOBiJUR+56xjatuokIsMg="
+- Mw/FEo30rgakhTJopxx4gPKKrOKCgGvjt9vTsko5ayo=
+- HhA/3rFszD5BMmmlk4IKE1PC9c4ergQoSq/QwTOpMu8=
+- yD4lvWqgtPXJ4xBcnBax0L6DfzGgaSRrSBHD6OBZohM=
+- DAjA0iOvf0PL81Q7SjVZzQzAs3iTw4ovyDGeIE6AwsI=
+- 6ubXfibVP4tQquIsDDFQgpPkq+UFbm20bRPWgemID0I=
+- OpvowwiqAz28xFRJ8VvyisJSPrDEmlrOpOUE2L6Ebhw=
+- zjnMeTczFFh2vF7mrySIHVuo3BTKvEiVyp/0xM1vNFQ=
+- DJV+nwmiREqIaHN79CsW9A1ONTO1fcMaYRJMLNG64xs=
+- "/m00aM9cdNjsKpW0Dy4FM4w3pCAvj61pLStkqc+bRoo="
+- lgo4rOdaL+lCaqXUi1NsxtsYogI7m91pjlYvwwI1haY=
+- bhADZIA73vFRUIZybsLMcfUrZVZIcY+vKECUmkrMXxs=
+- UnopsfaI+MAfVzAYkDxzCIuMp6yD4kNlfQuw2upMjNo=
+- wmZeo+ElaMN7dA/6IXUbGXqr8KFkp8pWKQa9fAlN3jQ=
+- QLuznm5obfL6RJpzQo2ecEwi1ujlv1aMxtXy85LzgSY=
+- KGfzBmYfKH6201zkAyL80Tma4zdBoLUWg3pDpJ+ySJU=
+- uRE2KGtQxb5JvT/b0AZIqYre1iOJTr2d69qpGthEylw=
+- Dtvf+sG3Fpw9nScC3CgKoweWlB0sVtnPj0ns2i+P9iM=
+- r/YwG8MzXGyVLZ3LhypfMbAg4B2huQS4VYRjYuAMQ8c=
+- HjCxSoABxzk7zSax1uCTpVtSfFftvEZ2zo8wd6d3328=
+- rZWNCjG4XO40RcKDEFYZynWhI84MyAnoyHF8cNawpGs=
+- nkVvJoB8k+xaSV79gsGTit2qEVqzf2JNavXnzSEuzjo=
+- rIKQkHrf/jZbE+aY8FlA7/6OrOuIiRHuX/KHmTJeciw=
+- hHWJ4T0jdiY9ZXEOTyMCl89XX2Muci7I4cVOhukUc4E=
+- T7XKO5gPe8PsenE0XE4CWqVcYE6PrKQHYUymSOijUzE=
+- jdWeQSlOU+4NcV6pt8Hte2qO77COZ02xqElvtf0PCm0=
+- R+8gIHSJt3X6TNysPDlLUXqyLXRgI3rj3xrA6JY2mdY=
+- geWx5sMODpIRio64oAXUqAARYtshfZpQU5NDTNF4sCQ=
+- XvLDlNW2PkF1zTMcdMhFPD42649H9tZIOX/2wTFP1wU=
+- dZNp++jj4sWN+L8MIQnFIQqdJ9pMKf0oktxtrHIjxTg=
+- vt6msdoP2BzyPKcfHFpaly9nlndrFYEwOd/tAlJoFbE=
+- V+6JLp8MaEDHbZ2WvQDEL3Qa9/O6/8RWnU6zlrp+bSs=
+- sCYg2SXIupAQm0OWvsn+LhCKD9tmLHFI3Y45cku+Ce4=
+- r7R+AFMRU+k4CFieQ9AsEfY5jFvId/eSTOvKghHI3Rg=
+- Rejz2UfPdM6poE5xcsdoqZs8Q4YrSLwvTa00jGOjpAk=
+- J6luOLDRPJGlgwXzaXke3DzK24i5eb/9sBcT6I41d1c=
+- moacH/Q5elsdkaIPO3Dv0y1qUs5667u4vbY6Nu/x89Q=
+- l6IvoEi6A5x7C6V0thyvRUbu4isASbLfDwsms85+cO4=
+- NEl/4A64ZbEbysf4dlFDyRTTw+tq4ZzSoWunoQB5xaA=
+- QC7tEU8KWD+3K852GWU5yaJWiMyIQMf6RNVPgRrF6jI=
+- rHqJwoIZSqb9Ge4JFjJeLlp8ywY1BWoty8sIS8H3H9g=
+- CgXXsnzHorHKcErcvR1uOrLBns4ABYbwO87qvyRUfkM=
+- CvZTGRCapFLdM1kKRwEFznsZRPP9zSCcef1ClCs798E=
+- AlCvgpTSisiDOKYfQFEFOMkdEmzO7fdJWWAm8n7aQ4o=
+- ispPNndPgqZ8UHy5yWZ5SC4sx2fy04UCJpVXpWawkvs=
+- ZSf/0Fd9RZt/TrGiNIF65W7Z3LAUtCCRx4kiAAsqtNY=
+- wgx2nbnh0RGIQcn0U4ANA9rS2YVOK6WJtOD7bA9C+IY=
+- fvRS9TjNI6aRqN1rJKyGvqGJZb9rLKcHPDiq68hNRWE=
+- SjSEs78vyw/19xfxiQSr0p0ii8EwmBFN9osu2aRiZQM=
+- HjsAkS5mh1u9AnJmVXlDYJq/lXWHyO4gJXi5UH733tA=
+- mPwHNntt8+fNBD7jyzfD5LaclEFuW0VJTp06TxweO7s=
+- tsSsQSrIgiNVI53XF8EcpbBzc+TbVQ0EI8G2rs7vhJM=
+- KT3g5qxP/BQcxHNhnzW1UUcWqu51nv0Cft/SQSwH/FE=
+- h/6l4YwyY7T+yPpsKeBn9gY8c3Jo+u1GR6Od/sX76Ak=
+- c1cB8oXLklP8/5ZJoPegnyfltZZwMN7YurpMVzFoNjY=
+- k9sXCHMvh7xFobfUqNV1LJnzVC+mafqKZ7l8LTUqCCA=
+- XFFqjKkJxyjpjGqSFTYzTgQhpgtD+H8lmoPFbH+NRlw=
+- nwY4/WD+7bwaXc7YI0BA1wfAhL113dLvuRVNvCIIlLs=
+- "/nyk6gqHFSIr9fTWxtG7li3cEYnwcsbH4tIGiLEIPoE="
+- YeJL5uqEDTcGQ8S7g5+9dz/LeS27139+fCaBK0MfYm4=
+- 3ebol0tGoe3c1+o7u4mTQvSMrYlrRydab4BgYuxcoUw=
+- FKQFNM5FvPSgclVNQtF1j7Qs+VThUWk90jgXAdk04SY=
+- W5AHGuomGluDgDXq2qUGasNHps/xtSgFPFf4WMZQiic=
+- 6I6WFQwc7ZEs+aUU6uOB/fFzJwxDk071xTyeKmkXXpI=
+- zTb1jB6e519ydxP6keCAm4Y9/G3yBI48Ogkxc79Ct2U=
+- HyVPqgTP+g0KjHX4UU8Ah0KcN8w1FvertpxmYNsuZAc=
+- ISP2zam46LfY/dQvq6peI8gszgJwIB+nSsQI3VKH3mo=
+- 7AIgBD4I7h+8oYr4K9NXqhYeNH/dfY6cAzSWyImb2gw=
+- NQQ0utBop6S+hVvDYtekrW5jHn9fDZFqKgRSBwBm0xc=
+- l+/tvBZquz36+xxAqYcwQ3pZVVtwYi1q7kQlaJAi4Ec=
+- 3qfNJ5MZpD33WbewdDPnWwU/i9wvdZ4yioMx0Hys1Ao=
+- Sfqq3kk76La2Fk7mf35NEBgSpd2pcNbKaT3ai4z4Lks=
+- jZsuZERQGgIkydgujRGydXeIDBetdQ081yxeOgc9etM=
+- GyTgwX6VLkx8rsW/qz2dgsQZQ9uzDCP9MjS0d8JFFgI=
+- 8c0pmlPFFnC5Jv+3gO4TgLlFEFspIHFzIXlpXi6192s=
+- rDFgsKkzrAPX+yabr4RD5lk2qkMiiB4wxgRD192hUtU=
+- kfBgqa1LOesijZU3KS+rGdJS+u0GwWnQ/ctKkFYPVnY=
+- dLaMgTgReytYNkyOsSWlVQffPABu+KIsInNChByNsoo=
+- BVSl3wLuEvGuNqUcqu80ox3rlFiki2KdpVSisyJGb0o=
+- DkgeJmfDri8XosgQfGeyKywpayxVAHDIQFDcZEqSyuU=
+- "//xWOwACEwqVAvnons6uhVgpHkEA3ZXN9ehsN9IhSuI="
+- zuxnSnbGOeM0yEk+kdQkQ8BpaQvsnPzdSct9bw93JQA=
+- XyiUS2MqJtbEqwzXsIXAkVxR5ZJbAJ0Lb1WLlqWKMaE=
+- nubA+svo0G452of/Q786TOUQr/IBVRjoQZT832uuF88=
+- KIawt0i37dt+X48eB/GZlwYRVs0DMgzIsmV2lX40bxk=
+- KDElNr8GvJhQy9yMGAPH65McxPp4jLfL97rYKAHT72c=
+- 5fqURTqSUBflFLLZGsi4ATCZBZwyJOBygXJxBKk5CNs=
+- rhpeQdfge+fvySdaPMAlol4cqlyH4r5DTcFnKcFaEiQ=
+- cQSC6qKA5rfp7ZiVgZdobqUncjcdI3QInmd/936uGOs=
+- gB0LPu9eyAiEf7I16sr+dJyYvmKt4WbevMxHEkrwpKs=
+- 3AuDAmI0o/WgdFYoWv8HCBrYMtWm6mCI+8H/oIQySt8=
+- DIjMhqc6p8QFj/fA4x1vEFwMo1ddWmjgYOldnISmB6k=
+- PnlDSO01EsRXrMAD6bUcIOZOqsvecOhrrrI3HzUn5yw=
+- q5XJ/TZ27CdByXExcrhyLoypXNW5lADD4TnswS7+2J0=
+- OluJkOYP+nThVGHXVH4PYqqU0Z3k495oe9MRdvi20Xs=
+- mh4FwP4NC3IceXOyl5zRBS+l4GddfGvgumeXkD7ZcmA=
+- n8Jszm4osxE+55AC5jaH9iV47ICF+A0tTW8uTtxh0Uo=
+- HBNSYC6ZXkQOG9IA1x88R9kP68wY/jIH+1fMOSDGR+w=
+- ZvYtGAfTgho4ZfJXO2nHS+Az8TQSQKyGH+/G1DC/9eA=
+- QARk6embsD8M2XMoktJUesZQc8XTJmfB1eZjmJW2vWo=
+- yRvZwojAp8XKG7gHy9XYnFqzN2SGLrRoCEF3M9oq+HM=
+- qhBZbDxtYPGEd+CAls8Evf0c5fx0p+eIws4Koad9dV0=
+- zLZA3colwfQ9P1YeUuZwfGo5B3yuEYVzZ7KIhzvlfQY=
+- PhKXrLifdZsfc2XHdJwezLMmTuw0QrTUwX9eJIgDp+8=
+- i9OuLfq0s9nRz8m2DK7720zYMowqnqAmMLXRv+XJb4k=
+- 7310iYwipL8L5DfTCYe8lFMQ3N8d20imvOCF83azAq4=
+- 6oQfu7CfhmmtvvhR58CCV803rjDEtdpI+6sRjPTQeIw=
+- ALj54imo7j5D6Zn1gBE+pUWNlVA1LrO5NmeCFxttXqU=
+- sN0mgygAIGLSp29XRsquBZBlIEgryp2fALPnQKD4O0c=
+- vvm8X7dyw5+zNXaOhbsYhHATCmX+HukkY4JxJ7TQZw8=
+- bug9C4qX539KYXHRkmgiECdD6jBdiWVST2JoR1CDNJ0=
+- hlK2uOGx+RKAyU/AI7zztQ6MKxflFVmc4gI337EeMl8=
+- TRGBiuzmaBdSZK9mjJVmQI4TBiYGQsfho2USxGxlMQ8=
+- XleeAVgRUkjnGQ6MPwG0adKP+DkdVC0Xn0w+q8LKQgA=
+- KBZRRTZNjcW6O+MKNM/7o2bKpZbSXraAq+mqwQkDZjE=
+- EYBBKvQ9eGBKc/FNWn74PT06dCUc6Pt7IyBDpRH78WE=
+- 51Om08Dp0itGKyRmX8m17je1RFTiGZQ8c0F76RvAdGU=
+- 2wRPGbwBNxhShjRczx8GfS2c4cjvs4NJskeWloUNtp8=
+- d4zHAmP0cn3opLGgtE8QrM165FaQmUeeLLJwKhhuK9M=
+- agraJ2Bsd0lb2PINuFxzuLEynw8Oq/xzOSQkDOjgdOc=
+- V4HsqA7hZqD/jB/BEvYbKB3l0uxiT/da9S/zAKbjfQg=
+- wfNf/Z5SaN7FNBPj0tiLMGeI9blbdHgS8qR5JB90K/k=
+- NTkPVFPoELYkgCjGPlhgzksuxkAdOEmn9PhxwNGDwis=
+- jGc6AQhXghPrQ2jtj503mbOlyQBTxTsuU8W6S2AXTPY=
+- 9tidPftDRWmvGp4U82TJX2BAMdRYq3lb7IzPj7KQbA8=
+- e4rV0kILHH1BtngxOnRg99PsBokPvqTe1wjCzwng7zw=
+- gd2jQ8TOiU6cwDjtN94qeKq6vo9OYH4uDjOFHoAwjWs=
+- AQvfeaoK/+0axaciGuwo1CXOUnMmVTRUsbf4nBqwdZY=
+- bWQlSBEND03RSwRrboqnQSp0m3xhpn8h+W2apYq7cws=
+- p0TZPyxuiDCnC0NBE+zeva9Wi6jAVXxjvQkUUodgrlM=
+- OVIG8LTbUVE4wWvVaGWcuRhQoEdK32HtCNdIpn8lIus=
+- ULzNsSnwFMZd/JexnlWL1buHBXZIgsrcq+ZPzsGbBvM=
+- 80KpNZMLkQKxiRW4LpZKnn7gUnP89s2BxecZADIQmME=
+- H9YgLJuzNcZQQFdfiStLkmllFymt8IA2vxG8RpTZQh0=
+- Kg5YR2/SaFm4d8pvkl2nz0KVnH+z4FgEWVCb7bfZUqs=
+- w5AG2uPSzDSxTHua53gS9v+peJKwxTkyrqnNvrsBTHU=
+- I3Mg1QlxfcPw1r3NXo3I+I+P2UsGxyjBqvlBGO00rzg=
+- TdP2ZuTQefkX7rADQEEWHunqMi0grv24308oUfrYNdo=
+- HKxNEB7mlv16cImnqXwekUlAlTbhcPk+y6CNEQ0S3P4=
+- Yd0p8v0piifzhcQ+rXTL5WVTIjhbWEOPt4B47crxhD0=
+- "+taN4DZs3Lu6BAiR/WlFKczgoVT05VvcjTNMTfK1c1w="
+- JlznZzBQOkSDMTU9ZWLD0gkmNqnaRIwkbWNmq9Gb3bc=
+- dz8phztuMdCnNk8HT8SLegBOr7HM+EaIPpnea+74raA=
+- MEb3Uc3G8uu52sR96pENjsa7uAYLOxc5mq2ayW6sjdE=
+- N1W0F7D5NwJqwbhno5fW3sgN/UY8IywtqvHel0uT2oI=
+- yztWOaBD9Vt4Pj+PxYovYlLbibOQonxnuhmbaol9DjA=
+- sPvdtBI8uyNE+jzW14WI/20kkUBZuyF1qsqgxArjbGg=
+- 4nPbIdGe6Vf5DDYVx6xnP9zyd1XxoKPqFNEyTqpvEWA=
+- wfrSp1grp20J7U4GGI0rKJMZMW8S9CM5Az4lK6vTxaI=
+- JAJDCMeWLB+7GwuPSB4WfsMCFdnFTRnejK10ouh7xww=
+- srXfJfgnVSqhsC/mwHLeHLAdWuZuGkusn3rIu5CBzRk=
+- cr7EvY9tkaiBjrLsZTnB19m0yVF4ZjxrNG0nyEJRJKM=
+- UQBbS/P2sjjxs9RXxsqyfCE7leGxIlcGNytuHQuhzfU=
+- 2QcMGsKR0/nWvdgpLn8NFIJ8b8af4fQaOCznPUQDvz8=
+- v5tZUcVQ9RnAikUVKC9dxp4KnlUVLV0xZXBDa5+hAdw=
+- 1RgB5IhRt2WXteV+uJwUTTeMvRz4J5KUGILaVkAmrYs=
+- CVUp3MiQIA9XCjVZpDJildkd6MR0T7EHARQu5SLYLqg=
+- 9bH2RPfBKLZSLy9JswBVdlXF8tto7xEHKcneiLfFwKk=
+- KKtPia19SlDPMLvvfVEh+BqVosIkkXlYDVEpToZF/vw=
+- bLl9mdSWkpdX6A1bdY5PfERvcJcBVPVHNoebWjF67QA=
+- 804lMAAR6OPgzmG0Lt8f2kF9PX8Ylsyp7HJEJ6Vq1E0=
+- FeKw08M4keuw8e9gnsQZQgwg4yDOlMZfvIwzEkSOsiU=
+- ipvPHlHoEtCvhGWo28yfdBBkvwrzs9COawJGQ3wZ9/s=
+- ky88G1YlfOhTmsJp16q0JVDaz4gY0HXwvfGZBWKq4+8=
+- gNQcVKjObSauC91QnbaxhxQMrjm0t3Emmg0AawYg4tI=
+- CxTVAaWURCoBxoWVQbyz6BZNGD0yk3uFGDVEL2nVyU4=
+- 2qrW5WBOjhe9nxCNkeJq/mKB2sj9oAkQQKem172bQ7U=
+- XIBWXbbynaCwGqElIsN7MvEhy+R6hh738AbLIpIt/6E=
+- noYZQa2L9by2SeX96S1xJSggCiFgGMJDc3FJjmq3aD0=
+- rUlBOGwJCsVBQtOLOQ0xMHXe/02HOhyC46JVQM9hESc=
+- mXBiZmZWCjJGXUzhDSjzIzNlr4M+Fe7VmITZR3hiw3k=
+- VgBxX0K/UcQNwzDXUM2Zb1j+rU3epWRmznSY0XgBs6U=
+- M2ZlsFHbrC5ZsykRwg9Rmh1Pp11lQHIH0uSk0J52InU=
+- EwVIWnEmCP3E0v0XgMcpGfL1TPKIUlgUv/cSBzf23a0=
+- nyoI/eQWzvIViUMkH+4d2PPMoJKv3e8dU66CMY4F9j4=
+- nkYz2HRrWaauwcgvL3xJ/D5JrHC2s4A/lnUt/4xIGvI=
+- pbgQoxkKOQM95NggUv329Ml2VRbWt+6wxJat+KPT78k=
+- 214Y+umoJGDWDTHrMAEdORw9B6HB8SuuDySVtrfk9O0=
+- hAgV85wV18y+O1oqM5LrkilPYpza0ATYNUpefrZY81Y=
+- yTRMXxB5986bAH5gSCn36ORRbpEy4Jjr1Y4sx/Kl/Uw=
+- 8WX8stKhPhV+dz2YtSVTMRTEDEMeoO1C5IhRSwfkcsA=
+- hf18iJ9xzxBTdVlc3cBrnTj8Viy2nFT4wWWqdR2Bs9k=
+- ufGVxcx+9q+tv7xCiSrUfTskxryUu1EMRWSpChTot5k=
+- u0Ifo124hc5Qew71w/I8sJxi6zePrjZBwWW99MAnKUk=
+- n78mG2LB18ANtzr7gd2X/fILNELjbjOMuTWbhWoDvcg=
+- ui0IMOZerbNxcIrzWScD1oNTtVZgkwAhEd98NPicn/A=
+- "/P0HXL42fBWNXPqjH6BmVqPmj2JjiNlu6Bs13aQxC1g="
+- 0rgMsWn6508zS79c0p/6A3QCefA7/40G5OSc35sHpOQ=
+- sH4UVS0IQVmdVJOWVn84s2sT1+/+aJ5S5Dcn06rFSQw=
+- pyus1RR9BCOwxVhGvWctQJtMTTvL9Q/d4dIAUls2MD8=
+- tU8IYjrkA59VvOy6SWEDf7RRPSupyysGZ8XblwrJSRE=
+- 5kdwilIGB0PrHcljcyz+3B6h2zoXhdo/ucUN05VP1wg=
+- H4wXl4nZQlky7/rjfrJmNQRbhWnKOT9S2qjQrXQ9MCE=
+- AzBjiDdtr/LnPSife0en9399Gdt0ifyNHThV+4rP+aU=
+- 0gWvsub33EowPjXdmOGzOdi11QCBYWP34mcobveuObw=
+- 31nCV3hdcHBqQR5w4SOv8oRNbVfrGfOgca/I4Bn18tg=
+- 2lUR0rqoPC51OFLx8vuhEAPtDEbJaCDHWJskOo3beHo=
+- sA7yYq+uVmtR90ASSmsS2YKiz/G78qtWMstUjpjab+s=
+- "+xEaNR39dirjam7uNFmRY/YRAqQPscrOhNmTLRoP5jY="
+- JFvJfsjZ1wwKjCxgSOav6lOWXwgKhum/G0Ewvzt69DI=
+- VW5gCdo+L8d6rOIo/jX0emgMj/nC7O3RJJDliDiFKfU=
+- KKh1mrapE9FvRcQrAJE7F1gw89sGeW1oexyqP+Syed4=
+- mZkRbsbhBS665CmvxuI8UHmLA2EF8h7KWxVNHPs5W0E=
+- 95RhML3zTfgXfZTro08U6iz/3K4T9cecNdo+uk9NyzY=
+- RsdZ6HGTX4zWPKyaJprSsEXtUYrADxn3lHHIveVSPZE=
+- XmCNe+ynUTyrGOajluqJTyxUZjedzB44iMwj1oWupgA=
+- xqFCfXEZzTwEnU/gjMAvEFjRUpH7poqZSBbqIJr3Pw4=
+- c0I8dwfELuYrQwROPkF9FPCd3JL9q2M1YqEe1Q/36h4=
+- TdaOKrOjCXMxjqkD4Iiz00gGVe9CNhCf5HJywcFYKIA=
+- l0SNh65h22FB6uOytdVg6n/D8RQ+yBPNaPlAq+RGFgI=
+- 8b5jVLQYKKC4qhIBCUoVzACt0qUtW95bWCoVdv39+9Q=
+- yAGUzvulO0o40G4y9VXhx0UDhVCZJ0Sodv++SV9Pk+E=
+- od9t/7Xkhp4vdQxBISGWmbn210jRa3EYSU0ffc8VheU=
+- JsmOw7RrCk971eRkJo33SgynryTy8exs/cJ7USKlmPo=
+- gpRUfp/pNn8pGiJZhRPYl5KNGg7rcTyitB8xl3GSxdE=
+- Lje/S9kUSyq8AwQvboeJ0IS+B47ajnTCTz9UG1EdjC0=
+- GlN2rXJ9ZSE6efMQhUHPlQEpaaDTBk8Qi13W5/jBm4k=
+- SiteGCLKEViFhuuRIyCBfzzwwRzV7Ik354qCCVBfToQ=
+- 3mieBVJlEQDBhSQcaHdl+PoWZQ64FDZG7i1soSt1khs=
+- JJ0fnLhGQQxAfGVpqii6esn4zW2VaFumMhpif6CZfbU=
+- 1ttndtLGn3hV5nETOyKhZwothwzsYN3VgjHAi6lID2Q=
+- xYXx3suYb3/xm40D3ro0arigSUzB5Naa2bissN++q28=
+- Ol2Xl8a5MTtOee7YyNNp6U6r43AM+GTGJeUQ67oC9bc=
+- QDpsVWuX1WNchPs+XW4LoSvB4icS7RmLBxSxHFFCw5Q=
+- dJms7UOGmyf1BXAeTtxzfwzDRq3RJA1LqG+/olHg/DU=
+- "//VJRe+gtANO8Pwu3v3Gyg0gwhjFAeGrw5/13U217vY="
+- kQKI2Onp3fWk+exCZOUHCmy+v79niLeiQ1wOcJGoZfQ=
+- rY+chJDASwEztzZbR6ttm9gah1+G6N+yyQlzNZX/9qI=
+- Zr7+7fLXQqKP9EYG8FxYMnHrt0PRj900wO/YMff4uew=
+- xjDLGV9SyIOjd7PB1eQD31dDvTMIjjgSqlB9EpCdvRA=
+- Cg7Lt6bqhRGBhhnc0qGAo++7VtoGhNAVoavr6plv+dM=
+- WZRdol0lIQRbS8hNt9X9RLLFUR/nzCR6jOWnm810ocI=
+- LoHw/5gnHQndwkai0jjZltW/OunSSpXdxoXFjj7t07E=
+- p58ujmDUG25tM3kUhLcC/VcYxhx5k+7BCS31xsRs2os=
+- 7W5EpCzJOCqVEptvAMWtHkvKwqM6sxh+88BOL2REtnw=
+- MEmh+DJ+AhXqkkueTgTNSw/xgAx0pTbZuB09jO2ZlNM=
+- B/8cpXXgKAlcKUuPJ76RU/KEVj22NsaT+8ULoog5VTQ=
+- TALDaODlDnnPQh4iSa6h6MYsokyAXWG/WN9f+TSJwNA=
+- 2KkosgQ9t340C1I1R78Wy0qkg/BkX+CikO0fIKq3Ylc=
+- nYJSmQXMzDeMW5PK2qDleGnU2geRbIKCpOmKIsj2o5w=
+- 2l2ISIQPhwXW4UzfHiUYJF/nxkXsl2GOEWsO7D07jx4=
+- zUwzt9czmPnFFzigaNYoJZS9PoRg5jZ8rdH9CWzIdI4=
+- i112SdRDM046mx3YBwTUg422V4rOHT1yzQVB3Sie4Gk=
+- NfgsBNbU5Obe7wByCnsbeolsjHZ8iqbZMeKw3yPH9Is=
+- 8Lg0nbjfp2WzoB1AXHzsILwzx6NZrc1GJRfzPqzsErs=
+- BKXLJghYGrlKkRlYXHiovUenLtj3o4WJNQaDrrO78NM=
+- k8awng/CkqmaLWDYYvSEtmyHCfeP06MM6uxkIqIZeCQ=
+- dzYUVvR5gvIQVY/OPpsqO6rlNfmJ80fKtugEg7pfnLA=
+- pVsBU/2/wPtWBeS6bLFGQunxfwCP64aFP7wnrSuf3wk=
+- OnkpMq8ocS56RISIbPmSmesjbrWCDQSB17Y2NR/y85c=
+- 3mZwvZbMt/lM7Av4pv6tXUaXlZ9m5xF1A8Qd2iJSyzw=
+- 4nFe4A+KtpXT4qlCExgPbzIUSho4Vf3Q+qEOlnsDOfQ=
+- RabDyG86Gm7Keh4ltN43qk1T5iz1V1K3v7lBvRG6JAA=
+- 2MSG81MAQurUww1M9grHf99oP9FXlglkCLGDlBwzV0Y=
+- B2EZsv3ZbXawSjQw4JyL+dPP4d/JkXwA5meZTC9apMs=
+- rc6YkYo3xqFYvr441hU4yNwsY2YiOQ6CdON0xRNRltg=
+- enX4tX/WbZe6/PUoqu+66NmRBVsxynZuUzLUrHbLEbU=
+- C9iy4Zkx/gZatbxQpeCID4n0zIErtrMaaMTnI892M/c=
+- KWj2C0xo/RvgAnB5sjBtJKB+yYVhKYR4AiD18puD6yA=
+- 8OLnUHkRcbA5G2guw1g1vWpcP3yNHQGRRR7He0118kA=
+- er4M2RnVcnyDpRHKkLEP76zm10eVPdXncuHsbU5vQTo=
+- P8m2iUWdc4+MiKOkiqnjNUIBa3pAUuABqqU2/KdIE8s=
+- yFVUNLnEMxH/mfn/fbsNVbfFOdri04+ow9i8rXOpK0g=
+- oiklucOTrWz7Pew5lwAOBuOKDt4LrTUWA9qmkicT36U=
+- YPgOrgm1PRJXUQMFGhGLm8J1BLvjrd83nlI4YNRiLd0=
+- SS8/ONa108qFlRTiUOJbplk1vN2fT0DBJLdz/lNv7n0=
+- Adtxq4BI90pLksJrp3KFreBoesGSdY6Bha1ScB9knvI=
+- 5xiy3T4Kwfkgjv3/6YAk50BuKcUcMbz6mmsp0ZUbGfQ=
+- CbbSGXsu6Ejn4Rrl1hopSExA4LePo/SS8frcFqjK6wQ=
+- "+WkkjWIbze1KNYKhw7F6ce7f76kSDDbuO9GVdDjNVbk="
+- KnAY3nkjE+ceyjw26kzv0lN+8rYfu96eLz4NUbTy9gA=
+- 0tr6Lm69xrOfy3oqhyd9Shwh3zyq7rYHI6aJAuyIs/E=
+- DR6kwlbNUKKnzL/SKz2ZWfb9ML2EC5/zx8Ze5OId8G0=
+- CDNU9kwZqvBk+QJwQmUXispwVINnsT8am3Xd4CIFJXE=
+- 5q4zVgibgBVeJCSq40zlpdwx7bq8G+DbC70jqnKue/Y=
+- adoG44w8v4AWgtuBImoYR/U2xjLC+7LiuiHsJWIG99w=
+- "/0efnYkQBcU17EMT03R6CV/03uMZmciBIn1fdOCdhdA="
+- ebuNKbrZyVNLWw0VT+vwzsXvvbnRWCG7ZnWvJjagYdI=
+- bPYV1byqx3g1Ko8fM2DSPwLzTsGC4lmJf9bOSF14cNQ=
+- "/h8iU39Q2BtjcaDHuRl7c/lZU3PSjRpUxVqQI/3g9Ag="
+- OnOVQkZGDd7uXM9ZRnbP1AyYbcSHgQSxgbIP8Lj/WKE=
+- 9b5LYuHEX2FRskfcrlE9qKkZBpQlNfDiwjK/fv5HUL0=
+- nki61uMcEoJYJXvZl+rMpmbw2uIEkCe1RepIoM9D7XE=
+- SEx4cgMaAbrI+TR+xjuagt8c49GRoQfudzdWwYWvjWA=
+- Nk9fBUVhruWyiSpDhjHa8wJhYgtyNKEmAJFDU7HdiHI=
+- qQEP0hqTxoezxMUGMTmTtaKt6HtxnQl5KxIKJ7hS90k=
+- qf0HhWJCCidraQLKPYv1g8A2bjivcuI76ZsWA1p2s5o=
+- jzmIhsMmtfjweyCsJQyH3mcj4GJHRGUnP+FSTyuQkvo=
+- unhSJX1Z6F/7f9SUQ5t3kZ2tctcwBqAr7V/CbpSA59Y=
+- YLQLeEanr+lzzgifGQaM/8U+QP4ozQdvk3xkT2EnqtY=
+- mve8lwlgkMXPPUw6j5Q5XxUVSDy7UKjO5Y+KyGHvdno=
+- bfpe2ZdhEzcUSgx591lc6uceM0+XSHfAMUVR66KBKo0=
+- W8uA0Ggda15CZ0sbIkMYgpJm3wbFigRH1RrGMR+vrnA=
+- i75AKd8P3qdj2WrIOuZ1R4kt9WPh0T+Ek7qsRJ14AEA=
+- hmfD7Go+m8JbK4RhyJYsj3iEWMyyRnZEmohKf5t6lYA=
+- Rs/SqrKdJLkDNQHtorV6fu6crDXebYhVhLXxMN8uqnc=
+- QvOMEld7oyjyOsS0ZXKg/5HiSwAGn22mDOgAWOUaARc=
+- 9AYWv69MHgYx0gYzDq0ZuGFUbQQAs/m+BYnbrcmFrY4=
+- jGJ1n1vgNi2zS+CJpBGDGuKhJX94bDlput9K9zJ7p00=
+- sP72IXJ/+Cp9M02fHwR9xmLtDifgWqj9Gu/Rmw//MSw=
+- yb5QNg8HgjbyiA7EG1WCnjS+UNvb0Ara+ZI4NEXd3xw=
+- PzNxx1nNTTm12axXH93hAHVoOR1iGw95n95w8PJFpuE=
+- lw6vDU3JvsPXQTT6PWnm+AJcA++CqD/Vx3guP+cAdec=
+- n878AIDYlOg8p9NgzlzNnq0sXYqAoQ+fqWmFEKq6hlo=
+- OadZXFnlqPVUw44G4Thf2a6BWq9WchLg3beU4aUpLkA=
+- MRPJ5F/wuOGfBuRD3qo2HO2wjUILDmNgYhnNWIH1+ic=
+- G60B5uPEHqcuG6jyY6FhWGL8bZFMpZWkSn7+FNC4AxA=
+- osqx3spYyDUxaDEPUgw7t+RUZuZyLXcb7RkSYHe1hwM=
+- NDYlG9/T9i3FNRqvGjTMY2hpZwtcNGnNsaBjSsSxKrE=
+- AgDwMxXWvxnwBlUHzrYWc6DJ86+vt+g5QRHvqD0GExs=
+- QrkQtK8w+QHYaShqmKkGMWtbsIke3dT152MK2o85INw=
+- iExhU3KrUuSXiJ8o8yRRhfRUrluhJ9KJvcDSs4Wgy6M=
+- r6LRJNdWEvgxNbYdaVuDm4GbrAt04yS9f19mPiuhmPg=
+- qj0VrROSZ+VqjJTNWwrzs7eK2StlONVk16iRNs/Frrs=
+- ekfUbjCl7+dJGb+1V8Y0cXYyjoMsQA9grc28GPaOhqg=
+- 5vRYB0ZorV2PVdQUu+K5CPu0aQnqNrGchUgEW77rFk4=
+- Raj2r0EjDH0yOeY5RxeytjArUZPYpE+RtU1HtEKyigk=
+- TPjFdtSRSioqWM8jDGPQqEuRwby23sDJU3dVC3aWWEQ=
+- vdYMT+g62dfIEWs40lzsl4TnPLd8hRzJyBDYCThQnV8=
+- HIi1JGAZyco4WsqcZ/ZjFEhiiFnhVj0lXA8cUTcG2lA=
+- H3OgGOmm4guttneOxXKoRqyB7Cu85CF7P4gGDqYO2ws=
+- St/5TrPRC7NGkRADEJ/1Ga+soZfvtIG5q3UFvneQKbI=
+- P7MS+MT47KwKBYaptEPtFAaXr5JW8yCGJaNwx31HyS0=
+- oTj0NXs6J2f941a96q8Rakf/rPO03ZgN4D/CL5El9TQ=
+- cx5upvAEIBNagCG/dQQ+6ZjcpATIbUiY7GqjUgAxkWA=
+- 6opxvJZu6+C8TIObtmX2qCMDnBJaeIx19Yh45zcpxUw=
+- afW0P6Sn7GfMHgqsJMxznxJz2+FXnW9kOe14QFoVMm0=
+- EXYEEGxR8TfdDEN97Geg7ISsszb9NwrgbwCweFtsZW4=
+- vDNvIMTpeEPB7jY8vIPufNBcybHlmu74uaIjbBVaRzQ=
+- qsZ2sCyZvwC27EOw/caSs6liNWjZ5XUflYpCwBe4Kjg=
+- N5gZ0XMqozFp98q4sRJdCSb1CRUGF6P3B9TpLST+LFQ=
+- I9Z6xG5qoNg/dGMD95zprlFFDtPM71bYrQy4tKSVjSA=
+- ozUXgMTgcv9b0wpotKR/dsX3KBzCo0djJO5L/TBWl3A=
+- czlyUVmJPFfD+T/PdspxXK43US3wIygZtjo14Iy4KqE=
+- eBVN9fXkJBsDtd01KmIoau2zl74Q8cZpYe0TyzNuQik=
+- od05XBnzoff/Dmv+PuYCjkNztBCFptp3arAqi68Smrs=
+- hKtYAwlnrqxRlvc62BjrJWp8dCp2r3eigy5sZ6oJzcI=
+- "/N2zupGri0/zighCTzQ/f0Zek6weYZJuLPKDudSTzgk="
+- c7Xq6QGFOsZ6A8o6lt51he2gHEP8i53lPUyqO0R1nyE=
+- 5EINk8D4jL76OwfFkiWmcMmLWf75rhfovGSruO+XQuM=
+- IFBM392q0LWQylPEhh7dT19c+cNIw4KVvS2/DpG8pMM=
+- 6/SdzYNvgQCEwU4PLatNwXaLvcWYBIG/IB/Pdncd/3o=
+- t67Uj1sygouVvIzSFHtuBtZgHAKRgeiMdw9DjTlxMkQ=
+- L3LuiYXOtPbJ7Czx8a584Ju/yjvqsCeKOHI17QFORbk=
+- vmYTxgtfN2mU1MscjBevvoXc+7Ga50wxwe/5ZeKJI/8=
+- yQK+6b6/adRpgcKQEMI25HFMH+tLrryeZ9Ga3GDh7BM=
+- R9IGWkVQWZPodU1F1P5gx+s/smtEoaGOlGZaZagFPWs=
+- QEk2QudohJhTDEmSmGLsqc6yDUaoT/LHa9OfRhOI7Hg=
+- MlC0pv12MeTenT9vTvJygRNAH0ja/xoIojcW/oLi2Xw=
+- RI1nMe6Y6o1cl81lG7S8PzkM2FsIxCtzFreyfZlUbV8=
+- eg0a+LsvOVcniyOxToaL9vQMwgPJXSGcQM7a01UoZ20=
+- FTurqh6fqjh+dpm6GrhoIK7Ns66H85+AKg6e+hRuZ9s=
+- 6RjfP1M5IjTe7M7cvxG+bMfvDbJ4Aa+/T0EMIqGmtuE=
+- JY8mTiyj8jdupUKqSRugcbJLwEQn9FYQ17Vtg+nzR+Q=
+- 7gQijFxtiwg/bOk/t9fb5IfgSpaynRP2J7AfmtU906Q=
+- gZIQq55wjV3dqIH4RJnQmFRv+J5YxV0aeV4aTJ3QsrA=
+- KyIZcFFBmi0CkkGJneMlZRu2/KhflBTXZZH8gVoiD0w=
+- J+IHZT+8CaDE76dnHj5u0C7HTd4JkEhcIh3U8fK70Mo=
+- wRIzYHCaUOWBBvdvnb5NY9bpqJKA+e0YUf0OGTgPmRA=
+- WUsRWdat897ihms0QV+tTkHWIBnw+S0fa1kPEiEPLGo=
+- cV4AAWjfU5ZshM9R9rvXzDlL8ce3XlbTexffOxpuPdM=
+- z8LUc3TEvlzFPI1OGlcRS140EZMHj26YXItJHJMPBYY=
+- SrbP+sO3kk3FIoazvPBMh/Az+c2sZvuXgy7P2Gux+T0=
+- xHDE9khTateH9Uy7BimIuwSVzo9Ns8hkMViL6IrEpsQ=
+- swStwzcZ1s3bltbE8apGYo2SdUe0m3DJgcf+qVPgYAo=
+- HdkwjfcuIaRuhEO1XK0Wa6Isa4jqMIsTyO/PuGz9GNk=
+- WV6F70BexBYaQbSAtxJClfdQyOzFWCK7UB+BWCI7oOg=
+- 5JhCPWiReLfueb+en2Ewi5vEfOeKFvj8UX5Xk6500RY=
+- AQRTUI11OzS8olUjAsIZf0HJOA5E57owIH6COM58JKg=
+- kyPdZ4bry/OshzV8x4uhq/2mz15VzQEJe5DUoobKyQ4=
+- 3kA32D9T5xVQCpzGubHD2owONjz6r2fjdWaJzcQoS28=
+- o5GGKKjyghroq+e74dgXJB3yy5PczUz66DstjGQQfEM=
+- 8cYSexb/Qiv6CbmDNKvkTF+IMn4qIYWi/gDOJs3PAHU=
+- fBZ7IrrS95MPgPQuU1XGWS0VtF2Ci0E5SCzNrALt5VI=
+- Tc8yskn4e4km8OJ5gUmyeXIIrmn0gua/X3o6yn7Y8gU=
+- oduZaAgKYU1pTm4/vrwyp9yCYx4ZtFhwsvuTC3+kX9s=
+- 2eIYrIcNeIwrdhcBwVrSvvnEhJkmljq+uiABHSU4kw4=
+- mfeMskLcLIUqdfYQDxj7i6tL/yRmHJWgA+j8XwZVuwc=
+- wrhIyacCXWHov0ysWDlsmVH1xgd/0TFAhg0G/8qnY6Y=
+- ZeO1hKvR2iu4v+8OjqyrI/hSOZ7F3MNKukGULgPGlVY=
+- YxFKHiwEBY70e/TKxXfQJlejNij3Ni/dUlWnXyOL6uA=
+- FdfBqg0bBNwR6SGJrIk96hZ5et/+QX5PEW/NeOLQbko=
+- Qg4I+sDPLHzdwotetMIPxSR2zBtyDwWetvVHVWs+dEg=
+- I8uwbQoJgHbA6f9dYzj+xmTaIF8CDEGQV9PuAJhcDl4=
+- KE//O9JUtIzKBai/xPrWngXK0NCGUToDSmahGIKeb6Q=
+- XLhJXZDcZeoND1yofNVsl+aYrNND5FD1d1/PioV1wsk=
+- fqqWhBfF2955MhqQnXhpI2fDwhYIMntJERbjnLro7zs=
+- IFVW8UIiA4rYrHqjV6t/cJZ2NFpBqiU97gh6C309S7o=
+- HgS9QpVk7i7kGIOj3TDhWUuYSQu9MpbA+uW5grSGF4E=
+- Lxg6TmRJOvPzd/dF7aUCNjzT5+9uTSZtREdY3gqF/Mg=
+- HyCtFyESd+uwzWJAR/fFnu86QkblWPfY1iMpkgvtr3c=
+- N+gGiuLE4J6IOHsR1VOygkBlYbL6v8f+XWjpcMTEHhI=
+- qP0QpCHwv9P4wlNg9HtoCRE4gYdrDzwa6qxIADnRr94=
+- QBM8VM1yDPd6F3+B1Ya2nudDfl37XnpLe1AuNpupnsY=
+- uHxSK78FN5flwBSxShG9x/FEbuLxYYPYbOPsELAucyE=
+- BQ+ZPqIyLUtpQPhWCiU6EXCf3Fqwj9mUvOsJaEbqFkU=
+- iE9Fiq/4s9OkgK5/p4Umuoo8a7f653xhZ0lqHwv2I+0=
+- 2ozHkJBVqWln+O7MsNtQvTMIgpoU+u4KyaGxxmegyFw=
+- AIkOkSLOG9PfxvzarkHKRqirMFDQfH0V+m8ZtBZMdZw=
+- fIxgoZ+gtt8nsqoM9q+dhNvgN7LZIHBIHXKk9xs24kw=
+- nErqr/nm17XT+v5lChXVBQDU9x/4D0oiDpmY3rjWzQo=
+- rAxw5Cke4DlorSQwlV/xlWinSaldePvqRSBsf9fwlaw=
+- 8ff6SLYy4p+r+L/4dyB22+G+1WD5JBRrfY8ERwNRRgE=
+- HVYgAHA0bY3XpuoCXNnQDSBIxCx9cLEpT12Ipf1MY9Q=
+- uqL88D5XubQxsjK7Q3vbh4wW0OBucmd99VGvBBUurjY=
+- PoGSz6F9j2YQ43z5f7i2y4a8G4LdYpFt57GXvPWq1v8=
+- 4JRGEUWvr3bJfpan2VQsC0+e7RWNpzOMKahAD6bUqz0=
+- tPvlcUTfiO7mmDj+5ZLf5/wlByLLzbIXzInmcpX9gHM=
+- 8SC7VpjVIMVpG21gOgC/1mLRO/F3oEVx+dEMB0XfoqU=
+- tmUMtafjCMEuYXoF4xooN+iL9R0f5mFMuJYSdDL+JXw=
+- jOlPyYHIPKWGFs2ILykllig2XQeaKFpaaKZIxeYcVPY=
+- 8EE8npbjxzPuFNv6GH965SwplxVF6JltptBhj3HN1Us=
+- nrfuf1UdLwrGhJgb0fHi+ko3WQGZY2dT7+YU1Nsw6OE=
+- "/C3/asPkylL+zsqcvBkSMhe1ciWe6bg1V/qAQTgfeZ8="
+- Qx6HbRSL37Dkd9nz2QqhHvciBQinHBEomP8BFA7Zl3c=
+- UzNqZ2xkwTllU7K3yS84EmdognyTtk2RQgacEO2npyE=
+- e+iOEibGUvBHehpxORQlYSy9lKXWPOOREDkzrPdsA94=
+- s0Vj4uvrdxcIugZIyThTg/YcwR6Gr2B2hIQzBZwUst8=
+- 1ZiQ4L8Qh9Fv4NWITl/iLTrOWyzkVIKykJwfhGBErlw=
+- m+E/ZrprtxRqRo6jdNtGfm+RovqbwVeLh/hSB4vdQ04=
+- nNz7vgGDsvGFXuL3NU+yqNF1sTOyJwUqCVMCtFWb9SU=
+- fGauNHCYrI7VvdgpPv3HcWdf/cVff6/q+yiqZN/oTnc=
+- HaBnK9Ny8YHXn9U5Gq7aENJW6/2V3bCaubUeUq/72/w=
+- syrba1TIcnBt6AVdFj+e8HFr4xPHC57GHqQGKDTja1Y=
+- ERVs0iSlxz09/oP2xblF5oGn/zhPD5UdZC+F8Km1U1A=
+- ag3AGwkFH9HfJh1B9bF8f94aZMG+KlHAAvk7aetIBh0=
+- OLukIYTJ1qpRsTXm4+Z/oxrrsk0ohUXkohMJ4ilYe2k=
+- 391aDD5wHDNTvHjkJ/BPyyxAugt7VGcW448s6Okpy7M=
+- "/ML0BHRngGVD4hl8Mqc3qnRciJ8WKITLus7TwrS9SKE="
+- 4spq7FJVj8wEMRmDutA+OMpeeZwjnSAq56T1iv+KaXA=
+- q0XoR0JpsCb2aNSYYLNoUBIuGKUNXqOPP+/a4IJhhlw=
+- Jbd9JLZxHNxHJ+tanuKSqRbFHEFDQ75BQslPfGFOhP0=
+- 8qypO4DK5oEiHwRF+k4sroofn4+h4XQdljnKrSIvU30=
+- 1wiM00R1Cj8J0diCwDMvGNinLTyoZGAQZwBuQrIBixw=
+- 6X5nlcklJ+bexY5+pEjVVY4RTxW4aU6Id84ou88PAao=
+- Uu89jZBzTEta3KRpaqFMBqNFeV8s4mVdpNyoQtWJvlY=
+- 0sqoLNtaCjP0K4Egg8SkxRlYBfW+IFjNnorsmrZYm7o=
+- wgz5h5Zmp2UAuxNq89DPTRVoMGSz71va1c7Uygg5Jro=
+- i5L34brPyV94fz6C0+oU0lfN3IPaDuw9m2N3Odj9mNo=
+- PZzbKRVASlP2O9MaFYaF4e9ubpkCvEhroJe4l6voMCc=
+- oDwy/NNRy6LZc4YisIO+0CLvB3k72StZ+uoCB2U/Nx0=
+- QCaW+7rG6DrfxlSfCgEPvi6/AdgMiiVSQnJxd1spWMU=
+- 4V0SkkwOCQGMaZ0mG06L8JgOzWP92igQJ2SFCu5PiQo=
+- DYJmWbtplAeOwSEQRHTTVPqZSh2/qHUwY4PmMD9JrTE=
+- 3EtOID+d3c5Gh/HWaYm4OP4oGrS7S1M700/ZtXe/ETY=
+- UZB298Ts/EQol6wxaCBtjjjtaaRPKoGpGb08A3oJXhI=
+- bPXuCc6hsj5eosljEN6rorwx+3Ua7rNkuRHEhchq3rs=
+- f4L3KiDL4pS5nQ5pzPXtZL0SbOPRcxNcFrUj4CWeAqA=
+- 8/bHAxGZdopT3adOkBN8Tr+QuvP1g5QXpIb9Vy8CFmE=
+- b16QUZxlm+fp25l28O2IrYzjM9TqbDs7DT2jksxP5fU=
+- t5u4vRZwUJtP4T66on4i8yqpIxgUok4zKm9ujUEm5d8=
+- QD9FoJZmXSXyt7B+Z54LrSSuK82uvmsOmCEPaywI1ow=
+- OJ1GOJobPj7MZDvCv4c8CYmqE2sVS0kt1lwG0hwv4yA=
+- 5gYAYWhqwYR9aytV1mrARPOBNHHQRRuchv5R3wAyKgA=
+- iSfKE14MFFI99aEoAOJeQdPqCVlz8WlxOMW6M39Vq0k=
+- mJoJ61gL0YX1sZ45fZNxCfLdeZfWLCFym0+WKbrwm2M=
+- 814fOrFqBl951cO81wXWc5UMSDbXcbE5PJPMzsaifAw=
+- 3oJMlWxaQvdXhodh4tepdAm2cBSSNrFb7FsUXnNNoBg=
+- J564jKqDsgBWoVzAnGE0urDniPt0o2mPuiLevewtpVI=
+- q0BAbBRU9vfSfaQuUcGCCnV84mx9NAThrRa/mDpKEtw=
+- CrAmWEKhx9E4URMzSdJB4qglSla+8dR1Dqt7xiAVimU=
+- ssnDiMnr0jL/pSDSn5Dl7YpodZOWV8jZP4mde1ZRq2A=
+- 4SO+KI9rs23kALAodkel7Y8E45XcQ+5L6N4oXB0OWA4=
+- bgAHRvFVpE0VIN5FNMDFlgcNb4N3uiIzpNHWXliS1eY=
+- RzYLjF9YxboEyM6EhUU0LASQz6sjzp9Q4W5Asd8lTvg=
+- sfjhv2U8BF0Kv7SfVwhqbfLbujvNntJsPUdJumPQETk=
+- NvUJV/Xgtu4+9FVnTaNahmZ/MxQgncFRTFEP6V6ECDE=
+- 3D0uceBS1lO2eMGm70bmva9MOrccHJpxuXjw3qJqG1I=
+- QwMVCjaLF0QjgeC0Sq1rQP1FJB+tmPaGtSld0Doi/uA=
+- INGhQVhul8yXj0m/6OcnECz+wQD+eOHa7VW5Ha30Lpg=
+- P9E9qMjjg3UNIV+rFJk1AI6ywNpPBJcoXZfNr2+r28c=
+- 9AtTDjSixheCMuGFTs05U8ut20iMJKXxffd10LZ1Mlg=
+- rsDniCTeEgXPmDhHgJg8byt8A/im5QNqUI7OdvGzxac=
+- LFi4Oxxmgp/2BomRZEt5P2Np42W4f3kOrkpA7NlxgAA=
+- Y21c1QTO+rlOHgE3zVliGZVmi7gYZrHyZc18GKmFZOU=
+- jOt77hodmh3k7Htw400+ltEOq1kKmWTD4IK4GDs7u00=
+- 3gFS+VJ3bwn1N9MJ8fPw7HNpyN8SlwcelGlS95jIYGM=
+- Ktuakm8ioLScmJh8GI1qAsgUvODvw4KUiysLI3KVv4M=
+- GFzo/GYMVgfAmvtETYH5GDAKH8dzfXgOSmwO1YccbdY=
+- 5dzT+Apax2liibTzh2Zb4JGpyE95HrELtz5BcIT73yg=
+- 6GQhy4ykKHEIFLAEPzKkbRjXyuzBoEYgw03OMFcJHcc=
+- xVDC9KDU/YwWKT4KsJSyJgXMhPfne6nvQN9RbY3w+8Y=
+- shSSsV2FUA70nlrZkOyKUwwndyuaPRKXcP1sczJXwZs=
+- ecfuFWAryaCLzWJX3670ECF8juJHNThnMmAKPli+yJE=
+- 5IRDkp9X7Ey5ZdNYqCWEkVWygohzxO0pKf1xG8nwE0c=
+- atYEabYF6JfEBb5AO/GaTu7zg1XGxllltrxwKqNVJo8=
+- WnOyqVMjmPwjugk7fEGvUOxzt0uU8gWK6QnvQ94MxPY=
+- XmoJUVgoOstTbaV7kZVTh0rv7AsQ/62DIvl+GUv2bvU=
+- M/EKTR9km5K+t6iOXS8Yp45cFGvesS+n49ZfRROiYRc=
+- W0I4nvhBOa6GQ68qGzaQiaPnKYxH5ZDRiiAbh+vuUeg=
+- A98uX6a6Xc3UO6nOgLKVXb3dL5FBgsWLuIK0R0jQqwA=
+- 15wTs/fnXB9hviPuKnKMmsGAZeZL2yrLqOrR/+2v0Wg=
+- 1Y/I2vAOm8PihMhTJqhIbfKEIS18xpz1YuclS200qTE=
+- CyKYRAhFyVOmfUwqHo5s1DYW1hSYquS1eUYfLPG7zLM=
+- 0Qanirl0KhpSoV7I4Fp9yUtCbCcgHDGexo5mumM33KM=
+- 3loa30/tzOFTORXtxgF3VH8QV7YbcRn9Ew4fdChwX3M=
+- UeKkZyHRBNkUjYW2F4M+d0X9vWeVywtQKltuox0zN44=
+- kY8vFkxuYLTf8aXsHzDrSOLiR1kV/jhCfzpCR6AUEFU=
+- LFq97yoOrLSfURWZGpMx6PeMWhX5hkJoFf2UhrUjBig=
+- 1Cb4Ol3AlW8e1+qGL+KELN6UxyHmXw5W6ZMo43pWMSI=
+- "+kQTKyOOZ5WPsX0zpx0yUiGAUHmQnDp/W+0aA2Zs+DQ="
+- LdKHvDqESlVTpfYYBt2AmQSnN6INO8fDUIAwZFQP1jY=
+- zp/ri7UR4qwpP+7Gi2bxALF2er7KbqirKP5FBANxMAg=
+- KvWpooWEV5189QU+ig+v9457nh3opI7OamZPYlS9rds=
+- u5PP+IUdSazN9ptmWwM54c8V9WwDbrvqXJ1CT3mOJH0=
+- ltl+mEMcbfFS8NxNKJTGytlr3xEnRMGb907EOj/+kvI=
+- Bp/KAJiC4T4BxrBVnJsUpDN8RJX4P9cgll7IDwdwppk=
+- TpOUymW+mHNAt7cG8kwokLvTFQ2u+JYTXa54IPUZT1w=
+- hQYpXW5B1UphMJlHtnDpX7H81KbB7/d4y5hmAxCrwDQ=
+- tvbzacU/ZEqpsVd42/8qHwX8m3Y9gjqoCzJfzF+1zsE=
+- 57wvlzr7jfrwD637GVlnQRCL4Iq0oQfGp5nEKbaExko=
+- zpebV4A5TITbABGwWHR+Tc50Y+AxmSl/BlbzW/9Br0Q=
+- pnkeDRKZIyQkZv/YyT47uZWHkbJJ6ALJs7Piw6x8DvE=
+- DfNlyhdmlYSqifPKpLIlvcNg6tAMROZ3nfAe4qaLd3k=
+- KyGKPebpw0jDxILK7p7Xk7eWPFTTu+dXqLG6f2TN3go=
+- bTUKIVWs8MDNfcuqsMlYdSClnl2kZ5SNClaPSmHA96A=
+- BrTqswkt3Xmm9kQK7qlib2jq9ZUVeOvBBY3Xa5wL8gQ=
+- JMc3Qp4DIel4PTobG6lmkHqepwb+K6hDIlnbUW7yHss=
+- lIAAD355RUZ6PQP60pLDKVK5W5CISIidS93/BwjTAYg=
+- 4qUw4lHTZ1A00j9cX4f1TsMYKgiLp9EzUIJHlPjmt24=
+- 4e0/Gsiet2qR6tOqGEvYt7t0Jq8wvsGSPUFdIC2FIcA=
+- zIbYZeR6Ja9Lj9rSZ+KMMKYE9yjnPuItL7VbTnqSzpY=
+- eOCgYf1eJN/dyTOEzS08mrGz4/+y7iQ05PkJAT6IpAU=
+- r1iddY2cVE5ewOYgNLHhqwjS4d3M+4KwSbMDWG2Z8T8=
+- vwric29nuAJKov2jBikeh5SXpOdVv2KPUtnsaIAbsb8=
+- f/Jsp2Nv7N6VdXyIx5EXzVczNE3Ok1mUugaDvYrjBHE=
+- "/nkUgWGEjk1QUGoRnMYgd2cQIxfgDDTK5wZ6bM7heJk="
+- gGN5zekvlFEz2afGDIqYON1SWfDQlHAZyYflf5NUQ4M=
+- tkQW19YRUMvvaQhgT1vdLyuM/oRXvSycqBn2WURFfJk=
+- f7l4y7NXGhdTTlCx5tzIKquYJaj6p4dguWq8IVRljBM=
+- E6W+eag0rfeITjoITtjmCVF16BEmjBgjPr7llhotV9Y=
+- a+N5BbHbHJEV1aeSaTJwBUWUzTOOBCaMxhl+tsEFEdY=
+- OecmncyqfRBjZ8B95gD+IhN7QBYxY9QfMU8li4Bmj7Q=
+- T7sZKdTgh4zVUnnOI2nRPE9zmes22i+kQn6c3lYpx7Q=
+- aikax2H1dhYYuYv+N82i5YN30bJIxQiIBkWdH6JY51w=
+- hjTB1Djt4EGEAWcNr/CzUQJXRO4n0P4jyAyNNfbGJDY=
+- 6gEWpv20LSR4akQDHIpfN4IBdchww+yQq0j8mJyki7U=
+- WBV1X8bvrM11FAGMwSBKKGv0t24h7vorGcpACfJjcrY=
+- if24p5Cwbhc7KyXBU4yX8FE/3YT0Y6xNbTMDzqjaej8=
+- Xf1G4npePo4G/LkoF7CVX3/SgEj1ADv9Tlvo5nv0F9s=
+- 0jlqH7y8zVzxf0xwlp+4/6aG74GMO7pXtaZWhOwxue4=
+- VG9yn5jrA6BIb0jtIERxHfydp54QMafIg8Mzv7bU6HQ=
+- kxFF1N3RgRvlReSsiKgfH9v68HecQ377oWuIRZUnTRE=
+- bHDQmZs+vwHnboGncq6dBIZrxusL1YyMtW/72ZnETIM=
+- HbhrSecNgOTm80zBEtPdlLj3VqH63nhAw2AER3YmBko=
+- P+whMYUrXbW3jl+7/n8CMJWfrs/buCG1ERcd33QGMLs=
+- 6rxE+lZVE+1TXzv6pNcLoo6tloMEawTQZ71X4BqIl4E=
+- HMxSEqMKElbBDJ0gxljBCZzRo9Q3uKAK3R6Wi9z3vRw=
+- LwDHDV+phn170iB+DQuH41qayWKo3jbuG+OUS2OzkUE=
+- e2SxL1HKOdxoyAm7EZeSwWn+JO98yaeRatOwGv7asH4=
+- aHs0ah/iw0i2A529zfglUuYjtflhCThKTaqIQlw9cTY=
+- 0EA9Wk+SLM4TIpJ/R/ZOP/7MzIuWWTkNW6A1xfRQiNc=
+- eTAbItLI25IdsmB1CH2cymDi8fYzSnmFZplrPF54ySo=
+- puvfsSbnAlQUEJEGr5+4v5z0YGbsH/jvIKJoB0ne8W4=
+- F8FTLKbP+PajqCAAKK9sJYC/N/OeEMsJZuilc+OySh8=
+- 1Mf02OT17jEjvsQtV8pL0Zki8J5F+aJZoGj5s4Ywbh0=
+- gXk1BTsqmbwBX6H4ZRPDaSrYwcrYo4vJTsytjoeJXpM=
+- 57wwpSpLQuh1O/njv/gvkrZdDRT7y1/YYoSG8N6poUQ=
+- SMWh0hf+hQgkZNLKHpChbRVGT6viD4YQ15tjqlh5e5s=
+- "/bgn0amXls0E7hy5aS8ZrZfcnF3GWbr/rho3hwNUfl8="
+- SNwTA2XGHzUlUczGVapVW+2cU+Mw3y3Pl9fB8ItprPo=
+- azn+ilkksKbZtRb9skPQMZCRNkMSTK+8c/AThtKSyPA=
+- lSPZ++ebzz7l+fZkqAQfo826S2uLuQLLXsM1pb8cp2k=
+- w7KStnaYTTn4oefZLvRK/z9nJBKAi+T8i8NGAQGfYug=
+- 2CCHpih75mAm5LS75sq2c7XmKfp9IADSDl4ioRgr6zw=
+- 2cwXyi+Zz+UIAiY7ZQvjZn1pIONzY3YIAd+oERUk9bg=
+- FPjspypKCBTUN5g9vS/nJsx2W0nkZegeZ7eLrEHFU64=
+- 46Wt54NiCX6dsyqVZ2jlqNNJgMkuNLPBRP5xwmnlPNA=
+- OmLugEg+JF5r2k8kvSeyB2O1q3//Azn+l6UYHqkRXmw=
+- mG3kUpZOngXjjz/4ivCzqJSN6Gdn8JQXvT2elQDeJzk=
+- p+tzbIzcEGy1NUjztiKb6nVLkSxwUWy94kC9282j/r8=
+- LaIf+7IV2MVlzdyVGN6uOG+SaMZj8EXBiTMhzwVtkWM=
+- 2Ehn2w7uzbc7XJJ5Eht6IHQPaVTVyKqRLG26BeA6reo=
+- fxjJ/LOqyT7jrcOUhqP8sf9pvUsDZDfA/CDzLywREtM=
+- YMYMv+ZpKWXBLvgtvxeQutvU3s94VEczRDhxtRmabRs=
+- WTGVeej8WsH+WBCrySZkuXt+tf+8G4X/la+qcBPNijQ=
+- AEhXV0TdLFFztrjo6kGRxLklvjrMlYf0Aaxtt3SbFMo=
+- hwU2Ehq0NzAEuTWABIYmnw9t3KxtVCk7wClv60we3dE=
+- Dnw+zVNkZ/ixHSznWiOE5Ll7PxS88L7uPToMBCDZ0qo=
+- Pd5uVNsqKYXPdvYtgw6JEaB04JYb+45K9omgIgMzsHs=
+- M2HcfrlgffLWa7bmV++Ep30jiuO0TZ5VTfTCI6t/uA8=
+- T3Vj1rAScmRrrzuocXbPCHd2FYdjxScDlsn2hzYtzIs=
+- wpRx9wzNhs0v5I53/XoJl+mdfFv6C8+p+76oZ9v4juA=
+- NBz5Aio+ZGkJjten0FYHH3MIfSg0B8OxCMbg/YmW7zw=
+- apjpLJIpHk9lyMNVtnxLYcrpkyNKwD57Ho4usyw76GU=
+- GZ/7VYjXL2iy4MLs7aG26H6dXNovw6Hjf7pRY/+1II8=
+- eqb5EAW5M/wsafVlJ0B1nJvJ9ZKoB6uzAoZjHh3e12E=
+- wB/GFG0JHKXRXDfNNt0S10P2CDuU12FYsGLwfjFSn6Q=
+- vAyhc8vhcq3v+eiLtYqeDdDXA5gKA6nYVFtgrTCPsKc=
+- FsQhQ2wTweJxi4mhppJXcTXK3Y2SLNnOti7UmS3FNdI=
+- g35Z0Dlqtfz6ZMSN/pifSW7SE4Z5k5mn1DWCkAhxp6M=
+- JGc1M4jACuEHEh2lsHdRWEexsUEmCnWy1idnu0mvgyw=
+- ZqVJFkRd1iG4Qi0Vfl1fPQt5T05K2rtvJcJ4Z67cvtU=
+- Gp4+9y0zNEeG4R072boXYyLNym9scQ8zoGwzHYG4TZo=
+- FVJgc9qVUNtJbj5nrtOKDZBae4s/H/U9ExJsXVa/Phw=
+- xfl1s1xyz+LacgCcmGanzl6vrc5XrzlcnBtslLOD7Sw=
+- "/DYHygRzqQ4NN1MhLfCZMZwQ8kIYZUuvmEiRsObyyvc="
+- WQasNhoTfi0oZGXNZYjrtaw/WulVABEAvEFXfD11F2Q=
+- C0H3ofcTKrPxluOS+t/ScTwpF2IAE/Y6+XMBHTM7/7E=
+- 7BDl1VDedmVFtyKKjfnIDTB7CdpHozrbgjDJxfwwPGI=
+- w++BlBUhPDty5JsK6ixZcsS0eplAX1r7eIwvU3Db/y0=
+- GoHZtEro+u3xHYF8NcTdGQYOXTKk3FjOQvMUnMUW71o=
+- I0xIw7nD3MLsIbaUpwdcbic2jXVv724KFaW/08nfJW4=
+- B8lwwaFzoQS8H0x8wTAWmn+HeD7zt7iylvDR7u8ODog=
+- "+qn2XTz5LTbhLDZOoG/aGbhGk0IsRcY/UJMfJzcMztw="
+- hJw5g5ulGBExYbj2b2cv0Ty4CqCGYJxtqj+SaFy5VUc=
+- 8nQ8sz6xksyw2zP7h7HGsf3Z54Q7CfuwVxUh7yy3TOE=
+- 6O6IXQnMpnGQ+ly2cCsa0NszHlmV9OX3VDTs1+vrbRY=
+- hswm1xwIS3/4b5eOrI5bTMTFyxa1CxmrnI53ruqF+kU=
+- kT9LSEIRz2BfQB2EA50DBvZevb2FPRuME6NlIoEO2Dw=
+- GcwC8m30PMVxvJ7XsMTSkiSj7CKVKSIXJe920CHIMm8=
+- ohHv5luTREoCk3as5bKLG6pjsXkshsbjT0/QmwouLTY=
+- tmu3XVM1d6Ovf82/Gow/iL+7QJBE0LhgzuW5WpywgOE=
+- Hscz7rIHM756cekgpZOaeUiXP2MZqT9RlIfwi0c4Pu8=
+- aC14nS9ZpOe+xMIIqd2MYo4y6gjFu8EU65vEKcg3dNc=
+- deBg12M5hRn2NGNlXsyS7mRchpnaPMYgG2CTuegSZKY=
+- qZo8qyv+zbTCQPgNAAdRJw/MUns+YxFY8YzVbFVvbhk=
+- SoyftR22+PxHPVGsqGibfduppubXzpxlZsmvjgvD+18=
+- as104UqTCfOIxO89cK69PIHJM2maIJlVFzHsBS6loJE=
+- yA2s4zoO7t530ZTtlJd88maSotYBm0Jr1YfN6sjO/fk=
+- gfpo7IR1WhC9C+tSW+g2CIyPlPs/3Y2jpjnnQRQX8JE=
+- B1EFxpKdEtmyQN9VPVduQRNtbdeij0BVF6JW6ZlshoU=
+- BILuDZ3lbMN8Qma1CAvolWuRDWjplAjFU7QJgdepkew=
+- "/OGpC8Rz7PChIUV5cJmq4uOCbLsmDs4R6tegi2+SBqs="
+- xkI9k8ltsx79w7RbY3Rc5yjJbN5vFRiovKZZPrCU8AM=
+- aAewzeNukmz5IvLNMnBaJWF9ehvrPdtCw7KJV/g5QfQ=
+- 2K4suO7l6rjBHzs0OepKKkCR4GZGgngh7hlrdWikFM0=
+- apnXK65b9Rz8P6lkL9tIQAlJPXgs5NyJulYDAbmxFa4=
+- ZZxujRGHPbxnlLXzfhi+RLDrOsmTp5yut0CSZBLk/Vo=
+- nZkytSYLsMlj0V4tU6b2hgxxlq2VbCLBSUcX6/udx90=
+- hTjtrmYvwj14T8YgAehD6O0SgDgkCkvq9+Ecdwic2Vs=
+- NSn3rg4oRDGJMI8zh+k0EwkUgSEERhH+FzdH61+033Y=
+- fzA/L9myxwr6OWY2g8PnbEf2AiU1BIWqGAJOBsBGgws=
+- CToLCfOg5ZUQkx135WXIspPlTLydoWUYMF2L47vGPGU=
+- dWcrW6XxgLohM9S0NdvjSPOAzLBT/M9/3UV3qGeQU8c=
+- g51lF+wQTixwzh2h2GsdicX1R7Zmrc3YJEVsl1bH4mE=
+- rtfvnJNefXK2R167lWo6Hy/sgMzhhIpmpf6PIaTo7Zc=
+- 4PiVhy1lslKP7slzUKOiErPUq4h0jiXQIqNGQdM4IWs=
+- vKeIPLN5KNUv1sMt+gzFgz8Sl4smcIVPY3kNrFN2GHg=
+- eJIHPgTHcEGepilwdwqHFVsl5W6QGzX7zlV4ot45Jx4=
+- XpODKz+vDHQbQvGu0QDz3INmscCENU6Ea6POozb0Kho=
+- HbRnxpjWRwaqhkOmQNIfIUAKKUzXB+SgNlr/kQJEmNw=
+- EIvQdmv0Ktl/xoMKPwyV/kxMeQac5WTBNk+LdTjP/hw=
+- fHYfUCDVU+kCYpdGy5XdBb72YPVHbS01pxaNgh934ng=
+- ANhcKzqVKbIpEIIQpgDOw3SDU5TksrZKqtGxZJHe6cQ=
+- 3qAhuF3dPJ6aLXXQncysNl389m+bpleEAM2dO7pmsOs=
+- oxXw+PDxfRkQC/4xLsQw+9YfJVCvskMDhtz8vx0brNs=
+- NC5IkXTMhXnQOOqXaDsBD+6G3iwnTSour8tZWyE+ZD8=
+- kBIhJumAFJkndDw8Kz0KyCA+O639aYmRYiqJpn3B9/U=
+- aftT9QHDmlyXTohdI66ZKiYujhqjkuK+o05xMZDPJXw=
+- wXMj/AsbqwDdo3ZN7zLxQy0K8US/1ff4yH5xlpZDBVk=
+- dw7bSsFXEd3MJMkzDFWfdvTCOiAcXxPPjuzuuB/pLmc=
+- 4A1Sqwf0XB9J5crSpJJ2XaVJXC/iSzJ4DEokdRw7DU0=
+- fFqNGiLWppm0oJU0SLRK/kUbj3GEnEzwnV1Q/lvYkoE=
+- REosRdFEUyWsX0uqRvjInjzjrEcNa40oalmifHws3+s=
+- vtGSxj0IMYjfWeYuQzDE3ThxdCyrZHkql6q706YVmm0=
+- wJZkmSaq1AVUSMYSNZ9wfwrTTyCpuSPWOcl+ZXP0opc=
+- gokpb6uTfvm50mi8fMEFYlIgWlIXgptkuQX2s5mg7Tw=
+- Y+4k5RuvS6xFuMRbhNDdGg59dc/ZEu7gX3rSG/9O3NM=
+- C7gE9wuCIahrDXY0wpilt4BfoSMwm51V3IaDm3OoAy8=
+- J/EypIfpS6IehqjASmDewR7VDr+lV7VsYpuhX3sNhXI=
+- mpAEA6wxO6J6G8gfCTJlK4Ag2sksI02Y+gsGvwBA7P0=
+- x3Xnt1ft5jDNCqERO9ECZhqziCnKUqZCKreChi8mhkY=
+- I7XtKaHoQJ9wZE5E+uuuea5ocxjv1xnZryn4SWsBaoE=
+- KPARbvQr9xgySUbxPXh6HUEnSggzXVLugz1bV38Coyo=
+- YIX+4pl6U/4V8ZXZB1kCOOwfcXrfasf9TX7RN/kYkqo=
+- F3VjFevUe3EQNZ/HsWgXm/by3zZG/MiIvIqgXHizisE=
+- zwuFT1oX/a13PUYkONTXMociuBfUCnTsuNmtefmKolE=
+- IoWHf77EuxrWRmptNZbzo2lkbbA6AtT3SiYWpQ1Fegw=
+- 7vE5TuO4DZzhjr/mGKBW5uApnBLNGcmceW1ILrUid8Y=
+- 0tAup03iyfqx2ALblpwY1AmoZjqWl5d7scmMzdneQ3I=
+- 2GW2W9s7o3kfyVSdLjFfPfu6ASjbMDFskTeV99eegyM=
+- ejGAujPJEd9EaR2yXl4sg8XLCo0WZVKRNFuKzL3fhj0=
+- 8CyWNXSBlyRDouttKGHIA/p3kPt9AiWy9Utr66lk9K0=
+- 3/xQSqVTWbkmXL6+HkAy/mALZEda4/0pwH0jIjM00K8=
+- hNiYd/DUBB77a/kaFvAkjy/Vc+avBcGflr7bn4gveII=
+- EpRPIjM8BJAG4r2/ey6hL9xFacIfBE64xMz5tVij4fc=
+- 5KCpDlrAfVQ1xvJcTPfMVlvst5e7W4PFFbxCfvMqR3A=
+- kmD4iaA8PeWoBrgCr9zKMIUTMoqQxEmIlV2NwT3ZNQQ=
+- hNnEuElQa22PgHWpAA5+CiVL5xBg6oifrTyIOVmI9Pw=
+- pa1+bVIlrQDF8F3ba7OxWXqEPMkvbPGISQ/8uIoe9O8=
+- qoAvZU4656qhtz+HJAVqBeJpGszqjdkAV5FggPhNfpM=
+- dhnujOpJGH8wlhbjDs9UvgciWbQ3YPH1UKZElF1VcvI=
+- fo7qXMYJgCcMnOt1zowIfUjXJhEP09F5Ifd07v2OGNg=
+- m5esXnZ+U5PNDu0JqOUbbzd166OPeSWT6gFui6nplrA=
+- 6hWWChHRwx0SAfBD2G67ORTmpAUT3KO+crhh6eRjJZ8=
+- wKGu7bZaO6qbfMzSekdcrnh4TRvQIlcGy9huzYjOH40=
+- CRNkAlrICUnbxEE/pGe8IDXwlKIv/claZOgbELFEtOg=
+- iTWxDwVuzQk3BP3fb/KSi2RJUIUQCLbcH4Lg9mTr2pk=
+- ZNcuXBwWmPWocsxkyioL3rj15uE8ITAiuBDWFoKmlZ4=
+- gtJD6Ff3mxWNiwwKDmEOrUGqoLQfrvV3NRwOOHG9Gpg=
+- VDOLVgAU4KZM50dnxgwNE2gxdaZLa0Uzr+N4nJPEVHA=
+- "/TqqO2bvnuEBPGo2iENlOzIrglfEMUnZeeRLgqzQmBA="
+- XxBZ/wCMKUuFT0ToC/Ka93lHJef3mPkqMOgtgMTwz2I=
+- 9d7IcLax+Ybw/PKhjXzVuqFggUCGfZw211N1Q5s7j6M=
+- F/gHVGRNM6xoWwhCpAIimtu0P8kxL3vfNrokI3ofH/s=
+- vO3PJPf0OpmE5w+rj/BVj6Nzp2hMexoE6/KkgdedufQ=
+- r/ffvkYU4G8gp060X8/DuGGxmx24p7x7Chu5DcFXXI0=
+- 0nEpi9KH+GazblpapyAaMGbLyd0gAAzgpZLO7k9zeLc=
+- hZMmRZJcNFWxG299pMh/SgY+DxKOKm/5vFjm1lpkh1c=
+- A2XKwUG2PHpXAlT2J9xs8e9hhV0smzidcohXiNtPDbM=
+- fWNUbXnMVqG48A2nJ6Bg1OC0wGYIF4zMfYjO6eM0GxI=
+- sFya1JzwrTuUKYluKXq1CFMtMzRrh+H5OW3J9UC+tLw=
+- j8ApOWnaxXwlpYyC8vTEO/XNizf8twxNe/qAqPfLWyw=
+- ATmsb6H7HskLwV/l6xNCHzJXm86EmraXqj7zt3gjrhc=
+- Amxvm/lBA8jSb8NHsDTDOmY+fkp7RdS1PHqtBpmwSV4=
+- gKxpemYXs3YXwFhT5YoNPRbm7aTyHsoEWGsu6ryFbOk=
+- 1/TTzO56zT3X+tOsK+Kq6cRPTpt/uALXMTbUxTkgFAo=
+- UjqhjsuJLFH72+KMV+EKkkGeCnPokx5Xi5i6/8z5nN0=
+- dcIJcL71kX5G91NCp5PMSKJewYJcE3iYkZ8BY/lIdrY=
+- XnN/iR2xF1RCo5/ec+UdeBpUVQbXHJVHem3rWYi9f5o=
+- GGXk+b6WOfx219TajuJeqnQh9oN6zLZ1FpN+aUaep+Q=
+- SBZsfBke6GxbAio+xyjPCabEiZg1cLrKVdHHaOsBuYw=
+- uEbpy34y23SBg8IhbnN+voPa0O5PUECPNDlDjvSdaIU=
+- 8nXJNpLw4g2loNN7TdC31vaclOu6YiFWVhYp/p/eBis=
+- 9tvT113uTrUMVW4qMERsDoBsyhYaz+unCLXm/titYzo=
+- 6rwcOFvgspXsnWbOW1NYSUYUFbS5XyxXhEfa17MKIlI=
+- ieAVNqwgcnlAnU3h5SU+AfShdp5pbbDWBiypuPVnZ8g=
+- i1VUUrFMjTVwYIlkoc5AZClzF0YhHUXdPJLCH7NcsDk=
+- 1hMMf8d6ou+0rNQ/zLeHl5tbV16oIMC6DY11r0yt1As=
+- Yn8fr0xcCxHFKKsWMjTIxP/Ri+ofrxXccNS74dzT+tg=
+- s9F+u+Tyt10ntjCc+q4Uh7ZnMBpzlR59UjoDnNLf4RA=
+- Om4p58jUz5lucKn5XgVeGCoawvp0iCHkiFnCVHr0qGs=
+- d3Uk8M+ceSWW6ys8V4AdvTe2mZkQ1+aTkiqyXJGT+qk=
+- MAyy16UnDd/M13n0bHvJg5229s9vCCAL6TQHYi0VmVQ=
+- SloMU7P99MoCwTZocmqJzZhMSLvVAMwJrbJfvd39U3Y=
+- zjpZhofI0uWqa+2tIOBZtKeMygra1+VjsHmY1c0ia4w=
+- rbpsDsio2J77A95kJCegkwL6T3R0mJ7PM/1UWjDS/1s=
+- ZVhP2wXU1wx5jDTHgQ42BSasqon8JGFtuZ3p4cP6cuY=
+- jltXt7YgkA4T3YSq4jkN6g7qGZqn+NNPR7coJCduk/c=
+- r27pkQZ1hpZAZSDKKsfZ6ZJaMC4X7H+94RQUlbUlb8k=
+- 7AQuUs8+tn9DI2qsX5miHSwVf3MY8O8mETUiwWDoP4k=
+- E6phJ+973KKWv+tukyRhlMRCQhn9/6u+XibAsPJe1+g=
+- bXQT02HBxQCbfOMws8X/V3YVV716c4cMDdRkU9LbSsc=
+- ud2iZNg8gvnHHxbhEocNguRWG4jJ31UwYNUcdMQHAVk=
+- JAb0blBsFLWpNnm+rPmon2h4UhpERFWGZybXpOqfb+k=
+- wco7fa2w+gWv+bUaPicrLmVItw6s2V5oTxG5Qkz6hrA=
+- o2DvFt1jdv81k5V+CUPokBZ7MKhGR1Cc8BWvDMt4qKc=
+- wZEi8puAtn/Ld2inXrjE5rlcR60+Mls4mwO8sPhnzC4=
+- qWoZxE1t7Tj0MepWepk8IcicFoDp91kKo6Bz/pQ1xDg=
+- yhn5SwrZp+ndrRWjewsGVsOVe/G6goZLp4eryn04XyQ=
+- Eq1KPvM5ZGhdVWOOlX20VvDYmmRQYcasN1EfbAYNciU=
+- vyy1imj2hNlaO3jvj2Ycmk5bCegsyPnMiMzpBSjK6yc=
+- cLYUT65NTBY8biBfAuNH9VO4DgMEOyer5m5TkkfXR9E=
+- fEpcJCtyoj2Zm7KAHGVEo2j0mjq4ae9XjhcQebRKmtg=
+- FQ8OkdRmgrwRXPW+DP0jInRHP0LdCyuTqF8N6z0Ovp8=
+- My0ep6/ymu42ZGw8cpKrXzxCw7mqHw6iWzEqXGtK2Ag=
+- ll9puu+2AobGAmK0Dc9AcXoiJ+712wDJtxfV3iRFNRE=
+- lSNPAntdpaCZKL3l/EMqih5SVVnKsIuDDovf90qduTo=
+- qj0v5PbTAdvWuPstL937eu6/O+xT//9LOaCWevqIxgk=
+- 8bPjYGgGx0OgeIYjqvxdHoLkNEhwoG8c0kVmI+/NHtA=
+- Rx2UEBPUF8Jmgnlee2AridB0JSwFAPEQVjeVwle5qVs=
+- bV1F1X688wQtoQRki6fN+/uBTxSO57rUhTNr4dV3OSw=
+- VF8HApXSYl6K+IbWVtbx5hoJzMoDrm2ygwTqHEGAOU8=
+- X1skrWVTFSXdzMrOBZja+qOG4wdJur8Sx7DNoq9FxYI=
+- nh80Hf+RYbadavxUFA6jDkRnbtNgDcmPUH8GeP5k4yA=
+- 2tCuKpCv6l5L20zJn6X+EmvS7eOx50uMaUp8eNK4b+Y=
+- WRLVWQzu3WFyTuINN7UVQnkWyRUIG8ytKeDGhEdgFMQ=
+- XOX9Wjq5Usj5/r1hcSaoHcSis18Hd0eNVD5LPrK/mMQ=
+- AaDGa1Rf7vz/Eb00pesW2MO3nIDXe24k3j3veZcNbDE=
+- QKE7dI7MJ7OL3iNaazSF/7V8F9KtHH9uAWitvOM6h3E=
+- tcxe2f6P66pD5CmEA/F6Am+CVeHiFytDR6k7b6On+/8=
+- y4stx3amY6WekwcN3NZFZ359TMTmCreKjQmw5xBTJkc=
+- 1niManhyL2/7ViBCFHa30LIjFUbtjwOfChnMOFRu9yc=
+- 8NE1GuAYMUwwUMKh1hJ4AdDCjnMGRttO732O/SlGGvk=
+- TL46zrNaPDCIoBiE6N/TJ6uyA1lMxyq0p0++YdV/gKU=
+- 0f4LPJBWodQdzbC3rtvF7T/285k1gKYhAyth3yZakKk=
+- "/HZHvucg75QBOp/wPZPXvcIx6qBy9zEuJ1WP7PXd5Sw="
+- x4GuzhupxsENWAGffInwKhpynZBjfskb2xqNHYSjFF0=
+- 3ADJA4UrsZ6yUK66BeU0ptIRYp130FUDOAa3g7rgmTc=
+- NYDJJnUYgH0UgDq2gh+gZ7TboSEKQUiiQ7npODuuah4=
+- UeTmUAM0z5SBmwGjYKnPk9myxcT/DQOqNVZUCGMaFyE=
+- 8bILM2lMgg9dYezmj00WXLU0i9bp/F22QZyRdqtBxKo=
+- qdTQef3V/i7GigwRj0DWlgt2oAMGGbZgjOt4V4lNNoA=
+- Ec6BiaR9QMii9WpvcGRPWra7ciG0SW5WQnlw7pCcXxk=
+- a2yw6gWgsNrO3iS26IfK2Gv9caPkqJfluFym/l5Cnnw=
+- RcTEkr4e89uuKoiVcENG08Ny79E2C0ZHkAotAECGs+0=
+- "/+wrBIKKlPCNKLHZO9lwLw4zlZMMw/NZPRGCvu3CNWE="
+- SrnzmOdnKNqFRiPvZS+icU56b63iMlvagpJDHpkyHFc=
+- JHn4D2R8LJGJsD+ri2UjydAAfKOQfUSu95MQqKDbvqc=
+- wENKfVAmHlccmD/h0JHyYUA2HNF2U6vrckKY1hoRgnY=
+- U+XnxaiEiTc5xM9kUoVyrrhG+OTKGYYYgodHXGWQD6s=
+- Nq5wSTa3BYiLQHI6x/8Rs8AYG4P4bsKg76kaFNYBCCw=
+- 4yrDHoTpVMTvMPemeZlIzfCPMOhd5QXiNxVcmycmWqU=
+- pJsdG1ipS+FmXLcrPpUxX0KmEfH/LKcX/iIVTT7M53o=
+- VNwLVJIWb6CFIK/XHAMPOYvXLvkNu3O8LgVjY+wBtUA=
+- G0Ahta1iX4/q4HzjnXfH1vARpzx1O7dLmfO5j6DI/F0=
+- kVSyb0KUUdJD9x9C/JHtIVmwPS+hO0kWrCmesi6qL1o=
+- hPUOiY956uAkPWbHECxdmZm2UL38RCsEv8TZw1RHCuw=
+- ocel7KGEwDFqoXD6L5smN45iQBiVWet/T/Ch5Zljjgk=
+- UJheFjmS8tBOTawpb2wxt9quQdZaOe7GE4cj0dBDCzA=
+- lnUgriPo7hSIi65ygJAxuYOYrkpjZ3Phj/+RfXdnkzQ=
+- W4hMb7IAF2Si0FR6feWUcDPvZ394E79rnAC2d7PotQ8=
+- S481OInZoF0XlG4m0BTv6ZQHy6i9nQEC1KqxDOYikEM=
+- SYRTBCXtOdhZQXiGYeC+Flfy8shL++kLZeTXHfl/6Ck=
+- h2BmO7Jfp581wsFPuca8vkRKjJOEkc0k/mhKcFbJ28Q=
+- s3DeFOlBQtShCKed9tDiZaC6P6LhD1fEs6iSt0yfhKo=
+- 9RU9Rn2oxRAHJRS18cm+GhuKDcfC7pitinXIZRzxE1U=
+- Px08QBeNWFjOouMWsGkqajzGJi3wIIGARpGvrDceSu0=
+- OkVz7uFRZgJn68FnRPVZv4mvgcE18VqOK2a/VXZfWuo=
+- xXRa8vR3zNV0OLTKVNVqzQD3qFlTC7g2N2Os8GnZr3g=
+- xPZ0LJ4ZYl+CCkSh9SaTpMNIO6Icoi/A/gnnPH2kINU=
+- PPLPGi7eQBSyHomQ3QzjDe/U5Kl68xbOfWJFPSI3XCA=
+- Zy6Fe/K7REjER/hUiqHsotu/R4+tem+uY7sJ5uSKVrs=
+- cVTG7rbNa3MwMXWf41qB943mBWhZ0vQ2PM7mP+/Hz/k=
+- CUDQEt7eIpGpueL6v8wY7qa8c76JbnZXwnOa4JRrwDE=
+- KyUL9aGMVlNg9F7Kie1P9RnJitWIbBY7bjxbSnWoQPk=
+- xSW/ubZ0FV59TCedXAYfJiizEeA626U4s3rjDyU8kTs=
+- 7Yub4ghC+T74tqsuF0/Qsp+Ol2XXZSmlBhLbce0eQvw=
+- vmyQb3FjDS96M3DUh/mzgtg63YtIn1wlDHYStLaPKAM=
+- fRKMTFNsKpqSvELYGWpGlN0wz3XjJHa+PtGjqSXGMeQ=
+- MaLVKCaD7bOiLFZfGZqpb7n/sxB681qtku4c1WfPwl0=
+- DkNlCxSOFVfe8h73rhbr2PfCHM+mduDZ9k4EJoGFWXA=
+- FWcXjWdW0Dv2ljIRW39Av2Ib6SsNgTZU3hRYZRnrXto=
+- z4w+P3GutQinRVc8fXCbuuCeAOdcau1beIiIVRenJnw=
+- Gv2olzenRfFdQoB9VPZ8gDcn11zkQ7DzpllTGziuZg8=
+- VgEeP2M5b2EuNU05InvdH7h7uqWKauKTytHX8Fwl5hc=
+- 2FSo2X7CUeh7oB6yc46/HxQUiikLdj0c43F94la5MhY=
+- mxvYGkNBYm8szfvqK/HqwTNcmwATF4JAQ61U1SBzC0E=
+- 2sluqhWZcjS/qgplHbFozNhLyLX1nFuwJ6eLbBZS9IU=
+- Pu8M0Ub2MmzOYwvB4+NVEgYLYvM9pZb+0V4EFhVw/CI=
+- ybjgZ2UfjKDdelJ6MYMNOvR6sk7q1GDYetqfMiOEvlE=
+- IuFfYTxYFKWMvtheKcyxQBYxIKIHjYH9zI4N2twYKXo=
+- DJbLS+8fhZEKjBohwdWrjdmINVtd8cGai8FnjWZjytw=
+- U9RTsMCLaziukVFdyI0l++zdHWAB8CJBlinfhE+LpDM=
+- Hu/3oH8++SZ+ZWpumosz9HmE+MdJ/vqnrdSn1uHAjSo=
+- YFR7tBIQwHhEAORq0Zqozn7gi+j2rIFT5HtPYCtzPGo=
+- PJ+iHVXQgUjzGSAP5SUfcurYTHgAfk0IRt4SVD9KnWA=
+- w3/aRy/+YjItY7px/kiWzplQnc1Ks8OaDbIce0cC/sY=
+- qlk0P567NU+1zf8Q6uf6cloLHi72uOo2DZWHP6KN+Kk=
+- SxeYEHkaGa87/q+9zwrKJ02ax9XwY7jgt1GJNgwM86M=
+- U797WjUeleOZxI7z0m/SLAtieor7kVOV5hw4rNM5vlM=
+- AI3VUMIV4HwrPMVd3Huo/rgSzKnDjjVSPvavgw3amdI=
+- v6Eguf4uZoKRFEQm2gzVxw41zSue0u+9iwqjvIYWLps=
+- RET1pPpC/42s4/pYX0iAKC+M0JElDwxGeUdYyts0sdE=
+- RUGXuomS2T+T/UV077a7wS414KRBe7V6pRDNPpLDCls=
+- qP7cxZ063p1LVQJa/VSCXsjaeYIlood4gIH0NJcwxQY=
+- eWVlKB8GJfTka12K/UrwXrZ/tWt4bqbAatIg2wLr0Gc=
+- zuKyWzZZ+dVlL3UONV3E7sa3aLcr7tW8hlbTMcDtCNY=
+- 2fuS47vmW+HxqtSoLu9FZ/eh6+LNEQyASblpi+enDIg=
+- 5vc/GhW5dXhigSOIIxcGzTrQ/DWHeycCdb7zT/hY+jY=
+- eogur8jrwTPU4XWZJZJKMgqmfxSu5m755CqGT/OukI0=
+- eVhH72osO+TI66gB58duyX4DlFVxwmobbLaTPuNtVxw=
+- XrtJ5JmmYT6DLkM6JyLt0NKUfVb9tNaErw8GxjH99jM=
+- ybqtDBfv11bKj/2+8YLWxna2WPG9VdtyfdcDw4EBZKY=
+- Rd2tsmZti14Ys167gvdb15D9T+ewJe19XWfECatGb4k=
+- icxNlVM+86iNZjxC9Tq2Hjf9N76Bm8RimQSak1vwtbE=
+- YoYmBMVCoyltUGzxqVQZ8Wf1KAH+U0CK0+qK2wDyVDE=
+- 3N8SUegRz53AKT28GJkUKlwi1ZNLb/aj+3tze5TTaY4=
+- 5RTIP/bTJM/FR3fOB++jlpfVvmAH9LttIfKjeJrBpGE=
+- CoYFD7N6Te82iF2pVX9bIqnhkXZ6gOekokFUEKRGK2g=
+- RrR19NPV2Gs1PZlF/9bJNfI+mjSb5RVW8k95hQlodPs=
+- hC3nsjnw1qsX3AjT/P5owJCprw61KFz7xUM7MwTF6+4=
+- 6wNLQdJ/7sPO6u+BOoaMp+XpZaqCRWDofWvtAjy2aUo=
+- m8bR1j71W5tYF1BjG9CrYAbq0MCTZHbLq6KNsrYXKi8=
+- GCIjbTijmO5+67UeTq2Agl9qKiVoOAhANuxCPCSJAYo=
+- BGOY9aAN07Gqo2Tb7TiYxibqm8EqI5oNXCFDX4PGVLE=
+- "+GV7xl8f8BCOs3Ru7LGli76ZoxLfVy9KYRjxQrVcDLs="
+- 9OmUcwCoS0y7JBC4kA20pLEz7DX3PizH894dhN9f6C4=
+- TNdLjkTXJvKZCQrGx3SDcop2KBzM2O6hnRS7fcIbX5k=
+- pxp8cBH1OhurNkLsLOElk/BSMKzo3h4+dkX2nvrBRD0=
+- m9mqDfAL4VwwAt76DQja0KtneyQWqqctu80t/zO90tA=
+- fuFi8hPOI0IqlSnWuF9nKZkS3xiF/oAu6OYJAsZ7HzI=
+- EGKM/ENPuH8x1nXTfgQCwtgkz+g5Ov96Ye5XqqfZCcM=
+- Hgnl3Ko0YwSrrsQWSkFXCmeBsTDc7O03j+Lo37uQAp0=
+- Nc4HbDxmwza/J311qHFPqwa+8ioWYZamA22sG/ggrJw=
+- 32NcyeQsRY5LtWH0EBNj5t0doZJPFcQCjEGhGnZsEcs=
+- yDS+xUHwaHPRQkgilgkXpZaFLtQqtb3wJ80CZeu/nek=
+- L8WS0RsdlQYFLvq2RWfLmPPyfurv0RW8w1myBs5nNdk=
+- 34UhxMyUNSSLPLQuFefN9CkTg3iAYY4CKlylR09ucEM=
+- VJy8/3ott/z8Q5W2vGyyXvkRGOz7ZJCJZ1B1yOHnJD4=
+- W6V6Mqk004CXtM00HtQYsxApOiKewaaZxR5pKlqas2I=
+- Fr7f2mC8hsWpjaDp2NxX+7+9p2sX3kyaZlTapfJa72o=
+- 4EV7Gz5OwgsrBk/0mOxrqncIGeMiJwYgdaMJlsYylLc=
+- "+M6FWRwx+flJohDonhM3upAnRdo4NWgtEU9VunIJlck="
+- IVL2N3qphB9b6qIav9iGp7k47XoD1tOqilJ1OAcg3eE=
+- C/fLj2udLqfC4BRAcNwMgzaW5s5Q3HoOaI48qCxl4uk=
+- Mm+IKgLQGS6RvzIRAM/vaQgHedabVnQutOUzccznmxI=
+- 5hySbV/rO41N5HciDXz59rKfAFfI3vaYOXXjGFDYJbE=
+- 6pKmEnvzR2O4+4PJoMGswcBYhbBJB0p60votS9+jaF0=
+- 6qXBXurwc9CHTw1LI8XBeqY0vaMbgH2j7a1BN7wVm+M=
+- gbAnyPKkhmE8tMCa6T8PKpI/Dh/VQfA0/GWkAqdjxZE=
+- 3CK4aOQxLw1yr/l3HOSbAVz1GqZCKvnxNfdFogqfWso=
+- fo1E6hmlog9gVbEFmYoS3ZZrNrLs3K7wS+rmUMDKPYk=
+- VDN72pNuJ+BaV8Nl2BilJBmY4U3f5o4DWwXGqWLrzgU=
+- Q0KtkOjxfat+oxcCokD+NStgxvjOIRwciC/oooeCg/8=
+- 8JkNyKD3D/tTdcWTY1mCb4ZGedMRnvBI3Rs1Ei1S3i8=
+- 6IHam+jXpjS07QMT/pFbsPqV1LjJRuUNdIDjsuwtFmo=
+- s42dFow67fFW9PJJuBra70tzh5BRBXP1e1Asygw18W8=
+- dy8Wbz16yyDlnKXDTXBrAaLkXjYEQ29gw0lWL/K1Kqc=
+- X/x9/Iduyu6VCB0hKQWQfnIGdwWUBAvrBYh6+gvvLHc=
+- Nuz5Ezzzu3lj1T8w/zAMnzdse6Eu76NBEUVjUYvqjsU=
+- kg2exLBUFV0Y724DAW8gi6fOWwAWxuQZHypIo21tW74=
+- liiOz6jo5IahGE+UnLH5flF+v/TLXVJWGCduWZeCz80=
+- 5WaUvuMITr4cL9lBg4+2XNLkUk4dkJ1U0nVgAE3yMZg=
+- 42ON1S0wDBOT8174Ve0ArjXma0isyKj7/Gtd+6M1WTs=
+- kLCLnQSRFLoJ+Jh7K4DIvoeE7l5LUrh673R/tCh2UuY=
+- q9S1CeIS/9QPDMC8db0niA1jOpNK1k9MHWWNyTTBM3U=
+- GtzYHw4//kpu2vWp2Rs0JzlV7paiUU1d6VBJZpUQXiE=
+- OQb24e9x93J+LjE30vG3x+LBbNK9ivzjABnhy9k5jTI=
+- j8Cqo6r+jwUXHCYrMVfFFnRTRsy3g+EQnwBqfTTSPAo=
+- TRPh2+erP5D3z59JvrT6erxWJJZ9bbebnmHAIsBl0IM=
+- XcKlx5NcHFDgopePtGX222GQTMK71jSz5vLXS5IhtBA=
+- GnnR3R1o0UkdiMQLiAw2/QSIIE8aQZqH4NpkpXe0FhA=
+- 3c1PL/KQ/Qu3MlNCSeaDIkMZ5YPAznsM7msZMULyDcI=
+- 6Py86ugKOWFHNHwwUabM4IhORX6QkpaZ1N5XE/8ofSQ=
+- AKuZ2XGuvu98eyHKFVUhTbtqcIwBRrMY8KiUqJtLqNw=
+- f1dB+9k0gfQiql0Dc8ixwLzn1Ln6kAvECsj8YkAR6Y0=
+- fTFrk95f9OaTZXCNh6uPm3mEAfdEQs8SpwnQtCIS4c0=
+- IlxxG+n6PAMCKTVJ6j6YMTAo4OkjLjIWaFWacJX2PIg=
+- S1Kaw3W0IXvhf+8aSm8WJBhcyZkJ6SJ4wHWeEqs9Yfo=
+- c9Uxk1VWWQKn2zDGbJXJZD4JKne335lkP6RNW/pL9G0=
+- 81fQJv0cY70xxJX6FLcsQIu/LTBdXgnL2AOXZzsEZXs=
+- pHPZBKUU4Sh+cft4vYfYExGZBU9GNzCa1DkW9HS0owA=
+- "/SVrNqz/D+AD5lo9UY8jlyNA/WhHlqS+783rRw17i7c="
+- Ta7+jOCRFtZvwW3Q8Ibi1F0aEMKmquic9PByRzqLxtU=
+- oDHtonOsGIYdETJK+ICFlSdAW01HWprFibxMi/1ZiJM=
+- cQm4Q1O7Mdk1OR2SlP5xFV2eYY4Sc7LxUx6RIxhhD2A=
+- KK2bGZPUf99Kit58lWb1IwhPJ1AlRpDjIIcZdKwi3Yg=
+- FQHdE0ceQn+uBu0A7fVJl8XdfZYo4uo9a1nDNURMYfE=
+- aHTqMdvglhnXdkQyO6ZMN51MWPOxDmFcOVjmvhAh/QE=
+- 4oeB38LDHjYlfvt+qPHTkj3tq+dKVmuQj6w4KSoM6wQ=
+- A649aPzX5+jyn940FO4Yg8ksdkhz0rQ8LazPos1qFbE=
+- KVWAQa2o5IAzaKblFXXdvJIXchaU4TZ+ro1713eMn7Q=
+- "/7UdGNiCVf1VTjJAB+JQPRUIMM3/+YlpfULe75dLIiU="
+- bv9mbodluNFwdxCAJqry+lJDfpoWu3fpR08qLBRyGlk=
+- OqEKzO9Nj18O2irJuSsWbZQ3jg+EbU11IBhwyMG9/hw=
+- uCLxzS3PxoW0foPjmAKJ/V2OP/OoLe8k19HWi7Jy6zI=
+- cUMUjeGVsDefN52EqIUfm4SOxRVRGJZSgff+qSNIKyw=
+- e9nKenVhFeq9/yqyge6djCL0S1HZemgBFp1l2Q/xYyc=
+- SzNdR1lLacnRYqKEdtHn5vYc8Caw8+Z9UPp64+U0qHY=
+- crVa4yiFaiLHwcCm050JEaIdVlOr7ObwFgkHzYhnFKM=
+- Sxp0HWeMMwTOwNlDysUc51+Hi0P4RPWgtcvHpNWhMjM=
+- K72+34GcfqvNRAE4x4uqrz8MRUGKTbRvk+6xfxlB3Ag=
+- c8Y9FsZo6cshES0W/RoGoG7tazcTDek5QAI46PJnA+A=
+- UCSvKDVHbBubQ6hSaFxpZRtoC3XNQ7C4XlyDQDxyZis=
+- k2oYXKqiZrucvpgengXLeM1zKwsygOuURBK7b4+PB68=
+- FUIm89QIYOX4JqnCanpmzT+kTniUqCJfzxD4AGDPz2M=
+- qgoTvPt4dRw1iLTdFhrrYIFSnThRU0iUhlMNIqaJ0RI=
+- Dy0f3roA7J9virN+z5F2dH1g+1CaeKOcT6Xxg+ADmYQ=
+- FhLvykx3bDfxC5E5Y4VnrOoWLgHRcairCHzij0keIxE=
+- G1ulYEcxy7AwMltMLjSYzK5pzWKBuLti8RFlJHLDXbU=
+- KN0uFezLnR4USVv3XqaaYSIHu9WrqdJrQseWbMS0z6Y=
+- poNJVhOW7CZKNQhHAkpFIdAL6qM1hmDCcJqA8xx6zdA=
+- smtwMKuzYinOgQPNbJ5Lvmihn/oCqPXAoW7ZnlMDFXk=
+- CDTC1gclrFkCJXs7eN0WGtJtHAKQ2/HkfMFK3VuMgUI=
+- vFTDQJBoXiuv9QGzPNUyKU039Sk036YHfjXs2gxL3OM=
+- jgqR7/0jHjCtgNJn3WZI4ZkaBe5CPom8LmB3seCYKnA=
+- 6pDV4OUM0SkBWwWvEMfriFBFMe61HZzvs0l88U2bGGg=
+- colCSUy3DR16ALUHrHkBjTXkPy5uUvESgl4DdwxLYCI=
+- yo8WiNxONAA1IHD4JAAqGMnzBuihRsMbR+KpL9qWd2w=
+- W5VDi99JJnSI+VJbjMPJVug2hQMYlQyZP5hDVMSLGBM=
+- y3SwEcrLPaVR+S2pUNG+8pRTNX7hrKe0RHShszeb8RI=
+- DDzjEh8nl5rUu9m6aaWK8aSd9hpYwozOB6mpdSrxNcM=
+- cYyK3KNxvEfgHtnpsEumjOLukUYY2HlBLltYTthQwXo=
+- ofV4h3wYSuhuzB5x/tCHskGV/KcJNafMr6LPN8vrWjo=
+- FVTG/BFqty8fZkjrq5+CNXO2IIn6gnIt3RZ2rbOeCCE=
+- nm75ADWuKm2GFJ6LNnKMW52ISynOBDY20PLU2AED5bc=
+- QlDA8aIAJ6ChsF1FVzJrLBPdcbnqxD+u7P1mQ4IgvsA=
+- 6rEblPRwDBHaPiZ1ngWbJkpXqhu2JhzRFLC9CNWFFUg=
+- 5oIbgukW2C/CTks+OzgiLAg0gwDfnQGOKRyP8qh3JBY=
+- NPRMxpJAX8KWgN4T6KCnJnCpKB9RFEX+/esEv0wBjPA=
+- DsrTbqaL5xz/1dqdWRKwc2mXAYCGGw0NhdQN4rksD1s=
+- OqSwMy7Ah5cCHb4cc7hoY+QzegfemHGEVi7q5V8vcZw=
+- ORcYgA4YwZXE9422ej3Qy0jGA60bkr+TQc+97eQemIg=
+- H87cMTsQJC0d8wIdRx+CPa+aC0mh1DDFNENvVT/8X7s=
+- vopfEcZ5E4WKohP8E9489PcNr4KAWLC3aycGT7jaSiI=
+- "/BHW8o5Z08wzwLFM62RL8JAuvWPWEhjf/p59rHwlRUI="
+- MVAv9MaZTB1mdioGEDJb6PWiAsR3rIOWYLdAeCh4UWo=
+- CJVzW77c8ySAAHn+VdD2yYJlZzgVOL9/nCc50PjNsH8=
+- GG2qKX3YQOSTHrvtmlncdcm0f28h9Yc1K6+qNJ05BMU=
+- 9nUdwr4wo60gFPD82wS48cKm6IwUA7IXd6DdoBg0XAY=
+- u1Oz3xfAHX6C1YAKcnYn1VP4rczfURRIkRObfZxEneA=
+- uwPgxK+ZGvEeiXb1rLTSBgLOakoTSwUXXPyrO4bEoGk=
+- 17/23yOO1mLqNPCP5KQL/nqnpUhgdkHtY/CP6B1b++Y=
+- iqML4LOGiui7FP7+B5NiCF73upGC/9kvr6u4A8mKRK8=
+- HZluAz1hLZrytEtwBh7g6Gi/0Uwt2QsSnh7et5U+eYU=
+- 5mH0yTXopagzSa+140dpXC6XLpZ7UO/NYY+TsLe0wks=
+- M82JFjR9JXZ98OAFeM9c4o9Xj8PP0suMXYjfEhnWQ/o=
+- 7Z10Og5E/Zf5OtR6y1Qv5JDOYJkrdjpKgl034/nKY6c=
+- oKRO2M/DK35hvv65m7/3cGgIw/5NzfR1Cords//NQAg=
+- DAri2cI57UsEfW62IcO/TMfteNo7bnxhh+ORGYh1MUY=
+- bS/jLcQknvfnNZxth0//u/M16DLkmiaBI24baGr3h5Q=
+- oHoz6xxZ3iB/zL9qS4+xFf29nVFatvys71vA6gDVd/0=
+- B6X94HiZPxbrqJFvLwSo7ksMl6iiKAc+gND1qnhXZ+M=
+- YOF9tCRBV1H3HdQ9E4GcX5XvU04AdzpvVthV/T1d3XU=
+- F5saPp4xnP8MpmceB6YtrX0Iew8qie7wICYSw8tGuvk=
+- yeUxMOPLolIYTUSibMOG+so/NgC7oIWPYOXuUNTAV9I=
+- Gecvpl/E7qThoFfP2z7iaL0JmkABZPEdyBjlvSqbiVE=
+- gjCeQRWm02/t6tGOGJ95LJ4kMCsNcsAtGcYXemS49h0=
+- FUtvftGwcyfj1MoOarjmYte2s8K5ItaLaKJ08TzZlMo=
+- g1PunVucIJSiCxNRRLtj3CGkXQi9eBZcEuKV9/358dI=
+- LvpTJoe9NdSexG9OAQxiVCz5CsrhaildsSZs5yjsQco=
+- VemKHlU9vAhLCiS8izbiGErXHwBsnu3KUpzlzFZHjtY=
+- KIllofLIg8cb/4pLOht2zHfRHmX3CRDV/v9BGk5f4bM=
+- "/aeZDTENIL/Lxw0oDe/f8rOnSTwQIOqz4nA0qC4R9PQ="
+- sxw1jSHz48GlXX2quQxPOuFszF2HfYL0gZww6tQPeQo=
+- EpimO5DcEJffnI0tyv+tGxPNM873Ydree2hBjtR0KA4=
+- zb7O0yMheP4+TDRLT8XuK1c6GepNi8R4nIB1f23Sx/g=
+- kOAzpkCLaRgZAORvOLmgC08W95e2XFZsVMpwp2jvVW4=
+- lfsNu3D4UFsd7fCqG8p1zdGSw5daBPMN6QwmoRVr7MU=
+- F3ynD0Le8SOONtoylHMmPtP+rdFAlMB5oiML4Bk0NvU=
+- UEVWRTxCjhcXIK0DCBIcnfGZO10eoiD7sOXiOHkp5Do=
+- zC627GOLT4BIUXQ4I4QxCFXfTet0M7ghAZLFICk/hxY=
+- rTzsaSA+63BCOp73ZgewRFGoZOhwXmkeH14doA7xcu8=
+- "+PlsTAU64WVhzDVKpJQxp0q6m6v/WliL/4ZdnoZY8q8="
+- qtrxjGOh6XMLiBLfrQW3rY/R3pOGaG/CwfQsTRuvpL4=
+- RKtsiL6DsySC8U8FwRX/Aeu5I/ts4bbCunY3Le6G6aw=
+- RHgjbxVl/Sbbaw6DmSGWgGDWNGs5xkQRTpjw17125ew=
+- CA3F7Qk85K2/MBHrPLSPaN1ITj4IsCC2q8qUMHAHd6g=
+- XMe5YKwcjvwfqeQYRIbsil9Xi9qKCs2Z1qkUcjVXCnI=
+- WUJroIrFOip/YldhbW1WSlb6SCKNZzEIZUD/f2WmocI=
+- EMjGNNQyMpOYN5BVAOi0OqHXX4oyEOY/nXw/3KVTgoA=
+- xFslzrv+iLHl+h/hY1x7ZDXAe7sqWTzurbgBCq0oQqQ=
+- r765G1r0DHtlMScKftPJzewlVASV+W8zHZb5dOQKNNM=
+- i4B6oFBaALPvSeJqKt6OMfzWxwDRo67ulxtJ1z2o6P8=
+- SMqvtoWDk2r9DXinv9cEbSSS+tlPPEhZFfdLtgEoYg0=
+- 8dLG7MIPFY5bsRgObcS+ci52qum9whT7YpqLomFMAYA=
+- 9rFEmDA3due8Exz3LywtHpYy/hW6D6pIPFT8mhdHV7A=
+- zJIAZJThZqOx5SVd6FteYcYlTKtNUaXRmCwtaRLf5CI=
+- 651KlkvIpxRalZbNBfp5+IobkU+HrT66V8ghzbVJJA0=
+- fuytuXqAbUCJddfqG+st7tbr+EH5YhkJUs9Yj9ESIYo=
+- Y4TkDGyAO92unZNI85dHpQPP4u3MLrHL39h+Blz7W/w=
+- nAVjbofyfk9Ot1sFTY+x/L57FyIqDlpNI4NOef5TSUM=
+- szY+l5CEakl2uErSS730aSkrmax6iohkjGF7exbaBYw=
+- cjmTYdpqd1T+yYbcpbfLrxyBCije1KuvVrIQbQbLeLA=
+- 8So4g425f3dnxh05IvoHNlbkB/ANjcczfltdCwCSIdo=
+- L8c8P6XME2Svv9rduxItWDaf3Fg+rlh3gx8Rt6bc2po=
+- 6cEPdSqIc+ymLWbcMWch1dzMtV3QyIvZZxVaqJ6iDsw=
+- Tcbwr7YhRrrOAw4wDmNECtkGIMOF6yPqhZoCOGfG09w=
+- d1RMi5NOm/o0qUnkQ4zWPamSCG3xqN0+zrvWin+ERy8=
+- Ur+3cP9MFT5hoJrzcr9ivBGjKs4WDAYHNjJBUPzLE9Y=
+- GAn3zQx1rPNPVtjBl4K5nGtfzRQSijzHmso4pPlK8/8=
+- G828z/HD064oeQXghQ5q+7VgECdvCppSrh/3h07zVyY=
+- Z0dNx8A8GDZCfYaDgEydct8W4BuqZnjYJKldXW9/Hy0=
+- 4MYdpjoAHPyQRtitUX47jXQPPnXSV3FIvEk8JbQuqmM=
+- 708yiY0AL3u8X2YEuy3PIb1pUxYzbGGvzMZ42pkCo9g=
+- lq4Ph1vZBi1eJjRzWQh3+C1lsWBXHg1DhcZdmj48CB8=
+- B4Y+emohVLqGSnGwgYxe0y94mZQwJUsuJy1GH80NNOs=
+- byK2TsB0SWYqGzOi0Jxn/+/ixvRUVGD6M8dI6WTFJik=
+- QPSj4iaMKnpp5utXb06lMXYkwq0wIgOBpbG6jrN4o/U=
+- cj41NPjifW7UZKK7tHGxgXuzeQj3OnAC8rgMsKPJXos=
+- GVWP5u1aOIvtdRgh8LdU8rhqdqsyH21VaRZPtSPabTo=
+- uFEjvFctlibMrvNbg0pglk/3nd+5lobYiKwFS7ybk7c=
+- c+TFFFbCW6tUWN9xlpPo3Xymsj4hS6LvEUL38psz6ZU=
+- Owz9ZBAFjDgC58AuctOL6Vee3RwoEF0BR24USMTSktE=
+- xkBrBr99eg+CtjfBbAAQAsCFUrSOmgZrcus9pzM+TJE=
+- EXI20LJNWF66WGOVfbZ3foQBkV0UQOYPq+dMPGd8w9s=
+- jgffJhKNinhHTcUlvw8KFq+kk0Th7UIpaS4TsxPeRqg=
+- mcnxGfshq8maOhTPieO0TH3jiS9fyeWMAX+A3gPReII=
+- idi3xJ7tp88Vsl9DsnhhmlP4dqhCtswu1yDz0ylP3Hs=
+- fQ/wbk+Nj2wdq4vzUD1nC3WatUM5uWYW575vNUiuqXQ=
+- fWee9GLxK0PXH3dM9koWGPvnsHQznkVVR5/iXQJ5xiw=
+- W0e7tJ5QgzKXIsskUcNLwIwYmkZqSuQHiTga8gFB2qQ=
+- 7Xk7S8xqMVRGLPCc1XGJuczFLdJqcts6KHiuL/c1D3o=
+- tKM3LAPoObm3QGQcdMTs6DrwCwiz5wloaxSSJTPtwUw=
+- yMw4iZ2aXojWk0uXx4zLGiXZ0UlPbAc8xQOG3h3Wwhs=
+- wjmynV3lHzvWWzlA2NB8NeImGqVMsMjg280Li2R4AxI=
+- Gw+TwohsVyR0J13tqW1Qn5wcMckqiv4Sb3Es6sk98x4=
+- WMJB+X1v1h/JH96KQASdug5AdKMGwZAEDPzSeLXkQSc=
+- SiM9pnxu2d/ZFs1U8PHz5dZ7RCWmeftpomyKeF/s5AA=
+- UFCUXN+ihkbRQOd2X4lWetUvjzWFw37/urWtV12o0BI=
+- YriTCcwN06BiKIwu9/g3cDHua1651BxUu5qTlvQzrG0=
+- a/ZyAMKJAncGcYRiUMMBilWUTDj6G7w2Pb4SphtQiWY=
+- cNtyRB4ccGmMgSUXqpMTK1gwO/ZyqVoCGj4tV76P6oY=
+- 56yCfCewrKoareT1DK0pfZluhix2V9+B9cNZWpDBhUc=
+- Q81Q7t7SQFF/2n+DrCrjkzGthx+dB03rMJ46cXt8bMk=
+- ZhI/ldPtU/8eV26DnE12FifqWeACP2fuSpS5L8jmhFs=
+- A1tsikrurWiG4mJJtCilxV/xj8pqt+1QyHNoNBh0Krc=
+- PDc/SVO4XN6liOBDbBNMgZ9XC3LB23QOs1z8lCuqK84=
+- n0e6SwFxRK0D4Ua5uZVg/Z97jUql8MI//zWceX8QGaI=
+- Hp5Ml8gzOSy8bspKHXw5A0kuQNcS6A78xXcZZ5hw59A=
+- "/IqDwwe8vYjXuEMhNwTlT4+5HMj3BXslrNsG8V6oDoM="
+- myj5QxqbHRljMkQ/u4uh2pkZRcD7RGHlQ0Qi/+ubaps=
+- axLuD8kSyVePIkfPnPYUt3wVeZ4Ao32MVgB3lmzd8nM=
+- 9kufK3Us0sXsnh7kfeDaimqFQjoI0FsmY7VM0l+1opU=
+- NbXzzG/bqnOkQXQSLgeLW3SuvG4eG1JrZK+v5VuwHyE=
+- TTHnXmUJLSiBiY65DODxWGMqSYL2H5QLlHnABol57Zc=
+- 5n9AQJrSCFPos8ZcffufuxRNm50AekOI/5frDwX1K9Q=
+- SKG33JO611iLRgyEKWz+GayMP3LmiFDImo2IMLyDiTg=
+- 6l5MTgholoa2+OChRzFA9UPkJZH7wEQOHh2P2eLSjts=
+- vmfbC7CJA4AU3FspZS9BuyNJd1x5tNO2LNIh3jQc664=
+- 1Codg+Fa1HlqEhAgAsf+01BXeoDv7u3hedi4aMQfxYw=
+- SCWF/2ixtTbyXubHe22AoEfewgHLCnd34IIehCIz2o8=
+- Ws5JgZmekSLlk+y2cxG+m06H6n+X2Aa3gqn4xqwKaAc=
+- 5Nwi0QZ2vD4ddWh4Sjv+QDi3hKJmgoqbkDNCdMVtPc8=
+- s9Uv1LNsklBnWuhCW7Y8JofFHOtgAyQCt+ACrF4vWqQ=
+- bq5p3jRy5rjjlfee6dRnYj4jxVpzW9L30k7xx1ehpUw=
+- tYlMCdH/8ZQez+c68DkoP5ODNK2waKpDfRk9CJbBKDA=
+- PQfYpVSeoHr9b8U7O6YcrkfzotWa6/xe/gPYukJsv3w=
+- RdGzE3YLmfhKeE8bDsfw/oXGqsctbm2Gjb5km6jDJjM=
+- S8i4ywCMg/dVPma042pTu1vlKi/ze+e2EE5NkxVCXEg=
+- ptgYlgxUy1VyN5kWC5vgtetVsfuD5hZBZLO3wKdGr6E=
+- OH7MqLsqnGzfHuJ4HfrIwbagcV9LPHw6VqnPiq9v7kY=
+- 6R+t8k94wIGXKiAVFG6betRja7WiCPVzO1TuRAdoIHg=
+- yhKpJohbgUE7jBpIBB3VNv3k/Nh/m7YsA8GjVedCpOQ=
+- GmfKvdUqbFQzFqDR1vSrkxI5vIdAZYmaBy4XWjfKQZ8=
+- VpGnklzwwJQso/QuoW9OcDpKuzwijG2RPvDGEoxlhUU=
+- 6JEzS1tAV5SOwoQJj6vP7x57MA/WnaZhf+NAfkfTACw=
+- iIscDRwjSiy3DJCK0LUPc4IG+7X8ASqrDOGtLr3UjsM=
+- V1F74rAvuC5tuBLt3q+cbwSxP7rHw0Lv83f13Q7anxw=
+- Cx1E2fX9tCOR2xhMLdg0BaOmiBBVpiqKiedJrnxbT4o=
+- "/gXZkXtSRTK2UfUI0TTThEbLeI3T1+qysTmOVPOwQhw="
+- vih2oaqNz7r8Pl8UWzpXJXU5OgFoY85Z5FaS0oRn5N0=
+- B7OP3x3R3EsWuQzAq0Jg0MQWdtUXaxBRXRylM4jzxRI=
+- 7qHx4HGSLNTrZFfEzosHasxCkdz4b1BRNnmXiaghrF0=
+- 5XoMzrhUesT4kbIeEuPswUyPZfzmfV0oHEZWL4E0Se0=
+- QJr+wt8aNIToGopx55SLSqIN7TWJn8BGgFgQytdGyo4=
+- BwlyXcrujM7qbL/D7cwQG7ig2h1t3vlZq2/P2L2NKyI=
+- qIyzcUN7LpiGbpYp7l8USE3Ygtma6lT8xUwDXK1weXo=
+- fZIEAl0rMpgE8133kwvC/wM8yu9ElF/O7qWrMweyFgA=
+- xSaaiD8an56tHKA84tl77PNqDeWj71OSDbIUSi6UFJU=
+- lJCcXs1fC2TErgFxzWiIkJWF6H9gv4Hx1CEe1rW5a8s=
+- wIe2O29iN5ZWsWFga+QvovPhmEh0vCW4WBHeUplN8ww=
+- 7/MBcQ/hGLJinv1ob8TrunV0NSAuk3/DsdNknkpTtvo=
+- Jl5RWcFp4K0/ayTPWCKneUKaQbSUZcPDLsjQeZN1ReE=
+- TxsUeY9bkWTIKCXrcZs6nItbaBqMuriDr8oUjoOAHBA=
+- KXqrxWu76IUce3sh6HtlrwMiACKFlspYjptbN1LUF2g=
+- Dr/N2SFy9e3xsDw3waD5s5h8MzGZ+oTyooOFT8FsbS0=
+- ZsySkCgmc5pFfhj59J0YEGlJ7RHlG2LaPcHOgkPGg1Q=
+- 7+KUQ2wzHw1jiTCRcuXwPOabHLkQBiQyjP/356ePwRg=
+- KxHcqLj/FaACfBtaX7l8LFTlFbfz2zskx9sq+damouM=
+- ux5lJ6XJXOB3TwL+V6hcmu8Mv7IXT+Uad9VjQI20X9k=
+- 1QxO6ixcKkTyX+J4rYdqQ3QTygqJEd4ckBmrATwwUkc=
+- JOfoyhbaKOyNRsXSnEpC1JZo3qhDwlHxC/Y8yNWU3h4=
+- a3Um54FXBoAsHhNaBWL4t9/kvmCLvbbaxbEK0Tw0RA0=
+- H+gn4lXZ+k0P3xDLLTOsaLE1NJGh/1HKUIwBgI8a2Ic=
+- O90AvF51EJNqKGewUOtH4tUFyKM2y5NnlbUwRGb7kQw=
+- ngtrEjvVPlEnVqL2IQX88d9wfUft1JmLuJb4uUAw1Dc=
+- KC0EmdB4n3dDMY0hCsNpLjPn4fF+ZBq7qoJe0rWUclw=
+- Vp40SU7b+nzNI4liU2pJ4syv1lyf2aCA5loisuFA/rA=
+- 92auT+C9v+6XknA/6vLB9osIIVxNLE912QtQYoyqOTQ=
+- fY8xA2MQMiWCI9wXCvqsxYG0uwBOV0ir/1MMjz2tnVs=
+- DNJiKaPBMmimt4zBtdhl3Jyic9nRHJhA0JGxlv0iwwY=
+- 0t4afg6DQs6Tl27DnNGwec6oee4ZsHp5Jc0/WeMUcHs=
+- w3sc2GVV6Gr774DM3T1b4qJcEHjdHVuC9uPkpBAi/Ig=
+- 5hvjtQap4yzn32KjOpo/7nVCNx9o7yoFnDqHp/uaPKw=
+- dL/8tP5nOhcKc6mrUgWuBgmMc8qyZQzmziu7nCNqDGw=
+- ZmqQmV5EBVrX0rn/L+dt1BW7Dc5JJCko0Iy7v9Wd1l4=
+- 3sX/ZKpa4O0p35D5vE0NxejIA1IhNnLg74iA1rqPv+w=
+- dF/oJxttoQiShlNMd8urtzREL80slKhJTQcQ/uwXieA=
+- k8WIZAAH72VaUQiQ2aRIXbg5PSX/vucn9heMVuQnRdw=
+- PktIT9MOzPSJezHBSqGXWv9y1PWbjEwAZZZxKj9+xEw=
+- VcauYfz2tMMhCAeMmZmu5CoOdsRaWTLdJimMcCdHowE=
+- FSR//Zat64fWnODGLiHiTpMjwvgkdDNLNbBID+UUacQ=
+- 28BUpiZygekoZRwEFSiq4iNo3Un86vN0tIXA+yGBAnc=
+- itIbvRHFjI9J+hJkpfQeutnWdrbzCD2za4mBN6/dJXs=
+- q44Y706+vt3AsxUs6ckAbhT8BSQuP8nOMiRupqlUMHQ=
+- kanvNWMBDqGvkWCD+fsDoRfU0NKml/gjaNofc3Yp9xc=
+- qkqeoD/KwVtfxjyUmsNOew/ReQZxasO45YxZnNxaUvA=
+- 4mTH+XlzcTacgiMKZs5+4GwHlXuGknZSUUAaspQ+zP0=
+- NptBxyvmkojtBvN60868Sq49WRYv8uUHtIZNNvE4bi8=
+- xvj/mC1EQ8QMfCp8JpqfmTS5qN1FzaEVeQgdkemxmPM=
+- SKOaMxFxeYyGbBa3REogt37y/q/7c96chfyZSM0Qois=
+- eYCWRKgw75JCSmYiclK4e737Yzqdqxi6RQwbjTVmXyA=
+- 6tboQVbHKLIsgmTWvyv1uYpTZrAhBG7qCb6p7Q3nIxA=
+- jRfb0oUpLwalQPslKEmHVdD2QrcNOjg9EVjaIoEW5co=
+- 68gERk1Iq4qU78st4mw/JTzl0kI1cJBsHA/TwxjDyYw=
+- uLvW8D54CKDyfaTNZ7Fx9daGFcfmIDxMFtXDKGspdxw=
+- 5GbISM2EHCtdP9OQ0GeDoDxAe038B4bZTO7E9YQPdBw=
+- U4+emfKKx5cmewxJqTZ368QHbtnFNdDI7KU3U7BKt+M=
+- rHvfspVSWdWudQs+YzRoWhO7UUotrwsB51tmw0NFIpI=
+- 75K3eLr+dx6JJFuJ7LwIpEpOFmwGZZkRiB84PURz6U8=
+- 9o4+zac5jVZUq6b4XX/BQs6O69Gip2UGRBf6RJ40CEA=
+- Y2QCZISah8kDVhKdmeoWXjeqX6vB/qRpBt8afKUNtJI=
+- 9u6U7LAU90+Ie53MUtrs9zqz4zMzIMrdmLy1nYlcUvU=
+- nsXb74fbOoNQ77aE9p6gFdj4OTWEPP2bkmeQiMt5sT8=
+- wjFw3YhY+LtccRrdeYGZB/Ya6QbOnN0upb+MWdhjckc=
+- aTDdqNfMY4lpFqgQTc0XaRzyQ0XBbcwQjlKMo8EQXIE=
+- nQxSrnAjFwz4Q32Gg67ukQOJI453krEsCPBPFcMFypo=
+- jOHNQ8WlOtXaDzno8Q+mcJUsSDnFp/MNP3VOkOloBSc=
+- UB1ay0rMBthdH+GXml02to8r7MBpdWFk92P8i9J10PA=
+- z/mGwlDDBsqBfqeAA/IIOwVgDMGd8B0/oq8Vi/uEiuw=
+- a1ZelRWhFBZdiJI0ZW96oV7EVOolyQsdThCb01Y8K6Q=
+- Ofg90xNzIFGlyfC07tzsN1xUO5i7y4/4QEjFz8i4dZk=
+- IvOrvO6k+BAbrmXkkebh1o4mQ2kWpfrg2O0i8oCKIWI=
+- vnQXXLdjfx3+xkwWmZ2sLqxhF17CjpefO/v9HVWFURc=
+- buqLBMaL+v6ZQH23cMInYzKC8JfQDnY9lZn2+LUm5HY=
+- pGejE+jDP93hA+06LDfkrERUJ/zrJI5mtrzBF9nKaDw=
+- ukZ2gvS4wKH090E3zzlZhWnhNpLuoxRrkQftYYiLtJk=
+- PcQ9u84RVaNxRXz7NMWm4uEDaN2bke5zazz2PsTB3V0=
+- bH3YC1vUH2IDmT5jjNM/xt802+Ko5C/grEdA9ikdrQo=
+- THE7ZgQztmjVWwC4f1xkzirVrrlCB9P7/FFjT+7+kIg=
+- dpI68jpx4zojCdJvTlSUueq0wPDEEy0Ax22KvHGwzy0=
+- jTy7dUF+EzbrHS5PDRjfvy4fp76wZBefqc0HuogTgZM=
+- PUhv5byI6bZsGFCbDYsXvcFuXY5Cakr7XjwWx8e+l74=
+- G27ey4LbcbMLWwW+WpSZ1NsT9L3nzO5eT0K4HiXBhgI=
+- Wy5MdmXgrQaxcN+NY5lSVTwqCuGT4KeMG8AopKLblIQ=
+- BFur3NIRiWDoyLjg7PZbc0aG4bGPWHEMlkZ3n0npQq4=
+- sM12t9eCk2LVgbc5wLKVq/UxgnkmCQeLsXqd2Rf/unw=
+- rJKEZqyc9vyJ4I1Z3bbS9ajiUtelDkONE44w2CROGtc=
+- 0R85YXJvA0f7gamHgT1vIdnvZQKyr+EdwTCJSyKNV3A=
+- OEvjR0SpV4poD8EutA6FeDHswubZn4WhbHD+dVTLmNM=
+- B/kXQAttsocXDcDi4e7qnlS+TYIlw2szpMC2l11QBHI=
+- x5jO7668oCOetZnStQ8jhOiZylgiWI1Usyg9oy3MMl4=
+- AClSVwmsiQ71L0fNOxZAs05wLPF25oCB/JhPx0Rm0TI=
+- qEdnNbN6VBo4QCoucDfHni0hf+l4Dl40NHFW72Hv9Cs=
+- '08tD55jidIPog07Y1bM6/wySkrBwZV8grb01pp4ffrw='
+- ggwFdya5WQl/mMiT1s85FO/tU6TCahfGIxxIAEOjbYw=
+- RKIUtLTCil/mV3KlZ/ib9Mf3uC8pL4LuN02Sqcrn6zs=
+- 3qcRGoQG4nz8i/X0nF1AiYQCsLWEsyhfBpFwo2+e7DE=
+- A7L8RRSo5Sc6ETUu5+mxl6wxNh2TIADkDvQSFx3u0oY=
+- j6Gf/g8kfXdLh1m2+10A1l+FVGBndZv82gBtjxyzatU=
+- IUK4ElAxy/bIsvQn6HD/1mXsI5uenCmPE1gmNQ1DEy0=
+- hi14iPjsPUhoCJkbz4Fc8261NVpR0fgdLzMivzZL0+g=
+- N+gBkfaePBRZDSslZfiod3qoNq2yZZjI6HFUw/JPkQo=
+- JUqiSKy0fdZUyj6lP0jCwm1kHSPX4uk6HsViWN92dMQ=
+- so5OkXdzlxvM14YR/9rnd1hhQiLlQlhdqpI9UM1X7XU=
+- "++iKYT85K5cUItzaHM4bm2vv03/vhgyjxf4GZWLW3To="
+- NUQFkO+FRpzD00O5bYgtxa76lJQgjwKI2aqXJ4Vlcc0=
+- kwsMbB1fE9OI0g4FMdRh+bdRbhAsSD2St/fkksdaxaY=
+- 24Sbvz5cgScSSq0OJNS2y1RUd8L7Qs0+4/60ZvoQ0BE=
+- hgVJBRwhli3HUoxok85bd6TBI3NAxsMZ8fkDS/9IINU=
+- a0EeOhAdLd/t9t/TDnZ499AR74smYz/UWElzYwchTbA=
+- j7V6qhkcdSiHkjx4JRnwuuqFk/MFoiQwpzcwZHEVzjE=
+- 0tjgVp4xxUcBVQASH52evgzuOw+14FytzjWLBt+8tcA=
+- PfZUUyla1Nbt/tmaPcmptRSZo7JW1Q+L49eYAcfKFX0=
+- zNFqE176uhhx3bOvLyBE1N2XxduUQPnnqwzmN141QXk=
+- 7weBZFq101DOY7AapxfWKOwX50AgurKh+m8GmCQ82RI=
+- uALzHXB000yJOq0bg8oXoQ8USDRZOTW/7K8Z6QUrkIk=
+- drZPo2UoJa4TSNwhCdPBJ7J97NNL60zwEMMC5ME/t6o=
+- pliB0o8np14mTd2o6HCQXxYlo3cfxqVVHaNcBEE7yM8=
+- J6DWACf0ujN1sFwcpFdUpB4x7uaX0mkgX21/AMN/X+w=
+- "+W2urN2FAYQ8JyKIV8B9gvM6dGO4qoLlc148SkkK3N0="
+- DNVgoRb5chojVxPtJcjZukQ+LH3NdmNXtyBq7FqHnlQ=
+- ZTyKxdlri4fYMDJ+Hp5A6HCxoHNZ1vJZptB1PZKHGU8=
+- zaGOMrD1PB25DlKHuSkOKTAQtxjZAdaWdx3Ez/6Vqt4=
+- MO2Ih72EdRJ1cZsjYsHpK1v9DWXB/3XiSSl9XCxkQTM=
+- 0P8nlOx6+HZKZUsxto0HrsD1GAU/7pYpkXpXgq2c+Dc=
+- Ethl4NMOZqHlCaJjYEIa+FSolNJB02Yuziq/akYVaD4=
+- 617q8qWc6SjgR/oFp3xuHE25K/8Tv3ysDNAgEZyMay8=
+- 27efuLMsHUGVQ1ZmmJeML8s49xBdGsGwp9ZfC7ROyF0=
+- P9hAbGCJZRFnEyR2Pgk5btjgp8AUYLCvT2WriQI1BlQ=
+- yXFlfwx3jD7+tcP7oFqLt7VT3/g+4wi/fWjdt1BK/X0=
+- XthOkQZhowKm86voiL5GdFVVhz+olyo2qy+SDf5ge4I=
+- AnX8oIwhbHeyoSJ4e2SRpnlqspPkCX5dYhYsDtSRDwg=
+- dso9qlkgSDrGRnKSz8fZU2m1RzGVhRjObZCIQyY0CBE=
+- mkU9z2g1gBMkyhI+RJFTvOxRr0FfR2W/Wj9dZPm1WzQ=
+- MzP2vSBki2FmrgoLSguQTW0UPiuO/0AleeQ/RamMSRE=
+- L34upX89v0DwCj2SsUl3iDHNI/OZsdze3lxJVCG2mMA=
+- WF0UrCO42sjNyir09WUfWp0oIz1tdoBhOBlxod95s6Y=
+- PLW3DOusFfVn4X1O0GhiRLKP1wRvJcWpodxVFNqsQao=
+- LgR/o1AfQ+qT2z7ODtKeYnnFaO/MUfBw4lDfSW1C0hQ=
+- U0pKjq/NhImvMjVtWnol+Ixwz+BEhTmnxClkwbiXo1k=
+- lbYfs0wTqYP+v0GGO4ZnmHypCzHF9VmH4gAIipb7cU4=
+- 0IyehenlxKbwGp2W9QYn8reHmPBVhy6Zrip5YFVJdfY=
+- 7c7OrZVxv7nx0RX52wHuks1TuzgE+S1qwzg7y4CIWng=
+- KObpqcWS/GX1RebMBY3kxe1JZ2hlETRrljdD8pAwSkI=
+- CJVCUF1lnOy7mIu1zP9bzPhb4t+owiE1kHmu4lMSmLs=
+- 2yzBgvuzhWkbcvOliMenSmRNO8ljRPJxB2u7ED/f4rI=
+- NhuQSydhSBjwCxRqOfaEGJnVVGg0X16MhCuM+ljzvdE=
+- K4Zem99JRO0nB5N6NgV5beCdXjGoS+JjclPwWO5svAA=
+- jj9oVeGPRNCTCRckzVkh7ffiFUbIYhnFDHrYXAu/rOM=
+- uqnbM092T9IfkBjegnAUAKw8XbOJDRWTKlzTF4ZKjpo=
+- 5keoMIcJlYtC77I3fqEo31fKf245fUgzGPB7Yx9Vx9A=
+- iwQwg7qbrmO3kRl2BhH9Aet/tie4VMisRwCc+7dzEOY=
+- sQmj9k+S7pFGXlQob9X3N2N7l2eF3JS6CzrT73JKjbU=
+- r56Bi1ZwnuA7o+UfnYEo5xQcMDm20jk9x7tJgvNhHB0=
+- R7Wl+GdnJXBviAXDMEvd57zwiMF7BC6kE/em8HdLP+o=
+- 4Nm0E0Xa0FE48RxGyeMMzapn49G28NaBTmnNmBybOhA=
+- LAkL2xLwUc/ZESHHPGlYQVhAlnOWqDVKy7Pm9izF+Ac=
+- 7rTGuE0EftsfC7F2/PqE8z24reJWrI8kQ6YcoYcqDe0=
+- ejDXgxDyNYlqNoWJIXb+Ptar4GyTzOkE6XbiUINldD0=
+- J3R2ibr5kDyarmnYKpXo3PQlTGSMoT5Mn/Sa2x5Ua7Y=
+- ZC7jUIEZT+3I6swfmgY+XMQOHNG4KGk8G73GVSva6z0=
+- 1+StfaTmfXIxWyso0r3VBQjIxjmV5yv4NhBpgZEnkGM=
+- e6hLV9so3NN1euE+4cWtd6GUrkVXXnJJG9zYEYur4PQ=
+- nsuQgdms/1GN+mIDa2zRNA4r+XetmdJra08o9NGBjbc=
+- DYfcSz+KIY5FNEtWszL83q8MFTbCmfA9f/bNpgVVxss=
+- QuRL5Up7xXRP+OK2olrcTSIgnFDhFsBsY68P420P7Nc=
+- 0yLiYjZWX8jGUOeCYrVF7yeOvrJqpLD1K/sCyqUyz7I=
+- 6uQriYjboec2TD6jYAawHHTNW61LiL+zxfnjguMEzvg=
+- qATd5cAAoSD8MTn7TE4z/WfknwQGbFt09Dle4QeF5+4=
+- wNuQ8p9Q1lll+LtguH6Y3EXqc6FGaz7CN/zg+QTvyoU=
+- DUtJblt1+K7W1FnOqmx1TG2HjbbHQ051pHMj2r538Bo=
+- G4cpA3Wg8Y0UxXrvWhfRSBpN+Ar2plUM1r33M1QrHaw=
+- 7qWf0b3GMUUh+gg1dqfqoFdbxml2ymSk54mEG4MzqoE=
+- PL4/41MQer0sd4WIExIGQDPPSxKu5w4Ahny1PCyAAoo=
+- Sp29OLIGJZtrQX/gf/ieBZyKVM+n9B9szaPCIzNPqTY=
+- hFrY+lp3yBMvfbaxNzj1NyXoBPPmUr5a7l8r9SlT3tw=
+- YMXlg95j1/oLWhpHErtdFEomS0sXz82RJ95LeN4K5fs=
+- VBiBQiOIOjIm9Vj4rMVdRJfMxChvnwTK4+Ahod3gSm8=
+- "/mfF8WCrS+YlMbFOJfoxm2OhrJsKdGqo34bSzd7bCzU="
+- dGJDFneXrwX1h78lwtyEdPYhlq2NOY6VhW5ihLhYa7o=
+- JOJb7fr/yJTRRaWY+VIXW7a4hTxOI/NHuKrM5/HE0RA=
+- 0uKdsNQlHJgTlwGE5xbN56asCj4BxBUnskEeQ9RgG3I=
+- clVE3pb6fjjnweORHXl3D23heMUtS4dwKtgg9f5+E1w=
+- 0hVoXFZbf+QGuIbE172dUfxbUtstnA5xU4JEk6f1wGI=
+- t/IkGarPDq6VY4wp90ENg+0aITrgrzN6lT9+5OaIz1w=
+- RWHgiNud7T1LKfsNe0LjMt0w+OZ1XtgbHJDUa/8kKmQ=
+- uv/fN6pdOWHHe6YxXc2DaCOiDq2Xhp1CMngN+OQ2nxM=
+- uRapChc8SLHVAax2NVK7mM0YxlwoVJVeRinCD0y3ZXk=
+- FD50xuGOP6c0F/dztFomC0DhywpNHOnHrykyz0GK7jU=
+- 68mMHvzisYJVF406GbMmXTEIeKkwp41L02t8xrxKcxI=
+- ShiYcW72d5b8yAx0JsT4dtOY3ifaXHOjrM9CXkKn9BI=
+- cxqACbfjrufECgpgDtPTl33ChiC760lPM8J6WTeTklo=
+- s3c0tDtXQgILvFPjOj+ceKYoTLKMiB2YghLJQkhBplY=
+- LjT9gr05FWHiLclqOqIuViBiuEe/ivu1sI3sIQ7TMYY=
+- bzkt7Y4STfKEjPfYECtyzygSxS62nf+2G3cMghoGAjQ=
+- s+O2TjUHmbmAq0IN6WaNgfoN16nXo1Mri9TRin4quqs=
+- tWtbUZvFHV4lO1hK07s1N4ZsJ79JMni3eAvGqMA740U=
+- 9q5lTkEtB5ShXPgkzwfseGCas8Wx4isk2HMUJ3zxQjQ=
+- Pon6G1RKeEUvQ8nJABE8UE+sHKCADph8sNjyYSgo4oA=
+- pPUFE3PZSSacmUCDVDLt9cHcnOYwEXzHnuWVa4wKK+k=
+- IPNJBscDeuUwpoJ43QIkAuyYfgVeC4s/HV0I0RIxlMc=
+- 5FalgUsQ0pH48yKpVGSlK7pFSgypvIrla0TYJoCd4BA=
+- JA3V48kJdr2yBJKcWo3eEKXaF7pUTNSFqKDXnP8czY8=
+- SZcGJ++K7ocMdzV9yszNPj7wZ/DtJe6YJl1xdri91bs=
+- JDfJ6662sTIB+DtH/WiFsvPtdgk+fePDbLKFtsBzeao=
+- QAlNCm+ovkD/2SenGyj7jGt2B5lrofiyqjgWt2Ig5Ug=
+- HyrE8xAPCIuI2/OS7HScUDb7Pj/60HEfE9qY5FReMSg=
+- ti07R5UH3MGh6lDOzu4rX0Igd/xa5/p/BGPHjMsrqcc=
+- 3Oehf5v/Sqfv6/MsOzoIIDO+fl8QMUM/EvR+ElKvwwM=
+- QF5gO0aYToWjBwHrx6bXV7+YjGR53ED7Q0QsUmnooqk=
+- MC//+FIzF2eOK0WfYqaoJaW/QSPMyPISd+SGkFBBmcE=
+- vduQsOe3Exw+MVkUwTl26j261S0a5iaffDXgns3DX4U=
+- LMVv3iup0lsHOOuqYli/mYTp0rP0fG9FepqlxiS4QL4=
+- R2Z+essAyorOyP2Mf7+qT0IiPqJwXiGi2KFE4VFfXnM=
+- S2S484gT4DM9u1Yg5mft+iVlMzrgetH107k1d5alDR0=
+- H8Svc0YljyyXx59pi9hqfZZ7+Kq0kenmK+N+RmPcJdE=
+- FB9hxMgRDrw2EW1obyXz8jcZiGHDfVta1kZehN8KlZ4=
+- 7T/TjdIGRY0L6U6pTd4OZKQovdSUqzLspLm+ZVOvIIg=
+- 2EZfM1RW1dgXEiC1J7wCkGvmGpxaFzRKBpDwiuG5LpA=
+- 7inrSoclZ4J4rEOc96v9KoSc3HN4prYxYBe4HFHXIOc=
+- B6YbOVnrCxkvx4C8urL22TUD/CQii6yPmGHiM+BM1t8=
+- Tq/ccwUquznEplT2gHDeJUcmz+ltX234xZDIMco8CZ0=
+- 9BVwj9+9yj0CbZUueCq2P4eSdw6mTitRtmqqZywei4k=
+- 6GXAnHVtqtL8yUJPtdOBi1Z+Ho6YfIDBD3sQD+26+1I=
+- yQ4JfkbbGTHC+zwPX7Xx3y8qoB4/bZQ1D2YvbbF8S8s=
+- PrykA+bQSApKNKnm9EErVHOD03qnDZyf+CWJjYs027w=
+- UOaWginCXkYKdWGhBYDVm4CyPtBAWU3LLVD2nUlsmhM=
+- ykFv9LteFao6jpox3mxHKM7xbE8Ya3iREi/wtMM7oOA=
+- ANQS7Y3MLVRPFt9nT2xIVshd0xOCyBdrcgTeL0x1bwc=
+- lY7Vbju4aumPu+hfcxSYqMnF/r4Wk84RlEgc+WVk9mw=
+- V1rSXAZ/kJVREKH9KjT5Nbkg1kzWfkgsx+592Pj6CgQ=
+- gos7+2ekbEFzzGQadw42+txAkqdTEL5q6CnwmJMSLbk=
+- yQRvejetDqfO5zNVmE+lQomC+LN8j3vOyR96xxp80QQ=
+- tqA6BLh43sqcGRPb2WBDL0GHgMaQcuNOlJxKlR+6k7U=
+- DD+TtMAAG5S56pRRDPNqVvUysloAtRNITTOXFV/4ZAU=
+- kFYu6WP8Hzmns5oZvB436XqxShWfFENXILHkPosE+m4=
+- Quhz82lNVM3Jkre7u/NVijk3JkOcxQy6mKHTbjbwFMs=
+- qD8GqUzaeZkC+Dt7pYWg08povs8x7PIbAPVnHjKmjfM=
+- n8yjWvNhNxBL8b2AYQydzm/w9j0+/zjEquPCWnzpxyY=
+- ME+ndAkB8eRYbmEeWeTRAK/zzQch7NGtyWbyacjFiRU=
+- rI+NiAPNZV2HpSL/eE1W9KUi0/aF9XatXiAwd2wymQw=
+- 8cQl3812u3XeK6gyyAvR8hY8xzXGgLy7Vx5ISQnq01w=
+- kV8g8gMSwo5jFu9FxDPy0ejP+eXE1fUiM2hC/xE8oYw=
+- 4op+83sQbCTLsK51Du3wELGtXwR+fIPb/gsUVOQEsy8=
+- ShBqfkiizxYnQ58+NVx1AnDoPxTgUPYGsPEratOdhAU=
+- aXwx1cHEKecMPluneEGcUTFJnahSRN+v0AQK7mw59pg=
+- OL4qncFOHc+api4xIHKcv7s+4Hjrh7zCv2vnt+dO+64=
+- SC9YogAJh2hXIfB9u788aeaB4oXAk8nvw9camxgZPm8=
+- UpbB+h25wagFoq+/4nQOzlYY2u8zBmJIjBVW7AI8Xtg=
+- zwpahWQL9ueBQH4KBIDdoeJZjxmDOPIM2YOQSU/i990=
+- ERjx7g3bBPAkHAyGM19io6OjsJXTOXcTEuAjE5nwvaU=
+- aVY0dZDCqCDDYwLRTqpjczTjxAvFAv0nACKZB08nNVQ=
+- 9hy0IoHs7QyHsj1abLpW9InbKqIMJ4zvH3LuaHc7S+M=
+- D5p5rNYg7KSRST6fA+ITfTqLoWFaOVMBDRz7h26yRx4=
+- ChHvPFHmJpzhOpp6qfTuvj/pnhx47JgZ23/X7LjaI48=
+- 93C+Q6xjzR0lruud7EjTUHs03QRaD7IKGFMVvVsGWbM=
+- ueo2xrHEvUhaKTZh8vlhstP7UDnpTg9hbuU2ULjQoa4=
+- tcC31kBpuQqDSlErjFTqSk8+z/j6aqrrXKj3HogdBYw=
+- 8W+sjYj7UPSEsVWctfCH5VAcT5wczJ9x4SNUexjntTY=
+- "+hA4BPoel3elS2Bp7lcNm39VK99UI5CsAOTJpSeZT+E="
+- UafAlY6HZVk0Fyy584FU+/PYaExLm8v2RGwrzrbVZus=
+- jIW2Y55i4QuDMKi4gwBLQ8EZBT19YrUSqYx36waWJ6c=
+- Tlw1KEmVcOS3+KU4GTJ5oj70pXahmTgZxNDL+a6r7do=
+- dYbe6cP/Yg7fOJvtxj7YpyMXledKThjzlae+I1j6Z6w=
+- iRlaMaE2JHnHZgSOvDTxjU/An/H0fMJDAbdsR91p4qs=
+- TvlUrYYjEe3eecITTWYng8XG/DAIB1iX1DSN6cZRz6k=
+- yi8gaeoMbkZYIi4G+N1jllnLteZ8u7pnNLwzSjeZvGg=
+- nH9yzRDZQ2xoFp5l33u5ePyivR+K0g7fspJ29xJBg9w=
+- XtduYxGLryTtWN5RnqYhxINj9hYfrAYSxjj6SihHUBM=
+- 0T/FRyGMRcgcpuEG4UwjeK575BzgMkAC8kznVS7xl0c=
+- HOrrxlfsrXlxqRaGlkyVweYdjtSphHlS9KUHq+9RzLk=
+- rMNvGibjk7zkiGNdhn0hKMRMzBGDzIDwCZU3OiIiqFA=
+- OTgymKbEPfxOMKhft3AYbaZLMclo4s3pjJ1IlZddlKU=
+- 9HXrarHLNfzJlKVXSFsZAk77EqbDeAK3z+Z0fzJVdmQ=
+- xCQlx7Y8DZKpSZkSMIWXQQkCH4e1JzXHY+bJ4khCBNI=
+- xUBAtLBAE6MOeKsmCZKfZpaeGvzC4dPgLRcWe5xFcYw=
+- dFCRQgpaJfJAEfKVD/GQ2HV4Kb7ELmZySyxR+0Tq32M=
+- 3/kDiDw/F7hKwadYj5mmGOyMtElXeo82gYo66ny8jHc=
+- S+XJi3utaTcYztkTGEmwnXVAuaGE2/dg35z7vgZ0Exw=
+- c42PQ7EoslxcNPCdwmOPSynAjDJJdV1peaZXuZtAoPg=
+- 6RqrEZliWauL0y0Kr7yD5uzyU4YI2c/55X3QhAdzz3k=
+- u6YsJ6vkKEZSYHOwUPN7x+jyfLeLbNOjUzf98sY1Fgg=
+- hYnGOwlDpiv9qbNdzMcaMPVnc4b298ZEwzB0Zc4s+lU=
+- MvkKD+b2Gv3y11t0jMBqkL2HkY+bAt6pCpbmz9gjUSY=
+- IFSDGO9BJswFHgrtF0LEhJHMdukfK0b4Br7UfSwRFjk=
+- DPfHbhZO4GPVmrPJLr87p/rxX23qbamCYLUyacDaXzs=
+- IPNcuCyUdwSeBi8zf6uIRbxby3JuWfj6R6joRqnaXFA=
+- 7D0AISJQ+5ldCMWgaS/rw/7bJDKB6z3v5F+j+/mSwj8=
+- i1G5oV0Ggehz0GTLqHbcsvd5lGfvLhNYsYpf1JI4hW8=
+- PUgQsUsiBu6u/vUQRLAfeEJSvvac6mNIrt7F2YsLaH0=
+- ZWxPW73Fro6nxq3reBg9PATzm8VLKFRpow3NPMMoqe4=
+- 03dNDRk0p72cAKJG6rbBcfxdHjWoBKEMf9u9xbwThpg=
+- M5IwaDQJ8jyKKzlO7TA8y8XN4bF28pdwkA/DdgjCQ7I=
+- 7ovKuzYIFABhVNP5zYuEwQxC/WOa8L2Jl9EY+ZU4EFQ=
+- 7xMlCSU7wMlnP5IqWp5L/LMM7UkzGL3npV6kLUkAn5E=
+- VpZiW6xAnWRAnB/IInWovuxwI/El0WphWxVAWqeZqzw=
+- ax5bbzYVYCwj4jJGR5W+j+nMmQmV3tLoK5N3ODdEEUE=
+- DIMVFRxYDjFwfCaLfQjL40YQRSWBwXSnKP6Wp7X2yzk=
+- eLcXPPPAIOskCOC3MnuEy/n4IyUlKlWKxFqTHiWJ0fc=
+- OaEQuxh+BghDcl37AAdcyWi81iGFhJsOGJ1TA6QLVtw=
+- kuY0jxTrEE2VMptZktr2bioBZ6ER01ArJf71obUo6F8=
+- ol53ObkDntzsY88ARALn6cBpLNQgGBF27V8m9IVTuHE=
+- onp9dJ5Te9O0IHl0V0d9nkc8LsXft4eu3kYE4CUYPu4=
+- mO+HZ3HeySJ4xzjDE0RwmgWeQSfBGfeMrF41OHTnw7w=
+- AuU3eiEQdQTunl4lk6l5StgbIBtY4Qzsx8V1Ev1EgUc=
+- RFWszyKrhPfJYlYwKjkayjXqHW2VlBxWewg9l7a9wso=
+- t8A+NLiehBrEfJhGItaxG10brS6EkdrpFzBIKA5MK4Q=
+- qbnrnLjw27bgyYsJNNLW27Fk2E/aLTJwJikPJAL6ErA=
+- RZIJLhBhx+qFryrtGUYhzBeidiuuM6eb+M4z/QFouAE=
+- 73E6Ixn+zKbXsp+R2TA3RNIQov5LM7Hr0kCvpgUx6+A=
+- zy6Xo43sQECQEpOY8QkpfdxRi0fmghVTKdRUrn4F7Gs=
+- uuroYJmcsHCsW5qmUxJ6GCVw/Hbh7prZzKUvbHBUtd8=
+- G4XTrrC0xmBgZ2P3MbSQJc2EPnzE0iNJ6suokXRf2YM=
+- qEiof+xFjx9hoxf/H6trJGT2dw3MrtAhes8iQFdUrcw=
+- 37LFcXArlXmP+fh/b1Q2+eqpoLaQeuIs71+APJCrcfY=
+- MC0mJzzr7UyVAHfqk/z6GloIhwnbSRBF2ZS5BnsDnMc=
+- "+CGpCvHFfSccR1Az72LrZcL4gGrCQdtlamXvcD4GJZs="
+- HCdkijfLrwPEnV3VTjV03kCczRLGPwAgylAeJwheAMM=
+- ZlNbe1TkYe+1JUd32MfNs4wpU0jEgj2c7R+I+ZfFklA=
+- 1AWI6ClS+8Bcje27Qdk8O5DH/Exyb2jPmjNITVJMwgw=
+- TdaMLI9S2EFsMeZJNDqgmInhM9lKqabl3sM3cGUYlh0=
+- vLwAw+gfaklIBhfI35VzAJnXkYGT0haKKZOQrkHxRi8=
+- pAGAnyl6Z4Lt2LnI6pEc1FAikjecBsHw1+hsvyTTEQk=
+- YVjjia16NlSHQvlhDHIcn1dYD3n3KXBW2b38yOFXFNI=
+- IEZW0OdRkYnrqhvE5Y8U+7ujJQ1WSpEl33oodYe+5lA=
+- utsC5czRvmEjsDaO0gdGw6YzKovyvc9stfZbK80q7hQ=
+- xNYlX+N3ZMir4MQ/qfK5oZYTi46HPs7cAPNCT5p3VYw=
+- WL5qrEW/ncDiAcweSVkQK1uRAtvFcuiE4M6Bt05fPS4=
+- 9jisiIbvojNg0TMHPZKsZTTuzT9w6LpAFEHzTEdxWfM=
+- R7LSC+fM/oPizJPodtm+IubWA52Joi86euPdu3+F/8I=
+- Kdqgm1owAi9HWK8dVk5eZs7CxcchU2g6CozQCGpamDE=
+- qI2eeP1zvj06eHEPa/rmW/aQBTGo1/lvIzLGkqvdQn0=
+- R3WgD63XlUCvxe+XiBTJK7yh2Wf18oXrtZEjennPnZo=
+- u/XaYM2cIpRYf/8+m1K9avBw6yMoZr9jE2QjMP4zp6g=
+- Gi9v9Esq1FNIpVszj3pQsIkyuFZ7+MiqsqCrmP9Ww/s=
+- REl6tsnpSz7llvtTLkmiWRHurwtaXOh2Xl/+06Td8DA=
+- Wi93pCHDvdpu7Rb7lVZ7DpUH4DU0RaxKEQLDERFElT0=
+- d4sJ5JkJ2VQrj2jP3gMCfds4IAG/yTsySt4GhOdFSc4=
+- "/UXrzOZ6CUG2F/TMbc3r1S1ZQB9YdAftMBFgSHZaroM="
+- fU3ZQre3IImmgb1QvyRXobgebqW5vPFs/5fWOfcLB+0=
+- Ppz4CYMgH2jd3322tBrZ0yAlnen7y+VZ9WcajMeYauQ=
+- XkyYFNzI6NyqsY6m7dtwoyWuPhHR+x5jnVVwPB33Yrw=
+- "+qkGGMR6oDHGLzc/yeyZHp3D9pkNCZNTzxvoxJPpbLo="
+- HrJEwshi0dB0urEqp1kFynWnXnhVaeqsdwFaWCPqLFk=
+- W6oIJKjcSJxgfUlcgdPfu2ZfyKySNVkprAwpox33+7E=
+- 9t44t62ncK3580hQpTo2t2jvTVPdLd7/jA4wyRsEC8Q=
+- x974PZtOJfXAqkSnMv+ph4berdgsS+wj+hkdHuchWaE=
+- QI1OqmRE5eaXXJmCMnWvBHXjDil3YaWZGZrCRsYZN0o=
+- dZ5JjaP4nP+lu2fLB4uTsLUo+gln6y0g/zzGIEOEV+k=
+- GNvawA+CSiw8W3f8ZNKKdBMzQpIEK9Ou7gCjgp/+H10=
+- K4llJ/nqyB+lvOF947vR5d8gJuhqKPC3TvIhyCDbQAI=
+- BaNyAavcYLqKr1ajmZeKUicSxUWI48fXo6JfSBmcx9Y=
+- G+Wh7kRqHPpp1nGOPx0ZJ+5+vta3GjYKchIv6IKsYDQ=
+- zt4zOw/yxTF8C3Aw229G/9JNLS8vuW9Jypdpz0s+Fa0=
+- DMEptITpDcZ+cyyRCpeG2dc2ZqNTWe66aZCCQxmOhGc=
+- oenOubBWpOPodCHUa5cec9jDx1G9DOruNIltXGMaC9Y=
+- B8fvQKnnkRTK9YUi2MEkKFiUx71dDbwa1P8yt+omnw4=
+- 3Pyh0dqUofXPnjXB1OC9XYOviNNDOVlDwOSL80FF6Fo=
+- BZ0fZJYjK+/NP/1Wp0GXupwhoWW6d4za1WLBQqwXhZA=
+- lQey2qBBnX5XsuSk3H7rWig3HFB+1qD/YNPJTaQh/1A=
+- L1BG0hmwMUPdzKDr5UdezDrAQiTbFRzCFcTzueq2hZg=
+- 1RvOI9UhZKtT03eTkPi50zmNII5MbSoup5KvPsrdtzo=
+- wzf30hPJwNZwgG4/hFoXaoCKUK67TTdIjuPsrLaZpZY=
+- ePYyhuFVab7bX8nxYPdsJGgVyyH7gsgnK0afsPbzMxg=
+- 8ErU0o4ZohHNHys52Bjsekg2JFlZMhdn3vtFrNo2GBE=
+- unpuIguOR0bO4a4LrEcmpIisWgBe9HQ/jGEdAKxzd1M=
+- aUOMPejen6mUt+WkOCLQK77LNLhL3iWUElK4kMAzQVY=
+- G9j8dRIgpIa8DOXzfeUTKEKT/2hcuPT5Nryjddo+ktI=
+- 7W4QS5CyHE3wgCfnpTU1T29+zrOCIEfJdKayGJJmCG8=
+- U5u0Pv5z3AGA+eE5WZAEYnBZG17fIakxVCd+hPjDPCY=
+- iaq43q+eYUv7PAwusiJ/C75iykmXMmE2seP//6TTSFY=
+- wo0d+kPkva6OyMyc9byBtJqYuvgU3Kx2P0kRrMrzFbA=
+- 6obIrd1Phj/e/UpPxVPcGpLfUAjENSBS6a7rsmxBA8o=
+- 8GiguxtEmQ6rZCN0h9IUbqkAfFrPkfjx/tQxMwE8xas=
+- FEyaB5/FhkHgQPBpZIR1eKLSpzprsZbK8K7twRZIfqA=
+- FQ6yMF0iQJ9x9SD9PwdZHOdNDHp+J5+UpteL3nEPE2c=
+- Js7VfsSqaSosVqtZ19NJ05og7nzF7KGM48VnxVVK/1M=
+- 8mA/oUy8hXn7j/2pxurHOTzIju2wpMu8qLDMVfOd72Y=
+- RjW40fDykyngdiRrks7SFvkPw70DOwwOrw910fcDhgc=
+- oOWNj7fY3UQzCxpVuhkAQ+kxYXVG1sBsS+9MLnczFkI=
+- JwLkxKPe7+exqqREwgx9P57x0q7Ou8bUgFKNngmPdyI=
+- s1scSQRk5q3Xm4n6hi9Nae3v0H96VLsuaapG9tIZa2s=
+- NDaaGOVFp0e6tab0osoXsDMDwH6jeGfjyLMopnrISZg=
+- V82aO75QP5skbnmKENhxDAhZ470JDJr9xknpv4bN+1U=
+- dnl7kG3qG/hyWiVazT5uost5LRIvHBgKcD/VPTpSa/M=
+- 2kDH+uwQGU6C/vKcOpQfDEBdzlaG0BJ7midG0/0ICDU=
+- 1HA/2tZdKRZVQ/Ugl0IZpzasr0wSaopxmLjsZKFQdq4=
+- 0uLwgg362K/r9YoC0eFnzE+iED2Ox/JgWINWGULTl0I=
+- Py3miAb1HyVHEQwGXR8FcqmrciZdbEa6cTmzGKZXabY=
+- 3wD5aTd/fOVrrUhcCFgYy83h+J9E9atj+kdUu7jBVJM=
+- 0iY7N3LZWfYz7pmnwSBBh26wMJ+/kVexiLUsYe9Zopc=
+- qSpeKViVMOM1iyTfMzpfVw9kkDOjfkJAp0nTcA02hf8=
+- WWZhmHL8HyP0teFXBP4EY4LBQH0cV0fUUxPSh8F1bek=
+- HVAHRwbTbsrsnYdchgQQB79B/x+97p020QGiws1Hj3Q=
+- a40rHPfmcaft6Ms9PU9RjgxOTJUv1rt9q1csvzwCeeE=
+- 47s24pCbC3bgW6A6tl/3985Uou9nNSj/p2zVgYFZBzA=
+- WIolfp8GtvzIhl6W686MkOKqXQGKx5XxcwgiE9JyP/Y=
+- pzapNfkL54OxLEQrVgtbyZGT28gSemS5HnEWzssQkiQ=
+- tM918FL+uLNyGKW8riBw5MjyQFYqRIsFSJxcNFftGWI=
+- NdxPy1iHRA16FjZnkENTwo/P7i1njo00Z4Tw9YNznS8=
+- VIobtJ8WU1ooLnhZQGSvdpQGmYHnV5uLtHsED5MpNIc=
+- FxXNHhLQ9UrqKOo+JgpZg3l02UBhfzJ5BR/b6qc2Mtw=
+- 7UVJxxT/vbpVgLqUalsMuOCpAsx0Swb8rtD6B07zxqY=
+- 8pNZgJLqS9p4U8nOOuEVxaKTlKSdJ5XSysjQzDHZueo=
+- zZupDfNELk7qN+whmnaVYpWfNvQGRgPogALqQm7AoRo=
+- bQKB+vC0c/yRUU6qcZjUC/Yn9Y0MuouqL6V2h+V/m2A=
+- An1gZi0SXeZm+KhNNvvJKxnv6fH2Arkqi0AYW0RGHsM=
+- WB3Nx1ets6uK0lQXSvgBf/3057myoC2lJNbzo1T9kdw=
+- 2t8WjWicy3P5/2DqplUHA0H4KbGnnJJ7GSG5CRecvBk=
+- E/vgelJgQ6NZpGUOZIuueoMHpKC/FY9Y61I256muDqA=
+- h8srspeWtxW876kpxoFvzoOiICZVYqcCx3Y6vff+d7s=
+- wA+wsjvz0QUzfNPywu+bxTzDkfm2ACeAbZ55UJQkW9k=
+- wFnZ9bWa8cKDIB+LI/OwwvF+5rJu779kC151QuC+clI=
+- ArgDMMBuP9SssCA6ZRrNi8yEeO8EvAp/L4OYLIIH9+s=
+- dsSRbruHU0LQBLqIH6TKzYAMKzpPn6I2Ioooxf8NAqQ=
+- Ma3rinvPde5Bdx+ymtLfbzkOWrmGIw+BYguat7K35ak=
+- aehxOy2DOh3y3nLriNz79RD2fOc2NXbiSQCjP217vN4=
+- 4LI2pYBCA0K3v6fCtjDfifOGNRGpKmymv/4X+l/EN1I=
+- pcx/iKSIvsaFCGn0Eazucm2YR8UiZ4P3Hd5ExkltY9w=
+- RWQ/y+l4TT3CtRnVKbw5Wn87WoWK4Foj5HXYQSHlNiI=
+- JULFpd9Nt7iJzCIssHc5wHuTNVaeYYudSQIaDY26EjA=
+- gFBvmp4R6f3ThqEVhN76/odm2vdXlhCUOIyrXHFJf1o=
+- CBG2xcMbBClrD4yHKus/5yiBL90ldhW+ym2oiYkg5ac=
+- ClEAsxtVIF+hkty/f2fJwmSevb97YcgaBwADa7A5eZg=
+- 0oLq2KcpfghsMf0wQNd6YS67F091PTdT18vMVr2lg5U=
+- Ir4q9d294bVc7ISVQUkDMXRXwdioah8BOaeftUOQDGs=
+- KMsBffyZBzqhtHwbMPQT4853TEmR60FY3lD527NtgEM=
+- mw66gYRw2Z3qW8ooUxKQHdkWJdtXZOkJJwPyVtO8cZw=
+- 0BmQCL7dyrfYlde0OpJxypx7Yi0hxkdwbu6WdWJ5hzM=
+- HnbrfnXnbI65A65qUDVo2iqPS5byI5w2xJARRoErZDE=
+- q/2unzWDT9YOOD7c4Hy3nbbllFVUE7gV3DotDva+vhk=
+- ltZn0JgSNxcilp1mEDOr/kyXzpAtarFcXJIt4Wbs2Zc=
+- FvyeckYRajM5dnFbTsIzmHUthXSwOCnAd4I5/WFSaNE=
+- 7699wzVfO5LmtyFK9zSDq2WXt4WOrlwwhrfaYQTG6ck=
+- n6kM0pi/rvobpKX7YxyszOp0g/S6QqyXFP8gbjKyjQc=
+- IiALApFHN7oOewZMva3Gig8su6F8ufwkXu4gVyo/Gvs=
+- EKuMt9h9SN/GDiw70Lo3955lioVbuI4wbCL6QivqKwE=
+- vK3ckM0mgavz0VZrJNdnsx4E98EGEXz5Lr8WE9TMi+g=
+- mEfEfB3tI61wJ08w89WehiPvMjaPtfCcA/PTstfKDrw=
+- muLIedS/KkuNXm357drLbhXxTwkcRJzcSnEExsJ0LFk=
+- CYRkdfVBfi7vBWES/eiGgMfQqKnyoHA+GAx94s7ju6I=
+- hVVUd/iu18krZj3vNHEkvFCtayjATuZ73MEA2lI4HvY=
+- xnKzQriupeTy6zG0vamtAjcBdhzObV2rLZ7J6nbOsZE=
+- VqKXlMWZCdjOYca/xNVEjQ9rHNeC/1oKYd9HbY7VUTg=
+- OlOxes1gdyJt9VrXU7nFNXVzR0FvXoNuLkFZWyPJgd4=
+- vNRRLmpzZ3QRKCvm6iCHVX/XOdZX3JZFqhpvO6EcFPU=
+- 4WYWZL8UXBuFSanxH3koeouj3YbP2U01XmBhf/o9gpA=
+- DrZD/TpvsQ46p4nOFHWfy61GKFrhWlBfKn/Ct1QOcDo=
+- XeWRfMM0I1a0sVyXEQY0J6oTUai4PYtRVsKShxgYcco=
+- RTXFm7yWum/nQmxh0A2DGt5L6fczAfGLMd0bA3CTRfk=
+- 3J4kc++D74Qy0Odaxk+5WG62+OmHfE/k8xAmjNQNidM=
+- yrvt51oJTe7zxEPgfX1wKaKw+Jznax5Po9GTGkJ0Eog=
+- 7hOgYrPK7iZawWcYZ76hEOwKxz9k7oVvqmN+NjWN2tA=
+- p/MxDIZLzRueJV7Q2TN5wxlt8w6HyDVyw+aIIXuxiU4=
+- JfOutR8cx4TZ0LzBZr2Hb4BnjVUow+XbDmJ6OZGrj+o=
+- XHc7Iup502eziBDn6a0QhkbtYuIxhozvsLEoDqiKxPA=
+- jHwL0pF8mL7yqR9RMwKfmPFnsbUK7ypGBvBiRZhiLv0=
+- j7YsyGr7EGwFhRiCLegqM9JCzizleIyo5Sfsiok+HA0=
+- "+FgukFNMiQbGSVnXkKRlGMnl8hFZLF+DlKmTCmDaEBc="
+- DcTb71CP2cMJonhD/2VUJehIgnguAMLvNLV4W8D2g1E=
+- iRSGWs5SIkBq8zmJHRKO89klVzC6iTEAmwgF7dWLehA=
+- inE/9axwDLpM2+5xWhu2vJR8+y+AoI0TzSGexLbWfR8=
+- TRpHGNuM0Ifpt/y2gpFl4ROOknr5guLgSy+rcxcGVzI=
+- mLUvU6kcsLA0ED06lHzkcVUu9KLVju6mlWnRVVKYX5g=
+- n9gndl4/2B6rBkDMy7MtVTUGk3S1IzLzMov1FLb1vMQ=
+- Zuzdkka6VUwV9rlhxx+/wkCyXUNP/iEcRSw4B5uCMk0=
+- 07ncz3ZIrZlfgtTKhivbvmHAQ5dHIINxXEMirSA7fXY=
+- DRMO0ID2eTAzbQhSJJJid3vdX6QFAtD86au2Q+bZrgI=
+- GtBdrxRufa22yzRfsc1YDXSRKrnvqOZiPCcscOlKQjo=
+- h/ZRXxxgKFgVoAoApQ9xdOyb2puLAMmgD1tuJdLQn6I=
+- U7xlipAulO7magSb81AI1hsNnrbTA4l9L/R41qmXsZg=
+- xkW4MaSwAI6+MoXKL7rst8f/PsscwMBbCrbkl/5Op8I=
+- uKb88iMeeVcroFrCS0kf1DR4jVa8695fUy12+Xr11yY=
+- "/44/ROq5zt3NwV/RndEbaTScZx33VzslIzRt+u+v19A="
+- SvI0cYSuVJ9B9ftlKULSYZS6JN0jOwS7I2pbmP/h9Bk=
+- tcdVqqsQOLPVYnu95/R8qAxfXASBxtM/BBOdB6oVMOc=
+- DbLB2rEWQcwiTXUiObX6qk5xiDt3n1FWuMxOPXDHrUI=
+- JTDV5+VIiRSfj51gSeJsn2PA3laH0PFFMs9FhJPeWmw=
+- N2U0rztlrBdPUyy4Y+TMH92FWxUYiMe5cwOM5EX7f6k=
+- xhGW8WvBs1XtyA9KXtWYw6AygT1BVpxLrJ674h4KZPM=
+- EhvfJNg3sQ0zdCP1vR+ofpnQg/dB5ybOndJkwPkvq6I=
+- "/l375a8JPxHYeXy3kYMwp9hntF9rtR2dkG1pK3WwsaQ="
+- gKEMrwdJIWr86KBaUuQx+IGc4vO81jE7H16D29bCUzI=
+- tJx/wO7gm5w2hcuK0KjiiG2O5d0fRM4AqKMgrsu5OM4=
+- Pw7uTsN1WVzi0G7KLLVWeK5L345A6jurK012edaJhO4=
+- "/4GMtJ1h5054Hg708FA9pG4CMo1MAAwg3NL5y6I4obc="
+- ecvcQ2rOaJhpgRCJDKVwQK2p+6fg+xXNDR1V2VZYtt4=
+- QMEr2cPhW0eZmu2h3Tvz7K13BJbOx6CrFiRvheyjZss=
+- 3y9j7uJH2OkHteWmhV/SuD5vxQzSSDUpxob9la5bZsQ=
+- 9o8GJ8P8uCUMEMhn4F06Trkl6PHYSWpWMWtpQ8YICzc=
+- 0KmDRMWOS4QYJb4lW0OV7yMZzzPVXVMBieyBHMvTWpI=
+- ngfxz2Rs08jmUOW0MjA+VcDPeajGVUgJo5qT38PIQjE=
+- KAMY96Rv1CvfCg/zHZH8HBZr4t7A0c1sBD8psLXRiEc=
+- L9b+PGfQorjRt/2s09jPSGMbfdQMBcrnXfy2n+jU0LA=
+- G4IVHlXotdQxqIlvmT38NAlo3/HB6k1T5xmCr62apgA=
+- 2i2PxIJMsymqfVQR8Z4A+ErmphMcGtPq266ndJrGkGU=
+- 3HxAL+2h3DhfBPOw5NleQ8quU5PhsyhvjQqmMmjcaJo=
+- r2eL7JOYwOBNq8y/gntdCZ96xJk7ErN69JZNiDiXeB8=
+- a1vGzc0XEPSRjLai4h3Al8dAK4J3u2eVHIauXWJjb3M=
+- tSfE7bTl9XIBtsqkkrp+Eb5nsX23D2+nkbaT5Y8mzL0=
+- KEkUn+wDBAltOY3fbmFfXgoM8pIExIGJXNQ6OmBVuII=
+- zQGllS1YmJF6KV6htUoi4fYqo6ig2DTm5sYl1YRpYZc=
+- wvbu/c09wZFB/mZeJx8KEe5jNO9I/k9BG+zQlMbwTVk=
+- Q2zTcMzUPWDX/G5JHFXE1MZZkepOanfvG+ek9vyoC2g=
+- beTKYqzhQ7oRgsYiVvw+sWG+tYLi3wmBBoAV6GCyoK0=
+- cq3H3DpicOFAoYo0R6+FoR0J+pLlwZ0cU0EkGRJElA8=
+- cPPitSHUgRDsvLSKPzDLFyH9WQOR+8IxyumTaekwtnQ=
+- MQUXQeRij/lgLwrpurgBSUm8XD0IYssXzqJJ3iGXEGc=
+- xayqPSUBjBATSu1BtTIqj2kmdytibZ529Yhv7sIqPkQ=
+- oD0uleWHdGCEf9Nnw3iJtG87wz1UC+b4OkmPuHXx9Zo=
+- 0pVGVeKbJog4XuP3M7RwB48JRUFAVsN15+003Ld6Na0=
+- ZGoWfcyL6NmsBcul8ld9JPJBNughx/LWAy4Gmr/v6a4=
+- 148kELff39j7rHa3NSkgUh9e9bctpKWp5DW3MKmi5YM=
+- 0PLQbfrV2188Gl9BmHvu7aFmhrhQw9Y72ESlKntTPh8=
+- q/NMqTzP2XzOfkcD3ODeppaepQrgHSKW1+sQqiVqOQk=
+- g2XcsMISrzYA5hUrmxGWEAJFtVIvOGsCkOh8tnmH4uk=
+- Eg2f7UIuSGB9EfjyMxVjjBiKRih7FoB03u3/LLoUp44=
+- 5eGpTJLy/RZFE3LUTk+aIYEhRpJsscYFdypiUfxjfCs=
+- g9nLW1t0Yvxb3Z2osIxQgQOP6v+YzIr8ZWm8cW2tb8I=
+- daSq3M/QP9kjkMkzLkYfTfSRhBZ2F7AjkPESanBIkQ8=
+- R4iRFbGcrT6gCwKp+Y1i145UpjDUOTS502GZvz+fRAE=
+- GUuNDXkyaNH8maJw5wu97HPCEB/Huj1B0g0PMcwn4Uo=
+- F1TnBu7P4XkUBpM7tMbGMctUek2vZ1lCs/vRdBn4eBI=
+- KAjl2nHGgnfMZx3/Ty7+Tn3cgEbVzpcbvNZwr2OQGFI=
+- sbU5m/YuyuBja/5gqLhGQTWAjMr+fXZMyesRJwYOWJE=
+- ogr/EG/gEdXdaW47cQUgD/dDMe646GW7gOvYKxJmWgc=
+- BLRHeDr+W/1LSOjbBnE3MZUmqY4CnDeyVZFUzszBBn4=
+- xMkhZfXLwAGKb42h8fSg9HUnBJWcsULEzHCsMHDiVrU=
+- cvEGrMJod12iiyHrZ0eb1lr0E1wk2ZnNlGC4MncqPiI=
+- efRGKUS5SF37XVDGSLw7TJgvMK9WAQOURF5uSVD1OIk=
+- C/XfO810khhpDfdB6y9+JAcRReJfIkXuQFf5a1+lO2k=
+- ljheuCj18viAh/M2Qw1ToOy5sQo3Fid166DhhbJY5+w=
+- 6tFVjJl2XJZ9v3Y5IHa01OKgsXqigVws2ZryHjsGqU4=
+- Pa8wZOaZSDwiXRVbIru37lw2dc9dEgo+NcxM4oxumH0=
+- WHcPdS5Xk9MFyNM2opxIOKx17voUQ8+VUO+GKNbBy50=
+- U8Zyf+lhcPXrjaxNZEAEBaZFYJJoEt45D4kFsMSBIcI=
+- lhpruKBhbMEqna29Dk15NKGOGiR+SFQLSCZcxKolWK4=
+- XfVYEzXvewZkXcLr9QIfcVVcw2tHRHCqnTyZqp2fjaU=
+- 8nbfKlphGqrlwQEkjk6ROUpgxkT3JiFsWD0YRRChRsw=
+- PXX5YkmdLzhr+Zfe2mYZuk3TVmmqhALiWUAZmHc9nII=
+- 3jbITuPoriTiuCZkAMwCQTQMetNiT6ghgQSuypasclE=
+- viyc4XJ4yTEYU903aeOUzeAEJYsj4e2CqaGMD+gwIxk=
+- AaaTjTfUeyM/TEqFPn7adNfpN+PBEO0BGqGLcTXOzTA=
+- N5DWE/+AtKV5qXAVqFYysHgDn3gxwY/7dGi5HYKzpms=
+- YLFTaqPoFeCAzNBSMB/1wuaQXkKAm54/Cv5e8TeUV2s=
+- CfxGpPloeqsSIVIYx257t0satryqiMfNa2q91AYbsX4=
+- h5pNdtl4+3ouvPOQ7XV2ALdOXfKKDmST2qiAcLUFXOQ=
+- fiX6VfczR7DAp92qGX+Hzww/CcoxNNqhLOFXAEBjJyY=
+- dScSC7tWpaQaYoaiGEjyM9StLMgO2HW2iika1KhvfPI=
+- i0y56S1LWunyi5QrUUYBqlLAYmOGuEPffqh6dqXkYgo=
+- pV+kHdgWQxEOymOmGlf3KAtfFc1REDz9D25Gyq9vv4w=
+- QxNqaz3Kxi9/flBl+DD1WrBeYBQefaT2cdHahuInWlc=
+- F8e0mntcB+G95+bBzYK1tuNi6My/GQNJFBk0+YrWYdI=
+- VafugmVZUL6wE8Gz7sEJJ+i1Elvx70iyDZgbt+hTMK4=
+- duxVK+r5yCMu5icDNKscl6AcqHgCtejQIwZNpJ1tI8I=
+- GeiL+dbqf2uei1FsWwawdp08pfNj6KvmtmulYGA0+Lc=
+- igsQKqYQRSiU+Hk7fvivCTKVDKCsE9hnHoZOEVALJlw=
+- yMOLnIc0XXOBU9d4sz7EK/E8XNLQn3xRtfeclfim1/k=
+- f9BvDiph/sS5P89434dZ5eTiotovWmJ8+tJLgBQW67s=
+- QPWYzHZVDSkEO0ziNKXkoPLllRoRO67Cgx+R1SDz20o=
+- y14PrHXJIa4CSH+tMMjQph7rPWx5MSb6bjCduFTNOqE=
+- NfkVZqaHwLN0ZjBI/G6rnPegUT6PCWzfHKK6aYb6jyM=
+- sQj9+Me2vdTmam4+LV3TY2RWgx+tMP2mio+CHu7gJxQ=
+- OldFoF+H3e4dtoshfcBDv6IG0ceqod0KfddrhSpzNZc=
+- HO5G7VT6BfPPBKop6EXSA+g+Xe/7GJ6EFVfkLdlILdo=
+- Xk/467X+hNjHlozsCMleCDxe3xIFSx0usa7QUeYELDM=
+- L19s5a4wtUql187RulZpgrqzS6KBSlHOGGXSwtiBXNQ=
+- NqoVqI56s2CEy2hFf6VzHA7Xaz29OHFGH3xWuPfAbls=
+- ibAKLt9tXVUuD6HSfDNW0mMoIqZ89U81CEqzFNNr7OE=
+- hOvBuseHn4pz6t6EDFF1R0hG7HHBSeaWQ+318Usn8SI=
+- gdBv6RZh61WIurCtRz+GDf1D8RUmpbQ3L7Qdwk6HsIE=
+- RqKBzTVJitbgswNZtXixxiIwaVcqFlmYQiDfp345Fyc=
+- Xzs7YUlXEAh8TYOylv48Z1qzkGFYN9UfJyvnDrIsAGA=
+- 29zwG/Us6sGFo+DXuFGZf/kLxm3kdSF1YtcfJwU4Rqs=
+- Q0b/kvW1deqrZz69fycjq722amsVeBMp52XzEGiMoOk=
+- CfunlD4irC7aJjOBPe8QWkvU1AIPNcmiPsFF38dSEdk=
+- J+7Pl/MN1M+uk2ilBjDyy8ctjOowySdZm7N2edluPtA=
+- Cmcki5rgZGhRiX0JmB/5QicOcXPN8s8epWH0U4UpHUk=
+- lY1RYCu/vRiyoIS6hIqCfCmVK/7xcMk2QZsJIplMBYk=
+- XVpwoteIeYNNs1g/jSdVguzgbq542w1ADdpaCJV8YHQ=
+- jG6W96tqApTKyoqWDZZMnC+7ijf1yniOD8gCvmvC9CU=
+- OV8pEHMcUI/e2C2C/1GUUUqCK6HIJtrSG7+bLT9xYHU=
+- qQogc3TSmnRnNjMBVgemtjbvyvBiZ6XOXJCvE9HKgTI=
+- xcCIEy5q2/MNT+Muk3q8mSMhKLZqt5lAr1gfx1YQ+HQ=
+- dlrZ9MfmLjHn0M3m8l5xMEEWx99si9RSBjdvgl/48Ps=
+- xALRO0qhpq6Wlxtob7wL3R/thz7L77D9AdV4ce4AFAc=
+- rPRUiJeNn/RR4wUZTl3YVFK3b3XtbKIjlCzUBp7VLaA=
+- dDBWFrK8FsBvGQNMqdsZ0qpJ1s9ctIGg0OtqnE2mXA4=
+- qR8Mwk/2i9cYDXSICxloayllxI3rAtve8twDY4ieWIM=
+- YXA0wi22ijplKa6RDR3FqlzaYjDRdzQLyU9m6XQLtIA=
+- zmQoco+Kw0lNDQVfjl/01s3wtEdWFS77M8+K+QAl9BE=
+- KUOlZ7wFvGbKYgHbxfAL7D93SkexuUKJoq6OeYNMIaU=
+- 5b32k2ZKg6dCeEuVyqWGkQwGFmzs5JNa9BRpifJui/g=
+- KjM0nn5gaorS4w48hFIfk3dFDPCQg+Fi4KmxSAzg+XI=
+- wbWboMVuayF9/dDeWeQHGFaFF8B2oBLE6jS6hn8IxeI=
+- zTQNGlxBUd6i+35Sqz8nrr+aQTX0UG1NbgMInwZumdI=
+- nt10Ev5l/wi9cfNOODq0NVt9HEavBXMYC1I/vzhA+Oc=
+- 3mJ4q57RKSyICemFIA720212rXEvdnRWE34SLN8H6DU=
+- AVAA9LWM5AIPzYDf5PuxXzmoZNd9BCM7uKCvSPoVG5g=
+- 8fpdbCZCbzfnPBw28I3YmhFyElMa3FnyIdi3C4HQVG0=
+- wryCnylRk5dfDLRpYkbigyiDjhUzKeK45D3iOwgALr8=
+- oYrE5vvT/AJKB6Idr7rDfYKMqKBKDjTzaPHsVODU//s=
+- MZuuKP5OZFwKywPY6ocCsz4Z0t5y3UhXO/1ePJyS5wo=
+- 9YfU1YZgYTgDnmT1ieysyzdepWOtwhoI+xaeZakZUks=
+- Q5CHSC0h1XWEttGibMnjhE7XXDbzPA95UgQqsEErJAU=
+- uWibRuBNqPHqKvFrsZhqc40G9UjzXZR72WRUBgeRjsQ=
+- WMax0yZT6Co63AT+E8AZJd/EFZ1J5eEaR+QBHB5/seE=
+- nI9aSnd55qgX7Q7P+7vPWTo1NwAkKHSDUDinn5Hcyuw=
+- BJWfqIEJNagJgYe3HSJ5tpPz0GbOrjq3I2dbvA/NxsI=
+- 71Ks4JDMxpDDEF9IPavlIcfhgcxnCNrnm9hFKPYYugk=
+- uCK7k5Bam9izoMCBaMQnaWQ2z4vzftSrjr9BoHZC7Rw=
+- n4Cl4UAXzMBasMkEx5KRWo4TSpfrwvWjx7yfew8hVQk=
+- P1wLX5wybnlopT/lIdtUbxaNUYSUmNUSHDothe6nQGA=
+- bu37ltcnrdR7ycEXCELh8JnukoDSIZ0x4rEaNwJu3sE=
+- yF9nuoAmDF+rvqJEJrg0qZTV4NjF457MXOG/cF6Lp5s=
+- KSniL0ixxnyiPYhx3YHM5BLhy0QQikaKsKv4jb98cyE=
+- BRkl0D/Y3LMKK4pawM+cos3gOOOEz5MeiqjK9X35GC0=
+- AM/kbSur0zPR2z+cH7PZ1sPVP9f+TPIGNQWm5abeOZc=
+- FQNDQxztVdO+VodW5FRTuO6tXdBv6PqPYs6uCX3S8h0=
+- rZsHArxBhJmx8vtO6j+H6arySusxXxK3ndLp76qb2iA=
+- ALR3dSD7HfrYomHcwE3JLhgN0DbxUZx2r3Mhpw4+aJg=
+- ZKgTa+2BMSE0YK8ziXLUt3UNcXZXzRAKFNOUjitGv48=
+- BbtDKkv21IRoYvT5igWaeo59RLnXCmDfN0//fPMXwK4=
+- s4KCJXicYbysWEKkwFnFWjOP4+RJ9G4pwmKj/pas0r0=
+- uclQZA4bN0DpisuT5mnGV2b2Zw3RYJupH/QQUrpIxvM=
+- IOfC6Jgn6Co+e0108wwtwu8WDsPwiUoX+5TXjtfDrZg=
+- PqRWtLPf7ytklsCZY6F3JJm+QNxwicsAe7QSWCYvQ0Y=
+- okVv8GHEnHGDFfaOD1QlKkoWAMnsLSbXefS3tgMe4EM=
+- 8kq8NLE/redugFeZ9xGH2mzZC5ysNzrmXtV/FDvWZOU=
+- Rchr/8uR1RnyUSzNcSs2NwOOYPl7uRGS/4vZHck7dnU=
+- vqfwiHW7MRzo38Y781oButo7B+0hg8HCHzkt0H5sz1M=
+- F0F+v+Blom5P4Cuf5F2x5pBcG2FSW5SK+U8c0nPYFBI=
+- Cx2/8nFKWozq1ygmKmYKWJUHLuykXB9WnSY2UnqQyWU=
+- THPWx311WcdCNgyr9b4Wc/qbjkvCqd15G8VE2Ft9YF4=
+- 4SuC0djXDxqEBPdRntEXjUKlFMuwvukAoWUOUM87ZYY=
+- eJuZ6SpLPbX40ngcXO/zGQU1Bm7QUO0QSZwFbosM0P8=
+- uNqCpJXm6lgvcW9yxwCVm7F275gfzinPBH1Zg6jEZuw=
+- XmEOa02CHgNaeGZjLT6rlI1Zx+9RVdbDQ2/lYAjBjCA=
+- U6YMlDbZ8UTgWs9Nf+EJGyH1To+gO8abGBZj4W+BseA=
+- r1f5ohtYO4Z0Qn70AeIih/R3T5VOXv942W0XOi82sQ8=
+- X1DdcnTv5CkRF6CJd5SFzkNTRT8JVgQ2lc8Ji8dqpRE=
+- tMTY0b+3vA63L65MsBvIKyGahhtuuNw2paJ3sGIZ3ts=
+- yK3kXtZPpO6NqfkHLwa9w7jlyqX62cW0gAwyd+z8I2k=
+- OLh7ENCNjCNGmHLxiAFwAz9Mp3yxBWO9RNcehXuACPQ=
+- 88iAEqyD7mQtqZvNnWPPbV6N6idrrjBl/03t/83CDGM=
+- AvUUgvVypHC0O7Jq6XkVlYufLCi++sypK9JBfDZpNCE=
+- aXRV7E0InvOvlwt8gRJqUUm+2FRf6qlOYtbbChEb6cI=
+- sCVvDvTfttJ81poboHEApjGv48p7yf49micWGgCPJnQ=
+- ggbSLwQ3gRCOaco7dLJoqC+h+rY+olgRXj7eG1IcAp0=
+- BMzGdEVf6ANTEhdiTLZxakWqSPxhBs031DQbh+ZXLsg=
+- H0VOjOjXxIchT6YzHXJMktfwYHFtIclrGWhTJTCcEqU=
+- jFyufA3QqcZrBBBr+XEAA8Gqekvjw9QhExVJSfRFJm4=
+- SsH1rv2bAl06+V7gRR3fQdu9FXt/vYppURyyHzaBGVc=
+- xrPlEC8mjRemBWJyCr62JbDTOYKJ9G9Rhh8bqzBV6J0=
+- BiXlv6Eeh7Qp8HaBu9/4u2fZcSA5uNjzYpoR3RmUIDk=
+- 5jgYAZH7pQ3jRvUBg9swG21DqKI+pIgqmDGVuQkdpnY=
+- pievSUPD4nEyghcFl8DHltzGjBIHIOMTBmOcNhvUMU4=
+- JWahjNHqxf4WGURD/gGjVA4mH9xppvDHm21S2VzXadQ=
+- hGFbGnvXaRLSnEQy8Rg7+iijQCzkIdcjZGhY8P2ywIg=
+- povzsv7VxcRK54xgykxYvf+AXyG/csxieyFRURziISA=
+- zFcSmkVJUZavuICjhhqr8hi0Aoz9NxeBb5PYxdmY7Ck=
+- BTQUFSSxh7VA05nmddxKDkYR3iM8DCPIyUZwD2YUdmI=
+- UPSAl+PloVZRJIuK40IQakqNamYlV5HCh+8MqjBLUdc=
+- x+QCJY7jlfiuCAm0kFEv1GVDuYuTgHex1Va6t1lub0Y=
+- ACX3VwTw15qux1kZbwDAXvIH3uDmeG9ySyWyNO+rQTw=
+- FIZKaiPe/JBdWOi8IXuTvBdveEI8IXXcCByrEPo5UrU=
+- 53O7XZAmqgmeeQzY79dN0XesDwLT2TFF/Q3WA1WkylA=
+- "+VK3S2CDVUgirrbwgrHptkb1bROF4tvzItSX8iccYuU="
+- F0y3USFJbSrd1m4SdRCtLIRJZ6g/9SofIva1t8uJSgc=
+- dy8P4bJuR0HAyfTno/o3W3AZWbu8bKRhBb1E5+PcjFA=
+- W0BicumJsVwMyCHTn0ifHiMMZVej4CFrFWtoEI1B+yg=
+- 8+rFbqRkeV88ziYxg5we272V85oy4HQsHsent4XZgYw=
+- trkBAlXOQK+ihKSLKa1dTGSneW3YmWztemWEnbQIieI=
+- lc5w2e3j8A8uSGemPQehI1N4NiDGE1VGg7afMEC/fgU=
+- kJzeSic+Xur4LVvZ9AiO0z2O5CGmTvczthZmL5oiBu4=
+- 4AD0D8Ts8DYHnBtrldfAMxudUUHdeb90vMqtoE+XTpw=
+- WLqjxUr1IfhwOQ8hJLa+MuaXiX1daTgY+BkvMFOJfy8=
+- ruEnHrbwwE9Oiif9hrC3ZXyqJvoWAT7Pd256kFGLX9I=
+- 9fHOjyXXgH5YMyxLwHRmjN120f54wWQVnsZ63XsADFk=
+- LLOAnuPteqGt2m9QT9jurqLGUFR3SyzpT0CwErf2I2M=
+- iukC3QkROqnw8CMqqGeaY+i5cSZij9+nd6N6M9zcrzM=
+- MABfVs8vTUTnLlb8VVIblmsEHyYjxfyluSivwRsH68E=
+- oLCk+dV4fpR2z0FuUq1I3MsVfmkK2yctos81inccX4E=
+- 2GyGUbeUuvqlNJb7uaGdFDOQimqVEWHY6vCIitnDyMg=
+- XpKG2oJhoV0rJVY95Cf9qlFe9/iOAPd2y6+t7R1MWvM=
+- GUgovfg4oaIVWiscpXmshsVsbMtS2XV6BgdxaAAN/+I=
+- boClvx9uFl9lllB2KQphY4394PKXJHTXO5VKEJYqOS8=
+- ZseC6PlbqVjyitquV2xComPCRJr0FvuERJm+9/1BstA=
+- S7XtUuVkSAwOtExkZjPABK6+fTIbPy0FywIK6ZRjMUg=
+- Z9cQpZCgbHaDbgzddbZEwnt26Z50MA1p8dhXlCoZKuY=
+- MfYed/JXnf3HFzgZTNejGi4/UGcUcORyREVOXt/WRl0=
+- q4ueArMmbwPJZw13vWKups8W94dN8xvizVZudkH57Xs=
+- 2EqaaysP6ubtu+KW2iFfMHmGhjxi9OpfMzBpDp48s8Q=
+- LVQ7vBK39q/nsOyyBTRgZzS54XbXR/Y6Js9B4mcQjKM=
+- hFul5BLQFB/X0pwIdiX5FF9f79+/KOW2FfgH3z2R0R4=
+- tLt/I2riTXJtpoFTw6XbsHlYQ1SO7A/CMZq1AF0dlvE=
+- WeIPaUVi2gmQIFjJ9G14lbcVO+WthLpqmNp5h/lXc88=
+- 23rZUDNuvS9VVjrZGMx5SvC07boDC1GBKproE1YT0Jc=
+- AKa3LUCJrHWyn9S9mWlgtTWMnW9knhx70PJYdWv8nKA=
+- 6NXRuQ/7Ld7K+xXNR9jfhv4nMDaKmupnsZje7xk2a08=
+- egGt6kuKEISnrr1/MlYIDDGjATqlrXY4rdITbW8+U3E=
+- SpxT3tKaDXrtzBa2yoTeDPVQO2loPqkT6Sn+eExXRTc=
+- AktMLyeSL87f08WZyfESiErfjDI2qTNbUfjR2hWhuAQ=
+- dMK2isGtlHhiZySCxlcsFMGjt5Dts7sZKlHtNjkjQKk=
+- gXSgXU4m0GMSLRGRl9PRV7SG+4EHRlBKzq0QOCDjbmE=
+- ssN+0ZpdJ2kaodYXeamHYF28A0jEJDR/R9axpzuNrso=
+- y3mwKyCv/xhwTJqd7bZQ2X47A1A2GIasIOht7GKOxyU=
+- ZbyGf2j/0cq3ct9CUr4mZILJ7FWHsU2x3/mtxdtBQMQ=
+- sowizQRQlXWNMz8a6vy2pkVZ3FD2sRZ3zpajBpgXQt0=
+- GmhC/8tou7wraZhowKnIDgbfrAV3VY4lSrh8yO1341c=
+- Mwh3HLGxntqRM02M0JEHkcFQI7CWkuu7SlIBIk7GBgg=
+- DW/924eX2oeDLEFQW/p4iJvRDKnQZ5vO6q1YxSyN7HI=
+- vOKWbAsNhTJ5Vr5K47oaAsYa6nwLLZqut19WvndUaMQ=
+- NieZllxQbLhkQO2IqPL4rDnt25EeCBkZOG+3mW+UR1Q=
+- W9etsCRGMbVEqF39SOkUK20f2rGQcRT5LV+KJ5ULAro=
+- lqHbeEqaxyFda7IA2ZhE0g3b9/WGha/GJkdpkIG1ra8=
+- c0duOCYxd3m4+ccgnHZPQ4GakBujezTnty+dy550r/o=
+- 4/O246ClDYsT/wjJNLqDyLcNoziYI+kGIwTXCIEx0lY=
+- 5xTMsjpRqEcOhAPaOEjmYnYvEQwg1AqAvHPjZh21Y3A=
+- ghooAgWqUo1pdvU6dnZSiqGa54/iDhizNkzWBO0XOTw=
+- KwqMI0u5U08mR93H/Ib1mZZRNXs1nw4TnQp5I4jraiU=
+- gNqfiAiVF2oKevCpG2HhIhiWQoHH23SxQHT65jwU+G0=
+- O02i4o6+C8Kw0wSX5HSKR2gWXFWLBzpquhXVffglPqo=
+- Vi1KukzBeDQJzDJ5bDsCQ7tnaMxV5RxN06fmXLVcJIo=
+- pe5w8VIlI/Gwo/SPTmGmNwi37OlOkfB5hjYq4rSNg18=
+- d7kOfpl4Olx/QuSQVCbvei+e1n1y536aYIh6KzDca8U=
+- 2vzOTH7UN4f5EVWkr0hVGFsAAldScQKAAElXNvIAWSU=
+- sQ5kJ4AuoKuWjzul1n5Pn5Ol7Q223eFvXcWtFw+8lnU=
+- NbbQqqrjnMWs3o89R/1q11lOUHxdebnxF4xbKO5fiVk=
+- pgOXSuSzZwbNBO7AnVvb7MXvjieWIrwB0g2C/akvXZc=
+- N+ctVHhL6TPdRtckD0eeCN/2qALq5dWEdNRvJK7tDe0=
+- VvSuNWOEfAlCWwS7epD+GPy8AiU9/CAf+KQPk+NR4Lw=
+- oDZHVaOKh7oZ57ohtmYFHx04fLj7va/zgjyiqVJAk7w=
+- GmvU2dedwKebU3lccNM0n6njiWij+++/6Hg++x0raqw=
+- DeBeRbnfM5UcWczuYuw/ONYfDa8Yf1aVmanv1unnH+U=
+- 6aL00OHwGxzWk+hpUe7MRCneQlkJqOUDjqCA+xWMP5I=
+- M0idJJt52Iq41QikRbpiYhX8juQdcHZxiEwHO97QAac=
+- "/Kx2tLLJmN780qBVtOrbD6jXktL1mZb+oJLfJRIcVHw="
+- XspIkdryrKvkqVgUvZYBQ9ZRsH/HLtqDEGBp9gAVyVg=
+- OTQsxB15mapFqJc1T//gZ1fOm+y612MsgL2iN75Peo8=
+- 5JozIW0YzfmJfIRb0g8DcvEkA3vgxXZj+VfCrySInLw=
+- xfIKZ50BTd1tNgg93b5hwV1lJhO4j8z9vPbXjYc4ZtY=
+- 1LRYZNnW+r/FaNdPJsNaur3iEFM316+aZgXhxWyJGqY=
+- yCB57ISiX9HvRjmEncnQm/WhUbhofRdiWY0g/u6KCAw=
+- hN4TaFZYV/gXAJDo1e7qk3JRK46k5yJPikI+CpswGXg=
+- GAPGoz0mk8kRS7poAiEDpKu1uKuwj+ELmAcv180G/0k=
+- zK2zRuJhCAictff+1JVxZDgSLGRYD2ZNk1/RNCprVQk=
+- EH3c6tVgdUHrfWMLEOFzy3rlPamI7r0PfDa6gUL1C2I=
+- qalaY6QuP93u6ZQhYEQf0aMle+RX8z81vlLgwZ1FkKs=
+- 97EVCfTWdcPETw3TfKgwuwLoz6WPBMRig8S/y9zh/0U=
+- 1SR1upSSpx7FB2KjoDtzrZR3K7GkToEudSMHIebUUf8=
+- voUAQsD8uza6xTjHZd0dqTWByNgejB3T7zfCqDpWYXE=
+- C2fGSk6WQOeNOWPaAEq1D6OFKG299p5dquH+xL0sMuQ=
+- "/U5+geH6BfUZ5K8f5mYjolSx+7XHKUVU73ZgkW320a4="
+- vFs/4y4jxPl+6jfjny+5gjx4DEurih2xSWaurGyyhKI=
+- hJXlcFP9D0kPNwnb58ZSjec7yQWN8MKw4wqMa70rDrU=
+- iXsxIHLC+yiDhol/rgh6PZQmjo1NMgGCthnle+YRsYE=
+- 3RDWGrjZ+qfYSoSPAazSYebSLNpAZXvwrJJTwDwJGkM=
+- CgqAt0wzuXB7KeLT6EPqC9woubIXd5EIb8wuRNYGMiU=
+- JSfVzuKuhuyA2IcHmKQ+aoe9JLoORAe7tv2zbeVYnso=
+- z6P0R6apfUGhWbRH3/mwP6HqGiH9XPKBvVCqXymhWC0=
+- yQvA9tN994p45a8Mmfbe2nTFKw+f94NB3MrxXdvXGS8=
+- dU33Tg97FUvQOM011nVM/q+axTf5nnIQu8d2kEiDO6E=
+- ocqf+O6EqydI9OWbgb+fB+/T0qv1ImcGKV1aACHWR1I=
+- 3vq7g3Z7aK3B15l3N5fRF+j5efvPvRw08mb75HFvR2M=
+- WjJBCFCKaUR3dj33Y0xmpxlqdN9u91jFKpH+UvhfCVc=
+- 6lWVzeiNgj1BEKLa+gKRtSsKW3lAnPShID4RDBIwJrg=
+- PFvbpOgyGUe0uw/I4UybmU9N5SAfHwEVyaROhLSI0dU=
+- hGBzcd5JKmbdFRT/sp7jAkMr2XQeZr0lyTKLS/PGSwk=
+- yrGOXAzfeiLLq/2eBrGmHaYbQXT9POVdN/ieySPZRao=
+- BNvdF3sky/AfvWFSZTi/68hFW2IjsBIlAgdoUq45M8E=
+- xNMI9vIKvk5rDyiZCP2Dzvl6H4myWZPgYS80PyVG5BU=
+- nlonFPd8ZmfPjcz1FygKFdUKoPsTk+FoSLUL9r6b0pI=
+- Vp0P+VO83+Tx5NMZcGOjXs1RThtnVUwP34QqPg4i3lU=
+- iPWd5IczXZjI+xsI3e5hXWRM3krK2KTRG5Zd+sFCJuo=
+- rebKXADuIFZLT+trZrbxRawNvlrBZjdPqdumAvsddT4=
+- 8dET+cCAkBJwGUYCvd86OnBuTm3T7cVl4oY+M6nBG30=
+- ivv9uNvli5UXHATB2Gg9Mpr/wX8/UMSWKSFMB1II5CY=
+- ra+d7G/ew8UlIiy6yVn2ow1dljzjpV3FknkIHTapCuo=
+- hNxrXW40ISRBZcTJQpam2S/vRBHJDYqhZ0AOCGNMa58=
+- SH5CDJ3rPIb7Do2R/bnHdleBHsJx0qC9+HV8dRU5Pv8=
+- UdW4SXohf56S5BY8Hd+x5HeFYuFZccQzFaapJdg8DiU=
+- GZHtdg8tzOvVCQ4+wGAI9KfdoZWyyf5LmAGYG4U75NY=
+- uA2k0pUOlW80yn6l5jmXmTtEqCDNoxRjuZPWXRdYt/Y=
+- wo7MTsbDw6i8GiK28Pc3rwE1flmDlJvPmNRhcFuEnYQ=
+- ZfKprgtD9CjlPmdKDf3WbL6zdUVdZtUorRWGBuno06w=
+- w5DPsQBPLpADa1ka/xBadxLWSYmZfVDT3SX7NNYwoOQ=
+- bTH22fvtnkI5VcJfuqK0PB85Qb/+BlSLG/LAcEYViro=
+- 7O97HmTHDey5eG33eNRw9yiMAu62uVyX2t5bRtdoq1A=
+- xH/hvv/eMUPtpqSMQhSUH0/60hsTRxuQyO3X/qUxPMM=
+- 1X0TmhiLDchOZaP68wbltwNd5lt7ZaHMn+gmcP48RwM=
+- AwB+kJb5xQDGeb0Yd0zlprY4U2tNEB4CdTRTTpCI75Y=
+- mmIu6vSmJps+6J/Wjbm/3hZj4WN4Ghl2u/yS4eCwmhM=
+- ghx4P4yVGjikJw8rrTL5NxDmo8Mv9QTmEQ58h79NKYw=
+- KcnDDgYEUVztmLPRT9iHUaj45Lm8adSDpnolfBSrefs=
+- kiam3ocrxI8EGb0W23b3QlBHV45Zz5qg3m1c9d1NuV0=
+- sqERV+OVkNw6CuNX5uikYyqiu1U9M41mv3D4Y7xLFHQ=
+- IBnDvW8Z3UcLbGDjim1uDqGBbb2BDBkFouDH4t4rzlA=
+- aGc6PUy41JfTGWCGwckP2W9kRU1mN4kMTr6OSoZKgZc=
+- qV5T7S8qS0CiTW3iUfTiTbSPSab9FVYv6vCm8V/Fzlg=
+- IFmu9HmWy4F3JhuaiVmk0yMwt8kkLiYQrPxPBzhCgT0=
+- uTo/XnP3D3nVsAwk7dw58J0x9E7DXIS1mHp00Hq71vc=
+- T3VGelLblr9yoxtvHr/YY7SGNjw3VZm0eylCsHI09Xw=
+- PWjZmd4ZTeOsPudRPIATiJF2s1Hkq+x4bP46naOF6rk=
+- pi7LjE+W86QBZdOjZ9HqZA/5Us8KomP+XT6Lp6ctkuY=
+- Fkb1qfjBVYAA5BiO6Yc7+/p9bBoKNBoDhenoso26LZ8=
+- CqgV/7PhDgk9cvXF3ureZ9i3fVrMjrBeyg6vCBnI7YE=
+- qM2fntVbf1hmBWyDrsFR/nvmvQCcOO9XrMZ5Fsqm/2U=
+- aa2kCIei2Af5eWqqXbSHTFXms/4KYRkPlZhvxMJZaxY=
+- L+928T47QkG82auAPDk09ukQGL6LeARXN83Fkey+zhQ=
+- 89CCxsoIERHMWu6g4sGGbt9QATjSXBIbPzk6Kc3j/LI=
+- 5cQe+x53uWAO7vxKwTJ2rt3EWJFWzIE6hOTsrWIWxI8=
+- R9r7aw9vjHIPd+8DnVN84RPE/+gLeacqb4qtcAdYjfI=
+- axVxMkDoevIEKakkQ7pL3S/kArAQC4vwVwiCV0ZD8J8=
+- LEBRjkKhHoIxMp7/dnZlw2o0CQ7dYRQ+ZulYSyuqMf4=
+- NV8gOPD3NRe5KJ212iLM9vKZ4/V5vuvzUne72XlHxy4=
+- wMwhMWfm8KexbD3eqwJTc+8hwUPxtHGQq6FFzp1s6ro=
+- O0hKzSpjcn1nip6j7IWrOL+xBWyzsh71tzJ5xr9h7mI=
+- lD7+0u6hVfOD3+XMrRKQJ4eyx8jZrvlmTr+fcgKXL3o=
+- nD9eELKHWQ69m04iDSPTZzecQPgEGWgm76O2w0nWiyU=
+- t7lgY0NWb8bYSxWjJPjgHv9YPMbGOAXHZ12dj8DaGwc=
+- PbxrL2/GeZU9H9LENE5glCZ7PusEMZC2Zg0HQa+fgUA=
+- srY8RCKwoGPyKC5KhH6muYxpZFnKkBVnljER+eQo3cM=
+- cExFnGcy0Wy5ZnJjpLQOHTwaRA9uB/vN7jyUNDCcsgM=
+- 7vRJGK1Blr8l9xPBuVumgKRHvMlNY6aH9Q3YCX+CMoI=
+- pKTBmYqkvIXaJ7gEuZsf8lnt0jwCB0IiGp4RvJbP/g8=
+- VMwUVgeEpz6grsIUWDzUpaPifPuo5PuQ+2E0jLNzE9A=
+- 8q88jgAhc5n49UGsCpGhMFmJrBjXOoyGt9AT4owcreY=
+- 6PGtK96jr5RJZR+Qb3tFgFM9bbqtW2qzZqMktti0pQE=
+- u0q1TuLBVQCgly6rWkPKSnDDsXoaIkwyoNHsgwvmYqM=
+- g3JkiKGxPgeVby5wZ/1pjsUMwx1OOH2BA2EBe4tYtcI=
+- lbb+FbPPi0xatsyTidS6MNxgmx44AtuoX5lRa5x2CrY=
+- "+kAWLYhY/YN07nYs0Kj+R09nFAxRSbBUHQRguai5s+k="
+- PxPagRu1oDZkQ+J0/xtyuDoM7XTd5/Fe/jSGfA6FG98=
+- qo+wg5ib5p1zK0PXT7XNHeLU7UBtyrYTYX1HvGE3ESk=
+- WW0VP9URU1lQzZ1W4c1VS1fqmsf68J/ZsKWlsC488vw=
+- SVaZ8DPNZpsuz6pCvRXN2g/MdFvkwm80JSJpViMebtY=
+- u9SxMYDFMFdWvfAdNQgwwQr67oXTVjFjrlrU/lnu90s=
+- gpo0Jzb4peVp/GeulGFn62n6w5Ti6ir1VVnj2tmLVmc=
+- d/TAtg7pOlyx5W96SBe4sRXS3nik2hnLbmJDoAwXiYs=
+- aXB+bRxafT9Wq5FE/4SxnoPJUrzkZ9kOGnrWKRCDfEk=
+- "+kYJGnyg1HnZkXP6pmCWEKCtC3bTiPsPGxFKL1MQteQ="
+- R+YlB2DoQWqWUY5DsFhaG6KCCLC9TrWDyWhVFthh5M8=
+- idMVq6f4csqepMAAiBgmrRyNSgfL2lN+06z1ys4g+eI=
+- PykvCZU9EPtrPxaljXv4HWvl73IADx5MT5SPVW1rlgk=
+- 8Zfx7Ov+ClgGsrle2N49uJjX/j+5QIQdMYcV27WxyVw=
+- 65p2UM1TK2HKn+wM+Ckkx7pzkL/ImOLUW8BnlnuYnMU=
+- 2/wD12KyD9uBBqv5q1d1MvcJDYH/HAdajWuOuvYMYp8=
+- 3rd1Kim5udn/jceKVNrXPWI9F0lQwpjgeAhCyH/0f9U=
+- asWf1bNI5vJubIn84urTiESOlFizrC0eIEvMNzWloV4=
+- tR5f1PcLBRLhq7db9UKMQvKvHLcwfWfaxEClFCU736E=
+- tEPsVKkfYen3Bgfxt9nstJYfIw4GPOJgwcQ68pTiJwA=
+- ZrETKgFzkQsB7joV705pWDu/L38eRGLJnvvhuatb+Ag=
+- 77I1nhi75RQpG30t/1F7/7C6mYF8ntZCjQ+hqrBvmXk=
+- l/jdamSEp8MusTuS1V63nlpoATWcPYpynXNk1icy3iE=
+- 1oLtTKTZicE07JTxVR4exYDdbVpuzenz015uSnF/veQ=
+- 2x7i68H1LBKQ1OgvTAH5II/UM3VYB1u5qrQMGnkeZRA=
+- Pckadn3KJp0WC0NS3BZCnMlvIn/2YdddgoMRqgtGoeM=
+- GifKO4jv0WLmJQ9AP6u+3sPdDJdRZ5Z3DPQ2osAzxbo=
+- RFcfp9Ufny1y/uFBSmiYSvGUS9qTieSlxEglYpJ5Aio=
+- beLUs2OC+KHI79S0I1HQD8bn0FRPE6A3VZIGrezldeM=
+- VwhyHEcQYzh22xNqlTDYgxy7xQIyCQgAyGYn8WYH1Kc=
+- 6Ut2XPMtrD6FU6RtB/vSG+sgl0Qrxsg1ZmFo/dlvVjc=
+- 9HWfO45lRb6lS3eri/dlgldaZ8wHVuVpSHxUIG25DJg=
+- zXlMs6JQQs8/xCY7SBjLNXziEhyyisk8jIzhwmrXpmQ=
+- 7JIzz6zgs67JpCFxDQ6ReC51Kewd672WNiygCg2zekQ=
+- Wf/hKnDfFRCeA0WVXjIwqXjzHrwo2P4+QtMGr7KLjoE=
+- "/XMhxAX4r0OBCmcju69vtclGGu4nO8NpPueQO+zE5uo="
+- FO/0R+dhIXS7vYnop2QHc2N9KtBU4lTTs7YDr1MtsWM=
+- G7S8x9e1DEiFSSiLwJtnDvs9y4lsENpuwVjvF+bc0iI=
+- kbJxqdnas1w5b826HbqNIcdfY5DxFM4pkQ6nWX3CyPs=
+- wIDkJlsWVot8IwQDuaiXmyKBb8P2HTUv2BfiaYFpZ/g=
+- 6lKmqJkPaoXrmGoIP2qX+lBiZdQd84wtn5Xv8d7i2Is=
+- ZUfyEIigKXwo/ub8u6NMoy5CL8C1mevJFVR+nC/puUs=
+- hMk3LQhWkfuXNYw2unI7oK/9adGVb+w8gUqMrl9Cb5w=
+- 9m2pFu2jhG5z3LRlpTRJ6mK+6E7gBtIT+W7qIrp88cI=
+- sNHYf2isKpq3eWoVMp+WP5VnwRdBtVXJCHN7nworOHE=
+- gyjU9lxK47jvJ8RrO6PE1KnMooFHY+H42tXEmHUYRM0=
+- qg+Jt6bMUEkJz5blYSF0v0FkSW4vDayV/pD4m72oI5E=
+- Qsw5oRYOVhcdQubVXdGYmiaV9BVk+pdJc7lGtycurWk=
+- 1uDTTvFjejgUL0Lsutihr1ucJPvLSUn85BfVtoFvn+E=
+- 6wqHGdhOQbTK202gKtqp2o1LTUroEGqg2YtG8NO8Z9Q=
+- rOeq8Mtlx8jqSulQ5/mj3lLOoG50T6PhbPMzoxnqI4A=
+- uNnCbH5gSwLIPZMnbxBpK2WlDBsHaotyCDWWlDNT45Y=
+- QYnZzhtSlVn0BfhnuhvEUTB6Ne7pyLXq625kd0Exo0M=
+- 2EWqBsNpfWVkPqi4QBaBuzzBxp71zyOA67v2nr75rSk=
+- G3M+MIlxAqhH0SA3dg+DsybIgaAdZxm3h5jhZiQt3GU=
+- 9JyLRDWOrItHw68e64HA3eHnwJ86KSDCv2cUUK0qtOU=
+- fcPowzD52NZ6BN5pB0Fy8NM+xIX7jIwAPjPay5b85GA=
+- toyIBgi9a2ynYLkXxVAuuRjvPaOXBZ9nCj1iTcjT5As=
+- 1bc4EUf2+7NIW3U14KSE0TYY4kVU+3xZrHw7XpBc/6w=
+- "+7YzeiunXQieHr/z7S9lv4MbdDk8mkhnEV8Edniw1jk="
+- XrnA9+XoI9mVRUrWqssGEm14eMFQC9jFE2ajyZ8nVkQ=
+- 2VHVPEQIF8u1Qq9t7SAClrs3P/ZX1kDhcSPxQjvII3g=
+- dV+dVRqCqD1HEiAJ6v1Jt+X0K2mkOnZlvOHi9akM/mw=
+- kjJY6dCVl/bvKW+UPd53xoSrvFdC+b8qZ3tlHCVJXrE=
+- zV/4GDtZysW/WCSVXpfOlAbGGevxzYy0rF20VTHqq1k=
+- 9Q1wDLNwCEz2dV/fOnwtqEEDRHlhwqh3tc6bXEzQqhM=
+- hNOsZEbS231+mcrL8BB9XhIfLp8yfh6eA3UdCW4QsCk=
+- '09W5yjMpW1i+E9XKVfXqof9TmDfRL4EI6ylgQp3zb0k='
+- tMqJ/vQtNtll+qCpu86zr7jgBjMbtJuB06TXN09+c6o=
+- wnbjf+Teh/AdbrvM66BzmtZVjTsQ0dO2aTJWaZK/bOo=
+- aFzsvlQHLG+Ea9MdxbVaCFzf+/SoBGCAQjMFzgGLfzM=
+- YZUZiu7FRXb1JHS/kssC7BxeEX0d2d286wj1v8VFoLg=
+- TKMDo79933voZSAx/wJ/NRlzogXN2vv/pYB1D2G8k84=
+- 53gMeUMuI4wj1nV6QOjj0A53XJrPNmA6glV80Rgki8g=
+- gSzMPol1CwsLMXeL1GwU6r9FpN3LMpjY0MlMJjkmhmw=
+- 4cbUfZ1hsJ6X6ujripG+jfEKMHbTqX05KPxjcgb9eXw=
+- UIC27nlODwzAwV13RAZD1vk8kiSxo2GLRDviArS4Ktg=
+- H6cEOfzFdY6pcHkb1KHZKLPts/vEtfAG1c3FlH2mHqo=
+- XHMEZ8AIhr8g8KWhDBUCiib5b0KyBpXE08c38zmJkA8=
+- "+6+OsUvhj9VRNSW8F8vLXNRuVUjvtrILij7nqhbQt/4="
+- 0bdlDOdL7JACz/uGgKrCDIpPK5TTl3k/27YNsLNKxjY=
+- YE0lVFxbfFUgCZgXW+BKOSvE+/h/r3HwzT86vKY8Jck=
+- P4mfRsmSgjgtuK+9978ECJl9esc/Y1PReb0YKCfMV9Y=
+- Jho1Hod3zLldibplvhjT0zxi4HM6LgHAqg7HN/cF7O8=
+- uBse+RNKjprW1EWH9o7Hy4GBfjfkt9Vl2ay3E/mCSIU=
+- eppfzzzG9fmqnR9u8TDvzcLw84hQov09Ymz6XYZRuc4=
+- EXavIzOMUNxqdhTsOVVdZTwLemvxGU6ae/kEO0zUDA8=
+- B/ndCzddqvCYsHuHbN52OOfoHZxOJw7zk0KDxGtLLUQ=
+- 1lSLER4HiSZ95uw1uAbNyMFtY073I/voczaEjR6qZ74=
+- pqr2TzDLw2Y7l/xQIAhz6ASx5ha2Jr1KinXGgK9QuEk=
+- LF3gw1HFHriB/tHTkrMu5RjbssUBso+GIy8/b4mNgg0=
+- 6C9FLaahk31AezvM6wUDHpKNv1JxPeBruOTONNycVVI=
+- x82OXfbQJK3kbelAc+Lnhjxco60KhBhJx+DgqKRL1Qs=
+- up5iPCPNwYKyWQn+NPp0lfGA4O/WQl1egKfRxQIHt3o=
+- vQ1VJVcNilH0/oOQV/Ukya3hraZon8KrnZRhEO3jB/A=
+- 8CJl4/M6eu1TocJ/ZiuwUAbjeL215phmnSJF4rY8CZo=
+- QeXui0zxuFD+K+6mvvqatRYqMT8ZB4lpIkFSVt4dCS4=
+- BD3/jtpmIjULcH1996augbJDuk1hsTytck5SbjL91ZM=
+- f2Rmw++qBdHIG44428Gcx+wFYJBP38OQPPoSzvv0Va8=
+- nRuxL0RMPWo+GitttfzkvuYM16rrv7LVg2lvBpQy/OY=
+- e0tZhpFXgDTPGYNISUTX0G2hW4OxmrUC57b/7JBs9w0=
+- 3w0yIfFhFENzx4UeJ5GdK2hloiDDL/pY8UVS/1iwmVs=
+- 3WKe49ZsPBOfj4GjVMfEf7WLtQPtNmnlk9I3wU+MSYg=
+- SwHTO6L37X2vY2XW8qZpkK7woYHfJwEwkRATCqnAu+Y=
+- 4Srnpzr0tPaT7Fm7pzGTvF+3Ax1Sok204R8z4zVrrAk=
+- 8bXLgTwib9JYPt8nX1G//wpwwWLJ7oG9pZXg5dohPk8=
+- v8xE9Yft5y0fmVqBtcbSs/5lm1ydvBTtCBkYpsuOZOA=
+- gA9V9cZ9YD50LN5fTflWjYFNSPVoaaflwe3MGJSV0hQ=
+- zUHp5AaKZXopIuEh71hmRvPfUa1GnIFaaA/0MuT8zUs=
+- VL8PNa1XW1B71n6GBsmzi1kkMU5cxqUWUyusFLSlf30=
+- V4ipCZbkQtXHqmUr9wx2ZQR7k2NAD0arJVEB0XUaCBY=
+- PuKA0JbKBUa03IQ5aFWu1CAKfyPOoMOLvymw1QBelTE=
+- z2iej/oIAVu771h2ADVTRVWiZ/XngABNhqFl94MaitM=
+- iaKIzIoe/P/tbWxX0aNoK7igvamsjacWoVEERobbsFY=
+- G9CjO2YFf3V6ZmBNYoqaiUrUZZs6pI8Xnk2+ohJRduw=
+- ppbfU1eocdug9wfI4KN4V6z2i2aqD1IvLUBUs6nT/MM=
+- PFpNzG3WX/Iv85m1J7iQ4fbCpLR6qq8XlT4AxD8qgzY=
+- 5wr8XPsKoWQ0rs3miE/VDjhCjFhVsFPoi9zA52uno2Y=
+- oXNEBirxwTjRze7Dwvr1HxJhSbN3cP0a+SB1X6ydGbY=
+- dirK9hbBeg+8OTR+xVxZPv9UOjX7As5B5L+bf+ChmlM=
+- YuMlrAzMAsYiw1MXIeM/hPW1FbXLpTzn44weunB9ffU=
+- JsonbNPT4YHFVucfgh4EVFHnubC6friJPmKFM7PUuvQ=
+- G8Bzln9MJPAA0Pw0AGz43oN1Kp2P3mgnixjAM7u2jSw=
+- tb0SR39RKBR+eZUSoR0sKIjUPHi1v5oM8+btWhiN7qg=
+- bnF/c1/DitoPiZ/Fx9KYASYElclUkAqSbPsltUb0Bb4=
+- bcgJC9npHLkMh5NEnm1Dqr9Pyxw+3KJmXlnn7S/dbAk=
+- A1XWPNs8DFOMKN0/Uskgo+8YuG7+q9ChQzTddlukzTk=
+- Es+qMFpp/Wev+fIxDWw+5Xj5sdsuzNq6+9omxmfrilY=
+- Cqi2xlkcI6KF33Yw0zoUHyaFlkLA34U1IpV6cN1LEHI=
+- nzQoDT3AZAn3xELKJONsuRRad9UlLQ2XEpRAgRkDhtw=
+- Dgxj1SHukVhcu3WTK6mvg6ROPHMPIpTrnmmL7csuPvg=
+- VIShRcmf4S4nZh1Eg3XGordgk4CazOG3XzflGjXIz4Y=
+- MvCyTBIhKX/78MVdzd/uIH1SUy4wx8gyl5USa9V6x18=
+- 9iIS7zck5q2bda9+nt/hQwnyXe8lxqvTprCNfyeeBjA=
+- gdSCGWkNAfz4Y09aILE6+WUlESLaQiVfxMk7XwE+hLY=
+- q+JJlTgnPUTz0NdcrgyPabCNT8QtHrTtNLy2kHXXR1k=
+- QxDVliC+2LZGz2zHWATr2Zl+4jLrhGSDCOyLWozKTyE=
+- 9L4R6FKwjcbulRdtlT22jOuFVNHDRqt9pDQ+UyyVB5E=
+- Qag2Xo4T8PM9RE4yNjQ4p+7ReC5M6mWX4xVO0TzKdtU=
+- cKhMnf8n+KcAsxQEXDlR4hD826UpbHod5sBA4McgKCU=
+- Am+fX8VW/lrI4iIiX9pbApTcCnPaniwgJYhI4wEssXc=
+- eitJsbkIghkFHCVswBRMSaU1p8OskCwtSGXTfPn6t80=
+- WE/0LhsjUw2BJmam/lIZrhqMPKfFr/ekVNnE8L7lpFQ=
+- FSvuKWVq2NvIdomdMn2irmUv5sWFjKK85GU7O9mQAZM=
+- 1B+G25rEHB14Zku4WhxUUyKHrmr/k5XXEaHLbuADz8c=
+- 2SMbiYQ0YekD1ThdFpra0qIs/uhehHFAtGtgX9DUXew=
+- XzvLKIoRHRR6J+ierW3IOBeiEkgID16BxKKUxh8uqD0=
+- PZt/z6dzA0zRN1FkWNuY8NNDCKcbBNLl8rQG5+awGzM=
+- SWqe8QoY2aeFg31odWyj/imdf9/7A7xAyb58MPGNBrU=
+- Pss0NW9b0ORJ3osdIT5re5xcRKvfZvsDaAuw4cr0h6E=
+- qaP9oMZQ5csp+VO8gSxaAO52zUHsnAHDjag+DBe+89c=
+- F/Yo/FBd5UXHTwfF5CJUd9MsVfT/+bH5T/iiDc2ZN1k=
+- pdFt1MV/XPUY4JH8KKXP8ZvZElvuBP/L/o8lOkoT4Rc=
+- Nda47X1vITbTJvFKhdMilk4c4kKeXFeYmPavgvZZWqM=
+- AgoVQdiz2jxNTMH3eXFbztdhjXt3yiK9MPvIfR5LB4Q=
+- cBekOZIAIK+FuK8Jy8CHtdbah28ZqCEJoA1tpB/p13M=
+- 4kVnYOBq/k4ezhfz2G6kOMytF1ZLCrOKkBLofPDE9IE=
+- kxa5+ZNZK/QiF3UEbFbVcyHqmdSX9cI9U8DGL+zWfCA=
+- i5hrfWNYMYnGVMu6jM3G4gPajVbZSFOgik3hF8VU3Vk=
+- ZfoctR1UWXHw6U1gzxVGWHpxYTvxU9GX8xEBZ0/8HD4=
+- pL1PLWd+iU2tJsoUdyHvqopGUbEnK1BDPC6by9UT3ug=
+- hR+KGBj+AEcR2J1DTKg62YAHFPIzGPnjOiEea7HLWGY=
+- GNwNsZdbUBmNgplTZK3FH7+OsLAnkOm9nEfzMzNzwRQ=
+- 1ASPOrfoUxYrsCL00zvZKae1lMm5w8nSVH428NMcV1Q=
+- QA45ut0Hm51jeEuCm07sFF9CGow3toPNw2eHH9jeXwI=
+- VK2nDkPXft4zLxCZc9Qf54yw5FNhNaD0IhEaLRT2G0U=
+- 1TBOOMkjLdz/FcCd17SREzzQ45sz55NwoFG9yhft4LU=
+- 21BCTGvV+h32xoHNGDHAB53bTE4zsOmZtKgA6oDT0eg=
+- FcYu73WzNHI3zxI378IOk76fZJiPGze0asDSrzoKCCo=
+- mDV2+NY+1JBuFvgdKluLCEF6jBb+xCnWo/DWHnInmRw=
+- dqgOLFN4O5GO9RUpV7C0gTn+OID2GugwgOEkPPXbJCw=
+- 9BbpKldFgFjqGYqHe7nAyJgYwISH379DwAsnuFhHRg0=
+- d1omnG96DE24RySmx7y+4mbXCvg2bxtpzA3nTpAVGzk=
+- gKSi5MbogaGZ6t6JY0Qrfv6lMemDsNcP2QqoSBV6LnM=
+- o8VAd73D/WVLjoUOKhaxWaOxYg3XqYDQ+cZTh+W+tS4=
+- Ats4p202ibznvMvFcIwuyjK+rnKZ8w2bwrbCwS/jZ9I=
+- n8WyMJ80ursSr66rqsbrqXGI9g1zzgeMN7rLbMoYCfU=
+- lt16+OX2LAETtywYcfXd0keBol8N7rnu6HMs+Do0bxM=
+- rri+UCZfdlDwzM2QG5IzRPOI/S2hrw2G86zoM9bF4ls=
+- iTACfTsOUILfZAUJLWPcBNaIDtbaNhbDeTxdx4XggBs=
+- ukHeygR7Tz0XezkUqwsYSssOPbp4BZh5LK7MNKrOXB0=
+- xqqHEBRXzkoq8UDDu6lp6MFgobf0yqNjI1NuCew99ok=
+- VFeyMsIYXU0dYG8HgMrUHa5VtiqwBdZyhOx8u4DOk38=
+- Zv0jcrOegIi9fIN3Wc5yCiRLR4B3In5MYmE+dfsg4xQ=
+- ryi0mJqp2kgiD9ptSks3zxhYAnqGSG+Frize58lsO4M=
+- 45qnPjeVp7QEOBksi2i12IKgIX65cvW1zwJeJC6+wrg=
+- awqHnu2rNFY3fabYUUONWCI0GVSJH2/UqJ0brf8fcDk=
+- 9SQmZrlI0IBggsh2DrSoLMun7NvT/bkztlKTYTnS0Ug=
+- rwuyzh3qT0u05o5XI7nhDbTe3GxhU9lgCo/MQM/n/ec=
+- 2oSCAKI+IrvBVVNgEqRv0nTzSwSY5tQ1RBPcF2ZxgpA=
+- "+5Hw/5q/SF51tY7sFaLQv7BS41LCHSaQxEgcJFMFVJg="
+- V7g8sfGHy5AHupI9pPev6s+VOg+bIfFETJtPDmYeqjQ=
+- YplDLTvFZMRgRaXdZfvd1NAFOedqMj/uk+r/WwtbWZg=
+- T9emxoQCM5p/7A1FQX0zndPMBY5+zM0jJEC9a4r/0u0=
+- Fi58rA/Tx2f5xrNAIiGF+fI87/MGbQeFTSnn27zCeg0=
+- 9Q/Y2bQbNKP0rZ/2AZ3jHDWLhQw+QtNrtZjvzIxrRes=
+- kUHm+rnti8YeyPgkbhq04LDcq+N/FRJVOlS8mrF6rE0=
+- MjanJH4Q5T2+AenclZ4GmNuAUMjs46wPx3Ex1pjVXPs=
+- MSItmXdgTekaChz1pvuB+5meGTAUSGUKGOCv4rsHRNw=
+- E7XiBiApa0tAmI7R7LzKJfoGfpIrv3j1GZ4tSVaPXJ4=
+- 6pd03DfMw86YRZdAFd+Y3HYCdAYshcUqKPrzZLpyo68=
+- tWWeyeWbG/JP7QHj6uAzXpBXm9NfpDD8/+kZ1GYgLuY=
+- tyhmfjsqaslx3xSCsSaAGFGb1qQwmL3uSCYKZ+YBW2s=
+- "+YCu6fHdbd2JBN0ZeVfyh3YY82CCArA7nlSBpXjdmz4="
+- gbNKku0M4sSh0oW8ZWslvObud1vQ9DGWyCu72wzaDvk=
+- o+GBljztfjUxFAbOcQ/xS5+QeQ5UiKcgrxZQqZ+mzlA=
+- gP+usTtGdcFs18SUjMF3RDV+CuIXRsXY975L9CKeWv0=
+- QXWMb1b/tLc3LGZGn+n4aKbOevB/+ruSxMRiLdF4hWg=
+- o842Bu7jIfNkDbodLvVd0b0solok2jzLtrxpK5EbZrk=
+- Z6g2Q840puWTvfLDpwaoDC93ClxPLhzFBJBz/XGvMO8=
+- X3NlwKecagWCwzeY+tskWNxCotYcxBJMTjDGjeqjk1c=
+- g7jhTMCIzCMI+SPCgPfOv6r1zj2voY4UnUKZx+ar/Kc=
+- WGrr1Ic++0UEUZev7r4r+QCciWO8YhUJfsQ1C8C2EY8=
+- o+LuUFpEf/vZIoCcFP/thQ03+kkD+TroqX3GZM2o1tQ=
+- nwwTiA5Hdl5MS4bA4nkQtc+rwcYAM1VfZt48wUTGS3I=
+- eaSP7LYJa+RhTn79MnsoHoIHTjTco2Egl0JGcKAWp+s=
+- Y2Ll3tE1hMZAKBJl2W9MOjP4I5vDihRRnhgL0y7z4Rw=
+- h375BKppyh8cNr4P34iiX5u9dt/f7pWxb90Q/cTPva8=
+- qJVIpWAOY4IZdbQp+bBX+KfxyZp8EKTp8T3Dm/jBxD4=
+- K52FoFWpFrb31gNF+q2ebBThjEGOKfYx6PdXzU3wx5Q=
+- OvVBi3ZxK6WsMy3/HyWt9cSTtiv4D+pUedStSn5VKx4=
+- 6RyM7a8vUWgls8ugszfRFa4SHMPXk0M0/d/6qdcHdKk=
+- 5LNp3y8bNN5eNR7u8+/9aPXflqS5PgqY7JBHmXXkslY=
+- 6EYd7uXthXVVQ3GyIjzRNlLR2OYBVEkO4BNuM/bjS3U=
+- FZ2FaGPh0J1MsI1LBs8xLu0w3sspi1vScnxX0HYOt/c=
+- 2Kr8S4R8NR0zb9o6x3niSsky30WHsBm0+arNZ0rZd70=
+- DaC5Q/MirXKtM57JuFkkzGNJQkyxB2b6+5kn2G0GqT8=
+- auIXFkoeO/5cXIm0JgqiCmD7tzUCCztlKnf5zDyoklU=
+- bF8oWp7b2r38rPqNl5nWyWcdlpWsfTRonIN9N3xaSU4=
+- QZTRcG7R9AjV4C1nJ3cBn01ThcdmqMbKisujFn02p7k=
+- 97zS1ONydvS6VyiLOwTCmDhk5bi2ea8tNrLn2CUmbtw=
+- HDEt1kJqFme+0KVxKJfIZP9JdJqY7ZQ50iYXzqvPBnw=
+- AfccmiurLBSDfWPr9DlZj0BYwE/WRdEbeU5wxpADgIY=
+- 0lsbU/7aO9Uph0Izi0sLdFZH2HfdOBEpvSwK6v6P9kw=
+- GL9u3ISAzzrzxIgGj85uNC1L1bfssk6ZHd6WttiSYO8=
+- SB7DEMKnfFKmEYLEHwy/oK0Qs7DwpEyvv+yrsjloFu0=
+- Hju4lHVvjWK1dZyxu+jlKSmSHuQKK180FHqZ2COtT6o=
+- i/TexUXhBbtU2vz+ZDa2eri/DAHXtXXYZYEGYbhY2G8=
+- cVqXSRxqOrWslm4feslCvlkThdImxk52pVAmxr026b0=
+- cW+f+Skxx5wdTiON2hWnvUE8z9Sn+lGu8pq8pE/SasU=
+- fcrJGqyR1+UTjI0SI8SpiFD/xXv+Nqjkpb0QEn2J4ws=
+- 9kSz0HcC3Ivp2MLuOnB6JQwsi/NFD9psvBm2eSLLDlU=
+- iTUmIaLltpiirM1JlYvaUO6XyN+BFOxUjLZltMLFjlk=
+- kBWzUCohdZfd/4cZOgwhn38cfKVLlEsQSIzNUMdyIzc=
+- c5jPEPKa/XFe13sHnLv6RyJxz9jkuni4n3LDRQMl+I4=
+- "+m5eWhiorxa5gjJI4y/ofZGlp7AODkXO3BYKLCvMf/Q="
+- khBIh2d60id47Zg5pBOqN74MXHLV22DFRstmQEndyAQ=
+- h2jO0l5jbt97LUoH/H3XTWbE6Ji1N78Ei1eJlO2RNgk=
+- ZnjUPnQtPzvgGo3oh2GHwAqfcWJCdEgiYclGSeb24EY=
+- FuU56IOiItEpBf56W6Fjj42VyppkWcAju61e9fk4pxE=
+- JD0CPMKyqDLpcXFEUWjdYibwZdz/aROnETWM7l7Cqdc=
+- SdYhMl61WmhHb7EHtticw6N/kxkox1I5haKKny9H3s4=
+- I84H/ofbhaZp1XImnECkDwdx4YGjnWD2CGUBoRLFG+c=
+- xlWNLBFoIjQ+AnLZQYJUzPaW8AWXv5bBACbaBYWQrOc=
+- 42+r2h8DYMjODgmY8kKCgC1K1qT4Xbzpcda58sYI9MU=
+- "+BWuYXw2Q0boxmGwenoTffCl05wRHt6WGI4GQdJieTY="
+- LYpBMieqJ9LSLIDPgXVAni6T558LclLWfPvdzBNJZho=
+- giNKPeUgrEPli3r0CeKC53llyuWoZt0HtwFyLiucRTs=
+- cXmwswmIAcK5Y4EreCf2fxOZ/TygOG9PbcXxsz6a9aA=
+- sCToQFsSdTvT0J34RJHU4RQD/ZsGq8PwI+kL+EWBC1M=
+- 4JjH+yI9EEGWqrcpe0c96TrFOMlZ7XBfZpLMh8XSYNM=
+- 9xApE5M9PX1rBdQBzPXB+MuQ1ZZqb9fN6Vbsh+BJ/P8=
+- JnXnMonAdODGstdS1IzfhcelhpL1qijULnsfMTtPaqI=
+- jSlOtVbffmLqMr3GUeaaaA7NnBBc7TYv5Ayb9hdGehw=
+- 5UaHxy8VWr+SWWbpO2Qby+axvfec+AduYH+gbieK9eA=
+- "/MoogEXt2GqYTPUAf6oQuDh4QrX2Nkt9pY6sK6lTJ+4="
+- s0Vk8cTNHZjcJqqk+IjjAgZWAzrdTdBiCyboIEk79cI=
+- NHyaOpHO7pYGU9bI73JEBfaJYnnOctjnf8dx964rC9Q=
+- nbjNaXqJu2baamMcwu6EGAoO1v3/naCuhOdbf494MOE=
+- eoJLt0+yFG+afLAKK1mcu5uItvTSxXSwiuoPW4dDh4o=
+- M9c+I0EcWTJyQd8cbOShT1dptg2w6biM9p+GTAXdZ/4=
+- Vg0j1/Fx/ihnGKOFq0QhlyNZCsuVwR+CkmlM5iDajfw=
+- C30K1mM9EkuXx033yOJFXEBIyNqSjeo6DP/16AzOH58=
+- aaXXNYgsjGEq3hzuwX6f1EALUDtQDKLGuYRIK7jQdg4=
+- HShxXkzXY12uGTOx4ImPdlvfrgAANQ9KSBcFiCEPctM=
+- HIgWjFJyudN9Do77V7GyRgd+YgkXEY0RZliTaa+PHGI=
+- GIdiR+YMUcHTWrhZ+LWkgdng4v1C+6zfVaBTQfW8chg=
+- 2K7MZSzk+zxjCVIWyxUnzXsVmWDapTdn+5aEB6X2jx8=
+- Fa+JUMiw7GKAuwOeWiH41mZbLdVxa+2Q14ti+tRT+cY=
+- 9kv4GlMVwykUFZhYHbNkDzONxmJsB56ufYLq7czuHnM=
+- RxcveXp13F/BkzxPy0yYHFuOtMZgnLXZNxx/VRgf5Mw=
+- IDdk4wB4PCC0twk/9Mdt6DYVzjG40inppvJRb6elSdA=
+- Epi0BcFr920vm2IU1aAfRdjeA88cfaHr6M5d+KUf/NM=
+- "+CQSfDnPFIHa9HUlv6l+USo2z6/lcdJxaZAJsa3L7lQ="
+- LR+dsTkgat4q3xGpJnv2vVUaWrkirCDPluT04xvgji4=
+- W6EC3ISSkS/M9TG67yX5Semj62o6uDBJ+lxVHot2Z0c=
+- mmWdFJmQN//6NRo5ixo0lTTZ3xzN6WUzzPHlBonO5ZA=
+- sBDvM3p6HFonREs9pKZKIlJm75Hwz/jtjiYuhzUzAOw=
+- u6ZU2f4KeHwyzqW4bA5IKnpS69l7OeC8Lm6uEwTlVFs=
+- 6QDwwKQKGJJ0sR37YPC5F0iO+/6rIpkamMEQen8lVwg=
+- 3YIHjDTrf7OsPFA9Z2TsjF+MooZbOv6UqSX3GdtXi48=
+- 1dOvShDFrvCwqVUgMCF/naWJDnkMJH84cbiS5d0KTCg=
+- 7ii7HOuIkrxch0wnz2iuyMfkkOhAVk+swAps37V5bRo=
+- vKK0Gisl4TfIP+40ave9Hg9SvVYFg8oHobQvmUTFxQs=
+- bqDipIQpzgRDvrRw4Echa6N7HfnD9olzuW6HctVPFyc=
+- 87LOGkh7yIdHToWSf6S8yk8/KrLp71MLMfSERH8eZSY=
+- H1qP501tMelooNzDNBrzxv4WJ3Du92BNCiCUn1RpoQg=
+- G1hD930uqmjDSgoVMYenTDLfhBRJDjaLV6sbpnejcPc=
+- vFXdOEbjSUCGcLb0Z31Dl9IOeA5I0l3enLeMZdI9Ig4=
+- qN1CeE8F6Wy1MwGMzgdZXIah1zG/Jijls96jLN5rjFo=
+- NwCt8fJfq4ICwTQ8SwtOP+xwbVfK1XQIZGe4s93yc+w=
+- TJGNPzGtAD1/1EnZ/ES2YX1HZdpXbvzDa+HRqE3lEn0=
+- cORI7zCZ39JII9bNAplMrDz5AydOogp+me0NeHFRrks=
+- 2h5n15RDgby8u6fXTfTR48YbRv2Ku54TvTEl6ddYKUU=
+- BWyny2D8pzaDqKy545g/zhL/FzKkhCRaXhbd0kxMO+Y=
+- lcSovp0/Ogrd7LHLowIqfu1LQtHR7PSBdsbDKowwhUY=
+- hhZYOCrrVuWstzuJODp6wbaysTOADNyeg9DymyPAXRw=
+- m/KW39XjIusEiH0vRw3SKVZ4HBh1Cuaki+fEywtR1hQ=
+- 7iwhGn/y+Y67JiOmKtbWqMX94g4WcCE+0X/zds4y2bU=
+- enrb7Gas+TRIXNiiOefjRRLdDmgeEIS5rTPG/JrvN6o=
+- Ro/INJFJPAenQhAfLbbhMOHEAZsfOYYKlmEzf/q+L28=
+- XLLY4J9tK0hZ8iW0B3G4QIxE/JG20wTQzY0xqcLIeiI=
+- E0By80Z9cOsho66QThheQf7eBU0ES4bAJMOARRAntz4=
+- nPubmHSw0H4evIAOn1asi3JiqbmM/blh55J+mY0g5nw=
+- O6fcBlJU4uSSxMIr1NPHozmxdclv+LSkmbxegwZ0MgI=
+- h7ft7IkEKSkA4/EkAAzxvpYXYogJ9QwQydjAOriRm10=
+- "+s64ZLtg2b7KZN1ejTzpM4y+rgZpvB9BqgJzsmQ6D3U="
+- tsdLlBJQ1hX6usbxi4yrKEKqibC/NRLL4UMpYIRSWGs=
+- CBWcgABdBLujocLHBEX4DiIH6jwYGgydE2rq8fK72Ls=
+- nS5ipUvaBeJu4IteDx68VUVOPpDhwoDAyWKAnVugEGg=
+- CGwaclbr99YVm1N9qzqKuJhNn/Ac16uinzELhNxyG3o=
+- iqH3IsQodCsGD1oWkXjW28WeJKHrWSUsZlzP4rWz3D4=
+- 6wzsIgm2BYdFAi+7jJDad1wwinc8DljXEQEThyM+ZSg=
+- kWt66Rv9s+KXPTUEas+u9TLaOvprNCgM86G/uH9hesQ=
+- NsCWWkxdabtW5ctqReNdRcVfk8kSsJYT4M0kzM1Vjq0=
+- q4H44VGGpI1LMSNWLhKfEr17N5zqwFNnFD46ZAc8p+4=
+- qKtnVBMGfwzGkm8WV35TYv9wMwyzGmkoWivNdbB/DXc=
+- 5A2uDvXbkTJZoRo+uqgzStQaA49S/uadB/kQp0qu8sM=
+- 1OFplN+QAuUylPiZcU1xN1kv0TuphvzQcCFNyF1lmWg=
+- LH0LsAG+GLbUvzvhyQ8Ukud0e+Nk4cVLqiGKIeJpfyE=
+- ppJjiajQpbvj8wbrsCxQB8ADCWecGeIqLfSb0AeRxrk=
+- KWiED+5fu+ATs1vYeejgxeiZ6/ppNnYyvYm4/EBT1ok=
+- xZC9YxZDJRaC4f+GkM2W6b18cp+y08J5xpBPpXGCL70=
+- y7mmw6Nz6nDa9bsRFFGIVYGh1/mn4dxz4gpwU2r5zW0=
+- MaiX1HmyqZD8k4rO6oWfqlAgq50K+/VtSYRi9smW5KY=
+- dmvZYUwTssLAs6o5hEXFuM4tBsipxEdGwQQTW8roO/I=
+- R0JEkFXeyKgO358pYlc5vOA9D13Hy+b2U3oQnk4Cce0=
+- "+DHhrwHeGlVVeTNT7ng7319lvGLzRg8NGXW+RmtlL9s="
+- gqeSaiKPh0aM+U/T1a7sYhqj27vEZ1BI5XlAzNyocK4=
+- GhabqvJZCzIqAAwdifMtXkOAjEgrTec9yJ4/o0bpR2E=
+- "/Tu8Lj+OaGxnVMHEI+vz1P/AdVZpNu9alSUmzlrN1Tw="
+- JNtFCFlYDe2aR8kvny56yDIrcOm6Mi/+XgV0nVnYkew=
+- M4NaWkqFZYAXaMK/XTEoTR/JnAYXNyKkYzFA14k7ZOY=
+- wgUnW0A1ohzq2pffSt1mpRnUhvWyd/3qiW+LsThHFdg=
+- Gq8O5RfC1GCKdoBziLyAAoFY3+g57bXG3zENs2KXAsU=
+- UjryrxH/MVGp6vLzQCh1vum3GJwpZUXKMDnVfhHtI8o=
+- "+416vYoAz91hRCa6lsAZ/Dnb0plO0Z2u2WY7RLEck7s="
+- KFZ9+bCO76fWPbJVlsLTZHa60xh4aLOXNuZ1JoOvNbo=
+- 79LXCyBJhUF9+XT0YkqJK6EO0TCS8x2ZrUj934lJ7Yg=
+- 7BDEAbi5FHrBcEH9em9VlMHCtJGx9Zdff45dBxWRgV0=
+- OtIkdteKlvFmGvVAwGbZ3lRXr7I8oY8DiOJJiMl/C4w=
+- 8QE7jJ496s5iwWLDQEHl/ANpBtgZVlxXz0jNYF2lezg=
+- EtDjTRhuHPa/iAcG/S+iyGSFJwli4QY9zkFhwpTdXr4=
+- "+lKNOt72HRmag0j/tI7wFDYD6i4K0AcSHDntxuOutWc="
+- dZZyIK0VQ4Tj+44Oy3315sCaVlyIBB/+ASFXS0RtgsQ=
+- ZnbqWsr7IryMJCIVQlnl2V8ay0SGdjvbfv/JhRvF67U=
+- 6KGZwpMRHP3sK7Ei/AmPdPswA/IKHCYJfgtQAtaRuTc=
+- FD0cIEFplJdiFF56fR2XY/7BJQqVNG8S+wNtt4qjs6E=
+- OMKDfq0YUP7UDyEK/5jZitrnU5heMijauHxBhdPVGJw=
+- iWG08JO3dgJEmg0wGe64/MGvBGpcp2PH1cBAKwWGRqo=
+- 9J77eQGf2Zrqez2KKlph42mryzmERJh4McnFWwAaA9s=
+- AqSlUlOcdWlQq42uluJRSYgut1/uGn9lPKkLa9PXvxA=
+- M3eq/s0J7Dfs+6ITnv6GIFjNfewRc6OTswslMJ+VD/0=
+- sU0yDqbwqoN+xkbqeA1eguUUQWegjk1x/z3P7e8skck=
+- vxQDxoJOguKT0uGRWfe0V7n2ziF111wlFwMmV9xHqos=
+- mu82NB70vK6O3DzTmK5XSkbpFF5Tj6rtt2AVsX85bVI=
+- 3A9/kGwW0cDYQu4N+4ieah3gAAm8GxI46xcJhcKq730=
+- "/QJmOfpBzqr74fFY18YREVS2rlp9T2MZGjb6+QblcHw="
+- wBHpjgN/GfAifPdSdE4/oRR6lwUmTQX1BLRGx7I3hVU=
+- f0yQ1ZwnMZY6GLviTpHOcocN2HIXblhkrreqigC3dTY=
+- idl0O3k7Iq65qBQqvVn99M2r/dAXlsMb51h8EU4NN8E=
+- UbdjaLOGgvyT1EEY0R5qZ1tf2nrbagkXdwwES2ORAFs=
+- KyB233k7pf2CyYwTWq4fA9BEt2BJxOB0FIRcSg19p4s=
+- EMyHQU9O7jBMQiBi3wRG5DUOEBzKy6heahHulVIIhN8=
+- I1uj+AM3Sz+OsFZfpyoyIzl9EaWG/4CPLkk8FNRxdi4=
+- dK8eou/cHJFC1w1Iq9Aaug8fYqNtBJwIlsdhACtISjg=
+- "+eLq5ok6duQ8AvtkwfKCHAFnhXiPw1eTu7u+5RlSXWA="
+- WG/JejRLINsuC9UgSm8RowQqa6YMxIR/OZIsfueLyIk=
+- wBqGzImBBP0QhmIgX8W7chI37ebdDWfF3TZ5j4br6pY=
+- 9hSi8ysYuj2sfHynrCvwSBA3xmJ5G48fgu7VXjMMiF4=
+- rl3Xu5clDu00j+jMY/WwHBOEVVmc7DEVo7wjZp4OZCI=
+- wx1tBzMfCanGHHjQ0FXMye+NnXYTxvZiPans8WSOiE8=
+- y9QnODWMTW0EHOmtAPoO5bj7YeuCdhPjK10kNjvQWZk=
+- vhxs66sgmRRQclM/L/EC1HGzJAGGRx5Qz7EeydV0Tyw=
+- 8EBNEUIsPF17haSWc7U0ToUZhJxIACEOKknsFb8vCCk=
+- 2iRuQPCsq0d2wGVqSn2GHdWbmQep/7aiyV51YtK7rOU=
+- K4OC+Wu0I8NwNRtbxh0QkN7gW7Xzc2x4IcrMG6qrWvU=
+- y0Lkri71F0tm90T43ZQq5HVg+4PGlFXf5o1etD599JE=
+- kjrONBC3SMnSd7Pv4vw+mSklejxk3Y/4ZliETrZGHX8=
+- K2KZIQIc5c4c9WEQ6zyX2/rjIShbW05ekarhVSc0ZN0=
+- 8qpu8Ux+mixcjwgZAv8wy4zdi+bwfyu7I+qdUjjrsHs=
+- ORC9o1lrvQSM+5fUXgZ1t8wHrYmq4rPmoaqWs36Anvk=
+- Jbf8hvC4YL1TBp20xA98xHt+Mcn7BNHNOTQJmYU8Vm8=
+- Rc0Ohu9i7dU42NhzpzktNewVUPpWV3WZSG0XDCWl1og=
+- WEgx6Bba2SWrvPXABszXI42B47f9CuUy9VoNNa2qd4k=
+- PtsipqcYmlXgP/kOzMNYCKapEhyklQJ1YIQ1b0iTb0o=
+- 7ANznY2U/zkWLD86M9uK/HsulnzdQSJLtrxVXb2vwe4=
+- 2qAM1ke/IZJaa8+56XxSfsO7AI6ZZlxe1F0CMmhW0Hw=
+- PCbHamx8rl8VcsG9+gumDBGe/rt+Yq58tb5IpMwj3+M=
+- UCHCZQuK8//Etp2DjxLyfF1jkK2p8N5WLiVPmHeRH0Y=
+- dFCUr/27C3ui2sCWHQA1ojkirThaDh86Cchg8Lsfbgo=
+- '09Idg6NDbwNCyPRAABkc2h5phZwcXc7hix8knE500sc='
+- hW1ToavAf/o/53LpPijOPvUChT7nyExtEwHmgod9gjA=
+- uWEYiUB7R9V0gRLkthQ4q2bhKYlT9EogvUGVvohNjVQ=
+- Z8/b1CoVuAi/PW6CYqtReLH9+ub2WfgsHqOzEjwfiak=
+- uIpUyMWsla5YGPVWrGxsMBxZe03k8i/s4PzgEOCkesA=
+- IjV28KVXs9N2hcB+59mvrjDnuKYHrK6OgzhXJZJEJUI=
+- Wa/IN6L5lw6xUibzaoW4+H+r3Kp8lGSBZrdChehdjN8=
+- SI5BXT1FyWU4zobywi+IwX7UDUt2C1iC0J0oF0B5LFw=
+- OG8al2VFFUBLW0STp1M/COSf9eYKNAeY+b3ly3FRbs0=
+- QSh5A4Aq/ssz4c+3DXoohbaWzyhe4GOYbm67t2cFQcA=
+- biui1ht2ZCtPXSdtftq+aM8bX4eOdIFJT8i45tY8Vnw=
+- 73SOf+TseWuG5ALWU/sDHFNjOeqZNaUadd1jl59rqOE=
+- pJdvHzpSjP06oyuxhFcG0fc2k2QFftBskEBD7nW4Kqw=
+- vXyu+dOUam3/DC8LvF8A+RxZbnz9WqpFjoAW/JRaIGA=
+- NLgP3gAE5beq692T8qzYi80IoEJnDsa2hsuCGWXMD0g=
+- 5+2g3BXC3zDD1BupBgWrmcqnbR+X5JCynYXp1W5KsyE=
+- loQSyXy0OKLP7Li3wl1obo5cmxHRIrwAyBa/rDhRUOo=
+- Q1xri6TgeGKtp6IXB1ISOh67QAggmCuk4EVESJlif60=
+- GFKL0GQcrL1bf1s91YU2GiBKfnvyfeZPLksZDsMTU60=
+- 0K89kDKywPPcH5SRgk6b9rw54LzaRmj3uQHT+PqqEhM=
+- "/QUh39zcgwUMmDha8DvAkmCm+fQcuDsy7f2tirg4rh4="
+- gc0eQspS1S/kWhrCAt1XknOcOz4LFDD0OHXg+aLBZ4o=
+- Tvpce2Gv4rVpgyy675K1IwmvtyopMkPuN1IEm3wry2M=
+- VS3SObO9ejN5sXd5vtdUawhZAy+oqFK4+58N+ySoY3w=
+- cUHdXSeDi5D/GIricwV1Sxm23U5q1mmYGjZheHBBebM=
+- pAcNhUD54w7NycbwbGKenFLu4zL/ncemP3laMl4MtH0=
+- CQlSRfrDFCxl+G7r22dkRPguJD2KYR+5La2zUt+JwJw=
+- TXwmVxIbEBS0aV+ouzQHkWTAwksGJHVVzFLCfJexOBY=
+- ekg1MzDJ9LFJ2DqwR9DTviGIMwYreqk48eF2Eu+/PI0=
+- 0b+EQgALTPG7J2jrx8wbOLRnWSBlQyXfPUkdHEb3yzU=
+- fOKuosEMRJhVTnIeuv3M8G9jWOimUGxM8d5h6gOFjuk=
+- LnejR38BfKPht911CD757PJiyb79jqBp13xHCZmRIyA=
+- VwytDJsKArzwLJY1SdwiBtmkgNLVP5hIfzHr/HYIZtM=
+- vTF8+wVJYhDbXQkNMkmRGP/NwnTfZH8L5NzQu72hPMQ=
+- zCltpEoGVskm7/H9qZSC5Bc0q7d8bLgJ9Gqq6J4bZbs=
+- aw0CuJ4Vtp2KlqbO2VYEpgFCBxQ2mpDrwR0psChjLug=
+- D1mNAzHgQJlPwjvCeZ77FQ75AnviSSrFj7zYGrylG4Y=
+- JALqMvj7UzZvl/Kh36A5kGkMAHngEUIj/6BP174cfhY=
+- STZ1oVa99KesLT7cp6oaGWwPNEavGJXbMJ/QQkv48h4=
+- rCDLBN/tvwN1wRnw5X+H9+KDE4JabDfoDKxJPvi33pw=
+- zV4mTtNEIpHmcQ7MMpMyKsbTj7CooFSWamoCjdN5RSE=
+- S2wm0bf03lNvtcxJZVpYDc7/25aj5drw0IIUEdhy0EI=
+- xE+5czyLhPUjwOu+3aCV2yNeOJ3HfHE4Ifd6MeAK3f8=
+- qbowz/yFNRspPsEOa7L9GqpDKQ6wAzz3F7MCATc8jVA=
+- WWRenwk+4tACIQCeQmg1n0OlYbwUTwH4AzSzjy/xzOs=
+- D3qXie6Qw+4yhuXm8/x0hM1BZVj1kOIIRnPEWdcipUA=
+- V0xnzVabqbxHU+7kBksXOjGiY4Vapv+GmrzrrtRWNg0=
+- tJB/oXuOmJAzwGCMostxZq71A29ovmlDeAlj7VVA50U=
+- ct69uVkbwo4gRCyH9j3Ylp3HZemRBP8qugEji6bUfd0=
+- IUE7WgFtaP4ERl9f3Lz2eLMyHD2nBBCh9rREa7ms3DA=
+- zCokcuU17Q+8326Vu5ilGfcda0Sqj+KFvT4g4RK67SM=
+- ti8stl0q3CQNLPi+Ebl4iBNOTwdcRkIi0l+134FuLKM=
+- 5PNatTM76FFyLu/TPuzvGO+hUgtkwWcLbk5+7YUQ6gs=
+- 1RqwAemqg9qacolVDN88kJN3cl9/Qg6cvzjuxVxsn1I=
+- 3ol+dL8XntDtHhzuh8BALPbyES8lHmAC/S1iczj+TeY=
+- VjHCXSghON8MJSlMVGswU13rN41GT0SscGlcVniWa+8=
+- N9PqadyG56fcIJWj8dn/uzD6xa84UGRR+6mm9IvvoS0=
+- VaDShZL2pWwQ/bqDnOW434fFWYYH2jVIpaDchCa3gqU=
+- S4Pt0kr+SUSKDV5Xf7+8wPmQqBdOS57r2maRu6iFTHU=
+- 6FLHOThm2Y94Q7OuuPRQ0XXF5K27vzcSt4zHo+X+1qw=
+- "/eGn5aTtMl/Be+p7vTJ0CTciCZkIfqSfALPzB5XmXZ0="
+- w2EmYb3+s2vpb5B4aApNAwp1YVMur2jRjcbLhG/P8JY=
+- N/v8Jiy5ywpL4UGpF6ga105iak1/id5cwvjaJ/YYVkw=
+- ZiCTQz3N2gZMz4ShKX0RoTtKLn4+5+kGLfy0vk0Efwk=
+- wfBEQibtvddwawDUicLKticxBpfyloIBHaeRJuwmCDk=
+- gxiffGrlT2l2D4605xOyax//hFKIuoe+r1bBTEX7fsg=
+- hxm74kuCWBe8MpyTmzaFGFpvRaGBnNUDBvkhhCFK9hg=
+- 3i64MDKLJqprrsXJfKa7hQqcdl5I+zIkgjM8ykbMME8=
+- V2eGvospHMuG7qtq3KQQRV2GNQLB+BOiLtUhFtrjKgo=
+- Lin034mcVJgDT0bV0IwUDtZ6beTXoNCtntIqidFNdUY=
+- oby8ETgTzyqOL4ahYjXCnGXq1ACtN9dJp353YT6P9po=
+- k8+0ggg7z/HASOa6LKvBBwReKam7OG3KMtZmmaraCR8=
+- Xtx784aAY/UqNNvTKaAVbXY41vsp42TpPSZsX9dO9kQ=
+- 6DF26u/MGujEoj28c+vPEi8mz7m6XHz0dj6WwcOKbGw=
+- EeKiRAQho4QDe5tTAKB1B15Q/+7V3+Zj1PzRWMRJbDQ=
+- EgdlISAJmCbBtCebZEGmDxh/IEQi26LN6X9BGJFoTSc=
+- 3mEhj9jIeR3LBHatXZpkgnvlhFkTosVz/JyhnOCrK+c=
+- tYHaBeT+x9moPyygMJ5upfHvLlxNr6+/s6pcIY0necY=
+- lT2kHTcnCaZXBPCJ+SyPIWcJydpMwxaj5xLA3mwEhDM=
+- XzMg711BXtp4zVfoPfFIrIaQMuGVoogJStQf+F1grzo=
+- OEVH/lJO3TPADRmJy4NkWaN2E/WuaDPNsIFC6vN5Gck=
+- chr1URqt8dT0OHJ3cX/8EkPzaF1+OoVQBgSJ4y1a92A=
+- 3wblA8lRJRbuWazTKcTqoNyjZvOwbKr95c+4GsJ8HtM=
+- XlpEfLWaR8qBv8l5v8mBG57XXJFsG1YbqUDzzhaRIvY=
+- TtvI9gIAw99urMVhGG3JiXVSXuHHu0sopNZt7TNh4Ko=
+- QO/gIb7zQAj0ROIuzUfl0ngWXcqaIJawu+VZaJLkZ90=
+- xIqFGMmHSG871bHkhnZrxji64LrtzEfp/0fPnpRydOs=
+- RiCDuJcjzwZXOEwXOvBYWM7JatbdtDZmyhMMgjmSJls=
+- Igwsc6/Wd19REwIXfR1nTCvqOKW6Dr+z+UlDlA2kUVM=
+- Z/6IpRT7JN1jTJvRmWoR+ew/1+npxoaQLY7ZUZwALUI=
+- dLPH3iZYCGJiNaNlJjFM9UKwod5X7d70EX02V0+VpIc=
+- RHBMA4cDKVicya7k6urwkgTyGSuMe1lizsf/wwzPnsk=
+- E8Z6ANi1YPjwlhvfYgJx1HrFtyTEGhX8eC2DfqtfMBA=
+- SFjnhohcEPuRX9zAQs5vKXSH2aKZRzjHW83nugkb6xo=
+- PHNt6UcdXzWi465M1HOMGqbpxwR3Ek1b/lYfLCBM9/U=
+- PaEYGa2Mx6zbaGgLs7oMi0qe7KiWJYsa9IyDh9T6hN0=
+- CmTua5c3wz4JpZNNMXFd1tZcT4GETzVI7cpMBVCHsLE=
+- X4lJ2FdrGLsQgQey2h3joqutMKj0xeT1m8gsAlqAfr8=
+- Npp2MI/6He1tnBlMVzPQQi6sTXv5Ct+/D3uqU9LiZlM=
+- miWPCgosAZH6vBK9EUNm83Q9tl56sBzLd/vbZ7Eijwk=
+- 63f5grXDsjWL9xFiZ6GFZvUv2Ohj+jD0jtbbD81inQk=
+- 4Rao9VUJGNkhLhIXFma+7WyRDtNfO1YJDlMbORd80L0=
+- W6zGoQgukz9UAn/cV3lJpQWSgYiLwP3vSl+0v5KxPSU=
+- 0L/Nw2Gp4SEuekWgWvF5eu70a6LJ00cptGzWml/+u14=
+- uECaZDYas+DKL71884vdxcDdp7UPfZ9n+7JNynrzNDg=
+- uIbpAeN+oUkgKg89+AdPhMxLph+KobC/HfGUFDhjbVs=
+- QLOEEc7rLusmRKc2mNWUlOGmSlgiPKEHEW5K5wz2FZk=
+- UWuN+F7ZColcICwnJGfz1xnBBZD1v0BmIYcSEak1X8Q=
+- pjhAosUkOGCq88/Tm66DUcvJQwMSAyD1MGIESuZNAMA=
+- W+/b4B246In/R4vygzVYu+MQ57ZPYw5cx0PkjxJuGto=
+- MDRuzyYk+13K8TSt6C1WdZJJRX8HLFd5K0O35UN0ZLM=
+- zsKprJVHlkDHimXYrJn3a+GQMbnY+szAT8zXCGfy+1U=
+- E9ClN2i5RUqOS0td9ePmJJBOul2lOeRcH2loV34hvTg=
+- hBVcGXKtmYbFCpZovwwvKOE0Lyd4YfFPfpUPHNyalDs=
+- j3REnyTbTbBRrwDu25zDDBByQ6NfrN3egc0jA7CTAtY=
+- uNmB8cfDwk/uiG7jt3iJ7sgjWe2R9ZtXovXEaor922c=
+- fnf1Ob1veX7/D6jvVNx7e21QznPcFqmhJtNUZ/iOgKM=
+- TAU1us6mFxEFJsdIKRkdf9Vuh01BS9aJmPw7FAUB/qY=
+- TGlfgxUFKkXlsruullDwzYNw7UWhpNHa019T2za+Jb0=
+- 02Mn5cXJ3LcPF72WrYGRetK/3LzwUkc3itgD0dXQO/I=
+- V1LpRrDinYG0iKsWR9AwpwWEU+bTvG7OkXyQhuN8FDo=
+- "/aPG8B/A0FF4i2HV1NPwUzVK2Cwddzk29qyYpcOD/GM="
+- 7izx+iGXV+0Na8s2pZkc6WmUKu6mXSwKb7dlulVMXOc=
+- vVIQpXLi6A3nlN6V2vUigwXVLRVf9Oy/wY1xpIU9yPk=
+- 73R5z6Dy4wz7Z94g9HskZ6IbyDP1TuNOSbWgva4u0CI=
+- dC0AAd+8OZc2vhCl688HjL2ELW9RKjBE4aax1SNQhPA=
+- YkMAC4Ha+Wu99j78RDb8B8tFPfmKJxlvf1SanIACY1w=
+- htHkWmkwHKK9dHxcfJTuQWwMjeS9z7Vro90SpJbEV6s=
+- TW7qPGvfZlfYBXOJ3gcDEu7cK+8HTze+kggoKKkycO0=
+- WCLotMf3s4BvtTy7HY1qatFdzYki+k2RSbkqFh8QwI0=
+- NDx5He2hCQXpwDvMrrdUE8nulgr3sfIpH0rMmSXiBlo=
+- To/gRsCo+x26il2YJYAHd//kNwzSECfOpT/Txly6NVI=
+- uE3/JMFL+yLqIUs9+2dJHd3tYM4+ucw2+V5joNBL+PY=
+- UwNf8ct0CV8nXyFdsmCVvZJufbBaFhcVWVI+6fqWOsY=
+- ozx/B39Hi8WVQ4MeHhSSQs5/njwg11MHdh07pG9ABYQ=
+- yP0XWU28OtnZsINQH5VhSfP2iRjfH+D9LO8mi6nHno8=
+- HYs+NfMBpbL98wfytHgCCyELoBimwD7rfaWpWVy40gc=
+- WnN6Q6CR58dBEocSmgCRcjzed/5VpnJrlWtXzu76b8A=
+- Z3XXi79Lzl/d7117+hg+LbPFu+RhwZa5iFP8sksp7hA=
+- xunikcc6Y8osIzQA/PjzdxTeBORXo3+L8IgLd2MJm6M=
+- eP7n1Hx2SOW+imuMQ13B+xaI/8FRwDvpN0OJP1YcCLA=
+- uCHgPBK+MQGfR81vzkSM69P93ZTcfQ3Izjg88MTpV6Y=
+- tc37EsCCa1fGam6mBs1vYRBYZ2dQ2efV2yBeZ9FmFG8=
+- Lltkl52jZvqV/EHJG3AMRGWoyu1frel1bVro0GU5M4o=
+- sPFezeZ8wElWez9z7/jAonA1SnkFPAegRieio+SS12U=
+- 6yHQKGWk9+QIi8wGbZK4DE6KNXrxRdCjS57dj++elTs=
+- eLcXtq7uj2e9AGoTpxUPKvPPjPeJKp65kEPkXd7XwMo=
+- "/nBgtUvu+ySTSIIeMszoOQqA9Y7/7h6Va+968A+zlZM="
+- pgK3fJGKtbXh41H06at9bD8pR9yJm+1S6uZtEqT41FU=
+- Q/QAq0tsZ5uDA646RsbfFpHb8vg4Rplum5j9q/D6yjI=
+- FgBabr8QYwDE2jaA4kTY2nDgRqnJRGKOoHtToblpuIQ=
+- TSdJF0SqZyIEr5sgNoIbu6luGPr6J/yAoQvxJSbmeNs=
+- u9ADSz6aprXfU3U0eIU6DCvg6EtXOJtsoVwgGbzOBxg=
+- WTVwzoH6hzLFcGWrt2V/XPh9WSO8fdKeYG5v1hWejos=
+- jMe/ZxeTKmsTthjLAPpaPt3XQpZ14ztossHjt6iDCv0=
+- wX562p4JFmXg4h0eln5KETdlZ2mcqRtOA0WYHFUv3iw=
+- G4lNxLqDh13YVlldca2A90tIkWa9mqWJudU1NUduJ8Q=
+- 7IN1novHX9uk5BTqpf2cVvkbOUXqT6Z5wuUsSmHl7Q0=
+- UhIvADuT7NJzWLVdzxpoCYwYIhcr+eqwz3Kzz0oIl4g=
+- gciTuZpz5B55Bse4iMTLoproIfz/EVuFpcMZf8WDdtk=
+- PujF/XbxKS99NVivNpDO3Yd8GwzI+PVAETB2+gEZXhU=
+- jG1TpkCl+fojK01KY7scOzGx79x4DqbuITLaiVq+jBs=
+- uiF2euSUr+WiFl3LMzjFMj6ZBwUONFQsQF1XXMMb9Sc=
+- K4++zHdomSEc1bX04tSFrVXlUPxyf2UqJVYoqvz599A=
+- Aqd1Mv028ktTJURaNZK/zZhPfnxXBSy75SSdPl+OIpM=
+- hupUmkNVHVe7allrqW1Qp93Tf0RwJ8lLUiMvk6b3h9c=
+- v6vKdWIjkFLpRJJCz3yla+d7JXXOsZavsRx/QizCbRw=
+- vrCzVZ9VxHiWEK0/Cj69gYaPPL3t4pkKI9YShjkfAHU=
+- TxUOLze+obi3MKphN/BjwduAqaQArxf2sT7FsBAhL5E=
+- C51lW5o6QUQqtUaHNy02Zi6v5WWB5R2CjFl/Sw30b7k=
+- WopZgTTEPumc0lTQYpW4ZnsBEcr/1dBd3lPBF3TVhK4=
+- aXzM7rR2t0gCyLgswTPtCIr5yzjqFXzhlOZ7+UzfWJk=
+- Y31hEsxPB6aUkLkAmhPK3124AdipDhW593Z5M0FTjC4=
+- fE1XbyzQ6Yo5l1xWCZ/1O6XzQqpH8Ravf1zCAz2gv6M=
+- 7MV3mN6kqUxez3mBx5eqtSnZ86vwTVtswGp4Iy4gql0=
+- m1KJ+ievuZuQ6SS+t94iuBcnOmxv2kbWNB+c6w/iLAY=
+- 1TUy2NJp86eGkLoiYTFk0CbV31K5dl2Zz35ybZ9sCIc=
+- ivf9wcZQyxDVaFJAFLHiMUvbn9XsPR0pYYD4yAeDHs4=
+- R5aJEaPUCU3qcmDO82cNKbTTNfC8AxJLpka+F6ltb+Q=
+- LIaALlXKaI99txG3KYw3stcVzbs8u4GMW/onSEIbnyI=
+- zSe2pq+RaxGVIEzl9j2dgb7rQd/f6fLuRhwNksPdP58=
+- Qq8gUosWDmVM7x9yQiH9tumeT2znoHn5ZnVWOAhK2lY=
+- AdczJWUkVfJ9rRf20HFnvh6AQqa3TYqOUuV3W3zvuS8=
+- OxSPw7CeSWTA9LCIIL6NCYb//zSJp0C60ODaFNS5Na4=
+- ygn1obdhDORxgOT7gsbOF+oXiq0gv8jnZqfOBcpIrSU=
+- demQMb8LZUsk48slwEo+4QuuwZB/G04pDVqdux7alxw=
+- i5lqDCv5zvNQ+2adZkMfBsfCnRDXnCc16fhHhnrGU+o=
+- jV+DUEu4N5d469LY/9Hgf8hzBzZJKa0F3YGjzVtDL1Y=
+- 0eCFlA3xHfN010L/OFFTtac6vCbLJFJAQGIFOwqb7X0=
+- 6gG625Sv1J4+4yH3uYE6OXLJ996nlSeH0es5wqOS5EU=
+- abBgdfOwWwIyHdUkUTw39XCu+S9eAnJHw2oLc1mKQ2s=
+- fKGoyGi9PnOFap+FDtjusIOU/PhGF3hwvy5XKw8QpLo=
+- 56fxwHAgiKOqLutqJrhTfVsylhLnBbxbucWl/V8tJMY=
+- "/64XViHMtkCkFBOWdi1mb+KMTWcycQw3ACg9+9pDsrM="
+- 3bJ1EX83TizmzylqHgPatX4U7ac0OlXGOYKsUswrDv0=
+- JlBVhMZkNlsZmUoIiAdvAAq7ajwZ/alFGrG00TkHFdQ=
+- XJf74lxPAwFo7xJl91aldj5mEVML9JPgqxwgIkpZ6DE=
+- A+oPsoOVHNXjgnqUouAK//qG/UJqHQFfFZmpcKAZ01A=
+- 9nRQ3y2t38qDpGXVWH00N0ZkgRWIq5zww+TnfABb1ag=
+- WfndPLoy1s1TJGlU8baNQZqOquYlkgvU+x8eHeR9C6g=
+- BypjjSLr6FNqUxMJcMh7zPN7+MdmCnIyPXp3na4meNg=
+- ejlQNGC9FAcRqybRD4nu70LMd8Dicfip8P9/FGw6lt4=
+- FnXBEUIy+hG26Wv0KwlINl+WWpuDF1S/N03NfqJtfyg=
+- qC3NWWj7Re8enfuLIP9/ov/afwv/Bb+5VuoJiTz2pak=
+- F1rGR0bFqBtOSUafYi2nerihZtNhBGZIqn5o6wRM8Qw=
+- YteIPR/BZp+U7cov6ILnVtQnlD5ydF/lLBdzm+7FSKE=
+- 37PhW3hxuFwvnoO/9rkHP61iyWS7R9wEyp8etGzzUvE=
+- 0CksZdFoIDqBW96EmteWo1p6HaDnRjgRI9N4oMrLbSY=
+- vQle9SUfM0k0Atf2uAOClifokFZekrM+GONwzvN7I2I=
+- 73vtho1RQlClUbjliFkZkReeZzh9qvlCZczQung9Pfs=
+- "/aXcPWDAyFeMKDBuz+KOg/MN3PNM5zOtyTSwAjSIcgw="
+- aZrNwxGjsNry7XRB4sEyiUH92ySjJ+aBUgUaGLIx/Mo=
+- WoOGCya+sRMGhSjKWux3aQ7y0o01fhTzBfwv8gRnvaw=
+- HrpL/XCb2lOOTTTEXmfjzo3P7QmptfTmlEi78B94Eck=
+- yG2dAvSmXhGrCBvHQ4BHX28jRmeDZ/FAJEkWs7CCqk4=
+- "/CNDFHEopQXULrWeVlheOo4JLYFVOBHvtCHrUn0DiCY="
+- F4Zp7+JBmLVLsU3vfAwqnXDe80YZ0Wf5vhjUwk+XOoI=
+- 8GsputXV5UhoQNlV4tUqLhLS95awgAaq9xwnb2NUD4g=
+- uJK9U4F2z1UPlU7+B64ModnjdLYGTngzkGsNZ+bCZRY=
+- 4nNgC7kWanej/ou7189FZIvomc1GioTx1yLMXkKC148=
+- 6Cukd0G1lqlGlS47QRaj73H0SLVQPpf4tk194Z+tzvE=
+- OkICZ2mpuv4knNwHaTEjNvO6DyR4L8WIMYyyo7zjIZM=
+- G83m6rdYBeTb5woDfpm7BYIwCDJqLx1mV+N564Yb3W0=
+- ECLG8I5Bzt1EQajvOJMSSdGbzJmsu2FkYvbYsBlagbo=
+- BCq3yNUI4S2aoLU8PNheyQTMK53FfhbuPIk4o6huwqw=
+- G2MucKEqZnGWitalXjka9+eR6ZOW/RnUDFGoaUe7Xf8=
+- kY4ZQWqzgxvPGIXOYhMOUrhxFBI17R5cY8UACvF2a84=
+- qmdTCRaDzrtgSJihKpcP8aZajAko5UKNvmUKBES2Mw0=
+- G8QRmWYLVlBCKMqqILr2T+9Xx5m8M3Dt82JVNGrPpEU=
+- aqRecnXW6O8X8xuOrv+QtrwsD3809dMaatTq/6AQN0c=
+- mKa4beZ7Rd4RGDhLRc13Ygf3sHpq2bw1Nzc+qrcay1M=
+- Omfygcm7L4QQp8od9rTzrerCxKZFJPq5QegmukzHnKo=
+- CcQ2IqWk/KCS5vVavhR1HsXYer/Cs9WP2EmTnSw4MsQ=
+- 9z1bsXV+2J8H+UKVB7PWRnoDQMRSea4O41sEY6O5dds=
+- wn/YqJmh+WRwe/ynRVHaycnzbg6uVSdgx/yNHS2PuyQ=
+- "+ZC5bpbcTy1mXEY8zpnd1VAWskvFhz3S+y8Rbkf4wLw="
+- B7GGDCOGRcS77952ClLSjP9rlQqRTci/7l1vzRvkm3I=
+- yjPMDESDo3r8eWZIAcIso5KJi8R/mu905wqg7CIJ0JM=
+- 6atTfcT4ml0uopUY/Mc2I/YOxYWTysv3t8XzRFti9cY=
+- GkS0XunkmiOG74lkoM2Mshq7Qx7rVhsH4G7oJFg0Xw8=
+- HfvifWTghvqpoIxsGiZSaU5t5sTkQMsxTRW/pSn5cOo=
+- CWunfGiLprYPqc0QVsYgYsDtA9/v0yhCbzWYSg09u7E=
+- muniwt9o/Xj6HrOr4stCmbJEJbP8U+Wn5oYWN8Sw9QY=
+- wiyOmD6lvDED9LFFe1JiaJCH/naCL5NIZrtHutlH6Ec=
+- wvtodXwi6Yrblfl+YFDrY2gQ1BoQuAPlwEB8K33EWbA=
+- aXQoDQoX/jjyoqUIp5xuaqEDWXRJrHSFlu+popk7gbo=
+- EMZs5+T17/AwjcYMJTcT/JcJ21Ro4AlhkUKKSbTYu0w=
+- "/tutQ1hImf2jrSbGkDPeQToUUfI40RFuNsMFADQfByo="
+- O7aKzLqLKKBg6DEaEN8peU6nvum4e9k+repgpRS5LVM=
+- OzeOShB6quCGMPpVnXbBki4DbR5Ojm7Auy4wm5IMJnE=
+- b1oDRgxSQ4OpvNkObbEtXJ1yJlKeZfrS47h5JeXV+zc=
+- WLPC4O7dVtCuZKE0ncLsBAkNbWVoqiEbQEU17eO2UPg=
+- iMuLmOpDkYXL+Hp4zq+q4xYXxiicFykBSFHJnNrxPbk=
+- fGirhj+OUEC866P9Q+aJFdkG/b0GHxpQiESLB+Q3CGU=
+- rdQly9Ji7IHwq/Nb/iNcTi/8o+a5yCxe/WHg9QPoTeA=
+- D1VHDX21Iky26QX9VBGHXIrtRxo5sJaje5wP5DmVgGE=
+- sfcB3lj1PLtn1htgmUkfYG2hXlapVgg+fNpBcY7YWZA=
+- pLWaOTsQDvRXbPkMbLbjfpX8PHdU0VY+zy38vSmy+XM=
+- 2V098eXuKjYbPU/h/bXxjXfykrWTXioAIhbo5QgL5bw=
+- s6BZM4ZcXyx6/Vdxh6B42xFgppAf1n5QDJN47Zk0WTc=
+- Mp8AT6P62rGD1g/PUOSZl2w0eVdIZ8uiIN7M336tU40=
+- PpXzDWivkLsMJ+M8zrZ+kCyr3XW+Q6nsWe0ZOFSuenc=
+- j/QYG0T5kcM8sQIJlpIgWMOvDEl47GZuyzY0TDo3UzU=
+- 70E9aj9Nm+61rtN6enyOJNHzrjyXpw+EQRVsTgLRkiI=
+- qFl8kb52VrOFcqUiD0YLNMFtGj5BSX3AbYMPj7eky+E=
+- HPtynkp3VoOIkXJy75G1/20bMy3wOJwheaOhBMoDGbw=
+- xXr/prilPEdQ2/XC/KosaDrubIdTZbOglePiMAWjBS0=
+- VORjuy8gg2XRmJHkpJ2YewabhiyNjIodQheQ9HXWT+U=
+- rSQFO5x7KSQJ8eW1DJcRlGdknEndx4HlwoU6Cs8nuoU=
+- jwS4zLHooWcDHMQ8c66UEByC+n6GIv2ltVd5t/YNcIE=
+- e2X3yKAnGh2sH/lBkxrjM7C+siK26w+sOkgCFnsWixo=
+- RXfVTS9bA1zUDlq4LwO7xOeWWMm0OMCzcD2ilwxgBu0=
+- d/7xiqwwoQPn2LH/TX9mgoDFJo//jeEpO5yIS4e8U+E=
+- "/NBJmXl4TxyFF7t/ExwMY1Id+3vYJxhF9cFIupLXCJo="
+- 1p7a6RDy1cSlDtbrAM4VTlHYnoHQuQfk74WEncLpcag=
+- 6J1pc8NiCiCUJN9XMXEMOpLwEmLn8cATkIs2wRyuyMg=
+- b3+XOSYtdgILKzCEGDvy7GmgFV67CRk2HNhILfoJNWw=
+- dqf+2ze1IAL2d9uAir6D8rIfZgrdtly+5/1VZKsMEAw=
+- Ahcll7j8+9zafikUrWeSCpVxwMwyd6k0F0q/RqASt8o=
+- e2FwtrxMCeP/k3bl7cPHfNOywswlyN1S/y2AE0N/mhc=
+- DTYsLYd7mONyJSyIzcCQc6d+/qvdWMEAVaVUCmDa7ss=
+- xA94FGr6YU+6FJJeUWg1Jj4DlTjjpVC24hLuoQDHYw0=
+- MgMj98cPlwlhd+1gA6bJE+xmlp4trq0rA9m0+s5n1UI=
+- ZWODJtf11frmxIq6Hc21bFc7zeGQK7hMBdphiULLkaE=
+- czKm2dZPaiIV17hkNMwE/uccJQLWSxnBwTAde9MVPKc=
+- lvKIbAoxW7aKFrxTFgRmE+e2Xl2nGc82c2UkE0As77U=
+- YAc1a/gqXac3DGpmHhdUO+4tDxBIYy+jbC7nD4qbg4w=
+- MzMFI53EsX8fo+GIHNPttDbfqm/QmeME0+g4EMrBshk=
+- EQtOUBT4SSk4fILmnOy/y1q5xyV8IMRFh0euu1iGZzo=
+- zdoPIGU+8ZLL/tqdox995uElnrsc/bg5vnCwZ2zxSqw=
+- iZ6WX5Dl/rnlIVju/pxVt6/vhJ33g7ZB+3EHKJu/cag=
+- 3bA03QL86Bk4JR6MD5HD2gO51olEFeSf7M4DMCmJpqw=
+- 5rMBX0Cs4VKmflL7AzwSJM/ihRCpUaFT8so0ZJKdt8M=
+- rgmJ1OIERs+omJSeReIrnrspAkrojDRZ5oEgkAALKds=
+- HRsRdmAg+9gxCVT9xZpRZznnQNFVy/MUKeVykaMlcYU=
+- vKniV4PdM+aybWMhg5Yrfbvnzl4IUCRKqWTWO6L/Mqc=
+- vmh2in1QHjZ/rr6iRcLy8hhdHZQGEInS0s4lp6uqEjw=
+- xnKjVLaLJIoPl/UmQzVqJJuxsk7M6/caT5qnce25bu8=
+- eGLrt+RzFPXb5qNR8G5G5yxMDHm0mSXHPiqc4YsjVoQ=
+- NBDYpR34zl2TOL/o/FGBYHGcn22++kT9Lbv5+nY6Vn4=
+- 8nb618gGZKOrt6voFygJq4bwM8rEHhNVDpNM8xAz/2s=
+- 0Yo97OBEoJSyqDCQp4zlna91p0FxQ16fBlhdYwL1W+c=
+- I6WR5IpIA9uOO51krbijxdjEkAcfTeWoaPttWmMJ7gI=
+- zPyXDiFzgKnwMoo2+kkfW+QmzUsQJ+QQ7OHYBKsp3wg=
+- d3nUmFHIb+2Gjk9HSkwtvH5n3lUkYoJKWfs5JW/d/NM=
+- D1wcL1WBxJ+guLsdYUSCsjaLGv5Rk5ehT5R5e9QTcOs=
+- WLbiE6ZIVucwVVQ7jRWUOLkG2OWxZGe8TYMf/Zh43mw=
+- 5uZ8nUl6f2cHmEgrSeqmjjKCr8axoNtyivKovIMK1sc=
+- AKWPxlkyJQvfyXIKIpuXzV7/j88vtZM7/4Tsd2rU/vY=
+- 61RN8GnsTjRkim/ETZluEESJtGEmh48H/0XuGvh5GN0=
+- JAo37VRwZ4UhCB+dvRY6lGDcpvk4uwqbFY1DCgaNCII=
+- zwIGN7B6upCyPGTdCYqmw4v3Ebc/HrCUVjh6KN6ekfY=
+- 5Dr3iYt4kIHwGBaA3SZ8aP+L+TrBVa3zcceECdDk+KM=
+- 2WIHl3wnoeB7HV9yzPlR99Ri20naIpaWfmS0R4mQ4BQ=
+- Lok7A2XMFrI1vMxq2NdtlWE4Ni9TAsbm5Rep3eUi69U=
+- O9NOErQWEyq/1QBMS7h7b7gMMQzZAnrXVujrxyqXXiM=
+- JPhM2B/9AjVQkGgXEdD46OI1VpbSqb8DJ5/uLbRa/9w=
+- IgxCy2dHjNfCZAk38FB5SqPlQLL5QDocs8qGA4Tri+o=
+- eC6bEUmbhdVLOe+6qNOAJZSNGNk5nokE6MrjC69De34=
+- CRt3S3OayO86+LsI5MYrtvy75KHJs9bcYZh+e6GPKto=
+- 0vCSHeVPclrVv+Jcl1lMzylf3ECqcL7mSphbXoLY5ic=
+- tWunK1yM8FDEnSVGL7WmCxiqB6pmqKSYXlKPKH3uwXQ=
+- 0MO9Ew/OO+c81xPEVXDpQPn3q5wKkwnZDGF6w6y+p98=
+- G77K6mlZNVwRU6PAzwPxnZnJJf5uVLM7YgkSBuR0Qfw=
+- ys/VJEADRN5RLP+n3Q0X6izIRr2fp5pmgfOBIHPU4H4=
+- Ytm1ayOp7hinI77UIlqxnkNxsvDk9MgwK+Kpdv85WhY=
+- AyzBH+elfn0KRMfayhul10rBRKiuCRJwU8Q2kCOrb8s=
+- lbOvMJkmFpVVWxKIawdYE/mkHJ2mKPIJLWI8QBILoDA=
+- Q3mx0goFqBfW2pizSbe0U7ZwZRFXiRgp1N+P9i6k0KA=
+- fDbFsPtrQ/o0OQdLxs8UDHop6rvQtsiFQ+zgDnj1+vQ=
+- lTEk9ZZQgRLPDGt7QQpZZG1NTv32WmeVBy6z7bAOtu8=
+- Bkf0sL/UmMN4PWlVYVmVmVTtpewByGE1J6VztMJsorA=
+- ZvH6gy1hmxxPU2MbHuCc22USTcip5ZiK/KMh21KYY0Q=
+- CZOPkSjQDZc/XPMYs0PgI2OKqIqT/Ycu2Z0hNGC3Vks=
+- B/UxYgNn4enOIirL7fOq8dbh0w/bY3KkFjM1ZxWRWIk=
+- 7vocfgyWS7ujpvA9QTJdQAgFD1AGLkPjvNnr9/dJaME=
+- CXy2wyWuv1MUyE6oJScVxSHj7hIBTR97I+mRDnQekkk=
+- AFSn3p1P3c04SrvFn+m92WoZJsNt+YcJEkwf8XRs0Mk=
+- GF79TOh2tpjP1x7xwQMRV8ecumLFEFx14CcPGhmDBh0=
+- lFcpmymsS6WpbHwr60l7GH0HRZiJn/V0ugGbOEq/62w=
+- "/j6Rr7CldvpTwJy65BUZvK5Ln169vR/7P08PGzqsWUY="
+- 4WiJe7STV/YMW3SQDFofB8OnlqctvdeEOqMSkWMs1/g=
+- nhXsgQ0kj9IDDDBrWCvzT9tTX5DRyTcDqePr0hJDbRE=
+- 7unG4ZBvblS0lnPw0bVuBS3irc6N9fM9YGybu8XmsEs=
+- GwHjChn5C1OhuILspl0oAsiTY3ZcktFtgbCTdjecgLo=
+- hmJGfcfJ6rajldxzWw460RCCzDWV38OmyJOqjNhmSf4=
+- MIlY6GXMyQ0cxB0peUk/6YYf4uZiG4lqgxxOk5fU7Eg=
+- s8Ka6ltQyF5KjWEQ+e9vzkNzQP8E+nI7ut3sEdeiDX0=
+- Cad/ROuOb+5kCrz6ZiachYW/wgVOQUUdzVqWBHDaHEw=
+- unXNOeqpHsae8gSqB+v/0kUr62yg/b5c26gb1zH0dG4=
+- X1cqDaQDsXqSUCef7rqKeXw4b0doEkexkPDYaemdSMY=
+- M735BEiyPUO7Xvuu9Zc3xLpJpz5XZAuZV4pyEEgix+w=
+- IOnBQizTRYfjn3TqyJGqckziA98wNCKTRZ3m+rNG60c=
+- cp72QrniROkpozj6e4ObFwg/l1VcjYq4VaO7lBYpkRM=
+- rdx+lF1hLa1sLuv2LVpyrRiSOv0ZWUN1BnKWcQ1WzNA=
+- s/iI/Y4rmIxyyqEuOK21fbA9WMtfOBe+QcXU3e9F0fI=
+- lM6jBeKpH+J4+tIAF5NRZx8xrfq9UglrwTBduaR61Vg=
+- doxa667dfdSpe/ZK9fJgZ9deTmHohL28o3QszHxM7sw=
+- tZQ+YEgeK/c6tRo4ftgtqHV8Gh6kmd/tCcUX5/ZGU40=
+- sbLBx93BTdfCmdz8HuxaR8gUh8ge85RfyTYV7Q/siBQ=
+- SgGxQJblE57EU66bXvprzz6rGJmYbuZNJxEV5Y4WlZ8=
+- FPtCbwLvyIpW7Sl+J+RozcsoLZxlyW/ivbXzHmA5XgQ=
+- h+bDQ0ygzydiLOuMXklj9WCRraXpSaLXgRnTMmM2jIQ=
+- iwBkgHWbs0GZ9TveQ6uYlC/tMdJOEl5B8f9Kp7CZixY=
+- zvcPa4GK2+SglpiCpW2OapSnhi77JgcoYRCeLS7JdS0=
+- 0gmrgie7g8inajsVK9+E2bcw+lNNbPKM5Aap0DwU7aw=
+- 9ckxayB6jvn1r27QEPjGclqRz8vRQxvEK2fn7bUyY2w=
+- D3PTa0szqDXLpaM7nihYNllodWHciHqU6T497Gt4mn4=
+- sV0WVd+KwQZb6yIakRuQp6Y0XtqS3/C+dytetE4N45Q=
+- mRfgDwVR7pXjg9a7f/BPgSW4JDNNQkIJdZp7HwqAmcU=
+- I3VKyvzM/BVP3pfb84+4mFc4ZjJ0F+X4fyyW+yvK5B8=
+- KSFQmXqyTRcBJ4gah/QX6Gq0bEGk70BxKWbmFyQBdbQ=
+- AA731ytHuRO8WO5AfaDgIDGGMRynR1egP5H65rB4zAM=
+- jStlehNVoFP/1NvxnhgoVgRwK0vLCLGFYkJDhazTc44=
+- lhNIyc8mNOicy8fgGLq/dm2fgUPC15tw3q0RovZF4Uo=
+- n4nV9ST9vYWQ3wdkD0e01kyw+yRRfS52VcREDxTFjJ0=
+- lGcwfZgx5rPyDVKD4o/3RruuU+ftaQUQIWz8W8aUplA=
+- w7s9RC66+JAiwMl1Pz6lU52wkN5uEuqL9N5w/8AAqvo=
+- czMZH9SW+ltzdUR72YkqZJonaereJyTFqIqgwPS3Ack=
+- fmnz5/NkSVlI5cmFdElJ1+a5C8XeA0QOaTV+/bFL2xQ=
+- KEp0ikkCq2r5eO/zoHWM+7rlhRazljcYxpmNAPVD7uA=
+- WPl0C3fLX6iCLd8Z8yHsSPCSIv8v7ywgCMDMDVAGT1U=
+- QfvrRvHK4/NTVw6xXCJL0J+5f9DtTqI6Yeih+k51f1I=
+- j/2ZZDNJ/r9EH5mtY+zy4s8W4/ZO1q1FMqoZcf3UAB8=
+- R0cBXHpBDOt1W1fzR/2Ns5hcTZZwGDD5wRklB5uoUTU=
+- iQ6mcbzc9mA/8zkxlN/WuUHnASt1qKVMPSkXrQtLTzM=
+- p1WoXPQUsIevQ+UxxPPv7GJ8hKZbVbBOPBQpv6KtCHA=
+- IwgcMuB4AN6t52DldHWM+NTegNpHtuKUlIAGsWSvvNU=
+- aif+Hj1Y+76cO1meog3TuV72N7Py8UfYG21pBd1gXGg=
+- Ek22G+51wXx4dKD1+tO2v89lAEo4FB5ycxnCBimZV2s=
+- 1gXwkbfJlNDhZW01MrIeOowG75GRKCzD96PlnUcR4A8=
+- NzybHRbD9zil8BflwaDokAnqYscjQxwfaoimgaA2dak=
+- 6CInmu42ReymSQ5aTPXrQZfWtla3UnkNYywvLA5V3dk=
+- gSnYeqUhafRqixmRNjzdTjsJx0w0qkMmGFcu4xYQ5aw=
+- pgR6GOsZeq2Ah0DKsghBR03XrzF/XicncmAGyaBU8ms=
+- B1wyNF3YIEfh09oqpo9MYlgeIPcVS3vBD7p5HO71880=
+- qIzCJ/LyLRc/ncYjbAz92jVooVZWwLvosoFOOPwHkjo=
+- efeSjz3rdDbW4RDfrjfo+A6pxl9V6oTrRJiVtjvFkJ4=
+- wqhN/64DmGuzOXaYZonRXh5Mh4ZAWFc334hQZE6lpY8=
+- KDrXhYiahgWxpdz2o38sPX0Eg0XYjllfV58V+yRFy9Q=
+- Ke2rXAMdb5k/IWLNHYuw/SiW5WA5zBxthcM62HiQ7u8=
+- ijHiFtK8oO+nrrRFp1Que5qtnvWNHlv+mxtdr/ZOrnM=
+- eXhLd6gS05oTvpsUh5ETVFAtX3SfS4ohktdeAWBvCPU=
+- 9bgLz2igaU6RNkrzELsyz/9lh7Snhv2WB1ypiQA1BEc=
+- TtSsct3+mzl0yC7yzU60GsjmY5xCb9VCOq4n5LGFEsM=
+- qIGzQIWRgVN5KQeORqb7GF/DQ71KGiP7yN6dEHNjlIE=
+- 8cq9D/jSVvU7Lvqdz+Ohx2Mly/CnAvsbLPVkMlxDajw=
+- yZrTahi1Uwqu5Ee1aYrkqZndVM/+mqvA4sYvrpYFZyY=
+- ktUX++jgUZLgr37JrmyCT42CO1RHQtVsgsXqZoj93EY=
+- N3Vd2tgpyqj9vEX7AVqFQuszrBd9+PTKhG6dCDpFtEg=
+- c5AvXIk96Y4Vm5SIlwqgDpBLXkVoQyPt+OJ2rU4sExw=
+- LOngX0AlK83FjGLTuxmAxIxCz3SdUGB2ExjBXXLhv04=
+- r1WC/jxUBiRe0/61uq3cRRK4BRdqkmFmTg0F2cK8Xs0=
+- TZ7grAQv9v1ulRm9Lizc3HmkSGN32et46pqtg1IOQiE=
+- vLvMmFbNw+KmTN5POomwnBuIqH0QNgtTvSQ3sNyZLOM=
+- A0eAYtlKmFBKBrNqx8EUF8UVIb2Ehlfb/DAwRY53UnY=
+- nMlvkmrcL/VIF/Jet4+DuXjgVYfbdQtZvtkOqL8WUF4=
+- QEreIqxRVbGEos52LUuRiEd5ZKmTVPc9KQXtw8+Dvgk=
+- kCiU+jjf0+TKLMGproBGn+X6m+SIrGVqONO79EJdc6Y=
+- 3fz1IZVs7AamdCdIK3IeJ9b05YZJiM+jxD2Lrk44F14=
+- OCucT46PSZ8OfrNt/uICR6F5bacD/E1ZWaq1AyE28WI=
+- V4pU+VLfXaKnpzO/gw3EhxSpcZdx7Co4gFHXZVZyCYo=
+- "/opWROu9GUxYf2+xp0/9yCZrQTGc0/IzOBv2LB3MOXY="
+- wHgKa+4pLlGMmNZob734TaopdH1xEOlfe7DYy0+8e1A=
+- ATzxm7WfQYwUN9E/3vOJWMbusQX7cMqc04QgzxKZLHk=
+- v8kmWAPy1oliyJ8mKkoTWIDM9L944YRaeS+dnC1+udA=
+- QEYgT7IeD/87ynSab1Cq2pVEByKw0AXGnm2tWRsqIs4=
+- gsq33wq/udldyk5ZN84paMeYxyb+pIwBa/l2MiHv2hM=
+- x2knJBmuQiKq7M+ouA+bO+C0DmVfiSuyI2yt8WtFc44=
+- SA2W3HDYVvFHtrm57/S0Cz1PahbDBBaAKMA857bmNE4=
+- epvgerBeGJquv8+Z9/ieAkhUdNgIK+jeLtvJ9RoFnJo=
+- ZQRejOc3qC+xDMkbYBtQQj2CaqjJ4KUmxo/DkVbZD/Q=
+- TTihMUUhFafEDt87LwOxc2xlYWRknWdbisi9Jr61z7o=
+- 3p7bIETQEvBFU+SbBNVMvsjopGpArVoZvF3M4doA7P0=
+- u2DMUNb8Eq5vt+2Fbh7E3P8ur3bCYx2XeyPdLfA7EUw=
+- cyQ8WO9ztsXGFIMhieUa5v0oNNG8xvI2RcYryAVNEzE=
+- AYx8OpWHtm1Nf626GpQQCFKstaKcokQwSnz0vnkGwf8=
+- 7p1KgMoxqb8AYDG0Fagx81cB8YnB6neOezaemXGZ7dw=
+- jkz85qB9RWpXPKZFYvpY2GxxN93Ovxh5ttSBhA7x5cg=
+- 7HXWstTle1caQf+gW64gypqOE0oayqnHHh4+9ymJUsY=
+- r6FEweQCv/1+p5/82Hkl51qfA6zgv5EFjdn0bXGM9ko=
+- OW84YTSu77pD6z9VnCLiDx7kM+A5Lw2c36NWqHXhqFk=
+- tsl41giTGDENeIZvbS6TwvDIrRh9Q/BRVGwLGn9p7kc=
+- VgLb0wcqVGTYEa0qobn97i6Pz63B57wVXB7pekOdz9c=
+- DNLjqF/HHxc1J21Jr8rIX5AcVQO/bOkeo7/f83YF2Gg=
+- Ad11H4N2Yy2S0FIif8ob2bYDtDcu7YQY07JbwPOYJ4g=
+- G+ZBvJYGdA6ErEbzNMw2cDl/g9t9YdMJFVh+TI2NGWg=
+- MVz/M9h8ZIA3AkS26TX+8hagZ8c4T//lJl21cBrR/oo=
+- mxiTiCESXdIF4VVEb3Y0rHpMb0c46DXJay3ppFvPLsw=
+- j8G1eRBCJMt1U01yucRFngyrek1g1GlGmQA+2nyZaMA=
+- BgxooIyHIGXt+TXoofYAyrQMred1M58kNjp4d1CdH1Q=
+- lOQ4yX+EIajfcru/IbI4ieuJlVoHPYyob9RlwtAZCF4=
+- Z+3ipJLhnnO7OyXk/wNd6APUf9QVo0gxRt9O+B2jhHA=
+- KH8vrShdJZGd5OagXy8DrvMn0DbB8n8H/SpBzwDeOOI=
+- FeCuSW6aXmKD6XRnyyMGpyHLKdptRtCzJMnOMsDhzWk=
+- DycVGGnFeGTnXg8qbm2+TEXJC+uB2F2WIIyWhhwKzks=
+- Aqs3jj+co4Mb2PC6RoGTUF24COsyju2LEa7P+2nZYLA=
+- gZWzQI31gcWPfAwDS7DhH5SmJMAHutriwCa7RlgnBn8=
+- Gm0U1boS2YcdoOLtm8Ahptrz67M6gpTYZaH/hVxu9PE=
+- nAlvpOYBSQAiM1Tva3HRtYvmTO87Xi7/MZbQ0mKv8wg=
+- WESL5NJ9gE7kesCz3Cn+4Cexdnc/gOq8pS9QKnMwDe8=
+- C2sMlFgwxL6SuBHNs9HmL2T4bfa4le58lurhZtpCLAM=
+- xwKwjbjjn+ZJMtR7/7J1oTD72g+5EMjBSoQVYuHsROM=
+- I0Z37/cJZqdbzHrKzsoT4obWZaF8DjOtLZoLsRPi0b4=
+- nh8cGAWGKbbAxBq6f3DvfOglIfwA6D2DxjcvFwth6EE=
+- QSmgmYEswpzDHX47T73nsJtaRsizF7zP0clAExT9h/c=
+- izqIsy+PBVFRmtsWq5GQJfns9tV9rdcnkKag2PIuM2Q=
+- wBprZzdlxYnPvncXTLmZJBH7TBVhsCwr3dtC6Pi/bF0=
+- K7+JrZyHdcOTXYVcJmqFnRRc0QkwEwsjtB0h+ctkH3Q=
+- lSi8VXB64MksNH2/9nonltXMY10ovSiH1giwvgEgm7U=
+- W6htl5l2Cf4iTU98pra9KUw3fTG7tUQa8/vSiCgA6WE=
+- B8xnODThMdVQqKlF6TUysYyBHn2ens31cokqa2yGz1Q=
+- JSHaow64/EwjauhP/PVDerdDE6FMnZJqC5n6p4N2hDk=
+- r1SsUYQTdZ7ANJp8kl6m8Yp3mEE7n0HRyGauALqw5SU=
+- 4UlfFnrQ2MOapKBefpRynaHv3gOtl5TKHNQ5C9owB40=
+- t4GIsjlMYjJ7t49O8MCdGP18h02XANX66syw9cU7o+k=
+- zBUyxfFHuTDOKYVqhGND73kpLFa0whyyAyDhNuEOJbY=
+- fLB2elch2nDUqoKT1Wwd59of9XynaUPC5Sg3tQpneQ0=
+- N1mEEYI/GGTgjTcB4drBkqHlXMrmSx3AmN5vF70XHNQ=
+- wQS74vhYRxb+io2L++VRhF/1/sVjeijoeClUul3tCZ8=
+- e97mb+qZR8yNWjLh7koJ+3GmRdhA7Zc0a99k5YAqpJw=
+- jwjUPqQRVEugetr1TlN2tnLXWBrsOG4jHvVw5dNUhto=
+- ocPAvRwszhTAGXWAO6YGz+fZuyy4873tardqDt1wW4s=
+- PJsEkjcbuar7QDu7IbQOvspzSKKiigQL23tkA9LdV3c=
+- sUk3CACdTZIwr/md/hMlELIK8SwTJil4HIkTBrkub6Q=
+- f2OE2RqwIN01Zwqn6MJuEHCzdxHd1KSXnCt9XJgiGs8=
+- unKYvsaJqYwZX7HJ4+dKF2MKmYRSjGHWnvlD6i/WJbc=
+- WpbUw+DyGJRanK2DLUBqnl8ZqAfmduP+ji0927QEZS0=
+- "/xTOtmWLzys8oOijkRSI7MKvb/NB6UDsuYUFGceaf18="
+- aey9oKN3Z331A81PG1ANe3hTAWLxZSYZ8XPdXuVF2NU=
+- YMSHWcie1pAoXeGqURSgkPA897NHORa3zuRS8RWmoYI=
+- bnpHYb3beS5nKCAZSOojxPQTANP+siAVXA+nxhv8Xo4=
+- gDf1HKUfXSkMAxxMiR3S1yX2uGPCmNXD9R3C2d8+NSU=
+- USr7bHYxc9OWuqDQhJTlhFaC31PRHwVhECIaZgGXPiw=
+- gQCsxSFfeCZFxgE7Zl1tIdt4JSZiLAbMW4qWxHW3pNc=
+- PKtw/uzSmbKpaoLeQwv3WrdHhNdj76aXJLHpBeI/oGI=
+- svcqSe1kVU+Pph+kS2/RKOzNUDAshIeoDaUWIilKsKk=
+- kkEZOfAxqUGNKXjpbrM5NZItZtCuYDKX5NMZ5IAPShI=
+- VTvyMHyfQD0E1UbiJOhl5mH1Irx3v9ygwGm07mJXhH8=
+- fbIpfYEnVop8qv0+Ot0d4XMZDq1KuF6DJG2aQXXGD+c=
+- j392I2wrl6Jw/YPilwaqozs7+HJ2+/ZPaFyXAp8xJDY=
+- wfmflLTjY8a8cfWlBc/ruuww1a/r2EWGkSAZ49/jpBo=
+- 4oYyDNPv6rK7slDeTF0UXB2I4U9JbuLN7eU5P4YEhnM=
+- rYjzhSt3ZwdaVrDDxuNfXF/Vqr57n3YMnSeXeWzS8u0=
+- L5kZB3XvFL6T/Kp4UsKOgqC70zpKG77T4KQbqSGqtFo=
+- Lobzz5+iyo2H323JGP3rFFlxdDPi9229OFJMllaRADs=
+- x7sqcgZMzfxhKw0Zb1wPhe2j7Hk/cwTMX8H5MsHHhFY=
+- HB2KrXXzFhmKFbV0xY2Y4QmMxtEfG9r1Xr4MthHVHGo=
+- h1KlVpP3mOJOX/M9PuHXqq71v5C/C/MtRPd5LxnlSO8=
+- iFg/4VEpWz4T8fHjyj7BiZNEHwYa2QHh7j+HHPGG/78=
+- TRTM+vP3UX3pVdt42a/BtGUYtMNeHhynPETHNQ3Tu3w=
+- 4XpbpTx9/McEHQ326d1yd2iBpDDE7l/eUNc+cXxOpVU=
+- 5K57WHH2CgGQwDaBIXFg8QZ08yLKSqW2UlJDiVcs5Vg=
+- X1xmblgv/JYtYyTFxg45MXS4ekQIxlnmxoBPAG0OfnI=
+- ALPohsDQBqS+r5k+ONZJ8j9F84MizW0XluaJdM5nPI8=
+- cFkHtznjgtfcqFHspnHdoSDFwA0550cHS/+MGJkGcuM=
+- 93K4v+EzAuPppyJZvY76hr4Rj/ZFiuDp1PSvdHDNPyY=
+- LbdkcqdCz/otP/iCOjc9eAp3aEDif1KdO6yT2b4lxGk=
+- V/iq5Qfnj+7rwaxZ6NIu55LbKSwKE/GwpCQc98jjuzU=
+- 31kFF09jPmU25oH8hVmSIfF4TJ76GS/Md50v/FWg4co=
+- rQJmFAjHJY9HEEpO0E+mvNWg2C4QaDVBe0In61k2yx4=
+- r8TWfJ8yYNWsr2FowqjgVc8DNCOSdzCOdOHT3UXl5Ag=
+- xQ84f8oNc5YiUbOEA7/6lLYgie0giDzxqpcd4nX2T2Y=
+- GGQJSbAPKADttb+JZVSuLahh3K05LkyrQsr2l6DeRWo=
+- sXbMA5IWC4teJfp8PuegdxJB+2x6Twf8/yoSHtw1c80=
+- un7RZV00h5UOFjKeZbKTsZwi4+CNbMcTKEq3gno07iw=
+- hb3IJCAW4IDGtA6sLT9V/VsA4qCKwh0cRykp0oYYdVI=
+- gkwd5hq5jezIRD4r9S0+6W9IBRvZJ7giUuM2wgo24XE=
+- 7Y1/x255Ol+l2K2IjokPJFJmnRnzbMt342C3GWH1vfA=
+- p54XlG4/Kq6EUEbVU6mfeAxm6Q2MsFuC9qGYR3T2fZk=
+- isSzuzcEfknaPx5yR405jkcV46fQ+WkdEiSStE+NT70=
+- vfUggkgg55TubMOD9W7rEvcZMz2mTHQGZ67LIg17bAo=
+- pLTFilRbZtI85tnBAAeInk00psOPJCPvh0VrvuEF8s8=
+- 0ehoevkXd90SS1nEOG5GpwpO3PqAcoYXgTV00VQfJNU=
+- Nr5OpKaXHhI0lwCZPKJ2woq9njA88nQvpKWDBfK3X7g=
+- HAWrVhNpj9r5seH09O9ftH2e1vXB8qqM0BVcSXkUyTU=
+- agz6CzfQb9KW7i1E4Wcymj6fUqp6jL02MvmDkl265WY=
+- ZXQksRMUdA55KKKueNxESMwqtdTksuFOQnQy/ZGI+/k=
+- fA4zLnxpNK24hSxcpbwX95GdCqLJA9tGCgvbEjLLy/Y=
+- ldMBaaWcQYtSATMV/IG8mf3wp7A6EW80arYoSW80ntU=
+- B8MP2h89+wUsLsFxrlzCzMiOShyKCW9cu4Oyxx20eao=
+- QUFcYOALUpwumyPvrRuZ2xq5m1eUI94bbhUZp2Oro7U=
+- LJduGMwz24ma95+pWigDOkzfTpT/HRJq3Os8IcQ28a0=
+- TmTC6L9LiKxZlsyi5elAmRl/BgOykMbsFdDKmZAJM5c=
+- dFf5jRQ5BN0MR9UEfYwoD9g72yy9LtgN7qDofv6VBvk=
+- e0HD1ItPd77YcF/JGj0xYDdKNGVla4PRb0LclixmYWE=
+- dysYHapmKdDcxeXGqvDoiwY2XJNvZ1ArAQgNdpI6xHQ=
+- HFBLIFUEvBlT0VpNkKrJZodCjvSovrVeVeToULhZWBU=
+- wZjqfp6Rn6r5BeEHu77nmas72JadVBIeSy/L9uQA2ZE=
+- "/9r/iokaM5kpyptQnT+IbkzKcGleGfmGwM7lYQ3pzfw="
+- ACr1j94KC9/EMkOgzWw7+LjtFdhFJLsW+ef520PkisU=
+- P0MKP1hLUfpTskx6y5zkqVauoJVw6BxXHt1PdEBlYnc=
+- "+eNKylZbP8d+UqE9jIyEnfVYn+ShuSqjAzI1ur0ZASw="
+- ynaFfEd43hFOfbbljQ3Cuh/+e8TwnS7dUWQZB6QnMB0=
+- '08bGvk36Ei/YrJMhY9rnW1OmowpKATzQ0GWrJ4kITCk='
+- Kjc4u1JHfI8OJawRsvRjgU0vcPeuMs9TH5OWZezY9go=
+- Lt+t3w0wJ0U3MFW/1EQ2/RElBVZTGPbjPq7KCQkyrT8=
+- A9niaVKxFYcuV6CtU3yhD1im2UwyKSpIg6n+nwzzInQ=
+- grxUPgXTCUHnXYSt7abyxV8bOJp1XljNB6tPhgcGA/Q=
+- cCcmuKs/nSXpA2pCiJiAUt3cKAeNhAVE9V42rnEyjmo=
+- frGoz6WrJPQFOatTwvHHVB/TK059QEgw+a2fuu2J87I=
+- DR5WfoJYItnQ4OouA2810tpEO+qpypUAZCjcpTEDrYo=
+- kfNOvlh/iVxWR62WR5FV2seKmYdeu273V/v6yw1fK3E=
+- 1FFaZqY7Y+lq8g+6T8gYwt2ZPHj6HEM6WNjNc4cA5t8=
+- kAsheN4/z84W78te7nRMQbYBaZTaHDF7vH21LG9CWcI=
+- T2XViFOj3tYBWDgSbuHbfBdcvYPNX9kAWiU8rzS0P6I=
+- IwduRxI4QUyPbmG22mYc9GRGl6pzaWJleFMa6QDrdKc=
+- 0/ZUvK2D2JDkk5f5XG9FDYheniqFxBGWiuOl53phips=
+- QOYkEShRuy3xpNfAhHJ6GDulmZgz4gkbL1GUntSeJ0M=
+- XPoO+i30iTGnd7KQTYXt9YkfynMjGVIYlzhEheljJ3Y=
+- IpB3y+bUpiXQ9JmGY53IG9LDh/u4eCGCsy5lQF2YkP0=
+- mJOIXBBSut9ukKAgFr6k3oKMfDlqrp3ycYm/BKChYtg=
+- 4kXpZfxY0dPUpDY5QE/GcljTN4GnId+N617CdJvvCMk=
+- huWdrApWHugv69jRvcIHH6xaA33LIXpS+mPRYKlHc9A=
+- iTGoWuht9czk45m/BAsiUKb1EgvQs6LqnLJH7pPTcFI=
+- l6QIaq1TToQKTI2gA2v1hbl2Eugl7s8qEFc8vFsRwvw=
+- YW4k/gEDfQ9NqxhxLsaEhYAw2/102eUC0F1B7XdNeRI=
+- 5lDafrvPL5GBUBhoLkE+9yK61q7MiVLrqBg2PBUM6uo=
+- ya8RDch52zkyEqXBAM+xnejMqn6CPU1LWqu653ts4Ew=
+- zgqvxGvGoAcUQuM9nIil1WTqZ/dDg/+QGY/F4Zqh21c=
+- TFZ2lMVTnfKyxZS/LcMGWabwISTt2KGeSbWjn0Jxx6g=
+- S5lDz6xtygxs2HSd/aI7j/v852V/NLcFgspEc4zRkbI=
+- 6vdhONKvCIVXUBzEQXqpA1aWBQYloruNzWqyqiiLgjc=
+- cw/Erz/FU9NpU0OtNpuQaDKf1HBnHgBttYgecr3l6+o=
+- vnr4v1N7JM9PZ92AwtQPBrxX8G4Zu0MMJseIenrx+xc=
+- 39ijFfG9rPsEJm6P3utMtRD1+piBg+ufpXUpJxaZB3Y=
+- T/DMHghqr4xXN6UpT8Ycyxgq3itHeHN7eyyukeN3L+0=
+- 5iR8a4Jmyr5YnVoW/QpVyYyWBS1F+Y+HocR1PqTAo7Q=
+- QzqgWCu2griDWJnimcycjhDHO/uyeq9uWVWlUEav8Go=
+- DOeVsygcxSuqM9F+8tZiqnIA1d4KPGbzSTMLV7Q00+E=
+- y2f4AIU6EHhFOq/rW+BFZ358MTTEbZFhuNl/PygdRhI=
+- GVOcnIAkHmCQtUqxguwqooSQ4bnjAKqn4E5hwWdoGbg=
+- LzC/OpxIJHOC2zOura7LhEWssd6jMxAYJ7quE0UG2ug=
+- W3qR3+aT6+L2pNSW+fNJ7X3gP07Qpgap7nRM7xJr6b4=
+- 1lALHd31IoNKEkSogg9aFUAauPfJwH3afi46j18R+Hw=
+- mD8VJXljHxqBK9lrTapUjimtJp64D1DDlpZUZm4w8vQ=
+- X1hUtWzabnEgqO48CnaC3CXw51gqY2kQl4EKeEmHjo0=
+- Pd8rnHuPLx7ZhQp58U8BUrPC5w6ds790ybbdpK3iQnI=
+- WSRLJdWWCHmGdi81pZhX5gETj9X6pGtWQM2txESzO0g=
+- rRd5gTuV/j9X6ZA9kY2HWd6Zxbame78PLOZ7aq7WI0o=
+- ND5nwIeQh6XzMhJ/yL3aCiwDJFE1uOC3ya8VJuRtYWs=
+- KHOpte53KEIuh4A3bImNPuKt8nASE8n4in8oWWjGXgM=
+- vcaitRCuJQ+D0dCGZJs4hRV46h8Y6/GnOayNSzjgzUw=
+- aGZSp+V2znbX+ZiP+Es3+pPlzSJI4p4YZp52RpBWYhA=
+- NA+Wsa5TNJWz/wXc8c7cptxPhSv1w1KUetGNRJrOZ3E=
+- QSNXXY3kDA+l9oaf/7GlIXLVJIR1Kxfv3sdaNmpg1Tc=
+- QgwH8YpQ3l+Fp6TwotTxxY8bqg51lgkszpCsRGQn36g=
+- D+L8N0mUy4EJJo9bc2v6m8Xc7761Pyzj47aWvZup5wg=
+- 8O1Tsg16xdVFkzaJ6ZlugsyXorGPW42KQuZqoQvGIJM=
+- 37/xAtpTFUStUUdJdztmhbtLwxqDIOu++CES6NwgFyg=
+- raQb9uTCD4EyiNwT/rujSM5tn8QzNUGTQt+N4ow/Tfg=
+- HmUQtt0y37TRibzpEowxFcTpSeZ16ARoLc/ZECHbF9Q=
+- Jt66v2/cQoWDRzAfevkFl0mfsDcU6CaofsVkEO18auI=
+- yaAADX9OLv0WiMraUJ9YzoPJ6IM00wsgqv8PSrhhdxs=
+- g193uHJWRzY7hIM+9zhXrUDYZPDhmJAQUVwUlWS2e64=
+- zL2g3vR0QJP7GabobdtkqGemJATdXflHt9aEjS5yN1Q=
+- 7/SGCusnwJdWrmtX46wwqOe8ZyzpwkQ3DcfiaOCJx8o=
+- VIYdvZdj9kiHhU6iLC3olDX1sZF/gHmtcZ+JkFY66Rk=
+- QoNudBH4RTpDsnam+SkLck3EZiW0d+8IIf8dUhQ2vBU=
+- "/F6RqRm8GtihSQc203f58P2QjkZ6azBFJBiVr1Gmv3Q="
+- nWX4cAQHx01ET+I/XnBHqkejalhVs4GDaYafWybE8wk=
+- x3FSiVr7cFL06grFFOtxA2A4bqsOccesfJB2E4s8GOQ=
+- KNVq0peWbdIGqnoSv7Ao+SIa0Ot2lYA6352fYHuy4+U=
+- uYhHeDDOnG7l1AYqtB9R8VT8eS92wW3CDzBaA+7CGCc=
+- kAok9rNjU7aisoAWTD6ydR++Xl5x3eJgotLW9Tz10L8=
+- 65RZcg/jRAWsddJhbK+UgKXY1qis0J9Dn73d+QbMaHY=
+- Y7/O7OQ2F6Ga9wTZ7LFGUw5nhFQurY2cxXtLqplGWXw=
+- CyHinLCU0Xg9OFijJz5JmFMFR3yXGokdGrhTIoHcO54=
+- tX1YV4S2Zd6aC+Yc5T2zZW7k5CzPzXCEmdynpZKR35o=
+- 1wCoreov4fNi1faaJTWHqoyu2fFA+tvxQ+PNFLtThUw=
+- CTfMnS0OPmy9K5xCPJW6N8HnzEC423xHyTYcy11+3pQ=
+- yR5hfdNph6amdrn+WbTc3qPIrfsV9T/O2n7YG5HiW5U=
+- 6jGDbWUlpJZvFgFB4bFkN6E74O37lDCylYj7IbxaiVU=
+- YRTmWvD5JM4bjI7K3wh3l3xNeqWkdMLdqofa8Qstbrk=
+- A/qUSm4dNWQpmV7/2kgB42Y5wYQzZmrY6vtDApahMuM=
+- 58cjCH1ksMfDl37C+QZ3JWXtIMfnlnTPN6tG/BmDcA8=
+- 4TiyuwK8c458CDIJfJG7EBftWAUafJOU6C2Bv/2O9Io=
+- z9GhuvhjIriaCTqIElDbf3uOqMpZzFi6TIyRKq67Qio=
+- KFWI/R6bVTP151XAAAl4z2j0XxcUUYKN0JJU75hbhyg=
+- UbrW4j6eyn0jaGx8u8NmTOXyWjZI6saCMnv7xMfeoHU=
+- gdpuKAGnlrtm5mV9J2K2BAjco+i02ZG4ob2fUNoUUBU=
+- AC5uKl7iHwWH/Elgzbrx6pI6BvBgI8DHzFrRb4ovJWs=
+- XX5JXrhKNHVhIKCBRboPLsos1LZojFJmijt/k1fi5mA=
+- Fu5Uc2OOWReVvpMHlhk3hJk8XnM7Z9ciDAvlBdqVSYg=
+- 7cQ93CgzsHgvLIb5orwTYF9n1TOcxxy0jRUNdLaoouc=
+- EwR6xVgeQhMLjISI7rAfLmisnV35wHy0Thvj0CQMEhE=
+- 1WOKgGXSDMgxT6UqHzZi3I9wGGg1/d/BMQZEgrG0DpM=
+- 1RQffdPwBjR+THYqnhTDizHmjI2eXPX+tFFqylMrj2E=
+- m/oo709U25rWb2q3YctlnrovBdZT3OeFrCw8+qqagkA=
+- W6N3+OKPvmGa/DxT+ATRXvBlkzKyTU4gJ4TOuTvS7O8=
+- l+sw6+MWv4Tr+20zhLX/8GcVYciv1W88TJ7Loda207w=
+- XzYadIKOWreLv4SnQgoovPtzMGBWgJw4D7lj7ucu/bM=
+- wBpW2pkJ9pez++XQnDLbgNCNCd7Qinp0M5dHmCscVos=
+- rzPVBcbnK03GuNaEOd4TFGbtQGa4Qv6gPw/2w7cml1o=
+- Gwu7Zg9IgMmhkmSmmt57/qQF+pdsPHht2P2TUnBWvHg=
+- 8sXt/O4MKLZ42rcj1aE+GMyavIZnI8eComkaNdD8XgU=
+- yq6L+mNMy5LMPx3sObU9OBG28N99z8adIZW6GCAw9mc=
+- PFMML0SzHRhngs2+6KCwqBU1DqU9TLe5eC7J2q0Wtsc=
+- r7nH0meriXdVWyNrUYzC7qFZQmaRDkv0B9/VHzDopys=
+- 6LVDukaFdJNkOoShnCjPhHVbneN+oI/7NVd4jz/5Jsw=
+- P+2E7hMDlBvuNXvrnQW5arySKiES7JCFcbdgNLl2BV4=
+- gVN0RW3K+C3l8FL+BfPpKlX5Dw4WStLPcOO21qSq7gM=
+- xFxiaYWp/9q8LmPAiqtizKJKTl1EBc8kph7NAh+0+U8=
+- B6PBEzabdhqqo4gS47Lju7omqS1qzI4OwN1dWza7DD4=
+- zwvvwO1kvgTeeLTl2OSbe3ek9DuYvmxE2zBU0vD9UuM=
+- 3bmJhkOF0phkYXOUVECMliXt/aiqQ4fdzi2OZMvHXMs=
+- GrKC949/yghx51k/jdIgG6bdXKxQRT9dzNPPs9Yq0i8=
+- "+Otzm2AQb1zd1jCB6Tyy24BX97fLsVvFj9tG0iBFyP0="
+- r4lk1ZIsZV6vjNm2thilPpwsB/wwsFvNWdYXAq8DRUg=
+- 7Di6n1gJsN8btiFe5IiNrf86I+bS0HrxOKFRz0c+VgA=
+- f0YrBckd0IYu9WjE9vsq2OrProMtE3kEd2kdYkiA0RY=
+- ulPxjCRUl+TXh3FtEVPAKU4EvbH6Zhju2MH3GSnGCE0=
+- KPxlZbOtYA6/rnp3kLbrm9L7xkbhG9mzkslwPUqulUE=
+- 5LHGLz375rQOO6AIoUiMsL9ryc6CkGwMqXQW3L4tQz0=
+- jRmhY8rcoUKjF2Jz6DeuYHAnunrWs88xgNG9d7AiFpM=
+- Ro0DPtEiAiV3a9mIKdCjBR1ykElncXIXbaC+vFVfbTA=
+- "+P5749YcU9gb0lZpMJTlL8+GK/CSAdHI1WWFOtPIr50="
+- pFRZlwpb9kxhKitUb+Gaswa5yCuXqfB6Ssksul6e6+k=
+- CK2WDqpLAcqdEaIHNigxeBRUXIlD/AzErissqEfrf4g=
+- FU4mvwMKbpV2fB5YgJ3DCsyBucNkjecp/MmH4XtG6gg=
+- V939FvdoD5i1pKH+KCLr0FALZwIxH9BixOCDe+Na54Y=
+- nMJCUeO5c7TM4tcoUlZX7nAZBYqGNW2EnBe2pnqZdEA=
+- s8YbximBuGaN7Q0pkz2YDBAjDJzBnqQGIh+hzoTLxf8=
+- yPSE4mCCGgJDFhZCiMZ1WbKom3Y+yph0KP7ATv/bIWY=
+- lznATkegEVex0YLf2qelIHaTxkVoxXgkKnqecYnO1TI=
+- yv7g4DuIIfLWBrbfgN7dNiWZNtJVcUqydb91GhvzjaE=
+- "/1ieYptUCh2UIoljhDAs1qgMm7YzT51TFjP3px7vVFw="
+- DXgcc2AwxlEtBKpCi7siiMbgm7tdUlb4nm7MW9f7R5o=
+- 3xk7XMHSD0Bb1Fv87ZOTlA41bqJHsiF0eZYClwx8hM0=
+- OymzMuvSqRJ1pAqz4zmh691H8HHsmAs3EjfiyW/oI+I=
+- 9Wyfv6qCDhXZb8O7zQCQKuI8kM3C0/+l8mC3X+YGYlk=
+- tbuLpXrUDOCAvVwi3DP5y341RrWQSBVDbnRjhOQVALc=
+- wev71EbMw/StugH5r/Rv5HpVlxww48HUjyNQMA1crAw=
+- YCmPRC8Z8fEIDUfgYuStdeji/C8fnzBPOuVLdGlU5aA=
+- 40QUwdxdZSzgvp+S/m1TRHfbzW9cqOEjnR+0KYHgsaQ=
+- rKaGOAllxbNuVUbCUkA88j6rtli8KU7r3gU6Ax6kxYk=
+- 7YVGbIPo0V2Cuq6Dl1WmPvjUasQ2l/bUDj5Pe1biy+k=
+- 8RsSw0GTuoisDYZ1AJbtttSeegZmC4tH6RN/EZnObww=
+- E4HSH8oF7s1oHHh5aJc8EPOMfpR9/zYfbvVLROa8jVY=
+- "/qfdQDzRcPWUSiG44DC63g7XXQYlTk2wkdVU43fZ/Bo="
+- dpMX2uoLSRDOe4UfWS9yOlAcdX+DpdAvSBmSFmfZi0s=
+- Hac2LrD0acHVcGBWejt6qH6QzoNWlYCdM2S0SGcbgPU=
+- T2MvvRXqIhR/+sCsvcetmIUmazQOg9b8eqVRiQUpwQw=
+- "/YUfT2nhONGKUJiMkRMooa4UOW3BYOK3shDmYhjemL8="
+- FOFDfSeQkGY0iFGnsKddMlcyz0Fi9LD2/+awGgzIzcU=
+- OB6Nmp6+2GLBSZS7O60vaqVToCND8arjZ2x8srtI0cQ=
+- C5S+eXH/T5NPljpgGpxa3Kk7dT0i0wsjlMyopMLp6qA=
+- ocdiDlPKaBd+UNNtgIRufHn1nWPKRSQoCCZM6q+L7h8=
+- 7w0makeGD/H76LH59uXHSu5IcfNPHskxmMqnwpG6FSU=
+- OUQE73K4JiCZy5XmDDva5SjYtID3dG5/WIv9huGaqK0=
+- oNdjDFglMgeOLa2nQa6bCuH7XGv5GCM1HnCCNiY2OWg=
+- C+dcjERnQQ+u36bTgvwg2WBudz8rfoYAo9UdC6RFcvE=
+- "/7pkm0Dd/1fDQOaCQQ8uqZPUmRfzQxHL/tX2Tn5m0YE="
+- N5YMS1iF4cEZu7yE4kiLtLGcLdKwYqSKbfxt5u2TBKQ=
+- dwyfB7B02JoF52bzTOorUHcaeOAn9I9+BwfvV7kE9JQ=
+- al8pjoUzD2/bEZvTCWeKpQqH+QLNzSst5/zJX1GyeVo=
+- LyxsvbbVzlB1NFBrL3InnR5ohvFFeud7ZItMVPg3Cig=
+- zCwK+W8MSWRu5x2KFSLv66Vcxf3IMu+iAcFZoGzplyI=
+- D44jGfHSqo08MyhM4z9b33A6mZN3Dgx+n7QlpYI/KAU=
+- id1z1nDHJLXYWLjCMq6Zd9XSecaLiOGB8H82Bg6y0f4=
+- 57m1WvwmvkOtR5U6IjsckIgvUyhfTBveRlN5urNSIms=
+- BF6KMtlOnMmqv8Og3aD5LcGM4U76dzw7dZT/eTt+fNU=
+- 5OVAkEsiJZ1NL7VHu49ucUbhIj2H0CjmqbP1AINWIjA=
+- 25x0XTnMqxgQQyxNX2Fm/xg/zn5RP8F5+fcNg6JltL8=
+- DZp1ZlA0mThldESFosPfPZpyuSVuM+Y02ob1nUQZXCI=
+- 65MQ6zqxnCzu7ujJUQTHV0DzrWG4e3eitpPVeEw/IiE=
+- AgVG6Pk6mddAvgElsMfBtctAioNkMLIctWqX4TZAit0=
+- He3BRqs4WOSbBOvPXacKuNjSaCJD+04sz71sniF8qS4=
+- LARwudzP09ptq0lLmlOGy6sqbX/FEyCL3sL2KytWHXo=
+- gcAwJoeWGok39ID2vqrfWWqP/U46tCrPGsjE0OEp2ew=
+- Wbuv2fsGq25tqEHLiwBUmIlpl3BPBoWmgw8hDRROyY4=
+- "+JQLvDBfoHdlvHuGow/iCce3ddypCnPSdAVNPLu58bw="
+- z1Ijn9YqRLd0I0oJTTyGLAPw87cY9CvpECUIWne+EH8=
+- zGiL5b1e7XJ9Q3gL5127bgAq4/k+JjYrAVfTPN1Z0WE=
+- VYNNGO1oHkl7Swt2cVXRIq5N6dBEwoFuaYjM7cBEgvs=
+- Sldlkae9lWyRXw/eN5jNg4ArHkHC45zL1VXmBSnZu94=
+- aTW0XAvjLNuxpXnXUQPkTdvFMG805YY9Jh3n8oal3Ns=
+- D603pG/UQ8ymugFu4eq9YfI0h9pht0ZvlmxBYZ9swz4=
+- cNCACvPjkV4IqNK7BkW1uYzMd9K2LHBP0JTvH8EQiHQ=
+- aOa3MxDDzI9DooAJWgKtqamY2GFjGh+T1lQdbaSLZO0=
+- Tz0K+ok040cA1J9o4p1+Nw2m/V+9ydIB6uhSDs786bM=
+- Nn9KQ+C38rsa31oLDNH3ZT8M1p/+VZHvkcW9VM3KEW4=
+- 5AskLPbeQCweHgHlJpSxVIU/vDRY+CXTFEL9DvcAjzU=
+- l+qdLSoxTr1WMw5EcPj87tt61F8sT5+7d7qHVXeHN+g=
+- c3djDh672HHcZP4gqt24In1lmQ8FLnXXkNi/1YFiZf0=
+- WTKnL6CI66hPjrn55XGcJbAJpHRM7shP20ABrhDEHd8=
+- 55zD/SRqd/gyDBFJJ1X4i2HyxzVnE6Q4F0x628ExoJw=
+- W7YAvi6KkaUst1k38olX2Wsj+gnGPczAkISGnMP7oRU=
+- d0ciT01z+uBybg5HMBst42YiE66hx8gIRaZS3cvySLE=
+- VCiJ11QHFFCoFo6MKzRDkRrDjJRKg0SIhi9En1MtwKk=
+- 0BatZkxYWIIVvkeuaInPSep6yOAOWUZiPe52mmczXVQ=
+- VFcLDOu6OvqVtTRqwDDFnTWQ5Q71EediUjzMnKMZOoA=
+- QZ7Mg1miEziiWM/DhWAk4P6gzkQmGL6MFnL6m7YyGww=
+- LxCB5o0ojZRPOboAXKXGLHme6fpvdtvzvd34tYIX1P0=
+- 2KHoIjqLlB6lgcmr16ftgY3FePyJXvOIGQVbvMQcLZ4=
+- 20oxWaH4XdV1cnFcOGkccmAqQnkfiZ4B1E+Pk5HoQu8=
+- A9r9Jt7LyfquOlS5EjBgb+f8ZP8qKb0rPw9z1c6q7I0=
+- 9A0csZZrlHckXAVOpSrVHzl8EhLP1NH8XVI3lnWhaXY=
+- Oi4nCw21Lh5wxL+PVVy6mRISWVAkSaGquQ1IlgaOsqM=
+- 7lJ9nSydVaaM+otdBIsCsNp+qVh7rTkN0so++VjkA0Q=
+- W04C9WuY/rSwGkEtOQv9plRUZyKZNJJPc10btMwU0RE=
+- viAwFbU78y77YENLE101SaYJ9xzTRO8RrYQlWiOj9Os=
+- Ft/A24xpu6gdfAaZNyj5Y3kD4qq/N5IlBJuhRgmKTuA=
+- qDjxWMZGDpbinSQNT5vejiyqMu1Kp90p1AOmxr2eQRk=
+- X69oOWRX8TJSX/k3UN4uIZWXAUWcEsl+zLmLoKHeMds=
+- gCX51AHnWxFYSg/dHJ8DSWjo+umB5lE6I7jmG9uebmY=
+- KX2XsoE4EG8yO2sVoR32Hx6R0CMzw5x855fJWTB/KEg=
+- iHQCclrunVhV1bLqt669c5LNLLqo0uLDGaLNZ2lpUs4=
+- z64IBbjtBLCQ/nYcA7j6zKWDiuS8mg1w349cRAv9oNY=
+- "+cjkGG3OHNPBbidw6Sc4d+6PP7xlhvQTLBRmWxFfUGM="
+- qxfTqsxqwfmRCv8P3RFsDLbmmVsneXYCJCdxkJDcxNw=
+- uy3LqDoqhBvRYauzjGsyu9jz+PD1Egx9FehwdgZsSLA=
+- Ycd3d+qcYOOeq/X94oZjDU2N0c+iM6Eb6aLbuEdlgAY=
+- 1lrPu6xGuMNSXeerqW17Fin5njFN94tV44Ql4RMeV3E=
+- KoFDapVEQW2jpg1fvNKSYkBLpXO5V5x7Pnu3AmT33XM=
+- EjIUjvkXbviQ5rD7yJcAgycfX7a+CKxJEgIpbViQJIU=
+- YNQpxZ6uT2QjTKF1KaGDTu4ORQS9esMTU++6Qv2R1jI=
+- PpkPuQ8egd3RgosxtfZt7CcWWHHFNmbq/cxumIN8rTc=
+- DXuwPjqgNt+h48JhEB3QtieGt8DwhVgRJVSbn0ssMlo=
+- IbP51gteVxwBHLOAztlpLghkcKoMA8J7vK+tfLBrnjw=
+- d6fPr+2XBGFrlm15O1RiQ5xDIJEbcdKOBGBBE7x8V+8=
+- bwH2he4m1g7I4BNqJ/Tqb4E0DGHUiy3ElbjgDbIUnw8=
+- 7s3mG+J168GZgXmVnWwWuClmzIKhFF0f40aUbBoWshQ=
+- MaXltheYJtxOQ5f7t7KdyN7JWxsBZ0OemFLvH93fIEA=
+- zljq5F9iT0OkEAqxmv2gNnzrC4AOJA1dyZDWfBs70kg=
+- jS2sEcCfru3fVOfHDBc596cnejid39+OMBGFQdj7pvA=
+- OsoSD9LMwNDWAe1JqJeezIeMYk0fNA6jLc/te8BJTDs=
+- pY010/P/hCQv6TM6aPV4fvLwysQzTaFgQuAehmGg4EM=
+- layulIJA7KrpcdbYNe6uQZ1FC0qR1OTtMXtEUw+eHFA=
+- 7v7D3nyqXati8ABVbuvP1ErgGaNrLU2/RtXj4ORu6eI=
+- Rgu/fQKJPvfD4ActKiCgTnE9cb2HzWIZD+9vtfMIMzs=
+- nXV+ofVzsHckQ07l+LtbDUNDh6OFblicEzQ3sQ1ACDw=
+- f1CIdillAxGsOfFxTjB9KdSvk8ErFulvaz15Zb7SHFM=
+- 8Ho6LtmOStJ4dahaSky1AHJrfHNqVEhGcJKrktcS6iQ=
+- hkK+CrJ33U44tDSSSRBOA0GqA2tkNkEwUzuEno+/d3A=
+- 5GclakQfZhXXfSwRt9Xsf+X4g/Al03jNDUdXIOmFLlI=
+- ObaNH+IHIXbmyW3sw10SV9p4/S8jdCy2Y5oJwYqRo5Q=
+- cWd0jnIraEqfv8IuX0DfCRyGGt0Tcrccyize/gPIMyg=
+- 8As9zEqWuOYjSvNZ76cAeh0KIAzndKqgT12gSV/RIXU=
+- eK9z6wt/U2hSejAjNapSM+B6vqFVsW/5p/W0WBuLdnQ=
+- et5DQGibUoy+O62HqAdIrSxe6ya2glGNivxs8bYdn0E=
+- iBT8YQODW7/8rUu1zk5qwT9s+ic5k9CtE0/r8aGUsj4=
+- rd68re6otiRSWziOpaicq3+FMIbPXGtH2FVPoMNOFuw=
+- 9KkEzweaj0bSx2fe/m+5M5o05PLaQlMn8KNaE75lTXE=
+- XeTnBIjMCPfDwKzizaK5X7Jjt1CxdHL1f+hHq4c0ID0=
+- N/pH74mV8/iwN03W4SI7iXEtaikQDLO9ZOM52wPtd2k=
+- r+TFkPUIb1QnEn5FNetRKI4M5i9COLyuOewIwM2z9Ks=
+- C3ytmj8KWe42ShY3kCuBy7naynYj34g6/zk11Mxshfg=
+- yNEhkBAPBy7I1HlVhhO91KsrUSuTjCjoyVD/iyu52GM=
+- ERfuUjY3GySS7cw7PLvcvmD4gpT9cVH8ZjjXyOporDE=
+- tiOY4jsB+qrHXJ9slkDKdP7dc+kqQu8mKP8rxl4iUPs=
+- hEqUeRZOFOfGVQ+WHh6rJorUwWPRuJzOk12qrNqYFeo=
+- NsDneuggZHtUY6PFWIpHKmjILJfcgfnhqNIkOGRVNHE=
+- kvsMCvMhogT2lPMPRCxoSM0xlHk/4AKRKx/2/PyakGQ=
+- B5H/2pLBFsfRG+aDYkII3nObJEQCWtCje/bXpgPpKeE=
+- 7daKsSt+ATYaYYc4gWOapYT+zUDbSU8pIDkJQxBCRUA=
+- YcjXYKOsN1mynr4fwtE9wDRA+WUfIMpZQ4ZUxsfzlJs=
+- 3wEOH2qrW9mdnj4RSUYX3V/T4EIuR1cT3oii9cgQ5Hk=
+- 25EgsT1S522iLdQ2sFbr2+ob+xL1e7WfJDQUkg7V+U0=
+- e1sXx4zBhZ8rcv/LlGzAdb0qiIPJvyLU4oTr9z+Az9c=
+- FKTm5De3OjUPKmpI0YznAWnRa+0JUhwDckbHavSc9Ik=
+- eKG8ZYANyYpz9kENU5nHvmJqcg9YliIjXRpHxY4MFlQ=
+- D/8odQNERqESlt1fZbmHPSMJ+hyy9KRY+uXkRpODkp0=
+- eu9UCBZRtVXStVm4HvECBceab3p0xNCkDDTh1hu+i+Q=
+- d1ucIAACxMwPQlMMKNk9D+KQmqqw+sba/pNk5qdIyf4=
+- rl3rhl05GvHFCINUNeCht9lA83fk0iKEA6fbrnYI6WI=
+- YmXbDIxdsWGeVzNIHu1kZPiyxuT6DZ8FzxAFZpZbbBs=
+- g3A1SQ8Iy2UQvXloaQgoCFbrKj0Hj93SGdUOR3CidHE=
+- klsTp++bS0lUlX6yjFl2cV6rpla+X28GjBmTsY1FdaI=
+- FLvj9yZIyNxXufGNmY3K/E5vjtpGtczCae4Ba3pyMDc=
+- eAsQz6qsSidfuLc5nJ2EPVqcRLzYYZGW2TJlR9ghLuE=
+- 8osXxChGDRypSRAAvp4D91zD4a8+lsfumoGu/fVLZyA=
+- jflQJlOPt+8FyL8VggWXXFww6Na/P65GBHPwBmjlYdM=
+- KIn1Rq+npmmlbhK0S4gVkT9fntWtmt1Pg0oYLUwgBQQ=
+- SJH4VkgKkOLxEyVVz0YX/QfR+uwcOzffCwnIs7XMsa4=
+- a4QZn1Y5Y+gfAjZLNRVT+N9BatzYkaoap87XVgjD9FM=
+- xoaJszjYRe6fSMnTMW8l/ehGL4xK5Gw+KMubLQrXmRk=
+- Sss+YmQcGYr1x2nIkH4BBGYGyG2yTYX+KhsubpKjNDU=
+- 00R2UruKX7spyFQjZm1kWlDBLUe5sDfS4OMm7wkiq+o=
+- sB6wAIBQt4gJxHhILbtVCjlI62FtYbVjNoVnQZKrV3k=
+- vQhetZpV9/cOc37wjLZTxEec+Agh1kxI4RMCizuViik=
+- DZQEOF6l7BHopBQYfrX0cw4JosBmIvLQcxIcRsYGfcQ=
+- GFR1XADNu8hYU43w0J33aHqWBnfYyImQ+GbIT0TyZ6o=
+- tO87Ct+94R/2R0PXZOvK1gNcZ3DpuADMXSrxbJGl3e8=
+- DjyxN6RigDVuy2akRyfPizmLgUvSI49zY4NR+9GUsUg=
+- SrWBHoqgSxqGsaepDsKFCUfzlJmu10WzpxajkBGfPvU=
+- mVE1vk46jWILVacl/3FUybslXK4C7guVAA+lQ5/60Vw=
+- XNPmQmdOiT6dP+3DnsU9FAmX/XDjQ8rGFkZNVUucO50=
+- KKuJUVzilGVR9PP0RGbYPvZ4aMGpCZ8UxYsZ4bKDmoM=
+- s/sohxuWETfHOMCDXZg1miPqQaal7BBxwDeP1aqm3lo=
+- rdumoVWVkIb+MoeuLSxC1o7ly9lfp+pprY7l3y8vMeo=
+- QoXWacyOufGoYEe/UEkY6xGyUsJV2MSwx5H+UhOjuLI=
+- gjXANKTeny3E4ROJTxVWzcA9KrClP23vfTRGQnxgQko=
+- IaqeWnt7y4zlGrHzorovdAWZCdA/FaROMsqtw7vtkw4=
+- tYrRGQbMZJCTZPRQez49d10xzF+6g3aMFngNsEm6VTc=
+- h0B4QUDYVIZbKqDybSRWOnTRn4YVd4T87jzmiDJKzbQ=
+- iLsNjS3Acu/wkNwZ2zVtZYjhu6rz8R0UEHuLn9xQgsg=
+- G3T9gp3e+QjjVqS5wKBZe1YoIREpn8tINNCDRYmLaL0=
+- "+vmeEfjfz8XU0FXbNAtig7DdkB3C/fLOy/JdHRz8GK4="
+- 6RrI6Ot1FCCPyE6T5VLGtKaxjAr7FWfa4E8x27IYmbk=
+- 3SxHrF+VSLrdlLKZCGI37QFrYQ+bm3i1bmWEpDg/mIM=
+- KhbMd+/nacW1qc2b1TxAGgnHGL8Dx+a6CeY+AL7Iiuw=
+- ELpGatN0J5uY81p4IB9dTJrfNqoCJ/kAuRCiK71La1s=
+- fgcXhqMFWjfGsuS2SWV/0mtKmQHYV+HYn6P52sY/Wzo=
+- JMBxzWZi5oMFNY4ANeT314s4yKf8Kiu6zzs6ZDWmI+A=
+- 0pqr5qim5qsahOSbLeLvs4FY3uIxn4opwyP0ba99Y1k=
+- GFVWAvGaJIivvwfBJezJs2VKgD5DbxRkUkKxrDB6YoY=
+- ooe6x/v8uQG8TAZAyLozFNL63UcfEeL20JG0TqY9ALY=
+- v5ksg3tA0y2I7ZUlZbPbxJoFt7Q/nrockAZKq3HqUUM=
+- 2OSp9hChZifCyGz8UXfA6Op7tSqYwGMfX3ZxwS2wpno=
+- W7ycbxUpcdjT0cQM2oHwDi2Th+yV8xK8gtcF0JUVmAU=
+- ve/0qcB1+jQnMZHxH0e1ciHsheyiRZ7z2IIpA9l2Z4Y=
+- DqrTjkSQtv+kh4hx8/+X2MqhUJEgtJvxhiVhxGrG0ec=
+- maUCjsNR8s/c9GxY7O6wh35liT+6DHAIqJHZ/23mECA=
+- r1DjKjslY+/4eppBigw0XYaIzciEhGV8quB80inf1SU=
+- KBT/+Z/AFuseJk05sGJjrN2ujbs08RsYtQfOQ8QJYfw=
+- wWwDs7TiSI9/m5hxLnEuvasUyjVIignl1Vz1e3ptRAs=
+- vXHfj1UdmG6OUId/rJpS9h9FQzOe1YgXKLnzZ1C658A=
+- dssk/+heBCWk4ap95/NpscxO6iRcMCWQ0ZxxIHnjBWE=
+- E/bWZ3f0tZhwGWqmzJVze23DxBxDRff5z6sAWIQo+vc=
+- 4yC3VJFR2a4PdEJ3tZtwY0OtMN1xYq2eHiziU4uPw+U=
+- Lxu3y6OBx3tjMhMP1s1qm6kVuJGAzTKxeouTceIsOzw=
+- PMA/awwvG80iMdVU7kMHYipQ9U9V+2DDQER2aPjb79Y=
+- 5xuvL/CV7ITCD9p78mvWYAiQRA5H0z+i2fT39g28Z/0=
+- rBa4ncLgBXKGDqGRi01UbgnLKA2CQtlMi2C7kqmqdn8=
+- Bhepa6w9XG8oZqCMrSB4ZSntY8qOuyiGMwRIDBvzrcs=
+- Q5z7MDJLLxBY2zUErGaTiDAqr6jUBJMQlS1CBLO8Rdk=
+- 3Q0q8f1XXDOPaCQwkCHNsJCgs218DBJApqJZBFcjgfg=
+- SjLrbtlLNv8FF4KoWo3dhLL6gnte1sFpsKUdcMe3944=
+- atuWskxWXPijy4jjuaarssdgK4A6js9R072RntVnPBw=
+- PNLuSXretLTa8u84CcLTFDPUR4FdJDz42/XBsKmETQU=
+- TrS5skSgBAJZvN0SWXcXpcivGGh9076Ni2IsOZPO8Wc=
+- 864iMXH5OrcVKBGV6mb99zhr2GBosf7Q5+vGphl3GF0=
+- HVYqXAWsjH/72dupc+P/sHp+SrLgX0bIr7mvr6BfqeY=
+- nNn8lgg+FJkdbC0UjGhbGNUSpM+bmGq68llVakNP+bY=
+- js7ciWNLmGvNbPsPenGw3+bwhdSi4XM+SOXZ1dwQ+V4=
+- Rk8MSGOiQPa7T5fqPHD1XE2jTUUK9YT+sbUCGPI4WSM=
+- "+SigciPliK9a1iHI7Q9x5UlaGzQXf5kiRPa30OGnVI4="
+- kadfRD+GCxDJU1hztiMstjSbKAdjMwvpI18MRVOtJnI=
+- AcBikd8qWOAvANta7wjdSJLpnzpKh90XsEKyK5dQAD4=
+- PK1SzjI/rXqAVZxQSZw5V7sQ6A5P69yNJV2SJnrJU0o=
+- TtHnhVkNUj4opdaX9eyTYVgUC0qEm2F/66hj+w9Djhs=
+- X46AZxwKWZFSMiJzrbrk2DmJDYOEseLWdnOjW1oSIHQ=
+- XL+KrGWiA14aSK75snaia515aWGc+F0V26XYNbGVLy8=
+- 1iBFTutW32S7FTlKDELxKvt+BZ6GqmCQBQ+VJtlBNaM=
+- v62ADo8PM+7xDC117Jcd89ZZZfyZVLDgO0z1a0CjizI=
+- "/ZaZwx+Px5JTXWH/GsNHAPtJ/85Nr6NAW3Q+tzKJ91g="
+- BbStE7kPXzaD4hj7/d/v/9WV4t7UGVEgCowuGEHipzg=
+- tcZ6UIeatgAnAdd7m2H2wcSpbTRUBjYwyCa0Clk58aM=
+- 46XmPXRG1CaOdfQZtqqpF8G/wRjrSYLv0lByJxCwMv4=
+- "/dKOUbBWvoLP0ahF/Gmy7tKdPvlwDCZKcEDb52SnOTU="
+- Bhs39EQ9WGuCz0ZNie8JYYzrpfHOLQEaRUxvpOgQUno=
+- Q1Vw1K+BkkfdiTTcIgQhrSz+gJgaJGD3CDcanzeRCJY=
+- Tjf6WjvL4oyxPApsyGoJAcypQeI5zYNrMhmwrc/a2qA=
+- bj1AO//K9RrXSU7r4MOSyH6HKbIueP1a8K1MMD1S0k4=
+- EpSPOcvRu0D2YQCtFkd5tZX3H2B6Ov8ouT3i35TP2Fk=
+- N5i8y+Tv0RASTGpHbBzSXdeXnaXL72nQUm28vNoD25I=
+- ZXS4aZBj03fvjAnxnjW4KhgHUa7TP6JF+EXHDSSDqJY=
+- 4np2hrgCjP7ntX2VTDq8z7KnAZaJJfUrvUgud75d4Ls=
+- FL3Nb9ZBgK9ed5Hfkbavjpo+e8hEmX64wpJScG35fKU=
+- g0jPQnLjpGI5sZstAqI3AkNwyNpe0fF3JTjMx8NMhCQ=
+- zERCkq7ctff+4pVMtiaHxOCH8QUtj0xHmI0xcfzgS8w=
+- XyIn5AxDRJqQM2xhqposrChP/EV/hCgSIH7irJFoG/I=
+- fbqTAj9DLH2FLli06cRQ0GXPts/E3rGsS9f5H35nOe0=
+- 1KSQ/2sLLUsFrKQvkIHlW3gT3eq/k+PyGWmmzDw9qj8=
+- mfd4SKJgJ4izRzkozDFOS9u0fi1z7dqiQ6m1UfL61/M=
+- lqHF3Klpb9n4YNp1SIkUlqlOpYOP+CT8H1sq5Zr/ykA=
+- 4cwLDWuza0LglQ6fIDdRzqTusKRAJVsybYYfTX4o3Es=
+- FBn0vIIywZy4HH/AL0FjG5lMr1ti3zSQ1IPgzdGBsXE=
+- utuDS1S7jmjAUTtx+LQl47s4v6MKyF01F6GeLFYpgqA=
+- u3TrmoT/Go/80ERa3rfpXeqlF6tjya9h/1QvFPYArKo=
+- 5F/u5/jRBg8bNMdoikG9l09sTUijW+q2TmxpDgdNEeQ=
+- gVjYSprbJopptG6FNJ0UV9GB3dFxiMxhK1UoMs32Pm0=
+- lEhmg8cBlAm5gqG5mhCnmBxOu7tR+ZntCHrOe3eOyGs=
+- kd0eoMoj8Dnp7VRbAslAKCskoDlN6thDeJM3WY91YuY=
+- AwPALDSwJuR8w8maZDxQhjgH068n0aUT82X/0JHQ8d0=
+- BcecW4vcLl0jDxw13B9m85VVeLayQ68FAkZxYQIhSRM=
+- sxKX//wBhykctXUyvoIBPOtNMyeEcJ+EoqEPcJDiRy0=
+- "+dqF+HAeJ1q0vlllff6a31+gaP6xk/x+Y61i5YgdmrA="
+- VDHZD+hKa+D5UMkLS7Pa4kXjkAYikfaJCkHtm8g3TaM=
+- PFsR++ABKHONwErjpSo0A38d+1WQadNt+S96fULBQMU=
+- Ds0l5wMjXN94E2rEzKrVrJCy7GniGq0rEex4WWY67KY=
+- "+ecUoW/ml4vyA2+/jXQih6DCQvg1zgS4L2+OyXF67ig="
+- Iym062M6LmLMwbbxX4e/RpK+az5+AG4Sxzve1JhyzL0=
+- gPD/G9P6MxV1Hsgy11p4rU/NUsPPS29YcVCkkxtEiyo=
+- Y4fGTXDn9ZBp6XRlO+jQt87GJt6zCerDU2K47jwCvTA=
+- Ou39bfNKsjtbEPsixmGFqzRunBx5/tcObUdj2HCPsuI=
+- LNZzdy0cZ0aysYCK57ywiLIgC+IslV1E9CxElTcL+ZE=
+- FJvKLe3GRaNGF4rYkO88xCR7v013z4fom+gWd7LBTSs=
+- GXmb/HAOQQU23BnxhKbl5KFxjEk0ITH3KHISLz8IBpw=
+- 9Ew3P8aF6C7xKyLAbWWGYo7sSw26jjgp2Oj8St1kh/I=
+- kBZoxA4anKbiLyStO8jA7HcN1uZXO/NrGzwzr6fEHPk=
+- bq4HwjFHN0PUzRl6A/YLENFmmMeCTiz6J6XwNXP2VgE=
+- Tfm4ROmPM3MyFellyQerE7jZUiHieGzhs1+yH8zA5F8=
+- ECukvxleU2N8BxJKPxpQc+3ixlpAKUFk9ceo08sT/YI=
+- zrdgoY+k5BBQ9k2LRxdBfiPFXOd5mV/tFJidBPaYxtE=
+- JREr6HQtN6I5fazgGbaFNN6H4MlE48kiuBbeXPdm0A0=
+- hY8pA8m6P5HMqQfvVTLDcGkujWTNtQ6BlrF4CPKsTIs=
+- FDl1wTZgscRBn01adXsmQgCGnXqw36wYGQ06QlctpL8=
+- iiYPBp5lMwb1wwTPQ4r0YnyE7aazjej8zLgBuSGBs/8=
+- 5M3SpSzUq0kh5upBE+Gnb/NOGTMc6/TfvkQW46uVG14=
+- "+jjiBcOzcbXkvZa5sQ5UTbK6bNfIg/fSBoDUavxMJ+s="
+- nlc8+f9ubLOB9LHjtUBhdoXuXwOT8WSV7fZV+4Z0za0=
+- R2TsRslJS3/uEUiOE0ngOGSF86G8OmUVWMlWOV30q24=
+- M1ankolPcEMcn3f/piA4W48YCKeoABdzLXoas0QFwo4=
+- nu8gbv9L+S9AhblRXRDah0KKObIHbmn9mXCHdjpzHWM=
+- nYLmiMTowUrbXr1VoDfRM0TJSuAWScdujUEfg8XoPSU=
+- dcHV55Kuis5tel67oJU58SMFIIuv0jmMzJcddw3tq9Q=
+- LqEHPUImlFqXF+I+FV/Ft1JoFDyHjvP2QGmMmNYTS3M=
+- YAXtVkmW/vJg4fzbLhWuBLbRroeYSTLIuQqL+8zciZU=
+- hURx084gjZkf3snIlKzeYGUR+YGvz/QFmCwK8qbzKTQ=
+- ukwURYWdlI7shWCChwIuFqd83PAM/4ti4KK5Rg4k4DE=
+- KC9grw2RWstcz6wpZTLoc3yGvLqoS03v5JHsv0QwnHw=
+- ycQzkKz54azXvaaeyrouSKcEf8b6egqZPwx5mwpU0eI=
+- iMWjtrmiAuXU0/Jmyj3t1i//Kqk/GfD1qg74uILpwoU=
+- jKmhQMEMDeEFbomSAQB1ML6ElUn+78Qmt/MYq4mb6Fc=
+- ia+rrhkoHsXYzJE/gs8fdlUgPrmTqjMLUURf1/fnrX0=
+- M+UcpbDrrO1M4Q5dug3BphWU/9c1LKkeJ98sSX5yE2E=
+- 8FEAHzINvZrIciVDKA12kp3DhwYMWbhogXMfOuNx2j8=
+- "/OS0p7+PKlROgomwq0Af6cJFVm09OiUNGditWQlQCdA="
+- 3fXuBPbdQ/mN8zbVjBefciyPv6XiQ/lMRL0MC7HTD2M=
+- zGZT+E3qt1idZvO33KqdSEwjcZVJJuwdbleaECa7Fvo=
+- O+SSrASeIis19e8ZuRZbOfia2h9k9XFso7ExpbXL4tA=
+- 1K3P+btNHJZYk7F23RPZ/NkTQ6Nm9tqP0Xi/qA/N4GY=
+- pmJEfTEyrYBJp3jEGzaW1lVWwGsHQiUXF/l2fvxoQ9U=
+- "/emWyPPP8INInco6CGAR3k9mG6Pnoy/Wkg9M5PO+6DY="
+- gf6S1dG2bLpkNaCSkiHpmxiNiRTHoEx+gexSSAcSyTc=
+- JQUqTRFiXFTWS5PdtXGaLcrUBJryOWa3vlw+trG+NgM=
+- XQR3s2C4gcCzVbAxqYLGTHWvQmR/3Wpwv1NtRoo7yY0=
+- 40ogLlM1PZhtDpqfpcHR6vVaMAE59W1U6uNEBUmxTUs=
+- dJ3lfA1bCeLHT4qWRMR/p3atqZ2Rkb/ItWJoaX63kUA=
+- m1pfxTvDAH/2gnjxS3ymFZbBxAGg9YGV2DEKgY951VU=
+- S1urMD5qRDRkWC3EtCjhyivVTlN4gM2mdxQvnch5t/4=
+- YkhudQZ6+icX2zzvEPxfuNg55kVZKPcLhuAIDm0Z9eg=
+- epvIMhnOGWotwLA2/tomJA/LMTcHf2fTukkbbuG9A8Y=
+- 479n+oTik5OpJ2lUg6w/DVBvX4HZJ/AKgGnTYciFyqc=
+- hHkm/pv+3qxUCobANQKDm6Jlj0xwUp0OhZK+0yM+nBo=
+- zhLYiNQ7a9MHm0PtKAyNgh+JLIia0rR7yWSFIYineyE=
+- lwDOpCDz85yAnFUAYadQvzNjF6I6GxWhC7JoPhaNk0A=
+- 7y3wtTnGwj3g9MvkJkjDAa4OIuiHNApFmftO9OJnjkg=
+- WbLa3z2EfK69ts0q5GLrxpF+B0uc0/lS3EEXGkm4fO8=
+- H1D7VhDaC74dSIlaKkEbwSnf3OoA7V9TBY7s/3/Egn8=
+- JWe7YwsVHHYPWdMsWVeI9sedssKXlMFW2dXidlOa3/0=
+- sZlsHafhA+lbMsXU3sEKZfeDF8jBoYSMz+1RajFMFOE=
+- hfOfVRScE3O9TafHjcjfXofmEjUZ/8tB5/GhXLnKe9c=
+- 768fe+y5nxGhV9a2hMTjFdiv48rmYzy7QT5qLjLbSEA=
+- Av6z6Tx5IcYQe/7o/rikGVN8NS8r9mDd2Anw/SjXEWI=
+- H83SqQH0qpQAge7YhFw8YYft8tUXZejwLce6B565BVE=
+- h8Q0I6N4efWv44UMOJXlqc2LTOlYcIdFe5O9Hq/RyKc=
+- "/rxKxm7jq60s4q0+oWGSENlmvcsbgnvqgMstKh1rM3I="
+- 4MyFQeo1i0IrFClJCM5OKSdOptESkxerH3qXwF7cvCk=
+- WvJ+Dt69K7W5XXiVO4n6J22hihnD+Uxs5v2ICV7cLbQ=
+- HFXqbq6zHiDdfChuJlcBsDOHbiRKYKX/BEUmnJgInEA=
+- 1oRR1BqjUXivmgRib5sthC4xNk8YVhQ6+pom9CCF83Y=
+- 1EBL10BIeA6Kz7zSfzFi/PhhpR21m2vrflz5aTlEfMo=
+- vXg1y4WPbxhXKJ9ml+in8h/bhlSoul8m86dXNeVmFY4=
+- iwBALHvJK/1f6vjtW5ESLghgLigO8O47nLzYZ+ctF70=
+- DObC294+SLUGiEDC+AfpA8b5n7X6oBynKxtxt2ACvf0=
+- voxtO6ZNAZ7Qx8ctrNdSpGhCt7dLfEcnd3qNcgMYc18=
+- rsahKk8N7QLijhYR+gzCahdXOXpt6fZ7Ne8IBAhdrr8=
+- vte0Pa1Z3rqqjfv+qPqDxMC6vbQ2kLWOjTkv1SbTGTs=
+- 6kJYYfcdDrj/sUkRElXSsvjWz98eEARgoefTeucrBdQ=
+- UFy6TCurBz/JoDdEYELhtqB+nR+K9Opz2dfXZ/Q0WPw=
+- aPOJOE5fAROUUtrVO7K/MojD1aVW9wepoP0o0QepCFc=
+- IOW8EayYbyLw96Ebm7PxuGqFy3uDFmZOxQRcDtoKhiM=
+- Hla7jd9krQXReqH74Phk7d4N1fZPSH+vJtCwY+LcinY=
+- mFxV17m93SaHZ9dNkKm+ysB3Z7urTx6AiejzvemwGnY=
+- fNtK6pmofCJNMvWPmu0hyf9NL0Q9U3/YFXf8LNqGlg0=
+- BFs4nKvmhC5MN3kvygmWT2JHbcHIfBBZj8XmLiNsdWQ=
+- kFIbyByuj3Dg2J2/P40cjxgad8Rsv6/QW17nTE8sow0=
+- 8rXoNiS5VjEpTyIoFr4yoC55Wl3OsbPzIF7Bvr0JdtQ=
+- g0kdAElsJ6JIZcj9XwDz+TDi13uRyfKTZB7Hov866P8=
+- 29b8CmXIJbs/4B+MnZqErrsdGijQ4Hk0oQbXlUNQyWs=
+- aWWdPmMfQRLEzH6d1QGu7bcDJ0FBOJ3tqBMLvo7TQZI=
+- o/3p8MZWvJbJ5V31/QBOu53Dxzq+YaJNeqNkBYEyp7Y=
+- 35eTUfFErNwVk4VQSHs+ZLE1ko6bIOiHRT/rUwyX0ek=
+- Qze8Uwn6GptsiagvKwFegbZLdz20zYU6eghpJQ3khSo=
+- dbL1jiVTtiA4DBgWtWeHAmvdGjwQYSlq/ppV+QFFNsY=
+- 5o04eGlbj6ukln+IAPP3FS3LQGTVd6vgZZc16pUg+2M=
+- FFRRdkuVYg5gSKmWado9Wx16xv/Tf4HmyWqUDwgQzcM=
+- 3O3516E1WTrBDCeIbshvx4Ub4AS5mCj1Qd0GB5tU68U=
+- h2Ma3hV+Gvh+xJPR14jtowUHvLc/CQfu/dOJFOAgZ6U=
+- gHzJiU0RMREdjHI4qQSy24+rzZgICDvlK4flx2G5KVQ=
+- UiSd6faBG9ep6q5wtJ5Zkw0YhgJk0pNSoJ9QsDAnt20=
+- 4+Ud1CyqoxcSBMKX6cz62z9zH6p3BARgkXfupIFAGTs=
+- 7EBkxth84wQop+PlLpaNAAG00jC89CjUgxRuONOjYdU=
+- qIcKkJsL/h6LgnhfHJifI6DY5il4o2QDEZBBX8DIh/I=
+- adYh3gyi9VhgpZdLA1t6RXCwreNjFivTHhEsHUdxwmE=
+- 6a+Kd4CYRi3nfoFT8NB3JFhsTUUAVpCj7CKTG3lcntE=
+- drGIO0qAlQijui84eN2OnQP4iUWpkau1D6HIeTPQIDg=
+- tPaR69bUvzy637YwzXgCi3VhAIlmRY9lY02iqoesfKg=
+- CvN77hl3wDn8UdZq7DLXim2m/U68Yy6rm1tNiIenYak=
+- WHOfAWB0XPi4Gqj2njMxYIy1RtmKuoV5MAiCNbvvmkw=
+- 2iUVm3jYk4IXX0kpnyknXhs8+LUrya9al/JdDHTRD5Y=
+- Ipq/qhfEqlCJlfO57M3ndy6FuPdBY9jkrmZgL+/REq8=
+- mc0CkhVxCpHEtRUklmdK8YkNCF3bBVEibC9wUMdYS1U=
+- r7Fc2Jw5azsxWbSDwg5XvIXgXOcOypsReb6EpKAb/q4=
+- ZsnCrnbehqs+n1Hmk+TWpOt1Fxfk9ZWaW0KBdE6sCmY=
+- nCW6dD8lm7sHiG1IQQvEAN15ak2lKMftReK/0ODlZwk=
+- sruEbmjuZQfJinnFHxvPpqFdYenBl1fGnCdhdmMn5Js=
+- j6Dy5P+/7zxKdAxbNw5Mb1c9tUZDkUytmAlsD95yvfk=
+- "+sdbTelFSzBtlAoQ4f3eCSfhkS/39ZMlisH81UNBHLU="
+- ePL/mcVxLHxu/BU64+fqTIlgXbaKzvpSXEZlUQPzuBU=
+- B4woNZICD1ZWsf82rTEjJmJwwfDe5Dm0P3I9ZM9neWs=
+- MJBvGg6F6WhTN/FrYiI/p9TGpxLd/IUg6q6tmIbNX3M=
+- nX3rllAxUfQQFxg0L1bYxCuxxPw94kaME8q2H72utRs=
+- DAzg4RdgBmHiTJjV4WVkzwhYH/l+z0SBkQHtaNUxYr0=
+- chdz0ueMCz+vYXSSlkkaUBtKQHSr8lHPJoqVeiBhJ48=
+- MhIp6CRHXRhOmp/JYMIT3rRDeV8OkjSB7WWZ+f6BPMw=
+- lH1RqfpQJrl5ACn81iLkDOmqrt8N9fQViK//od5i7m8=
+- C7h41OQn+POkilZmyp0/chDkr/uWHWs8uuUsAT5WLX0=
+- gAp/aAJf0JzlQDFv0LWD/IYLNkg9vBip8jnEl8ferf8=
+- YDAp5Ca90am5IQCDBAIoTpDraQ+o8O/kuoAKbLLmsEA=
+- th7Q9sdZdd9Exem5hb2cf2N/N1Dt5fXX4ppcMz0v5XA=
+- IuAtfzI1psMOKFSXuXxdziasoItlpgb4TXGKQE+zofY=
+- CGrVxOhZ6n0AJLODpnS5x51xnTzDECTL3a/h4eZ1JS4=
+- 4x+9mp/U4Qgh/8KJDwkxoPueYF7Ay+s2SyKUe4V+jc0=
+- KMYrOfESZdnX6EPfNDBuLx2KXOlqUoyXkZTPRhNtwHs=
+- 1O+HlsYkNJ37jMcRHJZbrsOWBfT5DGY8kggYINfdOsY=
+- E3/TlQtWaOsk4fAIO4elWRWUX1qv25KB++SyA+VCP2o=
+- kaN+L8us3cJzSbKW/DfwDSnePW3W9mP2Pedj7EF8XV8=
+- F/lxwxSqEcDbo74dTollKEjHTRQCugu4KRMjA7Zihuo=
+- BkosIdwMtxcYlwxEgUlT7pQla2gxh0SIBjryymQWnSE=
+- s6C5oHPfjiFfHKMG1D8BAGcZAKADisL/d3gRvRX8hfQ=
+- ETkdTRB1oipqiEW2DGwvUn/f2XmPIKPeSxR9jJDB4hA=
+- GY8RCccW+aJOk9XJYD8gC3cx2wZgM1jPQNNXTq3gIEk=
+- 3AP3PqQm9Y7TVONZOn81JaxiXwl17PAGkF8q2Ezojqs=
+- LVxy96V4+g15/uQT79FDp454W/2dhp0Z4Iqk1j65glw=
+- 9NAwGWahOsKBptVfoE/Py7fQhCcfwBu1bkAZ/VGv/io=
+- caQX9j97G0KoECH8artSzQewSdKDFbcGJht5Pdi5ibI=
+- BcpaZgBXqwyDdNZeW0kyf6FZjwVccmSlOA/Qmy8PpSo=
+- PMFPAya3quxBvDU4umHaeAlQOyRar30liD1EXyZIN0Y=
+- qNIMeqNzIkKYJuHzz6uwi7AtKyvBPBKyJyzCP/Q4lpI=
+- Gz2EElFYuMGV0PTejiGM7EjIep8wBTe7SEZrpSH2xeo=
+- Ec20FO3OOkyXHPBSJq8Ew/SVuYUiSOJjv7TNF9jako8=
+- qs/cF84i8B+XcX7DLhoFWGhYZdI0cmGUnUg3tIxEObc=
+- G26h0F3ZyRNmyjbMoWSxxxiUEMuX9KMGzRSf9oA/Qmg=
+- rJ7zdYoPMmNCklQOPJEHUwqdILFSIsFIJVOg7JrGdvU=
+- 2lsjUwjqx3gT+W56MWsrPONLY08eDYapYaqG7C3t87s=
+- rpPTAg7wpD4Z1cggWK0G0oyM898fe5WoiBATgBb3wdQ=
+- QNJH1Tl7C6lh/58oMsfs3Y9kqB9ThanDet5MwJu3CZI=
+- H6Gr6pcgiuyyTJ310nx6q/gl2Iu+Ds2oMZLPtRroc8A=
+- ycuuMhNbUSIl5ghrJIqzO6s93KUq3J0b3oT/DAwnMMw=
+- "+TzXtEZC29eA5ATdAVSthF92Ddog+XzuHl16otChMFM="
+- E5JBWE7aYaE86ajZq0TrHC+VdtFmIdcpr+RjX5+raiw=
+- 9O0cAfxj8g26WZTGE8cwP6LJ2p+c8jefSjIiyB1f3e8=
+- jCLjgI6suBKFY9m67H8te6PwrY5n0kTJEdBu0FBP5Rg=
+- b/KngzNxmMPn36TcY1zcnIqiL6SKIxkBeotFl0mUYsA=
+- ZU1ZXdoWrbiZQ/x1W6GQNKy4wJ91ty3Z73rUJ9WgFl0=
+- QcEazUi/cPNgQreNY5MCAvuUZFdWcidC4VcMeQJU768=
+- wVf3btocJplIJNi52cQK91v76+D++wAwR5EjRmRoTyM=
+- eoPrjuGNr0ODw7fJL9jwzN9hKxfyII9BEUKmt728P6g=
+- RE6GzVL4bn/VnaMXG4LZCRuY+TGV0EYa3xfvDdoeI/I=
+- WbJFF7IkyJxumgwhQTC89W6tHue0fXO7JwaazixMil4=
+- E2jZ5OwyYLCrzCEnDJiT3qliPCya4juDkqLuekHEIdU=
+- dICN4QWg1M5Cia9dlgiqy3PPhbRQuhN6YRdYQAdhz/U=
+- wLdCXGuST97oUnWsY+sorZ17kV5MJ+MiA8VnvGweNsc=
+- "+qG0rUzOj/hBsg1qieJ9f+zh1ZwYTbcPQInBehUvg1E="
+- Dal5qPwl7xyfZ60A/K7qbL58QW0heHyzhSboeRYQrh8=
+- iUAWdinADBE1MNWaV0CfBRyPbkKGqUlvGHWStkHP2kI=
+- KYOczAfGGFtPCZHCRISr7OyeoP98gpSy+T114c/ODTM=
+- "/Ea5e49sAmUrgTqncccKTjJzUdZ0TuD0hdqpdthHGD4="
+- 0m3dCjIkiW/qmElSu1Z6/zpIyKYlzJN7pVglVfzXQuU=
+- tY7tLgkC5ohwT95rYoQPnw8lRRjUejgcJ0IwHXh4T04=
+- il9m2WK84MDyt+65NchsWRFUQTEKpoRwVrW6UrYi7ZQ=
+- 7GOwSFJpTzeS2BZ2FrYw/jRAs81c350kmt/XCXwb+FQ=
+- 0I5aaqn9/Brp//AHsS5l1XkTaL6jF9ZfADZeVENxLhg=
+- Vl37GEwLCsxCyl+aeviZgAeeZtFMmJc8b+/vEyEu6eA=
+- ACO/scC9kKXXQvgEFKdEz/B80FiVWWO6aRFVo3RLVGU=
+- wg5P06cvNKQzEuvNx1cmTaRfGF9mfyv9lGX6Gi1eUBw=
+- Jb7HA3Mj2BGoq0F3uQqm3CcY0FJSuZPyvR8OcPSWneg=
+- Lv39bORofAGlsFCLV6ZPVfXWWkoUluNaGngnlVFhafI=
+- 9RKz2Zjh84ngm+SX00EyTG91rL2uCtioL4wKouNFKxk=
+- aHwL0achJtl+EI/YjL9o0z/hjTuDWjcaZkVhX3LSmRU=
+- Y5qcugGIT3paREQ4F9d89ZIE1arKcZmqFSlyszoxhRM=
+- HhdIwCM5V5fuFsKIry4qTyLPjpJL1Gj1ZH8/pmnnl8M=
+- K8l8LVtXzCdIyP3U0eM8zpcQ6krkVpNIOmrrKycVMkg=
+- aIWiGFH7sR01QvyU0IWsTS/L3rFK+Ttib3MKW6hUMMc=
+- uuAYtCJ66okLHznmhrwVEk6ByFBxee/YHJY1WTN36Ys=
+- OtWBY+PKl/7Of+o5JmduyRiCE8Z57QUVRFJ4lncgRFM=
+- kcljo8/Fre6lRUR1pIsHZk9OlrgCQyMu8017LHTVOTM=
+- ZvwLvY+5pR7JYpv1oYuktV00x3sUOUTLjxkbcBSpwNE=
+- NTFAiA3VZ5hPrqC0hm/F7Witqgtm/iiWjimD/y1c1UQ=
+- BDBvtDjy3e6dWYbUz3zbhmFR3UzeOBNP6NEiGqC1eXE=
+- riXWer41kM8cU/jemMmXxDMwqM3OAxdboOzNBmV04+A=
+- 3/gQxjmLp2J+q4WVpUXNcbG/rJFJDPOQ62hbrfEKi0E=
+- P3HcX2LiGrreDIO5lZUOwpseS06xkW+2M2SqdWWiMXE=
+- LVzqfqsD0cysqpL0600IOCNHjStAfalCWiTTmlLLQ8g=
+- d4fkdqjemXbney6g7kwQ62BmMmtL0gvmU0DHGETw4OM=
+- pK+9QkW1dM+Hg5Tv399lF9DCtK/zyvUWpL4WWHuYA2U=
+- be1IEkrhY4eKI8XxmvU6ttdSuRCnvKGfYeQLK2zafAY=
+- BxdywSnFsyhS1SFZResRSrwwHMPnI1llg0XSE6QZCWA=
+- Pv19A7LWgj5LzucfE7Zans9Kf/hdsNQ7xfe0m8W3fQU=
+- 8IT55ySOeAygv/YFW+C6PuKxMzkfHF2vxqYucCKg7Kc=
+- PSiEUVLD1e/zsEilGtRRT5SmmWkPhXsiW7N492Knoac=
+- TxKrhYSczebVWvG7/xtJMZtch96ah+d31qoPsgNAgnw=
+- 2TMn8RFuobPA9coIQbhRHvqnPjE7W4PflRxfIc2ieLs=
+- zMwAupmXRSuVnpe0bRiEA76AoFXhDo9bjEm7Vl0VmJI=
+- "+0k6IMdGr0XHOmo9k58GcUbG/BuYaVomKpRMyRpiGZ8="
+- TztUn9VXIEfjevZF/4Y20e3KF0X89MFdjIg3ieVytjE=
+- vKbLm2SMNd6lWDLrl/GCwEF3cLAdLD/kg+gQjf8xqx0=
+- 9fVa4xxHTB84QZHFNh6gCmHOEl6B9F4Q1V9Ss9HAx88=
+- 25DXvIGS4FL7QJcK9lm3hJ/dE5rRq9/wi7NdRrtSdOo=
+- b5tBglNCDrA5g2PFxlSCsE1uLut2tYLwYNYKb49Wr+0=
+- 4f+euC22MMQkuYQgcKx1V150KgamOtrPDNm26lzlFxs=
+- b41kOLtOJAkK/CC9+YBhaUtGh9HvtDX0NL6O+8OOgIc=
+- 4FYc8TnUFC4bONd7arLb8kE17sRDW/mALyJcd45Dt4Y=
+- iPsqshwb6kfVTqsVXlQcvvUdPqh5OPioskI422hcPaE=
+- OpNl1pX7os6kHru3wDE3TRsZpT1U5AjS9t3Jnhxy87A=
+- Jt5B0jqBmbq+BKmPhzK1B2RWG7ScMkr3BX09FP9eiOY=
+- LKrC5CqI5BTPc9g1ioKsyZagD6ughyQb1t7LLQ04xio=
+- We1fVlbr3tdWpzArszyut6l6OHLh8wjtHr9NlPK9+1E=
+- 51bQn/27GpofCJkduFACT40ltfn24q5rYqAmcj4ygdg=
+- B0gOOjnAm+ZNlasdh27WkgW/a9fwrTv143TwxeNVxJs=
+- Y7HPpolBgooz/rZ7rfy3kbTytj5kJdvxi89F17Vv3eY=
+- MO1IIYMn3bm746Fwia1HOeqI4dcv9xm7nW/M9bpRjtY=
+- 9UZdyrIw7ZWf04we7ggJcYc6PevWWLCyinx3HLE0Szc=
+- hdCCYuDmz2lurNTkjLItAky9OCdUO/zbp6AOO5TLmPg=
+- DWkSzcEi/WPjLgJkqw37ppj9b9I58WHMrOobCN3NjFo=
+- 5U6UB7Y/HSSzuZInQeCgHi1lZPhIDGHGMU/L9VoBeLI=
+- w/AKKiOv613b2QBB1nlNO4FUnpCF8ZGcpBo+IljQu1M=
+- McOVbcPpFONk/n+sU2wUp/1TP39dt0j/MZBxYChaxSQ=
+- VI8oMKmW6NCULTz+Ngi0AmqfpLcyXXFQMSz+v5ekfeo=
+- 4iHLscFNFgnkMwUrbaZkZHA9fggC9R03Ls6L5uBqqSE=
+- 2WL/Jl5Qgy74+sqnvUmAOqy1rkA5wnsWSIwQgh1eqb0=
+- zMaS37BLSp3fBFi3SfoGs4ojp3QwmwFXgEZhITa4oCE=
+- h+wRwq+E1sWnlqn/K6+zQbXjD2iBdYj1I1MYocW+X2M=
+- rVLWzfMCMn/q6ibZK0USvE35dxCXKlt3z5B2/aoWku4=
+- ga1vXDPf9TGmqvlxcLGVLkEuY6cyHiq4zwDCOnkT00Q=
+- xTzBqonY46FfhIA5QzPQDogOho5Z/Ox89a0cmaaYopE=
+- 138neBWyLTK/YlWDbAV5ibRPTxQ4te8DtCQF8/eD5EU=
+- mrdBucgbJz34alynPsvgd9vdxsXcEOL9KvSjLG6tdK8=
+- bh9W4xR7vnGDWQrRLRjGshfu4Npe0fhyxPiEAiGOh8A=
+- 9lO7OwP6Vq3MAgphRKvXgzOUngpEwD4MIauKZkolWeM=
+- RmWe6K3Y7GsIfCuhiTeEbgmDxuzk/U4Xz5IgXXzrx8s=
+- yBBBI8siJIVlX63YsGBFv913s2POeiHGHOMxWFRok28=
+- mkQMoPy8Mffl6Af99Wwq02MWBUE/XUivAroIssSuzB0=
+- 4w7PwcYdOXAuWk/bAB1iH26hbodN+jXJTAaDfG8kYCg=
+- 4jcW6rZrVo0t+u6drEd1P8yykjfhUsAyCdXxvrzwbmY=
+- RhcFexa9SUuTIGp9dLjiwWDJM/TM52zIy1Vnrk3imtE=
+- PS9j4Hs9sKa80Urc907OVh2+Ksk6+gT2Zqe/R7kZY5s=
+- hG/Wbndn02A0PfY0C2F8E8fIAbiudg5A1grkojrxq0A=
+- 2HPA+BerepJ2QCtDvc/0aL3pAoF6Wq6HWgLfap1p1rE=
+- m4dnBtoNynwOF2rc2qy1aceQ0nQI3vV+m0eqIrke2V8=
+- A+pBkedoA4MN8hMSc5GTYnzXTJMLCpYybZqvWacfYSM=
+- 4dO8EG3tIKlvXFCbtpGXjKCyvCU8naAyAHYizbbDc/c=
+- IcOlKawwQJEbDWXnhkStV2RDZby5bAgMrcrfdnVN8Uk=
+- Eiv2rMB/xI4kJ7nV9st1XlcNmkvu6gUeVUcbhFavyMs=
+- lf9An1+R+ADW4e3hN3WQucNTBOCCclDv0p4o3pkr3h8=
+- SRzVW1JOtU/esHOldJk7UC7bH/tYnV/3RcP4PTxBwY4=
+- MZmHg8pHycpmuSS60jskifDpIzCU2+GghmwEE0Dp8Rk=
+- ONXP48ps1+pJko7MetrH288sVK3BE8fGKTdaAQl5nrQ=
+- d5RhsNSwhBYjIFN4a+XA9TYjzxUKvDctrPMow0F2abo=
+- lZehTLZ3M38iULR2RBnLpyTuOvyM1w56ggcT1R87+lg=
+- 8Ah/VwgYpu+V/adNz880hXMfvNXFLnTZWpYqGce7xYc=
+- bmV0Px+90kzmDKRxnYJIHnN7ae0KPz4s/JQ6OmQ3oxU=
+- FXk5JoOiXamKc379IJd8Ve0B6rBiD+pQN+hBJXAYz1I=
+- N7aWcTpVzxrPzFK/5CUKPA0fQuhjZ+HY4iGuupanmvQ=
+- z2Fr1oj2NHZyy3AJAe2/isGxwp2RqmhKImoiNfT948I=
+- 8B7PW+WL67fsbfRRUUX3b18rX0KseA0sc/GHikWagq0=
+- ktdAKFpd6AXfHDVKQ20eZnjpz5HnIUaGFuu3uFjEbyQ=
+- dHBbJEdT96m1FOUCp6C+kcbERKIENPt49XOCQRdtZMc=
+- bBw7fx39xEU8zXgt856KqjBM521r8QBHJgtYI6bfHWU=
+- bkcVdKtHOrKZYYovu/jxMNADkWKaiOKiAgvDmLA7538=
+- HcWMK7bl/jVnNozczE5GFRP8hdC3fxdWjO0WSIbrLZM=
+- uEaB5y7aNjiW9PvSbGYP8aWFUXb+Lgfnuslgfhahes8=
+- Z600IaWWGozdrWX13h/2mGulCzuehm13Dz7hzXA7x1o=
+- U0HMZ+5qUuMkcwGqM/5XB4yo1GHIsLFsls+8hDNUsDw=
+- TTl5LZs1iuJQ3sAm2gG7VXoGqN92qxgGvwCmzxwuu40=
+- Hw84GKSNVUivWnajTRJcltAaNIMzyOv6wjjrrOO3RVg=
+- MuzguPF5XBuNybgElwe0Im5LQJpv3mxiTRVNwea/5p4=
+- l/zfdCPQNo0naOorqLBCbnLOcgRgBNgJcv9TbzRybOY=
+- tKTtBUpsvb5+d/Woj50n1gHGFBLSjMiyuQc0+Eo3p2Y=
+- peGL8X9YTnaGjSIuxzJ6oEeDhErbPtknb9Bl6603MB8=
+- UdkM6jlpFSap6wTTxDYxNzcFricgBHZKxJXpwNSADaY=
+- gnAlTGzKk+JVXP1dkxXKae7+fx71cihyTWDuKYPzPa0=
+- eUcwSHH0gSlquFDLMnSRytMxgEnPk97tGEsBjuIuwkE=
+- QW5IqRGG9hoCbZM7M+tNVTApIDYv88xYPgw9hWhxjhg=
+- DdFcX3gZdkWx+9PsCL87ywH4R9LQ3r6H7sH7sPLmylo=
+- vqocgBT3SIR3gkPLlIXPlUgjKQTfZmnP4gSFbBBbwHg=
+- icbUkUeGpQwDH5M5vqfCc5cxlXpq+hlQSMPA301dLh4=
+- miKHLh0BUrcJ4qe49O8byeI/mUqpZphKseafkQ5HBV8=
+- 1do//MFZLBILKDTBNsNdZhjF0MYqjUIqZlShIZaViS8=
+- 2D5eI5SJqsAt4RbHSRYga1o6yyviGjpq/y/KsDcYdV0=
+- Jj9ZpJ/ytkXLjM5KKGqH2drts2d6Jdq9XjrzZDgxMdo=
+- EzV+XsZaZ6HeOVR/WQo11d4OUJ06ADU/PN6zt4qHGzw=
+- DjcaGtgLZQlOVF/lVYmumQ6motox9YKNs6+JHJ2jFY4=
+- l8ZZgeA/8VqjcXnn9kIF2P029qcfQBKbrOO2rI2fiQo=
+- 6+SOXKdGQUPESw2254UCviCTlvgzKdhsoplrVmTuzfw=
+- oK6PCd/Pe7N0ikA5K9DG4VU6XrcWNWsQKFqW6hJp68g=
+- A8eA12/gqJdCoL0m8oruMrvCbfp1li/DRw7U6jqLefs=
+- iL/8XcNXTMTFd+XvPL33r+ZIT6EPAXtSxyNqgYdvN4g=
+- sXdeIpt/VQEHE2fJur6GdHXwnoo8vl24vNGUnSiVizo=
+- 5dqWe2guRmP9s9/LB82U8mKzqf4XWK1b1MXVmHX76jQ=
+- RqW2xPfBwLUvpmHrYl2fzMCj7F2qSW1nchCMSQwjsKY=
+- JdyeRqG4Xj1doG88Xgz/5TPfdQxsNyXtx/EeYSezbz8=
+- v9IF+DAX0EUwPUbbfbPvANQVd7GIUDLdUBtfmzoP2J8=
+- "/gAwDQxGeBLWjPLI8hkszVGN9ApxycWP8TZo9yXg4GA="
+- 4nFwdRdkOgU4yQlKu/8xSAao6E0WYv5yCJoFJK/E+q8=
+- oDZs/20iBoYWTZ0ThLUSaH+A127L1zNWBRtEBPr1NjU=
+- tO0QC4PRSAcLTvqS+yKCSSlQIpUOicSX3osZMc+7NfM=
+- Bw/9VsuZXHsD/+wepo/nVjL/D00HM44mglx+l0S10qc=
+- 49q0eGqgKWHNThA4oZ1JBgxcYpybvESRwLN6qkqcMZU=
+- V/UewvrM+Z3YxSXgHCgYDxKn2v1lSZP53+iDQG9BpKY=
+- rocewR9ZAACMTR9tqOPO1ruQH3saOmfwet6lc7rnVBI=
+- uI2h4TnNPNOk6eEXDJOqV2lQ13Qh37da+2Zo2AihQEc=
+- R7OJlCG2px5sDr1bDuUlA+qXsYL+x13t7GwaTt3s460=
+- 5OgTrpelQzJ9YYKcSYiR31HVN6X+wqXEZtFya74yXwc=
+- 2ljlsWeAhVeWxvuslJke6jlMtgToSjkaDlP1VshtZjk=
+- dHQrsa1Bc+xYlYJ0fVfISTk13jEbVNUJpIRUj4vIhkY=
+- Na7WO5xwLO4rdvUIMKuGxUlvOBr7oOjqMyP2/0I5dA0=
+- l8WSMUojyFxLyhIqZAta+sctb2WKM+sM9997yqKHYE4=
+- fMq+b8Xpvs3RYNmjigioHBiQwqQ9Jh/mzSGQd5B+ta0=
+- kHGesevcKE/D28JXohISPAoSiQG+Cu6ajHrrqhQf2o0=
+- oi/PbvD22Tirc5EHsl05qOVLVu7TC0fw2LfHGGh9RU8=
+- Qq6Q8aCvmJEo2ik1gQF64c4DYnfZE2FlbMbWTRA9vEc=
+- RaCEnT+Gv/B6229Aw8ZMgQSmSW/aCu/imlqcBGNHm9w=
+- fKnIYHFFbdBRDtyPl0/xIJKAz2UCp69P0VofYnQ+YHQ=
+- aJmoFpj1yZ91bNssMd9sSAHeqm+YsWiY2NV+Q8NiiBg=
+- jhS0V4VM2MKMQyDKwd/gsI+qY8qoi8bZM8hGbf98SHE=
+- 3EnHs5a+QZD0hVlKfpvCdaDoZVkphieFHeNXrb7o858=
+- D9H/Ovw+wZb2e0wx+OOeUp5sU7KRPIvgnzEex6C+GAs=
+- 5/1XaTw6qivDW6AH2rsJ5yCJJ1fO/mmIF7pCcjfp4lc=
+- dH1+njb/gJKwoJvAdAL+IrOcyxLjBykyK2y0RF7tO08=
+- gCVwGGxvoj7el7Og4ZIW2QFikbx0u8xfzfEDbjfyH2M=
+- OL5V1erC/CAVMRd/XVYcTE0BdSVDUwQMWsQ8ij8S4zU=
+- 0x3R1btlF+cr1Z5whDqL9GBNICEFPP6SOT7FtuWnTT4=
+- U6XcijXrmXOrNSWD/AtmGHGSGIye3TJbG0lZTTsPax0=
+- Zm3rbXbRnWAx8y/btwSSM/sPWIHlL8DZJIDyivOL+GY=
+- 1Humh96D9is2EFvy5y9FYHCDsrGABSq5shRPihUL6Ug=
+- 5e1oRBp0XISL0sCG894m78kMe0kZpDnNCzVkAmZvhJc=
+- nNiwznJYXo3wwDKfq5pWInpp8UYbhaflWi2kpmK0j2k=
+- RMdqCahLDS3lYQLHOAmoz1gW+1+as9M9NITUzGwH+TQ=
+- rZ/k2XPg7q5qDH/fftGaKfhRgu/g6rxvYYB9EN4P8Cs=
+- iY0RNfSsp2Phr6X8O7sJiyK6BcIBynfff485mQPltiI=
+- fGQNc/jENKrDSPb/85zFvtVqDS5Y3ca0dpdZJf9WaIw=
+- "/PaXeX3oxH/bNI6OLkEG+6Etgggh73OkFSJTBgAGBUs="
+- oEY0fcLMMyAmYQBlcJXVolYOeYq0kvteGE2gsrWY3oY=
+- Prnx+IobO14mPwgoawTxDi1vDaiAsAZtr2txWG5dHbo=
+- Szavhm5PsSs1mnjzyxwoUiaq9JxOqsr0LB4dS2eX3SY=
+- MtCg8jznVSLiIZETfx7yXkiUXiBEaBcWse3av105mPc=
+- wkyxTSjuXrMLRU5mIWhqjA9sBim7sY1esWSfQIj39uk=
+- Df2qZL1vC9M0J3Vc9nLZdaWO3T+CO3pUvjb41+ahr+g=
+- GdnxFudeDcfS2VbhormzS9PAcASZcZvEENBqHZtUDFg=
+- V3wIe+id3cLkmqeK9SEn2ELul3tQoI39xdH6JDJ4WW4=
+- U+HO03m1vHERXrmZSpid5/d7G/dsdNHvmQCP/2E84EU=
+- WfdJIuwH1/EK92LpYM7aZLhN78hdaBb10z5danQ7gCc=
+- 48Dty+hLH6qIJ5OBmvr6qZgFv1JcHj3XuGgz/vlJ2T4=
+- jEWBEYjSmDnH6pYpAr/C+rv1RG+vRA06XVLL310CAcY=
+- jVhe2LQlRfldlIQps4XmCmBt3czt+eZS+aG1h+64c38=
+- ZeGUK9IglIvzpo4BXDDU3FVAdG/X0bCWc9APEQKSzJM=
+- 3Z83HJhJmsNqosCx4UvZpnAgwRrbFzPyBghPY/CcDD0=
+- IEkHsTPyRGNUNovPCz0IEHJKvX715Iwa9HHqbrwW71o=
+- kQBaoQ2HI9g1VOdncVRSM9UdWdIt7FiXltck4j5lyXo=
+- U0YJIbui5f97kkHr7Sn2/Z6kYE/9cZnJ9h0HoUBgTt0=
+- ZuGlEqMO5bcua2UnEcNvkAJx4DDxAno9IFVZ32GWNmk=
+- nbE6MLaGa93wIFcoBgIfzfIPIbO6CQEOkmu+P8TdIU8=
+- ZqPizxoxeG9LUgDocnkBa4Y1roKQZANawZaon9k0Ms0=
+- eT1P3Anntq3YxDLYbP1gP+27I+6Dg7XyTecT5hvcyAA=
+- WzSkEgbsUbo343Qkx41kXIoaTOwQD0Afj/oSSYLPXBM=
+- Q1OOIdw5ip8nCvIXBZpY0rwievxTzHrCmxmfZfgdVu0=
+- xy+LOySvv+KaliJDw3FseW6wqsf8WH6+M7/I97+Z2Cs=
+- DDOYeZUMD+m5+diti/JNucC9t/wganfPcDucJMltCto=
+- bCSqdG3Z8NwgY63TsuxRfupQ6U4klymqf5pNksDWTzM=
+- 5ABeLBV4oegmxupj8LMfUbKTDQXcnrlhaHniK4HtLOg=
+- X/7S4b1Ws346l0ommhwmqa+H9oVMpgRIRzpyklbuBA8=
+- iQekmbVK4MU28gXvlx0Qba2SsX+6utsAnEHixb0QJc4=
+- tYZ6KnY2azBPgzTTjpSnfd4ptKk1CY160kSKT+/IQXQ=
+- HGmdBtlmOgO/zeMqufe69XbzR4BzGOVZQT6eNa1TBO8=
+- "+UH1V+6JaDAPZUyHxVZk/JjPQ5aGeN6lW1kq2lRSGfQ="
+- btZF7w4avqG/Hk6TX/BPnhjTmBI4f2PNo0FbRiQPBAU=
+- 84TA5PiNyepVySugFstDsNgh52xmdq2i0N5eOSl2sw0=
+- uDa+NY/KaIyMbdyBMKy4ZUzJ0f/5NB72geumPHQdX2M=
+- grpwG8gf+ciwYRni+ZkJIpcx/KXKSE/xbJGCa+TsU9E=
+- 0XRuHOnRae2r0MfFFOvLsWUIqAn82mQeTJF94rFDlT8=
+- 0bNwf73Goi0W6Vv2uRBkb12cKz7YG9Y31FT/ubsJSOQ=
+- kHfRr2nHvClJyRCzpHhnFwIavI7pgTFFjAZg3UtfzXg=
+- tR176m0CaapRutfDyOJ+GfKCHi2YM79+gGR82VIrGRI=
+- h9l9FebDutdk6TQIBOeWjVHkCC/VHju6akGORg+h1ME=
+- yQNOSa8sMoiLmrMrGNqxb/qoXqDLfZu0+JzOVDHnZq4=
+- Lisk+O5Au4R/6FuyMzajnvWUjmtJ2JdBnO1odmsWlno=
+- FmjBH7gdZPQfapRIh5gOWCqAoDu8ut+qOQU9MUzKBZo=
+- UL/uSccG12ZBF3eqwcnzVFbDPs6ir7TzyPEDOwKYvck=
+- BZYEkhFbuUlBYHiunak32/8bHZpHS9OATxceGZJXBG4=
+- YGvkvn2kXsquAgaFebdXn0ySGMh/p+NnizsVHHhFeQg=
+- atZloD3F7pVpoG4NhHVmFYnb8UVgnAq9/iQ6xCh7zLE=
+- O2YE0xAJxLNJCM9bGEsRDc/6MVNHS6P9+KqLvxAr0Dk=
+- JiiNmQRcjM3vw0EpU8QegLcQ8XCg2FCR7SrqKOUH0Os=
+- 3KIbqVH56AWX0dR6DivxseTjPQ7LhnQRULBPXRtzzZI=
+- 1MQqFBTWOIaskoYnor5iYrAIAPhHGbxd0adTqx6G3vE=
+- LEAgk/VmoOO1NbkA7M6PKzBLtsq63W11s60SPfgGq5A=
+- 2ywn834eP+uE2VVBepMKaEF9vu6n2mxpuM5y+Dnumck=
+- gBSQMHFdXAVAg8yFxzAGOD9g43SaaTRVm1z810G25MQ=
+- A6n94KyTRmBePjtnHpPkjuou4xYXXGJXLYypeg3AWXM=
+- ozpzfTg+PI298e+4mhwPyKkiq+avQY/dxAItbobVNpM=
+- NoTPfUyrTbXIevnKR8P78nf9KwEv6FDjqxCf5/oHZWI=
+- 7Q0VMyNgk1DZd3e+q1V//oNNk/YVwKn32MAXZ9f8FY0=
+- yRKG4aqSzLt1ThawFn26m/qDEaX6PVLUPlYpOAlmgNQ=
+- bVCO0gI1DPLwiUL4XIWaPh+p1bXt9FA7ldF1LfsFSTc=
+- A5rRC6xbvTNDQWHtKV1CbYhr78pjihefrWoKU0QqUps=
+- dn/GTOMdmHHpddI5p5LtCtHWcDIpkrCjuIcgfxXVsLw=
+- Sw52rOLxjRvNJ5eEHVy0gYMhw2rnaQBf5yAmdm58f2A=
+- 4SHZrJJU1FUhsbBOl+ENWtQ3UZvknBP5/4fmnAMfebk=
+- M828OHKzeJd27/YXjNdYXZybCAx1KqTpLCdNdo4qfqI=
+- NLjgkkWabDXsGdayFIrghqtwpRQWEzg/4eWK0n7a9oE=
+- j00J+aDsUlwyk4VgVFD82bPC/lPPNK4NG21+UPcmyKI=
+- qs+24d1XoUSKAXjaqG1F1TdGcPuHv1slxGVN9ob5U70=
+- Y2jg61xlhBTS7r4khn5BHoKty49oCEA/Q3QRQRptdS8=
+- htcV7nK3sxkyneBZLy6wOPPjscIcxyb7fZ4gXENz8pI=
+- P7xp5Fxdc+fPW/tRgxmTrU0S96a3hfxtCXZE9AzsSUE=
+- iMZ+WDc0LaSA16dNyR2VOHIw8BhZOTM6WYKA5lSCkiM=
+- 9TZAjqCQsJXMK9Lx2YJNyBtqhG5VLCU06v0nmK+z/Rw=
+- nwWzbjW18tz6WlF+GJWsluZJgWbUj5316UzZ8/eQYW8=
+- v8CJQCPmhrwog2q0ph2AQfrdheuqGASxLogg9FFUUU0=
+- ZLzglePah/etlfSxlJLU1THUVdMOaPM46DjZfxEpSFI=
+- ADwT4DeS5O9jCJTBjsywFe9U75bH40R1Tq09fcJh3W0=
+- N9b7lC0xFZAFMjB5/fmgewypk72nbcC8grfme83UN+c=
+- cJDBSbtIo/fl/bV7NbcE4VRdOkzYiqj6RopFPfmYarc=
+- e/ICeu951R+2yr0o4BLa8v5AjYQVa0qkqbHaSlsM6yQ=
+- 034l7Hb6JxgOna810QdqdPXze2ZUFAPqLr8pAv5F68s=
+- 3mFlC86DB3OOWM1V6NM1Ws0fCkjPiSzPiPwQ21dTEZQ=
+- JTZ84JGke3QVsmzxwNx3xAgE18HKfD+gfHufTJ6yt/Y=
+- g5z00ccpZPHyBcJIdyzZs7d99hEkXWh2DUFpw8bbdEI=
+- 14sccjog3zQRYrsxOGwGNoryVc39qWom4T8A8YXzwOQ=
+- 6hgltHTcXbipTyRoZG27sGGnm4tdi9SvYrZKKg2sNAI=
+- iY+efm/nLdpl79/2e1JCbuo46gEJuntKAlrzSLlD33Q=
+- jl62A0gvAHaLYMsX+Ufic9aqfIL/r45Ymgb26EHDzvg=
+- VXqul46cUwCNF2OJkY2671FxFy8Pfy8dunLOTSrxK/g=
+- 9Myswg7XyCx7/pAS84zBJZhW5TLL5dxrxD0+N63rjj0=
+- sa12CQb2ddFzwH2e/jIQDPEiQjBn2nLIRuu0kNtrahM=
+- n0wnkH0CURxI4y4UZg/e5Wegap7oENmvoZJJCcILa30=
+- xhneWTi6PYqFptFj9iF9Lck1lji94eIUZOvMGQB8L+Q=
+- z8LDVuDe/f+g9mkWgqY/4/rhh2xtSV5r1DMF6bnES/o=
+- OgD0qXB7BJ/1DJprOIv791x6SccbScATswm/lMxh4dU=
+- RfwSiNSvm6wMkeXwesO5cmgiSQERjdZt/+VPde+Qr08=
+- ajTWVL3Yt13YERVyQHnsuVm34Gs7J/M5GbQALNB6Ba4=
+- LBqC6yYAQ774bFbUJcs+LvkeJBRGes3fGFURGbZl/Ts=
+- AIVQjG3JnHnRGcjy+MyQrAnw7kis5kqumGxGyc0oGc8=
+- ecLCKAr02SUY6MHGunjHSDW4dk5oM03gp3tk4abQxmA=
+- XN0vZQqmF0mgwNEBvIDEVId+rStmcWVMybyZYzEgcE4=
+- POsjLLq/FFJfIC7B22nd2m9/Ard74abzdGG8V2lb2jE=
+- sNvoEe/W1ADDkFHLrSSbtDtEfAmSLXBiKhSBbGSczDI=
+- 2lLjMymhPrvyjKK8C4b3F02xa32CpbdmLOyufZ6IgKs=
+- cT/ftj/XHXXN1nK+PZe46EsxqTevYxslydP2ji+vt00=
+- wte184/zVKrfAujy76CD6PtUelstd/Xt1kXp3xDxRP0=
+- Uk7reTMgJIPTFohohFnrmCcumzPu5tdb6UXicUvGqPU=
+- UeOZnVBHvjOndgRGA7KfsF4+eg18sIMQrhQnkN960yI=
+- URj2MLwydG+wuV20DNm3LIqiOVoOGqeUxzE3AhSu6M8=
+- 1JZ3sF5MLO5+31bnSVL0cf/1R5jvJEuG4WmxFDi1gkk=
+- d6rrMsfo3M0niKYXU8Mh0JVNTcyHIzTg2KDDTnIE2AE=
+- UqDHYhxl0cqfHXVBz67iGugkrO77st66OFD/RJ++m2I=
+- qvf8yltLJEpm+PdUwDbANOf5S6dIUphsHxlgORwUuTI=
+- K11/TMP6l8cebCr44m7FFp0hz8uN7/k5bdFfp7rCAGE=
+- yL6UsTkwzSiCzDMlqzOVk/iK9snQ69ePxj5Sh/XAAZs=
+- w1weVVxu+E4m7lz2U8LTJOQtZWjvtnoMzqbzqm5q0Tg=
+- DSEcg3PXyWToI2sUSKb/mtPxajaeNC3Lf2+Hc6fGDMU=
+- KVjbilfHhuM7MVfxjgDQViABjjaRaIrgEyvmUfOmSSc=
+- 325wcE7mdzxl8VJddP7fo2kZHCSln3ZoOZJVrBXmw74=
+- LLuwq0jLHPrWk4BCiT+cemdt9Bng1CCq8XwgceRws1w=
+- uwMLGqoe9GaQEhyCR43T6jteVwYOdMzA+gEqXs+9Lm4=
+- hMOQ2tXt0Ls2H2h0fzoeXSzd643eIJFHsA2SnBqAt8g=
+- kxkWuqYnrtbteS/P6/2olrSs9YqXGZctoZd3xemC4ik=
+- m7UI+vAM4DYaeOt3a5AEvOicAR06x8uSbQCXMMTKf50=
+- IF4L3s+H1y6djzFs0OgRXWj3tCOW+Ai9P1BwpLfkVXM=
+- paCqVZnekpeJFiCe3AkB4t+ZnG5iOLOiNFiNffl7qpU=
+- yRrDRb0bqdRlEH4ty2kvVcI+x7pKUVaMp5JMmAsXby8=
+- 1pEofBsj/28pipoaHV8x88e9x2qJ4DF+78sbALlVOBI=
+- LRoiZii45wCQkoq39WvSgSlHBqLtZORIpOVeeH5ZGT0=
+- 2pw+xYBx1W7Kvwt3qJqRJlQIpGuqpOfU9qR1pAC/4CY=
+- BmrWkKaVoyb50vh7qgvSK4FgmuKr8QGdchjrae+fn8U=
+- IghaqSm8169LI9nZwEah1P3ovlH3nZE5Lvr++WV0qwE=
+- gHrl8420e/+LCbN62APLEO9RR1Z6iaM6Zrsygt9K2WY=
+- dYVlpgqFiiAAWvjbrvPFeNewEcn6btc7JS3JqUXarQI=
+- auwbpySjD6dsQCau5T29nRkXdTEkvSiqMQ2w248x2V0=
+- V99yAiT9h8W/0OZgV8fqxweWXxGmUC2XEEd+TbNG8j0=
+- MAEGp1frYD9Zz3Ed9JG+NfRYGHBlqIz9HIQ4o7NFCqQ=
+- w6G48bH3RHpe1T5x1uh5OVmY4+cCHgx0jVs40aFDAaU=
+- X+icwxVoz/ALl4JlKd0OM6aI1/otZs/mGodQBNQzw/4=
+- 36tY44+RWkR5LeJzVQVJ2J0IkhatBHYc2cJ83BFJslg=
+- igsx+gbz2kc5tH+YZ15LLPQGC85m7S3W6ipJorqEkUA=
+- uU8yuInt/aH9rOMBpGN53juVCShnRAAWi20KyRb91uI=
+- 31qC85QZqHs0HuZXZtDNLUwlJyHTPv7dVH9qmQJsow0=
+- IE8upgUcM/wrU2dLBwhvg/WBTAhPPl7Af8SOiNMzE6M=
+- Q/u8zyNY0fqCyy5R2n0NS+4dlOyg0PaiY+BFug5oIVk=
+- eJYcT0dmVuPq8OWrkZKkzL6Bl7V2ove5o2tKPEWSCWY=
+- JcvCKwCbPyhbCztNBbAloRYZyl067Foruwr2kAhOwCc=
+- 5jR3zihGCf4IFgKKslhSUGCmPi7zM05pSnropEA3VPY=
+- lhx2P3K5qfduHpjvdeO4rGl5u4cifsnnAdvxPwBDSD4=
+- RErjHBJLSHS3OTgwpbYfUTUcqYrCs4veWVMS5zULANk=
+- 4I0mOE8glIQg/U/yYz8QK958rLo6978UgrMj5HxJp3U=
+- XpOoa3vle8ITfFaUM7+j5k71URoaMyyEM2mx5nsg9fE=
+- 2bo71RpEMZaHBdXiB0LLu7JjP0bJEOA2PiHRTA+C+C0=
+- NW43di2EiJL5Xcy/4AUqnF5yyQlN65CUNGhVgPwh5Fg=
+- c/0cgqUskM+JwQfB7CTqNyNgKP3mJB8icGakwUMcD78=
+- I+xZ5ds3KnfhPymTYvtMBrOjCJ011oJ7P/e+lFnH7L8=
+- vuuieWlSo78DBAN3vTMBHP7ZMbWlrtGwonMgB5Kanb8=
+- Mlclk6oJGXudVsq6kMVdm4PUQUpTKvBsoHFzDPcYTy8=
+- tVSIx4IVRIOnfmox7DFg5bS+5DpP+cdbkNjZzmPWBZ8=
+- iH6WnG+gbgpxbDZjVxWL4HoU7paVM9dPAo/TqnKzRJg=
+- WAtpByQ/Eb4KoIh8ZLwTpC0SYGsishbP7rLF7wBtp9U=
+- 0jdJ3Ii1/+axT6daFEjl8yQV2x1R6UGGhL1fRzcvyxU=
+- YW5/42xP1HqvE2RkaQtxoS8HMpOvfJp/X/FcuRkjJOA=
+- 4Ll3tueJHUjOcVjLHbhZIWjWvk+pj0KKd7LPbLPcCeA=
+- tGHqzo+OUqjwzz+YiswYmmy0UYdcDd3eoplmMzvO4PA=
+- QkktoGI0rQrHb11d69ttGuAnz/vnRqHBO4m7i8ATkTc=
+- DAvqzviHe78kFusA8rXcljVOJt0d9VFzIEWbEjaGD4w=
+- rLnlOEZOtFT5IZS/uFXF+H8GdOT1FiB/jCjXugz58GQ=
+- UT0P2PsLGOq5clRW9Od6+M4N0hw6imIxlWB2IFeoOkQ=
+- h5bCK3qQ2K5mSsu0yc/0r1NXofcVTGUB9uXJnPK846I=
+- RRuPRAEcc0orB/OhoPLajAQEbiu4a+aXD+SvsRoldiA=
+- ViEngoRoJXeBYU3yA4J67MwBT3atRxDHJyZnoitMKdQ=
+- xheeNOIEyCIuYNm3GHTXHLryN31912X9e6sBlop4mCs=
+- b6wTNhbpkQU/c6x6BfjZHRHn09V6GTSHKv+DiqyiDvg=
+- RbR0c+m4bHDzJya+lvx4Ao5Q0fhC5SDFbVy2mOzuIFw=
+- p9NR8945nHRgZKU8aZWasQMSoAPsY5hbzYb7bcHEAtU=
+- IfI8J7KIi0TYYfpd2GvTNArwI7s1Y6IlXdJnXOBwaqE=
+- fWVz+WIiaOsGMx549sovvyZVs4KZInBavdGVw22h808=
+- lDXz5ac2qAvDMBDkipbA1U3neMJq3kVaaOe2Oc5jDGM=
+- viqtfPr4vDRvAcq+84S204C7b+p/d8e7v8+a0h81ORw=
+- DFBf/D1aCIKhOmwIK07+fZMT4D5IdSHc+L9maBLW2yQ=
+- mtoF/SumizVj82QSrZ921O2tyyzlnS6aaQ1xWRFI+6c=
+- PCMTUuYTiT3shHZgZ6yxQrr2WBNhKYBu3K3NCOpsu+U=
+- 1BKa5U+OATJeI251c6CfHtnLORLFpXIdUJKq/y6lzp8=
+- HakTOrnb0R0pN+yNMS4eJWmFcFnnPMct+S5nCSiYOrU=
+- fvCvp2xY5sV2Af1jL3qqKeD5fsWwZA4NTdEzXjspxcE=
+- bEsuo9JySNSGZt0qAqM8V6jNz3o1GjUZYzu9jlI2dKA=
+- ZmPRcPqMuZ/ZoU0qMuOcpc9Q8NetbReFJKMPiCBOwQo=
+- Da4+taW0lwg2oh/4RGU+nuzfHG+rk6iMoPwkfulXlpI=
+- 23/4jmI5mPWqPGOHQaNOIqux5JuWUO2MOROVpsvvW0c=
+- w6qkedtx2QnS5vYspanSF6Zn+g8xmt5Ic1z4xh/5S98=
+- lOsx9jIxwgycFS+1o3t1AJEWWz6IXjz6v6tcgNqIbyY=
+- qCDFGg2TazJiOvwT5bQ1XWDuo0+eO9EFtAIK6dGOZhI=
+- DAWF4gLfScyqzmjN72YiAMgVmHUB6+TaNFWcx4hxSTQ=
+- j7iyPRBKkUlfDBTRMzvgTz2ySHpsxyYTl4e7dSZ/Zuw=
+- 19KeRTYikTtqCMP2fzgzfNomzIvjQFbv/1HspglmFM0=
+- ywsqVug0OFnviw36T4X+/d9nTSZpebRZa56R4sfzlzU=
+- O30ZQ/A+is/terdOiVtZkg11y3i6aCJHpIdJjibbwKs=
+- 5aEZPpw8FDe6hHpnH69YJwUKF8YJw3hSgp+bA6xWk5I=
+- eEvjnojoKjHthyluAb7Bi8E9Ax5HIbZ168llJ+9GC9w=
+- Lmuc8187OcUcUUGbcP7VQvjK8fAV7oxk2yG7WhC9Dfc=
+- iFlgxqr84jkMfgHLVZKB7kb48DXeaLRvjG82UhrYASo=
+- uCEiqJ8acgct9TKrzw3hjSWKhHtsmYx6qSzS1GV2ReE=
+- bor2pmq7H0OBWy6t4wMgLygWd/0X47ZAWGGjTaHuSy0=
+- h4mBdk1S6qY8BzC/P0AO0CMQfc6Tidb2C0A5decH4QE=
+- VNzMfMSDD7w5MdXmzBcvFZ7HOFIsTg37ZXMSPsAv2+0=
+- 4V2bAFGYgx2pOkonzxPO1uDEM+ytFFCBGv7Z8xpPafc=
+- ZmUvICMV70pxXcw7igeMY9m2Cdn8C/ctPGcBeLPO58g=
+- BOswP3SUIvbj2rZrQUHRQaPYw1S9h8YvI/Q7pvopM4A=
+- Gc2lMVmKNUTLJUyJC8pYL9LCykNtwGsSIFwYWemp44c=
+- D2xoc3Z7MR4eAIFP8KChswDsSuMVY9ZfxIaj44rxz6Y=
+- cEYL61Qqgv7oC2iMbcoedClumbguZfUjsqsWHkhjPzo=
+- hHj2vMtliSP2lBbUSJ9CWaBK/593FfblX4ig5wZ+Njw=
+- GlTb9KCuh3cmMwbkot8FaxVHtN8CWDZRP0OXSwahxoo=
+- 9CugIUtAQazxh0Hvy5fXDIG/S+VU0AphpNikm0hv7lE=
+- "/1H2OjxIvJnC/GRV0BkcjrKA7XAeOmcAm8hKppUrXhs="
+- "/7k7qtE6NtJrcK5HDltnm7ozvwA6pDPmQ638SbtgfYs="
+- ENCLkOyUv4pILeQe8UgM+SGYa7n5rXp0D116MCE4J64=
+- 1BtWP+DiaIpXncDq2FkZjemJEAJ2U9Kur//FK4XJ6zM=
+- d29BaoFmy4OAadlgOkUnhWrWrYvYaCbMrnHrT5QNaFI=
+- qpXob/BR5Fg1D9cgh7QvMvBRf28xmK5aQder8i8JeqA=
+- FLUcg761/LSCsgNPk1Gf4xl3pq0cCNenum0dDJGw35c=
+- UwllxBC0JzozVivmlpoiXhLTlEulOPZrkuJFmUVwnmw=
+- TjQcc+qliDmWBdLAe3ckMWPt5kl9+0saI7JUD9wQXYo=
+- 7XUn8ra3UhpJhjz5nAUbr+QnCHwwk+h/GP97EJhGnbE=
+- MO+WlJEDhwc+BVGFAOfN1eqi+DmRRfYWRhw8sREsbTE=
+- CQw3ijQWrdgkyyg0kcoPZIRerG2gJimC9n8cyzAiBSs=
+- VKdUOYPxaEbX4e19M1wyhUHd0/KbELZdsAY866fOd4E=
+- fULgS8i94eM//z3+RJXPE5fZo/PPYd9XBO+0GRMdwVE=
+- o8vGIUZgQ1ygQIsucNCrtgW1qSuAMfMnBzYEllgXu/g=
+- i+4b5V8GZgAdyy8guYnDpwzHiLL3+NJUw/OAc9CCX7Q=
+- 1zix+GAw/knZOxsSb6DQewcHp6r697KJHXcElx0OQwk=
+- "+ChWTp4YyhB6RNrD8MJNkCFr8GGYVLvyjyH/1FL9liQ="
+- soFE/N0PPSoPXkBMgN55HYVWk3glP7v6/Cawuh4rGLE=
+- r3IkCHNJ5AYm6Ca4lSk9U5ypu+mJYYZn0Hau279GDrs=
+- yx6bRvx9sSgugcYZPXV1/962zrNRXDDchlVOw6MDrTQ=
+- hfyQgjd5HDPKdwCpE6u/Bckg7i1fQLD1TDxHmRcU9ao=
+- Kd2IEYC7dc+TiDC6CgYsooZv99kRAHUWGid2+RSxsBY=
+- PzZUtJv/x06QeHe/niTI/J2qWtWKVt1aFSHEMQbn33I=
+- 0lctYlCov1FJ5hSXi2PShdnBUm9eXqhSyjmJdFwQ8gA=
+- WcMwAoO9U5OfjMd+H6yGHBf115WYpgQeqhSVn5YgCi0=
+- zZkMbJ0mOeIY8C95CkBGPI0/wzgKhRei8PRbRNVk0R8=
+- woDLdB1BMHK77NpcPCmnan8AIRAflO3TXoq0UVcynxc=
+- EXVt4vR9NMZltEkZ6rj+NI2SCEfdfJUqdUBOM9/lQzs=
+- hc325KSn/fptdf6EO7/nYzbG0WSr7oRTtAWiWCLnMM4=
+- XTHJmZQtdpjGF1m9bQyGlZFf2O/Rb43BARJvok4hrdI=
+- o7zytUV578NcgnRMZRvGqsvZU7BfditAlJcB1u8Mo2A=
+- X2WjqBcyLVwg25riK5vO+2G3ci4mu6DKGfvylOhHxHA=
+- 6kpsmvB+LfoVoaTevA9lXdmsf/RpabRzzSmxhDzS3s8=
+- B205F5lgt7XMozIpoz/nA0pLnpNxqGMkb7G4gbjpI3s=
+- Kqa6hLeV+t+cBTRsrX+Phc7XTu40v1zVGophYQFAn3M=
+- LmGv0l08p2oO4mXbwiul6dhE3JOsXy43GZo0/L3LWlI=
+- mQRoisLNO/4bd/2rcaYoafnyXTEAZ8cpsTJl3UWTZA4=
+- 37eptnxoeLadMbjx7UZ2iuggugdQv+PVNym+o05u2LI=
+- PTBNw9ZIaFwX+VwiSEf7xBEtRgVl11gF2b+RBy7aUs4=
+- F1+36BRdEesFGgqsysG/Oj7RbvZ9ID0Cwy3sNaOq3nc=
+- XbKDhF0wvOxAFmTX4ZigU7W12OhGERlACrtjtKM43UQ=
+- 852sbLq6U14sIHzQzY8VSXQiPISPcn+Ys1ZM6labQc8=
+- SUQRqPtiOc//4GIEbQCNdOcgDHE4XDvliSuLwVWnBkM=
+- EA3ryJ9GvdwQSm0uovuFPaK8/sOqWFFpXZmRxxFlY3c=
+- cIx17oV9KNOGDe2xuGVFTxio3HRglpVwtaRh50MY6/U=
+- 4Dri4ybPiijjnyaqHktH08kcDA1lRiIUY8qLs4ASi4k=
+- EP4e1e2ONckozB7l+GIpM9IhmqVmfL4+Ue8MgxdQo/g=
+- JUQqYdDwf6ZIwbzK8fkixQuE3UUJ+/BjBbIRNX/AOBA=
+- odDMR4bQz1B75/eY29uFQFv2UFwAP/F/iLqlHMmPKcQ=
+- Fc444KVpBZ3LimdHFyS4783SPZiKp79zfRjZLSIIaW4=
+- 3dtWwxn323ks1o+F0YkvKi71Cwp7bPYeaJXQkPjKDE4=
+- RbFk7cipBF8ZBeuGQrjKp3XfsE52DSHcLL5UXo/IHn4=
+- jXMpyDvGrLlTpYTQyBbDjfAXQ84fjOZC3MLscvQkfto=
+- uY0/df2sh36Zh4qbcJ8OE7YD2zJz6kYp+4BAdepANfU=
+- vmyovjdIGbBIgCd2Nc+l3Yo8e4iLTZbGs8KK0vxN/W0=
+- 6+HK5uoalLBej6pE3tigvDGmffj4NvvQOMbFiOSwt9I=
+- RVmrtb0r6zp5CCwJgHb0GHYtaQQRZWh/opbN5E5QxbA=
+- HW9VptEHKeJYl1DzjeKoFnBD9KmPByR9h6nXO0FipsU=
+- Ot8fxTm5Kb5cD5fSc5FUvFK6w6aRlB/lxzEz5qWf3ik=
+- C1EJBc3eF9xm8fstmrz/JKOJHNONqdB8YJREWavpD3A=
+- dQpoPtPpJMdb2e5wd02RdqkzVqeTk2UJI/4wjVBqc00=
+- Ppe3Z+clxthO1FQ3BSuDVTbZxV6TXv4/pQPdlKFuNoE=
+- WKI5KpfmK1Wbu0U5Dt7iJ8LfSN6AU7FK6CpsTqNtf6Y=
+- a5/9cWMd1k4SMe9B4TjNKbkzxocHPB9ypmVxmY2b0qM=
+- BRKW1yQQSOhl0XYwuv5WpIyu4YGpHBqhRwGjQQmNtJA=
+- WfJx9jCTVZYvD+xk3KNs/0R3bqThme0KNQFH4V4POmw=
+- I3Cd/K3iHDOslJkZuc4sgJjCRXSwZnxx2LFFuYOCa8w=
+- Qqlksz2yCDJxShRdBqg8rIyRQ1ld0Ge6lgPXy1YdTiA=
+- "/TkkC6qOBpXLB3CiSaRgsNTpnaU6AKcnskomnNFkVq4="
+- 1VMh5FTbWt4T2jaH9elC06WWTxFlLOUczvgd4qV9JoQ=
+- elHQZKGiFqaS91P82rJ25P8gGgHYtm9W1Q1NcZ/Q3Ic=
+- SEur0MCV/viMi2r4QACCbNv6i6Up4CxDUlSqgdpZghM=
+- F2MfsB3Furn4yhPjQlKVDZVM4NAMjp/X8cBcJf7eQzE=
+- eE3PQFZAlHX1Rto0YzkVukc55+AP7r9ZUZxNsR4qz0o=
+- xKm/18E0ofkiDIutsDYC7sXvOureesFAi7EBHbmdrWE=
+- TwCHF5bGGJZng3uFdP1MxqOZrLEyy1aDDTMPL+pbHA4=
+- csywOtZ321qS24QYDcPILyWRxTSt/s6jlfswp0ogwow=
+- pqPKDsfEFeL6CnJz+q/4fcKhNwbw+z/XalonPzP59+E=
+- Tnatg1RGFDfATvm5skJUC2QG14L/LD+yiv2rW0I/iP4=
+- TCNigHXWFbqQ4SZEubVjiDmshfjlYGFd3OE3Qm8fFb8=
+- 11PKfNrC/CZvwZajluQM0fOzkbtTvJx8VJKZ7jVbjkQ=
+- Oq2+gHIS93ZMUPN7KmCQI2LK3uRjB1N3kj7hRmo1vEA=
+- lTreDyjdoLB5R+qIXiXY+mUjf0fBS1Qx5jCeH+0kSWU=
+- 9aLZ4zsLJkk/Yt1HCEquYt8crTcTwfT3C38xsxAdztI=
+- hwfu1EnYAQ0ZHFNlC9iuYmTz/pj9h96D0kDp8CwFa/Q=
+- MNQwhf3HMJ/fTS3vV08RYxM1yn1Hishlr1tEvpo/TiU=
+- EGVoNEIBnnvGaZD/UCUYJKOPzqtcZugXmFgtEfJMg68=
+- f06VFHsrQ25jTnk+bnWcmATPW9FzN7AoJ3f/njVmk20=
+- Tj3P+xMe6AAoStkUw/XVZbGp3iNtd4uNzWhPKqC//Ak=
+- 4MJ2vLspvcNuG9ECMHgCy0vGh6NVSlPeCni9l6ILa2c=
+- xcYoMD1jVYeCKkkPbbUUWs+LDZG08wWA6VWx3Rsk1tQ=
+- kDSZiV4CkW/dHquL4aBA46Suk0dxDdW0s1aIWq0Yehc=
+- g6cm+1tYgcbMfOzxNS8oaXlE+PSWBe98Gx4Qj2TshyE=
+- xC/qanh/qoTMab30ngVA/TdN1EYaafn+MZzRdyPqNtA=
+- vJYn94j/bl+XuTIb74YR2o+ZPDuFoGWVkV0x7+GUpK4=
+- HGX/AW2x5Y5vc+C3Dce/tlRRhOdV072zxeJ1gLBfmbg=
+- uXR6XTzK38W0jmIu/jPr9zNEPIf+bLAe7apBEwbl9C8=
+- WPBz6x8m0+hiUgC/yqg2pnPHzMabHaemPKSMpUxA8Ug=
+- 6R6I7op07Wm5Vsl9CGcHJ68ZsCJFnJO5DHALdkMZoG8=
+- y+aIiRWKvwsCurIp5/2GUWCedQbRt7+ykqRLRDCagPo=
+- fEnP0CVqX8YMcTG8iS1eNCHcnP4f3MEfW9BwEuUOaaQ=
+- QuzDFiOufOAzMJwT/W7dz2mt32XDKNE2hL7mVUFdcwU=
+- gtqZZfIrM3KzYm1vLS8CJap1hwP/af9+8gM+jn0LfFo=
+- H94yH9s8wRaLH2ntB5kBgljt3Icjqno4EhVXFAV9XcY=
+- kQ7swLBhTCqeOCx2QJ9pStBaU4QlIA3b08UFlpDrIGk=
+- b6MhLWJVifuNadrFWnpgnQHgd0AAU/5h0PV3bLwJt+o=
+- nFcpWV+yh8wqIlYoBEi75wbAqGbwNyUVx0g94q9OZTI=
+- bkYcjn/5SlseEeuJ4gMD0woGDSsYrL6p+eHxAeoQgFs=
+- y+rOjVg5i/DLq9F8tsLBrM9NN145cr3sJmy5NUjBX6I=
+- iRGEumtgaVlOFW6DPZmp9vnmOFu/HRCToxRXn1rJJZU=
+- Q82ad6h8bbEwO+Rio+Fy5qeUif3G/uV/iGoXp8kZZgc=
+- 1j9HATZ7xnr10uJA0JQP8fJWc7/7hOS4CH2lVwgc3tE=
+- AWDJitbc1J24JP7JzJKpGtFeLKb0MVrbLyVJgMEpQVU=
+- iiAdq6ehFLO4CKr6iMb6ZOPGK/n/LhtIK9VF53Etmx0=
+- E1wVhG+rTsqL7t/s4Ol9Wo3rwxHieuz0YB3reZohkrs=
+- POyHu0RtYHJ/C3ujqeE3x6Ak26JpgFuj+LN1Sg2faIo=
+- lIWrhsZHmA/fAMIKcmBIT+2wqBEKAojWRBGA+4XeePc=
+- rp0fbwoXBcXjVqVptNK3W5SFCCxmNFdVJ59hDUQW5dg=
+- FbdnQ49UVyqKFge/v+Bjsisa4Ae2i5Yfirddz1YRUxU=
+- iLAQgYTw2mkS7D5Zd+v7qFPMDaKD8UZnLc9uvJduujc=
+- yO06N4SfTLQxrUlqiY1HiJnyO9SgDQtxCe3RpGM2Z30=
+- BjpZuKDJWFPu0PTmNM4+TwypaF3trD2TAkz9LA2fPqs=
+- WSTmqGC3EFhCFOizk803HyckX+yFpceSqJtt8vSNhvc=
+- ba0uA/ZXTInl5CHlireXXUYovIlrjOHQpG8F9XeLtoI=
+- "/nOCYK1YhvrQUOyrzcKEJrrdyEPXrijYP/ItyhZo7+s="
+- 0wFX0m2xdr+fe6IfHjS4TsgWT4b2jMTqy0OEq+xNvKM=
+- L+TT3aiEw+N967OXXYKhOXtJrxHk9AM5GJc1LVYJMvE=
+- "+/fdGZGRC8o50A6W8xttnaLg3oZ/aNjw5+gKkp+jvi8="
+- ZffEwA4iFxONF/H/pgim7mx68cWj4cCyM1+I42uZAxg=
+- JKAe55O7jyYlg55H/KIzTg53KZk62IlsBkg16XJ25Ig=
+- M+eiZtRbViM6saANDJz9sV3Ml1QTSIdB/7b+jJLg6Pw=
+- 0tkpQaKEcCzCM0wakBfsFviIqqjQGRX7YpleXgqE79M=
+- OmRzodiTteIVuZ83ciC3W2vmrDVv//gaXYnCrglVVLI=
+- XxhPa8qPgWaAKFWnjpfR9JfXbo+64HFqp2626/WT9j4=
+- VR8/moTqiCmJx2Kp5YHnvw6IDFJ8LzyQXx8jWWUDeyo=
+- SVX2JMnye+151iJn70j/sowkgxUdarjH56mi++DwuFY=
+- JUcFeYOjZefV3IOxm4DF2HUWc2h0xuOADfHpyIKRYso=
+- 7onZ0ueyf0b7i5ap0ZzA2ArA+XJMX3QTGP6Y+K1gCf8=
+- 0FJcGIUVCPjF/zvrwryZWxXC+mzgrOuyuWRiuRjrT8c=
+- 8Fio9qMxXras5DOmahrdNMQ/QFn9+bjNcZzsCid+5/w=
+- cbPDhiGS9ADLW46OTgxJnxddvJCzbSdvPu7F8GlGJVk=
+- MJk3Wz3YUjs1Dr+Vg9m73Nc/XTTAWsxPXZgGIZZW8aI=
+- eb3Q5iX77Am0nYY/QbNuj3nzPHAGHX/zTpdgSabUl5M=
+- PxldwaAWjc8ezQn3XPDnI+TE4JbqKJz39iZqBGstWN0=
+- Ily9VuiOqIMor0sG1psUywLGXLjPWNt453Keg/TXRAU=
+- GYeDduLICeFmPEEa2ZMEHtKs99pZS4sD6p5Ix8I9A5A=
+- LxWlBvFeV4Lfau2YkKMu6j+TBthuwLEYDFbYB482mU8=
+- Hs9KYKwLGP4AVtJmr4lTJMDyySssu3GPYBsID1SARd0=
+- KsmrJGlWeueRMl/lzJ7IZO5folUXq9/epFFGi63MCk0=
+- 0bfXivNvKYYXvv9PyDSBmBUaE9RNAa0+TDBeKBH6dAs=
+- e+gPrsvq7BLRmHCR+mf2Cwz0ChEJtP5l4XVj1+nq7gg=
+- fGNLv29o03bIeAk2j1QSQ0aaksAn7PhfttSIN06VOmY=
+- m2lfu4MVrKS76jkoJ+md7G2VbkDE7ECxZ8WMi21X2RU=
+- "+ynq5BP8yvCE05zvI46UHGYKlN0N00dptF5rI+VZUxU="
+- feA92yvqFmr3kUR0dkmjdfTHphkBDBTkefVNVPriyDk=
+- "+qvHJ9EmEfBdUkOFnVmrbBhajXo3pePkW4ubvSAgtyA="
+- 2eVAbpMtyCETcnwrwikWuKDGu3hbP8qSDTbyP7CtZ9k=
+- 3sHxA0Qlx0UZpKpZthfKyHZXZF5NptVAPQsQaEqilB4=
+- nMy9dbHhHpqVevpNxzV202JvuOlyNqvAVT7SG+rm11E=
+- kcLStcbClgJE5/gcmpUjNsZOeZ+tXmPRcXzLAk/jC4o=
+- K4kVJVu0VZ1PKFBr65Gs3kVE560HsnoJg8Q7bmzpaf0=
+- bjyscssXHOW/vYat4Aw9FA6mgRzpje7A8Q6IMl19b5I=
+- R1cOJf4JLdQSYnkqbjdYpcPfEq2e7cYWqgiIanJw8mo=
+- 63xok7tXuojDV8l5vLAbMN5cu0Ev7smAzLxP2jHwr8M=
+- 4ezjH/aTneRdaa5gcCLcxPuAvoaygv+uL+zN1h9uyNA=
+- X0dmTSc03BHj3PuHfR15Fth7uXQHlnrZicv4Yv6DRWU=
+- GV210ZBNejTwsqKKIr76Rf3LJrz+y3uIT58Py3QjLrQ=
+- b4yYekofqDfKuLAaF8Vmk0t4d9utOe31ZZqViBZzzYY=
+- vsMRbzYmDoaOv5fOpCV5mWXUZDHYC9KP1uuWOR+oCJA=
+- hyaYfFERgDSYKyCLupmfYmNRod3f45NmV0lMkdP5now=
+- yH0zTQbjnJzZi4lGExu1NrdsDF1Ze7I/ijxR8pW869g=
+- wc4OtLAGTD3LDCjhNQKco2Pz8pbeupjpGFTeG1noi8E=
+- z+TchL+T5xQ7khsC3nee9K7k8UEhxNsoIBC3V5vy8Z4=
+- oSqCboogJ1nsM3HhGBoL7ok2JTMgywsOKam+rPakpnY=
+- T7DVhk6/J1k80jfDcqJ5nXBxjLR2xfJ4mQBTV4ze7aA=
+- tnbI1+XQjwJoa/wZVJnRRtUWOKy10eSQqlOnWuBaV9o=
+- zlqE69seee4ZzrmI7kzzkbw1DIsB9yxy3n/zbAGOXdA=
+- "+tesQoSA37FVIi/QaXCmfcC2FHbsENXpIHFTr2Dja8s="
+- 8i0xR1E7uyGPKGSvvJhMI4vNf/myaJ6LtMKGApNmUzI=
+- CkZm6Ab0Lye6RBjZEQvrWTIzch9YzU7FbWvRYNlsFjc=
+- J+UIGFyrH8KaGhRMSYIQgA/+qcNYG071jYeZ5plq1ak=
+- fNz4mA3p7nHuxbl4SECw3rik9WOCT1/EQkAERh2xZeY=
+- 0hPfqdQykdhGwt7GFcixuQyJ/zM+ht6PuPhhR7c4MOU=
+- joRAvQdrhc2NAevtMPRKdBpt6PNQIOedBjrvKuthe5c=
+- zr/lZhv8rCkqFmWnVowQwBPzudG+iaVKvNCngocV1u8=
+- BlheydhJ7GLCtgs0hLWnpswuDfz9n980G9gOysjR3N4=
+- 8izQ2PmnwzB1Dv5orN0OO4ztwBnna6Z1dz6X29j4Xz8=
+- "/8K+aoa69tGGWU1t7YIcC360JJuBGsMspd0bI+1mnYY="
+- QjKbFOsky6OOGRCn04Y4E9SHL0QDhM7ft3uraRlTRwU=
+- PNntBBLE0HQf3MueQjkoHQzsuBSkOiny01yCardjgYc=
+- XxDov2GHVPyO+70H1R0pDp2pWv9WaNCmElNKkoeuGaU=
+- dTOclmi2P067WYGUNDOZug1x515Deekz/5T2uCTbd3A=
+- DkKcK9XZ0q/fD5FwIO5CFTXUNobdir0T6NzhhGtjQuY=
+- mkvXTMPw0Fv6MjZDeRMIkSI9CPge2NNvMmyT0XhEKOw=
+- Wain/qiGpPPEHIeBaKsrH+4nWlwaNcR7Oi7MY14LsR8=
+- bY4u87LJ8EgtuqvW8/26pryiJoCk0JIl/Q/UIf8RDI0=
+- LRIS3G+Eczil/SvIozebihpR6uX6YYivZllsq1pod4c=
+- lsz5THCfyE+OERRNG15fDGVMhFksEAcorWRNQOhoXI8=
+- 3jDP3Hk1Bd0WkrLAMbLP/TxUbr9rjHPZmAe3qAKRlyI=
+- aoB3iCWRDFoFc6lnioo7ywey9OkG3c1MM7bsNrpg4Ho=
+- MCIwhDWtRswkrS7eVvaOIbUkIcD4Gfk4S7XB0dQd1v8=
+- hEfV6F248TUGH9366NNaVMfiqDZY1aKGDWdhsWK3tMM=
+- VhMUAa1mtyo0dQdG9Pjku3hLKO0vA1MQ0H6aN/6yPxM=
+- 3hx1yCfMVduoyyH7VkFkToiZxW4r2Qp32ATnlMjSQ/E=
+- 2CS8IoVDKgQ8sVXXqTkcEMSnPFVVccLH8wKvej4G/44=
+- R4XjhrvMoUWpXMrD8mmpvrno3bac15GYufA9+aMylH4=
+- JAcNivYLWWJxyA+H1NutMz8vJR4YAyNJd7BNdIS2ynM=
+- md7VU8m2r3AW4cFXcNNXqiPND6w2oMpXjfjKtUFu8rw=
+- Z1LKOM4qp39shYLZ0B8s2P/n5bTqPKcdQkFR194mkKc=
+- XY12U1VlR16CrZo+wwOjNB0B7sMMQ1uoZQp6d5joVP4=
+- 8tM/7vl6Htr74fdDHDFl6xo+rbces5BS0AGOUKKB9Lk=
+- kJu2gKrpMNoT8WSYrEpnGvJADBCLmXIGb7+8HP6xbZc=
+- DvpFjlItzbndgvMrBWYSSamU+/NDWrjIER9+0GKO6CQ=
+- "+fALAG2oSUqjsjwrEIUdBzXv7jtuxLjlmSW/t0WaHJY="
+- bDejj0QHnHQlSdSe0QcAdX5ECCkKgiX1RB0deRAohzc=
+- 6QNEAanh8ZKxOeag0CLx8t/DVzlksTpNmkgoPvy/n8k=
+- IheBmTgPf2eSqXjsNQNoln4p0GDnNstPO0xDSx3Fe6A=
+- F63SSo0v7RZ7HLzQ0IeGFlm4M5Lj0BWmh93yssSRWLA=
+- mi32kI+igdAMen2jT999MfCYtog4nAy6w6Eaa1YKG9A=
+- YQfbST2Edz67itfMsZtwR1tbd2Jo/8pIf0n+zY8pEfk=
+- S86b/K2Jitji0SXijrAGHzcCNIJFLJ2NDHEcFR4heks=
+- 5Zt5t+ji+me8RboUdeb6p+9TFcmrfcfxdeO0rvePALs=
+- jtQQbyAJmZMgxoxXWgs2L+iNxIpdj/XACsqSdh2n3Ug=
+- dXEoY3507QIOxw0v4ZlaiICSi+r0oOhpmS7CZFSqerM=
+- tz5ncsl+znXJpj5YD/CyRKsGfaqYdYpM8OF0W3NME04=
+- huAivPkBYp83qvKnvJ/YFz41TyvCaT5pVsW8J/3T0aE=
+- eBj4bfO/2+4S0F3Nfs1PescOaKAy7S+JnvRNJbBxmYQ=
+- G/EkgNwhkP4sis3wn0v2ynzlaY7tZOWSjF4us0wC4eE=
+- "+BBHUM/kukHllDtBAgtqpgsTZZu/fcfrSx5J4R+WalE="
+- Ne+Xd1FRNRRURlyuek5eqGzgO3AWHJmiuWYIjjKx16w=
+- P36Sw9fUKbkrJNmNbS3FFjGoucO2g7g28SskobFgFb8=
+- jS0Cj8XSISs+MOv6yZ0x96BlgyYZ+r692WNnlIqVOuk=
+- ni4y2Aj+aUyLwLKu/rok7nG2Qwj+vEwXQjOb2U0oxSY=
+- GvhaTRCRUStB1pxBAyH+43SxnYq05eNWSBtadYr5LW0=
+- yE3o0Q/Glv50eJo7n73w5Z2RRBDWbXO77mCxFKq4Rdk=
+- Vp8br4/NFm3UTbei7+z8TfivjLFV9uO+FarsSPsy+VA=
+- 378ZwhQvoTJ4cJ5JJh0zwEZDsd1VIeoh9iSxIbGp0o8=
+- 2DJaO5P/oFkk13aOu8YnMepyZqs/j2D6wWZyXvR37no=
+- APFWvyRDh2w6FGXzZDIO9Hl6LX8m4vQcquUn0xYiNQE=
+- RRQDBLOo7jK+mb3FEeRbQNE63wehHf4EGkbt1mShr+A=
+- B1p7k2/Fel+ge4LarFcpkXgxNB3PUnxOv1lnzzN6YXI=
+- gXrNS9RmQgc8dufVJ69mwhf+kO2rBxE10dRBdoFbSGg=
+- 4qWMiuTvHcUR6h93kUZ1pqQHQwKg1ejaoERjegrWfTg=
+- 0oUZcldJ9bNX9TrY9cx81y3eu6sseF7584mE62EAhTI=
+- bnX9zkbKi9o8Erzax39IOFj0+1kJyT5poEDrtkwrX2k=
+- z0bE6R//6cUyt6cbFnfAbdfotDpjC0QGQuDSkEMgmCU=
+- z7//uWAKdPECyBIqLzyUmPzGYr7hkb/QQCacuvpB7sw=
+- IjFY4pUqOrm6gZli3+8YkHyHFf85YdO4y+ckg68yvNs=
+- Oe2Ryvv1zOqZ/+/4DIy0LrZEZtrvwUXAIrS/Vchwx/4=
+- u4LZ+vBjBQ18CQNmmT1osznp0ZoE6PcLcvd4J2CliDw=
+- 6BP7SLoTeTN075gKw+myWidw0a6I4ZS2gIuDk/NKuDU=
+- 1PFPAN/zvVUZ4k3TM/PnYnH9KiycH1bM6NaCVCXsf+U=
+- OZIblTHLJOXG7+7646PMulvkQAR45m/lio63GmffRZU=
+- EQLJO7j3GGMwIjNFDmEEmjXMc3mmRvC0WSDtlu3qgzY=
+- zzsjoNEXk0zEAGjTo3724LEeWoN5mEnC4sk0TWK+4nI=
+- B9LLRg94avOEe8WQUXMfXrUHEPMa9NUtsHxmnLYD+wM=
+- RUwE1Gts4cez2Tr5bpGTxJ/QSgZBJPAzjOCPd8O0vq0=
+- RZUeA3paUKrpYG295fHYWwDt3jWBUo/a8FqOqYb3FiQ=
+- DTLg5WhLqPiis1DZYZCg4mtPsFN8ciJXApzYuDYQpDE=
+- WUXA2Jd89jDH1yY5OPXN3CyXTvf5gwv9tGspEBEV3rI=
+- I6ujS9lkfxNETm6GvCOKu/0Fzwy/uvz2yI8UoCjdEGQ=
+- FnUxE2DiZH76JTee/E3uEDdC9eYfLEMIufm4BSded64=
+- CXHU1FH+jM4i2FFF3PoHKiQ0UkuSgLELwOeMFWjj5UY=
+- 1B07Pu+fGaBqIfTQ8lk+7Jlzw4VfI6lBm0CIZREW6FI=
+- ElrO+5qG4I7LaXr5QH+rXiZqvufucpn0WxfxKhCCRWM=
+- 4agcWwaZ49jHlUcm8V7Y7U9dORVTPa6W1w6xrFDk3S8=
+- TTzgFd/HXIRxQLD3DkPKsm0fNAXtmK6lSbR8bJJ9odY=
+- fo7+ALb69+xrrM6x3gwgjZOAvCKFQayDTbZK8DWtItM=
+- jvCuI7OZCyqFWTthkoeQ/9j9/XXkFh6ovpN4NDpjYV0=
+- nYMCC/CDIa0gel/A/hMj0dfKAX8n60OsJ5o24TF5bdk=
+- 4dXU6OrUTiyh2JzC39H9qL2i9MNDaLDBYRYDANjkYMM=
+- mdXzKByuzopaZcNx3UWikyXtOd4OVwkzAfoKOwy4lAs=
+- 1nXrPT5xy5w9Sp41BB/iPDHJRvY8F6YdG8wNGkpQu9M=
+- m6rKtufxveFFJ9wri4ZCGsLjvDPZwfPF2RbITDuOm/0=
+- Ak7oribXt9R9ruQHE1x51xRbhbCBR+YCqspMkyfMS7U=
+- 8toPTJrYTqHyuSQjBddRXYA16zPi+yRla89h51akx3Q=
+- Smf7vudY1gnu/oXBJysuocttejS2o52ZOwBthDhXdiA=
+- rfn9GljOavGrttgwSB1GjhquvEUBeHd25yq7TBJgGZQ=
+- H/tBtTWa7ffuY0bdEzjDe9FhmUBVAuizdIwVphd70VA=
+- O3271WQ9SpRoY+GiG4uoyYMCwAeYh6xqLiWtfWNVrrI=
+- rRsw+YjBVWJ4GlFz6vMgxw3apVCwkGTMXo882rWCQWQ=
+- 3lJILM4gG6zGpUSCcPNkQeGaUa5dJk7A9oODpFnCyKg=
+- 2ny66o9nD1zX3NFQQINmQBLUMjSAzKS/gJmX4HOfSno=
+- opjO+A9DFAUohmc4l/Ngd642PPmDvcLtk1fnVEWneGw=
+- 2OzhZeUEj77ROGq2JKWwp1V86uXIGiYpDdrlPiJqL58=
+- 44mhlg8Xsf4ST/BhMR2GO8t7wC5+kre+ZWGSIJgp4jE=
+- QHuAd7nPmFYh7togfS65hv+pO5bMaD+F3D27pnEf5a8=
+- ASzyllk98+VeMnY5ueJ15krcHcSk9FCv8WbTbExAd1g=
+- Hzk8+K4dnIfORT17Xjf7A9i3uEOfut1Lee9snWAaF3A=
+- O20h1UWfb2xgxsIAGGiEDP88v6E+pP1N8qyBkyvpySA=
+- dVXyhawmXroihHIaa8sBe6cHil4FTmqPEx82NyL+tjM=
+- n+YgxhX7JaskwABqKjg8AaLO8vi1hzv4M5XjJdawFBY=
+- L5XfNK3EO3p7P+Gc0IRqlNOnnqSOO2WNmNlny+tbTgk=
+- QbxHwsuoKaWb5KXfOzExV9fQMUEkFsMeJSsHCNe8sL0=
+- kQHAwUu+cj9iQUkdV5ElSPwq5GeMFdgp2HQJdfyfUWk=
+- SO3xPyK5sXQ2WQ7t14gcCma6xZ6kgkwyR7T5LHPcwkk=
+- vi4io7Ap3g1iyUzHH5YuMfFThyIn638ikkFZBQBEDrs=
+- R8ZHsGHOl3ghg744Hafaayf1aoW1soCFH6V9Rxb54TY=
+- TgMRRze5BM0V/iw5msLqr+oqHOE7x67P0WtERZ7+hjg=
+- Q1JisbCEuFK5vt3wndyCPF1nt2Hkj1rsNbOHNO4ry0o=
+- Bhx+nWtsxJFpCkj/6ARXaJkt41fg/5Ts61ey5Md5KEU=
+- R99pWzGJY4vrpt4hSVrQ9JJBO835lwsK42ftRXxOn38=
+- fCiBLpOCPuAaoBqRzEBlUy3Euk5jFOu0+5EbN2BxQUA=
+- GA3lL17ztYwRmsr+yhW9KF4w6GQYsvVm+WnL6uEgAOc=
+- Z7zb2rzzAHj1NWISaK4V7T5y1cLhH2XA2nx6mhbLfJ8=
+- ZpgFkA3tG+tb8OafYwZ1ni49KLK6NrCfxMTaGYFqNHg=
+- coDOjh/aMvJICvvZmHkoFESruzQ+MPbmVTD/vcAnC98=
+- zyOqAaoCS0p9lVK/+riPcLgwiE9aNOidWm09J2KKoj8=
+- zNRwKGy4bRwBPszvZ43NXv2i48JzZE7RT4eWV3GcvKk=
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 0d759de4900..146e9b48442 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -6,6 +6,7 @@ const path = require('path');
const BABEL_VERSION = require('@babel/core/package.json').version;
const SOURCEGRAPH_VERSION = require('@sourcegraph/code-host-integration/package.json').version;
+const GITLAB_WEB_IDE_VERSION = require('@gitlab/web-ide/package.json').version;
const BABEL_LOADER_VERSION = require('babel-loader/package.json').version;
const CompressionPlugin = require('compression-webpack-plugin');
@@ -54,17 +55,31 @@ const INCREMENTAL_COMPILER_RECORD_HISTORY = IS_DEV_SERVER && !process.env.CI;
const WEBPACK_REPORT = process.env.WEBPACK_REPORT && process.env.WEBPACK_REPORT !== 'false';
const WEBPACK_MEMORY_TEST =
process.env.WEBPACK_MEMORY_TEST && process.env.WEBPACK_MEMORY_TEST !== 'false';
-const NO_COMPRESSION = process.env.NO_COMPRESSION && process.env.NO_COMPRESSION !== 'false';
-const NO_SOURCEMAPS = process.env.NO_SOURCEMAPS && process.env.NO_SOURCEMAPS !== 'false';
+let NO_COMPRESSION = process.env.NO_COMPRESSION && process.env.NO_COMPRESSION !== 'false';
+let NO_SOURCEMAPS = process.env.NO_SOURCEMAPS && process.env.NO_SOURCEMAPS !== 'false';
+let NO_HASHED_CHUNKS = process.env.NO_HASHED_CHUNKS && process.env.NO_HASHED_CHUNKS !== 'false';
+
+if (WEBPACK_REPORT) {
+ console.log('Webpack report enabled. Running a "slim" production build.');
+ // For our webpack report we need no source maps, compression _or_ hashed file names.
+ NO_SOURCEMAPS = true;
+ NO_COMPRESSION = true;
+ NO_HASHED_CHUNKS = true;
+}
const WEBPACK_OUTPUT_PATH = path.join(ROOT_PATH, 'public/assets/webpack');
const WEBPACK_PUBLIC_PATH = '/assets/webpack/';
const SOURCEGRAPH_PACKAGE = '@sourcegraph/code-host-integration';
+const GITLAB_WEB_IDE_PACKAGE = '@gitlab/web-ide';
const SOURCEGRAPH_PATH = path.join('sourcegraph', SOURCEGRAPH_VERSION, '/');
const SOURCEGRAPH_OUTPUT_PATH = path.join(WEBPACK_OUTPUT_PATH, SOURCEGRAPH_PATH);
const SOURCEGRAPH_PUBLIC_PATH = path.join(WEBPACK_PUBLIC_PATH, SOURCEGRAPH_PATH);
+const GITLAB_WEB_IDE_PATH = path.join('gitlab-vscode', GITLAB_WEB_IDE_VERSION, '/');
+const GITLAB_WEB_IDE_OUTPUT_PATH = path.join(WEBPACK_OUTPUT_PATH, GITLAB_WEB_IDE_PATH);
+const GITLAB_WEB_IDE_PUBLIC_PATH = path.join(WEBPACK_PUBLIC_PATH, GITLAB_WEB_IDE_PATH);
+
const devtool = IS_PRODUCTION ? 'source-map' : 'cheap-module-eval-source-map';
let autoEntriesCount = 0;
@@ -245,8 +260,10 @@ module.exports = {
output: {
path: WEBPACK_OUTPUT_PATH,
publicPath: WEBPACK_PUBLIC_PATH,
- filename: IS_PRODUCTION ? '[name].[contenthash:8].bundle.js' : '[name].bundle.js',
- chunkFilename: IS_PRODUCTION ? '[name].[contenthash:8].chunk.js' : '[name].chunk.js',
+ filename:
+ IS_PRODUCTION && !NO_HASHED_CHUNKS ? '[name].[contenthash:8].bundle.js' : '[name].bundle.js',
+ chunkFilename:
+ IS_PRODUCTION && !NO_HASHED_CHUNKS ? '[name].[contenthash:8].chunk.js' : '[name].chunk.js',
globalObject: 'this', // allow HMR and web workers to play nice
},
@@ -473,7 +490,9 @@ module.exports = {
new VueLoaderPlugin(),
// automatically configure monaco editor web workers
- new MonacoWebpackPlugin(),
+ new MonacoWebpackPlugin({
+ filename: '[name].[contenthash:8].worker.js',
+ }),
new GraphqlKnownOperationsPlugin({ filename: 'graphql_known_operations.yml' }),
@@ -583,6 +602,10 @@ module.exports = {
},
},
{
+ from: path.join(ROOT_PATH, 'node_modules', GITLAB_WEB_IDE_PACKAGE, 'dist', 'public'),
+ to: GITLAB_WEB_IDE_OUTPUT_PATH,
+ },
+ {
from: path.join(
ROOT_PATH,
'node_modules/@gitlab/visual-review-tools/dist/visual_review_toolbar.js',
@@ -677,6 +700,8 @@ module.exports = {
statsFilename: path.join(ROOT_PATH, 'webpack-report/stats.json'),
statsOptions: {
source: false,
+ errors: false,
+ warnings: false,
},
}),
@@ -689,6 +714,7 @@ module.exports = {
IS_JH: IS_JH ? 'window.gon && window.gon.jh' : JSON.stringify(false),
// This is used by Sourcegraph because these assets are loaded dnamically
'process.env.SOURCEGRAPH_PUBLIC_PATH': JSON.stringify(SOURCEGRAPH_PUBLIC_PATH),
+ 'process.env.GITLAB_WEB_IDE_PUBLIC_PATH': JSON.stringify(GITLAB_WEB_IDE_PUBLIC_PATH),
...(IS_PRODUCTION ? {} : { LIVE_RELOAD: DEV_SERVER_LIVERELOAD }),
}),
diff --git a/danger/Dangerfile-bundle_size b/danger/Dangerfile-bundle_size
index 23ab726096e..122002c1a7b 100644
--- a/danger/Dangerfile-bundle_size
+++ b/danger/Dangerfile-bundle_size
@@ -1,38 +1,7 @@
# frozen_string_literal: true
# This file isn't named "Dangerfile" so that it's not imported by default since it's only meant to be run in the `bundle-size-review` job.
-analysis_result = "./bundle-size-review/analysis.json"
-markdown_result = "./bundle-size-review/comparison.md"
-
-# Executing the webpack-entry-point-analyser
-# We would like to do that in the CI file directly,
-# but unfortunately the head_commit SHA is not available
-# as a CI variable due to our merge into master simulation
-analyze_cmd = [
- "webpack-entry-point-analyser",
- "--from-file ./webpack-report/stats.json",
- "--json #{analysis_result}",
- " --sha #{gitlab&.head_commit}"
-].join(" ")
-
-# execute analysis
-`#{analyze_cmd}`
-
-# We are executing the comparison by comparing the start_sha
-# to the current pipeline result. The start_sha is the commit
-# from master that was merged into for the merged pipeline.
-comparison_cmd = [
- "webpack-compare-reports",
- "--job #{ENV["CI_JOB_ID"]}",
- "--to-file #{analysis_result}",
- "--html ./bundle-size-review/comparison.html",
- "--markdown #{markdown_result}"
-].join(" ")
-
-# execute comparison
-`#{comparison_cmd}`
-
-comment = `cat #{markdown_result}`
+comment = `cat ./bundle-size-review/comparison.md`
unless comment.strip.empty?
markdown(<<~MARKDOWN)
diff --git a/danger/config_files/Dangerfile b/danger/config_files/Dangerfile
new file mode 100644
index 00000000000..dcd2e44df07
--- /dev/null
+++ b/danger/config_files/Dangerfile
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+config_files.add_suggestion_for_missing_introduced_by_url
diff --git a/danger/database/Dangerfile b/danger/database/Dangerfile
index 7b3a32358fe..df6ffab7c94 100644
--- a/danger/database/Dangerfile
+++ b/danger/database/Dangerfile
@@ -31,9 +31,8 @@ by removing the ~database label and re-running the [`danger-review` job](#{ENV['
MSG
DB_MIGRATION_TESTING_REQUIRED_MESSAGE = <<~MSG
-1. If this is not a ~"Community contribution" or from a Fork, kick off the
- `db:gitlabcom-database-testing` manual job.
-
+1. Kick off the `db:gitlabcom-database-testing` manual job. This job can also be used before
+ requesting review to test your migrations against production data.
MSG
DATABASE_APPROVED_LABEL = 'database::approved'
diff --git a/danger/plugins/config_files.rb b/danger/plugins/config_files.rb
new file mode 100644
index 00000000000..2604a491d03
--- /dev/null
+++ b/danger/plugins/config_files.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+require_relative '../../tooling/danger/config_files'
+
+module Danger
+ class ConfigFiles < ::Danger::Plugin
+ # Put the helper code somewhere it can be tested
+ include Tooling::Danger::ConfigFiles
+ end
+end
diff --git a/data/deprecations/14-10-manual-iteration-management.yml b/data/deprecations/14-10-manual-iteration-management.yml
deleted file mode 100644
index f677f4fe668..00000000000
--- a/data/deprecations/14-10-manual-iteration-management.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-- name: "Manual iteration management" # The name of the feature to be deprecated
- announcement_milestone: "14.10" # The milestone when this feature was first announced as deprecated.
- announcement_date: "2022-04-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-04-22" # 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 # If this deprecation is a breaking change, set this value to true
- reporter: mcelicalderonG # GitLab username of the person reporting the deprecation
- body: | # Do not modify this line, instead modify the lines below.
- Manual iteration management is deprecated and only automatic iteration cadences will be supported in the future.
-
- Creating and deleting iterations will be fully removed in 16.0. Updating all iteration fields except for
- `description` will also be removed.
-
- On the GraphQL API the following mutations will be removed:
-
- 1. `iterationCreate`
- 1. `iterationDelete`
-
- The update `updateIteration` mutation will only allow updating the iteration's `description`. The following
- arguments will be removed:
-
- 1. `title`
- 1. `dueDate`
- 1. `startDate`
-
- For more information about iteration cadences, you can refer to
- [the documentation of the feature](https://docs.gitlab.com/ee/user/group/iterations/#iteration-cadences).
-# The following items are not published on the docs page, but may be used in the future.
- stage: Plan
- 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/356069
- 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/14-5-certificate-based-integration-with-kubernetes-saas.yml b/data/deprecations/14-5-certificate-based-integration-with-kubernetes-saas.yml
index 774b41a4170..559189d759b 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,11 +1,11 @@
- name: "SaaS certificate-based integration with Kubernetes"
announcement_milestone: "14.5"
announcement_date: "2021-11-15"
- removal_milestone: "15.6"
- removal_date: "2022-11-22" # the date of the milestone release when this feature is planned to be removed
+ removal_milestone: "15.9"
+ removal_date: "2023-02-22" # the date of the milestone release when this feature is planned to be removed
breaking_change: true
body: |
- The certificate-based integration with Kubernetes will be [deprecated and removed](https://about.gitlab.com/blog/2021/11/15/deprecating-the-cert-based-kubernetes-integration/). As a GitLab SaaS customer, on new namespaces, you will no longer be able to integrate GitLab and your cluster using the certificate-based approach as of GitLab 15.0. The integration for current users will be enabled per namespace. The integrations are expected to be switched off completely on GitLab SaaS around 2022 November 22.
+ The certificate-based integration with Kubernetes will be [deprecated and removed](https://about.gitlab.com/blog/2021/11/15/deprecating-the-cert-based-kubernetes-integration/). As a GitLab SaaS customer, on new namespaces, you will no longer be able to integrate GitLab and your cluster using the certificate-based approach as of GitLab 15.0. The integration for current users will be enabled per namespace.
For a more robust, secure, forthcoming, and reliable integration with Kubernetes, we recommend you use the
[agent for Kubernetes](https://docs.gitlab.com/ee/user/clusters/agent/) to connect Kubernetes clusters with GitLab. [How do I migrate?](https://docs.gitlab.com/ee/user/infrastructure/clusters/migrate_to_gitlab_agent.html)
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 43a071ff746..f76bd9f5424 100644
--- a/data/deprecations/14-5-certificate-based-integration-with-kubernetes.yml
+++ b/data/deprecations/14-5-certificate-based-integration-with-kubernetes.yml
@@ -1,15 +1,15 @@
- name: "Self-managed certificate-based integration with Kubernetes"
announcement_milestone: "14.5"
announcement_date: "2021-11-15"
- removal_milestone: "16.0"
- removal_date: "2023-05-22" # the date of the milestone release when this feature is planned to be removed
+ removal_milestone: "17.0"
+ removal_date: "2024-05-22" # the date of the milestone release when this feature is planned to be removed
breaking_change: true
body: |
The certificate-based integration with Kubernetes [will be deprecated and removed](https://about.gitlab.com/blog/2021/11/15/deprecating-the-cert-based-kubernetes-integration/).
As a self-managed customer, we are introducing the [feature flag](../administration/feature_flags.md#enable-or-disable-the-feature) `certificate_based_clusters` in GitLab 15.0 so you can keep your certificate-based integration enabled. However, the feature flag will be disabled by default, so this change is a **breaking change**.
- In GitLab 16.0 we will remove both the feature and its related code. Until the final removal in 16.0, features built on this integration will continue to work, if you enable the feature flag. Until the feature is removed, GitLab will continue to fix security and critical issues as they arise.
+ In GitLab 17.0 we will remove both the feature and its related code. Until the final removal in 17.0, features built on this integration will continue to work, if you enable the feature flag. Until the feature is removed, GitLab will continue to fix security and critical issues as they arise.
For a more robust, secure, forthcoming, and reliable integration with Kubernetes, we recommend you use the
[agent for Kubernetes](https://docs.gitlab.com/ee/user/clusters/agent/) to connect Kubernetes clusters with GitLab. [How do I migrate?](https://docs.gitlab.com/ee/user/infrastructure/clusters/migrate_to_gitlab_agent.html)
diff --git a/data/deprecations/15-3-vulnerabilityFindingDismiss-mutation.yml b/data/deprecations/15-3-vulnerabilityFindingDismiss-mutation.yml
new file mode 100644
index 00000000000..25e8a6a2488
--- /dev/null
+++ b/data/deprecations/15-3-vulnerabilityFindingDismiss-mutation.yml
@@ -0,0 +1,23 @@
+#
+# REQUIRED FIELDS
+#
+- name: "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
+ 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: matt_wilson # (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/367166 # (required) Link to the deprecation issue in GitLab
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ You can use the vulnerabilityFindingDismiss GraphQL mutation to set the status of a vulnerability finding to `Dismissed`. Previously, this mutation used the `id` field to identify findings uniquely. However, this did not work for dismissing findings from the pipeline security tab. Therefore, using the `id` field as an identifier has been dropped in favor of the `uuid` field. Using the 'uuid' field as an identifier allows you to dismiss the finding from the pipeline security tab.
+#
+# OPTIONAL FIELDS
+#
+ 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.
+ 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/graphql/reference/index.html#mutationvulnerabilityfindingdismiss
+ 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-4-confidence-field-in-graphql.yml b/data/deprecations/15-4-confidence-field-in-graphql.yml
new file mode 100644
index 00000000000..da3287eeff9
--- /dev/null
+++ b/data/deprecations/15-4-confidence-field-in-graphql.yml
@@ -0,0 +1,14 @@
+- name: "Vulnerability confidence field"
+ announcement_milestone: "15.4"
+ announcement_date: "2022-09-22"
+ removal_milestone: "16.0"
+ removal_date: "2023-05-22"
+ breaking_change: true
+ reporter: matt_wilson
+ stage: govern
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372332
+ body: |
+ In GitLab 15.3, [security report schemas below version 15 were deprecated](https://docs.gitlab.com/ee/update/deprecations.html#security-report-schemas-version-14xx).
+ The `confidence` attribute on vulnerability findings exists only in schema versions before `15-0-0`, and therefore is effectively deprecated since GitLab 15.4 supports schema version `15-0-0`. To maintain consistency
+ between the reports and our public APIs, the `confidence` attribute on any vulnerability-related components of our GraphQL API is now deprecated and will be
+ removed in 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
new file mode 100644
index 00000000000..2265354c4c7
--- /dev/null
+++ b/data/deprecations/15-4-create-deprecation-draft-quick-action-toggle.yml
@@ -0,0 +1,12 @@
+- name: "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.
+ 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
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365365 # (required) Link to the deprecation issue in GitLab
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ In order to make the behavior of toggling the draft status of a merge request more clear via a quick action, we're deprecating and removing the toggle behavior of the `/draft` quick action. Beginning with the 16.0 release of GitLab, `/draft` will only set a merge request to Draft and a new `/ready` quick action will be used to remove the draft status.
+ documentation_url: # (optional) This is a link to the current documentation page
diff --git a/data/deprecations/15-4-cs-docker-variables.yml b/data/deprecations/15-4-cs-docker-variables.yml
new file mode 100644
index 00000000000..37f2552ac00
--- /dev/null
+++ b/data/deprecations/15-4-cs-docker-variables.yml
@@ -0,0 +1,11 @@
+- name: "Container Scanning variables that reference Docker"
+ announcement_milestone: "15.4"
+ announcement_date: "2022-09-22"
+ removal_milestone: "16.0"
+ removal_date: "2023-05-22"
+ breaking_change: true
+ reporter: sam.white
+ stage: secure
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/371840
+ body: |
+ All Container Scanning variables that are prefixed by `DOCKER_` in variable name are deprecated. This includes the `DOCKER_IMAGE`, `DOCKER_PASSWORD`, `DOCKER_USER`, and `DOCKERFILE_PATH` variables. Support for these variables will be removed in the GitLab 16.0 release. Use the [new variable names](https://docs.gitlab.com/ee/user/application_security/container_scanning/#available-cicd-variables) `CS_IMAGE`, `CS_REGISTRY_PASSWORD`, `CS_REGISTRY_USER`, and `CS_DOCKERFILE_PATH` in place of the deprecated names.
diff --git a/data/deprecations/15-4-deprecate-bundled-grafana.yml b/data/deprecations/15-4-deprecate-bundled-grafana.yml
new file mode 100644
index 00000000000..b6659e01c42
--- /dev/null
+++ b/data/deprecations/15-4-deprecate-bundled-grafana.yml
@@ -0,0 +1,17 @@
+- name: "Bundled Grafana deprecated"
+ announcement_milestone: "15.3"
+ announcement_date: "2022-08-22"
+ removal_milestone: "15.4"
+ removal_date: "2022-09-22"
+ breaking_change: false
+ reporter: dorrino
+ body: | # Do not modify this line, instead modify the lines below.
+ In GitLab 15.4, we will be swapping the bundled Grafana to a fork of Grafana maintained by GitLab.
+
+ There was an [identified CVE for Grafana](https://nvd.nist.gov/vuln/detail/CVE-2022-31107), and to mitigate this security vulnerability, we must swap to our own fork because the older version of Grafana we were bundling is no longer receiving long-term support.
+
+ This is not expected to cause any incompatibilities with the previous version of Grafana. Neither when using our bundled version, nor when using an external instance of Grafana.
+ stage: Enablement
+ tiers: [Free, Premium, Ultimate]
+ issue_url: https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/6972
+ documentation_url: https://docs.gitlab.com/ee/administration/monitoring/performance/grafana_configuration.html
diff --git a/data/deprecations/15-4-non-expiring-access-tokens.yml b/data/deprecations/15-4-non-expiring-access-tokens.yml
new file mode 100644
index 00000000000..c4becf9ed34
--- /dev/null
+++ b/data/deprecations/15-4-non-expiring-access-tokens.yml
@@ -0,0 +1,25 @@
+- name: "Non-expiring access tokens"
+ announcement_milestone: "15.4"
+ announcement_date: "2022-09-22"
+ removal_milestone: "16.0"
+ removal_date: "2023-05-22"
+ breaking_change: true
+ reporter: hsutor
+ body: | # Do not modify this line, instead modify the lines below.
+ Access tokens that have no expiration date are valid indefinitely, which presents a security risk if the access token
+ is divulged. Because access tokens that have an exipiration date are better, from GitLab 15.3 we
+ [populate a default expiration date](https://gitlab.com/gitlab-org/gitlab/-/issues/348660).
+
+ In GitLab 16.0, any [personal](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html),
+ [project](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html), or
+ [group](https://docs.gitlab.com/ee/user/group/settings/group_access_tokens.html) access token that does not have an
+ expiration date will automatically have an expiration date set at one year.
+
+ We recommend giving your access tokens an expiration date in line with your company's security policies before the
+ default is applied:
+
+ - On GitLab.com during the 16.0 milestone.
+ - On GitLab self-managed instances when they are upgraded to 16.0.
+ stage: Manage
+ tiers: [Free, Premium, Ultimate]
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/369122
diff --git a/data/deprecations/15-4-starboard-directive.yml b/data/deprecations/15-4-starboard-directive.yml
new file mode 100644
index 00000000000..07b7cdf7fd6
--- /dev/null
+++ b/data/deprecations/15-4-starboard-directive.yml
@@ -0,0 +1,11 @@
+- name: "Starboard directive in the config for the GitLab Agent for Kubernetes"
+ announcement_milestone: "15.4"
+ announcement_date: "2022-09-22"
+ removal_milestone: "16.0"
+ removal_date: "2023-05-22"
+ breaking_change: true
+ reporter: sam.white
+ stage: secure
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/368828
+ body: |
+ GitLab's operational container scanning capabilities no longer require starboard to be installed. Consequently, use of the `starboard:` directive in the configuration file for the GitLab Agent for Kubernetes is now deprecated and is scheduled for removal in GitLab 16.0. Update your configuration file to use the `container_scanning:` directive.
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 3487deaf5a7..46f6012a7db 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
@@ -2,8 +2,8 @@
# 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.
- announcement_milestone: "15.8" # (required) The milestone when this feature was deprecated.
- announcement_date: "2023-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.
+ 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.
removal_date: "2023-05-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: true # (required) Change to true if this removal is a breaking change.
@@ -11,17 +11,19 @@
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/366477 # (required) Link to the deprecation issue in GitLab
body: | # (required) Do not modify this line, instead modify the lines below.
- All [security report schema](https://gitlab.com/gitlab-org/security-products/security-report-schemas) versions before 15.0.0 are considered deprecated in GitLab %15.8. Specifically, all [schemas](https://gitlab.com/gitlab-org/gitlab/-/tree/master/ee/lib/ee/gitlab/ci/parsers/security/validators/schemas) that match 14.*.* will be deprecated.
+ Version 14.x.x [security report schemas](https://gitlab.com/gitlab-org/security-products/security-report-schemas) are deprecated.
- Please note that any [security report scanner integration](https://docs.gitlab.com/ee/development/integrations/secure.html) with GitLab using a deprecated schema version will result in a deprecation warning as a result of [report validation](https://docs.gitlab.com/ee/development/integrations/secure.html#report-validation).
+ In GitLab 15.8 and later, [security report scanner integrations](https://docs.gitlab.com/ee/development/integrations/secure.html) that use schema version 14.x.x will display a deprecation warning in the pipeline's **Security** tab.
- See [Security report validation](https://docs.gitlab.com/ee/user/application_security/#security-report-validation) for more information.
+ In GitLab 16.0 and later, the feature will be removed. Security reports that use schema version 14.x.x will cause an error in the pipeline's **Security** tab.
+
+ For more information, refer to [security report validation](https://docs.gitlab.com/ee/user/application_security/#security-report-validation).
#
# OPTIONAL FIELDS
#
- end_of_support_milestone: "15.8" # (optional) Use "XX.YY" format. The milestone when support for this feature will end.
- end_of_support_date: "2023-01-22" # (optional) The date of the milestone release when support for this feature will end.
- tiers: [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]
+ 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.
+ tiers: [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: # (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/templates/_deprecation_template.md.erb b/data/deprecations/templates/_deprecation_template.md.erb
index 93713a5e407..0725ce2891e 100644
--- a/data/deprecations/templates/_deprecation_template.md.erb
+++ b/data/deprecations/templates/_deprecation_template.md.erb
@@ -53,7 +53,7 @@ sole discretion of GitLab Inc.
### <%= deprecation["name"]%>
<% 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"]%>)
+End of Support: GitLab <span class="removal-milestone"><%= deprecation["end_of_support_milestone"]%></span> (<%= deprecation["end_of_support_date"]%>)<br />
<% end -%>
Planned removal: GitLab <span class="removal-milestone"><%= deprecation["removal_milestone"]%></span> (<%= deprecation["removal_date"]%>)
<% if deprecation["breaking_change"] -%>
diff --git a/data/deprecations/templates/example.yml b/data/deprecations/templates/example.yml
index c69ebe2c768..e5e04e5d216 100644
--- a/data/deprecations/templates/example.yml
+++ b/data/deprecations/templates/example.yml
@@ -1,12 +1,11 @@
# This is a template for a feature deprecation.
#
-# Please refer to the deprecation guidelines to confirm your understanding of GitLab's definitions.
+# 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.
-#
-# If an End of Support period applies, the announcement should be shared with GitLab Support
-# in the `#spt_managers` on Slack and mention `@gitlab-com/support` in this MR.
+# 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.
#
@@ -35,10 +34,16 @@
END OF BODY COMMENT -->
#
-# OPTIONAL FIELDS
+# 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
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 16aab3d14c2..dac96032359 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
@@ -43,9 +43,8 @@
gitlab_rails['env'] = { 'GITLAB_LEGACY_BACKGROUND_UPLOADS' => 'artifacts,external_diffs,lfs,uploads,packages,dependency_proxy,terraform_state,pages' }
```
- Prefixes will be supported officially in [GitLab 15.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91307).
- This workaround will be dropped, so we encourage migrating to consolidated object storage.
-
+ Support for prefixes was restored in GitLab 15.2 via [this MR](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91307).
+ Support for setting `GITLAB_LEGACY_BACKGROUND_UPLOADS` will be removed in GitLab 15.4.
stage: Enablement
tiers: [Core, Premium, Ultimate]
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
new file mode 100644
index 00000000000..3ba5b451718
--- /dev/null
+++ b/data/removals/15_3/15-3-vulnerability-report-state-sort.yml
@@ -0,0 +1,25 @@
+#
+# 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.
+ 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.
+ removal_date: "2022-08-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: matt_wilson # (required) GitLab username of the person reporting the removal
+ 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/360516 # (required) Link to the deprecation issue in GitLab
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ The ability to sort the Vulnerability Report by the `State` column was disabled and put behind a feature flag in GitLab 14.10 due to a refactor
+ of the underlying data model. The feature flag has remained off by default as further refactoring will be required to ensure sorting
+ by this value remains performant. Due to very low usage of the `State` column for sorting, the feature flag is instead removed in 15.3 to simplify the codebase and prevent any unwanted performance degradation.
+#
+# OPTIONAL FIELDS
+#
+ 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.
+ 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_3/15-3-vulnerability-report-tool-sort.yml b/data/removals/15_3/15-3-vulnerability-report-tool-sort.yml
new file mode 100644
index 00000000000..9aba5ca5ee7
--- /dev/null
+++ b/data/removals/15_3/15-3-vulnerability-report-tool-sort.yml
@@ -0,0 +1,26 @@
+#
+# 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.
+ 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.
+ removal_date: "2022-08-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: matt_wilson # (required) GitLab username of the person reporting the removal
+ 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/363138 # (required) Link to the deprecation issue in GitLab
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ The ability to sort the Vulnerability Report by the `Tool` column (scan type) was disabled and put behind a feature flag in GitLab 14.10 due to a refactor
+ of the underlying data model. The feature flag has remained off by default as further refactoring will be required to ensure sorting
+ by this value remains performant. Due to very low usage of the `Tool` column for sorting, the feature flag is instead removed in
+ GitLab 15.3 to simplify the codebase and prevent any unwanted performance degradation.
+#
+# OPTIONAL FIELDS
+#
+ 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.
+ 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_3/removal_debian9.yml b/data/removals/15_3/removal_debian9.yml
new file mode 100644
index 00000000000..5438922173f
--- /dev/null
+++ b/data/removals/15_3/removal_debian9.yml
@@ -0,0 +1,6 @@
+- name: "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
+ body: |
+ Long term service and support (LTSS) for [Debian 9 Stretch ended in July 2022](https://wiki.debian.org/LTS). Therefore, we will no longer support the Debian 9 distribution for the GitLab package. Users can upgrade to Debian 10 or Debian 11.
diff --git a/data/removals/15_4/15-4-sast-analyzer-consolidation.yml b/data/removals/15_4/15-4-sast-analyzer-consolidation.yml
new file mode 100644
index 00000000000..825fb2b4bfc
--- /dev/null
+++ b/data/removals/15_4/15-4-sast-analyzer-consolidation.yml
@@ -0,0 +1,30 @@
+- name: "SAST analyzer consolidation and CI/CD template changes"
+ announcement_milestone: "14.8"
+ announcement_date: "2022-02-22"
+ removal_milestone: "15.4"
+ removal_date: "2022-09-22"
+ breaking_change: true
+ reporter: connorgilbert
+ stage: Secure
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/352554
+ body: | # (required) Do not modify this line, instead modify the lines below.
+ We have replaced the GitLab SAST [analyzers](https://docs.gitlab.com/ee/user/application_security/sast/analyzers/) for certain languages in GitLab 15.4 as part of our long-term strategy to deliver a more consistent user experience, faster scan times, and reduced CI minute usage.
+
+ Starting from GitLab 15.4, the [GitLab-managed SAST CI/CD template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml) uses [Semgrep-based scanning](https://docs.gitlab.com/ee/user/application_security/sast/analyzers.html#transition-to-semgrep-based-scanning) instead of the following analyzers:
+
+ - [ESLint](https://gitlab.com/gitlab-org/security-products/analyzers/eslint) for JavaScript, TypeScript, React
+ - [Gosec](https://gitlab.com/gitlab-org/security-products/analyzers/gosec) for Go
+ - [Bandit](https://gitlab.com/gitlab-org/security-products/analyzers/bandit) for Python
+ - [SpotBugs](https://gitlab.com/gitlab-org/security-products/analyzers/spotbugs) for Java
+
+ We will no longer make any updates to the ESLint-, Gosec-, and Bandit-based analyzers.
+ The SpotBugs-based analyzer will continue to be used for Groovy, Kotlin, and Scala scanning.
+
+ We won't delete container images previously published for these analyzers, so older versions of the CI/CD template will continue to work.
+
+ If you changed the default GitLab SAST configuration, you may need to update your configuration as detailed in the [deprecation issue for this change](https://gitlab.com/gitlab-org/gitlab/-/issues/352554#actions-required).
+# The following items are not published on the docs page, but may be used in the future.
+ tiers: [Free, Silver, Gold, Core, Premium, Ultimate]
+ documentation_url: https://docs.gitlab.com/ee/user/application_security/sast/analyzers.html#transition-to-semgrep-based-scanning # (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/templates/example.yml b/data/removals/templates/example.yml
index 3674583cd7d..8d7d694be26 100644
--- a/data/removals/templates/example.yml
+++ b/data/removals/templates/example.yml
@@ -32,8 +32,6 @@
#
# OPTIONAL FIELDS
#
- 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.
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
diff --git a/db/docs/analytics_cycle_analytics_aggregations.yml b/db/docs/analytics_cycle_analytics_aggregations.yml
index b79cb949d1f..ab92c5c078b 100644
--- a/db/docs/analytics_cycle_analytics_aggregations.yml
+++ b/db/docs/analytics_cycle_analytics_aggregations.yml
@@ -4,6 +4,6 @@ classes:
- Analytics::CycleAnalytics::Aggregation
feature_categories:
- value_stream_management
-description: TODO
+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'
diff --git a/db/docs/analytics_cycle_analytics_group_value_streams.yml b/db/docs/analytics_cycle_analytics_group_value_streams.yml
index d41ed8168d0..8942439dddd 100644
--- a/db/docs/analytics_cycle_analytics_group_value_streams.yml
+++ b/db/docs/analytics_cycle_analytics_group_value_streams.yml
@@ -4,6 +4,6 @@ classes:
- Analytics::CycleAnalytics::GroupValueStream
feature_categories:
- value_stream_management
-description: TODO
+description: Store group level Value Stream objects.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36658
milestone: '13.2'
diff --git a/db/docs/analytics_cycle_analytics_issue_stage_events.yml b/db/docs/analytics_cycle_analytics_issue_stage_events.yml
index cc993799033..b3f6a9f4716 100644
--- a/db/docs/analytics_cycle_analytics_issue_stage_events.yml
+++ b/db/docs/analytics_cycle_analytics_issue_stage_events.yml
@@ -4,6 +4,6 @@ classes:
- Analytics::CycleAnalytics::IssueStageEvent
feature_categories:
- value_stream_management
-description: TODO
+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'
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 796db2a54af..ae3074d544b 100644
--- a/db/docs/analytics_cycle_analytics_merge_request_stage_events.yml
+++ b/db/docs/analytics_cycle_analytics_merge_request_stage_events.yml
@@ -4,6 +4,6 @@ classes:
- Analytics::CycleAnalytics::MergeRequestStageEvent
feature_categories:
- value_stream_management
-description: TODO
+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'
diff --git a/db/docs/analytics_cycle_analytics_project_stages.yml b/db/docs/analytics_cycle_analytics_project_stages.yml
index 051f1c585ba..1ff917faf94 100644
--- a/db/docs/analytics_cycle_analytics_project_stages.yml
+++ b/db/docs/analytics_cycle_analytics_project_stages.yml
@@ -4,6 +4,6 @@ classes:
- Analytics::CycleAnalytics::ProjectStage
feature_categories:
- value_stream_management
-description: TODO
+description: Persists project level value stream analytics stages.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/15061
milestone: '12.2'
diff --git a/db/docs/analytics_cycle_analytics_stage_event_hashes.yml b/db/docs/analytics_cycle_analytics_stage_event_hashes.yml
index cec953a261d..3df5ee1c172 100644
--- a/db/docs/analytics_cycle_analytics_stage_event_hashes.yml
+++ b/db/docs/analytics_cycle_analytics_stage_event_hashes.yml
@@ -4,6 +4,6 @@ classes:
- Analytics::CycleAnalytics::StageEventHash
feature_categories:
- value_stream_management
-description: TODO
+description: Stores hashes of Value Stream Analytics stage configurations.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67259
milestone: '14.2'
diff --git a/db/docs/analytics_devops_adoption_segments.yml b/db/docs/analytics_devops_adoption_segments.yml
index 18abfcaff09..4b22c5926c2 100644
--- a/db/docs/analytics_devops_adoption_segments.yml
+++ b/db/docs/analytics_devops_adoption_segments.yml
@@ -4,6 +4,6 @@ classes:
- Analytics::DevopsAdoption::EnabledNamespace
feature_categories:
- devops_reports
-description: TODO
+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'
diff --git a/db/docs/analytics_devops_adoption_snapshots.yml b/db/docs/analytics_devops_adoption_snapshots.yml
index 8bb87fd08fc..3fcaea684d6 100644
--- a/db/docs/analytics_devops_adoption_snapshots.yml
+++ b/db/docs/analytics_devops_adoption_snapshots.yml
@@ -4,6 +4,6 @@ classes:
- Analytics::DevopsAdoption::Snapshot
feature_categories:
- devops_reports
-description: TODO
+description: Contains periodical DevOps Adoption data points.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47388
milestone: '13.7'
diff --git a/db/docs/analytics_language_trend_repository_languages.yml b/db/docs/analytics_language_trend_repository_languages.yml
index b27543dffaf..c42328b6bc8 100644
--- a/db/docs/analytics_language_trend_repository_languages.yml
+++ b/db/docs/analytics_language_trend_repository_languages.yml
@@ -3,7 +3,7 @@ table_name: analytics_language_trend_repository_languages
classes:
- Analytics::LanguageTrend::RepositoryLanguage
feature_categories:
-- devops_reports
-description: TODO
+- source_code_management
+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'
diff --git a/db/docs/analytics_usage_trends_measurements.yml b/db/docs/analytics_usage_trends_measurements.yml
index 88e66fd7c98..1672c195e3d 100644
--- a/db/docs/analytics_usage_trends_measurements.yml
+++ b/db/docs/analytics_usage_trends_measurements.yml
@@ -4,6 +4,6 @@ classes:
- Analytics::UsageTrends::Measurement
feature_categories:
- devops_reports
-description: TODO
+description: Contains periodically snapshotted database record counts.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62797
milestone: '14.0'
diff --git a/db/docs/ci_partitions.yml b/db/docs/ci_partitions.yml
new file mode 100644
index 00000000000..8dfa31f05f9
--- /dev/null
+++ b/db/docs/ci_partitions.yml
@@ -0,0 +1,9 @@
+---
+table_name: ci_partitions
+classes:
+- Ci::Partition
+feature_categories:
+- continuous_integration
+description: Database partitioning metadata for CI tables
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96856
+milestone: '15.4'
diff --git a/db/docs/conversational_development_index_metrics.yml b/db/docs/conversational_development_index_metrics.yml
index fb96c376953..9371f9f1bfb 100644
--- a/db/docs/conversational_development_index_metrics.yml
+++ b/db/docs/conversational_development_index_metrics.yml
@@ -4,6 +4,6 @@ classes:
- DevOpsReport::Metric
feature_categories:
- devops_reports
-description: TODO
+description: Contains data for calculating DevOps score.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/26dde5f55f1dac2e6bea4f7e1dfa51c72dc756cb
milestone: '9.3'
diff --git a/db/docs/dora_configurations.yml b/db/docs/dora_configurations.yml
new file mode 100644
index 00000000000..e13cf088670
--- /dev/null
+++ b/db/docs/dora_configurations.yml
@@ -0,0 +1,9 @@
+---
+table_name: dora_configurations
+classes:
+- Dora::Configuration
+feature_categories:
+- continuous_delivery
+description: Stores project specific configurations for DORA4 calculations.
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96561
+milestone: '15.4'
diff --git a/db/docs/dora_daily_metrics.yml b/db/docs/dora_daily_metrics.yml
index 210af9f6472..09f2ad02bfe 100644
--- a/db/docs/dora_daily_metrics.yml
+++ b/db/docs/dora_daily_metrics.yml
@@ -3,7 +3,7 @@ table_name: dora_daily_metrics
classes:
- Dora::DailyMetrics
feature_categories:
-- value_stream_management
-description: TODO
+- continuous_delivery
+description: Stores daily snapshots of DORA4 metrics per environment.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55473
milestone: '13.10'
diff --git a/db/docs/events.yml b/db/docs/events.yml
index 9ad923a25c7..d766bc7cae3 100644
--- a/db/docs/events.yml
+++ b/db/docs/events.yml
@@ -4,7 +4,7 @@ classes:
- Event
- PushEvent
feature_categories:
-- value_stream_management
-description: TODO
+- users
+description: Stores user generated events.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/a847501fd2ffc1c4becc7d0d352d80168d9b3568
milestone: "<6.0"
diff --git a/db/docs/fork_networks.yml b/db/docs/fork_networks.yml
index f0978b3fcc4..51123405baf 100644
--- a/db/docs/fork_networks.yml
+++ b/db/docs/fork_networks.yml
@@ -3,7 +3,7 @@ table_name: fork_networks
classes:
- ForkNetwork
feature_categories:
-- devops_reports
-description: TODO
+- source_code_management
+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'
diff --git a/db/docs/ghost_user_migrations.yml b/db/docs/ghost_user_migrations.yml
new file mode 100644
index 00000000000..f4e69e71baa
--- /dev/null
+++ b/db/docs/ghost_user_migrations.yml
@@ -0,0 +1,9 @@
+---
+table_name: ghost_user_migrations
+classes:
+- GhostUserMigration
+feature_categories:
+- users
+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'
diff --git a/db/docs/insights.yml b/db/docs/insights.yml
index 988893208a7..2439f289340 100644
--- a/db/docs/insights.yml
+++ b/db/docs/insights.yml
@@ -4,6 +4,6 @@ classes:
- Insight
feature_categories:
- value_stream_management
-description: TODO
+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'
diff --git a/db/docs/issue_metrics.yml b/db/docs/issue_metrics.yml
index a451e649128..3d2055a155c 100644
--- a/db/docs/issue_metrics.yml
+++ b/db/docs/issue_metrics.yml
@@ -4,6 +4,6 @@ classes:
- Issue::Metrics
feature_categories:
- value_stream_management
-description: TODO
+description: Store various metrics for issues.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/516c838a1846d049814765afa85c28a3c14a5b9f
milestone: '8.12'
diff --git a/db/docs/merge_request_metrics.yml b/db/docs/merge_request_metrics.yml
index 5faf14d0875..0b166eee455 100644
--- a/db/docs/merge_request_metrics.yml
+++ b/db/docs/merge_request_metrics.yml
@@ -5,6 +5,6 @@ classes:
feature_categories:
- value_stream_management
- code_review
-description: Store various metrics for merge requests
+description: Store various metrics for merge requests.
introduced_by_url: https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/5986
milestone: '8.12'
diff --git a/db/docs/merge_request_predictions.yml b/db/docs/merge_request_predictions.yml
new file mode 100644
index 00000000000..7495f0934a4
--- /dev/null
+++ b/db/docs/merge_request_predictions.yml
@@ -0,0 +1,9 @@
+---
+table_name: merge_request_predictions
+classes:
+- MergeRequest::Prediction
+feature_categories:
+- workflow_automation
+description: Includes machine learning model predictions
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97622
+milestone: '15.4'
diff --git a/db/docs/onboarding_progresses.yml b/db/docs/onboarding_progresses.yml
index 4166b934570..80b70fe0b1f 100644
--- a/db/docs/onboarding_progresses.yml
+++ b/db/docs/onboarding_progresses.yml
@@ -1,7 +1,7 @@
---
table_name: onboarding_progresses
classes:
-- OnboardingProgress
+- Onboarding::Progress
feature_categories:
- onboarding
description: TODO
diff --git a/db/docs/packages_rpm_metadata.yml b/db/docs/packages_rpm_metadata.yml
new file mode 100644
index 00000000000..cd34529ff0c
--- /dev/null
+++ b/db/docs/packages_rpm_metadata.yml
@@ -0,0 +1,9 @@
+---
+table_name: packages_rpm_metadata
+classes:
+- Packages::Rpm::Metadatum
+feature_categories:
+- package_registry
+description: Rpm package metadata
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96019
+milestone: '15.4'
diff --git a/db/docs/protected_environment_deploy_access_levels.yml b/db/docs/protected_environment_deploy_access_levels.yml
index cd3bba9171f..c25044dc7e2 100644
--- a/db/docs/protected_environment_deploy_access_levels.yml
+++ b/db/docs/protected_environment_deploy_access_levels.yml
@@ -1,7 +1,7 @@
---
table_name: protected_environment_deploy_access_levels
classes:
-- ProtectedEnvironment::DeployAccessLevel
+- ProtectedEnvironments::DeployAccessLevel
feature_categories:
- continuous_delivery
description: https://docs.gitlab.com/ee/ci/environments/protected_environments.html
diff --git a/db/docs/sbom_vulnerable_component_versions.yml b/db/docs/sbom_vulnerable_component_versions.yml
new file mode 100644
index 00000000000..147af9c046b
--- /dev/null
+++ b/db/docs/sbom_vulnerable_component_versions.yml
@@ -0,0 +1,11 @@
+---
+table_name: sbom_vulnerable_component_versions
+classes:
+- Sbom::VulnerableComponentVersion
+feature_categories:
+- container_scanning
+- dependency_scanning
+- license_compliance
+description: Stores information about vulnerable SBoM components
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95622
+milestone: '15.4'
diff --git a/db/docs/spam_logs.yml b/db/docs/spam_logs.yml
index 35fa08ef46d..6e16b3600c8 100644
--- a/db/docs/spam_logs.yml
+++ b/db/docs/spam_logs.yml
@@ -3,7 +3,7 @@ table_name: spam_logs
classes:
- SpamLog
feature_categories:
-- authentication_and_authorization
-description: TODO
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/commit/d20e75a8d80c2828336cd22897ea6868d666f8a5
+- instance_resiliency
+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'
diff --git a/db/docs/vulnerability_advisories.yml b/db/docs/vulnerability_advisories.yml
new file mode 100644
index 00000000000..2c88be94a11
--- /dev/null
+++ b/db/docs/vulnerability_advisories.yml
@@ -0,0 +1,11 @@
+---
+table_name: vulnerability_advisories
+classes:
+- Vulnerabilities::Advisory
+feature_categories:
+- container_scanning
+- dependency_scanning
+- license_compliance
+description: Stores vulnerability advisories
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95622
+milestone: '15.4'
diff --git a/db/fixtures/development/044_add_security_training_providers.rb b/db/fixtures/development/044_add_security_training_providers.rb
new file mode 100644
index 00000000000..6fbd9f66127
--- /dev/null
+++ b/db/fixtures/development/044_add_security_training_providers.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+Gitlab::Seeder.quiet do
+ ::Gitlab::DatabaseImporters::Security::TrainingProviders::Importer.upsert_providers
+end
diff --git a/db/fixtures/development/14_pipelines.rb b/db/fixtures/development/14_pipelines.rb
index e8fa3ee2110..8f63ce3dbfe 100644
--- a/db/fixtures/development/14_pipelines.rb
+++ b/db/fixtures/development/14_pipelines.rb
@@ -60,7 +60,7 @@ class Gitlab::Seeder::Pipelines
::Ci::ProcessPipelineService.new(pipeline).execute
end
- ::Gitlab::Seeder::Ci::DailyBuildGroupReportResult.new(@project).seed if @project.last_pipeline
+ ::Gitlab::Seeders::Ci::DailyBuildGroupReportResult.new(@project).seed if @project.last_pipeline
end
private
@@ -129,7 +129,7 @@ class Gitlab::Seeder::Pipelines
end
def setup_artifacts(build)
- return unless build.stage == "build"
+ return unless build.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 +141,7 @@ class Gitlab::Seeder::Pipelines
end
def setup_test_reports(build)
- return unless build.stage == "test" && build.name == "rspec:osx"
+ return unless build.stage_name == "test" && build.name == "rspec:osx"
if build.ref == build.project.default_branch
artifacts_cache_file(test_reports_pass_path) do |file|
diff --git a/db/fixtures/development/17_cycle_analytics.rb b/db/fixtures/development/17_cycle_analytics.rb
index fa19775a571..8e9952e9ba1 100644
--- a/db/fixtures/development/17_cycle_analytics.rb
+++ b/db/fixtures/development/17_cycle_analytics.rb
@@ -154,6 +154,8 @@ class Gitlab::Seeder::CycleAnalytics
@developers << user
end
+
+ AuthorizedProjectUpdate::ProjectRecalculateService.new(project).execute
end
def create_new_vsm_project
diff --git a/db/fixtures/development/24_forks.rb b/db/fixtures/development/24_forks.rb
index 536d9f9e2ba..a3db84ab1b7 100644
--- a/db/fixtures/development/24_forks.rb
+++ b/db/fixtures/development/24_forks.rb
@@ -24,6 +24,8 @@ Sidekiq::Testing.inline! do
# the `after_commit` queue to ensure the job is run now.
fork_project.send(:_run_after_commit_queue)
fork_project.import_state.send(:_run_after_commit_queue)
+ # We should also force-run the project authorizations refresh job for the created project.
+ AuthorizedProjectUpdate::ProjectRecalculateService.new(fork_project).execute
# Expire repository cache after import to ensure
# valid_repo? call below returns a correct answer
diff --git a/db/fixtures/development/33_triage_ops.rb b/db/fixtures/development/33_triage_ops.rb
index b2dd3861169..14832ee4af9 100644
--- a/db/fixtures/development/33_triage_ops.rb
+++ b/db/fixtures/development/33_triage_ops.rb
@@ -7,6 +7,42 @@ class Gitlab::Seeder::TriageOps
WEBHOOK_URL = 'http://0.0.0.0:$PORT$'
WEBHOOK_TOKEN = "triage-ops-webhook-token"
+ WORK_TYPE_LABELS = %w(
+ bug::availability
+ bug::mobile
+ bug::performance
+ bug::vulnerability
+ feature::addition
+ feature::consolidation
+ feature::enhancement
+ feature::removal
+ maintenance::dependency
+ maintenance::pipelines
+ maintenance::refactor
+ maintenance::test-gap
+ maintenance::usability
+ maintenance::workflow
+ type::bug
+ type::feature
+ type::maintenance
+ )
+
+ WORKFLOW_LABELS = %w(
+ workflow::blocked
+ workflow::design
+ workflow::in dev
+ workflow::in review
+ workflow::planning breakdown
+ workflow::production
+ workflow::ready
+ workflow::ready for design
+ workflow::ready for development
+ workflow::ready for review
+ workflow::refinement
+ workflow::validation backlog
+ workflow::verification
+ )
+
def seed!
puts "Updating settings to allow web hooks to localhost"
ApplicationSetting.current_without_cache.update!(allow_local_requests_from_web_hooks_and_services: true)
@@ -33,8 +69,12 @@ class Gitlab::Seeder::TriageOps
ensure_webhook_for('gitlab-org')
puts "Ensuring work type labels"
- ensure_work_type_labels_for('gitlab-com')
- ensure_work_type_labels_for('gitlab-org')
+ ensure_labels_for(WORK_TYPE_LABELS, 'gitlab-com')
+ ensure_labels_for(WORK_TYPE_LABELS, 'gitlab-org')
+
+ puts "Ensuring workflow type labels"
+ ensure_labels_for(WORKFLOW_LABELS, 'gitlab-com')
+ ensure_labels_for(WORKFLOW_LABELS, 'gitlab-org')
end
end
end
@@ -89,27 +129,7 @@ class Gitlab::Seeder::TriageOps
puts "Hook with url '#{hook.url}' and token '#{hook.token}' for '#{group_path}' is present now."
end
- def ensure_work_type_labels_for(group_path)
- label_titles = [
- 'bug::availability',
- 'bug::mobile',
- 'bug::performance',
- 'bug::vulnerability',
- 'feature::addition',
- 'feature::consolidation',
- 'feature::enhancement',
- 'feature::removal',
- 'maintenance::dependency',
- 'maintenance::pipelines',
- 'maintenance::refactor',
- 'maintenance::test-gap',
- 'maintenance::usability',
- 'maintenance::workflow',
- 'type::bug',
- 'type::feature',
- 'type::maintenance',
- ]
-
+ def ensure_labels_for(label_titles, group_path)
group = Group.find_by_full_path(group_path)
label_titles.each do |label_title|
diff --git a/db/fixtures/development/98_gitlab_instance_administration_project.rb b/db/fixtures/development/98_gitlab_instance_administration_project.rb
index 9f50ce6a7e4..3338f2bd2fc 100644
--- a/db/fixtures/development/98_gitlab_instance_administration_project.rb
+++ b/db/fixtures/development/98_gitlab_instance_administration_project.rb
@@ -1,7 +1,10 @@
# frozen_string_literal: true
response = Sidekiq::Worker.skipping_transaction_check do
- ::Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService.new.execute
+ result = ::Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService.new.execute
+ AuthorizedProjectUpdate::ProjectRecalculateService.new(result[:project]).execute
+
+ result
end
if response[:status] == :success
diff --git a/db/fixtures/production/004_add_security_training_providers.rb b/db/fixtures/production/004_add_security_training_providers.rb
new file mode 100644
index 00000000000..6fbd9f66127
--- /dev/null
+++ b/db/fixtures/production/004_add_security_training_providers.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+Gitlab::Seeder.quiet do
+ ::Gitlab::DatabaseImporters::Security::TrainingProviders::Importer.upsert_providers
+end
diff --git a/db/migrate/20220406193806_add_maven_package_requests_forwarding_to_application_settings.rb b/db/migrate/20220406193806_add_maven_package_requests_forwarding_to_application_settings.rb
new file mode 100644
index 00000000000..60b2efd3e9c
--- /dev/null
+++ b/db/migrate/20220406193806_add_maven_package_requests_forwarding_to_application_settings.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class AddMavenPackageRequestsForwardingToApplicationSettings < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def up
+ add_column(:application_settings, :maven_package_requests_forwarding, :boolean, default: true, null: false)
+ end
+
+ def down
+ remove_column(:application_settings, :maven_package_requests_forwarding)
+ end
+end
diff --git a/db/migrate/20220603125200_add_show_diff_preview_in_email_to_namespace_settings.rb b/db/migrate/20220603125200_add_show_diff_preview_in_email_to_namespace_settings.rb
new file mode 100644
index 00000000000..ad32d589840
--- /dev/null
+++ b/db/migrate/20220603125200_add_show_diff_preview_in_email_to_namespace_settings.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddShowDiffPreviewInEmailToNamespaceSettings < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :namespace_settings, :show_diff_preview_in_email, :boolean, default: true, null: false
+ end
+end
diff --git a/db/migrate/20220711142148_add_invitation_flow_enforcement_to_application_settings.rb b/db/migrate/20220711142148_add_invitation_flow_enforcement_to_application_settings.rb
new file mode 100644
index 00000000000..f323faf9aa1
--- /dev/null
+++ b/db/migrate/20220711142148_add_invitation_flow_enforcement_to_application_settings.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class AddInvitationFlowEnforcementToApplicationSettings < Gitlab::Database::Migration[2.0]
+ def change
+ add_column :application_settings, :invitation_flow_enforcement,
+ :boolean,
+ default: false,
+ null: false
+ end
+end
diff --git a/db/migrate/20220726171440_create_ghost_user_migrations.rb b/db/migrate/20220726171440_create_ghost_user_migrations.rb
new file mode 100644
index 00000000000..c64ca4f7765
--- /dev/null
+++ b/db/migrate/20220726171440_create_ghost_user_migrations.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+class CreateGhostUserMigrations < Gitlab::Database::Migration[2.0]
+ def change
+ create_table :ghost_user_migrations do |t|
+ t.bigint :user_id, index: { unique: true }, null: false
+ t.bigint :initiator_user_id
+ t.timestamps_with_timezone null: false
+ t.boolean :hard_delete, default: false, null: false
+ end
+ end
+end
diff --git a/db/migrate/20220726171450_add_user_fk_to_ghost_user_migrations.rb b/db/migrate/20220726171450_add_user_fk_to_ghost_user_migrations.rb
new file mode 100644
index 00000000000..5904d132d1b
--- /dev/null
+++ b/db/migrate/20220726171450_add_user_fk_to_ghost_user_migrations.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddUserFkToGhostUserMigrations < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :ghost_user_migrations, :users, column: :user_id, on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key :ghost_user_migrations, column: :user_id
+ end
+ end
+end
diff --git a/db/migrate/20220803004853_add_auto_ban_user_to_namespace_settings.rb b/db/migrate/20220803004853_add_auto_ban_user_to_namespace_settings.rb
new file mode 100644
index 00000000000..6e03177ce7a
--- /dev/null
+++ b/db/migrate/20220803004853_add_auto_ban_user_to_namespace_settings.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class AddAutoBanUserToNamespaceSettings < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :namespace_settings, :auto_ban_user_on_excessive_projects_download, :boolean,
+ default: false, null: false
+ end
+end
diff --git a/db/migrate/20220803235114_add_auto_ban_user_to_application_settings.rb b/db/migrate/20220803235114_add_auto_ban_user_to_application_settings.rb
new file mode 100644
index 00000000000..3b57c340921
--- /dev/null
+++ b/db/migrate/20220803235114_add_auto_ban_user_to_application_settings.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+class AddAutoBanUserToApplicationSettings < Gitlab::Database::Migration[2.0]
+ def change
+ add_column :application_settings, :auto_ban_user_on_excessive_projects_download, :boolean,
+ default: false, null: false
+ end
+end
diff --git a/db/migrate/20220815152905_create_vulnerability_advisories.rb b/db/migrate/20220815152905_create_vulnerability_advisories.rb
new file mode 100644
index 00000000000..f01a17c6352
--- /dev/null
+++ b/db/migrate/20220815152905_create_vulnerability_advisories.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class CreateVulnerabilityAdvisories < Gitlab::Database::Migration[2.0]
+ def change
+ create_table :vulnerability_advisories, id: false do |t|
+ t.uuid :uuid, null: false
+ t.timestamps_with_timezone null: false
+ t.primary_key :id
+ t.date :created_date, null: false
+ t.date :published_date, null: false
+ t.text :description, limit: 2048
+ t.text :title, limit: 2048
+ t.text :component_name, limit: 2048
+ t.text :solution, limit: 2048
+ t.text :not_impacted, limit: 2048
+ t.text :cvss_v2, limit: 128
+ t.text :cvss_v3, limit: 128
+ t.text :affected_range, limit: 32
+ t.text :identifiers, array: true, default: []
+ t.text :fixed_versions, array: true, default: []
+ t.text :urls, array: true, default: []
+ t.text :links, array: true, default: []
+ end
+ end
+end
diff --git a/db/migrate/20220816135816_create_sbom_vulnerable_component_versions.rb b/db/migrate/20220816135816_create_sbom_vulnerable_component_versions.rb
new file mode 100644
index 00000000000..6f50376f6fa
--- /dev/null
+++ b/db/migrate/20220816135816_create_sbom_vulnerable_component_versions.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class CreateSbomVulnerableComponentVersions < Gitlab::Database::Migration[2.0]
+ ADVISORY_INDEX_NAME = "index_vulnerable_component_versions_on_vulnerability_advisory"
+ SBOM_COMPONENT_INDEX_NAME = "index_vulnerable_component_versions_on_sbom_component_version"
+
+ def change
+ create_table :sbom_vulnerable_component_versions do |t|
+ t.references :vulnerability_advisory,
+ index: { name: ADVISORY_INDEX_NAME }
+
+ t.references :sbom_component_version,
+ index: { name: SBOM_COMPONENT_INDEX_NAME }
+
+ t.timestamps_with_timezone null: false
+ end
+ end
+end
diff --git a/db/migrate/20220817122907_re_add_show_diff_preview_in_email_to_project_settings.rb b/db/migrate/20220817122907_re_add_show_diff_preview_in_email_to_project_settings.rb
new file mode 100644
index 00000000000..bb5649e3a99
--- /dev/null
+++ b/db/migrate/20220817122907_re_add_show_diff_preview_in_email_to_project_settings.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class ReAddShowDiffPreviewInEmailToProjectSettings < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :project_settings, :show_diff_preview_in_email, :boolean, default: true, null: false
+ end
+end
diff --git a/db/migrate/20220818095225_add_max_pages_custom_domains_per_project.rb b/db/migrate/20220818095225_add_max_pages_custom_domains_per_project.rb
new file mode 100644
index 00000000000..c5e1f5aede6
--- /dev/null
+++ b/db/migrate/20220818095225_add_max_pages_custom_domains_per_project.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class AddMaxPagesCustomDomainsPerProject < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ CONSTRAINT_NAME = "app_settings_max_pages_custom_domains_per_project_check"
+
+ def up
+ return if column_exists?(:application_settings, :max_pages_custom_domains_per_project)
+
+ add_column :application_settings, :max_pages_custom_domains_per_project, :integer, null: false, default: 0
+ add_check_constraint :application_settings, "max_pages_custom_domains_per_project >= 0", CONSTRAINT_NAME
+ end
+
+ def down
+ return unless column_exists?(:application_settings, :max_pages_custom_domains_per_project)
+
+ remove_column :application_settings, :max_pages_custom_domains_per_project
+ end
+end
diff --git a/db/migrate/20220818132108_add_deleted_on_to_ml_experiments.rb b/db/migrate/20220818132108_add_deleted_on_to_ml_experiments.rb
new file mode 100644
index 00000000000..e6ba9f78553
--- /dev/null
+++ b/db/migrate/20220818132108_add_deleted_on_to_ml_experiments.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddDeletedOnToMlExperiments < Gitlab::Database::Migration[2.0]
+ def change
+ add_column :ml_experiments, :deleted_on, :datetime_with_timezone, index: true
+ end
+end
diff --git a/db/migrate/20220819153725_add_vulnerability_advisory_foreign_key_to_sbom_vulnerable_component_versions.rb b/db/migrate/20220819153725_add_vulnerability_advisory_foreign_key_to_sbom_vulnerable_component_versions.rb
new file mode 100644
index 00000000000..7f0c817875f
--- /dev/null
+++ b/db/migrate/20220819153725_add_vulnerability_advisory_foreign_key_to_sbom_vulnerable_component_versions.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddVulnerabilityAdvisoryForeignKeyToSbomVulnerableComponentVersions < Gitlab::Database::Migration[2.0]
+ SOURCE_TABLE = :sbom_vulnerable_component_versions
+ TARGET_TABLE = :vulnerability_advisories
+ COLUMN = :vulnerability_advisory_id
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key SOURCE_TABLE, TARGET_TABLE, column: COLUMN, on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key SOURCE_TABLE, column: COLUMN
+ end
+ end
+end
diff --git a/db/migrate/20220819162852_add_sbom_component_version_foreign_key_to_sbom_vulnerable_component_versions.rb b/db/migrate/20220819162852_add_sbom_component_version_foreign_key_to_sbom_vulnerable_component_versions.rb
new file mode 100644
index 00000000000..a4f2005693c
--- /dev/null
+++ b/db/migrate/20220819162852_add_sbom_component_version_foreign_key_to_sbom_vulnerable_component_versions.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddSbomComponentVersionForeignKeyToSbomVulnerableComponentVersions < Gitlab::Database::Migration[2.0]
+ SOURCE_TABLE = :sbom_vulnerable_component_versions
+ TARGET_TABLE = :sbom_component_versions
+ COLUMN = :sbom_component_version_id
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key SOURCE_TABLE, TARGET_TABLE, column: COLUMN, on_delete: :cascade
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key SOURCE_TABLE, column: COLUMN
+ end
+ end
+end
diff --git a/db/migrate/20220822102651_add_namespace_id_to_broadcast_message.rb b/db/migrate/20220822102651_add_namespace_id_to_broadcast_message.rb
new file mode 100644
index 00000000000..5413f447366
--- /dev/null
+++ b/db/migrate/20220822102651_add_namespace_id_to_broadcast_message.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddNamespaceIdToBroadcastMessage < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :broadcast_messages, :namespace_id, :bigint
+ end
+end
diff --git a/db/migrate/20220822103638_add_index_and_foreign_key_to_broadcast_message.rb b/db/migrate/20220822103638_add_index_and_foreign_key_to_broadcast_message.rb
new file mode 100644
index 00000000000..a577e5ad157
--- /dev/null
+++ b/db/migrate/20220822103638_add_index_and_foreign_key_to_broadcast_message.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexAndForeignKeyToBroadcastMessage < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_broadcast_messages_on_namespace_id'
+
+ def up
+ add_concurrent_index :broadcast_messages, :namespace_id, name: INDEX_NAME
+ add_concurrent_foreign_key :broadcast_messages, :namespaces, column: :namespace_id, on_delete: :cascade
+ end
+
+ def down
+ remove_foreign_key_if_exists :broadcast_messages, column: :namespace_id
+ remove_concurrent_index_by_name :broadcast_messages, name: INDEX_NAME
+ end
+end
diff --git a/db/migrate/20220824175648_limit_namespaces_sync_triggers_to_traversal_ids_update.rb b/db/migrate/20220824175648_limit_namespaces_sync_triggers_to_traversal_ids_update.rb
new file mode 100644
index 00000000000..142744b5493
--- /dev/null
+++ b/db/migrate/20220824175648_limit_namespaces_sync_triggers_to_traversal_ids_update.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+class LimitNamespacesSyncTriggersToTraversalIdsUpdate < Gitlab::Database::Migration[2.0]
+ include Gitlab::Database::SchemaHelpers
+
+ enable_lock_retries!
+
+ TABLE_NAME = 'namespaces'
+ EVENT_TABLE_NAME = 'namespaces_sync_events'
+ FUNCTION_NAME = 'insert_namespaces_sync_event'
+ OLD_TRIGGER_ON_INSERT = 'trigger_namespaces_parent_id_on_insert'
+ OLD_TRIGGER_ON_UPDATE = 'trigger_namespaces_parent_id_on_update'
+ NEW_TRIGGER_ON_UPDATE = 'trigger_namespaces_traversal_ids_on_update'
+
+ def up
+ create_trigger(TABLE_NAME, NEW_TRIGGER_ON_UPDATE, FUNCTION_NAME, fires: 'AFTER UPDATE') do
+ <<~SQL
+ WHEN (OLD.traversal_ids IS DISTINCT FROM NEW.traversal_ids)
+ SQL
+ end
+ drop_trigger(TABLE_NAME, OLD_TRIGGER_ON_UPDATE)
+ drop_trigger(TABLE_NAME, OLD_TRIGGER_ON_INSERT)
+ end
+
+ # Revert both triggers to the version defined in db/migrate/20211011141242_create_namespaces_sync_trigger.rb
+ def down
+ create_trigger(TABLE_NAME, OLD_TRIGGER_ON_INSERT, FUNCTION_NAME, fires: 'AFTER INSERT')
+ create_trigger(TABLE_NAME, OLD_TRIGGER_ON_UPDATE, FUNCTION_NAME, fires: 'AFTER UPDATE') do
+ <<~SQL
+ WHEN (OLD.parent_id IS DISTINCT FROM NEW.parent_id)
+ SQL
+ end
+ drop_trigger(TABLE_NAME, NEW_TRIGGER_ON_UPDATE)
+ end
+end
diff --git a/db/migrate/20220824194103_remove_existing_work_item_type_backfill_migrations.rb b/db/migrate/20220824194103_remove_existing_work_item_type_backfill_migrations.rb
new file mode 100644
index 00000000000..ed970e30099
--- /dev/null
+++ b/db/migrate/20220824194103_remove_existing_work_item_type_backfill_migrations.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class RemoveExistingWorkItemTypeBackfillMigrations < Gitlab::Database::Migration[2.0]
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ JOB_CLASS_NAME = 'BackfillWorkItemTypeIdForIssues'
+
+ class BatchedMigration < MigrationRecord
+ self.table_name = 'batched_background_migrations'
+ end
+
+ def up
+ # cleaning up so we can remove a custom batching strategy that is no longer necessary
+ # some environments might already have this background migrations scheduled and probably completed
+ BatchedMigration.where(job_class_name: JOB_CLASS_NAME).delete_all
+ end
+
+ def down
+ # no-op
+ # we will reschedule this migration in the future, no need to add back here
+ end
+end
diff --git a/db/migrate/20220825105631_add_cube_api_key_to_application_settings.rb b/db/migrate/20220825105631_add_cube_api_key_to_application_settings.rb
new file mode 100644
index 00000000000..6ee5ad52315
--- /dev/null
+++ b/db/migrate/20220825105631_add_cube_api_key_to_application_settings.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class AddCubeApiKeyToApplicationSettings < Gitlab::Database::Migration[2.0]
+ def change
+ # rubocop:disable Migration/AddLimitToTextColumns
+ add_column :application_settings, :cube_api_base_url, :text
+ add_column :application_settings, :encrypted_cube_api_key, :binary
+ add_column :application_settings, :encrypted_cube_api_key_iv, :binary
+ # rubocop:enable Migration/AddLimitToTextColumns
+ end
+end
diff --git a/db/migrate/20220825134827_remove_not_null_constraint_for_confidence_columns.rb b/db/migrate/20220825134827_remove_not_null_constraint_for_confidence_columns.rb
new file mode 100644
index 00000000000..e41d93ebf8b
--- /dev/null
+++ b/db/migrate/20220825134827_remove_not_null_constraint_for_confidence_columns.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class RemoveNotNullConstraintForConfidenceColumns < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ change_column_null :vulnerabilities, :confidence, true
+ change_column_null :vulnerability_occurrences, :confidence, true
+ change_column_null :security_findings, :confidence, true
+ end
+
+ def down
+ # no-op: We can not set `NOT NULL` constraint here as there can be NULL values already.
+ end
+end
diff --git a/db/migrate/20220828094411_add_rpm_max_file_size_to_plan_limits.rb b/db/migrate/20220828094411_add_rpm_max_file_size_to_plan_limits.rb
new file mode 100644
index 00000000000..32489fd5bce
--- /dev/null
+++ b/db/migrate/20220828094411_add_rpm_max_file_size_to_plan_limits.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddRpmMaxFileSizeToPlanLimits < Gitlab::Database::Migration[2.0]
+ DOWNTIME = false
+
+ def change
+ add_column :plan_limits, :rpm_max_file_size, :bigint, default: 5.gigabytes, null: false
+ end
+end
diff --git a/db/migrate/20220828131848_create_packages_rpm_metadata.rb b/db/migrate/20220828131848_create_packages_rpm_metadata.rb
new file mode 100644
index 00000000000..bfaa441fc78
--- /dev/null
+++ b/db/migrate/20220828131848_create_packages_rpm_metadata.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+class CreatePackagesRpmMetadata < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ with_lock_retries do
+ create_table :packages_rpm_metadata, id: false do |t|
+ t.references :package,
+ primary_key: true,
+ default: nil,
+ index: true,
+ foreign_key: { to_table: :packages_packages, on_delete: :cascade },
+ type: :bigint
+ t.text :release, default: '1', null: false, limit: 128
+ t.text :summary, default: '', null: false, limit: 1000
+ t.text :description, default: '', null: false, limit: 5000
+ t.text :arch, default: '', null: false, limit: 255
+ t.text :license, null: true, limit: 1000
+ t.text :url, null: true, limit: 1000
+ end
+ end
+ end
+
+ def down
+ with_lock_retries do
+ drop_table :packages_rpm_metadata
+ end
+ end
+end
diff --git a/db/migrate/20220829183356_replace_index_on_credit_card_validations.rb b/db/migrate/20220829183356_replace_index_on_credit_card_validations.rb
new file mode 100644
index 00000000000..05fa7f75feb
--- /dev/null
+++ b/db/migrate/20220829183356_replace_index_on_credit_card_validations.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class ReplaceIndexOnCreditCardValidations < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ OLD_INDEX_NAME = 'index_user_credit_card_validations_meta_data_full_match'
+ NEW_INDEX_NAME = 'index_user_credit_card_validations_meta_data_full_match_lower'
+ OLD_FIELDS = [:holder_name, :expiration_date, :last_digits, :credit_card_validated_at]
+ NEW_FIELDS = 'lower(holder_name), expiration_date, last_digits, credit_card_validated_at'
+
+ def up
+ add_concurrent_index :user_credit_card_validations, NEW_FIELDS, name: NEW_INDEX_NAME
+ remove_concurrent_index :user_credit_card_validations, OLD_FIELDS, name: OLD_INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :user_credit_card_validations, OLD_FIELDS, name: OLD_INDEX_NAME
+ remove_concurrent_index :user_credit_card_validations, NEW_FIELDS, name: NEW_INDEX_NAME
+ end
+end
diff --git a/db/migrate/20220830082928_add_text_limit_to_cube_api_base_url.rb b/db/migrate/20220830082928_add_text_limit_to_cube_api_base_url.rb
new file mode 100644
index 00000000000..7bab796a47f
--- /dev/null
+++ b/db/migrate/20220830082928_add_text_limit_to_cube_api_base_url.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class AddTextLimitToCubeApiBaseUrl < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ add_text_limit :application_settings, :cube_api_base_url, 512
+ end
+
+ def down
+ remove_text_limit :application_settings, :cube_api_base_url
+ end
+end
diff --git a/db/migrate/20220830114228_create_dora_configuration_table.rb b/db/migrate/20220830114228_create_dora_configuration_table.rb
new file mode 100644
index 00000000000..ee5960d14b6
--- /dev/null
+++ b/db/migrate/20220830114228_create_dora_configuration_table.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class CreateDoraConfigurationTable < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ create_table :dora_configurations do |t|
+ t.references :project, null: false, index: { unique: true }, foreign_key: { on_delete: :cascade }
+ t.text :branches_for_lead_time_for_changes, null: false, array: true, default: []
+ end
+ end
+
+ def down
+ drop_table :dora_configurations
+ end
+end
diff --git a/db/migrate/20220831182105_add_constraints_view.rb b/db/migrate/20220831182105_add_constraints_view.rb
new file mode 100644
index 00000000000..03c183b6e9f
--- /dev/null
+++ b/db/migrate/20220831182105_add_constraints_view.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+class AddConstraintsView < Gitlab::Database::Migration[2.0]
+ def up
+ execute(<<~SQL)
+ CREATE OR REPLACE VIEW postgres_constraints
+ AS
+ SELECT
+ pg_constraint.oid AS oid,
+ pg_constraint.conname AS name,
+ pg_constraint.contype AS constraint_type,
+ pg_constraint.convalidated AS constraint_valid,
+ (SELECT array_agg(attname ORDER BY ordering)
+ FROM unnest(pg_constraint.conkey) WITH ORDINALITY attnums(attnum, ordering)
+ INNER JOIN pg_attribute ON pg_attribute.attnum = attnums.attnum AND pg_attribute.attrelid = pg_class.oid
+ ) AS column_names,
+ pg_namespace.nspname::text || '.'::text || pg_class.relname::text AS table_identifier,
+ -- pg_constraint reports a 0 oid rather than null if the constraint is not a partition child constraint.
+ nullif(pg_constraint.conparentid, 0) AS parent_constraint_oid,
+ pg_get_constraintdef(pg_constraint.oid) AS definition
+ FROM pg_constraint
+ INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid
+ INNER JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid;
+ SQL
+ end
+
+ def down
+ execute(<<~SQL)
+ DROP VIEW postgres_constraints;
+ SQL
+ end
+end
diff --git a/db/migrate/20220901035722_add_temp_project_member_index.rb b/db/migrate/20220901035722_add_temp_project_member_index.rb
new file mode 100644
index 00000000000..0765ef09b5c
--- /dev/null
+++ b/db/migrate/20220901035722_add_temp_project_member_index.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddTempProjectMemberIndex < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ TABLE_NAME = :members
+ INDEX_NAME = 'index_project_members_on_id_temp'
+
+ def up
+ add_concurrent_index TABLE_NAME, :id, name: INDEX_NAME, where: "source_type = 'Project'"
+ end
+
+ def down
+ remove_concurrent_index TABLE_NAME, :id, name: INDEX_NAME
+ end
+end
diff --git a/db/migrate/20220901124637_add_last_downloaded_at_to_packages.rb b/db/migrate/20220901124637_add_last_downloaded_at_to_packages.rb
new file mode 100644
index 00000000000..0172ab573ea
--- /dev/null
+++ b/db/migrate/20220901124637_add_last_downloaded_at_to_packages.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddLastDownloadedAtToPackages < Gitlab::Database::Migration[2.0]
+ def change
+ add_column :packages_packages, :last_downloaded_at, :datetime_with_timezone
+ end
+end
diff --git a/db/migrate/20220901131828_add_environments_project_name_lower_pattern_ops_index.rb b/db/migrate/20220901131828_add_environments_project_name_lower_pattern_ops_index.rb
new file mode 100644
index 00000000000..d8b7cd9be28
--- /dev/null
+++ b/db/migrate/20220901131828_add_environments_project_name_lower_pattern_ops_index.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddEnvironmentsProjectNameLowerPatternOpsIndex < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_environments_on_project_name_varchar_pattern_ops'
+
+ def up
+ add_concurrent_index :environments, 'project_id, lower(name) varchar_pattern_ops', name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :environments, INDEX_NAME
+ end
+end
diff --git a/db/migrate/20220901212027_add_merge_request_id_to_environments.rb b/db/migrate/20220901212027_add_merge_request_id_to_environments.rb
new file mode 100644
index 00000000000..4a230c737ae
--- /dev/null
+++ b/db/migrate/20220901212027_add_merge_request_id_to_environments.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddMergeRequestIdToEnvironments < Gitlab::Database::Migration[2.0]
+ def change
+ add_column :environments, :merge_request_id, :bigint
+ end
+end
diff --git a/db/migrate/20220902065314_create_ci_partitions.rb b/db/migrate/20220902065314_create_ci_partitions.rb
new file mode 100644
index 00000000000..1a8a4f172f8
--- /dev/null
+++ b/db/migrate/20220902065314_create_ci_partitions.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class CreateCiPartitions < Gitlab::Database::Migration[2.0]
+ def change
+ create_table :ci_partitions do |t|
+ t.timestamps_with_timezone null: false
+ end
+ end
+end
diff --git a/db/migrate/20220902065316_create_default_partition_record.rb b/db/migrate/20220902065316_create_default_partition_record.rb
new file mode 100644
index 00000000000..6493fb23d4c
--- /dev/null
+++ b/db/migrate/20220902065316_create_default_partition_record.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class CreateDefaultPartitionRecord < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+ restrict_gitlab_migration gitlab_schema: :gitlab_ci
+
+ def up
+ execute(<<~SQL)
+ INSERT INTO "ci_partitions" ("id", "created_at", "updated_at")
+ VALUES (100, now(), now());
+ SQL
+
+ reset_pk_sequence!('ci_partitions')
+ end
+
+ def down
+ execute(<<~SQL)
+ DELETE FROM "ci_partitions" WHERE "ci_partitions"."id" = 100;
+ SQL
+ end
+end
diff --git a/db/migrate/20220902065317_add_partition_id_to_ci_builds.rb b/db/migrate/20220902065317_add_partition_id_to_ci_builds.rb
new file mode 100644
index 00000000000..6257164b44e
--- /dev/null
+++ b/db/migrate/20220902065317_add_partition_id_to_ci_builds.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiBuilds < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ # rubocop:disable Migration/AddColumnsToWideTables
+ def change
+ add_column :ci_builds, :partition_id, :bigint, default: 100, null: false
+ end
+ # rubocop:enable Migration/AddColumnsToWideTables
+end
diff --git a/db/migrate/20220902065558_add_partition_id_to_ci_builds_metadata.rb b/db/migrate/20220902065558_add_partition_id_to_ci_builds_metadata.rb
new file mode 100644
index 00000000000..e04ea99539f
--- /dev/null
+++ b/db/migrate/20220902065558_add_partition_id_to_ci_builds_metadata.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiBuildsMetadata < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_builds_metadata, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20220902065611_add_partition_id_to_ci_job_artifacts.rb b/db/migrate/20220902065611_add_partition_id_to_ci_job_artifacts.rb
new file mode 100644
index 00000000000..1d9eeb0330e
--- /dev/null
+++ b/db/migrate/20220902065611_add_partition_id_to_ci_job_artifacts.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiJobArtifacts < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_job_artifacts, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20220902065623_add_partition_id_to_ci_pipelines.rb b/db/migrate/20220902065623_add_partition_id_to_ci_pipelines.rb
new file mode 100644
index 00000000000..bb3e7c27ee8
--- /dev/null
+++ b/db/migrate/20220902065623_add_partition_id_to_ci_pipelines.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiPipelines < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_pipelines, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20220902065635_add_partition_id_to_ci_stages.rb b/db/migrate/20220902065635_add_partition_id_to_ci_stages.rb
new file mode 100644
index 00000000000..0ddbf491ee9
--- /dev/null
+++ b/db/migrate/20220902065635_add_partition_id_to_ci_stages.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiStages < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_stages, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20220902065647_add_partition_id_to_ci_pipeline_variables.rb b/db/migrate/20220902065647_add_partition_id_to_ci_pipeline_variables.rb
new file mode 100644
index 00000000000..14f17b371b4
--- /dev/null
+++ b/db/migrate/20220902065647_add_partition_id_to_ci_pipeline_variables.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddPartitionIdToCiPipelineVariables < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column :ci_pipeline_variables, :partition_id, :bigint, default: 100, null: false
+ end
+end
diff --git a/db/migrate/20220902165931_index_evironments_on_merge_request_id.rb b/db/migrate/20220902165931_index_evironments_on_merge_request_id.rb
new file mode 100644
index 00000000000..9d9f84e94ee
--- /dev/null
+++ b/db/migrate/20220902165931_index_evironments_on_merge_request_id.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class IndexEvironmentsOnMergeRequestId < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = 'index_environments_on_merge_request_id'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :environments, :merge_request_id, name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :environments, INDEX_NAME
+ end
+end
diff --git a/db/migrate/20220902170131_add_fk_constraint_to_environments_merge_request_id.rb b/db/migrate/20220902170131_add_fk_constraint_to_environments_merge_request_id.rb
new file mode 100644
index 00000000000..863aefe56fb
--- /dev/null
+++ b/db/migrate/20220902170131_add_fk_constraint_to_environments_merge_request_id.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class AddFkConstraintToEnvironmentsMergeRequestId < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key :environments, :merge_requests, column: :merge_request_id, on_delete: :nullify
+ end
+
+ def down
+ remove_foreign_key_if_exists :environments, column: :merge_request_id
+ end
+end
diff --git a/db/migrate/20220906093857_add_column_branch_filter_strategy_to_web_hooks.rb b/db/migrate/20220906093857_add_column_branch_filter_strategy_to_web_hooks.rb
new file mode 100644
index 00000000000..739bedda9e0
--- /dev/null
+++ b/db/migrate/20220906093857_add_column_branch_filter_strategy_to_web_hooks.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddColumnBranchFilterStrategyToWebHooks < Gitlab::Database::Migration[2.0]
+ def change
+ add_column :web_hooks, :branch_filter_strategy, :integer, null: false, default: 0, limit: 2
+ end
+end
diff --git a/db/migrate/20220906155105_add_start_time_and_end_time_and_status_to_ml_candidates.rb b/db/migrate/20220906155105_add_start_time_and_end_time_and_status_to_ml_candidates.rb
new file mode 100644
index 00000000000..68138881139
--- /dev/null
+++ b/db/migrate/20220906155105_add_start_time_and_end_time_and_status_to_ml_candidates.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddStartTimeAndEndTimeAndStatusToMlCandidates < Gitlab::Database::Migration[2.0]
+ def change
+ add_column :ml_candidates, :start_time, :bigint
+ add_column :ml_candidates, :end_time, :bigint
+ add_column :ml_candidates, :status, :smallint, default: 0, null: false
+ end
+end
diff --git a/db/migrate/20220906204832_add_locked_to_ci_pipeline_artifacts.rb b/db/migrate/20220906204832_add_locked_to_ci_pipeline_artifacts.rb
new file mode 100644
index 00000000000..dae4c560bb2
--- /dev/null
+++ b/db/migrate/20220906204832_add_locked_to_ci_pipeline_artifacts.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class AddLockedToCiPipelineArtifacts < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ TABLE_NAME = 'ci_pipeline_artifacts'
+ COLUMN_NAME = 'locked'
+
+ def up
+ with_lock_retries do
+ add_column TABLE_NAME, COLUMN_NAME, :smallint, default: 2
+ end
+ end
+
+ def down
+ with_lock_retries do
+ remove_column TABLE_NAME, COLUMN_NAME
+ end
+ end
+end
diff --git a/db/migrate/20220907124320_add_internal_to_notes.rb b/db/migrate/20220907124320_add_internal_to_notes.rb
new file mode 100644
index 00000000000..081914f7830
--- /dev/null
+++ b/db/migrate/20220907124320_add_internal_to_notes.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddInternalToNotes < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ add_column(:notes, :internal, :boolean, default: false, null: false)
+ end
+end
diff --git a/db/migrate/20220909091410_add_dismissal_reason_to_vulnerability_state_transitions.rb b/db/migrate/20220909091410_add_dismissal_reason_to_vulnerability_state_transitions.rb
new file mode 100644
index 00000000000..01fcb3aa6e1
--- /dev/null
+++ b/db/migrate/20220909091410_add_dismissal_reason_to_vulnerability_state_transitions.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddDismissalReasonToVulnerabilityStateTransitions < Gitlab::Database::Migration[2.0]
+ def change
+ add_column :vulnerability_state_transitions, :dismissal_reason, :smallint
+ end
+end
diff --git a/db/migrate/20220909094752_add_free_user_cap_over_limt_notified_at_to_namespace_details.rb b/db/migrate/20220909094752_add_free_user_cap_over_limt_notified_at_to_namespace_details.rb
new file mode 100644
index 00000000000..775705eae73
--- /dev/null
+++ b/db/migrate/20220909094752_add_free_user_cap_over_limt_notified_at_to_namespace_details.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class AddFreeUserCapOverLimtNotifiedAtToNamespaceDetails < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ TABLE_NAME = 'namespace_details'
+ COLUMN_NAME = 'free_user_cap_over_limt_notified_at'
+
+ def up
+ with_lock_retries do
+ add_column(TABLE_NAME, COLUMN_NAME, :datetime_with_timezone)
+ end
+ end
+
+ def down
+ with_lock_retries do
+ remove_column TABLE_NAME, COLUMN_NAME
+ end
+ end
+end
diff --git a/db/migrate/20220909113809_add_environments_project_name_lower_pattern_ops_state_index.rb b/db/migrate/20220909113809_add_environments_project_name_lower_pattern_ops_state_index.rb
new file mode 100644
index 00000000000..870c809beb6
--- /dev/null
+++ b/db/migrate/20220909113809_add_environments_project_name_lower_pattern_ops_state_index.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddEnvironmentsProjectNameLowerPatternOpsStateIndex < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_environments_on_project_name_varchar_pattern_ops_state'
+
+ def up
+ add_concurrent_index :environments, 'project_id, lower(name) 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/20220912180807_add_epoch_column_to_rpm_metadata.rb b/db/migrate/20220912180807_add_epoch_column_to_rpm_metadata.rb
new file mode 100644
index 00000000000..842d917c8a3
--- /dev/null
+++ b/db/migrate/20220912180807_add_epoch_column_to_rpm_metadata.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddEpochColumnToRpmMetadata < Gitlab::Database::Migration[2.0]
+ def change
+ add_column :packages_rpm_metadata, :epoch, :integer, null: false, default: 0
+ end
+end
diff --git a/db/migrate/20220913082930_rename_iterations_cadences_last_run_date_to_next_run_date.rb b/db/migrate/20220913082930_rename_iterations_cadences_last_run_date_to_next_run_date.rb
new file mode 100644
index 00000000000..2adc4202b79
--- /dev/null
+++ b/db/migrate/20220913082930_rename_iterations_cadences_last_run_date_to_next_run_date.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class RenameIterationsCadencesLastRunDateToNextRunDate < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ rename_column_concurrently :iterations_cadences, :last_run_date, :next_run_date
+ end
+
+ def down
+ undo_rename_column_concurrently :iterations_cadences, :last_run_date, :next_run_date
+ end
+end
diff --git a/db/migrate/20220914005141_change_namespace_id_not_null_in_members.rb b/db/migrate/20220914005141_change_namespace_id_not_null_in_members.rb
new file mode 100644
index 00000000000..250746b95b8
--- /dev/null
+++ b/db/migrate/20220914005141_change_namespace_id_not_null_in_members.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class ChangeNamespaceIdNotNullInMembers < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ add_not_null_constraint :members, :member_namespace_id, validate: false
+ end
+
+ def down
+ remove_not_null_constraint :members, :member_namespace_id
+ end
+end
diff --git a/db/migrate/20220914010233_change_members_namespace_foreign_key_on_delete_constraint.rb b/db/migrate/20220914010233_change_members_namespace_foreign_key_on_delete_constraint.rb
new file mode 100644
index 00000000000..2ee98d59c3e
--- /dev/null
+++ b/db/migrate/20220914010233_change_members_namespace_foreign_key_on_delete_constraint.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+class ChangeMembersNamespaceForeignKeyOnDeleteConstraint < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ TARGET_COLUMN = :member_namespace_id
+
+ def up
+ # add the new FK before removing the old one
+ add_concurrent_foreign_key(
+ :members,
+ :namespaces,
+ column: TARGET_COLUMN,
+ name: fk_name("#{TARGET_COLUMN}_new"),
+ on_delete: :cascade,
+ validate: false
+ )
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key_if_exists(:members, column: TARGET_COLUMN, name: fk_name("#{TARGET_COLUMN}_new"))
+ end
+ end
+
+ def fk_name(column_name)
+ # generate a FK name
+ concurrent_foreign_key_name(:members, column_name)
+ end
+end
diff --git a/db/migrate/20220915140802_create_merge_request_predictions.rb b/db/migrate/20220915140802_create_merge_request_predictions.rb
new file mode 100644
index 00000000000..20cd7e58092
--- /dev/null
+++ b/db/migrate/20220915140802_create_merge_request_predictions.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class CreateMergeRequestPredictions < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def up
+ create_table :merge_request_predictions, id: false do |t|
+ t.references :merge_request,
+ primary_key: true, null: false, type: :bigint,
+ index: false, foreign_key: { on_delete: :cascade }
+
+ t.timestamps_with_timezone null: false
+ t.jsonb :suggested_reviewers, null: false, default: {}
+ end
+ end
+
+ def down
+ drop_table :merge_request_predictions
+ end
+end
diff --git a/db/post_migrate/20210812013042_remove_duplicate_project_authorizations.rb b/db/post_migrate/20210812013042_remove_duplicate_project_authorizations.rb
index 6fdc30d09c6..1c2e2b52e8b 100644
--- a/db/post_migrate/20210812013042_remove_duplicate_project_authorizations.rb
+++ b/db/post_migrate/20210812013042_remove_duplicate_project_authorizations.rb
@@ -55,7 +55,8 @@ class RemoveDuplicateProjectAuthorizations < ActiveRecord::Migration[6.1]
end
def batch(&block)
- order = Gitlab::Pagination::Keyset::Order.build([
+ order = Gitlab::Pagination::Keyset::Order.build(
+ [
Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
attribute_name: 'user_id',
order_expression: ProjectAuthorization.arel_table[:user_id].asc,
diff --git a/db/post_migrate/20220601110011_schedule_remove_self_managed_wiki_notes.rb b/db/post_migrate/20220601110011_schedule_remove_self_managed_wiki_notes.rb
new file mode 100644
index 00000000000..9e6594bb9b6
--- /dev/null
+++ b/db/post_migrate/20220601110011_schedule_remove_self_managed_wiki_notes.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+class ScheduleRemoveSelfManagedWikiNotes < Gitlab::Database::Migration[2.0]
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ MIGRATION = 'RemoveSelfManagedWikiNotes'
+ INTERVAL = 2.minutes
+
+ disable_ddl_transaction!
+
+ def up
+ return if skip_migration?
+
+ queue_batched_background_migration(
+ MIGRATION,
+ :notes,
+ :id,
+ job_interval: INTERVAL,
+ batch_size: 10_000,
+ sub_batch_size: 1_000
+ )
+ end
+
+ def down
+ return if skip_migration?
+
+ delete_batched_background_migration(MIGRATION, :notes, :id, [])
+ end
+
+ private
+
+ def skip_migration?
+ Gitlab.staging? || Gitlab.com?
+ end
+end
diff --git a/db/post_migrate/20220606054503_add_tmp_index_job_artifacts_id_and_expire_at.rb b/db/post_migrate/20220606054503_add_tmp_index_job_artifacts_id_and_expire_at.rb
new file mode 100644
index 00000000000..28346eb1a97
--- /dev/null
+++ b/db/post_migrate/20220606054503_add_tmp_index_job_artifacts_id_and_expire_at.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class AddTmpIndexJobArtifactsIdAndExpireAt < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'tmp_index_ci_job_artifacts_on_id_expire_at_file_type_trace'
+
+ EXPIRE_AT_ON_22_MIDNIGHT_IN_TIMEZONE_OR_TRACE = <<~SQL
+ (EXTRACT(day FROM timezone('UTC', expire_at)) IN (21, 22, 23)
+ AND EXTRACT(minute FROM timezone('UTC', expire_at)) IN (0, 30, 45)
+ AND EXTRACT(second FROM timezone('UTC', expire_at)) = 0)
+ OR file_type = 3
+ SQL
+
+ def up
+ return if Gitlab.com?
+ return if index_exists_by_name?(:ci_job_artifacts, INDEX_NAME)
+
+ add_concurrent_index :ci_job_artifacts, :id,
+ where: EXPIRE_AT_ON_22_MIDNIGHT_IN_TIMEZONE_OR_TRACE, name: INDEX_NAME
+ end
+
+ def down
+ return if Gitlab.com?
+ return unless index_exists_by_name?(:ci_job_artifacts, INDEX_NAME)
+
+ remove_concurrent_index_by_name :ci_job_artifacts, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220606080509_fix_incorrect_job_artifacts_expire_at.rb b/db/post_migrate/20220606080509_fix_incorrect_job_artifacts_expire_at.rb
new file mode 100644
index 00000000000..8fea22f5579
--- /dev/null
+++ b/db/post_migrate/20220606080509_fix_incorrect_job_artifacts_expire_at.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+class FixIncorrectJobArtifactsExpireAt < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_ci
+
+ MIGRATION = 'RemoveBackfilledJobArtifactsExpireAt'
+ BATCH_CLASS = 'RemoveBackfilledJobArtifactsExpireAtBatchingStrategy'
+ BATCH_SIZE = 500
+ INTERVAL = 2.minutes.freeze
+
+ def up
+ return if Gitlab.com?
+
+ queue_batched_background_migration(
+ MIGRATION,
+ :ci_job_artifacts,
+ :id,
+ job_interval: INTERVAL,
+ batch_class_name: BATCH_CLASS,
+ batch_size: BATCH_SIZE
+ )
+ end
+
+ def down
+ return if Gitlab.com?
+
+ delete_batched_background_migration(MIGRATION, :ci_job_artifacts, :id, [])
+ end
+end
diff --git a/db/post_migrate/20220615154500_schedule_backfill_cluster_agents_has_vulnerabilities.rb b/db/post_migrate/20220615154500_schedule_backfill_cluster_agents_has_vulnerabilities.rb
new file mode 100644
index 00000000000..74aeb2947a9
--- /dev/null
+++ b/db/post_migrate/20220615154500_schedule_backfill_cluster_agents_has_vulnerabilities.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+class ScheduleBackfillClusterAgentsHasVulnerabilities < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ MIGRATION = 'BackfillClusterAgentsHasVulnerabilities'
+ DELAY_INTERVAL = 2.minutes
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ ensure_batched_background_migration_is_finished(
+ job_class_name: 'BackfillVulnerabilityReadsClusterAgent',
+ table_name: :vulnerability_reads,
+ column_name: :id,
+ job_arguments: []
+ )
+
+ queue_batched_background_migration(
+ MIGRATION,
+ :cluster_agents,
+ :id,
+ job_interval: DELAY_INTERVAL
+ )
+ end
+
+ def down
+ delete_batched_background_migration(MIGRATION, :cluster_agents, :id, [])
+ end
+end
diff --git a/db/post_migrate/20220616171355_update_vulnerabilities_project_id_id_active_cis_index.rb b/db/post_migrate/20220616171355_update_vulnerabilities_project_id_id_active_cis_index.rb
new file mode 100644
index 00000000000..047ae0d1132
--- /dev/null
+++ b/db/post_migrate/20220616171355_update_vulnerabilities_project_id_id_active_cis_index.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class UpdateVulnerabilitiesProjectIdIdActiveCisIndex < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ NEW_INDEX_NAME = 'idx_vulnerabilities_on_project_id_and_id_active_cis_dft_branch'
+ OLD_INDEX_NAME = 'index_vulnerabilities_on_project_id_and_id_active_cis'
+ OLD_INDEX_FILTER_CONDITION = 'report_type = 7 AND state = ANY(ARRAY[1, 4])'
+ NEW_INDEX_FILTER_CONDITION = 'report_type = 7 AND state = ANY(ARRAY[1, 4]) AND present_on_default_branch IS TRUE'
+
+ def up
+ add_concurrent_index :vulnerabilities, [:project_id, :id],
+ where: NEW_INDEX_FILTER_CONDITION,
+ name: NEW_INDEX_NAME
+
+ remove_concurrent_index_by_name(:vulnerabilities, OLD_INDEX_NAME)
+ end
+
+ def down
+ add_concurrent_index :vulnerabilities, [:project_id, :id], where: OLD_INDEX_FILTER_CONDITION, name: OLD_INDEX_NAME
+
+ remove_concurrent_index_by_name(:vulnerabilities, NEW_INDEX_NAME)
+ end
+end
diff --git a/db/post_migrate/20220706145113_backfill_namespace_id_on_issues.rb b/db/post_migrate/20220706145113_backfill_namespace_id_on_issues.rb
new file mode 100644
index 00000000000..8114967ac8e
--- /dev/null
+++ b/db/post_migrate/20220706145113_backfill_namespace_id_on_issues.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+class BackfillNamespaceIdOnIssues < Gitlab::Database::Migration[2.0]
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+ disable_ddl_transaction!
+
+ MIGRATION = 'BackfillProjectNamespaceOnIssues'
+ DELAY_INTERVAL = 2.minutes
+ BATCH_SIZE = 500
+ MAX_BATCH_SIZE = 10_000
+ SUB_BATCH_SIZE = 10
+
+ def up
+ queue_batched_background_migration(
+ MIGRATION,
+ :issues,
+ :id,
+ 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, :issues, :id, [])
+ end
+end
diff --git a/db/post_migrate/20220707192420_remove_tmp_idx_merge_requests_draft_and_status.rb b/db/post_migrate/20220707192420_remove_tmp_idx_merge_requests_draft_and_status.rb
new file mode 100644
index 00000000000..a8cfea02463
--- /dev/null
+++ b/db/post_migrate/20220707192420_remove_tmp_idx_merge_requests_draft_and_status.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class RemoveTmpIdxMergeRequestsDraftAndStatus < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = "tmp_index_merge_requests_draft_and_status"
+ CORRECTED_REGEXP_STR = "^(\\[draft\\]|\\(draft\\)|draft:|draft|\\[WIP\\]|WIP:|WIP)"
+
+ def up
+ remove_concurrent_index_by_name :merge_requests, INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :merge_requests, :id,
+ where: "draft = false AND state_id = 1 AND ((title)::text ~* '#{CORRECTED_REGEXP_STR}'::text)",
+ name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220726225114_remove_tmp_index_group_membership_namespace_id_column.rb b/db/post_migrate/20220726225114_remove_tmp_index_group_membership_namespace_id_column.rb
new file mode 100644
index 00000000000..db47be6a1e3
--- /dev/null
+++ b/db/post_migrate/20220726225114_remove_tmp_index_group_membership_namespace_id_column.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class RemoveTmpIndexGroupMembershipNamespaceIdColumn < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = 'tmp_index_for_namespace_id_migration_on_group_members'
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name :members, INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :members, :id,
+ where: "members.member_namespace_id IS NULL and members.type = 'GroupMember'",
+ name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220801155858_schedule_disable_legacy_open_source_licence_for_recent_public_projects.rb b/db/post_migrate/20220801155858_schedule_disable_legacy_open_source_licence_for_recent_public_projects.rb
new file mode 100644
index 00000000000..a662cfe4be3
--- /dev/null
+++ b/db/post_migrate/20220801155858_schedule_disable_legacy_open_source_licence_for_recent_public_projects.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+class ScheduleDisableLegacyOpenSourceLicenceForRecentPublicProjects < Gitlab::Database::Migration[2.0]
+ MIGRATION = 'DisableLegacyOpenSourceLicenceForRecentPublicProjects'
+ INTERVAL = 2.minutes
+ BATCH_SIZE = 1_000
+ MAX_BATCH_SIZE = 5_000
+ SUB_BATCH_SIZE = 200
+
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ return unless Gitlab.com?
+
+ queue_batched_background_migration(
+ MIGRATION,
+ :projects,
+ :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, :projects, :id, [])
+ end
+end
diff --git a/db/post_migrate/20220809002011_schedule_destroy_invalid_group_members.rb b/db/post_migrate/20220809002011_schedule_destroy_invalid_group_members.rb
new file mode 100644
index 00000000000..3db21c290b9
--- /dev/null
+++ b/db/post_migrate/20220809002011_schedule_destroy_invalid_group_members.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+class ScheduleDestroyInvalidGroupMembers < Gitlab::Database::Migration[2.0]
+ MIGRATION = 'DestroyInvalidGroupMembers'
+ DELAY_INTERVAL = 2.minutes
+ BATCH_SIZE = 1_000
+ MAX_BATCH_SIZE = 2_000
+ SUB_BATCH_SIZE = 50
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ disable_ddl_transaction!
+
+ def up
+ queue_batched_background_migration(
+ MIGRATION,
+ :members,
+ :id,
+ job_interval: DELAY_INTERVAL,
+ batch_size: BATCH_SIZE,
+ max_batch_size: MAX_BATCH_SIZE,
+ sub_batch_size: SUB_BATCH_SIZE,
+ gitlab_schema: :gitlab_main
+ )
+ end
+
+ def down
+ delete_batched_background_migration(MIGRATION, :members, :id, [])
+ end
+end
diff --git a/db/post_migrate/20220809214730_add_note_metadata_temp_index_on_id_where_task.rb b/db/post_migrate/20220809214730_add_note_metadata_temp_index_on_id_where_task.rb
new file mode 100644
index 00000000000..1c5a1b68041
--- /dev/null
+++ b/db/post_migrate/20220809214730_add_note_metadata_temp_index_on_id_where_task.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddNoteMetadataTempIndexOnIdWhereTask < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = 'tmp_index_system_note_metadata_on_id_where_task'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :system_note_metadata, [:id, :action], where: "action = 'task'", name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :system_note_metadata, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220809223215_change_task_system_note_wording_to_checklist_item.rb b/db/post_migrate/20220809223215_change_task_system_note_wording_to_checklist_item.rb
new file mode 100644
index 00000000000..df7c2d325d2
--- /dev/null
+++ b/db/post_migrate/20220809223215_change_task_system_note_wording_to_checklist_item.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+class ChangeTaskSystemNoteWordingToChecklistItem < Gitlab::Database::Migration[2.0]
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+ disable_ddl_transaction!
+
+ MIGRATION = 'RenameTaskSystemNoteToChecklistItem'
+ DELAY_INTERVAL = 2.minutes
+ BATCH_SIZE = 10_000
+ MAX_BATCH_SIZE = 20_000
+ SUB_BATCH_SIZE = 100
+
+ def up
+ queue_batched_background_migration(
+ MIGRATION,
+ :system_note_metadata,
+ :id,
+ 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, :system_note_metadata, :id, [])
+ end
+end
diff --git a/db/post_migrate/20220815061621_rename_web_hooks_service_id_to_integration_id.rb b/db/post_migrate/20220815061621_rename_web_hooks_service_id_to_integration_id.rb
new file mode 100644
index 00000000000..6bcee7f51df
--- /dev/null
+++ b/db/post_migrate/20220815061621_rename_web_hooks_service_id_to_integration_id.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class RenameWebHooksServiceIdToIntegrationId < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ rename_column_concurrently :web_hooks, :service_id, :integration_id
+ end
+
+ def down
+ undo_rename_column_concurrently :web_hooks, :service_id, :integration_id
+ end
+end
diff --git a/db/post_migrate/20220816075638_drop_uuid_and_id_index_from_security_findings.rb b/db/post_migrate/20220816075638_drop_uuid_and_id_index_from_security_findings.rb
new file mode 100644
index 00000000000..7a41e0b7835
--- /dev/null
+++ b/db/post_migrate/20220816075638_drop_uuid_and_id_index_from_security_findings.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class DropUuidAndIdIndexFromSecurityFindings < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = :index_on_security_findings_uuid_and_id_order_desc
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name :security_findings, name: INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :security_findings, [:uuid, :id], order: { id: :desc }, name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220816163444_update_start_date_for_iterations_cadences.rb b/db/post_migrate/20220816163444_update_start_date_for_iterations_cadences.rb
new file mode 100644
index 00000000000..631e8941add
--- /dev/null
+++ b/db/post_migrate/20220816163444_update_start_date_for_iterations_cadences.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+class UpdateStartDateForIterationsCadences < Gitlab::Database::Migration[2.0]
+ include ::Gitlab::Database::DynamicModelHelpers
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+ disable_ddl_transaction!
+
+ def up
+ each_batch_range('iterations_cadences', connection: connection) do |min, max|
+ execute(<<~SQL)
+ UPDATE iterations_cadences
+ SET start_date=ic.first_upcoming_iteration_start_date
+ FROM (
+ SELECT ic.id, sprints2.first_upcoming_iteration_start_date
+ FROM iterations_cadences as ic,
+ LATERAL (
+ -- For each cadence, query for the due date of its current iteration
+ SELECT due_date as current_iteration_due_date FROM sprints
+ WHERE iterations_cadence_id=ic.id AND start_date <= current_date AND due_date >= current_date
+ LIMIT 1
+ ) as sprints1,
+ LATERAL (
+ -- For each cadence, query for the start date of the first upcoming iteration (i.e, it starts after the current iteration)
+ SELECT start_date as first_upcoming_iteration_start_date FROM sprints
+ WHERE iterations_cadence_id=ic.id AND start_date > sprints1.current_iteration_due_date
+ ORDER BY start_date ASC LIMIT 1
+ ) as sprints2
+ WHERE ic.automatic=true AND ic.id BETWEEN #{min} AND #{max}
+ ) as ic
+ WHERE iterations_cadences.id=ic.id;
+ SQL
+ end
+ end
+
+ def down
+ each_batch_range('iterations_cadences', connection: connection) do |min, max|
+ execute(<<~SQL)
+ UPDATE iterations_cadences
+ SET start_date=ic.first_iteration_start_date
+ FROM (
+ SELECT ic.id, sprints.start_date as first_iteration_start_date
+ FROM iterations_cadences as ic,
+ LATERAL (
+ SELECT start_date FROM sprints WHERE iterations_cadence_id=ic.id ORDER BY start_date ASC LIMIT 1
+ ) as sprints
+ WHERE ic.automatic=true AND ic.id BETWEEN #{min} AND #{max}
+ ) as ic
+ WHERE iterations_cadences.id=ic.id;
+ SQL
+ end
+ end
+end
diff --git a/db/post_migrate/20220820221036_update_tmp_non_migrated_index_on_container_repositories.rb b/db/post_migrate/20220820221036_update_tmp_non_migrated_index_on_container_repositories.rb
new file mode 100644
index 00000000000..eea58ad7951
--- /dev/null
+++ b/db/post_migrate/20220820221036_update_tmp_non_migrated_index_on_container_repositories.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class UpdateTmpNonMigratedIndexOnContainerRepositories < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ NEW_INDEX_NAME = 'tmp_index_container_repos_on_non_migrated'
+ OLD_INDEX_NAME = 'tmp_idx_container_repos_on_non_migrated'
+ MIGRATION_PHASE_1_ENDED_AT = '2022-01-23'
+
+ def up
+ add_concurrent_index :container_repositories,
+ [:project_id, :id],
+ name: NEW_INDEX_NAME,
+ where: "migration_state != 'import_done'"
+ remove_concurrent_index_by_name :container_repositories, OLD_INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :container_repositories,
+ [:project_id, :id],
+ name: OLD_INDEX_NAME,
+ where: "migration_state != 'import_done' AND created_at < '#{MIGRATION_PHASE_1_ENDED_AT}'"
+ remove_concurrent_index_by_name :container_repositories, NEW_INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220822071909_remove_other_role_from_user_details.rb b/db/post_migrate/20220822071909_remove_other_role_from_user_details.rb
new file mode 100644
index 00000000000..a0177bf2605
--- /dev/null
+++ b/db/post_migrate/20220822071909_remove_other_role_from_user_details.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class RemoveOtherRoleFromUserDetails < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def change
+ remove_column :user_details, :other_role, :text
+ end
+end
diff --git a/db/post_migrate/20220822090656_drop_build_coverage_regex_from_project.rb b/db/post_migrate/20220822090656_drop_build_coverage_regex_from_project.rb
new file mode 100644
index 00000000000..70c6b660318
--- /dev/null
+++ b/db/post_migrate/20220822090656_drop_build_coverage_regex_from_project.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class DropBuildCoverageRegexFromProject < Gitlab::Database::Migration[2.0]
+ enable_lock_retries!
+
+ def up
+ remove_column :projects, :build_coverage_regex
+ end
+
+ def down
+ add_column :projects, :build_coverage_regex, :string # rubocop: disable Migration/AddColumnsToWideTables
+ end
+end
diff --git a/db/post_migrate/20220822094804_add_issues_authorization_index.rb b/db/post_migrate/20220822094804_add_issues_authorization_index.rb
new file mode 100644
index 00000000000..e09b5f8d93b
--- /dev/null
+++ b/db/post_migrate/20220822094804_add_issues_authorization_index.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddIssuesAuthorizationIndex < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'idx_open_issues_on_project_and_confidential_and_author_and_id'
+
+ def up
+ prepare_async_index :issues, [:project_id, :confidential, :author_id, :id], name: INDEX_NAME, where: 'state_id = 1'
+ end
+
+ def down
+ unprepare_async_index :issues, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220823084747_prepare_removal_partial_trigram_indexes_for_issues.rb b/db/post_migrate/20220823084747_prepare_removal_partial_trigram_indexes_for_issues.rb
new file mode 100644
index 00000000000..a8ea6abdcba
--- /dev/null
+++ b/db/post_migrate/20220823084747_prepare_removal_partial_trigram_indexes_for_issues.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class PrepareRemovalPartialTrigramIndexesForIssues < Gitlab::Database::Migration[2.0]
+ TITLE_INDEX_NAME = 'index_issues_on_title_trigram_non_latin'
+ DESCRIPTION_INDEX_NAME = 'index_issues_on_description_trigram_non_latin'
+
+ def up
+ prepare_async_index_removal :issues, :title, name: TITLE_INDEX_NAME
+ prepare_async_index_removal :issues, :description, name: DESCRIPTION_INDEX_NAME
+ end
+
+ def down
+ unprepare_async_index_by_name :issues, DESCRIPTION_INDEX_NAME
+ unprepare_async_index_by_name :issues, TITLE_INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220824114218_add_tmp_index_approval_merge_request_rules.rb b/db/post_migrate/20220824114218_add_tmp_index_approval_merge_request_rules.rb
new file mode 100644
index 00000000000..64171b3053e
--- /dev/null
+++ b/db/post_migrate/20220824114218_add_tmp_index_approval_merge_request_rules.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddTmpIndexApprovalMergeRequestRules < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ TMP_INDEX_NAME = 'tmp_index_approval_merge_request_rules_on_report_type_equal_one'
+
+ def up
+ # to be removed as part of https://gitlab.com/gitlab-org/gitlab/-/issues/372224
+ add_concurrent_index :approval_merge_request_rules,
+ [:id, :report_type],
+ name: TMP_INDEX_NAME,
+ where: "report_type = 1"
+ end
+
+ def down
+ remove_concurrent_index_by_name :approval_merge_request_rules, TMP_INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220825061250_drop_tmp_index_todos_attention_request_action_idx.rb b/db/post_migrate/20220825061250_drop_tmp_index_todos_attention_request_action_idx.rb
new file mode 100644
index 00000000000..091de49e1c9
--- /dev/null
+++ b/db/post_migrate/20220825061250_drop_tmp_index_todos_attention_request_action_idx.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class DropTmpIndexTodosAttentionRequestActionIdx < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = "tmp_index_todos_attention_request_action"
+ ATTENTION_REQUESTED = 10
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name :todos, INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :todos, [:id],
+ where: "action = #{ATTENTION_REQUESTED}",
+ name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220825142324_replace_issues_authorization_index.rb b/db/post_migrate/20220825142324_replace_issues_authorization_index.rb
new file mode 100644
index 00000000000..b033cb22490
--- /dev/null
+++ b/db/post_migrate/20220825142324_replace_issues_authorization_index.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class ReplaceIssuesAuthorizationIndex < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'idx_open_issues_on_project_and_confidential_and_author_and_id'
+ OLD_INDEX_NAME = 'idx_open_issues_on_project_id_and_confidential'
+
+ def up
+ add_concurrent_index :issues, [:project_id, :confidential, :author_id, :id], name: INDEX_NAME, where: 'state_id = 1'
+ remove_concurrent_index_by_name :issues, OLD_INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :issues, [:project_id, :confidential], name: OLD_INDEX_NAME, where: 'state_id = 1'
+ remove_concurrent_index_by_name :issues, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220826165048_drop_temporary_job_trace_index.rb b/db/post_migrate/20220826165048_drop_temporary_job_trace_index.rb
new file mode 100644
index 00000000000..0cad7cd1968
--- /dev/null
+++ b/db/post_migrate/20220826165048_drop_temporary_job_trace_index.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class DropTemporaryJobTraceIndex < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = 'tmp_index_ci_job_artifacts_on_id_where_trace_and_expire_at'
+
+ def up
+ prepare_async_index_removal :ci_job_artifacts, :id, name: INDEX_NAME
+ end
+
+ def down
+ unprepare_async_index_by_name :ci_job_artifacts, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220826175058_fully_remove_temporary_job_trace_index.rb b/db/post_migrate/20220826175058_fully_remove_temporary_job_trace_index.rb
new file mode 100644
index 00000000000..2a18e63106a
--- /dev/null
+++ b/db/post_migrate/20220826175058_fully_remove_temporary_job_trace_index.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class FullyRemoveTemporaryJobTraceIndex < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = 'tmp_index_ci_job_artifacts_on_id_where_trace_and_expire_at'
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name :ci_job_artifacts, name: INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :ci_job_artifacts, :id, name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220830051704_add_temporary_index_for_orphaned_invited_members.rb b/db/post_migrate/20220830051704_add_temporary_index_for_orphaned_invited_members.rb
new file mode 100644
index 00000000000..90254ac3d86
--- /dev/null
+++ b/db/post_migrate/20220830051704_add_temporary_index_for_orphaned_invited_members.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class AddTemporaryIndexForOrphanedInvitedMembers < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ TMP_INDEX_NAME = 'tmp_idx_orphaned_invited_members'
+
+ def up
+ add_concurrent_index('members', :id, where: query_condition, name: TMP_INDEX_NAME)
+ end
+
+ def down
+ remove_concurrent_index_by_name('members', TMP_INDEX_NAME) if index_exists_by_name?('members', TMP_INDEX_NAME)
+ end
+
+ private
+
+ def query_condition
+ 'invite_token IS NULL and invite_accepted_at IS NOT NULL AND user_id IS NULL'
+ end
+end
diff --git a/db/post_migrate/20220830061704_orphaned_invited_members_cleanup.rb b/db/post_migrate/20220830061704_orphaned_invited_members_cleanup.rb
new file mode 100644
index 00000000000..c5249510164
--- /dev/null
+++ b/db/post_migrate/20220830061704_orphaned_invited_members_cleanup.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class OrphanedInvitedMembersCleanup < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ # rubocop:disable Style/SymbolProc
+ membership.where(query_condition).each_batch(of: 100) do |relation|
+ relation.delete_all
+ end
+ # rubocop:enable Style/SymbolProc
+ end
+
+ def down
+ # This migration is irreversible
+ end
+
+ private
+
+ def membership
+ @membership ||= define_batchable_model('members')
+ end
+
+ def query_condition
+ 'invite_token IS NULL and invite_accepted_at IS NOT NULL AND user_id IS NULL'
+ end
+end
diff --git a/db/post_migrate/20220830071704_remove_temporary_index_for_orphaned_invited_members.rb b/db/post_migrate/20220830071704_remove_temporary_index_for_orphaned_invited_members.rb
new file mode 100644
index 00000000000..c6b712da4c0
--- /dev/null
+++ b/db/post_migrate/20220830071704_remove_temporary_index_for_orphaned_invited_members.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class RemoveTemporaryIndexForOrphanedInvitedMembers < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ TMP_INDEX_NAME = 'tmp_idx_orphaned_invited_members'
+
+ def up
+ remove_concurrent_index_by_name('members', TMP_INDEX_NAME) if index_exists_by_name?('members', TMP_INDEX_NAME)
+ end
+
+ def down
+ add_concurrent_index('members', :id, where: query_condition, name: TMP_INDEX_NAME)
+ end
+
+ private
+
+ def query_condition
+ 'invite_token IS NULL and invite_accepted_at IS NOT NULL AND user_id IS NULL'
+ end
+end
diff --git a/db/post_migrate/20220830172142_reschedule_issue_work_item_type_id_backfill.rb b/db/post_migrate/20220830172142_reschedule_issue_work_item_type_id_backfill.rb
new file mode 100644
index 00000000000..5495f0e53b4
--- /dev/null
+++ b/db/post_migrate/20220830172142_reschedule_issue_work_item_type_id_backfill.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+class RescheduleIssueWorkItemTypeIdBackfill < Gitlab::Database::Migration[2.0]
+ MIGRATION = 'BackfillWorkItemTypeIdForIssues'
+ BATCH_SIZE = 10_000
+ MAX_BATCH_SIZE = 30_000
+ SUB_BATCH_SIZE = 100
+ INTERVAL = 1.minute
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ class MigrationWorkItemType < MigrationRecord
+ self.table_name = 'work_item_types'
+
+ def self.id_by_type
+ where(namespace_id: nil).order(:base_type).pluck(:base_type, :id).to_h
+ end
+ end
+
+ def up
+ # We expect no more than 5 types. Only 3 of them are expected to have associated issues at the moment
+ MigrationWorkItemType.id_by_type.each do |base_type, type_id|
+ queue_batched_background_migration(
+ MIGRATION,
+ :issues,
+ :id,
+ base_type,
+ type_id,
+ job_interval: INTERVAL,
+ batch_size: BATCH_SIZE,
+ max_batch_size: MAX_BATCH_SIZE,
+ sub_batch_size: SUB_BATCH_SIZE
+ )
+ end
+ end
+
+ def down
+ Gitlab::Database::BackgroundMigration::BatchedMigration.where(job_class_name: MIGRATION).delete_all
+ end
+end
diff --git a/db/post_migrate/20220831021358_add_index_on_issue_health_status.rb b/db/post_migrate/20220831021358_add_index_on_issue_health_status.rb
new file mode 100644
index 00000000000..940bf6b5c38
--- /dev/null
+++ b/db/post_migrate/20220831021358_add_index_on_issue_health_status.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddIndexOnIssueHealthStatus < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ TABLE_NAME = :issues
+ INDEX_NAME = 'index_issues_on_project_id_health_status_created_at_id'
+
+ def up
+ add_concurrent_index TABLE_NAME, [:project_id, :health_status, :created_at, :id], name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index TABLE_NAME, [:project_id, :health_status, :created_at, :id], name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220831132802_delete_approval_rules_for_vulnerability.rb b/db/post_migrate/20220831132802_delete_approval_rules_for_vulnerability.rb
new file mode 100644
index 00000000000..b29678f1826
--- /dev/null
+++ b/db/post_migrate/20220831132802_delete_approval_rules_for_vulnerability.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+class DeleteApprovalRulesForVulnerability < Gitlab::Database::Migration[2.0]
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+ disable_ddl_transaction!
+
+ BATCH_SIZE = 500
+ MAX_BATCH_SIZE = 1_000
+ SUB_BATCH_SIZE = 10
+ MIGRATION = 'DeleteApprovalRulesWithVulnerability'
+ 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,
+ max_batch_size: MAX_BATCH_SIZE,
+ sub_batch_size: SUB_BATCH_SIZE
+ )
+
+ queue_batched_background_migration(
+ MIGRATION,
+ :approval_merge_request_rules,
+ :id,
+ job_interval: INTERVAL,
+ batch_size: BATCH_SIZE,
+ max_batch_size: MAX_BATCH_SIZE,
+ sub_batch_size: SUB_BATCH_SIZE
+ )
+ end
+
+ def down
+ # the data deleted is related to a feature removed in 15.0: https://gitlab.com/gitlab-org/gitlab/-/issues/357300
+ delete_batched_background_migration(MIGRATION, :approval_project_rules, :id, [])
+ delete_batched_background_migration(MIGRATION, :approval_merge_request_rules, :id, [])
+ end
+end
diff --git a/db/post_migrate/20220901035725_schedule_destroy_invalid_project_members.rb b/db/post_migrate/20220901035725_schedule_destroy_invalid_project_members.rb
new file mode 100644
index 00000000000..bc90232f855
--- /dev/null
+++ b/db/post_migrate/20220901035725_schedule_destroy_invalid_project_members.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+class ScheduleDestroyInvalidProjectMembers < Gitlab::Database::Migration[2.0]
+ MIGRATION = 'DestroyInvalidProjectMembers'
+ DELAY_INTERVAL = 2.minutes
+ BATCH_SIZE = 50_000
+ MAX_BATCH_SIZE = 100_000
+ SUB_BATCH_SIZE = 200
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ queue_batched_background_migration(
+ MIGRATION,
+ :members,
+ :id,
+ job_interval: DELAY_INTERVAL,
+ batch_size: BATCH_SIZE,
+ max_batch_size: MAX_BATCH_SIZE,
+ sub_batch_size: SUB_BATCH_SIZE,
+ gitlab_schema: :gitlab_main
+ )
+ end
+
+ def down
+ delete_batched_background_migration(MIGRATION, :members, :id, [])
+ end
+end
diff --git a/db/post_migrate/20220901071310_add_tmp_index_user_callouts_on_attention_request_feature_names.rb b/db/post_migrate/20220901071310_add_tmp_index_user_callouts_on_attention_request_feature_names.rb
new file mode 100644
index 00000000000..10b339b90c1
--- /dev/null
+++ b/db/post_migrate/20220901071310_add_tmp_index_user_callouts_on_attention_request_feature_names.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddTmpIndexUserCalloutsOnAttentionRequestFeatureNames < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = "tmp_index_user_callouts_on_attention_request_feature_names"
+ ATTENTION_REQUEST_CALLOUTS = [47, 48]
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :user_callouts, [:id],
+ where: "feature_name IN (#{ATTENTION_REQUEST_CALLOUTS.join(',')})",
+ name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :user_callouts, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220901071355_cleanup_attention_request_user_callouts.rb b/db/post_migrate/20220901071355_cleanup_attention_request_user_callouts.rb
new file mode 100644
index 00000000000..cbd0d120bb8
--- /dev/null
+++ b/db/post_migrate/20220901071355_cleanup_attention_request_user_callouts.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+class CleanupAttentionRequestUserCallouts < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ ATTENTION_REQUEST_CALLOUTS = [47, 48]
+ # 47 and 48 were removed with https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95446
+
+ def up
+ define_batchable_model('user_callouts')
+ .where(feature_name: ATTENTION_REQUEST_CALLOUTS)
+ .each_batch { |batch| batch.delete_all } # rubocop:disable Style/SymbolProc
+ end
+
+ def down
+ # Attention request feature has been reverted.
+ end
+end
diff --git a/db/post_migrate/20220901073300_remove_partial_trigram_indexes_for_issues.rb b/db/post_migrate/20220901073300_remove_partial_trigram_indexes_for_issues.rb
new file mode 100644
index 00000000000..096b74bc1c6
--- /dev/null
+++ b/db/post_migrate/20220901073300_remove_partial_trigram_indexes_for_issues.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class RemovePartialTrigramIndexesForIssues < Gitlab::Database::Migration[2.0]
+ TITLE_INDEX_NAME = 'index_issues_on_title_trigram_non_latin'
+ DESCRIPTION_INDEX_NAME = 'index_issues_on_description_trigram_non_latin'
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name :issues, TITLE_INDEX_NAME
+ remove_concurrent_index_by_name :issues, DESCRIPTION_INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :issues, :title,
+ name: TITLE_INDEX_NAME,
+ using: :gin, opclass: { description: :gin_trgm_ops },
+ where: "title NOT SIMILAR TO '[\\u0000-\\u218F]*' OR description NOT SIMILAR TO '[\\u0000-\\u218F]*'"
+
+ add_concurrent_index :issues, :description,
+ name: DESCRIPTION_INDEX_NAME,
+ using: :gin, opclass: { description: :gin_trgm_ops },
+ where: "title NOT SIMILAR TO '[\\u0000-\\u218F]*' OR description NOT SIMILAR TO '[\\u0000-\\u218F]*'"
+ end
+end
diff --git a/db/post_migrate/20220901184106_add_not_null_to_board_group_recent_visits.rb b/db/post_migrate/20220901184106_add_not_null_to_board_group_recent_visits.rb
new file mode 100644
index 00000000000..1dead32efb6
--- /dev/null
+++ b/db/post_migrate/20220901184106_add_not_null_to_board_group_recent_visits.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddNotNullToBoardGroupRecentVisits < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ add_not_null_constraint :board_group_recent_visits, :user_id, validate: false
+ add_not_null_constraint :board_group_recent_visits, :group_id, validate: false
+ add_not_null_constraint :board_group_recent_visits, :board_id, validate: false
+ end
+
+ def down
+ remove_not_null_constraint :board_group_recent_visits, :user_id
+ remove_not_null_constraint :board_group_recent_visits, :board_id
+ remove_not_null_constraint :board_group_recent_visits, :group_id
+ end
+end
diff --git a/db/post_migrate/20220901184246_add_not_null_to_board_project_recent_visits.rb b/db/post_migrate/20220901184246_add_not_null_to_board_project_recent_visits.rb
new file mode 100644
index 00000000000..4f0cb4d3d68
--- /dev/null
+++ b/db/post_migrate/20220901184246_add_not_null_to_board_project_recent_visits.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddNotNullToBoardProjectRecentVisits < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ add_not_null_constraint :board_project_recent_visits, :user_id, validate: false
+ add_not_null_constraint :board_project_recent_visits, :project_id, validate: false
+ add_not_null_constraint :board_project_recent_visits, :board_id, validate: false
+ end
+
+ def down
+ remove_not_null_constraint :board_project_recent_visits, :user_id
+ remove_not_null_constraint :board_project_recent_visits, :project_id
+ remove_not_null_constraint :board_project_recent_visits, :board_id
+ end
+end
diff --git a/db/post_migrate/20220902111016_delete_null_records_from_board_group_recent_visits.rb b/db/post_migrate/20220902111016_delete_null_records_from_board_group_recent_visits.rb
new file mode 100644
index 00000000000..4b55ecc013d
--- /dev/null
+++ b/db/post_migrate/20220902111016_delete_null_records_from_board_group_recent_visits.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class DeleteNullRecordsFromBoardGroupRecentVisits < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ execute('DELETE FROM board_group_recent_visits WHERE user_id is null OR group_id is null OR board_id is null')
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20220902111038_delete_null_records_from_board_project_recent_visits.rb b/db/post_migrate/20220902111038_delete_null_records_from_board_project_recent_visits.rb
new file mode 100644
index 00000000000..bb261f80f73
--- /dev/null
+++ b/db/post_migrate/20220902111038_delete_null_records_from_board_project_recent_visits.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class DeleteNullRecordsFromBoardProjectRecentVisits < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ execute('DELETE FROM board_project_recent_visits WHERE user_id is null OR project_id is null OR board_id is null')
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20220902204048_move_security_findings_table_to_gitlab_partitions_dynamic_schema.rb b/db/post_migrate/20220902204048_move_security_findings_table_to_gitlab_partitions_dynamic_schema.rb
new file mode 100644
index 00000000000..7b80b6a15bd
--- /dev/null
+++ b/db/post_migrate/20220902204048_move_security_findings_table_to_gitlab_partitions_dynamic_schema.rb
@@ -0,0 +1,232 @@
+# frozen_string_literal: true
+
+# rubocop:disable Migration/WithLockRetriesDisallowedMethod
+class MoveSecurityFindingsTableToGitlabPartitionsDynamicSchema < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_MAPPING_OF_PARTITION = {
+ index_security_findings_on_unique_columns: :security_findings_1_uuid_scan_id_partition_number_idx,
+ index_security_findings_on_confidence: :security_findings_1_confidence_idx,
+ index_security_findings_on_project_fingerprint: :security_findings_1_project_fingerprint_idx,
+ index_security_findings_on_scan_id_and_deduplicated: :security_findings_1_scan_id_deduplicated_idx,
+ index_security_findings_on_scan_id_and_id: :security_findings_1_scan_id_id_idx,
+ index_security_findings_on_scanner_id: :security_findings_1_scanner_id_idx,
+ index_security_findings_on_severity: :security_findings_1_severity_idx
+ }.freeze
+
+ INDEX_MAPPING_AFTER_CREATING_FROM_PARTITION = {
+ partition_name_placeholder_pkey: :security_findings_pkey,
+ partition_name_placeholder_uuid_scan_id_partition_number_idx: :index_security_findings_on_unique_columns,
+ partition_name_placeholder_confidence_idx: :index_security_findings_on_confidence,
+ partition_name_placeholder_project_fingerprint_idx: :index_security_findings_on_project_fingerprint,
+ partition_name_placeholder_scan_id_deduplicated_idx: :index_security_findings_on_scan_id_and_deduplicated,
+ partition_name_placeholder_scan_id_id_idx: :index_security_findings_on_scan_id_and_id,
+ partition_name_placeholder_scanner_id_idx: :index_security_findings_on_scanner_id,
+ partition_name_placeholder_severity_idx: :index_security_findings_on_severity
+ }.freeze
+
+ INDEX_MAPPING_AFTER_CREATING_FROM_ITSELF = {
+ security_findings_pkey1: :security_findings_pkey,
+ security_findings_uuid_scan_id_partition_number_idx1: :index_security_findings_on_unique_columns,
+ security_findings_confidence_idx1: :index_security_findings_on_confidence,
+ security_findings_project_fingerprint_idx1: :index_security_findings_on_project_fingerprint,
+ security_findings_scan_id_deduplicated_idx1: :index_security_findings_on_scan_id_and_deduplicated,
+ security_findings_scan_id_id_idx1: :index_security_findings_on_scan_id_and_id,
+ security_findings_scanner_id_idx1: :index_security_findings_on_scanner_id,
+ security_findings_severity_idx1: :index_security_findings_on_severity
+ }.freeze
+
+ LATEST_PARTITION_SQL = <<~SQL
+ SELECT
+ partitions.relname AS partition_name
+ FROM pg_inherits
+ JOIN pg_class parent ON pg_inherits.inhparent = parent.oid
+ JOIN pg_class partitions ON pg_inherits.inhrelid = partitions.oid
+ WHERE
+ parent.relname = 'security_findings'
+ ORDER BY (regexp_matches(partitions.relname, 'security_findings_(\\d+)'))[1]::int DESC
+ LIMIT 1
+ SQL
+
+ CURRENT_CHECK_CONSTRAINT_SQL = <<~SQL
+ SELECT
+ pg_get_constraintdef(pg_catalog.pg_constraint.oid)
+ FROM
+ pg_catalog.pg_constraint
+ INNER JOIN pg_class ON pg_class.oid = pg_catalog.pg_constraint.conrelid
+ WHERE
+ conname = 'check_partition_number' AND
+ pg_class.relname = 'security_findings'
+ SQL
+
+ def up
+ with_lock_retries do
+ lock_tables
+
+ execute(<<~SQL)
+ ALTER TABLE security_findings RENAME TO security_findings_#{candidate_partition_number};
+ SQL
+
+ execute(<<~SQL)
+ ALTER INDEX security_findings_pkey RENAME TO security_findings_#{candidate_partition_number}_pkey;
+ SQL
+
+ execute(<<~SQL)
+ CREATE TABLE security_findings (
+ LIKE security_findings_#{candidate_partition_number} INCLUDING ALL
+ ) PARTITION BY LIST (partition_number);
+ SQL
+
+ execute(<<~SQL)
+ ALTER SEQUENCE security_findings_id_seq OWNED BY #{connection.current_schema}.security_findings.id;
+ SQL
+
+ execute(<<~SQL)
+ ALTER TABLE security_findings
+ ADD CONSTRAINT fk_rails_729b763a54 FOREIGN KEY (scanner_id) REFERENCES vulnerability_scanners(id) ON DELETE CASCADE;
+ SQL
+
+ execute(<<~SQL)
+ ALTER TABLE security_findings
+ ADD CONSTRAINT fk_rails_bb63863cf1 FOREIGN KEY (scan_id) REFERENCES security_scans(id) ON DELETE CASCADE;
+ SQL
+
+ execute(<<~SQL)
+ ALTER TABLE security_findings_#{candidate_partition_number} SET SCHEMA gitlab_partitions_dynamic;
+ SQL
+
+ execute(<<~SQL)
+ ALTER TABLE security_findings ATTACH PARTITION gitlab_partitions_dynamic.security_findings_#{candidate_partition_number} FOR VALUES IN (#{candidate_partition_number});
+ SQL
+
+ execute(<<~SQL)
+ ALTER TABLE security_findings DROP CONSTRAINT check_partition_number;
+ SQL
+
+ index_mapping = INDEX_MAPPING_OF_PARTITION.transform_values do |value|
+ value.to_s.sub('partition_name_placeholder', "security_findings_#{candidate_partition_number}")
+ end
+
+ rename_indices('gitlab_partitions_dynamic', index_mapping)
+ end
+ end
+
+ def down
+ # If there is already a partition for the `security_findings` table,
+ # we can promote that table to be the original one to save the data.
+ # Otherwise, we have to bring back the non-partitioned `security_findings`
+ # table from the partitioned one.
+ if latest_partition
+ create_non_partitioned_security_findings_with_data
+ else
+ create_non_partitioned_security_findings_without_data
+ end
+ end
+
+ private
+
+ def lock_tables
+ execute(<<~SQL)
+ LOCK TABLE vulnerability_scanners, security_scans, security_findings IN ACCESS EXCLUSIVE MODE
+ SQL
+ end
+
+ def current_check_constraint
+ execute(CURRENT_CHECK_CONSTRAINT_SQL).first['pg_get_constraintdef']
+ end
+
+ def candidate_partition_number
+ @candidate_partition_number ||= current_check_constraint.match(/partition_number\s?=\s?(\d+)/).captures.first
+ end
+
+ def latest_partition
+ @latest_partition ||= execute(LATEST_PARTITION_SQL).first&.fetch('partition_name', nil)
+ end
+
+ def latest_partition_number
+ latest_partition.match(/security_findings_(\d+)/).captures.first
+ end
+
+ # rubocop:disable Migration/DropTable (These methods are called from the `down` method)
+ def create_non_partitioned_security_findings_with_data
+ with_lock_retries do
+ lock_tables
+
+ execute(<<~SQL)
+ ALTER TABLE security_findings DETACH PARTITION gitlab_partitions_dynamic.#{latest_partition};
+ SQL
+
+ execute(<<~SQL)
+ ALTER TABLE gitlab_partitions_dynamic.#{latest_partition} SET SCHEMA #{connection.current_schema};
+ SQL
+
+ execute(<<~SQL)
+ ALTER SEQUENCE security_findings_id_seq OWNED BY #{latest_partition}.id;
+ SQL
+
+ execute(<<~SQL)
+ DROP TABLE security_findings;
+ SQL
+
+ execute(<<~SQL)
+ ALTER TABLE #{latest_partition} RENAME TO security_findings;
+ SQL
+
+ index_mapping = INDEX_MAPPING_AFTER_CREATING_FROM_PARTITION.transform_keys do |key|
+ key.to_s.sub('partition_name_placeholder', latest_partition)
+ end
+
+ rename_indices(connection.current_schema, index_mapping)
+ end
+
+ add_check_constraint(:security_findings, "(partition_number = #{latest_partition_number})", :check_partition_number)
+ end
+
+ def create_non_partitioned_security_findings_without_data
+ with_lock_retries do
+ lock_tables
+
+ execute(<<~SQL)
+ ALTER TABLE security_findings RENAME TO security_findings_1;
+ SQL
+
+ execute(<<~SQL)
+ CREATE TABLE security_findings (
+ LIKE security_findings_1 INCLUDING ALL
+ );
+ SQL
+
+ execute(<<~SQL)
+ ALTER SEQUENCE security_findings_id_seq OWNED BY #{connection.current_schema}.security_findings.id;
+ SQL
+
+ execute(<<~SQL)
+ DROP TABLE security_findings_1;
+ SQL
+
+ execute(<<~SQL)
+ ALTER TABLE ONLY security_findings
+ ADD CONSTRAINT fk_rails_729b763a54 FOREIGN KEY (scanner_id) REFERENCES vulnerability_scanners(id) ON DELETE CASCADE;
+ SQL
+
+ execute(<<~SQL)
+ ALTER TABLE ONLY security_findings
+ ADD CONSTRAINT fk_rails_bb63863cf1 FOREIGN KEY (scan_id) REFERENCES security_scans(id) ON DELETE CASCADE;
+ SQL
+
+ rename_indices(connection.current_schema, INDEX_MAPPING_AFTER_CREATING_FROM_ITSELF)
+ end
+
+ add_check_constraint(:security_findings, "(partition_number = 1)", :check_partition_number)
+ end
+
+ def rename_indices(schema, mapping)
+ mapping.each do |index_name, new_index_name|
+ execute(<<~SQL)
+ ALTER INDEX #{schema}.#{index_name} RENAME TO #{new_index_name};
+ SQL
+ end
+ end
+ # rubocop:enable Migration/DropTable
+end
+# rubocop:enable Migration/WithLockRetriesDisallowedMethod
diff --git a/db/post_migrate/20220904173342_validate_not_null_constraint_board_group_recent_visits.rb b/db/post_migrate/20220904173342_validate_not_null_constraint_board_group_recent_visits.rb
new file mode 100644
index 00000000000..0e5a504d0eb
--- /dev/null
+++ b/db/post_migrate/20220904173342_validate_not_null_constraint_board_group_recent_visits.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class ValidateNotNullConstraintBoardGroupRecentVisits < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ validate_not_null_constraint :board_group_recent_visits, :user_id
+ validate_not_null_constraint :board_group_recent_visits, :group_id
+ validate_not_null_constraint :board_group_recent_visits, :board_id
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20220904173430_validate_not_null_constraint_board_project_recent_visits.rb b/db/post_migrate/20220904173430_validate_not_null_constraint_board_project_recent_visits.rb
new file mode 100644
index 00000000000..ff73a179f69
--- /dev/null
+++ b/db/post_migrate/20220904173430_validate_not_null_constraint_board_project_recent_visits.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class ValidateNotNullConstraintBoardProjectRecentVisits < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ validate_not_null_constraint :board_project_recent_visits, :user_id
+ validate_not_null_constraint :board_project_recent_visits, :project_id
+ validate_not_null_constraint :board_project_recent_visits, :board_id
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20220905090300_add_tmp_index_merge_request_reviewers_attention_request_state.rb b/db/post_migrate/20220905090300_add_tmp_index_merge_request_reviewers_attention_request_state.rb
new file mode 100644
index 00000000000..e192f76484c
--- /dev/null
+++ b/db/post_migrate/20220905090300_add_tmp_index_merge_request_reviewers_attention_request_state.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddTmpIndexMergeRequestReviewersAttentionRequestState < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = "tmp_index_merge_request_reviewers_on_attention_requested_state"
+ ATTENTION_REQUESTED_STATE = 2
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :merge_request_reviewers, [:id],
+ where: "state = #{ATTENTION_REQUESTED_STATE}",
+ name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :merge_request_reviewers, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220905090339_reset_attention_requested_merge_requests_reviewers_state_to_unreviewed.rb b/db/post_migrate/20220905090339_reset_attention_requested_merge_requests_reviewers_state_to_unreviewed.rb
new file mode 100644
index 00000000000..d335095e674
--- /dev/null
+++ b/db/post_migrate/20220905090339_reset_attention_requested_merge_requests_reviewers_state_to_unreviewed.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+class ResetAttentionRequestedMergeRequestsReviewersStateToUnreviewed < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ BATCH_SIZE = 500
+
+ class MergeRequestReviewer < MigrationRecord
+ self.table_name = 'merge_request_reviewers'
+
+ enum state: {
+ unreviewed: 0,
+ reviewed: 1,
+ attention_requested: 2
+ }
+
+ include ::EachBatch
+ end
+
+ def up
+ MergeRequestReviewer
+ .where(state: MergeRequestReviewer.states['attention_requested'])
+ .each_batch(of: BATCH_SIZE) { |batch| batch.update_all(state: MergeRequestReviewer.states['unreviewed']) }
+ end
+
+ def down
+ # no op
+ end
+end
diff --git a/db/post_migrate/20220905112710_add_async_index_to_todos_to_cover_pending_query.rb b/db/post_migrate/20220905112710_add_async_index_to_todos_to_cover_pending_query.rb
new file mode 100644
index 00000000000..e2bca2fae1a
--- /dev/null
+++ b/db/post_migrate/20220905112710_add_async_index_to_todos_to_cover_pending_query.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddAsyncIndexToTodosToCoverPendingQuery < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_on_todos_user_project_target_and_state'
+ COLUMNS = %i[user_id project_id target_type target_id id].freeze
+
+ def up
+ prepare_async_index :todos, COLUMNS, name: INDEX_NAME, where: "state = 'pending'"
+ end
+
+ def down
+ unprepare_async_index :todos, COLUMNS, name: INDEX_NAME, where: "state='pending'"
+ end
+end
diff --git a/db/post_migrate/20220906074449_schedule_disable_legacy_open_source_license_for_projects_less_than_one_mb.rb b/db/post_migrate/20220906074449_schedule_disable_legacy_open_source_license_for_projects_less_than_one_mb.rb
new file mode 100644
index 00000000000..ee48ace13e0
--- /dev/null
+++ b/db/post_migrate/20220906074449_schedule_disable_legacy_open_source_license_for_projects_less_than_one_mb.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+class ScheduleDisableLegacyOpenSourceLicenseForProjectsLessThanOneMb < Gitlab::Database::Migration[2.0]
+ MIGRATION = 'DisableLegacyOpenSourceLicenseForProjectsLessThanOneMb'
+ 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/20220906212931_add_partial_index_for_ci_pipeline_artifacts_unlocked_with_expire_at.rb b/db/post_migrate/20220906212931_add_partial_index_for_ci_pipeline_artifacts_unlocked_with_expire_at.rb
new file mode 100644
index 00000000000..a24187dd56b
--- /dev/null
+++ b/db/post_migrate/20220906212931_add_partial_index_for_ci_pipeline_artifacts_unlocked_with_expire_at.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddPartialIndexForCiPipelineArtifactsUnlockedWithExpireAt < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ TABLE_NAME = 'ci_pipeline_artifacts'
+ INDEX_NAME = 'ci_pipeline_artifacts_on_expire_at_for_removal'
+ CONDITIONS = 'locked = 0 AND expire_at IS NOT NULL'
+
+ def up
+ add_concurrent_index TABLE_NAME, [:expire_at], where: CONDITIONS, name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name TABLE_NAME, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220908125146_remove_free_user_cap_remediation_worker.rb b/db/post_migrate/20220908125146_remove_free_user_cap_remediation_worker.rb
new file mode 100644
index 00000000000..e95ea9c58b4
--- /dev/null
+++ b/db/post_migrate/20220908125146_remove_free_user_cap_remediation_worker.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class RemoveFreeUserCapRemediationWorker < Gitlab::Database::Migration[2.0]
+ def up
+ Sidekiq::Cron::Job.find('free_user_cap_data_remediation')&.destroy
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20220909114220_drop_environments_project_name_lower_pattern_ops_index.rb b/db/post_migrate/20220909114220_drop_environments_project_name_lower_pattern_ops_index.rb
new file mode 100644
index 00000000000..3ab71809039
--- /dev/null
+++ b/db/post_migrate/20220909114220_drop_environments_project_name_lower_pattern_ops_index.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+# reverts db/migrate/20220901131828_add_environments_project_name_lower_pattern_ops_index.rb
+class DropEnvironmentsProjectNameLowerPatternOpsIndex < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_environments_on_project_name_varchar_pattern_ops'
+
+ def up
+ remove_concurrent_index_by_name :environments, INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :environments, 'project_id, lower(name) varchar_pattern_ops', name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220912085047_add_index_to_todos_pending_query.rb b/db/post_migrate/20220912085047_add_index_to_todos_pending_query.rb
new file mode 100644
index 00000000000..7d721421463
--- /dev/null
+++ b/db/post_migrate/20220912085047_add_index_to_todos_pending_query.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddIndexToTodosPendingQuery < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_on_todos_user_project_target_and_state'
+ COLUMNS = %i[user_id project_id target_type target_id id].freeze
+
+ def up
+ add_concurrent_index :todos, COLUMNS, name: INDEX_NAME, where: "state = 'pending'"
+ end
+
+ def down
+ remove_concurrent_index_by_name :todos, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220912110433_prepare_create_internal_notes_index_on_id.rb b/db/post_migrate/20220912110433_prepare_create_internal_notes_index_on_id.rb
new file mode 100644
index 00000000000..241bdfa4715
--- /dev/null
+++ b/db/post_migrate/20220912110433_prepare_create_internal_notes_index_on_id.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class PrepareCreateInternalNotesIndexOnId < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = 'index_notes_on_id_where_internal'
+
+ def up
+ prepare_async_index :notes, :id, where: 'internal = true', name: INDEX_NAME
+ end
+
+ def down
+ unprepare_async_index_by_name :notes, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220913030552_add_tmp_index_system_note_metadata_on_attention_request_actions.rb b/db/post_migrate/20220913030552_add_tmp_index_system_note_metadata_on_attention_request_actions.rb
new file mode 100644
index 00000000000..3418dabc0e9
--- /dev/null
+++ b/db/post_migrate/20220913030552_add_tmp_index_system_note_metadata_on_attention_request_actions.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddTmpIndexSystemNoteMetadataOnAttentionRequestActions < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = "tmp_index_system_note_metadata_on_attention_request_actions"
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :system_note_metadata, [:id],
+ where: "action IN ('attention_requested', 'attention_request_removed')",
+ name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :system_note_metadata, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220913030624_cleanup_attention_request_related_system_notes.rb b/db/post_migrate/20220913030624_cleanup_attention_request_related_system_notes.rb
new file mode 100644
index 00000000000..b7d6908696b
--- /dev/null
+++ b/db/post_migrate/20220913030624_cleanup_attention_request_related_system_notes.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+class CleanupAttentionRequestRelatedSystemNotes < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ BATCH_SIZE = 100
+
+ class SystemNoteMetadata < MigrationRecord
+ include EachBatch
+
+ self.table_name = 'system_note_metadata'
+ end
+
+ class Note < MigrationRecord
+ self.table_name = 'notes'
+ end
+
+ def up
+ SystemNoteMetadata
+ .where(action: %w[attention_requested attention_request_removed])
+ .each_batch(of: BATCH_SIZE) do |batch|
+ Note.where(id: batch.pluck(:note_id)).delete_all
+ end
+ end
+
+ def down
+ # no op
+ end
+end
diff --git a/db/post_migrate/20220913082728_drop_index_cadence_create_iterations_automation.rb b/db/post_migrate/20220913082728_drop_index_cadence_create_iterations_automation.rb
new file mode 100644
index 00000000000..8b961bc1c7e
--- /dev/null
+++ b/db/post_migrate/20220913082728_drop_index_cadence_create_iterations_automation.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class DropIndexCadenceCreateIterationsAutomation < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = 'cadence_create_iterations_automation'
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name :iterations_cadences, INDEX_NAME
+ end
+
+ def down
+ execute(
+ <<-SQL
+ CREATE INDEX CONCURRENTLY #{INDEX_NAME} ON iterations_cadences
+ USING BTREE(automatic, duration_in_weeks, (DATE ((COALESCE("iterations_cadences"."last_run_date", DATE('01-01-1970')) + "iterations_cadences"."duration_in_weeks" * INTERVAL '1 week'))))
+ WHERE duration_in_weeks IS NOT NULL
+ SQL
+ )
+ end
+end
diff --git a/db/post_migrate/20220913083015_clean_up_rename_iterations_cadences_last_run_date_to_next_run_date.rb b/db/post_migrate/20220913083015_clean_up_rename_iterations_cadences_last_run_date_to_next_run_date.rb
new file mode 100644
index 00000000000..7618e1841bd
--- /dev/null
+++ b/db/post_migrate/20220913083015_clean_up_rename_iterations_cadences_last_run_date_to_next_run_date.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class CleanUpRenameIterationsCadencesLastRunDateToNextRunDate < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ cleanup_concurrent_column_rename :iterations_cadences, :last_run_date, :next_run_date
+ end
+
+ def down
+ undo_cleanup_concurrent_column_rename :iterations_cadences, :last_run_date, :next_run_date
+ end
+end
diff --git a/db/post_migrate/20220914093408_add_unique_id_partition_id_index_to_ci_build_metadata.rb b/db/post_migrate/20220914093408_add_unique_id_partition_id_index_to_ci_build_metadata.rb
new file mode 100644
index 00000000000..1f40118c809
--- /dev/null
+++ b/db/post_migrate/20220914093408_add_unique_id_partition_id_index_to_ci_build_metadata.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class AddUniqueIdPartitionIdIndexToCiBuildMetadata < Gitlab::Database::Migration[2.0]
+ TABLE_NAME = :ci_builds_metadata
+ INDEX_NAME = :index_ci_builds_metadata_on_id_partition_id_unique
+
+ def up
+ prepare_async_index(TABLE_NAME, %i[id partition_id], unique: true, name: INDEX_NAME)
+ end
+
+ def down
+ unprepare_async_index(:ci_builds_metadata, %i[id partition_id], name: INDEX_NAME)
+ end
+end
diff --git a/db/post_migrate/20220915103831_add_unique_build_id_partition_id_index_to_ci_build_metadata.rb b/db/post_migrate/20220915103831_add_unique_build_id_partition_id_index_to_ci_build_metadata.rb
new file mode 100644
index 00000000000..74b60390057
--- /dev/null
+++ b/db/post_migrate/20220915103831_add_unique_build_id_partition_id_index_to_ci_build_metadata.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class AddUniqueBuildIdPartitionIdIndexToCiBuildMetadata < Gitlab::Database::Migration[2.0]
+ TABLE_NAME = :ci_builds_metadata
+ INDEX_NAME = :index_ci_builds_metadata_on_build_id_partition_id_unique
+
+ def up
+ prepare_async_index(TABLE_NAME, %i[build_id partition_id], unique: true, name: INDEX_NAME)
+ end
+
+ def down
+ unprepare_async_index(:ci_builds_metadata, %i[build_id partition_id], name: INDEX_NAME)
+ end
+end
diff --git a/db/post_migrate/20220916112841_remove_unused_aggregation_columns.rb b/db/post_migrate/20220916112841_remove_unused_aggregation_columns.rb
new file mode 100644
index 00000000000..f5333c84042
--- /dev/null
+++ b/db/post_migrate/20220916112841_remove_unused_aggregation_columns.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+class RemoveUnusedAggregationColumns < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ def up
+ with_lock_retries do
+ remove_column :analytics_cycle_analytics_aggregations, :last_full_run_processed_records
+ remove_column :analytics_cycle_analytics_aggregations, :last_full_run_runtimes_in_seconds
+ remove_column :analytics_cycle_analytics_aggregations, :last_full_run_issues_updated_at
+ remove_column :analytics_cycle_analytics_aggregations, :last_full_run_mrs_updated_at
+ remove_column :analytics_cycle_analytics_aggregations, :last_full_run_issues_id
+ remove_column :analytics_cycle_analytics_aggregations, :last_full_run_merge_requests_id
+ end
+ end
+
+ def down
+ with_lock_retries do
+ add_column(:analytics_cycle_analytics_aggregations,
+ :last_full_run_processed_records,
+ :integer,
+ array: true,
+ default: [],
+ null: false,
+ if_not_exists: true)
+ add_column(:analytics_cycle_analytics_aggregations,
+ :last_full_run_runtimes_in_seconds,
+ :integer,
+ array: true,
+ default: [],
+ null: false,
+ if_not_exists: true)
+ add_column(:analytics_cycle_analytics_aggregations,
+ :last_full_run_issues_updated_at,
+ :datetime_with_timezone,
+ if_not_exists: true)
+ add_column(:analytics_cycle_analytics_aggregations,
+ :last_full_run_mrs_updated_at,
+ :datetime_with_timezone,
+ if_not_exists: true)
+ add_column(:analytics_cycle_analytics_aggregations,
+ :last_full_run_issues_id,
+ :integer,
+ if_not_exists: true)
+ add_column(:analytics_cycle_analytics_aggregations,
+ :last_full_run_merge_requests_id,
+ :integer,
+ if_not_exists: true)
+ end
+
+ add_check_constraint(:analytics_cycle_analytics_aggregations,
+ 'CARDINALITY(last_full_run_runtimes_in_seconds) <= 10',
+ 'chk_rails_7810292ec9')
+
+ add_check_constraint(:analytics_cycle_analytics_aggregations,
+ 'CARDINALITY(last_full_run_processed_records) <= 10',
+ 'chk_rails_8b9e89687c')
+ end
+end
diff --git a/db/schema_migrations/20220406193806 b/db/schema_migrations/20220406193806
new file mode 100644
index 00000000000..a5dfed18303
--- /dev/null
+++ b/db/schema_migrations/20220406193806
@@ -0,0 +1 @@
+f2ed979f3af7aec03defc737add2e5d5bf4aad080d501003744ee42f902074d5 \ No newline at end of file
diff --git a/db/schema_migrations/20220601110011 b/db/schema_migrations/20220601110011
new file mode 100644
index 00000000000..64c76649095
--- /dev/null
+++ b/db/schema_migrations/20220601110011
@@ -0,0 +1 @@
+9dc41d0d5f1c87f27327b254c955eada4fcc5c6158c513128e6fbdadd6c34932 \ No newline at end of file
diff --git a/db/schema_migrations/20220603125200 b/db/schema_migrations/20220603125200
new file mode 100644
index 00000000000..5da1d1992ab
--- /dev/null
+++ b/db/schema_migrations/20220603125200
@@ -0,0 +1 @@
+7631f2c1f9b2647ae6de47675305a2d5c1b213229c85b6f161412f83884bad87 \ No newline at end of file
diff --git a/db/schema_migrations/20220606054503 b/db/schema_migrations/20220606054503
new file mode 100644
index 00000000000..1ec25932ece
--- /dev/null
+++ b/db/schema_migrations/20220606054503
@@ -0,0 +1 @@
+ed815f5e2766913ded3479c2cdc8a885ab7164ae280e309cba34394668392a2b \ No newline at end of file
diff --git a/db/schema_migrations/20220606080509 b/db/schema_migrations/20220606080509
new file mode 100644
index 00000000000..7403068a8da
--- /dev/null
+++ b/db/schema_migrations/20220606080509
@@ -0,0 +1 @@
+3afc50d92878da71453cfb23ad29d16123e4986e3304aff62013f4655b065d38 \ No newline at end of file
diff --git a/db/schema_migrations/20220615154500 b/db/schema_migrations/20220615154500
new file mode 100644
index 00000000000..36e35ec7d3a
--- /dev/null
+++ b/db/schema_migrations/20220615154500
@@ -0,0 +1 @@
+fd138239f6970b892fdb8190fb65b3364bb9ba5396100ba3d5d695eef6436dcf \ No newline at end of file
diff --git a/db/schema_migrations/20220616171355 b/db/schema_migrations/20220616171355
new file mode 100644
index 00000000000..cd212025f70
--- /dev/null
+++ b/db/schema_migrations/20220616171355
@@ -0,0 +1 @@
+63ec85b4f8b7eb15c232c4a25c1e63027c38c23caf81a89c4d05227a6be00e4b \ No newline at end of file
diff --git a/db/schema_migrations/20220706145113 b/db/schema_migrations/20220706145113
new file mode 100644
index 00000000000..8ed19a92025
--- /dev/null
+++ b/db/schema_migrations/20220706145113
@@ -0,0 +1 @@
+e37da383a2e69e5e3157180b33017fc64af6ee009fc3dd317ae69931d37c6350 \ No newline at end of file
diff --git a/db/schema_migrations/20220707192420 b/db/schema_migrations/20220707192420
new file mode 100644
index 00000000000..2122f9ed954
--- /dev/null
+++ b/db/schema_migrations/20220707192420
@@ -0,0 +1 @@
+b3d06405e0e0398579077d255901bed4b6f86f89a93dae8e97ddec9c2c496837 \ No newline at end of file
diff --git a/db/schema_migrations/20220711142148 b/db/schema_migrations/20220711142148
new file mode 100644
index 00000000000..709ce89616a
--- /dev/null
+++ b/db/schema_migrations/20220711142148
@@ -0,0 +1 @@
+d7062e116066ac922d42f43ef8ee6783d6bc3a30d6c36b4d70788ce47bba076f \ No newline at end of file
diff --git a/db/schema_migrations/20220726171440 b/db/schema_migrations/20220726171440
new file mode 100644
index 00000000000..3bd56cb8346
--- /dev/null
+++ b/db/schema_migrations/20220726171440
@@ -0,0 +1 @@
+5fce175152ab9c954b35d612800b381634248fe0c02b5dfc06c4650b8df9e787 \ No newline at end of file
diff --git a/db/schema_migrations/20220726171450 b/db/schema_migrations/20220726171450
new file mode 100644
index 00000000000..76a5597f9fe
--- /dev/null
+++ b/db/schema_migrations/20220726171450
@@ -0,0 +1 @@
+86d469a9dc2d22451728d310bd2b830c5cdb69033bdf6b5fe30d45c6c6b8ae4b \ No newline at end of file
diff --git a/db/schema_migrations/20220726225114 b/db/schema_migrations/20220726225114
new file mode 100644
index 00000000000..32f1eb44d17
--- /dev/null
+++ b/db/schema_migrations/20220726225114
@@ -0,0 +1 @@
+24e39665566d86ddca2bfc134fd37e11840edc9bc34add077e39fa7e5b8272d5 \ No newline at end of file
diff --git a/db/schema_migrations/20220801155858 b/db/schema_migrations/20220801155858
new file mode 100644
index 00000000000..16e0ee3a0a4
--- /dev/null
+++ b/db/schema_migrations/20220801155858
@@ -0,0 +1 @@
+aa09e7e3cdc7c7ee011b92a1ccbebdddefd9dfc2717af03a40073c6bc5be4001 \ No newline at end of file
diff --git a/db/schema_migrations/20220803004853 b/db/schema_migrations/20220803004853
new file mode 100644
index 00000000000..59f9dcd0d50
--- /dev/null
+++ b/db/schema_migrations/20220803004853
@@ -0,0 +1 @@
+39f8ae1258fa9cab98e5231d5adfe7fdf648b50fba29790aaa46786f2b2f6f04 \ No newline at end of file
diff --git a/db/schema_migrations/20220803235114 b/db/schema_migrations/20220803235114
new file mode 100644
index 00000000000..469005b8bef
--- /dev/null
+++ b/db/schema_migrations/20220803235114
@@ -0,0 +1 @@
+a669aca9370ecd086b582164e68366ca459754b26e096301c2dc7121a7e9ab58 \ No newline at end of file
diff --git a/db/schema_migrations/20220809002011 b/db/schema_migrations/20220809002011
new file mode 100644
index 00000000000..952c468b1cb
--- /dev/null
+++ b/db/schema_migrations/20220809002011
@@ -0,0 +1 @@
+12bc6c4a89c4362024d66a21690412f8946b8f6eaf9dc09cccaf8d54b7f45b17 \ No newline at end of file
diff --git a/db/schema_migrations/20220809214730 b/db/schema_migrations/20220809214730
new file mode 100644
index 00000000000..0358f9b1798
--- /dev/null
+++ b/db/schema_migrations/20220809214730
@@ -0,0 +1 @@
+ac9e478c7aaa351059d38dddf9a94eda7481a2bbcd4e9e5361fffb0c999be945 \ No newline at end of file
diff --git a/db/schema_migrations/20220809223215 b/db/schema_migrations/20220809223215
new file mode 100644
index 00000000000..84f5e9314a5
--- /dev/null
+++ b/db/schema_migrations/20220809223215
@@ -0,0 +1 @@
+1f383c2db106880e8a909b9292023ae099e224d733c87e356c61038bc84cf702 \ No newline at end of file
diff --git a/db/schema_migrations/20220815061621 b/db/schema_migrations/20220815061621
new file mode 100644
index 00000000000..66fc6a68014
--- /dev/null
+++ b/db/schema_migrations/20220815061621
@@ -0,0 +1 @@
+60a7782e9eaed833362e314fe3ae35f881ee051d9b529c59638833ce92d2db2d \ No newline at end of file
diff --git a/db/schema_migrations/20220815152905 b/db/schema_migrations/20220815152905
new file mode 100644
index 00000000000..9e71fe96edc
--- /dev/null
+++ b/db/schema_migrations/20220815152905
@@ -0,0 +1 @@
+8494a4a9c708ddfc63d86891b83f01c0883d8b88ebea2177980780a381d74704 \ No newline at end of file
diff --git a/db/schema_migrations/20220816075638 b/db/schema_migrations/20220816075638
new file mode 100644
index 00000000000..9f4e1fbc3ee
--- /dev/null
+++ b/db/schema_migrations/20220816075638
@@ -0,0 +1 @@
+78e03575edb66cfaeea75f2ff653efc77156ca9e0b1ea0fa989172c3caa0b195 \ No newline at end of file
diff --git a/db/schema_migrations/20220816135816 b/db/schema_migrations/20220816135816
new file mode 100644
index 00000000000..ff2b64004dd
--- /dev/null
+++ b/db/schema_migrations/20220816135816
@@ -0,0 +1 @@
+47dcd45d2b9c35c4e3ca707d54a983e8321dd86b2b66d4bccca9001884bc6d13 \ No newline at end of file
diff --git a/db/schema_migrations/20220816163444 b/db/schema_migrations/20220816163444
new file mode 100644
index 00000000000..83429df6a43
--- /dev/null
+++ b/db/schema_migrations/20220816163444
@@ -0,0 +1 @@
+acb8985c03358890280d3a0bbd52215896461cfdd7b4379b488a0aa87ed8182f \ No newline at end of file
diff --git a/db/schema_migrations/20220817122907 b/db/schema_migrations/20220817122907
new file mode 100644
index 00000000000..fb6951e19d5
--- /dev/null
+++ b/db/schema_migrations/20220817122907
@@ -0,0 +1 @@
+4db4f50d2e23527516eccdeae60059803df7add21ca7a2c40f1670dba9744496 \ No newline at end of file
diff --git a/db/schema_migrations/20220818095225 b/db/schema_migrations/20220818095225
new file mode 100644
index 00000000000..9f420931b9d
--- /dev/null
+++ b/db/schema_migrations/20220818095225
@@ -0,0 +1 @@
+ec31d14ce1a9f7b08985c2d304ab768a41139e81b694dcb1ec920623201504e6 \ No newline at end of file
diff --git a/db/schema_migrations/20220818132108 b/db/schema_migrations/20220818132108
new file mode 100644
index 00000000000..77683e61f2e
--- /dev/null
+++ b/db/schema_migrations/20220818132108
@@ -0,0 +1 @@
+7abea29f31054d1e0337d3fa434f55cc1c354701da89e257c764b85cd2cc2768 \ No newline at end of file
diff --git a/db/schema_migrations/20220819153725 b/db/schema_migrations/20220819153725
new file mode 100644
index 00000000000..617303214e4
--- /dev/null
+++ b/db/schema_migrations/20220819153725
@@ -0,0 +1 @@
+5819791e71e9118680e24eceef92364c78ed51dda375db9902f693147ddd9765 \ No newline at end of file
diff --git a/db/schema_migrations/20220819162852 b/db/schema_migrations/20220819162852
new file mode 100644
index 00000000000..b1d1fcb4c70
--- /dev/null
+++ b/db/schema_migrations/20220819162852
@@ -0,0 +1 @@
+1e0109c4e1a0512864f3ed16d0a9bc82b40b5c6fb1586acaffe18191821df18f \ No newline at end of file
diff --git a/db/schema_migrations/20220820221036 b/db/schema_migrations/20220820221036
new file mode 100644
index 00000000000..6f7c4059487
--- /dev/null
+++ b/db/schema_migrations/20220820221036
@@ -0,0 +1 @@
+16825936e8e6a4f0a1f001a83ecf81f180ee2eb15589eebe821fee2706456cef \ No newline at end of file
diff --git a/db/schema_migrations/20220822071909 b/db/schema_migrations/20220822071909
new file mode 100644
index 00000000000..fd8af68d1ee
--- /dev/null
+++ b/db/schema_migrations/20220822071909
@@ -0,0 +1 @@
+60a72830780190214d6c86fc2d07dc0fc138f6cc258689c1d106bb456b130047 \ No newline at end of file
diff --git a/db/schema_migrations/20220822090656 b/db/schema_migrations/20220822090656
new file mode 100644
index 00000000000..bc1ec6d44b9
--- /dev/null
+++ b/db/schema_migrations/20220822090656
@@ -0,0 +1 @@
+696550615046e26d4012d8b5a5fb741d85c23d4d0d08a4a781da0123c0543de1 \ No newline at end of file
diff --git a/db/schema_migrations/20220822094804 b/db/schema_migrations/20220822094804
new file mode 100644
index 00000000000..6e4e3b0d5e1
--- /dev/null
+++ b/db/schema_migrations/20220822094804
@@ -0,0 +1 @@
+035e918bcb674fdf1300a5bccbad87806311e6de8589f2db57d7af9cd0108ee9 \ No newline at end of file
diff --git a/db/schema_migrations/20220822102651 b/db/schema_migrations/20220822102651
new file mode 100644
index 00000000000..851535d2924
--- /dev/null
+++ b/db/schema_migrations/20220822102651
@@ -0,0 +1 @@
+2bf5f851ee8919f2306a36ae299cd3c30943d5cc3356981bab2091ff104ef127 \ No newline at end of file
diff --git a/db/schema_migrations/20220822103638 b/db/schema_migrations/20220822103638
new file mode 100644
index 00000000000..5633f697b1b
--- /dev/null
+++ b/db/schema_migrations/20220822103638
@@ -0,0 +1 @@
+9aee5b0e3475736170d7169fd3c8ac9933d976ee69a2769dea29ee4bc553af27 \ No newline at end of file
diff --git a/db/schema_migrations/20220823084747 b/db/schema_migrations/20220823084747
new file mode 100644
index 00000000000..d748dee5916
--- /dev/null
+++ b/db/schema_migrations/20220823084747
@@ -0,0 +1 @@
+f192ab50bed6dd03ae9c9c51c89d870ad6d2d1cbae129ed2da31fc061b9901d4 \ No newline at end of file
diff --git a/db/schema_migrations/20220824114218 b/db/schema_migrations/20220824114218
new file mode 100644
index 00000000000..bddb1a00d0b
--- /dev/null
+++ b/db/schema_migrations/20220824114218
@@ -0,0 +1 @@
+7674883ca0ee06d9e70841ca8e01a8e4e74eb5958797032a134afa6790699c86 \ No newline at end of file
diff --git a/db/schema_migrations/20220824175648 b/db/schema_migrations/20220824175648
new file mode 100644
index 00000000000..7a54d9c2a41
--- /dev/null
+++ b/db/schema_migrations/20220824175648
@@ -0,0 +1 @@
+5f2c56f9dd6f7ddbf34718f37fc9c37dfd9afeeae9cd2efb9fd465874b6ea8c0 \ No newline at end of file
diff --git a/db/schema_migrations/20220824194103 b/db/schema_migrations/20220824194103
new file mode 100644
index 00000000000..388bc5dfe66
--- /dev/null
+++ b/db/schema_migrations/20220824194103
@@ -0,0 +1 @@
+5bc756cf41923c2287c73e298d21ba49bae2ff6edad1629d078d192cb85dc5b8 \ No newline at end of file
diff --git a/db/schema_migrations/20220825061250 b/db/schema_migrations/20220825061250
new file mode 100644
index 00000000000..62ce31a672a
--- /dev/null
+++ b/db/schema_migrations/20220825061250
@@ -0,0 +1 @@
+0338843ad56b423559e613f00df205122b4f6db194cf49712b2ff46b2ad030e0 \ No newline at end of file
diff --git a/db/schema_migrations/20220825105631 b/db/schema_migrations/20220825105631
new file mode 100644
index 00000000000..6d6691cc6a4
--- /dev/null
+++ b/db/schema_migrations/20220825105631
@@ -0,0 +1 @@
+2e5f7b79076a35fdb61aec46dea27d45f81e47c20f962f12b494fc7a8c714813 \ No newline at end of file
diff --git a/db/schema_migrations/20220825134827 b/db/schema_migrations/20220825134827
new file mode 100644
index 00000000000..256a203e005
--- /dev/null
+++ b/db/schema_migrations/20220825134827
@@ -0,0 +1 @@
+f70447919b6346e3bcdc029e1493166efd28ae562b94013d80ac280e19787e2a \ No newline at end of file
diff --git a/db/schema_migrations/20220825142324 b/db/schema_migrations/20220825142324
new file mode 100644
index 00000000000..98f8863d3d7
--- /dev/null
+++ b/db/schema_migrations/20220825142324
@@ -0,0 +1 @@
+9df1108b41fdb4546d4c62edffba2a386bcdf486749096b3fb49d289e5c9698d \ No newline at end of file
diff --git a/db/schema_migrations/20220826165048 b/db/schema_migrations/20220826165048
new file mode 100644
index 00000000000..0539118356d
--- /dev/null
+++ b/db/schema_migrations/20220826165048
@@ -0,0 +1 @@
+75cb9d7b4a0bc8ad26b3bf6bf41a4414bcc4307607de058fc35fe4ece7009423 \ No newline at end of file
diff --git a/db/schema_migrations/20220826175058 b/db/schema_migrations/20220826175058
new file mode 100644
index 00000000000..55449ae1047
--- /dev/null
+++ b/db/schema_migrations/20220826175058
@@ -0,0 +1 @@
+2553878c425173fc41e64723814d4bca8f3f59f98479080e74a85f327412f3d4 \ No newline at end of file
diff --git a/db/schema_migrations/20220828094411 b/db/schema_migrations/20220828094411
new file mode 100644
index 00000000000..50acfab59b9
--- /dev/null
+++ b/db/schema_migrations/20220828094411
@@ -0,0 +1 @@
+7373697e5064a5ecca5881e7b98a30deba033bf8d79d2121cd17200f72815252 \ No newline at end of file
diff --git a/db/schema_migrations/20220828131848 b/db/schema_migrations/20220828131848
new file mode 100644
index 00000000000..0cd8dbbebaa
--- /dev/null
+++ b/db/schema_migrations/20220828131848
@@ -0,0 +1 @@
+d38668a9110a69f12c4d60886ace04da4f6dd7f250763a888d3c428a74032b7d \ No newline at end of file
diff --git a/db/schema_migrations/20220829183356 b/db/schema_migrations/20220829183356
new file mode 100644
index 00000000000..087a8a8ab6b
--- /dev/null
+++ b/db/schema_migrations/20220829183356
@@ -0,0 +1 @@
+4d8be5080046eff9c3736cd2494c02b2d2cb1eeea2753479617cb344bc5b1cbb \ No newline at end of file
diff --git a/db/schema_migrations/20220830051704 b/db/schema_migrations/20220830051704
new file mode 100644
index 00000000000..5785862da4f
--- /dev/null
+++ b/db/schema_migrations/20220830051704
@@ -0,0 +1 @@
+aa0b767ad0e38500e0eef83d5c8306054952363166f8cc2076ce48feeac1b0e1 \ No newline at end of file
diff --git a/db/schema_migrations/20220830061704 b/db/schema_migrations/20220830061704
new file mode 100644
index 00000000000..7a0db1acc65
--- /dev/null
+++ b/db/schema_migrations/20220830061704
@@ -0,0 +1 @@
+badc3556e1dea545bbf8b55fb33065f45598df9b3fda74bffd28e89d7485e0b4 \ No newline at end of file
diff --git a/db/schema_migrations/20220830071704 b/db/schema_migrations/20220830071704
new file mode 100644
index 00000000000..bc9d7fd0f8b
--- /dev/null
+++ b/db/schema_migrations/20220830071704
@@ -0,0 +1 @@
+85e401f0920c6eb13b6756f191ccdf70494ca40f8133f05bbd5f23ba295b115d \ No newline at end of file
diff --git a/db/schema_migrations/20220830082928 b/db/schema_migrations/20220830082928
new file mode 100644
index 00000000000..9c7cf011ab7
--- /dev/null
+++ b/db/schema_migrations/20220830082928
@@ -0,0 +1 @@
+4d7bde950a405f424c0bf3828d21e6bfd16746e091e177abfb397114c5b5b53c \ No newline at end of file
diff --git a/db/schema_migrations/20220830114228 b/db/schema_migrations/20220830114228
new file mode 100644
index 00000000000..44b26221fd5
--- /dev/null
+++ b/db/schema_migrations/20220830114228
@@ -0,0 +1 @@
+fad5bab727bdaed1d17950d320baecd995dcc8a91816e2cfcdff6d1b393c637d \ No newline at end of file
diff --git a/db/schema_migrations/20220830172142 b/db/schema_migrations/20220830172142
new file mode 100644
index 00000000000..3db3c5f4948
--- /dev/null
+++ b/db/schema_migrations/20220830172142
@@ -0,0 +1 @@
+77d17e190cc1b879960763ef32458480897e3da9483503d99c18b5aacd080ce3 \ No newline at end of file
diff --git a/db/schema_migrations/20220831021358 b/db/schema_migrations/20220831021358
new file mode 100644
index 00000000000..186bfbdb4c3
--- /dev/null
+++ b/db/schema_migrations/20220831021358
@@ -0,0 +1 @@
+a1bca159b3ea6abbe3f3178bb1106b3b1886de74dfcdf0a41ec8a48ac6bd421a \ No newline at end of file
diff --git a/db/schema_migrations/20220831132802 b/db/schema_migrations/20220831132802
new file mode 100644
index 00000000000..542bf0c240b
--- /dev/null
+++ b/db/schema_migrations/20220831132802
@@ -0,0 +1 @@
+ce0fdbed5966929816028cdd27f597ebb722ff0058d4e78b700a96952dd1274f \ No newline at end of file
diff --git a/db/schema_migrations/20220831182105 b/db/schema_migrations/20220831182105
new file mode 100644
index 00000000000..6f4b0f46ff1
--- /dev/null
+++ b/db/schema_migrations/20220831182105
@@ -0,0 +1 @@
+80828666cac381dde65dc208764b6e1c7fe703b63c708410f72afdd33886fc60 \ No newline at end of file
diff --git a/db/schema_migrations/20220901035722 b/db/schema_migrations/20220901035722
new file mode 100644
index 00000000000..aa9ea1cdf21
--- /dev/null
+++ b/db/schema_migrations/20220901035722
@@ -0,0 +1 @@
+afcbf032220e9e40ab6ae25d6ac8ea9df7f46649bf70219be9b206af6d9d0c7c \ No newline at end of file
diff --git a/db/schema_migrations/20220901035725 b/db/schema_migrations/20220901035725
new file mode 100644
index 00000000000..3c60c0188a2
--- /dev/null
+++ b/db/schema_migrations/20220901035725
@@ -0,0 +1 @@
+877ff6aab260278dfa3e886f093f34ee8004bbdaec2aabc12cebee37a879fd8d \ No newline at end of file
diff --git a/db/schema_migrations/20220901071310 b/db/schema_migrations/20220901071310
new file mode 100644
index 00000000000..9134d6669a0
--- /dev/null
+++ b/db/schema_migrations/20220901071310
@@ -0,0 +1 @@
+dee16fbf6edef6da0c9c55b0dca290bc1a1939b8d747ace6594a4d73a70adc7d \ No newline at end of file
diff --git a/db/schema_migrations/20220901071355 b/db/schema_migrations/20220901071355
new file mode 100644
index 00000000000..5e8681b9ff0
--- /dev/null
+++ b/db/schema_migrations/20220901071355
@@ -0,0 +1 @@
+3fe94b8d0102c5b5ed7824477cf4a535e42da9a232a320f770442530c77eb960 \ No newline at end of file
diff --git a/db/schema_migrations/20220901073300 b/db/schema_migrations/20220901073300
new file mode 100644
index 00000000000..47cba2c6c05
--- /dev/null
+++ b/db/schema_migrations/20220901073300
@@ -0,0 +1 @@
+92ca7bd3f150c9d447e6ab2152b7039379fece41bdef85addcf59b464dc95eb8 \ No newline at end of file
diff --git a/db/schema_migrations/20220901124637 b/db/schema_migrations/20220901124637
new file mode 100644
index 00000000000..b5a08a248e0
--- /dev/null
+++ b/db/schema_migrations/20220901124637
@@ -0,0 +1 @@
+59ea43b60e0fb009823d82e99494a7fcb31eeaddc0a6ccbf43009977cdd32526 \ No newline at end of file
diff --git a/db/schema_migrations/20220901131828 b/db/schema_migrations/20220901131828
new file mode 100644
index 00000000000..b41fef9415b
--- /dev/null
+++ b/db/schema_migrations/20220901131828
@@ -0,0 +1 @@
+c32756c482bdda948f911d0405d2373673041c57ebc514cfc5f172ba6fda9185 \ No newline at end of file
diff --git a/db/schema_migrations/20220901184106 b/db/schema_migrations/20220901184106
new file mode 100644
index 00000000000..d529617762a
--- /dev/null
+++ b/db/schema_migrations/20220901184106
@@ -0,0 +1 @@
+3934393670a67a38e0e558f7be2cb2b2a51d268c49f5992624dedfb2bc826ee4 \ No newline at end of file
diff --git a/db/schema_migrations/20220901184246 b/db/schema_migrations/20220901184246
new file mode 100644
index 00000000000..556f3ff1b3e
--- /dev/null
+++ b/db/schema_migrations/20220901184246
@@ -0,0 +1 @@
+fe06d38ab5a86850e5b915243dd70d0fe7fef9a61d9bd54c71651aa8eb1eb0e3 \ No newline at end of file
diff --git a/db/schema_migrations/20220901212027 b/db/schema_migrations/20220901212027
new file mode 100644
index 00000000000..8112c3492eb
--- /dev/null
+++ b/db/schema_migrations/20220901212027
@@ -0,0 +1 @@
+3e29afa3670370b8f5801523711d0689f1228a880b1941c44798f4bc76bedbb0 \ No newline at end of file
diff --git a/db/schema_migrations/20220902065314 b/db/schema_migrations/20220902065314
new file mode 100644
index 00000000000..8197a41403d
--- /dev/null
+++ b/db/schema_migrations/20220902065314
@@ -0,0 +1 @@
+d1ca445a17c742d435cba3d898e61242a3df9c92caeadecba147fce858d8cb80 \ No newline at end of file
diff --git a/db/schema_migrations/20220902065316 b/db/schema_migrations/20220902065316
new file mode 100644
index 00000000000..e9c3598206e
--- /dev/null
+++ b/db/schema_migrations/20220902065316
@@ -0,0 +1 @@
+910d87fbab226671b8e12b236be43970f6b2a3083f30df9586b3f8edf779f4af \ No newline at end of file
diff --git a/db/schema_migrations/20220902065317 b/db/schema_migrations/20220902065317
new file mode 100644
index 00000000000..fa60ee97fef
--- /dev/null
+++ b/db/schema_migrations/20220902065317
@@ -0,0 +1 @@
+11c65391a6744d7d7c303c6593dafa8e6dca392675974a2a1df2c164afbd4fe1 \ No newline at end of file
diff --git a/db/schema_migrations/20220902065558 b/db/schema_migrations/20220902065558
new file mode 100644
index 00000000000..2886e656d41
--- /dev/null
+++ b/db/schema_migrations/20220902065558
@@ -0,0 +1 @@
+cce779cc52b2bb175ccd3d07ac6a7df3711ae362fa0a5004bfc58fa1eb440e1f \ No newline at end of file
diff --git a/db/schema_migrations/20220902065611 b/db/schema_migrations/20220902065611
new file mode 100644
index 00000000000..365cb0f6194
--- /dev/null
+++ b/db/schema_migrations/20220902065611
@@ -0,0 +1 @@
+8ec0cc23559ba1b83042bed4abf8c47487ecb999fa66e602fbf4a9edac0569ec \ No newline at end of file
diff --git a/db/schema_migrations/20220902065623 b/db/schema_migrations/20220902065623
new file mode 100644
index 00000000000..cf75e086f31
--- /dev/null
+++ b/db/schema_migrations/20220902065623
@@ -0,0 +1 @@
+4f2076138e65849d60cf093f140afa1abaa7beea4d6c95048e6743168a7f17a9 \ No newline at end of file
diff --git a/db/schema_migrations/20220902065635 b/db/schema_migrations/20220902065635
new file mode 100644
index 00000000000..bd131598d78
--- /dev/null
+++ b/db/schema_migrations/20220902065635
@@ -0,0 +1 @@
+49a86fa87974f2c0cdc5a38726ab792f70c43e7f215495323d0999fd9f6e45f6 \ No newline at end of file
diff --git a/db/schema_migrations/20220902065647 b/db/schema_migrations/20220902065647
new file mode 100644
index 00000000000..31ee9352fe6
--- /dev/null
+++ b/db/schema_migrations/20220902065647
@@ -0,0 +1 @@
+812f25371d731d03bd4727328ad0daaf954595e24a314dd5f1adccdc3a4532c4 \ No newline at end of file
diff --git a/db/schema_migrations/20220902111016 b/db/schema_migrations/20220902111016
new file mode 100644
index 00000000000..33ea3c75bba
--- /dev/null
+++ b/db/schema_migrations/20220902111016
@@ -0,0 +1 @@
+13a36d34ff1c812a8993be87721b9d9472c26cfb6fcd8f56e9aa3c59d97183a9 \ No newline at end of file
diff --git a/db/schema_migrations/20220902111038 b/db/schema_migrations/20220902111038
new file mode 100644
index 00000000000..ecfad43fff1
--- /dev/null
+++ b/db/schema_migrations/20220902111038
@@ -0,0 +1 @@
+65c55f8fe4037d0e492f1c2a4ff83481a0da3ab17f6c63a56c181fba5d5822ba \ No newline at end of file
diff --git a/db/schema_migrations/20220902165931 b/db/schema_migrations/20220902165931
new file mode 100644
index 00000000000..3933f0d87fb
--- /dev/null
+++ b/db/schema_migrations/20220902165931
@@ -0,0 +1 @@
+b29e850775a327dcf6e37e25a43066a0638a55a4e0bd6b818cf496f0b97c6f82 \ No newline at end of file
diff --git a/db/schema_migrations/20220902170131 b/db/schema_migrations/20220902170131
new file mode 100644
index 00000000000..e598289a154
--- /dev/null
+++ b/db/schema_migrations/20220902170131
@@ -0,0 +1 @@
+5b1c25848e3e890fe27c3a43effce093af5f0fe42118c7976919acef84387a0a \ No newline at end of file
diff --git a/db/schema_migrations/20220902204048 b/db/schema_migrations/20220902204048
new file mode 100644
index 00000000000..c5fc6ee1448
--- /dev/null
+++ b/db/schema_migrations/20220902204048
@@ -0,0 +1 @@
+577a3808889d0e53af3c45ee38e852b8e653f7292c0144769811e4662e9c8c7b \ No newline at end of file
diff --git a/db/schema_migrations/20220904173342 b/db/schema_migrations/20220904173342
new file mode 100644
index 00000000000..951b667372c
--- /dev/null
+++ b/db/schema_migrations/20220904173342
@@ -0,0 +1 @@
+407fd539c6ff5cb8e71a2da80fe9dfb0002a45d5fce84a391b2332a653d6e09e \ No newline at end of file
diff --git a/db/schema_migrations/20220904173430 b/db/schema_migrations/20220904173430
new file mode 100644
index 00000000000..6c1750b8784
--- /dev/null
+++ b/db/schema_migrations/20220904173430
@@ -0,0 +1 @@
+28b91d351f3d23377d79116bdd268871f755675efb3df647e2bea51482e1aff8 \ No newline at end of file
diff --git a/db/schema_migrations/20220905090300 b/db/schema_migrations/20220905090300
new file mode 100644
index 00000000000..32d1ab47c9e
--- /dev/null
+++ b/db/schema_migrations/20220905090300
@@ -0,0 +1 @@
+e15a37d20202e204fcf02ab68d3b616eec0501c2c323db69ed2fb39df0d017f6 \ No newline at end of file
diff --git a/db/schema_migrations/20220905090339 b/db/schema_migrations/20220905090339
new file mode 100644
index 00000000000..52c2e038c93
--- /dev/null
+++ b/db/schema_migrations/20220905090339
@@ -0,0 +1 @@
+f3a7e9c501498b22786960694a8c1f35d2db69c8c48319f99dfb81c41aa5c6ca \ No newline at end of file
diff --git a/db/schema_migrations/20220905112710 b/db/schema_migrations/20220905112710
new file mode 100644
index 00000000000..9f20a8cc9b6
--- /dev/null
+++ b/db/schema_migrations/20220905112710
@@ -0,0 +1 @@
+85db0670a8557421a59678f19324411d61220eae12ea68f565d458a7393f6b2e \ No newline at end of file
diff --git a/db/schema_migrations/20220906074449 b/db/schema_migrations/20220906074449
new file mode 100644
index 00000000000..df657642eb3
--- /dev/null
+++ b/db/schema_migrations/20220906074449
@@ -0,0 +1 @@
+fc34cdbddc61ee9c23b790101f911d21892cf2ace34e3615b920817374c803f9 \ No newline at end of file
diff --git a/db/schema_migrations/20220906093857 b/db/schema_migrations/20220906093857
new file mode 100644
index 00000000000..a5bc36ab6ef
--- /dev/null
+++ b/db/schema_migrations/20220906093857
@@ -0,0 +1 @@
+394f346e3a93f8a6b74fd0461eb59f569c6a18f90ae653c330a38e3a3706b5f6 \ No newline at end of file
diff --git a/db/schema_migrations/20220906155105 b/db/schema_migrations/20220906155105
new file mode 100644
index 00000000000..b5aec9a8279
--- /dev/null
+++ b/db/schema_migrations/20220906155105
@@ -0,0 +1 @@
+a3eb4d190652c43f95f8823f11957064fcf097a1fd6641562a09de5ae02ceb6e \ No newline at end of file
diff --git a/db/schema_migrations/20220906204832 b/db/schema_migrations/20220906204832
new file mode 100644
index 00000000000..a63248d6221
--- /dev/null
+++ b/db/schema_migrations/20220906204832
@@ -0,0 +1 @@
+bda120b4684900c0763af116557930a77b2dfa3c3884ae7f8d4183db546fa019 \ No newline at end of file
diff --git a/db/schema_migrations/20220906212931 b/db/schema_migrations/20220906212931
new file mode 100644
index 00000000000..38f14a166e7
--- /dev/null
+++ b/db/schema_migrations/20220906212931
@@ -0,0 +1 @@
+0a6bd5578f5180fac269ffd8a78fc87b7bd95be4b0246890d5c57d79f2a856f8 \ No newline at end of file
diff --git a/db/schema_migrations/20220907124320 b/db/schema_migrations/20220907124320
new file mode 100644
index 00000000000..59077465bf9
--- /dev/null
+++ b/db/schema_migrations/20220907124320
@@ -0,0 +1 @@
+5b645f66351e9515826be882767ad07671b7b5fad7b942bc74325b05c84130ac \ No newline at end of file
diff --git a/db/schema_migrations/20220908125146 b/db/schema_migrations/20220908125146
new file mode 100644
index 00000000000..2b8475f0d70
--- /dev/null
+++ b/db/schema_migrations/20220908125146
@@ -0,0 +1 @@
+d5d264f90203ba371edcf0688d1227aa69cbf0018033d141257e4c88072ee7d7 \ No newline at end of file
diff --git a/db/schema_migrations/20220909091410 b/db/schema_migrations/20220909091410
new file mode 100644
index 00000000000..49738ad23af
--- /dev/null
+++ b/db/schema_migrations/20220909091410
@@ -0,0 +1 @@
+34e485c0c94960fc07a3f529aed749c2bbc1a72bb49d064225a37b85134f70f2 \ No newline at end of file
diff --git a/db/schema_migrations/20220909094752 b/db/schema_migrations/20220909094752
new file mode 100644
index 00000000000..4660f31f8d0
--- /dev/null
+++ b/db/schema_migrations/20220909094752
@@ -0,0 +1 @@
+2a0fb7dc05ed2949745cb85df20ad0f46f38f9efc6e7d84e559cff3f647fed9e \ No newline at end of file
diff --git a/db/schema_migrations/20220909113809 b/db/schema_migrations/20220909113809
new file mode 100644
index 00000000000..9bf2a6b0e03
--- /dev/null
+++ b/db/schema_migrations/20220909113809
@@ -0,0 +1 @@
+ec9f278411b727587be787eabfa356f3c497b1927283a85063d7067495097fe0 \ No newline at end of file
diff --git a/db/schema_migrations/20220909114220 b/db/schema_migrations/20220909114220
new file mode 100644
index 00000000000..7100121ace6
--- /dev/null
+++ b/db/schema_migrations/20220909114220
@@ -0,0 +1 @@
+2a5833aca02f3d42f0bd0c6861fb244166b9f8bd2a5b29a1d97df9b8cdea5c11 \ No newline at end of file
diff --git a/db/schema_migrations/20220912085047 b/db/schema_migrations/20220912085047
new file mode 100644
index 00000000000..7279f94eb21
--- /dev/null
+++ b/db/schema_migrations/20220912085047
@@ -0,0 +1 @@
+30d9f3352daa48f529486030e30667a1339b04e96b207be815505477ab498adb \ No newline at end of file
diff --git a/db/schema_migrations/20220912110433 b/db/schema_migrations/20220912110433
new file mode 100644
index 00000000000..0e31168be7d
--- /dev/null
+++ b/db/schema_migrations/20220912110433
@@ -0,0 +1 @@
+3bf30b096f71664f5bfae88cc21b1d7992ebcbbb6eb9eb15348bc689291bffd2 \ No newline at end of file
diff --git a/db/schema_migrations/20220912180807 b/db/schema_migrations/20220912180807
new file mode 100644
index 00000000000..77fb556b1fd
--- /dev/null
+++ b/db/schema_migrations/20220912180807
@@ -0,0 +1 @@
+6959c82221a22ac1a2aba39a1a023f227989ac26b08fc0aa5a0596f597e0098c \ No newline at end of file
diff --git a/db/schema_migrations/20220913030552 b/db/schema_migrations/20220913030552
new file mode 100644
index 00000000000..6d6a68788dc
--- /dev/null
+++ b/db/schema_migrations/20220913030552
@@ -0,0 +1 @@
+39538feebc6f7f4e1822148567ed369eee1a7ed7ee718f7e913e2b585cc0e808 \ No newline at end of file
diff --git a/db/schema_migrations/20220913030624 b/db/schema_migrations/20220913030624
new file mode 100644
index 00000000000..8a0641b4f8c
--- /dev/null
+++ b/db/schema_migrations/20220913030624
@@ -0,0 +1 @@
+baac0b236b7e91f9aacd03f3cf1ce84974f6c389529143e9b2813d9b70224e53 \ No newline at end of file
diff --git a/db/schema_migrations/20220913082728 b/db/schema_migrations/20220913082728
new file mode 100644
index 00000000000..18b538fada9
--- /dev/null
+++ b/db/schema_migrations/20220913082728
@@ -0,0 +1 @@
+0143a083e7083e9324a0e27a3a42083b56939cf841eb3d9c26d26b4b774d55d0 \ No newline at end of file
diff --git a/db/schema_migrations/20220913082930 b/db/schema_migrations/20220913082930
new file mode 100644
index 00000000000..8afd779b269
--- /dev/null
+++ b/db/schema_migrations/20220913082930
@@ -0,0 +1 @@
+3ae91ffae238c36a8e5ea021acfca8faa1c817d87078a5df9cf8213f259548a7 \ No newline at end of file
diff --git a/db/schema_migrations/20220913083015 b/db/schema_migrations/20220913083015
new file mode 100644
index 00000000000..4f6b2a9459a
--- /dev/null
+++ b/db/schema_migrations/20220913083015
@@ -0,0 +1 @@
+19012eef52669209fa487d8a72d3e4363a6588250d9cb068ce7ffed72f95ac11 \ No newline at end of file
diff --git a/db/schema_migrations/20220914005141 b/db/schema_migrations/20220914005141
new file mode 100644
index 00000000000..88859155884
--- /dev/null
+++ b/db/schema_migrations/20220914005141
@@ -0,0 +1 @@
+df7862d3bab250feb867ecf60134bbfdffdfd6ea4f3a5a9b2c7e546e0aa89e3f \ No newline at end of file
diff --git a/db/schema_migrations/20220914010233 b/db/schema_migrations/20220914010233
new file mode 100644
index 00000000000..777c73c0be6
--- /dev/null
+++ b/db/schema_migrations/20220914010233
@@ -0,0 +1 @@
+be86548616ce5b4e6f0caf6db79c49ac523766257d20c6f5465d21a0e53f46d0 \ No newline at end of file
diff --git a/db/schema_migrations/20220914093408 b/db/schema_migrations/20220914093408
new file mode 100644
index 00000000000..d18e80f8bfc
--- /dev/null
+++ b/db/schema_migrations/20220914093408
@@ -0,0 +1 @@
+3dfa9c266943018e06463b1eaf80fed67df602a9f3ee22c2054c6a05040a7382 \ No newline at end of file
diff --git a/db/schema_migrations/20220915103831 b/db/schema_migrations/20220915103831
new file mode 100644
index 00000000000..29a516c0e60
--- /dev/null
+++ b/db/schema_migrations/20220915103831
@@ -0,0 +1 @@
+86eeefedacc05cd6d4da63fd24b55af8e32f00d1c4e21b3f507d08310504adee \ No newline at end of file
diff --git a/db/schema_migrations/20220915140802 b/db/schema_migrations/20220915140802
new file mode 100644
index 00000000000..676e295aa14
--- /dev/null
+++ b/db/schema_migrations/20220915140802
@@ -0,0 +1 @@
+9b0f19a59e104f0df6abac7d58012701dcf9a031116f5cc643e407506e186cc2 \ No newline at end of file
diff --git a/db/schema_migrations/20220916112841 b/db/schema_migrations/20220916112841
new file mode 100644
index 00000000000..23013f9fdb3
--- /dev/null
+++ b/db/schema_migrations/20220916112841
@@ -0,0 +1 @@
+0bc8cd07786c950037731a0443e0d7da9c9692da39f13787b24769dbd122ba88 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index b055d831ce6..bfdb4e508f2 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -22,6 +22,40 @@ RETURN NULL;
END
$$;
+CREATE FUNCTION function_for_trigger_a645cee67576() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ NEW."service_id" := NEW."integration_id";
+ RETURN NEW;
+END
+$$;
+
+CREATE FUNCTION function_for_trigger_a87bcfdf0f0b() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ IF NEW."service_id" IS NULL AND NEW."integration_id" IS NOT NULL THEN
+ NEW."service_id" = NEW."integration_id";
+ END IF;
+
+ IF NEW."integration_id" IS NULL AND NEW."service_id" IS NOT NULL THEN
+ NEW."integration_id" = NEW."service_id";
+ END IF;
+
+ RETURN NEW;
+END
+$$;
+
+CREATE FUNCTION function_for_trigger_aca5c963d732() RETURNS trigger
+ LANGUAGE plpgsql
+ AS $$
+BEGIN
+ NEW."integration_id" := NEW."service_id";
+ RETURN NEW;
+END
+$$;
+
CREATE FUNCTION gitlab_schema_prevent_write() RETURNS trigger
LANGUAGE plpgsql
AS $$
@@ -423,6 +457,22 @@ CREATE TABLE loose_foreign_keys_deleted_records (
)
PARTITION BY LIST (partition);
+CREATE TABLE security_findings (
+ id bigint NOT NULL,
+ scan_id bigint NOT NULL,
+ scanner_id bigint NOT NULL,
+ severity smallint NOT NULL,
+ confidence smallint,
+ project_fingerprint text,
+ deduplicated boolean DEFAULT false NOT NULL,
+ uuid uuid,
+ overridden_uuid uuid,
+ partition_number integer DEFAULT 1 NOT NULL,
+ CONSTRAINT check_6c2851a8c9 CHECK ((uuid IS NOT NULL)),
+ CONSTRAINT check_b9508c6df8 CHECK ((char_length(project_fingerprint) <= 40))
+)
+PARTITION BY LIST (partition_number);
+
CREATE TABLE verification_codes (
created_at timestamp with time zone DEFAULT now() NOT NULL,
visitor_id_code text NOT NULL,
@@ -10767,18 +10817,12 @@ CREATE TABLE analytics_cycle_analytics_aggregations (
group_id bigint NOT NULL,
incremental_runtimes_in_seconds integer[] DEFAULT '{}'::integer[] NOT NULL,
incremental_processed_records integer[] DEFAULT '{}'::integer[] NOT NULL,
- last_full_run_runtimes_in_seconds integer[] DEFAULT '{}'::integer[] NOT NULL,
- last_full_run_processed_records integer[] DEFAULT '{}'::integer[] NOT NULL,
last_incremental_issues_id integer,
last_incremental_merge_requests_id integer,
- last_full_run_issues_id integer,
- last_full_run_merge_requests_id integer,
last_incremental_run_at timestamp with time zone,
last_incremental_issues_updated_at timestamp with time zone,
last_incremental_merge_requests_updated_at timestamp with time zone,
last_full_run_at timestamp with time zone,
- last_full_run_issues_updated_at timestamp with time zone,
- last_full_run_mrs_updated_at timestamp with time zone,
last_consistency_check_updated_at timestamp with time zone,
enabled boolean DEFAULT true NOT NULL,
full_runtimes_in_seconds integer[] DEFAULT '{}'::integer[] NOT NULL,
@@ -10796,8 +10840,6 @@ CREATE TABLE analytics_cycle_analytics_aggregations (
last_consistency_check_merge_requests_end_event_timestamp timestamp with time zone,
last_consistency_check_merge_requests_issuable_id bigint,
CONSTRAINT chk_rails_1ef688e577 CHECK ((cardinality(incremental_runtimes_in_seconds) <= 10)),
- CONSTRAINT chk_rails_7810292ec9 CHECK ((cardinality(last_full_run_processed_records) <= 10)),
- CONSTRAINT chk_rails_8b9e89687c CHECK ((cardinality(last_full_run_runtimes_in_seconds) <= 10)),
CONSTRAINT chk_rails_e16bf3913a CHECK ((cardinality(incremental_processed_records) <= 10)),
CONSTRAINT full_processed_records_size CHECK ((cardinality(full_processed_records) <= 10)),
CONSTRAINT full_runtimes_in_seconds_size CHECK ((cardinality(full_runtimes_in_seconds) <= 10))
@@ -11426,6 +11468,7 @@ CREATE TABLE application_settings (
inactive_projects_min_size_mb integer DEFAULT 0 NOT NULL,
inactive_projects_send_warning_email_after_months integer DEFAULT 1 NOT NULL,
delayed_group_deletion boolean DEFAULT true NOT NULL,
+ maven_package_requests_forwarding boolean DEFAULT true NOT NULL,
arkose_labs_namespace text DEFAULT 'client'::text NOT NULL,
max_export_size integer DEFAULT 0,
encrypted_slack_app_signing_secret bytea,
@@ -11456,13 +11499,20 @@ CREATE TABLE application_settings (
error_tracking_api_url text,
git_rate_limit_users_allowlist text[] DEFAULT '{}'::text[] NOT NULL,
error_tracking_access_token_encrypted text,
+ invitation_flow_enforcement boolean DEFAULT false NOT NULL,
package_registry_cleanup_policies_worker_capacity integer DEFAULT 2 NOT NULL,
deactivate_dormant_users_period integer DEFAULT 90 NOT NULL,
+ auto_ban_user_on_excessive_projects_download boolean DEFAULT false NOT NULL,
+ max_pages_custom_domains_per_project integer DEFAULT 0 NOT NULL,
+ cube_api_base_url text,
+ encrypted_cube_api_key bytea,
+ encrypted_cube_api_key_iv bytea,
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_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)),
@@ -11488,6 +11538,7 @@ CREATE TABLE application_settings (
CONSTRAINT check_7ccfe2764a CHECK ((char_length(arkose_labs_namespace) <= 255)),
CONSTRAINT check_85a39b68ff CHECK ((char_length(encrypted_ci_jwt_signing_key_iv) <= 255)),
CONSTRAINT check_8dca35398a CHECK ((char_length(public_runner_releases_url) <= 255)),
+ CONSTRAINT check_8e7df605a1 CHECK ((char_length(cube_api_base_url) <= 512)),
CONSTRAINT check_9a719834eb CHECK ((char_length(secret_detection_token_revocation_url) <= 255)),
CONSTRAINT check_9c6c447a13 CHECK ((char_length(maintenance_mode_message) <= 255)),
CONSTRAINT check_a5704163cc CHECK ((char_length(secret_detection_revocation_token_types_url) <= 255)),
@@ -12022,7 +12073,10 @@ CREATE TABLE board_group_recent_visits (
updated_at timestamp with time zone NOT NULL,
user_id integer,
board_id integer,
- group_id integer
+ group_id integer,
+ CONSTRAINT check_409f6caea4 CHECK ((user_id IS NOT NULL)),
+ CONSTRAINT check_ddc74243ef CHECK ((group_id IS NOT NULL)),
+ CONSTRAINT check_fa7711a898 CHECK ((board_id IS NOT NULL))
);
CREATE SEQUENCE board_group_recent_visits_id_seq
@@ -12055,7 +12109,10 @@ CREATE TABLE board_project_recent_visits (
updated_at timestamp with time zone NOT NULL,
user_id integer,
project_id integer,
- board_id integer
+ board_id integer,
+ CONSTRAINT check_0386e26981 CHECK ((board_id IS NOT NULL)),
+ CONSTRAINT check_d9cc9b79da CHECK ((project_id IS NOT NULL)),
+ CONSTRAINT check_df7762a99a CHECK ((user_id IS NOT NULL))
);
CREATE SEQUENCE board_project_recent_visits_id_seq
@@ -12250,7 +12307,8 @@ CREATE TABLE broadcast_messages (
broadcast_type smallint DEFAULT 1 NOT NULL,
dismissable boolean,
target_access_levels integer[] DEFAULT '{}'::integer[] NOT NULL,
- theme smallint DEFAULT 0 NOT NULL
+ theme smallint DEFAULT 0 NOT NULL,
+ namespace_id bigint
);
CREATE SEQUENCE broadcast_messages_id_seq
@@ -12579,6 +12637,7 @@ CREATE TABLE ci_builds (
scheduling_type smallint,
id bigint NOT NULL,
stage_id bigint,
+ partition_id bigint DEFAULT 100 NOT NULL,
CONSTRAINT check_1e2fbd1b39 CHECK ((lock_version IS NOT NULL))
);
@@ -12605,7 +12664,8 @@ CREATE TABLE ci_builds_metadata (
build_id bigint NOT NULL,
id bigint NOT NULL,
runtime_runner_features jsonb DEFAULT '{}'::jsonb NOT NULL,
- id_tokens jsonb DEFAULT '{}'::jsonb NOT NULL
+ id_tokens jsonb DEFAULT '{}'::jsonb NOT NULL,
+ partition_id bigint DEFAULT 100 NOT NULL
);
CREATE SEQUENCE ci_builds_metadata_id_seq
@@ -12770,6 +12830,7 @@ CREATE TABLE ci_job_artifacts (
job_id bigint NOT NULL,
locked smallint DEFAULT 2,
original_filename text,
+ partition_id bigint DEFAULT 100 NOT NULL,
CONSTRAINT check_27f0f6dbab CHECK ((file_store IS NOT NULL)),
CONSTRAINT check_85573000db CHECK ((char_length(original_filename) <= 512))
);
@@ -12875,6 +12936,21 @@ CREATE SEQUENCE ci_namespace_monthly_usages_id_seq
ALTER SEQUENCE ci_namespace_monthly_usages_id_seq OWNED BY ci_namespace_monthly_usages.id;
+CREATE TABLE ci_partitions (
+ id bigint NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL
+);
+
+CREATE SEQUENCE ci_partitions_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE ci_partitions_id_seq OWNED BY ci_partitions.id;
+
CREATE TABLE ci_pending_builds (
id bigint NOT NULL,
build_id bigint NOT NULL,
@@ -12916,6 +12992,7 @@ CREATE TABLE ci_pipeline_artifacts (
verification_retry_count smallint,
verification_checksum bytea,
verification_failure text,
+ locked smallint DEFAULT 2,
CONSTRAINT check_191b5850ec CHECK ((char_length(file) <= 255)),
CONSTRAINT check_abeeb71caf CHECK ((file IS NOT NULL)),
CONSTRAINT ci_pipeline_artifacts_verification_failure_text_limit CHECK ((char_length(verification_failure) <= 255))
@@ -13018,7 +13095,8 @@ CREATE TABLE ci_pipeline_variables (
encrypted_value_iv character varying,
pipeline_id integer NOT NULL,
variable_type smallint DEFAULT 1 NOT NULL,
- raw boolean DEFAULT true NOT NULL
+ raw boolean DEFAULT true NOT NULL,
+ partition_id bigint DEFAULT 100 NOT NULL
);
CREATE SEQUENCE ci_pipeline_variables_id_seq
@@ -13060,6 +13138,7 @@ CREATE TABLE ci_pipelines (
external_pull_request_id bigint,
ci_ref_id bigint,
locked smallint DEFAULT 1 NOT NULL,
+ partition_id bigint DEFAULT 100 NOT NULL,
CONSTRAINT check_d7e99a025e CHECK ((lock_version IS NOT NULL))
);
@@ -13364,6 +13443,7 @@ CREATE TABLE ci_stages (
lock_version integer DEFAULT 0,
"position" integer,
id bigint NOT NULL,
+ partition_id bigint DEFAULT 100 NOT NULL,
CONSTRAINT check_81b431e49b CHECK ((lock_version IS NOT NULL))
);
@@ -14757,6 +14837,21 @@ CREATE SEQUENCE dingtalk_tracker_data_id_seq
ALTER SEQUENCE dingtalk_tracker_data_id_seq OWNED BY dingtalk_tracker_data.id;
+CREATE TABLE dora_configurations (
+ id bigint NOT NULL,
+ project_id bigint NOT NULL,
+ branches_for_lead_time_for_changes text[] DEFAULT '{}'::text[] NOT NULL
+);
+
+CREATE SEQUENCE dora_configurations_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE dora_configurations_id_seq OWNED BY dora_configurations.id;
+
CREATE TABLE dora_daily_metrics (
id bigint NOT NULL,
environment_id bigint NOT NULL,
@@ -14934,7 +15029,8 @@ CREATE TABLE environments (
slug character varying NOT NULL,
auto_stop_at timestamp with time zone,
auto_delete_at timestamp with time zone,
- tier smallint
+ tier smallint,
+ merge_request_id bigint
);
CREATE SEQUENCE environments_id_seq
@@ -15704,6 +15800,24 @@ CREATE SEQUENCE geo_reset_checksum_events_id_seq
ALTER SEQUENCE geo_reset_checksum_events_id_seq OWNED BY geo_reset_checksum_events.id;
+CREATE TABLE ghost_user_migrations (
+ id bigint NOT NULL,
+ user_id bigint NOT NULL,
+ initiator_user_id bigint,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ hard_delete boolean DEFAULT false NOT NULL
+);
+
+CREATE SEQUENCE ghost_user_migrations_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE ghost_user_migrations_id_seq OWNED BY ghost_user_migrations.id;
+
CREATE TABLE gitlab_subscription_histories (
id bigint NOT NULL,
gitlab_subscription_created_at timestamp with time zone,
@@ -16709,7 +16823,6 @@ CREATE TABLE iterations_cadences (
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
start_date date,
- last_run_date date,
duration_in_weeks integer,
iterations_in_advance integer,
active boolean DEFAULT true NOT NULL,
@@ -16717,6 +16830,7 @@ CREATE TABLE iterations_cadences (
title text NOT NULL,
roll_over boolean DEFAULT false NOT NULL,
description text,
+ next_run_date date,
CONSTRAINT check_5c5d2b44bd CHECK ((char_length(description) <= 5000)),
CONSTRAINT check_fedff82d3b CHECK ((char_length(title) <= 255))
);
@@ -17378,6 +17492,22 @@ CREATE SEQUENCE merge_request_metrics_id_seq
ALTER SEQUENCE merge_request_metrics_id_seq OWNED BY merge_request_metrics.id;
+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
+);
+
+CREATE SEQUENCE merge_request_predictions_merge_request_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE merge_request_predictions_merge_request_id_seq OWNED BY merge_request_predictions.merge_request_id;
+
CREATE TABLE merge_request_reviewers (
id bigint NOT NULL,
user_id bigint NOT NULL,
@@ -17643,7 +17773,10 @@ CREATE TABLE ml_candidates (
updated_at timestamp with time zone NOT NULL,
iid uuid NOT NULL,
experiment_id bigint NOT NULL,
- user_id bigint
+ user_id bigint,
+ start_time bigint,
+ end_time bigint,
+ status smallint DEFAULT 0 NOT NULL
);
CREATE SEQUENCE ml_candidates_id_seq
@@ -17663,6 +17796,7 @@ CREATE TABLE ml_experiments (
project_id bigint NOT NULL,
user_id bigint,
name text NOT NULL,
+ deleted_on timestamp with time zone,
CONSTRAINT check_ee07a0be2c CHECK ((char_length(name) <= 255))
);
@@ -17725,7 +17859,8 @@ CREATE TABLE namespace_details (
updated_at timestamp with time zone,
cached_markdown_version integer,
description text,
- description_html text
+ description_html text,
+ free_user_cap_over_limt_notified_at timestamp with time zone
);
CREATE TABLE namespace_limits (
@@ -17781,12 +17916,14 @@ CREATE TABLE namespace_settings (
subgroup_runner_token_expiration_interval integer,
project_runner_token_expiration_interval integer,
exclude_from_free_user_cap boolean DEFAULT false NOT NULL,
+ show_diff_preview_in_email boolean DEFAULT true NOT NULL,
enabled_git_access_protocol smallint DEFAULT 0 NOT NULL,
unique_project_download_limit smallint DEFAULT 0 NOT NULL,
unique_project_download_limit_interval_in_seconds integer DEFAULT 0 NOT NULL,
project_import_level smallint DEFAULT 50 NOT NULL,
include_for_free_user_cap_preview boolean DEFAULT false NOT NULL,
unique_project_download_limit_allowlist text[] DEFAULT '{}'::text[] NOT NULL,
+ auto_ban_user_on_excessive_projects_download boolean DEFAULT false NOT NULL,
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))
);
@@ -17934,7 +18071,8 @@ CREATE TABLE notes (
resolved_by_push boolean,
review_id bigint,
confidential boolean,
- last_edited_at timestamp with time zone
+ last_edited_at timestamp with time zone,
+ internal boolean DEFAULT false NOT NULL
);
CREATE SEQUENCE notes_id_seq
@@ -18771,7 +18909,8 @@ CREATE TABLE packages_packages (
version character varying,
package_type smallint NOT NULL,
creator_id integer,
- status smallint DEFAULT 0 NOT NULL
+ status smallint DEFAULT 0 NOT NULL,
+ last_downloaded_at timestamp with time zone
);
CREATE SEQUENCE packages_packages_id_seq
@@ -18790,6 +18929,23 @@ CREATE TABLE packages_pypi_metadata (
CONSTRAINT check_379019d5da CHECK ((char_length(required_python) <= 255))
);
+CREATE TABLE packages_rpm_metadata (
+ package_id bigint NOT NULL,
+ release text DEFAULT '1'::text NOT NULL,
+ summary text DEFAULT ''::text NOT NULL,
+ description text DEFAULT ''::text NOT NULL,
+ arch text DEFAULT ''::text NOT NULL,
+ license text,
+ url text,
+ epoch integer DEFAULT 0 NOT NULL,
+ CONSTRAINT check_3798bae3d6 CHECK ((char_length(arch) <= 255)),
+ CONSTRAINT check_5d29ba59ac CHECK ((char_length(description) <= 5000)),
+ CONSTRAINT check_6e8cbd536d CHECK ((char_length(url) <= 1000)),
+ CONSTRAINT check_845ba4d7d0 CHECK ((char_length(license) <= 1000)),
+ CONSTRAINT check_b010bf4870 CHECK ((char_length(summary) <= 1000)),
+ CONSTRAINT check_c3e2fc2e89 CHECK ((char_length(release) <= 128))
+);
+
CREATE TABLE packages_rubygems_metadata (
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
@@ -19077,7 +19233,8 @@ CREATE TABLE plan_limits (
web_hook_calls_low integer DEFAULT 0 NOT NULL,
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
+ ci_max_artifact_size_cyclonedx integer DEFAULT 1 NOT NULL,
+ rpm_max_file_size bigint DEFAULT '5368709120'::bigint NOT NULL
);
CREATE SEQUENCE plan_limits_id_seq
@@ -19160,6 +19317,21 @@ CREATE VIEW postgres_autovacuum_activity AS
COMMENT ON VIEW postgres_autovacuum_activity IS 'Contains information about PostgreSQL backends currently performing autovacuum operations on the tables indicated here.';
+CREATE VIEW postgres_constraints AS
+ SELECT pg_constraint.oid,
+ pg_constraint.conname AS name,
+ pg_constraint.contype AS constraint_type,
+ pg_constraint.convalidated AS constraint_valid,
+ ( SELECT array_agg(pg_attribute.attname ORDER BY attnums.ordering) AS array_agg
+ FROM (unnest(pg_constraint.conkey) WITH ORDINALITY attnums(attnum, ordering)
+ JOIN pg_attribute ON (((pg_attribute.attnum = attnums.attnum) AND (pg_attribute.attrelid = pg_class.oid))))) AS column_names,
+ (((pg_namespace.nspname)::text || '.'::text) || (pg_class.relname)::text) AS table_identifier,
+ NULLIF(pg_constraint.conparentid, (0)::oid) AS parent_constraint_oid,
+ pg_get_constraintdef(pg_constraint.oid) AS definition
+ FROM ((pg_constraint
+ JOIN pg_class ON ((pg_constraint.conrelid = pg_class.oid)))
+ JOIN pg_namespace ON ((pg_class.relnamespace = pg_namespace.oid)));
+
CREATE VIEW postgres_foreign_keys AS
SELECT pg_constraint.oid,
pg_constraint.conname AS name,
@@ -19898,6 +20070,7 @@ CREATE TABLE project_settings (
target_platforms character varying[] DEFAULT '{}'::character varying[] NOT NULL,
enforce_auth_checks_on_uploads boolean DEFAULT true NOT NULL,
selective_code_owner_removals boolean DEFAULT false NOT NULL,
+ show_diff_preview_in_email boolean DEFAULT true NOT NULL,
CONSTRAINT check_3a03e7557a CHECK ((char_length(previous_default_branch) <= 4096)),
CONSTRAINT check_b09644994b CHECK ((char_length(squash_commit_template) <= 500)),
CONSTRAINT check_bde223416c CHECK ((show_default_award_emojis IS NOT NULL)),
@@ -19980,7 +20153,6 @@ CREATE TABLE projects (
mirror_user_id integer,
shared_runners_enabled boolean DEFAULT true NOT NULL,
runners_token character varying,
- build_coverage_regex character varying,
build_allow_git_fetch boolean DEFAULT true NOT NULL,
build_timeout integer DEFAULT 3600 NOT NULL,
mirror_trigger_builds boolean DEFAULT false NOT NULL,
@@ -20819,6 +20991,23 @@ CREATE SEQUENCE sbom_sources_id_seq
ALTER SEQUENCE sbom_sources_id_seq OWNED BY sbom_sources.id;
+CREATE TABLE sbom_vulnerable_component_versions (
+ id bigint NOT NULL,
+ vulnerability_advisory_id bigint,
+ sbom_component_version_id bigint,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL
+);
+
+CREATE SEQUENCE sbom_vulnerable_component_versions_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE sbom_vulnerable_component_versions_id_seq OWNED BY sbom_vulnerable_component_versions.id;
+
CREATE TABLE schema_migrations (
version character varying NOT NULL,
finished_at timestamp with time zone DEFAULT now()
@@ -20860,22 +21049,6 @@ CREATE SEQUENCE scim_oauth_access_tokens_id_seq
ALTER SEQUENCE scim_oauth_access_tokens_id_seq OWNED BY scim_oauth_access_tokens.id;
-CREATE TABLE security_findings (
- id bigint NOT NULL,
- scan_id bigint NOT NULL,
- scanner_id bigint NOT NULL,
- severity smallint NOT NULL,
- confidence smallint NOT NULL,
- project_fingerprint text,
- deduplicated boolean DEFAULT false NOT NULL,
- uuid uuid,
- overridden_uuid uuid,
- partition_number integer DEFAULT 1 NOT NULL,
- CONSTRAINT check_6c2851a8c9 CHECK ((uuid IS NOT NULL)),
- CONSTRAINT check_b9508c6df8 CHECK ((char_length(project_fingerprint) <= 40)),
- CONSTRAINT check_partition_number CHECK ((partition_number = 1))
-);
-
CREATE SEQUENCE security_findings_id_seq
START WITH 1
INCREMENT BY 1
@@ -21853,7 +22026,6 @@ CREATE TABLE user_details (
job_title character varying(200) DEFAULT ''::character varying NOT NULL,
bio character varying(255) DEFAULT ''::character varying NOT NULL,
webauthn_xid text,
- other_role text,
provisioned_by_group_id bigint,
pronouns text,
pronunciation text,
@@ -21862,7 +22034,6 @@ CREATE TABLE user_details (
requires_credit_card_verification boolean DEFAULT false NOT NULL,
CONSTRAINT check_245664af82 CHECK ((char_length(webauthn_xid) <= 100)),
CONSTRAINT check_a73b398c60 CHECK ((char_length(phone) <= 50)),
- CONSTRAINT check_b132136b01 CHECK ((char_length(other_role) <= 100)),
CONSTRAINT check_eeeaf8d4f0 CHECK ((char_length(pronouns) <= 50)),
CONSTRAINT check_f932ed37db CHECK ((char_length(pronunciation) <= 255))
);
@@ -22226,7 +22397,7 @@ CREATE TABLE vulnerabilities (
state smallint DEFAULT 1 NOT NULL,
severity smallint NOT NULL,
severity_overridden boolean DEFAULT false,
- confidence smallint NOT NULL,
+ confidence smallint,
confidence_overridden boolean DEFAULT false,
resolved_by_id bigint,
resolved_at timestamp with time zone,
@@ -22250,6 +22421,44 @@ CREATE SEQUENCE vulnerabilities_id_seq
ALTER SEQUENCE vulnerabilities_id_seq OWNED BY vulnerabilities.id;
+CREATE TABLE vulnerability_advisories (
+ uuid uuid NOT NULL,
+ created_at timestamp with time zone NOT NULL,
+ updated_at timestamp with time zone NOT NULL,
+ id bigint NOT NULL,
+ created_date date NOT NULL,
+ published_date date NOT NULL,
+ description text,
+ title text,
+ component_name text,
+ solution text,
+ not_impacted text,
+ cvss_v2 text,
+ cvss_v3 text,
+ affected_range text,
+ identifiers text[] DEFAULT '{}'::text[],
+ fixed_versions text[] DEFAULT '{}'::text[],
+ urls text[] DEFAULT '{}'::text[],
+ links text[] DEFAULT '{}'::text[],
+ CONSTRAINT check_3ab0544d19 CHECK ((char_length(title) <= 2048)),
+ CONSTRAINT check_3b57023409 CHECK ((char_length(affected_range) <= 32)),
+ CONSTRAINT check_4d5cd7be9c CHECK ((char_length(component_name) <= 2048)),
+ CONSTRAINT check_962f256a51 CHECK ((char_length(solution) <= 2048)),
+ CONSTRAINT check_aae93955fb CHECK ((char_length(cvss_v3) <= 128)),
+ CONSTRAINT check_b8a17497f3 CHECK ((char_length(cvss_v2) <= 128)),
+ CONSTRAINT check_c05a35f418 CHECK ((char_length(not_impacted) <= 2048)),
+ CONSTRAINT check_ff9f6483b6 CHECK ((char_length(description) <= 2048))
+);
+
+CREATE SEQUENCE vulnerability_advisories_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+ALTER SEQUENCE vulnerability_advisories_id_seq OWNED BY vulnerability_advisories.id;
+
CREATE TABLE vulnerability_exports (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
@@ -22538,7 +22747,7 @@ CREATE TABLE vulnerability_occurrences (
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
severity smallint NOT NULL,
- confidence smallint NOT NULL,
+ confidence smallint,
report_type smallint NOT NULL,
project_id integer NOT NULL,
scanner_id bigint NOT NULL,
@@ -22652,6 +22861,7 @@ CREATE TABLE vulnerability_state_transitions (
updated_at timestamp with time zone NOT NULL,
author_id bigint,
comment text,
+ dismissal_reason smallint,
CONSTRAINT check_fca4a7ca39 CHECK ((char_length(comment) <= 255))
);
@@ -22750,7 +22960,9 @@ CREATE TABLE web_hooks (
backoff_count smallint DEFAULT 0 NOT NULL,
disabled_until timestamp with time zone,
encrypted_url_variables bytea,
- encrypted_url_variables_iv bytea
+ encrypted_url_variables_iv bytea,
+ integration_id integer,
+ branch_filter_strategy smallint DEFAULT 0 NOT NULL
);
CREATE SEQUENCE web_hooks_id_seq
@@ -23129,6 +23341,8 @@ ALTER TABLE ONLY ci_namespace_mirrors ALTER COLUMN id SET DEFAULT nextval('ci_na
ALTER TABLE ONLY ci_namespace_monthly_usages ALTER COLUMN id SET DEFAULT nextval('ci_namespace_monthly_usages_id_seq'::regclass);
+ALTER TABLE ONLY ci_partitions ALTER COLUMN id SET DEFAULT nextval('ci_partitions_id_seq'::regclass);
+
ALTER TABLE ONLY ci_pending_builds ALTER COLUMN id SET DEFAULT nextval('ci_pending_builds_id_seq'::regclass);
ALTER TABLE ONLY ci_pipeline_artifacts ALTER COLUMN id SET DEFAULT nextval('ci_pipeline_artifacts_id_seq'::regclass);
@@ -23291,6 +23505,8 @@ ALTER TABLE ONLY diff_note_positions ALTER COLUMN id SET DEFAULT nextval('diff_n
ALTER TABLE ONLY dingtalk_tracker_data ALTER COLUMN id SET DEFAULT nextval('dingtalk_tracker_data_id_seq'::regclass);
+ALTER TABLE ONLY dora_configurations ALTER COLUMN id SET DEFAULT nextval('dora_configurations_id_seq'::regclass);
+
ALTER TABLE ONLY dora_daily_metrics ALTER COLUMN id SET DEFAULT nextval('dora_daily_metrics_id_seq'::regclass);
ALTER TABLE ONLY draft_notes ALTER COLUMN id SET DEFAULT nextval('draft_notes_id_seq'::regclass);
@@ -23379,6 +23595,8 @@ ALTER TABLE ONLY geo_repository_updated_events ALTER COLUMN id SET DEFAULT nextv
ALTER TABLE ONLY geo_reset_checksum_events ALTER COLUMN id SET DEFAULT nextval('geo_reset_checksum_events_id_seq'::regclass);
+ALTER TABLE ONLY ghost_user_migrations ALTER COLUMN id SET DEFAULT nextval('ghost_user_migrations_id_seq'::regclass);
+
ALTER TABLE ONLY gitlab_subscription_histories ALTER COLUMN id SET DEFAULT nextval('gitlab_subscription_histories_id_seq'::regclass);
ALTER TABLE ONLY gitlab_subscriptions ALTER COLUMN id SET DEFAULT nextval('gitlab_subscriptions_id_seq'::regclass);
@@ -23529,6 +23747,8 @@ ALTER TABLE ONLY merge_request_diffs ALTER COLUMN id SET DEFAULT nextval('merge_
ALTER TABLE ONLY merge_request_metrics ALTER COLUMN id SET DEFAULT nextval('merge_request_metrics_id_seq'::regclass);
+ALTER TABLE ONLY merge_request_predictions ALTER COLUMN merge_request_id SET DEFAULT nextval('merge_request_predictions_merge_request_id_seq'::regclass);
+
ALTER TABLE ONLY merge_request_reviewers ALTER COLUMN id SET DEFAULT nextval('merge_request_reviewers_id_seq'::regclass);
ALTER TABLE ONLY merge_request_user_mentions ALTER COLUMN id SET DEFAULT nextval('merge_request_user_mentions_id_seq'::regclass);
@@ -23793,6 +24013,8 @@ ALTER TABLE ONLY sbom_occurrences ALTER COLUMN id SET DEFAULT nextval('sbom_occu
ALTER TABLE ONLY sbom_sources ALTER COLUMN id SET DEFAULT nextval('sbom_sources_id_seq'::regclass);
+ALTER TABLE ONLY sbom_vulnerable_component_versions ALTER COLUMN id SET DEFAULT nextval('sbom_vulnerable_component_versions_id_seq'::regclass);
+
ALTER TABLE ONLY scim_identities ALTER COLUMN id SET DEFAULT nextval('scim_identities_id_seq'::regclass);
ALTER TABLE ONLY scim_oauth_access_tokens ALTER COLUMN id SET DEFAULT nextval('scim_oauth_access_tokens_id_seq'::regclass);
@@ -23913,6 +24135,8 @@ ALTER TABLE ONLY users_statistics ALTER COLUMN id SET DEFAULT nextval('users_sta
ALTER TABLE ONLY vulnerabilities ALTER COLUMN id SET DEFAULT nextval('vulnerabilities_id_seq'::regclass);
+ALTER TABLE ONLY vulnerability_advisories ALTER COLUMN id SET DEFAULT nextval('vulnerability_advisories_id_seq'::regclass);
+
ALTER TABLE ONLY vulnerability_exports ALTER COLUMN id SET DEFAULT nextval('vulnerability_exports_id_seq'::regclass);
ALTER TABLE ONLY vulnerability_external_issue_links ALTER COLUMN id SET DEFAULT nextval('vulnerability_external_issue_links_id_seq'::regclass);
@@ -24786,6 +25010,9 @@ ALTER TABLE ONLY chat_teams
ALTER TABLE vulnerability_scanners
ADD CONSTRAINT check_37608c9db5 CHECK ((char_length(vendor) <= 255)) NOT VALID;
+ALTER TABLE members
+ ADD CONSTRAINT check_508774aac0 CHECK ((member_namespace_id IS NOT NULL)) NOT VALID;
+
ALTER TABLE sprints
ADD CONSTRAINT check_ccd8a1eae0 CHECK ((start_date IS NOT NULL)) NOT VALID;
@@ -24858,6 +25085,9 @@ ALTER TABLE ONLY ci_namespace_mirrors
ALTER TABLE ONLY ci_namespace_monthly_usages
ADD CONSTRAINT ci_namespace_monthly_usages_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY ci_partitions
+ ADD CONSTRAINT ci_partitions_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY ci_pending_builds
ADD CONSTRAINT ci_pending_builds_pkey PRIMARY KEY (id);
@@ -25134,6 +25364,9 @@ ALTER TABLE ONLY diff_note_positions
ALTER TABLE ONLY dingtalk_tracker_data
ADD CONSTRAINT dingtalk_tracker_data_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY dora_configurations
+ ADD CONSTRAINT dora_configurations_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY dora_daily_metrics
ADD CONSTRAINT dora_daily_metrics_pkey PRIMARY KEY (id);
@@ -25272,6 +25505,9 @@ ALTER TABLE ONLY geo_repository_updated_events
ALTER TABLE ONLY geo_reset_checksum_events
ADD CONSTRAINT geo_reset_checksum_events_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY ghost_user_migrations
+ ADD CONSTRAINT ghost_user_migrations_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY gitlab_subscription_histories
ADD CONSTRAINT gitlab_subscription_histories_pkey PRIMARY KEY (id);
@@ -25536,6 +25772,9 @@ ALTER TABLE ONLY merge_request_diffs
ALTER TABLE ONLY merge_request_metrics
ADD CONSTRAINT merge_request_metrics_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY merge_request_predictions
+ ADD CONSTRAINT merge_request_predictions_pkey PRIMARY KEY (merge_request_id);
+
ALTER TABLE ONLY merge_request_reviewers
ADD CONSTRAINT merge_request_reviewers_pkey PRIMARY KEY (id);
@@ -25752,6 +25991,9 @@ ALTER TABLE ONLY packages_packages
ALTER TABLE ONLY packages_pypi_metadata
ADD CONSTRAINT packages_pypi_metadata_pkey PRIMARY KEY (package_id);
+ALTER TABLE ONLY packages_rpm_metadata
+ ADD CONSTRAINT packages_rpm_metadata_pkey PRIMARY KEY (package_id);
+
ALTER TABLE ONLY packages_rubygems_metadata
ADD CONSTRAINT packages_rubygems_metadata_pkey PRIMARY KEY (package_id);
@@ -26013,6 +26255,9 @@ ALTER TABLE ONLY sbom_occurrences
ALTER TABLE ONLY sbom_sources
ADD CONSTRAINT sbom_sources_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY sbom_vulnerable_component_versions
+ ADD CONSTRAINT sbom_vulnerable_component_versions_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY schema_migrations
ADD CONSTRAINT schema_migrations_pkey PRIMARY KEY (version);
@@ -26229,6 +26474,9 @@ ALTER TABLE ONLY verification_codes
ALTER TABLE ONLY vulnerabilities
ADD CONSTRAINT vulnerabilities_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY vulnerability_advisories
+ ADD CONSTRAINT vulnerability_advisories_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY vulnerability_exports
ADD CONSTRAINT vulnerability_exports_pkey PRIMARY KEY (id);
@@ -27268,10 +27516,10 @@ CREATE INDEX ca_aggregations_last_full_run_at ON analytics_cycle_analytics_aggre
CREATE INDEX ca_aggregations_last_incremental_run_at ON analytics_cycle_analytics_aggregations USING btree (last_incremental_run_at NULLS FIRST) WHERE (enabled IS TRUE);
-CREATE INDEX cadence_create_iterations_automation ON iterations_cadences USING btree (automatic, duration_in_weeks, date((COALESCE(last_run_date, '1970-01-01'::date) + ((duration_in_weeks)::double precision * '7 days'::interval)))) WHERE (duration_in_weeks IS NOT NULL);
-
CREATE INDEX ci_builds_gitlab_monitor_metrics ON ci_builds USING btree (status, created_at, project_id) WHERE ((type)::text = 'Ci::Build'::text);
+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);
CREATE UNIQUE INDEX commit_user_mentions_on_commit_id_and_note_id_unique_index ON commit_user_mentions USING btree (commit_id, note_id);
@@ -27404,7 +27652,7 @@ CREATE UNIQUE INDEX idx_on_external_status_checks_project_id_external_url ON ext
CREATE UNIQUE INDEX idx_on_external_status_checks_project_id_name ON external_status_checks USING btree (project_id, name);
-CREATE INDEX idx_open_issues_on_project_id_and_confidential ON issues USING btree (project_id, confidential) WHERE (state_id = 1);
+CREATE INDEX idx_open_issues_on_project_and_confidential_and_author_and_id ON issues USING btree (project_id, confidential, author_id, id) WHERE (state_id = 1);
CREATE INDEX idx_packages_debian_group_component_files_on_architecture_id ON packages_debian_group_component_files USING btree (architecture_id);
@@ -27472,6 +27720,8 @@ CREATE UNIQUE INDEX idx_vuln_signatures_on_occurrences_id_and_signature_sha ON v
CREATE UNIQUE INDEX idx_vuln_signatures_uniqueness_signature_sha ON vulnerability_finding_signatures USING btree (finding_id, algorithm_type, signature_sha);
+CREATE INDEX idx_vulnerabilities_on_project_id_and_id_active_cis_dft_branch ON vulnerabilities USING btree (project_id, id) WHERE ((report_type = 7) AND (state = ANY (ARRAY[1, 4])) AND (present_on_default_branch IS TRUE));
+
CREATE INDEX idx_vulnerabilities_partial_devops_adoption_and_default_branch ON vulnerabilities USING btree (project_id, created_at, present_on_default_branch) WHERE (state <> 1);
CREATE UNIQUE INDEX idx_vulnerability_ext_issue_links_on_vulne_id_and_ext_issue ON vulnerability_external_issue_links USING btree (vulnerability_id, external_type, external_project_key, external_issue_key);
@@ -27740,6 +27990,8 @@ CREATE INDEX index_boards_on_project_id ON boards USING btree (project_id);
CREATE INDEX index_broadcast_message_on_ends_at_and_broadcast_type_and_id ON broadcast_messages USING btree (ends_at, broadcast_type, id);
+CREATE INDEX index_broadcast_messages_on_namespace_id ON broadcast_messages USING btree (namespace_id);
+
CREATE INDEX index_btree_namespaces_traversal_ids ON namespaces USING btree (traversal_ids);
CREATE INDEX index_bulk_import_configurations_on_bulk_import_id ON bulk_import_configurations USING btree (bulk_import_id);
@@ -28358,6 +28610,8 @@ CREATE UNIQUE INDEX index_diff_note_positions_on_note_id_and_diff_type ON diff_n
CREATE INDEX index_dingtalk_tracker_data_on_integration_id ON dingtalk_tracker_data USING btree (integration_id);
+CREATE UNIQUE INDEX index_dora_configurations_on_project_id ON dora_configurations USING btree (project_id);
+
CREATE UNIQUE INDEX index_dora_daily_metrics_on_environment_id_and_date ON dora_daily_metrics USING btree (environment_id, date);
CREATE INDEX index_draft_notes_on_author_id ON draft_notes USING btree (author_id);
@@ -28384,6 +28638,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_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);
CREATE UNIQUE INDEX index_environments_on_project_id_and_name ON environments USING btree (project_id, name);
@@ -28394,6 +28650,8 @@ CREATE INDEX index_environments_on_project_id_and_tier ON environments USING btr
CREATE INDEX index_environments_on_project_id_state_environment_type ON environments USING btree (project_id, state, environment_type);
+CREATE INDEX index_environments_on_project_name_varchar_pattern_ops_state ON environments USING btree (project_id, lower((name)::text) varchar_pattern_ops, state);
+
CREATE INDEX index_environments_on_state_and_auto_delete_at ON environments USING btree (auto_delete_at) WHERE ((auto_delete_at IS NOT NULL) AND ((state)::text = 'stopped'::text));
CREATE INDEX index_environments_on_state_and_auto_stop_at ON environments USING btree (state, auto_stop_at) WHERE ((auto_stop_at IS NOT NULL) AND ((state)::text = 'available'::text));
@@ -28582,6 +28840,8 @@ CREATE INDEX index_geo_repository_updated_events_on_source ON geo_repository_upd
CREATE INDEX index_geo_reset_checksum_events_on_project_id ON geo_reset_checksum_events USING btree (project_id);
+CREATE UNIQUE INDEX index_ghost_user_migrations_on_user_id ON ghost_user_migrations USING btree (user_id);
+
CREATE INDEX index_gin_ci_namespace_mirrors_on_traversal_ids ON ci_namespace_mirrors USING gin (traversal_ids);
CREATE INDEX index_gin_ci_pending_builds_on_namespace_traversal_ids ON ci_pending_builds USING gin (namespace_traversal_ids);
@@ -28812,8 +29072,6 @@ 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);
-CREATE INDEX index_issues_on_description_trigram_non_latin ON issues USING gin (description gin_trgm_ops) WHERE (((title)::text !~ similar_escape('[\u0000-\u218F]*'::text, NULL::text)) OR (description !~ similar_escape('[\u0000-\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);
CREATE INDEX index_issues_on_id_and_weight ON issues USING btree (id, weight);
@@ -28842,14 +29100,14 @@ CREATE INDEX index_issues_on_project_id_closed_at_desc_state_id_and_id ON issues
CREATE INDEX index_issues_on_project_id_closed_at_state_id_and_id ON issues USING btree (project_id, closed_at, state_id, id);
+CREATE INDEX index_issues_on_project_id_health_status_created_at_id ON issues USING btree (project_id, health_status, created_at, id);
+
CREATE INDEX index_issues_on_promoted_to_epic_id ON issues USING btree (promoted_to_epic_id) WHERE (promoted_to_epic_id IS NOT NULL);
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);
-CREATE INDEX index_issues_on_title_trigram_non_latin ON issues USING gin (title gin_trgm_ops) WHERE (((title)::text !~ similar_escape('[\u0000-\u218F]*'::text, NULL::text)) OR (description !~ similar_escape('[\u0000-\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);
@@ -29310,7 +29568,7 @@ CREATE INDEX index_on_projects_path ON projects USING btree (path);
CREATE INDEX index_on_routes_lower_path ON routes USING btree (lower((path)::text));
-CREATE INDEX index_on_security_findings_uuid_and_id_order_desc ON security_findings USING btree (uuid, id DESC);
+CREATE INDEX index_on_todos_user_project_target_and_state ON todos USING btree (user_id, project_id, target_type, target_id, id) WHERE ((state)::text = 'pending'::text);
CREATE INDEX index_on_users_lower_email ON users USING btree (lower((email)::text));
@@ -29432,6 +29690,8 @@ CREATE INDEX index_packages_packages_on_project_id_and_version ON packages_packa
CREATE INDEX index_packages_project_id_name_partial_for_nuget ON packages_packages USING btree (project_id, name) WHERE (((name)::text <> 'NuGet.Temporary.Package'::text) AND (version IS NOT NULL) AND (package_type = 4));
+CREATE INDEX index_packages_rpm_metadata_on_package_id ON packages_rpm_metadata USING btree (package_id);
+
CREATE INDEX index_packages_tags_on_package_id ON packages_tags USING btree (package_id);
CREATE INDEX index_packages_tags_on_package_id_and_updated_at ON packages_tags USING btree (package_id, updated_at DESC);
@@ -29572,6 +29832,8 @@ CREATE INDEX index_project_group_links_on_project_id ON project_group_links USIN
CREATE INDEX index_project_import_data_on_project_id ON project_import_data USING btree (project_id);
+CREATE INDEX index_project_members_on_id_temp ON members USING btree (id) WHERE ((source_type)::text = 'Project'::text);
+
CREATE INDEX index_project_mirror_data_on_last_successful_update_at ON project_mirror_data USING btree (last_successful_update_at);
CREATE INDEX index_project_mirror_data_on_last_update_at_and_retry_count ON project_mirror_data USING btree (last_update_at, retry_count);
@@ -29938,20 +30200,6 @@ 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_findings_on_confidence ON security_findings USING btree (confidence);
-
-CREATE INDEX index_security_findings_on_project_fingerprint ON security_findings USING btree (project_fingerprint);
-
-CREATE INDEX index_security_findings_on_scan_id_and_deduplicated ON security_findings USING btree (scan_id, deduplicated);
-
-CREATE INDEX index_security_findings_on_scan_id_and_id ON security_findings USING btree (scan_id, id);
-
-CREATE INDEX index_security_findings_on_scanner_id ON security_findings USING btree (scanner_id);
-
-CREATE INDEX index_security_findings_on_severity ON security_findings USING btree (severity);
-
-CREATE UNIQUE INDEX index_security_findings_on_unique_columns ON security_findings USING btree (uuid, scan_id, partition_number);
-
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);
@@ -30238,7 +30486,7 @@ CREATE UNIQUE INDEX index_user_canonical_emails_on_user_id ON user_canonical_ema
CREATE UNIQUE INDEX index_user_canonical_emails_on_user_id_and_canonical_email ON user_canonical_emails USING btree (user_id, canonical_email);
-CREATE INDEX index_user_credit_card_validations_meta_data_full_match ON user_credit_card_validations USING btree (holder_name, expiration_date, last_digits, credit_card_validated_at);
+CREATE INDEX index_user_credit_card_validations_meta_data_full_match_lower ON user_credit_card_validations USING btree (lower(holder_name), expiration_date, last_digits, credit_card_validated_at);
CREATE INDEX index_user_credit_card_validations_meta_data_partial_match ON user_credit_card_validations USING btree (expiration_date, last_digits, network, credit_card_validated_at);
@@ -30364,8 +30612,6 @@ CREATE INDEX index_vulnerabilities_on_last_edited_by_id ON vulnerabilities USING
CREATE INDEX index_vulnerabilities_on_milestone_id ON vulnerabilities USING btree (milestone_id);
-CREATE INDEX index_vulnerabilities_on_project_id_and_id_active_cis ON vulnerabilities USING btree (project_id, id) WHERE ((report_type = 7) AND (state = ANY (ARRAY[1, 4])));
-
CREATE INDEX index_vulnerabilities_on_project_id_and_state_and_severity ON vulnerabilities USING btree (project_id, state, severity);
CREATE INDEX index_vulnerabilities_on_resolved_by_id ON vulnerabilities USING btree (resolved_by_id);
@@ -30486,6 +30732,10 @@ CREATE UNIQUE INDEX index_vulnerability_statistics_on_unique_project_id ON vulne
CREATE UNIQUE INDEX index_vulnerability_user_mentions_on_note_id ON vulnerability_user_mentions USING btree (note_id) WHERE (note_id IS NOT NULL);
+CREATE INDEX index_vulnerable_component_versions_on_sbom_component_version ON sbom_vulnerable_component_versions USING btree (sbom_component_version_id);
+
+CREATE INDEX index_vulnerable_component_versions_on_vulnerability_advisory ON sbom_vulnerable_component_versions USING btree (vulnerability_advisory_id);
+
CREATE UNIQUE INDEX index_vulns_user_mentions_on_vulnerability_id ON vulnerability_user_mentions USING btree (vulnerability_id) WHERE (note_id IS NULL);
CREATE UNIQUE INDEX index_vulns_user_mentions_on_vulnerability_id_and_note_id ON vulnerability_user_mentions USING btree (vulnerability_id, note_id);
@@ -30496,6 +30746,8 @@ CREATE INDEX index_web_hook_logs_part_on_web_hook_id ON ONLY web_hook_logs USING
CREATE INDEX index_web_hooks_on_group_id ON web_hooks USING btree (group_id) WHERE ((type)::text = 'GroupHook'::text);
+CREATE INDEX index_web_hooks_on_integration_id ON web_hooks USING btree (integration_id);
+
CREATE INDEX index_web_hooks_on_project_id ON web_hooks USING btree (project_id);
CREATE INDEX index_web_hooks_on_project_id_recent_failures ON web_hooks USING btree (project_id, recent_failures);
@@ -30590,6 +30842,20 @@ CREATE UNIQUE INDEX partial_index_sop_configs_on_project_id ON security_orchestr
CREATE INDEX partial_index_user_id_app_id_created_at_token_not_revoked ON oauth_access_tokens USING btree (resource_owner_id, application_id, created_at) WHERE (revoked_at IS NULL);
+CREATE INDEX security_findings_confidence_idx ON ONLY security_findings USING btree (confidence);
+
+CREATE INDEX security_findings_project_fingerprint_idx ON ONLY security_findings USING btree (project_fingerprint);
+
+CREATE INDEX security_findings_scan_id_deduplicated_idx ON ONLY security_findings USING btree (scan_id, deduplicated);
+
+CREATE INDEX security_findings_scan_id_id_idx ON ONLY security_findings USING btree (scan_id, id);
+
+CREATE INDEX security_findings_scanner_id_idx ON ONLY security_findings USING btree (scanner_id);
+
+CREATE INDEX security_findings_severity_idx ON ONLY security_findings USING btree (severity);
+
+CREATE UNIQUE INDEX security_findings_uuid_scan_id_partition_number_idx ON ONLY security_findings USING btree (uuid, scan_id, partition_number);
+
CREATE UNIQUE INDEX snippet_user_mentions_on_snippet_id_and_note_id_index ON snippet_user_mentions USING btree (snippet_id, note_id);
CREATE UNIQUE INDEX snippet_user_mentions_on_snippet_id_index ON snippet_user_mentions USING btree (snippet_id) WHERE (note_id IS NULL);
@@ -30598,17 +30864,17 @@ 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_container_repos_on_non_migrated ON container_repositories USING btree (project_id, id) WHERE ((migration_state <> 'import_done'::text) AND (created_at < '2022-01-23 00:00:00'::timestamp without time zone));
+CREATE INDEX tmp_index_approval_merge_request_rules_on_report_type_equal_one ON approval_merge_request_rules USING btree (id, report_type) WHERE (report_type = 1);
CREATE INDEX tmp_index_ci_job_artifacts_on_expire_at_where_locked_unknown ON ci_job_artifacts USING btree (expire_at, job_id) WHERE ((locked = 2) AND (expire_at IS NOT NULL));
-CREATE INDEX tmp_index_ci_job_artifacts_on_id_where_trace_and_expire_at ON ci_job_artifacts USING btree (id) WHERE ((file_type = 3) AND (expire_at = ANY (ARRAY['2021-04-22 00:00:00+00'::timestamp with time zone, '2021-05-22 00:00:00+00'::timestamp with time zone, '2021-06-22 00:00:00+00'::timestamp with time zone, '2022-01-22 00:00:00+00'::timestamp with time zone, '2022-02-22 00:00:00+00'::timestamp with time zone, '2022-03-22 00:00:00+00'::timestamp with time zone, '2022-04-22 00:00:00+00'::timestamp with time zone])));
+CREATE INDEX tmp_index_ci_job_artifacts_on_id_expire_at_file_type_trace ON ci_job_artifacts USING btree (id) WHERE (((date_part('day'::text, timezone('UTC'::text, expire_at)) = ANY (ARRAY[(21)::double precision, (22)::double precision, (23)::double precision])) AND (date_part('minute'::text, timezone('UTC'::text, expire_at)) = ANY (ARRAY[(0)::double precision, (30)::double precision, (45)::double precision])) AND (date_part('second'::text, timezone('UTC'::text, expire_at)) = (0)::double precision)) OR (file_type = 3));
CREATE INDEX tmp_index_cis_vulnerability_reads_on_id ON vulnerability_reads USING btree (id) WHERE (report_type = 7);
-CREATE INDEX tmp_index_container_repositories_on_id_migration_state ON container_repositories USING btree (id, migration_state);
+CREATE INDEX tmp_index_container_repos_on_non_migrated ON container_repositories USING btree (project_id, id) WHERE (migration_state <> 'import_done'::text);
-CREATE INDEX tmp_index_for_namespace_id_migration_on_group_members ON members USING btree (id) WHERE ((member_namespace_id IS NULL) AND ((type)::text = 'GroupMember'::text));
+CREATE INDEX tmp_index_container_repositories_on_id_migration_state ON container_repositories USING btree (id, migration_state);
CREATE INDEX tmp_index_for_null_project_namespace_id ON projects USING btree (id) WHERE (project_namespace_id IS NULL);
@@ -30618,7 +30884,7 @@ CREATE INDEX tmp_index_issues_on_issue_type_and_id ON issues USING btree (issue_
CREATE INDEX tmp_index_members_on_state ON members USING btree (state) WHERE (state = 2);
-CREATE INDEX tmp_index_merge_requests_draft_and_status ON merge_requests USING btree (id) WHERE ((draft = false) AND (state_id = 1) AND ((title)::text ~* '^(\[draft\]|\(draft\)|draft:|draft|\[WIP\]|WIP:|WIP)'::text));
+CREATE INDEX tmp_index_merge_request_reviewers_on_attention_requested_state ON merge_request_reviewers USING btree (id) 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));
@@ -30628,7 +30894,11 @@ CREATE INDEX tmp_index_on_vulnerabilities_non_dismissed ON vulnerabilities USING
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_todos_attention_request_action ON todos USING btree (id) WHERE (action = 10);
+CREATE INDEX tmp_index_system_note_metadata_on_attention_request_actions ON system_note_metadata USING btree (id) WHERE ((action)::text = ANY ((ARRAY['attention_requested'::character varying, 'attention_request_removed'::character varying])::text[]));
+
+CREATE INDEX tmp_index_system_note_metadata_on_id_where_task ON system_note_metadata USING btree (id, action) WHERE ((action)::text = 'task'::text);
+
+CREATE INDEX tmp_index_user_callouts_on_attention_request_feature_names ON user_callouts USING btree (id) WHERE (feature_name = ANY (ARRAY[47, 48]));
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]));
@@ -31962,6 +32232,12 @@ CREATE TRIGGER nullify_merge_request_metrics_build_data_on_update BEFORE UPDATE
CREATE TRIGGER projects_loose_fk_trigger AFTER DELETE ON projects REFERENCING OLD TABLE AS old_table FOR EACH STATEMENT EXECUTE FUNCTION insert_into_loose_foreign_keys_deleted_records();
+CREATE TRIGGER trigger_a645cee67576 BEFORE UPDATE OF integration_id ON web_hooks FOR EACH ROW EXECUTE FUNCTION function_for_trigger_a645cee67576();
+
+CREATE TRIGGER trigger_a87bcfdf0f0b BEFORE INSERT ON web_hooks FOR EACH ROW EXECUTE FUNCTION function_for_trigger_a87bcfdf0f0b();
+
+CREATE TRIGGER trigger_aca5c963d732 BEFORE UPDATE OF service_id ON web_hooks FOR EACH ROW EXECUTE FUNCTION function_for_trigger_aca5c963d732();
+
CREATE TRIGGER trigger_delete_project_namespace_on_project_delete AFTER DELETE ON projects FOR EACH ROW WHEN ((old.project_namespace_id IS NOT NULL)) EXECUTE FUNCTION delete_associated_project_namespace();
CREATE TRIGGER trigger_has_external_issue_tracker_on_delete AFTER DELETE ON integrations FOR EACH ROW WHEN ((((old.category)::text = 'issue_tracker'::text) AND (old.active = true) AND (old.project_id IS NOT NULL))) EXECUTE FUNCTION set_has_external_issue_tracker();
@@ -31982,9 +32258,7 @@ CREATE TRIGGER trigger_insert_or_update_vulnerability_reads_from_occurrences AFT
CREATE TRIGGER trigger_insert_vulnerability_reads_from_vulnerability AFTER UPDATE ON vulnerabilities FOR EACH ROW WHEN (((old.present_on_default_branch IS NOT TRUE) AND (new.present_on_default_branch IS TRUE))) EXECUTE FUNCTION insert_vulnerability_reads_from_vulnerability();
-CREATE TRIGGER trigger_namespaces_parent_id_on_insert AFTER INSERT ON namespaces FOR EACH ROW EXECUTE FUNCTION insert_namespaces_sync_event();
-
-CREATE TRIGGER trigger_namespaces_parent_id_on_update AFTER UPDATE ON namespaces FOR EACH ROW WHEN ((old.parent_id IS DISTINCT FROM new.parent_id)) EXECUTE FUNCTION insert_namespaces_sync_event();
+CREATE TRIGGER trigger_namespaces_traversal_ids_on_update AFTER UPDATE ON namespaces FOR EACH ROW WHEN ((old.traversal_ids IS DISTINCT FROM new.traversal_ids)) EXECUTE FUNCTION insert_namespaces_sync_event();
CREATE TRIGGER trigger_projects_parent_id_on_insert AFTER INSERT ON projects FOR EACH ROW EXECUTE FUNCTION insert_projects_sync_event();
@@ -32014,6 +32288,9 @@ ALTER TABLE ONLY deployments
ALTER TABLE ONLY epics
ADD CONSTRAINT fk_013c9f36ca FOREIGN KEY (due_date_sourcing_epic_id) REFERENCES epics(id) ON DELETE SET NULL;
+ALTER TABLE ONLY environments
+ ADD CONSTRAINT fk_01a033a308 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE SET NULL;
+
ALTER TABLE ONLY incident_management_escalation_rules
ADD CONSTRAINT fk_0314ee86eb FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
@@ -32110,6 +32387,9 @@ ALTER TABLE ONLY boards
ALTER TABLE ONLY epics
ADD CONSTRAINT fk_1fbed67632 FOREIGN KEY (start_date_sourcing_milestone_id) REFERENCES milestones(id) ON DELETE SET NULL;
+ALTER TABLE ONLY ghost_user_migrations
+ ADD CONSTRAINT fk_202e642a2f FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY coverage_fuzzing_corpuses
ADD CONSTRAINT fk_204d40056a FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
@@ -32173,6 +32453,9 @@ ALTER TABLE ONLY lfs_objects_projects
ALTER TABLE ONLY vulnerability_merge_request_links
ADD CONSTRAINT fk_2ef3954596 FOREIGN KEY (vulnerability_id) REFERENCES vulnerabilities(id) ON DELETE CASCADE;
+ALTER TABLE ONLY members
+ ADD CONSTRAINT fk_2f85abf8f1 FOREIGN KEY (member_namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE NOT VALID;
+
ALTER TABLE ONLY analytics_cycle_analytics_group_stages
ADD CONSTRAINT fk_3078345d6d FOREIGN KEY (stage_event_hash_id) REFERENCES analytics_cycle_analytics_stage_event_hashes(id) ON DELETE CASCADE;
@@ -32416,6 +32699,9 @@ ALTER TABLE ONLY vulnerabilities
ALTER TABLE ONLY issue_customer_relations_contacts
ADD CONSTRAINT fk_7b92f835bb FOREIGN KEY (contact_id) REFERENCES customer_relations_contacts(id) ON DELETE CASCADE;
+ALTER TABLE ONLY broadcast_messages
+ ADD CONSTRAINT fk_7bf2ec43da FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY vulnerabilities
ADD CONSTRAINT fk_7c5bb22a22 FOREIGN KEY (due_date_sourcing_milestone_id) REFERENCES milestones(id) ON DELETE SET NULL;
@@ -32473,6 +32759,9 @@ ALTER TABLE ONLY requirements_management_test_reports
ALTER TABLE ONLY issues
ADD CONSTRAINT fk_899c8f3231 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY sbom_vulnerable_component_versions
+ ADD CONSTRAINT fk_8a2a1197f9 FOREIGN KEY (sbom_component_version_id) REFERENCES sbom_component_versions(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY protected_branch_merge_access_levels
ADD CONSTRAINT fk_8a3072ccb3 FOREIGN KEY (protected_branch_id) REFERENCES protected_branches(id) ON DELETE CASCADE;
@@ -32773,6 +33062,9 @@ ALTER TABLE ONLY lists
ALTER TABLE ONLY agent_activity_events
ADD CONSTRAINT fk_d6f785c9fc FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;
+ALTER TABLE ONLY sbom_vulnerable_component_versions
+ ADD CONSTRAINT fk_d720a1959a FOREIGN KEY (vulnerability_advisory_id) REFERENCES vulnerability_advisories(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY metrics_users_starred_dashboards
ADD CONSTRAINT fk_d76a2b9a8c FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
@@ -32797,6 +33089,9 @@ ALTER TABLE ONLY project_group_links
ALTER TABLE ONLY project_topics
ADD CONSTRAINT fk_db13576296 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY web_hooks
+ ADD CONSTRAINT fk_db1ea5699b FOREIGN KEY (integration_id) REFERENCES integrations(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY security_scans
ADD CONSTRAINT fk_dbc89265b9 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
@@ -33664,7 +33959,7 @@ ALTER TABLE ONLY project_custom_attributes
ALTER TABLE ONLY ci_pending_builds
ADD CONSTRAINT fk_rails_725a2644a3 FOREIGN KEY (build_id) REFERENCES ci_builds(id) ON DELETE CASCADE;
-ALTER TABLE ONLY security_findings
+ALTER TABLE security_findings
ADD CONSTRAINT fk_rails_729b763a54 FOREIGN KEY (scanner_id) REFERENCES vulnerability_scanners(id) ON DELETE CASCADE;
ALTER TABLE ONLY dast_scanner_profiles
@@ -34072,6 +34367,9 @@ ALTER TABLE ONLY issues_prometheus_alert_events
ALTER TABLE ONLY merge_trains
ADD CONSTRAINT fk_rails_b374b5225d FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
+ALTER TABLE ONLY merge_request_predictions
+ ADD CONSTRAINT fk_rails_b3b78cbcd0 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY incident_management_escalation_rules
ADD CONSTRAINT fk_rails_b3c9c17bd4 FOREIGN KEY (oncall_schedule_id) REFERENCES incident_management_oncall_schedules(id) ON DELETE CASCADE;
@@ -34093,6 +34391,9 @@ ALTER TABLE ONLY approval_project_rules_protected_branches
ALTER TABLE ONLY packages_composer_cache_files
ADD CONSTRAINT fk_rails_b82cea43a0 FOREIGN KEY (namespace_id) REFERENCES namespaces(id) ON DELETE SET NULL;
+ALTER TABLE ONLY dora_configurations
+ ADD CONSTRAINT fk_rails_b9b8d90ddb FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY merge_trains
ADD CONSTRAINT fk_rails_b9d67af01d FOREIGN KEY (target_project_id) REFERENCES projects(id) ON DELETE CASCADE;
@@ -34102,7 +34403,7 @@ ALTER TABLE ONLY approval_project_rules_users
ALTER TABLE ONLY lists
ADD CONSTRAINT fk_rails_baed5f39b7 FOREIGN KEY (milestone_id) REFERENCES milestones(id) ON DELETE CASCADE;
-ALTER TABLE ONLY security_findings
+ALTER TABLE security_findings
ADD CONSTRAINT fk_rails_bb63863cf1 FOREIGN KEY (scan_id) REFERENCES security_scans(id) ON DELETE CASCADE;
ALTER TABLE ONLY packages_debian_project_component_files
@@ -34273,6 +34574,9 @@ ALTER TABLE ONLY geo_hashed_storage_attachments_events
ALTER TABLE ONLY ml_candidate_params
ADD CONSTRAINT fk_rails_d4a51d1185 FOREIGN KEY (candidate_id) REFERENCES ml_candidates(id);
+ALTER TABLE ONLY packages_rpm_metadata
+ ADD CONSTRAINT fk_rails_d79f02264b FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY merge_request_reviewers
ADD CONSTRAINT fk_rails_d9fec24b9d FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
diff --git a/doc/.vale/gitlab/British.yml b/doc/.vale/gitlab/British.yml
index f724eb19fa9..c63cbd917c7 100644
--- a/doc/.vale/gitlab/British.yml
+++ b/doc/.vale/gitlab/British.yml
@@ -80,6 +80,9 @@ swap:
neighbour: neighbor
normalise: normalize
offence: offense
+ optimise: optimize
+ optimised: optimized
+ optimising: optimizing
organise: organize
orientated: oriented
paralyse: paralyze
diff --git a/doc/.vale/gitlab/InternalLinkExtension.yml b/doc/.vale/gitlab/InternalLinkExtension.yml
index 5783c4347a9..52142b50dfc 100644
--- a/doc/.vale/gitlab/InternalLinkExtension.yml
+++ b/doc/.vale/gitlab/InternalLinkExtension.yml
@@ -5,9 +5,9 @@
#
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
extends: existence
-message: 'Link "%s" must use the .md file extension.'
+message: 'Link "%s" must link directly to a file and use the .md file extension.'
link: https://docs.gitlab.com/ee/development/documentation/styleguide/index.html#links-to-internal-documentation
level: error
scope: raw
raw:
- - '\[.+\]\([\w\/\.-]+\.html[^)]*\)'
+ - '\[[^\]]+\]\([^:\)]+(\/(#[^\)]+)?\)|\.html(#.+)?\))'
diff --git a/doc/.vale/gitlab/Markdown_emoji.yml b/doc/.vale/gitlab/Markdown_emoji.yml
new file mode 100644
index 00000000000..ac0dab2d69d
--- /dev/null
+++ b/doc/.vale/gitlab/Markdown_emoji.yml
@@ -0,0 +1,13 @@
+---
+# Warning: gitlab.Markdown_emoji
+#
+# Check for use of GLFM emoji syntax (https://docs.gitlab.com/ee/user/markdown.html#emojis), which doesn't render correctly in documentation.
+#
+# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
+extends: existence
+message: 'This appears to be GLFM emoji syntax. Replace "%s" with GitLab SVGs or Unicode emojis.'
+link: https://docs.gitlab.com/ee/development/documentation/styleguide/#gitlab-svg-icons
+level: warning
+scope: text
+raw:
+ - '(?:\s+|^):[a-zA-Z0-9\-_\+]+:(?:\s+|$|\.)'
diff --git a/doc/administration/application_settings_cache.md b/doc/administration/application_settings_cache.md
index 30fd9ab85a8..88e39a50b6c 100644
--- a/doc/administration/application_settings_cache.md
+++ b/doc/administration/application_settings_cache.md
@@ -18,7 +18,9 @@ extra load for Redis and PostgreSQL.
To change the expiry value:
-**For Omnibus installations**
+::Tabs
+
+:::TabTitle Omnibus package
1. Edit `/etc/gitlab/gitlab.rb`:
@@ -34,7 +36,7 @@ To change the expiry value:
gitlab-ctl restart
```
-**For installations from source**
+:::TabTitle Source
1. Edit `config/gitlab.yml`:
@@ -45,3 +47,5 @@ To change the expiry value:
1. Save the file, and then [restart](restart_gitlab.md#installations-from-source)
GitLab for the changes to take effect.
+
+::EndTabs
diff --git a/doc/administration/audit_event_streaming.md b/doc/administration/audit_event_streaming.md
index 3bb0ce41861..9ec7b81bfd0 100644
--- a/doc/administration/audit_event_streaming.md
+++ b/doc/administration/audit_event_streaming.md
@@ -1,5 +1,5 @@
---
-stage: Manage
+stage: Govern
group: Compliance
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
---
@@ -16,7 +16,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - Custom HTTP headers API [made generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/366524) in GitLab 15.3. [Feature flag `streaming_audit_event_headers`](https://gitlab.com/gitlab-org/gitlab/-/issues/362941) removed.
> - Custom HTTP headers UI [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/361630) in GitLab 15.2 [with a flag](feature_flags.md) named `custom_headers_streaming_audit_events_ui`. Disabled by default.
> - 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.4.
+> - [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.
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.
@@ -38,7 +39,7 @@ Streaming destinations receive **all** audit event data, which could include sen
Users with the Owner role for a group can add streaming destinations for it:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Security & Compliance > Audit events**.
1. On the main area, select **Streams** tab.
1. Select **Add streaming destination** to show the section for adding destinations.
@@ -71,6 +72,25 @@ mutation {
}
```
+Group owners can also optionally specify their own verification token (instead of the default GitLab-generated one) using the GraphQL `auditEventsStreamingHeadersCreate`
+mutation. Verification token length must be within 16 to 24 characters and trailing whitespace are not trimmed. GitLab recommends setting a cryptographically random and unique value. For example:
+
+```graphql
+mutation {
+ externalAuditEventDestinationCreate(input: { destinationUrl: "https://mydomain.io/endpoint/ingest", groupPath: "my-group", verificationToken: "unique-random-verification-token-here" } ) {
+ errors
+ externalAuditEventDestination {
+ id
+ destinationUrl
+ verificationToken
+ group {
+ name
+ }
+ }
+ }
+}
+```
+
Event streaming is enabled if:
- The returned `errors` object is empty.
@@ -97,7 +117,7 @@ Users with the Owner role for a group can list streaming destinations.
To list the streaming destinations:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Security & Compliance > Audit events**.
1. On the main area, select **Streams** tab.
1. To the right of the item, select **Edit** (**{pencil}**) to see all the custom HTTP headers.
@@ -141,7 +161,7 @@ Users with the Owner role for a group can update streaming destinations.
To update a streaming destinations custom HTTP headers:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Security & Compliance > Audit events**.
1. On the main area, select **Streams** tab.
1. To the right of the item, select **Edit** (**{pencil}**).
@@ -195,14 +215,14 @@ When the last destination is successfully deleted, streaming is disabled for the
To delete a streaming destination:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Security & Compliance > Audit events**.
1. On the main area, select the **Streams** tab.
1. To the right of the item, select **Delete** (**{remove}**).
To delete only the custom HTTP headers for a streaming destination:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Security & Compliance > Audit events**.
1. On the main area, select the **Streams** tab.
1. To the right of the item, **Edit** (**{pencil}**).
@@ -248,9 +268,9 @@ The header is deleted if the returned `errors` object is empty.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345424) in GitLab 14.8.
Each streaming destination has a unique verification token (`verificationToken`) that can be used to verify the authenticity of the event. This
-token is generated when the event destination is created and cannot be changed.
+token is either specified by the Owner or generated automatically when the event destination is created and cannot be changed.
-Each streamed event contains a random alphanumeric identifier for the `X-Gitlab-Event-Streaming-Token` HTTP header that can be verified against
+Each streamed event contains the verification token in the `X-Gitlab-Event-Streaming-Token` HTTP header that can be verified against
the destination's value when [listing streaming destinations](#list-streaming-destinations).
### Use the GitLab UI
@@ -259,7 +279,7 @@ the destination's value when [listing streaming destinations](#list-streaming-de
Users with the Owner role for a group can list streaming destinations and see the verification tokens:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Security & Compliance > Audit events**.
1. On the main area, select the **Streams**.
1. View the verification token on the right side of each item.
@@ -297,7 +317,7 @@ FLAG:
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 `audit_event_streaming_git_operations`.
-Streaming audit events can be sent when signed-in users push or pull a project's remote Git repositories:
+Streaming audit events can be sent when signed-in users push, pull, or clone a project's remote Git repositories:
- [Using SSH](../user/ssh.md).
- Using HTTP or HTTPS.
diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md
index 92504c226fb..b6c267bfd0c 100644
--- a/doc/administration/audit_events.md
+++ b/doc/administration/audit_events.md
@@ -1,5 +1,5 @@
---
-stage: Manage
+stage: Govern
group: Compliance
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
---
@@ -152,7 +152,7 @@ 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 13.5)
+- 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.
@@ -209,7 +209,7 @@ Instance events do not include group or project audit events.
To view the server-wide audit events:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Audit Events**.
The following user actions are recorded:
@@ -309,7 +309,7 @@ audit events.
To export the audit events to CSV:
-1. On the top bar, select **Menu > Admin**.
+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**.
diff --git a/doc/administration/audit_reports.md b/doc/administration/audit_reports.md
index e33b5153c5b..e363e7862ea 100644
--- a/doc/administration/audit_reports.md
+++ b/doc/administration/audit_reports.md
@@ -1,5 +1,5 @@
---
-stage: Manage
+stage: Govern
group: Compliance
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
description: 'Learn how to create evidence artifacts typically requested by a 3rd party auditor.'
diff --git a/doc/administration/auditor_users.md b/doc/administration/auditor_users.md
index 561aa5d7b2e..bd48cfdbc4f 100644
--- a/doc/administration/auditor_users.md
+++ b/doc/administration/auditor_users.md
@@ -31,7 +31,7 @@ To create a new user account with auditor access (or change an existing user):
To create a user account with auditor access:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Create a new user or edit an existing one. Set **Access Level** to **Auditor**.
1. If you created a user, select **Create user**. For an existing user, select **Save changes**.
diff --git a/doc/administration/auth/atlassian.md b/doc/administration/auth/atlassian.md
index 6be53922a5a..1d20d87bdf4 100644
--- a/doc/administration/auth/atlassian.md
+++ b/doc/administration/auth/atlassian.md
@@ -71,7 +71,10 @@ To enable the Atlassian OmniAuth provider for passwordless authentication you mu
1. Change `YOUR_CLIENT_ID` and `YOUR_CLIENT_SECRET` to the Client credentials you received in [application registration](#atlassian-application-registration) steps.
1. Save the configuration file.
-1. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect if you installed GitLab via Omnibus or from source respectively.
+
+1. For the changes to take effect:
+ - If you installed via Omnibus, [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
+ - If you installed from source, [restart GitLab](../restart_gitlab.md#installations-from-source).
On the sign-in page there should now be an Atlassian icon below the regular sign in form. Select the icon to begin the authentication process.
diff --git a/doc/administration/auth/index.md b/doc/administration/auth/index.md
index a412875501e..2644e3a1f50 100644
--- a/doc/administration/auth/index.md
+++ b/doc/administration/auth/index.md
@@ -34,31 +34,3 @@ For more information, see the links shown on this page for each external provide
| **User Removal** | SCIM (remove user from top-level group) | LDAP (remove user from groups and block from the instance) |
1. Using Just-In-Time (JIT) provisioning, user accounts are created when the user first signs in.
-
-## Change apps or configuration
-
-When GitLab doesn't support having multiple providers (such as OAuth), GitLab configuration and user identification must be
-updated at the same time if the provider or app is changed.
-
-These instructions apply to all methods of authentication where GitLab stores an `extern_uid` and it is the only data used
-for user authentication.
-
-When changing apps within a provider, if the user `extern_uid` does not change, only the GitLab configuration must be
-updated.
-
-To swap configurations:
-
-1. Change provider configuration in your `gitlab.rb` file.
-1. Update `extern_uid` for all users that have an identity in GitLab for the previous provider.
-
-To find the `extern_uid`, look at an existing user's current `extern_uid` for an ID that matches the appropriate field in
-your current provider for the same user.
-
-There are two methods to update the `extern_uid`:
-
-- Using the [Users API](../../api/users.md#user-modification). Pass the provider name and the new `extern_uid`.
-- Using the [Rails console](../operations/rails_console.md):
-
- ```ruby
- Identity.where(extern_uid: 'old-id').update!(extern_uid: 'new-id')`
- ```
diff --git a/doc/administration/auth/jwt.md b/doc/administration/auth/jwt.md
index 99cba3f220d..71ab084065a 100644
--- a/doc/administration/auth/jwt.md
+++ b/doc/administration/auth/jwt.md
@@ -70,8 +70,9 @@ JWT provides you with a secret key for you to use.
1. Change `YOUR_APP_SECRET` to the client secret and set `auth_url` to your redirect URL.
1. Save the configuration file.
-1. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect if you
- installed GitLab via Omnibus or from source respectively.
+1. For the changes to take effect:
+ - If you installed via Omnibus, [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
+ - If you installed from source, [restart GitLab](../restart_gitlab.md#installations-from-source).
On the sign in page there should now be a JWT icon below the regular sign in form.
Select the icon to begin the authentication process. JWT asks the user to
diff --git a/doc/administration/auth/ldap/index.md b/doc/administration/auth/ldap/index.md
index 2f0a0db9d6f..19f656d2f14 100644
--- a/doc/administration/auth/ldap/index.md
+++ b/doc/administration/auth/ldap/index.md
@@ -344,7 +344,7 @@ The `user_filter` DN can contain special characters. For example:
OU=GitLab\2C Inc,DC=gitlab,DC=com
```
-- Escape open and close brackets with `\28` and `\29`, respectively. For example:
+- Escape open brackets with `\28` and close brackets with `\29`. For example:
```plaintext
OU=Gitlab \28Inc\29,DC=gitlab,DC=com
diff --git a/doc/administration/auth/ldap/ldap-troubleshooting.md b/doc/administration/auth/ldap/ldap-troubleshooting.md
index 64ef27cbf51..a68e6ae2649 100644
--- a/doc/administration/auth/ldap/ldap-troubleshooting.md
+++ b/doc/administration/auth/ldap/ldap-troubleshooting.md
@@ -5,7 +5,9 @@ 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/engineering/ux/technical-writing/#assignments
---
-# LDAP Troubleshooting for Administrators **(FREE SELF)**
+# Troubleshooting LDAP **(FREE SELF)**
+
+If you are an administrator, use the following information to troubleshoot LDAP.
## Common Problems & Workflows
@@ -165,7 +167,7 @@ may see the following message: `Access denied for your LDAP account`.
We have a workaround, based on toggling the access level of affected users:
-1. As an administrator, on the top bar, select **Menu > Admin**.
+1. As an administrator, on the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Select the name of the affected user.
1. In the user's administrative page, press **Edit** on the top right of the page.
@@ -210,7 +212,7 @@ This shows you which user has this email address. One of two steps must be taken
remove this email as a secondary email and make it a primary one so GitLab
associates this profile to the LDAP identity.
-The user can do either of these steps
+The user can do either of these steps
[in their profile](../../../user/profile/index.md#access-your-user-profile) or an administrator can do it.
#### Projects limit errors
@@ -223,7 +225,7 @@ field contains no data:
To resolve this:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, go to **Settings > General**.
1. Expand both of the following:
- **Account and limit**.
@@ -366,7 +368,7 @@ things to debug the situation.
- Ensure the correct [LDAP group link is added to the GitLab group](ldap_synchronization.md#add-group-links).
- Check that the user has an LDAP identity:
1. Sign in to GitLab as an administrator user.
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Search for the user.
1. Open the user by selecting their name. Do not select **Edit**.
@@ -411,6 +413,63 @@ If all the above are true and the users are still not getting access,
[look through the output](#example-console-output-after-a-group-sync) to see what happens when
GitLab syncs the `admin_group`.
+#### Sync now button stuck in the UI
+
+The **Sync now** button on the **Group > Members** page of a group can become stuck. The button becomes stuck after it is pressed and the page is reloaded. The button then
+cannot be selected again.
+
+The **Sync now** button can become stuck for many reasons and requires debugging for specific cases. The following are two possible causes and possible solutions to the problem.
+
+##### Invalid memberships
+
+The **Sync now** button becomes stuck if some of the group's members or requesting members are invalid. You can track progress on improving the visibility of this problem in
+a [relevant issue](https://gitlab.com/gitlab-org/gitlab/-/issues/348226). You can use a [Rails console](#rails-console) to confirm if this problem is causing the **Sync now**
+button to be stuck:
+
+```ruby
+# Find the group in question
+group = Group.find_by(name: 'my_gitlab_group')
+
+# Look for errors on the Group itself
+group.valid?
+group.errors.map(&:full_messages)
+
+# Look for errors among the group's members and requesters
+group.requesters.map(&:valid?)
+group.requesters.map(&:errors).map(&:full_messages)
+group.members.map(&:valid?)
+group.members.map(&:errors).map(&:full_messages)
+```
+
+A displayed error can identify the problem and point to a solution. For example, the Support Team has seen the following error:
+
+```ruby
+irb(main):018:0> group.members.map(&:errors).map(&:full_messages)
+=> [["The member's email address is not allowed for this group. Go to the group’s &#39;Settings &gt; General&#39; page, and check &#39;Restrict membership by email domain&#39;."]]
+```
+
+This error showed that an Administrator chose to [restrict group membership by email domain](../../../user/group/access_and_permissions.md#restrict-group-access-by-domain),
+but there was a typo in the domain. After the domain setting was fixed, the **Sync now** button functioned again.
+
+##### Missing LDAP configuration on Sidekiq nodes
+
+The **Sync now** button becomes stuck when GitLab is scaled over multiple nodes and the LDAP configuration is missing from
+[the `/etc/gitlab/gitlab.rb` on the nodes running Sidekiq](../../sidekiq/index.md#configure-ldap-and-user-or-group-synchronization).
+In this case, the Sidekiq jobs seem to disappear.
+
+LDAP is required on the Sidekiq nodes because LDAP has multiple jobs that are
+run asynchronously that require a local LDAP configuration:
+
+- [User sync](ldap_synchronization.md#user-sync).
+- [Group sync](ldap_synchronization.md#group-sync).
+
+You can test whether missing LDAP configuration is the problem by running [the Rake task to check LDAP](#ldap-check)
+on each node that is running Sidekiq. If LDAP is set up correctly on this node, it connects to the LDAP server and returns users.
+
+To solve this issue, [configure LDAP](../../sidekiq/index.md#configure-ldap-and-user-or-group-synchronization) on the Sidekiq nodes.
+When configured, run [the Rake task to check LDAP](#ldap-check) to confirm
+that the GitLab node can connect to LDAP.
+
#### Sync all groups
NOTE:
@@ -430,7 +489,7 @@ Next, [learn how to read the output](#example-console-output-after-a-group-sync)
##### Example console output after a group sync
-Like the output from the user sync, the output from the
+Like the output from the user sync, the output from the
[manual group sync](#sync-all-groups) is also very verbose. However, it contains lots
of helpful information.
@@ -600,6 +659,66 @@ 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"
+
+You can receive the following error when authenticating with an LDAP server:
+
+```plaintext
+Could not authenticate you from Ldapmain because "Unknown provider (ldapsecondary). available providers: ["ldapmain"]".
+```
+
+This error is caused when using an account that previously authenticated with an LDAP server that is renamed or removed from your GitLab configuration. For example:
+
+- Initially, `main` and `secondary` are set in `ldap_servers` in GitLab configuration.
+- The `secondary` setting is removed or renamed to `main`.
+- A user attempting to sign in has an `identify` record for `secondary`, but that is no longer configured.
+
+Use the [Rails console](../../operations/rails_console.md) to list affected users and check what LDAP servers they have identities for:
+
+```ruby
+ldap_identities = Identity.where(provider: "ldapsecondary")
+ldap_identities.each do |identity|
+ u=User.find_by_id(identity.user_id)
+ ui=Identity.where(user_id: identity.user_id)
+ puts "user: #{u.username}\n #{u.email}\n last activity: #{u.last_activity_on}\n #{identity.provider} ID: #{identity.id} external: #{identity.extern_uid}"
+ puts " all identities:"
+ ui.each do |alli|
+ puts " - #{alli.provider} ID: #{alli.id} external: #{alli.extern_uid}"
+ end
+end;nil
+```
+
+You can solve this error in two ways.
+
+### Rename references to the LDAP server
+
+This solution is suitable when the LDAP servers are replicas of each other, and the affected users should be able to sign in using a configured LDAP server. For example, if a
+load balancer is now used to manage LDAP high availability and a separate secondary sign-in option is no longer needed.
+
+NOTE:
+If the LDAP servers aren't replicas of each other, this solution stops affected users from being able to sign in.
+
+To [rename references to the LDAP server](../../raketasks/ldap.md#other-options) that is no longer configured, run:
+
+```shell
+sudo gitlab-rake gitlab:ldap:rename_provider[ldapsecondary,ldapmain]
+```
+
+### Remove the `identity` records that relate to the removed LDAP server
+
+With this solution, affected users can sign in with the configured LDAP servers and a new `identity` record is created by GitLab. In a
+[Rails console](../../operations/rails_console.md), delete the `ldapsecondary` identities:
+
+```ruby
+ldap_identities = Identity.where(provider: "ldapsecondary")
+ldap_identities.each do |identity|
+ puts "Destroying identity: #{identity.id} #{identity.provider}: #{identity.extern_uid}"
+ identity.destroy!
+rescue => e
+ puts 'Error generated when destroying identity:\n ' + e.to_s
+end; nil
+```
+
## Expired license causes errors with multiple LDAP servers
Using [multiple LDAP servers](index.md#use-multiple-ldap-servers) requires a valid license. An expired license can
diff --git a/doc/administration/auth/ldap/ldap_synchronization.md b/doc/administration/auth/ldap/ldap_synchronization.md
index 62706a9e3b9..37a27fc058e 100644
--- a/doc/administration/auth/ldap/ldap_synchronization.md
+++ b/doc/administration/auth/ldap/ldap_synchronization.md
@@ -38,9 +38,11 @@ fail. This means the user cannot sign in or push or pull code.
The process also updates the following user information:
-- Email address
-- SSH public keys (if `sync_ssh_keys` is set)
-- Kerberos identity (if Kerberos is enabled)
+- Name. Because of a [sync issue](https://gitlab.com/gitlab-org/gitlab/-/issues/342598), `name` is not synchronized if
+ [**Prevent users from changing their profile name**](../../../user/admin_area/settings/account_and_limit_settings.md#disable-user-profile-name-changes) is enabled.
+- Email address.
+- SSH public keys if `sync_ssh_keys` is set.
+- Kerberos identity if Kerberos is enabled.
### Adjust LDAP user sync schedule
@@ -192,7 +194,7 @@ When enabled, the following applies:
To enable it, you must:
1. [Configure LDAP](index.md#configure-ldap).
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. Ensure the **Lock memberships to LDAP synchronization** checkbox is selected.
diff --git a/doc/administration/auth/oidc.md b/doc/administration/auth/oidc.md
index 8c5bf96e99e..9f3c96902f8 100644
--- a/doc/administration/auth/oidc.md
+++ b/doc/administration/auth/oidc.md
@@ -250,7 +250,7 @@ but `LocalAccounts` works for authenticating against local, Active Directory acc
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" PartnerClaimType="email" />
```
-1. For OIDC discovery to work with B2C, the policy must be configured with an issuer compatible with the
+1. For OIDC discovery to work with B2C, the policy must be configured with an issuer compatible with the
[OIDC specification](https://openid.net/specs/openid-connect-discovery-1_0.html#rfc.section.4.3).
See the [token compatibility settings](https://docs.microsoft.com/en-us/azure/active-directory-b2c/configure-tokens?pivots=b2c-custom-policy#token-compatibility-settings).
In `TrustFrameworkBase.xml` under `JwtIssuer`, set `IssuanceClaimPattern` to `AuthorityWithTfp`:
diff --git a/doc/administration/compliance.md b/doc/administration/compliance.md
index 8c7f8bf766d..573ffbf4686 100644
--- a/doc/administration/compliance.md
+++ b/doc/administration/compliance.md
@@ -1,5 +1,5 @@
---
-stage: Manage
+stage: Govern
group: Compliance
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
---
diff --git a/doc/administration/environment_variables.md b/doc/administration/environment_variables.md
index 3adb057daa4..e70758e2774 100644
--- a/doc/administration/environment_variables.md
+++ b/doc/administration/environment_variables.md
@@ -36,6 +36,7 @@ You can use the following environment variables to override certain values:
| `GITLAB_SHARED_RUNNERS_REGISTRATION_TOKEN` | string | Sets the initial registration token used for runners. |
| `RAILS_ENV` | string | The Rails environment; can be one of `production`, `development`, `staging`, or `test`. |
| `UNSTRUCTURED_RAILS_LOG` | string | Enables the unstructured log in addition to JSON logs (defaults to `true`). |
+| `GITLAB_RAILS_CACHE_DEFAULT_TTL_SECONDS` | integer | The default TTL used for entries stored in the Rails-cache. Default is `28800`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95042) in 15.3. |
## Adding more variables
diff --git a/doc/administration/external_pipeline_validation.md b/doc/administration/external_pipeline_validation.md
index 99876cdf503..b9bf0cfdc50 100644
--- a/doc/administration/external_pipeline_validation.md
+++ b/doc/administration/external_pipeline_validation.md
@@ -44,6 +44,7 @@ required number of seconds.
"required" : [
"project",
"user",
+ "credit_card",
"pipeline",
"builds",
"total_builds_count",
@@ -85,6 +86,17 @@ required number of seconds.
"sign_in_count": { "type": "integer" }
}
},
+ "credit_card": {
+ "type": "object",
+ "required": [
+ "similar_cards_count",
+ "similar_holder_names_count"
+ ],
+ "properties": {
+ "similar_cards_count": { "type": "integer" },
+ "similar_holder_names_count": { "type": "integer" }
+ }
+ },
"pipeline": {
"type": "object",
"required": [
diff --git a/doc/administration/feature_flags.md b/doc/administration/feature_flags.md
index 77f7f621a07..75e65c2a9d6 100644
--- a/doc/administration/feature_flags.md
+++ b/doc/administration/feature_flags.md
@@ -117,7 +117,7 @@ Some feature flags can be enabled or disabled on a per project basis:
Feature.enable(:<feature flag>, Project.find(<project id>))
```
-For example, to enable the [`:product_analytics`](../operations/product_analytics.md#enable-or-disable-product-analytics) feature flag for project `1234`:
+For example, to enable the [`:product_analytics`](../operations/product_analytics.md) feature flag for project `1234`:
```ruby
Feature.enable(:product_analytics, Project.find(1234))
@@ -155,6 +155,9 @@ You can view all GitLab administrator set feature flags:
```ruby
Feature.all
=> [#<Flipper::Feature:198220 name="my_awesome_feature", state=:on, enabled_gate_names=[:boolean], adapter=:memoizable>]
+
+# Nice output
+Feature.all.map {|f| [f.name, f.state]}
```
### Unset feature flag
diff --git a/doc/administration/geo/disaster_recovery/background_verification.md b/doc/administration/geo/disaster_recovery/background_verification.md
index 97c9a6c5576..d3aa2c97833 100644
--- a/doc/administration/geo/disaster_recovery/background_verification.md
+++ b/doc/administration/geo/disaster_recovery/background_verification.md
@@ -55,7 +55,7 @@ Feature.enable('geo_repository_verification')
On the **primary** site:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Sites**.
1. Expand **Verification information** tab for that site to view automatic checksumming
status for repositories and wikis. Successes are shown in green, pending work
@@ -65,7 +65,7 @@ On the **primary** site:
On the **secondary** site:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Sites**.
1. Expand **Verification information** tab for that site to view automatic checksumming
status for repositories and wikis. Successes are shown in green, pending work
@@ -93,7 +93,7 @@ increase load and vice versa.
On the **primary** site:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Sites**.
1. Select **Edit** for the **primary** site to customize the minimum
re-verification interval:
@@ -141,7 +141,7 @@ sudo gitlab-rake geo:verification:wiki:reset
If the **primary** and **secondary** sites have a checksum verification mismatch, the cause may not be apparent. To find the cause of a checksum mismatch:
1. On the **primary** site:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Projects**.
1. Find the project that you want to check the checksum differences and
select its name.
diff --git a/doc/administration/geo/disaster_recovery/bring_primary_back.md b/doc/administration/geo/disaster_recovery/bring_primary_back.md
index a2d4f35a7c3..1991b747af0 100644
--- a/doc/administration/geo/disaster_recovery/bring_primary_back.md
+++ b/doc/administration/geo/disaster_recovery/bring_primary_back.md
@@ -41,7 +41,7 @@ To bring the former **primary** site up to date:
NOTE:
If you [changed the DNS records](index.md#step-4-optional-updating-the-primary-domain-dns-record)
- for this site during disaster recovery procedure you may need to
+ for this site during disaster recovery procedure you may need to
[block all the writes to this site](planned_failover.md#prevent-updates-to-the-primary-site)
during this procedure.
diff --git a/doc/administration/geo/disaster_recovery/index.md b/doc/administration/geo/disaster_recovery/index.md
index 0cae94fcec1..e82c130ba01 100644
--- a/doc/administration/geo/disaster_recovery/index.md
+++ b/doc/administration/geo/disaster_recovery/index.md
@@ -93,7 +93,7 @@ Note the following when promoting a secondary:
the **secondary** to the **primary**.
- If you encounter an `ActiveRecord::RecordInvalid: Validation failed: Name has already been taken`
error message during this process, for more information, see this
- [troubleshooting advice](../replication/troubleshooting.md#fixing-errors-during-a-failover-or-when-promoting-a-secondary-to-a-primary-node).
+ [troubleshooting advice](../replication/troubleshooting.md#fixing-errors-during-a-failover-or-when-promoting-a-secondary-to-a-primary-site).
- If you run into errors when using `--force` or `--skip-preflight-checks` before 13.5 during this process,
for more information, see this
[troubleshooting advice](../replication/troubleshooting.md#errors-when-using---skip-preflight-checks-or---force).
@@ -774,4 +774,4 @@ If you are running GitLab 14.4 and earlier:
## Troubleshooting
-This section was moved to [another location](../replication/troubleshooting.md#fixing-errors-during-a-failover-or-when-promoting-a-secondary-to-a-primary-node).
+This section was moved to [another location](../replication/troubleshooting.md#fixing-errors-during-a-failover-or-when-promoting-a-secondary-to-a-primary-site).
diff --git a/doc/administration/geo/disaster_recovery/planned_failover.md b/doc/administration/geo/disaster_recovery/planned_failover.md
index 6c3353e7d7e..d0dbecce43a 100644
--- a/doc/administration/geo/disaster_recovery/planned_failover.md
+++ b/doc/administration/geo/disaster_recovery/planned_failover.md
@@ -149,7 +149,7 @@ ensure these processes are close to 100% as possible during active use.
On the **secondary** site:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Sites**.
Replicated objects (shown in green) should be close to 100%,
and there should be no failures (shown in red). If a large proportion of
@@ -177,7 +177,7 @@ This [content was moved to another location](background_verification.md).
On the **primary** site:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Messages**.
1. Add a message notifying users on the maintenance window.
You can check under **Geo > Sites** to estimate how long it
@@ -190,7 +190,7 @@ To ensure that all data is replicated to a secondary site, updates (write reques
be disabled on the **primary** site:
1. Enable [maintenance mode](../../maintenance_mode/index.md) on the **primary** site.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
1. On the Sidekiq dashboard, select **Cron**.
1. Select `Disable All` to disable non-Geo periodic background jobs.
@@ -206,7 +206,7 @@ GitLab 13.9 through GitLab 14.3 are affected by a bug in which the Geo secondary
1. If you are manually replicating any data not managed by Geo, trigger the
final replication process now.
1. On the **primary** site:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
1. On the Sidekiq dashboard, select **Queues**, and wait for all queues except
those with `geo` in the name to drop to 0.
@@ -221,7 +221,7 @@ GitLab 13.9 through GitLab 14.3 are affected by a bug in which the Geo secondary
- The Geo log cursor is up to date (0 events behind).
1. On the **secondary** site:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
1. On the Sidekiq dashboard, select **Queues**, and wait for all the `geo`
queues to drop to 0 queued and 0 running jobs.
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 46b2ccddefd..11baf383c67 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
@@ -68,7 +68,7 @@ GitLab 13.9 through GitLab 14.3 are affected by a bug in which the Geo secondary
On the **secondary** site:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
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
@@ -133,7 +133,7 @@ follow these steps to avoid unnecessary data loss:
connection.
1. On the **primary** site:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
1. On the Sidekiq dashboard, select **Cron**.
1. Select `Disable All` to disable any non-Geo periodic background jobs.
@@ -151,7 +151,7 @@ follow these steps to avoid unnecessary data loss:
[data not managed by Geo](../../replication/datatypes.md#limitations-on-replicationverification),
trigger the final replication process now.
1. On the **primary** site:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
1. On the Sidekiq dashboard, select **Queues**, and wait for all queues except
those with `geo` in the name to drop to 0.
@@ -166,7 +166,7 @@ follow these steps to avoid unnecessary data loss:
- The Geo log cursor is up to date (0 events behind).
1. On the **secondary** site:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
1. On the Sidekiq dashboard, select **Queues**, and wait for all the `geo`
queues to drop to 0 queued and 0 running jobs.
@@ -274,7 +274,7 @@ the **secondary** to the **primary**.
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-node).
+[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
conjunction with multiple servers, as it can only
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 da26023e4f9..2958c119c20 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
@@ -118,7 +118,7 @@ follow these steps to avoid unnecessary data loss:
connection.
1. On the **primary** site:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
1. On the Sidekiq dashboard, select **Cron**.
1. Select `Disable All` to disable any non-Geo periodic background jobs.
@@ -136,7 +136,7 @@ follow these steps to avoid unnecessary data loss:
[data not managed by Geo](../../replication/datatypes.md#limitations-on-replicationverification),
trigger the final replication process now.
1. On the **primary** site:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
1. On the Sidekiq dashboard, select **Queues**, and wait for all queues except
those with `geo` in the name to drop to 0.
@@ -151,7 +151,7 @@ follow these steps to avoid unnecessary data loss:
- The Geo log cursor is up to date (0 events behind).
1. On the **secondary** site:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
1. On the Sidekiq dashboard, select **Queues**, and wait for all the `geo`
queues to drop to 0 queued and 0 running jobs.
@@ -220,7 +220,7 @@ Note the following when promoting a secondary:
the **secondary** to the **primary**.
- 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-node).
+ [the troubleshooting advice](../../replication/troubleshooting.md#fixing-errors-during-a-failover-or-when-promoting-a-secondary-to-a-primary-site).
To promote the secondary site running GitLab 14.5 and later:
diff --git a/doc/administration/geo/index.md b/doc/administration/geo/index.md
index e3bf5ccdfe7..db298d4fdfc 100644
--- a/doc/administration/geo/index.md
+++ b/doc/administration/geo/index.md
@@ -47,7 +47,7 @@ In addition, it:
Geo provides:
- Read-only **secondary** sites: Maintain one **primary** GitLab site while still enabling read-only **secondary** sites for each of your distributed teams.
-- Authentication system hooks: **Secondary** sites receives all authentication data (like user accounts and logins) from the **primary** instance.
+- Authentication system hooks: **Secondary** sites receive all authentication data (like user accounts and logins) from the **primary** instance.
- An intuitive UI: **Secondary** sites use the same web interface your team has grown accustomed to. In addition, there are visual notifications that block write operations and make it clear that a user is on a **secondary** sites.
### Gitaly Cluster
@@ -124,6 +124,7 @@ The following are required to run Geo:
- Git 2.9 or later
- Git-lfs 2.4.2 or later on the user side when using LFS
- All sites must run [the same GitLab and PostgreSQL versions](setup/database.md#postgresql-replication).
+- If using different operating system versions between Geo sites, [check OS locale data compatibility](replication/troubleshooting.md#check-os-locale-data-compatibility) across Geo sites.
Additionally, check the GitLab [minimum requirements](../../install/requirements.md),
and we recommend you use the latest version of GitLab for a better experience.
@@ -158,7 +159,7 @@ public URL of the primary site is used.
To update the internal URL of the primary Geo site:
-1. On the top bar, go to **Menu > Admin > Geo > Sites**.
+1. On the top bar, select **Main menu > Admin > Geo > Sites**.
1. Select **Edit** on the primary site.
1. Change the **Internal URL**, then select **Save changes**.
@@ -204,6 +205,7 @@ This list of limitations only reflects the latest version of GitLab. If you are
- GitLab Runners cannot register with a **secondary** site. Support for this is [planned for the future](https://gitlab.com/gitlab-org/gitlab/-/issues/3294).
- [Selective synchronization](replication/configuration.md#selective-synchronization) only limits what repositories and files are replicated. The entire PostgreSQL data is still replicated. Selective synchronization is not built to accommodate compliance / export control use cases.
- [Pages access control](../../user/project/pages/pages_access_control.md) doesn't work on secondaries. See [GitLab issue #9336](https://gitlab.com/gitlab-org/gitlab/-/issues/9336) for details.
+- [GitLab chart with Geo](https://docs.gitlab.com/charts/advanced/geo/) does not support [Unified URLs](secondary_proxy/index.md#set-up-a-unified-url-for-geo-sites). See [GitLab issue #3522](https://gitlab.com/gitlab-org/charts/gitlab/-/issues/3522) for more details.
### Limitations on replication/verification
diff --git a/doc/administration/geo/replication/configuration.md b/doc/administration/geo/replication/configuration.md
index 7666a450648..39d1f5ae602 100644
--- a/doc/administration/geo/replication/configuration.md
+++ b/doc/administration/geo/replication/configuration.md
@@ -202,7 +202,7 @@ keys must be manually replicated to the **secondary** site.
```
1. Navigate to the Primary Node GitLab Instance:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Sites**.
1. Select **Add site**.
![Add secondary site](img/adding_a_secondary_v13_3.png)
@@ -222,19 +222,23 @@ keys must be manually replicated to the **secondary** site.
gitlab-ctl restart
```
- Check if there are any common issue with your Geo setup by running:
+ Check if there are any common issues with your Geo setup by running:
```shell
gitlab-rake gitlab:geo:check
```
+ If any of the checks fail, check the [troubleshooting documentation](troubleshooting.md).
+
1. SSH into a **Rails or Sidekiq server on your primary** site and login as root to verify the
- **secondary** site is reachable or there are any common issue with your Geo setup:
+ **secondary** site is reachable or there are any common issues with your Geo setup:
```shell
gitlab-rake gitlab:geo:check
```
+ If any of the checks fail, check the [troubleshooting documentation](troubleshooting.md).
+
Once added to the Geo administration page and restarted, the **secondary** site automatically starts
replicating missing data from the **primary** site in a process known as **backfill**.
Meanwhile, the **primary** site starts to notify each **secondary** site of any changes, so
@@ -305,7 +309,7 @@ method to be enabled. This is enabled by default, but if converting an existing
On the **primary** site:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand **Visibility and access controls**.
1. Ensure "Enabled Git access protocols" is set to either "Both SSH and HTTP(S)" or "Only HTTP(S)".
@@ -315,7 +319,7 @@ On the **primary** site:
You can sign in to the **secondary** site with the same credentials you used with
the **primary** site. After you sign in:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Sites**.
1. Verify that it's correctly identified as a **secondary** Geo site, and that
Geo is enabled.
diff --git a/doc/administration/geo/replication/container_registry.md b/doc/administration/geo/replication/container_registry.md
index b425e5dcc0d..01ba81b6dbe 100644
--- a/doc/administration/geo/replication/container_registry.md
+++ b/doc/administration/geo/replication/container_registry.md
@@ -12,7 +12,7 @@ You can set up a Container Registry on your **secondary** Geo site that mirrors
## Supported container registries
Geo supports the following types of container registries:
-
+
- [Docker](https://docs.docker.com/registry/)
- [OCI](https://github.com/opencontainers/distribution-spec/blob/main/spec.md)
@@ -26,7 +26,7 @@ The following container image formats are support by Geo:
In addition, Geo also supports [BuildKit cache images](https://github.com/moby/buildkit).
-## Supported storage
+## Supported storage
### Docker
@@ -34,7 +34,7 @@ For more information on supported registry storage drivers see
[Docker registry storage drivers](https://docs.docker.com/registry/storage-drivers/)
Read the [Load balancing considerations](https://docs.docker.com/registry/deploying/#load-balancing-considerations)
-when deploying the Registry, and how to set up the storage driver for the GitLab integrated
+when deploying the Registry, and how to set up the storage driver for the GitLab integrated
[Container Registry](../../packages/container_registry.md#use-object-storage).
### Registries that support OCI artifacts
@@ -160,7 +160,7 @@ For each application and Sidekiq node on the **secondary** site:
To verify Container Registry replication is working, on the **secondary** site:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Nodes**.
The initial replication, or "backfill", is probably still in progress.
diff --git a/doc/administration/geo/replication/disable_geo.md b/doc/administration/geo/replication/disable_geo.md
index f0658ae45a2..84bc2e034b9 100644
--- a/doc/administration/geo/replication/disable_geo.md
+++ b/doc/administration/geo/replication/disable_geo.md
@@ -36,7 +36,7 @@ to do that.
To remove the **primary** site:
1. [Remove all secondary Geo sites](remove_geo_site.md)
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Nodes**.
1. Select **Remove** for the **primary** node.
1. Confirm by selecting **Remove** when the prompt appears.
diff --git a/doc/administration/geo/replication/object_storage.md b/doc/administration/geo/replication/object_storage.md
index d2e10678f8c..0336a1669f9 100644
--- a/doc/administration/geo/replication/object_storage.md
+++ b/doc/administration/geo/replication/object_storage.md
@@ -41,7 +41,7 @@ whether they are stored on the local file system or in object storage.
To enable GitLab replication:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Nodes**.
1. Select **Edit** on the **secondary** site.
1. In the **Synchronization Settings** section, find the **Allow this secondary node to replicate content on Object Storage**
diff --git a/doc/administration/geo/replication/remove_geo_site.md b/doc/administration/geo/replication/remove_geo_site.md
index 0d6715a93b7..b136f6cc8b8 100644
--- a/doc/administration/geo/replication/remove_geo_site.md
+++ b/doc/administration/geo/replication/remove_geo_site.md
@@ -9,7 +9,7 @@ type: howto
**Secondary** sites can be removed from the Geo cluster using the Geo administration page of the **primary** site. To remove a **secondary** site:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Nodes**.
1. Select the **Remove** button for the **secondary** site you want to remove.
1. Confirm by selecting **Remove** when the prompt appears.
diff --git a/doc/administration/geo/replication/troubleshooting.md b/doc/administration/geo/replication/troubleshooting.md
index 26d192f62cd..d64ad2549e8 100644
--- a/doc/administration/geo/replication/troubleshooting.md
+++ b/doc/administration/geo/replication/troubleshooting.md
@@ -19,24 +19,24 @@ Here is a list of steps you should take to attempt to fix problem:
Before attempting more advanced troubleshooting:
-- Check [the health of the **secondary** node](#check-the-health-of-the-secondary-node).
+- Check [the health of the **secondary** site](#check-the-health-of-the-secondary-site).
- Check [if PostgreSQL replication is working](#check-if-postgresql-replication-is-working).
-### Check the health of the **secondary** node
+### Check the health of the **secondary** site
-On the **primary** node:
+On the **primary** site:
-1. On the top bar, select **Menu > Admin**.
-1. On the left sidebar, select **Geo > Nodes**.
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Geo > Sites**.
-We perform the following health checks on each **secondary** node
+We perform the following health checks on each **secondary** site
to help identify if something is wrong:
-- Is the node running?
-- Is the node's secondary database configured for streaming replication?
-- Is the node's secondary tracking database configured?
-- Is the node's secondary tracking database connected?
-- Is the node's secondary tracking database up-to-date?
+- Is the site running?
+- Is the secondary site's database configured for streaming replication?
+- Is the secondary site's tracking database configured?
+- Is the secondary site's tracking database connected?
+- Is the secondary site's tracking database up-to-date?
![Geo health check](img/geo_site_health_v14_0.png)
@@ -48,8 +48,8 @@ health check manually to get this information and a few more details.
#### Health check Rake task
-This Rake task can be run on an app node in the **primary** or **secondary**
-Geo nodes:
+This Rake task can be run on a **Rails** node in the **primary** or **secondary**
+Geo sites:
```shell
sudo gitlab-rake gitlab:geo:check
@@ -275,11 +275,11 @@ sudo gitlab-rake gitlab:geo:check
Checking Geo ... Finished
```
- Ensure you have added the secondary node in the Admin Area of the **primary** node.
- Also ensure you entered the `external_url` or `gitlab_rails['geo_node_name']`
- when adding the secondary node in the Admin Area of the **primary** node.
- In GitLab 12.3 and earlier, edit the secondary node in the Admin Area of the **primary**
- node and ensure that there is a trailing `/` in the `Name` field.
+ Ensure you have added the secondary site in the **Main menu > Admin > Geo > Sites** on the web interface for the **primary** site.
+ Also ensure you entered the `gitlab_rails['geo_node_name']`
+ when adding the secondary site in the Admin Area of the **primary** site.
+ In GitLab 12.3 and earlier, edit the secondary site in the Admin Area of the **primary**
+ site and ensure that there is a trailing `/` in the `Name` field.
- Check returns `Exception: PG::UndefinedTable: ERROR: relation "geo_nodes" does not exist`.
@@ -321,7 +321,7 @@ error messages (indicated by `Database replication working? ... no` in the
This means that the `max_replication_slots` PostgreSQL variable needs to
be set on the **primary** database. This setting defaults to 1. You may need to
-increase this value if you have more **secondary** nodes.
+increase this value if you have more **secondary** sites.
Be sure to restart PostgreSQL for this to take effect. See the
[PostgreSQL replication setup](../setup/database.md#postgresql-replication) guide for more details.
@@ -329,13 +329,13 @@ Be sure to restart PostgreSQL for this to take effect. See the
### Message: `FATAL: could not start WAL streaming: ERROR: replication slot "geo_secondary_my_domain_com" does not exist`?
This occurs when PostgreSQL does not have a replication slot for the
-**secondary** node by that name.
+**secondary** site by that name.
-You may want to rerun the [replication process](../setup/database.md) on the **secondary** node .
+You may want to rerun the [replication process](../setup/database.md) on the **secondary** site .
### Message: "Command exceeded allowed execution time" when setting up replication?
-This may happen while [initiating the replication process](../setup/database.md#step-3-initiate-the-replication-process) on the **secondary** node,
+This may happen while [initiating the replication process](../setup/database.md#step-3-initiate-the-replication-process) on the **secondary** site,
and indicates your initial dataset is too large to be replicated in the default timeout (30 minutes).
Re-run `gitlab-ctl replicate-geo-database`, but include a larger value for
@@ -374,8 +374,8 @@ log data to build up in `pg_xlog`. Removing the unused slots can reduce the amou
Slots where `active` is `f` are not active.
-- When this slot should be active, because you have a **secondary** node configured using that slot,
- sign in to that **secondary** node and check the [PostgreSQL logs](../../logs/index.md#postgresql-logs)
+- When this slot should be active, because you have a **secondary** site configured using that slot,
+ sign in on the web interface for the **secondary** site and check the [PostgreSQL logs](../../logs/index.md#postgresql-logs)
to view why the replication is not running.
- If you are no longer using the slot (for example, you no longer have Geo enabled), you can remove it with in the
@@ -398,12 +398,12 @@ These long-running queries are
[planned to be removed in the future](https://gitlab.com/gitlab-org/gitlab/-/issues/34269),
but as a workaround, we recommend enabling
[`hot_standby_feedback`](https://www.postgresql.org/docs/10/hot-standby.html#HOT-STANDBY-CONFLICT).
-This increases the likelihood of bloat on the **primary** node as it prevents
+This increases the likelihood of bloat on the **primary** site as it prevents
`VACUUM` from removing recently-dead rows. However, it has been used
successfully in production on GitLab.com.
To enable `hot_standby_feedback`, add the following to `/etc/gitlab/gitlab.rb`
-on the **secondary** node:
+on the **secondary** site:
```ruby
postgresql['hot_standby_feedback'] = 'on'
@@ -463,14 +463,14 @@ This happens if data is detected in the `projects` table. When one or more proje
is aborted to prevent accidental data loss. To bypass this message, pass the `--force` option to the command.
In GitLab 13.4, a seed project is added when GitLab is first installed. This makes it necessary to pass `--force` even
-on a new Geo secondary node. There is an [issue to account for seed projects](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5618)
+on a new Geo secondary site. There is an [issue to account for seed projects](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5618)
when checking the database.
### Message: `Synchronization failed - Error syncing repository`
WARNING:
If large repositories are affected by this problem,
-their resync may take a long time and cause significant load on your Geo nodes,
+their resync may take a long time and cause significant load on your Geo sites,
storage and network systems.
If you see the error message `Synchronization failed - Error syncing repository` along with `fatal: fsck error in packed object`, this indicates
@@ -483,7 +483,7 @@ it's possible to override the consistency checks instead. To do that, follow
[the instructions in the Gitaly docs](../../gitaly/configure_gitaly.md#repository-consistency-checks).
You can also get the error message `Synchronization failed - Error syncing repository` along with the following log messages, this indicates that the expected `geo` remote is not present in the `.git/config` file
-of a repository on the secondary Geo node's file system:
+of a repository on the secondary Geo site's file system:
```json
{
@@ -505,7 +505,7 @@ of a repository on the secondary Geo node's file system:
To solve this:
-1. Sign in to the secondary Geo node.
+1. Sign in on the web interface for the secondary Geo site.
1. Back up [the `.git` folder](../../repository_storage_types.md#translate-hashed-storage-paths).
@@ -538,7 +538,7 @@ To solve this:
end
```
-### Very large repositories never successfully synchronize on the **secondary** node
+### Very large repositories never successfully synchronize on the **secondary** site
GitLab places a timeout on all repository clones, including project imports
and Geo synchronization operations. If a fresh `git clone` of a repository
@@ -546,7 +546,8 @@ on the **primary** takes more than the default three hours, you may be affected
To increase the timeout:
-1. On the **secondary** node, add the following line to `/etc/gitlab/gitlab.rb`:
+1. On the **Sidekiq nodes on your secondary** site,
+add the following line to `/etc/gitlab/gitlab.rb`:
```ruby
gitlab_rails['gitlab_shell_git_timeout'] = 14400
@@ -563,9 +564,9 @@ long enough to accommodate a full clone of your largest repositories.
### New LFS objects are never replicated
-If new LFS objects are never replicated to secondary Geo nodes, check the version of
+If new LFS objects are never replicated to secondary Geo sites, check the version of
GitLab you are running. GitLab versions 11.11.x or 12.0.x are affected by
-[a bug that results in new LFS objects not being replicated to Geo secondary nodes](https://gitlab.com/gitlab-org/gitlab/-/issues/32696).
+[a bug that results in new LFS objects not being replicated to Geo secondary sites](https://gitlab.com/gitlab-org/gitlab/-/issues/32696).
To resolve the issue, upgrade to GitLab 12.1 or later.
@@ -574,9 +575,9 @@ To resolve the issue, upgrade to GitLab 12.1 or later.
During a [backfill](../index.md#backfill), failures are scheduled to be retried at the end
of the backfill queue, therefore these failures only clear up **after** the backfill completes.
-### Resetting Geo **secondary** node replication
+### Resetting Geo **secondary** site replication
-If you get a **secondary** node in a broken state and want to reset the replication state,
+If you get a **secondary** site in a broken state and want to reset the replication state,
to start again from scratch, there are a few steps that can help you:
1. Stop Sidekiq and the Geo LogCursor.
@@ -617,8 +618,8 @@ to start again from scratch, there are a few steps that can help you:
1. Optional. Rename other data folders and create new ones.
WARNING:
- You may still have files on the **secondary** node that have been removed from the **primary** node, but this
- removal has not been reflected. If you skip this step, these files are not removed from the Geo node.
+ You may still have files on the **secondary** site that have been removed from the **primary** site, but this
+ removal has not been reflected. If you skip this step, these files are not removed from the Geo **secondary** site.
Any uploaded content (like file attachments, avatars, or LFS objects) is stored in a
subfolder in one of these paths:
@@ -667,7 +668,7 @@ to start again from scratch, there are a few steps that can help you:
### Design repository failures on mirrored projects and project imports
-On the top bar, under **Menu > Admin > Geo > Nodes**,
+On the top bar, under **Main menu > Admin > Geo > Sites**,
if the Design repositories progress bar shows
`Synced` and `Failed` greater than 100%, and negative `Queued`, the instance
is likely affected by
@@ -714,7 +715,7 @@ Counts:
{"synced"=>3}
```
-#### If you are promoting a Geo secondary site running on a single server
+#### If you are promoting a Geo secondary site running on a single node
`gitlab-ctl promotion-preflight-checks` fails due to the existence of
`failed` rows in the `geo_design_registry` table. Use the
@@ -831,10 +832,10 @@ We recommend transferring each failing repository individually and checking for
after each transfer. Follow the [single target `rsync` instructions](../../operations/moving_repositories.md#single-rsync-to-another-server)
to transfer each affected repository from the primary to the secondary site.
-## Fixing errors during a failover or when promoting a secondary to a primary node
+## Fixing errors during a failover or when promoting a secondary to a primary site
The following are possible error messages that might be encountered during failover or
-when promoting a secondary to a primary node with strategies to resolve them.
+when promoting a secondary to a primary site with strategies to resolve them.
### Message: `ActiveRecord::RecordInvalid: Validation failed: Name has already been taken`
@@ -868,14 +869,14 @@ or `gitlab-ctl promote-to-primary-node`, either:
```
- Upgrade to GitLab 12.6.3 or later if it is safe to do so. For example,
- if the failover was just a test. A
+ if the failover was just a test. A
[caching-related bug](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22021) was fixed.
### Message: `ActiveRecord::RecordInvalid: Validation failed: Enabled Geo primary node cannot be disabled`
-If you disabled a secondary node, either with the [replication pause task](../index.md#pausing-and-resuming-replication)
+If you disabled a secondary site, either with the [replication pause task](../index.md#pausing-and-resuming-replication)
(GitLab 13.2) or by using the user interface (GitLab 13.1 and earlier), you must first
-re-enable the node before you can continue. This is fixed in GitLab 13.4.
+re-enable the site before you can continue. This is fixed in GitLab 13.4.
This can be fixed in the database.
@@ -894,7 +895,7 @@ This can be fixed in the database.
```
1. Run the following command, replacing `https://<secondary url>/` with the URL
- for your secondary server. You can use either `http` or `https`, but ensure that you
+ for your secondary node. You can use either `http` or `https`, but ensure that you
end the URL with a slash (`/`):
```sql
@@ -987,32 +988,31 @@ sudo gitlab-rake geo:set_secondary_as_primary
## Expired artifacts
If you notice for some reason there are more artifacts on the Geo
-secondary node than on the Geo primary node, you can use the Rake task
+**secondary** site than on the Geo **primary** site, you can use the Rake task
to [cleanup orphan artifact files](../../../raketasks/cleanup.md#remove-orphan-artifact-files).
-On a Geo **secondary** node, this command also cleans up all Geo
+On a Geo **secondary** site, this command also cleans up all Geo
registry record related to the orphan files on disk.
## Fixing sign in errors
### Message: The redirect URI included is not valid
-If you are able to sign in to the **primary** node, but you receive this error message
-when attempting to sign in to a **secondary**, you should verify the Geo
-node's URL matches its external URL.
+If you are able to sign in to the web interface for the **primary** site, but you receive this error message
+when attempting to sign in to a **secondary** web interface, you should verify the Geo
+site's URL matches its external URL.
-On the **primary** node:
+On the **primary** site:
-1. On the top bar, select **Menu > Admin**.
-1. On the left sidebar, select **Geo > Nodes**.
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Geo > Sites**.
1. Find the affected **secondary** site and select **Edit**.
1. Ensure the **URL** field matches the value found in `/etc/gitlab/gitlab.rb`
- in `external_url "https://gitlab.example.com"` on the frontend servers of
- the **secondary** node.
+ in `external_url "https://gitlab.example.com"` on the **Rails nodes of the secondary** site.
## Fixing common errors
-This section documents common error messages reported in the Admin Area, and how to fix them.
+This section documents common error messages reported in the Admin Area on the web interface, and how to fix them.
### Geo database configuration file is missing
@@ -1029,11 +1029,11 @@ has the correct permissions.
Geo cannot reuse an existing tracking database.
It is safest to use a fresh secondary, or reset the whole secondary by following
-[Resetting Geo secondary node replication](#resetting-geo-secondary-node-replication).
+[Resetting Geo secondary site replication](#resetting-geo-secondary-site-replication).
-### Geo node has a database that is writable which is an indication it is not configured for replication with the primary node
+### Geo site has a database that is writable which is an indication it is not configured for replication with the primary site
-This error message refers to a problem with the database replica on a **secondary** node,
+This error message refers to a problem with the database replica on a **secondary** site,
which Geo expects to have access to. It usually means, either:
- An unsupported replication method was used (for example, logical replication).
@@ -1043,24 +1043,24 @@ which Geo expects to have access to. It usually means, either:
Geo **secondary** sites require two separate PostgreSQL instances:
-- A read-only replica of the **primary** node.
+- A read-only replica of the **primary** site.
- A regular, writable instance that holds replication metadata. That is, the Geo tracking database.
This error message indicates that the replica database in the **secondary** site is misconfigured and replication has stopped.
To restore the database and resume replication, you can do one of the following:
-- [Reset the Geo secondary site replication](#resetting-geo-secondary-node-replication).
+- [Reset the Geo secondary site replication](#resetting-geo-secondary-site-replication).
- [Set up a new secondary Geo Omnibus instance](../setup/index.md#using-omnibus-gitlab).
If you set up a new secondary from scratch, you must also [remove the old site from the Geo cluster](remove_geo_site.md#removing-secondary-geo-sites).
-### Geo node does not appear to be replicating the database from the primary node
+### Geo site does not appear to be replicating the database from the primary site
The most common problems that prevent the database from replicating correctly are:
-- **Secondary** nodes cannot reach the **primary** node. Check credentials, firewall rules, and so on.
-- SSL certificate problems. Make sure you copied `/etc/gitlab/gitlab-secrets.json` from the **primary** node.
+- **Secondary** sites cannot reach the **primary** site. Check credentials, [firewall rules](../index.md#firewall-rules), and so on.
+- SSL certificate problems. Make sure you copied `/etc/gitlab/gitlab-secrets.json` from the **primary** site.
- Database storage disk is full.
- Database replication slot is misconfigured.
- Database is not using a replication slot or another alternative and cannot catch-up because WAL files were purged.
@@ -1072,26 +1072,26 @@ Make sure you follow the [Geo database replication](../setup/database.md) instru
If you are using Omnibus GitLab installation, something might have failed during upgrade. You can:
- Run `sudo gitlab-ctl reconfigure`.
-- Manually trigger the database migration by running: `sudo gitlab-rake db:migrate:geo` as root on the **secondary** node.
+- Manually trigger the database migration by running: `sudo gitlab-rake db:migrate:geo` as root on the **secondary** site.
### GitLab indicates that more than 100% of repositories were synced
This can be caused by orphaned records in the project registry. You can clear them
[using a Rake task](../../../administration/raketasks/geo.md#remove-orphaned-project-registries).
-### Geo Admin Area returns 404 error for a secondary node
+### Geo Admin Area returns 404 error for a secondary site
-Sometimes `sudo gitlab-rake gitlab:geo:check` indicates that the **secondary** node is
-healthy, but a 404 Not Found error message for the **secondary** node is returned in the Geo Admin Area on
-the **primary** node.
+Sometimes `sudo gitlab-rake gitlab:geo:check` indicates that **Rails nodes of the secondary** sites are
+healthy, but a 404 Not Found error message for the **secondary** site is returned in the Geo Admin Area on the web interface for
+the **primary** site.
To resolve this issue:
-- Try restarting the **secondary** using `sudo gitlab-ctl restart`.
-- Check `/var/log/gitlab/gitlab-rails/geo.log` to see if the **secondary** node is
- using IPv6 to send its status to the **primary** node. If it is, add an entry to
- the **primary** node using IPv4 in the `/etc/hosts` file. Alternatively, you should
- [enable IPv6 on the **primary** node](https://docs.gitlab.com/omnibus/settings/nginx.html#setting-the-nginx-listen-address-or-addresses).
+- Try restarting **each Rails, Sidekiq and Gitaly nodes on your secondary site** using `sudo gitlab-ctl restart`.
+- Check `/var/log/gitlab/gitlab-rails/geo.log` on Sidekiq nodes to see if the **secondary** site is
+ using IPv6 to send its status to the **primary** site. If it is, add an entry to
+ the **primary** site using IPv4 in the `/etc/hosts` file. Alternatively, you should
+ [enable IPv6 on the **primary** site](https://docs.gitlab.com/omnibus/settings/nginx.html#setting-the-nginx-listen-address-or-addresses).
### Secondary site returns 502 errors with Geo proxying
@@ -1167,7 +1167,7 @@ To fix this issue, set the primary site's internal URL to a URL that is:
You may have problems if you're running a version of [Git LFS](https://git-lfs.github.com/) before 2.4.2.
As noted in [this authentication issue](https://github.com/git-lfs/git-lfs/issues/3025),
-requests redirected from the secondary to the primary node do not properly send the
+requests redirected from the secondary to the primary site do not properly send the
Authorization header. This may result in either an infinite `Authorization <-> Redirect`
loop, or Authorization error messages.
@@ -1194,13 +1194,13 @@ The partial failover to a secondary Geo *site* may be the result of a temporary/
1. SSH into every Sidekiq, PostgresSQL, Gitaly, and Rails node in the **secondary** site and run one of the following commands:
- - To promote the secondary node to primary:
+ - To promote the secondary site to primary:
```shell
sudo gitlab-ctl geo promote
```
- - To promote the secondary node to primary **without any further confirmation**:
+ - To promote the secondary site to primary **without any further confirmation**:
```shell
sudo gitlab-ctl geo promote --force
@@ -1230,3 +1230,37 @@ If the above steps are **not successful**, proceed through the next steps:
1. Verify you can connect to the newly-promoted **primary** site using the URL used previously for the **secondary** site.
1. If successful, the **secondary** site is now promoted to the **primary** site.
+
+## Additional tools
+
+There are useful snippets for manipulating Geo internals in the [GitLab Rails Cheat Sheet](../../troubleshooting/gitlab_rails_cheat_sheet.md#geo). For example, you can find how to manually sync or verify a replicable in Rails console.
+
+## Check OS locale data compatibility
+
+If different operating systems or different operating system versions are deployed across Geo sites, we recommend that you perform a locale data compatibility check setting up Geo.
+
+Geo uses PostgreSQL and Streaming Replication to replicate data across Geo sites. PostgreSQL uses locale data provided by the operating system’s C library for sorting text. If the locale data in the C library is incompatible across Geo sites, erroneous query results that lead to [incorrect behavior on secondary sites](https://gitlab.com/gitlab-org/gitlab/-/issues/360723). See [here](https://wiki.postgresql.org/wiki/Locale_data_changes) for more details.
+
+On all hosts running PostgreSQL, across all Geo sites, run the following shell command:
+
+```shell
+( echo "1-1"; echo "11" ) | LC_COLLATE=en_US.UTF-8 sort
+```
+
+The output will either look like:
+
+```plaintext
+1-1
+11
+```
+
+or the reverse order:
+
+```plaintext
+11
+1-1
+```
+
+If the output is identical on all hosts, then they running compatible versions of locale data.
+
+If the output differs on some hosts, then PostgreSQL replication will not work properly. We advise that you select operating system versions that are compatible.
diff --git a/doc/administration/geo/replication/tuning.md b/doc/administration/geo/replication/tuning.md
index 755ab45a76c..370c50c93db 100644
--- a/doc/administration/geo/replication/tuning.md
+++ b/doc/administration/geo/replication/tuning.md
@@ -14,7 +14,7 @@ in the background.
On the **primary** site:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Sites**.
1. Select **Edit** of the secondary site you want to tune.
1. Under **Tuning settings**, there are several variables that can be tuned to
diff --git a/doc/administration/geo/replication/version_specific_upgrades.md b/doc/administration/geo/replication/version_specific_upgrades.md
index 350310c7076..6211b5689b1 100644
--- a/doc/administration/geo/replication/version_specific_upgrades.md
+++ b/doc/administration/geo/replication/version_specific_upgrades.md
@@ -157,6 +157,13 @@ to perform the following additional steps for the zero-downtime upgrade:
sudo gitlab-rake db:migrate
```
+1. Hot reload `puma` and `sidekiq` services:
+
+ ```shell
+ sudo gitlab-ctl hup puma
+ sudo gitlab-ctl restart sidekiq
+ ```
+
If you have already run the final `sudo gitlab-rake db:migrate` command on the deploy node and have
encountered the [column rename issue](https://gitlab.com/gitlab-org/gitlab/-/issues/324160), you might
see the following error:
@@ -183,8 +190,8 @@ GitLab 13.9 through GitLab 14.3 are affected by a bug in which enabling [GitLab
each upgraded reference. Delay any upgrade attempts until this is in the
[13.7.5 patch release.](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/3002).
More details are available [in this issue](https://gitlab.com/gitlab-org/git/-/issues/79).
-- A new secret is generated in `/etc/gitlab/gitlab-secrets.json`.
- In an HA GitLab or GitLab Geo environment, secrets need to be the same on all nodes.
+- A new secret is generated in `/etc/gitlab/gitlab-secrets.json`.
+ In an HA GitLab or GitLab Geo environment, secrets need to be the same on all nodes.
Ensure this new secret is also accounted for if you are manually syncing the file across
nodes, or manually specifying secrets in `/etc/gitlab/gitlab.rb`.
@@ -247,7 +254,7 @@ the recommended procedure, see the
## Upgrading to GitLab 12.9
WARNING:
-GitLab 12.9.0 through GitLab 12.9.3 are affected by
+GitLab 12.9.0 through GitLab 12.9.3 are affected by
[a bug that stops repository verification](https://gitlab.com/gitlab-org/gitlab/-/issues/213523).
The issue is fixed in GitLab 12.9.4. Upgrade to GitLab 12.9.4 or later.
@@ -401,6 +408,6 @@ For the recommended procedure, see the
## Upgrading to GitLab 12.0
WARNING:
-This version is affected by a
+This version is affected by a
[bug that results in new LFS objects not being replicated to Geo secondary sites](https://gitlab.com/gitlab-org/gitlab/-/issues/32696).
The issue is fixed in GitLab 12.1. Be sure to upgrade to GitLab 12.1 or later.
diff --git a/doc/administration/geo/secondary_proxy/index.md b/doc/administration/geo/secondary_proxy/index.md
index 6c1812b2754..731b5012663 100644
--- a/doc/administration/geo/secondary_proxy/index.md
+++ b/doc/administration/geo/secondary_proxy/index.md
@@ -12,11 +12,6 @@ type: howto
> - [Disabled by default for different URLs](https://gitlab.com/gitlab-org/gitlab/-/issues/325732) in GitLab 14.6 [with a flag](../../feature_flags.md) named `geo_secondary_proxy_separate_urls`.
> - [Enabled by default for different URLs](https://gitlab.com/gitlab-org/gitlab/-/issues/346112) in GitLab 15.1.
-FLAG:
-On self-managed GitLab, this feature is only available by default for Geo sites using a unified URL. See below to
-[set up a unified URL for Geo sites](#set-up-a-unified-url-for-geo-sites).
-The feature is not ready for production use with separate URLs.
-
Use Geo proxying to:
- Have secondary sites serve read-write traffic by proxying to the primary site.
@@ -110,10 +105,13 @@ gitlab:
## Geo proxying with Separate URLs
-Since GitLab 15.1, Geo secondary proxying is enabled by default for separate URLs also.
+> Geo secondary proxying for separate URLs is [enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/346112) in GitLab 15.1.
+
+NOTE:
+The feature flag described in this section is planned to be deprecated and removed in a future release. Support for read-only Geo secondary sites is proposed in [issue 366810](https://gitlab.com/gitlab-org/gitlab/-/issues/366810), you can upvote and share your use cases in that issue.
-There are minor known issues linked in the
-["Geo secondary proxying with separate URLs" epic](https://gitlab.com/groups/gitlab-org/-/epics/6865).
+There are minor known issues linked in the
+["Geo secondary proxying with separate URLs" epic](https://gitlab.com/groups/gitlab-org/-/epics/6865).
You can also add feedback in the epic about any use-cases that
are not possible anymore with proxying enabled.
diff --git a/doc/administration/gitaly/configure_gitaly.md b/doc/administration/gitaly/configure_gitaly.md
index 5b868c274cd..ac03c3ffc02 100644
--- a/doc/administration/gitaly/configure_gitaly.md
+++ b/doc/administration/gitaly/configure_gitaly.md
@@ -1089,9 +1089,10 @@ lots of CI fetch traffic.
The pack-objects cache wraps `git pack-objects`, an internal part of
Git that gets invoked indirectly via the PostUploadPack and
-SSHUploadPack Gitaly RPCs. These are the RPCs that Gitaly runs when a
-user does a Git fetch via HTTP or SSH, respectively. When the cache is
-enabled, anything that uses PostUploadPack or SSHUploadPack can
+SSHUploadPack Gitaly RPCs. Gitaly runs PostUploadPack when a
+user does a Git fetch via HTTP, or SSHUploadPack when a
+user does a Git fetch via SSH.
+When the cache is enabled, anything that uses PostUploadPack or SSHUploadPack can
benefit from it. It is orthogonal to:
- The transport (HTTP or SSH).
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index b053da7ac9b..c7f7c4c58a5 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -72,7 +72,7 @@ the current status of these issues, please refer to the referenced issues and ep
|:--------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------|
| Gitaly Cluster + Geo - Issues retrying failed syncs | If Gitaly Cluster is used on a Geo secondary site, repositories that have failed to sync could continue to fail when Geo tries to resync them. Recovering from this state requires assistance from support to run manual steps. | No known solution prior to GitLab 15.0. In GitLab 15.0 to 15.2, enable the [`gitaly_praefect_generated_replica_paths` feature flag](#praefect-generated-replica-paths-gitlab-150-and-later). In GitLab 15.3, the feature flag is enabled by default. |
| Praefect unable to insert data into the database due to migrations not being applied after an upgrade | If the database is not kept up to date with completed migrations, then the Praefect node is unable to perform normal operation. | Make sure the Praefect database is up and running with all migrations completed (For example: `/opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-migrate-status` should show a list of all applied migrations). Consider [requesting upgrade assistance](https://about.gitlab.com/support/scheduling-upgrade-assistance/) so your upgrade plan can be reviewed by support. |
-| Restoring a Gitaly Cluster node from a snapshot in a running cluster | Because the Gitaly Cluster runs with consistent state, introducing a single node that is behind will result in the cluster not being able to reconcile the nodes data and other nodes data | Don't restore a single Gitaly Cluster node from a backup snapshot. If you must restore from backup, it's best to snapshot all Gitaly Cluster nodes at the same time and take a database dump of the Praefect database. |
+| Restoring a Gitaly Cluster node from a snapshot in a running cluster | Because the Gitaly Cluster runs with consistent state, introducing a single node that is behind will result in the cluster not being able to reconcile the nodes data and other nodes data | Don't restore a single Gitaly Cluster node from a backup snapshot. If you must restore from backup, it's best to [shut down GitLab](../read_only_gitlab.md#shut-down-the-gitlab-ui), snapshot all Gitaly Cluster nodes at the same time and take a database dump of the Praefect database. |
### Snapshot backup and recovery limitations
@@ -598,6 +598,7 @@ To migrate to Gitaly Cluster:
1. Create the required storage. Refer to
[repository storage recommendations](praefect.md#repository-storage-recommendations).
1. Create and configure [Gitaly Cluster](praefect.md).
+1. Configure the existing Gitaly instance [to use TPC](praefect.md#use-tcp-for-existing-gitlab-instances), if not already configured that way.
1. [Move the repositories](../operations/moving_repositories.md#move-repositories). To migrate to
Gitaly Cluster, existing repositories stored outside Gitaly Cluster must be moved. There is no
automatic migration but the moves can be scheduled with the GitLab API.
@@ -660,7 +661,14 @@ The code removed from GitLab during the Gitaly migration project affected these
performance workaround for these NFS-based deployments, we re-introduced some of the old Rugged
code. This re-introduced code is informally referred to as the "Rugged patches".
-### How it works
+### Automatic detection
+
+> Automatic detection for Rugged [disabled](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95445) in GitLab 15.3.
+
+FLAG:
+On self-managed GitLab, by default automatic detection of whether Rugged should be used (per storage) is not available.
+To make it available, ask an administrator to [disable the feature flag](../../administration/feature_flags.md) named
+`skip_rugged_auto_detect`.
The Ruby methods that perform direct Git access are behind
[feature flags](../../development/gitaly.md#legacy-rugged-code), disabled by default. It wasn't
@@ -685,12 +693,13 @@ To see if GitLab can access the repository file system directly, we use the foll
- GitLab Rails tries to read the metadata file directly. If it exists, and if the UUID's match,
assume we have direct access.
-Versions of GitLab 15.3 and later disable direct Git access by default.
+Direct Git access is:
-For versions of GitLab prior to 15.3, direct Git access is enabled by
-default in Omnibus GitLab because it fills in the correct repository
-paths in the GitLab configuration file `config/gitlab.yml`. This
-satisfies the UUID check.
+- [Disabled](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95445) by default in GitLab 15.3 and later for
+ compatibility with [Praefect-generated replica paths](#praefect-generated-replica-paths-gitlab-150-and-later). It
+ can be enabled if Rugged [feature flags](../../development/gitaly.md#legacy-rugged-code) are enabled.
+- Enabled by default in GitLab 15.2 and earlier because it fills in the correct repository paths in the GitLab
+ configuration file `config/gitlab.yml`. This satisfies the UUID check.
### Transition to Gitaly Cluster
diff --git a/doc/administration/gitaly/praefect.md b/doc/administration/gitaly/praefect.md
index 488e6a0df5f..bd03aa1bdbc 100644
--- a/doc/administration/gitaly/praefect.md
+++ b/doc/administration/gitaly/praefect.md
@@ -97,7 +97,7 @@ If you [installed](https://about.gitlab.com/install/) GitLab using the Omnibus G
### Preparation
-Before beginning, you should already have a working GitLab instance.
+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
@@ -164,7 +164,8 @@ The replication state is internal to each instance of GitLab and should
not be replicated.
These instructions help set up a single PostgreSQL database, which creates a single point of
-failure. Alternatively, [you can use PostgreSQL replication and failover](../postgresql/replication_and_failover.md).
+failure. To avoid this, you can configure your own clustered PostgreSQL. Support for PostgreSQL replication and failover using Omnibus GitLab is being tracked in
+[a relevant epic](https://gitlab.com/groups/gitlab-org/-/epics/7814).
The following options are available:
@@ -221,7 +222,7 @@ For using Omnibus-provided PgBouncer you need to take the following additional s
recommend using the PostgreSQL that is shipped with Omnibus as the backend. The following
instructions only work on Omnibus-provided PostgreSQL:
-1. For Omnibus-provided PgBouncer, you need to use the hash of `praefect` user instead the of the
+1. For Omnibus-provided PgBouncer, you need to use the hash of `praefect` password instead the of the
actual password:
```sql
@@ -331,7 +332,7 @@ To configure the additional connection, you must either:
#### Configure a new PgBouncer database with `pool_mode = session`
We recommend using PgBouncer with `session` pool mode. You can use the
-[bundled PgBouncer](../postgresql/pgbouncer.md) or use an external PgBouncer and
+[bundled PgBouncer](../postgresql/pgbouncer.md) or use an external PgBouncer and
[configure it manually](https://www.pgbouncer.org/config.html).
The following example uses the bundled PgBouncer and sets up two separate connection pools on PostgreSQL host,
@@ -620,7 +621,7 @@ Updates to example must be made at:
gitlab-ctl reconfigure
```
-1. To ensure that Praefect
+1. To ensure that Praefect
[has updated its Prometheus listen address](https://gitlab.com/gitlab-org/gitaly/-/issues/2734),
[restart Praefect](../restart_gitlab.md#omnibus-gitlab-restart):
@@ -884,7 +885,7 @@ For more information on Gitaly server configuration, see our
gitlab_shell['secret_token'] = 'GITLAB_SHELL_SECRET_TOKEN'
```
-1. Configure and `internal_api_url`, which is also needed for `git push` operations:
+1. Configure an `internal_api_url`, which is also needed for `git push` operations:
```ruby
# Configure the gitlab-shell API callback URL. Without this, `git push` will
@@ -928,7 +929,7 @@ For more information on Gitaly server configuration, see our
gitlab-ctl reconfigure
```
-1. To ensure that Gitaly
+1. To ensure that Gitaly
[has updated its Prometheus listen address](https://gitlab.com/gitlab-org/gitaly/-/issues/2734),
[restart Gitaly](../restart_gitlab.md#omnibus-gitlab-restart):
@@ -1114,7 +1115,7 @@ Particular attention should be shown to:
1. Check that the Praefect storage is configured to store new repositories:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Repository**.
1. Expand the **Repository storage** section.
diff --git a/doc/administration/gitaly/recovery.md b/doc/administration/gitaly/recovery.md
index bd4846a986d..4bbf25d7cdd 100644
--- a/doc/administration/gitaly/recovery.md
+++ b/doc/administration/gitaly/recovery.md
@@ -430,6 +430,44 @@ This command fails if:
- The repository is already being tracked by the Praefect database.
- The repository does not exist on disk.
+### Manually track large numbers of repositories
+
+> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/6319) in GitLab 15.4.
+
+The `track-repositories` Praefect sub-command adds large batches of on-disk repositories to the Praefect database for tracking. This can
+be useful when migrating an existing instance to new infrastructure and ingesting all existing repositories into a fresh Gitaly Cluster.
+
+```shell
+# Omnibus GitLab install
+sudo gitlab-ctl praefect track-repositories --input-path /path/to/input.json
+
+# Source install
+sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml track-repositories -input-path /path/to/input.json
+```
+
+The command validates that all entries:
+
+- Are formatted correctly and contain required fields.
+- Correspond to a valid Git repository on disk.
+- Are not currently tracked in the Praefect database.
+
+If any entry fails these checks, the command aborts prior to attempting to track a repository.
+
+- `input-path` is the path to a file containing a list of repositories formatted as newline-delimited JSON objects. Objects must contain the following keys:
+ - `relative_path`: corresponds with `repository` in [track-repositories](#manually-track-repositories).
+ - `authoritative-storage`: the storage Praefect is to treat as the primary.
+ - `virtual-storage`: the virtual storage the repository is located in.
+
+ For example:
+
+ ```json
+ {"relative_path":"@hashed/f5/ca/f5ca38f748a1d6eaf726b8a42fb575c3c71f1864a8143301782de13da2d9202b.git","authoritative_storage":"gitaly-1","virtual_storage":"default"}
+ {"relative_path":"@hashed/f8/9f/f89f8d0e735a91c5269ab08d72fa27670d000e7561698d6e664e7b603f5c4e40.git","authoritative_storage":"gitaly-2","virtual_storage":"default"}
+ ```
+
+- `-replicate-immediately`, causes the command to replicate the repository to its secondaries immediately.
+ Otherwise, replication jobs are scheduled for execution in the database and are picked up by a Praefect background process.
+
### List virtual storage details
> [Introduced](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/4609) in GitLab 15.1.
diff --git a/doc/administration/gitaly/troubleshooting.md b/doc/administration/gitaly/troubleshooting.md
index e308fa9da43..285ec3d2631 100644
--- a/doc/administration/gitaly/troubleshooting.md
+++ b/doc/administration/gitaly/troubleshooting.md
@@ -20,7 +20,7 @@ and our advice on [parsing the `gitaly/current` file](../logs/log_parsing.md#par
When using standalone Gitaly servers, you must make sure they are the same version
as GitLab to ensure full compatibility:
-1. On the top bar, select **Menu > Admin** on your GitLab instance.
+1. On the top bar, select **Main menu > Admin** on your GitLab instance.
1. On the left sidebar, select **Overview > Gitaly Servers**.
1. Confirm all Gitaly servers indicate that they are up to date.
diff --git a/doc/administration/housekeeping.md b/doc/administration/housekeeping.md
index 31b7ac9fd3e..cb0156f8e2d 100644
--- a/doc/administration/housekeeping.md
+++ b/doc/administration/housekeeping.md
@@ -27,7 +27,7 @@ GitLab automatically runs `git gc` and `git repack` on repositories after Git pu
You can change how often this happens or turn it off:
-1. On the top bar, select **Menu > Admin**.
+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](#housekeeping-options).
diff --git a/doc/administration/inactive_project_deletion.md b/doc/administration/inactive_project_deletion.md
index 224b52d420e..824a444fdd2 100644
--- a/doc/administration/inactive_project_deletion.md
+++ b/doc/administration/inactive_project_deletion.md
@@ -1,23 +1,21 @@
---
-stage: Manage
+stage: Govern
group: Compliance
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
---
# Inactive project deletion **(FREE SELF)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0 [with a flag](../administration/feature_flags.md) named `inactive_projects_deletion`. 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 `inactive_projects_deletion`.
-On GitLab.com, this feature is not available. This feature is not ready for production use.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0 [with a flag](../administration/feature_flags.md) named `inactive_projects_deletion`. Disabled by default.
+> - [Feature flag `inactive_projects_deletion`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96803) removed in GitLab 15.4.
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.
+For the default setting on GitLab.com, see the [GitLab.com settings page](../user/gitlab_com/index.md#inactive-project-deletion).
+
## Configure inactive project deletion
You can configure inactive projects deletion or turn it off using either:
@@ -62,7 +60,7 @@ You can use the [Application settings API](../api/settings.md#change-application
To configure inactive projects with the GitLab UI:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Repository maintenance**.
1. In the **Inactive project deletion** section, configure the necessary options.
diff --git a/doc/administration/index.md b/doc/administration/index.md
index 3f2ae3170ab..58284a74bf7 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -82,7 +82,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
#### Customizing GitLab appearance
-- [Header logo](../user/admin_area/appearance.md#navigation-bar): Change the logo on all pages and email headers.
+- [Header logo](../user/admin_area/appearance.md#top-bar): Change the logo on all pages and email headers.
- [Favicon](../user/admin_area/appearance.md#favicon): Change the default favicon to your own logo.
- [Branded login page](../user/admin_area/appearance.md#sign-in--sign-up-pages): Customize the login page with your own logo, title, and description.
- ["New Project" page](../user/admin_area/appearance.md#new-project-pages): Customize the text to be displayed on the page that opens whenever your users create a new project.
diff --git a/doc/administration/instance_limits.md b/doc/administration/instance_limits.md
index 31434a8ee6f..0cf2e3a1131 100644
--- a/doc/administration/instance_limits.md
+++ b/doc/administration/instance_limits.md
@@ -158,13 +158,19 @@ Set the limit to `0` to disable it.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80631) in GitLab 14.9.
-This setting limits global search requests.
+This setting limits global search requests as follows:
| Limit | Default (requests per minute) |
|-------------------------|-------------------------------|
| Authenticated user | 30 |
| Unauthenticated user | 10 |
+Depending on the number of enabled [scopes](../user/search/advanced_search.md#global-search-scopes), a global search request can consume two to seven requests per minute. You may want to disable one or more scopes to use fewer requests. Global search requests that exceed the search rate limit per minute return the following error:
+
+```plaintext
+This endpoint has been requested too many times. Try again later.
+```
+
### Pipeline creation rate limit
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/362475) in GitLab 15.0.
@@ -581,7 +587,7 @@ This limit is [enabled on GitLab.com](../user/gitlab_com/index.md#gitlab-cicd).
The total number of instance level CI/CD variables is limited at the instance level.
This limit is checked each time a new instance level variable is created. If a new variable
-would cause the total number of variables to exceed the limit, the new variable is created.
+would cause the total number of variables to exceed the limit, the new variable is not created.
On self-managed instances this limit is defined for the `default` plan. By default,
this limit is set to `25`.
@@ -648,7 +654,7 @@ installation, run the following in the [GitLab Rails console](operations/rails_c
Plan.default.actual_limits.update!(ci_max_artifact_size_junit: 10)
```
-### Number of files per GitLab Pages web-site
+### Number of files per GitLab Pages website
The total number of file entries (including directories and symlinks) is limited to `200,000` per
GitLab Pages website.
@@ -663,6 +669,14 @@ For example, to change the limit to `100`:
Plan.default.actual_limits.update!(pages_file_entries: 100)
```
+### Number of custom domains per GitLab Pages website
+
+The total number of custom domains per GitLab Pages website is limited to `150` for [GitLab SaaS](../subscriptions/gitlab_com/index.md).
+
+The default limit for [GitLab self-managed](../subscriptions/self_managed/index.md) is `0` (unlimited).
+To set a limit on your self-managed instance, use the
+[Admin Area](pages/index.md#set-maximum-number-of-gitlab-pages-custom-domains-for-a-project).
+
### Number of registered runners per scope
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/321368) in GitLab 13.12. Disabled by default.
@@ -736,7 +750,7 @@ You can change these limits in the [GitLab Rails console](operations/rails_conso
- To update the maximum YAML size, update `max_yaml_size_bytes` with the new value in megabytes:
```ruby
- ApplicationSetting.update!(max_yaml_size_bytes: 2.megabytes)
+ ApplicationSetting.update(max_yaml_size_bytes: 2.megabytes)
```
The `max_yaml_size_bytes` value is not directly tied to the size of the YAML file,
@@ -745,7 +759,7 @@ You can change these limits in the [GitLab Rails console](operations/rails_conso
- To update the maximum YAML depth, update `max_yaml_depth` with the new value in megabytes:
```ruby
- ApplicationSetting.update!(max_yaml_depth: 125)
+ ApplicationSetting.update(max_yaml_depth: 125)
```
### Limit dotenv variables
diff --git a/doc/administration/integration/kroki.md b/doc/administration/integration/kroki.md
index a6fcfe6c80f..fb4659175b0 100644
--- a/doc/administration/integration/kroki.md
+++ b/doc/administration/integration/kroki.md
@@ -56,7 +56,7 @@ read the [Kroki installation](https://docs.kroki.io/kroki/setup/install/#_images
You need to enable Kroki integration from Settings under Admin Area.
To do that, log in with an administrator account and follow these steps:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. Go to **Settings > General**.
1. Expand the **Kroki** section.
1. Select **Enable Kroki** checkbox.
diff --git a/doc/administration/integration/mailgun.md b/doc/administration/integration/mailgun.md
index c007116d213..37e81f220cf 100644
--- a/doc/administration/integration/mailgun.md
+++ b/doc/administration/integration/mailgun.md
@@ -43,7 +43,7 @@ After configuring your Mailgun domain for the webhook endpoints,
you're ready to enable the Mailgun integration:
1. Sign in to GitLab as an [Administrator](../../user/permissions.md) user.
-1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. On the top bar, select **Main menu >** **{admin}** **Admin**.
1. On the left sidebar, go to **Settings > General** and expand the **Mailgun** section.
1. Select the **Enable Mailgun** check box.
1. Enter the Mailgun HTTP webhook signing key as described in
diff --git a/doc/administration/integration/plantuml.md b/doc/administration/integration/plantuml.md
index 58c9e01a151..de790c7ce40 100644
--- a/doc/administration/integration/plantuml.md
+++ b/doc/administration/integration/plantuml.md
@@ -211,7 +211,7 @@ stop;
After configuring your local PlantUML server, you're ready to enable the PlantUML integration:
1. Sign in to GitLab as an [Administrator](../../user/permissions.md) user.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, go to **Settings > General** and expand the **PlantUML** section.
1. Select the **Enable PlantUML** checkbox.
1. Set the PlantUML instance as `https://gitlab.example.com/-/plantuml/`,
diff --git a/doc/administration/integration/terminal.md b/doc/administration/integration/terminal.md
index 88b320941de..41984bbe7a7 100644
--- a/doc/administration/integration/terminal.md
+++ b/doc/administration/integration/terminal.md
@@ -1,6 +1,6 @@
---
-stage: Ecosystem
-group: Integrations
+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/engineering/ux/technical-writing/#assignments
---
@@ -114,7 +114,7 @@ they receive a `Connection failed` message.
By default, terminal sessions do not expire. To limit the terminal session
lifetime in your GitLab instance:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. Select
[**Settings > Web terminal**](../../user/admin_area/settings/index.md#general).
1. Set a `max session time`.
diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md
index a4ab0e07020..14749a9c7f6 100644
--- a/doc/administration/job_artifacts.md
+++ b/doc/administration/job_artifacts.md
@@ -437,6 +437,16 @@ If you need to manually remove job artifacts associated with multiple jobs while
```ruby
builds_to_clear = builds_with_artifacts.where("finished_at < ?", 1.week.ago)
builds_to_clear.find_each do |build|
+ Ci::JobArtifacts::DeleteService.new(build).execute
+ build.update!(artifacts_expire_at: Time.now)
+ end
+ ```
+
+ In [GitLab 15.3 and earlier](https://gitlab.com/gitlab-org/gitlab/-/issues/372537), use the following instead:
+
+ ```ruby
+ builds_to_clear = builds_with_artifacts.where("finished_at < ?", 1.week.ago)
+ builds_to_clear.find_each do |build|
build.artifacts_expire_at = Time.now
build.erase_erasable_artifacts!
end
@@ -489,7 +499,7 @@ If you need to manually remove **all** job artifacts associated with multiple jo
print "Ci::Build ID #{build.id}... "
if build.erasable?
- build.erase(erased_by: admin_user)
+ Ci::BuildEraseService.new(build, admin_user).execute
puts "Erased"
else
puts "Skipped (Nothing to erase or not erasable)"
@@ -497,6 +507,9 @@ If you need to manually remove **all** job artifacts associated with multiple jo
end
```
+ In [GitLab 15.3 and earlier](https://gitlab.com/gitlab-org/gitlab/-/issues/369132), replace
+ `Ci::BuildEraseService.new(build, admin_user).execute` with `build.erase(erased_by: admin_user)`.
+
`1.week.ago` is a Rails `ActiveSupport::Duration` method which calculates a new
date or time in the past. Other valid examples are:
diff --git a/doc/administration/job_logs.md b/doc/administration/job_logs.md
index d2837bfa96e..84da4e31d92 100644
--- a/doc/administration/job_logs.md
+++ b/doc/administration/job_logs.md
@@ -134,8 +134,8 @@ For more information, see [delete references to missing artifacts](raketasks/che
## Incremental logging architecture
-> - [Deployed behind a feature flag](../user/feature_flags.md), disabled by default.
-> - Enabled on GitLab.com.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/18169) in GitLab 10.8 [with a flag](feature_flags.md) named `ci_enable_live_trace`. Disabled by default.
+> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/241471) in GitLab 13.6.
> - [Recommended for production use](https://gitlab.com/groups/gitlab-org/-/epics/4275) in GitLab 13.6.
> - [Recommended for production use with AWS S3](https://gitlab.com/gitlab-org/gitlab/-/issues/273498) in GitLab 13.7.
> - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-incremental-logging).
diff --git a/doc/administration/load_balancer.md b/doc/administration/load_balancer.md
index 988bddcfe62..87b63c6272d 100644
--- a/doc/administration/load_balancer.md
+++ b/doc/administration/load_balancer.md
@@ -31,7 +31,7 @@ Configure your load balancers to pass connections on port 443 as 'TCP' rather
than 'HTTP(S)' protocol. This passes the connection to the application nodes
NGINX service untouched. NGINX has the SSL certificate and listen on port 443.
-See [NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
+See the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
for details on managing SSL certificates and configuring NGINX.
### Load Balancers terminate SSL without backend SSL
@@ -41,8 +41,8 @@ The load balancers is be responsible for managing SSL certificates and
terminating SSL.
Because communication between the load balancers and GitLab isn't secure,
-there is some additional configuration needed. See
-[NGINX Proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#supporting-proxied-ssl)
+there is some additional configuration needed. See the
+[proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-a-reverse-proxy-or-load-balancer-ssl-termination)
for details.
### Load Balancers terminate SSL with backend SSL
@@ -55,7 +55,7 @@ Traffic is secure between the load balancers and NGINX in this
scenario. There is no need to add configuration for proxied SSL because the
connection is secure all the way. However, configuration must be
added to GitLab to configure SSL certificates. See
-[NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
+the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
for details on managing SSL certificates and configuring NGINX.
## Ports
@@ -115,14 +115,18 @@ Configure DNS for an alternate SSH hostname such as `altssh.gitlab.example.com`.
It is strongly recommend that multi-node deployments configure load balancers to use the [readiness check](../user/admin_area/monitoring/health_check.md#readiness) to ensure a node is ready to accept traffic, before routing traffic to it. This is especially important when utilizing Puma, as there is a brief period during a restart where Puma doesn't accept requests.
-<!-- ## Troubleshooting
+## Troubleshooting
-Include any troubleshooting steps that you can foresee. If you know beforehand what issues
-one might have when setting this up, or when something is changed, or on upgrading, it's
-important to describe those, too. Think of things that may go wrong and include them here.
-This is important to minimize requests for support, and to avoid doc comments with
-questions that you know someone might ask.
+### The health check is returning a `408` HTTP code via the load balancer
-Each scenario can be a third-level heading, e.g. `### 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. -->
+If you are using [AWS's Classic Load Balancer](https://docs.aws.amazon.com/en_en/elasticloadbalancing/latest/classic/elb-ssl-security-policy.html#ssl-ciphers)
+in GitLab 15.0 or later, you must to enable the `AES256-GCM-SHA384` cipher in NGINX.
+See [AES256-GCM-SHA384 SSL cipher no longer allowed by default by NGINX](https://docs.gitlab.com/omnibus/update/gitlab_15_changes.html#aes256-gcm-sha384-ssl-cipher-no-longer-allowed-by-default-by-nginx)
+for more information.
+
+The default ciphers for a GitLab version can be
+viewed in the [`files/gitlab-cookbooks/gitlab/attributes/default.rb`](https://gitlab.com/gitlab-org/omnibus-gitlab/-/blob/master/files/gitlab-cookbooks/gitlab/attributes/default.rb)
+file and selecting the Git tag that correlates with your target GitLab version
+(for example `15.0.5+ee.0`). If required by your load balancer, you can then define
+[custom SSL ciphers](https://docs.gitlab.com/omnibus/settings/ssl.html#use-custom-ssl-ciphers)
+for NGINX.
diff --git a/doc/administration/maintenance_mode/index.md b/doc/administration/maintenance_mode/index.md
index 00e28e650ea..60de8e2fd3a 100644
--- a/doc/administration/maintenance_mode/index.md
+++ b/doc/administration/maintenance_mode/index.md
@@ -21,7 +21,7 @@ Maintenance Mode allows most external actions that do not change internal state.
There are three ways to enable Maintenance Mode as an administrator:
- **Web UI**:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand **Maintenance Mode**, and toggle **Enable Maintenance Mode**.
You can optionally add a message for the banner as well.
@@ -45,7 +45,7 @@ There are three ways to enable Maintenance Mode as an administrator:
There are three ways to disable Maintenance Mode:
- **Web UI**:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand **Maintenance Mode**, and toggle **Enable Maintenance Mode**.
You can optionally add a message for the banner as well.
@@ -173,7 +173,7 @@ it is recommended that you disable all cron jobs except for those related to Geo
To monitor queues and disable jobs:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
### Incident management
diff --git a/doc/administration/monitoring/gitlab_self_monitoring_project/index.md b/doc/administration/monitoring/gitlab_self_monitoring_project/index.md
index 2553638291d..4b62a8ae931 100644
--- a/doc/administration/monitoring/gitlab_self_monitoring_project/index.md
+++ b/doc/administration/monitoring/gitlab_self_monitoring_project/index.md
@@ -38,12 +38,12 @@ This project can be used to:
## Create the self monitoring project
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling** and expand **Self monitoring**.
1. Toggle **Self monitoring** on.
1. After your GitLab instance creates the project, GitLab displays a link to the
project in the text above the **Self monitoring** toggle. You can also find it
- from the top bar by selecting **Menu > Project**, then selecting **Your projects**.
+ from the top bar by selecting **Main menu > Projects**.
## Delete the self monitoring project
@@ -51,7 +51,7 @@ WARNING:
Deleting the self monitoring project removes any changes made to the project. If
you create the project again, it's created in its default state.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, go to **Settings > Metrics and profiling** and expand **Self monitoring**.
1. Toggle **Self monitoring** off.
1. In the confirmation dialog that opens, select **Delete self monitoring project**.
diff --git a/doc/administration/monitoring/performance/gitlab_configuration.md b/doc/administration/monitoring/performance/gitlab_configuration.md
index 128ddad6555..74db35eebc2 100644
--- a/doc/administration/monitoring/performance/gitlab_configuration.md
+++ b/doc/administration/monitoring/performance/gitlab_configuration.md
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
GitLab Performance Monitoring is disabled by default. To enable it and change any of its
settings:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**
(`/admin/application_settings/metrics_and_profiling`).
1. Add the necessary configuration changes.
diff --git a/doc/administration/monitoring/performance/grafana_configuration.md b/doc/administration/monitoring/performance/grafana_configuration.md
index 3a76e2e4578..6e9ea0d8d42 100644
--- a/doc/administration/monitoring/performance/grafana_configuration.md
+++ b/doc/administration/monitoring/performance/grafana_configuration.md
@@ -62,7 +62,7 @@ repository.
After setting up Grafana, you can enable a link to access it easily from the
GitLab sidebar:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**
and expand **Metrics - Grafana**.
1. Select the **Add a link to Grafana** checkbox.
@@ -72,14 +72,14 @@ GitLab sidebar:
- *Otherwise,* enter the full URL of the Grafana instance.
1. Select **Save changes**.
-GitLab displays your link in the **Menu > Admin > Monitoring > Metrics Dashboard**.
+GitLab displays your link in the **Main menu > Admin > Monitoring > Metrics Dashboard**.
## Required Scopes
> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5822) in GitLab 13.10.
When setting up Grafana through the process above, no scope shows in the screen at
-**Menu > Admin > Applications > GitLab Grafana**. However, the `read_user` scope is
+**Main menu > Admin > Applications > GitLab Grafana**. However, the `read_user` scope is
required and is provided to the application automatically. Setting any scope other than
`read_user` without also including `read_user` leads to this error when you try to log in using
GitLab as the OAuth provider:
diff --git a/doc/administration/monitoring/performance/performance_bar.md b/doc/administration/monitoring/performance/performance_bar.md
index 759f485c109..c23046158e1 100644
--- a/doc/administration/monitoring/performance/performance_bar.md
+++ b/doc/administration/monitoring/performance/performance_bar.md
@@ -108,7 +108,7 @@ The performance bar is disabled by default for non-administrators. To enable it
for a given group:
1. Sign in as a user with administrator access.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**
(`admin/application_settings/metrics_and_profiling`), and expand
**Profiling - Performance bar**.
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index a2def8a9f64..00dae8e4dd5 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
To enable the GitLab Prometheus metrics:
1. Log in to GitLab as a user with administrator access.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**.
1. Find the **Metrics - Prometheus** section, and select **Add link to Prometheus**.
1. [Restart GitLab](../../restart_gitlab.md#omnibus-gitlab-restart) for the changes to take effect.
@@ -146,6 +146,8 @@ The following metrics are available:
| `gitlab_snowplow_failed_events_total` | Counter | 14.1 | Total number of GitLab Snowplow product intelligence events emission failures | |
| `gitlab_snowplow_successful_events_total` | Counter | 14.1 | Total number of GitLab Snowplow product intelligence events emission successes | |
| `gitlab_ci_build_trace_errors_total` | Counter | 14.4 | Total amount of different error types on a build trace | `error_reason` |
+| `gitlab_presentable_object_cacheless_render_real_duration_seconds` | Histogram | 15.3 | Duration of real time spent caching and representing specific web request objects | `controller`, `action` |
+| `cached_object_operations_total` | Counter | 15.3 | Total number of objects cached for specific web requests | `controller`, `action` |
## Metrics controlled by a feature flag
@@ -390,7 +392,7 @@ Some basic Ruby runtime metrics are available:
## Redis metrics
These client metrics are meant to complement Redis server metrics.
-These metrics are broken down per
+These metrics are broken down per
[Redis instance](https://docs.gitlab.com/omnibus/settings/redis.html#running-with-multiple-redis-instances).
These metrics all have a `storage` label which indicates the Redis
instance (`cache`, `shared_state`, and so on).
diff --git a/doc/administration/monitoring/prometheus/index.md b/doc/administration/monitoring/prometheus/index.md
index 6f6ac5c5d4b..c4aa607fa4d 100644
--- a/doc/administration/monitoring/prometheus/index.md
+++ b/doc/administration/monitoring/prometheus/index.md
@@ -277,11 +277,6 @@ To use an external Prometheus server:
static_configs:
- targets:
- 1.1.1.1:9168
- - job_name: gitlab_exporter_process
- metrics_path: "/process"
- static_configs:
- - targets:
- - 1.1.1.1:9168
- job_name: gitaly
static_configs:
- targets:
diff --git a/doc/administration/nfs.md b/doc/administration/nfs.md
index 29f00fec3f7..9967cce5b73 100644
--- a/doc/administration/nfs.md
+++ b/doc/administration/nfs.md
@@ -95,34 +95,22 @@ We are investigating the use of
## Improving NFS performance with GitLab
NFS performance with GitLab can in some cases be improved with
-[direct Git access](gitaly/index.md#direct-access-to-git-in-gitlab) using
-[Rugged](https://github.com/libgit2/rugged).
+[direct Git access](gitaly/index.md#direct-access-to-git-in-gitlab) using [Rugged](https://github.com/libgit2/rugged).
-Versions of GitLab after 12.2 and prior to 15.3 automatically detect if
-Rugged can and should be used per storage.
+Depending on the GitLab version, GitLab [automatically detects](gitaly/index.md#automatic-detection) if Rugged can and should
+be used per storage.
-NOTE:
-GitLab 15.3 and later disables this automatic detection. Auto-detection can be enabled via the
-`skip_rugged_auto_detect` feature flag:
-
-```ruby
-Feature.disable(:skip_rugged_auto_detect)
-```
-
-In addition, if you previously enabled Rugged using the feature flag and
-you want to use automatic detection instead, you must unset the feature
-flag:
+If the Rugged feature flag is explicitly set to either `true` or `false`, GitLab uses the value explicitly set. If you
+previously enabled Rugged using the feature flag and you want to use automatic detection instead, you must unset
+the feature flag:
```shell
sudo gitlab-rake gitlab:features:unset_rugged
```
-If the Rugged feature flag is explicitly set to either `true` or `false`, GitLab uses the value explicitly set.
-
-From GitLab 12.7, Rugged is only automatically enabled for use with Puma
-if the [Puma thread count is set to `1`](../install/requirements.md#puma-settings).
-
-To use Rugged with a Puma thread count of more than `1`, enable Rugged using the [feature flag](../development/gitaly.md#legacy-rugged-code).
+From GitLab 12.7, Rugged is only automatically enabled for use with Puma if the
+[Puma thread count is set to `1`](../install/requirements.md#puma-settings). To use Rugged with a Puma thread count of
+more than `1`, enable Rugged using the [feature flag](../development/gitaly.md#legacy-rugged-code).
## NFS server
diff --git a/doc/administration/object_storage.md b/doc/administration/object_storage.md
index 0299d5f8b0c..fd9ab9b5972 100644
--- a/doc/administration/object_storage.md
+++ b/doc/administration/object_storage.md
@@ -26,7 +26,7 @@ GitLab has been tested by vendors and customers on a number of object storage pr
### Known compatibility issues
-- Dell EMC ECS: Prior to GitLab 13.3, there is a
+- Dell EMC ECS: Prior to GitLab 13.3, there is a
[known bug in GitLab Workhorse that prevents HTTP Range Requests from working with CI job artifacts](https://gitlab.com/gitlab-org/gitlab/-/issues/223806).
Be sure to upgrade to GitLab 13.3.0 or above if you use S3 storage with this hardware.
@@ -528,7 +528,7 @@ supported by consolidated configuration form, refer to the following guides:
| Object storage type | Supported by consolidated configuration? |
|---------------------|------------------------------------------|
-| [Backups](../raketasks/backup_gitlab.md#uploading-backups-to-a-remote-cloud-storage) | **{dotted-circle}** No |
+| [Backups](../raketasks/backup_gitlab.md#upload-backups-to-a-remote-cloud-storage) | **{dotted-circle}** No |
| [Job artifacts](job_artifacts.md#using-object-storage) including archived job logs | **{check-circle}** Yes |
| [LFS objects](lfs/index.md#storing-lfs-objects-in-remote-object-storage) | **{check-circle}** Yes |
| [Uploads](uploads.md#using-object-storage) | **{check-circle}** Yes |
@@ -578,7 +578,7 @@ real bucket into multiple virtual buckets. If your object storage
bucket is called `my-gitlab-objects` you can configure uploads to go
into `my-gitlab-objects/uploads`, artifacts into
`my-gitlab-objects/artifacts`, etc. The application will act as if
-these are separate buckets. Note that use of bucket prefixes
+these are separate buckets. Note that use of bucket prefixes
[may not work correctly with Helm backups](https://gitlab.com/gitlab-org/charts/gitlab/-/issues/3376).
Helm-based installs require separate buckets to
@@ -693,7 +693,7 @@ configuration.
When configured either with an instance profile or with the consolidated
object configuration, GitLab Workhorse properly uploads files to S3
buckets that have [SSE-S3 or SSE-KMS encryption enabled by default](https://docs.aws.amazon.com/kms/latest/developerguide/services-s3.html).
-Customer master keys (CMKs) and SSE-C encryption are
+Customer master keys (CMKs) and SSE-C encryption are
[not supported since this requires sending the encryption keys in every request](https://gitlab.com/gitlab-org/gitlab/-/issues/226006).
##### Server-side encryption headers
@@ -701,7 +701,7 @@ Customer master keys (CMKs) and SSE-C encryption are
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38240) in GitLab 13.3.
Setting a default encryption on an S3 bucket is the easiest way to
-enable encryption, but you may want to
+enable encryption, but you may want to
[set a bucket policy to ensure only encrypted objects are uploaded](https://aws.amazon.com/premiumsupport/knowledge-center/s3-bucket-store-kms-encrypted-objects/).
To do this, you must configure GitLab to send the proper encryption headers
in the `storage_options` configuration section:
diff --git a/doc/administration/operations/fast_ssh_key_lookup.md b/doc/administration/operations/fast_ssh_key_lookup.md
index a3240a6041b..8523b881730 100644
--- a/doc/administration/operations/fast_ssh_key_lookup.md
+++ b/doc/administration/operations/fast_ssh_key_lookup.md
@@ -106,7 +106,7 @@ users as long as a large file exists.
To disable writes to the `authorized_keys` file:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**.
1. Expand **Performance optimization**.
1. Clear the **Use authorized_keys file to authenticate SSH keys** checkbox.
@@ -125,7 +125,7 @@ This overview is brief. Refer to the above instructions for more context.
1. [Rebuild the `authorized_keys` file](../raketasks/maintenance.md#rebuild-authorized_keys-file).
1. Enable writes to the `authorized_keys` file.
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**.
1. Expand **Performance optimization**.
1. Select the **Use authorized_keys file to authenticate SSH keys** checkbox.
diff --git a/doc/administration/operations/index.md b/doc/administration/operations/index.md
index a6e66abdbdb..179958c6df1 100644
--- a/doc/administration/operations/index.md
+++ b/doc/administration/operations/index.md
@@ -18,7 +18,7 @@ Keep your GitLab instance up and running smoothly.
- [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)**
- [Puma](puma.md): Understand Puma and puma-worker-killer.
-- Speed up SSH operations by
+- Speed up SSH operations by
[Authorizing SSH users via a fast, indexed lookup to the GitLab database](fast_ssh_key_lookup.md), and/or
by [doing away with user SSH keys stored on GitLab entirely in favor of SSH certificates](ssh_certificates.md).
- [File System Performance Benchmarking](filesystem_benchmarking.md): File system
diff --git a/doc/administration/operations/puma.md b/doc/administration/operations/puma.md
index 5ce469d3e63..8e7594dfc2d 100644
--- a/doc/administration/operations/puma.md
+++ b/doc/administration/operations/puma.md
@@ -156,10 +156,8 @@ For deployments where NFS is used to store Git repositories, GitLab uses
[direct Git access](../gitaly/index.md#direct-access-to-git-in-gitlab) to improve performance by using
[Rugged](https://github.com/libgit2/rugged).
-Rugged usage is automatically enabled if direct Git access
-[is available](../gitaly/index.md#how-it-works)
-and Puma is running single threaded, unless it is disabled by a
-[feature flag](../../development/gitaly.md#legacy-rugged-code).
+Rugged usage is automatically enabled if direct Git access [is available](../gitaly/index.md#automatic-detection) and
+Puma is running single threaded, unless it is disabled by a [feature flag](../../development/gitaly.md#legacy-rugged-code).
MRI Ruby uses a Global VM Lock (GVL). GVL allows MRI Ruby to be multi-threaded, but running at
most on a single core.
diff --git a/doc/administration/operations/rails_console.md b/doc/administration/operations/rails_console.md
index 430dfbc637c..627dfbeb66c 100644
--- a/doc/administration/operations/rails_console.md
+++ b/doc/administration/operations/rails_console.md
@@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Rails console **(FREE SELF)**
-At the heart of GitLab is a web application
+At the heart of GitLab is a web application
[built using the Ruby on Rails framework](https://about.gitlab.com/blog/2018/10/29/why-we-use-rails-to-build-gitlab/).
The [Rails console](https://guides.rubyonrails.org/command_line.html#rails-console).
provides a way to interact with your GitLab instance from the command line, and also grants access to the amazing tools built right into Rails.
@@ -19,7 +19,7 @@ with no consequences, you are strongly advised to do so in a test environment.
The Rails console is for GitLab system administrators who are troubleshooting
a problem or need to retrieve some data that can only be done through direct
-access of the GitLab application. Basic knowledge of Ruby is needed (try
+access of the GitLab application. Basic knowledge of Ruby is needed (try
[this 30-minute tutorial](https://try.ruby-lang.org/) for a quick introduction).
Rails experience is useful but not required.
@@ -136,7 +136,7 @@ root
1
```
-Some basic knowledge of Ruby will be very useful. Try
+Some basic knowledge of Ruby will be very useful. Try
[this 30-minute tutorial](https://try.ruby-lang.org/) for a quick introduction.
Rails experience is helpful but not essential.
@@ -202,6 +202,44 @@ sudo chmod 700 /scripts
sudo gitlab-rails runner /scripts/helloworld.rb
```
+## Find specific methods for an object
+
+```ruby
+Array.methods.select { |m| m.to_s.include? "sing" }
+Array.methods.grep(/sing/)
+```
+
+## Find method source
+
+```ruby
+instance_of_object.method(:foo).source_location
+
+# Example for when we would call project.private?
+project.method(:private?).source_location
+```
+
+## Limiting output
+
+Adding a semicolon(`;`) and a follow-up statement at the end of a statement prevents the default implicit return output. This can be used if you are already explicitly printing details and potentially have a lot of return output:
+
+```ruby
+puts ActiveRecord::Base.descendants; :ok
+Project.select(&:pages_deployed?).each {|p| puts p.pages_url }; true
+```
+
+## Get or store the result of last operation
+
+Underscore(`_`) represents the implicit return of the previous statement. You can use this to quickly assign a variable from the output of the previous command:
+
+```ruby
+Project.last
+# => #<Project id:2537 root/discard>>
+project = _
+# => #<Project id:2537 root/discard>>
+project.id
+# => 2537
+```
+
## Active Record objects
### Looking up database-persisted objects
@@ -331,6 +369,15 @@ D, [2020-03-05T17:18:30.406047 #910] DEBUG -- : User Load (2.6ms) SELECT "use
For more on different ways to retrieve data from the database using Active
Record, please see the [Active Record Query Interface documentation](https://guides.rubyonrails.org/active_record_querying.html).
+## Query the database using an Active Record model
+
+```ruby
+m = Model.where('attribute like ?', 'ex%')
+
+# for example to query the projects
+projects = Project.where('path like ?', 'Oumua%')
+```
+
### Modifying Active Record objects
In the previous section, we learned about retrieving database records using
diff --git a/doc/administration/operations/ssh_certificates.md b/doc/administration/operations/ssh_certificates.md
index 1e405189342..8069dad4d8d 100644
--- a/doc/administration/operations/ssh_certificates.md
+++ b/doc/administration/operations/ssh_certificates.md
@@ -159,7 +159,7 @@ users (especially if they're renewed) than you have deploy keys.
Users can still bypass SSH certificate authentication by manually
uploading an SSH public key to their profile, relying on the
`~/.ssh/authorized_keys` fallback to authenticate it. There's
-currently no feature to prevent this,
+currently no feature to prevent this,
[but there's an open request for adding it](https://gitlab.com/gitlab-org/gitlab/-/issues/23260).
Such a restriction can currently be hacked in by, for example, providing a
diff --git a/doc/administration/package_information/supported_os.md b/doc/administration/package_information/supported_os.md
index e4d7ea72cdc..5ccabd66ed0 100644
--- a/doc/administration/package_information/supported_os.md
+++ b/doc/administration/package_information/supported_os.md
@@ -24,6 +24,7 @@ The following lists the currently supported OSs and their possible EOL dates.
| OpenSUSE 15.3 | GitLab CE / GitLab EE 14.5.0 | x86_64, aarch64 | [OpenSUSE Install Documentation](https://about.gitlab.com/install/#opensuse-leap-15-3) | Nov 2022 | <https://en.opensuse.org/Lifetime> |
| RHEL 8 | GitLab CE / GitLab EE 12.8.1 | x86_64, arm64 | [Use CentOS Install Documentation](https://about.gitlab.com/install/#centos-7) | May 2029 | [RHEL Details](https://access.redhat.com/support/policy/updates/errata/#Life_Cycle_Dates) |
| SLES 12 | GitLab EE 9.0.0 | x86_64 | [Use OpenSUSE Install Documentation](https://about.gitlab.com/install/#opensuse-leap-15-3) | Oct 2027 | <https://www.suse.com/lifecycle/> |
+| SLES 15 | GitLab EE 14.8.0 | x86_64 | [Use OpenSUSE Install Documentation](https://about.gitlab.com/install/#opensuse-leap-15-3) | Dec 2024 | <https://www.suse.com/lifecycle/> |
| Oracle Linux | GitLab CE / GitLab EE 8.14.0 | x86_64 | [Use CentOS Install Documentation](https://about.gitlab.com/install/#centos-7) | Jul 2024 | <https://www.oracle.com/a/ocom/docs/elsp-lifetime-069338.pdf> |
| Scientific Linux | GitLab CE / GitLab EE 8.14.0 | x86_64 | [Use CentOS Install Documentation](https://about.gitlab.com/install/#centos-7) | June 2024 | <https://scientificlinux.org/downloads/sl-versions/sl7/> |
| Ubuntu 18.04 | GitLab CE / GitLab EE 10.7.0 | amd64 | [Ubuntu Install Documentation](https://about.gitlab.com/install/#ubuntu) | April 2023 | <https://wiki.ubuntu.com/Releases> |
diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md
index 8b115ca1af4..537840ce785 100644
--- a/doc/administration/packages/container_registry.md
+++ b/doc/administration/packages/container_registry.md
@@ -23,7 +23,7 @@ may or may not be available by default.
The Container Registry is automatically enabled and available on your GitLab domain, port 5050 if:
-- You're using the built-in [Let's Encrypt integration](https://docs.gitlab.com/omnibus/settings/ssl.html#lets-encrypt-integration), and
+- You're using the built-in [Let's Encrypt integration](https://docs.gitlab.com/omnibus/settings/ssl.html#enable-the-lets-encrypt-integration), and
- You're using GitLab 12.5 or later.
Otherwise, the Container Registry is not enabled. To enable it:
@@ -199,7 +199,7 @@ a wildcard certificate if hosted under a subdomain of your existing GitLab
domain, for example, `registry.gitlab.example.com`.
As well as manually generated SSL certificates (explained here), certificates automatically
-generated by Let's Encrypt are also [supported in Omnibus installs](https://docs.gitlab.com/omnibus/settings/ssl.html#host-services).
+generated by Let's Encrypt are also [supported in Omnibus installs](https://docs.gitlab.com/omnibus/settings/ssl.html).
Let's assume that you want the container Registry to be accessible at
`https://registry.gitlab.example.com`.
@@ -322,7 +322,7 @@ the Container Registry by themselves, follow the steps below.
In GitLab, tokens for the Container Registry expire every five minutes.
To increase the token duration:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Container Registry**.
1. For the **Authorization token duration (minutes)**, update the value.
@@ -906,7 +906,7 @@ project.container_repositories.find_each do |repo|
puts repo.attributes
# Start the tag cleanup
- puts Projects::ContainerRepository::CleanupTagsService.new(repo, user, policy.attributes.except("created_at", "updated_at")).execute()
+ puts Projects::ContainerRepository::CleanupTagsService.new(container_repository: repo, current_user: user, params: policy.attributes.except("created_at", "updated_at")).execute
end
```
@@ -1202,7 +1202,7 @@ Before diving in to the following sections, here's some basic troubleshooting:
been synchronized (for example, via NTP).
1. If you are using an S3-backed Registry, double check that the IAM
- permissions and the S3 credentials (including region) are correct. See
+ permissions and the S3 credentials (including region) are correct. See
[the sample IAM policy](https://docs.docker.com/registry/storage-drivers/s3/)
for more details.
@@ -1631,7 +1631,7 @@ wrong. However, since all communications between Docker clients and servers
are done over HTTPS, it's a bit difficult to decrypt the traffic quickly even
if you know the private key. What can we do instead?
-One way would be to disable HTTPS by setting up an
+One way would be to disable HTTPS by setting up an
[insecure Registry](https://docs.docker.com/registry/insecure/). This could introduce a
security hole and is only recommended for local testing. If you have a
production system and can't or don't want to do this, there is another way:
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index e545fb3cd1b..024fb12a51f 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -156,7 +156,7 @@ Watch the [video tutorial](https://youtu.be/dD8c7WNcc6s) for this configuration.
**Requirements:**
- [Wildcard DNS setup](#dns-configuration)
-- Wildcard TLS certificate
+- TLS certificate. Can be either Wildcard, or any other type meeting the [requirements](../../user/project/pages/custom_domains_ssl_tls_certification/index.md#manual-addition-of-ssltls-certificates).
---
@@ -191,6 +191,12 @@ to use the HTTPS protocol.
WARNING:
Multiple wildcards for one instance is not supported. Only one wildcard per instance can be assigned.
+WARNING:
+GitLab Pages does not update the OAuth application if changes are made to the redirect URI.
+Before you reconfigure, remove the `gitlab_pages` section from `/etc/gitlab/gitlab-secrets.json`,
+then run `gitlab-ctl reconfigure`. For more information, read
+[GitLab Pages does not regenerate OAuth](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/3947).
+
### Wildcard domains with TLS-terminating Load Balancer
**Requirements:**
@@ -368,7 +374,7 @@ world. Custom domains and TLS are supported.
If you don't have IPv6, you can omit the IPv6 address.
-1. If you haven't named your certificate and key `example.io.crt` and `example.io.key` respectively,
+1. If you haven't named your certificate `example.io.crt` and your key `example.io.key`,
then you need to also add the full paths as shown below:
```ruby
@@ -397,7 +403,7 @@ domain as a custom domain to their project.
If your user base is private or otherwise trusted, you can disable the
verification requirement:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Pages**.
1. Clear the **Require users to prove ownership of custom domains** checkbox.
@@ -414,7 +420,7 @@ sites served under a custom domain.
To enable it:
1. Choose an email address on which you want to receive notifications about expiring domains.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Pages**.
1. Enter the email address for receiving notifications and accept Let's Encrypt's Terms of Service.
@@ -467,7 +473,7 @@ pre-existing applications must modify the GitLab Pages OAuth application. Follow
this:
1. Enable [access control](#access-control).
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Applications**.
1. Expand **GitLab Pages**.
1. Clear the `api` scope's checkbox and select the desired scope's checkbox (for example,
@@ -486,7 +492,7 @@ This can be helpful to restrict information published with Pages websites to the
of your instance only.
To do that:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Pages**.
1. Select the **Disable public access to Pages sites** checkbox.
@@ -523,7 +529,7 @@ For Omnibus, this is fixed by [installing a custom CA in Omnibus GitLab](https:/
> [Introduced](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/548) in GitLab 14.8.
-If GitLab has been [configured to require mutual TLS](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-2-way-ssl-client-authentication), you need to add the client certificates to Pages:
+If GitLab has been [configured to require mutual TLS](https://docs.gitlab.com/omnibus/settings/ssl.html#enable-2-way-ssl-client-authentication), you need to add the client certificates to Pages:
1. Configure in `/etc/gitlab/gitlab.rb`:
@@ -699,7 +705,7 @@ Prerequisites:
To set the global maximum pages size for a project:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Pages**.
1. Enter a value under **Maximum size of pages**.
@@ -713,7 +719,7 @@ Prerequisites:
To set the maximum size of each GitLab Pages site in a group, overriding the inherited setting:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Pages**.
1. Enter a value under **Maximum size** in MB.
@@ -727,11 +733,24 @@ Prerequisites:
To set the maximum size of GitLab Pages site in a project, overriding the inherited setting:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Pages**.
1. Enter a value under **Maximum size of pages** in MB.
1. Select **Save changes**.
+## Set maximum number of GitLab Pages custom domains for a project
+
+Prerequisite:
+
+- You must be an administrator of a self-managed GitLab instance.
+
+To set the maximum number of GitLab Pages custom domains for a project:
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Settings > Preferences**, and expand **Pages**.
+1. Enter a value for **Maximum number of custom domains per project**. Use `0` for unlimited domains.
+1. Select **Save changes**.
+
## Running GitLab Pages on a separate server
You can run the GitLab Pages daemon on a separate server to decrease the load on
@@ -933,7 +952,7 @@ The following settings are:
| `connection` | Various connection options described below. | |
NOTE:
-If you want to stop using and disconnect the NFS server, you need to
+If you want to stop using and disconnect the NFS server, you need to
[explicitly disable local storage](#disable-pages-local-storage), and it's only possible after upgrading to GitLab 13.11.
#### S3-compatible connection settings
@@ -1379,7 +1398,7 @@ Upgrading to an [officially supported operating system](https://about.gitlab.com
This problem comes from the permissions of the GitLab Pages OAuth application. To fix it:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Applications > GitLab Pages**.
1. Edit the application.
1. Under **Scopes**, ensure that the `api` scope is selected.
@@ -1502,3 +1521,12 @@ To fix that:
- Store your deployments locally, by commenting out that line.
1. Save the changes you made to your `gitlab.rb` file, then [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
+
+### 404 error `The page you're looking for could not be found`
+
+If you get a `404 Page Not Found` response from GitLab Pages:
+
+1. Check `.gitlab-ci.yml` contains the job `pages:`.
+1. Check the current project's pipeline to confirm the job `pages:deploy` is being run.
+
+Without the `pages:deploy` job, the updates to your GitLab Pages site are never published.
diff --git a/doc/administration/pages/source.md b/doc/administration/pages/source.md
index 5ab508dbaca..14ac05e0293 100644
--- a/doc/administration/pages/source.md
+++ b/doc/administration/pages/source.md
@@ -364,9 +364,8 @@ world. Custom domains and TLS are supported.
```
1. Edit `/etc/default/gitlab` and set `gitlab_pages_enabled` to `true` in
- order to enable the pages daemon. In `gitlab_pages_options` the
- `-pages-domain`, `-listen-http` and `-listen-https` must match the `host`,
- `external_http` and `external_https` settings that you set above respectively.
+ order to enable the pages daemon. In `gitlab_pages_options`, you must match the
+ `-pages-domain` with `host`, `-listen-http` with `external_http`, and `-listen-https` with `external_https` settings.
The `-root-cert` and `-root-key` settings are the wildcard TLS certificates
of the `example.io` domain:
@@ -486,7 +485,7 @@ The default for the maximum size of unpacked archives per project is 100 MB.
To change this value:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Pages**.
1. Update the value for **Maximum size of pages (MB)**.
diff --git a/doc/administration/polling.md b/doc/administration/polling.md
index 5699bf78c04..7f1e7a047cf 100644
--- a/doc/administration/polling.md
+++ b/doc/administration/polling.md
@@ -26,7 +26,7 @@ The default value (`1`) is recommended for the majority of GitLab installations.
To adjust the polling interval multiplier:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Polling interval multiplier**.
1. Set a value for the polling interval multiplier. This multiplier is applied to all resources at
diff --git a/doc/administration/raketasks/maintenance.md b/doc/administration/raketasks/maintenance.md
index a900da52aba..c4401b49180 100644
--- a/doc/administration/raketasks/maintenance.md
+++ b/doc/administration/raketasks/maintenance.md
@@ -326,14 +326,20 @@ database: gitlabhq_production
Database migrations can be stuck in an incomplete state, with a `down`
status in the output of the `sudo gitlab-rake db:migrate:status` command.
-To complete these migrations, use the following Rake task:
+1. To complete these migrations, use the following Rake task:
-```shell
-sudo gitlab-rake db:migrate
-```
+ ```shell
+ sudo gitlab-rake db:migrate
+ ```
+
+1. After the command completes, run `sudo gitlab-rake db:migrate:status` to check if all migrations are completed (have an `up` status).
+
+1. Hot reload `puma` and `sidekiq` services:
-After the command completes, run `sudo gitlab-rake db:migrate:status` to check if all
-migrations are completed (have an `up` status).
+ ```shell
+ sudo gitlab-ctl hup puma
+ sudo gitlab-ctl restart sidekiq
+ ```
## Rebuild database indexes
diff --git a/doc/administration/raketasks/project_import_export.md b/doc/administration/raketasks/project_import_export.md
index f3a9845d129..00bd71af6c5 100644
--- a/doc/administration/raketasks/project_import_export.md
+++ b/doc/administration/raketasks/project_import_export.md
@@ -45,7 +45,7 @@ Note the following:
compatible as described in the [Version history](../../user/project/settings/import_export.md#version-history).
- The project import option must be enabled:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand **Visibility and access controls**.
1. Under **Import sources**, check the "Project export enabled" option.
diff --git a/doc/administration/raketasks/storage.md b/doc/administration/raketasks/storage.md
index 27b899dd1b1..fc0ff23c5b1 100644
--- a/doc/administration/raketasks/storage.md
+++ b/doc/administration/raketasks/storage.md
@@ -109,7 +109,7 @@ sudo gitlab-rake gitlab:storage:migrate_to_hashed ID_FROM=50 ID_TO=100
To monitor the progress in GitLab:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
1. Watch how long the `hashed_storage:hashed_storage_project_migrate` queue
takes to finish. After it reaches zero, you can confirm every project
diff --git a/doc/administration/raketasks/uploads/migrate.md b/doc/administration/raketasks/uploads/migrate.md
index c73840cb9ff..216c0875645 100644
--- a/doc/administration/raketasks/uploads/migrate.md
+++ b/doc/administration/raketasks/uploads/migrate.md
@@ -79,7 +79,8 @@ The Rake task uses three parameters to find uploads to migrate:
NOTE:
These parameters are mainly internal to the structure of GitLab, you may want to refer to the task list
-instead below.
+instead below. After running these individual tasks, we recommend that you run the [all-in-one Rake task](#all-in-one-rake-task)
+to migrate any uploads not included in the listed types.
This task also accepts an environment variable which you can use to override
the default batch size:
diff --git a/doc/administration/redis/replication_and_failover.md b/doc/administration/redis/replication_and_failover.md
index c4b83b66738..b775b579fd4 100644
--- a/doc/administration/redis/replication_and_failover.md
+++ b/doc/administration/redis/replication_and_failover.md
@@ -343,7 +343,7 @@ NOTE:
If you are using an external Redis Sentinel instance, be sure
to exclude the `requirepass` parameter from the Sentinel
configuration. This parameter causes clients to report `NOAUTH
-Authentication required.`.
+Authentication required.`.
[Redis Sentinel 3.2.x does not support password authentication](https://github.com/antirez/redis/issues/3279).
Now that the Redis servers are all set up, let's configure the Sentinel
diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md
index fcc32100bc8..5d676dac000 100644
--- a/doc/administration/reference_architectures/10k_users.md
+++ b/doc/administration/reference_architectures/10k_users.md
@@ -18,30 +18,30 @@ full list of reference architectures, see
> - **Test requests per second (RPS) rates:** API: 200 RPS, Web: 20 RPS, Git (Pull): 20 RPS, Git (Push): 4 RPS
> - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/10k)**
-| Service | Nodes | Configuration | GCP | AWS | Azure |
-|-----------------------------------------------------|----------------|-------------------------|------------------|----------------|----------------|
-| External load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Consul<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| PostgreSQL<sup>1</sup> | 3 | 8 vCPU, 30 GB memory | `n1-standard-8` | `m5.2xlarge` | `D8s v3` |
-| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Internal load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Redis/Sentinel - Cache<sup>2</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| Redis/Sentinel - Persistent<sup>2</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| Gitaly<sup>5</sup> | 3 | 16 vCPU, 60 GB memory | `n1-standard-16` | `m5.4xlarge` | `D16s v3` |
-| Praefect<sup>5</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Sidekiq | 4 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| GitLab Rails | 3 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` | `F32s v2` |
-| Monitoring node | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
-| Object storage<sup>4</sup> | Not applicable | Not applicable | Not applicable | Not applicable | Not applicable |
-| NFS server (non-Gitaly) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
+| Service | Nodes | Configuration | GCP | AWS | Azure |
+|------------------------------------------|-------|-------------------------|------------------|----------------|-----------|
+| External load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Consul<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| PostgreSQL<sup>1</sup> | 3 | 8 vCPU, 30 GB memory | `n1-standard-8` | `m5.2xlarge` | `D8s v3` |
+| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Internal load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Redis/Sentinel - Cache<sup>2</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
+| Redis/Sentinel - Persistent<sup>2</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
+| Gitaly<sup>5</sup> | 3 | 16 vCPU, 60 GB memory | `n1-standard-16` | `m5.4xlarge` | `D16s v3` |
+| Praefect<sup>5</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Sidekiq | 4 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
+| GitLab Rails | 3 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` | `F32s v2` |
+| Monitoring node | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
+| Object storage<sup>4</sup> | - | - | - | - | - |
+| NFS server (non-Gitaly) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
<!-- 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. [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. However, Amazon Aurora is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440), and Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for 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. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
-4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+4. Should be run on reputable Cloud Provider or Self Managed solutions. More information can be found in the [Configure the object storage](#configure-the-object-storage) section.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
<!-- markdownlint-enable MD029 -->
@@ -142,10 +142,12 @@ Before starting, you should take note of the following requirements / guidance f
This reference architecture was built and tested on Google Cloud Platform (GCP) using the
[Intel Xeon E5 v3 (Haswell)](https://cloud.google.com/compute/docs/cpu-platforms)
-CPU platform. On different hardware you may find that adjustments, either lower
-or higher, are required for your CPU or node counts. For more information, see
-our [Sysbench](https://github.com/akopytov/sysbench)-based
-[CPU benchmarks](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Reference-Architectures/GCP-CPU-Benchmarks).
+CPU platform as a baseline ([Sysbench benchmark](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Reference-Architectures/GCP-CPU-Benchmarks)).
+
+Newer, similarly sized CPUs are supported and may have improved performance as a result. For Omnibus environments, ARM-based equivalents are also supported.
+
+NOTE:
+Any "burstable" instance types are not recommended due to inconsistent performance.
### Supported infrastructure
@@ -186,10 +188,11 @@ 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.
-1. [Configure NFS](#configure-nfs)
- to have shared disk storage service for certain GitLab operations (non Gitaly or Object Storage).
The servers start on the same 10.6.0.0/24 private network range, and can
connect to each other freely on these addresses.
@@ -259,7 +262,7 @@ Configure your load balancer to pass connections on port 443 as `TCP` rather
than `HTTP(S)` protocol. This will pass the connection to the application node's
NGINX service untouched. NGINX will have the SSL certificate and listen on port 443.
-See the [NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
+See the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
for details on managing SSL certificates and configuring NGINX.
### Load balancer terminates SSL without backend SSL
@@ -270,7 +273,7 @@ terminating SSL.
Since communication between the load balancer and GitLab will not be secure,
there is some additional configuration needed. See the
-[NGINX proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#supporting-proxied-ssl)
+[proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-a-reverse-proxy-or-load-balancer-ssl-termination)
for details.
### Load balancer terminates SSL with backend SSL
@@ -283,7 +286,7 @@ Traffic will also be secure between the load balancers and NGINX in this
scenario. There is no need to add configuration for proxied SSL since the
connection will be secure all the way. However, configuration will need to be
added to GitLab to configure SSL certificates. See
-[NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
+the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
for details on managing SSL certificates and configuring NGINX.
### Readiness checks
@@ -2009,7 +2012,7 @@ On each node perform the following:
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-1. If you're [using NFS](#configure-nfs):
+1. If you're [using NFS](#configure-nfs-optional):
1. If necessary, install the NFS client utility packages using the following
commands:
@@ -2069,7 +2072,7 @@ On each node perform the following:
When you specify `https` in the `external_url`, as in the previous example,
GitLab expects that the SSL certificates are in `/etc/gitlab/ssl/`. If the
certificates aren't present, NGINX will fail to start. For more information, see
-the [NGINX documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
+the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
### GitLab Rails post-configuration
@@ -2175,7 +2178,7 @@ 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) and in general it's better
+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.
@@ -2187,7 +2190,6 @@ GitLab has been tested on a number of object storage providers:
- [Oracle Cloud Infrastructure](https://docs.cloud.oracle.com/en-us/iaas/Content/Object/Tasks/s3compatibleapi.htm)
- [OpenStack Swift (S3 compatibility mode)](https://docs.openstack.org/swift/latest/s3_compat.html)
- [Azure Blob storage](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction)
-- On-premises hardware and appliances from various storage vendors.
- MinIO. We have [a guide to deploying this](https://docs.gitlab.com/charts/advanced/external-object-storage/minio.html) within our Helm Chart documentation.
There are two ways of specifying object storage configuration in GitLab:
@@ -2197,25 +2199,13 @@ There are two ways of specifying object storage configuration in GitLab:
- [Storage-specific form](../object_storage.md#storage-specific-configuration): Every object defines its
own object storage [connection and configuration](../object_storage.md#connection-settings).
-Starting with GitLab 13.2, consolidated object storage configuration is available. It simplifies your GitLab configuration since the connection details are shared across object types. Refer to [Consolidated object storage configuration](../object_storage.md#consolidated-object-storage-configuration) guide for instructions on how to set it up.
-
-For configuring object storage in GitLab 13.1 and earlier, or for storage types not
-supported by consolidated configuration form, refer to the following guides based
-on what features you intend to use:
-
-|Object storage type|Supported by consolidated configuration?|
-|-------------------|----------------------------------------|
-| [Backups](../../raketasks/backup_gitlab.md#uploading-backups-to-a-remote-cloud-storage) | No |
-| [Job artifacts](../job_artifacts.md#using-object-storage) including archived job logs | Yes |
-| [LFS objects](../lfs/index.md#storing-lfs-objects-in-remote-object-storage) | Yes |
-| [Uploads](../uploads.md#using-object-storage) | Yes |
-| [Container Registry](../packages/container_registry.md#use-object-storage) (optional feature) | No |
-| [Merge request diffs](../merge_request_diffs.md#using-object-storage) | Yes |
-| [Mattermost](https://docs.mattermost.com/administration/config-settings.html#file-storage)| No |
-| [Packages](../packages/index.md#using-object-storage) (optional feature) | Yes |
-| [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature) | Yes |
-| [Autoscale runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional for improved performance) | No |
-| [Terraform state files](../terraform_state.md#using-object-storage) | Yes |
+The consolidated form is used in the following examples when available.
+
+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).
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.
@@ -2228,29 +2218,13 @@ in the future.
</a>
</div>
-## Enable incremental logging
+### Enable incremental logging
GitLab Runner returns job logs in chunks which Omnibus GitLab caches temporarily on disk in `/var/opt/gitlab/gitlab-ci/builds` by default, even when using consolidated object storage. With default configuration, this directory needs to be shared through NFS on any GitLab Rails and Sidekiq nodes.
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 Advanced Search
-
-You can leverage Elasticsearch and [enable Advanced Search](../../integration/advanced_search/elasticsearch.md)
-for faster, more advanced code search across your entire GitLab instance.
-
-Elasticsearch cluster design and requirements are dependent on your specific
-data. For recommended best practices about how to set up your Elasticsearch
-cluster alongside your instance, read how to
-[choose the optimal cluster configuration](../../integration/advanced_search/elasticsearch.md#guidance-on-choosing-optimal-cluster-configuration).
-
-<div align="right">
- <a type="button" class="btn btn-default" href="#setup-components">
- Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
-
-## Configure NFS
+## Configure NFS (optional)
[Object storage](#configure-the-object-storage), along with [Gitaly](#configure-gitaly)
are recommended over NFS wherever possible for improved performance.
@@ -2258,14 +2232,24 @@ 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. Technical support is planned to be
-unavailable from GitLab 15.0. No further enhancements are planned for this feature.
+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)
+for faster, more advanced code search across your entire GitLab instance.
+
+Elasticsearch cluster design and requirements are dependent on your specific
+data. For recommended best practices about how to set up your Elasticsearch
+cluster alongside your instance, read how to
+[choose the optimal cluster configuration](../../integration/advanced_search/elasticsearch.md#guidance-on-choosing-optimal-cluster-configuration).
+
<div align="right">
<a type="button" class="btn btn-default" href="#setup-components">
Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i>
@@ -2292,6 +2276,10 @@ to be complex. **This setup is only recommended** if you have strong working
knowledge and experience in Kubernetes. The rest of this
section assumes this.
+NOTE:
+**Gitaly Cluster is not supported to be run in Kubernetes**.
+Refer to [epic 6127](https://gitlab.com/groups/gitlab-org/-/epics/6127) for more details.
+
### Cluster topology
The following tables and diagram detail the hybrid environment using the same formats
@@ -2302,11 +2290,11 @@ use Google Cloud's Kubernetes Engine (GKE) or AWS Elastic Kubernetes Service (EK
and CPU requirements should translate to most other providers. We hope to update this in the
future with further specific cloud provider details.
-| Service | 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 |
-| Supporting services such as NGINX, Prometheus | 2 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | 7.75 vCPU, 25 GB memory |
+| Service | 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 |
+| Supporting services | 2 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | 7.75 vCPU, 25 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.
@@ -2316,25 +2304,25 @@ future with further specific cloud provider details.
Next are the backend components that run on static compute VMs via Omnibus (or External PaaS
services where applicable):
-| Service | Nodes | Configuration | GCP | AWS |
-|------------------------------------------|----------------|-----------------------|------------------|----------------|
-| Consul<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| PostgreSQL<sup>1</sup> | 3 | 8 vCPU, 30 GB memory | `n1-standard-8` | `m5.2xlarge` |
-| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Internal load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| 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</sup> | 3 | 16 vCPU, 60 GB memory | `n1-standard-16` | `m5.4xlarge` |
-| Praefect<sup>5</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Object storage<sup>4</sup> | Not applicable | Not applicable | Not applicable | Not applicable |
+| Service | Nodes | Configuration | GCP | AWS |
+|------------------------------------------|-------|-----------------------|------------------|--------------|
+| Consul<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| PostgreSQL<sup>1</sup> | 3 | 8 vCPU, 30 GB memory | `n1-standard-8` | `m5.2xlarge` |
+| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| Internal load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| 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</sup> | 3 | 16 vCPU, 60 GB memory | `n1-standard-16` | `m5.4xlarge` |
+| Praefect<sup>5</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| Object storage<sup>4</sup> | - | - | - | - |
<!-- 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. [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. However, Amazon Aurora is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440), and Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for 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. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
-4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+4. Should be run on reputable Cloud Provider or Self Managed solutions. More information can be found in the [Configure the object storage](#configure-the-object-storage) section.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
<!-- markdownlint-enable MD029 -->
@@ -2353,7 +2341,7 @@ card "Kubernetes via Helm Charts" as kubernetes {
collections "**Sidekiq** x4" as sidekiq #ff8dd1
}
- card "**Supporting Services**" as support
+ card "**Supporting Services** x2" as support
}
card "**Internal Load Balancer**" as ilb #9370DB
@@ -2419,29 +2407,42 @@ documents how to apply the calculated configuration to the Helm Chart.
#### Webservice
-Webservice pods typically need about 1 vCPU and 1.25 GB of memory _per worker_.
-Each Webservice pod consumes roughly 4 vCPUs and 5 GB of memory using
+Webservice pods typically need about 1 CPU and 1.25 GB of memory _per worker_.
+Each Webservice pod consumes roughly 4 CPUs and 5 GB of memory using
the [recommended topology](#cluster-topology) because four worker processes
are created by default and each pod has other small processes running.
For 10,000 users we recommend a total Puma worker count of around 80.
With the [provided recommendations](#cluster-topology) this allows the deployment of up to 20
Webservice pods with 4 workers per pod and 5 pods per node. Expand available resources using
-the ratio of 1 vCPU to 1.25 GB of memory _per each worker process_ for each additional
+the ratio of 1 CPU to 1.25 GB of memory _per each worker process_ for each additional
Webservice pod.
For further information on resource usage, see the [Webservice resources](https://docs.gitlab.com/charts/charts/gitlab/webservice/#resources).
#### Sidekiq
-Sidekiq pods should generally have 1 vCPU and 2 GB of memory.
+Sidekiq pods should generally have 0.9 CPU and 2 GB of memory.
[The provided starting point](#cluster-topology) allows the deployment of up to
-14 Sidekiq pods. Expand available resources using the 1 vCPU to 2GB memory
+14 Sidekiq pods. Expand available resources using the 0.9 CPU to 2 GB memory
ratio for each additional pod.
For further information on resource usage, see the [Sidekiq resources](https://docs.gitlab.com/charts/charts/gitlab/sidekiq/#resources).
+### Supporting
+
+The Supporting Node Pool is designed to house all supporting deployments that don't need to be
+on the Webservice and Sidekiq pools.
+
+This includes various deployments related to the Cloud Provider's implementation and supporting
+GitLab deployments such as NGINX or [GitLab Shell](https://docs.gitlab.com/charts/charts/gitlab/gitlab-shell/).
+
+If you wish to make any additional deployments, such as for Monitoring, it's recommended
+to deploy these in this pool where possible and not in the Webservice or Sidekiq pools, as the Supporting pool has been designed
+specifically to accommodate several additional deployments. However, if your deployments don't fit into the
+pool as given, you can increase the node pool accordingly.
+
<div align="right">
<a type="button" class="btn btn-default" href="#setup-components">
Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i>
diff --git a/doc/administration/reference_architectures/1k_users.md b/doc/administration/reference_architectures/1k_users.md
index 1a774603863..96b1e541f92 100644
--- a/doc/administration/reference_architectures/1k_users.md
+++ b/doc/administration/reference_architectures/1k_users.md
@@ -72,10 +72,12 @@ Before starting, you should take note of the following requirements / guidance f
This reference architecture was built and tested on Google Cloud Platform (GCP) using the
[Intel Xeon E5 v3 (Haswell)](https://cloud.google.com/compute/docs/cpu-platforms)
-CPU platform. On different hardware you may find that adjustments, either lower
-or higher, are required for your CPU or node counts. For more information, see
-our [Sysbench](https://github.com/akopytov/sysbench)-based
-[CPU benchmarks](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Reference-Architectures/GCP-CPU-Benchmarks).
+CPU platform as a baseline ([Sysbench benchmark](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Reference-Architectures/GCP-CPU-Benchmarks)).
+
+Newer, similarly sized CPUs are supported and may have improved performance as a result. For Omnibus environments, ARM-based equivalents are also supported.
+
+NOTE:
+Any "burstable" instance types are not recommended due to inconsistent performance.
### Supported infrastructure
diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md
index 51d3e3d39a6..423dbc7abfb 100644
--- a/doc/administration/reference_architectures/25k_users.md
+++ b/doc/administration/reference_architectures/25k_users.md
@@ -18,30 +18,30 @@ full list of reference architectures, see
> - **Test requests per second (RPS) rates:** API: 500 RPS, Web: 50 RPS, Git (Pull): 50 RPS, Git (Push): 10 RPS
> - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/25k)**
-| Service | Nodes | Configuration | GCP | AWS | Azure |
-|---------------------------------------------------|----------------|-------------------------|------------------|----------------|----------------|
-| External load balancing node<sup>3</sup> | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
-| Consul<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| PostgreSQL<sup>1</sup> | 3 | 16 vCPU, 60 GB memory | `n1-standard-16` | `m5.4xlarge` | `D16s v3` |
-| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Internal load balancing node<sup>3</sup> | 1 | 4 vCPU, 3.6GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
-| Redis/Sentinel - Cache<sup>2</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| Redis/Sentinel - Persistent<sup>2</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| Gitaly<sup>5</sup> | 3 | 32 vCPU, 120 GB memory | `n1-standard-32` | `m5.8xlarge` | `D32s v3` |
-| Praefect<sup>5</sup> | 3 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
-| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Sidekiq | 4 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| GitLab Rails | 5 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` | `F32s v2` |
-| Monitoring node | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
-| Object storage<sup>4</sup> | Not applicable | Not applicable | Not applicable | Not applicable | Not applicable |
-| NFS server (non-Gitaly) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
+| Service | Nodes | Configuration | GCP | AWS | Azure |
+|------------------------------------------|-------|-------------------------|------------------|--------------|-----------|
+| External load balancing node<sup>3</sup> | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
+| Consul<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| PostgreSQL<sup>1</sup> | 3 | 16 vCPU, 60 GB memory | `n1-standard-16` | `m5.4xlarge` | `D16s v3` |
+| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Internal load balancing node<sup>3</sup> | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
+| Redis/Sentinel - Cache<sup>2</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
+| Redis/Sentinel - Persistent<sup>2</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
+| Gitaly<sup>5</sup> | 3 | 32 vCPU, 120 GB memory | `n1-standard-32` | `m5.8xlarge` | `D32s v3` |
+| Praefect<sup>5</sup> | 3 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
+| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Sidekiq | 4 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
+| GitLab Rails | 5 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` | `F32s v2` |
+| Monitoring node | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
+| Object storage<sup>4</sup> | - | - | - | - | - |
+| NFS server (non-Gitaly) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
<!-- 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. [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. However, Amazon Aurora is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440), and Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for 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. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
-4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+4. Should be run on reputable Cloud Provider or Self Managed solutions. More information can be found in the [Configure the object storage](#configure-the-object-storage) section.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
<!-- markdownlint-enable MD029 -->
@@ -142,10 +142,12 @@ Before starting, you should take note of the following requirements / guidance f
This reference architecture was built and tested on Google Cloud Platform (GCP) using the
[Intel Xeon E5 v3 (Haswell)](https://cloud.google.com/compute/docs/cpu-platforms)
-CPU platform. On different hardware you may find that adjustments, either lower
-or higher, are required for your CPU or node counts. For more information, see
-our [Sysbench](https://github.com/akopytov/sysbench)-based
-[CPU benchmarks](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Reference-Architectures/GCP-CPU-Benchmarks).
+CPU platform as a baseline ([Sysbench benchmark](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Reference-Architectures/GCP-CPU-Benchmarks)).
+
+Newer, similarly sized CPUs are supported and may have improved performance as a result. For Omnibus environments, ARM-based equivalents are also supported.
+
+NOTE:
+Any "burstable" instance types are not recommended due to inconsistent performance.
### Supported infrastructure
@@ -186,11 +188,11 @@ 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.
-1. [Configure NFS](#configure-nfs)
- to have shared disk storage service for certain GitLab operations (non
- Gitaly or Object Storage).
The servers start on the same 10.6.0.0/24 private network range, and can
connect to each other freely on these addresses.
@@ -262,7 +264,7 @@ Configure your load balancer to pass connections on port 443 as `TCP` rather
than `HTTP(S)` protocol. This will pass the connection to the application node's
NGINX service untouched. NGINX will have the SSL certificate and listen on port 443.
-See the [NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
+See the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
for details on managing SSL certificates and configuring NGINX.
### Load balancer terminates SSL without backend SSL
@@ -273,7 +275,7 @@ terminating SSL.
Since communication between the load balancer and GitLab will not be secure,
there is some additional configuration needed. See the
-[NGINX proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#supporting-proxied-ssl)
+[proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-a-reverse-proxy-or-load-balancer-ssl-termination)
for details.
### Load balancer terminates SSL with backend SSL
@@ -285,8 +287,8 @@ end users will see.
Traffic will also be secure between the load balancers and NGINX in this
scenario. There is no need to add configuration for proxied SSL since the
connection will be secure all the way. However, configuration will need to be
-added to GitLab to configure SSL certificates. See
-[NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
+added to GitLab to configure SSL certificates. See the
+[HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
for details on managing SSL certificates and configuring NGINX.
### Readiness checks
@@ -2015,7 +2017,7 @@ On each node perform the following:
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-1. If you're [using NFS](#configure-nfs):
+1. If you're [using NFS](#configure-nfs-optional):
1. If necessary, install the NFS client utility packages using the following
commands:
@@ -2074,7 +2076,7 @@ On each node perform the following:
When you specify `https` in the `external_url`, as in the previous example,
GitLab expects that the SSL certificates are in `/etc/gitlab/ssl/`. If the
certificates aren't present, NGINX will fail to start. For more information, see
-the [NGINX documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
+the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
### GitLab Rails post-configuration
@@ -2179,9 +2181,9 @@ To configure the Monitoring node:
## Configure the object storage
GitLab supports using an object storage service for holding numerous types of data.
-Object storage is also recommended over [NFS](#configure-nfs) and in general
-it's better in larger setups as object storage is typically much more performant,
-reliable, and scalable.
+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:
@@ -2191,7 +2193,6 @@ GitLab has been tested on a number of object storage providers:
- [Oracle Cloud Infrastructure](https://docs.cloud.oracle.com/en-us/iaas/Content/Object/Tasks/s3compatibleapi.htm)
- [OpenStack Swift (S3 compatibility mode)](https://docs.openstack.org/swift/latest/s3_compat.html)
- [Azure Blob storage](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction)
-- On-premises hardware and appliances from various storage vendors.
- MinIO. We have [a guide to deploying this](https://docs.gitlab.com/charts/advanced/external-object-storage/minio.html) within our Helm Chart documentation.
There are two ways of specifying object storage configuration in GitLab:
@@ -2201,25 +2202,13 @@ There are two ways of specifying object storage configuration in GitLab:
- [Storage-specific form](../object_storage.md#storage-specific-configuration): Every object defines its
own object storage [connection and configuration](../object_storage.md#connection-settings).
-Starting with GitLab 13.2, consolidated object storage configuration is available. It simplifies your GitLab configuration since the connection details are shared across object types. Refer to [Consolidated object storage configuration](../object_storage.md#consolidated-object-storage-configuration) guide for instructions on how to set it up.
-
-For configuring object storage in GitLab 13.1 and earlier, or for storage types not
-supported by consolidated configuration form, refer to the following guides based
-on what features you intend to use:
-
-|Object storage type|Supported by consolidated configuration?|
-|-------------------|----------------------------------------|
-| [Backups](../../raketasks/backup_gitlab.md#uploading-backups-to-a-remote-cloud-storage) | No |
-| [Job artifacts](../job_artifacts.md#using-object-storage) including archived job logs | Yes |
-| [LFS objects](../lfs/index.md#storing-lfs-objects-in-remote-object-storage) | Yes |
-| [Uploads](../uploads.md#using-object-storage) | Yes |
-| [Container Registry](../packages/container_registry.md#use-object-storage) (optional feature) | No |
-| [Merge request diffs](../merge_request_diffs.md#using-object-storage) | Yes |
-| [Mattermost](https://docs.mattermost.com/administration/config-settings.html#file-storage)| No |
-| [Packages](../packages/index.md#using-object-storage) (optional feature) | Yes |
-| [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature) | Yes |
-| [Autoscale runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional for improved performance) | No |
-| [Terraform state files](../terraform_state.md#using-object-storage) | Yes |
+The consolidated form is used in the following examples when available.
+
+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).
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.
@@ -2232,12 +2221,28 @@ in the future.
</a>
</div>
-## Enable incremental logging
+### Enable incremental logging
GitLab Runner returns job logs in chunks which Omnibus GitLab caches temporarily on disk in `/var/opt/gitlab/gitlab-ci/builds` by default, even when using consolidated object storage. With default configuration, this directory needs to be shared through NFS on any GitLab Rails and Sidekiq nodes.
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)
@@ -2254,22 +2259,6 @@ cluster alongside your instance, read how to
</a>
</div>
-## Configure NFS
-
-[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. Technical support is planned to be
-unavailable from GitLab 15.0. 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).
-
## Cloud Native Hybrid reference architecture with Helm Charts (alternative)
As an alternative approach, you can also run select components of GitLab as Cloud Native
@@ -2290,6 +2279,10 @@ to be complex. **This setup is only recommended** if you have strong working
knowledge and experience in Kubernetes. The rest of this
section assumes this.
+NOTE:
+**Gitaly Cluster is not supported to be run in Kubernetes**.
+Refer to [epic 6127](https://gitlab.com/groups/gitlab-org/-/epics/6127) for more details.
+
### Cluster topology
The following tables and diagram detail the hybrid environment using the same formats
@@ -2300,11 +2293,11 @@ use Google Cloud's Kubernetes Engine (GKE) or AWS Elastic Kubernetes Service (EK
and CPU requirements should translate to most other providers. We hope to update this in the
future with further specific cloud provider details.
-| Service | 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 |
-| Supporting services such as NGINX, Prometheus | 2 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | 7.75 vCPU, 25 GB memory |
+| Service | 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 |
+| Supporting services | 2 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | 7.75 vCPU, 25 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.
@@ -2314,25 +2307,25 @@ future with further specific cloud provider details.
Next are the backend components that run on static compute VMs via Omnibus (or External PaaS
services where applicable):
-| Service | Nodes | Configuration | GCP | AWS |
-|------------------------------------------|----------------|------------------------|------------------|----------------|
-| 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` |
-| 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</sup> | 3 | 32 vCPU, 120 GB memory | `n1-standard-32` | `m5.8xlarge` |
-| Praefect<sup>5</sup> | 3 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` |
-| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Object storage<sup>4</sup> | Not applicable | Not applicable | Not applicable | Not applicable |
+| Service | Nodes | Configuration | GCP | AWS |
+|------------------------------------------|-------|------------------------|------------------|--------------|
+| 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` |
+| 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</sup> | 3 | 32 vCPU, 120 GB memory | `n1-standard-32` | `m5.8xlarge` |
+| Praefect<sup>5</sup> | 3 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` |
+| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| Object storage<sup>4</sup> | - | - | - | - |
<!-- 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. [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. However, Amazon Aurora is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440), and Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for 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. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
-4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+4. Should be run on reputable Cloud Provider or Self Managed solutions. More information can be found in the [Configure the object storage](#configure-the-object-storage) section.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
<!-- markdownlint-enable MD029 -->
@@ -2347,11 +2340,11 @@ card "Kubernetes via Helm Charts" as kubernetes {
card "**External Load Balancer**" as elb #6a9be7
together {
- collections "**Webservice** x4" as gitlab #32CD32
+ collections "**Webservice** x7" as gitlab #32CD32
collections "**Sidekiq** x4" as sidekiq #ff8dd1
}
- card "**Supporting Services**" as support
+ card "**Supporting Services** x2" as support
}
card "**Internal Load Balancer**" as ilb #9370DB
@@ -2417,29 +2410,42 @@ documents how to apply the calculated configuration to the Helm Chart.
#### Webservice
-Webservice pods typically need about 1 vCPU and 1.25 GB of memory _per worker_.
-Each Webservice pod consumes roughly 4 vCPUs and 5 GB of memory using
+Webservice pods typically need about 1 CPU and 1.25 GB of memory _per worker_.
+Each Webservice pod consumes roughly 4 CPUs and 5 GB of memory using
the [recommended topology](#cluster-topology) because four worker processes
are created by default and each pod has other small processes running.
For 25,000 users we recommend a total Puma worker count of around 140.
With the [provided recommendations](#cluster-topology) this allows the deployment of up to 35
Webservice pods with 4 workers per pod and 5 pods per node. Expand available resources using
-the ratio of 1 vCPU to 1.25 GB of memory _per each worker process_ for each additional
+the ratio of 1 CPU to 1.25 GB of memory _per each worker process_ for each additional
Webservice pod.
For further information on resource usage, see the [Webservice resources](https://docs.gitlab.com/charts/charts/gitlab/webservice/#resources).
#### Sidekiq
-Sidekiq pods should generally have 1 vCPU and 2 GB of memory.
+Sidekiq pods should generally have 0.9 CPU and 2 GB of memory.
[The provided starting point](#cluster-topology) allows the deployment of up to
-14 Sidekiq pods. Expand available resources using the 1 vCPU to 2GB memory
+14 Sidekiq pods. Expand available resources using the 0.9 CPU to 2 GB memory
ratio for each additional pod.
For further information on resource usage, see the [Sidekiq resources](https://docs.gitlab.com/charts/charts/gitlab/sidekiq/#resources).
+### Supporting
+
+The Supporting Node Pool is designed to house all supporting deployments that don't need to be
+on the Webservice and Sidekiq pools.
+
+This includes various deployments related to the Cloud Provider's implementation and supporting
+GitLab deployments such as NGINX or [GitLab Shell](https://docs.gitlab.com/charts/charts/gitlab/gitlab-shell/).
+
+If you wish to make any additional deployments, such as for Monitoring, it's recommended
+to deploy these in this pool where possible and not in the Webservice or Sidekiq pools, as the Supporting pool has been designed
+specifically to accommodate several additional deployments. However, if your deployments don't fit into the
+pool as given, you can increase the node pool accordingly.
+
<div align="right">
<a type="button" class="btn btn-default" href="#setup-components">
Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i>
diff --git a/doc/administration/reference_architectures/2k_users.md b/doc/administration/reference_architectures/2k_users.md
index 8ca58da55ca..99cc6d47f6a 100644
--- a/doc/administration/reference_architectures/2k_users.md
+++ b/doc/administration/reference_architectures/2k_users.md
@@ -19,22 +19,22 @@ For a full list of reference architectures, see
> - **Test requests per second (RPS) rates:** API: 40 RPS, Web: 4 RPS, Git (Pull): 4 RPS, Git (Push): 1 RPS
> - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/2k)**
-| Service | Nodes | Configuration | GCP | AWS | Azure |
-|------------------------------------------|----------------|-------------------------|-----------------|----------------|----------------|
-| Load balancer<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| PostgreSQL<sup>1</sup> | 1 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | `D2s v3` |
-| Redis<sup>2</sup> | 1 | 1 vCPU, 3.75 GB memory | `n1-standard-1` | `m5.large` | `D2s v3` |
-| Gitaly | 1 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| 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> | Not applicable | Not applicable | Not applicable | Not applicable | Not applicable |
-| NFS server (non-Gitaly) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
+| Service | Nodes | Configuration | GCP | AWS | Azure |
+|----------------------------|-------|------------------------|-----------------|--------------|----------|
+| Load balancer<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| PostgreSQL<sup>1</sup> | 1 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | `D2s v3` |
+| Redis<sup>2</sup> | 1 | 1 vCPU, 3.75 GB memory | `n1-standard-1` | `m5.large` | `D2s v3` |
+| Gitaly | 1 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
+| 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. [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. However, Amazon Aurora is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440), and Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for 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 as reputable third-party external PaaS Redis solutions. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run as reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
-4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+4. Should be run on reputable Cloud Provider or Self Managed solutions. More information can be found in the [Configure the object storage](#configure-the-object-storage) section.
<!-- markdownlint-enable MD029 -->
NOTE:
@@ -78,10 +78,12 @@ Before starting, you should take note of the following requirements / guidance f
This reference architecture was built and tested on Google Cloud Platform (GCP) using the
[Intel Xeon E5 v3 (Haswell)](https://cloud.google.com/compute/docs/cpu-platforms)
-CPU platform. On different hardware you may find that adjustments, either lower
-or higher, are required for your CPU or node counts. For more information, see
-our [Sysbench](https://github.com/akopytov/sysbench)-based
-[CPU benchmarks](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Reference-Architectures/GCP-CPU-Benchmarks).
+CPU platform as a baseline ([Sysbench benchmark](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Reference-Architectures/GCP-CPU-Benchmarks)).
+
+Newer, similarly sized CPUs are supported and may have improved performance as a result. For Omnibus environments, ARM-based equivalents are also supported.
+
+NOTE:
+Any "burstable" instance types are not recommended due to inconsistent performance.
### Supported infrastructure
@@ -110,11 +112,11 @@ 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 Advanced Search](#configure-advanced-search) (optional) for faster,
- more advanced code search across your entire GitLab instance.
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.
## Configure the external load balancer
@@ -147,7 +149,7 @@ of `HTTP(S)`. This will pass the connection unaltered to the application node's
NGINX service, which has the SSL certificate and listens to port 443.
For details about managing SSL certificates and configuring NGINX, see the
-[NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
+[HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
### Load balancer terminates SSL without backend SSL
@@ -157,7 +159,7 @@ terminating SSL.
Due to communication between the load balancer and GitLab not being secure,
you'll need to complete some additional configuration. For details, see the
-[NGINX proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#supporting-proxied-ssl).
+[proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-a-reverse-proxy-or-load-balancer-ssl-termination).
### Load balancer terminates SSL with backend SSL
@@ -169,7 +171,7 @@ Traffic will be secure between the load balancers and NGINX in this scenario,
and there's no need to add a configuration for proxied SSL. However, you'll
need to add a configuration to GitLab to configure SSL certificates. For
details about managing SSL certificates and configuring NGINX, see the
-[NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
+[HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
### Readiness checks
@@ -750,7 +752,7 @@ On each node perform the following:
When you specify `https` in the `external_url`, as in the previous example,
GitLab expects that the SSL certificates are in `/etc/gitlab/ssl/`. If the
certificates aren't present, NGINX will fail to start. For more information, see
-the [NGINX documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
+the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
### GitLab Rails post-configuration
@@ -885,10 +887,10 @@ running [Prometheus](../monitoring/prometheus/index.md) and
## Configure the object storage
-GitLab supports using an object storage service for holding several types of
-data, and is recommended over [NFS](#configure-nfs-optional). In general,
-object storage services are better for larger environments, as object storage
-is typically much more performant, reliable, and scalable.
+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:
@@ -898,7 +900,6 @@ GitLab has been tested on a number of object storage providers:
- [Oracle Cloud Infrastructure](https://docs.cloud.oracle.com/en-us/iaas/Content/Object/Tasks/s3compatibleapi.htm)
- [OpenStack Swift (S3 compatibility mode)](https://docs.openstack.org/swift/latest/s3_compat.html)
- [Azure Blob storage](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction)
-- On-premises hardware and appliances from various storage vendors.
- MinIO. We have [a guide to deploying this](https://docs.gitlab.com/charts/advanced/external-object-storage/minio.html) within our Helm Chart documentation.
There are two ways of specifying object storage configuration in GitLab:
@@ -908,29 +909,13 @@ There are two ways of specifying object storage configuration in GitLab:
- [Storage-specific form](../object_storage.md#storage-specific-configuration): Every object defines its
own object storage [connection and configuration](../object_storage.md#connection-settings).
-Starting with GitLab 13.2, consolidated object storage configuration is available. It simplifies your GitLab configuration since the connection details are shared across object types. Refer to [Consolidated object storage configuration](../object_storage.md#consolidated-object-storage-configuration) guide for instructions on how to set it up.
-
-GitLab Runner returns job logs in chunks which Omnibus GitLab caches temporarily on disk in `/var/opt/gitlab/gitlab-ci/builds` by default, even when using consolidated object storage. With default configuration, this directory needs to be shared via NFS on any GitLab Rails and Sidekiq nodes.
-
-In GitLab 13.6 and later, it's also recommended to switch to [Incremental logging](../job_logs.md#incremental-logging-architecture), which uses Redis instead of disk space for temporary caching of job logs. This is required when no NFS node has been deployed.
+The consolidated form is used in the following examples when available.
-For configuring object storage in GitLab 13.1 and earlier, or for storage types not
-supported by consolidated configuration form, refer to the following guides based
-on what features you intend to use:
-
-|Object storage type|Supported by consolidated configuration?|
-|-------------------|----------------------------------------|
-| [Backups](../../raketasks/backup_gitlab.md#uploading-backups-to-a-remote-cloud-storage) | No |
-| [Job artifacts](../job_artifacts.md#using-object-storage) including archived job logs | Yes |
-| [LFS objects](../lfs/index.md#storing-lfs-objects-in-remote-object-storage) | Yes |
-| [Uploads](../uploads.md#using-object-storage) | Yes |
-| [Container Registry](../packages/container_registry.md#use-object-storage) (optional feature) | No |
-| [Merge request diffs](../merge_request_diffs.md#using-object-storage) | Yes |
-| [Mattermost](https://docs.mattermost.com/administration/config-settings.html#file-storage)| No |
-| [Packages](../packages/index.md#using-object-storage) (optional feature) | Yes |
-| [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature) | Yes |
-| [Autoscale runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional for improved performance) | No |
-| [Terraform state files](../terraform_state.md#using-object-storage) | Yes |
+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).
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.
@@ -943,21 +928,11 @@ in the future.
</a>
</div>
-## Configure Advanced Search **(PREMIUM SELF)**
-
-You can leverage Elasticsearch and [enable Advanced Search](../../integration/advanced_search/elasticsearch.md)
-for faster, more advanced code search across your entire GitLab instance.
+### Enable incremental logging
-Elasticsearch cluster design and requirements are dependent on your specific
-data. For recommended best practices about how to set up your Elasticsearch
-cluster alongside your instance, read how to
-[choose the optimal cluster configuration](../../integration/advanced_search/elasticsearch.md#guidance-on-choosing-optimal-cluster-configuration).
+GitLab Runner returns job logs in chunks which Omnibus GitLab caches temporarily on disk in `/var/opt/gitlab/gitlab-ci/builds` by default, even when using consolidated object storage. With default configuration, this directory needs to be shared through NFS on any GitLab Rails and Sidekiq nodes.
-<div align="right">
- <a type="button" class="btn btn-default" href="#setup-components">
- Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i>
- </a>
-</div>
+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)
@@ -968,14 +943,30 @@ possible.
See how to [configure NFS](../nfs.md).
WARNING:
-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.
+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)
+for faster, more advanced code search across your entire GitLab instance.
+
+Elasticsearch cluster design and requirements are dependent on your specific
+data. For recommended best practices about how to set up your Elasticsearch
+cluster alongside your instance, read how to
+[choose the optimal cluster configuration](../../integration/advanced_search/elasticsearch.md#guidance-on-choosing-optimal-cluster-configuration).
+
+<div align="right">
+ <a type="button" class="btn btn-default" href="#setup-components">
+ Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i>
+ </a>
+</div>
+
## Cloud Native Hybrid reference architecture with Helm Charts (alternative)
As an alternative approach, you can also run select components of GitLab as Cloud Native
@@ -998,6 +989,10 @@ to be complex. **This setup is only recommended** if you have strong working
knowledge and experience in Kubernetes. The rest of this
section assumes this.
+NOTE:
+**Gitaly Cluster is not supported to be run in Kubernetes**.
+Refer to [epic 6127](https://gitlab.com/groups/gitlab-org/-/epics/6127) for more details.
+
### Cluster topology
The following tables and diagram detail the hybrid environment using the same formats
@@ -1008,11 +1003,11 @@ use Google Cloud's Kubernetes Engine (GKE) or AWS Elastic Kubernetes Service (EK
and CPU requirements should translate to most other providers. We hope to update this in the
future with further specific cloud provider details.
-| Service | 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 | 1 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | 3.9 vCPU, 11.8 GB memory |
-| Supporting services such as NGINX, Prometheus | 2 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | 1.9 vCPU, 5.5 GB memory |
+| Service | 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 |
+| Supporting services | 2 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | 1.9 vCPU, 5.5 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.
@@ -1022,18 +1017,18 @@ future with further specific cloud provider details.
Next are the backend components that run on static compute VMs via Omnibus (or External PaaS
services where applicable):
-| Service | Nodes | Configuration | GCP | AWS |
-|----------------------------|----------------|------------------------|-----------------|----------------|
-| PostgreSQL<sup>1</sup> | 1 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` |
-| Redis<sup>2</sup> | 1 | 1 vCPU, 3.75 GB memory | `n1-standard-1` | `m5.large` |
-| Gitaly | 1 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` |
-| Object storage<sup>3</sup> | Not applicable | Not applicable | Not applicable | Not applicable |
+| Service | Nodes | Configuration | GCP | AWS |
+|----------------------------|-------|------------------------|-----------------|-------------|
+| PostgreSQL<sup>1</sup> | 1 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` |
+| Redis<sup>2</sup> | 1 | 1 vCPU, 3.75 GB memory | `n1-standard-1` | `m5.large` |
+| Gitaly | 1 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` |
+| Object storage<sup>3</sup> | - | - | - | - |
<!-- 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. [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. However, Amazon Aurora is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440), and Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for 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. Google Memorystore and AWS ElastiCache are known to work.
-3. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+3. Should be run on reputable Cloud Provider or Self Managed solutions. More information can be found in the [Configure the object storage](#configure-the-object-storage) section.
<!-- markdownlint-enable MD029 -->
NOTE:
@@ -1048,9 +1043,10 @@ card "Kubernetes via Helm Charts" as kubernetes {
together {
collections "**Webservice** x3" as gitlab #32CD32
- card "**Sidekiq**" as sidekiq #ff8dd1
- collections "**Supporting Services** x2" as support
+ collections "**Sidekiq** x2" as sidekiq #ff8dd1
}
+
+ collections "**Supporting Services** x2" as support
}
card "**Gitaly**" as gitaly #FF8C00
@@ -1081,29 +1077,42 @@ documents how to apply the calculated configuration to the Helm Chart.
#### Webservice
-Webservice pods typically need about 1 vCPU and 1.25 GB of memory _per worker_.
-Each Webservice pod consumes roughly 4 vCPUs and 5 GB of memory using
+Webservice pods typically need about 1 CPU and 1.25 GB of memory _per worker_.
+Each Webservice pod consumes roughly 4 CPUs and 5 GB of memory using
the [recommended topology](#cluster-topology) because two worker processes
are created by default and each pod has other small processes running.
For 2,000 users we recommend a total Puma worker count of around 12.
With the [provided recommendations](#cluster-topology) this allows the deployment of up to 3
Webservice pods with 4 workers per pod and 1 pod per node. Expand available resources using
-the ratio of 1 vCPU to 1.25 GB of memory _per each worker process_ for each additional
+the ratio of 1 CPU to 1.25 GB of memory _per each worker process_ for each additional
Webservice pod.
For further information on resource usage, see the [Webservice resources](https://docs.gitlab.com/charts/charts/gitlab/webservice/#resources).
#### Sidekiq
-Sidekiq pods should generally have 1 vCPU and 2 GB of memory.
+Sidekiq pods should generally have 0.9 CPU and 2 GB of memory.
[The provided starting point](#cluster-topology) allows the deployment of up to
-4 Sidekiq pods. Expand available resources using the 1 vCPU to 2 GB memory
+4 Sidekiq pods. Expand available resources using the 0.9 CPU to 2 GB memory
ratio for each additional pod.
For further information on resource usage, see the [Sidekiq resources](https://docs.gitlab.com/charts/charts/gitlab/sidekiq/#resources).
+### Supporting
+
+The Supporting Node Pool is designed to house all supporting deployments that don't need to be
+on the Webservice and Sidekiq pools.
+
+This includes various deployments related to the Cloud Provider's implementation and supporting
+GitLab deployments such as NGINX or [GitLab Shell](https://docs.gitlab.com/charts/charts/gitlab/gitlab-shell/).
+
+If you wish to make any additional deployments, such as for Monitoring, it's recommended
+to deploy these in this pool where possible and not in the Webservice or Sidekiq pools, as the Supporting pool has been designed
+specifically to accommodate several additional deployments. However, if your deployments don't fit into the
+pool as given, you can increase the node pool accordingly.
+
<div align="right">
<a type="button" class="btn btn-default" href="#setup-components">
Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i>
diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md
index db81ae7ae3c..5c227e3dc27 100644
--- a/doc/administration/reference_architectures/3k_users.md
+++ b/doc/administration/reference_architectures/3k_users.md
@@ -28,29 +28,29 @@ For a full list of reference architectures, see
> - **Test requests per second (RPS) rates:** API: 60 RPS, Web: 6 RPS, Git (Pull): 6 RPS, Git (Push): 1 RPS
> - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/3k)**
-| Service | Nodes | Configuration | GCP | AWS | Azure |
-|--------------------------------------------|----------------|-----------------------|-----------------|----------------|----------------|
-| External load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Redis<sup>2</sup> | 3 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | `D2s v3` |
-| Consul<sup>1</sup> + Sentinel<sup>2</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| PostgreSQL<sup>1</sup> | 3 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | `D2s v3` |
-| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Internal load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Gitaly<sup>5</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| Praefect<sup>5</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Sidekiq | 4 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | `D2s v3` |
-| GitLab Rails | 3 | 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> | Not applicable | Not applicable | Not applicable | Not applicable | Not applicable |
-| NFS server (non-Gitaly) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
+| Service | Nodes | Configuration | GCP | AWS | Azure |
+|-------------------------------------------|-------|-----------------------|-----------------|--------------|----------|
+| External load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Redis<sup>2</sup> | 3 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | `D2s v3` |
+| Consul<sup>1</sup> + Sentinel<sup>2</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| PostgreSQL<sup>1</sup> | 3 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | `D2s v3` |
+| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Internal load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Gitaly<sup>5</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
+| Praefect<sup>5</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Sidekiq | 4 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | `D2s v3` |
+| GitLab Rails | 3 | 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` |
<!-- 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. [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. However, Amazon Aurora is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440), and Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for 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. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
-4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+4. Should be run on reputable Cloud Provider or Self Managed solutions. More information can be found in the [Configure the object storage](#configure-the-object-storage) section.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
<!-- markdownlint-enable MD029 -->
@@ -148,10 +148,12 @@ Before starting, you should take note of the following requirements / guidance f
This reference architecture was built and tested on Google Cloud Platform (GCP) using the
[Intel Xeon E5 v3 (Haswell)](https://cloud.google.com/compute/docs/cpu-platforms)
-CPU platform. On different hardware you may find that adjustments, either lower
-or higher, are required for your CPU or node counts. For more information, see
-our [Sysbench](https://github.com/akopytov/sysbench)-based
-[CPU benchmarks](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Reference-Architectures/GCP-CPU-Benchmarks).
+CPU platform as a baseline ([Sysbench benchmark](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Reference-Architectures/GCP-CPU-Benchmarks)).
+
+Newer, similarly sized CPUs are supported and may have improved performance as a result. For Omnibus environments, ARM-based equivalents are also supported.
+
+NOTE:
+Any "burstable" instance types are not recommended due to inconsistent performance.
### Supported infrastructure
@@ -192,11 +194,11 @@ 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 Advanced Search](#configure-advanced-search) (optional) for faster,
- more advanced code search across your entire GitLab instance.
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.
The servers start on the same 10.6.0.0/24 private network range, and can
connect to each other freely on these addresses.
@@ -263,7 +265,7 @@ Configure your load balancer to pass connections on port 443 as `TCP` rather
than `HTTP(S)` protocol. This will pass the connection to the application node's
NGINX service untouched. NGINX will have the SSL certificate and listen on port 443.
-See the [NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
+See the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
for details on managing SSL certificates and configuring NGINX.
### Load balancer terminates SSL without backend SSL
@@ -274,7 +276,7 @@ terminating SSL.
Since communication between the load balancer and GitLab will not be secure,
there is some additional configuration needed. See the
-[NGINX proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#supporting-proxied-ssl)
+[proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-a-reverse-proxy-or-load-balancer-ssl-termination)
for details.
### Load balancer terminates SSL with backend SSL
@@ -286,8 +288,8 @@ end users will see.
Traffic will also be secure between the load balancers and NGINX in this
scenario. There is no need to add configuration for proxied SSL since the
connection will be secure all the way. However, configuration will need to be
-added to GitLab to configure SSL certificates. See
-[NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
+added to GitLab to configure SSL certificates. See the
+[HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
for details on managing SSL certificates and configuring NGINX.
### Readiness checks
@@ -2005,7 +2007,7 @@ On each node perform the following:
When you specify `https` in the `external_url`, as in the previous example,
GitLab expects that the SSL certificates are in `/etc/gitlab/ssl/`. If the
certificates aren't present, NGINX will fail to start. For more information, see
-the [NGINX documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
+the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
### GitLab Rails post-configuration
@@ -2128,7 +2130,6 @@ GitLab has been tested on a number of object storage providers:
- [Oracle Cloud Infrastructure](https://docs.cloud.oracle.com/en-us/iaas/Content/Object/Tasks/s3compatibleapi.htm)
- [OpenStack Swift (S3 compatibility mode)](https://docs.openstack.org/swift/latest/s3_compat.html)
- [Azure Blob storage](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction)
-- On-premises hardware and appliances from various storage vendors.
- MinIO. We have [a guide to deploying this](https://docs.gitlab.com/charts/advanced/external-object-storage/minio.html) within our Helm Chart documentation.
There are two ways of specifying object storage configuration in GitLab:
@@ -2138,25 +2139,13 @@ There are two ways of specifying object storage configuration in GitLab:
- [Storage-specific form](../object_storage.md#storage-specific-configuration): Every object defines its
own object storage [connection and configuration](../object_storage.md#connection-settings).
-Starting with GitLab 13.2, consolidated object storage configuration is available. It simplifies your GitLab configuration since the connection details are shared across object types. Refer to [Consolidated object storage configuration](../object_storage.md#consolidated-object-storage-configuration) guide for instructions on how to set it up.
-
-For configuring object storage in GitLab 13.1 and earlier, or for storage types not
-supported by consolidated configuration form, refer to the following guides based
-on what features you intend to use:
-
-|Object storage type|Supported by consolidated configuration?|
-|-------------------|----------------------------------------|
-| [Backups](../../raketasks/backup_gitlab.md#uploading-backups-to-a-remote-cloud-storage) | No |
-| [Job artifacts](../job_artifacts.md#using-object-storage) including archived job logs | Yes |
-| [LFS objects](../lfs/index.md#storing-lfs-objects-in-remote-object-storage) | Yes |
-| [Uploads](../uploads.md#using-object-storage) | Yes |
-| [Container Registry](../packages/container_registry.md#use-object-storage) (optional feature) | No |
-| [Merge request diffs](../merge_request_diffs.md#using-object-storage) | Yes |
-| [Mattermost](https://docs.mattermost.com/administration/config-settings.html#file-storage)| No |
-| [Packages](../packages/index.md#using-object-storage) (optional feature) | Yes |
-| [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature) | Yes |
-| [Autoscale runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional for improved performance) | No |
-| [Terraform state files](../terraform_state.md#using-object-storage) | Yes |
+The consolidated form is used in the following examples when available.
+
+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).
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.
@@ -2169,12 +2158,28 @@ in the future.
</a>
</div>
-## Enable incremental logging
+### Enable incremental logging
GitLab Runner returns job logs in chunks which Omnibus GitLab caches temporarily on disk in `/var/opt/gitlab/gitlab-ci/builds` by default, even when using consolidated object storage. With default configuration, this directory needs to be shared through NFS on any GitLab Rails and Sidekiq nodes.
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)
@@ -2191,22 +2196,6 @@ cluster alongside your instance, read how to
</a>
</div>
-## 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. Technical support is planned to be
-unavailable from GitLab 15.0. 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).
-
## Supported modifications for lower user counts (HA)
The 3k GitLab reference architecture is the smallest we recommend that achieves High Availability (HA).
@@ -2252,6 +2241,10 @@ to be complex. **This setup is only recommended** if you have strong working
knowledge and experience in Kubernetes. The rest of this
section assumes this.
+NOTE:
+**Gitaly Cluster is not supported to be run in Kubernetes**.
+Refer to [epic 6127](https://gitlab.com/groups/gitlab-org/-/epics/6127) for more details.
+
### Cluster topology
The following tables and diagram detail the hybrid environment using the same formats
@@ -2262,11 +2255,11 @@ use Google Cloud's Kubernetes Engine (GKE) or AWS Elastic Kubernetes Service (EK
and CPU requirements should translate to most other providers. We hope to update this in the
future with further specific cloud provider details.
-| Service | 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 |
-| 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 | 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 |
+| 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.
@@ -2276,24 +2269,24 @@ future with further specific cloud provider details.
Next are the backend components that run on static compute VMs via Omnibus (or External PaaS
services where applicable):
-| Service | Nodes | Configuration | GCP | AWS |
-|-------------------------------------------|----------------|-----------------------|-----------------|----------------|
-| Redis<sup>2</sup> | 3 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` |
-| Consul<sup>1</sup> + Sentinel<sup>2</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| PostgreSQL<sup>1</sup> | 3 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` |
-| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Internal load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Gitaly<sup>5</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` |
-| Praefect<sup>5</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Object storage<sup>4</sup> | Not applicable | Not applicable | Not applicable | Not applicable |
+| Service | Nodes | Configuration | GCP | AWS |
+|-------------------------------------------|-------|-----------------------|-----------------|-------------|
+| Redis<sup>2</sup> | 3 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` |
+| Consul<sup>1</sup> + Sentinel<sup>2</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| PostgreSQL<sup>1</sup> | 3 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` |
+| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| Internal load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| Gitaly<sup>5</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` |
+| Praefect<sup>5</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| Object storage<sup>4</sup> | - | - | - | - |
<!-- 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. [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. However, Amazon Aurora is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440), and Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for 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. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
-4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+4. Should be run on reputable Cloud Provider or Self Managed solutions. More information can be found in the [Configure the object storage](#configure-the-object-storage) section.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
<!-- markdownlint-enable MD029 -->
@@ -2308,11 +2301,11 @@ card "Kubernetes via Helm Charts" as kubernetes {
card "**External Load Balancer**" as elb #6a9be7
together {
- collections "**Webservice** x4" as gitlab #32CD32
- collections "**Sidekiq** x4" as sidekiq #ff8dd1
+ collections "**Webservice** x2" as gitlab #32CD32
+ collections "**Sidekiq** x3" as sidekiq #ff8dd1
}
- card "**Supporting Services**" as support
+ card "**Supporting Services** x2" as support
}
card "**Internal Load Balancer**" as ilb #9370DB
@@ -2375,29 +2368,42 @@ documents how to apply the calculated configuration to the Helm Chart.
#### Webservice
-Webservice pods typically need about 1 vCPU and 1.25 GB of memory _per worker_.
-Each Webservice pod consumes roughly 4 vCPUs and 5 GB of memory using
+Webservice pods typically need about 1 CPU and 1.25 GB of memory _per worker_.
+Each Webservice pod consumes roughly 4 CPUs and 5 GB of memory using
the [recommended topology](#cluster-topology) because four worker processes
are created by default and each pod has other small processes running.
For 3,000 users we recommend a total Puma worker count of around 16.
With the [provided recommendations](#cluster-topology) this allows the deployment of up to 4
Webservice pods with 4 workers per pod and 2 pods per node. Expand available resources using
-the ratio of 1 vCPU to 1.25 GB of memory _per each worker process_ for each additional
+the ratio of 1 CPU to 1.25 GB of memory _per each worker process_ for each additional
Webservice pod.
For further information on resource usage, see the [Webservice resources](https://docs.gitlab.com/charts/charts/gitlab/webservice/#resources).
#### Sidekiq
-Sidekiq pods should generally have 1 vCPU and 2 GB of memory.
+Sidekiq pods should generally have 0.9 CPU and 2 GB of memory.
[The provided starting point](#cluster-topology) allows the deployment of up to
-8 Sidekiq pods. Expand available resources using the 1 vCPU to 2GB memory
+8 Sidekiq pods. Expand available resources using the 0.9 CPU to 2 GB memory
ratio for each additional pod.
For further information on resource usage, see the [Sidekiq resources](https://docs.gitlab.com/charts/charts/gitlab/sidekiq/#resources).
+### Supporting
+
+The Supporting Node Pool is designed to house all supporting deployments that don't need to be
+on the Webservice and Sidekiq pools.
+
+This includes various deployments related to the Cloud Provider's implementation and supporting
+GitLab deployments such as NGINX or [GitLab Shell](https://docs.gitlab.com/charts/charts/gitlab/gitlab-shell/).
+
+If you wish to make any additional deployments, such as for Monitoring, it's recommended
+to deploy these in this pool where possible and not in the Webservice or Sidekiq pools, as the Supporting pool has been designed
+specifically to accommodate several additional deployments. However, if your deployments don't fit into the
+pool as given, you can increase the node pool accordingly.
+
<div align="right">
<a type="button" class="btn btn-default" href="#setup-components">
Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i>
diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md
index 535e483cb7d..bddec55ba71 100644
--- a/doc/administration/reference_architectures/50k_users.md
+++ b/doc/administration/reference_architectures/50k_users.md
@@ -18,30 +18,30 @@ full list of reference architectures, see
> - **Test requests per second (RPS) rates:** API: 1000 RPS, Web: 100 RPS, Git (Pull): 100 RPS, Git (Push): 20 RPS
> - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/50k)**
-| Service | Nodes | Configuration | GCP | AWS | Azure |
-|---------------------------------------------------|----------------|-------------------------|------------------|----------------|----------------|
-| External load balancing node<sup>3</sup> | 1 | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` | `c5.2xlarge` | `F8s v2` |
-| Consul<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| PostgreSQL<sup>1</sup> | 3 | 32 vCPU, 120 GB memory | `n1-standard-32` | `m5.8xlarge` | `D32s v3` |
-| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Internal load balancing node<sup>3</sup> | 1 | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` | `c5.2xlarge` | `F8s v2` |
-| Redis/Sentinel - Cache<sup>2</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| Redis/Sentinel - Persistent<sup>2</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| Gitaly<sup>5</sup> | 3 | 64 vCPU, 240 GB memory | `n1-standard-64` | `m5.16xlarge` | `D64s v3` |
-| Praefect<sup>5</sup> | 3 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
-| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Sidekiq | 4 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| GitLab Rails | 12 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` | `F32s v2` |
-| Monitoring node | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
-| Object storage<sup>4</sup> | Not applicable | Not applicable | Not applicable | Not applicable | Not applicable |
-| NFS server (non-Gitaly) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
+| Service | Nodes | Configuration | GCP | AWS | Azure |
+|------------------------------------------|-------|-------------------------|------------------|---------------|-----------|
+| External load balancing node<sup>3</sup> | 1 | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` | `c5.2xlarge` | `F8s v2` |
+| Consul<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| PostgreSQL<sup>1</sup> | 3 | 32 vCPU, 120 GB memory | `n1-standard-32` | `m5.8xlarge` | `D32s v3` |
+| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Internal load balancing node<sup>3</sup> | 1 | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` | `c5.2xlarge` | `F8s v2` |
+| Redis/Sentinel - Cache<sup>2</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
+| Redis/Sentinel - Persistent<sup>2</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
+| Gitaly<sup>5</sup> | 3 | 64 vCPU, 240 GB memory | `n1-standard-64` | `m5.16xlarge` | `D64s v3` |
+| Praefect<sup>5</sup> | 3 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
+| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Sidekiq | 4 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
+| GitLab Rails | 12 | 32 vCPU, 28.8 GB memory | `n1-highcpu-32` | `c5.9xlarge` | `F32s v2` |
+| Monitoring node | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
+| Object storage<sup>4</sup> | - | - | - | - | - |
+| NFS server (non-Gitaly) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
<!-- 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. [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. However, Amazon Aurora is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440), and Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for 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. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
-4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+4. Should be run on reputable Cloud Provider or Self Managed solutions. More information can be found in the [Configure the object storage](#configure-the-object-storage) section.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
<!-- markdownlint-enable MD029 -->
@@ -142,10 +142,12 @@ Before starting, you should take note of the following requirements / guidance f
This reference architecture was built and tested on Google Cloud Platform (GCP) using the
[Intel Xeon E5 v3 (Haswell)](https://cloud.google.com/compute/docs/cpu-platforms)
-CPU platform. On different hardware you may find that adjustments, either lower
-or higher, are required for your CPU or node counts. For more information, see
-our [Sysbench](https://github.com/akopytov/sysbench)-based
-[CPU benchmarks](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Reference-Architectures/GCP-CPU-Benchmarks).
+CPU platform as a baseline ([Sysbench benchmark](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Reference-Architectures/GCP-CPU-Benchmarks)).
+
+Newer, similarly sized CPUs are supported and may have improved performance as a result. For Omnibus environments, ARM-based equivalents are also supported.
+
+NOTE:
+Any "burstable" instance types are not recommended due to inconsistent performance.
### Supported infrastructure
@@ -186,10 +188,11 @@ 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.
-1. [Configure NFS](#configure-nfs)
- to have shared disk storage service for certain GitLab operations (non Gitaly or Object Storage).
The servers start on the same 10.6.0.0/24 private network range, and can
connect to each other freely on these addresses.
@@ -268,7 +271,7 @@ Configure your load balancer to pass connections on port 443 as `TCP` rather
than `HTTP(S)` protocol. This will pass the connection to the application node's
NGINX service untouched. NGINX will have the SSL certificate and listen on port 443.
-See the [NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
+See the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
for details on managing SSL certificates and configuring NGINX.
### Load balancer terminates SSL without backend SSL
@@ -279,7 +282,7 @@ terminating SSL.
Since communication between the load balancer and GitLab will not be secure,
there is some additional configuration needed. See the
-[NGINX proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#supporting-proxied-ssl)
+[proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-a-reverse-proxy-or-load-balancer-ssl-termination)
for details.
### Load balancer terminates SSL with backend SSL
@@ -291,8 +294,8 @@ end users will see.
Traffic will also be secure between the load balancers and NGINX in this
scenario. There is no need to add configuration for proxied SSL since the
connection will be secure all the way. However, configuration will need to be
-added to GitLab to configure SSL certificates. See
-[NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
+added to GitLab to configure SSL certificates. See the
+[HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
for details on managing SSL certificates and configuring NGINX.
### Readiness checks
@@ -2031,7 +2034,7 @@ On each node perform the following:
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
-1. If you're [using NFS](#configure-nfs):
+1. If you're [using NFS](#configure-nfs-optional):
1. If necessary, install the NFS client utility packages using the following
commands:
@@ -2090,7 +2093,7 @@ On each node perform the following:
When you specify `https` in the `external_url`, as in the previous example,
GitLab expects that the SSL certificates are in `/etc/gitlab/ssl/`. If the
certificates aren't present, NGINX will fail to start. For more information, see
-the [NGINX documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
+the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
### GitLab Rails post-configuration
@@ -2195,7 +2198,7 @@ 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) and in general it's better
+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.
@@ -2207,7 +2210,6 @@ GitLab has been tested on a number of object storage providers:
- [Oracle Cloud Infrastructure](https://docs.cloud.oracle.com/en-us/iaas/Content/Object/Tasks/s3compatibleapi.htm)
- [OpenStack Swift (S3 compatibility mode)](https://docs.openstack.org/swift/latest/s3_compat.html)
- [Azure Blob storage](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction)
-- On-premises hardware and appliances from various storage vendors.
- MinIO. We have [a guide to deploying this](https://docs.gitlab.com/charts/advanced/external-object-storage/minio.html) within our Helm Chart documentation.
There are two ways of specifying object storage configuration in GitLab:
@@ -2217,25 +2219,13 @@ There are two ways of specifying object storage configuration in GitLab:
- [Storage-specific form](../object_storage.md#storage-specific-configuration): Every object defines its
own object storage [connection and configuration](../object_storage.md#connection-settings).
-Starting with GitLab 13.2, consolidated object storage configuration is available. It simplifies your GitLab configuration since the connection details are shared across object types. Refer to [Consolidated object storage configuration](../object_storage.md#consolidated-object-storage-configuration) guide for instructions on how to set it up.
-
-For configuring object storage in GitLab 13.1 and earlier, or for storage types not
-supported by consolidated configuration form, refer to the following guides based
-on what features you intend to use:
-
-|Object storage type|Supported by consolidated configuration?|
-|-------------------|----------------------------------------|
-| [Backups](../../raketasks/backup_gitlab.md#uploading-backups-to-a-remote-cloud-storage) | No |
-| [Job artifacts](../job_artifacts.md#using-object-storage) including archived job logs | Yes |
-| [LFS objects](../lfs/index.md#storing-lfs-objects-in-remote-object-storage) | Yes |
-| [Uploads](../uploads.md#using-object-storage) | Yes |
-| [Container Registry](../packages/container_registry.md#use-object-storage) (optional feature) | No |
-| [Merge request diffs](../merge_request_diffs.md#using-object-storage) | Yes |
-| [Mattermost](https://docs.mattermost.com/administration/config-settings.html#file-storage)| No |
-| [Packages](../packages/index.md#using-object-storage) (optional feature) | Yes |
-| [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature) | Yes |
-| [Autoscale runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional for improved performance) | No |
-| [Terraform state files](../terraform_state.md#using-object-storage) | Yes |
+The consolidated form is used in the following examples when available.
+
+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).
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.
@@ -2248,12 +2238,28 @@ in the future.
</a>
</div>
-## Enable incremental logging
+### Enable incremental logging
GitLab Runner returns job logs in chunks which Omnibus GitLab caches temporarily on disk in `/var/opt/gitlab/gitlab-ci/builds` by default, even when using consolidated object storage. With default configuration, this directory needs to be shared through NFS on any GitLab Rails and Sidekiq nodes.
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)
@@ -2270,22 +2276,6 @@ cluster alongside your instance, read how to
</a>
</div>
-## Configure NFS
-
-[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. Technical support is planned to be
-unavailable from GitLab 15.0. 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).
-
## Cloud Native Hybrid reference architecture with Helm Charts (alternative)
As an alternative approach, you can also run select components of GitLab as Cloud Native
@@ -2306,6 +2296,10 @@ to be complex. **This setup is only recommended** if you have strong working
knowledge and experience in Kubernetes. The rest of this
section assumes this.
+NOTE:
+**Gitaly Cluster is not supported to be run in Kubernetes**.
+Refer to [epic 6127](https://gitlab.com/groups/gitlab-org/-/epics/6127) for more details.
+
### Cluster topology
The following tables and diagram detail the hybrid environment using the same formats
@@ -2316,11 +2310,11 @@ use Google Cloud's Kubernetes Engine (GKE) or AWS Elastic Kubernetes Service (EK
and CPU requirements should translate to most other providers. We hope to update this in the
future with further specific cloud provider details.
-| Service | 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 |
-| Supporting services such as NGINX, Prometheus | 2 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | 7.75 vCPU, 25 GB memory |
+| Service | 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 |
+| Supporting services | 2 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | 7.75 vCPU, 25 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.
@@ -2330,25 +2324,25 @@ future with further specific cloud provider details.
Next are the backend components that run on static compute VMs via Omnibus (or External PaaS
services where applicable):
-| Service | Nodes | Configuration | GCP | AWS |
-|------------------------------------------|----------------|------------------------|------------------|----------------|
-| Consul<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| PostgreSQL<sup>1</sup> | 3 | 32 vCPU, 120 GB memory | `n1-standard-32` | `m5.8xlarge` |
-| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Internal load balancing node<sup>3</sup> | 1 | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` | `c5.2xlarge` |
-| 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</sup> | 3 | 64 vCPU, 240 GB memory | `n1-standard-64` | `m5.16xlarge` |
-| Praefect<sup>5</sup> | 3 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` |
-| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Object storage<sup>4</sup> | Not applicable | Not applicable | Not applicable | Not applicable |
+| Service | Nodes | Configuration | GCP | AWS |
+|------------------------------------------|-------|------------------------|------------------|---------------|
+| Consul<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| PostgreSQL<sup>1</sup> | 3 | 32 vCPU, 120 GB memory | `n1-standard-32` | `m5.8xlarge` |
+| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| Internal load balancing node<sup>3</sup> | 1 | 8 vCPU, 7.2 GB memory | `n1-highcpu-8` | `c5.2xlarge` |
+| 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</sup> | 3 | 64 vCPU, 240 GB memory | `n1-standard-64` | `m5.16xlarge` |
+| Praefect<sup>5</sup> | 3 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` |
+| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| Object storage<sup>4</sup> | - | - | - | - |
<!-- 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. [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. However, Amazon Aurora is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440), and Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for 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. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
-4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+4. Should be run on reputable Cloud Provider or Self Managed solutions. More information can be found in the [Configure the object storage](#configure-the-object-storage) section.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
<!-- markdownlint-enable MD029 -->
@@ -2363,11 +2357,11 @@ card "Kubernetes via Helm Charts" as kubernetes {
card "**External Load Balancer**" as elb #6a9be7
together {
- collections "**Webservice** x4" as gitlab #32CD32
+ collections "**Webservice** x16" as gitlab #32CD32
collections "**Sidekiq** x4" as sidekiq #ff8dd1
}
- card "**Supporting Services**" as support
+ card "**Supporting Services** x2" as support
}
card "**Internal Load Balancer**" as ilb #9370DB
@@ -2433,29 +2427,42 @@ documents how to apply the calculated configuration to the Helm Chart.
#### Webservice
-Webservice pods typically need about 1 vCPU and 1.25 GB of memory _per worker_.
-Each Webservice pod consumes roughly 4 vCPUs and 5 GB of memory using
+Webservice pods typically need about 1 CPU and 1.25 GB of memory _per worker_.
+Each Webservice pod consumes roughly 4 CPUs and 5 GB of memory using
the [recommended topology](#cluster-topology) because four worker processes
are created by default and each pod has other small processes running.
For 50,000 users we recommend a total Puma worker count of around 320.
With the [provided recommendations](#cluster-topology) this allows the deployment of up to 80
Webservice pods with 4 workers per pod and 5 pods per node. Expand available resources using
-the ratio of 1 vCPU to 1.25 GB of memory _per each worker process_ for each additional
+the ratio of 1 CPU to 1.25 GB of memory _per each worker process_ for each additional
Webservice pod.
For further information on resource usage, see the [Webservice resources](https://docs.gitlab.com/charts/charts/gitlab/webservice/#resources).
#### Sidekiq
-Sidekiq pods should generally have 1 vCPU and 2 GB of memory.
+Sidekiq pods should generally have 0.9 CPU and 2 GB of memory.
[The provided starting point](#cluster-topology) allows the deployment of up to
-14 Sidekiq pods. Expand available resources using the 1 vCPU to 2GB memory
+14 Sidekiq pods. Expand available resources using the 0.9 CPU to 2 GB memory
ratio for each additional pod.
For further information on resource usage, see the [Sidekiq resources](https://docs.gitlab.com/charts/charts/gitlab/sidekiq/#resources).
+### Supporting
+
+The Supporting Node Pool is designed to house all supporting deployments that don't need to be
+on the Webservice and Sidekiq pools.
+
+This includes various deployments related to the Cloud Provider's implementation and supporting
+GitLab deployments such as NGINX or [GitLab Shell](https://docs.gitlab.com/charts/charts/gitlab/gitlab-shell/).
+
+If you wish to make any additional deployments, such as for Monitoring, it's recommended
+to deploy these in this pool where possible and not in the Webservice or Sidekiq pools, as the Supporting pool has been designed
+specifically to accommodate several additional deployments. However, if your deployments don't fit into the
+pool as given, you can increase the node pool accordingly.
+
<div align="right">
<a type="button" class="btn btn-default" href="#setup-components">
Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i>
diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md
index e19440e7660..0e599df7c1f 100644
--- a/doc/administration/reference_architectures/5k_users.md
+++ b/doc/administration/reference_architectures/5k_users.md
@@ -25,29 +25,29 @@ costly-to-operate environment by using the
> - **Test requests per second (RPS) rates:** API: 100 RPS, Web: 10 RPS, Git (Pull): 10 RPS, Git (Push): 2 RPS
> - **[Latest Results](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/5k)**
-| Service | Nodes | Configuration | GCP | AWS | Azure |
-|--------------------------------------------|----------------|-------------------------|-----------------|----------------|----------------|
-| External load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Redis<sup>2</sup> | 3 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | `D2s v3` |
-| Consul<sup>1</sup> + Sentinel<sup>2</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| PostgreSQL<sup>1</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
-| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Internal load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Gitaly<sup>5</sup> | 3 | 8 vCPU, 30 GB memory | `n1-standard-8` | `m5.2xlarge` | `D8s v3` |
-| Praefect<sup>5</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Sidekiq | 4 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | `D2s v3` |
-| GitLab Rails | 3 | 16 vCPU, 14.4 GB memory | `n1-highcpu-16` | `c5.4xlarge` | `F16s v2` |
-| Monitoring node | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
-| Object storage<sup>4</sup> | Not applicable | Not applicable | Not applicable | Not applicable | Not applicable |
-| NFS server (non-Gitaly) | 1 | 4 vCPU, 3.6 GB memory | `n1-highcpu-4` | `c5.xlarge` | `F4s v2` |
+| Service | Nodes | Configuration | GCP | AWS | Azure |
+|-------------------------------------------|-------|-------------------------|-----------------|--------------|----------|
+| External load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Redis<sup>2</sup> | 3 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | `D2s v3` |
+| Consul<sup>1</sup> + Sentinel<sup>2</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| PostgreSQL<sup>1</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` | `D4s v3` |
+| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Internal load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Gitaly<sup>5</sup> | 3 | 8 vCPU, 30 GB memory | `n1-standard-8` | `m5.2xlarge` | `D8s v3` |
+| Praefect<sup>5</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` | `F2s v2` |
+| Sidekiq | 4 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` | `D2s v3` |
+| GitLab Rails | 3 | 16 vCPU, 14.4 GB memory | `n1-highcpu-16` | `c5.4xlarge` | `F16s 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` |
<!-- 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. [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. However, Amazon Aurora is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440), and Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for 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. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
-4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+4. Should be run on reputable Cloud Provider or Self Managed solutions. More information can be found in the [Configure the object storage](#configure-the-object-storage) section.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
<!-- markdownlint-enable MD029 -->
@@ -145,10 +145,12 @@ Before starting, you should take note of the following requirements / guidance f
This reference architecture was built and tested on Google Cloud Platform (GCP) using the
[Intel Xeon E5 v3 (Haswell)](https://cloud.google.com/compute/docs/cpu-platforms)
-CPU platform. On different hardware you may find that adjustments, either lower
-or higher, are required for your CPU or node counts. For more information, see
-our [Sysbench](https://github.com/akopytov/sysbench)-based
-[CPU benchmarks](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Reference-Architectures/GCP-CPU-Benchmarks).
+CPU platform as a baseline ([Sysbench benchmark](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Reference-Architectures/GCP-CPU-Benchmarks)).
+
+Newer, similarly sized CPUs are supported and may have improved performance as a result. For Omnibus environments, ARM-based equivalents are also supported.
+
+NOTE:
+Any "burstable" instance types are not recommended due to inconsistent performance.
### Supported infrastructure
@@ -189,12 +191,11 @@ 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 Advanced Search](#configure-advanced-search) (optional) for faster,
- more advanced code search across your entire GitLab instance.
1. [Configure NFS](#configure-nfs-optional) (optional, and not recommended)
to have shared disk storage service as an alternative to Gitaly or object
- storage. You can skip this step if you're not using GitLab Pages (which
- requires NFS).
+ storage.
+1. [Configure Advanced Search](#configure-advanced-search) (optional) for faster,
+ more advanced code search across your entire GitLab instance.
The servers start on the same 10.6.0.0/24 private network range, and can
connect to each other freely on these addresses.
@@ -261,7 +262,7 @@ Configure your load balancer to pass connections on port 443 as `TCP` rather
than `HTTP(S)` protocol. This passes the connection to the application node's
NGINX service untouched. NGINX has the SSL certificate and listen on port 443.
-See the [NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
+See the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
for details on managing SSL certificates and configuring NGINX.
### Load balancer terminates SSL without backend SSL
@@ -272,7 +273,7 @@ terminating SSL.
Since communication between the load balancer and GitLab is not secure,
there is some additional configuration needed. See the
-[NGINX proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#supporting-proxied-ssl)
+[proxied SSL documentation](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-a-reverse-proxy-or-load-balancer-ssl-termination)
for details.
### Load balancer terminates SSL with backend SSL
@@ -284,8 +285,8 @@ end users see.
Traffic is also secure between the load balancers and NGINX in this
scenario. There is no need to add configuration for proxied SSL since the
connection is secure all the way. However, configuration needs to be
-added to GitLab to configure SSL certificates. See
-[NGINX HTTPS documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
+added to GitLab to configure SSL certificates. See the
+[HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html)
for details on managing SSL certificates and configuring NGINX.
### Readiness checks
@@ -2005,7 +2006,7 @@ On each node perform the following:
When you specify `https` in the `external_url`, as in the previous example,
GitLab expects that the SSL certificates are in `/etc/gitlab/ssl/`. If the
certificates aren't present, NGINX fails to start. For more information, see
-the [NGINX documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
+the [HTTPS documentation](https://docs.gitlab.com/omnibus/settings/ssl.html).
### GitLab Rails post-configuration
@@ -2128,7 +2129,6 @@ GitLab has been tested on a number of object storage providers:
- [Oracle Cloud Infrastructure](https://docs.cloud.oracle.com/en-us/iaas/Content/Object/Tasks/s3compatibleapi.htm)
- [OpenStack Swift (S3 compatibility mode)](https://docs.openstack.org/swift/latest/s3_compat.html)
- [Azure Blob storage](https://docs.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction)
-- On-premises hardware and appliances from various storage vendors.
- MinIO. We have [a guide to deploying this](https://docs.gitlab.com/charts/advanced/external-object-storage/minio.html) within our Helm Chart documentation.
There are two ways of specifying object storage configuration in GitLab:
@@ -2138,25 +2138,13 @@ There are two ways of specifying object storage configuration in GitLab:
- [Storage-specific form](../object_storage.md#storage-specific-configuration): Every object defines its
own object storage [connection and configuration](../object_storage.md#connection-settings).
-Starting with GitLab 13.2, consolidated object storage configuration is available. It simplifies your GitLab configuration since the connection details are shared across object types. Refer to [Consolidated object storage configuration](../object_storage.md#consolidated-object-storage-configuration) guide for instructions on how to set it up.
-
-For configuring object storage in GitLab 13.1 and earlier, or for storage types not
-supported by consolidated configuration form, refer to the following guides based
-on what features you intend to use:
-
-|Object storage type|Supported by consolidated configuration?|
-|-------------------|----------------------------------------|
-| [Backups](../../raketasks/backup_gitlab.md#uploading-backups-to-a-remote-cloud-storage) | No |
-| [Job artifacts](../job_artifacts.md#using-object-storage) including archived job logs | Yes |
-| [LFS objects](../lfs/index.md#storing-lfs-objects-in-remote-object-storage) | Yes |
-| [Uploads](../uploads.md#using-object-storage) | Yes |
-| [Container Registry](../packages/container_registry.md#use-object-storage) (optional feature) | No |
-| [Merge request diffs](../merge_request_diffs.md#using-object-storage) | Yes |
-| [Mattermost](https://docs.mattermost.com/administration/config-settings.html#file-storage)| No |
-| [Packages](../packages/index.md#using-object-storage) (optional feature) | Yes |
-| [Dependency Proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature) | Yes |
-| [Autoscale runner caching](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) (optional for improved performance) | No |
-| [Terraform state files](../terraform_state.md#using-object-storage) | Yes |
+The consolidated form is used in the following examples when available.
+
+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).
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.
@@ -2169,12 +2157,28 @@ in the future.
</a>
</div>
-## Enable incremental logging
+### Enable incremental logging
GitLab Runner returns job logs in chunks which Omnibus GitLab caches temporarily on disk in `/var/opt/gitlab/gitlab-ci/builds` by default, even when using consolidated object storage. With default configuration, this directory needs to be shared through NFS on any GitLab Rails and Sidekiq nodes.
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)
@@ -2191,22 +2195,6 @@ cluster alongside your instance, read how to
</a>
</div>
-## 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. Technical support is planned to be
-unavailable from GitLab 15.0. 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).
-
## Cloud Native Hybrid reference architecture with Helm Charts (alternative)
As an alternative approach, you can also run select components of GitLab as Cloud Native
@@ -2227,6 +2215,10 @@ to be complex. **This setup is only recommended** if you have strong working
knowledge and experience in Kubernetes. The rest of this
section assumes this.
+NOTE:
+**Gitaly Cluster is not supported to be run in Kubernetes**.
+Refer to [epic 6127](https://gitlab.com/groups/gitlab-org/-/epics/6127) for more details.
+
### Cluster topology
The following tables and diagram detail the hybrid environment using the same formats
@@ -2251,24 +2243,24 @@ future with further specific cloud provider details.
Next are the backend components that run on static compute VMs via Omnibus (or External PaaS
services where applicable):
-| Service | Nodes | Configuration | GCP | AWS |
-|-------------------------------------------|----------------|-----------------------|-----------------|----------------|
-| Redis<sup>2</sup> | 3 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` |
-| Consul<sup>1</sup> + Sentinel<sup>2</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| PostgreSQL<sup>1</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` |
-| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Internal load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Gitaly<sup>5</sup> | 3 | 8 vCPU, 30 GB memory | `n1-standard-8` | `m5.2xlarge` |
-| Praefect<sup>5</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
-| Object storage<sup>4</sup> | Not applicable | Not applicable | Not applicable | Not applicable |
+| Service | Nodes | Configuration | GCP | AWS |
+|-------------------------------------------|-------|-----------------------|-----------------|--------------|
+| Redis<sup>2</sup> | 3 | 2 vCPU, 7.5 GB memory | `n1-standard-2` | `m5.large` |
+| Consul<sup>1</sup> + Sentinel<sup>2</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| PostgreSQL<sup>1</sup> | 3 | 4 vCPU, 15 GB memory | `n1-standard-4` | `m5.xlarge` |
+| PgBouncer<sup>1</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| Internal load balancing node<sup>3</sup> | 1 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| Gitaly<sup>5</sup> | 3 | 8 vCPU, 30 GB memory | `n1-standard-8` | `m5.2xlarge` |
+| Praefect<sup>5</sup> | 3 | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| Praefect PostgreSQL<sup>1</sup> | 1+ | 2 vCPU, 1.8 GB memory | `n1-highcpu-2` | `c5.large` |
+| Object storage<sup>4</sup> | - | - | - | - |
<!-- 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. [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. However, Amazon Aurora is **incompatible** with load balancing enabled by default in [14.4.0](../../update/index.md#1440), and Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for 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. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
-4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
+4. Should be run on reputable Cloud Provider or Self Managed solutions. More information can be found in the [Configure the object storage](#configure-the-object-storage) section.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
<!-- markdownlint-enable MD029 -->
@@ -2283,11 +2275,11 @@ card "Kubernetes via Helm Charts" as kubernetes {
card "**External Load Balancer**" as elb #6a9be7
together {
- collections "**Webservice** x4" as gitlab #32CD32
- collections "**Sidekiq** x4" as sidekiq #ff8dd1
+ collections "**Webservice** x5" as gitlab #32CD32
+ collections "**Sidekiq** x3" as sidekiq #ff8dd1
}
- card "**Supporting Services**" as support
+ card "**Supporting Services** x2" as support
}
card "**Internal Load Balancer**" as ilb #9370DB
@@ -2350,29 +2342,42 @@ documents how to apply the calculated configuration to the Helm Chart.
#### Webservice
-Webservice pods typically need about 1 vCPU and 1.25 GB of memory _per worker_.
-Each Webservice pod consumes roughly 4 vCPUs and 5 GB of memory using
+Webservice pods typically need about 1 CPU and 1.25 GB of memory _per worker_.
+Each Webservice pod consumes roughly 4 CPUs and 5 GB of memory using
the [recommended topology](#cluster-topology) because four worker processes
are created by default and each pod has other small processes running.
For 5,000 users we recommend a total Puma worker count of around 40.
With the [provided recommendations](#cluster-topology) this allows the deployment of up to 10
Webservice pods with 4 workers per pod and 2 pods per node. Expand available resources using
-the ratio of 1 vCPU to 1.25 GB of memory _per each worker process_ for each additional
+the ratio of 1 CPU to 1.25 GB of memory _per each worker process_ for each additional
Webservice pod.
For further information on resource usage, see the [Webservice resources](https://docs.gitlab.com/charts/charts/gitlab/webservice/#resources).
#### Sidekiq
-Sidekiq pods should generally have 1 vCPU and 2 GB of memory.
+Sidekiq pods should generally have 0.9 CPU and 2 GB of memory.
[The provided starting point](#cluster-topology) allows the deployment of up to
-8 Sidekiq pods. Expand available resources using the 1 vCPU to 2GB memory
+8 Sidekiq pods. Expand available resources using the 0.9 CPU to 2 GB memory
ratio for each additional pod.
For further information on resource usage, see the [Sidekiq resources](https://docs.gitlab.com/charts/charts/gitlab/sidekiq/#resources).
+### Supporting
+
+The Supporting Node Pool is designed to house all supporting deployments that don't need to be
+on the Webservice and Sidekiq pools.
+
+This includes various deployments related to the Cloud Provider's implementation and supporting
+GitLab deployments such as NGINX or [GitLab Shell](https://docs.gitlab.com/charts/charts/gitlab/gitlab-shell/).
+
+If you wish to make any additional deployments, such as for Monitoring, it's recommended
+to deploy these in this pool where possible and not in the Webservice or Sidekiq pools, as the Supporting pool has been designed
+specifically to accommodate several additional deployments. However, if your deployments don't fit into the
+pool as given, you can increase the node pool accordingly.
+
<div align="right">
<a type="button" class="btn btn-default" href="#setup-components">
Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i>
diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md
index a97c8611239..4615499d6fa 100644
--- a/doc/administration/repository_checks.md
+++ b/doc/administration/repository_checks.md
@@ -20,7 +20,7 @@ committed to a repository. GitLab administrators can:
To check a project's repository using GitLab UI:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Projects**.
1. Select the project to check.
1. In the **Repository check** section, select **Trigger repository check**.
@@ -32,7 +32,7 @@ project page in the Admin Area. If the checks fail, see [what to do](#what-to-do
Instead of checking repositories manually, GitLab can be configured to run the checks periodically:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Repository** (`/admin/application_settings/repository`).
1. Expand the **Repository maintenance** section.
1. Enable **Enable repository checks**.
@@ -68,7 +68,7 @@ You can run [`git fsck`](https://git-scm.com/docs/git-fsck) using the command li
1. Run the check. For example:
```shell
- sudo /opt/gitlab/embedded/bin/git -C /var/opt/gitlab/git-data/repositories/@hashed/0b/91/0b91...f9.git fsck
+ sudo -u git /opt/gitlab/embedded/bin/git -C /var/opt/gitlab/git-data/repositories/@hashed/0b/91/0b91...f9.git fsck
```
## What to do if a check failed
@@ -81,7 +81,15 @@ If a repository check fails, locate the error in the [`repocheck.log` file](logs
If periodic repository checks cause false alarms, you can clear all repository check states:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Repository** (`/admin/application_settings/repository`).
1. Expand the **Repository maintenance** section.
1. Select **Clear all repository checks**.
+
+### Error: `failed to parse commit <commit SHA> from object database for commit-graph`
+
+You can see a `failed to parse commit <commit SHA> from object database for commit-graph` error in repository check logs. This error occurs if your `commit-graph` cache is out
+of date. The `commit-graph` cache is an auxiliary cache and is not required for regular Git operations.
+
+While the message can be safely ignored, see the issue [error: Could not read from object database for commit-graph](https://gitlab.com/gitlab-org/gitaly/-/issues/2359)
+for more details.
diff --git a/doc/administration/repository_storage_paths.md b/doc/administration/repository_storage_paths.md
index f001ba35bca..482cbd97e37 100644
--- a/doc/administration/repository_storage_paths.md
+++ b/doc/administration/repository_storage_paths.md
@@ -146,7 +146,7 @@ Omnibus stores the repositories in a `repositories` subdirectory of the `git-dat
After you [configure](#configure-repository-storage-paths) multiple repository storage paths, you
can choose where new repositories are stored:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Repository** and expand the **Repository storage**
section.
1. Enter values in the **Storage nodes for new repositories** fields.
diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md
index 7ce629b5d71..865daba9e89 100644
--- a/doc/administration/repository_storage_types.md
+++ b/doc/administration/repository_storage_types.md
@@ -79,7 +79,7 @@ Administrators can look up a project's hashed path from its name or ID using:
To look up a project's hash path in the Admin Area:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Projects** and select the project.
The **Gitaly relative path** is displayed there and looks similar to:
diff --git a/doc/administration/restart_gitlab.md b/doc/administration/restart_gitlab.md
index 6625039504a..e5ec12054b8 100644
--- a/doc/administration/restart_gitlab.md
+++ b/doc/administration/restart_gitlab.md
@@ -102,7 +102,7 @@ depend on those files.
## Installations from source
-If you have followed the official installation guide to
+If you have followed the official installation guide to
[install GitLab from source](../install/installation.md), run the following command to restart GitLab:
```shell
diff --git a/doc/administration/server_hooks.md b/doc/administration/server_hooks.md
index 1a47dc4ccf2..4148abf1bdc 100644
--- a/doc/administration/server_hooks.md
+++ b/doc/administration/server_hooks.md
@@ -30,7 +30,7 @@ alternatives to server hooks include:
To create server hooks for a repository:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. Go to **Overview > Projects** and select the project you want to add a server hook to.
1. On the page that appears, locate the value of **Gitaly relative path**. This path is where server hooks must be located.
- If you are using [hashed storage](repository_storage_types.md#hashed-storage), see
@@ -56,8 +56,7 @@ If the server hook code is properly implemented, it should execute when the Git
## Create global server hooks for all repositories
-To create a Git hook that applies to all repositories, set a global server hook. The default global server hook directory
-is in the GitLab Shell directory. Any server hook added there applies to all repositories, including:
+To create a Git hook that applies to all repositories, set a global server hook. Global server hooks also apply to:
- [Project and group wiki](../user/project/wiki/index.md) repositories. Their storage directory names are in the format
`<id>.wiki.git`.
@@ -66,36 +65,36 @@ is in the GitLab Shell directory. Any server hook added there applies to all rep
### Choose a server hook directory
-Before creating a global server hook, you must choose a directory for it. The default global server hook directory:
+Before creating a global server hook, you must choose a directory for it.
-- For Omnibus GitLab installations is usually `/opt/gitlab/embedded/service/gitlab-shell/hooks`.
-- For an installation from source is usually `/home/git/gitlab-shell/hooks`.
+For Omnibus GitLab installations, the directory is set in `gitlab.rb` under `gitaly['custom_hooks_dir']`. You can either:
-To use a different directory for global server hooks, set `custom_hooks_dir` in Gitaly configuration:
+- Use the default suggestion of the `/var/opt/gitlab/gitaly/custom_hooks` directory by uncommenting it.
+- Add your own setting.
-- For Omnibus installations, set in `gitlab.rb`.
-- For source installations, the configuration location depends on the GitLab version. For:
- - GitLab 13.0 and earlier, set in `gitlab-shell/config.yml`.
- - GitLab 13.1 and later, set in `gitaly/config.toml` under the `[hooks]` section. However, GitLab honors the
- `custom_hooks_dir` value in `gitlab-shell/config.yml` if the value in `gitaly/config.toml` is blank or non-existent.
+For installations from source:
+
+- The directory is set in a configuration file. The location of the configuration file depends on the GitLab version:
+ - For GitLab 13.1 and later, the directory is set in `gitaly/config.toml` under the `[hooks]` section. However,
+ GitLab honors the `custom_hooks_dir` value in `gitlab-shell/config.yml` if the value in `gitaly/config.toml` is blank
+ or non-existent.
+ - For GitLab 13.0 and earlier, the directory set in `gitlab-shell/config.yml`.
+- The default directory is `/home/git/gitlab-shell/hooks`.
### Create the global server hook
To create a global server hook for all repositories:
1. On the GitLab server, go to the configured global server hook directory.
-1. In the configured global server hook directory:
- - To create a single server hook, create a file with a name that matches the hook type. For example, for a
- `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. Inside this new directory, add your server hook. Server hooks can be in any programming language. Ensure the
+1. In the configured global server hook directory, 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`.
+1. Inside this new directory, add your server hooks. 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
pattern (`*~`).
-If the server hook code is properly implemented, it should execute when the Git hook is next triggered.
+If the server hook code is properly implemented, it should execute when the Git hook is next triggered. Hooks are executed in alphabetical order by filename in the hook type
+subdirectories.
## Chained server hooks
diff --git a/doc/administration/sidekiq/extra_sidekiq_processes.md b/doc/administration/sidekiq/extra_sidekiq_processes.md
index 1cd3771c94d..b774b0d3e14 100644
--- a/doc/administration/sidekiq/extra_sidekiq_processes.md
+++ b/doc/administration/sidekiq/extra_sidekiq_processes.md
@@ -95,7 +95,7 @@ To start multiple processes:
To view the Sidekiq processes in GitLab:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
## Negate settings
diff --git a/doc/administration/static_objects_external_storage.md b/doc/administration/static_objects_external_storage.md
index 21949388f19..a288b19f164 100644
--- a/doc/administration/static_objects_external_storage.md
+++ b/doc/administration/static_objects_external_storage.md
@@ -16,7 +16,7 @@ storage such as a content delivery network (CDN).
To configure external storage for static objects:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Repository**.
1. Expand the **External storage for repository static objects** section.
1. Enter the base URL and an arbitrary token. When you [set up external storage](#set-up-external-storage),
diff --git a/doc/administration/system_hooks.md b/doc/administration/system_hooks.md
index cb141a18ae6..0d70744e942 100644
--- a/doc/administration/system_hooks.md
+++ b/doc/administration/system_hooks.md
@@ -56,7 +56,7 @@ for Push and Tag events, but we never display commits.
To create a system hook:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **System Hooks**.
1. Provide the **URL** and **Secret Token**.
1. Select the checkbox next to each optional **Trigger** you want to enable.
diff --git a/doc/administration/terraform_state.md b/doc/administration/terraform_state.md
index 7a8d7774948..5a272025987 100644
--- a/doc/administration/terraform_state.md
+++ b/doc/administration/terraform_state.md
@@ -78,8 +78,8 @@ Terraform state files are stored locally, follow the steps below.
## Using object storage **(FREE SELF)**
-Instead of storing Terraform state files on disk, we recommend the use of
-[one of the supported object storage options](object_storage.md#options).
+Instead of storing Terraform state files on disk, we recommend the use of
+[one of the supported object storage options](object_storage.md#options).
This configuration relies on valid credentials to be configured already.
[Read more about using object storage with GitLab](object_storage.md).
diff --git a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
index aa4dbec4f95..40bb15ecb70 100644
--- a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
+++ b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
@@ -28,22 +28,6 @@ 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.
-## Find specific methods for an object
-
-```ruby
-Array.methods.select { |m| m.to_s.include? "sing" }
-Array.methods.grep(/sing/)
-```
-
-## Find method source
-
-```ruby
-instance_of_object.method(:foo).source_location
-
-# Example for when we would call project.private?
-project.method(:private?).source_location
-```
-
## Attributes
View available attributes, formatted using pretty print (`pp`).
@@ -78,28 +62,6 @@ Notify.test_email(e, "Test email for #{n}", 'Test email').deliver_now
Notify.test_email(u.email, "Test email for #{u.name}", 'Test email').deliver_now
```
-## Limiting output
-
-Adding a semicolon(`;`) and a follow-up statement at the end of a statement prevents the default implicit return output. This can be used if you are already explicitly printing details and potentially have a lot of return output:
-
-```ruby
-puts ActiveRecord::Base.descendants; :ok
-Project.select(&:pages_deployed?).each {|p| puts p.pages_url }; true
-```
-
-## Get or store the result of last operation
-
-Underscore(`_`) represents the implicit return of the previous statement. You can use this to quickly assign a variable from the output of the previous command:
-
-```ruby
-Project.last
-# => #<Project id:2537 root/discard>>
-project = _
-# => #<Project id:2537 root/discard>>
-project.id
-# => 2537
-```
-
## Open object in `irb`
Sometimes it is easier to go through a method if you are in the context of the object. You can shim into the namespace of `Object` to let you open `irb` in the context of any object:
@@ -115,15 +77,6 @@ irb(#<Project>)> web_url
# => "https://gitlab-example/root/discard"
```
-## Query the database using an ActiveRecord Model
-
-```ruby
-m = Model.where('attribute like ?', 'ex%')
-
-# for example to query the projects
-projects = Project.where('path like ?', 'Oumua%')
-```
-
## View all keys in cache
```ruby
@@ -166,18 +119,6 @@ Benchmark.bm do |x|
end
```
-## Feature flags
-
-### Show all feature flags that are enabled
-
-```ruby
-# Regular output
-Feature.all
-
-# Nice output
-Feature.all.map {|f| [f.name, f.state]}
-```
-
## Projects
### Clear a project's cache
@@ -813,30 +754,6 @@ subgroup.members.map(&:errors).map(&:full_messages)
subgroup.members_and_requesters.map(&:errors).map(&:full_messages)
```
-## Authentication
-
-### Re-enable standard web sign-in form
-
-Re-enable the standard username and password-based sign-in form if it was disabled as a [Sign-in restriction](../../user/admin_area/settings/sign_in_restrictions.md#password-authentication-enabled).
-
-You can use this method when a configured external authentication provider (through SSO or an LDAP configuration) is facing an outage and direct sign-in access to GitLab is required.
-
-```ruby
-Gitlab::CurrentSettings.update!(password_authentication_enabled_for_web: true)
-```
-
-## SCIM
-
-### Find groups using an SQL query
-
-Find and store an array of groups based on an SQL query:
-
-```ruby
-# Finds groups and subgroups that end with '%oup'
-Group.find_by_sql("SELECT * FROM namespaces WHERE name LIKE '%oup'")
-=> [#<Group id:3 @test-group>, #<Group id:4 @template-group/template-subgroup>]
-```
-
## Routes
### Remove redirecting routes
@@ -1173,6 +1090,38 @@ This content has been converted to a Rake task, see [verify database values can
## Geo
+### Reverify all uploads (or any SSF data type which is verified)
+
+1. SSH into a GitLab Rails node in the primary Geo site.
+1. Open [Rails console](../operations/rails_console.md).
+1. Mark all uploads as "pending verification":
+
+ ```ruby
+ Upload.verification_state_table_class.each_batch do |relation|
+ relation.update_all(verification_state: 0)
+ end
+ ```
+
+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.
+
+A similar thing can be done for all Models handled by the [Geo Self-Service Framework](../../development/geo/framework.md) which have implemented verification:
+
+- `LfsObject`
+- `MergeRequestDiff`
+- `Packages::PackageFile`
+- `Terraform::StateVersion`
+- `SnippetRepository`
+- `Ci::PipelineArtifact`
+- `PagesDeployment`
+- `Upload`
+- `Ci::JobArtifact`
+- `Ci::SecureFile`
+
+NOTE:
+`GroupWikiRepository` is not in the previous list since verification is not implemented.
+There is an [issue to implement this functionality in the Admin UI](https://gitlab.com/gitlab-org/gitlab/-/issues/364729).
+
### Artifacts
#### Find failed artifacts
@@ -1362,54 +1311,3 @@ Prints the metrics saved in `conversational_development_index_metrics`.
```shell
rake gitlab:usage_data:generate_and_send
```
-
-## Elasticsearch
-
-### Configuration attributes
-
-Open the rails console (`gitlab rails c`) and run the following command to see all the available attributes:
-
-```ruby
-ApplicationSetting.last.attributes
-```
-
-Among other attributes, the output contains all the settings available in the [Elasticsearch Integration page](../../integration/advanced_search/elasticsearch.md), such as `elasticsearch_indexing`, `elasticsearch_url`, `elasticsearch_replicas`, and `elasticsearch_pause_indexing`.
-
-#### Setting attributes
-
-You can then set anyone of Elasticsearch integration settings by issuing a command similar to:
-
-```ruby
-ApplicationSetting.last.update(elasticsearch_url: '<your ES URL and port>')
-
-#or
-
-ApplicationSetting.last.update(elasticsearch_indexing: false)
-```
-
-#### Getting attributes
-
-You can then check if the settings have been set in the [Elasticsearch Integration page](../../integration/advanced_search/elasticsearch.md) or in the rails console by issuing:
-
-```ruby
-Gitlab::CurrentSettings.elasticsearch_url
-
-#or
-
-Gitlab::CurrentSettings.elasticsearch_indexing
-```
-
-#### Changing the Elasticsearch password
-
-```ruby
-es_url = Gitlab::CurrentSettings.current_application_settings
-
-# Confirm the current ElasticSearch URL
-es_url.elasticsearch_url
-
-# Set the ElasticSearch URL
-es_url.elasticsearch_url = "http://<username>:<password>@your.es.host:<port>"
-
-# Save the change
-es_url.save!
-```
diff --git a/doc/administration/troubleshooting/linux_cheat_sheet.md b/doc/administration/troubleshooting/linux_cheat_sheet.md
index 6ff6e562a7d..c1a428018c2 100644
--- a/doc/administration/troubleshooting/linux_cheat_sheet.md
+++ b/doc/administration/troubleshooting/linux_cheat_sheet.md
@@ -204,7 +204,7 @@ or you can build it from source if you have the Rust compiler.
#### How to use the tool
-First run the tool with `summary` flag to get a summary of the top processes sorted by time spent actively performing tasks.
+First run the tool with `summary` flag to get a summary of the top processes sorted by time spent actively performing tasks.
You can also sort based on total time, # of system calls made, PID #, and # of child processes
using the `-s` or `--sort` flag. The number of results defaults to 25 processes, but
can be changed using the `-c`/`--count` option. See `--help` for full details.
diff --git a/doc/administration/troubleshooting/ssl.md b/doc/administration/troubleshooting/ssl.md
index e1cd92a788f..c5f3f0ed8d1 100644
--- a/doc/administration/troubleshooting/ssl.md
+++ b/doc/administration/troubleshooting/ssl.md
@@ -13,7 +13,7 @@ main SSL documentation:
- [Omnibus SSL Configuration](https://docs.gitlab.com/omnibus/settings/ssl.html).
- [Self-signed certificates or custom Certification Authorities for GitLab Runner](https://docs.gitlab.com/runner/configuration/tls-self-signed.html).
-- [Manually configuring HTTPS](https://docs.gitlab.com/omnibus/settings/nginx.html#manually-configuring-https).
+- [Configure HTTPS manually](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-https-manually).
## Using an internal CA certificate with GitLab
@@ -251,3 +251,13 @@ You must specify that Git should use OpenSSL:
```shell
git config --system http.sslbackend openssl
```
+
+Alternatively, you can ignore SSL verification by running:
+
+WARNING:
+Proceed with caution when [ignoring SSL](https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpsslVerify)
+due to the potential security issues associated with disabling this option at global level. Use this option _only_ when troubleshooting, and reinstate SSL verification immediately after.
+
+```shell
+git config --global http.sslVerify false
+```
diff --git a/doc/administration/troubleshooting/tracing_correlation_id.md b/doc/administration/troubleshooting/tracing_correlation_id.md
index ee59b7c2504..917e27bab70 100644
--- a/doc/administration/troubleshooting/tracing_correlation_id.md
+++ b/doc/administration/troubleshooting/tracing_correlation_id.md
@@ -6,6 +6,6 @@ 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 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/user_settings.md b/doc/administration/user_settings.md
index 2e879f8789d..0a3f351c695 100644
--- a/doc/administration/user_settings.md
+++ b/doc/administration/user_settings.md
@@ -8,10 +8,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
GitLab administrators can modify user settings for the entire GitLab instance.
-## Prevent users from creating top-level groups
+## Prevent new users from creating top-level groups
-By default, new users can create top-level groups. To disable your users'
-ability to create top-level groups:
+By default, new users can create top-level groups. To disable new users'
+ability to create top-level groups (does not affect existing users' setting):
**Omnibus GitLab installations**
@@ -33,6 +33,13 @@ ability to create top-level groups:
1. [Restart GitLab](restart_gitlab.md#installations-from-source).
+### Prevent existing users from creating top-level groups
+
+Administrators can:
+
+- Use the Admin Area to [prevent an existing user from creating top-level groups](../user/admin_area/index.md#prevent-a-user-from-creating-groups).
+- Use the [modify an existing user API endpoint](../api/users.md#user-modification) to change the `can_create_group` setting.
+
## Prevent users from changing their usernames
By default, new users can change their usernames. To disable your users'
diff --git a/doc/administration/whats-new.md b/doc/administration/whats-new.md
index 05c769cf20c..d8003d579ac 100644
--- a/doc/administration/whats-new.md
+++ b/doc/administration/whats-new.md
@@ -6,37 +6,39 @@ info: For assistance with this What's new page, see https://about.gitlab.com/han
# What's new **(FREE)**
-With each monthly release, GitLab includes some of the highlights from the last 10
-GitLab versions in the **What's new** feature. To access it:
+You can view some of the highlights from the last 10
+GitLab versions in the **What's new** feature. It lists new features available in different
+[GitLab tiers](https://about.gitlab.com/pricing/).
-1. In the top navigation bar, select the **{question}** icon.
-1. Select **What's new** from the menu.
-
-The **What's new** describes new features available in multiple
-[GitLab tiers](https://about.gitlab.com/pricing/). While all users can see the
-feature list, the feature list is tailored to your subscription type:
+All users can see the feature list, but the entries might differ depending on the subscription type:
-- Features only available to self-managed installations are not shown on GitLab.com.
- Features only available on GitLab.com are not shown to self-managed installations.
+- Features only available to self-managed installations are not shown on GitLab.com.
-## Self-managed installations
+ NOTE:
+ For self-managed installations, the updated **What's new** is included
+ in the first patch release after a new version, such as `13.10.1`.
-Due to our release post process, the content for **What's new** is not finalized
-when a new version (`.0` release) is cut. The updated **What's new** is included
-in the first patch release, such as `13.10.1`.
+## Access What's new
+
+To access the **What's new** feature:
+
+1. On the top bar, select the **{question}** icon.
+1. Select **What's new** from the menu.
-## Configure What's new variant
+## Configure What's new
-You can configure the What's new variant:
+You can configure **What's new** to display features based on the tier,
+or you can hide it. To configure it:
-1. On the top bar, select **Menu > Admin**.
-1. On the left sidebar, select **Settings > Preferences**, then expand **What's new**.
-1. Choose one of the following options:
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Settings > Preferences**.
+1. Expand **What's new**, and choose one of the following options:
| Option | Description |
| ------ | ----------- |
- | Enable What's new: All tiers | What's new presents new features from all tiers to help you keep track of all new features. |
- | Enable What's new: Current tier only | What's new presents new features for your current subscription tier, while hiding new features not available to your subscription tier. |
- | Disable What's new | What's new is disabled and can no longer be viewed. |
+ | Enable What's new: All tiers | Presents new features from all tiers. |
+ | Enable What's new: Current tier only | Presents new features for your current subscription tier, and hides new features outside of your tier. |
+ | Disable What's new | Disables this feature, so it no longer displays under the **{question}** icon. |
-1. Save your changes.
+1. Select **Save changes**.
diff --git a/doc/api/api_resources.md b/doc/api/api_resources.md
index aa942582e9c..7d613852d23 100644
--- a/doc/api/api_resources.md
+++ b/doc/api/api_resources.md
@@ -31,7 +31,10 @@ The following API resources are available in the project context:
| [Commits](commits.md) | `/projects/:id/repository/commits`, `/projects/:id/statuses` |
| [Container Registry](container_registry.md) | `/projects/:id/registry/repositories` |
| [Custom attributes](custom_attributes.md) | `/projects/:id/custom_attributes` (also available for groups and users) |
+| [Composer distributions](packages/composer.md) | `/projects/:id/packages/composer` (also available for groups) |
+| [Conan distributions](packages/conan.md) | `/projects/:id/packages/conan` (also available standalone) |
| [Debian distributions](packages/debian_project_distributions.md) | `/projects/:id/debian_distributions` (also available for groups) |
+| [Debian packages](packages/debian.md) | `/projects/:id/packages/debian` (also available for groups) |
| [Dependencies](dependencies.md) **(ULTIMATE)** | `/projects/:id/dependencies` |
| [Deploy keys](deploy_keys.md) | `/projects/:id/deploy_keys` (also available standalone) |
| [Deploy tokens](deploy_tokens.md) | `/projects/:id/deploy_tokens` (also available for groups and standalone) |
@@ -43,6 +46,8 @@ The following API resources are available in the project context:
| [Feature Flag User Lists](feature_flag_user_lists.md) | `/projects/:id/feature_flags_user_lists` |
| [Feature Flags](feature_flags.md) | `/projects/:id/feature_flags` |
| [Freeze Periods](freeze_periods.md) | `/projects/:id/freeze_periods` |
+| [Go Proxy](packages/go_proxy.md) | `/projects/:id/packages/go` |
+| [Helm repository](packages/helm.md) | `/projects/:id/packages/helm_repository` |
| [Integrations](integrations.md) (Formerly "services") | `/projects/:id/integrations` |
| [Invitations](invitations.md) | `/projects/:id/invitations` (also available for groups) |
| [Issue boards](boards.md) | `/projects/:id/boards` |
@@ -51,8 +56,10 @@ The following API resources are available in the project context:
| [Issues](issues.md) | `/projects/:id/issues` (also available for groups and standalone) |
| [Iterations](iterations.md) **(PREMIUM)** | `/projects/:id/iterations` (also available for groups) |
| [Jobs](jobs.md) | `/projects/:id/jobs`, `/projects/:id/pipelines/.../jobs` |
+| [Jobs Artifacts](job_artifacts.md) | `/projects/:id/jobs/:job_id/artifacts` |
| [Labels](labels.md) | `/projects/:id/labels` |
| [Managed licenses](managed_licenses.md) **(ULTIMATE)** | `/projects/:id/managed_licenses` |
+| [Maven repository](packages/maven.md) | `/projects/:id/packages/maven` (also available for groups and standalone) |
| [Members](members.md) | `/projects/:id/members` (also available for groups) |
| [Merge request approvals](merge_request_approvals.md) **(PREMIUM)** | `/projects/:id/approvals`, `/projects/:id/merge_requests/.../approvals` |
| [Merge requests](merge_requests.md) | `/projects/:id/merge_requests` (also available for groups and standalone) |
@@ -60,6 +67,8 @@ The following API resources are available in the project context:
| [Metadata](metadata.md) | `/metadata` |
| [Notes](notes.md) (comments) | `/projects/:id/issues/.../notes`, `/projects/:id/snippets/.../notes`, `/projects/:id/merge_requests/.../notes` (also available for groups) |
| [Notification settings](notification_settings.md) | `/projects/:id/notification_settings` (also available for groups and standalone) |
+| [NPM repository](packages/npm.md) | `/projects/:id/packages/npm` |
+| [NuGet packages](packages/nuget.md) | `/projects/:id/packages/nuget` (also available for groups) |
| [Packages](packages.md) | `/projects/:id/packages` |
| [Pages domains](pages_domains.md) | `/projects/:id/pages` (also available standalone) |
| [Pipeline schedules](pipeline_schedules.md) | `/projects/:id/pipeline_schedules` |
@@ -78,6 +87,7 @@ The following API resources are available in the project context:
| [Protected branches](protected_branches.md) | `/projects/:id/protected_branches` |
| [Protected environments](protected_environments.md) | `/projects/:id/protected_environments` |
| [Protected tags](protected_tags.md) | `/projects/:id/protected_tags` |
+| [PyPI packages](packages/pypi.md) | `/projects/:id/packages/pypi` (also available for groups) |
| [Release links](releases/links.md) | `/projects/:id/releases/.../assets/links` |
| [Releases](releases/index.md) | `/projects/:id/releases` |
| [Remote mirrors](remote_mirrors.md) | `/projects/:id/remote_mirrors` |
@@ -85,9 +95,11 @@ The following API resources are available in the project context:
| [Repository files](repository_files.md) | `/projects/:id/repository/files` |
| [Repository submodules](repository_submodules.md) | `/projects/:id/repository/submodules` |
| [Resource label events](resource_label_events.md) | `/projects/:id/issues/.../resource_label_events`, `/projects/:id/merge_requests/.../resource_label_events` (also available for groups) |
+| [Ruby gems](packages/rubygems.md) | `/projects/:id/packages/rubygems` |
| [Runners](runners.md) | `/projects/:id/runners` (also available standalone) |
| [Search](search.md) | `/projects/:id/search` (also available for groups and standalone) |
| [Tags](tags.md) | `/projects/:id/repository/tags` |
+| [Terraform modules](packages/terraform-modules.md) | `/projects/:id/packages/terraform/mdoules` (also available standalone) |
| [User-starred metrics dashboards](metrics_user_starred_dashboards.md ) | `/projects/:id/metrics/user_starred_dashboards` |
| [Visual Review discussions](visual_review_discussions.md) **(PREMIUM)** | `/projects/:id/merge_requests/:merge_request_id/visual_review_discussions` |
| [Vulnerabilities](vulnerabilities.md) **(ULTIMATE)** | `/vulnerabilities/:id` |
diff --git a/doc/api/audit_events.md b/doc/api/audit_events.md
index 80d7b23d642..5bff3cf49fc 100644
--- a/doc/api/audit_events.md
+++ b/doc/api/audit_events.md
@@ -1,5 +1,5 @@
---
-stage: Manage
+stage: Govern
group: Compliance
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
---
diff --git a/doc/api/container_registry.md b/doc/api/container_registry.md
index 0dd2106b4d1..f23641abb4f 100644
--- a/doc/api/container_registry.md
+++ b/doc/api/container_registry.md
@@ -80,6 +80,11 @@ Example response:
}
```
+## Container Registry pagination
+
+By default, `GET` requests return 20 results at a time because the API results
+are [paginated](index.md#pagination).
+
## List registry repositories
### Within a project
diff --git a/doc/api/custom_attributes.md b/doc/api/custom_attributes.md
index 94f924c051d..e15ed4bceee 100644
--- a/doc/api/custom_attributes.md
+++ b/doc/api/custom_attributes.md
@@ -1,6 +1,6 @@
---
-stage: Ecosystem
-group: Integrations
+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/engineering/ux/technical-writing/#assignments
---
diff --git a/doc/api/deployments.md b/doc/api/deployments.md
index 01fa4e11884..8b99c21a385 100644
--- a/doc/api/deployments.md
+++ b/doc/api/deployments.md
@@ -58,6 +58,9 @@ Example response:
"started_at": null,
"status": "success",
"tag": false,
+ "project": {
+ "ci_job_token_scope_enabled": false
+ },
"user": {
"id": 1,
"name": "Administrator",
@@ -128,6 +131,9 @@ Example response:
"started_at": null,
"status": "success",
"tag": false,
+ "project": {
+ "ci_job_token_scope_enabled": false
+ },
"user": {
"id": 1,
"name": "Administrator",
@@ -226,6 +232,9 @@ Example response:
"created_at": "2016-08-11T11:32:24.456Z",
"started_at": null,
"finished_at": "2016-08-11T11:32:35.145Z",
+ "project": {
+ "ci_job_token_scope_enabled": false
+ },
"user": {
"id": 1,
"name": "Administrator",
diff --git a/doc/api/discussions.md b/doc/api/discussions.md
index a5610adad79..60ce10590e1 100644
--- a/doc/api/discussions.md
+++ b/doc/api/discussions.md
@@ -1109,7 +1109,7 @@ GET /projects/:id/repository/commits/:commit_id/discussions
| Attribute | Type | Required | Description |
| ------------------- | ---------------- | ---------- | ------------ |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) |
-| `commit_id` | integer | yes | The ID of a commit |
+| `commit_id` | string | yes | The SHA of a commit |
```json
[
@@ -1237,7 +1237,7 @@ Diff comments contain also position:
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>"\
- "https://gitlab.example.com/api/v4/projects/5/repository/commits/11/discussions"
+ "https://gitlab.example.com/api/v4/projects/5/repository/commits/<commit_id>/discussions"
```
### Get single commit discussion item
@@ -1253,12 +1253,12 @@ Parameters:
| Attribute | Type | Required | Description |
| ------------------- | -------------- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) |
-| `commit_id` | integer | yes | The ID of a commit |
-| `discussion_id` | integer | yes | The ID of a discussion item |
+| `commit_id` | string | yes | The SHA of a commit |
+| `discussion_id` | string | yes | The ID of a discussion item |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>"\
- "https://gitlab.example.com/api/v4/projects/5/repository/commits/11/discussions/<discussion_id>"
+ "https://gitlab.example.com/api/v4/projects/5/repository/commits/<commit_id>/discussions/<discussion_id>"
```
### Create new commit thread
@@ -1294,7 +1294,7 @@ Parameters:
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>"\
- "https://gitlab.example.com/api/v4/projects/5/repository/commits/11/discussions?body=comment"
+ "https://gitlab.example.com/api/v4/projects/5/repository/commits/<commit_id>/discussions?body=comment"
```
The rules for creating the API request are the same as when
@@ -1314,15 +1314,15 @@ Parameters:
| Attribute | Type | Required | Description |
| ------------------- | -------------- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) |
-| `commit_id` | integer | yes | The ID of a commit |
-| `discussion_id` | integer | yes | The ID of a thread |
+| `commit_id` | string | yes | The SHA of a commit |
+| `discussion_id` | string | yes | The ID of a thread |
| `note_id` | integer | yes | The ID of a thread note |
| `body` | string | yes | The content of the note/reply |
| `created_at` | string | no | Date time string, ISO 8601 formatted, such `2016-03-11T03:45:40Z` (requires administrator or project/group owner rights) |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>"\
- "https://gitlab.example.com/api/v4/projects/5/repository/commits/11/discussions/<discussion_id>/notes?body=comment
+ "https://gitlab.example.com/api/v4/projects/5/repository/commits/<commit_id>/discussions/<discussion_id>/notes?body=comment
```
### Modify an existing commit thread note
@@ -1338,21 +1338,21 @@ Parameters:
| Attribute | Type | Required | Description |
| ------------------- | -------------- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) |
-| `commit_id` | integer | yes | The ID of a commit |
-| `discussion_id` | integer | yes | The ID of a thread |
+| `commit_id` | string | yes | The SHA of a commit |
+| `discussion_id` | string | yes | The ID of a thread |
| `note_id` | integer | yes | The ID of a thread note |
| `body` | string | no | The content of a note |
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>"\
- "https://gitlab.example.com/api/v4/projects/5/repository/commits/11/discussions/<discussion_id>/notes/1108?body=comment"
+ "https://gitlab.example.com/api/v4/projects/5/repository/commits/<commit_id>/discussions/<discussion_id>/notes/1108?body=comment"
```
Resolving a note:
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>"\
- "https://gitlab.example.com/api/v4/projects/5/repository/commits/11/discussions/<discussion_id>/notes/1108?resolved=true"
+ "https://gitlab.example.com/api/v4/projects/5/repository/commits/<commit_id>/discussions/<discussion_id>/notes/1108?resolved=true"
```
### Delete a commit thread note
@@ -1368,11 +1368,11 @@ Parameters:
| Attribute | Type | Required | Description |
| ------------------- | -------------- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) |
-| `commit_id` | integer | yes | The ID of a commit |
-| `discussion_id` | integer | yes | The ID of a thread |
+| `commit_id` | string | yes | The SHA of a commit |
+| `discussion_id` | string | yes | The ID of a thread |
| `note_id` | integer | yes | The ID of a thread note |
```shell
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>"\
- "https://gitlab.example.com/api/v4/projects/5/repository/commits/11/discussions/636"
+ "https://gitlab.example.com/api/v4/projects/5/repository/commits/<commit_id>/discussions/<discussion_id>/notes/636"
```
diff --git a/doc/api/dora/metrics.md b/doc/api/dora/metrics.md
index 99473ca7b4c..543b6583fde 100644
--- a/doc/api/dora/metrics.md
+++ b/doc/api/dora/metrics.md
@@ -105,3 +105,6 @@ parameter:
| `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. |
| `time_to_restore_service` | The median number of seconds an incident was open during the time period. Available only for production environment. |
+
+NOTE:
+The API returns the `monthly` and `all` intervals by calculating the median of the daily median values. This can introduce a slight inaccuracy in the returned data.
diff --git a/doc/api/environments.md b/doc/api/environments.md
index d67808bfd61..3f5f711e10b 100644
--- a/doc/api/environments.md
+++ b/doc/api/environments.md
@@ -68,6 +68,9 @@ Example response:
"started_at": "2019-03-25T12:54:50.082Z",
"finished_at": "2019-03-25T18:55:13.216Z",
"duration": 21623.13423,
+ "project": {
+ "ci_job_token_scope_enabled": false
+ },
"user": {
"id": 1,
"name": "Administrator",
@@ -180,6 +183,9 @@ Example of response
"started_at": "2019-03-25T12:54:50.082Z",
"finished_at": "2019-03-25T18:55:13.216Z",
"duration": 21623.13423,
+ "project": {
+ "ci_job_token_scope_enabled": false
+ },
"user": {
"id": 1,
"name": "Administrator",
diff --git a/doc/api/events.md b/doc/api/events.md
index 3f032c72870..e4490459030 100644
--- a/doc/api/events.md
+++ b/doc/api/events.md
@@ -1,5 +1,5 @@
---
-stage: Manage
+stage: Govern
group: Compliance
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
---
diff --git a/doc/api/graphql/audit_report.md b/doc/api/graphql/audit_report.md
index 988db29912f..dc7eb10b810 100644
--- a/doc/api/graphql/audit_report.md
+++ b/doc/api/graphql/audit_report.md
@@ -1,6 +1,6 @@
---
-stage: Ecosystem
-group: Integrations
+stage: Govern
+group: Compliance
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
---
diff --git a/doc/api/graphql/getting_started.md b/doc/api/graphql/getting_started.md
index 258f781528b..386ef6c403b 100644
--- a/doc/api/graphql/getting_started.md
+++ b/doc/api/graphql/getting_started.md
@@ -26,6 +26,7 @@ local computer. A GraphQL request can be made as a `POST` request to `/api/graph
with the query as the payload. You can authorize your request by generating a
[personal access token](../../user/profile/personal_access_tokens.md) to use as
a bearer token.
+This token requires at least the `read_api` scope.
Example:
@@ -36,6 +37,16 @@ curl "https://gitlab.com/api/graphql" --header "Authorization: Bearer $GRAPHQL_T
--data "{\"query\": \"query {currentUser {name}}\"}"
```
+To nest strings in the query string,
+wrap the data in single quotes or escape the strings with `\\`:
+
+```shell
+curl "https://gitlab.com/api/graphql" --header "Authorization: Bearer $GRAPHQL_TOKEN" \
+ --header "Content-Type: application/json" --request POST \
+ --data '{"query": "query {project(fullPath: \"<group>/<subgroup>/<project>\") {jobs {nodes {id duration}}}}"}'
+ # or "{\"query\": \"query {project(fullPath: \\\"<group>/<subgroup>/<project>\\\") {jobs {nodes {id duration}}}}\"}"
+```
+
### GraphiQL
GraphiQL (pronounced "graphical") allows you to run queries directly against
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index ab5b5a92203..f9125e5f4d4 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -280,7 +280,7 @@ Returns [`Namespace`](#namespace).
### `Query.package`
-Find a package.
+Find a package. This field can only be resolved for one query in any single request.
Returns [`PackageDetailsType`](#packagedetailstype).
@@ -317,11 +317,11 @@ four standard [pagination arguments](#connection-pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="queryprojectsids"></a>`ids` | [`[ID!]`](#id) | Filter projects by IDs. |
-| <a id="queryprojectsmembership"></a>`membership` | [`Boolean`](#boolean) | Limit projects that the current user is a member of. |
-| <a id="queryprojectssearch"></a>`search` | [`String`](#string) | Search query for project name, path, or description. |
+| <a id="queryprojectsmembership"></a>`membership` | [`Boolean`](#boolean) | Return only projects that the current user is a member of. |
+| <a id="queryprojectssearch"></a>`search` | [`String`](#string) | Search query, which can be for the project name, a path, or a description. |
| <a id="queryprojectssearchnamespaces"></a>`searchNamespaces` | [`Boolean`](#boolean) | Include namespace in project search. |
-| <a id="queryprojectssort"></a>`sort` | [`String`](#string) | Sort order of results. |
-| <a id="queryprojectstopics"></a>`topics` | [`[String!]`](#string) | Filters projects by topics. |
+| <a id="queryprojectssort"></a>`sort` | [`String`](#string) | Sort order of results. Format: '<field_name>_<sort_direction>', for example: 'id_desc' or 'name_asc'. |
+| <a id="queryprojectstopics"></a>`topics` | [`[String!]`](#string) | Filter projects by topics. |
### `Query.queryComplexity`
@@ -742,6 +742,25 @@ Input type: `ApiFuzzingCiConfigurationCreateInput`
| <a id="mutationapifuzzingciconfigurationcreateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationapifuzzingciconfigurationcreategitlabciyamleditpath"></a>`gitlabCiYamlEditPath` **{warning-solid}** | [`String`](#string) | **Deprecated:** The configuration snippet is now generated client-side. Deprecated in 14.6. |
+### `Mutation.artifactDestroy`
+
+Input type: `ArtifactDestroyInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationartifactdestroyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationartifactdestroyid"></a>`id` | [`CiJobArtifactID!`](#cijobartifactid) | ID of the artifact to delete. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationartifactdestroyartifact"></a>`artifact` | [`CiJobArtifact`](#cijobartifact) | Deleted artifact. |
+| <a id="mutationartifactdestroyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationartifactdestroyerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+
### `Mutation.auditEventsStreamingHeadersCreate`
Input type: `AuditEventsStreamingHeadersCreateInput`
@@ -1409,7 +1428,9 @@ Input type: `CreateComplianceFrameworkInput`
### `Mutation.createCustomEmoji`
-Available only when feature flag `custom_emoji` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
+WARNING:
+**Introduced** in 13.6.
+This feature is in Alpha. It can be changed or removed at any time.
Input type: `CreateCustomEmojiInput`
@@ -2271,7 +2292,9 @@ Input type: `DestroyContainerRepositoryTagsInput`
### `Mutation.destroyCustomEmoji`
-Available only when feature flag `custom_emoji` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice.
+WARNING:
+**Introduced** in 13.6.
+This feature is in Alpha. It can be changed or removed at any time.
Input type: `DestroyCustomEmojiInput`
@@ -2786,6 +2809,7 @@ Input type: `ExternalAuditEventDestinationCreateInput`
| <a id="mutationexternalauditeventdestinationcreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationexternalauditeventdestinationcreatedestinationurl"></a>`destinationUrl` | [`String!`](#string) | Destination URL. |
| <a id="mutationexternalauditeventdestinationcreategrouppath"></a>`groupPath` | [`ID!`](#id) | Group path. |
+| <a id="mutationexternalauditeventdestinationcreateverificationtoken"></a>`verificationToken` | [`String`](#string) | Verification token. |
#### Fields
@@ -3033,6 +3057,7 @@ Input type: `IssueMoveListInput`
| <a id="mutationissuemovelistiid"></a>`iid` | [`String!`](#string) | IID of the issue to mutate. |
| <a id="mutationissuemovelistmoveafterid"></a>`moveAfterId` | [`ID`](#id) | ID of issue that should be placed after the current issue. |
| <a id="mutationissuemovelistmovebeforeid"></a>`moveBeforeId` | [`ID`](#id) | ID of issue that should be placed before the current issue. |
+| <a id="mutationissuemovelistpositioninlist"></a>`positionInList` | [`Int`](#int) | Position of issue within the board list. Positions start at 0. Use -1 to move to the end of the list. |
| <a id="mutationissuemovelistprojectpath"></a>`projectPath` | [`ID!`](#id) | Project the issue to mutate is in. |
| <a id="mutationissuemovelisttolistid"></a>`toListId` | [`ID`](#id) | ID of the board list that the issue will be moved to. |
@@ -3457,6 +3482,26 @@ Input type: `JiraImportUsersInput`
| <a id="mutationjiraimportuserserrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationjiraimportusersjirausers"></a>`jiraUsers` | [`[JiraUser!]`](#jirauser) | Users returned from Jira, matched by email and name if possible. |
+### `Mutation.jobArtifactsDestroy`
+
+Input type: `JobArtifactsDestroyInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationjobartifactsdestroyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationjobartifactsdestroyid"></a>`id` | [`CiBuildID!`](#cibuildid) | ID of the job to mutate. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationjobartifactsdestroyclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationjobartifactsdestroydestroyedartifactscount"></a>`destroyedArtifactsCount` | [`Int!`](#int) | Number of artifacts deleted. |
+| <a id="mutationjobartifactsdestroyerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+| <a id="mutationjobartifactsdestroyjob"></a>`job` | [`CiJob`](#cijob) | Job with artifacts to be deleted. |
+
### `Mutation.jobCancel`
Input type: `JobCancelInput`
@@ -4326,7 +4371,7 @@ Input type: `ReleaseCreateInput`
| <a id="mutationreleasecreatename"></a>`name` | [`String`](#string) | Name of the release. |
| <a id="mutationreleasecreateprojectpath"></a>`projectPath` | [`ID!`](#id) | Full path of the project the release is associated with. |
| <a id="mutationreleasecreateref"></a>`ref` | [`String`](#string) | Commit SHA or branch name to use if creating a new tag. |
-| <a id="mutationreleasecreatereleasedat"></a>`releasedAt` | [`Time`](#time) | Date and time for the release. Defaults to the current date and time. |
+| <a id="mutationreleasecreatereleasedat"></a>`releasedAt` | [`Time`](#time) | Date and time for the release. Defaults to the current time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). Only provide this field if creating an upcoming or historical release. |
| <a id="mutationreleasecreatetagmessage"></a>`tagMessage` | [`String`](#string) | Message to use if creating a new annotated tag. |
| <a id="mutationreleasecreatetagname"></a>`tagName` | [`String!`](#string) | Name of the tag to associate with the release. |
@@ -4450,6 +4495,7 @@ Input type: `RunnerUpdateInput`
| ---- | ---- | ----------- |
| <a id="mutationrunnerupdateaccesslevel"></a>`accessLevel` | [`CiRunnerAccessLevel`](#cirunneraccesslevel) | Access level of the runner. |
| <a id="mutationrunnerupdateactive"></a>`active` **{warning-solid}** | [`Boolean`](#boolean) | **Deprecated:** This was renamed. Please use `paused`. Deprecated in 14.8. |
+| <a id="mutationrunnerupdateassociatedprojects"></a>`associatedProjects` | [`[ProjectID!]`](#projectid) | Projects associated with the runner. Available only for project runners. |
| <a id="mutationrunnerupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationrunnerupdatedescription"></a>`description` | [`String`](#string) | Description of the runner. |
| <a id="mutationrunnerupdateid"></a>`id` | [`CiRunnerID!`](#cirunnerid) | ID of the runner to update. |
@@ -4575,6 +4621,47 @@ Input type: `ScanExecutionPolicyCommitInput`
| <a id="mutationscanexecutionpolicycommitclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationscanexecutionpolicycommiterrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+### `Mutation.securityFindingCreateIssue`
+
+Input type: `SecurityFindingCreateIssueInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationsecurityfindingcreateissueclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationsecurityfindingcreateissueproject"></a>`project` | [`ProjectID!`](#projectid) | ID of the project to attach the issue to. |
+| <a id="mutationsecurityfindingcreateissueuuid"></a>`uuid` | [`String!`](#string) | UUID of the security finding to be used to create an issue. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationsecurityfindingcreateissueclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationsecurityfindingcreateissueerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+| <a id="mutationsecurityfindingcreateissueissue"></a>`issue` | [`Issue`](#issue) | Issue created after mutation. |
+
+### `Mutation.securityFindingDismiss`
+
+Input type: `SecurityFindingDismissInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationsecurityfindingdismissclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationsecurityfindingdismisscomment"></a>`comment` | [`String`](#string) | Comment why finding should be dismissed. |
+| <a id="mutationsecurityfindingdismissdismissalreason"></a>`dismissalReason` | [`VulnerabilityDismissalReason`](#vulnerabilitydismissalreason) | Reason why finding should be dismissed. |
+| <a id="mutationsecurityfindingdismissuuid"></a>`uuid` | [`String!`](#string) | UUID of the finding to be dismissed. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationsecurityfindingdismissclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationsecurityfindingdismisserrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+| <a id="mutationsecurityfindingdismissuuid"></a>`uuid` | [`String`](#string) | UUID of dismissed finding. |
+
### `Mutation.securityPolicyProjectAssign`
Assigns the specified project(`security_policy_project_id`) as security policy project for the given project(`full_path`). If the project already has a security policy project, this reassigns the project's security policy project with the given `security_policy_project_id`.
@@ -5094,6 +5181,8 @@ Input type: `UpdateDependencyProxyImageTtlGroupPolicyInput`
### `Mutation.updateDependencyProxySettings`
+These settings can be adjusted by the group Owner or Maintainer. However, in GitLab 16.0, we will be limiting this to the Owner role. [GitLab-#364441](https://gitlab.com/gitlab-org/gitlab/-/issues/364441) proposes making this change to match the permissions level in the user interface.
+
Input type: `UpdateDependencyProxySettingsInput`
#### Arguments
@@ -5456,7 +5545,7 @@ Input type: `VulnerabilityCreateInput`
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationvulnerabilitycreateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
-| <a id="mutationvulnerabilitycreateconfidence"></a>`confidence` | [`VulnerabilityConfidence`](#vulnerabilityconfidence) | Confidence of the vulnerability (defaults to `unknown`). |
+| <a id="mutationvulnerabilitycreateconfidence"></a>`confidence` **{warning-solid}** | [`VulnerabilityConfidence`](#vulnerabilityconfidence) | **Deprecated:** This field will be removed from the Vulnerability domain model. Deprecated in 15.4. |
| <a id="mutationvulnerabilitycreateconfirmedat"></a>`confirmedAt` | [`Time`](#time) | Timestamp of when the vulnerability state changed to confirmed (defaults to creation time if status is `confirmed`). |
| <a id="mutationvulnerabilitycreatedescription"></a>`description` | [`String!`](#string) | Long text section that describes the vulnerability in more detail. |
| <a id="mutationvulnerabilitycreatedetectedat"></a>`detectedAt` | [`Time`](#time) | Timestamp of when the vulnerability was first detected (defaults to creation time). |
@@ -5728,6 +5817,7 @@ Input type: `WorkItemUpdateInput`
| <a id="mutationworkitemupdatedescriptionwidget"></a>`descriptionWidget` | [`WorkItemWidgetDescriptionInput`](#workitemwidgetdescriptioninput) | Input for description 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="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="mutationworkitemupdatetitle"></a>`title` | [`String`](#string) | Title of the work item. |
@@ -6025,6 +6115,7 @@ The connection type for [`BoardEpic`](#boardepic).
| Name | Type | Description |
| ---- | ---- | ----------- |
+| <a id="boardepicconnectioncount"></a>`count` | [`Int!`](#int) | Total count of collection. |
| <a id="boardepicconnectionedges"></a>`edges` | [`[BoardEpicEdge]`](#boardepicedge) | A list of edges. |
| <a id="boardepicconnectionnodes"></a>`nodes` | [`[BoardEpic]`](#boardepic) | A list of nodes. |
| <a id="boardepicconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
@@ -6063,6 +6154,29 @@ The edge type for [`BoardList`](#boardlist).
| <a id="boardlistedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="boardlistedgenode"></a>`node` | [`BoardList`](#boardlist) | The item at the end of the edge. |
+#### `BranchRuleConnection`
+
+The connection type for [`BranchRule`](#branchrule).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="branchruleconnectionedges"></a>`edges` | [`[BranchRuleEdge]`](#branchruleedge) | A list of edges. |
+| <a id="branchruleconnectionnodes"></a>`nodes` | [`[BranchRule]`](#branchrule) | A list of nodes. |
+| <a id="branchruleconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+
+#### `BranchRuleEdge`
+
+The edge type for [`BranchRule`](#branchrule).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="branchruleedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| <a id="branchruleedgenode"></a>`node` | [`BranchRule`](#branchrule) | The item at the end of the edge. |
+
#### `CiBuildNeedConnection`
The connection type for [`CiBuildNeed`](#cibuildneed).
@@ -6210,6 +6324,7 @@ The connection type for [`CiGroupVariable`](#cigroupvariable).
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="cigroupvariableconnectionedges"></a>`edges` | [`[CiGroupVariableEdge]`](#cigroupvariableedge) | A list of edges. |
+| <a id="cigroupvariableconnectionlimit"></a>`limit` | [`Int!`](#int) | Maximum amount of group CI/CD variables. |
| <a id="cigroupvariableconnectionnodes"></a>`nodes` | [`[CiGroupVariable]`](#cigroupvariable) | A list of nodes. |
| <a id="cigroupvariableconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
@@ -6385,6 +6500,7 @@ The connection type for [`CiProjectVariable`](#ciprojectvariable).
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="ciprojectvariableconnectionedges"></a>`edges` | [`[CiProjectVariableEdge]`](#ciprojectvariableedge) | A list of edges. |
+| <a id="ciprojectvariableconnectionlimit"></a>`limit` | [`Int!`](#int) | Maximum amount of project CI/CD variables. |
| <a id="ciprojectvariableconnectionnodes"></a>`nodes` | [`[CiProjectVariable]`](#ciprojectvariable) | A list of nodes. |
| <a id="ciprojectvariableconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
@@ -6959,6 +7075,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. |
+#### `DeploymentConnection`
+
+The connection type for [`Deployment`](#deployment).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="deploymentconnectionedges"></a>`edges` | [`[DeploymentEdge]`](#deploymentedge) | A list of edges. |
+| <a id="deploymentconnectionnodes"></a>`nodes` | [`[Deployment]`](#deployment) | A list of nodes. |
+| <a id="deploymentconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+
+#### `DeploymentEdge`
+
+The edge type for [`Deployment`](#deployment).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="deploymentedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| <a id="deploymentedgenode"></a>`node` | [`Deployment`](#deployment) | The item at the end of the edge. |
+
#### `DesignAtVersionConnection`
The connection type for [`DesignAtVersion`](#designatversion).
@@ -7151,6 +7290,7 @@ The connection type for [`Epic`](#epic).
| Name | Type | Description |
| ---- | ---- | ----------- |
+| <a id="epicconnectioncount"></a>`count` | [`Int!`](#int) | Total count of collection. |
| <a id="epicconnectionedges"></a>`edges` | [`[EpicEdge]`](#epicedge) | A list of edges. |
| <a id="epicconnectionnodes"></a>`nodes` | [`[Epic]`](#epic) | A list of nodes. |
| <a id="epicconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
@@ -7700,6 +7840,29 @@ The edge type for [`MemberInterface`](#memberinterface).
| <a id="memberinterfaceedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="memberinterfaceedgenode"></a>`node` | [`MemberInterface`](#memberinterface) | The item at the end of the edge. |
+#### `MergeAccessLevelConnection`
+
+The connection type for [`MergeAccessLevel`](#mergeaccesslevel).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mergeaccesslevelconnectionedges"></a>`edges` | [`[MergeAccessLevelEdge]`](#mergeaccessleveledge) | A list of edges. |
+| <a id="mergeaccesslevelconnectionnodes"></a>`nodes` | [`[MergeAccessLevel]`](#mergeaccesslevel) | A list of nodes. |
+| <a id="mergeaccesslevelconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+
+#### `MergeAccessLevelEdge`
+
+The edge type for [`MergeAccessLevel`](#mergeaccesslevel).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mergeaccessleveledgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| <a id="mergeaccessleveledgenode"></a>`node` | [`MergeAccessLevel`](#mergeaccesslevel) | The item at the end of the edge. |
+
#### `MergeRequestAssigneeConnection`
The connection type for [`MergeRequestAssignee`](#mergerequestassignee).
@@ -8258,6 +8421,29 @@ The edge type for [`ProjectMember`](#projectmember).
| <a id="projectmemberedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="projectmemberedgenode"></a>`node` | [`ProjectMember`](#projectmember) | The item at the end of the edge. |
+#### `PushAccessLevelConnection`
+
+The connection type for [`PushAccessLevel`](#pushaccesslevel).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="pushaccesslevelconnectionedges"></a>`edges` | [`[PushAccessLevelEdge]`](#pushaccessleveledge) | A list of edges. |
+| <a id="pushaccesslevelconnectionnodes"></a>`nodes` | [`[PushAccessLevel]`](#pushaccesslevel) | A list of nodes. |
+| <a id="pushaccesslevelconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+
+#### `PushAccessLevelEdge`
+
+The edge type for [`PushAccessLevel`](#pushaccesslevel).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="pushaccessleveledgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| <a id="pushaccessleveledgenode"></a>`node` | [`PushAccessLevel`](#pushaccesslevel) | The item at the end of the edge. |
+
#### `ReleaseAssetLinkConnection`
The connection type for [`ReleaseAssetLink`](#releaseassetlink).
@@ -9726,6 +9912,7 @@ Represents an epic on an issue board.
| <a id="boardepiccolor"></a>`color` | [`String`](#string) | Color of the epic. Returns `null` if `epic_color_highlight` feature flag is disabled. |
| <a id="boardepicconfidential"></a>`confidential` | [`Boolean`](#boolean) | Indicates if the epic is confidential. |
| <a id="boardepiccreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp of when the epic was created. |
+| <a id="boardepicdefaultprojectforissuecreation"></a>`defaultProjectForIssueCreation` | [`Project`](#project) | Default Project for issue creation. Based on the project the user created the last issue in. |
| <a id="boardepicdescendantcounts"></a>`descendantCounts` | [`EpicDescendantCount`](#epicdescendantcount) | Number of open and closed descendant epics and issues. |
| <a id="boardepicdescendantweightsum"></a>`descendantWeightSum` | [`EpicDescendantWeights`](#epicdescendantweights) | Total weight of open and closed issues in the epic and its descendants. |
| <a id="boardepicdescription"></a>`description` | [`String`](#string) | Description of the epic. |
@@ -9795,7 +9982,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="boardepicancestorsiid"></a>`iid` | [`ID`](#id) | IID of the epic, e.g., "1". |
| <a id="boardepicancestorsiidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
| <a id="boardepicancestorsiids"></a>`iids` | [`[ID!]`](#id) | List of IIDs of epics, e.g., `[1, 2]`. |
-| <a id="boardepicancestorsin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument. |
+| <a id="boardepicancestorsin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'. |
| <a id="boardepicancestorsincludeancestorgroups"></a>`includeAncestorGroups` | [`Boolean`](#boolean) | Include epics from ancestor groups. |
| <a id="boardepicancestorsincludedescendantgroups"></a>`includeDescendantGroups` | [`Boolean`](#boolean) | Include epics from descendant groups. |
| <a id="boardepicancestorslabelname"></a>`labelName` | [`[String!]`](#string) | Filter epics by labels. |
@@ -9833,7 +10020,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="boardepicchildreniid"></a>`iid` | [`ID`](#id) | IID of the epic, e.g., "1". |
| <a id="boardepicchildreniidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
| <a id="boardepicchildreniids"></a>`iids` | [`[ID!]`](#id) | List of IIDs of epics, e.g., `[1, 2]`. |
-| <a id="boardepicchildrenin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument. |
+| <a id="boardepicchildrenin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'. |
| <a id="boardepicchildrenincludeancestorgroups"></a>`includeAncestorGroups` | [`Boolean`](#boolean) | Include epics from ancestor groups. |
| <a id="boardepicchildrenincludedescendantgroups"></a>`includeDescendantGroups` | [`Boolean`](#boolean) | Include epics from descendant groups. |
| <a id="boardepicchildrenlabelname"></a>`labelName` | [`[String!]`](#string) | Filter epics by labels. |
@@ -9937,6 +10124,32 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="branchcommit"></a>`commit` | [`Commit`](#commit) | Commit for the branch. |
| <a id="branchname"></a>`name` | [`String!`](#string) | Name of the branch. |
+### `BranchProtection`
+
+Branch protection details for a branch rule.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="branchprotectionallowforcepush"></a>`allowForcePush` | [`Boolean!`](#boolean) | Toggle force push to the branch for users with write access. |
+| <a id="branchprotectioncodeownerapprovalrequired"></a>`codeOwnerApprovalRequired` | [`Boolean!`](#boolean) | Enforce code owner approvals before allowing a merge. |
+| <a id="branchprotectionmergeaccesslevels"></a>`mergeAccessLevels` | [`MergeAccessLevelConnection`](#mergeaccesslevelconnection) | Details about who can merge when this branch is the source branch. (see [Connections](#connections)) |
+| <a id="branchprotectionpushaccesslevels"></a>`pushAccessLevels` | [`PushAccessLevelConnection`](#pushaccesslevelconnection) | Details about who can push when this branch is the source branch. (see [Connections](#connections)) |
+
+### `BranchRule`
+
+List of branch rules for a project, grouped by branch name.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="branchrulebranchprotection"></a>`branchProtection` | [`BranchProtection!`](#branchprotection) | Branch protections configured for this branch rule. |
+| <a id="branchrulecreatedat"></a>`createdAt` | [`Time!`](#time) | Timestamp of when the branch rule was created. |
+| <a id="branchrulename"></a>`name` | [`String!`](#string) | Branch name, with wildcards, for the branch rules. |
+| <a id="branchruleupdatedat"></a>`updatedAt` | [`Time!`](#time) | Timestamp of when the branch rule was last updated. |
+
### `BurnupChartDailyTotals`
Represents the total number of issues and their weights for a particular day.
@@ -10050,6 +10263,18 @@ Represents the total number of issues and their weights for a particular day.
| <a id="ciconfigstagegroups"></a>`groups` | [`CiConfigGroupConnection`](#ciconfiggroupconnection) | Groups of jobs for the stage. (see [Connections](#connections)) |
| <a id="ciconfigstagename"></a>`name` | [`String`](#string) | Name of the stage. |
+### `CiConfigVariable`
+
+CI/CD config variables.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="ciconfigvariabledescription"></a>`description` | [`String`](#string) | Description for the CI/CD config variable. |
+| <a id="ciconfigvariablekey"></a>`key` | [`String`](#string) | Name of the variable. |
+| <a id="ciconfigvariablevalue"></a>`value` | [`String`](#string) | Value of the variable. |
+
### `CiGroup`
#### Fields
@@ -10139,6 +10364,7 @@ CI/CD variables for a GitLab instance.
| <a id="cijobtags"></a>`tags` | [`[String!]`](#string) | Tags for the current job. |
| <a id="cijobtriggered"></a>`triggered` | [`Boolean`](#boolean) | Whether the job was triggered. |
| <a id="cijobuserpermissions"></a>`userPermissions` | [`JobPermissions!`](#jobpermissions) | Permissions for the current user on the resource. |
+| <a id="cijobwebpath"></a>`webPath` | [`String`](#string) | Web path of the job. |
### `CiJobArtifact`
@@ -10147,8 +10373,11 @@ CI/CD variables for a GitLab instance.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="cijobartifactdownloadpath"></a>`downloadPath` | [`String`](#string) | URL for downloading the artifact's file. |
+| <a id="cijobartifactexpireat"></a>`expireAt` | [`Time`](#time) | Expiry date of the artifact. |
| <a id="cijobartifactfiletype"></a>`fileType` | [`JobArtifactFileType`](#jobartifactfiletype) | File type of the artifact. |
+| <a id="cijobartifactid"></a>`id` | [`CiJobArtifactID!`](#cijobartifactid) | ID of the artifact. |
| <a id="cijobartifactname"></a>`name` | [`String`](#string) | File name of the artifact. |
+| <a id="cijobartifactsize"></a>`size` | [`Int!`](#int) | Size of the artifact in bytes. |
### `CiJobTokenScopeType`
@@ -10240,7 +10469,6 @@ CI/CD variables for a project.
| <a id="cirunnerplatformname"></a>`platformName` | [`String`](#string) | Platform provided by the runner. |
| <a id="cirunnerprivateprojectsminutescostfactor"></a>`privateProjectsMinutesCostFactor` | [`Float`](#float) | Private projects' "minutes cost factor" associated with the runner (GitLab.com only). |
| <a id="cirunnerprojectcount"></a>`projectCount` | [`Int`](#int) | Number of projects that the runner is associated with. |
-| <a id="cirunnerprojects"></a>`projects` | [`ProjectConnection`](#projectconnection) | Projects the runner is associated with. For project runners only. (see [Connections](#connections)) |
| <a id="cirunnerpublicprojectsminutescostfactor"></a>`publicProjectsMinutesCostFactor` | [`Float`](#float) | Public projects' "minutes cost factor" associated with the runner (GitLab.com only). |
| <a id="cirunnerrevision"></a>`revision` | [`String`](#string) | Revision of the runner. |
| <a id="cirunnerrununtagged"></a>`runUntagged` | [`Boolean!`](#boolean) | Indicates the runner is able to run untagged jobs. |
@@ -10256,7 +10484,7 @@ CI/CD variables for a project.
##### `CiRunner.jobs`
-Jobs assigned to the runner.
+Jobs assigned to the runner. This field can only be resolved for one runner in any single request.
Returns [`CiJobConnection`](#cijobconnection).
@@ -10270,6 +10498,26 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="cirunnerjobsstatuses"></a>`statuses` | [`[CiJobStatus!]`](#cijobstatus) | Filter jobs by status. |
+##### `CiRunner.projects`
+
+Find projects the runner is associated with. For project runners only.
+
+Returns [`ProjectConnection`](#projectconnection).
+
+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="cirunnerprojectsmembership"></a>`membership` | [`Boolean`](#boolean) | Return only projects that the current user is a member of. |
+| <a id="cirunnerprojectssearch"></a>`search` | [`String`](#string) | Search query, which can be for the project name, a path, or a description. |
+| <a id="cirunnerprojectssearchnamespaces"></a>`searchNamespaces` | [`Boolean`](#boolean) | Include namespace in project search. |
+| <a id="cirunnerprojectssort"></a>`sort` **{warning-solid}** | [`String`](#string) | **Deprecated** in 15.4. Default sort order will change in 16.0. Specify `"id_asc"` if query results' order is important. |
+| <a id="cirunnerprojectstopics"></a>`topics` | [`[String!]`](#string) | Filter projects by topics. |
+
##### `CiRunner.status`
Status of the runner.
@@ -10339,6 +10587,7 @@ GitLab CI/CD configuration template.
| <a id="clusteragentname"></a>`name` | [`String`](#string) | Name of the cluster agent. |
| <a id="clusteragentproject"></a>`project` | [`Project`](#project) | Project this cluster agent is associated with. |
| <a id="clusteragentupdatedat"></a>`updatedAt` | [`Time`](#time) | Timestamp the cluster agent was updated. |
+| <a id="clusteragentvulnerabilityimages"></a>`vulnerabilityImages` | [`VulnerabilityContainerImageConnection`](#vulnerabilitycontainerimageconnection) | Container images reported on the agent vulnerabilities. (see [Connections](#connections)) |
| <a id="clusteragentwebpath"></a>`webPath` | [`String`](#string) | Web path of the cluster agent. |
#### Fields with arguments
@@ -10950,6 +11199,60 @@ Group-level Dependency Proxy settings.
| ---- | ---- | ----------- |
| <a id="dependencyproxysettingenabled"></a>`enabled` | [`Boolean!`](#boolean) | Indicates whether the dependency proxy is enabled for the group. |
+### `Deployment`
+
+The deployment of an environment.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <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. |
+| <a id="deploymentid"></a>`id` | [`ID`](#id) | Global ID of the deployment. |
+| <a id="deploymentiid"></a>`iid` | [`ID`](#id) | Project-level internal ID of the deployment. |
+| <a id="deploymentjob"></a>`job` | [`CiJob`](#cijob) | Pipeline job of the deployment. |
+| <a id="deploymentref"></a>`ref` | [`String`](#string) | Git-Ref that the deployment ran on. |
+| <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="deploymenttriggerer"></a>`triggerer` | [`UserCore`](#usercore) | User who executed the deployment. |
+| <a id="deploymentupdatedat"></a>`updatedAt` | [`Time`](#time) | When the deployment record was updated. |
+
+### `DeploymentDetails`
+
+The details of the deployment.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <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. |
+
+### `DeploymentTag`
+
+Tags for a given deployment.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="deploymenttagname"></a>`name` | [`String`](#string) | Name of this git tag. |
+| <a id="deploymenttagpath"></a>`path` | [`String`](#string) | Path for this tag. |
+
### `Design`
A single design.
@@ -11373,14 +11676,51 @@ Describes where code is deployed for a project.
| Name | Type | Description |
| ---- | ---- | ----------- |
+| <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="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. |
| <a id="environmentlatestopenedmostseverealert"></a>`latestOpenedMostSevereAlert` | [`AlertManagementAlert`](#alertmanagementalert) | Most severe open alert for the environment. If multiple alerts have equal severity, the most recent is returned. |
| <a id="environmentname"></a>`name` | [`String!`](#string) | Human-readable name of the environment. |
| <a id="environmentpath"></a>`path` | [`String!`](#string) | Path to the environment. |
+| <a id="environmentslug"></a>`slug` | [`String`](#string) | Slug of the environment. |
| <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. |
#### Fields with arguments
+##### `Environment.deployments`
+
+Deployments of the environment. This field can only be resolved for one project in any single request.
+
+Returns [`DeploymentConnection`](#deploymentconnection).
+
+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="environmentdeploymentsorderby"></a>`orderBy` | [`DeploymentsOrderByInput`](#deploymentsorderbyinput) | Order by a specified field. |
+| <a id="environmentdeploymentsstatuses"></a>`statuses` | [`[DeploymentStatus!]`](#deploymentstatus) | Statuses of the deployments. |
+
+##### `Environment.lastDeployment`
+
+Last deployment of the environment.
+
+Returns [`Deployment`](#deployment).
+
+###### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="environmentlastdeploymentstatus"></a>`status` | [`DeploymentStatus!`](#deploymentstatus) | Status of the Deployment. |
+
##### `Environment.metricsDashboard`
Metrics dashboard schema for the environment.
@@ -11411,6 +11751,7 @@ Represents an epic.
| <a id="epiccolor"></a>`color` | [`String`](#string) | Color of the epic. Returns `null` if `epic_color_highlight` feature flag is disabled. |
| <a id="epicconfidential"></a>`confidential` | [`Boolean`](#boolean) | Indicates if the epic is confidential. |
| <a id="epiccreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp of when the epic was created. |
+| <a id="epicdefaultprojectforissuecreation"></a>`defaultProjectForIssueCreation` | [`Project`](#project) | Default Project for issue creation. Based on the project the user created the last issue in. |
| <a id="epicdescendantcounts"></a>`descendantCounts` | [`EpicDescendantCount`](#epicdescendantcount) | Number of open and closed descendant epics and issues. |
| <a id="epicdescendantweightsum"></a>`descendantWeightSum` | [`EpicDescendantWeights`](#epicdescendantweights) | Total weight of open and closed issues in the epic and its descendants. |
| <a id="epicdescription"></a>`description` | [`String`](#string) | Description of the epic. |
@@ -11479,7 +11820,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="epicancestorsiid"></a>`iid` | [`ID`](#id) | IID of the epic, e.g., "1". |
| <a id="epicancestorsiidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
| <a id="epicancestorsiids"></a>`iids` | [`[ID!]`](#id) | List of IIDs of epics, e.g., `[1, 2]`. |
-| <a id="epicancestorsin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument. |
+| <a id="epicancestorsin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'. |
| <a id="epicancestorsincludeancestorgroups"></a>`includeAncestorGroups` | [`Boolean`](#boolean) | Include epics from ancestor groups. |
| <a id="epicancestorsincludedescendantgroups"></a>`includeDescendantGroups` | [`Boolean`](#boolean) | Include epics from descendant groups. |
| <a id="epicancestorslabelname"></a>`labelName` | [`[String!]`](#string) | Filter epics by labels. |
@@ -11517,7 +11858,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="epicchildreniid"></a>`iid` | [`ID`](#id) | IID of the epic, e.g., "1". |
| <a id="epicchildreniidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
| <a id="epicchildreniids"></a>`iids` | [`[ID!]`](#id) | List of IIDs of epics, e.g., `[1, 2]`. |
-| <a id="epicchildrenin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument. |
+| <a id="epicchildrenin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'. |
| <a id="epicchildrenincludeancestorgroups"></a>`includeAncestorGroups` | [`Boolean`](#boolean) | Include epics from ancestor groups. |
| <a id="epicchildrenincludedescendantgroups"></a>`includeDescendantGroups` | [`Boolean`](#boolean) | Include epics from descendant groups. |
| <a id="epicchildrenlabelname"></a>`labelName` | [`[String!]`](#string) | Filter epics by labels. |
@@ -11665,6 +12006,7 @@ Relationship between an epic and an issue.
| <a id="epicissueepicissueid"></a>`epicIssueId` | [`ID!`](#id) | ID of the epic-issue relation. |
| <a id="epicissueescalationpolicy"></a>`escalationPolicy` | [`EscalationPolicyType`](#escalationpolicytype) | Escalation policy associated with the issue. Available for issues which support escalation. |
| <a id="epicissueescalationstatus"></a>`escalationStatus` | [`IssueEscalationStatus`](#issueescalationstatus) | Escalation status of the issue. |
+| <a id="epicissuehasepic"></a>`hasEpic` | [`Boolean!`](#boolean) | Indicates if the issue belongs to an epic. Can return true and not show an associated epic when the user has no access to the epic. |
| <a id="epicissuehealthstatus"></a>`healthStatus` | [`HealthStatus`](#healthstatus) | Current health status. |
| <a id="epicissuehidden"></a>`hidden` | [`Boolean`](#boolean) | Indicates the issue is hidden because the author has been banned. Will always return `null` if `ban_user_feature_flag` feature flag is disabled. |
| <a id="epicissuehumantimeestimate"></a>`humanTimeEstimate` | [`String`](#string) | Human-readable time estimate of the issue. |
@@ -12144,7 +12486,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="groupcontainerrepositoriescount"></a>`containerRepositoriesCount` | [`Int!`](#int) | Number of container repositories in the group. |
| <a id="groupcontainslockedprojects"></a>`containsLockedProjects` | [`Boolean!`](#boolean) | Includes at least one project where the repository size exceeds the limit. |
| <a id="groupcrossprojectpipelineavailable"></a>`crossProjectPipelineAvailable` | [`Boolean!`](#boolean) | Indicates if the cross_project_pipeline feature is available for the namespace. |
-| <a id="groupcustomemoji"></a>`customEmoji` | [`CustomEmojiConnection`](#customemojiconnection) | Custom emoji within this namespace. Available only when feature flag `custom_emoji` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice. (see [Connections](#connections)) |
+| <a id="groupcustomemoji"></a>`customEmoji` **{warning-solid}** | [`CustomEmojiConnection`](#customemojiconnection) | **Introduced** in 13.6. This feature is in Alpha. It can be changed or removed at any time. Custom emoji within this namespace. |
| <a id="groupdependencyproxyblobcount"></a>`dependencyProxyBlobCount` | [`Int!`](#int) | Number of dependency proxy blobs cached in the group. |
| <a id="groupdependencyproxyblobs"></a>`dependencyProxyBlobs` | [`DependencyProxyBlobConnection`](#dependencyproxyblobconnection) | Dependency Proxy blobs. (see [Connections](#connections)) |
| <a id="groupdependencyproxyimagecount"></a>`dependencyProxyImageCount` | [`Int!`](#int) | Number of dependency proxy images cached in the group. |
@@ -12367,7 +12709,7 @@ Returns [`Epic`](#epic).
| <a id="groupepiciid"></a>`iid` | [`ID`](#id) | IID of the epic, e.g., "1". |
| <a id="groupepiciidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
| <a id="groupepiciids"></a>`iids` | [`[ID!]`](#id) | List of IIDs of epics, e.g., `[1, 2]`. |
-| <a id="groupepicin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument. |
+| <a id="groupepicin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'. |
| <a id="groupepicincludeancestorgroups"></a>`includeAncestorGroups` | [`Boolean`](#boolean) | Include epics from ancestor groups. |
| <a id="groupepicincludedescendantgroups"></a>`includeDescendantGroups` | [`Boolean`](#boolean) | Include epics from descendant groups. |
| <a id="groupepiclabelname"></a>`labelName` | [`[String!]`](#string) | Filter epics by labels. |
@@ -12417,7 +12759,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="groupepicsiid"></a>`iid` | [`ID`](#id) | IID of the epic, e.g., "1". |
| <a id="groupepicsiidstartswith"></a>`iidStartsWith` | [`String`](#string) | Filter epics by IID for autocomplete. |
| <a id="groupepicsiids"></a>`iids` | [`[ID!]`](#id) | List of IIDs of epics, e.g., `[1, 2]`. |
-| <a id="groupepicsin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument. |
+| <a id="groupepicsin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'. |
| <a id="groupepicsincludeancestorgroups"></a>`includeAncestorGroups` | [`Boolean`](#boolean) | Include epics from ancestor groups. |
| <a id="groupepicsincludedescendantgroups"></a>`includeDescendantGroups` | [`Boolean`](#boolean) | Include epics from descendant groups. |
| <a id="groupepicslabelname"></a>`labelName` | [`[String!]`](#string) | Filter epics by labels. |
@@ -12450,6 +12792,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="groupgroupmembersaccesslevels"></a>`accessLevels` | [`[AccessLevelEnum!]`](#accesslevelenum) | Filter members by the given access levels. |
| <a id="groupgroupmembersrelations"></a>`relations` | [`[GroupMemberRelation!]`](#groupmemberrelation) | Filter members by the given member relations. |
| <a id="groupgroupmemberssearch"></a>`search` | [`String`](#string) | Search query. |
+| <a id="groupgroupmemberssort"></a>`sort` | [`MemberSort`](#membersort) | sort query. |
##### `Group.issues`
@@ -12477,8 +12820,10 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="groupissuescrmcontactid"></a>`crmContactId` | [`String`](#string) | ID of a contact assigned to the issues. |
| <a id="groupissuescrmorganizationid"></a>`crmOrganizationId` | [`String`](#string) | ID of an organization assigned to the issues. |
| <a id="groupissuesepicid"></a>`epicId` | [`String`](#string) | ID of an epic associated with the issues, "none" and "any" values are supported. |
+| <a id="groupissueshealthstatus"></a>`healthStatus` | [`HealthStatus`](#healthstatus) | Health status of the issue. |
| <a id="groupissuesiid"></a>`iid` | [`String`](#string) | IID of the issue. For example, "1". |
| <a id="groupissuesiids"></a>`iids` | [`[String!]`](#string) | List of IIDs of issues. For example, `["1", "2"]`. |
+| <a id="groupissuesin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'. |
| <a id="groupissuesincludearchived"></a>`includeArchived` | [`Boolean`](#boolean) | Return issues from archived projects. |
| <a id="groupissuesincludesubepics"></a>`includeSubepics` | [`Boolean`](#boolean) | Whether to include subepics when filtering issues by epicId. |
| <a id="groupissuesincludesubgroups"></a>`includeSubgroups` | [`Boolean`](#boolean) | Include issues belonging to subgroups. |
@@ -12653,6 +12998,19 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="groupmilestonestimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. |
| <a id="groupmilestonestitle"></a>`title` | [`String`](#string) | Title of the milestone. |
+##### `Group.organizationStateCounts`
+
+Counts of organizations by status for the group.
+
+Returns [`OrganizationStateCounts`](#organizationstatecounts).
+
+###### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="grouporganizationstatecountssearch"></a>`search` | [`String`](#string) | Search term to find organizations with. |
+| <a id="grouporganizationstatecountsstate"></a>`state` | [`CustomerRelationsOrganizationState`](#customerrelationsorganizationstate) | State of the organizations to search for. |
+
##### `Group.organizations`
Find organizations of this group.
@@ -12669,11 +13027,12 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="grouporganizationsids"></a>`ids` | [`[CustomerRelationsOrganizationID!]`](#customerrelationsorganizationid) | Filter organizations by IDs. |
| <a id="grouporganizationssearch"></a>`search` | [`String`](#string) | Search term used to find organizations with. |
+| <a id="grouporganizationssort"></a>`sort` | [`OrganizationSort`](#organizationsort) | Criteria to sort organizations by. |
| <a id="grouporganizationsstate"></a>`state` | [`CustomerRelationsOrganizationState`](#customerrelationsorganizationstate) | State of the organization to search for. |
##### `Group.packages`
-Packages of the group.
+Packages of the group. This field can only be resolved for one group in any single request.
Returns [`PackageConnection`](#packageconnection).
@@ -12727,7 +13086,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="grouprunnersactive"></a>`active` **{warning-solid}** | [`Boolean`](#boolean) | **Deprecated** in 14.8. This was renamed. Use: `paused`. |
-| <a id="grouprunnersmembership"></a>`membership` | [`RunnerMembershipFilter`](#runnermembershipfilter) | Control which runners to include in the results. |
+| <a id="grouprunnersmembership"></a>`membership` | [`CiRunnerMembershipFilter`](#cirunnermembershipfilter) | Control which runners to include in the results. |
| <a id="grouprunnerspaused"></a>`paused` | [`Boolean`](#boolean) | Filter runners by `paused` (true) or `active` (false) status. |
| <a id="grouprunnerssearch"></a>`search` | [`String`](#string) | Filter by full token or partial text in description field. |
| <a id="grouprunnerssort"></a>`sort` | [`CiRunnerSort`](#cirunnersort) | Sort order of results. |
@@ -12841,8 +13200,10 @@ Returns [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount).
| Name | Type | Description |
| ---- | ---- | ----------- |
+| <a id="groupvulnerabilityseveritiescountclusteragentid"></a>`clusterAgentId` | [`[ClustersAgentID!]`](#clustersagentid) | Filter vulnerabilities by `cluster_agent_id`. Vulnerabilities with a `reportType` of `cluster_image_scanning` are only included with this filter. |
| <a id="groupvulnerabilityseveritiescounthasissues"></a>`hasIssues` | [`Boolean`](#boolean) | Filter vulnerabilities that do or do not have issues. |
| <a id="groupvulnerabilityseveritiescounthasresolution"></a>`hasResolution` | [`Boolean`](#boolean) | Filter vulnerabilities that do or do not have a resolution. |
+| <a id="groupvulnerabilityseveritiescountimage"></a>`image` | [`[String!]`](#string) | Filter vulnerabilities by location image. When this filter is present, the response only matches entries for a `reportType` that includes `container_scanning`, `cluster_image_scanning`. |
| <a id="groupvulnerabilityseveritiescountprojectid"></a>`projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. |
| <a id="groupvulnerabilityseveritiescountreporttype"></a>`reportType` | [`[VulnerabilityReportType!]`](#vulnerabilityreporttype) | Filter vulnerabilities by report type. |
| <a id="groupvulnerabilityseveritiescountscanner"></a>`scanner` | [`[String!]`](#string) | Filter vulnerabilities by scanner. |
@@ -13090,7 +13451,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| Name | Type | Description |
| ---- | ---- | ----------- |
-| <a id="instancesecuritydashboardprojectssearch"></a>`search` | [`String`](#string) | Search query for project name, path, or description. |
+| <a id="instancesecuritydashboardprojectssearch"></a>`search` | [`String`](#string) | Search query, which can be for the project name, a path, or a description. |
##### `InstanceSecurityDashboard.vulnerabilitySeveritiesCount`
@@ -13102,8 +13463,10 @@ Returns [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount).
| Name | Type | Description |
| ---- | ---- | ----------- |
+| <a id="instancesecuritydashboardvulnerabilityseveritiescountclusteragentid"></a>`clusterAgentId` | [`[ClustersAgentID!]`](#clustersagentid) | Filter vulnerabilities by `cluster_agent_id`. Vulnerabilities with a `reportType` of `cluster_image_scanning` are only included with this filter. |
| <a id="instancesecuritydashboardvulnerabilityseveritiescounthasissues"></a>`hasIssues` | [`Boolean`](#boolean) | Filter vulnerabilities that do or do not have issues. |
| <a id="instancesecuritydashboardvulnerabilityseveritiescounthasresolution"></a>`hasResolution` | [`Boolean`](#boolean) | Filter vulnerabilities that do or do not have a resolution. |
+| <a id="instancesecuritydashboardvulnerabilityseveritiescountimage"></a>`image` | [`[String!]`](#string) | Filter vulnerabilities by location image. When this filter is present, the response only matches entries for a `reportType` that includes `container_scanning`, `cluster_image_scanning`. |
| <a id="instancesecuritydashboardvulnerabilityseveritiescountprojectid"></a>`projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. |
| <a id="instancesecuritydashboardvulnerabilityseveritiescountreporttype"></a>`reportType` | [`[VulnerabilityReportType!]`](#vulnerabilityreporttype) | Filter vulnerabilities by report type. |
| <a id="instancesecuritydashboardvulnerabilityseveritiescountscanner"></a>`scanner` | [`[String!]`](#string) | Filter vulnerabilities by scanner. |
@@ -13155,6 +13518,7 @@ Describes an issuable resource link for incident issues.
| <a id="issueepic"></a>`epic` | [`Epic`](#epic) | Epic to which this issue belongs. |
| <a id="issueescalationpolicy"></a>`escalationPolicy` | [`EscalationPolicyType`](#escalationpolicytype) | Escalation policy associated with the issue. Available for issues which support escalation. |
| <a id="issueescalationstatus"></a>`escalationStatus` | [`IssueEscalationStatus`](#issueescalationstatus) | Escalation status of the issue. |
+| <a id="issuehasepic"></a>`hasEpic` | [`Boolean!`](#boolean) | Indicates if the issue belongs to an epic. Can return true and not show an associated epic when the user has no access to the epic. |
| <a id="issuehealthstatus"></a>`healthStatus` | [`HealthStatus`](#healthstatus) | Current health status. |
| <a id="issuehidden"></a>`hidden` | [`Boolean`](#boolean) | Indicates the issue is hidden because the author has been banned. Will always return `null` if `ban_user_feature_flag` feature flag is disabled. |
| <a id="issuehumantimeestimate"></a>`humanTimeEstimate` | [`String`](#string) | Human-readable time estimate of the issue. |
@@ -13501,6 +13865,19 @@ Maven metadata.
| <a id="mavenmetadatapath"></a>`path` | [`String!`](#string) | Path of the Maven package. |
| <a id="mavenmetadataupdatedat"></a>`updatedAt` | [`Time!`](#time) | Date of most recent update. |
+### `MergeAccessLevel`
+
+Represents the merge access level of a branch protection.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mergeaccesslevelaccesslevel"></a>`accessLevel` | [`Int!`](#int) | GitLab::Access level. |
+| <a id="mergeaccesslevelaccessleveldescription"></a>`accessLevelDescription` | [`String!`](#string) | Human readable representation for this access level. |
+| <a id="mergeaccesslevelgroup"></a>`group` | [`Group`](#group) | Group associated with this access level. |
+| <a id="mergeaccessleveluser"></a>`user` | [`UserCore`](#usercore) | User associated with this access level. |
+
### `MergeRequest`
#### Fields
@@ -13579,6 +13956,7 @@ Maven metadata.
| <a id="mergerequestsquashonmerge"></a>`squashOnMerge` | [`Boolean!`](#boolean) | Indicates if squash on merge is enabled. |
| <a id="mergerequeststate"></a>`state` | [`MergeRequestState!`](#mergerequeststate) | State of the merge request. |
| <a id="mergerequestsubscribed"></a>`subscribed` | [`Boolean!`](#boolean) | Indicates if the currently logged in user is subscribed to this merge request. |
+| <a id="mergerequestsuggestedreviewers"></a>`suggestedReviewers` **{warning-solid}** | [`SuggestedReviewersType`](#suggestedreviewerstype) | **Introduced** in 15.4. This feature is in Alpha. It can be changed or removed at any time. Suggested reviewers for merge request. Returns `null` if `suggested_reviewers` feature flag is disabled. This flag is disabled by default and only available on GitLab.com because the feature is experimental and is subject to change without notice. |
| <a id="mergerequesttargetbranch"></a>`targetBranch` | [`String!`](#string) | Target branch of the merge request. |
| <a id="mergerequesttargetbranchexists"></a>`targetBranchExists` | [`Boolean!`](#boolean) | Indicates if the target branch of the merge request exists. |
| <a id="mergerequesttargetproject"></a>`targetProject` | [`Project!`](#project) | Target project of the merge request. |
@@ -14953,6 +15331,18 @@ Active period time range for on-call rotation.
| <a id="oncallrotationactiveperiodtypeendtime"></a>`endTime` | [`String`](#string) | End of the rotation active period. |
| <a id="oncallrotationactiveperiodtypestarttime"></a>`startTime` | [`String`](#string) | Start of the rotation active period. |
+### `OrganizationStateCounts`
+
+Represents the total number of organizations for the represented states.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="organizationstatecountsactive"></a>`active` | [`Int`](#int) | Number of organizations with state `ACTIVE`. |
+| <a id="organizationstatecountsall"></a>`all` | [`Int`](#int) | Number of organizations with state `ALL`. |
+| <a id="organizationstatecountsinactive"></a>`inactive` | [`Int`](#int) | Number of organizations with state `INACTIVE`. |
+
### `Package`
Represents a package with pipelines in the Package Registry.
@@ -15047,6 +15437,7 @@ Represents a package details in the Package Registry.
| <a id="packagedetailstypecreatedat"></a>`createdAt` | [`Time!`](#time) | Date of creation. |
| <a id="packagedetailstypedependencylinks"></a>`dependencyLinks` | [`PackageDependencyLinkConnection`](#packagedependencylinkconnection) | Dependency link. (see [Connections](#connections)) |
| <a id="packagedetailstypeid"></a>`id` | [`PackagesPackageID!`](#packagespackageid) | ID of the package. |
+| <a id="packagedetailstypelastdownloadedat"></a>`lastDownloadedAt` | [`Time`](#time) | Last time that a file of this package was downloaded. |
| <a id="packagedetailstypemavenurl"></a>`mavenUrl` | [`String`](#string) | Url of the Maven project endpoint. |
| <a id="packagedetailstypemetadata"></a>`metadata` | [`PackageMetadata`](#packagemetadata) | Package metadata. |
| <a id="packagedetailstypename"></a>`name` | [`String!`](#string) | Name of the package. |
@@ -15438,8 +15829,9 @@ Represents vulnerability finding of a security report on the pipeline.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="pipelinesecurityreportfindingassets"></a>`assets` | [`[AssetType!]`](#assettype) | List of assets associated with the vulnerability. |
-| <a id="pipelinesecurityreportfindingconfidence"></a>`confidence` | [`String`](#string) | Type of the security report that found the vulnerability. |
+| <a id="pipelinesecurityreportfindingconfidence"></a>`confidence` **{warning-solid}** | [`String`](#string) | **Deprecated** in 15.4. This field will be removed from the Finding domain model. |
| <a id="pipelinesecurityreportfindingdescription"></a>`description` | [`String`](#string) | Description of the vulnerability finding. |
+| <a id="pipelinesecurityreportfindingdescriptionhtml"></a>`descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. |
| <a id="pipelinesecurityreportfindingevidence"></a>`evidence` | [`VulnerabilityEvidence`](#vulnerabilityevidence) | Evidence for the vulnerability. |
| <a id="pipelinesecurityreportfindingfalsepositive"></a>`falsePositive` | [`Boolean`](#boolean) | Indicates whether the vulnerability is a false positive. |
| <a id="pipelinesecurityreportfindingidentifiers"></a>`identifiers` | [`[VulnerabilityIdentifier!]!`](#vulnerabilityidentifier) | Identifiers of the vulnerability finding. |
@@ -15469,6 +15861,7 @@ Represents vulnerability finding of a security report on the pipeline.
| <a id="projectarchived"></a>`archived` | [`Boolean`](#boolean) | Indicates the archived status of the project. |
| <a id="projectautoclosereferencedissues"></a>`autocloseReferencedIssues` | [`Boolean`](#boolean) | Indicates if issues referenced by merge requests and commits within the default branch are closed automatically. |
| <a id="projectavatarurl"></a>`avatarUrl` | [`String`](#string) | URL to avatar image file of the project. |
+| <a id="projectbranchrules"></a>`branchRules` | [`BranchRuleConnection`](#branchruleconnection) | Branch rules configured for the project. (see [Connections](#connections)) |
| <a id="projectcicdsettings"></a>`ciCdSettings` | [`ProjectCiCdSetting`](#projectcicdsetting) | CI/CD settings for the project. |
| <a id="projectciconfigpathordefault"></a>`ciConfigPathOrDefault` | [`String!`](#string) | Path of the CI configuration file. |
| <a id="projectcijobtokenscope"></a>`ciJobTokenScope` | [`CiJobTokenScopeType`](#cijobtokenscopetype) | The CI Job Tokens scope of access. |
@@ -15671,6 +16064,22 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="projectboardsid"></a>`id` | [`BoardID`](#boardid) | Find a board by its ID. |
+##### `Project.ciConfigVariables`
+
+CI/CD config variable.
+
+WARNING:
+**Introduced** in 15.3.
+This feature is in Alpha. It can be changed or removed at any time.
+
+Returns [`[CiConfigVariable!]`](#ciconfigvariable).
+
+###### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="projectciconfigvariablessha"></a>`sha` | [`String!`](#string) | Sha. |
+
##### `Project.ciTemplate`
Find a single CI/CD template by name.
@@ -15787,6 +16196,18 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="projectdastsitevalidationsnormalizedtargeturls"></a>`normalizedTargetUrls` | [`[String!]`](#string) | Normalized URL of the target to be scanned. |
| <a id="projectdastsitevalidationsstatus"></a>`status` | [`DastSiteValidationStatusEnum`](#dastsitevalidationstatusenum) | Status of the site validation. |
+##### `Project.deployment`
+
+Details of the deployment of the project.
+
+Returns [`DeploymentDetails`](#deploymentdetails).
+
+###### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="projectdeploymentiid"></a>`iid` | [`ID!`](#id) | Project-level internal ID of the Deployment. |
+
##### `Project.environment`
A single environment of the project.
@@ -15931,8 +16352,10 @@ Returns [`Issue`](#issue).
| <a id="projectissuecrmcontactid"></a>`crmContactId` | [`String`](#string) | ID of a contact assigned to the issues. |
| <a id="projectissuecrmorganizationid"></a>`crmOrganizationId` | [`String`](#string) | ID of an organization assigned to the issues. |
| <a id="projectissueepicid"></a>`epicId` | [`String`](#string) | ID of an epic associated with the issues, "none" and "any" values are supported. |
+| <a id="projectissuehealthstatus"></a>`healthStatus` | [`HealthStatus`](#healthstatus) | Health status of the issue. |
| <a id="projectissueiid"></a>`iid` | [`String`](#string) | IID of the issue. For example, "1". |
| <a id="projectissueiids"></a>`iids` | [`[String!]`](#string) | List of IIDs of issues. For example, `["1", "2"]`. |
+| <a id="projectissuein"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'. |
| <a id="projectissueincludesubepics"></a>`includeSubepics` | [`Boolean`](#boolean) | Whether to include subepics when filtering issues by epicId. |
| <a id="projectissueiterationid"></a>`iterationId` | [`[ID]`](#id) | List of iteration Global IDs applied to the issue. |
| <a id="projectissueiterationwildcardid"></a>`iterationWildcardId` | [`IterationWildcardId`](#iterationwildcardid) | Filter by iteration ID wildcard. |
@@ -15974,6 +16397,7 @@ Returns [`IssueStatusCountsType`](#issuestatuscountstype).
| <a id="projectissuestatuscountscrmorganizationid"></a>`crmOrganizationId` | [`String`](#string) | ID of an organization assigned to the issues. |
| <a id="projectissuestatuscountsiid"></a>`iid` | [`String`](#string) | IID of the issue. For example, "1". |
| <a id="projectissuestatuscountsiids"></a>`iids` | [`[String!]`](#string) | List of IIDs of issues. For example, `["1", "2"]`. |
+| <a id="projectissuestatuscountsin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'. |
| <a id="projectissuestatuscountslabelname"></a>`labelName` | [`[String]`](#string) | Labels applied to this issue. |
| <a id="projectissuestatuscountsmilestonetitle"></a>`milestoneTitle` | [`[String]`](#string) | Milestone applied to this issue. |
| <a id="projectissuestatuscountsmilestonewildcardid"></a>`milestoneWildcardId` | [`MilestoneWildcardId`](#milestonewildcardid) | Filter issues by milestone ID wildcard. |
@@ -16012,8 +16436,10 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="projectissuescrmcontactid"></a>`crmContactId` | [`String`](#string) | ID of a contact assigned to the issues. |
| <a id="projectissuescrmorganizationid"></a>`crmOrganizationId` | [`String`](#string) | ID of an organization assigned to the issues. |
| <a id="projectissuesepicid"></a>`epicId` | [`String`](#string) | ID of an epic associated with the issues, "none" and "any" values are supported. |
+| <a id="projectissueshealthstatus"></a>`healthStatus` | [`HealthStatus`](#healthstatus) | Health status of the issue. |
| <a id="projectissuesiid"></a>`iid` | [`String`](#string) | IID of the issue. For example, "1". |
| <a id="projectissuesiids"></a>`iids` | [`[String!]`](#string) | List of IIDs of issues. For example, `["1", "2"]`. |
+| <a id="projectissuesin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'. |
| <a id="projectissuesincludesubepics"></a>`includeSubepics` | [`Boolean`](#boolean) | Whether to include subepics when filtering issues by epicId. |
| <a id="projectissuesiterationid"></a>`iterationId` | [`[ID]`](#id) | List of iteration Global IDs applied to the issue. |
| <a id="projectissuesiterationwildcardid"></a>`iterationWildcardId` | [`IterationWildcardId`](#iterationwildcardid) | Filter by iteration ID wildcard. |
@@ -16080,6 +16506,18 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="projectiterationstimeframe"></a>`timeframe` | [`Timeframe`](#timeframe) | List items overlapping the given timeframe. |
| <a id="projectiterationstitle"></a>`title` **{warning-solid}** | [`String`](#string) | **Deprecated** in 15.4. The argument will be removed in 15.4. Please use `search` and `in` fields instead. |
+##### `Project.job`
+
+One job belonging to the project, selected by ID.
+
+Returns [`CiJob`](#cijob).
+
+###### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="projectjobid"></a>`id` | [`JobID!`](#jobid) | ID of the job. |
+
##### `Project.jobs`
Jobs of a project. This field can only be resolved for one project in any single request.
@@ -16301,6 +16739,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="projectprojectmembersrelations"></a>`relations` | [`[ProjectMemberRelation!]`](#projectmemberrelation) | Filter members by the given member relations. |
| <a id="projectprojectmemberssearch"></a>`search` | [`String`](#string) | Search query. |
+| <a id="projectprojectmemberssort"></a>`sort` | [`MemberSort`](#membersort) | sort query. |
##### `Project.release`
@@ -16546,8 +16985,10 @@ Returns [`VulnerabilitySeveritiesCount`](#vulnerabilityseveritiescount).
| Name | Type | Description |
| ---- | ---- | ----------- |
+| <a id="projectvulnerabilityseveritiescountclusteragentid"></a>`clusterAgentId` | [`[ClustersAgentID!]`](#clustersagentid) | Filter vulnerabilities by `cluster_agent_id`. Vulnerabilities with a `reportType` of `cluster_image_scanning` are only included with this filter. |
| <a id="projectvulnerabilityseveritiescounthasissues"></a>`hasIssues` | [`Boolean`](#boolean) | Filter vulnerabilities that do or do not have issues. |
| <a id="projectvulnerabilityseveritiescounthasresolution"></a>`hasResolution` | [`Boolean`](#boolean) | Filter vulnerabilities that do or do not have a resolution. |
+| <a id="projectvulnerabilityseveritiescountimage"></a>`image` | [`[String!]`](#string) | Filter vulnerabilities by location image. When this filter is present, the response only matches entries for a `reportType` that includes `container_scanning`, `cluster_image_scanning`. |
| <a id="projectvulnerabilityseveritiescountprojectid"></a>`projectId` | [`[ID!]`](#id) | Filter vulnerabilities by project. |
| <a id="projectvulnerabilityseveritiescountreporttype"></a>`reportType` | [`[VulnerabilityReportType!]`](#vulnerabilityreporttype) | Filter vulnerabilities by report type. |
| <a id="projectvulnerabilityseveritiescountscanner"></a>`scanner` | [`[String!]`](#string) | Filter vulnerabilities by scanner. |
@@ -16591,6 +17032,7 @@ four standard [pagination arguments](#connection-pagination-arguments):
| ---- | ---- | ----------- |
| <a id="projectworkitemsiid"></a>`iid` | [`String`](#string) | IID of the issue. For example, "1". |
| <a id="projectworkitemsiids"></a>`iids` | [`[String!]`](#string) | List of IIDs of work items. For example, `["1", "2"]`. |
+| <a id="projectworkitemsin"></a>`in` | [`[IssuableSearchableField!]`](#issuablesearchablefield) | Specify the fields to perform the search in. Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.'. |
| <a id="projectworkitemssearch"></a>`search` | [`String`](#string) | Search query for title or description. |
| <a id="projectworkitemssort"></a>`sort` | [`WorkItemSort`](#workitemsort) | Sort work items by this criteria. |
| <a id="projectworkitemsstate"></a>`state` | [`IssuableState`](#issuablestate) | Current state of this work item. |
@@ -16743,6 +17185,19 @@ The alert condition for Prometheus.
| <a id="prometheusalerthumanizedtext"></a>`humanizedText` | [`String!`](#string) | Human-readable text of the alert condition. |
| <a id="prometheusalertid"></a>`id` | [`ID!`](#id) | ID of the alert condition. |
+### `PushAccessLevel`
+
+Represents the push access level of a branch protection.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="pushaccesslevelaccesslevel"></a>`accessLevel` | [`Int!`](#int) | GitLab::Access level. |
+| <a id="pushaccesslevelaccessleveldescription"></a>`accessLevelDescription` | [`String!`](#string) | Human readable representation for this access level. |
+| <a id="pushaccesslevelgroup"></a>`group` | [`Group`](#group) | Group associated with this access level. |
+| <a id="pushaccessleveluser"></a>`user` | [`UserCore`](#usercore) | User associated with this access level. |
+
### `PushRules`
Represents rules that commit pushes must follow.
@@ -17629,6 +18084,18 @@ Represents an entry from the future subscriptions.
| <a id="subscriptionfutureentrytype"></a>`type` | [`String!`](#string) | Type of license the subscription will yield. |
| <a id="subscriptionfutureentryusersinlicensecount"></a>`usersInLicenseCount` | [`Int`](#int) | Number of paid user seats. |
+### `SuggestedReviewersType`
+
+Represents a Suggested Reviewers result set.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="suggestedreviewerstypereviewers"></a>`reviewers` | [`[String!]!`](#string) | List of reviewers. |
+| <a id="suggestedreviewerstypetopn"></a>`topN` | [`Int`](#int) | Number of reviewers returned. |
+| <a id="suggestedreviewerstypeversion"></a>`version` | [`String`](#string) | Suggested reviewer version. |
+
### `TaskCompletionStatus`
Completion status of tasks.
@@ -18785,6 +19252,7 @@ Represents a vulnerability scanner.
| <a id="vulnerabilityscannerid"></a>`id` | [`ID`](#id) | ID of the scanner. |
| <a id="vulnerabilityscannername"></a>`name` | [`String`](#string) | Name of the vulnerability scanner. |
| <a id="vulnerabilityscannerreporttype"></a>`reportType` | [`VulnerabilityReportType`](#vulnerabilityreporttype) | Type of the vulnerability report. |
+| <a id="vulnerabilityscannerreporttypehumanized"></a>`reportTypeHumanized` | [`String`](#string) | Humanized type of the vulnerability report. |
| <a id="vulnerabilityscannervendor"></a>`vendor` | [`String`](#string) | Vendor of the vulnerability scanner. |
### `VulnerabilitySeveritiesCount`
@@ -18918,6 +19386,9 @@ Represents a description widget.
| ---- | ---- | ----------- |
| <a id="workitemwidgetdescriptiondescription"></a>`description` | [`String`](#string) | Description of the work item. |
| <a id="workitemwidgetdescriptiondescriptionhtml"></a>`descriptionHtml` | [`String`](#string) | The GitLab Flavored Markdown rendering of `description`. |
+| <a id="workitemwidgetdescriptionedited"></a>`edited` | [`Boolean!`](#boolean) | Whether the description has been edited since the work item was created. |
+| <a id="workitemwidgetdescriptionlasteditedat"></a>`lastEditedAt` | [`Time`](#time) | Timestamp of when the work item's description was last edited. |
+| <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. |
### `WorkItemWidgetHierarchy`
@@ -18932,6 +19403,17 @@ Represents a hierarchy widget.
| <a id="workitemwidgethierarchyparent"></a>`parent` | [`WorkItem`](#workitem) | Parent work item. |
| <a id="workitemwidgethierarchytype"></a>`type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. |
+### `WorkItemWidgetIteration`
+
+Represents an iteration widget.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="workitemwidgetiterationiteration"></a>`iteration` | [`Iteration`](#iteration) | Iteration of the work item. |
+| <a id="workitemwidgetiterationtype"></a>`type` | [`WorkItemWidgetType`](#workitemwidgettype) | Widget type. |
+
### `WorkItemWidgetLabels`
Represents the labels widget.
@@ -19207,6 +19689,15 @@ 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. |
+### `CiRunnerMembershipFilter`
+
+Values for filtering runners in namespaces. The previous type name `RunnerMembershipFilter` was deprecated in 15.4.
+
+| Value | Description |
+| ----- | ----------- |
+| <a id="cirunnermembershipfilterdescendants"></a>`DESCENDANTS` | Include runners that have either a direct or inherited relationship. These runners can be specific to a project or a group. |
+| <a id="cirunnermembershipfilterdirect"></a>`DIRECT` | Include runners that have a direct relationship. |
+
### `CiRunnerSort`
Values for sorting runners.
@@ -19339,18 +19830,18 @@ Values for sorting contacts.
| ----- | ----------- |
| <a id="contactsortcreated_asc"></a>`CREATED_ASC` | Created at ascending order. |
| <a id="contactsortcreated_desc"></a>`CREATED_DESC` | Created at descending order. |
-| <a id="contactsortdescription_asc"></a>`DESCRIPTION_ASC` | Description by ascending order. |
-| <a id="contactsortdescription_desc"></a>`DESCRIPTION_DESC` | Description by descending order. |
-| <a id="contactsortemail_asc"></a>`EMAIL_ASC` | Email by ascending order. |
-| <a id="contactsortemail_desc"></a>`EMAIL_DESC` | Email by descending order. |
-| <a id="contactsortfirst_name_asc"></a>`FIRST_NAME_ASC` | First name by ascending order. |
-| <a id="contactsortfirst_name_desc"></a>`FIRST_NAME_DESC` | First name by descending order. |
-| <a id="contactsortlast_name_asc"></a>`LAST_NAME_ASC` | Last name by ascending order. |
-| <a id="contactsortlast_name_desc"></a>`LAST_NAME_DESC` | Last name by descending order. |
-| <a id="contactsortorganization_asc"></a>`ORGANIZATION_ASC` | Organization by ascending order. |
-| <a id="contactsortorganization_desc"></a>`ORGANIZATION_DESC` | Organization by descending order. |
-| <a id="contactsortphone_asc"></a>`PHONE_ASC` | Phone by ascending order. |
-| <a id="contactsortphone_desc"></a>`PHONE_DESC` | Phone by descending order. |
+| <a id="contactsortdescription_asc"></a>`DESCRIPTION_ASC` | Description in ascending order. |
+| <a id="contactsortdescription_desc"></a>`DESCRIPTION_DESC` | Description in descending order. |
+| <a id="contactsortemail_asc"></a>`EMAIL_ASC` | Email in ascending order. |
+| <a id="contactsortemail_desc"></a>`EMAIL_DESC` | Email in descending order. |
+| <a id="contactsortfirst_name_asc"></a>`FIRST_NAME_ASC` | First name in ascending order. |
+| <a id="contactsortfirst_name_desc"></a>`FIRST_NAME_DESC` | First name in descending order. |
+| <a id="contactsortlast_name_asc"></a>`LAST_NAME_ASC` | Last name in ascending order. |
+| <a id="contactsortlast_name_desc"></a>`LAST_NAME_DESC` | Last name in descending order. |
+| <a id="contactsortorganization_asc"></a>`ORGANIZATION_ASC` | Organization in ascending order. |
+| <a id="contactsortorganization_desc"></a>`ORGANIZATION_DESC` | Organization in descending order. |
+| <a id="contactsortphone_asc"></a>`PHONE_ASC` | Phone in ascending order. |
+| <a id="contactsortphone_desc"></a>`PHONE_DESC` | Phone in descending order. |
| <a id="contactsortupdated_asc"></a>`UPDATED_ASC` | Updated at ascending order. |
| <a id="contactsortupdated_desc"></a>`UPDATED_DESC` | Updated at descending order. |
| <a id="contactsortcreated_asc"></a>`created_asc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `CREATED_ASC`. |
@@ -19447,8 +19938,9 @@ Values for sorting tags.
| Value | Description |
| ----- | ----------- |
-| <a id="customerrelationsorganizationstateactive"></a>`active` | Active organization. |
-| <a id="customerrelationsorganizationstateinactive"></a>`inactive` | Inactive organization. |
+| <a id="customerrelationsorganizationstateactive"></a>`active` | Active organizations. |
+| <a id="customerrelationsorganizationstateall"></a>`all` | All available organizations. |
+| <a id="customerrelationsorganizationstateinactive"></a>`inactive` | Inactive organizations. |
### `DastProfileCadenceUnit`
@@ -19552,6 +20044,20 @@ Weight of the data visualization palette.
| <a id="dependencyproxymanifeststatuspending_destruction"></a>`PENDING_DESTRUCTION` | Dependency proxy manifest has a status of pending_destruction. |
| <a id="dependencyproxymanifeststatusprocessing"></a>`PROCESSING` | Dependency proxy manifest has a status of processing. |
+### `DeploymentStatus`
+
+All deployment statuses.
+
+| Value | Description |
+| ----- | ----------- |
+| <a id="deploymentstatusblocked"></a>`BLOCKED` | A deployment that is blocked. |
+| <a id="deploymentstatuscanceled"></a>`CANCELED` | A deployment that is canceled. |
+| <a id="deploymentstatuscreated"></a>`CREATED` | A deployment that is created. |
+| <a id="deploymentstatusfailed"></a>`FAILED` | A deployment that is failed. |
+| <a id="deploymentstatusrunning"></a>`RUNNING` | A deployment that is running. |
+| <a id="deploymentstatusskipped"></a>`SKIPPED` | A deployment that is skipped. |
+| <a id="deploymentstatussuccess"></a>`SUCCESS` | A deployment that is success. |
+
### `DeploymentTier`
All environment deployment tiers.
@@ -19595,6 +20101,7 @@ Detailed representation of whether a GitLab merge request can be merged.
| <a id="detailedmergestatusbroken_status"></a>`BROKEN_STATUS` | Can not merge the source into the target branch, potential conflict. |
| <a id="detailedmergestatuschecking"></a>`CHECKING` | Currently checking for mergeability. |
| <a id="detailedmergestatusci_must_pass"></a>`CI_MUST_PASS` | Pipeline must succeed before merging. |
+| <a id="detailedmergestatusci_still_running"></a>`CI_STILL_RUNNING` | Pipeline is still running. |
| <a id="detailedmergestatusdiscussions_not_resolved"></a>`DISCUSSIONS_NOT_RESOLVED` | Discussions must be resolved before merging. |
| <a id="detailedmergestatusdraft_status"></a>`DRAFT_STATUS` | Merge request must not be draft before merging. |
| <a id="detailedmergestatusmergeable"></a>`MERGEABLE` | Branch can be merged. |
@@ -19985,6 +20492,25 @@ Possible identifier types for a measurement.
| <a id="measurementidentifierprojects"></a>`PROJECTS` | Project count. |
| <a id="measurementidentifierusers"></a>`USERS` | User count. |
+### `MemberSort`
+
+Values for sorting members.
+
+| Value | Description |
+| ----- | ----------- |
+| <a id="membersortaccess_level_asc"></a>`ACCESS_LEVEL_ASC` | Access level ascending order. |
+| <a id="membersortaccess_level_desc"></a>`ACCESS_LEVEL_DESC` | Access level descending order. |
+| <a id="membersortcreated_asc"></a>`CREATED_ASC` | Created at ascending order. |
+| <a id="membersortcreated_desc"></a>`CREATED_DESC` | Created at descending order. |
+| <a id="membersortupdated_asc"></a>`UPDATED_ASC` | Updated at ascending order. |
+| <a id="membersortupdated_desc"></a>`UPDATED_DESC` | Updated at descending order. |
+| <a id="membersortuser_full_name_asc"></a>`USER_FULL_NAME_ASC` | User's full name ascending order. |
+| <a id="membersortuser_full_name_desc"></a>`USER_FULL_NAME_DESC` | User's full name descending order. |
+| <a id="membersortcreated_asc"></a>`created_asc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `CREATED_ASC`. |
+| <a id="membersortcreated_desc"></a>`created_desc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `CREATED_DESC`. |
+| <a id="membersortupdated_asc"></a>`updated_asc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `UPDATED_ASC`. |
+| <a id="membersortupdated_desc"></a>`updated_desc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `UPDATED_DESC`. |
+
### `MergeRequestNewState`
New state to apply to a merge request.
@@ -20000,7 +20526,6 @@ State of a review of a GitLab merge request.
| Value | Description |
| ----- | ----------- |
-| <a id="mergerequestreviewstateattention_requested"></a>`ATTENTION_REQUESTED` | The merge request is attention_requested. |
| <a id="mergerequestreviewstatereviewed"></a>`REVIEWED` | The merge request is reviewed. |
| <a id="mergerequestreviewstateunreviewed"></a>`UNREVIEWED` | The merge request is unreviewed. |
@@ -20166,6 +20691,27 @@ Rotation length unit of an on-call rotation.
| <a id="oncallrotationunitenumhours"></a>`HOURS` | Hours. |
| <a id="oncallrotationunitenumweeks"></a>`WEEKS` | Weeks. |
+### `OrganizationSort`
+
+Values for sorting organizations.
+
+| Value | Description |
+| ----- | ----------- |
+| <a id="organizationsortcreated_asc"></a>`CREATED_ASC` | Created at ascending order. |
+| <a id="organizationsortcreated_desc"></a>`CREATED_DESC` | Created at descending order. |
+| <a id="organizationsortdefault_rate_asc"></a>`DEFAULT_RATE_ASC` | Default Rate in ascending order. |
+| <a id="organizationsortdefault_rate_desc"></a>`DEFAULT_RATE_DESC` | Default Rate in descending order. |
+| <a id="organizationsortdescription_asc"></a>`DESCRIPTION_ASC` | Description in ascending order. |
+| <a id="organizationsortdescription_desc"></a>`DESCRIPTION_DESC` | Description in descending order. |
+| <a id="organizationsortname_asc"></a>`NAME_ASC` | Name in ascending order. |
+| <a id="organizationsortname_desc"></a>`NAME_DESC` | Name in descending order. |
+| <a id="organizationsortupdated_asc"></a>`UPDATED_ASC` | Updated at ascending order. |
+| <a id="organizationsortupdated_desc"></a>`UPDATED_DESC` | Updated at descending order. |
+| <a id="organizationsortcreated_asc"></a>`created_asc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `CREATED_ASC`. |
+| <a id="organizationsortcreated_desc"></a>`created_desc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `CREATED_DESC`. |
+| <a id="organizationsortupdated_asc"></a>`updated_asc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `UPDATED_ASC`. |
+| <a id="organizationsortupdated_desc"></a>`updated_desc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `UPDATED_DESC`. |
+
### `PackageDependencyType`
| Value | Description |
@@ -20231,6 +20777,7 @@ Values for sorting package.
| <a id="packagetypeenumnpm"></a>`NPM` | Packages from the npm package manager. |
| <a id="packagetypeenumnuget"></a>`NUGET` | Packages from the Nuget package manager. |
| <a id="packagetypeenumpypi"></a>`PYPI` | Packages from the PyPI package manager. |
+| <a id="packagetypeenumrpm"></a>`RPM` | Packages from the Rpm package manager. |
| <a id="packagetypeenumrubygems"></a>`RUBYGEMS` | Packages from the Rubygems package manager. |
| <a id="packagetypeenumterraform_module"></a>`TERRAFORM_MODULE` | Packages from the Terraform Module package manager. |
@@ -20377,15 +20924,6 @@ Status of a requirement based on last test report.
| <a id="requirementstatusfiltermissing"></a>`MISSING` | Requirements without any test report. |
| <a id="requirementstatusfilterpassed"></a>`PASSED` | Passed test report. |
-### `RunnerMembershipFilter`
-
-Values for filtering runners in namespaces.
-
-| Value | Description |
-| ----- | ----------- |
-| <a id="runnermembershipfilterdescendants"></a>`DESCENDANTS` | Include runners that have either a direct relationship or a relationship with descendants. These can be project runners or group runners (in the case where group is queried). |
-| <a id="runnermembershipfilterdirect"></a>`DIRECT` | Include runners that have a direct relationship. |
-
### `SastUiComponentSize`
Size of UI component in SAST configuration page.
@@ -20547,6 +21085,15 @@ Common sort values.
| <a id="sortupdated_asc"></a>`updated_asc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `UPDATED_ASC`. |
| <a id="sortupdated_desc"></a>`updated_desc` **{warning-solid}** | **Deprecated** in 13.5. This was renamed. Use: `UPDATED_DESC`. |
+### `SortDirectionEnum`
+
+Values for sort direction.
+
+| Value | Description |
+| ----- | ----------- |
+| <a id="sortdirectionenumasc"></a>`ASC` | Ascending order. |
+| <a id="sortdirectionenumdesc"></a>`DESC` | Descending order. |
+
### `TestCaseStatus`
| Value | Description |
@@ -20644,8 +21191,6 @@ Name of the feature that the callout is for.
| Value | Description |
| ----- | ----------- |
| <a id="usercalloutfeaturenameenumactive_user_count_threshold"></a>`ACTIVE_USER_COUNT_THRESHOLD` | Callout feature name for active_user_count_threshold. |
-| <a id="usercalloutfeaturenameenumattention_requests_side_nav"></a>`ATTENTION_REQUESTS_SIDE_NAV` | Callout feature name for attention_requests_side_nav. |
-| <a id="usercalloutfeaturenameenumattention_requests_top_nav"></a>`ATTENTION_REQUESTS_TOP_NAV` | Callout feature name for attention_requests_top_nav. |
| <a id="usercalloutfeaturenameenumbuy_pipeline_minutes_notification_dot"></a>`BUY_PIPELINE_MINUTES_NOTIFICATION_DOT` | Callout feature name for buy_pipeline_minutes_notification_dot. |
| <a id="usercalloutfeaturenameenumcanary_deployment"></a>`CANARY_DEPLOYMENT` | Callout feature name for canary_deployment. |
| <a id="usercalloutfeaturenameenumci_deprecation_warning_for_types_keyword"></a>`CI_DEPRECATION_WARNING_FOR_TYPES_KEYWORD` | Callout feature name for ci_deprecation_warning_for_types_keyword. |
@@ -20658,6 +21203,7 @@ Name of the feature that the callout is for.
| <a id="usercalloutfeaturenameenumgeo_migrate_hashed_storage"></a>`GEO_MIGRATE_HASHED_STORAGE` | Callout feature name for geo_migrate_hashed_storage. |
| <a id="usercalloutfeaturenameenumgke_cluster_integration"></a>`GKE_CLUSTER_INTEGRATION` | Callout feature name for gke_cluster_integration. |
| <a id="usercalloutfeaturenameenumgold_trial_billings"></a>`GOLD_TRIAL_BILLINGS` | Callout feature name for gold_trial_billings. |
+| <a id="usercalloutfeaturenameenummerge_request_settings_moved_callout"></a>`MERGE_REQUEST_SETTINGS_MOVED_CALLOUT` | Callout feature name for merge_request_settings_moved_callout. |
| <a id="usercalloutfeaturenameenummr_experience_survey"></a>`MR_EXPERIENCE_SURVEY` | Callout feature name for mr_experience_survey. |
| <a id="usercalloutfeaturenameenumnamespace_storage_limit_banner_alert_threshold"></a>`NAMESPACE_STORAGE_LIMIT_BANNER_ALERT_THRESHOLD` | Callout feature name for namespace_storage_limit_banner_alert_threshold. |
| <a id="usercalloutfeaturenameenumnamespace_storage_limit_banner_error_threshold"></a>`NAMESPACE_STORAGE_LIMIT_BANNER_ERROR_THRESHOLD` | Callout feature name for namespace_storage_limit_banner_error_threshold. |
@@ -20899,6 +21445,7 @@ Type of a work item widget.
| <a id="workitemwidgettypeassignees"></a>`ASSIGNEES` | Assignees widget. |
| <a id="workitemwidgettypedescription"></a>`DESCRIPTION` | Description widget. |
| <a id="workitemwidgettypehierarchy"></a>`HIERARCHY` | Hierarchy widget. |
+| <a id="workitemwidgettypeiteration"></a>`ITERATION` | Iteration widget. |
| <a id="workitemwidgettypelabels"></a>`LABELS` | Labels widget. |
| <a id="workitemwidgettypestart_and_due_date"></a>`START_AND_DUE_DATE` | Start And Due Date widget. |
| <a id="workitemwidgettypeverification_status"></a>`VERIFICATION_STATUS` | Verification Status widget. |
@@ -20983,6 +21530,12 @@ A `CiBuildID` is a global ID. It is encoded as a string.
An example `CiBuildID` is: `"gid://gitlab/Ci::Build/1"`.
+### `CiJobArtifactID`
+
+A `CiJobArtifactID` is a global ID. It is encoded as a string.
+
+An example `CiJobArtifactID` is: `"gid://gitlab/Ci::JobArtifact/1"`.
+
### `CiPipelineID`
A `CiPipelineID` is a global ID. It is encoded as a string.
@@ -22155,6 +22708,7 @@ Implementations:
- [`WorkItemWidgetAssignees`](#workitemwidgetassignees)
- [`WorkItemWidgetDescription`](#workitemwidgetdescription)
- [`WorkItemWidgetHierarchy`](#workitemwidgethierarchy)
+- [`WorkItemWidgetIteration`](#workitemwidgetiteration)
- [`WorkItemWidgetLabels`](#workitemwidgetlabels)
- [`WorkItemWidgetStartAndDueDate`](#workitemwidgetstartandduedate)
- [`WorkItemWidgetVerificationStatus`](#workitemwidgetverificationstatus)
@@ -22301,6 +22855,17 @@ Input type for DastSiteProfile authentication.
| <a id="dastsiteprofileauthinputusername"></a>`username` | [`String`](#string) | Username to authenticate with on the target. |
| <a id="dastsiteprofileauthinputusernamefield"></a>`usernameField` | [`String`](#string) | Name of username field at the sign-in HTML form. |
+### `DeploymentsOrderByInput`
+
+Values for ordering deployments by a specific field.
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="deploymentsorderbyinputcreatedat"></a>`createdAt` | [`SortDirectionEnum`](#sortdirectionenum) | Order by Created time. |
+| <a id="deploymentsorderbyinputfinishedat"></a>`finishedAt` | [`SortDirectionEnum`](#sortdirectionenum) | Order by Finished time. |
+
### `DiffImagePositionInput`
#### Arguments
@@ -22697,6 +23262,14 @@ A time-frame defined as a closed inclusive range of two dates.
| <a id="workitemwidgethierarchyupdateinputchildrenids"></a>`childrenIds` | [`[WorkItemID!]`](#workitemid) | Global IDs of children work items. |
| <a id="workitemwidgethierarchyupdateinputparentid"></a>`parentId` | [`WorkItemID`](#workitemid) | Global ID of the parent work item. Use `null` to remove the association. |
+### `WorkItemWidgetIterationInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="workitemwidgetiterationinputiterationid"></a>`iterationId` | [`IterationID`](#iterationid) | Iteration to assign to the work item. |
+
### `WorkItemWidgetStartAndDueDateUpdateInput`
#### Arguments
diff --git a/doc/api/graphql/users_example.md b/doc/api/graphql/users_example.md
index 60ca7090aac..1c366587e5f 100644
--- a/doc/api/graphql/users_example.md
+++ b/doc/api/graphql/users_example.md
@@ -1,6 +1,6 @@
---
-stage: Ecosystem
-group: Integrations
+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/engineering/ux/technical-writing/#assignments
---
diff --git a/doc/api/group_protected_environments.md b/doc/api/group_protected_environments.md
index 0f1527f8968..3f1d932e0c8 100644
--- a/doc/api/group_protected_environments.md
+++ b/doc/api/group_protected_environments.md
@@ -15,7 +15,7 @@ Read more about [group-level protected environments](../ci/environments/protecte
## Valid access levels
-The access levels are defined in the `ProtectedEnvironment::DeployAccessLevel::ALLOWED_ACCESS_LEVELS` method.
+The access levels are defined in the `ProtectedEnvironments::DeployAccessLevel::ALLOWED_ACCESS_LEVELS` method.
Currently, these levels are recognized:
```plaintext
@@ -48,13 +48,14 @@ Example response:
"name":"production",
"deploy_access_levels":[
{
+ "id": 12,
"access_level": 40,
"access_level_description": "Maintainers",
"user_id": null,
"group_id": null
}
],
- "required_approval_count": 0
+ "required_approval_count": 0
}
]
```
@@ -83,6 +84,7 @@ Example response:
"name":"production",
"deploy_access_levels":[
{
+ "id": 12,
"access_level":40,
"access_level_description":"Maintainers",
"user_id":null,
@@ -123,13 +125,182 @@ Example response:
"name":"production",
"deploy_access_levels":[
{
+ "id": 12,
"access_level": 40,
"access_level_description": "protected-access-group",
"user_id": null,
"group_id": 9899826
}
],
- "required_approval_count": 0
+ "required_approval_count": 0
+}
+```
+
+## Update an environment
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/351854) in GitLab 15.4.
+
+Updates a single environment.
+
+```plaintext
+PUT /groups/:id/protected_environments/:name
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) maintained by the authenticated user. |
+| `name` | string | yes | The deployment tier of the protected environment. One of `production`, `staging`, `testing`, `development`, or `other`. Read more about [deployment tiers](../ci/environments/index.md#deployment-tier-of-environments).|
+| `deploy_access_levels` | array | no | Array of access levels allowed to deploy, with each described by a hash. One of `user_id`, `group_id` or `access_level`. They take the form of `{user_id: integer}`, `{group_id: integer}` or `{access_level: integer}` respectively. |
+| `required_approval_count` | integer | no | The number of approvals required to deploy to this environment. |
+| `approval_rules` | array | no | Array of access levels allowed to approve, with each described by a hash. One of `user_id`, `group_id` or `access_level`. They take the form of `{user_id: integer}`, `{group_id: integer}` or `{access_level: integer}` respectively. You can also specify the number of required approvals from the specified entity with `required_approvals` field. See [Multiple approval rules](../ci/environments/deployment_approvals.md#multiple-approval-rules) for more information. |
+
+To update:
+
+- **`user_id`**: Ensure the updated user belongs to the given group with the Maintainer role (or above). You must also pass the `id` of either a `deploy_access_level` or `approval_rule` in the respective hash.
+- **`group_id`**: Ensure the updated group is a sub-group of the group this protected environment belongs to. You must also pass the `id` of either a `deploy_access_level` or `approval_rule` in the respective hash.
+
+To delete:
+
+- You must pass `_destroy` set to `true`. See the following examples.
+
+### Example: Create a `deploy_access_level` record
+
+```shell
+curl --header 'Content-Type: application/json' --request PUT \
+ --data '{"deploy_access_levels": [{"group_id": 9899829, access_level: 40}], "required_approval_count": 1}' \
+ --header "PRIVATE-TOKEN: <your_access_token>" \
+ "https://gitlab.example.com/api/v4/groups/22034114/protected_environments/production"
+```
+
+Example response:
+
+```json
+{
+ "name": "production",
+ "deploy_access_levels": [
+ {
+ "id": 12,
+ "access_level": 40,
+ "access_level_description": "protected-access-group",
+ "user_id": null,
+ "group_id": 9899829,
+ "group_inheritance_type": 1
+ }
+ ],
+ "required_approval_count": 0
+}
+```
+
+### Example: Update a `deploy_access_level` record
+
+```shell
+curl --header 'Content-Type: application/json' --request PUT \
+ --data '{"deploy_access_levels": [{"id": 12, "group_id": 22034120}], "required_approval_count": 2}' \
+ --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/22034114/protected_environments/production"
+```
+
+```json
+{
+ "name": "production",
+ "deploy_access_levels": [
+ {
+ "id": 12,
+ "access_level": 40,
+ "access_level_description": "protected-access-group",
+ "user_id": null,
+ "group_id": 22034120,
+ "group_inheritance_type": 0
+ }
+ ],
+ "required_approval_count": 2
+}
+```
+
+### Example: Delete a `deploy_access_level` record
+
+```shell
+curl --header 'Content-Type: application/json' --request PUT \
+ --data '{"deploy_access_levels": [{"id": 12, "_destroy": true}], "required_approval_count": 0}' \
+ --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/22034114/protected_environments/production"
+```
+
+Example response:
+
+```json
+{
+ "name": "production",
+ "deploy_access_levels": [],
+ "required_approval_count": 0
+}
+```
+
+### Example: Create an `approval_rule` record
+
+```shell
+curl --header 'Content-Type: application/json' --request PUT \
+ --data '{"approval_rules": [{"group_id": 134, "required_approvals": 1}]}' \
+ --header "PRIVATE-TOKEN: <your_access_token>" \
+ "https://gitlab.example.com/api/v4/groups/22034114/protected_environments/production"
+```
+
+Example response:
+
+```json
+{
+ "name": "production",
+ "approval_rules": [
+ {
+ "id": 38,
+ "user_id": null,
+ "group_id": 134,
+ "access_level": null,
+ "access_level_description": "qa-group",
+ "required_approvals": 1,
+ "group_inheritance_type": 0
+ }
+ ]
+}
+```
+
+### Example: Update an `approval_rule` record
+
+```shell
+curl --header 'Content-Type: application/json' --request PUT \
+ --data '{"approval_rules": [{"id": 38, "group_id": 135, "required_approvals": 2}]}' \
+ --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/22034114/protected_environments/production"
+```
+
+```json
+{
+ "name": "production",
+ "approval_rules": [
+ {
+ "id": 38,
+ "user_id": null,
+ "group_id": 135,
+ "access_level": null,
+ "access_level_description": "security-group",
+ "required_approvals": 2,
+ "group_inheritance_type": 0
+ }
+ ]
+}
+```
+
+### Example: Delete an `approval_rule` record
+
+```shell
+curl --header 'Content-Type: application/json' --request PUT \
+ --data '{"approval_rules": [{"id": 38, "_destroy": true}]}' \
+ --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/22034114/protected_environments/production"
+```
+
+Example response:
+
+```json
+{
+ "name": "production",
+ "approval_rules": []
}
```
diff --git a/doc/api/group_repository_storage_moves.md b/doc/api/group_repository_storage_moves.md
index 1b3940479bb..f1ad7f51ea0 100644
--- a/doc/api/group_repository_storage_moves.md
+++ b/doc/api/group_repository_storage_moves.md
@@ -194,6 +194,11 @@ Example response:
## Schedule a repository storage move for a group
+Schedules a repository storage move for a group. This endpoint:
+
+- Moves only group Wiki repositories.
+- Doesn't move repositories for projects in a group. To schedule project moves, use the [Project repository storage moves](project_repository_storage_moves.md) API.
+
```plaintext
POST /groups/:group_id/repository_storage_moves
```
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 9cbd12e664c..d3c0d659d08 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -62,7 +62,8 @@ GET /groups
"full_path": "foo-bar",
"file_template_project_id": 1,
"parent_id": null,
- "created_at": "2020-01-15T12:36:29.590Z"
+ "created_at": "2020-01-15T12:36:29.590Z",
+ "ip_restriction_ranges": null
}
]
```
@@ -684,7 +685,8 @@ Example response:
}
]
}
- ]
+ ],
+ "ip_restriction_ranges": null
}
```
@@ -874,6 +876,50 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
"https://gitlab.example.com/api/v4/groups/4/projects/56"
```
+## Get groups to which a user can transfer a group
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/371117) in GitLab 15.4
+
+Retrieve a list of groups to which the user can transfer a group.
+
+```plaintext
+GET /groups/:id/transfer_locations
+```
+
+| Attribute | Type | Required | Description |
+|-------------|----------------|------------------------|-------------|
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the group to be transferred](index.md#namespaced-path-encoding). |
+| `search` | string | **{dotted-circle}** No | The group names to search for. |
+
+Example request:
+
+```shell
+curl --request GET "https://gitlab.example.com/api/v4/groups/1/transfer_locations"
+```
+
+Example response:
+
+```json
+[
+ {
+ "id": 27,
+ "web_url": "https://gitlab.example.com/groups/gitlab",
+ "name": "GitLab",
+ "avatar_url": null,
+ "full_name": "GitLab",
+ "full_path": "GitLab"
+ },
+ {
+ "id": 31,
+ "web_url": "https://gitlab.example.com/groups/foobar",
+ "name": "FooBar",
+ "avatar_url": null,
+ "full_name": "FooBar",
+ "full_path": "FooBar"
+ }
+]
+```
+
## Transfer a group to a new parent group / Turn a subgroup to a top-level group
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/23831) in GitLab 14.6.
@@ -882,7 +928,7 @@ Transfer a group to a new parent group or turn a subgroup to a top-level group.
- With the Owner role for the group to transfer.
- With permission to [create a subgroup](../user/group/subgroups/index.md#create-a-subgroup) in the new parent group if transferring a group.
-- With [permission to create a top-level group](../administration/user_settings.md#prevent-users-from-creating-top-level-groups) if turning a subgroup into a top-level group.
+- With [permission to create a top-level group](../administration/user_settings.md) if turning a subgroup into a top-level group.
```plaintext
POST /groups/:id/transfer
@@ -905,7 +951,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
> `unique_project_download_limit`, `unique_project_download_limit_interval_in_seconds`, and `unique_project_download_limit_allowlist` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92970) in GitLab 15.3 [with a flag](../administration/feature_flags.md) named `limit_unique_project_downloads_per_namespace_user`. Disabled by default.
FLAG:
-On self-managed GitLab, by default `unique_project_download_limit`, `unique_project_download_limit_interval_in_seconds`, and `unique_project_download_limit_allowlist` are not available.
+On self-managed GitLab, by default `unique_project_download_limit`, `unique_project_download_limit_interval_in_seconds`, `unique_project_download_limit_allowlist` and `auto_ban_user_on_excessive_projects_download` are not available.
To make them available, ask an administrator to [enable the feature flag](../administration/feature_flags.md)
named `limit_unique_project_downloads_per_namespace_user`.
@@ -944,6 +990,8 @@ PUT /groups/:id
| `unique_project_download_limit` **(ULTIMATE)** | integer | no | Maximum number of unique projects a user can download in the specified time period before they are banned. Available only on top-level groups. Default: 0, Maximum: 10,000. |
| `unique_project_download_limit_interval_in_seconds` **(ULTIMATE)** | integer | no | Time period during which a user can download a maximum amount of projects before they are banned. Available only on top-level groups. Default: 0, Maximum: 864,000 seconds (10 days). |
| `unique_project_download_limit_allowlist` **(ULTIMATE)** | array of strings | no | List of usernames excluded from the unique project download limit. Available only on top-level groups. Default: `[]`, Maximum: 100 usernames. |
+| `auto_ban_user_on_excessive_projects_download` **(ULTIMATE)** | boolean | no | When enabled, users are automatically banned from the group when they download more than the maximum number of unique projects specified by `unique_project_download_limit` and `unique_project_download_limit_interval_in_seconds`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94159) in GitLab 15.4. |
+| `ip_restriction_ranges` **(PREMIUM)** | string | no | Comma-separated list of IP addresses or subnet masks to restrict group access. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/351493) in GitLab 15.4. |
NOTE:
The `projects` and `shared_projects` attributes in the response are deprecated and [scheduled for removal in API v5](https://gitlab.com/gitlab-org/gitlab/-/issues/213797).
@@ -1019,7 +1067,8 @@ Example response:
"shared_with_groups": [],
"request_access_enabled": false
}
- ]
+ ],
+ "ip_restriction_ranges": null
}
```
@@ -1064,14 +1113,32 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab
--form "avatar=@/tmp/example.png"
```
+### Remove a group avatar
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96421) in GitLab 15.4.
+
+To remove a group avatar, use a blank value for the `avatar` attribute.
+
+Example request:
+
+```shell
+curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/22" \
+ --data "avatar="
+```
+
## Remove group
+> - Immediately deleting subgroups was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/360008) in GitLab 15.3 [with a flag](../administration/feature_flags.md) named `immediate_delete_subgroup_api`. Disabled by default.
+> - Immediately deleting subgroups was [enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/368276) in GitLab 15.4.
+> - Immediately deleting subgroups was [enabled](https://gitlab.com/gitlab-org/gitlab/-/issues/368276) by default in GitLab 15.4.
+
Only available to group owners and administrators.
-This endpoint either:
+This endpoint:
-- Removes group, and queues a background job to delete all projects in the group as well.
-- Since [GitLab 12.8](https://gitlab.com/gitlab-org/gitlab/-/issues/33257), on [Premium](https://about.gitlab.com/pricing/) or higher tiers, marks a group for deletion. The deletion happens 7 days later by default, but this can be changed in the [instance settings](../user/admin_area/settings/visibility_and_access_controls.md#deletion-protection).
+- On Premium and higher tiers, marks the group for deletion. The deletion happens 7 days later by default, but you can change the retention period in the [instance settings](../user/admin_area/settings/visibility_and_access_controls.md#deletion-protection).
+- On Free tier, removes the group immediately and queues a background job to delete all projects in the group.
+- Deletes a subgroup immediately if the subgroup is marked for deletion (GitLab 15.4 and later). The endpoint does not immediately delete top-level groups.
```plaintext
DELETE /groups/:id
@@ -1079,9 +1146,11 @@ DELETE /groups/:id
Parameters:
-| Attribute | Type | Required | Description |
-| --------------- | -------------- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) |
+| Attribute | Type | Required | Description |
+|----------------------|------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) |
+| `permanently_remove` **(PREMIUM)** | boolean/string | no | Immediately deletes a subgroup if it is marked for deletion. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368276) in GitLab 15.4 |
+| `full_path` **(PREMIUM)** | string | no | Full path of subgroup to use with `permanently_remove`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368276) in GitLab 15.4. To find the subgroup path, see the [group details](groups.md#details-of-a-group) |
The response is `202 Accepted` if the user has authorization.
@@ -1232,6 +1301,7 @@ GET /groups/:id/hooks/:hook_id
"url": "http://example.com/hook",
"group_id": 3,
"push_events": true,
+ "push_events_branch_filter": "",
"issues_events": true,
"confidential_issues_events": true,
"merge_requests_events": true,
@@ -1258,10 +1328,11 @@ POST /groups/:id/hooks
```
| Attribute | Type | Required | Description |
-| -----------------------------| -------------- | ---------| ----------- |
+| -----------------------------| -------------- |----------| ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) |
| `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 |
@@ -1291,6 +1362,7 @@ PUT /groups/:id/hooks/:hook_id
| `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 |
@@ -1369,7 +1441,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 | Minimum [access level](members.md#valid-access-levels) for members of the LDAP group |
+| `group_access` | integer | yes | [Access level](members.md#valid-access-levels) for members of the LDAP group |
| `provider` | string | yes | LDAP provider for the LDAP group link |
NOTE:
@@ -1420,7 +1492,8 @@ To delete the LDAP group link, provide either a `cn` or a `filter`, but not both
## SAML Group Links **(PREMIUM)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/290367) in GitLab 15.3.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/290367) in GitLab 15.3.0.
+> - `access_level` type [changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95607) from `string` to `integer` in GitLab 15.3.3.
List, get, add, and delete SAML group links.
@@ -1438,13 +1511,12 @@ Supported attributes:
|:----------|:---------------|:---------|:-------------------------------------------------------------------------|
| `id` | integer/string | yes | ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) |
-If successful, returns [`200`](index.md#status-codes) and the following
-response attributes:
+If successful, returns [`200`](index.md#status-codes) and the following response attributes:
-| Attribute | Type | Description |
-|:-------------------|:-------|:-------------------------------------------------------------------------------------|
-| `[].name` | string | Name of the SAML group |
-| `[].access_level` | integer | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
+| 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 |
Example request:
@@ -1482,13 +1554,12 @@ Supported attributes:
| `id` | integer/string | yes | ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) |
| `saml_group_name` | string | yes | Name of an SAML group |
-If successful, returns [`200`](index.md#status-codes) and the following
-response attributes:
+If successful, returns [`200`](index.md#status-codes) and the following response attributes:
-| Attribute | Type | Description |
-|:---------------|:-------|:-------------------------------------------------------------------------------------|
-| `name` | string | Name of the SAML group |
-| `access_level` | integer | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
+| 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 |
Example request:
@@ -1515,19 +1586,18 @@ POST /groups/:id/saml_group_links
Supported attributes:
-| Attribute | Type | Required | Description |
-|:-------------------|:---------------|:---------|:-------------------------------------------------------------------------------------|
-| `id` | integer/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 | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
+| Attribute | Type | Required | Description |
+|:-------------------|:---------------|:---------|:-----------------------------------------------------------------------------|
+| `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 |
-If successful, returns [`201`](index.md#status-codes) and the following
-response attributes:
+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 | Minimum [access level](members.md#valid-access-levels) for members of the SAML group |
+| 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 |
Example request:
@@ -1557,7 +1627,7 @@ Supported attributes:
| Attribute | Type | Required | Description |
|:-------------------|:---------------|:---------|:-------------------------------------------------------------------------|
| `id` | integer/string | yes | ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) |
-| `saml_group_name` | string | yes | Name of an SAML group |
+| `saml_group_name` | string | yes | Name of a SAML group |
If successful, returns [`204`](index.md#status-codes) status code without any response body.
diff --git a/doc/api/integrations.md b/doc/api/integrations.md
index 7912f3f6bf7..b7e110a7eef 100644
--- a/doc/api/integrations.md
+++ b/doc/api/integrations.md
@@ -691,6 +691,36 @@ Get Confluence integration settings for a project.
GET /projects/:id/integrations/confluence
```
+## Shimo integration
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/343386) in GitLab 14.5 [with a flag](../administration/feature_flags.md) named `shimo_integration`. Disabled by default.
+> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/343386) in GitLab 15.4.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/343386) in GitLab 15.4. [Feature flag `shimo_integration`](https://gitlab.com/gitlab-org/gitlab/-/issues/345356) removed.
+
+Replaces the link to the internal wiki with a link to a Shimo Workspace.
+
+### Create/Edit Shimo integration
+
+Set Shimo integration for a project.
+
+```plaintext
+PUT /projects/:id/integrations/shimo
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `external_wiki_url` | string | true | Shimo Workspace URL |
+
+### Disable Shimo integration
+
+Disable the Shimo integration for a project. Integration settings are preserved.
+
+```plaintext
+DELETE /projects/:id/integrations/shimo
+```
+
## External wiki
Replaces the link to the internal wiki with a link to an external wiki.
diff --git a/doc/api/issues.md b/doc/api/issues.md
index e2c9fbd878d..6e8aaa3d489 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -294,7 +294,7 @@ GET /groups/:id/issues?state=opened
| `created_before` | datetime | no | Return issues created on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
| `due_date` | string | no | Return issues that have no due date, are overdue, or whose due date is this week, this month, or between two weeks ago and next month. Accepts: `0` (no due date), `any`, `today`, `tomorrow`, `overdue`, `week`, `month`, `next_month_and_previous_two_weeks`. |
| `epic_id` **(PREMIUM)** | integer | no | Return issues associated with the given epic ID. `None` returns issues that are not associated with an epic. `Any` returns issues that are associated with an epic. _([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46887) in GitLab 13.6)_
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) owned by the authenticated user |
+| `id` | integer/string | yes | The global ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) owned by the authenticated user |
| `iids[]` | integer array | no | Return only the issues having the given `iid` |
| `issue_type` | string | no | Filter to a given type of issue. One of `issue`, `incident`, or `test_case`. _([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/260375) in GitLab 13.12)_ |
| `iteration_id` **(PREMIUM)** | integer | no | Return issues assigned to the given iteration ID. `None` returns issues that do not belong to an iteration. `Any` returns issues that belong to an iteration. Mutually exclusive with `iteration_title`. _([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118742) in GitLab 13.6)_ |
@@ -498,7 +498,7 @@ GET /projects/:id/issues?state=opened
| `created_before` | datetime | no | Return issues created on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
| `due_date` | string | no | Return issues that have no due date, are overdue, or whose due date is this week, this month, or between two weeks ago and next month. Accepts: `0` (no due date), `any`, `today`, `tomorrow`, `overdue`, `week`, `month`, `next_month_and_previous_two_weeks`. |
| `epic_id` **(PREMIUM)** | integer | no | Return issues associated with the given epic ID. `None` returns issues that are not associated with an epic. `Any` returns issues that are associated with an epic. _([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46887) in GitLab 13.6)_
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
+| `id` | integer/string | yes | The global ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
| `iids[]` | integer array | no | Return only the issues having the given `iid` |
| `issue_type` | string | no | Filter to a given type of issue. One of `issue`, `incident`, or `test_case`. _([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/260375) in GitLab 13.12)_ |
| `iteration_id` **(PREMIUM)** | integer | no | Return issues assigned to the given iteration ID. `None` returns issues that do not belong to an iteration. `Any` returns issues that belong to an iteration. Mutually exclusive with `iteration_title`. _([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118742) in GitLab 13.6)_ |
@@ -845,7 +845,7 @@ GET /projects/:id/issues/:issue_iid
| 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 |
+| `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 |
```shell
@@ -1011,7 +1011,7 @@ POST /projects/:id/issues
| `due_date` | string | no | The due date. Date time string in the format `YYYY-MM-DD`, for example `2016-03-11` |
| `epic_id` **(PREMIUM)** | integer | no | ID of the epic to add the issue to. Valid values are greater than or equal to 0. |
| `epic_iid` **(PREMIUM)** | integer | no | IID of the epic to add the issue to. Valid values are greater than or equal to 0. (deprecated, [scheduled for removal](https://gitlab.com/gitlab-org/gitlab/-/issues/35157) in API version 5) |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
+| `id` | integer/string | yes | The global ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
| `iid` | integer/string | no | The internal ID of the project's issue (requires administrator or project owner rights) |
| `issue_type` | string | no | The type of issue. One of `issue`, `incident`, or `test_case`. Default is `issue`. |
| `labels` | string | no | Comma-separated label names for an issue |
@@ -1179,7 +1179,7 @@ PUT /projects/:id/issues/:issue_iid
| `due_date` | string | no | The due date. Date time string in the format `YYYY-MM-DD`, for example `2016-03-11` |
| `epic_id` **(PREMIUM)** | integer | no | ID of the epic to add the issue to. Valid values are greater than or equal to 0. |
| `epic_iid` **(PREMIUM)** | integer | no | IID of the epic to add the issue to. Valid values are greater than or equal to 0. (deprecated, [scheduled for removal](https://gitlab.com/gitlab-org/gitlab/-/issues/35157) in API version 5) |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
+| `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 |
| `issue_type` | string | no | Updates the type of issue. One of `issue`, `incident`, or `test_case`. |
| `labels` | string | no | Comma-separated label names for an issue. Set to an empty string to unassign all labels. |
@@ -1325,7 +1325,7 @@ DELETE /projects/:id/issues/:issue_iid
| 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 |
+| `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 |
```shell
@@ -1344,10 +1344,10 @@ PUT /projects/:id/issues/:issue_iid/reorder
| 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 |
+| `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 the project's issue |
-| `move_after_id` | integer | no | The ID of a project's issue that should be placed after this issue |
-| `move_before_id` | integer | no | The ID of a project's issue that should be placed before this issue |
+| `move_after_id` | integer | no | The global ID of a project's issue that should be placed after this issue |
+| `move_before_id` | integer | no | The global ID of a project's issue that should be placed before this issue |
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/4/issues/85/reorder?move_after_id=51&move_before_id=92"
@@ -1368,7 +1368,7 @@ POST /projects/:id/issues/:issue_iid/move
| 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 |
+| `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 |
| `to_project_id` | integer | yes | The ID of the new project |
@@ -1621,7 +1621,7 @@ POST /projects/:id/issues/:issue_iid/subscribe
| 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 |
+| `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 |
```shell
@@ -1764,7 +1764,7 @@ POST /projects/:id/issues/:issue_iid/unsubscribe
| 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 |
+| `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 |
```shell
@@ -1838,7 +1838,7 @@ POST /projects/:id/issues/:issue_iid/todo
| 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 |
+| `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 |
```shell
@@ -1959,7 +1959,7 @@ Supported attributes:
| 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 |
+| `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 |
| `body` | String | yes | The content of a note. Must contain `/promote` at the start of a new line. |
@@ -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 |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
+| `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 |
```shell
@@ -2038,7 +2038,7 @@ POST /projects/:id/issues/:issue_iid/reset_time_estimate
| 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 |
+| `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 |
```shell
@@ -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 |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
+| `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 |
@@ -2096,7 +2096,7 @@ POST /projects/:id/issues/:issue_iid/reset_spent_time
| 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 |
+| `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 |
```shell
@@ -2125,7 +2125,7 @@ GET /projects/:id/issues/:issue_iid/time_stats
| 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 |
+| `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 |
```shell
@@ -2156,7 +2156,7 @@ GET /projects/:id/issues/:issue_iid/related_merge_requests
| 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 |
+| `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 |
```shell
@@ -2316,7 +2316,7 @@ GET /projects/:id/issues/:issue_iid/closed_by
| 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 |
+| `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 issue |
```shell
@@ -2393,7 +2393,7 @@ GET /projects/:id/issues/:issue_iid/participants
| 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 |
+| `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 |
```shell
@@ -2437,7 +2437,7 @@ GET /projects/:id/issues/:issue_iid/user_agent_detail
| 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 |
+| `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 |
```shell
@@ -2469,7 +2469,7 @@ POST /projects/:id/issues/:issue_iid/metric_images
| 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 |
+| `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 |
| `file` | file | yes | The image file to be uploaded |
| `url` | string | no | The URL to view more metric information |
@@ -2503,7 +2503,7 @@ GET /projects/:id/issues/:issue_iid/metric_images
| 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 |
+| `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 |
```shell
@@ -2541,7 +2541,7 @@ PUT /projects/:id/issues/:issue_iid/metric_images/:image_id
| 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 |
+| `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 |
| `image_id` | integer | yes | The ID of the image |
| `url` | string | no | The URL to view more metric information |
@@ -2574,7 +2574,7 @@ DELETE /projects/:id/issues/:issue_iid/metric_images/:image_id
| 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 |
+| `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 |
| `image_id` | integer | yes | The ID of the image |
diff --git a/doc/api/job_artifacts.md b/doc/api/job_artifacts.md
index 31da0638d23..d1bd40b91b9 100644
--- a/doc/api/job_artifacts.md
+++ b/doc/api/job_artifacts.md
@@ -70,7 +70,7 @@ is the same as [getting the job's artifacts](#get-job-artifacts), but by
defining the job's name instead of its ID.
NOTE:
-If a pipeline is [parent of other child pipelines](../ci/pipelines/parent_child_pipelines.md), artifacts
+If a pipeline is [parent of other child pipelines](../ci/pipelines/downstream_pipelines.md#parent-child-pipelines), artifacts
are searched in hierarchical order from parent to child. For example, if both parent and
child pipelines have a job with the same name, the artifact from the parent pipeline is returned.
@@ -175,7 +175,7 @@ The artifact file provides more detail than what is available in the
[CSV export](../user/application_security/vulnerability_report/index.md#export-vulnerability-details).
In [GitLab 13.5](https://gitlab.com/gitlab-org/gitlab/-/issues/201784) and later, artifacts
-for [parent and child pipelines](../ci/pipelines/parent_child_pipelines.md) are searched in hierarchical
+for [parent and child pipelines](../ci/pipelines/downstream_pipelines.md#parent-child-pipelines) are searched in hierarchical
order from parent to child. For example, if both parent and child pipelines have a
job with the same name, the artifact from the parent pipeline is returned.
diff --git a/doc/api/jobs.md b/doc/api/jobs.md
index 647f8eafa62..1548045d7c2 100644
--- a/doc/api/jobs.md
+++ b/doc/api/jobs.md
@@ -303,7 +303,7 @@ Example of response
```
In GitLab 13.3 and later, this endpoint [returns data for any pipeline](pipelines.md#get-a-single-pipeline)
-including [child pipelines](../ci/pipelines/parent_child_pipelines.md).
+including [child pipelines](../ci/pipelines/downstream_pipelines.md#parent-child-pipelines).
In GitLab 13.5 and later, this endpoint does not return retried jobs in the response
by default. Additionally, jobs are sorted by ID in descending order (newest first).
@@ -368,6 +368,9 @@ Example of response
"status": "pending",
"tag": false,
"web_url": "https://example.com/foo/bar/-/jobs/7",
+ "project": {
+ "ci_job_token_scope_enabled": false
+ },
"user": {
"id": 1,
"name": "Administrator",
@@ -454,6 +457,9 @@ Example of response
"failure_reason": "script_failure",
"tag": false,
"web_url": "https://example.com/foo/bar/-/jobs/8",
+ "project": {
+ "ci_job_token_scope_enabled": false
+ },
"user": {
"id": 1,
"name": "Administrator",
@@ -611,6 +617,9 @@ Example of response
"status": "failed",
"tag": false,
"web_url": "https://example.com/foo/bar/-/jobs/8",
+ "project": {
+ "ci_job_token_scope_enabled": false
+ },
"user": {
"id": 1,
"name": "Administrator",
@@ -701,6 +710,9 @@ Example of response
"status": "canceled",
"tag": false,
"web_url": "https://example.com/foo/bar/-/jobs/1",
+ "project": {
+ "ci_job_token_scope_enabled": false
+ },
"user": null
}
```
@@ -751,6 +763,9 @@ Example of response
"status": "pending",
"tag": false,
"web_url": "https://example.com/foo/bar/-/jobs/1",
+ "project": {
+ "ci_job_token_scope_enabled": false
+ },
"user": null
}
```
@@ -806,6 +821,9 @@ Example of response
"status": "failed",
"tag": false,
"web_url": "https://example.com/foo/bar/-/jobs/1",
+ "project": {
+ "ci_job_token_scope_enabled": false
+ },
"user": null
}
```
@@ -816,7 +834,7 @@ You can't delete archived jobs with the API, but you can
## Run a job
-Triggers a manual action to start a job.
+For a job in manual status, trigger an action to start the job.
```plaintext
POST /projects/:id/jobs/:job_id/play
@@ -882,6 +900,9 @@ Example response:
"status": "pending",
"tag": false,
"web_url": "https://example.com/foo/bar/-/jobs/1",
+ "project": {
+ "ci_job_token_scope_enabled": false
+ },
"user": null
}
```
diff --git a/doc/api/members.md b/doc/api/members.md
index a9817918d0b..aff601ba33f 100644
--- a/doc/api/members.md
+++ b/doc/api/members.md
@@ -47,6 +47,8 @@ GET /projects/:id/members
| `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 |
| `query` | string | no | A query string to search for members |
| `user_ids` | array of integers | no | Filter the results on the given user IDs |
+| `skip_users` | array of integers | no | Filter skipped users out of the results |
+| `show_seat_info` | boolean | no | Show seat information for users |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/members"
@@ -75,8 +77,7 @@ Example response:
},
"expires_at": "2012-10-22T14:13:35Z",
"access_level": 30,
- "group_saml_identity": null,
- "membership_state": "active"
+ "group_saml_identity": null
},
{
"id": 2,
@@ -101,8 +102,7 @@ Example response:
"extern_uid":"ABC-1234567890",
"provider": "group_saml",
"saml_provider_id": 10
- },
- "membership_state": "active"
+ }
}
]
```
@@ -133,6 +133,7 @@ GET /projects/:id/members/all
| `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 |
| `query` | string | no | A query string to search for members |
| `user_ids` | array of integers | no | Filter the results on the given user IDs |
+| `show_seat_info` | boolean | no | Show seat information for users |
| `state` | string | no | Filter results by member state, one of `awaiting` or `active` **(PREMIUM)** |
```shell
@@ -162,8 +163,7 @@ Example response:
},
"expires_at": "2012-10-22T14:13:35Z",
"access_level": 30,
- "group_saml_identity": null,
- "membership_state": "active"
+ "group_saml_identity": null
},
{
"id": 2,
@@ -188,8 +188,7 @@ Example response:
"extern_uid":"ABC-1234567890",
"provider": "group_saml",
"saml_provider_id": 10
- },
- "membership_state": "active"
+ }
},
{
"id": 3,
@@ -209,8 +208,7 @@ Example response:
},
"expires_at": "2012-11-22T14:13:35Z",
"access_level": 30,
- "group_saml_identity": null,
- "membership_state": "active"
+ "group_saml_identity": null
}
]
```
@@ -256,8 +254,7 @@ Example response:
"web_url": "http://192.168.1.8:3000/root"
},
"expires_at": null,
- "group_saml_identity": null,
- "membership_state": "active"
+ "group_saml_identity": null
}
```
@@ -304,8 +301,7 @@ Example response:
},
"email": "john@example.com",
"expires_at": null,
- "group_saml_identity": null,
- "membership_state": "active"
+ "group_saml_identity": null
}
```
@@ -335,7 +331,6 @@ GET /groups/:id/billable_members
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) owned by the authenticated user |
| `search` | string | no | A query string to search for group members by name, username, or public email. |
| `sort` | string | no | A query string containing parameters that specify the sort attribute and order. See supported values below. |
-| `include_awaiting_members` | boolean | no | Determines if awaiting members are included. |
The supported values for the `sort` attribute are:
@@ -369,7 +364,6 @@ Example response:
"web_url": "http://192.168.1.8:3000/root",
"last_activity_on": "2021-01-27",
"membership_type": "group_member",
- "membership_state": "active",
"removable": true,
"created_at": "2021-01-03T12:16:02.000Z"
},
@@ -383,7 +377,6 @@ Example response:
"email": "john@example.com",
"last_activity_on": "2021-01-25",
"membership_type": "group_member",
- "membership_state": "active",
"removable": true,
"created_at": "2021-01-04T18:46:42.000Z"
},
@@ -396,7 +389,6 @@ Example response:
"web_url": "http://192.168.1.8:3000/root",
"last_activity_on": "2021-01-20",
"membership_type": "group_invite",
- "membership_state": "awaiting",
"removable": false,
"created_at": "2021-01-09T07:12:31.000Z"
}
diff --git a/doc/api/merge_request_approvals.md b/doc/api/merge_request_approvals.md
index 1d99c323946..1fcce40a5b0 100644
--- a/doc/api/merge_request_approvals.md
+++ b/doc/api/merge_request_approvals.md
@@ -23,16 +23,17 @@ following endpoint:
GET /projects/:id/approvals
```
-**Parameters:**
+Supported attributes:
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | ------------------- |
-| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
```json
{
"approvals_before_merge": 2,
"reset_approvals_on_push": true,
+ "selective_code_owner_removals": false,
"disable_overriding_approvers_per_merge_request": false,
"merge_requests_author_approval": true,
"merge_requests_disable_committers_approval": false,
@@ -51,22 +52,24 @@ endpoint:
POST /projects/:id/approvals
```
-**Parameters:**
+Supported attributes:
-| Attribute | Type | Required | Description |
-| ------------------------------------------------ | ------- | -------- | --------------------------------------------------------------------------------------------------- |
-| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
-| `approvals_before_merge` | integer | no | How many approvals are required before an MR can be merged. Deprecated in 12.0 in favor of Approval Rules API. |
-| `reset_approvals_on_push` | boolean | no | Reset approvals on a new push |
-| `disable_overriding_approvers_per_merge_request` | boolean | no | Allow or prevent overriding approvers per MR |
-| `merge_requests_author_approval` | boolean | no | Allow or prevent authors from self approving merge requests; `true` means authors can self approve |
-| `merge_requests_disable_committers_approval` | boolean | no | Allow or prevent committers from self approving merge requests |
-| `require_password_to_approve` | boolean | no | Require approver to enter a password to authenticate before adding the approval |
+| Attribute | Type | Required | Description |
+| ------------------------------------------------ | ------- | -------- | -- |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
+| `approvals_before_merge` | integer | **{dotted-circle}** No | How many approvals are required before a merge request can be merged. Deprecated in GitLab 12.0 in favor of Approval Rules API. |
+| `disable_overriding_approvers_per_merge_request` | boolean | **{dotted-circle}** No | Allow or prevent overriding approvers per merge request. |
+| `merge_requests_author_approval` | boolean | **{dotted-circle}** No | Allow or prevent authors from self approving merge requests; `true` means authors can self approve. |
+| `merge_requests_disable_committers_approval` | boolean | **{dotted-circle}** No | Allow or prevent committers from self approving merge requests. |
+| `require_password_to_approve` | boolean | **{dotted-circle}** No | Require approver to enter a password to authenticate before adding the approval. |
+| `reset_approvals_on_push` | boolean | **{dotted-circle}** No | Reset approvals on a new push. |
+| `selective_code_owner_removals` | boolean | **{dotted-circle}** No | Reset approvals from Code Owners if their files changed. Can be enabled only if `reset_approvals_on_push` is disabled. |
```json
{
"approvals_before_merge": 2,
"reset_approvals_on_push": true,
+ "selective_code_owner_removals": false,
"disable_overriding_approvers_per_merge_request": false,
"merge_requests_author_approval": false,
"merge_requests_disable_committers_approval": false,
@@ -76,9 +79,7 @@ POST /projects/:id/approvals
### Get project-level rules
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11877) in GitLab 12.3.
> - Moved to GitLab Premium in 13.9.
-> - `protected_branches` property was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/460) in GitLab 12.7.
> - 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.
@@ -90,11 +91,11 @@ GET /projects/:id/approval_rules
Use the `page` and `per_page` [pagination](index.md#offset-based-pagination) parameters to restrict the list of approval rules.
-**Parameters:**
+Supported attributes:
| Attribute | Type | Required | Description |
|----------------------|---------|----------|-----------------------------------------------------------|
-| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
```json
[
@@ -191,12 +192,12 @@ You can request information about a single project approval rules using the foll
GET /projects/:id/approval_rules/:approval_rule_id
```
-**Parameters:**
+Supported attributes:
| Attribute | Type | Required | Description |
|----------------------|---------|----------|-----------------------------------------------------------|
-| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
-| `approval_rule_id` | integer | yes | The ID of a approval rule |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
+| `approval_rule_id` | integer | **{check-circle}** Yes | The ID of a approval rule. |
```json
{
@@ -282,7 +283,6 @@ GET /projects/:id/approval_rules/:approval_rule_id
### Create project-level rule
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11877) in GitLab 12.3.
> - 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.
@@ -293,19 +293,19 @@ You can create project approval rules using the following endpoint:
POST /projects/:id/approval_rules
```
-**Parameters:**
+Supported attributes:
-| Attribute | Type | Required | Description |
-|-------------------------------------|-------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
-| `name` | string | yes | The name of the approval rule |
-| `report_type` | string | no | The report type required when the rule type is `report_approver`. The supported report types are: `license_scanning` and `code_coverage`. |
-| `approvals_required` | integer | yes | The number of required approvals for this rule |
-| `rule_type` | string | 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 | no | The ids of users as approvers |
-| `group_ids` | Array | no | The ids of groups as approvers |
-| `protected_branch_ids` | Array | no | The IDs of protected branches to scope the rule by. To identify the ID, [use the API](protected_branches.md#list-protected-branches). |
-| `applies_to_all_protected_branches` | boolean | 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. |
+| Attribute | Type | Required | Description |
+|-------------------------------------|-------------------|----------|------------ |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
+| `approvals_required` | integer | **{check-circle}** Yes | The number of required approvals for this rule. |
+| `name` | string | **{check-circle}** Yes | The name of the approval rule. |
+| `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). |
+| `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. |
```json
{
@@ -408,7 +408,6 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
### Update project-level rule
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11877) in GitLab 12.3.
> - 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.
@@ -421,18 +420,18 @@ PUT /projects/:id/approval_rules/:approval_rule_id
**Important:** Approvers and groups not in the `users`/`groups` parameters are **removed**
-**Parameters:**
+Supported attributes:
-| Attribute | Type | Required | Description |
-|-------------------------------------|-------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
-| `approval_rule_id` | integer | yes | The ID of a approval rule |
-| `name` | string | yes | The name of the approval rule |
-| `approvals_required` | integer | yes | The number of required approvals for this rule |
-| `user_ids` | Array | no | The ids of users as approvers |
-| `group_ids` | Array | no | The ids of groups as approvers |
-| `protected_branch_ids` | Array | no | The IDs of protected branches to scope the rule by. To identify the ID, [use the API](protected_branches.md#list-protected-branches). |
-| `applies_to_all_protected_branches` | boolean | 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. |
+| Attribute | Type | Required | Description |
+|-------------------------------------|-------------------|----------|-------------|
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
+| `approvals_required` | integer | **{check-circle}** Yes | The number of required approvals for this rule. |
+| `approval_rule_id` | integer | **{check-circle}** Yes | The ID of a approval rule. |
+| `name` | string | **{check-circle}** Yes | The name of the approval rule. |
+| `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). |
+| `user_ids` | Array | **{dotted-circle}** No | The IDs of users as approvers. |
```json
{
@@ -518,8 +517,7 @@ PUT /projects/:id/approval_rules/:approval_rule_id
### Delete project-level rule
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11877) in GitLab 12.3.
-> - Moved to GitLab Premium in 13.9.
+> Moved to GitLab Premium in 13.9.
You can delete project approval rules using the following endpoint:
@@ -527,12 +525,12 @@ You can delete project approval rules using the following endpoint:
DELETE /projects/:id/approval_rules/:approval_rule_id
```
-**Parameters:**
+Supported attributes:
| Attribute | Type | Required | Description |
|--------------------|-------------------|----------|------------------------------------------------------------------------------|
-| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
-| `approval_rule_id` | integer | yes | The ID of a approval rule |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
+| `approval_rule_id` | integer | **{check-circle}** Yes | The ID of a approval rule. |
## Merge request-level MR approvals
@@ -549,12 +547,12 @@ following endpoint:
GET /projects/:id/merge_requests/:merge_request_iid/approvals
```
-**Parameters:**
+Supported attributes:
| Attribute | Type | Required | Description |
|---------------------|-------------------|----------|------------------------------------------------------------------------------|
-| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
-| `merge_request_iid` | integer | yes | The IID of MR |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
+| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
```json
{
@@ -595,13 +593,13 @@ endpoint:
POST /projects/:id/merge_requests/:merge_request_iid/approvals
```
-**Parameters:**
+Supported attributes:
-| Attribute | Type | Required | Description |
-|----------------------|-------------------|----------|------------------------------------------------------------------------------------------------|
-| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
-| `merge_request_iid` | integer | yes | The IID of MR |
-| `approvals_required` | integer | yes | Approvals required before MR can be merged. Deprecated in 12.0 in favor of Approval Rules API. |
+| Attribute | Type | Required | Description |
+|----------------------|-------------------|----------|-------------|
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
+| `approvals_required` | integer | **{check-circle}** Yes | Approvals required before MR can be merged. Deprecated in GitLab 12.0 in favor of Approval Rules API. |
+| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
```json
{
@@ -622,8 +620,7 @@ POST /projects/:id/merge_requests/:merge_request_iid/approvals
### Get the approval state of merge requests
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13712) in GitLab 12.3.
-> - Moved to GitLab Premium in 13.9.
+> Moved to GitLab Premium in 13.9.
You can request information about a merge request's approval state by using the following endpoint:
@@ -637,12 +634,12 @@ are created for the merge request. If there are none, it is `false`.
This includes additional information about the users who have already approved
(`approved_by`) and whether a rule is already approved (`approved`).
-**Parameters:**
+Supported attributes:
| Attribute | Type | Required | Description |
|---------------------|-------------------|----------|------------------------------------------------------------------------------|
-| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
-| `merge_request_iid` | integer | yes | The IID of MR |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
+| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
```json
{
@@ -695,7 +692,6 @@ This includes additional information about the users who have already approved
### Get merge request level rules
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13712) in GitLab 12.3.
> - 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.
@@ -707,12 +703,12 @@ GET /projects/:id/merge_requests/:merge_request_iid/approval_rules
Use the `page` and `per_page` [pagination](index.md#offset-based-pagination) parameters to restrict the list of approval rules.
-**Parameters:**
+Supported attributes:
| Attribute | Type | Required | Description |
|---------------------|---------|----------|---------------------|
-| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
-| `merge_request_iid` | integer | yes | The IID of MR |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
+| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
```json
[
@@ -784,13 +780,13 @@ You can request information about a single merge request approval rule using the
GET /projects/:id/merge_requests/:merge_request_iid/approval_rules/:approval_rule_id
```
-**Parameters:**
+Supported attributes:
| Attribute | Type | Required | Description |
|---------------------|---------|----------|------------------------------------------------------------------------------|
-| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
-| `merge_request_iid` | integer | yes | The IID of a merge request. |
-| `approval_rule_id` | integer | yes | The ID of an approval rule. |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
+| `approval_rule_id` | integer | **{check-circle}** Yes | The ID of an approval rule. |
+| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of a merge request. |
```json
{
@@ -852,8 +848,7 @@ GET /projects/:id/merge_requests/:merge_request_iid/approval_rules/:approval_rul
### Create merge request level rule
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11877) in GitLab 12.3.
-> - Moved to GitLab Premium in 13.9.
+> Moved to GitLab Premium in 13.9.
You can create merge request approval rules using the following endpoint:
@@ -861,17 +856,17 @@ You can create merge request approval rules using the following endpoint:
POST /projects/:id/merge_requests/:merge_request_iid/approval_rules
```
-**Parameters:**
+Supported attributes:
| Attribute | Type | Required | Description |
|----------------------------|---------|----------|------------------------------------------------|
-| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
-| `merge_request_iid` | integer | yes | The IID of MR |
-| `name` | string | yes | The name of the approval rule |
-| `approvals_required` | integer | yes | The number of required approvals for this rule |
-| `approval_project_rule_id` | integer | no | The ID of a project-level approval rule |
-| `user_ids` | Array | no | The ids of users as approvers |
-| `group_ids` | Array | no | The ids of groups as approvers |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
+| `approvals_required` | integer | **{check-circle}** Yes | The number of required approvals for this rule. |
+| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
+| `name` | string | **{check-circle}** Yes | The name of the approval rule. |
+| `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. |
**Important:** When `approval_project_rule_id` is set, the `name`, `users` and
`groups` of project-level rule are copied. The `approvals_required` specified
@@ -937,8 +932,7 @@ is used.
### Update merge request level rule
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11877) in GitLab 12.3.
-> - Moved to GitLab Premium in 13.9.
+> Moved to GitLab Premium in 13.9.
You can update merge request approval rules using the following endpoint:
@@ -951,17 +945,17 @@ PUT /projects/:id/merge_requests/:merge_request_iid/approval_rules/:approval_rul
**Important:** Updating a `report_approver` or `code_owner` rule is not allowed.
These are system generated rules.
-**Parameters:**
+Supported attributes:
| Attribute | Type | Required | Description |
|----------------------|---------|----------|------------------------------------------------|
-| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
-| `merge_request_iid` | integer | yes | The IID of a merge request. |
-| `approval_rule_id` | integer | yes | The ID of an approval rule. |
-| `name` | string | yes | The name of the approval rule. |
-| `approvals_required` | integer | yes | The number of required approvals for this rule. |
-| `user_ids` | Array | no | The IDs of users as approvers. |
-| `group_ids` | Array | no | The IDs of groups as approvers. |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
+| `approvals_required` | integer | **{check-circle}** Yes | The number of required approvals for this rule. |
+| `approval_rule_id` | integer | **{check-circle}** Yes | The ID of an approval rule. |
+| `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. |
+| `user_ids` | Array | **{dotted-circle}** No | The IDs of users as approvers. |
```json
{
@@ -1023,8 +1017,7 @@ These are system generated rules.
### Delete merge request level rule
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11877) in GitLab 12.3.
-> - Moved to GitLab Premium in 13.9.
+> Moved to GitLab Premium in 13.9.
You can delete merge request approval rules using the following endpoint:
@@ -1035,13 +1028,13 @@ DELETE /projects/:id/merge_requests/:merge_request_iid/approval_rules/:approval_
**Important:** Deleting a `report_approver` or `code_owner` rule is not allowed.
These are system generated rules.
-**Parameters:**
+Supported attributes:
| Attribute | Type | Required | Description |
|---------------------|---------|----------|---------------------------|
-| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
-| `merge_request_iid` | integer | yes | The IID of the merge request |
-| `approval_rule_id` | integer | yes | The ID of an approval rule |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
+| `approval_rule_id` | integer | **{check-circle}** Yes | The ID of an approval rule. |
+| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
## Approve merge request
@@ -1054,14 +1047,14 @@ endpoint:
POST /projects/:id/merge_requests/:merge_request_iid/approve
```
-**Parameters:**
+Supported attributes:
| Attribute | Type | Required | Description |
|---------------------|---------|----------|-------------------------|
-| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
-| `merge_request_iid` | integer | yes | The IID of the merge request |
-| `sha` | string | no | The `HEAD` of the merge request |
-| `approval_password` | string | no | Current user's password. Required if [**Require user password to approve**](../user/project/merge_requests/approvals/settings.md#require-user-password-to-approve) is enabled in the project settings. |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
+| `approval_password` | string | **{dotted-circle}** No | Current user's password. Required if [**Require user password to approve**](../user/project/merge_requests/approvals/settings.md#require-user-password-to-approve) is enabled in the project settings. |
+| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of the merge request. |
+| `sha` | string | **{dotted-circle}** No | The `HEAD` of the merge request. |
The `sha` parameter works in the same way as
when [accepting a merge request](merge_requests.md#merge-a-merge-request): if it is passed, then it must
@@ -1117,9 +1110,9 @@ endpoint:
POST /projects/:id/merge_requests/:merge_request_iid/unapprove
```
-**Parameters:**
+Supported attributes:
| Attribute | Type | Required | Description |
|---------------------|---------|----------|---------------------|
-| `id` | integer or string | yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding) |
-| `merge_request_iid` | integer | yes | The IID of a merge request |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of a project](index.md#namespaced-path-encoding). |
+| `merge_request_iid` | integer | **{check-circle}** Yes | The IID of a merge request. |
diff --git a/doc/api/merge_request_context_commits.md b/doc/api/merge_request_context_commits.md
index 9984c5abb70..08020e5a613 100644
--- a/doc/api/merge_request_context_commits.md
+++ b/doc/api/merge_request_context_commits.md
@@ -17,10 +17,10 @@ GET /projects/:id/merge_requests/:merge_request_iid/context_commits
Parameters:
-| Attribute | Type | Required | Description |
-|---------------------|---------|----------|-----------------------------------------------------------------------------------------------------------------|
-| `id` | integer | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request |
+| Attribute | Type | Required | Description |
+|---------------------|---------|----------|-------------|
+| `id` | integer | **{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. |
```json
[
@@ -51,18 +51,18 @@ POST /projects/:id/merge_requests/:merge_request_iid/context_commits
Parameters:
-| Attribute | Type | Required | Description |
-|---------------------|---------|----------|-----------------------------------------------------------------------------------------------------------------|
-| `id` | integer | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request |
+| Attribute | Type | Required | Description |
+|---------------------|---------|----------|-------------|
+| `id` | integer | **{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. |
```plaintext
POST /projects/:id/merge_requests/
```
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `commits` | string array | yes | The context commits' SHA |
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `commits` | string array | **{check-circle}** Yes | The context commits' SHA. |
```json
[
@@ -92,8 +92,8 @@ DELETE /projects/:id/merge_requests/:merge_request_iid/context_commits
Parameters:
-| Attribute | Type | Required | Description |
-|---------------------|--------------|----------|-----------------------------------------------------------------------------------------------------------------|
-| `id` | integer | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request |
-| `commits` | string array | yes | The context commits' SHA |
+| Attribute | Type | Required | Description |
+|---------------------|--------------|----------|--------------|
+| `commits` | string array | **{check-circle}** Yes | The context commits' SHA. |
+| `id` | integer | **{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. |
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index a491756e5f0..82125aec366 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -6,8 +6,6 @@ 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.10 in favour of `references`.
-> - `reviewer_username` and `reviewer_id` were [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49341) in GitLab 13.8.
> - `draft` was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63473) as a replacement for `work_in_progress` in GitLab 14.0.
> - `merge_user` was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/349031) as an eventual replacement for `merged_by` in GitLab 14.7.
@@ -38,40 +36,40 @@ GET /merge_requests?scope=assigned_to_me
GET /merge_requests?search=foo&in=title
```
-Parameters:
-
-| Attribute | Type | Required | Description |
-| ------------------------------- | -------------- | -------- | ---------------------------------------------------------------------------------------------------------------------- |
-| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged`. |
-| `order_by` | string | 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.|
-| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc`. |
-| `milestone` | string | no | Return merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. |
-| `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request. |
-| `labels` | string | 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. |
-| `with_labels_details` | boolean | no | If `true`, response returns more details for each label in labels field: `:name`, `:color`, `:description`, `:description_html`, `:text_color`. Default is `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21413) in GitLab 12.7. |
-| `with_merge_status_recheck` | boolean | 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. |
-| `created_after` | datetime | no | Return merge requests created on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
-| `created_before` | datetime | no | Return merge requests created on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
-| `updated_after` | datetime | no | Return merge requests updated on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
-| `updated_before` | datetime | no | Return merge requests updated on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
-| `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`. Defaults to `created_by_me`. |
-| `author_id` | integer | 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 | no | Returns merge requests created by the given `username`. Mutually exclusive with `author_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/13060) in GitLab 12.10. |
-| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. |
-| `approver_ids` **(PREMIUM)** | integer array | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
-| `approved_by_ids` **(PREMIUM)** | integer array | no | Returns merge requests which have been approved by all the users with the given `id`s (Max: 5). `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. |
-| `reviewer_id` | integer | 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 | 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. |
-| `my_reaction_emoji` | string | 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. |
-| `source_branch` | string | no | Return merge requests with the given source branch. |
-| `target_branch` | string | no | Return merge requests with the given target branch. |
-| `search` | string | no | Search merge requests against their `title` and `description`. |
-| `in` | string | no | Modify the scope of the `search` attribute. `title`, `description`, or a string joining them with comma. Default is `title,description`. |
-| `wip` | string | no | Filter merge requests against their `wip` status. `yes` to return *only* draft merge requests, `no` to return *non-draft* merge requests. |
-| `not` | Hash | 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`. |
-| `environment` | string | no | Returns merge requests deployed to the given environment. |
-| `deployed_before` | datetime | no | Return merge requests deployed before the given date/time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
-| `deployed_after` | datetime | no | Return merge requests deployed after the given date/time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
+Supported attributes:
+
+| Attribute | Type | Required | Description |
+| ------------------------------- | -------------- | -------- | ----------- |
+| `approved_by_ids` **(PREMIUM)** | integer array | **{dotted-circle}** No | Returns merge requests which have been approved by all the users with the given `id`. Maximum of 5. `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. |
+| `approver_ids` **(PREMIUM)** | integer array | **{dotted-circle}** No | Returns merge requests which have specified all the users with the given `id` as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
+| `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`). |
+| `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.|
+| `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`. |
+| `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`). |
+| `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. |
+| `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. |
```json
[
@@ -239,39 +237,39 @@ are the same. In the case of a merge request from a fork,
`target_project_id` and `project_id` are the same and
`source_project_id` is the fork project's ID.
-Parameters:
-
-| 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. |
-| `iids[]` | integer array | no | Return the request having the given `iid`. |
-| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged`. |
-| `order_by` | string | 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. |
-| `sort` | string | no | Return requests sorted in `asc` or `desc` order. Default is `desc`. |
-| `milestone` | string | no | Return merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. |
-| `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request. |
-| `labels` | string | 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. |
-| `with_labels_details` | boolean | no | If `true`, response returns more details for each label in labels field: `:name`, `:color`, `:description`, `:description_html`, `:text_color`. Default is `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21413) in GitLab 12.7. |
-| `with_merge_status_recheck` | boolean | 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. |
-| `created_after` | datetime | no | Return merge requests created on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
-| `created_before` | datetime | no | Return merge requests created on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
-| `updated_after` | datetime | no | Return merge requests updated on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
-| `updated_before` | datetime | no | Return merge requests updated on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`) |
-| `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me`, or `all`. |
-| `author_id` | integer | no | Returns merge requests created by the given user `id`. Mutually exclusive with `author_username`. |
-| `author_username` | string | no | Returns merge requests created by the given `username`. Mutually exclusive with `author_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/13060) in GitLab 12.10. |
-| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. |
-| `approver_ids` **(PREMIUM)** | integer array | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
-| `approved_by_ids` **(PREMIUM)** | integer array | no | Returns merge requests which have been approved by all the users with the given `id`s (Max: 5). `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. |
-| `reviewer_id` | integer | 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 | 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. |
-| `my_reaction_emoji` | string | 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. |
-| `source_branch` | string | no | Return merge requests with the given source branch. |
-| `target_branch` | string | no | Return merge requests with the given target branch. |
-| `search` | string | no | Search merge requests against their `title` and `description`. |
-| `wip` | string | no | Filter merge requests against their `wip` status. `yes` to return *only* draft merge requests, `no` to return *non-draft* merge requests. |
-| `not` | Hash | 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`. |
-| `environment` | string | no | Returns merge requests deployed to the given environment. |
+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. |
+| `approved_by_ids` **(PREMIUM)** | integer array | **{dotted-circle}** No | Returns merge requests which have been approved by all the users with the given `id`, with a maximum of 5. `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. |
+| `approver_ids` **(PREMIUM)** | integer array | **{dotted-circle}** No | Returns merge requests which have specified all the users with the given `id` as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
+| `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`). |
+| `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. |
+| `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`. |
+| `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`). |
+| `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`. |
+| `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. |
```json
[
@@ -427,38 +425,38 @@ 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.
-Parameters:
-
-| Attribute | Type | Required | Description |
-| ------------------------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) owned by the authenticated user. |
-| `state` | string | no | Return all merge requests or just those that are `opened`, `closed`, `locked`, or `merged`. |
-| `order_by` | string | 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. |
-| `sort` | string | no | Return merge requests sorted in `asc` or `desc` order. Default is `desc`. |
-| `milestone` | string | no | Return merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. |
-| `view` | string | no | If `simple`, returns the `iid`, URL, title, description, and basic state of merge request. |
-| `labels` | string | 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. |
-| `with_labels_details` | boolean | no | If `true`, response returns more details for each label in labels field: `:name`, `:color`, `:description`, `:description_html`, `:text_color`. Default is `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/21413) in GitLab 12.7. |
-| `with_merge_status_recheck` | boolean | 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. |
-| `created_after` | datetime | no | Return merge requests created on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
-| `created_before` | datetime | no | Return merge requests created on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
-| `updated_after` | datetime | no | Return merge requests updated on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
-| `updated_before` | datetime | no | Return merge requests updated on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
-| `scope` | string | no | Return merge requests for the given scope: `created_by_me`, `assigned_to_me` or `all`. |
-| `author_id` | integer | no | Returns merge requests created by the given user `id`. Mutually exclusive with `author_username`. |
-| `author_username` | string | no | Returns merge requests created by the given `username`. Mutually exclusive with `author_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/13060) in GitLab 12.10. |
-| `assignee_id` | integer | no | Returns merge requests assigned to the given user `id`. `None` returns unassigned merge requests. `Any` returns merge requests with an assignee. |
-| `approver_ids` **(PREMIUM)** | integer array | no | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
-| `approved_by_ids` **(PREMIUM)** | integer array | no | Returns merge requests which have been approved by all the users with the given `id`s (Max: 5). `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. |
-| `approved_by_usernames` **(PREMIUM)** | string array | no | Returns merge requests which have been approved by all the users with the given `username`s (Max: 5). `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. |
-| `reviewer_id` | integer | 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 | 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. |
-| `my_reaction_emoji` | string | 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. |
-| `source_branch` | string | no | Return merge requests with the given source branch. |
-| `target_branch` | string | no | Return merge requests with the given target branch. |
-| `search` | string | no | Search merge requests against their `title` and `description`. |
-| `non_archived` | boolean | no | Return merge requests from non archived projects only. Default is true. _([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23809) in GitLab 12.8)_. |
-| `not` | Hash | 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`. |
+Supported attributes:
+
+| Attribute | Type | Required | Description |
+| ------------------------------- | -------------- | -------- | ----------- |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `approved_by_ids` **(PREMIUM)** | integer array | **{dotted-circle}** No | Returns merge requests which have been approved by all the users with the given `id`, with a maximum of 5. `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. |
+| `approved_by_usernames` **(PREMIUM)** | string array | **{dotted-circle}** No | Returns merge requests which have been approved by all the users with the given `username`, with a maximum of 5. `None` returns merge requests with no approvals. `Any` returns merge requests with an approval. |
+| `approver_ids` **(PREMIUM)** | integer array | **{dotted-circle}** No | Returns merge requests which have specified all the users with the given `id`s as individual approvers. `None` returns merge requests without approvers. `Any` returns merge requests with an approver. |
+| `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. |
+| `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`. |
+| `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`). |
+| `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. |
```json
[
@@ -608,15 +606,15 @@ it is capped at 1,000. In that case, the API returns the string
GET /projects/:id/merge_requests/:merge_request_iid
```
-Parameters:
+Supported attributes:
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
-| `render_html` | boolean | no | If `true` response includes rendered HTML for title and description. |
-| `include_diverged_commits_count` | boolean | no | If `true` response includes the commits behind the target branch. |
-| `include_rebase_in_progress` | boolean | no | If `true` response includes whether a rebase operation is in progress. |
+| 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. |
+| `include_diverged_commits_count` | boolean | **{dotted-circle}** No | If `true`, response includes the commits behind the target branch. |
+| `include_rebase_in_progress` | boolean | **{dotted-circle}** No | If `true`, response includes whether a rebase operation is in progress. |
+| `render_html` | boolean | **{dotted-circle}** No | If `true`, response includes rendered HTML for title and description. |
```json
{
@@ -797,12 +795,12 @@ Get a list of merge request participants.
GET /projects/:id/merge_requests/:merge_request_iid/participants
```
-Parameters:
+Supported attributes:
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
+| 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. |
```json
[
@@ -833,12 +831,12 @@ Get a list of merge request reviewers.
GET /projects/:id/merge_requests/:merge_request_iid/reviewers
```
-Parameters:
+Supported attributes:
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
+| 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. |
```json
[
@@ -851,14 +849,6 @@ Parameters:
"avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon",
"web_url": "http://localhost/user1"
},
- "updated_state_by": {
- "id": 1,
- "name": "John Doe1",
- "username": "user1",
- "state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon",
- "web_url": "http://localhost/user1"
- },
"state": "unreviewed",
"created_at": "2022-07-27T17:03:27.684Z"
},
@@ -871,14 +861,6 @@ Parameters:
"avatar_url": "http://www.gravatar.com/avatar/10fc7f102be8de7657fb4d80898bbfe3?s=80&d=identicon",
"web_url": "http://localhost/user2"
},
- "updated_state_by": {
- "id": 1,
- "name": "John Doe1",
- "username": "user1",
- "state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon",
- "web_url": "http://localhost/user1"
- },
"state": "reviewed",
"created_at": "2022-07-27T17:03:27.684Z"
}
@@ -893,12 +875,12 @@ Get a list of merge request commits.
GET /projects/:id/merge_requests/:merge_request_iid/commits
```
-Parameters:
+Supported attributes:
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
+| 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. |
```json
[
@@ -940,13 +922,13 @@ still apply.
GET /projects/:id/merge_requests/:merge_request_iid/changes
```
-Parameters:
+Supported attributes:
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
-| `access_raw_diffs` | boolean | no | Retrieve change diffs via Gitaly. |
+| 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. |
+| `access_raw_diffs` | boolean | **{dotted-circle}** No | Retrieve change diffs via Gitaly. |
```json
{
@@ -1062,12 +1044,12 @@ Get a list of merge request pipelines.
GET /projects/:id/merge_requests/:merge_request_iid/pipelines
```
-Parameters:
+Supported attributes:
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
+| 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. |
```json
[
@@ -1082,8 +1064,6 @@ Parameters:
## Create MR Pipeline
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/31722) in GitLab 12.3.
-
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.
It requires `.gitlab-ci.yml` to be configured with `only: [merge_requests]` to create jobs.
@@ -1098,12 +1078,12 @@ The new pipeline can be:
POST /projects/:id/merge_requests/:merge_request_iid/pipelines
```
-Parameters:
+Supported attributes:
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
+| 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. |
```json
{
@@ -1152,24 +1132,24 @@ Creates a new merge request.
POST /projects/:id/merge_requests
```
-| 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 |
-| `source_branch` | string | yes | The source branch. |
-| `target_branch` | string | yes | The target branch. |
-| `title` | string | yes | Title of MR. |
-| `assignee_id` | integer | no | Assignee user ID. |
-| `assignee_ids` | integer array | no | The ID of the users to assign the MR to. Set to `0` or provide an empty value to unassign all assignees. |
-| `reviewer_ids` | integer array | no | The ID of the users added as a reviewer to the MR. If set to `0` or left empty, no reviewers are added. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49341) in GitLab 13.8. |
-| `description` | string | no | Description of MR. Limited to 1,048,576 characters. |
-| `target_project_id` | integer | no | The target project (numeric ID). |
-| `labels` | string | no | Labels for MR as a comma-separated list. |
-| `milestone_id` | integer | no | The global ID of a milestone. |
-| `remove_source_branch` | boolean | no | Flag indicating if a merge request should remove the source branch when merging. |
-| `allow_collaboration` | boolean | no | Allow commits from members who can merge to the target branch. |
-| `allow_maintainer_to_push` | boolean | no | Alias of `allow_collaboration`. |
-| `approvals_before_merge` **(PREMIUM)** | integer | no | Number of approvals required before this can be merged (see below). |
-| `squash` | boolean | no | Squash commits into a single commit when merging. |
+| 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 |
+| `source_branch` | string | **{check-circle}** Yes | The source branch. |
+| `target_branch` | string | **{check-circle}** Yes | The target branch. |
+| `title` | string | **{check-circle}** Yes | Title of MR. |
+| `allow_collaboration` | boolean | **{dotted-circle}** No | Allow commits from members who can merge to the target branch. |
+| `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. |
+| `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. |
+| `remove_source_branch` | boolean | **{dotted-circle}** No | Flag indicating if a merge request should remove the source branch when merging. |
+| `reviewer_ids` | integer array | **{dotted-circle}** No | The ID of the users added as a reviewer to the merge request. If set to `0` or left empty, no reviewers are added. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49341) in GitLab 13.8. |
+| `squash` | boolean | **{dotted-circle}** No | Squash commits into a single commit when merging. |
+| `target_project_id` | integer | **{dotted-circle}** No | Numeric ID of the target project. |
If `approvals_before_merge` is not provided, it inherits the value from the target project. If provided, the following conditions must hold for it to take effect:
@@ -1320,26 +1300,26 @@ Updates an existing merge request. You can change the target branch, title, or e
PUT /projects/:id/merge_requests/:merge_request_iid
```
-| 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. |
-| `merge_request_iid` | integer | yes | The ID of a merge request. |
-| `target_branch` | string | no | The target branch. |
-| `title` | string | no | Title of MR. |
-| `assignee_id` | integer | no | The ID of the user to assign the merge request to. Set to `0` or provide an empty value to unassign all assignees. |
-| `assignee_ids` | integer array | no | The ID of the users to assign the MR to. Set to `0` or provide an empty value to unassign all assignees. |
-| `reviewer_ids` | integer array | no | The ID of the users set as a reviewer to the MR. Set the value to `0` or provide an empty value to unset all reviewers. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49341) in GitLab 13.8. |
-| `milestone_id` | integer | no | The global ID of a milestone to assign the merge request to. Set to `0` or provide an empty value to unassign a milestone.|
-| `labels` | string | no | Comma-separated label names for a merge request. Set to an empty string to unassign all labels. |
-| `add_labels` | string | no | Comma-separated label names to add to a merge request. |
-| `remove_labels` | string | no | Comma-separated label names to remove from a merge request. |
-| `description` | string | no | Description of MR. Limited to 1,048,576 characters. |
-| `state_event` | string | no | New state (close/reopen). |
-| `remove_source_branch` | boolean | no | Flag indicating if a merge request should remove the source branch when merging. |
-| `squash` | boolean | no | Squash commits into a single commit when merging. |
-| `discussion_locked` | boolean | no | Flag indicating if the merge request's discussion is locked. If the discussion is locked only project members can add, edit or resolve comments. |
-| `allow_collaboration` | boolean | no | Allow commits from members who can merge to the target branch. |
-| `allow_maintainer_to_push` | boolean | no | Alias of `allow_collaboration`. |
+| 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 ID of a merge request. |
+| `add_labels` | string | **{dotted-circle}** No | Comma-separated label names to add to a merge request. |
+| `allow_collaboration` | boolean | **{dotted-circle}** No | Allow commits from members who can merge to the target branch. |
+| `allow_maintainer_to_push` | boolean | **{dotted-circle}** No | Alias of `allow_collaboration`. |
+| `assignee_id` | integer | **{dotted-circle}** No | The ID of the user to assign the merge request 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. |
+| `discussion_locked` | boolean | **{dotted-circle}** No | Flag indicating if the merge request's discussion is locked. If the discussion is locked only project members can add, edit or resolve comments. |
+| `labels` | string | **{dotted-circle}** No | Comma-separated label names for a merge request. Set to an empty string to unassign all labels. |
+| `milestone_id` | integer | **{dotted-circle}** No | The global ID of a milestone to assign the merge request to. Set to `0` or provide an empty value to unassign a milestone.|
+| `remove_labels` | string | **{dotted-circle}** No | Comma-separated label names to remove from a merge request. |
+| `remove_source_branch` | boolean | **{dotted-circle}** No | Flag indicating if a merge request should remove the source branch when merging. |
+| `reviewer_ids` | integer array | **{dotted-circle}** No | The ID of the users set as a reviewer to the merge request. Set the value to `0` or provide an empty value to unset all reviewers. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49341) in GitLab 13.8. |
+| `squash` | boolean | **{dotted-circle}** No | Squash commits into a single commit when merging. |
+| `state_event` | string | **{dotted-circle}** No | New state (close/reopen). |
+| `target_branch` | string | **{dotted-circle}** No | The target branch. |
+| `title` | string | **{dotted-circle}** No | Title of MR. |
Must include at least one non-required attribute from above.
@@ -1501,10 +1481,10 @@ Only for administrators and project owners. Deletes the merge request in questio
DELETE /projects/:id/merge_requests/:merge_request_iid
```
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
+| 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. |
```shell
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/4/merge_requests/85"
@@ -1518,18 +1498,18 @@ Accept and merge changes submitted with MR using this API.
PUT /projects/:id/merge_requests/:merge_request_iid/merge
```
-Parameters:
+Supported attributes:
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
-| `merge_commit_message` | string | no | Custom merge commit message. |
-| `squash_commit_message` | string | no | Custom squash commit message. |
-| `squash` | boolean | no | If `true` the commits are squashed into a single commit on merge. |
-| `should_remove_source_branch` | boolean | no | If `true` removes the source branch. |
-| `merge_when_pipeline_succeeds` | boolean | no | If `true` the MR is merged when the pipeline succeeds. |
-| `sha` | string | no | If present, then this SHA must match the HEAD of the source branch, otherwise the merge fails. |
+| 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. |
+| `merge_commit_message` | string | **{dotted-circle}** No | Custom merge commit message. |
+| `merge_when_pipeline_succeeds` | boolean | **{dotted-circle}** No | If `true`, the merge request is merged when the pipeline succeeds. |
+| `sha` | string | **{dotted-circle}** No | If present, then this SHA must match the HEAD of the source branch, otherwise the merge fails. |
+| `should_remove_source_branch` | boolean | **{dotted-circle}** No | If `true`, removes the source branch. |
+| `squash_commit_message` | string | **{dotted-circle}** No | Custom squash commit message. |
+| `squash` | boolean | **{dotted-circle}** No | If `true`, the commits are squashed into a single commit on merge. |
```json
{
@@ -1681,12 +1661,12 @@ the `approvals_before_merge` parameter:
This API returns specific HTTP status codes on failure:
-| HTTP Status | Message | Reason |
-|:------------|--------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|
-| `401` | `Unauthorized` | This user does not have permission to accept this merge request. |
-| `405` | `Method Not Allowed` | The merge request is not able to be merged. |
-| `409` | `SHA does not match HEAD of source branch` | The provided `sha` parameter does not match the HEAD of the source. |
-| `422` | `Branch cannot be merged` | The merge request failed to merge. |
+| HTTP Status | Message | Reason |
+|:------------|---------|--------|
+| `401` | `Unauthorized` | This user does not have permission to accept this merge request. |
+| `405` | `Method Not Allowed` | The merge request is not able to be merged. |
+| `409` | `SHA does not match HEAD of source branch` | The provided `sha` parameter does not match the HEAD of the source. |
+| `422` | `Branch cannot be merged` | The merge request failed to merge. |
For additional important notes on response data, read [Single merge request response notes](#single-merge-request-response-notes).
@@ -1709,12 +1689,12 @@ It returns the HEAD commit of `refs/merge-requests/:iid/merge` in the response b
GET /projects/:id/merge_requests/:merge_request_iid/merge_ref
```
-Parameters:
+Supported attributes:
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
+| 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. |
```json
{
@@ -1736,12 +1716,12 @@ This API returns specific HTTP status codes on failure:
POST /projects/:id/merge_requests/:merge_request_iid/cancel_merge_when_pipeline_succeeds
```
-Parameters:
+Supported attributes:
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
+| 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. |
```json
{
@@ -1905,11 +1885,11 @@ you receive a `403 Forbidden` response.
PUT /projects/:id/merge_requests/:merge_request_iid/rebase
```
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
-| `skip_ci` | boolean | no | Set to `true` to skip creating a CI pipeline. |
+| 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. |
+| `skip_ci` | boolean | **{dotted-circle}** No | Set to `true` to skip creating a CI pipeline. |
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/76/merge_requests/1/rebase"
@@ -1956,6 +1936,26 @@ If the rebase operation fails, the response includes the following:
}
```
+## Reset approvals of a merge request
+
+Clear all approvals of merge request.
+
+Available only for [bot users](../user/project/settings/project_access_tokens.md#bot-users-for-projects)
+based on project or group tokens. Users without bot permissions receive a `401 Unauthorized` response.
+
+```plaintext
+PUT /projects/:id/merge_requests/:merge_request_iid/reset_approvals
+```
+
+| 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. |
+
+```shell
+curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/76/merge_requests/1/reset_approvals"
+```
+
## Comments on merge requests
Comments are done via the [notes](notes.md) resource.
@@ -1968,10 +1968,10 @@ Get all the issues that would be closed by merging the provided merge request.
GET /projects/:id/merge_requests/:merge_request_iid/closes_issues
```
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
+| 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. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/76/merge_requests/1/closes_issues"
@@ -2044,10 +2044,10 @@ status code `HTTP 304 Not Modified` is returned.
POST /projects/:id/merge_requests/:merge_request_iid/subscribe
```
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
+| 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. |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/merge_requests/17/subscribe"
@@ -2214,10 +2214,10 @@ not subscribed to the merge request, the status code `HTTP 304 Not Modified` is
POST /projects/:id/merge_requests/:merge_request_iid/unsubscribe
```
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
+| 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. |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/merge_requests/17/unsubscribe"
@@ -2384,10 +2384,10 @@ status code `HTTP 304 Not Modified` is returned.
POST /projects/:id/merge_requests/:merge_request_iid/todo
```
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
+| 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. |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/merge_requests/27/todo"
@@ -2513,8 +2513,8 @@ GET /projects/:id/merge_requests/:merge_request_iid/versions
| Attribute | Type | Required | Description |
|---------------------|---------|----------|---------------------------------------|
-| `id` | String | yes | The ID of the project. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
+| `id` | String | **{check-circle}** Yes | The ID of the project. |
+| `merge_request_iid` | integer | **{check-circle}** Yes | The internal ID of the merge request. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/merge_requests/1/versions"
@@ -2548,8 +2548,8 @@ Example response:
| SHA field | Purpose |
|--------------------|-------------------------------------------------------------------------------------|
-| `head_commit_sha` | The HEAD commit of the source branch. |
| `base_commit_sha` | The merge-base commit SHA between the source branch and the target branches. |
+| `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
@@ -2563,9 +2563,9 @@ GET /projects/:id/merge_requests/:merge_request_iid/versions/:version_id
| Attribute | Type | Required | Description |
|---------------------|---------|----------|-------------------------------------------|
-| `id` | String | yes | The ID of the project. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
-| `version_id` | integer | yes | The ID of the merge request diff version. |
+| `id` | String | **{check-circle}** Yes | The ID of the project. |
+| `merge_request_iid` | integer | **{check-circle}** Yes | The internal ID of the merge request. |
+| `version_id` | integer | **{check-circle}** Yes | The ID of the merge request diff version. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/merge_requests/1/versions/1"
@@ -2629,11 +2629,11 @@ Sets an estimated time of work for this merge request.
POST /projects/:id/merge_requests/:merge_request_iid/time_estimate
```
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
-| `duration` | string | yes | The duration in human format, such as `3h30m`. |
+| 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. |
+| `duration` | string | **{check-circle}** Yes | The duration in human format, such as `3h30m`. |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/merge_requests/93/time_estimate?duration=3h30m"
@@ -2658,10 +2658,10 @@ Resets the estimated time for this merge request to 0 seconds.
POST /projects/:id/merge_requests/:merge_request_iid/reset_time_estimate
```
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of a project's merge_request. |
+| 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 a project's merge request. |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/merge_requests/93/reset_time_estimate"
@@ -2686,12 +2686,12 @@ Adds spent time for this merge request.
POST /projects/:id/merge_requests/:merge_request_iid/add_spent_time
```
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
-| `duration` | string | yes | The duration in human format, such as `3h30m` |
-| `summary` | string | no | A summary of how the time was spent. |
+| 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. |
+| `duration` | string | **{check-circle}** Yes | The duration in human format, such as `3h30m` |
+| `summary` | string | **{dotted-circle}** No | A summary of how the time was spent. |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/merge_requests/93/add_spent_time?duration=1h"
@@ -2716,10 +2716,10 @@ Resets the total spent time for this merge request to 0 seconds.
POST /projects/:id/merge_requests/:merge_request_iid/reset_spent_time
```
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of a project's merge_request. |
+| 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 a project's merge request. |
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/merge_requests/93/reset_spent_time"
@@ -2742,10 +2742,10 @@ Example response:
GET /projects/:id/merge_requests/:merge_request_iid/time_stats
```
-| 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. |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request. |
+| 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. |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/merge_requests/93/time_stats"
diff --git a/doc/api/metadata.md b/doc/api/metadata.md
index 70c29ef5748..0f27960937d 100644
--- a/doc/api/metadata.md
+++ b/doc/api/metadata.md
@@ -1,6 +1,6 @@
---
-stage: Ecosystem
-group: Integrations
+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/engineering/ux/technical-writing/#assignments
---
diff --git a/doc/api/oauth2.md b/doc/api/oauth2.md
index 12704f6fc87..0b7e0ba08eb 100644
--- a/doc/api/oauth2.md
+++ b/doc/api/oauth2.md
@@ -261,7 +261,7 @@ Check the [RFC spec](https://tools.ietf.org/html/rfc6749#section-4.3) for a
detailed flow description.
NOTE:
-The Resource Owner Password Credentials is disabled for users with
+The Resource Owner Password Credentials is disabled for users with
[two-factor authentication](../user/profile/account/two_factor_authentication.md) turned on.
These users can access the API using [personal access tokens](../user/profile/personal_access_tokens.md)
instead.
@@ -335,43 +335,6 @@ access_token = client.password.get_token('user@example.com', 'secret')
puts access_token.token
```
-<!--- start_remove The following content will be removed on remove_date: '2022-08-22' -->
-
-### Implicit grant flow (removed)
-
-Implicit grant flow is inherently insecure and the IETF has removed it in [OAuth 2.1](https://oauth.net/2.1/).
-It is [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/288516) in GitLab 14.0 and is
-[removed](https://gitlab.com/gitlab-org/gitlab/-/issues/344609) in GitLab 15.0.
-
-We recommend that you use [Authorization code with PKCE](#authorization-code-with-proof-key-for-code-exchange-pkce)
-instead.
-
-Unlike the authorization code flow, the client receives an `access token`
-immediately as a result of the authorization request. The flow does not use the
-client secret or the authorization code, as the application
-code and storage is accessible on client browsers and mobile devices.
-
-To request the access token, you should redirect the user to the
-`/oauth/authorize` endpoint using `token` response type:
-
-```plaintext
-https://gitlab.example.com/oauth/authorize?client_id=APP_ID&redirect_uri=REDIRECT_URI&response_type=token&state=YOUR_UNIQUE_STATE_HASH&scope=REQUESTED_SCOPES
-```
-
-This prompts the user to approve the applications access to their account
-based on the scopes specified in `REQUESTED_SCOPES` and then redirect back to
-the `REDIRECT_URI` you provided. The [scope parameter](../integration/oauth_provider.md#authorized-applications)
- is a space-separated list of scopes you want to have access to (for example, `scope=read_user+profile`
-would request `read_user` and `profile` scopes). The redirect
-includes a fragment with `access_token` as well as token details in GET
-parameters, for example:
-
-```plaintext
-https://example.com/oauth/redirect#access_token=ABCDExyz123&state=YOUR_UNIQUE_STATE_HASH&token_type=bearer&expires_in=3600
-```
-
-<!--- end_remove -->
-
## Access GitLab API with `access token`
The `access token` allows you to make requests to the API on behalf of a user.
@@ -391,7 +354,11 @@ curl --header "Authorization: Bearer OAUTH-TOKEN" "https://gitlab.example.com/ap
A token with [scope](../integration/oauth_provider.md#authorized-applications)
`read_repository` or `write_repository` can access Git over HTTPS. Use the token as the password.
-The username must be `oauth2`, not your username.
+The username must be `oauth2`, not your username:
+
+```plaintext
+https://oauth2:<your_access_token>@gitlab.example.com/project_path/project_name.git
+```
## Retrieve the token information
diff --git a/doc/api/packages.md b/doc/api/packages.md
index 3cd93bd09c1..d91f4f0de7b 100644
--- a/doc/api/packages.md
+++ b/doc/api/packages.md
@@ -213,6 +213,7 @@ Example response:
"delete_api_path": "/namespace1/project1/-/packages/1"
},
"created_at": "2019-11-27T03:37:38.711Z",
+ "last_downloaded_at": "2022-09-07T07:51:50.504Z"
"pipelines": [
{
"id": 123,
diff --git a/doc/api/packages/conan.md b/doc/api/packages/conan.md
index 3ac2eeb40b1..637c3d27d75 100644
--- a/doc/api/packages/conan.md
+++ b/doc/api/packages/conan.md
@@ -38,7 +38,7 @@ The examples in this document all use the instance-level prefix.
/packages/conan/v1
```
-When using the instance-level routes, be aware that there is a
+When using the instance-level routes, be aware that there is a
[naming restriction](../../user/packages/conan_repository/index.md#package-recipe-naming-convention-for-instance-remotes)
for Conan recipes.
diff --git a/doc/api/packages/debian.md b/doc/api/packages/debian.md
index 4abb7bc7112..598124ba2b9 100644
--- a/doc/api/packages/debian.md
+++ b/doc/api/packages/debian.md
@@ -212,11 +212,11 @@ curl --header "Private-Token: <personal_access_token>" \
This writes the downloaded file using the remote filename in the current directory.
-## Download a binary file's index
+## Download a packages index
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64923) in GitLab 14.2.
-Download a distribution index.
+Download a packages index.
```plaintext
GET <route-prefix>/dists/*distribution/:component/binary-:architecture/Packages
@@ -229,14 +229,73 @@ GET <route-prefix>/dists/*distribution/:component/binary-:architecture/Packages
| `architecture` | string | yes | The distribution architecture type. |
```shell
-curl --header "Private-Token: <personal_access_token>" "https://gitlab.example.com/api/v4/projects/1/packages/debian/dists/my-distro/main/amd64/Packages"
+curl --header "Private-Token: <personal_access_token>" "https://gitlab.example.com/api/v4/projects/1/packages/debian/dists/my-distro/main/binary-amd64/Packages"
```
Write the output to a file:
```shell
curl --header "Private-Token: <personal_access_token>" \
- "https://gitlab.example.com/api/v4/projects/1/packages/debian/dists/my-distro/main/amd64/Packages" \
+ "https://gitlab.example.com/api/v4/projects/1/packages/debian/dists/my-distro/main/binary-amd64/Packages" \
+ --remote-name
+```
+
+This writes the downloaded file using the remote filename in the current directory.
+
+## Download a Debian Installer packages index
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71918) in GitLab 15.4.
+
+Download a Debian Installer packages index.
+
+```plaintext
+GET <route-prefix>/dists/*distribution/:component/debian-installer/binary-:architecture/Packages
+```
+
+| Attribute | Type | Required | Description |
+| ----------------- | ------ | -------- | ----------- |
+| `distribution` | string | yes | The codename or suite of the Debian distribution. |
+| `component` | string | yes | The distribution component name. |
+| `architecture` | string | yes | The distribution architecture type. |
+
+```shell
+curl --header "Private-Token: <personal_access_token>" "https://gitlab.example.com/api/v4/projects/1/packages/debian/dists/my-distro/main/debian-installer/binary-amd64/Packages"
+```
+
+Write the output to a file:
+
+```shell
+curl --header "Private-Token: <personal_access_token>" \
+ "https://gitlab.example.com/api/v4/projects/1/packages/debian/dists/my-distro/main/debian-installer/binary-amd64/Packages" \
+ --remote-name
+```
+
+This writes the downloaded file using the remote filename in the current directory.
+
+## Download a source packages index
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/71918) in GitLab 15.4.
+
+Download a source packages index.
+
+```plaintext
+GET <route-prefix>/dists/*distribution/:component/source/Sources
+```
+
+| Attribute | Type | Required | Description |
+| ----------------- | ------ | -------- | ----------- |
+| `distribution` | string | yes | The codename or suite of the Debian distribution. |
+| `component` | string | yes | The distribution component name. |
+
+```shell
+curl --header "Private-Token: <personal_access_token>" "https://gitlab.example.com/api/v4/projects/1/packages/debian/dists/my-distro/main/source/Sources"
+```
+
+Write the output to a file:
+
+```shell
+curl --header "Private-Token: <personal_access_token>" \
+ "https://gitlab.example.com/api/v4/projects/1/packages/debian/dists/my-distro/main/source/Sources" \
--remote-name
```
diff --git a/doc/api/packages/terraform-modules.md b/doc/api/packages/terraform-modules.md
index 24db7094a3c..daafe0579e7 100644
--- a/doc/api/packages/terraform-modules.md
+++ b/doc/api/packages/terraform-modules.md
@@ -12,8 +12,8 @@ WARNING:
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 Maven packages from the GitLab
-package registry, see the [Terraform modules registry documentation](../../user/packages/terraform_module_registry/index.md).
+For instructions on how to upload and install Terraform modules from the GitLab
+infrastructure registry, see the [Terraform modules registry documentation](../../user/packages/terraform_module_registry/index.md).
## List available versions for a specific module
@@ -114,7 +114,7 @@ Example response:
## Get specific version for a specific module
-Get information about the latest version for a given module.
+Get information about a specific version for a given module.
```plaintext
GET packages/terraform/modules/v1/:module_namespace/:module_name/:module_system/1.0.0
diff --git a/doc/api/personal_access_tokens.md b/doc/api/personal_access_tokens.md
index 620b5c2ed0b..849b5c75684 100644
--- a/doc/api/personal_access_tokens.md
+++ b/doc/api/personal_access_tokens.md
@@ -1,5 +1,5 @@
---
-stage: Manage
+stage: Govern
group: Compliance
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
---
@@ -134,9 +134,13 @@ curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://git
### Using a request header
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/350240) in GitLab 15.0.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/350240) in GitLab 15.0. Limited to tokens with `api` scope.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/369103) in GitLab 15.4, any token can use this endpoint.
-Revokes a personal access token that is passed in using a request header.
+Revokes a personal access token that is passed in using a request header. Requires:
+
+- `api` scope in GitLab 15.0 to GitLab 15.3.
+- Any scope in GitLab 15.4 and later.
```plaintext
DELETE /personal_access_tokens/self
diff --git a/doc/api/pipeline_triggers.md b/doc/api/pipeline_triggers.md
index 8db071bf811..9f3120bd5d7 100644
--- a/doc/api/pipeline_triggers.md
+++ b/doc/api/pipeline_triggers.md
@@ -155,7 +155,7 @@ or a [CI/CD job token](../ci/jobs/ci_job_token.md) for authentication.
With a CI/CD job token, the [triggered pipeline is a multi-project pipeline](../ci/jobs/ci_job_token.md#trigger-a-multi-project-pipeline-by-using-a-cicd-job-token).
The job that authenticates the request becomes associated with the upstream pipeline,
-which is visible on the [pipeline graph](../ci/pipelines/multi_project_pipelines.md#multi-project-pipeline-visualization).
+which is visible on the [pipeline graph](../ci/pipelines/downstream_pipelines.md#view-multi-project-pipelines-in-pipeline-graphs).
If you use a trigger token in a job, the job is not associated with the upstream pipeline.
diff --git a/doc/api/pipelines.md b/doc/api/pipelines.md
index 2e601f6e24a..23c55cfb177 100644
--- a/doc/api/pipelines.md
+++ b/doc/api/pipelines.md
@@ -80,7 +80,7 @@ Example of response
Get one pipeline from a project.
-You can also get a single [child pipeline](../ci/pipelines/parent_child_pipelines.md).
+You can also get a single [child pipeline](../ci/pipelines/downstream_pipelines.md#parent-child-pipelines).
[Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36494) in GitLab 13.3.
```plaintext
@@ -427,7 +427,7 @@ related objects, such as builds, logs, artifacts, and triggers.
**This action cannot be undone.**
Deleting a pipeline does not automatically delete its
-[child pipelines](../ci/pipelines/parent_child_pipelines.md).
+[child pipelines](../ci/pipelines/downstream_pipelines.md#parent-child-pipelines).
See the [related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/39503)
for details.
diff --git a/doc/api/product_analytics.md b/doc/api/product_analytics.md
new file mode 100644
index 00000000000..c3114baff8a
--- /dev/null
+++ b/doc/api/product_analytics.md
@@ -0,0 +1,67 @@
+---
+stage: Analyze
+group: Product Analytics
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+---
+
+# Product analytics API
+
+> Introduced in GitLab 15.4 [with a flag](../administration/feature_flags.md) named `cube_api_proxy`. Disabled by default.
+
+FLAG:
+On self-managed GitLab, by default this feature is not available. To make it available per project or for your entire instance, ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `cube_api_proxy`.
+On GitLab.com, this feature is not available.
+This feature is not ready for production use.
+
+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
+
+Generate an access token that can be used to query the Cube API. For example:
+
+```plaintext
+POST /projects/:id/product_analytics/request
+```
+
+| Attribute | Type | Required | Description |
+| --------- |------------------| -------- |---------------------------------------------------------------|
+| `id` | integer | yes | The ID of a project that the current user has read access to. |
+
+### Request body
+
+The body of the request should be a valid Cube query.
+
+```json
+{
+ "query": {
+ "measures": [
+ "Jitsu.count"
+ ],
+ "timeDimensions": [
+ {
+ "dimension": "Jitsu.utcTime",
+ "dateRange": "This week"
+ }
+ ],
+ "order": [
+ [
+ "Jitsu.count",
+ "desc"
+ ],
+ [
+ "Jitsu.docPath",
+ "desc"
+ ],
+ [
+ "Jitsu.utcTime",
+ "asc"
+ ]
+ ],
+ "dimensions": [
+ "Jitsu.docPath"
+ ],
+ "limit": 23
+ }
+}
+```
diff --git a/doc/api/project_level_variables.md b/doc/api/project_level_variables.md
index 81bb4a26614..39e7f441b42 100644
--- a/doc/api/project_level_variables.md
+++ b/doc/api/project_level_variables.md
@@ -5,7 +5,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
type: reference, api
---
-# Project-level Variables API **(FREE)**
+# Project-level CI/CD variables API **(FREE)**
## List project variables
@@ -44,9 +44,10 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
]
```
-## Show variable details
+## Get a single variable
-Get the details of a project's specific variable.
+Get the details of a single variable. If there are multiple variables with the same key,
+use `filter` to select the correct `environment_scope`.
```plaintext
GET /projects/:id/variables/:key
@@ -73,9 +74,11 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
}
```
-## Create variable
+## Create a variable
-Create a new variable.
+Create a new variable. If a variable with the same `key` already exists, the new variable
+must have a different `environment_scope`. Otherwise, GitLab returns a message similar to:
+`VARIABLE_NAME has already been taken`.
```plaintext
POST /projects/:id/variables
@@ -107,9 +110,10 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" \
}
```
-## Update variable
+## Update a variable
-Update a project's variable.
+Update a project's variable. If there are multiple variables with the same key,
+use `filter` to select the correct `environment_scope`.
```plaintext
PUT /projects/:id/variables/:key
@@ -142,9 +146,10 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" \
}
```
-## Remove variable
+## Delete a variable
-Remove a project's variable.
+Delete a project's variable. If there are multiple variables with the same key,
+use `filter` to select the correct `environment_scope`.
```plaintext
DELETE /projects/:id/variables/:key
@@ -165,10 +170,34 @@ curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://git
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34490) in GitLab 13.2.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/227052) in GitLab 13.4.
-This parameter is used for filtering by attributes, such as `environment_scope`.
+When multiple variables have the same `key`, [GET](#get-a-single-variable), [PUT](#update-a-variable),
+or [DELETE](#delete-a-variable) requests might return:
-Example usage:
-
-```shell
-curl --globoff --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/variables/VARIABLE_1?filter[environment_scope]=production"
+```plaintext
+There are multiple variables with provided parameters. Please use 'filter[environment_scope]'.
```
+
+Use `filter[environment_scope]` to select the variable with the matching `environment_scope` attribute.
+
+For example:
+
+- GET:
+
+ ```shell
+ curl --globoff --header "PRIVATE-TOKEN: <your_access_token>" \
+ "https://gitlab.example.com/api/v4/projects/1/variables/SCOPED_VARIABLE_1?filter[environment_scope]=production"
+ ```
+
+- PUT:
+
+ ```shell
+ curl --request PUT --globoff --header "PRIVATE-TOKEN: <your_access_token>" \
+ "https://gitlab.example.com/api/v4/projects/1/variables/SCOPED_VARIABLE_1?value=scoped-variable-updated-value&environment_scope=production&filter[environment_scope]=production"
+ ```
+
+- DELETE:
+
+ ```shell
+ curl --request DELETE --globoff --header "PRIVATE-TOKEN: <your_access_token>" \
+ "https://gitlab.example.com/api/v4/projects/1/variables/SCOPED_VARIABLE_1?filter[environment_scope]=production"
+ ```
diff --git a/doc/api/project_templates.md b/doc/api/project_templates.md
index 7763087e759..14dd0761487 100644
--- a/doc/api/project_templates.md
+++ b/doc/api/project_templates.md
@@ -30,8 +30,8 @@ GET /projects/:id/templates/:type
| Attribute | Type | Required | Description |
| ---------- | ------ | -------- | ----------- |
-| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) |
-| `type` | string | yes | The type of the template. Accepted values are: `dockerfiles`, `gitignores`, `gitlab_ci_ymls`, `licenses`, `issues`, `merge_requests` |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
+| `type` | string | **{check-circle}** Yes | The type of the template. Accepted values are: `dockerfiles`, `gitignores`, `gitlab_ci_ymls`, `licenses`, `issues`, or `merge_requests`. |
Example response (licenses):
@@ -96,12 +96,12 @@ GET /projects/:id/templates/:type/:name
| Attribute | Type | Required | Description |
| ---------- | ------ | -------- | ----------- |
-| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) |
-| `type` | string | yes| The type of the template. One of: `dockerfiles`, `gitignores`, `gitlab_ci_ymls`, `licenses`, `issues`, or `merge_requests`. |
-| `name` | string | yes | The key of the template, as obtained from the collection endpoint |
-| `source_template_project_id` | integer | no | The project ID where a given template is being stored. This is useful when multiple templates from different projects have the same name. If multiple templates have the same name, the match from `closest ancestor` is returned if `source_template_project_id` is not specified |
-| `project` | string | no | The project name to use when expanding placeholders in the template. Only affects licenses |
-| `fullname` | string | no | The full name of the copyright holder to use when expanding placeholders in the template. Only affects licenses |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
+| `name` | string | **{check-circle}** Yes | The key of the template, as obtained from the collection endpoint. |
+| `type` | string | **{check-circle}** Yes | The type of the template. One of: `dockerfiles`, `gitignores`, `gitlab_ci_ymls`, `licenses`, `issues`, or `merge_requests`. |
+| `fullname` | string | **{dotted-circle}** No | The full name of the copyright holder to use when expanding placeholders in the template. Affects only licenses. |
+| `project` | string | **{dotted-circle}** No | The project name to use when expanding placeholders in the template. Affects only licenses. |
+| `source_template_project_id` | integer | **{dotted-circle}** No | The project ID where a given template is being stored. Helpful when multiple templates from different projects have the same name. If multiple templates have the same name, the match from `closest ancestor` is returned if `source_template_project_id` is not specified, |
Example response (Dockerfile):
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 75eea394a40..26733801b45 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -417,6 +417,7 @@ GET /users/:user_id/projects
"merge_method": "merge",
"squash_option": "default_on",
"autoclose_referenced_issues": true,
+ "enforce_auth_checks_on_uploads": true,
"suggestion_commit_message": null,
"merge_commit_template": null,
"squash_commit_template": null,
@@ -539,6 +540,7 @@ GET /users/:user_id/projects
"service_desk_enabled": false,
"service_desk_address": null,
"autoclose_referenced_issues": true,
+ "enforce_auth_checks_on_uploads": true,
"suggestion_commit_message": null,
"merge_commit_template": null,
"squash_commit_template": null,
@@ -671,6 +673,7 @@ Example response:
"merge_method": "merge",
"squash_option": "default_on",
"autoclose_referenced_issues": true,
+ "enforce_auth_checks_on_uploads": true,
"suggestion_commit_message": null,
"merge_commit_template": null,
"squash_commit_template": null,
@@ -786,6 +789,7 @@ Example response:
"service_desk_enabled": false,
"service_desk_address": null,
"autoclose_referenced_issues": true,
+ "enforce_auth_checks_on_uploads": true,
"suggestion_commit_message": null,
"merge_commit_template": null,
"squash_commit_template": null,
@@ -962,6 +966,7 @@ GET /projects/:id
"service_desk_address": null,
"autoclose_referenced_issues": true,
"suggestion_commit_message": null,
+ "enforce_auth_checks_on_uploads": true,
"merge_commit_template": null,
"squash_commit_template": null,
"marked_for_deletion_at": "2020-04-03", // Deprecated and will be removed in API v5 in favor of marked_for_deletion_on
@@ -1218,7 +1223,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your-token>" \
| `mirror` **(PREMIUM)** | boolean | **{dotted-circle}** No | Enables pull mirroring in a project. |
| `namespace_id` | integer | **{dotted-circle}** No | Namespace for the new project (defaults to the current user's namespace). |
| `only_allow_merge_if_all_discussions_are_resolved` | boolean | **{dotted-circle}** No | Set whether merge requests can only be merged when all the discussions are resolved. |
-| `only_allow_merge_if_pipeline_succeeds` | boolean | **{dotted-circle}** No | Set whether merge requests can only be merged with successful pipelines. This setting is named [**Pipelines must succeed**](../user/project/merge_requests/merge_when_pipeline_succeeds.md#only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds) in the project settings. |
+| `only_allow_merge_if_pipeline_succeeds` | boolean | **{dotted-circle}** No | Set whether merge requests can only be merged with successful pipelines. This setting is named [**Pipelines must succeed**](../user/project/merge_requests/merge_when_pipeline_succeeds.md#require-a-successful-pipeline-for-merge) in the project settings. |
| `operations_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `packages_enabled` | boolean | **{dotted-circle}** No | Enable or disable packages repository feature. |
| `pages_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, `enabled`, or `public`. |
@@ -1277,6 +1282,7 @@ POST /projects/user/:user_id
| `default_branch` | string | **{dotted-circle}** No | The [default branch](../user/project/repository/branches/default.md) name. Requires `initialize_with_readme` to be `true`. |
| `description` | string | **{dotted-circle}** No | Short project description. |
| `emails_disabled` | boolean | **{dotted-circle}** No | Disable email notifications. |
+| `enforce_auth_checks_on_uploads` | boolean | **{dotted-circle}** No | Enforce [auth checks](../security/user_file_uploads.md#enable-authorization-checks-for-all-media-files) on uploads. |
| `external_authorization_classification_label` **(PREMIUM)** | string | **{dotted-circle}** No | The classification label for the project. |
| `forking_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `group_with_project_templates_id` **(PREMIUM)** | integer | **{dotted-circle}** No | For group-level custom templates, specifies ID of group from which all the custom project templates are sourced. Leave empty for instance-level templates. Requires `use_custom_template` to be true. |
@@ -1370,6 +1376,7 @@ Supported attributes:
| `default_branch` | string | **{dotted-circle}** No | The [default branch](../user/project/repository/branches/default.md) name. |
| `description` | string | **{dotted-circle}** No | Short project description. |
| `emails_disabled` | boolean | **{dotted-circle}** No | Disable email notifications. |
+| `enforce_auth_checks_on_uploads` | boolean | **{dotted-circle}** No | Enforce [auth checks](../security/user_file_uploads.md#enable-authorization-checks-for-all-media-files) on uploads. |
| `external_authorization_classification_label` **(PREMIUM)** | string | **{dotted-circle}** No | The classification label for the project. |
| `forking_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `import_url` | string | **{dotted-circle}** No | URL the repository was imported from. |
@@ -1542,6 +1549,7 @@ Example responses:
"merge_method": "merge",
"squash_option": "default_on",
"autoclose_referenced_issues": true,
+ "enforce_auth_checks_on_uploads": true,
"suggestion_commit_message": null,
"merge_commit_template": null,
"container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site",
@@ -1648,6 +1656,7 @@ Example response:
"merge_method": "merge",
"squash_option": "default_on",
"autoclose_referenced_issues": true,
+ "enforce_auth_checks_on_uploads": true,
"suggestion_commit_message": null,
"merge_commit_template": null,
"container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site",
@@ -1752,6 +1761,7 @@ Example response:
"merge_method": "merge",
"squash_option": "default_on",
"autoclose_referenced_issues": true,
+ "enforce_auth_checks_on_uploads": true,
"suggestion_commit_message": null,
"merge_commit_template": null,
"container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site",
@@ -1952,6 +1962,7 @@ Example response:
"merge_method": "merge",
"squash_option": "default_on",
"autoclose_referenced_issues": true,
+ "enforce_auth_checks_on_uploads": true,
"suggestion_commit_message": null,
"merge_commit_template": null,
"container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site",
@@ -2079,6 +2090,7 @@ Example response:
"merge_method": "merge",
"squash_option": "default_on",
"autoclose_referenced_issues": true,
+ "enforce_auth_checks_on_uploads": true,
"suggestion_commit_message": null,
"merge_commit_template": null,
"container_registry_image_prefix": "registry.example.com/diaspora/diaspora-project-site",
@@ -2248,6 +2260,19 @@ Returned object:
}
```
+## Remove a project avatar
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92604) in GitLab 15.4.
+
+To remove a project avatar, use a blank value for the `avatar` attribute.
+
+Example request:
+
+```shell
+curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" \
+ --data "avatar=" "https://gitlab.example.com/api/v4/projects/5"
+```
+
## Share project with group
Allow to share project with group.
@@ -2596,6 +2621,50 @@ DELETE /projects/:id/push_rule
|-----------|----------------|------------------------|-------------|
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
+## Get groups to which a user can transfer a project
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/371006) in GitLab 15.4
+
+Retrieve a list of groups to which the user can transfer a project.
+
+```plaintext
+GET /projects/:id/transfer_locations
+```
+
+| 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). |
+| `search` | string | **{dotted-circle}** No | The group names to search for. |
+
+Example request:
+
+```shell
+curl --request GET "https://gitlab.example.com/api/v4/projects/1/transfer_locations"
+```
+
+Example response:
+
+```json
+[
+ {
+ "id": 27,
+ "web_url": "https://gitlab.example.com/groups/gitlab",
+ "name": "GitLab",
+ "avatar_url": null,
+ "full_name": "GitLab",
+ "full_path": "GitLab"
+ },
+ {
+ "id": 31,
+ "web_url": "https://gitlab.example.com/groups/foobar",
+ "name": "FooBar",
+ "avatar_url": null,
+ "full_name": "FooBar",
+ "full_path": "FooBar"
+ }
+]
+```
+
## Transfer a project to a new namespace
> The `_links.cluster_agents` attribute in the response [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/347047) in GitLab 14.10.
diff --git a/doc/api/protected_environments.md b/doc/api/protected_environments.md
index 46cb461b64b..5b52d11feda 100644
--- a/doc/api/protected_environments.md
+++ b/doc/api/protected_environments.md
@@ -11,7 +11,7 @@ type: concepts, howto
## Valid access levels
-The access levels are defined in the `ProtectedEnvironment::DeployAccessLevel::ALLOWED_ACCESS_LEVELS` method.
+The access levels are defined in the `ProtectedEnvironments::DeployAccessLevel::ALLOWED_ACCESS_LEVELS` method.
Currently, these levels are recognized:
```plaintext
@@ -54,6 +54,7 @@ Example response:
"name":"production",
"deploy_access_levels":[
{
+ "id": 12,
"access_level":40,
"access_level_description":"Maintainers",
"user_id":null,
@@ -61,7 +62,7 @@ Example response:
"group_inheritance_type": 0
}
],
- "required_approval_count": 0
+ "required_approval_count": 0
}
]
```
@@ -90,6 +91,7 @@ Example response:
"name":"production",
"deploy_access_levels":[
{
+ "id": 12,
"access_level": 40,
"access_level_description": "Maintainers",
"user_id": null,
@@ -97,7 +99,7 @@ Example response:
"group_inheritance_type": 0
}
],
- "required_approval_count": 0
+ "required_approval_count": 0
}
```
@@ -109,6 +111,20 @@ Protects a single environment:
POST /projects/:id/protected_environments
```
+| 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. |
+| `name` | string | yes | The name of the environment. |
+| `deploy_access_levels` | array | yes | Array of access levels allowed to deploy, with each described by a hash. |
+| `required_approval_count` | integer | no | The number of approvals required to deploy to this environment. |
+| `approval_rules` | array | no | Array of access levels allowed to approve, with each described by a hash. See [Multiple approval rules](../ci/environments/deployment_approvals.md#multiple-approval-rules) for more information. |
+
+Elements in the `deploy_access_levels` and `approval_rules` array should be one of `user_id`, `group_id` or
+`access_level`, and take the form `{user_id: integer}`, `{group_id: integer}` or
+`{access_level: integer}`. Optionally you can specify the `group_inheritance_type` on each as one of the [valid group inheritance types](#group-inheritance-types).
+
+Each user must have access to the project and each group must [have this project shared](../user/project/members/share_project_with_groups.md).
+
```shell
curl --header 'Content-Type: application/json' --request POST \
--data '{"name": "production", "deploy_access_levels": [{"group_id": 9899826}], "approval_rules": [{"group_id": 134}, {"group_id": 135, "required_approvals": 2}]}' \
@@ -116,19 +132,84 @@ curl --header 'Content-Type: application/json' --request POST \
"https://gitlab.example.com/api/v4/projects/22034114/protected_environments"
```
+Example response:
+
+```json
+{
+ "name": "production",
+ "deploy_access_levels": [
+ {
+ "id": 12,
+ "access_level": 40,
+ "access_level_description": "protected-access-group",
+ "user_id": null,
+ "group_id": 9899826,
+ "group_inheritance_type": 0
+ }
+ ],
+ "required_approval_count": 0,
+ "approval_rules": [
+ {
+ "id": 38,
+ "user_id": null,
+ "group_id": 134,
+ "access_level": null,
+ "access_level_description": "qa-group",
+ "required_approvals": 1,
+ "group_inheritance_type": 0
+ },
+ {
+ "id": 39,
+ "user_id": null,
+ "group_id": 135,
+ "access_level": null,
+ "access_level_description": "security-group",
+ "required_approvals": 2,
+ "group_inheritance_type": 0
+ }
+ ]
+}
+```
+
+## Update a protected environment
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/351854) in GitLab 15.4.
+
+Updates a single environment.
+
+```plaintext
+PUT /projects/:id/protected_environments/:name
+```
+
| 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. |
| `name` | string | yes | The name of the environment. |
-| `deploy_access_levels` | array | yes | Array of access levels allowed to deploy, with each described by a hash. |
-| `required_approval_count` | integer | no | The number of approvals required to deploy to this environment. This is part of Deployment Approvals, which isn't yet available for use. For details, see [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/343864). |
+| `deploy_access_levels` | array | no | Array of access levels allowed to deploy, with each described by a hash. |
+| `required_approval_count` | integer | no | The number of approvals required to deploy to this environment. |
| `approval_rules` | array | no | Array of access levels allowed to approve, with each described by a hash. See [Multiple approval rules](../ci/environments/deployment_approvals.md#multiple-approval-rules) for more information. |
Elements in the `deploy_access_levels` and `approval_rules` array should be one of `user_id`, `group_id` or
`access_level`, and take the form `{user_id: integer}`, `{group_id: integer}` or
`{access_level: integer}`. Optionally you can specify the `group_inheritance_type` on each as one of the [valid group inheritance types](#group-inheritance-types).
-Each user must have access to the project and each group must [have this project shared](../user/project/members/share_project_with_groups.md).
+To update:
+
+- **`user_id`**: Ensure the updated user has access to the project. You must also pass the `id` of either a `deploy_access_level` or `approval_rule` in the respective hash.
+- **`group_id`**: Ensure the updated group [have this project shared](../user/project/members/share_project_with_groups.md). You must also pass the `id` of either a `deploy_access_level` or `approval_rule` in the respective hash.
+
+To delete:
+
+- You must pass `_destroy` set to `true`. See the following examples.
+
+### Example: Create a `deploy_access_level` record
+
+```shell
+curl --header 'Content-Type: application/json' --request PUT \
+ --data '{"deploy_access_levels": [{"group_id": 9899829, access_level: 40}], "required_approval_count": 1}' \
+ --header "PRIVATE-TOKEN: <your_access_token>" \
+ "https://gitlab.example.com/api/v4/projects/22034114/protected_environments/production"
+```
Example response:
@@ -137,32 +218,128 @@ Example response:
"name": "production",
"deploy_access_levels": [
{
+ "id": 12,
"access_level": 40,
"access_level_description": "protected-access-group",
"user_id": null,
- "group_id": 9899826,
+ "group_id": 9899829,
+ "group_inheritance_type": 1
+ }
+ ],
+ "required_approval_count": 0
+}
+```
+
+### Example: Update a `deploy_access_level` record
+
+```shell
+curl --header 'Content-Type: application/json' --request PUT \
+ --data '{"deploy_access_levels": [{"id": 12, "group_id": 22034120}], "required_approval_count": 2}' \
+ --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/22034114/protected_environments/production"
+```
+
+```json
+{
+ "name": "production",
+ "deploy_access_levels": [
+ {
+ "id": 12,
+ "access_level": 40,
+ "access_level_description": "protected-access-group",
+ "user_id": null,
+ "group_id": 22034120,
"group_inheritance_type": 0
}
],
- "required_approval_count": 0,
- "approval_rules": [
- {
- "user_id": null,
- "group_id": 134,
- "access_level": null,
- "access_level_description": "qa-group",
- "required_approvals": 1,
- "group_inheritance_type": 0
- },
- {
- "user_id": null,
- "group_id": 135,
- "access_level": null,
- "access_level_description": "security-group",
- "required_approvals": 2,
- "group_inheritance_type": 0
- }
- ]
+ "required_approval_count": 2
+}
+```
+
+### Example: Delete a `deploy_access_level` record
+
+```shell
+curl --header 'Content-Type: application/json' --request PUT \
+ --data '{"deploy_access_levels": [{"id": 12, "_destroy": true}], "required_approval_count": 0}' \
+ --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/22034114/protected_environments/production"
+```
+
+Example response:
+
+```json
+{
+ "name": "production",
+ "deploy_access_levels": [],
+ "required_approval_count": 0
+}
+```
+
+### Example: Create an `approval_rule` record
+
+```shell
+curl --header 'Content-Type: application/json' --request PUT \
+ --data '{"approval_rules": [{"group_id": 134, "required_approvals": 1}]}' \
+ --header "PRIVATE-TOKEN: <your_access_token>" \
+ "https://gitlab.example.com/api/v4/projects/22034114/protected_environments/production"
+```
+
+Example response:
+
+```json
+{
+ "name": "production",
+ "approval_rules": [
+ {
+ "id": 38,
+ "user_id": null,
+ "group_id": 134,
+ "access_level": null,
+ "access_level_description": "qa-group",
+ "required_approvals": 1,
+ "group_inheritance_type": 0
+ }
+ ]
+}
+```
+
+### Example: Update an `approval_rule` record
+
+```shell
+curl --header 'Content-Type: application/json' --request PUT \
+ --data '{"approval_rules": [{"id": 38, "group_id": 135, "required_approvals": 2}]}' \
+ --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/22034114/protected_environments/production"
+```
+
+```json
+{
+ "name": "production",
+ "approval_rules": [
+ {
+ "id": 38,
+ "user_id": null,
+ "group_id": 135,
+ "access_level": null,
+ "access_level_description": "security-group",
+ "required_approvals": 2,
+ "group_inheritance_type": 0
+ }
+ ]
+}
+```
+
+### Example: Delete an `approval_rule` record
+
+```shell
+curl --header 'Content-Type: application/json' --request PUT \
+ --data '{"approval_rules": [{"id": 38, "_destroy": true}]}' \
+ --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/22034114/protected_environments/production"
+```
+
+Example response:
+
+```json
+{
+ "name": "production",
+ "approval_rules": []
}
```
@@ -174,11 +351,11 @@ Unprotects the given protected environment:
DELETE /projects/:id/protected_environments/:name
```
-```shell
-curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/protected_environments/staging"
-```
-
| 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. |
| `name` | string | yes | The name of the protected environment. |
+
+```shell
+curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/5/protected_environments/staging"
+```
diff --git a/doc/api/releases/index.md b/doc/api/releases/index.md
index 1332eea26c0..e286fefc462 100644
--- a/doc/api/releases/index.md
+++ b/doc/api/releases/index.md
@@ -386,7 +386,7 @@ POST /projects/:id/releases
| `assets:links:url` | string | required by: `assets:links` | The URL of the link. Link URLs must be unique within the release. |
| `assets:links:filepath` | string | no | Optional path for a [Direct Asset link](../../user/project/releases/release_fields.md#permanent-links-to-release-assets).
| `assets:links:link_type` | string | no | The type of the link: `other`, `runbook`, `image`, `package`. Defaults to `other`.
-| `released_at` | datetime | no | The date when the release is/was ready. Defaults to the current time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
+| `released_at` | datetime | no | Date and time for the release. Defaults to the current time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). Only provide this field if creating an [upcoming](../../user/project/releases/index.md#upcoming-releases) or [historical](../../user/project/releases/index.md#historical-releases) release. |
Example request:
diff --git a/doc/api/repositories.md b/doc/api/repositories.md
index bf2ead43519..7d94edc0872 100644
--- a/doc/api/repositories.md
+++ b/doc/api/repositories.md
@@ -342,7 +342,7 @@ tags using these formats:
- `vX.Y.Z`
- `X.Y.Z`
-Where `X.Y.Z` is a version that follows [semantic versioning](https://semver.org/).
+Where `X.Y.Z` is a version that follows [semantic versioning](https://semver.org/).
For example, consider a project with the following tags:
- v1.0.0-pre1
diff --git a/doc/api/resource_label_events.md b/doc/api/resource_label_events.md
index 9c05d32c992..da265972f28 100644
--- a/doc/api/resource_label_events.md
+++ b/doc/api/resource_label_events.md
@@ -1,5 +1,5 @@
---
-stage: Manage
+stage: Govern
group: Compliance
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
---
diff --git a/doc/api/resource_state_events.md b/doc/api/resource_state_events.md
index b2e886618d5..8e957df8145 100644
--- a/doc/api/resource_state_events.md
+++ b/doc/api/resource_state_events.md
@@ -8,8 +8,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/35210/) in GitLab 13.2.
-Resource state events keep track of what happens to GitLab [issues](../user/project/issues/index.md) and
-[merge requests](../user/project/merge_requests/index.md).
+Resource state events keep track of what happens to GitLab [issues](../user/project/issues/index.md)
+[merge requests](../user/project/merge_requests/index.md) and [epics starting with GitLab 15.4](../user/group/epics/index.md)
Use them to track which state was set, who did it, and when it happened.
@@ -212,3 +212,105 @@ Example response:
"state": "closed"
}
```
+
+## Epics
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97554) in GitLab 15.4.
+
+### List group epic state events
+
+Returns a list of all state events for a single epic.
+
+```plaintext
+GET /groups/:id/epics/:epic_id/resource_state_events
+```
+
+| Attribute | Type | Required | Description |
+|-------------| -------------- | -------- |--------------------------------------------------------------------------------|
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding). |
+| `epic_id` | integer | yes | The ID of an epic. |
+
+Example request:
+
+```shell
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/5/epics/11/resource_state_events"
+```
+
+Example response:
+
+```json
+[
+ {
+ "id": 142,
+ "user": {
+ "id": 1,
+ "name": "Administrator",
+ "username": "root",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "web_url": "http://gitlab.example.com/root"
+ },
+ "created_at": "2018-08-20T13:38:20.077Z",
+ "resource_type": "Epic",
+ "resource_id": 11,
+ "state": "opened"
+ },
+ {
+ "id": 143,
+ "user": {
+ "id": 1,
+ "name": "Administrator",
+ "username": "root",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "web_url": "http://gitlab.example.com/root"
+ },
+ "created_at": "2018-08-21T14:38:20.077Z",
+ "resource_type": "Epic",
+ "resource_id": 11,
+ "state": "closed"
+ }
+]
+```
+
+### Get single epic state event
+
+Returns a single state event for a specific group epic.
+
+```plaintext
+GET /groups/:id/epics/:epic_id/resource_state_events/:resource_state_event_id
+```
+
+Parameters:
+
+| Attribute | Type | Required | Description |
+|---------------------------| -------------- | -------- |-------------------------------------------------------------------------------|
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding). |
+| `epic_id` | integer | yes | The ID of an epic. |
+| `resource_state_event_id` | integer | yes | The ID of a state event. |
+
+Example request:
+
+```shell
+curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/5/epics/11/resource_state_events/143"
+```
+
+Example response:
+
+```json
+{
+ "id": 143,
+ "user": {
+ "id": 1,
+ "name": "Administrator",
+ "username": "root",
+ "state": "active",
+ "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "web_url": "http://gitlab.example.com/root"
+ },
+ "created_at": "2018-08-21T14:38:20.077Z",
+ "resource_type": "Epic",
+ "resource_id": 11,
+ "state": "closed"
+}
+```
diff --git a/doc/api/settings.md b/doc/api/settings.md
index d11269113a1..467fa6bfc37 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -89,6 +89,7 @@ Example response:
"asset_proxy_url": "https://assets.example.com",
"asset_proxy_whitelist": ["example.com", "*.example.com", "your-instance.com"],
"asset_proxy_allowlist": ["example.com", "*.example.com", "your-instance.com"],
+ "maven_package_requests_forwarding": true,
"npm_package_requests_forwarding": true,
"pypi_package_requests_forwarding": true,
"snippet_size_limit": 52428800,
@@ -201,6 +202,7 @@ Example response:
"allow_local_requests_from_hooks_and_services": true,
"allow_local_requests_from_web_hooks_and_services": true,
"allow_local_requests_from_system_hooks": false,
+ "maven_package_requests_forwarding": true,
"npm_package_requests_forwarding": true,
"pypi_package_requests_forwarding": true,
"snippet_size_limit": 52428800,
@@ -276,7 +278,7 @@ listed in the descriptions of the relevant settings.
| `deactivate_dormant_users` | boolean | no | Enable [automatic deactivation of dormant users](../user/admin_area/moderate_users.md#automatically-deactivate-dormant-users). |
| `deactivate_dormant_users_period` | integer | no | Length of time (in days) after which a user is considered dormant. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336747) in GitLab 15.3. |
| `default_artifacts_expire_in` | string | no | Set the default expiration time for each job's artifacts. |
-| `default_branch_name` | string | no | [Instance-level custom initial branch name](../user/project/repository/branches/default.md#instance-level-custom-initial-branch-name) ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/225258) in GitLab 13.2). |
+| `default_branch_name` | string | no | [Instance-level custom initial branch name](../user/project/repository/branches/default.md#instance-level-custom-initial-branch-name). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/225258) in GitLab 13.2. |
| `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`. |
@@ -286,12 +288,12 @@ listed in the descriptions of the relevant settings.
| `default_snippet_visibility` | string | no | What visibility level new snippets receive. Can take `private`, `internal` and `public` as a parameter. Default is `private`. |
| `delayed_project_deletion` **(PREMIUM SELF)** | boolean | no | Enable delayed project deletion by default in new groups. Default is `false`. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), can only be enabled when `delayed_group_deletion` is true. |
| `delayed_group_deletion` **(PREMIUM SELF)** | boolean | no | Enable delayed group deletion. Default is `true`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352959) in GitLab 15.0. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), disables and locks the group-level setting for delayed protect deletion when set to `false`. |
-| `delete_inactive_projects` | boolean | no | Enable inactive project deletion feature. Default is `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84519) in GitLab 14.10. [Became operational](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85689) in GitLab 15.0 (with feature flag `inactive_projects_deletion`, disabled by default). |
+| `delete_inactive_projects` | boolean | no | Enable inactive project deletion feature. Default is `false`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84519) in GitLab 14.10. [Became operational without feature flag](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96803) in GitLab 15.4. |
| `deletion_adjourned_period` **(PREMIUM SELF)** | integer | no | The number of days to wait before deleting a project or group that is marked for deletion. Value must be between `1` and `90`. Defaults to `7`. [From GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/issues/352960), a hook on `deletion_adjourned_period` sets the period to `1` on every update, and sets both `delayed_project_deletion` and `delayed_group_deletion` to `false` if the period is `0`. |
| `diff_max_patch_bytes` | integer | no | Maximum [diff patch size](../user/admin_area/diff_limits.md), in bytes. |
| `diff_max_files` | integer | no | Maximum [files in a diff](../user/admin_area/diff_limits.md). |
| `diff_max_lines` | integer | no | Maximum [lines in a diff](../user/admin_area/diff_limits.md). |
-| `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_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. |
| `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. |
@@ -377,7 +379,7 @@ listed in the descriptions of the relevant settings.
| `max_artifacts_size` | integer | no | Maximum artifacts size in MB. |
| `max_attachment_size` | integer | no | Limit attachment size in MB. |
| `max_export_size` | integer | no | Maximum export size in MB. 0 for unlimited. Default = 0 (unlimited). |
-| `max_import_size` | integer | no | Maximum import size in MB. 0 for unlimited. Default = 0 (unlimited) [Modified](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50MB to 0 in GitLab 13.8. |
+| `max_import_size` | integer | no | Maximum import size in MB. 0 for unlimited. Default = 0 (unlimited). [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50 MB to 0 in GitLab 13.8. |
| `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. |
@@ -385,10 +387,12 @@ listed in the descriptions of the relevant settings.
| `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. |
| `git_rate_limit_users_allowlist` **(ULTIMATE SELF)** | array of strings | no | List of usernames excluded from Git anti-abuse rate limits. Default: `[]`, Maximum: 100 usernames. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/90815) in GitLab 15.2. |
+| `auto_ban_user_on_excessive_projects_download` **(ULTIMATE SELF)** | boolean | no | When enabled, users will get automatically banned from the application when they download more than the maximum number of unique projects in the time period specified by `max_number_of_repository_downloads` and `max_number_of_repository_downloads_within_time_period` respectively. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94153) in GitLab 15.4 |
| `mirror_available` | boolean | no | Allow repository mirroring to configured by project Maintainers. If disabled, only Administrators can configure repository mirroring. |
| `mirror_capacity_threshold` **(PREMIUM)** | integer | no | Minimum capacity to be available before scheduling more mirrors preemptively. |
| `mirror_max_capacity` **(PREMIUM)** | integer | no | Maximum number of mirrors that can be synchronizing at the same time. |
| `mirror_max_delay` **(PREMIUM)** | integer | no | Maximum time (in minutes) between updates that a mirror can have when scheduled to synchronize. |
+| `maven_package_requests_forwarding` **(PREMIUM)** | boolean | no | Use repo.maven.apache.org as a default remote repository when the package is not found in the GitLab Package Registry for Maven. |
| `npm_package_requests_forwarding` **(PREMIUM)** | boolean | no | Use npmjs.org as a default remote repository when the package is not found in the GitLab Package Registry for npm. |
| `pypi_package_requests_forwarding` **(PREMIUM)** | boolean | no | Use pypi.org as a default remote repository when the package is not found in the GitLab Package Registry for PyPI. |
| `outbound_local_requests_whitelist` | array of strings | no | Define a list of trusted domains or IP addresses to which local requests are allowed when local requests for hooks and services are disabled.
diff --git a/doc/api/status_checks.md b/doc/api/status_checks.md
index 92e003bf80d..1cedd2d3730 100644
--- a/doc/api/status_checks.md
+++ b/doc/api/status_checks.md
@@ -1,5 +1,5 @@
---
-stage: Manage
+stage: Govern
group: Compliance
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"
type: reference, api
diff --git a/doc/api/system_hooks.md b/doc/api/system_hooks.md
index a883c3c1613..14339460270 100644
--- a/doc/api/system_hooks.md
+++ b/doc/api/system_hooks.md
@@ -10,7 +10,7 @@ All methods require administrator authorization.
You can configure the URL endpoint of the system hooks from the GitLab user interface:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. Select **System Hooks** (`/admin/hooks`).
Read more about [system hooks](../administration/system_hooks.md).
diff --git a/doc/api/tags.md b/doc/api/tags.md
index 903cb361587..45c621f534e 100644
--- a/doc/api/tags.md
+++ b/doc/api/tags.md
@@ -8,6 +8,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## List project repository tags
+> `version` value for the `order_by` attribute [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95150) in GitLab 15.4.
+
Get a list of repository tags from a project, sorted by update date and time in descending order. This endpoint can be accessed without authentication if the
repository is publicly accessible.
@@ -19,9 +21,9 @@ Parameters:
| 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 tags ordered by `name` or `updated` fields. Default is `updated` |
-| `sort` | string | no | Return tags sorted in `asc` or `desc` order. Default is `desc` |
+| `id` | integer or 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 tags ordered by `name`, `updated`, or `version`. Default is `updated`. |
+| `sort` | string | no | Return tags sorted in `asc` or `desc` order. Default is `desc`. |
| `search` | string | no | Return list of tags matching the search criteria. You can use `^term` and `term$` to find tags that begin and end with `term` respectively. No other regular expressions are supported. |
```json
@@ -115,7 +117,7 @@ Parameters:
| 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 |
+| `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 |
| `ref` | string | yes | Create tag using commit SHA, another tag name, or branch name |
| `message` | string | no | Creates annotated tag |
@@ -172,5 +174,5 @@ Parameters:
| 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 |
+| `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 |
diff --git a/doc/api/topics.md b/doc/api/topics.md
index ee88a43ff1c..38d99244bb6 100644
--- a/doc/api/topics.md
+++ b/doc/api/topics.md
@@ -217,7 +217,7 @@ curl --request PUT \
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80725) in GitLab 14.9.
-You must be an administrator to delete a project.
+You must be an administrator to delete a project topic.
When you delete a project topic, you also delete the topic assignment for projects.
```plaintext
@@ -237,3 +237,43 @@ curl --request DELETE \
--header "PRIVATE-TOKEN: <your_access_token>" \
"https://gitlab.example.com/api/v4/topics/1"
```
+
+## Merge topics
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/95501) in GitLab 15.4.
+
+You must be an administrator to merge a source topic into a target topic.
+When you merge topics, you delete the source topic and move all assigned projects to the target topic.
+
+```plaintext
+POST /topics/merge
+```
+
+Supported attributes:
+
+| Attribute | Type | Required | Description |
+| ----------------- | ------- | ---------------------- | -------------------------- |
+| `source_topic_id` | integer | **{check-circle}** Yes | ID of source project topic |
+| `target_topic_id` | integer | **{check-circle}** Yes | ID of target project topic |
+
+Example request:
+
+```shell
+curl --request POST \
+ --data "source_topic_id=2&target_topic_id=1" \
+ --header "PRIVATE-TOKEN: <your_access_token>" \
+ "https://gitlab.example.com/api/v4/topics/merge"
+```
+
+Example response:
+
+```json
+{
+ "id": 1,
+ "name": "topic1",
+ "title": "Topic 1",
+ "description": null,
+ "total_projects_count": 0,
+ "avatar_url": null
+}
+```
diff --git a/doc/api/version.md b/doc/api/version.md
index 7d072e23410..efdf2b8b626 100644
--- a/doc/api/version.md
+++ b/doc/api/version.md
@@ -1,6 +1,6 @@
---
-stage: Ecosystem
-group: Integrations
+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/engineering/ux/technical-writing/#assignments
---
diff --git a/doc/api/vulnerabilities.md b/doc/api/vulnerabilities.md
index 66d0579bacb..c90dc226661 100644
--- a/doc/api/vulnerabilities.md
+++ b/doc/api/vulnerabilities.md
@@ -1,5 +1,5 @@
---
-stage: Secure
+stage: Govern
group: Threat Insights
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
---
diff --git a/doc/api/vulnerability_exports.md b/doc/api/vulnerability_exports.md
index 6f6a661dbd5..59943ede3e6 100644
--- a/doc/api/vulnerability_exports.md
+++ b/doc/api/vulnerability_exports.md
@@ -1,5 +1,5 @@
---
-stage: Secure
+stage: Govern
group: Threat Insights
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
---
diff --git a/doc/api/vulnerability_findings.md b/doc/api/vulnerability_findings.md
index 46479009d7d..fcfef848f14 100644
--- a/doc/api/vulnerability_findings.md
+++ b/doc/api/vulnerability_findings.md
@@ -1,5 +1,5 @@
---
-stage: Secure
+stage: Govern
group: Threat Insights
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
---
diff --git a/doc/architecture/blueprints/_template.md b/doc/architecture/blueprints/_template.md
new file mode 100644
index 00000000000..e99ce61970a
--- /dev/null
+++ b/doc/architecture/blueprints/_template.md
@@ -0,0 +1,142 @@
+---
+status: proposed
+creation-date: yyyy-mm-dd
+authors: [ "@username" ]
+coach: "@username"
+owning-section: "~section::<section>"
+participating-sections: []
+approvers: [ "@product-manager", "@engineering-manager" ]
+---
+
+<!--
+**Note:** Please remove comment blocks for sections you've filled in.
+When your blueprint is complete, 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
+/ evolve as you move forward with the proposal. You are not constrained by the
+content in this template. If you have a good idea about what should be in your
+blueprint, you can ignore the template, but if you don't know yet what should
+be in it, this template might be handy.
+
+- **Fill out this file as best you can.** At minimum, you should fill in the
+ "Summary", and "Motivation" sections. These can be brief and may be a copy
+ of issue or epic descriptions if the initiative is already on Product's
+ roadmap.
+- **Create a MR for this blueprint.** Assign it to an Architecture Evolution
+ Coach (i.e. a Principal+ engineer).
+- **Merge early and iterate.** Avoid getting hung up on specific details and
+ instead aim to get the goals of the blueprint clarified and merged quickly.
+ The best way to do this is to just start with the high-level sections and fill
+ out details incrementally in subsequent MRs.
+
+Just because a blueprint is merged does not mean it is complete or approved.
+Any blueprint is a working document and subject to change at any time.
+
+When editing blueprints, aim for tightly-scoped, single-topic MRs to keep
+discussions focused. If you disagree with what is already in a document, open a
+new MR with suggested changes.
+
+If there are new details that belong in the blueprint, edit the blueprint. Once
+a feature has become "implemented", major changes should get new blueprints.
+
+The canonical place for the latest set of instructions (and the likely source
+of this file) is [here](/doc/architecture/blueprints/_template.md).
+-->
+
+# {+ Title of Blueprint +}
+
+<!--
+This is the title of your blueprint. Keep it short, simple, and descriptive. A
+good title can help communicate what the blueprint is and should be considered
+as part of any review.
+-->
+
+[[_TOC_]]
+
+## Summary
+
+<!--
+This section is very important, because very often it is the only section that
+will be read by team members. We sometimes call it an "Executive summary",
+because executives usually don't have time to read entire document like this.
+Focus on writing this section in a way that anyone can understand what is says,
+the audience here is everyone: executives, product managers, engineers, wider
+community members.
+
+A good summary is probably at least a paragraph in length.
+-->
+
+## Motivation
+
+<!--
+This section is for explicitly listing the motivation, goals and non-goals of
+this blueprint. Describe why the change is important, all the opportunities,
+and the benefits to users.
+
+The motivation section can optionally provide links to issues that demonstrate
+interest in a blueprint within the wider GitLab community. Links to
+documentation for competing products and services is also encouraged in cases
+where they demonstrate clear gaps in the functionality GitLab provides.
+
+For concrete proposals we recommend laying out goals and non-goals explicitly,
+but this section may be framed in terms of problem statements, challenges, or
+opportunities. The latter may be a more suitable framework in cases where the
+problem is not well-defined or design details not yet established.
+-->
+
+### Goals
+
+<!--
+List the specific goals / opportunities of the blueprint.
+
+- What is it trying to achieve?
+- How will we know that this has succeeded?
+- What are other less tangible opportunities here?
+-->
+
+### Non-Goals
+
+<!--
+Listing non-goals helps to focus discussion and make progress. This section is
+optional.
+
+- What is out of scope for this blueprint?
+-->
+
+## Proposal
+
+<!--
+This is where we get down to the specifics of what the proposal actually is,
+but keep it simple! This should have enough detail that reviewers can
+understand exactly what you're proposing, but should not include things like
+API designs or implementation. The "Design Details" section below is for the
+real nitty-gritty.
+-->
+
+## Design and implementation details
+
+<!--
+This section should contain enough information that the specifics of your
+change are understandable. This may include API specs (though not always
+required) or even code snippets. If there's any ambiguity about HOW your
+proposal will be implemented, this is the place to discuss them.
+
+If you are not sure how many implementation details you should include in the
+blueprint, the rule of thumb here is to provide enough context for people to
+understand the proposal. As you move forward with the implementation, you may
+need to add more implementation details to the blueprint, as those may become
+an important context for important technical decisions made along the way. A
+blueprint is also a register of such technical decisions. If a technical
+decision requires additional context before it can be made, you probably should
+document this context in a blueprint. If it is a small technical decision that
+can be made in a merge request by an author and a maintainer, you probably do
+not need to document it here. The impact a technical decision will have is
+another helpful information - if a technical decision is very impactful,
+documenting it, along with associated implementation details, is advisable.
+
+If it's helpful to include workflow diagrams or any other related images.
+Diagrams authored in GitLab flavored markdown are preferred. In cases where
+that is not feasible, images should be placed under `images/` in the same
+directory as the `index.md` for the proposal.
+-->
diff --git a/doc/architecture/blueprints/ci_data_decay/index.md b/doc/architecture/blueprints/ci_data_decay/index.md
index 7c0bdf299db..23c8e9df1bb 100644
--- a/doc/architecture/blueprints/ci_data_decay/index.md
+++ b/doc/architecture/blueprints/ci_data_decay/index.md
@@ -48,7 +48,7 @@ PostgreSQL database running on GitLab.com.
This volume contributes to significant performance problems, development
challenges and is often related to production incidents.
-We also expect a [significant growth in the number of builds executed on GitLab.com](../ci_scale/index.md)
+We also expect a [significant growth in the number of builds executed on GitLab.com](../ci_scale/index.md)
in the upcoming years.
## Opportunity
@@ -61,7 +61,7 @@ pipelines that are older than a few months might help us to move this data out
of the primary database, to a different storage, that is more performant and
cost effective.
-It is already possible to prevent processing builds
+It is already possible to prevent processing builds
[that have been archived](../../../user/admin_area/settings/continuous_integration.md#archive-jobs).
When a build gets archived it will not be possible to retry it, but we still do
keep all the processing metadata in the database, and it consumes resources
@@ -232,7 +232,7 @@ In progress.
## Timeline
-- 2021-01-21: Parent [CI Scaling](../ci_scale/) blueprint [merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52203) created.
+- 2021-01-21: Parent [CI Scaling](../ci_scale/index.md) blueprint [merge request](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52203) created.
- 2021-04-26: CI Scaling blueprint approved and merged.
- 2021-09-10: CI/CD data time decay blueprint discussions started.
- 2022-01-07: CI/CD data time decay blueprint [merged](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70052).
diff --git a/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md b/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md
index 868dae4fc6c..baec14e3f0f 100644
--- a/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md
+++ b/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md
@@ -74,7 +74,12 @@ violates our [principle of 100 GB max size](../database_scaling/size-limits.md).
We also want to [build alerting](https://gitlab.com/gitlab-com/gl-infra/tamland/-/issues/5)
to notify us when this number is exceeded.
-We’ve seen numerous S1 and S2 database-related production environment
+Large SQL tables increase index maintenance time, during which freshly deleted tuples
+cannot be cleaned by `autovacuum`. This highlight the need for small tables.
+We will measure how much bloat we accumulate when (re)indexing huge tables. Base on this analysis,
+we will be able to set up SLO (dead tuples / bloat), associated with (re)indexing.
+
+We've seen numerous S1 and S2 database-related production environment
incidents, over the last couple of months, for example:
- S1: 2022-03-17 [Increase in writes in `ci_builds` table](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/6625)
@@ -130,7 +135,7 @@ remaining database tables when it becomes necessary.
It is also important to avoid large data migrations. We store almost 6
terabytes of data in the biggest CI/CD tables, in many different columns and
indexes. Migrating this amount of data might be challenging and could cause
-instability in the production environment. Due to this concern, we’ve developed
+instability in the production environment. Due to this concern, we've developed
a way to attach an existing database table as a partition zero without downtime
and excessive database locking, what has been demonstrated in one of the
[first proofs of concept](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/80186).
@@ -145,7 +150,7 @@ Our plan is to use logical partition IDs. We want to start with the
`ci_pipelines` table and create a `partition_id` column with a `DEFAULT` value
of `100` or `1000`. Using a `DEFAULT` value avoids the challenge of backfilling
this value for every row. Adding a `CHECK` constraint prior to attaching the
-first partition tells PostgreSQL that we’ve already ensured consistency and
+first partition tells PostgreSQL that we've already ensured consistency and
there is no need to check it while holding an exclusive table lock when
attaching this table as a partition to the routing table (partitioned schema
definition). We will increment this value every time we create a new partition
@@ -159,21 +164,32 @@ and artifacts, will share the same value. We want to add the `partition_id`
column into all 6 problematic tables because we can avoid backfilling this data
when we decide it is time to start partitioning them.
-We want to partition CI/CD data iteratively, so we will start with the
-pipelines table, and create at least one, but likely two, partitions. The
-pipelines table will be partitioned using the `LIST` partitioning strategy. It
-is possible that, after some time, `p_ci_pipelines` will store data in two
-partitions with IDs of `100` and `101`. Then we will try partitioning
-`ci_builds`. Therefore we might want to use `RANGE` partitioning in
-`p_ci_builds` with IDs `100` and `101`, because builds for the two logical
-partitions used will still be stored in a single table.
+We want to partition CI/CD data iteratively. We plan to start with the
+`ci_builds_metadata` table, because this is the fastest growing table in the CI
+database and want to contain this rapid growth. This table has also the most
+simple access patterns - a row from it is being read when a build is exposed to
+a runner, and other access patterns are relatively simple too. Starting with
+`p_ci_builds_metadata` will allow us to achieve tangible and quantifiable
+results earlier, and will become a new pattern that makes partitioning the
+largest table possible. We will partition builds metadata using the `LIST`
+partitioning strategy.
+
+Once we have many partitions attached to `p_ci_builds_metadata`, with many
+`partition_ids` we will choose another CI table to partition next. In that case
+we might want to use `RANGE` partitioning in for that next table because
+`p_ci_builds_metadata` will already have many physical partitions, and
+therefore many logical `partition_ids` will be used at that time. For example,
+if we choose `ci_builds` as the next partitioning candidate, after having
+partitioned `p_ci_builds_metadata`, it will have many different values stored
+in `ci_builds.partition_id`. Using `RANGE` partitioning in that case might be
+easier.
Physical partitioning and logical partitioning will be separated, and a
-strategy will be determined when we implement partitioning for the respective
-database tables. Using `RANGE` partitioning works similarly to using `LIST`
-partitioning in database tables other than `ci_pipelines`, but because we can
-guarantee continuity of `partition_id` values, using `RANGE` partitioning might
-be a better strategy.
+strategy will be determined when we implement physical partitioning for the
+respective database tables. Using `RANGE` partitioning works similarly to using
+`LIST` partitioning in database tables, but because we can guarantee continuity
+of `partition_id` values, using `RANGE` partitioning might be a better
+strategy.
## Why do we want to use explicit logical partition ids?
@@ -201,9 +217,30 @@ find this number, though we might not need to do this.
The single and uniform `partition_id` value for pipeline data gives us more
choices later on than primary-keys-based partitioning.
+## Altering partitioned tables
+
+It will still be possible to run `ALTER TABLE` statements against partitioned tables,
+similarly to how the tables behaved before partitioning. When PostgreSQL runs
+an `ALTER TABLE` statement against a parent partitioned table, it acquires the same
+lock on all child partitions and updates each to keep them in sync. This differs from
+running `ALTER TABLE` on a non-partitioned table in a few key ways:
+
+- PostgreSQL acquires `ACCESS EXCLUSIVE` locks against a larger number of tables, but
+ not a larger amount of data, than it would were the table not partitioned.
+ Each partition will be locked similarly to the parent table, and all will be updated
+ in a single transaction.
+- Lock duration will be increased based on the number of partitions involved.
+ All `ALTER TABLE` statements executed on the GitLab database (other than `VALIDATE CONSTRAINT`)
+ take small constant amounts of time per table modified. PostgreSQL will need
+ to modify each partition in sequence, increasing the runtime of the lock. This
+ time will still remain very small until there are many partitions involved.
+- If thousands of partitions are involved in an `ALTER TABLE`, we will need to verify that
+ the value of `max_locks_per_transaction` is high enough to support all of the locks that
+ need to be taken during the operation.
+
## Splitting large partitions into smaller ones
-We want to start with the initial `pipeline_id` number `100` (or higher, like
+We want to start with the initial `partition_id` number `100` (or higher, like
`1000`, depending on our calculations and estimations). We do not want to start
from 1, because existing tables are also large already, and we might want to
split them into smaller partitions. If we start with `100`, we will be able to
@@ -217,6 +254,18 @@ smaller ones (it's not yet clear if we will need to do this), we might be able
to just use background migrations to update partition IDs, and PostgreSQL is
smart enough to move rows between partitions on its own.
+### Naming conventions
+
+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 simply 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
+[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.
+
## Storing partitions metadata in the database
In order to build an efficient mechanism that will be responsible for creating
@@ -225,8 +274,8 @@ metadata table, called `ci_partitions`. In that table we would store metadata
about all the logical partitions, with many pipelines per partition. We may
need to store a range of pipeline ids per logical partition. Using it we will
be able to find the `partition_id` number for a given pipeline ID and we will
-also find information about which logical partitions are “active†or
-“archivedâ€, which will help us to implement a time-decay pattern using database
+also find information about which logical partitions are "active" or
+"archived", which will help us to implement a time-decay pattern using database
declarative partitioning.
`ci_partitions` table will store information about a partition identifier,
@@ -302,9 +351,116 @@ scope block takes an argument). Preloading instance dependent scopes is not
supported.
```
-We also need to build a proof of concept for removing data on the PostgreSQL
-side (using foreign keys with `ON DELETE CASCADE`) and removing data through
-Rails associations, as this might be an important area of uncertainty.
+### Foreign keys
+
+Foreign keys must reference columns that either are a primary key or form a
+unique constraint. We can define them using these strategies:
+
+#### Between routing tables sharing partition ID
+
+For relations that are part of the same pipeline hierarchy it is possible to
+share the `partition_id` column to define the foreign key constraint:
+
+```plaintext
+p_ci_pipelines:
+ - id
+ - partition_id
+
+p_ci_builds:
+ - id
+ - partition_id
+ - pipeline_id
+```
+
+In this case, `p_ci_builds.partition_id` indicates the partition for the build
+and also for the pipeline. We can add a FK on the routing table using:
+
+```sql
+ALTER TABLE ONLY p_ci_builds
+ ADD CONSTRAINT fk_on_pipeline_and_partition
+ FOREIGN KEY (pipeline_id, partition_id)
+ REFERENCES p_ci_pipelines(id, partition_id) ON DELETE CASCADE;
+```
+
+#### Between routing tables with different partition IDs
+
+It's not possible to reuse the `partition_id` for all relations in the CI domain,
+so in this case we'll need to store the value as a different attribute. For
+example, when canceling redundant pipelines we store on the old pipeline row
+the ID of the new pipeline that cancelled it as `auto_canceled_by_id`:
+
+```plaintext
+p_ci_pipelines:
+ - id
+ - partition_id
+ - auto_canceled_by_id
+ - auto_canceled_by_partition_id
+```
+
+In this case we can't ensure that the canceling pipeline is part of the same
+hierarchy as the canceled pipelines, so we need an extra attribute to store its
+partition, `auto_canceled_by_partition_id`, and the FK becomes:
+
+```sql
+ALTER TABLE ONLY p_ci_pipelines
+ ADD CONSTRAINT fk_cancel_redundant_pieplines
+ FOREIGN KEY (auto_canceled_by_id, auto_canceled_by_partition_id)
+ REFERENCES p_ci_pipelines(id, partition_id) ON DELETE SET NULL;
+```
+
+#### Between routing tables and regular tables
+
+Not all of the tables in the CI domain will be partitioned, so we'll have routing
+tables that will reference non-partitioned tables, for example we reference
+`external_pull_requests` from `ci_pipelines`:
+
+```sql
+FOREIGN KEY (external_pull_request_id)
+REFERENCES external_pull_requests(id)
+ON DELETE SET NULL
+```
+
+In this case we only need to move the FK definition from the partition level
+to the routing table so that new pipeline partitions may use it:
+
+```sql
+ALTER TABLE p_ci_pipelines
+ ADD CONSTRAINT fk_external_request
+ FOREIGN KEY (external_pull_request_id)
+ REFERENCES external_pull_requests(id) ON DELETE SET NULL;
+```
+
+#### Between regular tables and routing tables
+
+Most of the tables from the CI domain reference at least one table that will be
+turned into a routing tables, for example `ci_pipeline_messages` references
+`ci_pipelines`. These definitions will need to be updated to use the routing
+tables and for this they will need a `partition_id` column:
+
+```plaintext
+p_ci_pipelines:
+ - id
+ - partition_id
+
+ci_pipeline_messages:
+ - id
+ - pipeline_id
+ - pipeline_partition_id
+```
+
+The foreign key can be defined by using:
+
+```sql
+ALTER TABLE ci_pipeline_messages ADD CONSTRAINT fk_pipeline_partitioned
+ FOREIGN KEY (pipeline_id, pipeline_partition_id)
+ REFERENCES p_ci_pipelines(id, partition_id) ON DELETE CASCADE;
+```
+
+The old FK definition will need to be removed, otherwise new inserts in the
+`ci_pipeline_messages` with pipeline IDs from non-zero partition will fail with
+reference errors.
+
+### Indexes
We [learned](https://gitlab.com/gitlab-org/gitlab/-/issues/360148) that `PostgreSQL`
does not allow to create a single index (unique or otherwise) across all partitions of a table.
@@ -465,7 +621,7 @@ strategy. The strategy, described in this document, is subject to iteration as
well. Whenever we find a better way to reduce the risk and improve our plan, we
should update this document as well.
-We’ve managed to find a way to avoid large-scale data migrations, and we are
+We've managed to find a way to avoid large-scale data migrations, and we are
building an iterative strategy for partitioning CI/CD data. We documented our
strategy here to share knowledge and solicit feedback from other team members.
diff --git a/doc/architecture/blueprints/ci_pipeline_components/index.md b/doc/architecture/blueprints/ci_pipeline_components/index.md
new file mode 100644
index 00000000000..94ec3e2f894
--- /dev/null
+++ b/doc/architecture/blueprints/ci_pipeline_components/index.md
@@ -0,0 +1,209 @@
+---
+stage: Stage
+group: Pipeline Authoring
+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
+comments: false
+description: 'Create a catalog of shareable pipeline constructs'
+---
+
+
+# CI/CD pipeline components catalog
+
+## Summary
+
+## Goals
+
+The goal of the CI/CD pipeline components catalog is to make the reusing pipeline configurations
+easier and more efficient.
+Providing a way to discover, understand and learn how to reuse pipeline constructs allows for a more streamlined experience.
+Having a CI/CD pipeline components catalog also sets a framework for users to collaborate on pipeline constructs so that they can be evolved
+and improved over time.
+
+This blueprint defines the architectural guidelines on how to build a CI/CD catalog of pipeline components.
+This blueprint also defines the long-term direction for iterations and improvements to the solution.
+
+## Challenges
+
+- GitLab CI/CD can have a steep learning curve for new users. Users must read the documentation and
+ [YAML reference](../../../ci/yaml/index.md) to understand how to configure their pipelines.
+- Developers are struggling to reuse existing CI/CD templates with the result of having to reinvent the wheel and write
+ YAML configurations repeatedly.
+- GitLab [CI templates](../../../development/cicd/templates.md#template-directories) provide users with
+ scaffolding pipeline or jobs for specific purposes.
+ However versioning them is challenging today due to being shipped with the GitLab instance.
+ See [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/17716) for more information.
+- Users of GitLab CI/CD (pipeline authors) today have their own ad-hoc way to organize shared pipeline
+ configurations inside their organization. Those configurations tend to be mostly undocumented.
+- The only discoverable configurations are GitLab CI templates. However they don't have any inline documentation
+ so it becomes harder to know what they do and how to use them without copy-pasting the content in the
+ editor and read the actual YAML.
+- It's harder to adopt additional GitLab features (CD, security, test, etc.).
+- There is no framework for testing reusable CI configurations.
+ Many configurations are not unit tested against single changes.
+- Communities, partners, 3rd parties, individual contributors, must go through the
+ [GitLab Contribution process](https://about.gitlab.com/community/contribute/) to contribute to GitLab managed
+ templates. See [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/323727) for more information.
+- GitLab has more than 100 of templates with some of them barely maintained after their addition.
+
+### Problems with GitLab CI templates
+
+- GitLab CI Templates have not been designed with deterministic behavior in mind.
+- GitLab CI Templates have not been design with reusability in mind.
+- `Jobs/` templates hard-code the `stage:` attribute but the user of the template must somehow override
+ or know in advance what stage is needed.
+ - The user should be able to import the job inside a given stage or pass the stage names as input parameter
+ when using the component.
+ - Failures in mapping the correct stage can result in confusing errors.
+- Some templates are designed to work with AutoDevops but are not generic enough
+ ([example](https://gitlab.com/gitlab-org/gitlab/-/blob/2c0e8e4470001442e999391df81e19732b3439e6/lib/gitlab/ci/templates/AWS/Deploy-ECS.gitlab-ci.yml)).
+- Many CI templates, especially those [language specific](https://gitlab.com/gitlab-org/gitlab/-/tree/2c0e8e4470001442e999391df81e19732b3439e6/lib/gitlab/ci/templates)
+ are tutorial/scaffolding-style templates.
+ - They are meant to show the user how a typical pipeline would look like but it requires high customization from the user perspective.
+ - They require a different UX: copy-paste in the position of the Pipeline Editor cursor.
+- Some templates like `SAST.latest.gitlab-ci.yml` add multiple jobs conditionally to the same pipeline.
+ - Ideally these jobs could run as a child pipeline and make the reports available to the parent pipeline.
+ - [This epic](https://gitlab.com/groups/gitlab-org/-/epics/8205) is necessary for Parent-child pipelines to be used.
+- Some templates incorrectly use `variables`, `image` and other top-level keywords but that defines them in all pipeline jobs,
+ not just those defined in the template.
+ - This technique introduces inheritance issues when a template modifies jobs unnecessarily.
+
+## Opportunities
+
+- Having a catalog of pipeline constructs where users can search and find what they need can greatly lower
+ the bar for new users.
+- Customers are already trying to rollout their ad-hoc catalog of shared configurations. We could provide a
+ standardized way to write, package and share pipeline constructs directly in the product.
+- As we implement new pipeline constructs (for example, reusable job steps) they could be items of the
+ catalog. The catalog can boost the adoption of new constructs.
+- The catalog can be a place where we strengthen our relationship with partners, having components offered
+ and maintained by our partners.
+- With discoverability and better versioning mechanism we can have more improvements and better collaboration.
+- Competitive landscape is showing the need for such feature
+ - [R2DevOps](https://r2devops.io) implements a catalog of CI templates for GitLab pipelines.
+ - [GitHub Actions](https://github.com/features/actions) provides an extensive catalog of reusable job steps.
+
+## Implementation guidelines
+
+- Start with the smallest user base. Dogfood the feature for `gitlab-org` and `gitlab-com` groups.
+ Involve the Engineering Productivity and other groups authoring pipeline configurations to test
+ and validate our solutions.
+- Ensure we can integrate all the feedback gathered, even if that means changing the technical design or
+ UX. Until we make the feature GA we should have clear expectations with early adopters.
+- Reuse existing functionality as much as possible. Don't reinvent the wheel on the initial iterations.
+ For example: reuse project features like title, description, avatar to build a catalog.
+- Leverage GitLab features for the development lifecycle of the components (testing via `.gitlab-ci.yml`,
+ release management, Pipeline Editor, etc.).
+- Design the catalog with self-managed support in mind.
+- Allow the catalog an the workflow to support future types of pipeline constructs and new ways of using them.
+- Design components and catalog following industry best practice related to building deterministic package managers.
+
+## Glossary
+
+This section defines some terms that are used throughout this document. With these terms we are only
+identifying abstract concepts and are subject to changes as we refine the design by discovering new insights.
+
+- **Component** Is the reusable unit of pipeline configuration.
+- **Project** Is the GitLab project attached to a repository. A project can contain multiple components.
+- **Catalog** is the collection of projects that are set to contain components.
+- **Version** is the release name of a tag in the project, which allows components to be pinned to a specific revision.
+
+## Characteristics of a component
+
+For best experience with any systems made of components it's fundamental that components are single purpose,
+isolated, reusable and resolvable.
+
+- **Single purpose**: a component must focus on a single goal and the scope be as small as possible.
+- **Isolation**: when a component is used in a pipeline, its implementation details should not leak outside the
+ component itself and into the main pipeline.
+- **Reusability:** a component is designed to be used in different pipelines.
+ Depending on the assumptions it's built on a component can be more or less generic.
+ Generic components are more reusable but may require more customization.
+- **Resolvable:** When a component depends on another component, this dependency needs to be explicit and trackable. Hidden dependencies can lead to myriads of problems.
+
+## Proposal
+
+Prerequisites to create a component:
+
+- 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.
+
+Characteristics of a component:
+
+- 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.
+
+## Limits
+
+Any MVC that exposes a feature should be added with limitations from the beginning.
+It's safer to add new features with restrictions than trying to limit a feature after it's being used.
+We can always soften the restrictions later depending on user demand.
+
+Some limits we could consider adding:
+
+- number of components that a single project can contain/export
+- number of imports that a `.gitlab-ci.yml` file can use
+- number of imports that a component can declare/use
+- max level of nested imports
+- max length of the exported component name
+
+## Iterations
+
+1. Experimentation phase
+ - Build an MVC behind a feature flag with `namespace` actor.
+ - Enable the feature flag only for `gitlab-com` and `gitlab-org` namespaces to initiate the dogfooding.
+ - Refine the solution and UX based on feedback.
+ - Find customers to be early adopters of this feature and iterate on their feedback.
+1. Design new pipeline constructs (in parallel with other phases)
+ - Start the technical and design process to work on proposals for new pipeline constructs (steps, workflows, templates).
+ - Implement new constructs. The catalog must be compatible with them.
+ - Dogfood new constructs and iterate on feedback.
+ - Release new constructs on private catalogs.
+1. Release the private catalog for groups on Ultimate plan.
+ - Iterate on feedback.
+1. Release the public catalog for all GitLab users (prospect feature)
+ - Publish new versions of GitLab CI templates as components using the new constructs whenever possible.
+ - 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 Leader | ? |
+| Product Manager | Dov Hershkovitch |
+| Architecture Evolution Coach | Kamil Trzciński |
+
+DRIs:
+
+| Role | Who
+|------------------------------|------------------------|
+| Leadership | ? |
+| Product | Dov Hershkovitch |
+| Engineering | ? |
+| UX | Nadia Sotnikova |
+
+Domain experts:
+
+| Area | Who
+|------------------------------|------------------------|
+| Verify / Pipeline authoring | Avielle Wolfe |
+| Verify / Pipeline authoring | Furkan Ayhan |
+| Verify / Pipeline execution | Fabio Pitino |
+
+<!-- vale gitlab.Spelling = YES -->
diff --git a/doc/architecture/blueprints/ci_scale/index.md b/doc/architecture/blueprints/ci_scale/index.md
index 5822ae2b5ed..75c4d05c334 100644
--- a/doc/architecture/blueprints/ci_scale/index.md
+++ b/doc/architecture/blueprints/ci_scale/index.md
@@ -115,13 +115,13 @@ of the CI/CD Apdex score, and sometimes even causes a significant performance
degradation in the production environment.
There are multiple other strategies that can improve performance and
-reliability. We can use [Redis queuing](https://gitlab.com/gitlab-org/gitlab/-/issues/322972), or
+reliability. We can use [Redis queuing](https://gitlab.com/gitlab-org/gitlab/-/issues/322972), or
[a separate table that will accelerate SQL queries used to build queues](https://gitlab.com/gitlab-org/gitlab/-/issues/322766)
and we want to explore them.
-**Status**: As of October 2021 the new architecture
+**Status**: As of October 2021 the new architecture
[has been implemented on GitLab.com](https://gitlab.com/groups/gitlab-org/-/epics/5909#note_680407908).
-The following epic tracks making it generally available:
+The following epic tracks making it generally available:
[Make the new pending builds architecture generally available](https://gitlab.com/groups/gitlab-org/-/epics/6954).
### Moving big amounts of data is challenging
@@ -171,7 +171,7 @@ Work required to achieve our next CI/CD scaling target is tracked in the
1. ✓ Migrate primary keys to big integers on GitLab.com.
1. ✓ Implement the new architecture of builds queuing on GitLab.com.
1. [Make the new builds queuing architecture generally available](https://gitlab.com/groups/gitlab-org/-/epics/6954).
-1. [Partition CI/CD data using time-decay pattern](../ci_data_decay/).
+1. [Partition CI/CD data using time-decay pattern](../ci_data_decay/index.md).
## Status
diff --git a/doc/architecture/blueprints/cloud_native_build_logs/index.md b/doc/architecture/blueprints/cloud_native_build_logs/index.md
index 0c941e332cb..3a06d73141b 100644
--- a/doc/architecture/blueprints/cloud_native_build_logs/index.md
+++ b/doc/architecture/blueprints/cloud_native_build_logs/index.md
@@ -12,7 +12,7 @@ Cloud native and the adoption of Kubernetes has been recognised by GitLab to be
one of the top two biggest tailwinds that are helping us grow faster as a
company behind the project.
-This effort is described in a more details
+This effort is described in a more details
[in the infrastructure team handbook](https://about.gitlab.com/handbook/engineering/infrastructure/production/kubernetes/gitlab-com/).
## Traditional build logs
@@ -88,7 +88,7 @@ even tried to replace NFS with
Since that time it has become apparent that the cost of operations and
maintenance of a NFS cluster is significant and that if we ever decide to
-migrate to Kubernetes
+migrate to Kubernetes
[we need to decouple GitLab from a shared local storage and NFS](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/426#note_375646396).
1. NFS might be a single point of failure
@@ -113,7 +113,7 @@ of complexity, maintenance cost and enormous, negative impact on availability.
The work needed to make the new architecture production ready and enabled on
GitLab.com had been tracked in [Cloud Native Build Logs on GitLab.com](https://gitlab.com/groups/gitlab-org/-/epics/4275) epic.
-Enabling this feature on GitLab.com is a subtask of
+Enabling this feature on GitLab.com is a subtask of
[making the new architecture generally available](https://gitlab.com/groups/gitlab-org/-/epics/3791) for everyone.
## Status
diff --git a/doc/architecture/blueprints/cloud_native_gitlab_pages/index.md b/doc/architecture/blueprints/cloud_native_gitlab_pages/index.md
index 89c3a4cd6b4..431bc19ad84 100644
--- a/doc/architecture/blueprints/cloud_native_gitlab_pages/index.md
+++ b/doc/architecture/blueprints/cloud_native_gitlab_pages/index.md
@@ -17,7 +17,7 @@ Cloud Native and the adoption of Kubernetes has been recognised by GitLab to be
one of the top two biggest tailwinds that are helping us grow faster as a
company behind the project.
-This effort is described in more detail
+This effort is described in more detail
[in the infrastructure team handbook page](https://about.gitlab.com/handbook/engineering/infrastructure/production/kubernetes/gitlab-com/).
GitLab Pages is tightly coupled with NFS and in order to unblock Kubernetes
@@ -55,7 +55,7 @@ even tried to replace NFS with
Since that time it has become apparent that the cost of operations and
maintenance of a NFS cluster is significant and that if we ever decide to
-migrate to Kubernetes
+migrate to Kubernetes
[we need to decouple GitLab from a shared local storage and NFS](https://gitlab.com/gitlab-org/gitlab-pages/-/issues/426#note_375646396).
1. NFS might be a single point of failure
@@ -83,7 +83,7 @@ graph TD
C -- Serves static content --> E(Visitors)
```
-This new architecture has been briefly described in
+This new architecture has been briefly described in
[the blog post](https://about.gitlab.com/blog/2020/08/03/how-gitlab-pages-uses-the-gitlab-api-to-serve-content/)
too.
diff --git a/doc/architecture/blueprints/database_scaling/size-limits.md b/doc/architecture/blueprints/database_scaling/size-limits.md
index 284f6402d3c..0bb1ae9efb4 100644
--- a/doc/architecture/blueprints/database_scaling/size-limits.md
+++ b/doc/architecture/blueprints/database_scaling/size-limits.md
@@ -138,7 +138,7 @@ There is no standard solution to reduce table sizes - there are many!
1. **Retention**: Delete unnecessary data, for example expire old and unneeded records.
1. **Remove STI**: We still use [single-table inheritance](../../../development/database/single_table_inheritance.md) in a few places, which is considered an anti-pattern. Redesigning this, we can split data into multiple tables.
1. **Index optimization**: Drop unnecessary indexes and consolidate overlapping indexes if possible.
-1. **Optimise data types**: Review data type decisions and optimise data types where possible (example: use integer instead of text for an enum column)
+1. **Optimize data types**: Review data type decisions and optimize data types where possible (example: use integer instead of text for an enum column)
1. **Partitioning**: Apply a partitioning scheme if there is a common access dimension.
1. **Normalization**: Review relational modeling and apply normalization techniques to remove duplicate data
1. **Vertical table splits**: Review column usage and split table vertically.
diff --git a/doc/architecture/blueprints/feature_flags_development/index.md b/doc/architecture/blueprints/feature_flags_development/index.md
index eaca7da6bd7..08253ac883c 100644
--- a/doc/architecture/blueprints/feature_flags_development/index.md
+++ b/doc/architecture/blueprints/feature_flags_development/index.md
@@ -23,7 +23,7 @@ The extensive usage of feature flags poses a few challenges
- Each feature flag that we add to codebase is a ~"technical debt" as it adds a
matrix of configurations.
- Testing each combination of feature flags is close to impossible, so we
- instead try to optimise our testing of feature flags to the most common
+ instead try to optimize our testing of feature flags to the most common
scenarios.
- There's a growing challenge of maintaining a growing number of feature flags.
We sometimes forget how our feature flags are configured or why we haven't
@@ -115,8 +115,8 @@ These are reason why these changes are needed:
## Iterations
-This work is being done as part of dedicated epic:
-[Improve internal usage of Feature Flags](https://gitlab.com/groups/gitlab-org/-/epics/3551).
+This work is being done as part of dedicated epic:
+[Improve internal usage of Feature Flags](https://gitlab.com/groups/gitlab-org/-/epics/3551).
This epic describes a meta reasons for making these changes.
## Who
diff --git a/doc/architecture/blueprints/graphql_api/index.md b/doc/architecture/blueprints/graphql_api/index.md
index eb045de491e..1ee322c412b 100644
--- a/doc/architecture/blueprints/graphql_api/index.md
+++ b/doc/architecture/blueprints/graphql_api/index.md
@@ -44,11 +44,11 @@ It is an opportunity to learn from our experience in evolving the REST API, for
the scale, and to apply this knowledge onto the GraphQL development efforts. We
can do that by building query-to-feature correlation mechanisms, adding
scalable state synchronization support and aligning GraphQL with other
-architectural initiatives being executed in parallel, like
+architectural initiatives being executed in parallel, like
[the support for direct uploads](https://gitlab.com/gitlab-org/gitlab/-/issues/280819).
GraphQL should be secure by default. We can avoid common security mistakes by
-building mechanisms that will help us to enforce
+building mechanisms that will help us to enforce
[OWASP GraphQL recommendations](https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html)
that are relevant to us.
diff --git a/doc/architecture/blueprints/object_storage/index.md b/doc/architecture/blueprints/object_storage/index.md
index b70339c8b8d..7a4ecd0e5a8 100644
--- a/doc/architecture/blueprints/object_storage/index.md
+++ b/doc/architecture/blueprints/object_storage/index.md
@@ -31,8 +31,8 @@ underlying implementation for shared, distributed, highly-available
(HA) file storage.
Over time, we have built support for object storage across the
-application, solving specific problems in a
-[multitude of iterations](https://about.gitlab.com/company/team/structure/working-groups/object-storage/#company-efforts-on-uploads).
+application, solving specific problems in a
+[multitude of iterations](https://about.gitlab.com/company/team/structure/working-groups/object-storage/#company-efforts-on-uploads).
This has led to increased complexity across the board, from development
(new features and bug fixes) to installation:
@@ -67,7 +67,7 @@ This has led to increased complexity across the board, from development
The following is a brief description of the main directions we can take to
remove the pain points affecting our object storage implementation.
-This is also available as [a YouTube video](https://youtu.be/X9V_w8hsM8E) recorded for the
+This is also available as [a YouTube video](https://youtu.be/X9V_w8hsM8E) recorded for the
[Object Storage Working Group](https://about.gitlab.com/company/team/structure/working-groups/object-storage/).
### Simplify GitLab architecture by shipping MinIO
@@ -78,7 +78,7 @@ local storage and object storage.
With local storage, there is the assumption of a shared storage
between components. This can be achieved by having a single box
-installation, without HA, or with a NFS, which
+installation, without HA, or with a NFS, which
[we no longer recommend](../../../administration/nfs.md).
We have a testing gap on object storage. It also requires Workhorse
@@ -134,7 +134,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.
+of them are unmaintained.
[We have customers who cannot push 5GB 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
@@ -214,7 +214,7 @@ Proposal:
DRIs:
-The DRI for this blueprint is the
+The DRI for this blueprint is the
[Object Storage Working Group](https://about.gitlab.com/company/team/structure/working-groups/object-storage/).
<!-- vale gitlab.Spelling = YES -->
diff --git a/doc/architecture/blueprints/pods/index.md b/doc/architecture/blueprints/pods/index.md
new file mode 100644
index 00000000000..fc33a4f441b
--- /dev/null
+++ b/doc/architecture/blueprints/pods/index.md
@@ -0,0 +1,162 @@
+---
+stage: enablement
+group: pods
+comments: false
+description: 'Pods'
+---
+
+# Pods
+
+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.
+
+## Summary
+
+Pods is a new architecture for our Software as a Service platform that is horizontally-scalable, resilient, and provides a more consistent user experience. It may also provide additional features in the future, such as data residency control (regions) and federated features.
+
+## Terminology
+
+We use the following terms to describe components and properties of the Pods architecture.
+
+### Pod
+
+A Pod is a set of infrastructure components that contains multiple workspaces that belong to different organizations. The components include both datastores (PostgreSQL, Redis etc.) and stateless services (web etc.). The infrastructure components provided within a Pod are shared among workspaces but not shared with other Pods. This isolation of infrastructure components means that Pods are independent from each other.
+
+#### Pod properties
+
+- Each pod is independent from the others
+- Infrastructure components are shared by workspaces within a Pod
+- More Pods can be provisioned to provide horizontal scalability
+- A failing Pod does not lead to failure of other Pods
+- Noisy neighbor effects are limited to within a Pod
+- Pods are not visible to organizations; it is an implementation detail
+- Pods may be located in different geographical regions (for example, EU, US, JP, UK)
+
+Discouraged synonyms: GitLab instance, cluster, shard
+
+### Workspace
+
+A [workspace](../../../user/workspace/index.md) is the name for the top-level namespace that is used by organizations to manage everything GitLab. It will provide similar administrative capabilities to a self-managed instance.
+
+See more in the [workspace group overview](https://about.gitlab.com/direction/manage/workspace/#overview).
+
+#### Workspace properties
+
+- Workspaces are isolated from each other by default
+- A workspace is located on a single Pod
+- Workspaces share the resources provided by a Pod
+
+### Top-Level namespace
+
+A top-level namespace is the logical object container in the code that represents all groups, subgroups and projects that belong to an organization.
+
+A top-level namespace is the root of nested collection namespaces and projects. The namespace and its related entities form a tree-like hierarchy: Namespaces are the nodes of the tree, projects are the leaves. An organization usually contains a single top-level namespace, called a workspace.
+
+Example:
+
+`https://gitlab.com/gitlab-org/gitlab/`:
+
+- `gitlab-org` is a `top-level namespace`; the root for all groups and projects of an organization
+- `gitlab` is a `project`; a project of the organization.
+
+Discouraged synonyms: Root-level namespace
+
+#### Top-level namespace properties
+
+Same as workspaces.
+
+### Users
+
+Users are available globally and not restricted to a single Pod. Users can create multiple workspaces and they may be members of several workspaces and contribute to them. Because users' activity is not limited to an individual Pod, their activity needs to be aggregated across Pods to reflect all their contributions (for example TODOs). This means, the Pods architecture may need to provide a central dashboard.
+
+#### User properties
+
+- Users are shared globally across all Pods
+- Users can create multiple workspaces
+- Users can be a member of multiple workspaces
+
+## Goals
+
+### Scalability
+
+The main goal of this new shared-infrastructure architecture is to provide additional scalability for our SaaS Platform. GitLab.com is largely monolithic and we have estimated (internal) that the current architecture has scalability limitations, even when database partitioning and decomposition are taken into account.
+
+Pods provide a horizontally scalable solution because additional Pods can be created based on demand. Pods can be provisioned and tuned as needed for optimal scalability.
+
+### Increased availability
+
+A major challenge for shared-infrastructure architectures is a lack of isolation between workspaces. This can lead to noisy neighbor effects. A organization's behavior inside a workspace can impact all other workspaces. This is highly undesirable. Pods provide isolation at the pod level. A group of organizations is fully isolated from other organizations located on a different Pod. This minimizes noisy neighbor effects while still benefiting from the cost-efficiency of shared infrastructure.
+
+Additionally, Pods provide a way to implement disaster recovery capabilities. Entire Pods may be replicated to read-only standbys with automatic failover capabilities.
+
+### A consistent experience
+
+Organizations should have the same user experience on our SaaS platform as they do on a self-managed GitLab instance.
+
+### Regions
+
+GitLab.com is only hosted within the United States of America. Organizations located in other regions have voiced demand for local SaaS offerings. Pods provide a path towards [GitLab Regions](https://gitlab.com/groups/gitlab-org/-/epics/6037) because Pods may be deployed within different geographies. Depending on which of the organization's data is located outside a Pod, this may solve data residency and compliance problems.
+
+## Market segment
+
+Pods would provide a solution for organizations in the small to medium business (up to 100 users) and the mid-market segment (up to 2000 users).
+(See [segmentation definitions](https://about.gitlab.com/handbook/sales/field-operations/gtm-resources/#segmentation).)
+Larger organizations may benefit substantially from [GitLab Dedicated](../../../subscriptions/gitlab_dedicated/index.md).
+
+## High-level architecture problems to solve
+
+A number of technical issues need to be resolved to implement Pods (in no particular order). This section will be expanded.
+
+1. How are users of an organization routed to the correct Pod containing their workspace?
+1. How do users authenticate?
+1. How are Pods rebalanced?
+1. How are Pods provisioned?
+1. How can Pods implement disaster recovery capabilities?
+
+## Iteration 1
+
+Ultimately, a Pods architecture should offer the same user experience as self-managed and GitLab dedicated. However, at this moment GitLab.com has many more "social-network"-like capabilities that will be difficult to implement with a Pods architecture. We should evaluate if the SMB and mid market segment is interested in these features, or if not having them is acceptable in most cases.
+
+The first iteration of Pods will still contain some limitations that would break cross-workspace workflows. This means it may only be acceptable for new customers, or for existing customers that are briefed.
+
+Limitations are:
+
+- An organization can create only a single workspace.
+- Workspaces are isolated from each other. This means cross-workspace workflows are broken.
+
+## Iteration 2
+
+Based on user research, we may want to change certain features to work across namespaces to allow organizations to interact with each other in specific circumstances. We may also allow organizations to have more than one workspace. This is particularly relevant for organizations with sub-divisions, or multi-national organizations that want to have workspaces in different regions.
+
+Additional features:
+
+- Specific features allow for cross-workspace interactions, for example forking, search.
+- An organization can own multiple workspaces on different Pods.
+
+### Links
+
+- [Internal Pods presentation](https://docs.google.com/presentation/d/1x1uIiN8FR9fhL7pzFh9juHOVcSxEY7d2_q4uiKKGD44/edit#slide=id.ge7acbdc97a_0_155)
+- [Pods Epic](https://gitlab.com/groups/gitlab-org/-/epics/7582)
+- [Database Group investigation](https://about.gitlab.com/handbook/engineering/development/enablement/data_stores/database/doc/root-namespace-sharding.html)
+- [Shopify Pods architecture](https://shopify.engineering/a-pods-architecture-to-allow-shopify-to-scale)
+- [Opstrace architecture](https://gitlab.com/gitlab-org/opstrace/opstrace/-/blob/main/docs/architecture/overview.md)
+
+### Who
+
+| Role | Who
+|------------------------------|-------------------------|
+| Author | Fabian Zimmer |
+| Architecture Evolution Coach | Kamil Trzciński |
+| Engineering Leader | TBD |
+| Product Manager | Fabian Zimmer |
+| Domain Expert / Database | TBD |
+
+DRIs:
+
+| Role | Who
+|------------------------------|------------------------|
+| Leadership | TBD |
+| Product | Fabian Zimmer |
+| Engineering | Thong Kuah |
diff --git a/doc/architecture/blueprints/rate_limiting/index.md b/doc/architecture/blueprints/rate_limiting/index.md
new file mode 100644
index 00000000000..692cef4b11d
--- /dev/null
+++ b/doc/architecture/blueprints/rate_limiting/index.md
@@ -0,0 +1,411 @@
+---
+stage: none
+group: unassigned
+comments: false
+description: 'Next Rate Limiting Architecture'
+---
+
+# Next Rate Limiting Architecture
+
+## Summary
+
+Introducing reasonable application limits is a very important step in any SaaS
+platform scaling strategy. The more users a SaaS platform has, the more
+important it is to introduce sensible rate limiting and policies enforcement
+that will help to achieve availability goals, reduce the problem of noisy
+neighbours for users and ensure that they can keep using a platform
+successfully.
+
+This is especially true for GitLab.com. Our goal is to have a reasonable and
+transparent strategy for enforcing application limits, which will become a
+definition of a responsible usage, to help us with keeping our availability and
+user satisfaction at a desired level.
+
+We've been introducing various application limits for many years already, but
+we've never had a consistent strategy for doing it. What we want to build now is
+a consistent framework used by engineers and product managers, across entire
+application stack, to define, expose and enforce limits and policies.
+
+Lack of consistency in defining limits, not being able to expose them to our
+users, support engineers and satellite services, has negative impact on our
+productivity, makes it difficult to introduce new limits and eventually
+prevents us from enforcing responsible usage on all layers of our application
+stack.
+
+This blueprint has been written to consolidate our limits and to describe the
+vision of our next rate limiting and policies enforcement architecture.
+
+_Disclaimer: The following contains information related to upcoming products,
+features, and functionality._
+
+_It is important to note that the information presented is for informational
+purposes only. Please do not rely on this information for purchasing or
+planning purposes._
+
+_As with all projects, the items mentioned in this document and linked pages are
+subject to change or delay. The development, release and timing of any
+products, features, or functionality remain at the sole discretion of GitLab
+Inc._
+
+## Goals
+
+**Implement a next architecture for rate limiting and policies definition.**
+
+## Challenges
+
+- We have many ways to define application limits, in many different places.
+- It is difficult to understand what limits have been applied to a request.
+- It is difficult to introduce new limits, even more to define policies.
+- 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).
+- 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.
+- There is no single way to change limits for a namespace / project / user / customer.
+- There is no single way to monitor limits through real-time metrics.
+- There is no framework for hierarchical limit configuration (instance / namespace / sub-group / project).
+- We allow disabling rate-limiting for some marquee SaaS customers, but this
+ increases a risk for those same customers. We should instead be able to set
+ higher limits.
+
+## Opportunity
+
+We want to build a new framework, making it easier to define limits, quotas and
+policies, and to enforce / adjust them in a controlled way, through robust
+monitoring capabilities.
+
+<!-- markdownlint-disable MD029 -->
+
+1. Build a framework to define and enforce limits in GitLab Rails.
+2. Build an API to consume limits in satellite service and expose them to users.
+3. Extract parts of this framework into a dedicated GitLab Limits Service.
+
+<!-- markdownlint-enable MD029 -->
+
+The most important opportunity here is consolidation happening on multiple
+levels:
+
+1. Consolidate on the application limits tooling used in GitLab Rails.
+1. Consolidate on the process of adding and managing application limits.
+1. Consolidate on the behavior of hierarchical cascade of limits and overrides.
+1. Consolidate on the application limits tooling used across entire application stack.
+1. Consolidate on the policies enforcement tooling used across entire company.
+
+Once we do that we will unlock another opportunity: to ship the new framework /
+tooling as a GitLab feature to unlock these consolidation benefits for our
+users, customers and entire wider community audience.
+
+### Limits, quotas and policies
+
+This document aims to describe our technical vision for building the next rate
+limiting architecture for GitLab.com. We refer to this architectural evolution
+as "the next rate limiting architecture", but this is a mental shortcut,
+because we actually want to build a better framework that will make it easier
+for us to manage not only rate limits, but also quotas and policies.
+
+Below you can find a short definition of what we understand by a limit, by a
+quota and by a policy.
+
+- **Limit:** A constraint on application usage, typically used to mitigate
+ 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
+- **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
+- **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
+ - _Example:_ deny access based on group-level constraints
+ (such as IP allowlist, SSO, and 2FA) across all services
+
+Technically, all of these are limits, because rate limiting is still
+"limiting", quota is usually a business limit, and policy limits what you can
+do with the application to enforce specific rules. By referring to a "limit" in
+this document we mean a limit that is defined to protect business, availability
+and security.
+
+### Framework to define and enforce limits
+
+First we want to build a new framework that will allow us to define and enforce
+application limits, in the GitLab Rails project context, in a more consistent
+and established way. In order to do that, we will need to build a new
+abstraction that will tell engineers how to define a limit in a structured way
+(presumably using YAML or Cue format) and then how to consume the limit in the
+application itself.
+
+We already do have many limits defined in the application, we can use them to
+triangulate to find a reasonable abstraction that will consolidate how we
+define, use and enforce limits.
+
+We envision building a simple Ruby library here (we can add it to LabKit) that
+will make it trivial for engineers to check if a certain limit has been
+exceeded or not.
+
+```yaml
+name: my_limit_name
+actors: user
+context: project, group, pipeline
+type: rate / second
+group: pipeline::execution
+limits:
+ warn: 2B / day
+ soft: 100k / s
+ hard: 500k / s
+```
+
+```ruby
+Gitlab::Limits::RateThreshold.enforce(:my_limit_name) do |threshold|
+ actor = current_user
+ context = current_project
+
+ threshold.available do |limit|
+ # ...
+ end
+
+ threshold.approaching do |limit|
+ # ...
+ end
+
+ threshold.exceeded do |limit|
+ # ...
+ end
+end
+```
+
+In the example above, when `my_limit_name` is defined in YAML, engineers will
+be check the current state and execute appropriate code block depending on the
+past usage / resource consumption.
+
+Things we want to build and support by default:
+
+1. Comprehensive dashboards showing how often limits are being hit.
+1. Notifications about the risk of hitting limits.
+1. Automation checking if limits definitions are being enforced properly.
+1. Different types of limits - time bound / number per resource etc.
+1. A panel that makes it easy to override limits per plan / namespace.
+1. Logging that will expose limits applied in Kibana.
+1. An automatically generated documentation page describing all the limits.
+
+### API to expose limits and policies
+
+Once we have an established a consistent way to define application limits we
+can build a few API endpoints that will allow us to expose them to our users,
+customers and other satellite services that may want to consume them.
+
+Users will be able to ask the API about the limits / thresholds that have been
+set for them, how often they are hitting them, and what impact those might have
+on their business. This kind of transparency can help them with communicating
+their needs to customer success team at GitLab, and we will be able to
+communicate how the responsible usage is defined at a given moment.
+
+Because of how GitLab architecture has been built, GitLab Rails application, in
+most cases, behaves as a central enterprise service bus (ESB) and there are a
+few satellite services communicating with it. Services like Container Registry,
+GitLab Runners, Gitaly, Workhorse, KAS could use the API to receive a set of
+application limits those are supposed to enforce. This will still allow us to
+define all of them in a single place.
+
+We should, however, avoid the possible negative-feedback-loop, that will put
+additional strain on the Rails application when there is a sudden increase in
+usage happening. This might be a big customer starting a new automation that
+traverses our API or a Denial of Service attack. In such cases, the additional
+traffic will reach GitLab Rails and subsequently also other satellite services.
+Then the satellite services may need to consult Rails again to obtain new
+instructions / policies around rate limiting the increased traffic. This can
+put additional strain on Rails application and eventually degrade performance
+even more. In order to avoid this problem, we should extract the API endpoints
+to separate service (see the section below) if the request rate to those
+endpoints depends on the volume of incoming traffic. Alternatively we can keep
+those endpoints in Rails if the increased traffic will not translate into
+increase of requests rate or increase in resources consumption on these API
+endpoints on the Rails side.
+
+#### Decoupled Limits Service
+
+At some point we may decide that it is time to extract a stateful backend
+responsible for storing metadata around limits, all the counters and state
+required, and exposing API, out of Rails.
+
+It is impossible to make a decision about extracting such a decoupled limits
+service yet, because we will need to ship more proof-of-concept work, and
+concrete iterations to inform us better about when and how we should do that. We
+will depend on the Evolution Architecture practice to guide us towards either
+extracting Decoupled Limits Service or not doing that at all.
+
+As we evolve this blueprint, we will document our findings and insights about
+how this service should look like, in this section of the document.
+
+### GitLab Policy Service
+
+_Disclaimer_: Extracting a GitLab Policy Service might be out of scope
+of the current workstream organized around implementing this blueprint.
+
+Not all limits can be easily described in YAML. There are some more complex
+policies that require a bit more sophisticated approach and a declarative
+programming language used to enforce them. One example of such a language might be
+[Rego](https://www.openpolicyagent.org/docs/latest/policy-language/) language.
+It is a standardized way to define policies in
+[OPA - Open Policy Agent](https://www.openpolicyagent.org/). At GitLab we are
+already using OPA in some departments. We envision the need to additional
+consolidation to not only consolidate on the tooling we are using internally at
+GitLab, but to also transform the Next Rate Limiting Architecture into
+something we can make a part of the product itself.
+
+Today, we already do have a policy service we are using to decide whether a
+pipeline can be created or not. There are many policies defined in
+[Pipeline Validation Service](https://gitlab.com/gitlab-org/modelops/anti-abuse/pipeline-validation-service).
+There is a significant opportunity here in transforming Pipeline Validation
+Service into a general purpose GitLab Policy Service / GitLab Policy Agent that
+will be well integrated into the GitLab product itself.
+
+Generalizing Pipeline Validation Service into GitLab Policy Service can bring a
+few interesting benefits:
+
+1. Consolidate on our tooling across the company to improve efficiency.
+1. Integrate our GitLab Rails limits framework to resolve policies using the policy service.
+1. Do not struggle to define complex policies in YAML and hack evaluating them in Ruby.
+1. Build a policy for GraphQL queries limiting using query execution cost estimation.
+1. Make it easier to resolve policies that do not need "hierarchical limits" structure.
+1. Make GitLab Policy Service part of the product and integrate it into the single application.
+
+We envision using GitLab Policy Service to be place to define policies that do
+not require knowing anything about the hierarchical structure of the limits.
+There are limits that do not need this, like IP addresses allow-list, spam
+checks, configuration validation etc.
+
+We defined "Policy" as a stateless, functional-style, limit. It takes input
+arguments and evaluates to either true or false. It should not require a global
+counter or any other volatile global state to get evaluated. It may still
+require to have a globally defined rules / configuration, but this state is not
+volatile in a same way a rate limiting counter may be, or a megabytes consumed
+to evaluate quota limit.
+
+#### Policies used internally and externally
+
+The GitLab Policy Service might be used in two different ways:
+
+1. Rails limits framework will use it as a source of policies enforced internally.
+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
+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.
+
+Both use-cases are valid but we will need to make technical decision about how
+to separate them. Even if we decide to implement them both in a single service,
+we will need to draw a strong boundary between the two.
+
+The same principle might apply to Decouple Limits Service described in one of
+the sections of this document above.
+
+#### The two limits / policy services
+
+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.
+
+## Hierarchical limits
+
+GitLab application aggregates users, projects, groups and namespaces in a
+hierarchical way. This hierarchical structure has been designed to make it
+easier to manage permissions, streamline workflows, and allow users and
+customers to store related projects, repositories, and other artifacts,
+together.
+
+It is important to design the new rate limiting framework in a way that it
+built on top of this hierarchical structure and engineers, customers, SREs and
+other stakeholders can understand how limits are being applied, enforced and
+overridden within the hierarchy of namespaces, groups and projects.
+
+We want to reduce the cognitive load required to understand how limits are
+being managed within the existing permissions structure. We might need to build
+a simple and easy-to-understand formula for how our application decides which
+limits and thresholds to apply for a given request and a given actor:
+
+> GitLab will read default limits for every operation, all overrides configured
+> and will choose a limit with the highest precedence configured. A limit
+> precedence needs to be explicitly configured for every override, a default
+> limit has precedence 100.
+
+One way in which we can simplify limits management in general is to:
+
+1. Have default limits / thresholds defined in YAML files with a default precedence 100.
+1. Allow limits to be overridden through the API, store overrides in the database.
+1. Every limit / threshold override needs to have an integer precedence value provided.
+1. Build an API that will take an actor and expose limits applicable for it.
+1. Build a dashboard showing actors with non-standard limits / overrides.
+1. Build a observability around this showing in Kibana when non-standard limits are being used.
+
+The points above represent an idea to use precedence score (or Z-Index for
+limits), but there may be better solutions, like just defining a direction of
+overrides - a lower limit might always override a limit defined higher in the
+hierarchy. Choosing a proper solution will require a thoughtful research.
+
+## Principles
+
+1. Try to avoid building rate limiting framework in a tightly coupled way.
+1. Build application limits API in a way that it can be easily extracted to a separate service.
+1. Build application limits definition in a way that is independent from the Rails application.
+1. Build tooling that produce consistent behavior and results across programming languages.
+1. Build the new framework in a way that we can extend to allow self-managed admins to customize limits.
+1. Maintain consistent features and behavior across SaaS and self-managed codebase.
+1. Be mindful about a cognitive load added by the hierarchical limits, aim to reduce it.
+
+## Status
+
+Request For Comments.
+
+## Timeline
+
+- 2022-04-27: [Rate Limit Architecture Working Group](https://about.gitlab.com/company/team/structure/working-groups/rate-limit-architecture/) started.
+- 2022-06-07: Working Group members [started submitting technical proposals](https://gitlab.com/gitlab-org/gitlab/-/issues/364524) for the next rate limiting architecture.
+- 2022-06-15: We started [scoring proposals](https://docs.google.com/spreadsheets/d/1DFHU1kSdTnpydwM5P2RK8NhVBNWgEHvzT72eOhB8F9E) submitted by Working Group members.
+- 2022-07-06: A fourth, [consolidated proposal](https://gitlab.com/gitlab-org/gitlab/-/issues/364524#note_1017640650), has been submitted.
+- 2022-07-12: Started working on the design document following [Architecture Evolution Workflow](https://about.gitlab.com/handbook/engineering/architecture/workflow/).
+- 2022-09-08: The initial version of the blueprint has been merged.
+
+## Who
+
+Proposal:
+
+<!-- vale gitlab.Spelling = NO -->
+
+| Role | Who
+|------------------------------|-------------------------|
+| Author | Grzegorz Bizon |
+| Author | Fabio Pitino |
+| Author | Marshall Cottrell |
+| Author | Hayley Swimelar |
+| Engineering Leader | Sam Goldstein |
+| Product Manager | |
+| Architecture Evolution Coach | |
+| Recommender | |
+| Recommender | |
+| Recommender | |
+| Recommender | |
+
+DRIs:
+
+| Role | Who
+|------------------------------|------------------------|
+| Leadership | |
+| Product | |
+| Engineering | |
+
+Domain experts:
+
+| Area | Who
+|------------------------------|------------------------|
+| | |
+
+<!-- vale gitlab.Spelling = YES -->
diff --git a/doc/architecture/blueprints/runner_scaling/index.md b/doc/architecture/blueprints/runner_scaling/index.md
index 494aaa6a641..8f7062a1148 100644
--- a/doc/architecture/blueprints/runner_scaling/index.md
+++ b/doc/architecture/blueprints/runner_scaling/index.md
@@ -33,7 +33,7 @@ This design choice was crucial for the GitLab Runner success. Since that time
the auto-scaling feature has been used by many users and customers and enabled
rapid growth of CI/CD adoption on GitLab.com.
-We can not, however, continue using Docker Machine. Work on that project
+We can not, however, continue using Docker Machine. Work on that project
[was paused in July 2018](https://github.com/docker/machine/issues/4537) and there
was no development made since that time (except for some highly important
security fixes). In 2018, after Docker Machine entered the "maintenance mode",
@@ -76,7 +76,7 @@ mechanism with a reliable and flexible mechanism. We might be unable to build a
drop-in replacement for Docker Machine, as there are presumably many reasons
why it has been deprecated. It is very difficult to maintain compatibility with
so many cloud providers, and it seems that Docker Machine has been deprecated
-in favor of Docker Desktop, which is not a viable replacement for us.
+in favor of Docker Desktop, which is not a viable replacement for us.
[This issue](https://github.com/docker/roadmap/issues/245) contains a discussion
about how people are using Docker Machine right now, and it seems that GitLab
CI is one of the most frequent reasons for people to keep using Docker Machine.
diff --git a/doc/ci/caching/index.md b/doc/ci/caching/index.md
index 88d59b1f223..d34f44ea9ba 100644
--- a/doc/ci/caching/index.md
+++ b/doc/ci/caching/index.md
@@ -84,7 +84,8 @@ test-job:
paths:
- .yarn-cache/
script:
- - bundle install --path=vendor
+ - bundle config set --local path 'vendor/ruby'
+ - bundle install
- yarn install --cache-folder .yarn-cache
- echo Run tests...
```
@@ -301,8 +302,7 @@ test:
If your project uses [pip](https://pip.pypa.io/en/stable/) to install
Python dependencies, the following example defines `cache` globally so that
-all jobs inherit it. Python libraries are installed in a virtual environment under `venv/`.
-pip's cache is defined under `.cache/pip/` and both are cached per-branch:
+all jobs inherit it. pip's cache is defined under `.cache/pip/` and is cached per-branch:
```yaml
#
@@ -317,13 +317,9 @@ variables:
# Pip's cache doesn't store the python packages
# https://pip.pypa.io/en/stable/reference/pip_install/#caching
-#
-# If you want to also cache the installed packages, you have to install
-# them in a virtualenv and cache it as well.
cache:
paths:
- .cache/pip
- - venv/
before_script:
- python -V # Print out python version for debugging
@@ -358,7 +354,8 @@ cache:
before_script:
- ruby -v # Print out ruby version for debugging
- - bundle install -j $(nproc) --path vendor/ruby # Install dependencies into ./vendor/ruby
+ - bundle config set --local path 'vendor/ruby' # The location to install the specified gems to
+ - bundle install -j $(nproc) # Install dependencies into ./vendor/ruby
rspec:
script:
@@ -384,14 +381,16 @@ cache:
test_job:
stage: test
before_script:
- - bundle install --without production --path vendor/ruby
+ - bundle config set --local path 'vendor/ruby'
+ - bundle install --without production
script:
- bundle exec rspec
deploy_job:
stage: production
before_script:
- - bundle install --without test --path vendor/ruby
+ - bundle config set --local path 'vendor/ruby' # The location to install the specified gems to
+ - bundle install --without test
script:
- bundle exec deploy
```
@@ -472,7 +471,7 @@ and should only be disabled in an environment where all users with Developer rol
To use the same cache for all branches:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **General pipelines**.
1. Clear the **Use separate caches for protected branches** checkbox.
@@ -569,7 +568,7 @@ The next time the pipeline runs, the cache is stored in a different location.
You can clear the cache in the GitLab UI:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **CI/CD > Pipelines**.
1. In the top right, select **Clear runner caches**.
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 c536ff59b84..cfe7be064fb 100644
--- a/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
+++ b/doc/ci/ci_cd_for_external_repos/bitbucket_integration.md
@@ -15,7 +15,8 @@ GitLab CI/CD can be used with Bitbucket Cloud by:
To use GitLab CI/CD with a Bitbucket Cloud repository:
1. In GitLab, create a project:
- 1. On the top menu, select **Projects > Create new project**.
+ 1. In GitLab, on the top bar, select **Main menu > Projects > View all projects**.
+ 1. On the right of the page, select **New project**.
1. Select **Run CI/CD for external repository**.
1. Select **Repository by URL**.
1. Fill in the fields with information from the repository in Bitbucket:
diff --git a/doc/ci/ci_cd_for_external_repos/github_integration.md b/doc/ci/ci_cd_for_external_repos/github_integration.md
index a928d315c6b..a48822fc214 100644
--- a/doc/ci/ci_cd_for_external_repos/github_integration.md
+++ b/doc/ci/ci_cd_for_external_repos/github_integration.md
@@ -34,7 +34,8 @@ repositories:
`repo` and `admin:repo_hook` so that GitLab can access your project,
update commit statuses, and create a web hook to notify GitLab of new commits.
1. In GitLab, create a project:
- 1. On the top menu, select **Projects > Create new project**.
+ 1. In GitLab, on the top bar, select **Main menu > Projects > View all projects**.
+ 1. On the right of the page, select **New project**.
1. Select **Run CI/CD for external repository**.
1. Select **GitHub**.
1. For **Personal access token**, paste the token.
@@ -61,7 +62,8 @@ To manually enable GitLab CI/CD for your repository:
1. Enter a **Token description** and update the scope to allow
`repo` so that GitLab can access your project and update commit statuses.
1. In GitLab, create a project:
- 1. On the top menu, select **Projects > Create new project**.
+ 1. In GitLab, on the top bar, select **Main menu > Projects > View all projects**.
+ 1. On the right of the page, select **New project**.
1. Select **Run CI/CD for external repository** and **Repository by URL**.
1. In the **Git repository URL** field, enter the HTTPS URL for your GitHub repository.
If your project is private, use the personal access token you just created for authentication.
diff --git a/doc/ci/ci_cd_for_external_repos/index.md b/doc/ci/ci_cd_for_external_repos/index.md
index e3a8141ed88..7c0889a329a 100644
--- a/doc/ci/ci_cd_for_external_repos/index.md
+++ b/doc/ci/ci_cd_for_external_repos/index.md
@@ -9,8 +9,8 @@ type: index, howto
>[Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/4642) in GitLab 10.6.
-GitLab CI/CD can be used with [GitHub](github_integration.md), [Bitbucket Cloud](bitbucket_integration.md), or any other
-Git server.
+GitLab CI/CD can be used with [GitHub](github_integration.md), [Bitbucket Cloud](bitbucket_integration.md),
+or any other Git server, though there are some [limitations](#limitations).
Instead of moving your entire project to GitLab, you can connect your
external repository to get the benefits of GitLab CI/CD.
@@ -24,7 +24,8 @@ snippets disabled. These features
To connect to an external repository:
-1. On the top bar, select **Menu > Projects > Create new project**.
+1. In GitLab, on the top bar, select **Main menu > Projects > View all projects**.
+1. On the right of the page, select **New project**.
1. Select **Run CI/CD for external repository**.
1. Select **GitHub** or **Repository by URL**.
1. Complete the fields.
@@ -86,7 +87,11 @@ The variable names are prefixed with `CI_EXTERNAL_PULL_REQUEST_`.
### Limitations
-This feature currently does not support Pull Requests from fork repositories. Any Pull Requests from fork repositories are ignored. [Read more](https://gitlab.com/gitlab-org/gitlab/-/issues/5667).
+This feature does not support:
+
+- The [manual connection method](github_integration.md#connect-manually) required for GitHub Enterprise.
+ If the integration is connected manually, external pull requests [do not trigger pipelines](https://gitlab.com/gitlab-org/gitlab/-/issues/323336#note_884820753).
+- Pull requests from fork repositories. [Pull Requests from fork repositories are ignored](https://gitlab.com/gitlab-org/gitlab/-/issues/5667).
Given that GitLab creates 2 pipelines, if changes are pushed to a remote branch that
references an open Pull Request, both contribute to the status of the Pull Request
diff --git a/doc/ci/cloud_deployment/ecs/deploy_to_aws_ecs.md b/doc/ci/cloud_deployment/ecs/deploy_to_aws_ecs.md
index aea7b492d4e..2d1c3f927e2 100644
--- a/doc/ci/cloud_deployment/ecs/deploy_to_aws_ecs.md
+++ b/doc/ci/cloud_deployment/ecs/deploy_to_aws_ecs.md
@@ -79,7 +79,7 @@ and [Container Registry](../../../user/packages/container_registry/index.md).
![Create project](img/initial-pipeline.png)
-1. Visit **Packages & Registries > Container Registry**. Make sure the application image has been
+1. Visit **Packages and registries > Container Registry**. Make sure the application image has been
pushed.
![Create project](img/registry.png)
diff --git a/doc/ci/cloud_deployment/index.md b/doc/ci/cloud_deployment/index.md
index 5df396e796e..82d914f0a6a 100644
--- a/doc/ci/cloud_deployment/index.md
+++ b/doc/ci/cloud_deployment/index.md
@@ -50,6 +50,7 @@ deploy:
script:
- aws s3 ...
- aws create-deployment ...
+ environment: production
```
GitLab provides a Docker image that includes the AWS CLI:
@@ -215,3 +216,14 @@ To deploy to EC2, complete the following steps.
- Your built application is pushed to your S3 bucket then and deployed to your EC2 instance, based
on the related JSON object's content. The deployment job finishes when the deployment to EC2
is done or has failed.
+
+## Troubleshooting
+
+### Error `'ascii' codec can't encode character '\uxxxx'`
+
+This error can occur when the response from the `aws-cli` utility used by the Cloud Deploy images contains a Unicode character. The Cloud Deploy images we provide do not have a defined locale and default to using ASCII. To resolve this error, add the following CI/CD variable:
+
+```yaml
+variables:
+ LANG: "UTF-8"
+```
diff --git a/doc/ci/cloud_services/azure/index.md b/doc/ci/cloud_services/azure/index.md
new file mode 100644
index 00000000000..901b36afde6
--- /dev/null
+++ b/doc/ci/cloud_services/azure/index.md
@@ -0,0 +1,157 @@
+---
+stage: Verify
+group: Pipeline Authoring
+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
+---
+
+# Configure OpenID Connect in Azure to retrieve temporary credentials
+
+This tutorial demonstrates how to use a JSON web token (JWT) in a GitLab CI/CD job
+to retrieve temporary credentials from Azure without needing to store secrets.
+
+To get started, configure OpenID Connect (OIDC) for identity federation between GitLab and Azure.
+For more information on using OIDC with GitLab, read [Connect to cloud services](../index.md).
+
+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://docs.microsoft.com/cli/azure/install-azure-cli).
+ Alternatively, you can follow all the steps below with the [Azure Cloud Shell](https://shell.azure.com/).
+- A GitLab project.
+
+To complete this tutorial:
+
+1. [Create Azure AD application and service principal](#create-azure-ad-application-and-service-principal).
+1. [Create Azure AD federated identity credentials](#create-azure-ad-federated-identity-credentials).
+1. [Grant permissions for the service principal](#grant-permissions-for-the-service-principal).
+1. [Retrieve a temporary credential](#retrieve-a-temporary-credential).
+
+For more information, review Azure's documentation on [Workload identity federation](https://docs.microsoft.com/azure/active-directory/develop/workload-identity-federation).
+
+## Create Azure AD application and service principal
+
+To create an [Azure AD application](https://docs.microsoft.com/cli/azure/ad/app?view=azure-cli-latest#az-ad-app-create)
+and service principal:
+
+1. In the Azure CLI, create the AD application:
+
+ ```shell
+ appId=$(az ad app create --display-name gitlab-oidc --query appId -otsv)
+ ```
+
+ Save the `appId` (Application client ID) output, as you need it later
+ to configure your GitLab CI/CD pipeline.
+
+1. Create a corresponding [Service Principal](https://docs.microsoft.com/cli/azure/ad/sp?view=azure-cli-latest#az-ad-sp-create):
+
+ ```shell
+ az ad sp create --id $appId --query appId -otsv
+ ```
+
+Instead of the Azure CLI, you can [use the Azure Portal to create these resources](https://docs.microsoft.com/azure/active-directory/develop/howto-create-service-principal-portal).
+
+## Create Azure AD federated identity credentials
+
+To create the federated identity credentials for the above Azure AD application:
+
+```shell
+objectId=$(az ad app show --id $appId --query id -otsv)
+
+cat <<EOF > body.json
+{
+ "name": "gitlab-federated-identity",
+ "issuer": "https://gitlab.example.com",
+ "subject": "project_path:<mygroup>/<myproject>:ref_type:branch:ref:<branch>",
+ "description": "GitLab service account federated identity",
+ "audiences": [
+ "https://gitlab.example.com"
+ ]
+}
+EOF
+
+az rest --method POST --uri "https://graph.microsoft.com/beta/applications/$objectId/federatedIdentityCredentials" --body @body.json
+```
+
+For issues related to the values of `issuer`, `subject` or `audiences`, see the
+[troubleshooting](#troubleshooting) details.
+
+Optionally, you can now verify the Azure AD application and the Azure AD federated
+identity credentials from the Azure Portal:
+
+1. Open the [Azure Active Directory App Registration](https://portal.azure.com/#view/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/~/RegisteredApps)
+ view and select the appropriate app registration by searching for the display name `gitlab-oidc`.
+1. On the overview page you can verify details like the `Application (client) ID`,
+ `Object ID`, and `Tenant ID`.
+1. Under `Certificates & secrets`, go to `Federated credentials` to review your
+ Azure AD federated identity credentials.
+
+## Grant permissions for the service principal
+
+After you create the credentials, use [`role assignment`](https://docs.microsoft.com/cli/azure/role/assignment?view=azure-cli-latest#az-role-assignment-create)
+to grant permissions to the above service principal to access to Azure resources:
+
+```shell
+az role assignment create --assignee $appId --role Reader --scope /subscriptions/<subscription-id>
+```
+
+You can find your subscription ID in:
+
+- The [Azure Portal](https://docs.microsoft.com/azure/azure-portal/get-subscription-tenant-id#find-your-azure-subscription).
+- The [Azure CLI](https://docs.microsoft.com/cli/azure/manage-azure-subscriptions-azure-cli#get-the-active-subscription).
+
+## Retrieve a temporary credential
+
+After you configure the Azure AD application and federated identity credentials,
+the CI/CD job can retrieve a temporary credential by using the [Azure CLI](https://docs.microsoft.com/cli/azure/reference-index?view=azure-cli-latest#az-login):
+
+```yaml
+default:
+ image: mcr.microsoft.com/azure-cli:latest
+
+variables:
+ AZURE_CLIENT_ID: "<client-id>"
+ AZURE_TENANT_ID: "<tenant-id>"
+
+auth:
+ script:
+ - az login --service-principal -u $AZURE_CLIENT_ID -t $AZURE_TENANT_ID --federated-token $CI_JOB_JWT_V2
+ - az account show
+```
+
+The CI/CD variables are:
+
+- `AZURE_CLIENT_ID`: The [application client ID you saved earlier](#create-azure-ad-application-and-service-principal).
+- `AZURE_TENANT_ID`: Your Azure Active Directory. You can
+ [find it by using the Azure CLI or Azure Portal](https://docs.microsoft.com/azure/active-directory/fundamentals/active-directory-how-to-find-tenant).
+- `CI_JOB_JWT_V2`: The JSON web token is a [predefined CI/CD variable](../../variables/predefined_variables.md).
+
+## Troubleshooting
+
+### "No matching federated identity record found"
+
+If you receive the error `ERROR: AADSTS70021: No matching federated identity record found for presented assertion.`
+you should verify:
+
+- The `Issuer` defined in the Azure AD federated identity credentials, for example
+ `https://gitlab.com` or your own GitLab URL.
+- The `Subject identifier` defined in the Azure AD federated identity credentials,
+ for example `project_path:<mygroup>/<myproject>:ref_type:branch:ref:<branch>`.
+ - For the `gitlab-group/gitlab-project` project and `main` branch it would be:
+ `project_path:gitlab-group/gitlab-project:ref_type:branch:ref:main`.
+ - The correct values of `mygroup` and `myproject` can be retrieved by checking the URL
+ when accessing your GitLab project or by selecting the **Clone** option in the project.
+- The `Audience` defined in the Azure AD federated identity credentials, for example `https://gitlab.com`
+ or your own GitLab URL.
+
+You can review these settings, as well as your `AZURE_CLIENT_ID` and `AZURE_TENANT_ID`
+CI/CD variables, from the Azure Portal:
+
+1. Open the [Azure Active Directory App Registration](https://portal.azure.com/#view/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/~/RegisteredApps)
+ view and select the appropriate app registration by searching for the display name `gitlab-oidc`.
+1. On the overview page you can verify details like the `Application (client) ID`,
+ `Object ID`, and `Tenant ID`.
+1. Under `Certificates & secrets`, go to `Federated credentials` to review your
+ Azure AD federated identity credentials.
+
+Review [Connect to cloud services](../index.md) for further details.
diff --git a/doc/ci/cloud_services/index.md b/doc/ci/cloud_services/index.md
index 1493a930099..93fedb0ffca 100644
--- a/doc/ci/cloud_services/index.md
+++ b/doc/ci/cloud_services/index.md
@@ -16,7 +16,7 @@ GitLab CI/CD supports [OpenID Connect (OIDC)](https://openid.net/connect/faq/) t
- Account on GitLab.
- Access to a cloud provider that supports OIDC to configure authorization and create roles.
-The original implementation of `CI_JOB_JWT` supports [HashiCorp Vault integration](../examples/authenticating-with-hashicorp-vault/). The updated implementation of `CI_JOB_JWT_V2` supports additional cloud providers with OIDC including AWS, GCP, and Vault.
+The original implementation of `CI_JOB_JWT` supports [HashiCorp Vault integration](../examples/authenticating-with-hashicorp-vault/index.md). The updated implementation of `CI_JOB_JWT_V2` supports additional cloud providers with OIDC including AWS, Azure, GCP, and Vault.
NOTE:
Configuring OIDC enables JWT token access to the target environments for all pipelines.
@@ -25,8 +25,9 @@ review for the pipeline, focusing on the additional access. You can use the [sof
as a starting point, and for more information about supply chain attacks, see
[How a DevOps Platform helps protect against supply chain attacks](https://about.gitlab.com/blog/2021/04/28/devops-platform-supply-chain-attacks/).
-WARNING:
-The `CI_JOB_JWT_V2` variable is under development [(alpha)](../../policy/alpha-beta-support.md#alpha-features) and is not yet suitable for production use.
+The `CI_JOB_JWT_V2` variable is available for testing, but the full feature is planned
+to be generally available when [issue 360657](https://gitlab.com/gitlab-org/gitlab/-/issues/360657)
+is complete.
## Use cases
@@ -38,7 +39,7 @@ The `CI_JOB_JWT_V2` variable is under development [(alpha)](../../policy/alpha-b
## How it works
-Each job has a JSON web token (JWT) provided as a CI/CD [predefined variable](../variables/predefined_variables.md) named `CI_JOB_JWT` or `CI_JOB_JWT_V2`. This JWT can be used to authenticate with the OIDC-supported cloud provider such as AWS, GCP, or Vault.
+Each job has a JSON web token (JWT) provided as a CI/CD [predefined variable](../variables/predefined_variables.md) named `CI_JOB_JWT` or `CI_JOB_JWT_V2`. This JWT can be used to authenticate with the OIDC-supported cloud provider such as AWS, Azure, GCP, or Vault.
The following fields are included in the JWT:
@@ -112,7 +113,7 @@ sequenceDiagram
```
-1. Create an OIDC identity provider in the cloud (for example, AWS, GCP, Vault).
+1. Create an OIDC identity provider in the cloud (for example, AWS, Azure, GCP, Vault).
1. Create a conditional role in the cloud service that filters to a group, project, branch, or tag.
1. The CI/CD job includes a predefined variable `CI_JOB_JWT_V2` that is a JWT token. You can use this token for authorization with your cloud API.
1. The cloud verifies the token, validates the conditional role from the payload, and returns a temporary credential.
@@ -138,4 +139,5 @@ To configure the trust between GitLab and OIDC, you must create a conditional ro
To connect with your cloud provider, see the following tutorials:
- [Configure OpenID Connect in AWS](aws/index.md)
+- [Configure OpenID Connect in Azure](azure/index.md)
- [Configure OpenID Connect in Google Cloud](google_cloud/index.md)
diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md
index ea4ad25637b..4c9cb2923d7 100644
--- a/doc/ci/docker/using_docker_build.md
+++ b/doc/ci/docker/using_docker_build.md
@@ -345,7 +345,7 @@ not without its own challenges:
root file system, you can use the job's working directory as a mount point for
child containers. For example, if you have files you want to share with a
child container, you might create a subdirectory under `/builds/$CI_PROJECT_PATH`
- and use it as your mount point. For a more detailed explanation, view
+ and use it as your mount point. For a more detailed explanation, view
[issue #41227](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/41227).
```yaml
@@ -406,7 +406,7 @@ sudo gitlab-runner register -n \
##### Enable registry mirror for `docker:dind` service
When the Docker daemon starts inside of the service container, it uses
-the default configuration. You may want to configure a
+the default configuration. You may want to configure a
[registry mirror](https://docs.docker.com/registry/recipes/mirror/) for
performance improvements and to ensure you don't reach Docker Hub rate limits.
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index fdd8b6d38b8..f985b02be33 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -406,7 +406,7 @@ image. This image is private and requires you to log in into a private container
To configure access for `<aws_account_id>.dkr.ecr.<region>.amazonaws.com`, follow these steps:
-1. Make sure `docker-credential-ecr-login` is available in the GitLab Runner `$PATH`.
+1. Make sure [`docker-credential-ecr-login`](https://github.com/awslabs/amazon-ecr-credential-helper) is available in the GitLab Runner `$PATH`.
1. Have any of the following [AWS credentials setup](https://github.com/awslabs/amazon-ecr-credential-helper#aws-credentials).
Make sure that GitLab Runner can access the credentials.
1. Make GitLab Runner use it. There are two ways to accomplish this. Either:
diff --git a/doc/ci/docker/using_kaniko.md b/doc/ci/docker/using_kaniko.md
index 712fd7b45d6..ee8d73dd113 100644
--- a/doc/ci/docker/using_kaniko.md
+++ b/doc/ci/docker/using_kaniko.md
@@ -39,8 +39,6 @@ few important details:
GitLab CI/CD.
- The entrypoint needs to be [overridden](using_docker_images.md#override-the-entrypoint-of-an-image),
otherwise the build script doesn't run.
-- A Docker `config.json` file needs to be created with the authentication
- information for the desired container registry.
In the following example, kaniko is used to:
@@ -50,7 +48,7 @@ In the following example, kaniko is used to:
The job runs only when a tag is pushed. A `config.json` file is created under
`/kaniko/.docker` with the needed GitLab Container Registry credentials taken from the
[predefined CI/CD variables](../variables/index.md#predefined-cicd-variables)
-GitLab CI/CD provides.
+GitLab CI/CD provides. These are automatically read by the Kaniko tool.
In the last step, kaniko uses the `Dockerfile` under the
root directory of the project, builds the Docker image and pushes it to the
@@ -60,13 +58,10 @@ project's Container Registry while tagging it with the Git tag:
build:
stage: build
image:
- name: gcr.io/kaniko-project/executor:debug
+ name: gcr.io/kaniko-project/executor:v1.9.0-debug
entrypoint: [""]
script:
- - mkdir -p /kaniko/.docker
- - echo "{\"auths\":{\"${CI_REGISTRY}\":{\"auth\":\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 -w 0)\"}}}" > /kaniko/.docker/config.json
- - >-
- /kaniko/executor
+ - /kaniko/executor
--context "${CI_PROJECT_DIR}"
--dockerfile "${CI_PROJECT_DIR}/Dockerfile"
--destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}"
@@ -86,7 +81,6 @@ you must add the corresponding CI/CD variables for authentication to the `config
If you use a custom GitLab Runner behind an http(s) proxy, kaniko needs to be set
up accordingly. This means:
-- Adding the proxy to `/kaniko/.docker/config.json`
- Passing the `http_proxy` environment variables as build arguments so the Dockerfile
instructions can use the proxy when building the image.
@@ -95,25 +89,20 @@ The previous example can be extended as follows:
```yaml
build:
stage: build
+ variables:
+ http_proxy: <your-proxy>
+ https_proxy: <your-proxy>
+ no_proxy: <your-no-proxy>
image:
- name: gcr.io/kaniko-project/executor:debug
+ name: gcr.io/kaniko-project/executor:v1.9.0-debug
entrypoint: [""]
script:
- - mkdir -p /kaniko/.docker
- - |-
- KANIKOPROXYBUILDARGS=""
- KANIKOCFG="\"auths\":{\"${CI_REGISTRY}\":{\"auth\":\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"}}"
- if [ "x${http_proxy}" != "x" -o "x${https_proxy}" != "x" ]; then
- KANIKOCFG="${KANIKOCFG}, \"proxies\": { \"default\": { \"httpProxy\": \"${http_proxy}\", \"httpsProxy\": \"${https_proxy}\", \"noProxy\": \"${no_proxy}\"}}"
- KANIKOPROXYBUILDARGS="--build-arg http_proxy=${http_proxy} --build-arg https_proxy=${https_proxy} --build-arg no_proxy=${no_proxy}"
- fi
- KANIKOCFG="{ ${KANIKOCFG} }"
- echo "${KANIKOCFG}" > /kaniko/.docker/config.json
- - >-
- /kaniko/executor
+ - /kaniko/executor
--context "${CI_PROJECT_DIR}"
+ --build-arg http_proxy=$http_proxy
+ --build-arg https_proxy=$https_proxy
+ --build-arg no_proxy=$no_proxy
--dockerfile "${CI_PROJECT_DIR}/Dockerfile"
- "${KANIKOPROXYBUILDARGS}"
--destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}"
rules:
- if: $CI_COMMIT_TAG
@@ -142,8 +131,6 @@ store:
```yaml
before_script:
- - mkdir -p /kaniko/.docker
- - echo "{\"auths\":{\"${CI_REGISTRY}\":{\"auth\":\"$(printf "%s:%s" "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json
- |
echo "-----BEGIN CERTIFICATE-----
...
diff --git a/doc/ci/enable_or_disable_ci.md b/doc/ci/enable_or_disable_ci.md
index dac52a4540e..bac06972c7b 100644
--- a/doc/ci/enable_or_disable_ci.md
+++ b/doc/ci/enable_or_disable_ci.md
@@ -30,7 +30,7 @@ When you disable GitLab CI/CD:
To disable GitLab CI/CD in your project:
-1. On the top bar, select **Menu > Projects** and find your 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. In the **Repository** section, turn off **CI/CD**.
@@ -40,7 +40,7 @@ To disable GitLab CI/CD in your project:
To enable GitLab CI/CD in your project:
-1. On the top bar, select **Menu > Projects** and find your 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. In the **Repository** section, turn on **CI/CD**.
diff --git a/doc/ci/environments/deployment_approvals.md b/doc/ci/environments/deployment_approvals.md
index 23fa5d45196..26461d9827f 100644
--- a/doc/ci/environments/deployment_approvals.md
+++ b/doc/ci/environments/deployment_approvals.md
@@ -65,9 +65,6 @@ co-exist and multiple approval rules takes the precedence over the unified appro
#### Unified approval setting
-NOTE:
-At this time, it is not possible to require approvals for an existing protected environment. The workaround is to unprotect the environment and configure approvals when re-protecting the environment.
-
There are two ways to configure approvals for a protected environment:
1. Using the [UI](protected_environments.md#protecting-environments)
@@ -137,7 +134,7 @@ Prerequisites:
To approve or reject a deployment to a protected environment using the UI:
-1. On the top bar, select **Menu > Projects** and find your project.
+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}**).
@@ -169,7 +166,7 @@ curl --data "status=approved&comment=Looks good to me" \
### Using the UI
-1. On the top bar, select **Menu > Projects** and find your project.
+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 being deployed to.
1. Look for the `blocked` label.
diff --git a/doc/ci/environments/deployment_safety.md b/doc/ci/environments/deployment_safety.md
index 5b2e2045bdc..90efc7ba9ef 100644
--- a/doc/ci/environments/deployment_safety.md
+++ b/doc/ci/environments/deployment_safety.md
@@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Deployment safety **(FREE)**
-[Deployment jobs](../jobs/#deployment-jobs) are a specific kind of CI/CD
+[Deployment jobs](../jobs/index.md#deployment-jobs) are a specific kind of CI/CD
job. They can be more sensitive than other jobs in a pipeline,
and might need to be treated with extra care. GitLab has several features
that help maintain deployment security and stability.
@@ -66,7 +66,7 @@ For more information, see [Resource Group documentation](../resource_groups/inde
## Skip outdated deployment jobs
The effective execution order of pipeline jobs can vary from run to run, which
-could cause undesired behavior. For example, a [deployment job](../jobs/#deployment-jobs)
+could cause undesired behavior. For example, a [deployment job](../jobs/index.md#deployment-jobs)
in a newer pipeline could finish before a deployment job in an older pipeline.
This creates a race condition where the older deployment finishes later,
overwriting the "newer" deployment.
@@ -131,7 +131,7 @@ All users with the Maintainer role for the project have access to production sec
that can deploy to a production environment, you can create a separate project and configure a new
permission model that isolates the CD permissions from the original project and prevents the
original users with the Maintainer role for the project from accessing the production secret and CD configuration. You can
-connect the CD project to your development projects by using [multi-project pipelines](../pipelines/multi_project_pipelines.md).
+connect the CD project to your development projects by using [multi-project pipelines](../pipelines/downstream_pipelines.md#multi-project-pipelines).
## Protect `.gitlab-ci.yml` from change
diff --git a/doc/ci/environments/environments_dashboard.md b/doc/ci/environments/environments_dashboard.md
index c3c1e7868fd..11e9fe90e25 100644
--- a/doc/ci/environments/environments_dashboard.md
+++ b/doc/ci/environments/environments_dashboard.md
@@ -21,7 +21,7 @@ diagnose if there is a block at a particular point, or if there's
a more systemic problem you need to investigate.
You can access the dashboard on the top bar by selecting
-**Menu > Environments**.
+**Main menu > Environments**.
![Environments Dashboard with projects](img/environments_dashboard_v12_5.png)
diff --git a/doc/ci/environments/index.md b/doc/ci/environments/index.md
index 7747f5e9b78..c34a978709b 100644
--- a/doc/ci/environments/index.md
+++ b/doc/ci/environments/index.md
@@ -30,7 +30,7 @@ Prerequisites:
To view a list of environments and deployments:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Environments**.
The environments are displayed.
@@ -57,7 +57,7 @@ You can create an environment and deployment in the UI or in your `.gitlab-ci.ym
In the UI:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Environments**.
1. Select **New environment**.
1. Enter a name and external URL.
@@ -364,7 +364,7 @@ If there is a problem with a deployment, you can retry it or roll it back.
To retry or rollback a deployment:
-1. On the top bar, select **Menu > Projects** and find your project.
+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.
1. To the right of the deployment name:
@@ -642,7 +642,7 @@ When the environment is stopped, the system runs `on_stop` actions
You can view a deployment's expiration date in the GitLab UI.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Environments**.
1. Select the name of the deployment.
@@ -652,7 +652,7 @@ In the top left, next to the environment name, the expiration date is displayed.
You can manually override a deployment's expiration date.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Environments**.
1. Select the deployment name.
1. On the top right, select the thumbtack (**{thumbtack}**).
@@ -670,7 +670,7 @@ You can delete [stopped environments](#stop-an-environment) in the GitLab UI or
To delete a stopped environment in the GitLab UI:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Environments**.
1. Select the **Stopped** tab.
1. Next to the environment you want to delete, select **Delete environment**.
@@ -785,7 +785,7 @@ Limitations of GitLab Auto Rollback:
GitLab Auto Rollback is turned off by default. To turn it on:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Automatic deployment rollbacks**.
1. Select the checkbox for **Enable automatic rollbacks**.
@@ -945,7 +945,7 @@ the `review/feature-1` spec takes precedence over `review/*` and `*` specs.
Renaming an environment through the UI is not possible.
Instead, you need to delete the old environment and create a new one:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Environments**.
1. Find the environment and stop it.
1. Delete the environment.
diff --git a/doc/ci/environments/protected_environments.md b/doc/ci/environments/protected_environments.md
index 17eccc38747..e63777dc0e0 100644
--- a/doc/ci/environments/protected_environments.md
+++ b/doc/ci/environments/protected_environments.md
@@ -24,9 +24,13 @@ Maintainer role.
## Protecting environments
+Prerequisites:
+
+- When granting the **Allowed to deploy** permission to a group or sub-group, the user configuring the protected environment must be a **direct member** of the group or sub-group to be added. Otherwise, the group or sub-group will not show up in the dropdown. For more information see [issue #345140](https://gitlab.com/gitlab-org/gitlab/-/issues/345140).
+
To protect an environment:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Protected environments**.
1. From the **Environment** list, select the environment you want to protect.
@@ -238,7 +242,7 @@ To protect a group-level environment, make sure your environments have the corre
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/325249) in GitLab 15.1.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Protected environments**.
1. From the **Environment** list, select the [deployment tier of environments](index.md#deployment-tier-of-environments) you want to protect.
diff --git a/doc/ci/examples/deployment/index.md b/doc/ci/examples/deployment/index.md
index b083dbb8177..b097f1fac76 100644
--- a/doc/ci/examples/deployment/index.md
+++ b/doc/ci/examples/deployment/index.md
@@ -47,6 +47,7 @@ staging:
script:
- gem install dpl
- dpl --provider=heroku --app=my-app-staging --api_key=$HEROKU_STAGING_API_KEY
+ environment: staging
```
In the above example we use Dpl to deploy `my-app-staging` to Heroku server with API key stored in `HEROKU_STAGING_API_KEY` secure variable.
@@ -70,6 +71,7 @@ staging:
- dpl --provider=heroku --app=my-app-staging --api_key=$HEROKU_STAGING_API_KEY
only:
- main
+ environment: staging
```
The first line `apt-get update -yq` updates the list of available packages,
@@ -93,6 +95,7 @@ staging:
- dpl --provider=heroku --app=my-app-staging --api_key=$HEROKU_STAGING_API_KEY
only:
- main
+ environment: staging
production:
stage: deploy
@@ -101,6 +104,7 @@ production:
- dpl --provider=heroku --app=my-app-production --api_key=$HEROKU_PRODUCTION_API_KEY
only:
- tags
+ environment: production
```
We created two deploy jobs that are executed on different events:
@@ -117,7 +121,7 @@ We also use two secure variables:
To store API keys as secure variables:
-1. On the top bar, select **Menu > Projects** and find your project.
+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**.
diff --git a/doc/ci/examples/end_to_end_testing_webdriverio/index.md b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
index 4d247a4ff74..5ff99755242 100644
--- a/doc/ci/examples/end_to_end_testing_webdriverio/index.md
+++ b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
@@ -228,6 +228,7 @@ deploy_terraform:
script:
# Your Review App deployment scripts - for a working example please check https://gitlab.com/Flockademic/Flockademic/blob/5a45f1c2412e93810fab50e2dab8949e2d0633c7/.gitlab-ci.yml#L315
- echo
+ environment: production
e2e:firefox:
stage: confidence-check
services:
diff --git a/doc/ci/examples/php.md b/doc/ci/examples/php.md
index 666c4d444d8..9b9f87fffbb 100644
--- a/doc/ci/examples/php.md
+++ b/doc/ci/examples/php.md
@@ -176,7 +176,7 @@ Using phpenv also allows to easily configure the PHP environment with:
phpenv config-add my_config.ini
```
-*__Important note:__ It seems `phpenv/phpenv`
+**Important note:** It seems `phpenv/phpenv`
[is abandoned](https://github.com/phpenv/phpenv/issues/57). There is a fork
at [`madumlao/phpenv`](https://github.com/madumlao/phpenv) that tries to bring
the project back to life. [`CHH/phpenv`](https://github.com/CHH/phpenv) also
diff --git a/doc/ci/examples/semantic-release.md b/doc/ci/examples/semantic-release.md
index 2e9e2dd33bf..eaa7d8ebcfa 100644
--- a/doc/ci/examples/semantic-release.md
+++ b/doc/ci/examples/semantic-release.md
@@ -95,7 +95,7 @@ As part of publishing a package, semantic-release increases the version number i
1. Under **select scopes**, select the **api** checkbox.
1. Select **Create project access token**.
1. Copy the value.
-1. On the top bar, select **Menu > Projects** and find your project.
+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. Select **Add variable**.
diff --git a/doc/ci/index.md b/doc/ci/index.md
index 80752830b85..be7088ab153 100644
--- a/doc/ci/index.md
+++ b/doc/ci/index.md
@@ -146,36 +146,30 @@ See also the [Why CI/CD?](https://docs.google.com/presentation/d/1OGgk2Tcxbpl7DJ
As GitLab CI/CD has evolved, certain breaking changes have
been necessary.
-#### 14.0
-
-- No breaking changes.
-
-#### 13.0
-
-- [Remove Backported `os.Expand`](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4915).
-- [Remove Fedora 29 package support](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/16158).
-- [Remove macOS 32-bit support](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/25466).
-- [Removed `debug/jobs/list?v=1` endpoint](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/6361).
-- [Remove support for array of strings when defining services for Docker executor](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4922).
-- [Remove `--docker-services` flag on register command](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/6404).
-- [Remove legacy build directory caching](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4180).
-- [Remove `FF_USE_LEGACY_VOLUMES_MOUNTING_ORDER` feature flag](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/6581).
-- [Remove support for Windows Server 1803](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/6553).
-
-#### 12.0
-
-- [Use `refspec` to clone/fetch Git repository](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4069).
-- [Old cache configuration](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4070).
-- [Old metrics server configuration](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4072).
-- [Remove `FF_K8S_USE_ENTRYPOINT_OVER_COMMAND`](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4073).
-- [Remove Linux distributions that reach EOL](https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/1130).
-- [Update command line API for helper images](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4013).
-- [Remove old `git clean` flow](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4175).
-
-#### 11.0
-
-- No breaking changes.
-
-#### 10.0
-
-- No breaking changes.
+For GitLab 15.0 and later, all breaking changes are documented on the following pages:
+
+- [Deprecations](../update/deprecations.md)
+- [Removals](../update/removals.md)
+
+The breaking changes for [GitLab Runner](https://docs.gitlab.com/runner/) in earlier
+major version releases are:
+
+- 14.0: No breaking changes.
+- 13.0:
+ - [Remove Backported `os.Expand`](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4915).
+ - [Remove Fedora 29 package support](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/16158).
+ - [Remove macOS 32-bit support](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/25466).
+ - [Removed `debug/jobs/list?v=1` endpoint](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/6361).
+ - [Remove support for array of strings when defining services for Docker executor](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4922).
+ - [Remove `--docker-services` flag on register command](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/6404).
+ - [Remove legacy build directory caching](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4180).
+ - [Remove `FF_USE_LEGACY_VOLUMES_MOUNTING_ORDER` feature flag](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/6581).
+ - [Remove support for Windows Server 1803](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/6553).
+- 12.0:
+ - [Use `refspec` to clone/fetch Git repository](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4069).
+ - [Old cache configuration](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4070).
+ - [Old metrics server configuration](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4072).
+ - [Remove `FF_K8S_USE_ENTRYPOINT_OVER_COMMAND`](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4073).
+ - [Remove Linux distributions that reach EOL](https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/1130).
+ - [Update command line API for helper images](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4013).
+ - [Remove old `git clean` flow](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4175).
diff --git a/doc/ci/interactive_web_terminal/index.md b/doc/ci/interactive_web_terminal/index.md
index e6a9f1fa646..03c905184cf 100644
--- a/doc/ci/interactive_web_terminal/index.md
+++ b/doc/ci/interactive_web_terminal/index.md
@@ -18,7 +18,7 @@ taken to protect the users.
NOTE:
[Shared runners on GitLab.com](../runners/index.md) do not
-provide an interactive web terminal. Follow
+provide an interactive web terminal. Follow
[this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/24674) for progress on
adding support. For groups and projects hosted on GitLab.com, interactive web
terminals are available when using your own group or project runner.
@@ -27,7 +27,7 @@ terminals are available when using your own group or project runner.
Two things need to be configured for the interactive web terminal to work:
-- The runner needs to have
+- The runner needs to have
[`[session_server]` configured properly](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-session_server-section)
- If you are using a reverse proxy with your GitLab instance, web terminals need to be
[enabled](../../administration/integration/terminal.md#enabling-and-disabling-terminal-support)
@@ -54,7 +54,7 @@ Not all executors are
NOTE:
The `docker` executor does not keep running
after the build script is finished. At that point, the terminal automatically
-disconnects and does not wait for the user to finish. Please follow
+disconnects and does not wait for the user to finish. Please follow
[this issue](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/3605) for updates on
improving this behavior.
diff --git a/doc/ci/jobs/ci_job_token.md b/doc/ci/jobs/ci_job_token.md
index 93f22da648a..812683ef2c1 100644
--- a/doc/ci/jobs/ci_job_token.md
+++ b/doc/ci/jobs/ci_job_token.md
@@ -24,12 +24,6 @@ You can use a GitLab CI/CD job token to authenticate with specific API endpoints
- [Releases](../../api/releases/index.md) and [Release links](../../api/releases/links.md).
- [Terraform plan](../../user/infrastructure/index.md).
-NOTE:
-There's an open issue,
-[GitLab-#333444](https://gitlab.com/gitlab-org/gitlab/-/issues/333444),
-which prevents you from using a job token with internal projects. This bug only impacts self-managed
-GitLab instances.
-
The token has the same permissions to access the API as the user that caused the
job to run. A user can cause a job to run by pushing a commit, triggering a manual job,
being the owner of a scheduled pipeline, and so on. Therefore, this user must be assigned to
@@ -95,7 +89,7 @@ The job token scope is only for controlling access to private projects.
### Configure the job token scope limit
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Token Access**.
1. Toggle **Limit CI_JOB_TOKEN access** to enabled.
@@ -121,6 +115,7 @@ trigger_pipeline:
- curl --request POST --form "token=$CI_JOB_TOKEN" --form ref=main "https://gitlab.example.com/api/v4/projects/9/trigger/pipeline"
rules:
- if: $CI_COMMIT_TAG
+ environment: production
```
If you use the `CI_PIPELINE_SOURCE` [predefined CI/CD variable](../variables/predefined_variables.md)
diff --git a/doc/ci/jobs/index.md b/doc/ci/jobs/index.md
index fd6afb1a0ad..806837e3dc8 100644
--- a/doc/ci/jobs/index.md
+++ b/doc/ci/jobs/index.md
@@ -47,7 +47,7 @@ Clicking an individual job shows you its job log, and allows you to:
To view the full list of jobs that ran in a project:
-1. On the top bar, select **Menu > Projects** and find the project.
+1. On the top bar, select **Main menu > Projects** and find the project.
1. On the left sidebar, select **CI/CD > Jobs**.
You can filter the list by [job status](#the-order-of-jobs-in-a-pipeline).
diff --git a/doc/ci/jobs/job_control.md b/doc/ci/jobs/job_control.md
index 0d5357e63ad..5a94c2e9bbc 100644
--- a/doc/ci/jobs/job_control.md
+++ b/doc/ci/jobs/job_control.md
@@ -243,8 +243,8 @@ check the value of the `$CI_PIPELINE_SOURCE` variable:
| `external` | When you use CI services other than GitLab. |
| `external_pull_request_event` | When an external pull request on GitHub is created or updated. See [Pipelines for external pull requests](../ci_cd_for_external_repos/index.md#pipelines-for-external-pull-requests). |
| `merge_request_event` | For pipelines created when a merge request is created or updated. Required to enable [merge request pipelines](../pipelines/merge_request_pipelines.md), [merged results pipelines](../pipelines/merged_results_pipelines.md), and [merge trains](../pipelines/merge_trains.md). |
-| `parent_pipeline` | For pipelines triggered by a [parent/child pipeline](../pipelines/parent_child_pipelines.md) with `rules`. Use this pipeline source in the child pipeline configuration so that it can be triggered by the parent pipeline. |
-| `pipeline` | For [multi-project pipelines](../pipelines/multi_project_pipelines.md) created by [using the API with `CI_JOB_TOKEN`](../pipelines/multi_project_pipelines.md#create-multi-project-pipelines-by-using-the-api), or the [`trigger`](../yaml/index.md#trigger) keyword. |
+| `parent_pipeline` | For pipelines triggered by a [parent/child pipeline](../pipelines/downstream_pipelines.md#parent-child-pipelines) with `rules`. Use this pipeline source in the child pipeline configuration so that it can be triggered by the parent pipeline. |
+| `pipeline` | For [multi-project pipelines](../pipelines/downstream_pipelines.md#multi-project-pipelines) created by [using the API with `CI_JOB_TOKEN`](../pipelines/downstream_pipelines.md#trigger-a-multi-project-pipeline-by-using-the-api), or the [`trigger`](../yaml/index.md#trigger) keyword. |
| `push` | For pipelines triggered by a `git push` event, including for branches and tags. |
| `schedule` | For [scheduled pipelines](../pipelines/schedules.md). |
| `trigger` | For pipelines created by using a [trigger token](../triggers/index.md#configure-cicd-jobs-to-run-in-triggered-pipelines). |
@@ -578,6 +578,8 @@ To run a manual job, you must have permission to merge to the assigned branch:
or deployment view.
1. Next to the manual job, select **Play** (**{play}**).
+You can also [add custom CI/CD variables when running a manual job](index.md#specifying-variables-when-running-manual-jobs).
+
### Protect manual jobs **(PREMIUM)**
Use [protected environments](../environments/protected_environments.md)
@@ -645,6 +647,7 @@ timed rollout 10%:
script: echo 'Rolling out 10% ...'
when: delayed
start_in: 30 minutes
+ environment: production
```
To stop the active timer of a delayed job, select **Unschedule** (**{time-out}**).
@@ -698,6 +701,7 @@ deploystacks:
parallel:
matrix:
- PROVIDER: [aws, ovh, gcp, vultr]
+ environment: production/$PROVIDER
```
You can also [create a multi-dimensional matrix](../yaml/index.md#parallelmatrix).
@@ -722,6 +726,7 @@ deploystacks:
STACK: [monitoring, backup]
- PROVIDER: [gcp, vultr]
STACK: [data]
+ environment: $PROVIDER/$STACK
```
This example generates 6 parallel `deploystacks` trigger jobs, each with different values
@@ -754,6 +759,7 @@ deploystacks:
STACK: [data]
tags:
- ${PROVIDER}-${STACK}
+ environment: $PROVIDER/$STACK
```
#### Fetch artifacts from a `parallel:matrix` job
@@ -784,6 +790,7 @@ deploy:
dependencies:
- "ruby: [2.7, aws]"
script: echo hello
+ environment: production
```
Quotes around the `dependencies` entry are required.
@@ -957,6 +964,33 @@ For example:
Pattern matching is case-sensitive by default. Use the `i` flag modifier to make a
pattern case-insensitive. For example: `/pattern/i`.
+#### Store the regex pattern in a variable
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35438) in GitLab 15.0 [with a flag](../../administration/feature_flags.md) named `ci_fix_rules_if_comparison_with_regexp_variable`, disabled by default.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/359740) and feature flag `ci_fix_rules_if_comparison_with_regexp_variable` removed in GitLab 15.1.
+
+Variables on the right side of `=~` and `!~` expressions are evaluated as regular expressions.
+The regular expression must be enclosed in forward slashes (`/`). For example:
+
+```yaml
+variables:
+ pattern: '/^ab.*/'
+
+regex-job1:
+ variables:
+ teststring: 'abcde'
+ script: echo "This job will run, because 'abcde' matches the /^ab.*/ pattern."
+ rules:
+ - if: '$teststring =~ $pattern'
+
+regex-job2:
+ variables:
+ teststring: 'fghij'
+ script: echo "This job will not run, because 'fghi' does not match the /^ab.*/ pattern."
+ rules:
+ - if: '$teststring =~ $pattern'
+```
+
### Join variable expressions together with `&&` or `||`
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/62867) in GitLab 12.0
diff --git a/doc/ci/lint.md b/doc/ci/lint.md
index 0811cc87e91..8c64d968b8b 100644
--- a/doc/ci/lint.md
+++ b/doc/ci/lint.md
@@ -24,7 +24,7 @@ configuration added with the [`includes` keyword](yaml/index.md#include).
To check CI/CD configuration with the CI lint tool:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **CI/CD > Pipelines**.
1. In the top right, select **CI lint**.
1. Paste a copy of the CI/CD configuration you want to check into the text box.
@@ -45,7 +45,7 @@ Prerequisites:
To simulate a pipeline:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **CI/CD > Pipelines**.
1. In the top right, select **CI lint**.
1. Paste a copy of the CI/CD configuration you want to check into the text box.
diff --git a/doc/ci/migration/circleci.md b/doc/ci/migration/circleci.md
index 7255d9aec82..efe11466674 100644
--- a/doc/ci/migration/circleci.md
+++ b/doc/ci/migration/circleci.md
@@ -136,6 +136,7 @@ job3:
job4:
stage: deploy
script: make deploy
+ environment: production
```
#### Scheduled run
@@ -196,6 +197,7 @@ deploy_prod:
script:
- echo "Deploy to production server"
when: manual
+ environment: production
```
### Filter job by branch
@@ -222,6 +224,7 @@ deploy:
- echo "Deploy job"
rules:
- if: $CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH =~ /^rc-/
+ environment: production
```
### Caching
diff --git a/doc/ci/migration/jenkins.md b/doc/ci/migration/jenkins.md
index c59116ea8ed..35c5a7e56c8 100644
--- a/doc/ci/migration/jenkins.md
+++ b/doc/ci/migration/jenkins.md
@@ -190,7 +190,7 @@ pdf:
Additionally, we have package management features like built-in container and package registries that you
can leverage. You can see the complete list of packaging features in the
-[Packages & Registries](../../user/packages/index.md) documentation.
+[Packages and registries](../../user/packages/index.md) documentation.
## Integrated features
diff --git a/doc/ci/mobile_devops.md b/doc/ci/mobile_devops.md
new file mode 100644
index 00000000000..6eb56434a1b
--- /dev/null
+++ b/doc/ci/mobile_devops.md
@@ -0,0 +1,42 @@
+---
+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/engineering/ux/technical-writing/#assignments
+type: reference
+---
+
+# Mobile DevOps
+
+GitLab Mobile DevOps is a collection of features and tools designed for mobile developers
+and teams to automate their build and release process using GitLab CI/CD. Mobile DevOps
+is an experimental feature developed by [GitLab Incubation Engineering](https://about.gitlab.com/handbook/engineering/incubation/).
+
+Mobile DevOps is still in development, but you can:
+
+- [Request a feature](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/feedback/-/issues/new?issuable_template=feature_request).
+- [Report a bug](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/feedback/-/issues/new?issuable_template=report_bug).
+- [Share feedback](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/feedback/-/issues/new?issuable_template=general_feedback).
+
+## Code Signing
+
+[Project-level Secure Files](secure_files/index.md) makes it easier to manage key stores, provision profiles,
+and signing certificates directly in a GitLab project.
+
+For a guided walkthrough of this feature, watch the [video demo](https://youtu.be/O7FbJu3H2YM).
+
+## Review Apps for Mobile
+
+You can use [Review Apps](review_apps/index.md) to preview changes directly from a merge request.
+Review Apps for Mobile brings that capability to mobile developers through an integration
+with [Appetize](https://appetize.io/).
+
+Watch a [video walkthrough](https://youtu.be/X15mI19TXa4) of this feature, or visit the
+[setup instructions](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/readme/-/issues/15)
+to get started.
+
+## Mobile SAST
+
+You can use [Static Application Security Testing (SAST)](../user/application_security/sast/index.md)
+to run static analyzers on code to check for known security vulnerabilities. Mobile SAST
+expands this functionality for mobile teams with an [experimental SAST feature](../user/application_security/sast/index.md#experimental-features)
+based on [Mobile Security Framework (MobSF)](https://github.com/MobSF/Mobile-Security-Framework-MobSF).
diff --git a/doc/ci/pipeline_editor/index.md b/doc/ci/pipeline_editor/index.md
index 0fd8fac7741..87c2b3f1c71 100644
--- a/doc/ci/pipeline_editor/index.md
+++ b/doc/ci/pipeline_editor/index.md
@@ -160,15 +160,23 @@ checkbox appears. Select it to start a new merge request after you commit the ch
### `Configuration validation currently not available` message
-This message is due to a problem with the syntax validation in the pipeline editor.
-If GitLab is unable to communicate with the service that validates the syntax, the
-information in these sections may not display properly:
-
-- The syntax status on the **Edit** tab (valid or invalid).
-- The **Visualize** tab.
-- The **Lint** tab.
-- The **View merged YAML** tab.
-
-You can still work on your CI/CD configuration and commit the changes you made without
-any issues. As soon as the service becomes available again, the syntax validation
-should display immediately.
+This message is caused by a problem validating the syntax in the pipeline editor.
+It can happen when:
+
+- GitLab is unable to communicate with the service that validates the syntax, so the
+ information in these sections may not display properly:
+
+ - The syntax status on the **Edit** tab (valid or invalid).
+ - The **Visualize** tab.
+ - The **Lint** tab.
+ - The **View merged YAML** tab.
+
+ You can still work on your CI/CD configuration and commit the changes you made without
+ any issues. As soon as the service becomes available again, the syntax validation
+ should display immediately.
+
+- Using [`include`](../yaml/index.md#include), but the included configuration files create a loop.
+ For example, `.gitlab-ci.yml` includes `file1.yml`, which includes `file2.yml`,
+ which includes `file1.yml`, creating a loop between `file1.yml` and `file2.yml`.
+
+ Remove one of the `include` lines to eliminate the loop and resolve the issue.
diff --git a/doc/ci/pipelines/cicd_minutes.md b/doc/ci/pipelines/cicd_minutes.md
index 4b7d3845361..02a74883244 100644
--- a/doc/ci/pipelines/cicd_minutes.md
+++ b/doc/ci/pipelines/cicd_minutes.md
@@ -49,7 +49,7 @@ Prerequisite:
To change the default quota that applies to all namespaces:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Continuous Integration and Deployment**.
1. In the **Quota of CI/CD minutes** box, enter the maximum number of CI/CD minutes.
@@ -70,7 +70,7 @@ Prerequisite:
To set a quota of CI/CD minutes for a namespace:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Groups**.
1. For the group you want to update, select **Edit**.
1. In the **Quota of CI/CD minutes** box, enter the maximum number of CI/CD minutes.
@@ -95,7 +95,7 @@ Prerequisite:
To view CI/CD minutes being used for your group:
-1. On the top bar, select **Menu > Groups** and find your group. The group must not be a subgroup.
+1. On the top bar, select **Main menu > Groups** and find your group. The group must not be a subgroup.
1. On the left sidebar, select **Settings > Usage Quotas**.
1. Select the **Pipelines** tab.
@@ -148,7 +148,7 @@ You can purchase additional CI/CD minutes for your group.
You cannot transfer purchased CI/CD minutes from one group to another,
so be sure to select the correct group.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > Usage Quotas**.
1. Select **Pipelines**.
1. Select **Buy additional minutes**.
@@ -201,9 +201,12 @@ can be higher than the end-to-end duration of a pipeline.
The cost factors for jobs running on shared runners on GitLab.com are:
-- `0.008` for public projects, and projects in the [GitLab for Open Source program](../../subscriptions/index.md#gitlab-for-open-source).
- For every 125 minutes of job execution time, you use 1 CI/CD minute.
- `1` for internal and private projects.
+- `0.5` for public projects in the [GitLab for Open Source program](../../subscriptions/index.md#gitlab-for-open-source).
+- `0.008` for public forks of public projects in the [GitLab for Open Source program](../../subscriptions/index.md#gitlab-for-open-source). For every 125 minutes of job execution time,
+ you use 1 CI/CD minute.
+- `0.04` for other public projects, after September 1, 2022 (previously `0.008`).
+ For every 25 minutes of job execution time, you use 1 CI/CD minute.
- Calculated differently for [community contributions to GitLab projects](#cost-factor-for-community-contributions-to-gitlab-projects).
The cost factors on self-managed instances are:
@@ -236,12 +239,14 @@ GitLab administrators can add a namespace to the reduced cost factor
### Additional costs on GitLab SaaS
-GitLab SaaS shared runners have different cost factors, depending on the runner type (Linux, Windows, macOS) and the virtual machine configuration.
+GitLab SaaS runners have different cost factors, depending on the runner type (Linux, Windows, macOS) and the virtual machine configuration.
-| GitLab SaaS runner type | Virtual machine configuration | CI/CD minutes cost factor |
+| GitLab SaaS runner type | Machine Type | CI/CD minutes cost factor |
| :--------- | :------------------- | :--------- |
-| Linux OS + Docker executor| 1 vCPU, 3.75 GB RAM |1|
-| macOS + shell executor | 4 vCPU, 10 GB RAM| 6 |
+| Linux OS + Docker executor| Small |1|
+| Linux OS + Docker executor| Medium |2|
+| Linux OS + Docker executor| Large |3|
+| macOS + shell executor | Large| 6 |
### Monthly reset of CI/CD minutes
diff --git a/doc/ci/pipelines/downstream_pipelines.md b/doc/ci/pipelines/downstream_pipelines.md
new file mode 100644
index 00000000000..6c7ec8e98ec
--- /dev/null
+++ b/doc/ci/pipelines/downstream_pipelines.md
@@ -0,0 +1,647 @@
+---
+stage: Verify
+group: Pipeline Authoring
+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
+---
+
+# Downstream pipelines **(FREE)**
+
+A downstream pipeline is any GitLab CI/CD pipeline triggered by another pipeline.
+A downstream pipeline can be either:
+
+- A [parent-child pipeline](downstream_pipelines.md#parent-child-pipelines), which is a downstream pipeline triggered
+ in the same project as the first pipeline.
+- A [multi-project pipeline](#multi-project-pipelines), which is a downstream pipeline triggered
+ in a different project than the first pipeline.
+
+Parent-child pipelines and multi-project pipelines can sometimes be used for similar purposes,
+but there are some key differences.
+
+Parent-child pipelines:
+
+- Run under the same project, ref, and commit SHA as the parent pipeline.
+- Affect the overall status of the ref the pipeline runs against. For example,
+ if a pipeline fails for the main branch, it's common to say that "main is broken".
+ The status of child pipelines don't directly affect the status of the ref, unless the child
+ pipeline is triggered with [`strategy:depend`](../yaml/index.md#triggerstrategy).
+- Are automatically canceled if the pipeline is configured with [`interruptible`](../yaml/index.md#interruptible)
+ when a new pipeline is created for the same ref.
+- Display only the parent pipelines in the pipeline index page. Child pipelines are
+ visible when visiting their parent pipeline's page.
+- Are limited to 2 levels of nesting. A parent pipeline can trigger multiple child pipelines,
+ and those child pipeline can trigger multiple child pipelines (`A -> B -> C`).
+
+Multi-project pipelines:
+
+- Are triggered from another pipeline, but the upstream (triggering) pipeline does
+ not have much control over the downstream (triggered) pipeline. However, it can
+ choose the ref of the downstream pipeline, and pass CI/CD variables to it.
+- Affect the overall status of the ref of the project it runs in, but does not
+ affect the status of the triggering pipeline's ref, unless it was triggered with
+ [`strategy:depend`](../yaml/index.md#triggerstrategy).
+- Are not automatically canceled in the downstream project when using [`interruptible`](../yaml/index.md#interruptible)
+ if a new pipeline runs for the same ref in the upstream pipeline. They can be
+ automatically canceled if a new pipeline is triggered for the same ref on the downstream project.
+- Multi-project pipelines are standalone pipelines because they are normal pipelines
+ that happened to be triggered by an external project. They are all visible on the pipeline index page.
+- Are independent, so there are no nesting limits.
+
+## Multi-project pipelines
+
+> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/199224) to GitLab Free in 12.8.
+
+You can set up [GitLab CI/CD](../index.md) across multiple projects, so that a pipeline
+in one project can trigger a downstream pipeline in another project. You can visualize the entire pipeline
+in one place, including all cross-project interdependencies.
+
+For example, you might deploy your web application from three different projects in GitLab.
+Each project has its own build, test, and deploy process. With multi-project pipelines you can
+visualize the entire pipeline, including all build and test stages for all three projects.
+
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+For an overview, see the [Multi-project pipelines demo](https://www.youtube.com/watch?v=g_PIwBM1J84).
+
+Multi-project pipelines are also useful for larger products that require cross-project interdependencies, like those
+with a [microservices architecture](https://about.gitlab.com/blog/2016/08/16/trends-in-version-control-land-microservices/).
+Learn more in the [Cross-project Pipeline Triggering and Visualization demo](https://about.gitlab.com/learn/)
+at GitLab@learn, in the Continuous Integration section.
+
+If you trigger a pipeline in a downstream private project, on the upstream project's pipelines page,
+you can view:
+
+- The name of the project.
+- The status of the pipeline.
+
+If you have a public project that can trigger downstream pipelines in a private project,
+make sure there are no confidentiality problems.
+
+### Trigger a multi-project pipeline from a job in your `.gitlab-ci.yml` file
+
+> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/199224) to GitLab Free in 12.8.
+
+When you use the [`trigger`](../yaml/index.md#trigger) keyword to create a multi-project
+pipeline in your `.gitlab-ci.yml` file, you create what is called a *trigger job*. For example:
+
+```yaml
+rspec:
+ stage: test
+ script: bundle exec rspec
+
+staging:
+ variables:
+ ENVIRONMENT: staging
+ stage: deploy
+ trigger: my/deployment
+```
+
+In this example, after the `rspec` job succeeds in the `test` stage,
+the `staging` trigger job starts. The initial status of this
+job is `pending`.
+
+GitLab then creates a downstream pipeline in the
+`my/deployment` project and, as soon as the pipeline is created, the
+`staging` job succeeds. The full path to the project is `my/deployment`.
+
+You can view the status for the pipeline, or you can display
+[the downstream pipeline's status instead](#mirror-the-status-of-a-downstream-pipeline-in-the-trigger-job).
+
+The user that creates the upstream pipeline must be able to create pipelines in the
+downstream project (`my/deployment`) too. If the downstream project is not found,
+or the user does not have [permission](../../user/permissions.md) to create a pipeline there,
+the `staging` job is marked as _failed_.
+
+#### Specify a downstream pipeline branch
+
+You can specify a branch name for the downstream pipeline to use.
+GitLab uses the commit on the head of the branch to
+create the downstream pipeline.
+
+```yaml
+rspec:
+ stage: test
+ script: bundle exec rspec
+
+staging:
+ stage: deploy
+ trigger:
+ project: my/deployment
+ branch: stable-11-2
+```
+
+Use:
+
+- The `project` keyword to specify the full path to a downstream project.
+ In [GitLab 15.3 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/367660), variable expansion is
+ supported.
+- The `branch` keyword to specify the name of a branch in the project specified by `project`.
+ In [GitLab 12.4 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/10126), variable expansion is
+ supported.
+
+Pipelines triggered on a protected branch in a downstream project use the [role](../../user/permissions.md)
+of the user that ran the trigger job in the upstream project. If the user does not
+have permission to run CI/CD pipelines against the protected branch, the pipeline fails. See
+[pipeline security for protected branches](index.md#pipeline-security-on-protected-branches).
+
+#### Use `rules` or `only`/`except` with multi-project pipelines
+
+You can use CI/CD variables or the [`rules`](../yaml/index.md#rulesif) keyword to
+[control job behavior](../jobs/job_control.md) for multi-project pipelines. When a
+downstream pipeline is triggered with the [`trigger`](../yaml/index.md#trigger) keyword,
+the value of the [`$CI_PIPELINE_SOURCE` predefined variable](../variables/predefined_variables.md)
+is `pipeline` for all its jobs.
+
+If you use [`only/except`](../yaml/index.md#only--except) to control job behavior, use the
+[`pipelines`](../yaml/index.md#onlyrefs--exceptrefs) keyword.
+
+### Trigger a multi-project pipeline by using the API
+
+> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/31573) to GitLab Free in 12.4.
+
+When you use the [`CI_JOB_TOKEN` to trigger pipelines](../jobs/ci_job_token.md),
+GitLab recognizes the source of the job token. The pipelines become related,
+so you can visualize their relationships on pipeline graphs.
+
+These relationships are displayed in the pipeline graph by showing inbound and
+outbound connections for upstream and downstream pipeline dependencies.
+
+When using:
+
+- CI/CD variables or [`rules`](../yaml/index.md#rulesif) to control job behavior, the value of
+ the [`$CI_PIPELINE_SOURCE` predefined variable](../variables/predefined_variables.md) is
+ `pipeline` for multi-project pipeline triggered through the API with `CI_JOB_TOKEN`.
+- [`only/except`](../yaml/index.md#only--except) to control job behavior, use the
+ `pipelines` keyword.
+
+## Parent-child pipelines
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16094) in GitLab 12.7.
+
+As pipelines grow more complex, a few related problems start to emerge:
+
+- The staged structure, where all steps in a stage must be completed before the first
+ job in next stage begins, causes arbitrary waits, slowing things down.
+- Configuration for the single global pipeline becomes very long and complicated,
+ making it hard to manage.
+- Imports with [`include`](../yaml/index.md#include) increase the complexity of the configuration, and create the potential
+ for namespace collisions where jobs are unintentionally duplicated.
+- Pipeline UX can become unwieldy with so many jobs and stages to work with.
+
+Additionally, sometimes the behavior of a pipeline needs to be more dynamic. The ability
+to choose to start sub-pipelines (or not) is a powerful ability, especially if the
+YAML is dynamically generated.
+
+![Parent pipeline graph expanded](img/parent_pipeline_graph_expanded_v14_3.png)
+
+Similarly to [multi-project pipelines](#multi-project-pipelines), a pipeline can trigger a
+set of concurrently running downstream child pipelines, but in the same project:
+
+- Child pipelines still execute each of their jobs according to a stage sequence, but
+ would be free to continue forward through their stages without waiting for unrelated
+ jobs in the parent pipeline to finish.
+- The configuration is split up into smaller child pipeline configurations. Each child pipeline contains only relevant steps which are
+ easier to understand. This reduces the cognitive load to understand the overall configuration.
+- Imports are done at the child pipeline level, reducing the likelihood of collisions.
+
+Child pipelines work well with other GitLab CI/CD features:
+
+- Use [`rules: changes`](../yaml/index.md#ruleschanges) to trigger pipelines only when
+ certain files change. This is useful for monorepos, for example.
+- Since the parent pipeline in `.gitlab-ci.yml` and the child pipeline run as normal
+ pipelines, they can have their own behaviors and sequencing in relation to triggers.
+
+See the [`trigger`](../yaml/index.md#trigger) keyword documentation for full details on how to
+include the child pipeline configuration.
+
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+For an overview, see [Parent-Child Pipelines feature demo](https://youtu.be/n8KpBSqZNbk).
+
+NOTE:
+The artifact containing the generated YAML file must not be larger than 5MB.
+
+### Trigger a parent-child pipeline
+
+The simplest case is [triggering a child pipeline](../yaml/index.md#trigger) using a
+local YAML file to define the pipeline configuration. In this case, the parent pipeline
+triggers the child pipeline, and continues without waiting:
+
+```yaml
+microservice_a:
+ trigger:
+ include: path/to/microservice_a.yml
+```
+
+You can include multiple files when defining a child pipeline. The child pipeline's
+configuration is composed of all configuration files merged together:
+
+```yaml
+microservice_a:
+ trigger:
+ include:
+ - local: path/to/microservice_a.yml
+ - template: Security/SAST.gitlab-ci.yml
+```
+
+In [GitLab 13.5 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/205157),
+you can use [`include:file`](../yaml/index.md#includefile) to trigger child pipelines
+with a configuration file in a different project:
+
+```yaml
+microservice_a:
+ trigger:
+ include:
+ - project: 'my-group/my-pipeline-library'
+ ref: 'main'
+ file: '/path/to/child-pipeline.yml'
+```
+
+The maximum number of entries that are accepted for `trigger:include` is three.
+
+### Merge request child pipelines
+
+To trigger a child pipeline as a [merge request pipeline](merge_request_pipelines.md) we need to:
+
+- Set the trigger job to run on merge requests:
+
+```yaml
+# parent .gitlab-ci.yml
+microservice_a:
+ trigger:
+ include: path/to/microservice_a.yml
+ rules:
+ - if: $CI_MERGE_REQUEST_ID
+```
+
+- Configure the child pipeline by either:
+
+ - Setting all jobs in the child pipeline to evaluate in the context of a merge request:
+
+ ```yaml
+ # child path/to/microservice_a.yml
+ workflow:
+ rules:
+ - if: $CI_MERGE_REQUEST_ID
+
+ job1:
+ script: ...
+
+ job2:
+ script: ...
+ ```
+
+ - Alternatively, setting the rule per job. For example, to create only `job1` in
+ the context of merge request pipelines:
+
+ ```yaml
+ # child path/to/microservice_a.yml
+ job1:
+ script: ...
+ rules:
+ - if: $CI_MERGE_REQUEST_ID
+
+ job2:
+ script: ...
+ ```
+
+### Dynamic child pipelines
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35632) in GitLab 12.9.
+
+Instead of running a child pipeline from a static YAML file, you can define a job that runs
+your own script to generate a YAML file, which is then used to trigger a child pipeline.
+
+This technique can be very powerful in generating pipelines targeting content that changed or to
+build a matrix of targets and architectures.
+
+<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).
+
+We also have an example project using
+[Dynamic Child Pipelines with Jsonnet](https://gitlab.com/gitlab-org/project-templates/jsonnet)
+which shows how to use a data templating language to generate your `.gitlab-ci.yml` at runtime.
+You could use a similar process for other templating languages like
+[Dhall](https://dhall-lang.org/) or [ytt](https://get-ytt.io/).
+
+The artifact path is parsed by GitLab, not the runner, so the path must match the
+syntax for the OS running GitLab. If GitLab is running on Linux but using a Windows
+runner for testing, the path separator for the trigger job would be `/`. Other CI/CD
+configuration for jobs, like scripts, that use the Windows runner would use `\`.
+
+For example, to trigger a child pipeline from a dynamically generated configuration file:
+
+```yaml
+generate-config:
+ stage: build
+ script: generate-ci-config > generated-config.yml
+ artifacts:
+ paths:
+ - generated-config.yml
+
+child-pipeline:
+ stage: test
+ trigger:
+ include:
+ - artifact: generated-config.yml
+ job: generate-config
+```
+
+The `generated-config.yml` is extracted from the artifacts and used as the configuration
+for triggering the child pipeline.
+
+In GitLab 12.9, the child pipeline could fail to be created in certain cases, causing the parent pipeline to fail.
+This is [resolved](https://gitlab.com/gitlab-org/gitlab/-/issues/209070) in GitLab 12.10.
+
+### Nested child pipelines
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/29651) in GitLab 13.4.
+> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/243747) in GitLab 13.5.
+
+Parent and child pipelines were introduced with a maximum depth of one level of child
+pipelines, which was later increased to two. A parent pipeline can trigger many child
+pipelines, and these child pipelines can trigger their own child pipelines. It's not
+possible to trigger another level of child pipelines.
+
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+For an overview, see [Nested Dynamic Pipelines](https://youtu.be/C5j3ju9je2M).
+
+## View a downstream pipeline
+
+In the [pipeline graph view](index.md#view-full-pipeline-graph), downstream pipelines display
+as a list of cards on the right of the graph.
+
+### Retry a downstream pipeline
+
+> - Retry from graph view [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/354974) in GitLab 15.0 [with a flag](../../administration/feature_flags.md) named `downstream_retry_action`. Disabled by default.
+> - Retry from graph view [generally available and feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/357406) in GitLab 15.1.
+
+To retry a completed downstream pipeline, select **Retry** (**{retry}**):
+
+- From the downstream pipeline's details page.
+- On the pipeline's card in the [pipeline graph view](index.md#view-full-pipeline-graph).
+
+### Cancel a downstream pipeline
+
+> - Retry from graph view [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/354974) in GitLab 15.0 [with a flag](../../administration/feature_flags.md) named `downstream_retry_action`. Disabled by default.
+> - Retry from graph view [generally available and feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/357406) in GitLab 15.1.
+
+To cancel a downstream pipeline that is still running, select **Cancel** (**{cancel}**):
+
+- From the downstream pipeline's details page.
+- On the pipeline's card in the [pipeline graph view](index.md#view-full-pipeline-graph).
+
+### Mirror the status of a downstream pipeline in the trigger job
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11238) in GitLab Premium 12.3.
+> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/199224) to GitLab Free in 12.8.
+
+You can mirror the pipeline status from the triggered pipeline to the source trigger job
+by using [`strategy: depend`](../yaml/index.md#triggerstrategy):
+
+::Tabs
+
+:::TabTitle Multi-project pipeline
+
+```yaml
+trigger_job:
+ trigger:
+ project: my/project
+ strategy: depend
+```
+
+:::TabTitle Parent-child pipeline
+
+```yaml
+trigger_job:
+ trigger:
+ include:
+ - local: path/to/child-pipeline.yml
+ strategy: depend
+```
+
+::EndTabs
+
+### View multi-project pipelines in pipeline graphs **(PREMIUM)**
+
+When you trigger a multi-project pipeline, the downstream pipeline displays
+to the right of the [pipeline graph](index.md#visualize-pipelines).
+
+![Multi-project pipeline graph](img/multi_project_pipeline_graph_v14_3.png)
+
+In [pipeline mini graphs](index.md#pipeline-mini-graphs), the downstream pipeline
+displays to the right of the mini graph.
+
+![Multi-project pipeline mini graph](img/pipeline_mini_graph_v15_0.png)
+
+## Pass artifacts to a downstream pipeline
+
+You can pass artifacts to a downstream pipeline by using [`needs:project`](../yaml/index.md#needsproject).
+
+1. In a job in the upstream pipeline, save the artifacts using the [`artifacts`](../yaml/index.md#artifacts) keyword.
+1. Trigger the downstream pipeline with a trigger job:
+
+ ```yaml
+ build_artifacts:
+ stage: build
+ script:
+ - echo "This is a test artifact!" >> artifact.txt
+ artifacts:
+ paths:
+ - artifact.txt
+
+ deploy:
+ stage: deploy
+ trigger: my/downstream_project
+ ```
+
+1. In a job in the downstream pipeline, fetch the artifacts from the upstream pipeline
+ by using `needs:project`. Set `job` to the job in the upstream pipeline to fetch artifacts from,
+ `ref` to the branch, and `artifacts: true`.
+
+ ```yaml
+ test:
+ stage: test
+ script:
+ - cat artifact.txt
+ needs:
+ - project: my/upstream_project
+ job: build_artifacts
+ ref: main
+ artifacts: true
+ ```
+
+### Pass artifacts from a Merge Request pipeline
+
+When you use `needs:project` to [pass artifacts to a downstream pipeline](#pass-artifacts-to-a-downstream-pipeline),
+the `ref` value is usually a branch name, like `main` or `development`.
+
+For merge request pipelines, the `ref` value is in the form of `refs/merge-requests/<id>/head`,
+where `id` is the merge request ID. You can retrieve this ref with the [`CI_MERGE_REQUEST_REF_PATH`](../variables/predefined_variables.md#predefined-variables-for-merge-request-pipelines)
+CI/CD variable. Do not use a branch name as the `ref` with merge request pipelines,
+because the downstream pipeline attempts to fetch artifacts from the latest branch pipeline.
+
+To fetch the artifacts from the upstream `merge request` pipeline instead of the `branch` pipeline,
+pass this variable to the downstream pipeline using variable inheritance:
+
+1. In a job in the upstream pipeline, save the artifacts using the [`artifacts`](../yaml/index.md#artifacts) keyword.
+1. In the job that triggers the downstream pipeline, pass the `$CI_MERGE_REQUEST_REF_PATH` variable by using
+ [variable inheritance](#pass-yaml-defined-cicd-variables):
+
+ ```yaml
+ build_artifacts:
+ stage: build
+ script:
+ - echo "This is a test artifact!" >> artifact.txt
+ artifacts:
+ paths:
+ - artifact.txt
+
+ upstream_job:
+ variables:
+ UPSTREAM_REF: $CI_MERGE_REQUEST_REF_PATH
+ trigger:
+ project: my/downstream_project
+ branch: my-branch
+ ```
+
+1. In a job in the downstream pipeline, fetch the artifacts from the upstream pipeline
+ by using `needs:project`. Set the `ref` to the `UPSTREAM_REF` variable, and `job`
+ to the job in the upstream pipeline to fetch artifacts from:
+
+ ```yaml
+ test:
+ stage: test
+ script:
+ - cat artifact.txt
+ needs:
+ - project: my/upstream_project
+ job: build_artifacts
+ ref: $UPSTREAM_REF
+ artifacts: true
+ ```
+
+This method works for fetching artifacts from a regular merge request parent pipeline,
+but fetching artifacts from [merge results](merged_results_pipelines.md) pipelines is not supported.
+
+## Pass CI/CD variables to a downstream pipeline
+
+You can pass CI/CD variables to a downstream pipeline with a few different methods,
+based on where the variable is created or defined.
+
+### Pass YAML-defined CI/CD variables
+
+You can use the `variables` keyword to pass CI/CD variables to a downstream pipeline,
+just like you would for any other job.
+
+For example, in a [multi-project pipeline](#multi-project-pipelines):
+
+```yaml
+rspec:
+ stage: test
+ script: bundle exec rspec
+
+staging:
+ variables:
+ ENVIRONMENT: staging
+ stage: deploy
+ trigger: my/deployment
+```
+
+The `ENVIRONMENT` variable is passed to every job defined in a downstream
+pipeline. It is available as a variable when GitLab Runner picks a job.
+
+In the following configuration, the `MY_VARIABLE` variable is passed to the downstream pipeline
+that is created when the `trigger-downstream` job is queued. This is because `trigger-downstream`
+job inherits variables declared in global variables blocks, and then we pass these variables to a downstream pipeline.
+
+```yaml
+variables:
+ MY_VARIABLE: my-value
+
+trigger-downstream:
+ variables:
+ ENVIRONMENT: something
+ trigger: my/project
+```
+
+### Prevent global variables from being passed
+
+You can stop global variables from reaching the downstream pipeline by using the [`inherit:variables` keyword](../yaml/index.md#inheritvariables).
+For example, in a [multi-project pipeline](#multi-project-pipelines):
+
+```yaml
+variables:
+ MY_GLOBAL_VAR: value
+
+trigger-downstream:
+ inherit:
+ variables: false
+ variables:
+ MY_LOCAL_VAR: value
+ trigger: my/project
+```
+
+In this example, the `MY_GLOBAL_VAR` variable is not available in the triggered pipeline.
+
+### Pass a predefined variable
+
+You might want to pass some information about the upstream pipeline using predefined variables.
+To do that, you can use interpolation to pass any variable. For example,
+in a [multi-project pipeline](#multi-project-pipelines):
+
+```yaml
+downstream-job:
+ variables:
+ UPSTREAM_BRANCH: $CI_COMMIT_REF_NAME
+ trigger: my/project
+```
+
+In this scenario, the `UPSTREAM_BRANCH` variable with the value of the upstream pipeline's
+`$CI_COMMIT_REF_NAME` is passed to `downstream-job`. It is available in the
+context of all downstream builds.
+
+You cannot use this method to forward [job-level persisted variables](../variables/where_variables_can_be_used.md#persisted-variables)
+to a downstream pipeline, as they are not available in trigger jobs.
+
+Upstream pipelines take precedence over downstream ones. If there are two
+variables with the same name defined in both upstream and downstream projects,
+the ones defined in the upstream project take precedence.
+
+### Pass dotenv variables created in a job **(PREMIUM)**
+
+You can pass variables to a downstream pipeline with [`dotenv` variable inheritance](../variables/index.md#pass-an-environment-variable-to-another-job)
+and [`needs:project`](../yaml/index.md#needsproject).
+
+For example, in a [multi-project pipeline](#multi-project-pipelines):
+
+1. Save the variables in a `.env` file.
+1. Save the `.env` file as a `dotenv` report.
+1. Trigger the downstream pipeline.
+
+ ```yaml
+ build_vars:
+ stage: build
+ script:
+ - echo "BUILD_VERSION=hello" >> build.env
+ artifacts:
+ reports:
+ dotenv: build.env
+
+ deploy:
+ stage: deploy
+ trigger: my/downstream_project
+ ```
+
+1. Set the `test` job in the downstream pipeline to inherit the variables from the `build_vars`
+ job in the upstream project with `needs`. The `test` job inherits the variables in the
+ `dotenv` report and it can access `BUILD_VERSION` in the script:
+
+ ```yaml
+ test:
+ stage: test
+ script:
+ - echo $BUILD_VERSION
+ needs:
+ - project: my/upstream_project
+ job: build_vars
+ ref: master
+ artifacts: true
+ ```
diff --git a/doc/ci/pipelines/img/downstream_pipeline_actions.png b/doc/ci/pipelines/img/downstream_pipeline_actions.png
deleted file mode 100644
index 4c4384bab57..00000000000
--- a/doc/ci/pipelines/img/downstream_pipeline_actions.png
+++ /dev/null
Binary files differ
diff --git a/doc/ci/pipelines/index.md b/doc/ci/pipelines/index.md
index 08264170d52..2696d3adabd 100644
--- a/doc/ci/pipelines/index.md
+++ b/doc/ci/pipelines/index.md
@@ -57,44 +57,10 @@ Pipelines can be configured in many different ways:
already been merged into the target branch.
- [Merge trains](../pipelines/merge_trains.md)
use merged results pipelines to queue merges one after the other.
-- [Parent-child pipelines](parent_child_pipelines.md) break down complex pipelines
+- [Parent-child pipelines](downstream_pipelines.md#parent-child-pipelines) break down complex pipelines
into one parent pipeline that can trigger multiple child sub-pipelines, which all
run in the same project and with the same SHA. This pipeline architecture is commonly used for mono-repos.
-- [Multi-project pipelines](multi_project_pipelines.md) combine pipelines for different projects together.
-
-### How parent-child pipelines compare to multi-project pipelines
-
-Parent-child pipelines and multi-project pipelines can sometimes be used for similar
-purposes, but there are some key differences:
-
-Parent-child pipelines:
-
-- Run under the same project, ref, and commit SHA as the parent pipeline.
-- Affect the overall status of the ref the pipeline runs against. For example,
- if a pipeline fails for the main branch, it's common to say that "main is broken".
- The status of child pipelines don't directly affect the status of the ref, unless the child
- pipeline is triggered with [`strategy:depend`](../yaml/index.md#triggerstrategy).
-- Are automatically canceled if the pipeline is configured with [`interruptible`](../yaml/index.md#interruptible)
- when a new pipeline is created for the same ref.
-- Display only the parent pipelines in the pipeline index page. Child pipelines are
- visible when visiting their parent pipeline's page.
-- Are limited to 2 levels of nesting. A parent pipeline can trigger multiple child pipelines,
- and those child pipeline can trigger multiple child pipelines (`A -> B -> C`).
-
-Multi-project pipelines:
-
-- Are triggered from another pipeline, but the upstream (triggering) pipeline does
- not have much control over the downstream (triggered) pipeline. However, it can
- choose the ref of the downstream pipeline, and pass CI/CD variables to it.
-- Affect the overall status of the ref of the project it runs in, but does not
- affect the status of the triggering pipeline's ref, unless it was triggered with
- [`strategy:depend`](../yaml/index.md#triggerstrategy).
-- Are not automatically canceled in the downstream project when using [`interruptible`](../yaml/index.md#interruptible)
- if a new pipeline runs for the same ref in the upstream pipeline. They can be
- automatically canceled if a new pipeline is triggered for the same ref on the downstream project.
-- Multi-project pipelines are standalone pipelines because they are normal pipelines
- that happened to be triggered by an external project. They are all visible on the pipeline index page.
-- Are independent, so there are no nesting limits.
+- [Multi-project pipelines](downstream_pipelines.md#multi-project-pipelines) combine pipelines for different projects together.
## Configure a pipeline
@@ -175,7 +141,7 @@ operation of the pipeline.
To execute a pipeline manually:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **CI/CD > Pipelines**.
1. Select **Run pipeline**.
1. In the **Run for branch name or tag** field, select the branch or tag to run the pipeline for.
@@ -288,7 +254,7 @@ page, then selecting **Delete**.
![Pipeline Delete](img/pipeline-delete.png)
Deleting a pipeline does not automatically delete its
-[child pipelines](parent_child_pipelines.md).
+[child pipelines](downstream_pipelines.md#parent-child-pipelines).
See the [related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/39503)
for details.
@@ -323,6 +289,34 @@ preserving deployment keys and other credentials from being unintentionally
accessed. To ensure that jobs intended to be executed on protected
runners do not use regular runners, they must be tagged accordingly.
+## Trigger a pipeline when an upstream project is rebuilt **(PREMIUM)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9045) in GitLab 12.8.
+
+You can trigger a pipeline in your project whenever a pipeline finishes for a new
+tag in a different project.
+
+Prerequisites:
+
+- The upstream project must be [public](../../user/public_access.md).
+- The user must have the Developer role
+ in the upstream project.
+
+To trigger the pipeline when the upstream project is rebuilt:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > CI/CD**.
+1. Expand **Pipeline subscriptions**.
+1. Enter the project you want to subscribe to, in the format `<namespace>/<project>`.
+ For example, if the project is `https://gitlab.com/gitlab-org/gitlab`, use `gitlab-org/gitlab`.
+1. Select **Subscribe**.
+
+Any pipelines that complete successfully for new tags in the subscribed project
+now trigger a pipeline on the current project's default branch. The maximum
+number of upstream pipeline subscriptions is 2 by default, for both the upstream and
+downstream projects. On self-managed instances, an administrator can change this
+[limit](../../administration/instance_limits.md#number-of-cicd-subscriptions-to-a-project).
+
### How pipeline duration is calculated
Total running time for a given pipeline excludes retries and pending
@@ -388,8 +382,8 @@ You can group the jobs by:
- [Job dependencies](#view-job-dependencies-in-the-pipeline-graph), which arranges
jobs based on their [`needs`](../yaml/index.md#needs) dependencies.
-[Multi-project pipeline graphs](multi_project_pipelines.md#multi-project-pipeline-visualization) help
-you visualize the entire pipeline, including all cross-project inter-dependencies. **(PREMIUM)**
+[Multi-project pipeline graphs](downstream_pipelines.md#view-multi-project-pipelines-in-pipeline-graphs) help
+you visualize the entire pipeline, including all cross-project inter-dependencies.
If a stage contains more than 100 jobs, only the first 100 jobs are listed in the
pipeline graph. The remaining jobs still run as normal. To see the jobs:
@@ -403,8 +397,9 @@ pipeline graph. The remaining jobs still run as normal. To see the jobs:
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/328538) in GitLab 14.0.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/328538) in GitLab 14.2.
-You can arrange jobs in the pipeline graph based on their [`needs`](../yaml/index.md#needs)
-dependencies.
+To arrange jobs in the pipeline graph based on their [`needs`](../yaml/index.md#needs)
+dependencies, select **Job dependencies** in the **Group jobs by** section. This option
+is available for pipelines with 3 or more jobs with `needs` job dependencies.
Jobs in the leftmost column run first, and jobs that depend on them are grouped in the next columns.
@@ -455,25 +450,6 @@ Pipeline analytics are available on the [**CI/CD Analytics** page](../../user/an
Pipeline status and test coverage report badges are available and configurable for each project.
For information on adding pipeline badges to projects, see [Pipeline badges](settings.md#pipeline-badges).
-### Downstream pipelines
-
-In the pipeline graph view, downstream pipelines ([Multi-project pipelines](multi_project_pipelines.md)
-and [Parent-child pipelines](parent_child_pipelines.md)) display as a list of cards
-on the right of the graph.
-
-#### Cancel or retry downstream pipelines from the graph view
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/354974) in GitLab 15.0 [with a flag](../../administration/feature_flags.md) named `downstream_retry_action`. Disabled by default.
-> - [Generally available and feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/357406) in GitLab 15.1.
-
-To cancel a downstream pipeline that is still running, select **Cancel** (**{cancel}**)
-on the pipeline's card.
-
-To retry a failed downstream pipeline, select **Retry** (**{retry}**)
-on the pipeline's card.
-
-![downstream pipeline actions](img/downstream_pipeline_actions.png)
-
## Pipelines API
GitLab provides API endpoints to:
diff --git a/doc/ci/pipelines/job_artifacts.md b/doc/ci/pipelines/job_artifacts.md
index c8babe3320d..f30ae32efb2 100644
--- a/doc/ci/pipelines/job_artifacts.md
+++ b/doc/ci/pipelines/job_artifacts.md
@@ -305,7 +305,7 @@ the artifact.
## How searching for job artifacts works
In [GitLab 13.5 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/201784), artifacts
-for [parent and child pipelines](parent_child_pipelines.md) are searched in hierarchical
+for [parent and child pipelines](downstream_pipelines.md#parent-child-pipelines) are searched in hierarchical
order from parent to child. For example, if both parent and child pipelines have a
job with the same name, the job artifact from the parent pipeline is returned.
@@ -388,7 +388,7 @@ Keeping the latest artifacts can use a large amount of storage space in projects
with a lot of jobs or large artifacts. If the latest artifacts are not needed in
a project, you can disable this behavior to save space:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Artifacts**.
1. Clear the **Keep artifacts from most recent successful jobs** checkbox.
@@ -451,3 +451,63 @@ test-job:
reports:
dotenv: build.env
```
+
+### Job artifacts are not expired
+
+If some job artifacts are not expiring as expected, check if the
+[**Keep artifacts from most recent successful jobs**](#keep-artifacts-from-most-recent-successful-jobs)
+setting is enabled.
+
+When this setting is enabled, job artifacts from the latest successful pipeline
+of each ref do not expire and are not deleted.
+
+### Error message `This job could not start because it could not retrieve the needed artifacts.`
+
+A job configured with [`needs:artifacts`](../yaml/index.md#needsartifacts) keyword
+fails to start and returns this error message if:
+
+- The job's dependencies cannot be found.
+- The job cannot access the relevant resources due to insufficient permissions.
+
+The troubleshooting steps to follow are determined by the syntax used in the job configuration.
+
+#### Job configured with `needs:project`
+
+The `could not retrieve the needed artifacts.` error can happen for a job using
+[`needs:project`](../yaml/index.md#needsproject), with a configuration similar to:
+
+```yaml
+rspec:
+ needs:
+ - project: org/another-project
+ job: dependency-job
+ ref: master
+ artifacts: true
+```
+
+To troubleshoot this job, verify that:
+
+- Project `org/another-project` is in a group with a Premium subscription plan.
+- The user running the job has permissions to access resources in `org/another-project`.
+- The `project`, `job`, and `ref` combination exists and results in the desired dependency.
+- Any variables in use evaluate to the correct values.
+
+#### Job configured with `needs:pipeline:job`
+
+The `could not retrieve the needed artifacts.` error can happen for a job using
+[`needs:pipeline:job`](../yaml/index.md#needspipelinejob), with a configuration similar to:
+
+```yaml
+rspec:
+ needs:
+ - pipeline: $UPSTREAM_PIPELINE_ID
+ job: dependency-job
+ artifacts: true
+```
+
+To troubleshoot this job, verify that:
+
+- The `$UPSTREAM_PIPELINE_ID` CI/CD variable is available in the current pipeline's
+ parent-child pipeline hierarchy.
+- The `pipeline` and `job` combination exists and resolves to an existing pipeline.
+- `dependency-job` has run and finished successfully.
diff --git a/doc/ci/pipelines/merge_request_pipelines.md b/doc/ci/pipelines/merge_request_pipelines.md
index 5ba489c9830..f6c93356046 100644
--- a/doc/ci/pipelines/merge_request_pipelines.md
+++ b/doc/ci/pipelines/merge_request_pipelines.md
@@ -218,3 +218,28 @@ is not considered successful if:
When using the [merge when pipeline succeeds](../../user/project/merge_requests/merge_when_pipeline_succeeds.md)
feature and both pipelines types are present, the merge request pipelines are checked,
not the branch pipelines.
+
+### `An error occurred while trying to run a new pipeline for this merge request.`
+
+This error can happen when you select **Run pipeline** in a merge request, but the
+project does not have merge request pipelines enabled anymore.
+
+Some possible reasons for this error message:
+
+- The project does not have merge request pipelines enabled, has no pipelines listed
+ in the **Pipelines** tab, and you select **Run pipelines**.
+- The project used to have merge request pipelines enabled, but the configuration
+ was removed. For example:
+
+ 1. The project has merge request pipelines enabled in the `.gitlab-ci.yml` configuration
+ file when the merge request is created.
+ 1. The **Run pipeline** options is available in the merge request's **Pipelines** tab,
+ and selecting **Run pipeline** at this point likely does not cause any errors.
+ 1. The project's `.gitlab-ci.yml` file is changed to remove the merge request pipelines configuration.
+ 1. The branch is rebased to bring the updated configuration into the merge request.
+ 1. Now the pipeline configuration no longer supports merge request pipelines,
+ but you select **Run pipeline** to run a merge request pipeline.
+
+If **Run pipeline** is available, but the project does not have merge request pipelines
+enabled, do not use this option. You can push a commit or rebase the branch to trigger
+new branch pipelines.
diff --git a/doc/ci/pipelines/merge_trains.md b/doc/ci/pipelines/merge_trains.md
index 2882cd378aa..88ab6163f3c 100644
--- a/doc/ci/pipelines/merge_trains.md
+++ b/doc/ci/pipelines/merge_trains.md
@@ -59,7 +59,7 @@ changes that are included in the target branch, and the `C` changes that are fro
the merge request already in the train.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
-Watch this video for a demonstration on
+Watch this video for a demonstration on
[how parallel execution of merge trains can prevent commits from breaking the default branch](https://www.youtube.com/watch?v=D4qCqXgZkHQ).
## Prerequisites
@@ -81,9 +81,8 @@ To enable merge trains for your project:
1. If you are on a self-managed GitLab instance, ensure the [feature flag](#merge-trains-feature-flag) is set correctly.
1. [Configure your CI/CD configuration file](merge_request_pipelines.md#prerequisites)
so that the pipeline or individual jobs run for merge requests.
-1. On the top bar, select **Menu > Projects** and find your project.
-1. On the left sidebar, select **Settings > General**.
-1. Expand **Merge requests**.
+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, verify that **Merge commit** is selected.
1. In the **Merge options** section, select **Enable merged results pipelines** (if not already selected) and **Enable merge trains**.
1. Select **Save changes**.
@@ -210,7 +209,7 @@ If it succeeds after a retry, the merge request is not removed from the merge tr
Sometimes the **Start/Add to merge train** button is not available and the merge request says,
"The pipeline for this merge request failed. Please retry the job or push a new commit to fix the failure."
-This issue occurs when [**Pipelines must succeed**](../../user/project/merge_requests/merge_when_pipeline_succeeds.md#only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds)
+This issue occurs when [**Pipelines must succeed**](../../user/project/merge_requests/merge_when_pipeline_succeeds.md#require-a-successful-pipeline-for-merge)
is enabled in **Settings > General > Merge requests**. This option requires that you
run a new successful pipeline before you can re-add a merge request to a merge train.
diff --git a/doc/ci/pipelines/merged_results_pipelines.md b/doc/ci/pipelines/merged_results_pipelines.md
index 777871a7c5f..7209a6b9a77 100644
--- a/doc/ci/pipelines/merged_results_pipelines.md
+++ b/doc/ci/pipelines/merged_results_pipelines.md
@@ -41,10 +41,9 @@ To use merged results pipelines:
To enable merged results pipelines in a project, you must have at least the
Maintainer role:
-1. On the top bar, select **Menu > Projects** and find your project.
-1. On the left sidebar, select **Settings > General**.
-1. Expand **Merge requests**.
-1. Select **Enable merged results pipelines**.
+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 options** section, select **Enable merged results pipelines**.
1. Select **Save changes**.
WARNING:
diff --git a/doc/ci/pipelines/multi_project_pipelines.md b/doc/ci/pipelines/multi_project_pipelines.md
index a71af78f410..824f1445818 100644
--- a/doc/ci/pipelines/multi_project_pipelines.md
+++ b/doc/ci/pipelines/multi_project_pipelines.md
@@ -1,419 +1,11 @@
---
-stage: Verify
-group: Pipeline Authoring
-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
-type: reference
+redirect_to: 'downstream_pipelines.md'
+remove_date: '2022-11-31'
---
-# Multi-project pipelines **(FREE)**
+This document was moved to [another location](downstream_pipelines.md).
-> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/199224) to GitLab Free in 12.8.
-
-You can set up [GitLab CI/CD](../index.md) across multiple projects, so that a pipeline
-in one project can trigger a pipeline in another project. You can visualize the entire pipeline
-in one place, including all cross-project interdependencies.
-
-For example, you might deploy your web application from three different projects in GitLab.
-Each project has its own build, test, and deploy process. With multi-project pipelines you can
-visualize the entire pipeline, including all build and test stages for all three projects.
-
-<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
-For an overview, see the [Multi-project pipelines demo](https://www.youtube.com/watch?v=g_PIwBM1J84).
-
-Multi-project pipelines are also useful for larger products that require cross-project interdependencies, like those
-with a [microservices architecture](https://about.gitlab.com/blog/2016/08/16/trends-in-version-control-land-microservices/).
-Learn more in the [Cross-project Pipeline Triggering and Visualization demo](https://about.gitlab.com/learn/)
-at GitLab@learn, in the Continuous Integration section.
-
-If you trigger a pipeline in a downstream private project, on the upstream project's pipelines page,
-you can view:
-
-- The name of the project.
-- The status of the pipeline.
-
-If you have a public project that can trigger downstream pipelines in a private project,
-make sure there are no confidentiality problems.
-
-## Create multi-project pipelines
-
-To create multi-project pipelines, you can:
-
-- [Define them in your `.gitlab-ci.yml` file](#define-multi-project-pipelines-in-your-gitlab-ciyml-file).
-- [Use the API](#create-multi-project-pipelines-by-using-the-api).
-
-### Define multi-project pipelines in your `.gitlab-ci.yml` file
-
-> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/199224) to GitLab Free in 12.8.
-
-When you use the [`trigger`](../yaml/index.md#trigger) keyword to create a multi-project
-pipeline in your `.gitlab-ci.yml` file, you create what is called a *trigger job*. For example:
-
-```yaml
-rspec:
- stage: test
- script: bundle exec rspec
-
-staging:
- variables:
- ENVIRONMENT: staging
- stage: deploy
- trigger: my/deployment
-```
-
-In this example, after the `rspec` job succeeds in the `test` stage,
-the `staging` trigger job starts. The initial status of this
-job is `pending`.
-
-GitLab then creates a downstream pipeline in the
-`my/deployment` project and, as soon as the pipeline is created, the
-`staging` job succeeds. The full path to the project is `my/deployment`.
-
-You can view the status for the pipeline, or you can display
-[the downstream pipeline's status instead](#mirror-status-of-a-triggered-pipeline-in-the-trigger-job).
-
-The user that creates the upstream pipeline must be able to create pipelines in the
-downstream project (`my/deployment`) too. If the downstream project is not found,
-or the user does not have [permission](../../user/permissions.md) to create a pipeline there,
-the `staging` job is marked as _failed_.
-
-#### Trigger job configuration limitations
-
-Trigger jobs can use only a limited set of the GitLab CI/CD [configuration keywords](../yaml/index.md).
-The keywords available for use in trigger jobs are:
-
-- [`trigger`](../yaml/index.md#trigger)
-- [`stage`](../yaml/index.md#stage)
-- [`allow_failure`](../yaml/index.md#allow_failure)
-- [`rules`](../yaml/index.md#rules)
-- [`only` and `except`](../yaml/index.md#only--except)
-- [`when`](../yaml/index.md#when) (only with a value of `on_success`, `on_failure`, or `always`)
-- [`extends`](../yaml/index.md#extends)
-- [`needs`](../yaml/index.md#needs), but not [`needs:project`](../yaml/index.md#needsproject)
-
-Trigger jobs cannot use [job-level persisted variables](../variables/where_variables_can_be_used.md#persisted-variables).
-
-#### Specify a downstream pipeline branch
-
-You can specify a branch name for the downstream pipeline to use.
-GitLab uses the commit on the head of the branch to
-create the downstream pipeline.
-
-```yaml
-rspec:
- stage: test
- script: bundle exec rspec
-
-staging:
- stage: deploy
- trigger:
- project: my/deployment
- branch: stable-11-2
-```
-
-Use:
-
-- The `project` keyword to specify the full path to a downstream project.
- In [GitLab 15.3 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/367660), variable expansion is
- supported.
-- The `branch` keyword to specify the name of a branch in the project specified by `project`.
- In [GitLab 12.4 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/10126), variable expansion is
- supported.
-
-Pipelines triggered on a protected branch in a downstream project use the [role](../../user/permissions.md)
-of the user that ran the trigger job in the upstream project. If the user does not
-have permission to run CI/CD pipelines against the protected branch, the pipeline fails. See
-[pipeline security for protected branches](index.md#pipeline-security-on-protected-branches).
-
-#### Pass CI/CD variables to a downstream pipeline by using the `variables` keyword
-
-Sometimes you might want to pass CI/CD variables to a downstream pipeline.
-You can do that by using the `variables` keyword, just like you would for any other job.
-
-```yaml
-rspec:
- stage: test
- script: bundle exec rspec
-
-staging:
- variables:
- ENVIRONMENT: staging
- stage: deploy
- trigger: my/deployment
-```
-
-The `ENVIRONMENT` variable is passed to every job defined in a downstream
-pipeline. It is available as a variable when GitLab Runner picks a job.
-
-In the following configuration, the `MY_VARIABLE` variable is passed to the downstream pipeline
-that is created when the `trigger-downstream` job is queued. This is because `trigger-downstream`
-job inherits variables declared in global variables blocks, and then we pass these variables to a downstream pipeline.
-
-```yaml
-variables:
- MY_VARIABLE: my-value
-
-trigger-downstream:
- variables:
- ENVIRONMENT: something
- trigger: my/project
-```
-
-You can stop global variables from reaching the downstream pipeline by using the [`inherit:variables` keyword](../yaml/index.md#inheritvariables).
-In this example, the `MY_GLOBAL_VAR` variable is not available in the triggered pipeline:
-
-```yaml
-variables:
- MY_GLOBAL_VAR: value
-
-trigger-downstream:
- inherit:
- variables: false
- variables:
- MY_LOCAL_VAR: value
- trigger: my/project
-```
-
-You might want to pass some information about the upstream pipeline using, for
-example, predefined variables. In order to do that, you can use interpolation
-to pass any variable. For example:
-
-```yaml
-downstream-job:
- variables:
- UPSTREAM_BRANCH: $CI_COMMIT_REF_NAME
- trigger: my/project
-```
-
-In this scenario, the `UPSTREAM_BRANCH` variable with the value of the upstream pipeline's
-`$CI_COMMIT_REF_NAME` is passed to `downstream-job`. It is available in the
-context of all downstream builds.
-
-You cannot use this method to forward [job-level persisted variables](../variables/where_variables_can_be_used.md#persisted-variables)
-to a downstream pipeline, as they are not available in trigger jobs.
-
-Upstream pipelines take precedence over downstream ones. If there are two
-variables with the same name defined in both upstream and downstream projects,
-the ones defined in the upstream project take precedence.
-
-#### Pass CI/CD variables to a downstream pipeline by using variable inheritance **(PREMIUM)**
-
-You can pass variables to a downstream pipeline with [`dotenv` variable inheritance](../variables/index.md#pass-an-environment-variable-to-another-job) and [`needs:project`](../yaml/index.md#needsproject).
-
-In the upstream pipeline:
-
-1. Save the variables in a `.env` file.
-1. Save the `.env` file as a `dotenv` report.
-1. Trigger the downstream pipeline.
-
- ```yaml
- build_vars:
- stage: build
- script:
- - echo "BUILD_VERSION=hello" >> build.env
- artifacts:
- reports:
- dotenv: build.env
-
- deploy:
- stage: deploy
- trigger: my/downstream_project
- ```
-
-1. Set the `test` job in the downstream pipeline to inherit the variables from the `build_vars`
- job in the upstream project with `needs`. The `test` job inherits the variables in the
- `dotenv` report and it can access `BUILD_VERSION` in the script:
-
- ```yaml
- test:
- stage: test
- script:
- - echo $BUILD_VERSION
- needs:
- - project: my/upstream_project
- job: build_vars
- ref: master
- artifacts: true
- ```
-
-#### Pass artifacts to a downstream pipeline
-
-You can pass artifacts to a downstream pipeline by using [`needs:project`](../yaml/index.md#needsproject).
-
-1. In a job in the upstream pipeline, save the artifacts using the [`artifacts`](../yaml/index.md#artifacts) keyword.
-1. Trigger the downstream pipeline with a trigger job:
-
- ```yaml
- build_artifacts:
- stage: build
- script:
- - echo "This is a test artifact!" >> artifact.txt
- artifacts:
- paths:
- - artifact.txt
-
- deploy:
- stage: deploy
- trigger: my/downstream_project
- ```
-
-1. In a job in the downstream pipeline, fetch the artifacts from the upstream pipeline
- by using `needs:project`. Set `job` to the job in the upstream pipeline to fetch artifacts from,
- `ref` to the branch, and `artifacts: true`.
-
- ```yaml
- test:
- stage: test
- script:
- - cat artifact.txt
- needs:
- - project: my/upstream_project
- job: build_artifacts
- ref: main
- artifacts: true
- ```
-
-#### Pass artifacts to a downstream pipeline from a Merge Request pipeline
-
-When you use `needs:project` to [pass artifacts to a downstream pipeline](#pass-artifacts-to-a-downstream-pipeline),
-the `ref` value is usually a branch name, like `main` or `development`.
-
-For merge request pipelines, the `ref` value is in the form of `refs/merge-requests/<id>/head`,
-where `id` is the merge request ID. You can retrieve this ref with the [`CI_MERGE_REQUEST_REF_PATH`](../variables/predefined_variables.md#predefined-variables-for-merge-request-pipelines)
-CI/CD variable. Do not use a branch name as the `ref` with merge request pipelines,
-because the downstream pipeline attempts to fetch artifacts from the latest branch pipeline.
-
-To fetch the artifacts from the upstream `merge request` pipeline instead of the `branch` pipeline,
-pass this variable to the downstream pipeline using variable inheritance:
-
-1. In a job in the upstream pipeline, save the artifacts using the [`artifacts`](../yaml/index.md#artifacts) keyword.
-1. In the job that triggers the downstream pipeline, pass the `$CI_MERGE_REQUEST_REF_PATH` variable by using
- [variable inheritance](#pass-cicd-variables-to-a-downstream-pipeline-by-using-the-variables-keyword):
-
- ```yaml
- build_artifacts:
- stage: build
- script:
- - echo "This is a test artifact!" >> artifact.txt
- artifacts:
- paths:
- - artifact.txt
-
- upstream_job:
- variables:
- UPSTREAM_REF: $CI_MERGE_REQUEST_REF_PATH
- trigger:
- project: my/downstream_project
- branch: my-branch
- ```
-
-1. In a job in the downstream pipeline, fetch the artifacts from the upstream pipeline
- by using `needs:project`. Set the `ref` to the `UPSTREAM_REF` variable, and `job`
- to the job in the upstream pipeline to fetch artifacts from:
-
- ```yaml
- test:
- stage: test
- script:
- - cat artifact.txt
- needs:
- - project: my/upstream_project
- job: build_artifacts
- ref: UPSTREAM_REF
- artifacts: true
- ```
-
-This method works for fetching artifacts from a regular merge request parent pipeline,
-but fetching artifacts from [merge results](merged_results_pipelines.md) pipelines is not supported.
-
-#### Use `rules` or `only`/`except` with multi-project pipelines
-
-You can use CI/CD variables or the [`rules`](../yaml/index.md#rulesif) keyword to
-[control job behavior](../jobs/job_control.md) for multi-project pipelines. When a
-downstream pipeline is triggered with the [`trigger`](../yaml/index.md#trigger) keyword,
-the value of the [`$CI_PIPELINE_SOURCE` predefined variable](../variables/predefined_variables.md)
-is `pipeline` for all its jobs.
-
-If you use [`only/except`](../yaml/index.md#only--except) to control job behavior, use the
-[`pipelines`](../yaml/index.md#onlyrefs--exceptrefs) keyword.
-
-#### Mirror status of a triggered pipeline in the trigger job
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11238) in GitLab Premium 12.3.
-> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/199224) to GitLab Free in 12.8.
-
-You can mirror the pipeline status from the triggered pipeline to the source trigger job
-by using [`strategy: depend`](../yaml/index.md#triggerstrategy). For example:
-
-```yaml
-trigger_job:
- trigger:
- project: my/project
- strategy: depend
-```
-
-### Create multi-project pipelines by using the API
-
-> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/31573) to GitLab Free in 12.4.
-
-When you use the [`CI_JOB_TOKEN` to trigger pipelines](../jobs/ci_job_token.md),
-GitLab recognizes the source of the job token. The pipelines become related,
-so you can visualize their relationships on pipeline graphs.
-
-These relationships are displayed in the pipeline graph by showing inbound and
-outbound connections for upstream and downstream pipeline dependencies.
-
-When using:
-
-- CI/CD variables or [`rules`](../yaml/index.md#rulesif) to control job behavior, the value of
- the [`$CI_PIPELINE_SOURCE` predefined variable](../variables/predefined_variables.md) is
- `pipeline` for multi-project pipeline triggered through the API with `CI_JOB_TOKEN`.
-- [`only/except`](../yaml/index.md#only--except) to control job behavior, use the
- `pipelines` keyword.
-
-## Trigger a pipeline when an upstream project is rebuilt **(PREMIUM)**
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9045) in GitLab 12.8.
-
-You can trigger a pipeline in your project whenever a pipeline finishes for a new
-tag in a different project.
-
-Prerequisites:
-
-- The upstream project must be [public](../../user/public_access.md).
-- The user must have the Developer role
- in the upstream project.
-
-To trigger the pipeline when the upstream project is rebuilt:
-
-1. On the top bar, select **Menu > Projects** and find your project.
-1. On the left sidebar, select **Settings > CI/CD**.
-1. Expand **Pipeline subscriptions**.
-1. Enter the project you want to subscribe to, in the format `<namespace>/<project>`.
- For example, if the project is `https://gitlab.com/gitlab-org/gitlab`, use `gitlab-org/gitlab`.
-1. Select **Subscribe**.
-
-Any pipelines that complete successfully for new tags in the subscribed project
-now trigger a pipeline on the current project's default branch. The maximum
-number of upstream pipeline subscriptions is 2 by default, for both the upstream and
-downstream projects. On self-managed instances, an administrator can change this
-[limit](../../administration/instance_limits.md#number-of-cicd-subscriptions-to-a-project).
-
-## Multi-project pipeline visualization **(PREMIUM)**
-
-When your pipeline triggers a downstream pipeline, the downstream pipeline displays
-to the right of the [pipeline graph](index.md#visualize-pipelines).
-
-![Multi-project pipeline graph](img/multi_project_pipeline_graph_v14_3.png)
-
-In [pipeline mini graphs](index.md#pipeline-mini-graphs), the downstream pipeline
-displays to the right of the mini graph.
-
-![Multi-project pipeline mini graph](img/pipeline_mini_graph_v15_0.png)
-
-## Retry or cancel multi-project pipelines
-
-If you have permission to trigger pipelines in the downstream project, you can
-retry or cancel multi-project pipelines:
-
-- [In the main graph view](index.md#downstream-pipelines).
-- From the downstream pipeline's details page.
+<!-- This redirect file can be deleted after <2022-11-31>. -->
+<!-- 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
index 3fd739087ec..be8ed8ba6d7 100644
--- a/doc/ci/pipelines/parent_child_pipelines.md
+++ b/doc/ci/pipelines/parent_child_pipelines.md
@@ -1,228 +1,11 @@
---
-stage: Verify
-group: Pipeline Authoring
-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
-type: reference
+redirect_to: 'downstream_pipelines.md'
+remove_date: '2022-12-05'
---
-# Parent-child pipelines **(FREE)**
+This document was moved to [another location](downstream_pipelines.md).
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16094) in GitLab 12.7.
-
-As pipelines grow more complex, a few related problems start to emerge:
-
-- The staged structure, where all steps in a stage must be completed before the first
- job in next stage begins, causes arbitrary waits, slowing things down.
-- Configuration for the single global pipeline becomes very long and complicated,
- making it hard to manage.
-- Imports with [`include`](../yaml/index.md#include) increase the complexity of the configuration, and create the potential
- for namespace collisions where jobs are unintentionally duplicated.
-- Pipeline UX can become unwieldy with so many jobs and stages to work with.
-
-Additionally, sometimes the behavior of a pipeline needs to be more dynamic. The ability
-to choose to start sub-pipelines (or not) is a powerful ability, especially if the
-YAML is dynamically generated.
-
-![Parent pipeline graph expanded](img/parent_pipeline_graph_expanded_v14_3.png)
-
-Similarly to [multi-project pipelines](multi_project_pipelines.md), a pipeline can trigger a
-set of concurrently running child pipelines, but within the same project:
-
-- Child pipelines still execute each of their jobs according to a stage sequence, but
- would be free to continue forward through their stages without waiting for unrelated
- jobs in the parent pipeline to finish.
-- The configuration is split up into smaller child pipeline configurations. Each child pipeline contains only relevant steps which are
- easier to understand. This reduces the cognitive load to understand the overall configuration.
-- Imports are done at the child pipeline level, reducing the likelihood of collisions.
-
-Child pipelines work well with other GitLab CI/CD features:
-
-- Use [`rules: changes`](../yaml/index.md#ruleschanges) to trigger pipelines only when
- certain files change. This is useful for monorepos, for example.
-- Since the parent pipeline in `.gitlab-ci.yml` and the child pipeline run as normal
- pipelines, they can have their own behaviors and sequencing in relation to triggers.
-
-See the [`trigger`](../yaml/index.md#trigger) keyword documentation for full details on how to
-include the child pipeline configuration.
-
-<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
-For an overview, see [Parent-Child Pipelines feature demo](https://youtu.be/n8KpBSqZNbk).
-
-NOTE:
-The artifact containing the generated YAML file must not be larger than 5MB.
-
-## Examples
-
-The simplest case is [triggering a child pipeline](../yaml/index.md#trigger) using a
-local YAML file to define the pipeline configuration. In this case, the parent pipeline
-triggers the child pipeline, and continues without waiting:
-
-```yaml
-microservice_a:
- trigger:
- include: path/to/microservice_a.yml
-```
-
-You can include multiple files when defining a child pipeline. The child pipeline's
-configuration is composed of all configuration files merged together:
-
-```yaml
-microservice_a:
- trigger:
- include:
- - local: path/to/microservice_a.yml
- - template: Security/SAST.gitlab-ci.yml
-```
-
-In [GitLab 13.5 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/205157),
-you can use [`include:file`](../yaml/index.md#includefile) to trigger child pipelines
-with a configuration file in a different project:
-
-```yaml
-microservice_a:
- trigger:
- include:
- - project: 'my-group/my-pipeline-library'
- ref: 'main'
- file: '/path/to/child-pipeline.yml'
-```
-
-The maximum number of entries that are accepted for `trigger:include` is three.
-
-Similar to [multi-project pipelines](multi_project_pipelines.md#mirror-status-of-a-triggered-pipeline-in-the-trigger-job),
-we can set the parent pipeline to depend on the status of the child pipeline upon completion:
-
-```yaml
-microservice_a:
- trigger:
- include:
- - local: path/to/microservice_a.yml
- - template: Security/SAST.gitlab-ci.yml
- strategy: depend
-```
-
-## Merge request child pipelines
-
-To trigger a child pipeline as a [merge request pipeline](merge_request_pipelines.md) we need to:
-
-- Set the trigger job to run on merge requests:
-
-```yaml
-# parent .gitlab-ci.yml
-microservice_a:
- trigger:
- include: path/to/microservice_a.yml
- rules:
- - if: $CI_MERGE_REQUEST_ID
-```
-
-- Configure the child pipeline by either:
-
- - Setting all jobs in the child pipeline to evaluate in the context of a merge request:
-
- ```yaml
- # child path/to/microservice_a.yml
- workflow:
- rules:
- - if: $CI_MERGE_REQUEST_ID
-
- job1:
- script: ...
-
- job2:
- script: ...
- ```
-
- - Alternatively, setting the rule per job. For example, to create only `job1` in
- the context of merge request pipelines:
-
- ```yaml
- # child path/to/microservice_a.yml
- job1:
- script: ...
- rules:
- - if: $CI_MERGE_REQUEST_ID
-
- job2:
- script: ...
- ```
-
-## Dynamic child pipelines
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35632) in GitLab 12.9.
-
-Instead of running a child pipeline from a static YAML file, you can define a job that runs
-your own script to generate a YAML file, which is then used to trigger a child pipeline.
-
-This technique can be very powerful in generating pipelines targeting content that changed or to
-build a matrix of targets and architectures.
-
-<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).
-
-We also have an example project using
-[Dynamic Child Pipelines with Jsonnet](https://gitlab.com/gitlab-org/project-templates/jsonnet)
-which shows how to use a data templating language to generate your `.gitlab-ci.yml` at runtime.
-You could use a similar process for other templating languages like
-[Dhall](https://dhall-lang.org/) or [ytt](https://get-ytt.io/).
-
-The artifact path is parsed by GitLab, not the runner, so the path must match the
-syntax for the OS running GitLab. If GitLab is running on Linux but using a Windows
-runner for testing, the path separator for the trigger job would be `/`. Other CI/CD
-configuration for jobs, like scripts, that use the Windows runner would use `\`.
-
-In GitLab 12.9, the child pipeline could fail to be created in certain cases, causing the parent pipeline to fail.
-This is [resolved](https://gitlab.com/gitlab-org/gitlab/-/issues/209070) in GitLab 12.10.
-
-### Dynamic child pipeline example
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35632) in GitLab 12.9.
-
-You can trigger a child pipeline from a [dynamically generated configuration file](../pipelines/parent_child_pipelines.md#dynamic-child-pipelines):
-
-```yaml
-generate-config:
- stage: build
- script: generate-ci-config > generated-config.yml
- artifacts:
- paths:
- - generated-config.yml
-
-child-pipeline:
- stage: test
- trigger:
- include:
- - artifact: generated-config.yml
- job: generate-config
-```
-
-The `generated-config.yml` is extracted from the artifacts and used as the configuration
-for triggering the child pipeline.
-
-## Nested child pipelines
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/29651) in GitLab 13.4.
-> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/243747) in GitLab 13.5.
-
-Parent and child pipelines were introduced with a maximum depth of one level of child
-pipelines, which was later increased to two. A parent pipeline can trigger many child
-pipelines, and these child pipelines can trigger their own child pipelines. It's not
-possible to trigger another level of child pipelines.
-
-<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
-For an overview, see [Nested Dynamic Pipelines](https://youtu.be/C5j3ju9je2M).
-
-## Pass CI/CD variables to a child pipeline
-
-You can pass CI/CD variables to a downstream pipeline using the same methods as
-multi-project pipelines:
-
-- [By using the `variable` keyword](multi_project_pipelines.md#pass-cicd-variables-to-a-downstream-pipeline-by-using-the-variables-keyword).
-- [By using variable inheritance](multi_project_pipelines.md#pass-cicd-variables-to-a-downstream-pipeline-by-using-variable-inheritance).
-
-## Retry or cancel child pipelines
-
-You can retry or cancel child pipelines:
-
-- [In the main graph view](index.md#downstream-pipelines).
-- In the child pipeline's details page.
+<!-- 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_architectures.md b/doc/ci/pipelines/pipeline_architectures.md
index 3ff22a16900..a02ac7ba067 100644
--- a/doc/ci/pipelines/pipeline_architectures.md
+++ b/doc/ci/pipelines/pipeline_architectures.md
@@ -84,12 +84,14 @@ deploy_a:
script:
- echo "This job deploys something. It will only run when all jobs in the"
- echo "test stage complete."
+ environment: production
deploy_b:
stage: deploy
script:
- echo "This job deploys something else. It will only run when all jobs in the"
- echo "test stage complete. It will start at about the same time as deploy_a."
+ environment: production
```
## Directed Acyclic Graph Pipelines
@@ -151,18 +153,20 @@ deploy_a:
script:
- echo "Since build_a and test_a run quickly, this deploy job can run much earlier."
- echo "It does not need to wait for build_b or test_b."
+ environment: production
deploy_b:
stage: deploy
needs: [test_b]
script:
- echo "Since build_b and test_b run slowly, this deploy job will run much later."
+ environment: production
```
## Child / Parent Pipelines
In the examples above, it's clear we've got two types of things that could be built independently.
-This is an ideal case for using [Child / Parent Pipelines](parent_child_pipelines.md)) via
+This is an ideal case for using [Child / Parent Pipelines](downstream_pipelines.md#parent-child-pipelines)) via
the [`trigger` keyword](../yaml/index.md#trigger). It separates out the configuration
into multiple files, keeping things very simple. You can also combine this with:
@@ -237,6 +241,7 @@ deploy_a:
needs: [test_a]
script:
- echo "This job deploys something."
+ environment: production
```
Example child `b` pipeline configuration, located in `/b/.gitlab-ci.yml`, making
@@ -266,6 +271,7 @@ deploy_b:
needs: [test_b]
script:
- echo "This job deploys something else."
+ environment: production
```
It's also possible to set jobs to run before or after triggering child pipelines,
diff --git a/doc/ci/pipelines/pipeline_efficiency.md b/doc/ci/pipelines/pipeline_efficiency.md
index ad43895d7ef..72711f9b9dd 100644
--- a/doc/ci/pipelines/pipeline_efficiency.md
+++ b/doc/ci/pipelines/pipeline_efficiency.md
@@ -187,7 +187,7 @@ shouldn't run, saving pipeline resources.
In a basic configuration, jobs always wait for all other jobs in earlier stages to complete
before running. This is the simplest configuration, but it's also the slowest in most
cases. [Directed Acyclic Graphs](../directed_acyclic_graph/index.md) and
-[parent/child pipelines](parent_child_pipelines.md) are more flexible and can
+[parent/child pipelines](downstream_pipelines.md#parent-child-pipelines) are more flexible and can
be more efficient, but can also make pipelines harder to understand and analyze.
### Caching
diff --git a/doc/ci/pipelines/schedules.md b/doc/ci/pipelines/schedules.md
index 8ab80e3798a..897caa340f9 100644
--- a/doc/ci/pipelines/schedules.md
+++ b/doc/ci/pipelines/schedules.md
@@ -27,7 +27,7 @@ Otherwise, the pipeline is not created. No error message is displayed.
To add a pipeline schedule:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **CI/CD > Schedules**.
1. Select **New schedule** and fill in the form.
- **Interval Pattern**: Select one of the preconfigured intervals, or enter a custom
@@ -45,7 +45,7 @@ To add a pipeline schedule:
The owner of a pipeline schedule can edit it:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. In the left sidebar, select **CI/CD > Schedules**.
1. Next to the schedule, select **Edit** (**{pencil}**) and fill in the form.
@@ -58,7 +58,7 @@ of the schedule.
To trigger a pipeline schedule manually, so that it runs immediately instead of
the next scheduled time:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **CI/CD > Schedules**.
1. On the right of the list, for
the pipeline you want to run, select **Play** (**{play}**).
@@ -74,7 +74,7 @@ including [protected environments](../environments/protected_environments.md) an
To take ownership of a pipeline created by a different user:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **CI/CD > Schedules**.
1. On the right of the list, for
the pipeline you want to become owner of, select **Take ownership**.
diff --git a/doc/ci/pipelines/settings.md b/doc/ci/pipelines/settings.md
index 34eae9828dd..d663dea7de8 100644
--- a/doc/ci/pipelines/settings.md
+++ b/doc/ci/pipelines/settings.md
@@ -25,7 +25,7 @@ For public and internal projects, you can change who can see your:
To change the visibility of your pipelines and related features:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **General pipelines**.
1. Select or clear the **Public pipelines** checkbox.
@@ -57,7 +57,7 @@ This setting has no effect when:
To change the pipeline visibility for non-project members:
-1. On the top bar, select **Menu > Projects** and find your 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. For **CI/CD**, choose:
@@ -73,7 +73,7 @@ is selected.
You can set pending or running pipelines to cancel automatically when a new pipeline runs on the same branch. You can enable this in the project settings:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **General Pipelines**.
1. Select the **Auto-cancel redundant pipelines** checkbox.
@@ -94,7 +94,7 @@ newer one, which may not be what you want.
To avoid this scenario:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **General pipelines**.
1. Select the **Skip outdated deployment jobs** checkbox.
@@ -130,7 +130,7 @@ directory. However, you can specify an alternate filename path, including locati
To customize the path:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **General pipelines**.
1. In the **CI/CD configuration file** field, enter the filename. If the file:
@@ -179,7 +179,7 @@ able to edit it.
You can choose how your repository is fetched from GitLab when a job runs.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **General pipelines**.
1. Under **Git strategy**, select an option:
@@ -200,7 +200,7 @@ in the `.gitlab-ci.yml` file.
You can limit the number of changes that GitLab CI/CD fetches when it clones
a repository.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **General pipelines**.
1. Under **Git strategy**, under **Git shallow clone**, enter a value.
@@ -217,7 +217,7 @@ in the `.gitlab-ci.yml` file.
You can define how long a job can run before it times out.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **General pipelines**.
1. In the **Timeout** field, enter the number of minutes, or a human-readable value like `2 hours`.
@@ -282,7 +282,7 @@ You can verify correct syntax using the [pipeline editor](../pipeline_editor/ind
To migrate from the project coverage setting to the `coverage` keyword, use the
regular expression displayed in the settings. Available in GitLab 14.10 and earlier:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **General pipelines**.
@@ -326,7 +326,7 @@ Use this regex for commonly used test tools.
To see the evolution of your project code coverage over time,
you can view a graph or download a CSV file with this data.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Repository**.
The historic data for each job is listed in the dropdown above the graph.
@@ -392,7 +392,7 @@ Support for [`semver`](https://semver.org/) sorting is tracked [in this issue](h
You can view the exact link for your badges. Then you can embed the badge in your HTML
or Markdown pages.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **General pipelines**.
1. In the **Pipeline status**, **Coverage report**, or **Latest release** sections, view the URLs for the images.
diff --git a/doc/ci/quick_start/index.md b/doc/ci/quick_start/index.md
index 0369824c92e..2b44cf3b898 100644
--- a/doc/ci/quick_start/index.md
+++ b/doc/ci/quick_start/index.md
@@ -109,6 +109,7 @@ To create a `.gitlab-ci.yml` file:
stage: deploy
script:
- echo "This job deploys something from the $CI_COMMIT_BRANCH branch."
+ environment: production
```
`$GITLAB_USER_LOGIN` and `$CI_COMMIT_BRANCH` are
diff --git a/doc/ci/resource_groups/index.md b/doc/ci/resource_groups/index.md
index e76c4621a0c..dff52a742a8 100644
--- a/doc/ci/resource_groups/index.md
+++ b/doc/ci/resource_groups/index.md
@@ -171,6 +171,7 @@ deploy:
include: deploy.gitlab-ci.yml
strategy: depend
resource_group: AWS-production
+ environment: production
```
```yaml
@@ -187,6 +188,7 @@ provision:
deployment:
stage: deploy
script: echo "Deploying..."
+ environment: production
```
You must define [`strategy: depend`](../yaml/index.md#triggerstrategy)
@@ -208,7 +210,7 @@ Read more how you can use GitLab for [safe deployments](../environments/deployme
Because [`oldest_first` process mode](#process-modes) enforces the jobs to be executed in a pipeline order,
there is a case that it doesn't work well with the other CI features.
-For example, when you run [a child pipeline](../pipelines/parent_child_pipelines.md)
+For example, when you run [a child pipeline](../pipelines/downstream_pipelines.md#parent-child-pipelines)
that requires the same resource group with the parent pipeline,
a dead lock could happen. Here is an example of a _bad_ setup:
@@ -224,6 +226,7 @@ deploy:
stage: deploy
script: echo
resource_group: production
+ environment: production
```
In a parent pipeline, it runs the `test` job that subsequently runs a child pipeline,
@@ -250,4 +253,5 @@ deploy:
stage: deploy
script: echo
resource_group: production
+ environment: production
```
diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md
index 6dd03033926..9bafb69482b 100644
--- a/doc/ci/review_apps/index.md
+++ b/doc/ci/review_apps/index.md
@@ -76,7 +76,7 @@ Prerequisite:
To use the Review Apps template:
-1. On the top bar, select **Menu > Projects** and find the project you want to create a Review App job for.
+1. On the top bar, select **Main menu > Projects** and find the project you want to create a Review App job for.
1. On the left sidebar, select **Deployments > Environments**.
1. Select **Enable Review Apps**.
1. Copy the provided code snippet and paste it into your
diff --git a/doc/ci/runners/configure_runners.md b/doc/ci/runners/configure_runners.md
index 3efa697bf2f..9d26ec63f96 100644
--- a/doc/ci/runners/configure_runners.md
+++ b/doc/ci/runners/configure_runners.md
@@ -151,7 +151,7 @@ different places.
To view the IP address of a shared runner you must have administrator access to
the GitLab instance. To determine this:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Runners**.
1. Find the runner in the table and view the **IP Address** column.
@@ -859,7 +859,7 @@ You can clean up group runners that have been inactive for more than three month
Group runners are those that were created at the group level.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Runners**.
1. Turn on the **Enable stale runner cleanup** toggle.
@@ -903,8 +903,8 @@ The version of GitLab Runner used by your runners should be
To determine which runners need to be upgraded:
1. View the list of runners:
- - For a group, on the top bar, select **Menu > Groups** and on the left sidebar, select **CI/CD > Runners**.
- - For the instance, select **Menu > Admin** and on the left sidebar, select **Runners**.
+ - For a group, on the top bar, select **Main menu > Groups**, find your group, and on the left sidebar select **CI/CD > Runners**.
+ - For the instance, select **Main menu > Admin** and on the left sidebar, select **Runners**.
1. Above the list of runners, view the status:
- **Outdated - recommended**: The runner does not have the latest `PATCH` version, which may make it vulnerable
@@ -912,3 +912,43 @@ To determine which runners need to be upgraded:
- **Outdated - available**: Newer versions are available but upgrading is not critical.
1. Filter the list by status to view which individual runners need to be upgraded.
+
+## Authentication token security
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/30942) in GitLab 15.3 [with a flag](../../administration/feature_flags.md) named `enforce_runner_token_expires_at`. 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 `enforce_runner_token_expires_at`.
+On GitLab.com, this feature is not available.
+
+Each runner has an [authentication token](../../api/runners.md#registration-and-authentication-tokens)
+to connect with the GitLab instance.
+
+To help prevent the token from being compromised, you can have the
+token rotate automatically at specified intervals. When the tokens are rotated,
+they are updated for each runner, regardless of the runner's status (`online` or `offline`).
+
+No manual intervention should be required, and no running jobs should be affected.
+
+If you need to manually update the authentication token, you can run a
+command to [reset the token](https://docs.gitlab.com/runner/commands/#gitlab-runner-reset-token).
+
+### Automatically rotate authentication tokens
+
+You can specify an interval for authentication tokens to rotate.
+This rotation helps ensure the security of the tokens assigned to your runners.
+
+Prerequisites:
+
+- Ensure your runners are using [GitLab Runner 15.3 or later](https://docs.gitlab.com/runner/#gitlab-runner-versions).
+
+To automatically rotate runner authentication tokens:
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Settings > CI/CD**.
+1. Expand **Continuous Integration and Deployment**
+1. Set a **Runners expiration** time for runners, leave empty for no expiration.
+1. Select **Save**.
+
+Before the interval expires, runners automatically request a new authentication token.
diff --git a/doc/ci/runners/index.md b/doc/ci/runners/index.md
index f69d1f0f730..1de24efa8aa 100644
--- a/doc/ci/runners/index.md
+++ b/doc/ci/runners/index.md
@@ -7,8 +7,8 @@ type: reference
# Runner SaaS **(FREE SAAS)**
-If you use GitLab SaaS (GitLab.com), your CI jobs automatically run on runners provided by GitLab.
-No configuration is required. Your jobs can run on:
+If you use GitLab SaaS (GitLab.com), your [untagged](../../ci/runners/configure_runners.md#use-tags-to-control-which-jobs-a-runner-can-run) CI jobs automatically run in containers on the Linux Runners.
+As long as shared runners are enabled for your project, no configuration is required. Your jobs can run on:
- [Linux runners](saas/linux_saas_runner.md).
- [Windows runners](saas/windows_saas_runner.md) ([Beta](../../policy/alpha-beta-support.md#beta-features)).
diff --git a/doc/ci/runners/runners_scope.md b/doc/ci/runners/runners_scope.md
index 9bd0b52f423..f8a33ea6861 100644
--- a/doc/ci/runners/runners_scope.md
+++ b/doc/ci/runners/runners_scope.md
@@ -28,7 +28,7 @@ If you are using a self-managed instance of GitLab:
going to your project's **Settings > CI/CD**, expanding **Runners**,
and selecting **Show runner installation instructions**.
These instructions are also available [in the documentation](https://docs.gitlab.com/runner/install/index.html).
-- The administrator can also configure a maximum number of shared runner
+- The administrator can also configure a maximum number of shared runner
[CI/CD minutes for each group](../pipelines/cicd_minutes.md#set-the-quota-of-cicd-minutes-for-a-specific-namespace).
If you are using GitLab.com:
@@ -51,7 +51,7 @@ For existing projects, an administrator must
To enable shared runners for a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Runners**.
1. Turn on the **Enable shared runners for this project** toggle.
@@ -60,7 +60,7 @@ To enable shared runners for a project:
To enable shared runners for a group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Runners**.
1. Turn on the **Enable shared runners for this group** toggle.
@@ -73,7 +73,7 @@ or group.
To disable shared runners for a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Runners**.
1. In the **Shared runners** area, turn off the **Enable shared runners for this project** toggle.
@@ -87,7 +87,7 @@ Shared runners are automatically disabled for a project:
To disable shared runners for a group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Runners**.
1. Turn off the **Enable shared runners for this group** toggle.
@@ -170,7 +170,7 @@ You must have the Owner role for the group.
To create a group runner:
1. [Install GitLab Runner](https://docs.gitlab.com/runner/install/).
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **CI/CD > Runners**.
1. Note the URL and token.
1. [Register the runner](https://docs.gitlab.com/runner/register/).
@@ -183,7 +183,7 @@ You can view and manage all runners for a group, its subgroups, and projects.
You can do this for your self-managed GitLab instance or for GitLab.com.
You must have the Owner role for the group.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **CI/CD > Runners**.
From this page, you can edit, pause, and remove runners from the group, its subgroups, and projects.
@@ -193,7 +193,7 @@ From this page, you can edit, pause, and remove runners from the group, its subg
You can pause or remove a group runner for your self-managed GitLab instance or for GitLab.com.
You must have the Owner role for the group.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **CI/CD > Runners**.
1. Select **Pause** or **Remove runner**.
- If you pause a group runner that is used by multiple projects, the runner pauses for all projects.
@@ -229,7 +229,7 @@ Prerequisite:
To create a specific runner:
1. [Install GitLab Runner](https://docs.gitlab.com/runner/install/).
-1. On the top bar, select **Menu > Projects** and find the project where you want to use the runner.
+1. On the top bar, select **Main menu > Projects** and find the project where you want to use the runner.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Runners**.
1. In the **Specific runners** section, note the URL and token.
@@ -250,7 +250,7 @@ You must have at least the Maintainer role for:
To enable a specific runner for a project:
-1. On the top bar, select **Menu > Projects** and find the project where you want to enable the runner.
+1. On the top bar, select **Main menu > Projects** and find the project where you want to enable the runner.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Runners**.
1. In the **Specific runners** area, by the runner you want, select **Enable for this project**.
@@ -269,7 +269,7 @@ but can also be changed later.
To lock or unlock a specific runner:
-1. On the top bar, select **Menu > Projects** and find the project where you want to enable the runner.
+1. On the top bar, select **Main menu > Projects** and find the project where you want to enable the runner.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Runners**.
1. Find the specific runner you want to lock or unlock. Make sure it's enabled. You cannot lock shared or group runners.
diff --git a/doc/ci/runners/saas/linux_saas_runner.md b/doc/ci/runners/saas/linux_saas_runner.md
index e96e89b47e5..a7d1b8722a5 100644
--- a/doc/ci/runners/saas/linux_saas_runner.md
+++ b/doc/ci/runners/saas/linux_saas_runner.md
@@ -4,43 +4,97 @@ group: Runner
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
---
-# SaaS runners on Linux **(FREE SAAS)**
+# SaaS runners on Linux
-SaaS runners on Linux are autoscaled ephemeral Google Cloud Platform virtual machines.
+When you run jobs on SaaS runners on Linux, the runners are on auto-scaled ephemeral virtual machine (VM) instances.
+Each VM uses the Google Container-Optimized OS (COS) and the latest version of Docker Engine.
+The default region for the VMs is `us-east1`.
-Autoscaling means reduced queue times to spin up CI/CD jobs, and isolated VMs for each job, thus maximizing security. These shared runners are available on GitLab.com.
+## Machine types available for private projects (x86-64)
-GitLab offers Ultimate tier capabilities and included CI/CD minutes per group per month for our [Open Source](https://about.gitlab.com/solutions/open-source/join/), [Education](https://about.gitlab.com/solutions/education/), and [Startups](https://about.gitlab.com/solutions/startups/) programs. For private projects, GitLab offers various [plans](https://about.gitlab.com/pricing/), starting with a Free tier.
+For the SaaS runners on Linux, GitLab offers a range of machine types for use in private projects.
+For Free, Premium, and Ultimate plan customers, jobs on these instances consume the CI/CD minutes allocated to your namespace.
-All your CI/CD jobs run on [n1-standard-1 instances](https://cloud.google.com/compute/docs/machine-types) with 3.75GB of RAM, Google COS and the latest Docker Engine
-installed. Instances provide 1 vCPU and 25GB of HDD disk space. The default
-region of the VMs is US East1.
-Each instance is used only for one job. This ensures that any sensitive data left on the system can't be accessed by other people's CI/CD jobs.
+| | Small | Medium | Large |
+|-------------------|---------------------------|---------------------------|--------------------------|
+| Specs | 1 vCPU, 3.75GB RAM | 2 vCPUs, 8GB RAM | 4 vCPUs, 16GB 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 |
-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, and a copy of your cloned repository.
+The `small` machine type is the default. Your job runs on this machine type if you don't specify
+a [tags:](../../yaml/index.md#tags) keyword in your `.gitlab-ci.yml` file.
+
+CI/CD jobs that run on `medium` and `large` machine types **will** consume CI minutes at a different rate than CI/CD jobs on the `small` machine type.
+
+Refer to the CI/CD minutes [cost factor](../../../ci/pipelines/cicd_minutes.md#cost-factor) for the cost factor applied to the machine type based on size.
+
+## Example of how to tag a job
-The `gitlab-shared-runners-manager-X.gitlab.com` fleet of runners are dedicated for GitLab projects as well as community forks of them. They use a slightly larger machine type (n1-standard-2) and have a bigger SSD disk size. They don't run untagged jobs and unlike the general fleet of shared runners, the instances are re-used up to 40 times.
+To use a machine type other than `small`, add a `tags:` keyword to your job.
+For example:
-Jobs handled by shared runners on GitLab.com (`shared-runners-manager-X.gitlab.com`)
-**time out after 3 hours**, regardless of the timeout configured in a
-project. Check issue [#4010](https://gitlab.com/gitlab-com/gl-infra/reliability/-/issues/4010) and [#4070](https://gitlab.com/gitlab-com/gl-infra/reliability/-/issues/4070) for the reference.
+```yaml
+stages:
+ - Prebuild
+ - Build
+ - Unit Test
-Jobs handled by shared runners on Windows and macOS on GitLab.com **time out after 1 hour** while this service is in the [Beta](../../../policy/alpha-beta-support.md#beta-features) stage.
+job_001:
+ stage: Prebuild
+ script:
+ - echo "this job runs on the default (small) instance"
-Below are the runners' settings.
+job_002:
+ tags: [ saas-linux-medium-amd64 ]
+ stage: Build
+ script:
+ - echo "this job runs on the medium instance"
+
+
+job_003:
+ tags: [ saas-linux-large-amd64 ]
+ stage: Unit Test
+ script:
+ - echo "this job runs on the large instance"
+
+```
-| Setting | GitLab.com | Default |
-| ----------- | ----------------- | ---------- |
-| Executor | `docker+machine` | - |
-| Default Docker image | `ruby:2.5` | - |
-| `privileged` (run [Docker in Docker](https://hub.docker.com/_/docker/)) | `true` | `false` |
+## SaaS runners for GitLab projects
-These runners share a [distributed cache](https://docs.gitlab.com/runner/configuration/autoscale.html#distributed-runners-caching) through use of a Google Cloud Storage (GCS) bucket. Cache contents not updated within the last 14 days are automatically removed through use of an [object lifecycle management policy](https://cloud.google.com/storage/docs/lifecycle).
+The `gitlab-shared-runners-manager-X.gitlab.com` fleet of runners are dedicated for
+GitLab projects and related community forks. These runners are backed by a Google Compute
+`n1-standard-2` machine type and do not run untagged jobs. Unlike the machine types used
+for private projects, each virtual machine is re-used up to 40 times.
+
+## SaaS runners on Linux settings
+
+Below are the settings for SaaS runners on Linux.
+
+| Setting | GitLab.com | Default |
+|-------------------------------------------------------------------------|------------------|---------|
+| Executor | `docker+machine` | - |
+| Default Docker image | `ruby:2.5` | - |
+| `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
+ the last 14 days are automatically removed, based on the
+ [object lifecycle management policy](https://cloud.google.com/storage/docs/lifecycle).
+
+- **Timeout settings**: Jobs handled by the SaaS Runners on Linux
+ **time out after 3 hours**, regardless of the timeout configured in a
+ project. For details, see issues [#4010](https://gitlab.com/gitlab-com/gl-infra/reliability/-/issues/4010)
+ 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,
+and a copy of your cloned repository.
## Pre-clone script
-With SaaS runners on Linux, you can run commands in a CI
+With SaaS runners on Linux, you can run commands in a CI/CD
job before the runner attempts to run `git init` and `git fetch` to
download a GitLab repository. The
[`pre_clone_script`](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section)
@@ -55,12 +109,13 @@ To use this feature, define a [CI/CD variable](../../../ci/variables/index.md#cu
`CI_PRE_CLONE_SCRIPT` that contains a bash script.
NOTE:
-The `CI_PRE_CLONE_SCRIPT` variable does not work on GitLab SaaS Windows or macOS Runners.
+The `CI_PRE_CLONE_SCRIPT` variable does not work on GitLab SaaS Windows or macOS runners.
### Pre-clone script example
This example was used in the `gitlab-org/gitlab` project until November 2021.
-The project no longer uses this optimization because the [pack-objects cache](../../../administration/gitaly/configure_gitaly.md#pack-objects-cache)
+The project no longer uses this optimization because the
+[pack-objects cache](../../../administration/gitaly/configure_gitaly.md#pack-objects-cache)
lets Gitaly serve the full CI/CD fetch traffic. See [Git fetch caching](../../../development/pipelines.md#git-fetch-caching).
The `CI_PRE_CLONE_SCRIPT` was defined as a project CI/CD variable:
diff --git a/doc/ci/secure_files/index.md b/doc/ci/secure_files/index.md
index 8e141c62ef6..ff5c29cdb06 100644
--- a/doc/ci/secure_files/index.md
+++ b/doc/ci/secure_files/index.md
@@ -23,7 +23,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/devops-for-mobile-apps/load-secure-files)
+by using the [load-secure-files](https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/load-secure-files)
tool.
NOTE:
@@ -34,7 +34,7 @@ Additional features and capabilities are planned.
To add a secure file to a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. In the **Secure Files** section, select **Expand**.
1. Select **Upload File**.
@@ -43,7 +43,7 @@ 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/devops-for-mobile-apps/load-secure-files)
+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)
tool to download the files in the job. After they are downloaded, you can use them
with your other script commands.
@@ -59,5 +59,5 @@ test:
variables:
SECURE_FILES_DOWNLOAD_PATH: './where/files/should/go/'
script:
- - curl --silent "https://gitlab.com/gitlab-org/incubation-engineering/devops-for-mobile-apps/load-secure-files/-/raw/main/installer" | bash
+ - curl --silent "https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/load-secure-files/-/raw/main/installer" | bash
```
diff --git a/doc/ci/services/index.md b/doc/ci/services/index.md
index ba3f806c96e..9b2bcc39b3e 100644
--- a/doc/ci/services/index.md
+++ b/doc/ci/services/index.md
@@ -8,9 +8,12 @@ type: index
# Services **(FREE)**
-The `services` keyword defines a Docker image that runs during a `job`
-linked to the Docker image that the image keyword defines. This allows
-you to access the service image during build time.
+When you configure CI/CD, you specify an image, which is used to create the container
+where your jobs will run. To specify this image, you use the `image` keyword.
+
+You can specify an additional image by using the `services` keyword. This additional
+image is used to create another container, which is available to the first container.
+The two containers have access to one another and can communicate when running the job.
The service image can run any application, but the most common use
case is to run a database container, for example:
diff --git a/doc/ci/testing/unit_test_reports.md b/doc/ci/testing/unit_test_reports.md
index 6294996c703..28356a62c99 100644
--- a/doc/ci/testing/unit_test_reports.md
+++ b/doc/ci/testing/unit_test_reports.md
@@ -156,7 +156,7 @@ If parsing JUnit report XML results in an error, an indicator is shown next to t
![Test Reports With Errors](img/pipelines_junit_test_report_with_errors_v13_10.png)
-For test case parsing limits, see [Max test cases per unit test report](../../user/gitlab_com/#gitlab-cicd).
+For test case parsing limits, see [Max test cases per unit test report](../../user/gitlab_com/index.md#gitlab-cicd).
GitLab does not parse very [large nodes](https://nokogiri.org/tutorials/parsing_an_html_xml_document.html#parse-options) of JUnit reports. There is [an issue](https://gitlab.com/gitlab-org/gitlab/-/issues/268035) open to make this optional.
diff --git a/doc/ci/triggers/index.md b/doc/ci/triggers/index.md
index 2c916ba092c..36d136727b0 100644
--- a/doc/ci/triggers/index.md
+++ b/doc/ci/triggers/index.md
@@ -13,7 +13,7 @@ to the [pipeline triggers API endpoint](../../api/pipeline_triggers.md).
When authenticating with the API, you can use:
- A [trigger token](#create-a-trigger-token) to trigger a branch or tag pipeline.
-- A [CI/CD job token](../jobs/ci_job_token.md) to trigger a [multi-project pipeline](../pipelines/multi_project_pipelines.md#create-multi-project-pipelines-by-using-the-api).
+- A [CI/CD job token](../jobs/ci_job_token.md) to [trigger a multi-project pipeline](../pipelines/downstream_pipelines.md#trigger-a-multi-project-pipeline-by-using-the-api).
## Create a trigger token
@@ -26,7 +26,7 @@ Prerequisite:
To create a trigger token:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Pipeline triggers**.
1. Enter a description and select **Add trigger**.
@@ -88,6 +88,7 @@ trigger_pipeline:
- 'curl --fail --request POST --form token=$MY_TRIGGER_TOKEN --form ref=main "https://gitlab.example.com/api/v4/projects/123456/trigger/pipeline"'
rules:
- if: $CI_COMMIT_TAG
+ environment: production
```
In this example:
@@ -152,7 +153,7 @@ users with the Owner and Maintainer role can view the values.
To revoke a trigger token:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Pipeline triggers**.
1. To the left of the trigger token you want to revoke, select **Revoke** (**{remove}**).
@@ -169,7 +170,7 @@ To [configure when to run jobs](../jobs/job_control.md) in triggered pipelines:
| `$CI_PIPELINE_SOURCE` value | `only`/`except` keywords | Trigger method |
|-----------------------------|--------------------------|---------------------|
| `trigger` | `triggers` | In pipelines triggered with the [pipeline triggers API](../../api/pipeline_triggers.md) by using a [trigger token](#create-a-trigger-token). |
-| `pipeline` | `pipelines` | In [multi-project pipelines](../pipelines/multi_project_pipelines.md#create-multi-project-pipelines-by-using-the-api) triggered with the [pipeline triggers API](../../api/pipeline_triggers.md) by using the [`$CI_JOB_TOKEN`](../jobs/ci_job_token.md), or by using the [`trigger`](../yaml/index.md#trigger) keyword in the CI/CD configuration file. |
+| `pipeline` | `pipelines` | In [multi-project pipelines](../pipelines/downstream_pipelines.md#trigger-a-multi-project-pipeline-by-using-the-api) triggered with the [pipeline triggers API](../../api/pipeline_triggers.md) by using the [`$CI_JOB_TOKEN`](../jobs/ci_job_token.md), or by using the [`trigger`](../yaml/index.md#trigger) keyword in the CI/CD configuration file. |
Additionally, the `$CI_PIPELINE_TRIGGERED` predefined CI/CD variable is set to `true`
in pipelines triggered with a trigger token.
diff --git a/doc/ci/troubleshooting.md b/doc/ci/troubleshooting.md
index 0230aaf7113..8a03ec0b56f 100644
--- a/doc/ci/troubleshooting.md
+++ b/doc/ci/troubleshooting.md
@@ -40,15 +40,26 @@ 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` will not work until the
-custom tags are set in the editor settings. For example, in VS Code, you can set
-`vscode-yaml` to parse `customTags`:
-
-```json
-"yaml.customTags": [
- "!reference sequence"
-]
-```
+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.
@@ -87,11 +98,11 @@ and [templates](examples/index.md#cicd-templates).
Some pipeline types have their own detailed usage guides that you should read
if you are using that type:
-- [Multi-project pipelines](pipelines/multi_project_pipelines.md): Have your pipeline trigger
+- [Multi-project pipelines](pipelines/downstream_pipelines.md#multi-project-pipelines): Have your pipeline trigger
a pipeline in a different project.
-- [Parent/child pipelines](pipelines/parent_child_pipelines.md): Have your main pipeline trigger
+- [Parent/child pipelines](pipelines/downstream_pipelines.md#parent-child-pipelines): Have your main pipeline trigger
and run separate pipelines in the same project. You can also
- [dynamically generate the child pipeline's configuration](pipelines/parent_child_pipelines.md#dynamic-child-pipelines)
+ [dynamically generate the child pipeline's configuration](pipelines/downstream_pipelines.md#dynamic-child-pipelines)
at runtime.
- [Merge request pipelines](pipelines/merge_request_pipelines.md): Run a pipeline
in the context of a merge request.
@@ -259,7 +270,7 @@ are enabled, the button is either **Add to merge train** or **Add to merge train
#### "A CI/CD pipeline must run and be successful before merge" message
-This message is shown if the [Pipelines must succeed](../user/project/merge_requests/merge_when_pipeline_succeeds.md#only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds)
+This message is shown if the [Pipelines must succeed](../user/project/merge_requests/merge_when_pipeline_succeeds.md#require-a-successful-pipeline-for-merge)
setting is enabled in the project and a pipeline has not yet run successfully.
This also applies if the pipeline has not been created yet, or if you are waiting
for an external CI service. If you don't use pipelines for your project, then you
@@ -316,11 +327,16 @@ To reduce the configuration size, you can:
[merged YAML](pipeline_editor/index.md#view-expanded-configuration) tab. Look for
duplicated configuration that can be removed or simplified.
- Move long or repeated `script` sections into standalone scripts in the project.
-- Use [parent and child pipelines](pipelines/parent_child_pipelines.md) to move some
+- Use [parent and child pipelines](pipelines/downstream_pipelines.md#parent-child-pipelines) to move some
work to jobs in an independent child pipeline.
On a self-managed instance, you can [increase the size limits](../administration/instance_limits.md#maximum-size-and-depth-of-cicd-configuration-yaml-files).
+### Error 500 when editing the `.gitlab-ci.yml` file
+
+A [loop of included configuration files](pipeline_editor/index.md#configuration-validation-currently-not-available-message)
+can cause a `500` error when editing the `.gitlab-ci.yml` file with the [web editor](../user/project/repository/web_editor.md).
+
## Pipeline warnings
Pipeline configuration warnings are shown when you:
diff --git a/doc/ci/variables/index.md b/doc/ci/variables/index.md
index 72df8d56815..25178903c9a 100644
--- a/doc/ci/variables/index.md
+++ b/doc/ci/variables/index.md
@@ -239,7 +239,7 @@ You can define instance variables via the UI or [API](../../api/instance_level_c
To add an instance variable:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD** and expand the **Variables** section.
1. Select the **Add variable** button, and fill in the details:
@@ -592,6 +592,7 @@ deploy:
BUILD_VARIABLE: value_from_deploy_job
script:
- echo "$BUILD_VARIABLE" # Output is: 'value_from_build_job' due to precedence
+ environment: production
```
The [`dependencies`](../yaml/index.md#dependencies) or
@@ -616,12 +617,19 @@ deploy_one:
- echo "$BUILD_VERSION" # Output is: 'hello'
dependencies:
- build
+ environment:
+ name: customer1
+ deployment_tier: production
+
deploy_two:
stage: deploy
script:
- echo "$BUILD_VERSION" # Output is empty
dependencies: []
+ environment:
+ name: customer2
+ deployment_tier: production
deploy_three:
stage: deploy
@@ -629,6 +637,10 @@ deploy_three:
- echo "$BUILD_VERSION" # Output is: 'hello'
needs:
- build
+ environment:
+ name: customer3
+ deployment_tier: production
+
deploy_four:
stage: deploy
@@ -637,6 +649,9 @@ deploy_four:
needs:
job: build
artifacts: true
+ environment:
+ name: customer4
+ deployment_tier: production
deploy_five:
stage: deploy
@@ -645,9 +660,12 @@ deploy_five:
needs:
job: build
artifacts: false
+ environment:
+ name: customer5
+ deployment_tier: production
```
-[Multi-project pipelines](../pipelines/multi_project_pipelines.md#pass-cicd-variables-to-a-downstream-pipeline-by-using-variable-inheritance)
+[Multi-project pipelines](../pipelines/downstream_pipelines.md#pass-dotenv-variables-created-in-a-job)
can also inherit variables from their upstream pipelines.
## CI/CD variable precedence
@@ -695,8 +713,8 @@ You can override the value of a variable when you:
1. Run a job manually in the UI.
1. Use [push options](../../user/project/push_options.md#push-options-for-gitlab-cicd).
1. Trigger a pipeline by using [the API](../triggers/index.md#pass-cicd-variables-in-the-api-call).
-1. Pass variables to a downstream pipeline [by using the `variable` keyword](../pipelines/multi_project_pipelines.md#pass-cicd-variables-to-a-downstream-pipeline-by-using-the-variables-keyword)
- or [by using variable inheritance](../pipelines/multi_project_pipelines.md#pass-cicd-variables-to-a-downstream-pipeline-by-using-variable-inheritance).
+1. Pass variables to a downstream pipeline [by using the `variable` keyword](../pipelines/downstream_pipelines.md#pass-cicd-variables-to-a-downstream-pipeline)
+ or [by using variable inheritance](../pipelines/downstream_pipelines.md#pass-dotenv-variables-created-in-a-job).
The pipeline variables declared in these events take [priority over other variables](#cicd-variable-precedence).
diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md
index f8900501244..77005e1cec4 100644
--- a/doc/ci/variables/predefined_variables.md
+++ b/doc/ci/variables/predefined_variables.md
@@ -66,8 +66,8 @@ as it can cause the pipeline to behave unexpectedly.
| `CI_JOB_IMAGE` | 12.9 | 12.9 | The name of the Docker image running the job. |
| `CI_JOB_JWT` | 12.10 | all | A RS256 JSON web token to authenticate with third party systems that support JWT authentication, for example [HashiCorp's Vault](../secrets/index.md). |
| `CI_JOB_JWT_V1` | 14.6 | all | The same value as `CI_JOB_JWT`. |
-| `CI_JOB_JWT_V2` | 14.6 | all | [**alpha:**](../../policy/alpha-beta-support.md#alpha-features) A newly formatted RS256 JSON web token to increase compatibility. Similar to `CI_JOB_JWT`, except the issuer (`iss`) claim is changed from `gitlab.com` to `https://gitlab.com`, `sub` has changed from `job_id` to a string that contains the project path, and an `aud` claim is added. Format is subject to change. Be aware, the `aud` field is a constant value. Trusting JWTs in multiple relying parties can lead to [one RP sending a JWT to another one and acting maliciously as a job](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72555#note_769112331). |
-| `CI_JOB_MANUAL` | 8.12 | all | `true` if a job was started manually. |
+| `CI_JOB_JWT_V2` | 14.6 | all | A newly formatted RS256 JSON web token to increase compatibility. Similar to `CI_JOB_JWT`, except the issuer (`iss`) claim is changed from `gitlab.com` to `https://gitlab.com`, `sub` has changed from `job_id` to a string that contains the project path, and an `aud` claim is added. Format is subject to change. Be aware, the `aud` field is a constant value. Trusting JWTs in multiple relying parties can lead to [one RP sending a JWT to another one and acting maliciously as a job](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/72555#note_769112331). **Note:** The `CI_JOB_JWT_V2` variable is available for testing, but the full feature is planned to be generally available when [issue 360657](https://gitlab.com/gitlab-org/gitlab/-/issues/360657) is complete.|
+| `CI_JOB_MANUAL` | 8.12 | all | Only available if the job was started manually. `true` when available. |
| `CI_JOB_NAME` | 9.0 | 0.5 | The name of the job. |
| `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`. |
@@ -117,6 +117,9 @@ as it can cause the pipeline to behave unexpectedly.
| `CI_SERVER_PORT` | 12.8 | all | The port of the GitLab instance URL, without host or protocol. For example `8080`. |
| `CI_SERVER_PROTOCOL` | 12.8 | all | The protocol of the GitLab instance URL, without host or port. For example `https`. |
| `CI_SERVER_REVISION` | all | all | GitLab revision that schedules jobs. |
+| `CI_SERVER_TLS_CA_FILE` | all | all | File containing the TLS CA certificate to verify the GitLab server when `tls-ca-file` set in [runner settings](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section). |
+| `CI_SERVER_TLS_CERT_FILE` | all | all | File containing the TLS certificate to verify the GitLab server when `tls-cert-file` set in [runner settings](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section). |
+| `CI_SERVER_TLS_KEY_FILE` | all | all | File containing the TLS key to verify the GitLab server when `tls-key-file` set in [runner settings](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section). |
| `CI_SERVER_URL` | 12.7 | all | The base URL of the GitLab instance, including protocol and port. For example `https://gitlab.example.com:8080`. |
| `CI_SERVER_VERSION_MAJOR` | 11.4 | all | The major version of the GitLab instance. For example, if the GitLab version is `13.6.1`, the `CI_SERVER_VERSION_MAJOR` is `13`. |
| `CI_SERVER_VERSION_MINOR` | 11.4 | all | The minor version of the GitLab instance. For example, if the GitLab version is `13.6.1`, the `CI_SERVER_VERSION_MINOR` is `6`. |
diff --git a/doc/ci/variables/where_variables_can_be_used.md b/doc/ci/variables/where_variables_can_be_used.md
index db83f2a14f3..6c1737f7c65 100644
--- a/doc/ci/variables/where_variables_can_be_used.md
+++ b/doc/ci/variables/where_variables_can_be_used.md
@@ -30,9 +30,10 @@ There are two places defined variables can be used. On the:
| [`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: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). |
| [`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/>Predefined project variables are supported: `GITLAB_FEATURES`, `CI_DEFAULT_BRANCH`, and all variables that start with `CI_PROJECT_` (for example `CI_PROJECT_NAME`). |
+| [`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). |
| [`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: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). |
diff --git a/doc/ci/yaml/artifacts_reports.md b/doc/ci/yaml/artifacts_reports.md
index 61ef8bbfab7..56a9da7cb84 100644
--- a/doc/ci/yaml/artifacts_reports.md
+++ b/doc/ci/yaml/artifacts_reports.md
@@ -80,20 +80,6 @@ GitLab can display the results of one or more reports in:
- The [security dashboard](../../user/application_security/security_dashboard/index.md).
- The [Project Vulnerability report](../../user/application_security/vulnerability_report/index.md).
-<!--- start_remove The following content will be removed on remove_date: '2023-08-22' -->
-
-## `artifacts:reports:cobertura` (removed)
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3708) in GitLab 12.9.
-> - [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78132) in GitLab 14.7.
-> - [Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/348980) in GitLab 15.0.
-
-This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78132) in GitLab 14.7 and
-[removed](https://gitlab.com/gitlab-org/gitlab/-/issues/348980) in GitLab 15.0. Use `artifacts:reports:coverage_report`
-instead.
-
-<!--- end_remove -->
-
## `artifacts:reports:coverage_report`
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/344533) in GitLab 14.10.
@@ -113,7 +99,8 @@ artifacts:
path: coverage/cobertura-coverage.xml
```
-The collected coverage report is uploaded to GitLab as an artifact.
+The collected coverage report is uploaded to GitLab as an artifact. You can use
+only one report per job.
GitLab can display the results of coverage report in the merge request
[diff annotations](../testing/test_coverage_visualization.md).
@@ -160,6 +147,30 @@ GitLab can display the results of one or more reports in:
- The [Project Vulnerability report](../../user/application_security/vulnerability_report/index.md).
- The [security dashboard](../../user/application_security/security_dashboard/index.md).
+## `artifacts:reports:cyclonedx`
+
+> [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.
+
+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:
+
+```yaml
+artifacts:
+ reports:
+ cyclonedx:
+ - gl-sbom-npm-npm.cdx.json
+ - gl-sbom-bundler-gem.cdx.json
+```
+
## `artifacts:reports:dast` **(ULTIMATE)**
The `dast` report collects [DAST vulnerabilities](../../user/application_security/dast/index.md). The collected DAST
@@ -183,7 +194,7 @@ GitLab can display the results of one or more reports in:
- The pipeline [**Security** tab](../../user/application_security/vulnerability_report/pipeline.md#view-vulnerabilities-in-a-pipeline).
- The [security dashboard](../../user/application_security/security_dashboard/index.md).
- The [Project Vulnerability report](../../user/application_security/vulnerability_report/index.md).
-- The [dependency list](../../user/application_security/dependency_list/).
+- The [dependency list](../../user/application_security/dependency_list/index.md).
## `artifacts:reports:dotenv`
@@ -329,27 +340,3 @@ GitLab can display the results of one or more reports in the merge request
[terraform widget](../../user/infrastructure/iac/mr_integration.md#output-terraform-plan-information-into-a-merge-request).
For more information, see [Output `terraform plan` information into a merge request](../../user/infrastructure/iac/mr_integration.md).
-
-## `artifacts:reports:cyclonedx`
-
-> [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.
-
-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:
-
-```yaml
-artifacts:
- reports:
- cyclonedx:
- - gl-sbom-npm-npm.cdx.json
- - gl-sbom-bundler-gem.cdx.json
-```
diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md
index 0b434e7013c..8aa3ebd4759 100644
--- a/doc/ci/yaml/index.md
+++ b/doc/ci/yaml/index.md
@@ -156,6 +156,12 @@ the time limit to resolve all files is 30 seconds.
- You can override included configuration by having the same job name or global keyword
in the `.gitlab-ci.yml` file. The two configurations are merged together, and the
configuration in the `.gitlab-ci.yml` file takes precedence over the included configuration.
+- If you rerun a:
+ - Job, the `include` files are not fetched again. All jobs in a pipeline use the configuration
+ fetched when the pipeline was created. Any changes to the source `include` files
+ do not affect job reruns.
+ - Pipeline, the `include` files are fetched again. If they changed after the last
+ pipeline run, the new pipeline uses the changed configuration.
**Related topics**:
@@ -607,6 +613,7 @@ job3:
stage: deploy
script:
- deploy_to_staging
+ environment: staging
```
In this example, `job1` and `job2` run in parallel:
@@ -1384,7 +1391,7 @@ In this example:
for the coverage number.
- If there are multiple coverage numbers found in the matched fragment, the first number is used.
- Leading zeros are removed.
-- Coverage output from [child pipelines](../pipelines/parent_child_pipelines.md)
+- Coverage output from [child pipelines](../pipelines/downstream_pipelines.md#parent-child-pipelines)
is not recorded or displayed. Check [the related issue](https://gitlab.com/gitlab-org/gitlab/-/issues/280818)
for more details.
@@ -1478,6 +1485,7 @@ test linux:
deploy:
stage: deploy
script: make deploy
+ environment: production
```
In this example, two jobs have artifacts: `build osx` and `build linux`. When `test osx` is executed,
@@ -1632,6 +1640,7 @@ these are all equivalent:
- `168 hours`
- `7 days`
- `one week`
+- `never`
**Example of `environment:auto_stop_in`**:
@@ -1896,13 +1905,10 @@ image:
#### `image:pull_policy`
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/21619) in GitLab 15.1 [with a flag](../../administration/feature_flags.md) named `ci_docker_image_pull_policy`. Disabled by default.
+> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/363186) in GitLab 15.2.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/363186) in GitLab 15.4. [Feature flag `ci_docker_image_pull_policy`](https://gitlab.com/gitlab-org/gitlab/-/issues/363186) removed.
> - Requires GitLab Runner 15.1 or later.
-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_docker_image_pull_policy`.
-The feature is not ready for production use.
-
The pull policy that the runner uses to fetch the Docker image.
**Keyword type**: Job keyword. You can use it only as part of a job or in the [`default` section](#default).
@@ -2123,6 +2129,7 @@ mac:rspec:
production:
stage: deploy
script: echo "Running production..."
+ environment: production
```
This example creates four paths of execution:
@@ -2286,14 +2293,14 @@ build_job:
**Related topics**:
-- To download artifacts between [parent-child pipelines](../pipelines/parent_child_pipelines.md),
+- To download artifacts between [parent-child pipelines](../pipelines/downstream_pipelines.md#parent-child-pipelines),
use [`needs:pipeline:job`](#needspipelinejob).
#### `needs:pipeline:job`
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/255983) in GitLab 13.7.
-A [child pipeline](../pipelines/parent_child_pipelines.md) can download artifacts from a job in
+A [child pipeline](../pipelines/downstream_pipelines.md#parent-child-pipelines) can download artifacts from a job in
its parent pipeline or another child pipeline in the same parent-child pipeline hierarchy.
**Keyword type**: Job keyword. You can use it only as part of a job.
@@ -2385,12 +2392,14 @@ deploy-job:
- job: test-job2
optional: true
- job: test-job1
+ environment: production
review-job:
stage: deploy
needs:
- job: test-job2
optional: true
+ environment: review
```
In this example:
@@ -2472,7 +2481,7 @@ when to add jobs to pipelines.
| `external` | When you use CI services other than GitLab. |
| `external_pull_requests` | When an external pull request on GitHub is created or updated (See [Pipelines for external pull requests](../ci_cd_for_external_repos/index.md#pipelines-for-external-pull-requests)). |
| `merge_requests` | For pipelines created when a merge request is created or updated. Enables [merge request pipelines](../pipelines/merge_request_pipelines.md), [merged results pipelines](../pipelines/merged_results_pipelines.md), and [merge trains](../pipelines/merge_trains.md). |
- | `pipelines` | For [multi-project pipelines](../pipelines/multi_project_pipelines.md) created by [using the API with `CI_JOB_TOKEN`](../pipelines/multi_project_pipelines.md#create-multi-project-pipelines-by-using-the-api), or the [`trigger`](#trigger) keyword. |
+ | `pipelines` | For [multi-project pipelines](../pipelines/downstream_pipelines.md#multi-project-pipelines) created by [using the API with `CI_JOB_TOKEN`](../pipelines/downstream_pipelines.md#trigger-a-multi-project-pipeline-by-using-the-api), or the [`trigger`](#trigger) keyword. |
| `pushes` | For pipelines triggered by a `git push` event, including for branches and tags. |
| `schedules` | For [scheduled pipelines](../pipelines/schedules.md). |
| `tags` | When the Git reference for a pipeline is a tag. |
@@ -2620,7 +2629,7 @@ docker build:
**Related topics**:
- [`only: changes` and `except: changes` examples](../jobs/job_control.md#onlychanges--exceptchanges-examples).
-- If you use `changes` with [only allow merge requests to be merged if the pipeline succeeds](../../user/project/merge_requests/merge_when_pipeline_succeeds.md#only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds),
+- If you use `changes` with [only allow merge requests to be merged if the pipeline succeeds](../../user/project/merge_requests/merge_when_pipeline_succeeds.md#require-a-successful-pipeline-for-merge),
you should [also use `only:merge_requests`](../jobs/job_control.md#use-onlychanges-with-merge-request-pipelines).
- [Jobs or pipelines can run unexpectedly when using `only: changes`](../jobs/job_control.md#jobs-or-pipelines-run-unexpectedly-when-using-changes).
@@ -2671,6 +2680,7 @@ pages:
- public
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
+ environment: production
```
This example moves all files from the root of the project to the `public/` directory.
@@ -2752,6 +2762,7 @@ deploystacks:
STACK: [monitoring, backup, app]
- PROVIDER: [gcp, vultr]
STACK: [data, processing]
+ environment: $PROVIDER/$STACK
```
The example generates 10 parallel `deploystacks` jobs, each with different values
@@ -3224,8 +3235,7 @@ job:
- Unlike variables in [`script`](../variables/index.md#use-cicd-variables-in-job-scripts)
sections, variables in rules expressions are always formatted as `$VARIABLE`.
- You can use `rules:if` with `include` to [conditionally include other configuration files](includes.md#use-rules-with-include).
-- In [GitLab 15.0 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/35438),
- variables on the right side of `=~` and `!~` expressions are evaluated as regular expressions.
+- CI/CD variables on the right side of `=~` and `!~` expressions are [evaluated as regular expressions](../jobs/job_control.md#store-the-regex-pattern-in-a-variable).
**Related topics**:
@@ -3643,12 +3653,9 @@ in that container.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/21619) in GitLab 15.1 [with a flag](../../administration/feature_flags.md) named `ci_docker_image_pull_policy`. Disabled by default.
> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/363186) in GitLab 15.2.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/363186) in GitLab 15.4. [Feature flag `ci_docker_image_pull_policy`](https://gitlab.com/gitlab-org/gitlab/-/issues/363186) removed.
> - Requires GitLab Runner 15.1 or later.
-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 `ci_docker_image_pull_policy`.
-
The pull policy that the runner uses to fetch the Docker image.
**Keyword type**: Job keyword. You can use it only as part of a job or in the [`default` section](#default).
@@ -3725,6 +3732,7 @@ job4:
stage: deploy
script:
- echo "This job deploys the code. It runs when the test stage completes."
+ environment: production
```
**Additional details**:
@@ -3881,54 +3889,46 @@ test:
### `trigger`
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8997) in GitLab Premium 11.8.
-> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/199224) to GitLab Free in 12.8.
+Use `trigger` to declare that a job is a "trigger job" which starts a
+[downstream pipeline](../pipelines/downstream_pipelines.md) that is either:
+
+- [A multi-project pipeline](../pipelines/downstream_pipelines.md#multi-project-pipelines).
+- [A child pipeline](../pipelines/downstream_pipelines.md#parent-child-pipelines).
-Use `trigger` to start a downstream pipeline that is either:
+Trigger jobs can use only a limited set of GitLab CI/CD configuration keywords.
+The keywords available for use in trigger jobs are:
-- [A multi-project pipeline](../pipelines/multi_project_pipelines.md).
-- [A child pipeline](../pipelines/parent_child_pipelines.md).
+- [`trigger`](#trigger).
+- [`stage`](#stage).
+- [`allow_failure`](#allow_failure).
+- [`rules`](#rules).
+- [`only` and `except`](#only--except).
+- [`when`](#when) (only with a value of `on_success`, `on_failure`, or `always`).
+- [`extends`](#extends).
+- [`needs`](#needs), but not [`needs:project`](#needsproject).
**Keyword type**: Job keyword. You can use it only as part of a job.
**Possible inputs**:
-- For multi-project pipelines, path to the downstream project. CI/CD variables
- [are supported](../variables/where_variables_can_be_used.md#gitlab-ciyml-file)
- in GitLab 15.3 and later.
-- For child pipelines, path to the child pipeline CI/CD configuration file.
+- For multi-project pipelines, the path to the downstream project. CI/CD variables [are supported](../variables/where_variables_can_be_used.md#gitlab-ciyml-file)
+ in GitLab 15.3 and later, but not [job-level persisted variables](../variables/where_variables_can_be_used.md#persisted-variables).
+ Alternatively, use [`trigger:project](#triggerproject).
+- For child pipelines, use [`trigger:include`](#triggerinclude).
-**Example of `trigger` for multi-project pipeline**:
+**Example of `trigger`**:
```yaml
-rspec:
- stage: test
- script: bundle exec rspec
-
-staging:
- stage: deploy
- trigger: my/deployment
-```
-
-**Example of `trigger` for child pipelines**:
-
-```yaml
-trigger_job:
- trigger:
- include: path/to/child-pipeline.yml
+trigger-multi-project-pipeline:
+ trigger: my-group/my-project
```
**Additional details**:
-- Jobs with `trigger` can only use a [limited set of keywords](../pipelines/multi_project_pipelines.md#define-multi-project-pipelines-in-your-gitlab-ciyml-file).
- For example, you can't run commands with [`script`](#script), [`before_script`](#before_script),
- or [`after_script`](#after_script). Also, [`environment`](#environment) is not supported with `trigger`.
- You [cannot use the API to start `when:manual` trigger jobs](https://gitlab.com/gitlab-org/gitlab/-/issues/284086).
- In [GitLab 13.5 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/201938), you
can use [`when:manual`](#when) in the same job as `trigger`. In GitLab 13.4 and
earlier, using them together causes the error `jobs:#{job-name} when should be on_success, on_failure or always`.
-- In [GitLab 13.2 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/197140/), you can
- view which job triggered a downstream pipeline in the [pipeline graph](../pipelines/index.md#visualize-pipelines).
- [Manual pipeline variables](../variables/index.md#override-a-defined-cicd-variable)
and [scheduled pipeline variables](../pipelines/schedules.md#add-a-pipeline-schedule)
are not passed to downstream pipelines by default. Use [trigger:forward](#triggerforward)
@@ -3938,12 +3938,75 @@ trigger_job:
**Related topics**:
-- [Multi-project pipeline configuration examples](../pipelines/multi_project_pipelines.md#define-multi-project-pipelines-in-your-gitlab-ciyml-file).
-- [Child pipeline configuration examples](../pipelines/parent_child_pipelines.md#examples).
+- [Multi-project pipeline configuration examples](../pipelines/downstream_pipelines.md#trigger-a-multi-project-pipeline-from-a-job-in-your-gitlab-ciyml-file).
- To run a pipeline for a specific branch, tag, or commit, you can use a [trigger token](../triggers/index.md)
to authenticate with the [pipeline triggers API](../../api/pipeline_triggers.md).
The trigger token is different than the `trigger` keyword.
+#### `trigger:include`
+
+Use `trigger:include` to declare that a job is a "trigger job" which starts a
+[child pipeline](../pipelines/downstream_pipelines.md#parent-child-pipelines).
+
+Use `trigger:include:artifact` to trigger a [dynamic child pipeline](../pipelines/downstream_pipelines.md#dynamic-child-pipelines).
+
+**Keyword type**: Job keyword. You can use it only as part of a job.
+
+**Possible inputs**:
+
+- The path to the child pipeline's configuration file.
+
+**Example of `trigger:include`**:
+
+```yaml
+trigger-child-pipeline:
+ trigger:
+ include: path/to/child-pipeline.gitlab-ci.yml
+```
+
+**Related topics**:
+
+- [Child pipeline configuration examples](../pipelines/downstream_pipelines.md#trigger-a-parent-child-pipeline).
+
+#### `trigger:project`
+
+Use `trigger:project` to declare that a job is a "trigger job" which starts a
+[multi-project pipeline](../pipelines/downstream_pipelines.md#multi-project-pipelines).
+
+By default, the multi-project pipeline triggers for the default branch. Use `trigger:branch`
+to specify a different branch.
+
+**Keyword type**: Job keyword. You can use it only as part of a job.
+
+**Possible inputs**:
+
+- The path to the downstream project. CI/CD variables [are supported](../variables/where_variables_can_be_used.md#gitlab-ciyml-file)
+ in GitLab 15.3 and later, but not [job-level persisted variables](../variables/where_variables_can_be_used.md#persisted-variables).
+
+**Example of `trigger:project`**:
+
+```yaml
+trigger-multi-project-pipeline:
+ trigger:
+ project: my-group/my-project
+```
+
+**Example of `trigger:project` for a different branch**:
+
+```yaml
+trigger-multi-project-pipeline:
+ trigger:
+ project: my-group/my-project
+ branch: development
+```
+
+**Related topics**:
+
+- [Multi-project pipeline configuration examples](../pipelines/downstream_pipelines.md#trigger-a-multi-project-pipeline-from-a-job-in-your-gitlab-ciyml-file).
+- To run a pipeline for a specific branch, tag, or commit, you can also use a [trigger token](../triggers/index.md)
+ to authenticate with the [pipeline triggers API](../../api/pipeline_triggers.md).
+ The trigger token is different than the `trigger` keyword.
+
#### `trigger:strategy`
Use `trigger:strategy` to force the `trigger` job to wait for the downstream pipeline to complete
@@ -3984,8 +4047,8 @@ successfully complete before starting.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/355572) in GitLab 15.1. [Feature flag `ci_trigger_forward_variables`](https://gitlab.com/gitlab-org/gitlab/-/issues/355572) removed.
Use `trigger:forward` to specify what to forward to the downstream pipeline. You can control
-what is forwarded to both [parent-child pipelines](../pipelines/parent_child_pipelines.md)
-and [multi-project pipelines](../pipelines/multi_project_pipelines.md).
+what is forwarded to both [parent-child pipelines](../pipelines/downstream_pipelines.md#parent-child-pipelines)
+and [multi-project pipelines](../pipelines/downstream_pipelines.md#multi-project-pipelines).
**Possible inputs**:
@@ -4062,6 +4125,7 @@ deploy_job:
stage: deploy
script:
- deploy-script --url $DEPLOY_SITE --path "/"
+ environment: production
deploy_review_job:
stage: deploy
@@ -4069,6 +4133,7 @@ deploy_review_job:
REVIEW_PATH: "/review"
script:
- deploy-review-script --url $DEPLOY_SITE --path $REVIEW_PATH
+ environment: production
```
**Additional details**:
@@ -4161,6 +4226,7 @@ deploy_job:
script:
- make deploy
when: manual
+ environment: production
cleanup_job:
stage: cleanup
@@ -4194,20 +4260,6 @@ In this example, the script:
The following keywords are deprecated.
-<!--- start_remove The following content will be removed on remove_date: '2022-08-22' -->
-
-### Globally-defined `types` (removed)
-
-The `types` keyword was deprecated in GitLab 9.0, and [removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/346823).
-Use [`stages`](#stages) instead.
-
-### Job-defined `type` (removed)
-
-The `type` keyword was deprecated in GitLab 9.0, and [removed in GitLab 15.0](https://gitlab.com/gitlab-org/gitlab/-/issues/346823).
-Use [`stage`](#stage) instead.
-
-<!--- end_remove -->
-
### Globally-defined `image`, `services`, `cache`, `before_script`, `after_script`
Defining `image`, `services`, `cache`, `before_script`, and
diff --git a/doc/ci/yaml/script.md b/doc/ci/yaml/script.md
index f1cdcf57e64..bd8d7f02a17 100644
--- a/doc/ci/yaml/script.md
+++ b/doc/ci/yaml/script.md
@@ -244,6 +244,7 @@ pages-job:
stage: deploy
script:
- curl --header 'PRIVATE-TOKEN: ${PRIVATE_TOKEN}' "https://gitlab.example.com/api/v4/projects"
+ environment: production
```
The YAML parser thinks the `:` defines a YAML keyword, and outputs the
@@ -257,6 +258,7 @@ pages-job:
stage: deploy
script:
- 'curl --header "PRIVATE-TOKEN: ${PRIVATE_TOKEN}" "https://gitlab.example.com/api/v4/projects"'
+ environment: production
```
### Job does not fail when using `&&` in a script
diff --git a/doc/ci/yaml/workflow.md b/doc/ci/yaml/workflow.md
index 743a2639c0c..d3e815c742d 100644
--- a/doc/ci/yaml/workflow.md
+++ b/doc/ci/yaml/workflow.md
@@ -176,7 +176,7 @@ include:
If a merge request displays `Checking pipeline status.`, but the message never goes
away (the "spinner" never stops spinning), it might be due to `workflow:rules`.
-This issue can happen if a project has [**Pipelines must succeed**](../../user/project/merge_requests/merge_when_pipeline_succeeds.md#only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds)
+This issue can happen if a project has [**Pipelines must succeed**](../../user/project/merge_requests/merge_when_pipeline_succeeds.md#require-a-successful-pipeline-for-merge)
enabled, but the `workflow:rules` prevent a pipeline from running for the merge request.
For example, with this workflow, merge requests cannot be merged, because no
diff --git a/doc/cloud_seed/index.md b/doc/cloud_seed/index.md
index 77b22609b12..4c56b6c4aaa 100644
--- a/doc/cloud_seed/index.md
+++ b/doc/cloud_seed/index.md
@@ -66,7 +66,7 @@ The generated service account has the following roles:
- `roles/cloudbuild.builds.builder`
- `roles/run.admin`
- `roles/storage.admin`
-- `roles/cloudsql.admin`
+- `roles/cloudsql.client`
- `roles/browser`
You can enhance security by storing CI variables in secret managers. Learn more about [secret management with GitLab](../ci/secrets/index.md).
@@ -106,6 +106,53 @@ This creates a new branch with the Cloud Run deployment pipeline (or injected in
and creates an associated merge request where the changes and deployment pipeline execution can be reviewed and merged
into the main branch.
+## Provision Cloud SQL Databases
+
+Relational database instances can be provisioned from the `Project :: Infrastructure :: Google Cloud` page. Cloud SQL is
+the underlying Google Cloud service that is used to provision the database instances.
+
+The following databases and versions are supported:
+
+- PostgreSQL: 14, 13, 12, 11, 10 and 9.6
+- MySQL: 8.0, 5.7 and 5.6
+- SQL Server
+ - 2019: Standard, Enterprise, Express and Web
+ - 2017: Standard, Enterprise, Express and Web
+
+Google Cloud pricing applies. Please refer to the [Cloud SQL pricing page](https://cloud.google.com/sql/pricing).
+
+1. [Create a database instance](#create-a-database-instance)
+1. [Database setup through a background worker](#database-setup-through-a-background-worker)
+1. [Connect to the database](#connect-to-the-database)
+1. [Managing the database instance](#managing-the-database-instance)
+
+### Create a database instance
+
+From the `Project :: Infrastructure :: Google Cloud` page, select the **Database** tab. Here you will find three
+buttons to create Postgres, MySQL, and SQL Server database instances.
+
+The database instance creation form has fields for GCP project, Git ref (branch or tag), database version and
+machine type. Upon submission, the database instance is created and the database setup is queued as a background job.
+
+### Database setup through a background worker
+
+Successful creation of the database instance triggers a background worker to perform the following tasks:
+
+- Create a database user
+- Create a database schema
+- Store the database details in the project's CI/CD variables
+
+### Connect to the database
+
+Once the database instance setup is complete, the database connection details are available as project variables. These
+can be managed through the `Project :: Settings :: CI` page and are made available to pipeline executing in the
+appropriate environment.
+
+### Managing the database instance
+
+The list of instances in the `Project :: Infrastructure :: Google Cloud :: Databases` links back to the Google Cloud
+Console. Select an instance to view the details and manage the instance.
+
## Contribute to Cloud Seed
There are several ways you can contribute to Cloud Seed:
diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md
index 0b36b9b2f2f..673ec692bf4 100644
--- a/doc/development/api_graphql_styleguide.md
+++ b/doc/development/api_graphql_styleguide.md
@@ -77,6 +77,63 @@ The complexity score of a query [can itself be queried for](../api/graphql/getti
Requests time out at 30 seconds.
+### Limit maximum field call count
+
+In some cases, you want to prevent the evaluation of a specific field on multiple parent nodes
+because it results in an N+1 query problem and there is no optimal solution. This should be
+considered an option of last resort, to be used only when methods such as
+[lookahead to preload associations](#look-ahead), or [using batching](graphql_guide/batchloader.md)
+have been considered.
+
+For example:
+
+```graphql
+# This usage is expected.
+query {
+ project {
+ environments
+ }
+}
+
+# This usage is NOT expected.
+# It results in N+1 query problem. EnvironmentsResolver can't use GraphQL batch loader in favor of GraphQL pagination.
+query {
+ projects {
+ nodes {
+ environments
+ }
+ }
+}
+```
+
+To prevent this, you can use the `Gitlab::Graphql::Limit::FieldCallCount` extension on the field:
+
+```ruby
+# This allows maximum 1 call to the `environments` field. If the field is evaluated on more than one node,
+# it raises an error.
+field :environments do
+ extension(::Gitlab::Graphql::Limit::FieldCallCount, limit: 1)
+ end
+```
+
+or you can apply the extension in a resolver class:
+
+```ruby
+module Resolvers
+ class EnvironmentsResolver < BaseResolver
+ extension(::Gitlab::Graphql::Limit::FieldCallCount, limit: 1)
+ # ...
+ end
+end
+```
+
+When you add this limit, make sure that the affected field's `description` is also updated accordingly. For example,
+
+```ruby
+field :environments,
+ description: 'Environments of the project. This field can only be resolved for one project in any single request.'
+```
+
## Breaking changes
The GitLab GraphQL API is [versionless](https://graphql.org/learn/best-practices/#versioning) which means
@@ -484,7 +541,7 @@ You can also
[change or remove Alpha items at any time](#breaking-change-exemptions) without needing to deprecate them. When the flag is removed, "release"
the schema item by removing its Alpha property to make it public.
-### Descriptions for feature flagged items
+### Descriptions for feature-flagged items
When using a feature flag to toggle the value or behavior of a schema item, the
`description` of the item must:
@@ -494,7 +551,11 @@ When using a feature flag to toggle the value or behavior of a schema item, the
- State what the field returns, or behavior is, when the feature flag is disabled (or
enabled, if more appropriate).
-Example of a feature-flagged field:
+### Examples of using feature flags
+
+#### Feature-flagged field
+
+A field value is toggled based on the feature flag state. A common use is to return `null` if the feature flag is disabled:
```ruby
field :foo, GraphQL::Types::String, null: true,
@@ -507,7 +568,10 @@ def foo
end
```
-Example of a feature-flagged argument:
+#### Feature-flagged argument
+
+An argument can be ignored, or have its value changed, based on the feature flag state.
+A common use is to ignore the argument when a feature flag is disabled:
```ruby
argument :foo, type: GraphQL::Types::String, required: false,
@@ -521,11 +585,23 @@ def resolve(args)
end
```
-### `feature_flag` property (deprecated)
+#### Feature-flagged mutation
-NOTE:
-This property is deprecated and should no longer be used. The property
-has been temporarily renamed to `_deprecated_feature_flag` and support for it will be removed in [#369202](https://gitlab.com/gitlab-org/gitlab/-/issues/369202).
+A mutation that cannot be performed due to a feature flag state is handled as a
+[non-recoverable mutation error](#failure-irrelevant-to-the-user). The error is returned at the top level:
+
+```ruby
+description 'Mutates an object. Does not mutate the object if ' \
+ '`my_feature_flag` feature flag is disabled.'
+
+def resolve(id: )
+ object = authorized_find!(id: id)
+
+ raise Gitlab::Graphql::Errors::ResourceNotAvailable, '`my_feature_flag` feature flag is disabled.' \
+ if Feature.disabled?(:my_feature_flag, object)
+ # ...
+end
+```
## Deprecating schema items
@@ -1611,7 +1687,7 @@ correctly rendered to the clients.
### Errors in mutations
-We encourage following the practice of
+We encourage following the practice of
[errors as data](https://graphql-ruby.org/mutations/mutation_errors) for mutations, which
distinguishes errors by who they are relevant to, defined by who can deal with
them.
diff --git a/doc/development/api_styleguide.md b/doc/development/api_styleguide.md
index b72ef1bffc4..7f7d78bb58e 100644
--- a/doc/development/api_styleguide.md
+++ b/doc/development/api_styleguide.md
@@ -110,14 +110,14 @@ Model.create(foo: params[:foo])
With Grape v1.3+, Array types must be defined with a `coerce_with`
block, or parameters, fails to validate when passed a string from an
-API request. See the
+API request. See the
[Grape upgrading documentation](https://github.com/ruby-grape/grape/blob/master/UPGRADING.md#ensure-that-array-types-have-explicit-coercions)
for more details.
### Automatic coercion of nil inputs
Prior to Grape v1.3.3, Array parameters with `nil` values would
-automatically be coerced to an empty Array. However, due to
+automatically be coerced to an empty Array. However, due to
[this pull request in v1.3.3](https://github.com/ruby-grape/grape/pull/2040), this
is no longer the case. For example, suppose you define a PUT `/test`
request that has an optional parameter:
@@ -259,7 +259,7 @@ In situations where the same model has multiple entities in the API
discretion with applying this scope. It may be that you optimize for the
most basic entity, with successive entities building upon that scope.
-The `with_api_entity_associations` scope also
+The `with_api_entity_associations` scope also
[automatically preloads data](https://gitlab.com/gitlab-org/gitlab/-/blob/19f74903240e209736c7668132e6a5a735954e7c/app%2Fmodels%2Ftodo.rb#L34)
for `Todo` _targets_ when returned in the [to-dos API](../api/todos.md).
diff --git a/doc/development/application_secrets.md b/doc/development/application_secrets.md
index 06a38eb238a..93e43856b34 100644
--- a/doc/development/application_secrets.md
+++ b/doc/development/application_secrets.md
@@ -17,6 +17,7 @@ This page is a development guide for application secrets.
|`db_key_base` | The base key to encrypt the data for `attr_encrypted` columns |
|`openid_connect_signing_key` | The signing key for OpenID Connect |
| `encrypted_settings_key_base` | The base key to encrypt settings files with |
+| `ci_jwt_signing_key` | The base key for encrypting the `CI_JOB_JWT` and `CI_JOB_JWT_V2` predefined CI/CD variables |
## Where the secrets are stored
diff --git a/doc/development/application_slis/index.md b/doc/development/application_slis/index.md
index 27e69ff3445..cb2eb9b8d90 100644
--- a/doc/development/application_slis/index.md
+++ b/doc/development/application_slis/index.md
@@ -25,6 +25,7 @@ to be emitted from the rails application:
## Existing SLIs
1. [`rails_request_apdex`](rails_request_apdex.md)
+1. `global_search_apdex`
## Defining a new SLI
@@ -45,8 +46,8 @@ for clarity, they define different metric names:
As shown in this example, they can share a base name (`foo` in this example). We
recommend this when they refer to the same operation.
-Before the first scrape, it is important to have
-[initialized the SLI with all possible label-combinations](https://prometheus.io/docs/practices/instrumentation/#avoid-missing-metrics).
+Before the first scrape, it is important to have
+[initialized the SLI with all possible label-combinations](https://prometheus.io/docs/practices/instrumentation/#avoid-missing-metrics).
This avoid confusing results when using these counters in calculations.
To initialize an SLI, use the `.initialize_sli` class method, for
@@ -135,10 +136,7 @@ After that, add the following information:
into the error budgets for stage groups.
- `description`: a Markdown string explaining the SLI. It will
be shown on dashboards and alerts.
-- `kind`: the kind of indicator. Only `sliDefinition.apdexKind` is supported at the moment.
- Reach out in
- [this issue](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/1395)
- if you want to implement an SLI for success or error rates.
+- `kind`: the kind of indicator. For example `sliDefinition.apdexKind`.
When done, run `make generate` to generate recording rules for
the new SLI. This command creates recordings for all services
@@ -152,9 +150,9 @@ When these changes are merged, and the aggregations in
the success ratio of the new aggregated metrics. For example:
```prometheus
-sum by (environment, stage, type)(gitlab_sli_aggregation:rails_request_apdex:apdex:success:rate_1h)
+sum by (environment, stage, type)(application_sli_aggregation:rails_request:apdex:success:rate_1h)
/
-sum by (environment, stage, type)(gitlab_sli_aggregation:rails_request_apdex:apdex:weight:rate_1h)
+sum by (environment, stage, type)(application_sli_aggregation:rails_request:apdex:weight:score_1h)
```
This shows the success ratio, which can guide you to set an
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index 10d6c0ae9c9..a813072a976 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -626,7 +626,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 Apache License v2.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 KBs to a maximum of 5TB.
#### NGINX
diff --git a/doc/development/audit_event_guide/index.md b/doc/development/audit_event_guide/index.md
index 0c66189a6f6..50d7eeed107 100644
--- a/doc/development/audit_event_guide/index.md
+++ b/doc/development/audit_event_guide/index.md
@@ -1,5 +1,5 @@
---
-stage: Manage
+stage: Govern
group: Compliance
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
---
@@ -31,7 +31,7 @@ To instrument an audit event, the following attributes should be provided:
| Attribute | Type | Required? | Description |
|:-------------|:---------------------|:----------|:------------------------------------------------------------------|
-| `name` | String | false | Action name to be audited. Used for error tracking |
+| `name` | String | false | Action name to be audited. Represents the [type of the event](#event-type-definitions). Used for error tracking |
| `author` | User | true | User who authors the change |
| `scope` | User, Project, Group | true | Scope which the audit event belongs to |
| `target` | Object | true | Target object being audited |
@@ -40,17 +40,15 @@ To instrument an audit event, the following attributes should be provided:
## How to instrument new Audit Events
-There are three ways of instrumenting audit events:
+1. Create a [YAML type definition](#add-a-new-audit-event-type) for the new audit event.
+1. Call `Gitlab::Audit::Auditor.audit`, passing an action block.
+
+The following ways of instrumenting audit events are deprecated:
- Create a new class in `ee/lib/ee/audit/` and extend `AuditEventService`
- Call `AuditEventService` after a successful action
-- Call `Gitlab::Audit::Auditor.audit` passing an action block
-
-This inconsistency leads to unexpected bugs, increases maintainer effort, and worsens the
-developer experience. Therefore, we suggest you use `Gitlab::Audit::Auditor` to
-instrument new audit events.
-With new service, we can instrument audit events in two ways:
+With `Gitlab::Audit::Auditor` service, we can instrument audit events in two ways:
- Using block for multiple events.
- Using standard method call for single events.
@@ -197,6 +195,34 @@ deactivate B
In addition to recording to the database, we also write these events to
[a log file](../../administration/logs/index.md#audit_jsonlog).
+## Event type definitions
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367847) in GitLab 15.4.
+
+All new audit events must have a type definition stored in `config/audit_events/types/` that contains a single source of truth for every auditable event in GitLab.
+
+### Add a new audit event type
+
+To add a new audit event type:
+
+1. Create a new file in `config/audit_events/types/` with the filename matching the name of the event type. For example, a definition for the event type triggered when a
+ user is added to a project might be stored in `config/audit_events/types/project_add_user.yml`.
+1. Add contents to the file that conform to the [schema](#schema) defined in `config/audit_events/types/type_schema.json`.
+1. Ensure that all calls to `Gitlab::Audit::Auditor` use the `name` defined in your file.
+
+### Schema
+
+| Field | Required | Description |
+| ----- | -------- |--------------|
+| `name` | yes | Unique, lowercase and underscored name describing the type of event. Must match the filename. |
+| `description` | yes | Human-readable description of how this event is triggered |
+| `group` | yes | Name of the group that introduced this audit event. For example, `manage::compliance` |
+| `introduced_by_issue` | yes | Issue URL that proposed the addition of this type |
+| `introduced_by_mr` | yes | MR URL that added this new type |
+| `milestone` | yes | Milestone in which this type was added |
+| `saved_to_database` | yes | Indicate whether to persist events to database and JSON logs |
+| `streamed` | yes | Indicate that events should be streamed to external services (if configured) |
+
## Event streaming
All events where the entity is a `Group` or `Project` are recorded in the audit log, and also streamed to one or more
diff --git a/doc/development/auto_devops.md b/doc/development/auto_devops.md
index 55ab234cc68..b9b8770207e 100644
--- a/doc/development/auto_devops.md
+++ b/doc/development/auto_devops.md
@@ -20,7 +20,7 @@ based on your project contents. When Auto DevOps is enabled for a
project, the user does not need to explicitly include any pipeline configuration
through a [`.gitlab-ci.yml` file](../ci/yaml/index.md).
-In the absence of a `.gitlab-ci.yml` file, the
+In the absence of a `.gitlab-ci.yml` file, the
[Auto DevOps CI/CD template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml)
is used implicitly to configure the pipeline for the project. This
template is a top-level template that includes other sub-templates,
diff --git a/doc/development/backend/create_source_code_be/index.md b/doc/development/backend/create_source_code_be/index.md
index e1ee78731de..a1322b3fa25 100644
--- a/doc/development/backend/create_source_code_be/index.md
+++ b/doc/development/backend/create_source_code_be/index.md
@@ -33,6 +33,9 @@ GitLab Shell handles Git SSH sessions for GitLab and modifies the list of author
For more information, [refer to the README](https://gitlab.com/gitlab-org/gitlab-shell/-/blob/main/README.md).
for GitLab Shell.
+To learn about the reasoning behind our creation of `gitlab-sshd`, read the blog post
+[Why we implemented our own SSHD solution](https://about.gitlab.com/blog/2022/08/17/why-we-have-implemented-our-own-sshd-solution-on-gitlab-sass/).
+
## GitLab Rails
### Gitaly touch points
diff --git a/doc/development/backend/ruby_style_guide.md b/doc/development/backend/ruby_style_guide.md
index a9fee02a15a..6ba5b8dd2c5 100644
--- a/doc/development/backend/ruby_style_guide.md
+++ b/doc/development/backend/ruby_style_guide.md
@@ -20,6 +20,17 @@ See also [guidelines for reusing abstractions](../reusing_abstractions.md).
Everything listed here can be [reopened for discussion](https://about.gitlab.com/handbook/values/#disagree-commit-and-disagree).
+## String literals quoting
+
+Due to the sheer amount of work to rectify, we do not care whether string
+literals are single, or double quoted.
+
+Previous discussions include:
+
+- <https://gitlab.com/gitlab-org/gitlab-foss/-/issues/44234>
+- <https://gitlab.com/gitlab-org/gitlab-foss/-/issues/36076>
+- <https://gitlab.com/gitlab-org/gitlab/-/issues/198046>
+
## Instance variable access using `attr_reader`
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/52351) in GitLab 14.1.
@@ -72,3 +83,106 @@ end
Public attributes should only be used if they are accessed outside of the class.
There is not a strong opinion on what strategy is used when attributes are only
accessed internally, as long as there is consistency in related code.
+
+## Newlines style guide
+
+This style guide recommends best practices for newlines in Ruby code.
+
+### Rule: separate code with newlines only to group together related logic
+
+```ruby
+# bad
+def method
+ issue = Issue.new
+
+ issue.save
+
+ render json: issue
+end
+```
+
+```ruby
+# good
+def method
+ issue = Issue.new
+ issue.save
+
+ render json: issue
+end
+```
+
+### Rule: separate code and block with newlines
+
+#### Newline before block
+
+```ruby
+# bad
+def method
+ issue = Issue.new
+ if issue.save
+ render json: issue
+ end
+end
+```
+
+```ruby
+# good
+def method
+ issue = Issue.new
+
+ if issue.save
+ render json: issue
+ end
+end
+```
+
+### Rule: Newline after block
+
+```ruby
+# bad
+def method
+ if issue.save
+ issue.send_email
+ end
+ render json: issue
+end
+```
+
+```ruby
+# good
+def method
+ if issue.save
+ issue.send_email
+ end
+
+ render json: issue
+end
+```
+
+#### Exception: no need for newline when code block starts or ends right inside another code block
+
+```ruby
+# bad
+def method
+
+ if issue
+
+ if issue.valid?
+ issue.save
+ end
+
+ end
+
+end
+```
+
+```ruby
+# good
+def method
+ if issue
+ if issue.valid?
+ issue.save
+ end
+ end
+end
+```
diff --git a/doc/development/build_test_package.md b/doc/development/build_test_package.md
index 4645bd02d9e..97dd24fc522 100644
--- a/doc/development/build_test_package.md
+++ b/doc/development/build_test_package.md
@@ -13,7 +13,7 @@ pipeline that can be used to trigger a pipeline in the Omnibus GitLab repository
that will create:
- A deb package for Ubuntu 16.04, available as a build artifact, and
-- A Docker image, which is pushed to the
+- A Docker image, which is pushed to the
[Omnibus GitLab container registry](https://gitlab.com/gitlab-org/omnibus-gitlab/container_registry)
(images titled `gitlab-ce` and `gitlab-ee` respectively and image tag is the
commit which triggered the pipeline).
diff --git a/doc/development/changelog.md b/doc/development/changelog.md
index c5b234069e3..c0296a6d75e 100644
--- a/doc/development/changelog.md
+++ b/doc/development/changelog.md
@@ -190,7 +190,7 @@ editor. Once closed, Git presents you with a new text editor instance to edit
the commit message of commit B. Add the trailer, then save and quit the editor.
If all went well, commit B is now updated.
-For more information about interactive rebases, take a look at
+For more information about interactive rebases, take a look at
[the Git documentation](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History).
---
diff --git a/doc/development/cicd/index.md b/doc/development/cicd/index.md
index e8e116037de..7a2dfa01d1e 100644
--- a/doc/development/cicd/index.md
+++ b/doc/development/cicd/index.md
@@ -37,7 +37,7 @@ On the left side we have the events that can trigger a pipeline based on various
- When a [merge request is created or updated](../../ci/pipelines/merge_request_pipelines.md).
- When an MR is added to a [Merge Train](../../ci/pipelines/merge_trains.md#merge-trains).
- A [scheduled pipeline](../../ci/pipelines/schedules.md).
-- When project is [subscribed to an upstream project](../../ci/pipelines/multi_project_pipelines.md#trigger-a-pipeline-when-an-upstream-project-is-rebuilt).
+- When project is [subscribed to an upstream project](../../ci/pipelines/index.md#trigger-a-pipeline-when-an-upstream-project-is-rebuilt).
- When [Auto DevOps](../../topics/autodevops/index.md) is enabled.
- When GitHub integration is used with [external pull requests](../../ci/ci_cd_for_external_repos/index.md#pipelines-for-external-pull-requests).
- When an upstream pipeline contains a [bridge job](../../ci/yaml/index.md#trigger) which triggers a downstream pipeline.
diff --git a/doc/development/cicd/pipeline_wizard.md b/doc/development/cicd/pipeline_wizard.md
index 7a0b70bd8e8..227a49d85db 100644
--- a/doc/development/cicd/pipeline_wizard.md
+++ b/doc/development/cicd/pipeline_wizard.md
@@ -58,7 +58,7 @@ consists of 2-3 steps, for a total of 3-4 steps visible to the user.
```yaml
# ~/pipeline_wizard/templates/my_template.yml
-
+id: gitlab/my-template
title: Set up my specific tech pipeline
description: Here's two or three introductory sentences that help the user understand what this wizard is going to set up.
steps:
@@ -156,12 +156,13 @@ Webpack does not parse it as an Object.
In the root element of the template file, you can define the following properties:
-| Name | Required | Type | Description |
-|---------------|------------------------|--------|---------------------------------------------------------------------------------------|
-| `title` | **{check-circle}** Yes | string | The page title as displayed to the user. It becomes an `h1` heading above the wizard. |
-| `description` | **{check-circle}** Yes | string | The page description as displayed to the user. |
-| `filename` | **{dotted-circle}** No | string | The name of the file that is being generated. Defaults to `.gitlab-ci.yml`. |
-| `steps` | **{check-circle}** Yes | list | A list of [step definitions](#step-reference). |
+| Name | Required | Type | Description |
+|---------------|------------------------|--------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `id` | **{check-circle}** Yes | string | A unique template ID. This ID should follow a namespacing pattern, with a forward slash `/` as separator. Templates committed to GitLab source code should always begin with `gitlab`. For example: `gitlab/my-template` |
+| `title` | **{check-circle}** Yes | string | The page title as displayed to the user. It becomes an `h1` heading above the wizard. |
+| `description` | **{check-circle}** Yes | string | The page description as displayed to the user. |
+| `filename` | **{dotted-circle}** No | string | The name of the file that is being generated. Defaults to `.gitlab-ci.yml`. |
+| `steps` | **{check-circle}** Yes | list | A list of [step definitions](#step-reference). |
### `step` Reference
diff --git a/doc/development/cicd/templates.md b/doc/development/cicd/templates.md
index 4ea7a9d960c..d0c56fb18bc 100644
--- a/doc/development/cicd/templates.md
+++ b/doc/development/cicd/templates.md
@@ -465,7 +465,10 @@ To add a metric definition for a new template:
- `name:` and `performance_indicator_type:`: Delete (not needed).
- `introduced_by_url:`: The URL of the MR adding the template.
- `data_source:`: Set to `redis_hll`.
- - All other fields that have no values: Set to empty strings (`''`).
+ - `description`: Add a short description of what this metric counts, for example: `Count of pipelines using the latest Auto Deploy template`
+ - `product_*`: Set to [section, stage, group, and feature category](https://about.gitlab.com/handbook/product/categories/#devops-stages)
+ as per the [metrics dictionary guide](../service_ping/metrics_dictionary.md#metrics-definition-and-validation).
+ If you are unsure what to use for these keywords, you can ask for help in the merge request.
- Add the following to the end of each file:
```yaml
diff --git a/doc/development/code_intelligence/index.md b/doc/development/code_intelligence/index.md
index a89730383e4..87697a5e252 100644
--- a/doc/development/code_intelligence/index.md
+++ b/doc/development/code_intelligence/index.md
@@ -35,7 +35,7 @@ sequenceDiagram
Workhorse-->>-Runner: request results
```
-1. The CI/CD job generates a document in an LSIF format (usually `dump.lsif`) using
+1. The CI/CD job generates a document in an LSIF format (usually `dump.lsif`) using
[an indexer](https://lsif.dev) for the language of a project. The format
[describes](https://github.com/sourcegraph/sourcegraph/blob/main/doc/code_intelligence/explanations/writing_an_indexer.md)
interactions between a method or function and its definitions or references. The
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index e9e546c6f9b..c320540401f 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -46,19 +46,28 @@ Read more about [author responsibilities](#the-responsibility-of-the-merge-reque
Domain experts are team members who have substantial experience with a specific technology,
product feature, or area of the codebase. Team members are encouraged to self-identify as
-domain experts and add it to their [team profiles](https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/master/data/team_members/person/README.md).
+domain experts and add it to their
+[team profiles](https://about.gitlab.com/handbook/engineering/workflow/code-review/#how-to-self-identify-as-a-domain-expert).
When self-identifying as a domain expert, it is recommended to assign the MR changing the `.yml` file to be merged by an already established Domain Expert or a corresponding Engineering Manager.
We make the following assumption with regards to automatically being considered a domain expert:
-- Team members working in a specific stage/group (for example, create: source code) are considered domain experts for that area of the app they work on
-- Team members working on a specific feature (for example, search) are considered domain experts for that feature
+- Team members working in a specific stage/group (for example, create: source code) are considered domain experts for that area of the app they work on.
+- Team members working on a specific feature (for example, search) are considered domain experts for that feature.
We default to assigning reviews to team members with domain expertise.
When a suitable [domain expert](#domain-experts) isn't available, you can choose any team member to review the MR, or simply follow the [Reviewer roulette](#reviewer-roulette) recommendation.
-Team members' domain expertise can be viewed on the [engineering projects](https://about.gitlab.com/handbook/engineering/projects/) page or on the [GitLab team page](https://about.gitlab.com/company/team/).
+To find a domain expert:
+
+- View the list of team members who work in the [stage or group](https://about.gitlab.com/handbook/product/categories/#devops-stages) related to the merge request.
+- View team members' domain expertise on the [engineering projects](https://about.gitlab.com/handbook/engineering/projects/) page or on the [GitLab team page](https://about.gitlab.com/company/team/). Domains are self-identified, so use your judgment to map the changes on your merge request to a domain.
+- Look for team members who have contributed to the files in the merge request. View the logs by running `git log <file>`.
+- Look for team members who have reviewed the files. You can find the relevant merge request by:
+ 1. Getting the commit SHA by using `git log <file>`.
+ 1. Navigating to `https://gitlab.com/gitlab-org/gitlab/-/commit/<SHA>`.
+ 1. Selecting the related merge request shown for the commit.
### Reviewer roulette
@@ -92,6 +101,12 @@ page, with these behaviors:
- 3ï¸âƒ£ - `:three:`
- 4ï¸âƒ£ - `:four:`
- 5ï¸âƒ£ - `:five:`
+
+ Review requests for merge requests that do not target the default branch of any
+ project under the [security group](https://gitlab.com/gitlab-org/security/) are
+ not counted. These MRs are usually backports, and maintainers or reviewers usually
+ do not need much time reviewing them.
+
- Team members whose Slack or [GitLab status](../user/profile/index.md#set-your-current-status) emoji
is 🔵 `:large_blue_circle:` are more likely to be picked. This applies to both reviewers and trainee maintainers.
- Reviewers with 🔵 `:large_blue_circle:` are two times as likely to be picked as other reviewers.
@@ -117,7 +132,7 @@ As an experiment, we want to introduce a `local` reviewer status for database re
focusing on work from a team/stage, but not outside of it. This helps to focus and build great domain
knowledge. We are not introducing changes to the reviewer roulette till we evaluate the impact and feedback from this
experiment. We ask to respect reviewers who decline reviews based on their focus on `local` reviews. For tracking purposes,
-please use in your personal YAML file entry: `- reviewer database local` instead of `- reviewer database`.
+please use in your personal YAML file entry: `- reviewer database local` instead of `- reviewer database`.
### Approval guidelines
@@ -125,40 +140,26 @@ As described in the section on the responsibility of the maintainer below, you
are recommended to get your merge request approved and merged by maintainers
with [domain expertise](#domain-experts).
-1. If your merge request includes `~backend` changes (*1*), it must be
- **approved by a [backend maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_maintainers_backend)**.
-1. If your merge request includes database migrations or changes to expensive queries (*2*), it must be
- **approved by a [database maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_maintainers_database)**.
- Read the [database review guidelines](database_review.md) for more details.
-1. If your merge request includes `~frontend` changes (*1*), it must be
- **approved by a [frontend maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_maintainers_frontend)**.
-1. If your merge request includes (`~UX`) user-facing changes (*3*), it must be
- **approved by a [Product Designer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_reviewers_UX)**.
- See the [design and user interface guidelines](contributing/design.md) for details.
-1. If your merge request includes adding a new JavaScript library (*1*)...
- - If the library significantly increases the
- [bundle size](https://gitlab.com/gitlab-org/frontend/playground/webpack-memory-metrics/-/blob/master/doc/report.md), it must
- be **approved by a [frontend foundations member](https://about.gitlab.com/direction/ecosystem/foundations/)**.
- - If the license used by the new library hasn't been approved for use in
- GitLab, the license must be **approved by a [legal department member](https://about.gitlab.com/handbook/legal/)**.
- More information about license compatibility can be found in our
- [GitLab Licensing and Compatibility documentation](licensing.md).
-1. If your merge request includes a new dependency or a file system change, it must be
- **approved by a [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.
-1. If your merge request includes documentation changes, it must be **approved
- by a [Technical writer](https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments)**,
- based on assignments in the appropriate [DevOps stage group](https://about.gitlab.com/handbook/product/categories/#devops-stages).
-1. If your merge request includes changes to development guidelines, follow the [review process](development_processes.md#development-guidelines-review) and get the approvals accordingly.
-1. If your merge request includes end-to-end **and** non-end-to-end changes (*4*), it must be **approved
- by a [Software Engineer in Test](https://about.gitlab.com/handbook/engineering/quality/#individual-contributors)**.
-1. If your merge request only includes end-to-end changes (*4*) **or** if the MR author is a [Software Engineer in Test](https://about.gitlab.com/handbook/engineering/quality/#individual-contributors), it must be **approved by a [Quality maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_maintainers_qa)**
-1. If your merge request includes a new or updated [application limit](https://about.gitlab.com/handbook/product/product-processes/#introducing-application-limits), it must be **approved by a [product manager](https://about.gitlab.com/company/team/)**.
-1. If your merge request includes Product Intelligence (telemetry or analytics) changes, it should be reviewed and approved by a [Product Intelligence engineer](https://gitlab.com/gitlab-org/analytics-section/product-intelligence/engineers).
-1. If your merge request includes an addition of, or changes to a [Feature spec](testing_guide/testing_levels.md#frontend-feature-tests), it must be **approved by a [Quality maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_maintainers_qa) or [Quality reviewer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_reviewers_qa)**.
-1. If your merge request introduces a new service to GitLab (Puma, Sidekiq, Gitaly are examples), it must be **approved by a [product manager](https://about.gitlab.com/company/team/)**. See the [process for adding a service component to GitLab](adding_service_component.md) for details.
-1. If your merge request includes changes related to authentication or authorization, it must be **approved by a [Manage:Authentication and Authorization team member](https://about.gitlab.com/company/team/)**. Check the [code review section on the group page](https://about.gitlab.com/handbook/engineering/development/dev/manage/authentication-and-authorization/#additional-considerations) for more details. Patterns for files known to require review from the team are listed in the in the `Authentication and Authorization` section of the [`CODEOWNERS`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/CODEOWNERS) file, and the team will be listed in the approvers section of all merge requests that modify these files.
-
-- (*1*): Specs other than JavaScript specs are considered `~backend` code. Haml markup is considered `~frontend` code. However, Ruby code within Haml templates is considered `~backend` code.
+| If your merge request includes | It must be approved by a |
+| ------------------------------- | ------------------------ |
+| `~backend` changes (*1*) | [Backend maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_maintainers_backend). |
+| `~database` migrations or changes to expensive queries (*2*) | [Database maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_maintainers_database). Refer to the [database review guidelines](database_review.md) for more details. |
+| `~workhorse` changes | [Workhorse maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_maintainers_workhorse). |
+| `~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/ecosystem/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). |
+| `~documentation` changes | [Technical writer](https://about.gitlab.com/handbook/engineering/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). |
+| Only End-to-end changes (*4*) **or** if the MR author is a [Software Engineer in Test](https://about.gitlab.com/handbook/engineering/quality/#individual-contributors) | [Quality maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_maintainers_qa). |
+| A new or updated [application limit](https://about.gitlab.com/handbook/product/product-processes/#introducing-application-limits) | [Product manager](https://about.gitlab.com/company/team/). |
+| Product Intelligence (telemetry or analytics) changes | [Product Intelligence engineer](https://gitlab.com/gitlab-org/analytics-section/product-intelligence/engineers). |
+| An addition of, or changes to a [Feature spec](testing_guide/testing_levels.md#frontend-feature-tests) | [Quality maintainer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_maintainers_qa) or [Quality reviewer](https://about.gitlab.com/handbook/engineering/projects/#gitlab_reviewers_qa). |
+| A new service to GitLab (Puma, Sidekiq, Gitaly are examples) | [Product manager](https://about.gitlab.com/company/team/). See the [process for adding a service component to GitLab](adding_service_component.md) for details. |
+| Changes related to authentication or authorization | [Manage:Authentication and Authorization team member](https://about.gitlab.com/company/team/). Check the [code review section on the group page](https://about.gitlab.com/handbook/engineering/development/dev/manage/authentication-and-authorization/#additional-considerations) for more details. Patterns for files known to require review from the team are listed in the in the `Authentication and Authorization` section of the [`CODEOWNERS`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/CODEOWNERS) file, and the team will be listed in the approvers section of all merge requests that modify these files. |
+
+- (*1*): Specs other than JavaScript specs are considered `~backend` code. Haml markup is considered `~frontend` code. However, Ruby code within Haml templates is considered `~backend` code. When in doubt, request both a frontend and backend review.
- (*2*): We encourage you to seek guidance from a database maintainer if your merge
request is potentially introducing expensive queries. It is most efficient to comment
on the line of code in question with the SQL queries so they can give their advice.
@@ -333,33 +334,29 @@ Because a maintainer's job only depends on their knowledge of the overall GitLab
codebase, and not that of any specific domain, they can review, approve, and merge
merge requests from any team and in any product area.
+Maintainers are the DRI of assuring that the acceptance criteria of a merge request are reasonably met.
+In general, [quality is everyone’s responsibility](https://about.gitlab.com/handbook/engineering/quality/),
+but maintainers of an MR are held responsible for **ensuring** that an MR meets those general quality standards.
+
+If a maintainer feels that an MR is substantial enough, or requires a [domain expert](#domain-experts),
+maintainers have the discretion to request a review from another reviewer, or maintainer. Here are some
+examples of maintainers proactively doing this during review:
+
+- <https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82708#note_872325561>
+- <https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38003#note_387981596>
+- <https://gitlab.com/gitlab-org/gitlab/-/merge_requests/14017#note_178828088>
+
Maintainers do their best to also review the specifics of the chosen solution
before merging, but as they are not necessarily [domain experts](#domain-experts), they may be poorly
placed to do so without an unreasonable investment of time. In those cases, they
defer to the judgment of the author and earlier reviewers, in favor of focusing on their primary responsibilities.
-If a maintainer feels that an MR is substantial enough that it warrants a review from a [domain expert](#domain-experts),
-and it is unclear whether a domain expert have been involved in the reviews to date,
-they may request a [domain expert's](#domain-experts) review before merging the MR.
-
If a developer who happens to also be a maintainer was involved in a merge request
as a reviewer, it is recommended that they are not also picked as the maintainer to ultimately approve and merge it.
Maintainers should check before merging if the merge request is approved by the
required approvers. If still awaiting further approvals from others, remove yourself as a reviewer then `@` mention the author and explain why in a comment. Stay as reviewer if you're merging the code.
-Maintainers must check before merging if the merge request is introducing new
-vulnerabilities, by inspecting the list in the merge request
-[Security Widget](../user/application_security/index.md).
-When in doubt, a [Security Engineer](https://about.gitlab.com/company/team/) can be involved. The list of detected
-vulnerabilities must be either empty or containing:
-
-- dismissed vulnerabilities in case of false positives
-- vulnerabilities converted to issues
-
-Maintainers should **never** dismiss vulnerabilities to "empty" the list,
-without duly verifying them.
-
Note that certain merge requests may target a stable branch. These are rare
events. These types of merge requests cannot be merged by the Maintainer.
Instead, these should be sent to the [Release Manager](https://about.gitlab.com/community/release-managers/).
@@ -418,7 +415,7 @@ first time.
codebase. Thorough descriptions help all reviewers understand your request
and test effectively.
- If you know your change depends on another being merged first, note it in the
- description and set a [merge request dependency](../user/project/merge_requests/merge_request_dependencies.md).
+ description and set a [merge request dependency](../user/project/merge_requests/dependencies.md).
- Be grateful for the reviewer's suggestions. ("Good call. I'll make that change.")
- Don't take it personally. The review is of the code, not of you.
- Explain why the code exists. ("It's like that because of these reasons. Would
@@ -489,7 +486,7 @@ experience, refactors the existing code). Then:
optionally resolve within the merge request or follow-up at a later stage.
- There's a [Chrome/Firefox add-on](https://gitlab.com/conventionalcomments/conventional-comments-button) which you can use to apply [Conventional Comment](https://conventionalcomments.org/) prefixes.
- Ensure there are no open dependencies. Check [linked issues](../user/project/issues/related_issues.md) for blockers. Clarify with the authors
-if necessary. If blocked by one or more open MRs, set an [MR dependency](../user/project/merge_requests/merge_request_dependencies.md).
+if necessary. If blocked by one or more open MRs, set an [MR dependency](../user/project/merge_requests/dependencies.md).
- After a round of line notes, it can be helpful to post a summary note such as
"Looks good to me", or "Just a couple things to address."
- Let the author know if changes are required following your review.
@@ -507,24 +504,25 @@ Before taking the decision to merge:
before merging. A comment must be posted if the MR is merged with any failed job.
- If the MR contains both Quality and non-Quality-related changes, the MR should be merged by the relevant maintainer for user-facing changes (backend, frontend, or database) after the Quality related changes are approved by a Software Engineer in Test.
-If a merge request is fundamentally ready, but needs only trivial fixes (such as
-typos), consider demonstrating a [bias for action](https://about.gitlab.com/handbook/values/#bias-for-action)
-by making those changes directly without going back to the author. You can do this by
-using the [suggest changes](../user/project/merge_requests/reviews/suggestions.md) feature to apply
-your own suggestions to the merge request. Note that:
-
-- If the changes are not straightforward, please prefer allowing the author to make the change.
-- **Before applying suggestions**, edit the merge request to make sure
- [squash and merge](../user/project/merge_requests/squash_and_merge.md#squash-and-merge)
- is enabled, otherwise, the pipeline's Danger job fails.
- - If a merge request does not have squash and merge enabled, and it
- has more than one commit, then see the note below about rewriting
- commit history.
-
-Authors are not authorized to merge their own merge requests and need to seek another maintainer to merge.
+At least one maintainer must approve an MR before it can be merged. MR authors and
+people who add commits to an MR are not authorized to approve the merge request,
+so they must seek a maintainer who has not contributed to the MR to approve the MR before it can be merged.
+
This policy is in place to satisfy the CHG-04 control of the GitLab
[Change Management Controls](https://about.gitlab.com/handbook/engineering/security/security-assurance/security-compliance/guidance/change-management.html).
+To implement this policy in `gitlab-org/gitlab`, we have enabled the following
+settings to ensure MRs get an approval from a top-level CODEOWNERS maintainer:
+
+- [Prevent approval by author](../user/project/merge_requests/approvals/settings.md#prevent-approval-by-author).
+- [Prevent approvals by users who add commits](../user/project/merge_requests/approvals/settings.md#prevent-approvals-by-users-who-add-commits).
+- [Prevent editing approval rules in merge requests](../user/project/merge_requests/approvals/settings.md#prevent-editing-approval-rules-in-merge-requests).
+- [Remove all approvals when commits are added to the source branch](../user/project/merge_requests/approvals/settings.md#remove-all-approvals-when-commits-are-added-to-the-source-branch)
+
+ There are scenarios such as rebasing locally or applying suggestions that are considered
+ the same as adding a commit and could reset existing approvals. Approvals are not removed
+ when rebasing from the UI or with the [`/rebase` quick action](../user/project/quick_actions.md).
+
When ready to merge:
WARNING:
diff --git a/doc/development/contributing/index.md b/doc/development/contributing/index.md
index 6999ffe810e..2ff51e765a3 100644
--- a/doc/development/contributing/index.md
+++ b/doc/development/contributing/index.md
@@ -99,10 +99,14 @@ If you would like to contribute to GitLab:
could speed them up.
- Consult the [Contribution Flow](#contribution-flow) section to learn the process.
-If you have any questions or need help visit [Getting Help](https://about.gitlab.com/get-help/) to
-learn how to communicate with GitLab. We have a [Gitter channel for contributors](https://gitter.im/gitlab/contributors),
-however we favor
-[asynchronous communication](https://about.gitlab.com/handbook/communication/#internal-communication) over real time communication.
+### Communication channels
+
+If you have any questions or need help, visit [Getting Help](https://about.gitlab.com/get-help/) to learn how to
+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](https://discord.gg/S4cwz9sR8u) where you can
+find other contributors in the `#contributors` channel.
Thanks for your contribution!
diff --git a/doc/development/contributing/merge_request_workflow.md b/doc/development/contributing/merge_request_workflow.md
index faa1642d50a..0e9ac569558 100644
--- a/doc/development/contributing/merge_request_workflow.md
+++ b/doc/development/contributing/merge_request_workflow.md
@@ -35,12 +35,23 @@ and see the [Development section](../../index.md) for the required guidelines.
## Merge request guidelines for contributors
-If you find an issue, please submit a merge request with a fix or improvement, if
-you can, and include tests. If you don't know how to fix the issue but can write a test
-that exposes the issue, we will accept that as well. In general, bug fixes that
-include a regression test are merged quickly, while new features without proper
-tests might be slower to receive feedback. The workflow to make a merge
-request is as follows:
+If you find an issue, please submit a merge request with a fix or improvement,
+if you can, and include tests.
+
+If the change is non-trivial, we encourage you to
+start a discussion with [a product manager or a member of the team](https://about.gitlab.com/handbook/product/categories/).
+You can do
+this by tagging them in an MR before submitting the code for review. Talking
+to team members can be helpful when making design decisions. Communicating the
+intent behind your changes can also help expedite merge request reviews.
+
+If
+you don't know how to fix the issue but can write a test that exposes the
+issue, we will accept that as well. In general, bug fixes that include a
+regression test are merged quickly. New features without proper tests
+might be slower to receive feedback.
+
+To create a merge request:
1. [Fork](../../user/project/repository/forking_workflow.md) the project into
your personal namespace (or group) on GitLab.com.
@@ -199,48 +210,27 @@ 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.
-If a regression occurs, we prefer you revert the change. We break the definition of done into two phases: [MR Merge](#mr-merge) and [Production use](#production-use).
+If a regression occurs, we prefer you revert the change.
Your contribution is *incomplete* until you have made sure it meets all of these
requirements.
-### MR Merge
+### Functionality
-1. Clear title and description explaining the relevancy of the contribution.
1. Working and clean code that is commented where needed.
1. The change is evaluated to [limit the impact of far-reaching work](https://about.gitlab.com/handbook/engineering/development/#reducing-the-impact-of-far-reaching-work).
-1. Testing:
-
- - [Unit, integration, and system tests](../testing_guide/index.md) that all pass
- on the CI server.
- - Peer member testing is optional but recommended when the risk of a change is high.
- This includes when the changes are [far-reaching](https://about.gitlab.com/handbook/engineering/development/#reducing-the-impact-of-far-reaching-work)
- or are for [components critical for security](../code_review.md#security).
- - Description includes any steps or setup required to ensure reviewers can view the changes you've made (for example, include any information about feature flags).
- - Regressions and bugs are covered with tests that reduce the risk of the issue happening
- again.
- - For tests that use Capybara, read
- [how to write reliable, asynchronous integration tests](https://thoughtbot.com/blog/write-reliable-asynchronous-integration-tests-with-capybara).
- - [Black-box tests/end-to-end tests](../testing_guide/testing_levels.md#black-box-tests-at-the-system-level-aka-end-to-end-tests)
- added if required. Please contact [the quality team](https://about.gitlab.com/handbook/engineering/quality/#teams)
- with any questions.
- - The change is tested in a review app where possible and if appropriate.
-1. In case of UI changes:
-
- - Use available components from the GitLab Design System,
- [Pajamas](https://design.gitlab.com/).
- - The MR must include *Before* and *After* screenshots if UI changes are made.
- - If the MR changes CSS classes, please include the list of affected pages, which
- can be found by running `grep css-class ./app -R`.
+1. [Performance guidelines](../merge_request_performance_guidelines.md) have been followed.
+1. [Secure coding guidelines](https://gitlab.com/gitlab-com/gl-security/security-guidelines) have been followed.
+1. [Application and rate limit guidelines](../merge_request_application_and_rate_limit_guidelines.md) have been followed.
+1. [Documented](../documentation/index.md) in the `/doc` directory.
1. If your MR touches code that executes shell commands, reads or opens files, or
handles paths to files on disk, make sure it adheres to the
[shell command guidelines](../shell_commands.md)
1. [Code changes should include observability instrumentation](../code_review.md#observability-instrumentation).
1. If your code needs to handle file storage, see the [uploads documentation](../uploads/index.md).
-1. If your merge request adds one or more migrations:
- - Make sure to execute all migrations on a fresh database before the MR is reviewed.
- If the review leads to large changes in the MR, execute the migrations again
- after the review is complete.
- - Write tests for more complex migrations.
+1. If your merge request adds one or more migrations, make sure to execute all migrations on a fresh database
+ before the MR is reviewed.
+ If the review leads to large changes in the MR, execute the migrations again
+ after the review is complete.
1. If your merge request adds new validations to existing models, to make sure the
data processing is backwards compatible:
@@ -259,12 +249,37 @@ requirements.
[self-managed instances](../../subscriptions/self_managed/index.md), so keep
that in mind for any data implications with your merge request.
+### Testing
+
+1. [Unit, integration, and system tests](../testing_guide/index.md) that all pass
+ on the CI server.
+1. Peer member testing is optional but recommended when the risk of a change is high.
+ This includes when the changes are [far-reaching](https://about.gitlab.com/handbook/engineering/development/#reducing-the-impact-of-far-reaching-work)
+ or are for [components critical for security](../code_review.md#security).
+1. Regressions and bugs are covered with tests that reduce the risk of the issue happening
+ again.
+1. For tests that use Capybara, read
+ [how to write reliable, asynchronous integration tests](https://thoughtbot.com/blog/write-reliable-asynchronous-integration-tests-with-capybara).
+1. [Black-box tests/end-to-end tests](../testing_guide/testing_levels.md#black-box-tests-at-the-system-level-aka-end-to-end-tests)
+ added if required. Please contact [the quality team](https://about.gitlab.com/handbook/engineering/quality/#teams)
+ with any questions.
+1. The change is tested in a review app where possible and if appropriate.
1. Code affected by a feature flag is covered by [automated tests with the feature flag enabled and disabled](../feature_flags/index.md#feature-flags-in-tests), or both
states are tested as part of peer member testing or as part of the rollout plan.
-1. [Performance guidelines](../merge_request_performance_guidelines.md) have been followed.
-1. [Secure coding guidelines](https://gitlab.com/gitlab-com/gl-security/security-guidelines) have been followed.
-1. [Application and rate limit guidelines](../merge_request_application_and_rate_limit_guidelines.md) have been followed.
-1. [Documented](../documentation/index.md) in the `/doc` directory.
+1. If your merge request adds one or more migrations, write tests for more complex migrations.
+
+### UI changes
+
+1. Use available components from the GitLab Design System,
+ [Pajamas](https://design.gitlab.com/).
+1. The MR must include *Before* and *After* screenshots if UI changes are made.
+1. If the MR changes CSS classes, please include the list of affected pages, which
+ can be found by running `grep css-class ./app -R`.
+
+### Description of changes
+
+1. Clear title and description explaining the relevancy of the contribution.
+1. Description includes any steps or setup required to ensure reviewers can view the changes you've made (for example, include any information about feature flags).
1. [Changelog entry added](../changelog.md), if necessary.
1. If your merge request introduces changes that require additional steps when
installing GitLab from source, add them to `doc/install/installation.md` in
@@ -274,10 +289,13 @@ requirements.
`doc/update/upgrading_from_source.md` in the same merge request. If these
instructions are specific to a version, add them to the "Version specific
upgrading instructions" section.
-1. Reviewed by relevant reviewers, and all concerns are addressed for Availability, Regressions, and Security. Documentation reviews should take place as soon as possible, but they should not block a merge request.
+
+### Approval
+
1. The [MR acceptance checklist](../code_review.md#acceptance-checklist) has been checked as confirmed in the MR.
1. Create an issue in the [infrastructure issue tracker](https://gitlab.com/gitlab-com/gl-infra/reliability/-/issues) to inform the Infrastructure department when your contribution is changing default settings or introduces a new setting, if relevant.
1. An agreed-upon [rollout plan](https://about.gitlab.com/handbook/engineering/development/processes/rollout-plans/).
+1. Reviewed by relevant reviewers, and all concerns are addressed for Availability, Regressions, and Security. Documentation reviews should take place as soon as possible, but they should not block a merge request.
1. Your merge request has at least 1 approval, but depending on your changes
you might need additional approvals. Refer to the [Approval guidelines](../code_review.md#approval-guidelines).
- You don't have to select any specific approvers, but you can if you really want
@@ -286,6 +304,8 @@ requirements.
### Production use
+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.
diff --git a/doc/development/contributing/style_guides.md b/doc/development/contributing/style_guides.md
index 7a4ebbdbadf..2e696cf517b 100644
--- a/doc/development/contributing/style_guides.md
+++ b/doc/development/contributing/style_guides.md
@@ -146,13 +146,20 @@ reduces the aforementioned [bike-shedding](https://en.wiktionary.org/wiki/bikesh
To that end, we encourage creation of new RuboCop rules in the codebase.
-We currently maintain Cops across several Ruby code bases, and not all of them are
+We maintain Cops across several Ruby code bases, and not all of them are
specific to the GitLab application.
When creating a new cop that could be applied to multiple applications, we encourage you
to add it to our [GitLab Styles](https://gitlab.com/gitlab-org/gitlab-styles) gem.
If the Cop targets rules that only apply to the main GitLab application,
it should be added to [GitLab](https://gitlab.com/gitlab-org/gitlab) instead.
+#### RuboCop node pattern
+
+When creating [node patterns](https://docs.rubocop.org/rubocop-ast/node_pattern.html) to match
+Ruby's AST, you can use [`scripts/rubocop-parse`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/scripts/rubocop-parse)
+to display the AST of a Ruby expression, in order to help you create the matcher.
+See also [!97024](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97024).
+
### Resolving RuboCop exceptions
When the number of RuboCop exceptions exceed the default [`exclude-limit` of 15](https://docs.rubocop.org/rubocop/1.2/usage/basic_usage.html#command-line-flags),
@@ -222,6 +229,34 @@ We're following [Ciro Santilli's Markdown Style Guide](https://cirosantilli.com/
See the dedicated [Documentation Style Guide](../documentation/styleguide/index.md).
+### Guidelines for good practices
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36576/) in GitLab 13.2 as GitLab Development documentation.
+
+*Good practice* examples demonstrate encouraged ways of writing code while
+comparing with examples of practices to avoid. These examples are labeled as
+*Bad* or *Good*. In GitLab development guidelines, when presenting the cases,
+it's recommended to follow a *first-bad-then-good* strategy. First demonstrate
+the *Bad* practice (how things *could* be done, which is often still working
+code), and then how things *should* be done better, using a *Good* example. This
+is typically an improved example of the same code.
+
+Consider the following guidelines when offering examples:
+
+- First, offer the *Bad* example, and then the *Good* one.
+- When only one bad case and one good case is given, use the same code block.
+- When more than one bad case or one good case is offered, use separated code
+ blocks for each. With many examples being presented, a clear separation helps
+ the reader to go directly to the good part. Consider offering an explanation
+ (for example, a comment, or a link to a resource) on why something is bad
+ practice.
+- Better and best cases can be considered part of the good cases' code block.
+ In the same code block, precede each with comments: `# Better` and `# Best`.
+
+Although the bad-then-good approach is acceptable for the GitLab development
+guidelines, do not use it for user documentation. For user documentation, use
+*Do* and *Don't*. For examples, see the [Pajamas Design System](https://design.gitlab.com/content/punctuation/).
+
## Python
See the dedicated [Python Development Guidelines](../python_guide/index.md).
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 8a8fe3c0a1e..4be3296b2bb 100644
--- a/doc/development/database/add_foreign_key_to_existing_column.md
+++ b/doc/development/database/add_foreign_key_to_existing_column.md
@@ -71,7 +71,7 @@ Migration file for adding `NOT VALID` foreign key:
```ruby
class AddNotValidForeignKeyToEmailsUser < Gitlab::Database::Migration[2.0]
def up
- add_concurrent_foreign_key :emails, :users, on_delete: :cascade, validate: false
+ add_concurrent_foreign_key :emails, :users, column: :user_id, on_delete: :cascade, validate: false
end
def down
diff --git a/doc/development/database/adding_database_indexes.md b/doc/development/database/adding_database_indexes.md
index 8abd7c8298e..774703bd54f 100644
--- a/doc/development/database/adding_database_indexes.md
+++ b/doc/development/database/adding_database_indexes.md
@@ -328,8 +328,8 @@ asynchronously during weekend hours. Due to generally lower traffic and fewer de
index destruction can proceed at a lower level of risk.
1. [Schedule the index to be removed](#schedule-the-index-to-be-removed).
-1. [Verify the MR was deployed and the index exists in production](#verify-the-mr-was-deployed-and-the-index-exists-in-production).
-1. [Add a migration to create the index synchronously](#add-a-migration-to-create-the-index-synchronously).
+1. [Verify the MR was deployed and the index exists in production](#verify-the-mr-was-deployed-and-the-index-no-longer-exists-in-production).
+1. [Add a migration to destroy the index synchronously](#add-a-migration-to-destroy-the-index-synchronously).
### Schedule the index to be removed
@@ -357,21 +357,21 @@ to remove them.
You must test the database index changes locally before creating a merge request.
-### Verify the MR was deployed and the index exists in production
+### Verify the MR was deployed and the index no longer exists in production
You can verify if the MR was deployed to GitLab.com with
`/chatops run auto_deploy status <merge_sha>`. To verify the existence of
the index, you can:
- Use a meta-command in `#database-lab`, for example: `\d <index_name>`.
- - Make sure the index is not [`invalid`](https://www.postgresql.org/docs/12/sql-createindex.html#:~:text=The%20psql%20%5Cd%20command%20will%20report%20such%20an%20index%20as%20INVALID).
+- Make sure the index no longer exists
- Ask someone in `#database` to check if the index exists.
- If you have access, you can verify directly on production or in a
production clone.
### Add a migration to destroy the index synchronously
-After you verify the index exists in the production database, create a second
+After you verify the index no longer exists in the production database, create a second
merge request that removes the index synchronously. The schema changes must be
updated and committed to `structure.sql` in this second merge request.
The synchronous migration results in a no-op on GitLab.com, but you should still add the
@@ -379,7 +379,7 @@ migration as expected for other installations. For example, to
create the second migration for the previous asynchronous example:
**WARNING:**
-Verify that the index no longer exist in production before merging a second migration with `remove_concurrent_index_by_name`.
+Verify that the index no longer exists in production before merging a second migration with `remove_concurrent_index_by_name`.
If the second migration is deployed before the index has been destroyed,
the index is destroyed synchronously when the second migration executes.
@@ -395,7 +395,7 @@ def up
end
def down
- add_concurrent_index :ci_builds, :some_column, INDEX_NAME
+ add_concurrent_index :ci_builds, :some_column, name: INDEX_NAME
end
```
@@ -403,7 +403,7 @@ end
To test changes for removing an index, use the asynchronous index helpers on your local environment:
-1. Enable the feature flags by running `Feature.enable(:database_async_index_destruction)` and `Feature.enable(:database_reindexing)` in the Rails console.
+1. Enable the feature flags by running `Feature.enable(:database_reindexing)` in the Rails console.
1. Run `bundle exec rails db:migrate` which should create an entry in the `postgres_async_indexes` table.
1. Run `bundle exec rails gitlab:db:reindex` destroy the index asynchronously.
1. To verify the index, open the PostgreSQL console by using the [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/main/doc/howto/postgresql.md)
diff --git a/doc/development/database/batched_background_migrations.md b/doc/development/database/batched_background_migrations.md
index edb22fcf436..192cd0d3e49 100644
--- a/doc/development/database/batched_background_migrations.md
+++ b/doc/development/database/batched_background_migrations.md
@@ -233,7 +233,7 @@ class CopyColumnUsingBackgroundMigrationJob < BatchedMigrationJob
end
```
-### Additional filters
+## Additional filters
By default, when creating background jobs to perform the migration, batched background migrations
iterate over the full specified table. This iteration is done using the
@@ -276,6 +276,10 @@ In the second (filtered) example, we know exactly 100 will be updated with each
end
```
+ NOTE:
+ For EE migrations that define `scope_to`, ensure the module extends `ActiveSupport::Concern`.
+ Otherwise, records are processed without taking the scope into consideration.
+
1. In the post-deployment migration, enqueue the batched background migration:
```ruby
diff --git a/doc/development/database/ci_mirrored_tables.md b/doc/development/database/ci_mirrored_tables.md
index 06f0087fafe..1d285e607fa 100644
--- a/doc/development/database/ci_mirrored_tables.md
+++ b/doc/development/database/ci_mirrored_tables.md
@@ -10,9 +10,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
As part of the database [decomposition work](https://gitlab.com/groups/gitlab-org/-/epics/6168),
which had the goal of splitting the single database GitLab is using, into two databases: `main` and
-`ci`, came the big challenge of
+`ci`, came the big challenge of
[removing all joins between the `main` and the `ci` tables](multiple_databases.md#removing-joins-between-ci-and-non-ci-tables).
-That is because PostgreSQL doesn't support joins between tables that belong to different databases.
+That is because PostgreSQL doesn't support joins between tables that belong to different databases.
However, some core application models in the main database are queried very often by the CI side.
For example:
diff --git a/doc/development/database/client_side_connection_pool.md b/doc/development/database/client_side_connection_pool.md
index 3cd0e836a8d..3143391a553 100644
--- a/doc/development/database/client_side_connection_pool.md
+++ b/doc/development/database/client_side_connection_pool.md
@@ -10,7 +10,7 @@ Ruby processes accessing the database through
ActiveRecord, automatically calculate the connection-pool size for the
process based on the concurrency.
-Because of the way [Ruby on Rails manages database connections](#connection-lifecycle),
+Because of the way [Ruby on Rails manages database connections](#connection-lifecycle),
it is important that we have at
least as many connections as we have threads. While there is a 'pool'
setting in [`database.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/database.yml.postgresql), it is not very practical because you need to
@@ -28,7 +28,7 @@ because connections are instantiated lazily.
## Troubleshooting connection-pool issues
-The connection-pool usage can be seen per environment in the
+The connection-pool usage can be seen per environment in the
[connection-pool saturation dashboard](https://dashboards.gitlab.net/d/alerts-sat_rails_db_connection_pool/alerts-rails_db_connection_pool-saturation-detail?orgId=1).
If the connection-pool is too small, this would manifest in
diff --git a/doc/development/database/database_debugging.md b/doc/development/database/database_debugging.md
index 5921dc942f2..591e526cc96 100644
--- a/doc/development/database/database_debugging.md
+++ b/doc/development/database/database_debugging.md
@@ -4,7 +4,7 @@ group: Database
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
---
-# Troubleshooting and Debugging Database
+# Troubleshooting and debugging the database
This section is to help give some copy-pasta you can use as a reference when you
run into some head-banging database problems.
diff --git a/doc/development/database/loose_foreign_keys.md b/doc/development/database/loose_foreign_keys.md
index 8dbccf048d7..0af12939629 100644
--- a/doc/development/database/loose_foreign_keys.md
+++ b/doc/development/database/loose_foreign_keys.md
@@ -221,7 +221,7 @@ ON DELETE CASCADE;
```
The migration must run after the `DELETE` trigger is installed and the loose
-foreign key definition is deployed. As such, it must be a
+foreign key definition is deployed. As such, it must be a
[post-deployment migration](post_deployment_migrations.md) dated after the migration for the
trigger. If the foreign key is deleted earlier, there is a good chance of
introducing data inconsistency which needs manual cleanup:
diff --git a/doc/development/database/multiple_databases.md b/doc/development/database/multiple_databases.md
index 31fc454f8a7..034a2c2e438 100644
--- a/doc/development/database/multiple_databases.md
+++ b/doc/development/database/multiple_databases.md
@@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Multiple Databases
To allow GitLab to scale further we
-[decomposed the GitLab application database into multiple databases](https://gitlab.com/groups/gitlab-org/-/epics/6168).
+[decomposed the GitLab application database into multiple databases](https://gitlab.com/groups/gitlab-org/-/epics/6168).
The two databases are `main` and `ci`. GitLab supports being run with either one database or two databases.
On GitLab.com we are using two separate databases.
diff --git a/doc/development/database/not_null_constraints.md b/doc/development/database/not_null_constraints.md
index 9b3d017b09f..cd2adc3ca28 100644
--- a/doc/development/database/not_null_constraints.md
+++ b/doc/development/database/not_null_constraints.md
@@ -53,8 +53,13 @@ end
## Add a `NOT NULL` constraint to an existing column
-Adding `NOT NULL` to existing database columns requires multiple steps split into at least two
-different releases:
+Adding `NOT NULL` to existing database columns usually requires multiple steps split into at least two
+different releases. If your table is small enough that you don't need to
+use a background migration, you can include all these in the same merge
+request. We recommend to use separate migrations to reduce
+transaction durations.
+
+The steps required are:
1. Release `N.M` (current release)
diff --git a/doc/development/database/ordering_table_columns.md b/doc/development/database/ordering_table_columns.md
index 7cd3d4fb208..a16df6a4499 100644
--- a/doc/development/database/ordering_table_columns.md
+++ b/doc/development/database/ordering_table_columns.md
@@ -117,7 +117,7 @@ divided into fixed size chunks as follows:
This means that excluding the variable sized data and tuple header, we need at
least 8 * 6 = 48 bytes per row.
-We can optimise this by using the following column order instead:
+We can optimize this by using the following column order instead:
| Column | Type | Size |
|:--------------|:----------------------------|:---------|
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 e2e1191018b..4b5d1fc8f72 100644
--- a/doc/development/database/strings_and_the_text_data_type.md
+++ b/doc/development/database/strings_and_the_text_data_type.md
@@ -148,7 +148,7 @@ to update the `title_html` with a title that has more than 1024 characters, the
a database error.
Adding or removing a constraint to an existing attribute requires that any application changes are
-deployed _first_,
+deployed _first_,
otherwise servers still in the old version of the application
[may try to update the attribute with invalid values](../multi_version_compatibility.md#ci-artifact-uploads-were-failing).
For these reasons, `add_text_limit` should run in a post-deployment migration.
diff --git a/doc/development/database/understanding_explain_plans.md b/doc/development/database/understanding_explain_plans.md
index 446a84d5232..c3cb408b35f 100644
--- a/doc/development/database/understanding_explain_plans.md
+++ b/doc/development/database/understanding_explain_plans.md
@@ -252,7 +252,7 @@ A scan on an index that required retrieving some data from the table.
Bitmap scans fall between sequential scans and index scans. These are typically
used when we would read too much data from an index scan, but too little to
-perform a sequential scan. A bitmap scan uses what is known as a
+perform a sequential scan. A bitmap scan uses what is known as a
[bitmap index](https://en.wikipedia.org/wiki/Bitmap_index) to perform its work.
The [source code of PostgreSQL](https://gitlab.com/postgres/postgres/blob/REL_11_STABLE/src/include/nodes/plannodes.h#L441)
@@ -295,9 +295,9 @@ because the previous node produced 36 rows.
This means that nested loops can quickly slow the query down if the various
child nodes keep producing many rows.
-## Optimising queries
+## Optimizing queries
-With that out of the way, let's see how we can optimise a query. Let's use the
+With that out of the way, let's see how we can optimize a query. Let's use the
following query as an example:
```sql
@@ -453,7 +453,7 @@ this works is that now PostgreSQL no longer needs to apply a `Filter`, as the
index only contains `twitter` values that are not empty.
Keep in mind that you shouldn't just add partial indexes every time you want to
-optimise a query. Every index has to be updated for every write, and they may
+optimize a query. Every index has to be updated for every write, and they may
require quite a bit of space, depending on the amount of indexed data. As a
result, first check if there are any existing indexes you may be able to reuse.
If there aren't any, check if you can perhaps slightly change an existing one to
@@ -471,10 +471,10 @@ buffer numbers. [Database Lab Engine](#database-lab-engine) guarantees that the
identical to production (and overall number of buffers is the same as on production),
but difference in cache state and I/O speed may lead to different timings.
-## Queries that can't be optimised
+## Queries that can't be optimized
-Now that we have seen how to optimise a query, let's look at another query that
-we might not be able to optimise:
+Now that we have seen how to optimize a query, let's look at another query that
+we might not be able to optimize:
```sql
EXPLAIN (ANALYZE, BUFFERS)
@@ -546,7 +546,7 @@ improve this query, other than _not_ running it at all.
What is important here is that while some may recommend to straight up add an
index the moment you see a sequential scan, it is _much more important_ to first
understand what your query does, how much data it retrieves, and so on. After
-all, you can not optimise something you do not understand.
+all, you can not optimize something you do not understand.
### Cardinality and selectivity
@@ -567,7 +567,7 @@ using an index is not worth it, because it would produce almost no unique rows.
## Rewriting queries
-So the above query can't really be optimised as-is, or at least not much. But
+So the above query can't really be optimized as-is, or at least not much. But
what if we slightly change the purpose of it? What if instead of retrieving all
projects with `visibility_level` 0 or 20, we retrieve those that a user
interacted with somehow?
diff --git a/doc/development/database_review.md b/doc/development/database_review.md
index 2decd304103..14d73437c36 100644
--- a/doc/development/database_review.md
+++ b/doc/development/database_review.md
@@ -116,9 +116,9 @@ the following preparations into account.
- Ensure that the Database Dictionary is updated as [documented](database/database_dictionary.md).
- Make migrations reversible by using the `change` method or include a `down` method when using `up`.
- Include either a rollback procedure or describe how to rollback changes.
-- Add the output of both migrating (`db:migrate`) and rolling back (`db:rollback`) for all migrations into the MR description.
- - Ensure the down method reverts the changes in `db/structure.sql`.
- - Update the migration output whenever you modify the migrations during the review process.
+- Check that the [`db:check-migrations`](database/dbcheck-migrations-job.md) pipeline job has run successfully and the migration rollback behaves as expected.
+ - Ensure the `db:check-schema` job has run successfully and no unexpected schema changes are introduced in a rollback. This job may only trigger a warning if the schema was changed.
+ - Verify that the previously mentioned jobs continue to succeed whenever you modify the migrations during the review process.
- Add tests for the migration in `spec/migrations` if necessary. See [Testing Rails migrations at GitLab](testing_guide/testing_migrations_guide.md) for more details.
- When [high-traffic](https://gitlab.com/gitlab-org/gitlab/-/blob/master/rubocop/rubocop-migrations.yml#L3) tables are involved in the migration, use the [`enable_lock_retries`](migration_style_guide.md#retry-mechanism-when-acquiring-database-locks) method to enable lock-retries. Review the relevant [examples in our documentation](migration_style_guide.md#usage-with-transactional-migrations) for use cases and solutions.
- Ensure RuboCop checks are not disabled unless there's a valid reason to.
@@ -181,9 +181,11 @@ Include in the MR description:
- When providing query plans, make sure it hits enough data:
- You can use a GitLab production replica to test your queries on a large scale,
through the `#database-lab` Slack channel or through [ChatOps](database/understanding_explain_plans.md#chatops).
- - Usually, the `gitlab-org` namespace (`namespace_id = 9970`) and the
- `gitlab-org/gitlab-foss` (`project_id = 13083`) or the `gitlab-org/gitlab` (`project_id = 278964`)
- projects provide enough data to serve as a good example.
+ - To produce a query plan with enough data, you can use the IDs of:
+ - The `gitlab-org` namespace (`namespace_id = 9970`), for queries involving a group.
+ - The `gitlab-org/gitlab-foss` (`project_id = 13083`) or the `gitlab-org/gitlab` (`project_id = 278964`) projects, for queries involving a project.
+ - The `gitlab-qa` user (`user_id = 1614863`), for queries involving a user.
+ - Optionally, you can also use your own `user_id`, or the `user_id` of a user with a long history within the project or group being used to generate the query plan.
- That means that no query plan should return 0 records or less records than the provided limit (if a limit is included). If a query is used in batching, a proper example batch with adequate included results should be identified and provided.
- If your queries belong to a new feature in GitLab.com and thus they don't return data in production:
- You may analyze the query and to provide the plan from a local environment.
diff --git a/doc/development/deprecation_guidelines/index.md b/doc/development/deprecation_guidelines/index.md
index f0364f60d38..1e9d3ebda77 100644
--- a/doc/development/deprecation_guidelines/index.md
+++ b/doc/development/deprecation_guidelines/index.md
@@ -11,16 +11,23 @@ to GitLab features.
## Terminology
+<!--
+If updating these definitions, be sure to update them in the handbook as well:
+https://about.gitlab.com/handbook/product/gitlab-the-product/#definitions
+-->
+
**Deprecation**:
+- Required before ending support for a feature or removing a feature.
- Feature not recommended for use.
- Development restricted to Priority 1 / Severity 1 bug fixes.
- Will be removed in a future major release.
-- Begins after a deprecation announcement outlining an end-of-support date.
+- Begins after a deprecation announcement outlining an end-of-support or removal date.
- Ends after the end-of-support date or removal date has passed.
**End of Support**:
+- Optional step before removal.
- Feature usage strongly discouraged.
- No support or fixes provided.
- No longer tested internally.
@@ -28,6 +35,10 @@ to GitLab features.
- Begins after an end-of-support date has passed.
- Ends after all relevant code has been removed.
+[Announcing an End of Support period](https://about.gitlab.com/handbook/marketing/blog/release-posts/#announcing-an-end-of-support-period)
+should only be used in special circumstances and is not recommended for general use.
+Most features should be deprecated and then removed.
+
**Removal**:
- Feature usage impossible.
diff --git a/doc/development/development_processes.md b/doc/development/development_processes.md
index e199aedd3f5..27ebe98bc63 100644
--- a/doc/development/development_processes.md
+++ b/doc/development/development_processes.md
@@ -67,7 +67,7 @@ Some changes affect more than one group. For example:
- Changes to [code review guidelines](code_review.md).
- Changes to [commit message guidelines](contributing/merge_request_workflow.md#commit-messages-guidelines).
-- Changes to guidelines in [feature flags in development of GitLab](feature_flags/).
+- Changes to guidelines in [feature flags in development of GitLab](feature_flags/index.md).
- Changes to [feature flags documentation guidelines](documentation/feature_flags.md).
In these cases, use the following workflow:
diff --git a/doc/development/distributed_tracing.md b/doc/development/distributed_tracing.md
index f49d024095d..9d62f2061ca 100644
--- a/doc/development/distributed_tracing.md
+++ b/doc/development/distributed_tracing.md
@@ -73,13 +73,13 @@ In this example, we have the following hypothetical values:
- `driver`: the driver such a Jaeger.
- `param_name`, `param_value`: these are driver specific configuration values. Configuration
- parameters for Jaeger are documented [further on in this document](#2-configure-the-gitlab_tracing-environment-variable)
+ parameters for Jaeger are documented [further on in this document](#2-configure-the-gitlab_tracing-environment-variable)
they should be URL encoded.
Multiple values should be separated by `&` characters like a URL.
## Using Jaeger in the GitLab Development Kit
-The first tracing implementation that GitLab supports is Jaeger, and the
+The first tracing implementation that GitLab supports is Jaeger, and the
[GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit/) supports distributed tracing with
Jaeger out-of-the-box.
@@ -116,7 +116,7 @@ Jaeger has many configuration options, but is very easy to start in an "all-in-o
memory for trace storage (and is therefore non-persistent). The main advantage of "all-in-one" mode
being ease of use.
-For more detailed configuration options, refer to the
+For more detailed configuration options, refer to the
[Jaeger documentation](https://www.jaegertracing.io/docs/1.9/getting-started/).
#### Using Docker
@@ -201,7 +201,7 @@ If `GITLAB_TRACING` is not configured correctly, this issue is logged:
```
By default, GitLab ships with the Jaeger tracer, but other tracers can be included at compile time.
-Details of how this can be done are included in the
+Details of how this can be done are included in the
[LabKit tracing documentation](https://pkg.go.dev/gitlab.com/gitlab-org/labkit/tracing).
If no log messages about tracing are emitted, the `GITLAB_TRACING` environment variable is likely
diff --git a/doc/development/documentation/feature_flags.md b/doc/development/documentation/feature_flags.md
index 89e54183e50..87d2930dcb5 100644
--- a/doc/development/documentation/feature_flags.md
+++ b/doc/development/documentation/feature_flags.md
@@ -24,7 +24,8 @@ When you document feature flags, you must:
## Add version history text
-When the state of a flag changes (for example, disabled by default to enabled by default), add the change to the version history.
+When the state of a flag changes (for example, disabled by default to enabled by default), add the change to the
+[version history](versions.md#add-a-version-history-item).
Possible version history entries are:
diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md
index ee439e93011..73c1874f09e 100644
--- a/doc/development/documentation/index.md
+++ b/doc/development/documentation/index.md
@@ -5,7 +5,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
description: Learn how to contribute to GitLab Documentation.
---
-# GitLab Documentation guidelines
+# GitLab documentation
The GitLab documentation is [intended as the single source of truth (SSOT)](https://about.gitlab.com/handbook/documentation/) for information about how to configure, use, and troubleshoot GitLab. The documentation contains use cases and usage instructions for every GitLab feature, organized by product area and subject. This includes topics and workflows that span multiple GitLab features and the use of GitLab with other applications.
@@ -42,7 +42,7 @@ run only the jobs that match the type of contribution. If your contribution cont
**only** documentation changes, then only documentation-related jobs run, and
the pipeline completes much faster than a code contribution.
-If you are submitting documentation-only changes to Omnibus or Charts,
+If you are submitting documentation-only changes to Omnibus, Charts, or Operator,
the fast pipeline is not determined automatically. Instead, create branches for
docs-only merge requests using the following guide:
@@ -240,9 +240,9 @@ Every GitLab instance includes documentation at `/help` (`https://gitlab.example
that matches the version of the instance. For example, <https://gitlab.com/help>.
The documentation available online at <https://docs.gitlab.com> is deployed every
-four hours from the default branch of [GitLab, Omnibus, Runner, and Charts](#source-files-and-rendered-web-locations).
+hour from the default branch of [GitLab, Omnibus, Runner, and Charts](#source-files-and-rendered-web-locations).
After a merge request that updates documentation is merged, it is available online
-in 4 hours or less.
+in an hour or less.
However, it's only available at `/help` on self-managed instances in the next released
version. The date an update is merged can impact which self-managed release the update
@@ -380,6 +380,47 @@ is made, Danger Bot leaves a comment with further instructions about the documen
process. This is configured in the `Dangerfile` in the GitLab repository under
[/danger/documentation/](https://gitlab.com/gitlab-org/gitlab/-/tree/master/danger/documentation).
+## Help and feedback section
+
+This section ([introduced](https://gitlab.com/gitlab-org/gitlab-docs/-/merge_requests/319) in GitLab 11.4)
+is displayed at the end of each document and can be omitted by adding a key into
+the front matter:
+
+```yaml
+---
+feedback: false
+---
+```
+
+The default is to leave it there. If you want to omit it from a document, you
+must check with a technical writer before doing so.
+
+## Disqus
+
+We have integrated the docs site with Disqus (introduced by
+[!151](https://gitlab.com/gitlab-org/gitlab-docs/-/merge_requests/151)),
+allowing our users to post comments.
+
+To omit only the comments from the feedback section, use the following key in
+the front matter:
+
+```yaml
+---
+comments: false
+---
+```
+
+We're hiding comments only in main index pages, such as [the main documentation index](../../index.md),
+since its content is too broad to comment on. Before omitting Disqus, you must
+check with a technical writer.
+
+Note that after adding `feedback: false` to the front matter, it will omit
+Disqus, therefore, don't add both keys to the same document.
+
+The click events in the feedback section are tracked with Google Tag Manager.
+The conversions can be viewed on Google Analytics by navigating to
+**Behavior > Events > Top events > docs**.
+
## Automatic screenshot generator
You can now set up an automatic screenshot generator to take and compress screenshots with the
diff --git a/doc/development/documentation/redirects.md b/doc/development/documentation/redirects.md
index 4c748924c67..1bc697f2878 100644
--- a/doc/development/documentation/redirects.md
+++ b/doc/development/documentation/redirects.md
@@ -16,12 +16,7 @@ description: Learn how to contribute to GitLab Documentation.
# Redirects in GitLab documentation
When you move, rename, or delete a page, you must add a redirect. Redirects reduce
-how often users get 404s when visiting the documentation site from out-of-date links, like:
-
-- Bookmarks
-- Links from external sites
-- Links from old blog posts
-- Links in the documentation site global navigation
+how often users get 404s when they visit the documentation site from out-of-date links.
Add a redirect to ensure:
@@ -36,9 +31,11 @@ Add a redirect to ensure:
Be sure to assign a technical writer to any merge request that moves, renames, or deletes a page.
Technical Writers can help with any questions and can review your change.
+## Types of redirects
+
There are two types of redirects:
-- [Redirect added into the documentation files themselves](#add-a-redirect), for users who
+- [Redirects added into the documentation files themselves](#redirect-to-a-page-that-already-exists), for users who
view the docs in `/help` on self-managed instances. For example,
[`/help` on GitLab.com](https://gitlab.com/help). These must be added in the same
MR that renames or moves a doc. Redirects to internal pages expire after three months
@@ -52,96 +49,109 @@ Expired redirect files are removed from the documentation projects by the
[`clean_redirects` Rake task](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/main/doc/raketasks.md#clean-up-redirects),
as part of the Technical Writing team's [monthly tasks](https://gitlab.com/gitlab-org/technical-writing/-/blob/main/.gitlab/issue_templates/tw-monthly-tasks.md).
-## Add a redirect
+## Redirect to a page that already exists
+
+To redirect a page to another page in the same repository:
+
+1. In the Markdown file that you want to direct to a new location:
+
+ - Delete all of the content.
+ - Add this content:
+
+ ```markdown
+ ---
+ redirect_to: '../newpath/to/file/index.md'
+ remove_date: 'YYYY-MM-DD'
+ ---
+
+ This document was moved to [another location](../path/to/file/index.md).
+
+ <!-- This redirect file can be deleted after <YYYY-MM-DD>. -->
+ <!-- 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 -->
+ ```
+
+ - Replace both instances of `../newpath/to/file/index.md` with the new file path.
+ - Replace both instances of `YYYY-MM-DD` with the expiration date, as explained in the template.
+
+1. If the page has Disqus comments, follow [the steps for pages with Disqus comments](#redirections-for-pages-with-disqus-comments).
+1. If the page had images that aren't used on any other pages, delete them.
-NOTE:
-If the renamed page is new, you can sometimes skip the following steps and ask a
-Technical Writer to manually add the redirect to [`redirects.yaml`](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/main/content/_data/redirects.yaml).
-For example, if you add a new page and then rename it before it's added to a release
-on the 18th. The old page is not in any version's `/help` section, so a technical writer
-can jump straight to the [Pages redirect](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/main/doc/maintenance.md#pages-redirects).
+After your changes are committed, search for and update all links that point to the old file:
-To add a redirect:
+- In <https://gitlab.com/gitlab-com/www-gitlab-com>, search for full URLs:
-1. In the repository (`gitlab`, `gitlab-runner`, `omnibus-gitlab`, or `charts`),
- create a new documentation file. Don't delete the old one. The easiest
- way is to copy it. For example:
+ ```shell
+ grep -r "docs.gitlab.com/ee/path/to/file.html" .
+ ```
- ```shell
- cp doc/user/search/old_file.md doc/api/new_file.md
+- In <https://gitlab.com/gitlab-org/gitlab-docs/-/tree/master/content/_data>,
+ search the navigation bar configuration files for the path with `.html`:
+
+ ```shell
+ grep -r "path/to/file.html" .
```
-1. Add the redirect code to the old documentation file by running the
- following Rake task. The first argument is the path of the old file,
- and the second argument is the path of the new file:
+- In any of the four internal projects, search for links in the docs
+ and codebase. Search for all variations, including full URL and just the path.
+ For example, go to the root directory of the `gitlab` project and run:
- - To redirect to a page in the same project, use relative paths and
- the `.md` extension. Both old and new paths start from the same location.
- In the following example, both paths are relative to `doc/`:
+ ```shell
+ grep -r "docs.gitlab.com/ee/path/to/file.html" .
+ grep -r "path/to/file.html" .
+ grep -r "path/to/file.md" .
+ grep -r "path/to/file" .
+ ```
- ```shell
- bundle exec rake "gitlab:docs:redirect[doc/user/search/old_file.md, doc/api/new_file.md]"
- ```
+ You might need to try variations of relative links, such as `../path/to/file` or
+ `../file` to find every case.
- - To redirect to a page in a different project or site, use the full URL (with `https://`) :
+### Move a file's location
- ```shell
- bundle exec rake "gitlab:docs:redirect[doc/user/search/old_file.md, https://example.com]"
- ```
+If you want to move a file from one location to another, you do not move it.
+Instead, you duplicate the file, and add the redirect code to the old file.
- - Alternatively, you can omit the arguments and be prompted to enter the values:
+1. Create the new file.
+1. Copy the contents of the old file to the new one.
+1. In the old file, delete all the content.
+1. In the old file, add the redirect code and follow the rest of the steps in
+ the [Redirect to a page that already exists](#redirect-to-a-page-that-already-exists) topic.
- ```shell
- bundle exec rake gitlab:docs:redirect
- ```
+## Use code to add a redirect
- If you don't want to use the Rake task, you can use the following template:
+If you prefer to use a script to create the redirect:
- ```markdown
- ---
- redirect_to: '../newpath/to/file/index.md'
- remove_date: 'YYYY-MM-DD'
- ---
+Add the redirect code to the old documentation file by running the
+following Rake task. The first argument is the path of the old file,
+and the second argument is the path of the new file:
- This document was moved to [another location](../path/to/file/index.md).
+- To redirect to a page in the same project, use relative paths and
+ the `.md` extension. Both old and new paths start from the same location.
+ In the following example, both paths are relative to `doc/`:
- <!-- This redirect file can be deleted after <YYYY-MM-DD>. -->
- <!-- 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 -->
- ```
+ ```shell
+ bundle exec rake "gitlab:docs:redirect[doc/user/search/old_file.md, doc/api/new_file.md]"
+ ```
- - Replace both instances of `../newpath/to/file/index.md` with the new file path.
- - Replace `YYYY-MM-DD` with the expiry date, as explained in the template.
-
-1. If the documentation page being moved has any Disqus comments, follow the steps
- described in [Redirections for pages with Disqus comments](#redirections-for-pages-with-disqus-comments).
-1. Open a merge request with your changes. If a documentation page
- you're removing includes images that aren't used
- with any other documentation pages, be sure to use your merge request to delete
- those images from the repository.
-1. Assign the merge request to a technical writer for review and merge.
-1. Search for links to the old documentation file. You must find and update all
- links that point to the old documentation file:
-
- - In <https://gitlab.com/gitlab-com/www-gitlab-com>, search for full URLs:
- `grep -r "docs.gitlab.com/ee/path/to/file.html" .`
- - In <https://gitlab.com/gitlab-org/gitlab-docs/-/tree/master/content/_data>,
- search the navigation bar configuration files for the path with `.html`:
- `grep -r "path/to/file.html" .`
- - In any of the four internal projects, search for links in the docs
- and codebase. Search for all variations, including full URL and just the path.
- For example, go to the root directory of the `gitlab` project and run:
-
- ```shell
- grep -r "docs.gitlab.com/ee/path/to/file.html" .
- grep -r "path/to/file.html" .
- grep -r "path/to/file.md" .
- grep -r "path/to/file" .
- ```
+- To redirect to a page in a different project or site, use the full URL (with `https://`) :
+
+ ```shell
+ bundle exec rake "gitlab:docs:redirect[doc/user/search/old_file.md, https://example.com]"
+ ```
+
+- Alternatively, you can omit the arguments and be prompted to enter the values:
+
+ ```shell
+ bundle exec rake gitlab:docs:redirect
+ ```
+
+## Redirecting a page created before the release
+
+If you create a new page and then rename it before it's added to a release on the 18th:
- You may need to try variations of relative links, such as `../path/to/file` or
- `../file` to find every case.
+Instead of following that procedure, ask a Technical Writer to manually add the redirect
+to [`redirects.yaml`](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/main/content/_data/redirects.yaml).
## Redirections for pages with Disqus comments
diff --git a/doc/development/documentation/restful_api_styleguide.md b/doc/development/documentation/restful_api_styleguide.md
index bf1461a810d..2991c1b3224 100644
--- a/doc/development/documentation/restful_api_styleguide.md
+++ b/doc/development/documentation/restful_api_styleguide.md
@@ -59,12 +59,12 @@ METHOD /endpoint
Supported attributes:
-| Attribute | Type | Required | Description |
-|:-------------------------|:---------|:-----------------------|:----------------------|
-| `attribute` | datatype | **{check-circle}** Yes | Detailed description. |
-| `attribute` **(<tier>)** | datatype | **{dotted-circle}** No | Detailed description. |
-| `attribute` | datatype | **{dotted-circle}** No | Detailed description. |
-| `attribute` | datatype | **{dotted-circle}** No | Detailed description. |
+| Attribute | Type | Required | Description |
+|:-------------------------|:---------|:---------|:----------------------|
+| `attribute` | datatype | Yes | Detailed description. |
+| `attribute` **(<tier>)** | datatype | No | Detailed description. |
+| `attribute` | datatype | No | Detailed description. |
+| `attribute` | datatype | No | Detailed description. |
If successful, returns [`<status_code>`](../../api/index.md#status-codes) and the following
response attributes:
@@ -123,13 +123,13 @@ To deprecate an attribute:
1. Add inline deprecation text to the description.
```markdown
- | Attribute | Type | Required | Description |
- |:--------------|:-------|:-----------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------|
- | `widget_name` | string | **{dotted-circle}** No | [Deprecated](<link-to-issue>) in GitLab 14.7 and is planned for removal in 15.4. Use `widget_id` instead. The name of the widget. |
+ | Attribute | Type | Required | Description |
+ |:--------------|:-------|:---------|:----------------------------------------------------------------------------------------------------------------------------------------------------|
+ | `widget_name` | string | No | [Deprecated](<link-to-issue>) in GitLab 14.7 and is planned for removal in 15.4. Use `widget_id` instead. The name of the widget. |
```
To widely announce a deprecation, or if it's a breaking change,
-[update the deprecations and removals documentation](../deprecation_guidelines/#update-the-deprecations-and-removals-documentation).
+[update the deprecations and removals documentation](../deprecation_guidelines/index.md#update-the-deprecations-and-removals-documentation).
## Method description
@@ -139,20 +139,20 @@ always be in code blocks using backticks (`` ` ``).
Sort the table by required attributes first, then alphabetically.
```markdown
-| Attribute | Type | Required | Description |
-|:-----------------------------|:--------------|:-----------------------|:----------------------------------------------------|
-| `title` | string | **{check-circle}** Yes | Title of the issue. |
-| `assignee_ids` **(PREMIUM)** | integer array | **{dotted-circle}** No | IDs of the users to assign the issue to. |
-| `confidential` | boolean | **{dotted-circle}** No | Sets the issue to confidential. Default is `false`. |
+| Attribute | Type | Required | Description |
+|:-----------------------------|:--------------|:---------|:----------------------------------------------------|
+| `title` | string | Yes | Title of the issue. |
+| `assignee_ids` **(PREMIUM)** | integer array | No | IDs of the users to assign the issue to. |
+| `confidential` | boolean | No | Sets the issue to confidential. Default is `false`. |
```
Rendered example:
-| Attribute | Type | Required | Description |
-|:-----------------------------|:--------------|:-----------------------|:----------------------------------------------------|
-| `title` | string | **{check-circle}** Yes | Title of the issue. |
-| `assignee_ids` **(PREMIUM)** | integer array | **{dotted-circle}** No | IDs of the users to assign the issue to. |
-| `confidential` | boolean | **{dotted-circle}** No | Sets the issue to confidential. Default is `false`. |
+| Attribute | Type | Required | Description |
+|:-----------------------------|:--------------|:---------|:----------------------------------------------------|
+| `title` | string | Yes | Title of the issue. |
+| `assignee_ids` **(PREMIUM)** | integer array | No | IDs of the users to assign the issue to. |
+| `confidential` | boolean | No | Sets the issue to confidential. Default is `false`. |
For information about writing attribute descriptions, see the [GraphQL API description style guide](../api_graphql_styleguide.md#description-style-guide).
diff --git a/doc/development/documentation/review_apps.md b/doc/development/documentation/review_apps.md
index 8cb9e6437b8..a50efceb307 100644
--- a/doc/development/documentation/review_apps.md
+++ b/doc/development/documentation/review_apps.md
@@ -57,7 +57,7 @@ If you want to know the in-depth details, here's what's really happening:
The following GitLab features are used among others:
- [Manual jobs](../../ci/jobs/job_control.md#create-a-job-that-must-be-run-manually)
-- [Multi project pipelines](../../ci/pipelines/multi_project_pipelines.md)
+- [Multi project pipelines](../../ci/pipelines/downstream_pipelines.md#multi-project-pipelines)
- [Review Apps](../../ci/review_apps/index.md)
- [Artifacts](../../ci/yaml/index.md#artifacts)
- [Specific runner](../../ci/runners/runners_scope.md#prevent-a-specific-runner-from-being-enabled-for-other-projects)
diff --git a/doc/development/documentation/site_architecture/deployment_process.md b/doc/development/documentation/site_architecture/deployment_process.md
index 5f6076f3195..8a9c2e1e8d7 100644
--- a/doc/development/documentation/site_architecture/deployment_process.md
+++ b/doc/development/documentation/site_architecture/deployment_process.md
@@ -144,18 +144,21 @@ graph LR
### Manually deploy to production
-GitLab Docs is deployed to production whenever the `Build docs.gitlab.com every 4 hours` scheduled pipeline runs. By
-default, this pipeline runs every four hours.
+GitLab Docs is deployed to production whenever the `Build docs.gitlab.com every hour` scheduled pipeline runs. By
+default, this pipeline runs every hour.
Maintainers can [manually](../../../ci/pipelines/schedules.md#run-manually) run this pipeline to force a deployment to
production:
1. Go to the [scheduled pipelines](https://gitlab.com/gitlab-org/gitlab-docs/-/pipeline_schedules) for `gitlab-docs`.
-1. Next to `Build docs.gitlab.com every 4 hours`, select **Play** (**{play}**).
+1. Next to `Build docs.gitlab.com every hour`, select **Play** (**{play}**).
The updated documentation is available in production after the `pages` and `pages:deploy` jobs
complete in the new pipeline.
+If you do not have the Maintainer role to perform this task, ask for help in the
+`#docs` Slack channel.
+
## Docker files
The [`dockerfiles` directory](https://gitlab.com/gitlab-org/gitlab-docs/blob/main/dockerfiles/) contains all needed
diff --git a/doc/development/documentation/site_architecture/index.md b/doc/development/documentation/site_architecture/index.md
index 2864bbe7404..44e4aac2756 100644
--- a/doc/development/documentation/site_architecture/index.md
+++ b/doc/development/documentation/site_architecture/index.md
@@ -33,7 +33,7 @@ Then you can use one of these approaches:
of the documentation in the external repository. The landing page is indexed and
searchable on <https://docs.gitlab.com>, but the rest of the documentation is not.
For example, the [GitLab Workflow extension for VS Code](../../../user/project/repository/vscode.md).
- We do not encourage the use of [pages with lists of links](../structure.md#topics-and-resources-pages),
+ We do not encourage the use of [pages with lists of links](../topic_types/index.md#topics-and-resources),
so only use this option if the recommended options are not feasible.
## Monthly release process (versions)
diff --git a/doc/development/documentation/structure.md b/doc/development/documentation/structure.md
index a5d1290a17a..35a93f08f66 100644
--- a/doc/development/documentation/structure.md
+++ b/doc/development/documentation/structure.md
@@ -1,395 +1,11 @@
---
-stage: none
-group: Style Guide
-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
-description: What to include in GitLab documentation pages.
+redirect_to: 'topic_types/index.md'
+remove_date: '2022-11-16'
---
-# Documentation topic types (CTRT)
+This document was moved to [another location](topic_types/index.md).
-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 (CTRT):
-
-- [Concept](#concept)
-- [Task](#task)
-- [Reference](#reference)
-- [Troubleshooting](#troubleshooting)
-
-In general, each page in our docset contains multiple topics. (Each heading indicates a new topic.)
-Each topic on a page should be a specific topic type. For example,
-a page with the title `Pipelines` can include topics that are concepts and tasks.
-
-A page might also contain only one type of information. These pages are generally one of our
-[other content types](#other-types-of-content).
-
-## Concept
-
-A concept introduces a single feature or concept.
-
-A concept should answer the questions:
-
-- What is this?
-- Why would I use it?
-
-Think of everything someone might want to know if they've never heard of this concept before.
-
-Don't tell them **how** to do this thing. Tell them **what it is**.
-
-If you start describing another concept, start a new concept and link to it.
-
-Concepts should be in this format:
-
-```markdown
-# Title (a noun, like "Widgets")
-
-A paragraph that explains what this thing is.
-
-Another paragraph that explains what this thing is.
-
-Remember, if you start to describe about another concept, stop yourself.
-Each concept should be about one concept only.
-```
-
-### Concept headings
-
-For the heading text, use a noun. For example, `Widgets` or `GDK dependency management`.
-
-If a noun is ambiguous, you can add a gerund. For example, `Documenting versions` instead of `Versions`.
-
-Avoid these heading titles:
-
-- `Overview` or `Introduction`. Instead, use a more specific
- noun or phrase that someone would search for.
-- `Use cases`. Instead, incorporate the information as part of the concept.
-- `How it works`. Instead, use a noun followed by `workflow`. For example, `Merge request workflow`.
-
-## Task
-
-A task gives instructions for how to complete a procedure.
-
-Tasks should be in this format:
-
-```markdown
-# Title (starts with an active verb, like "Create a widget" or "Delete a widget")
-
-Do this task when you want to...
-
-Prerequisites (optional):
-
-- Thing 1
-- Thing 2
-- Thing 3
-
-To do this task:
-
-1. Location then action. (Go to this menu, then select this item.)
-1. Another step.
-1. Another step.
-
-Task result (optional). Next steps (optional).
-```
-
-Here is an example.
-
-```markdown
-# Create an issue
-
-Create an issue when you want to track bugs or future work.
-
-Prerequisites:
-
-- You must have at least the Developer role for the project.
-
-To create an issue:
-
-1. On the top bar, select **Menu > Projects** and find your project.
-1. On the left sidebar, select **Issues > List**.
-1. In the top right corner, select **New issue**.
-1. Complete the fields. (If you have reference content that lists each field, link to it here.)
-1. Select **Create issue**.
-
-The issue is created. You can view it by going to **Issues > List**.
-```
-
-### Task headings
-
-For the heading text, use the structure `active verb` + `noun`.
-For example, `Create an issue`.
-
-If you have several tasks on a page that share prerequisites, you can use the title
-`Prerequisites` and link to it.
-
-## Reference
-
-Reference information should be in an easily-scannable format,
-like a table or list. It's similar to a dictionary or encyclopedia entry.
-
-```markdown
-# Title (a noun, like "Pipeline settings" or "Administrator options")
-
-Introductory sentence.
-
-| Setting | Description |
-|---------|-------------|
-| **Name** | Descriptive sentence about the setting. |
-```
-
-### Reference headings
-
-Reference headings are usually nouns.
-
-Avoid these heading titles:
-
-- `Important notes`. Instead, incorporate this information
- closer to where it belongs. For example, this information might be a prerequisite
- for a task, or information about a concept.
-- `Limitations`. Instead, move the content near other similar information.
- If you must, you can use the title `Known issues`.
-
-## Troubleshooting
-
-Troubleshooting topics should be the last topics on a page.
-
-Troubleshooting can be one of three categories:
-
-- **An introductory topic.** This topic introduces the troubleshooting section of a page.
- For example:
-
- ```markdown
- ## Troubleshooting
-
- When working with <x feature>, you might encounter the following issues.
- ```
-
-- **Troubleshooting task.** The title should be similar to a [standard task](#task).
- For example, "Run debug tools" or "Verify syntax."
-
-- **Troubleshooting reference.** This information includes the error message. For example:
-
- ```markdown
- ### The error message or a description of it
-
- You might get an error that states <error message>.
-
- This issue occurs when...
-
- The workaround is...
- ```
-
- If multiple causes or workarounds exist, consider putting them into a table format.
- If you use the exact error message, surround it in backticks so it's styled as code.
-
-If a page has more than five troubleshooting topics, put the content on a separate page that has troubleshooting information exclusively. Name the page `Troubleshooting <featurename>`.
-
-### Troubleshooting headings
-
-For the heading of a **Troubleshooting reference** topic:
-
-- Consider including at least a partial error message.
-- Use fewer than 70 characters.
-
-If you do not put the full error in the title, include it in the body text.
-
-### Related topics
-
-If inline links are not sufficient, you can create a topic called **Related topics**
-and include an unordered list of related topics. This topic should be above the Troubleshooting section.
-
-```markdown
-# Related topics
-
-- [Configure your pipeline](link-to-topic)
-- [Trigger a pipeline manually](link-to-topic)
-```
-
-## General heading text guidelines
-
-In general, for heading text:
-
-- Be clear and direct. Make every word count.
-- Use articles and prepositions.
-- Follow [capitalization](styleguide/index.md#capitalization) guidelines.
-- Do not repeat text from earlier headings. For example, if the page is about merge requests,
- instead of `Troubleshooting merge requests`, use only `Troubleshooting`.
-
-See also [guidelines for headings in Markdown](styleguide/index.md#headings-in-markdown).
-
-## Other types of content
-
-There are other types of content in the GitLab documentation that don't
-classify as one of the four primary [topic types](#documentation-topic-types-ctrt).
-These include:
-
-- [Tutorials](#tutorials)
-- [Get started pages](#get-started)
-- [Topics and resources pages](#topics-and-resources-pages)
-
-In most cases, these pages are standalone.
-
-### Tutorials
-
-A tutorial is an end-to-end walkthrough of a complex workflow or scenario.
-In general, you might consider using a tutorial when:
-
-- The workflow requires a number of sequential steps where each step consists
- of sub-steps.
-- The steps cover a variety of GitLab features or third-party tools.
-
-Tutorials are learning aids that complement our core documentation.
-They do not introduce new features.
-Always use the primary [topic types](#documentation-topic-types-ctrt) to document new features.
-
-Tutorials should be in this format:
-
-```markdown
-# Title (starts with "Tutorial:" followed by an active verb, like "Tutorial: Create a website")
-
-A paragraph that explains what the tutorial does, and the expected outcome.
-
-To create a website:
-
-1. [Do the first task](#do-the-first-task)
-1. [Do the second task](#do-the-second-task)
-
-Prerequisites (optional):
-
-- Thing 1
-- Thing 2
-- Thing 3
-
-## Do the first task
-
-To do step 1:
-
-1. First step.
-1. Another step.
-1. Another step.
-
-## Do the second task
-
-Before you begin, make sure you have [done the first task](#do-the-first-task).
-
-To do step 2:
-
-1. First step.
-1. Another step.
-1. Another step.
-```
-
-### Get started
-
-A get started page is a set of steps to help a user get set up
-quickly to use a single GitLab feature or tool.
-It might consist of more than one task.
-
-Get started pages should be in this format:
-
-```markdown
-# Title ("Get started with <feature>")
-
-Complete the following steps to ... .
-
-1. First step.
-1. Another step.
-1. Another step.
-
-If you need to add more than one task,
-consider using subsections for each distinct task.
-```
-
-### Topics and resources pages
-
-This page has a list of links that point to important sections
-of documentation for a specific GitLab feature or tool.
-
-We do not encourage the use of these types of pages.
-Lists like this can get out of date quickly and offer little value to users.
-We've included this type here because:
-
-- There are existing pages in the documentation that follow this format,
- and they should be standardized.
-- They can sometimes help navigate a complex section of the documentation.
-
-If you come across a page like this
-or you have to create one, use this format:
-
-```markdown
-# Title ("Topics and resources for <feature>")
-
-Brief sentence to describe the feature.
-
-Refer to these resources for more information about <this feature>:
-
-- Link 1
-- Link 2
-- Link 3
-```
-
-## Help and feedback section
-
-This section ([introduced](https://gitlab.com/gitlab-org/gitlab-docs/-/merge_requests/319) in GitLab 11.4)
-is displayed at the end of each document and can be omitted by adding a key into
-the front matter:
-
-```yaml
----
-feedback: false
----
-```
-
-The default is to leave it there. If you want to omit it from a document, you
-must check with a technical writer before doing so.
-
-### Disqus
-
-We also have integrated the docs site with Disqus (introduced by
-[!151](https://gitlab.com/gitlab-org/gitlab-docs/-/merge_requests/151)),
-allowing our users to post comments.
-
-To omit only the comments from the feedback section, use the following key in
-the front matter:
-
-```yaml
----
-comments: false
----
-```
-
-We're hiding comments only in main index pages, such as [the main documentation index](../../index.md),
-since its content is too broad to comment on. Before omitting Disqus, you must
-check with a technical writer.
-
-Note that after adding `feedback: false` to the front matter, it will omit
-Disqus, therefore, don't add both keys to the same document.
-
-The click events in the feedback section are tracked with Google Tag Manager.
-The conversions can be viewed on Google Analytics by navigating to
-**Behavior > Events > Top events > docs**.
-
-## Guidelines for good practices
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36576/) in GitLab 13.2 as GitLab Development documentation.
-
-*Good practice* examples demonstrate encouraged ways of writing code while
-comparing with examples of practices to avoid. These examples are labeled as
-*Bad* or *Good*. In GitLab development guidelines, when presenting the cases,
-it's recommended to follow a *first-bad-then-good* strategy. First demonstrate
-the *Bad* practice (how things *could* be done, which is often still working
-code), and then how things *should* be done better, using a *Good* example. This
-is typically an improved example of the same code.
-
-Consider the following guidelines when offering examples:
-
-- First, offer the *Bad* example, and then the *Good* one.
-- When only one bad case and one good case is given, use the same code block.
-- When more than one bad case or one good case is offered, use separated code
- blocks for each. With many examples being presented, a clear separation helps
- the reader to go directly to the good part. Consider offering an explanation
- (for example, a comment, or a link to a resource) on why something is bad
- practice.
-- Better and best cases can be considered part of the good cases' code block.
- In the same code block, precede each with comments: `# Better` and `# Best`.
-
-Although the bad-then-good approach is acceptable for the GitLab development
-guidelines, do not use it for user documentation. For user documentation, use
-*Do* and *Don't*. For examples, see the [Pajamas Design System](https://design.gitlab.com/content/punctuation/).
+<!-- 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/index.md b/doc/development/documentation/styleguide/index.md
index 709e6b2d0d9..bc79bf0fbe2 100644
--- a/doc/development/documentation/styleguide/index.md
+++ b/doc/development/documentation/styleguide/index.md
@@ -887,7 +887,7 @@ To be consistent, use these templates when you write navigation steps in a task
To open project settings:
```markdown
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **General pipelines**.
```
@@ -895,7 +895,7 @@ To open project settings:
To open group settings:
```markdown
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **General pipelines**.
```
@@ -903,7 +903,7 @@ To open group settings:
To open the Admin Area:
```markdown
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
```
To select your avatar:
@@ -950,7 +950,7 @@ Use the phrase **Complete the fields**.
For example:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Push rules**.
1. Complete the fields.
@@ -1361,6 +1361,48 @@ It renders on the GitLab documentation site as:
> - This is a list item
> - Second item in the list
+## Tabs
+
+On the docs site, you can format text so it's displayed as tabs.
+
+To create a set of tabs, follow this example:
+
+```plaintext
+::Tabs
+
+:::TabTitle Tab One
+
+Here's some content in tab one.
+
+:::TabTitle Tab Two
+
+Here's some other content in tab two.
+
+::EndTabs
+```
+
+This code renders on the GitLab documentation site as:
+
+::Tabs
+
+:::TabTitle Tab One
+
+Here's some content in tab one.
+
+:::TabTitle Tab Two
+
+Here's some other content in tab two.
+
+::EndTabs
+
+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`
+- `15.1 and earlier`, `15.2 and later`
+
+See [Pajamas](https://design.gitlab.com/components/tabs/#guidelines) for details.
+
## Terms
To maintain consistency through GitLab documentation, use these styles and terms.
diff --git a/doc/development/documentation/styleguide/word_list.md b/doc/development/documentation/styleguide/word_list.md
index 1976caefc8e..029c7389290 100644
--- a/doc/development/documentation/styleguide/word_list.md
+++ b/doc/development/documentation/styleguide/word_list.md
@@ -8,8 +8,12 @@ description: 'Writing styles, markup, formatting, and other standards for GitLab
# Recommended word list
To help ensure consistency in the documentation, the Technical Writing team
-recommends these wording choices. The GitLab handbook also maintains a list of
-[top misused terms](https://about.gitlab.com/handbook/communication/top-misused-terms/).
+recommends these word choices. In addition:
+
+- The GitLab handbook contains a list of
+ [top misused terms](https://about.gitlab.com/handbook/communication/top-misused-terms/).
+- The documentation [style guide](../styleguide#language) includes details
+ about language and capitalization.
For guidance not on this page, we defer to these style guides:
@@ -19,6 +23,10 @@ For guidance not on this page, we defer to these style guides:
<!-- vale off -->
<!-- markdownlint-disable -->
+## `&`
+
+Do not use Latin abbreviations. Use **and** instead, unless you are documenting a UI element that uses an `&`.
+
## `@mention`
Try to avoid **`@mention`**. Say **mention** instead, and consider linking to the
@@ -75,7 +83,7 @@ Instead of:
## Admin Area
-Use title case **Admin Area** to refer to the area of the UI that you access when you select **Menu > Admin**.
+Use title case **Admin Area** to refer to the area of the UI that you access when you select **Main menu > Admin**.
This area of the UI says **Admin Area** at the top of the page and on the menu.
## agent
@@ -139,6 +147,18 @@ Do not use **and so on**. Instead, be more specific. For details, see
Use [**section**](#section) instead of **area**. The only exception is [the Admin Area](#admin-area).
+## as
+
+Do not use **as** to mean **because**.
+
+Use:
+
+- Because none of the endpoints return an ID...
+
+Instead of:
+
+- As none of the endpoints return an ID...
+
## associate
Do not use **associate** when describing adding issues to epics, or users to issues, merge requests,
@@ -265,6 +285,13 @@ Use title case for the GitLab Container Registry.
Do not use **currently** when talking about the product or its features. The documentation describes the product as it is today.
([Vale](../testing.md#vale) rule: [`CurrentStatus.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/CurrentStatus.yml))
+## default branch
+
+Use **default branch** to refer generically to the primary branch in the repository.
+Users can set the default branch by using a UI setting.
+
+For examples that use the default branch, use `main` instead of [`master`](#master).
+
## Dependency Proxy
Use title case for the GitLab Dependency Proxy.
@@ -394,7 +421,7 @@ Information in FAQs belongs with other similar information, under an easily sear
## field
-Use **box** instead of **field** or **text box**.
+Use **text box** instead of **field** or **box**.
Use:
@@ -407,7 +434,7 @@ Instead of:
However, you can make an exception when you are writing a task and you need to refer to all
of the fields at once. For example:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **General pipelines**.
1. Complete the fields.
@@ -604,6 +631,10 @@ Instead of:
- Buy a license.
- Purchase a license.
+## limitations
+
+Do not use **limitations**. Use **known issues** instead.
+
## log in, log on
Do not use **log in** or **log on**. Use [sign in](#sign-in) instead. If the user interface has **Log in**, you can use it.
@@ -644,7 +675,8 @@ Do not use **manpower**. Use words like **workforce** or **GitLab team members**
## master
-Do not use **master**. Options are **primary** or **main**. ([Vale](../testing.md#vale) rule: [`InclusionCultural.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/InclusionCultural.yml))
+Do not use `master`. Use `main` when you need a sample [default branch name](#default-branch).
+([Vale](../testing.md#vale) rule: [`InclusionCultural.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/InclusionCultural.yml))
## may, might
diff --git a/doc/development/documentation/testing.md b/doc/development/documentation/testing.md
index 428a57a11fb..59a078bdec0 100644
--- a/doc/development/documentation/testing.md
+++ b/doc/development/documentation/testing.md
@@ -190,7 +190,7 @@ To update the linting images:
1. In `gitlab-docs`, open a merge request to update `.gitlab-ci.yml` to use the new tooling
version. ([Example MR](https://gitlab.com/gitlab-org/gitlab-docs/-/merge_requests/2571))
-1. When merged, start a `Build docs.gitlab.com every 4 hours` [scheduled pipeline](https://gitlab.com/gitlab-org/gitlab-docs/-/pipeline_schedules).
+1. When merged, start a `Build docs.gitlab.com every hour` [scheduled pipeline](https://gitlab.com/gitlab-org/gitlab-docs/-/pipeline_schedules).
1. Go the pipeline you started, and manually run the relevant build-images job,
for example, `image:docs-lint-markdown`.
1. In the job output, get the name of the new image.
diff --git a/doc/development/documentation/topic_types/concept.md b/doc/development/documentation/topic_types/concept.md
new file mode 100644
index 00000000000..a20bb93a97f
--- /dev/null
+++ b/doc/development/documentation/topic_types/concept.md
@@ -0,0 +1,46 @@
+---
+stage: none
+group: Style Guide
+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
+---
+
+# Concept topic type
+
+A concept introduces a single feature or concept.
+
+A concept should answer the questions:
+
+- What is this?
+- Why would I use it?
+
+Think of everything someone might want to know if they've never heard of this concept before.
+
+Don't tell them **how** to do this thing. Tell them **what it is**.
+
+If you start describing another concept, start a new concept and link to it.
+
+Concepts should be in this format:
+
+```markdown
+# Title (a noun, like "Widgets")
+
+A paragraph that explains what this thing is.
+
+Another paragraph that explains what this thing is.
+
+Remember, if you start to describe about another concept, stop yourself.
+Each concept should be about one concept only.
+```
+
+## Concept headings
+
+For the heading text, use a noun. For example, `Widgets` or `GDK dependency management`.
+
+If a noun is ambiguous, you can add a gerund. For example, `Documenting versions` instead of `Versions`.
+
+Avoid these heading titles:
+
+- `Overview` or `Introduction`. Instead, use a more specific
+ noun or phrase that someone would search for.
+- `Use cases`. Instead, incorporate the information as part of the concept.
+- `How it works`. Instead, use a noun followed by `workflow`. For example, `Merge request workflow`.
diff --git a/doc/development/documentation/topic_types/index.md b/doc/development/documentation/topic_types/index.md
new file mode 100644
index 00000000000..af3e66fe87a
--- /dev/null
+++ b/doc/development/documentation/topic_types/index.md
@@ -0,0 +1,130 @@
+---
+stage: none
+group: Style Guide
+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
+---
+
+# 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:
+
+- [Concept](concept.md)
+- [Task](task.md)
+- [Reference](reference.md)
+- [Troubleshooting](troubleshooting.md)
+
+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.
+
+## Other topic types
+
+In addition to the four primary topic types, we have a few other types.
+
+### Related topics
+
+If inline links are not sufficient, you can create a topic called **Related topics**
+and include an unordered list of related topics. This topic should be above the Troubleshooting section.
+
+```markdown
+# Related topics
+
+- [Configure your pipeline](link-to-topic).
+- [Trigger a pipeline manually](link-to-topic).
+```
+
+### Tutorials
+
+A tutorial is page that contains an end-to-end walkthrough of a complex workflow or scenario.
+In general, you might consider using a tutorial when:
+
+- The workflow requires a number of sequential steps where each step consists
+ of sub-steps.
+- The steps cover a variety of GitLab features or third-party tools.
+
+Tutorials are learning aids that complement our core documentation.
+They do not introduce new features.
+Always use the primary [topic types](#documentation-topic-types-ctrt) to document new features.
+
+Tutorials should be in this format:
+
+```markdown
+# Title (starts with "Tutorial:" followed by an active verb, like "Tutorial: Create a website")
+
+A paragraph that explains what the tutorial does, and the expected outcome.
+
+To create a website:
+
+1. [Do the first task](#do-the-first-task)
+1. [Do the second task](#do-the-second-task)
+
+Prerequisites (optional):
+
+- Thing 1
+- Thing 2
+- Thing 3
+
+## Do the first task
+
+To do step 1:
+
+1. First step.
+1. Another step.
+1. Another step.
+
+## Do the second task
+
+Before you begin, make sure you have [done the first task](#do-the-first-task).
+
+To do step 2:
+
+1. First step.
+1. Another step.
+1. Another step.
+```
+
+### Get started
+
+A get started page is a set of steps to help a user get set up
+quickly to use a single GitLab feature or tool.
+It consists of more than one task.
+
+Get started pages should be in this format:
+
+```markdown
+# Title ("Get started with <feature>")
+
+Complete the following steps to ... .
+
+1. First step.
+1. Another step.
+1. Another step.
+
+If you need to add more than one task,
+consider using subsections for each distinct task.
+```
+
+In the left nav, use `Get started` as the text. On the page itself, spell out
+the full name. For example, `Get started with application security`.
+
+### Topics and resources
+
+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.
+
+## Heading text guidelines
+
+In general, for heading text:
+
+- Be clear and direct. Make every word count.
+- Use articles and prepositions.
+- Follow [capitalization](../styleguide/index.md#capitalization) guidelines.
+- Do not repeat text from earlier headings. For example, if the page is about merge requests,
+ instead of `Troubleshooting merge requests`, use only `Troubleshooting`.
+
+See also [guidelines for headings in Markdown](../styleguide/index.md#headings-in-markdown).
diff --git a/doc/development/documentation/topic_types/reference.md b/doc/development/documentation/topic_types/reference.md
new file mode 100644
index 00000000000..42f4f5f6f94
--- /dev/null
+++ b/doc/development/documentation/topic_types/reference.md
@@ -0,0 +1,32 @@
+---
+stage: none
+group: Style Guide
+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
+---
+
+# Reference topic type
+
+Reference information should be in an easily-scannable format,
+like a table or list. It's similar to a dictionary or encyclopedia entry.
+
+```markdown
+# Title (a noun, like "Pipeline settings" or "Administrator options")
+
+Introductory sentence.
+
+| Setting | Description |
+|---------|-------------|
+| **Name** | Descriptive sentence about the setting. |
+```
+
+## Reference headings
+
+Reference headings are usually nouns.
+
+Avoid these heading titles:
+
+- `Important notes`. Instead, incorporate this information
+ closer to where it belongs. For example, this information might be a prerequisite
+ for a task, or information about a concept.
+- `Limitations`. Instead, move the content near other similar information.
+ If you must, you can use the title `Known issues`.
diff --git a/doc/development/documentation/topic_types/task.md b/doc/development/documentation/topic_types/task.md
new file mode 100644
index 00000000000..3488cb90cf9
--- /dev/null
+++ b/doc/development/documentation/topic_types/task.md
@@ -0,0 +1,65 @@
+---
+stage: none
+group: Style Guide
+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
+---
+
+# Task topic type
+
+A task gives instructions for how to complete a procedure.
+
+Tasks should be in this format:
+
+```markdown
+# Title (starts with an active verb, like "Create a widget" or "Delete a widget")
+
+Do this task when you want to...
+
+Prerequisites (optional):
+
+- Thing 1
+- Thing 2
+- Thing 3
+
+To do this task:
+
+1. Location then action. (Go to this menu, then select this item.)
+1. Another step.
+1. Another step.
+
+Task result (optional). Next steps (optional).
+```
+
+Here is an example.
+
+```markdown
+# Create an issue
+
+Create an issue when you want to track bugs or future work.
+
+Prerequisites:
+
+- You must have at least the Developer role for the project.
+
+To create an issue:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Issues > List**.
+1. In the top right corner, select **New issue**.
+1. Complete the fields. (If you have reference content that lists each field, link to it here.)
+1. Select **Create issue**.
+
+The issue is created. You can view it by going to **Issues > List**.
+```
+
+## Task headings
+
+For the heading text, use the structure `active verb` + `noun`.
+For example, `Create an issue`.
+
+If you have several tasks on a page that share prerequisites, you can use the title
+`Prerequisites` and link to it.
+
+## Related topics
+
+- [View the format for writing task steps](../styleguide/index.md#navigation).
diff --git a/doc/development/documentation/topic_types/troubleshooting.md b/doc/development/documentation/topic_types/troubleshooting.md
new file mode 100644
index 00000000000..35187bd892e
--- /dev/null
+++ b/doc/development/documentation/topic_types/troubleshooting.md
@@ -0,0 +1,56 @@
+---
+stage: none
+group: Style Guide
+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
+---
+
+# Troubleshooting topic type
+
+Troubleshooting topics should be the last topics on a page.
+
+If a page has more than five troubleshooting topics, put the content on a separate page that has troubleshooting information exclusively. Name the page `Troubleshooting <feature>`
+and in the left nav, use the word `Troubleshoot` only.
+
+Troubleshooting can be one of three types.
+
+## An introductory topic
+
+This topic introduces the troubleshooting section of a page.
+For example:
+
+```markdown
+## Troubleshooting
+
+When working with <x feature>, you might encounter the following issues.
+```
+
+## Troubleshooting task
+
+The title should be similar to a [standard task](task.md).
+For example, "Run debug tools" or "Verify syntax."
+
+## Troubleshooting reference
+
+This topic includes the error message. For example:
+
+```markdown
+### The error message or a description of it
+
+You might get an error that states <error message>.
+
+This issue occurs when...
+
+The workaround is...
+```
+
+If multiple causes or workarounds exist, consider putting them into a table format.
+If you use the exact error message, surround it in backticks so it's styled as code.
+
+## Troubleshooting headings
+
+For the heading of a **Troubleshooting reference** topic:
+
+- Consider including at least a partial error message.
+- Use fewer than 70 characters.
+
+If you do not put the full error in the title, include it in the body text.
diff --git a/doc/development/documentation/versions.md b/doc/development/documentation/versions.md
index 3679c731a77..85733603cfe 100644
--- a/doc/development/documentation/versions.md
+++ b/doc/development/documentation/versions.md
@@ -69,6 +69,11 @@ If a feature is moved to another subscription tier, use `moved`:
> - [Moved](<link-to-issue>) from GitLab Premium to GitLab Free in 12.0.
```
+#### Features introduced behind feature flags
+
+When features are introduced behind feature flags, you must add details about the feature flag to the documentation.
+For more information, see [Document features deployed behind feature flags](feature_flags.md).
+
### Inline version text
If you're adding content to an existing topic, you can add version information
@@ -205,7 +210,7 @@ You can say that we plan to remove a feature.
### Legal disclaimer for future features
-If you **must** write about features we have not yet delivered, put this exact disclaimer near the content it applies to.
+If you **must** write about features we have not yet delivered, put this exact disclaimer about forward-looking statements near the content it applies to.
```markdown
DISCLAIMER:
@@ -227,6 +232,6 @@ As with all projects, the items mentioned on this page are subject to change or
The development, release, and timing of any products, features, or functionality remain at the
sole discretion of GitLab Inc.
-If all of the content on the page is not available, use the disclaimer once at the top of the page.
+If all of the content on the page is not available, use the disclaimer about forward-looking statements once at the top of the page.
If the content in a topic is not ready, use the disclaimer in the topic.
diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md
index 777bc77875e..869cb0bab0a 100644
--- a/doc/development/ee_features.md
+++ b/doc/development/ee_features.md
@@ -20,7 +20,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## Implement a new EE feature
-If you're developing a GitLab Starter, GitLab Premium, or GitLab Ultimate licensed feature, use these steps to
+If you're developing a GitLab Premium or GitLab Ultimate licensed feature, use these steps to
add your new feature or extend it.
GitLab license features are added to [`ee/app/models/gitlab_subscriptions/features.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/models/gitlab_subscriptions/features.rb). To determine how
@@ -33,9 +33,9 @@ Use the following questions to guide you:
must locate the existing feature identifier to [guard it](#guard-your-ee-feature).
- If this is a new feature, decide on an identifier, such as `my_feature_name`, to add to the
`features.rb` file.
-1. Is this a **GitLab Starter**, **GitLab Premium**, or **GitLab Ultimate** feature?
- - Based on the plan you choose to use the feature in, add the feature identifier to `STARTER_FEATURES`,
- `PREMIUM_FEATURES`, or `ULTIMATE_FEATURES`.
+1. Is this a **GitLab Premium** or **GitLab Ultimate** feature?
+ - Based on the plan you choose to use the feature in, add the feature identifier to `PREMIUM_FEATURES`
+ or `ULTIMATE_FEATURES`.
1. Will this feature be available globally (system-wide at the GitLab instance level)?
- Features such as [Geo](../administration/geo/index.md) and
[Database Load Balancing](../administration/postgresql/database_load_balancing.md) are used by the entire instance
@@ -281,7 +281,7 @@ There are a few gotchas with it:
overriding the method, because we can't know when the overridden method
(that is, calling `super` in the overriding method) would want to stop early.
In this case, we shouldn't just override it, but update the original method
- to make it call the other method we want to extend, like a
+ to make it call the other method we want to extend, like a
[template method pattern](https://en.wikipedia.org/wiki/Template_method_pattern).
For example, given this base:
@@ -1128,7 +1128,7 @@ EE licensed features that enhance existing functionality in the UI add new
elements or interactions to your Vue application as components.
To separate template differences, use a child EE component to separate Vue template differences.
-You must import the EE component [asynchronously](https://vuejs.org/v2/guide/components-dynamic-async.html#Async-Components).
+You must import the EE component [asynchronously](https://v2.vuejs.org/v2/guide/components-dynamic-async.html#Async-Components).
This allows GitLab to load the correct component in EE, while in CE GitLab loads an empty component
that renders nothing. This code **must** exist in the CE repository, in addition to the EE repository.
diff --git a/doc/development/elasticsearch.md b/doc/development/elasticsearch.md
index 47942817790..b3996e16fa1 100644
--- a/doc/development/elasticsearch.md
+++ b/doc/development/elasticsearch.md
@@ -277,7 +277,7 @@ These Advanced Search migrations, like any other GitLab changes, need to support
Depending on the order of deployment, it's possible that the migration
has started or finished and there's still a server running the application code from before the
-migration. We need to take this into consideration until we can
+migration. We need to take this into consideration until we can
[ensure all Advanced Search migrations start after the deployment has finished](https://gitlab.com/gitlab-org/gitlab/-/issues/321619).
### Reverting a migration
@@ -317,7 +317,7 @@ safely can.
We choose to use GitLab major version upgrades as a safe time to remove
backwards compatibility for indices that have not been fully migrated. We
-[document this in our upgrade documentation](../update/index.md#upgrading-to-a-new-major-version).
+[document this in our upgrade documentation](../update/index.md#upgrading-to-a-new-major-version).
We also choose to replace the migration code with the halted migration
and remove tests so that:
@@ -399,7 +399,7 @@ that may contain information to help diagnose performance issues.
### Performance Bar
-Elasticsearch requests will be displayed in the
+Elasticsearch requests will be displayed in the
[`Performance Bar`](../administration/monitoring/performance/performance_bar.md), which can
be used both locally in development and on any deployed GitLab instance to
diagnose poor search performance. This will show the exact queries being made,
@@ -495,7 +495,7 @@ theoretically be used to figure out what needs to be replayed are:
These updates can be replayed by triggering another
`ElasticDeleteProjectWorker`.
-With the above methods and taking regular
+With the above methods and taking regular
[Elasticsearch snapshots](https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshot-restore.html)
we should be able to recover from different kinds of data loss issues in a
relatively short period of time compared to indexing everything from
diff --git a/doc/development/emails.md b/doc/development/emails.md
index 1b3c9226dd8..c997916aa21 100644
--- a/doc/development/emails.md
+++ b/doc/development/emails.md
@@ -160,9 +160,9 @@ and Helm Chart configuration (see [example merge request](https://gitlab.com/git
#### Rationale
This was done because to avoid [thread deadlocks](https://github.com/ruby/net-imap/issues/14), `MailRoom` needs
-an updated version of the `net-imap` gem. However, this
-[version of the net-imap cannot be installed by an unprivileged user](https://github.com/ruby/net-imap/issues/14) due to
-[an error installing the digest gem](https://github.com/ruby/digest/issues/14).
+an updated version of the `net-imap` gem. However, this
+[version of the net-imap cannot be installed by an unprivileged user](https://github.com/ruby/net-imap/issues/14) due to
+[an error installing the digest gem](https://github.com/ruby/digest/issues/14).
[This bug in the Ruby interpreter](https://bugs.ruby-lang.org/issues/17761) was fixed in Ruby
3.0.2.
diff --git a/doc/development/experiment_guide/index.md b/doc/development/experiment_guide/index.md
index e11e516485a..500a19fe1ad 100644
--- a/doc/development/experiment_guide/index.md
+++ b/doc/development/experiment_guide/index.md
@@ -10,7 +10,7 @@ Experiments can be conducted by any GitLab team, most often the teams from the
[Growth Sub-department](https://about.gitlab.com/handbook/engineering/development/growth/).
Experiments are not tied to releases because they primarily target GitLab.com.
-Experiments are run as an A/B/n test, and are behind an [experiment feature flag](../feature_flags/#experiment-type)
+Experiments are run as an A/B/n test, and are behind an [experiment feature flag](../feature_flags/index.md#experiment-type)
to turn the test on or off. Based on the data the experiment generates, the team decides
if the experiment had a positive impact and should be made the new default, or rolled back.
diff --git a/doc/development/export_csv.md b/doc/development/export_csv.md
index 0f50d1438fc..29e80f676da 100644
--- a/doc/development/export_csv.md
+++ b/doc/development/export_csv.md
@@ -11,10 +11,10 @@ This document lists the different implementations of CSV export in GitLab codeba
| Export type | How it works | Advantages | Disadvantages | Existing examples |
|---|---|---|---|---|
| Streaming | - Query and yield data in batches to a response stream.<br>- Download starts immediately. | - Report available immediately. | - No progress indicator.<br>- Requires a reliable connection. | [Export Audit Event Log](../administration/audit_events.md#export-to-csv) |
-| Downloading | - Query and write data in batches to a temporary file.<br>- Loads the file into memory.<br>- Sends the file to the client. | - Report available immediately. | - Large amount of data might cause request timeout.<br>- Memory intensive.<br>- Request expires when user navigates to a different page. | - [Export Chain of Custody Report](../user/compliance/compliance_report/#chain-of-custody-report)<br>- [Export License Usage File](../subscriptions/self_managed/index.md#export-your-license-usage) |
+| Downloading | - Query and write data in batches to a temporary file.<br>- Loads the file into memory.<br>- Sends the file to the client. | - Report available immediately. | - Large amount of data might cause request timeout.<br>- Memory intensive.<br>- Request expires when user navigates to a different page. | - [Export Chain of Custody Report](../user/compliance/compliance_report/index.md#chain-of-custody-report)<br>- [Export License Usage File](../subscriptions/self_managed/index.md#export-your-license-usage) |
| As email attachment | - Asynchronously process the query with background job.<br>- Email uses the export as an attachment. | - Asynchronous processing. | - Requires users use a different app (email) to download the CSV.<br>- Email providers may limit attachment size. | - [Export issues](../user/project/issues/csv_export.md)<br>- [Export merge requests](../user/project/merge_requests/csv_export.md) |
| As downloadable link in email (*) | - Asynchronously process the query with background job.<br>- Email uses an export link. | - Asynchronous processing.<br>- Bypasses email provider attachment size limit. | - Requires users use a different app (email).<br>- Requires additional storage and cleanup. | [Export User Permissions](https://gitlab.com/gitlab-org/gitlab/-/issues/1772) |
-| Polling (non-persistent state) | - Asynchronously processes the query with the background job.<br>- Frontend(FE) polls every few seconds to check if CSV file is ready. | - Asynchronous processing.<br>- Automatically downloads to local machine on completion.<br>- In-app solution. | - Non-persistable request - request expires when user navigates to a different page.<br>- API is processed for each polling request. | [Export Vulnerabilities](../user/application_security/vulnerability_report/#export-vulnerability-details) |
+| Polling (non-persistent state) | - Asynchronously processes the query with the background job.<br>- Frontend(FE) polls every few seconds to check if CSV file is ready. | - Asynchronous processing.<br>- Automatically downloads to local machine on completion.<br>- In-app solution. | - Non-persistable request - request expires when user navigates to a different page.<br>- API is processed for each polling request. | [Export Vulnerabilities](../user/application_security/vulnerability_report/index.md#export-vulnerability-details) |
| Polling (persistent state) (*) | - Asynchronously processes the query with background job.<br>- Backend (BE) maintains the export state<br>- FE polls every few seconds to check status.<br>- FE shows 'Download link' when export is ready.<br>- User can download or regenerate a new report. | - Asynchronous processing.<br>- No database calls made during the polling requests (HTTP 304 status is returned until export status changes).<br>- Does not require user to stay on page until export is complete.<br>- In-app solution.<br>- Can be expanded into a generic CSV feature (such as dashboard / CSV API). | - Requires to maintain export states in DB.<br>- Does not automatically download the CSV export to local machine, requires users to select 'Download'. | [Export Merge Commits Report](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43055) |
NOTE:
diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md
index 442dda20d23..6dcc57b0ff5 100644
--- a/doc/development/fe_guide/graphql.md
+++ b/doc/development/fe_guide/graphql.md
@@ -729,8 +729,8 @@ In this case, we can either:
- Skip passing a cursor.
- Pass `null` explicitly to `after`.
-After data is fetched, we can use the `update`-hook as an opportunity
-[to customize the data that is set in the Vue component property](https://apollo.vuejs.org/api/smart-query.html#options).
+After data is fetched, we can use the `update`-hook as an opportunity
+[to customize the data that is set in the Vue component property](https://apollo.vuejs.org/api/smart-query.html#options).
This allows us to get a hold of the `pageInfo` object among other data.
In the `result`-hook, we can inspect the `pageInfo` object to see if we need to fetch
diff --git a/doc/development/fe_guide/index.md b/doc/development/fe_guide/index.md
index 02086ec5f1b..d90c270153e 100644
--- a/doc/development/fe_guide/index.md
+++ b/doc/development/fe_guide/index.md
@@ -36,7 +36,8 @@ Sign in to BrowserStack with the credentials saved in the **Engineering** vault
## Initiatives
-Current high-level frontend goals are listed on [Frontend Epics](https://gitlab.com/groups/gitlab-org/-/epics?label_name%5B%5D=frontend).
+You can find current frontend initiatives with a cross-functional impact on epics
+with the label [frontend-initiative](https://gitlab.com/groups/gitlab-org/-/epics?state=opened&page=1&sort=UPDATED_AT_DESC&label_name[]=frontend-initiative).
## Principles
diff --git a/doc/development/fe_guide/storybook.md b/doc/development/fe_guide/storybook.md
index 45342eb6d72..a3a1fa2160f 100644
--- a/doc/development/fe_guide/storybook.md
+++ b/doc/development/fe_guide/storybook.md
@@ -46,10 +46,12 @@ To add a story:
1. Write the story as per the [official Storybook instructions](https://storybook.js.org/docs/vue/writing-stories/introduction/)
- Notes:
- - Specify the `title` field of the story as the component's file path from the `javascripts/` directory.
-
- For example, if the component is located at `app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue`, specify the story `title` as `vue_shared/components/sidebar/todo_toggle/todo_button`. This will ensure the Storybook navigation maps closely to our internal directory structure.
+ NOTE:
+ Specify the `title` field of the story as the component's file path from the `javascripts/` directory, without the `/components` part.
+ For example, if the component is located at `app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue`,
+ specify the story `title` as `vue_shared/sidebar/todo_toggle/todo_button`.
+ If the component is located in the `ee/` directory, make sure to prefix the story's title with `ee/` as well.
+ This will ensure the Storybook navigation maps closely to our internal directory structure.
## Mock backend APIs
diff --git a/doc/development/fe_guide/style/vue.md b/doc/development/fe_guide/style/vue.md
index c9bd0e1b35a..39dc9cc9c4e 100644
--- a/doc/development/fe_guide/style/vue.md
+++ b/doc/development/fe_guide/style/vue.md
@@ -121,7 +121,7 @@ Check the [rules](https://github.com/vuejs/eslint-plugin-vue#bulb-rules) for mor
1. **Extensions**: Use `.vue` extension for Vue components. Do not use `.js` as file extension
([#34371](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/34371)).
-1. **Reference Naming**: Use PascalCase for their instances:
+1. **Reference Naming**: Use PascalCase for their default imports:
```javascript
// bad
@@ -431,7 +431,7 @@ must be unique. It's advised to use `kebab-case` namespaces.
Useful links:
-1. [`key`](https://vuejs.org/v2/guide/list.html#key)
+1. [Maintaining State](https://v2.vuejs.org/v2/guide/list.html#Maintaining-State)
1. [Vue Style Guide: Keyed v-for](https://vuejs.org/v2/style-guide/#Keyed-v-for-essential)
## Vue testing
@@ -448,10 +448,8 @@ Typically, when testing a Vue component, the component should be "re-mounted" in
To achieve this:
1. Create a mutable `wrapper` variable inside the top-level `describe` block.
-1. Mount the component using [`mount`](https://v1.test-utils.vuejs.org/api/#mount)/
-[`shallowMount`](https://v1.test-utils.vuejs.org/api/#shallowMount).
-1. Reassign the resulting [`Wrapper`](https://v1.test-utils.vuejs.org/api/wrapper/#wrapper)
-instance to our `wrapper` variable.
+1. Mount the component using [`mount`](https://v1.test-utils.vuejs.org/api/#mount) or [`shallowMount`](https://v1.test-utils.vuejs.org/api/#shallowMount).
+1. Reassign the resulting [`Wrapper`](https://v1.test-utils.vuejs.org/api/wrapper/#wrapper) instance to our `wrapper` variable.
Creating a global, mutable wrapper provides a number of advantages, including the ability to:
@@ -671,6 +669,6 @@ In the coming months you should fix that tech debt, with its priority to be dete
not be rewritten. For example, jQuery tests rewritten to Vue tests.
1. You may choose to use VueX as a centralized state management. If you choose not to use VueX, you
must use the *store pattern* which can be found in the
-[Vue.js documentation](https://vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch).
+[Vue.js documentation](https://v2.vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch).
1. Once you have chosen a centralized state-management solution you must use it for your entire
application. Don't mix and match your state-management solutions.
diff --git a/doc/development/fe_guide/tooling.md b/doc/development/fe_guide/tooling.md
index 2bb6cbfaf7a..4b55580c33c 100644
--- a/doc/development/fe_guide/tooling.md
+++ b/doc/development/fe_guide/tooling.md
@@ -63,8 +63,8 @@ disabled due to legacy compatibility reasons but they are in the process of bein
Do not disable specific ESLint rules. To avoid introducing technical debt, you may disable the following
rules only if you are invoking/instantiating existing code modules.
-- [`no-new`](https://eslint.org/docs/rules/no-new)
-- [`class-method-use-this`](https://eslint.org/docs/rules/class-methods-use-this)
+- [`no-new`](https://eslint.org/docs/latest/rules/no-new)
+- [`class-method-use-this`](https://eslint.org/docs/latest/rules/class-methods-use-this)
Disable these rules on a per-line basis. This makes it easier to refactor in the
future. For example, use `eslint-disable-next-line` or `eslint-disable-line`.
diff --git a/doc/development/fe_guide/troubleshooting.md b/doc/development/fe_guide/troubleshooting.md
index c0894621ed1..ab10c5bf988 100644
--- a/doc/development/fe_guide/troubleshooting.md
+++ b/doc/development/fe_guide/troubleshooting.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/engineering/ux/technical-writing/#assignments
---
-# Troubleshooting
+# Troubleshooting frontend development issues
Running into a problem? Maybe this will help ¯\_(ツ)_/¯.
diff --git a/doc/development/fe_guide/view_component.md b/doc/development/fe_guide/view_component.md
index 2e373e6933b..7ddce609ee7 100644
--- a/doc/development/fe_guide/view_component.md
+++ b/doc/development/fe_guide/view_component.md
@@ -33,7 +33,7 @@ Consider this list a best effort. The full list can be found in [`app/components
#### Alert
-The `Pajamas::AlertComponent` follows the [Pajamas Alert](https://design.gitlab.com/components/alert) specification.
+The `Pajamas::AlertComponent` follows the [Pajamas Alert](https://design.gitlab.com/components/alert/) specification.
**Examples:**
@@ -57,7 +57,7 @@ For the full list of options, see its
#### Banner
-The `Pajamas::BannerComponent` follows the [Pajamas Banner](https://design.gitlab.com/components/banner) specification.
+The `Pajamas::BannerComponent` follows the [Pajamas Banner](https://design.gitlab.com/components/banner/) specification.
**Examples:**
@@ -88,7 +88,7 @@ For the full list of options, see its
#### Button
-The `Pajamas::ButtonComponent` follows the [Pajamas Button](https://design.gitlab.com/components/button) specification.
+The `Pajamas::ButtonComponent` follows the [Pajamas Button](https://design.gitlab.com/components/button/) specification.
**Examples:**
@@ -125,7 +125,7 @@ For the full list of options, see its
#### Card
-The `Pajamas::CardComponent` follows the [Pajamas Card](https://design.gitlab.com/components/card) specification.
+The `Pajamas::CardComponent` follows the [Pajamas Card](https://design.gitlab.com/components/card/) specification.
**Examples:**
@@ -188,7 +188,7 @@ For the full list of options, see its
#### Toggle
-The `Pajamas::ToggleComponent` follows the [Pajamas Toggle](https://design.gitlab.com/components/toggle) specification.
+The `Pajamas::ToggleComponent` follows the [Pajamas Toggle](https://design.gitlab.com/components/toggle/) specification.
```haml
= render Pajamas::ToggleComponent.new(classes: 'js-force-push-toggle',
@@ -205,7 +205,34 @@ To actually initialize this component, make sure to call the `initToggle` helper
For the full list of options, see its
[source](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/components/pajamas/toggle_component.rb).
-### Best practices
+## Layouts
+
+Layout components can be used to create common layout patterns used in GitLab.
+
+### Available components
+
+#### Horizontal section
+
+Many of the settings pages use a layout where the title and description are on the left and the settings fields are on the right. The `Layouts::HorizontalSectionComponent` can be used to create this layout.
+
+**Example:**
+
+```haml
+= render ::Layouts::HorizontalSectionComponent.new(options: { class: 'gl-mb-6' }) do |c|
+ = c.title { _('Naming, visibility') }
+ = c.description do
+ = _('Update your group name, description, avatar, and visibility.')
+ = link_to _('Learn more about groups.'), help_page_path('user/group/index')
+ = c.body do
+ .form-group.gl-form-group
+ = f.label :name, _('New group name')
+ = f.text_field :name
+```
+
+For the full list of options, see its
+[source](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/components/layouts/horizontal_section_component.rb).
+
+## Best practices
- If you are about to create a new view in Haml, use the available components
over creating plain Haml tags with CSS classes.
diff --git a/doc/development/fe_guide/vue.md b/doc/development/fe_guide/vue.md
index 27660c0f5f7..5cb461c8ca0 100644
--- a/doc/development/fe_guide/vue.md
+++ b/doc/development/fe_guide/vue.md
@@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Vue
-To get started with Vue, read through [their documentation](https://vuejs.org/v2/guide/).
+To get started with Vue, read through [their documentation](https://v2.vuejs.org/v2/guide/index.html).
## Examples
@@ -23,8 +23,8 @@ The main goal we are trying to achieve is to have only one data flow, and only o
To achieve this goal we use [Vuex](#vuex).
You can also read about this architecture in Vue documentation about
-[state management](https://vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch)
-and about [one way data flow](https://vuejs.org/v2/guide/components.html#One-Way-Data-Flow).
+[state management](https://v2.vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch)
+and about [one way data flow](https://v2.vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow).
### Components and Store
@@ -322,7 +322,7 @@ For example, tables are used in a quite amount of places across GitLab, a table
would be a good fit for a component. On the other hand, a table cell used only
in one table would not be a good use of this pattern.
-You can read more about components in Vue.js site, [Component System](https://vuejs.org/v2/guide/#Composing-with-Components).
+You can read more about components in Vue.js site, [Component System](https://v2.vuejs.org/v2/guide/#Composing-with-Components).
### A folder for the Store
@@ -348,8 +348,7 @@ recommended to observe objects with their own stateful behavior.
Based on the Vue guidance:
-- **Do not** use or create a JavaScript class in your [data function](https://v2.vuejs.org/v2/api/#data),
-such as `user: new User()`.
+- **Do not** use or create a JavaScript class in your [data function](https://v2.vuejs.org/v2/api/#data).
- **Do not** add new JavaScript class implementations.
- **Do** use [GraphQL](../api_graphql_styleguide.md), [Vuex](vuex.md) or a set of components if
cannot use primitives or objects.
@@ -531,8 +530,7 @@ Each Vue component has a unique output. This output is always present in the ren
Although each method of a Vue component can be tested individually, our goal is to test the output
of the render function, which represents the state at all times.
-Visit the [Vue testing guide](https://v2.vuejs.org/v2/guide/testing.html#Unit-Testing) for help
-testing the rendered output.
+Visit the [Vue testing guide](https://v2.vuejs.org/v2/guide/testing.html#Unit-Testing) for help.
Here's an example of a well structured unit test for [this Vue component](#appendix---vue-component-subject-under-test):
@@ -671,7 +669,7 @@ it('should fire the click event', () => {
})
```
-When firing a Vue event, use [`emit`](https://vuejs.org/v2/guide/components-custom-events.html).
+When firing a Vue event, use [`emit`](https://v2.vuejs.org/v2/guide/components-custom-events.html).
```javascript
wrapper = shallowMount(DropdownItem);
diff --git a/doc/development/fe_guide/vuex.md b/doc/development/fe_guide/vuex.md
index 8bfb912161a..2d1569b7812 100644
--- a/doc/development/fe_guide/vuex.md
+++ b/doc/development/fe_guide/vuex.md
@@ -245,7 +245,7 @@ A mutation written like this is easier to maintain. In addition, we avoid errors
### `getters.js`
Sometimes we may need to get derived state based on store state, like filtering for a specific prop.
-Using a getter also caches the result based on dependencies due to [how computed props work](https://vuejs.org/v2/guide/computed.html#Computed-Caching-vs-Methods)
+Using a getter also caches the result based on dependencies due to [how computed props work](https://v2.vuejs.org/v2/guide/computed.html#Computed-Caching-vs-Methods)
This can be done through the `getters`:
```javascript
@@ -364,7 +364,7 @@ export default initialState => ({
We made the conscious decision to avoid this pattern to improve the ability to
discover and search our frontend codebase. The same applies
-when [providing data to a Vue app](vue.md#providing-data-from-haml-to-javascript). The reasoning for this is described in
+when [providing data to a Vue app](vue.md#providing-data-from-haml-to-javascript). The reasoning for this is described in
[this discussion](https://gitlab.com/gitlab-org/frontend/rfcs/-/issues/56#note_302514865):
> Consider a `someStateKey` is being used in the store state. You _may_ not be
diff --git a/doc/development/fe_guide/widgets.md b/doc/development/fe_guide/widgets.md
index b54f9add97d..c6bb89d1fe8 100644
--- a/doc/development/fe_guide/widgets.md
+++ b/doc/development/fe_guide/widgets.md
@@ -18,11 +18,11 @@ When building a widget, we should follow a few principles described below.
All widgets should use the same stack (Vue + Apollo Client).
To make it happen, we must add Vue Apollo to the application root (if we use a widget
as a component) or provide it directly to a widget. For sidebar widgets, use the
-[sidebar Apollo Client and Apollo Provider](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/sidebar/graphql.js):
+[issuable Apollo Client and Apollo Provider](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/graphql_shared/issuable_client.js):
```javascript
import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
-import { apolloProvider } from '~/sidebar/graphql';
+import { apolloProvider } from '~/graphql_shared/issuable_client';
function mountConfidentialComponent() {
new Vue({
@@ -118,7 +118,7 @@ In this case, we can use a renderless component that imports a client and listen
```javascript
import { fetchPolicies } from '~/lib/graphql';
import { confidentialityQueries } from '~/sidebar/constants';
-import { defaultClient as gqlClient } from '~/sidebar/graphql';
+import { defaultClient as gqlClient } from '~/graphql_shared/issuable_client';
created() {
if (this.issuableType !== IssuableType.Issue) {
diff --git a/doc/development/feature_development.md b/doc/development/feature_development.md
index e50c1edd282..fd1c7f4afa5 100644
--- a/doc/development/feature_development.md
+++ b/doc/development/feature_development.md
@@ -174,6 +174,7 @@ See [database guidelines](database/index.md).
## Domain-specific guides
- [CI/CD development documentation](cicd/index.md)
+- [Sec Section development documentation](sec/index.md)
## Technical Reference by Group
diff --git a/doc/development/feature_flags/controls.md b/doc/development/feature_flags/controls.md
index 8a862e5f7cd..63dad3070c7 100644
--- a/doc/development/feature_flags/controls.md
+++ b/doc/development/feature_flags/controls.md
@@ -112,6 +112,8 @@ incidents or in-progress change issues, for example:
2021-06-29 Canary deployment failing QA tests
```
+Before enabling a feature flag, verify that you are not violating any [Production Change Lock periods](https://about.gitlab.com/handbook/engineering/infrastructure/change-management/#production-change-lock-pcl) and are in compliance with the [Feature Flags and the Change Management Process](https://about.gitlab.com/handbook/engineering/infrastructure/change-management/#feature-flags-and-the-change-management-process).
+
The following `/chatops` commands should be performed in the Slack
`#production` channel.
@@ -337,7 +339,7 @@ take one of the following actions:
To remove a feature flag, open **one merge request** to make the changes. In the MR:
-1. Add the ~"feature flag" label so release managers are aware the changes are hidden behind a feature flag.
+1. Add the ~"feature flag" label so release managers are aware of the removal.
1. If the merge request has to be picked into a stable branch, add the
appropriate `~"Pick into X.Y"` label, for example `~"Pick into 13.0"`.
See [the feature flag process](https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle/#including-a-feature-behind-feature-flag-in-the-final-release)
diff --git a/doc/development/feature_flags/index.md b/doc/development/feature_flags/index.md
index 502a028f089..444b53f9c8d 100644
--- a/doc/development/feature_flags/index.md
+++ b/doc/development/feature_flags/index.md
@@ -433,8 +433,8 @@ When using the percentage rollout of actors on multiple feature flags, the actor
For example, the following feature flags are enabled for a certain percentage of actors:
```plaintext
-/chatops run chatops feature set feature-set-1 25 --actors
-/chatops run chatops feature set feature-set-2 25 --actors
+/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.
@@ -514,12 +514,12 @@ You can also enable a feature flag for a given gate:
Feature.enable(:feature_flag_name, Project.find_by_full_path("root/my-project"))
```
-### Removing a feature flag locally (in development)
+### Disabling a feature flag locally (in development)
When manually enabling or disabling a feature flag from the Rails console, its default value gets overwritten.
This can cause confusion when changing the flag's `default_enabled` attribute.
-To reset the feature flag to the default status, you can remove it in the rails console (`rails c`)
+To reset the feature flag to the default status, you can disable it in the rails console (`rails c`)
as follows:
```ruby
@@ -535,16 +535,18 @@ Feature.remove(:feature_flag_name)
```mermaid
graph LR
- A[flag: default off] -->|'added' / 'changed'| B(flag: default on)
+ A[flag: default off] -->|'added' / 'changed' / 'fixed' / '...'| B(flag: default on)
B -->|'other'| C(remove flag, keep new code)
B -->|'removed' / 'changed'| D(remove flag, keep old code)
- A -->|'added' / 'changed'| C
+ A -->|'added' / 'changed' / 'fixed' / '...'| C
A -->|no changelog| D
```
- Any change behind a feature flag that is **enabled** by default **should** have a changelog entry.
- The changelog for a feature flag should describe the feature and not the
flag, unless a default on feature flag is removed keeping the new code (`other` in the flowchart above).
+- A feature flag can also be used for rolling out a bug fix or a maintenance work. In this scenario, the changelog
+ must be related to it, for example; `fixed` or `other`.
## Feature flags in tests
diff --git a/doc/development/fips_compliance.md b/doc/development/fips_compliance.md
index c690408ee60..1029ed88eac 100644
--- a/doc/development/fips_compliance.md
+++ b/doc/development/fips_compliance.md
@@ -65,7 +65,7 @@ listed here that also do not work properly in FIPS mode:
- [Solutions for vulnerabilities](../user/application_security/vulnerabilities/index.md#resolve-a-vulnerability)
for yarn projects.
- [Static Application Security Testing (SAST)](../user/application_security/sast/index.md)
- supports a reduced set of [analyzers](../user/application_security/sast/#fips-enabled-images)
+ supports a reduced set of [analyzers](../user/application_security/sast/index.md#fips-enabled-images)
when operating in FIPS-compliant mode.
- Advanced Search is currently not included in FIPS mode. It must not be enabled in order to be FIPS-compliant.
- [Gravatar or Libravatar-based profile images](../administration/libravatar.md) are not FIPS-compliant.
@@ -578,6 +578,6 @@ Merge requests that can trigger Package and QA, can trigger a FIPS package and a
Reference Architecture test pipeline. The base image used for the trigger is
Ubuntu 20.04 FIPS:
-1. Trigger `package-and-qa`, if not already triggered.
+1. Trigger `e2e:package-and-test` job, if not already triggered.
1. On the `gitlab-omnibus-mirror` child pipeline, manually trigger `Trigger:package:fips`.
1. When the package job is complete, manually trigger the `RAT:FIPS` job.
diff --git a/doc/development/gemfile.md b/doc/development/gemfile.md
index f9cf69020bb..87304a761ea 100644
--- a/doc/development/gemfile.md
+++ b/doc/development/gemfile.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/engineering/ux/technical-writing/#assignments
---
-# `Gemfile` guidelines
+# Gemfile guidelines
When adding a new entry to `Gemfile` or upgrading an existing dependency pay
attention to the following rules.
@@ -15,6 +15,23 @@ 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.
+## Request an Appsec review
+
+When adding a new gem to our `Gemfile` or even changing versions in
+`Gemfile.lock` it is strongly recommended that you
+[request a Security review](https://about.gitlab.com/handbook/engineering/security/#how-to-request-a-security-review).
+New gems add an extra security risk for GitLab, and it is important to
+evaluate this risk before we ship this to production. Technically, just adding
+a new gem and pushing to a branch in our main `gitlab` project is a security
+risk as it will run in CI using your GitLab.com credentials. As such you should
+evaluate early on if you think this gem seems legitimate before you even
+install it.
+
+Reviewers should also be aware of our related
+[recommendations for reviewing community contributions](code_review.md#community-contributions)
+and take care before running a pipeline for community contributions that
+contains changes to `Gemfile` or `Gemfile.lock`.
+
## License compliance
Refer to [licensing guidelines](licensing.md) for ensuring license compliance.
@@ -73,7 +90,7 @@ to a gem, go through these steps:
apply if someone who currently works at GitLab wants to maintain
the gem beyond their time working at GitLab.
-When publishing a gem to RubyGems.org, also note the section on
+When publishing a gem to RubyGems.org, also note the section on
[gem owners](https://about.gitlab.com/handbook/developer-onboarding/#ruby-gems)
in the handbook.
@@ -132,7 +149,7 @@ that also relied on `thor` but had its version pinned to a vulnerable
one. These changes are easy to miss in the `Gemfile.lock`. Pinning the
version would result in a conflict that would need to be solved.
-To avoid upgrading indirect dependencies, we can use
+To avoid upgrading indirect dependencies, we can use
[`bundle update --conservative`](https://bundler.io/man/bundle-update.1.html#OPTIONS).
When submitting a merge request including a dependency update,
diff --git a/doc/development/geo.md b/doc/development/geo.md
index f042af42de5..1ae9d9ee32b 100644
--- a/doc/development/geo.md
+++ b/doc/development/geo.md
@@ -653,8 +653,7 @@ on, check out our [self-service framework](geo/framework.md).
### GET:Geo pipeline
-As part of the [package-and-qa](testing_guide/end_to_end/index.md#using-the-package-and-qa-job) pipeline, there is an option to manually trigger a job named `GET:Geo`. This
-pipeline uses [GET](https://gitlab.com/gitlab-org/gitlab-environment-toolkit) to spin up a
+As part of the [e2e:package-and-test](testing_guide/end_to_end/index.md#using-the-package-and-test-job) pipeline, there is an option to manually trigger a job named `GET:Geo`. This pipeline uses [GET](https://gitlab.com/gitlab-org/gitlab-environment-toolkit) to spin up a
[1k](../administration/reference_architectures/1k_users.md) Geo installation,
and run the [`gitlab-qa`](https://gitlab.com/gitlab-org/gitlab-qa) Geo scenario against the instance.
When working on Geo features, it is a good idea to ensure the `qa-geo` job passes in a triggered `GET:Geo pipeline`.
@@ -669,7 +668,7 @@ see the [QA documentation](https://gitlab.com/gitlab-org/gitlab/-/tree/master/qa
The pipeline involves the interaction of multiple different projects:
-- [GitLab](https://gitlab.com/gitlab-org/gitlab) - The [package-and-qa job](testing_guide/end_to_end/index.md#using-the-package-and-qa-job) is launched from merge requests in this project.
+- [GitLab](https://gitlab.com/gitlab-org/gitlab) - The [`e2e:package-and-test` job](testing_guide/end_to_end/index.md#using-the-package-and-test-job) is launched from merge requests in this project.
- [`omnibus-gitlab`](https://gitlab.com/gitlab-org/omnibus-gitlab) - Builds relevant artifacts containing the changes from the triggering merge request pipeline.
- [GET-Configs/Geo](https://gitlab.com/gitlab-org/quality/gitlab-environment-toolkit-configs/Geo) - Coordinates the lifecycle of a short-lived Geo installation that can be evaluated.
- [GET](https://gitlab.com/gitlab-org/gitlab-environment-toolkit) - Contains the necessary logic for creating and destroying Geo installations. Used by `GET-Configs/Geo`.
diff --git a/doc/development/geo/proxying.md b/doc/development/geo/proxying.md
index 2f0226c489c..d4cb611e965 100644
--- a/doc/development/geo/proxying.md
+++ b/doc/development/geo/proxying.md
@@ -128,7 +128,7 @@ Secondary-->>Client: admin/geo/replication/projects logged in response (session
## Git pull
-For historical reasons, the `push_from_secondary` path is used to forward a Git pull. There is
+For historical reasons, the `push_from_secondary` path is used to forward a Git pull. There is
[an issue proposing to rename this route](https://gitlab.com/gitlab-org/gitlab/-/issues/292690) to avoid confusion.
### Git pull over HTTP(s)
diff --git a/doc/development/git_object_deduplication.md b/doc/development/git_object_deduplication.md
index a6b359769f8..a20bdf633cd 100644
--- a/doc/development/git_object_deduplication.md
+++ b/doc/development/git_object_deduplication.md
@@ -18,7 +18,7 @@ GitLab implements Git object deduplication.
### Understanding Git alternates
-At the Git level, we achieve deduplication by using
+At the Git level, we achieve deduplication by using
[Git alternates](https://git-scm.com/docs/gitrepository-layout#gitrepository-layout-objects).
Git alternates is a mechanism that lets a repository borrow objects from
another repository on the same machine.
@@ -99,7 +99,7 @@ are as follows:
### Assumptions
-- All repositories in a pool must use [hashed storage](../administration/repository_storage_types.md).
+- All repositories in a pool must use [hashed storage](../administration/repository_storage_types.md).
This is so that we don't have to ever worry about updating paths in
`object/info/alternates` files.
- All repositories in a pool must be on the same Gitaly storage shard.
diff --git a/doc/development/github_importer.md b/doc/development/github_importer.md
index 0aa1bad711d..9740ae04553 100644
--- a/doc/development/github_importer.md
+++ b/doc/development/github_importer.md
@@ -71,7 +71,7 @@ This worker imports all pull requests. For every pull request a job for the
### 5. Stage::ImportPullRequestsMergedByWorker
-This worker imports the pull requests' _merged-by_ user information. The
+This worker imports the pull requests' _merged-by_ user information. The
[_List pull requests_](https://docs.github.com/en/rest/pulls#list-pull-requests)
API doesn't provide this information. Therefore, this stage must fetch each merged pull request
individually to import this information. A
@@ -101,7 +101,20 @@ label links in the same worker removes the need for performing a separate crawl
through the API data, reducing the number of API calls necessary to import a
project.
-### 8. Stage::ImportNotesWorker
+### 8. Stage::ImportIssueEventsWorker
+
+This worker imports all issues and pull request events. For every event, we
+schedule a job for the `Gitlab::GithubImport::ImportIssueEventWorker` worker.
+
+We can import both issues and pull request events by single stage because of a specific aspect of the GitHub API. It looks like that under the hood, issues and pull requests
+GitHub are stored in a single table. Therefore, they have globally-unique IDs and so:
+
+- Every pull request is an issue.
+- Issues aren't pull requests.
+
+Therefore, both issues and pull requests have a common API for most related things.
+
+### 9. Stage::ImportNotesWorker
This worker imports regular comments for both issues and pull requests. For
every comment, we schedule a job for the
@@ -112,7 +125,28 @@ returns comments for both issues and pull requests. This means we have to wait
for all issues and pull requests to be imported before we can import regular
comments.
-### 9. Stage::FinishImportWorker
+### 10. Stage::ImportAttachmentsWorker
+
+This worker imports release notes attachments that are linked inside Markdown.
+For every release of the project, we schedule a job of
+`Gitlab::GithubImport::ImportReleaseAttachmentsWorker` for every comment.
+
+Each job:
+
+1. Iterates over all attachment links inside of a specific release note.
+1. Downloads the attachment.
+1. Replaces the old link with a newly-generated link to GitLab.
+
+### 11. Stage::ImportProtectedBranchesWorker
+
+This worker imports protected branch rules.
+For every rule that exists on GitHub, we schedule a job of
+`Gitlab::GithubImport::ImportProtectedBranchWorker`.
+
+Each job compares the branch protection rules from GitHub and GitLab and applies
+the strictest of the rules to the branches in GitLab.
+
+### 12. Stage::FinishImportWorker
This worker completes the import process by performing some housekeeping
(such as flushing any caches) and by marking the import as completed.
diff --git a/doc/development/gitlab_flavored_markdown/specification_guide/index.md b/doc/development/gitlab_flavored_markdown/specification_guide/index.md
index 756b87cd407..c1227e5d33f 100644
--- a/doc/development/gitlab_flavored_markdown/specification_guide/index.md
+++ b/doc/development/gitlab_flavored_markdown/specification_guide/index.md
@@ -345,8 +345,50 @@ For the [Markdown snapshot testing](#markdown-snapshot-testing) to work
properly, you must account for these differences in a way that ensures the tests are reliable,
and always behave the same across different test runs or environments.
-To account for these differences, there is a process called **_normalization_**. Normalization
-allows custom regular expressions with
+To account for these differences, there is a process called **_normalization_**. Several ways to approach normalization exist:
+
+1. Fixture-based normalization
+1. Environment-variable-based normalization
+1. Regex-based normalization
+
+#### Fixture-based normalization
+
+Fixture-based normalization should be used whenever possible, because it is simpler and easier to
+understand than regex-based normalization.
+
+The [Markdown snapshot testing](#markdown-snapshot-testing) uses RSpec to generate the
+[example snapshot files](#example-snapshot-files). RSpec enables you to:
+
+- Use the same powerful fixture support and helpers as all the rest of the GitLab RSpec suite.
+- Use fixtures to control the state of the database when the example snapshots are generated.
+- Extract this fixture setup to an RSpec shared context. This shared context is used to ensure
+ the same database state exists wherever the snapshot tests are run, either by the CI suite, or
+ locally via [`run-snapshot-tests.sh`](#run-snapshot-testssh-script).
+
+You can see the RSpec shared context containing these fixtures in
+[`spec/support/shared_contexts/glfm/example_snapshot_fixtures.rb`](https://gitlab.com/gitlab-org/gitlab/blob/master/spec/support/shared_contexts/glfm/example_snapshot_fixtures.rb).
+
+#### Environment-variable-based normalization
+
+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
+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.
+
+The current example of this is when normally random footnote IDs are overridden to be deterministic
+by setting `GITLAB_TEST_FOOTNOTE_ID`. It is set along with the fixtures setup in the
+[`spec/support/shared_contexts/glfm/example_snapshot_fixtures.rb`](https://gitlab.com/gitlab-org/gitlab/blob/master/spec/support/shared_contexts/glfm/example_snapshot_fixtures.rb)
+shared context.
+
+#### Regex-based normalization
+
+If neither fixture-based nor environment-variable-based normalization can be used, use regex-based
+normalization. It is powerful, but more complex, and requires more maintenance.
+It requires referring to specific examples by name, and crafting the proper regexes.
+
+Regex-based normalization allows custom regular expressions with
[_capturing groups_](https://ruby-doc.org/core-3.1.2/Regexp.html#class-Regexp-label-Capturing)
to be applied to two different versions of HTML or JSON for a given Markdown example,
and the contents of the captured groups can be replaced with the same fixed values.
@@ -653,10 +695,16 @@ is the manually updated canonical Markdown+HTML examples for GLFM extensions.
- It contains examples in the [standard backtick-delimited `spec.txt` format](#various-markdown-specifications),
each of which contain a Markdown example and the corresponding canonical HTML.
+- For all GitLab examples, the "extension" annotation after the backticks should consist of only
+ `example gitlab`. It does not currently include any additional extension annotations describing
+ the specific Markdown, unlike the GitHub Flavored Markdown examples, which do include
+ these additional annotations (such as `example strikethrough`).
- The `update-specification.rb` script inserts it as new sections before the appendix
of generated `spec.txt`.
-- It should consist of `H1` header sections, with all examples nested exactly 2 levels deep within `H2`
- header sections.
+- It should consist of `H1` header sections, with all examples nested either 2 or 3 levels deep
+ within `H2` or `H3` header sections.
+- `H3` header sections must be nested within `H2` header sections. They cannot be
+ nested directly within `H1` header sections.
`glfm_specification/input/gitlab_flavored_markdown/glfm_canonical_examples.txt` sample entries:
@@ -670,7 +718,7 @@ The actual file should not have these prefixed `|` characters.
|
|## Strong but with two asterisks
|
-|```````````````````````````````` example
+|```````````````````````````````` example gitlab
|**bold**
|.
|<p><strong>bold</strong></p>
@@ -680,7 +728,7 @@ The actual file should not have these prefixed `|` characters.
|
|## Strong but with HTML
|
-|```````````````````````````````` example
+|```````````````````````````````` example gitlab
|<strong>
|bold
|</strong>
@@ -738,7 +786,7 @@ The following optional entries are supported for each example. They all default
`glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml` sample entry:
```yaml
-07_99_an_example_with_incomplete_wysiwyg_implementation_1:
+07_99_00_an_example_with_incomplete_wysiwyg_implementation_1:
skip_update_example_snapshots: 'An explanation of the reason for skipping.'
skip_update_example_snapshot_html_static: 'An explanation of the reason for skipping.'
skip_update_example_snapshot_html_wysiwyg: 'An explanation of the reason for skipping.'
@@ -771,40 +819,73 @@ to be specified for a Markdown example.
00_uri: &00_uri
- regex: '(href|data-src)(=")(.*?)(test-file\.(png|zip)")'
replacement: '\1\2URI_PREFIX\4'
-01_01__section_one__example_containing_a_uri__001:
+01_01_00__section_one__example_containing_a_uri__001:
html:
static:
canonical:
- 01_01_uri: *00_uri
+ 01_01_00_uri: *00_uri
snapshot:
- 01_01_uri: *00_uri
+ 01_01_00_uri: *00_uri
wysiwyg:
- 01_01_uri: *00_uri
+ 01_01_00_uri: *00_uri
prosemirror_json:
- 01_01_uri: *00_uri
-07_01__gitlab_specific_markdown__footnotes__001:
+ 01_01_00_uri: *00_uri
+07_01_00__gitlab_specific_markdown__footnotes__001:
# YAML anchors which are only shared within a single example should be defined within the example
shared:
- 07_01_href: &07_01_href
+ 07_01_00_href: &07_01_00_href
- regex: '(href)(=")(.+?)(")'
replacement: '\1\2REF\4'
- 07_01_id: &07_01_id
+ 07_01_00_id: &07_01_00_id
- regex: '(id)(=")(.+?)(")'
replacement: '\1\2ID\4'
html:
static:
canonical:
- 07_01_href: *07_01_href
- 07_01_id: *07_01_id
+ 07_01_00_href: *07_01_00_href
+ 07_01_00_id: *07_01_00_id
snapshot:
- 07_01_href: *07_01_href
- 07_01_id: *07_01_id
+ 07_01_00_href: *07_01_00_href
+ 07_01_00_id: *07_01_00_id
wysiwyg:
- 07_01_href: *07_01_href
- 07_01_id: *07_01_id
+ 07_01_00_href: *07_01_00_href
+ 07_01_00_id: *07_01_00_id
prosemirror_json:
- 07_01_href: *07_01_href
- 07_01_id: *07_01_id
+ 07_01_00_href: *07_01_00_href
+ 07_01_00_id: *07_01_00_id
+```
+
+##### `glfm_example_metadata.yml`
+
+[`glfm_specification/input/gitlab_flavored_markdown/glfm_example_metadata.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/input/gitlab_flavored_markdown/glfm_example_metadata.yml)
+allows control over other aspects of the snapshot example generation process.
+
+- It is manually updated.
+- The `ee` fields determine whether the example is an EE-only example. If the `ee` field is `true`,
+ 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
+ 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.
+
+`glfm_specification/input/gitlab_flavored_markdown/glfm_example_metadata.yml` sample entries:
+
+```yaml
+---
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__001:
+ api_request_override_path: /groups/glfm_group/preview_markdown
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__002:
+ api_request_override_path: /glfm_group/glfm_project/preview_markdown
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__003:
+ api_request_override_path: /glfm_group/glfm_project/preview_markdown
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__004:
+ api_request_override_path: /-/snippets/preview_markdown
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__005:
+ api_request_override_path: /glfm_group/glfm_project/-/wikis/new_page/preview_markdown
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__006:
+ ee: true
+ api_request_override_path: /groups/glfm_group/-/wikis/new_page/preview_markdown
```
#### Output specification files
@@ -891,19 +972,19 @@ CommonMark, GFM, and GLFM example names, each with a unique canonical name.
`glfm_specification/example_snapshots/examples_index.yml` sample entries:
```yaml
-02_01_preliminaries_characters_and_lines_1:
+02_01_00_preliminaries_characters_and_lines_1:
spec_txt_example_position: 1
source_specification: commonmark
-03_01_blocks_and_inlines_precedence_1:
+03_01_00_blocks_and_inlines_precedence_1:
spec_txt_example_position: 12
source_specification: commonmark
-05_03_container_blocks_task_list_items_1:
+05_03_00_container_blocks_task_list_items_1:
spec_txt_example_position: 279
source_specification: github
-06_04_inlines_emphasis_and_strong_emphasis_1:
+06_04_00_inlines_emphasis_and_strong_emphasis_1:
spec_txt_example_position: 360
source_specification: github
-07_01_audio_link_1:
+07_01_00_audio_link_1:
spec_txt_example_position: 301
source_specification: gitlab
```
@@ -923,7 +1004,7 @@ for each entry in `glfm_specification/example_snapshots/examples_index.yml`
`glfm_specification/example_snapshots/markdown.yml` sample entry:
```yaml
-06_04_inlines_emphasis_and_strong_emphasis_1: |
+06_04_00_inlines_emphasis_and_strong_emphasis_1: |
*foo bar*
```
@@ -958,7 +1039,7 @@ Any exceptions or failures which occur when generating HTML are replaced with an
`glfm_specification/example_snapshots/html.yml` sample entry:
```yaml
-06_04_inlines_emphasis_and_strong_emphasis_1:
+06_04_00_inlines_emphasis_and_strong_emphasis_1:
canonical: |
<p><em>foo bar</em></p>
static: |
@@ -983,7 +1064,7 @@ contains the ProseMirror JSON for each entry in `glfm_specification/example_snap
`glfm_specification/example_snapshots/prosemirror_json.yml` sample entry:
```yaml
-06_04_inlines_emphasis_and_strong_emphasis_1: |-
+06_04_00_inlines_emphasis_and_strong_emphasis_1: |-
{
"type": "doc",
"content": [
diff --git a/doc/development/go_guide/dependencies.md b/doc/development/go_guide/dependencies.md
index 2a53fa590e3..7cad5bbf417 100644
--- a/doc/development/go_guide/dependencies.md
+++ b/doc/development/go_guide/dependencies.md
@@ -44,8 +44,8 @@ end with a timestamp and the first 12 characters of the commit identifier:
If a VCS tag matches one of these patterns, it is ignored.
-For a complete understanding of Go modules and versioning, see
-[this series of blog posts](https://go.dev/blog/using-go-modules)
+For a complete understanding of Go modules and versioning, see
+[this series of blog posts](https://go.dev/blog/using-go-modules)
on the official Go website.
## 'Module' vs 'Package'
diff --git a/doc/development/go_guide/index.md b/doc/development/go_guide/index.md
index 711b0662a8c..3adafa8750f 100644
--- a/doc/development/go_guide/index.md
+++ b/doc/development/go_guide/index.md
@@ -145,7 +145,7 @@ Go GitLab linter plugins are maintained in the [`gitlab-org/language-tools/go/li
## Dependencies
Dependencies should be kept to the minimum. The introduction of a new
-dependency should be argued in the merge request, as per our [Approval Guidelines](../code_review.md#approval-guidelines).
+dependency should be argued in the merge request, as per our [Approval Guidelines](../code_review.md#approval-guidelines).
Both [License Scanning](../../user/compliance/license_compliance/index.md)
and [Dependency Scanning](../../user/application_security/dependency_scanning/index.md)
should be activated on all projects to ensure new dependencies
@@ -153,7 +153,7 @@ security status and license compatibility.
### Modules
-In Go 1.11 and later, a standard dependency system is available behind the name
+In Go 1.11 and later, a standard dependency system is available behind the name
[Go Modules](https://github.com/golang/go/wiki/Modules). It provides a way to
define and lock dependencies for reproducible builds. It should be used
whenever possible.
@@ -166,7 +166,7 @@ projects, and makes merge requests easier to review.
In some cases, such as building a Go project for it to act as a dependency of a
CI run for another project, removing the `vendor/` directory means the code must
be downloaded repeatedly, which can lead to intermittent problems due to rate
-limiting or network failures. In these circumstances, you should
+limiting or network failures. In these circumstances, you should
[cache the downloaded code between](../../ci/caching/index.md#cache-go-dependencies).
There was a
diff --git a/doc/development/import_export.md b/doc/development/import_export.md
index 6cbbb6bf716..c66ac0418ac 100644
--- a/doc/development/import_export.md
+++ b/doc/development/import_export.md
@@ -305,6 +305,29 @@ export_reorders:
nulls_position: :nulls_last
```
+### Conditional export
+
+When associated resources are from outside the project, you might need to
+validate that a user who is exporting the project or 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.
+
+For example:
+
+```yaml
+include_if_exportable:
+ project:
+ issues:
+ - epic_issue
+```
+
+This definition:
+
+1. Calls the issue's `exportable_association?(:epic_issue, current_user: current_user)` method.
+1. If the method returns true, includes the issue's `epic_issue` association for the issue.
+
### Import
The import job status moves from `none` to `finished` or `failed` into different states:
diff --git a/doc/development/import_project.md b/doc/development/import_project.md
index 7c55d2e2668..1f3bf860257 100644
--- a/doc/development/import_project.md
+++ b/doc/development/import_project.md
@@ -149,26 +149,10 @@ You might see an error like `N is out of range for ActiveModel::Type::Integer wi
where `N` is the integer exceeding the 4-byte integer limit. If that's the case, you
are likely hitting the issue with rebalancing of `relative_position` field of the issues.
-The feature flag to enable the rebalance automatically was enabled on GitLab.com.
-We intend to enable it by default on self-managed instances when the issue
-[Rebalance issues FF rollout](https://gitlab.com/gitlab-org/gitlab/-/issues/343368)
-is implemented.
-
-If the feature is not enabled by default on your GitLab version, run the following
-commands in the [Rails console](../administration/operations/rails_console.md) as
-a workaround. Replace the ID with the ID of your project you were trying to import:
-
```ruby
-# Check if the feature is enabled on your instance. If it is, rebalance should work automatically on your instance
-Feature.enabled?(:rebalance_issues,Project.find(ID).root_namespace)
-
# Check the current maximum value of relative_position
Issue.where(project_id: Project.find(ID).root_namespace.all_projects).maximum(:relative_position)
-# Enable `rebalance_issues` feauture and check that it was successfully enabled
-Feature.enable(:rebalance_issues,Project.find(ID).root_namespace)
-Feature.enabled?(:rebalance_issues,Project.find(ID).root_namespace)
-
# Run the rebalancing process and check if the maximum value of relative_position has changed
Issues::RelativePositionRebalancingService.new(Project.find(ID).root_namespace.all_projects).execute
Issue.where(project_id: Project.find(ID).root_namespace.all_projects).maximum(:relative_position)
diff --git a/doc/development/integrations/jenkins.md b/doc/development/integrations/jenkins.md
index f430fc380b1..bb541d26df2 100644
--- a/doc/development/integrations/jenkins.md
+++ b/doc/development/integrations/jenkins.md
@@ -24,7 +24,7 @@ brew services start jenkins
GitLab does not allow requests to localhost or the local network by default. When running Jenkins on your local machine, you need to enable local access.
1. Log into your GitLab instance as an administrator.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**.
1. Expand **Outbound requests** and check the following checkboxes:
diff --git a/doc/development/integrations/secure.md b/doc/development/integrations/secure.md
index 55e57a3c2ee..2c5dd1c0500 100644
--- a/doc/development/integrations/secure.md
+++ b/doc/development/integrations/secure.md
@@ -1,6 +1,6 @@
---
-stage: Protect
-group: Container Security
+stage: Secure
+group: Static Analysis
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
---
@@ -254,7 +254,7 @@ Following the POSIX exit code standard, the scanner exits with 0 for success and
Success also includes the case when vulnerabilities are found.
When a CI job fails, security report results are not ingested by GitLab, even if the job
-[allows failure](../../ci/yaml/#allow_failure). The report artifacts are still uploaded to GitLab and available
+[allows failure](../../ci/yaml/index.md#allow_failure). The report artifacts are still uploaded to GitLab and available
for [download in the pipeline security tab](../../user/application_security/vulnerability_report/pipeline.md#download-security-scan-outputs).
When executing a scanning job using the [Docker-in-Docker privileged mode](../../user/application_security/sast/index.md#requirements),
@@ -488,8 +488,8 @@ the risk. End-users interact with this field, whereas GitLab automatically proce
##### Identifiers
The `identifiers` array describes the detected vulnerability. An identifier object's `type` and
-`value` fields are used to tell if two identifiers are the same. The user interface uses the
-object's `name` and `url` fields to display the identifier.
+`value` fields are used to [tell if two identifiers are the same](../../user/application_security/vulnerability_report/pipeline.md#deduplication-process).
+The user interface uses the object's `name` and `url` fields to display the identifier.
We recommend that you use the identifiers the GitLab scanners already define:
@@ -509,12 +509,10 @@ which is shared by some of the analyzers that GitLab maintains. You can [contrib
new generic identifiers to if needed. Analyzers may also produce vendor-specific or product-specific
identifiers, which don't belong in the [common library](https://gitlab.com/gitlab-org/security-products/analyzers/common).
-The first item of the `identifiers` array is called the
-[primary identifier](../../user/application_security/terminology/index.md#primary-identifier).
-The primary identifier is particularly important, because it is used to
+The first item of the `identifiers` array is called the
+[primary identifier](../../user/application_security/terminology/index.md#primary-identifier), and
+it is used to
[track vulnerabilities](#tracking-and-merging-vulnerabilities) as new commits are pushed to the repository.
-Identifiers are also used to [merge duplicate vulnerabilities](#tracking-and-merging-vulnerabilities)
-reported for the same commit, except for `CWE` and `WASC`.
Not all vulnerabilities have CVEs, and a CVE can be identified multiple times. As a result, a CVE
isn't a stable identifier and you shouldn't assume it as such when tracking vulnerabilities.
@@ -666,11 +664,14 @@ Users may give feedback on a vulnerability:
GitLab tracks vulnerabilities so that user feedback is not lost
when new Git commits are pushed to the repository.
-Vulnerabilities are tracked using a combination of three attributes:
+Vulnerabilities are tracked using a
+[`UUIDv5`](https://gitlab.com/gitlab-org/gitlab/-/blob/1272957c4a55e616569721febccb685c056ca1e4/ee/app/models/vulnerabilities/finding.rb#L364-368)
+digest, which is generated by a `SHA-1` hash of four attributes:
- [Report type](#category)
-- [Location fingerprint](#location)
- [Primary identifier](#identifiers)
+- [Location fingerprint](#location)
+- Project ID
Right now, GitLab cannot track a vulnerability if its location changes
as new Git commits are pushed, and this results in user feedback being lost.
@@ -678,12 +679,7 @@ For instance, user feedback on a SAST vulnerability is lost
if the affected file is renamed or the affected line moves down.
This is addressed in [issue #7586](https://gitlab.com/gitlab-org/gitlab/-/issues/7586).
-In some cases, the multiple scans executed in the same CI pipeline result in duplicates
-that are automatically merged using the vulnerability location and identifiers.
-Two vulnerabilities are considered to be the same if they share the same [location fingerprint](#location)
-and at least one [identifier](#identifiers). Two identifiers are the same if they share the same `type` and `id`.
-CWE and WASC identifiers are not considered because they describe categories of vulnerability flaws,
-but not specific security flaws.
+See also [deduplication process](../../user/application_security/vulnerability_report/pipeline.md#deduplication-process).
##### Severity and confidence
diff --git a/doc/development/internal_api/index.md b/doc/development/internal_api/index.md
index 9b29af3e433..c35d40a7b7f 100644
--- a/doc/development/internal_api/index.md
+++ b/doc/development/internal_api/index.md
@@ -148,7 +148,7 @@ curl --request POST --header "Gitlab-Shared-Secret: <Base64 encoded token>" \
## Authorized Keys Check
This endpoint is called by the GitLab Shell authorized keys
-check. Which is called by OpenSSH for
+check. Which is called by OpenSSH for
[fast SSH key lookup](../../administration/operations/fast_ssh_key_lookup.md).
| Attribute | Type | Required | Description |
diff --git a/doc/development/internal_users.md b/doc/development/internal_users.md
index 95ca593e31e..24785c0cf48 100644
--- a/doc/development/internal_users.md
+++ b/doc/development/internal_users.md
@@ -8,6 +8,8 @@ info: "See the Technical Writers assigned to Development Guidelines: https://abo
# Internal users
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97584) in GitLab 15.4, bots are indicated with a badge in user listings.
+
GitLab uses internal users (sometimes referred to as "bots") to perform
actions or functions that cannot be attributed to a regular user.
diff --git a/doc/development/lfs.md b/doc/development/lfs.md
index 5900eb68294..20157e9e805 100644
--- a/doc/development/lfs.md
+++ b/doc/development/lfs.md
@@ -76,13 +76,13 @@ process, which writes the contents to the standard output.
1. The archive data is sent back to the client.
In step 7, the `gitaly-lfs-smudge` filter must talk to Workhorse, not to
-Rails, or an invalid LFS blob is saved. To support this, GitLab 13.5
+Rails, or an invalid LFS blob is saved. To support this, GitLab 13.5
[changed the default Omnibus configuration to have Gitaly talk to the Workhorse](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/4592)
instead of Rails.
One side effect of this change: the correlation ID of the original
request is not preserved for the internal API requests made by Gitaly
(or `gitaly-lfs-smudge`), such as the one made in step 8. The
-correlation IDs for those API requests are random values until
+correlation IDs for those API requests are random values until
[this Workhorse issue](https://gitlab.com/gitlab-org/gitlab-workhorse/-/issues/309) is
resolved.
diff --git a/doc/development/logging.md b/doc/development/logging.md
index f1fa7f4c8c9..467fb68f3ae 100644
--- a/doc/development/logging.md
+++ b/doc/development/logging.md
@@ -385,7 +385,7 @@ end
## Additional steps with new log files
1. Consider log retention settings. By default, Omnibus rotates any
- logs in `/var/log/gitlab/gitlab-rails/*.log` every hour and
+ logs in `/var/log/gitlab/gitlab-rails/*.log` every hour and
[keep at most 30 compressed files](https://docs.gitlab.com/omnibus/settings/logs.html#logrotate).
On GitLab.com, that setting is only 6 compressed files. These settings should suffice
for most users, but you may need to tweak them in [Omnibus GitLab](https://gitlab.com/gitlab-org/omnibus-gitlab).
@@ -395,7 +395,7 @@ end
a merge request to the [`gitlab_fluentd`](https://gitlab.com/gitlab-cookbooks/gitlab_fluentd)
project. See [this example](https://gitlab.com/gitlab-cookbooks/gitlab_fluentd/-/merge_requests/51/diffs).
-1. Be sure to update the [GitLab CE/EE documentation](../administration/logs/index.md) and the
+1. Be sure to update the [GitLab CE/EE documentation](../administration/logs/index.md) and the
[GitLab.com runbooks](https://gitlab.com/gitlab-com/runbooks/blob/master/docs/logging/README.md).
## Control logging visibility
diff --git a/doc/development/merge_request_concepts/index.md b/doc/development/merge_request_concepts/index.md
index 331f0e01579..d463f6ba290 100644
--- a/doc/development/merge_request_concepts/index.md
+++ b/doc/development/merge_request_concepts/index.md
@@ -10,7 +10,7 @@ info: "See the Technical Writers assigned to Development Guidelines: https://abo
NOTE:
The documentation below is the single source of truth for the merge request terminology and functionality.
-The merge request is made up of several different key components and ideas that encompass the overall merge request experience. These concepts sometimes have competing and confusing terminology or overlap with other concepts. The concepts this will cover are:
+The merge request is made up of several different key components and ideas that encompass the overall merge request experience. These concepts sometimes have competing and confusing terminology or overlap with other concepts. This page covers the following concepts:
1. Merge widget
1. Report widgets
@@ -40,15 +40,17 @@ Reports are widgets within the merge request that report information about chang
## Merge checks
-Merge checks are statuses that can either pass or fail and conditionally control the availability of the merge button being available within a merge request. The key distinguishing factor in a merge check is that users **do not** interact with the merge checks inside of the merge request, but are able to influence whether or not the check passes or fails. Results from the check are processed as true/false to determine whether or not a merge request can be merged. Examples include:
+Merge checks are statuses that can either pass or fail and conditionally control the availability of the merge button being available within a merge request. The key distinguishing factor in a merge check is that users **do not** interact with the merge checks inside of the merge request, but are able to influence whether or not the check passes or fails. Results from the check are processed as true/false to determine whether or not a merge request can be merged.
-- Merge conflicts.
-- Pipeline success.
-- Threads resolution.
-- [External status checks](../../user/project/merge_requests/status_checks.md).
-- Required approvals.
+Examples of merge checks include:
-When all of the required merge checks are satisfied a merge request becomes mergeable.
+- Merge conflicts
+- Pipeline success
+- Threads resolution
+- [External status checks](../../user/project/merge_requests/status_checks.md)
+- Required approvals
+
+A merge request can be merged only when all of the required merge checks are satisfied.
## Approvals
@@ -58,8 +60,8 @@ Additionally, approval settings provide configuration options to define how thos
Examples of approval rules and settings include:
-1. [merge request approval rules](../../user/project/merge_requests/approvals/rules.md)
-1. [code owner approvals](../../user/project/code_owners.md)
-1. [security approvals](../../user/application_security/index.md#security-approvals-in-merge-requests)
-1. [prevent editing approval rules](../../user/project/merge_requests/approvals/settings.md#prevent-editing-approval-rules-in-merge-requests)
-1. [remove all approvals when commits are added](../../user/project/merge_requests/approvals/settings.md#remove-all-approvals-when-commits-are-added-to-the-source-branch)
+- [Merge request approval rules](../../user/project/merge_requests/approvals/rules.md)
+- [Code owner approvals](../../user/project/code_owners.md)
+- [Security approvals](../../user/application_security/index.md#security-approvals-in-merge-requests)
+- [Prevent editing approval rules](../../user/project/merge_requests/approvals/settings.md#prevent-editing-approval-rules-in-merge-requests)
+- [Remove all approvals when commits are added](../../user/project/merge_requests/approvals/settings.md#remove-all-approvals-when-commits-are-added-to-the-source-branch)
diff --git a/doc/development/merge_request_performance_guidelines.md b/doc/development/merge_request_performance_guidelines.md
index 7ff25705ae6..895fc6f92a4 100644
--- a/doc/development/merge_request_performance_guidelines.md
+++ b/doc/development/merge_request_performance_guidelines.md
@@ -394,7 +394,7 @@ query for every mention of `@alice`.
Caching data per transaction can be done using
[RequestStore](https://github.com/steveklabnik/request_store) (use
`Gitlab::SafeRequestStore` to avoid having to remember to check
-`RequestStore.active?`). Caching data in Redis can be done using
+`RequestStore.active?`). Caching data in Redis can be done using
[Rails' caching system](https://guides.rubyonrails.org/caching_with_rails.html).
## Pagination
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index 64d8b22f1b8..4e569579f37 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -1228,7 +1228,7 @@ If using a model in the migrations, you should first
[clear the column cache](https://api.rubyonrails.org/classes/ActiveRecord/ModelSchema/ClassMethods.html#method-i-reset_column_information)
using `reset_column_information`.
-If using a model that leverages single table inheritance (STI), there are
+If using a model that leverages single table inheritance (STI), there are
[special considerations](database/single_table_inheritance.md#in-migrations).
This avoids problems where a column that you are using was altered and cached
diff --git a/doc/development/newlines_styleguide.md b/doc/development/newlines_styleguide.md
index 57962129b2f..014affa3e04 100644
--- a/doc/development/newlines_styleguide.md
+++ b/doc/development/newlines_styleguide.md
@@ -1,108 +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/engineering/ux/technical-writing/#assignments
+redirect_to: 'backend/ruby_style_guide.md#newlines-style-guide'
+remove_date: '2022-12-15'
---
-# Newlines style guide
+This document was moved to [another location](backend/ruby_style_guide.md#newlines-style-guide).
-This style guide recommends best practices for newlines in Ruby code.
-
-## Rule: separate code with newlines only to group together related logic
-
-```ruby
-# bad
-def method
- issue = Issue.new
-
- issue.save
-
- render json: issue
-end
-```
-
-```ruby
-# good
-def method
- issue = Issue.new
- issue.save
-
- render json: issue
-end
-```
-
-## Rule: separate code and block with newlines
-
-### Newline before block
-
-```ruby
-# bad
-def method
- issue = Issue.new
- if issue.save
- render json: issue
- end
-end
-```
-
-```ruby
-# good
-def method
- issue = Issue.new
-
- if issue.save
- render json: issue
- end
-end
-```
-
-## Newline after block
-
-```ruby
-# bad
-def method
- if issue.save
- issue.send_email
- end
- render json: issue
-end
-```
-
-```ruby
-# good
-def method
- if issue.save
- issue.send_email
- end
-
- render json: issue
-end
-```
-
-### Exception: no need for newline when code block starts or ends right inside another code block
-
-```ruby
-# bad
-def method
-
- if issue
-
- if issue.valid?
- issue.save
- end
-
- end
-
-end
-```
-
-```ruby
-# good
-def method
- if issue
- if issue.valid?
- issue.save
- end
- end
-end
-```
+<!-- This redirect file can be deleted after 2022-12-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/packages/new_format_development.md b/doc/development/packages/new_format_development.md
index f7d02f9160b..73a2b2f1f81 100644
--- a/doc/development/packages/new_format_development.md
+++ b/doc/development/packages/new_format_development.md
@@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
This document guides you through adding support to GitLab for a new a [package management system](../../administration/packages/index.md).
-See the already supported formats in the [Packages & Registries documentation](../../user/packages/index.md)
+See the already supported formats in the [Packages and registries documentation](../../user/packages/index.md)
It is possible to add a new format with only backend changes.
This guide is superficial and does not cover the way the code should be written.
@@ -91,7 +91,7 @@ extended when possible to keep the common package logic grouped as much as possi
### Configuration
-GitLab has a `packages` section in its configuration file (`gitlab.rb`).
+GitLab has a `packages` section in its configuration file (`gitlab.rb` or `gitlab.yml`).
It applies to all package systems supported by GitLab. Usually you don't need
to add anything there.
diff --git a/doc/development/permissions.md b/doc/development/permissions.md
index 8e517b8577c..d348d659cad 100644
--- a/doc/development/permissions.md
+++ b/doc/development/permissions.md
@@ -147,15 +147,41 @@ into different features like Merge Requests and CI flow.
## Where should permissions be checked?
-By default, controllers, API endpoints, and GraphQL types/fields are responsible for authorization. See [Secure Coding Guidelines > Permissions](secure_coding_guidelines.md#permissions).
+We should typically apply defense-in-depth (implementing multiple checks at
+various layers) starting with low-level layers, such as finders and services,
+followed by high-level layers, such as GraphQL, public REST API, and controllers.
+
+See [Guidelines for reusing abstractions](reusing_abstractions.md).
+
+Protecting the same resources at many points means that if one layer of defense is compromised
+or missing, customer data is still protected by the additional layers.
+
+See the permissions section in the [Secure Coding Guidelines](secure_coding_guidelines.md#permissions).
### Considerations
-- Many actions are completely or partially extracted to services, finders, and other classes, so it is normal to do permission checks "downstream".
-- Often, authorization logic must be incorporated in DB queries to filter records.
+Services or finders are appropriate locations because:
+
+- Multiple endpoints share services or finders so downstream logic is more likely to be re-used.
+- Sometimes authorization logic must be incorporated in DB queries to filter records.
+- Permission checks at the display layer should be avoided except to provide better UX
+ and not as a security check. For example, showing and hiding non-data elements like buttons.
+
+The downsides to defense-in-depth are:
+
- `DeclarativePolicy` rules are relatively performant, but conditions may perform database calls.
-- Multiple permission checks across layers can be difficult to reason about, which is its own security risk. For example, duplicate authorization logic could diverge.
-- Should we apply defense-in-depth with permission checks? [Join the discussion](https://gitlab.com/gitlab-org/gitlab/-/issues/324135)
+- Higher maintenance costs.
+
+### Exceptions
+
+Developers can choose to do authorization in only a single area after weighing
+the risks and drawbacks for their specific case.
+
+Prefer domain logic (services or finders) as the source of truth when making exceptions.
+
+Logic, like backend worker logic, might not need authorization based on the current user.
+If the service or finder's constructor does not expect `current_user`, then it typically won't
+check permissions.
### Tips
diff --git a/doc/development/pipelines.md b/doc/development/pipelines.md
index d57e5bbeb26..648a2aac339 100644
--- a/doc/development/pipelines.md
+++ b/doc/development/pipelines.md
@@ -103,7 +103,7 @@ When you need to revert a merge request, to get accelerated feedback, you can ad
When this label is assigned, the following steps of the CI/CD pipeline are skipped:
-- The `package-and-qa` job.
+- The `e2e:package-and-test` job.
- The `rspec:undercoverage` job.
- The entire [Review Apps process](testing_guide/review_apps.md).
@@ -221,8 +221,8 @@ that includes `rspec-profile` in their name.
### Logging
-- Rails logging to `log/test.log` is disabled by default in CI
- [for performance reasons](https://jtway.co/speed-up-your-rails-test-suite-by-6-in-1-line-13fedb869ec4).
+- Rails logging to `log/test.log` is disabled by default in CI
+ [for performance reasons](https://jtway.co/speed-up-your-rails-test-suite-by-6-in-1-line-13fedb869ec4).
To override this setting, provide the
`RAILS_ENABLE_TEST_LOG` environment variable.
@@ -341,13 +341,20 @@ We also run our test suite against PG11 upon specific database library changes i
### Current versions testing
-| Where? | PostgreSQL version | Ruby version |
-| ------ | ------------------ | ------------ |
-| Merge requests | 12 (default version), 11 for DB library changes | 2.7 (default version) |
-| `master` branch commits | 12 (default version), 11 for DB library changes | 2.7 (default version) |
-| `maintenance` scheduled pipelines (every 2 hours at even hour) | 12 (default version), 11 for DB library changes | 2.7 (default version) |
-| `maintenance` scheduled pipelines (every 2 hours at odd hour) | 12 (default version), 11 for DB library changes | 3.0 (set in the schedule variables) |
-| `nightly` scheduled pipelines | 12 (default version), 11, 13 | 2.7 (default version) |
+| Where? | PostgreSQL version | Ruby version |
+|------------------------------------------------------------------------------------------------|-------------------------------------------------|--------------|
+| Merge requests | 12 (default version), 11 for DB library changes | 2.7 (default version) |
+| `master` branch commits | 12 (default version), 11 for DB library changes | 2.7 (default version) |
+| `maintenance` scheduled pipelines for the `master` branch (every even-numbered hour) | 12 (default version), 11 for DB library changes | 2.7 (default version) |
+| `maintenance` scheduled pipelines for the `ruby3` branch (every odd-numbered hour), see below. | 12 (default version), 11 for DB library changes | 3.0 (coded in the branch) |
+| `nightly` scheduled pipelines for the `master` branch | 12 (default version), 11, 13 | 2.7 (default version) |
+
+The pipeline configuration for the scheduled pipeline testing Ruby 3 is
+stored in the `ruby3-sync` branch. The pipeline updates the `ruby3` branch
+with latest `master`, and then it triggers a regular branch pipeline for
+`ruby3`. Any changes in `ruby3` are only for running the pipeline. It should
+never be merged back to `master`. Any other Ruby 3 changes should go into
+`master` directly, which should be compatible with Ruby 2.7.
### Long-term plan
@@ -489,7 +496,7 @@ graph RL;
class 2_3-1 criticalPath;
2_3-1 --> 1-5
- 2_4-1["package-and-qa (102 minutes)"];
+ 2_4-1["e2e:package-and-test (102 minutes)"];
class 2_4-1 criticalPath;
click 2_4-1 "https://app.periscopedata.com/app/gitlab/652085/Engineering-Productivity---Pipeline-Build-Durations?widget=6914305&udv=0"
2_4-1 --> 1-2 & 2_3-1 & 1-15;
diff --git a/doc/development/policies.md b/doc/development/policies.md
index f0c9d0ec5f9..ddd6b8e9bce 100644
--- a/doc/development/policies.md
+++ b/doc/development/policies.md
@@ -10,6 +10,8 @@ The DeclarativePolicy framework is designed to assist in performance of policy c
The policy used is based on the subject's class name - so `Ability.allowed?(user, :some_ability, project)` creates a `ProjectPolicy` and check permissions on that.
+The Ruby gem source is available in the [declarative-policy](https://gitlab.com/gitlab-org/ruby/gems/declarative-policy) GitLab project.
+
## Managing Permission Rules
Permissions are broken into two parts: `conditions` and `rules`. Conditions are boolean expressions that can access the database and the environment, while rules are statically configured combinations of expressions and other rules that enable or prevent certain abilities. For an ability to be allowed, it must be enabled by at least one rule, and not prevented by any.
diff --git a/doc/development/rails_update.md b/doc/development/rails_update.md
index 9907a78421f..bda21860eae 100644
--- a/doc/development/rails_update.md
+++ b/doc/development/rails_update.md
@@ -27,7 +27,7 @@ We strive to run GitLab using the latest Rails releases to benefit from performa
1. Run `yarn patch-package @rails/ujs` after updating this to ensure our local patch file version matches.
1. Create an MR with the `pipeline:run-all-rspec` label and see if pipeline breaks.
1. To resolve and debug spec failures use `git bisect` against the rails repository. See the [debugging section](#git-bisect-against-rails) below.
-1. Include links to the Gem diffs between the two versions in the merge request description. For example, this is the gem diff for
+1. Include links to the Gem diffs between the two versions in the merge request description. For example, this is the gem diff for
[`activesupport` 6.1.3.2 to 6.1.4.1](https://my.diffend.io/gems/activerecord/6.1.3.2/6.1.4.1).
### Prepare an MR for Gitaly
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index c13f1195df3..f300904fc19 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -188,6 +188,8 @@ Alternatively you can use the following on each spec run,
bundle exec spring rspec some_spec.rb
```
+## RuboCop tasks
+
## Generate initial RuboCop TODO list
One way to generate the initial list is to run the Rake task `rubocop:todo:generate`:
@@ -209,6 +211,18 @@ Some shells require brackets to be escaped or quoted.
See [Resolving RuboCop exceptions](contributing/style_guides.md#resolving-rubocop-exceptions)
on how to proceed from here.
+### Run RuboCop in graceful mode
+
+You can run RuboCop in "graceful mode". This means all enabled cop rules are
+silenced which have "grace period" activated (via `Details: grace period`).
+
+Run:
+
+```shell
+bundle exec rake 'rubocop:check:graceful'
+bundle exec rake 'rubocop:check:graceful[Gitlab/NamespacedClass]'
+```
+
## Compile Frontend Assets
You shouldn't ever need to compile frontend assets manually in development, but
diff --git a/doc/development/real_time.md b/doc/development/real_time.md
index 21f3ee1f3b2..f113d4dd3b7 100644
--- a/doc/development/real_time.md
+++ b/doc/development/real_time.md
@@ -60,7 +60,7 @@ downstream services.
To mitigate this, ensure that the code establishing the new WebSocket connection
is feature flagged and defaulted to `off`. A careful, percentage-based roll-out
-of the feature flag ensures that effects can be observed on the
+of the feature flag ensures that effects can be observed on the
[WebSocket dashboard](https://dashboards.gitlab.net/d/websockets-main/websockets-overview?orgId=1)
1. Create a
diff --git a/doc/development/redis/new_redis_instance.md b/doc/development/redis/new_redis_instance.md
index efaf1e5a6d0..24885b40eb9 100644
--- a/doc/development/redis/new_redis_instance.md
+++ b/doc/development/redis/new_redis_instance.md
@@ -265,7 +265,7 @@ instances to cope without this functional partition.
If we decide to keep the migration code:
- We should document the migration steps.
-- If we used a feature flag, we should ensure it's an
+- If we used a feature flag, we should ensure it's an
[ops type feature flag](../feature_flags/index.md#ops-type), as these are long-lived flags.
Otherwise, we can remove the flags and conclude the project.
diff --git a/doc/development/reusing_abstractions.md b/doc/development/reusing_abstractions.md
index ef4e8b0310f..826782d7036 100644
--- a/doc/development/reusing_abstractions.md
+++ b/doc/development/reusing_abstractions.md
@@ -206,6 +206,31 @@ response = ServiceResponse.success(payload: { issue: issue })
response.payload[:issue] # => issue
```
+Error responses can also specify the failure `reason` which can be used by the caller
+to understand the nature of the failure.
+The caller, if an HTTP endpoint, could translate the reason symbol into an HTTP status code:
+
+```ruby
+response = ServiceResponse.error(
+ message: 'Job is in a state that cannot be retried',
+ reason: :job_not_retrieable)
+
+if response.success?
+ head :ok
+if response.reason == :job_not_retriable
+ head :unprocessable_entity
+else
+ head :bad_request
+end
+```
+
+For common failures such as resource `:not_found` or operation `:forbidden`, we could
+leverage the Rails [HTTP status symbols](http://www.railsstatuscodes.com/) as long as
+they are sufficiently specific for the domain logic involved.
+For other failures use domain-specific reasons whenever possible.
+
+For example: `:job_not_retriable`, `:duplicate_package`, `:merge_request_not_mergeable`.
+
### Finders
Everything in `app/finders`, typically used for retrieving data from a database.
diff --git a/doc/development/routing.md b/doc/development/routing.md
index 3d5857b4237..54a531730f9 100644
--- a/doc/development/routing.md
+++ b/doc/development/routing.md
@@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Routing
-The GitLab backend is written primarily with Rails so it uses
+The GitLab backend is written primarily with Rails so it uses
[Rails routing](https://guides.rubyonrails.org/routing.html). Beside Rails best
practices, there are few rules unique to the GitLab application. To
support subgroups, GitLab project and group routes use the wildcard
diff --git a/doc/development/ruby3_gotchas.md b/doc/development/ruby3_gotchas.md
index dbe6fa13eee..db328b0b1a5 100644
--- a/doc/development/ruby3_gotchas.md
+++ b/doc/development/ruby3_gotchas.md
@@ -163,3 +163,40 @@ For Ruby 3 compliance, this should be changed to one of the following invocation
- `f(**{k: v})`
- `f(k: v)`
+
+## RSpec `with` argument matcher fails for shorthand Hash syntax
+
+Because keyword arguments ("kwargs") are a first-class concept in Ruby 3, keyword arguments are not
+converted into internal `Hash` instances anymore. This leads to RSpec method argument matchers failing
+when the receiver takes a positional options hash instead of kwargs:
+
+```ruby
+def m(options={}); end
+```
+
+```ruby
+expect(subject).to receive(:m).with(a: 42)
+```
+
+In Ruby 3 this expectations fails with the following error:
+
+```plaintext
+ Failure/Error:
+
+ #<subject> received :m with unexpected arguments
+ expected: ({:a=>42})
+ got: ({:a=>42})
+```
+
+This happens because RSpec uses a kwargs argument matcher here, but the method takes a hash.
+It works in Ruby 2, because `a: 42` is converted to a hash first and RSpec will use a hash argument matcher.
+
+A workaround is to not use the shorthand syntax and pass an actual `Hash` instead whenever we know a method
+to take an options hash:
+
+```ruby
+# Note the braces around the key-value pair.
+expect(subject).to receive(:m).with({ a: 42 })
+```
+
+For more information, see [the official issue report for RSpec](https://github.com/rspec/rspec-mocks/issues/1460).
diff --git a/doc/development/scalability.md b/doc/development/scalability.md
index b7ee0ca1167..66f436bd391 100644
--- a/doc/development/scalability.md
+++ b/doc/development/scalability.md
@@ -35,7 +35,7 @@ The application has a tight coupling to the database schema. When the
application starts, Rails queries the database schema, caching the tables and
column types for the data requested. Because of this schema cache, dropping a
column or table while the application is running can produce 500 errors to the
-user. This is why we have a
+user. This is why we have a
[process for dropping columns and other no-downtime changes](database/avoiding_downtime_in_migrations.md).
#### Multi-tenancy
@@ -61,10 +61,10 @@ There are two ways to deal with this:
- Sharding. Distribute data across multiple databases.
Partitioning is a built-in PostgreSQL feature and requires minimal changes
-in the application. However, it
+in the application. However, it
[requires PostgreSQL 11](https://www.2ndquadrant.com/en/blog/partitioning-evolution-postgresql-11/).
-For example, a natural way to partition is to
+For example, a natural way to partition is to
[partition tables by dates](https://gitlab.com/groups/gitlab-org/-/epics/2023). For example,
the `events` and `audit_events` table are natural candidates for this
kind of partitioning.
@@ -77,9 +77,9 @@ to abstract data access into API calls that abstract the database from
the application, but this is a significant amount of work.
There are solutions that may help abstract the sharding to some extent
-from the application. For example, we want to look at
+from the application. For example, we want to look at
[Citus Data](https://www.citusdata.com/product/community) closely. Citus Data
-provides a Rails plugin that adds a
+provides a Rails plugin that adds a
[tenant ID to ActiveRecord models](https://www.citusdata.com/blog/2017/01/05/easily-scale-out-multi-tenant-apps/).
Sharding can also be done based on feature verticals. This is the
@@ -97,11 +97,11 @@ systems.
#### Database size
-A recent
+A recent
[database checkup shows a breakdown of the table sizes on GitLab.com](https://gitlab.com/gitlab-com/gl-infra/reliability/-/issues/8022#master-1022016101-8).
Since `merge_request_diff_files` contains over 1 TB of data, we want to
-reduce/eliminate this table first. GitLab has support for
-[storing diffs in object storage](../administration/merge_request_diffs.md), which we
+reduce/eliminate this table first. GitLab has support for
+[storing diffs in object storage](../administration/merge_request_diffs.md), which we
[want to do on GitLab.com](https://gitlab.com/gitlab-com/gl-infra/reliability/-/issues/7356).
#### High availability
@@ -149,7 +149,7 @@ limitation:
- Use a multi-threaded connection pooler (for example,
[Odyssey](https://gitlab.com/gitlab-com/gl-infra/reliability/-/issues/7776).
-On some Linux systems, it's possible to run
+On some Linux systems, it's possible to run
[multiple PgBouncer instances on the same port](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/4796).
On GitLab.com, we run multiple PgBouncer instances on different ports to
diff --git a/doc/development/sec/index.md b/doc/development/sec/index.md
new file mode 100644
index 00000000000..06c20cee0bb
--- /dev/null
+++ b/doc/development/sec/index.md
@@ -0,0 +1,69 @@
+---
+stage: Secure
+group: Static Analysis
+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
+type: index, concepts, howto
+---
+
+# Sec section development **(FREE)**
+
+The Sec section is responsible for GitLab application security features, the "Sec" part of
+DevSecOps. Development guides that are specific to the Sec section are listed here.
+
+See [Terminology](../../user/application_security/terminology) for an overview of our shared terminology.
+
+## Architecture
+
+- [Overview](#overview)
+- [Scanning](#scanning)
+- [Processing, visualization, and management](#processing-visualization-and-management)
+- [Severity Levels](../../user/application_security/vulnerabilities/severities.md)
+
+## Overview
+
+The architecture supporting the Secure features is split into two main parts:
+
+- Scanning
+- Processing, visualization, and management
+
+```mermaid
+flowchart LR
+ subgraph G1[Scanning]
+ Scanner
+ Analyzer
+ CI[CI Jobs]
+ end
+ subgraph G2[Processing, visualization, and management]
+ Parsers
+ Database
+ Views
+ Interactions
+ end
+ G1 --Report Artifact--> G2
+```
+
+### Scanning
+
+The scanning part is responsible for finding vulnerabilities in given resources, and exporting results.
+The scans are executed in CI/CD jobs via several small projects called [Analyzers](../../user/application_security/terminology/index.md#analyzer), which can be found in our [Analyzers sub-group](https://gitlab.com/gitlab-org/security-products/analyzers).
+The Analyzers are wrappers around security tools called [Scanners](../../user/application_security/terminology/index.md#scanner), developed internally or externally, to integrate them into GitLab.
+The Analyzers are mainly written in Go.
+
+Some 3rd party integrators also make additional Scanners available by following our [integration documentation](../integrations/secure.md), which leverages the same architecture.
+
+The results of the scans are exported as JSON reports that must comply with the [Secure report format](../../user/application_security/terminology/index.md#secure-report-format) and are uploaded as [CI/CD Job Report artifacts](../../ci/pipelines/job_artifacts.md) to make them available for processing after the pipelines completes.
+
+### Processing, visualization, and management
+
+After the data is available as a Report Artifact it can be processed by the GitLab Rails application to enable our security features, including:
+
+- [Security Dashboards](../../user/application_security/security_dashboard/index.md), Merge Request widget, Pipeline view, and so on.
+- [Interactions with vulnerabilities](../../user/application_security/index.md#interact-with-findings-and-vulnerabilities).
+- [Approval rules](../../user/application_security/index.md#security-approvals-in-merge-requests).
+
+Depending on the context, the security reports may be stored either in the database or stay as Report Artifacts for on-demand access.
+
+## CI/CD template development
+
+While CI/CD templates are the responsibiility of the Verify section, many are critical to the Sec Section's feature usage.
+If you are working with CI/CD templates, please read the [development guide for GitLab CI/CD templates](../cicd/templates.md).
diff --git a/doc/development/secure_coding_guidelines.md b/doc/development/secure_coding_guidelines.md
index 8053b4285e6..4c2f3118366 100644
--- a/doc/development/secure_coding_guidelines.md
+++ b/doc/development/secure_coding_guidelines.md
@@ -81,7 +81,7 @@ text = "foo\nbar"
p text.match /^bar$/
```
-The output of this example is `#<MatchData "bar">`, as Ruby treats the input `text` line by line. In order to match the whole __string__ the Regex anchors `\A` and `\z` should be used.
+The output of this example is `#<MatchData "bar">`, as Ruby treats the input `text` line by line. To match the whole **string**, the Regex anchors `\A` and `\z` should be used.
#### Impact
diff --git a/doc/development/service_ping/implement.md b/doc/development/service_ping/implement.md
index 0ebc58dd669..5448bbb4293 100644
--- a/doc/development/service_ping/implement.md
+++ b/doc/development/service_ping/implement.md
@@ -127,7 +127,7 @@ Examples using `usage_data.rb` have been [deprecated](usage_data.md). We recomme
#### Grouping and batch operations
-The `count`, `distinct_count`, `sum`, and `average` batch counters can accept an `ActiveRecord::Relation`
+The `count`, `distinct_count` and `sum` batch counters can accept an `ActiveRecord::Relation`
object, which groups by a specified column. With a grouped relation, the methods do batch counting,
handle errors, and returns a hash table of key-value pairs.
@@ -142,9 +142,6 @@ distinct_count(Project.group(:visibility_level), :creator_id)
sum(Issue.group(:state_id), :weight))
# returns => {1=>3542, 2=>6820}
-
-average(Issue.group(:state_id), :weight))
-# returns => {1=>3.5, 2=>2.5}
```
#### Add operation
@@ -275,7 +272,7 @@ Events are handled by counter classes in the `Gitlab::UsageDataCounters` namespa
1. Listed in [`Gitlab::UsageDataCounters::COUNTERS`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/usage_data_counters.rb#L5) to be then included in `Gitlab::UsageData`.
-1. Specified in the metric definition using the `RedisMetric` instrumentation class as a `counter_class` option to be picked up using the [metric instrumentation](metrics_instrumentation.md) framework. Refer to the [Redis metrics](metrics_instrumentation.md#redis-metrics) documentation for an example implementation.
+1. Specified in the metric definition using the `RedisMetric` instrumentation class by their `prefix` option to be picked up using the [metric instrumentation](metrics_instrumentation.md) framework. Refer to the [Redis metrics](metrics_instrumentation.md#redis-metrics) documentation for an example implementation.
Inheriting classes are expected to override `KNOWN_EVENTS` and `PREFIX` constants to build event names and associated metrics. For example, for prefix `issues` and events array `%w[create, update, delete]`, three metrics will be added to the Service Ping payload: `counts.issues_create`, `counts.issues_update` and `counts.issues_delete`.
@@ -385,7 +382,7 @@ Implemented using Redis methods [PFADD](https://redis.io/commands/pfadd/) and [P
- `controller_actions`: the controller actions to track.
- `name`: the event name.
- `conditions`: optional custom conditions. Uses the same format as Rails callbacks.
- - `destinations`: optional list of destinations. Currently supports `:redis_hll` and `:snowplow`. Default: [:redis_hll].
+ - `destinations`: optional list of destinations. Currently supports `:redis_hll` and `:snowplow`. Default: `:redis_hll`.
- `&block`: optional block that computes and returns the `custom_id` that we want to track. This overrides the `visitor_id`.
Example:
@@ -623,7 +620,7 @@ alt_usage_data(999)
### Add counters to build new metrics
When adding the results of two counters, use the `add` Service Data method that
-handles fallback values and exceptions. It also generates a valid [SQL export](index.md#export-service-ping-sql-queries-and-definitions).
+handles fallback values and exceptions. It also generates a valid [SQL export](index.md#export-service-ping-data).
Example:
@@ -773,7 +770,7 @@ To set up Service Ping locally, you must:
1. Using the `gitlab` Rails console, manually trigger Service Ping:
```ruby
- ServicePing::SubmitService.new.execute
+ GitlabServicePingWorker.new.perform('triggered_from_cron' => false)
```
1. Use the `versions` Rails console to check the Service Ping was successfully received,
@@ -809,7 +806,7 @@ This is the recommended approach to test Prometheus-based Service Ping.
To verify your change, build a new Omnibus image from your code branch using CI/CD, download the image,
and run a local container instance:
-1. From your merge request, select the `qa` stage, then trigger the `package-and-qa` job. This job triggers an Omnibus
+1. From your merge request, select the `qa` stage, then trigger the `e2e:package-and-test` job. This job triggers an Omnibus
build in a [downstream pipeline of the `omnibus-gitlab-mirror` project](https://gitlab.com/gitlab-org/build/omnibus-gitlab-mirror/-/pipelines).
1. In the downstream pipeline, wait for the `gitlab-docker` job to finish.
1. Open the job logs and locate the full container name including the version. It takes the following form: `registry.gitlab.com/gitlab-org/build/omnibus-gitlab-mirror/gitlab-ee:<VERSION>`.
diff --git a/doc/development/service_ping/index.md b/doc/development/service_ping/index.md
index 4481fe33bda..f252eb967aa 100644
--- a/doc/development/service_ping/index.md
+++ b/doc/development/service_ping/index.md
@@ -374,9 +374,9 @@ Possible values are "Amazon Aurora PostgreSQL", "PostgreSQL on Amazon RDS", "Clo
In GitLab 13.5, `pg_system_id` was added to send the [PostgreSQL system identifier](https://www.2ndquadrant.com/en/blog/support-for-postgresqls-system-identifier-in-barman/).
-## Export Service Ping SQL queries and definitions
+## Export Service Ping data
-Two Rake tasks exist to export Service Ping definitions.
+Rake tasks exist to export Service Ping data in different formats.
- The Rake tasks export the raw SQL queries for `count`, `distinct_count`, `sum`.
- The Rake tasks export the Redis counter class or the line of the Redis block for `redis_usage_data`.
@@ -385,12 +385,15 @@ Two Rake tasks exist to export Service Ping definitions.
In the home directory of your local GitLab installation run the following Rake tasks for the YAML and JSON versions respectively:
```shell
-# for YAML export
+# for YAML export of SQL queries
bin/rake gitlab:usage_data:dump_sql_in_yaml
-# for JSON export
+# for JSON export of SQL queries
bin/rake gitlab:usage_data:dump_sql_in_json
+# for JSON export of Non SQL data
+bin/rake gitlab:usage_data:dump_non_sql_in_json
+
# You may pipe the output into a file
bin/rake gitlab:usage_data:dump_sql_in_yaml > ~/Desktop/usage-metrics-2020-09-02.yaml
```
@@ -405,7 +408,7 @@ To generate Service Ping, use [Teleport](https://goteleport.com/docs/) or a deta
1. Request temporary [access](https://gitlab.com/gitlab-com/runbooks/-/blob/master/docs/Teleport/Connect_to_Rails_Console_via_Teleport.md#how-to-use-teleport-to-connect-to-rails-console) to the required environment.
1. After your approval is issued, [access the Rails console](https://gitlab.com/gitlab-com/runbooks/-/blob/master/docs/Teleport/Connect_to_Rails_Console_via_Teleport.md#access-approval).
-1. Run `ServicePing::SubmitService.new.execute`.
+1. Run `GitlabServicePingWorker.new.perform('triggered_from_cron' => false)`.
#### Trigger Service Ping with a detached screen session
@@ -430,7 +433,7 @@ To generate Service Ping, use [Teleport](https://goteleport.com/docs/) or a deta
1. Run:
```shell
- ServicePing::SubmitService.new.execute
+ GitlabServicePingWorker.new.perform('triggered_from_cron' => false)
```
1. To detach from screen, press `ctrl + A`, `ctrl + D`.
@@ -490,7 +493,7 @@ To skip database write operations, DevOps report creation, and storage of usage
```shell
skip_db_write:
-ServicePing::SubmitService.new(skip_db_write: true).execute
+GitlabServicePingWorker.new.perform('triggered_from_cron' => false, 'skip_db_write' => true)
```
## Monitoring
diff --git a/doc/development/service_ping/metrics_instrumentation.md b/doc/development/service_ping/metrics_instrumentation.md
index 9dc37386111..debb3a68c04 100644
--- a/doc/development/service_ping/metrics_instrumentation.md
+++ b/doc/development/service_ping/metrics_instrumentation.md
@@ -154,22 +154,24 @@ end
You can use Redis metrics to track events not kept in the database, for example, a count of how many times the search bar has been used.
-[Example of a merge request that adds a `Redis` metric](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66582).
+[Example of a merge request that adds a `Redis` metric](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97009).
+
+Please note that `RedisMetric` class can only be used as the `instrumentation_class` for Redis metrics with simple counters classes (classes that only inherit `BaseCounter` and set `PREFIX` and `KNOWN_EVENTS` constants). In case the counter class has additional logic included in it, a new `instrumentation_class`, inheriting from `RedisMetric`, needs to be created. This new class needs to include the additional logic from the counter class.
Count unique values for `source_code_pushes` event.
Required options:
- `event`: the event name.
-- `counter_class`: one of the counter classes from the `Gitlab::UsageDataCounters` namespace; it should implement `read` method or inherit it from `BaseCounter`.
+- `prefix`: the value of the `PREFIX` constant used in the counter classes from the `Gitlab::UsageDataCounters` namespace.
```yaml
time_frame: all
data_source: redis
-instrumentation_class: 'RedisMetric'
+instrumentation_class: RedisMetric
options:
event: pushes
- counter_class: SourceCodeCounter
+ prefix: source_code
```
### Availability-restrained Redis metrics
@@ -197,14 +199,14 @@ You must also use the class's name in the YAML setup.
```yaml
time_frame: all
data_source: redis
-instrumentation_class: 'MergeUsageCountRedisMetric'
+instrumentation_class: MergeUsageCountRedisMetric
options:
event: pushes
- counter_class: SourceCodeCounter
+ prefix: source_code
```
## Redis HyperLogLog metrics
-
+
You can use Redis HyperLogLog metrics to track events not kept in the database and incremented for unique values such as unique users,
for example, a count of how many different users used the search bar.
@@ -215,7 +217,7 @@ Count unique values for `i_quickactions_approve` event.
```yaml
time_frame: 28d
data_source: redis_hll
-instrumentation_class: 'RedisHLLMetric'
+instrumentation_class: RedisHLLMetric
options:
events:
- i_quickactions_approve
@@ -246,7 +248,7 @@ You must also use the class's name in the YAML setup.
```yaml
time_frame: 28d
data_source: redis_hll
-instrumentation_class: 'MergeUsageCountRedisHLLMetric'
+instrumentation_class: MergeUsageCountRedisHLLMetric
options:
events:
- i_quickactions_approve
@@ -286,7 +288,7 @@ You must also include the instrumentation class name in the YAML setup.
```yaml
time_frame: 28d
-instrumentation_class: 'IssuesBoardsCountMetric'
+instrumentation_class: IssuesBoardsCountMetric
```
## Generic metrics
@@ -337,13 +339,13 @@ To create a stub instrumentation for a Service Ping metric, you can use a dedica
The generator takes the class name as an argument and the following options:
- `--type=TYPE` Required. Indicates the metric type. It must be one of: `database`, `generic`, `redis`, `numbers`.
-- `--operation` Required for `database` & `numebers` type.
+- `--operation` Required for `database` & `numbers` type.
- For `database` it must be one of: `count`, `distinct_count`, `estimate_batch_distinct_count`, `sum`, `average`.
- For `numbers` it must be: `add`.
- `--ee` Indicates if the metric is for EE.
```shell
-rails generate gitlab:usage_metric CountIssues --type database
+rails generate gitlab:usage_metric CountIssues --type database --operation distinct_count
create lib/gitlab/usage/metrics/instrumentations/count_issues_metric.rb
create spec/lib/gitlab/usage/metrics/instrumentations/count_issues_metric_spec.rb
```
diff --git a/doc/development/service_ping/troubleshooting.md b/doc/development/service_ping/troubleshooting.md
index 29ab334f867..4431d5df3ff 100644
--- a/doc/development/service_ping/troubleshooting.md
+++ b/doc/development/service_ping/troubleshooting.md
@@ -4,7 +4,7 @@ group: Product Intelligence
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
---
-# Troubleshooting
+# Troubleshooting Service Ping
## Service Ping Payload drop
@@ -58,7 +58,7 @@ checking the configuration file of your GitLab instance:
- Using the Admin Area:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**.
1. Expand **Usage Statistics**.
1. Are you able to check or uncheck the checkbox to disable Service Ping?
@@ -115,7 +115,7 @@ To work around this bug, you have two options:
sudo gitlab-ctl reconfigure
```
- 1. In GitLab, on the top bar, select **Menu > Admin**.
+ 1. In GitLab, on the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**.
1. Expand **Usage Statistics**.
1. Clear the **Enable Service Ping** checkbox.
diff --git a/doc/development/shell_scripting_guide/index.md b/doc/development/shell_scripting_guide/index.md
index 3d58fabad72..0591d2c64d0 100644
--- a/doc/development/shell_scripting_guide/index.md
+++ b/doc/development/shell_scripting_guide/index.md
@@ -38,7 +38,7 @@ in a particular case.
According to the [GitLab installation requirements](../../install/requirements.md),
this guide covers only those shells that are used by
-[supported Linux distributions](../../install/requirements.md#supported-linux-distributions),
+[supported Linux distributions](../../administration/package_information/supported_os.md#supported-operating-systems),
that is:
- [POSIX Shell](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html)
diff --git a/doc/development/sidekiq/compatibility_across_updates.md b/doc/development/sidekiq/compatibility_across_updates.md
index 1d369b5a970..ac34d099202 100644
--- a/doc/development/sidekiq/compatibility_across_updates.md
+++ b/doc/development/sidekiq/compatibility_across_updates.md
@@ -18,13 +18,13 @@ several possible situations:
## Adding new workers
-On GitLab.com, we
-[do not currently have a Sidekiq deployment in the canary stage](https://gitlab.com/gitlab-org/gitlab/-/issues/19239).
+On GitLab.com, we
+[do not currently have a Sidekiq deployment in the canary stage](https://gitlab.com/gitlab-org/gitlab/-/issues/19239).
This means that a new worker than can be scheduled from an HTTP endpoint may
be scheduled from canary but not run on Sidekiq until the full
production deployment is complete. This can be several hours later than
scheduling the job. For some workers, this will not be a problem. For
-others - particularly [latency-sensitive jobs](worker_attributes.md#latency-sensitive-jobs) -
+others - particularly [latency-sensitive jobs](worker_attributes.md#latency-sensitive-jobs) -
this will result in a poor user experience.
This only applies to new worker classes when they are first introduced.
diff --git a/doc/development/sidekiq/idempotent_jobs.md b/doc/development/sidekiq/idempotent_jobs.md
index 5d1ebce763e..da36cdc72aa 100644
--- a/doc/development/sidekiq/idempotent_jobs.md
+++ b/doc/development/sidekiq/idempotent_jobs.md
@@ -78,7 +78,7 @@ GitLab supports two deduplication strategies:
- `until_executing`, which is the default strategy
- `until_executed`
-More [deduplication strategies have been suggested](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/195).
+More [deduplication strategies have been suggested](https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/195).
If you are implementing a worker that could benefit from a different
strategy, please comment in the issue.
diff --git a/doc/development/snowplow/index.md b/doc/development/snowplow/index.md
index 24cd9093267..42a968b9df0 100644
--- a/doc/development/snowplow/index.md
+++ b/doc/development/snowplow/index.md
@@ -31,7 +31,7 @@ Snowplow tracking is enabled on GitLab.com, and we use it for most of our tracki
To enable Snowplow tracking on a self-managed instance:
-1. On the top bar, select **Menu > Admin**, then select **Settings > General**.
+1. On the top bar, select **Main menu > Admin**, then select **Settings > General**.
Alternatively, go to `admin/application_settings/general` in your browser.
1. Expand **Snowplow**.
diff --git a/doc/development/snowplow/troubleshooting.md b/doc/development/snowplow/troubleshooting.md
index 42a433e6a94..3ad4c6c9549 100644
--- a/doc/development/snowplow/troubleshooting.md
+++ b/doc/development/snowplow/troubleshooting.md
@@ -4,7 +4,13 @@ group: Product Intelligence
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
---
-# Troubleshooting
+# Troubleshooting Snowplow
+
+## Monitoring
+
+This page covers dashboards and alerts coming from a number of internal tools.
+
+For a brief video overview of the tools used to monitor Snowplow usage, please check out [this internal video](https://www.youtube.com/watch?v=NxPS0aKa_oU) (you must be logged into GitLab Unfiltered to view).
## Good events drop
diff --git a/doc/development/sql.md b/doc/development/sql.md
index 7101bf7fb4b..029874011c4 100644
--- a/doc/development/sql.md
+++ b/doc/development/sql.md
@@ -80,7 +80,7 @@ USING GIN(column_name gin_trgm_ops);
```
The key here is the `GIN(column_name gin_trgm_ops)` part. This creates a
-[GIN index](https://www.postgresql.org/docs/current/gin.html)
+[GIN index](https://www.postgresql.org/docs/current/gin.html)
with the operator class set to `gin_trgm_ops`. These indexes
_can_ be used by `ILIKE` / `LIKE` and can lead to greatly improved performance.
One downside of these indexes is that they can easily get quite large (depending
@@ -397,7 +397,7 @@ default.
While `WHERE IN` and `WHERE EXISTS` can be used to produce the same data it is
recommended to use `WHERE EXISTS` whenever possible. While in many cases
-PostgreSQL can optimise `WHERE IN` quite well there are also many cases where
+PostgreSQL can optimize `WHERE IN` quite well there are also many cases where
`WHERE EXISTS` performs (much) better.
In Rails you have to use this by creating SQL fragments:
diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md
index 79a72981e3f..221d6b89b20 100644
--- a/doc/development/testing_guide/best_practices.md
+++ b/doc/development/testing_guide/best_practices.md
@@ -83,6 +83,31 @@ You can silence deprecation warnings by setting the environment variable
SILENCE_DEPRECATIONS=1 bin/rspec spec/models/project_spec.rb
```
+### Test order
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93137) in GitLab 15.4.
+
+All new spec files are run in [random order](https://gitlab.com/gitlab-org/gitlab/-/issues/337399)
+to surface flaky tests that are dependent on test order.
+
+When randomized:
+
+- The string `# order random` is added below the example group description.
+- The used seed is shown in the spec output below the test suite summary. For example, `Randomized with seed 27443`.
+
+For a list of spec files which are still run in defined order, see [`rspec_order_todo.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/support/rspec_order_todo.yml).
+
+To make spec files run in random order, check their order dependency with:
+
+```shell
+scripts/rspec_check_order_dependence spec/models/project_spec.rb
+```
+
+If the specs pass the check the script removes them from
+[`rspec_order_todo.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/spec/support/rspec_order_todo.yml) automatically.
+
+If the specs fail the check they must be fixed before than can run in random order.
+
### Test speed
GitLab has a massive test suite that, without [parallelization](../pipelines.md#test-suite-parallelization), can take hours
@@ -232,7 +257,7 @@ Here is an example of when `let_it_be` cannot be used, but `let_it_be_with_reloa
```ruby
let_it_be(:user) { create(:user) }
-let_it_be_with_reload(:project) { create(:project) } # The test will fail if `let_it_be` is used
+let_it_be_with_reload(:project) { create(:project) } # The test will fail if `let_it_be` is used
context 'with a developer' do
before do
@@ -422,7 +447,7 @@ Use the coverage reports to ensure your tests cover 100% of your code.
### System / Feature tests
NOTE:
-Before writing a new system test,
+Before writing a new system test,
[please consider **not** writing one](testing_levels.md#consider-not-writing-a-system-test)!
- Feature specs should be named `ROLE_ACTION_spec.rb`, such as
@@ -711,6 +736,7 @@ should either:
- Add `require_dependency 're2'` to files in your library that need `re2` gem,
to make this requirement explicit. This approach is preferred.
- Add it to the spec itself.
+- Use `rubocop_spec_helper` for RuboCop related specs.
It takes around one second to load tests that are using `fast_spec_helper`
instead of 30+ seconds in case of a regular `spec_helper`.
@@ -909,7 +935,7 @@ By default, Sidekiq jobs are enqueued into a jobs array and aren't processed.
If a test queues Sidekiq jobs and need them to be processed, the
`:sidekiq_inline` trait can be used.
-The `:sidekiq_might_not_need_inline` trait was added when
+The `:sidekiq_might_not_need_inline` trait was added when
[Sidekiq inline mode was changed to fake mode](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/15479)
to all the tests that needed Sidekiq to actually process jobs. Tests with
this trait should be either fixed to not rely on Sidekiq processing jobs, or their
@@ -1048,7 +1074,7 @@ Most tests for Elasticsearch logic relate to:
There are some exceptions, such as checking for structural changes rather than individual records in an index.
-The `:elastic_with_delete_by_query` trait was added to reduce run time for pipelines by creating and deleting indices
+The `:elastic_delete_by_query` trait was added to reduce run time for pipelines by creating and deleting indices
at the start and end of each context only. The [Elasticsearch DeleteByQuery API](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html)
is used to delete data in all indices in between examples to ensure a clean index.
@@ -1081,12 +1107,11 @@ Snowplow performs **runtime type checks** by using the [contracts gem](https://r
Because Snowplow is **by default disabled in tests and development**, it can be hard to
**catch exceptions** when mocking `Gitlab::Tracking`.
-To catch runtime errors due to type checks, you can enable Snowplow in tests. Mark the spec with
-`:snowplow` and use the `expect_snowplow_event` helper, which checks for
+To catch runtime errors due to type checks you can use `expect_snowplow_event`, which checks for
calls to `Gitlab::Tracking#event`.
```ruby
-describe '#show', :snowplow do
+describe '#show' do
it 'tracks snowplow events' do
get :show
@@ -1111,7 +1136,7 @@ end
When you want to ensure that no event got called, you can use `expect_no_snowplow_event`.
```ruby
- describe '#show', :snowplow do
+ describe '#show' do
it 'does not track any snowplow events' do
get :show
@@ -1357,6 +1382,47 @@ RSpec.configure do |config|
end
```
+### Testing Ruby constants
+
+When testing code that uses Ruby constants, focus the test on the behavior that depends on the constant,
+rather than testing the values of the constant.
+
+For example, the following is preferred because it tests the behavior of the class method `.categories`.
+
+```ruby
+ describe '.categories' do
+ it 'gets CE unique category names' do
+ expect(described_class.categories).to include(
+ 'deploy_token_packages',
+ 'user_packages',
+ # ...
+ 'kubernetes_agent'
+ )
+ end
+ end
+```
+
+On the other hand, testing the value of the constant itself, often only repeats the values
+in the code and the test, which provides little value.
+
+```ruby
+ describe CATEGORIES do
+ it 'has values' do
+ expect(CATEGORIES).to eq([
+ 'deploy_token_packages',
+ 'user_packages',
+ # ...
+ 'kubernetes_agent'
+ ])
+ end
+end
+```
+
+In critical cases where an error on a constant could have a catastrophic impact,
+testing the constant values might be useful as an added safeguard. For example,
+if it could bring down the entire GitLab service, cause a customer to be billed more than they should be,
+or [cause the universe to implode](../contributing/verify/index.md#do-not-cause-our-universe-to-implode).
+
### Factories
GitLab uses [factory_bot](https://github.com/thoughtbot/factory_bot) as a test fixture replacement.
diff --git a/doc/development/testing_guide/end_to_end/best_practices.md b/doc/development/testing_guide/end_to_end/best_practices.md
index bfda94b1f1d..b17ca9e6f8f 100644
--- a/doc/development/testing_guide/end_to_end/best_practices.md
+++ b/doc/development/testing_guide/end_to_end/best_practices.md
@@ -62,13 +62,13 @@ In those and similar cases we need to include the test case link by other means.
To illustrate, there are two tests in the shared examples in [`qa/specs/features/ee/browser_ui/3_create/repository/restrict_push_protected_branch_spec.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/qa/specs/features/ee/browser_ui/3_create/repository/restrict_push_protected_branch_spec.rb):
```ruby
-shared_examples 'unselected maintainer' do |testcase|
+RSpec.shared_examples 'unselected maintainer' do |testcase|
it 'user fails to push', testcase: testcase do
...
end
end
-shared_examples 'selected developer' do |testcase|
+RSpec.shared_examples 'selected developer' do |testcase|
it 'user pushes and merges', testcase: testcase do
...
end
@@ -415,7 +415,7 @@ except(page).to have_no_text('hidden')
Unfortunately, that's not automatically the case for the predicate methods that we add to our
[page objects](page_objects.md). We need to [create our own negatable matchers](https://relishapp.com/rspec/rspec-expectations/v/3-9/docs/custom-matchers/define-a-custom-matcher#matcher-with-separate-logic-for-expect().to-and-expect().not-to).
-The initial example uses the `have_job` matcher which is derived from the
+The initial example uses the `have_job` matcher which is derived from the
[`has_job?` predicate method of the `Page::Project::Pipeline::Show` page object](https://gitlab.com/gitlab-org/gitlab/-/blob/87864b3047c23b4308f59c27a3757045944af447/qa/qa/page/project/pipeline/show.rb#L53).
To create a negatable matcher, we use `has_no_job?` for the negative case:
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 33f73304a26..9a39161f1ad 100644
--- a/doc/development/testing_guide/end_to_end/feature_flags.md
+++ b/doc/development/testing_guide/end_to_end/feature_flags.md
@@ -195,12 +195,10 @@ End-to-end tests should pass with a feature flag enabled before it is enabled on
There are two ways to confirm that end-to-end tests pass:
- If a merge request adds or edits a [feature flag definition file](../../feature_flags/index.md#feature-flag-definition-and-validation),
- two `package-and-qa` jobs (`package-and-qa-ff-enabled` and `package-and-qa-ff-disabled`) are included automatically in the merge request
- pipeline. One job enables the defined feature flag and the other job disables it. The jobs execute the same suite of tests to confirm
- that they pass with the feature flag either enabled or disabled.
-- In some cases, if `package-and-qa` hasn't been triggered automatically, or if it has run the tests with the default feature flag values
- (which might not be desired), you can create a Draft MR that enables the feature flag to ensure that all E2E tests pass with the feature
- flag enabled.
+ two `e2e:package-and-test` jobs (`ee:instance-parallel` and `ee:instance-parallel-ff-inverse`) are included automatically in the merge request pipeline.
+ One job runs the application with default feature flag state and another sets it to inverse value. The jobs execute the same suite of tests to confirm that they pass with the feature flag either enabled or disabled.
+- In some cases, if end-to-end test jobs didn't trigger automatically, or if it has run the tests with the default feature flag values (which might not be desired),
+ you can create a Draft MR that enables the feature flag to ensure that all E2E tests pass with the feature flag enabled and disabled.
### Troubleshooting end-to-end test failures with feature flag enabled
@@ -216,8 +214,8 @@ If enabling the feature flag results in E2E test failures, you can browse the ar
### Test execution during feature development
If an end-to-end test enables a feature flag, the end-to-end test suite can be used to test changes in a merge request
-by running the `package-and-qa` job in the merge request pipeline. If the feature flag and relevant changes have already been merged, you can confirm that the tests
-pass on the default branch. The end-to-end tests run on the default branch every two hours, and the results are posted to a
+by running the `e2e:package-and-test` job in the merge request pipeline. If the feature flag and relevant changes have already been merged, you can confirm that the tests
+pass on the default branch. The end-to-end tests run on the default branch every two hours, and the results are posted to a
[Test Session Report, which is available in the testcase-sessions project](https://gitlab.com/gitlab-org/quality/testcase-sessions/-/issues?label_name%5B%5D=found%3Amain).
If the relevant tests do not enable the feature flag themselves, you can check if the tests will need to be updated by opening
diff --git a/doc/development/testing_guide/end_to_end/index.md b/doc/development/testing_guide/end_to_end/index.md
index 989d090d581..cc9c02a65ce 100644
--- a/doc/development/testing_guide/end_to_end/index.md
+++ b/doc/development/testing_guide/end_to_end/index.md
@@ -8,16 +8,15 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## What is end-to-end testing?
-End-to-end testing is a strategy used to check whether your application works
+End-to-end (e2e) testing is a strategy used to check whether your application works
as expected across the entire software stack and architecture, including
integration of all micro-services and components that are supposed to work
together.
## How do we test GitLab?
-We use [Omnibus GitLab](https://gitlab.com/gitlab-org/omnibus-gitlab) to build GitLab packages and then we
-test these packages using the [GitLab QA orchestrator](https://gitlab.com/gitlab-org/gitlab-qa) tool, which is
-a black-box testing framework for the API and the UI.
+We use [Omnibus GitLab](https://gitlab.com/gitlab-org/omnibus-gitlab) to build GitLab packages and then we test these packages
+using the [GitLab QA orchestrator](https://gitlab.com/gitlab-org/gitlab-qa) tool to run the end-to-end tests located in the `qa` directory.
### Testing nightly builds
@@ -33,11 +32,9 @@ You can find these pipelines at <https://gitlab.com/gitlab-org/quality/staging/p
### Testing code in merge requests
-#### Using the `package-and-qa` job
+#### Using the package-and-test job
-It is possible to run end-to-end tests for a merge request, eventually being run in
-a pipeline in the [`gitlab-org/gitlab-qa-mirror`](https://gitlab.com/gitlab-org/gitlab-qa-mirror) project,
-by triggering the `package-and-qa` manual action in the `qa` stage (not
+It is possible to run end-to-end tests for a merge request by triggering the `e2e:package-and-test` manual action in the `qa` stage (not
available for forks).
**This runs end-to-end tests against a custom EE (with an Ultimate license)
@@ -59,7 +56,7 @@ graph TB
subgraph "`gitlab-org/gitlab` pipeline"
A1[`build-images` stage<br>`build-qa-image` and `build-assets-image` jobs]
- A2[`qa` stage<br>`package-and-qa` job]
+ A2[`qa` stage<br>`e2e:package-and-test` job]
end
subgraph "`gitlab-org/build/omnibus-gitlab-mirror` pipeline"
@@ -72,38 +69,30 @@ subgraph "`gitlab-org/gitlab-qa-mirror` pipeline"
```
1. In the [`gitlab-org/gitlab` pipeline](https://gitlab.com/gitlab-org/gitlab):
- 1. Developer triggers the `package-and-qa` manual action (available once the `build-qa-image` and
+ 1. Developer triggers the `e2e:package-and-test` manual action (available once the `build-qa-image` and
`build-assets-image` jobs are done), that can be found in GitLab merge
- requests. This starts a chain of pipelines in multiple projects.
- 1. The script being executed triggers a pipeline in
+ requests. This starts a e2e test child pipeline.
+ 1. E2E child pipeline triggers a downstream pipeline in
[`gitlab-org/build/omnibus-gitlab-mirror`](https://gitlab.com/gitlab-org/build/omnibus-gitlab-mirror)
and polls for the resulting status. We call this a _status attribution_.
1. In the [`gitlab-org/build/omnibus-gitlab-mirror` pipeline](https://gitlab.com/gitlab-org/build/omnibus-gitlab-mirror):
1. Docker image is being built and pushed to its Container Registry.
- 1. Finally, the `Trigger:qa-test` job triggers a new end-to-end pipeline in
- [`gitlab-org/gitlab-qa-mirror`](https://gitlab.com/gitlab-org/gitlab-qa-mirror/pipelines) and polls for the resulting status.
+ 1. Once Docker images are built and pushed jobs in `test` stage are started
-1. In the [`gitlab-org/gitlab-qa-mirror` pipeline](https://gitlab.com/gitlab-org/gitlab-qa-mirror):
+1. In the `Test` stage:
1. Container for the Docker image stored in the [`gitlab-org/build/omnibus-gitlab-mirror`](https://gitlab.com/gitlab-org/build/omnibus-gitlab-mirror) registry is spun-up.
1. End-to-end tests are run with the `gitlab-qa` executable, which spin up a container for the end-to-end image from the [`gitlab-org/gitlab`](https://gitlab.com/gitlab-org/gitlab) registry.
-1. The result of the [`gitlab-org/gitlab-qa-mirror` pipeline](https://gitlab.com/gitlab-org/gitlab-qa-mirror) is being
- propagated upstream (through polling from upstream pipelines), through [`gitlab-org/build/omnibus-gitlab-mirror`](https://gitlab.com/gitlab-org/build/omnibus-gitlab-mirror), back to the [`gitlab-org/gitlab`](https://gitlab.com/gitlab-org/gitlab) merge request.
-
-We plan to [add more specific information](https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/156)
-about the tests included in each job/scenario that runs in `gitlab-org/gitlab-qa-mirror`.
-
NOTE:
You may have noticed that we use `gitlab-org/build/omnibus-gitlab-mirror` instead of
-`gitlab-org/omnibus-gitlab`, and `gitlab-org/gitlab-qa-mirror` instead of `gitlab-org/gitlab-qa`.
+`gitlab-org/omnibus-gitlab`.
This is due to technical limitations in the GitLab permission model: the ability to run a pipeline
against a protected branch is controlled by the ability to push/merge to this branch.
This means that for developers to be able to trigger a pipeline for the default branch in
-`gitlab-org/omnibus-gitlab`/`gitlab-org/gitlab-qa`, they would need to have the
-Maintainer role for those projects.
+`gitlab-org/omnibus-gitlab`, they would need to have the Maintainer role for this project.
For security reasons and implications, we couldn't open up the default branch to all the Developers.
-Hence we created these mirrors where Developers and Maintainers are allowed to push/merge to the default branch.
+Hence we created this mirror where Developers and Maintainers are allowed to push/merge to the default branch.
This problem was discovered in <https://gitlab.com/gitlab-org/gitlab-qa/-/issues/63#note_107175160> and the "mirror"
work-around was suggested in <https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/4717>.
A feature proposal to segregate access control regarding running pipelines from ability to push/merge was also created at <https://gitlab.com/gitlab-org/gitlab/-/issues/24585>.
@@ -111,22 +100,19 @@ A feature proposal to segregate access control regarding running pipelines from
#### With merged results pipelines
In a merged results pipeline, the pipeline runs on a new ref that contains the merge result of the source and target branch.
-However, this ref is not available to the `gitlab-qa-mirror` pipeline.
-For this reason, the end-to-end tests on a merged results pipeline would use the head of the merge request source branch.
+The end-to-end tests on a merged results pipeline would use the new ref instead of the head of the merge request source branch.
```mermaid
graph LR
-A["a1b1c1 - branch HEAD (CI_MERGE_REQUEST_SOURCE_BRANCH_SHA)"]
-B["x1y1z1 - master HEAD"]
-C["d1e1f1 - merged results (CI_COMMIT_SHA)"]
+A["x1y1z1 - master HEAD"]
+B["d1e1f1 - merged results (CI_COMMIT_SHA)"]
-A --> C
-B --> C
+A --> B
-A --> E["E2E tests"]
-C --> D["Merged results pipeline"]
+B --> C["Merged results pipeline"]
+C --> D["E2E tests"]
```
##### Running custom tests
@@ -140,7 +126,7 @@ a flaky test we first want to make sure that it's no longer flaky.
We can do that using the `ce:custom-parallel` and `ee:custom-parallel` jobs.
Both are manual jobs that you can configure using custom variables.
When clicking the name (not the play icon) of one of the parallel jobs,
-you are prompted to enter variables. You can use any of
+you are prompted to enter variables. You can use any of
[the variables that can be used with `gitlab-qa`](https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/what_tests_can_be_run.md#supported-gitlab-environment-variables)
as well as these:
@@ -150,8 +136,8 @@ as well as these:
| `QA_TESTS` | The tests to run (no default, which means run all the tests in the scenario). Use file paths as you would when running tests via RSpec, for example, `qa/specs/features/ee/browser_ui` would include all the `EE` UI tests. |
| `QA_RSPEC_TAGS` | The RSpec tags to add (no default) |
-For now,
-[manual jobs with custom variables don't use the same variable when retried](https://gitlab.com/gitlab-org/gitlab/-/issues/31367),
+For now,
+[manual jobs with custom variables don't use the same variable when retried](https://gitlab.com/gitlab-org/gitlab/-/issues/31367),
so if you want to run the same tests multiple times,
specify the same variables in each `custom-parallel` job (up to as
many of the 10 available jobs that you want to run).
@@ -165,7 +151,7 @@ automatically started: it runs the QA smoke suite against the
You can also manually start the `review-qa-all`: it runs the full QA suite
against the [Review App](../review_apps.md).
-**This runs end-to-end tests against a Review App based on
+**This runs end-to-end tests against a Review App based on
[the official GitLab Helm chart](https://gitlab.com/gitlab-org/charts/gitlab/), itself deployed with custom
[Cloud Native components](https://gitlab.com/gitlab-org/build/CNG) built from your merge request's changes.**
@@ -197,7 +183,7 @@ Use these environment variables to configure metrics export:
| -------- | -------- | ----------- |
| `QA_INFLUXDB_URL` | `true` | Should be set to `https://influxdb.quality.gitlab.net`. No default value. |
| `QA_INFLUXDB_TOKEN` | `true` | InfluxDB write token that can be found under `Influxdb auth tokens` document in `Gitlab-QA` `1Password` vault. No default value. |
-| `QA_RUN_TYPE` | `false` | Arbitrary name for test execution, like `package-and-qa`. Automatically inferred from the project name for live environment test executions. No default value. |
+| `QA_RUN_TYPE` | `false` | Arbitrary name for test execution, like `package-and-test`. Automatically inferred from the project name for live environment test executions. No default value. |
| `QA_EXPORT_TEST_METRICS` | `false` | Flag to enable or disable metrics export. Defaults to `true`. |
## Test reports
@@ -231,7 +217,7 @@ a link to the current test report.
Each type of scheduled pipeline generates a static link for the latest test report according to its stage:
-- [`master`](https://storage.googleapis.com/gitlab-qa-allure-reports/package-and-qa/master/index.html)
+- [`master`](https://storage.googleapis.com/gitlab-qa-allure-reports/e2e-package-and-test/master/index.html)
- [`staging-full`](https://storage.googleapis.com/gitlab-qa-allure-reports/staging-full/master/index.html)
- [`staging-sanity`](https://storage.googleapis.com/gitlab-qa-allure-reports/staging-sanity/master/index.html)
- [`staging-sanity-no-admin`](https://storage.googleapis.com/gitlab-qa-allure-reports/staging-sanity-no-admin/master/index.html)
@@ -244,7 +230,7 @@ Each type of scheduled pipeline generates a static link for the latest test repo
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
the existing tests against a live GitLab instance or against a pre-built Docker image,
-use the [GitLab QA orchestrator](https://gitlab.com/gitlab-org/gitlab-qa/tree/master/README.md). See also
+use the [GitLab QA orchestrator](https://gitlab.com/gitlab-org/gitlab-qa/tree/master/README.md). See also
[examples of the test scenarios you can run via the orchestrator](https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/what_tests_can_be_run.md#examples).
On the other hand, if you would like to run against a local development GitLab
@@ -263,7 +249,7 @@ architecture. See the [documentation about it](https://gitlab.com/gitlab-org/git
Once you 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),
+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).
### Consider **not** writing an end-to-end test
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 322f2412e5b..1abaf3ef323 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,27 +15,28 @@ 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 Preprod. See [testing with feature flags](../../../development/testing_guide/end_to_end/feature_flags.md) for more details. |
| `: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. |
| `:group_saml` | The test requires a GitLab instance that has SAML SSO enabled at the group level. Interacts with an external SAML identity provider. Paired with the `:orchestrated` tag. |
| `:instance_saml` | The test requires a GitLab instance that has SAML SSO enabled at the instance level. Interacts with an external SAML identity provider. Paired with the `:orchestrated` tag. |
-| `:integrations` | This aims to test the available [integrations](../../../user/project/integrations/index.md#available-integrations). The test requires Docker to be installed in the run context. It will provision the containers and can be run against a local instance or using the `gitlab-qa` scenario `Test::Integration::Integrations` |
+| `:integrations` | This aims to test the available [integrations](../../../user/project/integrations/index.md#available-integrations). The test requires Docker to be installed in the run context. It will provision the containers and can be run against a local instance or using the `gitlab-qa` scenario `Test::Integration::Integrations` |
| `:issue`, `:issue_${num}` | Optional links to issues which might be related to the spec. Helps keep track of related issues and can also be used by tools that create test reports. Currently added automatically to `Allure` test report. Multiple tags can be used by adding an optional numeric suffix like `issue_1`, `issue_2` etc. |
-| `:service_ping_disabled` | The test interacts with the GitLab configuration service ping at the instance level to turn Admin Area setting service ping checkbox on or off. This tag will have the test run only in the `service_ping_disabled` job and must be paired with the `:orchestrated` and `:requires_admin` tags. |
+| `:service_ping_disabled` | The test interacts with the GitLab configuration service ping at the instance level to turn Admin Area setting service ping checkbox on or off. This tag will have the test run only in the `service_ping_disabled` job and must be paired with the `:orchestrated` and `:requires_admin` tags. |
| `:jira` | The test requires a Jira Server. [GitLab-QA](https://gitlab.com/gitlab-org/gitlab-qa) provisions the Jira Server in a Docker container when the `Test::Integration::Jira` test scenario is run. |
| `:kubernetes` | The test includes a GitLab instance that is configured to be run behind an SSH tunnel, allowing a TLS-accessible GitLab. This test also includes provisioning of at least one Kubernetes cluster to test against. _This tag is often be paired with `:orchestrated`._ |
| `:ldap_no_server` | The test requires a GitLab instance to be configured to use LDAP. To be used with the `:orchestrated` tag. It does not spin up an LDAP server at orchestration time. Instead, it creates the LDAP server at runtime. |
| `:ldap_no_tls` | The test requires a GitLab instance to be configured to use an external LDAP server with TLS not enabled. |
| `:ldap_tls` | The test requires a GitLab instance to be configured to use an external LDAP server with TLS enabled. |
| `:mattermost` | The test requires a GitLab Mattermost service on the GitLab instance. |
-| `:metrics` | The test requires a GitLab instance where [dedicated metrics exporters](../../../administration/monitoring/prometheus/web_exporter.md) are running alongside Puma and Sidekiq. |
+| `:metrics` | The test requires a GitLab instance where [dedicated metrics exporters](../../../administration/monitoring/prometheus/web_exporter.md) are running alongside Puma and Sidekiq. |
| `:mixed_env` | The test should only be executed in environments that have a paired canary version available through traffic routing based on the existence of the `gitlab_canary=true` cookie. Tests in this category are switching the cookie mid-test to validate mixed deployment environments. |
| `:object_storage` | The test requires a GitLab instance to be configured to use multiple [object storage types](../../../administration/object_storage.md). Uses MinIO as the object storage server. |
| `:only` | The test is only to be run in specific execution contexts. See [test execution context selection](execution_context_selection.md) for more information. |
| `:orchestrated` | The GitLab instance under test may be [configured by `gitlab-qa`](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/docs/what_tests_can_be_run.md#orchestrated-tests) to be different to the default GitLab configuration, or `gitlab-qa` may launch additional services in separate Docker containers, or both. Tests tagged with `:orchestrated` are excluded when testing environments where we can't dynamically modify the GitLab configuration (for example, Staging). |
-| `:packages` | The test requires a GitLab instance that has the [Package Registry](../../../administration/packages/#gitlab-package-registry-administration) enabled. |
+| `:packages` | The test requires a GitLab instance that has the [Package Registry](../../../administration/packages/index.md#gitlab-package-registry-administration) enabled. |
+| `:product_group` | Specifies what product group the test belongs to. See [Product sections, stages, groups, and categories](https://about.gitlab.com/handbook/product/categories) for the comprehensive groups list. |
| `:quarantine` | The test has been [quarantined](https://about.gitlab.com/handbook/engineering/quality/quality-engineering/debugging-qa-test-failures/#quarantining-tests), runs in a separate job that only includes quarantined tests, and is allowed to fail. The test is skipped in its regular job so that if it fails it doesn't hold up the pipeline. Note that you can also [quarantine a test only when it runs in a specific context](execution_context_selection.md#quarantine-a-test-for-a-specific-environment). |
| `:relative_url` | The test requires a GitLab instance to be installed under a [relative URL](../../../install/relative_url.md). |
| `:reliable` | The test has been [promoted to a reliable test](https://about.gitlab.com/handbook/engineering/quality/quality-engineering/reliable-tests/#promoting-an-existing-test-to-reliable) meaning it passes consistently in all pipelines, including merge requests. |
@@ -44,7 +45,7 @@ This is a partial list of the [RSpec metadata](https://relishapp.com/rspec/rspec
| `:requires_git_protocol_v2` | The test requires that Git protocol version 2 is enabled on the server. It's assumed to be enabled by default but if not the test can be skipped by setting `QA_CAN_TEST_GIT_PROTOCOL_V2` to `false`. |
| `:requires_praefect` | The test requires that the GitLab instance uses [Gitaly Cluster](../../../administration/gitaly/praefect.md) (a.k.a. Praefect) as the repository storage . It's assumed to be used by default but if not the test can be skipped by setting `QA_CAN_TEST_PRAEFECT` to `false`. |
| `:runner` | The test depends on and sets up a GitLab Runner instance, typically to run a pipeline. |
-| `:sanity_feature_flags` | The test verifies the functioning of the feature flag handling part of the test framework |
+| `:sanity_feature_flags` | The test verifies the functioning of the feature flag handling part of the test framework |
| `:skip_live_env` | The test is excluded when run against live deployed environments such as Staging, Canary, and Production. |
| `:skip_fips_env` | The test is excluded when run against an environment in FIPS mode. |
| `:skip_signup_disabled` | The test uses UI to sign up a new user and is skipped in any environment that does not allow new user registration via the UI. |
diff --git a/doc/development/testing_guide/flaky_tests.md b/doc/development/testing_guide/flaky_tests.md
index 3c8df7d3416..7409f15969d 100644
--- a/doc/development/testing_guide/flaky_tests.md
+++ b/doc/development/testing_guide/flaky_tests.md
@@ -123,7 +123,7 @@ reproduction.
### Hanging specs
-If a spec hangs, it might be caused by a [bug in Rails](https://github.com/rails/rails/issues/34310):
+If a spec hangs, it might be caused by a [bug in Rails](https://github.com/rails/rails/issues/45994):
- <https://gitlab.com/gitlab-org/gitlab/-/merge_requests/81112>
- <https://gitlab.com/gitlab-org/gitlab/-/issues/337039>
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index 2845dde9a24..22a8792bac6 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -420,7 +420,11 @@ it('passes', () => {
### Waiting in tests
Sometimes a test needs to wait for something to happen in the application before it continues.
-Avoid using [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout) because it makes the reason for waiting unclear. Instead use one of the following approaches.
+
+You should try to avoid:
+
+- [`setTimeout`](https://developer.mozilla.org/en-US/docs/Web/API/setTimeout) because it makes the reason for waiting unclear. Additionally, it is faked in our tests so its usage is tricky.
+- [`setImmediate`](https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate) because it is no longer supported in Jest 27 and later. See [this epic](https://gitlab.com/groups/gitlab-org/-/epics/7002) for details.
#### Promises and Ajax calls
@@ -448,14 +452,15 @@ it('waits for an Ajax call', async () => {
});
```
-If you are not able to register handlers to the `Promise`, for example because it is executed in a synchronous Vue life cycle hook, take a look at the [waitFor](#wait-until-axios-requests-finish) helpers or you can flush all pending `Promise`s:
+If you cannot register handlers to the `Promise`, for example because it is executed in a synchronous Vue lifecycle hook, take a look at the [`waitFor`](#wait-until-axios-requests-finish) helpers or flush all pending `Promise`s with:
**in Jest:**
```javascript
-it('waits for an Ajax call', () => {
+it('waits for an Ajax call', async () => {
synchronousFunction();
- jest.runAllTicks();
+
+ await waitForPromises();
expect(something).toBe('done');
});
@@ -872,7 +877,7 @@ This will create a new fixture located at
`tmp/tests/frontend/fixtures-ee/graphql/releases/graphql/queries/all_releases.query.graphql.json`.
You can import the JSON fixture in a Jest test using the `test_fixtures` alias
-[as described below](#use-fixtures).
+[as described previously](#use-fixtures).
## Data-driven tests
@@ -1073,7 +1078,7 @@ testAction(
<!-- vale gitlab.Spelling = NO -->
-The Axios Utils mock module located in `spec/frontend/mocks/ce/lib/utils/axios_utils.js` contains two helper methods for Jest tests that spawn HTTP requests.
+The Axios Utils mock module located in `spec/frontend/__helpers__/mocks/axios_utils.js` contains two helper methods for Jest tests that spawn HTTP requests.
These are very useful if you don't have a handle to the request's Promise, for example when a Vue component does a request as part of its life cycle.
<!-- vale gitlab.Spelling = YES -->
diff --git a/doc/development/testing_guide/index.md b/doc/development/testing_guide/index.md
index cd7c70e2eaa..e50902c4995 100644
--- a/doc/development/testing_guide/index.md
+++ b/doc/development/testing_guide/index.md
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
This document describes various guidelines and best practices for automated
testing of the GitLab project.
-It is meant to be an _extension_ of the
+It is meant to be an _extension_ of the
[Thoughtbot testing style guide](https://github.com/thoughtbot/guides/tree/master/testing-rspec). If
this guide defines a rule that contradicts the Thoughtbot guide, this guide
takes precedence. Some guidelines may be repeated verbatim to stress their
diff --git a/doc/development/testing_guide/testing_migrations_guide.md b/doc/development/testing_guide/testing_migrations_guide.md
index 261a4f4a27e..3006a2230ac 100644
--- a/doc/development/testing_guide/testing_migrations_guide.md
+++ b/doc/development/testing_guide/testing_migrations_guide.md
@@ -317,7 +317,7 @@ To test these you usually have to:
- Verify that the expected jobs were scheduled, with the correct set
of records, the correct batch size, interval, etc.
-The behavior of the background migration itself needs to be verified in a
+The behavior of the background migration itself needs to be verified in a
[separate test for the background migration class](#example-background-migration-test).
This spec tests the
diff --git a/doc/development/value_stream_analytics.md b/doc/development/value_stream_analytics.md
index 0d545fa8e3f..bbea89d5645 100644
--- a/doc/development/value_stream_analytics.md
+++ b/doc/development/value_stream_analytics.md
@@ -6,33 +6,86 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Value stream analytics development guide
-Value stream analytics calculates the time between two arbitrary events recorded on domain objects and provides aggregated statistics about the duration.
+For information on how to configure value stream analytics (VSA) in GitLab, see our [analytics documentation](../user/analytics/value_stream_analytics.md).
-For information on how to configure value stream analytics in GitLab, see our [analytics documentation](../user/analytics/value_stream_analytics.md).
+## How does Value Stream Analytics work?
-## Stage
+Value Stream Analytics calculates the duration between two timestamp columns or timestamp
+expressions and runs various aggregations on the data.
-During development, events occur that move issues and merge requests through different stages of progress until they are considered finished. These stages can be expressed with the `Stage` model.
+For example:
-Example stage:
+- Duration between the Merge Request creation time and Merge Request merge time.
+- Duration between the Issue creation time and Issue close time.
-- Name: Development
-- Start event: Issue created
-- End event: Issue first mentioned in commit
-- Parent: `Group: gitlab-org`
+This duration is exposed in various ways:
+
+- Aggregation: median, average
+- Listing: list the duration for individual Merge Request and Issue records
+
+Apart from the durations, we expose the record count within a stage.
+
+## Feature availability
+
+- Group level (licensed): Requires Ultimate or Premium subscription. This version is the most
+feature-full.
+- Project level (licensed): We are continually adding features to project level VSA to bring it in line with group level VSA.
+- Project level (FOSS): Keep it as is.
+
+|Feature|Group level (licensed)|Project level (licensed)|Project level (FOSS)|
+|-|-|-|-|
+|Create custom value streams|Yes|No, only one value stream (default) is present with the default stages|no, only one value stream (default) is present with the default stages|
+|Create custom stages|Yes|No|No|
+|Filtering (author, label, milestone, etc.)|Yes|Yes|Yes|
+|Stage time chart|Yes|No|No|
+|Total time chart|Yes|No|No|
+|Task by type chart|Yes|No|No|
+|DORA Metrics|Yes|Yes|No|
+|Cycle time and lead time summary (Key metrics)|Yes|Yes|No|
+|New issues, commits and deploys (Key metrics)|Yes, excluding commits|Yes|Yes|
+|Uses aggregated backend|Yes|No|No|
+|Date filter behavior|Filters items [finished within the date range](https://gitlab.com/groups/gitlab-org/-/epics/6046)|Filters items by creation date.|Filters items by creation date.|
+|Authorization|At least reporter|At least reporter|Can be public.|
+
+## VSA core domain objects
+
+### Stages
+
+A stage represents an event pair (start and end events) with additional metadata, such as the name
+of the stage. Stages are configurable by the user within the pairing rules defined in the backend.
+
+**Example stage: Code Review**
+
+- Start event identifier: Merge request creation time.
+- Start event column: uses the `merge_requests.created_at` timestamp column.
+- End event identifier: Merge request merge time.
+- End event column: uses the `merge_request_metrics.merged_at` timestamp column.
+- Stage event hash ID: a calculated hash for the pair of start and end event identifiers.
+ - If two stages have the same configuration of start and end events, then their stage event hash.
+ IDs are identical.
+ - The stage event hash ID is later used to store the aggregated data in partitioned database tables.
+
+Historically, value stream analytics defined [7 stages](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/analytics/cycle_analytics/default_stages.rb)
+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.
### Events
Events are the smallest building blocks of the value stream analytics feature. A stage consists of two events:
-- Start
-- End
+- Start event
+- End event
These events play a key role in the duration calculation.
Formula: `duration = end_event_time - start_event_time`
-To make the duration calculation flexible, each `Event` is implemented as a separate class. They're responsible for defining a timestamp expression that is used in the calculation query.
+To make the duration calculation flexible, each `Event` is implemented as a separate class.
+They're responsible for defining a timestamp expression that is used in the calculation query.
#### Implementing an `Event` class
@@ -81,7 +134,7 @@ def apply_query_customization(query)
end
```
-### Validating start and end events
+#### Validating start and end events
Some start/end event pairs are not "compatible" with each other. For example:
@@ -171,23 +224,7 @@ graph LR;
MergeRequestLabelRemoved --> MergeRequestLabelRemoved;
```
-### Parent
-
-Teams and organizations might define their own way of building software, thus stages can be completely different. For each stage, a parent object needs to be defined.
-
-Currently supported parents:
-
-- `Project`
-- `Group`
-
-#### How parent relationship it work
-
-1. User navigates to the value stream analytics page.
-1. User selects a group.
-1. Backend loads the defined stages for the selected group.
-1. Additions and modifications to the stages are persisted within the selected group only.
-
-### Default stages
+## Default stages
The [original implementation](https://gitlab.com/gitlab-org/gitlab/-/issues/847) of value stream analytics defined 7 stages. These stages are always available for each parent, however altering these stages is not possible.
@@ -209,31 +246,15 @@ The reason for this was that we'd like to add the abilities to hide and order st
For a new calculation or a query, implement it as a new method call in the `DataCollector` class.
-## Database query
-
-Structure of the database query:
-
-```sql
-SELECT (customized by: Median or RecordsFetcher or DataForDurationChart)
-FROM OBJECT_TYPE (Issue or MergeRequest)
-INNER JOIN (several JOIN statements, depending on the events)
-WHERE
- (Filter by the PARENT model, example: filter Issues from Project A)
- (Date range filter based on the OBJECT_TYPE.created_at)
- (Check if the START_EVENT is earlier than END_EVENT, preventing negative duration)
-```
-
-Structure of the `SELECT` statement for `Median`:
+To support the aggregated value stream analytics backend, these classes were reimplemented within [`Aggregated`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/analytics/cycle_analytics/aggregated) namespace.
-```sql
-SELECT (calculate median from START_EVENT_TIME-END_EVENT_TIME)
-```
+### Database query backend
-Structure of the `SELECT` statement for `DataForDurationChart`:
+VSA supports two backends: [aggregated](value_stream_analytics/value_stream_analytics_aggregated_backend.md) and "live". The live query backend can be
+considered legacy, which will be phased out at some point.
-```sql
-SELECT (START_EVENT_TIME-END_EVENT_TIME) as duration, END_EVENT.timestamp
-```
+- "live": uses the standard `IssuableFinders`.
+- aggregated: queries data from pre-aggregated database tables.
## High-level overview
@@ -244,6 +265,31 @@ SELECT (START_EVENT_TIME-END_EVENT_TIME) as duration, END_EVENT.timestamp
- Responsible for composing queries and define feature specific business logic.
- `DataCollector`, `Event`, `StageEvents`, etc.
+## Frontend
+
+[Project VSA](../user/analytics/value_stream_analytics.md) is available for all users and:
+
+- Includes a mixture of key and DORA metrics based on the tier.
+- Uses the set of [default stages](#default-stages).
+
+[Group VSA](../user/group/value_stream_analytics/index.md) is only available for licensed users and extends project VSA to include:
+
+- An [overview stage](https://gitlab.com/gitlab-org/gitlab/-/issues/321438).
+- The ability to create custom value streams.
+
+The group and project level VSA frontends are both built with Vue and Vuex and follow a similar pattern:
+
+- The `index.js` file extracts any URL query parameters, creates the Vue app and Vuex store, and dispatches an `initialize` Vuex action.
+- The `base.vue` file is used to render the main components for each page, metrics, filters, charts, and the stage table.
+
+The group VSA Vuex store makes use of [Vuex modules](https://vuex.vuejs.org/guide/modules.html) to separate some of the state and logic used for rendering the charts.
+
+### Shared components
+
+Parts of the UI are shared between project VSA and group VSA such as the stage table and path. These shared components live in the project VSA directory `app/assets/javascripts/cycle_analytics/components` and are included at the group level VSA where needed.
+
+All the frontend code for group-level features are located in `ee/app/assets/javascripts/analytics/cycle_analytics/components`.
+
## Testing
Since we have a lots of events and possible pairings, testing each pairing is not possible. The rule is to have at least one test case using an `Event` class.
@@ -252,3 +298,31 @@ Writing a test case for a stage using a new `Event` can be challenging since dat
- Different parents: `Group` or `Project`
- Different calculations: `Median`, `RecordsFetcher` or `DataForDurationChart`
+
+The VSA frontend is tested extensively on two different levels (integration, unit):
+
+- End-to-end integration tests using a real backend via Capybara and RSpec.
+- Jest frontend tests with pre-generated data fixtures.
+
+## Development setup and testing
+
+Running Value Stream Analytics can be done via the [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit). By default, you'll be able to view the project-level (FOSS) version of the feature.
+
+If your GDK is up and running, you can run the seed script to generate some data:
+
+```shell
+SEED_CYCLE_ANALYTICS=true SEED_VSA=true FILTER=cycle_analytics rake db:seed_fu
+```
+
+The data generator script creates a new group and a new project with issue and merge request
+data (see the output of the script). To view the group-level version of the feature, you
+need to request a license for your GDK instance.
+
+After this step, you can access the group level value stream analytics page where you can create
+value streams and stages. The data aggregation might be delayed so you might not see the
+data right after the stage creation. To speed up this process, you can run the following command
+in your rails console (`rails c`):
+
+```ruby
+Analytics::CycleAnalytics::ReaggregationWorker.new.perform
+```
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 79262e2d0dc..6087d4bd8f7 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
@@ -21,8 +21,7 @@ Value Stream Analytics (VSA).
## Current Status
-As of 14.8 the aggregated VSA backend is used only in the `gitlab-org` group, for testing purposes
-. We plan to gradually roll it out in the next major release (15.0) for the rest of the groups.
+The aggregated backend is used by default since GitLab 15.0 on the group-level.
## Motivation
@@ -53,44 +52,6 @@ database with a minimal development effort.
used by the system.
- Example: `MIN(issues.created_at, issues.updated_at)`
-## How does Value Stream Analytics work?
-
-Value Stream Analytics calculates the duration between two timestamp columns or timestamp
-expressions and runs various aggregations on the data.
-
-Examples:
-
-- Duration between the Merge Request creation time and Merge Request merge time.
-- Duration between the Issue creation time and Issue close time.
-
-This duration is exposed in various ways:
-
-- Aggregation: median, average
-- Listing: list the duration for individual Merge Request and Issue records
-
-Apart from the durations, we expose the record count within a stage.
-
-### Stages
-
-A stage represents an event pair (start and end events) with additional metadata, such as the name
-of the stage. Stages are configurable by the user within the pairing rules defined in the backend.
-
-**Example stage: Code Review**
-
-- Start event identifier: Merge Request creation time
-- Start event column: uses the `merge_requests.created_at` timestamp column.
-- End event identifier: Merge Request merge time
-- End event column: uses the `merge_request_metrics.merged_at` timestamp column.
-- Stage event hash ID: a calculated hash for the pair of start and end event identifiers.
- - If two stages have the same configuration of start and end events, then their stage event hash
- IDs are identical.
- - The stage event hash ID is later used to store the aggregated data in partitioned database tables.
-
-### Value streams
-
-Value streams are container objects for the stages. There can be multiple value streams per group
-or project focusing on different aspects of the Dev Ops lifecycle.
-
### Example configuration
![vsa object hierarchy example](img/object_hierarchy_example_V14_10.png)
diff --git a/doc/development/work_items.md b/doc/development/work_items.md
index 3625f85eb82..f15c66ae847 100644
--- a/doc/development/work_items.md
+++ b/doc/development/work_items.md
@@ -36,7 +36,7 @@ Here are some problems with current issues usage and why we are looking into wor
differences in common interactions that the user needs to hold a complicated mental
model of how they each behave.
- Issues are not extensible enough to support all of the emerging jobs they need to facilitate.
-- Codebase maintainability and feature development becomes a bigger challenge as we grow the Issue type.
+- Codebase maintainability and feature development becomes a bigger challenge as we grow the Issue type
beyond its core role of issue tracking into supporting the different work item types and handling
logic and structure differences.
- New functionality is typically implemented with first class objects that import behavior from issues via
@@ -204,3 +204,33 @@ provide a smooth migration path of epics to WIT with minimal disruption to user
We will move towards work items, work item types, and custom widgets (CW) in an iterative process.
For a rough outline of the work ahead of us, see [epic 6033](https://gitlab.com/groups/gitlab-org/-/epics/6033).
+
+## Redis HLL Counter Schema
+
+We need a more scalable Redis counter schema for work items that is inclusive of Plan xMAU, Project Management xMAU, Certify xMAU, and
+Product Planning xMAU. We cannot aggregate and dedupe events across features within a group or at the stage level with
+our current Redis slot schema.
+
+All three Plan product groups will be using the same base object (`work item`). Each product group still needs to
+track MAU.
+
+### Proposed aggregate counter schema
+
+```mermaid
+graph TD
+ Event[Specific Interaction Counter] --> AC[Aggregate Counters]
+ AC --> Plan[Plan xMAU]
+ AC --> PM[Project Management xMAU]
+ AC --> PP[Product Planning xMAU]
+ AC --> Cer[Certify xMAU]
+ AC --> WI[Work Items Users]
+```
+
+### Implementation
+
+The new aggregate schema is already implemented and we are already tracking work item unique actions
+in [GitLab.com](https://gitlab.com).
+
+For implementation details, this [MR](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93231) can be used
+as a reference. The MR covers the definition of new unique actions, event tracking in the code and also
+adding the new unique actions to the required aggregate counters.
diff --git a/doc/development/workhorse/index.md b/doc/development/workhorse/index.md
index 962124248ef..f210f511954 100644
--- a/doc/development/workhorse/index.md
+++ b/doc/development/workhorse/index.md
@@ -10,7 +10,7 @@ GitLab Workhorse is a smart reverse proxy for GitLab. It handles
"large" HTTP requests such as file downloads, file uploads, Git
push/pull and Git archive downloads.
-Workhorse itself is not a feature, but there are
+Workhorse itself is not a feature, but there are
[several features in GitLab](gitlab_features.md) that would not work efficiently without Workhorse.
The canonical source for Workhorse is
diff --git a/doc/gitlab-basics/start-using-git.md b/doc/gitlab-basics/start-using-git.md
index c6de723246c..875bd00ee55 100644
--- a/doc/gitlab-basics/start-using-git.md
+++ b/doc/gitlab-basics/start-using-git.md
@@ -148,7 +148,7 @@ between your computer and GitLab.
```
1. GitLab requests your username and password:
- - If you have 2FA enabled for your account, you must use a [Personal Access Token](../user/profile/personal_access_tokens.md)
+ - If you have 2FA enabled for your account, you must [clone using a token](#clone-using-a-token)
with `read_repository` or `write_repository` permissions instead of your account's password.
- If you don't have 2FA enabled, use your account's password.
@@ -163,6 +163,24 @@ On Windows, if you enter your password incorrectly multiple times and an `Access
add your namespace (username or group) to the path:
`git clone https://namespace@gitlab.com/gitlab-org/gitlab.git`.
+#### Clone using a token
+
+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 can use any of these tokens to authenticate when cloning over HTTPS:
+
+- [Personal access tokens](../user/profile/personal_access_tokens.md).
+- [Deploy tokens](../user/project/deploy_tokens/index.md).
+- [Project access tokens](../user/project/settings/project_access_tokens.md).
+- [Group access tokens](../user/group/settings/group_access_tokens.md).
+
+```shell
+git clone https://<username>:<token>@gitlab.example.com/tanuki/awesome_project.git
+```
+
### Convert a local directory into a repository
You can initialize a local folder so Git tracks it as a repository.
diff --git a/doc/install/aws/manual_install_aws.md b/doc/install/aws/manual_install_aws.md
index 7494dc1e95e..2aa2aa0c3f7 100644
--- a/doc/install/aws/manual_install_aws.md
+++ b/doc/install/aws/manual_install_aws.md
@@ -483,7 +483,7 @@ Connect to your GitLab instance via **Bastion Host A** using [SSH Agent Forwardi
#### Disable Let's Encrypt
-Because we're adding our SSL certificate at the load balancer, we do not need the GitLab built-in support for Let's Encrypt. Let's Encrypt [is enabled by default](https://docs.gitlab.com/omnibus/settings/ssl.html#lets-encrypt-integration) when using an `https` domain in GitLab 10.7 and later, so we must explicitly disable it:
+Because we're adding our SSL certificate at the load balancer, we do not need the GitLab built-in support for Let's Encrypt. Let's Encrypt [is enabled by default](https://docs.gitlab.com/omnibus/settings/ssl.html#enable-the-lets-encrypt-integration) when using an `https` domain in GitLab 10.7 and later, so we must explicitly disable it:
1. Open `/etc/gitlab/gitlab.rb` and disable it:
@@ -605,7 +605,7 @@ Now that we have our EC2 instance ready, follow the [documentation to install Gi
#### Add Support for Proxied SSL
-As we are terminating SSL at our [load balancer](#load-balancer), follow the steps at [Supporting proxied SSL](https://docs.gitlab.com/omnibus/settings/nginx.html#supporting-proxied-ssl) to configure this in `/etc/gitlab/gitlab.rb`.
+As we are terminating SSL at our [load balancer](#load-balancer), follow the steps at [Supporting proxied SSL](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-a-reverse-proxy-or-load-balancer-ssl-termination) to configure this in `/etc/gitlab/gitlab.rb`.
Remember to run `sudo gitlab-ctl reconfigure` after saving the changes to the `gitlab.rb` file.
diff --git a/doc/install/azure/index.md b/doc/install/azure/index.md
index 7f155a78140..dd2e7d678e8 100644
--- a/doc/install/azure/index.md
+++ b/doc/install/azure/index.md
@@ -233,7 +233,7 @@ The first thing that appears is the sign-in page. GitLab creates an administrato
The credentials are:
- Username: `root`
-- Password: the password is automatically created, and there are
+- Password: the password is automatically created, and there are
[two ways to find it](https://docs.bitnami.com/azure/faq/get-started/find-credentials/).
After signing in, be sure to immediately [change the password](../../user/profile/index.md#change-your-password).
@@ -248,7 +248,7 @@ in this section whenever you need to update GitLab.
To determine the version of GitLab you're currently running:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Dashboard**.
1. Find the version under the **Components** table.
@@ -294,7 +294,7 @@ up-to-date GitLab instance.
## Next steps and further configuration
Now that you have a functional GitLab instance, follow the
-[next steps](../index.md#next-steps) to learn what more you can do with your
+[next steps](../next_steps.md) to learn what more you can do with your
new installation.
## Troubleshooting
diff --git a/doc/install/cloud_native/index.md b/doc/install/cloud_native/index.md
index f3265500877..d971cd419e9 100644
--- a/doc/install/cloud_native/index.md
+++ b/doc/install/cloud_native/index.md
@@ -1,51 +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/engineering/ux/technical-writing/#assignments
-comments: false
-description: Install GitLab in a cloud native environment
-type: index
+redirect_to: 'https://docs.gitlab.com/charts/'
+remove_date: '2023-09-09'
---
-# Cloud Native GitLab **(FREE SELF)**
+This document was moved to [another location](https://docs.gitlab.com/charts/).
-[Cloud Native GitLab](https://gitlab.com/gitlab-org/build/CNG) provides cloud
-native containers to deploy GitLab. These containers may be deployed and managed
-via Helm using GitLab Charts or GitLab Operator on Kubernetes, OpenShift,
-and Kubernetes compatible container platforms:
-
-- [Helm charts](https://docs.gitlab.com/charts/): The cloud native Helm chart
- installs GitLab and all of its components on Kubernetes. Use this method if
- your infrastructure is built on Kubernetes and you're familiar with how it
- works. The methods for management, observability, and some concepts are
- different than traditional deployments.
-- [GitLab Operator](https://docs.gitlab.com/operator/): The GitLab Operator
- provides an installation and management method for GitLab following the
- [Kubernetes Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/).
- You can also use the GitLab Operator to run GitLab in an
- [OpenShift](../openshift_and_gitlab/index.md) environment.
-
-Here's an overview of how the containers are built:
-
-```mermaid
-graph TD
- subgraph Code
- CNG --> HC
- CNG --> GOP
- HC --> GOP
- end
-
- subgraph Deploy
- GOP --> K8s
- GOP --> OS
- CNG --> DC
- HC --> K8s
- end
-
- CNG[Cloud Native GitLab containers]
- HC[Helm Chart]
- K8s(Kubernetes)
- GOP[GitLab Operator]
- OS(OpenShift)
- DC(Docker Compose)
-```
+<!-- This redirect file can be deleted after <2023-09-09>. -->
+<!-- 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/install/docker.md b/doc/install/docker.md
index 7ec1b7c741a..bf08fecc9ca 100644
--- a/doc/install/docker.md
+++ b/doc/install/docker.md
@@ -301,7 +301,7 @@ point to a valid URL.
To receive e-mails from GitLab you have to configure the
[SMTP settings](https://docs.gitlab.com/omnibus/settings/smtp.html) because the GitLab Docker image doesn't
have an SMTP server installed. You may also be interested in
-[enabling HTTPS](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
+[enabling HTTPS](https://docs.gitlab.com/omnibus/settings/ssl.html).
After you make all the changes you want, you will need to restart the container
in order to reconfigure GitLab:
@@ -462,7 +462,9 @@ In most cases, upgrading GitLab is as easy as downloading the newest Docker
To upgrade GitLab that was [installed using Docker Engine](#install-gitlab-using-docker-engine):
-1. Take a [backup](#back-up-gitlab).
+1. Take a [backup](#back-up-gitlab). As a minimum, back up [the database](#create-a-database-backup) and
+ the GitLab secrets file.
+
1. Stop the running container:
```shell
@@ -506,7 +508,9 @@ when upgrading between major versions.
To upgrade GitLab that was [installed using Docker Compose](#install-gitlab-using-docker-compose):
-1. Take a [backup](#back-up-gitlab).
+1. Take a [backup](#back-up-gitlab). As a minimum, back up [the database](#create-a-database-backup) and
+ the GitLab secrets file.
+
1. Download the newest release and upgrade your GitLab instance:
```shell
@@ -527,8 +531,11 @@ We recommend you convert from the same version of CE to EE (for example, CE 14.1
This is not explicitly necessary, and any standard upgrade (for example, CE 14.0 to EE 14.1) should work.
The following steps assume that you are upgrading the same version.
-1. Take a [backup](#back-up-gitlab).
+1. Take a [backup](#back-up-gitlab). As a minimum, back up [the database](#create-a-database-backup) and
+ the GitLab secrets file.
+
1. Stop the current CE container, and remove or rename it.
+
1. To create a new container with GitLab EE,
replace `ce` with `ee` in your `docker run` command or `docker-compose.yml` file.
However, reuse the CE container name, port and file mappings, and version.
@@ -538,6 +545,22 @@ The following steps assume that you are upgrading the same version.
This is an optional step. If you [installed the documentation site](#install-the-product-documentation),
see how to [upgrade to another version](../administration/docs_self_host.md#upgrade-using-docker).
+### Downgrade GitLab
+
+To downgrade GitLab after an upgrade:
+
+1. Follow the upgrade procedure, but [specify the tag for the original version of GitLab](#use-tagged-versions-of-gitlab)
+ instead of `latest`.
+
+1. Restore the [database backup you made](#create-a-database-backup) as part of the upgrade.
+
+ - Restoring is required to back out database data and schema changes (migrations) made as part of the upgrade.
+ - GitLab backups must be restored to the exact same version and edition.
+ - [Follow the restore steps for Docker images](../raketasks/restore_gitlab.md#restore-for-docker-image-and-gitlab-helm-chart-installations), including
+ stopping Puma and Sidekiq. Only the database must be restored, so add
+ `SKIP=artifacts,repositories,registry,uploads,builds,pages,lfs,packages,terraform_state`
+ to the `gitlab-backup restore` command line arguments.
+
## Back up GitLab
You can create a GitLab backup with:
@@ -554,6 +577,23 @@ If configuration is provided entirely via the `GITLAB_OMNIBUS_CONFIG` environmen
meaning no configuration is set directly in the `gitlab.rb` file, then there is no need
to back up the `gitlab.rb` file.
+WARNING:
+[Backing up the GitLab secrets file](../raketasks/backup_gitlab.md#storing-configuration-files) is required
+to avoid [complicated steps](../raketasks/backup_restore.md#when-the-secrets-file-is-lost) when recovering
+GitLab from backup. The secrets file is stored at `/etc/gitlab/gitlab-secrets.json` inside the container, or
+`$GITLAB_HOME/config/gitlab-secrets.json` [on the container host](#set-up-the-volumes-location).
+
+### Create a database backup
+
+A database backup is required to roll back GitLab upgrade if you encounter issues.
+
+```shell
+docker exec -t <container name> gitlab-backup create SKIP=artifacts,repositories,registry,uploads,builds,pages,lfs,packages,terraform_state
+```
+
+The backup is written to `/var/opt/gitlab/backups` which should be on a
+[volume mounted by Docker](#set-up-the-volumes-location).
+
## Installing GitLab Community Edition
[GitLab CE Docker image](https://hub.docker.com/r/gitlab/gitlab-ce/)
diff --git a/doc/install/google_cloud_platform/index.md b/doc/install/google_cloud_platform/index.md
index b63ab8d4e2b..e924b6f7c41 100644
--- a/doc/install/google_cloud_platform/index.md
+++ b/doc/install/google_cloud_platform/index.md
@@ -42,7 +42,7 @@ To deploy GitLab on GCP you must create a virtual machine:
![Launch on Compute Engine](img/vm_details.png)
-1. To select the size, type, and desired [operating system](../requirements.md#supported-linux-distributions),
+1. To select the size, type, and desired [operating system](../../administration/package_information/supported_os.md#supported-operating-systems),
select **Change** under `Boot disk`. select **Select** when finished.
1. As a last step allow HTTP and HTTPS traffic, then select **Create**. The process finishes in a few seconds.
@@ -117,8 +117,8 @@ here's how you configure GitLab to be aware of the change:
### Configuring HTTPS with the domain name
-Although not needed, it's strongly recommended to secure GitLab with a TLS
-certificate. Follow the steps in the [Omnibus documentation](https://docs.gitlab.com/omnibus/settings/nginx.html#enable-https).
+Although not needed, it's strongly recommended to secure GitLab with a
+[TLS certificate](https://docs.gitlab.com/omnibus/settings/ssl.html).
### Configuring the email SMTP settings
diff --git a/doc/install/index.md b/doc/install/index.md
index 413c5116c14..89203c652d1 100644
--- a/doc/install/index.md
+++ b/doc/install/index.md
@@ -7,46 +7,14 @@ description: Read through the GitLab installation methods.
type: index
---
-# Installation **(FREE SELF)**
+# Install GitLab **(FREE SELF)**
-GitLab can be installed in most GNU/Linux distributions, and with several
-cloud providers. To get the best experience from GitLab, you must balance
-performance, reliability, ease of administration (backups, upgrades, and
-troubleshooting), and the cost of hosting.
+You can install GitLab on most GNU/Linux distributions, on several
+cloud providers, and in Kubernetes clusters.
-## Requirements
+To get the best experience, you should balance performance, reliability,
+ease of administration (backups, upgrades, and troubleshooting) with the cost of hosting.
-Before you install GitLab, be sure to review the [system requirements](requirements.md).
-The system requirements include details about the minimum hardware, software,
-database, and additional requirements to support GitLab.
+To get started, [choose your installation method](install_methods.md).
-## Choose the installation method
-
-Depending on your platform, select from the following available methods to
-install GitLab:
-
-| Installation method | Description | When to choose |
-|----------------------------------------------------------------|-------------|----------------|
-| [Linux package](https://docs.gitlab.com/omnibus/installation/) | The official deb/rpm packages (also known as Omnibus GitLab) that contains a bundle of GitLab and the components it depends on, including PostgreSQL, Redis, and Sidekiq. | This method is recommended for getting started. The Linux packages are mature, scalable, and are used today on GitLab.com. If you need additional flexibility and resilience, we recommend deploying GitLab as described in the [reference architecture documentation](../administration/reference_architectures/index.md). |
-| [Helm charts](https://docs.gitlab.com/charts/) | The cloud native Helm chart for installing GitLab and all of its components on Kubernetes. | When installing GitLab on Kubernetes, it has some trade-offs that you must be aware of: <br/>- Administration and troubleshooting requires Kubernetes knowledge.<br/>- It can be more expensive for smaller installations. The default installation requires more resources than a single node Linux package deployment, as most services are deployed in a redundant fashion.<br/><br/> Use this method if your infrastructure is built on Kubernetes and you're familiar with how it works. The methods for management, observability, and some concepts are different than traditional deployments. |
-| [Docker](docker.md) | The GitLab packages, Dockerized. | Use this method if you're familiar with Docker. |
-| [Source](installation.md) | Install GitLab and all of its components from scratch. | Use this method if none of the previous methods are available for your platform. Can be used for unsupported systems like \*BSD.|
-| [GitLab Environment Toolkit (GET)](https://gitlab.com/gitlab-org/gitlab-environment-toolkit#documentation) | The GitLab Environment toolkit provides a set of automation tools to deploy a [reference architecture](../administration/reference_architectures/index.md) on most major cloud providers. | Customers are very welcome to trial and evaluate GET today, however be aware of [key limitations](https://gitlab.com/gitlab-org/gitlab-environment-toolkit#missing-features-to-be-aware-of) of the current iteration. For production environments further manual setup is required based on your specific requirements. |
-| [GitLab Operator](https://docs.gitlab.com/operator/) | The GitLab Operator provides an installation and management method for GitLab following the [Kubernetes Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/). | Use the GitLab Operator to run GitLab in an [OpenShift](openshift_and_gitlab/index.md) environment. |
-
-## Install GitLab on cloud providers
-
-Regardless of the installation method, you can install GitLab on several cloud
-providers, assuming the cloud provider supports it. Here are several possible installation
-methods, the majority which use the Linux packages:
-
-| Cloud provider | Description |
-|---------------------------------------------------------------|-------------|
-| [AWS (HA)](aws/index.md) | Install GitLab on AWS using the community AMIs provided by GitLab. |
-| [Google Cloud Platform (GCP)](google_cloud_platform/index.md) | Install GitLab on a VM in GCP. |
-| [Azure](azure/index.md) | Install GitLab from Azure Marketplace. |
-
-## Next steps
-
-After you complete the steps for installing GitLab, you can
-[configure your instance](next_steps.md).
+If you already have a running instance, learn how to [configure it](next_steps.md).
diff --git a/doc/install/install_methods.md b/doc/install/install_methods.md
new file mode 100644
index 00000000000..6c62deba6fa
--- /dev/null
+++ b/doc/install/install_methods.md
@@ -0,0 +1,51 @@
+---
+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/engineering/ux/technical-writing/#assignments
+comments: false
+description: Read through the GitLab installation methods.
+type: index
+---
+
+# Installation methods
+
+You can install GitLab by using any of the following methods.
+
+| Installation method | Description | When to choose |
+|----------------------------------------------------------------|-------------|----------------|
+| [Linux package](https://docs.gitlab.com/omnibus/installation/) | The official deb/rpm packages (also known as Omnibus GitLab). The package has GitLab and dependent components, including PostgreSQL, Redis, and Sidekiq. | Use if you want the most mature, scalable method. This version is also used on GitLab.com. <br>- For additional flexibility and resilience, see the [reference architecture documentation](../administration/reference_architectures/index.md).<br>- Review the [system requirements](requirements.md).<br>- View the [list of supported Linux operating systems](../administration/package_information/supported_os.md#supported-operating-systems). |
+| [Helm chart](https://docs.gitlab.com/charts/) | A chart for installing a cloud-native version of GitLab and its components on Kubernetes. | Use if your infrastructure is on Kubernetes and you're familiar with how it works. Management, observability, and some concepts are different than traditional deployments.<br/>- Administration and troubleshooting requires Kubernetes knowledge.<br/>- It can be more expensive for smaller installations. The default installation requires more resources than a single node Linux package deployment, because most services are deployed in a redundant fashion.<br/><br/> |
+| [Docker](docker.md) | The GitLab packages in a Docker container. | Use if you're familiar with Docker. |
+| [Source](installation.md) | GitLab and its components from scratch. | Use if none of the previous methods are available for your platform. Can use for unsupported systems like \*BSD.|
+| [GitLab Environment Toolkit (GET)](https://gitlab.com/gitlab-org/gitlab-environment-toolkit#documentation) | A set of automation tools. | Use to deploy a [reference architecture](../administration/reference_architectures/index.md) on most major cloud providers. Has some [limitations](https://gitlab.com/gitlab-org/gitlab-environment-toolkit#missing-features-to-be-aware-of) and manual setup for production environments. |
+| [GitLab Operator](https://docs.gitlab.com/operator/) | An installation and management method that follows the [Kubernetes Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/). | Use to run GitLab in an [OpenShift](openshift_and_gitlab/index.md) environment. |
+
+## Cloud providers
+
+You can install GitLab on several cloud providers.
+
+| Cloud provider | Description |
+|---------------------------------------------------------------|-------------|
+| [AWS (HA)](aws/index.md) | Install GitLab on AWS using the community AMIs provided by GitLab. |
+| [Google Cloud Platform (GCP)](google_cloud_platform/index.md) | Install GitLab on a VM in GCP. |
+| [Azure](azure/index.md) | Install GitLab from Azure Marketplace. |
+
+## Unsupported Linux distributions and Unix-like operating systems
+
+- Arch Linux
+- Fedora
+- FreeBSD
+- Gentoo
+- macOS
+
+Installation of GitLab on these operating systems is possible, but not supported.
+See the [installation from source guide](installation.md) and the [installation guides](https://about.gitlab.com/install/) for more information.
+
+See [OS versions that are no longer supported](../administration/package_information/supported_os.md#os-versions-that-are-no-longer-supported) for Omnibus installs page
+for a list of supported and unsupported OS versions as well as the last support GitLab version for that OS.
+
+## Microsoft Windows
+
+GitLab is developed for Linux-based operating systems.
+It does **not** run on Microsoft Windows, and we have no plans to support it in the near future. For the latest development status, view this [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/22337).
+Consider using a virtual machine to run GitLab.
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 2f6adb06322..5982e354ae5 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -129,7 +129,7 @@ sudo apt-get install libkrb5-dev
### Git
-From GitLab 13.6, we recommend you use the
+From GitLab 13.6, we recommend you use the
[Git version provided by Gitaly](https://gitlab.com/gitlab-org/gitaly/-/issues/2729)
that:
@@ -239,7 +239,7 @@ sudo make install
GitLab has several daemons written in Go. To install
GitLab we need a Go compiler. The instructions below assume you use 64-bit
-Linux. You can find downloads for other platforms at the
+Linux. You can find downloads for other platforms at the
[Go download page](https://go.dev/dl).
```shell
diff --git a/doc/install/migrate/compare_sm_to_saas.md b/doc/install/migrate/compare_sm_to_saas.md
new file mode 100644
index 00000000000..df79987a2fa
--- /dev/null
+++ b/doc/install/migrate/compare_sm_to_saas.md
@@ -0,0 +1,124 @@
+---
+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/engineering/ux/technical-writing/#assignments
+---
+
+# Comparison of GitLab self-managed with GitLab SaaS
+
+GitLab SaaS is the largest hosted instance of GitLab in the world, managed by an
+[all-remote team](https://about.gitlab.com/company/culture/all-remote/) that knows GitLab best. With GitLab SaaS, updates, maintenance, and patches are all performed by this team.
+
+Self-managed GitLab gives you a deeper breadth of control over many of the functions and systems of the application.
+
+## Administration
+
+In GitLab SaaS, administration tasks are limited compared to a self-managed application.
+
+In a self-managed instance:
+
+- You have complete access and administrative control over the application, including the [Admin Area](../../user/admin_area/settings/index.md).
+- You can impersonate, create, add, and remove users.
+- You can assign the [`Auditor`](../../administration/auditor_users.md) user type and `External` role.
+
+On GitLab SaaS:
+
+- You have limited administrative control. For example, you cannot impersonate, create, add, or remove users.
+- You cannot access the [Admin Area](../../user/admin_area/settings/index.md).
+- You cannot assign the `Auditor` user type and `External` role.
+
+## Logs
+
+Logs give insight into your processes and can help GitLab Support maintain your application and resolve problems.
+
+In a self-managed instance:
+
+- You have full access to system logs.
+
+On GitLab SaaS:
+
+- You do not have access to system logs because they are at the instance level, and managed by the GitLab [infrastructure team](https://about.gitlab.com/handbook/engineering/infrastructure/).
+- You can view [Audit Events](../../administration/audit_events.md) and the [GitLab API](../../api/audit_events.md).
+- You must [request audit information](https://about.gitlab.com/handbook/support/workflows/log_requests.html) from the Support team.
+
+## Runners
+
+Runners are available for both SaaS and self-managed applications.
+
+In a self-managed instance, your runner availability and options are broader, but there are more [security concerns](https://docs.gitlab.com/runner/security/#security-for-self-managed-runners) to consider.
+
+On GitLab SaaS:
+
+- Private [runners](../../ci/runners/index.md) are available for GitLab SaaS [groups](../../user/group/index.md) and [projects](../../user/project/index.md).
+- Shared runners provided by GitLab SaaS are not configurable. Each runner instance is used once for only one job, ensuring any sensitive data left on the system is destroyed after the job is complete.
+- Shared runners are subject to usage limits and are [plan specific](https://about.gitlab.com/pricing/).
+
+## Custom Git hooks
+
+In a self-managed instance you can use any custom Git hooks.
+
+On GitLab SaaS:
+
+- SaaS users do not have access to the file system, and cannot use custom Git hooks.
+- You can use [webhooks](../../user/project/integrations/webhooks.md) as an alternative.
+
+## API and GraphQL
+
+In a self-managed instance, users can access all API endpoints, including those that require instance `admin` permissions.
+
+On GitLab SaaS:
+
+- SaaS users have access to all of the [API endpoints](../../api/index.md) except those that require instance `admin` permissions.
+- Only authorized GitLab engineers have administrative access.
+
+## Authentication
+
+In a self-managed instance:
+
+- You can use an internal encryption key for your data store.
+- You can view console logs.
+- You can enforce jobs on every pipeline across the group or organization.
+- You have control over your data backup.
+- You can use the [Interactive Web Terminal](../../ci/interactive_web_terminal/index.md#interactive-web-terminals) for shared runners.
+
+On GitLab SaaS:
+
+- You cannot use internal encryption key for the data store ([bring-your-own-key](https://about.gitlab.com/handbook/engineering/security/vulnerability_management/encryption-policy.html#rolling-your-own-crypto)).
+- You cannot view console logs.
+- You cannot enforce jobs on every pipeline across the group or organization.
+- You cannot configure or control data backups. You must use [group](../../api/group_import_export.md) and [project](../../api/project_import_export.md) export.
+- The [Interactive Web Terminal](../../ci/interactive_web_terminal/index.md#interactive-web-terminals) is not available for shared runners.
+
+## Public or private projects
+
+Project privacy is different when using a self-managed application or GitLab SaaS.
+
+In a self-managed instance, you control who can view your projects.
+
+On GitLab SaaS:
+
+- The GitLab SaaS instance is open to the public.
+- When your projects are set as `Public`, they are open to everyone on the public internet.
+
+## Encryption
+
+In a self-managed instance, you control the encryption type and configuration.
+
+On GitLab SaaS:
+
+- An [Access Management Process](https://about.gitlab.com/handbook/engineering/security/#access-management-process) is in place.
+- All data on GitLab.com is encrypted at rest by default. Access to encryption keys is strictly managed by GitLab.
+- GitLab does not access your tenant data except as part of a verified service request from you.
+
+## Support
+
+In a self-managed instance:
+
+- You can access any of your back-end systems.
+- Our Support team can request logs to assist you.
+
+On GitLab SaaS:
+
+- For your privacy and security, there is no public access to GitLab back-end systems.
+- Support staff work with [Site Reliability Engineers](https://about.gitlab.com/job-families/engineering/infrastructure/site-reliability-engineer/) to support the [infrastructure](https://about.gitlab.com/handbook/engineering/infrastructure/).
+- GitLab Support can access instance logs and view projects, as well as impersonate users. The Support Team can access your logs.
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 93d66dc92a9..03870897417 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -4,38 +4,9 @@ group: Distribution
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
---
-# GitLab installation minimum requirements **(FREE SELF)**
+# Installation system requirements **(FREE SELF)**
-This page includes information about both the supported operating systems and
-the minimum requirements needed to install and use GitLab.
-
-## Operating Systems
-
-### Supported Linux distributions
-
-See the [list of supported operating systems](../administration/package_information/supported_os.md#supported-operating-systems).
-
-For the installation options, see [the main installation page](index.md).
-
-### Unsupported Linux distributions and Unix-like operating systems
-
-- Arch Linux
-- Fedora
-- FreeBSD
-- Gentoo
-- macOS
-
-Installation of GitLab on these operating systems is possible, but not supported.
-See the [installation from source guide](installation.md) and the [installation guides](https://about.gitlab.com/install/) for more information.
-
-See [OS versions that are no longer supported](../administration/package_information/supported_os.md#os-versions-that-are-no-longer-supported) for Omnibus installs page
-for a list of supported and unsupported OS versions as well as the last support GitLab version for that OS.
-
-### Microsoft Windows
-
-GitLab is developed for Linux-based operating systems.
-It does **not** run on Microsoft Windows, and we have no plans to support it in the near future. For the latest development status view this [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/22337).
-Consider using a virtual machine to run GitLab.
+This page includes information about the minimum requirements you need to install and use GitLab.
## Software requirements
diff --git a/doc/integration/advanced_search/elasticsearch.md b/doc/integration/advanced_search/elasticsearch.md
index dc3dc4d2012..755dc5230e9 100644
--- a/doc/integration/advanced_search/elasticsearch.md
+++ b/doc/integration/advanced_search/elasticsearch.md
@@ -51,7 +51,7 @@ Memory, CPU, and storage resource amounts vary depending on the amount of data y
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.com has minimal CPU requirements for Elasticsearch. Multiple cores provide extra concurrency, which is more beneficial than faster CPUs.
+- [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.
## Install Elasticsearch
@@ -166,7 +166,7 @@ may need to set the `production -> elasticsearch -> indexer_path` setting in you
### View indexing errors
Errors from the [GitLab Elasticsearch Indexer](https://gitlab.com/gitlab-org/gitlab-elasticsearch-indexer) are reported in
-the [`sidekiq.log`](../../administration/logs/index.md#sidekiqlog) file with a `json.exception.class` of `Gitlab::Elastic::Indexer::Error`.
+the [`elasticsearch.log`](../../administration/logs/index.md#elasticsearchlog) file and the [`sidekiq.log`](../../administration/logs/index.md#sidekiqlog) file with a `json.exception.class` of `Gitlab::Elastic::Indexer::Error`.
These errors may occur when indexing Git repository data.
## Enable Advanced Search
@@ -175,7 +175,7 @@ For GitLab instances with more than 50GB repository data you can follow the inst
To enable Advanced Search, you must have administrator access to GitLab:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Advanced Search**.
NOTE:
@@ -223,7 +223,7 @@ The following Elasticsearch settings are available:
| `Number of Elasticsearch shards` | Elasticsearch indices are split into multiple shards for performance reasons. In general, you should use at least 5 shards, and indices with tens of millions of documents need to have more shards ([see below](#guidance-on-choosing-optimal-cluster-configuration)). Changes to this value do not take effect until the index is recreated. You can read more about tradeoffs in the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/scalability.html). |
| `Number of Elasticsearch replicas` | Each Elasticsearch shard can have a number of replicas. These are a complete copy of the shard, and can provide increased query performance or resilience against hardware failure. Increasing this value increases total disk space required by the index. |
| `Limit the number of namespaces and projects that can be indexed` | Enabling this allows you to select namespaces and projects to index. All other namespaces and projects use database search instead. If you enable this option but do not select any namespaces or projects, none are indexed. [Read more below](#limit-the-number-of-namespaces-and-projects-that-can-be-indexed).|
-| `Using AWS hosted Elasticsearch with IAM credentials` | Sign your Elasticsearch requests using [AWS IAM authorization](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html), [AWS EC2 Instance Profile Credentials](https://docs.aws.amazon.com/codedeploy/latest/userguide/getting-started-create-iam-instance-profile.html#getting-started-create-iam-instance-profile-cli), or [AWS ECS Tasks Credentials](https://docs.aws.amazon.com/AmazonECS/latest/userguide/task-iam-roles.html). Please refer to [Identity and Access Management in Amazon OpenSearch Service](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/ac.html) for details of AWS hosted OpenSearch domain access policy configuration. |
+| `Using AWS OpenSearch Service with IAM credentials` | Sign your OpenSearch requests using [AWS IAM authorization](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html), [AWS EC2 Instance Profile Credentials](https://docs.aws.amazon.com/codedeploy/latest/userguide/getting-started-create-iam-instance-profile.html#getting-started-create-iam-instance-profile-cli), or [AWS ECS Tasks Credentials](https://docs.aws.amazon.com/AmazonECS/latest/userguide/task-iam-roles.html). Please refer to [Identity and Access Management in Amazon OpenSearch Service](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/ac.html) for details of AWS hosted OpenSearch domain access policy configuration. |
| `AWS Region` | The AWS region in which your OpenSearch Service is located. |
| `AWS Access Key` | The AWS access key. |
| `AWS Secret Access Key` | The AWS secret access key. |
@@ -271,7 +271,7 @@ You can improve the language support for Chinese and Japanese languages by utili
To enable languages support:
1. Install the desired plugins, please refer to [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/plugins/7.9/installation.html) for plugins installation instructions. The plugins must be installed on every node in the cluster, and each node must be restarted after installation. For a list of plugins, see the table later in this section.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Advanced Search**.
1. Locate **Custom analyzers: language support**.
1. Enable plugins support for **Indexing**.
@@ -292,7 +292,7 @@ For guidance on what to install, see the following Elasticsearch language plugin
To disable the Elasticsearch integration:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Advanced Search**.
1. Uncheck **Elasticsearch indexing** and **Search with Elasticsearch enabled**.
1. Select **Save changes**.
@@ -308,7 +308,7 @@ To disable the Elasticsearch integration:
## Unpause Indexing
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Advanced Search**.
1. Expand **Advanced Search**.
1. Clear the **Pause Elasticsearch indexing** checkbox.
@@ -331,7 +331,7 @@ index alias to it which becomes the new `primary` index. At the end, we resume t
To trigger the reindexing process:
1. Sign in to your GitLab instance as an administrator.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Advanced Search**.
1. Expand **Elasticsearch zero-downtime reindexing**.
1. Select **Trigger cluster reindexing**.
@@ -348,7 +348,7 @@ While the reindexing is running, you can follow its progress under that same sec
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55681) in GitLab 13.12.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Advanced Search**.
1. Expand **Elasticsearch zero-downtime reindexing**, and you'll
find the following options:
@@ -396,7 +396,7 @@ Sometimes, you might want to abandon the unfinished reindex job and resume the i
bundle exec rake gitlab:elastic:mark_reindex_failed RAILS_ENV=production
```
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Advanced Search**.
1. Expand **Advanced Search**.
1. Clear the **Pause Elasticsearch indexing** checkbox.
@@ -562,7 +562,7 @@ For basic guidance on choosing a cluster configuration you may refer to [Elastic
- A good guideline is to ensure you keep the number of shards per node below 20 per GB heap it has configured. A node with a 30GB heap should therefore have a maximum of 600 shards, but the further below this limit you can keep it the better. This generally helps the cluster stay in good health.
- Number of Elasticsearch shards:
- Small shards result in small segments, which increases overhead. Aim to keep the average shard size between at least a few GB and a few tens of GB.
- - Another consideration is the number of documents. To determine the number of shards to use, sum the numbers in the **Menu > Admin > Dashboard > Statistics** pane (the number of documents to be indexed), divide by 5 million, and add 5. For example:
+ - Another consideration is the number of documents. To determine the number of shards to use, sum the numbers in the **Main menu > Admin > Dashboard > Statistics** pane (the number of documents to be indexed), divide by 5 million, and add 5. For example:
- If you have fewer than about 2,000,000 documents, use the default of 5 shards
- 10,000,000 documents: `10000000/5000000 + 5` = 7 shards
- 100,000,000 documents: `100000000/5000000 + 5` = 25 shards
@@ -639,7 +639,7 @@ Make sure to prepare for this task by having a
```
This enqueues a Sidekiq job for each project that needs to be indexed.
- You can view the jobs in **Menu > Admin > Monitoring > Background Jobs > Queues Tab**
+ You can view the jobs in **Main menu > Admin > Monitoring > Background Jobs > Queues Tab**
and select `elastic_commit_indexer`, or you can query indexing status using a Rake task:
```shell
diff --git a/doc/integration/advanced_search/elasticsearch_troubleshooting.md b/doc/integration/advanced_search/elasticsearch_troubleshooting.md
index fb558441d6a..e1a566541c2 100644
--- a/doc/integration/advanced_search/elasticsearch_troubleshooting.md
+++ b/doc/integration/advanced_search/elasticsearch_troubleshooting.md
@@ -5,10 +5,68 @@ group: Global Search
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
---
-# Elasticsearch troubleshooting **(PREMIUM SELF)**
+# Troubleshooting Elasticsearch **(PREMIUM SELF)**
Use the following information to troubleshoot Elasticsearch issues.
+## Set configurations in the Rails console
+
+See [Starting a Rails console session](../../administration/operations/rails_console.md#starting-a-rails-console-session).
+
+### List attributes
+
+To list all available attributes:
+
+1. Open the Rails console (`gitlab rails c`).
+1. Run the following command:
+
+```ruby
+ApplicationSetting.last.attributes
+```
+
+The output contains all the settings available in [Elasticsearch integration](../../integration/advanced_search/elasticsearch.md), such as `elasticsearch_indexing`, `elasticsearch_url`, `elasticsearch_replicas`, and `elasticsearch_pause_indexing`.
+
+### Set attributes
+
+To set an Elasticsearch integration setting, run a command like:
+
+```ruby
+ApplicationSetting.last.update(elasticsearch_url: '<your ES URL and port>')
+
+#or
+
+ApplicationSetting.last.update(elasticsearch_indexing: false)
+```
+
+### Get attributes
+
+To check if the settings have been set in [Elasticsearch integration](../../integration/advanced_search/elasticsearch.md) or in the Rails console, run a command like:
+
+```ruby
+Gitlab::CurrentSettings.elasticsearch_url
+
+#or
+
+Gitlab::CurrentSettings.elasticsearch_indexing
+```
+
+### Change the password
+
+To change the Elasticsearch password, run the following commands:
+
+```ruby
+es_url = Gitlab::CurrentSettings.current_application_settings
+
+# Confirm the current Elasticsearch URL
+es_url.elasticsearch_url
+
+# Set the Elasticsearch URL
+es_url.elasticsearch_url = "http://<username>:<password>@your.es.host:<port>"
+
+# Save the change
+es_url.save!
+```
+
## View logs
One of the most valuable tools for identifying issues with the Elasticsearch
@@ -58,8 +116,9 @@ There are a couple of ways to achieve that:
::Gitlab::CurrentSettings.elasticsearch_limit_indexing? # Whether or not Elasticsearch is limited only to certain projects/namespaces
```
-- Confirm searches use Elasticsearch by accessing the [rails console]
- (../../administration/operations/rails_console.md) and running the following commands:
+- Confirm searches use Elasticsearch by accessing the
+ [rails console](../../administration/operations/rails_console.md) and running the following
+ commands:
```rails
u = User.find_by_email('email_of_user_doing_search')
diff --git a/doc/integration/akismet.md b/doc/integration/akismet.md
index 9a85511836f..a2b70d42bb6 100644
--- a/doc/integration/akismet.md
+++ b/doc/integration/akismet.md
@@ -1,6 +1,6 @@
---
-stage: Ecosystem
-group: Integrations
+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/engineering/ux/technical-writing/#assignments
---
@@ -30,7 +30,7 @@ To use Akismet:
1. Sign in or create a new account.
1. Select **Show** to reveal the API key, and copy the API key's value.
1. Sign in to GitLab as an administrator.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Reporting** (`/admin/application_settings/reporting`).
1. Select the **Enable Akismet** checkbox.
1. Fill in the API key from step 3.
diff --git a/doc/integration/cas.md b/doc/integration/cas.md
index 38305967246..45c79cd9726 100644
--- a/doc/integration/cas.md
+++ b/doc/integration/cas.md
@@ -71,8 +71,8 @@ configure CAS for back-channel logout.
1. Save the configuration file.
-1. [Reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) or
- [restart GitLab](../administration/restart_gitlab.md#installations-from-source) for the changes to
- take effect if you installed GitLab via Omnibus or from source respectively.
+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).
On the sign in page there should now be a CAS tab in the sign in form.
diff --git a/doc/integration/datadog.md b/doc/integration/datadog.md
index b8624545c41..42337006189 100644
--- a/doc/integration/datadog.md
+++ b/doc/integration/datadog.md
@@ -27,7 +27,7 @@ project, group, or instance level:
1. *For project-level or group-level integrations:* In GitLab, go to your project or group.
1. *For instance-level integrations:*
1. Sign in to GitLab as a user with administrator access.
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Integrations**.
1. Scroll to **Add an integration**, and select **Datadog**.
1. Select **Active** to enable the integration.
diff --git a/doc/integration/ding_talk.md b/doc/integration/ding_talk.md
index 437648b1adf..71dadd766b2 100644
--- a/doc/integration/ding_talk.md
+++ b/doc/integration/ding_talk.md
@@ -83,4 +83,6 @@ Sign in to DingTalk Open Platform and create an application on it. DingTalk gene
1. Save the configuration file.
-1. [Reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../administration/restart_gitlab.md#installations-from-source) for the changes to take effect if you installed GitLab via Omnibus or from source respectively.
+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).
diff --git a/doc/integration/facebook.md b/doc/integration/facebook.md
index 99ebce32936..ea5a3cc6d38 100644
--- a/doc/integration/facebook.md
+++ b/doc/integration/facebook.md
@@ -104,8 +104,9 @@ Facebook. Facebook generates an app ID and secret key for you to use.
1. Save the configuration file.
-1. [Reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../administration/restart_gitlab.md#installations-from-source) for the changes to take effect if you
- installed GitLab via Omnibus or from source respectively.
+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).
On the sign in page there should now be a Facebook icon below the regular sign
in form. Select the icon to begin the authentication process. Facebook asks the
diff --git a/doc/integration/gitpod.md b/doc/integration/gitpod.md
index 271505d3d6f..c2b27e79d6e 100644
--- a/doc/integration/gitpod.md
+++ b/doc/integration/gitpod.md
@@ -47,7 +47,7 @@ For GitLab self-managed instances, a GitLab administrator needs to:
1. Set up a Gitpod instance to integrate with GitLab. Refer to the [Gitpod documentation](https://www.gitpod.io/docs/self-hosted/latest)
to get your instance up and running.
1. Enable it in GitLab:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Gitpod** configuration section.
1. Select the **Enable Gitpod integration** checkbox.
diff --git a/doc/integration/google.md b/doc/integration/google.md
index 4862b898ed5..80176fac41b 100644
--- a/doc/integration/google.md
+++ b/doc/integration/google.md
@@ -117,10 +117,9 @@ On your GitLab server:
```
1. Save the configuration file.
-1. [Reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure)
- or [restart GitLab](../administration/restart_gitlab.md#installations-from-source) for
- the changes to take effect if you installed GitLab via Omnibus or from source
- respectively.
+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).
On the sign in page there should now be a Google icon below the regular sign in
form. Select the icon to begin the authentication process. Google asks the
diff --git a/doc/integration/jenkins.md b/doc/integration/jenkins.md
index 5b779f22bd3..0a4c2c27c31 100644
--- a/doc/integration/jenkins.md
+++ b/doc/integration/jenkins.md
@@ -128,7 +128,7 @@ Configure the GitLab integration with Jenkins in one of the following ways.
GitLab recommends this approach for Jenkins integrations because it is easier to configure
than the [webhook integration](#configure-a-webhook).
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **Jenkins**.
1. Select the **Active** checkbox.
@@ -177,7 +177,7 @@ If you get this error message while configuring GitLab, the following are possib
- GitLab is unable to reach your Jenkins instance at the address. If your GitLab instance is self-managed, try pinging the
Jenkins instance at the domain provided on the GitLab instance.
- The Jenkins instance is at a local address and is not included in the
- [GitLab installation's allowlist](../security/webhooks.md#allowlist-for-local-requests).
+ [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).
diff --git a/doc/integration/jira/configure.md b/doc/integration/jira/configure.md
index bfeac230f89..58789afff46 100644
--- a/doc/integration/jira/configure.md
+++ b/doc/integration/jira/configure.md
@@ -22,7 +22,7 @@ Prerequisites:
To configure your project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **Jira**.
1. Select **Enable integration**.
diff --git a/doc/integration/jira/development_panel.md b/doc/integration/jira/development_panel.md
index 4d5a9f92257..d52d86c5658 100644
--- a/doc/integration/jira/development_panel.md
+++ b/doc/integration/jira/development_panel.md
@@ -38,7 +38,7 @@ After the integration is [set up on GitLab and Jira](#configure-the-integration)
- Refer to any Jira issue by its ID (in uppercase) in GitLab branch names,
commit messages, and merge request titles.
- See the linked branches, commits, and merge requests in Jira issues.
-- Create GitLab branches from Jira Cloud issues ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66032) in GitLab 14.2).
+- Create GitLab branches from Jira Cloud issues ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66032) in GitLab 14.2 for the GitLab for Jira app).
At this time, merge requests are called "pull requests" in Jira issues.
This name may change in a future Jira release.
@@ -85,9 +85,10 @@ documentation for [central administration of project integrations](../../user/ad
## Limitations
-This integration is not supported on GitLab instances under a
-[relative URL](https://docs.gitlab.com/omnibus/settings/configuration.html#configuring-a-relative-url-for-gitlab).
-For example, `http://example.com/gitlab`.
+- This integration is not supported on GitLab instances under a
+[relative URL](https://docs.gitlab.com/omnibus/settings/configuration.html#configure-a-relative-url-for-gitlab)
+(for example, `http://example.com/gitlab`).
+- [Creating a branch](https://gitlab.com/gitlab-org/gitlab/-/issues/2647) is only supported by the GitLab for Jira app and is not available within the DVCS integration. See [officially supported DVCS features](https://confluence.atlassian.com/adminjiraserver/integrating-with-development-tools-938846890.html) for more information.
## Troubleshoot the development panel
diff --git a/doc/integration/jira/dvcs.md b/doc/integration/jira/dvcs.md
index 43a5349e0e5..ce097a4db23 100644
--- a/doc/integration/jira/dvcs.md
+++ b/doc/integration/jira/dvcs.md
@@ -174,7 +174,7 @@ Error obtaining access token. Cannot access https://gitlab.example.com from Jira
- The [GitLab Jira integration](index.md) requires
GitLab to connect to Jira. Any TLS issues that arise from a private certificate
authority or self-signed certificate are resolved
- [on the GitLab server](https://docs.gitlab.com/omnibus/settings/ssl.html#other-certificate-authorities),
+ [on the GitLab server](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates),
as GitLab is the TLS client.
- The Jira Development panel integration requires Jira to connect to GitLab, which
causes Jira to be the TLS client. If your GitLab server's certificate is not
@@ -277,7 +277,7 @@ In the example above, the merge requests feature is disabled.
To resolve the issue, enable the relevant feature:
-1. On the top bar, select **Menu > Projects** and find your 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 toggles to enable the features as needed.
diff --git a/doc/integration/jira/issues.md b/doc/integration/jira/issues.md
index 2d9e928e654..98dd4526fd9 100644
--- a/doc/integration/jira/issues.md
+++ b/doc/integration/jira/issues.md
@@ -54,10 +54,9 @@ You can [disable comments](#disable-comments-on-jira-issues) on issues.
You can prevent merge requests from being merged if they do not refer to a Jira issue.
To enforce this:
-1. On the top bar, select **Menu > Projects** and find your project.
-1. On the left sidebar, select **Settings > General**.
-1. Expand **Merge requests**.
-1. Under **Merge checks**, select the **Require an associated issue from Jira** checkbox.
+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 checks** section, select **Require an associated issue from Jira**.
1. Select **Save**.
After you enable this feature, a merge request that doesn't reference an associated
diff --git a/doc/integration/kerberos.md b/doc/integration/kerberos.md
index 257ba4e6708..5c9af96ebe8 100644
--- a/doc/integration/kerberos.md
+++ b/doc/integration/kerberos.md
@@ -110,13 +110,15 @@ set up GitLab to create a new account when a Kerberos user tries to sign in.
### Link a Kerberos account to an existing GitLab account
+> Kerberos SPNEGO [renamed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96335) to Kerberos in GitLab 15.4.
+
If you're an administrator, you can link a Kerberos account to an
existing GitLab account. To do so:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Select a user, then select the **Identities** tab.
-1. Select 'Kerberos SPNEGO' in the 'Provider' dropdown box.
+1. From the **Provider** dropdown list, select **Kerberos**.
1. Make sure the **Identifier** corresponds to the Kerberos username.
1. Select **Save changes**.
@@ -125,7 +127,7 @@ If you're not an administrator:
1. In the top-right corner, select your avatar.
1. Select **Edit profile**.
1. On the left sidebar, select **Account**.
-1. In the **Service sign-in** section, select **Connect Kerberos SPNEGO**.
+1. In the **Service sign-in** section, select **Connect Kerberos**.
If you don't see a **Service sign-in** Kerberos option, follow the
requirements in [Enable single sign-on](#enable-single-sign-on).
@@ -153,7 +155,7 @@ With that information at hand:
```
1. As an administrator, you can confirm the new, blocked account:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users** and review the **Blocked** tab.
1. You can enable the user.
1. If `block_auto_created_users` is false, the Kerberos user is
@@ -305,15 +307,12 @@ We [deprecated](../update/deprecations.md#omniauth-kerberos-gem) password-based
Kerberos sign-ins in GitLab 14.3 and [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/2908)
it in GitLab 15.0. You must switch to ticket-based sign in.
-Depending on your existing GitLab configuration, the 'Sign in with:
-Kerberos SPNEGO' button may already be visible on your GitLab sign-in
-page. If not, then add the settings [described above](#configuration).
+Depending on your existing GitLab configuration, **Sign in with:
+Kerberos** may already be visible on your GitLab sign-in page.
+If not, then add the settings [described above](#configuration).
-Once you have verified that the 'Kerberos SPNEGO' button works
-without entering any passwords, you can proceed to disable
-password-based Kerberos sign-ins. To do this you need only need to
-remove the OmniAuth provider named `kerberos` from your `gitlab.yml` /
-`gitlab.rb` file.
+To disable password-based Kerberos sign-ins, remove the OmniAuth provider
+`kerberos` from your `gitlab.yml`/`gitlab.rb` file.
**For installations from source**
@@ -365,9 +364,18 @@ mechanisms it supports to GitLab. If it doesn't support any of the mechanisms
GitLab supports, authentication fails with a message like this in the log:
```plaintext
-OmniauthKerberosSpnegoController: failed to process Negotiate/Kerberos authentication: gss_accept_sec_context did not return GSS_S_COMPLETE: An unsupported mechanism was requested Unknown error
+OmniauthKerberosController: failed to process Negotiate/Kerberos authentication: gss_accept_sec_context did not return GSS_S_COMPLETE: An unsupported mechanism was requested Unknown error
```
+There are a number of potential causes and solutions for this error message.
+
+#### Kerberos integration not using a dedicated port
+
+GitLab CI/CD doesn’t work with a Kerberos-enabled GitLab instance unless the Kerberos integration
+is configured to [use a dedicated port](kerberos.md#http-git-access-with-kerberos-token-passwordless-authentication).
+
+#### Lack of connectivity between client machine and Kerberos server
+
This is usually seen when the browser is unable to contact the Kerberos server
directly. It falls back to an unsupported mechanism known as
[`IAKERB`](https://k5wiki.kerberos.org/wiki/Projects/IAKERB), which tries to use
@@ -377,6 +385,8 @@ If you're experiencing this error, ensure there is connectivity between the
client machine and the Kerberos server - this is a prerequisite! Traffic may be
blocked by a firewall, or the DNS records may be incorrect.
+#### Mismatched forward and reverse DNS records for GitLab instance hostname
+
Another failure mode occurs when the forward and reverse DNS records for the
GitLab server do not match. Often, Windows clients work in this case while
Linux clients fail. They use reverse DNS while detecting the Kerberos
@@ -389,6 +399,8 @@ match. So for instance, if you access GitLab as `gitlab.example.com`, resolving
to IP address `1.2.3.4`, then `4.3.2.1.in-addr.arpa` must be a `PTR` record for
`gitlab.example.com`.
+#### Missing Kerberos libraries on browser or client machine
+
Finally, it's possible that the browser or client machine lack Kerberos support
completely. Ensure that the Kerberos libraries are installed and that you can
authenticate to other Kerberos services.
diff --git a/doc/integration/mattermost/index.md b/doc/integration/mattermost/index.md
index 3293732b59b..1e57e45aef3 100644
--- a/doc/integration/mattermost/index.md
+++ b/doc/integration/mattermost/index.md
@@ -79,7 +79,7 @@ mattermost_nginx['ssl_certificate'] = "/etc/gitlab/ssl/mattermost-nginx.crt"
mattermost_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/mattermost-nginx.key"
```
-where `mattermost-nginx.crt` and `mattermost-nginx.key` are SSL cert and key, respectively.
+where `mattermost-nginx.crt` is the SSL certificate and `mattermost-nginx.key` is the SSL key.
Once the configuration is set, run `sudo gitlab-ctl reconfigure` to apply the changes.
@@ -140,7 +140,7 @@ mattermost['gitlab_user_api_endpoint'] = "http://gitlab.example.com/api/v4/user"
Save the changes and then run `sudo gitlab-ctl reconfigure`. If there are no errors your GitLab and GitLab Mattermost should be configured correctly.
-### Specify numeric user and group identifiers
+## Specify numeric user and group identifiers
Omnibus GitLab creates a user and group `mattermost`. You can specify the
numeric identifiers for these users in `/etc/gitlab/gitlab.rb` as follows:
@@ -152,7 +152,7 @@ mattermost['gid'] = 1234
Run `sudo gitlab-ctl reconfigure` to apply the changes.
-### Setting custom environment variables
+## Setting custom environment variables
If necessary you can set custom environment variables to be used by Mattermost
via `/etc/gitlab/gitlab.rb`. This can be useful if the Mattermost server
@@ -165,7 +165,7 @@ mattermost['env'] = {"HTTP_PROXY" => "my_proxy", "HTTPS_PROXY" => "my_proxy", "N
Run `sudo gitlab-ctl reconfigure` to apply the changes.
-### Connecting to the bundled PostgreSQL database
+## Connecting to the bundled PostgreSQL database
If you need to connect to the bundled PostgreSQL database and are using the default Omnibus GitLab database configuration, you can connect as
the PostgreSQL superuser:
@@ -174,14 +174,14 @@ the PostgreSQL superuser:
sudo gitlab-psql -d mattermost_production
```
-### Back up GitLab Mattermost
+## Back up GitLab Mattermost
GitLab Mattermost is not included in the regular [Omnibus GitLab backup](../../raketasks/backup_restore.md) Rake task.
The general Mattermost [backup and disaster recovery](https://docs.mattermost.com/deploy/backup-disaster-recovery.html) documentation can be used as a guide
on what needs to be backed up.
-#### Back up the bundled PostgreSQL database
+### Back up the bundled PostgreSQL database
If you need to back up the bundled PostgreSQL database and are using the default Omnibus GitLab database configuration, you can back up using this command:
@@ -189,7 +189,7 @@ If you need to back up the bundled PostgreSQL database and are using the default
sudo -i -u gitlab-psql -- /opt/gitlab/embedded/bin/pg_dump -h /var/opt/gitlab/postgresql mattermost_production | gzip > mattermost_dbdump_$(date --rfc-3339=date).sql.gz
```
-#### Back up the `data` directory and `config.json`
+### Back up the `data` directory and `config.json`
Mattermost has a `data` directory and `config.json` file that need to be backed up as well:
@@ -197,7 +197,7 @@ Mattermost has a `data` directory and `config.json` file that need to be backed
sudo tar -zcvf mattermost_data_$(date --rfc-3339=date).gz -C /var/opt/gitlab/mattermost data config.json
```
-### Restore GitLab Mattermost
+## Restore GitLab Mattermost
If you have previously [created a backup of GitLab Mattermost](#back-up-gitlab-mattermost), you can run the following commands to restore it:
@@ -227,11 +227,11 @@ sudo chown mattermost:mattermost /var/opt/gitlab/mattermost/config.json
sudo gitlab-ctl start mattermost
```
-### Mattermost Command Line Tools (CLI)
+## Mattermost Command Line Tools (CLI)
[`mmctl`](https://docs.mattermost.com/manage/mmctl-command-line-tool.html) is a CLI tool for the Mattermost server which is installed locally and uses the Mattermost API, but may also be used remotely. You must configure Mattermost either for local connections or authenticate as an administrator with local login credentials (not through GitLab SSO). The executable is located at `/opt/gitlab/embedded/bin/mmctl`.
-#### Use `mmctl` through a local connection
+### Use `mmctl` through a local connection
For local connections, the `mmctl` binary and Mattermost must be run from the same server. To enable the local socket:
@@ -269,7 +269,7 @@ wd3g5zpepjgbfjgpdjaas7yj6a: feedbackbot (feedbackbot@localhost)
There are 4 users on local instance
```
-#### Use `mmctl` through a remote connection
+### Use `mmctl` through a remote connection
For remote connections or local connections where the socket cannot be used,
create a non SSO user and give that user admin privileges. Those credentials
@@ -522,7 +522,6 @@ sequenceDiagram
For help and support around your GitLab Mattermost deployment please see:
-- [Troubleshooting Forum](https://forum.mattermost.com/t/how-to-use-the-troubleshooting-forum/150) for configuration questions and issues.
-- [Troubleshooting FAQ](https://docs.mattermost.com/install/troubleshooting.html).
+- [Troubleshooting Mattermost issues](https://docs.mattermost.com/install/troubleshooting.html).
- [Mattermost GitLab Issues Support Handbook](https://docs.mattermost.com/process/support.html?highlight=omnibus#gitlab-issues).
- [GitLab Mattermost issue tracker](https://gitlab.com/gitlab-org/gitlab-mattermost/-/issues) for verified bugs with repro steps.
diff --git a/doc/integration/oauth_provider.md b/doc/integration/oauth_provider.md
index 962f5c4e5fb..21184f7b678 100644
--- a/doc/integration/oauth_provider.md
+++ b/doc/integration/oauth_provider.md
@@ -79,7 +79,7 @@ To add a new application for a group:
To create an application for your GitLab instance:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Applications**.
1. Select **New application**.
@@ -103,8 +103,11 @@ When applications are deleted, all grants and tokens associated with the applica
## Authorized applications
-Every application you authorize with your GitLab credentials is shown
-in the **Authorized applications** section under **Settings > Applications**.
+To see all the application you've authorized with your GitLab credentials:
+
+1. On the top bar, in the top right corner, select your avatar.
+1. Select **Edit profile** and then select **Applications**.
+1. Scroll down to the **Authorized applications** section.
The GitLab OAuth 2 applications support scopes, which allow various actions that any given
application can perform. Available scopes are depicted in the following table.
@@ -124,3 +127,13 @@ application can perform. Available scopes are depicted in the following table.
| `email` | Grants read-only access to the user's primary email address using [OpenID Connect](openid_connect_provider.md). |
At any time you can revoke any access by clicking **Revoke**.
+
+## Hashed OAuth application secrets
+
+> Introduced in GitLab 15.4 [with a flag](../administration/feature_flags.md) named `hash_oauth_secrets`. 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 `hash_oauth_secrets`.
+On GitLab.com, this feature is not available.
+
+By default, OAuth application secrets are stored as plain text in the database. When enabled, OAuth application secrets are stored in the database in hashed format and are only available to users immediately after creating OAuth applications.
diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md
index e297c13a2da..0dfc78b508b 100644
--- a/doc/integration/omniauth.md
+++ b/doc/integration/omniauth.md
@@ -46,7 +46,7 @@ configure the settings that are common for all providers.
Setting | Description | Default value
---------------------------|-------------|--------------
`allow_single_sign_on` | Enables you to list the providers that automatically create a GitLab account. The provider names are available in the **OmniAuth provider name** column in the [supported providers table](#supported-providers). | The default is `false`. If `false`, users must be created manually, or they can't sign in using OmniAuth.
-`auto_link_ldap_user` | If enabled, creates an LDAP identity in GitLab for users that are created through an OmniAuth provider. You can enable this setting if you have the [LDAP (ActiveDirectory)](../administration/auth/ldap/index.md) integration enabled. Requires the `uid` of the user to be the same in both LDAP and the OmniAuth provider. | The default is `false`.
+`auto_link_ldap_user` | If enabled, 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. | The default is `false`.
`block_auto_created_users` | If enabled, blocks users that are automatically created from signing in until they are approved by an administrator. | The default is `true`. If you set the value to `false`, make sure you only define providers for `allow_single_sign_on` 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:
@@ -195,7 +195,7 @@ By default, sign-in is enabled for all the OAuth providers configured in `config
To enable or disable an OmniAuth provider:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings**.
1. Expand **Sign-in restrictions**.
1. In the **Enabled OAuth authentication sources** section, select or clear the checkbox for each provider you want to enable or disable.
@@ -437,6 +437,34 @@ then override the icon in one of two ways:
}
```
+## Change apps or configuration
+
+Because GitLab doesn't support having multiple providers in OAuth, GitLab configuration and user identification must be
+updated at the same time if the provider or app is changed.
+
+These instructions apply to all methods of authentication where GitLab stores an `extern_uid` and it is the only data used
+for user authentication.
+
+When changing apps within a provider, if the user `extern_uid` does not change, only the GitLab configuration must be
+updated.
+
+To swap configurations:
+
+1. Change provider configuration in your `gitlab.rb` file.
+1. Update `extern_uid` for all users that have an identity in GitLab for the previous provider.
+
+To find the `extern_uid`, look at an existing user's current `extern_uid` for an ID that matches the appropriate field in
+your current provider for the same user.
+
+There are two methods to update the `extern_uid`:
+
+- Using the [Users API](../api/users.md#user-modification). Pass the provider name and the new `extern_uid`.
+- Using the [Rails console](../administration/operations/rails_console.md):
+
+ ```ruby
+ Identity.where(extern_uid: 'old-id').update!(extern_uid: 'new-id')`
+ ```
+
## Limitations
Most supported OmniAuth providers don't support Git over HTTP password authentication.
diff --git a/doc/integration/recaptcha.md b/doc/integration/recaptcha.md
index 4963dea19a4..a5fd8db63bd 100644
--- a/doc/integration/recaptcha.md
+++ b/doc/integration/recaptcha.md
@@ -1,6 +1,6 @@
---
-stage: Ecosystem
-group: Integrations
+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/engineering/ux/technical-writing/#assignments
---
@@ -17,7 +17,7 @@ To use reCAPTCHA, first create a site and private key.
1. Go to the [Google reCAPTCHA page](https://www.google.com/recaptcha/admin).
1. To get reCAPTCHA v2 keys, fill in the form and select **Submit**.
1. Sign in to your GitLab server as an administrator.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Reporting** (`admin/application_settings/reporting`).
1. Expand **Spam and Anti-bot Protection**.
1. In the reCAPTCHA fields, enter the keys you obtained in the previous steps.
diff --git a/doc/integration/salesforce.md b/doc/integration/salesforce.md
index 5300018e888..70d6e0aa0d8 100644
--- a/doc/integration/salesforce.md
+++ b/doc/integration/salesforce.md
@@ -81,9 +81,10 @@ To get the credentials (a pair of Client ID and Client Secret), you must [create
![Salesforce App Secret Details](img/salesforce_app_secret_details.png)
1. Save the configuration file.
-1. [Reconfigure GitLab](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) or
- [restart GitLab](../administration/restart_gitlab.md#installations-from-source) for the changes
- to take effect if you installed GitLab via Omnibus or from source respectively.
+
+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).
On the sign in page, there should now be a Salesforce icon below the regular sign in form.
Select the icon to begin the authentication process. Salesforce asks the user to sign in and authorize the GitLab application.
diff --git a/doc/integration/saml.md b/doc/integration/saml.md
index 0c517d07f41..ef31f276025 100644
--- a/doc/integration/saml.md
+++ b/doc/integration/saml.md
@@ -143,7 +143,9 @@ as described in the section on [Security](#security). Otherwise, your users are
1. Change the value of `issuer` to a unique name, which identifies the application
to the IdP.
-1. For the changes to take effect, you must [reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) GitLab if you installed via Omnibus or [restart GitLab](../administration/restart_gitlab.md#installations-from-source) if you installed from source.
+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. Register the GitLab SP in your SAML 2.0 IdP, using the application name specified
in `issuer`.
@@ -169,7 +171,8 @@ is returned to GitLab and signed in.
You can configure GitLab to use multiple SAML identity providers if:
-- Each provider has a unique name set that matches a name set in `args`.
+- 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`.
@@ -182,9 +185,9 @@ Example multiple providers configuration for Omnibus GitLab:
```ruby
gitlab_rails['omniauth_providers'] = [
{
- name: 'saml_1',
+ name: 'saml',
args: {
- name: 'saml_1', # This is mandatory and must match the provider name
+ 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
@@ -192,9 +195,9 @@ gitlab_rails['omniauth_providers'] = [
label: 'Provider 1' # Differentiate the two buttons and providers in the UI
},
{
- name: 'saml_2',
+ name: 'saml1',
args: {
- name: 'saml_2', # This is mandatory and must match the provider name
+ 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
@@ -210,9 +213,9 @@ Example providers configuration for installations from source:
omniauth:
providers:
- {
- name: 'saml_1',
+ name: 'saml',
args: {
- name: 'saml_1', # This is mandatory and must match the provider name
+ 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
@@ -220,9 +223,9 @@ omniauth:
label: 'Provider 1' # Differentiate the two buttons and providers in the UI
}
- {
- name: 'saml_2',
+ name: 'saml1',
args: {
- name: 'saml_2', # This is mandatory and must match the provider name
+ 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
@@ -800,8 +803,8 @@ If you have any questions on configuring the SAML app, please contact your provi
### Okta setup notes
-1. In the Okta administrator section, make sure to select Classic UI view in the top left corner. From there, choose to **Add an App**.
-1. When the app screen comes up you see another button to **Create an App** and
+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
@@ -859,117 +862,4 @@ connect to the Google Workspace SAML app.
## Troubleshooting
-### SAML Response
-
-You can find the base64-encoded SAML Response in the [`production_json.log`](../administration/logs/index.md#production_jsonlog). This response is sent from the IdP, and contains user information that is consumed by GitLab. Many errors in the SAML integration can be solved by decoding this response and comparing it to the SAML settings in the GitLab configuration file.
-
-### GitLab+SAML Testing Environments
-
-To troubleshoot, [a complete GitLab+SAML testing environment using Docker compose](https://gitlab.com/gitlab-com/support/toolbox/replication/tree/master/compose_files)
-is available.
-
-If you only require a SAML provider for testing, 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 (IdP) is available.
-
-### 500 error after login
-
-If you see a "500 error" in GitLab when you are redirected back from the SAML
-sign-in page, this could indicate that:
-
-- GitLab couldn't get the email address for the SAML user. Ensure the IdP provides a claim containing the user's
- email address using the claim name `email` or `mail`.
-- The certificate set your `gitlab.rb` file for `idp_cert_fingerprint` or `idp_cert` file is incorrect.
-- Your `gitlab.rb` file is set to enable `idp_cert_fingerprint`, and `idp_cert` is being provided, or the reverse.
-
-### 422 error after login
-
-If you see a "422 error" in GitLab when you are redirected from the SAML
-sign-in page, you might have an incorrectly configured Assertion Consumer
-Service (ACS) URL on the identity provider.
-
-Make sure the ACS URL points to `https://gitlab.example.com/users/auth/saml/callback`, where
-`gitlab.example.com` is the URL of your GitLab instance.
-
-If the ACS URL is correct, and you still have errors, review the other
-[Troubleshooting](#troubleshooting) sections.
-
-If you are sure that the ACS URL is correct, proceed to the [Redirect back to the login screen with no evident error](#redirect-back-to-the-login-screen-with-no-evident-error)
-section for further troubleshooting steps.
-
-### Redirect back to the login screen with no evident error
-
-If after signing in into your SAML server you are redirected back to the sign in page and
-no error is displayed, check your `production.log` file. It most likely contains the
-message `Can't verify CSRF token authenticity`. This means that there is an error during
-the SAML request, but in GitLab 11.7 and earlier this error never reaches GitLab due to
-the CSRF check.
-
-To bypass this you can add `skip_before_action :verify_authenticity_token` to the
-`omniauth_callbacks_controller.rb` file immediately before the `after_action :verify_known_sign_in` line and
-comment out the `protect_from_forgery` line using a `#`. Restart Puma for this
-change to take effect. This allows the error to hit GitLab, where it can then
-be seen in the usual logs, or as a flash message on the login screen.
-
-That file is located in `/opt/gitlab/embedded/service/gitlab-rails/app/controllers`
-for Omnibus installations and by default in `/home/git/gitlab/app/controllers` for
-installations from source. Restart Puma using the `sudo gitlab-ctl restart puma`
-command on Omnibus installations and `sudo service gitlab restart` on installations
-from source.
-
-You may also find the [SAML Tracer](https://addons.mozilla.org/en-US/firefox/addon/saml-tracer/)
-(Firefox) and [SAML Chrome Panel](https://chrome.google.com/webstore/detail/saml-chrome-panel/paijfdbeoenhembfhkhllainmocckace)
-(Chrome) browser extensions useful in your debugging.
-
-### Invalid audience
-
-This error means that the IdP doesn't recognize GitLab as a valid sender and
-receiver of SAML requests. Make sure to:
-
-- Add the GitLab callback URL to the approved audiences of the IdP server.
-- Avoid trailing whitespace in the `issuer` string.
-
-### Missing claims, or `Email can't be blank` errors
-
-The IdP server needs to pass certain information in order for GitLab to either
-create an account, or match the login information to an existing account. `email`
-is the minimum amount of information that needs to be passed. If the IdP server
-is not providing this information, all SAML requests fail.
-
-Make sure this information is provided.
-
-Another issue that can result in this error is when the correct information is being sent by
-the IdP, 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](#attribute_statements).
-
-### Key validation error, Digest mismatch or Fingerprint mismatch
-
-These errors all come from a similar place, the SAML certificate. SAML requests
-must be validated using either a fingerprint, a certificate, or a validator.
-
-For this requirement, be sure to take the following into account:
-
-- If a fingerprint is used, it must be the SHA1 fingerprint
-- If no certificate is provided in the settings, a fingerprint or fingerprint
- validator needs to be provided and the response from the server must contain
- a certificate (`<ds:KeyInfo><ds:X509Data><ds:X509Certificate>`)
-- If a certificate is provided in the settings, it is no longer necessary for
- the request to contain one. In this case the fingerprint or fingerprint
- validators are optional
-
-If none of the above described scenarios is valid, the request
-fails with one of the mentioned errors.
-
-### User is blocked when signing in through SAML
-
-The following are the most likely reasons that a user is blocked when signing in through SAML:
-
-- In the configuration, `gitlab_rails['omniauth_block_auto_created_users'] = true` is set and this is the user's first time signing in.
-- There are [`required_groups`](#required-groups) configured, but the user is not a member of one.
-
-### Google workspace troubleshooting tips
-
-The Google Workspace documentation on [SAML app error messages](https://support.google.com/a/answer/6301076?hl=en) is helpful for debugging if you are seeing an error from Google while signing in.
-Pay particular attention to the following 403 errors:
-
-- `app_not_configured`
-- `app_not_configured_for_user`
+See our [troubleshooting SAML guide](../user/group/saml_sso/troubleshooting.md).
diff --git a/doc/integration/sourcegraph.md b/doc/integration/sourcegraph.md
index 72ad0bcc32d..731c21c17fa 100644
--- a/doc/integration/sourcegraph.md
+++ b/doc/integration/sourcegraph.md
@@ -49,7 +49,7 @@ You can skip this step if you already have your GitLab repositories searchable i
### Configure your GitLab instance with Sourcegraph
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Sourcegraph** configuration section.
1. Check **Enable Sourcegraph**.
diff --git a/doc/integration/twitter.md b/doc/integration/twitter.md
index 2218529a729..aa9014adc49 100644
--- a/doc/integration/twitter.md
+++ b/doc/integration/twitter.md
@@ -88,7 +88,8 @@ Twitter. Twitter generates a client ID and secret key for you to use.
1. Save the configuration file.
-1. [Reconfigure](../administration/restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart GitLab](../administration/restart_gitlab.md#installations-from-source) for the changes to take effect if you
- installed GitLab via Omnibus or from source respectively.
+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).
On the sign in page there should now be a Twitter icon below the regular sign in form. Select the icon to begin the authentication process. Twitter asks the user to sign in and authorize the GitLab application. If everything goes well the user is returned to GitLab and signed in.
diff --git a/doc/operations/error_tracking.md b/doc/operations/error_tracking.md
index 08acf77b6c7..22e21c01fbd 100644
--- a/doc/operations/error_tracking.md
+++ b/doc/operations/error_tracking.md
@@ -46,7 +46,7 @@ least Maintainer [permissions](../user/permissions.md) to enable the Sentry inte
Make sure to give the token at least the following scopes: `project:read`, `event:read`, and
`event:write` (for resolving events).
1. In GitLab, enable error tracking:
- 1. On the top bar, select **Menu > Projects** and find your project.
+ 1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Monitor > Error Tracking**.
1. Select **Enable error tracking**.
1. In GitLab, ensure error tracking is active.
@@ -136,10 +136,7 @@ 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 not available.
-
-WARNING:
-Turning on integrated error tracking may impact performance, depending on your error rates.
+On GitLab.com, please follow [our user guide](https://gitlab.com/gitlab-org/opstrace/opstrace/-/blob/main/docs/guides/user/error_tracking.md) to get started.
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
diff --git a/doc/operations/feature_flags.md b/doc/operations/feature_flags.md
index 9e7d452c259..0dccaa6bfe9 100644
--- a/doc/operations/feature_flags.md
+++ b/doc/operations/feature_flags.md
@@ -37,7 +37,7 @@ with GitLab, so it's up to developers to use a compatible client library and
To create and enable a feature flag:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Feature Flags**.
1. Select **New feature flag**.
1. Enter a name that starts with a letter and contains only lowercase letters, digits, underscores (`_`),
@@ -180,7 +180,7 @@ For example:
To create a user list:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Feature Flags**.
1. Select **View user lists**
1. Select **New user list**.
@@ -196,7 +196,7 @@ When viewing a list, you can rename it by selecting **Edit** (**{pencil}**).
To add users to a user list:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Feature Flags**.
1. Select **Edit** (**{pencil}**) next to the list you want to add users to.
1. Select **Add Users**.
@@ -210,7 +210,7 @@ To add users to a user list:
To remove users from a user list:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Feature Flags**.
1. Select **Edit** (**{pencil}**) next to the list you want to change.
1. Select **Remove** (**{remove}**) next to the ID you want to remove.
@@ -224,7 +224,7 @@ code so that you can clean it up when it's time to remove the feature flag.
To search for code references of a feature flag:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Feature Flags**.
1. Edit the feature flag you want to remove.
1. Select **More actions** (**{ellipsis_v}**).
@@ -235,7 +235,7 @@ To search for code references of a feature flag:
In [GitLab 13.0 and earlier](https://gitlab.com/gitlab-org/gitlab/-/issues/8621),
to disable a feature flag for a specific environment:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Feature Flags**.
1. For the feature flag you want to disable, select **Edit** (**{pencil}**).
1. To disable the flag:
@@ -250,7 +250,7 @@ to disable a feature flag for a specific environment:
To disable a feature flag for all environments:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Feature Flags**.
1. For the feature flag you want to disable, slide the Status toggle to **Disabled**.
@@ -265,7 +265,7 @@ Then prepare your application with a client library.
To get the access credentials that your application needs to communicate with GitLab:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Feature Flags**.
1. Select **Configure** to view the following:
- **API URL**: URL where the client (application) connects to get a list of feature flags.
diff --git a/doc/operations/incident_management/alerts.md b/doc/operations/incident_management/alerts.md
index a4b34807094..7e4223c0820 100644
--- a/doc/operations/incident_management/alerts.md
+++ b/doc/operations/incident_management/alerts.md
@@ -120,7 +120,7 @@ Prerequisite:
To view the logs for an alert:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Monitor > Alerts**.
1. Select the alert you want to view.
1. Below the title of the alert, select the **Metrics** tab.
@@ -198,7 +198,7 @@ To assign an alert:
1. Display the list of current alerts:
- 1. On the top bar, select **Menu > Projects** and find your project.
+ 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 details.
@@ -226,7 +226,7 @@ add a to-do item:
1. Display the list of current alerts:
- 1. On the top bar, select **Menu > Projects** and find your project.
+ 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**.
diff --git a/doc/operations/incident_management/escalation_policies.md b/doc/operations/incident_management/escalation_policies.md
index c24824e55f8..56ff733e395 100644
--- a/doc/operations/incident_management/escalation_policies.md
+++ b/doc/operations/incident_management/escalation_policies.md
@@ -22,7 +22,7 @@ Prerequisite:
To create an escalation policy:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Monitor > Escalation Policies**.
1. Select **Add an escalation policy**.
1. Enter the policy's name and description, and
@@ -46,7 +46,7 @@ the paged users is created on the alert.
To update an escalation policy:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Monitor > Escalation Policies**.
1. Select **Edit escalation policy** (**{pencil}**).
1. Edit the information.
@@ -56,7 +56,7 @@ To update an escalation policy:
To delete an escalation policy:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Monitor > Escalation Policies**.
1. Select **Delete escalation policy** (**{remove}**).
1. On the confirmation dialog, select **Delete escalation policy**.
diff --git a/doc/operations/incident_management/incident_timeline_events.md b/doc/operations/incident_management/incident_timeline_events.md
new file mode 100644
index 00000000000..743f9b429d6
--- /dev/null
+++ b/doc/operations/incident_management/incident_timeline_events.md
@@ -0,0 +1,94 @@
+---
+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/engineering/ux/technical-writing/#assignments
+---
+
+# Timeline events
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/344059) in GitLab 15.2 [with a flag](../../administration/feature_flags.md) named `incident_timeline`. 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 `incident_timeline`.
+On GitLab.com, this feature is available.
+
+Incident timelines are an important part of record keeping for incidents.
+Timelines can show executives and external viewers what happened during an incident,
+and which steps were taken for it to be resolved.
+
+## View the timeline
+
+Incident timeline events are listed in ascending order of the date and time.
+They are grouped with dates and are listed in ascending order of the time when they occurred:
+
+![Incident timeline events list](img/timeline_events_v15_1.png)
+
+To view the event timeline of an incident:
+
+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.
+1. Select the **Timeline** tab.
+
+## Create an event
+
+You can create a timeline event in many ways in GitLab.
+
+### Using the form
+
+Create a timeline event manually using the form.
+
+Prerequisites:
+
+- You must have at least the Developer role for the project.
+
+To create a timeline event:
+
+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.
+1. Select the **Timeline** tab.
+1. Select **Add new timeline event**.
+1. Complete the required fields.
+1. Select **Save** or **Save and add another event**.
+
+### Using a quick action
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368721) in GitLab 15.4.
+
+You can create a timeline event using the `/timeline` [quick action](../../user/project/quick_actions.md).
+
+### From a comment on the incident
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/344058) in GitLab 15.4.
+
+Prerequisites:
+
+- You must have at least the Developer role for the project.
+
+To create a timeline event from a comment on the incident:
+
+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.
+1. Create a comment or choose an existing comment.
+1. On the comment you want to add, select **Add comment to incident timeline** (**{clock}**).
+
+The comment is shown on the incident timeline as a timeline event.
+
+## Delete an event
+
+You can also delete timeline events.
+
+Prerequisites:
+
+- You must have at least the Developer role for the project.
+
+To delete a timeline event:
+
+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.
+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**.
diff --git a/doc/operations/incident_management/incidents.md b/doc/operations/incident_management/incidents.md
index c1a4c1eb93e..2cb2e5f8045 100644
--- a/doc/operations/incident_management/incidents.md
+++ b/doc/operations/incident_management/incidents.md
@@ -205,64 +205,10 @@ field populated.
### Timeline events
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/344059) in GitLab 15.2 [with a flag](../../administration/feature_flags.md) named `incident_timeline`. Enabled by default.
+Incident timelines give a high-level overview of what happened
+during an incident, and the steps that were taken for it to be resolved.
-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 `incident_timeline`.
-On GitLab.com, this feature is available.
-
-Incident timelines are an important part of record keeping for incidents.
-They give a high-level overview, to executives and external viewers, of what happened during the incident,
-and the steps that were taken for it to be resolved.
-
-#### View the event timeline
-
-Incident timeline events are listed in ascending order of the date and time.
-They are grouped with dates and are listed in ascending order of the time when they occured:
-
-![Incident timeline events list](img/timeline_events_v15_1.png)
-
-To view the event timeline of an incident:
-
-1. On the top bar, select **Menu > Projects** and find your project.
-1. On the left sidebar, select **Monitor > Incidents**.
-1. Select an incident.
-1. Select the **Timeline** tab.
-
-#### Create a timeline event
-
-Create a timeline event manually using the form.
-
-Prerequisites:
-
-- You must have at least the Developer role for the project.
-
-To create a timeline event:
-
-1. On the top bar, select **Menu > Projects** and find your project.
-1. On the left sidebar, select **Monitor > Incidents**.
-1. Select an incident.
-1. Select the **Timeline** tab.
-1. Select **Add new timeline event**.
-1. Complete the required fields.
-1. Select **Save** or **Save and add another event**.
-
-#### Delete a timeline event
-
-You can also delete timeline events.
-
-Prerequisites:
-
-- You must have at least the Developer role for the project.
-
-To delete a timeline event:
-
-1. On the top bar, select **Menu > Projects** and find your project.
-1. On the left sidebar, select **Monitor > Incidents**.
-1. Select an incident.
-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**.
+Read more about [timeline events](incident_timeline_events.md) and how to enable this feature.
### Recent updates view **(PREMIUM)**
@@ -297,32 +243,28 @@ as a column in the Incidents List, and as a field on newly created Incidents. If
the incident isn't closed before the SLA period ends, GitLab adds a `missed::SLA`
label to the incident.
-## Incident actions
-
-There are different actions available to help triage and respond to incidents.
-
-### Assign incidents
+## 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 a milestone
Associate an incident to a milestone by selecting **Edit** next to the milestone feature in the right-hand side bar.
-### Change severity
+## 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 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
+## 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.
@@ -343,7 +285,7 @@ In GitLab 15.1 and earlier, updating the status of an [incident created from an
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)**
+## 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.
@@ -362,30 +304,30 @@ In GitLab 15.1 and earlier, the escalation policy for [incidents created from al
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
+## 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
+## 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
+## Linked resources
-In an incident, you can [links to various resources](linked_resources.md),
+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
+## 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
@@ -398,7 +340,7 @@ 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
+## 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.
diff --git a/doc/operations/incident_management/linked_resources.md b/doc/operations/incident_management/linked_resources.md
index d2254a30f91..3fe4a325cdb 100644
--- a/doc/operations/incident_management/linked_resources.md
+++ b/doc/operations/incident_management/linked_resources.md
@@ -29,7 +29,7 @@ Linked resources for an incident are listed under the **Summary** tab.
To view the linked resources of an incident:
-1. On the top bar, select **Menu > Projects** and find your project.
+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.
@@ -43,24 +43,40 @@ Prerequisites:
To add a linked resource:
-1. On the top bar, select **Menu > Projects** and find your project.
+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.
1. In the **Linked resources** section, select the plus icon (**{plus-square}**).
1. Complete the required fields.
1. Select **Add**.
+### Link Zoom meetings from an incident **(PREMIUM)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/230853) in GitLab 15.4.
+
+Use the `/zoom` [quick action](../../user/project/quick_actions.md) to add multiple Zoom links to an incident:
+
+```plaintext
+/zoom https://example.zoom.us/j/123456789
+```
+
+You can also submit a short optional description with the link. The description shows instead of the URL in the **Linked resources** section of the incident issue:
+
+```plaintext
+/zoom https://example.zoom.us/j/123456789, Low on memory incident
+```
+
## Remove a linked resource
You can also remove a linked resource.
-Prerequisities:
+Prerequisites:
- You must have at least the Reporter role for the project.
To remove a linked resource:
-1. On the top bar, select **Menu > Projects** and find your project.
+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.
1. In the **Linked resources** section, select **Remove** (**{close}**).
diff --git a/doc/operations/incident_management/oncall_schedules.md b/doc/operations/incident_management/oncall_schedules.md
index 9b2e9159429..f1fb3503195 100644
--- a/doc/operations/incident_management/oncall_schedules.md
+++ b/doc/operations/incident_management/oncall_schedules.md
@@ -28,7 +28,7 @@ Prerequisite:
To create an on-call schedule:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Monitor > On-call Schedules**.
1. Select **Add a schedule**.
1. Enter the schedule's name and description and select a time zone.
@@ -43,7 +43,7 @@ create [rotations](#rotations) for your schedule.
To update a schedule:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Monitor > On-call Schedules**.
1. Select **Edit schedule** (**{pencil}**).
1. Edit the information.
@@ -56,7 +56,7 @@ interval (if one is set) to the corresponding times in the new time zone.
To delete a schedule:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Monitor > On-call Schedules**.
1. Select **Delete escalation policy** (**{remove}**).
1. On the confirmation dialog, select **Delete schedule**.
@@ -67,7 +67,7 @@ Add rotations to an existing schedule to put your team members on-call.
To create a rotation:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Monitor > On-call Schedules**.
1. Select the **Add a rotation** link.
1. Enter the following information:
@@ -85,7 +85,7 @@ To create a rotation:
To edit a rotation:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Monitor > On-call Schedules**.
1. In the **Rotations** section, select **Edit rotation** (**{pencil}**).
1. Edit the information.
@@ -95,7 +95,7 @@ To edit a rotation:
To delete a rotation:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Monitor > On-call Schedules**.
1. In the **Rotations** section, select **Delete rotation** (**{remove}**).
1. On the confirmation dialog, select **Delete rotation**.
diff --git a/doc/operations/incident_management/paging.md b/doc/operations/incident_management/paging.md
index 3eeeb67bf51..837fc9c72f5 100644
--- a/doc/operations/incident_management/paging.md
+++ b/doc/operations/incident_management/paging.md
@@ -27,7 +27,7 @@ Email notifications are available in projects for triggered alerts. Project
members with the **Owner** or **Maintainer** roles have the option to receive
a single email notification for new alerts.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Monitor**.
1. Expand **Alerts**.
1. On the **Alert settings** tab, select the
diff --git a/doc/operations/incident_management/status_page.md b/doc/operations/incident_management/status_page.md
index fe75c1812c8..ae4d75396ae 100644
--- a/doc/operations/incident_management/status_page.md
+++ b/doc/operations/incident_management/status_page.md
@@ -45,7 +45,7 @@ Prerequisite:
To provide GitLab with the AWS account information needed to push content to your Status Page:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Monitor**.
1. Expand **Status Page**.
1. Select the **Active** checkbox.
@@ -96,7 +96,7 @@ the issue can potentially [publish comments to your GitLab Status Page](#publish
After creating the CI/CD variables, configure the Project you want to use for
Incident issues:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Monitor**.
1. Expand **Status page**.
1. Fill in your cloud provider's credentials and make sure to select the **Active** checkbox.
diff --git a/doc/operations/metrics/alerts.md b/doc/operations/metrics/alerts.md
index 12bd975db5d..6017e2ee16c 100644
--- a/doc/operations/metrics/alerts.md
+++ b/doc/operations/metrics/alerts.md
@@ -17,16 +17,6 @@ your team when environment performance falls outside of the boundaries you set.
Alerts are not currently supported for [Prometheus cluster integrations](../../user/clusters/integrations.md).
-<!--- start_remove The following content will be removed on remove_date: '2022-09-22' -->
-
-## External Prometheus instances (removed)
-
-This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/219142) in GitLab 13.2 and [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/338834) in 15.0.
-To manually configure a Prometheus server, we recommend
-you use the [generic alerts integration](../incident_management/integrations.md).
-
-<!--- end_remove -->
-
## Trigger actions from alerts **(ULTIMATE)**
Alerts can be used to trigger actions, like opening an issue automatically
diff --git a/doc/operations/metrics/embed_grafana.md b/doc/operations/metrics/embed_grafana.md
index 17ff0ff01a3..1a1ac77ce23 100644
--- a/doc/operations/metrics/embed_grafana.md
+++ b/doc/operations/metrics/embed_grafana.md
@@ -14,7 +14,7 @@ embed Grafana panels using either:
## Use Grafana-rendered images
-You can embed live [Grafana](https://docs.gitlab.com/omnibus/settings/grafana.html) panels as
+You can embed live [Grafana](https://docs.gitlab.com/omnibus/settings/grafana.html) panels as
[a direct link](https://grafana.com/docs/grafana/v7.5/sharing/share-panel/#use-direct-link).
Your Grafana instance must:
diff --git a/doc/operations/product_analytics.md b/doc/operations/product_analytics.md
index 98ba6a9203c..e21770bc579 100644
--- a/doc/operations/product_analytics.md
+++ b/doc/operations/product_analytics.md
@@ -1,81 +1,45 @@
---
stage: Analytics
-group: Product Intelligence
+group: Product Analytics
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
---
# Product Analytics **(FREE)**
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/225167) in GitLab 13.3.
-> - It's deployed behind a feature flag, disabled by default.
-> - It's disabled on GitLab.com.
-> - It's able to be enabled or disabled per-project.
-> - It's not recommended for production use.
-> - To use it in GitLab self-managed instances, ask a GitLab administrator to enable it.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/225167) in GitLab 13.3 [with a flag](../administration/feature_flags.md) named `product_analytics`. Disabled by default.
-GitLab allows you to go from planning an application to getting feedback. Feedback
-is not just observability, but also knowing how people use your product.
-Product Analytics uses events sent from your application to know how they are using it.
-It's based on [Snowplow](https://github.com/snowplow/snowplow), the best open-source
-event tracker. With Product Analytics, you can receive and analyze the Snowplow data
-inside GitLab.
+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 `product_analytics`. On GitLab.com, this feature is not available. The feature is not ready for production use.
-## Enable or disable Product Analytics
+GitLab enables you to go from planning an application to getting feedback. You can use
+Product Analytics to receive and analyze events sent from your application. This analysis
+provides observability information and feedback on how people use your product.
-Product Analytics is under development and not ready for production use. It's
-deployed behind a feature flag that's **disabled by default**.
-[GitLab administrators with access to the GitLab Rails console](../administration/feature_flags.md)
-can enable it for your instance. Product Analytics can be enabled or disabled per-project.
+Events are collected by a [Rails collector](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36443) and
+then processed with [Snowplow](https://github.com/snowplow/snowplow). Events are stored in a GitLab database.
-To enable it:
+## View Product Analytics
-```ruby
-# Instance-wide
-Feature.enable(:product_analytics)
-# or by project
-Feature.enable(:product_analytics, Project.find(<project ID>))
-```
+You can view the event data collected about your applications.
-To disable it:
+Prerequisite:
-```ruby
-# Instance-wide
-Feature.disable(:product_analytics)
-# or by project
-Feature.disable(:product_analytics, Project.find(<project ID>))
-```
+- You must have at least the Reporter role.
-## Access Product Analytics
+To access Product Analytics:
-After enabling the feature flag for Product Analytics, you can access the
-user interface:
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Monitor > Product Analytics**.
-1. Sign in to GitLab as a user with at least the Reporter role.
-1. Navigate to **Monitor > Product Analytics**.
+The Product Analytics interface contains:
-The user interface contains:
+- An Events tab that shows the recent events and a total count.
+- A Graph tab that shows graphs based on events of the last 30 days.
+- A Test tab that sends a sample event payload.
+- A Setup page containing the code to implement in your application.
-- An Events page that shows the recent events and a total count.
-- A test page that sends a sample event.
-- A setup page containing the code to implement in your application.
-
-## Rate limits for Product Analytics
+## Rate limits
While Product Analytics is under development, it's rate-limited to
**100 events per minute** per project. This limit prevents the events table in the
database from growing too quickly.
-
-## Data storage for Product Analytics
-
-Product Analytics stores events are stored in GitLab database.
-
-WARNING:
-This data storage is experimental, and GitLab is likely to remove this data during
-future development.
-
-## Event collection
-
-Events are collected by [Rails collector](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36443),
-allowing GitLab to ship the feature fast. Due to scalability issue, GitLab plans
-to switch to a separate application, such as
-[snowplow-go-collector](https://gitlab.com/gitlab-org/snowplow-go-collector), for event collection.
diff --git a/doc/policy/maintenance.md b/doc/policy/maintenance.md
index e77a0459150..02a1c5a53dc 100644
--- a/doc/policy/maintenance.md
+++ b/doc/policy/maintenance.md
@@ -12,11 +12,11 @@ patch, and security releases. New releases are announced on the [GitLab blog](ht
Our current policy is:
-- Backporting bug fixes for **only the current stable release** at any given time. (See [patch releases](#patch-releases).)
-- Backporting security fixes **to the previous two monthly releases in addition to the current stable release**. (See [security releases](#security-releases).)
+- Backporting bug fixes for **only the current stable release** at any given time - see [patch releases](#patch-releases) below.
+- Backporting security fixes **to the previous two monthly releases in addition to the current stable release**. In some circumstances (outlined in [security releases](#security-releases) below) we may address a security vulnerability using the [patch release](#patch-releases) process or regular monthly release process, that is, providing an update to the current stable release only, with no backports.
In rare cases, release managers may make an exception and backport to more than
-the last two monthly releases. See
+the last two monthly releases. See
[Backporting to older releases](#backporting-to-older-releases) for more information.
## Versioning
@@ -132,13 +132,16 @@ To request backporting to more than one stable release for consideration, raise
### Security releases
Security releases are a special kind of patch release that only include security
-fixes and patches (see below) for the previous two monthly releases in addition to the current stable release.
+fixes and patches for the previous two monthly releases in addition to the current stable release.
For very serious security issues, there is
[precedent](https://about.gitlab.com/releases/2016/05/02/cve-2016-4340-patches/)
to backport security fixes to even more monthly releases of GitLab.
This decision is made on a case-by-case basis.
+In some circumstances we may choose to address a vulnerability using the [patch release](#patch-releases) process or the regular monthly release process, that is, updating the current stable release only, with no backports. Factors influencing this decision include very low likelihood of exploitation, low impact, fix complexity and risk to stability. We will **always address
+high and critical** security issues with a security release.
+
## More information
You may also want to read our:
diff --git a/doc/raketasks/backup_gitlab.md b/doc/raketasks/backup_gitlab.md
index 4629364ce3d..0a38416825a 100644
--- a/doc/raketasks/backup_gitlab.md
+++ b/doc/raketasks/backup_gitlab.md
@@ -35,6 +35,11 @@ WARNING:
The backup command requires [additional parameters](backup_restore.md#back-up-and-restore-for-installations-using-pgbouncer) when
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
+[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.
+
Depending on your version of GitLab, use the following command if you installed
GitLab using the Omnibus package:
@@ -262,7 +267,7 @@ sudo -u git -H bundle exec rake gitlab:backup:create SKIP=db,uploads RAILS_ENV=p
### Skipping tar creation
NOTE:
-It is not possible to skip the tar creation when using [object storage](#uploading-backups-to-a-remote-cloud-storage) for backups.
+It is not possible to skip the tar creation when using [object storage](#upload-backups-to-a-remote-cloud-storage) for backups.
The last part of creating a backup is generation of a `.tar` file containing
all the parts. In some cases (for example, if the backup is picked up by other
@@ -341,12 +346,14 @@ To create an incremental backup, run:
sudo gitlab-backup create INCREMENTAL=yes PREVIOUS_BACKUP=<timestamp_of_backup>
```
-Incremental backups can also be created from [an untarred backup](#skipping-tar-creation) by using `SKIP=tar`:
+To create an [untarred](#skipping-tar-creation) incremental backup from a tarred backup, use `SKIP=tar`:
```shell
sudo gitlab-backup create INCREMENTAL=yes SKIP=tar
```
+You can't create an incremental backup from an [untarred](#skipping-tar-creation) backup.
+
### Back up specific repository storages
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86896) in GitLab 15.0.
@@ -391,7 +398,7 @@ For example, to back up all repositories for all projects in **Group A** (`group
sudo -u git -H bundle exec rake gitlab:backup:create REPOSITORIES_PATHS=group-a,group-b/project-c
```
-### Uploading backups to a remote (cloud) storage
+### Upload backups to a remote (cloud) storage
NOTE:
It is not possible to [skip the tar creation](#skipping-tar-creation) when using object storage for backups.
@@ -401,7 +408,7 @@ the `.tar` file it creates. In the following example, we use Amazon S3 for
storage, but Fog also lets you use [other storage providers](https://fog.io/storage/).
GitLab also [imports cloud drivers](https://gitlab.com/gitlab-org/gitlab/-/blob/da46c9655962df7d49caef0e2b9f6bbe88462a02/Gemfile#L113)
for AWS, Google, OpenStack Swift, Rackspace, and Aliyun. A local driver is
-[also available](#uploading-to-locally-mounted-shares).
+[also available](#upload-to-locally-mounted-shares).
[Read more about using object storage with GitLab](../administration/object_storage.md).
@@ -452,8 +459,8 @@ gitlab_rails['backup_upload_storage_options'] = {
##### SSE-KMS
-To enable SSE-KMS, you'll need the
-[KMS key via its Amazon Resource Name (ARN) in the `arn:aws:kms:region:acct-id:key/key-id` format](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html).
+To enable SSE-KMS, you'll need the
+[KMS key via its Amazon Resource Name (ARN) in the `arn:aws:kms:region:acct-id:key/key-id` format](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html).
Under the `backup_upload_storage_options` configuration setting, set:
- `server_side_encryption` to `aws:kms`.
@@ -722,7 +729,7 @@ Users of GitLab 12.1 and earlier should use the command `gitlab-rake gitlab:back
### Skip uploading backups to remote storage
-If you have configured GitLab to [upload backups in a remote storage](#uploading-backups-to-a-remote-cloud-storage),
+If you have configured GitLab to [upload backups in a remote storage](#upload-backups-to-a-remote-cloud-storage),
you can use the `SKIP=remote` option to skip uploading your backups to the remote storage.
For Omnibus GitLab packages:
@@ -737,23 +744,40 @@ For installations from source:
sudo -u git -H bundle exec rake gitlab:backup:create SKIP=remote RAILS_ENV=production
```
-### Uploading to locally mounted shares
+### Upload to locally-mounted shares
+
+You can send backups to a locally-mounted share (for example, `NFS`,`CIFS`, or `SMB`) using the Fog
+[`Local`](https://github.com/fog/fog-local#usage) storage provider.
+
+To do this, you must set the following configuration keys:
+
+- `backup_upload_connection.local_root`: mounted directory that backups are copied to.
+- `backup_upload_remote_directory`: subdirectory of the `backup_upload_connection.local_root` directory. It is created if it doesn't exist.
+ If you want to copy the tarballs to the root of your mounted directory, use `.`.
-You may also send backups to a mounted share (for example, `NFS`,`CIFS`, or
-`SMB`) by using the Fog [`Local`](https://github.com/fog/fog-local#usage)
-storage provider. The directory pointed to by the `local_root` key _must_ be
-owned by the `git` user _when mounted_ (mounting with the `uid=` of the `git`
-user for `CIFS` and `SMB`) or the user that you are executing the backup tasks
-as (for Omnibus packages, this is the `git` user).
+When mounted, the directory set in the `local_root` key must be owned by either:
-The `backup_upload_remote_directory` _must_ be set in addition to the
-`local_root` key. This is the sub directory inside the mounted directory that
-backups are copied to, and is created if it does not exist. If the
-directory that you want to copy the tarballs to is the root of your mounted
-directory, use `.` instead.
+- The `git` user. So, mounting with the `uid=` of the `git` user for `CIFS` and `SMB`.
+- The user that you are executing the backup tasks as. For Omnibus GitLab, this is the `git` user.
Because file system performance may affect overall GitLab performance,
-[GitLab doesn't recommend using cloud-based file systems for storage](../administration/nfs.md#avoid-using-cloud-based-file-systems).
+[we don't recommend using cloud-based file systems for storage](../administration/nfs.md#avoid-using-cloud-based-file-systems).
+
+#### Avoid conflicting configuration
+
+Don't set the following configuration keys to the same path:
+
+- `gitlab_rails['backup_path']` (`backup.path` for source installations).
+- `gitlab_rails['backup_upload_connection'].local_root` (`backup.upload.connection.local_root` for source installations).
+
+The `backup_path` configuration key sets the local location of the backup file. The `upload` configuration key is
+intended for use when the backup file is uploaded to a separate server, perhaps for archival purposes.
+
+If these configuration keys are set to the same location, the upload feature fails because a backup already exists at
+the upload location. This failure causes the upload feature to delete the backup because it assumes it's a residual file
+remaining after the failed upload attempt.
+
+#### Configure uploads to locally-mounted shares
For Omnibus GitLab packages:
@@ -878,7 +902,7 @@ for backups. The next time the backup task runs, backups older than the `backup_
pruned.
This configuration option manages only local files. GitLab doesn't prune old
-files stored in a third-party [object storage](#uploading-backups-to-a-remote-cloud-storage)
+files stored in a third-party [object storage](#upload-backups-to-a-remote-cloud-storage)
because the user may not have permission to list and delete files. It's
recommended that you configure the appropriate retention policy for your object
storage (for example, [AWS S3](https://docs.aws.amazon.com/AmazonS3/latest/user-guide/create-lifecycle.html)).
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index 878511b3e14..03413aca2af 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -12,7 +12,7 @@ An application data backup creates an archive file that contains the database,
all repositories and all attachments.
You can only restore a backup to **exactly the same version and type (CE/EE)**
-of GitLab on which it was created. The best way to
+of GitLab on which it was created. The best way to
[migrate your projects from one server to another](#migrate-to-a-new-server) is through a backup and restore.
WARNING:
@@ -79,10 +79,18 @@ For detailed information on restoring GitLab, see [Restore GitLab](restore_gitla
## Alternative backup strategies
-If your GitLab instance contains a lot of Git repository data, you may find the
-GitLab backup script to be too slow. If your GitLab instance has a lot of forked
-projects, the regular backup task also duplicates the Git data for all of them.
-In these cases, consider using file system snapshots as part of your backup strategy.
+In the following cases, consider using file system data transfer or snapshots as part of your backup strategy:
+
+- Your GitLab instance contains a lot of Git repository data and the GitLab backup script is too slow.
+- 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.
+
+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,
+ don't use these methods to migrate from Ubuntu to Fedora.
+- Data consistency is very important. We recommend stopping GitLab with `sudo gitlab-ctl stop` before taking doing a file system transfer (with rsync, for example) or taking a
+ snapshot.
Example: Amazon Elastic Block Store (EBS)
@@ -190,7 +198,7 @@ tables will [be logged by PostgreSQL](../administration/logs/index.md#postgresql
ERROR: relation "tablename" does not exist at character 123
```
-This happens because the task uses `pg_dump`, which
+This happens because the task uses `pg_dump`, which
[sets a null search path and explicitly includes the schema in every SQL query](https://gitlab.com/gitlab-org/gitlab/-/issues/23211)
to address [CVE-2018-1058](https://www.postgresql.org/about/news/postgresql-103-968-9512-9417-and-9322-released-1834/).
@@ -318,7 +326,7 @@ To prepare the new server:
```
1. Disable periodic background jobs:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
1. Under the Sidekiq dashboard, select **Cron** tab and then
**Disable All**.
@@ -398,7 +406,7 @@ To prepare the new server:
1. [Restore the GitLab backup](#restore-gitlab).
1. Verify that the Redis database restored correctly:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
1. Under the Sidekiq dashboard, verify that the numbers
match with what was shown on the old server.
diff --git a/doc/raketasks/restore_gitlab.md b/doc/raketasks/restore_gitlab.md
index 9d9a4d7b8c8..7b3a60b436c 100644
--- a/doc/raketasks/restore_gitlab.md
+++ b/doc/raketasks/restore_gitlab.md
@@ -157,6 +157,8 @@ the restore target directories are empty.
For both these installation types, the backup tarball has to be available in
the backup location (default location is `/var/opt/gitlab/backups`).
+If you use Docker Swarm, [first disable the health check](#restore-gitlab-from-backup-using-docker-swarm).
+
For Docker installations, the restore task can be run from host:
```shell
@@ -188,6 +190,20 @@ issue.
The GitLab Helm chart uses a different process, documented in
[restoring a GitLab Helm chart installation](https://gitlab.com/gitlab-org/charts/gitlab/blob/master/doc/backup-restore/restore.md).
+### Restore GitLab from backup using Docker Swarm
+
+Docker Swarm might restart the container during the restore process because Puma is shut down,
+and so the container health check fails. To work around this problem, disable the health check
+mechanism in Docker compose file:
+
+```yaml
+healthcheck:
+ disable: true
+```
+
+Read more in issue #6846,
+[GitLab restore can fail owing to `gitlab-healthcheck`](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/6846).
+
## Restore for installation from source
First, ensure your backup tar file is in the backup directory described in the
diff --git a/doc/security/img/allowlist_v13_0.png b/doc/security/img/allowlist_v13_0.png
deleted file mode 100644
index 973b53a57a4..00000000000
--- a/doc/security/img/allowlist_v13_0.png
+++ /dev/null
Binary files differ
diff --git a/doc/security/img/outbound_requests_section_v12_2.png b/doc/security/img/outbound_requests_section_v12_2.png
deleted file mode 100644
index 3dc99868a35..00000000000
--- a/doc/security/img/outbound_requests_section_v12_2.png
+++ /dev/null
Binary files differ
diff --git a/doc/security/information_exclusivity.md b/doc/security/information_exclusivity.md
index 754d5fff843..2eeb436316f 100644
--- a/doc/security/information_exclusivity.md
+++ b/doc/security/information_exclusivity.md
@@ -24,7 +24,7 @@ limitation.
You can take steps to prevent unintentional sharing and information
destruction. This limitation is the reason why only certain people are allowed
to [add users to a project](../user/project/members/index.md)
-and why only a GitLab administrator can
+and why only a GitLab administrator can
[force push a protected branch](../user/project/protected_branches.md).
<!-- ## Troubleshooting
diff --git a/doc/security/password_length_limits.md b/doc/security/password_length_limits.md
index 04c3a5c99e1..57466d1ed5d 100644
--- a/doc/security/password_length_limits.md
+++ b/doc/security/password_length_limits.md
@@ -30,7 +30,7 @@ The user password length is set to a minimum of 8 characters by default.
To change the minimum password length using GitLab UI:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General** and expand **Sign-up restrictions**.
![Minimum password length settings](../user/admin_area/img/minimum_password_length_settings_v12_6.png)
diff --git a/doc/security/ssh_keys_restrictions.md b/doc/security/ssh_keys_restrictions.md
index eca52c41e4f..138ebb9858a 100644
--- a/doc/security/ssh_keys_restrictions.md
+++ b/doc/security/ssh_keys_restrictions.md
@@ -20,7 +20,7 @@ limit the allowed SSH key algorithms.
GitLab allows you to restrict the allowed SSH key technology as well as specify
the minimum key length for each technology:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General** (`/admin/application_settings/general`).
1. Expand the **Visibility and access controls** section:
diff --git a/doc/security/token_overview.md b/doc/security/token_overview.md
index a2119c86268..e585f2caeca 100644
--- a/doc/security/token_overview.md
+++ b/doc/security/token_overview.md
@@ -124,17 +124,16 @@ This table shows available scopes per token. Scopes can be limited further on to
## Security considerations
-Access tokens should be treated like passwords and kept secure.
-
-Adding them to URLs is a security risk. This is especially true when cloning or adding a remote, as Git then writes the URL to its `.git/config` file in plain text. URLs are also generally logged by proxies and application servers, which makes those credentials visible to system administrators.
-
-Instead, API calls can be passed an access token using headers, like [the `Private-Token` header](../api/index.md#personalprojectgroup-access-tokens).
-
-Tokens can also be stored using a [Git credential storage](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage).
-
-Tokens should not be committed to your source code. Instead, consider an approach such as [using external secrets in CI](../ci/secrets/index.md).
-
-When creating a scoped token, consider using the most limited scope possible to reduce the impact of accidentally leaking the token.
-
-When creating a token, consider setting a token that expires when your task is complete. For example, if performing a one-off import, set the
-token to expire after a few hours or a day. This reduces the impact of a token that is accidentally leaked because it is useless when it expires.
+- Access tokens should be treated like passwords and kept secure.
+- Adding access tokens to URLs is a security risk, especially when cloning or adding a remote because Git then writes the URL to its `.git/config` file in plain text. URLs are
+ also generally logged by proxies and application servers, which makes those credentials visible to system administrators. Instead, pass API calls an access token using
+ headers like [the `Private-Token` header](../api/index.md#personalprojectgroup-access-tokens).
+- Tokens can also be stored using a [Git credential storage](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage).
+- Tokens should not be committed to your source code. Instead, consider an approach such as [using external secrets in CI](../ci/secrets/index.md).
+- When creating a scoped token, consider using the most limited scope possible to reduce the impact of accidentally leaking the token.
+- When creating a token, consider setting a token that expires when your task is complete. For example, if performing a one-off import, set the
+ token to expire after a few hours or a day. This reduces the impact of a token that is accidentally leaked because it is useless when it expires.
+- Be careful not to include tokens when pasting code, console commands, or log outputs into an issue or MR description or comment.
+- Don’t log credentials in the console logs. Consider [protecting](../ci/variables/index.md#protected-cicd-variables) and
+ [masking](../ci/variables/index.md#mask-a-cicd-variable) your credentials.
+- Review all currently active access tokens of all types on a regular basis and revoke any that are no longer needed.
diff --git a/doc/security/two_factor_authentication.md b/doc/security/two_factor_authentication.md
index 93d77a69f0e..c808cf4e321 100644
--- a/doc/security/two_factor_authentication.md
+++ b/doc/security/two_factor_authentication.md
@@ -26,7 +26,7 @@ cannot leave the 2FA configuration area at `/-/profile/two_factor_auth`.
To enable 2FA for all users:
-1. On the top bar, select **Menu > Admin**.
+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-in restrictions** section, where you can configure both.
@@ -52,7 +52,7 @@ Prerequisites:
To enforce 2FA only for certain groups:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Permissions and group features**.
1. Select **All users in this group must set up two-factor authentication**.
diff --git a/doc/security/unlock_user.md b/doc/security/unlock_user.md
index efe9c5784ad..041527f18af 100644
--- a/doc/security/unlock_user.md
+++ b/doc/security/unlock_user.md
@@ -14,7 +14,7 @@ Users are locked after ten failed sign-in attempts. These users remain locked:
## Unlock a user from the Admin Area
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Use the search bar to find the locked user.
1. From the **User administration** dropdown select **Unlock**.
diff --git a/doc/security/user_email_confirmation.md b/doc/security/user_email_confirmation.md
index 54920b15362..172e26db618 100644
--- a/doc/security/user_email_confirmation.md
+++ b/doc/security/user_email_confirmation.md
@@ -11,7 +11,7 @@ GitLab can be configured to require confirmation of a user's email address when
the user signs up. When this setting is enabled, the user is unable to sign in until
they confirm their email address.
-1. On the top bar, select **Menu > Admin**.
+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.
diff --git a/doc/security/user_file_uploads.md b/doc/security/user_file_uploads.md
index 7c11d01396d..ddb8392d2be 100644
--- a/doc/security/user_file_uploads.md
+++ b/doc/security/user_file_uploads.md
@@ -39,7 +39,7 @@ Prerequisite:
To configure authentication settings for all media files:
-1. On the top bar, select **Menu > Projects** and find your 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. Scroll to **Project visibility** and select **Require authentication to view media files**.
diff --git a/doc/security/webhooks.md b/doc/security/webhooks.md
index f2066ee4a42..e1daa705355 100644
--- a/doc/security/webhooks.md
+++ b/doc/security/webhooks.md
@@ -7,85 +7,80 @@ type: concepts, reference, howto
# Webhooks and insecure internal web services **(FREE SELF)**
-NOTE:
-On GitLab.com, the [maximum number of webhooks and their size](../user/gitlab_com/index.md#webhooks) per project, and per group, is limited.
-
-If you have non-GitLab web services running on your GitLab server or within its
-local network, these may be vulnerable to exploitation via Webhooks.
-
-With [Webhooks](../user/project/integrations/webhooks.md), you and your project
-maintainers and owners can set up URLs to be triggered when specific changes
-occur in your projects. Normally, these requests are sent to external web
-services specifically set up for this purpose, that process the request and its
-attached data in some appropriate way.
-
-Things get hairy, however, when a Webhook is set up with a URL that doesn't
-point to an external, but to an internal service, that may do something
-completely unintended when the webhook is triggered and the POST request is
-sent.
-
-Webhook requests are made by the GitLab server itself and use a single
-(optional) secret token per hook for authorization (instead of a user or
-repository-specific token). As a result, these requests may have broader access than
-intended, including access to everything running on the server hosting the webhook. This
-may include the GitLab server or API itself (for example, `http://localhost:123`).
-Depending on the called webhook, this may also result in network access
-to other servers within that webhook server's local network (for example,
-`http://192.168.1.12:345`), even if these services are otherwise protected
-and inaccessible from the outside world.
-
-If a web service does not require authentication, Webhooks can be used to
-trigger destructive commands by getting the GitLab server to make POST requests
-to endpoints like `http://localhost:123/some-resource/delete`.
-
-To prevent this type of exploitation from happening, starting with GitLab 10.6,
-all Webhook requests to the current GitLab instance server address and/or in a
-private network are forbidden by default. That means that all requests made
-to `127.0.0.1`, `::1` and `0.0.0.0`, as well as IPv4 `10.0.0.0/8`, `172.16.0.0/12`,
-`192.168.0.0/16` and IPv6 site-local (`ffc0::/10`) addresses aren't allowed.
-
-This behavior can be overridden:
-
-1. On the top bar, select **Menu > Admin**.
+Users with at least the Maintainer role can set up [webhooks](../user/project/integrations/webhooks.md) that are
+triggered when specific changes occur in a project. When triggered, a `POST` HTTP request is sent to a URL. A webhook is
+usually configured to send data to a specific external web service, which processes the data in an appropriate way.
+
+However, a webhook can be configured with a URL for an internal web service instead of an external web service.
+When the webhook is triggered, non-GitLab web services running on your GitLab server or in its local network could be
+exploited.
+
+Webhook requests are made by the GitLab server itself and use a single optional secret token per hook for authorization
+instead of:
+
+- A user token.
+- A repository-specific token.
+
+As a result, these requests can have broader access than intended, including access to everything running on the server
+that hosts the webhook including:
+
+- The GitLab server.
+- The API itself.
+- For some webhooks, network access to other servers in that webhook server's local network, even if these services
+ are otherwise protected and inaccessible from the outside world.
+
+Webhooks can be used to trigger destructive commands using web services that don't require authentication. These webhooks
+can get the GitLab server to make `POST` HTTP requests to endpoints that delete resources.
+
+## Allow webhook and service requests to local network
+
+To prevent exploitation of insecure internal web services, all webhook requests to the following local network addresses are not allowed:
+
+- The current GitLab instance server address.
+- Private network addresses, including `127.0.0.1`, `::1`, `0.0.0.0`, `10.0.0.0/8`, `172.16.0.0/12`,
+ `192.168.0.0/16`, and IPv6 site-local (`ffc0::/10`) addresses.
+
+To allow access to these addresses:
+
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**.
-1. Expand the **Outbound requests** section:
- ![Outbound requests Admin Area settings](img/outbound_requests_section_v12_2.png)
-1. Select **Allow requests to the local network from web hooks and services**.
+1. Expand **Outbound requests**.
+1. Select the **Allow requests to the local network from web hooks and services** checkbox.
-NOTE:
-*System hooks* are enabled to make requests to local network by default since they are
-set up by administrators. However, you can turn this off by disabling the
-**Allow requests to the local network from system hooks** option.
+## Prevent system hook requests to local network
-## Allowlist for local requests
+[System hooks](../administration/system_hooks.md) are permitted to make requests to local network by default because
+they are set up by administrators. To prevent system hook requests to the local network:
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/44496) in GitLab 12.2
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Settings > Network**.
+1. Expand **Outbound requests**.
+1. Clear the **Allow requests to the local network from system hooks** checkbox.
-You can allow certain domains and IP addresses to be accessible to both *system hooks*
-and *webhooks* even when local requests are not allowed by adding them to the
-allowlist:
+## Create an allowlist for local requests
-1. On the top bar, select **Menu > Admin**.
-1. On the left sidebar, select **Settings > Network** (`/admin/application_settings/network`)
- and expand **Outbound requests**:
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/44496) in GitLab 12.2
- ![Outbound local requests allowlist](img/allowlist_v13_0.png)
+You can allow certain domains and IP addresses to be accessible to both system hooks and webhooks, even when local
+requests are forbidden. To add these domains to the allowlist:
-The allowed entries can be separated by semicolons, commas or whitespaces
-(including newlines) and be in different formats like hostnames, IP addresses and/or
-IP ranges. IPv6 is supported. Hostnames that contain Unicode characters should
-use [Internationalized Domain Names in Applications](https://www.icann.org/en/icann-acronyms-and-terms/internationalized-domain-names-in-applications-en)
-(IDNA) encoding.
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Settings > Network**.
+1. Expand **Outbound requests** and add entries.
-The allowlist can hold a maximum of 1000 entries. Each entry can be a maximum of
-255 characters.
+The entries can:
-You can allow a particular port by specifying it in the allowlist entry.
-For example `127.0.0.1:8080` only allows connections to port 8080 on `127.0.0.1`.
-If no port is mentioned, all ports on that IP/domain are allowed. An IP range
-allows all ports on all IPs in that range.
+- Be separated by semicolons, commas, or whitespaces (including newlines).
+- Be in different formats like hostnames, IP addresses, IP address ranges. IPv6 is supported. Hostnames that contain
+ Unicode characters should use [Internationalized Domain Names in Applications](https://www.icann.org/en/icann-acronyms-and-terms/internationalized-domain-names-in-applications-en)
+ (IDNA) encoding.
+- Include ports. For example, `127.0.0.1:8080` only allows connections to port 8080 on `127.0.0.1`. If no port is specified,
+ all ports on that IP address or domain are allowed. An IP address range allows all ports on all IP addresses in that
+ range.
+- Number no more than 1000 entries of no more than 255 characters for each entry.
+- Not contain wildcards (for example, `*.example.com`).
-Example:
+For example:
```plaintext
example.com;gitlab.example.com
@@ -96,9 +91,6 @@ example.com;gitlab.example.com
example.com:8080
```
-NOTE:
-Wildcards (`*.example.com`) are not currently supported.
-
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
diff --git a/doc/subscriptions/gitlab_com/index.md b/doc/subscriptions/gitlab_com/index.md
index 6b83a00cac1..aed8bafb780 100644
--- a/doc/subscriptions/gitlab_com/index.md
+++ b/doc/subscriptions/gitlab_com/index.md
@@ -48,7 +48,7 @@ Prerequisite:
To see the status of your GitLab SaaS subscription:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > Billing**.
The following information is displayed:
@@ -98,7 +98,7 @@ In this case, they would see only the features available to that subscription.
To view a list of seats being used:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > Usage Quotas**.
1. On the **Seats** tab, view usage information.
@@ -128,7 +128,7 @@ For example:
To export seat usage data as a CSV file:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > Billing**.
1. Under **Seats currently in use**, select **See usage**.
1. Select **Export list**.
@@ -179,7 +179,7 @@ The following is emailed to you:
To remove a billable user from your subscription:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > Billing**.
1. In the **Seats currently in use** section, select **See usage**.
1. In the row for the user you want to remove, on the right side, select the ellipsis and **Remove user**.
@@ -299,7 +299,7 @@ for your personal or group namespace. CI/CD minutes are a **one-time purchase**,
NOTE:
Free namespaces are subject to a 5GB storage and 10GB transfer [soft limit](https://about.gitlab.com/pricing/). Once all storage is available to view in the usage quota workflow, GitLab will automatically enforce the namespace storage limit and the project limit will be removed. This change will be announced separately. The storage and transfer add-on can be purchased to increase the limits.
-Projects have a free storage quota of 10 GB. To exceed this quota you must first
+Projects have a free storage quota of 10 GB. To exceed this quota you must first
[purchase one or more storage subscription units](#purchase-more-storage-and-transfer). Each unit provides 10 GB of additional
storage per namespace. A storage subscription is renewed annually. For more details, see
[Usage Quotas](../../user/usage_quotas.md).
@@ -346,7 +346,7 @@ main quota. You can find pricing for additional storage on the
To purchase additional storage for your group on GitLab SaaS:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > Usage Quotas**.
1. Select **Storage** tab.
1. Select **Purchase more storage**.
diff --git a/doc/subscriptions/gitlab_dedicated/index.md b/doc/subscriptions/gitlab_dedicated/index.md
index 218f6b7f824..e5f75fd5a7a 100644
--- a/doc/subscriptions/gitlab_dedicated/index.md
+++ b/doc/subscriptions/gitlab_dedicated/index.md
@@ -20,13 +20,13 @@ GitLab Dedicated enables you to offload the operational overhead of managing the
- 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) is supported.
- Networking:
- - Public connectivity
+ - 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.
- 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.
-- Upgrade strategy:
+ 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.
+- 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.
-- Backup strategy: regular backups taken and tested.
+- Backups: regular backups taken and tested.
- Choice of cloud region: upon onboarding, choose the cloud region where you want to deploy your instance. Some AWS regions have limited features and as a result, we are not able to deploy production instances to those regions. See below for the [full list of regions](#aws-regions-not-supported) not currently supported.
- Security: Data encrypted at rest and in transit using latest encryption standards.
- Application: Self-managed [Ultimate feature set](https://about.gitlab.com/pricing/self-managed/feature-comparison/) with the exception of the unsupported features [listed below](#features-not-available-at-launch).
diff --git a/doc/subscriptions/index.md b/doc/subscriptions/index.md
index ed96fbd91ef..e71954f1968 100644
--- a/doc/subscriptions/index.md
+++ b/doc/subscriptions/index.md
@@ -109,7 +109,7 @@ Purchases in the Customers Portal require a credit card on record as a payment m
multiple credit cards to your account, so that purchases for different products are charged to the
correct card.
-If you would like to use an alternative method to pay, please
+If you would like to use an alternative method to pay, please
[contact our Sales team](https://about.gitlab.com/sales/).
To change your payment method:
@@ -194,7 +194,7 @@ GitLab for Open Source Program benefits apply to an entire GitLab namespace. To
To add a license:
-1. On the top bar, select **Menu > Projects** and find your 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.
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.
@@ -211,7 +211,7 @@ Benefits of the GitLab Open Source Program apply to all projects in a GitLab nam
##### Screenshot 1: License overview
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select your project avatar. If you haven't specified an avatar for your project, the avatar displays as a single letter.
1. Take a screenshot of the project overview that clearly displays the license you've chosen for your project.
@@ -219,7 +219,7 @@ Benefits of the GitLab Open Source Program apply to all projects in a GitLab nam
##### Screenshot 2: License contents
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Repository** and locate the project's `LICENSE` file.
1. Take a screenshot of the contents of the file. Make sure the screenshot includes the title of the license.
@@ -229,7 +229,7 @@ Benefits of the GitLab Open Source Program apply to all projects in a GitLab nam
To be eligible for the GitLab Open Source Program, projects must be publicly visible. To check your project's public visibility settings:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. From the left sidebar, select **Settings > General**.
1. Expand **Visibility, project features, permissions**.
1. From the **Project visibility** dropdown list, select **Public**.
diff --git a/doc/subscriptions/self_managed/index.md b/doc/subscriptions/self_managed/index.md
index 758b472d67b..4ae38de54bc 100644
--- a/doc/subscriptions/self_managed/index.md
+++ b/doc/subscriptions/self_managed/index.md
@@ -49,7 +49,7 @@ Prorated charges are not possible without a quarterly usage report.
You can view users for your license and determine if you've gone over your subscription.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left menu, select **Subscription**.
The lists of users are displayed.
@@ -218,7 +218,7 @@ to IP address `104.18.26.123:443` (`customers.gitlab.com`).
You can manually sync your subscription details at any time.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Subscription**.
1. In the **Subscription details** section, select **Sync subscription details**.
@@ -241,7 +241,7 @@ instance, ensure you're purchasing enough seats to
If you are an administrator, you can view the status of your subscription:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Subscription**.
The **Subscription** page includes the following details:
@@ -265,7 +265,7 @@ It also displays the following information:
If you are an administrator, you can export your license usage into a CSV:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Subscription**.
1. In the top right, select **Export license usage file**.
diff --git a/doc/topics/application_development_platform/index.md b/doc/topics/application_development_platform/index.md
index fac9f963a98..524ba2aaf6d 100644
--- a/doc/topics/application_development_platform/index.md
+++ b/doc/topics/application_development_platform/index.md
@@ -1,67 +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/engineering/ux/technical-writing/#assignments
+redirect_to: '../../user/infrastructure/index.md'
+remove_date: '2022-12-08'
---
-# Application Development Platform **(FREE)**
+This document was moved to [another location](../../user/infrastructure/index.md).
-The GitLab Application Development Platform refers to the set of GitLab features used to create, configure, and manage
-a complete software development environment. It provides development, operations, and security teams with a robust feature set aimed at supporting best practices out of the box.
-
-## Overview
-
-The GitLab Application Development Platform aims to:
-
-- Reduce and even eliminate the time it takes for an Operations team
- to provide a full environment for software developers.
-- Get developers up and running fast so they can focus on writing
- great applications with a robust development feature set.
-- Provide best-of-breed security features so that applications developed
- with GitLab are not affected by vulnerabilities that may lead to security
- problems and unintended use.
-
-It is comprised of the following high-level elements:
-
-1. Compute
-1. Build, test, and deploy a wide range of applications
-1. Security
-1. Observability
-
-We believe the use of these common building blocks equate to big gains for teams of all sizes, resulting from the adoption
-of newer, more efficient, more profitable, and less error-prone techniques for shipping software applications.
-
-### Compute
-
-Because at GitLab we are [cloud-native first](https://about.gitlab.com/handbook/product/#cloud-native-first) our
-Application Development Platform initially focuses on providing robust support for Kubernetes, with other platforms
-to follow. Teams can bring their own clusters and we additionally make it easy to create new infrastructure
-with various cloud providers.
-
-### Build, test, deploy
-
-In order to provide modern DevOps workflows, our Application Development Platform relies on
-[Auto DevOps](../autodevops/index.md) to provide those workflows. Auto DevOps works with
-any Kubernetes cluster; you're not limited to running on GitLab infrastructure. Additionally, Auto DevOps offers
-an incremental consumption path. Because it is [composable](../autodevops/customize.md#using-components-of-auto-devops),
-you can use as much or as little of the default pipeline as you'd like, and deeply customize without having to integrate a completely different platform.
-
-### Security
-
-The Application Development Platform helps you ensure that the applications you create are not affected by vulnerabilities
-that may lead to security problems and unintended use. This can be achieved by making use of the embedded security features of Auto DevOps,
-which inform security teams and developers if there is something to consider changing in their apps
-before it is too late to create a preventative fix. The following features are included:
-
-- [Auto SAST (Static Application Security Testing)](../autodevops/stages.md#auto-sast)
-- [Auto Dependency Scanning](../autodevops/stages.md#auto-dependency-scanning)
-- [Auto Container Scanning](../autodevops/stages.md#auto-container-scanning)
-- [Auto DAST (Dynamic Application Security Testing)](../autodevops/stages.md#auto-dast)
-
-### Observability
-
-Performance is a critical aspect of the user experience, and ensuring your application is responsive and available is everyone's
-responsibility. The Application Development Platform integrates key performance analytics and feedback
-into GitLab, automatically. The following features are included:
-
-- [Auto Monitoring](../autodevops/stages.md#auto-monitoring)
+<!-- This redirect file can be deleted after <2022-12-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 (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/topics/autodevops/cloud_deployments/auto_devops_with_ecs.md b/doc/topics/autodevops/cloud_deployments/auto_devops_with_ecs.md
index a9b466653bf..4c084f405cd 100644
--- a/doc/topics/autodevops/cloud_deployments/auto_devops_with_ecs.md
+++ b/doc/topics/autodevops/cloud_deployments/auto_devops_with_ecs.md
@@ -13,7 +13,7 @@ You can choose to target AWS ECS as a deployment platform instead of using Kuber
To get started on Auto DevOps to AWS ECS, you must add a specific CI/CD variable.
To do so, follow these steps:
-1. In GitLab, on the top bar, select **Menu > Projects** and find your project.
+1. In GitLab, on the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Auto DevOps**.
1. Specify which AWS platform to target during the Auto DevOps deployment
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 1fea573faa1..5f37ca30604 100644
--- a/doc/topics/autodevops/cloud_deployments/auto_devops_with_gke.md
+++ b/doc/topics/autodevops/cloud_deployments/auto_devops_with_gke.md
@@ -138,7 +138,7 @@ While Auto DevOps is enabled by default, Auto DevOps can be disabled at both
the instance level (for self-managed instances) and the group level. Complete
these steps to enable Auto DevOps if it's disabled:
-1. On the top bar, select **Menu > Projects** and find the application project.
+1. On the top bar, select **Main menu > Projects** and find the application project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Auto DevOps**.
1. Select **Default to Auto DevOps pipeline** to display more options.
diff --git a/doc/topics/autodevops/customize.md b/doc/topics/autodevops/customize.md
index 7f9707a6939..4d97f012a92 100644
--- a/doc/topics/autodevops/customize.md
+++ b/doc/topics/autodevops/customize.md
@@ -425,6 +425,7 @@ applications.
| `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
@@ -512,7 +513,7 @@ these prefixed variables available to the deployed application as environment va
To configure your application variables:
-1. On the top bar, select **Menu > Projects** and find your project.
+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
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index dc58f42f30e..c21ed5ed444 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -12,15 +12,14 @@ info: To determine the technical writer assigned to the Stage/Group associated w
GitLab Auto DevOps is a collection of pre-configured features and integrations
that work together to support your software delivery process.
-Auto DevOps features and integrations:
+Auto DevOps detects your programming language and uses [CI/CD templates](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates)
+to create and run default pipelines to build and test your application. Then, you can [configure deployments](requirements.md) to deploy your apps to staging
+and production, and set up [Review Apps](stages.md#auto-review-apps)
+to preview your changes per branch.
-- Detect your code's language.
-- Build and test your application.
-- Measure code quality.
-- Scan for vulnerabilities and security flaws.
-- Check for licensing issues.
-- Monitor in real time.
-- Deploy your application.
+You can use default settings to quickly ship your apps, and iterate and [customize](customize.md) later.
+
+You can also [manage Auto DevOps with APIs](customize.md#extend-auto-devops-with-the-api).
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For an introduction to Auto DevOps, watch [Auto DevOps in GitLab 11.0](https://youtu.be/0Tc0YYBxqi4).
@@ -58,33 +57,6 @@ Based on the DevOps [stages](stages.md), use Auto DevOps to:
- [Auto Static Application Security Testing (SAST)](stages.md#auto-sast)
- [Auto Secret Detection](stages.md#auto-secret-detection)
-### How it works
-
-Auto DevOps detects your code language and uses [CI/CD templates](https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates)
-to create and run default pipelines. All you need to kick it off is to
-[enable](#enable-or-disable-auto-devops) it.
-
-Auto DevOps starts by building and testing your application. Then, based on your
-[predefined deployment configuration](requirements.md),
-creates the necessary jobs to deploy your apps to staging
-and/or production. It also sets up [Review Apps](stages.md#auto-review-apps)
-so that you can preview your changes in a per-branch basis.
-
-Note that you don't need to set up the deployment upfront. Auto DevOps
-still builds and tests your application. You can define the deployment later.
-
-Auto DevOps avoids the hassle of having to create entire pipelines manually.
-Keep it simple and facilitate an iterative approach: ship your app first,
-then explore the [customizations](customize.md) later.
-You can also [manage Auto DevOps with APIs](customize.md#extend-auto-devops-with-the-api).
-
-Some of the benefits of using Auto DevOps as part of your workflow are:
-
-- Consistency: always start from default templates.
-- Simplicity: create your pipeline with the default settings first, iterate later.
-- Productivity: deploy multiple apps in a short period of time.
-- Efficiency: get things done fast.
-
### Comparison to application platforms and PaaS
Auto DevOps provides features often included in an application
@@ -146,15 +118,15 @@ you can enable it for a [group](#at-the-group-level) or an
[instance](#at-the-instance-level). This can save you the time of
enabling it one by one.
-Only project Maintainers can enable or disable Auto DevOps at the project level.
+Prerequisites:
-Before enabling Auto DevOps, ensure that your project does not have a
-`.gitlab-ci.yml` present. If present, your CI/CD configuration takes
-precedence over the Auto DevOps pipeline.
+- You must have at least the Maintainer role for the project.
+- Ensure your project does not have a `.gitlab-ci.yml` present. If present, your CI/CD configuration takes
+ precedence over the Auto DevOps pipeline.
To enable Auto DevOps for a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Auto DevOps**.
1. Select the **Default to Auto DevOps pipeline** checkbox.
@@ -178,12 +150,13 @@ rather than enabling individually for each subgroup or project.
When enabled for a group, you can still disable Auto DevOps
for the subgroups and projects where you don't want to use it.
-Only GitLab administrators and group owners can enable or disable Auto DevOps
-at the group level.
+Prerequisites:
+
+- You must have at least the Owner role for the group.
To enable Auto DevOps for a group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Auto DevOps**.
1. Select the **Default to Auto DevOps pipeline** checkbox.
@@ -193,9 +166,9 @@ To disable Auto DevOps on the group level, follow the same process and
clear the **Default to Auto DevOps pipeline** checkbox.
After enabling Auto DevOps at the group level, you can trigger the
-Auto DevOps pipeline for any project that belongs to that group. To do so:
+Auto DevOps pipeline for any project that belongs to that group:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. Make sure the project doesn't contain a `.gitlab-ci.yml` file.
1. On the left sidebar, select **CI/CD > Pipelines**.
1. To trigger the Auto DevOps pipeline, select **Run pipeline**.
@@ -207,15 +180,16 @@ instance become enabled. This is convenient when you want to run Auto DevOps by
default for all projects. You can still disable Auto DevOps individually for
the groups and projects where you don't want to run it.
-Only GitLab administrators can enable or disable Auto DevOps at the instance
-level.
-
-Even when disabled for an instance, group owners and project maintainers
+Even when disabled for an instance, group Owners and project Maintainers
can still enable Auto DevOps at the group and project levels.
+Prerequisites:
+
+- You must be an administrator for the instance.
+
To enable Auto DevOps for your instance:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Auto DevOps**.
1. Select the **Default to Auto DevOps pipeline** checkbox.
@@ -232,6 +206,13 @@ it remains unchanged and Auto DevOps doesn't affect it.
To disable Auto DevOps in the instance level, follow the same process
and clear the **Default to Auto DevOps pipeline** checkbox.
+### Private registry support
+
+There is no guarantee that you can use a private container registry with Auto DevOps.
+
+Instead, use the [GitLab Container Registry](../../user/packages/container_registry/index.md) with Auto DevOps to
+simplify configuration and prevent any unforeseen issues.
+
### Quick start
- [Use Auto DevOps to deploy to a Kubernetes cluster on Google Kubernetes Engine (GKE)](cloud_deployments/auto_devops_with_gke.md)
@@ -252,16 +233,7 @@ match your new GitLab version:
- Environment variables.
- [Upgrading PostgreSQL](upgrading_postgresql.md).
-## Limitations
-
-### Private registry support
-
-We cannot guarantee that you can use a private container registry with Auto DevOps.
-
-We strongly advise you to use GitLab Container Registry with Auto DevOps to
-simplify configuration and prevent any unforeseen issues.
-
-### Install applications behind a proxy
+## Install applications behind a proxy
The GitLab integration with Helm does not support installing applications when
behind a proxy.
diff --git a/doc/topics/autodevops/prepare_deployment.md b/doc/topics/autodevops/prepare_deployment.md
index 22a50df7f54..c16a6c837cd 100644
--- a/doc/topics/autodevops/prepare_deployment.md
+++ b/doc/topics/autodevops/prepare_deployment.md
@@ -44,7 +44,7 @@ To define the base domain, either:
- In the project, group, or instance level: go to your cluster settings and add it there.
- In the project or group level: add it as an environment variable: `KUBE_INGRESS_BASE_DOMAIN`.
-- In the instance level: go to **Menu > Admin > Settings > CI/CD > Continuous Integration and Delivery** and add it there.
+- In the instance level: go to **Main menu > Admin > Settings > CI/CD > Continuous Integration and Delivery** and add it there.
The base domain variable `KUBE_INGRESS_BASE_DOMAIN` follows the same order of precedence
as other environment [variables](../../ci/variables/index.md#cicd-variable-precedence).
diff --git a/doc/topics/autodevops/requirements.md b/doc/topics/autodevops/requirements.md
index f3ea13ad1ce..9dffb490807 100644
--- a/doc/topics/autodevops/requirements.md
+++ b/doc/topics/autodevops/requirements.md
@@ -41,7 +41,7 @@ that works best for your needs:
You can choose the deployment method when enabling Auto DevOps or later:
-1. In GitLab, on the top bar, select **Menu > Projects** and find your project.
+1. In GitLab, on the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Auto DevOps**.
1. Choose the deployment strategy.
@@ -61,7 +61,7 @@ To define the base domain, either:
- In the project, group, or instance level: go to your cluster settings and add it there.
- In the project or group level: add it as an environment variable: `KUBE_INGRESS_BASE_DOMAIN`.
-- In the instance level: go to **Menu > Admin > Settings > CI/CD > Continuous Integration and Delivery** and add it there.
+- In the instance level: go to **Main menu > Admin > Settings > CI/CD > Continuous Integration and Delivery** and add it there.
The base domain variable `KUBE_INGRESS_BASE_DOMAIN` follows the same order of precedence
as other environment [variables](../../ci/variables/index.md#cicd-variable-precedence).
diff --git a/doc/topics/autodevops/stages.md b/doc/topics/autodevops/stages.md
index 4e8d0e08eff..62356807854 100644
--- a/doc/topics/autodevops/stages.md
+++ b/doc/topics/autodevops/stages.md
@@ -199,7 +199,7 @@ see the documentation.
## Auto Secret Detection
> - Introduced in GitLab 13.1.
-> - Select functionality [made available](../../user/application_security/secret_detection/index.md#making-secret-detection-available-to-all-gitlab-tiers) in all tiers in GitLab 13.3
+> - Select functionality [made available](../../user/application_security/secret_detection/index.md#features-per-tier) in all tiers in GitLab 13.3
Secret Detection uses the
[Secret Detection Docker image](https://gitlab.com/gitlab-org/security-products/analyzers/secrets) to run Secret Detection on the current code, and checks for leaked secrets. Auto Secret Detection requires [GitLab Runner](https://docs.gitlab.com/runner/) 11.5 or above.
diff --git a/doc/topics/autodevops/troubleshooting.md b/doc/topics/autodevops/troubleshooting.md
index 045f843be44..bf3dc27c0e8 100644
--- a/doc/topics/autodevops/troubleshooting.md
+++ b/doc/topics/autodevops/troubleshooting.md
@@ -9,6 +9,12 @@ info: To determine the technical writer assigned to the Stage/Group associated w
The information in this documentation page describes common errors when using
Auto DevOps, and any available workarounds.
+## Trace Helm commands
+
+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).
+
## Unable to select a buildpack
Auto Build and Auto Test may fail to detect your language or framework with the
diff --git a/doc/topics/build_your_application.md b/doc/topics/build_your_application.md
index d7097e55052..d48b8383271 100644
--- a/doc/topics/build_your_application.md
+++ b/doc/topics/build_your_application.md
@@ -12,5 +12,5 @@ code, and use CI/CD to generate your application. Include packages in your app a
- [Repositories](../user/project/repository/index.md)
- [Merge requests](../user/project/merge_requests/index.md)
- [CI/CD](../ci/index.md)
-- [Packages & Registries](../user/packages/index.md)
+- [Packages and registries](../user/packages/index.md)
- [Application infrastructure](../user/infrastructure/index.md)
diff --git a/doc/topics/git/cherry_picking.md b/doc/topics/git/cherry_picking.md
index 98458133937..d9314c3becc 100644
--- a/doc/topics/git/cherry_picking.md
+++ b/doc/topics/git/cherry_picking.md
@@ -17,8 +17,8 @@ and apply those changes to another branch. Cherry-picks can help you:
You can cherry-pick commits from the command line. In the GitLab user interface,
you can also:
-- Cherry-pick [all changes from a merge request](../../user/project/merge_requests/cherry_pick_changes.md#cherry-pick-a-merge-request).
-- Cherry-pick [a single commit](../../user/project/merge_requests/cherry_pick_changes.md#cherry-pick-a-commit).
+- Cherry-pick [all changes from a merge request](../../user/project/merge_requests/cherry_pick_changes.md#cherry-pick-all-changes-from-a-merge-request).
+- Cherry-pick [a single commit](../../user/project/merge_requests/cherry_pick_changes.md#cherry-pick-a-single-commit).
- Cherry-pick [from a fork to the upstream repository](../../user/project/merge_requests/cherry_pick_changes.md#cherry-pick-into-a-project).
## Cherry-pick from the command line
diff --git a/doc/topics/git/index.md b/doc/topics/git/index.md
index 54af1e99797..3e78e366e00 100644
--- a/doc/topics/git/index.md
+++ b/doc/topics/git/index.md
@@ -36,7 +36,7 @@ The following resources can help you get started with Git:
- [GitLab Git Cheat Sheet (download)](https://about.gitlab.com/images/press/git-cheat-sheet.pdf)
- Commits:
- [Revert a commit](../../user/project/merge_requests/revert_changes.md#revert-a-commit)
- - [Cherry-picking a commit](../../user/project/merge_requests/cherry_pick_changes.md#cherry-pick-a-commit)
+ - [Cherry-picking a commit](../../user/project/merge_requests/cherry_pick_changes.md)
- [Squashing commits](../gitlab_flow.md#squashing-commits-with-rebase)
- [Squash-and-merge](../../user/project/merge_requests/squash_and_merge.md)
- [Signing commits](../../user/project/repository/gpg_signed_commits/index.md)
diff --git a/doc/topics/git/lfs/migrate_to_git_lfs.md b/doc/topics/git/lfs/migrate_to_git_lfs.md
index 864615e7264..dda15845088 100644
--- a/doc/topics/git/lfs/migrate_to_git_lfs.md
+++ b/doc/topics/git/lfs/migrate_to_git_lfs.md
@@ -9,7 +9,7 @@ description: "How to migrate an existing Git repository to Git LFS with BFG."
WARNING:
The following documentation is deprecated. We recommend using
-[`git lfs migrate`](https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-migrate.1.ronn)
+[`git lfs migrate`](https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-migrate.adoc)
instead of the method documented below.
Using Git LFS can help you to reduce the size of your Git
diff --git a/doc/topics/git/numerous_undo_possibilities_in_git/index.md b/doc/topics/git/numerous_undo_possibilities_in_git/index.md
index 9786d1399f7..0ed7b2b5e03 100644
--- a/doc/topics/git/numerous_undo_possibilities_in_git/index.md
+++ b/doc/topics/git/numerous_undo_possibilities_in_git/index.md
@@ -209,7 +209,7 @@ To recover from multiple incorrect commits:
The commits are now `A-B-C-D-E`.
Alternatively, with GitLab,
-you can [cherry-pick](../../../user/project/merge_requests/cherry_pick_changes.md#cherry-pick-a-commit)
+you can [cherry-pick](../../../user/project/merge_requests/cherry_pick_changes.md#cherry-pick-a-single-commit)
that commit into a new merge request.
NOTE:
diff --git a/doc/topics/offline/quick_start_guide.md b/doc/topics/offline/quick_start_guide.md
index 353ba094d1e..c00c9621756 100644
--- a/doc/topics/offline/quick_start_guide.md
+++ b/doc/topics/offline/quick_start_guide.md
@@ -71,7 +71,7 @@ sudo EXTERNAL_URL="http://my-host.internal" dpkg -i <gitlab_package_name>.deb
## Enabling SSL
Follow these steps to enable SSL for your fresh instance. These steps reflect those for
-[manually configuring SSL in Omnibus's NGINX configuration](https://docs.gitlab.com/omnibus/settings/nginx.html#manually-configuring-https):
+[manually configuring SSL in Omnibus's NGINX configuration](https://docs.gitlab.com/omnibus/settings/ssl.html#configure-https-manually):
1. Make the following changes to `/etc/gitlab/gitlab.rb`:
diff --git a/doc/topics/release_your_application.md b/doc/topics/release_your_application.md
index 61ca1468dca..31fc9b4dbb9 100644
--- a/doc/topics/release_your_application.md
+++ b/doc/topics/release_your_application.md
@@ -30,13 +30,13 @@ to Kubernetes clusters using the [GitLab agent](../user/clusters/agent/install/i
#### GitOps deployments **(PREMIUM)**
-With the [GitLab agent for Kubernetes](../user/clusters/agent/install/index.md), you can perform
-[pull-based deployments of Kubernetes manifests](../user/clusters/agent/gitops.md). This provides a scalable, secure,
+With the [GitLab agent for Kubernetes](../user/clusters/agent/install/index.md), you can perform
+[pull-based deployments of Kubernetes manifests](../user/clusters/agent/gitops.md). This provides a scalable, secure,
and cloud-native approach to manage Kubernetes deployments.
#### Deploy to Kubernetes from GitLab CI/CD
-With the [GitLab agent for Kubernetes](../user/clusters/agent/install/index.md), you can perform
+With the [GitLab agent for Kubernetes](../user/clusters/agent/install/index.md), you can perform
[push-based deployments](../user/clusters/agent/ci_cd_workflow.md) from GitLab CI/CD. The agent provides
a secure and reliable connection between GitLab and your Kubernetes cluster.
diff --git a/doc/tutorials/make_your_first_git_commit.md b/doc/tutorials/make_your_first_git_commit.md
index be9023c6ae0..879257fc3b8 100644
--- a/doc/tutorials/make_your_first_git_commit.md
+++ b/doc/tutorials/make_your_first_git_commit.md
@@ -83,8 +83,8 @@ Here's an overview of what we're going to do:
To start, create a sample project in GitLab.
-1. In GitLab, on the top bar, select **Menu > Projects > Create new project**.
-1. Select **Create blank project**.
+1. In GitLab, on the top bar, select **Main menu > Projects > View all projects**.
+1. On the right of the page, select **New project**.
1. For **Project name**, enter `My sample project`. The project slug is generated for you.
This slug is the URL you can use to access the project after it's created.
1. Ensure **Initialize repository with a README** is selected.
@@ -238,7 +238,7 @@ to the default branch (`main`).
NOTE:
For this tutorial, you merge your branch directly to the default branch for your
-repository. In GitLab, you typically use a [merge request](../user/project/merge_requests/)
+repository. In GitLab, you typically use a [merge request](../user/project/merge_requests/index.md)
to merge your branch.
### View your changes in GitLab
diff --git a/doc/tutorials/move_personal_project_to_a_group.md b/doc/tutorials/move_personal_project_to_a_group.md
index 5ebbf813ab9..49ce09a2ed4 100644
--- a/doc/tutorials/move_personal_project_to_a_group.md
+++ b/doc/tutorials/move_personal_project_to_a_group.md
@@ -46,8 +46,8 @@ Maintainer role for the group.
If you don't have a group, create one:
-1. On the top bar, select **Menu > Groups > Create group**
-1. Select **Create group**.
+1. On the top bar, select **Main menu > Groups > View all groups**.
+1. On the right of the page, select **New group**.
1. In **Group name**, enter a name for the group.
1. In **Group URL**, enter a path for the group, which is used as the namespace.
1. Choose the [visibility level](../user/public_access.md).
@@ -64,7 +64,7 @@ Before you move your project to a group:
Now you're ready to move your project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Advanced**.
1. Under **Transfer project**, choose the group to transfer the project to.
@@ -85,7 +85,7 @@ your related resources and tools, such as websites and package managers.
You can now view your project in your group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. Look for your project under **Subgroups and projects**.
Start enjoying the benefits of a group! For example, as the group Owner, you can
diff --git a/doc/update/deprecations.md b/doc/update/deprecations.md
index b69f8de2947..44a7137f698 100644
--- a/doc/update/deprecations.md
+++ b/doc/update/deprecations.md
@@ -45,24 +45,91 @@ sole discretion of GitLab Inc.
<div class="announcement-milestone">
-## Announced in 15.8
+## Announced in 15.4
<div class="deprecation removal-160 breaking-change">
-### Security report schemas version 14.x.x
+### Container Scanning variables that reference Docker
+
+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.
+
+All Container Scanning variables that are prefixed by `DOCKER_` in variable name are deprecated. This includes the `DOCKER_IMAGE`, `DOCKER_PASSWORD`, `DOCKER_USER`, and `DOCKERFILE_PATH` variables. Support for these variables will be removed in the GitLab 16.0 release. Use the [new variable names](https://docs.gitlab.com/ee/user/application_security/container_scanning/#available-cicd-variables) `CS_IMAGE`, `CS_REGISTRY_PASSWORD`, `CS_REGISTRY_USER`, and `CS_DOCKERFILE_PATH` in place of the deprecated names.
+
+</div>
+
+<div class="deprecation removal-160 breaking-change">
+
+### Non-expiring access tokens
+
+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.
+
+Access tokens that have no expiration date are valid indefinitely, which presents a security risk if the access token
+is divulged. Because access tokens that have an exipiration date are better, from GitLab 15.3 we
+[populate a default expiration date](https://gitlab.com/gitlab-org/gitlab/-/issues/348660).
+
+In GitLab 16.0, any [personal](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html),
+[project](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html), or
+[group](https://docs.gitlab.com/ee/user/group/settings/group_access_tokens.html) access token that does not have an
+expiration date will automatically have an expiration date set at one year.
+
+We recommend giving your access tokens an expiration date in line with your company's security policies before the
+default is applied:
+
+- On GitLab.com during the 16.0 milestone.
+- On GitLab self-managed instances when they are upgraded to 16.0.
+
+</div>
+
+<div class="deprecation removal-160 breaking-change">
+
+### Starboard directive in the config for the GitLab Agent for Kubernetes
-End of Support: GitLab <span class="removal-milestone">15.8</span> (2023-01-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/).
Review the details carefully before upgrading.
-All [security report schema](https://gitlab.com/gitlab-org/security-products/security-report-schemas) versions before 15.0.0 are considered deprecated in GitLab %15.8. Specifically, all [schemas](https://gitlab.com/gitlab-org/gitlab/-/tree/master/ee/lib/ee/gitlab/ci/parsers/security/validators/schemas) that match 14.*.* will be deprecated.
+GitLab's operational container scanning capabilities no longer require starboard to be installed. Consequently, use of the `starboard:` directive in the configuration file for the GitLab Agent for Kubernetes is now deprecated and is scheduled for removal in GitLab 16.0. Update your configuration file to use the `container_scanning:` directive.
+
+</div>
+
+<div class="deprecation removal-160 breaking-change">
+
+### Toggle behavior of `/draft` quick action in merge requests
+
+Planned removal: GitLab <span class="removal-milestone">16.0</span> (2022-05-22)
-Please note that any [security report scanner integration](https://docs.gitlab.com/ee/development/integrations/secure.html) with GitLab using a deprecated schema version will result in a deprecation warning as a result of [report validation](https://docs.gitlab.com/ee/development/integrations/secure.html#report-validation).
+WARNING:
+This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
+Review the details carefully before upgrading.
-See [Security report validation](https://docs.gitlab.com/ee/user/application_security/#security-report-validation) for more information.
+In order to make the behavior of toggling the draft status of a merge request more clear via a quick action, we're deprecating and removing the toggle behavior of the `/draft` quick action. Beginning with the 16.0 release of GitLab, `/draft` will only set a merge request to Draft and a new `/ready` quick action will be used to remove the draft status.
+
+</div>
+
+<div class="deprecation removal-160 breaking-change">
+
+### Vulnerability confidence field
+
+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.
+
+In GitLab 15.3, [security report schemas below version 15 were deprecated](https://docs.gitlab.com/ee/update/deprecations.html#security-report-schemas-version-14xx).
+The `confidence` attribute on vulnerability findings exists only in schema versions before `15-0-0`, and therefore is effectively deprecated since GitLab 15.4 supports schema version `15-0-0`. To maintain consistency
+between the reports and our public APIs, the `confidence` attribute on any vulnerability-related components of our GraphQL API is now deprecated and will be
+removed in 16.0.
</div>
</div>
@@ -88,6 +155,20 @@ next major release, GitLab 16.0. This gem sees very little use and its
</div>
+<div class="deprecation removal-154">
+
+### Bundled Grafana deprecated
+
+Planned removal: GitLab <span class="removal-milestone">15.4</span> (2022-09-22)
+
+In GitLab 15.4, we will be swapping the bundled Grafana to a fork of Grafana maintained by GitLab.
+
+There was an [identified CVE for Grafana](https://nvd.nist.gov/vuln/detail/CVE-2022-31107), and to mitigate this security vulnerability, we must swap to our own fork because the older version of Grafana we were bundling is no longer receiving long-term support.
+
+This is not expected to cause any incompatibilities with the previous version of Grafana. Neither when using our bundled version, nor when using an external instance of Grafana.
+
+</div>
+
<div class="deprecation removal-160 breaking-change">
### CAS OmniAuth provider
@@ -121,7 +202,7 @@ The [**Maximum number of active pipelines per project** limit](https://docs.gitl
### Redis 5 deprecated
-End of Support: GitLab <span class="removal-milestone">15.6</span> (2022-11-22)
+End of Support: GitLab <span class="removal-milestone">15.6</span> (2022-11-22)<br />
Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-05-22)
WARNING:
@@ -133,6 +214,40 @@ Redis 5 has reached the end of life in April 2022 and will no longer be supporte
If you are using your own Redis 5.0 instance, you should upgrade it to Redis 6.0 or higher before upgrading to GitLab 16.0 or higher.
</div>
+
+<div class="deprecation removal-160 breaking-change">
+
+### Security report schemas version 14.x.x
+
+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.
+
+Version 14.x.x [security report schemas](https://gitlab.com/gitlab-org/security-products/security-report-schemas) are deprecated.
+
+In GitLab 15.8 and later, [security report scanner integrations](https://docs.gitlab.com/ee/development/integrations/secure.html) that use schema version 14.x.x will display a deprecation warning in the pipeline's **Security** tab.
+
+In GitLab 16.0 and later, the feature will be removed. Security reports that use schema version 14.x.x will cause an error in the pipeline's **Security** tab.
+
+For more information, refer to [security report validation](https://docs.gitlab.com/ee/user/application_security/#security-report-validation).
+
+</div>
+
+<div class="deprecation removal-160 breaking-change">
+
+### Use of `id` field in vulnerabilityFindingDismiss mutation
+
+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.
+
+You can use the vulnerabilityFindingDismiss GraphQL mutation to set the status of a vulnerability finding to `Dismissed`. Previously, this mutation used the `id` field to identify findings uniquely. However, this did not work for dismissing findings from the pipeline security tab. Therefore, using the `id` field as an identifier has been dropped in favor of the `uuid` field. Using the 'uuid' field as an identifier allows you to dismiss the finding from the pipeline security tab.
+
+</div>
</div>
<div class="announcement-milestone">
@@ -337,38 +452,6 @@ In GitLab 15.0, for Dependency Scanning, the default version of Java that the sc
</div>
-<div class="deprecation removal-160 breaking-change">
-
-### Manual iteration management
-
-Planned removal: GitLab <span class="removal-milestone">16.0</span> (2023-04-22)
-
-WARNING:
-This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
-Review the details carefully before upgrading.
-
-Manual iteration management is deprecated and only automatic iteration cadences will be supported in the future.
-
-Creating and deleting iterations will be fully removed in 16.0. Updating all iteration fields except for
-`description` will also be removed.
-
-On the GraphQL API the following mutations will be removed:
-
- 1. `iterationCreate`
- 1. `iterationDelete`
-
-The update `updateIteration` mutation will only allow updating the iteration's `description`. The following
-arguments will be removed:
-
- 1. `title`
- 1. `dueDate`
- 1. `startDate`
-
-For more information about iteration cadences, you can refer to
-[the documentation of the feature](https://docs.gitlab.com/ee/user/group/iterations/#iteration-cadences).
-
-</div>
-
<div class="deprecation removal-150 breaking-change">
### Outdated indices of Advanced Search migrations
@@ -1757,17 +1840,17 @@ When checking if a runner is `paused`, API users are advised to check the boolea
</div>
-<div class="deprecation removal-156 breaking-change">
+<div class="deprecation removal-159 breaking-change">
### SaaS certificate-based integration with Kubernetes
-Planned removal: GitLab <span class="removal-milestone">15.6</span> (2022-11-22)
+Planned removal: GitLab <span class="removal-milestone">15.9</span> (2023-02-22)
WARNING:
This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
Review the details carefully before upgrading.
-The certificate-based integration with Kubernetes will be [deprecated and removed](https://about.gitlab.com/blog/2021/11/15/deprecating-the-cert-based-kubernetes-integration/). As a GitLab SaaS customer, on new namespaces, you will no longer be able to integrate GitLab and your cluster using the certificate-based approach as of GitLab 15.0. The integration for current users will be enabled per namespace. The integrations are expected to be switched off completely on GitLab SaaS around 2022 November 22.
+The certificate-based integration with Kubernetes will be [deprecated and removed](https://about.gitlab.com/blog/2021/11/15/deprecating-the-cert-based-kubernetes-integration/). As a GitLab SaaS customer, on new namespaces, you will no longer be able to integrate GitLab and your cluster using the certificate-based approach as of GitLab 15.0. The integration for current users will be enabled per namespace.
For a more robust, secure, forthcoming, and reliable integration with Kubernetes, we recommend you use the
[agent for Kubernetes](https://docs.gitlab.com/ee/user/clusters/agent/) to connect Kubernetes clusters with GitLab. [How do I migrate?](https://docs.gitlab.com/ee/user/infrastructure/clusters/migrate_to_gitlab_agent.html)
@@ -1778,11 +1861,11 @@ GitLab self-managed customers can still use the feature [with a feature flag](ht
</div>
-<div class="deprecation removal-160 breaking-change">
+<div class="deprecation removal-170 breaking-change">
### Self-managed certificate-based integration with Kubernetes
-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/).
@@ -1792,7 +1875,7 @@ The certificate-based integration with Kubernetes [will be deprecated and remove
As a self-managed customer, we are introducing the [feature flag](../administration/feature_flags.md#enable-or-disable-the-feature) `certificate_based_clusters` in GitLab 15.0 so you can keep your certificate-based integration enabled. However, the feature flag will be disabled by default, so this change is a **breaking change**.
-In GitLab 16.0 we will remove both the feature and its related code. Until the final removal in 16.0, features built on this integration will continue to work, if you enable the feature flag. Until the feature is removed, GitLab will continue to fix security and critical issues as they arise.
+In GitLab 17.0 we will remove both the feature and its related code. Until the final removal in 17.0, features built on this integration will continue to work, if you enable the feature flag. Until the feature is removed, GitLab will continue to fix security and critical issues as they arise.
For a more robust, secure, forthcoming, and reliable integration with Kubernetes, we recommend you use the
[agent for Kubernetes](https://docs.gitlab.com/ee/user/clusters/agent/) to connect Kubernetes clusters with GitLab. [How do I migrate?](https://docs.gitlab.com/ee/user/infrastructure/clusters/migrate_to_gitlab_agent.html)
diff --git a/doc/update/index.md b/doc/update/index.md
index d4412d85355..bffecf58304 100644
--- a/doc/update/index.md
+++ b/doc/update/index.md
@@ -41,7 +41,7 @@ There are also instructions when you want to
### Installation from source
-- [Upgrading Community Edition and Enterprise Edition from source](upgrading_from_source.md) -
+- [Upgrading Community Edition and Enterprise Edition from source](upgrading_from_source.md) -
The guidelines for upgrading Community Edition and Enterprise Edition from source.
- [Patch versions](patch_versions.md) guide includes the steps needed for a
patch version, such as 13.2.0 to 13.2.1, and apply to both Community and Enterprise
@@ -147,7 +147,7 @@ Some installations [may need to run GitLab 14.0 for at least a day](#1400) to co
To check the status of batched background migrations:
-1. On the top bar, select **Menu > Admin**.
+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)
@@ -328,7 +328,7 @@ sudo -u git -H bundle exec rake gitlab:elastic:list_pending_migrations
### What do you do if your Advanced Search migrations are stuck?
In GitLab 15.0, an Advanced Search migration named `DeleteOrphanedCommit` can be permanently stuck
-in a pending state across upgrades. This issue
+in a pending state across upgrades. This issue
[is corrected in GitLab 15.1](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89539).
If you are a self-managed customer who uses GitLab 15.0 with Advanced Search, you will experience performance degradation.
@@ -380,7 +380,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) -> [latest `15.Y.Z`](https://gitlab.com/gitlab-org/gitlab/-/releases)
+`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.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
@@ -465,6 +465,21 @@ 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.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).
+ This migration might take hours or days to complete on larger GitLab instances.
+
+### 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.
+
+### 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.
+
### 15.2.0
- GitLab installations that have multiple web nodes should be
@@ -472,6 +487,19 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
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.
+- 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:
+
+ ```plaintext
+ fork/exec /var/opt/gitlab/gitaly/run/gitaly-<nnnn>/gitaly-git2go-v15: permission denied
+ ```
+
+ To resolve this, remove the `noexec` option from the filesystem mount. An alternative is to change the Gitaly runtime directory:
+
+ 1. Add `gitaly['runtime_dir'] = '<PATH_WITH_EXEC_PERM>'` to `/etc/gitlab/gitlab.rb` and specify a location without `noexec` set.
+ 1. Run `sudo gitlab-ctl reconfigure`.
+- [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.
### 15.1.0
@@ -490,6 +518,8 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
- Unauthenticated requests to the [`ciConfig` GraphQL field](../api/graphql/reference/index.md#queryciconfig) are no longer supported.
Before you upgrade to GitLab 15.1, add an [access token](../api/index.md#authentication) to your requests.
The user creating the token must have [permission](../user/permissions.md) to create pipelines in the project.
+- [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.
### 15.0.0
@@ -500,6 +530,12 @@ and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with ap
- The use of encrypted S3 buckets with storage-specific configuration is no longer supported after [removing support for using `background_upload`](removals.md#background-upload-for-object-storage).
- The [certificate-based Kubernetes integration (DEPRECATED)](../user/infrastructure/clusters/index.md#certificate-based-kubernetes-integration-deprecated) is disabled by default, but you can be re-enable it through the [`certificate_based_clusters` feature flag](../administration/feature_flags.md#how-to-enable-and-disable-features-behind-flags) until GitLab 16.0.
- When you use the GitLab Helm Chart project with a custom `serviceAccount`, ensure it has `get` and `list` permissions for the `serviceAccount` and `secret` resources.
+- The [`custom_hooks_dir`](../administration/server_hooks.md#create-global-server-hooks-for-all-repositories) setting for configuring global server hooks is now configured in
+ Gitaly. The previous implementation in GitLab Shell was removed in GitLab 15.0. With this change, global server hooks are stored only inside a subdirectory named after the
+ hook type. Global server hooks can no longer be a single hook file in the root of the custom hooks directory. For example, you must use `<custom_hooks_dir>/<hook_name>.d/*` rather
+ 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.
### 14.10.0
@@ -1040,7 +1076,7 @@ In 13.1.0, you must upgrade to either:
Failure to do so results in internal errors in the Gitaly service in some RPCs due
to the use of the new `--end-of-options` Git flag.
-Additionally, in GitLab 13.1.0, the version of
+Additionally, in GitLab 13.1.0, the version of
[Rails was upgraded from 6.0.3 to 6.0.3.1](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/33454).
The Rails upgrade included a change to CSRF token generation which is
not backwards-compatible - GitLab servers with the new Rails version
@@ -1121,7 +1157,7 @@ for more information.
When [Maintenance mode](../administration/maintenance_mode/index.md) is enabled, users cannot sign in with SSO, SAML, or LDAP.
-Users who were signed in before Maintenance mode was enabled, continue to be signed in. If the administrator who enabled Maintenance mode loses their session, then they can't disable Maintenance mode via the UI. In that case, you can [disable Maintenance mode via the API or Rails console](../administration/maintenance_mode/#disable-maintenance-mode).
+Users who were signed in before Maintenance mode was enabled, continue to be signed in. If the administrator who enabled Maintenance mode loses their session, then they can't disable Maintenance mode via the UI. In that case, you can [disable Maintenance mode via the API or Rails console](../administration/maintenance_mode/index.md#disable-maintenance-mode).
[This bug](https://gitlab.com/gitlab-org/gitlab/-/issues/329261) was fixed in GitLab 14.5.0 and backported into 14.4.3 and 14.3.5.
@@ -1145,6 +1181,27 @@ by a database engine bug that causes a segmentation fault.
Read more [in the issue](https://gitlab.com/gitlab-org/gitlab/-/issues/364763).
+### Geo: Incorrect object storage LFS file deletion on secondary sites in GitLab 15.0.0 to 15.3.2
+
+[Incorrect deletion of object storage files on Geo secondary sites]((https://gitlab.com/gitlab-org/gitlab/-/issues/371397))
+can occur in GitLab 15.0.0 to 15.3.2 in the following situations:
+
+- GitLab-managed object storage replication is disabled, and LFS objects are created while importing a project with object storage enabled.
+- GitLab-managed replication to sync object storage is enabled and subsequently disabled.
+
+This issue is resolved in 15.3.3. Customers who have both LFS enabled and LFS objects being replicated across Geo sites
+should upgrade directly to 15.3.3 to reduce the risk of data loss on secondary sites.
+
+### Geo: LFS transfers redirect to primary from secondary site mid-session in GitLab 15.1.0 to 15.3.2
+
+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 in GitLab 15.1.0 to 15.3.2 when [Geo proxying](../administration/geo/secondary_proxy/index.md) is enabled. Geo proxying is enabled by default in GitLab 15.1 and later.
+
+This issue is resolved in GitLab 15.3.3, so customers with the following configuration should upgrade to 15.3.3 or later:
+
+- LFS is enabled.
+- LFS objects are being replicated across Geo sites.
+- Repositories are being pulled by using a Geo secondary site.
+
## Miscellaneous
- [MySQL to PostgreSQL](mysql_to_postgresql.md) guides you through migrating
diff --git a/doc/update/package/index.md b/doc/update/package/index.md
index 12a8b6f3190..06a56f49cc1 100644
--- a/doc/update/package/index.md
+++ b/doc/update/package/index.md
@@ -330,3 +330,10 @@ To fix this error:
```shell
sudo gitlab-ctl reconfigure
```
+
+1. Hot reload `puma` and `sidekiq` services:
+
+ ```shell
+ sudo gitlab-ctl hup puma
+ sudo gitlab-ctl restart sidekiq
+ ```
diff --git a/doc/update/plan_your_upgrade.md b/doc/update/plan_your_upgrade.md
index 0947bab855f..03bdf161756 100644
--- a/doc/update/plan_your_upgrade.md
+++ b/doc/update/plan_your_upgrade.md
@@ -132,7 +132,7 @@ to your instance and then upgrade it for any relevant features you're using.
[turning on maintenance mode](../administration/maintenance_mode/index.md) during the
upgrade.
- About PostgreSQL:
- - On the top bar, select **Menu > Admin**, and look for the version of
+ - On the top bar, select **Main menu > Admin**, and look for the version of
PostgreSQL you are using.
If [a PostgreSQL upgrade is needed](../administration/package_information/postgresql_versions.md),
account for the relevant
@@ -172,6 +172,9 @@ 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).
+
After updating GitLab, you may have to upgrade
[Elasticsearch if the new version breaks compatibility](../integration/advanced_search/elasticsearch.md#version-requirements).
Updating Elasticsearch is **out of scope for GitLab Support**.
diff --git a/doc/update/removals.md b/doc/update/removals.md
index cdb35b5faa0..9b5596d67f2 100644
--- a/doc/update/removals.md
+++ b/doc/update/removals.md
@@ -31,6 +31,49 @@ For removal reviewers (Technical Writers only):
https://about.gitlab.com/handbook/marketing/blog/release-posts/#update-the-removals-doc
-->
+## Removed in 15.4
+
+### SAST analyzer consolidation and CI/CD template changes
+
+WARNING:
+This is a [breaking change](https://docs.gitlab.com/ee/development/deprecation_guidelines/).
+Review the details carefully before upgrading.
+
+We have replaced the GitLab SAST [analyzers](https://docs.gitlab.com/ee/user/application_security/sast/analyzers/) for certain languages in GitLab 15.4 as part of our long-term strategy to deliver a more consistent user experience, faster scan times, and reduced CI minute usage.
+
+Starting from GitLab 15.4, the [GitLab-managed SAST CI/CD template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml) uses [Semgrep-based scanning](https://docs.gitlab.com/ee/user/application_security/sast/analyzers.html#transition-to-semgrep-based-scanning) instead of the following analyzers:
+
+- [ESLint](https://gitlab.com/gitlab-org/security-products/analyzers/eslint) for JavaScript, TypeScript, React
+- [Gosec](https://gitlab.com/gitlab-org/security-products/analyzers/gosec) for Go
+- [Bandit](https://gitlab.com/gitlab-org/security-products/analyzers/bandit) for Python
+- [SpotBugs](https://gitlab.com/gitlab-org/security-products/analyzers/spotbugs) for Java
+
+We will no longer make any updates to the ESLint-, Gosec-, and Bandit-based analyzers.
+The SpotBugs-based analyzer will continue to be used for Groovy, Kotlin, and Scala scanning.
+
+We won't delete container images previously published for these analyzers, so older versions of the CI/CD template will continue to work.
+
+If you changed the default GitLab SAST configuration, you may need to update your configuration as detailed in the [deprecation issue for this change](https://gitlab.com/gitlab-org/gitlab/-/issues/352554#actions-required).
+
+## Removed in 15.3
+
+### Support for Debian 9
+
+Long term service and support (LTSS) for [Debian 9 Stretch ended in July 2022](https://wiki.debian.org/LTS). Therefore, we will no longer support the Debian 9 distribution for the GitLab package. Users can upgrade to Debian 10 or Debian 11.
+
+### Vulnerability Report sort by State
+
+The ability to sort the Vulnerability Report by the `State` column was disabled and put behind a feature flag in GitLab 14.10 due to a refactor
+of the underlying data model. The feature flag has remained off by default as further refactoring will be required to ensure sorting
+by this value remains performant. Due to very low usage of the `State` column for sorting, the feature flag is instead removed in 15.3 to simplify the codebase and prevent any unwanted performance degradation.
+
+### Vulnerability Report sort by Tool
+
+The ability to sort the Vulnerability Report by the `Tool` column (scan type) was disabled and put behind a feature flag in GitLab 14.10 due to a refactor
+of the underlying data model. The feature flag has remained off by default as further refactoring will be required to ensure sorting
+by this value remains performant. Due to very low usage of the `Tool` column for sorting, the feature flag is instead removed in
+GitLab 15.3 to simplify the codebase and prevent any unwanted performance degradation.
+
## Removed in 15.2
### Support for older browsers
@@ -121,8 +164,8 @@ If you have set a prefix, you can use a workaround to revert to background uploa
gitlab_rails['env'] = { 'GITLAB_LEGACY_BACKGROUND_UPLOADS' => 'artifacts,external_diffs,lfs,uploads,packages,dependency_proxy,terraform_state,pages' }
```
-Prefixes will be supported officially in [GitLab 15.2](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91307).
-This workaround will be dropped, so we encourage migrating to consolidated object storage.
+Support for prefixes was restored in GitLab 15.2 via [this MR](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91307).
+Support for setting `GITLAB_LEGACY_BACKGROUND_UPLOADS` will be removed in GitLab 15.4.
### Container Network and Host Security
diff --git a/doc/update/upgrading_from_source.md b/doc/update/upgrading_from_source.md
index 2df11e8f741..4f54e69d8c9 100644
--- a/doc/update/upgrading_from_source.md
+++ b/doc/update/upgrading_from_source.md
@@ -119,7 +119,7 @@ rm go1.17.10.linux-amd64.tar.gz
To check you are running the minimum required Git version, see
[Git versions](../install/installation.md#software-requirements).
-From GitLab 13.6, we recommend you use the
+From GitLab 13.6, we recommend you use the
[Git version provided by Gitaly](https://gitlab.com/gitlab-org/gitaly/-/issues/2729)
that:
diff --git a/doc/update/zero_downtime.md b/doc/update/zero_downtime.md
index 0abdd769a6b..29bb1c4bcb4 100644
--- a/doc/update/zero_downtime.md
+++ b/doc/update/zero_downtime.md
@@ -53,7 +53,7 @@ migrating data. Background migrations are only added in the monthly releases.
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
+(if the above conditions are met) we require that you
[wait for background migrations to complete](index.md#checking-for-background-migrations-before-upgrading)
between each major/minor release upgrade.
The time necessary to complete these migrations can be reduced by
@@ -492,9 +492,11 @@ You can only upgrade one minor release at a time.
The order of steps is important. While following these steps, make
sure you follow them in the right order, on the correct node.
+### Update the Geo primary site
+
Log in to your **primary** node, executing the following:
-1. Create an empty file at `/etc/gitlab/skip-auto-reconfigure`. This prevents upgrades from running `gitlab-ctl reconfigure`, which by default automatically stops GitLab, runs all database migrations, and restarts GitLab.
+1. Create an empty file at `/etc/gitlab/skip-auto-reconfigure`. This prevents upgrades from running `gitlab-ctl reconfigure`, which by default automatically stops GitLab, runs all database migrations, and restarts GitLab:
```shell
sudo touch /etc/gitlab/skip-auto-reconfigure
@@ -512,7 +514,7 @@ Log in to your **primary** node, executing the following:
sudo gitlab-ctl reconfigure
```
-1. Update the GitLab package
+1. Update the GitLab package:
```shell
# Debian/Ubuntu
@@ -522,18 +524,13 @@ Log in to your **primary** node, executing the following:
sudo yum install gitlab-ee
```
-1. To get the database migrations and latest code in place, run
+1. To get the database migrations and latest code in place, run:
```shell
sudo SKIP_POST_DEPLOYMENT_MIGRATIONS=true gitlab-ctl reconfigure
```
-1. Hot reload `puma` and `sidekiq` services
-
- ```shell
- sudo gitlab-ctl hup puma
- sudo gitlab-ctl restart sidekiq
- ```
+### Update the Geo secondary site
On each **secondary** node, executing the following:
@@ -555,7 +552,7 @@ On each **secondary** node, executing the following:
sudo gitlab-ctl reconfigure
```
-1. Update the GitLab package
+1. Update the GitLab package:
```shell
# Debian/Ubuntu
@@ -565,26 +562,20 @@ On each **secondary** node, executing the following:
sudo yum install gitlab-ee
```
-1. To get the database migrations and latest code in place, run
+1. To get the database migrations and latest code in place, run:
```shell
sudo SKIP_POST_DEPLOYMENT_MIGRATIONS=true gitlab-ctl reconfigure
```
-1. Hot reload `puma`, `sidekiq` and restart `geo-logcursor` services
-
- ```shell
- sudo gitlab-ctl hup puma
- sudo gitlab-ctl restart sidekiq
- sudo gitlab-ctl restart geo-logcursor
- ```
-
-1. Run post-deployment database migrations, specific to the Geo database
+1. Run post-deployment database migrations, specific to the Geo database:
```shell
sudo gitlab-rake db:migrate:geo
```
+### Finalize the update
+
After all **secondary** nodes are updated, finalize
the update on the **primary** node:
@@ -594,6 +585,16 @@ the update on the **primary** node:
sudo gitlab-rake db:migrate
```
+- After the update is finalized on the primary node, hot reload `puma` and
+restart `sidekiq` and `geo-logcursor` services on **all primary and secondary**
+nodes:
+
+ ```shell
+ sudo gitlab-ctl hup puma
+ sudo gitlab-ctl restart sidekiq
+ sudo gitlab-ctl restart geo-logcursor
+ ```
+
After updating all nodes (both **primary** and all **secondaries**), check their status:
- Verify Geo configuration and dependencies
diff --git a/doc/user/admin_area/analytics/dev_ops_reports.md b/doc/user/admin_area/analytics/dev_ops_reports.md
index 2ad18d5f70e..b4471a4602a 100644
--- a/doc/user/admin_area/analytics/dev_ops_reports.md
+++ b/doc/user/admin_area/analytics/dev_ops_reports.md
@@ -12,7 +12,7 @@ from planning to monitoring.
To see DevOps Reports:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Analytics > DevOps Reports**.
## DevOps Score
diff --git a/doc/user/admin_area/analytics/index.md b/doc/user/admin_area/analytics/index.md
index 9315b926acc..9147a48aa8e 100644
--- a/doc/user/admin_area/analytics/index.md
+++ b/doc/user/admin_area/analytics/index.md
@@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
Administrators have access to instance-wide analytics:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Analytics**.
There are several kinds of statistics:
diff --git a/doc/user/admin_area/analytics/usage_trends.md b/doc/user/admin_area/analytics/usage_trends.md
index 5f46908adb0..f21ee627f7f 100644
--- a/doc/user/admin_area/analytics/usage_trends.md
+++ b/doc/user/admin_area/analytics/usage_trends.md
@@ -19,7 +19,7 @@ Usage Trends data refreshes daily.
To view Usage Trends:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Analytics > Usage Trends**.
## Total counts
diff --git a/doc/user/admin_area/appearance.md b/doc/user/admin_area/appearance.md
index 4c3bdde223b..fbc2f8d1827 100644
--- a/doc/user/admin_area/appearance.md
+++ b/doc/user/admin_area/appearance.md
@@ -10,12 +10,12 @@ disqus_identifier: 'https://docs.gitlab.com/ee/customization/branded_login_page.
Several options are available for customizing the appearance of a self-managed instance
of GitLab. To access these settings:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Appearance**.
-## Navigation bar
+## Top bar
-By default, the navigation bar has the GitLab logo, but this can be customized with
+By default, the **top bar** has the GitLab logo, but this can be customized with
any image desired. It is optimized for images 28px high (any width), but any image can be
used (less than 1 MB) and it is automatically resized.
diff --git a/doc/user/admin_area/broadcast_messages.md b/doc/user/admin_area/broadcast_messages.md
index a6e6a839912..b508b71ddac 100644
--- a/doc/user/admin_area/broadcast_messages.md
+++ b/doc/user/admin_area/broadcast_messages.md
@@ -57,7 +57,7 @@ To display messages to users on your GitLab instance, add a broadcast message.
To add a broadcast message:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Messages**.
1. Add the text for the message to the **Message** field. You can style a message's content using Markdown, emoji, and the `a` and `br` HTML tags.
The `br` tag inserts a line break. The `a` HTML tag accepts `class` and `style` attributes with the following CSS properties:
@@ -83,7 +83,7 @@ If you must make changes to a broadcast message, you can edit it.
To edit a broadcast message:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Messages**.
1. From the list of broadcast messages, select the edit button for the message.
1. After making the required changes, select **Update broadcast message**.
@@ -97,7 +97,7 @@ You can delete a broadcast message while it's active.
To delete a broadcast message:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Messages**.
1. From the list of broadcast messages, select the delete button for the message.
diff --git a/doc/user/admin_area/credentials_inventory.md b/doc/user/admin_area/credentials_inventory.md
index 4308b45df78..02c4cd05b23 100644
--- a/doc/user/admin_area/credentials_inventory.md
+++ b/doc/user/admin_area/credentials_inventory.md
@@ -31,7 +31,7 @@ You can also [revoke](#revoke-a-users-personal-access-token) and [delete](#delet
To access the Credentials inventory:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Credentials**.
## Revoke a user's personal access token
diff --git a/doc/user/admin_area/custom_project_templates.md b/doc/user/admin_area/custom_project_templates.md
index c3c580a91c3..0c03652bfb5 100644
--- a/doc/user/admin_area/custom_project_templates.md
+++ b/doc/user/admin_area/custom_project_templates.md
@@ -34,7 +34,7 @@ To set project templates at the group level, see [Custom group-level project tem
To select the group to use as the source for the project templates:
-1. On the top bar, navigate to **Menu > Admin > Settings > Templates**.
+1. On the top bar, navigate to **Main menu > Admin > Settings > Templates**.
1. Expand **Custom project templates**.
1. Select a group to use.
1. Select **Save changes**.
diff --git a/doc/user/admin_area/diff_limits.md b/doc/user/admin_area/diff_limits.md
index b50748ca97e..f646d9f95fd 100644
--- a/doc/user/admin_area/diff_limits.md
+++ b/doc/user/admin_area/diff_limits.md
@@ -33,7 +33,7 @@ set values are presented as **Too large** are cannot be expanded in the UI.
To configure these values:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand **Diff limits**.
1. Enter a value for the diff limit.
diff --git a/doc/user/admin_area/email_from_gitlab.md b/doc/user/admin_area/email_from_gitlab.md
index 52f27ed48e0..09643fc290e 100644
--- a/doc/user/admin_area/email_from_gitlab.md
+++ b/doc/user/admin_area/email_from_gitlab.md
@@ -22,7 +22,7 @@ For information about email notifications originating from GitLab, read
## Sending emails to users from GitLab
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Select **Send email to users**.
diff --git a/doc/user/admin_area/geo_sites.md b/doc/user/admin_area/geo_sites.md
index e577fdf60f1..fecc0e7c541 100644
--- a/doc/user/admin_area/geo_sites.md
+++ b/doc/user/admin_area/geo_sites.md
@@ -11,7 +11,7 @@ You can configure various settings for GitLab Geo sites. For more information, s
On either the primary or secondary site:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Sites**.
## Common settings
@@ -71,7 +71,7 @@ the primary uses the secondary's internal URL to contact it directly.
The internal URL defaults to external URL. To change it:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Sites**.
1. Select **Edit** on the site you want to customize.
1. Edit the internal URL.
diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md
index c689d61ad68..207b7e6f2d8 100644
--- a/doc/user/admin_area/index.md
+++ b/doc/user/admin_area/index.md
@@ -12,7 +12,7 @@ self-managed instances. If you are an administrator, you can access the Admin Ar
by visiting `/admin` on your self-managed instance. You can also access it through
the UI:
-- GitLab versions 14.0 and later: on the top bar, select **Menu > Admin**.
+- GitLab versions 14.0 and later: on the top bar, select **Main menu > Admin**.
- GitLab versions 13.12 and earlier: on the top bar, select the Admin Area icon (**{admin}**).
NOTE:
@@ -47,7 +47,7 @@ The Dashboard provides statistics and system information about the GitLab instan
To access the Dashboard, either:
-- On the top bar, select **Menu > Admin**.
+- On the top bar, select **Main menu > Admin**.
- Visit `/admin` on your self-managed instance.
The Dashboard is the default view of the Admin Area, and is made up of the following sections:
@@ -71,7 +71,7 @@ You can administer all projects in the GitLab instance from the Admin Area's Pro
To access the Projects page:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Projects**.
1. Select the **All**, **Private**, **Internal**, or **Public** tab to list only
projects of that criteria.
@@ -112,7 +112,7 @@ You can combine the filter options. For example, to list only public projects wi
You can administer all users in the GitLab instance from the Admin Area's Users page:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
To list users matching a specific criteria, select one of the following tabs on the **Users** page:
@@ -157,7 +157,7 @@ This allows the administrator to "see what the user sees," and take actions on b
You can impersonate a user in the following ways:
- Through the UI:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. From the list of users, select a user.
1. Select **Impersonate**.
@@ -175,7 +175,7 @@ By default, impersonation is enabled. GitLab can be configured to [disable imper
When using authentication providers, administrators can see the identities for a user:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. From the list of users, select a user.
1. Select **Identities**.
@@ -221,7 +221,7 @@ GitLab billing is based on the number of [**Billable users**](../../subscription
You must be an administrator to manually add emails to users:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users** (`/admin/users`).
1. Locate the user and select them.
1. Select **Edit**.
@@ -237,7 +237,7 @@ The [Cohorts](user_cohorts.md) tab displays the monthly cohorts of new users and
By default, users can create groups. To prevent a user from creating a top level group:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users** (`/admin/users`).
1. Locate the user and select them.
1. Select **Edit**.
@@ -252,7 +252,7 @@ You can administer all groups in the GitLab instance from the Admin Area's Group
To access the Groups page:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Groups**.
For each group, the page displays their name, description, size, number of projects in the group,
@@ -277,7 +277,7 @@ GitLab instance from the Admin Area's Topics page.
To access the Topics page:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Topics**.
For each topic, the page displays its name and the number of projects labeled with the topic.
@@ -288,6 +288,8 @@ To edit a topic, select **Edit** in that topic's row.
To remove a topic, select **Remove** in that topic's row.
+To remove a topic and move all assigned projects to another topic, select **Merge topics**.
+
To search for topics by name, enter your criteria in the search box. The topic search is case
insensitive and applies partial matching.
@@ -302,7 +304,7 @@ You can administer all jobs in the GitLab instance from the Admin Area's Jobs pa
To access the Jobs page:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Jobs**. All jobs are listed, in descending order of job ID.
1. Select the **All** tab to list all jobs. Select the **Pending**, **Running**, or **Finished**
tab to list only jobs of that status.
@@ -328,7 +330,7 @@ You can administer all runners in the GitLab instance from the Admin Area's **Ru
To access the **Runners** page:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Runners**.
#### Search and filter runners
@@ -347,6 +349,22 @@ You can also filter runners by status, type, and tag. To filter:
![Attributes of a runner, with the **Search or filter results...** field active](img/index_runners_search_or_filter_v14_5.png)
+#### Bulk delete runners
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/370241) in GitLab 15.4 [with a flag](../../administration/feature_flags.md) named `admin_runners_bulk_delete`. 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 `admin_runners_bulk_delete`. On GitLab.com, this feature is not available but can be enabled by GitLab.com administrators.
+
+You can delete multiple runners at the same time.
+
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Overview > Runners**.
+1. To the left of the runners you want to delete, select the checkbox.
+ To select all of the runners on the page, select the checkbox above
+ the list.
+1. Select **Delete selected**.
+
#### Runner attributes
For each runner, the following attributes are listed:
@@ -369,7 +387,7 @@ page. For more details, see [Gitaly](../../administration/gitaly/index.md).
To access the **Gitaly Servers** page:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Gitaly Servers**.
For each Gitaly server, the following details are listed:
diff --git a/doc/user/admin_area/license.md b/doc/user/admin_area/license.md
index ac5e5ded859..a5338aa35b8 100644
--- a/doc/user/admin_area/license.md
+++ b/doc/user/admin_area/license.md
@@ -28,7 +28,7 @@ To activate your instance with an activation code:
- Your subscription confirmation email.
- The [Customers Portal](https://customers.gitlab.com/customers/sign_in), on the **Manage Purchases** page.
1. Sign in to your GitLab self-managed instance.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Subscription**.
1. Paste the activation code in **Activation code**.
1. Read and accept the terms of service.
diff --git a/doc/user/admin_area/license_file.md b/doc/user/admin_area/license_file.md
index 99669b2a4d3..352d79ee381 100644
--- a/doc/user/admin_area/license_file.md
+++ b/doc/user/admin_area/license_file.md
@@ -18,7 +18,7 @@ link to the **Add license** page should be displayed.
Otherwise, to add your license:
1. Sign in to GitLab as an administrator.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. In the **Add License** area, add a license by either uploading the file or entering the key.
1. Select the **Terms of Service** checkbox.
@@ -79,7 +79,7 @@ To go back to Free features, [delete all expired licenses](#remove-a-license).
To remove a license from a self-managed instance:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Subscription**.
1. Select **Remove license**.
@@ -89,7 +89,7 @@ Repeat these steps to remove all licenses, including those applied in the past.
To view your license details:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Subscription**.
You can add and view more than one license, but only the latest license in
diff --git a/doc/user/admin_area/merge_requests_approvals.md b/doc/user/admin_area/merge_requests_approvals.md
index e090d4e7f88..38b177766cf 100644
--- a/doc/user/admin_area/merge_requests_approvals.md
+++ b/doc/user/admin_area/merge_requests_approvals.md
@@ -19,7 +19,7 @@ and can no longer be changed:
To enable merge request approval settings for an instance:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **{push-rules}** **Push Rules**, and expand **Merge request approvals**.
1. Choose the required options.
1. Select **Save changes**.
diff --git a/doc/user/admin_area/moderate_users.md b/doc/user/admin_area/moderate_users.md
index ab581cd3aa8..6d632a6bdb6 100644
--- a/doc/user/admin_area/moderate_users.md
+++ b/doc/user/admin_area/moderate_users.md
@@ -40,7 +40,7 @@ sign in.
To view user sign ups pending approval:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Select the **Pending approval** tab.
@@ -50,7 +50,7 @@ A user sign up pending approval can be approved or rejected from the Admin Area.
To approve or reject a user sign up:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Select the **Pending approval** tab.
1. Optional. Select a user.
@@ -75,7 +75,7 @@ administrators can choose to block the user.
Users can be blocked [via an abuse report](review_abuse_reports.md#blocking-users),
by removing them in LDAP, or directly from the Admin Area. To do this:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Optional. Select a user.
1. Select the **{settings}** **User administration** dropdown.
@@ -98,7 +98,7 @@ Users can also be blocked using the [GitLab API](../../api/users.md#block-user).
A blocked user can be unblocked from the Admin Area. To do this:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Select the **Blocked** tab.
1. Optional. Select a user.
@@ -114,7 +114,7 @@ Users can also be unblocked using the [GitLab API](../../api/users.md#unblock-us
The unblock option may be unavailable for LDAP users. To enable the unblock option,
the LDAP identity first needs to be deleted:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Select the **Blocked** tab.
1. Select a user.
@@ -153,7 +153,7 @@ Users are notified about account deactivation if
A user can be deactivated from the Admin Area. To do this:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Optional. Select a user.
1. Select the **{settings}** **User administration** dropdown.
@@ -169,19 +169,21 @@ Users can also be deactivated using the [GitLab API](../../api/users.md#deactiva
### Automatically deactivate dormant users
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/320875) in GitLab 14.0.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/320875) in GitLab 14.0.
+> - Customizable time period [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/336747) in GitLab 15.4
Administrators can enable automatic deactivation of users who either:
- Were created more than a week ago and have not signed in.
-- Have no activity in the last 90 days.
+- Have no activity for a specified period of time (defaults to 90 days).
To do this:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Account and limit** section.
1. Under **Dormant users**, check **Deactivate dormant users after 90 days of inactivity**.
+1. Under **Period of inactivity (days)**, enter a period of time before deactivation.
1. Select **Save changes**.
When this feature is enabled, GitLab runs a job once a day to deactivate the dormant users.
@@ -196,7 +198,7 @@ A deactivated user can be activated from the Admin Area.
To do this:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Select the **Deactivated** tab.
1. Optional. Select a user.
@@ -229,7 +231,7 @@ To block a user and hide their contributions, administrators can ban the user.
Users can be banned using the Admin Area. To do this:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Optional. Select a user.
1. Select the **{settings}** **User administration** dropdown.
@@ -241,7 +243,7 @@ The banned user does not consume a [seat](../../subscriptions/self_managed/index
A banned user can be unbanned using the Admin Area. To do this:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Select the **Banned** tab.
1. Optional. Select a user.
@@ -255,7 +257,7 @@ The user's state is set to active and they consume a
Use the Admin Area to delete users.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Select the **Banned** tab.
1. Optional. Select a user.
@@ -269,7 +271,7 @@ You can only delete a user if there are inherited or direct owners of a group. Y
You can also delete a user and their contributions, such as merge requests, issues, and groups of which they are the only group owner.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Select the **Banned** tab.
1. Optional. Select a user.
diff --git a/doc/user/admin_area/monitoring/background_migrations.md b/doc/user/admin_area/monitoring/background_migrations.md
index 87374849674..092a49dc7e9 100644
--- a/doc/user/admin_area/monitoring/background_migrations.md
+++ b/doc/user/admin_area/monitoring/background_migrations.md
@@ -59,7 +59,8 @@ Use the following database queries to see the state of the current batched backg
```sql
SELECT
- id job_class_name,
+ id,
+ job_class_name,
table_name,
column_name,
job_arguments
diff --git a/doc/user/admin_area/reporting/git_abuse_rate_limit.md b/doc/user/admin_area/reporting/git_abuse_rate_limit.md
index ad3ecfa3a5a..11b0e2403aa 100644
--- a/doc/user/admin_area/reporting/git_abuse_rate_limit.md
+++ b/doc/user/admin_area/reporting/git_abuse_rate_limit.md
@@ -19,7 +19,7 @@ When both flags are enabled, the administrator receives an email when a user is
## Configure Git abuse rate limiting
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Reporting**.
1. Expand **Git abuse rate limit**.
1. Update the Git abuse rate limit settings:
diff --git a/doc/user/admin_area/reporting/spamcheck.md b/doc/user/admin_area/reporting/spamcheck.md
index 2360c1f2899..670b0521732 100644
--- a/doc/user/admin_area/reporting/spamcheck.md
+++ b/doc/user/admin_area/reporting/spamcheck.md
@@ -40,7 +40,7 @@ Spamcheck is only available for package-based installations:
## Configure GitLab to use Spamcheck
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Reporting**.
1. Expand **Spam and Anti-bot Protection**.
1. Update the Spam Check settings:
diff --git a/doc/user/admin_area/review_abuse_reports.md b/doc/user/admin_area/review_abuse_reports.md
index a5e7fcb1b8e..16295f340d8 100644
--- a/doc/user/admin_area/review_abuse_reports.md
+++ b/doc/user/admin_area/review_abuse_reports.md
@@ -16,7 +16,7 @@ reports in the Admin Area.
To receive notifications of new abuse reports by email:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Reporting**.
1. Expand the **Abuse reports** section.
1. Provide an email address and select **Save changes**.
@@ -26,14 +26,14 @@ The notification email address can also be set and retrieved
## Reporting abuse
-To find out more about reporting abuse, see
+To find out more about reporting abuse, see
[abuse reports user documentation](../report_abuse.md).
## Resolving abuse reports
To access abuse reports:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Abuse Reports**.
There are 3 ways to resolve an abuse report, with a button for each method:
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 e33cf4a9082..e8f80cfb40f 100644
--- a/doc/user/admin_area/settings/account_and_limit_settings.md
+++ b/doc/user/admin_area/settings/account_and_limit_settings.md
@@ -16,7 +16,7 @@ the [project limits for existing users](#projects-limit-for-a-user).
To configure the maximum number of projects in personal namespaces for new users:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**, then expand **Account and limit**.
1. Increase or decrease that **Default projects limit** value.
@@ -28,7 +28,7 @@ in their users personal namespace. However, projects can still be created in a g
You can edit a specific user, and change the maximum number of projects this user
can create in their personal namespace:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview** > **Users**.
1. From the list of users, select a user.
1. Select **Edit**.
@@ -39,7 +39,7 @@ can create in their personal namespace:
The maximum file size for attachments in GitLab comments and replies is 10 MB.
To change the maximum attachment size:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**, then expand **Account and limit**.
1. Increase or decrease by changing the value in **Maximum attachment size (MB)**.
@@ -53,7 +53,7 @@ For GitLab.com repository size limits, read [accounts and limit settings](../../
You can change the maximum push size for your instance:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
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)**.
@@ -72,7 +72,7 @@ Use [Git LFS](../../../topics/git/lfs/index.md) to add large files to a reposito
To modify the maximum file size for exports in GitLab:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**, then expand **Account and limit**.
1. Increase or decrease by changing the value in **Maximum export size (MB)**.
@@ -82,7 +82,7 @@ To modify the maximum file size for exports in GitLab:
To modify the maximum file size for imports in GitLab:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**, then expand **Account and limit**.
1. Increase or decrease by changing the value in **Maximum import size (MB)**.
@@ -108,7 +108,7 @@ The default prefix is `glpat-` but administrators can change it.
To change the default global prefix:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Account and limit** section.
1. Fill in the **Personal Access Token prefix** field.
@@ -154,7 +154,7 @@ These settings can be found in:
1. Fill in the **Repository size limit (MB)** field in the **Naming, visibility** section.
1. Select **Save changes**.
- GitLab global settings:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Account and limit** section.
1. Fill in the **Size limit per repository (MB)** field.
@@ -182,7 +182,7 @@ GitLab administrators can choose to customize the session duration (in minutes)
To set a limit on how long these sessions are valid:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Account and limit** section.
1. Fill in the **Session duration for Git operations when 2FA is enabled (minutes)** field.
@@ -209,7 +209,7 @@ there are no restrictions.
To set a lifetime on how long SSH keys are valid:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Account and limit** section.
1. Fill in the **Maximum allowable lifetime for SSH keys (days)** field.
@@ -225,18 +225,6 @@ Once a lifetime for SSH keys is set, GitLab:
NOTE:
When a user's SSH key becomes invalid they can delete and re-add the same key again.
-<!--- start_remove The following content will be removed on remove_date: '2022-08-22' -->
-## Allow expired SSH keys to be used (removed) **(ULTIMATE SELF)**
-
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/250480) in GitLab 13.9.
-> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/320970) in GitLab 14.0.
-> - [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/351963) in GitLab 14.8.
-> - [Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/351963) in GitLab 15.0.
-
-This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/351963) in GitLab 14.8.
-This feature was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/351963) in GitLab 15.0.
-<!--- end_remove -->
-
## Limit the lifetime of access tokens **(ULTIMATE SELF)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3649) in GitLab 12.6.
@@ -257,7 +245,7 @@ there are no restrictions.
To set a lifetime on how long access tokens are valid:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Account and limit** section.
1. Fill in the **Maximum allowable lifetime for access tokens (days)** field.
@@ -291,7 +279,7 @@ To maintain integrity of user details in [Audit Events](../../../administration/
To do this:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**, then expand **Account and limit**.
1. Select the **Prevent users from changing their profile name** checkbox.
diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md
index 638b61f6197..dab3c78d9d1 100644
--- a/doc/user/admin_area/settings/continuous_integration.md
+++ b/doc/user/admin_area/settings/continuous_integration.md
@@ -15,7 +15,7 @@ job artifacts.
To enable (or disable) [Auto DevOps](../../../topics/autodevops/index.md)
for all projects:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Check (or uncheck to disable) the box that says **Default to Auto DevOps pipeline for all projects**.
1. Optionally, set up the [Auto DevOps base domain](../../../topics/autodevops/requirements.md#auto-devops-base-domain)
@@ -32,7 +32,7 @@ If you want to disable it for a specific project, you can do so in
You can set all new projects to have the instance's shared runners available by default.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Continuous Integration and Deployment**.
1. Select the **Enable shared runners for new projects** checkbox.
@@ -48,7 +48,7 @@ limit on the number of [CI/CD minutes](../../../ci/pipelines/cicd_minutes.md) yo
To enable a specific runner for one or more projects:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. From the left sidebar, select **Overview > Runners**.
1. Select the runner you want to edit.
1. In the top right, select **Edit** (**{pencil}**).
@@ -61,7 +61,7 @@ To enable a specific runner for one or more projects:
To display details about the instance's shared runners in all projects'
runner settings:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Continuous Integration and Deployment**.
1. Enter text, including Markdown if you want, in the **Shared runner details** field. For example:
@@ -70,7 +70,7 @@ runner settings:
To view the rendered details:
-1. On the top bar, select **Menu > Project** and select any group or project.
+1. On the top bar, select **Main menu > Projects** or **Main menu > Groups** and find your project or group.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Runners**.
@@ -90,7 +90,7 @@ The value is in MB and the default is 100MB per job. To change it at the:
- Instance level:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD > Continuous Integration and Deployment**.
1. Change the value of **Maximum artifacts size (MB)**.
1. Select **Save changes** for the changes to take effect.
@@ -117,7 +117,7 @@ can be set in the Admin Area of your GitLab instance. The syntax of duration is
described in [`artifacts:expire_in`](../../../ci/yaml/index.md#artifactsexpire_in)
and the default value is `30 days`.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Change the value of default expiration time.
1. Select **Save changes** for the changes to take effect.
@@ -148,7 +148,7 @@ If disabled at the instance level, you cannot enable this per-project.
To disable the setting:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Continuous Integration and Deployment**.
1. Clear the **Keep the latest artifacts for all jobs in the latest successful pipelines** checkbox.
@@ -168,7 +168,7 @@ but persisting the traces and artifacts for auditing purposes.
To set the duration for which the jobs are considered as old and expired:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand the **Continuous Integration and Deployment** section.
1. Set the value of **Archive jobs**.
@@ -185,7 +185,7 @@ For the value set for GitLab.com, see [Scheduled job archiving](../../gitlab_com
To set all new [CI/CD variables](../../../ci/variables/index.md) as
[protected](../../../ci/variables/index.md#protected-cicd-variables) by default:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Select **Protect CI/CD variables by default**.
@@ -196,7 +196,7 @@ To set all new [CI/CD variables](../../../ci/variables/index.md) as
The default CI/CD configuration file and path for new projects can be set in the Admin Area
of your GitLab instance (`.gitlab-ci.yml` if not set):
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Input the new file and path in the **Default CI/CD configuration file** field.
1. Hit **Save changes** for the changes to take effect.
@@ -210,7 +210,7 @@ It is also possible to specify a [custom CI/CD configuration file for a specific
You can configure some [CI/CD limits](../../../administration/instance_limits.md#cicd-limits)
from the Admin Area:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand the **Continuous Integration and Deployment** section.
1. In the **CI/CD limits** section, you can set the following limits:
@@ -232,7 +232,7 @@ walkthrough on how to add one.
To enable or disable the banner:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Select or clear the **Enable pipeline suggestion banner** checkbox.
1. Select **Save changes**.
@@ -267,7 +267,7 @@ in the pipeline editor.
To select a CI/CD template for the required pipeline configuration:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand the **Required pipeline configuration** section.
1. Select a CI/CD template from the dropdown.
@@ -275,13 +275,25 @@ To select a CI/CD template for the required pipeline configuration:
## Package Registry configuration
+### Maven Forwarding **(PREMIUM SELF)**
+
+GitLab administrators can disable the forwarding of Maven requests to [Maven Central](https://search.maven.org/).
+
+To disable forwarding Maven requests:
+
+1. On the top bar, select **Menu > Admin**.
+1. On the left sidebar, select **Settings > CI/CD**.
+1. Expand the **Package Registry** section.
+1. Clear the checkbox **Forward Maven package requests to the Maven Registry if the packages are not found in the GitLab Package Registry**.
+1. Select **Save changes**.
+
### npm Forwarding **(PREMIUM SELF)**
GitLab administrators can disable the forwarding of npm requests to [npmjs.com](https://www.npmjs.com/).
To disable it:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand the **Package Registry** section.
1. Clear the checkbox **Forward npm package requests to the npm Registry if the packages are not found in the GitLab Package Registry**.
@@ -293,7 +305,7 @@ GitLab administrators can disable the forwarding of PyPI requests to [pypi.org](
To disable it:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand the **Package Registry** section.
1. Clear the checkbox **Forward PyPI package requests to the PyPI Registry if the packages are not found in the GitLab Package Registry**.
@@ -305,7 +317,7 @@ GitLab administrators can adjust the maximum allowed file size for each package
To set the maximum file size:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand the **Package Registry** section.
1. Find the package type you would like to adjust.
@@ -325,7 +337,7 @@ By default, all members of a project and group are able to register runners.
To change this:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Runner registration**.
1. Clear the checkbox if you don't want to display runner registration
diff --git a/doc/user/admin_area/settings/deprecated_api_rate_limits.md b/doc/user/admin_area/settings/deprecated_api_rate_limits.md
index d651e445a95..7298d55b051 100644
--- a/doc/user/admin_area/settings/deprecated_api_rate_limits.md
+++ b/doc/user/admin_area/settings/deprecated_api_rate_limits.md
@@ -34,7 +34,7 @@ Prerequisites:
To override the general user and IP rate limits for requests to deprecated API endpoints:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**.
1. Expand **Deprecated API Rate Limits**.
1. Select the check boxes for the types of rate limits you want to enable:
diff --git a/doc/user/admin_area/settings/email.md b/doc/user/admin_area/settings/email.md
index e4fc3b6e6d4..96fab153e85 100644
--- a/doc/user/admin_area/settings/email.md
+++ b/doc/user/admin_area/settings/email.md
@@ -11,7 +11,7 @@ You can customize some of the content in emails sent from your GitLab instance.
## Custom logo
-The logo in the header of some emails can be customized, see the [logo customization section](../appearance.md#navigation-bar).
+The logo in the header of some emails can be customized, see the [logo customization section](../appearance.md#top-bar).
## Include author name in email notification email body **(PREMIUM SELF)**
@@ -21,7 +21,7 @@ address in the body of the email instead.
To include the author's email address in the email body:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences** (`/admin/application_settings/preferences`).
1. Expand **Email**.
1. Select the **Include author name in email notification email body** checkbox.
@@ -33,7 +33,7 @@ GitLab can send email in multipart format (HTML and plain text) or plain text on
To enable multipart email:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences** (`/admin/application_settings/preferences`).
1. Expand **Email**.
1. Select **Enable multipart email**.
@@ -48,7 +48,7 @@ This configuration option sets the email hostname for [private commit emails](..
To change the hostname used in private commit emails:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences** (`/admin/application_settings/preferences`).
1. Expand **Email**.
1. Enter the desired hostname in the **Custom hostname (for private commit emails)** field.
@@ -66,7 +66,7 @@ can be used for legal, auditing, or compliance reasons, for example.
To add additional text to emails:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences** (`/admin/application_settings/preferences`).
1. Expand **Email**.
1. Enter your text in the **Additional text** field.
@@ -78,7 +78,7 @@ GitLab sends email notifications to users when their account has been deactivate
To disable these notifications:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences** (`/admin/application_settings/preferences`).
1. Expand **Email**.
1. Clear the **Enable user deactivation emails** checkbox.
diff --git a/doc/user/admin_area/settings/external_authorization.md b/doc/user/admin_area/settings/external_authorization.md
index d6e6deb0274..9db85eb6ea8 100644
--- a/doc/user/admin_area/settings/external_authorization.md
+++ b/doc/user/admin_area/settings/external_authorization.md
@@ -47,7 +47,7 @@ Alternatively, learn where to install custom certificates by using
The external authorization service can be enabled by an administrator:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand **External authorization**.
1. Complete the fields.
diff --git a/doc/user/admin_area/settings/files_api_rate_limits.md b/doc/user/admin_area/settings/files_api_rate_limits.md
index 544c81e0583..963bca096a4 100644
--- a/doc/user/admin_area/settings/files_api_rate_limits.md
+++ b/doc/user/admin_area/settings/files_api_rate_limits.md
@@ -30,7 +30,7 @@ Prerequisite:
To override the general user and IP rate limits for requests to the Repository files API:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**.
1. Expand **Files API Rate Limits**.
1. Select the check boxes for the types of rate limits you want to enable:
diff --git a/doc/user/admin_area/settings/floc.md b/doc/user/admin_area/settings/floc.md
index dccab461b85..b7d3d8bfa20 100644
--- a/doc/user/admin_area/settings/floc.md
+++ b/doc/user/admin_area/settings/floc.md
@@ -22,10 +22,10 @@ Permissions-Policy: interest-cohort=()
To enable it:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
-1. Expand **Federated Learning of Cohorts**.
-1. Check the box.
+1. Expand **Federated Learning of Cohorts (FLoC)**.
+1. Select the **Participate in FLoC** checkbox.
1. Select **Save changes**.
<!-- ## Troubleshooting
diff --git a/doc/user/admin_area/settings/git_lfs_rate_limits.md b/doc/user/admin_area/settings/git_lfs_rate_limits.md
index c10300baeef..519bbbef4e4 100644
--- a/doc/user/admin_area/settings/git_lfs_rate_limits.md
+++ b/doc/user/admin_area/settings/git_lfs_rate_limits.md
@@ -21,7 +21,7 @@ rate limits.
Git LFS rate limits are disabled by default. If enabled and configured, these limits
supersede the [general user and IP rate limits](user_and_ip_rate_limits.md):
-1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. On the top bar, select **Main menu >** **{admin}** **Admin**.
1. On the left sidebar, select **Settings > Network**.
1. Expand **Git LFS Rate Limits**.
1. Select **Enable authenticated Git LFS request rate limit**.
diff --git a/doc/user/admin_area/settings/gitaly_timeouts.md b/doc/user/admin_area/settings/gitaly_timeouts.md
index 8866a044241..dcf7574136a 100644
--- a/doc/user/admin_area/settings/gitaly_timeouts.md
+++ b/doc/user/admin_area/settings/gitaly_timeouts.md
@@ -11,7 +11,7 @@ configured to make sure that long-running Gitaly calls don't needlessly take up
To access Gitaly timeout settings:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand the **Gitaly timeouts** section.
diff --git a/doc/user/admin_area/settings/help_page.md b/doc/user/admin_area/settings/help_page.md
index 0bf5dc1d37a..38161d0607c 100644
--- a/doc/user/admin_area/settings/help_page.md
+++ b/doc/user/admin_area/settings/help_page.md
@@ -16,7 +16,7 @@ the GitLab sign-in page.
You can add a help message, which is shown at the top of the GitLab `/help` page (for example,
<https://gitlab.com/help>):
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Sign-in and Help page**.
1. In **Additional text to show on the Help page**, enter the information you want to display on `/help`.
@@ -31,10 +31,9 @@ is restricted, `/help` is visible only to signed-in users.
## Add a help message to the sign-in page
-You can add a help message, which is shown on the GitLab sign-in page. The message appears in a new
-section titled **Need Help?**, located below the sign-in page message:
+You can add a help message, which is shown on the GitLab sign-in page. The message appears on the sign-in page:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Sign-in and Help page**.
1. In **Additional text to show on the sign-in page**, enter the information you want to
@@ -47,7 +46,7 @@ You can now see the message on the sign-in page.
GitLab marketing-related entries are occasionally shown on the Help page. To hide these entries:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Sign-in and Help page**.
1. Select the **Hide marketing-related entries from the Help page** checkbox.
@@ -60,7 +59,7 @@ You can specify a custom URL to which users are directed when they:
- Select **Support** from the Help dropdown.
- Select **See our website for help** on the Help page.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Sign-in and Help page**.
1. In the **Support page URL** field, enter the URL.
@@ -81,7 +80,7 @@ You can redirect these `/help` links to either:
- The more navigable and searchable version published at [`docs.gitlab.com`](https://docs.gitlab.com).
- A destination that meets [necessary requirements](#destination-requirements).
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Sign-in and Help page**.
1. In the **Documentation pages URL** field, enter the URL.
diff --git a/doc/user/admin_area/settings/import_export_rate_limits.md b/doc/user/admin_area/settings/import_export_rate_limits.md
index 7d5a928eedf..cfe4f49388e 100644
--- a/doc/user/admin_area/settings/import_export_rate_limits.md
+++ b/doc/user/admin_area/settings/import_export_rate_limits.md
@@ -13,7 +13,7 @@ You can configure the rate limits for imports and exports of projects and groups
To change a rate limit:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**, then expand **Import and export rate limits**.
1. Change the value of any rate limit. The rate limits are per minute per user, not per IP address.
Set to `0` to disable a rate limit.
diff --git a/doc/user/admin_area/settings/incident_management_rate_limits.md b/doc/user/admin_area/settings/incident_management_rate_limits.md
index ed2d707af0a..f9e91d26db5 100644
--- a/doc/user/admin_area/settings/incident_management_rate_limits.md
+++ b/doc/user/admin_area/settings/incident_management_rate_limits.md
@@ -30,7 +30,7 @@ Requests that exceed the limit are logged into `auth.log`.
To set inbound incident management alert limits:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**.
1. Expand **Incident Management Limits**.
1. Select the **Enable Incident Management inbound alert limit** checkbox.
diff --git a/doc/user/admin_area/settings/index.md b/doc/user/admin_area/settings/index.md
index 2e27b213f16..7a63f2059ba 100644
--- a/doc/user/admin_area/settings/index.md
+++ b/doc/user/admin_area/settings/index.md
@@ -19,7 +19,7 @@ read [GitLab.com settings](../../gitlab_com/index.md).
To access the **Admin Area**:
1. Sign in to your GitLab instance as an administrator.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings**, and the group of settings to view:
- [General](#general)
- [Geo](#geo)
@@ -197,6 +197,6 @@ The **Templates** settings contain:
You can change the [Default first day of the week](../../profile/preferences.md)
for the entire GitLab instance:
-1. On the top bar, select **Menu > Admin**.
+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.
diff --git a/doc/user/admin_area/settings/instance_template_repository.md b/doc/user/admin_area/settings/instance_template_repository.md
index 51695ef7fd2..0d63fe5db7f 100644
--- a/doc/user/admin_area/settings/instance_template_repository.md
+++ b/doc/user/admin_area/settings/instance_template_repository.md
@@ -20,7 +20,7 @@ while the project remains secure.
To select a project to serve as the custom template repository:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Templates**.
1. Expand **Templates**
1. From the dropdown list, select the project to use as the template repository.
diff --git a/doc/user/admin_area/settings/package_registry_rate_limits.md b/doc/user/admin_area/settings/package_registry_rate_limits.md
index 1aeb011d880..62cddd2781c 100644
--- a/doc/user/admin_area/settings/package_registry_rate_limits.md
+++ b/doc/user/admin_area/settings/package_registry_rate_limits.md
@@ -30,7 +30,7 @@ no difference in functionality compared to the general user and IP rate limits.
To enable the unauthenticated request rate limit:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**, and expand **Package registry rate limits**.
1. Select **Enable unauthenticated request rate limit**.
@@ -43,7 +43,7 @@ To enable the unauthenticated request rate limit:
To enable the authenticated API request rate limit:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**, and expand **Package registry rate limits**.
1. Select **Enable authenticated API request rate limit**.
diff --git a/doc/user/admin_area/settings/project_integration_management.md b/doc/user/admin_area/settings/project_integration_management.md
index 010ba6a12fc..58afc6a104c 100644
--- a/doc/user/admin_area/settings/project_integration_management.md
+++ b/doc/user/admin_area/settings/project_integration_management.md
@@ -22,7 +22,7 @@ Only the complete settings for an integration can be inherited. Per-field inheri
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2137) in GitLab 13.3 for project-level integrations.
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2543) in GitLab 13.6 for group-level integrations.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Integrations**.
1. Select an integration.
1. Enter configuration details and select **Save changes**.
@@ -54,7 +54,7 @@ integration on all non-configured groups and projects by default.
### Remove an instance-level default setting
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Integrations**.
1. Select an integration.
1. Select **Reset** and confirm.
@@ -68,7 +68,7 @@ Resetting an instance-level default setting removes the integration from all pro
You can view which projects in your instance use custom settings that [override the instance-level default settings](#use-custom-settings-for-a-group-or-project-integration)
for an integration.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Integrations**.
1. Select an integration.
1. Select the **Projects using custom settings** tab.
diff --git a/doc/user/admin_area/settings/push_event_activities_limit.md b/doc/user/admin_area/settings/push_event_activities_limit.md
index 760ce96d987..cd982bb4aa3 100644
--- a/doc/user/admin_area/settings/push_event_activities_limit.md
+++ b/doc/user/admin_area/settings/push_event_activities_limit.md
@@ -26,7 +26,7 @@ the activity feed.
To modify this setting:
- In the Admin Area:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**, then expand **Performance optimization**.
- Through the [Application settings API](../../../api/settings.md#list-of-settings-that-can-be-accessed-via-api-calls)
as `push_event_activities_limit`.
diff --git a/doc/user/admin_area/settings/rate_limit_on_issues_creation.md b/doc/user/admin_area/settings/rate_limit_on_issues_creation.md
index 6c0c15243da..8d08da8246a 100644
--- a/doc/user/admin_area/settings/rate_limit_on_issues_creation.md
+++ b/doc/user/admin_area/settings/rate_limit_on_issues_creation.md
@@ -12,7 +12,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
This setting allows you to rate limit the requests to the issue and epic creation endpoints.
To can change its value:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**.
1. Expand **Issues Rate Limits**.
1. Under **Max requests per minute**, enter the new value.
diff --git a/doc/user/admin_area/settings/rate_limit_on_notes_creation.md b/doc/user/admin_area/settings/rate_limit_on_notes_creation.md
index 0a07cf095ee..f55c2f3bd4a 100644
--- a/doc/user/admin_area/settings/rate_limit_on_notes_creation.md
+++ b/doc/user/admin_area/settings/rate_limit_on_notes_creation.md
@@ -13,7 +13,7 @@ You can configure the per-user rate limit for requests to the note creation endp
To change the note creation rate limit:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**.
1. Expand **Notes rate limit**.
1. In the **Maximum requests per minute** box, enter the new value.
diff --git a/doc/user/admin_area/settings/rate_limit_on_pipelines_creation.md b/doc/user/admin_area/settings/rate_limit_on_pipelines_creation.md
index fce6179f5cf..ba383d74701 100644
--- a/doc/user/admin_area/settings/rate_limit_on_pipelines_creation.md
+++ b/doc/user/admin_area/settings/rate_limit_on_pipelines_creation.md
@@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
You can set a limit so that users and processes can't request more than a certain number of pipelines each minute. This limit can help save resources and improve stability.
-For example, if you set a limit of `10`, and `11` requests are sent to the [trigger API](../../../ci/triggers/) within one minute,
+For example, if you set a limit of `10`, and `11` requests are sent to the [trigger API](../../../ci/triggers/index.md) within one minute,
the eleventh request is blocked. Access to the endpoint is allowed again after one minute.
This limit is:
@@ -26,7 +26,7 @@ Requests that exceed the limit are logged in the `application_json.log` file.
To limit the number of pipeline requests:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**.
1. Expand **Pipelines Rate Limits**.
1. Under **Max requests per minute**, enter a value greater than `0`.
diff --git a/doc/user/admin_area/settings/rate_limit_on_users_api.md b/doc/user/admin_area/settings/rate_limit_on_users_api.md
index 5eed989f73f..9792fd1000d 100644
--- a/doc/user/admin_area/settings/rate_limit_on_users_api.md
+++ b/doc/user/admin_area/settings/rate_limit_on_users_api.md
@@ -13,7 +13,7 @@ You can configure the per user rate limit for requests to [Users API](../../../a
To change the rate limit:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**.
1. Expand **Users API rate limit**.
1. In the **Maximum requests per 10 minutes** text box, enter the new value.
diff --git a/doc/user/admin_area/settings/rate_limits_on_raw_endpoints.md b/doc/user/admin_area/settings/rate_limits_on_raw_endpoints.md
index 028d5e4c2f3..cb3ca0fe756 100644
--- a/doc/user/admin_area/settings/rate_limits_on_raw_endpoints.md
+++ b/doc/user/admin_area/settings/rate_limits_on_raw_endpoints.md
@@ -11,7 +11,7 @@ type: reference
This setting defaults to `300` requests per minute, and allows you to rate limit the requests to raw endpoints:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**.
1. Expand **Performance optimization**.
diff --git a/doc/user/admin_area/settings/sidekiq_job_limits.md b/doc/user/admin_area/settings/sidekiq_job_limits.md
index 750665285b4..c1990572aee 100644
--- a/doc/user/admin_area/settings/sidekiq_job_limits.md
+++ b/doc/user/admin_area/settings/sidekiq_job_limits.md
@@ -17,7 +17,7 @@ Redis. To avoid excessive memory for Redis, we:
To access Sidekiq job size limits:
-1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Sidekiq job size limits**.
1. Adjust the compression threshold or size limit. The compression can
diff --git a/doc/user/admin_area/settings/sign_in_restrictions.md b/doc/user/admin_area/settings/sign_in_restrictions.md
index 7316b1bdbb8..bdffe87b75c 100644
--- a/doc/user/admin_area/settings/sign_in_restrictions.md
+++ b/doc/user/admin_area/settings/sign_in_restrictions.md
@@ -12,7 +12,7 @@ You can use **Sign-in restrictions** to customize authentication restrictions fo
To access sign-in restriction settings:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Sign-in restrictions** section.
@@ -26,7 +26,7 @@ You can restrict the password authentication for web interface and Git over HTTP
- **Git over HTTP(S)**: When this feature is disabled, a [Personal Access Token](../../profile/personal_access_tokens.md)
or LDAP password must be used to authenticate.
-In the event of an external authentication provider outage, use the [GitLab Rails console](../../../administration/operations/rails_console.md) to [re-enable the standard web sign-in form](../../../administration/troubleshooting/gitlab_rails_cheat_sheet.md#re-enable-standard-web-sign-in-form). This configuration can also be changed over the [Application settings REST API](../../../api/settings.md#change-application-settings) while authenticating with an administrator account's personal access token.
+In the event of an external authentication provider outage, use the [GitLab Rails console](../../../administration/operations/rails_console.md) to [re-enable the standard web sign-in form](#re-enable-standard-web-sign-in-form-in-rails-console). This configuration can also be changed over the [Application settings REST API](../../../api/settings.md#change-application-settings) while authenticating with an administrator account's personal access token.
## Admin Mode
@@ -121,21 +121,21 @@ For example, if you include the following information in the noted text box:
To access this text box:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**, and expand the **Sign-in restrictions** section.
```
Your users see the **Custom sign-in text** when they navigate to the sign-in screen for your
GitLab instance.
-<!-- ## Troubleshooting
+## Troubleshooting
-Include any troubleshooting steps that you can foresee. If you know beforehand what issues
-one might have when setting this up, or when something is changed, or on upgrading, it's
-important to describe those, too. Think of things that may go wrong and include them here.
-This is important to minimize requests for support, and to avoid doc comments with
-questions that you know someone might ask.
+### Re-enable standard web sign-in form in rails console
-Each scenario can be a third-level heading, e.g. `### 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. -->
+Re-enable the standard username and password-based sign-in form if it was disabled as a [Sign-in restriction](#password-authentication-enabled).
+
+You can use this method through the [rails console](../../../administration/operations/rails_console.md#starting-a-rails-console-session) when a configured external authentication provider (through SSO or an LDAP configuration) is facing an outage and direct sign-in access to GitLab is required.
+
+```ruby
+Gitlab::CurrentSettings.update!(password_authentication_enabled_for_web: true)
+```
diff --git a/doc/user/admin_area/settings/sign_up_restrictions.md b/doc/user/admin_area/settings/sign_up_restrictions.md
index 56abb3d4701..a0ec964e8db 100644
--- a/doc/user/admin_area/settings/sign_up_restrictions.md
+++ b/doc/user/admin_area/settings/sign_up_restrictions.md
@@ -22,7 +22,7 @@ you do not expect public users to sign up for an account.
To disable sign ups:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**, and expand **Sign-up restrictions**.
1. Clear the **Sign-up enabled** checkbox, then select **Save changes**.
@@ -38,7 +38,7 @@ enabled by default for new GitLab instances. It is only applicable if sign ups a
To require administrator approval for new sign ups:
-1. On the top bar, select **Menu > Admin**.
+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 **Require admin approval for new sign-ups** checkbox, then select **Save changes**.
@@ -58,7 +58,7 @@ their email address before they are allowed to sign in.
To enforce confirmation of the email address used for new sign ups:
-1. On the top bar, select **Menu > Admin**.
+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**.
@@ -76,7 +76,7 @@ user cap, the users in pending approval state are automatically approved in a ba
### Set the user cap number
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand **Sign-up restrictions**.
1. Enter a number in **User cap**.
@@ -86,7 +86,7 @@ New user sign ups are subject to the user cap restriction.
## Remove the user cap
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand **Sign-up restrictions**.
1. Remove the number from **User cap**.
@@ -130,7 +130,7 @@ You can add additional complexity requirements. Changes to password complexity r
Existing passwords are unaffected. To change password complexity requirements:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand **Sign-up restrictions**.
1. Under **Minimum password length (number of characters)**, select additional password complexity requirements. You can require numbers, uppercase letters, lowercase letters,
@@ -159,7 +159,7 @@ reduce the risk of malicious users creating spam accounts with disposable email
To create an email domain allowlist or denylist:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**, and expand **Sign-up restrictions**.
1. For the allowlist, you must enter the list manually. For the denylist, you can enter the list
manually or upload a `.txt` file that contains list entries.
diff --git a/doc/user/admin_area/settings/terms.md b/doc/user/admin_area/settings/terms.md
index 870fa7ad18e..d26ace161bb 100644
--- a/doc/user/admin_area/settings/terms.md
+++ b/doc/user/admin_area/settings/terms.md
@@ -17,7 +17,7 @@ for example `https://gitlab.example.com/-/users/terms`.
To enforce acceptance of a Terms of Service and Privacy Policy:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Terms of Service and Privacy Policy** section.
1. Check the **All users must accept the Terms of Service and Privacy Policy to access GitLab** checkbox.
diff --git a/doc/user/admin_area/settings/third_party_offers.md b/doc/user/admin_area/settings/third_party_offers.md
index 04ec9ed652f..59c100dc016 100644
--- a/doc/user/admin_area/settings/third_party_offers.md
+++ b/doc/user/admin_area/settings/third_party_offers.md
@@ -18,7 +18,7 @@ questions when creating a group.
To toggle the display of customer experience improvement content and third-party offers:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings**, and expand **Customer experience improvement & third-party offers**.
1. Select **Do not display content for customer experience improvement and offers from third parties**.
1. Select **Save changes**.
diff --git a/doc/user/admin_area/settings/usage_statistics.md b/doc/user/admin_area/settings/usage_statistics.md
index afb937494e0..ad018abf809 100644
--- a/doc/user/admin_area/settings/usage_statistics.md
+++ b/doc/user/admin_area/settings/usage_statistics.md
@@ -56,7 +56,7 @@ Registration is not yet required for participation, but may be added in a future
### Enable registration features
1. Sign in as a user with administrator access.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**.
1. Expand the **Usage statistics** section.
1. If not enabled, select the **Enable Service Ping** checkbox.
@@ -113,7 +113,7 @@ If your GitLab instance is behind a proxy, set the appropriate
To enable or disable Service Ping and version check:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**.
1. Expand **Usage statistics**.
1. Select or clear the **Enable version check** and **Enable Service Ping** checkboxes.
@@ -165,7 +165,7 @@ the Admin Area:
You can view the exact JSON payload sent to GitLab Inc. in the Admin Area. To view the payload:
1. Sign in as a user with administrator access.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**.
1. Expand the **Usage statistics** section.
1. Select **Preview payload**.
@@ -183,7 +183,7 @@ or if the Service Ping [cron job](../../../development/service_ping/index.md#how
To upload the payload manually:
1. Sign in as a user with administrator access.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Service** usage data.
1. Select **Download payload**.
1. Save the JSON file.
diff --git a/doc/user/admin_area/settings/user_and_ip_rate_limits.md b/doc/user/admin_area/settings/user_and_ip_rate_limits.md
index a35cbe5381a..86676e4a63e 100644
--- a/doc/user/admin_area/settings/user_and_ip_rate_limits.md
+++ b/doc/user/admin_area/settings/user_and_ip_rate_limits.md
@@ -31,7 +31,7 @@ counted as web traffic.
To enable the unauthenticated request rate limit:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**, and expand **User and IP rate limits**.
1. Select **Enable unauthenticated API request rate limit**.
@@ -44,7 +44,7 @@ To enable the unauthenticated request rate limit:
To enable the unauthenticated request rate limit:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**, and expand **User and IP rate limits**.
1. Select **Enable unauthenticated web request rate limit**.
@@ -57,7 +57,7 @@ To enable the unauthenticated request rate limit:
To enable the authenticated API request rate limit:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**, and expand **User and IP rate limits**.
1. Select **Enable authenticated API request rate limit**.
@@ -70,7 +70,7 @@ To enable the authenticated API request rate limit:
To enable the unauthenticated request rate limit:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**, and expand **User and IP rate limits**.
1. Select **Enable authenticated web request rate limit**.
@@ -88,7 +88,7 @@ plain-text body, which by default is `Retry later`.
To use a custom response:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**, and expand **User and IP rate limits**.
1. In the **Plain-text response to send to clients that hit a rate limit** text box,
add the plain-text response message.
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 118d375da01..87c24f04a1c 100644
--- a/doc/user/admin_area/settings/visibility_and_access_controls.md
+++ b/doc/user/admin_area/settings/visibility_and_access_controls.md
@@ -13,7 +13,7 @@ specific controls on branches, projects, snippets, groups, and more.
To access the visibility and access control options:
1. Sign in to GitLab as a user with Administrator access level.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
@@ -24,7 +24,7 @@ Instance-level protections for project creation define which roles can
on the instance. To alter which roles have permission to create projects:
1. Sign in to GitLab as a user with Administrator access level.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. For **Default project creation protection**, select the desired roles:
@@ -40,7 +40,7 @@ on the instance. To alter which roles have permission to create projects:
By default both administrators and anyone with the **Owner** role can delete a project. To restrict project deletion to only administrators:
1. Sign in to GitLab as a user with administrator access.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. Scroll to:
@@ -78,7 +78,7 @@ deleted groups will remain restorable within a retention period.
To configure delayed project deletion:
1. Sign in to GitLab as a user with administrator access.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. Scroll to:
@@ -112,7 +112,7 @@ Alternatively, projects that are marked for removal can be deleted immediately.
To set the default [visibility levels for new projects](../../public_access.md):
1. Sign in to GitLab as a user with Administrator access level.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. Select the desired default project visibility:
@@ -127,7 +127,7 @@ To set the default [visibility levels for new projects](../../public_access.md):
To set the default visibility levels for new [snippets](../../snippets.md):
1. Sign in to GitLab as a user with Administrator access level.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. Select the desired default snippet visibility.
@@ -141,7 +141,7 @@ For more details on snippet visibility, read
To set the default visibility levels for new groups:
1. Sign in to GitLab as a user with Administrator access level.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. Select the desired default group visibility:
@@ -158,7 +158,7 @@ For more details on group visibility, see
To restrict visibility levels for projects, snippets, and selected pages:
1. Sign in to GitLab as a user with Administrator access level.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. In the **Restricted visibility levels** section, select the desired visibility levels to restrict.
@@ -177,7 +177,7 @@ For more details on project visibility, see
You can specify from which hosting sites users can [import their projects](../../project/import/index.md):
1. Sign in to GitLab as a user with Administrator access level.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. Select each of **Import sources** to allow.
@@ -189,7 +189,7 @@ To enable the export of
[projects and their data](../../project/settings/import_export.md#export-a-project-and-its-data):
1. Sign in to GitLab as a user with Administrator access level.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. Select **Project export enabled**.
@@ -205,7 +205,7 @@ The GitLab restrictions apply at the application level.
To specify the enabled Git access protocols:
1. Sign in to GitLab as a user with Administrator access level.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. Select the desired Git access protocols:
@@ -277,13 +277,8 @@ work in every repository. They can only be re-enabled by an administrator user o
## Configure globally-allowed IP address ranges
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87579) in GitLab 15.1 [with a flag](../../../administration/feature_flags.md) named `group_ip_restrictions_allow_global`. 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 `group_ip_restrictions_allow_global`.
-On GitLab.com, this feature is available.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87579) in GitLab 15.1 [with a flag](../../../administration/feature_flags.md) named `group_ip_restrictions_allow_global`. Disabled by default.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/366445) in GitLab 15.4. [Feature flag `group_ip_restrictions_allow_global`](https://gitlab.com/gitlab-org/gitlab/-/issues/366445) removed.
This setting allows you to set IP address ranges to be combined with group-level IP allowlists.
It helps administrators prevent aspects of the GitLab installation from being blocked
@@ -296,7 +291,7 @@ daemon to be unable to fetch artifacts from the pipeline runs.
To add a IP address range to the group-level allowlist:
1. Sign in to GitLab as a user with Administrator access level.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Visibility and access controls** section.
1. In **Globally-allowed IP ranges**, provide a value.
diff --git a/doc/user/admin_area/user_cohorts.md b/doc/user/admin_area/user_cohorts.md
index 28376923810..816e629f496 100644
--- a/doc/user/admin_area/user_cohorts.md
+++ b/doc/user/admin_area/user_cohorts.md
@@ -10,7 +10,7 @@ You can analyze your users' GitLab activities over time.
To view user cohorts:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Select the **Cohorts** tab.
diff --git a/doc/user/analytics/ci_cd_analytics.md b/doc/user/analytics/ci_cd_analytics.md
index f4075c3420b..b92d68222f5 100644
--- a/doc/user/analytics/ci_cd_analytics.md
+++ b/doc/user/analytics/ci_cd_analytics.md
@@ -32,7 +32,7 @@ View pipeline duration history:
To view CI/CD analytics:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > CI/CD Analytics**.
## View deployment frequency chart **(ULTIMATE)**
@@ -50,7 +50,7 @@ The deployment frequency chart is available for groups and projects.
To view the deployment frequency chart:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > CI/CD Analytics**.
1. Select the **Deployment frequency** tab.
@@ -68,11 +68,11 @@ merge requests to be deployed to a production environment. This chart is availab
- For time periods in which no merge requests were deployed, the charts render a
red, dashed line.
- lead time for changes is one of the four [DORA metrics](index.md#devops-research-and-assessment-dora-key-metrics) that DevOps teams use for measuring excellence in software delivery.
+ Lead time for changes is one of the four [DORA metrics](index.md#devops-research-and-assessment-dora-key-metrics) that DevOps teams use for measuring excellence in software delivery.
To view the lead time for changes chart:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > CI/CD Analytics**.
1. Select the **Lead time** tab.
@@ -88,7 +88,7 @@ Time to restore service is one of the four [DORA metrics](index.md#devops-resear
To view the time to restore service chart:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > CI/CD Analytics**.
1. Select the **Time to restore service** tab.
@@ -104,6 +104,6 @@ Change failure rate is one of the four [DORA metrics](index.md#devops-research-a
To view the change failure rate chart:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > CI/CD Analytics**.
1. Select the **Change failure rate** tab.
diff --git a/doc/user/analytics/code_review_analytics.md b/doc/user/analytics/code_review_analytics.md
index dc02512702a..dd985b6b090 100644
--- a/doc/user/analytics/code_review_analytics.md
+++ b/doc/user/analytics/code_review_analytics.md
@@ -6,53 +6,32 @@ info: To determine the technical writer assigned to the Stage/Group associated w
---
-# Code Review Analytics **(PREMIUM)**
+# Code review analytics **(PREMIUM)**
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/38062) in GitLab 12.7.
-> - Moved to GitLab Premium in 13.9.
+> Moved to GitLab Premium in 13.9.
-Use Code Review Analytics to view the longest-running reviews among open merge
-requests, and:
+Use code review analytics to view review metrics per merge request and
+make improvements to your code review process:
-- Take action on individual merge requests.
-- Reduce overall cycle time.
+- A high number of comments or commits may indicate:
+ - The code is too complex.
+ - Authors who require more training.
+- A long review time may indicate:
+ - Types of work that move slower than other types.
+ - Opportunities to accelerate your development cycle.
+- Fewer comments and approvers may indicate staffing requirements.
-Code Review Analytics is available to users with at least the Reporter role, and displays a table of open merge requests that have at least one non-author comment. The review time is measured from the time the first non-author comment was submitted.
+Code review analytics displays a table of open merge requests that have at least one non-author comment.
+The review time is measured from when the first non-author comment was submitted.
-NOTE:
-Initially, no data appears. Data is populated as users comment on open merge requests.
+## View code review analytics
-![Code Review Analytics](img/code_review_analytics_v13_11.png "List of code reviews; oldest review first.")
+Prerequisite:
-The table is sorted by:
+- You must have at least the Reporter role.
-- **Review time**: Helping you to quickly find the longest-running reviews which may need intervention
- or to be broken down into smaller parts.
-- Other columns: Display the author, approvers, comment count, and line change (-/+) counts.
+To view code review analytics:
-## View Code Review Analytics
-
-To view Code Review Analytics:
-
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Code Review**.
1. Filter merge requests by milestone and label.
-
-## Use cases
-
-This feature is designed for [development team leaders](https://about.gitlab.com/handbook/product/personas/#delaney-development-team-lead)
-and others who want to understand broad code review dynamics, and identify patterns to explain them.
-
-You can use Code Review Analytics to:
-
-- Expose your team's unique challenges with code review.
-- Identify improvements that might substantially accelerate your development cycle.
-- Your team agrees that code review is moving too slow.
-- The [Value Stream Analytics feature](value_stream_analytics.md) shows that reviews are your team's most time-consuming step.
-- Analyze the patterns and trends of different types of work that are moving slow.
-
-For example:
-
-- Lots of comments or commits? Maybe the code is too complex.
-- A particular author is involved? Maybe more training is required.
-- Few comments and approvers? Maybe your team is understaffed.
diff --git a/doc/user/analytics/img/code_review_analytics_v13_11.png b/doc/user/analytics/img/code_review_analytics_v13_11.png
deleted file mode 100644
index b559b934a89..00000000000
--- a/doc/user/analytics/img/code_review_analytics_v13_11.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/analytics/issue_analytics.md b/doc/user/analytics/issue_analytics.md
index 62fff443073..86ae45a02b8 100644
--- a/doc/user/analytics/issue_analytics.md
+++ b/doc/user/analytics/issue_analytics.md
@@ -15,7 +15,7 @@ prior.
To access the chart:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Issue**.
Hover over each bar to see the total number of issues.
diff --git a/doc/user/analytics/merge_request_analytics.md b/doc/user/analytics/merge_request_analytics.md
index 038b2f0c97e..4f40575a4ef 100644
--- a/doc/user/analytics/merge_request_analytics.md
+++ b/doc/user/analytics/merge_request_analytics.md
@@ -28,7 +28,7 @@ You must have at least the Reporter role to view merge request analytics.
To view merge request analytics:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Merge request**.
## View the number of merge requests in a date range
@@ -38,7 +38,7 @@ To view merge request analytics:
To view the number of merge requests merged during a specific date range:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Merge request**.
1. Optional. Filter results:
1. Select the filter bar.
@@ -63,6 +63,6 @@ created and when it's merged. Closed and un-merged merge requests are not includ
To view **Mean time to merge**:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Merge request**. The **Mean time to merge** number
is shown on the dashboard.
diff --git a/doc/user/analytics/productivity_analytics.md b/doc/user/analytics/productivity_analytics.md
index f8b28ead155..f0841dc979b 100644
--- a/doc/user/analytics/productivity_analytics.md
+++ b/doc/user/analytics/productivity_analytics.md
@@ -26,7 +26,7 @@ Prerequisite:
- You must have at least the Reporter role for the group.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Productivity**.
1. Optional. Filter results:
1. Select a project from the dropdown list.
@@ -44,7 +44,7 @@ Use the following charts in productivity analytics to view the velocity of your
merge requests to merge after they were created.
- **Trendline**: number of merge requests that were merged in a specific time period.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Productivity**.
To filter time metrics:
@@ -56,7 +56,7 @@ To filter time metrics:
To view commit statistics for your group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Productivity**.
1. Under the **Trendline** scatterplot, view the commit statistics:
- The left histogram shows the number of hours between commits, comments, and merges.
diff --git a/doc/user/analytics/repository_analytics.md b/doc/user/analytics/repository_analytics.md
index 5fd4a567b58..7723da7397d 100644
--- a/doc/user/analytics/repository_analytics.md
+++ b/doc/user/analytics/repository_analytics.md
@@ -30,7 +30,7 @@ Commits in a project's [wiki](../project/wiki/index.md#track-wiki-events) are no
To review repository analytics for a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Repository**.
## How repository analytics chart data is updated
diff --git a/doc/user/analytics/value_stream_analytics.md b/doc/user/analytics/value_stream_analytics.md
index a71136628cf..d5756c5f822 100644
--- a/doc/user/analytics/value_stream_analytics.md
+++ b/doc/user/analytics/value_stream_analytics.md
@@ -32,7 +32,7 @@ Value stream analytics is also available for [groups](../group/value_stream_anal
To view value stream analytics for your project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Value stream**.
1. To view metrics for a particular stage, select a stage below the **Filter results** text box.
1. Optional. Filter the results:
@@ -61,7 +61,7 @@ Value stream analytics shows the median time spent by issues or merge requests i
To view the median time spent in each stage:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Value stream**.
1. Optional. Filter the results:
1. Select the **Filter results** text box.
@@ -81,7 +81,7 @@ Value stream analytics shows the lead time and cycle time for issues in your pro
To view the lead time and cycle time for issues:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Value stream**.
1. Optional. Filter the results:
1. Select the **Filter results** text box.
@@ -101,7 +101,7 @@ Lead time for changes is the median duration between when a merge request is mer
To view the lead time for changes for merge requests in your project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Value stream**.
1. Optional. Filter the results:
1. Select the **Filter results** text box.
@@ -113,19 +113,26 @@ To view the lead time for changes for merge requests in your project:
The **Lead Time for Changes** metrics display below the **Filter results** text box.
-## View number of successful deployments **(PREMIUM)**
+## View number of successful deployments **(FREE)**
-To view deployment metrics, you must have a
+Prerequisites:
+
+- To view deployment metrics, you must have a
[production environment configured](../../ci/environments/index.md#deployment-tier-of-environments).
-Value stream analytics shows the following deployment metrics for your project:
+Value stream analytics shows the following deployment metrics for your project within the specified date range:
- Deploys: The number of successful deployments in the date range.
- Deployment Frequency: The average number of successful deployments per day in the date range.
+If you have a GitLab Premium or Ultimate subscription:
+
+- The number of successful deployments is calculated with DORA data.
+- The data is filtered based on environment and environment tier.
+
To view deployment metrics for your project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Value stream**.
1. Optional. Filter the results:
1. Select the **Filter results** text box.
diff --git a/doc/user/application_security/api_fuzzing/index.md b/doc/user/application_security/api_fuzzing/index.md
index 80e4700c34c..8e371ed4dc6 100644
--- a/doc/user/application_security/api_fuzzing/index.md
+++ b/doc/user/application_security/api_fuzzing/index.md
@@ -39,6 +39,7 @@ or other scanners) during a scan could cause inaccurate results.
You can run a Web API fuzzing scan using the following methods:
- [OpenAPI Specification](#openapi-specification) - version 2, and 3.
+- [GraphQL Schema](#graphql-schema)
- [HTTP Archive](#http-archive-har) (HAR)
- [Postman Collection](#postman-collection) - version 2.0 or 2.1
@@ -76,6 +77,7 @@ To enable Web API fuzzing:
- For manual configuration instructions, see the respective section, depending on the API type:
- [OpenAPI Specification](#openapi-specification)
+ - [GraphQL Schema](#graphql-schema)
- [HTTP Archive (HAR)](#http-archive-har)
- [Postman Collection](#postman-collection)
- Otherwise, see [Web API fuzzing configuration form](#web-api-fuzzing-configuration-form).
@@ -95,7 +97,7 @@ a YAML snippet that you can paste in your GitLab CI/CD configuration.
To generate an API Fuzzing configuration snippet:
-1. On the top bar, select **Menu > Projects** and find your project.
+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 **API Fuzzing** row, select **Enable API Fuzzing**.
1. Complete the fields. For details see [Available CI/CD variables](#available-cicd-variables).
@@ -262,7 +264,7 @@ Example `.gitlab-ci.yml` file using a HAR file:
FUZZAPI_TARGET_URL: http://test-deployment/
```
-This is a minimal configuration for API fuzzing. From here you can:
+This example is a minimal configuration for API fuzzing. From here you can:
- [Run your first scan](#running-your-first-scan).
- [Add authentication](#authentication).
@@ -270,6 +272,118 @@ This is a minimal configuration for API fuzzing. From here you can:
For details of API fuzzing configuration options, see [Available CI/CD variables](#available-cicd-variables).
+### GraphQL Schema
+
+> Support for GraphQL Schema was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352780) in GitLab 15.4.
+
+GraphQL is a query language for your API and an alternative to REST APIs.
+API Fuzzing supports testing GraphQL endpoints multiple ways:
+
+- Test using the GraphQL Schema. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352780) in GitLab 15.4.
+- Test using a recording (HAR) of GraphQL queries.
+- Test using a Postman Collection containing GraphQL queries.
+
+This section documents how to test using a GraphQL schema. The GraphQL schema support in
+API Fuzzing is able to query the schema from endpoints that support introspection.
+Introspection is enabled by default to allow tools like GraphiQL to work.
+
+#### API Fuzzing scanning with a GraphQL endpoint URL
+
+The GraphQL support in API Fuzzing is able to query a GraphQL endpoint for the schema.
+
+NOTE:
+The GraphQL endpoint must support introspection queries for this method to work correctly.
+
+To configure API Fuzzing to use an GraphQL endpoint URL that provides information about the target API to test:
+
+1. [Include](../../../ci/yaml/index.md#includetemplate)
+ the [`API-Fuzzing.gitlab-ci.yml` template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml) in your `.gitlab-ci.yml` file.
+
+1. Provide the GraphQL endpoint path, for example `/api/graphql`. Specify the path by adding the `FUZZAPI_GRAPHQL` variable.
+
+1. The target API instance's base URL is also required. Provide it by using the `FUZZAPI_TARGET_URL`
+ variable or an `environment_url.txt` file.
+
+ Adding the URL in an `environment_url.txt` file at your project's root is great for testing in
+ dynamic environments. See the [dynamic environment solutions](#dynamic-environment-solutions) section of our documentation for more information.
+
+Complete example configuration of using a GraphQL endpoint URL:
+
+```yaml
+stages:
+ - fuzz
+
+include:
+ - template: API-Fuzzing.gitlab-ci.yml
+
+apifuzzer_fuzz:
+ variables:
+ FUZZAPI_GRAPHQL: /api/graphql
+ FUZZAPI_TARGET_URL: http://test-deployment/
+```
+
+This example is a minimal configuration for API Fuzzing. From here you can:
+
+- [Run your first scan](#running-your-first-scan).
+- [Add authentication](#authentication).
+- Learn how to [handle false positives](#handling-false-positives).
+
+#### API Fuzzing with a GraphQL Schema file
+
+To configure API Fuzzing to use a GraphQl schema file that provides information about the target API to test:
+
+1. [Include](../../../ci/yaml/index.md#includetemplate)
+ the [`API-Fuzzing.gitlab-ci.yml` template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml) in your `.gitlab-ci.yml` file.
+
+1. Provide the GraphQL endpoint path, for example `/api/graphql`. Specify the path by adding the `FUZZAPI_GRAPHQL` variable.
+
+1. Provide the location of the GraphQL schema file. You can provide the location as a file path
+ or URL. Specify the location by adding the `FUZZAPI_GRAPHQL_SCHEMA` variable.
+
+1. The target API instance's base URL is also required. Provide it by using the `FUZZAPI_TARGET_URL`
+ variable or an `environment_url.txt` file.
+
+ Adding the URL in an `environment_url.txt` file at your project's root is great for testing in
+ dynamic environments. See the [dynamic environment solutions](#dynamic-environment-solutions) section of our documentation for more information.
+
+Complete example configuration of using an GraphQL schema file:
+
+```yaml
+stages:
+ - fuzz
+
+include:
+ - template: API-Fuzzing.gitlab-ci.yml
+
+apifuzzer_fuzz:
+ variables:
+ FUZZAPI_GRAPHQL: /api/graphql
+ FUZZAPI_GRAPHQL_SCHEMA: test-api-graphql.schema
+ FUZZAPI_TARGET_URL: http://test-deployment/
+```
+
+Complete example configuration of using an GraphQL schema file URL:
+
+```yaml
+stages:
+ - fuzz
+
+include:
+ - template: API-Fuzzing.gitlab-ci.yml
+
+apifuzzer_fuzz:
+ variables:
+ FUZZAPI_GRAPHQL: /api/graphql
+ FUZZAPI_GRAPHQL_SCHEMA: http://file-store/files/test-api-graphql.schema
+ FUZZAPI_TARGET_URL: http://test-deployment/
+```
+
+This example is a minimal configuration for API Fuzzing. From here you can:
+
+- [Run your first scan](#running-your-first-scan).
+- [Add authentication](#authentication).
+- Learn how to [handle false positives](#handling-false-positives).
+
### Postman Collection
The [Postman API Client](https://www.postman.com/product/api-client/) is a popular tool that
@@ -344,34 +458,264 @@ For details of API fuzzing configuration options, see [Available CI/CD variables
#### Postman variables
+> - Support for Postman Environment file format was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1.
+> - Support for multiple variable files was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1.
+> - Support for Postman variable scopes: Global and Environment was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1.
+
+##### Variables in Postman Client
+
Postman allows the developer to define placeholders that can be used in different parts of the
-requests. These placeholders are called variables, as explained in the Postman documentation,
-[Using variables](https://learning.postman.com/docs/sending-requests/variables/).
+requests. These placeholders are called variables, as explained in [using variables](https://learning.postman.com/docs/sending-requests/variables/).
You can use variables to store and reuse values in your requests and scripts. For example, you can
edit the collection to add variables to the document:
![Edit collection variable tab View](img/api_fuzzing_postman_collection_edit_variable.png)
+Or alternatively, you can add variables in an environment:
+
+![Edit environment variables View](img/api_fuzzing_postman_environment_edit_variable.png)
+
You can then use the variables in sections such as URL, headers, and others:
![Edit request using variables View](img/api_fuzzing_postman_request_edit.png)
-Variables can be defined at different [scopes](https://learning.postman.com/docs/sending-requests/variables/#variable-scopes)
-(for example, Global, Collection, Environment, Local, and Data). In this example, they're defined at
-the Environment scope:
+Postman has grown from a basic client tool with a nice UX experience to a more complex ecosystem that allows testing APIs with scripts, creating complex collections that trigger secondary requests, and setting variables along the way. Not every feature in the Postman ecosystem is supported. For example, scripts are not supported. The main focus of the Postman support is to ingest Postman Collection definitions that are used by the Postman Client and their related variables defined in the workspace, environments, and the collections themselves.
-![Edit environment variables View](img/api_fuzzing_postman_environment_edit_variable.png)
+Postman allows creating variables in different scopes. Each scope has a different level of visibility in the Postman tools. For example, you can create a variable in a _global environment_ scope that is seen by every operation definition and workspace. You can also create a variable in a specific _environment_ scope that is only visible and used when that specific environment is selected for use. Some scopes are not always available, for example in the Postman ecosystem you can create requests in the Postman Client, these requests do not have a _local_ scope, but test scripts do.
-When you export a Postman collection, only Postman collection variables are exported into the
-Postman file. For example, Postman does not export environment-scoped variables into the Postman
-file.
+Variable scopes in Postman can be a daunting topic and not everyone is familiar with it. We strongly recommend that you read [Variable Scopes](https://learning.postman.com/docs/sending-requests/variables/#variable-scopes) from Postman documentation before moving forward.
-By default, the API fuzzer uses the Postman file to resolve Postman variable values. If a JSON file
-is set in a GitLab CI/CD variable `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`, then the JSON
-file takes precedence to get Postman variable values.
+As mentioned above, there are different variable scopes, and each of them has a purpose and can be used to provide more flexibility to your Postman document. There is an important note on how values for variables are computed, as per Postman documentation:
-WARNING:
-Although Postman can export environment variables into a JSON file, the format is not compatible with the JSON expected by `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`.
+> If a variable with the same name is declared in two different scopes, the value stored in the variable with narrowest scope is used. For example, if there is a global variable named `username` and a local variable named `username`, the local value is used when the request runs.
+
+The following is a summary of the variable scopes supported by the Postman Client and API Fuzzing:
+
+- **Global Environment (Global) scope** is a special pre-defined environment that is available throughout a workspace. We can also refer to the _global environment_ scope as the _global_ scope. The Postman Client allows exporting the global environment into a JSON file, which can be used with API Fuzzing.
+- **Environment scope** is a named group of variables created by a user in the Postman Client.
+The Postman Client supports a single active environment along with the global environment. The variables defined in an active user-created environment take precedence over variables defined in the global environment. The Postman Client allows exporting your environment into a JSON file, which can be used with API Fuzzing.
+- **Collection scope** is a group of variables declared in a given collection. The collection variables are available to the collection where they have been declared and the nested requests or collections. Variables defined in the collection scope take precedence over the _global environment_ scope and also the _environment_ scope.
+The Postman Client can export one or more collections into a JSON file, this JSON file contains selected collections, requests, and collection variables.
+- **API Fuzzing Scope** is a new scope added by API Fuzzing to allow users to provide extra variables, or override variables defined in other supported scopes. This scope is not supported by Postman. The _API Fuzzing Scope_ variables are provided using a [custom JSON file format](#api-fuzzing-scope-custom-json-file-format).
+ - Override values defined in the environment or collection
+ - Defining variables from scripts
+ - Define a single row of data from the unsupported _data scope_
+- **Data scope** is a group of variables in which their name and values come from JSON or CSV files. A Postman collection runner like [Newman](https://learning.postman.com/docs/running-collections/using-newman-cli/command-line-integration-with-newman/) or [Postman Collection Runner](https://learning.postman.com/docs/running-collections/intro-to-collection-runs/) executes the requests in a collection as many times as entries have the JSON or CSV file. A good use case for these variables is to automate tests using scripts in Postman.
+API Fuzzing does **not** support reading data from a CSV or JSON file.
+- **Local scope** are variables that are defined in Postman scripts. API Fuzzing does **not** support Postman scripts and by extension, variables defined in scripts. You can still provide values for the script-defined variables by defining them in one of the supported scopes, or our custom JSON format.
+
+Not all scopes are supported by API Fuzzing and variables defined in scripts are not supported. The following table is sorted by broadest scope to narrowest scope.
+
+| Scope |Postman | API Fuzzing | Comment |
+| ------------------ |:---------:|:------------:| :--------|
+| Global Environment | Yes | Yes | Special pre-defined environment |
+| Environment | Yes | Yes | Named environments |
+| Collection | Yes | Yes | Defined in your postman collection |
+| API Fuzzing Scope | No | Yes | Custom scope added by API Fuzzing |
+| Data | Yes | No | External files in CSV or JSON format |
+| Local | Yes | No | Variables defined in scripts |
+
+For more details on how to define variables and export variables in different scopes, see:
+
+- [Defining collection variables](https://learning.postman.com/docs/sending-requests/variables/#defining-collection-variables)
+- [Defining environment variables](https://learning.postman.com/docs/sending-requests/variables/#defining-environment-variables)
+- [Defining global variables](https://learning.postman.com/docs/sending-requests/variables/#defining-global-variables)
+
+##### Exporting from Postman Client
+
+The Postman Client lets you export different file formats, for instance, you can export a Postman collection or a Postman environment.
+The exported environment can be the global environment (which is always available) or can be any custom environment you previously have created. When you export a Postman Collection, it may contain only declarations for _collection_ and _local_ scoped variables; _environment_ scoped variables are not included.
+
+To get the declaration for _environment_ scoped variables, you have to export a given environment at the time. Each exported file only includes variables from the selected environment.
+
+For more details on exporting variables in different supported scopes, see:
+
+- [Exporting collections](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-collections)
+- [Exporting environments](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments)
+- [Downloading global environments](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments)
+
+##### API Fuzzing Scope, custom JSON file format
+
+Our custom JSON file format is a JSON object where each object property represents a variable name and the property value represents the variable value. This file can be created using your favorite text editor, or it can be produced by an earlier job in your pipeline.
+
+This example defines two variables `base_url` and `token` in the API Fuzzing scope:
+
+```json
+{
+ "base_url": "http://127.0.0.1/",
+ "token": "Token 84816165151"
+}
+```
+
+##### Using scopes with API Fuzzing
+
+The scopes: _global_, _environment_, _collection_, and _GitLab API Fuzzing_ are supported in [GitLab 15.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356312). GitLab 15.0 and earlier, supports only the _collection_, and _GitLab API Fuzzing_ scopes.
+
+The following table provides a quick reference for mapping scope files/URLs to API Fuzzing configuration variables:
+
+| Scope | How to Provide |
+| ------------------ | --------------- |
+| Global Environment | FUZZAPI_POSTMAN_COLLECTION_VARIABLES |
+| Environment | FUZZAPI_POSTMAN_COLLECTION_VARIABLES |
+| Collection | FUZZAPI_POSTMAN_COLLECTION |
+| API Fuzzing Scope | FUZZAPI_POSTMAN_COLLECTION_VARIABLES |
+| Data | Not supported |
+| Local | Not supported |
+
+The Postman Collection document automatically includes any _collection_ scoped variables. The Postman Collection is provided with the configuration variable `FUZZAPI_POSTMAN_COLLECTION`. This variable can be set to a single [exported Postman collection](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-collections).
+
+Variables from other scopes are provided through the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES` configuration variable. The configuration variable supports a comma (`,`) delimited file list in [GitLab 15.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356312). GitLab 15.0 and earlier, supports only one single file. The order of the files provided is not important as the files provide the needed scope information.
+
+The configuration variable `FUZZAPI_POSTMAN_COLLECTION_VARIABLES` can be set to:
+
+- [Exported Global environment](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments)
+- [Exported environments](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments)
+- [API Fuzzing Custom JSON format](#api-fuzzing-scope-custom-json-file-format)
+
+##### Undefined Postman variables
+
+There is a chance that API Fuzzing engine does not find all variables references that your Postman collection file is using. Some cases can be:
+
+- You are using _data_ or _local_ scoped variables, and as stated previously these scopes are not supported by API Fuzzing. Thus, assuming the values for these variables have not been provided through [the API Fuzzing scope](#api-fuzzing-scope-custom-json-file-format), then the values of the _data_ and _local_ scoped variables are undefined.
+- A variable name was typed incorrectly, and the name does not match the defined variable.
+- Postman Client supports a new dynamic variable that is not supported by API Fuzzing.
+
+When possible, API Fuzzing follows the same behavior as the Postman Client does when dealing with undefined variables. The text of the variable reference remains the same, and there is no text substitution. The same behavior also applies to any unsupported dynamic variables.
+
+For example, if a request definition in the Postman Collection references the variable `{{full_url}}` and the variable is not found it is left unchanged with the value `{{full_url}}`.
+
+##### Dynamic Postman variables
+
+In addition to variables that a user can define at various scope levels, Postman has a set of pre-defined variables called _dynamic_ variables. The [_dynamic_ variables](https://learning.postman.com/docs/writing-scripts/script-references/variables-list/) are already defined and their name is prefixed with a dollar sign (`$`), for instance, `$guid`. _Dynamic_ variables can be used like any other variable, and in the Postman Client, they produce random values during the request/collection run.
+
+An important difference between API Fuzzing and Postman is that API Fuzzing returns the same value for each usage of the same dynamic variables. This differs from the Postman Client behavior which returns a random value on each use of the same dynamic variable. In other words, API Fuzzing uses static values for dynamic variables while Postman uses random values.
+
+The supported dynamic variables during the scanning process are:
+
+| Variable | Value |
+| ----------- | ----------- |
+| `$guid` | `611c2e81-2ccb-42d8-9ddc-2d0bfa65c1b4` |
+| `$isoTimestamp` | `2020-06-09T21:10:36.177Z` |
+| `$randomAbbreviation` | `PCI` |
+| `$randomAbstractImage` | `http://no-a-valid-host/640/480/abstract` |
+| `$randomAdjective` | `auxiliary` |
+| `$randomAlphaNumeric` | `a` |
+| `$randomAnimalsImage` | `http://no-a-valid-host/640/480/animals` |
+| `$randomAvatarImage` | `https://no-a-valid-host/path/to/some/image.jpg` |
+| `$randomBankAccount` | `09454073` |
+| `$randomBankAccountBic` | `EZIAUGJ1` |
+| `$randomBankAccountIban` | `MU20ZPUN3039684000618086155TKZ` |
+| `$randomBankAccountName` | `Home Loan Account` |
+| `$randomBitcoin` | `3VB8JGT7Y4Z63U68KGGKDXMLLH5` |
+| `$randomBoolean` | `true` |
+| `$randomBs` | `killer leverage schemas` |
+| `$randomBsAdjective` | `viral` |
+| `$randomBsBuzz` | `repurpose` |
+| `$randomBsNoun` | `markets` |
+| `$randomBusinessImage` | `http://no-a-valid-host/640/480/business` |
+| `$randomCatchPhrase` | `Future-proofed heuristic open architecture` |
+| `$randomCatchPhraseAdjective` | `Business-focused` |
+| `$randomCatchPhraseDescriptor` | `bandwidth-monitored` |
+| `$randomCatchPhraseNoun` | `superstructure` |
+| `$randomCatsImage` | `http://no-a-valid-host/640/480/cats` |
+| `$randomCity` | `Spinkahaven` |
+| `$randomCityImage` | `http://no-a-valid-host/640/480/city` |
+| `$randomColor` | `fuchsia` |
+| `$randomCommonFileExt` | `wav` |
+| `$randomCommonFileName` | `well_modulated.mpg4` |
+| `$randomCommonFileType` | `audio` |
+| `$randomCompanyName` | `Grady LLC` |
+| `$randomCompanySuffix` | `Inc` |
+| `$randomCountry` | `Kazakhstan` |
+| `$randomCountryCode` | `MD` |
+| `$randomCreditCardMask` | `3622` |
+| `$randomCurrencyCode` | `ZMK` |
+| `$randomCurrencyName` | `Pound Sterling` |
+| `$randomCurrencySymbol` | `£` |
+| `$randomDatabaseCollation` | `utf8_general_ci` |
+| `$randomDatabaseColumn` | `updatedAt` |
+| `$randomDatabaseEngine` | `Memory` |
+| `$randomDatabaseType` | `text` |
+| `$randomDateFuture` | `Tue Mar 17 2020 13:11:50 GMT+0530 (India Standard Time)` |
+| `$randomDatePast` | `Sat Mar 02 2019 09:09:26 GMT+0530 (India Standard Time)` |
+| `$randomDateRecent` | `Tue Jul 09 2019 23:12:37 GMT+0530 (India Standard Time)` |
+| `$randomDepartment` | `Electronics` |
+| `$randomDirectoryPath` | `/usr/local/bin` |
+| `$randomDomainName` | `trevor.info` |
+| `$randomDomainSuffix` | `org` |
+| `$randomDomainWord` | `jaden` |
+| `$randomEmail` | `Iva.Kovacek61@no-a-valid-host.com` |
+| `$randomExampleEmail` | `non-a-valid-user@example.net` |
+| `$randomFashionImage` | `http://no-a-valid-host/640/480/fashion` |
+| `$randomFileExt` | `war` |
+| `$randomFileName` | `neural_sri_lanka_rupee_gloves.gdoc` |
+| `$randomFilePath` | `/home/programming_chicken.cpio` |
+| `$randomFileType` | `application` |
+| `$randomFirstName` | `Chandler` |
+| `$randomFoodImage` | `http://no-a-valid-host/640/480/food` |
+| `$randomFullName` | `Connie Runolfsdottir` |
+| `$randomHexColor` | `#47594a` |
+| `$randomImageDataUri` | `data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20baseProfile%3D%22full%22%20width%3D%22undefined%22%20height%3D%22undefined%22%3E%20%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22grey%22%2F%3E%20%20%3Ctext%20x%3D%220%22%20y%3D%2220%22%20font-size%3D%2220%22%20text-anchor%3D%22start%22%20fill%3D%22white%22%3Eundefinedxundefined%3C%2Ftext%3E%20%3C%2Fsvg%3E` |
+| `$randomImageUrl` | `http://no-a-valid-host/640/480` |
+| `$randomIngverb` | `navigating` |
+| `$randomInt` | `494` |
+| `$randomIP` | `241.102.234.100` |
+| `$randomIPV6` | `dbe2:7ae6:119b:c161:1560:6dda:3a9b:90a9` |
+| `$randomJobArea` | `Mobility` |
+| `$randomJobDescriptor` | `Senior` |
+| `$randomJobTitle` | `International Creative Liaison` |
+| `$randomJobType` | `Supervisor` |
+| `$randomLastName` | `Schneider` |
+| `$randomLatitude` | `55.2099` |
+| `$randomLocale` | `ny` |
+| `$randomLongitude` | `40.6609` |
+| `$randomLoremLines` | `Ducimus in ut mollitia.\nA itaque non.\nHarum temporibus nihil voluptas.\nIste in sed et nesciunt in quaerat sed.` |
+| `$randomLoremParagraph` | `Ab aliquid odio iste quo voluptas voluptatem dignissimos velit. Recusandae facilis qui commodi ea magnam enim nostrum quia quis. Nihil est suscipit assumenda ut voluptatem sed. Esse ab voluptas odit qui molestiae. Rem est nesciunt est quis ipsam expedita consequuntur.` |
+| `$randomLoremParagraphs` | `Voluptatem rem magnam aliquam ab id aut quaerat. Placeat provident possimus voluptatibus dicta velit non aut quasi. Mollitia et aliquam expedita sunt dolores nam consequuntur. Nam dolorum delectus ipsam repudiandae et ipsam ut voluptatum totam. Nobis labore labore recusandae ipsam quo.` |
+| `$randomLoremSentence` | `Molestias consequuntur nisi non quod.` |
+| `$randomLoremSentences` | `Et sint voluptas similique iure amet perspiciatis vero sequi atque. Ut porro sit et hic. Neque aspernatur vitae fugiat ut dolore et veritatis. Ab iusto ex delectus animi. Voluptates nisi iusto. Impedit quod quae voluptate qui.` |
+| `$randomLoremSlug` | `eos-aperiam-accusamus, beatae-id-molestiae, qui-est-repellat` |
+| `$randomLoremText` | `Quisquam asperiores exercitationem ut ipsum. Aut eius nesciunt. Et reiciendis aut alias eaque. Nihil amet laboriosam pariatur eligendi. Sunt ullam ut sint natus ducimus. Voluptas harum aspernatur soluta rem nam.` |
+| `$randomLoremWord` | `est` |
+| `$randomLoremWords` | `vel repellat nobis` |
+| `$randomMACAddress` | `33:d4:68:5f:b4:c7` |
+| `$randomMimeType` | `audio/vnd.vmx.cvsd` |
+| `$randomMonth` | `February` |
+| `$randomNamePrefix` | `Dr.` |
+| `$randomNameSuffix` | `MD` |
+| `$randomNatureImage` | `http://no-a-valid-host/640/480/nature` |
+| `$randomNightlifeImage` | `http://no-a-valid-host/640/480/nightlife` |
+| `$randomNoun` | `bus` |
+| `$randomPassword` | `t9iXe7COoDKv8k3` |
+| `$randomPeopleImage` | `http://no-a-valid-host/640/480/people` |
+| `$randomPhoneNumber` | `700-008-5275` |
+| `$randomPhoneNumberExt` | `27-199-983-3864` |
+| `$randomPhrase` | `You can't program the monitor without navigating the mobile XML program!` |
+| `$randomPrice` | `531.55` |
+| `$randomProduct` | `Pizza` |
+| `$randomProductAdjective` | `Unbranded` |
+| `$randomProductMaterial` | `Steel` |
+| `$randomProductName` | `Handmade Concrete Tuna` |
+| `$randomProtocol` | `https` |
+| `$randomSemver` | `7.0.5` |
+| `$randomSportsImage` | `http://no-a-valid-host/640/480/sports` |
+| `$randomStreetAddress` | `5742 Harvey Streets` |
+| `$randomStreetName` | `Kuhic Island` |
+| `$randomTransactionType` | `payment` |
+| `$randomTransportImage` | `http://no-a-valid-host/640/480/transport` |
+| `$randomUrl` | `https://no-a-valid-host.net` |
+| `$randomUserAgent` | `Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.9.8; rv:15.6) Gecko/20100101 Firefox/15.6.6` |
+| `$randomUserName` | `Jarrell.Gutkowski` |
+| `$randomUUID` | `6929bb52-3ab2-448a-9796-d6480ecad36b` |
+| `$randomVerb` | `navigate` |
+| `$randomWeekday` | `Thursday` |
+| `$randomWord` | `withdrawal` |
+| `$randomWords` | `Samoa Synergistic sticky copying Grocery` |
+| `$timestamp` | `1562757107` |
+
+##### Example: Global Scope
+
+In this example, [the _global_ scope is exported](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments) from the Postman Client as `global-scope.json` and provided to API Fuzzing through the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES` configuration variable.
Here is an example of using `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`:
@@ -384,21 +728,171 @@ include:
variables:
FUZZAPI_PROFILE: Quick-10
- FUZZAPI_POSTMAN_COLLECTION: postman-collection_serviceA.json
+ FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
+ FUZZAPI_POSTMAN_COLLECTION_VARIABLES: global-scope.json
+ FUZZAPI_TARGET_URL: http://test-deployment/
+```
+
+##### Example: Environment Scope
+
+In this example, [the _environment_ scope is exported](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments) from the Postman Client as `environment-scope.json` and provided to API Fuzzing through the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES` configuration variable.
+
+Here is an example of using `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`:
+
+```yaml
+stages:
+ - fuzz
+
+include:
+ - template: API-Fuzzing.gitlab-ci.yml
+
+variables:
+ FUZZAPI_PROFILE: Quick
+ FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
+ FUZZAPI_POSTMAN_COLLECTION_VARIABLES: environment-scope.json
+ FUZZAPI_TARGET_URL: http://test-deployment/
+```
+
+##### Example: Collection Scope
+
+The _collection_ scope variables are included in the exported Postman Collection file and provided through the `FUZZAPI_POSTMAN_COLLECTION` configuration variable.
+
+Here is an example of using `FUZZAPI_POSTMAN_COLLECTION`:
+
+```yaml
+stages:
+ - fuzz
+
+include:
+ - template: API-Fuzzing.gitlab-ci.yml
+
+variables:
+ FUZZAPI_PROFILE: Quick
+ FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
FUZZAPI_TARGET_URL: http://test-deployment/
FUZZAPI_POSTMAN_COLLECTION_VARIABLES: variable-collection-dictionary.json
```
-The file `variable-collection-dictionary.json` is a JSON document. This JSON is an object with
-key-value pairs for properties. The keys are the variables' names, and the values are the variables'
+##### Example: API Fuzzing Scope
+
+The API Fuzzing Scope is used for two main purposes, defining _data_ and _local_ scope variables that are not supported by API Fuzzing, and changing the value of an existing variable defined in another scope. The API Fuzzing Scope is provided through the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES` configuration variable.
+
+Here is an example of using `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`:
+
+```yaml
+stages:
+ - fuzz
+
+include:
+ - template: API-Fuzzing.gitlab-ci.yml
+
+variables:
+ FUZZAPI_PROFILE: Quick
+ FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
+ FUZZAPI_POSTMAN_COLLECTION_VARIABLES: api-fuzzing-scope.json
+ FUZZAPI_TARGET_URL: http://test-deployment/
+```
+
+The file `api-fuzzing-scope.json` uses our [custom JSON file format](#api-fuzzing-scope-custom-json-file-format). This JSON is an object with key-value pairs for properties. The keys are the variables' names, and the values are the variables'
values. For example:
- ```json
- {
- "base_url": "http://127.0.0.1/",
- "token": "Token 84816165151"
- }
- ```
+```json
+{
+ "base_url": "http://127.0.0.1/",
+ "token": "Token 84816165151"
+}
+```
+
+##### Example: Multiple Scopes
+
+In this example, a _global_ scope, _environment_ scope, and _collection_ scope are configured. The first step is to export our various scopes.
+
+- [Export the _global_ scope](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments) as `global-scope.json`
+- [Export the _environment_ scope](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments) as `environment-scope.json`
+- Export the Postman Collection which includes the _collection_ scope as `postman-collection.json`
+
+The Postman Collection is provided using the `FUZZAPI_POSTMAN_COLLECTION` variable, while the other scopes are provided using the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`. API Fuzzing can identify which scope the provided files match using data provided in each file.
+
+```yaml
+stages:
+ - fuzz
+
+include:
+ - template: API-Fuzzing.gitlab-ci.yml
+
+variables:
+ FUZZAPI_PROFILE: Quick
+ FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
+ FUZZAPI_POSTMAN_COLLECTION_VARIABLES: global-scope.json,environment-scope.json
+ FUZZAPI_TARGET_URL: http://test-deployment/
+```
+
+##### Example: Changing a Variables Value
+
+When using exported scopes, it's often the case that the value of a variable must be changed for use with API Fuzzing. For example, a _collection_ scoped variable might contain a variable named `api_version` with a value of `v2`, while your test needs a value of `v1`. Instead of modifying the exported collection to change the value, the API Fuzzing scope can be used to change its value. This works because the _API Fuzzing_ scope takes precedence over all other scopes.
+
+The _collection_ scope variables are included in the exported Postman Collection file and provided through the `FUZZAPI_POSTMAN_COLLECTION` configuration variable.
+
+The API Fuzzing Scope is provided through the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES` configuration variable, but first, we must create the file.
+The file `api-fuzzing-scope.json` uses our [custom JSON file format](#api-fuzzing-scope-custom-json-file-format). This JSON is an object with key-value pairs for properties. The keys are the variables' names, and the values are the variables'
+values. For example:
+
+```json
+{
+ "api_version": "v1"
+}
+```
+
+Our CI definition:
+
+```yaml
+stages:
+ - fuzz
+
+include:
+ - template: API-Fuzzing.gitlab-ci.yml
+
+variables:
+ FUZZAPI_PROFILE: Quick
+ FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
+ FUZZAPI_POSTMAN_COLLECTION_VARIABLES: api-fuzzing-scope.json
+ FUZZAPI_TARGET_URL: http://test-deployment/
+```
+
+##### Example: Changing a Variables Value with Multiple Scopes
+
+When using exported scopes, it's often the case that the value of a variable must be changed for use with API Fuzzing. For example, an _environment_ scope might contain a variable named `api_version` with a value of `v2`, while your test needs a value of `v1`. Instead of modifying the exported file to change the value, the API Fuzzing scope can be used. This works because the _API Fuzzing_ scope takes precedence over all other scopes.
+
+In this example, a _global_ scope, _environment_ scope, _collection_ scope, and _API Fuzzing_ scope are configured. The first step is to export and create our various scopes.
+
+- [Export the _global_ scope](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments) as `global-scope.json`
+- [Export the _environment_ scope](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments) as `environment-scope.json`
+- Export the Postman Collection which includes the _collection_ scope as `postman-collection.json`
+
+The API Fuzzing scope is used by creating a file `api-fuzzing-scope.json` using our [custom JSON file format](#api-fuzzing-scope-custom-json-file-format). This JSON is an object with key-value pairs for properties. The keys are the variables' names, and the values are the variables'
+values. For example:
+
+```json
+{
+ "api_version": "v1"
+}
+```
+
+The Postman Collection is provided using the `FUZZAPI_POSTMAN_COLLECTION` variable, while the other scopes are provided using the `FUZZAPI_POSTMAN_COLLECTION_VARIABLES`. API Fuzzing can identify which scope the provided files match using data provided in each file.
+
+```yaml
+stages:
+ - fuzz
+
+include:
+ - template: API-Fuzzing.gitlab-ci.yml
+
+variables:
+ FUZZAPI_PROFILE: Quick
+ FUZZAPI_POSTMAN_COLLECTION: postman-collection.json
+ FUZZAPI_POSTMAN_COLLECTION_VARIABLES: global-scope.json,environment-scope.json,api-fuzzing-scope.json
+ FUZZAPI_TARGET_URL: http://test-deployment/
+```
## API fuzzing configuration
@@ -420,21 +914,23 @@ provide a script that performs an authentication flow or calculates the token.
#### HTTP Basic Authentication
[HTTP basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
-is an authentication method built in to the HTTP protocol and used in conjunction with
+is an authentication method built into the HTTP protocol and used in conjunction with
[transport layer security (TLS)](https://en.wikipedia.org/wiki/Transport_Layer_Security).
-To use HTTP basic authentication, two CI/CD variables are added to your `.gitlab-ci.yml` file:
-- `FUZZAPI_HTTP_USERNAME`: The username for authentication.
-- `FUZZAPI_HTTP_PASSWORD`: The password for authentication.
+We recommended that you [create a CI/CD variable](../../../ci/variables/index.md#custom-cicd-variables)
+for the password (for example, `TEST_API_PASSWORD`), and set it to be masked. You can create CI/CD
+variables from the GitLab project's page at **Settings > CI/CD**, in the **Variables** section.
+Because of the [limitations on masked variables](../../../ci/variables/index.md#mask-a-cicd-variable),
+you should Base64-encode the password before adding it as a variable.
-For the password, we recommended that you [create a CI/CD variable](../../../ci/variables/index.md#custom-cicd-variables)
-(for example, `TEST_API_PASSWORD`) set to the password. You can create CI/CD variables from the
-GitLab projects page at **Settings > CI/CD**, in the **Variables** section. Use that variable
-as the value for `FUZZAPI_HTTP_PASSWORD`:
+Finally, add two CI/CD variables to your `.gitlab-ci.yml` file:
+
+- `FUZZAPI_HTTP_USERNAME`: The username for authentication.
+- `FUZZAPI_HTTP_PASSWORD_BASE64`: The Base64-encoded password for authentication.
```yaml
stages:
- - fuzz
+ - fuzz
include:
- template: API-Fuzzing.gitlab-ci.yml
@@ -444,9 +940,13 @@ variables:
FUZZAPI_HAR: test-api-recording.har
FUZZAPI_TARGET_URL: http://test-deployment/
FUZZAPI_HTTP_USERNAME: testuser
- FUZZAPI_HTTP_PASSWORD: $TEST_API_PASSWORD
+ FUZZAPI_HTTP_PASSWORD_BASE64: $TEST_API_PASSWORD
```
+#### Raw password
+
+If you do not want to Base64-encode the password (or if you are using GitLab 15.3 or earlier) you can provide the raw password `FUZZAPI_HTTP_PASSWORD`, instead of using `FUZZAPI_HTTP_PASSWORD_BASE64`.
+
#### Bearer Tokens
Bearer tokens are used by several different authentication mechanisms, including OAuth2 and JSON Web
@@ -493,7 +993,7 @@ Follow these steps to provide the bearer token with `FUZZAPI_OVERRIDES_ENV`:
##### Token generated at test runtime
If the bearer token must be generated and doesn't expire during testing, you can provide to API
-fuzzing a file containing the token. A prior stage and job, or part of the API fuzzing job, can
+fuzzing with a file containing the token. A prior stage and job, or part of the API fuzzing job, can
generate this file.
API fuzzing expects to receive a JSON file with the following structure:
@@ -605,7 +1105,10 @@ profile increases as the number of tests increases.
|[`FUZZAPI_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. |
|[`FUZZAPI_OPENAPI_MEDIA_TYPES`](#openapi-specification) | Colon (`:`) separated media types accepted for testing. Default is disabled. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/333304) in GitLab 14.10. |
|[`FUZZAPI_HAR`](#http-archive-har) | HTTP Archive (HAR) file. |
+|[`FUZZAPI_GRAPHQL`](#graphql-schema) | Path to GraphQL endpoint, for example `/api/graphql`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352780) in GitLab 15.4. |
+|[`FUZZAPI_GRAPHQL_SCHEMA`](#graphql-schema) | A URL or filename for a GraphQL schema in JSON format. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352780) in GitLab 15.4. |
|[`FUZZAPI_POSTMAN_COLLECTION`](#postman-collection) | Postman Collection file. |
+|[`FUZZAPI_POSTMAN_COLLECTION_VARIABLES`](#postman-variables) | Path to a JSON file to extract Postman variable values. The support for comma-separated (`,`) files was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1. |
|[`FUZZAPI_POSTMAN_COLLECTION_VARIABLES`](#postman-variables) | Path to a JSON file to extract Postman variable values. |
|[`FUZZAPI_OVERRIDES_FILE`](#overrides) | Path to a JSON file containing overrides. |
|[`FUZZAPI_OVERRIDES_ENV`](#overrides) | JSON string containing headers to override. |
@@ -616,6 +1119,7 @@ profile increases as the number of tests increases.
|[`FUZZAPI_OVERRIDES_INTERVAL`](#overrides) | How often to run overrides command in seconds. Defaults to `0` (once). |
|[`FUZZAPI_HTTP_USERNAME`](#http-basic-authentication) | Username for HTTP authentication. |
|[`FUZZAPI_HTTP_PASSWORD`](#http-basic-authentication) | Password for HTTP authentication. |
+|[`FUZZAPI_HTTP_PASSWORD_BASE64`](#http-basic-authentication) | Password for HTTP authentication, Base64-encoded. [Introduced](https://gitlab.com/gitlab-org/security-products/analyzers/api-fuzzing-src/-/merge_requests/702) in GitLab 15.4. |
### Overrides
@@ -1062,21 +1566,21 @@ This example excludes the `/auth` resource. This does not exclude child resource
```yaml
variables:
- FUZZAPI_EXCLUDE_PATHS=/auth
+ FUZZAPI_EXCLUDE_PATHS: /auth
```
To exclude `/auth`, and child resources (`/auth/child`), we use a wildcard.
```yaml
variables:
- FUZZAPI_EXCLUDE_PATHS=/auth*
+ FUZZAPI_EXCLUDE_PATHS: /auth*
```
To exclude multiple paths we can use the `;` character. In this example we exclude `/auth*` and `/v1/*`.
```yaml
variables:
- FUZZAPI_EXCLUDE_PATHS=/auth*;/v1/*
+ FUZZAPI_EXCLUDE_PATHS: /auth*;/v1/*
```
### Exclude parameters
@@ -1336,7 +1840,15 @@ Each value in `FUZZAPI_EXCLUDE_URLS` is a regular expression. Characters such as
The following example excludes the URL `http://target/api/auth` and its child resources.
```yaml
+stages:
+ - fuzz
+
+include:
+ - template: API-Fuzzing.gitlab-ci.yml
+
variables:
+ FUZZAPI_TARGET_URL: http://target/
+ FUZZAPI_OPENAPI: test-api-specification.json
FUZZAPI_EXCLUDE_URLS: http://target/api/auth
```
@@ -1345,7 +1857,15 @@ variables:
To exclude the URLs `http://target/api/buy` and `http://target/api/sell` but allowing to scan their child resources, for instance: `http://target/api/buy/toy` or `http://target/api/sell/chair`. You could use the value `http://target/api/buy/$,http://target/api/sell/$`. This value is using two regular expressions, each of them separated by a `,` character. Hence, it contains `http://target/api/buy$` and `http://target/api/sell$`. In each regular expression, the trailing `$` character points out where the matching URL should end.
```yaml
+stages:
+ - fuzz
+
+include:
+ - template: API-Fuzzing.gitlab-ci.yml
+
variables:
+ FUZZAPI_TARGET_URL: http://target/
+ FUZZAPI_OPENAPI: test-api-specification.json
FUZZAPI_EXCLUDE_URLS: http://target/api/buy/$,http://target/api/sell/$
```
@@ -1354,7 +1874,15 @@ variables:
In order to exclude the URLs: `http://target/api/buy` and `http://target/api/sell`, and their child resources. To provide multiple URLs we use the `,` character as follows:
```yaml
+stages:
+ - fuzz
+
+include:
+ - template: API-Fuzzing.gitlab-ci.yml
+
variables:
+ FUZZAPI_TARGET_URL: http://target/
+ FUZZAPI_OPENAPI: test-api-specification.json
FUZZAPI_EXCLUDE_URLS: http://target/api/buy,http://target/api/sell
```
@@ -1363,7 +1891,15 @@ variables:
In order to exclude exactly `https://target/api/v1/user/create` and `https://target/api/v2/user/create` or any other version (`v3`,`v4`, and more). We could use `https://target/api/v.*/user/create$`, in the previous regular expression `.` indicates any character and `*` indicates zero or more times, additionally `$` indicates that the URL should end there.
```yaml
+stages:
+ - fuzz
+
+include:
+ - template: API-Fuzzing.gitlab-ci.yml
+
variables:
+ FUZZAPI_TARGET_URL: http://target/
+ FUZZAPI_OPENAPI: test-api-specification.json
FUZZAPI_EXCLUDE_URLS: https://target/api/v.*/user/create$
```
@@ -1679,11 +2215,11 @@ For more information, see [Offline environments](../offline_deployments/index.md
### `Error waiting for API Security '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 DAST API analyzer.
+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.
The version information can be found in the job details for the `apifuzzer_fuzz` job.
-If the issue is occurring with versions v1.6.196 or greater, please contact Support and provide the following information:
+If the issue is occurring with versions v1.6.196 or greater, contact Support and provide the following information:
1. Reference this troubleshooting section and ask for the issue to be escalated to the Dynamic Analysis Team.
1. The full console output of the job.
@@ -1750,12 +2286,15 @@ This solution is for pipelines in which the target API URL doesn't change (is st
For environments where the target API remains the same, we recommend you specify the target URL by using the `FUZZAPI_TARGET_URL` environment variable. In your `.gitlab-ci.yml` file, add a variable `FUZZAPI_TARGET_URL`. The variable must be set to the base URL of API testing target. For example:
```yaml
+stages:
+ - fuzz
+
include:
- - template: API-Fuzzing.gitlab-ci.yml
+ - template: API-Fuzzing.gitlab-ci.yml
- variables:
- FUZZAPI_TARGET_URL: http://test-deployment/
- FUZZAPI_OPENAPI: test-api-specification.json
+variables:
+ FUZZAPI_TARGET_URL: http://test-deployment/
+ FUZZAPI_OPENAPI: test-api-specification.json
```
#### Dynamic environment solutions
@@ -1793,7 +2332,7 @@ TODO
### Use OpenAPI with an invalid schema
-There are cases where the document is autogenerated with an invalid schema or cannot be edited manually in a timely manner. In those scenarios, the API Security is able to perform a relaxed validation by setting the variable `FUZZAPI_OPENAPI_RELAXED_VALIDATION`. We recommend providing a fully compliant OpenAPI document to prevent unexpected behaviors.
+There are cases where the document is autogenerated with an invalid schema or cannot be edited manually in a timely manner. In those scenarios, the API Fuzzing is able to perform a relaxed validation by setting the variable `FUZZAPI_OPENAPI_RELAXED_VALIDATION`. We recommend providing a fully compliant OpenAPI document to prevent unexpected behaviors.
#### Edit a non-compliant OpenAPI file
@@ -1810,25 +2349,25 @@ If your OpenAPI document is generated manually, load your document in the editor
Relaxed validation is meant for cases when the OpenAPI document cannot meet OpenAPI specifications, but it still has enough content to be consumed by different tools. A validation is performed but less strictly in regards to document schema.
-API Security can still try to consume an OpenAPI document that does not fully comply with OpenAPI specifications. To instruct API Security to perform a relaxed validation, set the variable `FUZZAPI_OPENAPI_RELAXED_VALIDATION` to any value, for example:
+API Fuzzing can still try to consume an OpenAPI document that does not fully comply with OpenAPI specifications. To instruct API Fuzzing analyzer to perform a relaxed validation, set the variable `FUZZAPI_OPENAPI_RELAXED_VALIDATION` to any value, for example:
```yaml
- stages:
- - fuzz
+stages:
+ - fuzz
- include:
- - template: API-Fuzzing.gitlab-ci.yml
+include:
+ - template: API-Fuzzing.gitlab-ci.yml
- variables:
- FUZZAPI_PROFILE: Quick-10
- FUZZAPI_TARGET_URL: http://test-deployment/
- FUZZAPI_OPENAPI: test-api-specification.json
- FUZZAPI_OPENAPI_RELAXED_VALIDATION: On
+variables:
+ FUZZAPI_PROFILE: Quick-10
+ FUZZAPI_TARGET_URL: http://test-deployment/
+ FUZZAPI_OPENAPI: test-api-specification.json
+ FUZZAPI_OPENAPI_RELAXED_VALIDATION: 'On'
```
### `No operation in the OpenAPI document is consuming any supported media type`
-API Security uses the specified media types in the OpenAPI document to generate requests. If no request can be created due to the lack of supported media types, then an error will be thrown.
+API Fuzzing uses the specified media types in the OpenAPI document to generate requests. If no request can be created due to the lack of supported media types, then an error will be thrown.
**Error message**
diff --git a/doc/user/application_security/configuration/index.md b/doc/user/application_security/configuration/index.md
index 9ca1a6f125f..32a523a1871 100644
--- a/doc/user/application_security/configuration/index.md
+++ b/doc/user/application_security/configuration/index.md
@@ -29,7 +29,7 @@ all security features are configured by default.
To view a project's security configuration:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Security & Compliance > Configuration**.
Select **Configuration history** to see the `.gitlab-ci.yml` file's history.
@@ -56,7 +56,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 via an automatic merge request](../secret_detection/index.md#enable-secret-detection-via-an-automatic-merge-request).
+ 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).
- [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 7bb3cb4f64c..961ccf6b563 100644
--- a/doc/user/application_security/container_scanning/index.md
+++ b/doc/user/application_security/container_scanning/index.md
@@ -1,7 +1,7 @@
---
type: reference, howto
-stage: Protect
-group: Container Security
+stage: Secure
+group: Composition Analysis
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
---
@@ -14,6 +14,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - Integration with Grype as an alternative scanner [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/326279) in GitLab 14.0.
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86092) the major analyzer version from `4` to `5` in GitLab 15.0.
> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86783) from GitLab Ultimate to GitLab Free in 15.0.
+> - Container Scanning variables that reference Docker [renamed](https://gitlab.com/gitlab-org/gitlab/-/issues/357264) in GitLab 15.4.
Your application's Docker image may itself be based on Docker images that contain known
vulnerabilities. By including an extra Container Scanning job in your pipeline that scans for those
@@ -68,7 +69,7 @@ information directly in the merge request.
| [Solutions for vulnerabilities (auto-remediation)](#solutions-for-vulnerabilities-auto-remediation) | No | Yes |
| Support for the [vulnerability allow list](#vulnerability-allowlisting) | No | Yes |
| [Access to Security Dashboard page](#security-dashboard) | No | Yes |
-| [Access to Dependency List page](../dependency_list/) | No | Yes |
+| [Access to Dependency List page](../dependency_list/index.md) | No | Yes |
## Requirements
@@ -83,7 +84,7 @@ To enable container scanning in your pipeline, you need the following:
- [Build and push](../../packages/container_registry/index.md#build-and-push-by-using-gitlab-cicd)
the Docker image to your project's container registry.
- If you're using a third-party container registry, you might need to provide authentication
- credentials through the `DOCKER_USER` and `DOCKER_PASSWORD` [configuration variables](#available-cicd-variables).
+ credentials through the `CS_REGISTRY_USER` and `CS_REGISTRY_PASSWORD` [configuration variables](#available-cicd-variables).
For more details on how to use these variables, see [authenticate to a remote registry](#authenticate-to-a-remote-registry).
## Configuration
@@ -157,13 +158,13 @@ include:
container_scanning:
variables:
- DOCKER_IMAGE: example.com/user/image:tag
+ CS_IMAGE: example.com/user/image:tag
```
##### Authenticate to a remote registry
-Scanning an image in a private registry requires authentication. Provide the username in the `DOCKER_USER`
-variable, and the password in the `DOCKER_PASSWORD` configuration variable.
+Scanning an image in a private registry requires authentication. Provide the username in the `CS_REGISTRY_USER`
+variable, and the password in the `CS_REGISTRY_PASSWORD` configuration variable.
For example, to scan an image from AWS Elastic Container Registry:
@@ -178,9 +179,9 @@ container_scanning:
include:
- template: Security/Container-Scanning.gitlab-ci.yml
- DOCKER_IMAGE: <aws_account_id>.dkr.ecr.<region>.amazonaws.com/<image>:<tag>
- DOCKER_USER: AWS
- DOCKER_PASSWORD: "$AWS_ECR_PASSWORD"
+ CS_IMAGE: <aws_account_id>.dkr.ecr.<region>.amazonaws.com/<image>:<tag>
+ CS_REGISTRY_USER: AWS
+ CS_REGISTRY_PASSWORD: "$AWS_ECR_PASSWORD"
```
Authenticating to a remote registry is not supported when [FIPS mode](../../../development/fips_compliance.md#enable-fips-mode) is enabled.
@@ -190,7 +191,7 @@ Authenticating to a remote registry is not supported when [FIPS mode](../../../d
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345434) in GitLab 14.6.
The `CS_DISABLE_DEPENDENCY_LIST` CI/CD variable controls whether the scan creates a
-[Dependency List](../dependency_list/)
+[Dependency List](../dependency_list/index.md)
report. This variable is currently only supported when the `trivy` analyzer is used. The variable's default setting of `"false"` causes the scan to create the report. To disable
the report, set the variable to `"true"`:
@@ -230,10 +231,10 @@ container_scanning:
```
When you enable this feature, you may see [duplicate findings](../terminology/index.md#duplicate-finding)
-in the [Vulnerability Report](../vulnerability_report/)
-if [Dependency Scanning](../dependency_scanning/)
+in the [Vulnerability Report](../vulnerability_report/index.md)
+if [Dependency Scanning](../dependency_scanning/index.md)
is enabled for your project. This happens because GitLab can't automatically deduplicate findings
-across different types of scanning tools. Please reference [this comparison](../dependency_scanning/#dependency-scanning-compared-to-container-scanning)
+across different types of scanning tools. Please reference [this comparison](../dependency_scanning/index.md#dependency-scanning-compared-to-container-scanning)
between GitLab Dependency Scanning and Container Scanning for more details on which types of dependencies are likely to be duplicated.
#### Available CI/CD variables
@@ -251,7 +252,7 @@ including a large number of false positives.
| `CI_APPLICATION_REPOSITORY` | `$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG` | Docker repository URL for the image to be scanned. | All |
| `CI_APPLICATION_TAG` | `$CI_COMMIT_SHA` | Docker repository tag for the image to be scanned. | All |
| `CS_ANALYZER_IMAGE` | `registry.gitlab.com/security-products/container-scanning:5` | Docker image of the analyzer. | All |
-| `CS_DEFAULT_BRANCH_IMAGE` | `""` | The name of the `DOCKER_IMAGE` on the default branch. See [Setting the default branch image](#setting-the-default-branch-image) for more details. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/338877) in GitLab 14.5. | All |
+| `CS_DEFAULT_BRANCH_IMAGE` | `""` | The name of the `CS_IMAGE` on the default branch. See [Setting the default branch image](#setting-the-default-branch-image) for more details. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/338877) in GitLab 14.5. | All |
| `CS_DISABLE_DEPENDENCY_LIST` | `"false"` | Disable Dependency Scanning for packages installed in the scanned image. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345434) in GitLab 14.6. | All |
| `CS_DISABLE_LANGUAGE_VULNERABILITY_SCAN` | `"true"` | Disable scanning for language-specific packages installed in the scanned image. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/345434) in GitLab 14.6. | All |
| `CS_DOCKER_INSECURE` | `"false"` | Allow access to secure Docker registries using HTTPS without validating the certificates. | All |
@@ -259,10 +260,14 @@ including a large number of false positives.
| `CS_IGNORE_UNFIXED` | `"false"` | Ignore vulnerabilities that are not fixed. | All |
| `CS_REGISTRY_INSECURE` | `"false"` | Allow access to insecure registries (HTTP only). Should only be set to `true` when testing the image locally. Works with all scanners, but the registry must listen on port `80/tcp` for Trivy to work. | All |
| `CS_SEVERITY_THRESHOLD` | `UNKNOWN` | Severity level threshold. The scanner outputs vulnerabilities with severity level higher than or equal to this threshold. Supported levels are Unknown, Low, Medium, High, and Critical. | Trivy |
-| `DOCKER_IMAGE` | `$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG` | The Docker image to be scanned. If set, this variable overrides the `$CI_APPLICATION_REPOSITORY` and `$CI_APPLICATION_TAG` variables. | All |
-| `DOCKER_PASSWORD` | `$CI_REGISTRY_PASSWORD` | Password for accessing a Docker registry requiring authentication. The default is only set if `$DOCKER_IMAGE` resides at [`$CI_REGISTRY`](../../../ci/variables/predefined_variables.md). Not supported when [FIPS mode](../../../development/fips_compliance.md#enable-fips-mode) is enabled. | All |
-| `DOCKER_USER` | `$CI_REGISTRY_USER` | Username for accessing a Docker registry requiring authentication. The default is only set if `$DOCKER_IMAGE` resides at [`$CI_REGISTRY`](../../../ci/variables/predefined_variables.md). Not supported when [FIPS mode](../../../development/fips_compliance.md#enable-fips-mode) is enabled. | All |
-| `DOCKERFILE_PATH` | `Dockerfile` | The path to the `Dockerfile` to use for generating remediations. By default, the scanner looks for a file named `Dockerfile` in the root directory of the project. You should configure this variable only if your `Dockerfile` is in a non-standard location, such as a subdirectory. See [Solutions for vulnerabilities](#solutions-for-vulnerabilities-auto-remediation) for more details. | All |
+| <!-- start_remove The following content will be removed on remove_date: '2023-08-22' --> `DOCKER_IMAGE` | `$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG` | **Deprecated** will be removed in GitLab 16.0. Replaced by `CS_IMAGE`. The Docker image to be scanned. If set, this variable overrides the `$CI_APPLICATION_REPOSITORY` and `$CI_APPLICATION_TAG` variables. | All |
+| `DOCKER_PASSWORD` | `$CI_REGISTRY_PASSWORD` | **Deprecated** will be removed in GitLab 16.0. Replaced by `CS_REGISTRY_PASSWORD`. Password for accessing a Docker registry requiring authentication. The default is only set if `$DOCKER_IMAGE` resides at [`$CI_REGISTRY`](../../../ci/variables/predefined_variables.md). Not supported when [FIPS mode](../../../development/fips_compliance.md#enable-fips-mode) is enabled. | All |
+| `DOCKER_USER` | `$CI_REGISTRY_USER` | **Deprecated** will be removed in GitLab 16.0. Replaced by `CS_REGISTRY_USER`. Username for accessing a Docker registry requiring authentication. The default is only set if `$DOCKER_IMAGE` resides at [`$CI_REGISTRY`](../../../ci/variables/predefined_variables.md). Not supported when [FIPS mode](../../../development/fips_compliance.md#enable-fips-mode) is enabled. | All |
+| `DOCKERFILE_PATH` | `Dockerfile` | **Deprecated** will be removed in GitLab 16.0. Replaced by `CS_DOCKERFILE_PATH`. The path to the `Dockerfile` to use for generating remediations. By default, the scanner looks for a file named `Dockerfile` in the root directory of the project. You should configure this variable only if your `Dockerfile` is in a non-standard location, such as a subdirectory. See [Solutions for vulnerabilities](#solutions-for-vulnerabilities-auto-remediation) for more details. | All <!-- end_remove --> |
+| `CS_IMAGE` | `$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG` | The Docker image to be scanned. If set, this variable overrides the `$CI_APPLICATION_REPOSITORY` and `$CI_APPLICATION_TAG` variables. | All |
+| `CS_REGISTRY_PASSWORD` | `$CI_REGISTRY_PASSWORD` | Password for accessing a Docker registry requiring authentication. The default is only set if `$CS_IMAGE` resides at [`$CI_REGISTRY`](../../../ci/variables/predefined_variables.md). Not supported when [FIPS mode](../../../development/fips_compliance.md#enable-fips-mode) is enabled. | All |
+| `CS_REGISTRY_USER` | `$CI_REGISTRY_USER` | Username for accessing a Docker registry requiring authentication. The default is only set if `$CS_IMAGE` resides at [`$CI_REGISTRY`](../../../ci/variables/predefined_variables.md). Not supported when [FIPS mode](../../../development/fips_compliance.md#enable-fips-mode) is enabled. | All |
+| `CS_DOCKERFILE_PATH` | `Dockerfile` | The path to the `Dockerfile` to use for generating remediations. By default, the scanner looks for a file named `Dockerfile` in the root directory of the project. You should configure this variable only if your `Dockerfile` is in a non-standard location, such as a subdirectory. See [Solutions for vulnerabilities](#solutions-for-vulnerabilities-auto-remediation) for more details. | All |
| `SECURE_LOG_LEVEL` | `info` | Set the minimum logging level. Messages of this logging level or higher are output. From highest to lowest severity, the logging levels are: `fatal`, `error`, `warn`, `info`, `debug`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10880) in GitLab 13.1. | All |
### Supported distributions
@@ -309,7 +314,7 @@ Starting with GitLab 14.10, `-fips` is automatically added to `CS_ANALYZER_IMAGE
enabled in the GitLab instance.
Container scanning of images in authenticated registries is not supported when [FIPS mode](../../../development/fips_compliance.md#enable-fips-mode)
-is enabled. When `CI_GITLAB_FIPS_MODE` is `"true"`, and `DOCKER_USER` or `DOCKER_PASSWORD` is set,
+is enabled. When `CI_GITLAB_FIPS_MODE` is `"true"`, and `CS_REGISTRY_USER` or `CS_REGISTRY_PASSWORD` is set,
the analyzer exits with an error and does not perform the scan.
### Enable Container Scanning through an automatic merge request
@@ -426,14 +431,14 @@ container_scanning:
variables:
CS_DEFAULT_BRANCH_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
before_script:
- - export DOCKER_IMAGE="$CI_REGISTRY_IMAGE/$CI_COMMIT_BRANCH:$CI_COMMIT_SHA"
+ - export CS_IMAGE="$CI_REGISTRY_IMAGE/$CI_COMMIT_BRANCH:$CI_COMMIT_SHA"
- |
if [ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]; then
- export DOCKER_IMAGE="$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"
+ export CS_IMAGE="$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"
fi
```
-`CS_DEFAULT_BRANCH_IMAGE` should remain the same for a given `DOCKER_IMAGE`. If it changes, then a
+`CS_DEFAULT_BRANCH_IMAGE` should remain the same for a given `CS_IMAGE`. If it changes, then a
duplicate set of vulnerabilities are created, which must be manually dismissed.
When using [Auto DevOps](../../../topics/autodevops/index.md), `CS_DEFAULT_BRANCH_IMAGE` is
@@ -500,7 +505,7 @@ This example excludes from `gl-container-scanning-report.json`:
- `generalallowlist` block allows you to specify CVE IDs globally. All vulnerabilities with matching CVE IDs are excluded from the scan report.
-- `images` block allows you to specify CVE IDs for each container image independently. All vulnerabilities from the given image with matching CVE IDs are excluded from the scan report. The image name is retrieved from one of the environment variables used to specify the Docker image to be scanned, such as `$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG` or `DOCKER_IMAGE`. The image provided in this block **must** match this value and **must not** include the tag value. For example, if you specify the image to be scanned using `DOCKER_IMAGE=alpine:3.7`, then you would use `alpine` in the `images` block, but you cannot use `alpine:3.7`.
+- `images` block allows you to specify CVE IDs for each container image independently. All vulnerabilities from the given image with matching CVE IDs are excluded from the scan report. The image name is retrieved from one of the environment variables used to specify the Docker image to be scanned, such as `$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG` or `CS_IMAGE`. The image provided in this block **must** match this value and **must not** include the tag value. For example, if you specify the image to be scanned using `CS_IMAGE=alpine:3.7`, then you would use `alpine` in the `images` block, but you cannot use `alpine:3.7`.
You can specify container image in multiple ways:
@@ -649,8 +654,8 @@ you're using a non-GitLab Docker registry, you must change the `$CI_REGISTRY` va
To scan an image in an external private registry, you must configure access credentials so the
container scanning analyzer can authenticate itself before attempting to access the image to scan.
-If you use the GitLab [Container Registry](../../packages/container_registry/),
-the `DOCKER_USER` and `DOCKER_PASSWORD` [configuration variables](#available-cicd-variables)
+If you use the GitLab [Container Registry](../../packages/container_registry/index.md),
+the `CS_REGISTRY_USER` and `CS_REGISTRY_PASSWORD` [configuration variables](#available-cicd-variables)
are set automatically and you can skip this configuration.
This example shows the configuration needed to scan images in a private [Google Container Registry](https://cloud.google.com/container-registry/):
@@ -661,12 +666,12 @@ include:
container_scanning:
variables:
- DOCKER_USER: _json_key
- DOCKER_PASSWORD: "$GCP_CREDENTIALS"
- DOCKER_IMAGE: "gcr.io/path-to-you-registry/image:tag"
+ CS_REGISTRY_USER: _json_key
+ CS_REGISTRY_PASSWORD: "$GCP_CREDENTIALS"
+ CS_IMAGE: "gcr.io/path-to-you-registry/image:tag"
```
-Before you commit this configuration, [add a CI/CD variable](../../../ci/variables/#add-a-cicd-variable-to-a-project)
+Before you commit this configuration, [add a CI/CD variable](../../../ci/variables/index.md#add-a-cicd-variable-to-a-project)
for `GCP_CREDENTIALS` containing the JSON key, as described in the
[Google Cloud Platform Container Registry documentation](https://cloud.google.com/container-registry/docs/advanced-authentication#json-key).
Also:
@@ -706,12 +711,12 @@ The results are stored in `gl-container-scanning-report.json`.
## Reports JSON format
The container scanning tool emits JSON reports which the [GitLab Runner](https://docs.gitlab.com/runner/)
-recognizes through the [`artifacts:reports`](../../../ci/yaml/#artifactsreports)
+recognizes through the [`artifacts:reports`](../../../ci/yaml/index.md#artifactsreports)
keyword in the CI configuration file.
Once the CI job finishes, the Runner uploads these reports to GitLab, which are then available in
the CI Job artifacts. In GitLab Ultimate, these reports can be viewed in the corresponding [pipeline](../vulnerability_report/pipeline.md)
-and become part of the [Vulnerability Report](../vulnerability_report/).
+and become part of the [Vulnerability Report](../vulnerability_report/index.md).
These reports must follow a format defined in the
[security report schemas](https://gitlab.com/gitlab-org/security-products/security-report-schemas/). See:
@@ -772,7 +777,7 @@ Some vulnerabilities can be fixed by applying the solution that GitLab
automatically generates.
To enable remediation support, the scanning tool _must_ have access to the `Dockerfile` specified by
-the [`DOCKERFILE_PATH`](#available-cicd-variables) CI/CD variable. To ensure that the scanning tool
+the [`CS_DOCKERFILE_PATH`](#available-cicd-variables) CI/CD variable. To ensure that the scanning tool
has access to this
file, it's necessary to set [`GIT_STRATEGY: fetch`](../../../ci/runners/configure_runners.md#git-strategy) in
your `.gitlab-ci.yml` file by following the instructions described in this document's
diff --git a/doc/user/application_security/coverage_fuzzing/index.md b/doc/user/application_security/coverage_fuzzing/index.md
index 154884c16e7..bec242a0426 100644
--- a/doc/user/application_security/coverage_fuzzing/index.md
+++ b/doc/user/application_security/coverage_fuzzing/index.md
@@ -53,7 +53,7 @@ You can use the following fuzzing engines to test the specified languages.
To confirm the status of coverage-guided fuzz testing:
-1. On the top bar, select **Menu > Projects** and find your project.
+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 **Coverage Fuzzing** section the status is:
- **Not configured**
@@ -174,7 +174,7 @@ artifacts files you can download from the CI/CD pipeline.
To view details of the corpus registry:
-1. On the top bar, select **Menu > Projects** and find your project.
+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 **Coverage Fuzzing** section, select **Manage corpus**.
@@ -202,7 +202,7 @@ provided by the `COVFUZZ_CORPUS_NAME` variable. The corpus is updated on every p
To upload an existing corpus file:
-1. On the top bar, select **Menu > Projects** and find your project.
+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 **Coverage Fuzzing** section, select **Manage corpus**.
1. Select **New corpus**.
@@ -277,7 +277,7 @@ For a complete example, read the [Go coverage-guided fuzzing example](https://gi
It's also possible to run the coverage-guided fuzzing jobs longer and without blocking your main
pipeline. This configuration uses the GitLab
-[parent-child pipelines](../../../ci/pipelines/parent_child_pipelines.md).
+[parent-child pipelines](../../../ci/pipelines/downstream_pipelines.md#parent-child-pipelines).
The suggested workflow in this scenario is to have long-running, asynchronous fuzzing jobs on the
main or development branch, and short synchronous fuzzing jobs on all other branches and MRs. This
@@ -376,4 +376,4 @@ corpus file extracts into a folder named `corpus`.
If you see this error message when running the fuzzing job with `COVFUZZ_USE_REGISTRY` set to `true`,
ensure that duplicates are allowed. For more details, see
-[duplicate Generic packages](../../packages/generic_packages/#do-not-allow-duplicate-generic-packages).
+[duplicate Generic packages](../../packages/generic_packages/index.md#do-not-allow-duplicate-generic-packages).
diff --git a/doc/user/application_security/cve_id_request.md b/doc/user/application_security/cve_id_request.md
index 5ffd47527c5..6f076bbe3f9 100644
--- a/doc/user/application_security/cve_id_request.md
+++ b/doc/user/application_security/cve_id_request.md
@@ -1,6 +1,6 @@
---
type: tutorial
-stage: Secure
+stage: Govern
group: Threat Insights
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
---
diff --git a/doc/user/application_security/dast/checks/16.7.md b/doc/user/application_security/dast/checks/16.7.md
index 2e6607575db..a052149ee4d 100644
--- a/doc/user/application_security/dast/checks/16.7.md
+++ b/doc/user/application_security/dast/checks/16.7.md
@@ -25,7 +25,7 @@ Only three directives are applicable for the `Strict-Transport-Security` header.
Note that invalid directives, or the `Strict-Transport-Security` header appearing more than once (if the values are
different) is considered invalid.
-Prior to adding to this security configuration to your website, it is recommended you review the hstspreload.org
+Prior to adding to this security configuration to your website, it is recommended you review the hstspreload.org
[Deployment Recommendations](https://hstspreload.org/#deployment-recommendations).
## Details
diff --git a/doc/user/application_security/dast/checks/209.1.md b/doc/user/application_security/dast/checks/209.1.md
index 2e4163bdec0..f2713a70afd 100644
--- a/doc/user/application_security/dast/checks/209.1.md
+++ b/doc/user/application_security/dast/checks/209.1.md
@@ -9,17 +9,17 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## Description
The application was found to return error data such as stack traces. Depending on the data contained within the error message,
-this information could be used by an attacker to conduct further attacks. While stack traces are helpful during development
-and debugging, they should not be presented to users when an error occurs.
+this information could be used by an attacker to conduct further attacks. While stack traces are helpful during development
+and debugging, they should not be presented to users when an error occurs.
## Remediation
Applications should handle exception conditions internally and map known failure types to error codes that can be displayed
to a user. These error codes should be customized to the application and returned along with the relevant HTTP error code.
-When an error occurs, the application identifies the error type or class, and displays a numerical value to the
-user. Requests should also be tracked so when a user is presented with an error code, it has a corresponding request ID.
-Support teams can then correlate the HTTP error, the customized error code, and the request ID in the log files to
+When an error occurs, the application identifies the error type or class, and displays a numerical value to the
+user. Requests should also be tracked so when a user is presented with an error code, it has a corresponding request ID.
+Support teams can then correlate the HTTP error, the customized error code, and the request ID in the log files to
determine the root cause of the error without leaking details to the end user.
Example of returning customized errors:
diff --git a/doc/user/application_security/dast/dast_troubleshooting.md b/doc/user/application_security/dast/dast_troubleshooting.md
index 0c7a9806c72..4e87f1898cc 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
---
-# Dynamic Application Security Testing (DAST) Troubleshooting **(ULTIMATE)**
+# Troubleshooting Dynamic Application Security Testing (DAST) **(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 a49dd8fd646..0f446ddee3e 100644
--- a/doc/user/application_security/dast/index.md
+++ b/doc/user/application_security/dast/index.md
@@ -280,7 +280,7 @@ page.
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.
-1. On the top bar, select **Menu > Projects** and find your project.
+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 **Dynamic Application Security Testing (DAST)** section, select **Enable DAST** or
**Configure DAST**.
@@ -357,13 +357,9 @@ variables:
#### Import API specification from a file
If your API specification file is in your repository, you can provide its filename as the target.
-The API specification file must be in the `/zap/wrk` directory.
```yaml
dast:
- before_script:
- - mkdir -p /zap/wrk
- - cp api-specification.yml /zap/wrk/api-specification.yml
variables:
GIT_STRATEGY: fetch
DAST_API_SPECIFICATION: api-specification.yml
@@ -1075,7 +1071,7 @@ The on-demand DAST scan runs and the project's dashboard shows the results.
To run a saved on-demand scan:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Security & Compliance > On-demand Scans**.
1. Select the **Scan library** tab.
1. In the scan's row, select **Run scan**.
@@ -1094,7 +1090,7 @@ The on-demand DAST scan runs, and the project's dashboard shows the results.
To schedule a scan:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Security & Compliance > On-demand Scans**.
1. Select **New scan**.
1. Complete the **Scan name** and **Description** text boxes.
@@ -1143,14 +1139,16 @@ To delete an on-demand scan:
1. In the saved scan's row select **More actions** (**{ellipsis_v}**), then select **Delete**.
1. Select **Delete** to confirm the deletion.
-### Site profile
+## Site profile
-A site profile describes the attributes of a web site to scan on demand with DAST. A site profile is
-required for an on-demand DAST scan.
+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
+on-demand scans.
-A site profile contains the following:
+A site profile contains:
-- **Profile name**: A name you assign to the site to be scanned.
+- **Profile name**: A name you assign to the site to be scanned. While a site profile is referenced
+ in either `.gitlab-ci.yml` or an on-demand scan, it **cannot** be renamed.
- **Site type**: The type of target to be scanned, either website or API scan.
- **Target URL**: The URL that DAST runs against.
- **Excluded URLs**: A comma-separated list of URLs to exclude from the scan.
@@ -1168,7 +1166,7 @@ When an API site type is selected, a [host override](#host-override) is used to
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.
-#### Site profile validation
+### Site profile validation
> - Site profile validation [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/233020) in GitLab 13.8.
> - Meta tag validation [introduced](https://gitlab.com/groups/gitlab-org/-/epics/6460) in GitLab 14.2.
@@ -1192,7 +1190,7 @@ All these methods are equivalent in functionality. Use whichever is feasible.
In [GitLab 14.2 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/324990), site profile
validation happens in a CI job using the [GitLab Runner](../../../ci/runners/index.md).
-#### Create a site profile
+### Create a site profile
To create a site profile:
@@ -1203,7 +1201,7 @@ To create a site profile:
The site profile is created.
-#### Edit a site profile
+### Edit a site profile
If a site profile is linked to a security policy, a user cannot edit the profile from this page. See
[Scan execution policies](../policies/scan-execution-policies.md)
@@ -1220,7 +1218,7 @@ To edit a site profile:
1. In the profile's row select the **More actions** (**{ellipsis_v}**) menu, then select **Edit**.
1. Edit the fields then select **Save profile**.
-#### Delete a site profile
+### Delete a site profile
If a site profile is linked to a security policy, a user cannot delete the profile from this page.
See [Scan execution policies](../policies/scan-execution-policies.md)
@@ -1234,13 +1232,13 @@ To delete a site profile:
1. In the profile's row, select the **More actions** (**{ellipsis_v}**) menu, then select **Delete**.
1. Select **Delete** to confirm the deletion.
-#### Validate a site profile
+### Validate a site profile
Validating a site is required to run an active scan.
To validate a site profile:
-1. On the top bar, select **Menu > Projects** and find your project.
+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 **Dynamic Application Security Testing (DAST)** section, select **Manage profiles**.
1. Select the **Site Profiles** tab.
@@ -1266,7 +1264,7 @@ To validate a site profile:
The site is validated and an active scan can run against it. A site profile's validation status is
revoked only when it's revoked manually, or its file, header, or meta tag is edited.
-#### Retry a failed validation
+### Retry a failed validation
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/322609) in GitLab 14.3.
> - [Deployed behind the `dast_failed_site_validations` flag](../../../administration/feature_flags.md), enabled by default.
@@ -1277,13 +1275,13 @@ page.
To retry a site profile's failed validation:
-1. On the top bar, select **Menu > Projects** and find your project.
+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 **Dynamic Application Security Testing (DAST)** section, select **Manage profiles**.
1. Select the **Site Profiles** tab.
1. In the profile's row, select **Retry validation**.
-#### Revoke a site profile's validation status
+### Revoke a site profile's validation status
WARNING:
When a site profile's validation status is revoked, all site profiles that share the same URL also
@@ -1297,12 +1295,12 @@ To revoke a site profile's validation status:
The site profile's validation status is revoked.
-#### Validated site profile headers
+### Validated site profile headers
The following are code samples of how you can provide the required site profile header in your
application.
-##### Ruby on Rails example for on-demand scan
+#### Ruby on Rails example for on-demand scan
Here's how you can add a custom header in a Ruby on Rails application:
@@ -1315,7 +1313,7 @@ class DastWebsiteTargetController < ActionController::Base
end
```
-##### Django example for on-demand scan
+#### Django example for on-demand scan
Here's how you can add a
[custom header in Django](https://docs.djangoproject.com/en/2.2/ref/request-response/#setting-header-fields):
@@ -1329,7 +1327,7 @@ class DastWebsiteTargetView(View):
return response
```
-##### Node (with Express) example for on-demand scan
+#### Node (with Express) example for on-demand scan
Here's how you can add a
[custom header in Node (with Express)](https://expressjs.com/en/5x/api.html#res.append):
@@ -1341,22 +1339,26 @@ app.get('/dast-website-target', function(req, res) {
})
```
-### Scanner profile
+## Scanner profile
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/222767) in GitLab 13.4.
> - [Added](https://gitlab.com/gitlab-org/gitlab/-/issues/225804) in GitLab 13.5: scan mode, AJAX spider, debug messages.
-A scanner profile defines the scanner settings used to run an on-demand scan:
+A scanner profile defines the configuration details of a security scanner. A scanner profile can be
+referenced in `.gitlab-ci.yml` and on-demand scans.
-- **Profile name:** A name you give the scanner profile. For example, "Spider_15".
+A scanner profile contains:
+
+- **Profile name:** A name you give the scanner profile. For example, "Spider_15". While a scanner
+ profile is referenced in either `.gitlab-ci.yml` or an on-demand scan, it **cannot** be renamed.
- **Scan mode:** A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities.
- **Spider timeout:** The maximum number of minutes allowed for the spider to traverse the site.
- **Target timeout:** The maximum number of seconds DAST waits for the site to be available before
starting the scan.
-- **AJAX spider:** Run the AJAX spider, in addition to the traditional spider, to crawl the target site.
+- **AJAX spider:** Run the AJAX spider, in addition to the traditional spider, to crawl the target site.
- **Debug messages:** Include debug messages in the DAST console output.
-#### Create a scanner profile
+### Create a scanner profile
To create a scanner profile:
@@ -1366,7 +1368,7 @@ To create a scanner profile:
1. Complete the form. For details of each field, see [Scanner profile](#scanner-profile).
1. Select **Save profile**.
-#### Edit a scanner profile
+### Edit a scanner profile
If a scanner profile is linked to a security policy, a user cannot edit the profile from this page.
See [Scan execution policies](../policies/scan-execution-policies.md)
@@ -1381,7 +1383,7 @@ To edit a scanner profile:
1. Edit the form.
1. Select **Save profile**.
-#### Delete a scanner profile
+### Delete a scanner profile
If a scanner profile is linked to a security policy, a user cannot delete the profile from this
page. See [Scan execution policies](../policies/scan-execution-policies.md)
diff --git a/doc/user/application_security/dast_api/index.md b/doc/user/application_security/dast_api/index.md
index 1f86f2ffa49..f15dce37123 100644
--- a/doc/user/application_security/dast_api/index.md
+++ b/doc/user/application_security/dast_api/index.md
@@ -55,6 +55,7 @@ The following projects demonstrate DAST API scanning:
You can specify the API you want to scan by using:
- [OpenAPI v2 or v3 Specification](#openapi-specification)
+- [GraphQL Schema](#graphql-schema)
- [HTTP Archive (HAR)](#http-archive-har)
- [Postman Collection v2.0 or v2.1](#postman-collection)
@@ -199,7 +200,119 @@ variables:
DAST_API_TARGET_URL: http://test-deployment/
```
-This is a minimal configuration for DAST API. From here you can:
+This example is a minimal configuration for DAST API. From here you can:
+
+- [Run your first scan](#running-your-first-scan).
+- [Add authentication](#authentication).
+- Learn how to [handle false positives](#handling-false-positives).
+
+### GraphQL Schema
+
+> Support for GraphQL Schema was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352780) in GitLab 15.4.
+
+GraphQL is a query language for your API and an alternative to REST APIs.
+DAST API supports testing GraphQL endpoints multiple ways:
+
+- Test using the GraphQL Schema. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352780) in GitLab 15.4.
+- Test using a recording (HAR) of GraphQL queries.
+- 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.
+Introspection is enabled by default to allow tools like GraphiQL to work.
+
+#### DAST API scanning with a GraphQL endpoint URL
+
+The GraphQL support in DAST API is able to query a GraphQL endpoint for the schema.
+
+NOTE:
+The GraphQL endpoint must support introspection queries for this method to work correctly.
+
+To configure DAST API to use a GraphQL endpoint URL that provides information about the target API to test:
+
+1. [Include](../../../ci/yaml/index.md#includetemplate)
+ the [`DAST-API.gitlab-ci.yml` template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/DAST-API.gitlab-ci.yml) in your `.gitlab-ci.yml` file.
+
+1. Provide the path to the GraphQL endpoint, for example `/api/graphql`. Specify the location by adding the `DAST_API_GRAPHQL` variable.
+
+1. The target API instance's base URL is also required. Provide it by using the `DAST_API_TARGET_URL`
+ variable or an `environment_url.txt` file.
+
+ Adding the URL in an `environment_url.txt` file at your project's root is great for testing in
+ dynamic environments. See the [dynamic environment solutions](#dynamic-environment-solutions) section of our documentation for more information.
+
+Complete example configuration of using a GraphQL endpoint path:
+
+```yaml
+stages:
+ - dast
+
+include:
+ - template: DAST-API.gitlab-ci.yml
+
+dast_api:
+ variables:
+ DAST_API_GRAPHQL: /api/graphql
+ DAST_API_TARGET_URL: http://test-deployment/
+```
+
+This example is a minimal configuration for DAST API. From here you can:
+
+- [Run your first scan](#running-your-first-scan).
+- [Add authentication](#authentication).
+- Learn how to [handle false positives](#handling-false-positives).
+
+#### DAST API scanning with a GraphQL Schema file
+
+To configure DAST API to use a GraphQL schema file that provides information about the target API to test:
+
+1. [Include](../../../ci/yaml/index.md#includetemplate)
+ the [`DAST-API.gitlab-ci.yml` template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/DAST-API.gitlab-ci.yml) in your `.gitlab-ci.yml` file.
+
+1. Provide the GraphQL endpoint path, for example `/api/graphql`. Specify the path by adding the `DAST_API_GRAPHQL` variable.
+
+1. Provide the location of the GraphQL schema file. You can provide the location as a file path
+ or URL. Specify the location by adding the `DAST_API_GRAPHQL_SCHEMA` variable.
+
+1. The target API instance's base URL is also required. Provide it by using the `DAST_API_TARGET_URL`
+ variable or an `environment_url.txt` file.
+
+ Adding the URL in an `environment_url.txt` file at your project's root is great for testing in
+ dynamic environments. See the [dynamic environment solutions](#dynamic-environment-solutions) section of our documentation for more information.
+
+Complete example configuration of using an GraphQL schema file:
+
+```yaml
+stages:
+ - dast
+
+include:
+ - template: DAST-API.gitlab-ci.yml
+
+dast_api:
+ variables:
+ DAST_API_GRAPHQL: /api/graphql
+ DAST_API_GRAPHQL_SCHEMA: test-api-graphql.schema
+ DAST_API_TARGET_URL: http://test-deployment/
+```
+
+Complete example configuration of using an GraphQL schema file URL:
+
+```yaml
+stages:
+ - dast
+
+include:
+ - template: DAST-API.gitlab-ci.yml
+
+dast_api:
+ variables:
+ DAST_API_GRAPHQL: /api/graphql
+ DAST_API_GRAPHQL_SCHEMA: http://file-store/files/test-api-graphql.schema
+ DAST_API_TARGET_URL: http://test-deployment/
+```
+
+This example is a minimal configuration for DAST API. From here you can:
- [Run your first scan](#running-your-first-scan).
- [Add authentication](#authentication).
@@ -267,35 +380,266 @@ This is a minimal configuration for DAST API. From here you can:
- [Add authentication](#authentication).
- Learn how to [handle false positives](#handling-false-positives).
-##### Postman variables
+#### Postman variables
+
+> - Support for Postman Environment file format was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1.
+> - Support for multiple variable files was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1.
+> - Support for Postman variable scopes: Global and Environment was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1.
+
+##### Variables in Postman Client
Postman allows the developer to define placeholders that can be used in different parts of the
-requests. These placeholders are called variables, as explained in [Using variables](https://learning.postman.com/docs/sending-requests/variables/).
+requests. These placeholders are called variables, as explained in [using variables](https://learning.postman.com/docs/sending-requests/variables/).
You can use variables to store and reuse values in your requests and scripts. For example, you can
edit the collection to add variables to the document:
![Edit collection variable tab View](img/dast_api_postman_collection_edit_variable.png)
+Or alternatively, you can add variables in an environment:
+
+![Edit environment variables View](img/dast_api_postman_environment_edit_variable.png)
+
You can then use the variables in sections such as URL, headers, and others:
![Edit request using variables View](img/dast_api_postman_request_edit.png)
-Variables can be defined at different [scopes](https://learning.postman.com/docs/sending-requests/variables/#variable-scopes)
-(for example, Global, Collection, Environment, Local, and Data). In this example, they're defined at
-the Environment scope:
+Postman has grown from a basic client tool with a nice UX experience to a more complex ecosystem that allows testing APIs with scripts, creating complex collections that trigger secondary requests, and setting variables along the way. Not every feature in the Postman ecosystem is supported. For example, scripts are not supported. The main focus of the Postman support is to ingest Postman Collection definitions that are used by the Postman Client and their related variables defined in the workspace, environments, and the collections themselves.
-![Edit environment variables View](img/dast_api_postman_environment_edit_variable.png)
+Postman allows creating variables in different scopes. Each scope has a different level of visibility in the Postman tools. For example, you can create a variable in a _global environment_ scope that is seen by every operation definition and workspace. You can also create a variable in a specific _environment_ scope that is only visible and used when that specific environment is selected for use. Some scopes are not always available, for example in the Postman ecosystem you can create requests in the Postman Client, these requests do not have a _local_ scope, but test scripts do.
-When you export a Postman collection, only Postman collection variables are exported into the
-Postman file. For example, Postman does not export environment-scoped variables into the Postman
-file.
+Variable scopes in Postman can be a daunting topic and not everyone is familiar with it. We strongly recommend that you read [Variable Scopes](https://learning.postman.com/docs/sending-requests/variables/#variable-scopes) from Postman documentation before moving forward.
-By default, the DAST API scanner uses the Postman file to resolve Postman variable values. If a JSON file
-is set in a GitLab CI/CD environment variable `DAST_API_POSTMAN_COLLECTION_VARIABLES`, then the JSON
-file takes precedence to get Postman variable values.
+As mentioned above, there are different variable scopes, and each of them has a purpose and can be used to provide more flexibility to your Postman document. There is an important note on how values for variables are computed, as per Postman documentation:
-WARNING:
-Although Postman can export environment variables into a JSON file, the format is not compatible with the JSON expected by `DAST_API_POSTMAN_COLLECTION_VARIABLES`.
+> If a variable with the same name is declared in two different scopes, the value stored in the variable with narrowest scope is used. For example, if there is a global variable named `username` and a local variable named `username`, the local value is used when the request runs.
+
+The following is a summary of the variable scopes supported by the Postman Client and DAST API:
+
+- **Global Environment (Global) scope** is a special pre-defined environment that is available throughout a workspace. We can also refer to the _global environment_ scope as the _global_ scope. The Postman Client allows exporting the global environment into a JSON file, which can be used with DAST API.
+- **Environment scope** is a named group of variables created by a user in the Postman Client.
+The Postman Client supports a single active environment along with the global environment. The variables defined in an active user-created environment take precedence over variables defined in the global environment. The Postman Client allows exporting your environment into a JSON file, which can be used with DAST API.
+- **Collection scope** is a group of variables declared in a given collection. The collection variables are available to the collection where they have been declared and the nested requests or collections. Variables defined in the collection scope take precedence over the _global environment_ scope and also the _environment_ scope.
+The Postman Client can export one or more collections into a JSON file, this JSON file contains selected collections, requests, and collection variables.
+- **DAST API Scope** is a new scope added by DAST API to allow users to provide extra variables, or override variables defined in other supported scopes. This scope is not supported by Postman. The _DAST API Scope_ variables are provided using a [custom JSON file format](#dast-api-scope-custom-json-file-format).
+ - Override values defined in the environment or collection
+ - Defining variables from scripts
+ - Define a single row of data from the unsupported _data scope_
+- **Data scope** is a group of variables in which their name and values come from JSON or CSV files. A Postman collection runner like [Newman](https://learning.postman.com/docs/running-collections/using-newman-cli/command-line-integration-with-newman/) or [Postman Collection Runner](https://learning.postman.com/docs/running-collections/intro-to-collection-runs/) executes the requests in a collection as many times as entries have the JSON or CSV file. A good use case for these variables is to automate tests using scripts in Postman.
+DAST API does **not** support reading data from a CSV or JSON file.
+- **Local scope** are variables that are defined in Postman scripts. DAST API does **not** support Postman scripts and by extension, variables defined in scripts. You can still provide values for the script-defined variables by defining them in one of the supported scopes, or our custom JSON format.
+
+Not all scopes are supported by DAST API and variables defined in scripts are not supported. The following table is sorted by broadest scope to narrowest scope.
+
+| Scope |Postman | DAST API | Comment |
+| ------------------ |:---------:|:------------:| :--------|
+| Global Environment | Yes | Yes | Special pre-defined environment |
+| Environment | Yes | Yes | Named environments |
+| Collection | Yes | Yes | Defined in your postman collection |
+| DAST API Scope | No | Yes | Custom scope added by DAST API |
+| Data | Yes | No | External files in CSV or JSON format |
+| Local | Yes | No | Variables defined in scripts |
+
+For more details on how to define variables and export variables in different scopes, see:
+
+- [Defining collection variables](https://learning.postman.com/docs/sending-requests/variables/#defining-collection-variables)
+- [Defining environment variables](https://learning.postman.com/docs/sending-requests/variables/#defining-environment-variables)
+- [Defining global variables](https://learning.postman.com/docs/sending-requests/variables/#defining-global-variables)
+
+##### Exporting from Postman Client
+
+The Postman Client lets you export different file formats, for instance, you can export a Postman collection or a Postman environment.
+The exported environment can be the global environment (which is always available) or can be any custom environment you previously have created. When you export a Postman Collection, it may contain only declarations for _collection_ and _local_ scoped variables; _environment_ scoped variables are not included.
+
+To get the declaration for _environment_ scoped variables, you have to export a given environment at the time. Each exported file only includes variables from the selected environment.
+
+For more details on exporting variables in different supported scopes, see:
+
+- [Exporting collections](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-collections)
+- [Exporting environments](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments)
+- [Downloading global environments](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments)
+
+##### DAST API Scope, custom JSON file format
+
+Our custom JSON file format is a JSON object where each object property represents a variable name and the property value represents the variable value. This file can be created using your favorite text editor, or it can be produced by an earlier job in your pipeline.
+
+This example defines two variables `base_url` and `token` in the DAST API scope:
+
+```json
+{
+ "base_url": "http://127.0.0.1/",
+ "token": "Token 84816165151"
+}
+```
+
+##### Using scopes with DAST API
+
+The scopes: _global_, _environment_, _collection_, and _GitLab DAST API_ are supported in [GitLab 15.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356312). GitLab 15.0 and earlier, supports only the _collection_, and _GitLab DAST API_ scopes.
+
+The following table provides a quick reference for mapping scope files/URLs to DAST API configuration variables:
+
+| Scope | How to Provide |
+| ------------------ | --------------- |
+| Global Environment | DAST_API_POSTMAN_COLLECTION_VARIABLES |
+| Environment | DAST_API_POSTMAN_COLLECTION_VARIABLES |
+| Collection | DAST_API_POSTMAN_COLLECTION |
+| DAST API Scope | DAST_API_POSTMAN_COLLECTION_VARIABLES |
+| Data | Not supported |
+| Local | Not supported |
+
+The Postman Collection document automatically includes any _collection_ scoped variables. The Postman Collection is provided with the configuration variable `DAST_API_POSTMAN_COLLECTION`. This variable can be set to a single [exported Postman collection](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-collections).
+
+Variables from other scopes are provided through the `DAST_API_POSTMAN_COLLECTION_VARIABLES` configuration variable. The configuration variable supports a comma (`,`) delimited file list in [GitLab 15.1 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/356312). GitLab 15.0 and earlier, supports only one single file. The order of the files provided is not important as the files provide the needed scope information.
+
+The configuration variable `DAST_API_POSTMAN_COLLECTION_VARIABLES` can be set to:
+
+- [Exported Global environment](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments)
+- [Exported environments](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments)
+- [DAST API Custom JSON format](#dast-api-scope-custom-json-file-format)
+
+##### Undefined Postman variables
+
+There is a chance that DAST API engine does not find all variables references that your Postman collection file is using. Some cases can be:
+
+- You are using _data_ or _local_ scoped variables, and as stated previously these scopes are not supported by DAST API. Thus, assuming the values for these variables have not been provided through [the DAST API scope](#dast-api-scope-custom-json-file-format), then the values of the _data_ and _local_ scoped variables are undefined.
+- A variable name was typed incorrectly, and the name does not match the defined variable.
+- Postman Client supports a new dynamic variable that is not supported by DAST API.
+
+When possible, DAST API follows the same behavior as the Postman Client does when dealing with undefined variables. The text of the variable reference remains the same, and there is no text substitution. The same behavior also applies to any unsupported dynamic variables.
+
+For example, if a request definition in the Postman Collection references the variable `{{full_url}}` and the variable is not found it is left unchanged with the value `{{full_url}}`.
+
+##### Dynamic Postman variables
+
+In addition to variables that a user can define at various scope levels, Postman has a set of pre-defined variables called _dynamic_ variables. The [_dynamic_ variables](https://learning.postman.com/docs/writing-scripts/script-references/variables-list/) are already defined and their name is prefixed with a dollar sign (`$`), for instance, `$guid`. _Dynamic_ variables can be used like any other variable, and in the Postman Client, they produce random values during the request/collection run.
+
+An important difference between DAST API and Postman is that DAST API returns the same value for each usage of the same dynamic variables. This differs from the Postman Client behavior which returns a random value on each use of the same dynamic variable. In other words, DAST API uses static values for dynamic variables while Postman uses random values.
+
+The supported dynamic variables during the scanning process are:
+
+| Variable | Value |
+| ----------- | ----------- |
+| `$guid` | `611c2e81-2ccb-42d8-9ddc-2d0bfa65c1b4` |
+| `$isoTimestamp` | `2020-06-09T21:10:36.177Z` |
+| `$randomAbbreviation` | `PCI` |
+| `$randomAbstractImage` | `http://no-a-valid-host/640/480/abstract` |
+| `$randomAdjective` | `auxiliary` |
+| `$randomAlphaNumeric` | `a` |
+| `$randomAnimalsImage` | `http://no-a-valid-host/640/480/animals` |
+| `$randomAvatarImage` | `https://no-a-valid-host/path/to/some/image.jpg` |
+| `$randomBankAccount` | `09454073` |
+| `$randomBankAccountBic` | `EZIAUGJ1` |
+| `$randomBankAccountIban` | `MU20ZPUN3039684000618086155TKZ` |
+| `$randomBankAccountName` | `Home Loan Account` |
+| `$randomBitcoin` | `3VB8JGT7Y4Z63U68KGGKDXMLLH5` |
+| `$randomBoolean` | `true` |
+| `$randomBs` | `killer leverage schemas` |
+| `$randomBsAdjective` | `viral` |
+| `$randomBsBuzz` | `repurpose` |
+| `$randomBsNoun` | `markets` |
+| `$randomBusinessImage` | `http://no-a-valid-host/640/480/business` |
+| `$randomCatchPhrase` | `Future-proofed heuristic open architecture` |
+| `$randomCatchPhraseAdjective` | `Business-focused` |
+| `$randomCatchPhraseDescriptor` | `bandwidth-monitored` |
+| `$randomCatchPhraseNoun` | `superstructure` |
+| `$randomCatsImage` | `http://no-a-valid-host/640/480/cats` |
+| `$randomCity` | `Spinkahaven` |
+| `$randomCityImage` | `http://no-a-valid-host/640/480/city` |
+| `$randomColor` | `fuchsia` |
+| `$randomCommonFileExt` | `wav` |
+| `$randomCommonFileName` | `well_modulated.mpg4` |
+| `$randomCommonFileType` | `audio` |
+| `$randomCompanyName` | `Grady LLC` |
+| `$randomCompanySuffix` | `Inc` |
+| `$randomCountry` | `Kazakhstan` |
+| `$randomCountryCode` | `MD` |
+| `$randomCreditCardMask` | `3622` |
+| `$randomCurrencyCode` | `ZMK` |
+| `$randomCurrencyName` | `Pound Sterling` |
+| `$randomCurrencySymbol` | `£` |
+| `$randomDatabaseCollation` | `utf8_general_ci` |
+| `$randomDatabaseColumn` | `updatedAt` |
+| `$randomDatabaseEngine` | `Memory` |
+| `$randomDatabaseType` | `text` |
+| `$randomDateFuture` | `Tue Mar 17 2020 13:11:50 GMT+0530 (India Standard Time)` |
+| `$randomDatePast` | `Sat Mar 02 2019 09:09:26 GMT+0530 (India Standard Time)` |
+| `$randomDateRecent` | `Tue Jul 09 2019 23:12:37 GMT+0530 (India Standard Time)` |
+| `$randomDepartment` | `Electronics` |
+| `$randomDirectoryPath` | `/usr/local/bin` |
+| `$randomDomainName` | `trevor.info` |
+| `$randomDomainSuffix` | `org` |
+| `$randomDomainWord` | `jaden` |
+| `$randomEmail` | `Iva.Kovacek61@no-a-valid-host.com` |
+| `$randomExampleEmail` | `non-a-valid-user@example.net` |
+| `$randomFashionImage` | `http://no-a-valid-host/640/480/fashion` |
+| `$randomFileExt` | `war` |
+| `$randomFileName` | `neural_sri_lanka_rupee_gloves.gdoc` |
+| `$randomFilePath` | `/home/programming_chicken.cpio` |
+| `$randomFileType` | `application` |
+| `$randomFirstName` | `Chandler` |
+| `$randomFoodImage` | `http://no-a-valid-host/640/480/food` |
+| `$randomFullName` | `Connie Runolfsdottir` |
+| `$randomHexColor` | `#47594a` |
+| `$randomImageDataUri` | `data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20version%3D%221.1%22%20baseProfile%3D%22full%22%20width%3D%22undefined%22%20height%3D%22undefined%22%3E%20%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22grey%22%2F%3E%20%20%3Ctext%20x%3D%220%22%20y%3D%2220%22%20font-size%3D%2220%22%20text-anchor%3D%22start%22%20fill%3D%22white%22%3Eundefinedxundefined%3C%2Ftext%3E%20%3C%2Fsvg%3E` |
+| `$randomImageUrl` | `http://no-a-valid-host/640/480` |
+| `$randomIngverb` | `navigating` |
+| `$randomInt` | `494` |
+| `$randomIP` | `241.102.234.100` |
+| `$randomIPV6` | `dbe2:7ae6:119b:c161:1560:6dda:3a9b:90a9` |
+| `$randomJobArea` | `Mobility` |
+| `$randomJobDescriptor` | `Senior` |
+| `$randomJobTitle` | `International Creative Liaison` |
+| `$randomJobType` | `Supervisor` |
+| `$randomLastName` | `Schneider` |
+| `$randomLatitude` | `55.2099` |
+| `$randomLocale` | `ny` |
+| `$randomLongitude` | `40.6609` |
+| `$randomLoremLines` | `Ducimus in ut mollitia.\nA itaque non.\nHarum temporibus nihil voluptas.\nIste in sed et nesciunt in quaerat sed.` |
+| `$randomLoremParagraph` | `Ab aliquid odio iste quo voluptas voluptatem dignissimos velit. Recusandae facilis qui commodi ea magnam enim nostrum quia quis. Nihil est suscipit assumenda ut voluptatem sed. Esse ab voluptas odit qui molestiae. Rem est nesciunt est quis ipsam expedita consequuntur.` |
+| `$randomLoremParagraphs` | `Voluptatem rem magnam aliquam ab id aut quaerat. Placeat provident possimus voluptatibus dicta velit non aut quasi. Mollitia et aliquam expedita sunt dolores nam consequuntur. Nam dolorum delectus ipsam repudiandae et ipsam ut voluptatum totam. Nobis labore labore recusandae ipsam quo.` |
+| `$randomLoremSentence` | `Molestias consequuntur nisi non quod.` |
+| `$randomLoremSentences` | `Et sint voluptas similique iure amet perspiciatis vero sequi atque. Ut porro sit et hic. Neque aspernatur vitae fugiat ut dolore et veritatis. Ab iusto ex delectus animi. Voluptates nisi iusto. Impedit quod quae voluptate qui.` |
+| `$randomLoremSlug` | `eos-aperiam-accusamus, beatae-id-molestiae, qui-est-repellat` |
+| `$randomLoremText` | `Quisquam asperiores exercitationem ut ipsum. Aut eius nesciunt. Et reiciendis aut alias eaque. Nihil amet laboriosam pariatur eligendi. Sunt ullam ut sint natus ducimus. Voluptas harum aspernatur soluta rem nam.` |
+| `$randomLoremWord` | `est` |
+| `$randomLoremWords` | `vel repellat nobis` |
+| `$randomMACAddress` | `33:d4:68:5f:b4:c7` |
+| `$randomMimeType` | `audio/vnd.vmx.cvsd` |
+| `$randomMonth` | `February` |
+| `$randomNamePrefix` | `Dr.` |
+| `$randomNameSuffix` | `MD` |
+| `$randomNatureImage` | `http://no-a-valid-host/640/480/nature` |
+| `$randomNightlifeImage` | `http://no-a-valid-host/640/480/nightlife` |
+| `$randomNoun` | `bus` |
+| `$randomPassword` | `t9iXe7COoDKv8k3` |
+| `$randomPeopleImage` | `http://no-a-valid-host/640/480/people` |
+| `$randomPhoneNumber` | `700-008-5275` |
+| `$randomPhoneNumberExt` | `27-199-983-3864` |
+| `$randomPhrase` | `You can't program the monitor without navigating the mobile XML program!` |
+| `$randomPrice` | `531.55` |
+| `$randomProduct` | `Pizza` |
+| `$randomProductAdjective` | `Unbranded` |
+| `$randomProductMaterial` | `Steel` |
+| `$randomProductName` | `Handmade Concrete Tuna` |
+| `$randomProtocol` | `https` |
+| `$randomSemver` | `7.0.5` |
+| `$randomSportsImage` | `http://no-a-valid-host/640/480/sports` |
+| `$randomStreetAddress` | `5742 Harvey Streets` |
+| `$randomStreetName` | `Kuhic Island` |
+| `$randomTransactionType` | `payment` |
+| `$randomTransportImage` | `http://no-a-valid-host/640/480/transport` |
+| `$randomUrl` | `https://no-a-valid-host.net` |
+| `$randomUserAgent` | `Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.9.8; rv:15.6) Gecko/20100101 Firefox/15.6.6` |
+| `$randomUserName` | `Jarrell.Gutkowski` |
+| `$randomUUID` | `6929bb52-3ab2-448a-9796-d6480ecad36b` |
+| `$randomVerb` | `navigate` |
+| `$randomWeekday` | `Thursday` |
+| `$randomWord` | `withdrawal` |
+| `$randomWords` | `Samoa Synergistic sticky copying Grocery` |
+| `$timestamp` | `1562757107` |
+
+##### Example: Global Scope
+
+In this example, [the _global_ scope is exported](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments) from the Postman Client as `global-scope.json` and provided to DAST API through the `DAST_API_POSTMAN_COLLECTION_VARIABLES` configuration variable.
Here is an example of using `DAST_API_POSTMAN_COLLECTION_VARIABLES`:
@@ -308,21 +652,170 @@ include:
variables:
DAST_API_PROFILE: Quick
- DAST_API_POSTMAN_COLLECTION: postman-collection_serviceA.json
- DAST_API_POSTMAN_COLLECTION_VARIABLES: variable-collection-dictionary.json
+ DAST_API_POSTMAN_COLLECTION: postman-collection.json
+ DAST_API_POSTMAN_COLLECTION_VARIABLES: global-scope.json
+ DAST_API_TARGET_URL: http://test-deployment/
+```
+
+##### Example: Environment Scope
+
+In this example, [the _environment_ scope is exported](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments) from the Postman Client as `environment-scope.json` and provided to DAST API through the `DAST_API_POSTMAN_COLLECTION_VARIABLES` configuration variable.
+
+Here is an example of using `DAST_API_POSTMAN_COLLECTION_VARIABLES`:
+
+```yaml
+stages:
+ - dast
+
+include:
+ - template: DAST-API.gitlab-ci.yml
+
+variables:
+ DAST_API_PROFILE: Quick
+ DAST_API_POSTMAN_COLLECTION: postman-collection.json
+ DAST_API_POSTMAN_COLLECTION_VARIABLES: environment-scope.json
DAST_API_TARGET_URL: http://test-deployment/
```
-The file `variable-collection-dictionary.json` is a JSON document. This JSON is an object with
-key-value pairs for properties. The keys are the variables' names, and the values are the variables'
+##### Example: Collection Scope
+
+The _collection_ scope variables are included in the exported Postman Collection file and provided through the `DAST_API_POSTMAN_COLLECTION` configuration variable.
+
+Here is an example of using `DAST_API_POSTMAN_COLLECTION`:
+
+```yaml
+stages:
+ - dast
+
+include:
+ - template: DAST-API.gitlab-ci.yml
+
+variables:
+ DAST_API_PROFILE: Quick
+ DAST_API_POSTMAN_COLLECTION: postman-collection.json
+ DAST_API_TARGET_URL: http://test-deployment/
+```
+
+##### Example: DAST API Scope
+
+The DAST API Scope is used for two main purposes, defining _data_ and _local_ scope variables that are not supported by DAST API, and changing the value of an existing variable defined in another scope. The DAST API Scope is provided through the `DAST_API_POSTMAN_COLLECTION_VARIABLES` configuration variable.
+
+Here is an example of using `DAST_API_POSTMAN_COLLECTION_VARIABLES`:
+
+```yaml
+stages:
+ - dast
+
+include:
+ - template: DAST-API.gitlab-ci.yml
+
+variables:
+ DAST_API_PROFILE: Quick
+ DAST_API_POSTMAN_COLLECTION: postman-collection.json
+ DAST_API_POSTMAN_COLLECTION_VARIABLES: dast-api-scope.json
+ DAST_API_TARGET_URL: http://test-deployment/
+```
+
+The file `dast-api-scope.json` uses our [custom JSON file format](#dast-api-scope-custom-json-file-format). This JSON is an object with key-value pairs for properties. The keys are the variables' names, and the values are the variables'
values. For example:
- ```json
- {
- "base_url": "http://127.0.0.1/",
- "token": "Token 84816165151"
- }
- ```
+```json
+{
+ "base_url": "http://127.0.0.1/",
+ "token": "Token 84816165151"
+}
+```
+
+##### Example: Multiple Scopes
+
+In this example, a _global_ scope, _environment_ scope, and _collection_ scope are configured. The first step is to export our various scopes.
+
+- [Export the _global_ scope](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments) as `global-scope.json`
+- [Export the _environment_ scope](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments) as `environment-scope.json`
+- Export the Postman Collection which includes the _collection_ scope as `postman-collection.json`
+
+The Postman Collection is provided using the `DAST_API_POSTMAN_COLLECTION` variable, while the other scopes are provided using the `DAST_API_POSTMAN_COLLECTION_VARIABLES`. DAST API can identify which scope the provided files match using data provided in each file.
+
+```yaml
+stages:
+ - dast
+
+include:
+ - template: DAST-API.gitlab-ci.yml
+
+variables:
+ DAST_API_PROFILE: Quick
+ DAST_API_POSTMAN_COLLECTION: postman-collection.json
+ DAST_API_POSTMAN_COLLECTION_VARIABLES: global-scope.json,environment-scope.json
+ DAST_API_TARGET_URL: http://test-deployment/
+```
+
+##### Example: Changing a Variables Value
+
+When using exported scopes, it's often the case that the value of a variable must be changed for use with DAST API. For example, a _collection_ scoped variable might contain a variable named `api_version` with a value of `v2`, while your test needs a value of `v1`. Instead of modifying the exported collection to change the value, the DAST API scope can be used to change its value. This works because the _DAST API_ scope takes precedence over all other scopes.
+
+The _collection_ scope variables are included in the exported Postman Collection file and provided through the `DAST_API_POSTMAN_COLLECTION` configuration variable.
+
+The DAST API Scope is provided through the `DAST_API_POSTMAN_COLLECTION_VARIABLES` configuration variable, but first, we must create the file.
+The file `dast-api-scope.json` uses our [custom JSON file format](#dast-api-scope-custom-json-file-format). This JSON is an object with key-value pairs for properties. The keys are the variables' names, and the values are the variables'
+values. For example:
+
+```json
+{
+ "api_version": "v1"
+}
+```
+
+Our CI definition:
+
+```yaml
+stages:
+ - dast
+
+include:
+ - template: DAST-API.gitlab-ci.yml
+
+variables:
+ DAST_API_PROFILE: Quick
+ DAST_API_POSTMAN_COLLECTION: postman-collection.json
+ DAST_API_POSTMAN_COLLECTION_VARIABLES: dast-api-scope.json
+ DAST_API_TARGET_URL: http://test-deployment/
+```
+
+##### Example: Changing a Variables Value with Multiple Scopes
+
+When using exported scopes, it's often the case that the value of a variable must be changed for use with DAST API. For example, an _environment_ scope might contain a variable named `api_version` with a value of `v2`, while your test needs a value of `v1`. Instead of modifying the exported file to change the value, the DAST API scope can be used. This works because the _DAST API_ scope takes precedence over all other scopes.
+
+In this example, a _global_ scope, _environment_ scope, _collection_ scope, and _DAST API_ scope are configured. The first step is to export and create our various scopes.
+
+- [Export the _global_ scope](https://learning.postman.com/docs/sending-requests/variables/#downloading-global-environments) as `global-scope.json`
+- [Export the _environment_ scope](https://learning.postman.com/docs/getting-started/importing-and-exporting-data/#exporting-environments) as `environment-scope.json`
+- Export the Postman Collection which includes the _collection_ scope as `postman-collection.json`
+
+The DAST API scope is used by creating a file `dast-api-scope.json` using our [custom JSON file format](#dast-api-scope-custom-json-file-format). This JSON is an object with key-value pairs for properties. The keys are the variables' names, and the values are the variables'
+values. For example:
+
+```json
+{
+ "api_version": "v1"
+}
+```
+
+The Postman Collection is provided using the `DAST_API_POSTMAN_COLLECTION` variable, while the other scopes are provided using the `DAST_API_POSTMAN_COLLECTION_VARIABLES`. DAST API can identify which scope the provided files match using data provided in each file.
+
+```yaml
+stages:
+ - dast
+
+include:
+ - template: DAST-API.gitlab-ci.yml
+
+variables:
+ DAST_API_PROFILE: Quick
+ DAST_API_POSTMAN_COLLECTION: postman-collection.json
+ DAST_API_POSTMAN_COLLECTION_VARIABLES: global-scope.json,environment-scope.json,dast-api-scope.json
+ DAST_API_TARGET_URL: http://test-deployment/
+```
## Authentication
@@ -332,17 +825,19 @@ provide a script that performs an authentication flow or calculates the token.
### HTTP Basic Authentication
[HTTP basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
-is an authentication method built in to the HTTP protocol and used in conjunction with
+is an authentication method built into the HTTP protocol and used in conjunction with
[transport layer security (TLS)](https://en.wikipedia.org/wiki/Transport_Layer_Security).
-To use HTTP basic authentication, two CI/CD variables are added to your `.gitlab-ci.yml` file:
-- `DAST_API_HTTP_USERNAME`: The username for authentication.
-- `DAST_API_HTTP_PASSWORD`: The password for authentication.
+We recommended that you [create a CI/CD variable](../../../ci/variables/index.md#custom-cicd-variables)
+for the password (for example, `TEST_API_PASSWORD`), and set it to be masked. You can create CI/CD
+variables from the GitLab project's page at **Settings > CI/CD**, in the **Variables** section.
+Because of the [limitations on masked variables](../../../ci/variables/index.md#mask-a-cicd-variable),
+you should Base64-encode the password before adding it as a variable.
-For the password, we recommended that you [create a CI/CD variable](../../../ci/variables/index.md#custom-cicd-variables)
-(for example, `TEST_API_PASSWORD`) set to the password. You can create CI/CD variables from the
-GitLab projects page at **Settings > CI/CD**, in the **Variables** section. Use that variable
-as the value for `DAST_API_HTTP_PASSWORD`:
+Finally, add two CI/CD variables to your `.gitlab-ci.yml` file:
+
+- `DAST_API_HTTP_USERNAME`: The username for authentication.
+- `DAST_API_HTTP_PASSWORD_BASE64`: The Base64-encoded password for authentication.
```yaml
stages:
@@ -356,9 +851,13 @@ variables:
DAST_API_HAR: test-api-recording.har
DAST_API_TARGET_URL: http://test-deployment/
DAST_API_HTTP_USERNAME: testuser
- DAST_API_HTTP_PASSWORD: $TEST_API_PASSWORD
+ DAST_API_HTTP_PASSWORD_BASE64: $TEST_API_PASSWORD
```
+#### Raw password
+
+If you do not want to Base64-encode the password (or if you are using GitLab 15.3 or earlier) you can provide the raw password `DAST_API_HTTP_PASSWORD`, instead of using `DAST_API_HTTP_PASSWORD_BASE64`.
+
### Bearer tokens
Bearer tokens are used by several different authentication mechanisms, including OAuth2 and JSON Web
@@ -383,7 +882,7 @@ Follow these steps to provide the Bearer token with `DAST_API_OVERRIDES_ENV`:
can create CI/CD variables from the GitLab projects page at **Settings > CI/CD**, in the
**Variables** section.
Due to the format of `TEST_API_BEARERAUTH` it's not possible to mask the variable.
- To mask the token's value, you can create a second variable with the token value's, and define
+ To mask the token's value, you can create a second variable with the token values, and define
`TEST_API_BEARERAUTH` with the value `{"headers":{"Authorization":"Bearer $MASKED_VARIABLE"}}`.
1. In your `.gitlab-ci.yml` file, set `DAST_API_OVERRIDES_ENV` to the variable you just created:
@@ -402,12 +901,12 @@ Follow these steps to provide the Bearer token with `DAST_API_OVERRIDES_ENV`:
DAST_API_OVERRIDES_ENV: $TEST_API_BEARERAUTH
```
-1. To validate that authentication is working, run an DAST API test and review the job logs
+1. To validate that authentication is working, run a DAST API test and review the job logs
and the test API's application logs.
#### Token generated at test runtime
-If the Bearer token must be generated and doesn't expire during testing, you can provide DAST API a file that has the token. A prior stage and job, or part of the DAST API job, can
+If the Bearer token must be generated and doesn't expire during testing, you can provide DAST API with a file that has the token. A prior stage and job, or part of the DAST API job, can
generate this file.
DAST API expects to receive a JSON file with the following structure:
@@ -439,7 +938,7 @@ variables:
DAST_API_OVERRIDES_FILE: dast-api-overrides.json
```
-To validate that authentication is working, run an DAST API test and review the job logs and
+To validate that authentication is working, run a DAST API test and review the job logs and
the test API's application logs.
#### Token has short expiration
@@ -552,8 +1051,10 @@ can be added, removed, and modified by creating a custom configuration.
|[`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. |
|[`DAST_API_OPENAPI_MEDIA_TYPES`](#openapi-specification) | Colon (`:`) separated media types accepted for testing. Default is disabled. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/333304) in GitLab 14.10. |
|[`DAST_API_HAR`](#http-archive-har) | HTTP Archive (HAR) file. |
+|[`DAST_API_GRAPHQL`](#graphql-schema) | Path to GraphQL endpoint, for example `/api/graphql`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352780) in GitLab 15.4. |
+|[`DAST_API_GRAPHQL_SCHEMA`](#graphql-schema) | A URL or filename for a GraphQL schema in JSON format. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/352780) in GitLab 15.4. |
|[`DAST_API_POSTMAN_COLLECTION`](#postman-collection) | Postman Collection file. |
-|[`DAST_API_POSTMAN_COLLECTION_VARIABLES`](#postman-variables) | Path to a JSON file to extract postman variable values. |
+|[`DAST_API_POSTMAN_COLLECTION_VARIABLES`](#postman-variables) | Path to a JSON file to extract Postman variable values. The support for comma-separated (`,`) files was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/356312) in GitLab 15.1. |
|[`DAST_API_OVERRIDES_FILE`](#overrides) | Path to a JSON file containing overrides. |
|[`DAST_API_OVERRIDES_ENV`](#overrides) | JSON string containing headers to override. |
|[`DAST_API_OVERRIDES_CMD`](#overrides) | Overrides command. |
@@ -562,7 +1063,8 @@ can be added, removed, and modified by creating a custom configuration.
|`DAST_API_POST_SCRIPT` | Run user command or script after scan session has finished. |
|[`DAST_API_OVERRIDES_INTERVAL`](#overrides) | How often to run overrides command in seconds. Defaults to `0` (once). |
|[`DAST_API_HTTP_USERNAME`](#http-basic-authentication) | Username for HTTP authentication. |
-|[`DAST_API_HTTP_PASSWORD`](#http-basic-authentication) | Password for HTTP authentication. |
+|[`DAST_API_HTTP_PASSWORD`](#http-basic-authentication) | Password for HTTP authentication. Consider using `DAST_API_HTTP_PASSWORD_BASE64` instead. |
+|[`DAST_API_HTTP_PASSWORD_BASE64`](#http-basic-authentication) | Password for HTTP authentication, base64-encoded. [Introduced](https://gitlab.com/gitlab-org/security-products/analyzers/api-fuzzing-src/-/merge_requests/702) in GitLab 15.4. |
|`DAST_API_SERVICE_START_TIMEOUT` | How long to wait for target API to become available in seconds. Default is 300 seconds. |
|`DAST_API_TIMEOUT` | How long to wait for API responses in seconds. Default is 30 seconds. |
@@ -1010,21 +1512,21 @@ This example excludes the `/auth` resource. This does not exclude child resource
```yaml
variables:
- DAST_API_EXCLUDE_PATHS=/auth
+ DAST_API_EXCLUDE_PATHS: /auth
```
To exclude `/auth`, and child resources (`/auth/child`), we use a wildcard.
```yaml
variables:
- DAST_API_EXCLUDE_PATHS=/auth*
+ DAST_API_EXCLUDE_PATHS: /auth*
```
To exclude multiple paths we use the `;` character. In this example we exclude `/auth*` and `/v1/*`.
```yaml
variables:
- DAST_API_EXCLUDE_PATHS=/auth*;/v1/*
+ DAST_API_EXCLUDE_PATHS: /auth*;/v1/*
```
To exclude one or more nested levels within a path we use `**`. In this example we are testing API endpoints. We are testing `/api/v1/` and `/api/v2/` of a data query requesting `mass`, `brightness` and `coordinates` data for `planet`, `moon`, `star`, and `satellite` objects. Example paths that could be scanned include, but are not limited to:
@@ -1037,7 +1539,7 @@ In this example we test the `brightness` endpoint only:
```yaml
variables:
- DAST_API_EXCLUDE_PATHS=/api/**/mass;/api/**/coordinates
+ DAST_API_EXCLUDE_PATHS: /api/**/mass;/api/**/coordinates
```
### Exclude parameters
@@ -1297,7 +1799,15 @@ Each value in `DAST_API_EXCLUDE_URLS` is a regular expression. Characters such a
The following example excludes the URL `http://target/api/auth` and its child resources.
```yaml
+stages:
+ - dast
+
+include:
+ - template: DAST-API.gitlab-ci.yml
+
variables:
+ DAST_API_TARGET_URL: http://target/
+ DAST_API_OPENAPI: test-api-specification.json
DAST_API_EXCLUDE_URLS: http://target/api/auth
```
@@ -1306,7 +1816,15 @@ variables:
To exclude the URLs `http://target/api/buy` and `http://target/api/sell` but allowing to scan their child resources, for instance: `http://target/api/buy/toy` or `http://target/api/sell/chair`. You could use the value `http://target/api/buy/$,http://target/api/sell/$`. This value is using two regular expressions, each of them separated by a `,` character. Hence, it contains `http://target/api/buy$` and `http://target/api/sell$`. In each regular expression, the trailing `$` character points out where the matching URL should end.
```yaml
+stages:
+ - dast
+
+include:
+ - template: DAST-API.gitlab-ci.yml
+
variables:
+ DAST_API_TARGET_URL: http://target/
+ DAST_API_OPENAPI: test-api-specification.json
DAST_API_EXCLUDE_URLS: http://target/api/buy/$,http://target/api/sell/$
```
@@ -1315,7 +1833,15 @@ variables:
In order to exclude the URLs: `http://target/api/buy` and `http://target/api/sell`, and their child resources. To provide multiple URLs we use the `,` character as follows:
```yaml
+stages:
+ - dast
+
+include:
+ - template: DAST-API.gitlab-ci.yml
+
variables:
+ DAST_API_TARGET_URL: http://target/
+ DAST_API_OPENAPI: test-api-specification.json
DAST_API_EXCLUDE_URLS: http://target/api/buy,http://target/api/sell
```
@@ -1324,7 +1850,15 @@ variables:
In order to exclude exactly `https://target/api/v1/user/create` and `https://target/api/v2/user/create` or any other version (`v3`,`v4`, and more). We could use `https://target/api/v.*/user/create$`, in the previous regular expression `.` indicates any character and `*` indicates zero or more times, additionally `$` indicates that the URL should end there.
```yaml
+stages:
+ - dast
+
+include:
+ - template: DAST-API.gitlab-ci.yml
+
variables:
+ DAST_API_TARGET_URL: http://target/
+ DAST_API_OPENAPI: test-api-specification.json
DAST_API_EXCLUDE_URLS: https://target/api/v.*/user/create$
```
@@ -1544,7 +2078,7 @@ The DAST API engine outputs an error message when it cannot establish a connecti
**Error message**
- In [GitLab 13.11 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/323939), `Failed to start scanner session (version header not found).`
-- In GitLab 13.10 and earlier, `API Security version header not found. Are you sure that you are connecting to the API Security server?`.
+- In GitLab 13.10 and earlier, `API Security version header not found. Are you sure that you are connecting to the DAST API server?`.
**Solution**
@@ -1568,12 +2102,15 @@ This solution is for pipelines in which the target API URL doesn't change (is st
For environments where the target API remains the same, we recommend you specify the target URL by using the `DAST_API_TARGET_URL` environment variable. In your `.gitlab-ci.yml`, add a variable `DAST_API_TARGET_URL`. The variable must be set to the base URL of API testing target. For example:
```yaml
+stages:
+ - dast
+
include:
- - template: DAST-API.gitlab-ci.yml
+ - template: DAST-API.gitlab-ci.yml
- variables:
- DAST_API_TARGET_URL: http://test-deployment/
- DAST_API_OPENAPI: test-api-specification.json
+variables:
+ DAST_API_TARGET_URL: http://test-deployment/
+ DAST_API_OPENAPI: test-api-specification.json
```
#### Dynamic environment solutions
@@ -1603,7 +2140,7 @@ deploy-test-target:
### Use OpenAPI with an invalid schema
-There are cases where the document is autogenerated with an invalid schema or cannot be edited manually in a timely manner. In those scenarios, the API Security is able to perform a relaxed validation by setting the variable `DAST_API_OPENAPI_RELAXED_VALIDATION`. We recommend providing a fully compliant OpenAPI document to prevent unexpected behaviors.
+There are cases where the document is autogenerated with an invalid schema or cannot be edited manually in a timely manner. In those scenarios, the DAST API is able to perform a relaxed validation by setting the variable `DAST_API_OPENAPI_RELAXED_VALIDATION`. We recommend providing a fully compliant OpenAPI document to prevent unexpected behaviors.
#### Edit a non-compliant OpenAPI file
@@ -1620,25 +2157,25 @@ If your OpenAPI document is generated manually, load your document in the editor
Relaxed validation is meant for cases when the OpenAPI document cannot meet OpenAPI specifications, but it still has enough content to be consumed by different tools. A validation is performed but less strictly in regards to document schema.
-API Security can still try to consume an OpenAPI document that does not fully comply with OpenAPI specifications. To instruct API Security to perform a relaxed validation, set the variable `DAST_API_OPENAPI_RELAXED_VALIDATION` to any value, for example:
+DAST API can still try to consume an OpenAPI document that does not fully comply with OpenAPI specifications. To instruct DAST API to perform a relaxed validation, set the variable `DAST_API_OPENAPI_RELAXED_VALIDATION` to any value, for example:
```yaml
- stages:
- - dast
+stages:
+ - dast
- include:
- - template: DAST-API.gitlab-ci.yml
+include:
+ - template: DAST-API.gitlab-ci.yml
- variables:
- DAST_API_PROFILE: Quick
- DAST_API_TARGET_URL: http://test-deployment/
- DAST_API_OPENAPI: test-api-specification.json
- DAST_API_OPENAPI_RELAXED_VALIDATION: On
+variables:
+ DAST_API_PROFILE: Quick
+ DAST_API_TARGET_URL: http://test-deployment/
+ DAST_API_OPENAPI: test-api-specification.json
+ DAST_API_OPENAPI_RELAXED_VALIDATION: 'On'
```
### `No operation in the OpenAPI document is consuming any supported media type`
-API Security uses the specified media types in the OpenAPI document to generate requests. If no request can be created due to the lack of supported media types, then an error will be thrown.
+DAST API uses the specified media types in the OpenAPI document to generate requests. If no request can be created due to the lack of supported media types, then an error will be thrown.
**Error message**
diff --git a/doc/user/application_security/dependency_scanning/analyzers.md b/doc/user/application_security/dependency_scanning/analyzers.md
index acbc94cba47..a59399f7e8d 100644
--- a/doc/user/application_security/dependency_scanning/analyzers.md
+++ b/doc/user/application_security/dependency_scanning/analyzers.md
@@ -32,14 +32,6 @@ The Dependency Scanning analyzers' current major version number is 2.
Dependency Scanning is pre-configured with a set of **default images** that are
maintained by GitLab, but users can also integrate their own **custom images**.
-<!--- start_remove The following content will be removed on remove_date: '2022-08-22' -->
-
-The [`bundler-audit`](https://gitlab.com/gitlab-org/gitlab/-/issues/289832) and [`retire.js`](https://gitlab.com/gitlab-org/gitlab/-/issues/350510) analyzers were deprecated
-in GitLab 14.8 and [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86704) in 15.0.
-Use Gemnasium instead.
-
-<!--- end_remove -->
-
## Official default analyzers
Any custom change to the official analyzers can be achieved by using a
diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md
index 521bb6adbf0..7aabbdd3194 100644
--- a/doc/user/application_security/dependency_scanning/index.md
+++ b/doc/user/application_security/dependency_scanning/index.md
@@ -50,12 +50,12 @@ possible, we encourage you to use all of our security scanning tools:
declared software dependencies (and those installed as a sub-dependency).
Dependency Scanning can not detect software dependencies that are pre-bundled
into the container's base image. To identify pre-bundled dependencies, enable
- [Container Scanning](../container_scanning/) language scanning using the
- [`CS_DISABLE_LANGUAGE_VULNERABILITY_SCAN` variable](../container_scanning/#report-language-specific-findings).
-- [Container Scanning](../container_scanning/) analyzes your containers and tells
+ [Container Scanning](../container_scanning/index.md) language scanning using the
+ [`CS_DISABLE_LANGUAGE_VULNERABILITY_SCAN` variable](../container_scanning/index.md#report-language-specific-findings).
+- [Container Scanning](../container_scanning/index.md) analyzes your containers and tells
you about known risks in the operating system's (OS) packages. You can configure it
to also report on software and language dependencies, if you enable it and use
- the [`CS_DISABLE_LANGUAGE_VULNERABILITY_SCAN` variable](../container_scanning/#report-language-specific-findings).
+ the [`CS_DISABLE_LANGUAGE_VULNERABILITY_SCAN` variable](../container_scanning/index.md#report-language-specific-findings).
Turning this variable on can result in some duplicate findings, as we do not yet
de-duplicate results between Container Scanning and Dependency Scanning. For more details,
efforts to de-duplicate these findings can be tracked in
@@ -304,7 +304,7 @@ table.supported-languages ul {
<li>
<a id="notes-regarding-supported-languages-and-package-managers-3"></a>
<p>
- npm is only supported when `lockfileVersion = 1` or `lockfileVersion = 2`. Work to add support for `lockfileVersion = 3` is being tracked in issue <a href="https://gitlab.com/gitlab-org/gitlab/-/issues/365176">GitLab#365176</a>.
+ 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>.
</p>
</li>
<li>
@@ -452,8 +452,8 @@ We only execute one build in the directory where a build file has been detected.
multiple Gradle, Maven, or sbt builds, or any combination of these, `gemnasium-maven` only analyzes dependencies for the first build file
that is detected. Build files are searched for in the following order:
-1. `build.gradle` or `build.gradle.kts` for single or [multi-project](https://docs.gradle.org/current/userguide/intro_multi_project_builds.html) Gradle builds.
1. `pom.xml` for single or [multi-module](https://maven.apache.org/pom.html#Aggregation) Maven projects.
+1. `build.gradle` or `build.gradle.kts` for single or [multi-project](https://docs.gradle.org/current/userguide/intro_multi_project_builds.html) Gradle builds.
1. `build.sbt` for single or [multi-project](https://www.scala-sbt.org/1.x/docs/Multi-Project.html) sbt builds.
The search begins with the root directory and then continues with subdirectories if no builds are found in the root directory. Consequently an sbt build file in the root directory would be detected before a Gradle build file in a subdirectory.
@@ -521,7 +521,7 @@ always take the latest dependency scanning artifact available.
To enable Dependency Scanning in a project, you can create a merge request:
-1. On the top bar, select **Menu > Projects** and find your project.
+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 **Dependency Scanning** row, select **Configure with a merge request**.
1. Review and merge the merge request to enable Dependency Scanning.
@@ -627,7 +627,7 @@ The following variables are used for configuring specific analyzers (used for a
The previous tables are not an exhaustive list of all variables that can be used. They contain all specific GitLab and analyzer variables we support and test. There are many variables, such as environment variables, that you can pass in and they will work. This is a large list, many of which we may be unaware of, and as such is not documented.
For example, to pass the non-GitLab environment variable `HTTPS_PROXY` to all Dependency Scanning jobs,
-set it as a [custom CI/CD variable in your `.gitlab-ci.yml`](../../../ci/variables/#create-a-custom-cicd-variable-in-the-gitlab-ciyml-file)
+set it as a [custom CI/CD variable in your `.gitlab-ci.yml`](../../../ci/variables/index.md#create-a-custom-cicd-variable-in-the-gitlab-ciyml-file)
file like this:
```yaml
diff --git a/doc/user/application_security/generate_test_vulnerabilities/index.md b/doc/user/application_security/generate_test_vulnerabilities/index.md
index aafbebb91cd..4d424acf9c3 100644
--- a/doc/user/application_security/generate_test_vulnerabilities/index.md
+++ b/doc/user/application_security/generate_test_vulnerabilities/index.md
@@ -1,6 +1,6 @@
---
type: reference, howto
-stage: Secure
+stage: Govern
group: Threat Insights
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
---
diff --git a/doc/user/application_security/get-started-security.md b/doc/user/application_security/get-started-security.md
index 4c2b971b5fa..ee7864b5ce9 100644
--- a/doc/user/application_security/get-started-security.md
+++ b/doc/user/application_security/get-started-security.md
@@ -6,25 +6,36 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Get started with GitLab application security **(ULTIMATE)**
-Complete the following steps to get the most from GitLab application security tools.
+<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
+For an overview, see [Adopting GitLab application security](https://www.youtube.com/watch?v=5QlxkiKR04k).
-1. Enable [Secret Detection](secret_detection/index.md) scanning for your default branch.
-1. Enable [Dependency Scanning](dependency_scanning/index.md) for your default branch so you can start identifying existing
+The following steps will help you get the most from GitLab application security tools. These steps are a recommended order of operations. You can choose to implement capabilities in a different order or omit features that do not apply to your specific needs.
+
+1. Enable [Secret Detection](secret_detection/index.md) and [Dependency Scanning](dependency_scanning/index.md)
+ to identify any leaked secrets and vulnerable packages in your codebase.
+
+ - For all security scanners, enable them by updating your `[.gitlab-ci.yml](../../ci/yaml/gitlab_ci_yaml.md)` directly on your `default` branch. This creates a baseline scan of your `default` branch, which is necessary for
+ feature branch scans to be compared against. This allows [merge requests](../project/merge_requests/index.md)
+ to display only newly-introduced vulnerabilities. Otherwise, merge requests will display every
+ vulnerability in the branch, regardless of whether it was introduced by a change in the branch.
+ - If you are after simplicity, enable only Secret Detection first. It only has one analyzer,
+ no build requirements, and relatively simple findings: is this a secret or not?
+ - It is good practice to enable Dependency Scanning early so you can start identifying existing
vulnerable packages in your codebase.
-1. Add security scans to feature branch pipelines. The same scans should be enabled as are running
- on your default branch. Subsequent scans will show only new vulnerabilities by comparing the feature branch to the default branch results.
1. Let your team get comfortable with [vulnerability reports](vulnerability_report/index.md) and
establish a vulnerability triage workflow.
1. Consider creating [labels](../project/labels.md) and [issue boards](../project/issue_board.md) to
help manage issues created from vulnerabilities. Issue boards allow all stakeholders to have a
- common view of all issues.
+ common view of all issues and track remediation progress.
+1. Use [scheduled pipelines](../../ci/pipelines/schedules.md#scheduled-pipelines) to regularly scan important branches such as `default` or those used for maintenance releases.
+ - Running regular dependency and [container scans](container_scanning/index.md) will surface newly-discovered vulnerabilities that already exist in your repository.
+ - Scheduled scans are most useful for projects or important branches with low development activity where pipeline scans are infrequent.
1. Create a [scan result policy](policies/index.md) to limit new vulnerabilities from being merged
- into your default branch.
+ into your `default` branch.
1. Monitor the [Security Dashboard](security_dashboard/index.md) trends to gauge success in
remediating existing vulnerabilities and preventing the introduction of new ones.
1. Enable other scan types such as [SAST](sast/index.md), [DAST](dast/index.md),
[Fuzz testing](coverage_fuzzing/index.md), or [Container Scanning](container_scanning/index.md).
- Be sure to add the same scan types to both feature pipelines and default branch pipelines.
1. Use [Compliance Pipelines](../../user/project/settings/index.md#compliance-pipeline-configuration)
or [Scan Execution Policies](policies/scan-execution-policies.md) to enforce required scan types
and ensure separation of duties between security and engineering.
diff --git a/doc/user/application_security/iac_scanning/index.md b/doc/user/application_security/iac_scanning/index.md
index 16f08de738b..1b9cdb11ea3 100644
--- a/doc/user/application_security/iac_scanning/index.md
+++ b/doc/user/application_security/iac_scanning/index.md
@@ -116,7 +116,7 @@ that you can download and analyze.
To enable IaC Scanning in a project, you can create a merge request:
-1. On the top bar, select **Menu > Projects** and find your project.
+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 **Infrastructure as Code (IaC) Scanning** row, select **Configure with a merge request**.
1. Review and merge the merge request to enable IaC Scanning.
diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md
index 7c7d5380a24..fbd617351da 100644
--- a/doc/user/application_security/index.md
+++ b/doc/user/application_security/index.md
@@ -98,7 +98,7 @@ approach.
## Security scanning with Auto DevOps
To enable all GitLab Security scanning tools, with default settings, enable
-[Auto DevOps](../../topics/autodevops/):
+[Auto DevOps](../../topics/autodevops/index.md):
- [Auto SAST](../../topics/autodevops/stages.md#auto-sast)
- [Auto Secret Detection](../../topics/autodevops/stages.md#auto-secret-detection)
@@ -361,7 +361,7 @@ Learn more on overriding security jobs:
- [Overriding SAST jobs](sast/index.md#overriding-sast-jobs).
- [Overriding Dependency Scanning jobs](dependency_scanning/index.md#overriding-dependency-scanning-jobs).
- [Overriding Container Scanning jobs](container_scanning/index.md#overriding-the-container-scanning-template).
-- [Overriding Secret Detection jobs](secret_detection/index.md#customizing-settings).
+- [Overriding Secret Detection jobs](secret_detection/index.md#configure-scan-settings).
- [Overriding DAST jobs](dast/index.md#customize-dast-settings).
- [Overriding License Compliance jobs](../compliance/license_compliance/index.md#overriding-the-template).
@@ -397,15 +397,6 @@ Validation depends on the schema version declared in the security report artifac
You can always find supported and deprecated schema versions in the [source code](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/parsers/security/validators/schema_validator.rb).
-<!--- start_remove The following content will be removed on remove_date: '2022-08-22' -->
-
-### Enable security report validation (removed)
-
- This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/354928) in GitLab 14.9
- and [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85400) in GitLab 15.0.
-
- <!--- end_remove -->
-
## Interact with findings and vulnerabilities
You can interact with the results of the security scanning tools in several locations:
@@ -414,7 +405,7 @@ You can interact with the results of the security scanning tools in several loca
- [Project Security Dashboard](security_dashboard/index.md)
- [Security pipeline tab](security_dashboard/index.md)
- [Group Security Dashboard](security_dashboard/index.md)
-- [Security Center](security_dashboard/#security-center)
+- [Security Center](security_dashboard/index.md#security-center)
- [Vulnerability Report](vulnerability_report/index.md)
- [Vulnerability Pages](vulnerabilities/index.md)
- [Dependency List](dependency_list/index.md)
@@ -455,7 +446,7 @@ Security and compliance teams must ensure that security scans:
GitLab provides two methods of accomplishing this, each with advantages and disadvantages.
-- [Compliance framework pipelines](../project/settings/#compliance-pipeline-configuration)
+- [Compliance framework pipelines](../project/settings/index.md#compliance-pipeline-configuration)
are recommended when:
- Scan execution enforcement is required for any scanner that uses a GitLab template, such as SAST IaC, DAST, Dependency Scanning,
@@ -497,6 +488,11 @@ Feedback is welcome on our vision for [unifying the user experience for these tw
-->
### Secure job failing with exit code 1
+WARNING:
+Debug logging can be a serious security risk. The output may contain the content of
+environment variables and other secrets available to the job. The output is uploaded
+to the GitLab server and visible in job logs.
+
If a Secure job is failing and it's unclear why, add `SECURE_LOG_LEVEL: "debug"` as a global CI/CD variable for
more verbose output that is helpful for troubleshooting.
@@ -534,6 +530,11 @@ Select **new pipeline** to run a new pipeline.
### Getting warning messages `… report.json: no matching files`
+WARNING:
+Debug logging can be a serious security risk. The output may contain the content of
+environment variables and other secrets available to the job. The output is uploaded
+to the GitLab server and visible in job logs.
+
This message is often followed by the [error `No files to upload`](../../ci/pipelines/job_artifacts.md#error-message-no-files-to-upload),
and preceded by other errors or warnings that indicate why the JSON report wasn't generated. Check
the entire job log for such messages. If you don't find these messages, retry the failed job after
diff --git a/doc/user/application_security/offline_deployments/index.md b/doc/user/application_security/offline_deployments/index.md
index 7aeb094093c..7344695886a 100644
--- a/doc/user/application_security/offline_deployments/index.md
+++ b/doc/user/application_security/offline_deployments/index.md
@@ -87,9 +87,9 @@ above. You can find more information at each of the pages below:
- [Container scanning offline directions](../container_scanning/index.md#running-container-scanning-in-an-offline-environment)
- [SAST offline directions](../sast/index.md#running-sast-in-an-offline-environment)
-- [Secret Detection offline directions](../secret_detection/#running-secret-detection-in-an-offline-environment)
+- [Secret Detection offline directions](../secret_detection/index.md#running-secret-detection-in-an-offline-environment)
- [DAST offline directions](../dast/run_dast_offline.md#run-dast-in-an-offline-environment)
-- [API Fuzzing offline directions](../api_fuzzing/#running-api-fuzzing-in-an-offline-environment)
+- [API Fuzzing offline directions](../api_fuzzing/index.md#running-api-fuzzing-in-an-offline-environment)
- [License Compliance offline directions](../../compliance/license_compliance/index.md#running-license-compliance-in-an-offline-environment)
- [Dependency Scanning offline directions](../dependency_scanning/index.md#running-dependency-scanning-in-an-offline-environment)
@@ -236,4 +236,4 @@ an offline environment.
Note that these steps are specific to GitLab Secure with AutoDevOps. Using other stages with
AutoDevOps may require other steps covered in the
-[Auto DevOps documentation](../../../topics/autodevops/).
+[Auto DevOps documentation](../../../topics/autodevops/index.md).
diff --git a/doc/user/application_security/policies/index.md b/doc/user/application_security/policies/index.md
index 53f9c400259..9b86ef7316a 100644
--- a/doc/user/application_security/policies/index.md
+++ b/doc/user/application_security/policies/index.md
@@ -1,6 +1,6 @@
---
-stage: Protect
-group: Container Security
+stage: Govern
+group: Security Policies
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
---
@@ -54,7 +54,7 @@ to select, edit, and unlink a security policy project.
As a project owner, take the following steps to create or edit an association between your current
project and a project that you would like to designate as the security policy project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Security & Compliance > Policies**.
1. Select **Edit Policy Project**, and search for and select the
project you would like to link from the dropdown menu.
@@ -78,7 +78,7 @@ policies for all available environments. You can check a
policy's information (for example, description or enforcement
status), and create and edit deployed policies:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Security & Compliance > Policies**.
![Policies List Page](img/policies_list_v15_1.png)
@@ -89,7 +89,7 @@ status), and create and edit deployed policies:
You can use the policy editor to create, edit, and delete policies:
-1. On the top bar, select **Menu > Projects** and find your group.
+1. On the top bar, select **Main menu > Projects** and find your group.
1. On the left sidebar, select **Security & Compliance > Policies**.
- To create a new policy, select **New policy** which is located in the **Policies** page's header.
You can then select which type of policy to create.
@@ -144,6 +144,6 @@ for more information on the product direction of security policies within GitLab
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 that contain the text `update-policy-<timestamp>`, you will get an error that states `Branch name does not follow the pattern 'update-policy-<timestamp>'`.
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 eb1f9a7c7b8..f2fc52a2de8 100644
--- a/doc/user/application_security/policies/scan-execution-policies.md
+++ b/doc/user/application_security/policies/scan-execution-policies.md
@@ -1,12 +1,13 @@
---
-stage: Protect
-group: Container Security
+stage: Govern
+group: Security Policies
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
---
# Scan execution policies **(ULTIMATE)**
-> Group-level security policies were [introduced](https://gitlab.com/groups/gitlab-org/-/epics/4425) in GitLab 15.2 [with a flag](../../../administration/feature_flags.md) named `group_level_security_policies`. Enabled by default.
+> - 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.
Group, sub-group, 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 sub-group level) pipeline. Required scans are injected into the CI pipeline as new jobs
@@ -14,10 +15,10 @@ with a long, random job name. In the unlikely event of a job name collision, the
any pre-existing job in the pipeline. If a policy is created at the group-level, it will apply to every child
project or sub-group. A group-level policy cannot be edited from a child project or sub-group.
-This feature has some overlap with [compliance framework pipelines](../../project/settings/#compliance-pipeline-configuration),
+This feature has some overlap with [compliance framework pipelines](../../project/settings/index.md#compliance-pipeline-configuration),
as we have not [unified the user experience for these two features](https://gitlab.com/groups/gitlab-org/-/epics/7312).
For details on the similarities and differences between these features, see
-[Enforce scan execution](../#enforce-scan-execution).
+[Enforce scan execution](../index.md#enforce-scan-execution).
NOTE:
Policy jobs are created in the `test` stage of the pipeline. If you modify the default pipeline
@@ -88,8 +89,7 @@ This rule enforces the defined actions and schedules a scan on the provided date
|------------|------|-----------------|-------------|
| `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). |
-| `cadence` | `string` | CRON expression (for example, `0 0 * * *`) | A whitespace-separated string containing five fields that represents the scheduled time. <!--- start_remove The following content will be removed on remove_date: '2022-08-22' --> |
-| `clusters` (removed) | `object` | | This field was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/356465) in 15.0. The cluster where the given policy enforces running selected scans (only for `container_scanning`/`cluster_image_scanning` scans). The key of the object is the name of the Kubernetes cluster configured for your project in GitLab. In the optionally provided value of the object, you can precisely select Kubernetes resources that are scanned. <!--- end_remove --> |
+| `cadence` | `string` | CRON expression (for example, `0 0 * * *`) | A whitespace-separated string containing five fields that represents the scheduled time. |
GitLab supports the following types of CRON syntax for the `cadence` field:
@@ -98,23 +98,6 @@ GitLab supports the following types of CRON syntax for the `cadence` field:
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.
-<!--- start_remove The following content will be removed on remove_date: '2022-08-22' -->
-
-### `cluster` schema (removed)
-
-This schema was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/356465) in 15.0.
-
-Use this schema to define `clusters` objects in the [`schedule` rule type](#schedule-rule-type).
-
-| Field | Type | Possible values | Description |
-|--------------|---------------------|--------------------------|-------------|
-| `containers` | `array` of `string` | | The container name that is scanned (only the first value is currently supported). |
-| `resources` | `array` of `string` | | The resource name that is scanned (only the first value is currently supported). |
-| `namespaces` | `array` of `string` | | The namespace that is scanned (only the first value is currently supported). |
-| `kinds` | `array` of `string` | `deployment`/`daemonset` | The resource kind that should be scanned (only the first value is currently supported). |
-
-<!--- end_remove -->
-
## `scan` action type
This action executes the selected `scan` with additional parameters when conditions for at least one
@@ -146,7 +129,7 @@ Note the following:
- A container scanning and cluster image scanning scans configured for the `pipeline` rule type ignores the cluster defined in the `clusters` object.
They use predefined CI/CD variables defined for your project. Cluster selection with the `clusters` object is supported for the `schedule` rule type.
A cluster with a name provided in the `clusters` object must be created and configured for the project.
-- The SAST scan uses the default template and runs in a [child pipeline](../../../ci/pipelines/parent_child_pipelines.md).
+- The SAST scan uses the default template and runs 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 3eee4957e2f..78a97b36e92 100644
--- a/doc/user/application_security/policies/scan-result-policies.md
+++ b/doc/user/application_security/policies/scan-result-policies.md
@@ -1,6 +1,6 @@
---
-stage: Protect
-group: Container Security
+stage: Govern
+group: Security Policies
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
---
diff --git a/doc/user/application_security/sast/analyzers.md b/doc/user/application_security/sast/analyzers.md
index cbd64e278c8..ec8e8e6fd93 100644
--- a/doc/user/application_security/sast/analyzers.md
+++ b/doc/user/application_security/sast/analyzers.md
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Moved](https://gitlab.com/groups/gitlab-org/-/epics/2098) from GitLab Ultimate to GitLab Free in 13.3.
Static Application Security Testing (SAST) uses analyzers
-to detect vulnerabilities in source code. Each analyzer is a wrapper around a [scanner](../terminology/#scanner), a third-party code analysis tool.
+to detect vulnerabilities in source code. Each analyzer is a wrapper around a [scanner](../terminology/index.md#scanner), a third-party code analysis tool.
The analyzers are published as Docker images that SAST uses to launch dedicated containers for each
analysis.
@@ -20,7 +20,7 @@ For each scanner, an analyzer:
- Exposes its detection logic.
- Handles its execution.
-- Converts its output to a [standard format](../terminology/#secure-report-format).
+- Converts its output to a [standard format](../terminology/index.md#secure-report-format).
## SAST analyzers
@@ -77,7 +77,7 @@ You can choose to disable the other analyzers early and use Semgrep-based scanni
- You'll enjoy significantly faster scanning, reduced CI minutes usage, and more customizable scanning rules.
- However, vulnerabilities previously reported by language-specific analyzers will be reported again under certain conditions, including if you've dismissed the vulnerabilities before. The system behavior depends on:
- whether you've excluded the Semgrep-based analyzer from running in the past.
- - which analyzer first discovered the vulnerabilities shown in the project's [Vulnerability Report](../vulnerability_report/).
+ - which analyzer first discovered the vulnerabilities shown in the project's [Vulnerability Report](../vulnerability_report/index.md).
### Vulnerability translation
@@ -103,7 +103,7 @@ You can choose to use Semgrep-based scanning instead of language-specific analyz
We recommend taking this approach if any of these cases applies:
-- You haven't used SAST before on a project, so you don't already have SAST vulnerabilities in your [Vulnerability Report](../vulnerability_report/).
+- You haven't used SAST before on a project, so you don't already have SAST vulnerabilities in your [Vulnerability Report](../vulnerability_report/index.md).
- You're having trouble configuring one of the analyzers whose coverage overlaps with Semgrep-based coverage. For example, you might have trouble setting up the SpotBugs-based analyzer to compile your code.
- You've already seen and dismissed vulnerabilities created by ESLint, Gosec, or Flawfinder scanning, and you've kept the re-created vulnerabilities created by Semgrep.
@@ -120,6 +120,36 @@ To switch to Semgrep-based scanning early, you can:
1. Merge the MR and wait for the default-branch pipeline to run.
1. Use the Vulnerability Report to dismiss the findings that are no longer detected by the language-specific analyzers.
+#### Preview Semgrep-based scanning
+
+You can see how Semgrep-based scanning will work in your projects before the GitLab-managed Stable CI/CD template for SAST is updated.
+We recommend that you test this change in a merge request but continue using the Stable template in your default branch pipeline configuration.
+
+In GitLab 15.3, we [activated a feature flag](https://gitlab.com/gitlab-org/gitlab/-/issues/362179) to migrate security findings on the default branch from other analyzers to Semgrep.
+We plan to [plan to remove the deprecated analyzers](https://gitlab.com/gitlab-org/gitlab/-/issues/352554) from the Stable CI/CD template in GitLab 15.4.
+
+To preview the upcoming changes to the CI/CD configuration:
+
+1. Open an MR to switch from the Stable CI/CD template, `SAST.gitlab-ci.yaml`, to [the Latest template](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml), `SAST.latest.gitlab-ci.yaml`.
+ - On GitLab.com, use the latest template directly:
+
+ ```yaml
+ include:
+ template: 'SAST.latest.gitlab-ci.yaml'
+ ```
+
+ - On a Self-Managed instance, download the template from GitLab.com:
+
+ ```yaml
+ include:
+ remote: 'https://gitlab.com/gitlab-org/gitlab/-/raw/2851f4d5/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml'
+ ```
+
+1. Verify that scanning jobs succeed in the MR. You'll notice findings from the removed analyzers in _Fixed_ and findings from Semgrep in _New_. (Some findings may show different names, descriptions, and severities, since GitLab manages and edits the Semgrep rulesets.)
+1. Close the MR.
+
+To learn more about Stable and Latest templates, see documentation on [CI/CD template versioning](../../../development/cicd/templates.md#versioning).
+
## Customize analyzers
Use [CI/CD variables](index.md#available-cicd-variables)
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index d4b0d5b972c..c9bfecffecc 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -78,16 +78,17 @@ You can also [view our language roadmap](https://about.gitlab.com/direction/secu
|------------------------------------------------|-----------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|
| .NET Core | [Security Code Scan](https://security-code-scan.github.io) | 11.0 |
| .NET Framework<sup>1</sup> | [Security Code Scan](https://security-code-scan.github.io) | 13.0 |
+| .NET (all versions, C# only) | [Semgrep](https://semgrep.dev) | 15.4 |
| Apex (Salesforce) | [PMD](https://pmd.github.io/pmd/index.html) | 12.1 |
| C | [Semgrep](https://semgrep.dev) | 14.2 |
| C/C++ | [Flawfinder](https://github.com/david-a-wheeler/flawfinder) | 10.7 |
| Elixir (Phoenix) | [Sobelow](https://github.com/nccgroup/sobelow) | 11.1 |
| Go | [Gosec](https://github.com/securego/gosec) | 10.7 |
| Go | [Semgrep](https://semgrep.dev) | 14.4 |
-| Groovy<sup>2</sup> | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 11.3 (Gradle) & 11.9 (Ant, Maven, SBT) |
+| Groovy<sup>2</sup> | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 11.3 (Gradle) & 11.9 (Maven, SBT) |
| Helm Charts | [Kubesec](https://github.com/controlplaneio/kubesec) | 13.1 |
| Java (any build system) | [Semgrep](https://semgrep.dev) | 14.10 |
-| Java<sup>2</sup> | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 10.6 (Maven), 10.8 (Gradle) & 11.9 (Ant, SBT) |
+| Java<sup>2</sup> | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 10.6 (Maven), 10.8 (Gradle) & 11.9 (SBT) |
| Java (Android) | [MobSF (beta)](https://github.com/MobSF/Mobile-Security-Framework-MobSF) | 13.5 |
| JavaScript | [ESLint security plugin](https://github.com/nodesecurity/eslint-plugin-security) | 11.8 |
| JavaScript | [Semgrep](https://semgrep.dev) | 13.10 |
@@ -103,16 +104,16 @@ You can also [view our language roadmap](https://about.gitlab.com/direction/secu
| React | [Semgrep](https://semgrep.dev) | 13.10 |
| Ruby | [brakeman](https://brakemanscanner.org) | 13.9 |
| Ruby on Rails | [brakeman](https://brakemanscanner.org) | 10.3 |
-| Scala<sup>2</sup> | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 11.0 (SBT) & 11.9 (Ant, Gradle, Maven) |
+| Scala<sup>2</sup> | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 11.0 (SBT) & 11.9 (Gradle, Maven) |
| Swift (iOS) | [MobSF (beta)](https://github.com/MobSF/Mobile-Security-Framework-MobSF) | 13.5 |
| TypeScript | [ESLint security plugin](https://github.com/nodesecurity/eslint-plugin-security) | 11.9, [merged](https://gitlab.com/gitlab-org/gitlab/-/issues/36059) with ESLint in 13.2 |
| TypeScript | [Semgrep](https://semgrep.dev) | 13.10 |
-1. .NET 4 support is limited. The analyzer runs in a Linux container and does not have access to Windows-specific libraries or features. We currently plan to [migrate C# coverage to Semgrep-based scanning](https://gitlab.com/gitlab-org/gitlab/-/issues/347258) to make it easier to scan C# projects.
-1. The SpotBugs-based analyzer supports [Ant](https://ant.apache.org/), [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/), and [SBT](https://www.scala-sbt.org/). It can also be used with variants like the
+1. .NET 4 support is limited. The analyzer runs in a Linux container and does not have access to Windows-specific libraries or features. Use the Semgrep-based scanner if you need .NET 4 support.
+1. The SpotBugs-based analyzer supports [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/), and [SBT](https://www.scala-sbt.org/). It can also be used with variants like the
[Gradle wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html),
[Grails](https://grails.org/),
-and the [Maven wrapper](https://github.com/takari/maven-wrapper).
+and the [Maven wrapper](https://github.com/takari/maven-wrapper). However, SpotBugs has [limitations](https://gitlab.com/gitlab-org/gitlab/-/issues/350801) when used against [Ant](https://ant.apache.org/)-based projects. We recommend using the Semgrep-based analyzer for Ant-based Java projects.
### Multi-project support
@@ -242,7 +243,7 @@ successfully, and an error may occur.
To enable and configure SAST with default settings:
-1. On the top bar, select **Menu > Projects** and find your project.
+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 SAST section, select **Configure with a merge request**.
1. Review and merge the merge request to enable SAST.
@@ -262,7 +263,7 @@ successfully, and an error may occur.
To enable and configure SAST with customizations:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Security & Compliance > Configuration**.
1. If the project does not have a `.gitlab-ci.yml` file, select **Enable SAST** in the Static
Application Security Testing (SAST) row, otherwise select **Configure SAST**.
@@ -337,7 +338,7 @@ False positive detection is available in a subset of the [supported languages](#
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5144) in GitLab 14.2.
Source code is volatile; as developers make changes, source code may move within files or between files.
-Security analyzers may have already reported vulnerabilities that are being tracked in the [Vulnerability Report](../vulnerability_report/).
+Security analyzers may have already reported vulnerabilities that are being tracked in the [Vulnerability Report](../vulnerability_report/index.md).
These vulnerabilities are linked to specific problematic code fragments so that they can be found and fixed.
If the code fragments are not tracked reliably as they move, vulnerability management is harder because the same vulnerability could be reported again.
@@ -550,8 +551,8 @@ Some analyzers can be customized with CI/CD variables.
| `KUBESEC_HELM_CHARTS_PATH` | Kubesec | Optional path to Helm charts that `helm` uses to generate a Kubernetes manifest that `kubesec` scans. If dependencies are defined, `helm dependency build` should be ran in a `before_script` to fetch the necessary dependencies. |
| `KUBESEC_HELM_OPTIONS` | Kubesec | Additional arguments for the `helm` executable. |
| `COMPILE` | Gosec, SpotBugs | Set to `false` to disable project compilation and dependency fetching. [Introduced for `SpotBugs`](https://gitlab.com/gitlab-org/gitlab/-/issues/195252) analyzer in GitLab 13.1 and [`Gosec`](https://gitlab.com/gitlab-org/gitlab/-/issues/330678) analyzer in GitLab 14.0. |
-| `ANT_HOME` | SpotBugs | The `ANT_HOME` variable. |
-| `ANT_PATH` | SpotBugs | Path to the `ant` executable. |
+| `ANT_HOME` | SpotBugs | The `ANT_HOME` variable. |
+| `ANT_PATH` | SpotBugs | Path to the `ant` executable. |
| `GRADLE_PATH` | SpotBugs | Path to the `gradle` executable. |
| `JAVA_OPTS` | SpotBugs | Additional arguments for the `java` executable. |
| `JAVA_PATH` | SpotBugs | Path to the `java` executable. |
@@ -565,6 +566,22 @@ Some analyzers can be customized with CI/CD variables.
| `PHPCS_SECURITY_AUDIT_PHP_EXTENSIONS` | phpcs-security-audit | Comma separated list of additional PHP Extensions. |
| `SAST_DISABLE_BABEL` | NodeJsScan | **{warning}** **[Removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/64025)** in GitLab 13.5 |
| `SAST_SEMGREP_METRICS` | Semgrep | Set to `"false"` to disable sending anonymized scan metrics to [r2c](https://r2c.dev/). Default: `true`. Introduced in GitLab 14.0 from the [confidential issue](../../project/issues/confidential_issues.md) `https://gitlab.com/gitlab-org/gitlab/-/issues/330565`. |
+| `SAST_SCANNER_ALLOWED_CLI_OPTS` | Semgrep | CLI options (arguments with value, or flags) that are passed to the underlying security scanner when running scan operation. Only a limited set of [options](#security-scanner-configuration) are accepted. Separate a CLI option and its value using either a blank space or equals (`=`) character. For example: `name1 value1` or `name1=value1`. Multiple options must be separated by blank spaces. For example: `name1 value1 name2 value2`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368565) in GitLab 15.3. |
+
+#### Security scanner configuration
+
+SAST analyzers internally use OSS security scanners to perform the analysis. We set the recommended
+configuration for the security scanner so that you need not to worry about tuning them. However,
+there can be some rare cases where our default scanner configuration does not suit your
+requirements.
+
+To allow some customization of scanner behavior, you can add a limited set of flags to the
+underlying scanner. Specify the flags in the `SAST_SCANNER_ALLOWED_CLI_OPTS` CI/CD variable. These
+flags are added to the scanner's CLI options.
+
+| Analyzer | CLI option | Description |
+|------------------------------------------------------------------------------|--------------------|------------------------------------------------------------------------------|
+| [Semgrep](https://gitlab.com/gitlab-org/security-products/analyzers/semgrep) | `--max-memory` | Sets the maximum system memory to use when running a rule on a single file. Measured in MB. |
#### Custom CI/CD variables
@@ -729,6 +746,31 @@ variables:
SECURE_LOG_LEVEL: "debug"
```
+### Pipeline errors related to changes in the GitLab-managed CI/CD template
+
+The [GitLab-managed SAST CI/CD template](#configure-sast-manually) controls which [analyzer](analyzers.md) jobs run and how they're configured. While using the template, you might experience a job failure or other pipeline error. For example, you might:
+
+- See an error message like `'<your job>' needs 'spotbugs-sast' job, but 'spotbugs-sast' is not in any previous stage` when you view an affected pipeline.
+- Experience another type of unexpected issue with your CI/CD pipeline configuration.
+
+If you're experiencing a job failure or seeing a SAST-related `yaml invalid` pipeline status, you can temporarily revert to an older version of the template so your pipelines keep working while you investigate the issue. To use an older version of the template, change the existing `include` statement in your CI/CD YAML file to refer to a specific template version, such as `v15.3.3-ee`:
+
+```yaml
+include:
+ remote: 'https://gitlab.com/gitlab-org/gitlab/-/raw/v15.3.3-ee/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml'
+```
+
+If your GitLab instance has limited network connectivity, you can also download the file and host it elsewhere.
+
+We recommend that you only use this solution temporarily and that you return to [the standard template](#configure-sast-manually) as soon as possible.
+
+### Errors in a specific analyzer job
+
+GitLab SAST [analyzers](analyzers.md) are released as container images.
+If you're seeing a new error that doesn't appear to be related to [the GitLab-managed SAST CI/CD template](#configure-sast-manually) or changes in your own project, you can try [pinning the affected analyzer to a specific older version](#pinning-to-minor-image-version).
+
+Each [analyzer project](analyzers.md#sast-analyzers) has a `CHANGELOG.md` file listing the changes made in each available version.
+
### `Error response from daemon: error processing tar file: docker-tar: relocation error`
This error occurs when the Docker version that runs the SAST job is `19.03.0`.
diff --git a/doc/user/application_security/secret_detection/index.md b/doc/user/application_security/secret_detection/index.md
index 93c32f998fa..fe029b26ce5 100644
--- a/doc/user/application_security/secret_detection/index.md
+++ b/doc/user/application_security/secret_detection/index.md
@@ -6,26 +6,42 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Secret Detection **(FREE)**
-> [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/222788) from GitLab Ultimate to GitLab Free in 13.3.
-
-A recurring problem when developing applications is that people may accidentally commit secrets to
-their remote Git repositories. Secrets include keys, passwords, API tokens, and other sensitive
+> - In GitLab 13.1, Secret Detection was split from the [SAST configuration](../sast/index.md#configuration)
+> into its own CI/CD template. If you're using GitLab 13.0 or earlier and SAST is enabled, then
+> Secret Detection is already enabled.
+> - [Moved](https://gitlab.com/gitlab-org/gitlab/-/issues/222788) from GitLab Ultimate to GitLab
+> Free in 13.3.
+> - [In GitLab 14.0](https://gitlab.com/gitlab-org/gitlab/-/issues/297269), Secret Detection jobs
+> `secret_detection_default_branch` and `secret_detection` were consolidated into one job,
+> `secret_detection`.
+
+People may accidentally commit secrets to
+remote Git repositories. Secrets include keys, passwords, API tokens, and other sensitive
information. Anyone with access to the repository could use the secrets for malicious purposes.
-Secrets exposed in this way must be treated as compromised, and be replaced, which can be costly.
-It's important to prevent secrets from being committed to a Git repository.
+Exposed secrets are compromised and must be replaced, which can be costly.
+
+To help prevent secrets from being committed to a Git repository, you can use Secret Detection
+to scan your repository for secrets. Scanning is language
+and framework agnostic, but does not support scanning binary files.
+
+Secret Detection uses a specific analyzer containing the
+[Gitleaks](https://github.com/zricethezav/gitleaks) tool to scan the repository for secrets, in a
+`secret-detection` job. The results are saved as a
+[Secret Detection report artifact](../../../ci/yaml/artifacts_reports.md#artifactsreportssecret_detection)
+that you can later download and analyze. Due to implementation limitations, we always take the
+latest Secret Detection artifact available.
+
+GitLab SaaS supports post-processing hooks, so you can take action when a secret is found. For
+more information, see [Post-processing and revocation](post_processing.md).
-Secret Detection uses the [Gitleaks](https://github.com/zricethezav/gitleaks) tool to scan the
-repository for secrets. All identified secrets are reported in the:
+All identified secrets are reported in the:
- Merge request widget
- Pipelines' **Security** tab
-- [Security Dashboard](../security_dashboard/)
+- [Security Dashboard](../security_dashboard/index.md)
![Secret Detection in merge request widget](img/secret_detection_v13_2.png)
-WARNING:
-Secret Detection does not support scanning binary files.
-
## Detected secrets
Secret Detection uses a [default ruleset](https://gitlab.com/gitlab-org/security-products/analyzers/secrets/-/blob/master/gitleaks.toml)
@@ -34,105 +50,57 @@ patterns using [custom rulesets](#custom-rulesets). If you want to contribute ru
"well-identifiable" secrets, follow the steps detailed in the
[community contributions guidelines](https://gitlab.com/gitlab-org/gitlab/-/issues/345453).
-## Requirements
-
-To run Secret Detection jobs, by default, you need GitLab Runner with the
-[`docker`](https://docs.gitlab.com/runner/executors/docker.html) or
-[`kubernetes`](https://docs.gitlab.com/runner/install/kubernetes.html) executor.
-If you're using the shared runners on GitLab.com, this is enabled by default.
-
-WARNING:
-Our Secret Detection jobs expect a Linux/amd64 container type. Windows containers are not supported.
-
-WARNING:
-If you use your own runners, make sure the Docker version installed
-is **not** `19.03.0`. See [troubleshooting information](../sast#error-response-from-daemon-error-processing-tar-file-docker-tar-relocation-error) for details.
-
-### Making Secret Detection available to all GitLab tiers
+## Features per tier
-To make Secret Detection available to as many customers as possible, we have enabled it for all GitLab tiers.
-However not all features are available on every tier. See the breakdown below for more details.
+Different features are available in different [GitLab tiers](https://about.gitlab.com/pricing/).
-#### Summary of features per tier
+| Capability | In Free & Premium | In Ultimate |
+|:---------------------------------------------------------------- |:-----------------------|:-----------------------|
+| [Configure Secret Detection scanner](#enable-secret-detection) | **{check-circle}** Yes | **{check-circle}** Yes |
+| [Customize Secret Detection settings](#configure-scan-settings) | **{check-circle}** Yes | **{check-circle}** Yes |
+| Download [JSON Report](../sast/index.md#reports-json-format) | **{check-circle}** Yes | **{check-circle}** Yes |
+| See new findings in the merge request widget | **{dotted-circle}** No | **{check-circle}** Yes |
+| View identified secrets in the pipelines' **Security** tab | **{dotted-circle}** No | **{check-circle}** Yes |
+| [Manage vulnerabilities](../vulnerabilities/index.md) | **{dotted-circle}** No | **{check-circle}** Yes |
+| [Access the Security Dashboard](../security_dashboard/index.md) | **{dotted-circle}** No | **{check-circle}** Yes |
+| [Customize Secret Detection rulesets](#custom-rulesets) | **{dotted-circle}** No | **{check-circle}** Yes |
-Different features are available in different [GitLab tiers](https://about.gitlab.com/pricing/),
-as shown in the following table:
+## Enable Secret Detection
-| Capability | In Free & Premium | In Ultimate |
-|:----------------------------------------------------------------|:--------------------|:-------------------|
-| [Configure Secret Detection scanner](#configuration) | **{check-circle}** | **{check-circle}** |
-| [Customize Secret Detection settings](#customizing-settings) | **{check-circle}** | **{check-circle}** |
-| Download [JSON Report](../sast/index.md#reports-json-format) | **{check-circle}** | **{check-circle}** |
-| See new findings in the merge request widget | **{dotted-circle}** | **{check-circle}** |
-| View identified secrets in the pipelines' **Security** tab | **{dotted-circle}** | **{check-circle}** |
-| [Manage vulnerabilities](../vulnerabilities/index.md) | **{dotted-circle}** | **{check-circle}** |
-| [Access the Security Dashboard](../security_dashboard/index.md) | **{dotted-circle}** | **{check-circle}** |
-| [Customize Secret Detection rulesets](#custom-rulesets) | **{dotted-circle}** | **{check-circle}** |
+Prerequisites:
-## Configuration
+- GitLab Runner with the [`docker`](https://docs.gitlab.com/runner/executors/docker.html) or
+[`kubernetes`](https://docs.gitlab.com/runner/install/kubernetes.html) executor. If you're using the
+shared runners on GitLab.com, this is enabled by default.
+- If you use your own runners, make sure the Docker version installed is **not** `19.03.0`. See
+ [troubleshooting information](../sast#error-response-from-daemon-error-processing-tar-file-docker-tar-relocation-error)
+ for details.
+- Linux/amd64 container type. Windows containers are not supported.
+- GitLab CI/CD configuration (`.gitlab-ci.yml`) must include the `test` stage.
-> - In GitLab 13.1, Secret Detection was split from the [SAST configuration](../sast#configuration) into its own CI/CD template. If you're using GitLab 13.0 or earlier and SAST is enabled, then Secret Detection is already enabled.
-> - [In GitLab 14.0](https://gitlab.com/gitlab-org/gitlab/-/issues/297269), Secret Detection jobs `secret_detection_default_branch` and `secret_detection` were consolidated into one job, `secret_detection`.
+To enable Secret Detection, either:
-Secret Detection is performed by a [specific analyzer](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/Secret-Detection.gitlab-ci.yml)
-during the `secret-detection` job. It runs regardless of your app's programming language.
+- Enable [Auto DevOps](../../../topics/autodevops/index.md), which includes [Auto Secret Detection](../../../topics/autodevops/stages.md#auto-secret-detection).
-The Secret Detection analyzer includes [Gitleaks](https://github.com/zricethezav/gitleaks) checks.
+- [Enable Secret Detection by including the template](#enable-secret-detection-by-including-the-template).
-Note that the Secret Detection analyzer ignores Password-in-URL vulnerabilities if the password
-begins with a dollar sign (`$`), as this likely indicates the password is an environment variable.
-For example, `https://username:$password@example.com/path/to/repo` isn't detected, while
-`https://username:password@example.com/path/to/repo` is.
+- [Enable Secret Detection using a merge request](#enable-secret-detection-using-a-merge-request).
-NOTE:
-You don't have to configure Secret Detection manually as shown in this section if you're using
-[Auto Secret Detection](../../../topics/autodevops/stages.md#auto-secret-detection),
-provided by [Auto DevOps](../../../topics/autodevops/index.md).
+### Enable Secret Detection by including the template
-To enable Secret Detection for GitLab 13.1 and later, you must include the
-`Secret-Detection.gitlab-ci.yml` template that's provided as a part of your GitLab installation. For
-GitLab versions earlier than 11.9, you can copy and use the job as defined in that template.
+We recommend this method if you have an existing GitLab CI/CD configuration file.
-Ensure your `.gitlab-ci.yml` file has a `stage` called `test`, and add the following to your `.gitlab-ci.yml` file:
+Add the following extract to your `.gitlab-ci.yml` file:
```yaml
include:
- template: Security/Secret-Detection.gitlab-ci.yml
```
-The included template creates Secret Detection jobs in your CI/CD pipeline and scans
-your project's source code for secrets.
-
-The results are saved as a
-[Secret Detection report artifact](../../../ci/yaml/artifacts_reports.md#artifactsreportssecret_detection)
-that you can later download and analyze. Due to implementation limitations, we
-always take the latest Secret Detection artifact available.
-
-### Supported distributions
-
-The default scanner images are build off a base Alpine image for size and maintainability.
-
-#### FIPS-enabled images
-
-> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6479) in GitLab 14.10.
-
-GitLab offers [Red Hat UBI](https://www.redhat.com/en/blog/introducing-red-hat-universal-base-image)
-versions of the images that are FIPS-enabled. To use the FIPS-enabled images, you can either:
-
-- Set the `SAST_IMAGE_SUFFIX` to `-fips`.
-- Add the `-fips` extension to the default image name.
-
-For example:
-
-```yaml
-variables:
- SECRET_DETECTION_IMAGE_SUFFIX: '-fips'
-
-include:
- - template: Security/Secret-Detection.gitlab-ci.yml
-```
+Pipelines now include a Secret Detection job, and the results are included in the merge request
+widget.
-### Enable Secret Detection via an automatic merge request
+### Enable Secret Detection using a 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.
@@ -142,45 +110,36 @@ This method works best with no existing `.gitlab-ci.yml` file, or with a minimal
file. If you have a complex GitLab configuration file it may not be parsed successfully, and an
error may occur.
-To enable Secret Detection in a project, you can create a merge request:
+To enable Secret Detection using a merge request:
-1. On the top bar, select **Menu > Projects** and find your project.
+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. Review and merge the merge request to enable Secret Detection.
+1. Review and merge the merge request.
-Pipelines now include a Secret Detection job.
+Pipelines now include a Secret Detection job, and the results are included in the merge request
+widget.
-### Customizing settings
+## Configure scan settings
The Secret Detection scan settings can be changed through [CI/CD variables](#available-cicd-variables)
-by using the
-[`variables`](../../../ci/yaml/index.md#variables) parameter in `.gitlab-ci.yml`.
+by using the [`variables`](../../../ci/yaml/index.md#variables) parameter in `.gitlab-ci.yml`.
WARNING:
-All customization of GitLab security scanning tools should be tested in a merge request before
+All configuration 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.
To override a job definition, (for example, change properties like `variables` or `dependencies`),
-declare a job with the same name as the secret detection job to override. Place this new job after the template
-inclusion and specify any additional keys under it.
-
-WARNING:
-Beginning in GitLab 13.0, the use of [`only` and `except`](../../../ci/yaml/index.md#only--except)
-is no longer supported. When overriding the template, you must use [`rules`](../../../ci/yaml/index.md#rules) instead.
+declare a job with the same name as the secret detection job to override. Place this new job after
+the template inclusion and specify any additional keys under it.
-#### `GIT_DEPTH` variable
+In the following example _extract_ of a `.gitlab-ci.yml` file:
-The [`GIT_DEPTH` CI/CD variable](../../../ci/runners/configure_runners.md#shallow-cloning) affects Secret Detection.
-The Secret Detection analyzer relies on generating patches between commits to scan content for
-secrets. If you override the default, ensure the value is greater than 1. If the number of commits
-in an MR is greater than the `GIT_DEPTH` value, Secret Detection will [fail to detect secrets](#error-couldnt-run-the-gitleaks-command-exit-status-2).
-
-#### Custom settings example
-
-In the following example, we include the Secret Detection template and at the same time we
-override the `secret_detection` job with the `SECRET_DETECTION_HISTORIC_SCAN` CI/CD variable to `true`:
+- The Secret Detection template is [included](../../../ci/yaml/index.md#include).
+- In the `secret_detection` job, the CI/CD variable `SECRET_DETECTION_HISTORIC_SCAN` is set to
+ `true`. Because the template is evaluated before the pipeline configuration, the last mention of
+ the variable takes precedence.
```yaml
include:
@@ -191,10 +150,7 @@ secret_detection:
SECRET_DETECTION_HISTORIC_SCAN: "true"
```
-Because the template is [evaluated before](../../../ci/yaml/index.md#include)
-the pipeline configuration, the last mention of the variable takes precedence.
-
-#### Available CI/CD variables
+### Available CI/CD variables
Secret Detection can be customized by defining available CI/CD variables:
@@ -202,7 +158,7 @@ Secret Detection can be customized by defining available CI/CD variables:
|-----------------------------------|---------------|-------------|
| `SECRET_DETECTION_EXCLUDED_PATHS` | "" | Exclude vulnerabilities from output based on the paths. This is a comma-separated list of patterns. Patterns can be globs (see [`doublestar.Match`](https://pkg.go.dev/github.com/bmatcuk/doublestar/v4@v4.0.2#Match) for supported patterns), or file or folder paths (for example, `doc,spec` ). Parent directories also match patterns. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/225273) in GitLab 13.3. |
| `SECRET_DETECTION_HISTORIC_SCAN` | false | Flag to enable a historic Gitleaks scan. |
-| `SECRET_DETECTION_IMAGE_SUFFIX` | "" | Suffix added to the image name. If set to `-fips`, `FIPS-enabled` images are used for scan. See [FIPS-enabled images](#fips-enabled-images) for more details. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/355519) in GitLab 14.10. |
+| `SECRET_DETECTION_IMAGE_SUFFIX` | "" | Suffix added to the image name. If set to `-fips`, `FIPS-enabled` images are used for scan. See [Use FIPS-enabled images](#use-fips-enabled-images) for more details. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/355519) in GitLab 14.10. |
| `SECRET_DETECTION_LOG_OPTIONS` | "" | [`git log`](https://git-scm.com/docs/git-log) options used to define commit ranges. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/350660) in GitLab 15.1.|
In previous GitLab versions, the following variables were also available:
@@ -211,45 +167,79 @@ In previous GitLab versions, the following variables were also available:
|-----------------------------------|---------------|-------------|
| `SECRET_DETECTION_COMMIT_FROM` | - | The commit a Gitleaks scan starts at. [Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/243564) in GitLab 13.5. Replaced with `SECRET_DETECTION_COMMITS`. |
| `SECRET_DETECTION_COMMIT_TO` | - | The commit a Gitleaks scan ends at. [Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/243564) in GitLab 13.5. Replaced with `SECRET_DETECTION_COMMITS`. |
-| `SECRET_DETECTION_COMMITS` | - | The list of commits that Gitleaks should scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/243564) in GitLab 13.5. [Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/352565) in GitLab 15.0. |
+| `SECRET_DETECTION_COMMITS` | - | The list of commits that Gitleaks should scan. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/243564) in GitLab 13.5. [Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/352565) in GitLab 15.0. |
-### Custom rulesets **(ULTIMATE)**
+#### Use FIPS-enabled images
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/211387) in GitLab 13.5.
-> - [Added](https://gitlab.com/gitlab-org/gitlab/-/issues/339614) support for
-> 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.
+> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6479) in GitLab 14.10.
-You can customize the default secret detection rules provided with GitLab.
-Ruleset customization supports the following capabilities that can be used
-simultaneously:
+The default scanner images are built off a base Alpine image for size and maintainability. GitLab
+offers [Red Hat UBI](https://www.redhat.com/en/blog/introducing-red-hat-universal-base-image)
+versions of the images that are FIPS-enabled.
-- [Disabling predefined rules](#disable-predefined-analyzer-rules).
-- [Overriding predefined rules](#override-predefined-analyzer-rules).
-- Modifying the default behavior of the Secret Detection analyzer by [synthesizing and passing a custom configuration](#synthesize-a-custom-configuration).
+To use the FIPS-enabled images, either:
-Customization allows replacing the default secret detection rules with rules that you define.
+- Set the `SECRET_DETECTION_IMAGE_SUFFIX` CI/CD variable to `-fips`.
+- Add the `-fips` extension to the default image name.
-To create a custom ruleset:
+For example:
-1. Create a `.gitlab` directory at the root of your project, if one doesn't already exist.
-1. Create a custom ruleset file named `secret-detection-ruleset.toml` in the `.gitlab` directory.
+```yaml
+variables:
+ SECRET_DETECTION_IMAGE_SUFFIX: '-fips'
-#### Disable predefined analyzer rules
+include:
+ - template: Security/Secret-Detection.gitlab-ci.yml
+```
-To disable analyzer rules:
+## Full history Secret Detection
-1. Set the `disabled` flag to `true` in the context of a `ruleset` section.
+By default, Secret Detection scans only the current state of the Git repository. Any secrets
+contained in the repository's history are not detected. To address this, Secret Detection can
+scan the Git repository's full history.
+
+We recommend you do a full history scan only once, after enabling Secret Detection. A full history
+can take a long time, especially for larger repositories with lengthy Git histories. After
+completing an initial full history scan, use only standard Secret Detection as part of your
+pipeline.
+
+### Enable full history Secret Detection
+
+To enable full history Secret Detection, set the variable `SECRET_DETECTION_HISTORIC_SCAN` to `true` in your `.gitlab-ci.yml` file.
+
+## Custom rulesets **(ULTIMATE)**
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/211387) in GitLab 13.5.
+> - [Added](https://gitlab.com/gitlab-org/gitlab/-/issues/339614) support for 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 Secret Detection rules provided with GitLab.
-1. In one or more `ruleset.identifier` subsections, list the rules that you want disabled. Every `ruleset.identifier` section has:
+The following customization options can be used separately, or in combination:
- - a `type` field, to name the predefined rule identifier.
- - a `value` field, to name the rule to be disabled.
+- [Disable predefined rules](#disable-predefined-analyzer-rules).
+- [Override predefined rules](#override-predefined-analyzer-rules).
+- [Synthesize a custom configuration](#synthesize-a-custom-configuration).
-##### Example: Disable predefined rules of Secret Detection analyzer
+### Disable predefined analyzer rules
-In the following example, the disabled rules is assigned to `secrets`
-by matching the `type` and `value` of identifiers:
+If there are specific Secret Detection rules that you don't want active, you can disable them.
+
+To disable analyzer rules:
+
+1. Create a `.gitlab` directory at the root of your project, if one doesn't already exist.
+1. Create a custom ruleset file named `secret-detection-ruleset.toml` in the `.gitlab` directory, if
+ one doesn't already exist.
+1. Set the `disabled` flag to `true` in the context of a `ruleset` section.
+1. In one or more `ruleset.identifier` subsections, list the rules to disable. Every
+ `ruleset.identifier` section has:
+ - A `type` field for the predefined rule identifier.
+ - A `value` field for the rule name.
+
+In the following example `secret-detection-ruleset.toml` file, the disabled rules are assigned to
+`secrets` by matching the `type` and `value` of identifiers:
```toml
[secrets]
@@ -260,29 +250,30 @@ by matching the `type` and `value` of identifiers:
value = "RSA private key"
```
-#### Override predefined analyzer rules
+### Override predefined analyzer rules
-To override rules:
-
-1. In one or more `ruleset.identifier` subsections, list the rules that you want to override. Every `ruleset.identifier` section has:
+If there are specific Secret Detection rules you want to customize, you can override them. For
+example, you might increase the severity of specific secrets.
- - a `type` field, to name the predefined rule identifier that the Secret Detection analyzer uses.
- - a `value` field, to name the rule to be overridden.
+To override rules:
+1. Create a `.gitlab` directory at the root of your project, if one doesn't already exist.
+1. Create a custom ruleset file named `secret-detection-ruleset.toml` in the `.gitlab` directory, if
+ one doesn't already exist.
+1. In one or more `ruleset.identifier` subsections, list the rules to override. Every
+ `ruleset.identifier` section has:
+ - A `type` field for the predefined rule identifier.
+ - A `value` field for the rule name.
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:
-
- description
- message
- name
- severity (valid options are: Critical, High, Medium, Low, Unknown, Info)
-##### Example: Override predefined rules of Secret Detection analyzer
-
-In the following example, rules
-are matched by the `type` and `value` of identifiers and
-then overridden:
+In the following example `secret-detection-ruleset.toml` file, rules are matched by the `type` and
+`value` of identifiers and then overridden:
```toml
[secrets]
@@ -297,166 +288,157 @@ then overridden:
severity = "Info"
```
-#### Synthesize a custom configuration
+### Synthesize a custom configuration
-To create a custom configuration, you can use passthrough chains.
+To create a custom configuration, you can use passthrough chains. Passthroughs can also be chained
+to build more complex configurations. For more details, see
+[SAST Customize ruleset](../sast/customize_rulesets.md).
-1. In the `secret-detection-ruleset.toml` file, do one of the following:
+In the `secret-detection-ruleset.toml` file, do one of the following:
- - Define a custom ruleset:
+- Define a custom ruleset, for example:
- ```toml
- [secrets]
- description = 'secrets custom rules configuration'
+ ```toml
+ [secrets]
+ description = 'secrets custom rules configuration'
- [[secrets.passthrough]]
- type = "raw"
- target = "gitleaks.toml"
- value = """\
- title = "gitleaks config"
- # add regexes to the regex table
- [[rules]]
- description = "Test for Raw Custom Rulesets"
- regex = '''Custom Raw Ruleset T[est]{3}'''
- """
- ```
+ [[secrets.passthrough]]
+ type = "raw"
+ target = "gitleaks.toml"
+ value = """\
+ title = "gitleaks config"
+ # add regexes to the regex table
+ [[rules]]
+ description = "Test for Raw Custom Rulesets"
+ regex = '''Custom Raw Ruleset T[est]{3}'''
+ """
+ ```
- - Provide the name of the file containing a custom ruleset:
+- Provide the name of the file containing a custom ruleset, for example:
- ```toml
- [secrets]
- description = 'secrets custom rules configuration'
+ ```toml
+ [secrets]
+ description = 'secrets custom rules configuration'
- [[secrets.passthrough]]
- type = "file"
- target = "gitleaks.toml"
- value = "config/gitleaks.toml"
- ```
+ [[secrets.passthrough]]
+ type = "file"
+ target = "gitleaks.toml"
+ value = "config/gitleaks.toml"
+ ```
-Passthroughs can also be chained to build more complex configurations.
-For more details, see [SAST Customize ruleset section](../sast/customize_rulesets.md).
+## Running Secret Detection in an offline environment **(PREMIUM SELF)**
-### Logging level
-
-To control the verbosity of logs set the `SECURE_LOG_LEVEL` CI/CD variable. Messages of this logging level or higher are output. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10880) in GitLab 13.1.
-
-From highest to lowest severity, the logging levels are:
-
-- `fatal`
-- `error`
-- `warn`
-- `info` (default)
-- `debug`
-
-## Post-processing and revocation
+For self-managed GitLab instances in an environment with limited, restricted, or intermittent access
+to external resources through the internet, some configuration changes are required for the Secret
+Detection job to run successfully. The instructions in this section must be completed together with
+the instructions detailed in [offline environments](../offline_deployments/index.md).
-Upon detection of a secret, GitLab SaaS supports post-processing hooks.
-For more information, see [Post-processing and revocation](post_processing.md).
+### Configure GitLab Runner
-## Full History Secret Detection
+By default, a runner tries to pull Docker images from the GitLab container registry even if a local
+copy is available. We recommend using this default setting, to ensure Docker images remain current.
+However, if no network connectivity is available, you must change the default GitLab Runner
+`pull_policy` variable.
-GitLab 12.11 introduced support for scanning the full history of a repository. This new functionality
-is particularly useful when you are enabling Secret Detection in a repository for the first time and you
-want to perform a full secret detection scan. Running a secret detection scan on the full history can take a long time,
-especially for larger repositories with lengthy Git histories. We recommend not setting this CI/CD variable
-as part of your normal job definition.
+Configure the GitLab Runner CI/CD variable `pull_policy` to
+[`if-not-present`](https://docs.gitlab.com/runner/executors/docker.html#using-the-if-not-present-pull-policy).
-A new configuration variable ([`SECRET_DETECTION_HISTORIC_SCAN`](#available-cicd-variables))
-can be set to change the behavior of the GitLab Secret Detection scan to run on the entire Git history of a repository.
+### Use local Secret Detection analyzer image
-## Running Secret Detection in an offline environment
+Use a local Secret Detection analyzer image if you want to obtain the image from a local Docker
+registry instead of the GitLab container registry.
-For self-managed GitLab instances in an environment with limited, restricted, or intermittent access
-to external resources through the internet, some adjustments are required for the Secret Detection job to
-run successfully. For more information, see [Offline environments](../offline_deployments/index.md).
+Prerequisites:
-### Requirements for offline Secret Detection
+- Importing Docker images into a local offline Docker registry depends on your
+ network security policy. Consult your IT staff to find an accepted and approved process
+ to import or temporarily access external resources.
-To use Secret Detection in an offline environment, you need:
+1. Import the default Secret Detection analyzer image from `registry.gitlab.com` into your
+ [local Docker container registry](../../packages/container_registry/index.md):
-- GitLab Runner with the [`docker` or `kubernetes` executor](#requirements).
-- A Docker Container Registry with locally available copy of Secret Detection [analyzer](https://gitlab.com/gitlab-org/security-products/analyzers) images.
-- Configure certificate checking of packages (optional).
+ ```plaintext
+ registry.gitlab.com/security-products/secret-detection:3
+ ```
-GitLab Runner has a [default `pull policy` of `always`](https://docs.gitlab.com/runner/executors/docker.html#using-the-always-pull-policy),
-meaning the runner tries to pull Docker images from the GitLab container registry even if a local
-copy is available. The GitLab Runner [`pull_policy` can be set to `if-not-present`](https://docs.gitlab.com/runner/executors/docker.html#using-the-if-not-present-pull-policy)
-in an offline environment if you prefer using only locally available Docker images. However, we
-recommend keeping the pull policy setting to `always` if not in an offline environment, as this
-enables the use of updated scanners in your CI/CD pipelines.
+ The Secret Detection analyzer's image is [periodically updated](../index.md#vulnerability-scanner-maintenance)
+ so you may need to periodically update the local copy.
-### Make GitLab Secret Detection analyzer image available inside your Docker registry
+1. Set the CI/CD variable `SECURE_ANALYZERS_PREFIX` to the local Docker container registry.
-Import the following default Secret Detection analyzer images from `registry.gitlab.com` into your
-[local Docker container registry](../../packages/container_registry/index.md):
+ ```yaml
+ include:
+ - template: Security/Secret-Detection.gitlab-ci.yml
-```plaintext
-registry.gitlab.com/security-products/secret-detection:3
-```
+ variables:
+ SECURE_ANALYZERS_PREFIX: "localhost:5000/analyzers"
+ ```
-The process for importing Docker images into a local offline Docker registry depends on
-**your network security policy**. Please consult your IT staff to find an accepted and approved
-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.
+The Secret Detection job should now use the local copy of the Secret Detection analyzer Docker
+image, without requiring internet access.
-For details on saving and transporting Docker images as a file, see Docker's 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/).
+### Configure a custom Certificate Authority
-### Set Secret Detection CI/CD variables to use the local Secret Detection analyzer container image
+To trust a custom Certificate Authority, set the `ADDITIONAL_CA_CERT_BUNDLE` variable to the bundle
+of CA certificates that you trust. Do this either in the `.gitlab-ci.yml` file, in a file
+variable, or as a CI/CD variable.
-Add the following configuration to your `.gitlab-ci.yml` file. You must replace
-`SECURE_ANALYZERS_PREFIX` to refer to your local Docker container registry:
+- In the `.gitlab-ci.yml` file, the `ADDITIONAL_CA_CERT_BUNDLE` value must contain the
+ [text representation of the X.509 PEM public-key certificate](https://tools.ietf.org/html/rfc7468#section-5.1).
-```yaml
-include:
- - template: Security/Secret-Detection.gitlab-ci.yml
+ For example:
-variables:
- SECURE_ANALYZERS_PREFIX: "localhost:5000/analyzers"
-```
+ ```yaml
+ variables:
+ ADDITIONAL_CA_CERT_BUNDLE: |
+ -----BEGIN CERTIFICATE-----
+ MIIGqTCCBJGgAwIBAgIQI7AVxxVwg2kch4d56XNdDjANBgkqhkiG9w0BAQsFADCB
+ ...
+ jWgmPqF3vUbZE0EyScetPJquRFRKIesyJuBFMAs=
+ -----END CERTIFICATE-----
+ ```
-The Secret Detection job should now use the local copy of the Secret Detection analyzer Docker image to scan your code and generate
-security reports without requiring internet access.
+- If using a file variable, set the value of `ADDITIONAL_CA_CERT_BUNDLE` to the path to the
+ certificate.
-#### If support for Custom Certificate Authorities are needed
+- If using a variable, set the value of `ADDITIONAL_CA_CERT_BUNDLE` to the text
+ representation of the certificate.
-Support for custom certificate authorities was introduced in the following versions.
+## Troubleshooting
-| Analyzer | Version |
-| -------- | ------- |
-| secrets | [v3.0.0](https://gitlab.com/gitlab-org/security-products/analyzers/secrets/-/releases/v3.0.0) |
+### Set the logging level
-To trust a custom Certificate Authority, set the `ADDITIONAL_CA_CERT_BUNDLE` variable to the bundle
-of CA certs that you want to trust in the SAST environment. The `ADDITIONAL_CA_CERT_BUNDLE` value should contain the [text representation of the X.509 PEM public-key certificate](https://tools.ietf.org/html/rfc7468#section-5.1). For example, to configure this value in the `.gitlab-ci.yml` file, use the following:
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10880) in GitLab 13.1.
-```yaml
-variables:
- ADDITIONAL_CA_CERT_BUNDLE: |
- -----BEGIN CERTIFICATE-----
- MIIGqTCCBJGgAwIBAgIQI7AVxxVwg2kch4d56XNdDjANBgkqhkiG9w0BAQsFADCB
- ...
- jWgmPqF3vUbZE0EyScetPJquRFRKIesyJuBFMAs=
- -----END CERTIFICATE-----
-```
+Set the logging level to `debug` when you need diagnostic information in a Secret Detection job log.
-The `ADDITIONAL_CA_CERT_BUNDLE` value can also be configured as a [custom variable in the UI](../../../ci/variables/index.md#custom-cicd-variables), either as a `file`, which requires the path to the certificate, or as a variable, which requires the text representation of the certificate.
+WARNING:
+Debug logging can be a serious security risk. The output may contain the content of environment
+variables and other secrets available to the job. The output is uploaded to the GitLab server and
+visible in job logs.
-## Troubleshooting
+1. In the `.gitlab-ci.yml` file, set the `SECURE_LOG_LEVEL` CI/CD variable to `debug`.
+1. Run the Secret Detection job.
+1. Analyze the content of the Secret Detection job.
+1. In the `.gitlab-ci.yml` file, set the `SECURE_LOG_LEVEL` CI/CD variable to `info` (default).
-### Getting warning message `gl-secret-detection-report.json: no matching files`
+### Warning: `gl-secret-detection-report.json: no matching files`
For information on this, see the [general Application Security troubleshooting section](../../../ci/pipelines/job_artifacts.md#error-message-no-files-to-upload).
### Error: `Couldn't run the gitleaks command: exit status 2`
-If a pipeline is triggered from a merge request containing 60 commits while the `GIT_DEPTH` variable's
-value is less than that, the Secret Detection job fails as the clone is not deep enough to contain all of the
-relevant commits. For information on the current default value, see the
-[pipeline configuration documentation](../../../ci/pipelines/settings.md#limit-the-number-of-changes-fetched-during-clone).
+The Secret Detection analyzer relies on generating patches between commits to scan content for
+secrets. If the number of commits in a merge request is greater than the value of the
+[`GIT_DEPTH` CI/CD variable](../../../ci/runners/configure_runners.md#shallow-cloning), Secret
+Detection [fails to detect secrets](#error-couldnt-run-the-gitleaks-command-exit-status-2).
+
+For example, if a pipeline is triggered from a merge request containing 60 commits and the
+`GIT_DEPTH` variable's value is less than 60, the Secret Detection job fails as the clone is not
+deep enough to contain all of the relevant commits. To veridy the current value, see
+[pipeline configuration](../../../ci/pipelines/settings.md#limit-the-number-of-changes-fetched-during-clone).
-To confirm this as the cause of the error, set the
-[logging level](../../application_security/secret_detection/index.md#logging-level) to `debug`, then
+To confirm this as the cause of the error, set the [logging level](#set-the-logging-level) to `debug`, then
rerun the pipeline. The logs should look similar to the following example. The text "object not
found" is a symptom of this error.
@@ -476,11 +458,12 @@ secret_detection:
GIT_DEPTH: 100
```
-### `secret-detection` job fails with `ERR fatal: ambiguous argument` message
+### Error: `ERR fatal: ambiguous argument`
-Your `secret-detection` job can fail with `ERR fatal: ambiguous argument` error if your
-repository's default branch is unrelated to the branch the job was triggered for.
-See issue [!352014](https://gitlab.com/gitlab-org/gitlab/-/issues/352014) for more details.
+Secret Detection can fail with the message `ERR fatal: ambiguous argument` error if your
+repository's default branch is unrelated to the branch the job was triggered for. See issue
+[!352014](https://gitlab.com/gitlab-org/gitlab/-/issues/352014) for more details.
-To resolve the issue, make sure to correctly [set your default branch](../../project/repository/branches/default.md#change-the-default-branch-name-for-a-project) on your repository. You should set it to a branch
-that has related history with the branch you run the `secret-detection` job on.
+To resolve the issue, make sure to correctly [set your default branch](../../project/repository/branches/default.md#change-the-default-branch-name-for-a-project)
+on your repository. You should set it to a branch that has related history with the branch you run
+the `secret-detection` job on.
diff --git a/doc/user/application_security/security_dashboard/index.md b/doc/user/application_security/security_dashboard/index.md
index f3c834e06c7..967e8da58a9 100644
--- a/doc/user/application_security/security_dashboard/index.md
+++ b/doc/user/application_security/security_dashboard/index.md
@@ -1,6 +1,6 @@
---
type: reference, howto
-stage: Secure
+stage: Govern
group: Threat Insights
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
---
@@ -49,12 +49,15 @@ To reduce false negatives in [dependency scans](../../../user/application_securi
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/285477) in GitLab 13.11, date range slider to visualize data between given dates.
The project Security Dashboard shows the total number of vulnerabilities
-over time, with up to 365 days of historical data. Data refreshes daily at 01:15 UTC.
-It shows statistics for all vulnerabilities.
+over time, with up to 365 days of historical data. Data refresh begins daily at 01:15 UTC via a scheduled job.
+Each refresh captures a snapshot of open vulnerabilities. Data is not backported to prior days
+so vulnerabilities opened after the job has already run for the day will not be reflected in the
+counts until the following day's refresh job.
+Project Security Dashboards show statistics for all vulnerabilities with a current status of `Needs triage` or `Confirmed` .
To view total number of vulnerabilities over time:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Security & Compliance > Security Dashboard**.
1. Filter and search for what you need.
- To filter the chart by severity, select the legend name.
@@ -67,7 +70,7 @@ To view total number of vulnerabilities over time:
To download an SVG image of the vulnerabilities chart:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Security & Compliance > Security dashboard**.
1. Select **Save chart as an image** (**{download}**).
@@ -78,7 +81,7 @@ branches of projects in a group and its subgroups.
To view vulnerabilities over time for a group:
-1. On the top bar, select **Menu > Groups** and select a group.
+1. On the top bar, select **Main menu > Groups** and select a group.
1. Select **Security > Security Dashboard**.
1. Hover over the chart to get more details about vulnerabilities.
- You can display the vulnerability trends over a 30, 60, or 90-day time frame (the default is 90 days).
@@ -88,15 +91,16 @@ To view vulnerabilities over time for a group:
## View project security status for a group
-Use the group Security Dashboard to view the security status of projects. The security status is based
-on the number of detected vulnerabilities.
+Use the group Security Dashboard to view the security status of projects.
To view project security status for a group:
-1. On the top bar, select **Menu > Groups** and select a group.
+1. On the top bar, select **Main menu > Groups** and select a group.
1. Select **Security > Security Dashboard**.
-Projects are [graded](#project-vulnerability-grades) by vulnerability severity. Dismissed vulnerabilities are excluded.
+Each project is assigned a letter [grade](#project-vulnerability-grades) according to the highest-severity open vulnerability.
+Dismissed or resolved vulnerabilities are excluded. Each project can receive only one letter grade and will appear only once
+in the Project security status report.
To view vulnerabilities, go to the group's [vulnerability report](../vulnerability_report/index.md).
@@ -127,13 +131,13 @@ The Security Center includes:
### View the Security Center
-To view the Security Center, on the top bar, select **Menu > Security**.
+To view the Security Center, on the top bar, select **Main menu > Security**.
### Add projects to the Security Center
To add projects to the Security Center:
-1. On the top bar, select **Menu > Security**.
+1. On the top bar, select **Main menu > Security**.
1. On the left sidebar, select **Settings**, or select **Add projects**.
1. Use the **Search your projects** text box to search for and select projects.
1. Select **Add projects**.
diff --git a/doc/user/application_security/vulnerabilities/index.md b/doc/user/application_security/vulnerabilities/index.md
index ad397c3fe04..91793272cce 100644
--- a/doc/user/application_security/vulnerabilities/index.md
+++ b/doc/user/application_security/vulnerabilities/index.md
@@ -1,5 +1,5 @@
---
-stage: Secure
+stage: Govern
group: Threat Insights
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
---
@@ -46,14 +46,14 @@ are reintroduced and detected by subsequent scans have a _new_ vulnerability rec
existing vulnerability is no longer detected in a project's `default` branch, you should change its
status to **Resolved**. This ensures that if it is accidentally reintroduced in a future merge, it
is reported again as a new record. You can use the Vulnerability Report's
-[Activity filter](../vulnerability_report/#activity-filter) to select all vulnerabilities that are
+[Activity filter](../vulnerability_report/index.md#activity-filter) to select all vulnerabilities that are
no longer detected, and change their status.
## Change status of a vulnerability
To change a vulnerability's status from its Vulnerability Page:
-1. On the top bar, select **Menu > Projects** and find your project.
+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 the vulnerability's description.
1. From the **Status** dropdown list select a status, then select **Change status**.
@@ -77,7 +77,7 @@ that when Jira integration is enabled, the GitLab issue feature is not available
To create a GitLab issue for a vulnerability:
-1. On the top bar, select **Menu > Projects** and find your project.
+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 the vulnerability's description.
1. Select **Create issue**.
@@ -99,7 +99,7 @@ Prerequisites:
To create a Jira issue for a vulnerability:
-1. On the top bar, select **Menu > Projects** and find your project.
+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 the vulnerability's description.
1. Select **Create Jira issue**.
@@ -132,7 +132,7 @@ Be aware of the following conditions between a vulnerability and a linked issue:
To link a vulnerability to existing issues:
-1. On the top bar, select **Menu > Projects** and find your project.
+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 the vulnerability's description.
1. In the **Linked issues** section, select the plus icon (**{plus}**).
@@ -167,7 +167,7 @@ To resolve a vulnerability, you can either:
To resolve the vulnerability with a merge request:
-1. On the top bar, select **Menu > Projects** and find your project.
+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 the vulnerability's description.
1. From the **Resolve with merge request** dropdown list, select **Resolve with merge request**.
@@ -179,7 +179,7 @@ Process the merge request according to your standard workflow.
To manually apply the patch that GitLab generated for a vulnerability:
-1. On the top bar, select **Menu > Projects** and find your project.
+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 the vulnerability's description.
1. From the **Resolve with merge request** dropdown list, select **Download patch to resolve**.
@@ -197,7 +197,7 @@ Security training helps your developers learn how to fix vulnerabilities. Develo
To enable security training for vulnerabilities in your project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Security & Compliance > Configuration**.
1. On the tab bar, select **Vulnerability Management**.
1. To enable a security training provider, turn on the toggle.
@@ -215,7 +215,7 @@ Vulnerabilities with a CWE are most likely to return a training result.
To view the security training for a vulnerability:
-1. On the top bar, select **Menu > Projects** and find your project.
+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 the vulnerability for which you want to view security training.
1. Select **View training**.
diff --git a/doc/user/application_security/vulnerabilities/severities.md b/doc/user/application_security/vulnerabilities/severities.md
index 987dac677e7..aed86cd93aa 100644
--- a/doc/user/application_security/vulnerabilities/severities.md
+++ b/doc/user/application_security/vulnerabilities/severities.md
@@ -1,6 +1,6 @@
---
type: reference
-stage: Secure
+stage: Govern
group: Threat Insights
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
---
diff --git a/doc/user/application_security/vulnerability_report/index.md b/doc/user/application_security/vulnerability_report/index.md
index 15a287356f8..ba448d410ea 100644
--- a/doc/user/application_security/vulnerability_report/index.md
+++ b/doc/user/application_security/vulnerability_report/index.md
@@ -1,6 +1,6 @@
---
type: reference, howto
-stage: Secure
+stage: Govern
group: Threat Insights
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
---
@@ -44,7 +44,7 @@ At the project level, the Vulnerability Report also contains:
To view the project-level vulnerability report:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Security & Compliance > Vulnerability report**.
## Vulnerability Report actions
@@ -57,6 +57,7 @@ From the Vulnerability Report you can:
- [View an issue raised for a vulnerability](#view-issues-raised-for-a-vulnerability).
- [Change the status of vulnerabilities](#change-status-of-vulnerabilities).
- [Export details of vulnerabilities](#export-vulnerability-details).
+- [Sort vulnerabilities by date](#sort-vulnerabilities-by-date-detected).
- [Manually add a vulnerability finding](#manually-add-a-vulnerability-finding).
## Vulnerability Report filters
@@ -186,6 +187,12 @@ Vulnerability records cannot be deleted, so a permanent record always remains.
If a vulnerability is dismissed in error, reverse the dismissal by changing its status.
+## Sort vulnerabilities by date detected
+
+By default, vulnerabilities are sorted by severity level, with the highest-severity vulnerabilities listed at the top.
+
+To sort vulnerabilities by the date each vulnerability was detected, click the "Detected" column header.
+
## Export vulnerability details
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213014) in the Security Center (previously known as the Instance Security Dashboard) and project-level Vulnerability Report (previously known as the Project Security Dashboard) in GitLab 13.0.
@@ -247,7 +254,7 @@ To undo this action, select a different status from the same menu.
To add a new vulnerability finding from your project level Vulnerability Report page:
-1. On the top bar, select **Menu > Projects** and find your project.
+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. Complete the fields and submit the form.
diff --git a/doc/user/application_security/vulnerability_report/pipeline.md b/doc/user/application_security/vulnerability_report/pipeline.md
index 32916f4c9c7..7faf273515c 100644
--- a/doc/user/application_security/vulnerability_report/pipeline.md
+++ b/doc/user/application_security/vulnerability_report/pipeline.md
@@ -1,6 +1,6 @@
---
type: reference, howto
-stage: Secure
+stage: Govern
group: Threat Insights
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
---
@@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
To view vulnerabilities in a pipeline:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **CI/CD > Pipelines**.
1. From the list, select the pipeline you want to check for vulnerabilities.
1. Select the **Security** tab.
@@ -26,7 +26,7 @@ For example, if a pipeline contains DAST and SAST jobs, but the DAST job fails b
The pipeline vulnerability report only shows results contained in the security report artifacts. This report differs from
the [Vulnerability Report](index.md), which contains cumulative results of all successful jobs, and from the merge request
-[security widget](../#view-security-scan-information-in-merge-requests), which combines the branch results with
+[security widget](../index.md#view-security-scan-information-in-merge-requests), which combines the branch results with
cumulative results.
Before GitLab displays results, the vulnerability findings in all pipeline reports are [deduplicated](#deduplication-process).
@@ -35,7 +35,7 @@ Before GitLab displays results, the vulnerability findings in all pipeline repor
**Scan details** shows a summary of vulnerability findings in the pipeline and the source reports.
-GitLab displays one row of information for each [scan type](../terminology/#scan-type-report-type) artifact present in
+GitLab displays one row of information for each [scan type](../terminology/index.md#scan-type-report-type) artifact present in
the pipeline.
Note that each scan type's total number of vulnerabilities includes dismissed findings. If the number of findings
@@ -83,11 +83,11 @@ incorporated once the pipeline finishes.
When a pipeline contains jobs that produce multiple security reports of the same type, it is possible that the same
vulnerability finding is present in multiple reports. This duplication is common when different scanners are used to
-increase coverage. The deduplication process allows you to maximize the vulnerability scanning coverage while reducing
+increase coverage, but can also exist within a single report. The deduplication process allows you to maximize the vulnerability scanning coverage while reducing
the number of findings you need to manage.
-A finding is considered a duplicate of another finding when their [scan type](../terminology/#scan-type-report-type),
-[location](../terminology/#location-fingerprint) and
+A finding is considered a duplicate of another finding when their [scan type](../terminology/index.md#scan-type-report-type),
+[location](../terminology/index.md#location-fingerprint), and one or more of its
[identifiers](../../../development/integrations/secure.md#identifiers) are the same.
The scan type must match because each can have its own definition for the location of a vulnerability. For example,
@@ -95,8 +95,9 @@ static analyzers are able to locate a file path and line number, whereas a conta
name instead.
When comparing identifiers, GitLab does not compare `CWE` and `WASC` during deduplication because they are
-"type identifiers" and are used to classify groups of vulnerabilities. Including these identifiers results in
-many findings being incorrectly considered duplicates.
+"type identifiers" and are used to classify groups of vulnerabilities. Including these identifiers would result in
+many findings being incorrectly considered duplicates. Two findings are considered unique if none of their
+identifiers match.
In a set of duplicated findings, the first occurrence of a finding is kept and the remaining are skipped. Security
reports are processed in alphabetical file path order, and findings are processed sequentially in the order they
@@ -124,16 +125,17 @@ appear in a report.
- Location fingerprint: `adc83b19e793491b1c6ea0fd8b46cd9f32e592fc`
- Identifiers: CWE-798
- Deduplication result: duplicates because `CWE` identifiers are ignored.
-- Example 3: matching scan type, location and identifiers.
+- Example 3: matching scan type, location and an identifier.
- Finding
- Scan type: `container_scanning`
- Location fingerprint: `adc83b19e793491b1c6ea0fd8b46cd9f32e592fc`
- - Identifiers: CVE-2022-25510, CWE-259
+ - Identifiers: CVE-2019-12345, CVE-2022-25510, CWE-259
- Other Finding
- Scan type: `container_scanning`
- Location fingerprint: `adc83b19e793491b1c6ea0fd8b46cd9f32e592fc`
- Identifiers: CVE-2022-25510, CWE-798
- Deduplication result: duplicates because all criteria match, and type identifiers are ignored.
+ Only one identifier needs to match, in this case CVE-2022-25510.
The examples above don't include the raw location values. Each scan type defines its own
`fingerprint_data`, which is used to generate a `SHA1` hash that is used as the `location_fingerprint`.
diff --git a/doc/user/clusters/agent/ci_cd_workflow.md b/doc/user/clusters/agent/ci_cd_workflow.md
index 16b92eb92a3..7a6c6dc8cd6 100644
--- a/doc/user/clusters/agent/ci_cd_workflow.md
+++ b/doc/user/clusters/agent/ci_cd_workflow.md
@@ -62,7 +62,7 @@ Authorization configuration can take one or two minutes to propagate.
To authorize the agent to access the GitLab project where you keep Kubernetes manifests:
-1. On the top bar, select **Menu > Projects** and find the project that contains the [agent configuration file](install/index.md#create-an-agent-configuration-file) (`config.yaml`).
+1. On the top bar, select **Main menu > Projects** and find the project that contains the [agent configuration file](install/index.md#create-an-agent-configuration-file) (`config.yaml`).
1. Edit the `config.yaml` file. Under the `ci_access` keyword, add the `projects` attribute.
1. For the `id`, add the path:
@@ -85,7 +85,7 @@ Choose the context to run `kubectl` commands from your CI/CD scripts.
To authorize the agent to access all of the GitLab projects in a group or subgroup:
-1. On the top bar, select **Menu > Projects** and find the project that contains the [agent configuration file](install/index.md#create-an-agent-configuration-file) (`config.yaml`).
+1. On the top bar, select **Main menu > Projects** and find the project that contains the [agent configuration file](install/index.md#create-an-agent-configuration-file) (`config.yaml`).
1. Edit the `config.yaml` file. Under the `ci_access` keyword, add the `groups` attribute.
1. For the `id`, add the path:
@@ -127,7 +127,7 @@ Run `kubectl config get-contexts`.
### Environments with both certificate-based and agent-based connections
-When you deploy to an environment that has both a
+When you deploy to an environment that has both a
[certificate-based cluster](../../infrastructure/clusters/index.md) (deprecated) and an agent connection:
- The certificate-based cluster's context is called `gitlab-deploy`. This context
diff --git a/doc/user/clusters/agent/gitops.md b/doc/user/clusters/agent/gitops.md
index 4978b56917b..67439788ef7 100644
--- a/doc/user/clusters/agent/gitops.md
+++ b/doc/user/clusters/agent/gitops.md
@@ -49,7 +49,7 @@ For details, view the [architecture documentation](https://gitlab.com/gitlab-org
To update a Kubernetes cluster by using GitOps, complete the following steps.
-1. Ensure you have a working Kubernetes cluster, and that the manifests are in a GitLab project.
+1. Ensure you have a working Kubernetes cluster, and that the manifests or [Helm charts](gitops/helm.md) are in a GitLab project.
1. In the same project, [register and install the GitLab agent](install/index.md).
1. Configure the agent configuration file so that the agent monitors the project for changes to the Kubernetes manifests.
Use the [GitOps configuration reference](#gitops-configuration-reference) for guidance.
@@ -112,12 +112,12 @@ a Kubernetes SIG project. You can read more about the available annotations in t
## Automatic drift remediation
-Drift happens when the current configuration of an infrastructure resource differs from its expected configuration.
-Typically, this is caused by manually editing resources directly through the service that created the resource. Minimizing the
-risk of drift helps to ensure configuration consistency and successful operations.
+Drift happens when the current configuration of an infrastructure resource differs from its desired configuration.
+Typically, this is caused by manually editing resources directly rather than via the used infrastructure-as-code
+mechanism. Minimizing the risk of drift helps to ensure configuration consistency and successful operations.
-In GitLab, the agent for Kubernetes regularly compares the expected state from the `git` repository with
-the known state from the `cluster`. Deviations from the `git` state are fixed at every check. These checks
+In GitLab, the agent for Kubernetes regularly compares the desired state from the `git` repository with
+the actual state from the Kubernetes cluster. Deviations from the `git` state are fixed at every check. These checks
happen automatically every 5 minutes. They are not configurable.
The agent uses [server-side applies](https://kubernetes.io/docs/reference/using-api/server-side-apply/).
@@ -127,6 +127,7 @@ are checked for drift. This facilitates the use of in-cluster controllers to mod
## Related topics
+- [Deploying Helm charts with the GitOps workflow](gitops/helm.md)
- [GitOps working examples for training and demos](https://gitlab.com/groups/guided-explorations/gl-k8s-agent/gitops/-/wikis/home)
- [Self-paced classroom workshop](https://gitlab-for-eks.awsworkshop.io) (Uses AWS EKS, but you can use for other Kubernetes clusters)
- [Managing Kubernetes secrets in a GitOps workflow](gitops/secrets_management.md)
diff --git a/doc/user/clusters/agent/gitops/helm.md b/doc/user/clusters/agent/gitops/helm.md
new file mode 100644
index 00000000000..bdc2664e7ba
--- /dev/null
+++ b/doc/user/clusters/agent/gitops/helm.md
@@ -0,0 +1,112 @@
+---
+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/engineering/ux/technical-writing/#assignments
+---
+
+# Using Helm charts to update a Kubernetes cluster (Alpha) **(FREE)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/371019) in GitLab 15.4.
+
+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
+Kubernetes.
+
+This feature is in Alpha and [an epic exists](https://gitlab.com/groups/gitlab-org/-/epics/7938)
+to track future work. Please tell us about your use cases by leaving comments in the epic.
+
+NOTE:
+This feature is Alpha. In future releases, to accommodate new features, the configuration format might change without notice.
+
+## GitOps workflow steps
+
+To update a Kubernetes cluster by using GitOps with charts, complete the following steps.
+
+1. Ensure you have a working Kubernetes cluster, and that the chart is in a GitLab project.
+1. In the same project, [register and install the GitLab agent](../install/index.md).
+1. Configure the agent configuration file so that the agent monitors the project for changes to the chart.
+ Use the [GitOps configuration reference](#helm-configuration-reference) for guidance.
+
+## Helm chart with GitOps workflow
+
+To update a Kubernetes cluster by using Helm charts:
+
+1. Ensure you have a working Kubernetes cluster.
+1. In a GitLab project:
+ - Store your Helm charts.
+ - [Register and install the GitLab agent](../install/index.md).
+1. Update the agent configuration file so that the agent monitors the project for changes to the chart.
+ Use the [configuration reference](#helm-configuration-reference) for guidance.
+
+Any time you commit updates to your chart repository, the agent applies the chart in the cluster.
+
+## Helm configuration reference
+
+The following snippet shows an example of the possible keys and values for the GitOps section of an [agent configuration file](../install/index.md#create-an-agent-configuration-file) (`config.yaml`).
+
+```yaml
+gitops:
+ charts:
+ - release_name: my-application-release
+ source:
+ project:
+ id: my-group/my-project-with-chart
+ path: dir-in-project/with/charts
+ namespace: my-ns
+ max_history: 1
+```
+
+| Keyword | Description |
+|--|--|
+| `charts` | List of charts you want to be applied in your cluster. Charts are applied concurrently. All charts must be in the same directory. |
+| `release_name` | Required. Name of the release to use when applying the chart. |
+| `id` | Required. ID of the project where Helm chart is committed. No authentication mechanisms are currently supported. |
+| `path` | Optional. Path of the chart in the project repository. Root of the repository is used by default. This is the directory with the `Chart.yaml` file. |
+| `namespace` | Optional. Namespace to use when applying the chart. Defaults to `default`. |
+| `max_history` | Optional. Maximum number of release [revisions to store in the cluster](https://helm.sh/docs/helm/helm_history/). |
+
+## Automatic drift remediation
+
+Drift happens when the current configuration of an infrastructure resource differs from its desired configuration.
+Typically, drift is caused by manually editing resources directly, rather than by editing the code that describes the desired state. Minimizing the risk of drift helps to ensure configuration consistency and successful operations.
+mechanism. Minimizing the risk of drift helps to ensure configuration consistency and successful operations.
+
+In GitLab, the agent for Kubernetes regularly compares the desired state from the chart source with
+the actual state from the Kubernetes cluster. Deviations from the desired state are fixed at every check. These checks
+happen automatically every 5 minutes. They are not configurable.
+
+## Example repository layout
+
+```plaintext
+/my-chart
+ ├── templates
+ | └── ...
+ ├── charts
+ | └── ...
+ ├── Chart.yaml
+ ├── Chart.lock
+ ├── values.yaml
+ ├── values.schema.json
+ └── some-file-used-in-chart.txt
+```
+
+## Known issues
+
+The following are known issues:
+
+- Your chart must be in a GitLab project. The project must be an agent configuration project or a public
+ project. This known issue also exists for manifest-based GitOps and is tracked in
+ [this epic](https://gitlab.com/groups/gitlab-org/-/epics/7704).
+- Values for the chart must be in a `values.yaml` file. This file must be with the chart,
+ in the same project and path.
+- Because of drift detection and remediation, release history, stored in the cluster, is not useful.
+ A new release is created every five minutes and the oldest release is discarded.
+ Eventually history consists only of the same information.
+ View [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/372023) for details.
+
+## Troubleshooting
+
+### Agent cannot find values for the chart
+
+Make sure values are in `values.yaml` and in the same directory as the `Chart.yaml` file.
+The filename must be lowercase, with `.yaml` extension (not `.yml`).
diff --git a/doc/user/clusters/agent/index.md b/doc/user/clusters/agent/index.md
index 0d2b68e154d..eb62a733d36 100644
--- a/doc/user/clusters/agent/index.md
+++ b/doc/user/clusters/agent/index.md
@@ -29,14 +29,40 @@ For more details about the agent's purpose and architecture, see the [architectu
## Workflows
-You can choose from two primary workflows.
+You can choose from two primary workflows. The GitOps workflow is recommended.
-In a [**GitOps** workflow](gitops.md), you keep your Kubernetes manifests in GitLab. You install a GitLab agent in your cluster, and
-any time you update your manifests, the agent updates the cluster. This workflow is fully driven with Git and is considered pull-based,
+### GitOps workflow
+
+In a [**GitOps** workflow](gitops.md):
+
+- You keep your Kubernetes manifests in GitLab.
+- You install a GitLab agent in your cluster.
+- Any time you update your manifests, the agent updates the cluster.
+- The cluster automatically cleans up unexpected changes. It uses
+ [server-side applies](https://kubernetes.io/docs/reference/using-api/server-side-apply/)
+ to fix any configuration inconsistencies that third parties introduce.
+
+This workflow is fully driven with Git and is considered **pull-based**,
because the cluster is pulling updates from your GitLab repository.
-In a [**CI/CD** workflow](ci_cd_workflow.md), you use GitLab CI/CD to query and update your cluster by using the Kubernetes API.
-This workflow is considered push-based, because GitLab is pushing requests from GitLab CI/CD to your cluster.
+GitLab recommends this workflow. We are actively investing in this workflow
+so we can provide a first-class experience.
+
+### GitLab CI/CD workflow
+
+In a [**CI/CD** workflow](ci_cd_workflow.md):
+
+- You configure GitLab CI/CD to use the Kubernetes API to query and update your cluster.
+
+This workflow is considered **push-based**, because GitLab is pushing requests
+from GitLab CI/CD to your cluster.
+
+Use this workflow:
+
+- When you have a heavily pipeline-oriented processes.
+- When you need to migrate to the agent but the GitOps workflow cannot support the use case you need.
+
+This workflow has a weaker security model and is not recommended for production deployments.
## Supported cluster versions
diff --git a/doc/user/clusters/agent/install/index.md b/doc/user/clusters/agent/install/index.md
index 4b0d8b77493..0240fbb45f0 100644
--- a/doc/user/clusters/agent/install/index.md
+++ b/doc/user/clusters/agent/install/index.md
@@ -40,7 +40,7 @@ To install the agent in your cluster:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/259669) in GitLab 13.7, the agent configuration file can be added to multiple directories (or subdirectories) of the repository.
> - Group authorization was [introduced](https://gitlab.com/groups/gitlab-org/-/epics/5784) in GitLab 14.3.
-The agent uses a YAML file for configuration settings. You must create this file if:
+For configuration settings, the agent uses a YAML file in the GitLab project. You must create this file if:
- You use [a GitOps workflow](../gitops.md#gitops-workflow-steps).
- You use [a GitLab CI/CD workflow](../ci_cd_workflow.md#gitlab-cicd-workflow-steps) and want to authorize a different project to use the agent.
@@ -56,7 +56,7 @@ To create an agent configuration file:
- Start with an alphanumeric character.
- End with an alphanumeric character.
-1. In the repository, create a directory in this location:
+1. In the repository, in the default branch, create this directory at the root:
```plaintext
.gitlab/agents/<agent-name>
@@ -81,7 +81,7 @@ Prerequisites:
You must register an agent before you can install the agent in your cluster. To register an agent:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
If you have an [agent configuration file](#create-an-agent-configuration-file),
it must be in this project. Your cluster manifest files should also be in this project.
1. From the left sidebar, select **Infrastructure > Kubernetes clusters**.
diff --git a/doc/user/clusters/agent/vulnerabilities.md b/doc/user/clusters/agent/vulnerabilities.md
index 5afe3ccec2b..2d20675b68b 100644
--- a/doc/user/clusters/agent/vulnerabilities.md
+++ b/doc/user/clusters/agent/vulnerabilities.md
@@ -6,7 +6,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Operational Container Scanning **(ULTIMATE)**
-> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6346) in GitLab 14.8.
+> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6346) in GitLab 14.8.
+> - [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/368828) the starboard directive in GitLab 15.4. The starboard directive will be removed in GitLab 16.0.
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.
@@ -19,12 +20,12 @@ to scan container images in your cluster for security vulnerabilities.
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 `starboard`
+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.
```yaml
-starboard:
+container_scanning:
cadence: '0 0 * * *' # Daily at 00:00 (Kubernetes cluster time)
```
@@ -42,7 +43,7 @@ if you would like to scan only the `development`, `staging`, and `production`
namespaces, you can use this configuration:
```yaml
-starboard:
+container_scanning:
cadence: '0 0 * * *'
vulnerability_report:
namespaces:
@@ -59,7 +60,7 @@ Prerequisite:
To view vulnerability information in GitLab:
-1. On the top bar, select **Menu > Projects** and find the project that contains the agent configuration file.
+1. On the top bar, select **Main menu > Projects** and find the project that contains the agent configuration file.
1. On the left sidebar, select **Infrastructure > Kubernetes clusters**.
1. Select the **Agent** tab.
1. Select an agent to view the cluster vulnerabilities.
diff --git a/doc/user/clusters/agent/work_with_agent.md b/doc/user/clusters/agent/work_with_agent.md
index 058243ec218..b28f7546379 100644
--- a/doc/user/clusters/agent/work_with_agent.md
+++ b/doc/user/clusters/agent/work_with_agent.md
@@ -18,7 +18,7 @@ Prerequisite:
To view the list of agents:
-1. On the top bar, select **Menu > Projects** and find the project that contains your agent configuration file.
+1. On the top bar, select **Main menu > Projects** and find the project that contains your agent configuration file.
1. On the left sidebar, select **Infrastructure > Kubernetes clusters**.
1. Select **Agent** tab to view clusters connected to GitLab through the agent.
@@ -37,7 +37,7 @@ The activity logs help you to identify problems and get the information
you need for troubleshooting. You can see events from a week before the
current date. To view an agent's activity:
-1. On the top bar, select **Menu > Projects** and find the project that contains your agent configuration file.
+1. On the top bar, select **Main menu > Projects** and find the project that contains your agent configuration file.
1. On the left sidebar, select **Infrastructure > Kubernetes clusters**.
1. Select the agent you want to see activity for.
@@ -59,7 +59,6 @@ To debug the cluster-side component (`agentk`) of the agent, set the log
level according to the available options:
- `error`
-- `warning`
- `info`
- `debug`
@@ -95,7 +94,7 @@ For more information about debugging, see [troubleshooting documentation](troubl
To reset the agent token without downtime:
1. Create a new token:
- 1. On the top bar, select **Menu > Projects** and find your project.
+ 1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Infrastructure > Kubernetes clusters**.
1. Select the agent you want to create a token for.
1. On the **Access tokens** tab, select **Create token**.
@@ -117,7 +116,7 @@ clean up those resources manually.
To remove an agent from the UI:
-1. On the top bar, select **Menu > Projects** and find the project that contains the agent configuration file.
+1. On the top bar, select **Main menu > Projects** and find the project that contains the agent configuration file.
1. From the left sidebar, select **Infrastructure > Kubernetes clusters**.
1. In the table, in the row for your agent, in the **Options** column, select the vertical ellipsis (**{ellipsis_v}**).
1. Select **Delete agent**.
diff --git a/doc/user/clusters/environments.md b/doc/user/clusters/environments.md
index cf71729b517..96f41531576 100644
--- a/doc/user/clusters/environments.md
+++ b/doc/user/clusters/environments.md
@@ -33,7 +33,7 @@ With cluster environments, you can gain insight into:
![Cluster environments page](img/cluster_environments_table_v12_3.png)
-Access to cluster environments is restricted to
+Access to cluster environments is restricted to
[group maintainers and owners](../permissions.md#group-members-permissions)
## Usage
diff --git a/doc/user/clusters/management_project.md b/doc/user/clusters/management_project.md
index 361276194b0..62f70faa630 100644
--- a/doc/user/clusters/management_project.md
+++ b/doc/user/clusters/management_project.md
@@ -61,7 +61,7 @@ To associate a cluster management project with your cluster:
**Infrastructure > Kubernetes clusters** page.
- [Group-level cluster](../group/clusters/index.md), go to your group's **Kubernetes**
page.
- - [Instance-level cluster](../instance/clusters/index.md), on the top bar, select **Menu > Admin > Kubernetes**.
+ - [Instance-level cluster](../instance/clusters/index.md), on the top bar, select **Main menu > Admin > Kubernetes**.
1. Expand **Advanced settings**.
1. From the **Cluster management project** dropdown, select the cluster management project
you created in the previous step.
diff --git a/doc/user/clusters/management_project_template.md b/doc/user/clusters/management_project_template.md
index 4b00784a7ae..cd71be321cc 100644
--- a/doc/user/clusters/management_project_template.md
+++ b/doc/user/clusters/management_project_template.md
@@ -45,7 +45,8 @@ If you have already configured the agent and connected a cluster with GitLab:
To create a project from the cluster management project template:
-1. On the top bar, select **Menu > Projects > Create new project**.
+1. In GitLab, on the top bar, select **Main menu > Projects > View all projects**.
+1. On the right of the page, select **New project**.
1. Select **Create from template**.
1. From the list of templates, next to **GitLab Cluster Management**, select **Use template**.
1. Enter the project details.
@@ -80,7 +81,7 @@ If you remove everything below this comment, the pipeline will succeed.
### The main `helmfile.yml` file
-The template contains a [Helmfile](https://github.com/roboll/helmfile) you can use to manage
+The template contains a [Helmfile](https://github.com/helmfile/helmfile) you can use to manage
cluster applications with [Helm v3](https://helm.sh/).
This file has a list of paths to other Helm files for each app. They're all commented out by default, so you must uncomment
@@ -89,7 +90,7 @@ the paths for the apps that you would like to use in your cluster.
By default, each `helmfile.yaml` in these sub-paths has the attribute `installed: true`. This means that every time
the pipeline runs, Helmfile tries to either install or update your apps according to the current state of your
cluster and Helm releases. If you change this attribute to `installed: false`, Helmfile tries try to uninstall this app
-from your cluster. [Read more](https://github.com/roboll/helmfile) about how Helmfile works.
+from your cluster. [Read more](https://helmfile.readthedocs.io/en/latest/) about how Helmfile works.
### Built-in applications
diff --git a/doc/user/clusters/migrating_from_gma_to_project_template.md b/doc/user/clusters/migrating_from_gma_to_project_template.md
index 9a59d135fa0..ce39e13d928 100644
--- a/doc/user/clusters/migrating_from_gma_to_project_template.md
+++ b/doc/user/clusters/migrating_from_gma_to_project_template.md
@@ -4,7 +4,7 @@ group: Configure
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
-# Migrate from GitLab Managed Apps to Cluster Management Projects **(FREE)**
+# Migrate from GitLab Managed Apps to Cluster Management Projects (DEPRECATED) **(FREE)**
The GitLab Managed Apps were deprecated in GitLab 14.0
in favor of user-controlled Cluster Management projects.
diff --git a/doc/user/compliance/compliance_report/index.md b/doc/user/compliance/compliance_report/index.md
index 4cd2705e917..96cb3f3ef38 100644
--- a/doc/user/compliance/compliance_report/index.md
+++ b/doc/user/compliance/compliance_report/index.md
@@ -1,6 +1,6 @@
---
type: reference, howto
-stage: Manage
+stage: Govern
group: Compliance
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
---
@@ -31,7 +31,7 @@ Prerequisites:
To view the compliance report:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Security & Compliance > Compliance report**.
### Severity levels scale
@@ -105,7 +105,7 @@ Depending on the merge strategy, the merge commit SHA can be a merge commit, squ
To generate the Chain of Custody report:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Security & Compliance > Compliance report**.
1. Select **List of all merge commits**.
@@ -122,7 +122,7 @@ Authenticated group owners can generate a commit-specific Chain of Custody repor
- Using the GitLab UI:
- 1. On the top bar, select **Menu > Groups** and find your group.
+ 1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Security & Compliance > Compliance report**.
1. At the top of the compliance report, to the right of **List of all merge commits**, select the down arrow (**{chevron-lg-down}**).
1. Enter the merge commit SHA, and then select **Export commit custody report**.
diff --git a/doc/user/compliance/index.md b/doc/user/compliance/index.md
index 7b46886e236..c6c4834228b 100644
--- a/doc/user/compliance/index.md
+++ b/doc/user/compliance/index.md
@@ -1,6 +1,6 @@
---
type: reference
-stage: Manage
+stage: Govern
group: Compliance
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
---
diff --git a/doc/user/compliance/license_compliance/img/denied_licenses_v13_3.png b/doc/user/compliance/license_compliance/img/denied_licenses_v13_3.png
deleted file mode 100644
index aa3deb0c154..00000000000
--- a/doc/user/compliance/license_compliance/img/denied_licenses_v13_3.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/compliance/license_compliance/img/denied_licenses_v15_3.png b/doc/user/compliance/license_compliance/img/denied_licenses_v15_3.png
new file mode 100644
index 00000000000..4ed84047133
--- /dev/null
+++ b/doc/user/compliance/license_compliance/img/denied_licenses_v15_3.png
Binary files differ
diff --git a/doc/user/compliance/license_compliance/index.md b/doc/user/compliance/license_compliance/index.md
index 1c9f9e85ab7..19b01e4d854 100644
--- a/doc/user/compliance/license_compliance/index.md
+++ b/doc/user/compliance/license_compliance/index.md
@@ -736,7 +736,9 @@ Note, the merge request is not able to be merged until the `denied` license is r
You may add a [`License-Check` approval rule](#enabling-license-approvals-within-a-project),
which enables a designated approver that can approve and then merge a merge request with `denied` license.
-![Merge request with denied licenses](img/denied_licenses_v13_3.png)
+These policies can be configured by using the [Managed Licenses API](../../../api/managed_licenses.md).
+
+![Merge request with denied licenses](img/denied_licenses_v15_3.png)
The **Policies** tab in the project's license compliance section displays your project's license
policies. Project maintainers can specify policies in this section.
@@ -763,7 +765,7 @@ license.
You can enable `License-Check` one of two ways:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Merge request approvals**.
1. Select **Enable** or **Edit**.
@@ -793,6 +795,18 @@ We recommend that you use the most recent version of all containers, and the mos
## Troubleshooting
+### The License Compliance widget is stuck in a loading state
+
+A loading spinner is displayed in the following scenarios:
+
+- While the pipeline is in progress.
+- If the pipeline is complete, but still parsing the results in the background.
+- If the license scanning job is complete, but the pipeline is still running.
+
+The License Compliance widget polls every few seconds for updated results. When the pipeline is complete, the first poll after pipeline completion triggers the parsing of the results. This can take a few seconds depending on the size of the generated report.
+
+The final state is when a successful pipeline run has been completed, parsed, and the licenses displayed in the widget.
+
### ASDF_PYTHON_VERSION does not automatically install the version
Defining a non-latest Python version in ASDF_PYTHON_VERSION [doesn't have it automatically installed](https://gitlab.com/gitlab-org/gitlab/-/issues/325604). If your project requires a non-latest version of Python:
@@ -888,3 +902,17 @@ root@6abb70e9f193:~#
NOTE:
Selecting a custom version of [Mono](https://www.mono-project.com/) or [.NET Core](https://dotnet.microsoft.com/download/dotnet) is currently not supported.
+
+### LicenseFinder::Maven: is not installed error
+
+If your project contains a `mvnw` or `mvnw.cmd` file, then the license scanning job may fail with the `LicenseFinder::Maven: is not installed error` error. To resolve this, modify the license scanning job to remove the files in the `before_script` section. Example:
+
+```yaml
+include:
+ - template: License-Scanning.gitlab-ci.yml
+
+license_scanning:
+ before_script:
+ - rm mvnw
+ - rm mvnw.cmd
+```
diff --git a/doc/user/crm/index.md b/doc/user/crm/index.md
index e71a983ccfd..79f18e3abf1 100644
--- a/doc/user/crm/index.md
+++ b/doc/user/crm/index.md
@@ -1,6 +1,6 @@
---
stage: Plan
-group: Product Planning
+group: Certify
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
---
@@ -36,7 +36,7 @@ you must enable CRM features for the subgroup.
To enable customer relations management in a group or subgroup:
-1. On the top bar, select **Menu > Groups** and find your group or subgroup.
+1. On the top bar, select **Main menu > Groups** and find your group or subgroup.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Permissions and group features** section.
1. Select **Customer relations is enabled**.
@@ -48,7 +48,7 @@ To enable customer relations management in a group or subgroup:
To view a group's contacts:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Customer relations > Contacts**.
![Contacts list](crm_contacts_v14_10.png)
@@ -57,7 +57,7 @@ To view a group's contacts:
To create a contact:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Customer relations > Contacts**.
1. Select **New contact**.
1. Complete all required fields.
@@ -70,7 +70,7 @@ contacts using the GraphQL API.
To edit an existing contact:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Customer relations > Contacts**.
1. Next to the contact you wish to edit, select **Edit** (**{pencil}**).
1. Edit the required fields.
@@ -85,7 +85,7 @@ contacts using the GraphQL API.
To view a group's organizations:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Customer relations > Organizations**.
![Organizations list](crm_organizations_v14_10.png)
@@ -94,7 +94,7 @@ To view a group's organizations:
To create an organization:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Customer relations > Organizations**.
1. Select **New organization**.
1. Complete all required fields.
@@ -107,7 +107,7 @@ organizations using the GraphQL API.
To edit an existing organization:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Customer relations > Organizations**.
1. Next to the organization you wish to edit, select **Edit** (**{pencil}**).
1. Edit the required fields.
@@ -125,7 +125,7 @@ issues are linked to contacts matching the email addresses in the sender and CC
To view a contact's issues, select a contact from the issue sidebar, or:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Customer relations > Contacts**.
1. Next to the contact whose issues you wish to view, select **View issues** (**{issues}**).
@@ -133,7 +133,7 @@ To view a contact's issues, select a contact from the issue sidebar, or:
To view an organization's issues:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Customer relations > Organizations**.
1. Next to the organization whose issues you wish to view, select **View issues** (**{issues}**).
diff --git a/doc/user/discussions/img/create-new-issue_v15.png b/doc/user/discussions/img/create-new-issue_v15.png
deleted file mode 100644
index 779196b6ba4..00000000000
--- a/doc/user/discussions/img/create-new-issue_v15.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/discussions/img/create_new_issue_v15_4.png b/doc/user/discussions/img/create_new_issue_v15_4.png
new file mode 100644
index 00000000000..3720b601cc5
--- /dev/null
+++ b/doc/user/discussions/img/create_new_issue_v15_4.png
Binary files differ
diff --git a/doc/user/discussions/img/unresolved_threads_v15.png b/doc/user/discussions/img/unresolved_threads_v15.png
deleted file mode 100644
index 113af20effc..00000000000
--- a/doc/user/discussions/img/unresolved_threads_v15.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/discussions/img/unresolved_threads_v15_4.png b/doc/user/discussions/img/unresolved_threads_v15_4.png
new file mode 100644
index 00000000000..1d1669de0f1
--- /dev/null
+++ b/doc/user/discussions/img/unresolved_threads_v15_4.png
Binary files differ
diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md
index 3fb0be6480c..ed0bcec9f49 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -91,7 +91,7 @@ For example, `28719b171a056960dfdc0012b625d0b47b123196` becomes
You can add comments and threads to a particular commit.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Repository > Commits**.
1. Below the commits, in the **Comment** field, enter a comment.
1. Select **Comment** or select the down arrow (**{chevron-down}**) to select **Start thread**.
@@ -309,15 +309,15 @@ To resolve a thread:
At the top of the page, the number of unresolved threads is updated:
-![Count of unresolved threads](img/unresolved_threads_v15.png)
+![Count of unresolved threads](img/unresolved_threads_v15_4.png)
### Move all unresolved threads in a merge request to an issue
If you have multiple unresolved threads in a merge request, you can
create an issue to resolve them separately. In the merge request, at the top of the page,
-select **Create issue to resolve all threads** (**{issue-new}**):
+click the ellipsis icon button (**{ellipsis_v}**) in the threads control and then select **Create issue to resolve all threads**:
-![Open new issue for all unresolved threads](img/create-new-issue_v15.png)
+![Open new issue for all unresolved threads](img/create_new_issue_v15_4.png)
All threads are marked as resolved, and a link is added from the merge request to
the newly created issue.
@@ -339,10 +339,9 @@ You can prevent merge requests from being merged until all threads are
resolved. When this setting is enabled, the **Unresolved threads** counter in a merge request
is shown in orange when at least one thread remains unresolved.
-1. On the top bar, select **Menu > Projects** and find your project.
-1. On the left sidebar, select **Settings > General**.
-1. Expand **Merge requests**.
-1. Under **Merge checks**, select the **All threads must be resolved** checkbox.
+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 checks** section, select the **All threads must be resolved** checkbox.
1. Select **Save changes**.
### Automatically resolve threads in a merge request when they become outdated
@@ -350,11 +349,10 @@ is shown in orange when at least one thread remains unresolved.
You can set merge requests to automatically resolve threads when lines are modified
with a new push.
-1. On the top bar, select **Menu > Projects** and find your project.
-1. On the left sidebar, select **Settings > General**.
-1. Expand **Merge requests**.
-1. Under **Merge options**, select the
- **Automatically resolve merge request diff threads when they become outdated** checkbox.
+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 options** section, select
+ **Automatically resolve merge request diff threads when they become outdated**.
1. Select **Save changes**.
Threads are now resolved if a push makes a diff section outdated.
diff --git a/doc/user/free_user_limit.md b/doc/user/free_user_limit.md
index a62b9c9e363..60091449256 100644
--- a/doc/user/free_user_limit.md
+++ b/doc/user/free_user_limit.md
@@ -8,10 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
From October 19, 2022, namespaces in GitLab.com on the Free tier
will be limited to five (5) members per [namespace](namespace/index.md).
-This limit applies to top-level groups and personal namespaces.
-
-In a personal namespace, the limit applies across all projects in your personal
-namespace.
+This limit applies to top-level private groups.
On the transition date, if your namespace has six or more unique members:
@@ -40,17 +37,11 @@ Prerequisite:
- You must have the Owner role for the group.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups > View all groups** and find your group.
1. On the left sidebar, select **Settings > Usage Quotas**.
1. To view all members, select the **Seats** tab.
1. To remove a member, select **Remove user**.
-NOTE:
-The **Usage Quotas** page is not available for personal namespaces. You can
-view and [remove members](project/members/index.md#remove-a-member-from-a-project)
-in each project instead. The five user limit includes all
-unique members across all projects in your personal namespace.
-
If you need more time to manage your members, or to try GitLab features
with a team of more than five members, you can [start a trial](https://about.gitlab.com/free-trial/).
A trial lasts for 30 days and includes an unlimited number of members.
diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index 53e459f7a09..62b685ea4f4 100644
--- a/doc/user/gitlab_com/index.md
+++ b/doc/user/gitlab_com/index.md
@@ -208,9 +208,9 @@ from those IPs and allow them.
GitLab.com is fronted by Cloudflare. For incoming connections to GitLab.com, you might need to allow CIDR blocks of Cloudflare ([IPv4](https://www.cloudflare.com/ips-v4/) and [IPv6](https://www.cloudflare.com/ips-v6/)).
-For outgoing connections from CI/CD runners, we are not providing static IP
-addresses. All GitLab.com shared runners are deployed into Google Cloud Platform (GCP). Any
-IP-based firewall can be configured by looking up all
+For outgoing connections from CI/CD runners, we are not providing static IP addresses.
+All GitLab.com shared runners are deployed into Google Cloud Platform (GCP) in `us-east1`.
+Any IP-based firewall can be configured by looking up
[IP address ranges or CIDR blocks for GCP](https://cloud.google.com/compute/docs/faq#find_ip_range).
## Hostname list
@@ -328,6 +328,12 @@ for `shared_buffers` is quite high, and we are
GitLab.com uses the default of 60 seconds for [Puma request timeouts](../../administration/operations/puma.md#change-the-worker-timeout).
+## Merge request reviewer maximum
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91406) in GitLab 15.3.
+
+A maximum of 100 reviewers can be assigned to a merge request.
+
## GitLab.com-specific rate limits
NOTE:
@@ -336,7 +342,7 @@ documentation.
When a request is rate limited, GitLab responds with a `429` status
code. The client should wait before attempting the request again. There
-are also informational headers with this response detailed in
+are also informational headers with this response detailed in
[rate limiting responses](#rate-limiting-responses).
The following table describes the rate limits for GitLab.com, both before and
@@ -358,8 +364,8 @@ after the limits change in January, 2021:
| **Pipeline creation** requests (for a given **project, user, and commit**) | | **25** requests per minute |
| **Alert integration endpoint** requests (for a given **project**) | | **3600** requests per hour |
-More details are available on the rate limits for
-[protected paths](#protected-paths-throttle) and
+More details are available on the rate limits for
+[protected paths](#protected-paths-throttle) and
[raw endpoints](../../user/admin_area/settings/rate_limits_on_raw_endpoints.md).
GitLab can rate-limit requests at several layers. The rate limits listed here
diff --git a/doc/user/group/access_and_permissions.md b/doc/user/group/access_and_permissions.md
index c469d6c2f6d..bdef13af3f9 100644
--- a/doc/user/group/access_and_permissions.md
+++ b/doc/user/group/access_and_permissions.md
@@ -12,13 +12,21 @@ Configure your groups to control group permissions and access.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34370) in GitLab 12.8.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/224129) in GitLab 13.4.
+> - [Moved to Settings/Repository](https://gitlab.com/gitlab-org/gitlab/-/issues/220365) in GitLab 15.4.
Group push rules allow group maintainers to set
[push rules](../project/repository/push_rules.md) for newly created projects in the specific group.
-To configure push rules for a group:
+In GitLab 15.4 and later, to configure push rules for a group:
-1. Go to the groups's **Push Rules** page.
+1. On the left sidebar, select **Push rules**.
+1. Select the settings you want.
+1. Select **Save Push Rules**.
+
+In GitLab 15.3 and earlier, to configure push rules for a group:
+
+1. On the left sidebar, select **Settings > Repository** page.
+1. Expand the **Pre-defined push rules** section.
1. Select the settings you want.
1. Select **Save Push Rules**.
@@ -27,6 +35,27 @@ The group's new subgroups have push rules set for them based on either:
- The closest parent group with push rules defined.
- Push rules set at the instance level, if no parent groups have push rules defined.
+## Restrict Git access protocols
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/365601) in GitLab 15.1 [with a flag](../../administration/feature_flags.md) named `group_level_git_protocol_control`. 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 `group_level_git_protocol_control`. On GitLab.com,
+this feature is available.
+
+You can set the permitted protocols used to access a group's repositories to either SSH, HTTPS, or both. This setting
+is disabled when the [instance setting](../admin_area/settings/visibility_and_access_controls.md#configure-enabled-git-access-protocols) is
+configured by an administrator.
+
+To change the permitted Git access protocols for a group:
+
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > General**.
+1. Expand the **Permissions and group features** section.
+1. Choose the permitted protocols from **Enabled Git access protocols**.
+1. Select **Save changes**.
+
## Restrict group access by IP address **(PREMIUM)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1985) in GitLab 12.0.
@@ -43,8 +72,6 @@ applies to:
You should consider some security implications before configuring IP address restrictions.
-- Restricting HTTP traffic on GitLab.com with IP address restrictions causes SSH requests (including Git operations over
- SSH) to fail. For more information, see [the relevant issue](https://gitlab.com/gitlab-org/gitlab/-/issues/271673).
- Administrators and group owners can access group settings from any IP address, regardless of IP restriction. However:
- Groups owners cannot access projects belonging to the group when accessing from a disallowed IP address.
- Administrators can access projects belonging to the group when accessing from a disallowed IP address.
@@ -57,14 +84,17 @@ You should consider some security implications before configuring IP address res
restricted IP address, the IP restriction prevents code from being cloned.
- Users may still see some events from the IP restricted groups and projects on their dashboard. Activity may include
push, merge, issue, or comment events.
+- IP access restrictions for Git operations via SSH are supported only on GitLab SaaS.
+ IP access restrictions applied to self-managed instances block SSH completely.
### Restrict group access by IP address
To restrict group access by IP address:
-1. Go to the group's **Settings > General** page.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > General**.
1. Expand the **Permissions and group features** section.
-1. In the **Allow access to the following IP addresses** field, enter IPv4 or IPv6 address ranges in CIDR notation.
+1. In the **Restrict access by IP address** field, enter IPv4 or IPv6 address ranges in CIDR notation.
1. Select **Save changes**.
In self-managed installations of GitLab 15.1 and later, you can also configure
@@ -81,7 +111,8 @@ You can prevent users with email addresses in specific domains from being added
To restrict group access by domain:
-1. Go to the group's **Settings > General** page.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > General**.
1. Expand the **Permissions and group features** section.
1. In the **Restrict membership by email** field, enter the domain names.
1. Select **Save changes**.
@@ -124,23 +155,24 @@ If you prevent group sharing outside the hierarchy for the **Animals** group:
To prevent sharing outside of the group's hierarchy:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Permissions and group features**.
-1. Select **Prevent members from sending invitations to groups outside of `<group_name>` and its subgroups**.
+1. Select **Members cannot invite groups outside of `<group_name>` and its subgroups**.
1. Select **Save changes**.
## Prevent a project from being shared with groups
-Prevent projects in a group from
-[sharing a project with another group](../project/members/share_project_with_groups.md)
+Prevent projects in a group from
+[sharing a project with another group](../project/members/share_project_with_groups.md)
to enable tighter control over project access.
To prevent a project from being shared with other groups:
-1. Go to the group's **Settings > General** page.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > General**.
1. Expand the **Permissions and group features** section.
-1. Select **Prevent sharing a project in `<group_name>` with other groups**.
+1. Select **Projects in `<group_name>` cannot be shared with other groups**.
1. Select **Save changes**.
This setting applies to all subgroups unless overridden by a group owner. Groups already
@@ -151,7 +183,7 @@ added to a project lose access when the setting is enabled.
As a group owner, you can prevent non-members from requesting access to
your group.
-1. On the top bar, select **Menu > Groups**.
+1. On the top bar, **Main menu > Groups** and find your group.
1. Select **Your Groups**.
1. Find the group and select it.
1. From the left menu, select **Settings > General**.
@@ -173,7 +205,8 @@ If even one is set to `true`, then the group does not allow outside forks.
To prevent projects from being forked outside the group:
-1. Go to the top-level group's **Settings > General** page.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > General**.
1. Expand the **Permissions and group features** section.
1. Check **Prevent project forking outside current group**.
1. Select **Save changes**.
@@ -194,9 +227,9 @@ The setting does not cascade. Projects in subgroups observe the subgroup configu
To prevent members from being added to projects in a group:
-1. Go to the group's **Settings > General** page.
-1. Expand the **Permissions and group features** section.
-1. Under **Membership**, select **Prevent adding new members to projects within this group**.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > General**.
+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.
@@ -241,7 +274,8 @@ To create group links via filter:
LDAP user permissions can be manually overridden by an administrator. To override a user's permissions:
-1. Go to your group's **Group information > Members** page.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Group information > Members**.
1. In the row for the user you are editing, select the pencil (**{pencil}**) icon.
1. Select **Edit permissions** in the modal.
diff --git a/doc/user/group/clusters/index.md b/doc/user/group/clusters/index.md
index 0da7eaa4d55..17a5551adbf 100644
--- a/doc/user/group/clusters/index.md
+++ b/doc/user/group/clusters/index.md
@@ -21,7 +21,7 @@ your group, enabling you to use the same cluster across multiple projects.
To view your group-level Kubernetes clusters:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Kubernetes**.
## Cluster management project
@@ -89,7 +89,7 @@ your cluster, which can cause deployment jobs to fail.
To clear the cache:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Kubernetes**.
1. Select your cluster.
1. Expand **Advanced settings**.
diff --git a/doc/user/group/contribution_analytics/index.md b/doc/user/group/contribution_analytics/index.md
index ba805d6e1bb..7750782a623 100644
--- a/doc/user/group/contribution_analytics/index.md
+++ b/doc/user/group/contribution_analytics/index.md
@@ -19,7 +19,7 @@ group.
To view Contribution Analytics:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Contribution**.
## Using Contribution Analytics
diff --git a/doc/user/group/devops_adoption/index.md b/doc/user/group/devops_adoption/index.md
index 333bef84dcc..bb18c69f7ae 100644
--- a/doc/user/group/devops_adoption/index.md
+++ b/doc/user/group/devops_adoption/index.md
@@ -36,7 +36,7 @@ Prerequisite:
To view DevOps Adoption:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > DevOps adoption**
## DevOps Adoption categories
@@ -80,7 +80,7 @@ twelve months. The chart only shows data from when you enabled DevOps Adoption f
To view feature adoption over time:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > DevOps adoption**.
1. Select the **Overview** tab.
diff --git a/doc/user/group/epics/epic_boards.md b/doc/user/group/epics/epic_boards.md
index f03a56d8a00..a8bbb0575b3 100644
--- a/doc/user/group/epics/epic_boards.md
+++ b/doc/user/group/epics/epic_boards.md
@@ -15,7 +15,7 @@ labels.
To view an epic board:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Epics > Boards**.
![GitLab epic board - Premium](img/epic_board_v14_1.png)
@@ -28,7 +28,7 @@ Prerequisites:
To create a new epic board:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Epics > Boards**.
1. In the upper left corner, select the dropdown with the current board name.
1. Select **Create new board**.
@@ -77,7 +77,7 @@ Prerequisites:
To create a new list:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Epics > Boards**.
1. In the upper-right corner, select **Create list**.
1. In the **New list** column expand the **Select a label** dropdown and select the label to use as
@@ -155,6 +155,42 @@ into another list. Learn about possible effects in [Dragging epics between lists
To move a list, select its top bar, and drag it horizontally.
You can't move the **Open** and **Closed** lists, but you can hide them when editing an epic board.
+#### Move an epic to the start of the list
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367473) in GitLab 15.4.
+
+When you have many epics, it's inconvenient to manually drag an epic from the bottom of a board list all
+the way to the top. You can move epics to the top of the list with a menu shortcut.
+
+Your epic is moved to the top of the list even if other epics are hidden by a filter.
+
+Prerequisites:
+
+- You must at least have the Reporter role for a group.
+
+To move an epic to the start of the list:
+
+1. In an epic board, hover over the card of the epic you want to move.
+1. Select the vertical ellipsis (**{ellipsis_v}**), then **Move to start of list**.
+
+#### Move an epic to the end of the list
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367473) in GitLab 15.4.
+
+When you have many epics, it's inconvenient to manually drag an epic from the top of a board list all
+the way to the bottom. You can move epics to the bottom of the list with a menu shortcut.
+
+Your epic is moved to the bottom of the list even if other epics are hidden by a filter.
+
+Prerequisites:
+
+- You must at least have the Reporter role for a group.
+
+To move an epic to the end of the list:
+
+1. In an epic board, hover over the card of the epic you want to move.
+1. Select the vertical ellipsis (**{ellipsis_v}**), then **Move to end of list**.
+
#### Dragging epics between lists
When you drag epics between lists, the result is different depending on the source list
diff --git a/doc/user/group/epics/manage_epics.md b/doc/user/group/epics/manage_epics.md
index 71d7b7fbb0c..26826ec5832 100644
--- a/doc/user/group/epics/manage_epics.md
+++ b/doc/user/group/epics/manage_epics.md
@@ -210,7 +210,7 @@ Prerequisites:
To view epics in a group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Epics**.
### Cached epic count
@@ -244,7 +244,7 @@ You can filter the list of epics by:
To filter:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Epics**.
1. Select the field **Search or filter results**.
1. From the dropdown menu, select the scope or enter plain text to search by epic title or description.
diff --git a/doc/user/group/import/index.md b/doc/user/group/import/index.md
index edf4d7677df..21b389581ae 100644
--- a/doc/user/group/import/index.md
+++ b/doc/user/group/import/index.md
@@ -88,6 +88,7 @@ migrated:
- Board Lists
- Boards
- Epics ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/250281) in 13.7)
+ - Epic resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
- Finisher
- Group Labels ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292429) in 13.9)
- Iterations ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/292428) in 13.10)
@@ -116,10 +117,17 @@ On self-managed GitLab, migrating project resources are not available by default
- 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)
- Issues ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/267946) in GitLab 14.4)
+ - Issue resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
+ - Issue resource milestone events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.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)
- Merge Requests ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339403) in GitLab 14.5)
+ - Multiple merge request assignees ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3)
+ - Merge request reviewers ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3)
+ - Merge request approvers ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3)
+ - 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)
- 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)
- Pipeline History ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339412) in GitLab 14.6)
@@ -167,3 +175,17 @@ 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`
+
+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:
+
+- The GitLab UI:
+
+ 1. On the top bar, select **Main menu > Groups** and find your group.
+ 1. On the left sidebar, select **Settings > General**.
+ 1. Expand **Advanced**.
+ 1. Under **Change group URL**, change the group URL to include non-numeric characters.
+
+- The [Groups API](../../../api/groups.md#update-group).
diff --git a/doc/user/group/insights/index.md b/doc/user/group/insights/index.md
index b3fdeb0ab41..0cde0f1f133 100644
--- a/doc/user/group/insights/index.md
+++ b/doc/user/group/insights/index.md
@@ -1,51 +1,48 @@
---
-type: reference, howto
stage: Manage
group: Optimize
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
---
-# Insights **(ULTIMATE)**
+# Insights for groups **(ULTIMATE)**
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/725) in GitLab 12.0.
-Configure the Insights that matter for your groups. Explore data such as
-triage hygiene, issues created or closed for a given period, average time for merge
-requests to be merged, and much more.
-
-![Insights example stacked bar chart](img/insights_example_stacked_bar_chart_v13_11.png)
+Configure Insights to explore data about you group's activity, such as
+triage hygiene, issues created or closed in a given period, and average time for merge
+requests to be merged.
## View your group's Insights
+Prerequisites:
+
+- You must have [permission](../../permissions.md#group-members-permissions) to view the group.
+- You must have access to a project to view information about its merge requests and issues,
+ and permission to view them if they are confidential.
+
To access your group's Insights:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Insights**.
+![Insights example stacked bar chart](img/insights_example_stacked_bar_chart_v13_11.png)
+
## Configure your Insights
-GitLab reads Insights from the [default configuration file](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/fixtures/insights/default.yml).
-If you want to customize it:
+GitLab reads Insights from the
+[default configuration file](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/fixtures/insights/default.yml).
+You can also create custom Insights charts that are more relevant for your group.
-1. Create a new file [`.gitlab/insights.yml`](../../project/insights/index.md)
+To customize your Insights:
+
+1. Create a new file [`.gitlab/insights.yml`](../../project/insights/index.md#writing-your-gitlabinsightsyml)
in a project that belongs to your group.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Insights**.
1. Select the project that contains your `.gitlab/insights.yml` configuration file.
1. Select **Save changes**.
-## Permissions
-
-If you have access to view a group, then you have access to view its Insights.
-
-NOTE:
-Issues or merge requests that you don't have access to (because you don't have
-access to the project they belong to, or because they are confidential) are
-filtered out of the Insights charts.
-
-You may also consult the [group permissions table](../../permissions.md#group-members-permissions).
-
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
diff --git a/doc/user/group/issues_analytics/index.md b/doc/user/group/issues_analytics/index.md
index 62337dabcc0..26d77edd89b 100644
--- a/doc/user/group/issues_analytics/index.md
+++ b/doc/user/group/issues_analytics/index.md
@@ -15,7 +15,7 @@ prior.
To access the chart:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Issue Analytics**.
Hover over each bar to see the total number of issues.
diff --git a/doc/user/group/iterations/index.md b/doc/user/group/iterations/index.md
index 530635802a6..4cc879ef32d 100644
--- a/doc/user/group/iterations/index.md
+++ b/doc/user/group/iterations/index.md
@@ -12,11 +12,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - Moved to GitLab Premium in 13.9.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/221047) in GitLab 14.6. [Feature flag `group_iterations`](https://gitlab.com/gitlab-org/gitlab/-/issues/221047) removed.
-WARNING:
-After [Iteration Cadences](#iteration-cadences) becomes generally available,
-manual iteration scheduling will be [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/356069) in GitLab 15.6.
-To enhance the role of iterations as time boundaries, we will also deprecate the title field.
-
Iterations are a way to track issues over a period of time. This allows teams
to track velocity and volatility metrics. Iterations can be used with [milestones](../../project/milestones/index.md)
for tracking over different time periods.
@@ -37,7 +32,7 @@ In GitLab, iterations are similar to milestones, with a few differences:
To view the iterations list:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. Select **Issues > Iterations**.
To view all the iterations in a cadence, ordered by descending date, select that iteration cadence.
@@ -66,7 +61,7 @@ Prerequisites:
To create an iteration:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Issues > Iterations**.
1. Select **New iteration**.
1. Enter the title, a description (optional), a start date, and a due date.
@@ -174,7 +169,7 @@ and get a more accurate understanding of scope attributable to each label.
To group issues by label:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Issues > Iterations**.
1. In the **Group by** dropdown, select **Label**.
1. Select the **Filter by label** dropdown.
@@ -187,7 +182,7 @@ To group issues by label:
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5077) in GitLab 14.1 [with a flag](../../../administration/feature_flags.md), named `iteration_cadences`. Disabled by default.
> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/354977) in GitLab 15.0: All scheduled iterations must start on the same day of the week as the cadence start day. Start date of cadence cannot be edited after the first iteration starts.
> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/354878) in GitLab 15.0.
-> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/367493) in GitLab 15.3: A new automation start date can be selected for cadence. Upcoming iterations will be scheduled to start on the same day of the week as the changed start date.
+> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/367493) in GitLab 15.4: A new automation start date can be selected for cadence. Upcoming iterations will be scheduled to start on the same day of the week as the changed start date. Iteration cadences can be manually managed by turning off the automatic scheduling feature.
Iteration cadences automate iteration scheduling. You can use them to
automate creating iterations every 1, 2, 3, or 4 weeks. You can also
@@ -203,11 +198,12 @@ Prerequisites:
To create an iteration cadence:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Issues > Iterations**.
1. Select **New iteration cadence**.
-1. Complete the fields.
- - Enter the title and description of the iteration cadence.
+1. Enter the title and description of the iteration cadence.
+1. To manually manage the iteration cadence, clear the **Enable automatic scheduling** checkbox and skip the next step.
+1. Complete the required fields to use automatic scheduling.
- Select the automation start date of the iteration cadence. Iterations will be scheduled to
begin on the same day of the week as the day of the week of the start date.
- From the **Duration** dropdown list, select how many weeks each iteration should last.
@@ -224,11 +220,11 @@ Prerequisites:
To edit an iteration cadence:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Issues > Iterations**.
1. Select **Edit iteration cadence**.
-When you edit the **Automation start date** field,
+When you are using automatic scheduling and edit the **Automation start date** field,
you must set a new start date that doesn't overlap with the existing
current or past iterations.
@@ -236,52 +232,23 @@ Editing **Upcoming iterations** is a non-destructive action.
If ten upcoming iterations already exist, changing the number under **Upcoming iterations** to `2`
doesn't delete the eight existing upcoming iterations.
-### Delete an iteration cadence
-
-> [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/343889) the minimum user role from Developer to Reporter in GitLab 15.0.
-
-Prerequisites:
-
-- You must have at least the Reporter role for a group.
-
-Deleting an iteration cadence also deletes all iterations within that cadence.
-
-To delete an iteration cadence:
-
-1. On the top bar, select **Menu > Groups** and find your group.
-1. On the left sidebar, select **Issues > Iterations**.
-1. Select the three-dot menu (**{ellipsis_v}**) > **Delete cadence** for the cadence you want to delete.
-1. Select **Delete cadence** in the confirmation modal.
-
-### Manual iteration cadences
-
-When you **enable** the iteration cadences feature, all previously
-created iterations are added to a default iteration cadence.
-You can continue to add, edit, and remove iterations in
-this default cadence.
-
-#### Convert a manual cadence to use automatic scheduling
-
-WARNING:
-The upgrade is irreversible. After it's done, a new manual iteration cadence cannot be created.
-
-Prerequisites:
+#### Turn on automatic scheduling for manual iterations cadence
-- You must have created [iterations](#iterations) without cadences before enabling iteration cadences for your group.
-To upgrade the iteration cadence to use the automation features:
-
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Issues > Iterations**.
-1. Select the three-dot menu (**{ellipsis_v}**) > **Edit cadence** for the cadence you want to upgrade.
+1. Select the three-dot menu (**{ellipsis_v}**) > **Edit cadence** for the cadence for which you want to enable automatic scheduling.
+1. Check the **Enable automatic scheduling** checkbox.
1. Complete the required fields **Duration**, **Upcoming iterations**, and **Automation start date**.
For **Automation start date**, you can select any date that doesn't overlap with the existing open iterations.
If you have upcoming iterations, the automatic scheduling adjusts them appropriately to fit
-your chosen duration.
+your chosen duration.
1. Select **Save changes**.
-#### Converted cadences example
+When you want to manage your iterations cadence manually again, edit your cadence and uncheck the **Enable automatic scheduling** checkbox.
+
+#### Example of turning on automatic scheduling for manual iterations cadence
-For example, suppose it's Friday, April 15, and you have three iterations in a manual cadence:
+Suppose it's Friday, April 15, and you have three iteration in a manual iterations cadence:
- Monday, April 4 - Friday, April 8 (closed)
- Tuesday, April 12 - Friday, April 15 (ongoing)
@@ -300,8 +267,25 @@ after the conversion you have the following iterations:
- Monday, April 18 - Sunday, April 24 (upcoming)
- Monday, April 25 - Sunday, May 1 (upcoming)
-Your existing upcoming iteration "Tuesday, April 12 - Friday, April 15"
+Your existing upcoming iteration "Tuesday, April 12 - Friday, April 15"
is changed to "April 18 - Sunday, April 24".
An additional upcoming iteration "April 25 - Sunday, May 1" is scheduled
to satisfy the requirement that there are at least two upcoming iterations scheduled.
+
+### Delete an iteration cadence
+
+> [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/343889) the minimum user role from Developer to Reporter in GitLab 15.0.
+
+Prerequisites:
+
+- You must have at least the Reporter role for a group.
+
+Deleting an iteration cadence also deletes all iterations within that cadence.
+
+To delete an iteration cadence:
+
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Issues > Iterations**.
+1. Select the three-dot menu (**{ellipsis_v}**) > **Delete cadence** for the cadence you want to delete.
+1. Select **Delete cadence**.
diff --git a/doc/user/group/manage.md b/doc/user/group/manage.md
index a9cc6cc8432..ca9ff0d7b3e 100644
--- a/doc/user/group/manage.md
+++ b/doc/user/group/manage.md
@@ -10,8 +10,7 @@ Use groups to manage one or more related projects at the same time.
## View groups
-1. On the top bar, select **Menu > Groups**.
-1. Select **Explore groups**.
+To view groups, on the top bar, select **Main menu > Groups > View all groups**.
The **Groups** page shows a list of groups, sorted by last updated date.
@@ -25,7 +24,7 @@ The **Groups** page shows a list of groups, sorted by last updated date.
To create a group:
1. On the top bar, either:
- - Select **Menu > Groups**, and on the right, select **Create group**.
+ - Select **Main menu > Groups > View all groups**, and on the right, select **New group**.
- To the left of the search box, select the plus sign and then **New group**.
1. Select **Create group**.
1. Enter a name for the group in **Group name**. For a list of words that cannot be used as group names, see
@@ -45,7 +44,7 @@ For details about groups, watch [GitLab Namespaces (users, groups and subgroups)
To remove a group and its contents:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Advanced** section.
1. In the **Remove group** section, select **Remove group**.
@@ -54,7 +53,7 @@ To remove a group and its contents:
A group can also be removed from the groups dashboard:
-1. On the top bar, select **Menu > Groups**.
+1. On the top bar, select **Main menu > Groups > View all groups**.
1. Select **Your Groups**.
1. Select (**{ellipsis_v}**) for the group you want to delete.
1. Select **Delete**.
@@ -83,7 +82,7 @@ Prerequisites:
To immediately remove a group marked for deletion:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Advanced**.
1. In the "Permanently remove group" section, select **Remove group**.
@@ -98,7 +97,8 @@ are deleted.
To restore a group that is marked for deletion:
-1. Go to your group's **Settings > General** page.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. Select **Settings > General**.
1. Expand the **Path, transfer, remove** section.
1. In the Restore group section, select **Restore group**.
@@ -106,7 +106,7 @@ To restore a group that is marked for deletion:
As a user, you can request to be a member of a group, if an administrator allows it.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. Under the group name, select **Request Access**.
As many as ten of the most-recently-active group owners receive an email with your request.
@@ -127,7 +127,7 @@ To find members in a group, you can sort, filter, or search.
Filter a group to find members. By default, all members in the group and subgroups are displayed.
-1. Go to the group and select **Group information > Members**.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. Above the list of members, in the **Filter members** box, enter filter criteria.
- To view members in the group only, select **Membership = Direct**.
- To view members of the group and its subgroups, select **Membership = Inherited**.
@@ -138,7 +138,8 @@ Filter a group to find members. By default, all members in the group and subgrou
You can search for members by name, username, or email.
-1. Go to the group and select **Group information > Members**.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Group information > Members**.
1. Above the list of members, in the **Filter members** box, enter search criteria.
1. To the right of the **Filter members** box, select the magnifying glass (**{search}**).
@@ -146,7 +147,8 @@ You can search for members by name, username, or email.
You can sort members by **Account**, **Access granted**, **Max role**, or **Last sign-in**.
-1. Go to the group and select **Group information > Members**.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Group information > Members**.
1. Above the list of members, on the top right, from the **Account** list, select
the criteria to filter by.
1. To switch the sort between ascending and descending, to the right of the **Account** list, select the
@@ -156,7 +158,7 @@ You can sort members by **Account**, **Access granted**, **Max role**, or **Last
You can give a user access to all projects in a group.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Group information > Members**.
1. Select **Invite members**.
1. Fill in the fields.
@@ -182,11 +184,12 @@ Prerequisites:
To remove a member from a group:
-1. Go to the group.
-1. From the left menu, select **Group information > Members**.
-1. Next to the member you want to remove, select **Delete**.
-1. Optional. On the **Remove member** confirmation box, select the
- **Also unassign this user from linked issues and merge requests** checkbox.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Group information > Members**.
+1. Next to the member you want to remove, select **Remove member**.
+1. Optional. On the **Remove member** confirmation box:
+ - To remove direct user membership from subgroups and projects, select the **Also remove direct user membership from subgroups and projects** checkbox.
+ - To unassign the user from linked issues and merge requests, select the **Also unassign this user from linked issues and merge requests** checkbox.
1. Select **Remove member**.
## Add projects to a group
@@ -207,12 +210,12 @@ By default, users with at least the Developer role can create projects under a g
To change this setting for a specific group:
-1. On the top bar, select **Menu > Groups**.
+1. On the top bar, select **Main menu > Groups > View all groups**.
1. Select **Your Groups**.
1. Find the group and select it.
1. From the left menu, select **Settings > General**.
1. Expand the **Permissions and group features** section.
-1. Select the desired option in the **Allowed to create projects** dropdown list.
+1. Select the desired option in the **Roles allowed to create projects** dropdown list.
1. Select **Save changes**.
To change this setting globally, see [Default project creation protection](../admin_area/settings/visibility_and_access_controls.md#define-which-roles-can-create-projects).
@@ -223,11 +226,13 @@ You can change the owner of a group. Each group must always have at least one
member with the Owner role.
- As an administrator:
- 1. Go to the group and from the left menu, select **Group information > Members**.
+ 1. On the top bar, select **Main menu > Groups** and find your group.
+ 1. On the left sidebar, select **Group information > Members**.
1. Give a different member the **Owner** role.
1. Refresh the page. You can now remove the **Owner** role from the original owner.
- As the current group's owner:
- 1. Go to the group and from the left menu, select **Group information > Members**.
+ 1. On the top bar, select **Main menu > Groups** and find your group.
+ 1. On the left sidebar, select **Group information > Members**.
1. Give a different member the **Owner** role.
1. Have the new owner sign in and remove the **Owner** role from you.
@@ -246,7 +251,8 @@ create a new group and transfer projects to it instead.
To change your group path (group URL):
-1. Go to your group's **Settings > General** page.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > General** page.
1. Expand the **Advanced** section.
1. Under **Change group URL**, enter a new name.
1. Select **Change group URL**.
@@ -326,7 +332,7 @@ When transferring groups, note:
To transfer a group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Advanced** section.
1. In the **Remove group** section, select **Transfer group**.
@@ -342,7 +348,7 @@ To transfer a group:
> - [User interface changed](https://gitlab.com/gitlab-org/gitlab/-/issues/352961) in GitLab 15.1.
[Delayed project deletion](../project/settings/index.md#delayed-project-deletion) is locked and disabled unless the instance-level settings for
-[deletion protection](../admin_area/settings/visibility_and_access_controls.md#deletion-protection) is enabled for either groups only or groups and projects.
+[deletion protection](../admin_area/settings/visibility_and_access_controls.md#deletion-protection) are enabled for either groups only or groups and projects.
When enabled on groups, projects in the group are deleted after a period of delay. During this period, projects are in a read-only state and can be restored.
The default period is seven days but [is configurable at the instance level](../admin_area/settings/visibility_and_access_controls.md#retention-period).
@@ -356,7 +362,8 @@ the default setting.
To enable delayed deletion of projects in a group:
-1. Go to the group's **Settings > General** page.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > General**.
1. Expand the **Permissions and group features** section.
1. Scroll to:
- (GitLab 15.1 and later) **Deletion protection** and select **Keep deleted projects**.
@@ -377,9 +384,10 @@ You can disable all email notifications related to the group, which includes its
To disable email notifications:
-1. Go to the group's **Settings > General** page.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > General**.
1. Expand the **Permissions and group features** section.
-1. Select **Disable email notifications**.
+1. Select **Email notifications are disabled**.
1. Select **Save changes**.
## Disable group mentions
@@ -396,9 +404,10 @@ This is particularly helpful for groups with a large number of users.
To disable group mentions:
-1. Go to the group's **Settings > General** page.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > General**.
1. Expand the **Permissions and group features** section.
-1. Select **Disable group mentions**.
+1. Select **Group mentions are disabled**.
1. Select **Save changes**.
## Export members as CSV **(PREMIUM)**
@@ -408,7 +417,8 @@ To disable group mentions:
You can export a list of members in a group or subgroup as a CSV.
-1. Go to your group or subgroup and select either **Group information > Members** or **Subgroup information > Members**.
+1. On the top bar, select **Main menu > Groups** and find your group or subgroup.
+1. On the left sidebar, select either **Group information > Members** or **Subgroup information > Members**.
1. Select **Export as CSV**.
1. After the CSV file has been generated, it is emailed as an attachment to the user that requested it.
@@ -434,7 +444,7 @@ Prerequisite:
To specify a user cap:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
You can set a cap on the top-level group only.
1. On the left sidebar, select **Settings > General**.
1. Expand **Permissions and group features**.
@@ -456,7 +466,7 @@ Prerequisite:
To remove the user cap:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Permissions and group features**.
1. In the **User cap** box, delete the value.
@@ -477,7 +487,7 @@ Prerequisite:
To approve members that are pending because they've exceeded the user cap:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > Usage Quotas**.
1. On the **Seats** tab, under the alert, select **View pending approvals**.
1. For each member you want to approve, select **Approve**.
@@ -508,7 +518,8 @@ Define project templates at a group level by setting a group as the template sou
To enable group file templates:
-1. Go to the group's **Settings > General** page.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > General**.
1. Expand the **Templates** section.
1. Choose a project to act as the template repository.
1. Select **Save changes**.
@@ -525,7 +536,8 @@ that belong to the group.
To view the merge request approval settings for a group:
-1. Go to the top-level group's **Settings > General** page.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > General**.
1. Expand the **Merge request approvals** section.
1. Select the settings you want.
1. Select **Save changes**.
@@ -548,7 +560,7 @@ Changes to [group wikis](../project/wiki/group.md) do not appear in group activi
You can view the most recent actions taken in a group, either in your browser or in an RSS feed:
-1. On the top bar, select **Menu > Groups**.
+1. On the top bar, select **Main menu > Groups > View all groups**.
1. Select **Your Groups**.
1. Find the group and select it.
1. On the left sidebar, select **Group information > Activity**.
@@ -568,3 +580,13 @@ the following checks when creating or updating namespaces or groups:
In the unlikely event that you see these errors in your GitLab installation,
[contact Support](https://about.gitlab.com/support/) so that we can improve this validation.
+
+### Find groups using an SQL query
+
+To find and store an array of groups based on an SQL query in the [rails console](../../administration/operations/rails_console.md):
+
+```ruby
+# Finds groups and subgroups that end with '%oup'
+Group.find_by_sql("SELECT * FROM namespaces WHERE name LIKE '%oup'")
+=> [#<Group id:3 @test-group>, #<Group id:4 @template-group/template-subgroup>]
+```
diff --git a/doc/user/group/repositories_analytics/index.md b/doc/user/group/repositories_analytics/index.md
index 6e3ea7d6c0f..f130ecb61dc 100644
--- a/doc/user/group/repositories_analytics/index.md
+++ b/doc/user/group/repositories_analytics/index.md
@@ -37,7 +37,7 @@ The **Analytics > Repositories** group page displays the average test coverage o
To see the latest code coverage for each project in your group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Repositories**.
1. In the **Latest test coverage results** section, from the **Select projects** dropdown list, choose the projects you want to check.
@@ -52,7 +52,7 @@ You can get a CSV of the code coverage data for all of the projects in your grou
To get the report:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Repositories**.
1. Select **Download historic test coverage data (.csv)**.
1. Select the projects and date range you want to include in the report.
diff --git a/doc/user/group/saml_sso/example_saml_config.md b/doc/user/group/saml_sso/example_saml_config.md
index 97e8f9c54a3..d09a8524247 100644
--- a/doc/user/group/saml_sso/example_saml_config.md
+++ b/doc/user/group/saml_sso/example_saml_config.md
@@ -31,20 +31,27 @@ If you are currently having an issue with GitLab, you may want to check your [su
## Azure Active Directory
-Basic SAML app configuration:
+This section has screenshots for the elements of Azure Active Directory configuration.
+
+### Basic SAML app configuration
![Azure AD basic SAML](img/AzureAD-basic_SAML.png)
-User claims and attributes:
+### User claims and attributes
![Azure AD user claims](img/AzureAD-claims.png)
-SCIM mapping:
+### SCIM mapping
+
+Provisioning:
![Azure AD SCIM Provisioning](img/AzureAD-scim_provisioning.png)
+
+Attribute mapping:
+
![Azure AD SCIM Attribute Mapping](img/AzureAD-scim_attribute_mapping.png)
-Group Sync:
+### Group Sync
![Azure Group Claims](img/azure_configure_group_claim.png)
diff --git a/doc/user/group/saml_sso/group_sync.md b/doc/user/group/saml_sso/group_sync.md
index 8bc316f9396..322b417d466 100644
--- a/doc/user/group/saml_sso/group_sync.md
+++ b/doc/user/group/saml_sso/group_sync.md
@@ -70,9 +70,9 @@ role.
Users granted:
- A higher role with Group Sync are displayed as having
- [direct membership](../../project/members/#display-direct-members) of the group.
+ [direct membership](../../project/members/index.md#display-direct-members) of the group.
- A lower or the same role with Group Sync are displayed as having
- [inherited membership](../../project/members/#display-inherited-members) of the group.
+ [inherited membership](../../project/members/index.md#display-inherited-members) of the group.
### Automatic member removal
@@ -167,3 +167,18 @@ graph TB
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/290367) in GitLab 15.3.
You can use the GitLab API to [list, add, and delete](../../../api/groups.md#saml-group-links) SAML group links.
+
+## Troubleshooting
+
+This section contains possible solutions for problems you might encounter.
+
+### User that belongs to many SAML groups automatically removed from GitLab group
+
+When using Azure AD as the SAML identity provider, users that belong to many SAML groups can be automatically removed from your GitLab group. Users are removed from GitLab
+groups if the group claim is missing from the user's SAML assertion.
+
+Because of a [known issue with Azure AD](https://support.esri.com/en/technical-article/000022190), if a user belongs to more than 150 SAML groups, the group claim is not sent
+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).
diff --git a/doc/user/group/saml_sso/img/scim_token_v13_3.png b/doc/user/group/saml_sso/img/scim_token_v13_3.png
deleted file mode 100644
index e98f755718a..00000000000
--- a/doc/user/group/saml_sso/img/scim_token_v13_3.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md
index 25060f8e749..d33cde0a6b1 100644
--- a/doc/user/group/saml_sso/index.md
+++ b/doc/user/group/saml_sso/index.md
@@ -24,7 +24,7 @@ If required, you can find [a glossary of common terms](../../../integration/saml
## Configure your identity provider
1. Find the information in GitLab required for configuration:
- 1. On the top bar, select **Menu > Groups** and find your group.
+ 1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > SAML SSO**.
1. Note the **Assertion consumer service URL**, **Identifier**, and **GitLab single sign-on URL**.
1. Configure your SAML identity provider app using the noted details.
@@ -79,7 +79,7 @@ You can configure the following attributes with GitLab.com Group SAML:
GitLab provides metadata XML that can be used to configure your identity provider.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > SAML SSO**.
1. Copy the provided **GitLab metadata URL**.
1. Follow your identity provider's documentation and paste the metadata URL when it's requested.
@@ -88,7 +88,7 @@ GitLab provides metadata XML that can be used to configure your identity provide
After you set up your identity provider to work with GitLab, you must configure GitLab to use it for authentication:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > SAML SSO**.
1. Find the SSO URL from your identity provider and enter it the **Identity provider single sign-on URL** field.
1. Find and enter the fingerprint for the SAML token signing certificate in the **Certificate** field.
@@ -101,6 +101,19 @@ After you set up your identity provider to work with GitLab, you must configure
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.
+### Additional configuration information
+
+Many SAML terms can vary between providers. It is possible that the information you are looking for is listed under another name.
+
+For more information, start with your identity provider's documentation. Look for their options and examples to see how they configure SAML. This can provide hints on what you need to configure GitLab to work with these providers.
+
+It can also help to look at our [more detailed docs for self-managed GitLab](../../../integration/saml.md).
+SAML configuration for GitLab.com is mostly the same as for self-managed instances.
+However, self-managed GitLab instances use a configuration file that supports more options as described in the external [OmniAuth SAML documentation](https://github.com/omniauth/omniauth-saml/).
+Internally that uses the [`ruby-saml` library](https://github.com/onelogin/ruby-saml), so we sometimes check there to verify low level details of less commonly used options.
+
+It can also help to compare the XML response from your provider with our [example XML used for internal testing](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/spec/fixtures/saml/response.xml).
+
### SSO enforcement
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/5291) in GitLab 11.8.
@@ -122,7 +135,7 @@ prompts the user to sign in again through SSO.
An [issue exists](https://gitlab.com/gitlab-org/gitlab/-/issues/297389) to add a similar SSO requirement for API activity.
-SSO has the following effects when enabled:
+SSO enforcement has the following effects when enabled:
- For groups, users can't share a project in the group outside the top-level group,
even if the project is forked.
@@ -157,8 +170,8 @@ If you have any questions on configuring the SAML app, please contact your provi
Follow the Azure documentation on [configuring single sign-on to applications](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/view-applications-portal) with the notes below for consideration.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
-For a demo of the Azure SAML setup including SCIM, see [SCIM Provisioning on Azure Using SAML SSO for Groups Demo](https://youtu.be/24-ZxmTeEBU). The video is outdated in regard to
-objectID mapping and the [SCIM documentation should be followed](scim_setup.md#azure-configuration-steps).
+For a demo of the Azure SAML setup including SCIM, see [SCIM Provisioning on Azure Using SAML SSO for Groups Demo](https://youtu.be/24-ZxmTeEBU).
+The video is outdated in regard to objectID mapping and you should follow the [SCIM documentation](scim_setup.md#configure-azure-active-directory).
| GitLab Setting | Azure Field |
| ------------------------------------ | ------------------------------------------ |
@@ -168,7 +181,7 @@ objectID mapping and the [SCIM documentation should be followed](scim_setup.md#a
| Identity provider single sign-on URL | Login URL |
| Certificate fingerprint | Thumbprint |
-The recommended attributes and claims settings are:
+The recommended attributes are:
- **Unique User Identifier (Name identifier)** set to `user.objectID`.
- **nameid-format** set to persistent.
@@ -311,6 +324,13 @@ On subsequent visits, you should be able to go [sign in to GitLab.com with SAML]
1. From the list of apps, select the "GitLab.com" app. (The name is set by the administrator of the identity provider.)
1. You are then signed in to GitLab.com and redirected to the group.
+### Change NameID for one or more users
+
+If the NameID changes for one or more users, they need to reconnect their SAML account.
+
+1. Ask relevant users to [unlink their account from the group](#unlinking-accounts).
+1. Ask relevant users to [link their account to the new SAML app](#linking-saml-to-your-existing-gitlabcom-account).
+
### Configure user settings from SAML response
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/263661) in GitLab 13.7.
@@ -357,6 +377,18 @@ convert the information to XML. An example SAML response is shown here.
</saml2:AttributeStatement>
```
+### Bypass user email confirmation with verified domains
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/238461) in GitLab 15.4.
+
+By default, users provisioned with SAML or SCIM are sent a verification email to verify their identity. Instead, you can
+[configure GitLab with a custom domain](../../project/pages/custom_domains_ssl_tls_certification/index.md) and GitLab
+will automatically confirm user accounts. Users will still receive an enterprise user welcome email.
+Confirmation is bypassed for users:
+
+- That are provisioned with SAML or SCIM.
+- That have an email address that belongs to the verified domain.
+
### Role
Starting from [GitLab 13.3](https://gitlab.com/gitlab-org/gitlab/-/issues/214523), group owners can set a
@@ -412,160 +444,4 @@ The [Generated passwords for users created through integrated authentication](..
## Troubleshooting
-This section contains possible solutions for problems you might encounter.
-
-### SAML debugging tools
-
-SAML responses are base64 encoded, so we recommend the following browser plugins to decode them on the fly:
-
-- [SAML-tracer](https://addons.mozilla.org/en-US/firefox/addon/saml-tracer/) for Firefox.
-- [SAML Message Decoder](https://chrome.google.com/webstore/detail/saml-message-decoder/mpabchoaimgbdbbjjieoaeiibojelbhm?hl=en) for Chrome.
-
-Specific attention should be paid to:
-
-- The [NameID](#nameid), which we use to identify which user is signing in. If the user has previously signed in, this [must match the value we have stored](#verifying-nameid).
-- The presence of a `X509Certificate`, which we require to verify the response signature.
-- The `SubjectConfirmation` and `Conditions`, which can cause errors if misconfigured.
-
-#### Generate a SAML Response
-
-SAML Responses can be used to preview the attribute names and values sent in the assertions list while attempting to sign in using an IdP.
-
-To generate a SAML Response:
-
-1. Install either:
- - [SAML Chrome Panel](https://chrome.google.com/webstore/detail/saml-chrome-panel/paijfdbeoenhembfhkhllainmocckace) for Chrome.
- - [SAML-tracer](https://addons.mozilla.org/en-US/firefox/addon/saml-tracer/) for Firefox.
-1. Open a new browser tab.
-1. Open the SAML tracer console:
- - Chrome: Right-click on the page, select **Inspect**, then select the **SAML** tab in the opened developer console.
- - Firefox: Select the SAML-tracer icon located on the browser toolbar.
-1. Go to the GitLab single sign-on URL for the group in the same browser tab with the SAML tracer open.
-1. Select **Authorize** or attempt to log in. A SAML response is displayed in the tracer console that resembles this
- [example SAML response](#example-saml-response).
-1. Within the SAML tracer, select the **Export** icon to save the response in JSON format.
-
-### Verifying configuration
-
-For convenience, we've included some [example resources](../../../user/group/saml_sso/example_saml_config.md) used by our Support Team. While they may help you verify the SAML app configuration, they are not guaranteed to reflect the current state of third-party products.
-
-### Verifying NameID
-
-In troubleshooting the Group SAML setup, any authenticated user can use the API to verify the NameID GitLab already has linked to the user by visiting [`https://gitlab.com/api/v4/user`](https://gitlab.com/api/v4/user) and checking the `extern_uid` under identities.
-
-Similarly, group members of a role with the appropriate permissions can make use of the [members API](../../../api/members.md) to view group SAML identity information for members of the group.
-
-This can then be compared to the [NameID](#nameid) being sent by the identity provider by decoding the message with a [SAML debugging tool](#saml-debugging-tools). We require that these match in order to identify users.
-
-### Users receive a 404
-
-Because SAML SSO for groups is a paid feature, your subscription expiring can result in a `404` error when you're signing in using SAML SSO on GitLab.com.
-If all users are receiving a `404` when attempting to log in using SAML, confirm
-[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).
-
-If a user is trying to sign in for the first time and the GitLab single sign-on URL has not [been configured](#configure-your-identity-provider), they may see a 404.
-As outlined in the [user access section](#linking-saml-to-your-existing-gitlabcom-account), a group Owner needs to provide the URL to users.
-
-### Message: "SAML authentication failed: Extern UID has already been taken"
-
-This error suggests you are signed in as a GitLab user but have already linked your SAML identity to a different GitLab user. Sign out and then try to sign in again using the SSO SAML link, which should log you into GitLab with the linked user account.
-
-If you do not wish to use that GitLab user with the SAML login, you can [unlink the GitLab account from the group's SAML](#unlinking-accounts).
-
-### Message: "SAML authentication failed: User has already been taken"
-
-The user that you're signed in with already has SAML linked to a different identity, or the NameID value has changed.
-Here are possible causes and solutions:
-
-| Cause | Solution |
-| ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| You've tried to link multiple SAML identities to the same user, for a given identity provider. | Change the identity that you sign in with. To do so, [unlink the previous SAML identity](#unlinking-accounts) from this GitLab account before attempting to sign in again. |
-| The NameID changes every time the user requests SSO identification | Check the NameID is not set with `Transient` format, or the NameID is not changing on subsequent requests.|
-
-### Message: "SAML authentication failed: Email has already been taken"
-
-| Cause | Solution |
-| ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
-| When a user account with the email address already exists in GitLab, but the user does not have the SAML identity tied to their account. | The user needs to [link their account](#user-access-and-management). |
-
-User accounts are created in one of the following ways:
-
-- User registration
-- Sign in through OAuth
-- Sign in through SAML
-- SCIM provisioning
-
-### Message: "SAML authentication failed: Extern UID has already been taken, User has already been taken"
-
-Getting both of these errors at the same time suggests the NameID capitalization provided by the identity provider didn't exactly match the previous value for that user.
-
-This can be prevented by configuring the [NameID](#nameid) to return a consistent value. Fixing this for an individual user involves [unlinking SAML in the GitLab account](#unlinking-accounts), although this causes group membership and to-do items to be lost.
-
-### Message: "Request to link SAML account must be authorized"
-
-Ensure that the user who is trying to link their GitLab account has been added as a user within the identity provider's SAML app.
-
-Alternatively, the SAML response may be missing the `InResponseTo` attribute in the
-`samlp:Response` tag, which is [expected by the SAML gem](https://github.com/onelogin/ruby-saml/blob/9f710c5028b069bfab4b9e2b66891e0549765af5/lib/onelogin/ruby-saml/response.rb#L307-L316).
-The identity provider administrator should ensure that the login is
-initiated by the service provider and not the identity provider.
-
-### Message: "Sign in to GitLab to connect your organization's account"
-
-A user can see this message when they are trying to [manually link SAML to their existing GitLab.com account](#linking-saml-to-your-existing-gitlabcom-account).
-
-To resolve this problem, the user should check they are using the correct GitLab password to log in. They first need to
-[reset their password](https://gitlab.com/users/password/new) if both:
-
-- The account was provisioned by SCIM.
-- This is the first time the user has logged in the username and password.
-
-### Stuck in a login "loop"
-
-Ensure that the **GitLab single sign-on URL** has been configured as "Login URL" (or similarly named field) in the identity provider's SAML app.
-
-Alternatively, when users need to [link SAML to their existing GitLab.com account](#linking-saml-to-your-existing-gitlabcom-account), provide the **GitLab single sign-on URL** and instruct users not to use the SAML app on first sign in.
-
-### The NameID has changed
-
-| Cause | Solution |
-| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| As mentioned in the [NameID](#nameid) section, if the NameID changes for any user, the user can be locked out. This is a common problem when an email address is used as the identifier. | Follow the steps outlined in the ["SAML authentication failed: User has already been taken"](#message-saml-authentication-failed-user-has-already-been-taken) section. |
-
-### I need additional information to configure my identity provider
-
-Many SAML terms can vary between providers. It is possible that the information you are looking for is listed under another name.
-
-For more information, start with your identity provider's documentation. Look for their options and examples to see how they configure SAML. This can provide hints on what you need to configure GitLab to work with these providers.
-
-It can also help to look at our [more detailed docs for self-managed GitLab](../../../integration/saml.md).
-SAML configuration for GitLab.com is mostly the same as for self-managed instances.
-However, self-managed GitLab instances use a configuration file that supports more options as described in the external [OmniAuth SAML documentation](https://github.com/omniauth/omniauth-saml/).
-Internally that uses the [`ruby-saml` library](https://github.com/onelogin/ruby-saml), so we sometimes check there to verify low level details of less commonly used options.
-
-It can also help to compare the XML response from your provider with our [example XML used for internal testing](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/spec/fixtures/saml/response.xml).
-
-### Searching Rails log
-
-With access to the rails log or `production_json.log` (available only to GitLab team members for GitLab.com),
-you should be able to find the base64 encoded SAML response by searching with the following filters:
-
-- `json.meta.caller_id`: `Groups::OmniauthCallbacksController#group_saml`
-- `json.meta.user` or `json.username`: `username`
-- `json.method`: `POST`
-- `json.path`: `/groups/GROUP-PATH/-/saml/callback`
-
-In a relevant log entry, the `json.params` should provide a valid response with:
-
-- `"key": "SAMLResponse"` and the `"value": (full SAML response)`,
-- `"key": "RelayState"` with `"value": "/group-path"`, and
-- `"key": "group_id"` with `"value": "group-path"`.
-
-In some cases, if the SAML response is lengthy, you may receive a `"key": "truncated"` with `"value":"..."`.
-In these cases, please ask a group owner for 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.
+See our [troubleshooting SAML guide](troubleshooting.md).
diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md
index 7962f171166..a67b2fbe02e 100644
--- a/doc/user/group/saml_sso/scim_setup.md
+++ b/doc/user/group/saml_sso/scim_setup.md
@@ -5,107 +5,129 @@ 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/engineering/ux/technical-writing/#assignments
---
-# SCIM provisioning using SAML SSO for GitLab.com groups **(PREMIUM SAAS)**
+# Configure SCIM for GitLab.com groups **(PREMIUM SAAS)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/9388) in GitLab 11.10.
-System for Cross-domain Identity Management (SCIM), is an open standard that enables the
-automation of user provisioning. When SCIM is provisioned for a GitLab group, membership of
-that group is synchronized between GitLab and the identity provider.
+You can use the open standard System for Cross-domain Identity Management (SCIM) to automatically:
-The GitLab [SCIM API](../../../api/scim.md) implements part of [the RFC7644 protocol](https://tools.ietf.org/html/rfc7644).
-
-## Features
+- Create users.
+- Remove users (deactivate SCIM identity).
-The following actions are available:
+GitLab SAML SSO SCIM doesn't support updating users.
-- Create users
-- Remove users (deactivate SCIM identity)
+When SCIM is enabled for a GitLab group, membership of that group is synchronized between GitLab and an identity provider.
-The following identity providers are supported:
-
-- Azure
-- Okta
+The GitLab [SCIM API](../../../api/scim.md) implements part of [the RFC7644 protocol](https://tools.ietf.org/html/rfc7644).
-## Requirements
+## Configure GitLab
-- [Group Single Sign-On](index.md) must be configured.
+Prerequisites:
-## GitLab configuration
+- [Group single sign-on](index.md) must be configured.
-Once [Group Single Sign-On](index.md) has been configured, we can:
+To configure GitLab SAML SSO SCIM:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > SAML SSO**.
1. Select **Generate a SCIM token**.
-1. Save the token and URL for use in the next step.
+1. For configuration of your identity provider, save the:
+ - Token from the **Your SCIM token** field.
+ - URL from the **SCIM API endpoint URL** field.
-![SCIM token configuration](img/scim_token_v13_3.png)
+## Configure an identity provider
-## Identity Provider configuration
+You can configure one of the following as an identity provider:
-- [Azure](#azure-configuration-steps)
-- [Okta](#okta-configuration-steps)
+- [Azure Active Directory](#configure-azure-active-directory).
+- [Okta](#configure-okta).
+- [OneLogin](#configure-onelogin).
-### Azure configuration steps
+NOTE:
+Other providers can work with GitLab but they have not been tested and are not supported.
-The SAML application that was created during [Single sign-on](index.md) setup for [Azure](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/view-applications-portal) now needs to be set up for SCIM. You can refer to [Azure SCIM setup documentation](https://docs.microsoft.com/en-us/azure/active-directory/app-provisioning/use-scim-to-provision-users-and-groups#getting-started).
+### Configure Azure Active Directory
-1. In your app, go to the Provisioning tab, and set the **Provisioning Mode** to **Automatic**.
- Then fill in the **Admin Credentials**, and save. The **Tenant URL** and **secret token** are the items
- retrieved in the [previous step](#gitlab-configuration).
+The SAML application created during [single sign-on](index.md) set up for
+[Azure Active Directory](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/view-applications-portal)
+must be set up for SCIM. For an example, see [example configuration](example_saml_config.md#scim-mapping).
-1. After saving, two more tabs appear:
+To configure Azure Active Directory for SCIM:
- - **Settings**: We recommend setting a notification email and selecting the **Send an email notification when a failure occurs** checkbox.
- You also control what is actually synced by selecting the **Scope**. For example, **Sync only assigned users and groups** only syncs the users and groups assigned to the application. Otherwise, it syncs the whole Active Directory.
+1. In your app, go to the **Provisioning** tab and select **Get started**.
+1. Set the **Provisioning Mode** to **Automatic**.
+1. Complete the **Admin Credentials** using the value of:
+ - **SCIM API endpoint URL** in GitLab for the **Tenant URL** field.
+ - **Your SCIM token** in GitLab for the **Secret Token** field.
+1. Select **Test Connection**. If the test is successful, save your configuration before continuing, or see the
+ [troubleshooting](#troubleshooting) information.
+1. Select **Save**.
- - **Mappings**: We recommend keeping **Provision Azure Active Directory Users** enabled, and disable **Provision Azure Active Directory Groups**.
- Leaving **Provision Azure Active Directory Groups** enabled does not break the SCIM user provisioning, but it causes errors in Azure AD that may be confusing and misleading.
+After saving, **Settings** and **Mappings** sections appear.
+
+1. Under **Settings**, if required, set a notification email and select the
+ **Send an email notification when a failure occurs** checkbox.
+1. Under **Mappings**, we recommend you:
+ 1. Keep **Provision Azure Active Directory Users** enabled and select the **Provision Azure Active Directory Users**
+ link to [configure attribute mappings](#configure-attribute-mappings).
+ 1. Below the mapping list select the **Show advanced options** checkbox.
+ 1. Select the **Edit attribute list for customappsso** link.
+ 1. Ensure the `id` is the primary and required field, and `externalId` is also required.
+ 1. Select **Save**.
+1. Return to the **Provisioning** tab, saving unsaved changes if necessary.
+1. Select **Edit attribute mappings**.
+1. Under **Mappings**:
+ 1. Select **Provision Azure Active Directory Groups**.
+ 1. On the Attribute Mapping page, turn off the **Enabled** toggle. Leaving it turned on doesn't break the SCIM user
+ provisioning, but it causes errors in Azure Active Directory that may be confusing and misleading.
+ 1. Select **Save**.
+1. Return to the **Provisioning** tab, saving unsaved changes if necessary.
+1. Select **Edit attribute mappings**.
+1. Turn on the **Provisioning Status** toggle. Synchronization details and any errors appears on the bottom of the
+ **Provisioning** screen, together with a link to the audit events.
-1. You can then test the connection by selecting **Test Connection**. If the connection is successful, save your configuration before moving on. See below for [troubleshooting](#troubleshooting).
+WARNING:
+Once synchronized, changing the field mapped to `id` and `externalId` may cause a number of errors. These include
+provisioning errors, duplicate users, and may prevent existing users from accessing the GitLab group.
-#### Configure attribute mapping
+#### Configure attribute mappings
-Follow [Azure documentation to configure the attribute mapping](https://docs.microsoft.com/en-us/azure/active-directory/app-provisioning/customize-application-attributes).
+While [configuring Azure Active Directory for SCIM](#configure-azure-active-directory), you configure attribute mappings.
+For an example, see [example configuration](example_saml_config.md#scim-mapping).
-The following table below provides an attribute mapping known to work with GitLab. If
-your SAML configuration differs from [the recommended SAML settings](index.md#azure-setup-notes),
-modify the corresponding `customappsso` settings accordingly. In particular, the `externalId` must
-match the [SAML NameID](index.md#nameid).
-If a mapping is not listed in the table, use the Azure defaults.
-For a list of required attributes, refer to the [SCIM API documentation](../../../api/scim.md).
+The following table provides attribute mappings known to work with GitLab.
-| Azure Active Directory Attribute | `customappsso` Attribute | Matching precedence |
-| -------------------------------- | ------------------------------ | ------------------- |
-| `objectId` | `externalId` | 1 |
-| `userPrincipalName` | `emails[type eq "work"].value` | |
-| `mailNickname` | `userName` | |
+| Source attribute | Target attribute | Matching precedence |
+|:--------------------|:-------------------------------|:--------------------|
+| `objectId` | `externalId` | 1 |
+| `userPrincipalName` | `emails[type eq "work"].value` | |
+| `mailNickname` | `userName` | |
-For guidance, you can view [an example configuration](example_saml_config.md#azure-active-directory).
+Each attribute mapping has:
-1. Below the mapping list select **Show advanced options > Edit attribute list for AppName**.
-1. Ensure the `id` is the primary and required field, and `externalId` is also required.
+- An Azure Active Directory attribute (source attribute).
+- A `customappsso` attribute (target attribute).
+- A matching precedence.
- NOTE:
- `username` should neither be primary nor required as we don't support
- that field on GitLab SCIM yet.
+For each attribute:
-1. Save all changes.
-1. In the **Provisioning** step, set the `Provisioning Status` to `On`.
+1. Select the attribute to edit it.
+1. Select the required settings.
+1. Select **Ok**.
-Once enabled, the synchronization details and any errors appears on the
-bottom of the **Provisioning** screen, together with a link to the audit events.
+If your SAML configuration differs from [the recommended SAML settings](index.md#azure-setup-notes), select the mapping
+attributes and modify them accordingly. In particular, the `objectId` source attribute must map to the `externalId`
+target attribute.
-WARNING:
-Once synchronized, changing the field mapped to `id` and `externalId` may cause a number of errors. These include provisioning errors, duplicate users, and may prevent existing users from accessing the GitLab group.
+If a mapping is not listed in the table, use the Azure Active Directory defaults. For a list of required attributes,
+refer to the [SCIM API documentation](../../../api/scim.md).
-### Okta configuration steps
+### Configure Okta
Before you start this section:
- Check that you are using Okta [Lifecycle Management](https://www.okta.com/products/lifecycle-management/) product. This product tier is required to use SCIM on Okta. To check which Okta product you are using, check your signed Okta contract, contact your Okta AE, CSM, or Okta support.
-- Complete the [GitLab configuration](#gitlab-configuration) process.
+- Complete the [GitLab configuration](#configure-gitlab) process.
- Complete the setup for SAML application for [Okta](https://developer.okta.com/docs/guides/build-sso-integration/saml2/main/), as described in the [Okta setup notes](index.md#okta-setup-notes).
- Check that your Okta SAML setup matches our documentation exactly, especially the NameID configuration. Otherwise, the Okta SCIM app may not work properly.
@@ -137,7 +159,7 @@ The Okta GitLab application currently only supports SCIM. Continue
using the separate Okta [SAML SSO](index.md) configuration along with the new SCIM
application described above. An [issue exists](https://gitlab.com/gitlab-org/gitlab/-/issues/216173) to add SAML support to the Okta GitLab application.
-### OneLogin
+### Configure OneLogin
As the developers of this app, OneLogin provides a "GitLab (SaaS)" app in their catalog, which includes a SCIM integration.
Please reach out to OneLogin if you encounter issues.
@@ -216,17 +238,17 @@ The **Identity** (`extern_uid`) value stored by GitLab is updated by SCIM whenev
This value is also used by SCIM to match users on the `id`, and is updated by SCIM whenever the `id` or `externalId` values change.
-It is important that this SCIM `id` and SCIM `externalId` are configured to the same value as the SAML `NameId`. SAML responses can be traced using [debugging tools](index.md#saml-debugging-tools), and any errors can be checked against our [SAML troubleshooting docs](index.md#troubleshooting).
+It is important that this SCIM `id` and SCIM `externalId` are configured to the same value as the SAML `NameId`. SAML responses can be traced using [debugging tools](troubleshooting.md#saml-debugging-tools), and any errors can be checked against our [SAML troubleshooting docs](troubleshooting.md).
### How do I verify user's SAML NameId matches the SCIM externalId
-Admins can use the Admin Area to [list SCIM identities for a user](../../admin_area/#user-identities).
+Admins can use the Admin Area to [list SCIM identities for a user](../../admin_area/index.md#user-identities).
Group owners can see the list of users and the `externalId` stored for each user in the group SAML SSO Settings page.
A possible alternative is to use the [SCIM API](../../../api/scim.md#get-a-list-of-scim-provisioned-users) to manually retrieve the `externalId` we have stored for users, also called the `external_uid` or `NameId`.
-To see how the `external_uid` compares to the value returned as the SAML NameId, you can have the user use a [SAML Tracer](index.md#saml-debugging-tools).
+To see how the `external_uid` compares to the value returned as the SAML NameId, you can have the user use a [SAML Tracer](troubleshooting.md#saml-debugging-tools).
### Update or fix mismatched SCIM externalId and SAML NameId
@@ -241,7 +263,7 @@ that provider may create duplicate users.
If the `externalId` for a user is not correct, and also doesn't match the SAML NameID,
you can address the problem in the following ways:
-- You can have users unlink and relink themselves, based on the ["SAML authentication failed: User has already been taken"](index.md#message-saml-authentication-failed-user-has-already-been-taken) section.
+- You can have users unlink and relink themselves, based on the ["SAML authentication failed: User has already been taken"](troubleshooting.md#message-saml-authentication-failed-user-has-already-been-taken) section.
- You can unlink all users simultaneously, by removing all users from the SAML app while provisioning is turned on.
- It may be possible to use the [SCIM API](../../../api/scim.md#update-a-single-scim-provisioned-user) to manually correct the `externalId` stored for users to match the SAML `NameId`.
To look up a user, you need to know the desired value that matches the `NameId` as well as the current `externalId`.
@@ -314,4 +336,4 @@ and the error response can include a HTML result of the GitLab URL `https://gitl
This error is harmless and occurs because Group provisioning was turned on but GitLab SCIM integration does not support it nor require it. To
remove the error, follow the instructions in the Azure configuration guide to disable the option
-[`Synchronize Azure Active Directory Groups to AppName`](#azure-configuration-steps).
+[`Synchronize Azure Active Directory Groups to AppName`](#configure-azure-active-directory).
diff --git a/doc/user/group/saml_sso/troubleshooting.md b/doc/user/group/saml_sso/troubleshooting.md
new file mode 100644
index 00000000000..177f33228c0
--- /dev/null
+++ b/doc/user/group/saml_sso/troubleshooting.md
@@ -0,0 +1,249 @@
+---
+type: reference
+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/engineering/ux/technical-writing/#assignments
+---
+
+# Troubleshooting SAML **(FREE)**
+
+This page contains possible solutions for problems you might encounter when using:
+
+- [SAML SSO for GitLab.com groups](index.md).
+- The self-managed instance-level [SAML OmniAuth Provider](../../../integration/saml.md).
+
+## SAML debugging tools
+
+SAML responses are base64 encoded, so we recommend the following browser plugins to decode them on the fly:
+
+- [SAML-tracer](https://addons.mozilla.org/en-US/firefox/addon/saml-tracer/) for Firefox.
+- [SAML Message Decoder](https://chrome.google.com/webstore/detail/saml-message-decoder/mpabchoaimgbdbbjjieoaeiibojelbhm?hl=en) for Chrome.
+
+Specific attention should be paid to:
+
+- The NameID, which we use to identify which user is signing in. If the user has previously signed in, this [must match the value we have stored](#verifying-nameid).
+- The presence of a `X509Certificate`, which we require to verify the response signature.
+- The `SubjectConfirmation` and `Conditions`, which can cause errors if misconfigured.
+
+### Generate a SAML response
+
+Use SAML responses to preview the attribute names and values sent in the assertions list while attempting to sign in
+using an identity provider.
+
+To generate a SAML Response:
+
+1. Install one of the browser debugging tools previously mentioned.
+1. Open a new browser tab.
+1. Open the SAML tracer console:
+ - Chrome: On a context menu on the page, select **Inspect**, then select the **SAML** tab in the opened developer
+ console.
+ - Firefox: Select the SAML-tracer icon located on the browser toolbar.
+1. Go to the GitLab single sign-on URL for the group in the same browser tab with the SAML tracer open.
+1. Select **Authorize** or attempt to log in. A SAML response is displayed in the tracer console that resembles this
+ [example SAML response](index.md#example-saml-response).
+1. Within the SAML tracer, select the **Export** icon to save the response in JSON format.
+
+## GitLab SAML Testing Environments
+
+To troubleshoot, [a complete GitLab with SAML testing environment using Docker compose](https://gitlab.com/gitlab-com/support/toolbox/replication/tree/master/compose_files)
+is available.
+
+If you only require a SAML provider for testing, 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 (identity provider) is available.
+
+You can test the SaaS feature locally by [enabling SAML for groups on a self-managed instance](../../../integration/saml.md#configuring-group-saml-on-a-self-managed-gitlab-instance).
+
+## Verifying configuration
+
+For convenience, we've included some [example resources](../../../user/group/saml_sso/example_saml_config.md) used by our Support Team. While they may help you verify the SAML app configuration, they are not guaranteed to reflect the current state of third-party products.
+
+## Searching Rails log for a SAML response **(FREE SELF)**
+
+You can find the base64-encoded SAML Response in the [`production_json.log`](../../../administration/logs/index.md#production_jsonlog).
+This response is sent from the identity provider, and contains user information that is consumed by GitLab.
+Many errors in the SAML integration can be solved by decoding this response and comparing it to the SAML settings in the GitLab configuration file.
+
+For example, with SAML for groups,
+you should be able to find the base64 encoded SAML response by searching with the following filters:
+
+- `json.meta.caller_id`: `Groups::OmniauthCallbacksController#group_saml`
+- `json.meta.user` or `json.username`: `username`
+- `json.method`: `POST`
+- `json.path`: `/groups/GROUP-PATH/-/saml/callback`
+
+In a relevant log entry, the `json.params` should provide a valid response with:
+
+- `"key": "SAMLResponse"` and the `"value": (full SAML response)`,
+- `"key": "RelayState"` with `"value": "/group-path"`, and
+- `"key": "group_id"` with `"value": "group-path"`.
+
+In some cases, if the SAML response is lengthy, you may receive a `"key": "truncated"` with `"value":"..."`.
+In these cases, use one of the [SAML debugging tools](#saml-debugging-tools), or for SAML SSO for groups,
+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.
+
+## Configuration errors
+
+### Invalid audience
+
+This error means that the identity provider doesn't recognize GitLab as a valid sender and
+receiver of SAML requests. Make sure to:
+
+- Add the GitLab callback URL to the approved audiences of the identity provider server.
+- Avoid trailing whitespace in the `issuer` string.
+
+### Key validation error, Digest mismatch or Fingerprint mismatch
+
+These errors all come from a similar place, the SAML certificate. SAML requests
+must be validated using either a fingerprint, a certificate, or a validator.
+
+For this requirement, be sure to take the following into account:
+
+- If a fingerprint is used, it must be the SHA1 fingerprint
+- If no certificate is provided in the settings, a fingerprint or fingerprint
+ validator needs to be provided and the response from the server must contain
+ a certificate (`<ds:KeyInfo><ds:X509Data><ds:X509Certificate>`)
+- If a certificate is provided in the settings, it is no longer necessary for
+ the request to contain one. In this case the fingerprint or fingerprint
+ validators are optional
+
+If none of the above described scenarios is valid, the request
+fails with one of the mentioned errors.
+
+### Missing claims, or `Email can't be blank` errors
+
+The identity provider server needs to pass certain information in order for GitLab to either
+create an account, or match the login information to an existing account. `email`
+is the minimum amount of information that needs to be passed. If the identity provider server
+is not providing this information, all SAML requests fail.
+
+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).
+
+## User sign in banner error messages
+
+### Message: "SAML authentication failed: Extern UID has already been taken"
+
+This error suggests you are signed in as a GitLab user but have already linked your SAML identity to a different GitLab user. Sign out and then try to sign in again using SAML, which should log you into GitLab with the linked user account.
+
+If you do not wish to use that GitLab user with the SAML login, you can [unlink the GitLab account from the SAML app](index.md#unlinking-accounts).
+
+### Message: "SAML authentication failed: User has already been taken"
+
+The user that you're signed in with already has SAML linked to a different identity, or the NameID value has changed.
+Here are possible causes and solutions:
+
+| Cause | Solution |
+| ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| You've tried to link multiple SAML identities to the same user, for a given identity provider. | Change the identity that you sign in with. To do so, [unlink the previous SAML identity](index.md#unlinking-accounts) from this GitLab account before attempting to sign in again. |
+| The NameID changes every time the user requests SSO identification | [Check the NameID](#verifying-nameid) is not set with `Transient` format, or the NameID is not changing on subsequent requests.|
+
+### Message: "SAML authentication failed: Email has already been taken"
+
+| Cause | Solution |
+| ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
+| When a user account with the email address already exists in GitLab, but the user does not have the SAML identity tied to their account. | The user needs to [link their account](index.md#user-access-and-management). |
+
+User accounts are created in one of the following ways:
+
+- User registration
+- Sign in through OAuth
+- Sign in through SAML
+- SCIM provisioning
+
+### Message: "SAML authentication failed: Extern UID has already been taken, User has already been taken"
+
+Getting both of these errors at the same time suggests the NameID capitalization provided by the identity provider didn't exactly match the previous value for that user.
+
+This can be prevented by configuring the NameID to return a consistent value. Fixing this for an individual user involves changing the identifier for the user. For GitLab.com, the user needs to [unlink their SAML from the GitLab account](index.md#unlinking-accounts).
+
+### Message: "Request to link SAML account must be authorized"
+
+Ensure that the user who is trying to link their GitLab account has been added as a user within the identity provider's SAML app.
+
+Alternatively, the SAML response may be missing the `InResponseTo` attribute in the
+`samlp:Response` tag, which is [expected by the SAML gem](https://github.com/onelogin/ruby-saml/blob/9f710c5028b069bfab4b9e2b66891e0549765af5/lib/onelogin/ruby-saml/response.rb#L307-L316).
+The identity provider administrator should ensure that the login is
+initiated by the service provider and not only the identity provider.
+
+### Message: "Sign in to GitLab to connect your organization's account" **(PREMIUM SAAS)**
+
+A user can see this message when they are trying to [manually link SAML to their existing GitLab.com account](index.md#linking-saml-to-your-existing-gitlabcom-account).
+
+To resolve this problem, the user should check they are using the correct GitLab password to log in. The user first needs
+to [reset their password](https://gitlab.com/users/password/new) if both:
+
+- The account was provisioned by SCIM.
+- They are signing in with username and password for the first time.
+
+## Other user sign in issues
+
+### Verifying NameID
+
+In troubleshooting, any authenticated user can use the API to verify the NameID GitLab already has linked to the user by visiting [`https://gitlab.com/api/v4/user`](https://gitlab.com/api/v4/user) and checking the `extern_uid` under identities.
+
+For self-managed, administrators can use the [users API](../../../api/users.md) to see the same information.
+
+When using SAML for groups, group members of a role with the appropriate permissions can make use of the [members API](../../../api/members.md) to view group SAML identity information for members of the group.
+
+This can then be compared to the NameID being sent by the identity provider by decoding the message with a [SAML debugging tool](#saml-debugging-tools). We require that these match in order to identify users.
+
+### Stuck in a login "loop"
+
+Ensure that the **GitLab single sign-on URL** (for GitLab.com) or the instance URL (for self-managed) has been configured as "Login URL" (or similarly named field) in the identity provider's SAML app.
+
+For GitLab.com, alternatively, when users need to [link SAML to their existing GitLab.com account](index.md#linking-saml-to-your-existing-gitlabcom-account), provide the **GitLab single sign-on URL** and instruct users not to use the SAML app on first sign in.
+
+### Users receive a 404 **(PREMIUM SAAS)**
+
+Because SAML SSO for groups is a paid feature, your subscription expiring can result in a `404` error when you're signing in using SAML SSO on GitLab.com.
+If all users are receiving a `404` when attempting to log in using SAML, confirm
+[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).
+
+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.
+
+### 500 error after login **(FREE SELF)**
+
+If you see a "500 error" in GitLab when you are redirected back from the SAML
+sign-in page, this could indicate that:
+
+- GitLab couldn't get the email address for the SAML user. Ensure the identity provider provides a claim containing the user's
+ email address using the claim name `email` or `mail`.
+- The certificate set your `gitlab.rb` file for `identity provider_cert_fingerprint` or `identity provider_cert` file is incorrect.
+- Your `gitlab.rb` file is set to enable `identity provider_cert_fingerprint`, and `identity provider_cert` is being provided, or the reverse.
+
+### 422 error after login **(FREE SELF)**
+
+If you see a "422 error" in GitLab when you are redirected from the SAML
+sign-in page, you might have an incorrectly configured Assertion Consumer
+Service (ACS) URL on the identity provider.
+
+Make sure the ACS URL points to `https://gitlab.example.com/users/auth/saml/callback`, where
+`gitlab.example.com` is the URL of your GitLab instance.
+
+If the ACS URL is correct, and you still have errors, review the other
+Troubleshooting sections.
+
+### User is blocked when signing in through SAML **(FREE SELF)**
+
+The following are the most likely reasons that a user is blocked when signing in through SAML:
+
+- In the configuration, `gitlab_rails['omniauth_block_auto_created_users'] = true` is set and this is the user's first time signing in.
+- [`required_groups`](../../../integration/saml.md#required-groups) are configured but the user is not a member of one.
+
+## Google workspace troubleshooting tips
+
+The Google Workspace documentation on [SAML app error messages](https://support.google.com/a/answer/6301076?hl=en) is helpful for debugging if you are seeing an error from Google while signing in.
+Pay particular attention to the following 403 errors:
+
+- `app_not_configured`
+- `app_not_configured_for_user`
diff --git a/doc/user/group/settings/group_access_tokens.md b/doc/user/group/settings/group_access_tokens.md
index c3098bb56c2..eb9c6af9edf 100644
--- a/doc/user/group/settings/group_access_tokens.md
+++ b/doc/user/group/settings/group_access_tokens.md
@@ -27,6 +27,13 @@ associated with a group rather than a project or user.
In self-managed instances, group access tokens are subject to the same [maximum lifetime limits](../../admin_area/settings/account_and_limit_settings.md#limit-the-lifetime-of-access-tokens) as personal access tokens if the limit is set.
+WARNING:
+The ability to create group access tokens without expiry was
+[deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/369122) in GitLab 15.4 and is planned for removal in GitLab
+16.0. When this ability is removed, existing group access tokens without an expiry are planned to have an expiry added.
+The automatic adding of an expiry occurs on GitLab.com during the 16.0 milestone. The automatic adding of an expiry
+occurs on self-managed instances when they are upgraded to GitLab 16.0. This change is a breaking change.
+
You can use group access tokens:
- On GitLab SaaS if you have the Premium license tier or higher. Group access tokens are not available with a [trial license](https://about.gitlab.com/free-trial/).
@@ -43,11 +50,12 @@ configured for personal access tokens.
## Create a group access token using UI
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214045) in GitLab 14.7.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214045) in GitLab 14.7.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348660) in GitLab 15.3, default expiration of 30 days and default role of Guest is populated in the UI.
To create a group access token:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > Access Tokens**.
1. Enter a name. The token name is visible to any user with permissions to view the group.
1. Optional. Enter an expiry date for the token. The token will expire on that date at midnight UTC. An instance-wide [maximum lifetime](../../admin_area/settings/account_and_limit_settings.md#limit-the-lifetime-of-access-tokens) setting can limit the maximum allowable lifetime in self-managed instances.
@@ -104,7 +112,7 @@ or API. However, administrators can use a workaround:
To revoke a group access token:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > Access Tokens**.
1. Next to the group access token to revoke, select **Revoke**.
@@ -138,7 +146,7 @@ The scope determines the actions you can perform when you authenticate with a gr
To enable or disable group access token creation for all sub-groups in a top-level group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Permissions and group features**.
1. Under **Permissions**, turn on or off **Users can create project access tokens and group access tokens in this group**.
@@ -154,8 +162,9 @@ to groups instead of projects. Bot users for groups:
- Do not count as licensed seats.
- Can have a maximum role of Owner for a group. For more information, see
[Create a group access token](../../../api/group_access_tokens.md#create-a-group-access-token).
-- The username is set to `group_{project_id}_bot` for the first access token. For example, `project_123_bot`.
-- The email is set to `group{group_id}_bot@noreply.{Gitlab.config.gitlab.host}`. For example, `group123_bot@noreply.example.com`.
-- All other properties are similar to [bot users for projects](../../project/settings/project_access_tokens.md#bot-users-for-projects)
+- Have a username set to `group_{group_id}_bot` for the first access token. For example, `group_123_bot`.
+- Have an email set to `group{group_id}_bot@noreply.{Gitlab.config.gitlab.host}`. For example, `group123_bot@noreply.example.com`.
+
+All other properties are similar to [bot users for projects](../../project/settings/project_access_tokens.md#bot-users-for-projects).
For more information, see [Bot users for projects](../../project/settings/project_access_tokens.md#bot-users-for-projects).
diff --git a/doc/user/group/settings/import_export.md b/doc/user/group/settings/import_export.md
index eb94a181647..7a398c7d086 100644
--- a/doc/user/group/settings/import_export.md
+++ b/doc/user/group/settings/import_export.md
@@ -28,7 +28,7 @@ Prerequisite:
To enable import and export for a group:
-1. On the top bar, select **Menu > Admin**.
+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.
@@ -44,6 +44,7 @@ be imported into the desired group structure.
- 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
@@ -55,6 +56,7 @@ The following items are exported:
- 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)
@@ -77,7 +79,7 @@ Prerequisites:
To export the contents of a group:
-1. On the top bar, select **Menu > Groups** and find your 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)
diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md
index 12434de5efc..657ef361bd5 100644
--- a/doc/user/group/subgroups/index.md
+++ b/doc/user/group/subgroups/index.md
@@ -56,7 +56,7 @@ the private subgroup.
To view the subgroups of a group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. Select the **Subgroups and projects** tab.
1. To view a nested subgroup, expand a subgroup in the hierarchy list.
@@ -84,7 +84,7 @@ You cannot host a GitLab Pages subgroup website with a top-level domain name. Fo
To create a subgroup:
-1. On the top bar, select **Menu > Groups** and find and select the parent group to add a subgroup to.
+1. On the top bar, select **Main menu > Groups** and find and select the parent group to add a subgroup to.
1. On the parent group's overview page, in the top right, select **New subgroup**.
1. Select **Create group**.
1. Fill in the fields. View a list of [reserved names](../../reserved_names.md) that cannot be used as group names.
@@ -98,13 +98,13 @@ default:
To change who can create subgroups on a group:
- As a user with the Owner role on the group:
- 1. On the top bar, select **Menu > Groups** and find your group.
+ 1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Permissions and group features**.
1. Select a role from **Roles allowed to create subgroups**.
1. Select **Save changes**.
- As an administrator:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Groups**.
1. In the group's row select **Edit**.
1. Select a role from **Allowed to create subgroups**.
@@ -159,7 +159,7 @@ Group permissions for a member can be changed only by:
To see if a member has inherited the permissions from a parent group:
-1. On the top bar, select **Menu > Groups** and find the group.
+1. On the top bar, select **Main menu > Groups** and find the group.
1. Select **Group information > Members**.
Members list for an example subgroup _Four_:
@@ -201,7 +201,7 @@ role on an ancestor group, add the user to the subgroup again with a higher role
## Mention subgroups
Mentioning subgroups ([`@<subgroup_name>`](../../discussions/index.md#mentions)) in issues, commits, and merge requests
-notifies all members of that group. Mentioning works the same as for projects and groups, and you can choose the group
+notifies all direct members of that group. Inherited members of a sub-group are not notified by mentions. Mentioning works the same as for projects and groups, and you can choose the group
of people to be notified.
<!-- ## Troubleshooting
diff --git a/doc/user/group/value_stream_analytics/index.md b/doc/user/group/value_stream_analytics/index.md
index 3e41b7b63cc..9078874d32c 100644
--- a/doc/user/group/value_stream_analytics/index.md
+++ b/doc/user/group/value_stream_analytics/index.md
@@ -35,7 +35,7 @@ Prerequisite:
To view value stream analytics for your group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Value stream**.
1. To view metrics for a particular stage, select a stage below the **Filter results** text box.
1. Optional. Filter the results:
@@ -73,7 +73,7 @@ To view deployment metrics, you must have a
To view the DORA metrics and key metrics:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Value stream**.
1. Optional. Filter the results:
1. Select the **Filter results** text box.
@@ -125,7 +125,7 @@ In GitLab 13.8 and earlier, deployment frequency metrics are calculated based on
### How value stream analytics aggregates data
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/335391) in GitLab 14.5 [with a flag](../../../administration/feature_flags.md) named `use_vsa_aggregated_tables`. Disabled by default.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/335391) in GitLab 14.5.
> - Filter by stop date toggle [added](https://gitlab.com/gitlab-org/gitlab/-/issues/352428) in GitLab 14.9
> - Data refresh badge [added](https://gitlab.com/gitlab-org/gitlab/-/issues/341739) in GitLab 14.9
> - Filter by stop date toggle [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/84356) in GitLab 14.9
@@ -154,7 +154,7 @@ Value stream analytics shows the median time spent by issues or merge requests i
To view the median time spent in each stage by a group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Value stream**.
1. Optional. Filter the results:
1. Select the **Filter results** text box.
@@ -251,7 +251,7 @@ You can change the name of a project environment in your GitLab CI/CD configurat
When you create a value stream, you can use GitLab default stages and hide or re-order them to customize. You can also
create custom stages in addition to those provided in the default template.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Value Stream**.
1. Select **Create new Value Stream**.
1. Enter a name for the value stream.
@@ -275,7 +275,7 @@ If you have recently upgraded to GitLab Premium, it can take up to 30 minutes fo
When you create a value stream, you can create and add custom stages that align with your own development workflows.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Value Stream**.
1. Select **Create value stream**.
1. For each stage:
@@ -301,7 +301,7 @@ time from a staging environment to production, you could use the following label
After you create a value stream, you can customize it to suit your purposes. To edit a value stream:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Value Stream**.
1. In the top right, select the dropdown list, and select a value stream.
1. Next to the value stream dropdown list, select **Edit**.
@@ -320,7 +320,7 @@ After you create a value stream, you can customize it to suit your purposes. To
To delete a custom value stream:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Value Stream**.
1. In the top right, select the dropdown list and then select the value stream you would like to delete.
1. Select **Delete (name of value stream)**.
@@ -336,7 +336,7 @@ To delete a custom value stream:
The **Total time chart** shows the average number of days it takes for development cycles to complete.
The chart shows data for the last 500 workflow items.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Value stream**.
1. Above the **Filter results** box, select a stage:
- To view a summary of the cycle time for all stages, select **Overview**.
@@ -349,13 +349,19 @@ The chart shows data for the last 500 workflow items.
- In the **From** field, select a start date.
- In the **To** field, select an end date.
-## Tasks by type chart
+## View tasks by type
-This chart shows a cumulative count of issues and merge requests per day.
+The **Tasks by type** chart displays the cumulative number of issues and merge requests per day for your group.
-This chart uses the global page filters for displaying data based on the selected
-group, projects, and time frame. The chart defaults to showing counts for issues but can be
-toggled to show data for merge requests and further refined for specific group-level labels.
+The chart uses the global page filters to display data based on the selected
+group, projects, and time frame.
-By default the top group-level labels (max. 10) are pre-selected, with the ability to
-select up to a total of 15 labels.
+To view tasks by type:
+
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Analytics > Value stream**.
+1. Below the **Filter results** text box, select **Overview**. The **Tasks by type** chart displays below the **Total time** chart.
+1. To switch between the task type, select the **Settings** (**{settings}**) dropdown list
+ and select **Issues** or **Merge Requests**.
+1. To add or remove labels, select the **Settings** (**{settings}**) dropdown list
+ and select or search for a label. By default the top group-level labels (maximum 10) are selected. You can select a maximum of 15 labels.
diff --git a/doc/user/infrastructure/clusters/connect/index.md b/doc/user/infrastructure/clusters/connect/index.md
index 37e1024a32c..af728cb5c21 100644
--- a/doc/user/infrastructure/clusters/connect/index.md
+++ b/doc/user/infrastructure/clusters/connect/index.md
@@ -34,17 +34,17 @@ your cluster's level.
**Project-level clusters:**
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Infrastructure > Kubernetes clusters**.
**Group-level clusters:**
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Kubernetes**.
**Instance-level clusters:**
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Kubernetes**.
## Security implications for clusters connected with certificates
diff --git a/doc/user/infrastructure/clusters/connect/new_civo_cluster.md b/doc/user/infrastructure/clusters/connect/new_civo_cluster.md
index ecf93958b1e..f9feef6329c 100644
--- a/doc/user/infrastructure/clusters/connect/new_civo_cluster.md
+++ b/doc/user/infrastructure/clusters/connect/new_civo_cluster.md
@@ -6,7 +6,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Create a Civo Kubernetes cluster
-Every new Civo account receives [$250 in credit](https://civo.com/signup) to get started with the GitLab integration with Civo Kubernetes. You can also use a marketplace app to install GitLab on your Civo Kubernetes cluster.
+Every new Civo account receives [$250 in credit](https://dashboard.civo.com/signup) to get started with the GitLab integration with Civo Kubernetes. You can also use a marketplace app to install GitLab on your Civo Kubernetes cluster.
Learn how to create a new cluster on Civo Kubernetes through
[Infrastructure as Code (IaC)](../../index.md). This process uses the Civo
@@ -15,7 +15,7 @@ by using the GitLab agent for Kubernetes.
**Prerequisites:**
-- A [Civo account](https://civo.com/signup).
+- A [Civo account](https://dashboard.civo.com/signup).
- [A runner](https://docs.gitlab.com/runner/install/) you can use to run the GitLab CI/CD pipeline.
**Steps:**
@@ -35,7 +35,8 @@ Start by [importing the example project by URL](../../../project/import/repo_by_
To import the project:
-1. On the top bar, select **Menu > Create new project**.
+1. In GitLab, on the top bar, select **Main menu > Projects > View all projects**.
+1. On the right of the page, select **New project**.
1. Select **Import project**.
1. Select **Repository by URL**.
1. For the **Git repository URL**, enter `https://gitlab.com/civocloud/gitlab-terraform-civo.git`.
@@ -64,7 +65,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_CIVO_TOKEN` to the [token](https://www.civo.com/account/security) from your Civo account.
+1. Set the variable `BASE64_CIVO_TOKEN` to the token from your Civo account.
1. Set the variable `TF_VAR_agent_token` to the agent token you received in the previous task.
1. Set the variable `TF_VAR_kas_address` to the agent server address in the previous task.
@@ -95,7 +96,7 @@ After configuring your project, manually trigger the provisioning of your cluste
When the pipeline finishes successfully, you can see your new cluster:
-- In Civo dashboard: on your [Kubernetes tab](https://www.civo.com/account/kubernetes).
+- In Civo dashboard: on your Kubernetes tab.
- In GitLab: from your project's sidebar, select **Infrastructure > Kubernetes clusters**.
## Use your cluster
diff --git a/doc/user/infrastructure/clusters/connect/new_eks_cluster.md b/doc/user/infrastructure/clusters/connect/new_eks_cluster.md
index 2f5967bd7ee..126968baee7 100644
--- a/doc/user/infrastructure/clusters/connect/new_eks_cluster.md
+++ b/doc/user/infrastructure/clusters/connect/new_eks_cluster.md
@@ -34,7 +34,8 @@ Start by [importing the example project by URL](../../../project/import/repo_by_
To import the project:
-1. On the top bar, select **Menu > Create new project**.
+1. In GitLab, on the top bar, select **Main menu > Projects > View all projects**.
+1. On the right of the page, select **New project**.
1. Select **Import project**.
1. Select **Repository by URL**.
1. For the **Git repository URL**, enter `https://gitlab.com/gitlab-org/configure/examples/gitlab-terraform-eks.git`.
@@ -82,7 +83,6 @@ contains other variables that you can override according to your needs:
- `TF_VAR_cluster_version`: Set the version of Kubernetes.
- `TF_VAR_instance_type`: Set the instance type for the Kubernetes nodes.
- `TF_VAR_instance_count`: Set the number of Kubernetes nodes.
-- `TF_VAR_agent_version`: Set the version of the GitLab agent.
- `TF_VAR_agent_namespace`: Set the Kubernetes namespace for the GitLab agent.
View the [AWS Terraform provider](https://registry.terraform.io/providers/hashicorp/aws/latest/docs) and the [Kubernetes Terraform provider](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs) documentation for further resource options.
diff --git a/doc/user/infrastructure/clusters/connect/new_gke_cluster.md b/doc/user/infrastructure/clusters/connect/new_gke_cluster.md
index 07d0c722d8b..a23a9e7a6e5 100644
--- a/doc/user/infrastructure/clusters/connect/new_gke_cluster.md
+++ b/doc/user/infrastructure/clusters/connect/new_gke_cluster.md
@@ -41,7 +41,8 @@ Start by [importing the example project by URL](../../../project/import/repo_by_
To import the project:
-1. On the top bar, select **Menu > Create new project**.
+1. In GitLab, on the top bar, select **Main menu > Projects > View all projects**.
+1. On the right of the page, select **New project**.
1. Select **Import project**.
1. Select **Repository by URL**.
1. For the **Git repository URL**, enter `https://gitlab.com/gitlab-org/configure/examples/gitlab-terraform-gke.git`.
@@ -107,7 +108,6 @@ contains other variables that you can override according to your needs:
- `TF_VAR_cluster_description`: Set a description for the cluster. We recommend setting this to `$CI_PROJECT_URL` to create a reference to your GitLab project on your GCP cluster detail page. This way you know which project was responsible for provisioning the cluster you see on the GCP dashboard.
- `TF_VAR_machine_type`: Set the machine type for the Kubernetes nodes.
- `TF_VAR_node_count`: Set the number of Kubernetes nodes.
-- `TF_VAR_agent_version`: Set the version of the GitLab agent.
- `TF_VAR_agent_namespace`: Set the Kubernetes namespace for the GitLab agent.
Refer to the [Google Terraform provider](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/provider_reference) and the [Kubernetes Terraform provider](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs) documentation for further resource options.
diff --git a/doc/user/infrastructure/clusters/manage/management_project_applications/certmanager.md b/doc/user/infrastructure/clusters/manage/management_project_applications/certmanager.md
index 51fd626ce0f..5f77b7e402a 100644
--- a/doc/user/infrastructure/clusters/manage/management_project_applications/certmanager.md
+++ b/doc/user/infrastructure/clusters/manage/management_project_applications/certmanager.md
@@ -30,8 +30,8 @@ And update the `applications/cert-manager/helmfile.yaml` with a valid email addr
```
NOTE:
-If your Kubernetes version is earlier than 1.20 and you are
-[migrating from GitLab Managed Apps to a cluster management project](../../../../clusters/migrating_from_gma_to_project_template.md),
+If your Kubernetes version is earlier than 1.20 and you are
+[migrating from GitLab Managed Apps to a cluster management project](../../../../clusters/migrating_from_gma_to_project_template.md),
then you can instead use `- path: applications/cert-manager-legacy/helmfile.yaml` to
take over an existing release of cert-manager v0.10.
diff --git a/doc/user/infrastructure/iac/index.md b/doc/user/infrastructure/iac/index.md
index 422552c5b71..d5eabb9ba46 100644
--- a/doc/user/infrastructure/iac/index.md
+++ b/doc/user/infrastructure/iac/index.md
@@ -64,7 +64,7 @@ In each GitLab major release (for example, 15.0), the latest templates replace t
To use a Terraform template:
-1. On the top bar, select **Menu > Projects** and find the project you want to integrate with Terraform.
+1. On the top bar, select **Main menu > Projects** and find the project you want to integrate with Terraform.
1. On the left sidebar, select **Repository > Files**.
1. Edit your `.gitlab-ci.yml` file, use the `include` attribute to fetch the Terraform template:
diff --git a/doc/user/infrastructure/iac/terraform_state.md b/doc/user/infrastructure/iac/terraform_state.md
index 4e78e0bbed5..d4fea6b7dba 100644
--- a/doc/user/infrastructure/iac/terraform_state.md
+++ b/doc/user/infrastructure/iac/terraform_state.md
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2673) in GitLab 13.0.
Terraform uses state files to store details about your infrastructure configuration.
-With Terraform remote [backends](https://www.terraform.io/language/settings/backends),
+With Terraform remote [backends](https://www.terraform.io/language/settings/backends/configuration),
you can store the state file in a remote and shared store.
GitLab provides a [Terraform HTTP backend](https://www.terraform.io/language/settings/backends/http)
@@ -109,7 +109,7 @@ inconsistent. Instead, use a remote storage resource.
[initialized for CI/CD](#initialize-a-terraform-state-as-a-backend-by-using-gitlab-cicd).
1. Copy a pre-populated Terraform `init` command:
- 1. On the top bar, select **Menu > Projects** and find your project.
+ 1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Infrastructure > Terraform**.
1. Next to the environment you want to use, select **Actions**
(**{ellipsis_v}**) and select **Copy Terraform init command**.
@@ -287,7 +287,7 @@ To read the Terraform state in the target project, you need at least the Develop
To view Terraform state files:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Infrastructure > Terraform**.
[An epic exists](https://gitlab.com/groups/gitlab-org/-/epics/4563) to track improvements to this UI.
diff --git a/doc/user/infrastructure/iac/troubleshooting.md b/doc/user/infrastructure/iac/troubleshooting.md
index e187fe54136..3286b550507 100644
--- a/doc/user/infrastructure/iac/troubleshooting.md
+++ b/doc/user/infrastructure/iac/troubleshooting.md
@@ -97,9 +97,7 @@ As a result, to create a plan and later use the same plan in another CI job, you
`Error: Error acquiring the state lock` errors when using `-backend-config=password=$CI_JOB_TOKEN`.
This happens because the value of `$CI_JOB_TOKEN` is only valid for the duration of the current job.
-Another possible error message for the same problem could be: `Error: Error loading state: HTTP remote state endpoint requires auth`.
-
-As a workaround, use [http backend configuration variables](https://www.terraform.io/docs/language/settings/backends/http.html#configuration-variables) in your CI job,
+As a workaround, use [http backend configuration variables](https://www.terraform.io/language/settings/backends/http#configuration-variables) in your CI job,
which is what happens behind the scenes when following the
[Get started using GitLab CI](terraform_state.md#initialize-a-terraform-state-as-a-backend-by-using-gitlab-cicd) instructions.
@@ -112,8 +110,8 @@ If you don't set `TF_STATE_NAME` or `TF_ADDRESS` in your job, the job fails with
To resolve this, ensure that either `TF_ADDRESS` or `TF_STATE_NAME` is accessible in the
job that returned the error:
-1. Configure the [CI/CD environment scope](../../../ci/variables/#add-a-cicd-variable-to-a-project) for the job.
-1. Set the job's [environment](../../../ci/yaml/#environment), matching the environment scope from the previous step.
+1. Configure the [CI/CD environment scope](../../../ci/variables/index.md#add-a-cicd-variable-to-a-project) for the job.
+1. Set the job's [environment](../../../ci/yaml/index.md#environment), matching the environment scope from the previous step.
### Error refreshing state: HTTP remote state endpoint requires auth
diff --git a/doc/user/instance/clusters/index.md b/doc/user/instance/clusters/index.md
index a5c1402b9ec..27aa3479b88 100644
--- a/doc/user/instance/clusters/index.md
+++ b/doc/user/instance/clusters/index.md
@@ -21,7 +21,7 @@ projects.
To view the instance level Kubernetes clusters:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Kubernetes**.
## Cluster precedence
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index 6a524fe206a..d61190fbd31 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -346,10 +346,13 @@ backslash `\`. Otherwise the diff highlight does not render correctly:
[View this topic in GitLab](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/user/markdown.md#math).
Math written in LaTeX syntax is rendered with [KaTeX](https://github.com/KaTeX/KaTeX).
+_KaTeX only supports a [subset](https://katex.org/docs/supported.html) of LaTeX._
+This syntax also works for the Asciidoctor `:stem: latexmath`. For details, see
+the [Asciidoctor user manual](https://asciidoctor.org/docs/user-manual/#activating-stem-support).
-Math written between dollar signs `$` is rendered inline with the text. Math written
-in a [code block](#code-spans-and-blocks) with the language declared as `math` is rendered
-on a separate line:
+Math written between dollar signs with backticks (``$`...`$``) is rendered
+inline with the text. Math written in a [code block](#code-spans-and-blocks) with
+the language declared as `math` is rendered on a separate line:
````markdown
This math is inline: $`a^2+b^2=c^2`$.
@@ -369,10 +372,44 @@ This math is on a separate line:
a^2+b^2=c^2
```
-_KaTeX only supports a [subset](https://katex.org/docs/supported.html) of LaTeX._
+#### LaTeX-compatible fencing
-This syntax also works for the Asciidoctor `:stem: latexmath`. For details, see
-the [Asciidoctor user manual](https://asciidoctor.org/docs/user-manual/#activating-stem-support).
+> Introduced in GitLab 15.4 [with a flag](../administration/feature_flags.md) named `markdown_dollar_math`. Disabled by default.
+
+[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,
+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.
+
+Math written between dollar signs (`$...$`) is rendered
+inline with the text. Math written between double dollar signs (`$$...$$`) is rendered
+on a separate line:
+
+````markdown
+This math is inline: $a^2+b^2=c^2$.
+
+This math is on a separate line: $$a^2+b^2=c^2$$
+
+This math is on a separate line:
+
+$$
+a^2+b^2=c^2
+$$
+````
+
+<!-- Uncomment the example below when the flag is enabled on GitLab.com -->
+<!-- This math is inline: $a^2+b^2=c^2$.
+
+This math is on a separate line: $$a^2+b^2=c^2$$
+
+This math is on a separate line:
+
+$$
+a^2+b^2=c^2
+$$ -->
### Task lists
@@ -611,9 +648,10 @@ Quote break.
If this section isn't rendered correctly, [view it in GitLab](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/user/markdown.md#multiline-blockquote).
GitLab Flavored Markdown extends the standard Markdown by also supporting multi-line blockquotes
-fenced by `>>>`:
+fenced by `>>>`, with a blank line before and after the block:
```markdown
+
>>>
If you paste a message from somewhere else
@@ -621,6 +659,7 @@ that spans multiple lines,
you can quote that without having to manually prepend `>` to every line!
>>>
+
```
>>>
@@ -772,6 +811,8 @@ Combined emphasis with **asterisks and _underscores_**.
Strikethrough uses two tildes. ~~Scratch this.~~
```
+<!-- markdownlint-disable MD050 -->
+
Emphasis, aka italics, with *asterisks* or _underscores_.
Strong emphasis, aka bold, with double **asterisks** or __underscores__.
@@ -780,6 +821,8 @@ Combined emphasis with **asterisks and _underscores_**.
Strikethrough uses two tildes. ~~Scratch this.~~
+<!-- markdownlint-enable MD050 -->
+
#### Multiple underscores in words and mid-word emphasis
If this section isn't rendered correctly,
@@ -1471,7 +1514,9 @@ Press <kbd>Enter</kbd> to go to the next page.
### Tables
-Tables are not part of the core Markdown spec, but they are part of GitLab Flavored Markdown.
+Tables are not part of the core Markdown specification, but are part of GitLab Flavored Markdown.
+
+#### Markdown
1. The first line contains the headers, separated by "pipes" (`|`).
1. The second line separates the headers from the cells.
@@ -1547,12 +1592,12 @@ but they do not render properly on `docs.gitlab.com`:
| cell 3 | <ul><li> - [ ] Task one </li><li> - [ ] Task two </li></ul> |
```
-#### Copy from spreadsheet and paste in Markdown
+##### Copy and paste from a spreadsheet
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27205) in GitLab 12.7.
If you're working in spreadsheet software (for example, Microsoft Excel, Google
-Sheets, or Apple Numbers), GitLab creates a Markdown table when you copy-and-paste
+Sheets, or Apple Numbers), GitLab creates a Markdown table when you copy and paste
from a spreadsheet. For example, suppose you have the
following spreadsheet:
@@ -1563,6 +1608,160 @@ entry and paste the spreadsheet:
![Paste to Markdown table](img/markdown_paste_table_v12_7.png)
+#### JSON
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86353) in GitLab 15.3.
+
+To render tables with JSON code blocks, use the following syntax:
+
+````markdown
+```json:table
+{}
+```
+````
+
+Watch the following video walkthrough of this feature:
+
+<div class="video-fallback">
+ See the video: <a href="https://www.youtube.com/watch?v=12yWKw1AdKY">Demo: JSON Tables in Markdown</a>.
+</div>
+<figure class="video-container">
+ <iframe src="https://www.youtube.com/embed/12yWKw1AdKY" frameborder="0" allowfullscreen="true"> </iframe>
+</figure>
+
+The `items` attribute is a list of objects representing the data points.
+
+````markdown
+```json:table
+{
+ "items" : [
+ {"a": "11", "b": "22", "c": "33"}
+ ]
+}
+```
+````
+
+To specify the table labels, use the `fields` attribute.
+
+````markdown
+```json:table
+{
+ "fields" : ["a", "b", "c"],
+ "items" : [
+ {"a": "11", "b": "22", "c": "33"}
+ ]
+}
+```
+````
+
+Not all elements of `items` must have corresponding values in `fields`.
+
+````markdown
+```json:table
+{
+ "fields" : ["a", "b", "c"],
+ "items" : [
+ {"a": "11", "b": "22", "c": "33"},
+ {"a": "211", "c": "233"}
+ ]
+}
+```
+````
+
+When `fields` is not explicitly specified, the labels are picked from the first element of `items`.
+
+````markdown
+```json:table
+{
+ "items" : [
+ {"a": "11", "b": "22", "c": "33"},
+ {"a": "211", "c": "233"}
+ ]
+}
+```
+````
+
+You can specify custom labels for `fields`.
+
+````markdown
+```json:table
+{
+ "fields" : [
+ {"key": "a", "label": "AA"},
+ {"key": "b", "label": "BB"},
+ {"key": "c", "label": "CC"}
+ ],
+ "items" : [
+ {"a": "11", "b": "22", "c": "33"},
+ {"a": "211", "b": "222", "c": "233"}
+ ]
+}
+```
+````
+
+You can enable sorting for individual elements of `fields`.
+
+````markdown
+```json:table
+{
+ "fields" : [
+ {"key": "a", "label": "AA", "sortable": true},
+ {"key": "b", "label": "BB"},
+ {"key": "c", "label": "CC"}
+ ],
+ "items" : [
+ {"a": "11", "b": "22", "c": "33"},
+ {"a": "211", "b": "222", "c": "233"}
+ ]
+}
+```
+````
+
+You can use the `filter` attribute to render a table with content filtered dynamically by user input.
+
+````markdown
+```json:table
+{
+ "fields" : [
+ {"key": "a", "label": "AA"},
+ {"key": "b", "label": "BB"},
+ {"key": "c", "label": "CC"}
+ ],
+ "items" : [
+ {"a": "11", "b": "22", "c": "33"},
+ {"a": "211", "b": "222", "c": "233"}
+ ],
+ "filter" : true
+}
+```
+````
+
+By default, every JSON table has the caption `Generated with JSON data`.
+You can override this caption by specifying the `caption` attribute.
+
+````markdown
+```json:table
+{
+ "items" : [
+ {"a": "11", "b": "22", "c": "33"}
+ ],
+ "caption" : "Custom caption"
+}
+```
+````
+
+If JSON is invalid, an error occurs.
+
+````markdown
+```json:table
+{
+ "items" : [
+ {"a": "11", "b": "22", "c": "33"}
+ ],
+}
+```
+````
+
## References
- This document leveraged heavily from the [Markdown-Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet).
diff --git a/doc/user/namespace/index.md b/doc/user/namespace/index.md
index 9ffc65a4e8e..138b0a355ad 100644
--- a/doc/user/namespace/index.md
+++ b/doc/user/namespace/index.md
@@ -6,11 +6,23 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Namespaces
-In GitLab, a *namespace* organizes related projects together.
+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.
+
GitLab has two types of namespaces:
-- A *personal* namespace, which is based on your username. Projects under a personal namespace must be configured one at a time.
-- A *group* or *subgroup* namespace. In these namespaces, you can manage multiple projects at once.
+- 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).
+ - 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.
+
+- A *group* or *subgroup* namespace:
+ - 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**.
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 1b14582a3f0..0a7e9d6395b 100644
--- a/doc/user/operations_dashboard/index.md
+++ b/doc/user/operations_dashboard/index.md
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
The Operations Dashboard provides a summary of each project's operational health,
including pipeline and alert status.
-To access the dashboard, on the top bar, select **Menu > Operations**.
+To access the dashboard, on the top bar, select **Main menu > Operations**.
## Adding a project to the dashboard
diff --git a/doc/user/packages/composer_repository/index.md b/doc/user/packages/composer_repository/index.md
index 4fc55d18253..84f544ec4ad 100644
--- a/doc/user/packages/composer_repository/index.md
+++ b/doc/user/packages/composer_repository/index.md
@@ -127,7 +127,7 @@ To publish the package with a deploy token:
- `<tag>` is the Git tag name of the version you want to publish.
To publish a branch, use `branch=<branch>` instead of `tag=<tag>`.
-You can view the published package by going to **Packages & Registries > Package Registry** and
+You can view the published package by going to **Packages and registries > Package Registry** and
selecting the **Composer** tab.
## Publish a Composer package by using CI/CD
@@ -145,11 +145,12 @@ You can publish a Composer package to the Package Registry as part of your CI/CD
script:
- apk add curl
- 'curl --header "Job-Token: $CI_JOB_TOKEN" --data tag=<tag> "${CI_API_V4_URL}/projects/$CI_PROJECT_ID/packages/composer"'
+ environment: production
```
1. Run the pipeline.
-To view the published package, go to **Packages & Registries > Package Registry** and select the **Composer** tab.
+To view the published package, go to **Packages and registries > Package Registry** and select the **Composer** tab.
### Use a CI/CD template
diff --git a/doc/user/packages/conan_repository/index.md b/doc/user/packages/conan_repository/index.md
index 7260dbb616c..ed106685b62 100644
--- a/doc/user/packages/conan_repository/index.md
+++ b/doc/user/packages/conan_repository/index.md
@@ -310,6 +310,7 @@ create_package:
- conan new <package-name>/0.1 -t
- conan create . <group-name>+<project-name>/stable
- CONAN_LOGIN_USERNAME=ci_user CONAN_PASSWORD=${CI_JOB_TOKEN} conan upload <package-name>/0.1@<group-name>+<project-name>/stable --all --remote=gitlab
+ environment: production
```
Additional Conan images to use as the basis of your CI file are available in the
@@ -389,7 +390,7 @@ There are two ways to remove a Conan package from the GitLab Package Registry.
- From the GitLab user interface:
- Go to your project's **Packages & Registries > Package Registry**. Remove the
+ Go to your project's **Packages and registries > Package Registry**. Remove the
package by selecting **Remove repository** (**{remove}**).
## Search for Conan packages in the Package Registry
diff --git a/doc/user/packages/container_registry/index.md b/doc/user/packages/container_registry/index.md
index a203de2ed2c..bfbc400f4dd 100644
--- a/doc/user/packages/container_registry/index.md
+++ b/doc/user/packages/container_registry/index.md
@@ -26,7 +26,7 @@ Registry for your GitLab instance, visit the
You can view the Container Registry for a project or group.
1. Go to your project or group.
-1. Go to **Packages & Registries > Container Registry**.
+1. Go to **Packages and registries > Container Registry**.
You can search, sort, filter, and [delete](#delete-images-from-within-gitlab)
containers on this page. You can share a filtered view by copying the URL from your browser.
@@ -40,7 +40,7 @@ If a project is public, so is the Container Registry.
You can view a list of tags associated with a given container image:
1. Go to your project or group.
-1. Go to **Packages & Registries > Container Registry**.
+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,
@@ -55,7 +55,7 @@ tags on this page. You can share a filtered view by copying the URL from your br
To download and run a container image hosted in the GitLab Container Registry:
1. Copy the link to your container image:
- - Go to your project or group's **Packages & Registries > Container Registry**
+ - Go to your project or group's **Packages and registries > Container Registry**
and find the image you want.
- Next to the image name, select **Copy**.
@@ -67,6 +67,8 @@ 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.
+
For more information on running Docker containers, visit the
[Docker documentation](https://docs.docker.com/engine/userguide/intro/).
@@ -97,15 +99,9 @@ registry.example.com/mynamespace/myproject/image:latest
registry.example.com/mynamespace/myproject/my/image:rc1
```
-## Build and push images by using Docker commands
-
-To build and push to the Container Registry, you can use Docker commands.
-
-### Authenticate with the Container Registry
+## Authenticate with the Container Registry
-Before you can build and push images, you must authenticate with the Container Registry.
-
-To authenticate, you can use:
+To authenticate with the Container Registry, you can use:
- A [personal access token](../../profile/personal_access_tokens.md).
- A [deploy token](../../project/deploy_tokens/index.md).
@@ -121,7 +117,9 @@ To authenticate, run the `docker` command. For example:
docker login registry.example.com -u <username> -p <token>
```
-### Build and push images by using Docker commands
+## Build and push images by using Docker commands
+
+Before you can build and push images, you must [authenticate](#authenticate-with-the-container-registry) with the Container Registry.
To build and push to the Container Registry:
@@ -139,7 +137,7 @@ To build and push to the Container Registry:
docker push registry.example.com/group/project/image
```
-To view these commands, go to your project's **Packages & Registries > Container Registry**.
+To view these commands, go to your project's **Packages and registries > Container Registry**.
## Build and push by using GitLab CI/CD
@@ -301,6 +299,7 @@ deploy:
- ./deploy.sh
only:
- main
+ environment: production
```
NOTE:
@@ -394,7 +393,7 @@ images), are automatically scheduled for deletion after 24 hours if left unrefer
To delete images from within GitLab:
-1. Navigate to your project's or group's **Packages & Registries > Container Registry**.
+1. Navigate 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:
@@ -508,7 +507,7 @@ You can, however, remove the Container Registry for a project:
and disable **Container Registry**.
1. Select **Save changes**.
-The **Packages & Registries > Container Registry** entry is removed from the project's sidebar.
+The **Packages and registries > Container Registry** entry is removed from the project's sidebar.
## Change visibility of the Container Registry
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 8f90f42ad08..76e3da9538f 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
@@ -125,7 +125,7 @@ upgrading to [GitLab Premium or Ultimate](https://about.gitlab.com/upgrade/).
## Purchase additional data transfer
-Read more about managing your [data transfer limits](../../../subscriptions/gitlab_com/#purchase-more-storage-and-transfer).
+Read more about managing your [data transfer limits](../../../subscriptions/gitlab_com/index.md#purchase-more-storage-and-transfer).
## Related issues
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 788e57b6410..b3dd8da9b41 100644
--- a/doc/user/packages/container_registry/reduce_container_registry_storage.md
+++ b/doc/user/packages/container_registry/reduce_container_registry_storage.md
@@ -113,7 +113,7 @@ You can create a cleanup policy in [the API](#use-the-cleanup-policy-api) or the
To create a cleanup policy in the UI:
-1. For your project, go to **Settings > Packages & Registries**.
+1. For your project, go to **Settings > Packages and registries**.
1. Expand the **Clean up image tags** section.
1. Complete the fields.
@@ -206,7 +206,7 @@ For self-managed instances, those settings can be updated in the [Rails console]
They are also available in the [administrator area](../../admin_area/index.md):
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. Go to **Settings > CI/CD > Container Registry**.
### Use the cleanup policy API
@@ -219,7 +219,7 @@ Examples:
```shell
curl --request PUT --header 'Content-Type: application/json;charset=UTF-8' --header "PRIVATE-TOKEN: <your_access_token>" \
- --data-binary '{"container_expiration_policy_attributes":{"cadence":"1month","enabled":true,"keep_n":1,"older_than":"14d","name_regex":"","name_regex_delete":".*","name_regex_keep":".*-main"}}' \
+ --data-binary '{"container_expiration_policy_attributes":{"cadence":"1month","enabled":true,"keep_n":1,"older_than":"14d","name_regex":".*","name_regex_keep":".*-main"}}' \
"https://gitlab.example.com/api/v4/projects/2"
```
diff --git a/doc/user/packages/debian_repository/index.md b/doc/user/packages/debian_repository/index.md
index 1d846a60281..4143ab0881f 100644
--- a/doc/user/packages/debian_repository/index.md
+++ b/doc/user/packages/debian_repository/index.md
@@ -175,3 +175,43 @@ To install a package:
```shell
sudo apt-get -y install -t <codename> <package-name>
```
+
+## Download a source package
+
+To download a source package:
+
+1. Configure the repository:
+
+ If you are using a private project, add your [credentials](#authenticate-to-the-package-registry) to your apt configuration:
+
+ ```shell
+ echo 'machine gitlab.example.com login <username> password <your_access_token>' \
+ | sudo tee /etc/apt/auth.conf.d/gitlab_project.conf
+ ```
+
+ Download your distribution key:
+
+ ```shell
+ sudo mkdir -p /usr/local/share/keyrings
+ curl --header "PRIVATE-TOKEN: <your_access_token>" \
+ "https://gitlab.example.com/api/v4/projects/<project_id>/debian_distributions/<codename>/key.asc" \
+ | \
+ gpg --dearmor \
+ | \
+ sudo tee /usr/local/share/keyrings/<codename>-archive-keyring.gpg \
+ > /dev/null
+ ```
+
+ Add your project as a source:
+
+ ```shell
+ echo 'deb-src [ signed-by=/usr/local/share/keyrings/<codename>-archive-keyring.gpg ] https://gitlab.example.com/api/v4/projects/<project_id>/packages/debian <codename> <component1> <component2>' \
+ | sudo tee /etc/apt/sources.list.d/gitlab_project-sources.list
+ sudo apt-get update
+ ```
+
+1. Download the source package:
+
+ ```shell
+ sudo apt-get source -t <codename> <package-name>
+ ```
diff --git a/doc/user/packages/dependency_proxy/index.md b/doc/user/packages/dependency_proxy/index.md
index b570bba73e5..1310f8eedaa 100644
--- a/doc/user/packages/dependency_proxy/index.md
+++ b/doc/user/packages/dependency_proxy/index.md
@@ -37,8 +37,8 @@ For a list of planned additions, view the
To enable or turn off the Dependency Proxy for a group:
-1. On the top bar, select **Menu > Groups** and find your group.
-1. On the left sidebar, select **Settings > Packages & Registries**.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Settings > Packages and registries**.
1. Expand the **Dependency Proxy** section.
1. To enable the proxy, turn on **Enable Proxy**. To turn it off, turn the toggle off.
@@ -50,8 +50,8 @@ for the entire GitLab instance.
To view the Dependency Proxy:
-1. On the top bar, select **Menu > Groups** and find your group.
-1. On the left sidebar, select **Packages & Registries > Dependency Proxy**.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Packages and registries > Dependency Proxy**.
The Dependency Proxy is not available for projects.
@@ -175,8 +175,8 @@ You can also use [custom CI/CD variables](../../../ci/variables/index.md#custom-
To store a Docker image in Dependency Proxy storage:
-1. On the top bar, select **Menu > Groups** and find your group.
-1. On the left sidebar, select **Packages & Registries > Dependency Proxy**.
+1. On the top bar, select **Main menu > Groups** and find your group.
+1. On the left sidebar, select **Packages and registries > Dependency Proxy**.
1. Copy the **Dependency Proxy image prefix**.
1. Use one of these commands. In these examples, the image is `alpine:latest`.
1. You can also pull images by digest to specify exactly which version of an image to pull.
diff --git a/doc/user/packages/dependency_proxy/reduce_dependency_proxy_storage.md b/doc/user/packages/dependency_proxy/reduce_dependency_proxy_storage.md
index 839684da875..fecf60feeef 100644
--- a/doc/user/packages/dependency_proxy/reduce_dependency_proxy_storage.md
+++ b/doc/user/packages/dependency_proxy/reduce_dependency_proxy_storage.md
@@ -33,7 +33,7 @@ image or tag from Docker Hub.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/340777) in GitLab 14.6
You can enable an automatic time-to-live (TTL) policy for the Dependency Proxy from the user
-interface. To do this, navigate to your group's **Settings > Packages & Registries > Dependency Proxy**
+interface. To do this, navigate to your group's **Settings > Packages and registries > Dependency Proxy**
and enable the setting to automatically clear items from the cache after 90 days.
### Enable cleanup policies with GraphQL
diff --git a/doc/user/packages/generic_packages/index.md b/doc/user/packages/generic_packages/index.md
index d4acb14b9ca..312a2c119d6 100644
--- a/doc/user/packages/generic_packages/index.md
+++ b/doc/user/packages/generic_packages/index.md
@@ -123,7 +123,7 @@ or the UI.
In the UI:
-1. For your group, go to **Settings > Packages & Registries**.
+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
@@ -215,7 +215,7 @@ It also demonstrates how to manage a semantic version for the generic package: s
### Internal Server error on large file uploads to S3
-S3-compatible object storage [limits the size of a single PUT request to 5GB](https://docs.aws.amazon.com/AmazonS3/latest/userguide/upload-objects.html). If the `aws_signature_version` is set to `2` in the [object storage connection settings](../../../administration/object_storage.md), attempting to publish a package file larger than the 5GB limit can result in a `HTTP 500: Internal Server Error` response.
+S3-compatible object storage [limits the size of a single PUT request to 5GB](https://docs.aws.amazon.com/AmazonS3/latest/userguide/upload-objects.html). If the `aws_signature_version` is set to `2` in the [object storage connection settings](../../../administration/object_storage.md), attempting to publish a package file larger than the 5GB limit can result in a `HTTP 500: Internal Server Error` response.
If you are receiving `HTTP 500: Internal Server Error` responses when publishing large files to S3, set the `aws_signature_version` to `4`:
diff --git a/doc/user/packages/harbor_container_registry/index.md b/doc/user/packages/harbor_container_registry/index.md
new file mode 100644
index 00000000000..720e274aee5
--- /dev/null
+++ b/doc/user/packages/harbor_container_registry/index.md
@@ -0,0 +1,69 @@
+---
+stage: Package
+group: Package
+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
+---
+
+# Harbor Registry **(FREE)**
+
+You can integrate the [Harbor container registry](../../../user/project/integrations/harbor.md#harbor-container-registry-integration) into GitLab and use Harbor as the container registry for your GitLab project to store images.
+
+## View the Harbor Registry
+
+You can view the Harbor Registry for a project or group.
+
+1. On the top bar, select **Main menu > Projects/Groups**.
+1. Go to the project or group that you are interested in.
+1. On the left sidebar, select **Packages and registries > Harbor Registry**.
+
+You can search, sort, and filter images on this page. You can share a filtered view by copying the URL from your browser.
+
+At the project level, you can see **CLI Commands** in the upper right corner, where you can copy
+corresponding commands to log in, build images, and push images. **CLI Commands** is not shown at
+the group level.
+
+NOTE:
+Default settings for the Harbor integration at the project level are inherited from the group level.
+
+## Use images from the Harbor Registry
+
+To download and run a Harbor image hosted in the GitLab Harbor Registry:
+
+1. Copy the link to your container image:
+ 1. Go to your project or group's **Packages and registries > Harbor Registry** and find the image you want.
+ 1. Click the **Copy** icon next to the image name.
+
+1. Use the command to run the container image you want.
+
+## View the tags of a specific artifact
+
+To view the list of tags associated with a specific artifact:
+
+1. Go to your project or group.
+1. Go to **Packages and registries > Harbor Registry**.
+1. Click the image name to view its artifacts.
+1. Select the artifact you want.
+
+This brings up the list of tags. You can view the tag count and the time published.
+
+You can also copy the tag URL and use it to pull the corresponding artifact.
+
+## Build and push images by using commands
+
+To build and push to the Harbor Registry:
+
+1. Authenticate with the Harbor Registry.
+1. Run the command to build or push.
+
+To view these commands, go to your project's **Packages and registries > Harbor Registry > CLI Commands**.
+
+## Disable the Harbor Registry for a project
+
+To remove the Harbor Registry for a project:
+
+1. Go to your project/group's **Settings > Integrations** page.
+1. Click **Harbor** under **Active integrations**.
+1. Clear the **Active** checkbox under **Enable integration**.
+1. Select **Save changes**.
+
+The **Packages and registries > Harbor Registry** entry is removed from the sidebar.
diff --git a/doc/user/packages/infrastructure_registry/index.md b/doc/user/packages/infrastructure_registry/index.md
index e6a179c9d12..48cc7b9dea9 100644
--- a/doc/user/packages/infrastructure_registry/index.md
+++ b/doc/user/packages/infrastructure_registry/index.md
@@ -18,7 +18,7 @@ projects.
To view packages within your project:
1. Go to the project.
-1. Go to **Packages & Registries > Infrastructure Registry**.
+1. Go to **Packages and registries > Infrastructure Registry**.
You can search, sort, and filter packages on this page.
@@ -49,7 +49,7 @@ You can see the pipeline that published the package as well as the commit and th
To download a package:
-1. Go to **Packages & Registries > Infrastructure Registry**.
+1. Go to **Packages and registries > Infrastructure Registry**.
1. Select the name of the package you want to download.
1. In the **Activity** section, select the name of the package you want to download.
@@ -64,7 +64,7 @@ You can delete packages by using [the API](../../../api/packages.md#delete-a-pro
To delete a package in the UI, from your project:
-1. Go to **Packages & Registries > Infrastructure Registry**.
+1. Go to **Packages and registries > Infrastructure Registry**.
1. Find the name of the package you want to delete.
1. Select **Delete**.
@@ -75,7 +75,7 @@ The package is permanently deleted.
The Infrastructure Registry is automatically enabled.
For self-managed instances, a GitLab administrator can
-[disable](../../../administration/packages/index.md) **Packages & Registries**,
+[disable](../../../administration/packages/index.md) **Packages and registries**,
which removes this menu item from the sidebar.
You can also remove the Infrastructure Registry for a specific project:
diff --git a/doc/user/packages/maven_repository/index.md b/doc/user/packages/maven_repository/index.md
index eaa04404a1f..957374245d2 100644
--- a/doc/user/packages/maven_repository/index.md
+++ b/doc/user/packages/maven_repository/index.md
@@ -605,7 +605,7 @@ To publish a package by using Gradle:
gradle publish
```
-Now navigate to your project's **Packages & Registries** page and view the published artifacts.
+Now navigate to your project's **Packages and registries** page and view the published artifacts.
### Publishing a package with the same name or version
@@ -624,7 +624,7 @@ To prevent users from publishing duplicate Maven packages, you can use the [Grap
In the UI:
-1. For your group, go to **Settings > Packages & Registries**.
+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.
@@ -670,14 +670,20 @@ Downloading from gitlab-maven: http://gitlab.example.com/api/v4/projects/PROJECT
### Use Maven with `mvn dependency:get`
-You can install packages by using the Maven commands directly.
+You can install packages by using the Maven `dependency:get` [command](https://maven.apache.org/plugins/maven-dependency-plugin/get-mojo.html) directly.
1. In your project directory, run:
```shell
- mvn dependency:get -Dartifact=com.nickkipling.app:nick-test-app:1.1-SNAPSHOT
+ 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).
+
+NOTE:
+The repository IDs in the command(`gitlab-maven`) and the `settings.xml` file must match.
+
The message should show that the package is downloading from the Package Registry:
```shell
@@ -697,9 +703,70 @@ dependencies {
}
```
+### Request forwarding to Maven Central
+
+> [Introduced](<https://gitlab.com/gitlab-org/gitlab/-/issues/362657>) behind a [feature flag](../../feature_flags.md), disabled by default in GitLab 15.4
+
+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.
+
+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 so that it will 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](#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)
+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.
+
+#### Setting GitLab as a mirror for the central proxy
+
+To ensure all package requests are sent to GitLab instead of Maven Central,
+you can override Maven Central as the central repository by adding a `<mirror>`
+section to your `settings.xml`:
+
+```xml
+<settings>
+ <servers>
+ <server>
+ <id>central-proxy</id>
+ <configuration>
+ <httpHeaders>
+ <property>
+ <name>Private-Token</name>
+ <value>{personal_access_token}</value>
+ </property>
+ </httpHeaders>
+ </configuration>
+ </server>
+ </servers>
+ <mirrors>
+ <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>
+ <mirrorOf>central</mirrorOf>
+ </mirror>
+ </mirrors>
+</settings>
+```
+
## Remove a package
-For your project, go to **Packages & Registries > Package Registry**.
+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.
diff --git a/doc/user/packages/npm_registry/index.md b/doc/user/packages/npm_registry/index.md
index 28de06b2d8a..678f5681890 100644
--- a/doc/user/packages/npm_registry/index.md
+++ b/doc/user/packages/npm_registry/index.md
@@ -284,7 +284,7 @@ To upload an npm package to your project, run this command:
npm publish
```
-To view the package, go to your project's **Packages & Registries**.
+To view the package, go to your project's **Packages and registries**.
You can also define `"publishConfig"` for your project in `package.json`. For example:
@@ -325,6 +325,7 @@ 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
diff --git a/doc/user/packages/nuget_repository/index.md b/doc/user/packages/nuget_repository/index.md
index 2e182d20811..5b1e5bbd304 100644
--- a/doc/user/packages/nuget_repository/index.md
+++ b/doc/user/packages/nuget_repository/index.md
@@ -403,6 +403,7 @@ updated:
- dotnet nuget push "bin/Release/*.nupkg" --source gitlab
only:
- main
+ environment: production
```
1. Commit the changes and push it to your GitLab repository to trigger a new CI/CD build.
diff --git a/doc/user/packages/package_registry/index.md b/doc/user/packages/package_registry/index.md
index 7748780d0e4..fe19c549536 100644
--- a/doc/user/packages/package_registry/index.md
+++ b/doc/user/packages/package_registry/index.md
@@ -26,7 +26,7 @@ Learn how to use the GitLab Package Registry to build your own custom package wo
You can view packages for your project or group.
1. Go to the project or group.
-1. Go to **Packages & Registries > Package Registry**.
+1. Go to **Packages and registries > Package Registry**.
You can search, sort, and filter packages on this page. You can share your search results by copying
and pasting the URL from your browser.
@@ -99,7 +99,7 @@ For information on reducing your storage use for the Package Registry, see
The Package Registry is automatically enabled.
If you are using a self-managed instance of GitLab, your administrator can remove
-the menu item, **Packages & Registries**, from the GitLab sidebar. For more information,
+the menu item, **Packages and registries**, from the GitLab sidebar. For more information,
see the [administration documentation](../../../administration/packages/index.md).
You can also remove the Package Registry for your project specifically:
@@ -109,7 +109,7 @@ You can also remove the Package Registry for your project specifically:
**Packages** feature.
1. Select **Save changes**.
-The **Packages & Registries > Package Registry** entry is removed from the sidebar.
+The **Packages and registries > Package Registry** entry is removed from the sidebar.
## Package Registry visibility permissions
diff --git a/doc/user/packages/package_registry/reduce_package_registry_storage.md b/doc/user/packages/package_registry/reduce_package_registry_storage.md
index cd7dd062f60..d85992fe05d 100644
--- a/doc/user/packages/package_registry/reduce_package_registry_storage.md
+++ b/doc/user/packages/package_registry/reduce_package_registry_storage.md
@@ -7,12 +7,12 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Reduce Package Registry Storage **(FREE)**
Without cleanup, package registries become large over time. When a large number of packages and
-their files are added:
+their assets are added:
- Fetching the list of packages becomes slower.
- They take up a large amount of storage space on the server, impacting your [storage usage quota](../../usage_quotas.md).
-We recommend deleting unnecessary packages and files. This page offers examples of how to do so.
+We recommend deleting unnecessary packages and assets. This page offers examples of how to do so.
## Check Package Registry Storage Use
@@ -29,40 +29,40 @@ You can delete packages by using [the API](../../../api/packages.md#delete-a-pro
To delete a package in the UI, from your group or project:
-1. Go to **Packages & Registries > Package Registry**.
+1. Go to **Packages and registries > Package Registry**.
1. Find the name of the package you want to delete.
1. Select **Delete**.
The package is permanently deleted.
-## Delete files associated with a package
+## Delete assets associated with a package
-To delete package files, you must have suitable [permissions](../../permissions.md).
+To delete package assets, you must have suitable [permissions](../../permissions.md).
You can delete packages by using [the API](../../../api/packages.md#delete-a-package-file) or the UI.
-To delete package files in the UI, from your group or project:
+To delete package assets in the UI, from your group or project:
-1. Go to **Packages & Registries > Package Registry**.
+1. Go to **Packages and registries > Package Registry**.
1. Find the name of the package you want to delete.
1. Select the package to view additional details.
-1. Find the name of the file you would like to delete.
-1. Expand the ellipsis and select **Delete file**.
+1. Find the name of the assets you would like to delete.
+1. Expand the ellipsis and select **Delete asset**.
-The package files are permanently deleted.
+The package assets are permanently deleted.
## Cleanup policy
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/346153) in GitLab 15.2.
Depending on the number of packages to remove, the process of manually deleting the packages can take a long time to finish.
-A cleanup policy defines a set of rules that, applied to a project, defines which package files you can automatically delete.
+A cleanup policy defines a set of rules that, applied to a project, defines which package assets you can automatically delete.
### Enable the cleanup policy
By default, the packages cleanup policy is disabled. To enable it:
-1. Go to your project **Settings > Packages & Registries**.
+1. Go to your project **Settings > Packages and registries**.
1. Expand **Manage storage used by package assets**.
1. Set the rules appropriately.
@@ -73,7 +73,7 @@ To access these project settings, you must be at least a maintainer on the relat
- `Number of duplicated assets to keep`. The number of duplicated assets to keep. Some package formats allow you
to upload more than one copy of an asset. You can limit the number of duplicated assets to keep and automatically
- delete the oldest files once the limit is reached.
+ delete the oldest assets once the limit is reached.
### Set cleanup limits to conserve resources
diff --git a/doc/user/packages/pypi_repository/index.md b/doc/user/packages/pypi_repository/index.md
index ba9ecbe50a3..302c88bf46f 100644
--- a/doc/user/packages/pypi_repository/index.md
+++ b/doc/user/packages/pypi_repository/index.md
@@ -309,7 +309,7 @@ Uploading mypypipackage-0.0.1.tar.gz
100%|███████████████████████████████████████████████████████████████████████████████████████████| 4.24k/4.24k [00:00<00:00, 11.0kB/s]
```
-To view the published package, go to your project's **Packages & Registries**
+To view the published package, go to your project's **Packages and registries**
page.
If you didn't use a `.pypirc` file to define your repository source, you can
diff --git a/doc/user/packages/rubygems_registry/index.md b/doc/user/packages/rubygems_registry/index.md
index 05113d0bc10..682a3e2ecf1 100644
--- a/doc/user/packages/rubygems_registry/index.md
+++ b/doc/user/packages/rubygems_registry/index.md
@@ -130,7 +130,7 @@ Pushing gem to https://gitlab.example.com/api/v4/projects/1/packages/rubygems...
{"message":"201 Created"}
```
-To view the published gem, go to your project's **Packages & Registries** page. Gems pushed to
+To view the published gem, go to your project's **Packages and registries** page. Gems pushed to
GitLab aren't displayed in your project's Packages UI immediately. It can take up to 10 minutes to
process a gem.
diff --git a/doc/user/packages/terraform_module_registry/index.md b/doc/user/packages/terraform_module_registry/index.md
index 436c55f9ee0..0a3de25bf7d 100644
--- a/doc/user/packages/terraform_module_registry/index.md
+++ b/doc/user/packages/terraform_module_registry/index.md
@@ -24,7 +24,7 @@ When you publish a Terraform Module, if it does not exist, it is created.
Prerequisites:
-- A package with the same name and version must not already exist.
+- A package with the same name and version must not already exist in the top-level namespace.
- Your project and group names must not include a dot (`.`). For example, `source = "gitlab.example.com/my.group/project.name"`.
- You must [authenticate with the API](../../../api/index.md#authentication). If authenticating with a deploy token, it must be configured with the `write_package_registry` scope.
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index eca94fce2e9..9a4590d9478 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -88,6 +88,8 @@ The following table lists project permissions available for each role:
| [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>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 | | ✓ | ✓ | ✓ | ✓ |
+| [Issue boards](project/issue_board.md):<br>Move issues between lists | | ✓ | ✓ | ✓ | ✓ |
| [Issues](project/issues/index.md):<br>Add Labels | ✓ (*15*) | ✓ | ✓ | ✓ | ✓ |
| [Issues](project/issues/index.md):<br>Assign | ✓ (*15*) | ✓ | ✓ | ✓ | ✓ |
| [Issues](project/issues/index.md):<br>Create (*18*) | ✓ | ✓ | ✓ | ✓ | ✓ |
@@ -95,7 +97,7 @@ The following table lists project permissions available for each role:
| [Issues](project/issues/index.md):<br>View [Design Management](project/issues/design_management.md) pages | ✓ | ✓ | ✓ | ✓ | ✓ |
| [Issues](project/issues/index.md):<br>View [related issues](project/issues/related_issues.md) | ✓ | ✓ | ✓ | ✓ | ✓ |
| [Issues](project/issues/index.md):<br>Set [weight](project/issues/issue_weight.md) | ✓ (*15*) | ✓ | ✓ | ✓ | ✓ |
-| [Issues]](project/issues/index.md):<br>Set [parent epic](group/epics/manage_epics.md#add-an-existing-issue-to-an-epic) | | ✓ | ✓ | ✓ | ✓ |
+| [Issues](project/issues/index.md):<br>Set [parent epic](group/epics/manage_epics.md#add-an-existing-issue-to-an-epic) | | ✓ | ✓ | ✓ | ✓ |
| [Issues](project/issues/index.md):<br>View [confidential issues](project/issues/confidential_issues.md) | (*2*) | ✓ | ✓ | ✓ | ✓ |
| [Issues](project/issues/index.md):<br>Close / reopen (*19*) | | ✓ | ✓ | ✓ | ✓ |
| [Issues](project/issues/index.md):<br>Lock threads | | ✓ | ✓ | ✓ | ✓ |
@@ -119,7 +121,7 @@ The following table lists project permissions available for each role:
| [Merge requests](project/merge_requests/index.md):<br>Add labels | | | ✓ | ✓ | ✓ |
| [Merge requests](project/merge_requests/index.md):<br>Lock threads | | | ✓ | ✓ | ✓ |
| [Merge requests](project/merge_requests/index.md):<br>Manage or accept | | | ✓ | ✓ | ✓ |
-| [Merge requests](project/merge_requests/index.md):<br>[Resolve a thread](discussions/#resolve-a-thread) | | | ✓ | ✓ | ✓ |
+| [Merge requests](project/merge_requests/index.md):<br>[Resolve a thread](discussions/index.md#resolve-a-thread) | | | ✓ | ✓ | ✓ |
| [Merge requests](project/merge_requests/index.md):<br>Manage [merge approval rules](project/merge_requests/approvals/settings.md) (project settings) | | | | ✓ | ✓ |
| [Merge requests](project/merge_requests/index.md):<br>Delete | | | | | ✓ |
| [Metrics dashboards](../operations/metrics/dashboards/index.md):<br>Manage user-starred metrics dashboards (*6*) | ✓ | ✓ | ✓ | ✓ | ✓ |
@@ -348,11 +350,6 @@ from pushing to a protected branch. Read through the documentation on
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).
-### Issue board permissions
-
-Find the current permissions for interacting with the issue board feature in the
-[issue boards permissions page](project/issue_board.md#permissions).
-
### File Locking permissions **(PREMIUM)**
The user that locks a file or directory is the only one that can edit and push their changes back to the repository where the locked objects are located.
@@ -386,7 +383,7 @@ The following table lists group permissions available for each role:
| Pull a container image using the dependency proxy | ✓ | ✓ | ✓ | ✓ | ✓ |
| View Contribution analytics | ✓ | ✓ | ✓ | ✓ | ✓ |
| View group [epic](group/epics/index.md) | ✓ | ✓ | ✓ | ✓ | ✓ |
-| View [group wiki](project/wiki/group.md) pages | ✓ (6) | ✓ | ✓ | ✓ | ✓ |
+| View [group wiki](project/wiki/group.md) pages | ✓ (5) | ✓ | ✓ | ✓ | ✓ |
| View [Insights](project/insights/index.md) | ✓ | ✓ | ✓ | ✓ | ✓ |
| View [Insights](project/insights/index.md) charts | ✓ | ✓ | ✓ | ✓ | ✓ |
| View [Issue analytics](analytics/issue_analytics.md) | ✓ | ✓ | ✓ | ✓ | ✓ |
@@ -398,13 +395,13 @@ 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) | | | | ✓ | ✓ |
-| Pull a Container Registry image | ✓ (7) | ✓ | ✓ | ✓ | ✓ |
+| Pull a Container Registry image | ✓ (6) | ✓ | ✓ | ✓ | ✓ |
| Remove a Container Registry image | | | ✓ | ✓ | ✓ |
| View [Group DevOps Adoption](group/devops_adoption/index.md) | | ✓ | ✓ | ✓ | ✓ |
| View metrics dashboard annotations | | ✓ | ✓ | ✓ | ✓ |
| View [Productivity analytics](analytics/productivity_analytics.md) | | ✓ | ✓ | ✓ | ✓ |
| Create and edit [group wiki](project/wiki/group.md) pages | | | ✓ | ✓ | ✓ |
-| Create project in group | | | ✓ (3)(5) | ✓ (3) | ✓ (3) |
+| Create project in group | | | ✓ (2)(4) | ✓ (2) | ✓ (2) |
| Create/edit/delete group milestones | | ✓ | ✓ | ✓ | ✓ |
| Create/edit/delete iterations | | ✓ | ✓ | ✓ | ✓ |
| Create/edit/delete metrics dashboard annotations | | | ✓ | ✓ | ✓ |
@@ -412,10 +409,10 @@ The following table lists group permissions available for each role:
| Purge the dependency proxy for a group | | | | | ✓ |
| Create/edit/delete dependency proxy [cleanup policies](packages/dependency_proxy/reduce_dependency_proxy_storage.md#cleanup-policies) | | | | ✓ | ✓ |
| Use [security dashboard](application_security/security_dashboard/index.md) | | | ✓ | ✓ | ✓ |
-| View group Audit Events | | | ✓ (7) | ✓ (7) | ✓ |
+| View group Audit Events | | | ✓ (6) | ✓ (6) | ✓ |
| Create subgroup | | | | ✓ (1) | ✓ |
| Delete [group wiki](project/wiki/group.md) pages | | | ✓ | ✓ | ✓ |
-| Edit [epic](group/epics/index.md) comments (posted by any user) | | | | ✓ (2) | ✓ (2) |
+| Edit [epic](group/epics/index.md) comments (posted by any user) | | | | ✓ | ✓ |
| List group deploy tokens | | | | ✓ | ✓ |
| Manage [group push rules](group/access_and_permissions.md#group-push-rules) | | | | ✓ | ✓ |
| View/manage group-level Kubernetes cluster | | | | ✓ | ✓ |
@@ -426,14 +423,14 @@ The following table lists group permissions available for each role:
| Delete group [epic](group/epics/index.md) | | | | | ✓ |
| Disable notification emails | | | | | ✓ |
| Edit group settings | | | | | ✓ |
-| Edit [SAML SSO](group/saml_sso/index.md) | | | | | ✓ (4) |
+| Edit [SAML SSO](group/saml_sso/index.md) | | | | | ✓ (3) |
| Filter members by 2FA status | | | | | ✓ |
| Manage group level CI/CD variables | | | | | ✓ |
| Manage group members | | | | | ✓ |
| Share (invite) groups with groups | | | | | ✓ |
| View 2FA status of members | | | | | ✓ |
-| View [Billing](../subscriptions/gitlab_com/index.md#view-your-gitlab-saas-subscription) | | | | | ✓ (4) |
-| View group [Usage Quotas](usage_quotas.md) page | | | | | ✓ (4) |
+| View [Billing](../subscriptions/gitlab_com/index.md#view-your-gitlab-saas-subscription) | | | | | ✓ (3) |
+| View group [Usage Quotas](usage_quotas.md) page | | | | | ✓ (3) |
| Manage group runners | | | | | ✓ |
| [Migrate groups](group/import/index.md) | | | | | ✓ |
| Manage [subscriptions, and purchase CI/CD minutes and storage](../subscriptions/gitlab_com/index.md) | | | | | ✓ |
@@ -441,14 +438,13 @@ The following table lists group permissions available for each role:
<!-- markdownlint-disable MD029 -->
1. Groups can be set to allow either Owners, or Owners and users with the Maintainer role, to [create subgroups](group/subgroups/index.md#create-a-subgroup).
-2. Introduced in GitLab 12.2.
-3. Default project creation role can be changed at:
+2. Default project creation role can be changed at:
- The [instance level](admin_area/settings/visibility_and_access_controls.md#define-which-roles-can-create-projects).
- The [group level](group/manage.md#specify-who-can-add-projects-to-a-group).
-4. Does not apply to subgroups.
-5. Developers can push commits to the default branch of a new project only if the [default branch protection](group/manage.md#change-the-default-branch-protection-of-a-group) is set to "Partially protected" or "Not protected".
-6. In addition, if your group is public or internal, all users who can see the group can also see group wiki pages.
-7. Users can only view events based on their individual actions.
+3. Does not apply to subgroups.
+4. Developers can push commits to the default branch of a new project only if the [default branch protection](group/manage.md#change-the-default-branch-protection-of-a-group) is set to "Partially protected" or "Not protected".
+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.
<!-- markdownlint-enable MD029 -->
@@ -470,7 +466,8 @@ project and should only have access to that project.
External users:
-- Can only create projects (including forks), subgroups, and snippets within the top-level group to which they belong.
+- Cannot create project, groups, and snippets within 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).
@@ -496,7 +493,7 @@ 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 **Menu > Admin**.
+ 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.
@@ -510,7 +507,7 @@ Additionally, users can be set as external users using:
By default, new users are not set as external users. This behavior can be changed
by an administrator:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Account and limit** section.
diff --git a/doc/user/profile/account/create_accounts.md b/doc/user/profile/account/create_accounts.md
index 694ed02a694..e3f7d47038d 100644
--- a/doc/user/profile/account/create_accounts.md
+++ b/doc/user/profile/account/create_accounts.md
@@ -33,7 +33,7 @@ Prerequisites:
To create a user manually:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users** (`/admin/users`).
1. Select **New user**.
1. Complete the fields.
diff --git a/doc/user/profile/account/delete_account.md b/doc/user/profile/account/delete_account.md
index 5ec29814e06..d2e0c1ad834 100644
--- a/doc/user/profile/account/delete_account.md
+++ b/doc/user/profile/account/delete_account.md
@@ -28,7 +28,7 @@ As a user, to delete your own account:
As an administrator, to delete a user account:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users**.
1. Select a user.
1. Under the **Account** tab, select:
diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md
index 4c859d98004..e4cf905bbce 100644
--- a/doc/user/profile/index.md
+++ b/doc/user/profile/index.md
@@ -111,8 +111,8 @@ the README file with information, it's included on your profile page.
To create a new project and add its README to your profile:
-1. On the top bar, select **Menu > Project**.
-1. Select **Create new project**.
+1. On the top bar, select **Main menu > Projects > View all projects**.
+1. On the right of the page, select **New project**.
1. Select **Create blank project**.
1. Enter the project details:
- In the **Project name** field, enter the name for your new project.
@@ -205,7 +205,7 @@ To set your current status:
1. Select a value from the **Clear status after** dropdown list.
1. Select **Set status**. Alternatively, you can select **Remove status** to remove your user status entirely.
-You can also set your current status by [using the API](../../api/users.md#user-status).
+You can also set your current status from [your user settings](#access-your-user-settings) or by [using the API](../../api/users.md#user-status).
If you select the **Busy** checkbox, remember to clear it when you become available again.
@@ -421,7 +421,7 @@ 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
and it expires after a set duration. GitLab administrators can determine the duration:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand **Account and limit**. The set duration is in **Session duration (minutes)**.
diff --git a/doc/user/profile/notifications.md b/doc/user/profile/notifications.md
index 0245f0615e0..1b737f14f68 100644
--- a/doc/user/profile/notifications.md
+++ b/doc/user/profile/notifications.md
@@ -108,7 +108,7 @@ To select a notification level for a group, use either of these methods:
Or:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. Select the notification dropdown, next to the bell icon (**{notifications}**).
1. Select the desired [notification level](#notification-levels).
@@ -139,7 +139,7 @@ To select a notification level for a project, use either of these methods:
Or:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. Select the notification dropdown, next to the bell icon (**{notifications}**).
1. Select the desired [notification level](#notification-levels).
diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md
index 427c412219a..2fd18f583a4 100644
--- a/doc/user/profile/personal_access_tokens.md
+++ b/doc/user/profile/personal_access_tokens.md
@@ -19,6 +19,13 @@ Personal access tokens can be an alternative to [OAuth2](../../api/oauth2.md) an
In both cases, you authenticate with a personal access token in place of your password.
+WARNING:
+The ability to create personal access tokens without expiry was
+[deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/369122) in GitLab 15.4 and is planned for removal in GitLab
+16.0. When this ability is removed, existing personal access tokens without an expiry are planned to have an expiry added.
+The automatic adding of an expiry occurs on GitLab.com during the 16.0 milestone. The automatic adding of an expiry
+occurs on self-managed instances when they are upgraded to GitLab 16.0. This change is a breaking change.
+
Personal access tokens are:
- Required when [two-factor authentication (2FA)](account/two_factor_authentication.md) is enabled.
@@ -40,6 +47,8 @@ Use impersonation tokens to automate authentication as a specific user.
## Create a personal access token
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348660) in GitLab 15.3, default expiration of 30 days is populated in the UI.
+
You can create as many personal access tokens as you like.
1. In the top-right corner, select your avatar.
diff --git a/doc/user/profile/preferences.md b/doc/user/profile/preferences.md
index e99a2dad980..31ab802a8b8 100644
--- a/doc/user/profile/preferences.md
+++ b/doc/user/profile/preferences.md
@@ -116,12 +116,11 @@ between the fixed (max. `1280px`) and the fluid (`100%`) application layout.
NOTE:
While `1280px` is the standard max width when using fixed layout, some pages still use 100% width, depending on the content.
-### Default dashboard
+### Dashboard
For users who have access to a large number of projects but only keep up with a
-select few, the amount of activity on the default Dashboard page can be
-overwhelming. Changing this setting allows you to redefine your default
-dashboard.
+select few, the amount of activity on the your dashboard can be
+overwhelming. Changing this setting allows you to redefine what is displayed by default.
You can include the following options for your default dashboard view:
diff --git a/doc/user/project/badges.md b/doc/user/project/badges.md
index 2f9e04fb828..cf0ff4ed8b9 100644
--- a/doc/user/project/badges.md
+++ b/doc/user/project/badges.md
@@ -21,7 +21,7 @@ If you find that you have to add the same badges to several projects, you may wa
To add a new badge to a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Badges**.
1. Under "Link", enter the URL that the badges should point to and under
@@ -41,7 +41,7 @@ A common project badge presents the GitLab CI pipeline status.
To add this badge to a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Badges**.
1. Under **Name**, enter _Pipeline Status_.
@@ -68,7 +68,7 @@ If you need individual badges for each project, either:
To add a new badge to a group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Badges**.
1. Under "Link", enter the URL that the badges should point to and under
@@ -118,7 +118,7 @@ https://gitlab.example.com/<project_path>/-/raw/<default_branch>/my-image.svg
To add a new badge to a group or project with a custom image:
-1. On the top bar, select **Menu** and find your group or project.
+1. On the top bar, select **Main menu** and find your group or project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Badges**.
1. Under **Name**, enter the name for the badge.
diff --git a/doc/user/project/canary_deployments.md b/doc/user/project/canary_deployments.md
index f8494116655..aac704e2cdd 100644
--- a/doc/user/project/canary_deployments.md
+++ b/doc/user/project/canary_deployments.md
@@ -38,8 +38,8 @@ want to make sure the performance stays the same, or improves. Developers need
to be careful when using canaries with user-facing changes, because by default,
requests from the same user are randomly distributed between canary and
non-canary pods, which could result in confusion or even errors. If needed, you
-may want to consider
-[setting `service.spec.sessionAffinity` to `ClientIP` in your Kubernetes service definitions](https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies),
+may want to consider
+[setting `service.spec.sessionAffinity` to `ClientIP` in your Kubernetes service definitions](https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies),
but that is beyond the scope of this document.
## Advanced traffic control with Canary Ingress
diff --git a/doc/user/project/clusters/add_eks_clusters.md b/doc/user/project/clusters/add_eks_clusters.md
index be73f2c9a01..d9339291328 100644
--- a/doc/user/project/clusters/add_eks_clusters.md
+++ b/doc/user/project/clusters/add_eks_clusters.md
@@ -56,7 +56,7 @@ cluster certificates:
1. Go to your:
- Project's **Infrastructure > Kubernetes clusters** page, for a project-level cluster.
- Group's **Kubernetes** page, for a group-level cluster.
- - **Menu > Admin > Kubernetes**, for an instance-level cluster.
+ - **Main menu > Admin > Kubernetes**, for an instance-level cluster.
1. Select **Integrate with a cluster certificate**.
1. Under the **Create new cluster** tab, select **Amazon EKS** to display an
`Account ID` and `External ID` needed for later steps.
@@ -248,7 +248,7 @@ For example, the following policy document allows assuming a role whose name sta
To configure Amazon authentication in GitLab, generate an access key for the
IAM user in the Amazon AWS console, and follow these steps:
-1. In GitLab, on the top bar, select **Menu > Admin > Settings > General** and expand the **Amazon EKS** section.
+1. In GitLab, on the top bar, select **Main menu > Admin > Settings > General** and expand the **Amazon EKS** section.
1. Check **Enable Amazon EKS integration**.
1. Enter your **Account ID**.
1. Enter your [access key and ID](#eks-access-key-and-id).
diff --git a/doc/user/project/clusters/add_existing_cluster.md b/doc/user/project/clusters/add_existing_cluster.md
index c55c11151ce..d7137c18a03 100644
--- a/doc/user/project/clusters/add_existing_cluster.md
+++ b/doc/user/project/clusters/add_existing_cluster.md
@@ -68,7 +68,7 @@ To add a Kubernetes cluster to your project, group, or instance:
1. Navigate to your:
1. Project's **{cloud-gear}** **Infrastructure > Kubernetes clusters** page, for a project-level cluster.
1. Group's **{cloud-gear}** **Kubernetes** page, for a group-level cluster.
- 1. **Menu > Admin > Kubernetes** page, for an instance-level cluster.
+ 1. **Main menu > Admin > Kubernetes** page, for an instance-level cluster.
1. On the **Kubernetes clusters** page, select the **Connect with a certificate** option from the **Actions** dropdown menu.
1. On the **Connect a cluster** page, fill in the details:
1. **Kubernetes cluster name** (required) - The name you wish to give the cluster.
diff --git a/doc/user/project/clusters/add_gke_clusters.md b/doc/user/project/clusters/add_gke_clusters.md
index bfaf9aab7b7..6ed02838e9b 100644
--- a/doc/user/project/clusters/add_gke_clusters.md
+++ b/doc/user/project/clusters/add_gke_clusters.md
@@ -51,7 +51,7 @@ Note the following:
cluster's pod address IP range is set to `/16` instead of the regular `/14`. `/16` is a CIDR
notation.
- GitLab requires basic authentication enabled and a client certificate issued for the cluster to
- set up an [initial service account](cluster_access.md). In
+ set up an [initial service account](cluster_access.md). In
[GitLab versions 11.10 and later](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/58208), the cluster creation process
explicitly requests GKE to create clusters with basic authentication enabled and a client
certificate.
@@ -63,7 +63,7 @@ cluster certificates:
- Project's **{cloud-gear}** **Infrastructure > Kubernetes clusters** page, for a project-level
cluster.
- Group's **{cloud-gear}** **Kubernetes** page, for a group-level cluster.
- - **Menu > Admin > Kubernetes** page, for an instance-level cluster.
+ - **Main menu > Admin > Kubernetes** page, for an instance-level cluster.
1. Select **Integrate with a cluster certificate**.
1. Under the **Create new cluster** tab, select **Google GKE**.
1. Connect your Google account if you haven't done already by clicking the
diff --git a/doc/user/project/clusters/add_remove_clusters.md b/doc/user/project/clusters/add_remove_clusters.md
index f1004a40a13..2fba00ae940 100644
--- a/doc/user/project/clusters/add_remove_clusters.md
+++ b/doc/user/project/clusters/add_remove_clusters.md
@@ -19,7 +19,7 @@ When you successfully connect an existing cluster using cluster certificates, th
1. Go to your:
- Project's **{cloud-gear}** **Infrastructure > Kubernetes clusters** page, for a project-level cluster.
- Group's **{cloud-gear}** **Kubernetes** page, for a group-level cluster.
- - **Menu > Admin > Kubernetes** page, for an instance-level cluster.
+ - **Main menu > Admin > Kubernetes** page, for an instance-level cluster.
1. Select the name of the cluster you want to disable.
1. Toggle **GitLab Integration** off (in gray).
1. Select **Save changes**.
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index f89d863e83b..940b58103f5 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -24,5 +24,5 @@ to a single project.
To view project-level Kubernetes clusters:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Infrastructure > Kubernetes clusters**.
diff --git a/doc/user/project/code_intelligence.md b/doc/user/project/code_intelligence.md
index 7f35caf2a68..860ebfbed14 100644
--- a/doc/user/project/code_intelligence.md
+++ b/doc/user/project/code_intelligence.md
@@ -17,7 +17,10 @@ development environments (IDE), including:
Code Intelligence is built into GitLab and powered by [LSIF](https://lsif.dev/)
(Language Server Index Format), a file format for precomputed code
-intelligence data.
+intelligence data. GitLab processes one LSIF file per project, and
+Code Intelligence does not support different LSIF files per branch.
+Follow epic [#4212, Code intelligence enhancements](https://gitlab.com/groups/gitlab-org/-/epics/4212)
+for progress on upcoming enhancements.
NOTE:
You can automate this feature in your applications by using [Auto DevOps](../../topics/autodevops/index.md).
@@ -59,13 +62,5 @@ under the **References** tab:
## Language support
Generating an LSIF file requires a language server indexer implementation for the
-relevant language.
-
-| Language | Implementation |
-|---|---|
-| Go | [`sourcegraph/lsif-go`](https://github.com/sourcegraph/lsif-go) |
-| JavaScript | [`sourcegraph/lsif-node`](https://github.com/sourcegraph/lsif-node) |
-| TypeScript | [`sourcegraph/lsif-node`](https://github.com/sourcegraph/lsif-node) |
-
-View a complete list of [available LSIF indexers](https://lsif.dev/#implementations-server) on their website and
+relevant language. View a complete list of [available LSIF indexers](https://lsif.dev/#implementations-server) on their website and
refer to their documentation to see how to generate an LSIF file for your specific language.
diff --git a/doc/user/project/deploy_boards.md b/doc/user/project/deploy_boards.md
index 41afbdada6b..63010610605 100644
--- a/doc/user/project/deploy_boards.md
+++ b/doc/user/project/deploy_boards.md
@@ -116,7 +116,7 @@ To display the deploy boards for a specific [environment](../../ci/environments/
Kubernetes.
NOTE:
- Matching based on the Kubernetes `app` label was removed in
+ Matching based on the Kubernetes `app` label was removed in
[GitLab 12.1](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/14020).
To migrate, please apply the required annotations (see above) and
re-deploy your application. If you are using Auto DevOps, this will
diff --git a/doc/user/project/deploy_keys/index.md b/doc/user/project/deploy_keys/index.md
index a9f19d27416..f424ec529b2 100644
--- a/doc/user/project/deploy_keys/index.md
+++ b/doc/user/project/deploy_keys/index.md
@@ -54,7 +54,7 @@ For example:
To view the deploy keys available to a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Deploy keys**.
@@ -72,7 +72,7 @@ Prerequisites:
- [Generate an SSH key pair](../../ssh.md#generate-an-ssh-key-pair). Put the private SSH
key on the host that requires access to the repository.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Deploy keys**.
1. Complete the fields.
@@ -92,7 +92,7 @@ Prerequisites:
To create a public deploy key:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Deploy Keys**.
1. Select **New deploy key**.
1. Complete the fields.
@@ -109,7 +109,7 @@ Prerequisites:
To grant a public deploy key access to a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Deploy keys**.
1. Select **Publicly accessible deploy keys**.
@@ -129,7 +129,7 @@ Prerequisites:
To disable a deploy key:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Deploy keys**.
1. Select **Disable** (**{cancel}**).
diff --git a/doc/user/project/deploy_tokens/index.md b/doc/user/project/deploy_tokens/index.md
index 595f5e541b7..04a2eeacffb 100644
--- a/doc/user/project/deploy_tokens/index.md
+++ b/doc/user/project/deploy_tokens/index.md
@@ -31,7 +31,7 @@ You can create as many deploy tokens as you need from the settings of your
project. Alternatively, you can also create [group-scoped deploy tokens](#group-deploy-token).
1. Sign in to your GitLab account.
-1. On the top bar, select **Menu > Projects** or **Menu > Groups** to find your project or group.
+1. On the top bar, select **Main menu > Projects** or **Main menu > Groups** to find your project or group.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Deploy tokens**.
1. Choose a name, and optionally, an expiration date and username for the token.
@@ -51,7 +51,7 @@ Deploy tokens expire at midnight UTC on the date you define.
To revoke a deploy token:
-1. On the top bar, select **Menu > Projects** or **Menu > Groups** to find your project or group.
+1. On the top bar, select **Main menu > Projects** or **Main menu > Groups** to find your project or group.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Deploy tokens**.
1. In the **Active Deploy Tokens** section, by the token you want to revoke, select **Revoke**.
@@ -190,7 +190,8 @@ To pull images from the Dependency Proxy, you must:
### GitLab deploy token
-> Support for `gitlab-deploy-token` at the group level [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214014) in GitLab 15.1 [with a flag](../../../administration/feature_flags.md) named `ci_variable_for_group_gitlab_deploy_token`. Enabled by default.
+> - Support for `gitlab-deploy-token` at the group level [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214014) in GitLab 15.1 [with a flag](../../../administration/feature_flags.md) named `ci_variable_for_group_gitlab_deploy_token`. Enabled by default.
+> - [Feature flag `ci_variable_for_group_gitlab_deploy_token`](https://gitlab.com/gitlab-org/gitlab/-/issues/363621) removed in GitLab 15.4.
There's a special case when it comes to deploy tokens. If a user creates one
named `gitlab-deploy-token`, the username and token of the deploy token is
diff --git a/doc/user/project/description_templates.md b/doc/user/project/description_templates.md
index 5df3a973cca..4050fa34026 100644
--- a/doc/user/project/description_templates.md
+++ b/doc/user/project/description_templates.md
@@ -32,7 +32,7 @@ directory in your repository.
To create an issue description template:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Repository**.
1. Next to the default branch, select **{plus}**.
1. Select **New file**.
@@ -51,7 +51,7 @@ push to your default branch.
To create a merge request description template:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Repository**.
1. Next to the default branch, select **{plus}**.
1. Select **New file**.
@@ -103,7 +103,7 @@ As a result, you can use the same templates in issues and merge requests in all
To re-use templates [you've created](../project/description_templates.md#create-an-issue-template):
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Templates**.
1. From the dropdown list, select your template project as the template repository at group level.
@@ -134,10 +134,9 @@ To set a default description template for merge requests, either:
This [doesn't overwrite](#priority-of-default-description-templates) the default template if one has been set in the project settings.
- Users on GitLab Premium and higher: set the default template in project settings:
- 1. On the top bar, select **Menu > Projects** and find your project.
- 1. On the left sidebar, select **Settings**.
- 1. Expand **Merge requests**.
- 1. Fill in the **Default description template for merge requests** text area.
+ 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 commit message template** section, fill in **Default description template for merge requests**.
1. Select **Save changes**.
To set a default description template for issues, either:
@@ -147,7 +146,7 @@ To set a default description template for issues, either:
This [doesn't overwrite](#priority-of-default-description-templates) the default template if one has been set in the project settings.
- Users on GitLab Premium and higher: set the default template in project settings:
- 1. On the top bar, select **Menu > Projects** and find your project.
+ 1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings**.
1. Expand **Default issue template**.
1. Fill in the **Default description template for issues** text area.
diff --git a/doc/user/project/file_lock.md b/doc/user/project/file_lock.md
index 4810fb96ed3..f1b9bde6cd0 100644
--- a/doc/user/project/file_lock.md
+++ b/doc/user/project/file_lock.md
@@ -220,7 +220,7 @@ To view the user who locked the file (if it was not you), hover over the button.
To view and remove file locks:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Repository > Locked Files**.
This list shows all the files locked either through LFS or GitLab UI.
diff --git a/doc/user/project/git_attributes.md b/doc/user/project/git_attributes.md
index 90f64b7262c..f2e4b65e3d4 100644
--- a/doc/user/project/git_attributes.md
+++ b/doc/user/project/git_attributes.md
@@ -23,5 +23,5 @@ ignored.
## Syntax Highlighting
The `.gitattributes` file can be used to define which language to use when
-syntax highlighting files and diffs. See
+syntax highlighting files and diffs. See
["Syntax Highlighting"](highlighting.md) for more information.
diff --git a/doc/user/project/import/clearcase.md b/doc/user/project/import/clearcase.md
index d9ad0c57d79..2d9f92c38e4 100644
--- a/doc/user/project/import/clearcase.md
+++ b/doc/user/project/import/clearcase.md
@@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
tools developed by IBM which also include a centralized version control system
similar to Git.
-A good read of ClearCase's basic concepts is can be found in this
+A good read of ClearCase's basic concepts is can be found in this
[StackOverflow post](https://stackoverflow.com/a/645771/974710).
The following table illustrates the main differences between ClearCase and Git:
diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md
index a3dfa3edff0..c04f734e8bb 100644
--- a/doc/user/project/import/github.md
+++ b/doc/user/project/import/github.md
@@ -26,7 +26,7 @@ If you are importing from GitHub Enterprise to a self-managed GitLab instance:
- You must first enable [GitHub integration](../../../integration/github.md).
- To import projects from GitHub Enterprise to GitLab.com, use the [Import API](../../../api/import.md).
-- If GitLab is behind a HTTP/HTTPS proxy, you must populate the [allowlist for local requests](../../../security/webhooks.md#allowlist-for-local-requests)
+- If GitLab is behind a HTTP/HTTPS proxy, you must populate the [allowlist for local requests](../../../security/webhooks.md#create-an-allowlist-for-local-requests)
with `github.com` and `api.github.com` to solve the hostname. For more information, read the issue
[Importing a GitHub project requires DNS resolution even when behind a proxy](https://gitlab.com/gitlab-org/gitlab/-/issues/37941).
@@ -62,7 +62,7 @@ For this association to succeed, each GitHub author and assignee in the reposito
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-github-user-account/managing-email-preferences/setting-your-commit-email-address)
+- 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.
GitLab content imports that use GitHub accounts require that the GitHub public-facing email address is populated. This means
@@ -76,7 +76,7 @@ field to be populated so you may have to add it on existing accounts.
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/reference/users#get-a-user) in the profile of the GitHub user
+- 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
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.
@@ -171,12 +171,15 @@ The following items of a project are imported:
- Repository description.
- Git repository data.
+- Branch protection rules. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/22650) in GitLab 15.4.
- Issues.
- Pull requests.
- Wiki pages.
- Milestones.
- Labels.
- Release note descriptions.
+- Release note attachments. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15620) in GitLab 15.4 with `github_importer_attachments_import`
+ [feature flag](../../../administration/feature_flags.md) disabled by default.
- Pull request review comments.
- Regular issue and pull request comments.
- [Git Large File Storage (LFS) Objects](../../../topics/git/lfs/index.md).
@@ -184,6 +187,8 @@ The following items of a project are imported:
- Pull request "merged by" information (GitLab.com and GitLab 13.7 and later).
- Pull request comments replies in discussions ([GitLab.com and GitLab 14.5 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/336596)).
- Diff Notes suggestions ([GitLab.com and GitLab 14.7 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/340624)).
+- Issue events and pull requests events. [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/7673) in GitLab 15.4 with `github_importer_issue_events_import`
+ [feature flag](../../../administration/feature_flags.md) disabled by default.
References to pull requests and issues are preserved. Each imported repository maintains visibility level unless that
[visibility level is restricted](../../public_access.md#restrict-use-of-public-or-internal-projects), in which case it
diff --git a/doc/user/project/import/gitlab_com.md b/doc/user/project/import/gitlab_com.md
index 4103367accc..8d30a9c7f52 100644
--- a/doc/user/project/import/gitlab_com.md
+++ b/doc/user/project/import/gitlab_com.md
@@ -5,29 +5,30 @@ group: Import
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
---
-# Project importing from GitLab.com to your private GitLab instance **(FREE)**
+# Import a project from GitLab.com to your private GitLab instance **(FREE)**
-You can import your existing GitLab.com projects to your GitLab instance, but keep in
-mind that it is possible only if GitLab.com integration is enabled on your GitLab instance.
-[Read more about GitLab.com integration for self-managed GitLab instances](../../../integration/gitlab.md).
+You can import your existing GitLab.com projects to your GitLab instance.
-To get to the importer page you need to go to "New project" page.
+Prerequisite:
-NOTE:
-If you are interested in importing Wiki and merge request data to your new instance,
-you'll need to follow the instructions for [exporting a project](../settings/import_export.md#export-a-project-and-its-data)
+- GitLab.com integration must be enabled on your GitLab instance.
+ [Read more about GitLab.com integration for self-managed GitLab instances](../../../integration/gitlab.md).
-![New project page](img/gitlab_new_project_page_v12_2.png)
+To import a GitLab.com project to your self-managed GitLab instance:
-Go to the **Import Projects** tab, then select **GitLab.com**, and you are redirected to GitLab.com
-for permission to access your projects. After accepting, you are automatically redirected to the importer.
+1. In GitLab, on the top bar, select **Main menu > Projects > View all projects**.
+1. On the right of the page, select **New project**.
+1. Select **Import project**.
+1. Select **GitLab.com**.
+1. Give GitLab.com permission to access your projects.
+1. Select **Import**.
-![Importer page](img/gitlab_importer.png)
+The importer imports your repository and issues.
+When the importer is done, a new GitLab project is created with your imported data.
-To import a project, select **Import**. The importer imports your repository and issues.
-Once the importer is done, a new GitLab project is created with your imported data.
+## Related topics
-## Automate group and project import **(PREMIUM)**
-
-For information on automating user, group, and project import API calls, see
-[Automate group and project import](index.md#automate-group-and-project-import).
+- To automate user, group, and project import API calls, see
+ [Automate group and project import](index.md#automate-group-and-project-import).
+- To import Wiki and merge request data to your new instance,
+ see [exporting a project](../settings/import_export.md#export-a-project-and-its-data).
diff --git a/doc/user/project/import/img/gitlab_importer.png b/doc/user/project/import/img/gitlab_importer.png
deleted file mode 100644
index 27d42eb492e..00000000000
--- a/doc/user/project/import/img/gitlab_importer.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/import/img/gitlab_new_project_page_v12_2.png b/doc/user/project/import/img/gitlab_new_project_page_v12_2.png
deleted file mode 100644
index ff6e5dbf4a1..00000000000
--- a/doc/user/project/import/img/gitlab_new_project_page_v12_2.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/import/index.md b/doc/user/project/import/index.md
index 432f043f945..72d533efd1b 100644
--- a/doc/user/project/import/index.md
+++ b/doc/user/project/import/index.md
@@ -85,7 +85,7 @@ Migrate the assets in this order:
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/)
+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
@@ -142,5 +142,10 @@ GitLab from:
- Bitbucket Server
- Bitbucket Data Center
-See the [Quick Start Guide](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/-/blob/master/docs/using-congregate.md#quick-start)
-to learn how to use this approach for migrating users, groups, and projects at scale.
+For more information, see:
+
+- [Quick Start](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/-/blob/master/docs/using-congregate.md#quick-start).
+- [Frequently Asked Migration Questions](https://gitlab.com/gitlab-org/professional-services-automation/tools/migration/congregate/-/blob/master/customer/famq.md),
+ including settings that need checking afterwards and other limitations.
+
+For support, customers must enter into a paid engagement with GitLab Professional Services.
diff --git a/doc/user/project/import/repo_by_url.md b/doc/user/project/import/repo_by_url.md
index 5163f957171..d64bea2bb41 100644
--- a/doc/user/project/import/repo_by_url.md
+++ b/doc/user/project/import/repo_by_url.md
@@ -9,7 +9,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
You can import your existing repositories by providing the Git URL:
-1. On the top bar, select **Menu > Create new project**.
+1. In GitLab, on the top bar, select **Main menu > Projects > View all projects**.
+1. On the right of the page, select **New project**.
1. Select the **Import project** tab.
1. Select **Repository by URL**.
1. Enter a **Git repository URL**.
diff --git a/doc/user/project/import/svn.md b/doc/user/project/import/svn.md
index b88abf91ae1..d6bcb0a2018 100644
--- a/doc/user/project/import/svn.md
+++ b/doc/user/project/import/svn.md
@@ -5,186 +5,87 @@ group: Import
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
---
-# Migrating from SVN to GitLab **(FREE)**
+# Migrate from Subversion to GitLab **(FREE)**
-Subversion (SVN) is a central version control system (VCS) while
-Git is a distributed version control system. There are some major differences
-between the two, for more information consult your favorite search engine.
+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`.
-There are two approaches to SVN to Git migration:
+You can follow the steps on this page to migrate to Git if your SVN repository:
-- [Git/SVN Mirror](#smooth-migration-with-a-gitsvn-mirror-using-subgit) which:
- - Makes the GitLab repository to mirror the SVN project.
- - Git and SVN repositories are kept in sync; you can use either one.
- - Smoothens the migration process and allows you to manage migration risks.
+- Has a standard format (trunk, branches, and tags).
+- Is not nested.
-- [Cut over migration](#cut-over-migration-with-svn2git) which:
- - Translates and imports the existing data and history from SVN to Git.
- - Is a fire and forget approach, good for smaller teams.
+For a non-standard repository see the [`svn2git` documentation](https://github.com/nirvdrum/svn2git).
-## Smooth migration with a Git/SVN mirror using SubGit
+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.
-[SubGit](https://subgit.com) is a tool for a smooth, stress-free SVN to Git
-migration. It creates a writable Git mirror of a local or remote Subversion
-repository and that way you can use both Subversion and Git as long as you like.
-It requires access to your GitLab server as it talks with the Git repositories
-directly in a file system level.
+## Install `svn2git`
-### SubGit prerequisites
+Install `svn2git` on a local workstation rather than the GitLab server:
-1. Install Oracle JRE 1.8 or newer. On Debian-based Linux distributions you can
- follow [this article](http://www.webupd8.org/2012/09/install-oracle-java-8-in-ubuntu-via-ppa.html).
-1. Download SubGit from <https://subgit.com/download>.
-1. Unpack the downloaded SubGit zip archive to the `/opt` directory. The `subgit`
- command is available at `/opt/subgit-VERSION/bin/subgit`.
+- On all systems you can install as a Ruby gem if you already have Ruby and Git installed:
-### SubGit configuration
+ ```shell
+ sudo gem install svn2git
+ ```
-The first step to mirror you SVN repository in GitLab is to create a new empty
-project that is used as a mirror. For Omnibus installations the path to
-the repository is
-`/var/opt/gitlab/git-data/repositories/USER/REPO.git` by default. For
-installations from source, the default repository directory is
-`/home/git/repositories/USER/REPO.git`. For convenience, assign this path to a
-variable:
+- On Debian-based Linux distributions you can install the native packages:
-```shell
-GIT_REPO_PATH=/var/opt/gitlab/git-data/repositories/USER/REPOS.git
-```
+ ```shell
+ sudo apt-get install git-core git-svn ruby
+ ```
-SubGit keeps this repository in sync with a remote SVN project. For
-convenience, assign your remote SVN project URL to a variable:
+## Prepare an authors file (recommended)
-```shell
-SVN_PROJECT_URL=http://svn.company.com/repos/project
-```
+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.
-Next you need to run SubGit to set up a Git/SVN mirror. Make sure the following
-`subgit` command is ran on behalf of the same user that keeps ownership of
-GitLab Git repositories (by default `git`):
+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.
-```shell
-subgit configure --layout auto $SVN_PROJECT_URL $GIT_REPO_PATH
-```
+1. Search through the SVN repository and output a list of authors:
-Adjust authors and branches mappings, if necessary. Open with your favorite
-text editor:
+ ```shell
+ svn log --quiet | grep -E "r[0-9]+ \| .+ \|" | cut -d'|' -f2 | sed 's/ //g' | sort | uniq
+ ```
-```shell
-edit $GIT_REPO_PATH/subgit/authors.txt
-edit $GIT_REPO_PATH/subgit/config
-```
+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:
-For more information regarding the SubGit configuration options, refer to
-[SubGit's documentation](https://subgit.com/documentation/) website.
+ ```plaintext
+ sidneyjones = Sidney Jones <sidneyjones@example.com>
+ ```
-### Initial translation
+## Migrate SVN repository to Git repository
-Now that SubGit has configured the Git/SVN repositories, run `subgit` to perform the
-initial translation of existing SVN revisions into the 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.
-```shell
-subgit install $GIT_REPO_PATH
-```
+For each repository to migrate:
-After the initial translation is completed, `subgit` keeps the Git repository and the SVN
-project sync - new Git commits are translated to
-SVN revisions and new SVN revisions are translated to Git commits. Mirror
-works transparently and does not require any special commands.
+1. Create a new directory and change into it.
+1. For repositories that:
-If you would prefer to perform one-time cut over migration with `subgit`, use
-the `import` command instead of `install`:
+ - Don't require a username and password, run:
-```shell
-subgit import $GIT_REPO_PATH
-```
+ ```shell
+ svn2git https://svn.example.com/path/to/repo --authors /path/to/authors.txt
+ ```
-### SubGit licensing
+ - Do require a username and password, run:
-Running SubGit in a mirror mode requires a
-[registration](https://subgit.com/pricing). Registration is free for open
-source, academic and startup projects.
+ ```shell
+ svn2git https://svn.example.com/path/to/repo --authors /path/to/authors.txt --username <username> --password <password>
+ ```
-### SubGit support
+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.
-For any questions related to SVN to GitLab migration with SubGit, you can
-contact the SubGit team directly at [support@subgit.com](mailto:support@subgit.com).
-
-## Cut over migration with svn2git
-
-NOTE:
-Any issues with svn2git should be directed to the [relevant project and maintainer](https://github.com/nirvdrum/svn2git).
-Check for existing issues and history for update frequency.
-
-If you are currently using an SVN repository, you can migrate the repository
-to Git and GitLab. We recommend a hard cut over - run the migration command once
-and then have all developers start using the new GitLab repository immediately.
-Otherwise, it's hard to keep changing in sync in both directions. The conversion
-process should be run on a local workstation.
-
-Install `svn2git`. 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
-```
-
-Optionally, prepare an authors file so `svn2git` can map SVN authors to Git authors.
-If you choose not to create the authors file then commits are not attributed
-to the correct GitLab user. Some users may not consider this a big issue while
-others want to ensure they complete this step. If you choose to map authors,
-you must map every author present on changes in the SVN
-repository. If you don't, the conversion fails and you have to update
-the author file accordingly. The following command searches through the
-repository and output a list of authors.
-
-```shell
-svn log --quiet | grep -E "r[0-9]+ \| .+ \|" | cut -d'|' -f2 | sed 's/ //g' | sort | uniq
-```
-
-Use the output from the last command to construct the authors file.
-Create a file called `authors.txt` and add one mapping per line.
-
-```plaintext
-janedoe = Jane Doe <janedoe@example.com>
-johndoe = John Doe <johndoe@example.com>
-```
-
-If your SVN repository is in the standard format (trunk, branches, tags,
-not nested) the conversion is simple. For a non-standard repository see
-[svn2git documentation](https://github.com/nirvdrum/svn2git). The following
-command will checkout the repository and do the conversion in the current
-working directory. Be sure to create a new directory for each repository before
-running the `svn2git` command. The conversion process takes some time.
-
-```shell
-svn2git https://svn.example.com/path/to/repo --authors /path/to/authors.txt
-```
-
-If your SVN repository requires a username and password add the
-`--username <username>` and `--password <password>` flags to the above command.
-`svn2git` also supports excluding certain file paths, branches, tags, and so on. See
-[svn2git documentation](https://github.com/nirvdrum/svn2git) or run
-`svn2git --help` for full documentation on all of the available options.
-
-Create a new GitLab project, into which you push your converted code.
-Copy the SSH or HTTP(S) repository URL from the project page. 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.com:<group>/<project>.git
-git push --all origin
-git push --tags origin
-```
-
-## Contribute to this guide
-
-We welcome all contributions that would expand this guide with instructions on
-how to migrate from SVN and other version control systems.
+ ```shell
+ git remote add origin git@gitlab.example.com:<group>/<project>.git
+ git push --all origin
+ git push --tags origin
+ ```
diff --git a/doc/user/project/insights/index.md b/doc/user/project/insights/index.md
index 53547e69e00..81293fb1645 100644
--- a/doc/user/project/insights/index.md
+++ b/doc/user/project/insights/index.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Optimize
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
---
@@ -158,7 +158,7 @@ Supported values are:
| `stacked-bar` | ![Insights example stacked bar chart](img/insights_example_stacked_bar_chart.png) |
NOTE:
-The `dora` data source only supports the `bar` chart type.
+The `dora` data source supports the `bar` and `line` chart types.
### `query`
@@ -352,7 +352,7 @@ dora:
title: "DORA charts"
charts:
- title: "DORA deployment frequency"
- type: bar # only bar chart is supported at the moment
+ type: bar # or line
query:
data_source: dora
params:
diff --git a/doc/user/project/integrations/asana.md b/doc/user/project/integrations/asana.md
index a10e261f10e..07b37b5be43 100644
--- a/doc/user/project/integrations/asana.md
+++ b/doc/user/project/integrations/asana.md
@@ -32,7 +32,7 @@ In Asana, create a Personal Access Token.
Complete these steps in GitLab:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **Asana**.
1. Ensure that the **Active** toggle is enabled.
diff --git a/doc/user/project/integrations/bamboo.md b/doc/user/project/integrations/bamboo.md
index 75f099268cb..7b39f6c7162 100644
--- a/doc/user/project/integrations/bamboo.md
+++ b/doc/user/project/integrations/bamboo.md
@@ -36,7 +36,7 @@ integration in GitLab.
## Configure GitLab
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **Atlassian Bamboo**.
1. Ensure the **Active** checkbox is selected.
diff --git a/doc/user/project/integrations/bugzilla.md b/doc/user/project/integrations/bugzilla.md
index ac4b9d0769b..f058950e0f4 100644
--- a/doc/user/project/integrations/bugzilla.md
+++ b/doc/user/project/integrations/bugzilla.md
@@ -14,7 +14,7 @@ You can configure Bugzilla as an
To enable the Bugzilla integration in a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **Bugzilla**.
1. Select the checkbox under **Enable integration**.
diff --git a/doc/user/project/integrations/custom_issue_tracker.md b/doc/user/project/integrations/custom_issue_tracker.md
index 71ca9f4f640..c3794aa1a2b 100644
--- a/doc/user/project/integrations/custom_issue_tracker.md
+++ b/doc/user/project/integrations/custom_issue_tracker.md
@@ -20,7 +20,7 @@ on the left sidebar in your project.
To enable a custom issue tracker in a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **Custom issue tracker**.
1. Select the checkbox under **Enable integration**.
diff --git a/doc/user/project/integrations/discord_notifications.md b/doc/user/project/integrations/discord_notifications.md
index 3780ea37c0b..dffcc780206 100644
--- a/doc/user/project/integrations/discord_notifications.md
+++ b/doc/user/project/integrations/discord_notifications.md
@@ -26,7 +26,7 @@ and configure it in GitLab.
With the webhook URL created in the Discord channel, you can set up the Discord Notifications service in GitLab.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **Discord Notifications**.
1. Ensure that the **Active** toggle is enabled.
diff --git a/doc/user/project/integrations/emails_on_push.md b/doc/user/project/integrations/emails_on_push.md
index c1c48c7fb12..37d9a86f8fa 100644
--- a/doc/user/project/integrations/emails_on_push.md
+++ b/doc/user/project/integrations/emails_on_push.md
@@ -11,7 +11,7 @@ that is pushed to your project.
To enable emails on push:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **Emails on push**.
1. In the **Recipients** section, provide a list of emails separated by spaces or newlines.
diff --git a/doc/user/project/integrations/ewm.md b/doc/user/project/integrations/ewm.md
index b02f1a06e96..45f3653757d 100644
--- a/doc/user/project/integrations/ewm.md
+++ b/doc/user/project/integrations/ewm.md
@@ -14,7 +14,7 @@ This IBM product was [formerly named Rational Team Concert](https://jazz.net/blo
To enable the EWM integration, in a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **EWM**.
1. Select the checkbox under **Enable integration**.
diff --git a/doc/user/project/integrations/github.md b/doc/user/project/integrations/github.md
index c07142d6edf..4be541d99cb 100644
--- a/doc/user/project/integrations/github.md
+++ b/doc/user/project/integrations/github.md
@@ -29,7 +29,7 @@ Complete these steps on GitHub:
Complete these steps in GitLab:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **GitHub**.
1. Ensure the **Active** checkbox is selected.
@@ -39,7 +39,7 @@ Complete these steps in GitLab:
1. Optional. Select **Test settings**.
1. Select **Save changes**.
-After configuring the integration, see [Pipelines for external pull requests](../../../ci/ci_cd_for_external_repos/#pipelines-for-external-pull-requests)
+After configuring the integration, see [Pipelines for external pull requests](../../../ci/ci_cd_for_external_repos/index.md#pipelines-for-external-pull-requests)
to configure pipelines to run for open pull requests.
### Static or dynamic status check names
diff --git a/doc/user/project/integrations/gitlab_slack_application.md b/doc/user/project/integrations/gitlab_slack_application.md
index dc56c2669f8..afc379e7a07 100644
--- a/doc/user/project/integrations/gitlab_slack_application.md
+++ b/doc/user/project/integrations/gitlab_slack_application.md
@@ -74,7 +74,6 @@ Slack user on GitLab.com.
The only difference with the [manually configurable Slack slash commands](slack_slash_commands.md)
is that all the commands should be prefixed with the `/gitlab` keyword.
-We are working on making this configurable in the future.
For example, to show the issue number `1001` under the `gitlab-org/gitlab`
project, you would do:
diff --git a/doc/user/project/integrations/hangouts_chat.md b/doc/user/project/integrations/hangouts_chat.md
index fbfa7d914a5..b2586383b43 100644
--- a/doc/user/project/integrations/hangouts_chat.md
+++ b/doc/user/project/integrations/hangouts_chat.md
@@ -10,7 +10,7 @@ Integrate your project to send notifications from GitLab to a
room of your choice in [Google Chat](https://chat.google.com/) (former Google
Hangouts).
-## How it works
+## Integration workflow
To enable this integration, first you need to create a webhook for the room in
Google Chat where you want to receive the notifications from your project.
@@ -23,9 +23,9 @@ notifications to Google Chat:
![Google Chat integration illustration](img/google_chat_integration_v13_11.png)
-## In Google Chat
+## Enable the integration in Google Chat
-Select a room and create a webhook:
+To enable the integration in Google Chat:
1. Enter the room where you want to receive notifications from GitLab.
1. Open the room dropdown menu on the top-left and select **Manage webhooks**.
@@ -36,9 +36,22 @@ Select a room and create a webhook:
For further details, see [the Google Chat documentation for configuring webhooks](https://developers.google.com/chat/how-tos/webhooks).
-## In GitLab
+### Enable threads in Google Chat
-Enable the Google Chat integration in GitLab:
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/27823) in GitLab 15.4.
+
+To enable threaded notifications for the same GitLab object (for example, an issue or merge request):
+
+1. Go to [Google Chat](https://chat.google.com/).
+1. In **Spaces**, select **+ > Create space**.
+1. Enter the space name and (optionally) other details, and select **Use threaded replies**.
+1. Select **Create**.
+
+You cannot enable threaded replies for existing Google Chat spaces.
+
+## Enable the integration in GitLab
+
+To enable the integration in GitLab:
1. In your project, go to **Settings > Integrations** and select **Google Chat**.
1. Scroll down to the end of the page where you find a **Webhook** field.
diff --git a/doc/user/project/integrations/harbor.md b/doc/user/project/integrations/harbor.md
index da35f0dc226..535703ff59e 100644
--- a/doc/user/project/integrations/harbor.md
+++ b/doc/user/project/integrations/harbor.md
@@ -25,7 +25,7 @@ In the Harbor instance, ensure that:
GitLab supports integrating Harbor projects at the group or project level. Complete these steps in GitLab:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **Harbor**.
1. Turn on the **Active** toggle under **Enable Integration**.
@@ -42,7 +42,9 @@ After the Harbor integration is activated:
- The global variables `$HARBOR_USERNAME`, `$HARBOR_HOST`, `$HARBOR_OCI`, `$HARBOR_PASSWORD`, `$HARBOR_URL`, and `$HARBOR_PROJECT` are created for CI/CD use.
- The project-level integration settings override the group-level integration settings.
-## Secure your requests to the Harbor APIs
+## Security considerations
+
+### Secure your requests to the Harbor APIs
For each API request through the Harbor integration, the credentials for your connection to the Harbor API use
the `username:password` combination. The following are suggestions for safe use:
@@ -51,6 +53,12 @@ the `username:password` combination. The following are suggestions for safe use:
- Follow the principle of least privilege (for access on Harbor) with your credentials.
- Have a rotation policy on your credentials.
+### CI/CD variable security
+
+Malicious code pushed to your `.gitlab-ci.yml` file could compromise your variables, including
+`$HARBOR_PASSWORD`, and send them to a third-party server. For more details, see
+[CI/CD variable security](../../../ci/variables/index.md#cicd-variable-security).
+
## Examples of Harbor variables in CI/CD
### Push a Docker image with kaniko
diff --git a/doc/user/project/integrations/img/mattermost_add_slash_command.png b/doc/user/project/integrations/img/mattermost_add_slash_command.png
deleted file mode 100644
index 7759efa183c..00000000000
--- a/doc/user/project/integrations/img/mattermost_add_slash_command.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/integrations/img/mattermost_bot_auth.png b/doc/user/project/integrations/img/mattermost_bot_auth.png
deleted file mode 100644
index a05d8da1237..00000000000
--- a/doc/user/project/integrations/img/mattermost_bot_auth.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/integrations/img/mattermost_bot_available_commands.png b/doc/user/project/integrations/img/mattermost_bot_available_commands.png
deleted file mode 100644
index 3232ccc3451..00000000000
--- a/doc/user/project/integrations/img/mattermost_bot_available_commands.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/integrations/img/mattermost_gitlab_token.png b/doc/user/project/integrations/img/mattermost_gitlab_token.png
deleted file mode 100644
index 63140503824..00000000000
--- a/doc/user/project/integrations/img/mattermost_gitlab_token.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/integrations/index.md b/doc/user/project/integrations/index.md
index 3ef40fdfe44..18e827f8df8 100644
--- a/doc/user/project/integrations/index.md
+++ b/doc/user/project/integrations/index.md
@@ -18,7 +18,7 @@ Prerequisites:
To view the available integrations for your project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
You can also view and manage integration settings across [all projects in an instance or group](../../admin_area/settings/project_integration_management.md).
@@ -76,6 +76,7 @@ You can configure the following integrations.
| [Pumble](pumble.md) | Send event notifications to a Pumble channel. | **{dotted-circle}** No |
| 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 |
| [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 |
diff --git a/doc/user/project/integrations/irker.md b/doc/user/project/integrations/irker.md
index b2c2aea2c2b..5f7de09cc9d 100644
--- a/doc/user/project/integrations/irker.md
+++ b/doc/user/project/integrations/irker.md
@@ -39,7 +39,7 @@ network. For more details, read
## Complete these steps in GitLab
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **irker (IRC gateway)**.
1. Ensure that the **Active** toggle is enabled.
diff --git a/doc/user/project/integrations/mattermost.md b/doc/user/project/integrations/mattermost.md
index 7dd4c1d1a8b..12575e34058 100644
--- a/doc/user/project/integrations/mattermost.md
+++ b/doc/user/project/integrations/mattermost.md
@@ -39,7 +39,7 @@ Display name override is not enabled by default, you need to ask your administra
After the Mattermost instance has an incoming webhook set up, you can set up GitLab
to send the notifications:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **Mattermost notifications**.
1. Select the GitLab events to generate notifications for. For each event you select, input the Mattermost channel
diff --git a/doc/user/project/integrations/mattermost_slash_commands.md b/doc/user/project/integrations/mattermost_slash_commands.md
index 768acb02ee6..28a5f2eec18 100644
--- a/doc/user/project/integrations/mattermost_slash_commands.md
+++ b/doc/user/project/integrations/mattermost_slash_commands.md
@@ -6,48 +6,46 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Mattermost slash commands **(FREE)**
-If your team uses [Mattermost](https://mattermost.com/) as a chat service, you can
-integrate GitLab commands into Mattermost chat. This integration enables users to
-run common operations, such as creating a GitLab issue, from the Mattermost chat
-environment.
+You can use slash commands to run common GitLab operations, like creating an issue,
+from a [Mattermost](https://mattermost.com/) chat environment.
GitLab can also send events (such as `issue created`) to Mattermost as part of the
-separately configured [Mattermost Notifications Service](mattermost.md).
+separately configured [Mattermost notifications](mattermost.md).
-## Prerequisites
+## Configuration options
-Mattermost [3.4 or later](https://mattermost.com/blog/category/platform/releases/) is required.
-GitLab provides different methods of configuring Mattermost slash commands, depending
-on your configuration:
+GitLab provides different ways to configure Mattermost slash commands. For any of these options,
+you must have Mattermost [3.4 or later](https://mattermost.com/blog/category/platform/releases/).
- **Omnibus GitLab installations**: Mattermost is bundled with
- [Omnibus GitLab](https://docs.gitlab.com/omnibus/). To configure Mattermost for Omnibus GitLab, read the
- [Omnibus GitLab Mattermost documentation](../../../integration/mattermost/index.md).
+ [Omnibus GitLab](https://docs.gitlab.com/omnibus/). To configure Mattermost for Omnibus GitLab,
+ read the [Omnibus GitLab Mattermost documentation](../../../integration/mattermost/index.md).
- **If Mattermost is installed on the same server as GitLab**, use the
- [automated configuration](#automated-configuration).
-- **For all other installations**, use the [manual configuration](#manual-configuration).
+ [automated configuration](#configure-automatically).
+- **For all other installations**, use the [manual configuration](#configure-manually).
-## Automated configuration
+## Configure automatically
-If Mattermost is installed on the same server as GitLab, the configuration process can be
-done for you by GitLab.
+If Mattermost is installed on the same server as GitLab,
+you can automatically configure Mattermost slash commands:
-Go to the Mattermost Slash Command service on your project and select **Add to Mattermost**.
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > Integrations**.
+1. In **Add an integration**, select **Mattermost slash commands**.
+1. In **Enable integration**, ensure the **Active** checkbox is selected.
+1. Select **Add to Mattermost**, and select **Save changes**.
-## Manual configuration
+## Configure manually
To manually configure slash commands in Mattermost, you must:
-1. [Enable custom slash commands](#enable-custom-slash-commands) in Mattermost.
-1. [Get configuration values](#get-configuration-values-from-gitlab) from GitLab.
-1. [Create a new slash command](#create-a-slash-command) in Mattermost.
-1. [Provide the Mattermost token](#provide-the-mattermost-token-to-gitlab) to GitLab.
+1. [Enable custom slash commands in Mattermost](#enable-custom-slash-commands-in-mattermost).
+ (This step is required only for installations from source.)
+1. [Get configuration values from GitLab](#get-configuration-values-from-gitlab).
+1. [Create a slash command in Mattermost](#create-a-slash-command-in-mattermost).
+1. [Provide the Mattermost token to GitLab](#provide-the-mattermost-token-to-gitlab).
-### Enable custom slash commands
-
-NOTE:
-Omnibus GitLab installations are preconfigured. This step is required only for
-installations from source.
+### Enable custom slash commands in Mattermost
To enable custom slash commands from the Mattermost administrator console:
@@ -58,80 +56,68 @@ To enable custom slash commands from the Mattermost administrator console:
- **Enable Custom Slash Commands**
- **Enable integrations to override usernames**
- **Enable integrations to override profile picture icons**
-1. Select **Save**, but do not close this browser tab, because you need it in
+1. Select **Save**, but do not close this browser tab. You need it in
a later step.
### Get configuration values from GitLab
-After you enable custom slash commands in Mattermost, you need configuration
-information from GitLab. To get this information:
+To get configuration values from GitLab:
-1. In a different browser tab than your current Mattermost session, sign in to
+1. In a different browser tab, sign in to
GitLab as a user with administrator access.
-1. On the top bar, select **Menu > Admin**.
-1. In the left menu, select **Settings > Integrations**, then select
- **Mattermost slash commands**.
-1. GitLab displays potential values for Mattermost settings. Copy the **Request URL**
- as you need it for the next step. All other values are suggestions.
-1. Do not close this browser tab, because you need it in future steps.
-
-Next, create a slash command in Mattermost with the values from GitLab.
+1. On the top bar, select **Main menu > Admin**.
+1. On the left sidebar, select **Settings > Integrations**.
+1. Select **Mattermost slash commands**. GitLab displays potential values for Mattermost settings.
+1. Copy the **Request URL** value. All other values are suggestions.
+1. Do not close this browser tab. You need it in a later step.
-### Create a slash command
+### Create a slash command in Mattermost
-To create a slash command, you need the values you obtained from GitLab in
-the previous step:
+To create a slash command in Mattermost:
-1. In the Mattermost tab you left open when you
- [enabled custom slash commands](#enable-custom-slash-commands), go to your
- team page.
+1. [In the Mattermost browser tab](#enable-custom-slash-commands-in-mattermost),
+ go to your team page.
1. Select the **{ellipsis_v}** **Settings** icon, and select **Integrations**.
-1. In the left menu, select **Slash commands**.
-1. Select **Add Slash Command**:
-
- ![Mattermost add command](img/mattermost_add_slash_command.png)
+1. On the left sidebar, select **Slash commands**.
+1. Select **Add Slash Command**.
1. Provide a **Display Name** and **Description** for your new command.
-1. Provide a **Command Trigger Word** according to your application's configuration:
+1. Provide a **Command Trigger Word** based on your application's configuration:
- - **If you intend to only connect one project to your Mattermost team**: Use
+ - **If you intend to only connect one project to your Mattermost team**, use
`/gitlab` for your trigger word.
- - **If you intend to connect multiple projects**: Use a trigger word that relates
+ - **If you intend to connect multiple projects**, use a trigger word that relates
to your project, such as `/project-name` or `/gitlab-project-name`.
-1. For **Request URL**, provide the value you copied from GitLab when you
- [viewed configuration values](#get-configuration-values-from-gitlab).
-1. For all other values, you may use the suggestions from GitLab or use your
+1. For **Request URL**, [paste the value you copied from GitLab](#get-configuration-values-from-gitlab).
+1. For all other values, you may use the suggestions from GitLab or your
preferred values.
-1. Copy the **Token** value, as you need it in a later step, and select **Done**.
+1. Copy the **Token** value, and select **Done**.
### Provide the Mattermost token to GitLab
-When you create a new slash command in Mattermost, it generates a token you must
+Creating a slash command in Mattermost generates a token you must
provide to GitLab:
-1. In the GitLab browser tab from
- [getting configuration values from GitLab](#get-configuration-values-from-gitlab),
- select the **Active** checkbox to enable this configuration.
-1. In the **Token** field, paste the token you obtained from Mattermost.
- ensure that the **Active** toggle is enabled.
-
- ![Mattermost copy token to GitLab](img/mattermost_gitlab_token.png)
-
-1. Select **Save changes** for the changes to take effect.
+1. [In the GitLab browser tab](#get-configuration-values-from-gitlab),
+ select the **Active** checkbox.
+1. In the **Token** text box, [paste the token you copied from Mattermost](#create-a-slash-command-in-mattermost).
+1. Select **Save changes**.
Your slash command can now communicate with your GitLab project.
-## Authorizing Mattermost to interact with GitLab
+## Connect your GitLab account to Mattermost
-The first time a user interacts with the newly created slash commands,
-Mattermost triggers an authorization process.
+Prerequisite:
-![Mattermost bot authorize](img/mattermost_bot_auth.png)
+- To run [slash commands](#available-slash-commands), you must have
+ [permission](../../permissions.md#project-members-permissions) to
+ perform the action in the GitLab project.
-This connects your Mattermost user with your GitLab user. You can
-see all authorized chat accounts in your profile's page under **Chat**.
+To interact with GitLab using Mattermost slash commands:
-When the authorization process is complete, you can start interacting with
-GitLab using the Mattermost commands.
+1. In a Mattermost chat environment, run your new slash command.
+1. Select **connect your GitLab account** to authorize access.
+
+You can see all authorized chat accounts in your Mattermost profile page under **Chat**.
## Available slash commands
@@ -139,30 +125,21 @@ The available slash commands for Mattermost are:
| Command | Description | Example |
| ------- | ----------- | ------- |
-| <kbd>/&lt;trigger&gt; issue new &lt;title&gt; <kbd>⇧ Shift</kbd>+<kbd>↵ Enter</kbd> &lt;description&gt;</kbd> | Create a new issue in the project that `<trigger>` is tied to. `<description>` is optional. | `/gitlab issue new We need to change the homepage` |
-| <kbd>/&lt;trigger&gt; issue show &lt;issue-number&gt;</kbd> | Show the issue with ID `<issue-number>` from the project that `<trigger>` is tied to. | `/gitlab issue show 42` |
-| <kbd>/&lt;trigger&gt; deploy &lt;environment&gt; to &lt;environment&gt;</kbd> | Start the CI job that deploys from one environment to another, for example `staging` to `production`. CI/CD must be [properly configured](../../../ci/yaml/index.md). | `/gitlab deploy staging to production` |
-
-To see a list of available commands to interact with GitLab, type the
-trigger word followed by <kbd>help</kbd>. Example: `/gitlab help`
-
-![Mattermost bot available commands](img/mattermost_bot_available_commands.png)
+| `/<trigger> issue new <title>` <kbd>Shift</kbd>+<kbd>Enter</kbd> `<description>` | Create a new issue in the project that `<trigger>` is tied to. `<description>` is optional. | `/gitlab issue new We need to change the homepage` |
+| `/<trigger> issue show <issue-number>` | Show the issue with ID `<issue-number>` from the project that `<trigger>` is tied to. | `/gitlab issue show 42` |
+| `/<trigger> deploy <environment> to <environment>` | Start the CI/CD job that deploys from one environment to another (for example, `staging` to `production`). CI/CD must be [properly configured](../../../ci/yaml/index.md). | `/gitlab deploy staging to production` |
+| `/<trigger> help` | View a list of available slash commands. | `/gitlab help` |
-## Permissions
+## Related topics
-The permissions to run the [available commands](#available-slash-commands) derive from
-the [permissions you have on the project](../../permissions.md#project-members-permissions).
+- [Mattermost slash commands](https://developers.mattermost.com/integrate/slash-commands/)
+- [Omnibus GitLab Mattermost](../../../integration/mattermost/index.md)
## Troubleshooting
-If an event is not being triggered, confirm that the channel you're using is a public one.
-Mattermost webhooks do not have access to private channels.
-
-If a private channel is required, you can edit the webhook's channel in Mattermost and
-select a private channel. It is not possible to use different channels for
-different types of notifications. All events are sent to the specified channel.
-
-## Further reading
+When a Mattermost slash command does not trigger an event in GitLab:
-- [Mattermost slash commands documentation](https://docs.mattermost.com/developer/slash-commands.html)
-- [Omnibus GitLab Mattermost](../../../integration/mattermost/)
+- Ensure you're using a public channel.
+ Mattermost webhooks do not have access to private channels.
+- If you require a private channel, edit the webhook channel,
+ and select a private one. All events are sent to the specified channel.
diff --git a/doc/user/project/integrations/microsoft_teams.md b/doc/user/project/integrations/microsoft_teams.md
index 6679bab745b..2e6954390fb 100644
--- a/doc/user/project/integrations/microsoft_teams.md
+++ b/doc/user/project/integrations/microsoft_teams.md
@@ -35,7 +35,7 @@ After you configure Microsoft Teams to receive notifications, you must configure
GitLab to send the notifications:
1. Sign in to GitLab as an administrator.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **Microsoft Teams notifications**.
1. To enable the integration, select **Active**.
diff --git a/doc/user/project/integrations/pivotal_tracker.md b/doc/user/project/integrations/pivotal_tracker.md
index 7f5414b86de..a0798da21f0 100644
--- a/doc/user/project/integrations/pivotal_tracker.md
+++ b/doc/user/project/integrations/pivotal_tracker.md
@@ -37,7 +37,7 @@ In Pivotal Tracker, [create an API token](https://www.pivotaltracker.com/help/ar
Complete these steps in GitLab:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **Pivotal Tracker**.
1. Ensure that the **Active** toggle is enabled.
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index 068a2810a53..c1181169261 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -62,7 +62,7 @@ GitLab can use these to access the resource. More information about authenticati
service account can be found at Google's documentation for
[Authenticating from a service account](https://cloud.google.com/iap/docs/authentication-howto#authenticating_from_a_service_account).
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **Prometheus**.
1. For **API URL**, provide the domain name or IP address of your server, such as
@@ -83,7 +83,7 @@ You can configure [Thanos](https://thanos.io/) as a drop-in replacement for Prom
with GitLab. Use the domain name or IP address of the Thanos server you'd like
to integrate with.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **Prometheus**.
1. Provide the domain name or IP address of your server, for example
diff --git a/doc/user/project/integrations/pumble.md b/doc/user/project/integrations/pumble.md
index cd28a7c0048..0eb3a38bb86 100644
--- a/doc/user/project/integrations/pumble.md
+++ b/doc/user/project/integrations/pumble.md
@@ -25,8 +25,8 @@ notifications:
1. To enable the integration for your group or project:
1. In your group or project, on the left sidebar, select **Settings > Integrations**.
-1. To enable the integration for your instance:
- 1. On the top bar, select **Menu > Admin**.
+1. To enable the integration for your instance:
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Integrations**.
1. Select the **Pumble** integration.
1. Ensure that the **Active** toggle is enabled.
@@ -36,4 +36,4 @@ notifications:
1. Optional. To test the integration, select **Test settings**.
1. Select **Save changes**.
-The Pumble channel begins to receive all applicable GitLab events.
+The Pumble channel begins to receive all applicable GitLab events.
diff --git a/doc/user/project/integrations/redmine.md b/doc/user/project/integrations/redmine.md
index a989b418199..bc1d299dccf 100644
--- a/doc/user/project/integrations/redmine.md
+++ b/doc/user/project/integrations/redmine.md
@@ -10,7 +10,7 @@ Use [Redmine](https://www.redmine.org/) as the issue tracker.
To enable the Redmine integration in a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **Redmine**.
1. Select the checkbox under **Enable integration**.
diff --git a/doc/user/project/integrations/servicenow.md b/doc/user/project/integrations/servicenow.md
index fdcbb498621..dc6c2da0d91 100644
--- a/doc/user/project/integrations/servicenow.md
+++ b/doc/user/project/integrations/servicenow.md
@@ -15,7 +15,7 @@ With the GitLab spoke in ServiceNow, you can automate actions for GitLab
projects, groups, users, issues, merge requests, branches, and repositories.
For a full list of features, see the
-[GitLab spoke documentation](https://docs.servicenow.com/bundle/orlando-servicenow-platform/page/administer/integrationhub-store-spokes/concept/gitlab-spoke.html).
+[GitLab spoke documentation](https://docs.servicenow.com/bundle/sandiego-application-development/page/administer/integrationhub-store-spokes/concept/gitlab-spoke.html).
You must [configure GitLab as an OAuth2 authentication service provider](../../../integration/oauth_provider.md),
which involves creating an application and then providing the Application ID
diff --git a/doc/user/project/integrations/shimo.md b/doc/user/project/integrations/shimo.md
new file mode 100644
index 00000000000..ea92b8b3f0b
--- /dev/null
+++ b/doc/user/project/integrations/shimo.md
@@ -0,0 +1,34 @@
+---
+stage: Ecosystem
+group: Integrations
+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
+---
+
+# Shimo Workspace integration **(FREE)**
+
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/343386) in GitLab 14.5 with a feature flag named `shimo_integration`. Disabled by default.
+> - [Enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/345356) in GitLab 15.4.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/345356) in GitLab 15.4. [Feature flag `shimo_integration`](https://gitlab.com/gitlab-org/gitlab/-/issues/345356) removed.
+
+[Shimo](https://shimo.im/) is a productivity suite that includes documents, spreadsheets, and slideshows in one interface. With this integration, you can use the Shimo Wiki directly within GitLab instead of the [GitLab group/project wiki](../wiki/index.md).
+
+## Configure settings in GitLab
+
+To enable the Shimo Workspace integration for your group or project:
+
+1. On the top bar, select **Main menu** and find your group or project.
+1. On the left sidebar, select **Settings > Integrations**.
+1. In **Add an integration**, select **Shimo Workspace**.
+1. In **Enable integration**, ensure the **Active** checkbox is selected.
+1. Provide the **Shimo Workspace URL** you want to link to your group or project (for example, `https://shimo.im/space/aBAYV6VvajUP873j`).
+1. Select **Save changes**.
+
+On the left sidebar, **Shimo** now appears instead of **Wiki**.
+
+## View the Shimo Workspace
+
+To view the Shimo Workspace from your group or project:
+
+1. On the top bar, select **Main menu** and find your group or project.
+1. On the left sidebar, select **Shimo**.
+1. On the **Shimo Workspace** page, select **Go to Shimo Workspace**.
diff --git a/doc/user/project/integrations/slack.md b/doc/user/project/integrations/slack.md
index dd0f57570aa..ae2e57a6d7f 100644
--- a/doc/user/project/integrations/slack.md
+++ b/doc/user/project/integrations/slack.md
@@ -22,7 +22,7 @@ to control GitLab from Slack. Slash commands are configured separately.
## Configure GitLab
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **Slack notifications**.
1. In the **Enable integration** section, select the **Active** checkbox.
diff --git a/doc/user/project/integrations/slack_slash_commands.md b/doc/user/project/integrations/slack_slash_commands.md
index 5ad344a7d8e..67d6befb5fc 100644
--- a/doc/user/project/integrations/slack_slash_commands.md
+++ b/doc/user/project/integrations/slack_slash_commands.md
@@ -21,7 +21,7 @@ For GitLab.com, use the [GitLab Slack app](gitlab_slack_application.md) instead.
Slack slash command integrations
are scoped to a project.
-1. In GitLab, on the top bar, select **Menu > Projects** and find your project.
+1. In GitLab, on the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **Slack slash commands**. Leave this browser tab open.
1. Open a new browser tab, sign in to your Slack team, and [start a new Slash Commands integration](https://my.slack.com/services/new/slash-commands).
diff --git a/doc/user/project/integrations/unify_circuit.md b/doc/user/project/integrations/unify_circuit.md
index 1e607d89e80..91beefd30ab 100644
--- a/doc/user/project/integrations/unify_circuit.md
+++ b/doc/user/project/integrations/unify_circuit.md
@@ -15,7 +15,7 @@ copy its URL.
In GitLab:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **Unify Circuit**.
1. Turn on the **Active** toggle.
diff --git a/doc/user/project/integrations/webex_teams.md b/doc/user/project/integrations/webex_teams.md
index e8b2470cf13..0b487e29d26 100644
--- a/doc/user/project/integrations/webex_teams.md
+++ b/doc/user/project/integrations/webex_teams.md
@@ -27,7 +27,7 @@ notifications:
1. Navigate to:
- **Settings > Integrations** in a project to enable the integration at the project level.
- **Settings > Integrations** in a group to enable the integration at the group level.
- - On the top bar, select **Menu > Admin**. Then, in the left sidebar,
+ - On the top bar, select **Main menu > Admin**. Then, in the left sidebar,
select **Settings > Integrations** to enable an instance-level integration.
1. Select the **Webex Teams** integration.
1. Ensure that the **Active** toggle is enabled.
diff --git a/doc/user/project/integrations/youtrack.md b/doc/user/project/integrations/youtrack.md
index 25fc9c4e1c3..e6071e7517d 100644
--- a/doc/user/project/integrations/youtrack.md
+++ b/doc/user/project/integrations/youtrack.md
@@ -14,7 +14,7 @@ You can configure YouTrack as an
To enable the YouTrack integration in a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **YouTrack**.
1. Select the checkbox under **Enable integration**.
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index 787e990526d..916d566bb20 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -8,14 +8,15 @@ info: To determine the technical writer assigned to the Stage/Group associated w
The issue board is a software project management tool used to plan,
organize, and visualize a workflow for a feature or product release.
-It can be used as a [Kanban](https://en.wikipedia.org/wiki/Kanban_(development)) or a
+
+You can use it as a [Kanban](https://en.wikipedia.org/wiki/Kanban_(development)) or a
[Scrum](https://en.wikipedia.org/wiki/Scrum_(software_development)) board.
-It pairs issue tracking and project management, keeping everything together,
-so that you don't need to jump between different platforms to organize your workflow.
+Issue boards pair issue tracking and project management, keeping everything together,
+so you can organize your workflow on a single platform.
-Issue boards build on the existing [issue tracking functionality](issues/index.md) and
-[labels](labels.md). Your issues appear as cards in vertical lists, organized by their assigned
+Issue boards use [issues](issues/index.md) and [labels](labels.md).
+Your issues appear as cards in vertical lists, organized by their assigned
labels, [milestones](#milestone-lists), or [assignees](#assignee-lists).
Issue boards help you to visualize and manage your entire process in GitLab.
@@ -31,21 +32,20 @@ boards in the same project.
![GitLab issue board - Core](img/issue_boards_core_v14_1.png)
-Different issue board features are available in different [GitLab tiers](https://about.gitlab.com/pricing/),
-as shown in the following table:
+Different issue board features are available in different [GitLab tiers](https://about.gitlab.com/pricing/):
| Tier | Number of project issue boards | Number of [group issue boards](#group-issue-boards) | [Configurable issue boards](#configurable-issue-boards) | [Assignee lists](#assignee-lists) |
| -------- | ------------------------------ | --------------------------------------------------- | ------------------------------------------------------- | --------------------------------- |
-| Free | Multiple | 1 | No | No |
-| Premium | Multiple | Multiple | Yes | Yes |
-| Ultimate | Multiple | Multiple | Yes | Yes |
+| Free | Multiple | 1 | **{dotted-circle}** No | **{dotted-circle}** No |
+| Premium | Multiple | Multiple | **{check-circle}** Yes | **{check-circle}** Yes |
+| Ultimate | Multiple | Multiple | **{check-circle}** Yes | **{check-circle}** Yes |
-To learn more, visit [GitLab Enterprise features for issue boards](#gitlab-enterprise-features-for-issue-boards) below.
+Read more about [GitLab Enterprise features for issue boards](#gitlab-enterprise-features-for-issue-boards).
![GitLab issue board - Premium](img/issue_boards_premium_v14_1.png)
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
-Watch a [video presentation](https://youtu.be/vjccjHI7aGI) of
+Watch a [video presentation](https://youtu.be/vjccjHI7aGI) (April 2020) of
the issue board feature.
## Multiple issue boards
@@ -68,17 +68,25 @@ GitLab automatically loads the last board you visited.
### Create an issue board
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
To create a new issue board:
-1. Select the dropdown with the current board name in the upper left corner of the issue boards page.
+1. Select the dropdown list with the current board name in the upper left corner of the issue boards page.
1. Select **Create new board**.
1. Enter the new board's name and select its scope: milestone, labels, assignee, or weight.
### Delete an issue board
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
To delete the currently active issue board:
-1. Select the dropdown with the current board name in the upper left corner of the issue boards page.
+1. Select the dropdown list with the current board name in the upper left corner of the issue boards page.
1. Select **Delete board**.
1. Select **Delete** to confirm.
@@ -192,12 +200,11 @@ card includes:
- Issue number
- Assignee
-## Permissions
+## Ordering issues in a list
-Users with at least the Reporter role can use all the functionality of the
-issue board feature to create or delete lists. They can also drag issues from one list to another.
+Prerequisites:
-## Ordering issues in a list
+- You must have at least the Reporter role for the project.
When an issue is created, the system assigns a relative order value that is greater than the maximum value
of that issue's project or root group. This means the issue will be at the bottom of any issue list that
@@ -275,16 +282,19 @@ Users on GitLab Free can use a single group issue board.
### Assignee lists **(PREMIUM)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/5784) in GitLab 11.0.
-
As in a regular list showing all issues with a chosen label, you can add
an assignee list that shows all issues assigned to a user.
-You can have a board with both label lists and assignee lists. To add an
-assignee list:
+You can have a board with both label lists and assignee lists.
+
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
+To add an assignee list:
1. Select **Create list**.
1. Select **Assignee**.
-1. In the dropdown, select a user.
+1. In the dropdown list, select a user.
1. Select **Add to board**.
Now that the assignee list is added, you can assign or unassign issues to that user
@@ -295,14 +305,18 @@ To remove an assignee list, just as with a label list, select the trash icon.
### Milestone lists **(PREMIUM)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/6469) in GitLab 11.2.
-
You're also able to create lists of a milestone. These are lists that filter issues by the assigned
-milestone, giving you more freedom and visibility on the issue board. To add a milestone list:
+milestone, giving you more freedom and visibility on the issue board.
+
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
+To add a milestone list:
1. Select **Create list**.
1. Select **Milestone**.
-1. In the dropdown, select a milestone.
+1. In the dropdown list, select a milestone.
1. Select **Add to board**.
Like the assignee lists, you're able to [drag issues](#move-issues-and-lists)
@@ -316,14 +330,17 @@ As in other list types, select the trash icon to remove a list.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/250479) in GitLab 13.11 [with a flag](../../administration/feature_flags.md) named `iteration_board_lists`. Enabled by default.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75404) in GitLab 14.6. Feature flag `iteration_board_lists` removed.
-You're also able to create lists of an iteration.
-These lists filter issues by the assigned iteration.
+You can create lists of issues in an iteration.
+
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
To add an iteration list:
1. Select **Create list**.
1. Select **Iteration**.
-1. In the dropdown, select an iteration.
+1. In the dropdown list, select an iteration.
1. Select **Add to board**.
Like the milestone lists, you're able to [drag issues](#move-issues-and-lists)
@@ -344,9 +361,13 @@ This feature is available both at the project and group level.
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
For a video overview, see [Epics Swimlanes Walkthrough - 13.6](https://www.youtube.com/watch?v=nHC7-kz5P2g) (November 2020).
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
To group issues by epic in an issue board:
-1. Select the **Group by** dropdown button.
+1. Select **Group by**.
1. Select **Epic**.
![Epics Swimlanes](img/epics_swimlanes_v14_1.png)
@@ -375,8 +396,7 @@ You can also [drag issues](#move-issues-and-lists) to change their position and
## Work In Progress limits **(PREMIUM)**
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/11403) in GitLab 12.7
-> - Moved to GitLab Premium in 13.9.
+> Moved to GitLab Premium in 13.9.
You can set a Work In Progress (WIP) limit for each issue list on an issue board. When a limit is
set, the list's header shows the number of issues in the list and the soft limit of issues.
@@ -389,6 +409,10 @@ Examples:
- You have a list with five issues with a limit of five. When you move another issue to that list,
the list's header displays **6/5**, with the six shown in red.
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
To set a WIP limit for a list:
1. Navigate to a Project or Group board of which you're a member.
@@ -399,8 +423,7 @@ To set a WIP limit for a list:
## Blocked issues **(PREMIUM)**
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/34723) in GitLab 12.8.
-> - [View blocking issues when hovering over blocked icon](https://gitlab.com/gitlab-org/gitlab/-/issues/210452) in GitLab 13.10.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/210452) in GitLab 13.10: View blocking issues when hovering over the "blocked" icon.
If an issue is [blocked by another issue](issues/related_issues.md#blocking-issues), an icon appears next to its title to indicate its blocked
status.
@@ -423,9 +446,6 @@ When you hover over the blocked icon (**{issue-block}**), a detailed information
- Change issue labels (by dragging an issue between lists).
- Close an issue (by dragging it to the **Closed** list).
-If you're not able to do some of the things above, make sure you have the right
-[permissions](#permissions).
-
### Edit an issue
> Editing title, iteration, and confidentiality [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/248908) in GitLab 14.1.
@@ -433,6 +453,10 @@ If you're not able to do some of the things above, make sure you have the right
You can edit an issue without leaving the board view.
To open the right sidebar, select an issue card (not its title).
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
You can edit the following issue attributes in the right sidebar:
- Assignees
@@ -462,6 +486,10 @@ at the end of the lists, before **Closed**. To move and reorder lists, drag them
Removing a list doesn't have any effect on issues and labels, as it's just the
list view that's removed. You can always create it again later if you need.
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
To remove a list from an issue board:
1. On the top of the list you want to remove, select the **List settings** icon (**{settings}**).
@@ -473,6 +501,10 @@ To remove a list from an issue board:
> The **Add issues** button was [removed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57329) in GitLab 13.11.
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
If your board is scoped to one or more attributes, go to the issues you want to add and apply the
same attributes as your board scope.
@@ -488,6 +520,11 @@ The issue should now show in the `Doing` list on your issue board.
> The **Remove from board** button was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/229507) in GitLab 13.10.
When an issue should no longer belong to a list, you can remove it.
+
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
The steps depend on the scope of the list:
1. To open the right sidebar, select the issue card.
@@ -502,6 +539,10 @@ The steps depend on the scope of the list:
You can use the filters on top of your issue board to show only
the results you want. It's similar to the filtering used in the [issue tracker](issues/index.md).
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
You can filter by the following:
- Assignee
@@ -577,6 +618,40 @@ into a different list. Learn about possible effects in [Dragging issues between
To move a list, select its top bar, and drag it horizontally.
You can't move the **Open** and **Closed** lists, but you can hide them when editing an issue board.
+#### Move an issue to the start of the list
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367473) in GitLab 15.4.
+
+You can move issues to the top of the list with a menu shortcut.
+
+Your issue is moved to the top of the list even if other issues are hidden by a filter.
+
+Prerequisites:
+
+- You must at least have the Reporter role for the project.
+
+To move an issue to the start of the list:
+
+1. In an issue board, hover over the card of the issue you want to move.
+1. Select the vertical ellipsis (**{ellipsis_v}**), then **Move to start of list**.
+
+#### Move an issue to the end of the list
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367473) in GitLab 15.4.
+
+You can move issues to the bottom of the list with a menu shortcut.
+
+Your issue is moved to the bottom of the list even if other issues are hidden by a filter.
+
+Prerequisites:
+
+- You must at least have the Reporter role for the project.
+
+To move an issue to the end of the list:
+
+1. In an issue board, hover over the card of the issue you want to move.
+1. Select the vertical ellipsis (**{ellipsis_v}**), then **Move to end of list**.
+
#### Dragging issues between lists
To move an issue to another list, select the issue card and drag it onto that list.
@@ -593,8 +668,7 @@ and the target list.
### Multi-select issue cards
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/18954) in GitLab 12.4.
-> - [Placed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61955) behind a [feature flag](../feature_flags.md), disabled by default in GitLab 14.0.
+> [Moved](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61955) behind a [feature flag](../feature_flags.md) named `board_multi_select` in GitLab 14.0. Disabled by default.
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available, ask an
@@ -605,6 +679,10 @@ The feature is not ready for production use.
You can select multiple issue cards, then drag the group to another position within the list, or to
another list. This makes it faster to reorder many issues at once.
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
To select and move multiple cards:
1. Select each card with <kbd>Control</kbd>+`Click` on Windows or Linux, or <kbd>Command</kbd>+`Click` on MacOS.
@@ -612,22 +690,6 @@ To select and move multiple cards:
![Multi-select Issue Cards](img/issue_boards_multi_select_v12_4.png)
-### First time using an issue board
-
-> - The automatic creation of the **To Do** and **Doing** lists [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/202144) in GitLab 13.5.
-> - [Deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/270583) in GitLab 13.7. In GitLab 13.7 and later, the **To Do** and **Doing** columns are not automatically created.
-
-WARNING:
-This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/270583) in GitLab 13.7.
-The **To Do** and **Doing** columns are no longer automatically created.
-
-In GitLab 13.5 and 13.6, the first time you open an issue board, you are presented with the default lists
-(**Open**, **To Do**, **Doing**, and **Closed**).
-
-If the **To Do** and **Doing** labels don't exist in the project or group, they are created, and
-their lists appear as empty. If any of them already exists, the list is filled with the issues that
-have that label.
-
## Tips
A few things to remember:
diff --git a/doc/user/project/issues/associate_zoom_meeting.md b/doc/user/project/issues/associate_zoom_meeting.md
index 41de91d9bd7..ef864dc2743 100644
--- a/doc/user/project/issues/associate_zoom_meeting.md
+++ b/doc/user/project/issues/associate_zoom_meeting.md
@@ -8,8 +8,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16609) in GitLab 12.4.
-In order to communicate synchronously for incidents management,
-GitLab allows to associate a Zoom meeting with an issue.
+To communicate synchronously for incidents management,
+you can associate a Zoom meeting with an issue.
After you start a Zoom call for a fire-fight, you need a way to
associate the conference call with an issue. This is so that your
team members can join swiftly without requesting a link.
@@ -36,6 +36,9 @@ You are only allowed to attach a single Zoom meeting to an issue. If you attempt
to add a second Zoom meeting using the `/zoom` quick action, it doesn't work. You
need to [remove it](#removing-an-existing-zoom-meeting-from-an-issue) first.
+Users on GitLab Premium and higher can also
+[add multiple Zoom links to incidents](../../../operations/incident_management/linked_resources.md#link-zoom-meetings-from-an-incident).
+
## Removing an existing Zoom meeting from an issue
Similarly to adding a Zoom meeting, you can remove it with a quick action:
diff --git a/doc/user/project/issues/confidential_issues.md b/doc/user/project/issues/confidential_issues.md
index 402ce4bebec..5a1e66c8f7d 100644
--- a/doc/user/project/issues/confidential_issues.md
+++ b/doc/user/project/issues/confidential_issues.md
@@ -22,7 +22,7 @@ confidential checkbox and hit **Save changes**.
When you create a confidential issue in a project, the project becomes listed in the **Contributed projects** section in your [profile](../../profile/index.md). **Contributed projects** does not show information about the confidential issue; it only shows the project name.
-![Creating a new confidential issue](img/confidential_issues_create.png)
+![Creating a new confidential issue](img/confidential_issues_create_v15_4.png)
## Modify issue confidentiality
@@ -39,9 +39,12 @@ The second way is to locate the **Confidentiality** section in the sidebar and s
| ![Turn off confidentiality](img/turn_off_confidentiality_v15_1.png) | ![Turn on confidentiality](img/turn_on_confidentiality_v15_1.png) |
Every change from regular to confidential and vice versa, is indicated by a
-system note in the issue's comments.
+system note in the issue's comments:
-![Confidential issues system notes](img/confidential_issues_system_notes.png)
+![Confidential issues system notes](img/confidential_issues_system_notes_v15_4.png)
+
+- **{eye-slash}** The issue is made confidential.
+- **{eye}** The issue is made public.
When an issue is made confidential, only users with at least the Reporter role
for the project have access to the issue.
@@ -51,7 +54,7 @@ the issue even if they were actively participating before the change.
## Confidential issue indicators
There are a few things that visually separate a confidential issue from a
-regular one. In the issues index page view, you can see the eye-slash (**{eye-slash}**) icon
+regular one. In the issues index page view, you can see the confidential (**{eye-slash}**) icon
next to the issues that are marked as confidential:
![Confidential issues index page](img/confidential_issues_index_page.png)
@@ -61,7 +64,7 @@ you cannot see confidential issues at all.
---
-Likewise, while inside the issue, you can see the eye-slash icon right next to
+Likewise, while inside the issue, you can see the confidential (**{eye-slash}**) icon right next to
the issue number. There is also an indicator in the comment area that the
issue you are commenting on is confidential.
diff --git a/doc/user/project/issues/img/confidential_issues_create.png b/doc/user/project/issues/img/confidential_issues_create.png
deleted file mode 100644
index 0a141eb39f8..00000000000
--- a/doc/user/project/issues/img/confidential_issues_create.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/issues/img/confidential_issues_create_v15_4.png b/doc/user/project/issues/img/confidential_issues_create_v15_4.png
new file mode 100644
index 00000000000..ff489ad8605
--- /dev/null
+++ b/doc/user/project/issues/img/confidential_issues_create_v15_4.png
Binary files differ
diff --git a/doc/user/project/issues/img/confidential_issues_system_notes.png b/doc/user/project/issues/img/confidential_issues_system_notes.png
deleted file mode 100644
index 355be80ecb6..00000000000
--- a/doc/user/project/issues/img/confidential_issues_system_notes.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/issues/img/confidential_issues_system_notes_v15_4.png b/doc/user/project/issues/img/confidential_issues_system_notes_v15_4.png
new file mode 100644
index 00000000000..e448f609112
--- /dev/null
+++ b/doc/user/project/issues/img/confidential_issues_system_notes_v15_4.png
Binary files differ
diff --git a/doc/user/project/issues/img/issue_weight_v13_11.png b/doc/user/project/issues/img/issue_weight_v13_11.png
deleted file mode 100644
index 842c154ea49..00000000000
--- a/doc/user/project/issues/img/issue_weight_v13_11.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/issues/img/related_issue_block_v15_3.png b/doc/user/project/issues/img/related_issue_block_v15_3.png
index 827ddeabf10..942f7a33fe0 100644
--- a/doc/user/project/issues/img/related_issue_block_v15_3.png
+++ b/doc/user/project/issues/img/related_issue_block_v15_3.png
Binary files differ
diff --git a/doc/user/project/issues/img/related_issues_add_v15_3.png b/doc/user/project/issues/img/related_issues_add_v15_3.png
index 7c6edf61427..28739c0b909 100644
--- a/doc/user/project/issues/img/related_issues_add_v15_3.png
+++ b/doc/user/project/issues/img/related_issues_add_v15_3.png
Binary files differ
diff --git a/doc/user/project/issues/issue_weight.md b/doc/user/project/issues/issue_weight.md
index fcc53a239dc..21d7fb0a764 100644
--- a/doc/user/project/issues/issue_weight.md
+++ b/doc/user/project/issues/issue_weight.md
@@ -13,15 +13,56 @@ When you have a lot of issues, it can be hard to get an overview.
With weighted issues, you can get a better idea of how much time,
value, or complexity a given issue has or costs.
-You can set the weight of an issue during its creation, by changing the
-value in the dropdown menu. You can set it to a non-negative integer
-value from 0, 1, 2, and so on.
-You can remove weight from an issue as well.
-A user with a Reporter role (or above) can set the weight.
+## View the issue weight
-This value appears on the right sidebar of an individual issue, as well as
-in the issues page next to a weight icon (**{weight}**).
+You can view the issue weight on:
-As an added bonus, you can see the total sum of all issues on the milestone page.
+- The right sidebar of each issue.
+- The issues page, next to a weight icon (**{weight}**).
+- [Issue boards](../issue_board.md), next to a weight icon (**{weight}**).
+- The [milestone](../milestones/index.md) page, as a total sum of issue weights.
-![issue page](img/issue_weight_v13_11.png)
+## Set the issue weight
+
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
+You can set the issue weight when you create or edit an issue.
+
+You must enter whole, positive numbers.
+
+When you change the weight of an issue, the new value overwrites the previous value.
+
+### When you create an issue
+
+To set the issue weight when you [create an issue](managing_issues.md#create-an-issue), enter a
+number under **Weight**.
+
+### From an existing issue
+
+To set the issue weight from an existing issue:
+
+1. Go to the issue.
+1. On the right sidebar, in the **Weight** section, select **Edit**.
+1. Enter the new weight.
+1. Select any area outside the dropdown list.
+
+### From an issue board
+
+To set the issue weight when you [edit an issue from an issue board](../issue_board.md#edit-an-issue):
+
+1. Go to your issue board.
+1. Select an issue card (not its title).
+1. On the right sidebar, in the **Weight** section, select **Edit**.
+1. Enter the new weight.
+1. Select any area outside the dropdown list.
+
+## Remove issue weight
+
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
+To remove the issue weight, follow the same steps as when you [set the issue weight](#set-the-issue-weight),
+and select **remove weight**.
diff --git a/doc/user/project/issues/managing_issues.md b/doc/user/project/issues/managing_issues.md
index 40e5a6d6a92..b4edb238479 100644
--- a/doc/user/project/issues/managing_issues.md
+++ b/doc/user/project/issues/managing_issues.md
@@ -33,7 +33,7 @@ Prerequisites:
To create an issue:
-1. On the top bar, select **Menu > Projects** and find your project.
+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**.
@@ -56,7 +56,7 @@ Prerequisites:
To create an issue from a group:
-1. On the top bar, select **Menu > Groups** and find your 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
@@ -103,7 +103,7 @@ Prerequisites:
To create an issue from a project issue board:
-1. On the top bar, select **Menu > Projects** and find your project.
+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.
@@ -111,7 +111,7 @@ To create an issue from a project issue board:
To create an issue from a group issue board:
-1. On the top bar, select **Menu > Groups** and find your group.
+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.
@@ -138,7 +138,7 @@ Prerequisites:
To email an issue to a project:
-1. On the top bar, select **Menu > Projects** and find your 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}**).
@@ -271,7 +271,7 @@ Prerequisites:
To edit multiple issues at the same time:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Issues**.
1. Select **Edit issues**. A sidebar on the right of your screen appears.
1. Select the checkboxes next to each issue you want to edit.
@@ -304,7 +304,7 @@ Prerequisites:
To edit multiple issues at the same time:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Issues**.
1. Select **Edit issues**. A sidebar on the right of your screen appears.
1. Select the checkboxes next to each issue you want to edit.
@@ -458,7 +458,8 @@ The default issue closing pattern regex:
#### Disable automatic issue closing
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/19754) in GitLab 12.7.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/19754) in GitLab 12.7.
+> - [Changed](https://gitlab.com/gitlab-org/gitlab/-/issues/240922) in GitLab 15.4: The referenced issue's project setting is checked instead of the project of the commit or merge request.
You can disable the automatic issue closing feature on a per-project basis
in the [project's settings](../settings/index.md).
@@ -469,23 +470,18 @@ Prerequisites:
To disable automatic issue closing:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Default branch**.
-1. Select **Auto-close referenced issues on default branch**.
+1. Clear the **Auto-close referenced issues on default branch** checkbox.
1. Select **Save changes**.
Referenced issues are still displayed, but are not closed automatically.
-The automatic issue closing is disabled by default in a project if the project has the issue tracker
-disabled. If you want to enable automatic issue closing, make sure to
-[enable GitLab Issues](../settings/index.md#configure-project-visibility-features-and-permissions).
-
Changing this setting applies only to new merge requests or commits. Already
closed issues remain as they are.
-If issue tracking is enabled, disabling automatic issue closing only applies to merge requests
-attempting to automatically close issues in the same project.
-Merge requests in other projects can still close another project's issues.
+Disabling automatic issue closing only applies to issues in the project where the setting was disabled.
+Merge requests and commits in this project can still close another project's issues.
#### Customize the issue closing pattern **(FREE SELF)**
@@ -602,7 +598,7 @@ GitLab displays the results on-screen, but you can also
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/39908) in GitLab 12.1.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Issues > List**.
1. In the **Search** box, type the issue ID. For example, enter filter `#10` to return only issue 10.
@@ -644,10 +640,15 @@ To copy the issue's email address:
> - [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.
-Assignees in the sidebar are updated in real time.
-When you're viewing an issue and somebody changes its assignee,
+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, [if enabled](../labels.md#real-time-changes-to-labels)
+
## Assignee
An issue can be assigned to one or [more users](multiple_assignees_for_issues.md).
@@ -685,6 +686,7 @@ Up to five similar issues, sorted by most recently updated, are displayed below
> - Health status of closed issues [can't be edited](https://gitlab.com/gitlab-org/gitlab/-/issues/220867) in GitLab 13.4 and later.
> - Issue health status visible in issue lists [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45141) in GitLab 13.6.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/213567) in GitLab 13.7.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218618) in GitLab 15.4: health status is visible on issue cards in issue boards.
To help you track issue statuses, you can assign a status to each issue.
This status marks issues as progressing as planned or needing attention to keep on schedule.
@@ -703,7 +705,11 @@ To edit health status of an issue:
- Needs attention (amber)
- At risk (red)
-You can then see the issue's status in the issues list and the epic tree.
+You can see the issue’s health status in:
+
+- Issues list
+- Epic tree
+- Issue cards in issue boards
After an issue is closed, its health status can't be edited and the **Edit** button becomes disabled
until the issue is reopened.
diff --git a/doc/user/project/issues/related_issues.md b/doc/user/project/issues/related_issues.md
index 9dc361b403f..d1e62a76103 100644
--- a/doc/user/project/issues/related_issues.md
+++ b/doc/user/project/issues/related_issues.md
@@ -29,7 +29,7 @@ Prerequisites:
To link one issue to another:
-1. In the **Linked issues** section of an issue,
+1. In the **Linked items** section of an issue,
select the add linked issue button (**{plus}**).
1. Select the relationship between the two issues. Either:
- **relates to**
@@ -61,7 +61,7 @@ You can also add a linked issue from a commit message or the description in anot
## Remove a linked issue
-In the **Linked issues** section of an issue, select the remove button (**{close}**) on the
+In the **Linked items** section of an issue, select the remove button (**{close}**) on the
right-side of each issue token to remove.
Due to the bi-directional relationship, the relationship no longer appears in either issue.
diff --git a/doc/user/project/labels.md b/doc/user/project/labels.md
index 62c5489d9c3..13c93fadf6e 100644
--- a/doc/user/project/labels.md
+++ b/doc/user/project/labels.md
@@ -58,7 +58,7 @@ You can also assign and unassign labels with [quick actions](quick_actions.md):
To view the **project's labels**:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Labels**.
Or:
@@ -75,7 +75,7 @@ project or group path where it was created.
To view the **group's labels**:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Group information > Labels**.
Or:
@@ -97,7 +97,7 @@ Prerequisites:
To create a project label:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Labels**.
1. Select **New label**.
1. In the **Title** field, enter a short, descriptive name for the label. You
@@ -131,7 +131,7 @@ To do so:
To create a group label:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Group information > Labels**.
1. Select **New label**.
1. In the **Title** field, enter a short, descriptive name for the label. You
@@ -171,7 +171,7 @@ Prerequisites:
To edit a **project** label:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Labels**.
1. Next to the label you want to edit, select **Edit** (**{pencil}**).
@@ -179,7 +179,7 @@ To edit a **project** label:
To edit a **group** label:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Group information > Labels**.
1. Next to the label you want to edit, select **Edit** (**{pencil}**).
@@ -197,7 +197,7 @@ Prerequisites:
To delete a **project** label:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Labels**.
1. Either:
@@ -210,7 +210,7 @@ To delete a **project** label:
To delete a **group** label:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Group information > Labels**.
1. Either:
@@ -240,7 +240,7 @@ Prerequisites:
To promote a project label to a group label:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Labels**.
1. Next to the **Subscribe** button, select the three dots (**{ellipsis_v}**) and
select **Promote to group label**.
@@ -291,7 +291,7 @@ Prerequisites:
To add the default labels to the project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Labels**.
1. Select **Generate a default set of labels**.
@@ -430,7 +430,7 @@ Prerequisites:
To prioritize a label:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Labels**.
1. Next to a label you want to prioritize, select the star (**{star-o}**).
diff --git a/doc/user/project/members/index.md b/doc/user/project/members/index.md
index 4a6272a0ca3..8d8169e8454 100644
--- a/doc/user/project/members/index.md
+++ b/doc/user/project/members/index.md
@@ -68,7 +68,7 @@ Prerequisite:
To add a user to a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Members**.
1. Select **Invite members**.
1. Enter an email address and select a [role](../../permissions.md).
@@ -106,7 +106,7 @@ Prerequisite:
To add groups to a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Members**.
1. Select **Invite a group**.
1. Select a group.
@@ -131,7 +131,7 @@ Prerequisite:
To import users:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Members**.
1. Select **Import from a project**.
1. Select the project. You can view only the projects for which you're a maintainer.
@@ -175,7 +175,7 @@ Prerequisites:
To remove a member from a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Members**.
1. Next to the project member you want to remove, select **Remove member**.
1. Optional. In the confirmation box, select the
@@ -197,7 +197,7 @@ You can filter and sort members in a project.
### Display inherited members
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Members**.
1. In the **Filter members** box, select `Membership` `=` `Inherited`.
1. Press <kbd>Enter</kbd>.
@@ -206,7 +206,7 @@ You can filter and sort members in a project.
### Display direct members
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Members**.
1. In the **Filter members** box, select `Membership` `=` `Direct`.
1. Press <kbd>Enter</kbd>.
@@ -229,7 +229,7 @@ You can sort members by **Account**, **Access granted**, **Max role**, or **Last
GitLab users can request to become a member of a project.
-1. On the top bar, select **Menu > Projects** and find the project you want to be a member of.
+1. On the top bar, select **Main menu > Projects** and find the project you want to be a member of.
1. By the project name, select **Request Access**.
![Request access button](img/request_access_button.png)
@@ -253,7 +253,7 @@ Prerequisite:
- You must be the project owner.
-1. On the top bar, select **Menu > Projects** and find your 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. Under **Project visibility**, select **Users can request access**.
diff --git a/doc/user/project/members/share_project_with_groups.md b/doc/user/project/members/share_project_with_groups.md
index 3d5b855a9d3..ee161deaabb 100644
--- a/doc/user/project/members/share_project_with_groups.md
+++ b/doc/user/project/members/share_project_with_groups.md
@@ -48,6 +48,7 @@ After sharing 'Project Acme' with 'Engineering':
- 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).
When you share a project, be aware of the following restrictions and outcomes:
@@ -83,7 +84,7 @@ The following outcomes occur:
## Share project with group lock
-It is possible to prevent projects in a group from
+It is possible to prevent projects in a group from
[sharing a project with another group](../members/share_project_with_groups.md).
This allows for tighter control over project access.
diff --git a/doc/user/project/merge_requests/approvals/rules.md b/doc/user/project/merge_requests/approvals/rules.md
index c9278c19322..32548215054 100644
--- a/doc/user/project/merge_requests/approvals/rules.md
+++ b/doc/user/project/merge_requests/approvals/rules.md
@@ -32,8 +32,9 @@ use the default approval rules from the target (upstream) project, not the sourc
To add a merge request approval rule:
-1. Go to your project and select **Settings > General**.
-1. Expand **Merge request (MR) approvals**, and then select **Add approval rule**.
+1. Go to your project and select **Settings > Merge requests**.
+1. In the **Merge request approvals** section, scroll to **Approval rules**.
+1. Select **Add approval rule**.
1. Add a human-readable **Rule name**.
1. Set the number of required approvals in **Approvals required**. A value of `0` makes
[the rule optional](#configure-optional-approval-rules), and any number greater than `0`
@@ -65,8 +66,9 @@ to existing merge requests:
To edit a merge request approval rule:
-1. Go to your project and select **Settings > General**.
-1. Expand **Merge request (MR) approvals**, and then select **Edit**.
+1. Go to your project and select **Settings > Merge requests**.
+1. In the **Merge request approvals** section, scroll to **Approval rules**.
+1. Select **Edit** next to the rule you want to edit.
1. Optional. Change the **Rule name**.
1. Set the number of required approvals in **Approvals required**. The minimum value is `0`.
1. Add or remove eligible approvers, as needed:
@@ -155,11 +157,11 @@ approve in these ways:
If you add [code owners](../../code_owners.md) to your repository, the owners of files
become eligible approvers in the project. To enable this merge request approval rule:
-1. Go to your project and select **Settings > General**.
-1. Expand **Merge request (MR) approvals**.
-1. Locate **All eligible users** and select the number of approvals required:
+1. Go to your project and select **Settings > Merge requests**.
+1. In the **Merge request approvals** section, scroll to **Approval rules**.
+1. Locate the **All eligible users** rule, and select the number of approvals required:
-![MR approvals by Code Owners](img/mr_approvals_by_code_owners_v15_2.png)
+ ![MR approvals by Code Owners](img/mr_approvals_by_code_owners_v15_2.png)
You can also
[require code owner approval](../../protected_branches.md#require-code-owner-approval-on-a-protected-branch)
@@ -182,9 +184,10 @@ granting them push access:
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),
based on the Reporter role.
-1. Go to your project and select **Settings > General**.
-1. Expand **Merge request (MR) approvals**.
-1. Select **Add approval rule** or **Update approval rule** and target the protected branch.
+1. Go to your project and select **Settings > Merge requests**.
+1. In the **Merge request approvals** section, scroll to **Approval rules**, and either:
+ - For a new rule, select **Add approval rule** and target the protected branch.
+ - For an existing rule, select **Edit** and target the protected branch.
1. [Add the group](../../../group/manage.md#create-a-group) to the permission list.
![Update approval rule](img/update_approval_rule_v13_10.png)
@@ -226,12 +229,12 @@ Approval rules are often relevant only to specific branches, like your
approval rule for certain branches:
1. [Create an approval rule](#add-an-approval-rule).
-1. Go to your project and select **Settings**.
-1. Expand **Merge request (MR) approvals**.
+1. Go to your project and select **Settings > Merge requests**.
+1. In the **Merge request approvals** section, scroll to **Approval rules**.
1. Select a **Target branch**:
- To apply the rule to all branches, select **All branches**.
- To apply the rule to all protected branches, select **All protected branches** (GitLab 15.3 and later).
- - To apply the rule to a specific branch, select it from the list:
+ - To apply the rule to a specific branch, select it from the list.
1. To enable this configuration, read
[Code Owner's approvals for protected branches](../../protected_branches.md#require-code-owner-approval-on-a-protected-branch).
diff --git a/doc/user/project/merge_requests/approvals/settings.md b/doc/user/project/merge_requests/approvals/settings.md
index 3ca8ddb508a..4fdf6d46b8b 100644
--- a/doc/user/project/merge_requests/approvals/settings.md
+++ b/doc/user/project/merge_requests/approvals/settings.md
@@ -16,8 +16,8 @@ those rules are applied as a merge request moves toward completion.
To view or edit merge request approval settings:
-1. Go to your project and select **Settings > General**.
-1. Expand **Merge request (MR) approvals**.
+1. Go to your project and select **Settings > Merge requests**.
+1. Expand **Approvals**.
### Approval settings
@@ -44,9 +44,9 @@ You can further define what happens to existing approvals when commits are added
By default, the author of a merge request cannot approve it. To change this setting:
-1. Go to your project and select **Settings > General**.
-1. Expand **Merge request (MR) approvals**.
-1. Clear the **Prevent approval by author** checkbox.
+1. On the left sidebar, select **Settings > Merge requests**.
+1. In the **Merge request approvals** section, scroll to **Approval settings** and
+ clear the **Prevent approval by author** checkbox.
1. Select **Save changes**.
Authors can edit the approval rule in an individual merge request and override
@@ -68,9 +68,9 @@ the project level or [instance level](../../../admin_area/merge_requests_approva
you can prevent committers from approving merge requests that are partially
their own. To do this:
-1. Go to your project and select **Settings > General**.
-1. Expand **Merge request (MR) approvals**.
-1. Select the **Prevent approvals by users who add commits** checkbox.
+1. On the left sidebar, select **Settings > Merge requests**.
+1. In the **Merge request approvals** section, scroll to **Approval settings** and
+ select **Prevent approvals by users who add commits**.
If this checkbox is cleared, an administrator has disabled it
[at the instance level](../../../admin_area/merge_requests_approvals.md), and
it can't be changed at the project level.
@@ -94,9 +94,9 @@ By default, users can override the approval rules you [create for a project](rul
on a per-merge-request basis. If you don't want users to change approval rules
on merge requests, you can disable this setting:
-1. Go to your project and select **Settings > General**.
-1. Expand **Merge request (MR) approvals**.
-1. Select the **Prevent editing approval rules in merge requests** checkbox.
+1. On the left sidebar, select **Settings > Merge requests**.
+1. In the **Merge request approvals** section, scroll to **Approval settings** and
+ select **Prevent editing approval rules in merge requests**.
1. Select **Save changes**.
This change affects all open merge requests.
@@ -112,9 +112,9 @@ permission enables an electronic signature for approvals, such as the one define
1. Enable password authentication for the web interface, as described in the
[sign-in restrictions documentation](../../../admin_area/settings/sign_in_restrictions.md#password-authentication-enabled).
-1. Go to your project and select **Settings > General**.
-1. Expand **Merge request (MR) approvals**.
-1. Select the **Require user password to approve** checkbox.
+1. On the left sidebar, select **Settings > Merge requests**.
+1. In the **Merge request approvals** section, scroll to **Approval settings** and
+ select **Require user password to approve**.
1. Select **Save changes**.
## Remove all approvals when commits are added to the source branch
@@ -123,9 +123,9 @@ By default, an approval on a merge request remains in place, even if you add mor
after the approval. If you want to remove all existing approvals on a merge request
when more changes are added to it:
-1. Go to your project and select **Settings > General**.
-1. Expand **Merge request (MR) approvals**.
-1. Select the **Remove all approvals when commits are added to the source branch** checkbox.
+1. On the left sidebar, select **Settings > Merge requests**.
+1. In the **Merge request approvals** section, scroll to **Approval settings** and
+ select **Remove all approvals when commits are added to the source branch**.
1. Select **Save changes**.
Approvals aren't removed when a merge request is [rebased from the UI](../methods/index.md#rebasing-in-semi-linear-merge-methods)
@@ -143,10 +143,9 @@ Prerequisite:
To do this:
-1. On the top bar, select **Menu > Projects** and find your project.
-1. On the left sidebar, select **Settings > General**.
-1. Expand **Merge request approvals**.
-1. Select **Remove approvals by Code Owners if their files changed**.
+1. On the left sidebar, select **Settings > Merge requests**.
+1. In the **Merge request approvals** section, scroll to **Approval settings** and
+ select **Remove approvals by Code Owners if their files changed**.
1. Select **Save changes**.
## Code coverage check approvals
diff --git a/doc/user/project/merge_requests/cherry_pick_changes.md b/doc/user/project/merge_requests/cherry_pick_changes.md
index 14f3979cf34..2040995280e 100644
--- a/doc/user/project/merge_requests/cherry_pick_changes.md
+++ b/doc/user/project/merge_requests/cherry_pick_changes.md
@@ -7,61 +7,106 @@ type: reference, concepts
# Cherry-pick changes **(FREE)**
-GitLab implements Git's powerful feature to
-[cherry-pick any commit](https://git-scm.com/docs/git-cherry-pick "Git cherry-pick documentation")
-with a **Cherry-pick** button in merge requests and commit details.
+In Git, *cherry-picking* is taking a single commit from one branch and adding it
+as the latest commit on another branch. The rest of the commits in the source branch
+are not added to the target. You should cherry-pick a commit when you need the
+change contained in a single commit, but you can't or don't want to pull the
+entire contents of that branch into another.
-## Cherry-pick a merge request
+You can use the GitLab UI to cherry-pick single commits or entire merge requests.
+You can even cherry-pick a commit from [a fork of your project](#cherry-pick-into-a-project).
-After the merge request has been merged, a **Cherry-pick** button displays
-to cherry-pick the changes introduced by that merge request.
+NOTE:
+Support for tracking commits cherry-picked from the command line
+is tracked [in this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/202215).
-![Cherry-pick merge request](img/cherry_pick_changes_mr.png)
+## Cherry-pick example
+
+In this example of cherry-picking, a Git repository has two branches: `develop` and `main`.
+This example shows a cherry-picked commit from one branch being added to another:
+
+```mermaid
+gitGraph
+ commit id: "A"
+ branch develop
+ commit id:"B"
+ checkout main
+ commit id:"C"
+ checkout develop
+ commit id:"D"
+ checkout main
+ commit id:"E"
+ cherry-pick id:"B"
+ commit id:"G"
+ checkout develop
+ commit id:"H"
+```
-After you select that button, a modal displays a
-[branch filter search box](../repository/branches/index.md#branch-filter-search-box)
-where you can choose to either:
+In this example, a cherry-pick of commit `B` from the `develop` branch is added
+after commit `E` in the `main` branch.
-- Cherry-pick the changes directly into the selected branch.
-- Create a new merge request with the cherry-picked changes.
+Commit `G` is added after the cherry-pick.
-### Track a cherry-pick
+## Cherry-pick all changes from a merge request
-> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2675) in GitLab 12.9.
+After a merge request is merged, you can cherry-pick all changes introduced
+by the merge request:
-When you cherry-pick a merge commit, GitLab displays a system note to the related merge
-request thread. It crosslinks the new commit and the existing merge request.
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Merge requests**, and find your merge request.
+1. Scroll to the merge request reports section, and find the **Merged by** report.
+1. In the top right, select **Cherry-pick**:
-![Cherry-pick tracking in merge request timeline](img/cherry_pick_mr_timeline_v12_9.png)
+ ![Cherry-pick merge request](img/cherry_pick_v15_4.png)
+1. In the modal window, select the project and branch to cherry-pick into.
+1. Optional. Select **Start a new merge request with these changes**.
+1. Select **Cherry-pick**.
-Each deployment's [list of associated merge requests](../../../api/deployments.md#list-of-merge-requests-associated-with-a-deployment) includes cherry-picked merge commits.
+## Cherry-pick a single commit
-NOTE:
-We only track cherry-pick executed from GitLab (both UI and API). Support for tracking cherry-picked commits through the command line
-is tracked [in this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/202215).
+You can cherry-pick a single commit from multiple locations in your GitLab project.
-## Cherry-pick a commit
+### From a project's commit list
-You can cherry-pick a commit from the commit details page:
+To cherry-pick a commit from the list of all commits for a project:
-![Cherry-pick commit](img/cherry_pick_changes_commit.png)
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Repository > Commits**.
+1. Select the title of the commit you want to cherry-pick.
+1. In the modal window, select the project and branch to cherry-pick into.
+1. Optional. Select **Start a new merge request with these changes**.
+1. Select **Cherry-pick**.
-Similar to cherry-picking a merge request, you can cherry-pick the changes
-directly into the target branch or create a new merge request to cherry-pick the
-changes.
+### From a merge request
-When cherry-picking merge commits, the mainline is always the
-first parent. If you want to use a different mainline, you need to do that
-from the command line.
+You can cherry-pick commits from any merge request in your project, regardless of
+whether the merge request is open or closed. To cherry-pick a commit from the
+list of commits included in a merge request:
-Here's a quick example to cherry-pick a merge commit using the second parent as the
-mainline:
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Merge requests**, and find your merge request.
+1. In the merge request's secondary menu, select **Commits** to display the commit details page.
+1. Select the title of the commit you want to cherry-pick.
+1. In the top right corner, select **Options > Cherry-pick** to show the cherry-pick modal.
+1. In the modal window, select the project and branch to cherry-pick into.
+1. Optional. Select **Start a new merge request with these changes**.
+1. Select **Cherry-pick**.
-```shell
-git cherry-pick -m 2 7a39eb0
-```
+### From the file view of a repository
-### Cherry-pick into a project
+You can cherry-pick from the list of previous commits affecting an individual file
+when you view that file in your project's Git repository:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Repository > Files** and go to the file
+ changed by the commit.
+1. Select **History**, then select the title of the commit you want to cherry-pick.
+1. In the top right corner, select **Options > Cherry-pick** to show the cherry-pick modal.
+1. In the modal window, select the project and branch to cherry-pick into.
+1. Optional. Select **Start a new merge request with these changes**.
+1. Select **Cherry-pick**.
+
+## Cherry-pick into a project
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/21268) in GitLab 13.11 behind a [feature flag](../../feature_flags.md), disabled by default.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/324154) in GitLab 14.0.
@@ -70,25 +115,38 @@ You can cherry-pick merge requests from the same project, or forks of the same
project, from the GitLab user interface:
1. In the merge request's secondary menu, select **Commits** to display the commit details page.
-1. Select the **Options** dropdown and select **Cherry-pick** to show the cherry-pick modal.
+1. In the top right corner, select **Options > Cherry-pick** to show the cherry-pick modal.
1. In **Pick into project** and **Pick into branch**, select the destination project and branch:
![Cherry-pick commit](img/cherry_pick_into_project_v13_11.png)
1. Optional. Select **Start a new merge request** if you're ready to create a merge request.
1. Select **Cherry-pick**.
+## View system notes for cherry-picked commits
+
+When you cherry-pick a merge commit in the GitLab UI or API, GitLab adds a system note
+to the related merge request thread in the format **{cherry-pick-commit}**
+`[USER]` **picked the changes into the branch** `[BRANCHNAME]` with commit** `[SHA]` `[DATE]`:
+
+![Cherry-pick tracking in merge request timeline](img/cherry_pick_mr_timeline_v15_4.png)
+
+The system note crosslinks the new commit and the existing merge request.
+Each deployment's [list of associated merge requests](../../../api/deployments.md#list-of-merge-requests-associated-with-a-deployment) includes cherry-picked merge commits.
+
## Related topics
-- The [Commits API](../../../api/commits.md) enables you to add custom messages
- to changes you cherry-pick through the API.
+- Use the [Commits API](../../../api/commits.md) to add custom messages
+ to changes when you use the API to cherry-pick.
-<!-- ## Troubleshooting
+## Troubleshooting
-Include any troubleshooting steps that you can foresee. If you know beforehand what issues
-one might have when setting this up, or when something is changed, or on upgrading, it's
-important to describe those, too. Think of things that may go wrong and include them here.
-This is important to minimize requests for support, and to avoid doc comments with
-questions that you know someone might ask.
+### Selecting a different parent commit when cherry-picking
-Each scenario can be a third-level heading, e.g. `### 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. -->
+When you cherry-pick a merge commit in the GitLab UI, the mainline is always the
+first parent. Use the command line to cherry-pick with a different mainline.
+
+Here's a quick example to cherry-pick a merge commit using the second parent as the
+mainline:
+
+```shell
+git cherry-pick -m 2 7a39eb0
+```
diff --git a/doc/user/project/merge_requests/commit_templates.md b/doc/user/project/merge_requests/commit_templates.md
index 6f9bc452b96..99a1739b1a4 100644
--- a/doc/user/project/merge_requests/commit_templates.md
+++ b/doc/user/project/merge_requests/commit_templates.md
@@ -29,8 +29,8 @@ Prerequisite:
To do this:
-1. On the top bar, select **Menu > Projects** and find your project.
-1. On the left sidebar, select **Settings > General** and expand **Merge requests**.
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > Merge requests**.
1. Depending on the type of template you want to create, scroll to either
[**Merge commit message template**](#default-template-for-merge-commits) or
[**Squash commit message template**](#default-template-for-squash-commits).
diff --git a/doc/user/project/merge_requests/creating_merge_requests.md b/doc/user/project/merge_requests/creating_merge_requests.md
index f30b20e9d34..1a44126f7ff 100644
--- a/doc/user/project/merge_requests/creating_merge_requests.md
+++ b/doc/user/project/merge_requests/creating_merge_requests.md
@@ -14,7 +14,7 @@ There are many different ways to create a merge request.
You can create a merge request from the list of merge requests.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left menu, select **Merge requests**.
1. In the top right, select **New merge request**.
1. Select a source and target branch and then **Compare branches and continue**.
@@ -43,7 +43,7 @@ You can create a merge request when you add, edit, or upload a file to a reposit
You can create a merge request when you create a branch.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left menu, select **Repository > Branches**.
1. Type a branch name and select **New branch**.
1. Above the file list, on the right side, select **Create merge request**.
@@ -90,7 +90,7 @@ to reduce the need for editing merge requests manually through the UI.
You can create a merge request from your fork to contribute back to the main project.
-1. On the top bar, select **Menu > Project**.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. Select your fork of the repository.
1. On the left menu, go to **Merge requests**, and select **New merge request**.
1. In the **Source branch** drop-down list box, select the branch in your forked repository as the source branch.
@@ -120,7 +120,7 @@ Prerequisites:
To create a merge request by sending an email:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left menu, select **Merge requests**.
1. In the top right, select **Email a new merge request to this project**.
An email address is displayed. Copy this address.
@@ -165,7 +165,7 @@ scenarios when you create a new merge request:
To have merge requests from a fork by default target your own fork
(instead of the upstream project), you can change the default.
-1. On the top bar, select **Menu > Project**.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left menu, select **Settings > General > Merge requests**.
1. In the **Target project** section, select the option you want to use for
your default target project.
diff --git a/doc/user/project/merge_requests/csv_export.md b/doc/user/project/merge_requests/csv_export.md
index 893b2bc6811..f997898f5a5 100644
--- a/doc/user/project/merge_requests/csv_export.md
+++ b/doc/user/project/merge_requests/csv_export.md
@@ -1,5 +1,5 @@
---
-stage: Manage
+stage: Govern
group: Compliance
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
---
@@ -12,7 +12,7 @@ Export all the data collected from a project's merge requests into a comma-separ
To export merge requests to a CSV file:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Merge requests** .
1. Add any searches or filters. This can help you keep the size of the CSV file under the 15MB limit. The limit ensures
the file can be emailed to a variety of email providers.
diff --git a/doc/user/project/merge_requests/dependencies.md b/doc/user/project/merge_requests/dependencies.md
new file mode 100644
index 00000000000..5b88e69357c
--- /dev/null
+++ b/doc/user/project/merge_requests/dependencies.md
@@ -0,0 +1,163 @@
+---
+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/engineering/ux/technical-writing/#assignments
+type: reference, concepts
+---
+
+# Merge request dependencies **(PREMIUM)**
+
+A single feature can span several merge requests, spread out across multiple projects,
+and the order in which the work merges can be significant. Use merge request dependencies
+when it's important to merge work in a specific order. Some examples:
+
+- Ensure changes to a required library are merged before changes to a project that
+ imports the library.
+- Prevent a documentation-only merge request from merging before the feature work
+ is itself merged.
+- Require a merge request updating a permissions matrix to merge, before merging work
+ from someone who hasn't yet been granted permissions.
+
+If your project `me/myexample` imports a library from `myfriend/library`,
+you might want to update your project to use a new feature in `myfriend/library`.
+However, if you merge changes to your project before the external library adds the
+new feature, you would break the default branch in your project. A merge request
+dependency prevents your work from merging too soon:
+
+```mermaid
+graph TB
+ A['me/myexample' project]
+ B['myfriend/library' project]
+ C[Merge request #1:<br>Create new version 2.5]
+ D[Merge request #2:<br>Add version 2.5<br>to build]
+ A-->|contains| D
+ B---->|contains| C
+ D-.->|depends on| C
+ C-.->|blocks| D
+```
+
+You could mark your `me/myexample` merge request as a [draft](drafts.md)
+and explain why in the comments. However, this approach is manual and does not scale, especially
+if your merge request relies on several others in multiple projects. Instead,
+use the draft (or ready) state to track the readiness of an individual
+merge request, and a merge request dependency to enforce merge order.
+
+NOTE:
+Merge request dependencies are a **PREMIUM** feature, but this restriction is
+enforced only for the dependent merge request. A merge request in a **PREMIUM**
+project can depend on a merge request in a **FREE** project, but a merge request
+in a **FREE** project cannot be marked as dependent.
+
+## View dependencies for a merge request
+
+If a merge request is dependent on another, the merge request reports section shows
+information about the dependency:
+
+![Dependencies in merge request widget](img/dependencies_view_v15_3.png)
+
+To view dependency information on a merge request:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Merge requests** and identify your merge request.
+1. Scroll to the merge request reports area. Dependent merge requests display information
+ about the total number of dependencies set, such as
+ **(status-warning)** **Depends on 1 merge request being merged**.
+1. Select **Expand** to view the title, milestone, assignee, and pipeline status
+ of each dependency.
+
+Until your merge request's dependencies all merge, your merge request
+cannot be merged. The message
+**Merge blocked: you can only merge after the above items are resolved** displays.
+
+### Closed merge requests
+
+Closed merge requests still prevent their dependents from being merged, because
+a merge request can close regardless of whether or not the planned work actually merged.
+
+If a merge request closes and the dependency is no longer relevant,
+remove it as a dependency to unblock the dependent merge request.
+
+## Create a new dependent merge request
+
+When you create a new merge request, you can prevent it from merging until after
+other specific work merges, even if the merge request is in a different project.
+
+Prerequisites:
+
+- You must have at least the Developer role or be allowed to create merge requests in the project.
+- The dependent merge request must be in a project in a **PREMIUM** or higher tier.
+
+To create a new merge request and mark it as dependent on another:
+
+1. [Create a new merge request](creating_merge_requests.md).
+1. In **Merge request dependencies**, paste either the reference or the full URL
+ to the merge requests that should merge before this work merges. References
+ are in the form of `path/to/project!merge_request_id`.
+1. Select **Create merge request**.
+
+## Edit a merge request to add a dependency
+
+You can edit an existing merge request and mark it as dependent on another.
+
+Prerequisite:
+
+- You must have at least the Developer role or be allowed to edit merge requests in the project.
+
+To do this:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Merge requests** and identify your merge request.
+1. Select **Edit**.
+1. In **Merge request dependencies**, paste either the reference or the full URL
+ to the merge requests that should merge before this work merges. References
+ are in the form of `path/to/project!merge_request_id`.
+
+## Remove a dependency from a merge request
+
+You can edit a dependent merge request and remove a dependency.
+
+Prerequisite:
+
+- You must have a role in the project that allows you to edit merge requests.
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Merge requests** and identify your merge request.
+1. Select **Edit**.
+1. Scroll to **Merge request dependencies** and select **Remove** next to the reference
+ for each dependency you want to remove.
+
+ NOTE:
+ Dependencies for merge requests you don't have access to are displayed as
+ **1 inaccessible merge request**, and can be removed the same way.
+1. Select **Save changes**.
+
+## Troubleshooting
+
+### API support for managing merge request dependencies
+
+No API support exists for managing dependencies. For more information, read
+[issue #12551](https://gitlab.com/gitlab-org/gitlab/-/issues/12551).
+
+### Preserving dependencies on project import or export
+
+Dependencies are not preserved when projects are imported or exported. For more
+information, read [issue #12549](https://gitlab.com/gitlab-org/gitlab/-/issues/12549).
+
+### Complex merge order dependencies are unsupported
+
+GitLab supports direct dependencies between merge requests, but does not support
+[indirect (nested) dependencies](https://gitlab.com/gitlab-org/gitlab/-/issues/11393).
+
+Acceptable dependency patterns include:
+
+- A single merge request can directly depend on a single merge request.
+- A single merge request can directly depend on multiple merge requests.
+- Multiple merge requests can directly depend on a single merge request.
+
+The indirect, nested dependency between `myfriend/library!10` and `mycorp/example!100` shown in this example is not supported:
+
+```mermaid
+graph LR;
+ A[myfriend/library!10]-->|depends on| B[herfriend/another-lib!1]
+ B-->|depends on| C[mycorp/example!100]
+```
diff --git a/doc/user/project/merge_requests/drafts.md b/doc/user/project/merge_requests/drafts.md
index 4bb6034c0bd..695c6d7e612 100644
--- a/doc/user/project/merge_requests/drafts.md
+++ b/doc/user/project/merge_requests/drafts.md
@@ -20,6 +20,7 @@ the **Merge** button until you remove the **Draft** flag:
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32692) in GitLab 13.2, Work-In-Progress (WIP) merge requests were renamed to **Draft**.
> - [Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/228685) all support for using **WIP** in GitLab 14.8.
> - **Mark as draft** and **Mark as ready** buttons [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/227421) in GitLab 13.5.
+> `/draft` quick action as a toggle [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92654) in GitLab 15.4.
There are several ways to flag a merge request as a draft:
@@ -29,8 +30,7 @@ There are several ways to flag a merge request as a draft:
below the **Title** field.
- **Commenting in an existing merge request**: Add the `/draft`
[quick action](../quick_actions.md#issues-merge-requests-and-epics)
- in a comment. This quick action is a toggle, and can be repeated to change the status
- back to Ready.
+ in a comment. GitLab 15.4 [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92654) the toggle behavior of `/draft`. To mark a merge request as ready, use `/ready`.
- **Creating a commit**: Add `draft:`, `Draft:`, `fixup!`, or `Fixup!` to the
beginning of a commit message targeting the merge request's source branch. This
is not a toggle, and adding this text again in a later commit doesn't mark the
diff --git a/doc/user/project/merge_requests/getting_started.md b/doc/user/project/merge_requests/getting_started.md
index 09ee828ffd3..9475c0d60ab 100644
--- a/doc/user/project/merge_requests/getting_started.md
+++ b/doc/user/project/merge_requests/getting_started.md
@@ -66,7 +66,7 @@ After you have created the merge request, you can also:
- [Discuss](../../discussions/index.md) your implementation with your team in the merge request thread.
- [Perform inline code reviews](reviews/index.md).
-- Add [merge request dependencies](merge_request_dependencies.md) to restrict it to be merged only when other merge requests have been merged.
+- Add [merge request dependencies](dependencies.md) to restrict it to be merged only when other merge requests have been merged.
- Preview continuous integration [pipelines on the merge request widget](widgets.md).
- Preview how your changes look directly on your deployed application with [Review Apps](widgets.md#live-preview-with-review-apps).
- [Allow collaboration on merge requests across forks](allow_collaboration.md).
diff --git a/doc/user/project/merge_requests/img/cancel-mwps_v15_4.png b/doc/user/project/merge_requests/img/cancel-mwps_v15_4.png
new file mode 100644
index 00000000000..7ed780d4389
--- /dev/null
+++ b/doc/user/project/merge_requests/img/cancel-mwps_v15_4.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/cherry_pick_changes_commit.png b/doc/user/project/merge_requests/img/cherry_pick_changes_commit.png
deleted file mode 100644
index c98821548f8..00000000000
--- a/doc/user/project/merge_requests/img/cherry_pick_changes_commit.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/img/cherry_pick_changes_mr.png b/doc/user/project/merge_requests/img/cherry_pick_changes_mr.png
deleted file mode 100644
index 8b51503419b..00000000000
--- a/doc/user/project/merge_requests/img/cherry_pick_changes_mr.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/img/cherry_pick_mr_timeline_v12_9.png b/doc/user/project/merge_requests/img/cherry_pick_mr_timeline_v12_9.png
deleted file mode 100644
index 919b576fcc6..00000000000
--- a/doc/user/project/merge_requests/img/cherry_pick_mr_timeline_v12_9.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/img/cherry_pick_mr_timeline_v15_4.png b/doc/user/project/merge_requests/img/cherry_pick_mr_timeline_v15_4.png
new file mode 100644
index 00000000000..d18c4aaec20
--- /dev/null
+++ b/doc/user/project/merge_requests/img/cherry_pick_mr_timeline_v15_4.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/cherry_pick_v15_4.png b/doc/user/project/merge_requests/img/cherry_pick_v15_4.png
new file mode 100644
index 00000000000..174bb113961
--- /dev/null
+++ b/doc/user/project/merge_requests/img/cherry_pick_v15_4.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/dependencies_edit_v12_4.png b/doc/user/project/merge_requests/img/dependencies_edit_v12_4.png
deleted file mode 100644
index 4edf0648794..00000000000
--- a/doc/user/project/merge_requests/img/dependencies_edit_v12_4.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/img/dependencies_view_v15_3.png b/doc/user/project/merge_requests/img/dependencies_view_v15_3.png
new file mode 100644
index 00000000000..d044e28046f
--- /dev/null
+++ b/doc/user/project/merge_requests/img/dependencies_view_v15_3.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_enable.png b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_enable.png
deleted file mode 100644
index 9487264b41a..00000000000
--- a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_enable.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_status.png b/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_status.png
deleted file mode 100644
index 70fa2efc855..00000000000
--- a/doc/user/project/merge_requests/img/merge_when_pipeline_succeeds_status.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/img/mwps_v15_4.png b/doc/user/project/merge_requests/img/mwps_v15_4.png
new file mode 100644
index 00000000000..f042912d470
--- /dev/null
+++ b/doc/user/project/merge_requests/img/mwps_v15_4.png
Binary files differ
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index 35ec075c674..500fb95c193 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -27,7 +27,7 @@ You can view merge requests for your project, group, or yourself.
To view all merge requests for a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Merge requests**.
Or, to use a [keyboard shortcut](../../shortcuts.md), press <kbd>g</kbd> + <kbd>m</kbd>.
@@ -36,7 +36,7 @@ Or, to use a [keyboard shortcut](../../shortcuts.md), press <kbd>g</kbd> + <kbd>
To view merge requests for all projects in a group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Merge requests**.
If your group contains subgroups, this view also displays merge requests from the subgroup projects.
@@ -160,13 +160,11 @@ change and whether you need access to a development environment:
## Assign a user to a merge request
-When a merge request is created, it's assigned by default to the person who created it.
-This person owns the merge request, but isn't responsible for [reviewing it](reviews/index.md).
-To assign the merge request to someone else, use the `/assign @user`
+To assign the merge request to a user, use the `/assign @user`
[quick action](../quick_actions.md#issues-merge-requests-and-epics) in a text area in
a merge request, or:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Merge requests** and find your merge request.
1. On the right sidebar, expand the right sidebar and locate the **Assignees** section.
1. Select **Edit**.
@@ -186,7 +184,7 @@ accountable for it:
To assign multiple assignees to a merge request, use the `/assign @user`
[quick action](../quick_actions.md#issues-merge-requests-and-epics) in a text area, or:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Merge requests** and find your merge request.
1. On the right sidebar, expand the right sidebar and locate the **Assignees** section.
1. Select **Edit** and, from the dropdown list, select all users you want
diff --git a/doc/user/project/merge_requests/merge_request_dependencies.md b/doc/user/project/merge_requests/merge_request_dependencies.md
index 6bfef6ab134..6242a77e931 100644
--- a/doc/user/project/merge_requests/merge_request_dependencies.md
+++ b/doc/user/project/merge_requests/merge_request_dependencies.md
@@ -1,141 +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/engineering/ux/technical-writing/#assignments
-type: reference, concepts
+redirect_to: 'dependencies.md'
+remove_date: '2022-11-22'
---
-# Merge request dependencies **(PREMIUM)**
+This document was moved to [another location](dependencies.md).
-> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9688) in GitLab 12.2.
-> - [Renamed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17291) from "Cross-project dependencies" to "Merge request dependencies" in GitLab 12.4.
-> - Intra-project MR dependencies were [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16799) in GitLab 12.4.
-
-Merge request dependencies allows a required order of merging
-between merge requests to be expressed. If a merge request "depends on" another,
-then it cannot be merged until its dependency is itself merged.
-
-NOTE:
-Merge requests dependencies are a **PREMIUM** feature, but this restriction is
-only enforced for the dependent merge request. A merge request in a **FREE**
-project can be a dependency of a **PREMIUM** merge request, but not
-the other way around.
-
-## Use cases
-
-- Ensure changes to a library are merged before changes to a project that
- imports the library.
-- Prevent a documentation-only merge request from being merged before the merge request
- implementing the feature to be documented.
-- Require a merge request updating a permissions matrix to be merged before merging a
- merge request from someone who hasn't yet been granted permissions.
-
-It is common for a single logical change to span several merge requests, spread
-out across multiple projects, and the order in which they are merged can be
-significant.
-
-For example, given a project `mycorp/awesome-project` that imports a library
-at `myfriend/awesome-lib`, adding a feature in `awesome-project` may **also**
-require changes to `awesome-lib`, and so necessitate two merge requests. Merging
-the `awesome-project` merge request before the `awesome-lib` one would
-break the default branch.
-
-The `awesome-project` merge request could be [marked as **Draft**](drafts.md),
-and the reason for the draft stated included in the comments. However, this
-requires the state of the `awesome-lib` merge request to be manually
-tracked, and doesn't scale well if the `awesome-project` merge request
-depends on changes to **several** other projects.
-
-By making the `awesome-project` merge request depend on the
-`awesome-lib` merge request instead, this relationship is
-automatically tracked by GitLab, and the draft state can be used to
-communicate the readiness of the code in each individual merge request
-instead.
-
-## Configuration
-
-To continue the above example, you can configure a dependency when creating the
-new merge request in `awesome-project` (or by editing it, if it already exists).
-The dependency needs to be configured on the **dependent** merge
-request. There is a **Merge request dependencies** section in the form:
-
-![Merge request dependencies form control](img/dependencies_edit_v12_4.png)
-
-Anyone who can edit a merge request can change the list of dependencies.
-
-New dependencies can be added by reference, or by URL. To remove a dependency,
-press the **X** by its reference.
-
-As dependencies can be specified across projects, it's possible that someone else
-has added a dependency for a merge request in a project you don't have access to.
-These are shown as a simple count:
-
-![Merge request dependencies form control with inaccessible merge requests](img/dependencies_edit_inaccessible_v12_4.png)
-
-If necessary, you can remove all the dependencies like this by pressing the
-**X**, just as you would for a single, visible dependency.
-
-Once you're finished, press the **Save changes** button to submit the request,
-or **Cancel** to return without making any changes.
-
-The list of configured dependencies, and the status of each one, is shown in the
-merge request widget:
-
-![Dependencies in merge request widget](img/dependencies_view_v12_2.png)
-
-Until all dependencies have, themselves, been merged, the **Merge**
-button is disabled for the dependent merge request. In
-particular, note that **closed merge requests** still prevent their
-dependents from being merged - it is impossible to automatically
-determine whether the dependency expressed by a closed merge request
-has been satisfied in some other way or not.
-
-If a merge request has been closed **and** the dependency is no longer relevant,
-it must be removed as a dependency, following the instructions above, before
-merge.
-
-## Limitations
-
-- API support: [issue #12551](https://gitlab.com/gitlab-org/gitlab/-/issues/12551)
-- Dependencies are not preserved across project export/import: [issue #12549](https://gitlab.com/gitlab-org/gitlab/-/issues/12549)
-- Complex merge order dependencies are not supported: [issue #11393](https://gitlab.com/gitlab-org/gitlab/-/issues/11393)
-
-The last item merits a little more explanation. Dependencies between merge
-requests can be described as a graph of relationships. The simplest possible
-graph has one merge request that depends upon another:
-
-```mermaid
-graph LR;
- myfriend/awesome-lib!10-->mycorp/awesome-project!100;
-```
-
-A more complex (and still supported) graph might have one merge request that
-directly depends upon several others:
-
-```mermaid
-graph LR;
- myfriend/awesome-lib!10-->mycorp/awesome-project!100;
- herfriend/another-lib!1-->mycorp/awesome-project!100;
-```
-
-Several different merge requests can also directly depend upon the
-same merge request:
-
-```mermaid
-graph LR;
- herfriend/another-lib!1-->myfriend/awesome-lib!10;
- herfriend/another-lib!1-->mycorp/awesome-project!100;
-```
-
-What is **not** supported is a "deep", or "nested" graph of dependencies. For example:
-
-```mermaid
-graph LR;
- herfriend/another-lib!1-->myfriend/awesome-lib!10;
- myfriend/awesome-lib!10-->mycorp/awesome-project!100;
-```
-
-In this example, `myfriend/awesome-lib!10` depends on `herfriend/another-lib!1`,
-and is itself a dependent of `mycorp/awesome-project!100`. This means that
-`myfriend/awesome-lib!10` becomes an **indirect** dependency of
-`mycorp/awesome-project!100`, which is not yet supported.
+<!-- 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/merge_when_pipeline_succeeds.md b/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md
index 9182cf11566..57c4ff455cb 100644
--- a/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md
+++ b/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md
@@ -7,125 +7,143 @@ type: reference, concepts
# Merge when pipeline succeeds **(FREE)**
-When reviewing a merge request that looks ready to merge but still has a
-pipeline running, you can set it to merge automatically when the
-pipeline succeeds. This way, you don't have to wait for the pipeline to
-finish and remember to merge the request manually.
+If you review a merge request and it's ready to merge, but the pipeline hasn't
+completed yet, you can set it to merge when the pipeline succeeds (MWPS). You don't
+have to remember later to merge the work manually:
-![Enable](img/merge_when_pipeline_succeeds_enable.png)
+![Enable MWPS on a merge request](img/mwps_v15_4.png)
-## How it works
+If the pipeline succeeds, the merge request is merged. If the pipeline fails, the
+author can either retry any failed jobs, or push new commits to fix the failure:
-When you select "Merge When Pipeline Succeeds", the status of the merge
-request is updated to show the impending merge. If you can't wait
-for the pipeline to succeed, you can choose **Merge immediately**
-in the dropdown menu on the right of the main button.
+- If a retried job succeeds on the second try, the merge request is merged.
+- If new commits are added to the merge request, GitLab cancels the MWPS request
+ to ensure the new changes are reviewed before merge.
-The author of the merge request and project members with the Developer role can
-cancel the automatic merge at any time before the pipeline finishes.
+## Set a merge request to MWPS
-![Status](img/merge_when_pipeline_succeeds_status.png)
+Prerequisites:
-When the pipeline succeeds, the merge request is automatically merged.
-When the pipeline fails, the author gets a chance to retry any failed jobs,
-or to push new commits to fix the failure.
+- You must have at least the Developer role in the project.
+- If the project is configured to require it, all threads in the
+ merge request [must be resolved](../../discussions/index.md#resolve-a-thread).
+- The merge request must have received all required approvals.
-When the jobs are retried and succeed on the second try, the merge request
-is automatically merged. When the merge request is updated with
-new commits, the automatic merge is canceled to allow the new
-changes to be reviewed.
+To do this when pushing from the command line, use the `merge_request.merge_when_pipeline_succeeds`
+[push option](../push_options.md).
-By default, all threads must be resolved before you see the **Merge when
-pipeline succeeds** button. If someone adds a new comment after
-the button is selected, but before the jobs in the CI pipeline are
-complete, the merge is blocked until you resolve all existing threads.
+To do this from the GitLab user interface:
-## Only allow merge requests to be merged if the pipeline succeeds
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Merge requests**.
+1. Scroll to the merge request reports section.
+1. Optional. Select your desired merge options, such as **Delete source branch**,
+ **Squash commits**, or **Edit commit message**.
+1. Select **Merge when pipeline succeeds**.
-You can prevent merge requests from being merged if:
+If a new comment is added to the merge request after you select **Merge when pipeline succeeds**,
+but before the pipeline completes, GitLab blocks the merge until you
+resolve all existing threads.
-- No pipeline ran.
-- The pipeline did not succeed.
+## Cancel an auto-merge
-This works for both:
+If a merge request is set to MWPS, you can cancel it.
-- GitLab CI/CD pipelines
-- Pipelines run from an [external CI integration](../integrations/index.md#available-integrations)
+Prerequisites:
+
+- You must either be the author of the merge request, or a project member with
+ at least the Developer role.
+- The merge request's pipeline must still be in progress.
+
+To do this:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Merge requests**.
+1. Scroll to the merge request reports section.
+1. Select **Cancel auto-merge**.
+
+![Status](img/cancel-mwps_v15_4.png)
+
+## Require a successful pipeline for merge
+
+You can configure your project to require a complete and successful pipeline before
+merge. This configuration works for both:
+
+- GitLab CI/CD pipelines.
+- Pipelines run from an [external CI integration](../integrations/index.md#available-integrations).
As a result, [disabling GitLab CI/CD pipelines](../../../ci/enable_or_disable_ci.md)
-does not disable this feature, as it is possible to use pipelines from external
-CI providers with this feature. To enable it, you must:
+does not disable this feature, but you can use pipelines from external
+CI providers with it.
+
+Prerequisites:
+
+- Ensure CI/CD is configured to run a pipeline for every merge request.
+- You must have at least the Maintainer role in the project.
+
+To enable this setting:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > Merge requests**.
+1. Scroll to **Merge checks**, and select **Pipelines must succeed**.
+ This setting also prevents merge requests from being merged if there is no pipeline,
+ which can [conflict with some rules](#merge-requests-dont-merge-when-successful-pipeline-is-required).
+1. Select **Save**.
+
+### Allow merge after skipped pipelines
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/211482) in GitLab 13.1.
+
+When the **Pipelines must succeed** checkbox is checked,
+[skipped pipelines](../../../ci/pipelines/index.md#skip-a-pipeline) prevent
+merge requests from being merged.
+
+Prerequisite:
-1. On the top bar, select **Menu > Projects** and find your project.
+- You must have at least the Maintainer role in the project.
+
+To change this behavior:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Merge requests**.
-1. Under **Merge checks**, select the **Pipelines must succeed** checkbox.
+1. Under **Merge checks**:
+ - Select **Pipelines must succeed**.
+ - Select **Skipped pipelines are considered successful**.
1. Select **Save**.
-This setting also prevents merge requests from being merged if there is no pipeline.
-You should be careful to configure CI/CD so that pipelines run for every merge request.
+## Troubleshooting
-### Limitations
+### Merge requests don't merge when successful pipeline is required
-When this setting is enabled, a merge request is prevented from being merged if there
-is no pipeline. This may conflict with some use cases where [`only/except`](../../../ci/yaml/index.md#only--except)
-or [`rules`](../../../ci/yaml/index.md#rules) are used and they don't generate any pipelines.
+If you require a successful pipeline for a merge, this setting can conflict with some
+use cases that do not generate pipelines, such as [`only/except`](../../../ci/yaml/index.md#only--except)
+or [`rules`](../../../ci/yaml/index.md#rules). Ensure your project
+[runs a pipeline](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/54226) for
+every merge request, and that the pipeline is successful.
-You should ensure that [there is always a pipeline](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/54226)
-and that it's successful.
+### Ensure test parity between pipeline types
-If both a branch pipeline and a merge request pipeline are triggered for a single
-merge request, only the success or failure of the *merge request pipeline* is checked.
-If the merge request pipeline is configured with fewer jobs than the branch pipeline,
-it could allow code that fails tests to be merged:
+If a merge request triggers both a branch pipeline and a merge request pipeline,
+the success or failure of only the *merge request pipeline* is checked.
+If the merge request pipeline contains fewer jobs than the branch pipeline,
+it could allow code that fails tests to be merged, like in this example:
```yaml
branch-pipeline-job:
rules:
- if: $CI_PIPELINE_SOURCE == "push"
script:
- - echo "Code testing scripts here, for example."
+ - echo "Testing happens here."
merge-request-pipeline-job:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
script:
- - echo "No tests run, but this pipeline always succeeds and enables merge."
+ - echo "No testing happens here. This pipeline always succeeds, and enables merge."
- echo true
```
-You should avoid configuration like this, and only use branch (`push`) pipelines
-or merge request pipelines, when possible. See [`rules` documentation](../../../ci/jobs/job_control.md#avoid-duplicate-pipelines)
-for details on avoiding two pipelines for a single merge request.
-
-### Skipped pipelines
-
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/211482) in GitLab 13.1.
-
-When the **Pipelines must succeed** checkbox is checked, [skipped pipelines](../../../ci/pipelines/index.md#skip-a-pipeline) prevent
-merge requests from being merged. To change this behavior:
-
-1. On the top bar, select **Menu > Projects** and find your project.
-1. On the left sidebar, select **Settings > General**.
-1. Expand **Merge requests**.
-1. Under **Merge checks**:
- - Ensure **Pipelines must succeed** is selected.
- - Select the **Skipped pipelines are considered successful** checkbox.
-1. Select **Save**.
-
-## From the command line
-
-You can use [Push Options](../push_options.md) to enable merge when pipeline succeeds
-for a merge request when pushing from the command line.
-
-<!-- ## Troubleshooting
-
-Include any troubleshooting steps that you can foresee. If you know beforehand what issues
-one might have when setting this up, or when something is changed, or on upgrading, it's
-important to describe those, too. Think of things that may go wrong and include them here.
-This is important to minimize requests for support, and to avoid doc comments with
-questions that you know someone might ask.
-
-Each scenario can be a third-level heading, e.g. `### 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. -->
+Instead, use branch (`push`) pipelines or merge request pipelines, when possible.
+For details on avoiding two pipelines for a single merge request, read the
+[`rules` documentation](../../../ci/jobs/job_control.md#avoid-duplicate-pipelines).
diff --git a/doc/user/project/merge_requests/methods/index.md b/doc/user/project/merge_requests/methods/index.md
index c4e4b40dc48..68dd6477408 100644
--- a/doc/user/project/merge_requests/methods/index.md
+++ b/doc/user/project/merge_requests/methods/index.md
@@ -12,9 +12,8 @@ merge requests are merged into an existing branch.
## Configure a project's merge method
-1. On the top bar, select **Menu > Projects** and find your project.
-1. On the left sidebar, select **Settings > General**.
-1. Expand **Merge requests**.
+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 **Save changes**.
@@ -110,7 +109,7 @@ gitGraph
```
This method is equivalent to `git merge --ff <source-branch>` for regular merges, and to
-`git merge -squash <source-branch>` for squash merges.
+`git merge --squash <source-branch>` for squash merges.
When the fast-forward merge
([`--ff-only`](https://git-scm.com/docs/git-merge#git-merge---ff-only)) setting
diff --git a/doc/user/project/merge_requests/revert_changes.md b/doc/user/project/merge_requests/revert_changes.md
index 8f433c13887..a6e0740ff78 100644
--- a/doc/user/project/merge_requests/revert_changes.md
+++ b/doc/user/project/merge_requests/revert_changes.md
@@ -6,50 +6,86 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Revert changes **(FREE)**
-You can use Git's powerful feature to [revert any commit](https://git-scm.com/docs/git-revert "Git revert documentation")
-by clicking the **Revert** button in merge requests and commit details.
+You can revert individual commits or an entire merge request in GitLab.
+When you revert a commit in Git, you create a new commit that reverses all actions
+taken in the original commit:
+
+- Lines added in the original commit are removed.
+- Lines removed in the original commit are added back.
+- Lines modified in the original commit are restored to their previous state.
+
+Your **revert commit** is still subject to your project's access controls and processes.
## Revert a merge request
-NOTE:
-The **Revert** button is shown only for projects that use the
-merge method "Merge Commit", which can be set under the project's
-**Settings > General > Merge request**. [Fast-forward commits](methods/index.md#fast-forward-merge)
-can not be reverted by using the merge request view.
+After a merge request is merged, you can revert all changes in the merge request.
+
+Prerequisites:
-After the merge request has been merged, use the **Revert** button
-to revert the changes introduced by that merge request.
+- You must have a role in the project that allows you to edit merge requests, and add
+ code to the repository.
+- Your project must use the [merge method](methods/index.md#fast-forward-merge) **Merge Commit**,
+ which is set in the project's **Settings > General > Merge request**. You can't revert
+ fast-forwarded commits from the GitLab UI.
-![Revert merge request](img/cherry_pick_changes_mr.png)
+To do this:
-After you select that button, a modal appears where you can choose to
-revert the changes directly into the selected branch or you can opt to
-create a new merge request with the revert changes.
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. On the left sidebar, select **Merge requests** and identify your merge request.
+1. Scroll to the merge request reports area, and find the report showing when the
+ merge request was merged.
+1. Select **Revert**.
+1. In **Revert in branch**, select the branch to revert your changes into.
+1. Optional. Select **Start a new merge request** to start a new merge request with the new revert commit.
+1. Select **Revert**.
-After the merge request has been reverted, the **Revert** button is no longer available.
+The option to **Revert** is no longer shown after a merge request is reverted.
## Revert a commit
-You can revert a commit from the commit details page:
+You can revert any commit in a repository into either:
+
+- The current branch.
+- A new merge request.
+
+Prerequisites:
+
+- You must have a role in the project that allows you to edit merge requests, and add
+ code to the repository.
+
+To do this:
+
+1. On the top bar, select **Main menu > Projects** and find your project.
+1. If you know the merge request that contains the commit:
+ 1. On the left sidebar, select **Merge requests** and identify your merge request.
+ 1. Select **Commits**, then select the title of the commit you want to revert. GitLab displays the contents of the commit.
+1. If you don't know the merge request the commit originated from:
+ 1. On the left sidebar, select **Repository > Commits**.
+ 1. Select the title of the commit to display full information about the commit.
+1. In the top right corner, select **Options**, then select **Revert**.
+1. In **Revert in branch**, select the branch to revert your changes into.
+1. Optional. Select **Start a new merge request** to start a new merge request with the new revert commit.
+1. Select **Revert**.
+
+The option to **Revert** is no longer shown after a commit is reverted.
-![Revert commit](img/cherry_pick_changes_commit.png)
+### Revert a merge commit to a different parent commit
-Similar to reverting a merge request, you can opt to revert the changes
-directly into the target branch or create a new merge request to revert the
-changes.
+When you revert a merge commit, the branch you merged to (usually `main`) is always the
+first parent. To revert a merge commit to a different parent,
+you must revert the commit from the command line:
-After a commit is reverted, the **Revert** button is no longer available.
+1. Identify the SHA of the parent commit you want to revert to.
+1. Identify the parent number of the commit you want to revert to. (Defaults to 1, for the first parent.)
+1. Modify this command, replacing `2` with the parent number, and `7a39eb0` with the commit SHA:
-When reverting merge commits, the mainline is always the
-first parent. If you want to use a different mainline, you need to do that
-from the command line.
+ ```shell
+ git revert -m 2 7a39eb0
+ ```
-Here's an example to revert a merge commit using the second parent as the
-mainline:
+## Related topics
-```shell
-git revert -m 2 7a39eb0
-```
+- [Official `git revert` documentation](https://git-scm.com/docs/git-revert)
<!-- ## Troubleshooting
diff --git a/doc/user/project/merge_requests/reviews/img/mr_summary_comment_v15_3.png b/doc/user/project/merge_requests/reviews/img/mr_summary_comment_v15_3.png
deleted file mode 100644
index 38e18115803..00000000000
--- a/doc/user/project/merge_requests/reviews/img/mr_summary_comment_v15_3.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/reviews/img/mr_summary_comment_v15_4.png b/doc/user/project/merge_requests/reviews/img/mr_summary_comment_v15_4.png
new file mode 100644
index 00000000000..47b7be3886d
--- /dev/null
+++ b/doc/user/project/merge_requests/reviews/img/mr_summary_comment_v15_4.png
Binary files differ
diff --git a/doc/user/project/merge_requests/reviews/index.md b/doc/user/project/merge_requests/reviews/index.md
index 27b223c48ec..78f82b019b8 100644
--- a/doc/user/project/merge_requests/reviews/index.md
+++ b/doc/user/project/merge_requests/reviews/index.md
@@ -56,12 +56,11 @@ displays next to your name.
You can submit your completed review in multiple ways:
- Use the `/submit_review` [quick action](../../quick_actions.md) in the text of a non-review comment.
-- Select **Finish review** and then **Submit review** in the footer at the bottom of the screen.
+- Select **Finish review**, then select **Submit review** at the bottom of the modal window.
+ In the modal window, you can supply a **Summary comment**, approve the merge request, and
+ include quick actions:
-Selecting **Finish review** opens a modal window to add an optional comment to summarize your review.
-You can also include quick actions:
-
-![Finish review with comment](img/mr_summary_comment_v15_3.png)
+ ![Finish review with comment](img/mr_summary_comment_v15_4.png)
When you submit your review, GitLab:
@@ -69,6 +68,7 @@ When you submit your review, GitLab:
- Sends a single email to every notifiable user of the merge request, with your
review comments attached. Replying to this email creates a new comment on the merge request.
- Perform any quick actions you added to your review comments.
+- Optional. Approves the merge request.
### Resolve or unresolve thread with a comment
@@ -96,7 +96,7 @@ If you have a review in progress, you can also add a comment from the **Overview
### Approval Rule information for Reviewers **(PREMIUM)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/233736) in GitLab 13.8.
-> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/293742) in GitLab 13.9.
+> - [Feature flag `reviewer_approval_rules` removed](https://gitlab.com/gitlab-org/gitlab/-/issues/293742) in GitLab 13.9.
When editing the **Reviewers** field in a new or existing merge request, GitLab
displays the name of the matching [approval rule](../approvals/rules.md)
diff --git a/doc/user/project/merge_requests/squash_and_merge.md b/doc/user/project/merge_requests/squash_and_merge.md
index 7e37990b9bf..066149afbb5 100644
--- a/doc/user/project/merge_requests/squash_and_merge.md
+++ b/doc/user/project/merge_requests/squash_and_merge.md
@@ -60,9 +60,8 @@ squash the commits as part of the merge process:
To configure the default squashing behavior for all merge requests in your project:
-1. On the top bar, select **Menu > Projects** and find your project.
-1. On the left sidebar, select **Settings > General**.
-1. Expand **Merge requests**.
+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 **Squash commits when merging** section, select your desired behavior:
- **Do not allow**: Squashing is never performed, and the option is not displayed.
- **Allow**: Squashing is allowed, but cleared by default.
diff --git a/doc/user/project/merge_requests/status_checks.md b/doc/user/project/merge_requests/status_checks.md
index 0d7794a3ebd..da705a53153 100644
--- a/doc/user/project/merge_requests/status_checks.md
+++ b/doc/user/project/merge_requests/status_checks.md
@@ -1,5 +1,5 @@
---
-stage: Manage
+stage: Govern
group: Compliance
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"
type: reference, concepts
@@ -61,9 +61,8 @@ using the API. You don't need to wait for a merge request webhook payload to be
Within each project's settings, you can see a list of status checks added to the project:
-1. In your project, go to **Settings > General**.
-1. Expand the **Merge requests** section.
-1. Scroll down to the **Status checks** sub-section.
+1. In your project, go to **Settings > Merge requests** section.
+1. Scroll down to **Status checks**.
![Status checks list](img/status_checks_list_view_v14_0.png)
diff --git a/doc/user/project/milestones/index.md b/doc/user/project/milestones/index.md
index ef734225fb4..723ca17ee56 100644
--- a/doc/user/project/milestones/index.md
+++ b/doc/user/project/milestones/index.md
@@ -37,27 +37,75 @@ For information about project and group milestones API, see:
To view the milestone list:
-1. On the top bar, select **Menu > Projects** and find your project or
- **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Projects** and find your project or
+ **Main menu > Groups** and find your group.
1. Select **Issues > Milestones**.
In a project, GitLab displays milestones that belong to the project.
In a group, GitLab displays milestones that belong to the group and all projects in the group.
-NOTE:
+### View milestones in a project with issues turned off
+
If a project has issue tracking
[turned off](../settings/index.md#configure-project-visibility-features-and-permissions),
you can get to the milestones page
-by going to its URL. To do so, add: `/-/milestones` to your project or group URL.
-For example `https://gitlab.com/gitlab-org/sample-data-templates/sample-gitlab-project/-/milestones`.
-This is tracked in [issue 339009](https://gitlab.com/gitlab-org/gitlab/-/issues/339009).
+by going to its URL.
+
+To do so:
+
+1. Go to your project.
+1. Add: `/-/milestones` to your project URL.
+ For example `https://gitlab.com/gitlab-org/sample-data-templates/sample-gitlab-project/-/milestones`.
+
+Alternatively, this project's issues are visible in the group's milestone page.
+
+Improving this experience is tracked in issue [339009](https://gitlab.com/gitlab-org/gitlab/-/issues/339009).
### View all milestones
You can view all the milestones you have access to in the entire GitLab namespace.
You might not see some milestones because they're in projects or groups you're not a member of.
-To do so, on the top bar select **Menu > Milestones**.
+To do so, on the top bar select **Main menu > Milestones**.
+
+### View milestone details
+
+To view more information about a milestone,
+in the milestone list select the title of the milestone you want to view.
+
+The milestone view shows the title and description.
+
+There are also tabs below these that show the following:
+
+- **Issues**: Shows all issues assigned to the milestone. These are displayed in three columns named:
+ - Unstarted Issues (open and unassigned)
+ - Ongoing Issues (open and assigned)
+ - Completed Issues (closed)
+- **Merge Requests**: Shows all merge requests assigned to the milestone. These are displayed in four columns named:
+ - Work in progress (open and unassigned)
+ - Waiting for merge (open and assigned)
+ - Rejected (closed)
+ - Merged
+- **Participants**: Shows all assignees of issues assigned to the milestone.
+- **Labels**: Shows all labels that are used in issues assigned to the milestone.
+
+#### Burndown charts
+
+The milestone view contains a [burndown and burnup chart](burndown_and_burnup_charts.md),
+showing the progress of completing a milestone.
+
+![burndown chart](img/burndown_and_burnup_charts_v15_3.png)
+
+#### Milestone sidebar
+
+The milestone sidebar on the milestone view shows the following:
+
+- Percentage complete, which is calculated as number of closed issues divided by total number of issues.
+- The start date and due date.
+- The total time spent on all issues and merge requests assigned to the milestone.
+- The total issue weight of all issues assigned to the milestone.
+
+![Project milestone page](img/milestones_project_milestone_page_sidebar_v13_11.png)
## Create a milestone
@@ -71,7 +119,7 @@ Prerequisites:
To create a milestone:
-1. On the top bar, select **Menu > Projects** and find your project or **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Projects** and find your project or **Main menu > Groups** and find your group.
1. On the left sidebar, select **Issues > Milestones**.
1. Select **New milestone**.
1. Enter the title.
@@ -90,7 +138,7 @@ Prerequisites:
To edit a milestone:
-1. On the top bar, select **Menu > Projects** and find your project or **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Projects** and find your project or **Main menu > Groups** and find your group.
1. Select a milestone's title.
1. Select **Edit**.
1. Edit the title, start date, due date, or description.
@@ -106,7 +154,7 @@ Prerequisites:
To edit a milestone:
-1. On the top bar, select **Menu > Projects** and find your project or **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Projects** and find your project or **Main menu > Groups** and find your group.
1. Select a milestone's title.
1. Select **Delete**.
1. Select **Delete milestone**.
@@ -131,7 +179,7 @@ Prerequisites:
To promote a project milestone:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. Either:
- Select **Promote to Group Milestone** (**{level-up}**).
- Select the milestone title, and then select **Promote**.
@@ -153,13 +201,13 @@ To assign or unassign a milestone:
You can also use the `/assign` [quick action](../quick_actions.md) in a comment.
-## Filtering issues and merge requests by milestone
+## Filter issues and merge requests by milestone
-### Filtering in list pages
+### Filters in list pages
From the project and group issue/merge request list pages, you can filter by both group and project milestones.
-### Filtering in issue boards
+### Filters in issue boards
From [project issue boards](../issue_board.md), you can filter by both group milestones and project
milestones in:
@@ -181,42 +229,6 @@ When filtering by milestone, in addition to choosing a specific project mileston
- **Upcoming**: Show issues or merge requests that have been assigned the open milestone and has the nearest due date in the future.
- **Started**: Show issues or merge requests that have an open assigned milestone with a start date that is before today.
-## Milestone view
-
-The milestone view shows the title and description.
-
-There are also tabs below these that show the following:
-
-- **Issues**: Shows all issues assigned to the milestone. These are displayed in three columns named:
- - Unstarted Issues (open and unassigned)
- - Ongoing Issues (open and assigned)
- - Completed Issues (closed)
-- **Merge Requests**: Shows all merge requests assigned to the milestone. These are displayed in four columns named:
- - Work in progress (open and unassigned)
- - Waiting for merge (open and assigned)
- - Rejected (closed)
- - Merged
-- **Participants**: Shows all assignees of issues assigned to the milestone.
-- **Labels**: Shows all labels that are used in issues assigned to the milestone.
-
-### Burndown Charts
-
-The milestone view contains a [burndown and burnup chart](burndown_and_burnup_charts.md),
-showing the progress of completing a milestone.
-
-![burndown chart](img/burndown_and_burnup_charts_v15_3.png)
-
-### Milestone sidebar
-
-The milestone sidebar on the milestone view shows the following:
-
-- Percentage complete, which is calculated as number of closed issues divided by total number of issues.
-- The start date and due date.
-- The total time spent on all issues and merge requests assigned to the milestone.
-- The total issue weight of all issues assigned to the milestone.
-
-![Project milestone page](img/milestones_project_milestone_page_sidebar_v13_11.png)
-
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
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 2c668e2c409..03f8ecac77f 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
@@ -7,17 +7,22 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Custom domains and SSL/TLS certificates **(FREE)**
-Setting up GitLab Pages with custom domains, and adding SSL/TLS certificates to them, are optional features of GitLab Pages.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/238461) in GitLab 15.4, you can use verified domains to [bypass user email confirmation for SAML- or SCIM-provisioned users](../../../group/saml_sso/index.md#bypass-user-email-confirmation-with-verified-domains).
-To use one or more custom domain names with your Pages site, you can:
+You can use custom domains:
-- Add a [custom **root domain** or a **subdomain**](#set-up-pages-with-a-custom-domain).
+- With GitLab Pages.
+- To [bypass user email confirmation for SAML- or SCIM-provisioned users](../../../group/saml_sso/index.md#bypass-user-email-confirmation-with-verified-domains).
+ When using custom domains this way, you use the GitLab Pages feature but can skip the [requirements](#requirements).
+
+To use one or more custom domain names:
+
+- Add a [custom **root domain** or a **subdomain**](#set-up-a-custom-domain).
- Add [SSL/TLS certification](#adding-an-ssltls-certificate-to-pages).
-## Set up Pages with a custom domain
+## Set up a custom domain
-To set up Pages with a custom domain name, read the requirements
-and steps below.
+To set up Pages with a custom domain name, read the requirements and steps below.
### Requirements
@@ -45,7 +50,7 @@ and steps below.
Follow the steps below to add your custom domain to Pages. See also
this document for an [overview on DNS records](dns_concepts.md).
-#### 1. Add a custom domain to Pages
+#### 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:
@@ -64,7 +69,7 @@ and paste them in your domain's control panel as a `TXT` record on the next step
![Get the verification code](img/get_domain_verification_code_v12_0.png)
-#### 3. Set up DNS records for Pages
+#### 3. Set up DNS records
Read this document for an [overview of DNS records for Pages](dns_concepts.md).
If you're familiar with the subject, follow the instructions below
@@ -144,7 +149,7 @@ They require:
| `_gitlab-pages-verification-code.www.example.com` | `TXT` | `gitlab-pages-verification-code=00112233445566778899aabbccddeeff` |
If you're using Cloudflare, check
-[Redirecting `www.domain.com` to `domain.com` with Cloudflare](#redirecting-wwwdomaincom-to-domaincom-with-cloudflare).
+[Redirecting `www.domain.com` to `domain.com` with Cloudflare](#redirect-wwwdomaincom-to-domaincom-with-cloudflare).
> **Notes**:
>
@@ -186,7 +191,7 @@ from the GitLab project.
in place. Your domain is periodically reverified, and may be
disabled if the record is removed.
-##### Troubleshooting Pages domain verification
+##### Troubleshoot domain verification
To manually verify that you have properly configured the domain verification
`TXT` DNS entry, you can run the following command in your terminal:
@@ -218,7 +223,7 @@ For a subdomain:
| `www.example.com` | `TXT` | `gitlab-pages-verification-code=00112233445566778899aabbccddeeff` |
| `_gitlab-pages-verification-code.www.example.com` | `TXT` | `gitlab-pages-verification-code=00112233445566778899aabbccddeeff` |
-### Adding more domain aliases
+### Add more domain aliases
You can add more than one alias (custom domains and subdomains) to the same project.
An alias can be understood as having many doors leading to the same room.
@@ -226,7 +231,7 @@ An alias can be understood as having many doors leading to the same room.
All the aliases you've set to your site are listed on **Setting > Pages**.
From that page, you can view, add, and remove them.
-### Redirecting `www.domain.com` to `domain.com` with Cloudflare
+### Redirect `www.domain.com` to `domain.com` with Cloudflare
If you use Cloudflare, you can redirect `www` to `domain.com`
without adding both `www.domain.com` and `domain.com` to GitLab.
diff --git a/doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md b/doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md
index 0cc6cb808d1..b5487f7a465 100644
--- a/doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md
+++ b/doc/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.md
@@ -30,7 +30,7 @@ Before you can enable automatic provisioning of an SSL certificate for your doma
- Acquired a domain (`example.com`) and added a [DNS entry](index.md)
pointing it to your Pages website. The top-level domain (`.com`) must be a
[public suffix](https://publicsuffix.org/).
-- [Added your domain to your Pages project](index.md#1-add-a-custom-domain-to-pages)
+- [Added your domain to your Pages project](index.md#1-add-a-custom-domain)
and verified your ownership.
- Verified your website is up and running, accessible through your custom domain.
@@ -76,7 +76,7 @@ If you get an error **Something went wrong while obtaining the Let's Encrypt cer
1. Make sure you have properly set only one `CNAME` or `A` DNS record for your domain.
1. Make sure your domain **doesn't have** an `AAAA` DNS record.
1. If you have a `CAA` DNS record for your domain or any higher level domains, make sure [it includes `letsencrypt.org`](https://letsencrypt.org/docs/caa/).
- 1. Make sure [your domain is verified](index.md#1-add-a-custom-domain-to-pages).
+ 1. Make sure [your domain is verified](index.md#1-add-a-custom-domain).
1. Go to step 1.
### Message "GitLab is obtaining a Let's Encrypt SSL certificate for this domain. This process can take some time. Please try again later." hangs for more than an hour
@@ -85,7 +85,7 @@ If you've enabled Let's Encrypt integration, but a certificate is absent after a
1. Go to your project's **Settings > Pages**.
1. Select **Remove** on your domain.
-1. [Add the domain again and verify it](index.md#1-add-a-custom-domain-to-pages).
+1. [Add the domain again and verify it](index.md#1-add-a-custom-domain).
1. [Enable Let's Encrypt integration for your domain](#enabling-lets-encrypt-integration-for-your-custom-domain).
1. If you still see the same message after some time:
1. Make sure you have properly set only one `CNAME` or `A` DNS record for your domain.
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 510f9332e7b..58e15104815 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
@@ -16,7 +16,7 @@ Use a `.gitlab-ci.yml` template when you have an existing project that you want
Your GitLab repository should contain files specific to an SSG, or plain HTML. After you complete
these steps, you may have to do additional configuration for the Pages site to generate properly.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select the project's name.
1. From the **Add** (**{plus}**) dropdown, select **New file**.
1. From the **Select a template type** dropdown, select `.gitlab-ci.yml`.
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 68a2a6a80ad..1c3d5d722cb 100644
--- a/doc/user/project/pages/getting_started/pages_from_scratch.md
+++ b/doc/user/project/pages/getting_started/pages_from_scratch.md
@@ -266,6 +266,7 @@ pages:
- public
rules:
- if: $CI_COMMIT_BRANCH == "main"
+ environment: production
```
Now add another job to the CI file, telling it to
@@ -289,6 +290,7 @@ pages:
- public
rules:
- if: $CI_COMMIT_BRANCH == "main"
+ environment: production
test:
stage: test
@@ -342,6 +344,7 @@ pages:
- public
rules:
- if: $CI_COMMIT_BRANCH == "main"
+ environment: production
test:
stage: test
@@ -386,6 +389,7 @@ pages:
- public
rules:
- if: $CI_COMMIT_BRANCH == "main"
+ environment: production
test:
stage: test
@@ -420,7 +424,7 @@ Now GitLab CI/CD not only builds the website, but also:
For more information, see the following blog posts.
-- Use GitLab CI/CD `environments` to
+- Use GitLab CI/CD `environments` to
[deploy your web app to staging and production](https://about.gitlab.com/blog/2021/02/05/ci-deployment-and-environments/).
- Learn how to run jobs
[sequentially, in parallel, or build a custom pipeline](https://about.gitlab.com/blog/2016/07/29/the-basics-of-gitlab-ci/).
diff --git a/doc/user/project/pages/getting_started/pages_ui.md b/doc/user/project/pages/getting_started/pages_ui.md
new file mode 100644
index 00000000000..7e618fbaec8
--- /dev/null
+++ b/doc/user/project/pages/getting_started/pages_ui.md
@@ -0,0 +1,58 @@
+---
+stage: Create
+group: Incubation
+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
+---
+
+# Tutorial: Use the GitLab UI to deploy your static site **(FREE)**
+
+This tutorial assumes you have a project that either:
+
+- 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).
+
+## Update your app to output files to the `public` folder
+
+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.
+
+For detailed instructions, read [Configure the public files folder](../public_folder.md).
+
+## Set up the `.gitlab-ci.yml` file
+
+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.
+
+To build your YAML file from the GitLab UI:
+
+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:
+ - 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
+ GitLab Pages deployment.
+
+## Troubleshooting
+
+### If you can't see the "Get Started with Pages" interface
+
+GitLab doesn't show this interface if you have either:
+
+- Deployed a GitLab Pages site before.
+- Committed a `.gitlab-ci.yml` through this interface at least once.
+
+To fix this problem:
+
+- If you see the message **Waiting for the Pages Pipeline to complete**, select
+ **Start over** to start the wizard again.
+- If your project has previously deployed GitLab Pages successfully,
+ [manually update](pages_from_scratch.md) your `.gitlab-ci.yml`.
diff --git a/doc/user/project/pages/index.md b/doc/user/project/pages/index.md
index af49522efe2..1f3628b74ec 100644
--- a/doc/user/project/pages/index.md
+++ b/doc/user/project/pages/index.md
@@ -38,12 +38,13 @@ Learn more about
To create a GitLab Pages website:
-| Document | Description |
-|----------|-------------|
+| Document | Description |
+|--------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|
+| [Use the GitLab UI to create a simple `.gitlab-ci.yml`](getting_started/pages_ui.md) | Add a Pages site to an existing project. Use the UI to set up a simple `.gitlab-ci.yml`. |
| [Create a `.gitlab-ci.yml` file from scratch](getting_started/pages_from_scratch.md) | Add a Pages site to an existing project. Learn how to create and configure your own CI file. |
-| [Use a `.gitlab-ci.yml` template](getting_started/pages_ci_cd_template.md) | Add a Pages site to an existing project. Use a pre-populated CI template file. |
-| [Fork a sample project](getting_started/pages_forked_sample_project.md) | Create a new project with Pages already configured by forking a sample project. |
-| [Use a project template](getting_started/pages_new_project_template.md) | Create a new project with Pages already configured by using a template. |
+| [Use a `.gitlab-ci.yml` template](getting_started/pages_ci_cd_template.md) | Add a Pages site to an existing project. Use a pre-populated CI template file. |
+| [Fork a sample project](getting_started/pages_forked_sample_project.md) | Create a new project with Pages already configured by forking a sample project. |
+| [Use a project template](getting_started/pages_new_project_template.md) | Create a new project with Pages already configured by using a template. |
To update a GitLab Pages website:
diff --git a/doc/user/project/pages/introduction.md b/doc/user/project/pages/introduction.md
index 3bd16a17f23..da024881ed6 100644
--- a/doc/user/project/pages/introduction.md
+++ b/doc/user/project/pages/introduction.md
@@ -314,6 +314,7 @@ pages:
artifacts:
paths:
- public
+ environment: production
```
The `FF_USE_FASTZIP` variable enables the [feature flag](https://docs.gitlab.com/runner/configuration/feature-flags.html#available-feature-flags) which is needed for [`ARTIFACT_COMPRESSION_LEVEL`](../../../ci/runners/configure_runners.md#artifact-and-cache-settings).
diff --git a/doc/user/project/pages/public_folder.md b/doc/user/project/pages/public_folder.md
new file mode 100644
index 00000000000..f9c80875cc9
--- /dev/null
+++ b/doc/user/project/pages/public_folder.md
@@ -0,0 +1,153 @@
+---
+description: 'Learn how to configure the build output folder for the most
+common static site generators'
+stage: Create
+group: Incubation
+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
+---
+
+# 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.
+
+## Guide by framework
+
+### Eleventy
+
+For Eleventy, you should either:
+
+1. Add the `--output=public` flag in Eleventy's build commands, for example:
+
+ `npx @11ty/eleventy --input=path/to/sourcefiles --output=public`
+
+1. Add the following to your `.eleventy.js` file:
+
+ ```javascript
+ // .eleventy.js
+ module.exports = function(eleventyConfig) {
+ return {
+ dir: {
+ output: "public"
+ }
+ }
+ };
+ ```
+
+### Astro
+
+By default, Astro uses the `public` folder to store static assets. For GitLab Pages,
+rename that folder to a collision-free alternative first:
+
+1. In your project directory, run:
+
+ ```shell
+ mv public static
+ ```
+
+1. Add the following to your `astro.config.mjs`. This code informs Astro about
+ our folder name remapping:
+
+ ```javascript
+ // astro.config.mjs
+ export default {
+ // GitLab Pages requires exposed files to be located in a folder called "public".
+ // So we're instructing Astro to put the static build output in a folder of that name.
+ dist: 'public',
+
+ // The folder name Astro uses for static files (`public`) is already reserved
+ // for the build output. So in deviation from the defaults we're using a folder
+ // called `static` instead.
+ public: 'static',
+ };
+ ```
+
+### 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).
+
+When using `adapter-static`, add the following to your `svelte.config.js`:
+
+```javascript
+// svelte.config.js
+import adapter from '@sveltejs/adapter-static';
+
+export default {
+ kit: {
+ adapter: adapter({
+ pages: 'public'
+ })
+ }
+};
+```
+
+### 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)
+
+Use the `-o public` flag after `next export` as the build command, for
+example:
+
+```shell
+next export -o public
+```
+
+### Nuxt.js
+
+NOTE:
+GitLab Pages supports only static sites.
+
+1. Add the following to your `nuxt.config.js`:
+
+ ```javascript
+ export default {
+ target: 'static',
+ generate: {
+ dir: 'public'
+ }
+ }
+ ```
+
+1. Configure your Nuxt.js application for
+ [Static Site Generation](https://nuxtjs.org/docs/features/deployment-targets#static-hosting).
+
+### Vite
+
+Update your `vite.config.js` to include the following:
+
+```javascript
+// vite.config.js
+export default {
+ build: {
+ outDir: 'public'
+ }
+}
+```
+
+### Webpack
+
+Update your `webpack.config.js` to include the following:
+
+```javascript
+// webpack.config.js
+module.exports = {
+ output: {
+ path: __dirname + '/public'
+ }
+};
+```
+
+## 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
+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.
diff --git a/doc/user/project/protected_branches.md b/doc/user/project/protected_branches.md
index 3e40a7962ae..9b685592c9d 100644
--- a/doc/user/project/protected_branches.md
+++ b/doc/user/project/protected_branches.md
@@ -41,7 +41,7 @@ Prerequisite:
To protect a branch:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Protected branches**.
1. From the **Branch** dropdown list, select the branch you want to protect.
@@ -63,7 +63,7 @@ Prerequisite:
To protect multiple branches at the same time:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Protected branches**.
1. From the **Branch** dropdown list, type the branch name and a wildcard.
@@ -96,7 +96,7 @@ from the command line or from a Git client application.
To create a new branch through the user interface:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Repository > Branches**.
1. Select **New branch**.
1. Fill in the branch name and select an existing branch, tag, or commit to
@@ -109,7 +109,7 @@ You can force everyone to submit a merge request, rather than allowing them to
check in directly to a protected branch. This setting is compatible with workflows
like the [GitLab workflow](../../topics/gitlab_flow.md).
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Protected branches**.
1. From the **Branch** dropdown list, select the branch you want to protect.
@@ -125,7 +125,7 @@ like the [GitLab workflow](../../topics/gitlab_flow.md).
You can allow everyone with write access to push to the protected branch.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Protected branches**.
1. From the **Branch** dropdown list, select the branch you want to protect.
@@ -153,7 +153,7 @@ Prerequisites:
To allow a deploy key to push to a protected branch:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Protected branches**.
1. From the **Branch** dropdown list, select the branch you want to protect.
@@ -172,7 +172,7 @@ protected branches.
To protect a new branch and enable force push:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Protected branches**.
1. From the **Branch** dropdown list, select the branch you want to protect.
@@ -184,7 +184,7 @@ To protect a new branch and enable force push:
To enable force pushes on branches that are already protected:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Protected branches**.
1. In the list of protected branches, next to the branch, turn on the **Allowed to force push** toggle.
@@ -200,7 +200,7 @@ For a protected branch, you can require at least one approval by a [Code Owner](
To protect a new branch and enable Code Owner's approval:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Protected branches**.
1. From the **Branch** dropdown list, select the branch you want to protect.
@@ -210,7 +210,7 @@ To protect a new branch and enable Code Owner's approval:
To enable Code Owner's approval on branches that are already protected:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Protected branches**.
1. In the list of protected branches, next to the branch, turn on the **Code owner approval** toggle.
@@ -242,7 +242,7 @@ for details about the pipelines security model.
Users with at least the Maintainer role can manually delete protected
branches by using the GitLab web interface:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Repository > Branches**.
1. Next to the branch you want to delete, select **Delete** (**{remove}**).
1. On the confirmation dialog, type the branch name.
diff --git a/doc/user/project/protected_tags.md b/doc/user/project/protected_tags.md
index 870c544cf4c..9b1e862af58 100644
--- a/doc/user/project/protected_tags.md
+++ b/doc/user/project/protected_tags.md
@@ -97,7 +97,7 @@ Prerequisite:
To do this:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Repository > Tags**.
1. Next to the tag you want to delete, select **Delete** (**{remove}**).
1. On the confirmation dialog, enter the tag name and select **Yes, delete protected tag**.
diff --git a/doc/user/project/push_options.md b/doc/user/project/push_options.md
index d02609cbdc7..3eb333f5785 100644
--- a/doc/user/project/push_options.md
+++ b/doc/user/project/push_options.md
@@ -102,7 +102,7 @@ long Git commands.
### Merge when pipeline succeeds alias
-To set up a Git alias for the
+To set up a Git alias for the
[merge when pipeline succeeds Git push option](#push-options-for-merge-requests):
```shell
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index 216d040734d..8b8eba62cce 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -67,7 +67,7 @@ threads. Some quick actions might not be available to all subscription tiers.
| `/copy_metadata <#issue>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Copy labels and milestone from another issue in the project. |
| `/create_merge_request <branch name>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Create a new merge request starting from the current issue. |
| `/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 | Toggle the [draft status](merge_requests/drafts.md). |
+| `/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. |
| `/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. |
@@ -100,7 +100,7 @@ threads. Some quick actions might not be available to all subscription tiers.
| `/remove_milestone` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Remove milestone. |
| `/remove_parent_epic` | **{dotted-circle}** No | **{dotted-circle}** No | **{check-circle}** Yes | Remove parent epic from epic ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10556) in GitLab 12.1). |
| `/remove_time_spent` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Remove time spent. |
-| `/remove_zoom` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove Zoom meeting from this issue ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16609) in GitLab 12.4). |
+| `/remove_zoom` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Remove Zoom meeting from this issue. |
| `/reopen` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Reopen. |
| `/severity <severity>` | **{check-circle}** Yes | **{check-circle}** No | **{check-circle}** No | Set the severity. Options for `<severity>` are `S1` ... `S4`, `critical`, `high`, `medium`, `low`, `unknown`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/334045) in GitLab 14.2. |
| `/shrug <comment>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Append the comment with `¯\_(ツ)_/¯`. |
@@ -110,6 +110,7 @@ threads. Some quick actions might not be available to all subscription tiers.
| `/tableflip <comment>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Append the comment with `(╯°□°)╯︵ â”»â”â”»`. |
| `/target_branch <local branch name>` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Set target branch. |
| `/title <new title>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Change title. |
+| `/timeline <timeline comment> \| <date(YYYY-MM-DD)> <time(HH:MM)>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add a timeline event to this incident. For example, `/timeline DB load spiked \| 2022-09-07 09:30`. ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368721) in GitLab 15.4). |
| `/todo` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add a to-do item. |
| `/unapprove` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Unapprove the merge request. ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8103) in GitLab 14.3 |
| `/unassign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Remove specific assignees. |
@@ -121,7 +122,7 @@ threads. Some quick actions might not be available to all subscription tiers.
| `/unlock` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Unlock the discussions. |
| `/unsubscribe` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Unsubscribe from notifications. |
| `/weight <value>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set weight. Valid options for `<value>` include `0`, `1`, `2`, and so on. |
-| `/zoom <Zoom URL>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add Zoom meeting to this issue ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16609) in GitLab 12.4). |
+| `/zoom <Zoom URL>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add a Zoom meeting to this issue or incident. In [GitLab 15.3 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/230853) users on GitLab Premium can add a short description when [adding a Zoom link to an incident](../../operations/incident_management/linked_resources.md#link-zoom-meetings-from-an-incident).|
## Commit messages
diff --git a/doc/user/project/releases/index.md b/doc/user/project/releases/index.md
index d3456e086ce..87ea95524fe 100644
--- a/doc/user/project/releases/index.md
+++ b/doc/user/project/releases/index.md
@@ -57,8 +57,6 @@ switch between ascending or descending order, select **Sort order**.
## Create a release
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32812) in GitLab 12.9. Releases can be created directly in the GitLab UI.
-
You can create a release:
- [Using a job in your CI/CD pipeline](#creating-a-release-by-using-a-cicd-job).
@@ -68,16 +66,16 @@ You can create a release:
We recommend creating a release as one of the last steps in your CI/CD pipeline.
+### Create a release in the Releases page
+
Prerequisites:
- You must have at least the Developer role for a project. For more information, read
[Release permissions](#release-permissions).
-### Create a release in the Releases page
-
To create a release in the Releases page:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Releases** and select **New release**.
1. From the [**Tag name**](release_fields.md#tag-name) dropdown, either:
- Select an existing Git tag. Selecting an existing tag that is already associated with a release
@@ -99,7 +97,7 @@ To create a release in the Tags page, add release notes to either an existing or
To add release notes to a new Git tag:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Repository > Tags**.
1. Select **New tag**.
1. Optional. Enter a tag message in the **Message** text box.
@@ -109,7 +107,7 @@ To add release notes to a new Git tag:
To edit release notes of an existing Git tag:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Repository > Tags**.
1. Select **Edit release notes** (**{pencil}**).
1. In the **Release notes** text box, enter the release's description.
@@ -124,8 +122,11 @@ You can create a release directly as part of the GitLab CI/CD pipeline by using
The release is created only if the job processes without error. If the API returns an error during
release creation, the release job fails.
-For examples of how you can create a release of your application in the CI/CD pipeline,
-see [Release CI/CD examples](release_cicd_examples.md).
+Methods for creating a release using a CI/CD job include:
+
+- [Create a release when a Git tag is created](release_cicd_examples.md#create-a-release-when-a-git-tag-is-created).
+- [Create a release when a commit is merged to the default branch](release_cicd_examples.md#create-a-release-when-a-commit-is-merged-to-the-default-branch).
+- [Create release metadata in a custom script](release_cicd_examples.md#create-release-metadata-in-a-custom-script).
### Use a custom SSL CA certificate authority
@@ -232,7 +233,7 @@ Prerequisites:
To delete a release in the UI:
-1. On the top bar, select **Menu > Projects** and find your project.
+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. On the **Edit Release** page, select **Delete**.
@@ -312,6 +313,7 @@ deploy_to_production:
script: deploy_to_prod.sh
rules:
- if: $CI_DEPLOY_FREEZE == null
+ environment: production
```
To set a deploy freeze window in the UI, complete these steps:
diff --git a/doc/user/project/releases/release_cicd_examples.md b/doc/user/project/releases/release_cicd_examples.md
index f1d3e55a707..bfd83a20caf 100644
--- a/doc/user/project/releases/release_cicd_examples.md
+++ b/doc/user/project/releases/release_cicd_examples.md
@@ -12,9 +12,13 @@ CI/CD pipeline.
## Create a release when a Git tag is created
-In this CI/CD example, pushing a Git tag to the repository, or creating a Git tag in the UI triggers
-the release. You can use this method if you prefer to create the Git tag manually, and create a
-release as a result.
+In this CI/CD example, the release is triggered by one of the following events:
+
+- Pushing a Git tag to the repository.
+- Creating a Git tag in the UI.
+
+You can use this method if you prefer to create the Git tag manually, and create a release as a
+result.
NOTE:
Do not provide Release notes when you create the Git tag in the UI. Providing release notes
@@ -40,8 +44,8 @@ release_job:
## Create a release when a commit is merged to the default branch
-In this CI/CD example, merging a commit to the default branch triggers the pipeline. You can use
-this method if your release workflow does not create a tag manually.
+In this CI/CD example, the release is triggered when you merge a commit to the default branch. You
+can use this method if your release workflow does not create a tag manually.
Key points in the following _extract_ of an example `.gitlab-ci.yml` file:
@@ -69,16 +73,75 @@ Environment variables set in `before_script` or `script` are not available for e
in the same job. Read more about
[potentially making variables available for expanding](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/6400).
+## Create release metadata in a custom script
+
+In this CI/CD example the release preparation is split into separate jobs for greater flexibility:
+
+- The `prepare_job` job generates the release metadata. Any image can be used to run the job,
+ including a custom image. The generated metadata is stored in the variable file `variables.env`.
+ This metadata is [passed to the downstream job](../../../ci/variables/index.md#pass-an-environment-variable-to-another-job).
+- The `release_job` uses the content from the variables file to create a release, using the
+ metadata passed to it in the variables file. This job must use the
+ `registry.gitlab.com/gitlab-org/release-cli:latest` image because it contains the release CLI.
+
+```yaml
+prepare_job:
+ stage: prepare # This stage must run before the release stage
+ rules:
+ - if: $CI_COMMIT_TAG
+ when: never # Do not run this job when a tag is created manually
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Run this job when commits are pushed or merged to the default branch
+ script:
+ - echo "EXTRA_DESCRIPTION=some message" >> variables.env # Generate the EXTRA_DESCRIPTION and TAG environment variables
+ - echo "TAG=v$(cat VERSION)" >> variables.env # and append to the variables.env file
+ artifacts:
+ reports:
+ dotenv: variables.env # Use artifacts:reports:dotenv to expose the variables to other jobs
+
+release_job:
+ stage: release
+ image: registry.gitlab.com/gitlab-org/release-cli:latest
+ needs:
+ - job: prepare_job
+ artifacts: true
+ rules:
+ - if: $CI_COMMIT_TAG
+ when: never # Do not run this job when a tag is created manually
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Run this job when commits are pushed or merged to the default branch
+ script:
+ - echo "running release_job for $TAG"
+ release:
+ name: 'Release $TAG'
+ description: 'Created using the release-cli $EXTRA_DESCRIPTION' # $EXTRA_DESCRIPTION and the $TAG
+ tag_name: '$TAG' # variables must be defined elsewhere
+ ref: '$CI_COMMIT_SHA' # in the pipeline. For example, in the
+ milestones: # prepare_job
+ - 'm1'
+ - 'm2'
+ - 'm3'
+ released_at: '2020-07-15T08:00:00Z' # Optional, is auto generated if not defined, or can use a variable.
+ assets:
+ links:
+ - name: 'asset1'
+ url: 'https://example.com/assets/1'
+ - name: 'asset2'
+ url: 'https://example.com/assets/2'
+ filepath: '/pretty/url/1' # optional
+ link_type: 'other' # optional
+```
+
## Skip multiple pipelines when creating a release
Creating a release using a CI/CD job could potentially trigger multiple pipelines if the associated tag does not exist already. To understand how this might happen, consider the following workflows:
- Tag first, release second:
+
1. A tag is created via UI or pushed.
1. A tag pipeline is triggered, and runs `release` job.
1. A release is created.
- Release first, tag second:
+
1. A pipeline is triggered when commits are pushed or merged to default branch. The pipeline runs `release` job.
1. A release is created.
1. A tag is created.
diff --git a/doc/user/project/releases/release_cli.md b/doc/user/project/releases/release_cli.md
index 9e65ab4bc01..90363fca8b0 100644
--- a/doc/user/project/releases/release_cli.md
+++ b/doc/user/project/releases/release_cli.md
@@ -46,7 +46,7 @@ Once installed, [the `release` keyword](../../../ci/yaml/index.md#release) is av
Or from the GitLab Package Registry:
```shell
- curl --location --output /usr/local/bin/release-cli "https://gitlab.com/api/v4/projects/gitlab-org%2Frelease-cli/packages/generic/release-cli/latest/release-cli-darwin-amd64"
+ curl --location --output /usr/local/bin/release-cli "https://gitlab.com/api/v4/projects/gitlab-org%2Frelease-cli/packages/generic/release-cli/latest/release-cli-linux-amd64"
```
1. Give it permissions to execute:
diff --git a/doc/user/project/repository/branches/default.md b/doc/user/project/repository/branches/default.md
index 3083ca5da3c..d31c64f4640 100644
--- a/doc/user/project/repository/branches/default.md
+++ b/doc/user/project/repository/branches/default.md
@@ -64,7 +64,7 @@ GitLab [administrators](../../../permissions.md) of self-managed instances can
customize the initial branch for projects hosted on that instance. Individual
groups and subgroups can override this instance-wide setting for their projects.
-1. On the top bar, select **Menu > Admin**.
+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.
@@ -115,7 +115,7 @@ you must either:
Administrators of self-managed instances can customize the initial default branch protection for projects hosted on that instance. Individual
groups and subgroups can override this instance-wide setting for their projects.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Default branch**.
1. Select [**Initial default branch protection**](#protect-initial-default-branches).
@@ -132,7 +132,7 @@ can be overridden on a per-group basis by the group's owner. In
[GitLab Premium or higher](https://about.gitlab.com/pricing/), GitLab administrators can
disable this privilege for group owners, enforcing the instance-level protection rule:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Repository**.
1. Expand the **Default branch** section.
1. Clear the **Allow owners to manage default branch protection per group** checkbox.
@@ -152,7 +152,7 @@ can be overridden on a per-group basis by the group's owner. In
[enforce protection of initial default branches](#prevent-overrides-of-default-branch-protection)
which locks this setting for group owners.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Default branch**.
1. Select [**Initial default branch protection**](#protect-initial-default-branches).
diff --git a/doc/user/project/repository/branches/index.md b/doc/user/project/repository/branches/index.md
index 6da2e5fc7ee..5e5a42a061b 100644
--- a/doc/user/project/repository/branches/index.md
+++ b/doc/user/project/repository/branches/index.md
@@ -59,7 +59,8 @@ To compare branches in a repository:
![Delete merged branches](img/delete_merged_branches.png)
This feature allows merged branches to be deleted in bulk. Only branches that
-have been merged and [are not protected](../../protected_branches.md) are deleted as part of
+have been merged into the project's default branch and
+[are not protected](../../protected_branches.md) are deleted as part of
this operation.
It's particularly useful to clean up old branches that were not deleted
diff --git a/doc/user/project/repository/gpg_signed_commits/index.md b/doc/user/project/repository/gpg_signed_commits/index.md
index b2a6c8848ce..d307a6a8580 100644
--- a/doc/user/project/repository/gpg_signed_commits/index.md
+++ b/doc/user/project/repository/gpg_signed_commits/index.md
@@ -193,10 +193,10 @@ you can sign individual commits manually, or configure Git to default to signed
You can review commits for a merge request, or for an entire project:
1. To review commits for a project:
- 1. On the top bar, select **Menu > Projects** and find your 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 **Menu > Projects** and find your project.
+ 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**
diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md
index 8e1286548b9..4926cf3812e 100644
--- a/doc/user/project/repository/index.md
+++ b/doc/user/project/repository/index.md
@@ -49,7 +49,7 @@ to a branch in the repository. When you use the command line, you can commit mul
on their respective thread.
- **Cherry-pick a commit:**
In GitLab, you can
- [cherry-pick a commit](../merge_requests/cherry_pick_changes.md#cherry-pick-a-commit)
+ [cherry-pick a commit](../merge_requests/cherry_pick_changes.md#cherry-pick-a-single-commit)
from the UI.
- **Revert a commit:**
[Revert a commit](../merge_requests/revert_changes.md#revert-a-commit)
@@ -201,6 +201,14 @@ To render an OpenAPI file:
## Repository size
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/368150) in GitLab 15.3, feature flags `gitaly_revlist_for_repo_size` and `gitaly_catfile_repo_size` for alternative repository size calculations.
+
+FLAG:
+On self-managed GitLab, by default GitLab uses the `du -sk` command to determine the size of a repository. GitLab can use either
+`git-rev-list` (enabled with feature flag `gitaly_revlist_for_repo_size`) or `git-cat-file` (enabled with feature flag
+`gitaly_catfile_repo_size`) instead. To switch between different calculation methods, ask an administrator to
+[enable or disable](../../../administration/feature_flags.md) these feature flags.
+
The **Project information** page shows the size of all files in the repository. The size is
updated, at most, every 15 minutes. The file size includes repository files, artifacts, and LFS.
diff --git a/doc/user/project/repository/managing_large_repositories.md b/doc/user/project/repository/managing_large_repositories.md
index ba425ae3dc7..d2ca49c118c 100644
--- a/doc/user/project/repository/managing_large_repositories.md
+++ b/doc/user/project/repository/managing_large_repositories.md
@@ -10,13 +10,13 @@ description: "Documentation on large repositories."
GitLab, like any Git based system, is subject to similar performance restraints when it comes to large
repositories that size into the gigabytes.
-On this page we detail several best practices to improve performance with these large repositories on GitLab.
+In the following sections, we detail several best practices for improving performance with these large repositories on GitLab.
## Large File System (LFS)
-It's *strongly* recommended in any Git system that binary or blob files (for example, packages, audio, video, graphics, etc.) are stored as Large File Storage (LFS) objects. In such setup, the Objects are stored elsewhere, such as in Object Storage, and this can reduce the repository size significantly, thus improving performance.
+It's *strongly* recommended in any Git system that binary or blob files (for example, packages, audio, video, or graphics) are stored as Large File Storage (LFS) objects. In such setup, the Objects are stored elsewhere, such as in Object Storage, and this can reduce the repository size significantly, thus improving performance.
-To analyze if the repository has these sorts of objects, it's recommended to run [`git-sizer`](https://github.com/github/git-sizer) to get a detailed analysis. This tool shows in detail what makes up the repository as well as highlights any areas of concern.
+To analyze if the repository has these sorts of objects, it's recommended to run a tool like [`git-sizer`](https://github.com/github/git-sizer) to get a detailed analysis. These tools can show in detail what makes up the repository as well as highlights any areas of concern. If any large objects are found, it's then recommended removing them with tools such as [`git filter-repo`](reducing_the_repo_size_using_git.md).
Refer to the [Git LFS documentation for more information](../../../topics/git/lfs/index.md).
diff --git a/doc/user/project/repository/mirror/bidirectional.md b/doc/user/project/repository/mirror/bidirectional.md
index 340d7b48a47..793ca2a5f1f 100644
--- a/doc/user/project/repository/mirror/bidirectional.md
+++ b/doc/user/project/repository/mirror/bidirectional.md
@@ -45,7 +45,7 @@ and [pull](pull.md#pull-from-a-remote-repository) mirrors in the upstream GitLab
To create the webhook in the downstream instance:
1. Create a [personal access token](../../../profile/personal_access_tokens.md) with `API` scope.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Webhooks**.
1. Add the webhook **URL**, which (in this case) uses the
[Pull Mirror API](../../../../api/projects.md#start-the-pull-mirroring-process-for-a-project)
diff --git a/doc/user/project/repository/mirror/index.md b/doc/user/project/repository/mirror/index.md
index b08530c34b3..176461aeba7 100644
--- a/doc/user/project/repository/mirror/index.md
+++ b/doc/user/project/repository/mirror/index.md
@@ -41,7 +41,7 @@ Prerequisite:
- If your mirror connects with `ssh://`, the host key must be detectable on the server,
or you must have a local copy of the key.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Mirroring repositories**.
1. Enter a **Git repository URL**. For security reasons, the URL to the original
@@ -89,7 +89,7 @@ Prerequisite:
- You must have at least the Maintainer role for the project.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Mirroring repositories**.
1. Scroll to **Mirrored repositories** and identify the mirror to update.
@@ -141,7 +141,7 @@ When you mirror a repository and select the **SSH public key** as your
authentication method, GitLab generates a public key for you. The non-GitLab server
needs this key to establish trust with your GitLab repository. To copy your SSH public key:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Mirroring repositories**.
1. Scroll to **Mirrored repositories**.
@@ -249,7 +249,7 @@ If you receive this error after creating a new project using
Check if the repository owner is specified in the URL of your mirrored repository:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Mirroring repositories**.
1. If no repository owner is specified, delete and add the URL again in this format,
diff --git a/doc/user/project/repository/mirror/pull.md b/doc/user/project/repository/mirror/pull.md
index d0f2b9a8088..159580dcfa5 100644
--- a/doc/user/project/repository/mirror/pull.md
+++ b/doc/user/project/repository/mirror/pull.md
@@ -61,7 +61,7 @@ Prerequisite:
with the `repo` scope. If 2FA is enabled, this personal access
token serves as your GitHub password.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Mirroring repositories**.
1. Enter the **Git repository URL**. Include the username
diff --git a/doc/user/project/repository/mirror/push.md b/doc/user/project/repository/mirror/push.md
index c00ebf415c9..10bdc54ecee 100644
--- a/doc/user/project/repository/mirror/push.md
+++ b/doc/user/project/repository/mirror/push.md
@@ -33,7 +33,7 @@ section.
To set up push mirroring for an existing project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Mirroring repositories**.
1. Enter a repository URL.
diff --git a/doc/user/project/repository/push_rules.md b/doc/user/project/repository/push_rules.md
index 46a9585604e..90d2fdb89d0 100644
--- a/doc/user/project/repository/push_rules.md
+++ b/doc/user/project/repository/push_rules.md
@@ -36,7 +36,7 @@ Prerequisite:
To create global push rules:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Push Rules**.
1. Expand **Push rules**.
1. Set the rule you want.
@@ -48,7 +48,7 @@ The push rule of an individual project overrides the global push rule.
To override global push rules for a specific project, or to update the rules
for an existing project to match new global push rules:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Push rules**.
1. Set the rule you want.
diff --git a/doc/user/project/repository/reducing_the_repo_size_using_git.md b/doc/user/project/repository/reducing_the_repo_size_using_git.md
index 344c288b607..f209c7ef137 100644
--- a/doc/user/project/repository/reducing_the_repo_size_using_git.md
+++ b/doc/user/project/repository/reducing_the_repo_size_using_git.md
@@ -46,7 +46,7 @@ To purge files from a GitLab repository:
[`git-sizer`](https://github.com/github/git-sizer#getting-started)
using a supported package manager or from source.
-1. Generate a fresh
+1. Generate a fresh
[export from the project](../settings/import_export.md#export-a-project-and-its-data) and download it.
This project export contains a backup copy of your repository *and* refs
we can use to purge files from your repository.
diff --git a/doc/user/project/service_desk.md b/doc/user/project/service_desk.md
index f32035102bb..bafa2005fdf 100644
--- a/doc/user/project/service_desk.md
+++ b/doc/user/project/service_desk.md
@@ -150,7 +150,7 @@ The templates are inherited. For example, in a project, you can also access temp
To use a custom description template with Service Desk:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. [Create a description template](description_templates.md#create-an-issue-template).
1. On the left sidebar, select **Settings > General > Service Desk**.
1. From the dropdown list **Template to append to all Service Desk issues**, search or select your template.
@@ -164,7 +164,7 @@ this name in the `From` header. The default display name is `GitLab Support Bot`
To edit the custom email display name:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General > Service Desk**.
1. Enter a new name in **Email display name**.
1. Select **Save Changes**.
@@ -358,9 +358,23 @@ to everyone who can view the project.
Behind the scenes, Service Desk works by the special Support Bot user creating issues. This user
does not count toward the license limit count.
+### Moving a Service Desk issue
+
+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.
+
## Troubleshooting Service Desk
### Emails to Service Desk do not create issues
Your emails might be ignored because they contain one of the
[email headers that GitLab ignores](../../administration/incoming_email.md#rejected-headers).
+
+### Responses to a Service Desk issue do not generate emails
+
+Your issue might have been moved to a different project.
+Moved Service Desk issues do not retain email participants.
diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md
index 4eeb7c5ba83..375e4a62b86 100644
--- a/doc/user/project/settings/import_export.md
+++ b/doc/user/project/settings/import_export.md
@@ -43,7 +43,7 @@ Prerequisites:
To export a project and its data, follow these steps:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Advanced**.
1. Select **Export project**.
@@ -57,13 +57,34 @@ moved to your configured `uploads_directory`. Every 24 hours, a worker deletes t
### Items that are exported
-The following items are exported:
+The [`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/project/import_export.yml)
+file lists the items exported and imported when migrating projects using file exports. View this file in the branch
+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.
+
+Items that are exported include:
- Project and wiki repositories
- Project uploads
- Project configuration, excluding integrations
-- Issues with comments, merge requests with diffs and comments, labels, milestones, snippets, time
- tracking, and other project entities
+- Issues
+ - Issue comments
+ - Issue resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
+ - Issue resource milestone events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
+- Merge requests
+ - Merge request diffs
+ - Merge request comments
+ - Merge request resource state events ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/291983) in GitLab 15.4)
+ - Merge request multiple assignees ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3)
+ - Merge request reviewers ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3)
+ - Merge request approvers ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/339520) in GitLab 15.3)
+- Labels
+- Milestones
+- Snippets
+- Time tracking and other project entities
- Design Management files and data
- LFS objects
- Issue boards
@@ -73,7 +94,7 @@ The following items are exported:
- Group members are exported as project members, as long as the user has the Maintainer role in the
exported project's group, or is an administrator
-The following items are **not** exported:
+Items that are **not** exported include:
- [Child pipeline history](https://gitlab.com/gitlab-org/gitlab/-/issues/221088)
- Build traces and artifacts
@@ -82,20 +103,11 @@ The following items are **not** exported:
- Pipeline triggers
- Webhooks
- Any encrypted tokens
-- Merge Request Approvers and [the number of required approvals](https://gitlab.com/gitlab-org/gitlab/-/issues/221088)
+- [Number of required approvals](https://gitlab.com/gitlab-org/gitlab/-/issues/221088)
- Repository size limits
- Deploy keys allowed to push to protected branches
- Secure Files
-These content rules also apply to creating projects from templates on the
-[group](../../group/custom_project_templates.md)
-or [instance](../../admin_area/custom_project_templates.md)
-levels, because the same export and import mechanisms are used.
-
-NOTE:
-For more details on the specific data persisted in a project export, see the
-[`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/project/import_export.yml) file.
-
## Import a project and its data
> Default maximum import file size [changed](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50 MB to unlimited in GitLab 13.8.
@@ -166,7 +178,8 @@ Imported users can be mapped by their public email addresses on self-managed ins
- Public email addresses are not set by default. Users must [set it in their profiles](../../profile/index.md#set-your-public-email)
for mapping to work correctly.
- For contributions to be mapped correctly, users must be an existing member of the namespace,
- or they can be added as a member of the project. Otherwise, a supplementary comment is left to mention that the original author and the MRs, notes, or issues that are owned by the importer.
+ or they can be added as a member of the project. Otherwise, a supplementary comment is left to mention that the original
+ author and the merge requests, notes, or issues that are owned by the importer.
- Imported users are set as [direct members](../members/index.md)
in the imported project.
@@ -403,5 +416,5 @@ Error adding importer user to Project members.
Validation failed: User project bots cannot be added to other groups / projects
```
-To use [Import REST APIs](../../../api/project_import_export.md),
+To use [Import REST API](../../../api/project_import_export.md),
pass regular user account credentials such as [personal access tokens](../../profile/personal_access_tokens.md).
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index b973a0f56d1..5c2118e02cf 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -13,7 +13,7 @@ Use the **Settings** page to manage the configuration options in your [project](
You must have at least the Maintainer role to view project settings.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. To display all settings in a section, select **Expand**.
1. Optional. Use the search box to find a setting.
@@ -23,7 +23,7 @@ You must have at least the Maintainer role to view project settings.
Use the project general settings to edit your project details.
1. Sign in to GitLab with at least the Maintainer role.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. In the **Project name** text box, enter your project name.
1. In the **Project description** text box, enter your project description.
@@ -35,7 +35,7 @@ Use topics to categorize projects and find similar new projects.
To assign topics to a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings** > **General**.
1. In the **Topics** text box, enter the project topics. Popular topics are suggested as you type.
1. Select **Save changes**.
@@ -51,7 +51,7 @@ requirements or needs additional oversight. The label can optionally apply
Group owners can create, edit, and delete compliance frameworks:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings** > **General**.
1. Expand the **Compliance frameworks** section.
@@ -170,7 +170,7 @@ include: # Execute individual project's configuration (if project contains .git
When used to enforce scan execution, this feature has some overlap with [scan execution policies](../../application_security/policies/scan-execution-policies.md),
as we have not [unified the user experience for these two features](https://gitlab.com/groups/gitlab-org/-/epics/7312).
For details on the similarities and differences between these features, see
-[Enforce scan execution](../../application_security/#enforce-scan-execution).
+[Enforce scan execution](../../application_security/index.md#enforce-scan-execution).
### Ensure compliance jobs are always run
@@ -214,10 +214,10 @@ Compliance pipelines start on the run of _every_ pipeline in a relevant project.
triggers a child pipeline, the compliance pipeline runs first. This can trigger the parent pipeline, instead of the child pipeline.
Therefore, in projects with compliance frameworks, we recommend replacing
-[parent-child pipelines](../../../ci/pipelines/parent_child_pipelines.md) with the following:
+[parent-child pipelines](../../../ci/pipelines/downstream_pipelines.md#parent-child-pipelines) with the following:
- Direct [`include`](../../../ci/yaml/index.md#include) statements that provide the parent pipeline with child pipeline configuration.
-- Child pipelines placed in another project that are run using the [trigger API](../../../ci/triggers/) rather than the parent-child
+- Child pipelines placed in another project that are run using the [trigger API](../../../ci/triggers/index.md) rather than the parent-child
pipeline feature.
This alternative ensures the compliance pipeline does not re-start the parent pipeline.
@@ -226,7 +226,7 @@ This alternative ensures the compliance pipeline does not re-start the parent pi
To configure visibility, features, and permissions for a project:
-1. On the top bar, select **Menu > Projects** and find your 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 **Visibility, project features, permissions** section.
1. To change the project visibility, select the dropdown list. If you select to **Public**, you limit access to some features to **Only Project Members**.
@@ -241,17 +241,17 @@ 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/) functionality |
-| **Merge requests** | ✓ | Enables [merge request](../merge_requests/) functionality; also see [Merge request settings](#configure-merge-request-settings-for-a-project). |
+| **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/) for your Docker images. |
-| **Analytics** | ✓ | Enables [analytics](../../analytics/). |
+| **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/). |
+| **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). |
| **Operations** | ✓ | Control access to Operations-related features, including [Operations Dashboard](../../../operations/index.md), [Environments and Deployments](../../../ci/environments/index.md), [Feature Flags](../../../operations/feature_flags.md). |
@@ -286,7 +286,7 @@ In some environments, users can submit a [CVE identifier request](../../applicat
To disable the CVE identifier request option in issues in your project:
-1. On the top bar, select **Menu > Projects** and find your 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 **Visibility, project features, permissions** section.
1. Under **Issues**, turn off the **CVE ID requests in the issue sidebar** toggle.
@@ -298,7 +298,7 @@ Prerequisites:
- You must be an Owner of the project to disable email notifications related to the project.
-1. On the top bar, select **Menu > Projects** and find your 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 **Visibility, project features, permissions** section.
1. Clear the **Disable email notifications** checkbox.
@@ -339,7 +339,7 @@ other features are read-only. Archived projects are also hidden from project lis
To archive a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Advanced**.
1. In the **Archive project** section, select **Archive project**.
@@ -355,7 +355,7 @@ Prerequisites:
- To unarchive a project, you must be an administrator or a project Owner.
1. Find the archived project.
- 1. On the top bar, select **Menu > Project**.
+ 1. On the top bar, select **Main menu > Projects > View all projects**.
1. Select **Explore projects**.
1. In the **Sort projects** dropdown list, select **Show archived projects**.
1. In the **Filter by name** field, enter the project name.
@@ -380,7 +380,7 @@ When you change the repository path, users may experience issues if they push to
To rename a repository:
-1. On the top bar, select **Menu > Projects** and find your 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.
@@ -402,7 +402,7 @@ Prerequisites:
To transfer a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Advanced**.
1. Under **Transfer project**, choose the namespace to transfer the project to.
@@ -420,7 +420,7 @@ to move any project to any namespace.
When you transfer a project from a namespace licensed for GitLab SaaS Premium or Ultimate to GitLab Free, the following paid feature data is deleted:
- [Project access tokens](../../../user/project/settings/project_access_tokens.md) are revoked
-- [Pipeline subscriptions](../../../ci/pipelines/multi_project_pipelines.md#trigger-a-pipeline-when-an-upstream-project-is-rebuilt)
+- [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.
## Delete a project
@@ -433,7 +433,7 @@ Prerequisite:
To delete a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Advanced**.
1. In the "Delete project" section, select **Delete project**.
@@ -472,7 +472,7 @@ Prerequisites:
To immediately delete a project marked for deletion:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Advanced**.
1. In the "Permanently delete project" section, select **Delete project**.
@@ -504,7 +504,7 @@ To restore the fork relationship, [use the API](../../../api/projects.md#create-
To remove a fork relationship:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Advanced**.
1. In the **Remove fork relationship** section, select **Remove fork relationship**.
diff --git a/doc/user/project/settings/project_access_tokens.md b/doc/user/project/settings/project_access_tokens.md
index 77a53874777..c9c5efce9b1 100644
--- a/doc/user/project/settings/project_access_tokens.md
+++ b/doc/user/project/settings/project_access_tokens.md
@@ -27,6 +27,13 @@ and [personal access tokens](../../profile/personal_access_tokens.md).
In self-managed instances, project access tokens are subject to the same [maximum lifetime limits](../../admin_area/settings/account_and_limit_settings.md#limit-the-lifetime-of-access-tokens) as personal access tokens if the limit is set.
+WARNING:
+The ability to create project access tokens without expiry was
+[deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/369122) in GitLab 15.4 and is planned for removal in GitLab
+16.0. When this ability is removed, existing project access tokens without an expiry are planned to have an expiry added.
+The automatic adding of an expiry occurs on GitLab.com during the 16.0 milestone. The automatic adding of an expiry
+occurs on self-managed instances when they are upgraded to GitLab 16.0. This change is a breaking change.
+
You can use project access tokens:
- On GitLab SaaS if you have the Premium license tier or higher. Project access tokens are not available with a [trial license](https://about.gitlab.com/free-trial/).
@@ -43,11 +50,12 @@ configured for personal access tokens.
## Create a project access token
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89114) in GitLab 15.1, Owners can select Owner role for project access tokens.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89114) in GitLab 15.1, Owners can select Owner role for project access tokens.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348660) in GitLab 15.3, default expiration of 30 days and default role of Guest is populated in the UI.
To create a project access token:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Access Tokens**.
1. Enter a name. The token name is visible to any user with permissions to view the project.
1. Optional. Enter an expiry date for the token. The token expires on that date at midnight UTC. An instance-wide [maximum lifetime](../../admin_area/settings/account_and_limit_settings.md#limit-the-lifetime-of-access-tokens) setting can limit the maximum allowable lifetime in self-managed instances.
@@ -62,7 +70,7 @@ A project access token is displayed. Save the project access token somewhere saf
To revoke a project access token:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Access Tokens**.
1. Next to the project access token to revoke, select **Revoke**.
@@ -85,7 +93,7 @@ The scope determines the actions you can perform when you authenticate with a pr
To enable or disable project access token creation for all projects in a top-level group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Permissions and group features**.
1. Under **Permissions**, turn on or off **Allow project and group access token creation**.
diff --git a/doc/user/project/time_tracking.md b/doc/user/project/time_tracking.md
index 971ecf66a3c..522ec962e53 100644
--- a/doc/user/project/time_tracking.md
+++ b/doc/user/project/time_tracking.md
@@ -170,7 +170,7 @@ The following time units are available:
In GitLab self-managed instances, you can limit the display of time units to hours.
To do so:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Localization**.
1. Under **Time tracking**, select the **Limit display of time tracking units to hours** checkbox.
diff --git a/doc/user/project/web_ide/index.md b/doc/user/project/web_ide/index.md
index 5a4e300a210..2a197c733cf 100644
--- a/doc/user/project/web_ide/index.md
+++ b/doc/user/project/web_ide/index.md
@@ -260,7 +260,7 @@ a `main` entry point inside the Web IDE.
Live Preview is enabled for all projects on GitLab.com. If you are an administrator
of a self-managed GitLab instance, and you want to enable Live Preview:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Scroll to **Web IDE** and select **Expand**:
![Administrator Live Preview setting](img/admin_live_preview_v13_0.png)
diff --git a/doc/user/project/wiki/group.md b/doc/user/project/wiki/group.md
index a3ba5789d39..03838a62d59 100644
--- a/doc/user/project/wiki/group.md
+++ b/doc/user/project/wiki/group.md
@@ -29,7 +29,7 @@ and higher can edit group wikis. Group wiki repositories can be moved using the
To access a group wiki:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. To display the wiki, either:
- On the left sidebar, select **Wiki**.
- On any page in the project, use the <kbd>g</kbd> + <kbd>w</kbd>
@@ -69,7 +69,7 @@ can enable or disable a group wiki through the group settings.
To open group settings:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Permissions and group features**.
1. Scroll to **Wiki** and select one of these options:
diff --git a/doc/user/project/wiki/index.md b/doc/user/project/wiki/index.md
index c7f675417bb..b8924c33b13 100644
--- a/doc/user/project/wiki/index.md
+++ b/doc/user/project/wiki/index.md
@@ -31,7 +31,7 @@ with sibling pages listed in alphabetical order. To view a list of all pages, se
To access a project wiki:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. To display the wiki, either:
- On the left sidebar, select **Wiki**.
- On any page in the project, use the <kbd>g</kbd> + <kbd>w</kbd>
@@ -61,7 +61,7 @@ When a wiki is created, it is empty. On your first visit, you can create the
home page users see when viewing the wiki. This page requires a specific title
to be used as your wiki's home page. To create it:
-1. On the top bar, select **Menu**.
+1. On the top bar, select **Main menu**.
- For project wikis, select **Projects** and find your project.
- For group wikis, select **Groups** and find your group.
1. On the left sidebar, select **Wiki**.
@@ -79,7 +79,7 @@ to be used as your wiki's home page. To create it:
Users with at least the Developer role can create new wiki pages:
-1. On the top bar, select **Menu**.
+1. On the top bar, select **Main menu**.
- For project wikis, select **Projects** and find your project.
- For group wikis, select **Groups** and find your group.
1. On the left sidebar, select **Wiki**.
@@ -142,7 +142,7 @@ may not be able to check out the wiki locally afterward.
You need at least the Developer role to edit a wiki page:
-1. On the top bar, select **Menu**.
+1. On the top bar, select **Main menu**.
- For project wikis, select **Projects** and find your project.
- For group wikis, select **Groups** and find your group.
1. On the left sidebar, select **Wiki**.
@@ -161,7 +161,7 @@ For an example, read [Table of contents](../../markdown.md#table-of-contents).
You need at least the Developer role to delete a wiki page:
-1. On the top bar, select **Menu**.
+1. On the top bar, select **Main menu**.
- For project wikis, select **Projects** and find your project.
- For group wikis, select **Groups** and find your group.
1. On the left sidebar, select **Wiki**.
@@ -174,7 +174,7 @@ You need at least the Developer role to delete a wiki page:
You need at least the Developer role to move a wiki page:
-1. On the top bar, select **Menu**.
+1. On the top bar, select **Main menu**.
- For project wikis, select **Projects** and find your project.
- For group wikis, select **Groups** and find your group.
1. On the left sidebar, select **Wiki**.
@@ -200,7 +200,7 @@ The history page shows:
To view the changes for a wiki page:
-1. On the top bar, select **Menu**.
+1. On the top bar, select **Main menu**.
- For project wikis, select **Projects** and find your project.
- For group wikis, select **Groups** and find your group.
1. On the left sidebar, select **Wiki**.
@@ -213,7 +213,7 @@ To view the changes for a wiki page:
You can see the changes made in a version of a wiki page, similar to versioned diff file views:
-1. On the top bar, select **Menu**.
+1. On the top bar, select **Main menu**.
- For project wikis, select **Projects** and find your project.
- For group wikis, select **Groups** and find your group.
1. On the left sidebar, select **Wiki**.
@@ -246,7 +246,7 @@ You need at least the Developer role to customize the wiki
navigation sidebar. This process creates a wiki page named `_sidebar` which fully
replaces the default sidebar navigation:
-1. On the top bar, select **Menu**.
+1. On the top bar, select **Main menu**.
- For project wikis, select **Projects** and find your project.
- For group wikis, select **Groups** and find your group.
1. On the left sidebar, select **Wiki**.
@@ -284,7 +284,7 @@ You can disable group wikis from the [group settings](group.md#configure-group-w
To add a link to an external wiki from a project's left sidebar:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **External wiki**.
1. Add the URL to your external wiki.
@@ -300,7 +300,7 @@ To hide the internal wiki from the sidebar, [disable the project's wiki](#disabl
To hide the link to an external wiki:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Integrations**.
1. Select **External wiki**.
1. In the **Enable integration** section, clear the **Active** checkbox.
@@ -310,7 +310,7 @@ To hide the link to an external wiki:
To disable a project's internal wiki:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. Go to your project and select **Settings > General**.
1. Expand **Visibility, project features, permissions**.
1. Scroll down to find **Wiki** and toggle it off (in gray).
diff --git a/doc/user/project/working_with_projects.md b/doc/user/project/working_with_projects.md
index 2501fa8b45c..e58bf5aa557 100644
--- a/doc/user/project/working_with_projects.md
+++ b/doc/user/project/working_with_projects.md
@@ -11,22 +11,9 @@ code are saved in projects, and most features are in the scope of projects.
## View projects
-To explore projects:
+To view projects, on the top bar, select **Main menu > Projects > View all projects**.
-1. On the top bar, select **Menu > Projects**.
-1. Select **Explore projects**.
-
-The **Projects** page shows a list of projects, sorted by last updated date.
-
-- To view projects with the most [stars](#star-a-project), select **Most stars**.
-- To view projects with the largest number of comments in the past month, select **Trending**.
-
-NOTE:
-The **Explore projects** tab is visible to unauthenticated users unless the
-[**Public** visibility level](../admin_area/settings/visibility_and_access_controls.md#restrict-visibility-levels)
-is restricted. Then the tab is visible only to signed-in users.
-
-### Who can view the **Projects** page
+### Who can view the Projects page
When you select a project, the project landing page shows the project contents.
@@ -53,11 +40,16 @@ visit the `/projects/:id` URL in your browser or other tool accessing the projec
To explore project topics:
-1. On the top bar, select **Menu > Projects**.
-1. Select **Explore topics**.
+1. On the top bar, select **Main menu > Projects > View all projects**.
+1. Select the **Explore topics** tab.
+1. To view projects associated with a topic, select a topic.
+
+The **Explore topics** tab shows a list of topics sorted by the number of associated projects.
-The **Projects** page shows list of topics sorted by the number of associated projects.
-To view projects associated with a topic, select a topic from the list.
+NOTE:
+The **Explore projects** tab is visible to unauthenticated users unless the
+[**Public** visibility level](../admin_area/settings/visibility_and_access_controls.md#restrict-visibility-levels)
+is restricted. Then the tab is visible only to signed-in users.
You can assign topics to a project on the [Project Settings page](settings/index.md#assign-topics-to-a-project).
@@ -68,8 +60,9 @@ If you're an instance administrator, you can administer all project topics from
To create a project in GitLab:
-1. On the top bar, select **Menu > Project > Create new project**.
-1. On the **Create new project** page, choose if you want to:
+1. On the top bar, select **Main menu > Projects > View all projects**.
+1. On the right of the page, select **New project**.
+1. Select an option:
- Create a [blank project](#create-a-blank-project).
- Create a project from a:
- [built-in template](#create-a-project-from-a-built-in-template).
@@ -88,7 +81,8 @@ To create a project in GitLab:
To create a blank project:
-1. On the top bar, select **Menu > Projects > Create new project**.
+1. On the top bar, select **Main menu > Projects > View all projects**.
+1. On the right of the page, select **New project**.
1. Select **Create blank project**.
1. Enter the project details:
- In the **Project name** field, enter the name of your project. You cannot use special characters at
@@ -119,7 +113,8 @@ Anyone can [contribute a built-in template](../../development/project_templates.
To create a project from a built-in template:
-1. On the top bar, select **Menu > Projects > Create new project**.
+1. On the top bar, select **Main menu > Projects > View all projects**.
+1. On the right of the page, select **New project**.
1. Select **Create from template**.
1. Select the **Built-in** tab.
1. From the list of templates:
@@ -145,7 +140,8 @@ Custom project templates are available at:
- The [instance-level](../../user/admin_area/custom_project_templates.md)
- The [group-level](../../user/group/custom_project_templates.md)
-1. On the top bar, select **Menu > Projects > Create new project**.
+1. On the top bar, select **Main menu > Projects > View all projects**.
+1. On the right of the page, select **New project**.
1. Select **Create from template**.
1. Select the **Instance** or **Group** tab.
1. From the list of templates:
@@ -171,7 +167,8 @@ HIPAA Audit Protocol published by the U.S Department of Health and Human Service
To create a project from the HIPAA Audit Protocol template:
-1. On the top bar, select **Menu > Projects > Create new project**.
+1. On the top bar, select **Main menu > Projects > View all projects**.
+1. On the right of the page, select **New project**.
1. Select **Create from template**.
1. Select the **Built-in** tab.
1. Locate the **HIPAA Audit Protocol** template:
@@ -210,9 +207,7 @@ Prerequisites:
[added to your GitLab account](../ssh.md#add-an-ssh-key-to-your-gitlab-account).
- You must have permission to add new projects to a namespace. To check if you have permission:
- 1. On the top bar, select **Menu > Projects**.
- 1. Select **Groups**.
- 1. Select a group.
+ 1. On the top bar, select **Main menu > Groups** and find your group.
1. Confirm that **New project** is visible in the upper right
corner. Contact your GitLab
administrator if you require permission.
@@ -258,15 +253,13 @@ You can add a star to projects you use frequently to make them easier to find.
To add a star to a project:
-1. On the top bar, select **Menu > Projects**.
-1. Select **Your projects** or **Explore projects**.
-1. Select a project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. In the upper right corner of the page, select **Star**.
## View starred projects
-1. On the top bar, select **Menu > Projects**.
-1. Select **Starred projects**.
+1. On the top bar, select **Main menu > Projects > View all projects**.
+1. Select the **Starred projects** tab.
1. GitLab displays information about your starred projects, including:
- Project description, including name, description, and icon.
@@ -284,8 +277,8 @@ called `my-project` under your username, the project is created at `https://gitl
To view your personal projects:
-1. On the top bar, select **Menu > Projects > Your Projects**.
-1. Under **Your projects**, select **Personal**.
+1. On the top bar, select **Main menu > Projects > View all projects**.
+1. In the **Your projects** tab, select **Personal**.
## Delete a project
@@ -294,9 +287,7 @@ you can [enable delayed project removal](../group/manage.md#enable-delayed-proje
To delete a project:
-1. On the top bar, select **Menu > Projects**.
-1. Select **Your projects** or **Explore projects**.
-1. Select a project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. Select **Settings > General**.
1. Expand the **Advanced** section.
1. Scroll down to the **Delete project** section.
@@ -315,7 +306,7 @@ projects within that group are not deleted immediately, but only after a delay.
To view a list of all projects that are pending deletion:
-1. On the top bar, select **Menu > Projects > Explore projects**.
+1. On the top bar, select **Main menu > Projects > View all projects**.
1. Based on your GitLab version:
- GitLab 14.6 and later: select the **Pending deletion** tab.
- GitLab 14.5 and earlier: select the **Deleted projects** tab.
@@ -330,9 +321,7 @@ Each project in the list shows:
To view the activity of a project:
-1. On the top bar, select **Menu > Projects**.
-1. Select **Your projects** or **Explore projects**.
-1. Select a project.
+1. On the top bar, select **Main menu > Projects** and find your project..
1. On the left sidebar, select **Project information > Activity**.
1. Select a tab to view the type of project activity.
@@ -340,7 +329,7 @@ To view the activity of a project:
You can search through your projects.
-1. On the top bar, select **Menu**.
+1. On the top bar, select **Main menu**.
1. In **Search your projects**, type the project name.
GitLab filters as you type.
@@ -366,9 +355,7 @@ member and cannot contribute.
To leave a project:
-1. On the top bar, select **Menu > Projects**.
-1. Select **Your projects** or **Explore projects**.
-1. Select a project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. Select **Leave project**. The **Leave project** option only displays
on the project dashboard when a project is part of a group under a
[group namespace](../namespace/index.md).
diff --git a/doc/user/public_access.md b/doc/user/public_access.md
index d821c1abe47..3accb0f5e3d 100644
--- a/doc/user/public_access.md
+++ b/doc/user/public_access.md
@@ -24,6 +24,8 @@ Public projects can be cloned **without any** authentication over HTTPS.
They are listed in the public access directory (`/public`) for all users.
+Public groups can have public, internal, or private subgroups.
+
**Any signed-in user** has the Guest role on the repository.
NOTE:
@@ -38,6 +40,8 @@ Internal projects can be cloned by any signed-in user except
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
Guest role on the repository.
@@ -53,13 +57,15 @@ Private projects can only be cloned and viewed by project members (except for gu
They appear in the public access directory (`/public`) for project members only.
+Private groups can only have private subgroups.
+
## Change project visibility
Prerequisite:
- You must have the Owner role for a project.
-1. On the top bar, select **Menu > Projects** and find your 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. Change **Project visibility** to either **Private**, **Internal**, or **Public**.
@@ -71,9 +77,9 @@ Prerequisite:
- You must have the Owner role for a group.
- Subgroups and projects must already have visibility settings that are at least as
- restrictive as the new setting for the group.
+ restrictive as the new setting of the parent group.
-1. On the top bar, select **Menu > Groups** and find your project.
+1. On the top bar, select **Main menu > Groups** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Naming, visibility**.
1. Under **Visibility level** select either **Private**, **Internal**, or **Public**.
diff --git a/doc/user/search/advanced_search.md b/doc/user/search/advanced_search.md
index 90d6a15901a..bec1a8f3d4c 100644
--- a/doc/user/search/advanced_search.md
+++ b/doc/user/search/advanced_search.md
@@ -5,60 +5,44 @@ info: "To determine the technical writer assigned to the Stage/Group associated
type: reference
---
-# GitLab Advanced Search **(PREMIUM)**
+# Advanced Search **(PREMIUM)**
> Moved to GitLab Premium in 13.9.
-Advanced Search uses Elasticsearch for faster, more advanced search across the entire
-GitLab instance.
+You can use Advanced Search for faster, more efficient search across the entire GitLab
+instance. Advanced Search is based on Elasticsearch, a purpose-built full-text search
+engine you can horizontally scale to get results in up to a second in most cases.
-Use Advanced Search when searching in:
+You can find code you want to update in all projects at once to save
+maintenance time and promote innersourcing.
+
+You can use Advanced Search in:
- Projects
- Issues
- Merge requests
- Milestones
- Users
-- Epics (when searching in a group only)
+- Epics (in groups only)
- Code
- Comments
- Commits
-- Wiki (except [group wikis](../project/wiki/group.md))
-
-Advanced Search can be useful in various scenarios:
-
-- **Faster searches:**
- Advanced Search is based on Elasticsearch, which is a purpose-built full
- text search engine that can be horizontally scaled so that it can provide
- search results in 1-2 seconds in most cases.
-- **Code Maintenance:**
- Finding all the code that needs to be updated at once across an entire
- instance can save time spent maintaining code.
- This is especially helpful for organizations with more than 10 active projects.
- This can also help build confidence is code refactoring to identify unknown impacts.
-- **Promote innersourcing:**
- Your company may consist of many different developer teams each of which has
- their own group where the various projects are hosted. Some of your applications
- may be connected to each other, so your developers need to instantly search
- throughout the GitLab instance and find the code they search for.
-
-## Configuring Advanced Search
-
-For self-managed GitLab instances, an administrator must
-[configure Advanced Search](../../integration/advanced_search/elasticsearch.md).
+- Project wikis (not [group wikis](../project/wiki/group.md))
-On GitLab.com, Advanced Search is enabled.
+## Configure Advanced Search
-## Advanced Search syntax
+- 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).
-See the documentation on [Advanced Search syntax](global_search/advanced_search_syntax.md).
+## Syntax
-## Search by issue or merge request ID
+See [Advanced Search syntax](global_search/advanced_search_syntax.md) for more information.
-You can search a specific issue or merge request by its ID with a special prefix.
+## Search by ID
-- To search by issue ID, use prefix `#` followed by issue ID. For example, [#23456](https://gitlab.com/search?snippets=&scope=issues&repository_ref=&search=%2323456&group_id=9970&project_id=278964)
-- To search by merge request ID, use prefix `!` followed by merge request ID. For example [!23456](https://gitlab.com/search?snippets=&scope=merge_requests&repository_ref=&search=%2123456&group_id=9970&project_id=278964)
+- To search by issue ID, use the `#` prefix followed by the issue ID (for example, [`#23456`](https://gitlab.com/search?snippets=&scope=issues&repository_ref=&search=%2323456&group_id=9970&project_id=278964)).
+- To search by merge request ID, use the `!` prefix followed by the merge request ID (for example, [`!23456`](https://gitlab.com/search?snippets=&scope=merge_requests&repository_ref=&search=%2123456&group_id=9970&project_id=278964)).
## Global search scopes **(FREE SELF)**
diff --git a/doc/user/search/index.md b/doc/user/search/index.md
index 76a85b55585..41eff28b088 100644
--- a/doc/user/search/index.md
+++ b/doc/user/search/index.md
@@ -44,7 +44,7 @@ search, or choose a specific group or project.
To search through code or other documents in a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the top bar, in the search field, type the string you want to search for.
1. Press **Enter**.
@@ -68,7 +68,7 @@ where the results were found.
You can search for a commit SHA.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the top bar, in the search field, type the SHA.
If a single result is returned, GitLab redirects to the commit result
diff --git a/doc/user/snippets.md b/doc/user/snippets.md
index cab18b221c1..f4683414dea 100644
--- a/doc/user/snippets.md
+++ b/doc/user/snippets.md
@@ -23,9 +23,15 @@ GitLab provides two types of snippets:
- **Personal snippets**: Created independent of any project.
You can set a [visibility level](public_access.md)
- for your snippet: public, internal, or private.
+ for your snippet: public or private.
- **Project snippets**: Always related to a specific project.
- Project snippets can be visible publicly or to only group members.
+ Project snippets can be visible publicly, or to only project members.
+
+NOTE:
+From July 2019, the `Internal` visibility setting is disabled for new projects, groups,
+and snippets on GitLab.com. Existing snippets using the `Internal`
+visibility setting keep this setting. You can read more about the change in the
+[relevant issue](https://gitlab.com/gitlab-org/gitlab/-/issues/12388).
## Create snippets
@@ -61,10 +67,10 @@ In GitLab versions 13.0 and later, snippets are [versioned by default](#versione
To discover all snippets visible to you in GitLab, you can:
- **View all snippets visible to you**: On the top bar of your GitLab
- instance, select **Menu > Snippets** to view your snippets dashboard.
+ instance, select **Main menu > Snippets** to view your snippets dashboard.
- **Visit [GitLab snippets](https://gitlab.com/dashboard/snippets)** for your snippets on GitLab.com.
- **Explore all public snippets**: On the top bar of your GitLab
- instance, select **Menu > Snippets** and select **Explore snippets** to view
+ instance, select **Main menu > Snippets** and select **Explore snippets** to view
[all public snippets](https://gitlab.com/explore/snippets).
- **View a project's snippets**: In your project,
go to **Snippets**.
@@ -219,7 +225,7 @@ Prerequisites:
To do this task:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Snippets**.
1. Select the snippet you want to report as spam.
1. Select **Submit as spam**.
diff --git a/doc/user/ssh.md b/doc/user/ssh.md
index 5667890757a..913140d2a01 100644
--- a/doc/user/ssh.md
+++ b/doc/user/ssh.md
@@ -254,6 +254,8 @@ A public and private key are generated.
## Add an SSH key to your GitLab account
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/271239) in GitLab 15.4, default expiration date suggested in UI.
+
To use SSH with GitLab, copy your public key to your GitLab account:
1. Copy the contents of your public key file. You can do this manually or use a script.
@@ -289,7 +291,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. In the **Expires at** box, select an expiration date. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/36243) in GitLab 12.9.)
+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
you from using the key. Administrators can view expiration dates and use them for
@@ -419,6 +421,9 @@ as both have a different home directory:
You can either copy over the `.ssh/` directory to use the same key, or generate a key in each environment.
+If you're running Windows 11 and using [OpenSSH for Windows](https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_overview), ensure the `HOME`
+environment variable is set correctly. Otherwise, your private SSH key might not be found.
+
Alternative tools include:
- [Cygwin](https://www.cygwin.com)
@@ -468,6 +473,7 @@ This indicates that something is wrong with your SSH setup.
- Try to manually register your private SSH key by using `ssh-agent`.
- Try to debug the connection by running `ssh -Tv git@example.com`.
Replace `example.com` with your GitLab URL.
+- Ensure you followed all the instructions in [Use SSH on Microsoft Windows](#use-ssh-on-microsoft-windows).
### `Could not resolve hostname` error
diff --git a/doc/user/tasks.md b/doc/user/tasks.md
index 16e5c85a354..15abc77ad47 100644
--- a/doc/user/tasks.md
+++ b/doc/user/tasks.md
@@ -10,13 +10,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
> - [Creating, editing, and deleting tasks](https://gitlab.com/groups/gitlab-org/-/epics/7169) introduced in GitLab 15.0.
> - [Enabled on GitLab.com and self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/334812) in GitLab 15.3.
-WARNING:
-Tasks are in [**Alpha**](../policy/alpha-beta-support.md#alpha-features).
-
-The following list are the known limitations:
+Known limitation:
- [Tasks currently cannot be accessed via REST API.](https://gitlab.com/gitlab-org/gitlab/-/issues/368055)
-- An issue's tasks can only currently be accessed via a reference within a description, comment, or direct URL (`.../-/work_items/[global_id]`).
For the latest updates, check the [Tasks Roadmap](https://gitlab.com/groups/gitlab-org/-/epics/7103).
@@ -40,7 +36,7 @@ to work items and adding custom work item types, visit
## View tasks
-View tasks in issues, in the **Child items** section.
+View tasks in issues, in the **Tasks** section.
You can also [filter the list of issues](project/issues/managing_issues.md#filter-the-list-of-issues)
for `Type = task`.
@@ -53,7 +49,7 @@ Prerequisites:
To create a task:
-1. In an issue description, in the **Child items** section, select **Add a task**.
+1. In the issue description, in the **Tasks** section, select **Add**.
1. Enter the task title.
1. Select **Create task**.
@@ -65,7 +61,7 @@ Prerequisites:
To edit a task:
-1. In the issue description, in the **Child items** section, select the task you want to edit.
+1. In the issue description, in the **Tasks** section, select the task you want to edit.
The task window opens.
1. Optional. To edit the title, select it and make your changes.
1. Optional. To edit the description, select the edit icon (**{pencil}**), make your changes, and
@@ -83,7 +79,7 @@ It's not possible to connect them again.
To remove a task from an issue:
-1. In the issue description, in the **Child items** section, next to the task you want to remove, select the options menu (**{ellipsis_v}**).
+1. In the issue description, in the **Tasks** section, next to the task you want to remove, select the options menu (**{ellipsis_v}**).
1. Select **Remove task**.
## Delete a task
@@ -96,6 +92,75 @@ Prerequisites:
To delete a task:
-1. In the issue description, in the **Child items** section, select the task you want to edit.
+1. In the issue description, in the **Tasks** section, select the task you want to edit.
1. In the task window, in the options menu (**{ellipsis_v}**), select **Delete task**.
1. Select **OK**.
+
+## Assign users to a task
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/334810) in GitLab 15.4.
+
+To show who is responsible for a task, you can assign users to it.
+
+Users on GitLab Free can assign one user per task.
+Users on GitLab Premium and higher can assign multiple users to a single task.
+See also [multiple assignees for issues](project/issues/multiple_assignees_for_issues.md).
+
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
+To change the assignee on a task:
+
+1. In the issue description, in the **Tasks** section, select the title of the task you want to edit.
+ The task window opens.
+1. Next to **Assignees**, select **Add assignees**.
+1. From the dropdown list, select the user(s) to add as an assignee.
+1. Select any area outside the dropdown list.
+
+## Set a start and due date
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/365399) in GitLab 15.4.
+
+You can set a [start and due date](project/issues/due_dates.md) on a task.
+
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
+You can set start and due dates on a task to show when work should begin and end.
+
+To set a due date:
+
+1. In the issue description, in the **Tasks** section, select the title of the task you want to edit.
+ The task window opens.
+1. If the task already has a due date next to **Due date**, select it. Otherwise, select **Add due date**.
+1. In the date picker, select the desired due date.
+
+To set a start date:
+
+1. In the issue description, in the **Tasks** section, select the title of the task you want to edit.
+ The task window opens.
+1. If the task already has a start date next to **Start date**, select it. Otherwise, select **Add start date**.
+1. In the date picker, select the desired due date.
+
+ The due date must be the same or later than the start date.
+ If you select a start date to be later than the due date, the due date is then changed to the same day.
+
+## Set task weight **(PREMIUM)**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/362550) in GitLab 15.3.
+
+Prerequisites:
+
+- You must have at least the Reporter role for the project.
+
+You can set weight on each task to show how much work it needs.
+This value is visible only when you view a task.
+
+To set issue weight of a task:
+
+1. In the issue description, in the **Tasks** section, select the title of the task you want to edit.
+ The task window opens.
+1. Next to **Weight**, enter a whole, positive number.
+1. Select the close icon (**{close}**).
diff --git a/doc/user/usage_quotas.md b/doc/user/usage_quotas.md
index 5d78b4bb795..828c9a3c4b0 100644
--- a/doc/user/usage_quotas.md
+++ b/doc/user/usage_quotas.md
@@ -32,36 +32,36 @@ To prevent exceeding the namespace storage quota, you can:
1. Reduce storage consumption by following the suggestions in the [Manage Your Storage Usage](#manage-your-storage-usage) section of this page.
1. Apply for [GitLab for Education](https://about.gitlab.com/solutions/education/join/), [GitLab for Open Source](https://about.gitlab.com/solutions/open-source/join/), or [GitLab for Startups](https://about.gitlab.com/solutions/startups/) if you meet the eligibility requirements.
-1. Consider using a [self-managed instance](../subscriptions/self_managed/) of GitLab which does not have these limits on the free tier.
+1. Consider using a [self-managed instance](../subscriptions/self_managed/index.md) of GitLab which does not have these limits on the free tier.
1. [Purchase additional storage](../subscriptions/gitlab_com/index.md#purchase-more-storage-and-transfer) units at $60/year for 10GB of storage.
1. [Start a trial](https://about.gitlab.com/free-trial/) or [upgrade to GitLab Premium or Ultimate](https://about.gitlab.com/pricing) which include higher limits and features that enable growing teams to ship faster without sacrificing on quality.
1. [Talk to an expert](https://page.gitlab.com/usage_limits_help.html) to learn more about your options and ask questions.
### Namespace storage limit enforcement schedule
-Storage limits for GitLab SaaS Free tier namespaces will not be enforced prior to 2022-10-19. Storage limits for GitLab SaaS Paid tier namespaces will not be enforced for prior to 2023-02-15.
+Storage limits for GitLab SaaS Free tier namespaces will not be enforced prior to 2022-10-19. Storage limits for GitLab SaaS Paid tier namespaces will not be enforced for prior to 2023-02-15. Enforcement will not occur until all storage types are accurately measured, including deduplication of forks for [Git](https://gitlab.com/gitlab-org/gitlab/-/issues/371671) and [LFS](https://gitlab.com/gitlab-org/gitlab/-/issues/370242).
-Impacted users are notified via email and in-app notifications at least 60 days prior to enforcement.
+Impacted users are notified via email and in-app notifications at least 60 days prior to enforcement.
### Project storage limit
-Namespaces on a GitLab SaaS **paid** tier (Premium and Ultimate) have a storage limit on their project repositories.
-A project's repository has a storage quota of 10 GB. A namespace has either a namespace-level storage limit or a project-level storage limit, but not both.
+Projects on GitLab SaaS have a 10GB storage limit on their Git repository and LFS storage.
+Once namespace-level storage limits are enforced, the project limit will be removed. A namespace has either a namespace-level storage limit or a project-level storage limit, but not both.
-- Paid tier namespaces have project-level storage limits enforced.
-- Free tier namespaces have namespace-level storage limits.
-
-When a project's repository reaches the quota, the project is locked. You cannot push changes to a locked project. To monitor the size of each
+When a project's repository and LFS reaches the quota, the project is locked. You cannot push changes to a locked project. To monitor the size of each
repository in a namespace, including a breakdown for each project, you can
-[view storage usage](#view-storage-usage). To allow a project's repository to exceed the free quota
+[view storage usage](#view-storage-usage). To allow a project's repository and LFS to exceed the free quota
you must purchase additional storage. For more details, see [Excess storage usage](#excess-storage-usage).
## View storage usage
-You can view storage usage for your project or [namespace](../user/namespace/index.md).
+Prerequisites:
+
+- To view storage usage for a project, you must have at least the Maintainer role for the project or Owner role for the namespace.
+- To view storage usage for a namespace, you must have the Owner role for the namespace.
1. Go to your project or namespace:
- - For a project, on the top bar, select **Menu > Projects** and find your project.
+ - For a project, on the top bar, select **Main menu > Projects** and find your project.
- For a namespace, enter the URL in your browser's toolbar.
1. From the left sidebar, select **Settings > Usage Quotas**.
1. Select the **Storage** tab.
@@ -87,20 +87,20 @@ The following storage usage statistics are available to a maintainer:
## Manage your storage usage
-You can use several methods to manage and reduce your usage for some storage types.
+To manage your storage, if you are a namespace Owner you can [purchase more storage for the namespace](../subscriptions/gitlab_com/index.md#purchase-more-storage-and-transfer).
-For more information, see the following pages:
+Depending on your role, you can also use the following methods to manage or reduce your storage:
-- [Reduce package registry storage](packages/package_registry/reduce_package_registry_storage.md)
-- [Reduce dependency proxy storage](packages/dependency_proxy/reduce_dependency_proxy_storage.md)
-- [Reduce repository size](project/repository/reducing_the_repo_size_using_git.md)
-- [Reduce container registry storage](packages/container_registry/reduce_container_registry_storage.md)
-- [Reduce container registry data transfers](packages/container_registry/reduce_container_registry_data_transfer.md)
-- [Reduce wiki repository size](../administration/wikis/index.md#reduce-wiki-repository-size)
+- [Reduce package registry storage](packages/package_registry/reduce_package_registry_storage.md).
+- [Reduce dependency proxy storage](packages/dependency_proxy/reduce_dependency_proxy_storage.md).
+- [Reduce repository size](project/repository/reducing_the_repo_size_using_git.md).
+- [Reduce container registry storage](packages/container_registry/reduce_container_registry_storage.md).
+- [Reduce container registry data transfers](packages/container_registry/reduce_container_registry_data_transfer.md).
+- [Reduce wiki repository size](../administration/wikis/index.md#reduce-wiki-repository-size).
## Excess storage usage
-Excess storage usage is the amount that a project's repository exceeds the free storage quota. If no
+Excess storage usage is the amount that a project's repository and LFS exceeds the free storage quota. If no
purchased storage is available the project is locked. You cannot push changes to a locked project.
To unlock a project you must [purchase more storage](../subscriptions/gitlab_com/index.md#purchase-more-storage-and-transfer)
for the namespace. When the purchase is completed, locked projects are automatically unlocked. The
@@ -115,7 +115,7 @@ The **Storage** tab of the **Usage Quotas** page warns you of the following:
### Excess storage example
-The following example describes an excess storage scenario for namespace _Example Company_:
+The following example describes an excess storage scenario for a namespace:
| Repository | Storage used | Excess storage | Quota | Status |
|------------|--------------|----------------|--------|-------------------|
@@ -125,12 +125,12 @@ The following example describes an excess storage scenario for namespace _Exampl
| Yellow | 2 GB | 0 GB | 10 GB | Not locked |
| **Totals** | **30 GB** | **0 GB** | - | - |
-The Red and Green projects are locked because their repositories have reached the quota. In this
+The Red and Green projects are locked because their repositories and LFS have reached the quota. In this
example, no additional storage has yet been purchased.
To unlock the Red and Green projects, 50 GB additional storage is purchased.
-Assuming the Green and Red projects' repositories grow past the 10 GB quota, the purchased storage
+Assuming the Green and Red projects' repositories and LFS grow past the 10 GB quota, the purchased storage
available decreases. All projects remain unlocked because 40 GB purchased storage is available:
50 GB (purchased storage) - 10 GB (total excess storage used).
diff --git a/glfm_specification/example_snapshots/examples_index.yml b/glfm_specification/example_snapshots/examples_index.yml
index 5778819e024..fd3e350f58a 100644
--- a/glfm_specification/example_snapshots/examples_index.yml
+++ b/glfm_specification/example_snapshots/examples_index.yml
@@ -1,2044 +1,2086 @@
---
-02_01__preliminaries__tabs__001:
+02_01_00__preliminaries__tabs__001:
spec_txt_example_position: 1
source_specification: commonmark
-02_01__preliminaries__tabs__002:
+02_01_00__preliminaries__tabs__002:
spec_txt_example_position: 2
source_specification: commonmark
-02_01__preliminaries__tabs__003:
+02_01_00__preliminaries__tabs__003:
spec_txt_example_position: 3
source_specification: commonmark
-02_01__preliminaries__tabs__004:
+02_01_00__preliminaries__tabs__004:
spec_txt_example_position: 4
source_specification: commonmark
-02_01__preliminaries__tabs__005:
+02_01_00__preliminaries__tabs__005:
spec_txt_example_position: 5
source_specification: commonmark
-02_01__preliminaries__tabs__006:
+02_01_00__preliminaries__tabs__006:
spec_txt_example_position: 6
source_specification: commonmark
-02_01__preliminaries__tabs__007:
+02_01_00__preliminaries__tabs__007:
spec_txt_example_position: 7
source_specification: commonmark
-02_01__preliminaries__tabs__008:
+02_01_00__preliminaries__tabs__008:
spec_txt_example_position: 8
source_specification: commonmark
-02_01__preliminaries__tabs__009:
+02_01_00__preliminaries__tabs__009:
spec_txt_example_position: 9
source_specification: commonmark
-02_01__preliminaries__tabs__010:
+02_01_00__preliminaries__tabs__010:
spec_txt_example_position: 10
source_specification: commonmark
-02_01__preliminaries__tabs__011:
+02_01_00__preliminaries__tabs__011:
spec_txt_example_position: 11
source_specification: commonmark
-03_01__blocks_and_inlines__precedence__001:
+03_01_00__blocks_and_inlines__precedence__001:
spec_txt_example_position: 12
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__001:
+04_01_00__leaf_blocks__thematic_breaks__001:
spec_txt_example_position: 13
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__002:
+04_01_00__leaf_blocks__thematic_breaks__002:
spec_txt_example_position: 14
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__003:
+04_01_00__leaf_blocks__thematic_breaks__003:
spec_txt_example_position: 15
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__004:
+04_01_00__leaf_blocks__thematic_breaks__004:
spec_txt_example_position: 16
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__005:
+04_01_00__leaf_blocks__thematic_breaks__005:
spec_txt_example_position: 17
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__006:
+04_01_00__leaf_blocks__thematic_breaks__006:
spec_txt_example_position: 18
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__007:
+04_01_00__leaf_blocks__thematic_breaks__007:
spec_txt_example_position: 19
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__008:
+04_01_00__leaf_blocks__thematic_breaks__008:
spec_txt_example_position: 20
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__009:
+04_01_00__leaf_blocks__thematic_breaks__009:
spec_txt_example_position: 21
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__010:
+04_01_00__leaf_blocks__thematic_breaks__010:
spec_txt_example_position: 22
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__011:
+04_01_00__leaf_blocks__thematic_breaks__011:
spec_txt_example_position: 23
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__012:
+04_01_00__leaf_blocks__thematic_breaks__012:
spec_txt_example_position: 24
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__013:
+04_01_00__leaf_blocks__thematic_breaks__013:
spec_txt_example_position: 25
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__014:
+04_01_00__leaf_blocks__thematic_breaks__014:
spec_txt_example_position: 26
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__015:
+04_01_00__leaf_blocks__thematic_breaks__015:
spec_txt_example_position: 27
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__016:
+04_01_00__leaf_blocks__thematic_breaks__016:
spec_txt_example_position: 28
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__017:
+04_01_00__leaf_blocks__thematic_breaks__017:
spec_txt_example_position: 29
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__018:
+04_01_00__leaf_blocks__thematic_breaks__018:
spec_txt_example_position: 30
source_specification: commonmark
-04_01__leaf_blocks__thematic_breaks__019:
+04_01_00__leaf_blocks__thematic_breaks__019:
spec_txt_example_position: 31
source_specification: commonmark
-04_02__leaf_blocks__atx_headings__001:
+04_02_00__leaf_blocks__atx_headings__001:
spec_txt_example_position: 32
source_specification: commonmark
-04_02__leaf_blocks__atx_headings__002:
+04_02_00__leaf_blocks__atx_headings__002:
spec_txt_example_position: 33
source_specification: commonmark
-04_02__leaf_blocks__atx_headings__003:
+04_02_00__leaf_blocks__atx_headings__003:
spec_txt_example_position: 34
source_specification: commonmark
-04_02__leaf_blocks__atx_headings__004:
+04_02_00__leaf_blocks__atx_headings__004:
spec_txt_example_position: 35
source_specification: commonmark
-04_02__leaf_blocks__atx_headings__005:
+04_02_00__leaf_blocks__atx_headings__005:
spec_txt_example_position: 36
source_specification: commonmark
-04_02__leaf_blocks__atx_headings__006:
+04_02_00__leaf_blocks__atx_headings__006:
spec_txt_example_position: 37
source_specification: commonmark
-04_02__leaf_blocks__atx_headings__007:
+04_02_00__leaf_blocks__atx_headings__007:
spec_txt_example_position: 38
source_specification: commonmark
-04_02__leaf_blocks__atx_headings__008:
+04_02_00__leaf_blocks__atx_headings__008:
spec_txt_example_position: 39
source_specification: commonmark
-04_02__leaf_blocks__atx_headings__009:
+04_02_00__leaf_blocks__atx_headings__009:
spec_txt_example_position: 40
source_specification: commonmark
-04_02__leaf_blocks__atx_headings__010:
+04_02_00__leaf_blocks__atx_headings__010:
spec_txt_example_position: 41
source_specification: commonmark
-04_02__leaf_blocks__atx_headings__011:
+04_02_00__leaf_blocks__atx_headings__011:
spec_txt_example_position: 42
source_specification: commonmark
-04_02__leaf_blocks__atx_headings__012:
+04_02_00__leaf_blocks__atx_headings__012:
spec_txt_example_position: 43
source_specification: commonmark
-04_02__leaf_blocks__atx_headings__013:
+04_02_00__leaf_blocks__atx_headings__013:
spec_txt_example_position: 44
source_specification: commonmark
-04_02__leaf_blocks__atx_headings__014:
+04_02_00__leaf_blocks__atx_headings__014:
spec_txt_example_position: 45
source_specification: commonmark
-04_02__leaf_blocks__atx_headings__015:
+04_02_00__leaf_blocks__atx_headings__015:
spec_txt_example_position: 46
source_specification: commonmark
-04_02__leaf_blocks__atx_headings__016:
+04_02_00__leaf_blocks__atx_headings__016:
spec_txt_example_position: 47
source_specification: commonmark
-04_02__leaf_blocks__atx_headings__017:
+04_02_00__leaf_blocks__atx_headings__017:
spec_txt_example_position: 48
source_specification: commonmark
-04_02__leaf_blocks__atx_headings__018:
+04_02_00__leaf_blocks__atx_headings__018:
spec_txt_example_position: 49
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__001:
+04_03_00__leaf_blocks__setext_headings__001:
spec_txt_example_position: 50
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__002:
+04_03_00__leaf_blocks__setext_headings__002:
spec_txt_example_position: 51
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__003:
+04_03_00__leaf_blocks__setext_headings__003:
spec_txt_example_position: 52
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__004:
+04_03_00__leaf_blocks__setext_headings__004:
spec_txt_example_position: 53
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__005:
+04_03_00__leaf_blocks__setext_headings__005:
spec_txt_example_position: 54
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__006:
+04_03_00__leaf_blocks__setext_headings__006:
spec_txt_example_position: 55
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__007:
+04_03_00__leaf_blocks__setext_headings__007:
spec_txt_example_position: 56
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__008:
+04_03_00__leaf_blocks__setext_headings__008:
spec_txt_example_position: 57
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__009:
+04_03_00__leaf_blocks__setext_headings__009:
spec_txt_example_position: 58
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__010:
+04_03_00__leaf_blocks__setext_headings__010:
spec_txt_example_position: 59
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__011:
+04_03_00__leaf_blocks__setext_headings__011:
spec_txt_example_position: 60
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__012:
+04_03_00__leaf_blocks__setext_headings__012:
spec_txt_example_position: 61
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__013:
+04_03_00__leaf_blocks__setext_headings__013:
spec_txt_example_position: 62
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__014:
+04_03_00__leaf_blocks__setext_headings__014:
spec_txt_example_position: 63
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__015:
+04_03_00__leaf_blocks__setext_headings__015:
spec_txt_example_position: 64
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__016:
+04_03_00__leaf_blocks__setext_headings__016:
spec_txt_example_position: 65
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__017:
+04_03_00__leaf_blocks__setext_headings__017:
spec_txt_example_position: 66
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__018:
+04_03_00__leaf_blocks__setext_headings__018:
spec_txt_example_position: 67
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__019:
+04_03_00__leaf_blocks__setext_headings__019:
spec_txt_example_position: 68
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__020:
+04_03_00__leaf_blocks__setext_headings__020:
spec_txt_example_position: 69
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__021:
+04_03_00__leaf_blocks__setext_headings__021:
spec_txt_example_position: 70
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__022:
+04_03_00__leaf_blocks__setext_headings__022:
spec_txt_example_position: 71
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__023:
+04_03_00__leaf_blocks__setext_headings__023:
spec_txt_example_position: 72
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__024:
+04_03_00__leaf_blocks__setext_headings__024:
spec_txt_example_position: 73
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__025:
+04_03_00__leaf_blocks__setext_headings__025:
spec_txt_example_position: 74
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__026:
+04_03_00__leaf_blocks__setext_headings__026:
spec_txt_example_position: 75
source_specification: commonmark
-04_03__leaf_blocks__setext_headings__027:
+04_03_00__leaf_blocks__setext_headings__027:
spec_txt_example_position: 76
source_specification: commonmark
-04_04__leaf_blocks__indented_code_blocks__001:
+04_04_00__leaf_blocks__indented_code_blocks__001:
spec_txt_example_position: 77
source_specification: commonmark
-04_04__leaf_blocks__indented_code_blocks__002:
+04_04_00__leaf_blocks__indented_code_blocks__002:
spec_txt_example_position: 78
source_specification: commonmark
-04_04__leaf_blocks__indented_code_blocks__003:
+04_04_00__leaf_blocks__indented_code_blocks__003:
spec_txt_example_position: 79
source_specification: commonmark
-04_04__leaf_blocks__indented_code_blocks__004:
+04_04_00__leaf_blocks__indented_code_blocks__004:
spec_txt_example_position: 80
source_specification: commonmark
-04_04__leaf_blocks__indented_code_blocks__005:
+04_04_00__leaf_blocks__indented_code_blocks__005:
spec_txt_example_position: 81
source_specification: commonmark
-04_04__leaf_blocks__indented_code_blocks__006:
+04_04_00__leaf_blocks__indented_code_blocks__006:
spec_txt_example_position: 82
source_specification: commonmark
-04_04__leaf_blocks__indented_code_blocks__007:
+04_04_00__leaf_blocks__indented_code_blocks__007:
spec_txt_example_position: 83
source_specification: commonmark
-04_04__leaf_blocks__indented_code_blocks__008:
+04_04_00__leaf_blocks__indented_code_blocks__008:
spec_txt_example_position: 84
source_specification: commonmark
-04_04__leaf_blocks__indented_code_blocks__009:
+04_04_00__leaf_blocks__indented_code_blocks__009:
spec_txt_example_position: 85
source_specification: commonmark
-04_04__leaf_blocks__indented_code_blocks__010:
+04_04_00__leaf_blocks__indented_code_blocks__010:
spec_txt_example_position: 86
source_specification: commonmark
-04_04__leaf_blocks__indented_code_blocks__011:
+04_04_00__leaf_blocks__indented_code_blocks__011:
spec_txt_example_position: 87
source_specification: commonmark
-04_04__leaf_blocks__indented_code_blocks__012:
+04_04_00__leaf_blocks__indented_code_blocks__012:
spec_txt_example_position: 88
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__001:
+04_05_00__leaf_blocks__fenced_code_blocks__001:
spec_txt_example_position: 89
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__002:
+04_05_00__leaf_blocks__fenced_code_blocks__002:
spec_txt_example_position: 90
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__003:
+04_05_00__leaf_blocks__fenced_code_blocks__003:
spec_txt_example_position: 91
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__004:
+04_05_00__leaf_blocks__fenced_code_blocks__004:
spec_txt_example_position: 92
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__005:
+04_05_00__leaf_blocks__fenced_code_blocks__005:
spec_txt_example_position: 93
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__006:
+04_05_00__leaf_blocks__fenced_code_blocks__006:
spec_txt_example_position: 94
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__007:
+04_05_00__leaf_blocks__fenced_code_blocks__007:
spec_txt_example_position: 95
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__008:
+04_05_00__leaf_blocks__fenced_code_blocks__008:
spec_txt_example_position: 96
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__009:
+04_05_00__leaf_blocks__fenced_code_blocks__009:
spec_txt_example_position: 97
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__010:
+04_05_00__leaf_blocks__fenced_code_blocks__010:
spec_txt_example_position: 98
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__011:
+04_05_00__leaf_blocks__fenced_code_blocks__011:
spec_txt_example_position: 99
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__012:
+04_05_00__leaf_blocks__fenced_code_blocks__012:
spec_txt_example_position: 100
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__013:
+04_05_00__leaf_blocks__fenced_code_blocks__013:
spec_txt_example_position: 101
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__014:
+04_05_00__leaf_blocks__fenced_code_blocks__014:
spec_txt_example_position: 102
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__015:
+04_05_00__leaf_blocks__fenced_code_blocks__015:
spec_txt_example_position: 103
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__016:
+04_05_00__leaf_blocks__fenced_code_blocks__016:
spec_txt_example_position: 104
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__017:
+04_05_00__leaf_blocks__fenced_code_blocks__017:
spec_txt_example_position: 105
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__018:
+04_05_00__leaf_blocks__fenced_code_blocks__018:
spec_txt_example_position: 106
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__019:
+04_05_00__leaf_blocks__fenced_code_blocks__019:
spec_txt_example_position: 107
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__020:
+04_05_00__leaf_blocks__fenced_code_blocks__020:
spec_txt_example_position: 108
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__021:
+04_05_00__leaf_blocks__fenced_code_blocks__021:
spec_txt_example_position: 109
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__022:
+04_05_00__leaf_blocks__fenced_code_blocks__022:
spec_txt_example_position: 110
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__023:
+04_05_00__leaf_blocks__fenced_code_blocks__023:
spec_txt_example_position: 111
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__024:
+04_05_00__leaf_blocks__fenced_code_blocks__024:
spec_txt_example_position: 112
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__025:
+04_05_00__leaf_blocks__fenced_code_blocks__025:
spec_txt_example_position: 113
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__026:
+04_05_00__leaf_blocks__fenced_code_blocks__026:
spec_txt_example_position: 114
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__027:
+04_05_00__leaf_blocks__fenced_code_blocks__027:
spec_txt_example_position: 115
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__028:
+04_05_00__leaf_blocks__fenced_code_blocks__028:
spec_txt_example_position: 116
source_specification: commonmark
-04_05__leaf_blocks__fenced_code_blocks__029:
+04_05_00__leaf_blocks__fenced_code_blocks__029:
spec_txt_example_position: 117
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__001:
+04_06_00__leaf_blocks__html_blocks__001:
spec_txt_example_position: 118
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__002:
+04_06_00__leaf_blocks__html_blocks__002:
spec_txt_example_position: 119
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__003:
+04_06_00__leaf_blocks__html_blocks__003:
spec_txt_example_position: 120
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__004:
+04_06_00__leaf_blocks__html_blocks__004:
spec_txt_example_position: 121
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__005:
+04_06_00__leaf_blocks__html_blocks__005:
spec_txt_example_position: 122
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__006:
+04_06_00__leaf_blocks__html_blocks__006:
spec_txt_example_position: 123
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__007:
+04_06_00__leaf_blocks__html_blocks__007:
spec_txt_example_position: 124
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__008:
+04_06_00__leaf_blocks__html_blocks__008:
spec_txt_example_position: 125
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__009:
+04_06_00__leaf_blocks__html_blocks__009:
spec_txt_example_position: 126
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__010:
+04_06_00__leaf_blocks__html_blocks__010:
spec_txt_example_position: 127
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__011:
+04_06_00__leaf_blocks__html_blocks__011:
spec_txt_example_position: 128
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__012:
+04_06_00__leaf_blocks__html_blocks__012:
spec_txt_example_position: 129
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__013:
+04_06_00__leaf_blocks__html_blocks__013:
spec_txt_example_position: 130
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__014:
+04_06_00__leaf_blocks__html_blocks__014:
spec_txt_example_position: 131
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__015:
+04_06_00__leaf_blocks__html_blocks__015:
spec_txt_example_position: 132
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__016:
+04_06_00__leaf_blocks__html_blocks__016:
spec_txt_example_position: 133
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__017:
+04_06_00__leaf_blocks__html_blocks__017:
spec_txt_example_position: 134
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__018:
+04_06_00__leaf_blocks__html_blocks__018:
spec_txt_example_position: 135
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__019:
+04_06_00__leaf_blocks__html_blocks__019:
spec_txt_example_position: 136
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__020:
+04_06_00__leaf_blocks__html_blocks__020:
spec_txt_example_position: 137
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__021:
+04_06_00__leaf_blocks__html_blocks__021:
spec_txt_example_position: 138
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__022:
+04_06_00__leaf_blocks__html_blocks__022:
spec_txt_example_position: 139
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__023:
+04_06_00__leaf_blocks__html_blocks__023:
spec_txt_example_position: 140
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__024:
+04_06_00__leaf_blocks__html_blocks__024:
spec_txt_example_position: 141
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__025:
+04_06_00__leaf_blocks__html_blocks__025:
spec_txt_example_position: 142
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__026:
+04_06_00__leaf_blocks__html_blocks__026:
spec_txt_example_position: 143
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__027:
+04_06_00__leaf_blocks__html_blocks__027:
spec_txt_example_position: 144
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__028:
+04_06_00__leaf_blocks__html_blocks__028:
spec_txt_example_position: 145
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__029:
+04_06_00__leaf_blocks__html_blocks__029:
spec_txt_example_position: 146
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__030:
+04_06_00__leaf_blocks__html_blocks__030:
spec_txt_example_position: 147
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__031:
+04_06_00__leaf_blocks__html_blocks__031:
spec_txt_example_position: 148
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__032:
+04_06_00__leaf_blocks__html_blocks__032:
spec_txt_example_position: 149
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__033:
+04_06_00__leaf_blocks__html_blocks__033:
spec_txt_example_position: 150
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__034:
+04_06_00__leaf_blocks__html_blocks__034:
spec_txt_example_position: 151
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__035:
+04_06_00__leaf_blocks__html_blocks__035:
spec_txt_example_position: 152
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__036:
+04_06_00__leaf_blocks__html_blocks__036:
spec_txt_example_position: 153
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__037:
+04_06_00__leaf_blocks__html_blocks__037:
spec_txt_example_position: 154
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__038:
+04_06_00__leaf_blocks__html_blocks__038:
spec_txt_example_position: 155
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__039:
+04_06_00__leaf_blocks__html_blocks__039:
spec_txt_example_position: 156
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__040:
+04_06_00__leaf_blocks__html_blocks__040:
spec_txt_example_position: 157
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__041:
+04_06_00__leaf_blocks__html_blocks__041:
spec_txt_example_position: 158
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__042:
+04_06_00__leaf_blocks__html_blocks__042:
spec_txt_example_position: 159
source_specification: commonmark
-04_06__leaf_blocks__html_blocks__043:
+04_06_00__leaf_blocks__html_blocks__043:
spec_txt_example_position: 160
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__001:
+04_07_00__leaf_blocks__link_reference_definitions__001:
spec_txt_example_position: 161
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__002:
+04_07_00__leaf_blocks__link_reference_definitions__002:
spec_txt_example_position: 162
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__003:
+04_07_00__leaf_blocks__link_reference_definitions__003:
spec_txt_example_position: 163
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__004:
+04_07_00__leaf_blocks__link_reference_definitions__004:
spec_txt_example_position: 164
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__005:
+04_07_00__leaf_blocks__link_reference_definitions__005:
spec_txt_example_position: 165
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__006:
+04_07_00__leaf_blocks__link_reference_definitions__006:
spec_txt_example_position: 166
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__007:
+04_07_00__leaf_blocks__link_reference_definitions__007:
spec_txt_example_position: 167
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__008:
+04_07_00__leaf_blocks__link_reference_definitions__008:
spec_txt_example_position: 168
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__009:
+04_07_00__leaf_blocks__link_reference_definitions__009:
spec_txt_example_position: 169
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__010:
+04_07_00__leaf_blocks__link_reference_definitions__010:
spec_txt_example_position: 170
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__011:
+04_07_00__leaf_blocks__link_reference_definitions__011:
spec_txt_example_position: 171
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__012:
+04_07_00__leaf_blocks__link_reference_definitions__012:
spec_txt_example_position: 172
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__013:
+04_07_00__leaf_blocks__link_reference_definitions__013:
spec_txt_example_position: 173
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__014:
+04_07_00__leaf_blocks__link_reference_definitions__014:
spec_txt_example_position: 174
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__015:
+04_07_00__leaf_blocks__link_reference_definitions__015:
spec_txt_example_position: 175
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__016:
+04_07_00__leaf_blocks__link_reference_definitions__016:
spec_txt_example_position: 176
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__017:
+04_07_00__leaf_blocks__link_reference_definitions__017:
spec_txt_example_position: 177
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__018:
+04_07_00__leaf_blocks__link_reference_definitions__018:
spec_txt_example_position: 178
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__019:
+04_07_00__leaf_blocks__link_reference_definitions__019:
spec_txt_example_position: 179
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__020:
+04_07_00__leaf_blocks__link_reference_definitions__020:
spec_txt_example_position: 180
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__021:
+04_07_00__leaf_blocks__link_reference_definitions__021:
spec_txt_example_position: 181
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__022:
+04_07_00__leaf_blocks__link_reference_definitions__022:
spec_txt_example_position: 182
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__023:
+04_07_00__leaf_blocks__link_reference_definitions__023:
spec_txt_example_position: 183
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__024:
+04_07_00__leaf_blocks__link_reference_definitions__024:
spec_txt_example_position: 184
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__025:
+04_07_00__leaf_blocks__link_reference_definitions__025:
spec_txt_example_position: 185
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__026:
+04_07_00__leaf_blocks__link_reference_definitions__026:
spec_txt_example_position: 186
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__027:
+04_07_00__leaf_blocks__link_reference_definitions__027:
spec_txt_example_position: 187
source_specification: commonmark
-04_07__leaf_blocks__link_reference_definitions__028:
+04_07_00__leaf_blocks__link_reference_definitions__028:
spec_txt_example_position: 188
source_specification: commonmark
-04_08__leaf_blocks__paragraphs__001:
+04_08_00__leaf_blocks__paragraphs__001:
spec_txt_example_position: 189
source_specification: commonmark
-04_08__leaf_blocks__paragraphs__002:
+04_08_00__leaf_blocks__paragraphs__002:
spec_txt_example_position: 190
source_specification: commonmark
-04_08__leaf_blocks__paragraphs__003:
+04_08_00__leaf_blocks__paragraphs__003:
spec_txt_example_position: 191
source_specification: commonmark
-04_08__leaf_blocks__paragraphs__004:
+04_08_00__leaf_blocks__paragraphs__004:
spec_txt_example_position: 192
source_specification: commonmark
-04_08__leaf_blocks__paragraphs__005:
+04_08_00__leaf_blocks__paragraphs__005:
spec_txt_example_position: 193
source_specification: commonmark
-04_08__leaf_blocks__paragraphs__006:
+04_08_00__leaf_blocks__paragraphs__006:
spec_txt_example_position: 194
source_specification: commonmark
-04_08__leaf_blocks__paragraphs__007:
+04_08_00__leaf_blocks__paragraphs__007:
spec_txt_example_position: 195
source_specification: commonmark
-04_08__leaf_blocks__paragraphs__008:
+04_08_00__leaf_blocks__paragraphs__008:
spec_txt_example_position: 196
source_specification: commonmark
-04_09__leaf_blocks__blank_lines__001:
+04_09_00__leaf_blocks__blank_lines__001:
spec_txt_example_position: 197
source_specification: commonmark
-04_10__leaf_blocks__tables_extension__001:
+04_10_00__leaf_blocks__tables_extension__001:
spec_txt_example_position: 198
source_specification: github
-04_10__leaf_blocks__tables_extension__002:
+04_10_00__leaf_blocks__tables_extension__002:
spec_txt_example_position: 199
source_specification: github
-04_10__leaf_blocks__tables_extension__003:
+04_10_00__leaf_blocks__tables_extension__003:
spec_txt_example_position: 200
source_specification: github
-04_10__leaf_blocks__tables_extension__004:
+04_10_00__leaf_blocks__tables_extension__004:
spec_txt_example_position: 201
source_specification: github
-04_10__leaf_blocks__tables_extension__005:
+04_10_00__leaf_blocks__tables_extension__005:
spec_txt_example_position: 202
source_specification: github
-04_10__leaf_blocks__tables_extension__006:
+04_10_00__leaf_blocks__tables_extension__006:
spec_txt_example_position: 203
source_specification: github
-04_10__leaf_blocks__tables_extension__007:
+04_10_00__leaf_blocks__tables_extension__007:
spec_txt_example_position: 204
source_specification: github
-04_10__leaf_blocks__tables_extension__008:
+04_10_00__leaf_blocks__tables_extension__008:
spec_txt_example_position: 205
source_specification: github
-05_01__container_blocks__block_quotes__001:
+05_01_00__container_blocks__block_quotes__001:
spec_txt_example_position: 206
source_specification: commonmark
-05_01__container_blocks__block_quotes__002:
+05_01_00__container_blocks__block_quotes__002:
spec_txt_example_position: 207
source_specification: commonmark
-05_01__container_blocks__block_quotes__003:
+05_01_00__container_blocks__block_quotes__003:
spec_txt_example_position: 208
source_specification: commonmark
-05_01__container_blocks__block_quotes__004:
+05_01_00__container_blocks__block_quotes__004:
spec_txt_example_position: 209
source_specification: commonmark
-05_01__container_blocks__block_quotes__005:
+05_01_00__container_blocks__block_quotes__005:
spec_txt_example_position: 210
source_specification: commonmark
-05_01__container_blocks__block_quotes__006:
+05_01_00__container_blocks__block_quotes__006:
spec_txt_example_position: 211
source_specification: commonmark
-05_01__container_blocks__block_quotes__007:
+05_01_00__container_blocks__block_quotes__007:
spec_txt_example_position: 212
source_specification: commonmark
-05_01__container_blocks__block_quotes__008:
+05_01_00__container_blocks__block_quotes__008:
spec_txt_example_position: 213
source_specification: commonmark
-05_01__container_blocks__block_quotes__009:
+05_01_00__container_blocks__block_quotes__009:
spec_txt_example_position: 214
source_specification: commonmark
-05_01__container_blocks__block_quotes__010:
+05_01_00__container_blocks__block_quotes__010:
spec_txt_example_position: 215
source_specification: commonmark
-05_01__container_blocks__block_quotes__011:
+05_01_00__container_blocks__block_quotes__011:
spec_txt_example_position: 216
source_specification: commonmark
-05_01__container_blocks__block_quotes__012:
+05_01_00__container_blocks__block_quotes__012:
spec_txt_example_position: 217
source_specification: commonmark
-05_01__container_blocks__block_quotes__013:
+05_01_00__container_blocks__block_quotes__013:
spec_txt_example_position: 218
source_specification: commonmark
-05_01__container_blocks__block_quotes__014:
+05_01_00__container_blocks__block_quotes__014:
spec_txt_example_position: 219
source_specification: commonmark
-05_01__container_blocks__block_quotes__015:
+05_01_00__container_blocks__block_quotes__015:
spec_txt_example_position: 220
source_specification: commonmark
-05_01__container_blocks__block_quotes__016:
+05_01_00__container_blocks__block_quotes__016:
spec_txt_example_position: 221
source_specification: commonmark
-05_01__container_blocks__block_quotes__017:
+05_01_00__container_blocks__block_quotes__017:
spec_txt_example_position: 222
source_specification: commonmark
-05_01__container_blocks__block_quotes__018:
+05_01_00__container_blocks__block_quotes__018:
spec_txt_example_position: 223
source_specification: commonmark
-05_01__container_blocks__block_quotes__019:
+05_01_00__container_blocks__block_quotes__019:
spec_txt_example_position: 224
source_specification: commonmark
-05_01__container_blocks__block_quotes__020:
+05_01_00__container_blocks__block_quotes__020:
spec_txt_example_position: 225
source_specification: commonmark
-05_01__container_blocks__block_quotes__021:
+05_01_00__container_blocks__block_quotes__021:
spec_txt_example_position: 226
source_specification: commonmark
-05_01__container_blocks__block_quotes__022:
+05_01_00__container_blocks__block_quotes__022:
spec_txt_example_position: 227
source_specification: commonmark
-05_01__container_blocks__block_quotes__023:
+05_01_00__container_blocks__block_quotes__023:
spec_txt_example_position: 228
source_specification: commonmark
-05_01__container_blocks__block_quotes__024:
+05_01_00__container_blocks__block_quotes__024:
spec_txt_example_position: 229
source_specification: commonmark
-05_01__container_blocks__block_quotes__025:
+05_01_00__container_blocks__block_quotes__025:
spec_txt_example_position: 230
source_specification: commonmark
-05_02__container_blocks__list_items__001:
+05_02_00__container_blocks__list_items__001:
spec_txt_example_position: 231
source_specification: commonmark
-05_02__container_blocks__list_items__002:
+05_02_00__container_blocks__list_items__002:
spec_txt_example_position: 232
source_specification: commonmark
-05_02__container_blocks__list_items__003:
+05_02_00__container_blocks__list_items__003:
spec_txt_example_position: 233
source_specification: commonmark
-05_02__container_blocks__list_items__004:
+05_02_00__container_blocks__list_items__004:
spec_txt_example_position: 234
source_specification: commonmark
-05_02__container_blocks__list_items__005:
+05_02_00__container_blocks__list_items__005:
spec_txt_example_position: 235
source_specification: commonmark
-05_02__container_blocks__list_items__006:
+05_02_00__container_blocks__list_items__006:
spec_txt_example_position: 236
source_specification: commonmark
-05_02__container_blocks__list_items__007:
+05_02_00__container_blocks__list_items__007:
spec_txt_example_position: 237
source_specification: commonmark
-05_02__container_blocks__list_items__008:
+05_02_00__container_blocks__list_items__008:
spec_txt_example_position: 238
source_specification: commonmark
-05_02__container_blocks__list_items__009:
+05_02_00__container_blocks__list_items__009:
spec_txt_example_position: 239
source_specification: commonmark
-05_02__container_blocks__list_items__010:
+05_02_00__container_blocks__list_items__010:
spec_txt_example_position: 240
source_specification: commonmark
-05_02__container_blocks__list_items__011:
+05_02_00__container_blocks__list_items__011:
spec_txt_example_position: 241
source_specification: commonmark
-05_02__container_blocks__list_items__012:
+05_02_00__container_blocks__list_items__012:
spec_txt_example_position: 242
source_specification: commonmark
-05_02__container_blocks__list_items__013:
+05_02_00__container_blocks__list_items__013:
spec_txt_example_position: 243
source_specification: commonmark
-05_02__container_blocks__list_items__014:
+05_02_00__container_blocks__list_items__014:
spec_txt_example_position: 244
source_specification: commonmark
-05_02__container_blocks__list_items__015:
+05_02_00__container_blocks__list_items__015:
spec_txt_example_position: 245
source_specification: commonmark
-05_02__container_blocks__list_items__016:
+05_02_00__container_blocks__list_items__016:
spec_txt_example_position: 246
source_specification: commonmark
-05_02__container_blocks__list_items__017:
+05_02_00__container_blocks__list_items__017:
spec_txt_example_position: 247
source_specification: commonmark
-05_02__container_blocks__list_items__018:
+05_02_00__container_blocks__list_items__018:
spec_txt_example_position: 248
source_specification: commonmark
-05_02__container_blocks__list_items__019:
+05_02_00__container_blocks__list_items__019:
spec_txt_example_position: 249
source_specification: commonmark
-05_02__container_blocks__list_items__020:
+05_02_00__container_blocks__list_items__020:
spec_txt_example_position: 250
source_specification: commonmark
-05_02__container_blocks__list_items__021:
+05_02_00__container_blocks__list_items__021:
spec_txt_example_position: 251
source_specification: commonmark
-05_02__container_blocks__list_items__022:
+05_02_00__container_blocks__list_items__022:
spec_txt_example_position: 252
source_specification: commonmark
-05_02__container_blocks__list_items__023:
+05_02_00__container_blocks__list_items__023:
spec_txt_example_position: 253
source_specification: commonmark
-05_02__container_blocks__list_items__024:
+05_02_00__container_blocks__list_items__024:
spec_txt_example_position: 254
source_specification: commonmark
-05_02__container_blocks__list_items__025:
+05_02_00__container_blocks__list_items__025:
spec_txt_example_position: 255
source_specification: commonmark
-05_02__container_blocks__list_items__026:
+05_02_00__container_blocks__list_items__026:
spec_txt_example_position: 256
source_specification: commonmark
-05_02__container_blocks__list_items__027:
+05_02_00__container_blocks__list_items__027:
spec_txt_example_position: 257
source_specification: commonmark
-05_02__container_blocks__list_items__028:
+05_02_00__container_blocks__list_items__028:
spec_txt_example_position: 258
source_specification: commonmark
-05_02__container_blocks__list_items__029:
+05_02_00__container_blocks__list_items__029:
spec_txt_example_position: 259
source_specification: commonmark
-05_02__container_blocks__list_items__030:
+05_02_00__container_blocks__list_items__030:
spec_txt_example_position: 260
source_specification: commonmark
-05_02__container_blocks__list_items__031:
+05_02_00__container_blocks__list_items__031:
spec_txt_example_position: 261
source_specification: commonmark
-05_02__container_blocks__list_items__032:
+05_02_00__container_blocks__list_items__032:
spec_txt_example_position: 262
source_specification: commonmark
-05_02__container_blocks__list_items__033:
+05_02_00__container_blocks__list_items__033:
spec_txt_example_position: 263
source_specification: commonmark
-05_02__container_blocks__list_items__034:
+05_02_00__container_blocks__list_items__034:
spec_txt_example_position: 264
source_specification: commonmark
-05_02__container_blocks__list_items__035:
+05_02_00__container_blocks__list_items__035:
spec_txt_example_position: 265
source_specification: commonmark
-05_02__container_blocks__list_items__036:
+05_02_00__container_blocks__list_items__036:
spec_txt_example_position: 266
source_specification: commonmark
-05_02__container_blocks__list_items__037:
+05_02_00__container_blocks__list_items__037:
spec_txt_example_position: 267
source_specification: commonmark
-05_02__container_blocks__list_items__038:
+05_02_00__container_blocks__list_items__038:
spec_txt_example_position: 268
source_specification: commonmark
-05_02__container_blocks__list_items__039:
+05_02_00__container_blocks__list_items__039:
spec_txt_example_position: 269
source_specification: commonmark
-05_02__container_blocks__list_items__040:
+05_02_00__container_blocks__list_items__040:
spec_txt_example_position: 270
source_specification: commonmark
-05_02__container_blocks__list_items__041:
+05_02_00__container_blocks__list_items__041:
spec_txt_example_position: 271
source_specification: commonmark
-05_02__container_blocks__list_items__042:
+05_02_00__container_blocks__list_items__042:
spec_txt_example_position: 272
source_specification: commonmark
-05_02__container_blocks__list_items__043:
+05_02_00__container_blocks__list_items__043:
spec_txt_example_position: 273
source_specification: commonmark
-05_02__container_blocks__list_items__044:
+05_02_00__container_blocks__list_items__044:
spec_txt_example_position: 274
source_specification: commonmark
-05_02__container_blocks__list_items__045:
+05_02_00__container_blocks__list_items__045:
spec_txt_example_position: 275
source_specification: commonmark
-05_02__container_blocks__list_items__046:
+05_02_00__container_blocks__list_items__046:
spec_txt_example_position: 276
source_specification: commonmark
-05_02__container_blocks__list_items__047:
+05_02_00__container_blocks__list_items__047:
spec_txt_example_position: 277
source_specification: commonmark
-05_02__container_blocks__list_items__048:
+05_02_00__container_blocks__list_items__048:
spec_txt_example_position: 278
source_specification: commonmark
-05_04__container_blocks__lists__001:
+05_04_00__container_blocks__lists__001:
spec_txt_example_position: 281
source_specification: commonmark
-05_04__container_blocks__lists__002:
+05_04_00__container_blocks__lists__002:
spec_txt_example_position: 282
source_specification: commonmark
-05_04__container_blocks__lists__003:
+05_04_00__container_blocks__lists__003:
spec_txt_example_position: 283
source_specification: commonmark
-05_04__container_blocks__lists__004:
+05_04_00__container_blocks__lists__004:
spec_txt_example_position: 284
source_specification: commonmark
-05_04__container_blocks__lists__005:
+05_04_00__container_blocks__lists__005:
spec_txt_example_position: 285
source_specification: commonmark
-05_04__container_blocks__lists__006:
+05_04_00__container_blocks__lists__006:
spec_txt_example_position: 286
source_specification: commonmark
-05_04__container_blocks__lists__007:
+05_04_00__container_blocks__lists__007:
spec_txt_example_position: 287
source_specification: commonmark
-05_04__container_blocks__lists__008:
+05_04_00__container_blocks__lists__008:
spec_txt_example_position: 288
source_specification: commonmark
-05_04__container_blocks__lists__009:
+05_04_00__container_blocks__lists__009:
spec_txt_example_position: 289
source_specification: commonmark
-05_04__container_blocks__lists__010:
+05_04_00__container_blocks__lists__010:
spec_txt_example_position: 290
source_specification: commonmark
-05_04__container_blocks__lists__011:
+05_04_00__container_blocks__lists__011:
spec_txt_example_position: 291
source_specification: commonmark
-05_04__container_blocks__lists__012:
+05_04_00__container_blocks__lists__012:
spec_txt_example_position: 292
source_specification: commonmark
-05_04__container_blocks__lists__013:
+05_04_00__container_blocks__lists__013:
spec_txt_example_position: 293
source_specification: commonmark
-05_04__container_blocks__lists__014:
+05_04_00__container_blocks__lists__014:
spec_txt_example_position: 294
source_specification: commonmark
-05_04__container_blocks__lists__015:
+05_04_00__container_blocks__lists__015:
spec_txt_example_position: 295
source_specification: commonmark
-05_04__container_blocks__lists__016:
+05_04_00__container_blocks__lists__016:
spec_txt_example_position: 296
source_specification: commonmark
-05_04__container_blocks__lists__017:
+05_04_00__container_blocks__lists__017:
spec_txt_example_position: 297
source_specification: commonmark
-05_04__container_blocks__lists__018:
+05_04_00__container_blocks__lists__018:
spec_txt_example_position: 298
source_specification: commonmark
-05_04__container_blocks__lists__019:
+05_04_00__container_blocks__lists__019:
spec_txt_example_position: 299
source_specification: commonmark
-05_04__container_blocks__lists__020:
+05_04_00__container_blocks__lists__020:
spec_txt_example_position: 300
source_specification: commonmark
-05_04__container_blocks__lists__021:
+05_04_00__container_blocks__lists__021:
spec_txt_example_position: 301
source_specification: commonmark
-05_04__container_blocks__lists__022:
+05_04_00__container_blocks__lists__022:
spec_txt_example_position: 302
source_specification: commonmark
-05_04__container_blocks__lists__023:
+05_04_00__container_blocks__lists__023:
spec_txt_example_position: 303
source_specification: commonmark
-05_04__container_blocks__lists__024:
+05_04_00__container_blocks__lists__024:
spec_txt_example_position: 304
source_specification: commonmark
-05_04__container_blocks__lists__025:
+05_04_00__container_blocks__lists__025:
spec_txt_example_position: 305
source_specification: commonmark
-05_04__container_blocks__lists__026:
+05_04_00__container_blocks__lists__026:
spec_txt_example_position: 306
source_specification: commonmark
-06_01__inlines__001:
+06_01_00__inlines__001:
spec_txt_example_position: 307
source_specification: commonmark
-06_02__inlines__backslash_escapes__001:
+06_02_00__inlines__backslash_escapes__001:
spec_txt_example_position: 308
source_specification: commonmark
-06_02__inlines__backslash_escapes__002:
+06_02_00__inlines__backslash_escapes__002:
spec_txt_example_position: 309
source_specification: commonmark
-06_02__inlines__backslash_escapes__003:
+06_02_00__inlines__backslash_escapes__003:
spec_txt_example_position: 310
source_specification: commonmark
-06_02__inlines__backslash_escapes__004:
+06_02_00__inlines__backslash_escapes__004:
spec_txt_example_position: 311
source_specification: commonmark
-06_02__inlines__backslash_escapes__005:
+06_02_00__inlines__backslash_escapes__005:
spec_txt_example_position: 312
source_specification: commonmark
-06_02__inlines__backslash_escapes__006:
+06_02_00__inlines__backslash_escapes__006:
spec_txt_example_position: 313
source_specification: commonmark
-06_02__inlines__backslash_escapes__007:
+06_02_00__inlines__backslash_escapes__007:
spec_txt_example_position: 314
source_specification: commonmark
-06_02__inlines__backslash_escapes__008:
+06_02_00__inlines__backslash_escapes__008:
spec_txt_example_position: 315
source_specification: commonmark
-06_02__inlines__backslash_escapes__009:
+06_02_00__inlines__backslash_escapes__009:
spec_txt_example_position: 316
source_specification: commonmark
-06_02__inlines__backslash_escapes__010:
+06_02_00__inlines__backslash_escapes__010:
spec_txt_example_position: 317
source_specification: commonmark
-06_02__inlines__backslash_escapes__011:
+06_02_00__inlines__backslash_escapes__011:
spec_txt_example_position: 318
source_specification: commonmark
-06_02__inlines__backslash_escapes__012:
+06_02_00__inlines__backslash_escapes__012:
spec_txt_example_position: 319
source_specification: commonmark
-06_02__inlines__backslash_escapes__013:
+06_02_00__inlines__backslash_escapes__013:
spec_txt_example_position: 320
source_specification: commonmark
-06_03__inlines__entity_and_numeric_character_references__001:
+06_03_00__inlines__entity_and_numeric_character_references__001:
spec_txt_example_position: 321
source_specification: commonmark
-06_03__inlines__entity_and_numeric_character_references__002:
+06_03_00__inlines__entity_and_numeric_character_references__002:
spec_txt_example_position: 322
source_specification: commonmark
-06_03__inlines__entity_and_numeric_character_references__003:
+06_03_00__inlines__entity_and_numeric_character_references__003:
spec_txt_example_position: 323
source_specification: commonmark
-06_03__inlines__entity_and_numeric_character_references__004:
+06_03_00__inlines__entity_and_numeric_character_references__004:
spec_txt_example_position: 324
source_specification: commonmark
-06_03__inlines__entity_and_numeric_character_references__005:
+06_03_00__inlines__entity_and_numeric_character_references__005:
spec_txt_example_position: 325
source_specification: commonmark
-06_03__inlines__entity_and_numeric_character_references__006:
+06_03_00__inlines__entity_and_numeric_character_references__006:
spec_txt_example_position: 326
source_specification: commonmark
-06_03__inlines__entity_and_numeric_character_references__007:
+06_03_00__inlines__entity_and_numeric_character_references__007:
spec_txt_example_position: 327
source_specification: commonmark
-06_03__inlines__entity_and_numeric_character_references__008:
+06_03_00__inlines__entity_and_numeric_character_references__008:
spec_txt_example_position: 328
source_specification: commonmark
-06_03__inlines__entity_and_numeric_character_references__009:
+06_03_00__inlines__entity_and_numeric_character_references__009:
spec_txt_example_position: 329
source_specification: commonmark
-06_03__inlines__entity_and_numeric_character_references__010:
+06_03_00__inlines__entity_and_numeric_character_references__010:
spec_txt_example_position: 330
source_specification: commonmark
-06_03__inlines__entity_and_numeric_character_references__011:
+06_03_00__inlines__entity_and_numeric_character_references__011:
spec_txt_example_position: 331
source_specification: commonmark
-06_03__inlines__entity_and_numeric_character_references__012:
+06_03_00__inlines__entity_and_numeric_character_references__012:
spec_txt_example_position: 332
source_specification: commonmark
-06_03__inlines__entity_and_numeric_character_references__013:
+06_03_00__inlines__entity_and_numeric_character_references__013:
spec_txt_example_position: 333
source_specification: commonmark
-06_03__inlines__entity_and_numeric_character_references__014:
+06_03_00__inlines__entity_and_numeric_character_references__014:
spec_txt_example_position: 334
source_specification: commonmark
-06_03__inlines__entity_and_numeric_character_references__015:
+06_03_00__inlines__entity_and_numeric_character_references__015:
spec_txt_example_position: 335
source_specification: commonmark
-06_03__inlines__entity_and_numeric_character_references__016:
+06_03_00__inlines__entity_and_numeric_character_references__016:
spec_txt_example_position: 336
source_specification: commonmark
-06_03__inlines__entity_and_numeric_character_references__017:
+06_03_00__inlines__entity_and_numeric_character_references__017:
spec_txt_example_position: 337
source_specification: commonmark
-06_04__inlines__code_spans__001:
+06_04_00__inlines__code_spans__001:
spec_txt_example_position: 338
source_specification: commonmark
-06_04__inlines__code_spans__002:
+06_04_00__inlines__code_spans__002:
spec_txt_example_position: 339
source_specification: commonmark
-06_04__inlines__code_spans__003:
+06_04_00__inlines__code_spans__003:
spec_txt_example_position: 340
source_specification: commonmark
-06_04__inlines__code_spans__004:
+06_04_00__inlines__code_spans__004:
spec_txt_example_position: 341
source_specification: commonmark
-06_04__inlines__code_spans__005:
+06_04_00__inlines__code_spans__005:
spec_txt_example_position: 342
source_specification: commonmark
-06_04__inlines__code_spans__006:
+06_04_00__inlines__code_spans__006:
spec_txt_example_position: 343
source_specification: commonmark
-06_04__inlines__code_spans__007:
+06_04_00__inlines__code_spans__007:
spec_txt_example_position: 344
source_specification: commonmark
-06_04__inlines__code_spans__008:
+06_04_00__inlines__code_spans__008:
spec_txt_example_position: 345
source_specification: commonmark
-06_04__inlines__code_spans__009:
+06_04_00__inlines__code_spans__009:
spec_txt_example_position: 346
source_specification: commonmark
-06_04__inlines__code_spans__010:
+06_04_00__inlines__code_spans__010:
spec_txt_example_position: 347
source_specification: commonmark
-06_04__inlines__code_spans__011:
+06_04_00__inlines__code_spans__011:
spec_txt_example_position: 348
source_specification: commonmark
-06_04__inlines__code_spans__012:
+06_04_00__inlines__code_spans__012:
spec_txt_example_position: 349
source_specification: commonmark
-06_04__inlines__code_spans__013:
+06_04_00__inlines__code_spans__013:
spec_txt_example_position: 350
source_specification: commonmark
-06_04__inlines__code_spans__014:
+06_04_00__inlines__code_spans__014:
spec_txt_example_position: 351
source_specification: commonmark
-06_04__inlines__code_spans__015:
+06_04_00__inlines__code_spans__015:
spec_txt_example_position: 352
source_specification: commonmark
-06_04__inlines__code_spans__016:
+06_04_00__inlines__code_spans__016:
spec_txt_example_position: 353
source_specification: commonmark
-06_04__inlines__code_spans__017:
+06_04_00__inlines__code_spans__017:
spec_txt_example_position: 354
source_specification: commonmark
-06_04__inlines__code_spans__018:
+06_04_00__inlines__code_spans__018:
spec_txt_example_position: 355
source_specification: commonmark
-06_04__inlines__code_spans__019:
+06_04_00__inlines__code_spans__019:
spec_txt_example_position: 356
source_specification: commonmark
-06_04__inlines__code_spans__020:
+06_04_00__inlines__code_spans__020:
spec_txt_example_position: 357
source_specification: commonmark
-06_04__inlines__code_spans__021:
+06_04_00__inlines__code_spans__021:
spec_txt_example_position: 358
source_specification: commonmark
-06_04__inlines__code_spans__022:
+06_04_00__inlines__code_spans__022:
spec_txt_example_position: 359
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__001:
+06_05_00__inlines__emphasis_and_strong_emphasis__001:
spec_txt_example_position: 360
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__002:
+06_05_00__inlines__emphasis_and_strong_emphasis__002:
spec_txt_example_position: 361
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__003:
+06_05_00__inlines__emphasis_and_strong_emphasis__003:
spec_txt_example_position: 362
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__004:
+06_05_00__inlines__emphasis_and_strong_emphasis__004:
spec_txt_example_position: 363
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__005:
+06_05_00__inlines__emphasis_and_strong_emphasis__005:
spec_txt_example_position: 364
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__006:
+06_05_00__inlines__emphasis_and_strong_emphasis__006:
spec_txt_example_position: 365
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__007:
+06_05_00__inlines__emphasis_and_strong_emphasis__007:
spec_txt_example_position: 366
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__008:
+06_05_00__inlines__emphasis_and_strong_emphasis__008:
spec_txt_example_position: 367
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__009:
+06_05_00__inlines__emphasis_and_strong_emphasis__009:
spec_txt_example_position: 368
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__010:
+06_05_00__inlines__emphasis_and_strong_emphasis__010:
spec_txt_example_position: 369
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__011:
+06_05_00__inlines__emphasis_and_strong_emphasis__011:
spec_txt_example_position: 370
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__012:
+06_05_00__inlines__emphasis_and_strong_emphasis__012:
spec_txt_example_position: 371
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__013:
+06_05_00__inlines__emphasis_and_strong_emphasis__013:
spec_txt_example_position: 372
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__014:
+06_05_00__inlines__emphasis_and_strong_emphasis__014:
spec_txt_example_position: 373
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__015:
+06_05_00__inlines__emphasis_and_strong_emphasis__015:
spec_txt_example_position: 374
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__016:
+06_05_00__inlines__emphasis_and_strong_emphasis__016:
spec_txt_example_position: 375
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__017:
+06_05_00__inlines__emphasis_and_strong_emphasis__017:
spec_txt_example_position: 376
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__018:
+06_05_00__inlines__emphasis_and_strong_emphasis__018:
spec_txt_example_position: 377
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__019:
+06_05_00__inlines__emphasis_and_strong_emphasis__019:
spec_txt_example_position: 378
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__020:
+06_05_00__inlines__emphasis_and_strong_emphasis__020:
spec_txt_example_position: 379
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__021:
+06_05_00__inlines__emphasis_and_strong_emphasis__021:
spec_txt_example_position: 380
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__022:
+06_05_00__inlines__emphasis_and_strong_emphasis__022:
spec_txt_example_position: 381
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__023:
+06_05_00__inlines__emphasis_and_strong_emphasis__023:
spec_txt_example_position: 382
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__024:
+06_05_00__inlines__emphasis_and_strong_emphasis__024:
spec_txt_example_position: 383
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__025:
+06_05_00__inlines__emphasis_and_strong_emphasis__025:
spec_txt_example_position: 384
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__026:
+06_05_00__inlines__emphasis_and_strong_emphasis__026:
spec_txt_example_position: 385
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__027:
+06_05_00__inlines__emphasis_and_strong_emphasis__027:
spec_txt_example_position: 386
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__028:
+06_05_00__inlines__emphasis_and_strong_emphasis__028:
spec_txt_example_position: 387
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__029:
+06_05_00__inlines__emphasis_and_strong_emphasis__029:
spec_txt_example_position: 388
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__030:
+06_05_00__inlines__emphasis_and_strong_emphasis__030:
spec_txt_example_position: 389
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__031:
+06_05_00__inlines__emphasis_and_strong_emphasis__031:
spec_txt_example_position: 390
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__032:
+06_05_00__inlines__emphasis_and_strong_emphasis__032:
spec_txt_example_position: 391
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__033:
+06_05_00__inlines__emphasis_and_strong_emphasis__033:
spec_txt_example_position: 392
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__034:
+06_05_00__inlines__emphasis_and_strong_emphasis__034:
spec_txt_example_position: 393
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__035:
+06_05_00__inlines__emphasis_and_strong_emphasis__035:
spec_txt_example_position: 394
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__036:
+06_05_00__inlines__emphasis_and_strong_emphasis__036:
spec_txt_example_position: 395
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__037:
+06_05_00__inlines__emphasis_and_strong_emphasis__037:
spec_txt_example_position: 396
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__038:
+06_05_00__inlines__emphasis_and_strong_emphasis__038:
spec_txt_example_position: 397
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__039:
+06_05_00__inlines__emphasis_and_strong_emphasis__039:
spec_txt_example_position: 398
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__040:
+06_05_00__inlines__emphasis_and_strong_emphasis__040:
spec_txt_example_position: 399
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__041:
+06_05_00__inlines__emphasis_and_strong_emphasis__041:
spec_txt_example_position: 400
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__042:
+06_05_00__inlines__emphasis_and_strong_emphasis__042:
spec_txt_example_position: 401
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__043:
+06_05_00__inlines__emphasis_and_strong_emphasis__043:
spec_txt_example_position: 402
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__044:
+06_05_00__inlines__emphasis_and_strong_emphasis__044:
spec_txt_example_position: 403
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__045:
+06_05_00__inlines__emphasis_and_strong_emphasis__045:
spec_txt_example_position: 404
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__046:
+06_05_00__inlines__emphasis_and_strong_emphasis__046:
spec_txt_example_position: 405
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__047:
+06_05_00__inlines__emphasis_and_strong_emphasis__047:
spec_txt_example_position: 406
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__048:
+06_05_00__inlines__emphasis_and_strong_emphasis__048:
spec_txt_example_position: 407
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__049:
+06_05_00__inlines__emphasis_and_strong_emphasis__049:
spec_txt_example_position: 408
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__050:
+06_05_00__inlines__emphasis_and_strong_emphasis__050:
spec_txt_example_position: 409
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__051:
+06_05_00__inlines__emphasis_and_strong_emphasis__051:
spec_txt_example_position: 410
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__052:
+06_05_00__inlines__emphasis_and_strong_emphasis__052:
spec_txt_example_position: 411
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__053:
+06_05_00__inlines__emphasis_and_strong_emphasis__053:
spec_txt_example_position: 412
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__054:
+06_05_00__inlines__emphasis_and_strong_emphasis__054:
spec_txt_example_position: 413
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__055:
+06_05_00__inlines__emphasis_and_strong_emphasis__055:
spec_txt_example_position: 414
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__056:
+06_05_00__inlines__emphasis_and_strong_emphasis__056:
spec_txt_example_position: 415
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__057:
+06_05_00__inlines__emphasis_and_strong_emphasis__057:
spec_txt_example_position: 416
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__058:
+06_05_00__inlines__emphasis_and_strong_emphasis__058:
spec_txt_example_position: 417
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__059:
+06_05_00__inlines__emphasis_and_strong_emphasis__059:
spec_txt_example_position: 418
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__060:
+06_05_00__inlines__emphasis_and_strong_emphasis__060:
spec_txt_example_position: 419
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__061:
+06_05_00__inlines__emphasis_and_strong_emphasis__061:
spec_txt_example_position: 420
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__062:
+06_05_00__inlines__emphasis_and_strong_emphasis__062:
spec_txt_example_position: 421
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__063:
+06_05_00__inlines__emphasis_and_strong_emphasis__063:
spec_txt_example_position: 422
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__064:
+06_05_00__inlines__emphasis_and_strong_emphasis__064:
spec_txt_example_position: 423
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__065:
+06_05_00__inlines__emphasis_and_strong_emphasis__065:
spec_txt_example_position: 424
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__066:
+06_05_00__inlines__emphasis_and_strong_emphasis__066:
spec_txt_example_position: 425
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__067:
+06_05_00__inlines__emphasis_and_strong_emphasis__067:
spec_txt_example_position: 426
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__068:
+06_05_00__inlines__emphasis_and_strong_emphasis__068:
spec_txt_example_position: 427
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__069:
+06_05_00__inlines__emphasis_and_strong_emphasis__069:
spec_txt_example_position: 428
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__070:
+06_05_00__inlines__emphasis_and_strong_emphasis__070:
spec_txt_example_position: 429
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__071:
+06_05_00__inlines__emphasis_and_strong_emphasis__071:
spec_txt_example_position: 430
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__072:
+06_05_00__inlines__emphasis_and_strong_emphasis__072:
spec_txt_example_position: 431
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__073:
+06_05_00__inlines__emphasis_and_strong_emphasis__073:
spec_txt_example_position: 432
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__074:
+06_05_00__inlines__emphasis_and_strong_emphasis__074:
spec_txt_example_position: 433
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__075:
+06_05_00__inlines__emphasis_and_strong_emphasis__075:
spec_txt_example_position: 434
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__076:
+06_05_00__inlines__emphasis_and_strong_emphasis__076:
spec_txt_example_position: 435
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__077:
+06_05_00__inlines__emphasis_and_strong_emphasis__077:
spec_txt_example_position: 436
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__078:
+06_05_00__inlines__emphasis_and_strong_emphasis__078:
spec_txt_example_position: 437
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__079:
+06_05_00__inlines__emphasis_and_strong_emphasis__079:
spec_txt_example_position: 438
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__080:
+06_05_00__inlines__emphasis_and_strong_emphasis__080:
spec_txt_example_position: 439
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__081:
+06_05_00__inlines__emphasis_and_strong_emphasis__081:
spec_txt_example_position: 440
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__082:
+06_05_00__inlines__emphasis_and_strong_emphasis__082:
spec_txt_example_position: 441
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__083:
+06_05_00__inlines__emphasis_and_strong_emphasis__083:
spec_txt_example_position: 442
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__084:
+06_05_00__inlines__emphasis_and_strong_emphasis__084:
spec_txt_example_position: 443
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__085:
+06_05_00__inlines__emphasis_and_strong_emphasis__085:
spec_txt_example_position: 444
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__086:
+06_05_00__inlines__emphasis_and_strong_emphasis__086:
spec_txt_example_position: 445
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__087:
+06_05_00__inlines__emphasis_and_strong_emphasis__087:
spec_txt_example_position: 446
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__088:
+06_05_00__inlines__emphasis_and_strong_emphasis__088:
spec_txt_example_position: 447
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__089:
+06_05_00__inlines__emphasis_and_strong_emphasis__089:
spec_txt_example_position: 448
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__090:
+06_05_00__inlines__emphasis_and_strong_emphasis__090:
spec_txt_example_position: 449
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__091:
+06_05_00__inlines__emphasis_and_strong_emphasis__091:
spec_txt_example_position: 450
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__092:
+06_05_00__inlines__emphasis_and_strong_emphasis__092:
spec_txt_example_position: 451
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__093:
+06_05_00__inlines__emphasis_and_strong_emphasis__093:
spec_txt_example_position: 452
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__094:
+06_05_00__inlines__emphasis_and_strong_emphasis__094:
spec_txt_example_position: 453
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__095:
+06_05_00__inlines__emphasis_and_strong_emphasis__095:
spec_txt_example_position: 454
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__096:
+06_05_00__inlines__emphasis_and_strong_emphasis__096:
spec_txt_example_position: 455
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__097:
+06_05_00__inlines__emphasis_and_strong_emphasis__097:
spec_txt_example_position: 456
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__098:
+06_05_00__inlines__emphasis_and_strong_emphasis__098:
spec_txt_example_position: 457
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__099:
+06_05_00__inlines__emphasis_and_strong_emphasis__099:
spec_txt_example_position: 458
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__100:
+06_05_00__inlines__emphasis_and_strong_emphasis__100:
spec_txt_example_position: 459
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__101:
+06_05_00__inlines__emphasis_and_strong_emphasis__101:
spec_txt_example_position: 460
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__102:
+06_05_00__inlines__emphasis_and_strong_emphasis__102:
spec_txt_example_position: 461
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__103:
+06_05_00__inlines__emphasis_and_strong_emphasis__103:
spec_txt_example_position: 462
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__104:
+06_05_00__inlines__emphasis_and_strong_emphasis__104:
spec_txt_example_position: 463
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__105:
+06_05_00__inlines__emphasis_and_strong_emphasis__105:
spec_txt_example_position: 464
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__106:
+06_05_00__inlines__emphasis_and_strong_emphasis__106:
spec_txt_example_position: 465
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__107:
+06_05_00__inlines__emphasis_and_strong_emphasis__107:
spec_txt_example_position: 466
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__108:
+06_05_00__inlines__emphasis_and_strong_emphasis__108:
spec_txt_example_position: 467
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__109:
+06_05_00__inlines__emphasis_and_strong_emphasis__109:
spec_txt_example_position: 468
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__110:
+06_05_00__inlines__emphasis_and_strong_emphasis__110:
spec_txt_example_position: 469
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__111:
+06_05_00__inlines__emphasis_and_strong_emphasis__111:
spec_txt_example_position: 470
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__112:
+06_05_00__inlines__emphasis_and_strong_emphasis__112:
spec_txt_example_position: 471
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__113:
+06_05_00__inlines__emphasis_and_strong_emphasis__113:
spec_txt_example_position: 472
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__114:
+06_05_00__inlines__emphasis_and_strong_emphasis__114:
spec_txt_example_position: 473
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__115:
+06_05_00__inlines__emphasis_and_strong_emphasis__115:
spec_txt_example_position: 474
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__116:
+06_05_00__inlines__emphasis_and_strong_emphasis__116:
spec_txt_example_position: 475
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__117:
+06_05_00__inlines__emphasis_and_strong_emphasis__117:
spec_txt_example_position: 476
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__118:
+06_05_00__inlines__emphasis_and_strong_emphasis__118:
spec_txt_example_position: 477
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__119:
+06_05_00__inlines__emphasis_and_strong_emphasis__119:
spec_txt_example_position: 478
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__120:
+06_05_00__inlines__emphasis_and_strong_emphasis__120:
spec_txt_example_position: 479
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__121:
+06_05_00__inlines__emphasis_and_strong_emphasis__121:
spec_txt_example_position: 480
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__122:
+06_05_00__inlines__emphasis_and_strong_emphasis__122:
spec_txt_example_position: 481
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__123:
+06_05_00__inlines__emphasis_and_strong_emphasis__123:
spec_txt_example_position: 482
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__124:
+06_05_00__inlines__emphasis_and_strong_emphasis__124:
spec_txt_example_position: 483
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__125:
+06_05_00__inlines__emphasis_and_strong_emphasis__125:
spec_txt_example_position: 484
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__126:
+06_05_00__inlines__emphasis_and_strong_emphasis__126:
spec_txt_example_position: 485
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__127:
+06_05_00__inlines__emphasis_and_strong_emphasis__127:
spec_txt_example_position: 486
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__128:
+06_05_00__inlines__emphasis_and_strong_emphasis__128:
spec_txt_example_position: 487
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__129:
+06_05_00__inlines__emphasis_and_strong_emphasis__129:
spec_txt_example_position: 488
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__130:
+06_05_00__inlines__emphasis_and_strong_emphasis__130:
spec_txt_example_position: 489
source_specification: commonmark
-06_05__inlines__emphasis_and_strong_emphasis__131:
+06_05_00__inlines__emphasis_and_strong_emphasis__131:
spec_txt_example_position: 490
source_specification: commonmark
-06_06__inlines__strikethrough_extension__001:
+06_06_00__inlines__strikethrough_extension__001:
spec_txt_example_position: 491
source_specification: github
-06_06__inlines__strikethrough_extension__002:
+06_06_00__inlines__strikethrough_extension__002:
spec_txt_example_position: 492
source_specification: github
-06_07__inlines__links__001:
+06_07_00__inlines__links__001:
spec_txt_example_position: 493
source_specification: commonmark
-06_07__inlines__links__002:
+06_07_00__inlines__links__002:
spec_txt_example_position: 494
source_specification: commonmark
-06_07__inlines__links__003:
+06_07_00__inlines__links__003:
spec_txt_example_position: 495
source_specification: commonmark
-06_07__inlines__links__004:
+06_07_00__inlines__links__004:
spec_txt_example_position: 496
source_specification: commonmark
-06_07__inlines__links__005:
+06_07_00__inlines__links__005:
spec_txt_example_position: 497
source_specification: commonmark
-06_07__inlines__links__006:
+06_07_00__inlines__links__006:
spec_txt_example_position: 498
source_specification: commonmark
-06_07__inlines__links__007:
+06_07_00__inlines__links__007:
spec_txt_example_position: 499
source_specification: commonmark
-06_07__inlines__links__008:
+06_07_00__inlines__links__008:
spec_txt_example_position: 500
source_specification: commonmark
-06_07__inlines__links__009:
+06_07_00__inlines__links__009:
spec_txt_example_position: 501
source_specification: commonmark
-06_07__inlines__links__010:
+06_07_00__inlines__links__010:
spec_txt_example_position: 502
source_specification: commonmark
-06_07__inlines__links__011:
+06_07_00__inlines__links__011:
spec_txt_example_position: 503
source_specification: commonmark
-06_07__inlines__links__012:
+06_07_00__inlines__links__012:
spec_txt_example_position: 504
source_specification: commonmark
-06_07__inlines__links__013:
+06_07_00__inlines__links__013:
spec_txt_example_position: 505
source_specification: commonmark
-06_07__inlines__links__014:
+06_07_00__inlines__links__014:
spec_txt_example_position: 506
source_specification: commonmark
-06_07__inlines__links__015:
+06_07_00__inlines__links__015:
spec_txt_example_position: 507
source_specification: commonmark
-06_07__inlines__links__016:
+06_07_00__inlines__links__016:
spec_txt_example_position: 508
source_specification: commonmark
-06_07__inlines__links__017:
+06_07_00__inlines__links__017:
spec_txt_example_position: 509
source_specification: commonmark
-06_07__inlines__links__018:
+06_07_00__inlines__links__018:
spec_txt_example_position: 510
source_specification: commonmark
-06_07__inlines__links__019:
+06_07_00__inlines__links__019:
spec_txt_example_position: 511
source_specification: commonmark
-06_07__inlines__links__020:
+06_07_00__inlines__links__020:
spec_txt_example_position: 512
source_specification: commonmark
-06_07__inlines__links__021:
+06_07_00__inlines__links__021:
spec_txt_example_position: 513
source_specification: commonmark
-06_07__inlines__links__022:
+06_07_00__inlines__links__022:
spec_txt_example_position: 514
source_specification: commonmark
-06_07__inlines__links__023:
+06_07_00__inlines__links__023:
spec_txt_example_position: 515
source_specification: commonmark
-06_07__inlines__links__024:
+06_07_00__inlines__links__024:
spec_txt_example_position: 516
source_specification: commonmark
-06_07__inlines__links__025:
+06_07_00__inlines__links__025:
spec_txt_example_position: 517
source_specification: commonmark
-06_07__inlines__links__026:
+06_07_00__inlines__links__026:
spec_txt_example_position: 518
source_specification: commonmark
-06_07__inlines__links__027:
+06_07_00__inlines__links__027:
spec_txt_example_position: 519
source_specification: commonmark
-06_07__inlines__links__028:
+06_07_00__inlines__links__028:
spec_txt_example_position: 520
source_specification: commonmark
-06_07__inlines__links__029:
+06_07_00__inlines__links__029:
spec_txt_example_position: 521
source_specification: commonmark
-06_07__inlines__links__030:
+06_07_00__inlines__links__030:
spec_txt_example_position: 522
source_specification: commonmark
-06_07__inlines__links__031:
+06_07_00__inlines__links__031:
spec_txt_example_position: 523
source_specification: commonmark
-06_07__inlines__links__032:
+06_07_00__inlines__links__032:
spec_txt_example_position: 524
source_specification: commonmark
-06_07__inlines__links__033:
+06_07_00__inlines__links__033:
spec_txt_example_position: 525
source_specification: commonmark
-06_07__inlines__links__034:
+06_07_00__inlines__links__034:
spec_txt_example_position: 526
source_specification: commonmark
-06_07__inlines__links__035:
+06_07_00__inlines__links__035:
spec_txt_example_position: 527
source_specification: commonmark
-06_07__inlines__links__036:
+06_07_00__inlines__links__036:
spec_txt_example_position: 528
source_specification: commonmark
-06_07__inlines__links__037:
+06_07_00__inlines__links__037:
spec_txt_example_position: 529
source_specification: commonmark
-06_07__inlines__links__038:
+06_07_00__inlines__links__038:
spec_txt_example_position: 530
source_specification: commonmark
-06_07__inlines__links__039:
+06_07_00__inlines__links__039:
spec_txt_example_position: 531
source_specification: commonmark
-06_07__inlines__links__040:
+06_07_00__inlines__links__040:
spec_txt_example_position: 532
source_specification: commonmark
-06_07__inlines__links__041:
+06_07_00__inlines__links__041:
spec_txt_example_position: 533
source_specification: commonmark
-06_07__inlines__links__042:
+06_07_00__inlines__links__042:
spec_txt_example_position: 534
source_specification: commonmark
-06_07__inlines__links__043:
+06_07_00__inlines__links__043:
spec_txt_example_position: 535
source_specification: commonmark
-06_07__inlines__links__044:
+06_07_00__inlines__links__044:
spec_txt_example_position: 536
source_specification: commonmark
-06_07__inlines__links__045:
+06_07_00__inlines__links__045:
spec_txt_example_position: 537
source_specification: commonmark
-06_07__inlines__links__046:
+06_07_00__inlines__links__046:
spec_txt_example_position: 538
source_specification: commonmark
-06_07__inlines__links__047:
+06_07_00__inlines__links__047:
spec_txt_example_position: 539
source_specification: commonmark
-06_07__inlines__links__048:
+06_07_00__inlines__links__048:
spec_txt_example_position: 540
source_specification: commonmark
-06_07__inlines__links__049:
+06_07_00__inlines__links__049:
spec_txt_example_position: 541
source_specification: commonmark
-06_07__inlines__links__050:
+06_07_00__inlines__links__050:
spec_txt_example_position: 542
source_specification: commonmark
-06_07__inlines__links__051:
+06_07_00__inlines__links__051:
spec_txt_example_position: 543
source_specification: commonmark
-06_07__inlines__links__052:
+06_07_00__inlines__links__052:
spec_txt_example_position: 544
source_specification: commonmark
-06_07__inlines__links__053:
+06_07_00__inlines__links__053:
spec_txt_example_position: 545
source_specification: commonmark
-06_07__inlines__links__054:
+06_07_00__inlines__links__054:
spec_txt_example_position: 546
source_specification: commonmark
-06_07__inlines__links__055:
+06_07_00__inlines__links__055:
spec_txt_example_position: 547
source_specification: commonmark
-06_07__inlines__links__056:
+06_07_00__inlines__links__056:
spec_txt_example_position: 548
source_specification: commonmark
-06_07__inlines__links__057:
+06_07_00__inlines__links__057:
spec_txt_example_position: 549
source_specification: commonmark
-06_07__inlines__links__058:
+06_07_00__inlines__links__058:
spec_txt_example_position: 550
source_specification: commonmark
-06_07__inlines__links__059:
+06_07_00__inlines__links__059:
spec_txt_example_position: 551
source_specification: commonmark
-06_07__inlines__links__060:
+06_07_00__inlines__links__060:
spec_txt_example_position: 552
source_specification: commonmark
-06_07__inlines__links__061:
+06_07_00__inlines__links__061:
spec_txt_example_position: 553
source_specification: commonmark
-06_07__inlines__links__062:
+06_07_00__inlines__links__062:
spec_txt_example_position: 554
source_specification: commonmark
-06_07__inlines__links__063:
+06_07_00__inlines__links__063:
spec_txt_example_position: 555
source_specification: commonmark
-06_07__inlines__links__064:
+06_07_00__inlines__links__064:
spec_txt_example_position: 556
source_specification: commonmark
-06_07__inlines__links__065:
+06_07_00__inlines__links__065:
spec_txt_example_position: 557
source_specification: commonmark
-06_07__inlines__links__066:
+06_07_00__inlines__links__066:
spec_txt_example_position: 558
source_specification: commonmark
-06_07__inlines__links__067:
+06_07_00__inlines__links__067:
spec_txt_example_position: 559
source_specification: commonmark
-06_07__inlines__links__068:
+06_07_00__inlines__links__068:
spec_txt_example_position: 560
source_specification: commonmark
-06_07__inlines__links__069:
+06_07_00__inlines__links__069:
spec_txt_example_position: 561
source_specification: commonmark
-06_07__inlines__links__070:
+06_07_00__inlines__links__070:
spec_txt_example_position: 562
source_specification: commonmark
-06_07__inlines__links__071:
+06_07_00__inlines__links__071:
spec_txt_example_position: 563
source_specification: commonmark
-06_07__inlines__links__072:
+06_07_00__inlines__links__072:
spec_txt_example_position: 564
source_specification: commonmark
-06_07__inlines__links__073:
+06_07_00__inlines__links__073:
spec_txt_example_position: 565
source_specification: commonmark
-06_07__inlines__links__074:
+06_07_00__inlines__links__074:
spec_txt_example_position: 566
source_specification: commonmark
-06_07__inlines__links__075:
+06_07_00__inlines__links__075:
spec_txt_example_position: 567
source_specification: commonmark
-06_07__inlines__links__076:
+06_07_00__inlines__links__076:
spec_txt_example_position: 568
source_specification: commonmark
-06_07__inlines__links__077:
+06_07_00__inlines__links__077:
spec_txt_example_position: 569
source_specification: commonmark
-06_07__inlines__links__078:
+06_07_00__inlines__links__078:
spec_txt_example_position: 570
source_specification: commonmark
-06_07__inlines__links__079:
+06_07_00__inlines__links__079:
spec_txt_example_position: 571
source_specification: commonmark
-06_07__inlines__links__080:
+06_07_00__inlines__links__080:
spec_txt_example_position: 572
source_specification: commonmark
-06_07__inlines__links__081:
+06_07_00__inlines__links__081:
spec_txt_example_position: 573
source_specification: commonmark
-06_07__inlines__links__082:
+06_07_00__inlines__links__082:
spec_txt_example_position: 574
source_specification: commonmark
-06_07__inlines__links__083:
+06_07_00__inlines__links__083:
spec_txt_example_position: 575
source_specification: commonmark
-06_07__inlines__links__084:
+06_07_00__inlines__links__084:
spec_txt_example_position: 576
source_specification: commonmark
-06_07__inlines__links__085:
+06_07_00__inlines__links__085:
spec_txt_example_position: 577
source_specification: commonmark
-06_07__inlines__links__086:
+06_07_00__inlines__links__086:
spec_txt_example_position: 578
source_specification: commonmark
-06_07__inlines__links__087:
+06_07_00__inlines__links__087:
spec_txt_example_position: 579
source_specification: commonmark
-06_08__inlines__images__001:
+06_08_00__inlines__images__001:
spec_txt_example_position: 580
source_specification: commonmark
-06_08__inlines__images__002:
+06_08_00__inlines__images__002:
spec_txt_example_position: 581
source_specification: commonmark
-06_08__inlines__images__003:
+06_08_00__inlines__images__003:
spec_txt_example_position: 582
source_specification: commonmark
-06_08__inlines__images__004:
+06_08_00__inlines__images__004:
spec_txt_example_position: 583
source_specification: commonmark
-06_08__inlines__images__005:
+06_08_00__inlines__images__005:
spec_txt_example_position: 584
source_specification: commonmark
-06_08__inlines__images__006:
+06_08_00__inlines__images__006:
spec_txt_example_position: 585
source_specification: commonmark
-06_08__inlines__images__007:
+06_08_00__inlines__images__007:
spec_txt_example_position: 586
source_specification: commonmark
-06_08__inlines__images__008:
+06_08_00__inlines__images__008:
spec_txt_example_position: 587
source_specification: commonmark
-06_08__inlines__images__009:
+06_08_00__inlines__images__009:
spec_txt_example_position: 588
source_specification: commonmark
-06_08__inlines__images__010:
+06_08_00__inlines__images__010:
spec_txt_example_position: 589
source_specification: commonmark
-06_08__inlines__images__011:
+06_08_00__inlines__images__011:
spec_txt_example_position: 590
source_specification: commonmark
-06_08__inlines__images__012:
+06_08_00__inlines__images__012:
spec_txt_example_position: 591
source_specification: commonmark
-06_08__inlines__images__013:
+06_08_00__inlines__images__013:
spec_txt_example_position: 592
source_specification: commonmark
-06_08__inlines__images__014:
+06_08_00__inlines__images__014:
spec_txt_example_position: 593
source_specification: commonmark
-06_08__inlines__images__015:
+06_08_00__inlines__images__015:
spec_txt_example_position: 594
source_specification: commonmark
-06_08__inlines__images__016:
+06_08_00__inlines__images__016:
spec_txt_example_position: 595
source_specification: commonmark
-06_08__inlines__images__017:
+06_08_00__inlines__images__017:
spec_txt_example_position: 596
source_specification: commonmark
-06_08__inlines__images__018:
+06_08_00__inlines__images__018:
spec_txt_example_position: 597
source_specification: commonmark
-06_08__inlines__images__019:
+06_08_00__inlines__images__019:
spec_txt_example_position: 598
source_specification: commonmark
-06_08__inlines__images__020:
+06_08_00__inlines__images__020:
spec_txt_example_position: 599
source_specification: commonmark
-06_08__inlines__images__021:
+06_08_00__inlines__images__021:
spec_txt_example_position: 600
source_specification: commonmark
-06_08__inlines__images__022:
+06_08_00__inlines__images__022:
spec_txt_example_position: 601
source_specification: commonmark
-06_09__inlines__autolinks__001:
+06_09_00__inlines__autolinks__001:
spec_txt_example_position: 602
source_specification: commonmark
-06_09__inlines__autolinks__002:
+06_09_00__inlines__autolinks__002:
spec_txt_example_position: 603
source_specification: commonmark
-06_09__inlines__autolinks__003:
+06_09_00__inlines__autolinks__003:
spec_txt_example_position: 604
source_specification: commonmark
-06_09__inlines__autolinks__004:
+06_09_00__inlines__autolinks__004:
spec_txt_example_position: 605
source_specification: commonmark
-06_09__inlines__autolinks__005:
+06_09_00__inlines__autolinks__005:
spec_txt_example_position: 606
source_specification: commonmark
-06_09__inlines__autolinks__006:
+06_09_00__inlines__autolinks__006:
spec_txt_example_position: 607
source_specification: commonmark
-06_09__inlines__autolinks__007:
+06_09_00__inlines__autolinks__007:
spec_txt_example_position: 608
source_specification: commonmark
-06_09__inlines__autolinks__008:
+06_09_00__inlines__autolinks__008:
spec_txt_example_position: 609
source_specification: commonmark
-06_09__inlines__autolinks__009:
+06_09_00__inlines__autolinks__009:
spec_txt_example_position: 610
source_specification: commonmark
-06_09__inlines__autolinks__010:
+06_09_00__inlines__autolinks__010:
spec_txt_example_position: 611
source_specification: commonmark
-06_09__inlines__autolinks__011:
+06_09_00__inlines__autolinks__011:
spec_txt_example_position: 612
source_specification: commonmark
-06_09__inlines__autolinks__012:
+06_09_00__inlines__autolinks__012:
spec_txt_example_position: 613
source_specification: commonmark
-06_09__inlines__autolinks__013:
+06_09_00__inlines__autolinks__013:
spec_txt_example_position: 614
source_specification: commonmark
-06_09__inlines__autolinks__014:
+06_09_00__inlines__autolinks__014:
spec_txt_example_position: 615
source_specification: commonmark
-06_09__inlines__autolinks__015:
+06_09_00__inlines__autolinks__015:
spec_txt_example_position: 616
source_specification: commonmark
-06_09__inlines__autolinks__016:
+06_09_00__inlines__autolinks__016:
spec_txt_example_position: 617
source_specification: commonmark
-06_09__inlines__autolinks__017:
+06_09_00__inlines__autolinks__017:
spec_txt_example_position: 618
source_specification: commonmark
-06_09__inlines__autolinks__018:
+06_09_00__inlines__autolinks__018:
spec_txt_example_position: 619
source_specification: commonmark
-06_09__inlines__autolinks__019:
+06_09_00__inlines__autolinks__019:
spec_txt_example_position: 620
source_specification: commonmark
-06_10__inlines__autolinks_extension__001:
+06_10_00__inlines__autolinks_extension__001:
spec_txt_example_position: 621
source_specification: github
-06_10__inlines__autolinks_extension__002:
+06_10_00__inlines__autolinks_extension__002:
spec_txt_example_position: 622
source_specification: github
-06_10__inlines__autolinks_extension__003:
+06_10_00__inlines__autolinks_extension__003:
spec_txt_example_position: 623
source_specification: github
-06_10__inlines__autolinks_extension__004:
+06_10_00__inlines__autolinks_extension__004:
spec_txt_example_position: 624
source_specification: github
-06_10__inlines__autolinks_extension__005:
+06_10_00__inlines__autolinks_extension__005:
spec_txt_example_position: 625
source_specification: github
-06_10__inlines__autolinks_extension__006:
+06_10_00__inlines__autolinks_extension__006:
spec_txt_example_position: 626
source_specification: github
-06_10__inlines__autolinks_extension__007:
+06_10_00__inlines__autolinks_extension__007:
spec_txt_example_position: 627
source_specification: github
-06_10__inlines__autolinks_extension__008:
+06_10_00__inlines__autolinks_extension__008:
spec_txt_example_position: 628
source_specification: github
-06_10__inlines__autolinks_extension__009:
+06_10_00__inlines__autolinks_extension__009:
spec_txt_example_position: 629
source_specification: github
-06_10__inlines__autolinks_extension__010:
+06_10_00__inlines__autolinks_extension__010:
spec_txt_example_position: 630
source_specification: github
-06_10__inlines__autolinks_extension__011:
+06_10_00__inlines__autolinks_extension__011:
spec_txt_example_position: 631
source_specification: github
-06_11__inlines__raw_html__001:
+06_11_00__inlines__raw_html__001:
spec_txt_example_position: 632
source_specification: commonmark
-06_11__inlines__raw_html__002:
+06_11_00__inlines__raw_html__002:
spec_txt_example_position: 633
source_specification: commonmark
-06_11__inlines__raw_html__003:
+06_11_00__inlines__raw_html__003:
spec_txt_example_position: 634
source_specification: commonmark
-06_11__inlines__raw_html__004:
+06_11_00__inlines__raw_html__004:
spec_txt_example_position: 635
source_specification: commonmark
-06_11__inlines__raw_html__005:
+06_11_00__inlines__raw_html__005:
spec_txt_example_position: 636
source_specification: commonmark
-06_11__inlines__raw_html__006:
+06_11_00__inlines__raw_html__006:
spec_txt_example_position: 637
source_specification: commonmark
-06_11__inlines__raw_html__007:
+06_11_00__inlines__raw_html__007:
spec_txt_example_position: 638
source_specification: commonmark
-06_11__inlines__raw_html__008:
+06_11_00__inlines__raw_html__008:
spec_txt_example_position: 639
source_specification: commonmark
-06_11__inlines__raw_html__009:
+06_11_00__inlines__raw_html__009:
spec_txt_example_position: 640
source_specification: commonmark
-06_11__inlines__raw_html__010:
+06_11_00__inlines__raw_html__010:
spec_txt_example_position: 641
source_specification: commonmark
-06_11__inlines__raw_html__011:
+06_11_00__inlines__raw_html__011:
spec_txt_example_position: 642
source_specification: commonmark
-06_11__inlines__raw_html__012:
+06_11_00__inlines__raw_html__012:
spec_txt_example_position: 643
source_specification: commonmark
-06_11__inlines__raw_html__013:
+06_11_00__inlines__raw_html__013:
spec_txt_example_position: 644
source_specification: commonmark
-06_11__inlines__raw_html__014:
+06_11_00__inlines__raw_html__014:
spec_txt_example_position: 645
source_specification: commonmark
-06_11__inlines__raw_html__015:
+06_11_00__inlines__raw_html__015:
spec_txt_example_position: 646
source_specification: commonmark
-06_11__inlines__raw_html__016:
+06_11_00__inlines__raw_html__016:
spec_txt_example_position: 647
source_specification: commonmark
-06_11__inlines__raw_html__017:
+06_11_00__inlines__raw_html__017:
spec_txt_example_position: 648
source_specification: commonmark
-06_11__inlines__raw_html__018:
+06_11_00__inlines__raw_html__018:
spec_txt_example_position: 649
source_specification: commonmark
-06_11__inlines__raw_html__019:
+06_11_00__inlines__raw_html__019:
spec_txt_example_position: 650
source_specification: commonmark
-06_11__inlines__raw_html__020:
+06_11_00__inlines__raw_html__020:
spec_txt_example_position: 651
source_specification: commonmark
-06_11__inlines__raw_html__021:
+06_11_00__inlines__raw_html__021:
spec_txt_example_position: 652
source_specification: commonmark
-06_12__inlines__disallowed_raw_html_extension__001:
+06_12_00__inlines__disallowed_raw_html_extension__001:
spec_txt_example_position: 653
source_specification: github
-06_13__inlines__hard_line_breaks__001:
+06_13_00__inlines__hard_line_breaks__001:
spec_txt_example_position: 654
source_specification: commonmark
-06_13__inlines__hard_line_breaks__002:
+06_13_00__inlines__hard_line_breaks__002:
spec_txt_example_position: 655
source_specification: commonmark
-06_13__inlines__hard_line_breaks__003:
+06_13_00__inlines__hard_line_breaks__003:
spec_txt_example_position: 656
source_specification: commonmark
-06_13__inlines__hard_line_breaks__004:
+06_13_00__inlines__hard_line_breaks__004:
spec_txt_example_position: 657
source_specification: commonmark
-06_13__inlines__hard_line_breaks__005:
+06_13_00__inlines__hard_line_breaks__005:
spec_txt_example_position: 658
source_specification: commonmark
-06_13__inlines__hard_line_breaks__006:
+06_13_00__inlines__hard_line_breaks__006:
spec_txt_example_position: 659
source_specification: commonmark
-06_13__inlines__hard_line_breaks__007:
+06_13_00__inlines__hard_line_breaks__007:
spec_txt_example_position: 660
source_specification: commonmark
-06_13__inlines__hard_line_breaks__008:
+06_13_00__inlines__hard_line_breaks__008:
spec_txt_example_position: 661
source_specification: commonmark
-06_13__inlines__hard_line_breaks__009:
+06_13_00__inlines__hard_line_breaks__009:
spec_txt_example_position: 662
source_specification: commonmark
-06_13__inlines__hard_line_breaks__010:
+06_13_00__inlines__hard_line_breaks__010:
spec_txt_example_position: 663
source_specification: commonmark
-06_13__inlines__hard_line_breaks__011:
+06_13_00__inlines__hard_line_breaks__011:
spec_txt_example_position: 664
source_specification: commonmark
-06_13__inlines__hard_line_breaks__012:
+06_13_00__inlines__hard_line_breaks__012:
spec_txt_example_position: 665
source_specification: commonmark
-06_13__inlines__hard_line_breaks__013:
+06_13_00__inlines__hard_line_breaks__013:
spec_txt_example_position: 666
source_specification: commonmark
-06_13__inlines__hard_line_breaks__014:
+06_13_00__inlines__hard_line_breaks__014:
spec_txt_example_position: 667
source_specification: commonmark
-06_13__inlines__hard_line_breaks__015:
+06_13_00__inlines__hard_line_breaks__015:
spec_txt_example_position: 668
source_specification: commonmark
-06_14__inlines__soft_line_breaks__001:
+06_14_00__inlines__soft_line_breaks__001:
spec_txt_example_position: 669
source_specification: commonmark
-06_14__inlines__soft_line_breaks__002:
+06_14_00__inlines__soft_line_breaks__002:
spec_txt_example_position: 670
source_specification: commonmark
-06_15__inlines__textual_content__001:
+06_15_00__inlines__textual_content__001:
spec_txt_example_position: 671
source_specification: commonmark
-06_15__inlines__textual_content__002:
+06_15_00__inlines__textual_content__002:
spec_txt_example_position: 672
source_specification: commonmark
-06_15__inlines__textual_content__003:
+06_15_00__inlines__textual_content__003:
spec_txt_example_position: 673
source_specification: commonmark
-07_01__gitlab_specific_markdown__footnotes__001:
+07_01_00__gitlab_specific_markdown__footnotes__001:
spec_txt_example_position: 674
source_specification: gitlab
-07_02__gitlab_specific_markdown__task_list_items__001:
+07_02_00__gitlab_specific_markdown__task_list_items__001:
spec_txt_example_position: 675
source_specification: gitlab
-07_02__gitlab_specific_markdown__task_list_items__002:
+07_02_00__gitlab_specific_markdown__task_list_items__002:
spec_txt_example_position: 676
source_specification: gitlab
-07_02__gitlab_specific_markdown__task_list_items__003:
+07_02_00__gitlab_specific_markdown__task_list_items__003:
spec_txt_example_position: 677
source_specification: gitlab
-07_02__gitlab_specific_markdown__task_list_items__004:
+07_02_00__gitlab_specific_markdown__task_list_items__004:
spec_txt_example_position: 678
source_specification: gitlab
-07_03__gitlab_specific_markdown__front_matter__001:
+07_03_00__gitlab_specific_markdown__front_matter__001:
spec_txt_example_position: 679
source_specification: gitlab
-07_03__gitlab_specific_markdown__front_matter__002:
+07_03_00__gitlab_specific_markdown__front_matter__002:
spec_txt_example_position: 680
source_specification: gitlab
-07_03__gitlab_specific_markdown__front_matter__003:
+07_03_00__gitlab_specific_markdown__front_matter__003:
spec_txt_example_position: 681
source_specification: gitlab
-07_03__gitlab_specific_markdown__front_matter__004:
+07_03_00__gitlab_specific_markdown__front_matter__004:
spec_txt_example_position: 682
source_specification: gitlab
-07_03__gitlab_specific_markdown__front_matter__005:
+07_03_00__gitlab_specific_markdown__front_matter__005:
spec_txt_example_position: 683
source_specification: gitlab
+07_04_00__gitlab_specific_markdown__audio__001:
+ spec_txt_example_position: 684
+ source_specification: gitlab
+07_04_00__gitlab_specific_markdown__audio__002:
+ spec_txt_example_position: 685
+ source_specification: gitlab
+07_05_00__gitlab_specific_markdown__video__001:
+ spec_txt_example_position: 686
+ source_specification: gitlab
+07_05_00__gitlab_specific_markdown__video__002:
+ spec_txt_example_position: 687
+ source_specification: gitlab
+07_06_00__gitlab_specific_markdown__table_of_contents__001:
+ spec_txt_example_position: 688
+ source_specification: gitlab
+07_06_00__gitlab_specific_markdown__table_of_contents__002:
+ spec_txt_example_position: 689
+ source_specification: gitlab
+07_06_00__gitlab_specific_markdown__table_of_contents__003:
+ spec_txt_example_position: 690
+ source_specification: gitlab
+07_06_00__gitlab_specific_markdown__table_of_contents__004:
+ spec_txt_example_position: 691
+ source_specification: gitlab
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__001:
+ spec_txt_example_position: 692
+ source_specification: gitlab
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__002:
+ spec_txt_example_position: 693
+ source_specification: gitlab
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__003:
+ spec_txt_example_position: 694
+ source_specification: gitlab
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__004:
+ spec_txt_example_position: 695
+ source_specification: gitlab
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__005:
+ spec_txt_example_position: 696
+ source_specification: gitlab
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__006:
+ spec_txt_example_position: 697
+ source_specification: gitlab
diff --git a/glfm_specification/example_snapshots/html.yml b/glfm_specification/example_snapshots/html.yml
index eda78a29da4..e683c37a7ac 100644
--- a/glfm_specification/example_snapshots/html.yml
+++ b/glfm_specification/example_snapshots/html.yml
@@ -1,19 +1,19 @@
---
-02_01__preliminaries__tabs__001:
+02_01_00__preliminaries__tabs__001:
canonical: "<pre><code>foo\tbaz\t\tbim\n</code></pre>\n"
static: "<div class=\"gl-relative markdown-code-block js-markdown-code\">\n<pre
data-sourcepos=\"1:2-1:13\" class=\"code highlight js-syntax-highlight language-plaintext\"
lang=\"plaintext\" data-canonical-lang=\"\" v-pre=\"true\"><code><span id=\"LC1\"
class=\"line\" lang=\"plaintext\">foo\tbaz\t\tbim</span></code></pre>\n<copy-code></copy-code>\n</div>"
wysiwyg: "<pre class=\"content-editor-code-block undefined code highlight\"><code>foo\tbaz\t\tbim</code></pre>"
-02_01__preliminaries__tabs__002:
+02_01_00__preliminaries__tabs__002:
canonical: "<pre><code>foo\tbaz\t\tbim\n</code></pre>\n"
static: "<div class=\"gl-relative markdown-code-block js-markdown-code\">\n<pre
data-sourcepos=\"1:4-1:15\" class=\"code highlight js-syntax-highlight language-plaintext\"
lang=\"plaintext\" data-canonical-lang=\"\" v-pre=\"true\"><code><span id=\"LC1\"
class=\"line\" lang=\"plaintext\">foo\tbaz\t\tbim</span></code></pre>\n<copy-code></copy-code>\n</div>"
wysiwyg: "<pre class=\"content-editor-code-block undefined code highlight\"><code>foo\tbaz\t\tbim</code></pre>"
-02_01__preliminaries__tabs__003:
+02_01_00__preliminaries__tabs__003:
canonical: "<pre><code>a\ta\ná½\ta\n</code></pre>\n"
static: "<div class=\"gl-relative markdown-code-block js-markdown-code\">\n<pre
data-sourcepos=\"1:5-2:9\" class=\"code highlight js-syntax-highlight language-plaintext\"
@@ -21,7 +21,7 @@
class=\"line\" lang=\"plaintext\">a\ta</span>\n<span id=\"LC2\" class=\"line\"
lang=\"plaintext\">á½\ta</span></code></pre>\n<copy-code></copy-code>\n</div>"
wysiwyg: "<pre class=\"content-editor-code-block undefined code highlight\"><code>a\ta\ná½\ta</code></pre>"
-02_01__preliminaries__tabs__004:
+02_01_00__preliminaries__tabs__004:
canonical: |
<ul>
<li>
@@ -38,7 +38,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>foo</p><p>bar</p></li></ul>
-02_01__preliminaries__tabs__005:
+02_01_00__preliminaries__tabs__005:
canonical: |
<ul>
<li>
@@ -59,7 +59,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>foo</p><pre class="content-editor-code-block undefined code highlight"><code> bar</code></pre></li></ul>
-02_01__preliminaries__tabs__006:
+02_01_00__preliminaries__tabs__006:
canonical: |
<blockquote>
<pre><code> foo
@@ -74,7 +74,7 @@
</blockquote>
wysiwyg: |-
<blockquote multiline="false"><pre class="content-editor-code-block undefined code highlight"><code> foo</code></pre></blockquote>
-02_01__preliminaries__tabs__007:
+02_01_00__preliminaries__tabs__007:
canonical: |
<ul>
<li>
@@ -93,7 +93,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p></p><pre class="content-editor-code-block undefined code highlight"><code> foo</code></pre></li></ul>
-02_01__preliminaries__tabs__008:
+02_01_00__preliminaries__tabs__008:
canonical: |
<pre><code>foo
bar
@@ -107,7 +107,7 @@
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>foo
bar</code></pre>
-02_01__preliminaries__tabs__009:
+02_01_00__preliminaries__tabs__009:
canonical: |
<ul>
<li>foo
@@ -134,7 +134,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>foo</p><ul bullet="*"><li><p>bar</p><ul bullet="*"><li><p>baz</p></li></ul></li></ul></li></ul>
-02_01__preliminaries__tabs__010:
+02_01_00__preliminaries__tabs__010:
canonical: |
<h1>Foo</h1>
static: |-
@@ -142,14 +142,14 @@
<a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>Foo</h1>
wysiwyg: |-
<h1>Foo</h1>
-02_01__preliminaries__tabs__011:
+02_01_00__preliminaries__tabs__011:
canonical: |
<hr />
static: |-
<hr data-sourcepos="1:1-1:6">
wysiwyg: |-
<hr>
-03_01__blocks_and_inlines__precedence__001:
+03_01_00__blocks_and_inlines__precedence__001:
canonical: |
<ul>
<li>`one</li>
@@ -162,7 +162,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>`one</p></li><li><p>two`</p></li></ul>
-04_01__leaf_blocks__thematic_breaks__001:
+04_01_00__leaf_blocks__thematic_breaks__001:
canonical: |
<hr />
<hr />
@@ -175,21 +175,21 @@
<hr>
<hr>
<hr>
-04_01__leaf_blocks__thematic_breaks__002:
+04_01_00__leaf_blocks__thematic_breaks__002:
canonical: |
<p>+++</p>
static: |-
<p data-sourcepos="1:1-1:3" dir="auto">+++</p>
wysiwyg: |-
<p>+++</p>
-04_01__leaf_blocks__thematic_breaks__003:
+04_01_00__leaf_blocks__thematic_breaks__003:
canonical: |
<p>===</p>
static: |-
<p data-sourcepos="1:1-1:3" dir="auto">===</p>
wysiwyg: |-
<p>===</p>
-04_01__leaf_blocks__thematic_breaks__004:
+04_01_00__leaf_blocks__thematic_breaks__004:
canonical: |
<p>--
**
@@ -202,7 +202,7 @@
<p>--
**
__</p>
-04_01__leaf_blocks__thematic_breaks__005:
+04_01_00__leaf_blocks__thematic_breaks__005:
canonical: |
<hr />
<hr />
@@ -215,7 +215,7 @@
<hr>
<hr>
<hr>
-04_01__leaf_blocks__thematic_breaks__006:
+04_01_00__leaf_blocks__thematic_breaks__006:
canonical: |
<pre><code>***
</code></pre>
@@ -226,7 +226,7 @@
</div>
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>***</code></pre>
-04_01__leaf_blocks__thematic_breaks__007:
+04_01_00__leaf_blocks__thematic_breaks__007:
canonical: |
<p>Foo
***</p>
@@ -236,42 +236,42 @@
wysiwyg: |-
<p>Foo
***</p>
-04_01__leaf_blocks__thematic_breaks__008:
+04_01_00__leaf_blocks__thematic_breaks__008:
canonical: |
<hr />
static: |-
<hr data-sourcepos="1:1-1:37">
wysiwyg: |-
<hr>
-04_01__leaf_blocks__thematic_breaks__009:
+04_01_00__leaf_blocks__thematic_breaks__009:
canonical: |
<hr />
static: |-
<hr data-sourcepos="1:2-1:6">
wysiwyg: |-
<hr>
-04_01__leaf_blocks__thematic_breaks__010:
+04_01_00__leaf_blocks__thematic_breaks__010:
canonical: |
<hr />
static: |-
<hr data-sourcepos="1:2-1:19">
wysiwyg: |-
<hr>
-04_01__leaf_blocks__thematic_breaks__011:
+04_01_00__leaf_blocks__thematic_breaks__011:
canonical: |
<hr />
static: |-
<hr data-sourcepos="1:1-1:21">
wysiwyg: |-
<hr>
-04_01__leaf_blocks__thematic_breaks__012:
+04_01_00__leaf_blocks__thematic_breaks__012:
canonical: |
<hr />
static: |-
<hr data-sourcepos="1:1-1:11">
wysiwyg: |-
<hr>
-04_01__leaf_blocks__thematic_breaks__013:
+04_01_00__leaf_blocks__thematic_breaks__013:
canonical: |
<p>_ _ _ _ a</p>
<p>a------</p>
@@ -284,14 +284,14 @@
<p>_ _ _ _ a</p>
<p>a------</p>
<p>---a---</p>
-04_01__leaf_blocks__thematic_breaks__014:
+04_01_00__leaf_blocks__thematic_breaks__014:
canonical: |
<p><em>-</em></p>
static: |-
<p data-sourcepos="1:2-1:4" dir="auto"><em>-</em></p>
wysiwyg: |-
<p><em>-</em></p>
-04_01__leaf_blocks__thematic_breaks__015:
+04_01_00__leaf_blocks__thematic_breaks__015:
canonical: |
<ul>
<li>foo</li>
@@ -312,7 +312,7 @@
<ul bullet="*"><li><p>foo</p></li></ul>
<hr>
<ul bullet="*"><li><p>bar</p></li></ul>
-04_01__leaf_blocks__thematic_breaks__016:
+04_01_00__leaf_blocks__thematic_breaks__016:
canonical: |
<p>Foo</p>
<hr />
@@ -325,7 +325,7 @@
<p>Foo</p>
<hr>
<p>bar</p>
-04_01__leaf_blocks__thematic_breaks__017:
+04_01_00__leaf_blocks__thematic_breaks__017:
canonical: |
<h2>Foo</h2>
<p>bar</p>
@@ -336,7 +336,7 @@
wysiwyg: |-
<h2>Foo</h2>
<p>bar</p>
-04_01__leaf_blocks__thematic_breaks__018:
+04_01_00__leaf_blocks__thematic_breaks__018:
canonical: |
<ul>
<li>Foo</li>
@@ -357,7 +357,7 @@
<ul bullet="*"><li><p>Foo</p></li></ul>
<hr>
<ul bullet="*"><li><p>Bar</p></li></ul>
-04_01__leaf_blocks__thematic_breaks__019:
+04_01_00__leaf_blocks__thematic_breaks__019:
canonical: |
<ul>
<li>Foo</li>
@@ -374,7 +374,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>Foo</p></li><li><p></p><hr></li></ul>
-04_02__leaf_blocks__atx_headings__001:
+04_02_00__leaf_blocks__atx_headings__001:
canonical: |
<h1>foo</h1>
<h2>foo</h2>
@@ -402,14 +402,14 @@
<h4>foo</h4>
<h5>foo</h5>
<h6>foo</h6>
-04_02__leaf_blocks__atx_headings__002:
+04_02_00__leaf_blocks__atx_headings__002:
canonical: |
<p>####### foo</p>
static: |-
<p data-sourcepos="1:1-1:11" dir="auto">####### foo</p>
wysiwyg: |-
<p>####### foo</p>
-04_02__leaf_blocks__atx_headings__003:
+04_02_00__leaf_blocks__atx_headings__003:
canonical: |
<p>#5 bolt</p>
<p>#hashtag</p>
@@ -419,14 +419,14 @@
wysiwyg: |-
<p>#5 bolt</p>
<p>#hashtag</p>
-04_02__leaf_blocks__atx_headings__004:
+04_02_00__leaf_blocks__atx_headings__004:
canonical: |
<p>## foo</p>
static: |-
<p data-sourcepos="1:1-1:27" dir="auto"><span>#</span># foo</p>
wysiwyg: |-
<p>## foo</p>
-04_02__leaf_blocks__atx_headings__005:
+04_02_00__leaf_blocks__atx_headings__005:
canonical: |
<h1>foo <em>bar</em> *baz*</h1>
static: |-
@@ -434,7 +434,7 @@
<a id="user-content-foo-bar-baz" class="anchor" href="#foo-bar-baz" aria-hidden="true"></a>foo <em>bar</em> *baz*</h1>
wysiwyg: |-
<h1>foo <em>bar</em> *baz*</h1>
-04_02__leaf_blocks__atx_headings__006:
+04_02_00__leaf_blocks__atx_headings__006:
canonical: |
<h1>foo</h1>
static: |-
@@ -442,7 +442,7 @@
<a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>foo</h1>
wysiwyg: |-
<h1>foo</h1>
-04_02__leaf_blocks__atx_headings__007:
+04_02_00__leaf_blocks__atx_headings__007:
canonical: |
<h3>foo</h3>
<h2>foo</h2>
@@ -458,7 +458,7 @@
<h3>foo</h3>
<h2>foo</h2>
<h1>foo</h1>
-04_02__leaf_blocks__atx_headings__008:
+04_02_00__leaf_blocks__atx_headings__008:
canonical: |
<pre><code># foo
</code></pre>
@@ -469,7 +469,7 @@
</div>
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code># foo</code></pre>
-04_02__leaf_blocks__atx_headings__009:
+04_02_00__leaf_blocks__atx_headings__009:
canonical: |
<p>foo
# bar</p>
@@ -479,7 +479,7 @@
wysiwyg: |-
<p>foo
# bar</p>
-04_02__leaf_blocks__atx_headings__010:
+04_02_00__leaf_blocks__atx_headings__010:
canonical: |
<h2>foo</h2>
<h3>bar</h3>
@@ -491,7 +491,7 @@
wysiwyg: |-
<h2>foo</h2>
<h3>bar</h3>
-04_02__leaf_blocks__atx_headings__011:
+04_02_00__leaf_blocks__atx_headings__011:
canonical: |
<h1>foo</h1>
<h5>foo</h5>
@@ -503,7 +503,7 @@
wysiwyg: |-
<h1>foo</h1>
<h5>foo</h5>
-04_02__leaf_blocks__atx_headings__012:
+04_02_00__leaf_blocks__atx_headings__012:
canonical: |
<h3>foo</h3>
static: |-
@@ -511,7 +511,7 @@
<a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>foo</h3>
wysiwyg: |-
<h3>foo</h3>
-04_02__leaf_blocks__atx_headings__013:
+04_02_00__leaf_blocks__atx_headings__013:
canonical: |
<h3>foo ### b</h3>
static: |-
@@ -519,7 +519,7 @@
<a id="user-content-foo-b" class="anchor" href="#foo-b" aria-hidden="true"></a>foo ### b</h3>
wysiwyg: |-
<h3>foo ### b</h3>
-04_02__leaf_blocks__atx_headings__014:
+04_02_00__leaf_blocks__atx_headings__014:
canonical: |
<h1>foo#</h1>
static: |-
@@ -527,7 +527,7 @@
<a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>foo#</h1>
wysiwyg: |-
<h1>foo#</h1>
-04_02__leaf_blocks__atx_headings__015:
+04_02_00__leaf_blocks__atx_headings__015:
canonical: |
<h3>foo ###</h3>
<h2>foo ###</h2>
@@ -544,7 +544,7 @@
<h3>foo ###</h3>
<h2>foo ###</h2>
<h1>foo #</h1>
-04_02__leaf_blocks__atx_headings__016:
+04_02_00__leaf_blocks__atx_headings__016:
canonical: |
<hr />
<h2>foo</h2>
@@ -558,7 +558,7 @@
<hr>
<h2>foo</h2>
<hr>
-04_02__leaf_blocks__atx_headings__017:
+04_02_00__leaf_blocks__atx_headings__017:
canonical: |
<p>Foo bar</p>
<h1>baz</h1>
@@ -572,7 +572,7 @@
<p>Foo bar</p>
<h1>baz</h1>
<p>Bar foo</p>
-04_02__leaf_blocks__atx_headings__018:
+04_02_00__leaf_blocks__atx_headings__018:
canonical: |
<h2></h2>
<h1></h1>
@@ -585,7 +585,7 @@
<h2></h2>
<h1></h1>
<h3></h3>
-04_03__leaf_blocks__setext_headings__001:
+04_03_00__leaf_blocks__setext_headings__001:
canonical: |
<h1>Foo <em>bar</em></h1>
<h2>Foo <em>bar</em></h2>
@@ -599,7 +599,7 @@
wysiwyg: |-
<h1>Foo <em>bar</em></h1>
<h2>Foo <em>bar</em></h2>
-04_03__leaf_blocks__setext_headings__002:
+04_03_00__leaf_blocks__setext_headings__002:
canonical: |
<h1>Foo <em>bar
baz</em></h1>
@@ -611,7 +611,7 @@
wysiwyg: |-
<h1>Foo <em>bar
baz</em></h1>
-04_03__leaf_blocks__setext_headings__003:
+04_03_00__leaf_blocks__setext_headings__003:
canonical: |
<h1>Foo <em>bar
baz</em></h1>
@@ -623,7 +623,7 @@
wysiwyg: |-
<h1>Foo <em>bar
baz</em></h1>
-04_03__leaf_blocks__setext_headings__004:
+04_03_00__leaf_blocks__setext_headings__004:
canonical: |
<h2>Foo</h2>
<h1>Foo</h1>
@@ -635,7 +635,7 @@
wysiwyg: |-
<h2>Foo</h2>
<h1>Foo</h1>
-04_03__leaf_blocks__setext_headings__005:
+04_03_00__leaf_blocks__setext_headings__005:
canonical: |
<h2>Foo</h2>
<h2>Foo</h2>
@@ -651,7 +651,7 @@
<h2>Foo</h2>
<h2>Foo</h2>
<h1>Foo</h1>
-04_03__leaf_blocks__setext_headings__006:
+04_03_00__leaf_blocks__setext_headings__006:
canonical: |
<pre><code>Foo
---
@@ -674,7 +674,7 @@
Foo</code></pre>
<hr>
-04_03__leaf_blocks__setext_headings__007:
+04_03_00__leaf_blocks__setext_headings__007:
canonical: |
<h2>Foo</h2>
static: |-
@@ -682,7 +682,7 @@
<a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>Foo</h2>
wysiwyg: |-
<h2>Foo</h2>
-04_03__leaf_blocks__setext_headings__008:
+04_03_00__leaf_blocks__setext_headings__008:
canonical: |
<p>Foo
---</p>
@@ -692,7 +692,7 @@
wysiwyg: |-
<p>Foo
---</p>
-04_03__leaf_blocks__setext_headings__009:
+04_03_00__leaf_blocks__setext_headings__009:
canonical: |
<p>Foo
= =</p>
@@ -708,7 +708,7 @@
= =</p>
<p>Foo</p>
<hr>
-04_03__leaf_blocks__setext_headings__010:
+04_03_00__leaf_blocks__setext_headings__010:
canonical: |
<h2>Foo</h2>
static: |-
@@ -716,7 +716,7 @@
<a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>Foo</h2>
wysiwyg: |-
<h2>Foo</h2>
-04_03__leaf_blocks__setext_headings__011:
+04_03_00__leaf_blocks__setext_headings__011:
canonical: |
<h2>Foo\</h2>
static: |-
@@ -724,7 +724,7 @@
<a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>Foo\</h2>
wysiwyg: |-
<h2>Foo\</h2>
-04_03__leaf_blocks__setext_headings__012:
+04_03_00__leaf_blocks__setext_headings__012:
canonical: |
<h2>`Foo</h2>
<p>`</p>
@@ -742,7 +742,7 @@
<p>`</p>
<h2>&lt;a title="a lot</h2>
<p>of dashes"/&gt;</p>
-04_03__leaf_blocks__setext_headings__013:
+04_03_00__leaf_blocks__setext_headings__013:
canonical: |
<blockquote>
<p>Foo</p>
@@ -756,7 +756,7 @@
wysiwyg: |-
<blockquote multiline="false"><p>Foo</p></blockquote>
<hr>
-04_03__leaf_blocks__setext_headings__014:
+04_03_00__leaf_blocks__setext_headings__014:
canonical: |
<blockquote>
<p>foo
@@ -773,7 +773,7 @@
<blockquote multiline="false"><p>foo
bar
===</p></blockquote>
-04_03__leaf_blocks__setext_headings__015:
+04_03_00__leaf_blocks__setext_headings__015:
canonical: |
<ul>
<li>Foo</li>
@@ -787,7 +787,7 @@
wysiwyg: |-
<ul bullet="*"><li><p>Foo</p></li></ul>
<hr>
-04_03__leaf_blocks__setext_headings__016:
+04_03_00__leaf_blocks__setext_headings__016:
canonical: |
<h2>Foo
Bar</h2>
@@ -798,7 +798,7 @@
wysiwyg: |-
<h2>Foo
Bar</h2>
-04_03__leaf_blocks__setext_headings__017:
+04_03_00__leaf_blocks__setext_headings__017:
canonical: |
<hr />
<h2>Foo</h2>
@@ -816,14 +816,14 @@
<pre language="yaml" class="content-editor-code-block undefined code highlight" isfrontmatter="true"><code>Foo</code></pre>
<h2>Bar</h2>
<p>Baz</p>
-04_03__leaf_blocks__setext_headings__018:
+04_03_00__leaf_blocks__setext_headings__018:
canonical: |
<p>====</p>
static: |-
<p data-sourcepos="2:1-2:4" dir="auto">====</p>
wysiwyg: |-
<p>====</p>
-04_03__leaf_blocks__setext_headings__019:
+04_03_00__leaf_blocks__setext_headings__019:
canonical: |
<hr />
<hr />
@@ -834,7 +834,7 @@
</div>
wysiwyg: |-
<pre language="yaml" class="content-editor-code-block undefined code highlight" isfrontmatter="true"><code></code></pre>
-04_03__leaf_blocks__setext_headings__020:
+04_03_00__leaf_blocks__setext_headings__020:
canonical: |
<ul>
<li>foo</li>
@@ -848,7 +848,7 @@
wysiwyg: |-
<ul bullet="*"><li><p>foo</p></li></ul>
<hr>
-04_03__leaf_blocks__setext_headings__021:
+04_03_00__leaf_blocks__setext_headings__021:
canonical: |
<pre><code>foo
</code></pre>
@@ -862,7 +862,7 @@
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>foo</code></pre>
<hr>
-04_03__leaf_blocks__setext_headings__022:
+04_03_00__leaf_blocks__setext_headings__022:
canonical: |
<blockquote>
<p>foo</p>
@@ -876,7 +876,7 @@
wysiwyg: |-
<blockquote multiline="false"><p>foo</p></blockquote>
<hr>
-04_03__leaf_blocks__setext_headings__023:
+04_03_00__leaf_blocks__setext_headings__023:
canonical: |
<h2>&gt; foo</h2>
static: |-
@@ -884,7 +884,7 @@
<a id="user-content--foo" class="anchor" href="#-foo" aria-hidden="true"></a>&gt; foo</h2>
wysiwyg: |-
<h2>&gt; foo</h2>
-04_03__leaf_blocks__setext_headings__024:
+04_03_00__leaf_blocks__setext_headings__024:
canonical: |
<p>Foo</p>
<h2>bar</h2>
@@ -898,7 +898,7 @@
<p>Foo</p>
<h2>bar</h2>
<p>baz</p>
-04_03__leaf_blocks__setext_headings__025:
+04_03_00__leaf_blocks__setext_headings__025:
canonical: |
<p>Foo
bar</p>
@@ -914,7 +914,7 @@
bar</p>
<hr>
<p>baz</p>
-04_03__leaf_blocks__setext_headings__026:
+04_03_00__leaf_blocks__setext_headings__026:
canonical: |
<p>Foo
bar</p>
@@ -930,7 +930,7 @@
bar</p>
<hr>
<p>baz</p>
-04_03__leaf_blocks__setext_headings__027:
+04_03_00__leaf_blocks__setext_headings__027:
canonical: |
<p>Foo
bar
@@ -946,7 +946,7 @@
bar
---
baz</p>
-04_04__leaf_blocks__indented_code_blocks__001:
+04_04_00__leaf_blocks__indented_code_blocks__001:
canonical: |
<pre><code>a simple
indented code block
@@ -960,7 +960,7 @@
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>a simple
indented code block</code></pre>
-04_04__leaf_blocks__indented_code_blocks__002:
+04_04_00__leaf_blocks__indented_code_blocks__002:
canonical: |
<ul>
<li>
@@ -977,7 +977,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>foo</p><p>bar</p></li></ul>
-04_04__leaf_blocks__indented_code_blocks__003:
+04_04_00__leaf_blocks__indented_code_blocks__003:
canonical: |
<ol>
<li>
@@ -998,7 +998,7 @@
</ol>
wysiwyg: |-
<ol parens="false"><li><p>foo</p><ul bullet="*"><li><p>bar</p></li></ul></li></ol>
-04_04__leaf_blocks__indented_code_blocks__004:
+04_04_00__leaf_blocks__indented_code_blocks__004:
canonical: |
<pre><code>&lt;a/&gt;
*hi*
@@ -1018,7 +1018,7 @@
*hi*
- one</code></pre>
-04_04__leaf_blocks__indented_code_blocks__005:
+04_04_00__leaf_blocks__indented_code_blocks__005:
canonical: |
<pre><code>chunk1
@@ -1047,7 +1047,7 @@
chunk3</code></pre>
-04_04__leaf_blocks__indented_code_blocks__006:
+04_04_00__leaf_blocks__indented_code_blocks__006:
canonical: "<pre><code>chunk1\n \n chunk2\n</code></pre>\n"
static: |-
<div class="gl-relative markdown-code-block js-markdown-code">
@@ -1058,7 +1058,7 @@
</div>
wysiwyg: "<pre class=\"content-editor-code-block undefined code highlight\"><code>chunk1\n
\ \n chunk2</code></pre>"
-04_04__leaf_blocks__indented_code_blocks__007:
+04_04_00__leaf_blocks__indented_code_blocks__007:
canonical: |
<p>Foo
bar</p>
@@ -1068,7 +1068,7 @@
wysiwyg: |-
<p>Foo
bar</p>
-04_04__leaf_blocks__indented_code_blocks__008:
+04_04_00__leaf_blocks__indented_code_blocks__008:
canonical: |
<pre><code>foo
</code></pre>
@@ -1082,7 +1082,7 @@
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>foo</code></pre>
<p>bar</p>
-04_04__leaf_blocks__indented_code_blocks__009:
+04_04_00__leaf_blocks__indented_code_blocks__009:
canonical: |
<h1>Heading</h1>
<pre><code>foo
@@ -1111,7 +1111,7 @@
<h2>Heading</h2>
<pre class="content-editor-code-block undefined code highlight"><code>foo</code></pre>
<hr>
-04_04__leaf_blocks__indented_code_blocks__010:
+04_04_00__leaf_blocks__indented_code_blocks__010:
canonical: |
<pre><code> foo
bar
@@ -1125,7 +1125,7 @@
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code> foo
bar</code></pre>
-04_04__leaf_blocks__indented_code_blocks__011:
+04_04_00__leaf_blocks__indented_code_blocks__011:
canonical: |
<pre><code>foo
</code></pre>
@@ -1136,7 +1136,7 @@
</div>
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>foo</code></pre>
-04_04__leaf_blocks__indented_code_blocks__012:
+04_04_00__leaf_blocks__indented_code_blocks__012:
canonical: "<pre><code>foo \n</code></pre>\n"
static: |-
<div class="gl-relative markdown-code-block js-markdown-code">
@@ -1145,7 +1145,7 @@
</div>
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>foo </code></pre>
-04_05__leaf_blocks__fenced_code_blocks__001:
+04_05_00__leaf_blocks__fenced_code_blocks__001:
canonical: |
<pre><code>&lt;
&gt;
@@ -1159,7 +1159,7 @@
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>&lt;
&gt;</code></pre>
-04_05__leaf_blocks__fenced_code_blocks__002:
+04_05_00__leaf_blocks__fenced_code_blocks__002:
canonical: |
<pre><code>&lt;
&gt;
@@ -1173,14 +1173,14 @@
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>&lt;
&gt;</code></pre>
-04_05__leaf_blocks__fenced_code_blocks__003:
+04_05_00__leaf_blocks__fenced_code_blocks__003:
canonical: |
<p><code>foo</code></p>
static: |-
<p data-sourcepos="1:1-3:2" dir="auto"><code>foo</code></p>
wysiwyg: |-
<p><code>foo</code></p>
-04_05__leaf_blocks__fenced_code_blocks__004:
+04_05_00__leaf_blocks__fenced_code_blocks__004:
canonical: |
<pre><code>aaa
~~~
@@ -1194,7 +1194,7 @@
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>aaa
~~~</code></pre>
-04_05__leaf_blocks__fenced_code_blocks__005:
+04_05_00__leaf_blocks__fenced_code_blocks__005:
canonical: |
<pre><code>aaa
```
@@ -1208,7 +1208,7 @@
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>aaa
```</code></pre>
-04_05__leaf_blocks__fenced_code_blocks__006:
+04_05_00__leaf_blocks__fenced_code_blocks__006:
canonical: |
<pre><code>aaa
```
@@ -1222,7 +1222,7 @@
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>aaa
```</code></pre>
-04_05__leaf_blocks__fenced_code_blocks__007:
+04_05_00__leaf_blocks__fenced_code_blocks__007:
canonical: |
<pre><code>aaa
~~~
@@ -1236,7 +1236,7 @@
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>aaa
~~~</code></pre>
-04_05__leaf_blocks__fenced_code_blocks__008:
+04_05_00__leaf_blocks__fenced_code_blocks__008:
canonical: |
<pre><code></code></pre>
static: |-
@@ -1246,7 +1246,7 @@
</div>
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code></code></pre>
-04_05__leaf_blocks__fenced_code_blocks__009:
+04_05_00__leaf_blocks__fenced_code_blocks__009:
canonical: |
<pre><code>
```
@@ -1263,7 +1263,7 @@
<pre class="content-editor-code-block undefined code highlight"><code>
```
aaa</code></pre>
-04_05__leaf_blocks__fenced_code_blocks__010:
+04_05_00__leaf_blocks__fenced_code_blocks__010:
canonical: |
<blockquote>
<pre><code>aaa
@@ -1281,7 +1281,7 @@
wysiwyg: |-
<blockquote multiline="false"><pre class="content-editor-code-block undefined code highlight"><code>aaa</code></pre></blockquote>
<p>bbb</p>
-04_05__leaf_blocks__fenced_code_blocks__011:
+04_05_00__leaf_blocks__fenced_code_blocks__011:
canonical: "<pre><code>\n \n</code></pre>\n"
static: |-
<div class="gl-relative markdown-code-block js-markdown-code">
@@ -1291,7 +1291,7 @@
</div>
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code></code></pre>
-04_05__leaf_blocks__fenced_code_blocks__012:
+04_05_00__leaf_blocks__fenced_code_blocks__012:
canonical: |
<pre><code></code></pre>
static: |-
@@ -1301,7 +1301,7 @@
</div>
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code></code></pre>
-04_05__leaf_blocks__fenced_code_blocks__013:
+04_05_00__leaf_blocks__fenced_code_blocks__013:
canonical: |
<pre><code>aaa
aaa
@@ -1315,7 +1315,7 @@
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>aaa
aaa</code></pre>
-04_05__leaf_blocks__fenced_code_blocks__014:
+04_05_00__leaf_blocks__fenced_code_blocks__014:
canonical: |
<pre><code>aaa
aaa
@@ -1332,7 +1332,7 @@
<pre class="content-editor-code-block undefined code highlight"><code>aaa
aaa
aaa</code></pre>
-04_05__leaf_blocks__fenced_code_blocks__015:
+04_05_00__leaf_blocks__fenced_code_blocks__015:
canonical: |
<pre><code>aaa
aaa
@@ -1349,7 +1349,7 @@
<pre class="content-editor-code-block undefined code highlight"><code>aaa
aaa
aaa</code></pre>
-04_05__leaf_blocks__fenced_code_blocks__016:
+04_05_00__leaf_blocks__fenced_code_blocks__016:
canonical: |
<pre><code>```
aaa
@@ -1366,7 +1366,7 @@
<pre class="content-editor-code-block undefined code highlight"><code>```
aaa
```</code></pre>
-04_05__leaf_blocks__fenced_code_blocks__017:
+04_05_00__leaf_blocks__fenced_code_blocks__017:
canonical: |
<pre><code>aaa
</code></pre>
@@ -1377,7 +1377,7 @@
</div>
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>aaa</code></pre>
-04_05__leaf_blocks__fenced_code_blocks__018:
+04_05_00__leaf_blocks__fenced_code_blocks__018:
canonical: |
<pre><code>aaa
</code></pre>
@@ -1388,7 +1388,7 @@
</div>
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>aaa</code></pre>
-04_05__leaf_blocks__fenced_code_blocks__019:
+04_05_00__leaf_blocks__fenced_code_blocks__019:
canonical: |
<pre><code>aaa
```
@@ -1402,7 +1402,7 @@
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>aaa
```</code></pre>
-04_05__leaf_blocks__fenced_code_blocks__020:
+04_05_00__leaf_blocks__fenced_code_blocks__020:
canonical: |
<p><code> </code>
aaa</p>
@@ -1412,7 +1412,7 @@
wysiwyg: |-
<p>
aaa</p>
-04_05__leaf_blocks__fenced_code_blocks__021:
+04_05_00__leaf_blocks__fenced_code_blocks__021:
canonical: |
<pre><code>aaa
~~~ ~~
@@ -1426,7 +1426,7 @@
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>aaa
~~~ ~~</code></pre>
-04_05__leaf_blocks__fenced_code_blocks__022:
+04_05_00__leaf_blocks__fenced_code_blocks__022:
canonical: |
<p>foo</p>
<pre><code>bar
@@ -1443,7 +1443,7 @@
<p>foo</p>
<pre class="content-editor-code-block undefined code highlight"><code>bar</code></pre>
<p>baz</p>
-04_05__leaf_blocks__fenced_code_blocks__023:
+04_05_00__leaf_blocks__fenced_code_blocks__023:
canonical: |
<h2>foo</h2>
<pre><code>bar
@@ -1462,7 +1462,7 @@
<h2>foo</h2>
<pre class="content-editor-code-block undefined code highlight"><code>bar</code></pre>
<h1>baz</h1>
-04_05__leaf_blocks__fenced_code_blocks__024:
+04_05_00__leaf_blocks__fenced_code_blocks__024:
canonical: |
<pre><code class="language-ruby">def foo(x)
return 3
@@ -1479,7 +1479,7 @@
<pre language="ruby" class="content-editor-code-block undefined code highlight"><code>def foo(x)
return 3
end</code></pre>
-04_05__leaf_blocks__fenced_code_blocks__025:
+04_05_00__leaf_blocks__fenced_code_blocks__025:
canonical: |
<pre><code class="language-ruby">def foo(x)
return 3
@@ -1496,7 +1496,7 @@
<pre language="ruby" class="content-editor-code-block undefined code highlight"><code>def foo(x)
return 3
end</code></pre>
-04_05__leaf_blocks__fenced_code_blocks__026:
+04_05_00__leaf_blocks__fenced_code_blocks__026:
canonical: |
<pre><code class="language-;"></code></pre>
static: |-
@@ -1506,7 +1506,7 @@
</div>
wysiwyg: |-
<pre language=";" class="content-editor-code-block undefined code highlight"><code></code></pre>
-04_05__leaf_blocks__fenced_code_blocks__027:
+04_05_00__leaf_blocks__fenced_code_blocks__027:
canonical: |
<p><code>aa</code>
foo</p>
@@ -1516,7 +1516,7 @@
wysiwyg: |-
<p><code>aa</code>
foo</p>
-04_05__leaf_blocks__fenced_code_blocks__028:
+04_05_00__leaf_blocks__fenced_code_blocks__028:
canonical: |
<pre><code class="language-aa">foo
</code></pre>
@@ -1527,7 +1527,7 @@
</div>
wysiwyg: |-
<pre language="aa" class="content-editor-code-block undefined code highlight"><code>foo</code></pre>
-04_05__leaf_blocks__fenced_code_blocks__029:
+04_05_00__leaf_blocks__fenced_code_blocks__029:
canonical: |
<pre><code>``` aaa
</code></pre>
@@ -1538,7 +1538,7 @@
</div>
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>``` aaa</code></pre>
-04_06__leaf_blocks__html_blocks__001:
+04_06_00__leaf_blocks__html_blocks__001:
canonical: |
<table><tr><td>
<pre>
@@ -1557,7 +1557,7 @@
<table><tbody><tr><td colspan="1" rowspan="1"><pre><p>**Hello**,
</p><p><em>world</em>.
</p></pre><p></p></td></tr></tbody></table>
-04_06__leaf_blocks__html_blocks__002:
+04_06_00__leaf_blocks__html_blocks__002:
canonical: |
<table>
<tr>
@@ -1581,7 +1581,7 @@
hi
</p></td></tr></tbody></table>
<p>okay.</p>
-04_06__leaf_blocks__html_blocks__003:
+04_06_00__leaf_blocks__html_blocks__003:
canonical: |2
<div>
*hello*
@@ -1595,7 +1595,7 @@
<div><p>
*hello*
</p></div>
-04_06__leaf_blocks__html_blocks__004:
+04_06_00__leaf_blocks__html_blocks__004:
canonical: |
</div>
*foo*
@@ -1605,7 +1605,7 @@
wysiwyg: |-
<p>
*foo*</p>
-04_06__leaf_blocks__html_blocks__005:
+04_06_00__leaf_blocks__html_blocks__005:
canonical: |
<DIV CLASS="foo">
<p><em>Markdown</em></p>
@@ -1616,7 +1616,7 @@
</div>
wysiwyg: |-
<div><p><em>Markdown</em></p></div>
-04_06__leaf_blocks__html_blocks__006:
+04_06_00__leaf_blocks__html_blocks__006:
canonical: |
<div id="foo"
class="bar">
@@ -1626,7 +1626,7 @@
</div>
wysiwyg: |-
<div></div>
-04_06__leaf_blocks__html_blocks__007:
+04_06_00__leaf_blocks__html_blocks__007:
canonical: |
<div id="foo" class="bar
baz">
@@ -1636,7 +1636,7 @@
</div>
wysiwyg: |-
<div></div>
-04_06__leaf_blocks__html_blocks__008:
+04_06_00__leaf_blocks__html_blocks__008:
canonical: |
<div>
*foo*
@@ -1650,7 +1650,7 @@
<div><p>
*foo*
</p><p><em>bar</em></p></div>
-04_06__leaf_blocks__html_blocks__009:
+04_06_00__leaf_blocks__html_blocks__009:
canonical: |
<div id="foo"
*hi*
@@ -1658,7 +1658,7 @@
<div></div>
wysiwyg: |-
<p></p>
-04_06__leaf_blocks__html_blocks__010:
+04_06_00__leaf_blocks__html_blocks__010:
canonical: |
<div class
foo
@@ -1666,7 +1666,7 @@
<div></div>
wysiwyg: |-
<p></p>
-04_06__leaf_blocks__html_blocks__011:
+04_06_00__leaf_blocks__html_blocks__011:
canonical: |
<div *???-&&&-<---
*foo*
@@ -1674,14 +1674,14 @@
<div></div>
wysiwyg: |-
<p></p>
-04_06__leaf_blocks__html_blocks__012:
+04_06_00__leaf_blocks__html_blocks__012:
canonical: |
<div><a href="bar">*foo*</a></div>
static: |-
<div><a href="bar">*foo*</a></div>
wysiwyg: |-
<div><p><a target="_blank" rel="noopener noreferrer nofollow" href="bar">*foo*</a></p></div>
-04_06__leaf_blocks__html_blocks__013:
+04_06_00__leaf_blocks__html_blocks__013:
canonical: |
<table><tr><td>
foo
@@ -1694,7 +1694,7 @@
<table><tbody><tr><td colspan="1" rowspan="1"><p>
foo
</p></td></tr></tbody></table>
-04_06__leaf_blocks__html_blocks__014:
+04_06_00__leaf_blocks__html_blocks__014:
canonical: |
<div></div>
``` c
@@ -1711,7 +1711,7 @@
``` c
int x = 33;
```</p>
-04_06__leaf_blocks__html_blocks__015:
+04_06_00__leaf_blocks__html_blocks__015:
canonical: |
<a href="foo">
*bar*
@@ -1724,7 +1724,7 @@
<p><a target="_blank" rel="noopener noreferrer nofollow" href="foo">
*bar*
</a></p>
-04_06__leaf_blocks__html_blocks__016:
+04_06_00__leaf_blocks__html_blocks__016:
canonical: |
<Warning>
*bar*
@@ -1734,7 +1734,7 @@
*bar*
wysiwyg: |-
<p></p>
-04_06__leaf_blocks__html_blocks__017:
+04_06_00__leaf_blocks__html_blocks__017:
canonical: |
<i class="foo">
*bar*
@@ -1747,7 +1747,7 @@
<p><em>
*bar*
</em></p>
-04_06__leaf_blocks__html_blocks__018:
+04_06_00__leaf_blocks__html_blocks__018:
canonical: |
</ins>
*bar*
@@ -1757,7 +1757,7 @@
wysiwyg: |-
<p>
*bar*</p>
-04_06__leaf_blocks__html_blocks__019:
+04_06_00__leaf_blocks__html_blocks__019:
canonical: |
<del>
*foo*
@@ -1770,7 +1770,7 @@
<p><s>
*foo*
</s></p>
-04_06__leaf_blocks__html_blocks__020:
+04_06_00__leaf_blocks__html_blocks__020:
canonical: |
<del>
<p><em>foo</em></p>
@@ -1781,14 +1781,14 @@
</del>
wysiwyg: |-
<p><em><s>foo</s></em></p>
-04_06__leaf_blocks__html_blocks__021:
+04_06_00__leaf_blocks__html_blocks__021:
canonical: |
<p><del><em>foo</em></del></p>
static: |-
<p data-sourcepos="1:1-1:16" dir="auto"><del><em>foo</em></del></p>
wysiwyg: |-
<p><em><s>foo</s></em></p>
-04_06__leaf_blocks__html_blocks__022:
+04_06_00__leaf_blocks__html_blocks__022:
canonical: |
<pre language="haskell"><code>
import Text.HTML.TagSoup
@@ -1815,7 +1815,7 @@
main = print $ parseTags tags
</code></p></pre>
<p>okay</p>
-04_06__leaf_blocks__html_blocks__023:
+04_06_00__leaf_blocks__html_blocks__023:
canonical: |
<script type="text/javascript">
// JavaScript example
@@ -1828,7 +1828,7 @@
<p data-sourcepos="6:1-6:4" dir="auto">okay</p>
wysiwyg: |-
<p>okay</p>
-04_06__leaf_blocks__html_blocks__024:
+04_06_00__leaf_blocks__html_blocks__024:
canonical: |
<style
type="text/css">
@@ -1846,7 +1846,7 @@
<p data-sourcepos="7:1-7:4" dir="auto">okay</p>
wysiwyg: |-
<p>okay</p>
-04_06__leaf_blocks__html_blocks__025:
+04_06_00__leaf_blocks__html_blocks__025:
canonical: |
<style
type="text/css">
@@ -1858,7 +1858,7 @@
foo
wysiwyg: |-
<p></p>
-04_06__leaf_blocks__html_blocks__026:
+04_06_00__leaf_blocks__html_blocks__026:
canonical: |
<blockquote>
<div>
@@ -1878,7 +1878,7 @@
foo
</p></div></blockquote>
<p>bar</p>
-04_06__leaf_blocks__html_blocks__027:
+04_06_00__leaf_blocks__html_blocks__027:
canonical: |
<ul>
<li>
@@ -1897,7 +1897,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p></p><div></div></li><li><p>foo</p></li></ul>
-04_06__leaf_blocks__html_blocks__028:
+04_06_00__leaf_blocks__html_blocks__028:
canonical: |
<style>p{color:red;}</style>
<p><em>foo</em></p>
@@ -1906,7 +1906,7 @@
<p data-sourcepos="2:1-2:5" dir="auto"><em>foo</em></p>
wysiwyg: |-
<p><em>foo</em></p>
-04_06__leaf_blocks__html_blocks__029:
+04_06_00__leaf_blocks__html_blocks__029:
canonical: |
<!-- foo -->*bar*
<p><em>baz</em></p>
@@ -1917,7 +1917,7 @@
<p>*bar*
</p>
<p><em>baz</em></p>
-04_06__leaf_blocks__html_blocks__030:
+04_06_00__leaf_blocks__html_blocks__030:
canonical: |
<script>
foo
@@ -1926,7 +1926,7 @@
1. *bar*
wysiwyg: |-
<p>1. *bar*</p>
-04_06__leaf_blocks__html_blocks__031:
+04_06_00__leaf_blocks__html_blocks__031:
canonical: |
<!-- Foo
@@ -1938,7 +1938,7 @@
<p data-sourcepos="5:1-5:4" dir="auto">okay</p>
wysiwyg: |-
<p>okay</p>
-04_06__leaf_blocks__html_blocks__032:
+04_06_00__leaf_blocks__html_blocks__032:
canonical: |
<?php
@@ -1957,13 +1957,13 @@
?&gt;
</p>
<p>okay</p>
-04_06__leaf_blocks__html_blocks__033:
+04_06_00__leaf_blocks__html_blocks__033:
canonical: |
<!DOCTYPE html>
static: ""
wysiwyg: |-
<p></p>
-04_06__leaf_blocks__html_blocks__034:
+04_06_00__leaf_blocks__html_blocks__034:
canonical: |
<![CDATA[
function matchwo(a,b)
@@ -1994,7 +1994,7 @@
<p data-sourcepos="13:1-13:4" dir="auto">okay</p>
wysiwyg: |-
<p>okay</p>
-04_06__leaf_blocks__html_blocks__035:
+04_06_00__leaf_blocks__html_blocks__035:
canonical: |2
<!-- foo -->
<pre><code>&lt;!-- foo --&gt;
@@ -2005,7 +2005,7 @@
class=\"line\" lang=\"plaintext\">&lt;!-- foo --&gt;</span></code></pre>\n<copy-code></copy-code>\n</div>"
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>&lt;!-- foo --&gt;</code></pre>
-04_06__leaf_blocks__html_blocks__036:
+04_06_00__leaf_blocks__html_blocks__036:
canonical: |2
<div>
<pre><code>&lt;div&gt;
@@ -2019,7 +2019,7 @@
</div>
wysiwyg: |-
<div><pre class="content-editor-code-block undefined code highlight"><code>&lt;div&gt;</code></pre></div>
-04_06__leaf_blocks__html_blocks__037:
+04_06_00__leaf_blocks__html_blocks__037:
canonical: |
<p>Foo</p>
<div>
@@ -2035,7 +2035,7 @@
<div><p>
bar
</p></div>
-04_06__leaf_blocks__html_blocks__038:
+04_06_00__leaf_blocks__html_blocks__038:
canonical: |
<div>
bar
@@ -2052,7 +2052,7 @@
</p></div>
<p>
*foo*</p>
-04_06__leaf_blocks__html_blocks__039:
+04_06_00__leaf_blocks__html_blocks__039:
canonical: |
<p>Foo
<a href="bar">
@@ -2065,7 +2065,7 @@
<p>Foo
<a target="_blank" rel="noopener noreferrer nofollow" href="bar">
baz</a></p>
-04_06__leaf_blocks__html_blocks__040:
+04_06_00__leaf_blocks__html_blocks__040:
canonical: |
<div>
<p><em>Emphasized</em> text.</p>
@@ -2076,7 +2076,7 @@
</div>
wysiwyg: |-
<div><p><em>Emphasized</em> text.</p></div>
-04_06__leaf_blocks__html_blocks__041:
+04_06_00__leaf_blocks__html_blocks__041:
canonical: |
<div>
*Emphasized* text.
@@ -2089,7 +2089,7 @@
<div><p>
*Emphasized* text.
</p></div>
-04_06__leaf_blocks__html_blocks__042:
+04_06_00__leaf_blocks__html_blocks__042:
canonical: |
<table>
<tr>
@@ -2110,7 +2110,7 @@
<table><tbody><tr><td colspan="1" rowspan="1"><p>
Hi
</p></td></tr></tbody></table>
-04_06__leaf_blocks__html_blocks__043:
+04_06_00__leaf_blocks__html_blocks__043:
canonical: |
<table>
<tr>
@@ -2136,7 +2136,7 @@
Hi
&lt;/td&gt;</code></pre>
<table><tbody><tr></tr></tbody></table>
-04_07__leaf_blocks__link_reference_definitions__001:
+04_07_00__leaf_blocks__link_reference_definitions__001:
canonical: |
<p><a href="/url" title="title">foo</a></p>
static: |-
@@ -2144,7 +2144,7 @@
wysiwyg: |-
<pre>[foo]: /url "title"</pre>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url" title="title">foo</a></p>
-04_07__leaf_blocks__link_reference_definitions__002:
+04_07_00__leaf_blocks__link_reference_definitions__002:
canonical: |
<p><a href="/url" title="the title">foo</a></p>
static: |-
@@ -2152,7 +2152,7 @@
wysiwyg: |-
<pre>[foo]: /url "the title"</pre>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url" title="the title">foo</a></p>
-04_07__leaf_blocks__link_reference_definitions__003:
+04_07_00__leaf_blocks__link_reference_definitions__003:
canonical: |
<p><a href="my_(url)" title="title (with parens)">Foo*bar]</a></p>
static: |-
@@ -2160,7 +2160,7 @@
wysiwyg: |-
<pre>[foo*bar\]]: my_(url) "title (with parens)"</pre>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="my_(url)" title="title (with parens)">Foo*bar]</a></p>
-04_07__leaf_blocks__link_reference_definitions__004:
+04_07_00__leaf_blocks__link_reference_definitions__004:
canonical: |
<p><a href="my%20url" title="title">Foo bar</a></p>
static: |-
@@ -2168,7 +2168,7 @@
wysiwyg: |-
<pre>[foo bar]: my url "title"</pre>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="my%20url" title="title">Foo bar</a></p>
-04_07__leaf_blocks__link_reference_definitions__005:
+04_07_00__leaf_blocks__link_reference_definitions__005:
canonical: |
<p><a href="/url" title="
title
@@ -2192,7 +2192,7 @@
line1
line2
">foo</a></p>
-04_07__leaf_blocks__link_reference_definitions__006:
+04_07_00__leaf_blocks__link_reference_definitions__006:
canonical: |
<p>[foo]: /url 'title</p>
<p>with blank line'</p>
@@ -2205,7 +2205,7 @@
<p>[foo]: /url 'title</p>
<p>with blank line'</p>
<p>[foo]</p>
-04_07__leaf_blocks__link_reference_definitions__007:
+04_07_00__leaf_blocks__link_reference_definitions__007:
canonical: |
<p><a href="/url">foo</a></p>
static: |-
@@ -2213,7 +2213,7 @@
wysiwyg: |-
<pre>[foo]: /url</pre>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url">foo</a></p>
-04_07__leaf_blocks__link_reference_definitions__008:
+04_07_00__leaf_blocks__link_reference_definitions__008:
canonical: |
<p>[foo]:</p>
<p>[foo]</p>
@@ -2223,7 +2223,7 @@
wysiwyg: |-
<p>[foo]:</p>
<p>[foo]</p>
-04_07__leaf_blocks__link_reference_definitions__009:
+04_07_00__leaf_blocks__link_reference_definitions__009:
canonical: |
<p><a href="">foo</a></p>
static: |-
@@ -2231,7 +2231,7 @@
wysiwyg: |-
<pre>[foo]: </pre>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="">foo</a></p>
-04_07__leaf_blocks__link_reference_definitions__010:
+04_07_00__leaf_blocks__link_reference_definitions__010:
canonical: |
<p>[foo]: <bar>(baz)</p>
<p>[foo]</p>
@@ -2241,7 +2241,7 @@
wysiwyg: |-
<p>[foo]: </p>
<p>[foo]</p>
-04_07__leaf_blocks__link_reference_definitions__011:
+04_07_00__leaf_blocks__link_reference_definitions__011:
canonical: |
<p><a href="/url%5Cbar*baz" title="foo&quot;bar\baz">foo</a></p>
static: |-
@@ -2249,7 +2249,7 @@
wysiwyg: |-
<pre>[foo]: /url\bar*baz "foo"bar\baz"</pre>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url%5Cbar*baz" title="foo&quot;bar\baz">foo</a></p>
-04_07__leaf_blocks__link_reference_definitions__012:
+04_07_00__leaf_blocks__link_reference_definitions__012:
canonical: |
<p><a href="url">foo</a></p>
static: |-
@@ -2257,7 +2257,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="url">foo</a></p>
<pre>[foo]: url</pre>
-04_07__leaf_blocks__link_reference_definitions__013:
+04_07_00__leaf_blocks__link_reference_definitions__013:
canonical: |
<p><a href="first">foo</a></p>
static: |-
@@ -2266,7 +2266,7 @@
<p><a target="_blank" rel="noopener noreferrer nofollow" href="first">foo</a></p>
<pre>[foo]: first</pre>
<pre>[foo]: second</pre>
-04_07__leaf_blocks__link_reference_definitions__014:
+04_07_00__leaf_blocks__link_reference_definitions__014:
canonical: |
<p><a href="/url">Foo</a></p>
static: |-
@@ -2274,7 +2274,7 @@
wysiwyg: |-
<pre>[foo]: /url</pre>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url">Foo</a></p>
-04_07__leaf_blocks__link_reference_definitions__015:
+04_07_00__leaf_blocks__link_reference_definitions__015:
canonical: |
<p><a href="/%CF%86%CE%BF%CF%85">αγω</a></p>
static: |-
@@ -2282,12 +2282,12 @@
wysiwyg: |-
<pre>[αγω]: /φου</pre>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/%CF%86%CE%BF%CF%85">αγω</a></p>
-04_07__leaf_blocks__link_reference_definitions__016:
+04_07_00__leaf_blocks__link_reference_definitions__016:
canonical: ""
static: ""
wysiwyg: |-
<pre>[foo]: /url</pre>
-04_07__leaf_blocks__link_reference_definitions__017:
+04_07_00__leaf_blocks__link_reference_definitions__017:
canonical: |
<p>bar</p>
static: |-
@@ -2295,14 +2295,14 @@
wysiwyg: |-
<pre>[foo]: /url</pre>
<p>bar</p>
-04_07__leaf_blocks__link_reference_definitions__018:
+04_07_00__leaf_blocks__link_reference_definitions__018:
canonical: |
<p>[foo]: /url &quot;title&quot; ok</p>
static: |-
<p data-sourcepos="1:1-1:22" dir="auto">[foo]: /url "title" ok</p>
wysiwyg: |-
<p>[foo]: /url "title" ok</p>
-04_07__leaf_blocks__link_reference_definitions__019:
+04_07_00__leaf_blocks__link_reference_definitions__019:
canonical: |
<p>&quot;title&quot; ok</p>
static: |-
@@ -2310,7 +2310,7 @@
wysiwyg: |-
<pre>[foo]: /url</pre>
<p>"title" ok</p>
-04_07__leaf_blocks__link_reference_definitions__020:
+04_07_00__leaf_blocks__link_reference_definitions__020:
canonical: |
<pre><code>[foo]: /url &quot;title&quot;
</code></pre>
@@ -2324,7 +2324,7 @@
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>[foo]: /url "title"</code></pre>
<p>[foo]</p>
-04_07__leaf_blocks__link_reference_definitions__021:
+04_07_00__leaf_blocks__link_reference_definitions__021:
canonical: |
<pre><code>[foo]: /url
</code></pre>
@@ -2338,7 +2338,7 @@
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>[foo]: /url</code></pre>
<p>[foo]</p>
-04_07__leaf_blocks__link_reference_definitions__022:
+04_07_00__leaf_blocks__link_reference_definitions__022:
canonical: |
<p>Foo
[bar]: /baz</p>
@@ -2351,7 +2351,7 @@
<p>Foo
[bar]: /baz</p>
<p>[bar]</p>
-04_07__leaf_blocks__link_reference_definitions__023:
+04_07_00__leaf_blocks__link_reference_definitions__023:
canonical: |
<h1><a href="/url">Foo</a></h1>
<blockquote>
@@ -2368,7 +2368,7 @@
<h1><a target="_blank" rel="noopener noreferrer nofollow" href="/url">Foo</a></h1>
<pre>[foo]: /url</pre>
<blockquote multiline="false"><p>bar</p></blockquote>
-04_07__leaf_blocks__link_reference_definitions__024:
+04_07_00__leaf_blocks__link_reference_definitions__024:
canonical: |
<h1>bar</h1>
<p><a href="/url">foo</a></p>
@@ -2380,7 +2380,7 @@
<pre>[foo]: /url</pre>
<h1>bar</h1>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url">foo</a></p>
-04_07__leaf_blocks__link_reference_definitions__025:
+04_07_00__leaf_blocks__link_reference_definitions__025:
canonical: |
<p>===
<a href="/url">foo</a></p>
@@ -2391,7 +2391,7 @@
<pre>[foo]: /url</pre>
<p>===
<a target="_blank" rel="noopener noreferrer nofollow" href="/url">foo</a></p>
-04_07__leaf_blocks__link_reference_definitions__026:
+04_07_00__leaf_blocks__link_reference_definitions__026:
canonical: |
<p><a href="/foo-url" title="foo">foo</a>,
<a href="/bar-url" title="bar">bar</a>,
@@ -2407,7 +2407,7 @@
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/foo-url" title="foo">foo</a>,
<a target="_blank" rel="noopener noreferrer nofollow" href="/bar-url" title="bar">bar</a>,
<a target="_blank" rel="noopener noreferrer nofollow" href="/baz-url">baz</a></p>
-04_07__leaf_blocks__link_reference_definitions__027:
+04_07_00__leaf_blocks__link_reference_definitions__027:
canonical: |
<p><a href="/url">foo</a></p>
<blockquote>
@@ -2419,12 +2419,12 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url">foo</a></p>
<blockquote multiline="false"><pre>[foo]: /url</pre></blockquote>
-04_07__leaf_blocks__link_reference_definitions__028:
+04_07_00__leaf_blocks__link_reference_definitions__028:
canonical: ""
static: ""
wysiwyg: |-
<pre>[foo]: /url</pre>
-04_08__leaf_blocks__paragraphs__001:
+04_08_00__leaf_blocks__paragraphs__001:
canonical: |
<p>aaa</p>
<p>bbb</p>
@@ -2434,7 +2434,7 @@
wysiwyg: |-
<p>aaa</p>
<p>bbb</p>
-04_08__leaf_blocks__paragraphs__002:
+04_08_00__leaf_blocks__paragraphs__002:
canonical: |
<p>aaa
bbb</p>
@@ -2450,7 +2450,7 @@
bbb</p>
<p>ccc
ddd</p>
-04_08__leaf_blocks__paragraphs__003:
+04_08_00__leaf_blocks__paragraphs__003:
canonical: |
<p>aaa</p>
<p>bbb</p>
@@ -2460,7 +2460,7 @@
wysiwyg: |-
<p>aaa</p>
<p>bbb</p>
-04_08__leaf_blocks__paragraphs__004:
+04_08_00__leaf_blocks__paragraphs__004:
canonical: |
<p>aaa
bbb</p>
@@ -2470,7 +2470,7 @@
wysiwyg: |-
<p>aaa
bbb</p>
-04_08__leaf_blocks__paragraphs__005:
+04_08_00__leaf_blocks__paragraphs__005:
canonical: |
<p>aaa
bbb
@@ -2483,7 +2483,7 @@
<p>aaa
bbb
ccc</p>
-04_08__leaf_blocks__paragraphs__006:
+04_08_00__leaf_blocks__paragraphs__006:
canonical: |
<p>aaa
bbb</p>
@@ -2493,7 +2493,7 @@
wysiwyg: |-
<p>aaa
bbb</p>
-04_08__leaf_blocks__paragraphs__007:
+04_08_00__leaf_blocks__paragraphs__007:
canonical: |
<pre><code>aaa
</code></pre>
@@ -2507,7 +2507,7 @@
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>aaa</code></pre>
<p>bbb</p>
-04_08__leaf_blocks__paragraphs__008:
+04_08_00__leaf_blocks__paragraphs__008:
canonical: |
<p>aaa<br />
bbb</p>
@@ -2517,7 +2517,7 @@
wysiwyg: |-
<p>aaa<br>
bbb</p>
-04_09__leaf_blocks__blank_lines__001:
+04_09_00__leaf_blocks__blank_lines__001:
canonical: |
<p>aaa</p>
<h1>aaa</h1>
@@ -2528,7 +2528,7 @@
wysiwyg: |-
<p>aaa</p>
<h1>aaa</h1>
-04_10__leaf_blocks__tables_extension__001:
+04_10_00__leaf_blocks__tables_extension__001:
canonical: |
<table>
<thead>
@@ -2561,7 +2561,7 @@
</table>
wysiwyg: |-
<table><tbody><tr><th colspan="1" rowspan="1"><p>foo</p></th><th colspan="1" rowspan="1"><p>bar</p></th></tr><tr><td colspan="1" rowspan="1"><p>baz</p></td><td colspan="1" rowspan="1"><p>bim</p></td></tr></tbody></table>
-04_10__leaf_blocks__tables_extension__002:
+04_10_00__leaf_blocks__tables_extension__002:
canonical: |
<table>
<thead>
@@ -2594,7 +2594,7 @@
</table>
wysiwyg: |-
<table><tbody><tr><th colspan="1" rowspan="1"><p>abc</p></th><th colspan="1" rowspan="1"><p>defghi</p></th></tr><tr><td colspan="1" rowspan="1"><p>bar</p></td><td colspan="1" rowspan="1"><p>baz</p></td></tr></tbody></table>
-04_10__leaf_blocks__tables_extension__003:
+04_10_00__leaf_blocks__tables_extension__003:
canonical: |
<table>
<thead>
@@ -2629,7 +2629,7 @@
</table>
wysiwyg: |-
<table><tbody><tr><th colspan="1" rowspan="1"><p>f|oo</p></th></tr><tr><td colspan="1" rowspan="1"><p>b <code>|</code> az</p></td></tr><tr><td colspan="1" rowspan="1"><p>b <strong>|</strong> im</p></td></tr></tbody></table>
-04_10__leaf_blocks__tables_extension__004:
+04_10_00__leaf_blocks__tables_extension__004:
canonical: |
<table>
<thead>
@@ -2669,7 +2669,7 @@
wysiwyg: |-
<table><tbody><tr><th colspan="1" rowspan="1"><p>abc</p></th><th colspan="1" rowspan="1"><p>def</p></th></tr><tr><td colspan="1" rowspan="1"><p>bar</p></td><td colspan="1" rowspan="1"><p>baz</p></td></tr></tbody></table>
<blockquote multiline="false"><p>bar</p></blockquote>
-04_10__leaf_blocks__tables_extension__005:
+04_10_00__leaf_blocks__tables_extension__005:
canonical: |
<table>
<thead>
@@ -2713,7 +2713,7 @@
wysiwyg: |-
<table><tbody><tr><th colspan="1" rowspan="1"><p>abc</p></th><th colspan="1" rowspan="1"><p>def</p></th></tr><tr><td colspan="1" rowspan="1"><p>bar</p></td><td colspan="1" rowspan="1"><p>baz</p></td></tr><tr><td colspan="1" rowspan="1"><p>bar</p></td><td colspan="1" rowspan="1"><p></p></td></tr></tbody></table>
<p>bar</p>
-04_10__leaf_blocks__tables_extension__006:
+04_10_00__leaf_blocks__tables_extension__006:
canonical: |
<p>| abc | def |
| --- |
@@ -2726,7 +2726,7 @@
<p>| abc | def |
| --- |
| bar |</p>
-04_10__leaf_blocks__tables_extension__007:
+04_10_00__leaf_blocks__tables_extension__007:
canonical: |
<table>
<thead>
@@ -2767,7 +2767,7 @@
</table>
wysiwyg: |-
<table><tbody><tr><th colspan="1" rowspan="1"><p>abc</p></th><th colspan="1" rowspan="1"><p>def</p></th></tr><tr><td colspan="1" rowspan="1"><p>bar</p></td><td colspan="1" rowspan="1"><p></p></td></tr><tr><td colspan="1" rowspan="1"><p>bar</p></td><td colspan="1" rowspan="1"><p>baz</p></td></tr></tbody></table>
-04_10__leaf_blocks__tables_extension__008:
+04_10_00__leaf_blocks__tables_extension__008:
canonical: |
<table>
<thead>
@@ -2788,7 +2788,7 @@
</table>
wysiwyg: |-
<table><tbody><tr><th colspan="1" rowspan="1"><p>abc</p></th><th colspan="1" rowspan="1"><p>def</p></th></tr></tbody></table>
-05_01__container_blocks__block_quotes__001:
+05_01_00__container_blocks__block_quotes__001:
canonical: |
<blockquote>
<h1>Foo</h1>
@@ -2805,7 +2805,7 @@
wysiwyg: |-
<blockquote multiline="false"><h1>Foo</h1><p>bar
baz</p></blockquote>
-05_01__container_blocks__block_quotes__002:
+05_01_00__container_blocks__block_quotes__002:
canonical: |
<blockquote>
<h1>Foo</h1>
@@ -2822,7 +2822,7 @@
wysiwyg: |-
<blockquote multiline="false"><h1>Foo</h1><p>bar
baz</p></blockquote>
-05_01__container_blocks__block_quotes__003:
+05_01_00__container_blocks__block_quotes__003:
canonical: |
<blockquote>
<h1>Foo</h1>
@@ -2839,7 +2839,7 @@
wysiwyg: |-
<blockquote multiline="false"><h1>Foo</h1><p>bar
baz</p></blockquote>
-05_01__container_blocks__block_quotes__004:
+05_01_00__container_blocks__block_quotes__004:
canonical: |
<pre><code>&gt; # Foo
&gt; bar
@@ -2856,7 +2856,7 @@
<pre class="content-editor-code-block undefined code highlight"><code>&gt; # Foo
&gt; bar
&gt; baz</code></pre>
-05_01__container_blocks__block_quotes__005:
+05_01_00__container_blocks__block_quotes__005:
canonical: |
<blockquote>
<h1>Foo</h1>
@@ -2873,7 +2873,7 @@
wysiwyg: |-
<blockquote multiline="false"><h1>Foo</h1><p>bar
baz</p></blockquote>
-05_01__container_blocks__block_quotes__006:
+05_01_00__container_blocks__block_quotes__006:
canonical: |
<blockquote>
<p>bar
@@ -2890,7 +2890,7 @@
<blockquote multiline="false"><p>bar
baz
foo</p></blockquote>
-05_01__container_blocks__block_quotes__007:
+05_01_00__container_blocks__block_quotes__007:
canonical: |
<blockquote>
<p>foo</p>
@@ -2904,7 +2904,7 @@
wysiwyg: |-
<blockquote multiline="false"><p>foo</p></blockquote>
<hr>
-05_01__container_blocks__block_quotes__008:
+05_01_00__container_blocks__block_quotes__008:
canonical: |
<blockquote>
<ul>
@@ -2926,7 +2926,7 @@
wysiwyg: |-
<blockquote multiline="false"><ul bullet="*"><li><p>foo</p></li></ul></blockquote>
<ul bullet="*"><li><p>bar</p></li></ul>
-05_01__container_blocks__block_quotes__009:
+05_01_00__container_blocks__block_quotes__009:
canonical: |
<blockquote>
<pre><code>foo
@@ -2948,7 +2948,7 @@
wysiwyg: |-
<blockquote multiline="false"><pre class="content-editor-code-block undefined code highlight"><code>foo</code></pre></blockquote>
<pre class="content-editor-code-block undefined code highlight"><code>bar</code></pre>
-05_01__container_blocks__block_quotes__010:
+05_01_00__container_blocks__block_quotes__010:
canonical: |
<blockquote>
<pre><code></code></pre>
@@ -2971,7 +2971,7 @@
<blockquote multiline="false"><pre class="content-editor-code-block undefined code highlight"><code></code></pre></blockquote>
<p>foo</p>
<pre class="content-editor-code-block undefined code highlight"><code></code></pre>
-05_01__container_blocks__block_quotes__011:
+05_01_00__container_blocks__block_quotes__011:
canonical: |
<blockquote>
<p>foo
@@ -2985,7 +2985,7 @@
wysiwyg: |-
<blockquote multiline="false"><p>foo
- bar</p></blockquote>
-05_01__container_blocks__block_quotes__012:
+05_01_00__container_blocks__block_quotes__012:
canonical: |
<blockquote>
</blockquote>
@@ -2994,7 +2994,7 @@
</blockquote>
wysiwyg: |-
<blockquote multiline="false"><p></p></blockquote>
-05_01__container_blocks__block_quotes__013:
+05_01_00__container_blocks__block_quotes__013:
canonical: |
<blockquote>
</blockquote>
@@ -3003,7 +3003,7 @@
</blockquote>
wysiwyg: |-
<blockquote multiline="false"><p></p></blockquote>
-05_01__container_blocks__block_quotes__014:
+05_01_00__container_blocks__block_quotes__014:
canonical: |
<blockquote>
<p>foo</p>
@@ -3014,7 +3014,7 @@
</blockquote>
wysiwyg: |-
<blockquote multiline="false"><p>foo</p></blockquote>
-05_01__container_blocks__block_quotes__015:
+05_01_00__container_blocks__block_quotes__015:
canonical: |
<blockquote>
<p>foo</p>
@@ -3032,7 +3032,7 @@
wysiwyg: |-
<blockquote multiline="false"><p>foo</p></blockquote>
<blockquote multiline="false"><p>bar</p></blockquote>
-05_01__container_blocks__block_quotes__016:
+05_01_00__container_blocks__block_quotes__016:
canonical: |
<blockquote>
<p>foo
@@ -3046,7 +3046,7 @@
wysiwyg: |-
<blockquote multiline="false"><p>foo
bar</p></blockquote>
-05_01__container_blocks__block_quotes__017:
+05_01_00__container_blocks__block_quotes__017:
canonical: |
<blockquote>
<p>foo</p>
@@ -3059,7 +3059,7 @@
</blockquote>
wysiwyg: |-
<blockquote multiline="false"><p>foo</p><p>bar</p></blockquote>
-05_01__container_blocks__block_quotes__018:
+05_01_00__container_blocks__block_quotes__018:
canonical: |
<p>foo</p>
<blockquote>
@@ -3073,7 +3073,7 @@
wysiwyg: |-
<p>foo</p>
<blockquote multiline="false"><p>bar</p></blockquote>
-05_01__container_blocks__block_quotes__019:
+05_01_00__container_blocks__block_quotes__019:
canonical: |
<blockquote>
<p>aaa</p>
@@ -3094,7 +3094,7 @@
<blockquote multiline="false"><p>aaa</p></blockquote>
<hr>
<blockquote multiline="false"><p>bbb</p></blockquote>
-05_01__container_blocks__block_quotes__020:
+05_01_00__container_blocks__block_quotes__020:
canonical: |
<blockquote>
<p>bar
@@ -3108,7 +3108,7 @@
wysiwyg: |-
<blockquote multiline="false"><p>bar
baz</p></blockquote>
-05_01__container_blocks__block_quotes__021:
+05_01_00__container_blocks__block_quotes__021:
canonical: |
<blockquote>
<p>bar</p>
@@ -3122,7 +3122,7 @@
wysiwyg: |-
<blockquote multiline="false"><p>bar</p></blockquote>
<p>baz</p>
-05_01__container_blocks__block_quotes__022:
+05_01_00__container_blocks__block_quotes__022:
canonical: |
<blockquote>
<p>bar</p>
@@ -3136,7 +3136,7 @@
wysiwyg: |-
<blockquote multiline="false"><p>bar</p></blockquote>
<p>baz</p>
-05_01__container_blocks__block_quotes__023:
+05_01_00__container_blocks__block_quotes__023:
canonical: |
<blockquote>
<blockquote>
@@ -3158,7 +3158,7 @@
wysiwyg: |-
<blockquote multiline="false"><blockquote multiline="false"><blockquote multiline="false"><p>foo
bar</p></blockquote></blockquote></blockquote>
-05_01__container_blocks__block_quotes__024:
+05_01_00__container_blocks__block_quotes__024:
canonical: |
<blockquote>
<blockquote>
@@ -3183,7 +3183,7 @@
<blockquote multiline="false"><blockquote multiline="false"><blockquote multiline="false"><p>foo
bar
baz</p></blockquote></blockquote></blockquote>
-05_01__container_blocks__block_quotes__025:
+05_01_00__container_blocks__block_quotes__025:
canonical: |
<blockquote>
<pre><code>code
@@ -3205,7 +3205,7 @@
wysiwyg: |-
<blockquote multiline="false"><pre class="content-editor-code-block undefined code highlight"><code>code</code></pre></blockquote>
<blockquote multiline="false"><p>not code</p></blockquote>
-05_02__container_blocks__list_items__001:
+05_02_00__container_blocks__list_items__001:
canonical: |
<p>A paragraph
with two lines.</p>
@@ -3229,7 +3229,7 @@
with two lines.</p>
<pre class="content-editor-code-block undefined code highlight"><code>indented code</code></pre>
<blockquote multiline="false"><p>A block quote.</p></blockquote>
-05_02__container_blocks__list_items__002:
+05_02_00__container_blocks__list_items__002:
canonical: |
<ol>
<li>
@@ -3259,7 +3259,7 @@
wysiwyg: |-
<ol parens="false"><li><p>A paragraph
with two lines.</p><pre class="content-editor-code-block undefined code highlight"><code>indented code</code></pre><blockquote multiline="false"><p>A block quote.</p></blockquote></li></ol>
-05_02__container_blocks__list_items__003:
+05_02_00__container_blocks__list_items__003:
canonical: |
<ul>
<li>one</li>
@@ -3273,7 +3273,7 @@
wysiwyg: |-
<ul bullet="*"><li><p>one</p></li></ul>
<p>two</p>
-05_02__container_blocks__list_items__004:
+05_02_00__container_blocks__list_items__004:
canonical: |
<ul>
<li>
@@ -3290,7 +3290,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>one</p><p>two</p></li></ul>
-05_02__container_blocks__list_items__005:
+05_02_00__container_blocks__list_items__005:
canonical: |
<ul>
<li>one</li>
@@ -3308,7 +3308,7 @@
wysiwyg: |-
<ul bullet="*"><li><p>one</p></li></ul>
<pre class="content-editor-code-block undefined code highlight"><code> two</code></pre>
-05_02__container_blocks__list_items__006:
+05_02_00__container_blocks__list_items__006:
canonical: |
<ul>
<li>
@@ -3325,7 +3325,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>one</p><p>two</p></li></ul>
-05_02__container_blocks__list_items__007:
+05_02_00__container_blocks__list_items__007:
canonical: |
<blockquote>
<blockquote>
@@ -3350,7 +3350,7 @@
</blockquote>
wysiwyg: |-
<blockquote multiline="false"><blockquote multiline="false"><ol parens="false"><li><p>one</p><p>two</p></li></ol></blockquote></blockquote>
-05_02__container_blocks__list_items__008:
+05_02_00__container_blocks__list_items__008:
canonical: |
<blockquote>
<blockquote>
@@ -3371,7 +3371,7 @@
</blockquote>
wysiwyg: |-
<blockquote multiline="false"><blockquote multiline="false"><ul bullet="*"><li><p>one</p></li></ul><p>two</p></blockquote></blockquote>
-05_02__container_blocks__list_items__009:
+05_02_00__container_blocks__list_items__009:
canonical: |
<p>-one</p>
<p>2.two</p>
@@ -3381,7 +3381,7 @@
wysiwyg: |-
<p>-one</p>
<p>2.two</p>
-05_02__container_blocks__list_items__010:
+05_02_00__container_blocks__list_items__010:
canonical: |
<ul>
<li>
@@ -3398,7 +3398,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>foo</p><p>bar</p></li></ul>
-05_02__container_blocks__list_items__011:
+05_02_00__container_blocks__list_items__011:
canonical: |
<ol>
<li>
@@ -3427,7 +3427,7 @@
</ol>
wysiwyg: |-
<ol parens="false"><li><p>foo</p><pre class="content-editor-code-block undefined code highlight"><code>bar</code></pre><p>baz</p><blockquote multiline="false"><p>bam</p></blockquote></li></ol>
-05_02__container_blocks__list_items__012:
+05_02_00__container_blocks__list_items__012:
canonical: |
<ul>
<li>
@@ -3457,7 +3457,7 @@
baz</code></pre></li></ul>
-05_02__container_blocks__list_items__013:
+05_02_00__container_blocks__list_items__013:
canonical: |
<ol start="123456789">
<li>ok</li>
@@ -3468,14 +3468,14 @@
</ol>
wysiwyg: |-
<ol parens="false"><li><p>ok</p></li></ol>
-05_02__container_blocks__list_items__014:
+05_02_00__container_blocks__list_items__014:
canonical: |
<p>1234567890. not ok</p>
static: |-
<p data-sourcepos="1:1-1:18" dir="auto">1234567890. not ok</p>
wysiwyg: |-
<p>1234567890. not ok</p>
-05_02__container_blocks__list_items__015:
+05_02_00__container_blocks__list_items__015:
canonical: |
<ol start="0">
<li>ok</li>
@@ -3486,7 +3486,7 @@
</ol>
wysiwyg: |-
<ol parens="false"><li><p>ok</p></li></ol>
-05_02__container_blocks__list_items__016:
+05_02_00__container_blocks__list_items__016:
canonical: |
<ol start="3">
<li>ok</li>
@@ -3497,14 +3497,14 @@
</ol>
wysiwyg: |-
<ol parens="false"><li><p>ok</p></li></ol>
-05_02__container_blocks__list_items__017:
+05_02_00__container_blocks__list_items__017:
canonical: |
<p>-1. not ok</p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto">-1. not ok</p>
wysiwyg: |-
<p>-1. not ok</p>
-05_02__container_blocks__list_items__018:
+05_02_00__container_blocks__list_items__018:
canonical: |
<ul>
<li>
@@ -3525,7 +3525,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>foo</p><pre class="content-editor-code-block undefined code highlight"><code>bar</code></pre></li></ul>
-05_02__container_blocks__list_items__019:
+05_02_00__container_blocks__list_items__019:
canonical: |
<ol start="10">
<li>
@@ -3546,7 +3546,7 @@
</ol>
wysiwyg: |-
<ol parens="false"><li><p>foo</p><pre class="content-editor-code-block undefined code highlight"><code>bar</code></pre></li></ol>
-05_02__container_blocks__list_items__020:
+05_02_00__container_blocks__list_items__020:
canonical: |
<pre><code>indented code
</code></pre>
@@ -3567,7 +3567,7 @@
<pre class="content-editor-code-block undefined code highlight"><code>indented code</code></pre>
<p>paragraph</p>
<pre class="content-editor-code-block undefined code highlight"><code>more code</code></pre>
-05_02__container_blocks__list_items__021:
+05_02_00__container_blocks__list_items__021:
canonical: |
<ol>
<li>
@@ -3594,7 +3594,7 @@
</ol>
wysiwyg: |-
<ol parens="false"><li><p></p><pre class="content-editor-code-block undefined code highlight"><code>indented code</code></pre><p>paragraph</p><pre class="content-editor-code-block undefined code highlight"><code>more code</code></pre></li></ol>
-05_02__container_blocks__list_items__022:
+05_02_00__container_blocks__list_items__022:
canonical: |
<ol>
<li>
@@ -3621,7 +3621,7 @@
</ol>
wysiwyg: |-
<ol parens="false"><li><p></p><pre class="content-editor-code-block undefined code highlight"><code> indented code</code></pre><p>paragraph</p><pre class="content-editor-code-block undefined code highlight"><code>more code</code></pre></li></ol>
-05_02__container_blocks__list_items__023:
+05_02_00__container_blocks__list_items__023:
canonical: |
<p>foo</p>
<p>bar</p>
@@ -3631,7 +3631,7 @@
wysiwyg: |-
<p>foo</p>
<p>bar</p>
-05_02__container_blocks__list_items__024:
+05_02_00__container_blocks__list_items__024:
canonical: |
<ul>
<li>foo</li>
@@ -3645,7 +3645,7 @@
wysiwyg: |-
<ul bullet="*"><li><p>foo</p></li></ul>
<p>bar</p>
-05_02__container_blocks__list_items__025:
+05_02_00__container_blocks__list_items__025:
canonical: |
<ul>
<li>
@@ -3662,7 +3662,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>foo</p><p>bar</p></li></ul>
-05_02__container_blocks__list_items__026:
+05_02_00__container_blocks__list_items__026:
canonical: |
<ul>
<li>foo</li>
@@ -3693,7 +3693,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>foo</p></li><li><p></p><pre class="content-editor-code-block undefined code highlight"><code>bar</code></pre></li><li><p></p><pre class="content-editor-code-block undefined code highlight"><code>baz</code></pre></li></ul>
-05_02__container_blocks__list_items__027:
+05_02_00__container_blocks__list_items__027:
canonical: |
<ul>
<li>foo</li>
@@ -3704,7 +3704,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>foo</p></li></ul>
-05_02__container_blocks__list_items__028:
+05_02_00__container_blocks__list_items__028:
canonical: |
<ul>
<li></li>
@@ -3719,7 +3719,7 @@
wysiwyg: |-
<ul bullet="*"><li><p></p></li></ul>
<p>foo</p>
-05_02__container_blocks__list_items__029:
+05_02_00__container_blocks__list_items__029:
canonical: |
<ul>
<li>foo</li>
@@ -3735,7 +3735,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>foo</p></li><li><p></p></li><li><p>bar</p></li></ul>
-05_02__container_blocks__list_items__030:
+05_02_00__container_blocks__list_items__030:
canonical: |
<ul>
<li>foo</li>
@@ -3751,7 +3751,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>foo</p></li><li><p></p></li><li><p>bar</p></li></ul>
-05_02__container_blocks__list_items__031:
+05_02_00__container_blocks__list_items__031:
canonical: |
<ol>
<li>foo</li>
@@ -3767,7 +3767,7 @@
</ol>
wysiwyg: |-
<ol parens="false"><li><p>foo</p></li><li><p></p></li><li><p>bar</p></li></ol>
-05_02__container_blocks__list_items__032:
+05_02_00__container_blocks__list_items__032:
canonical: |
<ul>
<li></li>
@@ -3779,7 +3779,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p></p></li></ul>
-05_02__container_blocks__list_items__033:
+05_02_00__container_blocks__list_items__033:
canonical: |
<p>foo
*</p>
@@ -3795,7 +3795,7 @@
*</p>
<p>foo
1.</p>
-05_02__container_blocks__list_items__034:
+05_02_00__container_blocks__list_items__034:
canonical: |
<ol>
<li>
@@ -3825,7 +3825,7 @@
wysiwyg: |-
<ol parens="false"><li><p>A paragraph
with two lines.</p><pre class="content-editor-code-block undefined code highlight"><code>indented code</code></pre><blockquote multiline="false"><p>A block quote.</p></blockquote></li></ol>
-05_02__container_blocks__list_items__035:
+05_02_00__container_blocks__list_items__035:
canonical: |
<ol>
<li>
@@ -3855,7 +3855,7 @@
wysiwyg: |-
<ol parens="false"><li><p>A paragraph
with two lines.</p><pre class="content-editor-code-block undefined code highlight"><code>indented code</code></pre><blockquote multiline="false"><p>A block quote.</p></blockquote></li></ol>
-05_02__container_blocks__list_items__036:
+05_02_00__container_blocks__list_items__036:
canonical: |
<ol>
<li>
@@ -3885,7 +3885,7 @@
wysiwyg: |-
<ol parens="false"><li><p>A paragraph
with two lines.</p><pre class="content-editor-code-block undefined code highlight"><code>indented code</code></pre><blockquote multiline="false"><p>A block quote.</p></blockquote></li></ol>
-05_02__container_blocks__list_items__037:
+05_02_00__container_blocks__list_items__037:
canonical: |
<pre><code>1. A paragraph
with two lines.
@@ -3911,7 +3911,7 @@
indented code
&gt; A block quote.</code></pre>
-05_02__container_blocks__list_items__038:
+05_02_00__container_blocks__list_items__038:
canonical: |
<ol>
<li>
@@ -3941,7 +3941,7 @@
wysiwyg: |-
<ol parens="false"><li><p>A paragraph
with two lines.</p><pre class="content-editor-code-block undefined code highlight"><code>indented code</code></pre><blockquote multiline="false"><p>A block quote.</p></blockquote></li></ol>
-05_02__container_blocks__list_items__039:
+05_02_00__container_blocks__list_items__039:
canonical: |
<ol>
<li>A paragraph
@@ -3955,7 +3955,7 @@
wysiwyg: |-
<ol parens="false"><li><p>A paragraph
with two lines.</p></li></ol>
-05_02__container_blocks__list_items__040:
+05_02_00__container_blocks__list_items__040:
canonical: |
<blockquote>
<ol>
@@ -3981,7 +3981,7 @@
wysiwyg: |-
<blockquote multiline="false"><ol parens="false"><li><p></p><blockquote multiline="false"><p>Blockquote
continued here.</p></blockquote></li></ol></blockquote>
-05_02__container_blocks__list_items__041:
+05_02_00__container_blocks__list_items__041:
canonical: |
<blockquote>
<ol>
@@ -4007,7 +4007,7 @@
wysiwyg: |-
<blockquote multiline="false"><ol parens="false"><li><p></p><blockquote multiline="false"><p>Blockquote
continued here.</p></blockquote></li></ol></blockquote>
-05_02__container_blocks__list_items__042:
+05_02_00__container_blocks__list_items__042:
canonical: |
<ul>
<li>foo
@@ -4042,7 +4042,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>foo</p><ul bullet="*"><li><p>bar</p><ul bullet="*"><li><p>baz</p><ul bullet="*"><li><p>boo</p></li></ul></li></ul></li></ul></li></ul>
-05_02__container_blocks__list_items__043:
+05_02_00__container_blocks__list_items__043:
canonical: |
<ul>
<li>foo</li>
@@ -4059,7 +4059,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>foo</p></li><li><p>bar</p></li><li><p>baz</p></li><li><p>boo</p></li></ul>
-05_02__container_blocks__list_items__044:
+05_02_00__container_blocks__list_items__044:
canonical: |
<ol start="10">
<li>foo
@@ -4078,7 +4078,7 @@
</ol>
wysiwyg: |-
<ol parens="false"><li><p>foo</p><ul bullet="*"><li><p>bar</p></li></ul></li></ol>
-05_02__container_blocks__list_items__045:
+05_02_00__container_blocks__list_items__045:
canonical: |
<ol start="10">
<li>foo</li>
@@ -4096,7 +4096,7 @@
wysiwyg: |-
<ol parens="false"><li><p>foo</p></li></ol>
<ul bullet="*"><li><p>bar</p></li></ul>
-05_02__container_blocks__list_items__046:
+05_02_00__container_blocks__list_items__046:
canonical: |
<ul>
<li>
@@ -4115,7 +4115,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p></p><ul bullet="*"><li><p>foo</p></li></ul></li></ul>
-05_02__container_blocks__list_items__047:
+05_02_00__container_blocks__list_items__047:
canonical: |
<ol>
<li>
@@ -4142,7 +4142,7 @@
</ol>
wysiwyg: |-
<ol parens="false"><li><p></p><ul bullet="*"><li><p></p><ol parens="false"><li><p>foo</p></li></ol></li></ul></li></ol>
-05_02__container_blocks__list_items__048:
+05_02_00__container_blocks__list_items__048:
canonical: |
<ul>
<li>
@@ -4166,7 +4166,7 @@
wysiwyg: |-
<ul bullet="*"><li><p></p><h1>Foo</h1></li><li><p></p><h2>Bar</h2><p>
baz</p></li></ul>
-05_04__container_blocks__lists__001:
+05_04_00__container_blocks__lists__001:
canonical: |
<ul>
<li>foo</li>
@@ -4186,7 +4186,7 @@
wysiwyg: |-
<ul bullet="*"><li><p>foo</p></li><li><p>bar</p></li></ul>
<ul bullet="*"><li><p>baz</p></li></ul>
-05_04__container_blocks__lists__002:
+05_04_00__container_blocks__lists__002:
canonical: |
<ol>
<li>foo</li>
@@ -4206,7 +4206,7 @@
wysiwyg: |-
<ol parens="false"><li><p>foo</p></li><li><p>bar</p></li></ol>
<ol parens="false"><li><p>baz</p></li></ol>
-05_04__container_blocks__lists__003:
+05_04_00__container_blocks__lists__003:
canonical: |
<p>Foo</p>
<ul>
@@ -4222,7 +4222,7 @@
wysiwyg: |-
<p>Foo</p>
<ul bullet="*"><li><p>bar</p></li><li><p>baz</p></li></ul>
-05_04__container_blocks__lists__004:
+05_04_00__container_blocks__lists__004:
canonical: |
<p>The number of windows in my house is
14. The number of doors is 6.</p>
@@ -4232,7 +4232,7 @@
wysiwyg: |-
<p>The number of windows in my house is
14. The number of doors is 6.</p>
-05_04__container_blocks__lists__005:
+05_04_00__container_blocks__lists__005:
canonical: |
<p>The number of windows in my house is</p>
<ol>
@@ -4246,7 +4246,7 @@
wysiwyg: |-
<p>The number of windows in my house is</p>
<ol parens="false"><li><p>The number of doors is 6.</p></li></ol>
-05_04__container_blocks__lists__006:
+05_04_00__container_blocks__lists__006:
canonical: |
<ul>
<li>
@@ -4273,7 +4273,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>foo</p></li><li><p>bar</p></li><li><p>baz</p></li></ul>
-05_04__container_blocks__lists__007:
+05_04_00__container_blocks__lists__007:
canonical: |
<ul>
<li>foo
@@ -4306,7 +4306,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>foo</p><ul bullet="*"><li><p>bar</p><ul bullet="*"><li><p>baz</p><p>bim</p></li></ul></li></ul></li></ul>
-05_04__container_blocks__lists__008:
+05_04_00__container_blocks__lists__008:
canonical: |
<ul>
<li>foo</li>
@@ -4330,7 +4330,7 @@
wysiwyg: |-
<ul bullet="*"><li><p>foo</p></li><li><p>bar</p></li></ul>
<ul bullet="*"><li><p>baz</p></li><li><p>bim</p></li></ul>
-05_04__container_blocks__lists__009:
+05_04_00__container_blocks__lists__009:
canonical: |
<ul>
<li>
@@ -4362,7 +4362,7 @@
wysiwyg: |-
<ul bullet="*"><li><p>foo</p><p>notcode</p></li><li><p>foo</p></li></ul>
<pre class="content-editor-code-block undefined code highlight"><code>code</code></pre>
-05_04__container_blocks__lists__010:
+05_04_00__container_blocks__lists__010:
canonical: |
<ul>
<li>a</li>
@@ -4385,7 +4385,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>a</p></li><li><p>b</p></li><li><p>c</p></li><li><p>d</p></li><li><p>e</p></li><li><p>f</p></li><li><p>g</p></li></ul>
-05_04__container_blocks__lists__011:
+05_04_00__container_blocks__lists__011:
canonical: |
<ol>
<li>
@@ -4412,7 +4412,7 @@
</ol>
wysiwyg: |-
<ol parens="false"><li><p>a</p></li><li><p>b</p></li><li><p>c</p></li></ol>
-05_04__container_blocks__lists__012:
+05_04_00__container_blocks__lists__012:
canonical: |
<ul>
<li>a</li>
@@ -4432,7 +4432,7 @@
wysiwyg: |-
<ul bullet="*"><li><p>a</p></li><li><p>b</p></li><li><p>c</p></li><li><p>d
- e</p></li></ul>
-05_04__container_blocks__lists__013:
+05_04_00__container_blocks__lists__013:
canonical: |
<ol>
<li>
@@ -4460,7 +4460,7 @@
wysiwyg: |-
<ol parens="false"><li><p>a</p></li><li><p>b</p></li></ol>
<pre class="content-editor-code-block undefined code highlight"><code>3. c</code></pre>
-05_04__container_blocks__lists__014:
+05_04_00__container_blocks__lists__014:
canonical: |
<ul>
<li>
@@ -4487,7 +4487,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>a</p></li><li><p>b</p></li><li><p>c</p></li></ul>
-05_04__container_blocks__lists__015:
+05_04_00__container_blocks__lists__015:
canonical: |
<ul>
<li>
@@ -4511,7 +4511,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>a</p></li><li><p></p></li><li><p>c</p></li></ul>
-05_04__container_blocks__lists__016:
+05_04_00__container_blocks__lists__016:
canonical: |
<ul>
<li>
@@ -4540,7 +4540,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>a</p></li><li><p>b</p><p>c</p></li><li><p>d</p></li></ul>
-05_04__container_blocks__lists__017:
+05_04_00__container_blocks__lists__017:
canonical: |
<ul>
<li>
@@ -4567,7 +4567,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>a</p></li><li><p>b</p><pre>[ref]: /url</pre></li><li><p>d</p></li></ul>
-05_04__container_blocks__lists__018:
+05_04_00__container_blocks__lists__018:
canonical: |
<ul>
<li>a</li>
@@ -4594,7 +4594,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>a</p></li><li><p></p><pre class="content-editor-code-block undefined code highlight"><code>b</code></pre></li><li><p>c</p></li></ul>
-05_04__container_blocks__lists__019:
+05_04_00__container_blocks__lists__019:
canonical: |
<ul>
<li>a
@@ -4621,7 +4621,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>a</p><ul bullet="*"><li><p>b</p><p>c</p></li></ul></li><li><p>d</p></li></ul>
-05_04__container_blocks__lists__020:
+05_04_00__container_blocks__lists__020:
canonical: |
<ul>
<li>a
@@ -4642,7 +4642,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>a</p><blockquote multiline="false"><p>b</p></blockquote></li><li><p>c</p></li></ul>
-05_04__container_blocks__lists__021:
+05_04_00__container_blocks__lists__021:
canonical: |
<ul>
<li>a
@@ -4669,7 +4669,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>a</p><blockquote multiline="false"><p>b</p></blockquote><pre class="content-editor-code-block undefined code highlight"><code>c</code></pre></li><li><p>d</p></li></ul>
-05_04__container_blocks__lists__022:
+05_04_00__container_blocks__lists__022:
canonical: |
<ul>
<li>a</li>
@@ -4680,7 +4680,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>a</p></li></ul>
-05_04__container_blocks__lists__023:
+05_04_00__container_blocks__lists__023:
canonical: |
<ul>
<li>a
@@ -4699,7 +4699,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>a</p><ul bullet="*"><li><p>b</p></li></ul></li></ul>
-05_04__container_blocks__lists__024:
+05_04_00__container_blocks__lists__024:
canonical: |
<ol>
<li>
@@ -4720,7 +4720,7 @@
</ol>
wysiwyg: |-
<ol parens="false"><li><p></p><pre class="content-editor-code-block undefined code highlight"><code>foo</code></pre><p>bar</p></li></ol>
-05_04__container_blocks__lists__025:
+05_04_00__container_blocks__lists__025:
canonical: |
<ul>
<li>
@@ -4743,7 +4743,7 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>foo</p><ul bullet="*"><li><p>bar</p></li></ul><p>baz</p></li></ul>
-05_04__container_blocks__lists__026:
+05_04_00__container_blocks__lists__026:
canonical: |
<ul>
<li>
@@ -4780,25 +4780,25 @@
</ul>
wysiwyg: |-
<ul bullet="*"><li><p>a</p><ul bullet="*"><li><p>b</p></li><li><p>c</p></li></ul></li><li><p>d</p><ul bullet="*"><li><p>e</p></li><li><p>f</p></li></ul></li></ul>
-06_01__inlines__001:
+06_01_00__inlines__001:
canonical: |
<p><code>hi</code>lo`</p>
static: |-
<p data-sourcepos="1:1-1:7" dir="auto"><code>hi</code>lo`</p>
wysiwyg: |-
<p><code>hi</code>lo`</p>
-06_02__inlines__backslash_escapes__001:
+06_02_00__inlines__backslash_escapes__001:
canonical: |
<p>!&quot;#$%&amp;'()*+,-./:;&lt;=&gt;?@[\]^_`{|}~</p>
static: |-
<p data-sourcepos="1:1-1:224" dir="auto"><span>!</span>"<span>#</span><span>$</span><span>%</span><span>&amp;</span>'()*+,-./:;&lt;=&gt;?<span>@</span>[\]<span>^</span>_`{|}<span>~</span></p>
wysiwyg: |-
<p>!"#$%&amp;'()*+,-./:;&lt;=&gt;?@[\]^_`{|}~</p>
-06_02__inlines__backslash_escapes__002:
+06_02_00__inlines__backslash_escapes__002:
canonical: "<p>\\\t\\A\\a\\ \\3\\φ\\«</p>\n"
static: "<p data-sourcepos=\"1:1-1:16\" dir=\"auto\">\\\t\\A\\a\\ \\3\\φ\\«</p>"
wysiwyg: "<p>\\\t\\A\\a\\ \\3\\φ\\«</p>"
-06_02__inlines__backslash_escapes__003:
+06_02_00__inlines__backslash_escapes__003:
canonical: |
<p>*not emphasized*
&lt;br/&gt; not a tag
@@ -4829,14 +4829,14 @@
# not a heading
[foo]: /url "not a reference"
&amp;ouml; not a character entity</p>
-06_02__inlines__backslash_escapes__004:
+06_02_00__inlines__backslash_escapes__004:
canonical: |
<p>\<em>emphasis</em></p>
static: |-
<p data-sourcepos="1:1-1:12" dir="auto">\<em>emphasis</em></p>
wysiwyg: |-
<p>\<em>emphasis</em></p>
-06_02__inlines__backslash_escapes__005:
+06_02_00__inlines__backslash_escapes__005:
canonical: |
<p>foo<br />
bar</p>
@@ -4846,14 +4846,14 @@
wysiwyg: |-
<p>foo<br>
bar</p>
-06_02__inlines__backslash_escapes__006:
+06_02_00__inlines__backslash_escapes__006:
canonical: |
<p><code>\[\`</code></p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto"><code>\[\`</code></p>
wysiwyg: |-
<p><code>\[\`</code></p>
-06_02__inlines__backslash_escapes__007:
+06_02_00__inlines__backslash_escapes__007:
canonical: |
<pre><code>\[\]
</code></pre>
@@ -4864,7 +4864,7 @@
</div>
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>\[\]</code></pre>
-06_02__inlines__backslash_escapes__008:
+06_02_00__inlines__backslash_escapes__008:
canonical: |
<pre><code>\[\]
</code></pre>
@@ -4875,28 +4875,28 @@
</div>
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>\[\]</code></pre>
-06_02__inlines__backslash_escapes__009:
+06_02_00__inlines__backslash_escapes__009:
canonical: |
<p><a href="http://example.com?find=%5C*">http://example.com?find=\*</a></p>
static: |-
<p data-sourcepos="1:1-1:28" dir="auto"><a href="http://example.com?find=%5C*" rel="nofollow noreferrer noopener" target="_blank">http://example.com?find=\*</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="http://example.com?find=%5C*">http://example.com?find=\*</a></p>
-06_02__inlines__backslash_escapes__010:
+06_02_00__inlines__backslash_escapes__010:
canonical: |
<a href="/bar\/)">
static: |-
<a href="/bar%5C/)" rel="nofollow noreferrer noopener" target="_blank"></a>
wysiwyg: |-
<p></p>
-06_02__inlines__backslash_escapes__011:
+06_02_00__inlines__backslash_escapes__011:
canonical: |
<p><a href="/bar*" title="ti*tle">foo</a></p>
static: |-
<p data-sourcepos="1:1-1:23" dir="auto"><a href="/bar*" title="ti*tle">foo</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/bar*" title="ti*tle">foo</a></p>
-06_02__inlines__backslash_escapes__012:
+06_02_00__inlines__backslash_escapes__012:
canonical: |
<p><a href="/bar*" title="ti*tle">foo</a></p>
static: |-
@@ -4904,7 +4904,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/bar*" title="ti*tle">foo</a></p>
<pre>[foo]: /bar* "ti*tle"</pre>
-06_02__inlines__backslash_escapes__013:
+06_02_00__inlines__backslash_escapes__013:
canonical: |
<pre><code class="language-foo+bar">foo
</code></pre>
@@ -4915,7 +4915,7 @@
</div>
wysiwyg: |-
<pre language="foo+bar" class="content-editor-code-block undefined code highlight"><code>foo</code></pre>
-06_03__inlines__entity_and_numeric_character_references__001:
+06_03_00__inlines__entity_and_numeric_character_references__001:
canonical: |
<p>  &amp; © Æ Ď
¾ ℋ ⅆ
@@ -4928,21 +4928,21 @@
<p>&nbsp; &amp; © Æ Ď
¾ ℋ ⅆ
∲ ≧̸</p>
-06_03__inlines__entity_and_numeric_character_references__002:
+06_03_00__inlines__entity_and_numeric_character_references__002:
canonical: |
<p># Ӓ Ϡ �</p>
static: |-
<p data-sourcepos="1:1-1:25" dir="auto"># Ӓ Ϡ �</p>
wysiwyg: |-
<p># Ӓ Ϡ �</p>
-06_03__inlines__entity_and_numeric_character_references__003:
+06_03_00__inlines__entity_and_numeric_character_references__003:
canonical: |
<p>&quot; ആ ಫ</p>
static: |-
<p data-sourcepos="1:1-1:22" dir="auto">" ആ ಫ</p>
wysiwyg: |-
<p>" ആ ಫ</p>
-06_03__inlines__entity_and_numeric_character_references__004:
+06_03_00__inlines__entity_and_numeric_character_references__004:
canonical: |
<p>&amp;nbsp &amp;x; &amp;#; &amp;#x;
&amp;#987654321;
@@ -4958,35 +4958,35 @@
&amp;#987654321;
&amp;#abcdef0;
&amp;ThisIsNotDefined; &amp;hi?;</p>
-06_03__inlines__entity_and_numeric_character_references__005:
+06_03_00__inlines__entity_and_numeric_character_references__005:
canonical: |
<p>&amp;copy</p>
static: |-
<p data-sourcepos="1:1-1:5" dir="auto">&amp;copy</p>
wysiwyg: |-
<p>&amp;copy</p>
-06_03__inlines__entity_and_numeric_character_references__006:
+06_03_00__inlines__entity_and_numeric_character_references__006:
canonical: |
<p>&amp;MadeUpEntity;</p>
static: |-
<p data-sourcepos="1:1-1:14" dir="auto">&amp;MadeUpEntity;</p>
wysiwyg: |-
<p>&amp;MadeUpEntity;</p>
-06_03__inlines__entity_and_numeric_character_references__007:
+06_03_00__inlines__entity_and_numeric_character_references__007:
canonical: |
<a href="&ouml;&ouml;.html">
static: |-
<a href="%C3%B6%C3%B6.html" rel="nofollow noreferrer noopener" target="_blank"></a>
wysiwyg: |-
<p></p>
-06_03__inlines__entity_and_numeric_character_references__008:
+06_03_00__inlines__entity_and_numeric_character_references__008:
canonical: |
<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p>
static: |-
<p data-sourcepos="1:1-1:37" dir="auto"><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/f%C3%B6%C3%B6" title="föö">foo</a></p>
-06_03__inlines__entity_and_numeric_character_references__009:
+06_03_00__inlines__entity_and_numeric_character_references__009:
canonical: |
<p><a href="/f%C3%B6%C3%B6" title="föö">foo</a></p>
static: |-
@@ -4994,7 +4994,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/f%C3%B6%C3%B6" title="föö">foo</a></p>
<pre>[foo]: /föö "föö"</pre>
-06_03__inlines__entity_and_numeric_character_references__010:
+06_03_00__inlines__entity_and_numeric_character_references__010:
canonical: |
<pre><code class="language-föö">foo
</code></pre>
@@ -5005,14 +5005,14 @@
</div>
wysiwyg: |-
<pre language="föö" class="content-editor-code-block undefined code highlight"><code>foo</code></pre>
-06_03__inlines__entity_and_numeric_character_references__011:
+06_03_00__inlines__entity_and_numeric_character_references__011:
canonical: |
<p><code>f&amp;ouml;&amp;ouml;</code></p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto"><code>f&amp;ouml;&amp;ouml;</code></p>
wysiwyg: |-
<p><code>f&amp;ouml;&amp;ouml;</code></p>
-06_03__inlines__entity_and_numeric_character_references__012:
+06_03_00__inlines__entity_and_numeric_character_references__012:
canonical: |
<pre><code>f&amp;ouml;f&amp;ouml;
</code></pre>
@@ -5023,7 +5023,7 @@
</div>
wysiwyg: |-
<pre class="content-editor-code-block undefined code highlight"><code>f&amp;ouml;f&amp;ouml;</code></pre>
-06_03__inlines__entity_and_numeric_character_references__013:
+06_03_00__inlines__entity_and_numeric_character_references__013:
canonical: |
<p>*foo*
<em>foo</em></p>
@@ -5033,7 +5033,7 @@
wysiwyg: |-
<p>*foo*
<em>foo</em></p>
-06_03__inlines__entity_and_numeric_character_references__014:
+06_03_00__inlines__entity_and_numeric_character_references__014:
canonical: |
<p>* foo</p>
<ul>
@@ -5047,7 +5047,7 @@
wysiwyg: |-
<p>* foo</p>
<ul bullet="*"><li><p>foo</p></li></ul>
-06_03__inlines__entity_and_numeric_character_references__015:
+06_03_00__inlines__entity_and_numeric_character_references__015:
canonical: |
<p>foo
@@ -5060,60 +5060,60 @@
<p>foo
bar</p>
-06_03__inlines__entity_and_numeric_character_references__016:
+06_03_00__inlines__entity_and_numeric_character_references__016:
canonical: "<p>\tfoo</p>\n"
static: "<p data-sourcepos=\"1:1-1:7\" dir=\"auto\">\tfoo</p>"
wysiwyg: "<p>\tfoo</p>"
-06_03__inlines__entity_and_numeric_character_references__017:
+06_03_00__inlines__entity_and_numeric_character_references__017:
canonical: |
<p>[a](url &quot;tit&quot;)</p>
static: |-
<p data-sourcepos="1:1-1:24" dir="auto"><a href="url" title="tit">a</a></p>
wysiwyg: |-
<p>[a](url "tit")</p>
-06_04__inlines__code_spans__001:
+06_04_00__inlines__code_spans__001:
canonical: |
<p><code>foo</code></p>
static: |-
<p data-sourcepos="1:1-1:5" dir="auto"><code>foo</code></p>
wysiwyg: |-
<p><code>foo</code></p>
-06_04__inlines__code_spans__002:
+06_04_00__inlines__code_spans__002:
canonical: |
<p><code>foo ` bar</code></p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto"><code>foo ` bar</code></p>
wysiwyg: |-
<p><code>foo ` bar</code></p>
-06_04__inlines__code_spans__003:
+06_04_00__inlines__code_spans__003:
canonical: |
<p><code>``</code></p>
static: |-
<p data-sourcepos="1:1-1:6" dir="auto"><code>``</code></p>
wysiwyg: |-
<p><code>``</code></p>
-06_04__inlines__code_spans__004:
+06_04_00__inlines__code_spans__004:
canonical: |
<p><code> `` </code></p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto"><code> `` </code></p>
wysiwyg: |-
<p><code> `` </code></p>
-06_04__inlines__code_spans__005:
+06_04_00__inlines__code_spans__005:
canonical: |
<p><code> a</code></p>
static: |-
<p data-sourcepos="1:1-1:4" dir="auto"><code> a</code></p>
wysiwyg: |-
<p><code> a</code></p>
-06_04__inlines__code_spans__006:
+06_04_00__inlines__code_spans__006:
canonical: |
<p><code> b </code></p>
static: |-
<p data-sourcepos="1:1-1:7" dir="auto"><code> b </code></p>
wysiwyg: |-
<p><code>&nbsp;b&nbsp;</code></p>
-06_04__inlines__code_spans__007:
+06_04_00__inlines__code_spans__007:
canonical: |
<p><code> </code>
<code> </code></p>
@@ -5122,224 +5122,224 @@
<code> </code></p>
wysiwyg: |-
<p></p>
-06_04__inlines__code_spans__008:
+06_04_00__inlines__code_spans__008:
canonical: |
<p><code>foo bar baz</code></p>
static: |-
<p data-sourcepos="1:1-5:2" dir="auto"><code>foo bar baz</code></p>
wysiwyg: |-
<p><code>foo bar baz</code></p>
-06_04__inlines__code_spans__009:
+06_04_00__inlines__code_spans__009:
canonical: |
<p><code>foo </code></p>
static: |-
<p data-sourcepos="1:1-3:2" dir="auto"><code>foo </code></p>
wysiwyg: |-
<p><code>foo </code></p>
-06_04__inlines__code_spans__010:
+06_04_00__inlines__code_spans__010:
canonical: |
<p><code>foo bar baz</code></p>
static: |-
<p data-sourcepos="1:1-2:4" dir="auto"><code>foo bar baz</code></p>
wysiwyg: |-
<p><code>foo bar baz</code></p>
-06_04__inlines__code_spans__011:
+06_04_00__inlines__code_spans__011:
canonical: |
<p><code>foo\</code>bar`</p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto"><code>foo\</code>bar`</p>
wysiwyg: |-
<p><code>foo\</code>bar`</p>
-06_04__inlines__code_spans__012:
+06_04_00__inlines__code_spans__012:
canonical: |
<p><code>foo`bar</code></p>
static: |-
<p data-sourcepos="1:1-1:11" dir="auto"><code>foo`bar</code></p>
wysiwyg: |-
<p><code>foo`bar</code></p>
-06_04__inlines__code_spans__013:
+06_04_00__inlines__code_spans__013:
canonical: |
<p><code>foo `` bar</code></p>
static: |-
<p data-sourcepos="1:1-1:14" dir="auto"><code>foo `` bar</code></p>
wysiwyg: |-
<p><code>foo `` bar</code></p>
-06_04__inlines__code_spans__014:
+06_04_00__inlines__code_spans__014:
canonical: |
<p>*foo<code>*</code></p>
static: |-
<p data-sourcepos="1:1-1:7" dir="auto">*foo<code>*</code></p>
wysiwyg: |-
<p>*foo<code>*</code></p>
-06_04__inlines__code_spans__015:
+06_04_00__inlines__code_spans__015:
canonical: |
<p>[not a <code>link](/foo</code>)</p>
static: |-
<p data-sourcepos="1:1-1:20" dir="auto">[not a <code>link](/foo</code>)</p>
wysiwyg: |-
<p>[not a <code>link](/foo</code>)</p>
-06_04__inlines__code_spans__016:
+06_04_00__inlines__code_spans__016:
canonical: |
<p><code>&lt;a href=&quot;</code>&quot;&gt;`</p>
static: |-
<p data-sourcepos="1:1-1:14" dir="auto"><code>&lt;a href="</code>"&gt;`</p>
wysiwyg: |-
<p><code>&lt;a href="</code>"&gt;`</p>
-06_04__inlines__code_spans__017:
+06_04_00__inlines__code_spans__017:
canonical: |
<p><a href="`">`</p>
static: |-
<p data-sourcepos="1:1-1:13" dir="auto"><a href="%60" rel="nofollow noreferrer noopener" target="_blank">`</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="`">`</a></p>
-06_04__inlines__code_spans__018:
+06_04_00__inlines__code_spans__018:
canonical: |
<p><code>&lt;http://foo.bar.</code>baz&gt;`</p>
static: |-
<p data-sourcepos="1:1-1:23" dir="auto"><code>&lt;http://foo.bar.</code>baz&gt;`</p>
wysiwyg: |-
<p><code>&lt;http://foo.bar.</code>baz&gt;`</p>
-06_04__inlines__code_spans__019:
+06_04_00__inlines__code_spans__019:
canonical: |
<p><a href="http://foo.bar.%60baz">http://foo.bar.`baz</a>`</p>
static: |-
<p data-sourcepos="1:1-1:22" dir="auto"><a href="http://foo.bar.%60baz" rel="nofollow noreferrer noopener" target="_blank">http://foo.bar.`baz</a>`</p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="http://foo.bar.%60baz">http://foo.bar.`baz</a>`</p>
-06_04__inlines__code_spans__020:
+06_04_00__inlines__code_spans__020:
canonical: |
<p>```foo``</p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto">```foo``</p>
wysiwyg: |-
<p>```foo``</p>
-06_04__inlines__code_spans__021:
+06_04_00__inlines__code_spans__021:
canonical: |
<p>`foo</p>
static: |-
<p data-sourcepos="1:1-1:4" dir="auto">`foo</p>
wysiwyg: |-
<p>`foo</p>
-06_04__inlines__code_spans__022:
+06_04_00__inlines__code_spans__022:
canonical: |
<p>`foo<code>bar</code></p>
static: |-
<p data-sourcepos="1:1-1:11" dir="auto">`foo<code>bar</code></p>
wysiwyg: |-
<p>`foo<code>bar</code></p>
-06_05__inlines__emphasis_and_strong_emphasis__001:
+06_05_00__inlines__emphasis_and_strong_emphasis__001:
canonical: |
<p><em>foo bar</em></p>
static: |-
<p data-sourcepos="1:1-1:9" dir="auto"><em>foo bar</em></p>
wysiwyg: |-
<p><em>foo bar</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__002:
+06_05_00__inlines__emphasis_and_strong_emphasis__002:
canonical: |
<p>a * foo bar*</p>
static: |-
<p data-sourcepos="1:1-1:12" dir="auto">a * foo bar*</p>
wysiwyg: |-
<p>a * foo bar*</p>
-06_05__inlines__emphasis_and_strong_emphasis__003:
+06_05_00__inlines__emphasis_and_strong_emphasis__003:
canonical: |
<p>a*&quot;foo&quot;*</p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto">a*"foo"*</p>
wysiwyg: |-
<p>a*"foo"*</p>
-06_05__inlines__emphasis_and_strong_emphasis__004:
+06_05_00__inlines__emphasis_and_strong_emphasis__004:
canonical: |
<p>* a *</p>
static: |-
<p data-sourcepos="1:1-1:7" dir="auto">* a *</p>
wysiwyg: |-
<p>*&nbsp;a&nbsp;*</p>
-06_05__inlines__emphasis_and_strong_emphasis__005:
+06_05_00__inlines__emphasis_and_strong_emphasis__005:
canonical: |
<p>foo<em>bar</em></p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto">foo<em>bar</em></p>
wysiwyg: |-
<p>foo<em>bar</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__006:
+06_05_00__inlines__emphasis_and_strong_emphasis__006:
canonical: |
<p>5<em>6</em>78</p>
static: |-
<p data-sourcepos="1:1-1:6" dir="auto">5<em>6</em>78</p>
wysiwyg: |-
<p>5<em>6</em>78</p>
-06_05__inlines__emphasis_and_strong_emphasis__007:
+06_05_00__inlines__emphasis_and_strong_emphasis__007:
canonical: |
<p><em>foo bar</em></p>
static: |-
<p data-sourcepos="1:1-1:9" dir="auto"><em>foo bar</em></p>
wysiwyg: |-
<p><em>foo bar</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__008:
+06_05_00__inlines__emphasis_and_strong_emphasis__008:
canonical: |
<p>_ foo bar_</p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto">_ foo bar_</p>
wysiwyg: |-
<p>_ foo bar_</p>
-06_05__inlines__emphasis_and_strong_emphasis__009:
+06_05_00__inlines__emphasis_and_strong_emphasis__009:
canonical: |
<p>a_&quot;foo&quot;_</p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto">a_"foo"_</p>
wysiwyg: |-
<p>a_"foo"_</p>
-06_05__inlines__emphasis_and_strong_emphasis__010:
+06_05_00__inlines__emphasis_and_strong_emphasis__010:
canonical: |
<p>foo_bar_</p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto">foo_bar_</p>
wysiwyg: |-
<p>foo_bar_</p>
-06_05__inlines__emphasis_and_strong_emphasis__011:
+06_05_00__inlines__emphasis_and_strong_emphasis__011:
canonical: |
<p>5_6_78</p>
static: |-
<p data-sourcepos="1:1-1:6" dir="auto">5_6_78</p>
wysiwyg: |-
<p>5_6_78</p>
-06_05__inlines__emphasis_and_strong_emphasis__012:
+06_05_00__inlines__emphasis_and_strong_emphasis__012:
canonical: |
<p>приÑтанÑм_ÑтремÑÑ‚ÑÑ_</p>
static: |-
<p data-sourcepos="1:1-1:38" dir="auto">приÑтанÑм_ÑтремÑÑ‚ÑÑ_</p>
wysiwyg: |-
<p>приÑтанÑм_ÑтремÑÑ‚ÑÑ_</p>
-06_05__inlines__emphasis_and_strong_emphasis__013:
+06_05_00__inlines__emphasis_and_strong_emphasis__013:
canonical: |
<p>aa_&quot;bb&quot;_cc</p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto">aa_"bb"_cc</p>
wysiwyg: |-
<p>aa_"bb"_cc</p>
-06_05__inlines__emphasis_and_strong_emphasis__014:
+06_05_00__inlines__emphasis_and_strong_emphasis__014:
canonical: |
<p>foo-<em>(bar)</em></p>
static: |-
<p data-sourcepos="1:1-1:11" dir="auto">foo-<em>(bar)</em></p>
wysiwyg: |-
<p>foo-<em>(bar)</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__015:
+06_05_00__inlines__emphasis_and_strong_emphasis__015:
canonical: |
<p>_foo*</p>
static: |-
<p data-sourcepos="1:1-1:5" dir="auto">_foo*</p>
wysiwyg: |-
<p>_foo*</p>
-06_05__inlines__emphasis_and_strong_emphasis__016:
+06_05_00__inlines__emphasis_and_strong_emphasis__016:
canonical: |
<p>*foo bar *</p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto">*foo bar *</p>
wysiwyg: |-
<p>*foo bar *</p>
-06_05__inlines__emphasis_and_strong_emphasis__017:
+06_05_00__inlines__emphasis_and_strong_emphasis__017:
canonical: |
<p>*foo bar
*</p>
@@ -5349,119 +5349,119 @@
wysiwyg: |-
<p>*foo bar
*</p>
-06_05__inlines__emphasis_and_strong_emphasis__018:
+06_05_00__inlines__emphasis_and_strong_emphasis__018:
canonical: |
<p>*(*foo)</p>
static: |-
<p data-sourcepos="1:1-1:7" dir="auto">*(*foo)</p>
wysiwyg: |-
<p>*(*foo)</p>
-06_05__inlines__emphasis_and_strong_emphasis__019:
+06_05_00__inlines__emphasis_and_strong_emphasis__019:
canonical: |
<p><em>(<em>foo</em>)</em></p>
static: |-
<p data-sourcepos="1:1-1:9" dir="auto"><em>(<em>foo</em>)</em></p>
wysiwyg: |-
<p><em>(foo</em>)</p>
-06_05__inlines__emphasis_and_strong_emphasis__020:
+06_05_00__inlines__emphasis_and_strong_emphasis__020:
canonical: |
<p><em>foo</em>bar</p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto"><em>foo</em>bar</p>
wysiwyg: |-
<p><em>foo</em>bar</p>
-06_05__inlines__emphasis_and_strong_emphasis__021:
+06_05_00__inlines__emphasis_and_strong_emphasis__021:
canonical: |
<p>_foo bar _</p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto">_foo bar _</p>
wysiwyg: |-
<p>_foo bar _</p>
-06_05__inlines__emphasis_and_strong_emphasis__022:
+06_05_00__inlines__emphasis_and_strong_emphasis__022:
canonical: |
<p>_(_foo)</p>
static: |-
<p data-sourcepos="1:1-1:7" dir="auto">_(_foo)</p>
wysiwyg: |-
<p>_(_foo)</p>
-06_05__inlines__emphasis_and_strong_emphasis__023:
+06_05_00__inlines__emphasis_and_strong_emphasis__023:
canonical: |
<p><em>(<em>foo</em>)</em></p>
static: |-
<p data-sourcepos="1:1-1:9" dir="auto"><em>(<em>foo</em>)</em></p>
wysiwyg: |-
<p><em>(foo</em>)</p>
-06_05__inlines__emphasis_and_strong_emphasis__024:
+06_05_00__inlines__emphasis_and_strong_emphasis__024:
canonical: |
<p>_foo_bar</p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto">_foo_bar</p>
wysiwyg: |-
<p>_foo_bar</p>
-06_05__inlines__emphasis_and_strong_emphasis__025:
+06_05_00__inlines__emphasis_and_strong_emphasis__025:
canonical: |
<p>_приÑтанÑм_ÑтремÑÑ‚ÑÑ</p>
static: |-
<p data-sourcepos="1:1-1:38" dir="auto">_приÑтанÑм_ÑтремÑÑ‚ÑÑ</p>
wysiwyg: |-
<p>_приÑтанÑм_ÑтремÑÑ‚ÑÑ</p>
-06_05__inlines__emphasis_and_strong_emphasis__026:
+06_05_00__inlines__emphasis_and_strong_emphasis__026:
canonical: |
<p><em>foo_bar_baz</em></p>
static: |-
<p data-sourcepos="1:1-1:13" dir="auto"><em>foo_bar_baz</em></p>
wysiwyg: |-
<p><em>foo_bar_baz</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__027:
+06_05_00__inlines__emphasis_and_strong_emphasis__027:
canonical: |
<p><em>(bar)</em>.</p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto"><em>(bar)</em>.</p>
wysiwyg: |-
<p><em>(bar)</em>.</p>
-06_05__inlines__emphasis_and_strong_emphasis__028:
+06_05_00__inlines__emphasis_and_strong_emphasis__028:
canonical: |
<p><strong>foo bar</strong></p>
static: |-
<p data-sourcepos="1:1-1:11" dir="auto"><strong>foo bar</strong></p>
wysiwyg: |-
<p><strong>foo bar</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__029:
+06_05_00__inlines__emphasis_and_strong_emphasis__029:
canonical: |
<p>** foo bar**</p>
static: |-
<p data-sourcepos="1:1-1:12" dir="auto">** foo bar**</p>
wysiwyg: |-
<p>** foo bar**</p>
-06_05__inlines__emphasis_and_strong_emphasis__030:
+06_05_00__inlines__emphasis_and_strong_emphasis__030:
canonical: |
<p>a**&quot;foo&quot;**</p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto">a**"foo"**</p>
wysiwyg: |-
<p>a**"foo"**</p>
-06_05__inlines__emphasis_and_strong_emphasis__031:
+06_05_00__inlines__emphasis_and_strong_emphasis__031:
canonical: |
<p>foo<strong>bar</strong></p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto">foo<strong>bar</strong></p>
wysiwyg: |-
<p>foo<strong>bar</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__032:
+06_05_00__inlines__emphasis_and_strong_emphasis__032:
canonical: |
<p><strong>foo bar</strong></p>
static: |-
<p data-sourcepos="1:1-1:11" dir="auto"><strong>foo bar</strong></p>
wysiwyg: |-
<p><strong>foo bar</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__033:
+06_05_00__inlines__emphasis_and_strong_emphasis__033:
canonical: |
<p>__ foo bar__</p>
static: |-
<p data-sourcepos="1:1-1:12" dir="auto">__ foo bar__</p>
wysiwyg: |-
<p>__ foo bar__</p>
-06_05__inlines__emphasis_and_strong_emphasis__034:
+06_05_00__inlines__emphasis_and_strong_emphasis__034:
canonical: |
<p>__
foo bar__</p>
@@ -5471,70 +5471,70 @@
wysiwyg: |-
<p>__
foo bar__</p>
-06_05__inlines__emphasis_and_strong_emphasis__035:
+06_05_00__inlines__emphasis_and_strong_emphasis__035:
canonical: |
<p>a__&quot;foo&quot;__</p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto">a__"foo"__</p>
wysiwyg: |-
<p>a__"foo"__</p>
-06_05__inlines__emphasis_and_strong_emphasis__036:
+06_05_00__inlines__emphasis_and_strong_emphasis__036:
canonical: |
<p>foo__bar__</p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto">foo__bar__</p>
wysiwyg: |-
<p>foo__bar__</p>
-06_05__inlines__emphasis_and_strong_emphasis__037:
+06_05_00__inlines__emphasis_and_strong_emphasis__037:
canonical: |
<p>5__6__78</p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto">5__6__78</p>
wysiwyg: |-
<p>5__6__78</p>
-06_05__inlines__emphasis_and_strong_emphasis__038:
+06_05_00__inlines__emphasis_and_strong_emphasis__038:
canonical: |
<p>приÑтанÑм__ÑтремÑÑ‚ÑÑ__</p>
static: |-
<p data-sourcepos="1:1-1:40" dir="auto">приÑтанÑм__ÑтремÑÑ‚ÑÑ__</p>
wysiwyg: |-
<p>приÑтанÑм__ÑтремÑÑ‚ÑÑ__</p>
-06_05__inlines__emphasis_and_strong_emphasis__039:
+06_05_00__inlines__emphasis_and_strong_emphasis__039:
canonical: |
<p><strong>foo, <strong>bar</strong>, baz</strong></p>
static: |-
<p data-sourcepos="1:1-1:21" dir="auto"><strong>foo, <strong>bar</strong>, baz</strong></p>
wysiwyg: |-
<p><strong>foo, bar</strong>, baz</p>
-06_05__inlines__emphasis_and_strong_emphasis__040:
+06_05_00__inlines__emphasis_and_strong_emphasis__040:
canonical: |
<p>foo-<strong>(bar)</strong></p>
static: |-
<p data-sourcepos="1:1-1:13" dir="auto">foo-<strong>(bar)</strong></p>
wysiwyg: |-
<p>foo-<strong>(bar)</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__041:
+06_05_00__inlines__emphasis_and_strong_emphasis__041:
canonical: |
<p>**foo bar **</p>
static: |-
<p data-sourcepos="1:1-1:12" dir="auto">**foo bar **</p>
wysiwyg: |-
<p>**foo bar **</p>
-06_05__inlines__emphasis_and_strong_emphasis__042:
+06_05_00__inlines__emphasis_and_strong_emphasis__042:
canonical: |
<p>**(**foo)</p>
static: |-
<p data-sourcepos="1:1-1:9" dir="auto">**(**foo)</p>
wysiwyg: |-
<p>**(**foo)</p>
-06_05__inlines__emphasis_and_strong_emphasis__043:
+06_05_00__inlines__emphasis_and_strong_emphasis__043:
canonical: |
<p><em>(<strong>foo</strong>)</em></p>
static: |-
<p data-sourcepos="1:1-1:11" dir="auto"><em>(<strong>foo</strong>)</em></p>
wysiwyg: |-
<p><em>(</em><strong><em>foo</em></strong><em>)</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__044:
+06_05_00__inlines__emphasis_and_strong_emphasis__044:
canonical: |
<p><strong>Gomphocarpus (<em>Gomphocarpus physocarpus</em>, syn.
<em>Asclepias physocarpa</em>)</strong></p>
@@ -5544,77 +5544,77 @@
wysiwyg: |-
<p><strong>Gomphocarpus (<em>Gomphocarpus physocarpus</em>, syn.
<em>Asclepias physocarpa</em>)</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__045:
+06_05_00__inlines__emphasis_and_strong_emphasis__045:
canonical: |
<p><strong>foo &quot;<em>bar</em>&quot; foo</strong></p>
static: |-
<p data-sourcepos="1:1-1:19" dir="auto"><strong>foo "<em>bar</em>" foo</strong></p>
wysiwyg: |-
<p><strong>foo "<em>bar</em>" foo</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__046:
+06_05_00__inlines__emphasis_and_strong_emphasis__046:
canonical: |
<p><strong>foo</strong>bar</p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto"><strong>foo</strong>bar</p>
wysiwyg: |-
<p><strong>foo</strong>bar</p>
-06_05__inlines__emphasis_and_strong_emphasis__047:
+06_05_00__inlines__emphasis_and_strong_emphasis__047:
canonical: |
<p>__foo bar __</p>
static: |-
<p data-sourcepos="1:1-1:12" dir="auto">__foo bar __</p>
wysiwyg: |-
<p>__foo bar __</p>
-06_05__inlines__emphasis_and_strong_emphasis__048:
+06_05_00__inlines__emphasis_and_strong_emphasis__048:
canonical: |
<p>__(__foo)</p>
static: |-
<p data-sourcepos="1:1-1:9" dir="auto">__(__foo)</p>
wysiwyg: |-
<p>__(__foo)</p>
-06_05__inlines__emphasis_and_strong_emphasis__049:
+06_05_00__inlines__emphasis_and_strong_emphasis__049:
canonical: |
<p><em>(<strong>foo</strong>)</em></p>
static: |-
<p data-sourcepos="1:1-1:11" dir="auto"><em>(<strong>foo</strong>)</em></p>
wysiwyg: |-
<p><em>(</em><strong><em>foo</em></strong><em>)</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__050:
+06_05_00__inlines__emphasis_and_strong_emphasis__050:
canonical: |
<p>__foo__bar</p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto">__foo__bar</p>
wysiwyg: |-
<p>__foo__bar</p>
-06_05__inlines__emphasis_and_strong_emphasis__051:
+06_05_00__inlines__emphasis_and_strong_emphasis__051:
canonical: |
<p>__приÑтанÑм__ÑтремÑÑ‚ÑÑ</p>
static: |-
<p data-sourcepos="1:1-1:40" dir="auto">__приÑтанÑм__ÑтремÑÑ‚ÑÑ</p>
wysiwyg: |-
<p>__приÑтанÑм__ÑтремÑÑ‚ÑÑ</p>
-06_05__inlines__emphasis_and_strong_emphasis__052:
+06_05_00__inlines__emphasis_and_strong_emphasis__052:
canonical: |
<p><strong>foo__bar__baz</strong></p>
static: |-
<p data-sourcepos="1:1-1:17" dir="auto"><strong>foo__bar__baz</strong></p>
wysiwyg: |-
<p><strong>foo__bar__baz</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__053:
+06_05_00__inlines__emphasis_and_strong_emphasis__053:
canonical: |
<p><strong>(bar)</strong>.</p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto"><strong>(bar)</strong>.</p>
wysiwyg: |-
<p><strong>(bar)</strong>.</p>
-06_05__inlines__emphasis_and_strong_emphasis__054:
+06_05_00__inlines__emphasis_and_strong_emphasis__054:
canonical: |
<p><em>foo <a href="/url">bar</a></em></p>
static: |-
<p data-sourcepos="1:1-1:17" dir="auto"><em>foo <a href="/url">bar</a></em></p>
wysiwyg: |-
<p><em>foo </em><a target="_blank" rel="noopener noreferrer nofollow" href="/url"><em>bar</em></a></p>
-06_05__inlines__emphasis_and_strong_emphasis__055:
+06_05_00__inlines__emphasis_and_strong_emphasis__055:
canonical: |
<p><em>foo
bar</em></p>
@@ -5624,126 +5624,126 @@
wysiwyg: |-
<p><em>foo
bar</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__056:
+06_05_00__inlines__emphasis_and_strong_emphasis__056:
canonical: |
<p><em>foo <strong>bar</strong> baz</em></p>
static: |-
<p data-sourcepos="1:1-1:17" dir="auto"><em>foo <strong>bar</strong> baz</em></p>
wysiwyg: |-
<p><em>foo </em><strong><em>bar</em></strong><em> baz</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__057:
+06_05_00__inlines__emphasis_and_strong_emphasis__057:
canonical: |
<p><em>foo <em>bar</em> baz</em></p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto"><em>foo <em>bar</em> baz</em></p>
wysiwyg: |-
<p><em>foo bar</em> baz</p>
-06_05__inlines__emphasis_and_strong_emphasis__058:
+06_05_00__inlines__emphasis_and_strong_emphasis__058:
canonical: |
<p><em><em>foo</em> bar</em></p>
static: |-
<p data-sourcepos="1:1-1:11" dir="auto"><em><em>foo</em> bar</em></p>
wysiwyg: |-
<p><em>foo</em> bar</p>
-06_05__inlines__emphasis_and_strong_emphasis__059:
+06_05_00__inlines__emphasis_and_strong_emphasis__059:
canonical: |
<p><em>foo <em>bar</em></em></p>
static: |-
<p data-sourcepos="1:1-1:11" dir="auto"><em>foo <em>bar</em></em></p>
wysiwyg: |-
<p><em>foo bar</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__060:
+06_05_00__inlines__emphasis_and_strong_emphasis__060:
canonical: |
<p><em>foo <strong>bar</strong> baz</em></p>
static: |-
<p data-sourcepos="1:1-1:17" dir="auto"><em>foo <strong>bar</strong> baz</em></p>
wysiwyg: |-
<p><em>foo </em><strong><em>bar</em></strong><em> baz</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__061:
+06_05_00__inlines__emphasis_and_strong_emphasis__061:
canonical: |
<p><em>foo<strong>bar</strong>baz</em></p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto"><em>foo<strong>bar</strong>baz</em></p>
wysiwyg: |-
<p><em>foo</em><strong><em>bar</em></strong><em>baz</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__062:
+06_05_00__inlines__emphasis_and_strong_emphasis__062:
canonical: |
<p><em>foo**bar</em></p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto"><em>foo**bar</em></p>
wysiwyg: |-
<p><em>foo**bar</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__063:
+06_05_00__inlines__emphasis_and_strong_emphasis__063:
canonical: |
<p><em><strong>foo</strong> bar</em></p>
static: |-
<p data-sourcepos="1:1-1:13" dir="auto"><em><strong>foo</strong> bar</em></p>
wysiwyg: |-
<p><strong><em>foo</em></strong><em> bar</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__064:
+06_05_00__inlines__emphasis_and_strong_emphasis__064:
canonical: |
<p><em>foo <strong>bar</strong></em></p>
static: |-
<p data-sourcepos="1:1-1:13" dir="auto"><em>foo <strong>bar</strong></em></p>
wysiwyg: |-
<p><em>foo </em><strong><em>bar</em></strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__065:
+06_05_00__inlines__emphasis_and_strong_emphasis__065:
canonical: |
<p><em>foo<strong>bar</strong></em></p>
static: |-
<p data-sourcepos="1:1-1:12" dir="auto"><em>foo<strong>bar</strong></em></p>
wysiwyg: |-
<p><em>foo</em><strong><em>bar</em></strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__066:
+06_05_00__inlines__emphasis_and_strong_emphasis__066:
canonical: |
<p>foo<em><strong>bar</strong></em>baz</p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto">foo<em><strong>bar</strong></em>baz</p>
wysiwyg: |-
<p>foo<strong><em>bar</em></strong>baz</p>
-06_05__inlines__emphasis_and_strong_emphasis__067:
+06_05_00__inlines__emphasis_and_strong_emphasis__067:
canonical: |
<p>foo<strong><strong><strong>bar</strong></strong></strong>***baz</p>
static: |-
<p data-sourcepos="1:1-1:24" dir="auto">foo<strong><strong><strong>bar</strong></strong></strong>***baz</p>
wysiwyg: |-
<p>foo<strong>bar</strong>***baz</p>
-06_05__inlines__emphasis_and_strong_emphasis__068:
+06_05_00__inlines__emphasis_and_strong_emphasis__068:
canonical: |
<p><em>foo <strong>bar <em>baz</em> bim</strong> bop</em></p>
static: |-
<p data-sourcepos="1:1-1:27" dir="auto"><em>foo <strong>bar <em>baz</em> bim</strong> bop</em></p>
wysiwyg: |-
<p><em>foo </em><strong><em>bar baz</em> bim</strong> bop</p>
-06_05__inlines__emphasis_and_strong_emphasis__069:
+06_05_00__inlines__emphasis_and_strong_emphasis__069:
canonical: |
<p><em>foo <a href="/url"><em>bar</em></a></em></p>
static: |-
<p data-sourcepos="1:1-1:19" dir="auto"><em>foo <a href="/url"><em>bar</em></a></em></p>
wysiwyg: |-
<p><em>foo </em><a target="_blank" rel="noopener noreferrer nofollow" href="/url"><em>bar</em></a></p>
-06_05__inlines__emphasis_and_strong_emphasis__070:
+06_05_00__inlines__emphasis_and_strong_emphasis__070:
canonical: |
<p>** is not an empty emphasis</p>
static: |-
<p data-sourcepos="1:1-1:27" dir="auto">** is not an empty emphasis</p>
wysiwyg: |-
<p>** is not an empty emphasis</p>
-06_05__inlines__emphasis_and_strong_emphasis__071:
+06_05_00__inlines__emphasis_and_strong_emphasis__071:
canonical: |
<p>**** is not an empty strong emphasis</p>
static: |-
<p data-sourcepos="1:1-1:36" dir="auto">**** is not an empty strong emphasis</p>
wysiwyg: |-
<p>**** is not an empty strong emphasis</p>
-06_05__inlines__emphasis_and_strong_emphasis__072:
+06_05_00__inlines__emphasis_and_strong_emphasis__072:
canonical: |
<p><strong>foo <a href="/url">bar</a></strong></p>
static: |-
<p data-sourcepos="1:1-1:19" dir="auto"><strong>foo <a href="/url">bar</a></strong></p>
wysiwyg: |-
<p><strong>foo </strong><a target="_blank" rel="noopener noreferrer nofollow" href="/url"><strong>bar</strong></a></p>
-06_05__inlines__emphasis_and_strong_emphasis__073:
+06_05_00__inlines__emphasis_and_strong_emphasis__073:
canonical: |
<p><strong>foo
bar</strong></p>
@@ -5753,63 +5753,63 @@
wysiwyg: |-
<p><strong>foo
bar</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__074:
+06_05_00__inlines__emphasis_and_strong_emphasis__074:
canonical: |
<p><strong>foo <em>bar</em> baz</strong></p>
static: |-
<p data-sourcepos="1:1-1:17" dir="auto"><strong>foo <em>bar</em> baz</strong></p>
wysiwyg: |-
<p><strong>foo <em>bar</em> baz</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__075:
+06_05_00__inlines__emphasis_and_strong_emphasis__075:
canonical: |
<p><strong>foo <strong>bar</strong> baz</strong></p>
static: |-
<p data-sourcepos="1:1-1:19" dir="auto"><strong>foo <strong>bar</strong> baz</strong></p>
wysiwyg: |-
<p><strong>foo bar</strong> baz</p>
-06_05__inlines__emphasis_and_strong_emphasis__076:
+06_05_00__inlines__emphasis_and_strong_emphasis__076:
canonical: |
<p><strong><strong>foo</strong> bar</strong></p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto"><strong><strong>foo</strong> bar</strong></p>
wysiwyg: |-
<p><strong>foo</strong> bar</p>
-06_05__inlines__emphasis_and_strong_emphasis__077:
+06_05_00__inlines__emphasis_and_strong_emphasis__077:
canonical: |
<p><strong>foo <strong>bar</strong></strong></p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto"><strong>foo <strong>bar</strong></strong></p>
wysiwyg: |-
<p><strong>foo bar</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__078:
+06_05_00__inlines__emphasis_and_strong_emphasis__078:
canonical: |
<p><strong>foo <em>bar</em> baz</strong></p>
static: |-
<p data-sourcepos="1:1-1:17" dir="auto"><strong>foo <em>bar</em> baz</strong></p>
wysiwyg: |-
<p><strong>foo <em>bar</em> baz</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__079:
+06_05_00__inlines__emphasis_and_strong_emphasis__079:
canonical: |
<p><strong>foo<em>bar</em>baz</strong></p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto"><strong>foo<em>bar</em>baz</strong></p>
wysiwyg: |-
<p><strong>foo<em>bar</em>baz</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__080:
+06_05_00__inlines__emphasis_and_strong_emphasis__080:
canonical: |
<p><strong><em>foo</em> bar</strong></p>
static: |-
<p data-sourcepos="1:1-1:13" dir="auto"><strong><em>foo</em> bar</strong></p>
wysiwyg: |-
<p><strong><em>foo</em> bar</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__081:
+06_05_00__inlines__emphasis_and_strong_emphasis__081:
canonical: |
<p><strong>foo <em>bar</em></strong></p>
static: |-
<p data-sourcepos="1:1-1:13" dir="auto"><strong>foo <em>bar</em></strong></p>
wysiwyg: |-
<p><strong>foo <em>bar</em></strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__082:
+06_05_00__inlines__emphasis_and_strong_emphasis__082:
canonical: |
<p><strong>foo <em>bar <strong>baz</strong>
bim</em> bop</strong></p>
@@ -5819,357 +5819,357 @@
wysiwyg: |-
<p><strong>foo <em>bar baz</em></strong><em>
bim</em> bop</p>
-06_05__inlines__emphasis_and_strong_emphasis__083:
+06_05_00__inlines__emphasis_and_strong_emphasis__083:
canonical: |
<p><strong>foo <a href="/url"><em>bar</em></a></strong></p>
static: |-
<p data-sourcepos="1:1-1:21" dir="auto"><strong>foo <a href="/url"><em>bar</em></a></strong></p>
wysiwyg: |-
<p><strong>foo </strong><a target="_blank" rel="noopener noreferrer nofollow" href="/url"><strong><em>bar</em></strong></a></p>
-06_05__inlines__emphasis_and_strong_emphasis__084:
+06_05_00__inlines__emphasis_and_strong_emphasis__084:
canonical: |
<p>__ is not an empty emphasis</p>
static: |-
<p data-sourcepos="1:1-1:27" dir="auto">__ is not an empty emphasis</p>
wysiwyg: |-
<p>__ is not an empty emphasis</p>
-06_05__inlines__emphasis_and_strong_emphasis__085:
+06_05_00__inlines__emphasis_and_strong_emphasis__085:
canonical: |
<p>____ is not an empty strong emphasis</p>
static: |-
<p data-sourcepos="1:1-1:36" dir="auto">____ is not an empty strong emphasis</p>
wysiwyg: |-
<p>____ is not an empty strong emphasis</p>
-06_05__inlines__emphasis_and_strong_emphasis__086:
+06_05_00__inlines__emphasis_and_strong_emphasis__086:
canonical: |
<p>foo ***</p>
static: |-
<p data-sourcepos="1:1-1:7" dir="auto">foo ***</p>
wysiwyg: |-
<p>foo ***</p>
-06_05__inlines__emphasis_and_strong_emphasis__087:
+06_05_00__inlines__emphasis_and_strong_emphasis__087:
canonical: |
<p>foo <em>*</em></p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto">foo <em>*</em></p>
wysiwyg: |-
<p>foo <em>*</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__088:
+06_05_00__inlines__emphasis_and_strong_emphasis__088:
canonical: |
<p>foo <em>_</em></p>
static: |-
<p data-sourcepos="1:1-1:7" dir="auto">foo <em>_</em></p>
wysiwyg: |-
<p>foo <em>_</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__089:
+06_05_00__inlines__emphasis_and_strong_emphasis__089:
canonical: |
<p>foo *****</p>
static: |-
<p data-sourcepos="1:1-1:9" dir="auto">foo *****</p>
wysiwyg: |-
<p>foo *****</p>
-06_05__inlines__emphasis_and_strong_emphasis__090:
+06_05_00__inlines__emphasis_and_strong_emphasis__090:
canonical: |
<p>foo <strong>*</strong></p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto">foo <strong>*</strong></p>
wysiwyg: |-
<p>foo <strong>*</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__091:
+06_05_00__inlines__emphasis_and_strong_emphasis__091:
canonical: |
<p>foo <strong>_</strong></p>
static: |-
<p data-sourcepos="1:1-1:9" dir="auto">foo <strong>_</strong></p>
wysiwyg: |-
<p>foo <strong>_</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__092:
+06_05_00__inlines__emphasis_and_strong_emphasis__092:
canonical: |
<p>*<em>foo</em></p>
static: |-
<p data-sourcepos="1:1-1:6" dir="auto">*<em>foo</em></p>
wysiwyg: |-
<p>*<em>foo</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__093:
+06_05_00__inlines__emphasis_and_strong_emphasis__093:
canonical: |
<p><em>foo</em>*</p>
static: |-
<p data-sourcepos="1:1-1:6" dir="auto"><em>foo</em>*</p>
wysiwyg: |-
<p><em>foo</em>*</p>
-06_05__inlines__emphasis_and_strong_emphasis__094:
+06_05_00__inlines__emphasis_and_strong_emphasis__094:
canonical: |
<p>*<strong>foo</strong></p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto">*<strong>foo</strong></p>
wysiwyg: |-
<p>*<strong>foo</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__095:
+06_05_00__inlines__emphasis_and_strong_emphasis__095:
canonical: |
<p>***<em>foo</em></p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto">***<em>foo</em></p>
wysiwyg: |-
<p>***<em>foo</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__096:
+06_05_00__inlines__emphasis_and_strong_emphasis__096:
canonical: |
<p><strong>foo</strong>*</p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto"><strong>foo</strong>*</p>
wysiwyg: |-
<p><strong>foo</strong>*</p>
-06_05__inlines__emphasis_and_strong_emphasis__097:
+06_05_00__inlines__emphasis_and_strong_emphasis__097:
canonical: |
<p><em>foo</em>***</p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto"><em>foo</em>***</p>
wysiwyg: |-
<p><em>foo</em>***</p>
-06_05__inlines__emphasis_and_strong_emphasis__098:
+06_05_00__inlines__emphasis_and_strong_emphasis__098:
canonical: |
<p>foo ___</p>
static: |-
<p data-sourcepos="1:1-1:7" dir="auto">foo ___</p>
wysiwyg: |-
<p>foo ___</p>
-06_05__inlines__emphasis_and_strong_emphasis__099:
+06_05_00__inlines__emphasis_and_strong_emphasis__099:
canonical: |
<p>foo <em>_</em></p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto">foo <em>_</em></p>
wysiwyg: |-
<p>foo <em>_</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__100:
+06_05_00__inlines__emphasis_and_strong_emphasis__100:
canonical: |
<p>foo <em>*</em></p>
static: |-
<p data-sourcepos="1:1-1:7" dir="auto">foo <em>*</em></p>
wysiwyg: |-
<p>foo <em>*</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__101:
+06_05_00__inlines__emphasis_and_strong_emphasis__101:
canonical: |
<p>foo _____</p>
static: |-
<p data-sourcepos="1:1-1:9" dir="auto">foo _____</p>
wysiwyg: |-
<p>foo _____</p>
-06_05__inlines__emphasis_and_strong_emphasis__102:
+06_05_00__inlines__emphasis_and_strong_emphasis__102:
canonical: |
<p>foo <strong>_</strong></p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto">foo <strong>_</strong></p>
wysiwyg: |-
<p>foo <strong>_</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__103:
+06_05_00__inlines__emphasis_and_strong_emphasis__103:
canonical: |
<p>foo <strong>*</strong></p>
static: |-
<p data-sourcepos="1:1-1:9" dir="auto">foo <strong>*</strong></p>
wysiwyg: |-
<p>foo <strong>*</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__104:
+06_05_00__inlines__emphasis_and_strong_emphasis__104:
canonical: |
<p>_<em>foo</em></p>
static: |-
<p data-sourcepos="1:1-1:6" dir="auto">_<em>foo</em></p>
wysiwyg: |-
<p>_<em>foo</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__105:
+06_05_00__inlines__emphasis_and_strong_emphasis__105:
canonical: |
<p><em>foo</em>_</p>
static: |-
<p data-sourcepos="1:1-1:6" dir="auto"><em>foo</em>_</p>
wysiwyg: |-
<p><em>foo</em>_</p>
-06_05__inlines__emphasis_and_strong_emphasis__106:
+06_05_00__inlines__emphasis_and_strong_emphasis__106:
canonical: |
<p>_<strong>foo</strong></p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto">_<strong>foo</strong></p>
wysiwyg: |-
<p>_<strong>foo</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__107:
+06_05_00__inlines__emphasis_and_strong_emphasis__107:
canonical: |
<p>___<em>foo</em></p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto">___<em>foo</em></p>
wysiwyg: |-
<p>___<em>foo</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__108:
+06_05_00__inlines__emphasis_and_strong_emphasis__108:
canonical: |
<p><strong>foo</strong>_</p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto"><strong>foo</strong>_</p>
wysiwyg: |-
<p><strong>foo</strong>_</p>
-06_05__inlines__emphasis_and_strong_emphasis__109:
+06_05_00__inlines__emphasis_and_strong_emphasis__109:
canonical: |
<p><em>foo</em>___</p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto"><em>foo</em>___</p>
wysiwyg: |-
<p><em>foo</em>___</p>
-06_05__inlines__emphasis_and_strong_emphasis__110:
+06_05_00__inlines__emphasis_and_strong_emphasis__110:
canonical: |
<p><strong>foo</strong></p>
static: |-
<p data-sourcepos="1:1-1:7" dir="auto"><strong>foo</strong></p>
wysiwyg: |-
<p><strong>foo</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__111:
+06_05_00__inlines__emphasis_and_strong_emphasis__111:
canonical: |
<p><em><em>foo</em></em></p>
static: |-
<p data-sourcepos="1:1-1:7" dir="auto"><em><em>foo</em></em></p>
wysiwyg: |-
<p><em>foo</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__112:
+06_05_00__inlines__emphasis_and_strong_emphasis__112:
canonical: |
<p><strong>foo</strong></p>
static: |-
<p data-sourcepos="1:1-1:7" dir="auto"><strong>foo</strong></p>
wysiwyg: |-
<p><strong>foo</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__113:
+06_05_00__inlines__emphasis_and_strong_emphasis__113:
canonical: |
<p><em><em>foo</em></em></p>
static: |-
<p data-sourcepos="1:1-1:7" dir="auto"><em><em>foo</em></em></p>
wysiwyg: |-
<p><em>foo</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__114:
+06_05_00__inlines__emphasis_and_strong_emphasis__114:
canonical: |
<p><strong><strong>foo</strong></strong></p>
static: |-
<p data-sourcepos="1:1-1:11" dir="auto"><strong><strong>foo</strong></strong></p>
wysiwyg: |-
<p><strong>foo</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__115:
+06_05_00__inlines__emphasis_and_strong_emphasis__115:
canonical: |
<p><strong><strong>foo</strong></strong></p>
static: |-
<p data-sourcepos="1:1-1:11" dir="auto"><strong><strong>foo</strong></strong></p>
wysiwyg: |-
<p><strong>foo</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__116:
+06_05_00__inlines__emphasis_and_strong_emphasis__116:
canonical: |
<p><strong><strong><strong>foo</strong></strong></strong></p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto"><strong><strong><strong>foo</strong></strong></strong></p>
wysiwyg: |-
<p><strong>foo</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__117:
+06_05_00__inlines__emphasis_and_strong_emphasis__117:
canonical: |
<p><em><strong>foo</strong></em></p>
static: |-
<p data-sourcepos="1:1-1:9" dir="auto"><em><strong>foo</strong></em></p>
wysiwyg: |-
<p><strong><em>foo</em></strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__118:
+06_05_00__inlines__emphasis_and_strong_emphasis__118:
canonical: |
<p><em><strong><strong>foo</strong></strong></em></p>
static: |-
<p data-sourcepos="1:1-1:13" dir="auto"><em><strong><strong>foo</strong></strong></em></p>
wysiwyg: |-
<p><strong><em>foo</em></strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__119:
+06_05_00__inlines__emphasis_and_strong_emphasis__119:
canonical: |
<p><em>foo _bar</em> baz_</p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto"><em>foo _bar</em> baz_</p>
wysiwyg: |-
<p><em>foo _bar</em> baz_</p>
-06_05__inlines__emphasis_and_strong_emphasis__120:
+06_05_00__inlines__emphasis_and_strong_emphasis__120:
canonical: |
<p><em>foo <strong>bar *baz bim</strong> bam</em></p>
static: |-
<p data-sourcepos="1:1-1:26" dir="auto"><em>foo <strong>bar *baz bim</strong> bam</em></p>
wysiwyg: |-
<p><em>foo </em><strong><em>bar *baz bim</em></strong><em> bam</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__121:
+06_05_00__inlines__emphasis_and_strong_emphasis__121:
canonical: |
<p>**foo <strong>bar baz</strong></p>
static: |-
<p data-sourcepos="1:1-1:17" dir="auto">**foo <strong>bar baz</strong></p>
wysiwyg: |-
<p>**foo <strong>bar baz</strong></p>
-06_05__inlines__emphasis_and_strong_emphasis__122:
+06_05_00__inlines__emphasis_and_strong_emphasis__122:
canonical: |
<p>*foo <em>bar baz</em></p>
static: |-
<p data-sourcepos="1:1-1:14" dir="auto">*foo <em>bar baz</em></p>
wysiwyg: |-
<p>*foo <em>bar baz</em></p>
-06_05__inlines__emphasis_and_strong_emphasis__123:
+06_05_00__inlines__emphasis_and_strong_emphasis__123:
canonical: |
<p>*<a href="/url">bar*</a></p>
static: |-
<p data-sourcepos="1:1-1:13" dir="auto">*<a href="/url">bar*</a></p>
wysiwyg: |-
<p>*<a target="_blank" rel="noopener noreferrer nofollow" href="/url">bar*</a></p>
-06_05__inlines__emphasis_and_strong_emphasis__124:
+06_05_00__inlines__emphasis_and_strong_emphasis__124:
canonical: |
<p>_foo <a href="/url">bar_</a></p>
static: |-
<p data-sourcepos="1:1-1:17" dir="auto">_foo <a href="/url">bar_</a></p>
wysiwyg: |-
<p>_foo <a target="_blank" rel="noopener noreferrer nofollow" href="/url">bar_</a></p>
-06_05__inlines__emphasis_and_strong_emphasis__125:
+06_05_00__inlines__emphasis_and_strong_emphasis__125:
canonical: |
<p>*<img src="foo" title="*"/></p>
static: |-
<p data-sourcepos="1:1-1:27" dir="auto">*<a class="no-attachment-icon" href="foo" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" title="*" decoding="async" class="lazy" data-src="foo"></a></p>
wysiwyg: |-
<p>*<img src="foo" title="*"></p>
-06_05__inlines__emphasis_and_strong_emphasis__126:
+06_05_00__inlines__emphasis_and_strong_emphasis__126:
canonical: |
<p>**<a href="**"></p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto">**<a href="**"></a></p>
wysiwyg: |-
<p>**</p>
-06_05__inlines__emphasis_and_strong_emphasis__127:
+06_05_00__inlines__emphasis_and_strong_emphasis__127:
canonical: |
<p>__<a href="__"></p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto">__<a href="__"></a></p>
wysiwyg: |-
<p>__</p>
-06_05__inlines__emphasis_and_strong_emphasis__128:
+06_05_00__inlines__emphasis_and_strong_emphasis__128:
canonical: |
<p><em>a <code>*</code></em></p>
static: |-
<p data-sourcepos="1:1-1:7" dir="auto"><em>a <code>*</code></em></p>
wysiwyg: |-
<p><em>a <code>*</code></em></p>
-06_05__inlines__emphasis_and_strong_emphasis__129:
+06_05_00__inlines__emphasis_and_strong_emphasis__129:
canonical: |
<p><em>a <code>_</code></em></p>
static: |-
<p data-sourcepos="1:1-1:7" dir="auto"><em>a <code>_</code></em></p>
wysiwyg: |-
<p><em>a <code>_</code></em></p>
-06_05__inlines__emphasis_and_strong_emphasis__130:
+06_05_00__inlines__emphasis_and_strong_emphasis__130:
canonical: |
<p>**a<a href="http://foo.bar/?q=**">http://foo.bar/?q=**</a></p>
static: |-
<p data-sourcepos="1:1-1:25" dir="auto">**a<a href="http://foo.bar/?q=**" rel="nofollow noreferrer noopener" target="_blank">http://foo.bar/?q=**</a></p>
wysiwyg: |-
<p>**a<a target="_blank" rel="noopener noreferrer nofollow" href="http://foo.bar/?q=**">http://foo.bar/?q=**</a></p>
-06_05__inlines__emphasis_and_strong_emphasis__131:
+06_05_00__inlines__emphasis_and_strong_emphasis__131:
canonical: |
<p>__a<a href="http://foo.bar/?q=__">http://foo.bar/?q=__</a></p>
static: |-
<p data-sourcepos="1:1-1:25" dir="auto">__a<a href="http://foo.bar/?q=__" rel="nofollow noreferrer noopener" target="_blank">http://foo.bar/?q=__</a></p>
wysiwyg: |-
<p>__a<a target="_blank" rel="noopener noreferrer nofollow" href="http://foo.bar/?q=__">http://foo.bar/?q=__</a></p>
-06_06__inlines__strikethrough_extension__001:
+06_06_00__inlines__strikethrough_extension__001:
canonical: |
<p><del>Hi</del> Hello, world!</p>
static: |-
<p data-sourcepos="1:1-1:20" dir="auto"><del>Hi</del> Hello, world!</p>
wysiwyg: |-
<p><s>Hi</s> Hello, world!</p>
-06_06__inlines__strikethrough_extension__002:
+06_06_00__inlines__strikethrough_extension__002:
canonical: |
<p>This ~~has a</p>
<p>new paragraph~~.</p>
@@ -6179,49 +6179,49 @@
wysiwyg: |-
<p>This ~~has a</p>
<p>new paragraph~~.</p>
-06_07__inlines__links__001:
+06_07_00__inlines__links__001:
canonical: |
<p><a href="/uri" title="title">link</a></p>
static: |-
<p data-sourcepos="1:1-1:20" dir="auto"><a href="/uri" title="title">link</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/uri" title="title">link</a></p>
-06_07__inlines__links__002:
+06_07_00__inlines__links__002:
canonical: |
<p><a href="/uri">link</a></p>
static: |-
<p data-sourcepos="1:1-1:12" dir="auto"><a href="/uri">link</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/uri">link</a></p>
-06_07__inlines__links__003:
+06_07_00__inlines__links__003:
canonical: |
<p><a href="">link</a></p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto"><a href="">link</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="">link</a></p>
-06_07__inlines__links__004:
+06_07_00__inlines__links__004:
canonical: |
<p><a href="">link</a></p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto"><a href="">link</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="">link</a></p>
-06_07__inlines__links__005:
+06_07_00__inlines__links__005:
canonical: |
<p>[link](/my uri)</p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto"><a href="/my%20uri">link</a></p>
wysiwyg: |-
<p>[link](/my uri)</p>
-06_07__inlines__links__006:
+06_07_00__inlines__links__006:
canonical: |
<p><a href="/my%20uri">link</a></p>
static: |-
<p data-sourcepos="1:1-1:17" dir="auto"><a href="/my%20uri">link</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/my%20uri">link</a></p>
-06_07__inlines__links__007:
+06_07_00__inlines__links__007:
canonical: |
<p>[link](foo
bar)</p>
@@ -6231,7 +6231,7 @@
wysiwyg: |-
<p>[link](foo
bar)</p>
-06_07__inlines__links__008:
+06_07_00__inlines__links__008:
canonical: |
<p>[link](<foo
bar>)</p>
@@ -6239,21 +6239,21 @@
<p data-sourcepos="1:1-2:5" dir="auto">[link]()</p>
wysiwyg: |-
<p>[link](</p>
-06_07__inlines__links__009:
+06_07_00__inlines__links__009:
canonical: |
<p><a href="b)c">a</a></p>
static: |-
<p data-sourcepos="1:1-1:10" dir="auto"><a href="b)c">a</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="b)c">a</a></p>
-06_07__inlines__links__010:
+06_07_00__inlines__links__010:
canonical: |
<p>[link](&lt;foo&gt;)</p>
static: |-
<p data-sourcepos="1:1-1:14" dir="auto"><a href="%3Cfoo%3E">link</a></p>
wysiwyg: |-
<p>[link](&lt;foo&gt;)</p>
-06_07__inlines__links__011:
+06_07_00__inlines__links__011:
canonical: |
<p>[a](&lt;b)c
[a](&lt;b)c&gt;
@@ -6266,42 +6266,42 @@
<p>[a](&lt;b)c
[a](&lt;b)c&gt;
[a](<strong>c)</strong></p>
-06_07__inlines__links__012:
+06_07_00__inlines__links__012:
canonical: |
<p><a href="(foo)">link</a></p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto"><a href="(foo)">link</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="(foo)">link</a></p>
-06_07__inlines__links__013:
+06_07_00__inlines__links__013:
canonical: |
<p><a href="foo(and(bar))">link</a></p>
static: |-
<p data-sourcepos="1:1-1:21" dir="auto"><a href="foo(and(bar))">link</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="foo(and(bar))">link</a></p>
-06_07__inlines__links__014:
+06_07_00__inlines__links__014:
canonical: |
<p><a href="foo(and(bar)">link</a></p>
static: |-
<p data-sourcepos="1:1-1:23" dir="auto"><a href="foo(and(bar)">link</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="foo(and(bar)">link</a></p>
-06_07__inlines__links__015:
+06_07_00__inlines__links__015:
canonical: |
<p><a href="foo(and(bar)">link</a></p>
static: |-
<p data-sourcepos="1:1-1:22" dir="auto"><a href="foo(and(bar)">link</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="foo(and(bar)">link</a></p>
-06_07__inlines__links__016:
+06_07_00__inlines__links__016:
canonical: |
<p><a href="foo):">link</a></p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto"><a>link</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="foo):">link</a></p>
-06_07__inlines__links__017:
+06_07_00__inlines__links__017:
canonical: |
<p><a href="#fragment">link</a></p>
<p><a href="http://example.com#fragment">link</a></p>
@@ -6314,28 +6314,28 @@
<p><a target="_blank" rel="noopener noreferrer nofollow" href="#fragment">link</a></p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="http://example.com#fragment">link</a></p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="http://example.com?foo=3#frag">link</a></p>
-06_07__inlines__links__018:
+06_07_00__inlines__links__018:
canonical: |
<p><a href="foo%5Cbar">link</a></p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto"><a href="foo%5Cbar">link</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="foo%5Cbar">link</a></p>
-06_07__inlines__links__019:
+06_07_00__inlines__links__019:
canonical: |
<p><a href="foo%20b%C3%A4">link</a></p>
static: |-
<p data-sourcepos="1:1-1:21" dir="auto"><a href="foo%20b%C3%A4">link</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="foo%20b%C3%A4">link</a></p>
-06_07__inlines__links__020:
+06_07_00__inlines__links__020:
canonical: |
<p><a href="%22title%22">link</a></p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto"><a href="%22title%22">link</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="%22title%22">link</a></p>
-06_07__inlines__links__021:
+06_07_00__inlines__links__021:
canonical: |
<p><a href="/url" title="title">link</a>
<a href="/url" title="title">link</a>
@@ -6346,154 +6346,154 @@
<a href="/url" title="title">link</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url" title="title">linklinklink</a></p>
-06_07__inlines__links__022:
+06_07_00__inlines__links__022:
canonical: |
<p><a href="/url" title="title &quot;&quot;">link</a></p>
static: |-
<p data-sourcepos="1:1-1:29" dir="auto"><a href="/url" title='title ""'>link</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url" title="title &quot;&quot;">link</a></p>
-06_07__inlines__links__023:
+06_07_00__inlines__links__023:
canonical: |
<p><a href="/url%C2%A0%22title%22">link</a></p>
static: |-
<p data-sourcepos="1:1-1:21" dir="auto"><a href="/url%C2%A0%22title%22">link</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url%C2%A0%22title%22">link</a></p>
-06_07__inlines__links__024:
+06_07_00__inlines__links__024:
canonical: |
<p>[link](/url &quot;title &quot;and&quot; title&quot;)</p>
static: |-
<p data-sourcepos="1:1-1:32" dir="auto">[link](/url "title "and" title")</p>
wysiwyg: |-
<p>[link](/url "title "and" title")</p>
-06_07__inlines__links__025:
+06_07_00__inlines__links__025:
canonical: |
<p><a href="/url" title="title &quot;and&quot; title">link</a></p>
static: |-
<p data-sourcepos="1:1-1:32" dir="auto"><a href="/url" title='title "and" title'>link</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url" title="title &quot;and&quot; title">link</a></p>
-06_07__inlines__links__026:
+06_07_00__inlines__links__026:
canonical: |
<p><a href="/uri" title="title">link</a></p>
static: |-
<p data-sourcepos="1:1-2:12" dir="auto"><a href="/uri" title="title">link</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/uri" title="title">link</a></p>
-06_07__inlines__links__027:
+06_07_00__inlines__links__027:
canonical: |
<p>[link] (/uri)</p>
static: |-
<p data-sourcepos="1:1-1:13" dir="auto">[link] (/uri)</p>
wysiwyg: |-
<p>[link] (/uri)</p>
-06_07__inlines__links__028:
+06_07_00__inlines__links__028:
canonical: |
<p><a href="/uri">link [foo [bar]]</a></p>
static: |-
<p data-sourcepos="1:1-1:24" dir="auto"><a href="/uri">link [foo [bar]]</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/uri">link [foo [bar]]</a></p>
-06_07__inlines__links__029:
+06_07_00__inlines__links__029:
canonical: |
<p>[link] bar](/uri)</p>
static: |-
<p data-sourcepos="1:1-1:17" dir="auto">[link] bar](/uri)</p>
wysiwyg: |-
<p>[link] bar](/uri)</p>
-06_07__inlines__links__030:
+06_07_00__inlines__links__030:
canonical: |
<p>[link <a href="/uri">bar</a></p>
static: |-
<p data-sourcepos="1:1-1:17" dir="auto">[link <a href="/uri">bar</a></p>
wysiwyg: |-
<p>[link <a target="_blank" rel="noopener noreferrer nofollow" href="/uri">bar</a></p>
-06_07__inlines__links__031:
+06_07_00__inlines__links__031:
canonical: |
<p><a href="/uri">link [bar</a></p>
static: |-
<p data-sourcepos="1:1-1:18" dir="auto"><a href="/uri">link [bar</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/uri">link [bar</a></p>
-06_07__inlines__links__032:
+06_07_00__inlines__links__032:
canonical: |
<p><a href="/uri">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>
static: |-
<p data-sourcepos="1:1-1:30" dir="auto"><a href="/uri">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/uri">link <em>foo </em><strong><em>bar<code>#</code></em></strong></a></p>
-06_07__inlines__links__033:
+06_07_00__inlines__links__033:
canonical: |
<p><a href="/uri"><img src="moon.jpg" alt="moon" /></a></p>
static: |-
<p data-sourcepos="1:1-1:25" dir="auto"><a href="/uri"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="moon" decoding="async" class="lazy" data-src="moon.jpg"></a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/uri"><img src="moon.jpg" alt="moon"></a></p>
-06_07__inlines__links__034:
+06_07_00__inlines__links__034:
canonical: |
<p>[foo <a href="/uri">bar</a>](/uri)</p>
static: |-
<p data-sourcepos="1:1-1:23" dir="auto">[foo <a href="/uri">bar</a>](/uri)</p>
wysiwyg: |-
<p>[foo <a target="_blank" rel="noopener noreferrer nofollow" href="/uri">bar</a>](/uri)</p>
-06_07__inlines__links__035:
+06_07_00__inlines__links__035:
canonical: |
<p>[foo <em>[bar <a href="/uri">baz</a>](/uri)</em>](/uri)</p>
static: |-
<p data-sourcepos="1:1-1:37" dir="auto">[foo <em>[bar <a href="/uri">baz</a>](/uri)</em>](/uri)</p>
wysiwyg: |-
<p>[foo <em>[bar </em><a target="_blank" rel="noopener noreferrer nofollow" href="/uri"><em>baz</em></a><em>](/uri)</em>](/uri)</p>
-06_07__inlines__links__036:
+06_07_00__inlines__links__036:
canonical: |
<p><img src="uri3" alt="[foo](uri2)" /></p>
static: |-
<p data-sourcepos="1:1-1:28" dir="auto"><a class="no-attachment-icon" href="uri3" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="[foo](uri2)" decoding="async" class="lazy" data-src="uri3"></a></p>
wysiwyg: |-
<p><img src="uri3" alt="[foo](uri2)"></p>
-06_07__inlines__links__037:
+06_07_00__inlines__links__037:
canonical: |
<p>*<a href="/uri">foo*</a></p>
static: |-
<p data-sourcepos="1:1-1:13" dir="auto">*<a href="/uri">foo*</a></p>
wysiwyg: |-
<p>*<a target="_blank" rel="noopener noreferrer nofollow" href="/uri">foo*</a></p>
-06_07__inlines__links__038:
+06_07_00__inlines__links__038:
canonical: |
<p><a href="baz*">foo *bar</a></p>
static: |-
<p data-sourcepos="1:1-1:16" dir="auto"><a href="baz*">foo *bar</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="baz*">foo *bar</a></p>
-06_07__inlines__links__039:
+06_07_00__inlines__links__039:
canonical: |
<p><em>foo [bar</em> baz]</p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto"><em>foo [bar</em> baz]</p>
wysiwyg: |-
<p><em>foo [bar</em> baz]</p>
-06_07__inlines__links__040:
+06_07_00__inlines__links__040:
canonical: |
<p>[foo <bar attr="](baz)"></p>
static: |-
<p data-sourcepos="1:1-1:24" dir="auto">[foo </p>
wysiwyg: |-
<p>[foo </p>
-06_07__inlines__links__041:
+06_07_00__inlines__links__041:
canonical: |
<p>[foo<code>](/uri)</code></p>
static: |-
<p data-sourcepos="1:1-1:13" dir="auto">[foo<code>](/uri)</code></p>
wysiwyg: |-
<p>[foo<code>](/uri)</code></p>
-06_07__inlines__links__042:
+06_07_00__inlines__links__042:
canonical: |
<p>[foo<a href="http://example.com/?search=%5D(uri)">http://example.com/?search=](uri)</a></p>
static: |-
<p data-sourcepos="1:1-1:39" dir="auto">[foo<a href="http://example.com/?search=%5D(uri)" rel="nofollow noreferrer noopener" target="_blank">http://example.com/?search=](uri)</a></p>
wysiwyg: |-
<p>[foo<a target="_blank" rel="noopener noreferrer nofollow" href="http://example.com/?search=%5D(uri)">http://example.com/?search=](uri)</a></p>
-06_07__inlines__links__043:
+06_07_00__inlines__links__043:
canonical: |
<p><a href="/url" title="title">foo</a></p>
static: |-
@@ -6501,7 +6501,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url" title="title">foo</a></p>
<pre>[bar]: /url "title"</pre>
-06_07__inlines__links__044:
+06_07_00__inlines__links__044:
canonical: |
<p><a href="/uri">link [foo [bar]]</a></p>
static: |-
@@ -6509,7 +6509,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/uri">link [foo [bar]]</a></p>
<pre>[ref]: /uri</pre>
-06_07__inlines__links__045:
+06_07_00__inlines__links__045:
canonical: |
<p><a href="/uri">link [bar</a></p>
static: |-
@@ -6517,7 +6517,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/uri">link [bar</a></p>
<pre>[ref]: /uri</pre>
-06_07__inlines__links__046:
+06_07_00__inlines__links__046:
canonical: |
<p><a href="/uri">link <em>foo <strong>bar</strong> <code>#</code></em></a></p>
static: |-
@@ -6525,7 +6525,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/uri">link <em>foo </em><strong><em>bar<code>#</code></em></strong></a></p>
<pre>[ref]: /uri</pre>
-06_07__inlines__links__047:
+06_07_00__inlines__links__047:
canonical: |
<p><a href="/uri"><img src="moon.jpg" alt="moon" /></a></p>
static: |-
@@ -6533,7 +6533,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/uri"><img src="moon.jpg" alt="moon"></a></p>
<pre>[ref]: /uri</pre>
-06_07__inlines__links__048:
+06_07_00__inlines__links__048:
canonical: |
<p>[foo <a href="/uri">bar</a>]<a href="/uri">ref</a></p>
static: |-
@@ -6541,7 +6541,7 @@
wysiwyg: |-
<p>[foo <a target="_blank" rel="noopener noreferrer nofollow" href="/uri">bar</a>]<a target="_blank" rel="noopener noreferrer nofollow" href="/uri">ref</a></p>
<pre>[ref]: /uri</pre>
-06_07__inlines__links__049:
+06_07_00__inlines__links__049:
canonical: |
<p>[foo <em>bar <a href="/uri">baz</a></em>]<a href="/uri">ref</a></p>
static: |-
@@ -6549,7 +6549,7 @@
wysiwyg: |-
<p>[foo <em>bar </em><a target="_blank" rel="noopener noreferrer nofollow" href="/uri"><em>baz</em></a>]<a target="_blank" rel="noopener noreferrer nofollow" href="/uri">ref</a></p>
<pre>[ref]: /uri</pre>
-06_07__inlines__links__050:
+06_07_00__inlines__links__050:
canonical: |
<p>*<a href="/uri">foo*</a></p>
static: |-
@@ -6557,7 +6557,7 @@
wysiwyg: |-
<p>*<a target="_blank" rel="noopener noreferrer nofollow" href="/uri">foo*</a></p>
<pre>[ref]: /uri</pre>
-06_07__inlines__links__051:
+06_07_00__inlines__links__051:
canonical: |
<p><a href="/uri">foo *bar</a></p>
static: |-
@@ -6565,7 +6565,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/uri">foo *bar</a></p>
<pre>[ref]: /uri</pre>
-06_07__inlines__links__052:
+06_07_00__inlines__links__052:
canonical: |
<p>[foo <bar attr="][ref]"></p>
static: |-
@@ -6573,7 +6573,7 @@
wysiwyg: |-
<p>[foo </p>
<pre>[ref]: /uri</pre>
-06_07__inlines__links__053:
+06_07_00__inlines__links__053:
canonical: |
<p>[foo<code>][ref]</code></p>
static: |-
@@ -6581,7 +6581,7 @@
wysiwyg: |-
<p>[foo<code>][ref]</code></p>
<pre>[ref]: /uri</pre>
-06_07__inlines__links__054:
+06_07_00__inlines__links__054:
canonical: |
<p>[foo<a href="http://example.com/?search=%5D%5Bref%5D">http://example.com/?search=][ref]</a></p>
static: |-
@@ -6589,7 +6589,7 @@
wysiwyg: |-
<p>[foo<a target="_blank" rel="noopener noreferrer nofollow" href="http://example.com/?search=%5D%5Bref%5D">http://example.com/?search=][ref]</a></p>
<pre>[ref]: /uri</pre>
-06_07__inlines__links__055:
+06_07_00__inlines__links__055:
canonical: |
<p><a href="/url" title="title">foo</a></p>
static: |-
@@ -6597,7 +6597,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url" title="title">foo</a></p>
<pre>[bar]: /url "title"</pre>
-06_07__inlines__links__056:
+06_07_00__inlines__links__056:
canonical: |
<p><a href="/url">Толпой</a> is a Russian word.</p>
static: |-
@@ -6605,7 +6605,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url">Толпой</a> is a Russian word.</p>
<pre>[толпой]: /url</pre>
-06_07__inlines__links__057:
+06_07_00__inlines__links__057:
canonical: |
<p><a href="/url">Baz</a></p>
static: |-
@@ -6613,7 +6613,7 @@
wysiwyg: |-
<pre>[foo bar]: /url</pre>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url">Baz</a></p>
-06_07__inlines__links__058:
+06_07_00__inlines__links__058:
canonical: |
<p>[foo] <a href="/url" title="title">bar</a></p>
static: |-
@@ -6621,7 +6621,7 @@
wysiwyg: |-
<p>[foo] <a target="_blank" rel="noopener noreferrer nofollow" href="/url" title="title">bar</a></p>
<pre>[bar]: /url "title"</pre>
-06_07__inlines__links__059:
+06_07_00__inlines__links__059:
canonical: |
<p>[foo]
<a href="/url" title="title">bar</a></p>
@@ -6632,7 +6632,7 @@
<p>[foo]
<a target="_blank" rel="noopener noreferrer nofollow" href="/url" title="title">bar</a></p>
<pre>[bar]: /url "title"</pre>
-06_07__inlines__links__060:
+06_07_00__inlines__links__060:
canonical: |
<p><a href="/url1">bar</a></p>
static: |-
@@ -6641,7 +6641,7 @@
<pre>[foo]: /url1</pre>
<pre>[foo]: /url2</pre>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url1">bar</a></p>
-06_07__inlines__links__061:
+06_07_00__inlines__links__061:
canonical: |
<p>[bar][foo!]</p>
static: |-
@@ -6649,7 +6649,7 @@
wysiwyg: |-
<p>[bar][foo!]</p>
<pre>[foo!]: /url</pre>
-06_07__inlines__links__062:
+06_07_00__inlines__links__062:
canonical: |
<p>[foo][ref[]</p>
<p>[ref[]: /uri</p>
@@ -6659,7 +6659,7 @@
wysiwyg: |-
<p>[foo][ref[]</p>
<p>[ref[]: /uri</p>
-06_07__inlines__links__063:
+06_07_00__inlines__links__063:
canonical: |
<p>[foo][ref[bar]]</p>
<p>[ref[bar]]: /uri</p>
@@ -6669,7 +6669,7 @@
wysiwyg: |-
<p>[foo][ref[bar]]</p>
<p>[ref[bar]]: /uri</p>
-06_07__inlines__links__064:
+06_07_00__inlines__links__064:
canonical: |
<p>[[[foo]]]</p>
<p>[[[foo]]]: /url</p>
@@ -6679,7 +6679,7 @@
wysiwyg: |-
<p>[[[foo]]]</p>
<p>[[[foo]]]: /url</p>
-06_07__inlines__links__065:
+06_07_00__inlines__links__065:
canonical: |
<p><a href="/uri">foo</a></p>
static: |-
@@ -6687,7 +6687,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/uri">foo</a></p>
<pre>[ref\[]: /uri</pre>
-06_07__inlines__links__066:
+06_07_00__inlines__links__066:
canonical: |
<p><a href="/uri">bar\</a></p>
static: |-
@@ -6695,7 +6695,7 @@
wysiwyg: |-
<pre>[bar\\]: /uri</pre>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/uri">bar\</a></p>
-06_07__inlines__links__067:
+06_07_00__inlines__links__067:
canonical: |
<p>[]</p>
<p>[]: /uri</p>
@@ -6705,7 +6705,7 @@
wysiwyg: |-
<p>[]</p>
<p>[]: /uri</p>
-06_07__inlines__links__068:
+06_07_00__inlines__links__068:
canonical: |
<p>[
]</p>
@@ -6721,7 +6721,7 @@
]</p>
<p>[
]: /uri</p>
-06_07__inlines__links__069:
+06_07_00__inlines__links__069:
canonical: |
<p><a href="/url" title="title">foo</a></p>
static: |-
@@ -6729,7 +6729,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url" title="title">foo</a></p>
<pre>[foo]: /url "title"</pre>
-06_07__inlines__links__070:
+06_07_00__inlines__links__070:
canonical: |
<p><a href="/url" title="title"><em>foo</em> bar</a></p>
static: |-
@@ -6737,7 +6737,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url" title="title"><em>foo</em> bar</a></p>
<pre>[*foo* bar]: /url "title"</pre>
-06_07__inlines__links__071:
+06_07_00__inlines__links__071:
canonical: |
<p><a href="/url" title="title">Foo</a></p>
static: |-
@@ -6745,7 +6745,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url" title="title">Foo</a></p>
<pre>[foo]: /url "title"</pre>
-06_07__inlines__links__072:
+06_07_00__inlines__links__072:
canonical: |
<p><a href="/url" title="title">foo</a>
[]</p>
@@ -6756,7 +6756,7 @@
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url" title="title">foo</a>
[]</p>
<pre>[foo]: /url "title"</pre>
-06_07__inlines__links__073:
+06_07_00__inlines__links__073:
canonical: |
<p><a href="/url" title="title">foo</a></p>
static: |-
@@ -6764,7 +6764,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url" title="title">foo</a></p>
<pre>[foo]: /url "title"</pre>
-06_07__inlines__links__074:
+06_07_00__inlines__links__074:
canonical: |
<p><a href="/url" title="title"><em>foo</em> bar</a></p>
static: |-
@@ -6772,7 +6772,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url" title="title"><em>foo</em> bar</a></p>
<pre>[*foo* bar]: /url "title"</pre>
-06_07__inlines__links__075:
+06_07_00__inlines__links__075:
canonical: |
<p>[<a href="/url" title="title"><em>foo</em> bar</a>]</p>
static: |-
@@ -6780,7 +6780,7 @@
wysiwyg: |-
<p>[<a target="_blank" rel="noopener noreferrer nofollow" href="/url" title="title"><em>foo</em> bar</a>]</p>
<pre>[*foo* bar]: /url "title"</pre>
-06_07__inlines__links__076:
+06_07_00__inlines__links__076:
canonical: |
<p>[[bar <a href="/url">foo</a></p>
static: |-
@@ -6788,7 +6788,7 @@
wysiwyg: |-
<p>[[bar <a target="_blank" rel="noopener noreferrer nofollow" href="/url">foo</a></p>
<pre>[foo]: /url</pre>
-06_07__inlines__links__077:
+06_07_00__inlines__links__077:
canonical: |
<p><a href="/url" title="title">Foo</a></p>
static: |-
@@ -6796,7 +6796,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url" title="title">Foo</a></p>
<pre>[foo]: /url "title"</pre>
-06_07__inlines__links__078:
+06_07_00__inlines__links__078:
canonical: |
<p><a href="/url">foo</a> bar</p>
static: |-
@@ -6804,7 +6804,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url">foo</a> bar</p>
<pre>[foo]: /url</pre>
-06_07__inlines__links__079:
+06_07_00__inlines__links__079:
canonical: |
<p>[foo]</p>
static: |-
@@ -6812,7 +6812,7 @@
wysiwyg: |-
<p>[foo]</p>
<pre>[foo]: /url "title"</pre>
-06_07__inlines__links__080:
+06_07_00__inlines__links__080:
canonical: |
<p>*<a href="/url">foo*</a></p>
static: |-
@@ -6820,7 +6820,7 @@
wysiwyg: |-
<pre>[foo*]: /url</pre>
<p>*<a target="_blank" rel="noopener noreferrer nofollow" href="/url">foo*</a></p>
-06_07__inlines__links__081:
+06_07_00__inlines__links__081:
canonical: |
<p><a href="/url2">foo</a></p>
static: |-
@@ -6829,7 +6829,7 @@
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url2">foo</a></p>
<pre>[foo]: /url1</pre>
<pre>[bar]: /url2</pre>
-06_07__inlines__links__082:
+06_07_00__inlines__links__082:
canonical: |
<p><a href="/url1">foo</a></p>
static: |-
@@ -6837,7 +6837,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url1">foo</a></p>
<pre>[foo]: /url1</pre>
-06_07__inlines__links__083:
+06_07_00__inlines__links__083:
canonical: |
<p><a href="">foo</a></p>
static: |-
@@ -6845,7 +6845,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="">foo</a></p>
<pre>[foo]: /url1</pre>
-06_07__inlines__links__084:
+06_07_00__inlines__links__084:
canonical: |
<p><a href="/url1">foo</a>(not a link)</p>
static: |-
@@ -6853,7 +6853,7 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url1">foo</a>(not a link)</p>
<pre>[foo]: /url1</pre>
-06_07__inlines__links__085:
+06_07_00__inlines__links__085:
canonical: |
<p>[foo]<a href="/url">bar</a></p>
static: |-
@@ -6861,7 +6861,7 @@
wysiwyg: |-
<p>[foo]<a target="_blank" rel="noopener noreferrer nofollow" href="/url">bar</a></p>
<pre>[baz]: /url</pre>
-06_07__inlines__links__086:
+06_07_00__inlines__links__086:
canonical: |
<p><a href="/url2">foo</a><a href="/url1">baz</a></p>
static: |-
@@ -6870,7 +6870,7 @@
<p><a target="_blank" rel="noopener noreferrer nofollow" href="/url2">foo</a><a target="_blank" rel="noopener noreferrer nofollow" href="/url1">baz</a></p>
<pre>[baz]: /url1</pre>
<pre>[bar]: /url2</pre>
-06_07__inlines__links__087:
+06_07_00__inlines__links__087:
canonical: |
<p>[foo]<a href="/url1">bar</a></p>
static: |-
@@ -6879,14 +6879,14 @@
<p>[foo]<a target="_blank" rel="noopener noreferrer nofollow" href="/url1">bar</a></p>
<pre>[baz]: /url1</pre>
<pre>[foo]: /url2</pre>
-06_08__inlines__images__001:
+06_08_00__inlines__images__001:
canonical: |
<p><img src="/url" alt="foo" title="title" /></p>
static: |-
<p data-sourcepos="1:1-1:20" dir="auto"><a class="no-attachment-icon" href="/url" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="foo" title="title" decoding="async" class="lazy" data-src="/url"></a></p>
wysiwyg: |-
<p><img src="/url" alt="foo" title="title"></p>
-06_08__inlines__images__002:
+06_08_00__inlines__images__002:
canonical: |
<p><img src="train.jpg" alt="foo bar" title="train &amp; tracks" /></p>
static: |-
@@ -6894,21 +6894,21 @@
wysiwyg: |-
<p><img src="train.jpg" alt="foo bar" title="train &amp; tracks"></p>
<pre>[foo *bar*]: train.jpg "train &amp; tracks"</pre>
-06_08__inlines__images__003:
+06_08_00__inlines__images__003:
canonical: |
<p><img src="/url2" alt="foo bar" /></p>
static: |-
<p data-sourcepos="1:1-1:26" dir="auto"><a class="no-attachment-icon" href="/url2" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="foo bar" decoding="async" class="lazy" data-src="/url2"></a></p>
wysiwyg: |-
<p><img src="/url2" alt="foo bar"></p>
-06_08__inlines__images__004:
+06_08_00__inlines__images__004:
canonical: |
<p><img src="/url2" alt="foo bar" /></p>
static: |-
<p data-sourcepos="1:1-1:25" dir="auto"><a class="no-attachment-icon" href="/url2" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="foo bar" decoding="async" class="lazy" data-src="/url2"></a></p>
wysiwyg: |-
<p><img src="/url2" alt="foo bar"></p>
-06_08__inlines__images__005:
+06_08_00__inlines__images__005:
canonical: |
<p><img src="train.jpg" alt="foo bar" title="train &amp; tracks" /></p>
static: |-
@@ -6916,7 +6916,7 @@
wysiwyg: |-
<p><img src="train.jpg" alt="foo bar" title="train &amp; tracks"></p>
<pre>[foo *bar*]: train.jpg "train &amp; tracks"</pre>
-06_08__inlines__images__006:
+06_08_00__inlines__images__006:
canonical: |
<p><img src="train.jpg" alt="foo bar" title="train &amp; tracks" /></p>
static: |-
@@ -6924,35 +6924,35 @@
wysiwyg: |-
<p><img src="train.jpg" alt="foo bar" title="train &amp; tracks"></p>
<pre>[foobar]: train.jpg "train &amp; tracks"</pre>
-06_08__inlines__images__007:
+06_08_00__inlines__images__007:
canonical: |
<p><img src="train.jpg" alt="foo" /></p>
static: |-
<p data-sourcepos="1:1-1:17" dir="auto"><a class="no-attachment-icon" href="train.jpg" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="foo" decoding="async" class="lazy" data-src="train.jpg"></a></p>
wysiwyg: |-
<p><img src="train.jpg" alt="foo"></p>
-06_08__inlines__images__008:
+06_08_00__inlines__images__008:
canonical: |
<p>My <img src="/path/to/train.jpg" alt="foo bar" title="title" /></p>
static: |-
<p data-sourcepos="1:1-1:45" dir="auto">My <a class="no-attachment-icon" href="/path/to/train.jpg" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="foo bar" title="title" decoding="async" class="lazy" data-src="/path/to/train.jpg"></a></p>
wysiwyg: |-
<p>My <img src="/path/to/train.jpg" alt="foo bar" title="title"></p>
-06_08__inlines__images__009:
+06_08_00__inlines__images__009:
canonical: |
<p><img src="url" alt="foo" /></p>
static: |-
<p data-sourcepos="1:1-1:13" dir="auto"><a class="no-attachment-icon" href="url" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="foo" decoding="async" class="lazy" data-src="url"></a></p>
wysiwyg: |-
<p><img src="url" alt="foo"></p>
-06_08__inlines__images__010:
+06_08_00__inlines__images__010:
canonical: |
<p><img src="/url" alt="" /></p>
static: |-
<p data-sourcepos="1:1-1:9" dir="auto"><a class="no-attachment-icon" href="/url" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="" decoding="async" class="lazy" data-src="/url"></a></p>
wysiwyg: |-
<p><img src="/url" alt=""></p>
-06_08__inlines__images__011:
+06_08_00__inlines__images__011:
canonical: |
<p><img src="/url" alt="foo" /></p>
static: |-
@@ -6960,7 +6960,7 @@
wysiwyg: |-
<p><img src="/url" alt="foo"></p>
<pre>[bar]: /url</pre>
-06_08__inlines__images__012:
+06_08_00__inlines__images__012:
canonical: |
<p><img src="/url" alt="foo" /></p>
static: |-
@@ -6968,7 +6968,7 @@
wysiwyg: |-
<p><img src="/url" alt="foo"></p>
<pre>[bar]: /url</pre>
-06_08__inlines__images__013:
+06_08_00__inlines__images__013:
canonical: |
<p><img src="/url" alt="foo" title="title" /></p>
static: |-
@@ -6976,7 +6976,7 @@
wysiwyg: |-
<p><img src="/url" alt="foo" title="title"></p>
<pre>[foo]: /url "title"</pre>
-06_08__inlines__images__014:
+06_08_00__inlines__images__014:
canonical: |
<p><img src="/url" alt="foo bar" title="title" /></p>
static: |-
@@ -6984,7 +6984,7 @@
wysiwyg: |-
<p><img src="/url" alt="foo bar" title="title"></p>
<pre>[*foo* bar]: /url "title"</pre>
-06_08__inlines__images__015:
+06_08_00__inlines__images__015:
canonical: |
<p><img src="/url" alt="Foo" title="title" /></p>
static: |-
@@ -6992,7 +6992,7 @@
wysiwyg: |-
<p><img src="/url" alt="Foo" title="title"></p>
<pre>[foo]: /url "title"</pre>
-06_08__inlines__images__016:
+06_08_00__inlines__images__016:
canonical: |
<p><img src="/url" alt="foo" title="title" />
[]</p>
@@ -7003,7 +7003,7 @@
<p><img src="/url" alt="foo" title="title">
[]</p>
<pre>[foo]: /url "title"</pre>
-06_08__inlines__images__017:
+06_08_00__inlines__images__017:
canonical: |
<p><img src="/url" alt="foo" title="title" /></p>
static: |-
@@ -7011,7 +7011,7 @@
wysiwyg: |-
<p><img src="/url" alt="foo" title="title"></p>
<pre>[foo]: /url "title"</pre>
-06_08__inlines__images__018:
+06_08_00__inlines__images__018:
canonical: |
<p><img src="/url" alt="foo bar" title="title" /></p>
static: |-
@@ -7019,7 +7019,7 @@
wysiwyg: |-
<p><img src="/url" alt="foo bar" title="title"></p>
<pre>[*foo* bar]: /url "title"</pre>
-06_08__inlines__images__019:
+06_08_00__inlines__images__019:
canonical: |
<p>![[foo]]</p>
<p>[[foo]]: /url &quot;title&quot;</p>
@@ -7029,7 +7029,7 @@
wysiwyg: |-
<p>![[foo]]</p>
<p>[[foo]]: /url "title"</p>
-06_08__inlines__images__020:
+06_08_00__inlines__images__020:
canonical: |
<p><img src="/url" alt="Foo" title="title" /></p>
static: |-
@@ -7037,7 +7037,7 @@
wysiwyg: |-
<p><img src="/url" alt="Foo" title="title"></p>
<pre>[foo]: /url "title"</pre>
-06_08__inlines__images__021:
+06_08_00__inlines__images__021:
canonical: |
<p>![foo]</p>
static: |-
@@ -7045,7 +7045,7 @@
wysiwyg: |-
<p>![foo]</p>
<pre>[foo]: /url "title"</pre>
-06_08__inlines__images__022:
+06_08_00__inlines__images__022:
canonical: |
<p>!<a href="/url" title="title">foo</a></p>
static: |-
@@ -7053,154 +7053,154 @@
wysiwyg: |-
<p>!<a target="_blank" rel="noopener noreferrer nofollow" href="/url" title="title">foo</a></p>
<pre>[foo]: /url "title"</pre>
-06_09__inlines__autolinks__001:
+06_09_00__inlines__autolinks__001:
canonical: |
<p><a href="http://foo.bar.baz">http://foo.bar.baz</a></p>
static: |-
<p data-sourcepos="1:1-1:20" dir="auto"><a href="http://foo.bar.baz" rel="nofollow noreferrer noopener" target="_blank">http://foo.bar.baz</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="http://foo.bar.baz">http://foo.bar.baz</a></p>
-06_09__inlines__autolinks__002:
+06_09_00__inlines__autolinks__002:
canonical: |
<p><a href="http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean">http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean</a></p>
static: |-
<p data-sourcepos="1:1-1:47" dir="auto"><a href="http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean" rel="nofollow noreferrer noopener" target="_blank">http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean">http://foo.bar.baz/test?q=hello&amp;id=22&amp;boolean</a></p>
-06_09__inlines__autolinks__003:
+06_09_00__inlines__autolinks__003:
canonical: |
<p><a href="irc://foo.bar:2233/baz">irc://foo.bar:2233/baz</a></p>
static: |-
<p data-sourcepos="1:1-1:24" dir="auto"><a href="irc://foo.bar:2233/baz">irc://foo.bar:2233/baz</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow">irc://foo.bar:2233/baz</a></p>
-06_09__inlines__autolinks__004:
+06_09_00__inlines__autolinks__004:
canonical: |
<p><a href="MAILTO:FOO@BAR.BAZ">MAILTO:FOO@BAR.BAZ</a></p>
static: |-
<p data-sourcepos="1:1-1:20" dir="auto"><a href="mailto:FOO@BAR.BAZ">MAILTO:FOO@BAR.BAZ</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="MAILTO:FOO@BAR.BAZ">MAILTO:FOO@BAR.BAZ</a></p>
-06_09__inlines__autolinks__005:
+06_09_00__inlines__autolinks__005:
canonical: |
<p><a href="a+b+c:d">a+b+c:d</a></p>
static: |-
<p data-sourcepos="1:1-1:9" dir="auto"><a href="a+b+c:d">a+b+c:d</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow">a+b+c:d</a></p>
-06_09__inlines__autolinks__006:
+06_09_00__inlines__autolinks__006:
canonical: |
<p><a href="made-up-scheme://foo,bar">made-up-scheme://foo,bar</a></p>
static: |-
<p data-sourcepos="1:1-1:26" dir="auto"><a href="made-up-scheme://foo,bar">made-up-scheme://foo,bar</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow">made-up-scheme://foo,bar</a></p>
-06_09__inlines__autolinks__007:
+06_09_00__inlines__autolinks__007:
canonical: |
<p><a href="http://../">http://../</a></p>
static: |-
<p data-sourcepos="1:1-1:12" dir="auto"><a href="http://../" rel="nofollow noreferrer noopener" target="_blank">http://../</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="http://../">http://../</a></p>
-06_09__inlines__autolinks__008:
+06_09_00__inlines__autolinks__008:
canonical: |
<p><a href="localhost:5001/foo">localhost:5001/foo</a></p>
static: |-
<p data-sourcepos="1:1-1:20" dir="auto"><a href="localhost:5001/foo">localhost:5001/foo</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow">localhost:5001/foo</a></p>
-06_09__inlines__autolinks__009:
+06_09_00__inlines__autolinks__009:
canonical: |
<p>&lt;http://foo.bar/baz bim&gt;</p>
static: |-
<p data-sourcepos="1:1-1:24" dir="auto">&lt;<a href="http://foo.bar/baz" rel="nofollow noreferrer noopener" target="_blank">http://foo.bar/baz</a> bim&gt;</p>
wysiwyg: |-
<p>&lt;<a target="_blank" rel="noopener noreferrer nofollow" href="http://foo.bar/baz">http://foo.bar/baz</a> bim&gt;</p>
-06_09__inlines__autolinks__010:
+06_09_00__inlines__autolinks__010:
canonical: |
<p><a href="http://example.com/%5C%5B%5C">http://example.com/\[\</a></p>
static: |-
<p data-sourcepos="1:1-1:24" dir="auto"><a href="http://example.com/%5C%5B%5C" rel="nofollow noreferrer noopener" target="_blank">http://example.com/\[\</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="http://example.com/%5C%5B%5C">http://example.com/\[\</a></p>
-06_09__inlines__autolinks__011:
+06_09_00__inlines__autolinks__011:
canonical: |
<p><a href="mailto:foo@bar.example.com">foo@bar.example.com</a></p>
static: |-
<p data-sourcepos="1:1-1:21" dir="auto"><a href="mailto:foo@bar.example.com">foo@bar.example.com</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="mailto:foo@bar.example.com">foo@bar.example.com</a></p>
-06_09__inlines__autolinks__012:
+06_09_00__inlines__autolinks__012:
canonical: |
<p><a href="mailto:foo+special@Bar.baz-bar0.com">foo+special@Bar.baz-bar0.com</a></p>
static: |-
<p data-sourcepos="1:1-1:30" dir="auto"><a href="mailto:foo+special@Bar.baz-bar0.com">foo+special@Bar.baz-bar0.com</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="mailto:foo+special@Bar.baz-bar0.com">foo+special@Bar.baz-bar0.com</a></p>
-06_09__inlines__autolinks__013:
+06_09_00__inlines__autolinks__013:
canonical: |
<p>&lt;foo+@bar.example.com&gt;</p>
static: |-
<p data-sourcepos="1:1-1:23" dir="auto">&lt;<a href="mailto:foo+@bar.example.com">foo+@bar.example.com</a>&gt;</p>
wysiwyg: |-
<p>&lt;<a target="_blank" rel="noopener noreferrer nofollow" href="mailto:foo+@bar.example.com">foo+@bar.example.com</a>&gt;</p>
-06_09__inlines__autolinks__014:
+06_09_00__inlines__autolinks__014:
canonical: |
<p>&lt;&gt;</p>
static: |-
<p data-sourcepos="1:1-1:2" dir="auto">&lt;&gt;</p>
wysiwyg: |-
<p>&lt;&gt;</p>
-06_09__inlines__autolinks__015:
+06_09_00__inlines__autolinks__015:
canonical: |
<p>&lt; http://foo.bar &gt;</p>
static: |-
<p data-sourcepos="1:1-1:18" dir="auto">&lt; <a href="http://foo.bar" rel="nofollow noreferrer noopener" target="_blank">http://foo.bar</a> &gt;</p>
wysiwyg: |-
<p>&lt; <a target="_blank" rel="noopener noreferrer nofollow" href="http://foo.bar">http://foo.bar</a> &gt;</p>
-06_09__inlines__autolinks__016:
+06_09_00__inlines__autolinks__016:
canonical: |
<p>&lt;m:abc&gt;</p>
static: |-
<p data-sourcepos="1:1-1:7" dir="auto">&lt;m:abc&gt;</p>
wysiwyg: |-
<p>&lt;m:abc&gt;</p>
-06_09__inlines__autolinks__017:
+06_09_00__inlines__autolinks__017:
canonical: |
<p>&lt;foo.bar.baz&gt;</p>
static: |-
<p data-sourcepos="1:1-1:13" dir="auto">&lt;foo.bar.baz&gt;</p>
wysiwyg: |-
<p>&lt;foo.bar.baz&gt;</p>
-06_09__inlines__autolinks__018:
+06_09_00__inlines__autolinks__018:
canonical: |
<p>http://example.com</p>
static: |-
<p data-sourcepos="1:1-1:18" dir="auto"><a href="http://example.com" rel="nofollow noreferrer noopener" target="_blank">http://example.com</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="http://example.com">http://example.com</a></p>
-06_09__inlines__autolinks__019:
+06_09_00__inlines__autolinks__019:
canonical: |
<p>foo@bar.example.com</p>
static: |-
<p data-sourcepos="1:1-1:19" dir="auto"><a href="mailto:foo@bar.example.com">foo@bar.example.com</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="mailto:foo@bar.example.com">foo@bar.example.com</a></p>
-06_10__inlines__autolinks_extension__001:
+06_10_00__inlines__autolinks_extension__001:
canonical: |
<p><a href="http://www.commonmark.org">www.commonmark.org</a></p>
static: |-
<p data-sourcepos="1:1-1:18" dir="auto"><a href="http://www.commonmark.org" rel="nofollow noreferrer noopener" target="_blank">www.commonmark.org</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="http://www.commonmark.org">www.commonmark.org</a></p>
-06_10__inlines__autolinks_extension__002:
+06_10_00__inlines__autolinks_extension__002:
canonical: |
<p>Visit <a href="http://www.commonmark.org/help">www.commonmark.org/help</a> for more information.</p>
static: |-
<p data-sourcepos="1:1-1:51" dir="auto">Visit <a href="http://www.commonmark.org/help" rel="nofollow noreferrer noopener" target="_blank">www.commonmark.org/help</a> for more information.</p>
wysiwyg: |-
<p>Visit <a target="_blank" rel="noopener noreferrer nofollow" href="http://www.commonmark.org/help">www.commonmark.org/help</a> for more information.</p>
-06_10__inlines__autolinks_extension__003:
+06_10_00__inlines__autolinks_extension__003:
canonical: |
<p>Visit <a href="http://www.commonmark.org">www.commonmark.org</a>.</p>
<p>Visit <a href="http://www.commonmark.org/a.b">www.commonmark.org/a.b</a>.</p>
@@ -7210,7 +7210,7 @@
wysiwyg: |-
<p>Visit <a target="_blank" rel="noopener noreferrer nofollow" href="http://www.commonmark.org">www.commonmark.org</a>.</p>
<p>Visit <a target="_blank" rel="noopener noreferrer nofollow" href="http://www.commonmark.org/a.b">www.commonmark.org/a.b</a>.</p>
-06_10__inlines__autolinks_extension__004:
+06_10_00__inlines__autolinks_extension__004:
canonical: |
<p><a href="http://www.google.com/search?q=Markup+(business)">www.google.com/search?q=Markup+(business)</a></p>
<p><a href="http://www.google.com/search?q=Markup+(business)">www.google.com/search?q=Markup+(business)</a>))</p>
@@ -7226,14 +7226,14 @@
<p><a target="_blank" rel="noopener noreferrer nofollow" href="http://www.google.com/search?q=Markup+(business)">www.google.com/search?q=Markup+(business)</a>))</p>
<p>(<a target="_blank" rel="noopener noreferrer nofollow" href="http://www.google.com/search?q=Markup+(business)">www.google.com/search?q=Markup+(business)</a>)</p>
<p>(<a target="_blank" rel="noopener noreferrer nofollow" href="http://www.google.com/search?q=Markup+(business)">www.google.com/search?q=Markup+(business)</a></p>
-06_10__inlines__autolinks_extension__005:
+06_10_00__inlines__autolinks_extension__005:
canonical: |
<p><a href="http://www.google.com/search?q=(business))+ok">www.google.com/search?q=(business))+ok</a></p>
static: |-
<p data-sourcepos="1:1-1:38" dir="auto"><a href="http://www.google.com/search?q=(business))+ok" rel="nofollow noreferrer noopener" target="_blank">www.google.com/search?q=(business))+ok</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="http://www.google.com/search?q=(business))+ok">www.google.com/search?q=(business))+ok</a></p>
-06_10__inlines__autolinks_extension__006:
+06_10_00__inlines__autolinks_extension__006:
canonical: |
<p><a href="http://www.google.com/search?q=commonmark&amp;hl=en">www.google.com/search?q=commonmark&amp;hl=en</a></p>
<p><a href="http://www.google.com/search?q=commonmark">www.google.com/search?q=commonmark</a>&amp;hl;</p>
@@ -7243,14 +7243,14 @@
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="http://www.google.com/search?q=commonmark&amp;hl=en">www.google.com/search?q=commonmark&amp;hl=en</a></p>
<p><a target="_blank" rel="noopener noreferrer nofollow" href="http://www.google.com/search?q=commonmark">www.google.com/search?q=commonmark</a>&amp;hl;</p>
-06_10__inlines__autolinks_extension__007:
+06_10_00__inlines__autolinks_extension__007:
canonical: |
<p><a href="http://www.commonmark.org/he">www.commonmark.org/he</a>&lt;lp</p>
static: |-
<p data-sourcepos="1:1-1:24" dir="auto"><a href="http://www.commonmark.org/he" rel="nofollow noreferrer noopener" target="_blank">www.commonmark.org/he</a>&lt;lp</p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="http://www.commonmark.org/he">www.commonmark.org/he</a>&lt;lp</p>
-06_10__inlines__autolinks_extension__008:
+06_10_00__inlines__autolinks_extension__008:
canonical: |
<p><a href="http://commonmark.org">http://commonmark.org</a></p>
<p>(Visit <a href="https://encrypted.google.com/search?q=Markup+(business)">https://encrypted.google.com/search?q=Markup+(business)</a>)</p>
@@ -7263,21 +7263,21 @@
<p><a target="_blank" rel="noopener noreferrer nofollow" href="http://commonmark.org">http://commonmark.org</a></p>
<p>(Visit <a target="_blank" rel="noopener noreferrer nofollow" href="https://encrypted.google.com/search?q=Markup+(business)">https://encrypted.google.com/search?q=Markup+(business)</a>)</p>
<p>Anonymous FTP is available at ftp://foo.bar.baz.</p>
-06_10__inlines__autolinks_extension__009:
+06_10_00__inlines__autolinks_extension__009:
canonical: |
<p><a href="mailto:foo@bar.baz">foo@bar.baz</a></p>
static: |-
<p data-sourcepos="1:1-1:11" dir="auto"><a href="mailto:foo@bar.baz">foo@bar.baz</a></p>
wysiwyg: |-
<p><a target="_blank" rel="noopener noreferrer nofollow" href="mailto:foo@bar.baz">foo@bar.baz</a></p>
-06_10__inlines__autolinks_extension__010:
+06_10_00__inlines__autolinks_extension__010:
canonical: |
<p>hello@mail+xyz.example isn't valid, but <a href="mailto:hello+xyz@mail.example">hello+xyz@mail.example</a> is.</p>
static: |-
<p data-sourcepos="1:1-1:66" dir="auto">hello@mail+xyz.example isn't valid, but <a href="mailto:hello+xyz@mail.example">hello+xyz@mail.example</a> is.</p>
wysiwyg: |-
<p>hello@mail+xyz.example isn't valid, but <a target="_blank" rel="noopener noreferrer nofollow" href="mailto:hello+xyz@mail.example">hello+xyz@mail.example</a> is.</p>
-06_10__inlines__autolinks_extension__011:
+06_10_00__inlines__autolinks_extension__011:
canonical: |
<p><a href="mailto:a.b-c_d@a.b">a.b-c_d@a.b</a></p>
<p><a href="mailto:a.b-c_d@a.b">a.b-c_d@a.b</a>.</p>
@@ -7293,21 +7293,21 @@
<p><a target="_blank" rel="noopener noreferrer nofollow" href="mailto:a.b-c_d@a.b">a.b-c_d@a.b</a>.</p>
<p>a.b-c_d@a.b-</p>
<p>a.b-c_d@a.b_</p>
-06_11__inlines__raw_html__001:
+06_11_00__inlines__raw_html__001:
canonical: |
<p><a><bab><c2c></p>
static: |-
<p data-sourcepos="1:1-1:13" dir="auto"><a></a></p>
wysiwyg: |-
<p></p>
-06_11__inlines__raw_html__002:
+06_11_00__inlines__raw_html__002:
canonical: |
<p><a/><b2/></p>
static: |-
<p data-sourcepos="1:1-1:9" dir="auto"><a></a></p>
wysiwyg: |-
<p></p>
-06_11__inlines__raw_html__003:
+06_11_00__inlines__raw_html__003:
canonical: |
<p><a /><b2
data="foo" ></p>
@@ -7315,7 +7315,7 @@
<p data-sourcepos="1:1-2:12" dir="auto"><a></a></p>
wysiwyg: |-
<p></p>
-06_11__inlines__raw_html__004:
+06_11_00__inlines__raw_html__004:
canonical: |
<p><a foo="bar" bam = 'baz <em>"</em>'
_boolean zoop:33=zoop:33 /></p>
@@ -7323,35 +7323,35 @@
<p data-sourcepos="1:1-2:27" dir="auto"><a></a></p>
wysiwyg: |-
<p></p>
-06_11__inlines__raw_html__005:
+06_11_00__inlines__raw_html__005:
canonical: |
<p>Foo <responsive-image src="foo.jpg" /></p>
static: |-
<p data-sourcepos="1:1-1:38" dir="auto">Foo </p>
wysiwyg: |-
<p>Foo </p>
-06_11__inlines__raw_html__006:
+06_11_00__inlines__raw_html__006:
canonical: |
<p>&lt;33&gt; &lt;__&gt;</p>
static: |-
<p data-sourcepos="1:1-1:9" dir="auto">&lt;33&gt; &lt;__&gt;</p>
wysiwyg: |-
<p>&lt;33&gt; &lt;__&gt;</p>
-06_11__inlines__raw_html__007:
+06_11_00__inlines__raw_html__007:
canonical: |
<p>&lt;a h*#ref=&quot;hi&quot;&gt;</p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto">&lt;a h*#ref="hi"&gt;</p>
wysiwyg: |-
<p>&lt;a h*#ref="hi"&gt;</p>
-06_11__inlines__raw_html__008:
+06_11_00__inlines__raw_html__008:
canonical: |
<p>&lt;a href=&quot;hi'&gt; &lt;a href=hi'&gt;</p>
static: |-
<p data-sourcepos="1:1-1:26" dir="auto">&lt;a href="hi'&gt; &lt;a href=hi'&gt;</p>
wysiwyg: |-
<p>&lt;a href="hi'&gt; &lt;a href=hi'&gt;</p>
-06_11__inlines__raw_html__009:
+06_11_00__inlines__raw_html__009:
canonical: |
<p>&lt; a&gt;&lt;
foo&gt;&lt;bar/ &gt;
@@ -7367,28 +7367,28 @@
foo&gt;&lt;bar/ &gt;
&lt;foo bar=baz
bim!bop /&gt;</p>
-06_11__inlines__raw_html__010:
+06_11_00__inlines__raw_html__010:
canonical: |
<p>&lt;a href='bar'title=title&gt;</p>
static: |-
<p data-sourcepos="1:1-1:25" dir="auto">&lt;a href='bar'title=title&gt;</p>
wysiwyg: |-
<p>&lt;a href='bar'title=title&gt;</p>
-06_11__inlines__raw_html__011:
+06_11_00__inlines__raw_html__011:
canonical: |
<p></a></foo ></p>
static: |-
<p data-sourcepos="1:1-1:11" dir="auto"></p>
wysiwyg: |-
<p></p>
-06_11__inlines__raw_html__012:
+06_11_00__inlines__raw_html__012:
canonical: |
<p>&lt;/a href=&quot;foo&quot;&gt;</p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto">&lt;/a href="foo"&gt;</p>
wysiwyg: |-
<p>&lt;/a href="foo"&gt;</p>
-06_11__inlines__raw_html__013:
+06_11_00__inlines__raw_html__013:
canonical: |
<p>foo <!-- this is a
comment - with hyphen --></p>
@@ -7396,14 +7396,14 @@
<p data-sourcepos="1:1-2:25" dir="auto">foo </p>
wysiwyg: |-
<p>foo </p>
-06_11__inlines__raw_html__014:
+06_11_00__inlines__raw_html__014:
canonical: |
<p>foo &lt;!-- not a comment -- two hyphens --&gt;</p>
static: |-
<p data-sourcepos="1:1-1:41" dir="auto">foo &lt;!-- not a comment -- two hyphens --&gt;</p>
wysiwyg: |-
<p>foo &lt;!-- not a comment -- two hyphens --&gt;</p>
-06_11__inlines__raw_html__015:
+06_11_00__inlines__raw_html__015:
canonical: |
<p>foo &lt;!--&gt; foo --&gt;</p>
<p>foo &lt;!-- foo---&gt;</p>
@@ -7413,49 +7413,49 @@
wysiwyg: |-
<p>foo &lt;!--&gt; foo --&gt;</p>
<p>foo &lt;!-- foo---&gt;</p>
-06_11__inlines__raw_html__016:
+06_11_00__inlines__raw_html__016:
canonical: |
<p>foo <?php echo $a; ?></p>
static: |-
<p data-sourcepos="1:1-1:21" dir="auto">foo <?php echo $a; ?></p>
wysiwyg: |-
<p>foo </p>
-06_11__inlines__raw_html__017:
+06_11_00__inlines__raw_html__017:
canonical: |
<p>foo <!ELEMENT br EMPTY></p>
static: |-
<p data-sourcepos="1:1-1:23" dir="auto">foo &lt;!ELEMENT br EMPTY&gt;</p>
wysiwyg: |-
<p>foo </p>
-06_11__inlines__raw_html__018:
+06_11_00__inlines__raw_html__018:
canonical: |
<p>foo <![CDATA[>&<]]></p>
static: |-
<p data-sourcepos="1:1-1:19" dir="auto">foo &lt;![CDATA[&gt;&amp;&lt;]]&gt;</p>
wysiwyg: |-
<p>foo &amp;&lt;]]&gt;</p>
-06_11__inlines__raw_html__019:
+06_11_00__inlines__raw_html__019:
canonical: |
<p>foo <a href="&ouml;"></p>
static: |-
<p data-sourcepos="1:1-1:21" dir="auto">foo <a href="%C3%B6" rel="nofollow noreferrer noopener" target="_blank"></a></p>
wysiwyg: |-
<p>foo </p>
-06_11__inlines__raw_html__020:
+06_11_00__inlines__raw_html__020:
canonical: |
<p>foo <a href="\*"></p>
static: |-
<p data-sourcepos="1:1-1:17" dir="auto">foo <a href="%5C*" rel="nofollow noreferrer noopener" target="_blank"></a></p>
wysiwyg: |-
<p>foo </p>
-06_11__inlines__raw_html__021:
+06_11_00__inlines__raw_html__021:
canonical: |
<p>&lt;a href=&quot;&quot;&quot;&gt;</p>
static: |-
<p data-sourcepos="1:1-1:13" dir="auto">&lt;a href="""&gt;</p>
wysiwyg: |-
<p>&lt;a href="""&gt;</p>
-06_12__inlines__disallowed_raw_html_extension__001:
+06_12_00__inlines__disallowed_raw_html_extension__001:
canonical: |
<p><strong> &lt;title> &lt;style> <em></p>
<blockquote>
@@ -7469,7 +7469,7 @@
wysiwyg: |-
<p></p>
<blockquote multiline="false"><p></p></blockquote>
-06_13__inlines__hard_line_breaks__001:
+06_13_00__inlines__hard_line_breaks__001:
canonical: |
<p>foo<br />
baz</p>
@@ -7479,7 +7479,7 @@
wysiwyg: |-
<p>foo<br>
baz</p>
-06_13__inlines__hard_line_breaks__002:
+06_13_00__inlines__hard_line_breaks__002:
canonical: |
<p>foo<br />
baz</p>
@@ -7489,7 +7489,7 @@
wysiwyg: |-
<p>foo<br>
baz</p>
-06_13__inlines__hard_line_breaks__003:
+06_13_00__inlines__hard_line_breaks__003:
canonical: |
<p>foo<br />
baz</p>
@@ -7499,7 +7499,7 @@
wysiwyg: |-
<p>foo<br>
baz</p>
-06_13__inlines__hard_line_breaks__004:
+06_13_00__inlines__hard_line_breaks__004:
canonical: |
<p>foo<br />
bar</p>
@@ -7509,7 +7509,7 @@
wysiwyg: |-
<p>foo<br>
bar</p>
-06_13__inlines__hard_line_breaks__005:
+06_13_00__inlines__hard_line_breaks__005:
canonical: |
<p>foo<br />
bar</p>
@@ -7519,7 +7519,7 @@
wysiwyg: |-
<p>foo<br>
bar</p>
-06_13__inlines__hard_line_breaks__006:
+06_13_00__inlines__hard_line_breaks__006:
canonical: |
<p><em>foo<br />
bar</em></p>
@@ -7529,7 +7529,7 @@
wysiwyg: |-
<p><em>foo<br>
bar</em></p>
-06_13__inlines__hard_line_breaks__007:
+06_13_00__inlines__hard_line_breaks__007:
canonical: |
<p><em>foo<br />
bar</em></p>
@@ -7539,27 +7539,27 @@
wysiwyg: |-
<p><em>foo<br>
bar</em></p>
-06_13__inlines__hard_line_breaks__008:
+06_13_00__inlines__hard_line_breaks__008:
canonical: |
<p><code>code span</code></p>
static: |-
<p data-sourcepos="1:1-2:5" dir="auto"><code>code span</code></p>
wysiwyg: |-
<p><code>code span</code></p>
-06_13__inlines__hard_line_breaks__009:
+06_13_00__inlines__hard_line_breaks__009:
canonical: |
<p><code>code\ span</code></p>
static: |-
<p data-sourcepos="1:1-2:5" dir="auto"><code>code\ span</code></p>
wysiwyg: |-
<p><code>code\ span</code></p>
-06_13__inlines__hard_line_breaks__010:
+06_13_00__inlines__hard_line_breaks__010:
canonical: "<p><a href=\"foo \nbar\"></p>\n"
static: |-
<p data-sourcepos="1:1-2:5" dir="auto"><a href="foo%20%20%0Abar" rel="nofollow noreferrer noopener" target="_blank"></a></p>
wysiwyg: |-
<p></p>
-06_13__inlines__hard_line_breaks__011:
+06_13_00__inlines__hard_line_breaks__011:
canonical: |
<p><a href="foo\
bar"></p>
@@ -7567,21 +7567,21 @@
<p data-sourcepos="1:1-2:5" dir="auto"><a href="foo%5C%0Abar" rel="nofollow noreferrer noopener" target="_blank"></a></p>
wysiwyg: |-
<p></p>
-06_13__inlines__hard_line_breaks__012:
+06_13_00__inlines__hard_line_breaks__012:
canonical: |
<p>foo\</p>
static: |-
<p data-sourcepos="1:1-1:4" dir="auto">foo\</p>
wysiwyg: |-
<p>foo\</p>
-06_13__inlines__hard_line_breaks__013:
+06_13_00__inlines__hard_line_breaks__013:
canonical: |
<p>foo</p>
static: |-
<p data-sourcepos="1:1-1:5" dir="auto">foo</p>
wysiwyg: |-
<p>foo</p>
-06_13__inlines__hard_line_breaks__014:
+06_13_00__inlines__hard_line_breaks__014:
canonical: |
<h3>foo\</h3>
static: |-
@@ -7589,7 +7589,7 @@
<a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>foo\</h3>
wysiwyg: |-
<h3>foo\</h3>
-06_13__inlines__hard_line_breaks__015:
+06_13_00__inlines__hard_line_breaks__015:
canonical: |
<h3>foo</h3>
static: |-
@@ -7597,7 +7597,7 @@
<a id="user-content-foo" class="anchor" href="#foo" aria-hidden="true"></a>foo</h3>
wysiwyg: |-
<h3>foo</h3>
-06_14__inlines__soft_line_breaks__001:
+06_14_00__inlines__soft_line_breaks__001:
canonical: |
<p>foo
baz</p>
@@ -7607,7 +7607,7 @@
wysiwyg: |-
<p>foo
baz</p>
-06_14__inlines__soft_line_breaks__002:
+06_14_00__inlines__soft_line_breaks__002:
canonical: |
<p>foo
baz</p>
@@ -7617,28 +7617,28 @@
wysiwyg: |-
<p>foo
baz</p>
-06_15__inlines__textual_content__001:
+06_15_00__inlines__textual_content__001:
canonical: |
<p>hello $.;'there</p>
static: |-
<p data-sourcepos="1:1-1:15" dir="auto">hello $.;'there</p>
wysiwyg: |-
<p>hello $.;'there</p>
-06_15__inlines__textual_content__002:
+06_15_00__inlines__textual_content__002:
canonical: |
<p>Foo χÏῆν</p>
static: |-
<p data-sourcepos="1:1-1:13" dir="auto">Foo χÏῆν</p>
wysiwyg: |-
<p>Foo χÏῆν</p>
-06_15__inlines__textual_content__003:
+06_15_00__inlines__textual_content__003:
canonical: |
<p>Multiple spaces</p>
static: |-
<p data-sourcepos="1:1-1:19" dir="auto">Multiple spaces</p>
wysiwyg: |-
<p>Multiple spaces</p>
-07_01__gitlab_specific_markdown__footnotes__001:
+07_01_00__gitlab_specific_markdown__footnotes__001:
canonical: |
<p>
footnote reference tag
@@ -7671,7 +7671,7 @@
wysiwyg: |-
<p>footnote reference tag <sup identifier="fortytwo">fortytwo</sup></p>
<div node="footnoteDefinition(paragraph(&quot;footnote text&quot;))" htmlattributes="[object Object]"><p>footnote text</p></div>
-07_02__gitlab_specific_markdown__task_list_items__001:
+07_02_00__gitlab_specific_markdown__task_list_items__001:
canonical: |
<ul>
<li>
@@ -7687,7 +7687,7 @@
</ul>
wysiwyg: |-
<ul start="1" parens="false" data-type="taskList"><li data-checked="false" data-type="taskItem"><label><input type="checkbox"><span></span></label><div><p>incomplete</p></div></li></ul>
-07_02__gitlab_specific_markdown__task_list_items__002:
+07_02_00__gitlab_specific_markdown__task_list_items__002:
canonical: |
<ul>
<li>
@@ -7703,7 +7703,7 @@
</ul>
wysiwyg: |-
<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>completed</p></div></li></ul>
-07_02__gitlab_specific_markdown__task_list_items__003:
+07_02_00__gitlab_specific_markdown__task_list_items__003:
canonical: |
<ul>
<li>
@@ -7720,7 +7720,9 @@
<task-button></task-button><input type="checkbox" class="task-list-item-checkbox" data-inapplicable disabled> <s>inapplicable</s>
</li>
</ul>
-07_02__gitlab_specific_markdown__task_list_items__004:
+ wysiwyg: |-
+ <ul bullet="*"><li><p>[~] inapplicable</p></li></ul>
+07_02_00__gitlab_specific_markdown__task_list_items__004:
canonical: |
<ul>
<li>
@@ -7743,7 +7745,9 @@
<p data-sourcepos="3:3-3:20">text in loose list</p>
</li>
</ul>
-07_03__gitlab_specific_markdown__front_matter__001:
+ wysiwyg: |-
+ Inapplicable task list items not yet implemented for WYSYWIG
+07_03_00__gitlab_specific_markdown__front_matter__001:
canonical: |
<pre>
<code>
@@ -7757,7 +7761,7 @@
</div>
wysiwyg: |-
<pre language="yaml" class="content-editor-code-block undefined code highlight" isfrontmatter="true"><code>title: YAML front matter</code></pre>
-07_03__gitlab_specific_markdown__front_matter__002:
+07_03_00__gitlab_specific_markdown__front_matter__002:
canonical: |
<pre>
<code>
@@ -7771,7 +7775,7 @@
</div>
wysiwyg: |-
<pre language="toml" class="content-editor-code-block undefined code highlight" isfrontmatter="true"><code>title: TOML front matter</code></pre>
-07_03__gitlab_specific_markdown__front_matter__003:
+07_03_00__gitlab_specific_markdown__front_matter__003:
canonical: |
<pre>
<code>
@@ -7791,7 +7795,7 @@
<pre language="json" class="content-editor-code-block undefined code highlight" isfrontmatter="true"><code>{
"title": "JSON front matter"
}</code></pre>
-07_03__gitlab_specific_markdown__front_matter__004:
+07_03_00__gitlab_specific_markdown__front_matter__004:
canonical: |
<p>text</p>
<hr>
@@ -7805,7 +7809,7 @@
<p>text</p>
<hr>
<h2>title: YAML front matter</h2>
-07_03__gitlab_specific_markdown__front_matter__005:
+07_03_00__gitlab_specific_markdown__front_matter__005:
canonical: |
<hr>
<h2>title: YAML front matter</h2>
@@ -7816,3 +7820,152 @@
wysiwyg: |-
<hr>
<h2>title: YAML front matter</h2>
+07_04_00__gitlab_specific_markdown__audio__001:
+ canonical: |
+ <p><audio src="audio.oga" title="audio title"></audio></p>
+ static: |-
+ <p data-sourcepos="1:1-1:33" dir="auto"><span class="media-container audio-container"><audio src="audio.oga" controls="true" data-setup="{}" data-title="audio title"></audio><a href="audio.oga" target="_blank" rel="noopener noreferrer" title="Download 'audio title'">audio title</a></span></p>
+ wysiwyg: |-
+ <p><span class="media-container audio-container"><audio src="audio.oga" controls="true" data-setup="{}" data-title="audio"></audio><a href="audio.oga">audio</a></span></p>
+07_04_00__gitlab_specific_markdown__audio__002:
+ canonical: |
+ <p><audio src="audio.oga" title="audio title"></audio></p>
+ static: |-
+ <p data-sourcepos="3:1-3:15" dir="auto"><span class="media-container audio-container"><audio src="audio.oga" controls="true" data-setup="{}" data-title="audio title"></audio><a href="audio.oga" target="_blank" rel="noopener noreferrer" title="Download 'audio title'">audio title</a></span></p>
+ wysiwyg: |-
+ <pre>[audio]: audio.oga "audio title"</pre>
+ <p><span class="media-container audio-container"><audio src="audio.oga" controls="true" data-setup="{}" data-title="audio"></audio><a href="audio.oga">audio</a></span></p>
+07_05_00__gitlab_specific_markdown__video__001:
+ canonical: |
+ <p><video src="video.m4v" title="video title"></video></p>
+ static: |-
+ <p data-sourcepos="1:1-1:33" dir="auto"><span class="media-container video-container"><video src="video.m4v" controls="true" data-setup="{}" data-title="video title" width="400" preload="metadata"></video><a href="video.m4v" target="_blank" rel="noopener noreferrer" title="Download 'video title'">video title</a></span></p>
+ wysiwyg: |-
+ <p><span class="media-container video-container"><video src="video.m4v" controls="true" data-setup="{}" data-title="video"></video><a href="video.m4v">video</a></span></p>
+07_05_00__gitlab_specific_markdown__video__002:
+ canonical: |
+ <p><video src="video.mov" title="video title"></video></p>
+ static: |-
+ <p data-sourcepos="3:1-3:15" dir="auto"><span class="media-container video-container"><video src="video.mov" controls="true" data-setup="{}" data-title="video title" width="400" preload="metadata"></video><a href="video.mov" target="_blank" rel="noopener noreferrer" title="Download 'video title'">video title</a></span></p>
+ wysiwyg: |-
+ <pre>[video]: video.mov "video title"</pre>
+ <p><span class="media-container video-container"><video src="video.mov" controls="true" data-setup="{}" data-title="video"></video><a href="video.mov">video</a></span></p>
+07_06_00__gitlab_specific_markdown__table_of_contents__001:
+ canonical: |
+ <nav>
+ <ul>
+ <li><a href="#heading-1">Heading 1</a></li>
+ <ul>
+ <li><a href="#heading-2">Heading 2</a></li>
+ </ul>
+ </ul>
+ </nav>
+ <h1>Heading 1</h1>
+ <h2>Heading 2</h2>
+ static: |-
+ <ul class="section-nav"><li>
+ <a href="#heading-1">Heading 1</a><ul><li><a href="#heading-2">Heading 2</a></li></ul>
+ </li></ul>
+ <h1 data-sourcepos="3:1-3:11" dir="auto">
+ <a id="user-content-heading-1" class="anchor" href="#heading-1" aria-hidden="true"></a>Heading 1</h1>
+ <h2 data-sourcepos="5:1-5:12" dir="auto">
+ <a id="user-content-heading-2" class="anchor" href="#heading-2" aria-hidden="true"></a>Heading 2</h2>
+ 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>Heading 1</h1>
+ <h2>Heading 2</h2>
+07_06_00__gitlab_specific_markdown__table_of_contents__002:
+ canonical: |
+ <nav>
+ <ul>
+ <li><a href="#heading-1">Heading 1</a></li>
+ <ul>
+ <li><a href="#heading-2">Heading 2</a></li>
+ </ul>
+ </ul>
+ </nav>
+ <h1>Heading 1</h1>
+ <h2>Heading 2</h2>
+ static: |-
+ <ul class="section-nav"><li>
+ <a href="#heading-1">Heading 1</a><ul><li><a href="#heading-2">Heading 2</a></li></ul>
+ </li></ul>
+ <h1 data-sourcepos="3:1-3:11" dir="auto">
+ <a id="user-content-heading-1" class="anchor" href="#heading-1" aria-hidden="true"></a>Heading 1</h1>
+ <h2 data-sourcepos="5:1-5:12" dir="auto">
+ <a id="user-content-heading-2" class="anchor" href="#heading-2" aria-hidden="true"></a>Heading 2</h2>
+ 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>Heading 1</h1>
+ <h2>Heading 2</h2>
+07_06_00__gitlab_specific_markdown__table_of_contents__003:
+ canonical: |
+ <p>[[<em>TOC</em>]]text</p>
+ <p>text[TOC]</p>
+ static: |-
+ <p data-sourcepos="1:1-2:4" dir="auto">[[<em>TOC</em>]]
+ text</p>
+ <p data-sourcepos="4:1-5:5" dir="auto">text
+ [TOC]</p>
+ wysiwyg: |-
+ <p>[[<em>TOC</em>]]
+ text</p>
+ <p>text
+ [TOC]</p>
+07_06_00__gitlab_specific_markdown__table_of_contents__004:
+ canonical: |
+ <nav>
+ <ul>
+ <li><a href="#heading-1">Heading 1</a></li>
+ </ul>
+ </nav>
+ <h1>Heading 1</h1>
+ static: |-
+ <ul class="section-nav"><li><a href="#heading-1">Heading 1</a></li></ul>
+ <h1 data-sourcepos="3:1-3:11" dir="auto">
+ <a id="user-content-heading-1" class="anchor" href="#heading-1" aria-hidden="true"></a>Heading 1</h1>
+ 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>Heading 1</h1>
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__001:
+ canonical: |
+ <p><a href="groups-test-file">groups-test-file</a></p>
+ static: |-
+ <p data-sourcepos="1:1-1:45" dir="auto"><a href="/groups/glfm_group/-/uploads/groups-test-file" data-canonical-src="/uploads/groups-test-file" data-link="true" class="gfm">groups-test-file</a></p>
+ wysiwyg: |-
+ Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__002:
+ canonical: |
+ <p><a href="projects-test-file">projects-test-file</a></p>
+ static: |-
+ <p data-sourcepos="1:1-1:40" dir="auto"><a href="/glfm_group/glfm_project/-/blob/master/projects-test-file">projects-test-file</a></p>
+ wysiwyg: |-
+ Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__003:
+ canonical: |
+ <p>This project snippet ID reference IS filtered: $88888</p>
+ static: |-
+ <p data-sourcepos="1:1-1:53" dir="auto">This project snippet ID reference IS filtered: <a href="/glfm_group/glfm_project/-/snippets/88888" data-reference-type="snippet" data-original="$88888" data-link="false" data-link-reference="false" data-project="77777" data-snippet="88888" data-container="body" data-placement="top" title="glfm_project_snippet" class="gfm gfm-snippet has-tooltip">$88888</a></p>
+ wysiwyg: |-
+ Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__004:
+ canonical: |
+ <p>This personal snippet ID reference is not filtered: $99999</p>
+ static: |-
+ <p data-sourcepos="1:1-1:58" dir="auto">This personal snippet ID reference is not filtered: $99999</p>
+ wysiwyg: |-
+ Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__005:
+ canonical: |
+ <p><a href="project-wikis-test-file">project-wikis-test-file</a></p>
+ static: |-
+ <p data-sourcepos="1:1-1:50" dir="auto"><a href="/glfm_group/glfm_project/-/wikis/project-wikis-test-file" data-canonical-src="project-wikis-test-file">project-wikis-test-file</a></p>
+ wysiwyg: |-
+ Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__006:
+ canonical: |
+ <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>
+ wysiwyg: |-
+ Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
diff --git a/glfm_specification/example_snapshots/markdown.yml b/glfm_specification/example_snapshots/markdown.yml
index c875beb77f5..9af824e120f 100644
--- a/glfm_specification/example_snapshots/markdown.yml
+++ b/glfm_specification/example_snapshots/markdown.yml
@@ -1,144 +1,144 @@
---
-02_01__preliminaries__tabs__001: "\tfoo\tbaz\t\tbim\n"
-02_01__preliminaries__tabs__002: " \tfoo\tbaz\t\tbim\n"
-02_01__preliminaries__tabs__003: " a\ta\n á½\ta\n"
-02_01__preliminaries__tabs__004: " - foo\n\n\tbar\n"
-02_01__preliminaries__tabs__005: "- foo\n\n\t\tbar\n"
-02_01__preliminaries__tabs__006: ">\t\tfoo\n"
-02_01__preliminaries__tabs__007: "-\t\tfoo\n"
-02_01__preliminaries__tabs__008: " foo\n\tbar\n"
-02_01__preliminaries__tabs__009: " - foo\n - bar\n\t - baz\n"
-02_01__preliminaries__tabs__010: "#\tFoo\n"
-02_01__preliminaries__tabs__011: "*\t*\t*\t\n"
-03_01__blocks_and_inlines__precedence__001: |
+02_01_00__preliminaries__tabs__001: "\tfoo\tbaz\t\tbim\n"
+02_01_00__preliminaries__tabs__002: " \tfoo\tbaz\t\tbim\n"
+02_01_00__preliminaries__tabs__003: " a\ta\n á½\ta\n"
+02_01_00__preliminaries__tabs__004: " - foo\n\n\tbar\n"
+02_01_00__preliminaries__tabs__005: "- foo\n\n\t\tbar\n"
+02_01_00__preliminaries__tabs__006: ">\t\tfoo\n"
+02_01_00__preliminaries__tabs__007: "-\t\tfoo\n"
+02_01_00__preliminaries__tabs__008: " foo\n\tbar\n"
+02_01_00__preliminaries__tabs__009: " - foo\n - bar\n\t - baz\n"
+02_01_00__preliminaries__tabs__010: "#\tFoo\n"
+02_01_00__preliminaries__tabs__011: "*\t*\t*\t\n"
+03_01_00__blocks_and_inlines__precedence__001: |
- `one
- two`
-04_01__leaf_blocks__thematic_breaks__001: |
+04_01_00__leaf_blocks__thematic_breaks__001: |
***
---
___
-04_01__leaf_blocks__thematic_breaks__002: |
+04_01_00__leaf_blocks__thematic_breaks__002: |
+++
-04_01__leaf_blocks__thematic_breaks__003: |
+04_01_00__leaf_blocks__thematic_breaks__003: |
===
-04_01__leaf_blocks__thematic_breaks__004: |
+04_01_00__leaf_blocks__thematic_breaks__004: |
--
**
__
-04_01__leaf_blocks__thematic_breaks__005: |2
+04_01_00__leaf_blocks__thematic_breaks__005: |2
***
***
***
-04_01__leaf_blocks__thematic_breaks__006: |2
+04_01_00__leaf_blocks__thematic_breaks__006: |2
***
-04_01__leaf_blocks__thematic_breaks__007: |
+04_01_00__leaf_blocks__thematic_breaks__007: |
Foo
***
-04_01__leaf_blocks__thematic_breaks__008: |
+04_01_00__leaf_blocks__thematic_breaks__008: |
_____________________________________
-04_01__leaf_blocks__thematic_breaks__009: |2
+04_01_00__leaf_blocks__thematic_breaks__009: |2
- - -
-04_01__leaf_blocks__thematic_breaks__010: |2
+04_01_00__leaf_blocks__thematic_breaks__010: |2
** * ** * ** * **
-04_01__leaf_blocks__thematic_breaks__011: |
+04_01_00__leaf_blocks__thematic_breaks__011: |
- - - -
-04_01__leaf_blocks__thematic_breaks__012: "- - - - \n"
-04_01__leaf_blocks__thematic_breaks__013: |
+04_01_00__leaf_blocks__thematic_breaks__012: "- - - - \n"
+04_01_00__leaf_blocks__thematic_breaks__013: |
_ _ _ _ a
a------
---a---
-04_01__leaf_blocks__thematic_breaks__014: |2
+04_01_00__leaf_blocks__thematic_breaks__014: |2
*-*
-04_01__leaf_blocks__thematic_breaks__015: |
+04_01_00__leaf_blocks__thematic_breaks__015: |
- foo
***
- bar
-04_01__leaf_blocks__thematic_breaks__016: |
+04_01_00__leaf_blocks__thematic_breaks__016: |
Foo
***
bar
-04_01__leaf_blocks__thematic_breaks__017: |
+04_01_00__leaf_blocks__thematic_breaks__017: |
Foo
---
bar
-04_01__leaf_blocks__thematic_breaks__018: |
+04_01_00__leaf_blocks__thematic_breaks__018: |
* Foo
* * *
* Bar
-04_01__leaf_blocks__thematic_breaks__019: |
+04_01_00__leaf_blocks__thematic_breaks__019: |
- Foo
- * * *
-04_02__leaf_blocks__atx_headings__001: |
+04_02_00__leaf_blocks__atx_headings__001: |
# foo
## foo
### foo
#### foo
##### foo
###### foo
-04_02__leaf_blocks__atx_headings__002: |
+04_02_00__leaf_blocks__atx_headings__002: |
####### foo
-04_02__leaf_blocks__atx_headings__003: |
+04_02_00__leaf_blocks__atx_headings__003: |
#5 bolt
#hashtag
-04_02__leaf_blocks__atx_headings__004: |
+04_02_00__leaf_blocks__atx_headings__004: |
\## foo
-04_02__leaf_blocks__atx_headings__005: |
+04_02_00__leaf_blocks__atx_headings__005: |
# foo *bar* \*baz\*
-04_02__leaf_blocks__atx_headings__006: "# foo \n"
-04_02__leaf_blocks__atx_headings__007: |2
+04_02_00__leaf_blocks__atx_headings__006: "# foo \n"
+04_02_00__leaf_blocks__atx_headings__007: |2
### foo
## foo
# foo
-04_02__leaf_blocks__atx_headings__008: |2
+04_02_00__leaf_blocks__atx_headings__008: |2
# foo
-04_02__leaf_blocks__atx_headings__009: |
+04_02_00__leaf_blocks__atx_headings__009: |
foo
# bar
-04_02__leaf_blocks__atx_headings__010: |
+04_02_00__leaf_blocks__atx_headings__010: |
## foo ##
### bar ###
-04_02__leaf_blocks__atx_headings__011: |
+04_02_00__leaf_blocks__atx_headings__011: |
# foo ##################################
##### foo ##
-04_02__leaf_blocks__atx_headings__012: "### foo ### \n"
-04_02__leaf_blocks__atx_headings__013: |
+04_02_00__leaf_blocks__atx_headings__012: "### foo ### \n"
+04_02_00__leaf_blocks__atx_headings__013: |
### foo ### b
-04_02__leaf_blocks__atx_headings__014: |
+04_02_00__leaf_blocks__atx_headings__014: |
# foo#
-04_02__leaf_blocks__atx_headings__015: |
+04_02_00__leaf_blocks__atx_headings__015: |
### foo \###
## foo #\##
# foo \#
-04_02__leaf_blocks__atx_headings__016: |
+04_02_00__leaf_blocks__atx_headings__016: |
****
## foo
****
-04_02__leaf_blocks__atx_headings__017: |
+04_02_00__leaf_blocks__atx_headings__017: |
Foo bar
# baz
Bar foo
-04_02__leaf_blocks__atx_headings__018: "## \n#\n### ###\n"
-04_03__leaf_blocks__setext_headings__001: |
+04_02_00__leaf_blocks__atx_headings__018: "## \n#\n### ###\n"
+04_03_00__leaf_blocks__setext_headings__001: |
Foo *bar*
=========
Foo *bar*
---------
-04_03__leaf_blocks__setext_headings__002: |
+04_03_00__leaf_blocks__setext_headings__002: |
Foo *bar
baz*
====
-04_03__leaf_blocks__setext_headings__003: " Foo *bar\nbaz*\t\n====\n"
-04_03__leaf_blocks__setext_headings__004: |
+04_03_00__leaf_blocks__setext_headings__003: " Foo *bar\nbaz*\t\n====\n"
+04_03_00__leaf_blocks__setext_headings__004: |
Foo
-------------------------
Foo
=
-04_03__leaf_blocks__setext_headings__005: |2
+04_03_00__leaf_blocks__setext_headings__005: |2
Foo
---
@@ -147,27 +147,27 @@
Foo
===
-04_03__leaf_blocks__setext_headings__006: |2
+04_03_00__leaf_blocks__setext_headings__006: |2
Foo
---
Foo
---
-04_03__leaf_blocks__setext_headings__007: "Foo\n ---- \n"
-04_03__leaf_blocks__setext_headings__008: |
+04_03_00__leaf_blocks__setext_headings__007: "Foo\n ---- \n"
+04_03_00__leaf_blocks__setext_headings__008: |
Foo
---
-04_03__leaf_blocks__setext_headings__009: |
+04_03_00__leaf_blocks__setext_headings__009: |
Foo
= =
Foo
--- -
-04_03__leaf_blocks__setext_headings__010: "Foo \n-----\n"
-04_03__leaf_blocks__setext_headings__011: |
+04_03_00__leaf_blocks__setext_headings__010: "Foo \n-----\n"
+04_03_00__leaf_blocks__setext_headings__011: |
Foo\
----
-04_03__leaf_blocks__setext_headings__012: |
+04_03_00__leaf_blocks__setext_headings__012: |
`Foo
----
`
@@ -175,236 +175,236 @@
<a title="a lot
---
of dashes"/>
-04_03__leaf_blocks__setext_headings__013: |
+04_03_00__leaf_blocks__setext_headings__013: |
> Foo
---
-04_03__leaf_blocks__setext_headings__014: |
+04_03_00__leaf_blocks__setext_headings__014: |
> foo
bar
===
-04_03__leaf_blocks__setext_headings__015: |
+04_03_00__leaf_blocks__setext_headings__015: |
- Foo
---
-04_03__leaf_blocks__setext_headings__016: |
+04_03_00__leaf_blocks__setext_headings__016: |
Foo
Bar
---
-04_03__leaf_blocks__setext_headings__017: |
+04_03_00__leaf_blocks__setext_headings__017: |
---
Foo
---
Bar
---
Baz
-04_03__leaf_blocks__setext_headings__018: |2
+04_03_00__leaf_blocks__setext_headings__018: |2
====
-04_03__leaf_blocks__setext_headings__019: |
+04_03_00__leaf_blocks__setext_headings__019: |
---
---
-04_03__leaf_blocks__setext_headings__020: |
+04_03_00__leaf_blocks__setext_headings__020: |
- foo
-----
-04_03__leaf_blocks__setext_headings__021: |2
+04_03_00__leaf_blocks__setext_headings__021: |2
foo
---
-04_03__leaf_blocks__setext_headings__022: |
+04_03_00__leaf_blocks__setext_headings__022: |
> foo
-----
-04_03__leaf_blocks__setext_headings__023: |
+04_03_00__leaf_blocks__setext_headings__023: |
\> foo
------
-04_03__leaf_blocks__setext_headings__024: |
+04_03_00__leaf_blocks__setext_headings__024: |
Foo
bar
---
baz
-04_03__leaf_blocks__setext_headings__025: |
+04_03_00__leaf_blocks__setext_headings__025: |
Foo
bar
---
baz
-04_03__leaf_blocks__setext_headings__026: |
+04_03_00__leaf_blocks__setext_headings__026: |
Foo
bar
* * *
baz
-04_03__leaf_blocks__setext_headings__027: |
+04_03_00__leaf_blocks__setext_headings__027: |
Foo
bar
\---
baz
-04_04__leaf_blocks__indented_code_blocks__001: |2
+04_04_00__leaf_blocks__indented_code_blocks__001: |2
a simple
indented code block
-04_04__leaf_blocks__indented_code_blocks__002: |2
+04_04_00__leaf_blocks__indented_code_blocks__002: |2
- foo
bar
-04_04__leaf_blocks__indented_code_blocks__003: |
+04_04_00__leaf_blocks__indented_code_blocks__003: |
1. foo
- bar
-04_04__leaf_blocks__indented_code_blocks__004: |2
+04_04_00__leaf_blocks__indented_code_blocks__004: |2
<a/>
*hi*
- one
-04_04__leaf_blocks__indented_code_blocks__005: " chunk1\n\n chunk2\n \n \n
- \n chunk3\n"
-04_04__leaf_blocks__indented_code_blocks__006: " chunk1\n \n chunk2\n"
-04_04__leaf_blocks__indented_code_blocks__007: |+
+04_04_00__leaf_blocks__indented_code_blocks__005: " chunk1\n\n chunk2\n \n
+ \n \n chunk3\n"
+04_04_00__leaf_blocks__indented_code_blocks__006: " chunk1\n \n chunk2\n"
+04_04_00__leaf_blocks__indented_code_blocks__007: |+
Foo
bar
-04_04__leaf_blocks__indented_code_blocks__008: |2
+04_04_00__leaf_blocks__indented_code_blocks__008: |2
foo
bar
-04_04__leaf_blocks__indented_code_blocks__009: |
+04_04_00__leaf_blocks__indented_code_blocks__009: |
# Heading
foo
Heading
------
foo
----
-04_04__leaf_blocks__indented_code_blocks__010: |2
+04_04_00__leaf_blocks__indented_code_blocks__010: |2
foo
bar
-04_04__leaf_blocks__indented_code_blocks__011: "\n \n foo\n \n\n"
-04_04__leaf_blocks__indented_code_blocks__012: " foo \n"
-04_05__leaf_blocks__fenced_code_blocks__001: |
+04_04_00__leaf_blocks__indented_code_blocks__011: "\n \n foo\n \n\n"
+04_04_00__leaf_blocks__indented_code_blocks__012: " foo \n"
+04_05_00__leaf_blocks__fenced_code_blocks__001: |
```
<
>
```
-04_05__leaf_blocks__fenced_code_blocks__002: |
+04_05_00__leaf_blocks__fenced_code_blocks__002: |
~~~
<
>
~~~
-04_05__leaf_blocks__fenced_code_blocks__003: |
+04_05_00__leaf_blocks__fenced_code_blocks__003: |
``
foo
``
-04_05__leaf_blocks__fenced_code_blocks__004: |
+04_05_00__leaf_blocks__fenced_code_blocks__004: |
```
aaa
~~~
```
-04_05__leaf_blocks__fenced_code_blocks__005: |
+04_05_00__leaf_blocks__fenced_code_blocks__005: |
~~~
aaa
```
~~~
-04_05__leaf_blocks__fenced_code_blocks__006: |
+04_05_00__leaf_blocks__fenced_code_blocks__006: |
````
aaa
```
``````
-04_05__leaf_blocks__fenced_code_blocks__007: |
+04_05_00__leaf_blocks__fenced_code_blocks__007: |
~~~~
aaa
~~~
~~~~
-04_05__leaf_blocks__fenced_code_blocks__008: |
+04_05_00__leaf_blocks__fenced_code_blocks__008: |
```
-04_05__leaf_blocks__fenced_code_blocks__009: |
+04_05_00__leaf_blocks__fenced_code_blocks__009: |
`````
```
aaa
-04_05__leaf_blocks__fenced_code_blocks__010: |
+04_05_00__leaf_blocks__fenced_code_blocks__010: |
> ```
> aaa
bbb
-04_05__leaf_blocks__fenced_code_blocks__011: "```\n\n \n```\n"
-04_05__leaf_blocks__fenced_code_blocks__012: |
+04_05_00__leaf_blocks__fenced_code_blocks__011: "```\n\n \n```\n"
+04_05_00__leaf_blocks__fenced_code_blocks__012: |
```
```
-04_05__leaf_blocks__fenced_code_blocks__013: |2
+04_05_00__leaf_blocks__fenced_code_blocks__013: |2
```
aaa
aaa
```
-04_05__leaf_blocks__fenced_code_blocks__014: |2
+04_05_00__leaf_blocks__fenced_code_blocks__014: |2
```
aaa
aaa
aaa
```
-04_05__leaf_blocks__fenced_code_blocks__015: |2
+04_05_00__leaf_blocks__fenced_code_blocks__015: |2
```
aaa
aaa
aaa
```
-04_05__leaf_blocks__fenced_code_blocks__016: |2
+04_05_00__leaf_blocks__fenced_code_blocks__016: |2
```
aaa
```
-04_05__leaf_blocks__fenced_code_blocks__017: |
+04_05_00__leaf_blocks__fenced_code_blocks__017: |
```
aaa
```
-04_05__leaf_blocks__fenced_code_blocks__018: |2
+04_05_00__leaf_blocks__fenced_code_blocks__018: |2
```
aaa
```
-04_05__leaf_blocks__fenced_code_blocks__019: |
+04_05_00__leaf_blocks__fenced_code_blocks__019: |
```
aaa
```
-04_05__leaf_blocks__fenced_code_blocks__020: |
+04_05_00__leaf_blocks__fenced_code_blocks__020: |
``` ```
aaa
-04_05__leaf_blocks__fenced_code_blocks__021: |
+04_05_00__leaf_blocks__fenced_code_blocks__021: |
~~~~~~
aaa
~~~ ~~
-04_05__leaf_blocks__fenced_code_blocks__022: |
+04_05_00__leaf_blocks__fenced_code_blocks__022: |
foo
```
bar
```
baz
-04_05__leaf_blocks__fenced_code_blocks__023: |
+04_05_00__leaf_blocks__fenced_code_blocks__023: |
foo
---
~~~
bar
~~~
# baz
-04_05__leaf_blocks__fenced_code_blocks__024: |
+04_05_00__leaf_blocks__fenced_code_blocks__024: |
```ruby
def foo(x)
return 3
end
```
-04_05__leaf_blocks__fenced_code_blocks__025: |
+04_05_00__leaf_blocks__fenced_code_blocks__025: |
~~~~ ruby startline=3 $%@#$
def foo(x)
return 3
end
~~~~~~~
-04_05__leaf_blocks__fenced_code_blocks__026: |
+04_05_00__leaf_blocks__fenced_code_blocks__026: |
````;
````
-04_05__leaf_blocks__fenced_code_blocks__027: |
+04_05_00__leaf_blocks__fenced_code_blocks__027: |
``` aa ```
foo
-04_05__leaf_blocks__fenced_code_blocks__028: |
+04_05_00__leaf_blocks__fenced_code_blocks__028: |
~~~ aa ``` ~~~
foo
~~~
-04_05__leaf_blocks__fenced_code_blocks__029: |
+04_05_00__leaf_blocks__fenced_code_blocks__029: |
```
``` aaa
```
-04_06__leaf_blocks__html_blocks__001: |
+04_06_00__leaf_blocks__html_blocks__001: |
<table><tr><td>
<pre>
**Hello**,
@@ -412,7 +412,7 @@
_world_.
</pre>
</td></tr></table>
-04_06__leaf_blocks__html_blocks__002: |
+04_06_00__leaf_blocks__html_blocks__002: |
<table>
<tr>
<td>
@@ -422,80 +422,80 @@
</table>
okay.
-04_06__leaf_blocks__html_blocks__003: |2
+04_06_00__leaf_blocks__html_blocks__003: |2
<div>
*hello*
<foo><a>
-04_06__leaf_blocks__html_blocks__004: |
+04_06_00__leaf_blocks__html_blocks__004: |
</div>
*foo*
-04_06__leaf_blocks__html_blocks__005: |
+04_06_00__leaf_blocks__html_blocks__005: |
<DIV CLASS="foo">
*Markdown*
</DIV>
-04_06__leaf_blocks__html_blocks__006: |
+04_06_00__leaf_blocks__html_blocks__006: |
<div id="foo"
class="bar">
</div>
-04_06__leaf_blocks__html_blocks__007: |
+04_06_00__leaf_blocks__html_blocks__007: |
<div id="foo" class="bar
baz">
</div>
-04_06__leaf_blocks__html_blocks__008: |
+04_06_00__leaf_blocks__html_blocks__008: |
<div>
*foo*
*bar*
-04_06__leaf_blocks__html_blocks__009: |
+04_06_00__leaf_blocks__html_blocks__009: |
<div id="foo"
*hi*
-04_06__leaf_blocks__html_blocks__010: |
+04_06_00__leaf_blocks__html_blocks__010: |
<div class
foo
-04_06__leaf_blocks__html_blocks__011: |
+04_06_00__leaf_blocks__html_blocks__011: |
<div *???-&&&-<---
*foo*
-04_06__leaf_blocks__html_blocks__012: |
+04_06_00__leaf_blocks__html_blocks__012: |
<div><a href="bar">*foo*</a></div>
-04_06__leaf_blocks__html_blocks__013: |
+04_06_00__leaf_blocks__html_blocks__013: |
<table><tr><td>
foo
</td></tr></table>
-04_06__leaf_blocks__html_blocks__014: |
+04_06_00__leaf_blocks__html_blocks__014: |
<div></div>
``` c
int x = 33;
```
-04_06__leaf_blocks__html_blocks__015: |
+04_06_00__leaf_blocks__html_blocks__015: |
<a href="foo">
*bar*
</a>
-04_06__leaf_blocks__html_blocks__016: |
+04_06_00__leaf_blocks__html_blocks__016: |
<Warning>
*bar*
</Warning>
-04_06__leaf_blocks__html_blocks__017: |
+04_06_00__leaf_blocks__html_blocks__017: |
<i class="foo">
*bar*
</i>
-04_06__leaf_blocks__html_blocks__018: |
+04_06_00__leaf_blocks__html_blocks__018: |
</ins>
*bar*
-04_06__leaf_blocks__html_blocks__019: |
+04_06_00__leaf_blocks__html_blocks__019: |
<del>
*foo*
</del>
-04_06__leaf_blocks__html_blocks__020: |
+04_06_00__leaf_blocks__html_blocks__020: |
<del>
*foo*
</del>
-04_06__leaf_blocks__html_blocks__021: |
+04_06_00__leaf_blocks__html_blocks__021: |
<del>*foo*</del>
-04_06__leaf_blocks__html_blocks__022: |
+04_06_00__leaf_blocks__html_blocks__022: |
<pre language="haskell"><code>
import Text.HTML.TagSoup
@@ -503,14 +503,14 @@
main = print $ parseTags tags
</code></pre>
okay
-04_06__leaf_blocks__html_blocks__023: |
+04_06_00__leaf_blocks__html_blocks__023: |
<script type="text/javascript">
// JavaScript example
document.getElementById("demo").innerHTML = "Hello JavaScript!";
</script>
okay
-04_06__leaf_blocks__html_blocks__024: |
+04_06_00__leaf_blocks__html_blocks__024: |
<style
type="text/css">
h1 {color:red;}
@@ -518,45 +518,45 @@
p {color:blue;}
</style>
okay
-04_06__leaf_blocks__html_blocks__025: |
+04_06_00__leaf_blocks__html_blocks__025: |
<style
type="text/css">
foo
-04_06__leaf_blocks__html_blocks__026: |
+04_06_00__leaf_blocks__html_blocks__026: |
> <div>
> foo
bar
-04_06__leaf_blocks__html_blocks__027: |
+04_06_00__leaf_blocks__html_blocks__027: |
- <div>
- foo
-04_06__leaf_blocks__html_blocks__028: |
+04_06_00__leaf_blocks__html_blocks__028: |
<style>p{color:red;}</style>
*foo*
-04_06__leaf_blocks__html_blocks__029: |
+04_06_00__leaf_blocks__html_blocks__029: |
<!-- foo -->*bar*
*baz*
-04_06__leaf_blocks__html_blocks__030: |
+04_06_00__leaf_blocks__html_blocks__030: |
<script>
foo
</script>1. *bar*
-04_06__leaf_blocks__html_blocks__031: |
+04_06_00__leaf_blocks__html_blocks__031: |
<!-- Foo
bar
baz -->
okay
-04_06__leaf_blocks__html_blocks__032: |
+04_06_00__leaf_blocks__html_blocks__032: |
<?php
echo '>';
?>
okay
-04_06__leaf_blocks__html_blocks__033: |
+04_06_00__leaf_blocks__html_blocks__033: |
<!DOCTYPE html>
-04_06__leaf_blocks__html_blocks__034: |
+04_06_00__leaf_blocks__html_blocks__034: |
<![CDATA[
function matchwo(a,b)
{
@@ -570,39 +570,39 @@
}
]]>
okay
-04_06__leaf_blocks__html_blocks__035: |2
+04_06_00__leaf_blocks__html_blocks__035: |2
<!-- foo -->
<!-- foo -->
-04_06__leaf_blocks__html_blocks__036: |2
+04_06_00__leaf_blocks__html_blocks__036: |2
<div>
<div>
-04_06__leaf_blocks__html_blocks__037: |
+04_06_00__leaf_blocks__html_blocks__037: |
Foo
<div>
bar
</div>
-04_06__leaf_blocks__html_blocks__038: |
+04_06_00__leaf_blocks__html_blocks__038: |
<div>
bar
</div>
*foo*
-04_06__leaf_blocks__html_blocks__039: |
+04_06_00__leaf_blocks__html_blocks__039: |
Foo
<a href="bar">
baz
-04_06__leaf_blocks__html_blocks__040: |
+04_06_00__leaf_blocks__html_blocks__040: |
<div>
*Emphasized* text.
</div>
-04_06__leaf_blocks__html_blocks__041: |
+04_06_00__leaf_blocks__html_blocks__041: |
<div>
*Emphasized* text.
</div>
-04_06__leaf_blocks__html_blocks__042: |
+04_06_00__leaf_blocks__html_blocks__042: |
<table>
<tr>
@@ -614,7 +614,7 @@
</tr>
</table>
-04_06__leaf_blocks__html_blocks__043: |
+04_06_00__leaf_blocks__html_blocks__043: |
<table>
<tr>
@@ -626,23 +626,23 @@
</tr>
</table>
-04_07__leaf_blocks__link_reference_definitions__001: |
+04_07_00__leaf_blocks__link_reference_definitions__001: |
[foo]: /url "title"
[foo]
-04_07__leaf_blocks__link_reference_definitions__002: " [foo]: \n /url \n 'the
- title' \n\n[foo]\n"
-04_07__leaf_blocks__link_reference_definitions__003: |
+04_07_00__leaf_blocks__link_reference_definitions__002: " [foo]: \n /url \n
+ \ 'the title' \n\n[foo]\n"
+04_07_00__leaf_blocks__link_reference_definitions__003: |
[Foo*bar\]]:my_(url) 'title (with parens)'
[Foo*bar\]]
-04_07__leaf_blocks__link_reference_definitions__004: |
+04_07_00__leaf_blocks__link_reference_definitions__004: |
[Foo bar]:
<my url>
'title'
[Foo bar]
-04_07__leaf_blocks__link_reference_definitions__005: |
+04_07_00__leaf_blocks__link_reference_definitions__005: |
[foo]: /url '
title
line1
@@ -650,91 +650,91 @@
'
[foo]
-04_07__leaf_blocks__link_reference_definitions__006: |
+04_07_00__leaf_blocks__link_reference_definitions__006: |
[foo]: /url 'title
with blank line'
[foo]
-04_07__leaf_blocks__link_reference_definitions__007: |
+04_07_00__leaf_blocks__link_reference_definitions__007: |
[foo]:
/url
[foo]
-04_07__leaf_blocks__link_reference_definitions__008: |
+04_07_00__leaf_blocks__link_reference_definitions__008: |
[foo]:
[foo]
-04_07__leaf_blocks__link_reference_definitions__009: |
+04_07_00__leaf_blocks__link_reference_definitions__009: |
[foo]: <>
[foo]
-04_07__leaf_blocks__link_reference_definitions__010: |
+04_07_00__leaf_blocks__link_reference_definitions__010: |
[foo]: <bar>(baz)
[foo]
-04_07__leaf_blocks__link_reference_definitions__011: |
+04_07_00__leaf_blocks__link_reference_definitions__011: |
[foo]: /url\bar\*baz "foo\"bar\baz"
[foo]
-04_07__leaf_blocks__link_reference_definitions__012: |
+04_07_00__leaf_blocks__link_reference_definitions__012: |
[foo]
[foo]: url
-04_07__leaf_blocks__link_reference_definitions__013: |
+04_07_00__leaf_blocks__link_reference_definitions__013: |
[foo]
[foo]: first
[foo]: second
-04_07__leaf_blocks__link_reference_definitions__014: |
+04_07_00__leaf_blocks__link_reference_definitions__014: |
[FOO]: /url
[Foo]
-04_07__leaf_blocks__link_reference_definitions__015: |
+04_07_00__leaf_blocks__link_reference_definitions__015: |
[ΑΓΩ]: /φου
[αγω]
-04_07__leaf_blocks__link_reference_definitions__016: |
+04_07_00__leaf_blocks__link_reference_definitions__016: |
[foo]: /url
-04_07__leaf_blocks__link_reference_definitions__017: |
+04_07_00__leaf_blocks__link_reference_definitions__017: |
[
foo
]: /url
bar
-04_07__leaf_blocks__link_reference_definitions__018: |
+04_07_00__leaf_blocks__link_reference_definitions__018: |
[foo]: /url "title" ok
-04_07__leaf_blocks__link_reference_definitions__019: |
+04_07_00__leaf_blocks__link_reference_definitions__019: |
[foo]: /url
"title" ok
-04_07__leaf_blocks__link_reference_definitions__020: |2
+04_07_00__leaf_blocks__link_reference_definitions__020: |2
[foo]: /url "title"
[foo]
-04_07__leaf_blocks__link_reference_definitions__021: |
+04_07_00__leaf_blocks__link_reference_definitions__021: |
```
[foo]: /url
```
[foo]
-04_07__leaf_blocks__link_reference_definitions__022: |
+04_07_00__leaf_blocks__link_reference_definitions__022: |
Foo
[bar]: /baz
[bar]
-04_07__leaf_blocks__link_reference_definitions__023: |
+04_07_00__leaf_blocks__link_reference_definitions__023: |
# [Foo]
[foo]: /url
> bar
-04_07__leaf_blocks__link_reference_definitions__024: |
+04_07_00__leaf_blocks__link_reference_definitions__024: |
[foo]: /url
bar
===
[foo]
-04_07__leaf_blocks__link_reference_definitions__025: |
+04_07_00__leaf_blocks__link_reference_definitions__025: |
[foo]: /url
===
[foo]
-04_07__leaf_blocks__link_reference_definitions__026: |
+04_07_00__leaf_blocks__link_reference_definitions__026: |
[foo]: /foo-url "foo"
[bar]: /bar-url
"bar"
@@ -743,211 +743,211 @@
[foo],
[bar],
[baz]
-04_07__leaf_blocks__link_reference_definitions__027: |
+04_07_00__leaf_blocks__link_reference_definitions__027: |
[foo]
> [foo]: /url
-04_07__leaf_blocks__link_reference_definitions__028: |
+04_07_00__leaf_blocks__link_reference_definitions__028: |
[foo]: /url
-04_08__leaf_blocks__paragraphs__001: |
+04_08_00__leaf_blocks__paragraphs__001: |
aaa
bbb
-04_08__leaf_blocks__paragraphs__002: |
+04_08_00__leaf_blocks__paragraphs__002: |
aaa
bbb
ccc
ddd
-04_08__leaf_blocks__paragraphs__003: |
+04_08_00__leaf_blocks__paragraphs__003: |
aaa
bbb
-04_08__leaf_blocks__paragraphs__004: |2
+04_08_00__leaf_blocks__paragraphs__004: |2
aaa
bbb
-04_08__leaf_blocks__paragraphs__005: |
+04_08_00__leaf_blocks__paragraphs__005: |
aaa
bbb
ccc
-04_08__leaf_blocks__paragraphs__006: |2
+04_08_00__leaf_blocks__paragraphs__006: |2
aaa
bbb
-04_08__leaf_blocks__paragraphs__007: |2
+04_08_00__leaf_blocks__paragraphs__007: |2
aaa
bbb
-04_08__leaf_blocks__paragraphs__008: "aaa \nbbb \n"
-04_09__leaf_blocks__blank_lines__001: " \n\naaa\n \n\n# aaa\n\n \n"
-04_10__leaf_blocks__tables_extension__001: |
+04_08_00__leaf_blocks__paragraphs__008: "aaa \nbbb \n"
+04_09_00__leaf_blocks__blank_lines__001: " \n\naaa\n \n\n# aaa\n\n \n"
+04_10_00__leaf_blocks__tables_extension__001: |
| foo | bar |
| --- | --- |
| baz | bim |
-04_10__leaf_blocks__tables_extension__002: |
+04_10_00__leaf_blocks__tables_extension__002: |
| abc | defghi |
:-: | -----------:
bar | baz
-04_10__leaf_blocks__tables_extension__003: |
+04_10_00__leaf_blocks__tables_extension__003: |
| f\|oo |
| ------ |
| b `\|` az |
| b **\|** im |
-04_10__leaf_blocks__tables_extension__004: |
+04_10_00__leaf_blocks__tables_extension__004: |
| abc | def |
| --- | --- |
| bar | baz |
> bar
-04_10__leaf_blocks__tables_extension__005: |
+04_10_00__leaf_blocks__tables_extension__005: |
| abc | def |
| --- | --- |
| bar | baz |
bar
bar
-04_10__leaf_blocks__tables_extension__006: |
+04_10_00__leaf_blocks__tables_extension__006: |
| abc | def |
| --- |
| bar |
-04_10__leaf_blocks__tables_extension__007: |
+04_10_00__leaf_blocks__tables_extension__007: |
| abc | def |
| --- | --- |
| bar |
| bar | baz | boo |
-04_10__leaf_blocks__tables_extension__008: |
+04_10_00__leaf_blocks__tables_extension__008: |
| abc | def |
| --- | --- |
-05_01__container_blocks__block_quotes__001: |
+05_01_00__container_blocks__block_quotes__001: |
> # Foo
> bar
> baz
-05_01__container_blocks__block_quotes__002: |
+05_01_00__container_blocks__block_quotes__002: |
># Foo
>bar
> baz
-05_01__container_blocks__block_quotes__003: |2
+05_01_00__container_blocks__block_quotes__003: |2
> # Foo
> bar
> baz
-05_01__container_blocks__block_quotes__004: |2
+05_01_00__container_blocks__block_quotes__004: |2
> # Foo
> bar
> baz
-05_01__container_blocks__block_quotes__005: |
+05_01_00__container_blocks__block_quotes__005: |
> # Foo
> bar
baz
-05_01__container_blocks__block_quotes__006: |
+05_01_00__container_blocks__block_quotes__006: |
> bar
baz
> foo
-05_01__container_blocks__block_quotes__007: |
+05_01_00__container_blocks__block_quotes__007: |
> foo
---
-05_01__container_blocks__block_quotes__008: |
+05_01_00__container_blocks__block_quotes__008: |
> - foo
- bar
-05_01__container_blocks__block_quotes__009: |
+05_01_00__container_blocks__block_quotes__009: |
> foo
bar
-05_01__container_blocks__block_quotes__010: |
+05_01_00__container_blocks__block_quotes__010: |
> ```
foo
```
-05_01__container_blocks__block_quotes__011: |
+05_01_00__container_blocks__block_quotes__011: |
> foo
- bar
-05_01__container_blocks__block_quotes__012: |
+05_01_00__container_blocks__block_quotes__012: |
>
-05_01__container_blocks__block_quotes__013: ">\n> \n> \n"
-05_01__container_blocks__block_quotes__014: ">\n> foo\n> \n"
-05_01__container_blocks__block_quotes__015: |
+05_01_00__container_blocks__block_quotes__013: ">\n> \n> \n"
+05_01_00__container_blocks__block_quotes__014: ">\n> foo\n> \n"
+05_01_00__container_blocks__block_quotes__015: |
> foo
> bar
-05_01__container_blocks__block_quotes__016: |
+05_01_00__container_blocks__block_quotes__016: |
> foo
> bar
-05_01__container_blocks__block_quotes__017: |
+05_01_00__container_blocks__block_quotes__017: |
> foo
>
> bar
-05_01__container_blocks__block_quotes__018: |
+05_01_00__container_blocks__block_quotes__018: |
foo
> bar
-05_01__container_blocks__block_quotes__019: |
+05_01_00__container_blocks__block_quotes__019: |
> aaa
***
> bbb
-05_01__container_blocks__block_quotes__020: |
+05_01_00__container_blocks__block_quotes__020: |
> bar
baz
-05_01__container_blocks__block_quotes__021: |
+05_01_00__container_blocks__block_quotes__021: |
> bar
baz
-05_01__container_blocks__block_quotes__022: |
+05_01_00__container_blocks__block_quotes__022: |
> bar
>
baz
-05_01__container_blocks__block_quotes__023: |
+05_01_00__container_blocks__block_quotes__023: |
> > > foo
bar
-05_01__container_blocks__block_quotes__024: |
+05_01_00__container_blocks__block_quotes__024: |
>>> foo
> bar
>>baz
-05_01__container_blocks__block_quotes__025: |
+05_01_00__container_blocks__block_quotes__025: |
> code
> not code
-05_02__container_blocks__list_items__001: |
+05_02_00__container_blocks__list_items__001: |
A paragraph
with two lines.
indented code
> A block quote.
-05_02__container_blocks__list_items__002: |
+05_02_00__container_blocks__list_items__002: |
1. A paragraph
with two lines.
indented code
> A block quote.
-05_02__container_blocks__list_items__003: |
+05_02_00__container_blocks__list_items__003: |
- one
two
-05_02__container_blocks__list_items__004: |
+05_02_00__container_blocks__list_items__004: |
- one
two
-05_02__container_blocks__list_items__005: |2
+05_02_00__container_blocks__list_items__005: |2
- one
two
-05_02__container_blocks__list_items__006: |2
+05_02_00__container_blocks__list_items__006: |2
- one
two
-05_02__container_blocks__list_items__007: |2
+05_02_00__container_blocks__list_items__007: |2
> > 1. one
>>
>> two
-05_02__container_blocks__list_items__008: |
+05_02_00__container_blocks__list_items__008: |
>>- one
>>
> > two
-05_02__container_blocks__list_items__009: |
+05_02_00__container_blocks__list_items__009: |
-one
2.two
-05_02__container_blocks__list_items__010: |
+05_02_00__container_blocks__list_items__010: |
- foo
bar
-05_02__container_blocks__list_items__011: |
+05_02_00__container_blocks__list_items__011: |
1. foo
```
@@ -957,62 +957,62 @@
baz
> bam
-05_02__container_blocks__list_items__012: |
+05_02_00__container_blocks__list_items__012: |
- Foo
bar
baz
-05_02__container_blocks__list_items__013: |
+05_02_00__container_blocks__list_items__013: |
123456789. ok
-05_02__container_blocks__list_items__014: |
+05_02_00__container_blocks__list_items__014: |
1234567890. not ok
-05_02__container_blocks__list_items__015: |
+05_02_00__container_blocks__list_items__015: |
0. ok
-05_02__container_blocks__list_items__016: |
+05_02_00__container_blocks__list_items__016: |
003. ok
-05_02__container_blocks__list_items__017: |
+05_02_00__container_blocks__list_items__017: |
-1. not ok
-05_02__container_blocks__list_items__018: |
+05_02_00__container_blocks__list_items__018: |
- foo
bar
-05_02__container_blocks__list_items__019: |2
+05_02_00__container_blocks__list_items__019: |2
10. foo
bar
-05_02__container_blocks__list_items__020: |2
+05_02_00__container_blocks__list_items__020: |2
indented code
paragraph
more code
-05_02__container_blocks__list_items__021: |
+05_02_00__container_blocks__list_items__021: |
1. indented code
paragraph
more code
-05_02__container_blocks__list_items__022: |
+05_02_00__container_blocks__list_items__022: |
1. indented code
paragraph
more code
-05_02__container_blocks__list_items__023: |2
+05_02_00__container_blocks__list_items__023: |2
foo
bar
-05_02__container_blocks__list_items__024: |
+05_02_00__container_blocks__list_items__024: |
- foo
bar
-05_02__container_blocks__list_items__025: |
+05_02_00__container_blocks__list_items__025: |
- foo
bar
-05_02__container_blocks__list_items__026: |
+05_02_00__container_blocks__list_items__026: |
-
foo
-
@@ -1021,130 +1021,130 @@
```
-
baz
-05_02__container_blocks__list_items__027: "- \n foo\n"
-05_02__container_blocks__list_items__028: |
+05_02_00__container_blocks__list_items__027: "- \n foo\n"
+05_02_00__container_blocks__list_items__028: |
-
foo
-05_02__container_blocks__list_items__029: |
+05_02_00__container_blocks__list_items__029: |
- foo
-
- bar
-05_02__container_blocks__list_items__030: "- foo\n- \n- bar\n"
-05_02__container_blocks__list_items__031: |
+05_02_00__container_blocks__list_items__030: "- foo\n- \n- bar\n"
+05_02_00__container_blocks__list_items__031: |
1. foo
2.
3. bar
-05_02__container_blocks__list_items__032: |
+05_02_00__container_blocks__list_items__032: |
*
-05_02__container_blocks__list_items__033: |
+05_02_00__container_blocks__list_items__033: |
foo
*
foo
1.
-05_02__container_blocks__list_items__034: |2
+05_02_00__container_blocks__list_items__034: |2
1. A paragraph
with two lines.
indented code
> A block quote.
-05_02__container_blocks__list_items__035: |2
+05_02_00__container_blocks__list_items__035: |2
1. A paragraph
with two lines.
indented code
> A block quote.
-05_02__container_blocks__list_items__036: |2
+05_02_00__container_blocks__list_items__036: |2
1. A paragraph
with two lines.
indented code
> A block quote.
-05_02__container_blocks__list_items__037: |2
+05_02_00__container_blocks__list_items__037: |2
1. A paragraph
with two lines.
indented code
> A block quote.
-05_02__container_blocks__list_items__038: |2
+05_02_00__container_blocks__list_items__038: |2
1. A paragraph
with two lines.
indented code
> A block quote.
-05_02__container_blocks__list_items__039: |2
+05_02_00__container_blocks__list_items__039: |2
1. A paragraph
with two lines.
-05_02__container_blocks__list_items__040: |
+05_02_00__container_blocks__list_items__040: |
> 1. > Blockquote
continued here.
-05_02__container_blocks__list_items__041: |
+05_02_00__container_blocks__list_items__041: |
> 1. > Blockquote
> continued here.
-05_02__container_blocks__list_items__042: |
+05_02_00__container_blocks__list_items__042: |
- foo
- bar
- baz
- boo
-05_02__container_blocks__list_items__043: |
+05_02_00__container_blocks__list_items__043: |
- foo
- bar
- baz
- boo
-05_02__container_blocks__list_items__044: |
+05_02_00__container_blocks__list_items__044: |
10) foo
- bar
-05_02__container_blocks__list_items__045: |
+05_02_00__container_blocks__list_items__045: |
10) foo
- bar
-05_02__container_blocks__list_items__046: |
+05_02_00__container_blocks__list_items__046: |
- - foo
-05_02__container_blocks__list_items__047: |
+05_02_00__container_blocks__list_items__047: |
1. - 2. foo
-05_02__container_blocks__list_items__048: |
+05_02_00__container_blocks__list_items__048: |
- # Foo
- Bar
---
baz
-05_04__container_blocks__lists__001: |
+05_04_00__container_blocks__lists__001: |
- foo
- bar
+ baz
-05_04__container_blocks__lists__002: |
+05_04_00__container_blocks__lists__002: |
1. foo
2. bar
3) baz
-05_04__container_blocks__lists__003: |
+05_04_00__container_blocks__lists__003: |
Foo
- bar
- baz
-05_04__container_blocks__lists__004: |
+05_04_00__container_blocks__lists__004: |
The number of windows in my house is
14. The number of doors is 6.
-05_04__container_blocks__lists__005: |
+05_04_00__container_blocks__lists__005: |
The number of windows in my house is
1. The number of doors is 6.
-05_04__container_blocks__lists__006: |
+05_04_00__container_blocks__lists__006: |
- foo
- bar
- baz
-05_04__container_blocks__lists__007: |
+05_04_00__container_blocks__lists__007: |
- foo
- bar
- baz
bim
-05_04__container_blocks__lists__008: |
+05_04_00__container_blocks__lists__008: |
- foo
- bar
@@ -1152,7 +1152,7 @@
- baz
- bim
-05_04__container_blocks__lists__009: |
+05_04_00__container_blocks__lists__009: |
- foo
notcode
@@ -1162,7 +1162,7 @@
<!-- -->
code
-05_04__container_blocks__lists__010: |
+05_04_00__container_blocks__lists__010: |
- a
- b
- c
@@ -1170,47 +1170,47 @@
- e
- f
- g
-05_04__container_blocks__lists__011: |
+05_04_00__container_blocks__lists__011: |
1. a
2. b
3. c
-05_04__container_blocks__lists__012: |
+05_04_00__container_blocks__lists__012: |
- a
- b
- c
- d
- e
-05_04__container_blocks__lists__013: |
+05_04_00__container_blocks__lists__013: |
1. a
2. b
3. c
-05_04__container_blocks__lists__014: |
+05_04_00__container_blocks__lists__014: |
- a
- b
- c
-05_04__container_blocks__lists__015: |
+05_04_00__container_blocks__lists__015: |
* a
*
* c
-05_04__container_blocks__lists__016: |
+05_04_00__container_blocks__lists__016: |
- a
- b
c
- d
-05_04__container_blocks__lists__017: |
+05_04_00__container_blocks__lists__017: |
- a
- b
[ref]: /url
- d
-05_04__container_blocks__lists__018: |
+05_04_00__container_blocks__lists__018: |
- a
- ```
b
@@ -1218,41 +1218,41 @@
```
- c
-05_04__container_blocks__lists__019: |
+05_04_00__container_blocks__lists__019: |
- a
- b
c
- d
-05_04__container_blocks__lists__020: |
+05_04_00__container_blocks__lists__020: |
* a
> b
>
* c
-05_04__container_blocks__lists__021: |
+05_04_00__container_blocks__lists__021: |
- a
> b
```
c
```
- d
-05_04__container_blocks__lists__022: |
+05_04_00__container_blocks__lists__022: |
- a
-05_04__container_blocks__lists__023: |
+05_04_00__container_blocks__lists__023: |
- a
- b
-05_04__container_blocks__lists__024: |
+05_04_00__container_blocks__lists__024: |
1. ```
foo
```
bar
-05_04__container_blocks__lists__025: |
+05_04_00__container_blocks__lists__025: |
* foo
* bar
baz
-05_04__container_blocks__lists__026: |
+05_04_00__container_blocks__lists__026: |
- a
- b
- c
@@ -1260,12 +1260,12 @@
- d
- e
- f
-06_01__inlines__001: |
+06_01_00__inlines__001: |
`hi`lo`
-06_02__inlines__backslash_escapes__001: |
+06_02_00__inlines__backslash_escapes__001: |
\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~
-06_02__inlines__backslash_escapes__002: "\\\t\\A\\a\\ \\3\\φ\\«\n"
-06_02__inlines__backslash_escapes__003: |
+06_02_00__inlines__backslash_escapes__002: "\\\t\\A\\a\\ \\3\\φ\\«\n"
+06_02_00__inlines__backslash_escapes__003: |
\*not emphasized*
\<br/> not a tag
\[not a link](/foo)
@@ -1275,794 +1275,794 @@
\# not a heading
\[foo]: /url "not a reference"
\&ouml; not a character entity
-06_02__inlines__backslash_escapes__004: |
+06_02_00__inlines__backslash_escapes__004: |
\\*emphasis*
-06_02__inlines__backslash_escapes__005: |
+06_02_00__inlines__backslash_escapes__005: |
foo\
bar
-06_02__inlines__backslash_escapes__006: |
+06_02_00__inlines__backslash_escapes__006: |
`` \[\` ``
-06_02__inlines__backslash_escapes__007: |2
+06_02_00__inlines__backslash_escapes__007: |2
\[\]
-06_02__inlines__backslash_escapes__008: |
+06_02_00__inlines__backslash_escapes__008: |
~~~
\[\]
~~~
-06_02__inlines__backslash_escapes__009: |
+06_02_00__inlines__backslash_escapes__009: |
<http://example.com?find=\*>
-06_02__inlines__backslash_escapes__010: |
+06_02_00__inlines__backslash_escapes__010: |
<a href="/bar\/)">
-06_02__inlines__backslash_escapes__011: |
+06_02_00__inlines__backslash_escapes__011: |
[foo](/bar\* "ti\*tle")
-06_02__inlines__backslash_escapes__012: |
+06_02_00__inlines__backslash_escapes__012: |
[foo]
[foo]: /bar\* "ti\*tle"
-06_02__inlines__backslash_escapes__013: |
+06_02_00__inlines__backslash_escapes__013: |
``` foo\+bar
foo
```
-06_03__inlines__entity_and_numeric_character_references__001: |
+06_03_00__inlines__entity_and_numeric_character_references__001: |
&nbsp; &amp; &copy; &AElig; &Dcaron;
&frac34; &HilbertSpace; &DifferentialD;
&ClockwiseContourIntegral; &ngE;
-06_03__inlines__entity_and_numeric_character_references__002: |
+06_03_00__inlines__entity_and_numeric_character_references__002: |
&#35; &#1234; &#992; &#0;
-06_03__inlines__entity_and_numeric_character_references__003: |
+06_03_00__inlines__entity_and_numeric_character_references__003: |
&#X22; &#XD06; &#xcab;
-06_03__inlines__entity_and_numeric_character_references__004: |
+06_03_00__inlines__entity_and_numeric_character_references__004: |
&nbsp &x; &#; &#x;
&#987654321;
&#abcdef0;
&ThisIsNotDefined; &hi?;
-06_03__inlines__entity_and_numeric_character_references__005: |
+06_03_00__inlines__entity_and_numeric_character_references__005: |
&copy
-06_03__inlines__entity_and_numeric_character_references__006: |
+06_03_00__inlines__entity_and_numeric_character_references__006: |
&MadeUpEntity;
-06_03__inlines__entity_and_numeric_character_references__007: |
+06_03_00__inlines__entity_and_numeric_character_references__007: |
<a href="&ouml;&ouml;.html">
-06_03__inlines__entity_and_numeric_character_references__008: |
+06_03_00__inlines__entity_and_numeric_character_references__008: |
[foo](/f&ouml;&ouml; "f&ouml;&ouml;")
-06_03__inlines__entity_and_numeric_character_references__009: |
+06_03_00__inlines__entity_and_numeric_character_references__009: |
[foo]
[foo]: /f&ouml;&ouml; "f&ouml;&ouml;"
-06_03__inlines__entity_and_numeric_character_references__010: |
+06_03_00__inlines__entity_and_numeric_character_references__010: |
``` f&ouml;&ouml;
foo
```
-06_03__inlines__entity_and_numeric_character_references__011: |
+06_03_00__inlines__entity_and_numeric_character_references__011: |
`f&ouml;&ouml;`
-06_03__inlines__entity_and_numeric_character_references__012: |2
+06_03_00__inlines__entity_and_numeric_character_references__012: |2
f&ouml;f&ouml;
-06_03__inlines__entity_and_numeric_character_references__013: |
+06_03_00__inlines__entity_and_numeric_character_references__013: |
&#42;foo&#42;
*foo*
-06_03__inlines__entity_and_numeric_character_references__014: |
+06_03_00__inlines__entity_and_numeric_character_references__014: |
&#42; foo
* foo
-06_03__inlines__entity_and_numeric_character_references__015: |
+06_03_00__inlines__entity_and_numeric_character_references__015: |
foo&#10;&#10;bar
-06_03__inlines__entity_and_numeric_character_references__016: |
+06_03_00__inlines__entity_and_numeric_character_references__016: |
&#9;foo
-06_03__inlines__entity_and_numeric_character_references__017: |
+06_03_00__inlines__entity_and_numeric_character_references__017: |
[a](url &quot;tit&quot;)
-06_04__inlines__code_spans__001: |
+06_04_00__inlines__code_spans__001: |
`foo`
-06_04__inlines__code_spans__002: |
+06_04_00__inlines__code_spans__002: |
`` foo ` bar ``
-06_04__inlines__code_spans__003: |
+06_04_00__inlines__code_spans__003: |
` `` `
-06_04__inlines__code_spans__004: |
+06_04_00__inlines__code_spans__004: |
` `` `
-06_04__inlines__code_spans__005: |
+06_04_00__inlines__code_spans__005: |
` a`
-06_04__inlines__code_spans__006: |
+06_04_00__inlines__code_spans__006: |
` b `
-06_04__inlines__code_spans__007: |
+06_04_00__inlines__code_spans__007: |
` `
` `
-06_04__inlines__code_spans__008: "``\nfoo\nbar \nbaz\n``\n"
-06_04__inlines__code_spans__009: "``\nfoo \n``\n"
-06_04__inlines__code_spans__010: "`foo bar \nbaz`\n"
-06_04__inlines__code_spans__011: |
+06_04_00__inlines__code_spans__008: "``\nfoo\nbar \nbaz\n``\n"
+06_04_00__inlines__code_spans__009: "``\nfoo \n``\n"
+06_04_00__inlines__code_spans__010: "`foo bar \nbaz`\n"
+06_04_00__inlines__code_spans__011: |
`foo\`bar`
-06_04__inlines__code_spans__012: |
+06_04_00__inlines__code_spans__012: |
``foo`bar``
-06_04__inlines__code_spans__013: |
+06_04_00__inlines__code_spans__013: |
` foo `` bar `
-06_04__inlines__code_spans__014: |
+06_04_00__inlines__code_spans__014: |
*foo`*`
-06_04__inlines__code_spans__015: |
+06_04_00__inlines__code_spans__015: |
[not a `link](/foo`)
-06_04__inlines__code_spans__016: |
+06_04_00__inlines__code_spans__016: |
`<a href="`">`
-06_04__inlines__code_spans__017: |
+06_04_00__inlines__code_spans__017: |
<a href="`">`
-06_04__inlines__code_spans__018: |
+06_04_00__inlines__code_spans__018: |
`<http://foo.bar.`baz>`
-06_04__inlines__code_spans__019: |
+06_04_00__inlines__code_spans__019: |
<http://foo.bar.`baz>`
-06_04__inlines__code_spans__020: |
+06_04_00__inlines__code_spans__020: |
```foo``
-06_04__inlines__code_spans__021: |
+06_04_00__inlines__code_spans__021: |
`foo
-06_04__inlines__code_spans__022: |
+06_04_00__inlines__code_spans__022: |
`foo``bar``
-06_05__inlines__emphasis_and_strong_emphasis__001: |
+06_05_00__inlines__emphasis_and_strong_emphasis__001: |
*foo bar*
-06_05__inlines__emphasis_and_strong_emphasis__002: |
+06_05_00__inlines__emphasis_and_strong_emphasis__002: |
a * foo bar*
-06_05__inlines__emphasis_and_strong_emphasis__003: |
+06_05_00__inlines__emphasis_and_strong_emphasis__003: |
a*"foo"*
-06_05__inlines__emphasis_and_strong_emphasis__004: |
+06_05_00__inlines__emphasis_and_strong_emphasis__004: |
* a *
-06_05__inlines__emphasis_and_strong_emphasis__005: |
+06_05_00__inlines__emphasis_and_strong_emphasis__005: |
foo*bar*
-06_05__inlines__emphasis_and_strong_emphasis__006: |
+06_05_00__inlines__emphasis_and_strong_emphasis__006: |
5*6*78
-06_05__inlines__emphasis_and_strong_emphasis__007: |
+06_05_00__inlines__emphasis_and_strong_emphasis__007: |
_foo bar_
-06_05__inlines__emphasis_and_strong_emphasis__008: |
+06_05_00__inlines__emphasis_and_strong_emphasis__008: |
_ foo bar_
-06_05__inlines__emphasis_and_strong_emphasis__009: |
+06_05_00__inlines__emphasis_and_strong_emphasis__009: |
a_"foo"_
-06_05__inlines__emphasis_and_strong_emphasis__010: |
+06_05_00__inlines__emphasis_and_strong_emphasis__010: |
foo_bar_
-06_05__inlines__emphasis_and_strong_emphasis__011: |
+06_05_00__inlines__emphasis_and_strong_emphasis__011: |
5_6_78
-06_05__inlines__emphasis_and_strong_emphasis__012: |
+06_05_00__inlines__emphasis_and_strong_emphasis__012: |
приÑтанÑм_ÑтремÑÑ‚ÑÑ_
-06_05__inlines__emphasis_and_strong_emphasis__013: |
+06_05_00__inlines__emphasis_and_strong_emphasis__013: |
aa_"bb"_cc
-06_05__inlines__emphasis_and_strong_emphasis__014: |
+06_05_00__inlines__emphasis_and_strong_emphasis__014: |
foo-_(bar)_
-06_05__inlines__emphasis_and_strong_emphasis__015: |
+06_05_00__inlines__emphasis_and_strong_emphasis__015: |
_foo*
-06_05__inlines__emphasis_and_strong_emphasis__016: |
+06_05_00__inlines__emphasis_and_strong_emphasis__016: |
*foo bar *
-06_05__inlines__emphasis_and_strong_emphasis__017: |
+06_05_00__inlines__emphasis_and_strong_emphasis__017: |
*foo bar
*
-06_05__inlines__emphasis_and_strong_emphasis__018: |
+06_05_00__inlines__emphasis_and_strong_emphasis__018: |
*(*foo)
-06_05__inlines__emphasis_and_strong_emphasis__019: |
+06_05_00__inlines__emphasis_and_strong_emphasis__019: |
*(*foo*)*
-06_05__inlines__emphasis_and_strong_emphasis__020: |
+06_05_00__inlines__emphasis_and_strong_emphasis__020: |
*foo*bar
-06_05__inlines__emphasis_and_strong_emphasis__021: |
+06_05_00__inlines__emphasis_and_strong_emphasis__021: |
_foo bar _
-06_05__inlines__emphasis_and_strong_emphasis__022: |
+06_05_00__inlines__emphasis_and_strong_emphasis__022: |
_(_foo)
-06_05__inlines__emphasis_and_strong_emphasis__023: |
+06_05_00__inlines__emphasis_and_strong_emphasis__023: |
_(_foo_)_
-06_05__inlines__emphasis_and_strong_emphasis__024: |
+06_05_00__inlines__emphasis_and_strong_emphasis__024: |
_foo_bar
-06_05__inlines__emphasis_and_strong_emphasis__025: |
+06_05_00__inlines__emphasis_and_strong_emphasis__025: |
_приÑтанÑм_ÑтремÑÑ‚ÑÑ
-06_05__inlines__emphasis_and_strong_emphasis__026: |
+06_05_00__inlines__emphasis_and_strong_emphasis__026: |
_foo_bar_baz_
-06_05__inlines__emphasis_and_strong_emphasis__027: |
+06_05_00__inlines__emphasis_and_strong_emphasis__027: |
_(bar)_.
-06_05__inlines__emphasis_and_strong_emphasis__028: |
+06_05_00__inlines__emphasis_and_strong_emphasis__028: |
**foo bar**
-06_05__inlines__emphasis_and_strong_emphasis__029: |
+06_05_00__inlines__emphasis_and_strong_emphasis__029: |
** foo bar**
-06_05__inlines__emphasis_and_strong_emphasis__030: |
+06_05_00__inlines__emphasis_and_strong_emphasis__030: |
a**"foo"**
-06_05__inlines__emphasis_and_strong_emphasis__031: |
+06_05_00__inlines__emphasis_and_strong_emphasis__031: |
foo**bar**
-06_05__inlines__emphasis_and_strong_emphasis__032: |
+06_05_00__inlines__emphasis_and_strong_emphasis__032: |
__foo bar__
-06_05__inlines__emphasis_and_strong_emphasis__033: |
+06_05_00__inlines__emphasis_and_strong_emphasis__033: |
__ foo bar__
-06_05__inlines__emphasis_and_strong_emphasis__034: |
+06_05_00__inlines__emphasis_and_strong_emphasis__034: |
__
foo bar__
-06_05__inlines__emphasis_and_strong_emphasis__035: |
+06_05_00__inlines__emphasis_and_strong_emphasis__035: |
a__"foo"__
-06_05__inlines__emphasis_and_strong_emphasis__036: |
+06_05_00__inlines__emphasis_and_strong_emphasis__036: |
foo__bar__
-06_05__inlines__emphasis_and_strong_emphasis__037: |
+06_05_00__inlines__emphasis_and_strong_emphasis__037: |
5__6__78
-06_05__inlines__emphasis_and_strong_emphasis__038: |
+06_05_00__inlines__emphasis_and_strong_emphasis__038: |
приÑтанÑм__ÑтремÑÑ‚ÑÑ__
-06_05__inlines__emphasis_and_strong_emphasis__039: |
+06_05_00__inlines__emphasis_and_strong_emphasis__039: |
__foo, __bar__, baz__
-06_05__inlines__emphasis_and_strong_emphasis__040: |
+06_05_00__inlines__emphasis_and_strong_emphasis__040: |
foo-__(bar)__
-06_05__inlines__emphasis_and_strong_emphasis__041: |
+06_05_00__inlines__emphasis_and_strong_emphasis__041: |
**foo bar **
-06_05__inlines__emphasis_and_strong_emphasis__042: |
+06_05_00__inlines__emphasis_and_strong_emphasis__042: |
**(**foo)
-06_05__inlines__emphasis_and_strong_emphasis__043: |
+06_05_00__inlines__emphasis_and_strong_emphasis__043: |
*(**foo**)*
-06_05__inlines__emphasis_and_strong_emphasis__044: |
+06_05_00__inlines__emphasis_and_strong_emphasis__044: |
**Gomphocarpus (*Gomphocarpus physocarpus*, syn.
*Asclepias physocarpa*)**
-06_05__inlines__emphasis_and_strong_emphasis__045: |
+06_05_00__inlines__emphasis_and_strong_emphasis__045: |
**foo "*bar*" foo**
-06_05__inlines__emphasis_and_strong_emphasis__046: |
+06_05_00__inlines__emphasis_and_strong_emphasis__046: |
**foo**bar
-06_05__inlines__emphasis_and_strong_emphasis__047: |
+06_05_00__inlines__emphasis_and_strong_emphasis__047: |
__foo bar __
-06_05__inlines__emphasis_and_strong_emphasis__048: |
+06_05_00__inlines__emphasis_and_strong_emphasis__048: |
__(__foo)
-06_05__inlines__emphasis_and_strong_emphasis__049: |
+06_05_00__inlines__emphasis_and_strong_emphasis__049: |
_(__foo__)_
-06_05__inlines__emphasis_and_strong_emphasis__050: |
+06_05_00__inlines__emphasis_and_strong_emphasis__050: |
__foo__bar
-06_05__inlines__emphasis_and_strong_emphasis__051: |
+06_05_00__inlines__emphasis_and_strong_emphasis__051: |
__приÑтанÑм__ÑтремÑÑ‚ÑÑ
-06_05__inlines__emphasis_and_strong_emphasis__052: |
+06_05_00__inlines__emphasis_and_strong_emphasis__052: |
__foo__bar__baz__
-06_05__inlines__emphasis_and_strong_emphasis__053: |
+06_05_00__inlines__emphasis_and_strong_emphasis__053: |
__(bar)__.
-06_05__inlines__emphasis_and_strong_emphasis__054: |
+06_05_00__inlines__emphasis_and_strong_emphasis__054: |
*foo [bar](/url)*
-06_05__inlines__emphasis_and_strong_emphasis__055: |
+06_05_00__inlines__emphasis_and_strong_emphasis__055: |
*foo
bar*
-06_05__inlines__emphasis_and_strong_emphasis__056: |
+06_05_00__inlines__emphasis_and_strong_emphasis__056: |
_foo __bar__ baz_
-06_05__inlines__emphasis_and_strong_emphasis__057: |
+06_05_00__inlines__emphasis_and_strong_emphasis__057: |
_foo _bar_ baz_
-06_05__inlines__emphasis_and_strong_emphasis__058: |
+06_05_00__inlines__emphasis_and_strong_emphasis__058: |
__foo_ bar_
-06_05__inlines__emphasis_and_strong_emphasis__059: |
+06_05_00__inlines__emphasis_and_strong_emphasis__059: |
*foo *bar**
-06_05__inlines__emphasis_and_strong_emphasis__060: |
+06_05_00__inlines__emphasis_and_strong_emphasis__060: |
*foo **bar** baz*
-06_05__inlines__emphasis_and_strong_emphasis__061: |
+06_05_00__inlines__emphasis_and_strong_emphasis__061: |
*foo**bar**baz*
-06_05__inlines__emphasis_and_strong_emphasis__062: |
+06_05_00__inlines__emphasis_and_strong_emphasis__062: |
*foo**bar*
-06_05__inlines__emphasis_and_strong_emphasis__063: |
+06_05_00__inlines__emphasis_and_strong_emphasis__063: |
***foo** bar*
-06_05__inlines__emphasis_and_strong_emphasis__064: |
+06_05_00__inlines__emphasis_and_strong_emphasis__064: |
*foo **bar***
-06_05__inlines__emphasis_and_strong_emphasis__065: |
+06_05_00__inlines__emphasis_and_strong_emphasis__065: |
*foo**bar***
-06_05__inlines__emphasis_and_strong_emphasis__066: |
+06_05_00__inlines__emphasis_and_strong_emphasis__066: |
foo***bar***baz
-06_05__inlines__emphasis_and_strong_emphasis__067: |
+06_05_00__inlines__emphasis_and_strong_emphasis__067: |
foo******bar*********baz
-06_05__inlines__emphasis_and_strong_emphasis__068: |
+06_05_00__inlines__emphasis_and_strong_emphasis__068: |
*foo **bar *baz* bim** bop*
-06_05__inlines__emphasis_and_strong_emphasis__069: |
+06_05_00__inlines__emphasis_and_strong_emphasis__069: |
*foo [*bar*](/url)*
-06_05__inlines__emphasis_and_strong_emphasis__070: |
+06_05_00__inlines__emphasis_and_strong_emphasis__070: |
** is not an empty emphasis
-06_05__inlines__emphasis_and_strong_emphasis__071: |
+06_05_00__inlines__emphasis_and_strong_emphasis__071: |
**** is not an empty strong emphasis
-06_05__inlines__emphasis_and_strong_emphasis__072: |
+06_05_00__inlines__emphasis_and_strong_emphasis__072: |
**foo [bar](/url)**
-06_05__inlines__emphasis_and_strong_emphasis__073: |
+06_05_00__inlines__emphasis_and_strong_emphasis__073: |
**foo
bar**
-06_05__inlines__emphasis_and_strong_emphasis__074: |
+06_05_00__inlines__emphasis_and_strong_emphasis__074: |
__foo _bar_ baz__
-06_05__inlines__emphasis_and_strong_emphasis__075: |
+06_05_00__inlines__emphasis_and_strong_emphasis__075: |
__foo __bar__ baz__
-06_05__inlines__emphasis_and_strong_emphasis__076: |
+06_05_00__inlines__emphasis_and_strong_emphasis__076: |
____foo__ bar__
-06_05__inlines__emphasis_and_strong_emphasis__077: |
+06_05_00__inlines__emphasis_and_strong_emphasis__077: |
**foo **bar****
-06_05__inlines__emphasis_and_strong_emphasis__078: |
+06_05_00__inlines__emphasis_and_strong_emphasis__078: |
**foo *bar* baz**
-06_05__inlines__emphasis_and_strong_emphasis__079: |
+06_05_00__inlines__emphasis_and_strong_emphasis__079: |
**foo*bar*baz**
-06_05__inlines__emphasis_and_strong_emphasis__080: |
+06_05_00__inlines__emphasis_and_strong_emphasis__080: |
***foo* bar**
-06_05__inlines__emphasis_and_strong_emphasis__081: |
+06_05_00__inlines__emphasis_and_strong_emphasis__081: |
**foo *bar***
-06_05__inlines__emphasis_and_strong_emphasis__082: |
+06_05_00__inlines__emphasis_and_strong_emphasis__082: |
**foo *bar **baz**
bim* bop**
-06_05__inlines__emphasis_and_strong_emphasis__083: |
+06_05_00__inlines__emphasis_and_strong_emphasis__083: |
**foo [*bar*](/url)**
-06_05__inlines__emphasis_and_strong_emphasis__084: |
+06_05_00__inlines__emphasis_and_strong_emphasis__084: |
__ is not an empty emphasis
-06_05__inlines__emphasis_and_strong_emphasis__085: |
+06_05_00__inlines__emphasis_and_strong_emphasis__085: |
____ is not an empty strong emphasis
-06_05__inlines__emphasis_and_strong_emphasis__086: |
+06_05_00__inlines__emphasis_and_strong_emphasis__086: |
foo ***
-06_05__inlines__emphasis_and_strong_emphasis__087: |
+06_05_00__inlines__emphasis_and_strong_emphasis__087: |
foo *\**
-06_05__inlines__emphasis_and_strong_emphasis__088: |
+06_05_00__inlines__emphasis_and_strong_emphasis__088: |
foo *_*
-06_05__inlines__emphasis_and_strong_emphasis__089: |
+06_05_00__inlines__emphasis_and_strong_emphasis__089: |
foo *****
-06_05__inlines__emphasis_and_strong_emphasis__090: |
+06_05_00__inlines__emphasis_and_strong_emphasis__090: |
foo **\***
-06_05__inlines__emphasis_and_strong_emphasis__091: |
+06_05_00__inlines__emphasis_and_strong_emphasis__091: |
foo **_**
-06_05__inlines__emphasis_and_strong_emphasis__092: |
+06_05_00__inlines__emphasis_and_strong_emphasis__092: |
**foo*
-06_05__inlines__emphasis_and_strong_emphasis__093: |
+06_05_00__inlines__emphasis_and_strong_emphasis__093: |
*foo**
-06_05__inlines__emphasis_and_strong_emphasis__094: |
+06_05_00__inlines__emphasis_and_strong_emphasis__094: |
***foo**
-06_05__inlines__emphasis_and_strong_emphasis__095: |
+06_05_00__inlines__emphasis_and_strong_emphasis__095: |
****foo*
-06_05__inlines__emphasis_and_strong_emphasis__096: |
+06_05_00__inlines__emphasis_and_strong_emphasis__096: |
**foo***
-06_05__inlines__emphasis_and_strong_emphasis__097: |
+06_05_00__inlines__emphasis_and_strong_emphasis__097: |
*foo****
-06_05__inlines__emphasis_and_strong_emphasis__098: |
+06_05_00__inlines__emphasis_and_strong_emphasis__098: |
foo ___
-06_05__inlines__emphasis_and_strong_emphasis__099: |
+06_05_00__inlines__emphasis_and_strong_emphasis__099: |
foo _\__
-06_05__inlines__emphasis_and_strong_emphasis__100: |
+06_05_00__inlines__emphasis_and_strong_emphasis__100: |
foo _*_
-06_05__inlines__emphasis_and_strong_emphasis__101: |
+06_05_00__inlines__emphasis_and_strong_emphasis__101: |
foo _____
-06_05__inlines__emphasis_and_strong_emphasis__102: |
+06_05_00__inlines__emphasis_and_strong_emphasis__102: |
foo __\___
-06_05__inlines__emphasis_and_strong_emphasis__103: |
+06_05_00__inlines__emphasis_and_strong_emphasis__103: |
foo __*__
-06_05__inlines__emphasis_and_strong_emphasis__104: |
+06_05_00__inlines__emphasis_and_strong_emphasis__104: |
__foo_
-06_05__inlines__emphasis_and_strong_emphasis__105: |
+06_05_00__inlines__emphasis_and_strong_emphasis__105: |
_foo__
-06_05__inlines__emphasis_and_strong_emphasis__106: |
+06_05_00__inlines__emphasis_and_strong_emphasis__106: |
___foo__
-06_05__inlines__emphasis_and_strong_emphasis__107: |
+06_05_00__inlines__emphasis_and_strong_emphasis__107: |
____foo_
-06_05__inlines__emphasis_and_strong_emphasis__108: |
+06_05_00__inlines__emphasis_and_strong_emphasis__108: |
__foo___
-06_05__inlines__emphasis_and_strong_emphasis__109: |
+06_05_00__inlines__emphasis_and_strong_emphasis__109: |
_foo____
-06_05__inlines__emphasis_and_strong_emphasis__110: |
+06_05_00__inlines__emphasis_and_strong_emphasis__110: |
**foo**
-06_05__inlines__emphasis_and_strong_emphasis__111: |
+06_05_00__inlines__emphasis_and_strong_emphasis__111: |
*_foo_*
-06_05__inlines__emphasis_and_strong_emphasis__112: |
+06_05_00__inlines__emphasis_and_strong_emphasis__112: |
__foo__
-06_05__inlines__emphasis_and_strong_emphasis__113: |
+06_05_00__inlines__emphasis_and_strong_emphasis__113: |
_*foo*_
-06_05__inlines__emphasis_and_strong_emphasis__114: |
+06_05_00__inlines__emphasis_and_strong_emphasis__114: |
****foo****
-06_05__inlines__emphasis_and_strong_emphasis__115: |
+06_05_00__inlines__emphasis_and_strong_emphasis__115: |
____foo____
-06_05__inlines__emphasis_and_strong_emphasis__116: |
+06_05_00__inlines__emphasis_and_strong_emphasis__116: |
******foo******
-06_05__inlines__emphasis_and_strong_emphasis__117: |
+06_05_00__inlines__emphasis_and_strong_emphasis__117: |
***foo***
-06_05__inlines__emphasis_and_strong_emphasis__118: |
+06_05_00__inlines__emphasis_and_strong_emphasis__118: |
_____foo_____
-06_05__inlines__emphasis_and_strong_emphasis__119: |
+06_05_00__inlines__emphasis_and_strong_emphasis__119: |
*foo _bar* baz_
-06_05__inlines__emphasis_and_strong_emphasis__120: |
+06_05_00__inlines__emphasis_and_strong_emphasis__120: |
*foo __bar *baz bim__ bam*
-06_05__inlines__emphasis_and_strong_emphasis__121: |
+06_05_00__inlines__emphasis_and_strong_emphasis__121: |
**foo **bar baz**
-06_05__inlines__emphasis_and_strong_emphasis__122: |
+06_05_00__inlines__emphasis_and_strong_emphasis__122: |
*foo *bar baz*
-06_05__inlines__emphasis_and_strong_emphasis__123: |
+06_05_00__inlines__emphasis_and_strong_emphasis__123: |
*[bar*](/url)
-06_05__inlines__emphasis_and_strong_emphasis__124: |
+06_05_00__inlines__emphasis_and_strong_emphasis__124: |
_foo [bar_](/url)
-06_05__inlines__emphasis_and_strong_emphasis__125: |
+06_05_00__inlines__emphasis_and_strong_emphasis__125: |
*<img src="foo" title="*"/>
-06_05__inlines__emphasis_and_strong_emphasis__126: |
+06_05_00__inlines__emphasis_and_strong_emphasis__126: |
**<a href="**">
-06_05__inlines__emphasis_and_strong_emphasis__127: |
+06_05_00__inlines__emphasis_and_strong_emphasis__127: |
__<a href="__">
-06_05__inlines__emphasis_and_strong_emphasis__128: |
+06_05_00__inlines__emphasis_and_strong_emphasis__128: |
*a `*`*
-06_05__inlines__emphasis_and_strong_emphasis__129: |
+06_05_00__inlines__emphasis_and_strong_emphasis__129: |
_a `_`_
-06_05__inlines__emphasis_and_strong_emphasis__130: |
+06_05_00__inlines__emphasis_and_strong_emphasis__130: |
**a<http://foo.bar/?q=**>
-06_05__inlines__emphasis_and_strong_emphasis__131: |
+06_05_00__inlines__emphasis_and_strong_emphasis__131: |
__a<http://foo.bar/?q=__>
-06_06__inlines__strikethrough_extension__001: |
+06_06_00__inlines__strikethrough_extension__001: |
~~Hi~~ Hello, world!
-06_06__inlines__strikethrough_extension__002: |
+06_06_00__inlines__strikethrough_extension__002: |
This ~~has a
new paragraph~~.
-06_07__inlines__links__001: |
+06_07_00__inlines__links__001: |
[link](/uri "title")
-06_07__inlines__links__002: |
+06_07_00__inlines__links__002: |
[link](/uri)
-06_07__inlines__links__003: |
+06_07_00__inlines__links__003: |
[link]()
-06_07__inlines__links__004: |
+06_07_00__inlines__links__004: |
[link](<>)
-06_07__inlines__links__005: |
+06_07_00__inlines__links__005: |
[link](/my uri)
-06_07__inlines__links__006: |
+06_07_00__inlines__links__006: |
[link](</my uri>)
-06_07__inlines__links__007: |
+06_07_00__inlines__links__007: |
[link](foo
bar)
-06_07__inlines__links__008: |
+06_07_00__inlines__links__008: |
[link](<foo
bar>)
-06_07__inlines__links__009: |
+06_07_00__inlines__links__009: |
[a](<b)c>)
-06_07__inlines__links__010: |
+06_07_00__inlines__links__010: |
[link](<foo\>)
-06_07__inlines__links__011: |
+06_07_00__inlines__links__011: |
[a](<b)c
[a](<b)c>
[a](<b>c)
-06_07__inlines__links__012: |
+06_07_00__inlines__links__012: |
[link](\(foo\))
-06_07__inlines__links__013: |
+06_07_00__inlines__links__013: |
[link](foo(and(bar)))
-06_07__inlines__links__014: |
+06_07_00__inlines__links__014: |
[link](foo\(and\(bar\))
-06_07__inlines__links__015: |
+06_07_00__inlines__links__015: |
[link](<foo(and(bar)>)
-06_07__inlines__links__016: |
+06_07_00__inlines__links__016: |
[link](foo\)\:)
-06_07__inlines__links__017: |
+06_07_00__inlines__links__017: |
[link](#fragment)
[link](http://example.com#fragment)
[link](http://example.com?foo=3#frag)
-06_07__inlines__links__018: |
+06_07_00__inlines__links__018: |
[link](foo\bar)
-06_07__inlines__links__019: |
+06_07_00__inlines__links__019: |
[link](foo%20b&auml;)
-06_07__inlines__links__020: |
+06_07_00__inlines__links__020: |
[link]("title")
-06_07__inlines__links__021: |
+06_07_00__inlines__links__021: |
[link](/url "title")
[link](/url 'title')
[link](/url (title))
-06_07__inlines__links__022: |
+06_07_00__inlines__links__022: |
[link](/url "title \"&quot;")
-06_07__inlines__links__023: |
+06_07_00__inlines__links__023: |
[link](/url "title")
-06_07__inlines__links__024: |
+06_07_00__inlines__links__024: |
[link](/url "title "and" title")
-06_07__inlines__links__025: |
+06_07_00__inlines__links__025: |
[link](/url 'title "and" title')
-06_07__inlines__links__026: |
+06_07_00__inlines__links__026: |
[link]( /uri
"title" )
-06_07__inlines__links__027: |
+06_07_00__inlines__links__027: |
[link] (/uri)
-06_07__inlines__links__028: |
+06_07_00__inlines__links__028: |
[link [foo [bar]]](/uri)
-06_07__inlines__links__029: |
+06_07_00__inlines__links__029: |
[link] bar](/uri)
-06_07__inlines__links__030: |
+06_07_00__inlines__links__030: |
[link [bar](/uri)
-06_07__inlines__links__031: |
+06_07_00__inlines__links__031: |
[link \[bar](/uri)
-06_07__inlines__links__032: |
+06_07_00__inlines__links__032: |
[link *foo **bar** `#`*](/uri)
-06_07__inlines__links__033: |
+06_07_00__inlines__links__033: |
[![moon](moon.jpg)](/uri)
-06_07__inlines__links__034: |
+06_07_00__inlines__links__034: |
[foo [bar](/uri)](/uri)
-06_07__inlines__links__035: |
+06_07_00__inlines__links__035: |
[foo *[bar [baz](/uri)](/uri)*](/uri)
-06_07__inlines__links__036: |
+06_07_00__inlines__links__036: |
![[[foo](uri1)](uri2)](uri3)
-06_07__inlines__links__037: |
+06_07_00__inlines__links__037: |
*[foo*](/uri)
-06_07__inlines__links__038: |
+06_07_00__inlines__links__038: |
[foo *bar](baz*)
-06_07__inlines__links__039: |
+06_07_00__inlines__links__039: |
*foo [bar* baz]
-06_07__inlines__links__040: |
+06_07_00__inlines__links__040: |
[foo <bar attr="](baz)">
-06_07__inlines__links__041: |
+06_07_00__inlines__links__041: |
[foo`](/uri)`
-06_07__inlines__links__042: |
+06_07_00__inlines__links__042: |
[foo<http://example.com/?search=](uri)>
-06_07__inlines__links__043: |
+06_07_00__inlines__links__043: |
[foo][bar]
[bar]: /url "title"
-06_07__inlines__links__044: |
+06_07_00__inlines__links__044: |
[link [foo [bar]]][ref]
[ref]: /uri
-06_07__inlines__links__045: |
+06_07_00__inlines__links__045: |
[link \[bar][ref]
[ref]: /uri
-06_07__inlines__links__046: |
+06_07_00__inlines__links__046: |
[link *foo **bar** `#`*][ref]
[ref]: /uri
-06_07__inlines__links__047: |
+06_07_00__inlines__links__047: |
[![moon](moon.jpg)][ref]
[ref]: /uri
-06_07__inlines__links__048: |
+06_07_00__inlines__links__048: |
[foo [bar](/uri)][ref]
[ref]: /uri
-06_07__inlines__links__049: |
+06_07_00__inlines__links__049: |
[foo *bar [baz][ref]*][ref]
[ref]: /uri
-06_07__inlines__links__050: |
+06_07_00__inlines__links__050: |
*[foo*][ref]
[ref]: /uri
-06_07__inlines__links__051: |
+06_07_00__inlines__links__051: |
[foo *bar][ref]
[ref]: /uri
-06_07__inlines__links__052: |
+06_07_00__inlines__links__052: |
[foo <bar attr="][ref]">
[ref]: /uri
-06_07__inlines__links__053: |
+06_07_00__inlines__links__053: |
[foo`][ref]`
[ref]: /uri
-06_07__inlines__links__054: |
+06_07_00__inlines__links__054: |
[foo<http://example.com/?search=][ref]>
[ref]: /uri
-06_07__inlines__links__055: |
+06_07_00__inlines__links__055: |
[foo][BaR]
[bar]: /url "title"
-06_07__inlines__links__056: |
+06_07_00__inlines__links__056: |
[Толпой][Толпой] is a Russian word.
[ТОЛПОЙ]: /url
-06_07__inlines__links__057: |
+06_07_00__inlines__links__057: |
[Foo
bar]: /url
[Baz][Foo bar]
-06_07__inlines__links__058: |
+06_07_00__inlines__links__058: |
[foo] [bar]
[bar]: /url "title"
-06_07__inlines__links__059: |
+06_07_00__inlines__links__059: |
[foo]
[bar]
[bar]: /url "title"
-06_07__inlines__links__060: |
+06_07_00__inlines__links__060: |
[foo]: /url1
[foo]: /url2
[bar][foo]
-06_07__inlines__links__061: |
+06_07_00__inlines__links__061: |
[bar][foo\!]
[foo!]: /url
-06_07__inlines__links__062: |
+06_07_00__inlines__links__062: |
[foo][ref[]
[ref[]: /uri
-06_07__inlines__links__063: |
+06_07_00__inlines__links__063: |
[foo][ref[bar]]
[ref[bar]]: /uri
-06_07__inlines__links__064: |
+06_07_00__inlines__links__064: |
[[[foo]]]
[[[foo]]]: /url
-06_07__inlines__links__065: |
+06_07_00__inlines__links__065: |
[foo][ref\[]
[ref\[]: /uri
-06_07__inlines__links__066: |
+06_07_00__inlines__links__066: |
[bar\\]: /uri
[bar\\]
-06_07__inlines__links__067: |
+06_07_00__inlines__links__067: |
[]
[]: /uri
-06_07__inlines__links__068: |
+06_07_00__inlines__links__068: |
[
]
[
]: /uri
-06_07__inlines__links__069: |
+06_07_00__inlines__links__069: |
[foo][]
[foo]: /url "title"
-06_07__inlines__links__070: |
+06_07_00__inlines__links__070: |
[*foo* bar][]
[*foo* bar]: /url "title"
-06_07__inlines__links__071: |
+06_07_00__inlines__links__071: |
[Foo][]
[foo]: /url "title"
-06_07__inlines__links__072: "[foo] \n[]\n\n[foo]: /url \"title\"\n"
-06_07__inlines__links__073: |
+06_07_00__inlines__links__072: "[foo] \n[]\n\n[foo]: /url \"title\"\n"
+06_07_00__inlines__links__073: |
[foo]
[foo]: /url "title"
-06_07__inlines__links__074: |
+06_07_00__inlines__links__074: |
[*foo* bar]
[*foo* bar]: /url "title"
-06_07__inlines__links__075: |
+06_07_00__inlines__links__075: |
[[*foo* bar]]
[*foo* bar]: /url "title"
-06_07__inlines__links__076: |
+06_07_00__inlines__links__076: |
[[bar [foo]
[foo]: /url
-06_07__inlines__links__077: |
+06_07_00__inlines__links__077: |
[Foo]
[foo]: /url "title"
-06_07__inlines__links__078: |
+06_07_00__inlines__links__078: |
[foo] bar
[foo]: /url
-06_07__inlines__links__079: |
+06_07_00__inlines__links__079: |
\[foo]
[foo]: /url "title"
-06_07__inlines__links__080: |
+06_07_00__inlines__links__080: |
[foo*]: /url
*[foo*]
-06_07__inlines__links__081: |
+06_07_00__inlines__links__081: |
[foo][bar]
[foo]: /url1
[bar]: /url2
-06_07__inlines__links__082: |
+06_07_00__inlines__links__082: |
[foo][]
[foo]: /url1
-06_07__inlines__links__083: |
+06_07_00__inlines__links__083: |
[foo]()
[foo]: /url1
-06_07__inlines__links__084: |
+06_07_00__inlines__links__084: |
[foo](not a link)
[foo]: /url1
-06_07__inlines__links__085: |
+06_07_00__inlines__links__085: |
[foo][bar][baz]
[baz]: /url
-06_07__inlines__links__086: |
+06_07_00__inlines__links__086: |
[foo][bar][baz]
[baz]: /url1
[bar]: /url2
-06_07__inlines__links__087: |
+06_07_00__inlines__links__087: |
[foo][bar][baz]
[baz]: /url1
[foo]: /url2
-06_08__inlines__images__001: |
+06_08_00__inlines__images__001: |
![foo](/url "title")
-06_08__inlines__images__002: |
+06_08_00__inlines__images__002: |
![foo *bar*]
[foo *bar*]: train.jpg "train & tracks"
-06_08__inlines__images__003: |
+06_08_00__inlines__images__003: |
![foo ![bar](/url)](/url2)
-06_08__inlines__images__004: |
+06_08_00__inlines__images__004: |
![foo [bar](/url)](/url2)
-06_08__inlines__images__005: |
+06_08_00__inlines__images__005: |
![foo *bar*][]
[foo *bar*]: train.jpg "train & tracks"
-06_08__inlines__images__006: |
+06_08_00__inlines__images__006: |
![foo *bar*][foobar]
[FOOBAR]: train.jpg "train & tracks"
-06_08__inlines__images__007: |
+06_08_00__inlines__images__007: |
![foo](train.jpg)
-06_08__inlines__images__008: |
+06_08_00__inlines__images__008: |
My ![foo bar](/path/to/train.jpg "title" )
-06_08__inlines__images__009: |
+06_08_00__inlines__images__009: |
![foo](<url>)
-06_08__inlines__images__010: |
+06_08_00__inlines__images__010: |
![](/url)
-06_08__inlines__images__011: |
+06_08_00__inlines__images__011: |
![foo][bar]
[bar]: /url
-06_08__inlines__images__012: |
+06_08_00__inlines__images__012: |
![foo][bar]
[BAR]: /url
-06_08__inlines__images__013: |
+06_08_00__inlines__images__013: |
![foo][]
[foo]: /url "title"
-06_08__inlines__images__014: |
+06_08_00__inlines__images__014: |
![*foo* bar][]
[*foo* bar]: /url "title"
-06_08__inlines__images__015: |
+06_08_00__inlines__images__015: |
![Foo][]
[foo]: /url "title"
-06_08__inlines__images__016: "![foo] \n[]\n\n[foo]: /url \"title\"\n"
-06_08__inlines__images__017: |
+06_08_00__inlines__images__016: "![foo] \n[]\n\n[foo]: /url \"title\"\n"
+06_08_00__inlines__images__017: |
![foo]
[foo]: /url "title"
-06_08__inlines__images__018: |
+06_08_00__inlines__images__018: |
![*foo* bar]
[*foo* bar]: /url "title"
-06_08__inlines__images__019: |
+06_08_00__inlines__images__019: |
![[foo]]
[[foo]]: /url "title"
-06_08__inlines__images__020: |
+06_08_00__inlines__images__020: |
![Foo]
[foo]: /url "title"
-06_08__inlines__images__021: |
+06_08_00__inlines__images__021: |
!\[foo]
[foo]: /url "title"
-06_08__inlines__images__022: |
+06_08_00__inlines__images__022: |
\![foo]
[foo]: /url "title"
-06_09__inlines__autolinks__001: |
+06_09_00__inlines__autolinks__001: |
<http://foo.bar.baz>
-06_09__inlines__autolinks__002: |
+06_09_00__inlines__autolinks__002: |
<http://foo.bar.baz/test?q=hello&id=22&boolean>
-06_09__inlines__autolinks__003: |
+06_09_00__inlines__autolinks__003: |
<irc://foo.bar:2233/baz>
-06_09__inlines__autolinks__004: |
+06_09_00__inlines__autolinks__004: |
<MAILTO:FOO@BAR.BAZ>
-06_09__inlines__autolinks__005: |
+06_09_00__inlines__autolinks__005: |
<a+b+c:d>
-06_09__inlines__autolinks__006: |
+06_09_00__inlines__autolinks__006: |
<made-up-scheme://foo,bar>
-06_09__inlines__autolinks__007: |
+06_09_00__inlines__autolinks__007: |
<http://../>
-06_09__inlines__autolinks__008: |
+06_09_00__inlines__autolinks__008: |
<localhost:5001/foo>
-06_09__inlines__autolinks__009: |
+06_09_00__inlines__autolinks__009: |
<http://foo.bar/baz bim>
-06_09__inlines__autolinks__010: |
+06_09_00__inlines__autolinks__010: |
<http://example.com/\[\>
-06_09__inlines__autolinks__011: |
+06_09_00__inlines__autolinks__011: |
<foo@bar.example.com>
-06_09__inlines__autolinks__012: |
+06_09_00__inlines__autolinks__012: |
<foo+special@Bar.baz-bar0.com>
-06_09__inlines__autolinks__013: |
+06_09_00__inlines__autolinks__013: |
<foo\+@bar.example.com>
-06_09__inlines__autolinks__014: |
+06_09_00__inlines__autolinks__014: |
<>
-06_09__inlines__autolinks__015: |
+06_09_00__inlines__autolinks__015: |
< http://foo.bar >
-06_09__inlines__autolinks__016: |
+06_09_00__inlines__autolinks__016: |
<m:abc>
-06_09__inlines__autolinks__017: |
+06_09_00__inlines__autolinks__017: |
<foo.bar.baz>
-06_09__inlines__autolinks__018: |
+06_09_00__inlines__autolinks__018: |
http://example.com
-06_09__inlines__autolinks__019: |
+06_09_00__inlines__autolinks__019: |
foo@bar.example.com
-06_10__inlines__autolinks_extension__001: |
+06_10_00__inlines__autolinks_extension__001: |
www.commonmark.org
-06_10__inlines__autolinks_extension__002: |
+06_10_00__inlines__autolinks_extension__002: |
Visit www.commonmark.org/help for more information.
-06_10__inlines__autolinks_extension__003: |
+06_10_00__inlines__autolinks_extension__003: |
Visit www.commonmark.org.
Visit www.commonmark.org/a.b.
-06_10__inlines__autolinks_extension__004: |
+06_10_00__inlines__autolinks_extension__004: |
www.google.com/search?q=Markup+(business)
www.google.com/search?q=Markup+(business)))
@@ -2070,25 +2070,25 @@
(www.google.com/search?q=Markup+(business))
(www.google.com/search?q=Markup+(business)
-06_10__inlines__autolinks_extension__005: |
+06_10_00__inlines__autolinks_extension__005: |
www.google.com/search?q=(business))+ok
-06_10__inlines__autolinks_extension__006: |
+06_10_00__inlines__autolinks_extension__006: |
www.google.com/search?q=commonmark&hl=en
www.google.com/search?q=commonmark&hl;
-06_10__inlines__autolinks_extension__007: |
+06_10_00__inlines__autolinks_extension__007: |
www.commonmark.org/he<lp
-06_10__inlines__autolinks_extension__008: |
+06_10_00__inlines__autolinks_extension__008: |
http://commonmark.org
(Visit https://encrypted.google.com/search?q=Markup+(business))
Anonymous FTP is available at ftp://foo.bar.baz.
-06_10__inlines__autolinks_extension__009: |
+06_10_00__inlines__autolinks_extension__009: |
foo@bar.baz
-06_10__inlines__autolinks_extension__010: |
+06_10_00__inlines__autolinks_extension__010: |
hello@mail+xyz.example isn't valid, but hello+xyz@mail.example is.
-06_10__inlines__autolinks_extension__011: |
+06_10_00__inlines__autolinks_extension__011: |
a.b-c_d@a.b
a.b-c_d@a.b.
@@ -2096,134 +2096,180 @@
a.b-c_d@a.b-
a.b-c_d@a.b_
-06_11__inlines__raw_html__001: |
+06_11_00__inlines__raw_html__001: |
<a><bab><c2c>
-06_11__inlines__raw_html__002: |
+06_11_00__inlines__raw_html__002: |
<a/><b2/>
-06_11__inlines__raw_html__003: |
+06_11_00__inlines__raw_html__003: |
<a /><b2
data="foo" >
-06_11__inlines__raw_html__004: |
+06_11_00__inlines__raw_html__004: |
<a foo="bar" bam = 'baz <em>"</em>'
_boolean zoop:33=zoop:33 />
-06_11__inlines__raw_html__005: |
+06_11_00__inlines__raw_html__005: |
Foo <responsive-image src="foo.jpg" />
-06_11__inlines__raw_html__006: |
+06_11_00__inlines__raw_html__006: |
<33> <__>
-06_11__inlines__raw_html__007: |
+06_11_00__inlines__raw_html__007: |
<a h*#ref="hi">
-06_11__inlines__raw_html__008: |
+06_11_00__inlines__raw_html__008: |
<a href="hi'> <a href=hi'>
-06_11__inlines__raw_html__009: |
+06_11_00__inlines__raw_html__009: |
< a><
foo><bar/ >
<foo bar=baz
bim!bop />
-06_11__inlines__raw_html__010: |
+06_11_00__inlines__raw_html__010: |
<a href='bar'title=title>
-06_11__inlines__raw_html__011: |
+06_11_00__inlines__raw_html__011: |
</a></foo >
-06_11__inlines__raw_html__012: |
+06_11_00__inlines__raw_html__012: |
</a href="foo">
-06_11__inlines__raw_html__013: |
+06_11_00__inlines__raw_html__013: |
foo <!-- this is a
comment - with hyphen -->
-06_11__inlines__raw_html__014: |
+06_11_00__inlines__raw_html__014: |
foo <!-- not a comment -- two hyphens -->
-06_11__inlines__raw_html__015: |
+06_11_00__inlines__raw_html__015: |
foo <!--> foo -->
foo <!-- foo--->
-06_11__inlines__raw_html__016: |
+06_11_00__inlines__raw_html__016: |
foo <?php echo $a; ?>
-06_11__inlines__raw_html__017: |
+06_11_00__inlines__raw_html__017: |
foo <!ELEMENT br EMPTY>
-06_11__inlines__raw_html__018: |
+06_11_00__inlines__raw_html__018: |
foo <![CDATA[>&<]]>
-06_11__inlines__raw_html__019: |
+06_11_00__inlines__raw_html__019: |
foo <a href="&ouml;">
-06_11__inlines__raw_html__020: |
+06_11_00__inlines__raw_html__020: |
foo <a href="\*">
-06_11__inlines__raw_html__021: |
+06_11_00__inlines__raw_html__021: |
<a href="\"">
-06_12__inlines__disallowed_raw_html_extension__001: |
+06_12_00__inlines__disallowed_raw_html_extension__001: |
<strong> <title> <style> <em>
<blockquote>
<xmp> is disallowed. <XMP> is also disallowed.
</blockquote>
-06_13__inlines__hard_line_breaks__001: "foo \nbaz\n"
-06_13__inlines__hard_line_breaks__002: |
+06_13_00__inlines__hard_line_breaks__001: "foo \nbaz\n"
+06_13_00__inlines__hard_line_breaks__002: |
foo\
baz
-06_13__inlines__hard_line_breaks__003: "foo \nbaz\n"
-06_13__inlines__hard_line_breaks__004: "foo \n bar\n"
-06_13__inlines__hard_line_breaks__005: |
+06_13_00__inlines__hard_line_breaks__003: "foo \nbaz\n"
+06_13_00__inlines__hard_line_breaks__004: "foo \n bar\n"
+06_13_00__inlines__hard_line_breaks__005: |
foo\
bar
-06_13__inlines__hard_line_breaks__006: "*foo \nbar*\n"
-06_13__inlines__hard_line_breaks__007: |
+06_13_00__inlines__hard_line_breaks__006: "*foo \nbar*\n"
+06_13_00__inlines__hard_line_breaks__007: |
*foo\
bar*
-06_13__inlines__hard_line_breaks__008: "`code \nspan`\n"
-06_13__inlines__hard_line_breaks__009: |
+06_13_00__inlines__hard_line_breaks__008: "`code \nspan`\n"
+06_13_00__inlines__hard_line_breaks__009: |
`code\
span`
-06_13__inlines__hard_line_breaks__010: "<a href=\"foo \nbar\">\n"
-06_13__inlines__hard_line_breaks__011: |
+06_13_00__inlines__hard_line_breaks__010: "<a href=\"foo \nbar\">\n"
+06_13_00__inlines__hard_line_breaks__011: |
<a href="foo\
bar">
-06_13__inlines__hard_line_breaks__012: |
+06_13_00__inlines__hard_line_breaks__012: |
foo\
-06_13__inlines__hard_line_breaks__013: "foo \n"
-06_13__inlines__hard_line_breaks__014: |
+06_13_00__inlines__hard_line_breaks__013: "foo \n"
+06_13_00__inlines__hard_line_breaks__014: |
### foo\
-06_13__inlines__hard_line_breaks__015: "### foo \n"
-06_14__inlines__soft_line_breaks__001: |
+06_13_00__inlines__hard_line_breaks__015: "### foo \n"
+06_14_00__inlines__soft_line_breaks__001: |
foo
baz
-06_14__inlines__soft_line_breaks__002: "foo \n baz\n"
-06_15__inlines__textual_content__001: |
+06_14_00__inlines__soft_line_breaks__002: "foo \n baz\n"
+06_15_00__inlines__textual_content__001: |
hello $.;'there
-06_15__inlines__textual_content__002: |
+06_15_00__inlines__textual_content__002: |
Foo χÏῆν
-06_15__inlines__textual_content__003: |
+06_15_00__inlines__textual_content__003: |
Multiple spaces
-07_01__gitlab_specific_markdown__footnotes__001: |
+07_01_00__gitlab_specific_markdown__footnotes__001: |
footnote reference tag [^fortytwo]
[^fortytwo]: footnote text
-07_02__gitlab_specific_markdown__task_list_items__001: |
+07_02_00__gitlab_specific_markdown__task_list_items__001: |
- [ ] incomplete
-07_02__gitlab_specific_markdown__task_list_items__002: |
+07_02_00__gitlab_specific_markdown__task_list_items__002: |
- [x] completed
-07_02__gitlab_specific_markdown__task_list_items__003: |
+07_02_00__gitlab_specific_markdown__task_list_items__003: |
- [~] inapplicable
-07_02__gitlab_specific_markdown__task_list_items__004: |
+07_02_00__gitlab_specific_markdown__task_list_items__004: |
- [~] inapplicable
text in loose list
-07_03__gitlab_specific_markdown__front_matter__001: |
+07_03_00__gitlab_specific_markdown__front_matter__001: |
---
title: YAML front matter
---
-07_03__gitlab_specific_markdown__front_matter__002: |
+07_03_00__gitlab_specific_markdown__front_matter__002: |
+++
title: TOML front matter
+++
-07_03__gitlab_specific_markdown__front_matter__003: |
+07_03_00__gitlab_specific_markdown__front_matter__003: |
;;;
{
"title": "JSON front matter"
}
;;;
-07_03__gitlab_specific_markdown__front_matter__004: |
+07_03_00__gitlab_specific_markdown__front_matter__004: |
text
---
title: YAML front matter
---
-07_03__gitlab_specific_markdown__front_matter__005: |2
+07_03_00__gitlab_specific_markdown__front_matter__005: |2
---
title: YAML front matter
---
+07_04_00__gitlab_specific_markdown__audio__001: |
+ ![audio](audio.oga "audio title")
+07_04_00__gitlab_specific_markdown__audio__002: |
+ [audio]: audio.oga "audio title"
+
+ ![audio][audio]
+07_05_00__gitlab_specific_markdown__video__001: |
+ ![video](video.m4v "video title")
+07_05_00__gitlab_specific_markdown__video__002: |
+ [video]: video.mov "video title"
+
+ ![video][video]
+07_06_00__gitlab_specific_markdown__table_of_contents__001: |
+ [TOC]
+
+ # Heading 1
+
+ ## Heading 2
+07_06_00__gitlab_specific_markdown__table_of_contents__002: |
+ [[_TOC_]]
+
+ # Heading 1
+
+ ## Heading 2
+07_06_00__gitlab_specific_markdown__table_of_contents__003: |
+ [[_TOC_]]
+ text
+
+ text
+ [TOC]
+07_06_00__gitlab_specific_markdown__table_of_contents__004: |2
+ [[_TOC_]]
+
+ # Heading 1
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__001: |
+ [groups-test-file](/uploads/groups-test-file)
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__002: |
+ [projects-test-file](projects-test-file)
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__003: |
+ This project snippet ID reference IS filtered: $88888
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__004: |
+ This personal snippet ID reference is not filtered: $99999
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__005: |
+ [project-wikis-test-file](project-wikis-test-file)
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__006: |
+ [group-wikis-test-file](group-wikis-test-file)
diff --git a/glfm_specification/example_snapshots/prosemirror_json.yml b/glfm_specification/example_snapshots/prosemirror_json.yml
index 9f0d462aed3..1051b5ccdd7 100644
--- a/glfm_specification/example_snapshots/prosemirror_json.yml
+++ b/glfm_specification/example_snapshots/prosemirror_json.yml
@@ -1,5 +1,5 @@
---
-02_01__preliminaries__tabs__001: |-
+02_01_00__preliminaries__tabs__001: |-
{
"type": "doc",
"content": [
@@ -18,7 +18,7 @@
}
]
}
-02_01__preliminaries__tabs__002: |-
+02_01_00__preliminaries__tabs__002: |-
{
"type": "doc",
"content": [
@@ -37,7 +37,7 @@
}
]
}
-02_01__preliminaries__tabs__003: |-
+02_01_00__preliminaries__tabs__003: |-
{
"type": "doc",
"content": [
@@ -56,7 +56,7 @@
}
]
}
-02_01__preliminaries__tabs__004: |-
+02_01_00__preliminaries__tabs__004: |-
{
"type": "doc",
"content": [
@@ -93,7 +93,7 @@
}
]
}
-02_01__preliminaries__tabs__005: |-
+02_01_00__preliminaries__tabs__005: |-
{
"type": "doc",
"content": [
@@ -134,7 +134,7 @@
}
]
}
-02_01__preliminaries__tabs__006: |-
+02_01_00__preliminaries__tabs__006: |-
{
"type": "doc",
"content": [
@@ -161,7 +161,7 @@
}
]
}
-02_01__preliminaries__tabs__007: |-
+02_01_00__preliminaries__tabs__007: |-
{
"type": "doc",
"content": [
@@ -196,7 +196,7 @@
}
]
}
-02_01__preliminaries__tabs__008: |-
+02_01_00__preliminaries__tabs__008: |-
{
"type": "doc",
"content": [
@@ -215,7 +215,7 @@
}
]
}
-02_01__preliminaries__tabs__009: |-
+02_01_00__preliminaries__tabs__009: |-
{
"type": "doc",
"content": [
@@ -287,7 +287,7 @@
}
]
}
-02_01__preliminaries__tabs__010: |-
+02_01_00__preliminaries__tabs__010: |-
{
"type": "doc",
"content": [
@@ -305,7 +305,7 @@
}
]
}
-02_01__preliminaries__tabs__011: |-
+02_01_00__preliminaries__tabs__011: |-
{
"type": "doc",
"content": [
@@ -314,7 +314,7 @@
}
]
}
-03_01__blocks_and_inlines__precedence__001: |-
+03_01_00__blocks_and_inlines__precedence__001: |-
{
"type": "doc",
"content": [
@@ -356,7 +356,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__001: |-
+04_01_00__leaf_blocks__thematic_breaks__001: |-
{
"type": "doc",
"content": [
@@ -371,7 +371,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__002: |-
+04_01_00__leaf_blocks__thematic_breaks__002: |-
{
"type": "doc",
"content": [
@@ -386,7 +386,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__003: |-
+04_01_00__leaf_blocks__thematic_breaks__003: |-
{
"type": "doc",
"content": [
@@ -401,7 +401,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__004: |-
+04_01_00__leaf_blocks__thematic_breaks__004: |-
{
"type": "doc",
"content": [
@@ -416,7 +416,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__005: |-
+04_01_00__leaf_blocks__thematic_breaks__005: |-
{
"type": "doc",
"content": [
@@ -431,7 +431,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__006: |-
+04_01_00__leaf_blocks__thematic_breaks__006: |-
{
"type": "doc",
"content": [
@@ -450,7 +450,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__007: |-
+04_01_00__leaf_blocks__thematic_breaks__007: |-
{
"type": "doc",
"content": [
@@ -465,7 +465,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__008: |-
+04_01_00__leaf_blocks__thematic_breaks__008: |-
{
"type": "doc",
"content": [
@@ -474,7 +474,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__009: |-
+04_01_00__leaf_blocks__thematic_breaks__009: |-
{
"type": "doc",
"content": [
@@ -483,7 +483,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__010: |-
+04_01_00__leaf_blocks__thematic_breaks__010: |-
{
"type": "doc",
"content": [
@@ -492,7 +492,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__011: |-
+04_01_00__leaf_blocks__thematic_breaks__011: |-
{
"type": "doc",
"content": [
@@ -501,7 +501,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__012: |-
+04_01_00__leaf_blocks__thematic_breaks__012: |-
{
"type": "doc",
"content": [
@@ -510,7 +510,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__013: |-
+04_01_00__leaf_blocks__thematic_breaks__013: |-
{
"type": "doc",
"content": [
@@ -543,7 +543,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__014: |-
+04_01_00__leaf_blocks__thematic_breaks__014: |-
{
"type": "doc",
"content": [
@@ -563,7 +563,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__015: |-
+04_01_00__leaf_blocks__thematic_breaks__015: |-
{
"type": "doc",
"content": [
@@ -616,7 +616,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__016: |-
+04_01_00__leaf_blocks__thematic_breaks__016: |-
{
"type": "doc",
"content": [
@@ -643,7 +643,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__017: |-
+04_01_00__leaf_blocks__thematic_breaks__017: |-
{
"type": "doc",
"content": [
@@ -670,7 +670,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__018: |-
+04_01_00__leaf_blocks__thematic_breaks__018: |-
{
"type": "doc",
"content": [
@@ -723,7 +723,7 @@
}
]
}
-04_01__leaf_blocks__thematic_breaks__019: |-
+04_01_00__leaf_blocks__thematic_breaks__019: |-
{
"type": "doc",
"content": [
@@ -762,7 +762,7 @@
}
]
}
-04_02__leaf_blocks__atx_headings__001: |-
+04_02_00__leaf_blocks__atx_headings__001: |-
{
"type": "doc",
"content": [
@@ -840,7 +840,7 @@
}
]
}
-04_02__leaf_blocks__atx_headings__002: |-
+04_02_00__leaf_blocks__atx_headings__002: |-
{
"type": "doc",
"content": [
@@ -855,7 +855,7 @@
}
]
}
-04_02__leaf_blocks__atx_headings__003: |-
+04_02_00__leaf_blocks__atx_headings__003: |-
{
"type": "doc",
"content": [
@@ -879,7 +879,7 @@
}
]
}
-04_02__leaf_blocks__atx_headings__004: |-
+04_02_00__leaf_blocks__atx_headings__004: |-
{
"type": "doc",
"content": [
@@ -894,7 +894,7 @@
}
]
}
-04_02__leaf_blocks__atx_headings__005: |-
+04_02_00__leaf_blocks__atx_headings__005: |-
{
"type": "doc",
"content": [
@@ -925,7 +925,7 @@
}
]
}
-04_02__leaf_blocks__atx_headings__006: |-
+04_02_00__leaf_blocks__atx_headings__006: |-
{
"type": "doc",
"content": [
@@ -943,7 +943,7 @@
}
]
}
-04_02__leaf_blocks__atx_headings__007: |-
+04_02_00__leaf_blocks__atx_headings__007: |-
{
"type": "doc",
"content": [
@@ -985,7 +985,7 @@
}
]
}
-04_02__leaf_blocks__atx_headings__008: |-
+04_02_00__leaf_blocks__atx_headings__008: |-
{
"type": "doc",
"content": [
@@ -1004,7 +1004,7 @@
}
]
}
-04_02__leaf_blocks__atx_headings__009: |-
+04_02_00__leaf_blocks__atx_headings__009: |-
{
"type": "doc",
"content": [
@@ -1019,7 +1019,7 @@
}
]
}
-04_02__leaf_blocks__atx_headings__010: |-
+04_02_00__leaf_blocks__atx_headings__010: |-
{
"type": "doc",
"content": [
@@ -1049,7 +1049,7 @@
}
]
}
-04_02__leaf_blocks__atx_headings__011: |-
+04_02_00__leaf_blocks__atx_headings__011: |-
{
"type": "doc",
"content": [
@@ -1079,7 +1079,7 @@
}
]
}
-04_02__leaf_blocks__atx_headings__012: |-
+04_02_00__leaf_blocks__atx_headings__012: |-
{
"type": "doc",
"content": [
@@ -1097,7 +1097,7 @@
}
]
}
-04_02__leaf_blocks__atx_headings__013: |-
+04_02_00__leaf_blocks__atx_headings__013: |-
{
"type": "doc",
"content": [
@@ -1115,7 +1115,7 @@
}
]
}
-04_02__leaf_blocks__atx_headings__014: |-
+04_02_00__leaf_blocks__atx_headings__014: |-
{
"type": "doc",
"content": [
@@ -1133,7 +1133,7 @@
}
]
}
-04_02__leaf_blocks__atx_headings__015: |-
+04_02_00__leaf_blocks__atx_headings__015: |-
{
"type": "doc",
"content": [
@@ -1175,7 +1175,7 @@
}
]
}
-04_02__leaf_blocks__atx_headings__016: |-
+04_02_00__leaf_blocks__atx_headings__016: |-
{
"type": "doc",
"content": [
@@ -1199,7 +1199,7 @@
}
]
}
-04_02__leaf_blocks__atx_headings__017: |-
+04_02_00__leaf_blocks__atx_headings__017: |-
{
"type": "doc",
"content": [
@@ -1235,7 +1235,7 @@
}
]
}
-04_02__leaf_blocks__atx_headings__018: |-
+04_02_00__leaf_blocks__atx_headings__018: |-
{
"type": "doc",
"content": [
@@ -1259,7 +1259,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__001: |-
+04_03_00__leaf_blocks__setext_headings__001: |-
{
"type": "doc",
"content": [
@@ -1307,7 +1307,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__002: |-
+04_03_00__leaf_blocks__setext_headings__002: |-
{
"type": "doc",
"content": [
@@ -1334,7 +1334,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__003: |-
+04_03_00__leaf_blocks__setext_headings__003: |-
{
"type": "doc",
"content": [
@@ -1361,7 +1361,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__004: |-
+04_03_00__leaf_blocks__setext_headings__004: |-
{
"type": "doc",
"content": [
@@ -1391,7 +1391,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__005: |-
+04_03_00__leaf_blocks__setext_headings__005: |-
{
"type": "doc",
"content": [
@@ -1433,7 +1433,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__006: |-
+04_03_00__leaf_blocks__setext_headings__006: |-
{
"type": "doc",
"content": [
@@ -1455,7 +1455,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__007: |-
+04_03_00__leaf_blocks__setext_headings__007: |-
{
"type": "doc",
"content": [
@@ -1473,7 +1473,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__008: |-
+04_03_00__leaf_blocks__setext_headings__008: |-
{
"type": "doc",
"content": [
@@ -1488,7 +1488,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__009: |-
+04_03_00__leaf_blocks__setext_headings__009: |-
{
"type": "doc",
"content": [
@@ -1515,7 +1515,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__010: |-
+04_03_00__leaf_blocks__setext_headings__010: |-
{
"type": "doc",
"content": [
@@ -1533,7 +1533,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__011: |-
+04_03_00__leaf_blocks__setext_headings__011: |-
{
"type": "doc",
"content": [
@@ -1551,7 +1551,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__012: |-
+04_03_00__leaf_blocks__setext_headings__012: |-
{
"type": "doc",
"content": [
@@ -1599,7 +1599,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__013: |-
+04_03_00__leaf_blocks__setext_headings__013: |-
{
"type": "doc",
"content": [
@@ -1625,7 +1625,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__014: |-
+04_03_00__leaf_blocks__setext_headings__014: |-
{
"type": "doc",
"content": [
@@ -1648,7 +1648,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__015: |-
+04_03_00__leaf_blocks__setext_headings__015: |-
{
"type": "doc",
"content": [
@@ -1679,7 +1679,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__016: |-
+04_03_00__leaf_blocks__setext_headings__016: |-
{
"type": "doc",
"content": [
@@ -1697,7 +1697,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__017: |-
+04_03_00__leaf_blocks__setext_headings__017: |-
{
"type": "doc",
"content": [
@@ -1738,7 +1738,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__018: |-
+04_03_00__leaf_blocks__setext_headings__018: |-
{
"type": "doc",
"content": [
@@ -1753,7 +1753,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__019: |-
+04_03_00__leaf_blocks__setext_headings__019: |-
{
"type": "doc",
"content": [
@@ -1767,7 +1767,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__020: |-
+04_03_00__leaf_blocks__setext_headings__020: |-
{
"type": "doc",
"content": [
@@ -1798,7 +1798,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__021: |-
+04_03_00__leaf_blocks__setext_headings__021: |-
{
"type": "doc",
"content": [
@@ -1820,7 +1820,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__022: |-
+04_03_00__leaf_blocks__setext_headings__022: |-
{
"type": "doc",
"content": [
@@ -1846,7 +1846,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__023: |-
+04_03_00__leaf_blocks__setext_headings__023: |-
{
"type": "doc",
"content": [
@@ -1864,7 +1864,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__024: |-
+04_03_00__leaf_blocks__setext_headings__024: |-
{
"type": "doc",
"content": [
@@ -1900,7 +1900,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__025: |-
+04_03_00__leaf_blocks__setext_headings__025: |-
{
"type": "doc",
"content": [
@@ -1927,7 +1927,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__026: |-
+04_03_00__leaf_blocks__setext_headings__026: |-
{
"type": "doc",
"content": [
@@ -1954,7 +1954,7 @@
}
]
}
-04_03__leaf_blocks__setext_headings__027: |-
+04_03_00__leaf_blocks__setext_headings__027: |-
{
"type": "doc",
"content": [
@@ -1969,7 +1969,7 @@
}
]
}
-04_04__leaf_blocks__indented_code_blocks__001: |-
+04_04_00__leaf_blocks__indented_code_blocks__001: |-
{
"type": "doc",
"content": [
@@ -1988,7 +1988,7 @@
}
]
}
-04_04__leaf_blocks__indented_code_blocks__002: |-
+04_04_00__leaf_blocks__indented_code_blocks__002: |-
{
"type": "doc",
"content": [
@@ -2025,7 +2025,7 @@
}
]
}
-04_04__leaf_blocks__indented_code_blocks__003: |-
+04_04_00__leaf_blocks__indented_code_blocks__003: |-
{
"type": "doc",
"content": [
@@ -2076,7 +2076,7 @@
}
]
}
-04_04__leaf_blocks__indented_code_blocks__004: |-
+04_04_00__leaf_blocks__indented_code_blocks__004: |-
{
"type": "doc",
"content": [
@@ -2095,7 +2095,7 @@
}
]
}
-04_04__leaf_blocks__indented_code_blocks__005: |-
+04_04_00__leaf_blocks__indented_code_blocks__005: |-
{
"type": "doc",
"content": [
@@ -2114,7 +2114,7 @@
}
]
}
-04_04__leaf_blocks__indented_code_blocks__006: |-
+04_04_00__leaf_blocks__indented_code_blocks__006: |-
{
"type": "doc",
"content": [
@@ -2133,7 +2133,7 @@
}
]
}
-04_04__leaf_blocks__indented_code_blocks__007: |-
+04_04_00__leaf_blocks__indented_code_blocks__007: |-
{
"type": "doc",
"content": [
@@ -2148,7 +2148,7 @@
}
]
}
-04_04__leaf_blocks__indented_code_blocks__008: |-
+04_04_00__leaf_blocks__indented_code_blocks__008: |-
{
"type": "doc",
"content": [
@@ -2176,7 +2176,7 @@
}
]
}
-04_04__leaf_blocks__indented_code_blocks__009: |-
+04_04_00__leaf_blocks__indented_code_blocks__009: |-
{
"type": "doc",
"content": [
@@ -2235,7 +2235,7 @@
}
]
}
-04_04__leaf_blocks__indented_code_blocks__010: |-
+04_04_00__leaf_blocks__indented_code_blocks__010: |-
{
"type": "doc",
"content": [
@@ -2254,7 +2254,7 @@
}
]
}
-04_04__leaf_blocks__indented_code_blocks__011: |-
+04_04_00__leaf_blocks__indented_code_blocks__011: |-
{
"type": "doc",
"content": [
@@ -2273,7 +2273,7 @@
}
]
}
-04_04__leaf_blocks__indented_code_blocks__012: |-
+04_04_00__leaf_blocks__indented_code_blocks__012: |-
{
"type": "doc",
"content": [
@@ -2292,7 +2292,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__001: |-
+04_05_00__leaf_blocks__fenced_code_blocks__001: |-
{
"type": "doc",
"content": [
@@ -2311,7 +2311,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__002: |-
+04_05_00__leaf_blocks__fenced_code_blocks__002: |-
{
"type": "doc",
"content": [
@@ -2330,7 +2330,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__003: |-
+04_05_00__leaf_blocks__fenced_code_blocks__003: |-
{
"type": "doc",
"content": [
@@ -2350,7 +2350,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__004: |-
+04_05_00__leaf_blocks__fenced_code_blocks__004: |-
{
"type": "doc",
"content": [
@@ -2369,7 +2369,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__005: |-
+04_05_00__leaf_blocks__fenced_code_blocks__005: |-
{
"type": "doc",
"content": [
@@ -2388,7 +2388,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__006: |-
+04_05_00__leaf_blocks__fenced_code_blocks__006: |-
{
"type": "doc",
"content": [
@@ -2407,7 +2407,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__007: |-
+04_05_00__leaf_blocks__fenced_code_blocks__007: |-
{
"type": "doc",
"content": [
@@ -2426,7 +2426,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__008: |-
+04_05_00__leaf_blocks__fenced_code_blocks__008: |-
{
"type": "doc",
"content": [
@@ -2439,7 +2439,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__009: |-
+04_05_00__leaf_blocks__fenced_code_blocks__009: |-
{
"type": "doc",
"content": [
@@ -2458,7 +2458,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__010: |-
+04_05_00__leaf_blocks__fenced_code_blocks__010: |-
{
"type": "doc",
"content": [
@@ -2494,7 +2494,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__011: |-
+04_05_00__leaf_blocks__fenced_code_blocks__011: |-
{
"type": "doc",
"content": [
@@ -2507,7 +2507,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__012: |-
+04_05_00__leaf_blocks__fenced_code_blocks__012: |-
{
"type": "doc",
"content": [
@@ -2520,7 +2520,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__013: |-
+04_05_00__leaf_blocks__fenced_code_blocks__013: |-
{
"type": "doc",
"content": [
@@ -2539,7 +2539,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__014: |-
+04_05_00__leaf_blocks__fenced_code_blocks__014: |-
{
"type": "doc",
"content": [
@@ -2558,7 +2558,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__015: |-
+04_05_00__leaf_blocks__fenced_code_blocks__015: |-
{
"type": "doc",
"content": [
@@ -2577,7 +2577,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__016: |-
+04_05_00__leaf_blocks__fenced_code_blocks__016: |-
{
"type": "doc",
"content": [
@@ -2596,7 +2596,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__017: |-
+04_05_00__leaf_blocks__fenced_code_blocks__017: |-
{
"type": "doc",
"content": [
@@ -2615,7 +2615,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__018: |-
+04_05_00__leaf_blocks__fenced_code_blocks__018: |-
{
"type": "doc",
"content": [
@@ -2634,7 +2634,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__019: |-
+04_05_00__leaf_blocks__fenced_code_blocks__019: |-
{
"type": "doc",
"content": [
@@ -2653,7 +2653,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__020: |-
+04_05_00__leaf_blocks__fenced_code_blocks__020: |-
{
"type": "doc",
"content": [
@@ -2668,7 +2668,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__021: |-
+04_05_00__leaf_blocks__fenced_code_blocks__021: |-
{
"type": "doc",
"content": [
@@ -2687,7 +2687,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__022: |-
+04_05_00__leaf_blocks__fenced_code_blocks__022: |-
{
"type": "doc",
"content": [
@@ -2724,7 +2724,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__023: |-
+04_05_00__leaf_blocks__fenced_code_blocks__023: |-
{
"type": "doc",
"content": [
@@ -2767,7 +2767,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__024: |-
+04_05_00__leaf_blocks__fenced_code_blocks__024: |-
{
"type": "doc",
"content": [
@@ -2786,7 +2786,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__025: |-
+04_05_00__leaf_blocks__fenced_code_blocks__025: |-
{
"type": "doc",
"content": [
@@ -2805,7 +2805,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__026: |-
+04_05_00__leaf_blocks__fenced_code_blocks__026: |-
{
"type": "doc",
"content": [
@@ -2818,7 +2818,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__027: |-
+04_05_00__leaf_blocks__fenced_code_blocks__027: |-
{
"type": "doc",
"content": [
@@ -2842,7 +2842,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__028: |-
+04_05_00__leaf_blocks__fenced_code_blocks__028: |-
{
"type": "doc",
"content": [
@@ -2861,7 +2861,7 @@
}
]
}
-04_05__leaf_blocks__fenced_code_blocks__029: |-
+04_05_00__leaf_blocks__fenced_code_blocks__029: |-
{
"type": "doc",
"content": [
@@ -2880,7 +2880,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__001: |-
+04_06_00__leaf_blocks__html_blocks__001: |-
{
"type": "doc",
"content": [
@@ -2944,7 +2944,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__002: |-
+04_06_00__leaf_blocks__html_blocks__002: |-
{
"type": "doc",
"content": [
@@ -2991,7 +2991,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__003: |-
+04_06_00__leaf_blocks__html_blocks__003: |-
{
"type": "doc",
"content": [
@@ -3011,7 +3011,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__004: |-
+04_06_00__leaf_blocks__html_blocks__004: |-
{
"type": "doc",
"content": [
@@ -3026,7 +3026,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__005: |-
+04_06_00__leaf_blocks__html_blocks__005: |-
{
"type": "doc",
"content": [
@@ -3051,7 +3051,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__006: |-
+04_06_00__leaf_blocks__html_blocks__006: |-
{
"type": "doc",
"content": [
@@ -3060,7 +3060,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__007: |-
+04_06_00__leaf_blocks__html_blocks__007: |-
{
"type": "doc",
"content": [
@@ -3069,7 +3069,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__008: |-
+04_06_00__leaf_blocks__html_blocks__008: |-
{
"type": "doc",
"content": [
@@ -3103,7 +3103,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__009: |-
+04_06_00__leaf_blocks__html_blocks__009: |-
{
"type": "doc",
"content": [
@@ -3112,7 +3112,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__010: |-
+04_06_00__leaf_blocks__html_blocks__010: |-
{
"type": "doc",
"content": [
@@ -3121,7 +3121,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__011: |-
+04_06_00__leaf_blocks__html_blocks__011: |-
{
"type": "doc",
"content": [
@@ -3130,7 +3130,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__012: |-
+04_06_00__leaf_blocks__html_blocks__012: |-
{
"type": "doc",
"content": [
@@ -3163,7 +3163,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__013: |-
+04_06_00__leaf_blocks__html_blocks__013: |-
{
"type": "doc",
"content": [
@@ -3201,7 +3201,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__014: |-
+04_06_00__leaf_blocks__html_blocks__014: |-
{
"type": "doc",
"content": [
@@ -3219,7 +3219,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__015: |-
+04_06_00__leaf_blocks__html_blocks__015: |-
{
"type": "doc",
"content": [
@@ -3247,7 +3247,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__016: |-
+04_06_00__leaf_blocks__html_blocks__016: |-
{
"type": "doc",
"content": [
@@ -3256,7 +3256,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__017: |-
+04_06_00__leaf_blocks__html_blocks__017: |-
{
"type": "doc",
"content": [
@@ -3276,7 +3276,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__018: |-
+04_06_00__leaf_blocks__html_blocks__018: |-
{
"type": "doc",
"content": [
@@ -3291,7 +3291,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__019: |-
+04_06_00__leaf_blocks__html_blocks__019: |-
{
"type": "doc",
"content": [
@@ -3311,7 +3311,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__020: |-
+04_06_00__leaf_blocks__html_blocks__020: |-
{
"type": "doc",
"content": [
@@ -3334,7 +3334,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__021: |-
+04_06_00__leaf_blocks__html_blocks__021: |-
{
"type": "doc",
"content": [
@@ -3357,7 +3357,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__022: |-
+04_06_00__leaf_blocks__html_blocks__022: |-
{
"type": "doc",
"content": [
@@ -3391,7 +3391,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__023: |-
+04_06_00__leaf_blocks__html_blocks__023: |-
{
"type": "doc",
"content": [
@@ -3406,7 +3406,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__024: |-
+04_06_00__leaf_blocks__html_blocks__024: |-
{
"type": "doc",
"content": [
@@ -3421,7 +3421,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__025: |-
+04_06_00__leaf_blocks__html_blocks__025: |-
{
"type": "doc",
"content": [
@@ -3430,7 +3430,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__026: |-
+04_06_00__leaf_blocks__html_blocks__026: |-
{
"type": "doc",
"content": [
@@ -3467,7 +3467,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__027: |-
+04_06_00__leaf_blocks__html_blocks__027: |-
{
"type": "doc",
"content": [
@@ -3506,7 +3506,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__028: |-
+04_06_00__leaf_blocks__html_blocks__028: |-
{
"type": "doc",
"content": [
@@ -3526,7 +3526,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__029: |-
+04_06_00__leaf_blocks__html_blocks__029: |-
{
"type": "doc",
"content": [
@@ -3555,7 +3555,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__030: |-
+04_06_00__leaf_blocks__html_blocks__030: |-
{
"type": "doc",
"content": [
@@ -3570,7 +3570,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__031: |-
+04_06_00__leaf_blocks__html_blocks__031: |-
{
"type": "doc",
"content": [
@@ -3585,7 +3585,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__032: |-
+04_06_00__leaf_blocks__html_blocks__032: |-
{
"type": "doc",
"content": [
@@ -3609,7 +3609,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__033: |-
+04_06_00__leaf_blocks__html_blocks__033: |-
{
"type": "doc",
"content": [
@@ -3618,7 +3618,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__034: |-
+04_06_00__leaf_blocks__html_blocks__034: |-
{
"type": "doc",
"content": [
@@ -3633,7 +3633,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__035: |-
+04_06_00__leaf_blocks__html_blocks__035: |-
{
"type": "doc",
"content": [
@@ -3652,7 +3652,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__036: |-
+04_06_00__leaf_blocks__html_blocks__036: |-
{
"type": "doc",
"content": [
@@ -3676,7 +3676,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__037: |-
+04_06_00__leaf_blocks__html_blocks__037: |-
{
"type": "doc",
"content": [
@@ -3705,7 +3705,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__038: |-
+04_06_00__leaf_blocks__html_blocks__038: |-
{
"type": "doc",
"content": [
@@ -3734,7 +3734,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__039: |-
+04_06_00__leaf_blocks__html_blocks__039: |-
{
"type": "doc",
"content": [
@@ -3766,7 +3766,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__040: |-
+04_06_00__leaf_blocks__html_blocks__040: |-
{
"type": "doc",
"content": [
@@ -3795,7 +3795,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__041: |-
+04_06_00__leaf_blocks__html_blocks__041: |-
{
"type": "doc",
"content": [
@@ -3815,7 +3815,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__042: |-
+04_06_00__leaf_blocks__html_blocks__042: |-
{
"type": "doc",
"content": [
@@ -3853,7 +3853,7 @@
}
]
}
-04_06__leaf_blocks__html_blocks__043: |-
+04_06_00__leaf_blocks__html_blocks__043: |-
{
"type": "doc",
"content": [
@@ -3883,7 +3883,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__001: |-
+04_07_00__leaf_blocks__link_reference_definitions__001: |-
{
"type": "doc",
"content": [
@@ -3925,7 +3925,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__002: |-
+04_07_00__leaf_blocks__link_reference_definitions__002: |-
{
"type": "doc",
"content": [
@@ -3967,7 +3967,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__003: |-
+04_07_00__leaf_blocks__link_reference_definitions__003: |-
{
"type": "doc",
"content": [
@@ -4009,7 +4009,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__004: |-
+04_07_00__leaf_blocks__link_reference_definitions__004: |-
{
"type": "doc",
"content": [
@@ -4051,7 +4051,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__005: |-
+04_07_00__leaf_blocks__link_reference_definitions__005: |-
{
"type": "doc",
"content": [
@@ -4093,7 +4093,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__006: |-
+04_07_00__leaf_blocks__link_reference_definitions__006: |-
{
"type": "doc",
"content": [
@@ -4126,7 +4126,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__007: |-
+04_07_00__leaf_blocks__link_reference_definitions__007: |-
{
"type": "doc",
"content": [
@@ -4168,7 +4168,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__008: |-
+04_07_00__leaf_blocks__link_reference_definitions__008: |-
{
"type": "doc",
"content": [
@@ -4192,7 +4192,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__009: |-
+04_07_00__leaf_blocks__link_reference_definitions__009: |-
{
"type": "doc",
"content": [
@@ -4234,7 +4234,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__010: |-
+04_07_00__leaf_blocks__link_reference_definitions__010: |-
{
"type": "doc",
"content": [
@@ -4258,7 +4258,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__011: |-
+04_07_00__leaf_blocks__link_reference_definitions__011: |-
{
"type": "doc",
"content": [
@@ -4300,7 +4300,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__012: |-
+04_07_00__leaf_blocks__link_reference_definitions__012: |-
{
"type": "doc",
"content": [
@@ -4342,7 +4342,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__013: |-
+04_07_00__leaf_blocks__link_reference_definitions__013: |-
{
"type": "doc",
"content": [
@@ -4398,7 +4398,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__014: |-
+04_07_00__leaf_blocks__link_reference_definitions__014: |-
{
"type": "doc",
"content": [
@@ -4440,7 +4440,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__015: |-
+04_07_00__leaf_blocks__link_reference_definitions__015: |-
{
"type": "doc",
"content": [
@@ -4482,7 +4482,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__016: |-
+04_07_00__leaf_blocks__link_reference_definitions__016: |-
{
"type": "doc",
"content": [
@@ -4502,7 +4502,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__017: |-
+04_07_00__leaf_blocks__link_reference_definitions__017: |-
{
"type": "doc",
"content": [
@@ -4531,7 +4531,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__018: |-
+04_07_00__leaf_blocks__link_reference_definitions__018: |-
{
"type": "doc",
"content": [
@@ -4546,7 +4546,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__019: |-
+04_07_00__leaf_blocks__link_reference_definitions__019: |-
{
"type": "doc",
"content": [
@@ -4575,7 +4575,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__020: |-
+04_07_00__leaf_blocks__link_reference_definitions__020: |-
{
"type": "doc",
"content": [
@@ -4603,7 +4603,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__021: |-
+04_07_00__leaf_blocks__link_reference_definitions__021: |-
{
"type": "doc",
"content": [
@@ -4631,7 +4631,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__022: |-
+04_07_00__leaf_blocks__link_reference_definitions__022: |-
{
"type": "doc",
"content": [
@@ -4655,7 +4655,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__023: |-
+04_07_00__leaf_blocks__link_reference_definitions__023: |-
{
"type": "doc",
"content": [
@@ -4717,7 +4717,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__024: |-
+04_07_00__leaf_blocks__link_reference_definitions__024: |-
{
"type": "doc",
"content": [
@@ -4771,7 +4771,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__025: |-
+04_07_00__leaf_blocks__link_reference_definitions__025: |-
{
"type": "doc",
"content": [
@@ -4817,7 +4817,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__026: |-
+04_07_00__leaf_blocks__link_reference_definitions__026: |-
{
"type": "doc",
"content": [
@@ -4929,7 +4929,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__027: |-
+04_07_00__leaf_blocks__link_reference_definitions__027: |-
{
"type": "doc",
"content": [
@@ -4979,7 +4979,7 @@
}
]
}
-04_07__leaf_blocks__link_reference_definitions__028: |-
+04_07_00__leaf_blocks__link_reference_definitions__028: |-
{
"type": "doc",
"content": [
@@ -4999,7 +4999,7 @@
}
]
}
-04_08__leaf_blocks__paragraphs__001: |-
+04_08_00__leaf_blocks__paragraphs__001: |-
{
"type": "doc",
"content": [
@@ -5023,7 +5023,7 @@
}
]
}
-04_08__leaf_blocks__paragraphs__002: |-
+04_08_00__leaf_blocks__paragraphs__002: |-
{
"type": "doc",
"content": [
@@ -5047,7 +5047,7 @@
}
]
}
-04_08__leaf_blocks__paragraphs__003: |-
+04_08_00__leaf_blocks__paragraphs__003: |-
{
"type": "doc",
"content": [
@@ -5071,7 +5071,7 @@
}
]
}
-04_08__leaf_blocks__paragraphs__004: |-
+04_08_00__leaf_blocks__paragraphs__004: |-
{
"type": "doc",
"content": [
@@ -5086,7 +5086,7 @@
}
]
}
-04_08__leaf_blocks__paragraphs__005: |-
+04_08_00__leaf_blocks__paragraphs__005: |-
{
"type": "doc",
"content": [
@@ -5101,7 +5101,7 @@
}
]
}
-04_08__leaf_blocks__paragraphs__006: |-
+04_08_00__leaf_blocks__paragraphs__006: |-
{
"type": "doc",
"content": [
@@ -5116,7 +5116,7 @@
}
]
}
-04_08__leaf_blocks__paragraphs__007: |-
+04_08_00__leaf_blocks__paragraphs__007: |-
{
"type": "doc",
"content": [
@@ -5144,7 +5144,7 @@
}
]
}
-04_08__leaf_blocks__paragraphs__008: |-
+04_08_00__leaf_blocks__paragraphs__008: |-
{
"type": "doc",
"content": [
@@ -5166,7 +5166,7 @@
}
]
}
-04_09__leaf_blocks__blank_lines__001: |-
+04_09_00__leaf_blocks__blank_lines__001: |-
{
"type": "doc",
"content": [
@@ -5193,7 +5193,7 @@
}
]
}
-04_10__leaf_blocks__tables_extension__001: |-
+04_10_00__leaf_blocks__tables_extension__001: |-
{
"type": "doc",
"content": [
@@ -5293,7 +5293,7 @@
}
]
}
-04_10__leaf_blocks__tables_extension__002: |-
+04_10_00__leaf_blocks__tables_extension__002: |-
{
"type": "doc",
"content": [
@@ -5393,7 +5393,7 @@
}
]
}
-04_10__leaf_blocks__tables_extension__003: |-
+04_10_00__leaf_blocks__tables_extension__003: |-
{
"type": "doc",
"content": [
@@ -5505,7 +5505,7 @@
}
]
}
-04_10__leaf_blocks__tables_extension__004: |-
+04_10_00__leaf_blocks__tables_extension__004: |-
{
"type": "doc",
"content": [
@@ -5622,7 +5622,7 @@
}
]
}
-04_10__leaf_blocks__tables_extension__005: |-
+04_10_00__leaf_blocks__tables_extension__005: |-
{
"type": "doc",
"content": [
@@ -5768,7 +5768,7 @@
}
]
}
-04_10__leaf_blocks__tables_extension__006: |-
+04_10_00__leaf_blocks__tables_extension__006: |-
{
"type": "doc",
"content": [
@@ -5783,7 +5783,7 @@
}
]
}
-04_10__leaf_blocks__tables_extension__007: |-
+04_10_00__leaf_blocks__tables_extension__007: |-
{
"type": "doc",
"content": [
@@ -5920,7 +5920,7 @@
}
]
}
-04_10__leaf_blocks__tables_extension__008: |-
+04_10_00__leaf_blocks__tables_extension__008: |-
{
"type": "doc",
"content": [
@@ -5977,7 +5977,7 @@
}
]
}
-05_01__container_blocks__block_quotes__001: |-
+05_01_00__container_blocks__block_quotes__001: |-
{
"type": "doc",
"content": [
@@ -6012,7 +6012,7 @@
}
]
}
-05_01__container_blocks__block_quotes__002: |-
+05_01_00__container_blocks__block_quotes__002: |-
{
"type": "doc",
"content": [
@@ -6047,7 +6047,7 @@
}
]
}
-05_01__container_blocks__block_quotes__003: |-
+05_01_00__container_blocks__block_quotes__003: |-
{
"type": "doc",
"content": [
@@ -6082,7 +6082,7 @@
}
]
}
-05_01__container_blocks__block_quotes__004: |-
+05_01_00__container_blocks__block_quotes__004: |-
{
"type": "doc",
"content": [
@@ -6101,7 +6101,7 @@
}
]
}
-05_01__container_blocks__block_quotes__005: |-
+05_01_00__container_blocks__block_quotes__005: |-
{
"type": "doc",
"content": [
@@ -6136,7 +6136,7 @@
}
]
}
-05_01__container_blocks__block_quotes__006: |-
+05_01_00__container_blocks__block_quotes__006: |-
{
"type": "doc",
"content": [
@@ -6159,7 +6159,7 @@
}
]
}
-05_01__container_blocks__block_quotes__007: |-
+05_01_00__container_blocks__block_quotes__007: |-
{
"type": "doc",
"content": [
@@ -6185,7 +6185,7 @@
}
]
}
-05_01__container_blocks__block_quotes__008: |-
+05_01_00__container_blocks__block_quotes__008: |-
{
"type": "doc",
"content": [
@@ -6243,7 +6243,7 @@
}
]
}
-05_01__container_blocks__block_quotes__009: |-
+05_01_00__container_blocks__block_quotes__009: |-
{
"type": "doc",
"content": [
@@ -6283,7 +6283,7 @@
}
]
}
-05_01__container_blocks__block_quotes__010: |-
+05_01_00__container_blocks__block_quotes__010: |-
{
"type": "doc",
"content": [
@@ -6320,7 +6320,7 @@
}
]
}
-05_01__container_blocks__block_quotes__011: |-
+05_01_00__container_blocks__block_quotes__011: |-
{
"type": "doc",
"content": [
@@ -6343,7 +6343,7 @@
}
]
}
-05_01__container_blocks__block_quotes__012: |-
+05_01_00__container_blocks__block_quotes__012: |-
{
"type": "doc",
"content": [
@@ -6360,7 +6360,7 @@
}
]
}
-05_01__container_blocks__block_quotes__013: |-
+05_01_00__container_blocks__block_quotes__013: |-
{
"type": "doc",
"content": [
@@ -6377,7 +6377,7 @@
}
]
}
-05_01__container_blocks__block_quotes__014: |-
+05_01_00__container_blocks__block_quotes__014: |-
{
"type": "doc",
"content": [
@@ -6400,7 +6400,7 @@
}
]
}
-05_01__container_blocks__block_quotes__015: |-
+05_01_00__container_blocks__block_quotes__015: |-
{
"type": "doc",
"content": [
@@ -6440,7 +6440,7 @@
}
]
}
-05_01__container_blocks__block_quotes__016: |-
+05_01_00__container_blocks__block_quotes__016: |-
{
"type": "doc",
"content": [
@@ -6463,7 +6463,7 @@
}
]
}
-05_01__container_blocks__block_quotes__017: |-
+05_01_00__container_blocks__block_quotes__017: |-
{
"type": "doc",
"content": [
@@ -6495,7 +6495,7 @@
}
]
}
-05_01__container_blocks__block_quotes__018: |-
+05_01_00__container_blocks__block_quotes__018: |-
{
"type": "doc",
"content": [
@@ -6527,7 +6527,7 @@
}
]
}
-05_01__container_blocks__block_quotes__019: |-
+05_01_00__container_blocks__block_quotes__019: |-
{
"type": "doc",
"content": [
@@ -6570,7 +6570,7 @@
}
]
}
-05_01__container_blocks__block_quotes__020: |-
+05_01_00__container_blocks__block_quotes__020: |-
{
"type": "doc",
"content": [
@@ -6593,7 +6593,7 @@
}
]
}
-05_01__container_blocks__block_quotes__021: |-
+05_01_00__container_blocks__block_quotes__021: |-
{
"type": "doc",
"content": [
@@ -6625,7 +6625,7 @@
}
]
}
-05_01__container_blocks__block_quotes__022: |-
+05_01_00__container_blocks__block_quotes__022: |-
{
"type": "doc",
"content": [
@@ -6657,7 +6657,7 @@
}
]
}
-05_01__container_blocks__block_quotes__023: |-
+05_01_00__container_blocks__block_quotes__023: |-
{
"type": "doc",
"content": [
@@ -6696,7 +6696,7 @@
}
]
}
-05_01__container_blocks__block_quotes__024: |-
+05_01_00__container_blocks__block_quotes__024: |-
{
"type": "doc",
"content": [
@@ -6735,7 +6735,7 @@
}
]
}
-05_01__container_blocks__block_quotes__025: |-
+05_01_00__container_blocks__block_quotes__025: |-
{
"type": "doc",
"content": [
@@ -6779,7 +6779,7 @@
}
]
}
-05_02__container_blocks__list_items__001: |-
+05_02_00__container_blocks__list_items__001: |-
{
"type": "doc",
"content": [
@@ -6824,7 +6824,7 @@
}
]
}
-05_02__container_blocks__list_items__002: |-
+05_02_00__container_blocks__list_items__002: |-
{
"type": "doc",
"content": [
@@ -6883,7 +6883,7 @@
}
]
}
-05_02__container_blocks__list_items__003: |-
+05_02_00__container_blocks__list_items__003: |-
{
"type": "doc",
"content": [
@@ -6920,7 +6920,7 @@
}
]
}
-05_02__container_blocks__list_items__004: |-
+05_02_00__container_blocks__list_items__004: |-
{
"type": "doc",
"content": [
@@ -6957,7 +6957,7 @@
}
]
}
-05_02__container_blocks__list_items__005: |-
+05_02_00__container_blocks__list_items__005: |-
{
"type": "doc",
"content": [
@@ -6998,7 +6998,7 @@
}
]
}
-05_02__container_blocks__list_items__006: |-
+05_02_00__container_blocks__list_items__006: |-
{
"type": "doc",
"content": [
@@ -7035,7 +7035,7 @@
}
]
}
-05_02__container_blocks__list_items__007: |-
+05_02_00__container_blocks__list_items__007: |-
{
"type": "doc",
"content": [
@@ -7089,7 +7089,7 @@
}
]
}
-05_02__container_blocks__list_items__008: |-
+05_02_00__container_blocks__list_items__008: |-
{
"type": "doc",
"content": [
@@ -7142,7 +7142,7 @@
}
]
}
-05_02__container_blocks__list_items__009: |-
+05_02_00__container_blocks__list_items__009: |-
{
"type": "doc",
"content": [
@@ -7166,7 +7166,7 @@
}
]
}
-05_02__container_blocks__list_items__010: |-
+05_02_00__container_blocks__list_items__010: |-
{
"type": "doc",
"content": [
@@ -7203,7 +7203,7 @@
}
]
}
-05_02__container_blocks__list_items__011: |-
+05_02_00__container_blocks__list_items__011: |-
{
"type": "doc",
"content": [
@@ -7271,7 +7271,7 @@
}
]
}
-05_02__container_blocks__list_items__012: |-
+05_02_00__container_blocks__list_items__012: |-
{
"type": "doc",
"content": [
@@ -7312,7 +7312,7 @@
}
]
}
-05_02__container_blocks__list_items__013: |-
+05_02_00__container_blocks__list_items__013: |-
{
"type": "doc",
"content": [
@@ -7341,7 +7341,7 @@
}
]
}
-05_02__container_blocks__list_items__014: |-
+05_02_00__container_blocks__list_items__014: |-
{
"type": "doc",
"content": [
@@ -7356,7 +7356,7 @@
}
]
}
-05_02__container_blocks__list_items__015: |-
+05_02_00__container_blocks__list_items__015: |-
{
"type": "doc",
"content": [
@@ -7385,7 +7385,7 @@
}
]
}
-05_02__container_blocks__list_items__016: |-
+05_02_00__container_blocks__list_items__016: |-
{
"type": "doc",
"content": [
@@ -7414,7 +7414,7 @@
}
]
}
-05_02__container_blocks__list_items__017: |-
+05_02_00__container_blocks__list_items__017: |-
{
"type": "doc",
"content": [
@@ -7429,7 +7429,7 @@
}
]
}
-05_02__container_blocks__list_items__018: |-
+05_02_00__container_blocks__list_items__018: |-
{
"type": "doc",
"content": [
@@ -7470,7 +7470,7 @@
}
]
}
-05_02__container_blocks__list_items__019: |-
+05_02_00__container_blocks__list_items__019: |-
{
"type": "doc",
"content": [
@@ -7512,7 +7512,7 @@
}
]
}
-05_02__container_blocks__list_items__020: |-
+05_02_00__container_blocks__list_items__020: |-
{
"type": "doc",
"content": [
@@ -7553,7 +7553,7 @@
}
]
}
-05_02__container_blocks__list_items__021: |-
+05_02_00__container_blocks__list_items__021: |-
{
"type": "doc",
"content": [
@@ -7611,7 +7611,7 @@
}
]
}
-05_02__container_blocks__list_items__022: |-
+05_02_00__container_blocks__list_items__022: |-
{
"type": "doc",
"content": [
@@ -7669,7 +7669,7 @@
}
]
}
-05_02__container_blocks__list_items__023: |-
+05_02_00__container_blocks__list_items__023: |-
{
"type": "doc",
"content": [
@@ -7693,7 +7693,7 @@
}
]
}
-05_02__container_blocks__list_items__024: |-
+05_02_00__container_blocks__list_items__024: |-
{
"type": "doc",
"content": [
@@ -7730,7 +7730,7 @@
}
]
}
-05_02__container_blocks__list_items__025: |-
+05_02_00__container_blocks__list_items__025: |-
{
"type": "doc",
"content": [
@@ -7767,7 +7767,7 @@
}
]
}
-05_02__container_blocks__list_items__026: |-
+05_02_00__container_blocks__list_items__026: |-
{
"type": "doc",
"content": [
@@ -7837,7 +7837,7 @@
}
]
}
-05_02__container_blocks__list_items__027: |-
+05_02_00__container_blocks__list_items__027: |-
{
"type": "doc",
"content": [
@@ -7865,7 +7865,7 @@
}
]
}
-05_02__container_blocks__list_items__028: |-
+05_02_00__container_blocks__list_items__028: |-
{
"type": "doc",
"content": [
@@ -7896,7 +7896,7 @@
}
]
}
-05_02__container_blocks__list_items__029: |-
+05_02_00__container_blocks__list_items__029: |-
{
"type": "doc",
"content": [
@@ -7946,7 +7946,7 @@
}
]
}
-05_02__container_blocks__list_items__030: |-
+05_02_00__container_blocks__list_items__030: |-
{
"type": "doc",
"content": [
@@ -7996,7 +7996,7 @@
}
]
}
-05_02__container_blocks__list_items__031: |-
+05_02_00__container_blocks__list_items__031: |-
{
"type": "doc",
"content": [
@@ -8047,7 +8047,7 @@
}
]
}
-05_02__container_blocks__list_items__032: |-
+05_02_00__container_blocks__list_items__032: |-
{
"type": "doc",
"content": [
@@ -8069,7 +8069,7 @@
}
]
}
-05_02__container_blocks__list_items__033: |-
+05_02_00__container_blocks__list_items__033: |-
{
"type": "doc",
"content": [
@@ -8093,7 +8093,7 @@
}
]
}
-05_02__container_blocks__list_items__034: |-
+05_02_00__container_blocks__list_items__034: |-
{
"type": "doc",
"content": [
@@ -8152,7 +8152,7 @@
}
]
}
-05_02__container_blocks__list_items__035: |-
+05_02_00__container_blocks__list_items__035: |-
{
"type": "doc",
"content": [
@@ -8211,7 +8211,7 @@
}
]
}
-05_02__container_blocks__list_items__036: |-
+05_02_00__container_blocks__list_items__036: |-
{
"type": "doc",
"content": [
@@ -8270,7 +8270,7 @@
}
]
}
-05_02__container_blocks__list_items__037: |-
+05_02_00__container_blocks__list_items__037: |-
{
"type": "doc",
"content": [
@@ -8289,7 +8289,7 @@
}
]
}
-05_02__container_blocks__list_items__038: |-
+05_02_00__container_blocks__list_items__038: |-
{
"type": "doc",
"content": [
@@ -8348,7 +8348,7 @@
}
]
}
-05_02__container_blocks__list_items__039: |-
+05_02_00__container_blocks__list_items__039: |-
{
"type": "doc",
"content": [
@@ -8377,7 +8377,7 @@
}
]
}
-05_02__container_blocks__list_items__040: |-
+05_02_00__container_blocks__list_items__040: |-
{
"type": "doc",
"content": [
@@ -8425,7 +8425,7 @@
}
]
}
-05_02__container_blocks__list_items__041: |-
+05_02_00__container_blocks__list_items__041: |-
{
"type": "doc",
"content": [
@@ -8473,7 +8473,7 @@
}
]
}
-05_02__container_blocks__list_items__042: |-
+05_02_00__container_blocks__list_items__042: |-
{
"type": "doc",
"content": [
@@ -8567,7 +8567,7 @@
}
]
}
-05_02__container_blocks__list_items__043: |-
+05_02_00__container_blocks__list_items__043: |-
{
"type": "doc",
"content": [
@@ -8637,7 +8637,7 @@
}
]
}
-05_02__container_blocks__list_items__044: |-
+05_02_00__container_blocks__list_items__044: |-
{
"type": "doc",
"content": [
@@ -8688,7 +8688,7 @@
}
]
}
-05_02__container_blocks__list_items__045: |-
+05_02_00__container_blocks__list_items__045: |-
{
"type": "doc",
"content": [
@@ -8739,7 +8739,7 @@
}
]
}
-05_02__container_blocks__list_items__046: |-
+05_02_00__container_blocks__list_items__046: |-
{
"type": "doc",
"content": [
@@ -8783,7 +8783,7 @@
}
]
}
-05_02__container_blocks__list_items__047: |-
+05_02_00__container_blocks__list_items__047: |-
{
"type": "doc",
"content": [
@@ -8845,7 +8845,7 @@
}
]
}
-05_02__container_blocks__list_items__048: |-
+05_02_00__container_blocks__list_items__048: |-
{
"type": "doc",
"content": [
@@ -8908,7 +8908,7 @@
}
]
}
-05_04__container_blocks__lists__001: |-
+05_04_00__container_blocks__lists__001: |-
{
"type": "doc",
"content": [
@@ -8972,7 +8972,7 @@
}
]
}
-05_04__container_blocks__lists__002: |-
+05_04_00__container_blocks__lists__002: |-
{
"type": "doc",
"content": [
@@ -9038,7 +9038,7 @@
}
]
}
-05_04__container_blocks__lists__003: |-
+05_04_00__container_blocks__lists__003: |-
{
"type": "doc",
"content": [
@@ -9089,7 +9089,7 @@
}
]
}
-05_04__container_blocks__lists__004: |-
+05_04_00__container_blocks__lists__004: |-
{
"type": "doc",
"content": [
@@ -9104,7 +9104,7 @@
}
]
}
-05_04__container_blocks__lists__005: |-
+05_04_00__container_blocks__lists__005: |-
{
"type": "doc",
"content": [
@@ -9142,7 +9142,7 @@
}
]
}
-05_04__container_blocks__lists__006: |-
+05_04_00__container_blocks__lists__006: |-
{
"type": "doc",
"content": [
@@ -9198,7 +9198,7 @@
}
]
}
-05_04__container_blocks__lists__007: |-
+05_04_00__container_blocks__lists__007: |-
{
"type": "doc",
"content": [
@@ -9279,7 +9279,7 @@
}
]
}
-05_04__container_blocks__lists__008: |-
+05_04_00__container_blocks__lists__008: |-
{
"type": "doc",
"content": [
@@ -9357,7 +9357,7 @@
}
]
}
-05_04__container_blocks__lists__009: |-
+05_04_00__container_blocks__lists__009: |-
{
"type": "doc",
"content": [
@@ -9421,7 +9421,7 @@
}
]
}
-05_04__container_blocks__lists__010: |-
+05_04_00__container_blocks__lists__010: |-
{
"type": "doc",
"content": [
@@ -9533,7 +9533,7 @@
}
]
}
-05_04__container_blocks__lists__011: |-
+05_04_00__container_blocks__lists__011: |-
{
"type": "doc",
"content": [
@@ -9590,7 +9590,7 @@
}
]
}
-05_04__container_blocks__lists__012: |-
+05_04_00__container_blocks__lists__012: |-
{
"type": "doc",
"content": [
@@ -9660,7 +9660,7 @@
}
]
}
-05_04__container_blocks__lists__013: |-
+05_04_00__container_blocks__lists__013: |-
{
"type": "doc",
"content": [
@@ -9716,7 +9716,7 @@
}
]
}
-05_04__container_blocks__lists__014: |-
+05_04_00__container_blocks__lists__014: |-
{
"type": "doc",
"content": [
@@ -9772,7 +9772,7 @@
}
]
}
-05_04__container_blocks__lists__015: |-
+05_04_00__container_blocks__lists__015: |-
{
"type": "doc",
"content": [
@@ -9822,7 +9822,7 @@
}
]
}
-05_04__container_blocks__lists__016: |-
+05_04_00__container_blocks__lists__016: |-
{
"type": "doc",
"content": [
@@ -9887,7 +9887,7 @@
}
]
}
-05_04__container_blocks__lists__017: |-
+05_04_00__container_blocks__lists__017: |-
{
"type": "doc",
"content": [
@@ -9957,7 +9957,7 @@
}
]
}
-05_04__container_blocks__lists__018: |-
+05_04_00__container_blocks__lists__018: |-
{
"type": "doc",
"content": [
@@ -10020,7 +10020,7 @@
}
]
}
-05_04__container_blocks__lists__019: |-
+05_04_00__container_blocks__lists__019: |-
{
"type": "doc",
"content": [
@@ -10093,7 +10093,7 @@
}
]
}
-05_04__container_blocks__lists__020: |-
+05_04_00__container_blocks__lists__020: |-
{
"type": "doc",
"content": [
@@ -10152,7 +10152,7 @@
}
]
}
-05_04__container_blocks__lists__021: |-
+05_04_00__container_blocks__lists__021: |-
{
"type": "doc",
"content": [
@@ -10224,7 +10224,7 @@
}
]
}
-05_04__container_blocks__lists__022: |-
+05_04_00__container_blocks__lists__022: |-
{
"type": "doc",
"content": [
@@ -10252,7 +10252,7 @@
}
]
}
-05_04__container_blocks__lists__023: |-
+05_04_00__container_blocks__lists__023: |-
{
"type": "doc",
"content": [
@@ -10302,7 +10302,7 @@
}
]
}
-05_04__container_blocks__lists__024: |-
+05_04_00__container_blocks__lists__024: |-
{
"type": "doc",
"content": [
@@ -10347,7 +10347,7 @@
}
]
}
-05_04__container_blocks__lists__025: |-
+05_04_00__container_blocks__lists__025: |-
{
"type": "doc",
"content": [
@@ -10406,7 +10406,7 @@
}
]
}
-05_04__container_blocks__lists__026: |-
+05_04_00__container_blocks__lists__026: |-
{
"type": "doc",
"content": [
@@ -10520,7 +10520,7 @@
}
]
}
-06_01__inlines__001: |-
+06_01_00__inlines__001: |-
{
"type": "doc",
"content": [
@@ -10544,7 +10544,7 @@
}
]
}
-06_02__inlines__backslash_escapes__001: |-
+06_02_00__inlines__backslash_escapes__001: |-
{
"type": "doc",
"content": [
@@ -10559,7 +10559,7 @@
}
]
}
-06_02__inlines__backslash_escapes__002: |-
+06_02_00__inlines__backslash_escapes__002: |-
{
"type": "doc",
"content": [
@@ -10574,7 +10574,7 @@
}
]
}
-06_02__inlines__backslash_escapes__003: |-
+06_02_00__inlines__backslash_escapes__003: |-
{
"type": "doc",
"content": [
@@ -10589,7 +10589,7 @@
}
]
}
-06_02__inlines__backslash_escapes__004: |-
+06_02_00__inlines__backslash_escapes__004: |-
{
"type": "doc",
"content": [
@@ -10613,7 +10613,7 @@
}
]
}
-06_02__inlines__backslash_escapes__005: |-
+06_02_00__inlines__backslash_escapes__005: |-
{
"type": "doc",
"content": [
@@ -10635,7 +10635,7 @@
}
]
}
-06_02__inlines__backslash_escapes__006: |-
+06_02_00__inlines__backslash_escapes__006: |-
{
"type": "doc",
"content": [
@@ -10655,7 +10655,7 @@
}
]
}
-06_02__inlines__backslash_escapes__007: |-
+06_02_00__inlines__backslash_escapes__007: |-
{
"type": "doc",
"content": [
@@ -10674,7 +10674,7 @@
}
]
}
-06_02__inlines__backslash_escapes__008: |-
+06_02_00__inlines__backslash_escapes__008: |-
{
"type": "doc",
"content": [
@@ -10693,7 +10693,7 @@
}
]
}
-06_02__inlines__backslash_escapes__009: |-
+06_02_00__inlines__backslash_escapes__009: |-
{
"type": "doc",
"content": [
@@ -10721,7 +10721,7 @@
}
]
}
-06_02__inlines__backslash_escapes__010: |-
+06_02_00__inlines__backslash_escapes__010: |-
{
"type": "doc",
"content": [
@@ -10730,7 +10730,7 @@
}
]
}
-06_02__inlines__backslash_escapes__011: |-
+06_02_00__inlines__backslash_escapes__011: |-
{
"type": "doc",
"content": [
@@ -10758,7 +10758,7 @@
}
]
}
-06_02__inlines__backslash_escapes__012: |-
+06_02_00__inlines__backslash_escapes__012: |-
{
"type": "doc",
"content": [
@@ -10800,7 +10800,7 @@
}
]
}
-06_02__inlines__backslash_escapes__013: |-
+06_02_00__inlines__backslash_escapes__013: |-
{
"type": "doc",
"content": [
@@ -10819,7 +10819,7 @@
}
]
}
-06_03__inlines__entity_and_numeric_character_references__001: |-
+06_03_00__inlines__entity_and_numeric_character_references__001: |-
{
"type": "doc",
"content": [
@@ -10834,7 +10834,7 @@
}
]
}
-06_03__inlines__entity_and_numeric_character_references__002: |-
+06_03_00__inlines__entity_and_numeric_character_references__002: |-
{
"type": "doc",
"content": [
@@ -10849,7 +10849,7 @@
}
]
}
-06_03__inlines__entity_and_numeric_character_references__003: |-
+06_03_00__inlines__entity_and_numeric_character_references__003: |-
{
"type": "doc",
"content": [
@@ -10864,7 +10864,7 @@
}
]
}
-06_03__inlines__entity_and_numeric_character_references__004: |-
+06_03_00__inlines__entity_and_numeric_character_references__004: |-
{
"type": "doc",
"content": [
@@ -10879,7 +10879,7 @@
}
]
}
-06_03__inlines__entity_and_numeric_character_references__005: |-
+06_03_00__inlines__entity_and_numeric_character_references__005: |-
{
"type": "doc",
"content": [
@@ -10894,7 +10894,7 @@
}
]
}
-06_03__inlines__entity_and_numeric_character_references__006: |-
+06_03_00__inlines__entity_and_numeric_character_references__006: |-
{
"type": "doc",
"content": [
@@ -10909,7 +10909,7 @@
}
]
}
-06_03__inlines__entity_and_numeric_character_references__007: |-
+06_03_00__inlines__entity_and_numeric_character_references__007: |-
{
"type": "doc",
"content": [
@@ -10918,7 +10918,7 @@
}
]
}
-06_03__inlines__entity_and_numeric_character_references__008: |-
+06_03_00__inlines__entity_and_numeric_character_references__008: |-
{
"type": "doc",
"content": [
@@ -10946,7 +10946,7 @@
}
]
}
-06_03__inlines__entity_and_numeric_character_references__009: |-
+06_03_00__inlines__entity_and_numeric_character_references__009: |-
{
"type": "doc",
"content": [
@@ -10988,7 +10988,7 @@
}
]
}
-06_03__inlines__entity_and_numeric_character_references__010: |-
+06_03_00__inlines__entity_and_numeric_character_references__010: |-
{
"type": "doc",
"content": [
@@ -11007,7 +11007,7 @@
}
]
}
-06_03__inlines__entity_and_numeric_character_references__011: |-
+06_03_00__inlines__entity_and_numeric_character_references__011: |-
{
"type": "doc",
"content": [
@@ -11027,7 +11027,7 @@
}
]
}
-06_03__inlines__entity_and_numeric_character_references__012: |-
+06_03_00__inlines__entity_and_numeric_character_references__012: |-
{
"type": "doc",
"content": [
@@ -11046,7 +11046,7 @@
}
]
}
-06_03__inlines__entity_and_numeric_character_references__013: |-
+06_03_00__inlines__entity_and_numeric_character_references__013: |-
{
"type": "doc",
"content": [
@@ -11070,7 +11070,7 @@
}
]
}
-06_03__inlines__entity_and_numeric_character_references__014: |-
+06_03_00__inlines__entity_and_numeric_character_references__014: |-
{
"type": "doc",
"content": [
@@ -11107,7 +11107,7 @@
}
]
}
-06_03__inlines__entity_and_numeric_character_references__015: |-
+06_03_00__inlines__entity_and_numeric_character_references__015: |-
{
"type": "doc",
"content": [
@@ -11122,7 +11122,7 @@
}
]
}
-06_03__inlines__entity_and_numeric_character_references__016: |-
+06_03_00__inlines__entity_and_numeric_character_references__016: |-
{
"type": "doc",
"content": [
@@ -11137,7 +11137,7 @@
}
]
}
-06_03__inlines__entity_and_numeric_character_references__017: |-
+06_03_00__inlines__entity_and_numeric_character_references__017: |-
{
"type": "doc",
"content": [
@@ -11152,7 +11152,7 @@
}
]
}
-06_04__inlines__code_spans__001: |-
+06_04_00__inlines__code_spans__001: |-
{
"type": "doc",
"content": [
@@ -11172,7 +11172,7 @@
}
]
}
-06_04__inlines__code_spans__002: |-
+06_04_00__inlines__code_spans__002: |-
{
"type": "doc",
"content": [
@@ -11192,7 +11192,7 @@
}
]
}
-06_04__inlines__code_spans__003: |-
+06_04_00__inlines__code_spans__003: |-
{
"type": "doc",
"content": [
@@ -11212,7 +11212,7 @@
}
]
}
-06_04__inlines__code_spans__004: |-
+06_04_00__inlines__code_spans__004: |-
{
"type": "doc",
"content": [
@@ -11232,7 +11232,7 @@
}
]
}
-06_04__inlines__code_spans__005: |-
+06_04_00__inlines__code_spans__005: |-
{
"type": "doc",
"content": [
@@ -11252,7 +11252,7 @@
}
]
}
-06_04__inlines__code_spans__006: |-
+06_04_00__inlines__code_spans__006: |-
{
"type": "doc",
"content": [
@@ -11272,7 +11272,7 @@
}
]
}
-06_04__inlines__code_spans__007: |-
+06_04_00__inlines__code_spans__007: |-
{
"type": "doc",
"content": [
@@ -11281,7 +11281,7 @@
}
]
}
-06_04__inlines__code_spans__008: |-
+06_04_00__inlines__code_spans__008: |-
{
"type": "doc",
"content": [
@@ -11301,7 +11301,7 @@
}
]
}
-06_04__inlines__code_spans__009: |-
+06_04_00__inlines__code_spans__009: |-
{
"type": "doc",
"content": [
@@ -11321,7 +11321,7 @@
}
]
}
-06_04__inlines__code_spans__010: |-
+06_04_00__inlines__code_spans__010: |-
{
"type": "doc",
"content": [
@@ -11341,7 +11341,7 @@
}
]
}
-06_04__inlines__code_spans__011: |-
+06_04_00__inlines__code_spans__011: |-
{
"type": "doc",
"content": [
@@ -11365,7 +11365,7 @@
}
]
}
-06_04__inlines__code_spans__012: |-
+06_04_00__inlines__code_spans__012: |-
{
"type": "doc",
"content": [
@@ -11385,7 +11385,7 @@
}
]
}
-06_04__inlines__code_spans__013: |-
+06_04_00__inlines__code_spans__013: |-
{
"type": "doc",
"content": [
@@ -11405,7 +11405,7 @@
}
]
}
-06_04__inlines__code_spans__014: |-
+06_04_00__inlines__code_spans__014: |-
{
"type": "doc",
"content": [
@@ -11429,7 +11429,7 @@
}
]
}
-06_04__inlines__code_spans__015: |-
+06_04_00__inlines__code_spans__015: |-
{
"type": "doc",
"content": [
@@ -11457,7 +11457,7 @@
}
]
}
-06_04__inlines__code_spans__016: |-
+06_04_00__inlines__code_spans__016: |-
{
"type": "doc",
"content": [
@@ -11481,7 +11481,7 @@
}
]
}
-06_04__inlines__code_spans__017: |-
+06_04_00__inlines__code_spans__017: |-
{
"type": "doc",
"content": [
@@ -11509,7 +11509,7 @@
}
]
}
-06_04__inlines__code_spans__018: |-
+06_04_00__inlines__code_spans__018: |-
{
"type": "doc",
"content": [
@@ -11533,7 +11533,7 @@
}
]
}
-06_04__inlines__code_spans__019: |-
+06_04_00__inlines__code_spans__019: |-
{
"type": "doc",
"content": [
@@ -11565,7 +11565,7 @@
}
]
}
-06_04__inlines__code_spans__020: |-
+06_04_00__inlines__code_spans__020: |-
{
"type": "doc",
"content": [
@@ -11580,7 +11580,7 @@
}
]
}
-06_04__inlines__code_spans__021: |-
+06_04_00__inlines__code_spans__021: |-
{
"type": "doc",
"content": [
@@ -11595,7 +11595,7 @@
}
]
}
-06_04__inlines__code_spans__022: |-
+06_04_00__inlines__code_spans__022: |-
{
"type": "doc",
"content": [
@@ -11619,7 +11619,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__001: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__001: |-
{
"type": "doc",
"content": [
@@ -11639,7 +11639,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__002: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__002: |-
{
"type": "doc",
"content": [
@@ -11654,7 +11654,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__003: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__003: |-
{
"type": "doc",
"content": [
@@ -11669,7 +11669,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__004: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__004: |-
{
"type": "doc",
"content": [
@@ -11684,7 +11684,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__005: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__005: |-
{
"type": "doc",
"content": [
@@ -11708,7 +11708,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__006: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__006: |-
{
"type": "doc",
"content": [
@@ -11736,7 +11736,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__007: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__007: |-
{
"type": "doc",
"content": [
@@ -11756,7 +11756,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__008: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__008: |-
{
"type": "doc",
"content": [
@@ -11771,7 +11771,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__009: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__009: |-
{
"type": "doc",
"content": [
@@ -11786,7 +11786,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__010: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__010: |-
{
"type": "doc",
"content": [
@@ -11801,7 +11801,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__011: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__011: |-
{
"type": "doc",
"content": [
@@ -11816,7 +11816,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__012: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__012: |-
{
"type": "doc",
"content": [
@@ -11831,7 +11831,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__013: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__013: |-
{
"type": "doc",
"content": [
@@ -11846,7 +11846,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__014: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__014: |-
{
"type": "doc",
"content": [
@@ -11870,7 +11870,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__015: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__015: |-
{
"type": "doc",
"content": [
@@ -11885,7 +11885,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__016: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__016: |-
{
"type": "doc",
"content": [
@@ -11900,7 +11900,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__017: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__017: |-
{
"type": "doc",
"content": [
@@ -11915,7 +11915,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__018: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__018: |-
{
"type": "doc",
"content": [
@@ -11930,7 +11930,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__019: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__019: |-
{
"type": "doc",
"content": [
@@ -11954,7 +11954,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__020: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__020: |-
{
"type": "doc",
"content": [
@@ -11978,7 +11978,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__021: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__021: |-
{
"type": "doc",
"content": [
@@ -11993,7 +11993,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__022: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__022: |-
{
"type": "doc",
"content": [
@@ -12008,7 +12008,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__023: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__023: |-
{
"type": "doc",
"content": [
@@ -12032,7 +12032,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__024: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__024: |-
{
"type": "doc",
"content": [
@@ -12047,7 +12047,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__025: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__025: |-
{
"type": "doc",
"content": [
@@ -12062,7 +12062,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__026: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__026: |-
{
"type": "doc",
"content": [
@@ -12082,7 +12082,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__027: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__027: |-
{
"type": "doc",
"content": [
@@ -12106,7 +12106,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__028: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__028: |-
{
"type": "doc",
"content": [
@@ -12126,7 +12126,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__029: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__029: |-
{
"type": "doc",
"content": [
@@ -12141,7 +12141,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__030: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__030: |-
{
"type": "doc",
"content": [
@@ -12156,7 +12156,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__031: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__031: |-
{
"type": "doc",
"content": [
@@ -12180,7 +12180,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__032: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__032: |-
{
"type": "doc",
"content": [
@@ -12200,7 +12200,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__033: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__033: |-
{
"type": "doc",
"content": [
@@ -12215,7 +12215,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__034: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__034: |-
{
"type": "doc",
"content": [
@@ -12230,7 +12230,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__035: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__035: |-
{
"type": "doc",
"content": [
@@ -12245,7 +12245,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__036: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__036: |-
{
"type": "doc",
"content": [
@@ -12260,7 +12260,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__037: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__037: |-
{
"type": "doc",
"content": [
@@ -12275,7 +12275,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__038: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__038: |-
{
"type": "doc",
"content": [
@@ -12290,7 +12290,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__039: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__039: |-
{
"type": "doc",
"content": [
@@ -12314,7 +12314,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__040: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__040: |-
{
"type": "doc",
"content": [
@@ -12338,7 +12338,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__041: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__041: |-
{
"type": "doc",
"content": [
@@ -12353,7 +12353,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__042: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__042: |-
{
"type": "doc",
"content": [
@@ -12368,7 +12368,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__043: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__043: |-
{
"type": "doc",
"content": [
@@ -12409,7 +12409,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__044: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__044: |-
{
"type": "doc",
"content": [
@@ -12471,7 +12471,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__045: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__045: |-
{
"type": "doc",
"content": [
@@ -12512,7 +12512,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__046: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__046: |-
{
"type": "doc",
"content": [
@@ -12536,7 +12536,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__047: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__047: |-
{
"type": "doc",
"content": [
@@ -12551,7 +12551,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__048: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__048: |-
{
"type": "doc",
"content": [
@@ -12566,7 +12566,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__049: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__049: |-
{
"type": "doc",
"content": [
@@ -12607,7 +12607,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__050: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__050: |-
{
"type": "doc",
"content": [
@@ -12622,7 +12622,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__051: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__051: |-
{
"type": "doc",
"content": [
@@ -12637,7 +12637,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__052: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__052: |-
{
"type": "doc",
"content": [
@@ -12657,7 +12657,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__053: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__053: |-
{
"type": "doc",
"content": [
@@ -12681,7 +12681,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__054: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__054: |-
{
"type": "doc",
"content": [
@@ -12721,7 +12721,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__055: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__055: |-
{
"type": "doc",
"content": [
@@ -12741,7 +12741,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__056: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__056: |-
{
"type": "doc",
"content": [
@@ -12782,7 +12782,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__057: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__057: |-
{
"type": "doc",
"content": [
@@ -12806,7 +12806,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__058: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__058: |-
{
"type": "doc",
"content": [
@@ -12830,7 +12830,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__059: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__059: |-
{
"type": "doc",
"content": [
@@ -12850,7 +12850,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__060: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__060: |-
{
"type": "doc",
"content": [
@@ -12891,7 +12891,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__061: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__061: |-
{
"type": "doc",
"content": [
@@ -12932,7 +12932,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__062: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__062: |-
{
"type": "doc",
"content": [
@@ -12952,7 +12952,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__063: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__063: |-
{
"type": "doc",
"content": [
@@ -12984,7 +12984,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__064: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__064: |-
{
"type": "doc",
"content": [
@@ -13016,7 +13016,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__065: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__065: |-
{
"type": "doc",
"content": [
@@ -13048,7 +13048,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__066: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__066: |-
{
"type": "doc",
"content": [
@@ -13079,7 +13079,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__067: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__067: |-
{
"type": "doc",
"content": [
@@ -13107,7 +13107,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__068: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__068: |-
{
"type": "doc",
"content": [
@@ -13152,7 +13152,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__069: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__069: |-
{
"type": "doc",
"content": [
@@ -13192,7 +13192,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__070: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__070: |-
{
"type": "doc",
"content": [
@@ -13207,7 +13207,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__071: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__071: |-
{
"type": "doc",
"content": [
@@ -13222,7 +13222,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__072: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__072: |-
{
"type": "doc",
"content": [
@@ -13262,7 +13262,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__073: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__073: |-
{
"type": "doc",
"content": [
@@ -13282,7 +13282,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__074: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__074: |-
{
"type": "doc",
"content": [
@@ -13323,7 +13323,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__075: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__075: |-
{
"type": "doc",
"content": [
@@ -13347,7 +13347,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__076: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__076: |-
{
"type": "doc",
"content": [
@@ -13371,7 +13371,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__077: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__077: |-
{
"type": "doc",
"content": [
@@ -13391,7 +13391,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__078: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__078: |-
{
"type": "doc",
"content": [
@@ -13432,7 +13432,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__079: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__079: |-
{
"type": "doc",
"content": [
@@ -13473,7 +13473,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__080: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__080: |-
{
"type": "doc",
"content": [
@@ -13505,7 +13505,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__081: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__081: |-
{
"type": "doc",
"content": [
@@ -13537,7 +13537,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__082: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__082: |-
{
"type": "doc",
"content": [
@@ -13582,7 +13582,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__083: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__083: |-
{
"type": "doc",
"content": [
@@ -13625,7 +13625,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__084: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__084: |-
{
"type": "doc",
"content": [
@@ -13640,7 +13640,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__085: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__085: |-
{
"type": "doc",
"content": [
@@ -13655,7 +13655,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__086: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__086: |-
{
"type": "doc",
"content": [
@@ -13670,7 +13670,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__087: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__087: |-
{
"type": "doc",
"content": [
@@ -13694,7 +13694,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__088: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__088: |-
{
"type": "doc",
"content": [
@@ -13718,7 +13718,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__089: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__089: |-
{
"type": "doc",
"content": [
@@ -13733,7 +13733,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__090: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__090: |-
{
"type": "doc",
"content": [
@@ -13757,7 +13757,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__091: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__091: |-
{
"type": "doc",
"content": [
@@ -13781,7 +13781,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__092: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__092: |-
{
"type": "doc",
"content": [
@@ -13805,7 +13805,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__093: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__093: |-
{
"type": "doc",
"content": [
@@ -13829,7 +13829,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__094: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__094: |-
{
"type": "doc",
"content": [
@@ -13853,7 +13853,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__095: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__095: |-
{
"type": "doc",
"content": [
@@ -13877,7 +13877,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__096: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__096: |-
{
"type": "doc",
"content": [
@@ -13901,7 +13901,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__097: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__097: |-
{
"type": "doc",
"content": [
@@ -13925,7 +13925,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__098: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__098: |-
{
"type": "doc",
"content": [
@@ -13940,7 +13940,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__099: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__099: |-
{
"type": "doc",
"content": [
@@ -13964,7 +13964,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__100: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__100: |-
{
"type": "doc",
"content": [
@@ -13988,7 +13988,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__101: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__101: |-
{
"type": "doc",
"content": [
@@ -14003,7 +14003,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__102: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__102: |-
{
"type": "doc",
"content": [
@@ -14027,7 +14027,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__103: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__103: |-
{
"type": "doc",
"content": [
@@ -14051,7 +14051,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__104: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__104: |-
{
"type": "doc",
"content": [
@@ -14075,7 +14075,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__105: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__105: |-
{
"type": "doc",
"content": [
@@ -14099,7 +14099,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__106: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__106: |-
{
"type": "doc",
"content": [
@@ -14123,7 +14123,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__107: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__107: |-
{
"type": "doc",
"content": [
@@ -14147,7 +14147,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__108: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__108: |-
{
"type": "doc",
"content": [
@@ -14171,7 +14171,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__109: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__109: |-
{
"type": "doc",
"content": [
@@ -14195,7 +14195,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__110: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__110: |-
{
"type": "doc",
"content": [
@@ -14215,7 +14215,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__111: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__111: |-
{
"type": "doc",
"content": [
@@ -14235,7 +14235,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__112: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__112: |-
{
"type": "doc",
"content": [
@@ -14255,7 +14255,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__113: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__113: |-
{
"type": "doc",
"content": [
@@ -14275,7 +14275,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__114: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__114: |-
{
"type": "doc",
"content": [
@@ -14295,7 +14295,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__115: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__115: |-
{
"type": "doc",
"content": [
@@ -14315,7 +14315,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__116: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__116: |-
{
"type": "doc",
"content": [
@@ -14335,7 +14335,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__117: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__117: |-
{
"type": "doc",
"content": [
@@ -14358,7 +14358,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__118: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__118: |-
{
"type": "doc",
"content": [
@@ -14381,7 +14381,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__119: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__119: |-
{
"type": "doc",
"content": [
@@ -14405,7 +14405,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__120: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__120: |-
{
"type": "doc",
"content": [
@@ -14446,7 +14446,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__121: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__121: |-
{
"type": "doc",
"content": [
@@ -14470,7 +14470,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__122: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__122: |-
{
"type": "doc",
"content": [
@@ -14494,7 +14494,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__123: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__123: |-
{
"type": "doc",
"content": [
@@ -14526,7 +14526,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__124: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__124: |-
{
"type": "doc",
"content": [
@@ -14558,7 +14558,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__125: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__125: |-
{
"type": "doc",
"content": [
@@ -14584,7 +14584,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__126: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__126: |-
{
"type": "doc",
"content": [
@@ -14599,7 +14599,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__127: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__127: |-
{
"type": "doc",
"content": [
@@ -14614,7 +14614,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__128: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__128: |-
{
"type": "doc",
"content": [
@@ -14646,7 +14646,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__129: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__129: |-
{
"type": "doc",
"content": [
@@ -14678,7 +14678,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__130: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__130: |-
{
"type": "doc",
"content": [
@@ -14710,7 +14710,7 @@
}
]
}
-06_05__inlines__emphasis_and_strong_emphasis__131: |-
+06_05_00__inlines__emphasis_and_strong_emphasis__131: |-
{
"type": "doc",
"content": [
@@ -14742,7 +14742,7 @@
}
]
}
-06_06__inlines__strikethrough_extension__001: |-
+06_06_00__inlines__strikethrough_extension__001: |-
{
"type": "doc",
"content": [
@@ -14766,7 +14766,7 @@
}
]
}
-06_06__inlines__strikethrough_extension__002: |-
+06_06_00__inlines__strikethrough_extension__002: |-
{
"type": "doc",
"content": [
@@ -14790,7 +14790,7 @@
}
]
}
-06_07__inlines__links__001: |-
+06_07_00__inlines__links__001: |-
{
"type": "doc",
"content": [
@@ -14818,7 +14818,7 @@
}
]
}
-06_07__inlines__links__002: |-
+06_07_00__inlines__links__002: |-
{
"type": "doc",
"content": [
@@ -14846,7 +14846,7 @@
}
]
}
-06_07__inlines__links__003: |-
+06_07_00__inlines__links__003: |-
{
"type": "doc",
"content": [
@@ -14874,7 +14874,7 @@
}
]
}
-06_07__inlines__links__004: |-
+06_07_00__inlines__links__004: |-
{
"type": "doc",
"content": [
@@ -14902,7 +14902,7 @@
}
]
}
-06_07__inlines__links__005: |-
+06_07_00__inlines__links__005: |-
{
"type": "doc",
"content": [
@@ -14917,7 +14917,7 @@
}
]
}
-06_07__inlines__links__006: |-
+06_07_00__inlines__links__006: |-
{
"type": "doc",
"content": [
@@ -14945,7 +14945,7 @@
}
]
}
-06_07__inlines__links__007: |-
+06_07_00__inlines__links__007: |-
{
"type": "doc",
"content": [
@@ -14960,7 +14960,7 @@
}
]
}
-06_07__inlines__links__008: |-
+06_07_00__inlines__links__008: |-
{
"type": "doc",
"content": [
@@ -14975,7 +14975,7 @@
}
]
}
-06_07__inlines__links__009: |-
+06_07_00__inlines__links__009: |-
{
"type": "doc",
"content": [
@@ -15003,7 +15003,7 @@
}
]
}
-06_07__inlines__links__010: |-
+06_07_00__inlines__links__010: |-
{
"type": "doc",
"content": [
@@ -15018,7 +15018,7 @@
}
]
}
-06_07__inlines__links__011: |-
+06_07_00__inlines__links__011: |-
{
"type": "doc",
"content": [
@@ -15042,7 +15042,7 @@
}
]
}
-06_07__inlines__links__012: |-
+06_07_00__inlines__links__012: |-
{
"type": "doc",
"content": [
@@ -15070,7 +15070,7 @@
}
]
}
-06_07__inlines__links__013: |-
+06_07_00__inlines__links__013: |-
{
"type": "doc",
"content": [
@@ -15098,7 +15098,7 @@
}
]
}
-06_07__inlines__links__014: |-
+06_07_00__inlines__links__014: |-
{
"type": "doc",
"content": [
@@ -15126,7 +15126,7 @@
}
]
}
-06_07__inlines__links__015: |-
+06_07_00__inlines__links__015: |-
{
"type": "doc",
"content": [
@@ -15154,7 +15154,7 @@
}
]
}
-06_07__inlines__links__016: |-
+06_07_00__inlines__links__016: |-
{
"type": "doc",
"content": [
@@ -15182,7 +15182,7 @@
}
]
}
-06_07__inlines__links__017: |-
+06_07_00__inlines__links__017: |-
{
"type": "doc",
"content": [
@@ -15254,7 +15254,7 @@
}
]
}
-06_07__inlines__links__018: |-
+06_07_00__inlines__links__018: |-
{
"type": "doc",
"content": [
@@ -15282,7 +15282,7 @@
}
]
}
-06_07__inlines__links__019: |-
+06_07_00__inlines__links__019: |-
{
"type": "doc",
"content": [
@@ -15310,7 +15310,7 @@
}
]
}
-06_07__inlines__links__020: |-
+06_07_00__inlines__links__020: |-
{
"type": "doc",
"content": [
@@ -15338,7 +15338,7 @@
}
]
}
-06_07__inlines__links__021: |-
+06_07_00__inlines__links__021: |-
{
"type": "doc",
"content": [
@@ -15366,7 +15366,7 @@
}
]
}
-06_07__inlines__links__022: |-
+06_07_00__inlines__links__022: |-
{
"type": "doc",
"content": [
@@ -15394,7 +15394,7 @@
}
]
}
-06_07__inlines__links__023: |-
+06_07_00__inlines__links__023: |-
{
"type": "doc",
"content": [
@@ -15422,7 +15422,7 @@
}
]
}
-06_07__inlines__links__024: |-
+06_07_00__inlines__links__024: |-
{
"type": "doc",
"content": [
@@ -15437,7 +15437,7 @@
}
]
}
-06_07__inlines__links__025: |-
+06_07_00__inlines__links__025: |-
{
"type": "doc",
"content": [
@@ -15465,7 +15465,7 @@
}
]
}
-06_07__inlines__links__026: |-
+06_07_00__inlines__links__026: |-
{
"type": "doc",
"content": [
@@ -15493,7 +15493,7 @@
}
]
}
-06_07__inlines__links__027: |-
+06_07_00__inlines__links__027: |-
{
"type": "doc",
"content": [
@@ -15508,7 +15508,7 @@
}
]
}
-06_07__inlines__links__028: |-
+06_07_00__inlines__links__028: |-
{
"type": "doc",
"content": [
@@ -15536,7 +15536,7 @@
}
]
}
-06_07__inlines__links__029: |-
+06_07_00__inlines__links__029: |-
{
"type": "doc",
"content": [
@@ -15551,7 +15551,7 @@
}
]
}
-06_07__inlines__links__030: |-
+06_07_00__inlines__links__030: |-
{
"type": "doc",
"content": [
@@ -15583,7 +15583,7 @@
}
]
}
-06_07__inlines__links__031: |-
+06_07_00__inlines__links__031: |-
{
"type": "doc",
"content": [
@@ -15611,7 +15611,7 @@
}
]
}
-06_07__inlines__links__032: |-
+06_07_00__inlines__links__032: |-
{
"type": "doc",
"content": [
@@ -15708,7 +15708,7 @@
}
]
}
-06_07__inlines__links__033: |-
+06_07_00__inlines__links__033: |-
{
"type": "doc",
"content": [
@@ -15743,7 +15743,7 @@
}
]
}
-06_07__inlines__links__034: |-
+06_07_00__inlines__links__034: |-
{
"type": "doc",
"content": [
@@ -15779,7 +15779,7 @@
}
]
}
-06_07__inlines__links__035: |-
+06_07_00__inlines__links__035: |-
{
"type": "doc",
"content": [
@@ -15836,7 +15836,7 @@
}
]
}
-06_07__inlines__links__036: |-
+06_07_00__inlines__links__036: |-
{
"type": "doc",
"content": [
@@ -15858,7 +15858,7 @@
}
]
}
-06_07__inlines__links__037: |-
+06_07_00__inlines__links__037: |-
{
"type": "doc",
"content": [
@@ -15890,7 +15890,7 @@
}
]
}
-06_07__inlines__links__038: |-
+06_07_00__inlines__links__038: |-
{
"type": "doc",
"content": [
@@ -15918,7 +15918,7 @@
}
]
}
-06_07__inlines__links__039: |-
+06_07_00__inlines__links__039: |-
{
"type": "doc",
"content": [
@@ -15942,7 +15942,7 @@
}
]
}
-06_07__inlines__links__040: |-
+06_07_00__inlines__links__040: |-
{
"type": "doc",
"content": [
@@ -15957,7 +15957,7 @@
}
]
}
-06_07__inlines__links__041: |-
+06_07_00__inlines__links__041: |-
{
"type": "doc",
"content": [
@@ -15981,7 +15981,7 @@
}
]
}
-06_07__inlines__links__042: |-
+06_07_00__inlines__links__042: |-
{
"type": "doc",
"content": [
@@ -16013,7 +16013,7 @@
}
]
}
-06_07__inlines__links__043: |-
+06_07_00__inlines__links__043: |-
{
"type": "doc",
"content": [
@@ -16055,7 +16055,7 @@
}
]
}
-06_07__inlines__links__044: |-
+06_07_00__inlines__links__044: |-
{
"type": "doc",
"content": [
@@ -16097,7 +16097,7 @@
}
]
}
-06_07__inlines__links__045: |-
+06_07_00__inlines__links__045: |-
{
"type": "doc",
"content": [
@@ -16139,7 +16139,7 @@
}
]
}
-06_07__inlines__links__046: |-
+06_07_00__inlines__links__046: |-
{
"type": "doc",
"content": [
@@ -16250,7 +16250,7 @@
}
]
}
-06_07__inlines__links__047: |-
+06_07_00__inlines__links__047: |-
{
"type": "doc",
"content": [
@@ -16299,7 +16299,7 @@
}
]
}
-06_07__inlines__links__048: |-
+06_07_00__inlines__links__048: |-
{
"type": "doc",
"content": [
@@ -16366,7 +16366,7 @@
}
]
}
-06_07__inlines__links__049: |-
+06_07_00__inlines__links__049: |-
{
"type": "doc",
"content": [
@@ -16445,7 +16445,7 @@
}
]
}
-06_07__inlines__links__050: |-
+06_07_00__inlines__links__050: |-
{
"type": "doc",
"content": [
@@ -16491,7 +16491,7 @@
}
]
}
-06_07__inlines__links__051: |-
+06_07_00__inlines__links__051: |-
{
"type": "doc",
"content": [
@@ -16533,7 +16533,7 @@
}
]
}
-06_07__inlines__links__052: |-
+06_07_00__inlines__links__052: |-
{
"type": "doc",
"content": [
@@ -16562,7 +16562,7 @@
}
]
}
-06_07__inlines__links__053: |-
+06_07_00__inlines__links__053: |-
{
"type": "doc",
"content": [
@@ -16600,7 +16600,7 @@
}
]
}
-06_07__inlines__links__054: |-
+06_07_00__inlines__links__054: |-
{
"type": "doc",
"content": [
@@ -16646,7 +16646,7 @@
}
]
}
-06_07__inlines__links__055: |-
+06_07_00__inlines__links__055: |-
{
"type": "doc",
"content": [
@@ -16688,7 +16688,7 @@
}
]
}
-06_07__inlines__links__056: |-
+06_07_00__inlines__links__056: |-
{
"type": "doc",
"content": [
@@ -16734,7 +16734,7 @@
}
]
}
-06_07__inlines__links__057: |-
+06_07_00__inlines__links__057: |-
{
"type": "doc",
"content": [
@@ -16776,7 +16776,7 @@
}
]
}
-06_07__inlines__links__058: |-
+06_07_00__inlines__links__058: |-
{
"type": "doc",
"content": [
@@ -16822,7 +16822,7 @@
}
]
}
-06_07__inlines__links__059: |-
+06_07_00__inlines__links__059: |-
{
"type": "doc",
"content": [
@@ -16868,7 +16868,7 @@
}
]
}
-06_07__inlines__links__060: |-
+06_07_00__inlines__links__060: |-
{
"type": "doc",
"content": [
@@ -16924,7 +16924,7 @@
}
]
}
-06_07__inlines__links__061: |-
+06_07_00__inlines__links__061: |-
{
"type": "doc",
"content": [
@@ -16953,7 +16953,7 @@
}
]
}
-06_07__inlines__links__062: |-
+06_07_00__inlines__links__062: |-
{
"type": "doc",
"content": [
@@ -16977,7 +16977,7 @@
}
]
}
-06_07__inlines__links__063: |-
+06_07_00__inlines__links__063: |-
{
"type": "doc",
"content": [
@@ -17001,7 +17001,7 @@
}
]
}
-06_07__inlines__links__064: |-
+06_07_00__inlines__links__064: |-
{
"type": "doc",
"content": [
@@ -17025,7 +17025,7 @@
}
]
}
-06_07__inlines__links__065: |-
+06_07_00__inlines__links__065: |-
{
"type": "doc",
"content": [
@@ -17067,7 +17067,7 @@
}
]
}
-06_07__inlines__links__066: |-
+06_07_00__inlines__links__066: |-
{
"type": "doc",
"content": [
@@ -17109,7 +17109,7 @@
}
]
}
-06_07__inlines__links__067: |-
+06_07_00__inlines__links__067: |-
{
"type": "doc",
"content": [
@@ -17133,7 +17133,7 @@
}
]
}
-06_07__inlines__links__068: |-
+06_07_00__inlines__links__068: |-
{
"type": "doc",
"content": [
@@ -17157,7 +17157,7 @@
}
]
}
-06_07__inlines__links__069: |-
+06_07_00__inlines__links__069: |-
{
"type": "doc",
"content": [
@@ -17199,7 +17199,7 @@
}
]
}
-06_07__inlines__links__070: |-
+06_07_00__inlines__links__070: |-
{
"type": "doc",
"content": [
@@ -17261,7 +17261,7 @@
}
]
}
-06_07__inlines__links__071: |-
+06_07_00__inlines__links__071: |-
{
"type": "doc",
"content": [
@@ -17303,7 +17303,7 @@
}
]
}
-06_07__inlines__links__072: |-
+06_07_00__inlines__links__072: |-
{
"type": "doc",
"content": [
@@ -17349,7 +17349,7 @@
}
]
}
-06_07__inlines__links__073: |-
+06_07_00__inlines__links__073: |-
{
"type": "doc",
"content": [
@@ -17391,7 +17391,7 @@
}
]
}
-06_07__inlines__links__074: |-
+06_07_00__inlines__links__074: |-
{
"type": "doc",
"content": [
@@ -17453,7 +17453,7 @@
}
]
}
-06_07__inlines__links__075: |-
+06_07_00__inlines__links__075: |-
{
"type": "doc",
"content": [
@@ -17523,7 +17523,7 @@
}
]
}
-06_07__inlines__links__076: |-
+06_07_00__inlines__links__076: |-
{
"type": "doc",
"content": [
@@ -17569,7 +17569,7 @@
}
]
}
-06_07__inlines__links__077: |-
+06_07_00__inlines__links__077: |-
{
"type": "doc",
"content": [
@@ -17611,7 +17611,7 @@
}
]
}
-06_07__inlines__links__078: |-
+06_07_00__inlines__links__078: |-
{
"type": "doc",
"content": [
@@ -17657,7 +17657,7 @@
}
]
}
-06_07__inlines__links__079: |-
+06_07_00__inlines__links__079: |-
{
"type": "doc",
"content": [
@@ -17686,7 +17686,7 @@
}
]
}
-06_07__inlines__links__080: |-
+06_07_00__inlines__links__080: |-
{
"type": "doc",
"content": [
@@ -17732,7 +17732,7 @@
}
]
}
-06_07__inlines__links__081: |-
+06_07_00__inlines__links__081: |-
{
"type": "doc",
"content": [
@@ -17788,7 +17788,7 @@
}
]
}
-06_07__inlines__links__082: |-
+06_07_00__inlines__links__082: |-
{
"type": "doc",
"content": [
@@ -17830,7 +17830,7 @@
}
]
}
-06_07__inlines__links__083: |-
+06_07_00__inlines__links__083: |-
{
"type": "doc",
"content": [
@@ -17872,7 +17872,7 @@
}
]
}
-06_07__inlines__links__084: |-
+06_07_00__inlines__links__084: |-
{
"type": "doc",
"content": [
@@ -17918,7 +17918,7 @@
}
]
}
-06_07__inlines__links__085: |-
+06_07_00__inlines__links__085: |-
{
"type": "doc",
"content": [
@@ -17964,7 +17964,7 @@
}
]
}
-06_07__inlines__links__086: |-
+06_07_00__inlines__links__086: |-
{
"type": "doc",
"content": [
@@ -18037,7 +18037,7 @@
}
]
}
-06_07__inlines__links__087: |-
+06_07_00__inlines__links__087: |-
{
"type": "doc",
"content": [
@@ -18097,7 +18097,7 @@
}
]
}
-06_08__inlines__images__001: |-
+06_08_00__inlines__images__001: |-
{
"type": "doc",
"content": [
@@ -18119,7 +18119,7 @@
}
]
}
-06_08__inlines__images__002: |-
+06_08_00__inlines__images__002: |-
{
"type": "doc",
"content": [
@@ -18155,7 +18155,7 @@
}
]
}
-06_08__inlines__images__003: |-
+06_08_00__inlines__images__003: |-
{
"type": "doc",
"content": [
@@ -18177,7 +18177,7 @@
}
]
}
-06_08__inlines__images__004: |-
+06_08_00__inlines__images__004: |-
{
"type": "doc",
"content": [
@@ -18199,7 +18199,7 @@
}
]
}
-06_08__inlines__images__005: |-
+06_08_00__inlines__images__005: |-
{
"type": "doc",
"content": [
@@ -18235,7 +18235,7 @@
}
]
}
-06_08__inlines__images__006: |-
+06_08_00__inlines__images__006: |-
{
"type": "doc",
"content": [
@@ -18271,7 +18271,7 @@
}
]
}
-06_08__inlines__images__007: |-
+06_08_00__inlines__images__007: |-
{
"type": "doc",
"content": [
@@ -18293,7 +18293,7 @@
}
]
}
-06_08__inlines__images__008: |-
+06_08_00__inlines__images__008: |-
{
"type": "doc",
"content": [
@@ -18319,7 +18319,7 @@
}
]
}
-06_08__inlines__images__009: |-
+06_08_00__inlines__images__009: |-
{
"type": "doc",
"content": [
@@ -18341,7 +18341,7 @@
}
]
}
-06_08__inlines__images__010: |-
+06_08_00__inlines__images__010: |-
{
"type": "doc",
"content": [
@@ -18363,7 +18363,7 @@
}
]
}
-06_08__inlines__images__011: |-
+06_08_00__inlines__images__011: |-
{
"type": "doc",
"content": [
@@ -18399,7 +18399,7 @@
}
]
}
-06_08__inlines__images__012: |-
+06_08_00__inlines__images__012: |-
{
"type": "doc",
"content": [
@@ -18435,7 +18435,7 @@
}
]
}
-06_08__inlines__images__013: |-
+06_08_00__inlines__images__013: |-
{
"type": "doc",
"content": [
@@ -18471,7 +18471,7 @@
}
]
}
-06_08__inlines__images__014: |-
+06_08_00__inlines__images__014: |-
{
"type": "doc",
"content": [
@@ -18507,7 +18507,7 @@
}
]
}
-06_08__inlines__images__015: |-
+06_08_00__inlines__images__015: |-
{
"type": "doc",
"content": [
@@ -18543,7 +18543,7 @@
}
]
}
-06_08__inlines__images__016: |-
+06_08_00__inlines__images__016: |-
{
"type": "doc",
"content": [
@@ -18583,7 +18583,7 @@
}
]
}
-06_08__inlines__images__017: |-
+06_08_00__inlines__images__017: |-
{
"type": "doc",
"content": [
@@ -18619,7 +18619,7 @@
}
]
}
-06_08__inlines__images__018: |-
+06_08_00__inlines__images__018: |-
{
"type": "doc",
"content": [
@@ -18655,7 +18655,7 @@
}
]
}
-06_08__inlines__images__019: |-
+06_08_00__inlines__images__019: |-
{
"type": "doc",
"content": [
@@ -18679,7 +18679,7 @@
}
]
}
-06_08__inlines__images__020: |-
+06_08_00__inlines__images__020: |-
{
"type": "doc",
"content": [
@@ -18715,7 +18715,7 @@
}
]
}
-06_08__inlines__images__021: |-
+06_08_00__inlines__images__021: |-
{
"type": "doc",
"content": [
@@ -18744,7 +18744,7 @@
}
]
}
-06_08__inlines__images__022: |-
+06_08_00__inlines__images__022: |-
{
"type": "doc",
"content": [
@@ -18790,7 +18790,7 @@
}
]
}
-06_09__inlines__autolinks__001: |-
+06_09_00__inlines__autolinks__001: |-
{
"type": "doc",
"content": [
@@ -18818,7 +18818,7 @@
}
]
}
-06_09__inlines__autolinks__002: |-
+06_09_00__inlines__autolinks__002: |-
{
"type": "doc",
"content": [
@@ -18846,7 +18846,7 @@
}
]
}
-06_09__inlines__autolinks__003: |-
+06_09_00__inlines__autolinks__003: |-
{
"type": "doc",
"content": [
@@ -18874,7 +18874,7 @@
}
]
}
-06_09__inlines__autolinks__004: |-
+06_09_00__inlines__autolinks__004: |-
{
"type": "doc",
"content": [
@@ -18902,7 +18902,7 @@
}
]
}
-06_09__inlines__autolinks__005: |-
+06_09_00__inlines__autolinks__005: |-
{
"type": "doc",
"content": [
@@ -18930,7 +18930,7 @@
}
]
}
-06_09__inlines__autolinks__006: |-
+06_09_00__inlines__autolinks__006: |-
{
"type": "doc",
"content": [
@@ -18958,7 +18958,7 @@
}
]
}
-06_09__inlines__autolinks__007: |-
+06_09_00__inlines__autolinks__007: |-
{
"type": "doc",
"content": [
@@ -18986,7 +18986,7 @@
}
]
}
-06_09__inlines__autolinks__008: |-
+06_09_00__inlines__autolinks__008: |-
{
"type": "doc",
"content": [
@@ -19014,7 +19014,7 @@
}
]
}
-06_09__inlines__autolinks__009: |-
+06_09_00__inlines__autolinks__009: |-
{
"type": "doc",
"content": [
@@ -19050,7 +19050,7 @@
}
]
}
-06_09__inlines__autolinks__010: |-
+06_09_00__inlines__autolinks__010: |-
{
"type": "doc",
"content": [
@@ -19078,7 +19078,7 @@
}
]
}
-06_09__inlines__autolinks__011: |-
+06_09_00__inlines__autolinks__011: |-
{
"type": "doc",
"content": [
@@ -19106,7 +19106,7 @@
}
]
}
-06_09__inlines__autolinks__012: |-
+06_09_00__inlines__autolinks__012: |-
{
"type": "doc",
"content": [
@@ -19134,7 +19134,7 @@
}
]
}
-06_09__inlines__autolinks__013: |-
+06_09_00__inlines__autolinks__013: |-
{
"type": "doc",
"content": [
@@ -19170,7 +19170,7 @@
}
]
}
-06_09__inlines__autolinks__014: |-
+06_09_00__inlines__autolinks__014: |-
{
"type": "doc",
"content": [
@@ -19185,7 +19185,7 @@
}
]
}
-06_09__inlines__autolinks__015: |-
+06_09_00__inlines__autolinks__015: |-
{
"type": "doc",
"content": [
@@ -19221,7 +19221,7 @@
}
]
}
-06_09__inlines__autolinks__016: |-
+06_09_00__inlines__autolinks__016: |-
{
"type": "doc",
"content": [
@@ -19236,7 +19236,7 @@
}
]
}
-06_09__inlines__autolinks__017: |-
+06_09_00__inlines__autolinks__017: |-
{
"type": "doc",
"content": [
@@ -19251,7 +19251,7 @@
}
]
}
-06_09__inlines__autolinks__018: |-
+06_09_00__inlines__autolinks__018: |-
{
"type": "doc",
"content": [
@@ -19279,7 +19279,7 @@
}
]
}
-06_09__inlines__autolinks__019: |-
+06_09_00__inlines__autolinks__019: |-
{
"type": "doc",
"content": [
@@ -19307,7 +19307,7 @@
}
]
}
-06_10__inlines__autolinks_extension__001: |-
+06_10_00__inlines__autolinks_extension__001: |-
{
"type": "doc",
"content": [
@@ -19335,7 +19335,7 @@
}
]
}
-06_10__inlines__autolinks_extension__002: |-
+06_10_00__inlines__autolinks_extension__002: |-
{
"type": "doc",
"content": [
@@ -19371,7 +19371,7 @@
}
]
}
-06_10__inlines__autolinks_extension__003: |-
+06_10_00__inlines__autolinks_extension__003: |-
{
"type": "doc",
"content": [
@@ -19437,7 +19437,7 @@
}
]
}
-06_10__inlines__autolinks_extension__004: |-
+06_10_00__inlines__autolinks_extension__004: |-
{
"type": "doc",
"content": [
@@ -19547,7 +19547,7 @@
}
]
}
-06_10__inlines__autolinks_extension__005: |-
+06_10_00__inlines__autolinks_extension__005: |-
{
"type": "doc",
"content": [
@@ -19575,7 +19575,7 @@
}
]
}
-06_10__inlines__autolinks_extension__006: |-
+06_10_00__inlines__autolinks_extension__006: |-
{
"type": "doc",
"content": [
@@ -19629,7 +19629,7 @@
}
]
}
-06_10__inlines__autolinks_extension__007: |-
+06_10_00__inlines__autolinks_extension__007: |-
{
"type": "doc",
"content": [
@@ -19661,7 +19661,7 @@
}
]
}
-06_10__inlines__autolinks_extension__008: |-
+06_10_00__inlines__autolinks_extension__008: |-
{
"type": "doc",
"content": [
@@ -19728,7 +19728,7 @@
}
]
}
-06_10__inlines__autolinks_extension__009: |-
+06_10_00__inlines__autolinks_extension__009: |-
{
"type": "doc",
"content": [
@@ -19756,7 +19756,7 @@
}
]
}
-06_10__inlines__autolinks_extension__010: |-
+06_10_00__inlines__autolinks_extension__010: |-
{
"type": "doc",
"content": [
@@ -19792,7 +19792,7 @@
}
]
}
-06_10__inlines__autolinks_extension__011: |-
+06_10_00__inlines__autolinks_extension__011: |-
{
"type": "doc",
"content": [
@@ -19864,7 +19864,7 @@
}
]
}
-06_11__inlines__raw_html__001: |-
+06_11_00__inlines__raw_html__001: |-
{
"type": "doc",
"content": [
@@ -19873,7 +19873,7 @@
}
]
}
-06_11__inlines__raw_html__002: |-
+06_11_00__inlines__raw_html__002: |-
{
"type": "doc",
"content": [
@@ -19882,7 +19882,7 @@
}
]
}
-06_11__inlines__raw_html__003: |-
+06_11_00__inlines__raw_html__003: |-
{
"type": "doc",
"content": [
@@ -19891,7 +19891,7 @@
}
]
}
-06_11__inlines__raw_html__004: |-
+06_11_00__inlines__raw_html__004: |-
{
"type": "doc",
"content": [
@@ -19900,7 +19900,7 @@
}
]
}
-06_11__inlines__raw_html__005: |-
+06_11_00__inlines__raw_html__005: |-
{
"type": "doc",
"content": [
@@ -19915,7 +19915,7 @@
}
]
}
-06_11__inlines__raw_html__006: |-
+06_11_00__inlines__raw_html__006: |-
{
"type": "doc",
"content": [
@@ -19930,7 +19930,7 @@
}
]
}
-06_11__inlines__raw_html__007: |-
+06_11_00__inlines__raw_html__007: |-
{
"type": "doc",
"content": [
@@ -19945,7 +19945,7 @@
}
]
}
-06_11__inlines__raw_html__008: |-
+06_11_00__inlines__raw_html__008: |-
{
"type": "doc",
"content": [
@@ -19960,7 +19960,7 @@
}
]
}
-06_11__inlines__raw_html__009: |-
+06_11_00__inlines__raw_html__009: |-
{
"type": "doc",
"content": [
@@ -19975,7 +19975,7 @@
}
]
}
-06_11__inlines__raw_html__010: |-
+06_11_00__inlines__raw_html__010: |-
{
"type": "doc",
"content": [
@@ -19990,7 +19990,7 @@
}
]
}
-06_11__inlines__raw_html__011: |-
+06_11_00__inlines__raw_html__011: |-
{
"type": "doc",
"content": [
@@ -19999,7 +19999,7 @@
}
]
}
-06_11__inlines__raw_html__012: |-
+06_11_00__inlines__raw_html__012: |-
{
"type": "doc",
"content": [
@@ -20014,7 +20014,7 @@
}
]
}
-06_11__inlines__raw_html__013: |-
+06_11_00__inlines__raw_html__013: |-
{
"type": "doc",
"content": [
@@ -20029,7 +20029,7 @@
}
]
}
-06_11__inlines__raw_html__014: |-
+06_11_00__inlines__raw_html__014: |-
{
"type": "doc",
"content": [
@@ -20044,7 +20044,7 @@
}
]
}
-06_11__inlines__raw_html__015: |-
+06_11_00__inlines__raw_html__015: |-
{
"type": "doc",
"content": [
@@ -20068,7 +20068,7 @@
}
]
}
-06_11__inlines__raw_html__016: |-
+06_11_00__inlines__raw_html__016: |-
{
"type": "doc",
"content": [
@@ -20083,7 +20083,7 @@
}
]
}
-06_11__inlines__raw_html__017: |-
+06_11_00__inlines__raw_html__017: |-
{
"type": "doc",
"content": [
@@ -20098,7 +20098,7 @@
}
]
}
-06_11__inlines__raw_html__018: |-
+06_11_00__inlines__raw_html__018: |-
{
"type": "doc",
"content": [
@@ -20113,7 +20113,7 @@
}
]
}
-06_11__inlines__raw_html__019: |-
+06_11_00__inlines__raw_html__019: |-
{
"type": "doc",
"content": [
@@ -20128,7 +20128,7 @@
}
]
}
-06_11__inlines__raw_html__020: |-
+06_11_00__inlines__raw_html__020: |-
{
"type": "doc",
"content": [
@@ -20143,7 +20143,7 @@
}
]
}
-06_11__inlines__raw_html__021: |-
+06_11_00__inlines__raw_html__021: |-
{
"type": "doc",
"content": [
@@ -20158,7 +20158,7 @@
}
]
}
-06_12__inlines__disallowed_raw_html_extension__001: |-
+06_12_00__inlines__disallowed_raw_html_extension__001: |-
{
"type": "doc",
"content": [
@@ -20178,7 +20178,7 @@
}
]
}
-06_13__inlines__hard_line_breaks__001: |-
+06_13_00__inlines__hard_line_breaks__001: |-
{
"type": "doc",
"content": [
@@ -20200,7 +20200,7 @@
}
]
}
-06_13__inlines__hard_line_breaks__002: |-
+06_13_00__inlines__hard_line_breaks__002: |-
{
"type": "doc",
"content": [
@@ -20222,7 +20222,7 @@
}
]
}
-06_13__inlines__hard_line_breaks__003: |-
+06_13_00__inlines__hard_line_breaks__003: |-
{
"type": "doc",
"content": [
@@ -20244,7 +20244,7 @@
}
]
}
-06_13__inlines__hard_line_breaks__004: |-
+06_13_00__inlines__hard_line_breaks__004: |-
{
"type": "doc",
"content": [
@@ -20266,7 +20266,7 @@
}
]
}
-06_13__inlines__hard_line_breaks__005: |-
+06_13_00__inlines__hard_line_breaks__005: |-
{
"type": "doc",
"content": [
@@ -20288,7 +20288,7 @@
}
]
}
-06_13__inlines__hard_line_breaks__006: |-
+06_13_00__inlines__hard_line_breaks__006: |-
{
"type": "doc",
"content": [
@@ -20325,7 +20325,7 @@
}
]
}
-06_13__inlines__hard_line_breaks__007: |-
+06_13_00__inlines__hard_line_breaks__007: |-
{
"type": "doc",
"content": [
@@ -20362,7 +20362,7 @@
}
]
}
-06_13__inlines__hard_line_breaks__008: |-
+06_13_00__inlines__hard_line_breaks__008: |-
{
"type": "doc",
"content": [
@@ -20382,7 +20382,7 @@
}
]
}
-06_13__inlines__hard_line_breaks__009: |-
+06_13_00__inlines__hard_line_breaks__009: |-
{
"type": "doc",
"content": [
@@ -20402,7 +20402,7 @@
}
]
}
-06_13__inlines__hard_line_breaks__010: |-
+06_13_00__inlines__hard_line_breaks__010: |-
{
"type": "doc",
"content": [
@@ -20411,7 +20411,7 @@
}
]
}
-06_13__inlines__hard_line_breaks__011: |-
+06_13_00__inlines__hard_line_breaks__011: |-
{
"type": "doc",
"content": [
@@ -20420,7 +20420,7 @@
}
]
}
-06_13__inlines__hard_line_breaks__012: |-
+06_13_00__inlines__hard_line_breaks__012: |-
{
"type": "doc",
"content": [
@@ -20435,7 +20435,7 @@
}
]
}
-06_13__inlines__hard_line_breaks__013: |-
+06_13_00__inlines__hard_line_breaks__013: |-
{
"type": "doc",
"content": [
@@ -20450,7 +20450,7 @@
}
]
}
-06_13__inlines__hard_line_breaks__014: |-
+06_13_00__inlines__hard_line_breaks__014: |-
{
"type": "doc",
"content": [
@@ -20468,7 +20468,7 @@
}
]
}
-06_13__inlines__hard_line_breaks__015: |-
+06_13_00__inlines__hard_line_breaks__015: |-
{
"type": "doc",
"content": [
@@ -20486,7 +20486,7 @@
}
]
}
-06_14__inlines__soft_line_breaks__001: |-
+06_14_00__inlines__soft_line_breaks__001: |-
{
"type": "doc",
"content": [
@@ -20501,7 +20501,7 @@
}
]
}
-06_14__inlines__soft_line_breaks__002: |-
+06_14_00__inlines__soft_line_breaks__002: |-
{
"type": "doc",
"content": [
@@ -20516,7 +20516,7 @@
}
]
}
-06_15__inlines__textual_content__001: |-
+06_15_00__inlines__textual_content__001: |-
{
"type": "doc",
"content": [
@@ -20531,7 +20531,7 @@
}
]
}
-06_15__inlines__textual_content__002: |-
+06_15_00__inlines__textual_content__002: |-
{
"type": "doc",
"content": [
@@ -20546,7 +20546,7 @@
}
]
}
-06_15__inlines__textual_content__003: |-
+06_15_00__inlines__textual_content__003: |-
{
"type": "doc",
"content": [
@@ -20561,7 +20561,7 @@
}
]
}
-07_01__gitlab_specific_markdown__footnotes__001: |-
+07_01_00__gitlab_specific_markdown__footnotes__001: |-
{
"type": "doc",
"content": [
@@ -20601,7 +20601,7 @@
}
]
}
-07_02__gitlab_specific_markdown__task_list_items__001: |-
+07_02_00__gitlab_specific_markdown__task_list_items__001: |-
{
"type": "doc",
"content": [
@@ -20634,7 +20634,7 @@
}
]
}
-07_02__gitlab_specific_markdown__task_list_items__002: |-
+07_02_00__gitlab_specific_markdown__task_list_items__002: |-
{
"type": "doc",
"content": [
@@ -20667,11 +20667,11 @@
}
]
}
-07_02__gitlab_specific_markdown__task_list_items__003: |-
+07_02_00__gitlab_specific_markdown__task_list_items__003: |-
Inapplicable task list items not yet implemented for WYSYWIG
-07_02__gitlab_specific_markdown__task_list_items__004: |-
+07_02_00__gitlab_specific_markdown__task_list_items__004: |-
Inapplicable task list items not yet implemented for WYSYWIG
-07_03__gitlab_specific_markdown__front_matter__001: |-
+07_03_00__gitlab_specific_markdown__front_matter__001: |-
{
"type": "doc",
"content": [
@@ -20691,7 +20691,7 @@
}
]
}
-07_03__gitlab_specific_markdown__front_matter__002: |-
+07_03_00__gitlab_specific_markdown__front_matter__002: |-
{
"type": "doc",
"content": [
@@ -20711,7 +20711,7 @@
}
]
}
-07_03__gitlab_specific_markdown__front_matter__003: |-
+07_03_00__gitlab_specific_markdown__front_matter__003: |-
{
"type": "doc",
"content": [
@@ -20731,7 +20731,7 @@
}
]
}
-07_03__gitlab_specific_markdown__front_matter__004: |-
+07_03_00__gitlab_specific_markdown__front_matter__004: |-
{
"type": "doc",
"content": [
@@ -20761,7 +20761,7 @@
}
]
}
-07_03__gitlab_specific_markdown__front_matter__005: |-
+07_03_00__gitlab_specific_markdown__front_matter__005: |-
{
"type": "doc",
"content": [
@@ -20782,3 +20782,247 @@
}
]
}
+07_04_00__gitlab_specific_markdown__audio__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "audio",
+ "attrs": {
+ "uploading": false,
+ "src": "audio.oga",
+ "canonicalSrc": "audio.oga",
+ "alt": "audio"
+ }
+ }
+ ]
+ }
+ ]
+ }
+07_04_00__gitlab_specific_markdown__audio__002: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "audio",
+ "url": "audio.oga",
+ "title": "audio title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[audio]: audio.oga \"audio title\""
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "audio",
+ "attrs": {
+ "uploading": false,
+ "src": "audio.oga",
+ "canonicalSrc": "audio",
+ "alt": "audio"
+ }
+ }
+ ]
+ }
+ ]
+ }
+07_05_00__gitlab_specific_markdown__video__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "video",
+ "attrs": {
+ "uploading": false,
+ "src": "video.m4v",
+ "canonicalSrc": "video.m4v",
+ "alt": "video"
+ }
+ }
+ ]
+ }
+ ]
+ }
+07_05_00__gitlab_specific_markdown__video__002: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "video",
+ "url": "video.mov",
+ "title": "video title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[video]: video.mov \"video title\""
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "video",
+ "attrs": {
+ "uploading": false,
+ "src": "video.mov",
+ "canonicalSrc": "video",
+ "alt": "video"
+ }
+ }
+ ]
+ }
+ ]
+ }
+07_06_00__gitlab_specific_markdown__table_of_contents__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "tableOfContents"
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "Heading 1"
+ }
+ ]
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 2
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "Heading 2"
+ }
+ ]
+ }
+ ]
+ }
+07_06_00__gitlab_specific_markdown__table_of_contents__002: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "tableOfContents"
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "Heading 1"
+ }
+ ]
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 2
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "Heading 2"
+ }
+ ]
+ }
+ ]
+ }
+07_06_00__gitlab_specific_markdown__table_of_contents__003: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "[["
+ },
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "italic"
+ }
+ ],
+ "text": "TOC"
+ },
+ {
+ "type": "text",
+ "text": "]]\ntext"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "text\n[TOC]"
+ }
+ ]
+ }
+ ]
+ }
+07_06_00__gitlab_specific_markdown__table_of_contents__004: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "tableOfContents"
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "Heading 1"
+ }
+ ]
+ }
+ ]
+ }
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__001: |-
+ Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__002: |-
+ Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__003: |-
+ Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__004: |-
+ Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__005: |-
+ Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__006: |-
+ Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
diff --git a/glfm_specification/input/gitlab_flavored_markdown/glfm_canonical_examples.txt b/glfm_specification/input/gitlab_flavored_markdown/glfm_canonical_examples.txt
index 332b311dff2..10a46dcca6b 100644
--- a/glfm_specification/input/gitlab_flavored_markdown/glfm_canonical_examples.txt
+++ b/glfm_specification/input/gitlab_flavored_markdown/glfm_canonical_examples.txt
@@ -13,7 +13,7 @@ examples may be split into multiple top-level headings in the future.
See
[the footnotes section of the user-facing documentation for GitLab Flavored Markdown](https://docs.gitlab.com/ee/user/markdown.html#footnotes).
-```````````````````````````````` example gitlab footnote
+```````````````````````````````` example gitlab
footnote reference tag [^fortytwo]
[^fortytwo]: footnote text
@@ -55,7 +55,7 @@ The following are some basic examples; more examples may be added in the future.
Incomplete task:
-```````````````````````````````` example gitlab tasklist
+```````````````````````````````` example gitlab
- [ ] incomplete
.
<ul>
@@ -69,7 +69,7 @@ incomplete
Completed task:
-```````````````````````````````` example gitlab tasklist
+```````````````````````````````` example gitlab
- [x] completed
.
<ul>
@@ -83,7 +83,7 @@ completed
Inapplicable task:
-```````````````````````````````` example gitlab tasklist
+```````````````````````````````` example gitlab
- [~] inapplicable
.
<ul>
@@ -100,7 +100,7 @@ inapplicable
Inapplicable task in a "loose" list. Note that the `<del>` tag is not applied to the
loose text; it has strikethrough applied with CSS.
-```````````````````````````````` example gitlab tasklist
+```````````````````````````````` example gitlab
- [~] inapplicable
text in loose list
@@ -131,7 +131,7 @@ This data can be used by static site generators like Jekyll, Hugo, and many othe
YAML front matter:
-```````````````````````````````` example gitlab frontmatter
+```````````````````````````````` example gitlab
---
title: YAML front matter
---
@@ -145,7 +145,7 @@ title: YAML front matter
TOML front matter:
-```````````````````````````````` example gitlab frontmatter
+```````````````````````````````` example gitlab
+++
title: TOML front matter
+++
@@ -159,7 +159,7 @@ title: TOML front matter
JSON front matter:
-```````````````````````````````` example gitlab frontmatter
+```````````````````````````````` example gitlab
;;;
{
"title": "JSON front matter"
@@ -177,7 +177,7 @@ JSON front matter:
Front matter blocks should be inserted at the top of the document:
-```````````````````````````````` example gitlab frontmatter
+```````````````````````````````` example gitlab
text
---
@@ -191,7 +191,7 @@ title: YAML front matter
Front matter block delimiters shouldn’t be preceded by space characters:
-```````````````````````````````` example gitlab frontmatter
+```````````````````````````````` example gitlab
---
title: YAML front matter
---
@@ -199,3 +199,191 @@ title: YAML front matter
<hr>
<h2>title: YAML front matter</h2>
````````````````````````````````
+
+## Audio
+
+See
+[audio](https://docs.gitlab.com/ee/user/markdown.html#audio) in the GitLab Flavored Markdown documentation.
+
+GLFM renders image elements as an audio player as long as the resource’s file extension is
+one of the following supported audio extensions `.mp3`, `.oga`, `.ogg`, `.spx`, and `.wav`.
+Audio ignore the alternative text part of an image declaration.
+
+```````````````````````````````` example gitlab
+![audio](audio.oga "audio title")
+.
+<p><audio src="audio.oga" title="audio title"></audio></p>
+````````````````````````````````
+
+Reference definitions work audio as well:
+
+```````````````````````````````` example gitlab
+[audio]: audio.oga "audio title"
+
+![audio][audio]
+.
+<p><audio src="audio.oga" title="audio title"></audio></p>
+````````````````````````````````
+
+## Video
+
+See
+[videos](https://docs.gitlab.com/ee/user/markdown.html#videos) in the GitLab Flavored Markdown documentation.
+
+GLFM renders image elements as a video player as long as the resource’s file extension is
+one of the following supported video extensions `.mp4`, `.m4v`, `.mov`, `.webm`, and `.ogv`.
+Videos ignore the alternative text part of an image declaration.
+
+
+```````````````````````````````` example gitlab
+![video](video.m4v "video title")
+.
+<p><video src="video.m4v" title="video title"></video></p>
+````````````````````````````````
+
+Reference definitions work video as well:
+
+```````````````````````````````` example gitlab
+[video]: video.mov "video title"
+
+![video][video]
+.
+<p><video src="video.mov" title="video title"></video></p>
+````````````````````````````````
+
+## Table of contents
+
+See
+[table of contents](https://docs.gitlab.com/ee/user/markdown.html#table-of-contents)
+in the GitLab Flavored Markdown documentation.
+
+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.
+
+```````````````````````````````` example gitlab
+[TOC]
+
+# Heading 1
+
+## Heading 2
+.
+<nav>
+ <ul>
+ <li><a href="#heading-1">Heading 1</a></li>
+ <ul>
+ <li><a href="#heading-2">Heading 2</a></li>
+ </ul>
+ </ul>
+</nav>
+<h1>Heading 1</h1>
+<h2>Heading 2</h2>
+````````````````````````````````
+
+```````````````````````````````` example gitlab
+[[_TOC_]]
+
+# Heading 1
+
+## Heading 2
+.
+<nav>
+ <ul>
+ <li><a href="#heading-1">Heading 1</a></li>
+ <ul>
+ <li><a href="#heading-2">Heading 2</a></li>
+ </ul>
+ </ul>
+</nav>
+<h1>Heading 1</h1>
+<h2>Heading 2</h2>
+````````````````````````````````
+
+A table of contents is a block element. It should preceded and followed by a blank
+line.
+
+```````````````````````````````` example gitlab
+[[_TOC_]]
+text
+
+text
+[TOC]
+.
+<p>[[<em>TOC</em>]]text</p>
+<p>text[TOC]</p>
+````````````````````````````````
+
+A table of contents can be indented with up to three spaces.
+
+```````````````````````````````` example gitlab
+ [[_TOC_]]
+
+# Heading 1
+.
+<nav>
+ <ul>
+ <li><a href="#heading-1">Heading 1</a></li>
+ </ul>
+</nav>
+<h1>Heading 1</h1>
+````````````````````````````````
+
+# Examples Using Internal Extensions
+
+## Markdown Preview API Request Overrides
+
+This section contains examples of all controllers which use `PreviewMarkdown` module
+and use different `markdown_context_params`. They exercise the various `preview_markdown`
+endpoints via `glfm_example_metadata.yml`.
+
+
+`preview_markdown` exercising `groups` API endpoint and `UploadLinkFilter`:
+
+```````````````````````````````` example gitlab
+[groups-test-file](/uploads/groups-test-file)
+.
+<p><a href="groups-test-file">groups-test-file</a></p>
+````````````````````````````````
+
+`preview_markdown` exercising `projects` API endpoint and `RepositoryLinkFilter`:
+
+```````````````````````````````` example gitlab
+[projects-test-file](projects-test-file)
+.
+<p><a href="projects-test-file">projects-test-file</a></p>
+````````````````````````````````
+
+`preview_markdown` exercising `projects` API endpoint and `SnippetReferenceFilter`:
+
+```````````````````````````````` example gitlab
+This project snippet ID reference IS filtered: $88888
+.
+<p>This project snippet ID reference IS filtered: $88888</p>
+````````````````````````````````
+
+`preview_markdown` exercising personal (non-project) `snippets` 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
+`SnippetReferenceFilter`, even if the ID is valid.
+
+```````````````````````````````` example gitlab
+This personal snippet ID reference is not filtered: $99999
+.
+<p>This personal snippet ID reference is not filtered: $99999</p>
+````````````````````````````````
+
+`preview_markdown` exercising project `wikis` API endpoint and `WikiLinkFilter`:
+
+```````````````````````````````` example gitlab
+[project-wikis-test-file](project-wikis-test-file)
+.
+<p><a href="project-wikis-test-file">project-wikis-test-file</a></p>
+````````````````````````````````
+
+`preview_markdown` exercising group `wikis` API endpoint and `WikiLinkFilter`. This example
+also requires an EE license enabling the `group_wikis` feature:
+
+```````````````````````````````` example gitlab
+[group-wikis-test-file](group-wikis-test-file)
+.
+<p><a href="group-wikis-test-file">group-wikis-test-file</a></p>
+````````````````````````````````
diff --git a/glfm_specification/input/gitlab_flavored_markdown/glfm_example_metadata.yml b/glfm_specification/input/gitlab_flavored_markdown/glfm_example_metadata.yml
new file mode 100644
index 00000000000..3c043f5fba1
--- /dev/null
+++ b/glfm_specification/input/gitlab_flavored_markdown/glfm_example_metadata.yml
@@ -0,0 +1,14 @@
+---
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__001:
+ api_request_override_path: /groups/glfm_group/preview_markdown
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__002:
+ api_request_override_path: /glfm_group/glfm_project/preview_markdown
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__003:
+ api_request_override_path: /glfm_group/glfm_project/preview_markdown
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__004:
+ api_request_override_path: /-/snippets/preview_markdown
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__005:
+ api_request_override_path: /glfm_group/glfm_project/-/wikis/new_page/preview_markdown
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__006:
+ ee: true
+ api_request_override_path: /groups/glfm_group/-/wikis/new_page/preview_markdown
diff --git a/glfm_specification/input/gitlab_flavored_markdown/glfm_example_normalizations.yml b/glfm_specification/input/gitlab_flavored_markdown/glfm_example_normalizations.yml
index 15df659f0f4..d576a8ddb51 100644
--- a/glfm_specification/input/gitlab_flavored_markdown/glfm_example_normalizations.yml
+++ b/glfm_specification/input/gitlab_flavored_markdown/glfm_example_normalizations.yml
@@ -9,19 +9,3 @@
00_uri: &00_uri
- regex: '(href|data-src)(=")(.*?)(test-file\.(png|zip)")'
replacement: '\1\2URI_PREFIX\4'
-07_01__gitlab_specific_markdown__footnotes__001:
- html:
- static:
- shared:
- 07_01_href: &07_01_href
- - regex: '(href)(=")(.+?)(")'
- replacement: '\1\2REF\4'
- 07_01_id: &07_01_id
- - regex: '(id)(=")(.+?)(")'
- replacement: '\1\2ID\4'
- canonical:
- 07_01_href: *07_01_href
- 07_01_id: *07_01_id
- snapshot:
- 07_01_href: *07_01_href
- 07_01_id: *07_01_id
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 3881819e38a..a74c3492324 100644
--- a/glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml
+++ b/glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml
@@ -1,5 +1,5 @@
---
-02_01__preliminaries__tabs__001:
+02_01_00__preliminaries__tabs__001:
# NOTE: False values are optional, they are only included here for reference. See
# https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#glfm_example_statusyml
# for more details.
@@ -12,15 +12,51 @@
skip_running_snapshot_static_html_tests: false # NOT YET SUPPORTED
skip_running_snapshot_wysiwyg_html_tests: false
skip_running_snapshot_prosemirror_json_tests: false
-07_02__gitlab_specific_markdown__task_list_items__003:
+07_02_00__gitlab_specific_markdown__task_list_items__003:
skip_update_example_snapshot_html_wysiwyg: Inapplicable task list items not yet implemented for WYSYWIG
skip_update_example_snapshot_prosemirror_json: Inapplicable task list items not yet implemented for WYSYWIG
skip_running_conformance_wysiwyg_tests: Inapplicable task list items not yet implemented for WYSYWIG
skip_running_snapshot_wysiwyg_html_tests: Inapplicable task list items not yet implemented for WYSYWIG
skip_running_snapshot_prosemirror_json_tests: Inapplicable task list items not yet implemented for WYSYWIG
-07_02__gitlab_specific_markdown__task_list_items__004:
+07_02_00__gitlab_specific_markdown__task_list_items__004:
skip_update_example_snapshot_html_wysiwyg: Inapplicable task list items not yet implemented for WYSYWIG
skip_update_example_snapshot_prosemirror_json: Inapplicable task list items not yet implemented for WYSYWIG
skip_running_conformance_wysiwyg_tests: Inapplicable task list items not yet implemented for WYSYWIG
skip_running_snapshot_wysiwyg_html_tests: Inapplicable task list items not yet implemented for WYSYWIG
skip_running_snapshot_prosemirror_json_tests: Inapplicable task list items not yet implemented for WYSYWIG
+08_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__001:
+ skip_update_example_snapshot_html_wysiwyg: Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+ skip_update_example_snapshot_prosemirror_json: Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+ 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_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__002:
+ skip_update_example_snapshot_html_wysiwyg: Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+ skip_update_example_snapshot_prosemirror_json: Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+ 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_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__003:
+ skip_update_example_snapshot_html_wysiwyg: Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+ skip_update_example_snapshot_prosemirror_json: Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+ 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_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__004:
+ skip_update_example_snapshot_html_wysiwyg: Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+ skip_update_example_snapshot_prosemirror_json: Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+ 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_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__005:
+ skip_update_example_snapshot_html_wysiwyg: Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+ skip_update_example_snapshot_prosemirror_json: Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+ 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_01_00__examples_using_internal_extensions__markdown_preview_api_request_overrides__006:
+ skip_update_example_snapshot_html_wysiwyg: Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+ skip_update_example_snapshot_prosemirror_json: Not yet implemented. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/92507#note_1068159236
+ 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
diff --git a/glfm_specification/output/spec.txt b/glfm_specification/output/spec.txt
index 32cb6a0594e..af4eba06758 100644
--- a/glfm_specification/output/spec.txt
+++ b/glfm_specification/output/spec.txt
@@ -9615,7 +9615,7 @@ examples may be split into multiple top-level headings in the future.
See
[the footnotes section of the user-facing documentation for GitLab Flavored Markdown](https://docs.gitlab.com/ee/user/markdown.html#footnotes).
-```````````````````````````````` example gitlab footnote
+```````````````````````````````` example gitlab
footnote reference tag [^fortytwo]
[^fortytwo]: footnote text
@@ -9657,7 +9657,7 @@ The following are some basic examples; more examples may be added in the future.
Incomplete task:
-```````````````````````````````` example gitlab tasklist
+```````````````````````````````` example gitlab
- [ ] incomplete
.
<ul>
@@ -9671,7 +9671,7 @@ incomplete
Completed task:
-```````````````````````````````` example gitlab tasklist
+```````````````````````````````` example gitlab
- [x] completed
.
<ul>
@@ -9685,7 +9685,7 @@ completed
Inapplicable task:
-```````````````````````````````` example gitlab tasklist
+```````````````````````````````` example gitlab
- [~] inapplicable
.
<ul>
@@ -9702,7 +9702,7 @@ inapplicable
Inapplicable task in a "loose" list. Note that the `<del>` tag is not applied to the
loose text; it has strikethrough applied with CSS.
-```````````````````````````````` example gitlab tasklist
+```````````````````````````````` example gitlab
- [~] inapplicable
text in loose list
@@ -9733,7 +9733,7 @@ This data can be used by static site generators like Jekyll, Hugo, and many othe
YAML front matter:
-```````````````````````````````` example gitlab frontmatter
+```````````````````````````````` example gitlab
---
title: YAML front matter
---
@@ -9747,7 +9747,7 @@ title: YAML front matter
TOML front matter:
-```````````````````````````````` example gitlab frontmatter
+```````````````````````````````` example gitlab
+++
title: TOML front matter
+++
@@ -9761,7 +9761,7 @@ title: TOML front matter
JSON front matter:
-```````````````````````````````` example gitlab frontmatter
+```````````````````````````````` example gitlab
;;;
{
"title": "JSON front matter"
@@ -9779,7 +9779,7 @@ JSON front matter:
Front matter blocks should be inserted at the top of the document:
-```````````````````````````````` example gitlab frontmatter
+```````````````````````````````` example gitlab
text
---
@@ -9793,7 +9793,7 @@ title: YAML front matter
Front matter block delimiters shouldn’t be preceded by space characters:
-```````````````````````````````` example gitlab frontmatter
+```````````````````````````````` example gitlab
---
title: YAML front matter
---
@@ -9802,6 +9802,194 @@ title: YAML front matter
<h2>title: YAML front matter</h2>
````````````````````````````````
+## Audio
+
+See
+[audio](https://docs.gitlab.com/ee/user/markdown.html#audio) in the GitLab Flavored Markdown documentation.
+
+GLFM renders image elements as an audio player as long as the resource’s file extension is
+one of the following supported audio extensions `.mp3`, `.oga`, `.ogg`, `.spx`, and `.wav`.
+Audio ignore the alternative text part of an image declaration.
+
+```````````````````````````````` example gitlab
+![audio](audio.oga "audio title")
+.
+<p><audio src="audio.oga" title="audio title"></audio></p>
+````````````````````````````````
+
+Reference definitions work audio as well:
+
+```````````````````````````````` example gitlab
+[audio]: audio.oga "audio title"
+
+![audio][audio]
+.
+<p><audio src="audio.oga" title="audio title"></audio></p>
+````````````````````````````````
+
+## Video
+
+See
+[videos](https://docs.gitlab.com/ee/user/markdown.html#videos) in the GitLab Flavored Markdown documentation.
+
+GLFM renders image elements as a video player as long as the resource’s file extension is
+one of the following supported video extensions `.mp4`, `.m4v`, `.mov`, `.webm`, and `.ogv`.
+Videos ignore the alternative text part of an image declaration.
+
+
+```````````````````````````````` example gitlab
+![video](video.m4v "video title")
+.
+<p><video src="video.m4v" title="video title"></video></p>
+````````````````````````````````
+
+Reference definitions work video as well:
+
+```````````````````````````````` example gitlab
+[video]: video.mov "video title"
+
+![video][video]
+.
+<p><video src="video.mov" title="video title"></video></p>
+````````````````````````````````
+
+## Table of contents
+
+See
+[table of contents](https://docs.gitlab.com/ee/user/markdown.html#table-of-contents)
+in the GitLab Flavored Markdown documentation.
+
+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.
+
+```````````````````````````````` example gitlab
+[TOC]
+
+# Heading 1
+
+## Heading 2
+.
+<nav>
+ <ul>
+ <li><a href="#heading-1">Heading 1</a></li>
+ <ul>
+ <li><a href="#heading-2">Heading 2</a></li>
+ </ul>
+ </ul>
+</nav>
+<h1>Heading 1</h1>
+<h2>Heading 2</h2>
+````````````````````````````````
+
+```````````````````````````````` example gitlab
+[[_TOC_]]
+
+# Heading 1
+
+## Heading 2
+.
+<nav>
+ <ul>
+ <li><a href="#heading-1">Heading 1</a></li>
+ <ul>
+ <li><a href="#heading-2">Heading 2</a></li>
+ </ul>
+ </ul>
+</nav>
+<h1>Heading 1</h1>
+<h2>Heading 2</h2>
+````````````````````````````````
+
+A table of contents is a block element. It should preceded and followed by a blank
+line.
+
+```````````````````````````````` example gitlab
+[[_TOC_]]
+text
+
+text
+[TOC]
+.
+<p>[[<em>TOC</em>]]text</p>
+<p>text[TOC]</p>
+````````````````````````````````
+
+A table of contents can be indented with up to three spaces.
+
+```````````````````````````````` example gitlab
+ [[_TOC_]]
+
+# Heading 1
+.
+<nav>
+ <ul>
+ <li><a href="#heading-1">Heading 1</a></li>
+ </ul>
+</nav>
+<h1>Heading 1</h1>
+````````````````````````````````
+
+# Examples Using Internal Extensions
+
+## Markdown Preview API Request Overrides
+
+This section contains examples of all controllers which use `PreviewMarkdown` module
+and use different `markdown_context_params`. They exercise the various `preview_markdown`
+endpoints via `glfm_example_metadata.yml`.
+
+
+`preview_markdown` exercising `groups` API endpoint and `UploadLinkFilter`:
+
+```````````````````````````````` example gitlab
+[groups-test-file](/uploads/groups-test-file)
+.
+<p><a href="groups-test-file">groups-test-file</a></p>
+````````````````````````````````
+
+`preview_markdown` exercising `projects` API endpoint and `RepositoryLinkFilter`:
+
+```````````````````````````````` example gitlab
+[projects-test-file](projects-test-file)
+.
+<p><a href="projects-test-file">projects-test-file</a></p>
+````````````````````````````````
+
+`preview_markdown` exercising `projects` API endpoint and `SnippetReferenceFilter`:
+
+```````````````````````````````` example gitlab
+This project snippet ID reference IS filtered: $88888
+.
+<p>This project snippet ID reference IS filtered: $88888</p>
+````````````````````````````````
+
+`preview_markdown` exercising personal (non-project) `snippets` 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
+`SnippetReferenceFilter`, even if the ID is valid.
+
+```````````````````````````````` example gitlab
+This personal snippet ID reference is not filtered: $99999
+.
+<p>This personal snippet ID reference is not filtered: $99999</p>
+````````````````````````````````
+
+`preview_markdown` exercising project `wikis` API endpoint and `WikiLinkFilter`:
+
+```````````````````````````````` example gitlab
+[project-wikis-test-file](project-wikis-test-file)
+.
+<p><a href="project-wikis-test-file">project-wikis-test-file</a></p>
+````````````````````````````````
+
+`preview_markdown` exercising group `wikis` API endpoint and `WikiLinkFilter`. This example
+also requires an EE license enabling the `group_wikis` feature:
+
+```````````````````````````````` example gitlab
+[group-wikis-test-file](group-wikis-test-file)
+.
+<p><a href="group-wikis-test-file">group-wikis-test-file</a></p>
+````````````````````````````````
+
<!-- END TESTS -->
# Appendix: A parsing strategy
diff --git a/lib/api/admin/batched_background_migrations.rb b/lib/api/admin/batched_background_migrations.rb
new file mode 100644
index 00000000000..675f3365bd3
--- /dev/null
+++ b/lib/api/admin/batched_background_migrations.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+
+module API
+ module Admin
+ class BatchedBackgroundMigrations < ::API::Base
+ feature_category :database
+ urgency :low
+
+ before do
+ authenticated_as_admin!
+ end
+
+ namespace 'admin' do
+ resources 'batched_background_migrations/:id' do
+ desc 'Retrieve a batched background migration'
+ params do
+ optional :database,
+ type: String,
+ values: Gitlab::Database.all_database_names,
+ desc: 'The name of the database',
+ default: 'main'
+ requires :id,
+ type: Integer,
+ desc: 'The batched background migration id'
+ end
+ get do
+ Gitlab::Database::SharedModel.using_connection(base_model.connection) do
+ present_entity(batched_background_migration)
+ end
+ end
+ end
+
+ resources 'batched_background_migrations' do
+ desc 'Get the list of the batched background migrations'
+ params do
+ optional :database,
+ type: String,
+ values: Gitlab::Database.all_database_names,
+ desc: 'The name of the database, the default `main`',
+ default: 'main'
+ end
+ get do
+ Gitlab::Database::SharedModel.using_connection(base_model.connection) do
+ migrations = Database::BatchedBackgroundMigrationsFinder.new(connection: base_model.connection).execute
+ present_entity(migrations)
+ end
+ end
+ end
+
+ resources 'batched_background_migrations/:id/resume' do
+ desc 'Resume a batched background migration'
+ params do
+ optional :database,
+ type: String,
+ values: Gitlab::Database.all_database_names,
+ desc: 'The name of the database',
+ default: 'main'
+ requires :id,
+ type: Integer,
+ desc: 'The batched background migration id'
+ end
+ put do
+ Gitlab::Database::SharedModel.using_connection(base_model.connection) do
+ batched_background_migration.execute!
+ present_entity(batched_background_migration)
+ end
+ end
+ end
+
+ resources 'batched_background_migrations/:id/pause' do
+ desc 'Pause a batched background migration'
+ params do
+ optional :database,
+ type: String,
+ values: Gitlab::Database.all_database_names,
+ desc: 'The name of the database',
+ default: 'main'
+ requires :id,
+ type: Integer,
+ desc: 'The batched background migration id'
+ end
+ put do
+ Gitlab::Database::SharedModel.using_connection(base_model.connection) do
+ batched_background_migration.pause!
+ present_entity(batched_background_migration)
+ end
+ end
+ end
+ end
+
+ helpers do
+ def batched_background_migration
+ @batched_background_migration ||= Gitlab::Database::BackgroundMigration::BatchedMigration.find(params[:id])
+ end
+
+ def base_model
+ database = params[:database] || Gitlab::Database::MAIN_DATABASE_NAME
+ @base_model ||= Gitlab::Database.database_base_models[database]
+ end
+
+ def present_entity(result)
+ present result,
+ with: ::API::Entities::BatchedBackgroundMigration
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/api.rb b/lib/api/api.rb
index e4158eee37f..443bf1d649a 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -167,6 +167,7 @@ module API
# Keep in alphabetical order
mount ::API::AccessRequests
+ mount ::API::Admin::BatchedBackgroundMigrations
mount ::API::Admin::Ci::Variables
mount ::API::Admin::InstanceClusters
mount ::API::Admin::PlanLimits
@@ -237,7 +238,6 @@ module API
mount ::API::ImportGithub
mount ::API::Integrations
mount ::API::Integrations::JiraConnect::Subscriptions
- mount ::API::Integrations::Slack::Events
mount ::API::Invitations
mount ::API::IssueLinks
mount ::API::Issues
@@ -263,6 +263,7 @@ module API
mount ::API::PackageFiles
mount ::API::Pages
mount ::API::PagesDomains
+ mount ::API::PersonalAccessTokens::SelfRevocation
mount ::API::PersonalAccessTokens
mount ::API::ProjectClusters
mount ::API::ProjectContainerRepositories
@@ -290,6 +291,7 @@ module API
mount ::API::ResourceLabelEvents
mount ::API::ResourceMilestoneEvents
mount ::API::ResourceStateEvents
+ mount ::API::RpmProjectPackages
mount ::API::RubygemPackages
mount ::API::Search
mount ::API::Settings
@@ -316,6 +318,7 @@ module API
mount ::API::Users
mount ::API::Version
mount ::API::Wikis
+ mount ::API::Ml::Mlflow
end
mount ::API::Internal::Base
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index b8444351029..5588818cbaf 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -52,25 +52,21 @@ module API
merged_branch_names = repository.merged_branch_names(branches.map(&:name))
- if Feature.enabled?(:api_caching_branches, user_project, type: :development)
- present_cached(
- branches,
- with: Entities::Branch,
- current_user: current_user,
- project: user_project,
- merged_branch_names: merged_branch_names,
- expires_in: 10.minutes,
- cache_context: -> (branch) { [current_user&.cache_key, merged_branch_names.include?(branch.name)] }
- )
- else
- present(
- branches,
- with: Entities::Branch,
- current_user: current_user,
- project: user_project,
- merged_branch_names: merged_branch_names
- )
- end
+ expiry_time = if Feature.enabled?(:increase_branch_cache_expiry, type: :ops)
+ 60.minutes
+ else
+ 10.minutes
+ end
+
+ present_cached(
+ branches,
+ with: Entities::Branch,
+ current_user: current_user,
+ project: user_project,
+ merged_branch_names: merged_branch_names,
+ expires_in: expiry_time,
+ cache_context: -> (branch) { [current_user&.cache_key, merged_branch_names.include?(branch.name)] }
+ )
end
end
@@ -146,7 +142,8 @@ module API
branch = find_branch!(params[:branch])
protected_branch = user_project.protected_branches.find_by(name: branch.name)
- protected_branch&.destroy
+
+ ::ProtectedBranches::DestroyService.new(user_project, current_user).execute(protected_branch) if protected_branch
present branch, with: Entities::Branch, current_user: current_user, project: user_project
end
diff --git a/lib/api/ci/job_artifacts.rb b/lib/api/ci/job_artifacts.rb
index b843404e9d7..b3a0a9ef54a 100644
--- a/lib/api/ci/job_artifacts.rb
+++ b/lib/api/ci/job_artifacts.rb
@@ -143,7 +143,7 @@ module API
reject_if_build_artifacts_size_refreshing!(build.project)
- build.erase_erasable_artifacts!
+ ::Ci::JobArtifacts::DeleteService.new(build).execute
status :no_content
end
diff --git a/lib/api/ci/jobs.rb b/lib/api/ci/jobs.rb
index cd5f1f77ced..6049993bf6f 100644
--- a/lib/api/ci/jobs.rb
+++ b/lib/api/ci/jobs.rb
@@ -142,7 +142,8 @@ module API
reject_if_build_artifacts_size_refreshing!(build.project)
- build.erase(erased_by: current_user)
+ ::Ci::BuildEraseService.new(build, current_user).execute
+
present build, with: Entities::Ci::Job
end
@@ -209,8 +210,8 @@ module API
.select { |_role, role_access_level| role_access_level <= user_access_level }
.map(&:first)
- environment = if environment_slug = current_authenticated_job.persisted_environment&.slug
- { slug: environment_slug }
+ environment = if persisted_environment = current_authenticated_job.persisted_environment
+ { tier: persisted_environment.tier, slug: persisted_environment.slug }
end
# 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/runners.rb b/lib/api/ci/runners.rb
index ec9b09a3419..4b578f8b7e5 100644
--- a/lib/api/ci/runners.rb
+++ b/lib/api/ci/runners.rb
@@ -93,7 +93,7 @@ module API
params[:active] = !params.delete(:paused) if params.include?(:paused)
update_service = ::Ci::Runners::UpdateRunnerService.new(runner)
- if update_service.update(declared_params(include_missing: false))
+ if update_service.execute(declared_params(include_missing: false)).success?
present runner, with: Entities::Ci::RunnerDetails, current_user: current_user
else
render_validation_error!(runner)
@@ -129,8 +129,17 @@ module API
authenticate_list_runners_jobs!(runner)
jobs = ::Ci::RunnerJobsFinder.new(runner, current_user, params).execute
+ jobs = jobs.preload( # rubocop: disable CodeReuse/ActiveRecord
+ [
+ :user,
+ { pipeline: { project: [:route, { namespace: :route }] } },
+ { project: [:route, { namespace: :route }] }
+ ]
+ )
+ jobs = paginate(jobs)
+ jobs.each(&:commit) # batch loads all commits in the page
- present paginate(jobs), with: Entities::Ci::JobBasicWithProject
+ present jobs, with: Entities::Ci::JobBasicWithProject
end
desc 'Reset runner authentication token' do
@@ -352,7 +361,7 @@ module API
def authenticate_list_runners_jobs!(runner)
return if current_user.admin?
- forbidden!("No access granted") unless can?(current_user, :read_runner, runner)
+ forbidden!("No access granted") unless can?(current_user, :read_builds, runner)
end
end
end
diff --git a/lib/api/composer_packages.rb b/lib/api/composer_packages.rb
index de59cb4a7c3..d9806fa37d1 100644
--- a/lib/api/composer_packages.rb
+++ b/lib/api/composer_packages.rb
@@ -150,17 +150,18 @@ module API
get 'archives/*package_name', urgency: :default do
authorize_read_package!(authorized_user_project)
- metadata = authorized_user_project
+ package = authorized_user_project
.packages
.composer
.with_name(params[:package_name])
.with_composer_target(params[:sha])
.first
- &.composer_metadatum
+ metadata = package&.composer_metadatum
not_found! unless metadata
track_package_event('pull_package', :composer, project: authorized_user_project, namespace: authorized_user_project.namespace)
+ package.touch_last_downloaded_at
send_git_archive authorized_user_project.repository, ref: metadata.target_sha, format: 'zip', append_sha: true
end
diff --git a/lib/api/concerns/packages/conan_endpoints.rb b/lib/api/concerns/packages/conan_endpoints.rb
index a90269b565c..d8c2eb4ff33 100644
--- a/lib/api/concerns/packages/conan_endpoints.rb
+++ b/lib/api/concerns/packages/conan_endpoints.rb
@@ -135,7 +135,7 @@ module API
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
get 'packages/:conan_package_reference', urgency: :low do
- authorize!(:read_package, project)
+ authorize_read_package!(project)
presenter = ::Packages::Conan::PackagePresenter.new(
package,
@@ -154,7 +154,7 @@ module API
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
get urgency: :low do
- authorize!(:read_package, project)
+ authorize_read_package!(project)
presenter = ::Packages::Conan::PackagePresenter.new(package, current_user, project)
@@ -237,7 +237,7 @@ module API
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
post 'packages/:conan_package_reference/upload_urls', urgency: :low do
- authorize!(:read_package, project)
+ authorize_read_package!(project)
status 200
present package_upload_urls, with: ::API::Entities::ConanPackage::ConanUploadUrls
@@ -250,7 +250,7 @@ module API
route_setting :authentication, job_token_allowed: true, basic_auth_personal_access_token: true
post 'upload_urls', urgency: :low do
- authorize!(:read_package, project)
+ authorize_read_package!(project)
status 200
present recipe_upload_urls, with: ::API::Entities::ConanPackage::ConanUploadUrls
diff --git a/lib/api/concerns/packages/debian_package_endpoints.rb b/lib/api/concerns/packages/debian_package_endpoints.rb
index e8d27448f02..2883944a745 100644
--- a/lib/api/concerns/packages/debian_package_endpoints.rb
+++ b/lib/api/concerns/packages/debian_package_endpoints.rb
@@ -35,12 +35,30 @@ module API
::Packages::Debian::DistributionsFinder.new(container, codename_or_suite: params[:distribution]).execute.last!
end
- def present_package_file!
+ def present_distribution_package_file!
not_found! unless params[:package_name].start_with?(params[:letter])
package_file = distribution_from!(user_project).package_files.with_file_name(params[:file_name]).last!
- present_carrierwave_file!(package_file.file)
+ present_package_file!(package_file)
+ end
+
+ def present_index_file!(file_type)
+ relation = "::Packages::Debian::#{project_or_group.class.name}ComponentFile".constantize
+
+ relation = relation
+ .preload_distribution
+ .with_container(project_or_group)
+ .with_codename_or_suite(params[:distribution])
+ .with_component_name(params[:component])
+ .with_file_type(file_type)
+ .with_architecture_name(params[:architecture])
+ .with_compression_type(nil)
+ .order_created_asc
+
+ relation = relation.with_file_sha256(params[:file_sha256]) if params[:file_sha256]
+
+ present_carrierwave_file!(relation.last!.file)
end
end
@@ -66,6 +84,7 @@ module API
namespace 'dists/*distribution', requirements: DISTRIBUTION_REQUIREMENTS do
# GET {projects|groups}/:id/packages/debian/dists/*distribution/Release.gpg
+ # 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'
end
@@ -76,6 +95,7 @@ module API
end
# GET {projects|groups}/:id/packages/debian/dists/*distribution/Release
+ # 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'
end
@@ -86,6 +106,7 @@ module API
end
# GET {projects|groups}/:id/packages/debian/dists/*distribution/InRelease
+ # 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'
end
@@ -97,31 +118,87 @@ module API
params do
requires :component, type: String, desc: 'The Debian Component', regexp: Gitlab::Regex.debian_component_regex
- requires :architecture, type: String, desc: 'The Debian Architecture', regexp: Gitlab::Regex.debian_architecture_regex
end
- namespace ':component/binary-:architecture', requirements: COMPONENT_ARCHITECTURE_REQUIREMENTS do
- # GET {projects|groups}/:id/packages/debian/dists/*distribution/:component/binary-:architecture/Packages
- desc 'The binary files index' do
- detail 'This feature was introduced in GitLab 13.5'
+ namespace ':component', requirements: COMPONENT_ARCHITECTURE_REQUIREMENTS do
+ params do
+ requires :architecture, type: String, desc: 'The Debian Architecture', regexp: Gitlab::Regex.debian_architecture_regex
+ end
+
+ namespace 'debian-installer/binary-:architecture' do
+ # GET {projects|groups}/:id/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/Packages
+ # 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'
+ end
+
+ route_setting :authentication, authenticate_non_public: true
+ get 'Packages' do
+ present_index_file!(:di_packages)
+ end
+
+ # GET {projects|groups}/:id/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/by-hash/SHA256/:file_sha256
+ # 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'
+ end
+
+ route_setting :authentication, authenticate_non_public: true
+ get 'by-hash/SHA256/:file_sha256' do
+ present_index_file!(:di_packages)
+ end
+ end
+
+ namespace 'source', requirements: COMPONENT_ARCHITECTURE_REQUIREMENTS do
+ # GET {projects|groups}/:id/packages/debian/dists/*distribution/:component/source/Sources
+ # 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'
+ end
+
+ route_setting :authentication, authenticate_non_public: true
+ get 'Sources' do
+ present_index_file!(:sources)
+ end
+
+ # GET {projects|groups}/:id/packages/debian/dists/*distribution/:component/source/by-hash/SHA256/:file_sha256
+ # 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'
+ end
+
+ route_setting :authentication, authenticate_non_public: true
+ get 'by-hash/SHA256/:file_sha256' do
+ present_index_file!(:sources)
+ end
+ end
+
+ params do
+ requires :architecture, type: String, desc: 'The Debian Architecture', regexp: Gitlab::Regex.debian_architecture_regex
end
- route_setting :authentication, authenticate_non_public: true
- get 'Packages' do
- relation = "::Packages::Debian::#{project_or_group.class.name}ComponentFile".constantize
-
- component_file = relation
- .preload_distribution
- .with_container(project_or_group)
- .with_codename_or_suite(params[:distribution])
- .with_component_name(params[:component])
- .with_file_type(:packages)
- .with_architecture_name(params[:architecture])
- .with_compression_type(nil)
- .order_created_asc
- .last!
-
- present_carrierwave_file!(component_file.file)
+ namespace 'binary-:architecture', requirements: COMPONENT_ARCHITECTURE_REQUIREMENTS do
+ # GET {projects|groups}/:id/packages/debian/dists/*distribution/:component/binary-:architecture/Packages
+ # 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'
+ end
+
+ route_setting :authentication, authenticate_non_public: true
+ get 'Packages' do
+ present_index_file!(:packages)
+ end
+
+ # GET {projects|groups}/:id/packages/debian/dists/*distribution/:component/binary-:architecture/by-hash/SHA256/:file_sha256
+ # 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'
+ end
+
+ route_setting :authentication, authenticate_non_public: true
+ get 'by-hash/SHA256/:file_sha256' do
+ present_index_file!(:packages)
+ end
end
end
end
diff --git a/lib/api/debian_group_packages.rb b/lib/api/debian_group_packages.rb
index 8bf4ac22802..0962d749558 100644
--- a/lib/api/debian_group_packages.rb
+++ b/lib/api/debian_group_packages.rb
@@ -48,7 +48,7 @@ module API
route_setting :authentication, authenticate_non_public: true
get 'pool/:distribution/:project_id/:letter/:package_name/:package_version/:file_name', requirements: PACKAGE_FILE_REQUIREMENTS do
- present_package_file!
+ present_distribution_package_file!
end
end
end
diff --git a/lib/api/debian_project_packages.rb b/lib/api/debian_project_packages.rb
index 06846d8f36e..9dedc4390f7 100644
--- a/lib/api/debian_project_packages.rb
+++ b/lib/api/debian_project_packages.rb
@@ -51,7 +51,7 @@ module API
route_setting :authentication, authenticate_non_public: true
get 'pool/:distribution/:letter/:package_name/:package_version/:file_name', requirements: PACKAGE_FILE_REQUIREMENTS do
- present_package_file!
+ present_distribution_package_file!
end
params do
diff --git a/lib/api/entities/batched_background_migration.rb b/lib/api/entities/batched_background_migration.rb
new file mode 100644
index 00000000000..eba17ff98f4
--- /dev/null
+++ b/lib/api/entities/batched_background_migration.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+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
+ end
+ end
+end
diff --git a/lib/api/entities/ci/job_basic.rb b/lib/api/entities/ci/job_basic.rb
index 0badde4089e..3d9318ec428 100644
--- a/lib/api/entities/ci/job_basic.rb
+++ b/lib/api/entities/ci/job_basic.rb
@@ -18,6 +18,12 @@ module API
expose :web_url do |job, _options|
Gitlab::Routing.url_helpers.project_job_url(job.project, job)
end
+
+ expose :project do
+ expose :ci_job_token_scope_enabled do |job|
+ job.project.ci_job_token_scope_enabled?
+ end
+ end
end
end
end
diff --git a/lib/api/entities/ci/job_request/image.rb b/lib/api/entities/ci/job_request/image.rb
index 83f64da6050..92d68269265 100644
--- a/lib/api/entities/ci/job_request/image.rb
+++ b/lib/api/entities/ci/job_request/image.rb
@@ -8,7 +8,7 @@ module API
expose :name, :entrypoint
expose :ports, using: Entities::Ci::JobRequest::Port
- expose :pull_policy, if: ->(_) { ::Feature.enabled?(:ci_docker_image_pull_policy) }
+ expose :pull_policy
end
end
end
diff --git a/lib/api/entities/ci/job_request/service.rb b/lib/api/entities/ci/job_request/service.rb
index 7d494c7e516..128591058fe 100644
--- a/lib/api/entities/ci/job_request/service.rb
+++ b/lib/api/entities/ci/job_request/service.rb
@@ -8,7 +8,7 @@ module API
expose :name, :entrypoint
expose :ports, using: Entities::Ci::JobRequest::Port
- expose :pull_policy, if: ->(_) { ::Feature.enabled?(:ci_docker_image_pull_policy) }
+ expose :pull_policy
expose :alias, :command
expose :variables
end
diff --git a/lib/api/entities/merge_request_reviewer.rb b/lib/api/entities/merge_request_reviewer.rb
index 3bf2ccc36aa..a47321ef929 100644
--- a/lib/api/entities/merge_request_reviewer.rb
+++ b/lib/api/entities/merge_request_reviewer.rb
@@ -4,7 +4,6 @@ module API
module Entities
class MergeRequestReviewer < Grape::Entity
expose :reviewer, as: :user, using: Entities::UserBasic
- expose :updated_state_by, using: Entities::UserBasic
expose :state
expose :created_at
end
diff --git a/lib/api/entities/ml/mlflow/experiment.rb b/lib/api/entities/ml/mlflow/experiment.rb
new file mode 100644
index 00000000000..cfe366feaab
--- /dev/null
+++ b/lib/api/entities/ml/mlflow/experiment.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ml
+ module Mlflow
+ class Experiment < Grape::Entity
+ expose :experiment do
+ expose :experiment_id
+ expose :name
+ expose :lifecycle_stage
+ expose :artifact_location
+ end
+
+ private
+
+ def lifecycle_stage
+ object.deleted_on? ? 'deleted' : 'active'
+ end
+
+ def experiment_id
+ object.iid.to_s
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ml/mlflow/new_experiment.rb b/lib/api/entities/ml/mlflow/new_experiment.rb
new file mode 100644
index 00000000000..09791839850
--- /dev/null
+++ b/lib/api/entities/ml/mlflow/new_experiment.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ml
+ module Mlflow
+ class NewExperiment < Grape::Entity
+ expose :experiment_id
+
+ private
+
+ def experiment_id
+ object.iid.to_s
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ml/mlflow/run.rb b/lib/api/entities/ml/mlflow/run.rb
new file mode 100644
index 00000000000..c679330206e
--- /dev/null
+++ b/lib/api/entities/ml/mlflow/run.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ml
+ module Mlflow
+ class Run < Grape::Entity
+ expose :run do
+ expose(:info) { |candidate| RunInfo.represent(candidate) }
+ expose(:data) { |candidate| {} }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ml/mlflow/run_info.rb b/lib/api/entities/ml/mlflow/run_info.rb
new file mode 100644
index 00000000000..096950e349d
--- /dev/null
+++ b/lib/api/entities/ml/mlflow/run_info.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ml
+ module Mlflow
+ class RunInfo < Grape::Entity
+ expose :run_id
+ expose :run_id, as: :run_uuid
+ expose(:experiment_id) { |candidate| candidate.experiment.iid.to_s }
+ expose(:start_time) { |candidate| candidate.start_time || 0 }
+ expose :end_time, expose_nil: false
+ expose(:status) { |candidate| candidate.status.to_s.upcase }
+ expose(:artifact_uri) { |candidate| 'not_implemented' }
+ expose(:lifecycle_stage) { |candidate| 'active' }
+ expose(:user_id) { |candidate| candidate.user_id.to_s }
+
+ private
+
+ def run_id
+ object.iid.to_s
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/ml/mlflow/update_run.rb b/lib/api/entities/ml/mlflow/update_run.rb
new file mode 100644
index 00000000000..5acdaab0e33
--- /dev/null
+++ b/lib/api/entities/ml/mlflow/update_run.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ module Ml
+ module Mlflow
+ class UpdateRun < Grape::Entity
+ expose :run_info
+
+ private
+
+ def run_info
+ ::API::Entities::Ml::Mlflow::RunInfo.represent object
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities/package.rb b/lib/api/entities/package.rb
index 1efd457aa5f..18fc0576dd4 100644
--- a/lib/api/entities/package.rb
+++ b/lib/api/entities/package.rb
@@ -39,6 +39,7 @@ module API
end
expose :created_at
+ expose :last_downloaded_at
expose :project_id, if: ->(_, opts) { opts[:group] }
expose :project_path, if: ->(obj, opts) { opts[:group] && Ability.allowed?(opts[:user], :read_project, obj.project) }
expose :tags
diff --git a/lib/api/entities/personal_access_token_with_details.rb b/lib/api/entities/personal_access_token_with_details.rb
deleted file mode 100644
index 5654bd4a1e1..00000000000
--- a/lib/api/entities/personal_access_token_with_details.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Entities
- class PersonalAccessTokenWithDetails < Entities::PersonalAccessToken
- expose :expired?, as: :expired
- expose :expires_soon?, as: :expires_soon
- expose :revoke_path do |token|
- Gitlab::Routing.url_helpers.revoke_profile_personal_access_token_path(token)
- end
- end
- end
-end
diff --git a/lib/api/entities/user_safe.rb b/lib/api/entities/user_safe.rb
index fb99c2e960d..127a8ef2160 100644
--- a/lib/api/entities/user_safe.rb
+++ b/lib/api/entities/user_safe.rb
@@ -3,9 +3,13 @@
module API
module Entities
class UserSafe < Grape::Entity
+ include RequestAwareEntity
+
expose :id, :username
expose :name do |user|
- user.redacted_name(options[:current_user])
+ current_user = request.respond_to?(:current_user) ? request.current_user : options.fetch(:current_user, nil)
+
+ user.redacted_name(current_user)
end
end
end
diff --git a/lib/api/generic_packages.rb b/lib/api/generic_packages.rb
index 0b1c06b3c26..ad5455c5de6 100644
--- a/lib/api/generic_packages.rb
+++ b/lib/api/generic_packages.rb
@@ -102,7 +102,7 @@ module API
track_package_event('pull_package', :generic, project: project, user: current_user, namespace: project.namespace)
- present_carrierwave_file!(package_file.file)
+ present_package_file!(package_file)
end
end
end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 82bbab5d7d4..6b1fc0d4279 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -96,9 +96,9 @@ module API
present options[:with].prepare_relation(projects, options), options
end
- def present_groups(params, groups)
+ def present_groups(params, groups, serializer: Entities::Group)
options = {
- with: Entities::Group,
+ with: serializer,
current_user: current_user,
statistics: params[:statistics] && current_user&.admin?
}
@@ -248,6 +248,8 @@ module API
authorize! :admin_group, group
+ group.remove_avatar! if params.key?(:avatar) && params[:avatar].nil?
+
if update_group(group)
present_group_details(params, group, with_projects: true)
else
@@ -392,6 +394,21 @@ module API
end
end
+ desc 'Get the groups to where the current group can be transferred to'
+ params do
+ optional :search, type: String, desc: 'Return list of namespaces matching the search criteria'
+ use :pagination
+ end
+ get ':id/transfer_locations', feature_category: :subgroups do
+ authorize! :admin_group, user_group
+ args = declared_params(include_missing: false)
+
+ groups = ::Groups::AcceptingGroupTransfersFinder.new(current_user, user_group, args).execute
+ groups = groups.with_route
+
+ 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'
params do
optional :group_id,
diff --git a/lib/api/helm_packages.rb b/lib/api/helm_packages.rb
index a1b265bc8f3..f90084a7e57 100644
--- a/lib/api/helm_packages.rb
+++ b/lib/api/helm_packages.rb
@@ -67,7 +67,7 @@ module API
track_package_event('pull_package', :helm, project: authorized_user_project, namespace: authorized_user_project.namespace)
- present_carrierwave_file!(package_file.file)
+ present_package_file!(package_file)
end
desc 'Authorize a chart upload from workhorse' do
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 1d0f0c6e7bb..e29d76a5950 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -7,9 +7,12 @@ module API
include Helpers::Pagination
include Helpers::PaginationStrategies
include Gitlab::Ci::Artifacts::Logger
+ include Gitlab::Utils::StrongMemoize
SUDO_HEADER = "HTTP_SUDO"
GITLAB_SHARED_SECRET_HEADER = "Gitlab-Shared-Secret"
+ GITLAB_SHELL_API_HEADER = "Gitlab-Shell-Api-Request"
+ GITLAB_SHELL_JWT_ISSUER = "gitlab-shell"
SUDO_PARAM = :sudo
API_USER_ENV = 'gitlab.api.user'
API_TOKEN_ENV = 'gitlab.api.token'
@@ -283,12 +286,22 @@ module API
end
def authenticate_by_gitlab_shell_token!
- input = params['secret_token']
- input ||= Base64.decode64(headers[GITLAB_SHARED_SECRET_HEADER]) if headers.key?(GITLAB_SHARED_SECRET_HEADER)
+ if Feature.enabled?(:gitlab_shell_jwt_token)
+ begin
+ payload, _ = JSONWebToken::HMACToken.decode(headers[GITLAB_SHELL_API_HEADER], secret_token)
+ unauthorized! unless payload['iss'] == GITLAB_SHELL_JWT_ISSUER
+ rescue JWT::DecodeError, JWT::ExpiredSignature, JWT::ImmatureSignature => ex
+ Gitlab::ErrorTracking.track_exception(ex)
+ unauthorized!
+ end
+ else
+ input = params['secret_token']
+ input ||= Base64.decode64(headers[GITLAB_SHARED_SECRET_HEADER]) if headers.key?(GITLAB_SHARED_SECRET_HEADER)
- input&.chomp!
+ input&.chomp!
- unauthorized! unless Devise.secure_compare(secret_token, input)
+ unauthorized! unless Devise.secure_compare(secret_token, input)
+ end
end
def authenticated_with_can_read_all_resources!
@@ -719,7 +732,13 @@ module API
end
def secret_token
- Gitlab::Shell.secret_token
+ if Feature.enabled?(:gitlab_shell_jwt_token)
+ strong_memoize(:secret_token) do
+ File.read(Gitlab.config.gitlab_shell.secret_file)
+ end
+ else
+ Gitlab::Shell.secret_token
+ end
end
def authenticate_non_public?
diff --git a/lib/api/helpers/groups_helpers.rb b/lib/api/helpers/groups_helpers.rb
index 2b10eebb009..e9af50b80be 100644
--- a/lib/api/helpers/groups_helpers.rb
+++ b/lib/api/helpers/groups_helpers.rb
@@ -11,8 +11,7 @@ module API
optional :visibility, type: String,
values: Gitlab::VisibilityLevel.string_values,
desc: 'The visibility of the group'
- # TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960
- optional :avatar, type: File, desc: 'Avatar image for the group' # rubocop:disable Scalability/FileUploads
+ optional :avatar, type: ::API::Validations::Types::WorkhorseFile, desc: 'Avatar image for the group'
optional :share_with_group_lock, type: Boolean, desc: 'Prevent sharing a project with another group within this group'
optional :require_two_factor_authentication, type: Boolean, desc: 'Require all users in this group to setup Two-factor authentication'
optional :two_factor_grace_period, type: Integer, desc: 'Time before Two-factor authentication is enforced'
diff --git a/lib/api/helpers/packages/conan/api_helpers.rb b/lib/api/helpers/packages/conan/api_helpers.rb
index 994d3c4c473..a9d91895cfe 100644
--- a/lib/api/helpers/packages/conan/api_helpers.rb
+++ b/lib/api/helpers/packages/conan/api_helpers.rb
@@ -23,7 +23,7 @@ module API
end
def present_download_urls(entity)
- authorize!(:read_package, project)
+ authorize_read_package!(project)
presenter = ::Packages::Conan::PackagePresenter.new(
package,
@@ -161,7 +161,7 @@ module API
end
def download_package_file(file_type)
- authorize!(:read_package, project)
+ authorize_read_package!(project)
package_file = ::Packages::Conan::PackageFileFinder
.new(
@@ -173,7 +173,7 @@ module API
track_package_event('pull_package', :conan, category: 'API::ConanPackages', user: current_user, project: project, namespace: project.namespace) if params[:file_name] == ::Packages::Conan::FileMetadatum::PACKAGE_BINARY
- present_carrierwave_file!(package_file.file)
+ present_package_file!(package_file)
end
def find_or_create_package
diff --git a/lib/api/helpers/packages/dependency_proxy_helpers.rb b/lib/api/helpers/packages/dependency_proxy_helpers.rb
index b8ae1dddd7e..a09499e00d7 100644
--- a/lib/api/helpers/packages/dependency_proxy_helpers.rb
+++ b/lib/api/helpers/packages/dependency_proxy_helpers.rb
@@ -6,16 +6,18 @@ module API
module DependencyProxyHelpers
REGISTRY_BASE_URLS = {
npm: 'https://registry.npmjs.org/',
- pypi: 'https://pypi.org/simple/'
+ pypi: 'https://pypi.org/simple/',
+ maven: 'https://repo.maven.apache.org/maven2/'
}.freeze
APPLICATION_SETTING_NAMES = {
npm: 'npm_package_requests_forwarding',
- pypi: 'pypi_package_requests_forwarding'
+ pypi: 'pypi_package_requests_forwarding',
+ maven: 'maven_package_requests_forwarding'
}.freeze
def redirect_registry_request(forward_to_registry, package_type, options)
- if forward_to_registry && redirect_registry_request_available?(package_type)
+ if forward_to_registry && redirect_registry_request_available?(package_type) && maven_forwarding_ff_enabled?(package_type, options[:target])
::Gitlab::Tracking.event(self.options[:for].name, "#{package_type}_request_forward")
redirect(registry_url(package_type, options))
else
@@ -33,6 +35,8 @@ module API
"#{base_url}#{options[:package_name]}"
when :pypi
"#{base_url}#{options[:package_name]}/"
+ when :maven
+ "#{base_url}#{options[:path]}/#{options[:file_name]}"
end
end
@@ -46,6 +50,16 @@ module API
.attributes
.fetch(application_setting_name, false)
end
+
+ private
+
+ def maven_forwarding_ff_enabled?(package_type, target)
+ return true unless package_type == :maven
+ return true if Feature.enabled?(:maven_central_request_forwarding)
+ return false unless target
+
+ Feature.enabled?(:maven_central_request_forwarding, target.root_ancestor)
+ end
end
end
end
diff --git a/lib/api/helpers/packages_helpers.rb b/lib/api/helpers/packages_helpers.rb
index 2221eec0f82..687c8330cc8 100644
--- a/lib/api/helpers/packages_helpers.rb
+++ b/lib/api/helpers/packages_helpers.rb
@@ -14,7 +14,7 @@ module API
end
def authorize_read_package!(subject = user_project)
- authorize!(:read_package, subject)
+ authorize!(:read_package, subject.try(:packages_policy_subject) || subject)
end
def authorize_create_package!(subject = user_project)
@@ -53,6 +53,11 @@ module API
category = args.delete(:category) || self.options[:for].name
::Gitlab::Tracking.event(category, event_name.to_s, **args)
end
+
+ def present_package_file!(package_file, supports_direct_download: true)
+ package_file.package.touch_last_downloaded_at
+ present_carrierwave_file!(package_file.file, supports_direct_download: supports_direct_download)
+ end
end
end
end
diff --git a/lib/api/helpers/personal_access_tokens_helpers.rb b/lib/api/helpers/personal_access_tokens_helpers.rb
new file mode 100644
index 00000000000..db28daa5396
--- /dev/null
+++ b/lib/api/helpers/personal_access_tokens_helpers.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module API
+ module Helpers
+ module PersonalAccessTokensHelpers
+ def finder_params(current_user)
+ if current_user.can_admin_all_resources?
+ { user: user(params[:user_id]) }
+ else
+ { user: current_user, impersonation: false }
+ end
+ end
+
+ def user(user_id)
+ UserFinder.new(user_id).find_by_id
+ end
+
+ def restrict_non_admins!
+ return if params[:user_id].blank?
+
+ unauthorized! unless Ability.allowed?(current_user, :read_user_personal_access_tokens, user(params[:user_id]))
+ end
+
+ def find_token(id)
+ PersonalAccessToken.find(id) || not_found!
+ end
+
+ def revoke_token(token)
+ service = ::PersonalAccessTokens::RevokeService.new(current_user, token: token).execute
+
+ service.success? ? no_content! : bad_request!(nil)
+ end
+ end
+ end
+end
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index 628182ad1ab..7ca3f55b5a2 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -39,6 +39,7 @@ module API
optional :emails_disabled, type: Boolean, desc: 'Disable email notifications'
optional :show_default_award_emojis, type: Boolean, desc: 'Show default award emojis'
+ optional :show_diff_preview_in_email, type: Boolean, desc: 'Include the code diff preview in merge request notification emails'
optional :warn_about_potentially_unwanted_characters, type: Boolean, desc: 'Warn about Potentially Unwanted Characters'
optional :enforce_auth_checks_on_uploads, type: Boolean, desc: 'Enforce auth check on uploads'
optional :shared_runners_enabled, type: Boolean, desc: 'Flag indication if shared runners are enabled for that project'
@@ -57,8 +58,7 @@ module API
optional :only_allow_merge_if_all_discussions_are_resolved, type: Boolean, desc: 'Only allow to merge if all threads are resolved'
optional :tag_list, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'Deprecated: Use :topics instead'
optional :topics, type: Array[String], coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce, desc: 'The list of topics for a project'
- # TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960
- optional :avatar, type: File, desc: 'Avatar image for project' # rubocop:disable Scalability/FileUploads
+ optional :avatar, type: ::API::Validations::Types::WorkhorseFile, desc: 'Avatar image for project'
optional :printing_merge_request_link_enabled, type: Boolean, desc: 'Show link to create/view merge request when pushing from the command line'
optional :merge_method, type: String, values: %w(ff rebase_merge merge), desc: 'The merge method used when merging merge requests'
optional :suggestion_commit_message, type: String, desc: 'The commit message used to apply merge request suggestions'
@@ -160,6 +160,7 @@ module API
:request_access_enabled,
:resolve_outdated_diff_discussions,
:restrict_user_defined_variables,
+ :show_diff_preview_in_email,
:security_and_compliance_access_level,
:squash_option,
:shared_runners_enabled,
diff --git a/lib/api/helpers/resource_events_helpers.rb b/lib/api/helpers/resource_events_helpers.rb
new file mode 100644
index 00000000000..c47a58e8fce
--- /dev/null
+++ b/lib/api/helpers/resource_events_helpers.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module API
+ module Helpers
+ module ResourceEventsHelpers
+ def self.eventable_types
+ # This is a method instead of a constant, allowing EE to more easily extend it.
+ {
+ Issue => { feature_category: :team_planning, id_field: 'IID' },
+ MergeRequest => { feature_category: :code_review, id_field: 'IID' }
+ }
+ end
+ end
+ end
+end
+
+API::Helpers::ResourceEventsHelpers.prepend_mod_with('API::Helpers::ResourceEventsHelpers')
diff --git a/lib/api/helpers/resource_label_events_helpers.rb b/lib/api/helpers/resource_label_events_helpers.rb
deleted file mode 100644
index eeb68362c1d..00000000000
--- a/lib/api/helpers/resource_label_events_helpers.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-module API
- module Helpers
- module ResourceLabelEventsHelpers
- def self.feature_category_per_eventable_type
- # This is a method instead of a constant, allowing EE to more easily
- # extend it.
- {
- Issue => :team_planning,
- MergeRequest => :code_review
- }
- end
- end
- end
-end
-
-API::Helpers::ResourceLabelEventsHelpers.prepend_mod_with('API::Helpers::ResourceLabelEventsHelpers')
diff --git a/lib/api/integrations/slack/events.rb b/lib/api/integrations/slack/events.rb
deleted file mode 100644
index 6227b75a9d7..00000000000
--- a/lib/api/integrations/slack/events.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-# This API endpoint handles all events sent from Slack once a Slack
-# workspace has installed the GitLab Slack app.
-#
-# See https://api.slack.com/apis/connections/events-api.
-module API
- class Integrations
- module Slack
- class Events < ::API::Base
- feature_category :integrations
-
- before { verify_slack_request! }
-
- helpers do
- def verify_slack_request!
- unauthorized! unless Request.verify!(request)
- end
- end
-
- namespace 'integrations/slack' do
- post :events do
- type = params['type']
- raise ArgumentError, "Unable to handle event type: '#{type}'" unless type == 'url_verification'
-
- status :ok
- UrlVerification.call(params)
- rescue ArgumentError => e
- # Track the error, but respond with a `2xx` because we don't want to risk
- # Slack rate-limiting, or disabling our app, due to error responses.
- # See https://api.slack.com/apis/connections/events-api.
- Gitlab::ErrorTracking.track_exception(e)
-
- no_content!
- end
- end
- end
- end
- end
-end
diff --git a/lib/api/integrations/slack/events/url_verification.rb b/lib/api/integrations/slack/events/url_verification.rb
deleted file mode 100644
index 4628b93665d..00000000000
--- a/lib/api/integrations/slack/events/url_verification.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-# frozen_string_literal: true
-
-module API
- class Integrations
- module Slack
- class Events
- class UrlVerification
- # When the GitLab Slack app is first configured to receive Slack events,
- # Slack will issue a special request to the endpoint and expect it to respond
- # with the `challenge` param.
- #
- # This must be done in-request, rather than on a queue.
- #
- # See https://api.slack.com/apis/connections/events-api.
- def self.call(params)
- { challenge: params[:challenge] }
- end
- end
- end
- end
- end
-end
diff --git a/lib/api/integrations/slack/request.rb b/lib/api/integrations/slack/request.rb
deleted file mode 100644
index df0109b07aa..00000000000
--- a/lib/api/integrations/slack/request.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-# frozen_string_literal: true
-
-module API
- class Integrations
- module Slack
- module Request
- VERIFICATION_VERSION = 'v0'
- VERIFICATION_TIMESTAMP_HEADER = 'X-Slack-Request-Timestamp'
- VERIFICATION_SIGNATURE_HEADER = 'X-Slack-Signature'
- VERIFICATION_DELIMITER = ':'
- VERIFICATION_HMAC_ALGORITHM = 'sha256'
- VERIFICATION_TIMESTAMP_EXPIRY = 1.minute.to_i
-
- # Verify the request by comparing the given request signature in the header
- # with a signature value that we compute according to the steps in:
- # https://api.slack.com/authentication/verifying-requests-from-slack.
- def self.verify!(request)
- return false unless Gitlab::CurrentSettings.slack_app_signing_secret
-
- timestamp, signature = request.headers.values_at(
- VERIFICATION_TIMESTAMP_HEADER,
- VERIFICATION_SIGNATURE_HEADER
- )
-
- return false if timestamp.nil? || signature.nil?
- return false if Time.current.to_i - timestamp.to_i >= VERIFICATION_TIMESTAMP_EXPIRY
-
- request.body.rewind
-
- basestring = [
- VERIFICATION_VERSION,
- timestamp,
- request.body.read
- ].join(VERIFICATION_DELIMITER)
-
- hmac_digest = OpenSSL::HMAC.hexdigest(
- VERIFICATION_HMAC_ALGORITHM,
- Gitlab::CurrentSettings.slack_app_signing_secret,
- basestring
- )
-
- # Signature will look like: 'v0=a2114d57b48eac39b9ad189dd8316235a7b4a8d21a10bd27519666489c69b503'
- ActiveSupport::SecurityUtils.secure_compare(
- signature,
- "#{VERIFICATION_VERSION}=#{hmac_digest}"
- )
- end
- end
- end
- end
-end
diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb
index 6f475fa8d74..c4464666020 100644
--- a/lib/api/internal/base.rb
+++ b/lib/api/internal/base.rb
@@ -133,11 +133,6 @@ module API
'Could not find a user for the given key' unless actor.user
end
- # TODO: backwards compatibility; remove after https://gitlab.com/gitlab-org/gitlab-shell/-/merge_requests/454 is merged
- def two_factor_otp_check
- { success: false, message: 'Feature is not available' }
- end
-
def two_factor_manual_otp_check
{ success: false, message: 'Feature is not available' }
end
@@ -339,13 +334,6 @@ module API
end
end
- # TODO: backwards compatibility; remove after https://gitlab.com/gitlab-org/gitlab-shell/-/merge_requests/454 is merged
- post '/two_factor_otp_check', feature_category: :authentication_and_authorization do
- status 200
-
- two_factor_manual_otp_check
- end
-
post '/two_factor_push_otp_check', feature_category: :authentication_and_authorization do
status 200
diff --git a/lib/api/maven_packages.rb b/lib/api/maven_packages.rb
index fb0221ee907..a3a25ec1696 100644
--- a/lib/api/maven_packages.rb
+++ b/lib/api/maven_packages.rb
@@ -22,6 +22,7 @@ module API
end
helpers ::API::Helpers::PackagesHelpers
+ helpers ::API::Helpers::Packages::DependencyProxyHelpers
helpers do
def path_exists?(path)
@@ -76,7 +77,10 @@ module API
format == 'jar'
end
- def present_carrierwave_file_with_head_support!(file, supports_direct_download: true)
+ def present_carrierwave_file_with_head_support!(package_file, supports_direct_download: true)
+ package_file.package.touch_last_downloaded_at
+ file = package_file.file
+
if head_request_on_aws_file?(file, supports_direct_download) && !file.file_storage?
return redirect(signed_head_url(file))
end
@@ -110,7 +114,31 @@ module API
project || group,
path: params[:path],
order_by_package_file: order_by_package_file
- ).execute!
+ ).execute
+ end
+
+ def find_and_present_package_file(package, file_name, format, params)
+ project = package&.project
+ package_file = nil
+
+ package_file = ::Packages::PackageFileFinder.new(package, file_name).execute if package
+
+ no_package_found = package_file ? false : true
+
+ redirect_registry_request(no_package_found, :maven, path: params[:path], file_name: params[:file_name], target: params[:target]) do
+ not_found!('Package') if no_package_found
+
+ case format
+ when 'md5'
+ package_file.file_md5
+ when 'sha1'
+ package_file.file_sha1
+ else
+ track_package_event('pull_package', :maven, project: project, namespace: project&.namespace) if jar_file?(format)
+
+ present_carrierwave_file_with_head_support!(package_file)
+ end
+ end
end
end
@@ -138,6 +166,8 @@ module API
package = fetch_package(file_name: file_name, project: project)
+ not_found!('Package') unless package
+
package_file = ::Packages::PackageFileFinder
.new(package, file_name).execute!
@@ -148,7 +178,7 @@ module API
package_file.file_sha1
else
track_package_event('pull_package', :maven, project: project, namespace: project.namespace) if jar_file?(format)
- present_carrierwave_file_with_head_support!(package_file.file)
+ present_carrierwave_file_with_head_support!(package_file)
end
end
@@ -166,31 +196,20 @@ module API
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
get ':id/-/packages/maven/*path/:file_name', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
# return a similar failure to group = find_group(params[:id])
- not_found!('Group') unless path_exists?(params[:path])
-
- file_name, format = extract_format(params[:file_name])
-
group = find_group(params[:id])
+ if Feature.disabled?(:maven_central_request_forwarding, group&.root_ancestor)
+ not_found!('Group') unless path_exists?(params[:path])
+ end
+
not_found!('Group') unless can?(current_user, :read_group, group)
+ file_name, format = extract_format(params[:file_name])
package = fetch_package(file_name: file_name, group: group)
- authorize_read_package!(package.project)
+ authorize_read_package!(package.project) if package
- package_file = ::Packages::PackageFileFinder
- .new(package, file_name).execute!
-
- case format
- when 'md5'
- package_file.file_md5
- when 'sha1'
- package_file.file_sha1
- else
- track_package_event('pull_package', :maven, project: package.project, namespace: package.project.namespace) if jar_file?(format)
-
- present_carrierwave_file_with_head_support!(package_file.file)
- end
+ find_and_present_package_file(package, file_name, format, params.merge(target: group))
end
end
@@ -208,7 +227,9 @@ module API
route_setting :authentication, job_token_allowed: true, deploy_token_allowed: true
get ':id/packages/maven/*path/:file_name', requirements: MAVEN_ENDPOINT_REQUIREMENTS do
# return a similar failure to user_project
- not_found!('Project') unless path_exists?(params[:path])
+ unless Feature.enabled?(:maven_central_request_forwarding, user_project&.root_ancestor)
+ not_found!('Project') unless path_exists?(params[:path])
+ end
authorize_read_package!(user_project)
@@ -216,19 +237,7 @@ module API
package = fetch_package(file_name: file_name, project: user_project)
- package_file = ::Packages::PackageFileFinder
- .new(package, file_name).execute!
-
- case format
- when 'md5'
- package_file.file_md5
- when 'sha1'
- package_file.file_sha1
- else
- track_package_event('pull_package', :maven, project: user_project, namespace: user_project.namespace) if jar_file?(format)
-
- present_carrierwave_file_with_head_support!(package_file.file)
- end
+ find_and_present_package_file(package, file_name, format, params.merge(target: user_project))
end
desc 'Workhorse authorize the maven package file upload' do
diff --git a/lib/api/members.rb b/lib/api/members.rb
index d26fdd09ee7..f4e38207aca 100644
--- a/lib/api/members.rb
+++ b/lib/api/members.rb
@@ -24,6 +24,7 @@ module API
params do
optional :query, type: String, desc: 'A query string to search for members'
optional :user_ids, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Array of user ids to look up for membership'
+ optional :skip_users, type: Array[Integer], coerce_with: ::API::Validations::Types::CommaSeparatedToIntegerArray.coerce, desc: 'Array of user ids to be skipped for membership'
optional :show_seat_info, type: Boolean, desc: 'Show seat information for members'
use :optional_filter_params_ee
use :pagination
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index a8f58e91067..1dc0e1f0d22 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -212,7 +212,17 @@ module API
recheck_mergeability_of(merge_requests: merge_requests) unless options[:skip_merge_status_recheck]
- present_cached merge_requests, expires_in: 8.hours, cache_context: -> (mr) { "#{current_user&.cache_key}:#{mr.merge_status}" }, **options
+ present_cached merge_requests,
+ expires_in: 8.hours,
+ cache_context: -> (mr) do
+ [
+ current_user&.cache_key,
+ mr.merge_status,
+ mr.merge_request_assignees.map(&:cache_key),
+ mr.merge_request_reviewers.map(&:cache_key)
+ ].join(":")
+ end,
+ **options
end
desc 'Create a merge request' do
@@ -544,6 +554,19 @@ module API
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.can_be_approved_by?(current_user)
+
+ merge_request.approvals.delete_all
+
+ status :accepted
+ end
+
desc 'List issues that will be closed on merge' do
success Entities::MRNote
end
diff --git a/lib/api/ml/mlflow.rb b/lib/api/ml/mlflow.rb
new file mode 100644
index 00000000000..4f5bd42f8f9
--- /dev/null
+++ b/lib/api/ml/mlflow.rb
@@ -0,0 +1,171 @@
+# frozen_string_literal: true
+
+require 'mime/types'
+
+module API
+ # MLFlow integration API, replicating the Rest API https://www.mlflow.org/docs/latest/rest-api.html#rest-api
+ module Ml
+ class Mlflow < ::API::Base
+ include APIGuard
+
+ # The first part of the url is the namespace, the second part of the URL is what the MLFlow client calls
+ MLFLOW_API_PREFIX = ':id/ml/mflow/api/2.0/mlflow/'
+
+ allow_access_with_scope :api
+ allow_access_with_scope :read_api, if: -> (request) { request.get? || request.head? }
+
+ before do
+ authenticate!
+ not_found! unless Feature.enabled?(:ml_experiment_tracking, user_project)
+ end
+
+ feature_category :mlops
+
+ content_type :json, 'application/json'
+ default_format :json
+
+ helpers do
+ def resource_not_found!
+ render_structured_api_error!({ error_code: 'RESOURCE_DOES_NOT_EXIST' }, 404)
+ end
+
+ def resource_already_exists!
+ render_structured_api_error!({ error_code: 'RESOURCE_ALREADY_EXISTS' }, 400)
+ end
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ desc 'API to interface with MLFlow Client, REST API version 1.28.0' do
+ detail 'This feature is gated by :ml_experiment_tracking.'
+ end
+ namespace MLFLOW_API_PREFIX do
+ resource :experiments do
+ desc 'Fetch experiment by experiment_id' do
+ success Entities::Ml::Mlflow::Experiment
+ detail 'https://www.mlflow.org/docs/1.28.0/rest-api.html#get-experiment'
+ end
+ params do
+ optional :experiment_id, type: String, default: '', desc: 'Experiment ID, in reference to the project'
+ end
+ get 'get', urgency: :low do
+ experiment = ::Ml::Experiment.by_project_id_and_iid(user_project.id, params[:experiment_id])
+
+ resource_not_found! unless experiment
+
+ present experiment, with: Entities::Ml::Mlflow::Experiment
+ end
+
+ desc 'Fetch experiment by experiment_name' do
+ success Entities::Ml::Mlflow::Experiment
+ detail 'https://www.mlflow.org/docs/1.28.0/rest-api.html#get-experiment-by-name'
+ end
+ params do
+ optional :experiment_name, type: String, default: '', desc: 'Experiment name'
+ end
+ get 'get-by-name', urgency: :low do
+ experiment = ::Ml::Experiment.by_project_id_and_name(user_project, params[:experiment_name])
+
+ resource_not_found! unless experiment
+
+ present experiment, with: Entities::Ml::Mlflow::Experiment
+ end
+
+ desc 'Create experiment' do
+ success Entities::Ml::Mlflow::NewExperiment
+ detail 'https://www.mlflow.org/docs/1.28.0/rest-api.html#create-experiment'
+ end
+ params do
+ requires :name, type: String, desc: 'Experiment name'
+ 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
+ resource_already_exists! if ::Ml::Experiment.has_record?(user_project.id, params[:name])
+
+ experiment = ::Ml::Experiment.create!(name: params[:name],
+ user: current_user,
+ project: user_project)
+
+ present experiment, with: Entities::Ml::Mlflow::NewExperiment
+ end
+ end
+
+ resource :runs do
+ desc 'Gets an MLFlow Run, which maps to GitLab Candidates' do
+ success Entities::Ml::Mlflow::Run
+ detail 'https://www.mlflow.org/docs/1.28.0/rest-api.html#get-run'
+ end
+ params do
+ optional :run_id, type: String, desc: 'UUID of the candidate.'
+ optional :run_uuid, type: String, desc: 'This parameter is ignored'
+ end
+ get 'get', urgency: :low do
+ candidate = ::Ml::Candidate.with_project_id_and_iid(user_project.id, params[:run_id])
+
+ resource_not_found! unless candidate
+
+ present candidate, with: Entities::Ml::Mlflow::Run
+ end
+
+ desc 'Creates a Run.' do
+ success Entities::Ml::Mlflow::Run
+ detail ['https://www.mlflow.org/docs/1.28.0/rest-api.html#create-run',
+ 'MLFlow Runs map to GitLab Candidates']
+ end
+ params do
+ requires :experiment_id, type: Integer,
+ desc: 'Id for the experiment, relative to the project'
+ optional :start_time, type: Integer,
+ 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'
+ end
+ post 'create', urgency: :low do
+ experiment = ::Ml::Experiment.by_project_id_and_iid(user_project.id, params[:experiment_id].to_i)
+
+ resource_not_found! unless experiment
+
+ candidate = ::Ml::Candidate.create!(
+ experiment: experiment,
+ user: current_user,
+ start_time: params[:start_time] || 0
+ )
+
+ present candidate, with: Entities::Ml::Mlflow::Run
+ end
+
+ desc 'Updates a Run.' do
+ success Entities::Ml::Mlflow::UpdateRun
+ detail ['https://www.mlflow.org/docs/1.28.0/rest-api.html#update-run',
+ 'MLFlow Runs map to GitLab Candidates']
+ end
+ params do
+ optional :run_id, type: String, desc: 'UUID of the candidate.'
+ optional :status, type: String,
+ values: ::Ml::Candidate.statuses.keys.map(&:upcase),
+ desc: "Status of the run. Accepts: " \
+ "#{::Ml::Candidate.statuses.keys.map(&:upcase)}."
+ optional :end_time, type: Integer, desc: 'Ending time of the run'
+ end
+ post 'update', urgency: :low do
+ candidate = ::Ml::Candidate.with_project_id_and_iid(user_project.id, params[:run_id])
+
+ resource_not_found! unless candidate
+
+ candidate.status = params[:status].downcase if params[:status]
+ candidate.end_time = params[:end_time] if params[:end_time]
+
+ candidate.save if candidate.valid?
+
+ present candidate, with: Entities::Ml::Mlflow::UpdateRun
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/namespaces.rb b/lib/api/namespaces.rb
index a12fbbb9bb6..eeb66c86b3b 100644
--- a/lib/api/namespaces.rb
+++ b/lib/api/namespaces.rb
@@ -66,6 +66,8 @@ module API
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)
+
namespace_path = params[:namespace]
existing_namespaces_within_the_parent = Namespace.without_project_namespaces.by_parent(params[:parent_id])
diff --git a/lib/api/npm_project_packages.rb b/lib/api/npm_project_packages.rb
index 21bb2e69799..166c0b755fe 100644
--- a/lib/api/npm_project_packages.rb
+++ b/lib/api/npm_project_packages.rb
@@ -35,7 +35,7 @@ module API
track_package_event('pull_package', package, category: 'API::NpmPackages', project: project, namespace: project.namespace)
- present_carrierwave_file!(package_file.file)
+ present_package_file!(package_file)
end
desc 'Create NPM package' do
diff --git a/lib/api/nuget_project_packages.rb b/lib/api/nuget_project_packages.rb
index 1e630cffea1..3e05ea13311 100644
--- a/lib/api/nuget_project_packages.rb
+++ b/lib/api/nuget_project_packages.rb
@@ -193,7 +193,7 @@ module API
)
# nuget and dotnet don't support 302 Moved status codes, supports_direct_download has to be set to false
- present_carrierwave_file!(package_file.file, supports_direct_download: false)
+ present_package_file!(package_file, supports_direct_download: false)
end
end
end
diff --git a/lib/api/personal_access_tokens.rb b/lib/api/personal_access_tokens.rb
index 0d7d2dc6a0c..1c00569bba2 100644
--- a/lib/api/personal_access_tokens.rb
+++ b/lib/api/personal_access_tokens.rb
@@ -18,34 +18,10 @@ module API
before do
authenticate!
- restrict_non_admins! unless current_user.admin?
+ restrict_non_admins! unless current_user.can_admin_all_resources?
end
- helpers do
- def finder_params(current_user)
- current_user.admin? ? { user: user(params[:user_id]) } : { user: current_user, impersonation: false }
- end
-
- def user(user_id)
- UserFinder.new(user_id).find_by_id
- end
-
- def restrict_non_admins!
- return if params[:user_id].blank?
-
- unauthorized! unless Ability.allowed?(current_user, :read_user_personal_access_tokens, user(params[:user_id]))
- end
-
- def find_token(id)
- PersonalAccessToken.find(id) || not_found!
- end
-
- def revoke_token(token)
- service = ::PersonalAccessTokens::RevokeService.new(current_user, token: token).execute
-
- service.success? ? no_content! : bad_request!(nil)
- end
- end
+ helpers ::API::Helpers::PersonalAccessTokensHelpers
resources :personal_access_tokens do
get do
@@ -63,14 +39,10 @@ module API
present token, with: Entities::PersonalAccessToken
else
# Only admins should be informed if the token doesn't exist
- current_user.admin? ? not_found! : unauthorized!
+ current_user.can_admin_all_resources? ? not_found! : unauthorized!
end
end
- delete 'self' do
- revoke_token(access_token)
- end
-
delete ':id' do
token = find_token(params[:id])
diff --git a/lib/api/personal_access_tokens/self_revocation.rb b/lib/api/personal_access_tokens/self_revocation.rb
new file mode 100644
index 00000000000..22e07f4cc7b
--- /dev/null
+++ b/lib/api/personal_access_tokens/self_revocation.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module API
+ class PersonalAccessTokens
+ class SelfRevocation < ::API::Base
+ include APIGuard
+
+ feature_category :authentication_and_authorization
+
+ helpers ::API::Helpers::PersonalAccessTokensHelpers
+
+ # As any token regardless of `scope` should be able to revoke itself
+ # all availabe scopes are allowed for this API class.
+ # Please be aware of the permissive scope when adding new endpoints to this class.
+ allow_access_with_scope(Gitlab::Auth.all_available_scopes)
+
+ before { authenticate! }
+
+ resource :personal_access_tokens do
+ delete 'self' do
+ revoke_token(access_token)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 6ed480518ee..8c58cc585d8 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -453,6 +453,8 @@ module API
filter_attributes_using_license!(attrs)
verify_update_project_attrs!(user_project, attrs)
+ user_project.remove_avatar! if attrs.key?(:avatar) && attrs[:avatar].nil?
+
result = ::Projects::UpdateService.new(user_project, current_user, attrs).execute
if result[:status] == :success
@@ -743,6 +745,22 @@ module API
end
end
+ desc 'Get the namespaces to where the project can be transferred'
+ params do
+ optional :search, type: String, desc: 'Return list of namespaces matching the search criteria'
+ use :pagination
+ end
+ get ":id/transfer_locations", feature_category: :projects do
+ authorize! :change_namespace, user_project
+ args = declared_params(include_missing: false)
+ args[:permission_scope] = :transfer_projects
+
+ groups = ::Groups::UserGroupsFinder.new(current_user, current_user, args).execute
+ groups = groups.with_route
+
+ present_groups(groups)
+ end
+
desc 'Show the storage information' do
success Entities::ProjectRepositoryStorage
end
diff --git a/lib/api/pypi_packages.rb b/lib/api/pypi_packages.rb
index f8a7a3c0ecc..ae583ca968a 100644
--- a/lib/api/pypi_packages.rb
+++ b/lib/api/pypi_packages.rb
@@ -120,7 +120,7 @@ module API
track_package_event('pull_package', :pypi)
- present_carrierwave_file!(package_file.file, supports_direct_download: true)
+ present_package_file!(package_file, supports_direct_download: true)
end
desc 'The PyPi Simple Group Index Endpoint' do
@@ -180,7 +180,7 @@ module API
track_package_event('pull_package', :pypi, project: project, namespace: project.namespace)
- present_carrierwave_file!(package_file.file, supports_direct_download: true)
+ present_package_file!(package_file, supports_direct_download: true)
end
desc 'The PyPi Simple Project Index Endpoint' do
diff --git a/lib/api/releases.rb b/lib/api/releases.rb
index 10e879ec70b..cdfcce9dddb 100644
--- a/lib/api/releases.rb
+++ b/lib/api/releases.rb
@@ -100,6 +100,62 @@ module API
present release, with: Entities::Release, current_user: current_user, include_html_description: params[:include_html_description]
end
+ desc 'Download a project release asset file' do
+ detail 'This feature was introduced in GitLab 15.4.'
+ named 'download_release_asset_file'
+ end
+ params do
+ requires :tag_name, type: String,
+ desc: 'The name of the tag.', as: :tag
+ requires :file_path, type: String,
+ file_path: true,
+ desc: 'The path to the file to download, as specified when creating the release asset.'
+ end
+ route_setting :authentication, job_token_allowed: true
+ get ':id/releases/:tag_name/downloads/*file_path', format: false, requirements: RELEASE_ENDPOINT_REQUIREMENTS do
+ authorize_download_code!
+
+ not_found! unless release
+
+ link = release.links.find_by_filepath!("/#{params[:file_path]}")
+
+ not_found! unless link
+
+ redirect link.url
+ end
+
+ desc 'Get the latest project release' do
+ detail 'This feature was introduced in GitLab 15.4.'
+ named 'get_latest_release'
+ end
+ params do
+ requires :suffix_path, type: String, file_path: true, desc: 'The path to be suffixed to the latest release'
+ end
+ route_setting :authentication, job_token_allowed: true
+ get ':id/releases/permalink/latest(/)(*suffix_path)', format: false, requirements: RELEASE_ENDPOINT_REQUIREMENTS do
+ authorize_download_code!
+
+ # Try to find the latest release
+ latest_release = find_latest_release
+ not_found! unless latest_release
+
+ # Build the full API URL with the tag of the latest release
+ redirect_url = api_v4_projects_releases_path(id: user_project.id, tag_name: latest_release.tag)
+
+ # Include the additional suffix_path if present
+ redirect_url += "/#{params[:suffix_path]}" if params[:suffix_path].present?
+
+ # Include any query parameter except `order_by` since we have plans to extend it in the future.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/352945 for reference.
+ query_parameters_except_order_by = get_query_params.except('order_by')
+
+ if query_parameters_except_order_by.present?
+ redirect_url += "?#{query_parameters_except_order_by.compact.to_param}"
+ end
+
+ redirect redirect_url
+ end
+
desc 'Create a new release' do
detail 'This feature was introduced in GitLab 11.7.'
named 'create_release'
@@ -232,6 +288,16 @@ module API
@release ||= user_project.releases.find_by_tag(params[:tag])
end
+ def find_latest_release
+ ReleasesFinder.new(user_project, current_user, { order_by: 'released_at', sort: 'desc' }).execute.first
+ end
+
+ def get_query_params
+ return {} unless @request.query_string.present?
+
+ Rack::Utils.parse_nested_query(@request.query_string)
+ end
+
def log_release_created_audit_event(release)
# extended in EE
end
diff --git a/lib/api/resource_label_events.rb b/lib/api/resource_label_events.rb
index cd56809f45a..e74b6509a17 100644
--- a/lib/api/resource_label_events.rb
+++ b/lib/api/resource_label_events.rb
@@ -7,20 +7,22 @@ module API
before { authenticate! }
- Helpers::ResourceLabelEventsHelpers.feature_category_per_eventable_type.each do |eventable_type, feature_category|
+ Helpers::ResourceEventsHelpers.eventable_types.each do |eventable_type, details|
parent_type = eventable_type.parent_class.to_s.underscore
eventables_str = eventable_type.to_s.underscore.pluralize
+ human_eventable_str = eventable_type.to_s.underscore.humanize.downcase
+ feature_category = details[:feature_category]
params do
requires :id, type: String, desc: "The ID of a #{parent_type}"
end
resource parent_type.pluralize.to_sym, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- desc "Get a list of #{eventable_type.to_s.downcase} resource label events" do
+ desc "Get a list of #{human_eventable_str} resource label events" do
success Entities::ResourceLabelEvent
detail 'This feature was introduced in 11.3'
end
params do
- requires :eventable_id, types: [Integer, String], desc: 'The ID of the eventable'
+ requires :eventable_id, types: [Integer, String], desc: "The #{details[:id_field]} of the #{human_eventable_str}"
use :pagination
end
@@ -32,13 +34,13 @@ module API
present ResourceLabelEvent.visible_to_user?(current_user, paginate(events)), with: Entities::ResourceLabelEvent
end
- desc "Get a single #{eventable_type.to_s.downcase} resource label event" do
+ desc "Get a single #{human_eventable_str} resource label event" do
success Entities::ResourceLabelEvent
detail 'This feature was introduced in 11.3'
end
params do
requires :event_id, type: String, desc: 'The ID of a resource label event'
- requires :eventable_id, types: [Integer, String], desc: 'The ID of the eventable'
+ requires :eventable_id, types: [Integer, String], desc: "The #{details[:id_field]} of the #{human_eventable_str}"
end
get ":id/#{eventables_str}/:eventable_id/resource_label_events/:event_id", feature_category: feature_category do
eventable = find_noteable(eventable_type, params[:eventable_id])
diff --git a/lib/api/resource_state_events.rb b/lib/api/resource_state_events.rb
index 4b92f320d6f..f817d55c505 100644
--- a/lib/api/resource_state_events.rb
+++ b/lib/api/resource_state_events.rb
@@ -7,41 +7,41 @@ module API
before { authenticate! }
- {
- Issue => :team_planning,
- MergeRequest => :code_review
- }.each do |eventable_class, feature_category|
- eventable_name = eventable_class.to_s.underscore
+ Helpers::ResourceEventsHelpers.eventable_types.each do |eventable_type, details|
+ parent_type = eventable_type.parent_class.to_s.underscore
+ eventables_str = eventable_type.to_s.underscore.pluralize
+ human_eventable_str = eventable_type.to_s.underscore.humanize.downcase
+ feature_category = details[:feature_category]
params do
- requires :id, type: String, desc: "The ID of a project"
+ requires :id, type: String, desc: "The ID of a #{parent_type}"
end
- resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
- desc "Get a list of #{eventable_class.to_s.downcase} resource state events" do
+ resource parent_type.pluralize.to_sym, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ desc "Get a list of #{human_eventable_str} resource state events" do
success Entities::ResourceStateEvent
end
params do
- requires :eventable_iid, types: Integer, desc: "The IID of the #{eventable_name}"
+ requires :eventable_id, types: Integer, desc: "The #{details[:id_field]} of the #{human_eventable_str}"
use :pagination
end
- get ":id/#{eventable_name.pluralize}/:eventable_iid/resource_state_events", feature_category: feature_category, urgency: :low do
- eventable = find_noteable(eventable_class, params[:eventable_iid])
+ get ":id/#{eventables_str}/:eventable_id/resource_state_events", feature_category: feature_category, urgency: :low do
+ eventable = find_noteable(eventable_type, params[:eventable_id])
events = ResourceStateEventFinder.new(current_user, eventable).execute
present paginate(events), with: Entities::ResourceStateEvent
end
- desc "Get a single #{eventable_class.to_s.downcase} resource state event" do
+ desc "Get a single #{human_eventable_str} resource state event" do
success Entities::ResourceStateEvent
end
params do
- requires :eventable_iid, types: Integer, desc: "The IID of the #{eventable_name}"
+ requires :eventable_id, types: Integer, desc: "The #{details[:id_field]} of the #{human_eventable_str}"
requires :event_id, type: Integer, desc: 'The ID of a resource state event'
end
- get ":id/#{eventable_name.pluralize}/:eventable_iid/resource_state_events/:event_id", feature_category: feature_category do
- eventable = find_noteable(eventable_class, params[:eventable_iid])
+ get ":id/#{eventables_str}/:eventable_id/resource_state_events/:event_id", feature_category: feature_category do
+ eventable = find_noteable(eventable_type, params[:eventable_id])
event = ResourceStateEventFinder.new(current_user, eventable).find(params[:event_id])
diff --git a/lib/api/rpm_project_packages.rb b/lib/api/rpm_project_packages.rb
new file mode 100644
index 00000000000..d17470ae92d
--- /dev/null
+++ b/lib/api/rpm_project_packages.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+module API
+ class RpmProjectPackages < ::API::Base
+ helpers ::API::Helpers::PackagesHelpers
+ helpers ::API::Helpers::Packages::BasicAuthHelpers
+ include ::API::Helpers::Authentication
+
+ feature_category :package_registry
+
+ before do
+ require_packages_enabled!
+
+ not_found! unless ::Feature.enabled?(:rpm_packages, authorized_user_project)
+
+ authorize_read_package!(authorized_user_project)
+ end
+
+ authenticate_with do |accept|
+ accept.token_types(:personal_access_token_with_username, :deploy_token_with_username, :job_token_with_username)
+ .sent_through(:http_basic_auth)
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ namespace ':id/packages/rpm' do
+ desc 'Download repository metadata files'
+ params do
+ requires :file_name, type: String, desc: 'Repository metadata file name'
+ end
+ get 'repodata/*file_name', requirements: { file_name: API::NO_SLASH_URL_PART_REGEX } do
+ not_found!
+ end
+
+ desc 'Download RPM package files'
+ params do
+ requires :package_file_id, type: Integer, desc: 'RPM package file id'
+ requires :file_name, type: String, desc: 'RPM package file name'
+ end
+ get '*package_file_id/*file_name', requirements: { file_name: API::NO_SLASH_URL_PART_REGEX } do
+ not_found!
+ end
+
+ desc 'Upload a RPM package'
+ post do
+ authorize_create_package!(authorized_user_project)
+
+ if authorized_user_project.actual_limits.exceeded?(:rpm_max_file_size, params[:file].size)
+ bad_request!('File is too large')
+ end
+
+ not_found!
+ end
+
+ desc 'Authorize package upload from workhorse'
+ post 'authorize' do
+ not_found!
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/rubygem_packages.rb b/lib/api/rubygem_packages.rb
index 85bbd0879b7..b4d02613e4c 100644
--- a/lib/api/rubygem_packages.rb
+++ b/lib/api/rubygem_packages.rb
@@ -65,7 +65,7 @@ module API
requires :file_name, type: String, desc: 'Package file name'
end
get "gems/:file_name", requirements: FILE_NAME_REQUIREMENTS do
- authorize!(:read_package, user_project)
+ authorize_read_package!(user_project)
package_files = ::Packages::PackageFile
.for_rubygem_with_file_name(user_project, params[:file_name])
@@ -74,7 +74,7 @@ module API
track_package_event('pull_package', :rubygems, project: user_project, namespace: user_project.namespace)
- present_carrierwave_file!(package_file.file)
+ present_package_file!(package_file)
end
namespace 'api/v1' do
diff --git a/lib/api/search.rb b/lib/api/search.rb
index 7aa3cf8a5cb..44bb4228786 100644
--- a/lib/api/search.rb
+++ b/lib/api/search.rb
@@ -65,9 +65,26 @@ module API
set_global_search_log_information
+ Gitlab::Metrics::GlobalSearchSlis.record_apdex(
+ elapsed: @search_duration_s,
+ search_type: search_type,
+ search_level: search_service.level,
+ search_scope: search_scope
+ )
+
Gitlab::UsageDataCounters::SearchCounter.count(:all_searches)
paginate(@results)
+
+ ensure
+ # If we raise an error somewhere in the @search_duration_s benchmark block, we will end up here
+ # with a 200 status code, but an empty @search_duration_s.
+ Gitlab::Metrics::GlobalSearchSlis.record_error_rate(
+ error: @search_duration_s.nil? || (status < 200 || status >= 400),
+ search_type: search_type,
+ search_level: search_service(additional_params).level,
+ search_scope: search_scope
+ )
end
def snippets?
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index c25a56d5f08..f393f862f55 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -98,6 +98,7 @@ module API
optional :max_export_size, type: Integer, desc: 'Maximum export size in MB'
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 :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'
diff --git a/lib/api/tags.rb b/lib/api/tags.rb
index 97a2aebf53b..c8ac68189f5 100644
--- a/lib/api/tags.rb
+++ b/lib/api/tags.rb
@@ -22,8 +22,8 @@ module API
params do
optional :sort, type: String, values: %w[asc desc], default: 'desc',
desc: 'Return tags sorted in updated by `asc` or `desc` order.'
- optional :order_by, type: String, values: %w[name updated], default: 'updated',
- desc: 'Return tags ordered by `name` or `updated` fields.'
+ optional :order_by, type: String, values: %w[name updated version], default: 'updated',
+ desc: 'Return tags ordered by `name`, `updated`, `version` fields.'
optional :search, type: String, desc: 'Return list of tags matching the search criteria'
optional :page_token, type: String, desc: 'Name of tag to start the paginaition from'
use :pagination
diff --git a/lib/api/topics.rb b/lib/api/topics.rb
index a08b4c6c107..38cfdc44021 100644
--- a/lib/api/topics.rb
+++ b/lib/api/topics.rb
@@ -94,5 +94,25 @@ module API
destroy_conditionally!(topic)
end
+
+ desc 'Merge topics' do
+ detail 'This feature was introduced in GitLab 15.4.'
+ success Entities::Projects::Topic
+ end
+ params do
+ requires :source_topic_id, type: Integer, desc: 'ID of source project topic'
+ requires :target_topic_id, type: Integer, desc: 'ID of target project topic'
+ end
+ post 'topics/merge' do
+ authenticated_as_admin!
+
+ source_topic = ::Projects::Topic.find(params[:source_topic_id])
+ target_topic = ::Projects::Topic.find(params[:target_topic_id])
+
+ response = ::Topics::MergeService.new(source_topic, target_topic).execute
+ render_api_error!(response.message, :bad_request) if response.error?
+
+ present target_topic, with: Entities::Projects::Topic
+ end
end
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index c93c0f601a0..1d1c633824e 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -54,8 +54,7 @@ module API
optional :admin, type: Boolean, desc: 'Flag indicating the user is an administrator'
optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups'
optional :external, type: Boolean, desc: 'Flag indicating the user is an external user'
- # TODO: remove rubocop disable - https://gitlab.com/gitlab-org/gitlab/issues/14960
- optional :avatar, type: File, desc: 'Avatar image for user' # rubocop:disable Scalability/FileUploads
+ optional :avatar, type: ::API::Validations::Types::WorkhorseFile, desc: 'Avatar image for user'
optional :theme_id, type: Integer, desc: 'The GitLab theme for the user'
optional :color_scheme_id, type: Integer, desc: 'The color scheme for the file viewer'
optional :private_profile, type: Boolean, desc: 'Flag indicating the user has a private profile'
@@ -733,7 +732,7 @@ module API
unless user.can_be_deactivated?
forbidden!('A blocked user cannot be deactivated by the API') if user.blocked?
forbidden!('An internal user cannot be deactivated by the API') if user.internal?
- forbidden!("The user you are trying to deactivate has been active in the past #{::User::MINIMUM_INACTIVE_DAYS} days and cannot be deactivated")
+ forbidden!("The user you are trying to deactivate has been active in the past #{Gitlab::CurrentSettings.deactivate_dormant_users_period} days and cannot be deactivated")
end
if user.deactivate
diff --git a/lib/banzai/filter/blockquote_fence_filter.rb b/lib/banzai/filter/blockquote_fence_filter.rb
index e07cbfe8d85..e5cf20d00df 100644
--- a/lib/banzai/filter/blockquote_fence_filter.rb
+++ b/lib/banzai/filter/blockquote_fence_filter.rb
@@ -6,13 +6,13 @@ module Banzai
REGEX = %r{
#{::Gitlab::Regex.markdown_code_or_html_blocks}
|
- (?=^>>>\ *\n.*\n>>>\ *$)(?:
+ (?=(?<=^\n|\A)>>>\ *\n.*\n>>>\ *(?=\n$|\z))(?:
# Blockquote:
# >>>
# Anything, including code and HTML blocks
# >>>
- ^>>>\ *\n
+ (?<=^\n|\A)>>>\ *\n
(?<quote>
(?:
# Any character that doesn't introduce a code or HTML block
@@ -30,7 +30,7 @@ module Banzai
\g<html>
)+?
)
- \n>>>\ *$
+ \n>>>\ *(?=\n$|\z)
)
}mx.freeze
diff --git a/lib/banzai/filter/kroki_filter.rb b/lib/banzai/filter/kroki_filter.rb
index 845c7f2bc0a..713ff2439fc 100644
--- a/lib/banzai/filter/kroki_filter.rb
+++ b/lib/banzai/filter/kroki_filter.rb
@@ -14,7 +14,10 @@ module Banzai
return doc unless settings.kroki_enabled
diagram_selectors = ::Gitlab::Kroki.formats(settings)
- .map { |diagram_type| %(pre[lang="#{diagram_type}"] > code) }
+ .map do |diagram_type|
+ %(pre[lang="#{diagram_type}"] > code,
+ pre > code[lang="#{diagram_type}"])
+ end
.join(', ')
xpath = Gitlab::Utils::Nokogiri.css_to_xpath(diagram_selectors)
@@ -22,7 +25,7 @@ module Banzai
diagram_format = "svg"
doc.xpath(xpath).each do |node|
- diagram_type = node.parent['lang']
+ diagram_type = node.parent['lang'] || node['lang']
diagram_src = node.content
image_src = create_image_src(diagram_type, diagram_format, diagram_src)
img_tag = Nokogiri::HTML::DocumentFragment.parse(%(<img src="#{image_src}" />))
@@ -33,8 +36,8 @@ module Banzai
img_tag.set_attribute('hidden', '') if lazy_load
img_tag.set_attribute('class', 'js-render-kroki')
- img_tag.set_attribute('data-diagram', node.parent['lang'])
- img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(node.content)}")
+ img_tag.set_attribute('data-diagram', diagram_type)
+ img_tag.set_attribute('data-diagram-src', "data:text/plain;base64,#{Base64.strict_encode64(diagram_src)}")
node.parent.replace(img_tag)
end
diff --git a/lib/banzai/filter/math_filter.rb b/lib/banzai/filter/math_filter.rb
index 0ac506776be..1ca4b2c89db 100644
--- a/lib/banzai/filter/math_filter.rb
+++ b/lib/banzai/filter/math_filter.rb
@@ -7,7 +7,7 @@ require 'uri'
# - app/assets/javascripts/behaviors/markdown/nodes/code_block.js
module Banzai
module Filter
- # HTML filter that adds class="code math" and removes the dollar sign in $`2+2`$.
+ # HTML filter that implements our math syntax, adding class="code math"
#
class MathFilter < HTML::Pipeline::Filter
CSS_MATH = 'pre.code.language-math'
@@ -15,14 +15,42 @@ module Banzai
CSS_CODE = 'code'
XPATH_CODE = Gitlab::Utils::Nokogiri.css_to_xpath(CSS_CODE).freeze
+ # These are based on the Pandoc heuristics,
+ # https://pandoc.org/MANUAL.html#extension-tex_math_dollars
+ # Note: at this time, using a dollar sign literal, `\$` inside
+ # a math statement does not work correctly.
+ # Corresponds to the "$...$" syntax
+ DOLLAR_INLINE_PATTERN = %r{
+ (?<matched>\$(?<math>(?:\S[^$\n]*?\S|[^$\s]))\$)(?:[^\d]|$)
+ }x.freeze
+
+ # Corresponds to the "$$...$$" syntax
+ DOLLAR_DISPLAY_INLINE_PATTERN = %r{
+ (?<matched>\$\$\ *(?<math>[^$\n]+?)\ *\$\$)
+ }x.freeze
+
+ # Corresponds to the $$\n...\n$$ syntax
+ DOLLAR_DISPLAY_BLOCK_PATTERN = %r{
+ ^(?<matched>\$\$\ *\n(?<math>.*)\n\$\$\ *)$
+ }x.freeze
+
+ # Order dependent. Handle the `$$` syntax before the `$` syntax
+ DOLLAR_MATH_PIPELINE = [
+ { pattern: DOLLAR_DISPLAY_INLINE_PATTERN, tag: :code, style: :display },
+ { pattern: DOLLAR_DISPLAY_BLOCK_PATTERN, tag: :pre, style: :display },
+ { pattern: DOLLAR_INLINE_PATTERN, tag: :code, style: :inline }
+ ].freeze
+
+ # Do not recognize math inside these tags
+ IGNORED_ANCESTOR_TAGS = %w[pre code tt].to_set
+
# Attribute indicating inline or display math.
STYLE_ATTRIBUTE = 'data-math-style'
# Class used for tagging elements that should be rendered
TAG_CLASS = 'js-render-math'
- INLINE_CLASSES = "code math #{TAG_CLASS}"
-
+ MATH_CLASSES = "code math #{TAG_CLASS}"
DOLLAR_SIGN = '$'
# Limit to how many nodes can be marked as math elements.
@@ -31,8 +59,48 @@ module Banzai
RENDER_NODES_LIMIT = 50
def call
- nodes_count = 0
+ @nodes_count = 0
+
+ process_dollar_pipeline if Feature.enabled?(:markdown_dollar_math, group)
+
+ process_dollar_backtick_inline
+ process_math_codeblock
+
+ doc
+ end
+
+ def process_dollar_pipeline
+ doc.xpath('descendant-or-self::text()').each do |node|
+ next if has_ancestor?(node, IGNORED_ANCESTOR_TAGS)
+
+ node_html = node.to_html
+ next unless node_html.match?(DOLLAR_INLINE_PATTERN) ||
+ node_html.match?(DOLLAR_DISPLAY_INLINE_PATTERN) ||
+ node_html.match?(DOLLAR_DISPLAY_BLOCK_PATTERN)
+
+ temp_doc = Nokogiri::HTML.fragment(node_html)
+ DOLLAR_MATH_PIPELINE.each do |pipeline|
+ temp_doc.xpath('child::text()').each do |temp_node|
+ html = temp_node.to_html
+ temp_node.content.scan(pipeline[:pattern]).each do |matched, math|
+ html.sub!(matched, math_html(tag: pipeline[:tag], style: pipeline[:style], math: math))
+ @nodes_count += 1
+ break if @nodes_count >= RENDER_NODES_LIMIT
+ end
+
+ temp_node.replace(html)
+
+ break if @nodes_count >= RENDER_NODES_LIMIT
+ end
+ end
+
+ node.replace(temp_doc)
+ end
+ end
+
+ # Corresponds to the "$`...`$" syntax
+ def process_dollar_backtick_inline
doc.xpath(XPATH_CODE).each do |code|
closing = code.next
opening = code.previous
@@ -44,22 +112,38 @@ module Banzai
closing.content.first == DOLLAR_SIGN &&
opening.content.last == DOLLAR_SIGN
- code[:class] = INLINE_CLASSES
+ code[:class] = MATH_CLASSES
code[STYLE_ATTRIBUTE] = 'inline'
closing.content = closing.content[1..]
opening.content = opening.content[0..-2]
- nodes_count += 1
- break if nodes_count >= RENDER_NODES_LIMIT
+ @nodes_count += 1
+ break if @nodes_count >= RENDER_NODES_LIMIT
end
end
+ end
+ # corresponds to the "```math...```" syntax
+ def process_math_codeblock
doc.xpath(XPATH_MATH).each do |el|
el[STYLE_ATTRIBUTE] = 'display'
el[:class] += " #{TAG_CLASS}"
end
+ end
- doc
+ private
+
+ def math_html(tag:, math:, style:)
+ case tag
+ when :code
+ "<code class=\"#{MATH_CLASSES}\" data-math-style=\"#{style}\">#{math}</code>"
+ when :pre
+ "<pre class=\"#{MATH_CLASSES}\" data-math-style=\"#{style}\"><code>#{math}</code></pre>"
+ end
+ end
+
+ def group
+ context[:group] || context[:project]&.group
end
end
end
diff --git a/lib/banzai/filter/plantuml_filter.rb b/lib/banzai/filter/plantuml_filter.rb
index cbcd547120d..82f6247cf03 100644
--- a/lib/banzai/filter/plantuml_filter.rb
+++ b/lib/banzai/filter/plantuml_filter.rb
@@ -31,7 +31,8 @@ module Banzai
private
def lang_tag
- @lang_tag ||= Gitlab::Utils::Nokogiri.css_to_xpath('pre[lang="plantuml"] > code').freeze
+ @lang_tag ||= Gitlab::Utils::Nokogiri
+ .css_to_xpath('pre[lang="plantuml"] > code, pre > code[lang="plantuml"]').freeze
end
def settings
diff --git a/lib/banzai/filter/references/reference_filter.rb b/lib/banzai/filter/references/reference_filter.rb
index 97ef71036a2..37734f6a45a 100644
--- a/lib/banzai/filter/references/reference_filter.rb
+++ b/lib/banzai/filter/references/reference_filter.rb
@@ -15,6 +15,8 @@ module Banzai
include RequestStoreReferenceCache
include OutputSafety
+ REFERENCE_TYPE_DATA_ATTRIBUTE = 'data-reference-type='
+
class << self
# Implement in child class
# Example: self.reference_type = :merge_request
@@ -132,13 +134,19 @@ module Banzai
def data_attribute(attributes = {})
attributes = attributes.reject { |_, v| v.nil? }
- attributes[:reference_type] ||= self.class.reference_type
+ # "data-reference-type=" attribute got moved into a constant because we need
+ # to use it on ReferenceRewriter class to detect if the markdown contains any reference
+ reference_type_attribute = "#{REFERENCE_TYPE_DATA_ATTRIBUTE}#{escape_once(self.class.reference_type)} "
+
attributes[:container] ||= 'body'
attributes[:placement] ||= 'top'
attributes.delete(:original) if context[:no_original_data]
+
attributes.map do |key, value|
%Q(data-#{key.to_s.dasherize}="#{escape_once(value)}")
- end.join(' ')
+ end
+ .join(' ')
+ .prepend(reference_type_attribute)
end
def ignore_ancestor_query
diff --git a/lib/banzai/pipeline/markup_pipeline.rb b/lib/banzai/pipeline/markup_pipeline.rb
index 17a73f29afb..330914f7238 100644
--- a/lib/banzai/pipeline/markup_pipeline.rb
+++ b/lib/banzai/pipeline/markup_pipeline.rb
@@ -9,8 +9,8 @@ module Banzai
Filter::AssetProxyFilter,
Filter::ExternalLinkFilter,
Filter::PlantumlFilter,
- Filter::SyntaxHighlightFilter,
- Filter::KrokiFilter
+ Filter::KrokiFilter,
+ Filter::SyntaxHighlightFilter
]
end
diff --git a/lib/bulk_imports/file_downloads/filename_fetch.rb b/lib/bulk_imports/file_downloads/filename_fetch.rb
new file mode 100644
index 00000000000..b6bb0fd8c81
--- /dev/null
+++ b/lib/bulk_imports/file_downloads/filename_fetch.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module BulkImports
+ module FileDownloads
+ module FilenameFetch
+ REMOTE_FILENAME_PATTERN = %r{filename="(?<filename>[^"]+)"}.freeze
+ FILENAME_SIZE_LIMIT = 255 # chars before the extension
+
+ def raise_error(message)
+ raise NotImplementedError
+ end
+
+ private
+
+ # Fetch the remote filename information from the request content-disposition header
+ # - Raises if the filename does not exist
+ # - If the filename is longer then 255 chars truncate it
+ # to be a total of 255 chars (with the extension)
+ # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ def remote_filename
+ @remote_filename ||= begin
+ pattern = BulkImports::FileDownloads::FilenameFetch::REMOTE_FILENAME_PATTERN
+ name = response_headers['content-disposition'].to_s
+ .match(pattern) # matches the filename pattern
+ .then { |match| match&.named_captures || {} } # ensures the match is a hash
+ .fetch('filename') # fetches the 'filename' key or raise KeyError
+
+ name = File.basename(name) # Ensures to remove path from the filename (../ for instance)
+ ensure_filename_size(name) # Ensures the filename is within the FILENAME_SIZE_LIMIT
+ end
+ rescue KeyError
+ raise_error 'Remote filename not provided in content-disposition header'
+ end
+ # rubocop:enable Gitlab/ModuleWithInstanceVariables
+
+ def ensure_filename_size(filename)
+ limit = BulkImports::FileDownloads::FilenameFetch::FILENAME_SIZE_LIMIT
+ return filename if filename.length <= limit
+
+ extname = File.extname(filename)
+ basename = File.basename(filename, extname)[0, limit]
+ "#{basename}#{extname}"
+ end
+ end
+ end
+end
diff --git a/lib/bulk_imports/file_downloads/validations.rb b/lib/bulk_imports/file_downloads/validations.rb
new file mode 100644
index 00000000000..ae94267a6e8
--- /dev/null
+++ b/lib/bulk_imports/file_downloads/validations.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+module BulkImports
+ module FileDownloads
+ module Validations
+ def raise_error(message)
+ raise NotImplementedError
+ end
+
+ def filepath
+ raise NotImplementedError
+ end
+
+ def file_size_limit
+ raise NotImplementedError
+ end
+
+ def response_headers
+ raise NotImplementedError
+ end
+
+ private
+
+ def validate_filepath
+ Gitlab::Utils.check_path_traversal!(filepath)
+ end
+
+ def validate_content_type
+ content_type = response_headers['content-type']
+
+ raise_error('Invalid content type') if content_type.blank? || allowed_content_types.exclude?(content_type)
+ end
+
+ def validate_symlink
+ return unless File.lstat(filepath).symlink?
+
+ File.delete(filepath)
+ raise_error 'Invalid downloaded file'
+ end
+
+ def validate_content_length
+ validate_size!(response_headers['content-length'])
+ end
+
+ def validate_size!(size)
+ if size.blank?
+ raise_error 'Missing content-length header'
+ elsif size.to_i > file_size_limit
+ raise_error format(
+ "File size %{size} exceeds limit of %{limit}",
+ size: ActiveSupport::NumberHelper.number_to_human_size(size),
+ limit: ActiveSupport::NumberHelper.number_to_human_size(file_size_limit)
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/lib/container_registry/gitlab_api_client.rb b/lib/container_registry/gitlab_api_client.rb
index be99fa75ffe..2947dcb4b40 100644
--- a/lib/container_registry/gitlab_api_client.rb
+++ b/lib/container_registry/gitlab_api_client.rb
@@ -31,7 +31,7 @@ module ContainerRegistry
def self.deduplicated_size(path)
with_dummy_client(token_config: { type: :nested_repositories_token, path: path&.downcase }) do |client|
- client.repository_details(path, sizing: :self_with_descendants)['size_bytes']
+ client.repository_details(path&.downcase, sizing: :self_with_descendants)['size_bytes']
end
end
diff --git a/lib/container_registry/tag.rb b/lib/container_registry/tag.rb
index 76188a937c0..bf44b74cf7b 100644
--- a/lib/container_registry/tag.rb
+++ b/lib/container_registry/tag.rb
@@ -4,7 +4,7 @@ module ContainerRegistry
class Tag
include Gitlab::Utils::StrongMemoize
- attr_reader :repository, :name
+ attr_reader :repository, :name, :updated_at
attr_writer :created_at
delegate :registry, :client, to: :repository
@@ -97,6 +97,17 @@ module ContainerRegistry
instance_variable_set(ivar(:memoized_created_at), date)
end
+ def updated_at=(string_value)
+ return unless string_value
+
+ @updated_at =
+ begin
+ DateTime.iso8601(string_value)
+ rescue ArgumentError
+ nil
+ end
+ end
+
def layers
return unless manifest
diff --git a/lib/error_tracking/sentry_client.rb b/lib/error_tracking/sentry_client.rb
index 6a341ddbe86..029389ab5d6 100644
--- a/lib/error_tracking/sentry_client.rb
+++ b/lib/error_tracking/sentry_client.rb
@@ -10,16 +10,32 @@ module ErrorTracking
Error = Class.new(StandardError)
MissingKeysError = Class.new(StandardError)
+ ResponseInvalidSizeError = Class.new(StandardError)
+
+ RESPONSE_SIZE_LIMIT = 1.megabyte
attr_accessor :url, :token
- def initialize(api_url, token)
+ def initialize(api_url, token, validate_size_guarded_by_feature_flag: false)
@url = api_url
@token = token
+ @validate_size_guarded_by_feature_flag = validate_size_guarded_by_feature_flag
+ end
+
+ def validate_size_guarded_by_feature_flag?
+ @validate_size_guarded_by_feature_flag
end
private
+ def validate_size(response)
+ return if Gitlab::Utils::DeepSize.new(response, max_size: RESPONSE_SIZE_LIMIT).valid?
+
+ limit = ActiveSupport::NumberHelper.number_to_human_size(RESPONSE_SIZE_LIMIT)
+ message = "Sentry API response is too big. Limit is #{limit}."
+ raise ResponseInvalidSizeError, message
+ end
+
def api_urls
@api_urls ||= SentryClient::ApiUrls.new(@url)
end
@@ -86,6 +102,8 @@ module ErrorTracking
def handle_response(response)
raise_error "Sentry response status code: #{response.code}" unless response.code.between?(200, 204)
+ validate_size(response.parsed_response) if validate_size_guarded_by_feature_flag?
+
{ body: response.parsed_response, headers: response.headers }
end
diff --git a/lib/error_tracking/sentry_client/issue.rb b/lib/error_tracking/sentry_client/issue.rb
index d0e6bd783f3..3c846eb0635 100644
--- a/lib/error_tracking/sentry_client/issue.rb
+++ b/lib/error_tracking/sentry_client/issue.rb
@@ -4,7 +4,6 @@ module ErrorTracking
class SentryClient
module Issue
BadRequestError = Class.new(StandardError)
- ResponseInvalidSizeError = Class.new(StandardError)
SENTRY_API_SORT_VALUE_MAP = {
# <accepted_by_client> => <accepted_by_sentry_api>
@@ -19,7 +18,9 @@ module ErrorTracking
issues = response[:issues]
pagination = response[:pagination]
- validate_size(issues)
+ # We check validate size only with feture flag disabled because when
+ # enabled we already check it when parsing the response.
+ validate_size(issues) unless validate_size_guarded_by_feature_flag?
handle_mapping_exceptions do
{
@@ -64,13 +65,6 @@ module ErrorTracking
}.compact
end
- def validate_size(issues)
- return if Gitlab::Utils::DeepSize.new(issues).valid?
-
- message = "Sentry API response is too big. Limit is #{Gitlab::Utils::DeepSize.human_default_max_size}."
- raise ResponseInvalidSizeError, message
- end
-
def get_issue(issue_id:)
http_get(api_urls.issue_url(issue_id))[:body]
end
diff --git a/lib/event_filter.rb b/lib/event_filter.rb
index 8c3377fdb80..f14b0a6b9e7 100644
--- a/lib/event_filter.rb
+++ b/lib/event_filter.rb
@@ -131,18 +131,19 @@ class EventFilter
finder_query = -> (id_expression) { Event.where(Event.arel_table[:id].eq(id_expression)) }
if order_hint_column.present?
- order = Gitlab::Pagination::Keyset::Order.build([
- Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: order_hint_column,
- order_expression: Event.arel_table[order_hint_column].desc,
- nullable: :nulls_last,
- distinct: false
- ),
- Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
- attribute_name: :id,
- order_expression: Event.arel_table[:id].desc
- )
- ])
+ order = Gitlab::Pagination::Keyset::Order.build(
+ [
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: order_hint_column,
+ order_expression: Event.arel_table[order_hint_column].desc,
+ nullable: :nulls_last,
+ distinct: false
+ ),
+ Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
+ attribute_name: :id,
+ order_expression: Event.arel_table[:id].desc
+ )
+ ])
finder_query = -> (_order_hint, id_expression) { Event.where(Event.arel_table[:id].eq(id_expression)) }
end
diff --git a/lib/generators/gitlab/usage_metric/templates/instrumentation_class_spec.rb.template b/lib/generators/gitlab/usage_metric/templates/instrumentation_class_spec.rb.template
index e984daee0a4..f8bd502ab77 100644
--- a/lib/generators/gitlab/usage_metric/templates/instrumentation_class_spec.rb.template
+++ b/lib/generators/gitlab/usage_metric/templates/instrumentation_class_spec.rb.template
@@ -3,5 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::<%= class_name %>Metric do
- it_behaves_like 'a correct instrumented metric value', {}, 1
+ let(:expected_value) { 1 }
+
+ it_behaves_like 'a correct instrumented metric value', { time_frame: 'all', data_source: 'database' }
end
diff --git a/lib/gitlab/abuse.rb b/lib/gitlab/abuse.rb
index cc95d3c1e0c..7db99d4b037 100644
--- a/lib/gitlab/abuse.rb
+++ b/lib/gitlab/abuse.rb
@@ -3,10 +3,10 @@
module Gitlab
module Abuse
CONFIDENCE_LEVELS = {
- certain: 1.0,
- likely: 0.8,
+ certain: 1.0,
+ likely: 0.8,
uncertain: 0.5,
- unknown: 0.0
+ unknown: 0.0
}.freeze
class << self
diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb
index 3e09d488bc3..fa025a2658f 100644
--- a/lib/gitlab/access.rb
+++ b/lib/gitlab/access.rb
@@ -41,9 +41,9 @@ module Gitlab
def options
{
- "Guest" => GUEST,
- "Reporter" => REPORTER,
- "Developer" => DEVELOPER,
+ "Guest" => GUEST,
+ "Reporter" => REPORTER,
+ "Developer" => DEVELOPER,
"Maintainer" => MAINTAINER
}
end
@@ -62,9 +62,9 @@ module Gitlab
def sym_options
{
- guest: GUEST,
- reporter: REPORTER,
- developer: DEVELOPER,
+ guest: GUEST,
+ reporter: REPORTER,
+ developer: DEVELOPER,
maintainer: MAINTAINER
}
end
@@ -120,9 +120,9 @@ module Gitlab
def project_creation_string_options
{
- 'noone' => NO_ONE_PROJECT_ACCESS,
- 'maintainer' => MAINTAINER_PROJECT_ACCESS,
- 'developer' => DEVELOPER_MAINTAINER_PROJECT_ACCESS
+ 'noone' => NO_ONE_PROJECT_ACCESS,
+ 'maintainer' => MAINTAINER_PROJECT_ACCESS,
+ 'developer' => DEVELOPER_MAINTAINER_PROJECT_ACCESS
}
end
@@ -147,7 +147,7 @@ module Gitlab
def subgroup_creation_string_options
{
- 'owner' => OWNER_SUBGROUP_ACCESS,
+ 'owner' => OWNER_SUBGROUP_ACCESS,
'maintainer' => MAINTAINER_SUBGROUP_ACCESS
}
end
diff --git a/lib/gitlab/alert_management/payload/base.rb b/lib/gitlab/alert_management/payload/base.rb
index 2d769148c5f..01dcb95eab5 100644
--- a/lib/gitlab/alert_management/payload/base.rb
+++ b/lib/gitlab/alert_management/payload/base.rb
@@ -149,6 +149,10 @@ module Gitlab
severity_mapping.fetch(severity_raw.to_s.downcase, UNMAPPED_SEVERITY)
end
+ def source
+ monitoring_tool || integration&.name
+ end
+
private
def plain_gitlab_fingerprint
diff --git a/lib/gitlab/alert_management/payload/generic.rb b/lib/gitlab/alert_management/payload/generic.rb
index 15238b5e50f..18e65779ead 100644
--- a/lib/gitlab/alert_management/payload/generic.rb
+++ b/lib/gitlab/alert_management/payload/generic.rb
@@ -6,6 +6,7 @@ module Gitlab
module Payload
class Generic < Base
DEFAULT_TITLE = 'New: Alert'
+ DEFAULT_SOURCE = 'Generic Alert Endpoint'
attribute :description, paths: 'description'
attribute :ends_at, paths: 'end_time', type: :time
@@ -22,6 +23,14 @@ module Gitlab
attribute :plain_gitlab_fingerprint, paths: 'fingerprint'
private :plain_gitlab_fingerprint
+
+ def resolved?
+ ends_at.present?
+ end
+
+ def source
+ super || DEFAULT_SOURCE
+ end
end
end
end
diff --git a/lib/gitlab/analytics/cycle_analytics/request_params.rb b/lib/gitlab/analytics/cycle_analytics/request_params.rb
index d0d8d68362e..ac9c465bf7d 100644
--- a/lib/gitlab/analytics/cycle_analytics/request_params.rb
+++ b/lib/gitlab/analytics/cycle_analytics/request_params.rb
@@ -105,9 +105,8 @@ module Gitlab
private
def use_aggregated_backend?
- group.present? && # for now it's only available on the group-level
- aggregation.enabled &&
- Feature.enabled?(:use_vsa_aggregated_tables, group)
+ # for now it's only available on the group-level
+ group.present? && aggregation.enabled
end
def aggregation_attributes
diff --git a/lib/gitlab/analytics/date_filler.rb b/lib/gitlab/analytics/date_filler.rb
new file mode 100644
index 00000000000..aa3db9f3635
--- /dev/null
+++ b/lib/gitlab/analytics/date_filler.rb
@@ -0,0 +1,112 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Analytics
+ # This class generates a date => value hash without gaps in the data points.
+ #
+ # Simple usage:
+ #
+ # > # We have the following data for the last 5 day:
+ # > input = { 3.days.ago.to_date => 10, Date.today => 5 }
+ #
+ # > # Format this data, so we can chart the complete date range:
+ # > Gitlab::Analytics::DateFiller.new(input, from: 4.days.ago, to: Date.today, default_value: 0).fill
+ # > {
+ # > Sun, 28 Aug 2022=>0,
+ # > Mon, 29 Aug 2022=>10,
+ # > Tue, 30 Aug 2022=>0,
+ # > Wed, 31 Aug 2022=>0,
+ # > Thu, 01 Sep 2022=>5
+ # > }
+ #
+ # Parameters:
+ #
+ # **input**
+ # A Hash containing data for the series or the chart. The key is a Date object
+ # or an object which can be converted to Date.
+ #
+ # **from**
+ # Start date of the range
+ #
+ # **to**
+ # End date of the range
+ #
+ # **period**
+ # Specifies the period in wich the dates should be generated. Options:
+ #
+ # - :day, generate date-value pair for each day in the given period
+ # - :week, generate date-value pair for each week (beginning of the week date)
+ # - :month, generate date-value pair for each week (beginning of the month date)
+ #
+ # Note: the Date objects in the `input` should follow the same pattern (beginning of ...)
+ #
+ # **default_value**
+ #
+ # Which value use when the `input` Hash does not contain data for the given day.
+ #
+ # **date_formatter**
+ #
+ # How to format the dates in the resulting hash.
+ class DateFiller
+ DEFAULT_DATE_FORMATTER = -> (date) { date }
+ PERIOD_STEPS = {
+ day: 1.day,
+ week: 1.week,
+ month: 1.month
+ }.freeze
+
+ def initialize(
+ input,
+ from:,
+ to:,
+ period: :day,
+ default_value: nil,
+ date_formatter: DEFAULT_DATE_FORMATTER)
+ @input = input.transform_keys(&:to_date)
+ @from = from.to_date
+ @to = to.to_date
+ @period = period
+ @default_value = default_value
+ @date_formatter = date_formatter
+ end
+
+ def fill
+ data = {}
+
+ current_date = from
+ loop do
+ transformed_date = transform_date(current_date)
+ break if transformed_date > to
+
+ formatted_date = date_formatter.call(transformed_date)
+
+ value = input.delete(transformed_date)
+ data[formatted_date] = value.nil? ? default_value : value
+
+ current_date = (current_date + PERIOD_STEPS.fetch(period)).to_date
+ end
+
+ raise "Input contains values which doesn't fall under the given period!" if input.any?
+
+ data
+ end
+
+ private
+
+ attr_reader :input, :from, :to, :period, :default_value, :date_formatter
+
+ def transform_date(date)
+ case period
+ when :day
+ date.beginning_of_day.to_date
+ when :week
+ date.beginning_of_week.to_date
+ when :month
+ date.beginning_of_month.to_date
+ else
+ raise "Unknown period given: #{period}"
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/application_rate_limiter.rb b/lib/gitlab/application_rate_limiter.rb
index a2d79b189a3..507f94d87a5 100644
--- a/lib/gitlab/application_rate_limiter.rb
+++ b/lib/gitlab/application_rate_limiter.rb
@@ -16,40 +16,43 @@ module Gitlab
# and only do that when it's needed.
def rate_limits # rubocop:disable Metrics/AbcSize
{
- issues_create: { threshold: -> { application_settings.issues_create_limit }, interval: 1.minute },
- notes_create: { threshold: -> { application_settings.notes_create_limit }, interval: 1.minute },
- project_export: { threshold: -> { application_settings.project_export_limit }, interval: 1.minute },
- project_download_export: { threshold: -> { application_settings.project_download_export_limit }, interval: 1.minute },
+ issues_create: { threshold: -> { application_settings.issues_create_limit }, interval: 1.minute },
+ notes_create: { threshold: -> { application_settings.notes_create_limit }, interval: 1.minute },
+ project_export: { threshold: -> { application_settings.project_export_limit }, interval: 1.minute },
+ project_download_export: { threshold: -> { application_settings.project_download_export_limit }, interval: 1.minute },
project_repositories_archive: { threshold: 5, interval: 1.minute },
- project_generate_new_export: { threshold: -> { application_settings.project_export_limit }, interval: 1.minute },
- project_import: { threshold: -> { application_settings.project_import_limit }, interval: 1.minute },
- project_testing_hook: { threshold: 5, interval: 1.minute },
- play_pipeline_schedule: { threshold: 1, interval: 1.minute },
- raw_blob: { threshold: -> { application_settings.raw_blob_request_limit }, interval: 1.minute },
- group_export: { threshold: -> { application_settings.group_export_limit }, interval: 1.minute },
- group_download_export: { threshold: -> { application_settings.group_download_export_limit }, interval: 1.minute },
- group_import: { threshold: -> { application_settings.group_import_limit }, interval: 1.minute },
- group_testing_hook: { threshold: 5, interval: 1.minute },
- profile_add_new_email: { threshold: 5, interval: 1.minute },
- web_hook_calls: { interval: 1.minute },
- web_hook_calls_mid: { interval: 1.minute },
- web_hook_calls_low: { interval: 1.minute },
- users_get_by_id: { threshold: -> { application_settings.users_get_by_id_limit }, interval: 10.minutes },
- username_exists: { threshold: 20, interval: 1.minute },
- user_sign_up: { threshold: 20, interval: 1.minute },
- user_sign_in: { threshold: 5, interval: 10.minutes },
- profile_resend_email_confirmation: { threshold: 5, interval: 1.minute },
- profile_update_username: { threshold: 10, interval: 1.minute },
- update_environment_canary_ingress: { threshold: 1, interval: 1.minute },
- auto_rollback_deployment: { threshold: 1, interval: 3.minutes },
- search_rate_limit: { threshold: -> { application_settings.search_rate_limit }, interval: 1.minute },
- search_rate_limit_unauthenticated: { threshold: -> { application_settings.search_rate_limit_unauthenticated }, interval: 1.minute },
- gitlab_shell_operation: { threshold: 600, interval: 1.minute },
- pipelines_create: { threshold: -> { application_settings.pipeline_limit_per_project_user_sha }, interval: 1.minute },
- temporary_email_failure: { threshold: 50, interval: 1.day },
- project_testing_integration: { threshold: 5, interval: 1.minute },
- email_verification: { threshold: 10, interval: 10.minutes },
- email_verification_code_send: { threshold: 10, interval: 1.hour }
+ project_generate_new_export: { threshold: -> { application_settings.project_export_limit }, interval: 1.minute },
+ project_import: { threshold: -> { application_settings.project_import_limit }, interval: 1.minute },
+ project_testing_hook: { threshold: 5, interval: 1.minute },
+ play_pipeline_schedule: { threshold: 1, interval: 1.minute },
+ raw_blob: { threshold: -> { application_settings.raw_blob_request_limit }, interval: 1.minute },
+ group_export: { threshold: -> { application_settings.group_export_limit }, interval: 1.minute },
+ group_download_export: { threshold: -> { application_settings.group_download_export_limit }, interval: 1.minute },
+ group_import: { threshold: -> { application_settings.group_import_limit }, interval: 1.minute },
+ group_testing_hook: { threshold: 5, interval: 1.minute },
+ profile_add_new_email: { threshold: 5, interval: 1.minute },
+ web_hook_calls: { interval: 1.minute },
+ web_hook_calls_mid: { interval: 1.minute },
+ web_hook_calls_low: { interval: 1.minute },
+ users_get_by_id: { threshold: -> { application_settings.users_get_by_id_limit }, interval: 10.minutes },
+ username_exists: { threshold: 20, interval: 1.minute },
+ user_sign_up: { threshold: 20, interval: 1.minute },
+ user_sign_in: { threshold: 5, interval: 10.minutes },
+ profile_resend_email_confirmation: { threshold: 5, interval: 1.minute },
+ profile_update_username: { threshold: 10, interval: 1.minute },
+ update_environment_canary_ingress: { threshold: 1, interval: 1.minute },
+ auto_rollback_deployment: { threshold: 1, interval: 3.minutes },
+ search_rate_limit: { threshold: -> { application_settings.search_rate_limit }, interval: 1.minute },
+ search_rate_limit_unauthenticated: { threshold: -> { application_settings.search_rate_limit_unauthenticated }, interval: 1.minute },
+ gitlab_shell_operation: { threshold: 600, interval: 1.minute },
+ pipelines_create: { threshold: -> { application_settings.pipeline_limit_per_project_user_sha }, interval: 1.minute },
+ temporary_email_failure: { threshold: 300, interval: 1.day },
+ permanent_email_failure: { threshold: 5, interval: 1.day },
+ project_testing_integration: { threshold: 5, interval: 1.minute },
+ email_verification: { threshold: 10, interval: 10.minutes },
+ email_verification_code_send: { threshold: 10, interval: 1.hour },
+ namespace_exists: { threshold: 20, interval: 1.minute },
+ fetch_google_ip_list: { threshold: 10, interval: 1.minute }
}.freeze
end
@@ -130,16 +133,16 @@ module Gitlab
# @param logger [Logger] Logger to log request to a specific log file. Defaults to Gitlab::AuthLogger
def log_request(request, type, current_user, logger = Gitlab::AuthLogger)
request_information = {
- message: 'Application_Rate_Limiter_Request',
- env: type,
- remote_ip: request.ip,
+ message: 'Application_Rate_Limiter_Request',
+ env: type,
+ remote_ip: request.ip,
request_method: request.request_method,
- path: request.fullpath
+ path: request.fullpath
}
if current_user
request_information.merge!({
- user_id: current_user.id,
+ user_id: current_user.id,
username: current_user.username
})
end
diff --git a/lib/gitlab/application_rate_limiter/increment_per_action.rb b/lib/gitlab/application_rate_limiter/increment_per_action.rb
index c99d03f1344..a3343c8a97c 100644
--- a/lib/gitlab/application_rate_limiter/increment_per_action.rb
+++ b/lib/gitlab/application_rate_limiter/increment_per_action.rb
@@ -5,9 +5,9 @@ module Gitlab
class IncrementPerAction < BaseStrategy
def increment(cache_key, expiry)
with_redis do |redis|
- redis.pipelined do
- redis.incr(cache_key)
- redis.expire(cache_key, expiry)
+ redis.pipelined do |pipeline|
+ pipeline.incr(cache_key)
+ pipeline.expire(cache_key, expiry)
end.first
end
end
diff --git a/lib/gitlab/application_rate_limiter/increment_per_actioned_resource.rb b/lib/gitlab/application_rate_limiter/increment_per_actioned_resource.rb
index 8b4197cfff9..7a68dd104a8 100644
--- a/lib/gitlab/application_rate_limiter/increment_per_actioned_resource.rb
+++ b/lib/gitlab/application_rate_limiter/increment_per_actioned_resource.rb
@@ -9,10 +9,10 @@ module Gitlab
def increment(cache_key, expiry)
with_redis do |redis|
- redis.pipelined do
- redis.sadd(cache_key, resource_key)
- redis.expire(cache_key, expiry)
- redis.scard(cache_key)
+ redis.pipelined do |pipeline|
+ pipeline.sadd(cache_key, resource_key)
+ pipeline.expire(cache_key, expiry)
+ pipeline.scard(cache_key)
end.last
end
end
diff --git a/lib/gitlab/audit/auditor.rb b/lib/gitlab/audit/auditor.rb
index c96be19f02d..4a6e4e2e06e 100644
--- a/lib/gitlab/audit/auditor.rb
+++ b/lib/gitlab/audit/auditor.rb
@@ -117,7 +117,7 @@ module Gitlab
# Only capture real users for successful authentication events.
user: author_if_user,
user_name: @author.name,
- ip_address: @ip_address,
+ ip_address: Gitlab::RequestContext.instance.client_ip || @author.current_sign_in_ip,
result: AuthenticationEvent.results[:success],
provider: @authentication_provider
}
diff --git a/lib/gitlab/audit/type/definition.rb b/lib/gitlab/audit/type/definition.rb
new file mode 100644
index 00000000000..af5dc9f4b44
--- /dev/null
+++ b/lib/gitlab/audit/type/definition.rb
@@ -0,0 +1,122 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Audit
+ module Type
+ class Definition
+ include ActiveModel::Validations
+
+ attr_reader :path
+ attr_reader :attributes
+
+ validate :validate_schema
+ validate :validate_file_name
+
+ InvalidAuditEventTypeError = Class.new(StandardError)
+
+ AUDIT_EVENT_TYPE_SCHEMA_PATH = Rails.root.join('config', 'audit_events', 'types', 'type_schema.json')
+ AUDIT_EVENT_TYPE_SCHEMA = JSONSchemer.schema(AUDIT_EVENT_TYPE_SCHEMA_PATH)
+
+ # The PARAMS in config/audit_events/types/type_schema.json
+ PARAMS = %i[
+ name
+ description
+ introduced_by_issue
+ introduced_by_mr
+ group
+ milestone
+ saved_to_database
+ streamed
+ ].freeze
+
+ PARAMS.each do |param|
+ define_method(param) do
+ attributes[param]
+ end
+ end
+
+ def initialize(path, opts = {})
+ @path = path
+ @attributes = {}
+
+ # assign nil, for all unknown opts
+ PARAMS.each do |param|
+ @attributes[param] = opts[param]
+ end
+ end
+
+ def key
+ name.to_sym
+ end
+
+ private
+
+ def validate_schema
+ schema_errors = AUDIT_EVENT_TYPE_SCHEMA
+ .validate(attributes.to_h.deep_stringify_keys)
+ .map { |error| JSONSchemer::Errors.pretty(error) }
+
+ errors.add(:base, schema_errors) if schema_errors.present?
+ end
+
+ def validate_file_name
+ # ignoring Style/GuardClause because if we move this into one line, we cause Layout/LineLength errors
+ # rubocop:disable Style/GuardClause
+ unless File.basename(path, ".yml") == name
+ errors.add(:base, "Audit event type '#{name}' has an invalid path: '#{path}'. " \
+ "'#{name}' must match the filename")
+ end
+ # rubocop:enable Style/GuardClause
+ end
+
+ class << self
+ def paths
+ @paths ||= [Rails.root.join('config', 'audit_events', 'types', '*.yml')]
+ end
+
+ def definitions
+ # We lazily load all definitions
+ @definitions ||= load_all!
+ end
+
+ def get(key)
+ definitions[key.to_sym]
+ end
+
+ private
+
+ def load_all!
+ paths.each_with_object({}) do |glob_path, definitions|
+ load_all_from_path!(definitions, glob_path)
+ end
+ end
+
+ def load_all_from_path!(definitions, glob_path)
+ Dir.glob(glob_path).each do |path|
+ definition = load_from_file(path)
+
+ if previous = definitions[definition.key]
+ raise InvalidAuditEventTypeError, "Audit event type '#{definition.key}' " \
+ "is already defined in '#{previous.path}'"
+ end
+
+ definitions[definition.key] = definition
+ end
+ end
+
+ def load_from_file(path)
+ definition = File.read(path)
+ definition = YAML.safe_load(definition)
+ definition.deep_symbolize_keys!
+
+ new(path, definition).tap(&:validate!)
+ rescue StandardError => e
+ raise InvalidAuditEventTypeError, "Invalid definition for `#{path}`: #{e.message}"
+ end
+ end
+ end
+ end
+ end
+end
+
+Gitlab::Audit::Type::Definition.prepend_mod
diff --git a/lib/gitlab/auth/ldap/config.rb b/lib/gitlab/auth/ldap/config.rb
index 82c6411c712..9dafd59561a 100644
--- a/lib/gitlab/auth/ldap/config.rb
+++ b/lib/gitlab/auth/ldap/config.rb
@@ -7,8 +7,8 @@ module Gitlab
class Config
NET_LDAP_ENCRYPTION_METHOD = {
simple_tls: :simple_tls,
- start_tls: :start_tls,
- plain: nil
+ start_tls: :start_tls,
+ plain: nil
}.freeze
attr_accessor :provider, :options
@@ -193,11 +193,11 @@ module Gitlab
def default_attributes
{
- 'username' => %W(#{uid} uid sAMAccountName userid).uniq,
- 'email' => %w(mail email userPrincipalName),
- 'name' => 'cn',
- 'first_name' => 'givenName',
- 'last_name' => 'sn'
+ 'username' => %W(#{uid} uid sAMAccountName userid).uniq,
+ 'email' => %w(mail email userPrincipalName),
+ 'name' => 'cn',
+ 'first_name' => 'givenName',
+ 'last_name' => 'sn'
}
end
diff --git a/lib/gitlab/auth/o_auth/auth_hash.rb b/lib/gitlab/auth/o_auth/auth_hash.rb
index 37f92792d2d..82a5aad360c 100644
--- a/lib/gitlab/auth/o_auth/auth_hash.rb
+++ b/lib/gitlab/auth/o_auth/auth_hash.rb
@@ -33,7 +33,7 @@ module Gitlab
end
def password
- @password ||= Gitlab::Utils.force_utf8(::User.random_password.downcase)
+ @password ||= Gitlab::Utils.force_utf8(::User.random_password)
end
def location
@@ -103,7 +103,7 @@ module Gitlab
{
username: username,
- email: email
+ email: email
}
end
end
diff --git a/lib/gitlab/auth/o_auth/provider.rb b/lib/gitlab/auth/o_auth/provider.rb
index 1a25ed10d81..2ce8677c8b7 100644
--- a/lib/gitlab/auth/o_auth/provider.rb
+++ b/lib/gitlab/auth/o_auth/provider.rb
@@ -5,14 +5,14 @@ module Gitlab
module OAuth
class Provider
LABELS = {
- "alicloud" => "AliCloud",
- "dingtalk" => "DingTalk",
- "github" => "GitHub",
- "gitlab" => "GitLab.com",
- "google_oauth2" => "Google",
- "azure_oauth2" => "Azure AD",
+ "alicloud" => "AliCloud",
+ "dingtalk" => "DingTalk",
+ "github" => "GitHub",
+ "gitlab" => "GitLab.com",
+ "google_oauth2" => "Google",
+ "azure_oauth2" => "Azure AD",
"azure_activedirectory_v2" => "Azure AD v2",
- 'atlassian_oauth2' => 'Atlassian'
+ 'atlassian_oauth2' => 'Atlassian'
}.freeze
def self.authentication(user, provider)
@@ -68,7 +68,9 @@ module Gitlab
nil
end
else
- provider = Gitlab.config.omniauth.providers.find { |provider| provider.name == name }
+ provider = Gitlab.config.omniauth.providers.find do |provider|
+ provider.name == name || (provider.name == 'openid_connect' && provider.args.name == name)
+ end
merge_provider_args_with_defaults!(provider)
provider
diff --git a/lib/gitlab/auth/o_auth/user.rb b/lib/gitlab/auth/o_auth/user.rb
index 7d9c4c0d7c1..1fed2b263da 100644
--- a/lib/gitlab/auth/o_auth/user.rb
+++ b/lib/gitlab/auth/o_auth/user.rb
@@ -240,11 +240,11 @@ module Gitlab
valid_username = Uniquify.new.string(valid_username) { |s| !NamespacePathValidator.valid_path?(s) }
{
- name: name.strip.presence || valid_username,
- username: valid_username,
- email: email,
- password: auth_hash.password,
- password_confirmation: auth_hash.password,
+ name: name.strip.presence || valid_username,
+ username: valid_username,
+ email: email,
+ password: auth_hash.password,
+ password_confirmation: auth_hash.password,
password_automatically_set: true
}
end
diff --git a/lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp.rb b/lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp.rb
index 9cf1b2247a7..88ad48c3db7 100644
--- a/lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp.rb
+++ b/lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp.rb
@@ -34,7 +34,7 @@ module Gitlab
end
def body
- { username: user.username,
+ { username: user.username,
token_code: @otp_code }
end
diff --git a/lib/gitlab/auth/user_access_denied_reason.rb b/lib/gitlab/auth/user_access_denied_reason.rb
index ff6dc7313bb..322dfa74d09 100644
--- a/lib/gitlab/auth/user_access_denied_reason.rb
+++ b/lib/gitlab/auth/user_access_denied_reason.rb
@@ -57,3 +57,5 @@ module Gitlab
end
end
end
+
+Gitlab::Auth::UserAccessDeniedReason.prepend_mod
diff --git a/lib/gitlab/background_migration/backfill_cluster_agents_has_vulnerabilities.rb b/lib/gitlab/background_migration/backfill_cluster_agents_has_vulnerabilities.rb
new file mode 100644
index 00000000000..2ee0594d0a6
--- /dev/null
+++ b/lib/gitlab/background_migration/backfill_cluster_agents_has_vulnerabilities.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Backfills the `vulnerability_reads.casted_cluster_agent_id` column
+ class BackfillClusterAgentsHasVulnerabilities < Gitlab::BackgroundMigration::BatchedMigrationJob
+ VULNERABILITY_READS_JOIN = <<~SQL
+ INNER JOIN vulnerability_reads
+ ON vulnerability_reads.casted_cluster_agent_id = cluster_agents.id AND
+ vulnerability_reads.project_id = cluster_agents.project_id AND
+ vulnerability_reads.report_type = 7
+ SQL
+
+ RELATION = ->(relation) do
+ relation
+ .where(has_vulnerabilities: false)
+ end
+
+ def perform
+ each_sub_batch(
+ operation_name: :update_all,
+ batching_scope: RELATION
+ ) do |sub_batch|
+ sub_batch
+ .joins(VULNERABILITY_READS_JOIN)
+ .update_all(has_vulnerabilities: true)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_with_corrected_regex.rb b/lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_with_corrected_regex.rb
index b9151343d6a..2d64b7378be 100644
--- a/lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_with_corrected_regex.rb
+++ b/lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_with_corrected_regex.rb
@@ -9,6 +9,7 @@ module Gitlab
# Migration only version of MergeRequest table
class MergeRequest < ::ApplicationRecord
include EachBatch
+ validates :suggested_reviewers, json_schema: { filename: 'merge_request_suggested_reviewers' }
CORRECTED_REGEXP_STR = "^(\\[draft\\]|\\(draft\\)|draft:|draft|\\[WIP\\]|WIP:|WIP)"
diff --git a/lib/gitlab/background_migration/backfill_project_feature_package_registry_access_level.rb b/lib/gitlab/background_migration/backfill_project_feature_package_registry_access_level.rb
index 814f5a897a9..ce4c4a28b37 100644
--- a/lib/gitlab/background_migration/backfill_project_feature_package_registry_access_level.rb
+++ b/lib/gitlab/background_migration/backfill_project_feature_package_registry_access_level.rb
@@ -22,7 +22,7 @@ module Gitlab
ProjectFeature.connection.execute(
<<~SQL
UPDATE project_features pf
- SET package_registry_access_level = (CASE p.packages_enabled
+ SET package_registry_access_level = (CASE p.packages_enabled
WHEN true THEN (CASE p.visibility_level
WHEN #{PROJECT_PUBLIC} THEN #{FEATURE_PUBLIC}
WHEN #{PROJECT_INTERNAL} THEN #{FEATURE_ENABLED}
diff --git a/lib/gitlab/background_migration/backfill_project_namespace_on_issues.rb b/lib/gitlab/background_migration/backfill_project_namespace_on_issues.rb
new file mode 100644
index 00000000000..815c346bb39
--- /dev/null
+++ b/lib/gitlab/background_migration/backfill_project_namespace_on_issues.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Back-fills the `issues.namespace_id` by setting it to corresponding project.project_namespace_id
+ class BackfillProjectNamespaceOnIssues < BatchedMigrationJob
+ def perform
+ each_sub_batch(
+ operation_name: :update_all,
+ batching_scope: -> (relation) {
+ relation.joins("INNER JOIN projects ON projects.id = issues.project_id")
+ .select("issues.id AS issue_id, projects.project_namespace_id").where(issues: { namespace_id: nil })
+ }
+ ) do |sub_batch|
+ connection.execute <<~SQL
+ UPDATE issues
+ SET namespace_id = projects.project_namespace_id
+ FROM (#{sub_batch.to_sql}) AS projects(issue_id, project_namespace_id)
+ WHERE issues.id = issue_id
+ SQL
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/backfill_project_repositories.rb b/lib/gitlab/background_migration/backfill_project_repositories.rb
index 05e2ed72fb3..c49ef9d10f5 100644
--- a/lib/gitlab/background_migration/backfill_project_repositories.rb
+++ b/lib/gitlab/background_migration/backfill_project_repositories.rb
@@ -212,8 +212,8 @@ module Gitlab
def build_attributes_for_project(project)
{
project_id: project.id,
- shard_id: find_shard_id(project.repository_storage),
- disk_path: project.disk_path
+ shard_id: find_shard_id(project.repository_storage),
+ disk_path: project.disk_path
}
end
diff --git a/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent.rb b/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent.rb
index 728b60f7a0e..0c41d6af209 100644
--- a/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent.rb
+++ b/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent.rb
@@ -10,16 +10,12 @@ module Gitlab
vulnerability_reads.project_id = cluster_agents.project_id
SQL
- RELATION = ->(relation) do
- relation
- .where(report_type: 7)
- end
+ CLUSTER_IMAGE_SCANNING_REPORT_TYPE = 7
+
+ scope_to ->(relation) { relation.where(report_type: CLUSTER_IMAGE_SCANNING_REPORT_TYPE) }
def perform
- each_sub_batch(
- operation_name: :update_all,
- batching_scope: RELATION
- ) do |sub_batch|
+ each_sub_batch(operation_name: :update_all) do |sub_batch|
sub_batch
.joins(CLUSTER_AGENTS_JOIN)
.update_all('casted_cluster_agent_id = CAST(vulnerability_reads.cluster_agent_id AS bigint)')
diff --git a/lib/gitlab/background_migration/backfill_work_item_type_id_for_issues.rb b/lib/gitlab/background_migration/backfill_work_item_type_id_for_issues.rb
index 32962f2bb89..86d53ad798d 100644
--- a/lib/gitlab/background_migration/backfill_work_item_type_id_for_issues.rb
+++ b/lib/gitlab/background_migration/backfill_work_item_type_id_for_issues.rb
@@ -4,11 +4,9 @@ module Gitlab
module BackgroundMigration
# Backfills the `issues.work_item_type_id` column, replacing any
# instances of `NULL` with the appropriate `work_item_types.id` based on `issues.issue_type`
- class BackfillWorkItemTypeIdForIssues
+ class BackfillWorkItemTypeIdForIssues < BatchedMigrationJob
# Basic AR model for issues table
class MigrationIssue < ApplicationRecord
- include ::EachBatch
-
self.table_name = 'issues'
scope :base_query, ->(base_type) { where(work_item_type_id: nil, issue_type: base_type) }
@@ -16,29 +14,27 @@ module Gitlab
MAX_UPDATE_RETRIES = 3
- def perform(start_id, end_id, batch_table, batch_column, sub_batch_size, pause_ms, base_type, base_type_id)
- parent_batch_relation = relation_scoped_to_range(batch_table, batch_column, start_id, end_id, base_type)
+ scope_to ->(relation) {
+ relation.where(issue_type: base_type)
+ }
+
+ job_arguments :base_type, :base_type_id
- parent_batch_relation.each_batch(column: batch_column, of: sub_batch_size) do |sub_batch|
+ def perform
+ each_sub_batch(
+ operation_name: :update_all,
+ batching_scope: -> (relation) { relation.where(work_item_type_id: nil) }
+ ) do |sub_batch|
first, last = sub_batch.pick(Arel.sql('min(id), max(id)'))
# The query need to be reconstructed because .each_batch modifies the default scope
# See: https://gitlab.com/gitlab-org/gitlab/-/issues/330510
reconstructed_sub_batch = MigrationIssue.unscoped.base_query(base_type).where(id: first..last)
- batch_metrics.time_operation(:update_all) do
- update_with_retry(reconstructed_sub_batch, base_type_id)
- end
-
- pause_ms = 0 if pause_ms < 0
- sleep(pause_ms * 0.001)
+ update_with_retry(reconstructed_sub_batch, base_type_id)
end
end
- def batch_metrics
- @batch_metrics ||= Gitlab::Database::BackgroundMigration::BatchMetrics.new
- end
-
private
# Retry mechanism required as update statements on the issues table will randomly take longer than
@@ -64,10 +60,6 @@ module Gitlab
def update_batch(sub_batch, base_type_id)
sub_batch.update_all(work_item_type_id: base_type_id)
end
-
- def relation_scoped_to_range(source_table, source_key_column, start_id, end_id, base_type)
- MigrationIssue.where(source_key_column => start_id..end_id).base_query(base_type)
- end
end
end
end
diff --git a/lib/gitlab/background_migration/batching_strategies/backfill_issue_work_item_type_batching_strategy.rb b/lib/gitlab/background_migration/batching_strategies/backfill_issue_work_item_type_batching_strategy.rb
deleted file mode 100644
index 7d5fef67c25..00000000000
--- a/lib/gitlab/background_migration/batching_strategies/backfill_issue_work_item_type_batching_strategy.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module BackgroundMigration
- module BatchingStrategies
- # Batching class to use for back-filling issue's work_item_type_id for a single issue type.
- # Batches will be scoped to records where the foreign key is NULL and only of a given issue type
- #
- # If no more batches exist in the table, returns nil.
- class BackfillIssueWorkItemTypeBatchingStrategy < PrimaryKeyBatchingStrategy
- def apply_additional_filters(relation, job_arguments:, job_class: nil)
- issue_type = job_arguments.first
-
- relation.where(issue_type: issue_type)
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy.rb b/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy.rb
index 9ad119310f7..72da2b5a2b7 100644
--- a/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy.rb
+++ b/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy.rb
@@ -3,18 +3,9 @@
module Gitlab
module BackgroundMigration
module BatchingStrategies
- # Batching class to use for back-filling project_statistic's container_registry_size.
- # Batches will be scoped to records where the project_ids are migrated
- #
- # If no more batches exist in the table, returns nil.
+ # Used to apply additional filters to the batching table, migrated to
+ # use BatchedMigrationJob#filter_batch with https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93771
class BackfillProjectStatisticsWithContainerRegistrySizeBatchingStrategy < PrimaryKeyBatchingStrategy
- MIGRATION_PHASE_1_ENDED_AT = Date.new(2022, 01, 23).freeze
-
- def apply_additional_filters(relation, job_arguments: [], job_class: nil)
- relation.where(created_at: MIGRATION_PHASE_1_ENDED_AT..).or(
- relation.where(migration_state: 'import_done')
- ).select(:project_id).distinct
- end
end
end
end
diff --git a/lib/gitlab/background_migration/batching_strategies/backfill_vulnerability_reads_cluster_agent_batching_strategy.rb b/lib/gitlab/background_migration/batching_strategies/backfill_vulnerability_reads_cluster_agent_batching_strategy.rb
index f0d015198dc..c2fa00f66de 100644
--- a/lib/gitlab/background_migration/batching_strategies/backfill_vulnerability_reads_cluster_agent_batching_strategy.rb
+++ b/lib/gitlab/background_migration/batching_strategies/backfill_vulnerability_reads_cluster_agent_batching_strategy.rb
@@ -3,16 +3,9 @@
module Gitlab
module BackgroundMigration
module BatchingStrategies
- # Batching class to use for back-filling vulnerability_read's casted_cluster_agent_id from cluster_agent_id.
- # Batches will be scoped to records where the report_type belongs to cluster_image_scanning.
- #
- # If no more batches exist in the table, returns nil.
+ # Used to apply additional filters to the batching table, migrated to
+ # use BatchedMigrationJob#filter_batch with https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93771
class BackfillVulnerabilityReadsClusterAgentBatchingStrategy < PrimaryKeyBatchingStrategy
- CLUSTER_IMAGE_SCANNING_REPORT_TYPE = 7
-
- def apply_additional_filters(relation, job_arguments: [], job_class: nil)
- relation.where(report_type: CLUSTER_IMAGE_SCANNING_REPORT_TYPE)
- end
end
end
end
diff --git a/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy.rb b/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy.rb
index e1855b6cfee..9504d4eec11 100644
--- a/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy.rb
+++ b/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy.rb
@@ -3,14 +3,9 @@
module Gitlab
module BackgroundMigration
module BatchingStrategies
- # Batching class to use for setting state in vulnerabilitites table.
- # Batches will be scoped to records where the dismissed_at is set.
- #
- # If no more batches exist in the table, returns nil.
+ # Used to apply additional filters to the batching table, migrated to
+ # use BatchedMigrationJob#filter_batch with https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93771
class DismissedVulnerabilitiesStrategy < PrimaryKeyBatchingStrategy
- def apply_additional_filters(relation, job_arguments: [], job_class: nil)
- relation.where.not(dismissed_at: nil)
- end
end
end
end
diff --git a/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy.rb b/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy.rb
index 1ffa4a052e5..43352b1bf91 100644
--- a/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy.rb
+++ b/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy.rb
@@ -22,8 +22,8 @@ module Gitlab
def next_batch(table_name, column_name, batch_min_value:, batch_size:, job_arguments:, job_class: nil)
model_class = define_batchable_model(table_name, connection: connection)
- quoted_column_name = model_class.connection.quote_column_name(column_name)
- relation = model_class.where("#{quoted_column_name} >= ?", batch_min_value)
+ arel_column = model_class.arel_table[column_name]
+ relation = model_class.where(arel_column.gteq(batch_min_value))
if job_class
relation = filter_batch(relation,
@@ -32,11 +32,10 @@ module Gitlab
)
end
- relation = apply_additional_filters(relation, job_arguments: job_arguments, job_class: job_class)
next_batch_bounds = nil
relation.each_batch(of: batch_size, column: column_name) do |batch| # rubocop:disable Lint/UnreachableLoop
- next_batch_bounds = batch.pick(Arel.sql("MIN(#{quoted_column_name}), MAX(#{quoted_column_name})"))
+ next_batch_bounds = batch.pick(arel_column.minimum, arel_column.maximum)
break
end
@@ -44,15 +43,6 @@ module Gitlab
next_batch_bounds
end
- # Deprecated
- #
- # Use `scope_to` to define additional filters on the migration job class.
- #
- # see https://docs.gitlab.com/ee/development/database/batched_background_migrations.html#adding-additional-filters.
- def apply_additional_filters(relation, job_arguments: [], job_class: nil)
- relation
- end
-
private
def filter_batch(relation, table_name:, column_name:, job_class:, job_arguments: [])
diff --git a/lib/gitlab/background_migration/batching_strategies/remove_backfilled_job_artifacts_expire_at_batching_strategy.rb b/lib/gitlab/background_migration/batching_strategies/remove_backfilled_job_artifacts_expire_at_batching_strategy.rb
new file mode 100644
index 00000000000..49525479637
--- /dev/null
+++ b/lib/gitlab/background_migration/batching_strategies/remove_backfilled_job_artifacts_expire_at_batching_strategy.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ module BatchingStrategies
+ # Used to apply additional filters to the batching table, migrated to
+ # use BatchedMigrationJob#filter_batch with https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96478
+ class RemoveBackfilledJobArtifactsExpireAtBatchingStrategy < PrimaryKeyBatchingStrategy
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/delete_approval_rules_with_vulnerability.rb b/lib/gitlab/background_migration/delete_approval_rules_with_vulnerability.rb
new file mode 100644
index 00000000000..739197898d9
--- /dev/null
+++ b/lib/gitlab/background_migration/delete_approval_rules_with_vulnerability.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # This class doesn't delete approval rules
+ # as this feature exists only in EE
+ class DeleteApprovalRulesWithVulnerability < BatchedMigrationJob
+ def perform
+ end
+ end
+ end
+end
+
+# rubocop:disable Layout/LineLength
+Gitlab::BackgroundMigration::DeleteApprovalRulesWithVulnerability.prepend_mod_with('Gitlab::BackgroundMigration::DeleteApprovalRulesWithVulnerability')
+# rubocop:enable Layout/LineLength
diff --git a/lib/gitlab/background_migration/destroy_invalid_group_members.rb b/lib/gitlab/background_migration/destroy_invalid_group_members.rb
new file mode 100644
index 00000000000..35ac42f76ab
--- /dev/null
+++ b/lib/gitlab/background_migration/destroy_invalid_group_members.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ class DestroyInvalidGroupMembers < Gitlab::BackgroundMigration::BatchedMigrationJob # rubocop:disable Style/Documentation
+ scope_to ->(relation) do
+ relation.where(source_type: 'Namespace')
+ .joins('LEFT OUTER JOIN namespaces ON members.source_id = namespaces.id')
+ .where(namespaces: { id: nil })
+ end
+
+ def perform
+ each_sub_batch(operation_name: :delete_all) do |sub_batch|
+ invalid_ids = sub_batch.map(&:id)
+ Gitlab::AppLogger.info({ message: 'Removing invalid group member records',
+ deleted_count: invalid_ids.size, ids: invalid_ids })
+
+ sub_batch.delete_all
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/destroy_invalid_project_members.rb b/lib/gitlab/background_migration/destroy_invalid_project_members.rb
new file mode 100644
index 00000000000..3c60f765c29
--- /dev/null
+++ b/lib/gitlab/background_migration/destroy_invalid_project_members.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ class DestroyInvalidProjectMembers < Gitlab::BackgroundMigration::BatchedMigrationJob # rubocop:disable Style/Documentation
+ scope_to ->(relation) { relation.where(source_type: 'Project') }
+
+ def perform
+ each_sub_batch(operation_name: :delete_all) do |sub_batch|
+ invalid_project_members = sub_batch
+ .joins('LEFT OUTER JOIN projects ON members.source_id = projects.id')
+ .where(projects: { id: nil })
+ invalid_ids = invalid_project_members.pluck(:id)
+
+ # the actual delete
+ deleted_count = invalid_project_members.delete_all
+
+ Gitlab::AppLogger.info({ message: 'Removing invalid project member records',
+ deleted_count: deleted_count,
+ ids: invalid_ids })
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/disable_legacy_open_source_licence_for_recent_public_projects.rb b/lib/gitlab/background_migration/disable_legacy_open_source_licence_for_recent_public_projects.rb
new file mode 100644
index 00000000000..824054b31f2
--- /dev/null
+++ b/lib/gitlab/background_migration/disable_legacy_open_source_licence_for_recent_public_projects.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Set `project_settings.legacy_open_source_license_available` to false for public projects created after 17/02/2022
+ class DisableLegacyOpenSourceLicenceForRecentPublicProjects < ::Gitlab::BackgroundMigration::BatchedMigrationJob
+ PUBLIC = 20
+ THRESHOLD_DATE = '2022-02-17 09:00:00'
+
+ # Migration only version of `project_settings` table
+ class ProjectSetting < ApplicationRecord
+ self.table_name = 'project_settings'
+ end
+
+ def perform
+ each_sub_batch(
+ operation_name: :disable_legacy_open_source_licence_for_recent_public_projects,
+ batching_scope: ->(relation) {
+ relation.where(visibility_level: PUBLIC).where('created_at >= ?', THRESHOLD_DATE)
+ }
+ ) do |sub_batch|
+ ProjectSetting.where(project_id: sub_batch)
+ .where(legacy_open_source_license_available: true)
+ .update_all(legacy_open_source_license_available: false)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_one_mb.rb b/lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_one_mb.rb
new file mode 100644
index 00000000000..6e4d5d8ddcb
--- /dev/null
+++ b/lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_one_mb.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Set `project_settings.legacy_open_source_license_available` to false for projects less than 1 MB
+ class DisableLegacyOpenSourceLicenseForProjectsLessThanOneMb < ::Gitlab::BackgroundMigration::BatchedMigrationJob
+ scope_to ->(relation) { relation.where(legacy_open_source_license_available: true) }
+
+ def perform
+ each_sub_batch(operation_name: :disable_legacy_open_source_license_for_projects_less_than_one_mb) 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 < ?', 1.megabyte)
+ .update_all(updates)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/mailers/unconfirm_mailer.rb b/lib/gitlab/background_migration/mailers/unconfirm_mailer.rb
index 3605b157f4f..2bf631c6c7d 100644
--- a/lib/gitlab/background_migration/mailers/unconfirm_mailer.rb
+++ b/lib/gitlab/background_migration/mailers/unconfirm_mailer.rb
@@ -11,7 +11,7 @@ module Gitlab
@user = user
@verification_from_mail = Gitlab.config.gitlab.email_from
- mail(
+ mail_with_locale(
template_path: 'unconfirm_mailer',
template_name: 'unconfirm_notification_email',
to: @user.notification_email_or_default,
diff --git a/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb b/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb
index 72380af2c53..9a42d035285 100644
--- a/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb
+++ b/lib/gitlab/background_migration/recalculate_vulnerabilities_occurrences_uuid.rb
@@ -58,7 +58,7 @@ class Gitlab::BackgroundMigration::RecalculateVulnerabilitiesOccurrencesUuid # r
development: "a143e9e2-41b3-47bc-9a19-081d089229f4",
test: "a143e9e2-41b3-47bc-9a19-081d089229f4",
staging: "a6930898-a1b2-4365-ab18-12aa474d9b26",
- production: "58dc0f06-936c-43b3-93bb-71693f1b6570"
+ production: "58dc0f06-936c-43b3-93bb-71693f1b6570"
}.freeze
NAMESPACE_REGEX = /(\h{8})-(\h{4})-(\h{4})-(\h{4})-(\h{4})(\h{8})/.freeze
diff --git a/lib/gitlab/background_migration/remove_backfilled_job_artifacts_expire_at.rb b/lib/gitlab/background_migration/remove_backfilled_job_artifacts_expire_at.rb
new file mode 100644
index 00000000000..d30263976e8
--- /dev/null
+++ b/lib/gitlab/background_migration/remove_backfilled_job_artifacts_expire_at.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # This detects and fixes job artifacts that have `expire_at` wrongly backfilled by the migration
+ # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47723.
+ # These job artifacts will not be deleted and will have their `expire_at` removed.
+ class RemoveBackfilledJobArtifactsExpireAt < BatchedMigrationJob
+ # The migration would have backfilled `expire_at`
+ # to midnight on the 22nd of the month of the local timezone,
+ # storing it as UTC time in the database.
+ #
+ # If the timezone setting has changed since the migration,
+ # the `expire_at` stored in the database could have changed to a different local time other than midnight.
+ # For example:
+ # - changing timezone from UTC+02:00 to UTC+02:30 would change the `expire_at` in local time 00:00:00 to 00:30:00.
+ # - changing timezone from UTC+00:00 to UTC-01:00 would change the `expire_at` in local time 00:00:00 to 23:00:00
+ # on the previous day (21st).
+ #
+ # Therefore job artifacts that have `expire_at` exactly on the 00, 30 or 45 minute mark
+ # on the dates 21, 22, 23 of the month will not be deleted.
+ # https://en.wikipedia.org/wiki/List_of_UTC_time_offsets
+ EXPIRES_ON_21_22_23_AT_MIDNIGHT_IN_TIMEZONE = <<~SQL
+ EXTRACT(day FROM timezone('UTC', expire_at)) IN (21, 22, 23)
+ AND EXTRACT(minute FROM timezone('UTC', expire_at)) IN (0, 30, 45)
+ AND EXTRACT(second FROM timezone('UTC', expire_at)) = 0
+ SQL
+
+ scope_to ->(relation) {
+ relation.where(EXPIRES_ON_21_22_23_AT_MIDNIGHT_IN_TIMEZONE)
+ .or(relation.where(file_type: 3))
+ }
+
+ def perform
+ each_sub_batch(
+ operation_name: :update_all
+ ) do |sub_batch|
+ sub_batch.update_all(expire_at: nil)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/remove_self_managed_wiki_notes.rb b/lib/gitlab/background_migration/remove_self_managed_wiki_notes.rb
new file mode 100644
index 00000000000..5b1d630bb03
--- /dev/null
+++ b/lib/gitlab/background_migration/remove_self_managed_wiki_notes.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Removes obsolete wiki notes
+ class RemoveSelfManagedWikiNotes < BatchedMigrationJob
+ def perform
+ each_sub_batch(
+ operation_name: :delete_all
+ ) do |sub_batch|
+ sub_batch.where(noteable_type: 'Wiki').delete_all
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item.rb b/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item.rb
new file mode 100644
index 00000000000..718fb0aaa71
--- /dev/null
+++ b/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Renames all system notes created when an issuable task is checked/unchecked
+ # from `task` into `checklist item`
+ # `marked the task **Task 1** as incomplete` => `marked the checklist item **Task 1** as incomplete`
+ class RenameTaskSystemNoteToChecklistItem < BatchedMigrationJob
+ REPLACE_REGEX = '\Amarked\sthe\stask'
+ TEXT_REPLACEMENT = 'marked the checklist item'
+
+ scope_to ->(relation) {
+ relation.where(system_note_metadata: { action: :task })
+ }
+
+ def perform
+ each_sub_batch(operation_name: :update_all) do |sub_batch|
+ ApplicationRecord.connection.execute <<~SQL
+ UPDATE notes
+ SET note = REGEXP_REPLACE(notes.note,'#{REPLACE_REGEX}', '#{TEXT_REPLACEMENT}')
+ FROM (#{sub_batch.select(:note_id).to_sql}) AS metadata_fields(note_id)
+ WHERE notes.id = note_id
+ SQL
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/set_correct_vulnerability_state.rb b/lib/gitlab/background_migration/set_correct_vulnerability_state.rb
index fd6cbcb8d05..a0cfeed618a 100644
--- a/lib/gitlab/background_migration/set_correct_vulnerability_state.rb
+++ b/lib/gitlab/background_migration/set_correct_vulnerability_state.rb
@@ -6,11 +6,10 @@ module Gitlab
class SetCorrectVulnerabilityState < BatchedMigrationJob
DISMISSED_STATE = 2
+ scope_to ->(relation) { relation.where.not(dismissed_at: nil) }
+
def perform
- each_sub_batch(
- operation_name: :update_vulnerabilities_state,
- batching_scope: -> (relation) { relation.where.not(dismissed_at: nil) }
- ) do |sub_batch|
+ each_sub_batch(operation_name: :update_vulnerabilities_state) do |sub_batch|
sub_batch.update_all(state: DISMISSED_STATE)
end
end
diff --git a/lib/gitlab/base_doorkeeper_controller.rb b/lib/gitlab/base_doorkeeper_controller.rb
index 81b01395542..c8520993b8e 100644
--- a/lib/gitlab/base_doorkeeper_controller.rb
+++ b/lib/gitlab/base_doorkeeper_controller.rb
@@ -3,6 +3,7 @@
# This is a base controller for doorkeeper.
# It adds the `can?` helper used in the views.
module Gitlab
+ # rubocop:disable Rails/ApplicationController
class BaseDoorkeeperController < ActionController::Base
include Gitlab::Allowable
include EnforcesTwoFactorAuthentication
@@ -12,4 +13,5 @@ module Gitlab
helper_method :can?
end
+ # rubocop:enable Rails/ApplicationController
end
diff --git a/lib/gitlab/cache/helpers.rb b/lib/gitlab/cache/helpers.rb
index 7b11d6bc9ff..48b6ca59367 100644
--- a/lib/gitlab/cache/helpers.rb
+++ b/lib/gitlab/cache/helpers.rb
@@ -57,9 +57,19 @@ module Gitlab
# @param expires_in [ActiveSupport::Duration, Integer] an expiry time for the cache entry
# @return [String]
def cached_object(object, presenter:, presenter_args:, context:, expires_in:)
- cache.fetch(contextual_cache_key(presenter, object, context), expires_in: expires_in) do
- Gitlab::Json.dump(presenter.represent(object, **presenter_args).as_json)
+ misses = 0
+
+ json = cache.fetch(contextual_cache_key(presenter, object, context), expires_in: expires_in) do
+ time_action(render_type: :object) do
+ misses += 1
+
+ Gitlab::Json.dump(presenter.represent(object, **presenter_args).as_json)
+ end
end
+
+ increment_cache_metric(render_type: :object, total_count: 1, miss_count: misses)
+
+ json
end
# Used for fetching or rendering multiple objects
@@ -71,10 +81,18 @@ module Gitlab
# @param expires_in [ActiveSupport::Duration, Integer] an expiry time for the cache entry
# @return [Array<String>]
def cached_collection(collection, presenter:, presenter_args:, context:, expires_in:)
+ misses = 0
+
json = fetch_multi(presenter, collection, context: context, expires_in: expires_in) do |obj|
- Gitlab::Json.dump(presenter.represent(obj, **presenter_args).as_json)
+ time_action(render_type: :collection) do
+ misses += 1
+
+ Gitlab::Json.dump(presenter.represent(obj, **presenter_args).as_json)
+ end
end
+ increment_cache_metric(render_type: :collection, total_count: collection.length, miss_count: misses)
+
json.values
end
@@ -106,6 +124,57 @@ module Gitlab
contextual_cache_key(presenter, object, context)
end
end
+
+ def increment_cache_metric(render_type:, total_count:, miss_count:)
+ return unless Feature.enabled?(:add_timing_to_certain_cache_actions)
+ return unless caller_id
+
+ metric_name = :cached_object_operations_total
+ hit_count = total_count - miss_count
+
+ current_transaction&.increment(
+ metric_name,
+ hit_count,
+ { caller_id: caller_id, render_type: render_type, cache_hit: true }
+ )
+
+ current_transaction&.increment(
+ metric_name,
+ miss_count,
+ { caller_id: caller_id, render_type: render_type, cache_hit: false }
+ )
+ end
+
+ def time_action(render_type:, &block)
+ if Feature.enabled?(:add_timing_to_certain_cache_actions)
+ real_start = Gitlab::Metrics::System.monotonic_time
+
+ presented_object = yield
+
+ real_duration_histogram(render_type).observe({}, Gitlab::Metrics::System.monotonic_time - real_start)
+
+ presented_object
+ else
+ yield
+ end
+ end
+
+ def real_duration_histogram(render_type)
+ Gitlab::Metrics.histogram(
+ :gitlab_presentable_object_cacheless_render_real_duration_seconds,
+ 'Duration of generating presentable objects to be cached in real time',
+ { caller_id: caller_id, render_type: render_type },
+ [0.1, 0.5, 1, 2]
+ )
+ end
+
+ def current_transaction
+ @current_transaction ||= ::Gitlab::Metrics::WebTransaction.current
+ end
+
+ def caller_id
+ @caller_id ||= Gitlab::ApplicationContext.current_context_attribute(:caller_id)
+ end
end
end
end
diff --git a/lib/gitlab/ci/ansi2html.rb b/lib/gitlab/ci/ansi2html.rb
index 10233cf4228..2ab702aa4f9 100644
--- a/lib/gitlab/ci/ansi2html.rb
+++ b/lib/gitlab/ci/ansi2html.rb
@@ -19,11 +19,11 @@ module Gitlab
}.freeze
STYLE_SWITCHES = {
- bold: 0x01,
- italic: 0x02,
- underline: 0x04,
- conceal: 0x08,
- cross: 0x10
+ bold: 0x01,
+ italic: 0x02,
+ underline: 0x04,
+ conceal: 0x08,
+ cross: 0x10
}.freeze
def self.convert(ansi, state = nil)
diff --git a/lib/gitlab/ci/ansi2json/parser.rb b/lib/gitlab/ci/ansi2json/parser.rb
index 79b42a5f5bf..fdd49df1e24 100644
--- a/lib/gitlab/ci/ansi2json/parser.rb
+++ b/lib/gitlab/ci/ansi2json/parser.rb
@@ -20,11 +20,11 @@ module Gitlab
}.freeze
STYLE_SWITCHES = {
- bold: 0x01,
- italic: 0x02,
- underline: 0x04,
- conceal: 0x08,
- cross: 0x10
+ bold: 0x01,
+ italic: 0x02,
+ underline: 0x04,
+ conceal: 0x08,
+ cross: 0x10
}.freeze
def self.bold?(mask)
diff --git a/lib/gitlab/ci/build/artifacts/adapters/zip_stream.rb b/lib/gitlab/ci/build/artifacts/adapters/zip_stream.rb
deleted file mode 100644
index 690a47097c6..00000000000
--- a/lib/gitlab/ci/build/artifacts/adapters/zip_stream.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Ci
- module Build
- module Artifacts
- module Adapters
- class ZipStream
- MAX_DECOMPRESSED_SIZE = 100.megabytes
- MAX_FILES_PROCESSED = 50
-
- attr_reader :stream
-
- InvalidStreamError = Class.new(StandardError)
-
- def initialize(stream)
- raise InvalidStreamError, "Stream is required" unless stream
-
- @stream = stream
- @files_processed = 0
- end
-
- def each_blob
- Zip::InputStream.open(stream) do |zio|
- while entry = zio.get_next_entry
- break if at_files_processed_limit?
- next unless should_process?(entry)
-
- @files_processed += 1
-
- yield entry.get_input_stream.read
- end
- end
- end
-
- private
-
- def should_process?(entry)
- file?(entry) && !too_large?(entry)
- end
-
- def file?(entry)
- # Check the file name as a workaround for incorrect
- # file type detection when using InputStream
- # https://github.com/rubyzip/rubyzip/issues/533
- entry.file? && !entry.name.end_with?('/')
- end
-
- def too_large?(entry)
- entry.size > MAX_DECOMPRESSED_SIZE
- end
-
- def at_files_processed_limit?
- @files_processed >= MAX_FILES_PROCESSED
- end
- end
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/ci/build/context/build.rb b/lib/gitlab/ci/build/context/build.rb
index 641aa71fb4e..a1a8e9288c7 100644
--- a/lib/gitlab/ci/build/context/build.rb
+++ b/lib/gitlab/ci/build/context/build.rb
@@ -32,7 +32,18 @@ module Gitlab
end
def build_attributes
- attributes.merge(pipeline_attributes)
+ attributes.merge(pipeline_attributes, ci_stage_attributes)
+ end
+
+ def ci_stage_attributes
+ {
+ ci_stage: ::Ci::Stage.new(
+ name: attributes[:stage],
+ position: attributes[:stage_idx],
+ pipeline: pipeline_attributes[:pipeline],
+ project: pipeline_attributes[:project]
+ )
+ }
end
end
end
diff --git a/lib/gitlab/ci/build/rules/rule/clause/exists.rb b/lib/gitlab/ci/build/rules/rule/clause/exists.rb
index e2b54797dc8..aebd81e7b07 100644
--- a/lib/gitlab/ci/build/rules/rule/clause/exists.rb
+++ b/lib/gitlab/ci/build/rules/rule/clause/exists.rb
@@ -24,7 +24,7 @@ module Gitlab
private
def worktree_paths(context)
- return unless context.project
+ return [] unless context.project
if @top_level_only
context.top_level_worktree_paths
diff --git a/lib/gitlab/ci/config/entry/current_variables.rb b/lib/gitlab/ci/config/entry/current_variables.rb
new file mode 100644
index 00000000000..3b6721ec92d
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/current_variables.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ ##
+ # Entry that represents CI/CD variables.
+ # The class will be renamed to `Variables` when removing the FF `ci_variables_refactoring_to_variable`.
+ #
+ class CurrentVariables < ::Gitlab::Config::Entry::ComposableHash
+ include ::Gitlab::Config::Entry::Validatable
+
+ validations do
+ validates :config, type: Hash
+ end
+
+ # Enable these lines when removing the FF `ci_variables_refactoring_to_variable`
+ # and renaming this class to `Variables`.
+ # def self.default(**)
+ # {}
+ # end
+
+ def value
+ @entries.to_h do |key, entry|
+ [key.to_s, entry.value]
+ end
+ end
+
+ def value_with_data
+ @entries.to_h do |key, entry|
+ [key.to_s, entry.value_with_data]
+ end
+ end
+
+ private
+
+ def composable_class(_name, _config)
+ Entry::Variable
+ end
+
+ def composable_metadata
+ { allowed_value_data: opt(:allowed_value_data) }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/entry/environment.rb b/lib/gitlab/ci/config/entry/environment.rb
index 96ba3553b46..a727da87308 100644
--- a/lib/gitlab/ci/config/entry/environment.rb
+++ b/lib/gitlab/ci/config/entry/environment.rb
@@ -54,7 +54,7 @@ module Gitlab
validates :on_stop, type: String, allow_nil: true
validates :kubernetes, type: Hash, allow_nil: true
- validates :auto_stop_in, duration: { parser: ::Gitlab::Ci::Build::DurationParser }, allow_nil: true
+ validates :auto_stop_in, type: String, allow_nil: true
end
end
diff --git a/lib/gitlab/ci/config/entry/image.rb b/lib/gitlab/ci/config/entry/image.rb
index 613f7ff3370..84e31ca1fc6 100644
--- a/lib/gitlab/ci/config/entry/image.rb
+++ b/lib/gitlab/ci/config/entry/image.rb
@@ -11,10 +11,7 @@ module Gitlab
include ::Gitlab::Ci::Config::Entry::Imageable
validations do
- validates :config, allowed_keys: IMAGEABLE_ALLOWED_KEYS,
- if: :ci_docker_image_pull_policy_enabled?
- validates :config, allowed_keys: IMAGEABLE_LEGACY_ALLOWED_KEYS,
- unless: :ci_docker_image_pull_policy_enabled?
+ validates :config, allowed_keys: IMAGEABLE_ALLOWED_KEYS
end
def value
@@ -25,7 +22,7 @@ module Gitlab
name: @config[:name],
entrypoint: @config[:entrypoint],
ports: (ports_value if ports_defined?),
- pull_policy: (ci_docker_image_pull_policy_enabled? ? pull_policy_value : nil)
+ pull_policy: pull_policy_value
}.compact
else
{}
diff --git a/lib/gitlab/ci/config/entry/imageable.rb b/lib/gitlab/ci/config/entry/imageable.rb
index f045ee3d549..1aecfee9ab9 100644
--- a/lib/gitlab/ci/config/entry/imageable.rb
+++ b/lib/gitlab/ci/config/entry/imageable.rb
@@ -13,7 +13,6 @@ module Gitlab
include ::Gitlab::Config::Entry::Configurable
IMAGEABLE_ALLOWED_KEYS = %i[name entrypoint ports pull_policy].freeze
- IMAGEABLE_LEGACY_ALLOWED_KEYS = %i[name entrypoint ports].freeze
included do
include ::Gitlab::Config::Entry::Validatable
@@ -47,10 +46,6 @@ module Gitlab
opt(:with_image_ports)
end
- def ci_docker_image_pull_policy_enabled?
- ::Feature.enabled?(:ci_docker_image_pull_policy)
- end
-
def skip_config_hash_validation?
true
end
diff --git a/lib/gitlab/ci/config/entry/legacy_variables.rb b/lib/gitlab/ci/config/entry/legacy_variables.rb
new file mode 100644
index 00000000000..5379f707537
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/legacy_variables.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ ##
+ # Entry that represents environment variables.
+ # This is legacy implementation and will be removed with the FF `ci_variables_refactoring_to_variable`.
+ #
+ class LegacyVariables < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Validatable
+
+ ALLOWED_VALUE_DATA = %i[value description].freeze
+
+ validations do
+ validates :config, variables: { allowed_value_data: ALLOWED_VALUE_DATA }, if: :use_value_data?
+ validates :config, variables: true, unless: :use_value_data?
+ end
+
+ def value
+ @config.to_h { |key, value| [key.to_s, expand_value(value)[:value]] }
+ end
+
+ def value_with_data
+ @config.to_h { |key, value| [key.to_s, expand_value(value)] }
+ end
+
+ def use_value_data?
+ opt(:use_value_data)
+ end
+
+ private
+
+ def expand_value(value)
+ if value.is_a?(Hash)
+ { value: value[:value].to_s, description: value[:description] }.compact
+ else
+ { value: value.to_s }
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/entry/processable.rb b/lib/gitlab/ci/config/entry/processable.rb
index 78794f524f4..2d2032b1d8c 100644
--- a/lib/gitlab/ci/config/entry/processable.rb
+++ b/lib/gitlab/ci/config/entry/processable.rb
@@ -29,7 +29,7 @@ module Gitlab
in: %i[only except start_in],
message: 'key may not be used with `rules`'
},
- if: :has_rules?
+ if: :has_rules?
with_options allow_nil: true do
validates :extends, array_of_strings_or_string: true
@@ -120,7 +120,7 @@ module Gitlab
stage: stage_value,
extends: extends,
rules: rules_value,
- job_variables: variables_value.to_h,
+ job_variables: variables_entry.value_with_data,
root_variables_inheritance: root_variables_inheritance,
only: only_value,
except: except_value,
diff --git a/lib/gitlab/ci/config/entry/root.rb b/lib/gitlab/ci/config/entry/root.rb
index ff11c757dfa..57e89bd7bc5 100644
--- a/lib/gitlab/ci/config/entry/root.rb
+++ b/lib/gitlab/ci/config/entry/root.rb
@@ -48,9 +48,10 @@ module Gitlab
description: 'Script that will be executed after each job.',
reserved: true
+ # use_value_data will be removed with the FF ci_variables_refactoring_to_variable
entry :variables, Entry::Variables,
description: 'Environment variables that will be used.',
- metadata: { use_value_data: true },
+ metadata: { use_value_data: true, allowed_value_data: %i[value description] },
reserved: true
entry :stages, Entry::Stages,
diff --git a/lib/gitlab/ci/config/entry/service.rb b/lib/gitlab/ci/config/entry/service.rb
index 0e19447dff8..4b3a9990df4 100644
--- a/lib/gitlab/ci/config/entry/service.rb
+++ b/lib/gitlab/ci/config/entry/service.rb
@@ -11,14 +11,9 @@ module Gitlab
include ::Gitlab::Ci::Config::Entry::Imageable
ALLOWED_KEYS = %i[command alias variables].freeze
- LEGACY_ALLOWED_KEYS = %i[command alias variables].freeze
validations do
- validates :config, allowed_keys: ALLOWED_KEYS + IMAGEABLE_ALLOWED_KEYS,
- if: :ci_docker_image_pull_policy_enabled?
- validates :config, allowed_keys: LEGACY_ALLOWED_KEYS + IMAGEABLE_LEGACY_ALLOWED_KEYS,
- unless: :ci_docker_image_pull_policy_enabled?
-
+ validates :config, allowed_keys: ALLOWED_KEYS + IMAGEABLE_ALLOWED_KEYS
validates :command, array_of_strings: true, allow_nil: true
validates :alias, type: String, allow_nil: true
validates :alias, type: String, presence: true, unless: ->(record) { record.ports.blank? }
@@ -43,7 +38,7 @@ module Gitlab
{ name: @config }
elsif hash?
@config.merge(
- pull_policy: (pull_policy_value if ci_docker_image_pull_policy_enabled?)
+ pull_policy: pull_policy_value
).compact
else
{}
diff --git a/lib/gitlab/ci/config/entry/variable.rb b/lib/gitlab/ci/config/entry/variable.rb
new file mode 100644
index 00000000000..253888aadeb
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/variable.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ ##
+ # Entry that represents a CI/CD variable.
+ #
+ class Variable < ::Gitlab::Config::Entry::Simplifiable
+ strategy :SimpleVariable, if: -> (config) { SimpleVariable.applies_to?(config) }
+ strategy :ComplexVariable, if: -> (config) { ComplexVariable.applies_to?(config) }
+
+ class SimpleVariable < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Validatable
+
+ class << self
+ def applies_to?(config)
+ Gitlab::Config::Entry::Validators::AlphanumericValidator.validate(config)
+ end
+ end
+
+ validations do
+ validates :key, alphanumeric: true
+ validates :config, alphanumeric: true
+ end
+
+ def value
+ @config.to_s
+ end
+
+ def value_with_data
+ { value: @config.to_s }
+ end
+ end
+
+ class ComplexVariable < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Validatable
+
+ class << self
+ def applies_to?(config)
+ config.is_a?(Hash)
+ end
+ end
+
+ 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?
+
+ validate do
+ allowed_value_data = Array(opt(:allowed_value_data))
+
+ if allowed_value_data.any?
+ extra_keys = config.keys - allowed_value_data
+
+ errors.add(:config, "uses invalid data keys: #{extra_keys.join(', ')}") if extra_keys.present?
+ else
+ errors.add(:config, "must be a string")
+ end
+ end
+ end
+
+ def value
+ config_value.to_s
+ end
+
+ def value_with_data
+ { value: value, description: config_description }.compact
+ end
+
+ def config_value
+ @config[:value]
+ end
+
+ def config_description
+ @config[:description]
+ end
+
+ def config_value_defined?
+ config.key?(:value)
+ end
+
+ def config_description_defined?
+ config.key?(:description)
+ end
+ end
+
+ class UnknownStrategy < ::Gitlab::Config::Entry::Node
+ def errors
+ ["variable definition must be either a string or a hash"]
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/entry/variables.rb b/lib/gitlab/ci/config/entry/variables.rb
index efb469ee32a..0284958d9d4 100644
--- a/lib/gitlab/ci/config/entry/variables.rb
+++ b/lib/gitlab/ci/config/entry/variables.rb
@@ -5,43 +5,21 @@ module Gitlab
class Config
module Entry
##
- # Entry that represents environment variables.
+ # Entry that represents CI/CD variables.
+ # CurrentVariables will be renamed to this class when removing the FF `ci_variables_refactoring_to_variable`.
#
- class Variables < ::Gitlab::Config::Entry::Node
- include ::Gitlab::Config::Entry::Validatable
-
- ALLOWED_VALUE_DATA = %i[value description].freeze
-
- validations do
- validates :config, variables: { allowed_value_data: ALLOWED_VALUE_DATA }, if: :use_value_data?
- validates :config, variables: true, unless: :use_value_data?
- end
-
- def value
- @config.to_h { |key, value| [key.to_s, expand_value(value)[:value]] }
+ class Variables
+ def self.new(...)
+ if YamlProcessor::FeatureFlags.enabled?(:ci_variables_refactoring_to_variable)
+ CurrentVariables.new(...)
+ else
+ LegacyVariables.new(...)
+ end
end
def self.default(**)
{}
end
-
- def value_with_data
- @config.to_h { |key, value| [key.to_s, expand_value(value)] }
- end
-
- def use_value_data?
- opt(:use_value_data)
- end
-
- private
-
- def expand_value(value)
- if value.is_a?(Hash)
- { value: value[:value].to_s, description: value[:description] }
- else
- { value: value.to_s, description: nil }
- end
- end
end
end
end
diff --git a/lib/gitlab/ci/jwt_v2.rb b/lib/gitlab/ci/jwt_v2.rb
index 278353220e4..4e01688a955 100644
--- a/lib/gitlab/ci/jwt_v2.rb
+++ b/lib/gitlab/ci/jwt_v2.rb
@@ -8,7 +8,7 @@ module Gitlab
def reserved_claims
super.merge(
iss: Settings.gitlab.base_url,
- sub: "project_path:#{project.full_path}:ref_type:#{ref_type}:ref:#{source_ref}",
+ sub: "project_path:#{project.full_path}:ref_type:#{ref_type}:ref:#{source_ref}",
aud: Settings.gitlab.base_url
)
end
diff --git a/lib/gitlab/ci/parsers/sbom/cyclonedx.rb b/lib/gitlab/ci/parsers/sbom/cyclonedx.rb
index deb20a2138c..aa594ca4049 100644
--- a/lib/gitlab/ci/parsers/sbom/cyclonedx.rb
+++ b/lib/gitlab/ci/parsers/sbom/cyclonedx.rb
@@ -6,7 +6,6 @@ module Gitlab
module Sbom
class Cyclonedx
SUPPORTED_SPEC_VERSIONS = %w[1.4].freeze
- COMPONENT_ATTRIBUTES = %w[type name version].freeze
def parse!(blob, sbom_report)
@report = sbom_report
@@ -62,10 +61,17 @@ module Gitlab
end
def parse_components
- data['components']&.each do |component|
- next unless supported_component_type?(component['type'])
+ data['components']&.each do |component_data|
+ type = component_data['type']
+ next unless supported_component_type?(type)
- report.add_component(component.slice(*COMPONENT_ATTRIBUTES))
+ component = ::Gitlab::Ci::Reports::Sbom::Component.new(
+ type: type,
+ name: component_data['name'],
+ version: component_data['version']
+ )
+
+ report.add_component(component)
end
end
diff --git a/lib/gitlab/ci/parsers/sbom/source/dependency_scanning.rb b/lib/gitlab/ci/parsers/sbom/source/dependency_scanning.rb
index ad04b3257f9..00ca723b258 100644
--- a/lib/gitlab/ci/parsers/sbom/source/dependency_scanning.rb
+++ b/lib/gitlab/ci/parsers/sbom/source/dependency_scanning.rb
@@ -21,11 +21,11 @@ module Gitlab
def source
return unless required_attributes_present?
- {
- 'type' => :dependency_scanning,
- 'data' => data,
- 'fingerprint' => fingerprint
- }
+ ::Gitlab::Ci::Reports::Sbom::Source.new(
+ type: :dependency_scanning,
+ data: data,
+ fingerprint: fingerprint
+ )
end
private
diff --git a/lib/gitlab/ci/parsers/security/common.rb b/lib/gitlab/ci/parsers/security/common.rb
index 13a159f3745..da7faaab6ff 100644
--- a/lib/gitlab/ci/parsers/security/common.rb
+++ b/lib/gitlab/ci/parsers/security/common.rb
@@ -7,16 +7,16 @@ module Gitlab
class Common
SecurityReportParserError = Class.new(Gitlab::Ci::Parsers::ParserError)
- def self.parse!(json_data, report, vulnerability_finding_signatures_enabled = false, validate: false)
- new(json_data, report, vulnerability_finding_signatures_enabled, validate: validate).parse!
+ def self.parse!(json_data, report, signatures_enabled: false, validate: false)
+ new(json_data, report, signatures_enabled: signatures_enabled, validate: validate).parse!
end
- def initialize(json_data, report, vulnerability_finding_signatures_enabled = false, validate: false)
+ def initialize(json_data, report, signatures_enabled: false, validate: false)
@json_data = json_data
@report = report
@project = report.project
@validate = validate
- @vulnerability_finding_signatures_enabled = vulnerability_finding_signatures_enabled
+ @signatures_enabled = signatures_enabled
end
def parse!
@@ -26,7 +26,7 @@ module Gitlab
raise SecurityReportParserError, "Invalid report format" unless report_data.is_a?(Hash)
- create_scanner
+ create_scanner(top_level_scanner_data)
create_scan
create_analyzer
@@ -77,7 +77,7 @@ module Gitlab
report_data,
report.version,
project: @project,
- scanner: top_level_scanner
+ scanner: top_level_scanner_data
)
end
@@ -89,8 +89,8 @@ module Gitlab
@report_version ||= report_data['version']
end
- def top_level_scanner
- @top_level_scanner ||= report_data.dig('scan', 'scanner')
+ def top_level_scanner_data
+ @top_level_scanner_data ||= report_data.dig('scan', 'scanner')
end
def scan_data
@@ -119,7 +119,7 @@ module Gitlab
evidence = create_evidence(data['evidence'])
signatures = create_signatures(tracking_data(data))
- if @vulnerability_finding_signatures_enabled && !signatures.empty?
+ if @signatures_enabled && !signatures.empty?
# NOT the signature_sha - the compare key is hashed
# to create the project_fingerprint
highest_priority_signature = signatures.max_by(&:priority)
@@ -138,7 +138,7 @@ module Gitlab
evidence: evidence,
severity: parse_severity_level(data['severity']),
confidence: parse_confidence_level(data['confidence']),
- scanner: create_scanner(data['scanner']),
+ scanner: create_scanner(top_level_scanner_data || data['scanner']),
scan: report&.scan,
identifiers: identifiers,
flags: flags,
@@ -149,7 +149,7 @@ module Gitlab
details: data['details'] || {},
signatures: signatures,
project_id: @project.id,
- vulnerability_finding_signatures_enabled: @vulnerability_finding_signatures_enabled))
+ vulnerability_finding_signatures_enabled: @signatures_enabled))
end
def create_signatures(tracking)
@@ -208,7 +208,7 @@ module Gitlab
report.analyzer = ::Gitlab::Ci::Reports::Security::Analyzer.new(**params)
end
- def create_scanner(scanner_data = top_level_scanner)
+ def create_scanner(scanner_data)
return unless scanner_data.is_a?(Hash)
report.add_scanner(
diff --git a/lib/gitlab/ci/parsers/security/validators/schema_validator.rb b/lib/gitlab/ci/parsers/security/validators/schema_validator.rb
index c075ada725a..28d6620e5c4 100644
--- a/lib/gitlab/ci/parsers/security/validators/schema_validator.rb
+++ b/lib/gitlab/ci/parsers/security/validators/schema_validator.rb
@@ -7,14 +7,14 @@ module Gitlab
module Validators
class SchemaValidator
SUPPORTED_VERSIONS = {
- cluster_image_scanning: %w[14.0.4 14.0.5 14.0.6 14.1.0 14.1.1 14.1.2],
- container_scanning: %w[14.0.0 14.0.1 14.0.2 14.0.3 14.0.4 14.0.5 14.0.6 14.1.0 14.1.1 14.1.2],
- coverage_fuzzing: %w[14.0.0 14.0.1 14.0.2 14.0.3 14.0.4 14.0.5 14.0.6 14.1.0 14.1.1 14.1.2],
- dast: %w[14.0.0 14.0.1 14.0.2 14.0.3 14.0.4 14.0.5 14.0.6 14.1.0 14.1.1 14.1.2],
- api_fuzzing: %w[14.0.0 14.0.1 14.0.2 14.0.3 14.0.4 14.0.5 14.0.6 14.1.0 14.1.1 14.1.2],
- dependency_scanning: %w[14.0.0 14.0.1 14.0.2 14.0.3 14.0.4 14.0.5 14.0.6 14.1.0 14.1.1 14.1.2],
- sast: %w[14.0.0 14.0.1 14.0.2 14.0.3 14.0.4 14.0.5 14.0.6 14.1.0 14.1.1 14.1.2],
- secret_detection: %w[14.0.0 14.0.1 14.0.2 14.0.3 14.0.4 14.0.5 14.0.6 14.1.0 14.1.1 14.1.2]
+ cluster_image_scanning: %w[14.0.4 14.0.5 14.0.6 14.1.0 14.1.1 14.1.2 14.1.3 15.0.0],
+ container_scanning: %w[14.0.0 14.0.1 14.0.2 14.0.3 14.0.4 14.0.5 14.0.6 14.1.0 14.1.1 14.1.2 14.1.3 15.0.0],
+ coverage_fuzzing: %w[14.0.0 14.0.1 14.0.2 14.0.3 14.0.4 14.0.5 14.0.6 14.1.0 14.1.1 14.1.2 14.1.3 15.0.0],
+ dast: %w[14.0.0 14.0.1 14.0.2 14.0.3 14.0.4 14.0.5 14.0.6 14.1.0 14.1.1 14.1.2 14.1.3 15.0.0],
+ api_fuzzing: %w[14.0.0 14.0.1 14.0.2 14.0.3 14.0.4 14.0.5 14.0.6 14.1.0 14.1.1 14.1.2 14.1.3 15.0.0],
+ dependency_scanning: %w[14.0.0 14.0.1 14.0.2 14.0.3 14.0.4 14.0.5 14.0.6 14.1.0 14.1.1 14.1.2 14.1.3 15.0.0],
+ sast: %w[14.0.0 14.0.1 14.0.2 14.0.3 14.0.4 14.0.5 14.0.6 14.1.0 14.1.1 14.1.2 14.1.3 15.0.0],
+ secret_detection: %w[14.0.0 14.0.1 14.0.2 14.0.3 14.0.4 14.0.5 14.0.6 14.1.0 14.1.1 14.1.2 14.1.3 15.0.0]
}.freeze
VERSIONS_TO_REMOVE_IN_16_0 = [].freeze
diff --git a/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/cluster-image-scanning-report-format.json b/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/cluster-image-scanning-report-format.json
new file mode 100644
index 00000000000..db4c7ab1425
--- /dev/null
+++ b/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/cluster-image-scanning-report-format.json
@@ -0,0 +1,977 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Report format for GitLab Cluster Image Scanning",
+ "description": "This schema provides the the report format for Cluster Image Scanning (https://docs.gitlab.com/ee/user/application_security/cluster_image_scanning/).",
+ "definitions": {
+ "detail_type": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/named_list"
+ },
+ {
+ "$ref": "#/definitions/list"
+ },
+ {
+ "$ref": "#/definitions/table"
+ },
+ {
+ "$ref": "#/definitions/text"
+ },
+ {
+ "$ref": "#/definitions/url"
+ },
+ {
+ "$ref": "#/definitions/code"
+ },
+ {
+ "$ref": "#/definitions/value"
+ },
+ {
+ "$ref": "#/definitions/diff"
+ },
+ {
+ "$ref": "#/definitions/markdown"
+ },
+ {
+ "$ref": "#/definitions/commit"
+ },
+ {
+ "$ref": "#/definitions/file_location"
+ },
+ {
+ "$ref": "#/definitions/module_location"
+ }
+ ]
+ },
+ "text_value": {
+ "type": "string"
+ },
+ "named_field": {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "$ref": "#/definitions/text_value",
+ "minLength": 1
+ },
+ "description": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "named_list": {
+ "type": "object",
+ "description": "An object with named and typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "named-list"
+ },
+ "items": {
+ "type": "object",
+ "patternProperties": {
+ "^.*$": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/named_field"
+ },
+ {
+ "$ref": "#/definitions/detail_type"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "list": {
+ "type": "object",
+ "description": "A list of typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "list"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ },
+ "table": {
+ "type": "object",
+ "description": "A table of typed fields",
+ "required": [
+ "type",
+ "rows"
+ ],
+ "properties": {
+ "type": {
+ "const": "table"
+ },
+ "header": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ },
+ "rows": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ }
+ },
+ "text": {
+ "type": "object",
+ "description": "Raw text",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "text"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "url": {
+ "type": "object",
+ "description": "A single URL",
+ "required": [
+ "type",
+ "href"
+ ],
+ "properties": {
+ "type": {
+ "const": "url"
+ },
+ "text": {
+ "$ref": "#/definitions/text_value"
+ },
+ "href": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "http://mysite.com"
+ ]
+ }
+ }
+ },
+ "code": {
+ "type": "object",
+ "description": "A codeblock",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "code"
+ },
+ "value": {
+ "type": "string"
+ },
+ "lang": {
+ "type": "string",
+ "description": "A programming language"
+ }
+ }
+ },
+ "value": {
+ "type": "object",
+ "description": "A field that can store a range of types of value",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "value"
+ },
+ "value": {
+ "type": [
+ "number",
+ "string",
+ "boolean"
+ ]
+ }
+ }
+ },
+ "diff": {
+ "type": "object",
+ "description": "A diff",
+ "required": [
+ "type",
+ "before",
+ "after"
+ ],
+ "properties": {
+ "type": {
+ "const": "diff"
+ },
+ "before": {
+ "type": "string"
+ },
+ "after": {
+ "type": "string"
+ }
+ }
+ },
+ "markdown": {
+ "type": "object",
+ "description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "markdown"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value",
+ "examples": [
+ "Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
+ ]
+ }
+ }
+ },
+ "commit": {
+ "type": "object",
+ "description": "A commit/tag/branch within the GitLab project",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "commit"
+ },
+ "value": {
+ "type": "string",
+ "description": "The commit SHA",
+ "minLength": 1
+ }
+ }
+ },
+ "file_location": {
+ "type": "object",
+ "description": "A location within a file in the project",
+ "required": [
+ "type",
+ "file_name",
+ "line_start"
+ ],
+ "properties": {
+ "type": {
+ "const": "file-location"
+ },
+ "file_name": {
+ "type": "string",
+ "minLength": 1
+ },
+ "line_start": {
+ "type": "integer"
+ },
+ "line_end": {
+ "type": "integer"
+ }
+ }
+ },
+ "module_location": {
+ "type": "object",
+ "description": "A location within a binary module of the form module+relative_offset",
+ "required": [
+ "type",
+ "module_name",
+ "offset"
+ ],
+ "properties": {
+ "type": {
+ "const": "module-location"
+ },
+ "module_name": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "compiled_binary"
+ ]
+ },
+ "offset": {
+ "type": "integer",
+ "examples": [
+ 100
+ ]
+ }
+ }
+ }
+ },
+ "self": {
+ "version": "14.1.3"
+ },
+ "required": [
+ "version",
+ "vulnerabilities"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "scan": {
+ "type": "object",
+ "required": [
+ "end_time",
+ "scanner",
+ "start_time",
+ "status",
+ "type"
+ ],
+ "properties": {
+ "end_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}\\:\\d{2}\\:\\d{2}$",
+ "examples": [
+ "2020-01-28T03:26:02"
+ ]
+ },
+ "messages": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Communication intended for the initiator of a scan.",
+ "required": [
+ "level",
+ "value"
+ ],
+ "properties": {
+ "level": {
+ "type": "string",
+ "description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
+ "enum": [
+ "info",
+ "warn",
+ "fatal"
+ ],
+ "examples": [
+ "info"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "The message to communicate.",
+ "minLength": 1,
+ "examples": [
+ "Permission denied, scanning aborted"
+ ]
+ }
+ }
+ }
+ },
+ "analyzer": {
+ "type": "object",
+ "description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "gitlab-dast"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the analyzer, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "GitLab DAST"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "pattern": "^https?://.+",
+ "description": "A link to more information about the analyzer.",
+ "examples": [
+ "https://docs.gitlab.com/ee/user/application_security/dast"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the analyzer.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ }
+ }
+ },
+ "scanner": {
+ "type": "object",
+ "description": "Object defining the scanner used to perform the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the scanner.",
+ "minLength": 1,
+ "examples": [
+ "my-sast-scanner"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the scanner, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "My SAST Scanner"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "description": "A link to more information about the scanner.",
+ "examples": [
+ "https://scanner.url"
+ ]
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the scanner.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the scanner.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "start_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}\\:\\d{2}\\:\\d{2}$",
+ "examples": [
+ "2020-02-14T16:01:59"
+ ]
+ },
+ "status": {
+ "type": "string",
+ "description": "Result of the scan.",
+ "enum": [
+ "success",
+ "failure"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "description": "Type of the scan.",
+ "enum": [
+ "cluster_image_scanning"
+ ]
+ }
+ }
+ },
+ "schema": {
+ "type": "string",
+ "description": "URI pointing to the validating security report schema.",
+ "format": "uri"
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the schema to which the JSON report conforms.",
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
+ },
+ "vulnerabilities": {
+ "type": "array",
+ "description": "Array of vulnerability objects.",
+ "items": {
+ "type": "object",
+ "description": "Describes the vulnerability using GitLab Flavored Markdown",
+ "required": [
+ "category",
+ "cve",
+ "identifiers",
+ "location",
+ "scanner"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ },
+ "category": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Describes where this vulnerability belongs (for example, SAST, Dependency Scanning, and so on)."
+ },
+ "name": {
+ "type": "string",
+ "description": "The name of the vulnerability. This must not include the finding's specific information."
+ },
+ "message": {
+ "type": "string",
+ "description": "A short text section that describes the vulnerability. This may include the finding's specific information."
+ },
+ "description": {
+ "type": "string",
+ "description": "A long text section describing the vulnerability more fully."
+ },
+ "cve": {
+ "type": "string",
+ "description": "(Deprecated - use vulnerabilities[].id instead) A fingerprint string value that represents a concrete finding. This is used to determine whether two findings are same, which may not be 100% accurate. Note that this is NOT a CVE as described by https://cve.mitre.org/."
+ },
+ "severity": {
+ "type": "string",
+ "description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Info",
+ "Unknown",
+ "Low",
+ "Medium",
+ "High",
+ "Critical"
+ ]
+ },
+ "confidence": {
+ "type": "string",
+ "description": "How reliable the vulnerability's assessment is. Possible values are Ignore, Unknown, Experimental, Low, Medium, High, and Confirmed. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Ignore",
+ "Unknown",
+ "Experimental",
+ "Low",
+ "Medium",
+ "High",
+ "Confirmed"
+ ]
+ },
+ "solution": {
+ "type": "string",
+ "description": "Explanation of how to fix the vulnerability."
+ },
+ "scanner": {
+ "description": "Describes the scanner used to find this vulnerability.",
+ "type": "object",
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The scanner's ID, as a snake_case string."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Human-readable name of the scanner."
+ }
+ }
+ },
+ "identifiers": {
+ "type": "array",
+ "minItems": 1,
+ "description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
+ "minLength": 1
+ },
+ "name": {
+ "type": "string",
+ "description": "Human-readable name of the identifier.",
+ "minLength": 1
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the identifier's documentation.",
+ "format": "uri"
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the identifier, for matching purpose.",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "links": {
+ "type": "array",
+ "description": "An array of references to external documentation or articles that describe the vulnerability.",
+ "items": {
+ "type": "object",
+ "required": [
+ "url"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the vulnerability details link."
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the vulnerability details document.",
+ "format": "uri"
+ }
+ }
+ }
+ },
+ "details": {
+ "$ref": "#/definitions/named_list/properties/items"
+ },
+ "tracking": {
+ "description": "Describes how this vulnerability should be tracked as the project changes.",
+ "oneOf": [
+ {
+ "description": "Declares that a series of items should be tracked using source-specific tracking methods.",
+ "required": [
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "source"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "description": "An item that should be tracked using source-specific tracking methods.",
+ "type": "object",
+ "required": [
+ "signatures"
+ ],
+ "properties": {
+ "file": {
+ "type": "string",
+ "description": "Path to the file where the vulnerability is located."
+ },
+ "start_line": {
+ "type": "number",
+ "description": "The first line of the file that includes the vulnerability."
+ },
+ "end_line": {
+ "type": "number",
+ "description": "The last line of the file that includes the vulnerability."
+ },
+ "signatures": {
+ "type": "array",
+ "description": "An array of calculated tracking signatures for this tracking item.",
+ "minItems": 1,
+ "items": {
+ "description": "A calculated tracking signature value and metadata.",
+ "required": [
+ "algorithm",
+ "value"
+ ],
+ "properties": {
+ "algorithm": {
+ "type": "string",
+ "description": "The algorithm used to generate the signature."
+ },
+ "value": {
+ "type": "string",
+ "description": "The result of this signature algorithm."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Each tracking type must declare its own type."
+ }
+ }
+ },
+ "flags": {
+ "description": "Flags that can be attached to vulnerabilities.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Informational flags identified and assigned to a vulnerability.",
+ "required": [
+ "type",
+ "origin",
+ "description"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Result of the scan.",
+ "enum": [
+ "flagged-as-likely-false-positive"
+ ]
+ },
+ "origin": {
+ "minLength": 1,
+ "description": "Tool that issued the flag.",
+ "type": "string"
+ },
+ "description": {
+ "minLength": 1,
+ "description": "What the flag is about.",
+ "type": "string"
+ }
+ }
+ }
+ },
+ "location": {
+ "type": "object",
+ "description": "Identifies the vulnerability's location.",
+ "required": [
+ "dependency",
+ "image",
+ "kubernetes_resource"
+ ],
+ "properties": {
+ "dependency": {
+ "type": "object",
+ "description": "Describes the dependency of a project where the vulnerability is located.",
+ "properties": {
+ "package": {
+ "type": "object",
+ "description": "Provides information on the package where the vulnerability is located.",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the package where the vulnerability is located."
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "Version of the vulnerable package."
+ },
+ "iid": {
+ "description": "ID that identifies the dependency in the scope of a dependency file.",
+ "type": "number"
+ },
+ "direct": {
+ "type": "boolean",
+ "description": "Tells whether this is a direct, top-level dependency of the scanned project."
+ },
+ "dependency_path": {
+ "type": "array",
+ "description": "Ancestors of the dependency, starting from a direct project dependency, and ending with an immediate parent of the dependency. The dependency itself is excluded from the path. Direct dependencies have no path.",
+ "items": {
+ "type": "object",
+ "required": [
+ "iid"
+ ],
+ "properties": {
+ "iid": {
+ "type": "number",
+ "description": "ID that is unique in the scope of a parent object, and specific to the resource type."
+ }
+ }
+ }
+ }
+ }
+ },
+ "operating_system": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "The operating system that contains the vulnerable package."
+ },
+ "image": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The analyzed Docker image.",
+ "examples": [
+ "index.docker.io/library/nginx:1.21"
+ ]
+ },
+ "kubernetes_resource": {
+ "type": "object",
+ "description": "The specific Kubernetes resource that was scanned.",
+ "required": [
+ "namespace",
+ "kind",
+ "name",
+ "container_name"
+ ],
+ "properties": {
+ "namespace": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "The Kubernetes namespace the resource that had its image scanned.",
+ "examples": [
+ "default",
+ "staging",
+ "production"
+ ]
+ },
+ "kind": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "The Kubernetes kind the resource that had its image scanned.",
+ "examples": [
+ "Deployment",
+ "DaemonSet"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "The name of the resource that had its image scanned.",
+ "examples": [
+ "nginx-ingress"
+ ]
+ },
+ "container_name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "The name of the container that had its image scanned.",
+ "examples": [
+ "nginx"
+ ]
+ },
+ "agent_id": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "The GitLab ID of the Kubernetes Agent which performed the scan.",
+ "examples": [
+ "1234"
+ ]
+ },
+ "cluster_id": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "The GitLab ID of the Kubernetes cluster when using cluster integration.",
+ "examples": [
+ "1234"
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "remediations": {
+ "type": "array",
+ "description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
+ "items": {
+ "type": "object",
+ "required": [
+ "fixes",
+ "summary",
+ "diff"
+ ],
+ "properties": {
+ "fixes": {
+ "type": "array",
+ "description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
+ "items": {
+ "type": "object",
+ "required": [
+ "cve"
+ ],
+ "properties": {
+ "cve": {
+ "type": "string",
+ "description": "(Deprecated - use vulnerabilities[].id instead) A fingerprint string value that represents a concrete finding. This is used to determine whether two findings are same, which may not be 100% accurate. Note that this is NOT a CVE as described by https://cve.mitre.org/."
+ }
+ }
+ }
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1,
+ "description": "An overview of how the vulnerabilities were fixed."
+ },
+ "diff": {
+ "type": "string",
+ "minLength": 1,
+ "description": "A base64-encoded remediation code diff, compatible with git apply."
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/container-scanning-report-format.json b/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/container-scanning-report-format.json
new file mode 100644
index 00000000000..641cfc82e48
--- /dev/null
+++ b/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/container-scanning-report-format.json
@@ -0,0 +1,911 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Report format for GitLab Container Scanning",
+ "description": "This schema provides the the report format for Container Scanning (https://docs.gitlab.com/ee/user/application_security/container_scanning).",
+ "definitions": {
+ "detail_type": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/named_list"
+ },
+ {
+ "$ref": "#/definitions/list"
+ },
+ {
+ "$ref": "#/definitions/table"
+ },
+ {
+ "$ref": "#/definitions/text"
+ },
+ {
+ "$ref": "#/definitions/url"
+ },
+ {
+ "$ref": "#/definitions/code"
+ },
+ {
+ "$ref": "#/definitions/value"
+ },
+ {
+ "$ref": "#/definitions/diff"
+ },
+ {
+ "$ref": "#/definitions/markdown"
+ },
+ {
+ "$ref": "#/definitions/commit"
+ },
+ {
+ "$ref": "#/definitions/file_location"
+ },
+ {
+ "$ref": "#/definitions/module_location"
+ }
+ ]
+ },
+ "text_value": {
+ "type": "string"
+ },
+ "named_field": {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "$ref": "#/definitions/text_value",
+ "minLength": 1
+ },
+ "description": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "named_list": {
+ "type": "object",
+ "description": "An object with named and typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "named-list"
+ },
+ "items": {
+ "type": "object",
+ "patternProperties": {
+ "^.*$": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/named_field"
+ },
+ {
+ "$ref": "#/definitions/detail_type"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "list": {
+ "type": "object",
+ "description": "A list of typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "list"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ },
+ "table": {
+ "type": "object",
+ "description": "A table of typed fields",
+ "required": [
+ "type",
+ "rows"
+ ],
+ "properties": {
+ "type": {
+ "const": "table"
+ },
+ "header": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ },
+ "rows": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ }
+ },
+ "text": {
+ "type": "object",
+ "description": "Raw text",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "text"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "url": {
+ "type": "object",
+ "description": "A single URL",
+ "required": [
+ "type",
+ "href"
+ ],
+ "properties": {
+ "type": {
+ "const": "url"
+ },
+ "text": {
+ "$ref": "#/definitions/text_value"
+ },
+ "href": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "http://mysite.com"
+ ]
+ }
+ }
+ },
+ "code": {
+ "type": "object",
+ "description": "A codeblock",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "code"
+ },
+ "value": {
+ "type": "string"
+ },
+ "lang": {
+ "type": "string",
+ "description": "A programming language"
+ }
+ }
+ },
+ "value": {
+ "type": "object",
+ "description": "A field that can store a range of types of value",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "value"
+ },
+ "value": {
+ "type": [
+ "number",
+ "string",
+ "boolean"
+ ]
+ }
+ }
+ },
+ "diff": {
+ "type": "object",
+ "description": "A diff",
+ "required": [
+ "type",
+ "before",
+ "after"
+ ],
+ "properties": {
+ "type": {
+ "const": "diff"
+ },
+ "before": {
+ "type": "string"
+ },
+ "after": {
+ "type": "string"
+ }
+ }
+ },
+ "markdown": {
+ "type": "object",
+ "description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "markdown"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value",
+ "examples": [
+ "Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
+ ]
+ }
+ }
+ },
+ "commit": {
+ "type": "object",
+ "description": "A commit/tag/branch within the GitLab project",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "commit"
+ },
+ "value": {
+ "type": "string",
+ "description": "The commit SHA",
+ "minLength": 1
+ }
+ }
+ },
+ "file_location": {
+ "type": "object",
+ "description": "A location within a file in the project",
+ "required": [
+ "type",
+ "file_name",
+ "line_start"
+ ],
+ "properties": {
+ "type": {
+ "const": "file-location"
+ },
+ "file_name": {
+ "type": "string",
+ "minLength": 1
+ },
+ "line_start": {
+ "type": "integer"
+ },
+ "line_end": {
+ "type": "integer"
+ }
+ }
+ },
+ "module_location": {
+ "type": "object",
+ "description": "A location within a binary module of the form module+relative_offset",
+ "required": [
+ "type",
+ "module_name",
+ "offset"
+ ],
+ "properties": {
+ "type": {
+ "const": "module-location"
+ },
+ "module_name": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "compiled_binary"
+ ]
+ },
+ "offset": {
+ "type": "integer",
+ "examples": [
+ 100
+ ]
+ }
+ }
+ }
+ },
+ "self": {
+ "version": "14.1.3"
+ },
+ "required": [
+ "version",
+ "vulnerabilities"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "scan": {
+ "type": "object",
+ "required": [
+ "end_time",
+ "scanner",
+ "start_time",
+ "status",
+ "type"
+ ],
+ "properties": {
+ "end_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}\\:\\d{2}\\:\\d{2}$",
+ "examples": [
+ "2020-01-28T03:26:02"
+ ]
+ },
+ "messages": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Communication intended for the initiator of a scan.",
+ "required": [
+ "level",
+ "value"
+ ],
+ "properties": {
+ "level": {
+ "type": "string",
+ "description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
+ "enum": [
+ "info",
+ "warn",
+ "fatal"
+ ],
+ "examples": [
+ "info"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "The message to communicate.",
+ "minLength": 1,
+ "examples": [
+ "Permission denied, scanning aborted"
+ ]
+ }
+ }
+ }
+ },
+ "analyzer": {
+ "type": "object",
+ "description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "gitlab-dast"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the analyzer, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "GitLab DAST"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "pattern": "^https?://.+",
+ "description": "A link to more information about the analyzer.",
+ "examples": [
+ "https://docs.gitlab.com/ee/user/application_security/dast"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the analyzer.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ }
+ }
+ },
+ "scanner": {
+ "type": "object",
+ "description": "Object defining the scanner used to perform the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the scanner.",
+ "minLength": 1,
+ "examples": [
+ "my-sast-scanner"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the scanner, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "My SAST Scanner"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "description": "A link to more information about the scanner.",
+ "examples": [
+ "https://scanner.url"
+ ]
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the scanner.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the scanner.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "start_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}\\:\\d{2}\\:\\d{2}$",
+ "examples": [
+ "2020-02-14T16:01:59"
+ ]
+ },
+ "status": {
+ "type": "string",
+ "description": "Result of the scan.",
+ "enum": [
+ "success",
+ "failure"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "description": "Type of the scan.",
+ "enum": [
+ "container_scanning"
+ ]
+ }
+ }
+ },
+ "schema": {
+ "type": "string",
+ "description": "URI pointing to the validating security report schema.",
+ "format": "uri"
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the schema to which the JSON report conforms.",
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
+ },
+ "vulnerabilities": {
+ "type": "array",
+ "description": "Array of vulnerability objects.",
+ "items": {
+ "type": "object",
+ "description": "Describes the vulnerability using GitLab Flavored Markdown",
+ "required": [
+ "category",
+ "cve",
+ "identifiers",
+ "location",
+ "scanner"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ },
+ "category": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Describes where this vulnerability belongs (for example, SAST, Dependency Scanning, and so on)."
+ },
+ "name": {
+ "type": "string",
+ "description": "The name of the vulnerability. This must not include the finding's specific information."
+ },
+ "message": {
+ "type": "string",
+ "description": "A short text section that describes the vulnerability. This may include the finding's specific information."
+ },
+ "description": {
+ "type": "string",
+ "description": "A long text section describing the vulnerability more fully."
+ },
+ "cve": {
+ "type": "string",
+ "description": "(Deprecated - use vulnerabilities[].id instead) A fingerprint string value that represents a concrete finding. This is used to determine whether two findings are same, which may not be 100% accurate. Note that this is NOT a CVE as described by https://cve.mitre.org/."
+ },
+ "severity": {
+ "type": "string",
+ "description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Info",
+ "Unknown",
+ "Low",
+ "Medium",
+ "High",
+ "Critical"
+ ]
+ },
+ "confidence": {
+ "type": "string",
+ "description": "How reliable the vulnerability's assessment is. Possible values are Ignore, Unknown, Experimental, Low, Medium, High, and Confirmed. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Ignore",
+ "Unknown",
+ "Experimental",
+ "Low",
+ "Medium",
+ "High",
+ "Confirmed"
+ ]
+ },
+ "solution": {
+ "type": "string",
+ "description": "Explanation of how to fix the vulnerability."
+ },
+ "scanner": {
+ "description": "Describes the scanner used to find this vulnerability.",
+ "type": "object",
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The scanner's ID, as a snake_case string."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Human-readable name of the scanner."
+ }
+ }
+ },
+ "identifiers": {
+ "type": "array",
+ "minItems": 1,
+ "description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
+ "minLength": 1
+ },
+ "name": {
+ "type": "string",
+ "description": "Human-readable name of the identifier.",
+ "minLength": 1
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the identifier's documentation.",
+ "format": "uri"
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the identifier, for matching purpose.",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "links": {
+ "type": "array",
+ "description": "An array of references to external documentation or articles that describe the vulnerability.",
+ "items": {
+ "type": "object",
+ "required": [
+ "url"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the vulnerability details link."
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the vulnerability details document.",
+ "format": "uri"
+ }
+ }
+ }
+ },
+ "details": {
+ "$ref": "#/definitions/named_list/properties/items"
+ },
+ "tracking": {
+ "description": "Describes how this vulnerability should be tracked as the project changes.",
+ "oneOf": [
+ {
+ "description": "Declares that a series of items should be tracked using source-specific tracking methods.",
+ "required": [
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "source"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "description": "An item that should be tracked using source-specific tracking methods.",
+ "type": "object",
+ "required": [
+ "signatures"
+ ],
+ "properties": {
+ "file": {
+ "type": "string",
+ "description": "Path to the file where the vulnerability is located."
+ },
+ "start_line": {
+ "type": "number",
+ "description": "The first line of the file that includes the vulnerability."
+ },
+ "end_line": {
+ "type": "number",
+ "description": "The last line of the file that includes the vulnerability."
+ },
+ "signatures": {
+ "type": "array",
+ "description": "An array of calculated tracking signatures for this tracking item.",
+ "minItems": 1,
+ "items": {
+ "description": "A calculated tracking signature value and metadata.",
+ "required": [
+ "algorithm",
+ "value"
+ ],
+ "properties": {
+ "algorithm": {
+ "type": "string",
+ "description": "The algorithm used to generate the signature."
+ },
+ "value": {
+ "type": "string",
+ "description": "The result of this signature algorithm."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Each tracking type must declare its own type."
+ }
+ }
+ },
+ "flags": {
+ "description": "Flags that can be attached to vulnerabilities.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Informational flags identified and assigned to a vulnerability.",
+ "required": [
+ "type",
+ "origin",
+ "description"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Result of the scan.",
+ "enum": [
+ "flagged-as-likely-false-positive"
+ ]
+ },
+ "origin": {
+ "minLength": 1,
+ "description": "Tool that issued the flag.",
+ "type": "string"
+ },
+ "description": {
+ "minLength": 1,
+ "description": "What the flag is about.",
+ "type": "string"
+ }
+ }
+ }
+ },
+ "location": {
+ "type": "object",
+ "description": "Identifies the vulnerability's location.",
+ "required": [
+ "dependency",
+ "operating_system",
+ "image"
+ ],
+ "properties": {
+ "dependency": {
+ "type": "object",
+ "description": "Describes the dependency of a project where the vulnerability is located.",
+ "properties": {
+ "package": {
+ "type": "object",
+ "description": "Provides information on the package where the vulnerability is located.",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the package where the vulnerability is located."
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "Version of the vulnerable package."
+ },
+ "iid": {
+ "description": "ID that identifies the dependency in the scope of a dependency file.",
+ "type": "number"
+ },
+ "direct": {
+ "type": "boolean",
+ "description": "Tells whether this is a direct, top-level dependency of the scanned project."
+ },
+ "dependency_path": {
+ "type": "array",
+ "description": "Ancestors of the dependency, starting from a direct project dependency, and ending with an immediate parent of the dependency. The dependency itself is excluded from the path. Direct dependencies have no path.",
+ "items": {
+ "type": "object",
+ "required": [
+ "iid"
+ ],
+ "properties": {
+ "iid": {
+ "type": "number",
+ "description": "ID that is unique in the scope of a parent object, and specific to the resource type."
+ }
+ }
+ }
+ }
+ }
+ },
+ "operating_system": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The operating system that contains the vulnerable package."
+ },
+ "image": {
+ "type": "string",
+ "minLength": 1,
+ "pattern": "^[^:]+(:\\d+[^:]*)?:[^:]+(:[^:]+)?$",
+ "description": "The analyzed Docker image."
+ },
+ "default_branch_image": {
+ "type": "string",
+ "maxLength": 255,
+ "pattern": "^[a-zA-Z0-9/_.-]+(:\\d+[a-zA-Z0-9/_.-]*)?:[a-zA-Z0-9_.-]+$",
+ "description": "The name of the image on the default branch."
+ }
+ }
+ }
+ }
+ }
+ },
+ "remediations": {
+ "type": "array",
+ "description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
+ "items": {
+ "type": "object",
+ "required": [
+ "fixes",
+ "summary",
+ "diff"
+ ],
+ "properties": {
+ "fixes": {
+ "type": "array",
+ "description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
+ "items": {
+ "type": "object",
+ "required": [
+ "cve"
+ ],
+ "properties": {
+ "cve": {
+ "type": "string",
+ "description": "(Deprecated - use vulnerabilities[].id instead) A fingerprint string value that represents a concrete finding. This is used to determine whether two findings are same, which may not be 100% accurate. Note that this is NOT a CVE as described by https://cve.mitre.org/."
+ }
+ }
+ }
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1,
+ "description": "An overview of how the vulnerabilities were fixed."
+ },
+ "diff": {
+ "type": "string",
+ "minLength": 1,
+ "description": "A base64-encoded remediation code diff, compatible with git apply."
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/coverage-fuzzing-report-format.json b/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/coverage-fuzzing-report-format.json
new file mode 100644
index 00000000000..59aa172444d
--- /dev/null
+++ b/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/coverage-fuzzing-report-format.json
@@ -0,0 +1,874 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Report format for GitLab Fuzz Testing",
+ "description": "This schema provides the report format for Coverage Guided Fuzz Testing (https://docs.gitlab.com/ee/user/application_security/coverage_fuzzing).",
+ "definitions": {
+ "detail_type": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/named_list"
+ },
+ {
+ "$ref": "#/definitions/list"
+ },
+ {
+ "$ref": "#/definitions/table"
+ },
+ {
+ "$ref": "#/definitions/text"
+ },
+ {
+ "$ref": "#/definitions/url"
+ },
+ {
+ "$ref": "#/definitions/code"
+ },
+ {
+ "$ref": "#/definitions/value"
+ },
+ {
+ "$ref": "#/definitions/diff"
+ },
+ {
+ "$ref": "#/definitions/markdown"
+ },
+ {
+ "$ref": "#/definitions/commit"
+ },
+ {
+ "$ref": "#/definitions/file_location"
+ },
+ {
+ "$ref": "#/definitions/module_location"
+ }
+ ]
+ },
+ "text_value": {
+ "type": "string"
+ },
+ "named_field": {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "$ref": "#/definitions/text_value",
+ "minLength": 1
+ },
+ "description": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "named_list": {
+ "type": "object",
+ "description": "An object with named and typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "named-list"
+ },
+ "items": {
+ "type": "object",
+ "patternProperties": {
+ "^.*$": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/named_field"
+ },
+ {
+ "$ref": "#/definitions/detail_type"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "list": {
+ "type": "object",
+ "description": "A list of typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "list"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ },
+ "table": {
+ "type": "object",
+ "description": "A table of typed fields",
+ "required": [
+ "type",
+ "rows"
+ ],
+ "properties": {
+ "type": {
+ "const": "table"
+ },
+ "header": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ },
+ "rows": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ }
+ },
+ "text": {
+ "type": "object",
+ "description": "Raw text",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "text"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "url": {
+ "type": "object",
+ "description": "A single URL",
+ "required": [
+ "type",
+ "href"
+ ],
+ "properties": {
+ "type": {
+ "const": "url"
+ },
+ "text": {
+ "$ref": "#/definitions/text_value"
+ },
+ "href": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "http://mysite.com"
+ ]
+ }
+ }
+ },
+ "code": {
+ "type": "object",
+ "description": "A codeblock",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "code"
+ },
+ "value": {
+ "type": "string"
+ },
+ "lang": {
+ "type": "string",
+ "description": "A programming language"
+ }
+ }
+ },
+ "value": {
+ "type": "object",
+ "description": "A field that can store a range of types of value",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "value"
+ },
+ "value": {
+ "type": [
+ "number",
+ "string",
+ "boolean"
+ ]
+ }
+ }
+ },
+ "diff": {
+ "type": "object",
+ "description": "A diff",
+ "required": [
+ "type",
+ "before",
+ "after"
+ ],
+ "properties": {
+ "type": {
+ "const": "diff"
+ },
+ "before": {
+ "type": "string"
+ },
+ "after": {
+ "type": "string"
+ }
+ }
+ },
+ "markdown": {
+ "type": "object",
+ "description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "markdown"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value",
+ "examples": [
+ "Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
+ ]
+ }
+ }
+ },
+ "commit": {
+ "type": "object",
+ "description": "A commit/tag/branch within the GitLab project",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "commit"
+ },
+ "value": {
+ "type": "string",
+ "description": "The commit SHA",
+ "minLength": 1
+ }
+ }
+ },
+ "file_location": {
+ "type": "object",
+ "description": "A location within a file in the project",
+ "required": [
+ "type",
+ "file_name",
+ "line_start"
+ ],
+ "properties": {
+ "type": {
+ "const": "file-location"
+ },
+ "file_name": {
+ "type": "string",
+ "minLength": 1
+ },
+ "line_start": {
+ "type": "integer"
+ },
+ "line_end": {
+ "type": "integer"
+ }
+ }
+ },
+ "module_location": {
+ "type": "object",
+ "description": "A location within a binary module of the form module+relative_offset",
+ "required": [
+ "type",
+ "module_name",
+ "offset"
+ ],
+ "properties": {
+ "type": {
+ "const": "module-location"
+ },
+ "module_name": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "compiled_binary"
+ ]
+ },
+ "offset": {
+ "type": "integer",
+ "examples": [
+ 100
+ ]
+ }
+ }
+ }
+ },
+ "self": {
+ "version": "14.1.3"
+ },
+ "required": [
+ "version",
+ "vulnerabilities"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "scan": {
+ "type": "object",
+ "required": [
+ "end_time",
+ "scanner",
+ "start_time",
+ "status",
+ "type"
+ ],
+ "properties": {
+ "end_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}\\:\\d{2}\\:\\d{2}$",
+ "examples": [
+ "2020-01-28T03:26:02"
+ ]
+ },
+ "messages": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Communication intended for the initiator of a scan.",
+ "required": [
+ "level",
+ "value"
+ ],
+ "properties": {
+ "level": {
+ "type": "string",
+ "description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
+ "enum": [
+ "info",
+ "warn",
+ "fatal"
+ ],
+ "examples": [
+ "info"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "The message to communicate.",
+ "minLength": 1,
+ "examples": [
+ "Permission denied, scanning aborted"
+ ]
+ }
+ }
+ }
+ },
+ "analyzer": {
+ "type": "object",
+ "description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "gitlab-dast"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the analyzer, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "GitLab DAST"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "pattern": "^https?://.+",
+ "description": "A link to more information about the analyzer.",
+ "examples": [
+ "https://docs.gitlab.com/ee/user/application_security/dast"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the analyzer.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ }
+ }
+ },
+ "scanner": {
+ "type": "object",
+ "description": "Object defining the scanner used to perform the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the scanner.",
+ "minLength": 1,
+ "examples": [
+ "my-sast-scanner"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the scanner, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "My SAST Scanner"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "description": "A link to more information about the scanner.",
+ "examples": [
+ "https://scanner.url"
+ ]
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the scanner.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the scanner.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "start_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}\\:\\d{2}\\:\\d{2}$",
+ "examples": [
+ "2020-02-14T16:01:59"
+ ]
+ },
+ "status": {
+ "type": "string",
+ "description": "Result of the scan.",
+ "enum": [
+ "success",
+ "failure"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "description": "Type of the scan.",
+ "enum": [
+ "coverage_fuzzing"
+ ]
+ }
+ }
+ },
+ "schema": {
+ "type": "string",
+ "description": "URI pointing to the validating security report schema.",
+ "format": "uri"
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the schema to which the JSON report conforms.",
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
+ },
+ "vulnerabilities": {
+ "type": "array",
+ "description": "Array of vulnerability objects.",
+ "items": {
+ "type": "object",
+ "description": "Describes the vulnerability using GitLab Flavored Markdown",
+ "required": [
+ "category",
+ "cve",
+ "identifiers",
+ "location",
+ "scanner"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ },
+ "category": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Describes where this vulnerability belongs (for example, SAST, Dependency Scanning, and so on)."
+ },
+ "name": {
+ "type": "string",
+ "description": "The name of the vulnerability. This must not include the finding's specific information."
+ },
+ "message": {
+ "type": "string",
+ "description": "A short text section that describes the vulnerability. This may include the finding's specific information."
+ },
+ "description": {
+ "type": "string",
+ "description": "A long text section describing the vulnerability more fully."
+ },
+ "cve": {
+ "type": "string",
+ "description": "(Deprecated - use vulnerabilities[].id instead) A fingerprint string value that represents a concrete finding. This is used to determine whether two findings are same, which may not be 100% accurate. Note that this is NOT a CVE as described by https://cve.mitre.org/."
+ },
+ "severity": {
+ "type": "string",
+ "description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Info",
+ "Unknown",
+ "Low",
+ "Medium",
+ "High",
+ "Critical"
+ ]
+ },
+ "confidence": {
+ "type": "string",
+ "description": "How reliable the vulnerability's assessment is. Possible values are Ignore, Unknown, Experimental, Low, Medium, High, and Confirmed. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Ignore",
+ "Unknown",
+ "Experimental",
+ "Low",
+ "Medium",
+ "High",
+ "Confirmed"
+ ]
+ },
+ "solution": {
+ "type": "string",
+ "description": "Explanation of how to fix the vulnerability."
+ },
+ "scanner": {
+ "description": "Describes the scanner used to find this vulnerability.",
+ "type": "object",
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The scanner's ID, as a snake_case string."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Human-readable name of the scanner."
+ }
+ }
+ },
+ "identifiers": {
+ "type": "array",
+ "minItems": 1,
+ "description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
+ "minLength": 1
+ },
+ "name": {
+ "type": "string",
+ "description": "Human-readable name of the identifier.",
+ "minLength": 1
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the identifier's documentation.",
+ "format": "uri"
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the identifier, for matching purpose.",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "links": {
+ "type": "array",
+ "description": "An array of references to external documentation or articles that describe the vulnerability.",
+ "items": {
+ "type": "object",
+ "required": [
+ "url"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the vulnerability details link."
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the vulnerability details document.",
+ "format": "uri"
+ }
+ }
+ }
+ },
+ "details": {
+ "$ref": "#/definitions/named_list/properties/items"
+ },
+ "tracking": {
+ "description": "Describes how this vulnerability should be tracked as the project changes.",
+ "oneOf": [
+ {
+ "description": "Declares that a series of items should be tracked using source-specific tracking methods.",
+ "required": [
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "source"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "description": "An item that should be tracked using source-specific tracking methods.",
+ "type": "object",
+ "required": [
+ "signatures"
+ ],
+ "properties": {
+ "file": {
+ "type": "string",
+ "description": "Path to the file where the vulnerability is located."
+ },
+ "start_line": {
+ "type": "number",
+ "description": "The first line of the file that includes the vulnerability."
+ },
+ "end_line": {
+ "type": "number",
+ "description": "The last line of the file that includes the vulnerability."
+ },
+ "signatures": {
+ "type": "array",
+ "description": "An array of calculated tracking signatures for this tracking item.",
+ "minItems": 1,
+ "items": {
+ "description": "A calculated tracking signature value and metadata.",
+ "required": [
+ "algorithm",
+ "value"
+ ],
+ "properties": {
+ "algorithm": {
+ "type": "string",
+ "description": "The algorithm used to generate the signature."
+ },
+ "value": {
+ "type": "string",
+ "description": "The result of this signature algorithm."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Each tracking type must declare its own type."
+ }
+ }
+ },
+ "flags": {
+ "description": "Flags that can be attached to vulnerabilities.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Informational flags identified and assigned to a vulnerability.",
+ "required": [
+ "type",
+ "origin",
+ "description"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Result of the scan.",
+ "enum": [
+ "flagged-as-likely-false-positive"
+ ]
+ },
+ "origin": {
+ "minLength": 1,
+ "description": "Tool that issued the flag.",
+ "type": "string"
+ },
+ "description": {
+ "minLength": 1,
+ "description": "What the flag is about.",
+ "type": "string"
+ }
+ }
+ }
+ },
+ "location": {
+ "description": "The location of the error",
+ "type": "object",
+ "properties": {
+ "crash_address": {
+ "type": "string",
+ "description": "The relative address in memory were the crash occurred.",
+ "examples": [
+ "0xabababab"
+ ]
+ },
+ "stacktrace_snippet": {
+ "type": "string",
+ "description": "The stack trace recorded during fuzzing resulting the crash.",
+ "examples": [
+ "func_a+0xabcd\nfunc_b+0xabcc"
+ ]
+ },
+ "crash_state": {
+ "type": "string",
+ "description": "Minimised and normalized crash stack-trace (called crash_state).",
+ "examples": [
+ "func_a+0xa\nfunc_b+0xb\nfunc_c+0xc"
+ ]
+ },
+ "crash_type": {
+ "type": "string",
+ "description": "Type of the crash.",
+ "examples": [
+ "Heap-Buffer-overflow",
+ "Division-by-zero"
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "remediations": {
+ "type": "array",
+ "description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
+ "items": {
+ "type": "object",
+ "required": [
+ "fixes",
+ "summary",
+ "diff"
+ ],
+ "properties": {
+ "fixes": {
+ "type": "array",
+ "description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
+ "items": {
+ "type": "object",
+ "required": [
+ "cve"
+ ],
+ "properties": {
+ "cve": {
+ "type": "string",
+ "description": "(Deprecated - use vulnerabilities[].id instead) A fingerprint string value that represents a concrete finding. This is used to determine whether two findings are same, which may not be 100% accurate. Note that this is NOT a CVE as described by https://cve.mitre.org/."
+ }
+ }
+ }
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1,
+ "description": "An overview of how the vulnerabilities were fixed."
+ },
+ "diff": {
+ "type": "string",
+ "minLength": 1,
+ "description": "A base64-encoded remediation code diff, compatible with git apply."
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/dast-report-format.json b/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/dast-report-format.json
new file mode 100644
index 00000000000..0e4c866794a
--- /dev/null
+++ b/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/dast-report-format.json
@@ -0,0 +1,1287 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Report format for GitLab DAST",
+ "description": "This schema provides the the report format for Dynamic Application Security Testing (https://docs.gitlab.com/ee/user/application_security/dast).",
+ "definitions": {
+ "detail_type": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/named_list"
+ },
+ {
+ "$ref": "#/definitions/list"
+ },
+ {
+ "$ref": "#/definitions/table"
+ },
+ {
+ "$ref": "#/definitions/text"
+ },
+ {
+ "$ref": "#/definitions/url"
+ },
+ {
+ "$ref": "#/definitions/code"
+ },
+ {
+ "$ref": "#/definitions/value"
+ },
+ {
+ "$ref": "#/definitions/diff"
+ },
+ {
+ "$ref": "#/definitions/markdown"
+ },
+ {
+ "$ref": "#/definitions/commit"
+ },
+ {
+ "$ref": "#/definitions/file_location"
+ },
+ {
+ "$ref": "#/definitions/module_location"
+ }
+ ]
+ },
+ "text_value": {
+ "type": "string"
+ },
+ "named_field": {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "$ref": "#/definitions/text_value",
+ "minLength": 1
+ },
+ "description": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "named_list": {
+ "type": "object",
+ "description": "An object with named and typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "named-list"
+ },
+ "items": {
+ "type": "object",
+ "patternProperties": {
+ "^.*$": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/named_field"
+ },
+ {
+ "$ref": "#/definitions/detail_type"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "list": {
+ "type": "object",
+ "description": "A list of typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "list"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ },
+ "table": {
+ "type": "object",
+ "description": "A table of typed fields",
+ "required": [
+ "type",
+ "rows"
+ ],
+ "properties": {
+ "type": {
+ "const": "table"
+ },
+ "header": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ },
+ "rows": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ }
+ },
+ "text": {
+ "type": "object",
+ "description": "Raw text",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "text"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "url": {
+ "type": "object",
+ "description": "A single URL",
+ "required": [
+ "type",
+ "href"
+ ],
+ "properties": {
+ "type": {
+ "const": "url"
+ },
+ "text": {
+ "$ref": "#/definitions/text_value"
+ },
+ "href": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "http://mysite.com"
+ ]
+ }
+ }
+ },
+ "code": {
+ "type": "object",
+ "description": "A codeblock",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "code"
+ },
+ "value": {
+ "type": "string"
+ },
+ "lang": {
+ "type": "string",
+ "description": "A programming language"
+ }
+ }
+ },
+ "value": {
+ "type": "object",
+ "description": "A field that can store a range of types of value",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "value"
+ },
+ "value": {
+ "type": [
+ "number",
+ "string",
+ "boolean"
+ ]
+ }
+ }
+ },
+ "diff": {
+ "type": "object",
+ "description": "A diff",
+ "required": [
+ "type",
+ "before",
+ "after"
+ ],
+ "properties": {
+ "type": {
+ "const": "diff"
+ },
+ "before": {
+ "type": "string"
+ },
+ "after": {
+ "type": "string"
+ }
+ }
+ },
+ "markdown": {
+ "type": "object",
+ "description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "markdown"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value",
+ "examples": [
+ "Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
+ ]
+ }
+ }
+ },
+ "commit": {
+ "type": "object",
+ "description": "A commit/tag/branch within the GitLab project",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "commit"
+ },
+ "value": {
+ "type": "string",
+ "description": "The commit SHA",
+ "minLength": 1
+ }
+ }
+ },
+ "file_location": {
+ "type": "object",
+ "description": "A location within a file in the project",
+ "required": [
+ "type",
+ "file_name",
+ "line_start"
+ ],
+ "properties": {
+ "type": {
+ "const": "file-location"
+ },
+ "file_name": {
+ "type": "string",
+ "minLength": 1
+ },
+ "line_start": {
+ "type": "integer"
+ },
+ "line_end": {
+ "type": "integer"
+ }
+ }
+ },
+ "module_location": {
+ "type": "object",
+ "description": "A location within a binary module of the form module+relative_offset",
+ "required": [
+ "type",
+ "module_name",
+ "offset"
+ ],
+ "properties": {
+ "type": {
+ "const": "module-location"
+ },
+ "module_name": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "compiled_binary"
+ ]
+ },
+ "offset": {
+ "type": "integer",
+ "examples": [
+ 100
+ ]
+ }
+ }
+ }
+ },
+ "self": {
+ "version": "14.1.3"
+ },
+ "required": [
+ "version",
+ "vulnerabilities"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "scan": {
+ "type": "object",
+ "required": [
+ "end_time",
+ "scanned_resources",
+ "scanner",
+ "start_time",
+ "status",
+ "type"
+ ],
+ "properties": {
+ "end_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}\\:\\d{2}\\:\\d{2}$",
+ "examples": [
+ "2020-01-28T03:26:02"
+ ]
+ },
+ "messages": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Communication intended for the initiator of a scan.",
+ "required": [
+ "level",
+ "value"
+ ],
+ "properties": {
+ "level": {
+ "type": "string",
+ "description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
+ "enum": [
+ "info",
+ "warn",
+ "fatal"
+ ],
+ "examples": [
+ "info"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "The message to communicate.",
+ "minLength": 1,
+ "examples": [
+ "Permission denied, scanning aborted"
+ ]
+ }
+ }
+ }
+ },
+ "analyzer": {
+ "type": "object",
+ "description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "gitlab-dast"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the analyzer, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "GitLab DAST"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "pattern": "^https?://.+",
+ "description": "A link to more information about the analyzer.",
+ "examples": [
+ "https://docs.gitlab.com/ee/user/application_security/dast"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the analyzer.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ }
+ }
+ },
+ "scanner": {
+ "type": "object",
+ "description": "Object defining the scanner used to perform the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the scanner.",
+ "minLength": 1,
+ "examples": [
+ "my-sast-scanner"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the scanner, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "My SAST Scanner"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "description": "A link to more information about the scanner.",
+ "examples": [
+ "https://scanner.url"
+ ]
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the scanner.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the scanner.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "start_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}\\:\\d{2}\\:\\d{2}$",
+ "examples": [
+ "2020-02-14T16:01:59"
+ ]
+ },
+ "status": {
+ "type": "string",
+ "description": "Result of the scan.",
+ "enum": [
+ "success",
+ "failure"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "description": "Type of the scan.",
+ "enum": [
+ "dast",
+ "api_fuzzing"
+ ]
+ },
+ "scanned_resources": {
+ "type": "array",
+ "description": "The attack surface scanned by DAST.",
+ "items": {
+ "type": "object",
+ "required": [
+ "method",
+ "url",
+ "type"
+ ],
+ "properties": {
+ "method": {
+ "type": "string",
+ "minLength": 1,
+ "description": "HTTP method of the scanned resource.",
+ "examples": [
+ "GET",
+ "POST",
+ "HEAD"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "minLength": 1,
+ "description": "URL of the scanned resource.",
+ "examples": [
+ "http://my.site.com/a-page"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Type of the scanned resource, for DAST, this must be 'url'.",
+ "examples": [
+ "url"
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "schema": {
+ "type": "string",
+ "description": "URI pointing to the validating security report schema.",
+ "format": "uri"
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the schema to which the JSON report conforms.",
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
+ },
+ "vulnerabilities": {
+ "type": "array",
+ "description": "Array of vulnerability objects.",
+ "items": {
+ "type": "object",
+ "description": "Describes the vulnerability using GitLab Flavored Markdown",
+ "required": [
+ "category",
+ "cve",
+ "identifiers",
+ "location",
+ "scanner"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ },
+ "category": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Describes where this vulnerability belongs (for example, SAST, Dependency Scanning, and so on)."
+ },
+ "name": {
+ "type": "string",
+ "description": "The name of the vulnerability. This must not include the finding's specific information."
+ },
+ "message": {
+ "type": "string",
+ "description": "A short text section that describes the vulnerability. This may include the finding's specific information."
+ },
+ "description": {
+ "type": "string",
+ "description": "A long text section describing the vulnerability more fully."
+ },
+ "cve": {
+ "type": "string",
+ "description": "(Deprecated - use vulnerabilities[].id instead) A fingerprint string value that represents a concrete finding. This is used to determine whether two findings are same, which may not be 100% accurate. Note that this is NOT a CVE as described by https://cve.mitre.org/."
+ },
+ "severity": {
+ "type": "string",
+ "description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Info",
+ "Unknown",
+ "Low",
+ "Medium",
+ "High",
+ "Critical"
+ ]
+ },
+ "confidence": {
+ "type": "string",
+ "description": "How reliable the vulnerability's assessment is. Possible values are Ignore, Unknown, Experimental, Low, Medium, High, and Confirmed. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Ignore",
+ "Unknown",
+ "Experimental",
+ "Low",
+ "Medium",
+ "High",
+ "Confirmed"
+ ]
+ },
+ "solution": {
+ "type": "string",
+ "description": "Explanation of how to fix the vulnerability."
+ },
+ "scanner": {
+ "description": "Describes the scanner used to find this vulnerability.",
+ "type": "object",
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The scanner's ID, as a snake_case string."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Human-readable name of the scanner."
+ }
+ }
+ },
+ "identifiers": {
+ "type": "array",
+ "minItems": 1,
+ "description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
+ "minLength": 1
+ },
+ "name": {
+ "type": "string",
+ "description": "Human-readable name of the identifier.",
+ "minLength": 1
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the identifier's documentation.",
+ "format": "uri"
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the identifier, for matching purpose.",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "links": {
+ "type": "array",
+ "description": "An array of references to external documentation or articles that describe the vulnerability.",
+ "items": {
+ "type": "object",
+ "required": [
+ "url"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the vulnerability details link."
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the vulnerability details document.",
+ "format": "uri"
+ }
+ }
+ }
+ },
+ "details": {
+ "$ref": "#/definitions/named_list/properties/items"
+ },
+ "tracking": {
+ "description": "Describes how this vulnerability should be tracked as the project changes.",
+ "oneOf": [
+ {
+ "description": "Declares that a series of items should be tracked using source-specific tracking methods.",
+ "required": [
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "source"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "description": "An item that should be tracked using source-specific tracking methods.",
+ "type": "object",
+ "required": [
+ "signatures"
+ ],
+ "properties": {
+ "file": {
+ "type": "string",
+ "description": "Path to the file where the vulnerability is located."
+ },
+ "start_line": {
+ "type": "number",
+ "description": "The first line of the file that includes the vulnerability."
+ },
+ "end_line": {
+ "type": "number",
+ "description": "The last line of the file that includes the vulnerability."
+ },
+ "signatures": {
+ "type": "array",
+ "description": "An array of calculated tracking signatures for this tracking item.",
+ "minItems": 1,
+ "items": {
+ "description": "A calculated tracking signature value and metadata.",
+ "required": [
+ "algorithm",
+ "value"
+ ],
+ "properties": {
+ "algorithm": {
+ "type": "string",
+ "description": "The algorithm used to generate the signature."
+ },
+ "value": {
+ "type": "string",
+ "description": "The result of this signature algorithm."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Each tracking type must declare its own type."
+ }
+ }
+ },
+ "flags": {
+ "description": "Flags that can be attached to vulnerabilities.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Informational flags identified and assigned to a vulnerability.",
+ "required": [
+ "type",
+ "origin",
+ "description"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Result of the scan.",
+ "enum": [
+ "flagged-as-likely-false-positive"
+ ]
+ },
+ "origin": {
+ "minLength": 1,
+ "description": "Tool that issued the flag.",
+ "type": "string"
+ },
+ "description": {
+ "minLength": 1,
+ "description": "What the flag is about.",
+ "type": "string"
+ }
+ }
+ }
+ },
+ "evidence": {
+ "type": "object",
+ "properties": {
+ "source": {
+ "type": "object",
+ "description": "Source of evidence",
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Unique source identifier",
+ "examples": [
+ "assert:LogAnalysis",
+ "assert:StatusCode"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Source display name",
+ "examples": [
+ "Log Analysis",
+ "Status Code"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "description": "Link to additional information",
+ "examples": [
+ "https://docs.gitlab.com/ee/development/integrations/secure.html"
+ ]
+ }
+ }
+ },
+ "summary": {
+ "type": "string",
+ "description": "Human readable string containing evidence of the vulnerability.",
+ "examples": [
+ "Credit card 4111111111111111 found",
+ "Server leaked information nginx/1.17.6"
+ ]
+ },
+ "request": {
+ "type": "object",
+ "description": "An HTTP request.",
+ "required": [
+ "headers",
+ "method",
+ "url"
+ ],
+ "properties": {
+ "headers": {
+ "type": "array",
+ "description": "HTTP headers present on the request.",
+ "items": {
+ "type": "object",
+ "required": [
+ "name",
+ "value"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Name of the HTTP header.",
+ "examples": [
+ "Accept",
+ "Content-Length",
+ "Content-Type"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the HTTP header.",
+ "examples": [
+ "*/*",
+ "560",
+ "application/json; charset=utf-8"
+ ]
+ }
+ }
+ }
+ },
+ "method": {
+ "type": "string",
+ "minLength": 1,
+ "description": "HTTP method used in the request.",
+ "examples": [
+ "GET",
+ "POST"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "minLength": 1,
+ "description": "URL of the request.",
+ "examples": [
+ "http://my.site.com/vulnerable-endpoint?show-credit-card"
+ ]
+ },
+ "body": {
+ "type": "string",
+ "description": "Body of the request for display purposes. Body must be suitable for display (not binary), and truncated to a reasonable size.",
+ "examples": [
+ "user=jsmith&first=%27&last=smith"
+ ]
+ }
+ }
+ },
+ "response": {
+ "type": "object",
+ "description": "An HTTP response.",
+ "required": [
+ "headers",
+ "reason_phrase",
+ "status_code"
+ ],
+ "properties": {
+ "headers": {
+ "type": "array",
+ "description": "HTTP headers present on the request.",
+ "items": {
+ "type": "object",
+ "required": [
+ "name",
+ "value"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Name of the HTTP header.",
+ "examples": [
+ "Accept",
+ "Content-Length",
+ "Content-Type"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the HTTP header.",
+ "examples": [
+ "*/*",
+ "560",
+ "application/json; charset=utf-8"
+ ]
+ }
+ }
+ }
+ },
+ "reason_phrase": {
+ "type": "string",
+ "description": "HTTP reason phrase of the response.",
+ "examples": [
+ "OK",
+ "Internal Server Error"
+ ]
+ },
+ "status_code": {
+ "type": "integer",
+ "description": "HTTP status code of the response.",
+ "examples": [
+ 200,
+ 500
+ ]
+ },
+ "body": {
+ "type": "string",
+ "description": "Body of the response for display purposes. Body must be suitable for display (not binary), and truncated to a reasonable size.",
+ "examples": [
+ "{\"user_id\": 2}"
+ ]
+ }
+ }
+ },
+ "supporting_messages": {
+ "type": "array",
+ "description": "Array of supporting http messages.",
+ "items": {
+ "type": "object",
+ "description": "A supporting http message.",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Message display name.",
+ "examples": [
+ "Unmodified",
+ "Recorded"
+ ]
+ },
+ "request": {
+ "type": "object",
+ "description": "An HTTP request.",
+ "required": [
+ "headers",
+ "method",
+ "url"
+ ],
+ "properties": {
+ "headers": {
+ "type": "array",
+ "description": "HTTP headers present on the request.",
+ "items": {
+ "type": "object",
+ "required": [
+ "name",
+ "value"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Name of the HTTP header.",
+ "examples": [
+ "Accept",
+ "Content-Length",
+ "Content-Type"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the HTTP header.",
+ "examples": [
+ "*/*",
+ "560",
+ "application/json; charset=utf-8"
+ ]
+ }
+ }
+ }
+ },
+ "method": {
+ "type": "string",
+ "minLength": 1,
+ "description": "HTTP method used in the request.",
+ "examples": [
+ "GET",
+ "POST"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "minLength": 1,
+ "description": "URL of the request.",
+ "examples": [
+ "http://my.site.com/vulnerable-endpoint?show-credit-card"
+ ]
+ },
+ "body": {
+ "type": "string",
+ "description": "Body of the request for display purposes. Body must be suitable for display (not binary), and truncated to a reasonable size.",
+ "examples": [
+ "user=jsmith&first=%27&last=smith"
+ ]
+ }
+ }
+ },
+ "response": {
+ "type": "object",
+ "description": "An HTTP response.",
+ "required": [
+ "headers",
+ "reason_phrase",
+ "status_code"
+ ],
+ "properties": {
+ "headers": {
+ "type": "array",
+ "description": "HTTP headers present on the request.",
+ "items": {
+ "type": "object",
+ "required": [
+ "name",
+ "value"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Name of the HTTP header.",
+ "examples": [
+ "Accept",
+ "Content-Length",
+ "Content-Type"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the HTTP header.",
+ "examples": [
+ "*/*",
+ "560",
+ "application/json; charset=utf-8"
+ ]
+ }
+ }
+ }
+ },
+ "reason_phrase": {
+ "type": "string",
+ "description": "HTTP reason phrase of the response.",
+ "examples": [
+ "OK",
+ "Internal Server Error"
+ ]
+ },
+ "status_code": {
+ "type": "integer",
+ "description": "HTTP status code of the response.",
+ "examples": [
+ 200,
+ 500
+ ]
+ },
+ "body": {
+ "type": "string",
+ "description": "Body of the response for display purposes. Body must be suitable for display (not binary), and truncated to a reasonable size.",
+ "examples": [
+ "{\"user_id\": 2}"
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "location": {
+ "type": "object",
+ "description": "Identifies the vulnerability's location.",
+ "properties": {
+ "hostname": {
+ "type": "string",
+ "description": "The protocol, domain, and port of the application where the vulnerability was found."
+ },
+ "method": {
+ "type": "string",
+ "description": "The HTTP method that was used to request the URL where the vulnerability was found."
+ },
+ "param": {
+ "type": "string",
+ "description": "A value provided by a vulnerability rule related to the found vulnerability. Examples include a header value, or a parameter used in a HTTP POST."
+ },
+ "path": {
+ "type": "string",
+ "description": "The path of the URL where the vulnerability was found. Typically, this would start with a forward slash."
+ }
+ }
+ },
+ "assets": {
+ "type": "array",
+ "description": "Array of build assets associated with vulnerability.",
+ "items": {
+ "type": "object",
+ "description": "Describes an asset associated with vulnerability.",
+ "required": [
+ "type",
+ "name",
+ "url"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "The type of asset",
+ "enum": [
+ "http_session",
+ "postman"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Display name for asset",
+ "examples": [
+ "HTTP Messages",
+ "Postman Collection"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Link to asset in build artifacts",
+ "examples": [
+ "https://gitlab.com/gitlab-org/security-products/dast/-/jobs/626397001/artifacts/file//output/zap_session.data"
+ ]
+ }
+ }
+ }
+ },
+ "discovered_at": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss.sss, representing when the vulnerability was discovered",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}\\:\\d{2}\\:\\d{2}\\.\\d{3}$",
+ "examples": [
+ "2020-01-28T03:26:02.956"
+ ]
+ }
+ }
+ }
+ },
+ "remediations": {
+ "type": "array",
+ "description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
+ "items": {
+ "type": "object",
+ "required": [
+ "fixes",
+ "summary",
+ "diff"
+ ],
+ "properties": {
+ "fixes": {
+ "type": "array",
+ "description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
+ "items": {
+ "type": "object",
+ "required": [
+ "cve"
+ ],
+ "properties": {
+ "cve": {
+ "type": "string",
+ "description": "(Deprecated - use vulnerabilities[].id instead) A fingerprint string value that represents a concrete finding. This is used to determine whether two findings are same, which may not be 100% accurate. Note that this is NOT a CVE as described by https://cve.mitre.org/."
+ }
+ }
+ }
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1,
+ "description": "An overview of how the vulnerabilities were fixed."
+ },
+ "diff": {
+ "type": "string",
+ "minLength": 1,
+ "description": "A base64-encoded remediation code diff, compatible with git apply."
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/dependency-scanning-report-format.json b/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/dependency-scanning-report-format.json
new file mode 100644
index 00000000000..652c2f48fe4
--- /dev/null
+++ b/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/dependency-scanning-report-format.json
@@ -0,0 +1,968 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Report format for GitLab Dependency Scanning",
+ "description": "This schema provides the the report format for Dependency Scanning analyzers (https://docs.gitlab.com/ee/user/application_security/dependency_scanning).",
+ "definitions": {
+ "detail_type": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/named_list"
+ },
+ {
+ "$ref": "#/definitions/list"
+ },
+ {
+ "$ref": "#/definitions/table"
+ },
+ {
+ "$ref": "#/definitions/text"
+ },
+ {
+ "$ref": "#/definitions/url"
+ },
+ {
+ "$ref": "#/definitions/code"
+ },
+ {
+ "$ref": "#/definitions/value"
+ },
+ {
+ "$ref": "#/definitions/diff"
+ },
+ {
+ "$ref": "#/definitions/markdown"
+ },
+ {
+ "$ref": "#/definitions/commit"
+ },
+ {
+ "$ref": "#/definitions/file_location"
+ },
+ {
+ "$ref": "#/definitions/module_location"
+ }
+ ]
+ },
+ "text_value": {
+ "type": "string"
+ },
+ "named_field": {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "$ref": "#/definitions/text_value",
+ "minLength": 1
+ },
+ "description": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "named_list": {
+ "type": "object",
+ "description": "An object with named and typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "named-list"
+ },
+ "items": {
+ "type": "object",
+ "patternProperties": {
+ "^.*$": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/named_field"
+ },
+ {
+ "$ref": "#/definitions/detail_type"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "list": {
+ "type": "object",
+ "description": "A list of typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "list"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ },
+ "table": {
+ "type": "object",
+ "description": "A table of typed fields",
+ "required": [
+ "type",
+ "rows"
+ ],
+ "properties": {
+ "type": {
+ "const": "table"
+ },
+ "header": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ },
+ "rows": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ }
+ },
+ "text": {
+ "type": "object",
+ "description": "Raw text",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "text"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "url": {
+ "type": "object",
+ "description": "A single URL",
+ "required": [
+ "type",
+ "href"
+ ],
+ "properties": {
+ "type": {
+ "const": "url"
+ },
+ "text": {
+ "$ref": "#/definitions/text_value"
+ },
+ "href": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "http://mysite.com"
+ ]
+ }
+ }
+ },
+ "code": {
+ "type": "object",
+ "description": "A codeblock",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "code"
+ },
+ "value": {
+ "type": "string"
+ },
+ "lang": {
+ "type": "string",
+ "description": "A programming language"
+ }
+ }
+ },
+ "value": {
+ "type": "object",
+ "description": "A field that can store a range of types of value",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "value"
+ },
+ "value": {
+ "type": [
+ "number",
+ "string",
+ "boolean"
+ ]
+ }
+ }
+ },
+ "diff": {
+ "type": "object",
+ "description": "A diff",
+ "required": [
+ "type",
+ "before",
+ "after"
+ ],
+ "properties": {
+ "type": {
+ "const": "diff"
+ },
+ "before": {
+ "type": "string"
+ },
+ "after": {
+ "type": "string"
+ }
+ }
+ },
+ "markdown": {
+ "type": "object",
+ "description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "markdown"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value",
+ "examples": [
+ "Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
+ ]
+ }
+ }
+ },
+ "commit": {
+ "type": "object",
+ "description": "A commit/tag/branch within the GitLab project",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "commit"
+ },
+ "value": {
+ "type": "string",
+ "description": "The commit SHA",
+ "minLength": 1
+ }
+ }
+ },
+ "file_location": {
+ "type": "object",
+ "description": "A location within a file in the project",
+ "required": [
+ "type",
+ "file_name",
+ "line_start"
+ ],
+ "properties": {
+ "type": {
+ "const": "file-location"
+ },
+ "file_name": {
+ "type": "string",
+ "minLength": 1
+ },
+ "line_start": {
+ "type": "integer"
+ },
+ "line_end": {
+ "type": "integer"
+ }
+ }
+ },
+ "module_location": {
+ "type": "object",
+ "description": "A location within a binary module of the form module+relative_offset",
+ "required": [
+ "type",
+ "module_name",
+ "offset"
+ ],
+ "properties": {
+ "type": {
+ "const": "module-location"
+ },
+ "module_name": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "compiled_binary"
+ ]
+ },
+ "offset": {
+ "type": "integer",
+ "examples": [
+ 100
+ ]
+ }
+ }
+ }
+ },
+ "self": {
+ "version": "14.1.3"
+ },
+ "required": [
+ "dependency_files",
+ "version",
+ "vulnerabilities"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "scan": {
+ "type": "object",
+ "required": [
+ "end_time",
+ "scanner",
+ "start_time",
+ "status",
+ "type"
+ ],
+ "properties": {
+ "end_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}\\:\\d{2}\\:\\d{2}$",
+ "examples": [
+ "2020-01-28T03:26:02"
+ ]
+ },
+ "messages": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Communication intended for the initiator of a scan.",
+ "required": [
+ "level",
+ "value"
+ ],
+ "properties": {
+ "level": {
+ "type": "string",
+ "description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
+ "enum": [
+ "info",
+ "warn",
+ "fatal"
+ ],
+ "examples": [
+ "info"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "The message to communicate.",
+ "minLength": 1,
+ "examples": [
+ "Permission denied, scanning aborted"
+ ]
+ }
+ }
+ }
+ },
+ "analyzer": {
+ "type": "object",
+ "description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "gitlab-dast"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the analyzer, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "GitLab DAST"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "pattern": "^https?://.+",
+ "description": "A link to more information about the analyzer.",
+ "examples": [
+ "https://docs.gitlab.com/ee/user/application_security/dast"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the analyzer.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ }
+ }
+ },
+ "scanner": {
+ "type": "object",
+ "description": "Object defining the scanner used to perform the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the scanner.",
+ "minLength": 1,
+ "examples": [
+ "my-sast-scanner"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the scanner, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "My SAST Scanner"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "description": "A link to more information about the scanner.",
+ "examples": [
+ "https://scanner.url"
+ ]
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the scanner.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the scanner.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "start_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}\\:\\d{2}\\:\\d{2}$",
+ "examples": [
+ "2020-02-14T16:01:59"
+ ]
+ },
+ "status": {
+ "type": "string",
+ "description": "Result of the scan.",
+ "enum": [
+ "success",
+ "failure"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "description": "Type of the scan.",
+ "enum": [
+ "dependency_scanning"
+ ]
+ }
+ }
+ },
+ "schema": {
+ "type": "string",
+ "description": "URI pointing to the validating security report schema.",
+ "format": "uri"
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the schema to which the JSON report conforms.",
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
+ },
+ "vulnerabilities": {
+ "type": "array",
+ "description": "Array of vulnerability objects.",
+ "items": {
+ "type": "object",
+ "description": "Describes the vulnerability using GitLab Flavored Markdown",
+ "required": [
+ "category",
+ "cve",
+ "identifiers",
+ "location",
+ "scanner"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ },
+ "category": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Describes where this vulnerability belongs (for example, SAST, Dependency Scanning, and so on)."
+ },
+ "name": {
+ "type": "string",
+ "description": "The name of the vulnerability. This must not include the finding's specific information."
+ },
+ "message": {
+ "type": "string",
+ "description": "A short text section that describes the vulnerability. This may include the finding's specific information."
+ },
+ "description": {
+ "type": "string",
+ "description": "A long text section describing the vulnerability more fully."
+ },
+ "cve": {
+ "type": "string",
+ "description": "(Deprecated - use vulnerabilities[].id instead) A fingerprint string value that represents a concrete finding. This is used to determine whether two findings are same, which may not be 100% accurate. Note that this is NOT a CVE as described by https://cve.mitre.org/."
+ },
+ "severity": {
+ "type": "string",
+ "description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Info",
+ "Unknown",
+ "Low",
+ "Medium",
+ "High",
+ "Critical"
+ ]
+ },
+ "confidence": {
+ "type": "string",
+ "description": "How reliable the vulnerability's assessment is. Possible values are Ignore, Unknown, Experimental, Low, Medium, High, and Confirmed. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Ignore",
+ "Unknown",
+ "Experimental",
+ "Low",
+ "Medium",
+ "High",
+ "Confirmed"
+ ]
+ },
+ "solution": {
+ "type": "string",
+ "description": "Explanation of how to fix the vulnerability."
+ },
+ "scanner": {
+ "description": "Describes the scanner used to find this vulnerability.",
+ "type": "object",
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The scanner's ID, as a snake_case string."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Human-readable name of the scanner."
+ }
+ }
+ },
+ "identifiers": {
+ "type": "array",
+ "minItems": 1,
+ "description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
+ "minLength": 1
+ },
+ "name": {
+ "type": "string",
+ "description": "Human-readable name of the identifier.",
+ "minLength": 1
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the identifier's documentation.",
+ "format": "uri"
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the identifier, for matching purpose.",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "links": {
+ "type": "array",
+ "description": "An array of references to external documentation or articles that describe the vulnerability.",
+ "items": {
+ "type": "object",
+ "required": [
+ "url"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the vulnerability details link."
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the vulnerability details document.",
+ "format": "uri"
+ }
+ }
+ }
+ },
+ "details": {
+ "$ref": "#/definitions/named_list/properties/items"
+ },
+ "tracking": {
+ "description": "Describes how this vulnerability should be tracked as the project changes.",
+ "oneOf": [
+ {
+ "description": "Declares that a series of items should be tracked using source-specific tracking methods.",
+ "required": [
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "source"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "description": "An item that should be tracked using source-specific tracking methods.",
+ "type": "object",
+ "required": [
+ "signatures"
+ ],
+ "properties": {
+ "file": {
+ "type": "string",
+ "description": "Path to the file where the vulnerability is located."
+ },
+ "start_line": {
+ "type": "number",
+ "description": "The first line of the file that includes the vulnerability."
+ },
+ "end_line": {
+ "type": "number",
+ "description": "The last line of the file that includes the vulnerability."
+ },
+ "signatures": {
+ "type": "array",
+ "description": "An array of calculated tracking signatures for this tracking item.",
+ "minItems": 1,
+ "items": {
+ "description": "A calculated tracking signature value and metadata.",
+ "required": [
+ "algorithm",
+ "value"
+ ],
+ "properties": {
+ "algorithm": {
+ "type": "string",
+ "description": "The algorithm used to generate the signature."
+ },
+ "value": {
+ "type": "string",
+ "description": "The result of this signature algorithm."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Each tracking type must declare its own type."
+ }
+ }
+ },
+ "flags": {
+ "description": "Flags that can be attached to vulnerabilities.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Informational flags identified and assigned to a vulnerability.",
+ "required": [
+ "type",
+ "origin",
+ "description"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Result of the scan.",
+ "enum": [
+ "flagged-as-likely-false-positive"
+ ]
+ },
+ "origin": {
+ "minLength": 1,
+ "description": "Tool that issued the flag.",
+ "type": "string"
+ },
+ "description": {
+ "minLength": 1,
+ "description": "What the flag is about.",
+ "type": "string"
+ }
+ }
+ }
+ },
+ "location": {
+ "type": "object",
+ "description": "Identifies the vulnerability's location.",
+ "required": [
+ "file",
+ "dependency"
+ ],
+ "properties": {
+ "file": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Path to the manifest or lock file where the dependency is declared (such as yarn.lock)."
+ },
+ "dependency": {
+ "type": "object",
+ "description": "Describes the dependency of a project where the vulnerability is located.",
+ "properties": {
+ "package": {
+ "type": "object",
+ "description": "Provides information on the package where the vulnerability is located.",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the package where the vulnerability is located."
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "Version of the vulnerable package."
+ },
+ "iid": {
+ "description": "ID that identifies the dependency in the scope of a dependency file.",
+ "type": "number"
+ },
+ "direct": {
+ "type": "boolean",
+ "description": "Tells whether this is a direct, top-level dependency of the scanned project."
+ },
+ "dependency_path": {
+ "type": "array",
+ "description": "Ancestors of the dependency, starting from a direct project dependency, and ending with an immediate parent of the dependency. The dependency itself is excluded from the path. Direct dependencies have no path.",
+ "items": {
+ "type": "object",
+ "required": [
+ "iid"
+ ],
+ "properties": {
+ "iid": {
+ "type": "number",
+ "description": "ID that is unique in the scope of a parent object, and specific to the resource type."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "remediations": {
+ "type": "array",
+ "description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
+ "items": {
+ "type": "object",
+ "required": [
+ "fixes",
+ "summary",
+ "diff"
+ ],
+ "properties": {
+ "fixes": {
+ "type": "array",
+ "description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
+ "items": {
+ "type": "object",
+ "required": [
+ "cve"
+ ],
+ "properties": {
+ "cve": {
+ "type": "string",
+ "description": "(Deprecated - use vulnerabilities[].id instead) A fingerprint string value that represents a concrete finding. This is used to determine whether two findings are same, which may not be 100% accurate. Note that this is NOT a CVE as described by https://cve.mitre.org/."
+ }
+ }
+ }
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1,
+ "description": "An overview of how the vulnerabilities were fixed."
+ },
+ "diff": {
+ "type": "string",
+ "minLength": 1,
+ "description": "A base64-encoded remediation code diff, compatible with git apply."
+ }
+ }
+ }
+ },
+ "dependency_files": {
+ "type": "array",
+ "description": "List of dependency files identified in the project.",
+ "items": {
+ "type": "object",
+ "required": [
+ "path",
+ "package_manager",
+ "dependencies"
+ ],
+ "properties": {
+ "path": {
+ "type": "string",
+ "minLength": 1
+ },
+ "package_manager": {
+ "type": "string",
+ "minLength": 1
+ },
+ "dependencies": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Describes the dependency of a project where the vulnerability is located.",
+ "properties": {
+ "package": {
+ "type": "object",
+ "description": "Provides information on the package where the vulnerability is located.",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the package where the vulnerability is located."
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "Version of the vulnerable package."
+ },
+ "iid": {
+ "description": "ID that identifies the dependency in the scope of a dependency file.",
+ "type": "number"
+ },
+ "direct": {
+ "type": "boolean",
+ "description": "Tells whether this is a direct, top-level dependency of the scanned project."
+ },
+ "dependency_path": {
+ "type": "array",
+ "description": "Ancestors of the dependency, starting from a direct project dependency, and ending with an immediate parent of the dependency. The dependency itself is excluded from the path. Direct dependencies have no path.",
+ "items": {
+ "type": "object",
+ "required": [
+ "iid"
+ ],
+ "properties": {
+ "iid": {
+ "type": "number",
+ "description": "ID that is unique in the scope of a parent object, and specific to the resource type."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/sast-report-format.json b/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/sast-report-format.json
new file mode 100644
index 00000000000..40d4d9f5287
--- /dev/null
+++ b/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/sast-report-format.json
@@ -0,0 +1,869 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Report format for GitLab SAST",
+ "description": "This schema provides the report format for Static Application Security Testing analyzers (https://docs.gitlab.com/ee/user/application_security/sast).",
+ "definitions": {
+ "detail_type": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/named_list"
+ },
+ {
+ "$ref": "#/definitions/list"
+ },
+ {
+ "$ref": "#/definitions/table"
+ },
+ {
+ "$ref": "#/definitions/text"
+ },
+ {
+ "$ref": "#/definitions/url"
+ },
+ {
+ "$ref": "#/definitions/code"
+ },
+ {
+ "$ref": "#/definitions/value"
+ },
+ {
+ "$ref": "#/definitions/diff"
+ },
+ {
+ "$ref": "#/definitions/markdown"
+ },
+ {
+ "$ref": "#/definitions/commit"
+ },
+ {
+ "$ref": "#/definitions/file_location"
+ },
+ {
+ "$ref": "#/definitions/module_location"
+ }
+ ]
+ },
+ "text_value": {
+ "type": "string"
+ },
+ "named_field": {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "$ref": "#/definitions/text_value",
+ "minLength": 1
+ },
+ "description": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "named_list": {
+ "type": "object",
+ "description": "An object with named and typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "named-list"
+ },
+ "items": {
+ "type": "object",
+ "patternProperties": {
+ "^.*$": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/named_field"
+ },
+ {
+ "$ref": "#/definitions/detail_type"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "list": {
+ "type": "object",
+ "description": "A list of typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "list"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ },
+ "table": {
+ "type": "object",
+ "description": "A table of typed fields",
+ "required": [
+ "type",
+ "rows"
+ ],
+ "properties": {
+ "type": {
+ "const": "table"
+ },
+ "header": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ },
+ "rows": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ }
+ },
+ "text": {
+ "type": "object",
+ "description": "Raw text",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "text"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "url": {
+ "type": "object",
+ "description": "A single URL",
+ "required": [
+ "type",
+ "href"
+ ],
+ "properties": {
+ "type": {
+ "const": "url"
+ },
+ "text": {
+ "$ref": "#/definitions/text_value"
+ },
+ "href": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "http://mysite.com"
+ ]
+ }
+ }
+ },
+ "code": {
+ "type": "object",
+ "description": "A codeblock",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "code"
+ },
+ "value": {
+ "type": "string"
+ },
+ "lang": {
+ "type": "string",
+ "description": "A programming language"
+ }
+ }
+ },
+ "value": {
+ "type": "object",
+ "description": "A field that can store a range of types of value",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "value"
+ },
+ "value": {
+ "type": [
+ "number",
+ "string",
+ "boolean"
+ ]
+ }
+ }
+ },
+ "diff": {
+ "type": "object",
+ "description": "A diff",
+ "required": [
+ "type",
+ "before",
+ "after"
+ ],
+ "properties": {
+ "type": {
+ "const": "diff"
+ },
+ "before": {
+ "type": "string"
+ },
+ "after": {
+ "type": "string"
+ }
+ }
+ },
+ "markdown": {
+ "type": "object",
+ "description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "markdown"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value",
+ "examples": [
+ "Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
+ ]
+ }
+ }
+ },
+ "commit": {
+ "type": "object",
+ "description": "A commit/tag/branch within the GitLab project",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "commit"
+ },
+ "value": {
+ "type": "string",
+ "description": "The commit SHA",
+ "minLength": 1
+ }
+ }
+ },
+ "file_location": {
+ "type": "object",
+ "description": "A location within a file in the project",
+ "required": [
+ "type",
+ "file_name",
+ "line_start"
+ ],
+ "properties": {
+ "type": {
+ "const": "file-location"
+ },
+ "file_name": {
+ "type": "string",
+ "minLength": 1
+ },
+ "line_start": {
+ "type": "integer"
+ },
+ "line_end": {
+ "type": "integer"
+ }
+ }
+ },
+ "module_location": {
+ "type": "object",
+ "description": "A location within a binary module of the form module+relative_offset",
+ "required": [
+ "type",
+ "module_name",
+ "offset"
+ ],
+ "properties": {
+ "type": {
+ "const": "module-location"
+ },
+ "module_name": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "compiled_binary"
+ ]
+ },
+ "offset": {
+ "type": "integer",
+ "examples": [
+ 100
+ ]
+ }
+ }
+ }
+ },
+ "self": {
+ "version": "14.1.3"
+ },
+ "required": [
+ "version",
+ "vulnerabilities"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "scan": {
+ "type": "object",
+ "required": [
+ "end_time",
+ "scanner",
+ "start_time",
+ "status",
+ "type"
+ ],
+ "properties": {
+ "end_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}\\:\\d{2}\\:\\d{2}$",
+ "examples": [
+ "2020-01-28T03:26:02"
+ ]
+ },
+ "messages": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Communication intended for the initiator of a scan.",
+ "required": [
+ "level",
+ "value"
+ ],
+ "properties": {
+ "level": {
+ "type": "string",
+ "description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
+ "enum": [
+ "info",
+ "warn",
+ "fatal"
+ ],
+ "examples": [
+ "info"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "The message to communicate.",
+ "minLength": 1,
+ "examples": [
+ "Permission denied, scanning aborted"
+ ]
+ }
+ }
+ }
+ },
+ "analyzer": {
+ "type": "object",
+ "description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "gitlab-dast"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the analyzer, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "GitLab DAST"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "pattern": "^https?://.+",
+ "description": "A link to more information about the analyzer.",
+ "examples": [
+ "https://docs.gitlab.com/ee/user/application_security/dast"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the analyzer.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ }
+ }
+ },
+ "scanner": {
+ "type": "object",
+ "description": "Object defining the scanner used to perform the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the scanner.",
+ "minLength": 1,
+ "examples": [
+ "my-sast-scanner"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the scanner, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "My SAST Scanner"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "description": "A link to more information about the scanner.",
+ "examples": [
+ "https://scanner.url"
+ ]
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the scanner.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the scanner.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "start_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}\\:\\d{2}\\:\\d{2}$",
+ "examples": [
+ "2020-02-14T16:01:59"
+ ]
+ },
+ "status": {
+ "type": "string",
+ "description": "Result of the scan.",
+ "enum": [
+ "success",
+ "failure"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "description": "Type of the scan.",
+ "enum": [
+ "sast"
+ ]
+ }
+ }
+ },
+ "schema": {
+ "type": "string",
+ "description": "URI pointing to the validating security report schema.",
+ "format": "uri"
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the schema to which the JSON report conforms.",
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
+ },
+ "vulnerabilities": {
+ "type": "array",
+ "description": "Array of vulnerability objects.",
+ "items": {
+ "type": "object",
+ "description": "Describes the vulnerability using GitLab Flavored Markdown",
+ "required": [
+ "category",
+ "cve",
+ "identifiers",
+ "location",
+ "scanner"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ },
+ "category": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Describes where this vulnerability belongs (for example, SAST, Dependency Scanning, and so on)."
+ },
+ "name": {
+ "type": "string",
+ "description": "The name of the vulnerability. This must not include the finding's specific information."
+ },
+ "message": {
+ "type": "string",
+ "description": "A short text section that describes the vulnerability. This may include the finding's specific information."
+ },
+ "description": {
+ "type": "string",
+ "description": "A long text section describing the vulnerability more fully."
+ },
+ "cve": {
+ "type": "string",
+ "description": "(Deprecated - use vulnerabilities[].id instead) A fingerprint string value that represents a concrete finding. This is used to determine whether two findings are same, which may not be 100% accurate. Note that this is NOT a CVE as described by https://cve.mitre.org/."
+ },
+ "severity": {
+ "type": "string",
+ "description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Info",
+ "Unknown",
+ "Low",
+ "Medium",
+ "High",
+ "Critical"
+ ]
+ },
+ "confidence": {
+ "type": "string",
+ "description": "How reliable the vulnerability's assessment is. Possible values are Ignore, Unknown, Experimental, Low, Medium, High, and Confirmed. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Ignore",
+ "Unknown",
+ "Experimental",
+ "Low",
+ "Medium",
+ "High",
+ "Confirmed"
+ ]
+ },
+ "solution": {
+ "type": "string",
+ "description": "Explanation of how to fix the vulnerability."
+ },
+ "scanner": {
+ "description": "Describes the scanner used to find this vulnerability.",
+ "type": "object",
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The scanner's ID, as a snake_case string."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Human-readable name of the scanner."
+ }
+ }
+ },
+ "identifiers": {
+ "type": "array",
+ "minItems": 1,
+ "description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
+ "minLength": 1
+ },
+ "name": {
+ "type": "string",
+ "description": "Human-readable name of the identifier.",
+ "minLength": 1
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the identifier's documentation.",
+ "format": "uri"
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the identifier, for matching purpose.",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "links": {
+ "type": "array",
+ "description": "An array of references to external documentation or articles that describe the vulnerability.",
+ "items": {
+ "type": "object",
+ "required": [
+ "url"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the vulnerability details link."
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the vulnerability details document.",
+ "format": "uri"
+ }
+ }
+ }
+ },
+ "details": {
+ "$ref": "#/definitions/named_list/properties/items"
+ },
+ "tracking": {
+ "description": "Describes how this vulnerability should be tracked as the project changes.",
+ "oneOf": [
+ {
+ "description": "Declares that a series of items should be tracked using source-specific tracking methods.",
+ "required": [
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "source"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "description": "An item that should be tracked using source-specific tracking methods.",
+ "type": "object",
+ "required": [
+ "signatures"
+ ],
+ "properties": {
+ "file": {
+ "type": "string",
+ "description": "Path to the file where the vulnerability is located."
+ },
+ "start_line": {
+ "type": "number",
+ "description": "The first line of the file that includes the vulnerability."
+ },
+ "end_line": {
+ "type": "number",
+ "description": "The last line of the file that includes the vulnerability."
+ },
+ "signatures": {
+ "type": "array",
+ "description": "An array of calculated tracking signatures for this tracking item.",
+ "minItems": 1,
+ "items": {
+ "description": "A calculated tracking signature value and metadata.",
+ "required": [
+ "algorithm",
+ "value"
+ ],
+ "properties": {
+ "algorithm": {
+ "type": "string",
+ "description": "The algorithm used to generate the signature."
+ },
+ "value": {
+ "type": "string",
+ "description": "The result of this signature algorithm."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Each tracking type must declare its own type."
+ }
+ }
+ },
+ "flags": {
+ "description": "Flags that can be attached to vulnerabilities.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Informational flags identified and assigned to a vulnerability.",
+ "required": [
+ "type",
+ "origin",
+ "description"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Result of the scan.",
+ "enum": [
+ "flagged-as-likely-false-positive"
+ ]
+ },
+ "origin": {
+ "minLength": 1,
+ "description": "Tool that issued the flag.",
+ "type": "string"
+ },
+ "description": {
+ "minLength": 1,
+ "description": "What the flag is about.",
+ "type": "string"
+ }
+ }
+ }
+ },
+ "location": {
+ "type": "object",
+ "description": "Identifies the vulnerability's location.",
+ "properties": {
+ "file": {
+ "type": "string",
+ "description": "Path to the file where the vulnerability is located."
+ },
+ "start_line": {
+ "type": "number",
+ "description": "The first line of the code affected by the vulnerability."
+ },
+ "end_line": {
+ "type": "number",
+ "description": "The last line of the code affected by the vulnerability."
+ },
+ "class": {
+ "type": "string",
+ "description": "Provides the name of the class where the vulnerability is located."
+ },
+ "method": {
+ "type": "string",
+ "description": "Provides the name of the method where the vulnerability is located."
+ }
+ }
+ },
+ "raw_source_code_extract": {
+ "type": "string",
+ "description": "Provides an unsanitized excerpt of the affected source code."
+ }
+ }
+ }
+ },
+ "remediations": {
+ "type": "array",
+ "description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
+ "items": {
+ "type": "object",
+ "required": [
+ "fixes",
+ "summary",
+ "diff"
+ ],
+ "properties": {
+ "fixes": {
+ "type": "array",
+ "description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
+ "items": {
+ "type": "object",
+ "required": [
+ "cve"
+ ],
+ "properties": {
+ "cve": {
+ "type": "string",
+ "description": "(Deprecated - use vulnerabilities[].id instead) A fingerprint string value that represents a concrete finding. This is used to determine whether two findings are same, which may not be 100% accurate. Note that this is NOT a CVE as described by https://cve.mitre.org/."
+ }
+ }
+ }
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1,
+ "description": "An overview of how the vulnerabilities were fixed."
+ },
+ "diff": {
+ "type": "string",
+ "minLength": 1,
+ "description": "A base64-encoded remediation code diff, compatible with git apply."
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/secret-detection-report-format.json b/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/secret-detection-report-format.json
new file mode 100644
index 00000000000..cfde126dd7b
--- /dev/null
+++ b/lib/gitlab/ci/parsers/security/validators/schemas/14.1.3/secret-detection-report-format.json
@@ -0,0 +1,892 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Report format for GitLab Secret Detection",
+ "description": "This schema provides the the report format for the Secret Detection analyzer (https://docs.gitlab.com/ee/user/application_security/secret_detection)",
+ "definitions": {
+ "detail_type": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/named_list"
+ },
+ {
+ "$ref": "#/definitions/list"
+ },
+ {
+ "$ref": "#/definitions/table"
+ },
+ {
+ "$ref": "#/definitions/text"
+ },
+ {
+ "$ref": "#/definitions/url"
+ },
+ {
+ "$ref": "#/definitions/code"
+ },
+ {
+ "$ref": "#/definitions/value"
+ },
+ {
+ "$ref": "#/definitions/diff"
+ },
+ {
+ "$ref": "#/definitions/markdown"
+ },
+ {
+ "$ref": "#/definitions/commit"
+ },
+ {
+ "$ref": "#/definitions/file_location"
+ },
+ {
+ "$ref": "#/definitions/module_location"
+ }
+ ]
+ },
+ "text_value": {
+ "type": "string"
+ },
+ "named_field": {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "$ref": "#/definitions/text_value",
+ "minLength": 1
+ },
+ "description": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "named_list": {
+ "type": "object",
+ "description": "An object with named and typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "named-list"
+ },
+ "items": {
+ "type": "object",
+ "patternProperties": {
+ "^.*$": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/named_field"
+ },
+ {
+ "$ref": "#/definitions/detail_type"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "list": {
+ "type": "object",
+ "description": "A list of typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "list"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ },
+ "table": {
+ "type": "object",
+ "description": "A table of typed fields",
+ "required": [
+ "type",
+ "rows"
+ ],
+ "properties": {
+ "type": {
+ "const": "table"
+ },
+ "header": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ },
+ "rows": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ }
+ },
+ "text": {
+ "type": "object",
+ "description": "Raw text",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "text"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "url": {
+ "type": "object",
+ "description": "A single URL",
+ "required": [
+ "type",
+ "href"
+ ],
+ "properties": {
+ "type": {
+ "const": "url"
+ },
+ "text": {
+ "$ref": "#/definitions/text_value"
+ },
+ "href": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "http://mysite.com"
+ ]
+ }
+ }
+ },
+ "code": {
+ "type": "object",
+ "description": "A codeblock",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "code"
+ },
+ "value": {
+ "type": "string"
+ },
+ "lang": {
+ "type": "string",
+ "description": "A programming language"
+ }
+ }
+ },
+ "value": {
+ "type": "object",
+ "description": "A field that can store a range of types of value",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "value"
+ },
+ "value": {
+ "type": [
+ "number",
+ "string",
+ "boolean"
+ ]
+ }
+ }
+ },
+ "diff": {
+ "type": "object",
+ "description": "A diff",
+ "required": [
+ "type",
+ "before",
+ "after"
+ ],
+ "properties": {
+ "type": {
+ "const": "diff"
+ },
+ "before": {
+ "type": "string"
+ },
+ "after": {
+ "type": "string"
+ }
+ }
+ },
+ "markdown": {
+ "type": "object",
+ "description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "markdown"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value",
+ "examples": [
+ "Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
+ ]
+ }
+ }
+ },
+ "commit": {
+ "type": "object",
+ "description": "A commit/tag/branch within the GitLab project",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "commit"
+ },
+ "value": {
+ "type": "string",
+ "description": "The commit SHA",
+ "minLength": 1
+ }
+ }
+ },
+ "file_location": {
+ "type": "object",
+ "description": "A location within a file in the project",
+ "required": [
+ "type",
+ "file_name",
+ "line_start"
+ ],
+ "properties": {
+ "type": {
+ "const": "file-location"
+ },
+ "file_name": {
+ "type": "string",
+ "minLength": 1
+ },
+ "line_start": {
+ "type": "integer"
+ },
+ "line_end": {
+ "type": "integer"
+ }
+ }
+ },
+ "module_location": {
+ "type": "object",
+ "description": "A location within a binary module of the form module+relative_offset",
+ "required": [
+ "type",
+ "module_name",
+ "offset"
+ ],
+ "properties": {
+ "type": {
+ "const": "module-location"
+ },
+ "module_name": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "compiled_binary"
+ ]
+ },
+ "offset": {
+ "type": "integer",
+ "examples": [
+ 100
+ ]
+ }
+ }
+ }
+ },
+ "self": {
+ "version": "14.1.3"
+ },
+ "required": [
+ "version",
+ "vulnerabilities"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "scan": {
+ "type": "object",
+ "required": [
+ "end_time",
+ "scanner",
+ "start_time",
+ "status",
+ "type"
+ ],
+ "properties": {
+ "end_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}\\:\\d{2}\\:\\d{2}$",
+ "examples": [
+ "2020-01-28T03:26:02"
+ ]
+ },
+ "messages": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Communication intended for the initiator of a scan.",
+ "required": [
+ "level",
+ "value"
+ ],
+ "properties": {
+ "level": {
+ "type": "string",
+ "description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
+ "enum": [
+ "info",
+ "warn",
+ "fatal"
+ ],
+ "examples": [
+ "info"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "The message to communicate.",
+ "minLength": 1,
+ "examples": [
+ "Permission denied, scanning aborted"
+ ]
+ }
+ }
+ }
+ },
+ "analyzer": {
+ "type": "object",
+ "description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "gitlab-dast"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the analyzer, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "GitLab DAST"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "pattern": "^https?://.+",
+ "description": "A link to more information about the analyzer.",
+ "examples": [
+ "https://docs.gitlab.com/ee/user/application_security/dast"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the analyzer.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ }
+ }
+ },
+ "scanner": {
+ "type": "object",
+ "description": "Object defining the scanner used to perform the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the scanner.",
+ "minLength": 1,
+ "examples": [
+ "my-sast-scanner"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the scanner, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "My SAST Scanner"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "description": "A link to more information about the scanner.",
+ "examples": [
+ "https://scanner.url"
+ ]
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the scanner.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the scanner.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "start_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}\\:\\d{2}\\:\\d{2}$",
+ "examples": [
+ "2020-02-14T16:01:59"
+ ]
+ },
+ "status": {
+ "type": "string",
+ "description": "Result of the scan.",
+ "enum": [
+ "success",
+ "failure"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "description": "Type of the scan.",
+ "enum": [
+ "secret_detection"
+ ]
+ }
+ }
+ },
+ "schema": {
+ "type": "string",
+ "description": "URI pointing to the validating security report schema.",
+ "format": "uri"
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the schema to which the JSON report conforms.",
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
+ },
+ "vulnerabilities": {
+ "type": "array",
+ "description": "Array of vulnerability objects.",
+ "items": {
+ "type": "object",
+ "description": "Describes the vulnerability using GitLab Flavored Markdown",
+ "required": [
+ "category",
+ "cve",
+ "identifiers",
+ "location",
+ "scanner"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ },
+ "category": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Describes where this vulnerability belongs (for example, SAST, Dependency Scanning, and so on)."
+ },
+ "name": {
+ "type": "string",
+ "description": "The name of the vulnerability. This must not include the finding's specific information."
+ },
+ "message": {
+ "type": "string",
+ "description": "A short text section that describes the vulnerability. This may include the finding's specific information."
+ },
+ "description": {
+ "type": "string",
+ "description": "A long text section describing the vulnerability more fully."
+ },
+ "cve": {
+ "type": "string",
+ "description": "(Deprecated - use vulnerabilities[].id instead) A fingerprint string value that represents a concrete finding. This is used to determine whether two findings are same, which may not be 100% accurate. Note that this is NOT a CVE as described by https://cve.mitre.org/."
+ },
+ "severity": {
+ "type": "string",
+ "description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Info",
+ "Unknown",
+ "Low",
+ "Medium",
+ "High",
+ "Critical"
+ ]
+ },
+ "confidence": {
+ "type": "string",
+ "description": "How reliable the vulnerability's assessment is. Possible values are Ignore, Unknown, Experimental, Low, Medium, High, and Confirmed. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Ignore",
+ "Unknown",
+ "Experimental",
+ "Low",
+ "Medium",
+ "High",
+ "Confirmed"
+ ]
+ },
+ "solution": {
+ "type": "string",
+ "description": "Explanation of how to fix the vulnerability."
+ },
+ "scanner": {
+ "description": "Describes the scanner used to find this vulnerability.",
+ "type": "object",
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The scanner's ID, as a snake_case string."
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Human-readable name of the scanner."
+ }
+ }
+ },
+ "identifiers": {
+ "type": "array",
+ "minItems": 1,
+ "description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
+ "minLength": 1
+ },
+ "name": {
+ "type": "string",
+ "description": "Human-readable name of the identifier.",
+ "minLength": 1
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the identifier's documentation.",
+ "format": "uri"
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the identifier, for matching purpose.",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "links": {
+ "type": "array",
+ "description": "An array of references to external documentation or articles that describe the vulnerability.",
+ "items": {
+ "type": "object",
+ "required": [
+ "url"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the vulnerability details link."
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the vulnerability details document.",
+ "format": "uri"
+ }
+ }
+ }
+ },
+ "details": {
+ "$ref": "#/definitions/named_list/properties/items"
+ },
+ "tracking": {
+ "description": "Describes how this vulnerability should be tracked as the project changes.",
+ "oneOf": [
+ {
+ "description": "Declares that a series of items should be tracked using source-specific tracking methods.",
+ "required": [
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "source"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "description": "An item that should be tracked using source-specific tracking methods.",
+ "type": "object",
+ "required": [
+ "signatures"
+ ],
+ "properties": {
+ "file": {
+ "type": "string",
+ "description": "Path to the file where the vulnerability is located."
+ },
+ "start_line": {
+ "type": "number",
+ "description": "The first line of the file that includes the vulnerability."
+ },
+ "end_line": {
+ "type": "number",
+ "description": "The last line of the file that includes the vulnerability."
+ },
+ "signatures": {
+ "type": "array",
+ "description": "An array of calculated tracking signatures for this tracking item.",
+ "minItems": 1,
+ "items": {
+ "description": "A calculated tracking signature value and metadata.",
+ "required": [
+ "algorithm",
+ "value"
+ ],
+ "properties": {
+ "algorithm": {
+ "type": "string",
+ "description": "The algorithm used to generate the signature."
+ },
+ "value": {
+ "type": "string",
+ "description": "The result of this signature algorithm."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Each tracking type must declare its own type."
+ }
+ }
+ },
+ "flags": {
+ "description": "Flags that can be attached to vulnerabilities.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Informational flags identified and assigned to a vulnerability.",
+ "required": [
+ "type",
+ "origin",
+ "description"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Result of the scan.",
+ "enum": [
+ "flagged-as-likely-false-positive"
+ ]
+ },
+ "origin": {
+ "minLength": 1,
+ "description": "Tool that issued the flag.",
+ "type": "string"
+ },
+ "description": {
+ "minLength": 1,
+ "description": "What the flag is about.",
+ "type": "string"
+ }
+ }
+ }
+ },
+ "location": {
+ "required": [
+ "commit"
+ ],
+ "properties": {
+ "file": {
+ "type": "string",
+ "description": "Path to the file where the vulnerability is located"
+ },
+ "commit": {
+ "type": "object",
+ "description": "Represents the commit in which the vulnerability was detected",
+ "required": [
+ "sha"
+ ],
+ "properties": {
+ "author": {
+ "type": "string"
+ },
+ "date": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ },
+ "sha": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ },
+ "start_line": {
+ "type": "number",
+ "description": "The first line of the code affected by the vulnerability"
+ },
+ "end_line": {
+ "type": "number",
+ "description": "The last line of the code affected by the vulnerability"
+ },
+ "class": {
+ "type": "string",
+ "description": "Provides the name of the class where the vulnerability is located"
+ },
+ "method": {
+ "type": "string",
+ "description": "Provides the name of the method where the vulnerability is located"
+ }
+ }
+ },
+ "raw_source_code_extract": {
+ "type": "string",
+ "description": "Provides an unsanitized excerpt of the affected source code."
+ }
+ }
+ }
+ },
+ "remediations": {
+ "type": "array",
+ "description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
+ "items": {
+ "type": "object",
+ "required": [
+ "fixes",
+ "summary",
+ "diff"
+ ],
+ "properties": {
+ "fixes": {
+ "type": "array",
+ "description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
+ "items": {
+ "type": "object",
+ "required": [
+ "cve"
+ ],
+ "properties": {
+ "cve": {
+ "type": "string",
+ "description": "(Deprecated - use vulnerabilities[].id instead) A fingerprint string value that represents a concrete finding. This is used to determine whether two findings are same, which may not be 100% accurate. Note that this is NOT a CVE as described by https://cve.mitre.org/."
+ }
+ }
+ }
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1,
+ "description": "An overview of how the vulnerabilities were fixed."
+ },
+ "diff": {
+ "type": "string",
+ "minLength": 1,
+ "description": "A base64-encoded remediation code diff, compatible with git apply."
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/cluster-image-scanning-report-format.json b/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/cluster-image-scanning-report-format.json
new file mode 100644
index 00000000000..7ccb39a2b8e
--- /dev/null
+++ b/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/cluster-image-scanning-report-format.json
@@ -0,0 +1,946 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/raw/master/dist/cluster-image-scanning-report-format.json",
+ "title": "Report format for GitLab Cluster Image Scanning",
+ "description": "This schema provides the the report format for Cluster Image Scanning (https://docs.gitlab.com/ee/user/application_security/cluster_image_scanning/).",
+ "definitions": {
+ "detail_type": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/named_list"
+ },
+ {
+ "$ref": "#/definitions/list"
+ },
+ {
+ "$ref": "#/definitions/table"
+ },
+ {
+ "$ref": "#/definitions/text"
+ },
+ {
+ "$ref": "#/definitions/url"
+ },
+ {
+ "$ref": "#/definitions/code"
+ },
+ {
+ "$ref": "#/definitions/value"
+ },
+ {
+ "$ref": "#/definitions/diff"
+ },
+ {
+ "$ref": "#/definitions/markdown"
+ },
+ {
+ "$ref": "#/definitions/commit"
+ },
+ {
+ "$ref": "#/definitions/file_location"
+ },
+ {
+ "$ref": "#/definitions/module_location"
+ }
+ ]
+ },
+ "text_value": {
+ "type": "string"
+ },
+ "named_field": {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "$ref": "#/definitions/text_value",
+ "minLength": 1
+ },
+ "description": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "named_list": {
+ "type": "object",
+ "description": "An object with named and typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "named-list"
+ },
+ "items": {
+ "type": "object",
+ "patternProperties": {
+ "^.*$": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/named_field"
+ },
+ {
+ "$ref": "#/definitions/detail_type"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "list": {
+ "type": "object",
+ "description": "A list of typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "list"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ },
+ "table": {
+ "type": "object",
+ "description": "A table of typed fields",
+ "required": [
+ "type",
+ "rows"
+ ],
+ "properties": {
+ "type": {
+ "const": "table"
+ },
+ "header": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ },
+ "rows": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ }
+ },
+ "text": {
+ "type": "object",
+ "description": "Raw text",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "text"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "url": {
+ "type": "object",
+ "description": "A single URL",
+ "required": [
+ "type",
+ "href"
+ ],
+ "properties": {
+ "type": {
+ "const": "url"
+ },
+ "text": {
+ "$ref": "#/definitions/text_value"
+ },
+ "href": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "http://mysite.com"
+ ]
+ }
+ }
+ },
+ "code": {
+ "type": "object",
+ "description": "A codeblock",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "code"
+ },
+ "value": {
+ "type": "string"
+ },
+ "lang": {
+ "type": "string",
+ "description": "A programming language"
+ }
+ }
+ },
+ "value": {
+ "type": "object",
+ "description": "A field that can store a range of types of value",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "value"
+ },
+ "value": {
+ "type": [
+ "number",
+ "string",
+ "boolean"
+ ]
+ }
+ }
+ },
+ "diff": {
+ "type": "object",
+ "description": "A diff",
+ "required": [
+ "type",
+ "before",
+ "after"
+ ],
+ "properties": {
+ "type": {
+ "const": "diff"
+ },
+ "before": {
+ "type": "string"
+ },
+ "after": {
+ "type": "string"
+ }
+ }
+ },
+ "markdown": {
+ "type": "object",
+ "description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "markdown"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value",
+ "examples": [
+ "Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
+ ]
+ }
+ }
+ },
+ "commit": {
+ "type": "object",
+ "description": "A commit/tag/branch within the GitLab project",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "commit"
+ },
+ "value": {
+ "type": "string",
+ "description": "The commit SHA",
+ "minLength": 1
+ }
+ }
+ },
+ "file_location": {
+ "type": "object",
+ "description": "A location within a file in the project",
+ "required": [
+ "type",
+ "file_name",
+ "line_start"
+ ],
+ "properties": {
+ "type": {
+ "const": "file-location"
+ },
+ "file_name": {
+ "type": "string",
+ "minLength": 1
+ },
+ "line_start": {
+ "type": "integer"
+ },
+ "line_end": {
+ "type": "integer"
+ }
+ }
+ },
+ "module_location": {
+ "type": "object",
+ "description": "A location within a binary module of the form module+relative_offset",
+ "required": [
+ "type",
+ "module_name",
+ "offset"
+ ],
+ "properties": {
+ "type": {
+ "const": "module-location"
+ },
+ "module_name": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "compiled_binary"
+ ]
+ },
+ "offset": {
+ "type": "integer",
+ "examples": [
+ 100
+ ]
+ }
+ }
+ }
+ },
+ "self": {
+ "version": "15.0.0"
+ },
+ "required": [
+ "scan",
+ "version",
+ "vulnerabilities"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "scan": {
+ "type": "object",
+ "required": [
+ "analyzer",
+ "end_time",
+ "scanner",
+ "start_time",
+ "status",
+ "type"
+ ],
+ "properties": {
+ "end_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
+ "examples": [
+ "2020-01-28T03:26:02"
+ ]
+ },
+ "messages": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Communication intended for the initiator of a scan.",
+ "required": [
+ "level",
+ "value"
+ ],
+ "properties": {
+ "level": {
+ "type": "string",
+ "description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
+ "enum": [
+ "info",
+ "warn",
+ "fatal"
+ ],
+ "examples": [
+ "info"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "The message to communicate.",
+ "minLength": 1,
+ "examples": [
+ "Permission denied, scanning aborted"
+ ]
+ }
+ }
+ }
+ },
+ "analyzer": {
+ "type": "object",
+ "description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "gitlab-dast"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the analyzer, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "GitLab DAST"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "pattern": "^https?://.+",
+ "description": "A link to more information about the analyzer.",
+ "examples": [
+ "https://docs.gitlab.com/ee/user/application_security/dast"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the analyzer.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ }
+ }
+ },
+ "scanner": {
+ "type": "object",
+ "description": "Object defining the scanner used to perform the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the scanner.",
+ "minLength": 1,
+ "examples": [
+ "my-sast-scanner"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the scanner, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "My SAST Scanner"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "description": "A link to more information about the scanner.",
+ "examples": [
+ "https://scanner.url"
+ ]
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the scanner.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the scanner.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "start_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
+ "examples": [
+ "2020-02-14T16:01:59"
+ ]
+ },
+ "status": {
+ "type": "string",
+ "description": "Result of the scan.",
+ "enum": [
+ "success",
+ "failure"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "description": "Type of the scan.",
+ "enum": [
+ "cluster_image_scanning"
+ ]
+ }
+ }
+ },
+ "schema": {
+ "type": "string",
+ "description": "URI pointing to the validating security report schema.",
+ "pattern": "^https?://.+"
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the schema to which the JSON report conforms.",
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
+ },
+ "vulnerabilities": {
+ "type": "array",
+ "description": "Array of vulnerability objects.",
+ "items": {
+ "type": "object",
+ "description": "Describes the vulnerability using GitLab Flavored Markdown",
+ "required": [
+ "id",
+ "identifiers",
+ "location"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 255,
+ "description": "The name of the vulnerability. This must not include the finding's specific information."
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 1048576,
+ "description": "A long text section describing the vulnerability more fully."
+ },
+ "severity": {
+ "type": "string",
+ "description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Info",
+ "Unknown",
+ "Low",
+ "Medium",
+ "High",
+ "Critical"
+ ]
+ },
+ "solution": {
+ "type": "string",
+ "maxLength": 7000,
+ "description": "Explanation of how to fix the vulnerability."
+ },
+ "identifiers": {
+ "type": "array",
+ "minItems": 1,
+ "description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
+ "minLength": 1
+ },
+ "name": {
+ "type": "string",
+ "description": "Human-readable name of the identifier.",
+ "minLength": 1
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the identifier's documentation.",
+ "pattern": "^https?://.+"
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the identifier, for matching purpose.",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "links": {
+ "type": "array",
+ "description": "An array of references to external documentation or articles that describe the vulnerability.",
+ "items": {
+ "type": "object",
+ "required": [
+ "url"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the vulnerability details link."
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the vulnerability details document.",
+ "pattern": "^https?://.+"
+ }
+ }
+ }
+ },
+ "details": {
+ "$ref": "#/definitions/named_list/properties/items"
+ },
+ "tracking": {
+ "description": "Describes how this vulnerability should be tracked as the project changes.",
+ "oneOf": [
+ {
+ "description": "Declares that a series of items should be tracked using source-specific tracking methods.",
+ "required": [
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "source"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "description": "An item that should be tracked using source-specific tracking methods.",
+ "type": "object",
+ "required": [
+ "signatures"
+ ],
+ "properties": {
+ "file": {
+ "type": "string",
+ "description": "Path to the file where the vulnerability is located."
+ },
+ "start_line": {
+ "type": "number",
+ "description": "The first line of the file that includes the vulnerability."
+ },
+ "end_line": {
+ "type": "number",
+ "description": "The last line of the file that includes the vulnerability."
+ },
+ "signatures": {
+ "type": "array",
+ "description": "An array of calculated tracking signatures for this tracking item.",
+ "minItems": 1,
+ "items": {
+ "description": "A calculated tracking signature value and metadata.",
+ "required": [
+ "algorithm",
+ "value"
+ ],
+ "properties": {
+ "algorithm": {
+ "type": "string",
+ "description": "The algorithm used to generate the signature."
+ },
+ "value": {
+ "type": "string",
+ "description": "The result of this signature algorithm."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Each tracking type must declare its own type."
+ }
+ }
+ },
+ "flags": {
+ "description": "Flags that can be attached to vulnerabilities.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Informational flags identified and assigned to a vulnerability.",
+ "required": [
+ "type",
+ "origin",
+ "description"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Result of the scan.",
+ "enum": [
+ "flagged-as-likely-false-positive"
+ ]
+ },
+ "origin": {
+ "minLength": 1,
+ "description": "Tool that issued the flag.",
+ "type": "string"
+ },
+ "description": {
+ "minLength": 1,
+ "description": "What the flag is about.",
+ "type": "string"
+ }
+ }
+ }
+ },
+ "location": {
+ "type": "object",
+ "description": "Identifies the vulnerability's location.",
+ "required": [
+ "dependency",
+ "image",
+ "kubernetes_resource"
+ ],
+ "properties": {
+ "dependency": {
+ "type": "object",
+ "description": "Describes the dependency of a project where the vulnerability is located.",
+ "required": [
+ "package",
+ "version"
+ ],
+ "properties": {
+ "package": {
+ "type": "object",
+ "description": "Provides information on the package where the vulnerability is located.",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the package where the vulnerability is located."
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "Version of the vulnerable package."
+ },
+ "iid": {
+ "description": "ID that identifies the dependency in the scope of a dependency file.",
+ "type": "number"
+ },
+ "direct": {
+ "type": "boolean",
+ "description": "Tells whether this is a direct, top-level dependency of the scanned project."
+ },
+ "dependency_path": {
+ "type": "array",
+ "description": "Ancestors of the dependency, starting from a direct project dependency, and ending with an immediate parent of the dependency. The dependency itself is excluded from the path. Direct dependencies have no path.",
+ "items": {
+ "type": "object",
+ "required": [
+ "iid"
+ ],
+ "properties": {
+ "iid": {
+ "type": "number",
+ "description": "ID that is unique in the scope of a parent object, and specific to the resource type."
+ }
+ }
+ }
+ }
+ }
+ },
+ "operating_system": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "The operating system that contains the vulnerable package."
+ },
+ "image": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The analyzed Docker image.",
+ "examples": [
+ "index.docker.io/library/nginx:1.21"
+ ]
+ },
+ "kubernetes_resource": {
+ "type": "object",
+ "description": "The specific Kubernetes resource that was scanned.",
+ "required": [
+ "namespace",
+ "kind",
+ "name",
+ "container_name"
+ ],
+ "properties": {
+ "namespace": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "The Kubernetes namespace the resource that had its image scanned.",
+ "examples": [
+ "default",
+ "staging",
+ "production"
+ ]
+ },
+ "kind": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "The Kubernetes kind the resource that had its image scanned.",
+ "examples": [
+ "Deployment",
+ "DaemonSet"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "The name of the resource that had its image scanned.",
+ "examples": [
+ "nginx-ingress"
+ ]
+ },
+ "container_name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "The name of the container that had its image scanned.",
+ "examples": [
+ "nginx"
+ ]
+ },
+ "agent_id": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "The GitLab ID of the Kubernetes Agent which performed the scan.",
+ "examples": [
+ "1234"
+ ]
+ },
+ "cluster_id": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 255,
+ "description": "The GitLab ID of the Kubernetes cluster when using cluster integration.",
+ "examples": [
+ "1234"
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "remediations": {
+ "type": "array",
+ "description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
+ "items": {
+ "type": "object",
+ "required": [
+ "fixes",
+ "summary",
+ "diff"
+ ],
+ "properties": {
+ "fixes": {
+ "type": "array",
+ "description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
+ "items": {
+ "type": "object",
+ "required": [
+ "id"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ }
+ }
+ }
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1,
+ "description": "An overview of how the vulnerabilities were fixed."
+ },
+ "diff": {
+ "type": "string",
+ "minLength": 1,
+ "description": "A base64-encoded remediation code diff, compatible with git apply."
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/container-scanning-report-format.json b/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/container-scanning-report-format.json
new file mode 100644
index 00000000000..2517832853e
--- /dev/null
+++ b/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/container-scanning-report-format.json
@@ -0,0 +1,880 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/raw/master/dist/container-scanning-report-format.json",
+ "title": "Report format for GitLab Container Scanning",
+ "description": "This schema provides the the report format for Container Scanning (https://docs.gitlab.com/ee/user/application_security/container_scanning).",
+ "definitions": {
+ "detail_type": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/named_list"
+ },
+ {
+ "$ref": "#/definitions/list"
+ },
+ {
+ "$ref": "#/definitions/table"
+ },
+ {
+ "$ref": "#/definitions/text"
+ },
+ {
+ "$ref": "#/definitions/url"
+ },
+ {
+ "$ref": "#/definitions/code"
+ },
+ {
+ "$ref": "#/definitions/value"
+ },
+ {
+ "$ref": "#/definitions/diff"
+ },
+ {
+ "$ref": "#/definitions/markdown"
+ },
+ {
+ "$ref": "#/definitions/commit"
+ },
+ {
+ "$ref": "#/definitions/file_location"
+ },
+ {
+ "$ref": "#/definitions/module_location"
+ }
+ ]
+ },
+ "text_value": {
+ "type": "string"
+ },
+ "named_field": {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "$ref": "#/definitions/text_value",
+ "minLength": 1
+ },
+ "description": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "named_list": {
+ "type": "object",
+ "description": "An object with named and typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "named-list"
+ },
+ "items": {
+ "type": "object",
+ "patternProperties": {
+ "^.*$": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/named_field"
+ },
+ {
+ "$ref": "#/definitions/detail_type"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "list": {
+ "type": "object",
+ "description": "A list of typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "list"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ },
+ "table": {
+ "type": "object",
+ "description": "A table of typed fields",
+ "required": [
+ "type",
+ "rows"
+ ],
+ "properties": {
+ "type": {
+ "const": "table"
+ },
+ "header": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ },
+ "rows": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ }
+ },
+ "text": {
+ "type": "object",
+ "description": "Raw text",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "text"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "url": {
+ "type": "object",
+ "description": "A single URL",
+ "required": [
+ "type",
+ "href"
+ ],
+ "properties": {
+ "type": {
+ "const": "url"
+ },
+ "text": {
+ "$ref": "#/definitions/text_value"
+ },
+ "href": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "http://mysite.com"
+ ]
+ }
+ }
+ },
+ "code": {
+ "type": "object",
+ "description": "A codeblock",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "code"
+ },
+ "value": {
+ "type": "string"
+ },
+ "lang": {
+ "type": "string",
+ "description": "A programming language"
+ }
+ }
+ },
+ "value": {
+ "type": "object",
+ "description": "A field that can store a range of types of value",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "value"
+ },
+ "value": {
+ "type": [
+ "number",
+ "string",
+ "boolean"
+ ]
+ }
+ }
+ },
+ "diff": {
+ "type": "object",
+ "description": "A diff",
+ "required": [
+ "type",
+ "before",
+ "after"
+ ],
+ "properties": {
+ "type": {
+ "const": "diff"
+ },
+ "before": {
+ "type": "string"
+ },
+ "after": {
+ "type": "string"
+ }
+ }
+ },
+ "markdown": {
+ "type": "object",
+ "description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "markdown"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value",
+ "examples": [
+ "Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
+ ]
+ }
+ }
+ },
+ "commit": {
+ "type": "object",
+ "description": "A commit/tag/branch within the GitLab project",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "commit"
+ },
+ "value": {
+ "type": "string",
+ "description": "The commit SHA",
+ "minLength": 1
+ }
+ }
+ },
+ "file_location": {
+ "type": "object",
+ "description": "A location within a file in the project",
+ "required": [
+ "type",
+ "file_name",
+ "line_start"
+ ],
+ "properties": {
+ "type": {
+ "const": "file-location"
+ },
+ "file_name": {
+ "type": "string",
+ "minLength": 1
+ },
+ "line_start": {
+ "type": "integer"
+ },
+ "line_end": {
+ "type": "integer"
+ }
+ }
+ },
+ "module_location": {
+ "type": "object",
+ "description": "A location within a binary module of the form module+relative_offset",
+ "required": [
+ "type",
+ "module_name",
+ "offset"
+ ],
+ "properties": {
+ "type": {
+ "const": "module-location"
+ },
+ "module_name": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "compiled_binary"
+ ]
+ },
+ "offset": {
+ "type": "integer",
+ "examples": [
+ 100
+ ]
+ }
+ }
+ }
+ },
+ "self": {
+ "version": "15.0.0"
+ },
+ "required": [
+ "scan",
+ "version",
+ "vulnerabilities"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "scan": {
+ "type": "object",
+ "required": [
+ "analyzer",
+ "end_time",
+ "scanner",
+ "start_time",
+ "status",
+ "type"
+ ],
+ "properties": {
+ "end_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
+ "examples": [
+ "2020-01-28T03:26:02"
+ ]
+ },
+ "messages": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Communication intended for the initiator of a scan.",
+ "required": [
+ "level",
+ "value"
+ ],
+ "properties": {
+ "level": {
+ "type": "string",
+ "description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
+ "enum": [
+ "info",
+ "warn",
+ "fatal"
+ ],
+ "examples": [
+ "info"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "The message to communicate.",
+ "minLength": 1,
+ "examples": [
+ "Permission denied, scanning aborted"
+ ]
+ }
+ }
+ }
+ },
+ "analyzer": {
+ "type": "object",
+ "description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "gitlab-dast"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the analyzer, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "GitLab DAST"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "pattern": "^https?://.+",
+ "description": "A link to more information about the analyzer.",
+ "examples": [
+ "https://docs.gitlab.com/ee/user/application_security/dast"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the analyzer.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ }
+ }
+ },
+ "scanner": {
+ "type": "object",
+ "description": "Object defining the scanner used to perform the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the scanner.",
+ "minLength": 1,
+ "examples": [
+ "my-sast-scanner"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the scanner, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "My SAST Scanner"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "description": "A link to more information about the scanner.",
+ "examples": [
+ "https://scanner.url"
+ ]
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the scanner.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the scanner.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "start_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
+ "examples": [
+ "2020-02-14T16:01:59"
+ ]
+ },
+ "status": {
+ "type": "string",
+ "description": "Result of the scan.",
+ "enum": [
+ "success",
+ "failure"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "description": "Type of the scan.",
+ "enum": [
+ "container_scanning"
+ ]
+ }
+ }
+ },
+ "schema": {
+ "type": "string",
+ "description": "URI pointing to the validating security report schema.",
+ "pattern": "^https?://.+"
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the schema to which the JSON report conforms.",
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
+ },
+ "vulnerabilities": {
+ "type": "array",
+ "description": "Array of vulnerability objects.",
+ "items": {
+ "type": "object",
+ "description": "Describes the vulnerability using GitLab Flavored Markdown",
+ "required": [
+ "id",
+ "identifiers",
+ "location"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 255,
+ "description": "The name of the vulnerability. This must not include the finding's specific information."
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 1048576,
+ "description": "A long text section describing the vulnerability more fully."
+ },
+ "severity": {
+ "type": "string",
+ "description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Info",
+ "Unknown",
+ "Low",
+ "Medium",
+ "High",
+ "Critical"
+ ]
+ },
+ "solution": {
+ "type": "string",
+ "maxLength": 7000,
+ "description": "Explanation of how to fix the vulnerability."
+ },
+ "identifiers": {
+ "type": "array",
+ "minItems": 1,
+ "description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
+ "minLength": 1
+ },
+ "name": {
+ "type": "string",
+ "description": "Human-readable name of the identifier.",
+ "minLength": 1
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the identifier's documentation.",
+ "pattern": "^https?://.+"
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the identifier, for matching purpose.",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "links": {
+ "type": "array",
+ "description": "An array of references to external documentation or articles that describe the vulnerability.",
+ "items": {
+ "type": "object",
+ "required": [
+ "url"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the vulnerability details link."
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the vulnerability details document.",
+ "pattern": "^https?://.+"
+ }
+ }
+ }
+ },
+ "details": {
+ "$ref": "#/definitions/named_list/properties/items"
+ },
+ "tracking": {
+ "description": "Describes how this vulnerability should be tracked as the project changes.",
+ "oneOf": [
+ {
+ "description": "Declares that a series of items should be tracked using source-specific tracking methods.",
+ "required": [
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "source"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "description": "An item that should be tracked using source-specific tracking methods.",
+ "type": "object",
+ "required": [
+ "signatures"
+ ],
+ "properties": {
+ "file": {
+ "type": "string",
+ "description": "Path to the file where the vulnerability is located."
+ },
+ "start_line": {
+ "type": "number",
+ "description": "The first line of the file that includes the vulnerability."
+ },
+ "end_line": {
+ "type": "number",
+ "description": "The last line of the file that includes the vulnerability."
+ },
+ "signatures": {
+ "type": "array",
+ "description": "An array of calculated tracking signatures for this tracking item.",
+ "minItems": 1,
+ "items": {
+ "description": "A calculated tracking signature value and metadata.",
+ "required": [
+ "algorithm",
+ "value"
+ ],
+ "properties": {
+ "algorithm": {
+ "type": "string",
+ "description": "The algorithm used to generate the signature."
+ },
+ "value": {
+ "type": "string",
+ "description": "The result of this signature algorithm."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Each tracking type must declare its own type."
+ }
+ }
+ },
+ "flags": {
+ "description": "Flags that can be attached to vulnerabilities.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Informational flags identified and assigned to a vulnerability.",
+ "required": [
+ "type",
+ "origin",
+ "description"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Result of the scan.",
+ "enum": [
+ "flagged-as-likely-false-positive"
+ ]
+ },
+ "origin": {
+ "minLength": 1,
+ "description": "Tool that issued the flag.",
+ "type": "string"
+ },
+ "description": {
+ "minLength": 1,
+ "description": "What the flag is about.",
+ "type": "string"
+ }
+ }
+ }
+ },
+ "location": {
+ "type": "object",
+ "description": "Identifies the vulnerability's location.",
+ "required": [
+ "dependency",
+ "operating_system",
+ "image"
+ ],
+ "properties": {
+ "dependency": {
+ "type": "object",
+ "description": "Describes the dependency of a project where the vulnerability is located.",
+ "required": [
+ "package",
+ "version"
+ ],
+ "properties": {
+ "package": {
+ "type": "object",
+ "description": "Provides information on the package where the vulnerability is located.",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the package where the vulnerability is located."
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "Version of the vulnerable package."
+ },
+ "iid": {
+ "description": "ID that identifies the dependency in the scope of a dependency file.",
+ "type": "number"
+ },
+ "direct": {
+ "type": "boolean",
+ "description": "Tells whether this is a direct, top-level dependency of the scanned project."
+ },
+ "dependency_path": {
+ "type": "array",
+ "description": "Ancestors of the dependency, starting from a direct project dependency, and ending with an immediate parent of the dependency. The dependency itself is excluded from the path. Direct dependencies have no path.",
+ "items": {
+ "type": "object",
+ "required": [
+ "iid"
+ ],
+ "properties": {
+ "iid": {
+ "type": "number",
+ "description": "ID that is unique in the scope of a parent object, and specific to the resource type."
+ }
+ }
+ }
+ }
+ }
+ },
+ "operating_system": {
+ "type": "string",
+ "minLength": 1,
+ "description": "The operating system that contains the vulnerable package."
+ },
+ "image": {
+ "type": "string",
+ "minLength": 1,
+ "pattern": "^[^:]+(:\\d+[^:]*)?:[^:]+(:[^:]+)?$",
+ "description": "The analyzed Docker image."
+ },
+ "default_branch_image": {
+ "type": "string",
+ "maxLength": 255,
+ "pattern": "^[a-zA-Z0-9/_.-]+(:\\d+[a-zA-Z0-9/_.-]*)?:[a-zA-Z0-9_.-]+$",
+ "description": "The name of the image on the default branch."
+ }
+ }
+ }
+ }
+ }
+ },
+ "remediations": {
+ "type": "array",
+ "description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
+ "items": {
+ "type": "object",
+ "required": [
+ "fixes",
+ "summary",
+ "diff"
+ ],
+ "properties": {
+ "fixes": {
+ "type": "array",
+ "description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
+ "items": {
+ "type": "object",
+ "required": [
+ "id"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ }
+ }
+ }
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1,
+ "description": "An overview of how the vulnerabilities were fixed."
+ },
+ "diff": {
+ "type": "string",
+ "minLength": 1,
+ "description": "A base64-encoded remediation code diff, compatible with git apply."
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/coverage-fuzzing-report-format.json b/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/coverage-fuzzing-report-format.json
new file mode 100644
index 00000000000..a2f9eb12992
--- /dev/null
+++ b/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/coverage-fuzzing-report-format.json
@@ -0,0 +1,836 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/raw/master/dist/coverage-fuzzing-report-format.json",
+ "title": "Report format for GitLab Fuzz Testing",
+ "description": "This schema provides the report format for Coverage Guided Fuzz Testing (https://docs.gitlab.com/ee/user/application_security/coverage_fuzzing).",
+ "definitions": {
+ "detail_type": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/named_list"
+ },
+ {
+ "$ref": "#/definitions/list"
+ },
+ {
+ "$ref": "#/definitions/table"
+ },
+ {
+ "$ref": "#/definitions/text"
+ },
+ {
+ "$ref": "#/definitions/url"
+ },
+ {
+ "$ref": "#/definitions/code"
+ },
+ {
+ "$ref": "#/definitions/value"
+ },
+ {
+ "$ref": "#/definitions/diff"
+ },
+ {
+ "$ref": "#/definitions/markdown"
+ },
+ {
+ "$ref": "#/definitions/commit"
+ },
+ {
+ "$ref": "#/definitions/file_location"
+ },
+ {
+ "$ref": "#/definitions/module_location"
+ }
+ ]
+ },
+ "text_value": {
+ "type": "string"
+ },
+ "named_field": {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "$ref": "#/definitions/text_value",
+ "minLength": 1
+ },
+ "description": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "named_list": {
+ "type": "object",
+ "description": "An object with named and typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "named-list"
+ },
+ "items": {
+ "type": "object",
+ "patternProperties": {
+ "^.*$": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/named_field"
+ },
+ {
+ "$ref": "#/definitions/detail_type"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "list": {
+ "type": "object",
+ "description": "A list of typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "list"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ },
+ "table": {
+ "type": "object",
+ "description": "A table of typed fields",
+ "required": [
+ "type",
+ "rows"
+ ],
+ "properties": {
+ "type": {
+ "const": "table"
+ },
+ "header": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ },
+ "rows": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ }
+ },
+ "text": {
+ "type": "object",
+ "description": "Raw text",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "text"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "url": {
+ "type": "object",
+ "description": "A single URL",
+ "required": [
+ "type",
+ "href"
+ ],
+ "properties": {
+ "type": {
+ "const": "url"
+ },
+ "text": {
+ "$ref": "#/definitions/text_value"
+ },
+ "href": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "http://mysite.com"
+ ]
+ }
+ }
+ },
+ "code": {
+ "type": "object",
+ "description": "A codeblock",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "code"
+ },
+ "value": {
+ "type": "string"
+ },
+ "lang": {
+ "type": "string",
+ "description": "A programming language"
+ }
+ }
+ },
+ "value": {
+ "type": "object",
+ "description": "A field that can store a range of types of value",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "value"
+ },
+ "value": {
+ "type": [
+ "number",
+ "string",
+ "boolean"
+ ]
+ }
+ }
+ },
+ "diff": {
+ "type": "object",
+ "description": "A diff",
+ "required": [
+ "type",
+ "before",
+ "after"
+ ],
+ "properties": {
+ "type": {
+ "const": "diff"
+ },
+ "before": {
+ "type": "string"
+ },
+ "after": {
+ "type": "string"
+ }
+ }
+ },
+ "markdown": {
+ "type": "object",
+ "description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "markdown"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value",
+ "examples": [
+ "Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
+ ]
+ }
+ }
+ },
+ "commit": {
+ "type": "object",
+ "description": "A commit/tag/branch within the GitLab project",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "commit"
+ },
+ "value": {
+ "type": "string",
+ "description": "The commit SHA",
+ "minLength": 1
+ }
+ }
+ },
+ "file_location": {
+ "type": "object",
+ "description": "A location within a file in the project",
+ "required": [
+ "type",
+ "file_name",
+ "line_start"
+ ],
+ "properties": {
+ "type": {
+ "const": "file-location"
+ },
+ "file_name": {
+ "type": "string",
+ "minLength": 1
+ },
+ "line_start": {
+ "type": "integer"
+ },
+ "line_end": {
+ "type": "integer"
+ }
+ }
+ },
+ "module_location": {
+ "type": "object",
+ "description": "A location within a binary module of the form module+relative_offset",
+ "required": [
+ "type",
+ "module_name",
+ "offset"
+ ],
+ "properties": {
+ "type": {
+ "const": "module-location"
+ },
+ "module_name": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "compiled_binary"
+ ]
+ },
+ "offset": {
+ "type": "integer",
+ "examples": [
+ 100
+ ]
+ }
+ }
+ }
+ },
+ "self": {
+ "version": "15.0.0"
+ },
+ "required": [
+ "scan",
+ "version",
+ "vulnerabilities"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "scan": {
+ "type": "object",
+ "required": [
+ "analyzer",
+ "end_time",
+ "scanner",
+ "start_time",
+ "status",
+ "type"
+ ],
+ "properties": {
+ "end_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
+ "examples": [
+ "2020-01-28T03:26:02"
+ ]
+ },
+ "messages": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Communication intended for the initiator of a scan.",
+ "required": [
+ "level",
+ "value"
+ ],
+ "properties": {
+ "level": {
+ "type": "string",
+ "description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
+ "enum": [
+ "info",
+ "warn",
+ "fatal"
+ ],
+ "examples": [
+ "info"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "The message to communicate.",
+ "minLength": 1,
+ "examples": [
+ "Permission denied, scanning aborted"
+ ]
+ }
+ }
+ }
+ },
+ "analyzer": {
+ "type": "object",
+ "description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "gitlab-dast"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the analyzer, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "GitLab DAST"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "pattern": "^https?://.+",
+ "description": "A link to more information about the analyzer.",
+ "examples": [
+ "https://docs.gitlab.com/ee/user/application_security/dast"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the analyzer.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ }
+ }
+ },
+ "scanner": {
+ "type": "object",
+ "description": "Object defining the scanner used to perform the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the scanner.",
+ "minLength": 1,
+ "examples": [
+ "my-sast-scanner"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the scanner, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "My SAST Scanner"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "description": "A link to more information about the scanner.",
+ "examples": [
+ "https://scanner.url"
+ ]
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the scanner.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the scanner.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "start_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
+ "examples": [
+ "2020-02-14T16:01:59"
+ ]
+ },
+ "status": {
+ "type": "string",
+ "description": "Result of the scan.",
+ "enum": [
+ "success",
+ "failure"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "description": "Type of the scan.",
+ "enum": [
+ "coverage_fuzzing"
+ ]
+ }
+ }
+ },
+ "schema": {
+ "type": "string",
+ "description": "URI pointing to the validating security report schema.",
+ "pattern": "^https?://.+"
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the schema to which the JSON report conforms.",
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
+ },
+ "vulnerabilities": {
+ "type": "array",
+ "description": "Array of vulnerability objects.",
+ "items": {
+ "type": "object",
+ "description": "Describes the vulnerability using GitLab Flavored Markdown",
+ "required": [
+ "id",
+ "identifiers",
+ "location"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 255,
+ "description": "The name of the vulnerability. This must not include the finding's specific information."
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 1048576,
+ "description": "A long text section describing the vulnerability more fully."
+ },
+ "severity": {
+ "type": "string",
+ "description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Info",
+ "Unknown",
+ "Low",
+ "Medium",
+ "High",
+ "Critical"
+ ]
+ },
+ "solution": {
+ "type": "string",
+ "maxLength": 7000,
+ "description": "Explanation of how to fix the vulnerability."
+ },
+ "identifiers": {
+ "type": "array",
+ "minItems": 1,
+ "description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
+ "minLength": 1
+ },
+ "name": {
+ "type": "string",
+ "description": "Human-readable name of the identifier.",
+ "minLength": 1
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the identifier's documentation.",
+ "pattern": "^https?://.+"
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the identifier, for matching purpose.",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "links": {
+ "type": "array",
+ "description": "An array of references to external documentation or articles that describe the vulnerability.",
+ "items": {
+ "type": "object",
+ "required": [
+ "url"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the vulnerability details link."
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the vulnerability details document.",
+ "pattern": "^https?://.+"
+ }
+ }
+ }
+ },
+ "details": {
+ "$ref": "#/definitions/named_list/properties/items"
+ },
+ "tracking": {
+ "description": "Describes how this vulnerability should be tracked as the project changes.",
+ "oneOf": [
+ {
+ "description": "Declares that a series of items should be tracked using source-specific tracking methods.",
+ "required": [
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "source"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "description": "An item that should be tracked using source-specific tracking methods.",
+ "type": "object",
+ "required": [
+ "signatures"
+ ],
+ "properties": {
+ "file": {
+ "type": "string",
+ "description": "Path to the file where the vulnerability is located."
+ },
+ "start_line": {
+ "type": "number",
+ "description": "The first line of the file that includes the vulnerability."
+ },
+ "end_line": {
+ "type": "number",
+ "description": "The last line of the file that includes the vulnerability."
+ },
+ "signatures": {
+ "type": "array",
+ "description": "An array of calculated tracking signatures for this tracking item.",
+ "minItems": 1,
+ "items": {
+ "description": "A calculated tracking signature value and metadata.",
+ "required": [
+ "algorithm",
+ "value"
+ ],
+ "properties": {
+ "algorithm": {
+ "type": "string",
+ "description": "The algorithm used to generate the signature."
+ },
+ "value": {
+ "type": "string",
+ "description": "The result of this signature algorithm."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Each tracking type must declare its own type."
+ }
+ }
+ },
+ "flags": {
+ "description": "Flags that can be attached to vulnerabilities.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Informational flags identified and assigned to a vulnerability.",
+ "required": [
+ "type",
+ "origin",
+ "description"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Result of the scan.",
+ "enum": [
+ "flagged-as-likely-false-positive"
+ ]
+ },
+ "origin": {
+ "minLength": 1,
+ "description": "Tool that issued the flag.",
+ "type": "string"
+ },
+ "description": {
+ "minLength": 1,
+ "description": "What the flag is about.",
+ "type": "string"
+ }
+ }
+ }
+ },
+ "location": {
+ "description": "The location of the error",
+ "type": "object",
+ "properties": {
+ "crash_address": {
+ "type": "string",
+ "description": "The relative address in memory were the crash occurred.",
+ "examples": [
+ "0xabababab"
+ ]
+ },
+ "stacktrace_snippet": {
+ "type": "string",
+ "description": "The stack trace recorded during fuzzing resulting the crash.",
+ "examples": [
+ "func_a+0xabcd\nfunc_b+0xabcc"
+ ]
+ },
+ "crash_state": {
+ "type": "string",
+ "description": "Minimised and normalized crash stack-trace (called crash_state).",
+ "examples": [
+ "func_a+0xa\nfunc_b+0xb\nfunc_c+0xc"
+ ]
+ },
+ "crash_type": {
+ "type": "string",
+ "description": "Type of the crash.",
+ "examples": [
+ "Heap-Buffer-overflow",
+ "Division-by-zero"
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "remediations": {
+ "type": "array",
+ "description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
+ "items": {
+ "type": "object",
+ "required": [
+ "fixes",
+ "summary",
+ "diff"
+ ],
+ "properties": {
+ "fixes": {
+ "type": "array",
+ "description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
+ "items": {
+ "type": "object",
+ "required": [
+ "id"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ }
+ }
+ }
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1,
+ "description": "An overview of how the vulnerabilities were fixed."
+ },
+ "diff": {
+ "type": "string",
+ "minLength": 1,
+ "description": "A base64-encoded remediation code diff, compatible with git apply."
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/dast-report-format.json b/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/dast-report-format.json
new file mode 100644
index 00000000000..10fafaf8975
--- /dev/null
+++ b/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/dast-report-format.json
@@ -0,0 +1,1241 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/raw/master/dist/dast-report-format.json",
+ "title": "Report format for GitLab DAST",
+ "description": "This schema provides the the report format for Dynamic Application Security Testing (https://docs.gitlab.com/ee/user/application_security/dast).",
+ "definitions": {
+ "detail_type": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/named_list"
+ },
+ {
+ "$ref": "#/definitions/list"
+ },
+ {
+ "$ref": "#/definitions/table"
+ },
+ {
+ "$ref": "#/definitions/text"
+ },
+ {
+ "$ref": "#/definitions/url"
+ },
+ {
+ "$ref": "#/definitions/code"
+ },
+ {
+ "$ref": "#/definitions/value"
+ },
+ {
+ "$ref": "#/definitions/diff"
+ },
+ {
+ "$ref": "#/definitions/markdown"
+ },
+ {
+ "$ref": "#/definitions/commit"
+ },
+ {
+ "$ref": "#/definitions/file_location"
+ },
+ {
+ "$ref": "#/definitions/module_location"
+ }
+ ]
+ },
+ "text_value": {
+ "type": "string"
+ },
+ "named_field": {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "$ref": "#/definitions/text_value",
+ "minLength": 1
+ },
+ "description": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "named_list": {
+ "type": "object",
+ "description": "An object with named and typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "named-list"
+ },
+ "items": {
+ "type": "object",
+ "patternProperties": {
+ "^.*$": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/named_field"
+ },
+ {
+ "$ref": "#/definitions/detail_type"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "list": {
+ "type": "object",
+ "description": "A list of typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "list"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ },
+ "table": {
+ "type": "object",
+ "description": "A table of typed fields",
+ "required": [
+ "type",
+ "rows"
+ ],
+ "properties": {
+ "type": {
+ "const": "table"
+ },
+ "header": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ },
+ "rows": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ }
+ },
+ "text": {
+ "type": "object",
+ "description": "Raw text",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "text"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "url": {
+ "type": "object",
+ "description": "A single URL",
+ "required": [
+ "type",
+ "href"
+ ],
+ "properties": {
+ "type": {
+ "const": "url"
+ },
+ "text": {
+ "$ref": "#/definitions/text_value"
+ },
+ "href": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "http://mysite.com"
+ ]
+ }
+ }
+ },
+ "code": {
+ "type": "object",
+ "description": "A codeblock",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "code"
+ },
+ "value": {
+ "type": "string"
+ },
+ "lang": {
+ "type": "string",
+ "description": "A programming language"
+ }
+ }
+ },
+ "value": {
+ "type": "object",
+ "description": "A field that can store a range of types of value",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "value"
+ },
+ "value": {
+ "type": [
+ "number",
+ "string",
+ "boolean"
+ ]
+ }
+ }
+ },
+ "diff": {
+ "type": "object",
+ "description": "A diff",
+ "required": [
+ "type",
+ "before",
+ "after"
+ ],
+ "properties": {
+ "type": {
+ "const": "diff"
+ },
+ "before": {
+ "type": "string"
+ },
+ "after": {
+ "type": "string"
+ }
+ }
+ },
+ "markdown": {
+ "type": "object",
+ "description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "markdown"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value",
+ "examples": [
+ "Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
+ ]
+ }
+ }
+ },
+ "commit": {
+ "type": "object",
+ "description": "A commit/tag/branch within the GitLab project",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "commit"
+ },
+ "value": {
+ "type": "string",
+ "description": "The commit SHA",
+ "minLength": 1
+ }
+ }
+ },
+ "file_location": {
+ "type": "object",
+ "description": "A location within a file in the project",
+ "required": [
+ "type",
+ "file_name",
+ "line_start"
+ ],
+ "properties": {
+ "type": {
+ "const": "file-location"
+ },
+ "file_name": {
+ "type": "string",
+ "minLength": 1
+ },
+ "line_start": {
+ "type": "integer"
+ },
+ "line_end": {
+ "type": "integer"
+ }
+ }
+ },
+ "module_location": {
+ "type": "object",
+ "description": "A location within a binary module of the form module+relative_offset",
+ "required": [
+ "type",
+ "module_name",
+ "offset"
+ ],
+ "properties": {
+ "type": {
+ "const": "module-location"
+ },
+ "module_name": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "compiled_binary"
+ ]
+ },
+ "offset": {
+ "type": "integer",
+ "examples": [
+ 100
+ ]
+ }
+ }
+ }
+ },
+ "self": {
+ "version": "15.0.0"
+ },
+ "required": [
+ "scan",
+ "version",
+ "vulnerabilities"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "scan": {
+ "type": "object",
+ "required": [
+ "analyzer",
+ "end_time",
+ "scanned_resources",
+ "scanner",
+ "start_time",
+ "status",
+ "type"
+ ],
+ "properties": {
+ "end_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
+ "examples": [
+ "2020-01-28T03:26:02"
+ ]
+ },
+ "messages": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Communication intended for the initiator of a scan.",
+ "required": [
+ "level",
+ "value"
+ ],
+ "properties": {
+ "level": {
+ "type": "string",
+ "description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
+ "enum": [
+ "info",
+ "warn",
+ "fatal"
+ ],
+ "examples": [
+ "info"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "The message to communicate.",
+ "minLength": 1,
+ "examples": [
+ "Permission denied, scanning aborted"
+ ]
+ }
+ }
+ }
+ },
+ "analyzer": {
+ "type": "object",
+ "description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "gitlab-dast"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the analyzer, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "GitLab DAST"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "pattern": "^https?://.+",
+ "description": "A link to more information about the analyzer.",
+ "examples": [
+ "https://docs.gitlab.com/ee/user/application_security/dast"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the analyzer.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ }
+ }
+ },
+ "scanner": {
+ "type": "object",
+ "description": "Object defining the scanner used to perform the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the scanner.",
+ "minLength": 1,
+ "examples": [
+ "my-sast-scanner"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the scanner, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "My SAST Scanner"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "description": "A link to more information about the scanner.",
+ "examples": [
+ "https://scanner.url"
+ ]
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the scanner.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the scanner.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "start_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
+ "examples": [
+ "2020-02-14T16:01:59"
+ ]
+ },
+ "status": {
+ "type": "string",
+ "description": "Result of the scan.",
+ "enum": [
+ "success",
+ "failure"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "description": "Type of the scan.",
+ "enum": [
+ "dast",
+ "api_fuzzing"
+ ]
+ },
+ "scanned_resources": {
+ "type": "array",
+ "description": "The attack surface scanned by DAST.",
+ "items": {
+ "type": "object",
+ "required": [
+ "method",
+ "url",
+ "type"
+ ],
+ "properties": {
+ "method": {
+ "type": "string",
+ "minLength": 1,
+ "description": "HTTP method of the scanned resource.",
+ "examples": [
+ "GET",
+ "POST",
+ "HEAD"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "minLength": 1,
+ "description": "URL of the scanned resource.",
+ "examples": [
+ "http://my.site.com/a-page"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Type of the scanned resource, for DAST, this must be 'url'.",
+ "examples": [
+ "url"
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "schema": {
+ "type": "string",
+ "description": "URI pointing to the validating security report schema.",
+ "pattern": "^https?://.+"
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the schema to which the JSON report conforms.",
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
+ },
+ "vulnerabilities": {
+ "type": "array",
+ "description": "Array of vulnerability objects.",
+ "items": {
+ "type": "object",
+ "description": "Describes the vulnerability using GitLab Flavored Markdown",
+ "required": [
+ "id",
+ "identifiers",
+ "location"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 255,
+ "description": "The name of the vulnerability. This must not include the finding's specific information."
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 1048576,
+ "description": "A long text section describing the vulnerability more fully."
+ },
+ "severity": {
+ "type": "string",
+ "description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Info",
+ "Unknown",
+ "Low",
+ "Medium",
+ "High",
+ "Critical"
+ ]
+ },
+ "solution": {
+ "type": "string",
+ "maxLength": 7000,
+ "description": "Explanation of how to fix the vulnerability."
+ },
+ "identifiers": {
+ "type": "array",
+ "minItems": 1,
+ "description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
+ "minLength": 1
+ },
+ "name": {
+ "type": "string",
+ "description": "Human-readable name of the identifier.",
+ "minLength": 1
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the identifier's documentation.",
+ "pattern": "^https?://.+"
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the identifier, for matching purpose.",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "links": {
+ "type": "array",
+ "description": "An array of references to external documentation or articles that describe the vulnerability.",
+ "items": {
+ "type": "object",
+ "required": [
+ "url"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the vulnerability details link."
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the vulnerability details document.",
+ "pattern": "^https?://.+"
+ }
+ }
+ }
+ },
+ "details": {
+ "$ref": "#/definitions/named_list/properties/items"
+ },
+ "tracking": {
+ "description": "Describes how this vulnerability should be tracked as the project changes.",
+ "oneOf": [
+ {
+ "description": "Declares that a series of items should be tracked using source-specific tracking methods.",
+ "required": [
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "source"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "description": "An item that should be tracked using source-specific tracking methods.",
+ "type": "object",
+ "required": [
+ "signatures"
+ ],
+ "properties": {
+ "file": {
+ "type": "string",
+ "description": "Path to the file where the vulnerability is located."
+ },
+ "start_line": {
+ "type": "number",
+ "description": "The first line of the file that includes the vulnerability."
+ },
+ "end_line": {
+ "type": "number",
+ "description": "The last line of the file that includes the vulnerability."
+ },
+ "signatures": {
+ "type": "array",
+ "description": "An array of calculated tracking signatures for this tracking item.",
+ "minItems": 1,
+ "items": {
+ "description": "A calculated tracking signature value and metadata.",
+ "required": [
+ "algorithm",
+ "value"
+ ],
+ "properties": {
+ "algorithm": {
+ "type": "string",
+ "description": "The algorithm used to generate the signature."
+ },
+ "value": {
+ "type": "string",
+ "description": "The result of this signature algorithm."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Each tracking type must declare its own type."
+ }
+ }
+ },
+ "flags": {
+ "description": "Flags that can be attached to vulnerabilities.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Informational flags identified and assigned to a vulnerability.",
+ "required": [
+ "type",
+ "origin",
+ "description"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Result of the scan.",
+ "enum": [
+ "flagged-as-likely-false-positive"
+ ]
+ },
+ "origin": {
+ "minLength": 1,
+ "description": "Tool that issued the flag.",
+ "type": "string"
+ },
+ "description": {
+ "minLength": 1,
+ "description": "What the flag is about.",
+ "type": "string"
+ }
+ }
+ }
+ },
+ "evidence": {
+ "type": "object",
+ "properties": {
+ "source": {
+ "type": "object",
+ "description": "Source of evidence",
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Unique source identifier",
+ "examples": [
+ "assert:LogAnalysis",
+ "assert:StatusCode"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Source display name",
+ "examples": [
+ "Log Analysis",
+ "Status Code"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "description": "Link to additional information",
+ "examples": [
+ "https://docs.gitlab.com/ee/development/integrations/secure.html"
+ ]
+ }
+ }
+ },
+ "summary": {
+ "type": "string",
+ "description": "Human readable string containing evidence of the vulnerability.",
+ "examples": [
+ "Credit card 4111111111111111 found",
+ "Server leaked information nginx/1.17.6"
+ ]
+ },
+ "request": {
+ "type": "object",
+ "description": "An HTTP request.",
+ "required": [
+ "headers",
+ "method",
+ "url"
+ ],
+ "properties": {
+ "headers": {
+ "type": "array",
+ "description": "HTTP headers present on the request.",
+ "items": {
+ "type": "object",
+ "required": [
+ "name",
+ "value"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Name of the HTTP header.",
+ "examples": [
+ "Accept",
+ "Content-Length",
+ "Content-Type"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the HTTP header.",
+ "examples": [
+ "*/*",
+ "560",
+ "application/json; charset=utf-8"
+ ]
+ }
+ }
+ }
+ },
+ "method": {
+ "type": "string",
+ "minLength": 1,
+ "description": "HTTP method used in the request.",
+ "examples": [
+ "GET",
+ "POST"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "minLength": 1,
+ "description": "URL of the request.",
+ "examples": [
+ "http://my.site.com/vulnerable-endpoint?show-credit-card"
+ ]
+ },
+ "body": {
+ "type": "string",
+ "description": "Body of the request for display purposes. Body must be suitable for display (not binary), and truncated to a reasonable size.",
+ "examples": [
+ "user=jsmith&first=%27&last=smith"
+ ]
+ }
+ }
+ },
+ "response": {
+ "type": "object",
+ "description": "An HTTP response.",
+ "required": [
+ "headers",
+ "reason_phrase",
+ "status_code"
+ ],
+ "properties": {
+ "headers": {
+ "type": "array",
+ "description": "HTTP headers present on the request.",
+ "items": {
+ "type": "object",
+ "required": [
+ "name",
+ "value"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Name of the HTTP header.",
+ "examples": [
+ "Accept",
+ "Content-Length",
+ "Content-Type"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the HTTP header.",
+ "examples": [
+ "*/*",
+ "560",
+ "application/json; charset=utf-8"
+ ]
+ }
+ }
+ }
+ },
+ "reason_phrase": {
+ "type": "string",
+ "description": "HTTP reason phrase of the response.",
+ "examples": [
+ "OK",
+ "Internal Server Error"
+ ]
+ },
+ "status_code": {
+ "type": "integer",
+ "description": "HTTP status code of the response.",
+ "examples": [
+ 200,
+ 500
+ ]
+ },
+ "body": {
+ "type": "string",
+ "description": "Body of the response for display purposes. Body must be suitable for display (not binary), and truncated to a reasonable size.",
+ "examples": [
+ "{\"user_id\": 2}"
+ ]
+ }
+ }
+ },
+ "supporting_messages": {
+ "type": "array",
+ "description": "Array of supporting http messages.",
+ "items": {
+ "type": "object",
+ "description": "A supporting http message.",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Message display name.",
+ "examples": [
+ "Unmodified",
+ "Recorded"
+ ]
+ },
+ "request": {
+ "type": "object",
+ "description": "An HTTP request.",
+ "required": [
+ "headers",
+ "method",
+ "url"
+ ],
+ "properties": {
+ "headers": {
+ "type": "array",
+ "description": "HTTP headers present on the request.",
+ "items": {
+ "type": "object",
+ "required": [
+ "name",
+ "value"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Name of the HTTP header.",
+ "examples": [
+ "Accept",
+ "Content-Length",
+ "Content-Type"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the HTTP header.",
+ "examples": [
+ "*/*",
+ "560",
+ "application/json; charset=utf-8"
+ ]
+ }
+ }
+ }
+ },
+ "method": {
+ "type": "string",
+ "minLength": 1,
+ "description": "HTTP method used in the request.",
+ "examples": [
+ "GET",
+ "POST"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "minLength": 1,
+ "description": "URL of the request.",
+ "examples": [
+ "http://my.site.com/vulnerable-endpoint?show-credit-card"
+ ]
+ },
+ "body": {
+ "type": "string",
+ "description": "Body of the request for display purposes. Body must be suitable for display (not binary), and truncated to a reasonable size.",
+ "examples": [
+ "user=jsmith&first=%27&last=smith"
+ ]
+ }
+ }
+ },
+ "response": {
+ "type": "object",
+ "description": "An HTTP response.",
+ "required": [
+ "headers",
+ "reason_phrase",
+ "status_code"
+ ],
+ "properties": {
+ "headers": {
+ "type": "array",
+ "description": "HTTP headers present on the request.",
+ "items": {
+ "type": "object",
+ "required": [
+ "name",
+ "value"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Name of the HTTP header.",
+ "examples": [
+ "Accept",
+ "Content-Length",
+ "Content-Type"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the HTTP header.",
+ "examples": [
+ "*/*",
+ "560",
+ "application/json; charset=utf-8"
+ ]
+ }
+ }
+ }
+ },
+ "reason_phrase": {
+ "type": "string",
+ "description": "HTTP reason phrase of the response.",
+ "examples": [
+ "OK",
+ "Internal Server Error"
+ ]
+ },
+ "status_code": {
+ "type": "integer",
+ "description": "HTTP status code of the response.",
+ "examples": [
+ 200,
+ 500
+ ]
+ },
+ "body": {
+ "type": "string",
+ "description": "Body of the response for display purposes. Body must be suitable for display (not binary), and truncated to a reasonable size.",
+ "examples": [
+ "{\"user_id\": 2}"
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "location": {
+ "type": "object",
+ "description": "Identifies the vulnerability's location.",
+ "properties": {
+ "hostname": {
+ "type": "string",
+ "description": "The protocol, domain, and port of the application where the vulnerability was found."
+ },
+ "method": {
+ "type": "string",
+ "description": "The HTTP method that was used to request the URL where the vulnerability was found."
+ },
+ "param": {
+ "type": "string",
+ "description": "A value provided by a vulnerability rule related to the found vulnerability. Examples include a header value, or a parameter used in a HTTP POST."
+ },
+ "path": {
+ "type": "string",
+ "description": "The path of the URL where the vulnerability was found. Typically, this would start with a forward slash."
+ }
+ }
+ },
+ "assets": {
+ "type": "array",
+ "description": "Array of build assets associated with vulnerability.",
+ "items": {
+ "type": "object",
+ "description": "Describes an asset associated with vulnerability.",
+ "required": [
+ "type",
+ "name",
+ "url"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "The type of asset",
+ "enum": [
+ "http_session",
+ "postman"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Display name for asset",
+ "examples": [
+ "HTTP Messages",
+ "Postman Collection"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Link to asset in build artifacts",
+ "examples": [
+ "https://gitlab.com/gitlab-org/security-products/dast/-/jobs/626397001/artifacts/file//output/zap_session.data"
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "remediations": {
+ "type": "array",
+ "description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
+ "items": {
+ "type": "object",
+ "required": [
+ "fixes",
+ "summary",
+ "diff"
+ ],
+ "properties": {
+ "fixes": {
+ "type": "array",
+ "description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
+ "items": {
+ "type": "object",
+ "required": [
+ "id"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ }
+ }
+ }
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1,
+ "description": "An overview of how the vulnerabilities were fixed."
+ },
+ "diff": {
+ "type": "string",
+ "minLength": 1,
+ "description": "A base64-encoded remediation code diff, compatible with git apply."
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/dependency-scanning-report-format.json b/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/dependency-scanning-report-format.json
new file mode 100644
index 00000000000..ade1ce9ea8f
--- /dev/null
+++ b/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/dependency-scanning-report-format.json
@@ -0,0 +1,944 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/raw/master/dist/dependency-scanning-report-format.json",
+ "title": "Report format for GitLab Dependency Scanning",
+ "description": "This schema provides the the report format for Dependency Scanning analyzers (https://docs.gitlab.com/ee/user/application_security/dependency_scanning).",
+ "definitions": {
+ "detail_type": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/named_list"
+ },
+ {
+ "$ref": "#/definitions/list"
+ },
+ {
+ "$ref": "#/definitions/table"
+ },
+ {
+ "$ref": "#/definitions/text"
+ },
+ {
+ "$ref": "#/definitions/url"
+ },
+ {
+ "$ref": "#/definitions/code"
+ },
+ {
+ "$ref": "#/definitions/value"
+ },
+ {
+ "$ref": "#/definitions/diff"
+ },
+ {
+ "$ref": "#/definitions/markdown"
+ },
+ {
+ "$ref": "#/definitions/commit"
+ },
+ {
+ "$ref": "#/definitions/file_location"
+ },
+ {
+ "$ref": "#/definitions/module_location"
+ }
+ ]
+ },
+ "text_value": {
+ "type": "string"
+ },
+ "named_field": {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "$ref": "#/definitions/text_value",
+ "minLength": 1
+ },
+ "description": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "named_list": {
+ "type": "object",
+ "description": "An object with named and typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "named-list"
+ },
+ "items": {
+ "type": "object",
+ "patternProperties": {
+ "^.*$": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/named_field"
+ },
+ {
+ "$ref": "#/definitions/detail_type"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "list": {
+ "type": "object",
+ "description": "A list of typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "list"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ },
+ "table": {
+ "type": "object",
+ "description": "A table of typed fields",
+ "required": [
+ "type",
+ "rows"
+ ],
+ "properties": {
+ "type": {
+ "const": "table"
+ },
+ "header": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ },
+ "rows": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ }
+ },
+ "text": {
+ "type": "object",
+ "description": "Raw text",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "text"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "url": {
+ "type": "object",
+ "description": "A single URL",
+ "required": [
+ "type",
+ "href"
+ ],
+ "properties": {
+ "type": {
+ "const": "url"
+ },
+ "text": {
+ "$ref": "#/definitions/text_value"
+ },
+ "href": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "http://mysite.com"
+ ]
+ }
+ }
+ },
+ "code": {
+ "type": "object",
+ "description": "A codeblock",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "code"
+ },
+ "value": {
+ "type": "string"
+ },
+ "lang": {
+ "type": "string",
+ "description": "A programming language"
+ }
+ }
+ },
+ "value": {
+ "type": "object",
+ "description": "A field that can store a range of types of value",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "value"
+ },
+ "value": {
+ "type": [
+ "number",
+ "string",
+ "boolean"
+ ]
+ }
+ }
+ },
+ "diff": {
+ "type": "object",
+ "description": "A diff",
+ "required": [
+ "type",
+ "before",
+ "after"
+ ],
+ "properties": {
+ "type": {
+ "const": "diff"
+ },
+ "before": {
+ "type": "string"
+ },
+ "after": {
+ "type": "string"
+ }
+ }
+ },
+ "markdown": {
+ "type": "object",
+ "description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "markdown"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value",
+ "examples": [
+ "Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
+ ]
+ }
+ }
+ },
+ "commit": {
+ "type": "object",
+ "description": "A commit/tag/branch within the GitLab project",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "commit"
+ },
+ "value": {
+ "type": "string",
+ "description": "The commit SHA",
+ "minLength": 1
+ }
+ }
+ },
+ "file_location": {
+ "type": "object",
+ "description": "A location within a file in the project",
+ "required": [
+ "type",
+ "file_name",
+ "line_start"
+ ],
+ "properties": {
+ "type": {
+ "const": "file-location"
+ },
+ "file_name": {
+ "type": "string",
+ "minLength": 1
+ },
+ "line_start": {
+ "type": "integer"
+ },
+ "line_end": {
+ "type": "integer"
+ }
+ }
+ },
+ "module_location": {
+ "type": "object",
+ "description": "A location within a binary module of the form module+relative_offset",
+ "required": [
+ "type",
+ "module_name",
+ "offset"
+ ],
+ "properties": {
+ "type": {
+ "const": "module-location"
+ },
+ "module_name": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "compiled_binary"
+ ]
+ },
+ "offset": {
+ "type": "integer",
+ "examples": [
+ 100
+ ]
+ }
+ }
+ }
+ },
+ "self": {
+ "version": "15.0.0"
+ },
+ "required": [
+ "dependency_files",
+ "scan",
+ "version",
+ "vulnerabilities"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "scan": {
+ "type": "object",
+ "required": [
+ "analyzer",
+ "end_time",
+ "scanner",
+ "start_time",
+ "status",
+ "type"
+ ],
+ "properties": {
+ "end_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
+ "examples": [
+ "2020-01-28T03:26:02"
+ ]
+ },
+ "messages": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Communication intended for the initiator of a scan.",
+ "required": [
+ "level",
+ "value"
+ ],
+ "properties": {
+ "level": {
+ "type": "string",
+ "description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
+ "enum": [
+ "info",
+ "warn",
+ "fatal"
+ ],
+ "examples": [
+ "info"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "The message to communicate.",
+ "minLength": 1,
+ "examples": [
+ "Permission denied, scanning aborted"
+ ]
+ }
+ }
+ }
+ },
+ "analyzer": {
+ "type": "object",
+ "description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "gitlab-dast"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the analyzer, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "GitLab DAST"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "pattern": "^https?://.+",
+ "description": "A link to more information about the analyzer.",
+ "examples": [
+ "https://docs.gitlab.com/ee/user/application_security/dast"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the analyzer.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ }
+ }
+ },
+ "scanner": {
+ "type": "object",
+ "description": "Object defining the scanner used to perform the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the scanner.",
+ "minLength": 1,
+ "examples": [
+ "my-sast-scanner"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the scanner, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "My SAST Scanner"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "description": "A link to more information about the scanner.",
+ "examples": [
+ "https://scanner.url"
+ ]
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the scanner.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the scanner.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "start_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
+ "examples": [
+ "2020-02-14T16:01:59"
+ ]
+ },
+ "status": {
+ "type": "string",
+ "description": "Result of the scan.",
+ "enum": [
+ "success",
+ "failure"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "description": "Type of the scan.",
+ "enum": [
+ "dependency_scanning"
+ ]
+ }
+ }
+ },
+ "schema": {
+ "type": "string",
+ "description": "URI pointing to the validating security report schema.",
+ "pattern": "^https?://.+"
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the schema to which the JSON report conforms.",
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
+ },
+ "vulnerabilities": {
+ "type": "array",
+ "description": "Array of vulnerability objects.",
+ "items": {
+ "type": "object",
+ "description": "Describes the vulnerability using GitLab Flavored Markdown",
+ "required": [
+ "id",
+ "identifiers",
+ "location"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 255,
+ "description": "The name of the vulnerability. This must not include the finding's specific information."
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 1048576,
+ "description": "A long text section describing the vulnerability more fully."
+ },
+ "severity": {
+ "type": "string",
+ "description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Info",
+ "Unknown",
+ "Low",
+ "Medium",
+ "High",
+ "Critical"
+ ]
+ },
+ "solution": {
+ "type": "string",
+ "maxLength": 7000,
+ "description": "Explanation of how to fix the vulnerability."
+ },
+ "identifiers": {
+ "type": "array",
+ "minItems": 1,
+ "description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
+ "minLength": 1
+ },
+ "name": {
+ "type": "string",
+ "description": "Human-readable name of the identifier.",
+ "minLength": 1
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the identifier's documentation.",
+ "pattern": "^https?://.+"
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the identifier, for matching purpose.",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "links": {
+ "type": "array",
+ "description": "An array of references to external documentation or articles that describe the vulnerability.",
+ "items": {
+ "type": "object",
+ "required": [
+ "url"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the vulnerability details link."
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the vulnerability details document.",
+ "pattern": "^https?://.+"
+ }
+ }
+ }
+ },
+ "details": {
+ "$ref": "#/definitions/named_list/properties/items"
+ },
+ "tracking": {
+ "description": "Describes how this vulnerability should be tracked as the project changes.",
+ "oneOf": [
+ {
+ "description": "Declares that a series of items should be tracked using source-specific tracking methods.",
+ "required": [
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "source"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "description": "An item that should be tracked using source-specific tracking methods.",
+ "type": "object",
+ "required": [
+ "signatures"
+ ],
+ "properties": {
+ "file": {
+ "type": "string",
+ "description": "Path to the file where the vulnerability is located."
+ },
+ "start_line": {
+ "type": "number",
+ "description": "The first line of the file that includes the vulnerability."
+ },
+ "end_line": {
+ "type": "number",
+ "description": "The last line of the file that includes the vulnerability."
+ },
+ "signatures": {
+ "type": "array",
+ "description": "An array of calculated tracking signatures for this tracking item.",
+ "minItems": 1,
+ "items": {
+ "description": "A calculated tracking signature value and metadata.",
+ "required": [
+ "algorithm",
+ "value"
+ ],
+ "properties": {
+ "algorithm": {
+ "type": "string",
+ "description": "The algorithm used to generate the signature."
+ },
+ "value": {
+ "type": "string",
+ "description": "The result of this signature algorithm."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Each tracking type must declare its own type."
+ }
+ }
+ },
+ "flags": {
+ "description": "Flags that can be attached to vulnerabilities.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Informational flags identified and assigned to a vulnerability.",
+ "required": [
+ "type",
+ "origin",
+ "description"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Result of the scan.",
+ "enum": [
+ "flagged-as-likely-false-positive"
+ ]
+ },
+ "origin": {
+ "minLength": 1,
+ "description": "Tool that issued the flag.",
+ "type": "string"
+ },
+ "description": {
+ "minLength": 1,
+ "description": "What the flag is about.",
+ "type": "string"
+ }
+ }
+ }
+ },
+ "location": {
+ "type": "object",
+ "description": "Identifies the vulnerability's location.",
+ "required": [
+ "file",
+ "dependency"
+ ],
+ "properties": {
+ "file": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Path to the manifest or lock file where the dependency is declared (such as yarn.lock)."
+ },
+ "dependency": {
+ "type": "object",
+ "description": "Describes the dependency of a project where the vulnerability is located.",
+ "required": [
+ "package",
+ "version"
+ ],
+ "properties": {
+ "package": {
+ "type": "object",
+ "description": "Provides information on the package where the vulnerability is located.",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the package where the vulnerability is located."
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "Version of the vulnerable package."
+ },
+ "iid": {
+ "description": "ID that identifies the dependency in the scope of a dependency file.",
+ "type": "number"
+ },
+ "direct": {
+ "type": "boolean",
+ "description": "Tells whether this is a direct, top-level dependency of the scanned project."
+ },
+ "dependency_path": {
+ "type": "array",
+ "description": "Ancestors of the dependency, starting from a direct project dependency, and ending with an immediate parent of the dependency. The dependency itself is excluded from the path. Direct dependencies have no path.",
+ "items": {
+ "type": "object",
+ "required": [
+ "iid"
+ ],
+ "properties": {
+ "iid": {
+ "type": "number",
+ "description": "ID that is unique in the scope of a parent object, and specific to the resource type."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "remediations": {
+ "type": "array",
+ "description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
+ "items": {
+ "type": "object",
+ "required": [
+ "fixes",
+ "summary",
+ "diff"
+ ],
+ "properties": {
+ "fixes": {
+ "type": "array",
+ "description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
+ "items": {
+ "type": "object",
+ "required": [
+ "id"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ }
+ }
+ }
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1,
+ "description": "An overview of how the vulnerabilities were fixed."
+ },
+ "diff": {
+ "type": "string",
+ "minLength": 1,
+ "description": "A base64-encoded remediation code diff, compatible with git apply."
+ }
+ }
+ }
+ },
+ "dependency_files": {
+ "type": "array",
+ "description": "List of dependency files identified in the project.",
+ "items": {
+ "type": "object",
+ "required": [
+ "path",
+ "package_manager",
+ "dependencies"
+ ],
+ "properties": {
+ "path": {
+ "type": "string",
+ "minLength": 1
+ },
+ "package_manager": {
+ "type": "string",
+ "minLength": 1
+ },
+ "dependencies": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Describes the dependency of a project where the vulnerability is located.",
+ "required": [
+ "package",
+ "version"
+ ],
+ "properties": {
+ "package": {
+ "type": "object",
+ "description": "Provides information on the package where the vulnerability is located.",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the package where the vulnerability is located."
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "Version of the vulnerable package."
+ },
+ "iid": {
+ "description": "ID that identifies the dependency in the scope of a dependency file.",
+ "type": "number"
+ },
+ "direct": {
+ "type": "boolean",
+ "description": "Tells whether this is a direct, top-level dependency of the scanned project."
+ },
+ "dependency_path": {
+ "type": "array",
+ "description": "Ancestors of the dependency, starting from a direct project dependency, and ending with an immediate parent of the dependency. The dependency itself is excluded from the path. Direct dependencies have no path.",
+ "items": {
+ "type": "object",
+ "required": [
+ "iid"
+ ],
+ "properties": {
+ "iid": {
+ "type": "number",
+ "description": "ID that is unique in the scope of a parent object, and specific to the resource type."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/sast-report-format.json b/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/sast-report-format.json
new file mode 100644
index 00000000000..9fae45d728e
--- /dev/null
+++ b/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/sast-report-format.json
@@ -0,0 +1,831 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/raw/master/dist/sast-report-format.json",
+ "title": "Report format for GitLab SAST",
+ "description": "This schema provides the report format for Static Application Security Testing analyzers (https://docs.gitlab.com/ee/user/application_security/sast).",
+ "definitions": {
+ "detail_type": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/named_list"
+ },
+ {
+ "$ref": "#/definitions/list"
+ },
+ {
+ "$ref": "#/definitions/table"
+ },
+ {
+ "$ref": "#/definitions/text"
+ },
+ {
+ "$ref": "#/definitions/url"
+ },
+ {
+ "$ref": "#/definitions/code"
+ },
+ {
+ "$ref": "#/definitions/value"
+ },
+ {
+ "$ref": "#/definitions/diff"
+ },
+ {
+ "$ref": "#/definitions/markdown"
+ },
+ {
+ "$ref": "#/definitions/commit"
+ },
+ {
+ "$ref": "#/definitions/file_location"
+ },
+ {
+ "$ref": "#/definitions/module_location"
+ }
+ ]
+ },
+ "text_value": {
+ "type": "string"
+ },
+ "named_field": {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "$ref": "#/definitions/text_value",
+ "minLength": 1
+ },
+ "description": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "named_list": {
+ "type": "object",
+ "description": "An object with named and typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "named-list"
+ },
+ "items": {
+ "type": "object",
+ "patternProperties": {
+ "^.*$": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/named_field"
+ },
+ {
+ "$ref": "#/definitions/detail_type"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "list": {
+ "type": "object",
+ "description": "A list of typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "list"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ },
+ "table": {
+ "type": "object",
+ "description": "A table of typed fields",
+ "required": [
+ "type",
+ "rows"
+ ],
+ "properties": {
+ "type": {
+ "const": "table"
+ },
+ "header": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ },
+ "rows": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ }
+ },
+ "text": {
+ "type": "object",
+ "description": "Raw text",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "text"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "url": {
+ "type": "object",
+ "description": "A single URL",
+ "required": [
+ "type",
+ "href"
+ ],
+ "properties": {
+ "type": {
+ "const": "url"
+ },
+ "text": {
+ "$ref": "#/definitions/text_value"
+ },
+ "href": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "http://mysite.com"
+ ]
+ }
+ }
+ },
+ "code": {
+ "type": "object",
+ "description": "A codeblock",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "code"
+ },
+ "value": {
+ "type": "string"
+ },
+ "lang": {
+ "type": "string",
+ "description": "A programming language"
+ }
+ }
+ },
+ "value": {
+ "type": "object",
+ "description": "A field that can store a range of types of value",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "value"
+ },
+ "value": {
+ "type": [
+ "number",
+ "string",
+ "boolean"
+ ]
+ }
+ }
+ },
+ "diff": {
+ "type": "object",
+ "description": "A diff",
+ "required": [
+ "type",
+ "before",
+ "after"
+ ],
+ "properties": {
+ "type": {
+ "const": "diff"
+ },
+ "before": {
+ "type": "string"
+ },
+ "after": {
+ "type": "string"
+ }
+ }
+ },
+ "markdown": {
+ "type": "object",
+ "description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "markdown"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value",
+ "examples": [
+ "Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
+ ]
+ }
+ }
+ },
+ "commit": {
+ "type": "object",
+ "description": "A commit/tag/branch within the GitLab project",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "commit"
+ },
+ "value": {
+ "type": "string",
+ "description": "The commit SHA",
+ "minLength": 1
+ }
+ }
+ },
+ "file_location": {
+ "type": "object",
+ "description": "A location within a file in the project",
+ "required": [
+ "type",
+ "file_name",
+ "line_start"
+ ],
+ "properties": {
+ "type": {
+ "const": "file-location"
+ },
+ "file_name": {
+ "type": "string",
+ "minLength": 1
+ },
+ "line_start": {
+ "type": "integer"
+ },
+ "line_end": {
+ "type": "integer"
+ }
+ }
+ },
+ "module_location": {
+ "type": "object",
+ "description": "A location within a binary module of the form module+relative_offset",
+ "required": [
+ "type",
+ "module_name",
+ "offset"
+ ],
+ "properties": {
+ "type": {
+ "const": "module-location"
+ },
+ "module_name": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "compiled_binary"
+ ]
+ },
+ "offset": {
+ "type": "integer",
+ "examples": [
+ 100
+ ]
+ }
+ }
+ }
+ },
+ "self": {
+ "version": "15.0.0"
+ },
+ "required": [
+ "scan",
+ "version",
+ "vulnerabilities"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "scan": {
+ "type": "object",
+ "required": [
+ "analyzer",
+ "end_time",
+ "scanner",
+ "start_time",
+ "status",
+ "type"
+ ],
+ "properties": {
+ "end_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
+ "examples": [
+ "2020-01-28T03:26:02"
+ ]
+ },
+ "messages": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Communication intended for the initiator of a scan.",
+ "required": [
+ "level",
+ "value"
+ ],
+ "properties": {
+ "level": {
+ "type": "string",
+ "description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
+ "enum": [
+ "info",
+ "warn",
+ "fatal"
+ ],
+ "examples": [
+ "info"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "The message to communicate.",
+ "minLength": 1,
+ "examples": [
+ "Permission denied, scanning aborted"
+ ]
+ }
+ }
+ }
+ },
+ "analyzer": {
+ "type": "object",
+ "description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "gitlab-dast"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the analyzer, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "GitLab DAST"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "pattern": "^https?://.+",
+ "description": "A link to more information about the analyzer.",
+ "examples": [
+ "https://docs.gitlab.com/ee/user/application_security/dast"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the analyzer.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ }
+ }
+ },
+ "scanner": {
+ "type": "object",
+ "description": "Object defining the scanner used to perform the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the scanner.",
+ "minLength": 1,
+ "examples": [
+ "my-sast-scanner"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the scanner, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "My SAST Scanner"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "description": "A link to more information about the scanner.",
+ "examples": [
+ "https://scanner.url"
+ ]
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the scanner.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the scanner.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "start_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
+ "examples": [
+ "2020-02-14T16:01:59"
+ ]
+ },
+ "status": {
+ "type": "string",
+ "description": "Result of the scan.",
+ "enum": [
+ "success",
+ "failure"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "description": "Type of the scan.",
+ "enum": [
+ "sast"
+ ]
+ }
+ }
+ },
+ "schema": {
+ "type": "string",
+ "description": "URI pointing to the validating security report schema.",
+ "pattern": "^https?://.+"
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the schema to which the JSON report conforms.",
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
+ },
+ "vulnerabilities": {
+ "type": "array",
+ "description": "Array of vulnerability objects.",
+ "items": {
+ "type": "object",
+ "description": "Describes the vulnerability using GitLab Flavored Markdown",
+ "required": [
+ "id",
+ "identifiers",
+ "location"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 255,
+ "description": "The name of the vulnerability. This must not include the finding's specific information."
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 1048576,
+ "description": "A long text section describing the vulnerability more fully."
+ },
+ "severity": {
+ "type": "string",
+ "description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Info",
+ "Unknown",
+ "Low",
+ "Medium",
+ "High",
+ "Critical"
+ ]
+ },
+ "solution": {
+ "type": "string",
+ "maxLength": 7000,
+ "description": "Explanation of how to fix the vulnerability."
+ },
+ "identifiers": {
+ "type": "array",
+ "minItems": 1,
+ "description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
+ "minLength": 1
+ },
+ "name": {
+ "type": "string",
+ "description": "Human-readable name of the identifier.",
+ "minLength": 1
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the identifier's documentation.",
+ "pattern": "^https?://.+"
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the identifier, for matching purpose.",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "links": {
+ "type": "array",
+ "description": "An array of references to external documentation or articles that describe the vulnerability.",
+ "items": {
+ "type": "object",
+ "required": [
+ "url"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the vulnerability details link."
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the vulnerability details document.",
+ "pattern": "^https?://.+"
+ }
+ }
+ }
+ },
+ "details": {
+ "$ref": "#/definitions/named_list/properties/items"
+ },
+ "tracking": {
+ "description": "Describes how this vulnerability should be tracked as the project changes.",
+ "oneOf": [
+ {
+ "description": "Declares that a series of items should be tracked using source-specific tracking methods.",
+ "required": [
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "source"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "description": "An item that should be tracked using source-specific tracking methods.",
+ "type": "object",
+ "required": [
+ "signatures"
+ ],
+ "properties": {
+ "file": {
+ "type": "string",
+ "description": "Path to the file where the vulnerability is located."
+ },
+ "start_line": {
+ "type": "number",
+ "description": "The first line of the file that includes the vulnerability."
+ },
+ "end_line": {
+ "type": "number",
+ "description": "The last line of the file that includes the vulnerability."
+ },
+ "signatures": {
+ "type": "array",
+ "description": "An array of calculated tracking signatures for this tracking item.",
+ "minItems": 1,
+ "items": {
+ "description": "A calculated tracking signature value and metadata.",
+ "required": [
+ "algorithm",
+ "value"
+ ],
+ "properties": {
+ "algorithm": {
+ "type": "string",
+ "description": "The algorithm used to generate the signature."
+ },
+ "value": {
+ "type": "string",
+ "description": "The result of this signature algorithm."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Each tracking type must declare its own type."
+ }
+ }
+ },
+ "flags": {
+ "description": "Flags that can be attached to vulnerabilities.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Informational flags identified and assigned to a vulnerability.",
+ "required": [
+ "type",
+ "origin",
+ "description"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Result of the scan.",
+ "enum": [
+ "flagged-as-likely-false-positive"
+ ]
+ },
+ "origin": {
+ "minLength": 1,
+ "description": "Tool that issued the flag.",
+ "type": "string"
+ },
+ "description": {
+ "minLength": 1,
+ "description": "What the flag is about.",
+ "type": "string"
+ }
+ }
+ }
+ },
+ "location": {
+ "type": "object",
+ "description": "Identifies the vulnerability's location.",
+ "properties": {
+ "file": {
+ "type": "string",
+ "description": "Path to the file where the vulnerability is located."
+ },
+ "start_line": {
+ "type": "number",
+ "description": "The first line of the code affected by the vulnerability."
+ },
+ "end_line": {
+ "type": "number",
+ "description": "The last line of the code affected by the vulnerability."
+ },
+ "class": {
+ "type": "string",
+ "description": "Provides the name of the class where the vulnerability is located."
+ },
+ "method": {
+ "type": "string",
+ "description": "Provides the name of the method where the vulnerability is located."
+ }
+ }
+ },
+ "raw_source_code_extract": {
+ "type": "string",
+ "description": "Provides an unsanitized excerpt of the affected source code."
+ }
+ }
+ }
+ },
+ "remediations": {
+ "type": "array",
+ "description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
+ "items": {
+ "type": "object",
+ "required": [
+ "fixes",
+ "summary",
+ "diff"
+ ],
+ "properties": {
+ "fixes": {
+ "type": "array",
+ "description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
+ "items": {
+ "type": "object",
+ "required": [
+ "id"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ }
+ }
+ }
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1,
+ "description": "An overview of how the vulnerabilities were fixed."
+ },
+ "diff": {
+ "type": "string",
+ "minLength": 1,
+ "description": "A base64-encoded remediation code diff, compatible with git apply."
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/secret-detection-report-format.json b/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/secret-detection-report-format.json
new file mode 100644
index 00000000000..fca00e17f26
--- /dev/null
+++ b/lib/gitlab/ci/parsers/security/validators/schemas/15.0.0/secret-detection-report-format.json
@@ -0,0 +1,854 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/raw/master/dist/secret-detection-report-format.json",
+ "title": "Report format for GitLab Secret Detection",
+ "description": "This schema provides the the report format for the Secret Detection analyzer (https://docs.gitlab.com/ee/user/application_security/secret_detection)",
+ "definitions": {
+ "detail_type": {
+ "oneOf": [
+ {
+ "$ref": "#/definitions/named_list"
+ },
+ {
+ "$ref": "#/definitions/list"
+ },
+ {
+ "$ref": "#/definitions/table"
+ },
+ {
+ "$ref": "#/definitions/text"
+ },
+ {
+ "$ref": "#/definitions/url"
+ },
+ {
+ "$ref": "#/definitions/code"
+ },
+ {
+ "$ref": "#/definitions/value"
+ },
+ {
+ "$ref": "#/definitions/diff"
+ },
+ {
+ "$ref": "#/definitions/markdown"
+ },
+ {
+ "$ref": "#/definitions/commit"
+ },
+ {
+ "$ref": "#/definitions/file_location"
+ },
+ {
+ "$ref": "#/definitions/module_location"
+ }
+ ]
+ },
+ "text_value": {
+ "type": "string"
+ },
+ "named_field": {
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "$ref": "#/definitions/text_value",
+ "minLength": 1
+ },
+ "description": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "named_list": {
+ "type": "object",
+ "description": "An object with named and typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "named-list"
+ },
+ "items": {
+ "type": "object",
+ "patternProperties": {
+ "^.*$": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/named_field"
+ },
+ {
+ "$ref": "#/definitions/detail_type"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "list": {
+ "type": "object",
+ "description": "A list of typed fields",
+ "required": [
+ "type",
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "list"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ },
+ "table": {
+ "type": "object",
+ "description": "A table of typed fields",
+ "required": [
+ "type",
+ "rows"
+ ],
+ "properties": {
+ "type": {
+ "const": "table"
+ },
+ "header": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ },
+ "rows": {
+ "type": "array",
+ "items": {
+ "type": "array",
+ "items": {
+ "$ref": "#/definitions/detail_type"
+ }
+ }
+ }
+ }
+ },
+ "text": {
+ "type": "object",
+ "description": "Raw text",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "text"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value"
+ }
+ }
+ },
+ "url": {
+ "type": "object",
+ "description": "A single URL",
+ "required": [
+ "type",
+ "href"
+ ],
+ "properties": {
+ "type": {
+ "const": "url"
+ },
+ "text": {
+ "$ref": "#/definitions/text_value"
+ },
+ "href": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "http://mysite.com"
+ ]
+ }
+ }
+ },
+ "code": {
+ "type": "object",
+ "description": "A codeblock",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "code"
+ },
+ "value": {
+ "type": "string"
+ },
+ "lang": {
+ "type": "string",
+ "description": "A programming language"
+ }
+ }
+ },
+ "value": {
+ "type": "object",
+ "description": "A field that can store a range of types of value",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "value"
+ },
+ "value": {
+ "type": [
+ "number",
+ "string",
+ "boolean"
+ ]
+ }
+ }
+ },
+ "diff": {
+ "type": "object",
+ "description": "A diff",
+ "required": [
+ "type",
+ "before",
+ "after"
+ ],
+ "properties": {
+ "type": {
+ "const": "diff"
+ },
+ "before": {
+ "type": "string"
+ },
+ "after": {
+ "type": "string"
+ }
+ }
+ },
+ "markdown": {
+ "type": "object",
+ "description": "GitLab flavoured markdown, see https://docs.gitlab.com/ee/user/markdown.html",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "markdown"
+ },
+ "value": {
+ "$ref": "#/definitions/text_value",
+ "examples": [
+ "Here is markdown `inline code` #1 [test](gitlab.com)\n\n![GitLab Logo](https://about.gitlab.com/images/press/logo/preview/gitlab-logo-white-preview.png)"
+ ]
+ }
+ }
+ },
+ "commit": {
+ "type": "object",
+ "description": "A commit/tag/branch within the GitLab project",
+ "required": [
+ "type",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "const": "commit"
+ },
+ "value": {
+ "type": "string",
+ "description": "The commit SHA",
+ "minLength": 1
+ }
+ }
+ },
+ "file_location": {
+ "type": "object",
+ "description": "A location within a file in the project",
+ "required": [
+ "type",
+ "file_name",
+ "line_start"
+ ],
+ "properties": {
+ "type": {
+ "const": "file-location"
+ },
+ "file_name": {
+ "type": "string",
+ "minLength": 1
+ },
+ "line_start": {
+ "type": "integer"
+ },
+ "line_end": {
+ "type": "integer"
+ }
+ }
+ },
+ "module_location": {
+ "type": "object",
+ "description": "A location within a binary module of the form module+relative_offset",
+ "required": [
+ "type",
+ "module_name",
+ "offset"
+ ],
+ "properties": {
+ "type": {
+ "const": "module-location"
+ },
+ "module_name": {
+ "type": "string",
+ "minLength": 1,
+ "examples": [
+ "compiled_binary"
+ ]
+ },
+ "offset": {
+ "type": "integer",
+ "examples": [
+ 100
+ ]
+ }
+ }
+ }
+ },
+ "self": {
+ "version": "15.0.0"
+ },
+ "required": [
+ "scan",
+ "version",
+ "vulnerabilities"
+ ],
+ "additionalProperties": true,
+ "properties": {
+ "scan": {
+ "type": "object",
+ "required": [
+ "analyzer",
+ "end_time",
+ "scanner",
+ "start_time",
+ "status",
+ "type"
+ ],
+ "properties": {
+ "end_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan finished.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
+ "examples": [
+ "2020-01-28T03:26:02"
+ ]
+ },
+ "messages": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Communication intended for the initiator of a scan.",
+ "required": [
+ "level",
+ "value"
+ ],
+ "properties": {
+ "level": {
+ "type": "string",
+ "description": "Describes the severity of the communication. Use info to communicate normal scan behaviour; warn to communicate a potentially recoverable problem, or a partial error; fatal to communicate an issue that causes the scan to halt.",
+ "enum": [
+ "info",
+ "warn",
+ "fatal"
+ ],
+ "examples": [
+ "info"
+ ]
+ },
+ "value": {
+ "type": "string",
+ "description": "The message to communicate.",
+ "minLength": 1,
+ "examples": [
+ "Permission denied, scanning aborted"
+ ]
+ }
+ }
+ }
+ },
+ "analyzer": {
+ "type": "object",
+ "description": "Object defining the analyzer used to perform the scan. Analyzers typically delegate to an underlying scanner to run the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "gitlab-dast"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the analyzer, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "GitLab DAST"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "pattern": "^https?://.+",
+ "description": "A link to more information about the analyzer.",
+ "examples": [
+ "https://docs.gitlab.com/ee/user/application_security/dast"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the analyzer.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the analyzer.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ }
+ }
+ },
+ "scanner": {
+ "type": "object",
+ "description": "Object defining the scanner used to perform the scan.",
+ "required": [
+ "id",
+ "name",
+ "version",
+ "vendor"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "description": "Unique id that identifies the scanner.",
+ "minLength": 1,
+ "examples": [
+ "my-sast-scanner"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "description": "A human readable value that identifies the scanner, not required to be unique.",
+ "minLength": 1,
+ "examples": [
+ "My SAST Scanner"
+ ]
+ },
+ "url": {
+ "type": "string",
+ "description": "A link to more information about the scanner.",
+ "examples": [
+ "https://scanner.url"
+ ]
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the scanner.",
+ "minLength": 1,
+ "examples": [
+ "1.0.2"
+ ]
+ },
+ "vendor": {
+ "description": "The vendor/maintainer of the scanner.",
+ "type": "object",
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The name of the vendor.",
+ "minLength": 1,
+ "examples": [
+ "GitLab"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "start_time": {
+ "type": "string",
+ "description": "ISO8601 UTC value with format yyyy-mm-ddThh:mm:ss, representing when the scan started.",
+ "pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$",
+ "examples": [
+ "2020-02-14T16:01:59"
+ ]
+ },
+ "status": {
+ "type": "string",
+ "description": "Result of the scan.",
+ "enum": [
+ "success",
+ "failure"
+ ]
+ },
+ "type": {
+ "type": "string",
+ "description": "Type of the scan.",
+ "enum": [
+ "secret_detection"
+ ]
+ }
+ }
+ },
+ "schema": {
+ "type": "string",
+ "description": "URI pointing to the validating security report schema.",
+ "pattern": "^https?://.+"
+ },
+ "version": {
+ "type": "string",
+ "description": "The version of the schema to which the JSON report conforms.",
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
+ },
+ "vulnerabilities": {
+ "type": "array",
+ "description": "Array of vulnerability objects.",
+ "items": {
+ "type": "object",
+ "description": "Describes the vulnerability using GitLab Flavored Markdown",
+ "required": [
+ "id",
+ "identifiers",
+ "location"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "maxLength": 255,
+ "description": "The name of the vulnerability. This must not include the finding's specific information."
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 1048576,
+ "description": "A long text section describing the vulnerability more fully."
+ },
+ "severity": {
+ "type": "string",
+ "description": "How much the vulnerability impacts the software. Possible values are Info, Unknown, Low, Medium, High, or Critical. Note that some analyzers may not report all these possible values.",
+ "enum": [
+ "Info",
+ "Unknown",
+ "Low",
+ "Medium",
+ "High",
+ "Critical"
+ ]
+ },
+ "solution": {
+ "type": "string",
+ "maxLength": 7000,
+ "description": "Explanation of how to fix the vulnerability."
+ },
+ "identifiers": {
+ "type": "array",
+ "minItems": 1,
+ "description": "An ordered array of references that identify a vulnerability on internal or external databases. The first identifier is the Primary Identifier, which has special meaning.",
+ "items": {
+ "type": "object",
+ "required": [
+ "type",
+ "name",
+ "value"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "for example, cve, cwe, osvdb, usn, or an analyzer-dependent type such as gemnasium).",
+ "minLength": 1
+ },
+ "name": {
+ "type": "string",
+ "description": "Human-readable name of the identifier.",
+ "minLength": 1
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the identifier's documentation.",
+ "pattern": "^https?://.+"
+ },
+ "value": {
+ "type": "string",
+ "description": "Value of the identifier, for matching purpose.",
+ "minLength": 1
+ }
+ }
+ }
+ },
+ "links": {
+ "type": "array",
+ "description": "An array of references to external documentation or articles that describe the vulnerability.",
+ "items": {
+ "type": "object",
+ "required": [
+ "url"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the vulnerability details link."
+ },
+ "url": {
+ "type": "string",
+ "description": "URL of the vulnerability details document.",
+ "pattern": "^https?://.+"
+ }
+ }
+ }
+ },
+ "details": {
+ "$ref": "#/definitions/named_list/properties/items"
+ },
+ "tracking": {
+ "description": "Describes how this vulnerability should be tracked as the project changes.",
+ "oneOf": [
+ {
+ "description": "Declares that a series of items should be tracked using source-specific tracking methods.",
+ "required": [
+ "items"
+ ],
+ "properties": {
+ "type": {
+ "const": "source"
+ },
+ "items": {
+ "type": "array",
+ "items": {
+ "description": "An item that should be tracked using source-specific tracking methods.",
+ "type": "object",
+ "required": [
+ "signatures"
+ ],
+ "properties": {
+ "file": {
+ "type": "string",
+ "description": "Path to the file where the vulnerability is located."
+ },
+ "start_line": {
+ "type": "number",
+ "description": "The first line of the file that includes the vulnerability."
+ },
+ "end_line": {
+ "type": "number",
+ "description": "The last line of the file that includes the vulnerability."
+ },
+ "signatures": {
+ "type": "array",
+ "description": "An array of calculated tracking signatures for this tracking item.",
+ "minItems": 1,
+ "items": {
+ "description": "A calculated tracking signature value and metadata.",
+ "required": [
+ "algorithm",
+ "value"
+ ],
+ "properties": {
+ "algorithm": {
+ "type": "string",
+ "description": "The algorithm used to generate the signature."
+ },
+ "value": {
+ "type": "string",
+ "description": "The result of this signature algorithm."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "description": "Each tracking type must declare its own type."
+ }
+ }
+ },
+ "flags": {
+ "description": "Flags that can be attached to vulnerabilities.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "description": "Informational flags identified and assigned to a vulnerability.",
+ "required": [
+ "type",
+ "origin",
+ "description"
+ ],
+ "properties": {
+ "type": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Result of the scan.",
+ "enum": [
+ "flagged-as-likely-false-positive"
+ ]
+ },
+ "origin": {
+ "minLength": 1,
+ "description": "Tool that issued the flag.",
+ "type": "string"
+ },
+ "description": {
+ "minLength": 1,
+ "description": "What the flag is about.",
+ "type": "string"
+ }
+ }
+ }
+ },
+ "location": {
+ "required": [
+ "commit"
+ ],
+ "properties": {
+ "file": {
+ "type": "string",
+ "description": "Path to the file where the vulnerability is located"
+ },
+ "commit": {
+ "type": "object",
+ "description": "Represents the commit in which the vulnerability was detected",
+ "required": [
+ "sha"
+ ],
+ "properties": {
+ "author": {
+ "type": "string"
+ },
+ "date": {
+ "type": "string"
+ },
+ "message": {
+ "type": "string"
+ },
+ "sha": {
+ "type": "string",
+ "minLength": 1
+ }
+ }
+ },
+ "start_line": {
+ "type": "number",
+ "description": "The first line of the code affected by the vulnerability"
+ },
+ "end_line": {
+ "type": "number",
+ "description": "The last line of the code affected by the vulnerability"
+ },
+ "class": {
+ "type": "string",
+ "description": "Provides the name of the class where the vulnerability is located"
+ },
+ "method": {
+ "type": "string",
+ "description": "Provides the name of the method where the vulnerability is located"
+ }
+ }
+ },
+ "raw_source_code_extract": {
+ "type": "string",
+ "description": "Provides an unsanitized excerpt of the affected source code."
+ }
+ }
+ }
+ },
+ "remediations": {
+ "type": "array",
+ "description": "An array of objects containing information on available remediations, along with patch diffs to apply.",
+ "items": {
+ "type": "object",
+ "required": [
+ "fixes",
+ "summary",
+ "diff"
+ ],
+ "properties": {
+ "fixes": {
+ "type": "array",
+ "description": "An array of strings that represent references to vulnerabilities fixed by this remediation.",
+ "items": {
+ "type": "object",
+ "required": [
+ "id"
+ ],
+ "properties": {
+ "id": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Unique identifier of the vulnerability. This is recommended to be a UUID.",
+ "examples": [
+ "642735a5-1425-428d-8d4e-3c854885a3c9"
+ ]
+ }
+ }
+ }
+ },
+ "summary": {
+ "type": "string",
+ "minLength": 1,
+ "description": "An overview of how the vulnerabilities were fixed."
+ },
+ "diff": {
+ "type": "string",
+ "minLength": 1,
+ "description": "A base64-encoded remediation code diff, compatible with git apply."
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/lib/gitlab/ci/parsers/test/junit.rb b/lib/gitlab/ci/parsers/test/junit.rb
index 999ffff85d2..d95ecff85cd 100644
--- a/lib/gitlab/ci/parsers/test/junit.rb
+++ b/lib/gitlab/ci/parsers/test/junit.rb
@@ -8,7 +8,9 @@ module Gitlab
JunitParserError = Class.new(Gitlab::Ci::Parsers::ParserError)
ATTACHMENT_TAG_REGEX = /\[\[ATTACHMENT\|(?<path>.+?)\]\]/.freeze
- def parse!(xml_data, test_suite, job:)
+ def parse!(xml_data, test_report, job:)
+ test_suite = test_report.get_suite(job.test_suite_name)
+
root = Hash.from_xml(xml_data)
total_parsed = 0
max_test_cases = job.max_test_cases_per_report
diff --git a/lib/gitlab/ci/pipeline/chain/assign_partition.rb b/lib/gitlab/ci/pipeline/chain/assign_partition.rb
new file mode 100644
index 00000000000..4b8efe13d44
--- /dev/null
+++ b/lib/gitlab/ci/pipeline/chain/assign_partition.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Pipeline
+ module Chain
+ class AssignPartition < Chain::Base
+ include Chain::Helpers
+
+ def perform!
+ @pipeline.partition_id = find_partition_id
+ end
+
+ def break?
+ @pipeline.errors.any?
+ end
+
+ private
+
+ def find_partition_id
+ if @command.creates_child_pipeline?
+ @command.parent_pipeline_partition_id
+ else
+ ::Ci::Pipeline.current_partition_value
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/pipeline/chain/command.rb b/lib/gitlab/ci/pipeline/chain/command.rb
index 0a6f6fd740c..14c320f77bf 100644
--- a/lib/gitlab/ci/pipeline/chain/command.rb
+++ b/lib/gitlab/ci/pipeline/chain/command.rb
@@ -80,6 +80,10 @@ module Gitlab
bridge&.parent_pipeline
end
+ def parent_pipeline_partition_id
+ parent_pipeline.partition_id if creates_child_pipeline?
+ end
+
def creates_child_pipeline?
bridge&.triggers_child_pipeline?
end
@@ -117,8 +121,14 @@ module Gitlab
end
def observe_jobs_count_in_alive_pipelines
+ jobs_count = if Feature.enabled?(:ci_limit_active_jobs_early, project)
+ project.all_pipelines.jobs_count_in_alive_pipelines
+ else
+ project.all_pipelines.builds_count_in_alive_pipelines
+ end
+
metrics.active_jobs_histogram
- .observe({ plan: project.actual_plan_name }, project.all_pipelines.jobs_count_in_alive_pipelines)
+ .observe({ plan: project.actual_plan_name }, jobs_count)
end
def increment_pipeline_failure_reason_counter(reason)
diff --git a/lib/gitlab/ci/pipeline/chain/config/content.rb b/lib/gitlab/ci/pipeline/chain/config/content.rb
index 3c150ca26bb..a14dec48619 100644
--- a/lib/gitlab/ci/pipeline/chain/config/content.rb
+++ b/lib/gitlab/ci/pipeline/chain/config/content.rb
@@ -7,6 +7,7 @@ module Gitlab
module Config
class Content < Chain::Base
include Chain::Helpers
+ include ::Gitlab::Utils::StrongMemoize
SOURCES = [
Gitlab::Ci::Pipeline::Chain::Config::Content::Parameter,
@@ -18,10 +19,10 @@ module Gitlab
].freeze
def perform!
- if config = find_config
- @pipeline.build_pipeline_config(content: config.content)
- @command.config_content = config.content
- @pipeline.config_source = config.source
+ if pipeline_config&.exists?
+ @pipeline.build_pipeline_config(content: pipeline_config.content)
+ @command.config_content = pipeline_config.content
+ @pipeline.config_source = pipeline_config.source
else
error('Missing CI config file')
end
@@ -33,7 +34,19 @@ module Gitlab
private
- def find_config
+ def pipeline_config
+ strong_memoize(:pipeline_config) do
+ next legacy_find_config if ::Feature.disabled?(:ci_project_pipeline_config_refactoring, project)
+
+ ::Gitlab::Ci::ProjectConfig.new(
+ project: project, sha: @pipeline.sha,
+ custom_content: @command.content,
+ pipeline_source: @command.source, pipeline_source_bridge: @command.bridge
+ )
+ end
+ end
+
+ def legacy_find_config
sources.each do |source|
config = source.new(@pipeline, @command)
return config if config.exists?
diff --git a/lib/gitlab/ci/pipeline/chain/config/content/source.rb b/lib/gitlab/ci/pipeline/chain/config/content/source.rb
index 8bc172f93d3..69dca1568b6 100644
--- a/lib/gitlab/ci/pipeline/chain/config/content/source.rb
+++ b/lib/gitlab/ci/pipeline/chain/config/content/source.rb
@@ -6,6 +6,7 @@ module Gitlab
module Chain
module Config
class Content
+ # When removing ci_project_pipeline_config_refactoring, this and its subclasses will be removed.
class Source
include Gitlab::Utils::StrongMemoize
diff --git a/lib/gitlab/ci/pipeline/chain/ensure_environments.rb b/lib/gitlab/ci/pipeline/chain/ensure_environments.rb
index 245ef32f06b..3dd9b85d9b2 100644
--- a/lib/gitlab/ci/pipeline/chain/ensure_environments.rb
+++ b/lib/gitlab/ci/pipeline/chain/ensure_environments.rb
@@ -18,7 +18,9 @@ module Gitlab
def ensure_environment(build)
return unless build.instance_of?(::Ci::Build) && build.has_environment?
- environment = ::Gitlab::Ci::Pipeline::Seed::Environment.new(build).to_resource
+ environment = ::Gitlab::Ci::Pipeline::Seed::Environment
+ .new(build, merge_request: @command.merge_request)
+ .to_resource
if environment.persisted?
build.persisted_environment = environment
diff --git a/lib/gitlab/ci/pipeline/chain/validate/external.rb b/lib/gitlab/ci/pipeline/chain/validate/external.rb
index 6e95c7988fc..915e48828d2 100644
--- a/lib/gitlab/ci/pipeline/chain/validate/external.rb
+++ b/lib/gitlab/ci/pipeline/chain/validate/external.rb
@@ -57,7 +57,8 @@ module Gitlab
}.compact
Gitlab::HTTP.post(
- validation_service_url, timeout: validation_service_timeout,
+ validation_service_url,
+ timeout: validation_service_timeout,
headers: headers,
body: validation_service_payload.to_json
)
@@ -96,13 +97,17 @@ module Gitlab
last_sign_in_ip: current_user.last_sign_in_ip,
sign_in_count: current_user.sign_in_count
},
+ credit_card: {
+ similar_cards_count: current_user.credit_card_validation&.similar_records&.count.to_i,
+ similar_holder_names_count: current_user.credit_card_validation&.similar_holder_names_count.to_i
+ },
pipeline: {
sha: pipeline.sha,
ref: pipeline.ref,
type: pipeline.source
},
builds: builds_validation_payload,
- total_builds_count: current_user.pipelines.jobs_count_in_alive_pipelines
+ total_builds_count: current_user.pipelines.builds_count_in_alive_pipelines
}
end
diff --git a/lib/gitlab/ci/pipeline/seed/build.rb b/lib/gitlab/ci/pipeline/seed/build.rb
index 93106b96af2..2e4267e986b 100644
--- a/lib/gitlab/ci/pipeline/seed/build.rb
+++ b/lib/gitlab/ci/pipeline/seed/build.rb
@@ -148,7 +148,9 @@ module Gitlab
ref: @pipeline.ref,
tag: @pipeline.tag,
trigger_request: @pipeline.legacy_trigger,
- protected: @pipeline.protected_ref?
+ protected: @pipeline.protected_ref?,
+ partition_id: @pipeline.partition_id,
+ metadata_attributes: { partition_id: @pipeline.partition_id }
}
end
diff --git a/lib/gitlab/ci/pipeline/seed/environment.rb b/lib/gitlab/ci/pipeline/seed/environment.rb
index 6bcc71a808b..8353bc523bf 100644
--- a/lib/gitlab/ci/pipeline/seed/environment.rb
+++ b/lib/gitlab/ci/pipeline/seed/environment.rb
@@ -5,17 +5,21 @@ module Gitlab
module Pipeline
module Seed
class Environment < Seed::Base
- attr_reader :job
+ attr_reader :job, :merge_request
- def initialize(job)
+ delegate :simple_variables, to: :job
+
+ def initialize(job, merge_request: nil)
@job = job
+ @merge_request = merge_request
end
def to_resource
environments.safe_find_or_create_by(name: expanded_environment_name) do |environment|
# Initialize the attributes at creation
- environment.auto_stop_in = auto_stop_in
+ environment.auto_stop_in = expanded_auto_stop_in
environment.tier = deployment_tier
+ environment.merge_request = merge_request
end
end
@@ -36,6 +40,12 @@ module Gitlab
def expanded_environment_name
job.expanded_environment_name
end
+
+ def expanded_auto_stop_in
+ return unless auto_stop_in
+
+ ExpandVariables.expand(auto_stop_in, -> { simple_variables.sort_and_expand_all })
+ end
end
end
end
diff --git a/lib/gitlab/ci/pipeline/seed/stage.rb b/lib/gitlab/ci/pipeline/seed/stage.rb
index 7cf6466cf4b..1c4247bd5ee 100644
--- a/lib/gitlab/ci/pipeline/seed/stage.rb
+++ b/lib/gitlab/ci/pipeline/seed/stage.rb
@@ -25,7 +25,8 @@ module Gitlab
{ name: @attributes.fetch(:name),
position: @attributes.fetch(:index),
pipeline: @pipeline,
- project: @pipeline.project }
+ project: @pipeline.project,
+ partition_id: @pipeline.partition_id }
end
def seeds
diff --git a/lib/gitlab/ci/processable_object_hierarchy.rb b/lib/gitlab/ci/processable_object_hierarchy.rb
new file mode 100644
index 00000000000..1122361e27e
--- /dev/null
+++ b/lib/gitlab/ci/processable_object_hierarchy.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class ProcessableObjectHierarchy < ::Gitlab::ObjectHierarchy
+ private
+
+ def middle_table
+ ::Ci::BuildNeed.arel_table
+ end
+
+ def from_tables(cte)
+ [objects_table, cte.table, middle_table]
+ end
+
+ def parent_id_column(_cte)
+ middle_table[:name]
+ end
+
+ def ancestor_conditions(cte)
+ middle_table[:name].eq(objects_table[:name]).and(
+ middle_table[:build_id].eq(cte.table[:id])
+ )
+ end
+
+ def descendant_conditions(cte)
+ middle_table[:build_id].eq(objects_table[:id]).and(
+ middle_table[:name].eq(cte.table[:name])
+ )
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/project_config.rb b/lib/gitlab/ci/project_config.rb
new file mode 100644
index 00000000000..ded6877ef29
--- /dev/null
+++ b/lib/gitlab/ci/project_config.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ # Locates project CI config
+ class ProjectConfig
+ # The order of sources is important:
+ # - EE uses Compliance first since it must be used first if compliance templates are enabled.
+ # (see ee/lib/ee/gitlab/ci/project_config.rb)
+ # - Parameter is used by on-demand security scanning which passes the actual CI YAML to use as argument.
+ # - Bridge is used for downstream pipelines since the config is defined in the bridge job. If lower in priority,
+ # it would evaluate the project's YAML file instead.
+ # - Repository / ExternalProject / Remote: their order is not important between each other.
+ # - AutoDevops is used as default option if nothing else is found and if AutoDevops is enabled.
+ SOURCES = [
+ ProjectConfig::Parameter,
+ ProjectConfig::Bridge,
+ ProjectConfig::Repository,
+ ProjectConfig::ExternalProject,
+ ProjectConfig::Remote,
+ ProjectConfig::AutoDevops
+ ].freeze
+
+ def initialize(project:, sha:, custom_content: nil, pipeline_source: nil, pipeline_source_bridge: nil)
+ @config = find_config(project, sha, custom_content, pipeline_source, pipeline_source_bridge)
+ end
+
+ delegate :content, :source, to: :@config, allow_nil: true
+
+ def exists?
+ !!@config&.exists?
+ end
+
+ private
+
+ def find_config(project, sha, custom_content, pipeline_source, pipeline_source_bridge)
+ sources.each do |source|
+ config = source.new(project, sha, custom_content, pipeline_source, pipeline_source_bridge)
+ return config if config.exists?
+ end
+
+ nil
+ end
+
+ def sources
+ SOURCES
+ end
+ end
+ end
+end
+
+Gitlab::Ci::ProjectConfig.prepend_mod_with('Gitlab::Ci::ProjectConfig')
diff --git a/lib/gitlab/ci/project_config/auto_devops.rb b/lib/gitlab/ci/project_config/auto_devops.rb
new file mode 100644
index 00000000000..c6905f480a2
--- /dev/null
+++ b/lib/gitlab/ci/project_config/auto_devops.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class ProjectConfig
+ class AutoDevops < Source
+ def content
+ strong_memoize(:content) do
+ next unless project&.auto_devops_enabled?
+
+ template = Gitlab::Template::GitlabCiYmlTemplate.find(template_name)
+ YAML.dump('include' => [{ 'template' => template.full_name }])
+ end
+ end
+
+ def source
+ :auto_devops_source
+ end
+
+ private
+
+ def template_name
+ 'Auto-DevOps'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/project_config/bridge.rb b/lib/gitlab/ci/project_config/bridge.rb
new file mode 100644
index 00000000000..c342ab2c215
--- /dev/null
+++ b/lib/gitlab/ci/project_config/bridge.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class ProjectConfig
+ class Bridge < Source
+ def content
+ return unless pipeline_source_bridge
+
+ pipeline_source_bridge.yaml_for_downstream
+ end
+
+ def source
+ :bridge_source
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/project_config/external_project.rb b/lib/gitlab/ci/project_config/external_project.rb
new file mode 100644
index 00000000000..0ed5d6fa226
--- /dev/null
+++ b/lib/gitlab/ci/project_config/external_project.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class ProjectConfig
+ class ExternalProject < Source
+ def content
+ strong_memoize(:content) do
+ next unless external_project_path?
+
+ path_file, path_project, ref = extract_location_tokens
+
+ config_location = { 'project' => path_project, 'file' => path_file }
+ config_location['ref'] = ref if ref.present?
+
+ YAML.dump('include' => [config_location])
+ end
+ end
+
+ def source
+ :external_project_source
+ end
+
+ private
+
+ # Example: path/to/.gitlab-ci.yml@another-group/another-project
+ def external_project_path?
+ ci_config_path =~ /\A.+(yml|yaml)@.+\z/
+ end
+
+ # Example: path/to/.gitlab-ci.yml@another-group/another-project:refname
+ def extract_location_tokens
+ path_file, path_project = ci_config_path.split('@', 2)
+
+ if path_project.include? ":"
+ project, ref = path_project.split(':', 2)
+ [path_file, project, ref]
+ else
+ [path_file, path_project]
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/project_config/parameter.rb b/lib/gitlab/ci/project_config/parameter.rb
new file mode 100644
index 00000000000..69e699c27f1
--- /dev/null
+++ b/lib/gitlab/ci/project_config/parameter.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class ProjectConfig
+ class Parameter < Source
+ def content
+ strong_memoize(:content) do
+ next unless custom_content.present?
+
+ custom_content
+ end
+ end
+
+ def source
+ :parameter_source
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/project_config/remote.rb b/lib/gitlab/ci/project_config/remote.rb
new file mode 100644
index 00000000000..cf1292706d2
--- /dev/null
+++ b/lib/gitlab/ci/project_config/remote.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class ProjectConfig
+ class Remote < Source
+ def content
+ strong_memoize(:content) do
+ next unless ci_config_path =~ URI::DEFAULT_PARSER.make_regexp(%w[http https])
+
+ YAML.dump('include' => [{ 'remote' => ci_config_path }])
+ end
+ end
+
+ def source
+ :remote_source
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/project_config/repository.rb b/lib/gitlab/ci/project_config/repository.rb
new file mode 100644
index 00000000000..435ad4d42fe
--- /dev/null
+++ b/lib/gitlab/ci/project_config/repository.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class ProjectConfig
+ class Repository < Source
+ def content
+ strong_memoize(:content) do
+ next unless file_in_repository?
+
+ YAML.dump('include' => [{ 'local' => ci_config_path }])
+ end
+ end
+
+ def source
+ :repository_source
+ end
+
+ private
+
+ def file_in_repository?
+ return unless project
+ return unless sha
+
+ project.repository.gitlab_ci_yml_for(sha, ci_config_path).present?
+ rescue GRPC::NotFound, GRPC::Internal
+ nil
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/project_config/source.rb b/lib/gitlab/ci/project_config/source.rb
new file mode 100644
index 00000000000..ebe5728163b
--- /dev/null
+++ b/lib/gitlab/ci/project_config/source.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class ProjectConfig
+ class Source
+ include Gitlab::Utils::StrongMemoize
+
+ def initialize(project, sha, custom_content, pipeline_source, pipeline_source_bridge)
+ @project = project
+ @sha = sha
+ @custom_content = custom_content
+ @pipeline_source = pipeline_source
+ @pipeline_source_bridge = pipeline_source_bridge
+ end
+
+ def exists?
+ strong_memoize(:exists) do
+ content.present?
+ end
+ end
+
+ def content
+ raise NotImplementedError
+ end
+
+ def source
+ raise NotImplementedError
+ end
+
+ private
+
+ attr_reader :project, :sha, :custom_content, :pipeline_source, :pipeline_source_bridge
+
+ def ci_config_path
+ @ci_config_path ||= project.ci_config_path_or_default
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/reports/coverage_report_generator.rb b/lib/gitlab/ci/reports/coverage_report_generator.rb
index 6d57e05aa63..88b3b14d5c9 100644
--- a/lib/gitlab/ci/reports/coverage_report_generator.rb
+++ b/lib/gitlab/ci/reports/coverage_report_generator.rb
@@ -35,7 +35,7 @@ module Gitlab
private
def report_builds
- @pipeline.latest_report_builds_in_self_and_descendants(::Ci::JobArtifact.coverage_reports)
+ @pipeline.latest_report_builds_in_self_and_project_descendants(::Ci::JobArtifact.of_report_type(:coverage))
end
end
end
diff --git a/lib/gitlab/ci/reports/sbom/component.rb b/lib/gitlab/ci/reports/sbom/component.rb
index 86b9be274cc..198b34451b4 100644
--- a/lib/gitlab/ci/reports/sbom/component.rb
+++ b/lib/gitlab/ci/reports/sbom/component.rb
@@ -7,10 +7,10 @@ module Gitlab
class Component
attr_reader :component_type, :name, :version
- def initialize(component = {})
- @component_type = component['type']
- @name = component['name']
- @version = component['version']
+ def initialize(type:, name:, version:)
+ @component_type = type
+ @name = name
+ @version = version
end
end
end
diff --git a/lib/gitlab/ci/reports/sbom/report.rb b/lib/gitlab/ci/reports/sbom/report.rb
index dc6b3153e51..4f84d12f78c 100644
--- a/lib/gitlab/ci/reports/sbom/report.rb
+++ b/lib/gitlab/ci/reports/sbom/report.rb
@@ -17,11 +17,11 @@ module Gitlab
end
def set_source(source)
- self.source = Source.new(source)
+ self.source = source
end
def add_component(component)
- components << Component.new(component)
+ components << component
end
private
diff --git a/lib/gitlab/ci/reports/sbom/source.rb b/lib/gitlab/ci/reports/sbom/source.rb
index 60bf30b65a5..ea0fb8d4fbb 100644
--- a/lib/gitlab/ci/reports/sbom/source.rb
+++ b/lib/gitlab/ci/reports/sbom/source.rb
@@ -7,10 +7,10 @@ module Gitlab
class Source
attr_reader :source_type, :data, :fingerprint
- def initialize(source = {})
- @source_type = source['type']
- @data = source['data']
- @fingerprint = source['fingerprint']
+ def initialize(type:, data:, fingerprint:)
+ @source_type = type
+ @data = data
+ @fingerprint = fingerprint
end
end
end
diff --git a/lib/gitlab/ci/reports/security/scanner.rb b/lib/gitlab/ci/reports/security/scanner.rb
index 1ac66a0c671..918df163ede 100644
--- a/lib/gitlab/ci/reports/security/scanner.rb
+++ b/lib/gitlab/ci/reports/security/scanner.rb
@@ -7,13 +7,13 @@ module Gitlab
class Scanner
ANALYZER_ORDER = {
"bundler_audit" => 1,
- "retire.js" => 2,
+ "retire.js" => 2,
"gemnasium" => 3,
"gemnasium-maven" => 3,
"gemnasium-python" => 3,
"bandit" => 1,
"spotbugs" => 1,
- "semgrep" => 2
+ "semgrep" => 2
}.freeze
attr_accessor :external_id, :name, :vendor, :version
diff --git a/lib/gitlab/ci/status/build/failed.rb b/lib/gitlab/ci/status/build/failed.rb
index 5d60aa8f540..a136044c124 100644
--- a/lib/gitlab/ci/status/build/failed.rb
+++ b/lib/gitlab/ci/status/build/failed.rb
@@ -31,6 +31,7 @@ module Gitlab
downstream_pipeline_creation_failed: 'downstream pipeline can not be created',
secrets_provider_not_found: 'secrets provider can not be found',
reached_max_descendant_pipelines_depth: 'reached maximum depth of child pipelines',
+ reached_max_pipeline_hierarchy_size: 'downstream pipeline tree is too large',
project_deleted: 'pipeline project was deleted',
user_blocked: 'pipeline user was blocked',
ci_quota_exceeded: 'no more CI minutes available',
@@ -39,7 +40,8 @@ module Gitlab
builds_disabled: 'project builds are disabled',
environment_creation_failure: 'environment creation failure',
deployment_rejected: 'deployment rejected',
- ip_restriction_failure: 'IP address restriction failure'
+ ip_restriction_failure: 'IP address restriction failure',
+ failed_outdated_deployment_job: 'failed outdated deployment job'
}.freeze
private_constant :REASONS
diff --git a/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml
index f0ddc4b4916..539e1a6385d 100644
--- a/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/DAST-Default-Branch-Deploy.gitlab-ci.yml
@@ -1,5 +1,5 @@
variables:
- DAST_AUTO_DEPLOY_IMAGE_VERSION: 'v2.33.0'
+ DAST_AUTO_DEPLOY_IMAGE_VERSION: 'v2.37.0'
.dast-auto-deploy:
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${DAST_AUTO_DEPLOY_IMAGE_VERSION}"
diff --git a/lib/gitlab/ci/templates/Jobs/Dependency-Scanning.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Dependency-Scanning.latest.gitlab-ci.yml
new file mode 100644
index 00000000000..70f85382967
--- /dev/null
+++ b/lib/gitlab/ci/templates/Jobs/Dependency-Scanning.latest.gitlab-ci.yml
@@ -0,0 +1,244 @@
+# 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/Dependency-Scanning.gitlab-ci.yml
+
+# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/
+#
+# Configure dependency 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/dependency_scanning/index.html#available-variables
+
+variables:
+ # Setting this variable will affect all Security templates
+ # (SAST, Dependency Scanning, ...)
+ SECURE_ANALYZERS_PREFIX: "$CI_TEMPLATE_REGISTRY_HOST/security-products"
+ DS_EXCLUDED_ANALYZERS: ""
+ DS_EXCLUDED_PATHS: "spec, test, tests, tmp"
+ DS_MAJOR_VERSION: 3
+
+dependency_scanning:
+ stage: test
+ script:
+ - echo "$CI_JOB_NAME is used for configuration only, and its script should not be executed"
+ - exit 1
+ artifacts:
+ reports:
+ dependency_scanning: gl-dependency-scanning-report.json
+ dependencies: []
+ rules:
+ - when: never
+
+.ds-analyzer:
+ extends: dependency_scanning
+ allow_failure: true
+ variables:
+ # DS_ANALYZER_IMAGE is an undocumented variable used internally to allow QA to
+ # override the analyzer image with a custom value. This may be subject to change or
+ # breakage across GitLab releases.
+ DS_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/$DS_ANALYZER_NAME:$DS_MAJOR_VERSION"
+ # DS_ANALYZER_NAME is an undocumented variable used in job definitions
+ # to inject the analyzer name in the image name.
+ DS_ANALYZER_NAME: ""
+ image:
+ name: "$DS_ANALYZER_IMAGE$DS_IMAGE_SUFFIX"
+ # `rules` must be overridden explicitly by each child job
+ # see https://gitlab.com/gitlab-org/gitlab/-/issues/218444
+ script:
+ - /analyzer run
+
+.cyclonedx-reports:
+ artifacts:
+ paths:
+ - "**/gl-sbom-*.cdx.json"
+
+.gemnasium-shared-rule:
+ exists:
+ - '{Gemfile.lock,*/Gemfile.lock,*/*/Gemfile.lock}'
+ - '{composer.lock,*/composer.lock,*/*/composer.lock}'
+ - '{gems.locked,*/gems.locked,*/*/gems.locked}'
+ - '{go.sum,*/go.sum,*/*/go.sum}'
+ - '{npm-shrinkwrap.json,*/npm-shrinkwrap.json,*/*/npm-shrinkwrap.json}'
+ - '{package-lock.json,*/package-lock.json,*/*/package-lock.json}'
+ - '{yarn.lock,*/yarn.lock,*/*/yarn.lock}'
+ - '{packages.lock.json,*/packages.lock.json,*/*/packages.lock.json}'
+ - '{conan.lock,*/conan.lock,*/*/conan.lock}'
+
+gemnasium-dependency_scanning:
+ extends:
+ - .ds-analyzer
+ - .cyclonedx-reports
+ variables:
+ DS_ANALYZER_NAME: "gemnasium"
+ GEMNASIUM_LIBRARY_SCAN_ENABLED: "true"
+ rules:
+ - if: $DEPENDENCY_SCANNING_DISABLED
+ when: never
+ - if: $DS_EXCLUDED_ANALYZERS =~ /gemnasium([^-]|$)/
+ when: never
+
+ # Add the job to merge request pipelines if there's an open merge request.
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event" &&
+ $GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
+ $CI_GITLAB_FIPS_MODE == "true"
+ exists: !reference [.gemnasium-shared-rule, exists]
+ variables:
+ DS_IMAGE_SUFFIX: "-fips"
+ DS_REMEDIATE: "false"
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event" &&
+ $GITLAB_FEATURES =~ /\bdependency_scanning\b/
+ exists: !reference [.gemnasium-shared-rule, exists]
+
+ # 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 &&
+ $GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
+ $CI_GITLAB_FIPS_MODE == "true"
+ exists: !reference [.gemnasium-shared-rule, exists]
+ variables:
+ DS_IMAGE_SUFFIX: "-fips"
+ DS_REMEDIATE: "false"
+ - if: $CI_COMMIT_BRANCH &&
+ $GITLAB_FEATURES =~ /\bdependency_scanning\b/
+ exists: !reference [.gemnasium-shared-rule, exists]
+
+.gemnasium-maven-shared-rule:
+ exists:
+ - '{build.gradle,*/build.gradle,*/*/build.gradle}'
+ - '{build.gradle.kts,*/build.gradle.kts,*/*/build.gradle.kts}'
+ - '{build.sbt,*/build.sbt,*/*/build.sbt}'
+ - '{pom.xml,*/pom.xml,*/*/pom.xml}'
+
+gemnasium-maven-dependency_scanning:
+ extends:
+ - .ds-analyzer
+ - .cyclonedx-reports
+ variables:
+ DS_ANALYZER_NAME: "gemnasium-maven"
+ rules:
+ - if: $DEPENDENCY_SCANNING_DISABLED
+ when: never
+ - if: $DS_EXCLUDED_ANALYZERS =~ /gemnasium-maven/
+ when: never
+
+ # Add the job to merge request pipelines if there's an open merge request.
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event" &&
+ $GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
+ $CI_GITLAB_FIPS_MODE == "true"
+ exists: !reference [.gemnasium-maven-shared-rule, exists]
+ variables:
+ DS_IMAGE_SUFFIX: "-fips"
+ DS_REMEDIATE: "false"
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event" &&
+ $GITLAB_FEATURES =~ /\bdependency_scanning\b/
+ exists: !reference [.gemnasium-maven-shared-rule, exists]
+
+ # 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 &&
+ $GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
+ $CI_GITLAB_FIPS_MODE == "true"
+ exists: !reference [.gemnasium-maven-shared-rule, exists]
+ variables:
+ DS_IMAGE_SUFFIX: "-fips"
+ - if: $CI_COMMIT_BRANCH &&
+ $GITLAB_FEATURES =~ /\bdependency_scanning\b/
+ exists: !reference [.gemnasium-maven-shared-rule, exists]
+
+.gemnasium-python-shared-rule:
+ exists:
+ - '{requirements.txt,*/requirements.txt,*/*/requirements.txt}'
+ - '{requirements.pip,*/requirements.pip,*/*/requirements.pip}'
+ - '{Pipfile,*/Pipfile,*/*/Pipfile}'
+ - '{requires.txt,*/requires.txt,*/*/requires.txt}'
+ - '{setup.py,*/setup.py,*/*/setup.py}'
+ - '{poetry.lock,*/poetry.lock,*/*/poetry.lock}'
+
+gemnasium-python-dependency_scanning:
+ extends:
+ - .ds-analyzer
+ - .cyclonedx-reports
+ variables:
+ DS_ANALYZER_NAME: "gemnasium-python"
+ rules:
+ - if: $DEPENDENCY_SCANNING_DISABLED
+ when: never
+ - if: $DS_EXCLUDED_ANALYZERS =~ /gemnasium-python/
+ when: never
+
+ # Add the job to merge request pipelines if there's an open merge request.
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event" &&
+ $GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
+ $CI_GITLAB_FIPS_MODE == "true"
+ exists: !reference [.gemnasium-python-shared-rule, exists]
+ variables:
+ DS_IMAGE_SUFFIX: "-fips"
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event" &&
+ $GITLAB_FEATURES =~ /\bdependency_scanning\b/
+ exists: !reference [.gemnasium-python-shared-rule, exists]
+ # Support passing of $PIP_REQUIREMENTS_FILE
+ # See https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#configuring-specific-analyzers-used-by-dependency-scanning
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event" &&
+ $GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
+ $PIP_REQUIREMENTS_FILE &&
+ $CI_GITLAB_FIPS_MODE == "true"
+ variables:
+ DS_IMAGE_SUFFIX: "-fips"
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event" &&
+ $GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
+ $PIP_REQUIREMENTS_FILE
+
+ # 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 &&
+ $GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
+ $CI_GITLAB_FIPS_MODE == "true"
+ exists: !reference [.gemnasium-python-shared-rule, exists]
+ variables:
+ DS_IMAGE_SUFFIX: "-fips"
+ - if: $CI_COMMIT_BRANCH &&
+ $GITLAB_FEATURES =~ /\bdependency_scanning\b/
+ exists: !reference [.gemnasium-python-shared-rule, exists]
+ # Support passing of $PIP_REQUIREMENTS_FILE
+ # See https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#configuring-specific-analyzers-used-by-dependency-scanning
+ - if: $CI_COMMIT_BRANCH &&
+ $GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
+ $PIP_REQUIREMENTS_FILE &&
+ $CI_GITLAB_FIPS_MODE == "true"
+ variables:
+ DS_IMAGE_SUFFIX: "-fips"
+ - if: $CI_COMMIT_BRANCH &&
+ $GITLAB_FEATURES =~ /\bdependency_scanning\b/ &&
+ $PIP_REQUIREMENTS_FILE
+
+bundler-audit-dependency_scanning:
+ extends: .ds-analyzer
+ variables:
+ DS_ANALYZER_NAME: "bundler-audit"
+ DS_MAJOR_VERSION: 2
+ script:
+ - echo "This job was deprecated in GitLab 14.8 and removed in GitLab 15.0"
+ - echo "For more information see https://gitlab.com/gitlab-org/gitlab/-/issues/347491"
+ - exit 1
+ rules:
+ - when: never
+
+retire-js-dependency_scanning:
+ extends: .ds-analyzer
+ variables:
+ DS_ANALYZER_NAME: "retire.js"
+ DS_MAJOR_VERSION: 2
+ script:
+ - echo "This job was deprecated in GitLab 14.8 and removed in GitLab 15.0"
+ - echo "For more information see https://gitlab.com/gitlab-org/gitlab/-/issues/289830"
+ - exit 1
+ rules:
+ - when: never
diff --git a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
index 1a2a8b4edb4..78fe108e8b9 100644
--- a/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Deploy.gitlab-ci.yml
@@ -1,5 +1,5 @@
variables:
- AUTO_DEPLOY_IMAGE_VERSION: 'v2.33.0'
+ AUTO_DEPLOY_IMAGE_VERSION: 'v2.37.0'
.auto-deploy:
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}"
diff --git a/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml
index cb8818357a2..bc2e1fed0d4 100644
--- a/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/Deploy.latest.gitlab-ci.yml
@@ -1,5 +1,5 @@
variables:
- AUTO_DEPLOY_IMAGE_VERSION: 'v2.33.0'
+ AUTO_DEPLOY_IMAGE_VERSION: 'v2.37.0'
.auto-deploy:
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}"
diff --git a/lib/gitlab/ci/templates/Jobs/License-Scanning.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/License-Scanning.latest.gitlab-ci.yml
new file mode 100644
index 00000000000..e47f669c2e2
--- /dev/null
+++ b/lib/gitlab/ci/templates/Jobs/License-Scanning.latest.gitlab-ci.yml
@@ -0,0 +1,48 @@
+# 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/License-Scanning.gitlab-ci.yml
+
+# Read more about this feature here: https://docs.gitlab.com/ee/user/compliance/license_compliance/index.html
+#
+# Configure license scanning with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/index.html).
+# List of available variables: https://docs.gitlab.com/ee/user/compliance/license_compliance/#available-variables
+
+variables:
+ # Setting this variable will affect all Security templates
+ # (SAST, Dependency Scanning, ...)
+ SECURE_ANALYZERS_PREFIX: "$CI_TEMPLATE_REGISTRY_HOST/security-products"
+
+ LICENSE_MANAGEMENT_SETUP_CMD: '' # If needed, specify a command to setup your environment with a custom package manager.
+ LICENSE_MANAGEMENT_VERSION: 4
+
+license_scanning:
+ stage: test
+ image:
+ name: "$SECURE_ANALYZERS_PREFIX/license-finder:$LICENSE_MANAGEMENT_VERSION"
+ entrypoint: [""]
+ variables:
+ LM_REPORT_VERSION: '2.1'
+ SETUP_CMD: $LICENSE_MANAGEMENT_SETUP_CMD
+ allow_failure: true
+ script:
+ - /run.sh analyze .
+ artifacts:
+ reports:
+ license_scanning: gl-license-scanning-report.json
+ dependencies: []
+ rules:
+ - if: $LICENSE_MANAGEMENT_DISABLED
+ when: never
+
+ # Add the job to merge request pipelines if there's an open merge request.
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event" &&
+ $GITLAB_FEATURES =~ /\blicense_scanning\b/
+
+ # 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 &&
+ $GITLAB_FEATURES =~ /\blicense_scanning\b/
diff --git a/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml
index dd164c00724..a6d47e31de2 100644
--- a/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml
@@ -36,19 +36,12 @@ sast:
bandit-sast:
extends: .sast-analyzer
- image:
- name: "$SAST_ANALYZER_IMAGE"
- variables:
- SAST_ANALYZER_IMAGE_TAG: 2
- SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/bandit:$SAST_ANALYZER_IMAGE_TAG"
+ script:
+ - echo "This job was deprecated in GitLab 14.8 and removed in GitLab 15.4"
+ - echo "For more information see https://gitlab.com/gitlab-org/gitlab/-/issues/352554"
+ - exit 1
rules:
- - if: $SAST_DISABLED
- when: never
- - if: $SAST_EXCLUDED_ANALYZERS =~ /bandit/
- when: never
- - if: $CI_COMMIT_BRANCH
- exists:
- - '**/*.py'
+ - when: never
brakeman-sast:
extends: .sast-analyzer
@@ -69,23 +62,12 @@ brakeman-sast:
eslint-sast:
extends: .sast-analyzer
- image:
- name: "$SAST_ANALYZER_IMAGE"
- variables:
- SAST_ANALYZER_IMAGE_TAG: 2
- SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/eslint:$SAST_ANALYZER_IMAGE_TAG"
+ script:
+ - echo "This job was deprecated in GitLab 14.8 and removed in GitLab 15.4"
+ - echo "For more information see https://gitlab.com/gitlab-org/gitlab/-/issues/352554"
+ - exit 1
rules:
- - if: $SAST_DISABLED
- when: never
- - if: $SAST_EXCLUDED_ANALYZERS =~ /eslint/
- when: never
- - if: $CI_COMMIT_BRANCH
- exists:
- - '**/*.html'
- - '**/*.js'
- - '**/*.jsx'
- - '**/*.ts'
- - '**/*.tsx'
+ - when: never
flawfinder-sast:
extends: .sast-analyzer
@@ -125,19 +107,12 @@ kubesec-sast:
gosec-sast:
extends: .sast-analyzer
- image:
- name: "$SAST_ANALYZER_IMAGE"
- variables:
- SAST_ANALYZER_IMAGE_TAG: 3
- SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/gosec:$SAST_ANALYZER_IMAGE_TAG"
+ script:
+ - echo "This job was deprecated in GitLab 14.8 and removed in GitLab 15.4"
+ - echo "For more information see https://gitlab.com/gitlab-org/gitlab/-/issues/352554"
+ - exit 1
rules:
- - if: $SAST_DISABLED
- when: never
- - if: $SAST_EXCLUDED_ANALYZERS =~ /gosec/
- when: never
- - if: $CI_COMMIT_BRANCH
- exists:
- - '**/*.go'
+ - when: never
.mobsf-sast:
extends: .sast-analyzer
@@ -261,6 +236,8 @@ semgrep-sast:
- '**/*.c'
- '**/*.go'
- '**/*.java'
+ - '**/*.cs'
+ - '**/*.html'
sobelow-sast:
extends: .sast-analyzer
@@ -297,6 +274,5 @@ spotbugs-sast:
- if: $CI_COMMIT_BRANCH
exists:
- '**/*.groovy'
- - '**/*.java'
- '**/*.scala'
- '**/*.kt'
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 c6938920ea4..c0ca821ebff 100644
--- a/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml
@@ -36,24 +36,12 @@ sast:
bandit-sast:
extends: .sast-analyzer
- image:
- name: "$SAST_ANALYZER_IMAGE"
- variables:
- SAST_ANALYZER_IMAGE_TAG: 2
- SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/bandit:$SAST_ANALYZER_IMAGE_TAG"
+ script:
+ - echo "This job was deprecated in GitLab 14.8 and removed in GitLab 15.3"
+ - echo "For more information see https://gitlab.com/gitlab-org/gitlab/-/issues/352554"
+ - exit 1
rules:
- - if: $SAST_DISABLED
- when: never
- - if: $SAST_EXCLUDED_ANALYZERS =~ /bandit/
- when: never
- - if: $CI_PIPELINE_SOURCE == "merge_request_event" # Add the job to merge request pipelines if there's an open merge request.
- exists:
- - '**/*.py'
- - 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.
- exists:
- - '**/*.py'
+ - when: never
brakeman-sast:
extends: .sast-analyzer
@@ -80,32 +68,12 @@ brakeman-sast:
eslint-sast:
extends: .sast-analyzer
- image:
- name: "$SAST_ANALYZER_IMAGE"
- variables:
- SAST_ANALYZER_IMAGE_TAG: 2
- SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/eslint:$SAST_ANALYZER_IMAGE_TAG"
+ script:
+ - echo "This job was deprecated in GitLab 14.8 and removed in GitLab 15.3"
+ - echo "For more information see https://gitlab.com/gitlab-org/gitlab/-/issues/352554"
+ - exit 1
rules:
- - if: $SAST_DISABLED
- when: never
- - if: $SAST_EXCLUDED_ANALYZERS =~ /eslint/
- when: never
- - if: $CI_PIPELINE_SOURCE == "merge_request_event" # Add the job to merge request pipelines if there's an open merge request.
- exists:
- - '**/*.html'
- - '**/*.js'
- - '**/*.jsx'
- - '**/*.ts'
- - '**/*.tsx'
- - 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.
- exists:
- - '**/*.html'
- - '**/*.js'
- - '**/*.jsx'
- - '**/*.ts'
- - '**/*.tsx'
+ - when: never
flawfinder-sast:
extends: .sast-analyzer
@@ -138,6 +106,15 @@ flawfinder-sast:
- '**/*.cp'
- '**/*.cxx'
+gosec-sast:
+ extends: .sast-analyzer
+ script:
+ - echo "This job was deprecated in GitLab 15.0 and removed in GitLab 15.2"
+ - echo "For more information see https://gitlab.com/gitlab-org/gitlab/-/issues/352554"
+ - exit 1
+ rules:
+ - when: never
+
kubesec-sast:
extends: .sast-analyzer
image:
@@ -159,27 +136,6 @@ kubesec-sast:
- if: $CI_COMMIT_BRANCH &&
$SCAN_KUBERNETES_MANIFESTS == 'true'
-gosec-sast:
- extends: .sast-analyzer
- image:
- name: "$SAST_ANALYZER_IMAGE"
- variables:
- SAST_ANALYZER_IMAGE_TAG: 3
- SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/gosec:$SAST_ANALYZER_IMAGE_TAG"
- rules:
- - if: $SAST_DISABLED
- when: never
- - if: $SAST_EXCLUDED_ANALYZERS =~ /gosec/
- when: never
- - if: $CI_PIPELINE_SOURCE == "merge_request_event" # Add the job to merge request pipelines if there's an open merge request.
- exists:
- - '**/*.go'
- - 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.
- exists:
- - '**/*.go'
-
.mobsf-sast:
extends: .sast-analyzer
image:
@@ -323,7 +279,7 @@ semgrep-sast:
image:
name: "$SAST_ANALYZER_IMAGE"
variables:
- SERACH_MAX_DEPTH: 20
+ SEARCH_MAX_DEPTH: 20
SAST_ANALYZER_IMAGE_TAG: 3
SAST_ANALYZER_IMAGE: "$SECURE_ANALYZERS_PREFIX/semgrep:$SAST_ANALYZER_IMAGE_TAG$SAST_IMAGE_SUFFIX"
rules:
@@ -341,6 +297,8 @@ semgrep-sast:
- '**/*.c'
- '**/*.go'
- '**/*.java'
+ - '**/*.html'
+ - '**/*.cs'
- 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.
@@ -353,6 +311,8 @@ semgrep-sast:
- '**/*.c'
- '**/*.go'
- '**/*.java'
+ - '**/*.html'
+ - '**/*.cs'
sobelow-sast:
extends: .sast-analyzer
@@ -394,7 +354,6 @@ spotbugs-sast:
- if: $CI_PIPELINE_SOURCE == "merge_request_event" # Add the job to merge request pipelines if there's an open merge request.
exists:
- '**/*.groovy'
- - '**/*.java'
- '**/*.scala'
- '**/*.kt'
- if: $CI_OPEN_MERGE_REQUESTS # Don't add it to a *branch* pipeline if it's already in a merge request pipeline.
@@ -402,6 +361,5 @@ spotbugs-sast:
- if: $CI_COMMIT_BRANCH # If there's no open merge request, add it to a *branch* pipeline instead.
exists:
- '**/*.groovy'
- - '**/*.java'
- '**/*.scala'
- '**/*.kt'
diff --git a/lib/gitlab/ci/templates/Katalon.gitlab-ci.yml b/lib/gitlab/ci/templates/Katalon.gitlab-ci.yml
new file mode 100644
index 00000000000..c8939c8f5a2
--- /dev/null
+++ b/lib/gitlab/ci/templates/Katalon.gitlab-ci.yml
@@ -0,0 +1,65 @@
+# This template is provided and maintained by Katalon, an official Technology Partner with GitLab.
+#
+# Use this template to run a Katalon Studio test from this repository.
+# You can:
+# - Copy and paste this template into a new `.gitlab-ci.yml` file.
+# - Add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword.
+#
+# In either case, you must also select which job you want to run, `.katalon_tests`
+# or `.katalon_tests_with_artifacts` (see configuration below), and add that configuration
+# to a new job with `extends:`. For example:
+#
+# Katalon-tests:
+# extends:
+# - .katalon_tests_with_artifacts
+#
+# Requirements:
+# - A Katalon Studio project with the content saved in the root GitLab repository folder.
+# - An active KRE license.
+# - A valid Katalon API key.
+#
+# CI/CD variables, set in the project CI/CD settings:
+# - KATALON_TEST_SUITE_PATH: The default path is `Test Suites/<Your Test Suite Name>`.
+# Defines which test suite to run.
+# - KATALON_API_KEY: The Katalon API key.
+# - KATALON_PROJECT_DIR: Optional. Add if the project is in another location.
+# - KATALON_ORG_ID: Optional. Add if you are part of multiple Katalon orgs.
+# Set to the Org ID that has KRE licenses assigned. For more info on the Org ID,
+# see https://support.katalon.com/hc/en-us/articles/4724459179545-How-to-get-Organization-ID-
+
+.katalon_tests:
+ # Use the latest version of the Katalon Runtime Engine. You can also use other versions of the
+ # Katalon Runtime Engine by specifying another tag, for example `katalonstudio/katalon:8.1.2`
+ # or `katalonstudio/katalon:8.3.0`.
+ image: 'katalonstudio/katalon'
+ services:
+ - docker:dind
+ variables:
+ # Specify the Katalon Studio project directory. By default, it is stored under the root project folder.
+ KATALON_PROJECT_DIR: $CI_PROJECT_DIR
+
+ # The following bash script has two different versions, one if you set the KATALON_ORG_ID
+ # CI/CD variable, and the other if you did not set it. If you have more than one org in
+ # admin.katalon.com you must set the KATALON_ORG_ID variable with an ORG ID or
+ # the Katalon Test Suite fails to run.
+ #
+ # You can update or add additional `katalonc` commands below. To see all of the arguments
+ # `katalonc` supports, go to https://docs.katalon.com/katalon-studio/docs/console-mode-execution.html
+ script:
+ - |-
+ if [[ $KATALON_ORG_ID == "" ]]; then
+ katalonc.sh -projectPath=$KATALON_PROJECT_DIR -apiKey=$KATALON_API_KEY -browserType="Chrome" -retry=0 -statusDelay=20 -testSuitePath="$KATALON_TEST_SUITE_PATH" -reportFolder=Reports/
+ else
+ katalonc.sh -projectPath=$KATALON_PROJECT_DIR -apiKey=$KATALON_API_KEY -browserType="Chrome" -retry=0 -statusDelay=20 -orgID=$KATALON_ORG_ID -testSuitePath="$KATALON_TEST_SUITE_PATH" -reportFolder=Reports/
+ fi
+
+# Upload the artifacts and make the junit report accessible under the Pipeline Tests
+.katalon_tests_with_artifacts:
+ extends: .katalon_tests
+ artifacts:
+ when: always
+ paths:
+ - Reports/
+ reports:
+ junit:
+ Reports/*/*/*/*.xml
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 3d7883fb87a..79a08c33fdf 100644
--- a/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
@@ -11,12 +11,12 @@
#
# Requirements:
# - A `test` stage to be present in the pipeline.
-# - You must define the image to be scanned in the DOCKER_IMAGE variable. If DOCKER_IMAGE is the
+# - 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 `DOCKER_USER` and `DOCKER_PASSWORD` variables if the
+# - 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
-# DOCKERFILE_PATH variable.
+# 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
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
new file mode 100644
index 00000000000..f7b1d12b3b3
--- /dev/null
+++ b/lib/gitlab/ci/templates/Security/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/Security/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
+
+ # 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/Terraform/Base.gitlab-ci.yml b/lib/gitlab/ci/templates/Terraform/Base.gitlab-ci.yml
index 3a956ebfc49..9a40a23b276 100644
--- a/lib/gitlab/ci/templates/Terraform/Base.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Terraform/Base.gitlab-ci.yml
@@ -9,7 +9,7 @@
# There is a more opinionated template which we suggest the users to abide,
# which is the lib/gitlab/ci/templates/Terraform.gitlab-ci.yml
image:
- name: "$CI_TEMPLATE_REGISTRY_HOST/gitlab-org/terraform-images/releases/terraform:1.1.9"
+ name: "$CI_TEMPLATE_REGISTRY_HOST/gitlab-org/terraform-images/releases/1.1:v0.43.0"
variables:
TF_ROOT: ${CI_PROJECT_DIR} # The relative path to the root directory of the Terraform project
diff --git a/lib/gitlab/ci/templates/npm.gitlab-ci.yml b/lib/gitlab/ci/templates/npm.gitlab-ci.yml
index 64c784f43cb..fb0d300338b 100644
--- a/lib/gitlab/ci/templates/npm.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/npm.gitlab-ci.yml
@@ -38,7 +38,7 @@ publish:
# Compare the version in package.json to all published versions.
# If the package.json version has not yet been published, run `npm publish`.
- |
- if [[ $(npm view "${NPM_PACKAGE_NAME}" versions) != *"'${NPM_PACKAGE_VERSION}'"* ]]; then
+ if [[ "$(npm view ${NPM_PACKAGE_NAME} versions)" != *"'${NPM_PACKAGE_VERSION}'"* ]]; then
npm publish
echo "Successfully published version ${NPM_PACKAGE_VERSION} of ${NPM_PACKAGE_NAME} to GitLab's NPM registry: ${CI_PROJECT_URL}/-/packages"
else
diff --git a/lib/gitlab/ci/trace.rb b/lib/gitlab/ci/trace.rb
index 95a60b852b8..c5664ef1cfb 100644
--- a/lib/gitlab/ci/trace.rb
+++ b/lib/gitlab/ci/trace.rb
@@ -23,7 +23,6 @@ module Gitlab
attr_reader :job
- delegate :old_trace, to: :job
delegate :can_attempt_archival_now?, :increment_archival_attempts!,
:archival_attempts_message, :archival_attempts_available?, to: :trace_metadata
@@ -82,7 +81,7 @@ module Gitlab
end
def live?
- job.trace_chunks.any? || current_path.present? || old_trace.present?
+ job.trace_chunks.any? || current_path.present?
end
def read(&block)
@@ -111,7 +110,6 @@ module Gitlab
# Erase the live trace
erase_trace_chunks!
FileUtils.rm_f(current_path) if current_path # Remove a trace file of a live trace
- job.erase_old_trace! if job.has_old_trace? # Remove a trace in database of a live trace
ensure
@current_path = nil
end
@@ -162,8 +160,6 @@ module Gitlab
Gitlab::Ci::Trace::ChunkedIO.new(job)
elsif current_path
File.open(current_path, "rb")
- elsif old_trace
- StringIO.new(old_trace)
end
end
@@ -210,11 +206,6 @@ module Gitlab
archive_stream!(stream)
FileUtils.rm(current_path)
end
- elsif old_trace
- StringIO.new(old_trace, 'rb').tap do |stream|
- archive_stream!(stream)
- job.erase_old_trace!
- end
end
end
diff --git a/lib/gitlab/ci/variables/builder.rb b/lib/gitlab/ci/variables/builder.rb
index 95dff83506d..528d72c9bcc 100644
--- a/lib/gitlab/ci/variables/builder.rb
+++ b/lib/gitlab/ci/variables/builder.rb
@@ -118,7 +118,7 @@ module Gitlab
def predefined_variables(job)
Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables.append(key: 'CI_JOB_NAME', value: job.name)
- variables.append(key: 'CI_JOB_STAGE', value: job.stage)
+ variables.append(key: 'CI_JOB_STAGE', value: job.stage_name)
variables.append(key: 'CI_JOB_MANUAL', value: 'true') if job.action?
variables.append(key: 'CI_PIPELINE_TRIGGERED', value: 'true') if job.trigger_request
@@ -127,7 +127,7 @@ module Gitlab
# legacy variables
variables.append(key: 'CI_BUILD_NAME', value: job.name)
- variables.append(key: 'CI_BUILD_STAGE', value: job.stage)
+ variables.append(key: 'CI_BUILD_STAGE', value: job.stage_name)
variables.append(key: 'CI_BUILD_TRIGGERED', value: 'true') if job.trigger_request
variables.append(key: 'CI_BUILD_MANUAL', value: 'true') if job.action?
end
diff --git a/lib/gitlab/ci/variables/helpers.rb b/lib/gitlab/ci/variables/helpers.rb
index 7cc727bb3ea..16e3afd8620 100644
--- a/lib/gitlab/ci/variables/helpers.rb
+++ b/lib/gitlab/ci/variables/helpers.rb
@@ -6,24 +6,24 @@ module Gitlab
module Helpers
class << self
def merge_variables(current_vars, new_vars)
- current_vars = transform_from_yaml_variables(current_vars)
- new_vars = transform_from_yaml_variables(new_vars)
+ return current_vars if new_vars.blank?
- transform_to_yaml_variables(
- current_vars.merge(new_vars)
- )
- end
+ current_vars = transform_to_array(current_vars) if current_vars.is_a?(Hash)
+ new_vars = transform_to_array(new_vars) if new_vars.is_a?(Hash)
- def transform_to_yaml_variables(vars)
- vars.to_h.map do |key, value|
- { key: key.to_s, value: value, public: true }
- end
+ (new_vars + current_vars).uniq { |var| var[:key] }
end
- def transform_from_yaml_variables(vars)
- return vars.stringify_keys.transform_values(&:to_s) if vars.is_a?(Hash)
+ def transform_to_array(vars)
+ return [] if vars.blank?
- vars.to_a.to_h { |var| [var[:key].to_s, var[:value]] }
+ vars.map do |key, data|
+ if data.is_a?(Hash)
+ { key: key.to_s, **data.except(:key) }
+ else
+ { key: key.to_s, value: data }
+ end
+ end
end
def inherit_yaml_variables(from:, to:, inheritance:)
@@ -35,7 +35,7 @@ module Gitlab
def apply_inheritance(variables, inheritance)
case inheritance
when true then variables
- when false then {}
+ when false then []
when Array then variables.select { |var| inheritance.include?(var[:key]) }
end
end
diff --git a/lib/gitlab/ci/yaml_processor/feature_flags.rb b/lib/gitlab/ci/yaml_processor/feature_flags.rb
index f03db9d0e6b..50d37f6e4a0 100644
--- a/lib/gitlab/ci/yaml_processor/feature_flags.rb
+++ b/lib/gitlab/ci/yaml_processor/feature_flags.rb
@@ -5,10 +5,10 @@ module Gitlab
class YamlProcessor
module FeatureFlags
ACTOR_KEY = 'ci_yaml_processor_feature_flag_actor'
+ CORRECT_USAGE_KEY = 'ci_yaml_processor_feature_flag_correct_usage'
NO_ACTOR_VALUE = :no_actor
-
- NoActorError = Class.new(StandardError)
NO_ACTOR_MESSAGE = "Actor not set. Ensure to call `enabled?` inside `with_actor` block"
+ NoActorError = Class.new(StandardError)
class << self
# Cache a feature flag actor as thread local variable so
@@ -31,6 +31,15 @@ module Gitlab
::Feature.enabled?(feature_flag, current_actor)
end
+ def ensure_correct_usage
+ previous = Thread.current[CORRECT_USAGE_KEY]
+ Thread.current[CORRECT_USAGE_KEY] = true
+
+ yield
+ ensure
+ Thread.current[CORRECT_USAGE_KEY] = previous
+ end
+
private
def current_actor
@@ -39,10 +48,22 @@ module Gitlab
value
rescue NoActorError => e
- Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
+ handle_missing_actor(e)
nil
end
+
+ def handle_missing_actor(exception)
+ if ensure_correct_usage?
+ Gitlab::ErrorTracking.track_and_raise_for_dev_exception(exception)
+ else
+ Gitlab::ErrorTracking.track_exception(exception)
+ end
+ end
+
+ def ensure_correct_usage?
+ Thread.current[CORRECT_USAGE_KEY] == true
+ end
end
end
end
diff --git a/lib/gitlab/ci/yaml_processor/result.rb b/lib/gitlab/ci/yaml_processor/result.rb
index 4bd1ac3b67f..f203f88442d 100644
--- a/lib/gitlab/ci/yaml_processor/result.rb
+++ b/lib/gitlab/ci/yaml_processor/result.rb
@@ -43,7 +43,7 @@ module Gitlab
end
def root_variables
- @root_variables ||= transform_to_yaml_variables(variables)
+ @root_variables ||= transform_to_array(variables)
end
def jobs
@@ -70,7 +70,7 @@ module Gitlab
environment: job[:environment_name],
coverage_regex: job[:coverage],
# yaml_variables is calculated with using job_variables in Seed::Build
- job_variables: transform_to_yaml_variables(job[:job_variables]),
+ job_variables: transform_to_array(job[:job_variables]),
root_variables_inheritance: job[:root_variables_inheritance],
needs_attributes: job.dig(:needs, :job),
interruptible: job[:interruptible],
@@ -114,7 +114,7 @@ module Gitlab
Gitlab::Ci::Variables::Helpers.inherit_yaml_variables(
from: root_variables,
- to: transform_to_yaml_variables(job[:job_variables]),
+ to: job[:job_variables],
inheritance: job.fetch(:root_variables_inheritance, true)
)
end
@@ -137,8 +137,8 @@ module Gitlab
job[:release]
end
- def transform_to_yaml_variables(variables)
- ::Gitlab::Ci::Variables::Helpers.transform_to_yaml_variables(variables)
+ def transform_to_array(variables)
+ ::Gitlab::Ci::Variables::Helpers.transform_to_array(variables)
end
end
end
diff --git a/lib/gitlab/cleanup/personal_access_tokens.rb b/lib/gitlab/cleanup/personal_access_tokens.rb
new file mode 100644
index 00000000000..a1e4b5765c2
--- /dev/null
+++ b/lib/gitlab/cleanup/personal_access_tokens.rb
@@ -0,0 +1,105 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Cleanup
+ class PersonalAccessTokens
+ # By default tokens that haven't been used for over 1 year will be revoked
+ DEFAULT_TIME_PERIOD = 1.year
+ # To prevent inadvertently revoking all tokens, we provide a minimum time
+ MINIMUM_TIME_PERIOD = 1.day
+
+ attr_reader :logger, :cut_off_date, :revocation_time, :group
+
+ def initialize(cut_off_date: DEFAULT_TIME_PERIOD.ago.beginning_of_day, logger: nil, group_full_path:)
+ @cut_off_date = cut_off_date
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ @group = Group.find_by_full_path(group_full_path)
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ raise "Group with full_path #{group_full_path} not found" unless @group
+ raise "Invalid time: #{@cut_off_date}" unless @cut_off_date <= MINIMUM_TIME_PERIOD.ago
+
+ # Use a static revocation time to make correlation of revoked
+ # tokens easier, should it be needed.
+ @revocation_time = Time.current.utc
+ @logger = logger || Gitlab::AppJsonLogger
+
+ raise "Invalid logger: #{@logger}" unless @logger.respond_to?(:info) && @logger.respond_to?(:warn)
+ end
+
+ def run!(dry_run: true, revoke_active_tokens: false)
+ # rubocop:disable Rails/Output
+ if dry_run
+ puts "Dry running. No changes will be made"
+ elsif revoke_active_tokens
+ puts "Revoking used and unused access tokens created before #{cut_off_date}..."
+ else
+ puts "Revoking access tokens last used and created before #{cut_off_date}..."
+ end
+ # rubocop:enable Rails/Output
+
+ tokens_to_revoke = revocable_tokens(revoke_active_tokens)
+
+ # rubocop:disable Cop/InBatches
+ tokens_to_revoke.in_batches do |access_tokens|
+ revoke_batch(access_tokens, dry_run)
+ end
+ # rubocop:enable Cop/InBatches
+ end
+
+ private
+
+ def revocable_tokens(revoke_active_tokens)
+ if revoke_active_tokens
+ PersonalAccessToken
+ .active
+ .owner_is_human
+ .created_before(cut_off_date)
+ .for_users(group.users)
+ else
+ PersonalAccessToken
+ .active
+ .owner_is_human
+ .last_used_before_or_unused(cut_off_date)
+ .for_users(group.users)
+ end
+ end
+
+ def revoke_batch(access_tokens, dry_run)
+ # Capture a simplified set of attributes for logging and for
+ # determining when an error has led some records to not be
+ # updated
+ attrs = access_tokens.as_json(only: [:id, :user_id])
+
+ # Use `update_all` to bypass any validations which might
+ # prevent revocation. Manually specify updated_at.
+ affected_row_count = dry_run ? 0 : access_tokens.update_all(revoked: true, updated_at: @revocation_time)
+
+ message = {
+ dry_run: dry_run,
+ message: "Revoke token batch",
+ token_count: attrs.size,
+ updated_count: affected_row_count,
+ tokens: attrs,
+ group_full_path: group.full_path
+ }
+
+ # rubocop:disable Rails/Output
+ if dry_run
+ puts "Dry run complete. #{attrs.size} rows would be affected"
+ logger.info(message)
+ elsif affected_row_count.eql?(attrs.size)
+ puts "Finished. #{attrs.size} rows affected"
+ logger.info(message)
+ else
+ # :nocov:
+ puts "ERROR. #{affected_row_count} tokens deleted, #{attrs.size} tokens should have been deleted"
+ logger.warn(message)
+ # :nocov:
+ end
+ # rubocop:enable Rails/Output
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/closing_issue_extractor.rb b/lib/gitlab/closing_issue_extractor.rb
index 8e624215065..7104de2a3c3 100644
--- a/lib/gitlab/closing_issue_extractor.rb
+++ b/lib/gitlab/closing_issue_extractor.rb
@@ -17,7 +17,6 @@ module Gitlab
def closed_by_message(message)
return [] if message.nil?
- return [] unless @project.autoclose_referenced_issues
closing_statements = []
message.scan(ISSUE_CLOSING_REGEX) do
@@ -27,8 +26,9 @@ module Gitlab
@extractor.analyze(closing_statements.join(" "))
@extractor.issues.reject do |issue|
- # Don't extract issues from the project this project was forked from
- @extractor.project.forked_from?(issue.project)
+ @extractor.project.forked_from?(issue.project) ||
+ !issue.project.autoclose_referenced_issues ||
+ !issue.project.issues_enabled?
end
end
end
diff --git a/lib/gitlab/cluster/lifecycle_events.rb b/lib/gitlab/cluster/lifecycle_events.rb
index e423d1f17da..be08ada9d2f 100644
--- a/lib/gitlab/cluster/lifecycle_events.rb
+++ b/lib/gitlab/cluster/lifecycle_events.rb
@@ -4,6 +4,11 @@ require_relative '../utils' # Gitlab::Utils
module Gitlab
module Cluster
+ # We take advantage of the fact that the application is pre-loaded in the primary
+ # process. If it's a pre-fork server like Puma, this will be the Puma master process.
+ # Otherwise it is the worker itself such as for Sidekiq.
+ PRIMARY_PID = $$
+
#
# LifecycleEvents lets Rails initializers register application startup hooks
# that are sensitive to forking. For example, to defer the creation of
diff --git a/lib/gitlab/config/entry/composable_hash.rb b/lib/gitlab/config/entry/composable_hash.rb
index 9531b7e56fd..0b892fd4552 100644
--- a/lib/gitlab/config/entry/composable_hash.rb
+++ b/lib/gitlab/config/entry/composable_hash.rb
@@ -25,9 +25,9 @@ module Gitlab
entry_class_name = entry_class.name.demodulize.underscore
factory = ::Gitlab::Config::Entry::Factory.new(entry_class)
- .value(config || {})
+ .value(config.nil? ? {} : config)
.with(key: name, parent: self, description: "#{name} #{entry_class_name} definition") # rubocop:disable CodeReuse/ActiveRecord
- .metadata(name: name)
+ .metadata(composable_metadata.merge(name: name))
@entries[name] = factory.create!
end
@@ -38,9 +38,15 @@ module Gitlab
end
end
+ private
+
def composable_class(name, config)
opt(:composable_class)
end
+
+ def composable_metadata
+ {}
+ end
end
end
end
diff --git a/lib/gitlab/config/entry/validators.rb b/lib/gitlab/config/entry/validators.rb
index cc24ae837f3..337cfbc5287 100644
--- a/lib/gitlab/config/entry/validators.rb
+++ b/lib/gitlab/config/entry/validators.rb
@@ -304,6 +304,7 @@ module Gitlab
end
end
+ # This will be removed with the FF `ci_variables_refactoring_to_variable`.
class VariablesValidator < ActiveModel::EachValidator
include LegacyValidationHelpers
@@ -336,6 +337,18 @@ module Gitlab
end
end
+ class AlphanumericValidator < ActiveModel::EachValidator
+ def self.validate(value)
+ value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(Integer)
+ end
+
+ def validate_each(record, attribute, value)
+ unless self.class.validate(value)
+ record.errors.add(attribute, 'must be an alphanumeric string')
+ end
+ end
+ end
+
class ExpressionValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless value.is_a?(String) && ::Gitlab::Ci::Pipeline::Expression::Statement.new(value).valid?
diff --git a/lib/gitlab/container_repository/tags/cache.rb b/lib/gitlab/container_repository/tags/cache.rb
index ff457fb9219..47a6e67a5a1 100644
--- a/lib/gitlab/container_repository/tags/cache.rb
+++ b/lib/gitlab/container_repository/tags/cache.rb
@@ -48,14 +48,14 @@ module Gitlab
::Gitlab::Redis::Cache.with do |redis|
# we use a pipeline instead of a MSET because each tag has
# a specific ttl
- redis.pipelined do
+ redis.pipelined do |pipeline|
cacheable_tags.each do |tag|
created_at = tag.created_at
# ttl is the max_ttl_in_seconds reduced by the number
# of seconds that the tag has already existed
ttl = max_ttl_in_seconds - (now - created_at).seconds
ttl = ttl.to_i
- redis.set(cache_key(tag), created_at.rfc3339, ex: ttl) if ttl > 0
+ pipeline.set(cache_key(tag), created_at.rfc3339, ex: ttl) if ttl > 0
end
end
end
diff --git a/lib/gitlab/data_builder/build.rb b/lib/gitlab/data_builder/build.rb
index 91e6fc11a53..4640f85bb0a 100644
--- a/lib/gitlab/data_builder/build.rb
+++ b/lib/gitlab/data_builder/build.rb
@@ -24,7 +24,7 @@ module Gitlab
# Leaving this way to have backward compatibility
build_id: build.id,
build_name: build.name,
- build_stage: build.stage,
+ build_stage: build.stage_name,
build_status: build.status,
build_created_at: build.created_at,
build_started_at: build.started_at,
diff --git a/lib/gitlab/data_builder/pipeline.rb b/lib/gitlab/data_builder/pipeline.rb
index 2c124b07006..320ebe5e80f 100644
--- a/lib/gitlab/data_builder/pipeline.rb
+++ b/lib/gitlab/data_builder/pipeline.rb
@@ -52,7 +52,8 @@ module Gitlab
runner: :tags,
job_artifacts_archive: [],
user: [],
- metadata: []
+ metadata: [],
+ ci_stage: []
}
}
)
@@ -110,7 +111,7 @@ module Gitlab
def build_hook_attrs(build)
{
id: build.id,
- stage: build.stage,
+ stage: build.stage_name,
name: build.name,
status: build.status,
created_at: build.created_at,
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 8703365b678..dd84127459d 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -242,7 +242,8 @@ module Gitlab
# in such cases it is fine to ignore such connections
return unless db_config
- primary_model = self.database_base_models.fetch(db_config.name.to_sym)
+ db_config_name = db_config.name.delete_suffix(LoadBalancing::LoadBalancer::REPLICA_SUFFIX)
+ primary_model = self.database_base_models.fetch(db_config_name.to_sym)
self.schemas_to_base_models.select do |_, child_models|
child_models.any? do |child_model|
diff --git a/lib/gitlab/database/background_migration/batched_migration.rb b/lib/gitlab/database/background_migration/batched_migration.rb
index 6aed1eed994..45f52765d0f 100644
--- a/lib/gitlab/database/background_migration/batched_migration.rb
+++ b/lib/gitlab/database/background_migration/batched_migration.rb
@@ -8,6 +8,7 @@ module Gitlab
BATCH_CLASS_MODULE = "#{JOB_CLASS_MODULE}::BatchingStrategies"
MAXIMUM_FAILED_RATIO = 0.5
MINIMUM_JOBS = 50
+ FINISHED_PROGRESS_VALUE = 100
self.table_name = :batched_background_migrations
@@ -24,6 +25,7 @@ module Gitlab
scope :queue_order, -> { order(id: :asc) }
scope :queued, -> { with_statuses(:active, :paused) }
+ scope :ordered_by_created_at_desc, -> { order(created_at: :desc) }
# on_hold_until is a temporary runtime status which puts execution "on hold"
scope :executable, -> { with_status(:active).where('on_hold_until IS NULL OR on_hold_until < NOW()') }
@@ -57,11 +59,11 @@ module Gitlab
state :finalizing, value: 5
event :pause do
- transition any => :paused
+ transition [:active, :paused] => :paused
end
event :execute do
- transition any => :active
+ transition [:active, :paused, :failed] => :active
end
event :finish do
@@ -231,7 +233,15 @@ module Gitlab
"BatchedMigration[id: #{id}]"
end
+ # Computes an estimation of the progress of the migration in percents.
+ #
+ # Because `total_tuple_count` is an estimation of the tuples based on DB statistics
+ # when the migration is complete there can actually be more or less tuples that initially
+ # estimated as `total_tuple_count` so the progress may not show 100%. For that reason when
+ # we know migration completed successfully, we just return the 100 value
def progress
+ return FINISHED_PROGRESS_VALUE if finished?
+
return unless total_tuple_count.to_i > 0
100 * migrated_tuple_count / total_tuple_count
diff --git a/lib/gitlab/database/background_migration/health_status.rb b/lib/gitlab/database/background_migration/health_status.rb
index 9a283074b32..506d2996ad5 100644
--- a/lib/gitlab/database/background_migration/health_status.rb
+++ b/lib/gitlab/database/background_migration/health_status.rb
@@ -18,7 +18,7 @@ module Gitlab
indicator.new(migration.health_context).evaluate
rescue StandardError => e
Gitlab::ErrorTracking.track_exception(e, migration_id: migration.id,
- job_class_name: migration.job_class_name)
+ job_class_name: migration.job_class_name)
Signals::Unknown.new(indicator, reason: "unexpected error: #{e.message} (#{e.class})")
end
diff --git a/lib/gitlab/database/batch_average_counter.rb b/lib/gitlab/database/batch_average_counter.rb
new file mode 100644
index 00000000000..9cb1e34ab67
--- /dev/null
+++ b/lib/gitlab/database/batch_average_counter.rb
@@ -0,0 +1,103 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ class BatchAverageCounter
+ COLUMN_FALLBACK = 0
+ DEFAULT_BATCH_SIZE = 1_000
+ FALLBACK = -1
+ MAX_ALLOWED_LOOPS = 10_000
+ OFFSET_BY_ONE = 1
+ SLEEP_TIME_IN_SECONDS = 0.01 # 10 msec sleep
+
+ attr_reader :relation, :column
+
+ def initialize(relation, column)
+ @relation = relation
+ @column = wrap_column(relation, column)
+ end
+
+ def count(batch_size: nil)
+ raise 'BatchAverageCounter can not be run inside a transaction' if transaction_open?
+
+ batch_size = batch_size.presence || DEFAULT_BATCH_SIZE
+
+ start = column_start
+ finish = column_finish
+
+ total_sum = 0
+ total_records = 0
+
+ batch_start = start
+
+ while batch_start < finish
+ begin
+ batch_end = [batch_start + batch_size, finish].min
+ batch_relation = build_relation_batch(batch_start, batch_end)
+
+ # We use `sum` and `count` instead of `average` here to not run into an "average of averages"
+ # problem as batches will have different sizes, so we are essentially summing up the values for
+ # each batch separately, and then dividing that result on the total number of records.
+ batch_sum, batch_count = batch_relation.pick(column.sum, column.count)
+
+ total_sum += batch_sum.to_i
+ total_records += batch_count
+
+ batch_start = batch_end
+ rescue ActiveRecord::QueryCanceled => error # rubocop:disable Database/RescueQueryCanceled
+ # retry with a safe batch size & warmer cache
+ if batch_size >= 2 * DEFAULT_BATCH_SIZE
+ batch_size /= 2
+ else
+ log_canceled_batch_fetch(batch_start, batch_relation.to_sql, error)
+
+ return FALLBACK
+ end
+ end
+
+ sleep(SLEEP_TIME_IN_SECONDS)
+ end
+
+ return FALLBACK if total_records == 0
+
+ total_sum.to_f / total_records
+ end
+
+ private
+
+ def column_start
+ relation.unscope(:group, :having).minimum(column) || COLUMN_FALLBACK
+ end
+
+ def column_finish
+ (relation.unscope(:group, :having).maximum(column) || COLUMN_FALLBACK) + OFFSET_BY_ONE
+ end
+
+ def build_relation_batch(start, finish)
+ relation.where(column.between(start...finish))
+ end
+
+ def log_canceled_batch_fetch(batch_start, query, error)
+ Gitlab::AppJsonLogger
+ .error(
+ event: 'batch_count',
+ relation: relation.table_name,
+ operation: 'average',
+ start: batch_start,
+ query: query,
+ message: "Query has been canceled with message: #{error.message}"
+ )
+ end
+
+ def transaction_open?
+ relation.connection.transaction_open?
+ end
+
+ def wrap_column(relation, column)
+ return column if column.is_a?(Arel::Attributes::Attribute)
+
+ relation.arel_table[column]
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/batch_count.rb b/lib/gitlab/database/batch_count.rb
index 92a41bb36ee..7a064fb4005 100644
--- a/lib/gitlab/database/batch_count.rb
+++ b/lib/gitlab/database/batch_count.rb
@@ -35,6 +35,10 @@ module Gitlab
BatchCounter.new(relation, column: column).count(batch_size: batch_size, start: start, finish: finish)
end
+ def batch_count_with_timeout(relation, column = nil, batch_size: nil, start: nil, finish: nil, timeout: nil, partial_results: nil)
+ BatchCounter.new(relation, column: column).count_with_timeout(batch_size: batch_size, start: start, finish: finish, timeout: timeout, partial_results: partial_results)
+ end
+
def batch_distinct_count(relation, column = nil, batch_size: nil, start: nil, finish: nil)
BatchCounter.new(relation, column: column).count(mode: :distinct, batch_size: batch_size, start: start, finish: finish)
end
@@ -44,7 +48,7 @@ module Gitlab
end
def batch_average(relation, column, batch_size: nil, start: nil, finish: nil)
- BatchCounter.new(relation, column: nil, operation: :average, operation_args: [column]).count(batch_size: batch_size, start: start, finish: finish)
+ BatchAverageCounter.new(relation, column).count(batch_size: batch_size)
end
class << self
diff --git a/lib/gitlab/database/batch_counter.rb b/lib/gitlab/database/batch_counter.rb
index 522b598cd9d..abb62140503 100644
--- a/lib/gitlab/database/batch_counter.rb
+++ b/lib/gitlab/database/batch_counter.rb
@@ -6,7 +6,6 @@ module Gitlab
FALLBACK = -1
MIN_REQUIRED_BATCH_SIZE = 1_250
DEFAULT_SUM_BATCH_SIZE = 1_000
- DEFAULT_AVERAGE_BATCH_SIZE = 1_000
MAX_ALLOWED_LOOPS = 10_000
SLEEP_TIME_IN_SECONDS = 0.01 # 10 msec sleep
ALLOWED_MODES = [:itself, :distinct].freeze
@@ -27,12 +26,19 @@ module Gitlab
def unwanted_configuration?(finish, batch_size, start)
(@operation == :count && batch_size <= MIN_REQUIRED_BATCH_SIZE) ||
(@operation == :sum && batch_size < DEFAULT_SUM_BATCH_SIZE) ||
- (@operation == :average && batch_size < DEFAULT_AVERAGE_BATCH_SIZE) ||
(finish - start) / batch_size >= MAX_ALLOWED_LOOPS ||
start >= finish
end
def count(batch_size: nil, mode: :itself, start: nil, finish: nil)
+ result = count_with_timeout(batch_size: batch_size, mode: mode, start: start, finish: finish, timeout: nil)
+
+ return FALLBACK if result[:status] != :completed
+
+ result[:count]
+ end
+
+ def count_with_timeout(batch_size: nil, mode: :itself, start: nil, finish: nil, timeout: nil, partial_results: nil)
raise 'BatchCount can not be run inside a transaction' if transaction_open?
check_mode!(mode)
@@ -44,12 +50,20 @@ module Gitlab
finish = actual_finish(finish)
raise "Batch counting expects positive values only for #{@column}" if start < 0 || finish < 0
- return FALLBACK if unwanted_configuration?(finish, batch_size, start)
+ return { status: :bad_config } if unwanted_configuration?(finish, batch_size, start)
- results = nil
+ results = partial_results
batch_start = start
+ start_time = ::Gitlab::Metrics::System.monotonic_time.seconds
+
while batch_start < finish
+
+ # Timeout elapsed, return partial result so the caller can continue later
+ if timeout && ::Gitlab::Metrics::System.monotonic_time.seconds - start_time > timeout
+ return { status: :timeout, partial_results: results, continue_from: batch_start }
+ end
+
begin
batch_end = [batch_start + batch_size, finish].min
batch_relation = build_relation_batch(batch_start, batch_end, mode)
@@ -62,14 +76,14 @@ module Gitlab
batch_size /= 2
else
log_canceled_batch_fetch(batch_start, mode, batch_relation.to_sql, error)
- return FALLBACK
+ return { status: :cancelled }
end
end
sleep(SLEEP_TIME_IN_SECONDS)
end
- results
+ { status: :completed, count: results }
end
def transaction_open?
@@ -94,7 +108,6 @@ module Gitlab
def batch_size_for_mode_and_operation(mode, operation)
return DEFAULT_SUM_BATCH_SIZE if operation == :sum
- return DEFAULT_AVERAGE_BATCH_SIZE if operation == :average
mode == :distinct ? DEFAULT_DISTINCT_BATCH_SIZE : DEFAULT_BATCH_SIZE
end
@@ -132,10 +145,6 @@ module Gitlab
message: "Query has been canceled with message: #{error.message}"
)
end
-
- def not_group_by_query?
- !@relation.is_a?(ActiveRecord::Relation) || @relation.group_values.blank?
- end
end
end
end
diff --git a/lib/gitlab/database/gitlab_schemas.yml b/lib/gitlab/database/gitlab_schemas.yml
index d05eee7d6e6..5725d7a4503 100644
--- a/lib/gitlab/database/gitlab_schemas.yml
+++ b/lib/gitlab/database/gitlab_schemas.yml
@@ -91,6 +91,7 @@ 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
@@ -182,6 +183,7 @@ 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
@@ -228,6 +230,7 @@ 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
@@ -315,6 +318,7 @@ 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
@@ -380,6 +384,7 @@ 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
@@ -399,6 +404,7 @@ 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
@@ -479,6 +485,7 @@ 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
@@ -549,6 +556,7 @@ 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
diff --git a/lib/gitlab/database/lock_writes_manager.rb b/lib/gitlab/database/lock_writes_manager.rb
index cd483d616bb..fe75cd763b4 100644
--- a/lib/gitlab/database/lock_writes_manager.rb
+++ b/lib/gitlab/database/lock_writes_manager.rb
@@ -5,42 +5,63 @@ module Gitlab
class LockWritesManager
TRIGGER_FUNCTION_NAME = 'gitlab_schema_prevent_write'
- def initialize(table_name:, connection:, database_name:, logger: nil)
+ 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
+ end
+
+ def table_locked_for_writes?(table_name)
+ query = <<~SQL
+ SELECT COUNT(*) from information_schema.triggers
+ WHERE event_object_table = '#{table_name}'
+ AND trigger_name = '#{write_trigger_name(table_name)}'
+ SQL
+
+ connection.select_value(query) == 3
end
def lock_writes
+ if table_locked_for_writes?(table_name)
+ logger&.info "Skipping lock_writes, because #{table_name} is already locked for writes"
+ return
+ end
+
logger&.info "Database: '#{database_name}', Table: '#{table_name}': Lock Writes".color(:yellow)
- sql = <<-SQL
- DROP TRIGGER IF EXISTS #{write_trigger_name(table_name)} ON #{table_name};
+ sql_statement = <<~SQL
CREATE TRIGGER #{write_trigger_name(table_name)}
BEFORE INSERT OR UPDATE OR DELETE OR TRUNCATE
ON #{table_name}
FOR EACH STATEMENT EXECUTE FUNCTION #{TRIGGER_FUNCTION_NAME}();
SQL
- with_retries(connection) do
- connection.execute(sql)
- end
+ execute_sql_statement(sql_statement)
end
def unlock_writes
logger&.info "Database: '#{database_name}', Table: '#{table_name}': Allow Writes".color(:green)
- sql = <<-SQL
- DROP TRIGGER IF EXISTS #{write_trigger_name(table_name)} ON #{table_name}
+ sql_statement = <<~SQL
+ DROP TRIGGER IF EXISTS #{write_trigger_name(table_name)} ON #{table_name};
SQL
- with_retries(connection) do
- connection.execute(sql)
- end
+ execute_sql_statement(sql_statement)
end
private
- attr_reader :table_name, :connection, :database_name, :logger
+ attr_reader :table_name, :connection, :database_name, :logger, :dry_run
+
+ def execute_sql_statement(sql)
+ if dry_run
+ logger&.info sql
+ else
+ with_retries(connection) do
+ connection.execute(sql)
+ end
+ end
+ end
def with_retries(connection, &block)
with_statement_timeout_retries do
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index db39524f4f6..e574422ce11 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -936,13 +936,14 @@ module Gitlab
def revert_backfill_conversion_of_integer_to_bigint(table, columns, primary_key: :id)
columns = Array.wrap(columns)
- conditions = ActiveRecord::Base.sanitize_sql([
- 'job_class_name = :job_class_name AND table_name = :table_name AND column_name = :column_name AND job_arguments = :job_arguments',
- job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
- table_name: table,
- column_name: primary_key,
- job_arguments: [columns, columns.map { |column| convert_to_bigint_column(column) }].to_json
- ])
+ conditions = ActiveRecord::Base.sanitize_sql(
+ [
+ 'job_class_name = :job_class_name AND table_name = :table_name AND column_name = :column_name AND job_arguments = :job_arguments',
+ job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
+ table_name: table,
+ column_name: primary_key,
+ job_arguments: [columns, columns.map { |column| convert_to_bigint_column(column) }].to_json
+ ])
execute("DELETE FROM batched_background_migrations WHERE #{conditions}")
end
diff --git a/lib/gitlab/database/migrations/base_background_runner.rb b/lib/gitlab/database/migrations/base_background_runner.rb
index a9440cafd30..76982a9da9b 100644
--- a/lib/gitlab/database/migrations/base_background_runner.rb
+++ b/lib/gitlab/database/migrations/base_background_runner.rb
@@ -40,7 +40,7 @@ module Gitlab
instrumentation = Instrumentation.new(result_dir: per_background_migration_result_dir)
batch_names = (1..).each.lazy.map { |i| "batch_#{i}" }
- jobs.shuffle.each do |j|
+ jobs.each do |j|
break if run_until <= Time.current
instrumentation.observe(version: nil,
diff --git a/lib/gitlab/database/migrations/test_background_runner.rb b/lib/gitlab/database/migrations/test_background_runner.rb
index f7713237b38..6da2e098d43 100644
--- a/lib/gitlab/database/migrations/test_background_runner.rb
+++ b/lib/gitlab/database/migrations/test_background_runner.rb
@@ -15,6 +15,7 @@ module Gitlab
def jobs_by_migration_name
traditional_background_migrations.group_by { |j| class_name_for_job(j) }
+ .transform_values(&:shuffle)
end
private
diff --git a/lib/gitlab/database/migrations/test_batched_background_runner.rb b/lib/gitlab/database/migrations/test_batched_background_runner.rb
index f38d847b0e8..c27ae6a2c5d 100644
--- a/lib/gitlab/database/migrations/test_batched_background_runner.rb
+++ b/lib/gitlab/database/migrations/test_batched_background_runner.rb
@@ -4,6 +4,7 @@ module Gitlab
module Database
module Migrations
class TestBatchedBackgroundRunner < BaseBackgroundRunner
+ include Gitlab::Database::DynamicModelHelpers
attr_reader :connection
def initialize(result_dir:, connection:)
@@ -18,31 +19,81 @@ module Gitlab
.to_h do |migration|
batching_strategy = migration.batch_class.new(connection: connection)
- all_migration_jobs = []
+ smallest_batch_start = migration.next_min_value
- min_value = migration.next_min_value
+ table_max_value = define_batchable_model(migration.table_name, connection: connection)
+ .maximum(migration.column_name)
- while (next_bounds = batching_strategy.next_batch(
- migration.table_name,
- migration.column_name,
- batch_min_value: min_value,
- batch_size: migration.batch_size,
- job_arguments: migration.job_arguments
- ))
+ largest_batch_start = table_max_value - migration.batch_size
+
+ # variance is the portion of the batch range that we shrink between variance * 0 and variance * 1
+ # to pick actual batches to sample.
+ variance = largest_batch_start - smallest_batch_start
+
+ batch_starts = uniform_fractions
+ .lazy # frac varies from 0 to 1, values in smallest_batch_start..largest_batch_start
+ .map { |frac| (variance * frac).to_i + smallest_batch_start }
+
+ # Track previously run batches so that we stop sampling if a new batch would intersect an older one
+ completed_batches = []
+
+ jobs_to_sample = batch_starts
+ # Stop sampling if a batch would intersect a previous batch
+ .take_while { |start| completed_batches.none? { |batch| batch.cover?(start) } }
+ .map do |batch_start|
+ next_bounds = batching_strategy.next_batch(
+ migration.table_name,
+ migration.column_name,
+ batch_min_value: batch_start,
+ batch_size: migration.batch_size,
+ job_arguments: migration.job_arguments
+ )
batch_min, batch_max = next_bounds
- all_migration_jobs << migration.create_batched_job!(batch_min, batch_max)
- min_value = batch_max + 1
+ job = migration.create_batched_job!(batch_min, batch_max)
+
+ completed_batches << (batch_min..batch_max)
+
+ job
end
- [migration.job_class_name, all_migration_jobs]
+ [migration.job_class_name, jobs_to_sample]
end
end
def run_job(job)
Gitlab::Database::BackgroundMigration::BatchedMigrationWrapper.new(connection: connection).perform(job)
end
+
+ def uniform_fractions
+ Enumerator.new do |y|
+ # Generates equally distributed fractions between 0 and 1, with increasing detail as more are pulled from
+ # the enumerator.
+ # 0, 1 (special case)
+ # 1/2
+ # 1/4, 3/4
+ # 1/8, 3/8, 5/8, 7/8
+ # etc.
+ # The pattern here is at each outer loop, the denominator multiplies by 2, and at each inner loop,
+ # the numerator counts up all odd numbers 1 <= n < denominator.
+ y << 0
+ y << 1
+
+ # denominators are each increasing power of 2
+ denominators = (1..).lazy.map { |exponent| 2**exponent }
+
+ denominators.each do |denominator|
+ # Numerators at the current step are all odd numbers between 1 and the denominator
+ numerators = (1..denominator).step(2)
+
+ numerators.each do |numerator|
+ next_frac = numerator.fdiv(denominator)
+ y << next_frac
+ end
+ end
+ end
+ end
end
end
end
diff --git a/lib/gitlab/database/partitioning.rb b/lib/gitlab/database/partitioning.rb
index 92825d41599..6314aff9914 100644
--- a/lib/gitlab/database/partitioning.rb
+++ b/lib/gitlab/database/partitioning.rb
@@ -33,6 +33,18 @@ module Gitlab
PartitionManager.new(model).sync_partitions
end
+ unless only_on
+ models_to_sync.each do |model|
+ next if model < ::Gitlab::Database::SharedModel && !(model < TableWithoutModel)
+
+ Gitlab::Database::EachDatabase.each_database_connection do |connection, connection_name|
+ if connection_name != model.connection_db_config.name
+ PartitionManager.new(model, connection: connection).sync_partitions
+ end
+ end
+ end
+ end
+
Gitlab::AppLogger.info(message: 'Finished sync of dynamic postgres partitions')
end
diff --git a/lib/gitlab/database/partitioning/convert_table_to_first_list_partition.rb b/lib/gitlab/database/partitioning/convert_table_to_first_list_partition.rb
new file mode 100644
index 00000000000..f45cf02ec9b
--- /dev/null
+++ b/lib/gitlab/database/partitioning/convert_table_to_first_list_partition.rb
@@ -0,0 +1,214 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module Partitioning
+ class ConvertTableToFirstListPartition
+ UnableToPartition = Class.new(StandardError)
+
+ include Gitlab::Database::MigrationHelpers
+
+ SQL_STATEMENT_SEPARATOR = ";\n\n"
+
+ attr_reader :partitioning_column, :table_name, :parent_table_name, :zero_partition_value
+
+ def initialize(migration_context:, table_name:, parent_table_name:, partitioning_column:, zero_partition_value:)
+ @migration_context = migration_context
+ @connection = migration_context.connection
+ @table_name = table_name
+ @parent_table_name = parent_table_name
+ @partitioning_column = partitioning_column
+ @zero_partition_value = zero_partition_value
+ end
+
+ def prepare_for_partitioning
+ assert_existing_constraints_partitionable
+
+ add_partitioning_check_constraint
+ end
+
+ def revert_preparation_for_partitioning
+ migration_context.remove_check_constraint(table_name, partitioning_constraint.name)
+ end
+
+ def partition
+ assert_existing_constraints_partitionable
+ assert_partitioning_constraint_present
+ create_parent_table
+ attach_foreign_keys_to_parent
+
+ migration_context.with_lock_retries(raise_on_exhaustion: true) do
+ migration_context.execute(sql_to_convert_table)
+ end
+ end
+
+ def revert_partitioning
+ migration_context.with_lock_retries(raise_on_exhaustion: true) do
+ migration_context.execute(<<~SQL)
+ ALTER TABLE #{connection.quote_table_name(parent_table_name)}
+ DETACH PARTITION #{connection.quote_table_name(table_name)};
+ SQL
+
+ alter_sequences_sql = alter_sequence_statements(old_table: parent_table_name, new_table: table_name)
+ .join(SQL_STATEMENT_SEPARATOR)
+
+ migration_context.execute(alter_sequences_sql)
+
+ # This takes locks for all the foreign keys that the parent table had.
+ # However, those same locks were taken while detaching the partition, and we can't avoid that.
+ # If we dropped the foreign key before detaching the partition to avoid this locking,
+ # the drop would cascade to the child partitions and drop their foreign keys as well
+ migration_context.drop_table(parent_table_name)
+ end
+
+ add_partitioning_check_constraint
+ end
+
+ private
+
+ attr_reader :connection, :migration_context
+
+ delegate :quote_table_name, :quote_column_name, to: :connection
+
+ def sql_to_convert_table
+ # The critical statement here is the attach_table_to_parent statement.
+ # The following statements could be run in a later transaction,
+ # but they acquire the same locks so it's much faster to incude them
+ # here.
+ [
+ attach_table_to_parent_statement,
+ alter_sequence_statements(old_table: table_name, new_table: parent_table_name),
+ remove_constraint_statement
+ ].flatten.join(SQL_STATEMENT_SEPARATOR)
+ end
+
+ def table_identifier
+ "#{connection.current_schema}.#{table_name}"
+ end
+
+ def assert_existing_constraints_partitionable
+ violating_constraints = Gitlab::Database::PostgresConstraint
+ .by_table_identifier(table_identifier)
+ .primary_or_unique_constraints
+ .not_including_column(partitioning_column)
+ .to_a
+
+ return if violating_constraints.empty?
+
+ violation_messages = violating_constraints.map { |c| "#{c.name} on (#{c.column_names.join(', ')})" }
+
+ raise UnableToPartition, <<~MSG
+ Constraints on #{table_name} are incompatible with partitioning on #{partitioning_column}
+
+ All primary key and unique constraints must include the partitioning column.
+ Violations:
+ #{violation_messages.join("\n")}
+ MSG
+ end
+
+ def partitioning_constraint
+ constraints_on_column = Gitlab::Database::PostgresConstraint
+ .by_table_identifier(table_identifier)
+ .check_constraints
+ .valid
+ .including_column(partitioning_column)
+
+ constraints_on_column.to_a.find do |constraint|
+ constraint.definition == "CHECK ((#{partitioning_column} = #{zero_partition_value}))"
+ end
+ end
+
+ def assert_partitioning_constraint_present
+ return if partitioning_constraint
+
+ raise UnableToPartition, <<~MSG
+ Table #{table_name} is not ready for partitioning.
+ Before partitioning, a check constraint must enforce that (#{partitioning_column} = #{zero_partition_value})
+ MSG
+ end
+
+ def add_partitioning_check_constraint
+ return if partitioning_constraint.present?
+
+ check_body = "#{partitioning_column} = #{connection.quote(zero_partition_value)}"
+ # Any constraint name would work. The constraint is found based on its definition before partitioning
+ migration_context.add_check_constraint(table_name, check_body, 'partitioning_constraint')
+
+ raise UnableToPartition, 'Error adding partitioning constraint' unless partitioning_constraint.present?
+ end
+
+ def create_parent_table
+ migration_context.execute(<<~SQL)
+ CREATE TABLE IF NOT EXISTS #{quote_table_name(parent_table_name)} (
+ LIKE #{quote_table_name(table_name)} INCLUDING ALL
+ ) PARTITION BY LIST(#{quote_column_name(partitioning_column)})
+ SQL
+ end
+
+ def attach_foreign_keys_to_parent
+ migration_context.foreign_keys(table_name).each do |fk|
+ # At this point no other connection knows about the parent table.
+ # Thus the only contended lock in the following transaction is on fk.to_table.
+ # So a deadlock is impossible.
+
+ # If we're rerunning this migration after a failure to acquire a lock, the foreign key might already exist.
+ # Don't try to recreate it in that case
+ if migration_context.foreign_keys(parent_table_name)
+ .any? { |p_fk| p_fk.options[:name] == fk.options[:name] }
+ next
+ end
+
+ migration_context.with_lock_retries(raise_on_exhaustion: true) do
+ migration_context.add_foreign_key(parent_table_name, fk.to_table, **fk.options)
+ end
+ end
+ end
+
+ def attach_table_to_parent_statement
+ <<~SQL
+ ALTER TABLE #{quote_table_name(parent_table_name)}
+ ATTACH PARTITION #{table_name}
+ FOR VALUES IN (#{zero_partition_value})
+ SQL
+ end
+
+ def alter_sequence_statements(old_table:, new_table:)
+ sequences_owned_by(old_table).map do |seq_info|
+ seq_name, column_name = seq_info.values_at(:name, :column_name)
+ <<~SQL.chomp
+ ALTER SEQUENCE #{quote_table_name(seq_name)} OWNED BY #{quote_table_name(new_table)}.#{quote_column_name(column_name)}
+ SQL
+ end
+ end
+
+ def remove_constraint_statement
+ <<~SQL
+ ALTER TABLE #{quote_table_name(parent_table_name)}
+ DROP CONSTRAINT #{quote_table_name(partitioning_constraint.name)}
+ SQL
+ end
+
+ # TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/373887
+ def sequences_owned_by(table_name)
+ sequence_data = connection.exec_query(<<~SQL, nil, [table_name])
+ SELECT seq_pg_class.relname AS seq_name,
+ dep_pg_class.relname AS table_name,
+ pg_attribute.attname AS col_name
+ FROM pg_class seq_pg_class
+ INNER JOIN pg_depend ON seq_pg_class.oid = pg_depend.objid
+ INNER JOIN pg_class dep_pg_class ON pg_depend.refobjid = dep_pg_class.oid
+ INNER JOIN pg_attribute ON dep_pg_class.oid = pg_attribute.attrelid
+ AND pg_depend.refobjsubid = pg_attribute.attnum
+ WHERE seq_pg_class.relkind = 'S'
+ AND dep_pg_class.relname = $1
+ SQL
+
+ sequence_data.map do |seq_info|
+ name, column_name = seq_info.values_at('seq_name', 'col_name')
+ { name: name, column_name: column_name }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/partitioning/partition_manager.rb b/lib/gitlab/database/partitioning/partition_manager.rb
index aac91eaadb1..55ca9ff8645 100644
--- a/lib/gitlab/database/partitioning/partition_manager.rb
+++ b/lib/gitlab/database/partitioning/partition_manager.rb
@@ -10,12 +10,15 @@ module Gitlab
MANAGEMENT_LEASE_KEY = 'database_partition_management_%s'
RETAIN_DETACHED_PARTITIONS_FOR = 1.week
- def initialize(model)
+ def initialize(model, connection: nil)
@model = model
- @connection_name = model.connection.pool.db_config.name
+ @connection = connection || model.connection
+ @connection_name = @connection.pool.db_config.name
end
def sync_partitions
+ return skip_synching_partitions unless table_partitioned?
+
Gitlab::AppLogger.info(
message: "Checking state of dynamic postgres partitions",
table_name: model.table_name,
@@ -43,9 +46,7 @@ module Gitlab
private
- attr_reader :model
-
- delegate :connection, to: :model
+ attr_reader :model, :connection
def missing_partitions
return [] unless connection.table_exists?(model.table_name)
@@ -129,6 +130,20 @@ module Gitlab
connection: connection
).run(&block)
end
+
+ def table_partitioned?
+ Gitlab::Database::SharedModel.using_connection(connection) do
+ Gitlab::Database::PostgresPartitionedTable.find_by_name_in_current_schema(model.table_name).present?
+ end
+ end
+
+ def skip_synching_partitions
+ Gitlab::AppLogger.warn(
+ message: "Skipping synching partitions",
+ table_name: model.table_name,
+ connection_name: @connection_name
+ )
+ end
end
end
end
diff --git a/lib/gitlab/database/partitioning/single_numeric_list_partition.rb b/lib/gitlab/database/partitioning/single_numeric_list_partition.rb
index 23ac73a0e53..4e38eea963b 100644
--- a/lib/gitlab/database/partitioning/single_numeric_list_partition.rb
+++ b/lib/gitlab/database/partitioning/single_numeric_list_partition.rb
@@ -8,7 +8,7 @@ module Gitlab
def self.from_sql(table, partition_name, definition)
# A list partition can support multiple values, but we only support a single number
- matches = definition.match(/FOR VALUES IN \('(?<value>\d+)'\)/)
+ matches = definition.match(/FOR VALUES IN \('?(?<value>\d+)'?\)/)
raise ArgumentError, 'Unknown partition definition' unless matches
@@ -29,17 +29,21 @@ module Gitlab
@partition_name || "#{table}_#{value}"
end
+ def data_size
+ execute("SELECT pg_table_size(#{quote(full_partition_name)})").first['pg_table_size']
+ end
+
def to_sql
<<~SQL
CREATE TABLE IF NOT EXISTS #{fully_qualified_partition}
- PARTITION OF #{conn.quote_table_name(table)}
- FOR VALUES IN (#{conn.quote(value)})
+ PARTITION OF #{quote_table_name(table)}
+ FOR VALUES IN (#{quote(value)})
SQL
end
def to_detach_sql
<<~SQL
- ALTER TABLE #{conn.quote_table_name(table)}
+ ALTER TABLE #{quote_table_name(table)}
DETACH PARTITION #{fully_qualified_partition}
SQL
end
@@ -63,8 +67,14 @@ module Gitlab
private
+ delegate :execute, :quote, :quote_table_name, to: :conn, private: true
+
+ def full_partition_name
+ "%s.%s" % [Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA, partition_name]
+ end
+
def fully_qualified_partition
- "%s.%s" % [conn.quote_table_name(Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA), conn.quote_table_name(partition_name)]
+ quote_table_name(full_partition_name)
end
def conn
diff --git a/lib/gitlab/database/partitioning/sliding_list_strategy.rb b/lib/gitlab/database/partitioning/sliding_list_strategy.rb
index 4b5349f0327..5bb34a86d43 100644
--- a/lib/gitlab/database/partitioning/sliding_list_strategy.rb
+++ b/lib/gitlab/database/partitioning/sliding_list_strategy.rb
@@ -14,7 +14,7 @@ module Gitlab
@next_partition_if = next_partition_if
@detach_partition_if = detach_partition_if
- ensure_partitioning_column_ignored!
+ ensure_partitioning_column_ignored_or_readonly!
end
def current_partitions
@@ -26,7 +26,7 @@ module Gitlab
def missing_partitions
if no_partitions_exist?
[initial_partition]
- elsif next_partition_if.call(active_partition.value)
+ elsif next_partition_if.call(active_partition)
[next_partition]
else
[]
@@ -44,7 +44,7 @@ module Gitlab
def extra_partitions
possibly_extra = current_partitions[0...-1] # Never consider the most recent partition
- extra = possibly_extra.take_while { |p| detach_partition_if.call(p.value) }
+ extra = possibly_extra.take_while { |p| detach_partition_if.call(p) }
default_value = current_default_value
if extra.any? { |p| p.value == default_value }
@@ -128,12 +128,17 @@ module Gitlab
Integer(value)
end
- def ensure_partitioning_column_ignored!
- unless model.ignored_columns.include?(partitioning_key.to_s)
- raise "Add #{partitioning_key} to #{model.name}.ignored_columns to use it with SlidingListStrategy"
+ def ensure_partitioning_column_ignored_or_readonly!
+ unless key_ignored_or_readonly?
+ raise "Add #{partitioning_key} to #{model.name}.ignored_columns or " \
+ "mark it as readonly to use it with SlidingListStrategy"
end
end
+ def key_ignored_or_readonly?
+ model.ignored_columns.include?(partitioning_key.to_s) || model.readonly_attribute?(partitioning_key.to_s)
+ end
+
def with_lock_retries(&block)
Gitlab::Database::WithLockRetries.new(
klass: self.class,
diff --git a/lib/gitlab/database/partitioning_migration_helpers/index_helpers.rb b/lib/gitlab/database/partitioning_migration_helpers/index_helpers.rb
index c9a3b5caf79..15b542cf089 100644
--- a/lib/gitlab/database/partitioning_migration_helpers/index_helpers.rb
+++ b/lib/gitlab/database/partitioning_migration_helpers/index_helpers.rb
@@ -77,8 +77,42 @@ module Gitlab
end
end
+ # Finds duplicate indexes for a given schema and table. This finds
+ # indexes where the index definition is identical but the names are
+ # different. Returns an array of arrays containing duplicate index name
+ # pairs.
+ #
+ # Example:
+ #
+ # find_duplicate_indexes('table_name_goes_here')
+ def find_duplicate_indexes(table_name, schema_name: connection.current_schema)
+ find_indexes(table_name, schema_name: schema_name)
+ .group_by { |r| r['index_id'] }
+ .select { |_, v| v.size > 1 }
+ .map { |_, indexes| indexes.map { |index| index['index_name'] } }
+ end
+
private
+ def find_indexes(table_name, schema_name: connection.current_schema)
+ indexes = connection.select_all(<<~SQL, 'SQL', [schema_name, table_name])
+ SELECT n.nspname AS schema_name,
+ c.relname AS table_name,
+ i.relname AS index_name,
+ regexp_replace(pg_get_indexdef(i.oid), 'INDEX .*? USING', '_') AS index_id
+ FROM pg_index x
+ JOIN pg_class c ON c.oid = x.indrelid
+ JOIN pg_class i ON i.oid = x.indexrelid
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
+ WHERE (c.relkind = ANY (ARRAY['r'::"char", 'm'::"char", 'p'::"char"]))
+ AND (i.relkind = ANY (ARRAY['i'::"char", 'I'::"char"]))
+ AND n.nspname = $1
+ AND c.relname = $2;
+ SQL
+
+ indexes.to_a
+ end
+
def find_partitioned_table(table_name)
partitioned_table = Gitlab::Database::PostgresPartitionedTable.find_by_name_in_current_schema(table_name)
diff --git a/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb b/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb
index a541ecf5316..695a5d7ec77 100644
--- a/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb
+++ b/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers.rb
@@ -251,6 +251,54 @@ module Gitlab
create_sync_trigger(source_table_name, trigger_name, function_name)
end
+ def prepare_constraint_for_list_partitioning(table_name:, partitioning_column:, parent_table_name:, initial_partitioning_value:)
+ validate_not_in_transaction!(:prepare_constraint_for_list_partitioning)
+
+ Gitlab::Database::Partitioning::ConvertTableToFirstListPartition
+ .new(migration_context: self,
+ table_name: table_name,
+ parent_table_name: parent_table_name,
+ partitioning_column: partitioning_column,
+ zero_partition_value: initial_partitioning_value
+ ).prepare_for_partitioning
+ end
+
+ def revert_preparing_constraint_for_list_partitioning(table_name:, partitioning_column:, parent_table_name:, initial_partitioning_value:)
+ validate_not_in_transaction!(:revert_preparing_constraint_for_list_partitioning)
+
+ Gitlab::Database::Partitioning::ConvertTableToFirstListPartition
+ .new(migration_context: self,
+ table_name: table_name,
+ parent_table_name: parent_table_name,
+ partitioning_column: partitioning_column,
+ zero_partition_value: initial_partitioning_value
+ ).revert_preparation_for_partitioning
+ end
+
+ def convert_table_to_first_list_partition(table_name:, partitioning_column:, parent_table_name:, initial_partitioning_value:)
+ validate_not_in_transaction!(:convert_table_to_first_list_partition)
+
+ Gitlab::Database::Partitioning::ConvertTableToFirstListPartition
+ .new(migration_context: self,
+ table_name: table_name,
+ parent_table_name: parent_table_name,
+ partitioning_column: partitioning_column,
+ zero_partition_value: initial_partitioning_value
+ ).partition
+ end
+
+ def revert_converting_table_to_first_list_partition(table_name:, partitioning_column:, parent_table_name:, initial_partitioning_value:)
+ validate_not_in_transaction!(:revert_converting_table_to_first_list_partition)
+
+ Gitlab::Database::Partitioning::ConvertTableToFirstListPartition
+ .new(migration_context: self,
+ table_name: table_name,
+ parent_table_name: parent_table_name,
+ partitioning_column: partitioning_column,
+ zero_partition_value: initial_partitioning_value
+ ).revert_partitioning
+ end
+
private
def assert_table_is_allowed(table_name)
diff --git a/lib/gitlab/database/postgres_constraint.rb b/lib/gitlab/database/postgres_constraint.rb
new file mode 100644
index 00000000000..fa590914332
--- /dev/null
+++ b/lib/gitlab/database/postgres_constraint.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ # Backed by the postgres_constraints view
+ class PostgresConstraint < SharedModel
+ IDENTIFIER_REGEX = /^\w+\.\w+$/.freeze
+ self.primary_key = :oid
+
+ scope :check_constraints, -> { where(constraint_type: 'c') }
+ scope :primary_key_constraints, -> { where(constraint_type: 'p') }
+ scope :unique_constraints, -> { where(constraint_type: 'u') }
+ scope :primary_or_unique_constraints, -> { where(constraint_type: %w[u p]) }
+
+ scope :including_column, ->(column) { where("? = ANY(column_names)", column) }
+ scope :not_including_column, ->(column) { where.not("? = ANY(column_names)", column) }
+
+ scope :valid, -> { where(constraint_valid: true) }
+
+ scope :by_table_identifier, ->(identifier) do
+ unless identifier =~ IDENTIFIER_REGEX
+ raise ArgumentError, "Table name is not fully qualified with a schema: #{identifier}"
+ end
+
+ where(table_identifier: identifier)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/query_analyzers/ci/partitioning_analyzer.rb b/lib/gitlab/database/query_analyzers/ci/partitioning_analyzer.rb
new file mode 100644
index 00000000000..c2d5dfc1a15
--- /dev/null
+++ b/lib/gitlab/database/query_analyzers/ci/partitioning_analyzer.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module QueryAnalyzers
+ module Ci
+ # The purpose of this analyzer is to detect queries not going through a partitioning routing table
+ class PartitioningAnalyzer < Database::QueryAnalyzers::Base
+ RoutingTableNotUsedError = Class.new(QueryAnalyzerError)
+
+ ENABLED_TABLES = %w[
+ ci_builds_metadata
+ ].freeze
+
+ class << self
+ def enabled?
+ ::Feature::FlipperFeature.table_exists? &&
+ ::Feature.enabled?(:ci_partitioning_analyze_queries, type: :ops)
+ end
+
+ def analyze(parsed)
+ analyze_legacy_tables_usage(parsed)
+ end
+
+ private
+
+ def analyze_legacy_tables_usage(parsed)
+ detected = ENABLED_TABLES & (parsed.pg.dml_tables + parsed.pg.select_tables)
+
+ return if detected.none?
+
+ ::Gitlab::ErrorTracking.track_and_raise_for_dev_exception(
+ RoutingTableNotUsedError.new("Detected non-partitioned table use #{detected.inspect}: #{parsed.sql}")
+ )
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics.rb b/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics.rb
index 06e2b114c91..b4b9161f0c2 100644
--- a/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics.rb
+++ b/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics.rb
@@ -14,7 +14,7 @@ module Gitlab
class << self
def enabled?
::Feature::FlipperFeature.table_exists? &&
- Feature.enabled?(:query_analyzer_gitlab_schema_metrics)
+ Feature.enabled?(:query_analyzer_gitlab_schema_metrics, type: :ops)
end
def analyze(parsed)
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 e0cb803b872..3b1751c863d 100644
--- a/lib/gitlab/database/query_analyzers/prevent_cross_database_modification.rb
+++ b/lib/gitlab/database/query_analyzers/prevent_cross_database_modification.rb
@@ -33,7 +33,7 @@ module Gitlab
def self.enabled?
::Feature::FlipperFeature.table_exists? &&
- Feature.enabled?(:detect_cross_database_modification)
+ Feature.enabled?(:detect_cross_database_modification, type: :ops)
end
def self.requires_tracking?(parsed)
diff --git a/lib/gitlab/database/reflection.rb b/lib/gitlab/database/reflection.rb
index 3ea7277571f..33c965cb150 100644
--- a/lib/gitlab/database/reflection.rb
+++ b/lib/gitlab/database/reflection.rb
@@ -114,7 +114,7 @@ module Gitlab
'PostgreSQL on Amazon RDS' => { statement: 'SHOW rds.extensions', error: /PG::UndefinedObject/ },
# Based on https://cloud.google.com/sql/docs/postgres/flags#postgres-c this should be specific
# to Cloud SQL for PostgreSQL
- 'Cloud SQL for PostgreSQL' => { statement: 'SHOW cloudsql.iam_authentication', error: /PG::UndefinedObject/ },
+ 'Cloud SQL for PostgreSQL' => { statement: 'SHOW cloudsql.iam_authentication', error: /PG::UndefinedObject/ },
# Based on
# - https://docs.microsoft.com/en-us/azure/postgresql/flexible-server/concepts-extensions
# - https://docs.microsoft.com/en-us/azure/postgresql/concepts-extensions
diff --git a/lib/gitlab/database/reindexing.rb b/lib/gitlab/database/reindexing.rb
index b96dffc99ac..aba45fcc57b 100644
--- a/lib/gitlab/database/reindexing.rb
+++ b/lib/gitlab/database/reindexing.rb
@@ -27,7 +27,7 @@ module Gitlab
# Hack: Before we do actual reindexing work, create async indexes
Gitlab::Database::AsyncIndexes.create_pending_indexes! if Feature.enabled?(:database_async_index_creation, type: :ops)
- Gitlab::Database::AsyncIndexes.drop_pending_indexes! if Feature.enabled?(:database_async_index_destruction, type: :ops)
+ Gitlab::Database::AsyncIndexes.drop_pending_indexes!
automatic_reindexing
end
diff --git a/lib/gitlab/database/tables_sorted_by_foreign_keys.rb b/lib/gitlab/database/tables_sorted_by_foreign_keys.rb
new file mode 100644
index 00000000000..9f096904d31
--- /dev/null
+++ b/lib/gitlab/database/tables_sorted_by_foreign_keys.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ class TablesSortedByForeignKeys
+ include TSort
+
+ def initialize(connection, tables)
+ @connection = connection
+ @tables = tables
+ end
+
+ def execute
+ strongly_connected_components
+ end
+
+ private
+
+ def tsort_each_node(&block)
+ tables_dependencies.each_key(&block)
+ end
+
+ def tsort_each_child(node, &block)
+ tables_dependencies[node].each(&block)
+ end
+
+ # 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]
+ 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)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/tables_truncate.rb b/lib/gitlab/database/tables_truncate.rb
new file mode 100644
index 00000000000..164520fbab3
--- /dev/null
+++ b/lib/gitlab/database/tables_truncate.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ class TablesTruncate
+ GITLAB_SCHEMAS_TO_IGNORE = %i[gitlab_geo].freeze
+
+ def initialize(database_name:, min_batch_size:, logger: nil, until_table: nil, dry_run: false)
+ @database_name = database_name
+ @min_batch_size = min_batch_size
+ @logger = logger
+ @until_table = until_table
+ @dry_run = dry_run
+ end
+
+ def execute
+ raise "Cannot truncate legacy tables in single-db setup" unless Gitlab::Database.has_config?(:ci)
+ raise "database is not supported" unless %w[main ci].include?(database_name)
+
+ 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)
+ end.keys
+
+ 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
+ raise "Table '#{table_name}' is not locked for writes. Run the rake task gitlab:db:lock_writes first"
+ end
+ end
+
+ if until_table
+ table_index = tables_sorted.find_index { |tables_group| tables_group.include?(until_table) }
+ raise "The table '#{until_table}' is not within the truncated tables" if table_index.nil?
+
+ tables_sorted = tables_sorted[0..table_index]
+ end
+
+ # 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)
+ 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)
+ truncated_tables = []
+
+ tables_sorted.flatten.each do |table|
+ sql_statement = "SELECT set_config('lock_writes.#{table}', 'false', false)"
+ logger&.info(sql_statement)
+ connection.execute(sql_statement) unless dry_run
+ end
+
+ # We do the truncation in stages to avoid high IO
+ # In each stage, we truncate the new tables along with the already truncated
+ # tables before. That's because PostgreSQL doesn't allow to truncate any table (A)
+ # without truncating any other table (B) that has a Foreign Key pointing to the table (A).
+ # even if table (B) is empty, because it has been already truncated in a previous stage.
+ tables_sorted.in_groups_of(min_batch_size, false).each do |tables_groups|
+ new_tables_to_truncate = tables_groups.flatten
+ logger&.info "= New tables to truncate: #{new_tables_to_truncate.join(', ')}"
+ truncated_tables.push(*new_tables_to_truncate).tap(&:sort!)
+ sql_statements = [
+ "SET LOCAL statement_timeout = 0",
+ "SET LOCAL lock_timeout = 0",
+ "TRUNCATE TABLE #{truncated_tables.join(', ')} RESTRICT"
+ ]
+
+ sql_statements.each { |sql_statement| logger&.info(sql_statement) }
+
+ next if dry_run
+
+ connection.transaction do
+ sql_statements.each { |sql_statement| connection.execute(sql_statement) }
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database_importers/security/training_providers/importer.rb b/lib/gitlab/database_importers/security/training_providers/importer.rb
new file mode 100644
index 00000000000..aa6a9f29c6d
--- /dev/null
+++ b/lib/gitlab/database_importers/security/training_providers/importer.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module DatabaseImporters
+ module Security
+ module TrainingProviders
+ module Importer
+ KONTRA_DATA = {
+ name: 'Kontra',
+ description: "Kontra Application Security provides interactive developer security education that
+ enables engineers to quickly learn security best practices
+ and fix issues in their code by analysing real-world software security vulnerabilities.",
+ url: "https://application.security/api/webhook/gitlab/exercises/search"
+ }.freeze
+
+ SCW_DATA = {
+ name: 'Secure Code Warrior',
+ description: "Resolve vulnerabilities faster and confidently with
+ highly relevant and bite-sized secure coding learning.",
+ url: "https://integration-api.securecodewarrior.com/api/v1/trial"
+ }.freeze
+
+ module Security
+ class TrainingProvider < ApplicationRecord
+ self.table_name = 'security_training_providers'
+ end
+ end
+
+ def self.upsert_providers
+ current_time = Time.current
+ timestamps = { created_at: current_time, updated_at: current_time }
+
+ Security::TrainingProvider.upsert_all(
+ [KONTRA_DATA.merge(timestamps), SCW_DATA.merge(timestamps)],
+ unique_by: :index_security_training_providers_on_unique_name
+ )
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/diff/file_collection/compare.rb b/lib/gitlab/diff/file_collection/compare.rb
index badebabb192..6d8395d048d 100644
--- a/lib/gitlab/diff/file_collection/compare.rb
+++ b/lib/gitlab/diff/file_collection/compare.rb
@@ -6,9 +6,9 @@ module Gitlab
class Compare < Base
def initialize(compare, project:, diff_options:, diff_refs: nil)
super(compare,
- project: project,
+ project: project,
diff_options: diff_options,
- diff_refs: diff_refs)
+ diff_refs: diff_refs)
end
def unfold_diff_lines(positions)
diff --git a/lib/gitlab/diff/highlight_cache.rb b/lib/gitlab/diff/highlight_cache.rb
index 7cfe0086f57..084ce63e36a 100644
--- a/lib/gitlab/diff/highlight_cache.rb
+++ b/lib/gitlab/diff/highlight_cache.rb
@@ -6,7 +6,7 @@ module Gitlab
include Gitlab::Utils::Gzip
include Gitlab::Utils::StrongMemoize
- EXPIRATION = 1.day
+ EXPIRATION = 1.hour
VERSION = 2
delegate :diffable, to: :@diff_collection
@@ -82,6 +82,16 @@ module Gitlab
private
+ def expiration
+ return 1.day unless Feature.enabled?(:highlight_diffs_renewable_expiration, diffable.project)
+
+ if Feature.enabled?(:highlight_diffs_short_renewable_expiration, diffable.project)
+ EXPIRATION
+ else
+ 8.hours
+ end
+ end
+
def set_highlighted_diff_lines(diff_file, content)
diff_file.highlighted_diff_lines = content.map do |line|
Gitlab::Diff::Line.safe_init_from_hash(line)
@@ -125,9 +135,9 @@ module Gitlab
#
def write_to_redis_hash(hash)
Gitlab::Redis::Cache.with do |redis|
- redis.pipelined do
+ redis.pipelined do |pipeline|
hash.each do |diff_file_id, highlighted_diff_lines_hash|
- redis.hset(
+ pipeline.hset(
key,
diff_file_id,
gzip_compress(highlighted_diff_lines_hash.to_json)
@@ -137,8 +147,7 @@ module Gitlab
end
# HSETs have to have their expiration date manually updated
- #
- redis.expire(key, EXPIRATION)
+ pipeline.expire(key, expiration)
end
record_memory_usage(fetch_memory_usage(redis, key))
@@ -188,11 +197,19 @@ module Gitlab
return {} unless file_paths.any?
results = []
+ cache_key = key
+ highlight_diffs_renewable_expiration_enabled = Feature.enabled?(:highlight_diffs_renewable_expiration, diffable.project)
+ expiration_period = expiration
Gitlab::Redis::Cache.with do |redis|
- results = redis.hmget(key, file_paths)
+ redis.pipelined do |pipeline|
+ results = pipeline.hmget(cache_key, file_paths)
+ pipeline.expire(key, expiration_period) if highlight_diffs_renewable_expiration_enabled
+ end
end
+ results = results.value
+
record_hit_ratio(results)
results.map! do |result|
diff --git a/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512.rb b/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512.rb
deleted file mode 100644
index 4bfb5f9e64c..00000000000
--- a/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module DoorkeeperSecretStoring
- class Pbkdf2Sha512 < ::Doorkeeper::SecretStoring::Base
- STRETCHES = 20_000
- # An empty salt is used because we need to look tokens up solely by
- # their hashed value. Additionally, tokens are always cryptographically
- # pseudo-random and unique, therefore salting provides no
- # additional security.
- SALT = ''
-
- def self.transform_secret(plain_secret)
- return plain_secret unless Feature.enabled?(:hash_oauth_tokens)
-
- Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512.digest(plain_secret, STRETCHES, SALT)
- end
-
- ##
- # Determines whether this strategy supports restoring
- # secrets from the database. This allows detecting users
- # trying to use a non-restorable strategy with +reuse_access_tokens+.
- def self.allows_restoring_secrets?
- false
- end
- end
- end
-end
diff --git a/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512.rb b/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512.rb
new file mode 100644
index 00000000000..e0884557496
--- /dev/null
+++ b/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+module Gitlab
+ module DoorkeeperSecretStoring
+ module Secret
+ class Pbkdf2Sha512 < ::Doorkeeper::SecretStoring::Base
+ STRETCHES = 20_000
+ # An empty salt is used because we need to look tokens up solely by
+ # their hashed value. Additionally, tokens are always cryptographically
+ # pseudo-random and unique, therefore salting provides no
+ # additional security.
+ SALT = ''
+
+ def self.transform_secret(plain_secret, stored_as_hash = false)
+ return plain_secret if Feature.disabled?(:hash_oauth_secrets) && !stored_as_hash
+
+ Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512.digest(plain_secret, STRETCHES, SALT)
+ end
+
+ ##
+ # Determines whether this strategy supports restoring
+ # secrets from the database. This allows detecting users
+ # trying to use a non-restorable strategy with +reuse_access_tokens+.
+ def self.allows_restoring_secrets?
+ false
+ end
+
+ ##
+ # Securely compare the given +input+ value with a +stored+ value
+ # processed by +transform_secret+.
+ def self.secret_matches?(input, stored)
+ stored_as_hash = stored.starts_with?('$pbkdf2-')
+ transformed_input = transform_secret(input, stored_as_hash)
+ ActiveSupport::SecurityUtils.secure_compare transformed_input, stored
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512.rb b/lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512.rb
new file mode 100644
index 00000000000..f9e6d4076f3
--- /dev/null
+++ b/lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module DoorkeeperSecretStoring
+ module Token
+ class Pbkdf2Sha512 < ::Doorkeeper::SecretStoring::Base
+ STRETCHES = 20_000
+ # An empty salt is used because we need to look tokens up solely by
+ # their hashed value. Additionally, tokens are always cryptographically
+ # pseudo-random and unique, therefore salting provides no
+ # additional security.
+ SALT = ''
+
+ def self.transform_secret(plain_secret)
+ return plain_secret unless Feature.enabled?(:hash_oauth_tokens)
+
+ Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512.digest(plain_secret, STRETCHES, SALT)
+ end
+
+ ##
+ # Determines whether this strategy supports restoring
+ # secrets from the database. This allows detecting users
+ # trying to use a non-restorable strategy with +reuse_access_tokens+.
+ def self.allows_restoring_secrets?
+ false
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/attachment_uploader.rb b/lib/gitlab/email/attachment_uploader.rb
index b67ca8d8a7d..931276588f0 100644
--- a/lib/gitlab/email/attachment_uploader.rb
+++ b/lib/gitlab/email/attachment_uploader.rb
@@ -20,8 +20,8 @@ module Gitlab
sanitize_exif_if_needed(content, tmp.path)
file = {
- tempfile: tmp,
- filename: attachment.filename,
+ tempfile: tmp,
+ filename: attachment.filename,
content_type: attachment.content_type
}
diff --git a/lib/gitlab/email/message/in_product_marketing/team.rb b/lib/gitlab/email/message/in_product_marketing/team.rb
index 6a0471ef9c5..ca99dd12c8e 100644
--- a/lib/gitlab/email/message/in_product_marketing/team.rb
+++ b/lib/gitlab/email/message/in_product_marketing/team.rb
@@ -42,18 +42,18 @@ module Gitlab
[
s_('InProductMarketing|Did you know teams that use GitLab are far more efficient?'),
list([
- s_('InProductMarketing|Goldman Sachs went from 1 build every two weeks to thousands of builds a day'),
- s_('InProductMarketing|Ticketmaster decreased their CI build time by 15X')
- ])
+ s_('InProductMarketing|Goldman Sachs went from 1 build every two weeks to thousands of builds a day'),
+ s_('InProductMarketing|Ticketmaster decreased their CI build time by 15X')
+ ])
].join("\n"),
s_("InProductMarketing|We know a thing or two about efficiency and we don't want to keep that to ourselves. Sign up for a free trial of GitLab Ultimate and your teams will be on it from day one."),
[
s_('InProductMarketing|Stop wondering and use GitLab to answer questions like:'),
list([
- s_('InProductMarketing|How long does it take us to close issues/MRs by types like feature requests, bugs, tech debt, security?'),
- s_('InProductMarketing|How many days does it take our team to complete various tasks?'),
- s_('InProductMarketing|What does our value stream timeline look like from product to development to review and production?')
- ])
+ s_('InProductMarketing|How long does it take us to close issues/MRs by types like feature requests, bugs, tech debt, security?'),
+ s_('InProductMarketing|How many days does it take our team to complete various tasks?'),
+ s_('InProductMarketing|What does our value stream timeline look like from product to development to review and production?')
+ ])
].join("\n")
][series]
end
diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb
index d55cf3202a6..293aa3b53bf 100644
--- a/lib/gitlab/email/message/repository_push.rb
+++ b/lib/gitlab/email/message/repository_push.rb
@@ -79,11 +79,11 @@ module Gitlab
@action_name ||=
case @action
when :create
- 'pushed new'
+ s_('Notify|pushed new')
when :delete
- 'deleted'
+ s_('Notify|deleted')
else
- 'pushed to'
+ s_('Notify|pushed to')
end
end
diff --git a/lib/gitlab/emoji.rb b/lib/gitlab/emoji.rb
index f539d627dcb..2b36b1c99bd 100644
--- a/lib/gitlab/emoji.rb
+++ b/lib/gitlab/emoji.rb
@@ -17,13 +17,13 @@ module Gitlab
def emoji_image_tag(name, src)
image_options = {
- class: 'emoji',
- src: src,
- title: ":#{name}:",
- alt: ":#{name}:",
+ class: 'emoji',
+ src: src,
+ title: ":#{name}:",
+ alt: ":#{name}:",
height: 20,
- width: 20,
- align: 'absmiddle'
+ width: 20,
+ align: 'absmiddle'
}
ActionController::Base.helpers.tag(:img, image_options)
diff --git a/lib/gitlab/encoding_helper.rb b/lib/gitlab/encoding_helper.rb
index f26ab6e3ed1..34c674c3003 100644
--- a/lib/gitlab/encoding_helper.rb
+++ b/lib/gitlab/encoding_helper.rb
@@ -71,6 +71,21 @@ module Gitlab
encode_utf8(data, replace: UNICODE_REPLACEMENT_CHARACTER)
end
+ # This method escapes unsupported UTF-8 characters instead of deleting them
+ def encode_utf8_with_escaping!(message)
+ return encode!(message) if Feature.disabled?(:escape_gitaly_refs)
+
+ message = force_encode_utf8(message)
+ return message if message.valid_encoding?
+
+ unless message.valid_encoding?
+ message = message.chars.map { |char| char.valid_encoding? ? char : escape_chars(char) }.join
+ end
+
+ # encode and clean the bad chars
+ message.replace clean(message)
+ end
+
def encode_utf8(message, replace: "")
message = force_encode_utf8(message)
return message if message.valid_encoding?
@@ -145,6 +160,15 @@ module Gitlab
message.force_encoding("UTF-8")
end
+ # Escapes \x80 - \xFF characters not supported by UTF-8
+ def escape_chars(char)
+ bytes = char.bytes
+
+ return char unless bytes.one?
+
+ "%#{bytes.first.to_s(16).upcase}"
+ end
+
def clean(message, replace: "")
message.encode(
"UTF-16BE",
diff --git a/lib/gitlab/etag_caching/middleware.rb b/lib/gitlab/etag_caching/middleware.rb
index a1918ee6ad5..f6431483a15 100644
--- a/lib/gitlab/etag_caching/middleware.rb
+++ b/lib/gitlab/etag_caching/middleware.rb
@@ -97,12 +97,12 @@ module Gitlab
def add_instrument_for_cache_hit(status, route, request)
payload = {
etag_route: route.name,
- params: request.filtered_parameters,
- headers: request.headers,
- format: request.format.ref,
- method: request.request_method,
- path: request.filtered_path,
- status: status
+ params: request.filtered_parameters,
+ headers: request.headers,
+ format: request.format.ref,
+ method: request.request_method,
+ path: request.filtered_path,
+ status: status
}
ActiveSupport::Notifications.instrument(
diff --git a/lib/gitlab/etag_caching/store.rb b/lib/gitlab/etag_caching/store.rb
index 44c6984c09b..437d577e70e 100644
--- a/lib/gitlab/etag_caching/store.rb
+++ b/lib/gitlab/etag_caching/store.rb
@@ -16,9 +16,9 @@ module Gitlab
etags = keys.map { generate_etag }
Gitlab::Redis::SharedState.with do |redis|
- redis.pipelined do
+ redis.pipelined do |pipeline|
keys.each_with_index do |key, i|
- redis.set(redis_shared_state_key(key), etags[i], ex: EXPIRY_TIME, nx: only_if_missing)
+ pipeline.set(redis_shared_state_key(key), etags[i], ex: EXPIRY_TIME, nx: only_if_missing)
end
end
end
diff --git a/lib/gitlab/experimentation.rb b/lib/gitlab/experimentation.rb
index 8a5432025d8..142d0e55593 100644
--- a/lib/gitlab/experimentation.rb
+++ b/lib/gitlab/experimentation.rb
@@ -70,9 +70,9 @@ module Gitlab
logger = Gitlab::ExperimentationLogger.build
logger.warn message: 'Subject must conform to the rollout strategy',
- experiment_key: experiment_key,
- subject: subject.class.to_s,
- rollout_strategy: rollout_strategy(experiment_key)
+ experiment_key: experiment_key,
+ subject: subject.class.to_s,
+ rollout_strategy: rollout_strategy(experiment_key)
end
def valid_subject_for_rollout_strategy?(experiment_key, subject)
diff --git a/lib/gitlab/external_authorization/cache.rb b/lib/gitlab/external_authorization/cache.rb
index 509daeb0248..c06711d16f8 100644
--- a/lib/gitlab/external_authorization/cache.rb
+++ b/lib/gitlab/external_authorization/cache.rb
@@ -20,8 +20,8 @@ module Gitlab
def store(new_access, new_reason, new_refreshed_at)
::Gitlab::Redis::Cache.with do |redis|
- redis.pipelined do
- redis.mapped_hmset(
+ redis.pipelined do |pipeline|
+ pipeline.mapped_hmset(
cache_key,
{
access: new_access.to_s,
@@ -30,7 +30,7 @@ module Gitlab
}
)
- redis.expire(cache_key, VALIDITY_TIME)
+ pipeline.expire(cache_key, VALIDITY_TIME)
end
end
end
diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb
index 612865ed1be..ca1a2b2a077 100644
--- a/lib/gitlab/fogbugz_import/importer.rb
+++ b/lib/gitlab/fogbugz_import/importer.rb
@@ -129,15 +129,15 @@ module Gitlab
author_id = user_info(bug['ixPersonOpenedBy'])[:gitlab_id] || project.creator_id
issue = Issue.create!(
- iid: bug['ixBug'],
- project_id: project.id,
- title: bug['sTitle'],
- description: body,
- author_id: author_id,
+ iid: bug['ixBug'],
+ project_id: project.id,
+ title: bug['sTitle'],
+ description: body,
+ author_id: author_id,
assignee_ids: [assignee_id],
- state: bug['fOpen'] == 'true' ? 'opened' : 'closed',
- created_at: date,
- updated_at: DateTime.parse(bug['dtLastUpdated'])
+ state: bug['fOpen'] == 'true' ? 'opened' : 'closed',
+ created_at: date,
+ updated_at: DateTime.parse(bug['dtLastUpdated'])
)
issue_labels = ::LabelsFinder.new(nil, project_id: project.id, title: labels).execute(skip_authorization: true)
@@ -184,11 +184,11 @@ module Gitlab
)
note = Note.create!(
- project_id: project.id,
- noteable_type: "Issue",
- noteable_id: issue.id,
- author_id: author_id,
- note: body
+ project_id: project.id,
+ noteable_type: "Issue",
+ noteable_id: issue.id,
+ author_id: author_id,
+ note: body
)
note.update_attribute(:created_at, date)
diff --git a/lib/gitlab/gfm/reference_rewriter.rb b/lib/gitlab/gfm/reference_rewriter.rb
index 40dcac5f46f..0c13ab604bc 100644
--- a/lib/gitlab/gfm/reference_rewriter.rb
+++ b/lib/gitlab/gfm/reference_rewriter.rb
@@ -55,7 +55,12 @@ module Gitlab
end
def needs_rewrite?
- strong_memoize(:needs_rewrite) { @text_html.include?('data-reference-type=') }
+ strong_memoize(:needs_rewrite) do
+ reference_type_attribute =
+ Banzai::Filter::References::ReferenceFilter::REFERENCE_TYPE_DATA_ATTRIBUTE
+
+ @text_html.include?(reference_type_attribute)
+ end
end
private
diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb
index 4b9f2ababc8..4b877bf44da 100644
--- a/lib/gitlab/git.rb
+++ b/lib/gitlab/git.rb
@@ -25,7 +25,7 @@ module Gitlab
include Gitlab::EncodingHelper
def ref_name(ref)
- encode!(ref).sub(%r{\Arefs/(tags|heads|remotes)/}, '')
+ encode_utf8_with_escaping!(ref).sub(%r{\Arefs/(tags|heads|remotes)/}, '')
end
def branch_name(ref)
diff --git a/lib/gitlab/git/diff.rb b/lib/gitlab/git/diff.rb
index 003cc87d65a..72f7413500f 100644
--- a/lib/gitlab/git/diff.rb
+++ b/lib/gitlab/git/diff.rb
@@ -230,7 +230,6 @@ module Gitlab
private
def encode_diff_to_utf8(replace_invalid_utf8_chars)
- return unless Feature.enabled?(:convert_diff_to_utf8_with_replacement_symbol)
return unless replace_invalid_utf8_chars && diff_should_be_converted?
@diff = Gitlab::EncodingHelper.encode_utf8_with_replacement_character(@diff)
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index ad655fedb6d..f1cd75258be 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -403,7 +403,7 @@ module Gitlab
wrapped_gitaly_errors do
gitaly_blob_client.list_blobs(revisions, limit: REV_LIST_COMMIT_LIMIT,
- with_paths: with_paths, dynamic_timeout: dynamic_timeout)
+ with_paths: with_paths, dynamic_timeout: dynamic_timeout)
end
end
@@ -701,7 +701,9 @@ module Gitlab
# Delete the specified branch from the repository
# Note: No Git hooks are executed for this action
def delete_branch(branch_name)
- write_ref(branch_name, Gitlab::Git::BLANK_SHA)
+ branch_name = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch_name}" unless branch_name.start_with?("refs/")
+
+ delete_refs(branch_name)
rescue CommandError => e
raise DeleteBranchError, e
end
@@ -913,8 +915,29 @@ module Gitlab
true
end
+ # Creates a commit
+ #
+ # @param [User] user The committer of the commit.
+ # @param [String] branch_name: The name of the branch to be created/updated.
+ # @param [String] message: The commit message.
+ # @param [Array<Hash>] actions: An array of files to be added/updated/removed.
+ # @option actions: [Symbol] :action One of :create, :create_dir, :update, :move, :delete, :chmod
+ # @option actions: [String] :file_path The path of the file or directory being added/updated/removed.
+ # @option actions: [String] :previous_path The path of the file being moved. Only used for the :move action.
+ # @option actions: [String,IO] :content The file content for :create or :update
+ # @option actions: [String] :encoding One of text, base64
+ # @option actions: [Boolean] :execute_filemode True sets the executable filemode on the file.
+ # @option actions: [Boolean] :infer_content True uses the existing file contents instead of using content on move.
+ # @param [String] author_email: The authors email, if unspecified the committers email is used.
+ # @param [String] author_name: The authors name, if unspecified the committers name is used.
+ # @param [String] start_branch_name: The name of the branch to be used as the parent of the commit. Only used if start_sha: is unspecified.
+ # @param [String] start_sha: The sha to be used as the parent of the commit.
+ # @param [Gitlab::Git::Repository] start_repository: The repository that contains the start branch or sha. Defaults to use this repository.
+ # @param [Boolean] force: Force update the branch.
+ # @return [Gitlab::Git::OperationService::BranchUpdate]
+ #
# rubocop:disable Metrics/ParameterLists
- def multi_action(
+ def commit_files(
user, branch_name:, message:, actions:,
author_email: nil, author_name: nil,
start_branch_name: nil, start_sha: nil, start_repository: nil,
@@ -989,8 +1012,8 @@ module Gitlab
gitaly_ref_client.branch_names_contains_sha(sha)
end
- def tag_names_contains_sha(sha)
- gitaly_ref_client.tag_names_contains_sha(sha)
+ def tag_names_contains_sha(sha, limit: 0)
+ gitaly_ref_client.tag_names_contains_sha(sha, limit: limit)
end
def search_files_by_content(query, ref, options = {})
@@ -1011,16 +1034,20 @@ module Gitlab
end
def search_files_by_name(query, ref)
- safe_query = Regexp.escape(query.sub(%r{^/*}, ""))
+ safe_query = query.sub(%r{^/*}, "")
ref ||= root_ref
return [] if empty? || safe_query.blank?
- gitaly_repository_client.search_files_by_name(ref, safe_query)
+ gitaly_repository_client.search_files_by_name(ref, safe_query).map do |file|
+ Gitlab::EncodingHelper.encode_utf8(file)
+ end
end
def search_files_by_regexp(filter, ref = 'HEAD')
- gitaly_repository_client.search_files_by_regexp(ref, filter)
+ gitaly_repository_client.search_files_by_regexp(ref, filter).map do |file|
+ Gitlab::EncodingHelper.encode_utf8(file)
+ end
end
def find_commits_by_message(query, ref, path, limit, offset)
@@ -1031,6 +1058,24 @@ module Gitlab
end
end
+ def list_commits_by(query, ref, author: nil, before: nil, after: nil, limit: 1000)
+ params = {
+ author: author,
+ ignore_case: true,
+ commit_message_patterns: query,
+ before: before,
+ after: after,
+ reverse: false,
+ pagination_params: { limit: limit }
+ }
+
+ wrapped_gitaly_errors do
+ gitaly_commit_client
+ .list_commits([ref], params)
+ .map { |c| commit(c) }
+ end
+ end
+
def list_last_commits_for_tree(sha, path, offset: 0, limit: 25, literal_pathspec: false)
wrapped_gitaly_errors do
gitaly_commit_client.list_last_commits_for_tree(sha, path, offset: offset, limit: limit, literal_pathspec: literal_pathspec)
diff --git a/lib/gitlab/git/tag.rb b/lib/gitlab/git/tag.rb
index 25895dc6728..5ed5158eeea 100644
--- a/lib/gitlab/git/tag.rb
+++ b/lib/gitlab/git/tag.rb
@@ -63,7 +63,7 @@ module Gitlab
end
def init_from_gitaly
- @name = encode!(@raw_tag.name.dup)
+ @name = encode_utf8_with_escaping!(@raw_tag.name.dup)
@target = @raw_tag.id
@message = message_from_gitaly_tag
diff --git a/lib/gitlab/git/wiki.rb b/lib/gitlab/git/wiki.rb
index 4bab94968d7..2228fcb886e 100644
--- a/lib/gitlab/git/wiki.rb
+++ b/lib/gitlab/git/wiki.rb
@@ -70,18 +70,6 @@ module Gitlab
@repository.exists?
end
- def write_page(name, format, content, commit_details)
- wrapped_gitaly_errors do
- gitaly_write_page(name, format, content, commit_details)
- end
- end
-
- def update_page(page_path, title, format, content, commit_details)
- wrapped_gitaly_errors do
- gitaly_update_page(page_path, title, format, content, commit_details)
- end
- end
-
def list_pages(limit: 0, sort: nil, direction_desc: false, load_content: false)
wrapped_gitaly_errors do
gitaly_list_pages(
@@ -113,21 +101,13 @@ module Gitlab
@gitaly_wiki_client ||= Gitlab::GitalyClient::WikiService.new(@repository)
end
- def gitaly_write_page(name, format, content, commit_details)
- gitaly_wiki_client.write_page(name, format, content, commit_details)
- end
-
- def gitaly_update_page(page_path, title, format, content, commit_details)
- gitaly_wiki_client.update_page(page_path, title, format, content, commit_details)
- end
-
def gitaly_find_page(title:, version: nil, dir: nil, load_content: true)
return unless title.present?
wiki_page, version = gitaly_wiki_client.find_page(title: title, version: version, dir: dir, load_content: load_content)
return unless wiki_page
- Gitlab::Git::WikiPage.new(wiki_page, version)
+ Gitlab::Git::WikiPage.from_gitaly_wiki_page(wiki_page, version)
rescue GRPC::InvalidArgument
nil
end
@@ -143,7 +123,7 @@ module Gitlab
end
gitaly_pages.map do |wiki_page, version|
- Gitlab::Git::WikiPage.new(wiki_page, version)
+ Gitlab::Git::WikiPage.from_gitaly_wiki_page(wiki_page, version)
end
end
end
diff --git a/lib/gitlab/git/wiki_page.rb b/lib/gitlab/git/wiki_page.rb
index a1f3d64ccde..57b7e7d53dd 100644
--- a/lib/gitlab/git/wiki_page.rb
+++ b/lib/gitlab/git/wiki_page.rb
@@ -5,17 +5,31 @@ module Gitlab
class WikiPage
attr_reader :url_path, :title, :format, :path, :version, :raw_data, :name, :historical, :formatted_data
- # This class abstracts away Gitlab::GitalyClient::WikiPage
- def initialize(gitaly_page, version)
- @url_path = gitaly_page.url_path
- @title = gitaly_page.title
- @format = gitaly_page.format
- @path = gitaly_page.path
- @raw_data = gitaly_page.raw_data
- @name = gitaly_page.name
- @historical = gitaly_page.historical?
+ class << self
+ # Abstracts away Gitlab::GitalyClient::WikiPage
+ def from_gitaly_wiki_page(gitaly_page, version)
+ new(
+ url_path: gitaly_page.url_path,
+ title: gitaly_page.title,
+ format: gitaly_page.format,
+ path: gitaly_page.path,
+ raw_data: gitaly_page.raw_data,
+ name: gitaly_page.name,
+ historical: gitaly_page.historical?,
+ version: version
+ )
+ end
+ end
- @version = version
+ def initialize(hash)
+ @url_path = hash[:url_path]
+ @title = hash[:title]
+ @format = hash[:format]
+ @path = hash[:path]
+ @raw_data = hash[:raw_data]
+ @name = hash[:name]
+ @historical = hash[:historical]
+ @version = hash[:version]
end
def historical?
diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb
index 0f306a9825d..312d1dddff1 100644
--- a/lib/gitlab/gitaly_client/commit_service.rb
+++ b/lib/gitlab/gitaly_client/commit_service.rb
@@ -232,7 +232,7 @@ module Gitlab
msg.paths.map do |path|
Gitlab::Git::ChangedPath.new(
status: path.status,
- path: EncodingHelper.encode!(path.path)
+ path: EncodingHelper.encode!(path.path)
)
end
end
@@ -251,14 +251,23 @@ module Gitlab
consume_commits_response(response)
end
- def list_commits(revisions, reverse: false, pagination_params: nil)
+ def list_commits(revisions, params = {})
request = Gitaly::ListCommitsRequest.new(
repository: @gitaly_repo,
revisions: Array.wrap(revisions),
- reverse: reverse,
- pagination_params: pagination_params
+ reverse: !!params[:reverse],
+ ignore_case: params[:ignore_case],
+ pagination_params: params[:pagination_params]
)
+ if params[:commit_message_patterns]
+ request.commit_message_patterns += Array.wrap(params[:commit_message_patterns])
+ end
+
+ request.author = encode_binary(params[:author]) if params[:author]
+ request.before = GitalyClient.timestamp(params[:before]) if params[:before]
+ request.after = GitalyClient.timestamp(params[:after]) if params[:after]
+
response = GitalyClient.call(@repository.storage, :commit_service, :list_commits, request, timeout: GitalyClient.medium_timeout)
consume_commits_response(response)
end
@@ -396,12 +405,12 @@ module Gitlab
def find_commits(options)
request = Gitaly::FindCommitsRequest.new(
- repository: @gitaly_repo,
- limit: options[:limit],
- offset: options[:offset],
- follow: options[:follow],
- skip_merges: options[:skip_merges],
- all: !!options[:all],
+ repository: @gitaly_repo,
+ limit: options[:limit],
+ offset: options[:offset],
+ follow: options[:follow],
+ skip_merges: options[:skip_merges],
+ all: !!options[:all],
first_parent: !!options[:first_parent],
global_options: parse_global_options!(options),
disable_walk: true, # This option is deprecated. The 'walk' implementation is being removed.
diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb
index c5c6ec1cdfa..7835fb32f59 100644
--- a/lib/gitlab/gitaly_client/operation_service.rb
+++ b/lib/gitlab/gitaly_client/operation_service.rb
@@ -85,8 +85,20 @@ module Gitlab
target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit)
Gitlab::Git::Branch.new(@repository, branch.name, target_commit.id, target_commit)
- rescue GRPC::FailedPrecondition => ex
- raise Gitlab::Git::Repository::InvalidRef, ex
+ rescue GRPC::BadStatus => e
+ detailed_error = GitalyClient.decode_detailed_error(e)
+
+ case detailed_error&.error
+ when :custom_hook
+ raise Gitlab::Git::PreReceiveError.new(custom_hook_error_message(detailed_error.custom_hook),
+ fallback_message: e.details)
+ else
+ if e.code == GRPC::Core::StatusCodes::FAILED_PRECONDITION
+ raise Gitlab::Git::Repository::InvalidRef, e
+ end
+
+ raise
+ end
end
def user_update_branch(branch_name, user, newrev, oldrev)
@@ -410,9 +422,9 @@ module Gitlab
end
end
- response = GitalyClient.call(@repository.storage, :operation_service,
- :user_commit_files, req_enum, timeout: GitalyClient.long_timeout,
- remote_storage: start_repository&.storage)
+ response = GitalyClient.call(
+ @repository.storage, :operation_service, :user_commit_files, req_enum,
+ timeout: GitalyClient.long_timeout, remote_storage: start_repository&.storage)
if (pre_receive_error = response.pre_receive_error.presence)
raise Gitlab::Git::PreReceiveError, pre_receive_error
diff --git a/lib/gitlab/gitaly_client/ref_service.rb b/lib/gitlab/gitaly_client/ref_service.rb
index 42f9c165610..bb6bc3121bd 100644
--- a/lib/gitlab/gitaly_client/ref_service.rb
+++ b/lib/gitlab/gitaly_client/ref_service.rb
@@ -7,7 +7,8 @@ module Gitlab
TAGS_SORT_KEY = {
'name' => Gitaly::FindAllTagsRequest::SortBy::Key::REFNAME,
- 'updated' => Gitaly::FindAllTagsRequest::SortBy::Key::CREATORDATE
+ 'updated' => Gitaly::FindAllTagsRequest::SortBy::Key::CREATORDATE,
+ 'version' => Gitaly::FindAllTagsRequest::SortBy::Key::VERSION_REFNAME
}.freeze
TAGS_SORT_DIRECTION = {
@@ -104,7 +105,7 @@ module Gitlab
return unless branch
target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit)
- Gitlab::Git::Branch.new(@repository, encode!(branch.name.dup), branch.target_commit.id, target_commit)
+ Gitlab::Git::Branch.new(@repository, branch.name.dup, branch.target_commit.id, target_commit)
end
def find_tag(tag_name)
@@ -258,7 +259,7 @@ module Gitlab
end
def sort_tags_by_param(sort_by)
- match = sort_by.match(/^(?<key>name|updated)_(?<direction>asc|desc)$/)
+ match = sort_by.match(/^(?<key>name|updated|version)_(?<direction>asc|desc)$/)
return unless match
@@ -269,14 +270,23 @@ module Gitlab
end
def consume_find_local_branches_response(response)
- response.flat_map do |message|
- message.branches.map do |gitaly_branch|
- Gitlab::Git::Branch.new(
- @repository,
- encode!(gitaly_branch.name.dup),
- gitaly_branch.commit_id,
- commit_from_local_branches_response(gitaly_branch)
- )
+ if Feature.enabled?(:gitaly_simplify_find_local_branches_response, type: :undefined)
+ response.flat_map do |message|
+ message.local_branches.map do |branch|
+ target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit)
+ Gitlab::Git::Branch.new(@repository, branch.name, branch.target_commit.id, target_commit)
+ end
+ end
+ else
+ response.flat_map do |message|
+ message.branches.map do |gitaly_branch|
+ Gitlab::Git::Branch.new(
+ @repository,
+ gitaly_branch.name.dup,
+ gitaly_branch.commit_id,
+ commit_from_local_branches_response(gitaly_branch)
+ )
+ end
end
end
end
diff --git a/lib/gitlab/gitaly_client/server_service.rb b/lib/gitlab/gitaly_client/server_service.rb
index 36bda67c26e..48fd0e66354 100644
--- a/lib/gitlab/gitaly_client/server_service.rb
+++ b/lib/gitlab/gitaly_client/server_service.rb
@@ -26,6 +26,19 @@ module Gitlab
storage_specific(disk_statistics)
end
+ def readiness_check
+ request = Gitaly::ReadinessCheckRequest.new(timeout: GitalyClient.medium_timeout)
+ response = GitalyClient.call(@storage, :server_service, :readiness_check, request, timeout: GitalyClient.default_timeout)
+
+ return { success: true } if response.ok_response
+
+ failed_checks = response.failure_response.failed_checks.map do |failed_check|
+ ["#{failed_check.name}: #{failed_check.error_message}"]
+ end
+
+ { success: false, message: failed_checks.join("\n") }
+ end
+
private
def storage_specific(response)
diff --git a/lib/gitlab/github_import/attachments_downloader.rb b/lib/gitlab/github_import/attachments_downloader.rb
new file mode 100644
index 00000000000..b71d5f753f2
--- /dev/null
+++ b/lib/gitlab/github_import/attachments_downloader.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ class AttachmentsDownloader
+ include ::Gitlab::ImportExport::CommandLineUtil
+ include ::BulkImports::FileDownloads::FilenameFetch
+ include ::BulkImports::FileDownloads::Validations
+
+ DownloadError = Class.new(StandardError)
+
+ FILENAME_SIZE_LIMIT = 255 # chars before the extension
+ DEFAULT_FILE_SIZE_LIMIT = 25.megabytes
+ TMP_DIR = File.join(Dir.tmpdir, 'github_attachments').freeze
+
+ attr_reader :file_url, :filename, :file_size_limit
+
+ def initialize(file_url, file_size_limit: DEFAULT_FILE_SIZE_LIMIT)
+ @file_url = file_url
+ @file_size_limit = file_size_limit
+
+ filename = URI(file_url).path.split('/').last
+ @filename = ensure_filename_size(filename)
+ end
+
+ def perform
+ validate_content_length
+ validate_filepath
+
+ file = download
+ validate_symlink
+ file
+ end
+
+ def delete
+ FileUtils.rm_rf File.dirname(filepath)
+ end
+
+ private
+
+ def raise_error(message)
+ raise DownloadError, message
+ end
+
+ def response_headers
+ @response_headers ||=
+ Gitlab::HTTP.perform_request(Net::HTTP::Head, file_url, {}).headers
+ end
+
+ def download
+ file = File.open(filepath, 'wb')
+ Gitlab::HTTP.perform_request(Net::HTTP::Get, file_url, stream_body: true) { |batch| file.write(batch) }
+ file
+ end
+
+ def filepath
+ @filepath ||= begin
+ dir = File.join(TMP_DIR, SecureRandom.uuid)
+ mkdir_p dir
+ File.join(dir, filename)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb
index 11a41149274..6cff15a204f 100644
--- a/lib/gitlab/github_import/client.rb
+++ b/lib/gitlab/github_import/client.rb
@@ -76,11 +76,15 @@ module Gitlab
each_object(:pull_request_reviews, repo_name, iid)
end
+ def repos(options = {})
+ octokit.repos(nil, options).map(&:to_h)
+ end
+
# Returns the details of a GitHub repository.
#
# name - The path (in the form `owner/repository`) of the repository.
def repository(name)
- with_rate_limit { octokit.repo(name) }
+ with_rate_limit { octokit.repo(name).to_h }
end
def pull_request(repo_name, iid)
@@ -99,6 +103,14 @@ module Gitlab
each_object(:releases, *args)
end
+ def branches(*args)
+ each_object(:branches, *args)
+ end
+
+ def branch_protection(repo_name, branch_name)
+ with_rate_limit { octokit.branch_protection(repo_name, branch_name) }
+ end
+
# Fetches data from the GitHub API and yields a Page object for every page
# of data, without loading all of them into memory.
#
@@ -167,7 +179,7 @@ module Gitlab
end
def search_repos_by_name(name, options = {})
- with_retry { octokit.search_repositories(search_query(str: name, type: :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)
diff --git a/lib/gitlab/github_import/importer/events/base_importer.rb b/lib/gitlab/github_import/importer/events/base_importer.rb
index 9ab1d916d33..8218acf2bfb 100644
--- a/lib/gitlab/github_import/importer/events/base_importer.rb
+++ b/lib/gitlab/github_import/importer/events/base_importer.rb
@@ -29,6 +29,19 @@ module Gitlab
def issuable_db_id(object)
IssuableFinder.new(project, object).database_id
end
+
+ def issuable_type(issue_event)
+ merge_request_event?(issue_event) ? MergeRequest.name : Issue.name
+ end
+
+ def merge_request_event?(issue_event)
+ issue_event.issuable_type == MergeRequest.name
+ end
+
+ def resource_event_belongs_to(issue_event)
+ belongs_to_key = merge_request_event?(issue_event) ? :merge_request_id : :issue_id
+ { belongs_to_key => issuable_db_id(issue_event) }
+ end
end
end
end
diff --git a/lib/gitlab/github_import/importer/events/changed_assignee.rb b/lib/gitlab/github_import/importer/events/changed_assignee.rb
index c8f6335e4a8..b75d41f40de 100644
--- a/lib/gitlab/github_import/importer/events/changed_assignee.rb
+++ b/lib/gitlab/github_import/importer/events/changed_assignee.rb
@@ -7,22 +7,22 @@ module Gitlab
class ChangedAssignee < BaseImporter
def execute(issue_event)
assignee_id = author_id(issue_event, author_key: :assignee)
- assigner_id = author_id(issue_event, author_key: :assigner)
+ author_id = author_id(issue_event, author_key: :actor)
- note_body = parse_body(issue_event, assigner_id, assignee_id)
+ note_body = parse_body(issue_event, assignee_id)
- create_note(issue_event, note_body, assigner_id)
+ create_note(issue_event, note_body, author_id)
end
private
- def create_note(issue_event, note_body, assigner_id)
+ def create_note(issue_event, note_body, author_id)
Note.create!(
system: true,
- noteable_type: Issue.name,
+ noteable_type: issuable_type(issue_event),
noteable_id: issuable_db_id(issue_event),
project: project,
- author_id: assigner_id,
+ author_id: author_id,
note: note_body,
system_note_metadata: SystemNoteMetadata.new(
{
@@ -36,12 +36,14 @@ module Gitlab
)
end
- def parse_body(issue_event, assigner_id, assignee_id)
+ def parse_body(issue_event, assignee_id)
+ assignee = User.find(assignee_id).to_reference
+
Gitlab::I18n.with_default_locale do
if issue_event.event == "unassigned"
- "unassigned #{User.find(assigner_id).to_reference}"
+ "unassigned #{assignee}"
else
- "assigned to #{User.find(assignee_id).to_reference}"
+ "assigned to #{assignee}"
end
end
end
diff --git a/lib/gitlab/github_import/importer/events/changed_label.rb b/lib/gitlab/github_import/importer/events/changed_label.rb
index 818a9202745..83130d18db9 100644
--- a/lib/gitlab/github_import/importer/events/changed_label.rb
+++ b/lib/gitlab/github_import/importer/events/changed_label.rb
@@ -12,13 +12,14 @@ module Gitlab
private
def create_event(issue_event)
- ResourceLabelEvent.create!(
- issue_id: issuable_db_id(issue_event),
+ attrs = {
user_id: author_id(issue_event),
label_id: label_finder.id_for(issue_event.label_title),
action: action(issue_event.event),
created_at: issue_event.created_at
- )
+ }.merge(resource_event_belongs_to(issue_event))
+
+ ResourceLabelEvent.create!(attrs)
end
def label_finder
diff --git a/lib/gitlab/github_import/importer/events/changed_milestone.rb b/lib/gitlab/github_import/importer/events/changed_milestone.rb
index 3164c041dc3..39b92d88b58 100644
--- a/lib/gitlab/github_import/importer/events/changed_milestone.rb
+++ b/lib/gitlab/github_import/importer/events/changed_milestone.rb
@@ -17,14 +17,15 @@ module Gitlab
private
def create_event(issue_event)
- ResourceMilestoneEvent.create!(
- issue_id: issuable_db_id(issue_event),
+ attrs = {
user_id: author_id(issue_event),
created_at: issue_event.created_at,
milestone_id: project.milestones.find_by_title(issue_event.milestone_title)&.id,
action: action(issue_event.event),
state: DEFAULT_STATE
- )
+ }.merge(resource_event_belongs_to(issue_event))
+
+ ResourceMilestoneEvent.create!(attrs)
end
def action(event_type)
diff --git a/lib/gitlab/github_import/importer/events/changed_reviewer.rb b/lib/gitlab/github_import/importer/events/changed_reviewer.rb
new file mode 100644
index 00000000000..17b1fa4ab45
--- /dev/null
+++ b/lib/gitlab/github_import/importer/events/changed_reviewer.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ module Importer
+ module Events
+ class ChangedReviewer < BaseImporter
+ def execute(issue_event)
+ requested_reviewer_id = author_id(issue_event, author_key: :requested_reviewer)
+ review_requester_id = author_id(issue_event, author_key: :review_requester)
+
+ note_body = parse_body(issue_event, requested_reviewer_id)
+
+ create_note(issue_event, note_body, review_requester_id)
+ end
+
+ private
+
+ def create_note(issue_event, note_body, review_requester_id)
+ Note.create!(
+ system: true,
+ noteable_type: issuable_type(issue_event),
+ noteable_id: issuable_db_id(issue_event),
+ project: project,
+ author_id: review_requester_id,
+ note: note_body,
+ system_note_metadata: SystemNoteMetadata.new(
+ {
+ action: 'reviewer',
+ created_at: issue_event.created_at,
+ updated_at: issue_event.created_at
+ }
+ ),
+ created_at: issue_event.created_at,
+ updated_at: issue_event.created_at
+ )
+ end
+
+ def parse_body(issue_event, requested_reviewer_id)
+ requested_reviewer = User.find(requested_reviewer_id).to_reference
+
+ if issue_event.event == 'review_request_removed'
+ "#{SystemNotes::IssuablesService.issuable_events[:review_request_removed]}" \
+ " #{requested_reviewer}"
+ else
+ "#{SystemNotes::IssuablesService.issuable_events[:review_requested]}" \
+ " #{requested_reviewer}"
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/importer/events/closed.rb b/lib/gitlab/github_import/importer/events/closed.rb
index ca8730d0f27..58d9dbf826c 100644
--- a/lib/gitlab/github_import/importer/events/closed.rb
+++ b/lib/gitlab/github_import/importer/events/closed.rb
@@ -17,7 +17,7 @@ module Gitlab
project_id: project.id,
author_id: author_id(issue_event),
action: 'closed',
- target_type: Issue.name,
+ target_type: issuable_type(issue_event),
target_id: issuable_db_id(issue_event),
created_at: issue_event.created_at,
updated_at: issue_event.created_at
@@ -25,15 +25,16 @@ module Gitlab
end
def create_state_event(issue_event)
- ResourceStateEvent.create!(
+ attrs = {
user_id: author_id(issue_event),
- issue_id: issuable_db_id(issue_event),
source_commit: issue_event.commit_id,
state: 'closed',
close_after_error_tracking_resolve: false,
close_auto_resolve_prometheus_alert: false,
created_at: issue_event.created_at
- )
+ }.merge(resource_event_belongs_to(issue_event))
+
+ ResourceStateEvent.create!(attrs)
end
end
end
diff --git a/lib/gitlab/github_import/importer/events/cross_referenced.rb b/lib/gitlab/github_import/importer/events/cross_referenced.rb
index 89fc1bdeb09..b56ae186d3c 100644
--- a/lib/gitlab/github_import/importer/events/cross_referenced.rb
+++ b/lib/gitlab/github_import/importer/events/cross_referenced.rb
@@ -33,7 +33,7 @@ module Gitlab
def create_note(issue_event, note_body, user_id)
Note.create!(
system: true,
- noteable_type: Issue.name,
+ noteable_type: issuable_type(issue_event),
noteable_id: issuable_db_id(issue_event),
project: project,
author_id: user_id,
diff --git a/lib/gitlab/github_import/importer/events/renamed.rb b/lib/gitlab/github_import/importer/events/renamed.rb
index 96d112b04c6..fb9e08116ba 100644
--- a/lib/gitlab/github_import/importer/events/renamed.rb
+++ b/lib/gitlab/github_import/importer/events/renamed.rb
@@ -14,7 +14,7 @@ module Gitlab
def note_params(issue_event)
{
noteable_id: issuable_db_id(issue_event),
- noteable_type: Issue.name,
+ noteable_type: issuable_type(issue_event),
project_id: project.id,
author_id: author_id(issue_event),
note: parse_body(issue_event),
diff --git a/lib/gitlab/github_import/importer/events/reopened.rb b/lib/gitlab/github_import/importer/events/reopened.rb
index b75344bf817..8abeba0777d 100644
--- a/lib/gitlab/github_import/importer/events/reopened.rb
+++ b/lib/gitlab/github_import/importer/events/reopened.rb
@@ -17,7 +17,7 @@ module Gitlab
project_id: project.id,
author_id: author_id(issue_event),
action: 'reopened',
- target_type: Issue.name,
+ target_type: issuable_type(issue_event),
target_id: issuable_db_id(issue_event),
created_at: issue_event.created_at,
updated_at: issue_event.created_at
@@ -25,12 +25,13 @@ module Gitlab
end
def create_state_event(issue_event)
- ResourceStateEvent.create!(
+ attrs = {
user_id: author_id(issue_event),
- issue_id: issuable_db_id(issue_event),
state: 'reopened',
created_at: issue_event.created_at
- )
+ }.merge(resource_event_belongs_to(issue_event))
+
+ ResourceStateEvent.create!(attrs)
end
end
end
diff --git a/lib/gitlab/github_import/importer/issue_event_importer.rb b/lib/gitlab/github_import/importer/issue_event_importer.rb
index ef456e56ee1..80749aae93c 100644
--- a/lib/gitlab/github_import/importer/issue_event_importer.rb
+++ b/lib/gitlab/github_import/importer/issue_event_importer.rb
@@ -15,11 +15,7 @@ module Gitlab
@client = client
end
- # TODO: Add MergeRequest events support
- # https://gitlab.com/groups/gitlab-org/-/epics/7673
def execute
- return if issue_event.issuable_type == 'MergeRequest'
-
importer = event_importer_class(issue_event)
if importer
importer.new(project, client).execute(issue_event)
@@ -49,6 +45,8 @@ module Gitlab
Gitlab::GithubImport::Importer::Events::CrossReferenced
when 'assigned', 'unassigned'
Gitlab::GithubImport::Importer::Events::ChangedAssignee
+ when 'review_requested', 'review_request_removed'
+ Gitlab::GithubImport::Importer::Events::ChangedReviewer
end
end
end
diff --git a/lib/gitlab/github_import/importer/protected_branch_importer.rb b/lib/gitlab/github_import/importer/protected_branch_importer.rb
new file mode 100644
index 00000000000..16215fdce8e
--- /dev/null
+++ b/lib/gitlab/github_import/importer/protected_branch_importer.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ module Importer
+ class ProtectedBranchImporter
+ attr_reader :protected_branch, :project, :client
+
+ # protected_branch - An instance of
+ # `Gitlab::GithubImport::Representation::ProtectedBranch`.
+ # project - An instance of `Project`
+ # client - An instance of `Gitlab::GithubImport::Client`
+ def initialize(protected_branch, project, client)
+ @protected_branch = protected_branch
+ @project = project
+ @client = client
+ end
+
+ def execute
+ # The creator of the project is always allowed to create protected
+ # branches, so we skip the authorization check in this service class.
+ ProtectedBranches::CreateService
+ .new(project, project.creator, params)
+ .execute(skip_authorization: true)
+ end
+
+ private
+
+ def params
+ {
+ name: protected_branch.id,
+ push_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }],
+ merge_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }],
+ allow_force_push: allow_force_push?
+ }
+ end
+
+ def allow_force_push?
+ if ProtectedBranch.protected?(project, protected_branch.id)
+ ProtectedBranch.allow_force_push?(project, protected_branch.id) && protected_branch.allow_force_pushes
+ else
+ protected_branch.allow_force_pushes
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/importer/protected_branches_importer.rb b/lib/gitlab/github_import/importer/protected_branches_importer.rb
new file mode 100644
index 00000000000..b5be823d5ab
--- /dev/null
+++ b/lib/gitlab/github_import/importer/protected_branches_importer.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ module Importer
+ class ProtectedBranchesImporter
+ include ParallelScheduling
+
+ # The method that will be called for traversing through all the objects to
+ # import, yielding them to the supplied block.
+ def each_object_to_import
+ repo = project.import_source
+
+ protected_branches = client.branches(repo).select { |branch| branch.protection&.enabled }
+ protected_branches.each do |protected_branch|
+ object = client.branch_protection(repo, protected_branch.name)
+ next if object.nil? || already_imported?(object)
+
+ yield object
+
+ Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
+ mark_as_imported(object)
+ end
+ end
+
+ def importer_class
+ ProtectedBranchImporter
+ end
+
+ def representation_class
+ Gitlab::GithubImport::Representation::ProtectedBranch
+ end
+
+ def sidekiq_worker_class
+ ImportProtectedBranchWorker
+ end
+
+ def object_type
+ :protected_branch
+ end
+
+ def collection_method
+ :protected_branches
+ end
+
+ def id_for_already_imported_cache(protected_branch)
+ protected_branch.name
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/importer/release_attachments_importer.rb b/lib/gitlab/github_import/importer/release_attachments_importer.rb
new file mode 100644
index 00000000000..6419851623c
--- /dev/null
+++ b/lib/gitlab/github_import/importer/release_attachments_importer.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ module Importer
+ class ReleaseAttachmentsImporter
+ attr_reader :release_db_id, :release_description, :project
+
+ # release - An instance of `ReleaseAttachments`.
+ # project - An instance of `Project`.
+ # client - An instance of `Gitlab::GithubImport::Client`.
+ def initialize(release_attachments, project, _client = nil)
+ @release_db_id = release_attachments.release_db_id
+ @release_description = release_attachments.description
+ @project = project
+ end
+
+ def execute
+ attachment_urls = MarkdownText.fetch_attachment_urls(release_description)
+ new_description = attachment_urls.reduce(release_description) do |description, url|
+ new_url = download_attachment(url)
+ description.gsub(url, new_url)
+ end
+
+ Release.find(release_db_id).update_column(:description, new_description)
+ end
+
+ private
+
+ # in: github attachment markdown url
+ # out: gitlab attachment markdown url
+ def download_attachment(markdown_url)
+ url = extract_url_from_markdown(markdown_url)
+ name_prefix = extract_name_from_markdown(markdown_url)
+
+ downloader = ::Gitlab::GithubImport::AttachmentsDownloader.new(url)
+ file = downloader.perform
+ uploader = UploadService.new(project, file, FileUploader).execute
+ "#{name_prefix}(#{uploader.to_h[:url]})"
+ ensure
+ downloader&.delete
+ end
+
+ # in: "![image-icon](https://user-images.githubusercontent.com/..)"
+ # out: https://user-images.githubusercontent.com/..
+ def extract_url_from_markdown(text)
+ text.match(%r{https://.*\)$}).to_a[0].chop
+ end
+
+ # in: "![image-icon](https://user-images.githubusercontent.com/..)"
+ # out: ![image-icon]
+ def extract_name_from_markdown(text)
+ text.match(%r{^!?\[.*\]}).to_a[0]
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/importer/releases_attachments_importer.rb b/lib/gitlab/github_import/importer/releases_attachments_importer.rb
new file mode 100644
index 00000000000..7221c802d83
--- /dev/null
+++ b/lib/gitlab/github_import/importer/releases_attachments_importer.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ module Importer
+ class ReleasesAttachmentsImporter
+ include ParallelScheduling
+
+ BATCH_SIZE = 100
+
+ # The method that will be called for traversing through all the objects to
+ # import, yielding them to the supplied block.
+ def each_object_to_import
+ project.releases.select(:id, :description).each_batch(of: BATCH_SIZE, column: :id) do |batch|
+ batch.each do |release|
+ next if already_imported?(release)
+
+ Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
+
+ yield release
+
+ # We mark the object as imported immediately so we don't end up
+ # scheduling it multiple times.
+ mark_as_imported(release)
+ end
+ end
+ end
+
+ def representation_class
+ Representation::ReleaseAttachments
+ end
+
+ def importer_class
+ ReleaseAttachmentsImporter
+ end
+
+ def sidekiq_worker_class
+ ImportReleaseAttachmentsWorker
+ end
+
+ def collection_method
+ :release_attachments
+ end
+
+ def object_type
+ :release_attachment
+ end
+
+ def id_for_already_imported_cache(release)
+ release.id
+ end
+
+ def object_representation(object)
+ representation_class.from_db_record(object)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/importer/repository_importer.rb b/lib/gitlab/github_import/importer/repository_importer.rb
index aba4729e9c8..708768a60cf 100644
--- a/lib/gitlab/github_import/importer/repository_importer.rb
+++ b/lib/gitlab/github_import/importer/repository_importer.rb
@@ -17,7 +17,7 @@ module Gitlab
# Returns true if we should import the wiki for the project.
# rubocop: disable CodeReuse/ActiveRecord
def import_wiki?
- client_repository&.has_wiki &&
+ client_repository[:has_wiki] &&
!project.wiki_repository_exists? &&
Gitlab::GitalyClient::RemoteService.exists?(wiki_url)
end
@@ -86,7 +86,7 @@ module Gitlab
private
def default_branch
- client_repository&.default_branch
+ client_repository[:default_branch]
end
def client_repository
diff --git a/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer.rb b/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer.rb
index 8e4015acbbc..8a9ddfc6ec0 100644
--- a/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer.rb
+++ b/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer.rb
@@ -7,7 +7,7 @@ module Gitlab
include ParallelScheduling
include SingleEndpointNotesImporting
- PROCESSED_PAGE_CACHE_KEY = 'issues/%{issue_iid}/%{collection}'
+ PROCESSED_PAGE_CACHE_KEY = 'issues/%{issuable_iid}/%{collection}'
BATCH_SIZE = 100
def initialize(project, client, parallel: true)
@@ -27,12 +27,20 @@ module Gitlab
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
- associated.issue = { 'number' => parent_record.iid }
+ pull_request = parent_record.is_a? MergeRequest
+ associated.issue = { 'number' => parent_record.iid, 'pull_request' => pull_request }
yield(associated)
mark_as_imported(associated)
end
+ # In Github Issues and MergeRequests uses the same API to get their events.
+ # Even more - they have commonly uniq iid
+ def each_associated_page(&block)
+ issues_collection.each_batch(of: BATCH_SIZE, column: :iid) { |batch| process_batch(batch, &block) }
+ merge_requests_collection.each_batch(of: BATCH_SIZE, column: :iid) { |batch| process_batch(batch, &block) }
+ end
+
def importer_class
IssueEventImporter
end
@@ -53,16 +61,20 @@ module Gitlab
:issue_timeline
end
- def parent_collection
+ def issues_collection
project.issues.where.not(iid: already_imported_parents).select(:id, :iid) # rubocop: disable CodeReuse/ActiveRecord
end
+ def merge_requests_collection
+ project.merge_requests.where.not(iid: already_imported_parents).select(:id, :iid) # rubocop: disable CodeReuse/ActiveRecord
+ end
+
def parent_imported_cache_key
"github-importer/issues/#{collection_method}/already-imported/#{project.id}"
end
- def page_counter_id(issue)
- PROCESSED_PAGE_CACHE_KEY % { issue_iid: issue.iid, collection: collection_method }
+ def page_counter_id(issuable)
+ PROCESSED_PAGE_CACHE_KEY % { issuable_iid: issuable.iid, collection: collection_method }
end
def id_for_already_imported_cache(event)
@@ -74,10 +86,10 @@ module Gitlab
end
# Cross-referenced events on Github doesn't have id.
- def compose_associated_id!(issue, event)
+ def compose_associated_id!(issuable, event)
return if event.event != 'cross-referenced'
- event.id = "cross-reference##{issue.id}-in-#{event.source.issue.id}"
+ event.id = "cross-reference##{issuable.iid}-in-#{event.source.issue.id}"
end
end
end
diff --git a/lib/gitlab/github_import/markdown_text.rb b/lib/gitlab/github_import/markdown_text.rb
index 692016bd005..bf2856bc77f 100644
--- a/lib/gitlab/github_import/markdown_text.rb
+++ b/lib/gitlab/github_import/markdown_text.rb
@@ -1,5 +1,8 @@
# frozen_string_literal: true
+# This class includes overriding Kernel#format method
+# what makes impossible to use it here
+# rubocop:disable Style/FormatString
module Gitlab
module GithubImport
class MarkdownText
@@ -8,6 +11,21 @@ module Gitlab
ISSUE_REF_MATCHER = '%{github_url}/%{import_source}/issues'
PULL_REF_MATCHER = '%{github_url}/%{import_source}/pull'
+ MEDIA_TYPES = %w[gif jpeg jpg mov mp4 png svg webm].freeze
+ DOC_TYPES = %w[
+ csv docx fodg fodp fods fodt gz log md odf odg odp ods
+ odt pdf pptx tgz txt xls xlsx zip
+ ].freeze
+ ALL_TYPES = (MEDIA_TYPES + DOC_TYPES).freeze
+
+ # On github.com we have base url for docs and CDN url for media.
+ # On github EE as far as we can know there is no CDN urls and media is placed on base url.
+ # To no escape the escaping symbol we use single quotes instead of double with interpolation.
+ # rubocop:disable Style/StringConcatenation
+ CDN_URL_MATCHER = '(!\[.+\]\(%{github_media_cdn}/\d+/(\w|-)+\.(' + MEDIA_TYPES.join('|') + ')\))'
+ BASE_URL_MATCHER = '(\[.+\]\(%{github_url}/.+/.+/files/\d+/.+\.(' + ALL_TYPES.join('|') + ')\))'
+ # rubocop:enable Style/StringConcatenation
+
class << self
def format(*args)
new(*args).to_s
@@ -24,8 +42,20 @@ module Gitlab
.gsub(pull_ref_matcher, url_helpers.project_merge_requests_url(project))
end
+ def fetch_attachment_urls(text)
+ cdn_url_matcher = CDN_URL_MATCHER % { github_media_cdn: Regexp.escape(github_media_cdn) }
+ doc_url_matcher = BASE_URL_MATCHER % { github_url: Regexp.escape(github_url) }
+
+ text.scan(Regexp.new(cdn_url_matcher)).map(&:first) +
+ text.scan(Regexp.new(doc_url_matcher)).map(&:first)
+ end
+
private
+ def github_media_cdn
+ 'https://user-images.githubusercontent.com'
+ end
+
# Returns github domain without slash in the end
def github_url
oauth_config = Gitlab::Auth::OAuth::Provider.config_for('github') || {}
@@ -63,3 +93,4 @@ module Gitlab
end
end
end
+# rubocop:enable Style/FormatString
diff --git a/lib/gitlab/github_import/parallel_scheduling.rb b/lib/gitlab/github_import/parallel_scheduling.rb
index a8c18c74d24..bf5046de36c 100644
--- a/lib/gitlab/github_import/parallel_scheduling.rb
+++ b/lib/gitlab/github_import/parallel_scheduling.rb
@@ -63,7 +63,7 @@ module Gitlab
# Imports all the objects in sequence in the current thread.
def sequential_import
each_object_to_import do |object|
- repr = representation_class.from_api_response(object, additional_object_data)
+ repr = object_representation(object)
importer_class.new(repr, project, client).execute
end
@@ -83,7 +83,7 @@ module Gitlab
import_arguments = []
each_object_to_import do |object|
- repr = representation_class.from_api_response(object, additional_object_data)
+ repr = object_representation(object)
import_arguments << [project.id, repr.to_hash, waiter.key]
@@ -210,6 +210,10 @@ module Gitlab
{}
end
+ def object_representation(object)
+ representation_class.from_api_response(object, additional_object_data)
+ end
+
def info(project_id, extra = {})
Logger.info(log_attributes(project_id, extra))
end
diff --git a/lib/gitlab/github_import/representation/expose_attribute.rb b/lib/gitlab/github_import/representation/expose_attribute.rb
index d2438ee8094..84de4d4798d 100644
--- a/lib/gitlab/github_import/representation/expose_attribute.rb
+++ b/lib/gitlab/github_import/representation/expose_attribute.rb
@@ -20,6 +20,10 @@ module Gitlab
end
end
end
+
+ def [](key)
+ respond_to?(key.to_sym) ? attributes[key] : nil
+ end
end
end
end
diff --git a/lib/gitlab/github_import/representation/issue_event.rb b/lib/gitlab/github_import/representation/issue_event.rb
index 67a5df73a97..89271a7dcd6 100644
--- a/lib/gitlab/github_import/representation/issue_event.rb
+++ b/lib/gitlab/github_import/representation/issue_event.rb
@@ -10,7 +10,8 @@ module Gitlab
attr_reader :attributes
expose_attribute :id, :actor, :event, :commit_id, :label_title, :old_title, :new_title,
- :milestone_title, :issue, :source, :assignee, :assigner, :created_at
+ :milestone_title, :issue, :source, :assignee, :review_requester,
+ :requested_reviewer, :created_at
# attributes - A Hash containing the event details. The keys of this
# Hash (and any nested hashes) must be symbols.
@@ -47,7 +48,8 @@ module Gitlab
issue: event.issue&.to_h&.symbolize_keys,
source: event.source,
assignee: user_representation(event.assignee),
- assigner: user_representation(event.assigner),
+ requested_reviewer: user_representation(event.requested_reviewer),
+ review_requester: user_representation(event.review_requester),
created_at: event.created_at
)
end
@@ -57,7 +59,8 @@ module Gitlab
hash = Representation.symbolize_hash(raw_hash)
hash[:actor] = user_representation(hash[:actor], source: :hash)
hash[:assignee] = user_representation(hash[:assignee], source: :hash)
- hash[:assigner] = user_representation(hash[:assigner], source: :hash)
+ hash[:requested_reviewer] = user_representation(hash[:requested_reviewer], source: :hash)
+ hash[:review_requester] = user_representation(hash[:review_requester], source: :hash)
new(hash)
end
diff --git a/lib/gitlab/github_import/representation/protected_branch.rb b/lib/gitlab/github_import/representation/protected_branch.rb
new file mode 100644
index 00000000000..b80b7cf1076
--- /dev/null
+++ b/lib/gitlab/github_import/representation/protected_branch.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GithubImport
+ module Representation
+ class ProtectedBranch
+ include ToHash
+ include ExposeAttribute
+
+ attr_reader :attributes
+
+ expose_attribute :id, :allow_force_pushes
+
+ # Builds a Branch Protection info from a GitHub API response.
+ # Resource structure details:
+ # https://docs.github.com/en/rest/branches/branch-protection#get-branch-protection
+ # branch_protection - An instance of `Sawyer::Resource` containing the protection details.
+ def self.from_api_response(branch_protection, _additional_object_data = {})
+ branch_name = branch_protection.url.match(%r{/branches/(\S{1,255})/protection$})[1]
+
+ hash = {
+ id: branch_name,
+ allow_force_pushes: branch_protection.allow_force_pushes.enabled
+ }
+
+ new(hash)
+ end
+
+ # Builds a new Protection using a Hash that was built from a JSON payload.
+ def self.from_json_hash(raw_hash)
+ new(Representation.symbolize_hash(raw_hash))
+ end
+
+ # attributes - A Hash containing the raw Protection details. The keys of this
+ # Hash (and any nested hashes) must be symbols.
+ def initialize(attributes)
+ @attributes = attributes
+ end
+
+ def github_identifiers
+ { id: id }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/representation/release_attachments.rb b/lib/gitlab/github_import/representation/release_attachments.rb
new file mode 100644
index 00000000000..fd272be2405
--- /dev/null
+++ b/lib/gitlab/github_import/representation/release_attachments.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+# This class only partly represents Release record from DB and
+# is used to connect ReleasesAttachmentsImporter with ReleaseAttachmentsImporter
+# without modifying ObjectImporter a lot.
+# Attachments are inside release's `description`.
+module Gitlab
+ module GithubImport
+ module Representation
+ class ReleaseAttachments
+ include ToHash
+ include ExposeAttribute
+
+ attr_reader :attributes
+
+ expose_attribute :release_db_id, :description
+
+ # Builds a event from a GitHub API response.
+ #
+ # release - An instance of `Release` model.
+ def self.from_db_record(release)
+ new(
+ release_db_id: release.id,
+ description: release.description
+ )
+ end
+
+ def self.from_json_hash(raw_hash)
+ new Representation.symbolize_hash(raw_hash)
+ end
+
+ # attributes - A Hash containing the event details. The keys of this
+ # Hash (and any nested hashes) must be symbols.
+ def initialize(attributes)
+ @attributes = attributes
+ end
+
+ def github_identifiers
+ { db_id: release_db_id }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/sequential_importer.rb b/lib/gitlab/github_import/sequential_importer.rb
index 6bc37337799..ab37bc92ee7 100644
--- a/lib/gitlab/github_import/sequential_importer.rb
+++ b/lib/gitlab/github_import/sequential_importer.rb
@@ -16,6 +16,7 @@ module Gitlab
].freeze
PARALLEL_IMPORTERS = [
+ Importer::ProtectedBranchesImporter,
Importer::PullRequestsImporter,
Importer::IssuesImporter,
Importer::DiffNotesImporter,
diff --git a/lib/gitlab/github_import/single_endpoint_notes_importing.rb b/lib/gitlab/github_import/single_endpoint_notes_importing.rb
index 0a3559adde3..aea4059dfbc 100644
--- a/lib/gitlab/github_import/single_endpoint_notes_importing.rb
+++ b/lib/gitlab/github_import/single_endpoint_notes_importing.rb
@@ -63,23 +63,27 @@ module Gitlab
mark_as_imported(associated)
end
- def each_associated_page
+ def each_associated_page(&block)
parent_collection.each_batch(of: BATCH_SIZE, column: :iid) do |batch|
- batch.each do |parent_record|
- # The page counter needs to be scoped by parent_record to avoid skipping
- # pages of notes from already imported parent_record.
- page_counter = PageCounter.new(project, page_counter_id(parent_record))
- repo = project.import_source
- options = collection_options.merge(page: page_counter.current)
+ process_batch(batch, &block)
+ end
+ end
- client.each_page(collection_method, repo, parent_record.iid, options) do |page|
- next unless page_counter.set(page.number)
+ def process_batch(batch)
+ batch.each do |parent_record|
+ # The page counter needs to be scoped by parent_record to avoid skipping
+ # pages of notes from already imported parent_record.
+ page_counter = PageCounter.new(project, page_counter_id(parent_record))
+ repo = project.import_source
+ options = collection_options.merge(page: page_counter.current)
- yield parent_record, page
- end
+ client.each_page(collection_method, repo, parent_record.iid, options) do |page|
+ next unless page_counter.set(page.number)
- mark_parent_imported(parent_record)
+ yield parent_record, page
end
+
+ mark_parent_imported(parent_record)
end
end
diff --git a/lib/gitlab/github_import/user_finder.rb b/lib/gitlab/github_import/user_finder.rb
index 6d6a00d260d..1feb0d450f0 100644
--- a/lib/gitlab/github_import/user_finder.rb
+++ b/lib/gitlab/github_import/user_finder.rb
@@ -45,8 +45,10 @@ module Gitlab
object&.actor
when :assignee
object&.assignee
- when :assigner
- object&.assigner
+ when :requested_reviewer
+ object&.requested_reviewer
+ when :review_requester
+ object&.review_requester
else
object&.author
end
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index 5f1802e323c..08a614edb4b 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -56,7 +56,6 @@ module Gitlab
push_frontend_feature_flag(:new_header_search)
push_frontend_feature_flag(:source_editor_toolbar)
push_frontend_feature_flag(:gl_avatar_for_all_user_avatars)
- push_frontend_feature_flag(:mr_attention_requests, current_user)
end
# Exposes the state of a feature flag to the frontend code.
diff --git a/lib/gitlab/graphql/errors.rb b/lib/gitlab/graphql/errors.rb
index 40b90310e8b..657364abfdf 100644
--- a/lib/gitlab/graphql/errors.rb
+++ b/lib/gitlab/graphql/errors.rb
@@ -7,6 +7,7 @@ module Gitlab
ArgumentError = Class.new(BaseError)
ResourceNotAvailable = Class.new(BaseError)
MutationError = Class.new(BaseError)
+ LimitError = Class.new(BaseError)
end
end
end
diff --git a/lib/gitlab/graphql/limit/field_call_count.rb b/lib/gitlab/graphql/limit/field_call_count.rb
new file mode 100644
index 00000000000..4165970a2a6
--- /dev/null
+++ b/lib/gitlab/graphql/limit/field_call_count.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Graphql
+ module Limit
+ class FieldCallCount < ::GraphQL::Schema::FieldExtension
+ def resolve(object:, arguments:, context:)
+ raise Gitlab::Graphql::Errors::ArgumentError, 'Limit must be specified.' unless limit
+ raise Gitlab::Graphql::Errors::LimitError, error_message if increment_call_count(context) > limit
+
+ yield(object, arguments)
+ end
+
+ private
+
+ def increment_call_count(context)
+ context[:call_count] ||= {}
+ context[:call_count][field] ||= 0
+ context[:call_count][field] += 1
+ end
+
+ def limit
+ options[:limit]
+ end
+
+ def error_message
+ "\"#{field.graphql_name}\" field can be requested only for #{limit} #{field.owner.graphql_name}(s) at a time."
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/graphql/pagination/keyset/connection.rb b/lib/gitlab/graphql/pagination/keyset/connection.rb
index b074c273996..987a5e7b74b 100644
--- a/lib/gitlab/graphql/pagination/keyset/connection.rb
+++ b/lib/gitlab/graphql/pagination/keyset/connection.rb
@@ -59,11 +59,15 @@ module Gitlab
if before
true
elsif first
- case sliced_nodes
- when Array
- sliced_nodes.size > limit_value
+ if Feature.enabled?(:graphql_keyset_pagination_without_next_page_query)
+ limited_nodes.size > limit_value
else
- sliced_nodes.limit(1).offset(limit_value).exists? # rubocop: disable CodeReuse/ActiveRecord
+ 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
else
false
@@ -89,7 +93,7 @@ module Gitlab
# So we're ok loading them into memory here as that's bound to happen
# anyway. Having them ready means we can modify the result while
# rendering the fields.
- @nodes ||= limited_nodes.to_a
+ @nodes ||= limited_nodes.to_a.take(limit_value) # rubocop: disable CodeReuse/ActiveRecord
end
def items
@@ -116,15 +120,21 @@ module Gitlab
end
if last
- paginated_nodes = LastItems.take_items(sliced_nodes, limit_value + 1)
+ paginated_nodes = sliced_nodes.last(limit_value + 1)
# there is an extra node, so there is a previous page
@has_previous_page = paginated_nodes.count > limit_value
@has_previous_page ? paginated_nodes.last(limit_value) : paginated_nodes
elsif loaded?(sliced_nodes)
- sliced_nodes.take(limit_value) # rubocop: disable CodeReuse/ActiveRecord
+ 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
else
- sliced_nodes.limit(limit_value) # rubocop: disable CodeReuse/ActiveRecord
+ sliced_nodes.limit(limit_value)
end
end
end
@@ -141,7 +151,7 @@ module Gitlab
def limit_value
# note: only first _or_ last can be specified, not both
- @limit_value ||= [first, last, max_page_size].compact.min
+ @limit_value ||= [first, last, max_page_size, GitlabSchema.default_max_page_size].compact.min
end
def loaded?(items)
diff --git a/lib/gitlab/graphql/pagination/keyset/last_items.rb b/lib/gitlab/graphql/pagination/keyset/last_items.rb
deleted file mode 100644
index 960567a6fbc..00000000000
--- a/lib/gitlab/graphql/pagination/keyset/last_items.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Graphql
- module Pagination
- module Keyset
- # This class handles the last(N) ActiveRecord call even if a special ORDER BY configuration is present.
- # For the last(N) call, ActiveRecord calls reverse_order, however for some cases it raises
- # ActiveRecord::IrreversibleOrderError error.
- class LastItems
- # rubocop: disable CodeReuse/ActiveRecord
- def self.take_items(scope, count)
- if Gitlab::Pagination::Keyset::Order.keyset_aware?(scope)
- order = Gitlab::Pagination::Keyset::Order.extract_keyset_order_object(scope)
- items = scope.reorder(order.reversed_order).first(count)
- items.is_a?(Array) ? items.reverse : items
- else
- scope.last(count)
- end
- end
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/graphql/type_name_deprecations.rb b/lib/gitlab/graphql/type_name_deprecations.rb
index c27ad1d54f5..1ec6fd1c09f 100644
--- a/lib/gitlab/graphql/type_name_deprecations.rb
+++ b/lib/gitlab/graphql/type_name_deprecations.rb
@@ -14,6 +14,9 @@ module Gitlab
DEPRECATIONS = [
Gitlab::Graphql::DeprecationsBase::NameDeprecation.new(
old_name: 'CiRunnerUpgradeStatusType', new_name: 'CiRunnerUpgradeStatus', milestone: '15.3'
+ ),
+ Gitlab::Graphql::DeprecationsBase::NameDeprecation.new(
+ old_name: 'RunnerMembershipFilter', new_name: 'CiRunnerMembershipFilter', milestone: '15.4'
)
].freeze
diff --git a/lib/gitlab/harbor/query.rb b/lib/gitlab/harbor/query.rb
index c120810ecf1..fcd984b01ce 100644
--- a/lib/gitlab/harbor/query.rb
+++ b/lib/gitlab/harbor/query.rb
@@ -17,7 +17,7 @@ module Gitlab
message: 'Id invalid'
}, allow_blank: true
validates :artifact_id, format: {
- with: /\A[a-zA-Z0-9\_\.\-$]+\z/,
+ with: /\A[a-zA-Z0-9\_\.\-$:]+\z/,
message: 'Id invalid'
}, allow_blank: true
validates :sort, format: {
diff --git a/lib/gitlab/health_checks/gitaly_check.rb b/lib/gitlab/health_checks/gitaly_check.rb
index f5f142c251f..2bd8ea711b5 100644
--- a/lib/gitlab/health_checks/gitaly_check.rb
+++ b/lib/gitlab/health_checks/gitaly_check.rb
@@ -27,17 +27,35 @@ module Gitlab
end
def check(storage_name)
- serv = Gitlab::GitalyClient::HealthCheckService.new(storage_name)
- result = serv.check
+ storage_healthy = healthy(storage_name)
+ unless storage_healthy[:success]
+ return HealthChecks::Result.new(
+ name,
+ storage_healthy[:success],
+ storage_healthy[:message],
+ shard: storage_name
+ )
+ end
+ storage_ready = ready(storage_name)
HealthChecks::Result.new(
name,
- result[:success],
- result[:message],
+ storage_ready[:success],
+ storage_ready[:message],
shard: storage_name
)
end
+ def healthy(storage_name)
+ serv = Gitlab::GitalyClient::HealthCheckService.new(storage_name)
+ serv.check
+ end
+
+ def ready(storage_name)
+ serv = Gitlab::GitalyClient::ServerService.new(storage_name)
+ serv.readiness_check
+ end
+
private
def metric_prefix
diff --git a/lib/gitlab/health_checks/redis.rb b/lib/gitlab/health_checks/redis.rb
new file mode 100644
index 00000000000..895bce5a5a9
--- /dev/null
+++ b/lib/gitlab/health_checks/redis.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module HealthChecks
+ module Redis
+ ALL_INSTANCE_CHECKS =
+ ::Gitlab::Redis::ALL_CLASSES.map do |instance_class|
+ check_class = Class.new
+ check_class.extend(RedisAbstractCheck)
+ const_set("#{instance_class.store_name}Check", check_class)
+
+ check_class
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/health_checks/redis/cache_check.rb b/lib/gitlab/health_checks/redis/cache_check.rb
deleted file mode 100644
index bd843bdaac4..00000000000
--- a/lib/gitlab/health_checks/redis/cache_check.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module HealthChecks
- module Redis
- class CacheCheck
- extend RedisAbstractCheck
- end
- end
- end
-end
diff --git a/lib/gitlab/health_checks/redis/queues_check.rb b/lib/gitlab/health_checks/redis/queues_check.rb
deleted file mode 100644
index fb92db937dc..00000000000
--- a/lib/gitlab/health_checks/redis/queues_check.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module HealthChecks
- module Redis
- class QueuesCheck
- extend RedisAbstractCheck
- end
- end
- end
-end
diff --git a/lib/gitlab/health_checks/redis/rate_limiting_check.rb b/lib/gitlab/health_checks/redis/rate_limiting_check.rb
deleted file mode 100644
index 0e9d94f7dff..00000000000
--- a/lib/gitlab/health_checks/redis/rate_limiting_check.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module HealthChecks
- module Redis
- class RateLimitingCheck
- extend RedisAbstractCheck
- end
- end
- end
-end
diff --git a/lib/gitlab/health_checks/redis/redis_abstract_check.rb b/lib/gitlab/health_checks/redis/redis_abstract_check.rb
index ecad4b06ea9..9a9a4d1faba 100644
--- a/lib/gitlab/health_checks/redis/redis_abstract_check.rb
+++ b/lib/gitlab/health_checks/redis/redis_abstract_check.rb
@@ -10,12 +10,12 @@ module Gitlab
successful?(check)
end
- private
-
def redis_instance_class_name
Gitlab::Redis.const_get(redis_instance_name.camelize, false)
end
+ private
+
def metric_prefix
"redis_#{redis_instance_name}_ping"
end
diff --git a/lib/gitlab/health_checks/redis/redis_check.rb b/lib/gitlab/health_checks/redis/redis_check.rb
deleted file mode 100644
index c793a939abd..00000000000
--- a/lib/gitlab/health_checks/redis/redis_check.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module HealthChecks
- module Redis
- class RedisCheck
- extend SimpleAbstractCheck
-
- class << self
- private
-
- def metric_prefix
- 'redis_ping'
- end
-
- def successful?(result)
- result == true
- end
-
- def check
- redis_health_checks.all?(&:check_up)
- end
-
- def redis_health_checks
- [
- Gitlab::HealthChecks::Redis::CacheCheck,
- Gitlab::HealthChecks::Redis::QueuesCheck,
- Gitlab::HealthChecks::Redis::SharedStateCheck,
- Gitlab::HealthChecks::Redis::TraceChunksCheck,
- Gitlab::HealthChecks::Redis::RateLimitingCheck,
- Gitlab::HealthChecks::Redis::SessionsCheck
- ]
- end
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/health_checks/redis/sessions_check.rb b/lib/gitlab/health_checks/redis/sessions_check.rb
deleted file mode 100644
index 90a4c868f40..00000000000
--- a/lib/gitlab/health_checks/redis/sessions_check.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module HealthChecks
- module Redis
- class SessionsCheck
- extend RedisAbstractCheck
- end
- end
- end
-end
diff --git a/lib/gitlab/health_checks/redis/shared_state_check.rb b/lib/gitlab/health_checks/redis/shared_state_check.rb
deleted file mode 100644
index 80f91784b8c..00000000000
--- a/lib/gitlab/health_checks/redis/shared_state_check.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module HealthChecks
- module Redis
- class SharedStateCheck
- extend RedisAbstractCheck
- end
- end
- end
-end
diff --git a/lib/gitlab/health_checks/redis/trace_chunks_check.rb b/lib/gitlab/health_checks/redis/trace_chunks_check.rb
deleted file mode 100644
index 9a89a1ce51d..00000000000
--- a/lib/gitlab/health_checks/redis/trace_chunks_check.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module HealthChecks
- module Redis
- class TraceChunksCheck
- extend RedisAbstractCheck
- end
- end
- end
-end
diff --git a/lib/gitlab/hook_data/project_member_builder.rb b/lib/gitlab/hook_data/project_member_builder.rb
index 90fc83fdf21..2ee61705ec1 100644
--- a/lib/gitlab/hook_data/project_member_builder.rb
+++ b/lib/gitlab/hook_data/project_member_builder.rb
@@ -37,16 +37,16 @@ module Gitlab
project = project_member.project || Project.unscoped.find(project_member.source_id)
{
- project_name: project.name,
- project_path: project.path,
- project_path_with_namespace: project.full_path,
- project_id: project.id,
- user_username: project_member.user.username,
- user_name: project_member.user.name,
- user_email: project_member.user.email,
- user_id: project_member.user.id,
- access_level: project_member.human_access,
- project_visibility: project.visibility
+ project_name: project.name,
+ project_path: project.path,
+ project_path_with_namespace: project.full_path,
+ project_id: project.id,
+ user_username: project_member.user.username,
+ user_name: project_member.user.name,
+ user_email: project_member.user.email,
+ user_id: project_member.user.id,
+ access_level: project_member.human_access,
+ project_visibility: project.visibility
}
end
diff --git a/lib/gitlab/i18n.rb b/lib/gitlab/i18n.rb
index 30465ff5f74..5b9216c0914 100644
--- a/lib/gitlab/i18n.rb
+++ b/lib/gitlab/i18n.rb
@@ -44,30 +44,30 @@ module Gitlab
TRANSLATION_LEVELS = {
'bg' => 0,
'cs_CZ' => 0,
- 'da_DK' => 39,
+ 'da_DK' => 38,
'de' => 17,
'en' => 100,
'eo' => 0,
- 'es' => 38,
+ 'es' => 37,
'fil_PH' => 0,
'fr' => 11,
'gl_ES' => 0,
'id_ID' => 0,
'it' => 1,
- 'ja' => 32,
- 'ko' => 12,
+ 'ja' => 31,
+ 'ko' => 17,
'nb_NO' => 26,
'nl_NL' => 0,
'pl_PL' => 4,
- 'pt_BR' => 55,
- 'ro_RO' => 100,
+ 'pt_BR' => 56,
+ 'ro_RO' => 99,
'ru' => 27,
'si_LK' => 10,
- 'tr_TR' => 12,
+ 'tr_TR' => 11,
'uk' => 50,
- 'zh_CN' => 99,
+ 'zh_CN' => 97,
'zh_HK' => 1,
- 'zh_TW' => 100
+ 'zh_TW' => 99
}.freeze
private_constant :TRANSLATION_LEVELS
diff --git a/lib/gitlab/import_export/attributes_finder.rb b/lib/gitlab/import_export/attributes_finder.rb
index 4abc3da1190..8843b4f5755 100644
--- a/lib/gitlab/import_export/attributes_finder.rb
+++ b/lib/gitlab/import_export/attributes_finder.rb
@@ -12,6 +12,7 @@ module Gitlab
@methods = config[:methods] || {}
@preloads = config[:preloads] || {}
@export_reorders = config[:export_reorders] || {}
+ @include_if_exportable = config[:include_if_exportable] || {}
end
def find_root(model_key)
@@ -35,7 +36,8 @@ module Gitlab
methods: @methods[model_key],
include: resolve_model_tree(model_tree),
preload: resolve_preloads(model_key, model_tree),
- export_reorder: @export_reorders[model_key]
+ export_reorder: @export_reorders[model_key],
+ include_if_exportable: @include_if_exportable[model_key]
}.compact
end
diff --git a/lib/gitlab/import_export/base/relation_factory.rb b/lib/gitlab/import_export/base/relation_factory.rb
index 1cbfcbdb595..bbec473d29d 100644
--- a/lib/gitlab/import_export/base/relation_factory.rb
+++ b/lib/gitlab/import_export/base/relation_factory.rb
@@ -31,6 +31,8 @@ module Gitlab
TOKEN_RESET_MODELS = %i[Project Namespace Group Ci::Trigger Ci::Build Ci::Runner ProjectHook ErrorTracking::ProjectErrorTrackingSetting].freeze
+ attr_reader :relation_name, :importable
+
def self.create(*args, **kwargs)
new(*args, **kwargs).create
end
diff --git a/lib/gitlab/import_export/base/relation_object_saver.rb b/lib/gitlab/import_export/base/relation_object_saver.rb
index ea989487ebd..3c473449ec0 100644
--- a/lib/gitlab/import_export/base/relation_object_saver.rb
+++ b/lib/gitlab/import_export/base/relation_object_saver.rb
@@ -58,8 +58,19 @@ module Gitlab
records.each_slice(BATCH_SIZE) do |batch|
valid_records, invalid_records = batch.partition { |record| record.valid? }
- invalid_subrelations << invalid_records
relation_object.public_send(relation_name) << valid_records
+
+ # Attempt to save some of the invalid subrelations, as they might be valid after all.
+ # For example, a merge request `Approval` validates presence of merge_request_id.
+ # It is not present at a time of calling `#valid?` above, since it's indeed missing.
+ # However, when saving such subrelation against already persisted merge request
+ # such validation won't fail (e.g. `merge_request.approvals << Approval.new(user_id: 1)`),
+ # as we're operating on a merge request that has `id` present.
+ invalid_records.each do |invalid_record|
+ relation_object.public_send(relation_name) << invalid_record
+
+ invalid_subrelations << invalid_record unless invalid_record.persisted?
+ end
end
end
end
diff --git a/lib/gitlab/import_export/group/import_export.yml b/lib/gitlab/import_export/group/import_export.yml
index 8df5d52bf77..a08efdf400b 100644
--- a/lib/gitlab/import_export/group/import_export.yml
+++ b/lib/gitlab/import_export/group/import_export.yml
@@ -27,6 +27,26 @@ included_attributes:
- :name
namespace_settings:
- :prevent_sharing_groups_outside_hierarchy
+ iterations_cadence: &iterations_cadence_definition
+ - :group_id
+ - :created_at
+ - :updated_at
+ - :start_date
+ - :active
+ - :roll_over
+ - :title
+ - :description
+ - :sequence
+ iterations_cadences: *iterations_cadence_definition
+ iteration: &iteration_definition
+ - :iid
+ - :created_at
+ - :updated_at
+ - :start_date
+ - :due_date
+ - :group_id
+ - :description
+ iterations: *iteration_definition
excluded_attributes:
group:
@@ -44,6 +64,23 @@ excluded_attributes:
- :max_pages_size
epics:
- :state_id
+ iterations_cadence: &iterations_cadence_definition
+ - :id
+ - :next_run_date
+ - :duration_in_weeks
+ - :iterations_in_advance
+ - :automatic
+ iterations_cadences: *iterations_cadence_definition
+ iteration: &iteration_excluded_definition
+ - :id
+ - :title
+ - :title_html
+ - :project_id
+ - :description_html
+ - :cached_markdown_version
+ - :iterations_cadence_id
+ - :sequence
+ iterations: *iteration_excluded_definition
methods:
labels:
@@ -83,6 +120,7 @@ ee:
- events:
- :push_event_payload
- :system_note_metadata
+ - :resource_state_events
- boards:
- :board_assignee
- :milestone
@@ -92,3 +130,5 @@ ee:
- milestone:
- events:
- :push_event_payload
+ - iterations_cadences:
+ - :iterations
diff --git a/lib/gitlab/import_export/group/legacy_import_export.yml b/lib/gitlab/import_export/group/legacy_import_export.yml
index 082d2346f3d..6507def7d01 100644
--- a/lib/gitlab/import_export/group/legacy_import_export.yml
+++ b/lib/gitlab/import_export/group/legacy_import_export.yml
@@ -24,6 +24,29 @@ included_attributes:
- :username
author:
- :name
+ iterations_cadence: &iterations_cadence_definition
+ - :group_id
+ - :created_at
+ - :updated_at
+ - :start_date
+ - :next_run_date
+ - :duration_in_weeks
+ - :iterations_in_advance
+ - :active
+ - :automatic
+ - :roll_over
+ - :title
+ - :description
+ iterations_cadences: *iterations_cadence_definition
+ iteration: &iteration_definition
+ - :iid
+ - :created_at
+ - :updated_at
+ - :start_date
+ - :due_date
+ - :group_id
+ - :description
+ iterations: *iteration_definition
excluded_attributes:
group:
@@ -41,6 +64,18 @@ excluded_attributes:
- :extra_shared_runners_minutes_limit
epics:
- :state_id
+ iterations_cadence: &iterations_cadence_definition
+ - :id
+ iterations_cadences: *iterations_cadence_definition
+ iteration: &iteration_excluded_definition
+ - :id
+ - :title
+ - :title_html
+ - :project_id
+ - :description_html
+ - :cached_markdown_version
+ - :iterations_cadence_id
+ iterations: *iteration_excluded_definition
methods:
labels:
@@ -79,6 +114,7 @@ ee:
- :award_emoji
- events:
- :push_event_payload
+ - :resource_state_events
- boards:
- :board_assignee
- :milestone
@@ -88,3 +124,5 @@ ee:
- milestone:
- events:
- :push_event_payload
+ - iterations_cadences:
+ - :iterations
diff --git a/lib/gitlab/import_export/group/legacy_tree_restorer.rb b/lib/gitlab/import_export/group/legacy_tree_restorer.rb
index 8b39362b6bb..fa9e765b33a 100644
--- a/lib/gitlab/import_export/group/legacy_tree_restorer.rb
+++ b/lib/gitlab/import_export/group/legacy_tree_restorer.rb
@@ -68,23 +68,23 @@ module Gitlab
def restorer
@relation_tree_restorer ||= RelationTreeRestorer.new(
- user: @user,
- shared: @shared,
- relation_reader: relation_reader,
- members_mapper: members_mapper,
- object_builder: object_builder,
- relation_factory: relation_factory,
- reader: reader,
- importable: @group,
+ user: @user,
+ shared: @shared,
+ relation_reader: relation_reader,
+ members_mapper: members_mapper,
+ object_builder: object_builder,
+ relation_factory: relation_factory,
+ reader: reader,
+ importable: @group,
importable_attributes: @group_attributes,
- importable_path: nil
+ importable_path: nil
)
end
def create_group(group_hash:, parent_group:)
group_params = {
- name: group_hash['name'],
- path: group_hash['path'],
+ name: group_hash['name'],
+ path: group_hash['path'],
parent_id: parent_group&.id,
visibility_level: sub_group_visibility_level(group_hash, parent_group)
}
diff --git a/lib/gitlab/import_export/group/relation_factory.rb b/lib/gitlab/import_export/group/relation_factory.rb
index 258078d595b..1b8436c4ed9 100644
--- a/lib/gitlab/import_export/group/relation_factory.rb
+++ b/lib/gitlab/import_export/group/relation_factory.rb
@@ -5,10 +5,11 @@ module Gitlab
module Group
class RelationFactory < Base::RelationFactory
OVERRIDES = {
- labels: :group_labels,
+ labels: :group_labels,
priorities: :label_priorities,
- label: :group_label,
- parent: :epic
+ label: :group_label,
+ parent: :epic,
+ iterations_cadences: 'Iterations::Cadence'
}.freeze
EXISTING_OBJECT_RELATIONS = %i[
@@ -25,7 +26,10 @@ module Gitlab
private
def setup_models
- setup_note if @relation_name == :notes
+ case @relation_name
+ when :notes then setup_note
+ when :'Iterations::Cadence' then setup_iterations_cadence
+ end
update_group_references
end
@@ -44,6 +48,10 @@ module Gitlab
def use_attributes_permitter?
false
end
+
+ def setup_iterations_cadence
+ @relation_hash['automatic'] = false
+ end
end
end
end
diff --git a/lib/gitlab/import_export/group/relation_tree_restorer.rb b/lib/gitlab/import_export/group/relation_tree_restorer.rb
index fab677bd772..5a78f2fb531 100644
--- a/lib/gitlab/import_export/group/relation_tree_restorer.rb
+++ b/lib/gitlab/import_export/group/relation_tree_restorer.rb
@@ -136,9 +136,9 @@ module Gitlab
attributes_permitter.permit(importable_class_sym, params)
else
Gitlab::ImportExport::AttributeCleaner.clean(
- relation_hash: params,
+ relation_hash: params,
relation_class: importable_class,
- excluded_keys: excluded_keys_for_relation(importable_class_sym))
+ excluded_keys: excluded_keys_for_relation(importable_class_sym))
end
end
diff --git a/lib/gitlab/import_export/group/tree_saver.rb b/lib/gitlab/import_export/group/tree_saver.rb
index 796b9258e57..b4c86c3fc7f 100644
--- a/lib/gitlab/import_export/group/tree_saver.rb
+++ b/lib/gitlab/import_export/group/tree_saver.rb
@@ -46,7 +46,8 @@ module Gitlab
group,
group_tree,
json_writer,
- exportable_path: "groups/#{group.id}"
+ exportable_path: "groups/#{group.id}",
+ current_user: @current_user
).execute
end
diff --git a/lib/gitlab/import_export/json/streaming_serializer.rb b/lib/gitlab/import_export/json/streaming_serializer.rb
index 78f43f79072..99396d64779 100644
--- a/lib/gitlab/import_export/json/streaming_serializer.rb
+++ b/lib/gitlab/import_export/json/streaming_serializer.rb
@@ -6,11 +6,7 @@ module Gitlab
class StreamingSerializer
include Gitlab::ImportExport::CommandLineUtil
- BATCH_SIZE = 2
-
- def self.batch_size(exportable)
- BATCH_SIZE
- end
+ BATCH_SIZE = 100
class Raw < String
def to_json(*_args)
@@ -18,8 +14,9 @@ module Gitlab
end
end
- def initialize(exportable, relations_schema, json_writer, exportable_path:, logger: Gitlab::Export::Logger)
+ def initialize(exportable, relations_schema, json_writer, current_user:, exportable_path:, logger: Gitlab::Export::Logger)
@exportable = exportable
+ @current_user = current_user
@exportable_path = exportable_path
@relations_schema = relations_schema
@json_writer = json_writer
@@ -63,7 +60,7 @@ module Gitlab
private
- attr_reader :json_writer, :relations_schema, :exportable, :logger
+ attr_reader :json_writer, :relations_schema, :exportable, :logger, :current_user
def serialize_many_relations(key, records, options)
log_relation_export(key, records.size)
@@ -77,7 +74,7 @@ module Gitlab
batch.each do |record|
before_read_callback(record)
- items << Raw.new(record.to_json(options))
+ items << exportable_json_record(record, options, key)
after_read_callback(record)
end
@@ -87,8 +84,29 @@ module Gitlab
json_writer.write_relation_array(@exportable_path, key, enumerator)
end
+ def exportable_json_record(record, options, key)
+ associations = relations_schema[:include_if_exportable]&.dig(key)
+ return Raw.new(record.to_json(options)) unless associations && options[:include]
+
+ filtered_options = options.deep_dup
+ associations.each do |association|
+ filtered_options[:include].delete_if do |option|
+ !exportable_json_association?(option, record, association.to_sym)
+ end
+ end
+
+ Raw.new(record.to_json(filtered_options))
+ end
+
+ def exportable_json_association?(option, record, association)
+ return true unless option.has_key?(association)
+ return false unless record.respond_to?(:exportable_association?)
+
+ record.exportable_association?(association, current_user: current_user)
+ end
+
def batch(relation, key)
- opts = { of: batch_size }
+ opts = { of: BATCH_SIZE }
order_by = reorders(relation, key)
# we need to sort issues by non primary key column(relative_position)
@@ -115,7 +133,7 @@ module Gitlab
enumerator = Enumerator.new do |items|
records.each do |record|
- items << Raw.new(record.to_json(options))
+ items << exportable_json_record(record, options, key)
end
end
@@ -125,7 +143,7 @@ module Gitlab
def serialize_single_relation(key, record, options)
log_relation_export(key)
- json = Raw.new(record.to_json(options))
+ json = exportable_json_record(record, options, key)
json_writer.write_relation(@exportable_path, key, json)
end
@@ -138,10 +156,6 @@ module Gitlab
relations_schema[:preload]
end
- def batch_size
- @batch_size ||= self.class.batch_size(@exportable)
- end
-
def reorders(relation, key)
export_reorder = relations_schema[:export_reorder]&.dig(key)
return unless export_reorder
diff --git a/lib/gitlab/import_export/legacy_relation_tree_saver.rb b/lib/gitlab/import_export/legacy_relation_tree_saver.rb
index c6b961ea210..cf75a2c7fa8 100644
--- a/lib/gitlab/import_export/legacy_relation_tree_saver.rb
+++ b/lib/gitlab/import_export/legacy_relation_tree_saver.rb
@@ -7,7 +7,7 @@ module Gitlab
def serialize(exportable, relations_tree)
Gitlab::ImportExport::FastHashSerializer
- .new(exportable, relations_tree, batch_size: batch_size(exportable))
+ .new(exportable, relations_tree)
.execute
end
@@ -18,12 +18,6 @@ module Gitlab
File.write(File.join(dir_path, filename), tree_json)
end
-
- private
-
- def batch_size(exportable)
- Gitlab::ImportExport::Json::StreamingSerializer.batch_size(exportable)
- end
end
end
end
diff --git a/lib/gitlab/import_export/members_mapper.rb b/lib/gitlab/import_export/members_mapper.rb
index b1f2a17d4b7..c94549a2b3f 100644
--- a/lib/gitlab/import_export/members_mapper.rb
+++ b/lib/gitlab/import_export/members_mapper.rb
@@ -139,7 +139,7 @@ module Gitlab
end
def parsed_hash(member)
- Gitlab::ImportExport::AttributeCleaner.clean(relation_hash: member.deep_stringify_keys,
+ Gitlab::ImportExport::AttributeCleaner.clean(relation_hash: member.deep_stringify_keys,
relation_class: relation_class)
end
diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml
index c5b8f3fd35b..33e4823f192 100644
--- a/lib/gitlab/import_export/project/import_export.yml
+++ b/lib/gitlab/import_export/project/import_export.yml
@@ -29,6 +29,9 @@ tree:
- resource_label_events:
- label:
- :priorities
+ - resource_milestone_events:
+ - :milestone
+ - :resource_state_events
- designs:
- notes:
- :author
@@ -82,6 +85,9 @@ tree:
- resource_label_events:
- label:
- :priorities
+ - resource_milestone_events:
+ - :milestone
+ - :resource_state_events
- :external_pull_requests
- ci_pipelines:
- notes:
@@ -287,6 +293,7 @@ included_attributes:
- :forking_access_level
- :metrics_dashboard_access_level
- :operations_access_level
+ - :monitor_access_level
- :analytics_access_level
- :security_and_compliance_access_level
- :container_registry_access_level
@@ -551,6 +558,7 @@ included_attributes:
- :failure_reason
- :scheduled_at
- :scheduling_type
+ - :ci_stage
ci_pipelines:
- :ref
- :sha
@@ -599,7 +607,6 @@ included_attributes:
merge_request_assignees:
- :user_id
- :created_at
- - :state
merge_request_reviewers:
- :user_id
- :created_at
@@ -699,6 +706,7 @@ included_attributes:
- :metrics_dashboard_access_level
- :analytics_access_level
- :operations_access_level
+ - :monitor_access_level
- :security_and_compliance_access_level
- :container_registry_access_level
- :package_registry_access_level
@@ -721,6 +729,18 @@ included_attributes:
- :build_git_strategy
- :build_enabled
- :security_and_compliance_enabled
+ resource_milestone_events:
+ - :user_id
+ - :action
+ - :created_at
+ - :state
+ resource_state_events:
+ - :user_id
+ - :state
+ - :created_at
+ - :source_commit
+ - :close_after_error_tracking_resolve
+ - :close_auto_resolve_prometheus_alert
# Do not include the following attributes for the models specified.
excluded_attributes:
@@ -989,6 +1009,46 @@ excluded_attributes:
milestone_releases:
- :milestone_id
- :release_id
+ resource_milestone_events:
+ - :id
+ - :issue_id
+ - :merge_request_id
+ - :milestone_id
+ resource_state_events:
+ - :id
+ - :issue_id
+ - :merge_request_id
+ - :epic_id
+ - :source_merge_request_id
+ iteration:
+ - :id
+ - :title
+ - :title_html
+ - :project_id
+ - :description_html
+ - :cached_markdown_version
+ - :iterations_cadence_id
+ - :sequence
+ resource_iteration_events:
+ - :id
+ - :issue_id
+ - :merge_request_id
+ - :iteration_id
+ iterations_cadence:
+ - :id
+ - :last_run_date
+ - :duration_in_weeks
+ - :iterations_in_advance
+ - :automatic
+ - :group_id
+ - :created_at
+ - :updated_at
+ - :start_date
+ - :active
+ - :roll_over
+ - :description
+ - :sequence
+
methods:
notes:
- :type
@@ -1062,6 +1122,11 @@ ee:
- epic_issue:
- :epic
- :issuable_sla
+ - iteration:
+ - :iterations_cadence
+ - resource_iteration_events:
+ - iteration:
+ - :iterations_cadence
- protected_branches:
- :unprotect_access_levels
- protected_environments:
@@ -1120,5 +1185,44 @@ ee:
- :auto_fix_dependency_scanning
- :auto_fix_sast
project:
- - :requirements_enabled
- - :requirements_access_level
+ - :requirements_enabled
+ - :requirements_access_level
+ resource_iteration_events:
+ - :user_id
+ - :action
+ - :created_at
+ iteration:
+ - :iid
+ - :created_at
+ - :updated_at
+ - :start_date
+ - :due_date
+ - :group_id
+ - :description
+ iterations_cadence:
+ - :title
+
+ preloads:
+ issues:
+ epic:
+
+ # When associated resources are from outside the project, you might need to
+ # validate that a user who is exporting the project or 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 issue's `exportable_association?(:epic_issue,
+ # current_user: current_user)` method and include issue's epic_issue association
+ # for each issue only if the method returns true:
+ #
+ # Example:
+ # include_if_exportable:
+ # project:
+ # issues:
+ # - epic_issue
+ include_if_exportable:
+ project:
+ issues:
+ - :epic_issue
diff --git a/lib/gitlab/import_export/project/import_task.rb b/lib/gitlab/import_export/project/import_task.rb
index 59bb8af750e..89f2b36ea58 100644
--- a/lib/gitlab/import_export/project/import_task.rb
+++ b/lib/gitlab/import_export/project/import_task.rb
@@ -80,8 +80,8 @@ module Gitlab
def import_params
{
namespace_id: namespace.id,
- path: project_path,
- file: File.open(file_path)
+ path: project_path,
+ file: File.open(file_path)
}
end
diff --git a/lib/gitlab/import_export/project/object_builder.rb b/lib/gitlab/import_export/project/object_builder.rb
index bf60d115a25..50a67a746f8 100644
--- a/lib/gitlab/import_export/project/object_builder.rb
+++ b/lib/gitlab/import_export/project/object_builder.rb
@@ -21,7 +21,7 @@ module Gitlab
end
def find
- return if epic? && group.nil?
+ return if group_relation_without_group?
return find_diff_commit_user if diff_commit_user?
return find_diff_commit if diff_commit?
@@ -60,7 +60,7 @@ module Gitlab
def prepare_attributes
attributes.dup.tap do |atts|
- atts.delete('group') unless epic?
+ atts.delete('group') unless epic? || iteration?
if label?
atts['type'] = 'ProjectLabel' # Always create project labels
@@ -141,6 +141,10 @@ module Gitlab
klass == MergeRequestDiffCommit
end
+ def iteration?
+ klass == Iteration
+ end
+
# If an existing group milestone used the IID
# claim the IID back and set the group milestone to use one available
# This is necessary to fix situations like the following:
@@ -157,7 +161,13 @@ module Gitlab
milestone.ensure_project_iid!
milestone.save!
end
+
+ def group_relation_without_group?
+ (epic? || iteration?) && group.nil?
+ end
end
end
end
end
+
+Gitlab::ImportExport::Project::ObjectBuilder.prepend_mod
diff --git a/lib/gitlab/import_export/project/relation_saver.rb b/lib/gitlab/import_export/project/relation_saver.rb
index b40827e36f8..8e91adac196 100644
--- a/lib/gitlab/import_export/project/relation_saver.rb
+++ b/lib/gitlab/import_export/project/relation_saver.rb
@@ -32,7 +32,8 @@ module Gitlab
project,
reader.project_tree,
json_writer,
- exportable_path: 'project'
+ exportable_path: 'project',
+ current_user: nil
)
end
diff --git a/lib/gitlab/import_export/project/relation_tree_restorer.rb b/lib/gitlab/import_export/project/relation_tree_restorer.rb
index 6e9548f393a..47196db6f8a 100644
--- a/lib/gitlab/import_export/project/relation_tree_restorer.rb
+++ b/lib/gitlab/import_export/project/relation_tree_restorer.rb
@@ -5,7 +5,7 @@ module Gitlab
module Project
class RelationTreeRestorer < ImportExport::Group::RelationTreeRestorer
# Relations which cannot be saved at project level (and have a group assigned)
- GROUP_MODELS = [GroupLabel, Milestone, Epic].freeze
+ GROUP_MODELS = [GroupLabel, Milestone, Epic, Iteration].freeze
private
diff --git a/lib/gitlab/import_export/project/tree_saver.rb b/lib/gitlab/import_export/project/tree_saver.rb
index 1b54e4b975e..bd34cd3ff6e 100644
--- a/lib/gitlab/import_export/project/tree_saver.rb
+++ b/lib/gitlab/import_export/project/tree_saver.rb
@@ -50,7 +50,8 @@ module Gitlab
reader.project_tree,
json_writer,
exportable_path: "project",
- logger: @logger
+ logger: @logger,
+ current_user: @current_user
)
Retriable.retriable(on: Net::OpenTimeout, on_retry: on_retry) do
diff --git a/lib/gitlab/instrumentation/redis.rb b/lib/gitlab/instrumentation/redis.rb
index 4fee779c767..a371930621d 100644
--- a/lib/gitlab/instrumentation/redis.rb
+++ b/lib/gitlab/instrumentation/redis.rb
@@ -4,15 +4,20 @@ module Gitlab
module Instrumentation
# Aggregates Redis measurements from different request storage sources.
class Redis
+ # Actioncable has it's separate instrumentation, but isn't configurable
+ # in the same way as all the other instances using a class.
ActionCable = Class.new(RedisBase)
- Cache = Class.new(RedisBase).enable_redis_cluster_validation
- Queues = Class.new(RedisBase)
- SharedState = Class.new(RedisBase).enable_redis_cluster_validation
- TraceChunks = Class.new(RedisBase).enable_redis_cluster_validation
- RateLimiting = Class.new(RedisBase).enable_redis_cluster_validation
- Sessions = Class.new(RedisBase).enable_redis_cluster_validation
-
- STORAGES = [ActionCable, Cache, Queues, SharedState, TraceChunks, RateLimiting, Sessions].freeze
+
+ STORAGES = (
+ Gitlab::Redis::ALL_CLASSES.map do |redis_instance_class|
+ instrumentation_class = Class.new(RedisBase)
+
+ instrumentation_class.enable_redis_cluster_validation unless redis_instance_class == Gitlab::Redis::Queues
+
+ const_set(redis_instance_class.store_name, instrumentation_class)
+ instrumentation_class
+ end << ActionCable
+ ).freeze
# Milliseconds represented in seconds (from 1 millisecond to 2 seconds).
QUERY_TIME_BUCKETS = [0.001, 0.0025, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2].freeze
diff --git a/lib/gitlab/instrumentation/redis_base.rb b/lib/gitlab/instrumentation/redis_base.rb
index 0beab008f73..0bd10597f24 100644
--- a/lib/gitlab/instrumentation/redis_base.rb
+++ b/lib/gitlab/instrumentation/redis_base.rb
@@ -20,21 +20,19 @@ module Gitlab
::RequestStore[call_duration_key] += duration
end
- def add_call_details(duration, args)
+ def add_call_details(duration, commands)
return unless Gitlab::PerformanceBar.enabled_for_request?
- # redis-rb passes an array (e.g. [[:get, key]])
- return unless args.length == 1
detail_store << {
- cmd: args.first,
+ commands: commands,
duration: duration,
backtrace: ::Gitlab::BacktraceCleaner.clean_backtrace(caller)
}
end
- def increment_request_count
+ def increment_request_count(amount = 1)
::RequestStore[request_count_key] ||= 0
- ::RequestStore[request_count_key] += 1
+ ::RequestStore[request_count_key] += amount
end
def increment_read_bytes(num_bytes)
@@ -78,9 +76,9 @@ module Gitlab
self
end
- def instance_count_request
+ def instance_count_request(amount = 1)
@request_counter ||= Gitlab::Metrics.counter(:gitlab_redis_client_requests_total, 'Client side Redis request count, per Redis server')
- @request_counter.increment({ storage: storage_key })
+ @request_counter.increment({ storage: storage_key }, amount)
end
def instance_count_exception(ex)
diff --git a/lib/gitlab/instrumentation/redis_interceptor.rb b/lib/gitlab/instrumentation/redis_interceptor.rb
index 14474693ddf..7e2acb91b94 100644
--- a/lib/gitlab/instrumentation/redis_interceptor.rb
+++ b/lib/gitlab/instrumentation/redis_interceptor.rb
@@ -13,27 +13,15 @@ module Gitlab
end
end
- def call(*args, &block)
- start = Gitlab::Metrics::System.monotonic_time # must come first so that 'start' is always defined
- instrumentation_class.instance_count_request
- instrumentation_class.redis_cluster_validate!(args.first)
-
- super(*args, &block)
- rescue ::Redis::BaseError => ex
- instrumentation_class.instance_count_exception(ex)
- raise ex
- ensure
- duration = Gitlab::Metrics::System.monotonic_time - start
-
- unless APDEX_EXCLUDE.include?(command_from_args(args))
- instrumentation_class.instance_observe_duration(duration)
+ def call(command)
+ instrument_call([command]) do
+ super
end
+ end
- if ::RequestStore.active?
- # These metrics measure total Redis usage per Rails request / job.
- instrumentation_class.increment_request_count
- instrumentation_class.add_duration(duration)
- instrumentation_class.add_call_details(duration, args)
+ def call_pipeline(pipeline)
+ instrument_call(pipeline.commands) do
+ super
end
end
@@ -50,6 +38,31 @@ module Gitlab
private
+ def instrument_call(commands)
+ start = Gitlab::Metrics::System.monotonic_time # must come first so that 'start' is always defined
+ instrumentation_class.instance_count_request(commands.size)
+
+ commands.each { |c| instrumentation_class.redis_cluster_validate!(c) }
+
+ yield
+ rescue ::Redis::BaseError => ex
+ instrumentation_class.instance_count_exception(ex)
+ raise ex
+ ensure
+ duration = Gitlab::Metrics::System.monotonic_time - start
+
+ unless exclude_from_apdex?(commands)
+ commands.each { instrumentation_class.instance_observe_duration(duration / commands.size) }
+ end
+
+ if ::RequestStore.active?
+ # These metrics measure total Redis usage per Rails request / job.
+ instrumentation_class.increment_request_count(commands.size)
+ instrumentation_class.add_duration(duration)
+ instrumentation_class.add_call_details(duration, commands)
+ end
+ end
+
def measure_write_size(command)
size = 0
@@ -97,10 +110,8 @@ module Gitlab
@options[:instrumentation_class] # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
- def command_from_args(args)
- command = args[0]
- command = command[0] if command.is_a?(Array)
- command.to_s.downcase
+ def exclude_from_apdex?(commands)
+ commands.any? { |command| APDEX_EXCLUDE.include?(command.first.to_s.downcase) }
end
end
end
diff --git a/lib/gitlab/issuable/clone/copy_resource_events_service.rb b/lib/gitlab/issuable/clone/copy_resource_events_service.rb
index 563805fcb01..448ac4c2ae0 100644
--- a/lib/gitlab/issuable/clone/copy_resource_events_service.rb
+++ b/lib/gitlab/issuable/clone/copy_resource_events_service.rb
@@ -49,7 +49,7 @@ module Gitlab
event.attributes
.except(*blocked_state_event_attributes)
.merge(entity_key => new_entity.id,
- 'state' => ResourceStateEvent.states[event.state])
+ 'state' => ResourceStateEvent.states[event.state])
end
end
@@ -62,9 +62,9 @@ module Gitlab
event.attributes
.except('id')
.merge(entity_key => new_entity.id,
- 'milestone_id' => milestone&.id,
- 'action' => ResourceMilestoneEvent.actions[event.action],
- 'state' => ResourceMilestoneEvent.states[event.state])
+ 'milestone_id' => milestone&.id,
+ 'action' => ResourceMilestoneEvent.actions[event.action],
+ 'state' => ResourceMilestoneEvent.states[event.state])
end
def copy_events(table_name, events_to_copy)
diff --git a/lib/gitlab/jira_import.rb b/lib/gitlab/jira_import.rb
index 60344e4be68..fd41d9eeb5a 100644
--- a/lib/gitlab/jira_import.rb
+++ b/lib/gitlab/jira_import.rb
@@ -66,11 +66,6 @@ module Gitlab
cache_class.write(cache_key, value)
end
- def self.cache_issue_mapping(issue_id, jira_issue_id, project_id)
- cache_key = JiraImport.jira_item_cache_key(project_id, jira_issue_id, :issues)
- cache_class.write(cache_key, issue_id)
- end
-
def self.get_import_label_id(project_id)
cache_class.read(JiraImport.import_label_cache_key(project_id))
end
diff --git a/lib/gitlab/kubernetes.rb b/lib/gitlab/kubernetes.rb
index 15163bd4a57..0a0a1defd11 100644
--- a/lib/gitlab/kubernetes.rb
+++ b/lib/gitlab/kubernetes.rb
@@ -71,11 +71,11 @@ module Gitlab
containers.map do |container|
{
- selectors: { pod: pod_name, container: container["name"] },
- url: container_exec_url(api_url, namespace, pod_name, container["name"]),
+ selectors: { pod: pod_name, container: container["name"] },
+ url: container_exec_url(api_url, namespace, pod_name, container["name"]),
subprotocols: ['channel.k8s.io'],
- headers: ::Gitlab::Kubernetes.build_header_hash,
- created_at: created_at
+ headers: ::Gitlab::Kubernetes.build_header_hash,
+ created_at: created_at
}
end
end
diff --git a/lib/gitlab/legacy_github_import/client.rb b/lib/gitlab/legacy_github_import/client.rb
index 7d78c8dee25..dd1502bbbcd 100644
--- a/lib/gitlab/legacy_github_import/client.rb
+++ b/lib/gitlab/legacy_github_import/client.rb
@@ -79,6 +79,20 @@ module Gitlab
@users[login] = api.user(login)
end
+ def repository(id)
+ request(:repository, id).to_h
+ end
+
+ def repos
+ repositories = request(:repos, nil)
+
+ if repositories.is_a?(Array)
+ repositories.map(&:to_h)
+ else
+ repositories
+ end
+ end
+
private
def api_endpoint
diff --git a/lib/gitlab/legacy_github_import/project_creator.rb b/lib/gitlab/legacy_github_import/project_creator.rb
index c54325bcdf5..01e04fa9c81 100644
--- a/lib/gitlab/legacy_github_import/project_creator.rb
+++ b/lib/gitlab/legacy_github_import/project_creator.rb
@@ -18,11 +18,11 @@ module Gitlab
attrs = {
name: name,
path: name,
- description: repo.description,
+ description: repo[:description],
namespace_id: namespace.id,
visibility_level: visibility_level,
import_type: type,
- import_source: repo.full_name,
+ import_source: repo[:full_name],
import_url: import_url,
skip_wiki: skip_wiki
}.merge!(extra_attrs)
@@ -33,11 +33,11 @@ module Gitlab
private
def import_url
- repo.clone_url.sub('://', "://#{session_data[:github_access_token]}@")
+ repo[:clone_url].sub('://', "://#{session_data[:github_access_token]}@")
end
def visibility_level
- visibility_level = repo.private ? Gitlab::VisibilityLevel::PRIVATE : @namespace.visibility_level
+ visibility_level = repo[:private] ? Gitlab::VisibilityLevel::PRIVATE : @namespace.visibility_level
visibility_level = Gitlab::CurrentSettings.default_project_visibility if Gitlab::CurrentSettings.restricted_visibility_levels.include?(visibility_level)
visibility_level
@@ -49,7 +49,7 @@ module Gitlab
# repository already exist.
#
def skip_wiki
- repo.has_wiki?
+ repo[:has_wiki]
end
end
end
diff --git a/lib/gitlab/mailgun/webhook_processors/failure_logger.rb b/lib/gitlab/mailgun/webhook_processors/failure_logger.rb
index a7a85bd1672..fa72abf1311 100644
--- a/lib/gitlab/mailgun/webhook_processors/failure_logger.rb
+++ b/lib/gitlab/mailgun/webhook_processors/failure_logger.rb
@@ -5,11 +5,12 @@ module Gitlab
module WebhookProcessors
class FailureLogger < Base
def execute
- log_failure if permanent_failure? || temporary_failure_over_threshold?
+ log_failure if permanent_failure_over_threshold? || temporary_failure_over_threshold?
end
- def permanent_failure?
- payload['event'] == 'failed' && payload['severity'] == 'permanent'
+ def permanent_failure_over_threshold?
+ payload['event'] == 'failed' && payload['severity'] == 'permanent' &&
+ Gitlab::ApplicationRateLimiter.throttled?(:permanent_email_failure, scope: payload['recipient'])
end
def temporary_failure_over_threshold?
diff --git a/lib/gitlab/manifest_import/metadata.rb b/lib/gitlab/manifest_import/metadata.rb
index 80dff075391..6fe9bb10cdf 100644
--- a/lib/gitlab/manifest_import/metadata.rb
+++ b/lib/gitlab/manifest_import/metadata.rb
@@ -14,9 +14,9 @@ module Gitlab
def save(repositories, group_id)
Gitlab::Redis::SharedState.with do |redis|
- redis.multi do
- redis.set(key_for('repositories'), Gitlab::Json.dump(repositories), ex: EXPIRY_TIME)
- redis.set(key_for('group_id'), group_id, ex: EXPIRY_TIME)
+ redis.multi do |multi|
+ multi.set(key_for('repositories'), Gitlab::Json.dump(repositories), ex: EXPIRY_TIME)
+ multi.set(key_for('group_id'), group_id, ex: EXPIRY_TIME)
end
end
end
diff --git a/lib/gitlab/marginalia/comment.rb b/lib/gitlab/marginalia/comment.rb
index f635f41ec39..aab58bfa211 100644
--- a/lib/gitlab/marginalia/comment.rb
+++ b/lib/gitlab/marginalia/comment.rb
@@ -31,7 +31,7 @@ module Gitlab
if job.is_a?(ActionMailer::MailDeliveryJob)
{
"class" => job.arguments.first,
- "jid" => job.job_id
+ "jid" => job.job_id
}
else
job
diff --git a/lib/gitlab/markdown_cache/redis/store.rb b/lib/gitlab/markdown_cache/redis/store.rb
index 5a8efa34097..752ab153f37 100644
--- a/lib/gitlab/markdown_cache/redis/store.rb
+++ b/lib/gitlab/markdown_cache/redis/store.rb
@@ -10,9 +10,9 @@ module Gitlab
results = {}
Gitlab::Redis::Cache.with do |r|
- r.pipelined do
+ r.pipelined do |pipeline|
subjects.each do |subject|
- results[subject.cache_key] = new(subject).read
+ results[subject.cache_key] = new(subject).read(pipeline)
end
end
end
@@ -34,11 +34,15 @@ module Gitlab
end
end
- def read
+ def read(pipeline = nil)
@loaded = true
- Gitlab::Redis::Cache.with do |r|
- r.mapped_hmget(markdown_cache_key, *fields)
+ if pipeline
+ pipeline.mapped_hmget(markdown_cache_key, *fields)
+ else
+ Gitlab::Redis::Cache.with do |r|
+ r.mapped_hmget(markdown_cache_key, *fields)
+ end
end
end
diff --git a/lib/gitlab/memory/jemalloc.rb b/lib/gitlab/memory/jemalloc.rb
index 7163a70a5cb..e20e186cab9 100644
--- a/lib/gitlab/memory/jemalloc.rb
+++ b/lib/gitlab/memory/jemalloc.rb
@@ -27,21 +27,27 @@ module Gitlab
# 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:, format: STATS_DEFAULT_FORMAT, filename_label: nil)
+ def dump_stats(path:, tmp_dir: Dir.tmpdir, format: STATS_DEFAULT_FORMAT, filename_label: nil)
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(file_path, 'wb') do |io|
+ File.open(tmp_file_path, 'wb') do |io|
write_stats(stats_print, io, format_settings)
end
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
end
diff --git a/lib/gitlab/memory/reports/jemalloc_stats.rb b/lib/gitlab/memory/reports/jemalloc_stats.rb
index b99bec4ac3e..05f0717d7c3 100644
--- a/lib/gitlab/memory/reports/jemalloc_stats.rb
+++ b/lib/gitlab/memory/reports/jemalloc_stats.rb
@@ -18,12 +18,19 @@ module Gitlab
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)
end
def run
return unless active?
- Gitlab::Memory::Jemalloc.dump_stats(path: reports_path, filename_label: worker_id).tap { cleanup }
+ Gitlab::Memory::Jemalloc.dump_stats(path: reports_path, tmp_dir: @tmp_dir, filename_label: worker_id).tap do
+ cleanup
+ end
end
def active?
diff --git a/lib/gitlab/memory/watchdog.rb b/lib/gitlab/memory/watchdog.rb
index 91edb68ad66..38231fa933b 100644
--- a/lib/gitlab/memory/watchdog.rb
+++ b/lib/gitlab/memory/watchdog.rb
@@ -16,8 +16,9 @@ module Gitlab
# The duration for which a process may be above a given fragmentation
# threshold is computed as `max_strikes * sleep_time_seconds`.
class Watchdog
- DEFAULT_SLEEP_TIME_SECONDS = 60
- DEFAULT_HEAP_FRAG_THRESHOLD = 0.5
+ DEFAULT_SLEEP_TIME_SECONDS = 60 * 5
+ DEFAULT_MAX_HEAP_FRAG = 0.5
+ DEFAULT_MAX_MEM_GROWTH = 3.0
DEFAULT_MAX_STRIKES = 5
# This handler does nothing. It returns `false` to indicate to the
@@ -29,7 +30,7 @@ module Gitlab
class NullHandler
include Singleton
- def on_high_heap_fragmentation(value)
+ def call
# NOP
false
end
@@ -41,7 +42,7 @@ module Gitlab
@pid = pid
end
- def on_high_heap_fragmentation(value)
+ def call
Process.kill(:TERM, @pid)
true
end
@@ -55,7 +56,7 @@ module Gitlab
@worker = ::Puma::Cluster::WorkerHandle.new(0, $$, 0, puma_options)
end
- def on_high_heap_fragmentation(value)
+ def call
@worker.term
true
end
@@ -63,6 +64,9 @@ module Gitlab
# max_heap_fragmentation:
# The degree to which the Ruby heap is allowed to be fragmented. Range [0,1].
+ # max_mem_growth:
+ # A multiplier for how much excess private memory a worker can map compared to a reference process
+ # (itself or the primary in a pre-fork server.)
# max_strikes:
# How many times the process is allowed to be above max_heap_fragmentation before
# a handler is invoked.
@@ -71,7 +75,8 @@ module Gitlab
def initialize(
handler: NullHandler.instance,
logger: Logger.new($stdout),
- max_heap_fragmentation: ENV['GITLAB_MEMWD_MAX_HEAP_FRAG']&.to_f || DEFAULT_HEAP_FRAG_THRESHOLD,
+ max_heap_fragmentation: ENV['GITLAB_MEMWD_MAX_HEAP_FRAG']&.to_f || DEFAULT_MAX_HEAP_FRAG,
+ max_mem_growth: ENV['GITLAB_MEMWD_MAX_MEM_GROWTH']&.to_f || DEFAULT_MAX_MEM_GROWTH,
max_strikes: ENV['GITLAB_MEMWD_MAX_STRIKES']&.to_i || DEFAULT_MAX_STRIKES,
sleep_time_seconds: ENV['GITLAB_MEMWD_SLEEP_TIME_SEC']&.to_i || DEFAULT_SLEEP_TIME_SECONDS,
**options)
@@ -79,17 +84,37 @@ module Gitlab
@handler = handler
@logger = logger
- @max_heap_fragmentation = max_heap_fragmentation
@sleep_time_seconds = sleep_time_seconds
@max_strikes = max_strikes
+ @stats = {
+ heap_frag: {
+ max: max_heap_fragmentation,
+ strikes: 0
+ },
+ mem_growth: {
+ max: max_mem_growth,
+ strikes: 0
+ }
+ }
@alive = true
- @strikes = 0
init_prometheus_metrics(max_heap_fragmentation)
end
- attr_reader :strikes, :max_heap_fragmentation, :max_strikes, :sleep_time_seconds
+ attr_reader :max_strikes, :sleep_time_seconds
+
+ def max_heap_fragmentation
+ @stats[:heap_frag][:max]
+ end
+
+ def max_mem_growth
+ @stats[:mem_growth][:max]
+ end
+
+ def strikes(stat)
+ @stats[stat][:strikes]
+ end
def call
@logger.info(log_labels.merge(message: 'started'))
@@ -97,7 +122,10 @@ module Gitlab
while @alive
sleep(@sleep_time_seconds)
- monitor_heap_fragmentation if Feature.enabled?(:gitlab_memory_watchdog, type: :ops)
+ next unless Feature.enabled?(:gitlab_memory_watchdog, type: :ops)
+
+ monitor_heap_fragmentation
+ monitor_memory_growth
end
@logger.info(log_labels.merge(message: 'stopped'))
@@ -109,32 +137,73 @@ module Gitlab
private
- def monitor_heap_fragmentation
- heap_fragmentation = Gitlab::Metrics::Memory.gc_heap_fragmentation
+ def monitor_memory_condition(stat_key)
+ return unless @alive
+
+ stat = @stats[stat_key]
+
+ ok, labels = yield(stat)
- if heap_fragmentation > @max_heap_fragmentation
- @strikes += 1
- @heap_frag_violations.increment
+ if ok
+ stat[:strikes] = 0
else
- @strikes = 0
+ stat[:strikes] += 1
+ @counter_violations.increment(reason: stat_key.to_s)
end
- if @strikes > @max_strikes
- # If the handler returns true, it means the event is handled and we can shut down.
- @alive = !handle_heap_fragmentation_limit_exceeded(heap_fragmentation)
- @strikes = 0
+ if stat[:strikes] > @max_strikes
+ @alive = !memory_limit_exceeded_callback(stat_key, labels)
+ stat[:strikes] = 0
end
end
- def handle_heap_fragmentation_limit_exceeded(value)
- @logger.warn(
- log_labels.merge(
- message: 'heap fragmentation limit exceeded',
- memwd_cur_heap_frag: value
- ))
- @heap_frag_violations_handled.increment
+ def monitor_heap_fragmentation
+ monitor_memory_condition(:heap_frag) do |stat|
+ heap_fragmentation = Gitlab::Metrics::Memory.gc_heap_fragmentation
+ [
+ heap_fragmentation <= stat[:max],
+ {
+ message: 'heap fragmentation limit exceeded',
+ memwd_cur_heap_frag: heap_fragmentation,
+ memwd_max_heap_frag: stat[:max]
+ }
+ ]
+ end
+ end
+
+ def monitor_memory_growth
+ monitor_memory_condition(:mem_growth) do |stat|
+ worker_uss = Gitlab::Metrics::System.memory_usage_uss_pss[:uss]
+ reference_uss = reference_mem[:uss]
+ memory_limit = stat[:max] * reference_uss
+ [
+ worker_uss <= memory_limit,
+ {
+ message: 'memory limit exceeded',
+ memwd_uss_bytes: worker_uss,
+ memwd_ref_uss_bytes: reference_uss,
+ memwd_max_uss_bytes: memory_limit
+ }
+ ]
+ end
+ end
+
+ # On pre-fork systems this would be the primary process memory from which workers fork.
+ # Otherwise it is the current process' memory.
+ #
+ # We initialize this lazily because in the initializer the application may not have
+ # finished booting yet, which would yield an incorrect baseline.
+ def reference_mem
+ @reference_mem ||= Gitlab::Metrics::System.memory_usage_uss_pss(pid: Gitlab::Cluster::PRIMARY_PID)
+ end
+
+ def memory_limit_exceeded_callback(stat_key, handler_labels)
+ all_labels = log_labels.merge(handler_labels)
+ .merge(memwd_cur_strikes: strikes(stat_key))
+ @logger.warn(all_labels)
+ @counter_violations_handled.increment(reason: stat_key.to_s)
- handler.on_high_heap_fragmentation(value)
+ handler.call
end
def handler
@@ -151,9 +220,7 @@ module Gitlab
worker_id: worker_id,
memwd_handler_class: handler.class.name,
memwd_sleep_time_s: @sleep_time_seconds,
- memwd_max_heap_frag: @max_heap_fragmentation,
memwd_max_strikes: @max_strikes,
- memwd_cur_strikes: @strikes,
memwd_rss_bytes: process_rss_bytes
}
end
@@ -174,14 +241,14 @@ module Gitlab
@heap_frag_limit.set({}, max_heap_fragmentation)
default_labels = { pid: worker_id }
- @heap_frag_violations = Gitlab::Metrics.counter(
- :gitlab_memwd_heap_frag_violations_total,
- 'Total number of times heap fragmentation in a Ruby process exceeded its allowed maximum',
+ @counter_violations = Gitlab::Metrics.counter(
+ :gitlab_memwd_violations_total,
+ 'Total number of times a Ruby process violated a memory threshold',
default_labels
)
- @heap_frag_violations_handled = Gitlab::Metrics.counter(
- :gitlab_memwd_heap_frag_violations_handled_total,
- 'Total number of times heap fragmentation violations in a Ruby process were handled',
+ @counter_violations_handled = Gitlab::Metrics.counter(
+ :gitlab_memwd_violations_handled_total,
+ 'Total number of times Ruby process memory violations were handled',
default_labels
)
end
diff --git a/lib/gitlab/metrics/dashboard/stages/grafana_formatter.rb b/lib/gitlab/metrics/dashboard/stages/grafana_formatter.rb
index 55d14d6f94a..622b6adec7e 100644
--- a/lib/gitlab/metrics/dashboard/stages/grafana_formatter.rb
+++ b/lib/gitlab/metrics/dashboard/stages/grafana_formatter.rb
@@ -40,8 +40,8 @@ module Gitlab
def formatted_panel
{
- title: panel[:title],
- type: CHART_TYPE,
+ title: panel[:title],
+ type: CHART_TYPE,
y_label: '', # Grafana panels do not include a Y-Axis label
metrics: panel[:targets].map.with_index do |target, idx|
formatted_metric(target, idx)
@@ -51,9 +51,9 @@ module Gitlab
def formatted_metric(metric, idx)
{
- id: "#{metric[:legendFormat]}_#{idx}",
- query_range: format_query(metric),
- label: replace_variables(metric[:legendFormat]),
+ id: "#{metric[:legendFormat]}_#{idx}",
+ query_range: format_query(metric),
+ label: replace_variables(metric[:legendFormat]),
prometheus_endpoint_path: prometheus_endpoint_for_metric(metric)
}.compact
end
diff --git a/lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics.rb b/lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics.rb
index 4e46eec17d6..3650ddf698a 100644
--- a/lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics.rb
+++ b/lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics.rb
@@ -24,15 +24,15 @@ module Gitlab
panel_group[:panels].each do |panel|
panel[:metrics].each do |metric|
prometheus_metrics << {
- project: project,
- title: panel[:title],
- y_label: panel[:y_label],
- query: metric[:query_range] || metric[:query],
- unit: metric[:unit],
- legend: metric[:label],
- identifier: metric[:id],
- group: Enums::PrometheusMetric.groups[:custom],
- common: false,
+ project: project,
+ title: panel[:title],
+ y_label: panel[:y_label],
+ query: metric[:query_range] || metric[:query],
+ unit: metric[:unit],
+ legend: metric[:label],
+ identifier: metric[:id],
+ group: Enums::PrometheusMetric.groups[:custom],
+ common: false,
dashboard_path: dashboard_path
}.compact
end
diff --git a/lib/gitlab/metrics/dashboard/validator/client.rb b/lib/gitlab/metrics/dashboard/validator/client.rb
index 588c677ca28..29f1274a097 100644
--- a/lib/gitlab/metrics/dashboard/validator/client.rb
+++ b/lib/gitlab/metrics/dashboard/validator/client.rb
@@ -34,8 +34,8 @@ module Gitlab
def post_schema_validator
PostSchemaValidator.new(
- project: project,
- metric_ids: custom_formats.metric_ids_cache,
+ project: project,
+ metric_ids: custom_formats.metric_ids_cache,
dashboard_path: dashboard_path
)
end
diff --git a/lib/gitlab/metrics/exporter/metrics_middleware.rb b/lib/gitlab/metrics/exporter/metrics_middleware.rb
index e17f1c13cf0..258b655229e 100644
--- a/lib/gitlab/metrics/exporter/metrics_middleware.rb
+++ b/lib/gitlab/metrics/exporter/metrics_middleware.rb
@@ -27,8 +27,8 @@ module Gitlab
labels = {
method: env['REQUEST_METHOD'].downcase,
- path: env['PATH_INFO'].to_s,
- code: response.first.to_s
+ path: env['PATH_INFO'].to_s,
+ code: response.first.to_s
}
@requests_total.increment(labels)
diff --git a/lib/gitlab/metrics/global_search_slis.rb b/lib/gitlab/metrics/global_search_slis.rb
new file mode 100644
index 00000000000..e37129fed38
--- /dev/null
+++ b/lib/gitlab/metrics/global_search_slis.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Metrics
+ module GlobalSearchSlis
+ class << self
+ # The following targets are the 99.95th percentile of code searches
+ # gathered on 24-08-2022
+ # from https://log.gprd.gitlab.net/goto/0c89cd80-23af-11ed-8656-f5f2137823ba (internal only)
+ BASIC_CONTENT_TARGET_S = 7.031
+ BASIC_CODE_TARGET_S = 21.903
+ ADVANCED_CONTENT_TARGET_S = 4.865
+ ADVANCED_CODE_TARGET_S = 13.546
+
+ def initialize_slis!
+ if Feature.enabled?(:global_search_custom_slis)
+ Gitlab::Metrics::Sli::Apdex.initialize_sli(:global_search, possible_labels)
+ end
+
+ return unless Feature.enabled?(:global_search_error_rate_sli)
+
+ Gitlab::Metrics::Sli::ErrorRate.initialize_sli(:global_search, possible_labels)
+ end
+
+ def record_apdex(elapsed:, search_type:, search_level:, search_scope:)
+ return unless Feature.enabled?(:global_search_custom_slis)
+
+ Gitlab::Metrics::Sli::Apdex[:global_search].increment(
+ labels: labels(search_type: search_type, search_level: search_level, search_scope: search_scope),
+ success: elapsed < duration_target(search_type, search_scope)
+ )
+ 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
+ )
+ end
+
+ private
+
+ def duration_target(search_type, search_scope)
+ if search_type == 'basic' && content_search?(search_scope)
+ BASIC_CONTENT_TARGET_S
+ elsif search_type == 'basic' && code_search?(search_scope)
+ BASIC_CODE_TARGET_S
+ elsif search_type == 'advanced' && content_search?(search_scope)
+ ADVANCED_CONTENT_TARGET_S
+ elsif search_type == 'advanced' && code_search?(search_scope)
+ ADVANCED_CODE_TARGET_S
+ end
+ end
+
+ def search_types
+ %w[basic advanced]
+ end
+
+ def search_levels
+ %w[project group global]
+ end
+
+ def search_scopes
+ Gitlab::Search::AbuseDetection::ALLOWED_SCOPES
+ end
+
+ def endpoint_ids
+ ['SearchController#show', 'GET /api/:version/search', 'GET /api/:version/projects/:id/(-/)search',
+ 'GET /api/:version/groups/:id/(-/)search']
+ end
+
+ def possible_labels
+ search_types.flat_map do |search_type|
+ search_levels.flat_map do |search_level|
+ search_scopes.flat_map do |search_scope|
+ endpoint_ids.flat_map do |endpoint_id|
+ {
+ search_type: search_type,
+ search_level: search_level,
+ search_scope: search_scope,
+ endpoint_id: endpoint_id
+ }
+ end
+ end
+ end
+ end
+ end
+
+ def labels(search_type:, search_level:, search_scope:)
+ {
+ search_type: search_type,
+ search_level: search_level,
+ search_scope: search_scope,
+ endpoint_id: endpoint_id
+ }
+ end
+
+ def endpoint_id
+ ::Gitlab::ApplicationContext.current_context_attribute(:caller_id)
+ end
+
+ def code_search?(search_scope)
+ search_scope == 'blobs'
+ end
+
+ def content_search?(search_scope)
+ !code_search?(search_scope)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/metrics/samplers/puma_sampler.rb b/lib/gitlab/metrics/samplers/puma_sampler.rb
index 848a55e59ff..d818aa43853 100644
--- a/lib/gitlab/metrics/samplers/puma_sampler.rb
+++ b/lib/gitlab/metrics/samplers/puma_sampler.rb
@@ -12,15 +12,15 @@ module Gitlab
def init_metrics
{
- puma_workers: ::Gitlab::Metrics.gauge(:puma_workers, 'Total number of workers'),
- puma_running_workers: ::Gitlab::Metrics.gauge(:puma_running_workers, 'Number of active workers'),
- puma_stale_workers: ::Gitlab::Metrics.gauge(:puma_stale_workers, 'Number of stale workers'),
- puma_running: ::Gitlab::Metrics.gauge(:puma_running, 'Number of running threads'),
+ puma_workers: ::Gitlab::Metrics.gauge(:puma_workers, 'Total number of workers'),
+ puma_running_workers: ::Gitlab::Metrics.gauge(:puma_running_workers, 'Number of active workers'),
+ puma_stale_workers: ::Gitlab::Metrics.gauge(:puma_stale_workers, 'Number of stale workers'),
+ puma_running: ::Gitlab::Metrics.gauge(:puma_running, 'Number of running threads'),
puma_queued_connections: ::Gitlab::Metrics.gauge(:puma_queued_connections, 'Number of connections in that worker\'s "todo" set waiting for a worker thread'),
puma_active_connections: ::Gitlab::Metrics.gauge(:puma_active_connections, 'Number of threads processing a request'),
- puma_pool_capacity: ::Gitlab::Metrics.gauge(:puma_pool_capacity, 'Number of requests the worker is capable of taking right now'),
- puma_max_threads: ::Gitlab::Metrics.gauge(:puma_max_threads, 'Maximum number of worker threads'),
- puma_idle_threads: ::Gitlab::Metrics.gauge(:puma_idle_threads, 'Number of spawned threads which are not processing a request')
+ puma_pool_capacity: ::Gitlab::Metrics.gauge(:puma_pool_capacity, 'Number of requests the worker is capable of taking right now'),
+ puma_max_threads: ::Gitlab::Metrics.gauge(:puma_max_threads, 'Maximum number of worker threads'),
+ puma_idle_threads: ::Gitlab::Metrics.gauge(:puma_idle_threads, 'Number of spawned threads which are not processing a request')
}
end
diff --git a/lib/gitlab/metrics/samplers/ruby_sampler.rb b/lib/gitlab/metrics/samplers/ruby_sampler.rb
index 8e002293347..4fe338ffc7f 100644
--- a/lib/gitlab/metrics/samplers/ruby_sampler.rb
+++ b/lib/gitlab/metrics/samplers/ruby_sampler.rb
@@ -31,16 +31,16 @@ module Gitlab
def init_metrics
metrics = {
- file_descriptors: ::Gitlab::Metrics.gauge(metric_name(:file, :descriptors), 'File descriptors used', labels),
- process_cpu_seconds_total: ::Gitlab::Metrics.gauge(metric_name(:process, :cpu_seconds_total), 'Process CPU seconds total'),
- process_max_fds: ::Gitlab::Metrics.gauge(metric_name(:process, :max_fds), 'Process max fds'),
- process_resident_memory_bytes: ::Gitlab::Metrics.gauge(metric_name(:process, :resident_memory_bytes), 'Memory used (RSS)', labels),
- process_unique_memory_bytes: ::Gitlab::Metrics.gauge(metric_name(:process, :unique_memory_bytes), 'Memory used (USS)', labels),
+ file_descriptors: ::Gitlab::Metrics.gauge(metric_name(:file, :descriptors), 'File descriptors used', labels),
+ process_cpu_seconds_total: ::Gitlab::Metrics.gauge(metric_name(:process, :cpu_seconds_total), 'Process CPU seconds total'),
+ process_max_fds: ::Gitlab::Metrics.gauge(metric_name(:process, :max_fds), 'Process max fds'),
+ process_resident_memory_bytes: ::Gitlab::Metrics.gauge(metric_name(:process, :resident_memory_bytes), 'Memory used (RSS)', labels),
+ process_unique_memory_bytes: ::Gitlab::Metrics.gauge(metric_name(:process, :unique_memory_bytes), 'Memory used (USS)', labels),
process_proportional_memory_bytes: ::Gitlab::Metrics.gauge(metric_name(:process, :proportional_memory_bytes), 'Memory used (PSS)', labels),
- process_start_time_seconds: ::Gitlab::Metrics.gauge(metric_name(:process, :start_time_seconds), 'Process start time seconds'),
- sampler_duration: ::Gitlab::Metrics.counter(metric_name(:sampler, :duration_seconds_total), 'Sampler time', labels),
- gc_duration_seconds: ::Gitlab::Metrics.histogram(metric_name(:gc, :duration_seconds), 'GC time', labels, GC_REPORT_BUCKETS),
- heap_fragmentation: ::Gitlab::Metrics.gauge(metric_name(:gc_stat_ext, :heap_fragmentation), 'Ruby heap fragmentation', labels)
+ process_start_time_seconds: ::Gitlab::Metrics.gauge(metric_name(:process, :start_time_seconds), 'Process start time seconds'),
+ sampler_duration: ::Gitlab::Metrics.counter(metric_name(:sampler, :duration_seconds_total), 'Sampler time', labels),
+ gc_duration_seconds: ::Gitlab::Metrics.histogram(metric_name(:gc, :duration_seconds), 'GC time', labels, GC_REPORT_BUCKETS),
+ heap_fragmentation: ::Gitlab::Metrics.gauge(metric_name(:gc_stat_ext, :heap_fragmentation), 'Ruby heap fragmentation', labels)
}
GC.stat.keys.each do |key|
diff --git a/lib/gitlab/metrics/system.rb b/lib/gitlab/metrics/system.rb
index e646846face..d7eef722d6e 100644
--- a/lib/gitlab/metrics/system.rb
+++ b/lib/gitlab/metrics/system.rb
@@ -10,8 +10,8 @@ module Gitlab
extend self
PROC_STAT_PATH = '/proc/self/stat'
- PROC_STATUS_PATH = '/proc/self/status'
- PROC_SMAPS_ROLLUP_PATH = '/proc/self/smaps_rollup'
+ PROC_STATUS_PATH = '/proc/%s/status'
+ PROC_SMAPS_ROLLUP_PATH = '/proc/%s/smaps_rollup'
PROC_LIMITS_PATH = '/proc/self/limits'
PROC_FD_GLOB = '/proc/self/fd/*'
@@ -34,14 +34,14 @@ module Gitlab
}
end
- # Returns the current process' RSS (resident set size) in bytes.
- def memory_usage_rss
- sum_matches(PROC_STATUS_PATH, rss: RSS_PATTERN)[:rss].kilobytes
+ # Returns the given process' RSS (resident set size) in bytes.
+ def memory_usage_rss(pid: 'self')
+ sum_matches(PROC_STATUS_PATH % pid, rss: RSS_PATTERN)[:rss].kilobytes
end
- # Returns the current process' USS/PSS (unique/proportional set size) in bytes.
- def memory_usage_uss_pss
- sum_matches(PROC_SMAPS_ROLLUP_PATH, uss: PRIVATE_PAGES_PATTERN, pss: PSS_PATTERN)
+ # Returns the given process' USS/PSS (unique/proportional set size) in bytes.
+ def memory_usage_uss_pss(pid: 'self')
+ sum_matches(PROC_SMAPS_ROLLUP_PATH % pid, uss: PRIVATE_PAGES_PATTERN, pss: PSS_PATTERN)
.transform_values(&:kilobytes)
end
diff --git a/lib/gitlab/nav/top_nav_menu_builder.rb b/lib/gitlab/nav/top_nav_menu_builder.rb
index 721ae1889b8..dca3432a6a1 100644
--- a/lib/gitlab/nav/top_nav_menu_builder.rb
+++ b/lib/gitlab/nav/top_nav_menu_builder.rb
@@ -6,9 +6,15 @@ module Gitlab
def initialize
@primary = []
@secondary = []
+ @last_header_added = nil
end
- def add_primary_menu_item(**args)
+ def add_primary_menu_item(header: nil, **args)
+ if header && (header != @last_header_added)
+ add_menu_header(dest: @primary, title: header)
+ @last_header_added = header
+ end
+
add_menu_item(dest: @primary, **args)
end
@@ -30,6 +36,12 @@ module Gitlab
dest.push(item)
end
+
+ def add_menu_header(dest:, **args)
+ header = ::Gitlab::Nav::TopNavMenuHeader.build(**args)
+
+ dest.push(header)
+ end
end
end
end
diff --git a/lib/gitlab/nav/top_nav_menu_header.rb b/lib/gitlab/nav/top_nav_menu_header.rb
new file mode 100644
index 00000000000..520091dbd97
--- /dev/null
+++ b/lib/gitlab/nav/top_nav_menu_header.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Nav
+ class TopNavMenuHeader
+ def self.build(title:)
+ {
+ type: :header,
+ title: title
+ }
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/nav/top_nav_menu_item.rb b/lib/gitlab/nav/top_nav_menu_item.rb
index 4cb38e6bb9b..75eb0e8a264 100644
--- a/lib/gitlab/nav/top_nav_menu_item.rb
+++ b/lib/gitlab/nav/top_nav_menu_item.rb
@@ -11,6 +11,7 @@ module Gitlab
def self.build(id:, title:, active: false, icon: '', href: '', view: '', css_class: nil, data: nil, emoji: nil)
{
id: id,
+ type: :item,
title: title,
active: active,
icon: icon,
diff --git a/lib/gitlab/nav/top_nav_view_model_builder.rb b/lib/gitlab/nav/top_nav_view_model_builder.rb
index 11ca6a3a3ba..a8e25708107 100644
--- a/lib/gitlab/nav/top_nav_view_model_builder.rb
+++ b/lib/gitlab/nav/top_nav_view_model_builder.rb
@@ -42,11 +42,14 @@ module Gitlab
def build
menu = @menu_builder.build
+ hide_menu_text = Feature.enabled?(:new_navbar_layout)
+
menu.merge({
views: @views,
shortcuts: @shortcuts,
- activeTitle: _('Menu')
- })
+ menuTitle: (_('Menu') unless hide_menu_text),
+ menuTooltip: (_('Main menu') if hide_menu_text)
+ }.compact)
end
end
end
diff --git a/lib/gitlab/no_cache_headers.rb b/lib/gitlab/no_cache_headers.rb
index f80ca2c1369..2d03741480d 100644
--- a/lib/gitlab/no_cache_headers.rb
+++ b/lib/gitlab/no_cache_headers.rb
@@ -4,8 +4,8 @@ module Gitlab
module NoCacheHeaders
DEFAULT_GITLAB_NO_CACHE_HEADERS = {
'Cache-Control' => "#{ActionDispatch::Http::Cache::Response::DEFAULT_CACHE_CONTROL}, no-store, no-cache",
- 'Pragma' => 'no-cache', # HTTP 1.0 compatibility
- 'Expires' => 'Fri, 01 Jan 1990 00:00:00 GMT'
+ 'Pragma' => 'no-cache', # HTTP 1.0 compatibility
+ 'Expires' => 'Fri, 01 Jan 1990 00:00:00 GMT'
}.freeze
def no_cache_headers
diff --git a/lib/gitlab/pagination/gitaly_keyset_pager.rb b/lib/gitlab/pagination/gitaly_keyset_pager.rb
index 1f1061fe4f1..d4de2791195 100644
--- a/lib/gitlab/pagination/gitaly_keyset_pager.rb
+++ b/lib/gitlab/pagination/gitaly_keyset_pager.rb
@@ -38,7 +38,7 @@ module Gitlab
if finder.is_a?(BranchesFinder)
Feature.enabled?(:branch_list_keyset_pagination, project)
elsif finder.is_a?(TagsFinder)
- Feature.enabled?(:tag_list_keyset_pagination, project)
+ true
elsif finder.is_a?(::Repositories::TreeFinder)
Feature.enabled?(:repository_tree_gitaly_pagination, project)
else
@@ -52,7 +52,7 @@ module Gitlab
if finder.is_a?(BranchesFinder)
Feature.enabled?(:branch_list_keyset_pagination, project)
elsif finder.is_a?(TagsFinder)
- Feature.enabled?(:tag_list_keyset_pagination, project)
+ true
elsif finder.is_a?(::Repositories::TreeFinder)
Feature.enabled?(:repository_tree_gitaly_pagination, project)
else
diff --git a/lib/gitlab/pagination/keyset/column_order_definition.rb b/lib/gitlab/pagination/keyset/column_order_definition.rb
index 302e7b406b1..d1fe1d2dfc1 100644
--- a/lib/gitlab/pagination/keyset/column_order_definition.rb
+++ b/lib/gitlab/pagination/keyset/column_order_definition.rb
@@ -213,7 +213,7 @@ module Gitlab
attr_reader :reversed_order_expression, :nullable, :distinct
def calculate_reversed_order(order_expression)
- unless AREL_ORDER_CLASSES.has_key?(order_expression.class) # Arel can reverse simple orders
+ unless order_expression.is_a?(Arel::Nodes::Ordering)
raise "Couldn't determine reversed order for `#{order_expression}`, please provide the `reversed_order_expression` parameter."
end
@@ -229,10 +229,10 @@ module Gitlab
end
def parse_order_direction(order_expression, order_direction)
- transformed_order_direction = if order_direction.nil? && AREL_ORDER_CLASSES[order_expression.class]
- AREL_ORDER_CLASSES[order_expression.class]
- elsif order_direction.present?
+ transformed_order_direction = if order_direction.present?
order_direction.to_s.downcase.to_sym
+ elsif order_expression.is_a?(Arel::Nodes::Ordering)
+ AREL_ORDER_CLASSES[order_expression.class] || AREL_ORDER_CLASSES[order_expression.value.class]
end
unless REVERSED_ORDER_DIRECTIONS.has_key?(transformed_order_direction)
diff --git a/lib/gitlab/patch/sidekiq_cron_poller.rb b/lib/gitlab/patch/sidekiq_cron_poller.rb
new file mode 100644
index 00000000000..630c364d455
--- /dev/null
+++ b/lib/gitlab/patch/sidekiq_cron_poller.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+# Patch to address https://github.com/ondrejbartas/sidekiq-cron/issues/361
+# This restores the poll interval to v1.2.0 behavior
+# https://github.com/ondrejbartas/sidekiq-cron/blob/v1.2.0/lib/sidekiq/cron/poller.rb#L36-L38
+# This patch only applies to v1.4.0
+require 'sidekiq/cron/version'
+
+if Gem::Version.new(Sidekiq::Cron::VERSION) != Gem::Version.new('1.4.0')
+ raise 'New version of sidekiq-cron detected, please remove or update this patch'
+end
+
+module Gitlab
+ module Patch
+ module SidekiqCronPoller
+ def poll_interval_average
+ Sidekiq.options[:poll_interval] || Sidekiq::Cron::POLL_INTERVAL
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/quick_actions/issue_actions.rb b/lib/gitlab/quick_actions/issue_actions.rb
index 189627506f3..4883c649a62 100644
--- a/lib/gitlab/quick_actions/issue_actions.rb
+++ b/lib/gitlab/quick_actions/issue_actions.rb
@@ -207,19 +207,22 @@ module Gitlab
desc { _('Add Zoom meeting') }
explanation { _('Adds a Zoom meeting.') }
- params '<Zoom URL>'
+ params do
+ zoom_link_params
+ end
types Issue
condition do
@zoom_service = zoom_link_service
+
@zoom_service.can_add_link?
end
- parse_params do |link|
- @zoom_service.parse_link(link)
+ parse_params do |link_params|
+ @zoom_service.parse_link(link_params)
end
- command :zoom do |link|
- result = @zoom_service.add_link(link)
+ command :zoom do |link, link_text = nil|
+ result = add_zoom_link(link, link_text)
@execution_message[:zoom] = result.message
- @updates.merge!(result.payload) if result.payload
+ merge_updates(result, @updates)
end
desc { _('Remove Zoom meeting') }
@@ -315,12 +318,52 @@ module Gitlab
@updates[:remove_contacts] = contact_emails.split(' ')
end
- private
-
- def zoom_link_service
- ::Issues::ZoomLinkService.new(project: quick_action_target.project, current_user: current_user, params: { issue: quick_action_target })
+ desc { _('Add a timeline event to incident') }
+ explanation { _('Adds a timeline event to incident.') }
+ params '<timeline comment> | <date(YYYY-MM-DD)> <time(HH:MM)>'
+ types Issue
+ condition do
+ quick_action_target.incident? &&
+ current_user.can?(:admin_incident_management_timeline_event, quick_action_target)
+ end
+ parse_params do |event_params|
+ Gitlab::QuickActions::TimelineTextAndDateTimeSeparator.new(event_params).execute
+ end
+ command :timeline do |event_text, date_time|
+ if event_text && date_time
+ timeline_event = timeline_event_create_service(event_text, date_time).execute
+
+ @execution_message[:timeline] =
+ if timeline_event.success?
+ _('Timeline event added successfully.')
+ else
+ _('Something went wrong while adding timeline event.')
+ end
+ end
end
end
+
+ private
+
+ def zoom_link_service
+ ::Issues::ZoomLinkService.new(project: quick_action_target.project, current_user: current_user, params: { issue: quick_action_target })
+ end
+
+ def zoom_link_params
+ '<Zoom URL>'
+ end
+
+ def add_zoom_link(link, _link_text)
+ zoom_link_service.add_link(link)
+ end
+
+ def merge_updates(result, update_hash)
+ update_hash.merge!(result.payload) if result.payload
+ end
+
+ def timeline_event_create_service(event_text, event_date_time)
+ ::IncidentManagement::TimelineEvents::CreateService.new(quick_action_target, current_user, { note: event_text, occurred_at: event_date_time, editable: true })
+ end
end
end
end
diff --git a/lib/gitlab/quick_actions/merge_request_actions.rb b/lib/gitlab/quick_actions/merge_request_actions.rb
index 3cb01db1491..d38b81bff0b 100644
--- a/lib/gitlab/quick_actions/merge_request_actions.rb
+++ b/lib/gitlab/quick_actions/merge_request_actions.rb
@@ -88,33 +88,21 @@ module Gitlab
@execution_message[:rebase] = _('Scheduled a rebase of branch %{branch}.') % { branch: branch }
end
- desc { _('Toggle the Draft status') }
+ desc { _('Set the Draft status') }
explanation do
- noun = quick_action_target.to_ability_name.humanize(capitalize: false)
- if quick_action_target.draft?
- _("Marks this %{noun} as ready.")
- else
- _("Marks this %{noun} as a draft.")
- end % { noun: noun }
+ draft_action_message(_("Marks"))
end
execution_message do
- noun = quick_action_target.to_ability_name.humanize(capitalize: false)
- if quick_action_target.draft?
- _("Marked this %{noun} as ready.")
- else
- _("Marked this %{noun} as a draft.")
- end % { noun: noun }
+ draft_action_message(_("Marked"))
end
types MergeRequest
condition do
quick_action_target.respond_to?(:draft?) &&
- # Allow it to mark as draft on MR creation page or through MR notes
- #
(quick_action_target.new_record? || current_user.can?(:"update_#{quick_action_target.to_ability_name}", quick_action_target))
end
command :draft do
- @updates[:wip_event] = quick_action_target.draft? ? 'ready' : 'draft'
+ @updates[:wip_event] = draft_action_command
end
desc { _('Set the Ready status') }
@@ -317,6 +305,25 @@ module Gitlab
end
end
+ def draft_action_message(verb)
+ noun = quick_action_target.to_ability_name.humanize(capitalize: false)
+ if !quick_action_target.draft?
+ _("%{verb} this %{noun} as a draft.")
+ elsif Feature.disabled?(:draft_quick_action_non_toggle, quick_action_target.project)
+ _("%{verb} this %{noun} as ready.")
+ else
+ _("No change to this %{noun}'s draft status.")
+ end % { verb: verb, noun: noun }
+ end
+
+ def draft_action_command
+ if Feature.disabled?(:draft_quick_action_non_toggle, quick_action_target.project)
+ quick_action_target.draft? ? 'ready' : 'draft'
+ else
+ 'draft'
+ end
+ end
+
def merge_orchestration_service
@merge_orchestration_service ||= ::MergeRequests::MergeOrchestrationService.new(project, current_user)
end
diff --git a/lib/gitlab/quick_actions/timeline_text_and_date_time_separator.rb b/lib/gitlab/quick_actions/timeline_text_and_date_time_separator.rb
new file mode 100644
index 00000000000..e8002656ff5
--- /dev/null
+++ b/lib/gitlab/quick_actions/timeline_text_and_date_time_separator.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module QuickActions
+ class TimelineTextAndDateTimeSeparator
+ DATETIME_REGEX = %r{(\d{2,4}[\-.]\d{1,2}[\-.]\d{1,2} \d{1,2}:\d{2})}.freeze
+ MIXED_DELIMITER = %r{([/.])}.freeze
+ TIME_REGEX = %r{(\d{1,2}:\d{2})}.freeze
+
+ def initialize(timeline_event_arg)
+ @timeline_event_arg = timeline_event_arg
+ @timeline_text = get_text
+ @timeline_date_string = get_raw_date_string
+ end
+
+ def execute
+ return if @timeline_event_arg.blank?
+ return if date_contains_mixed_delimiters?
+ return [@timeline_text, get_current_date_time] unless date_time_present?
+ return unless valid_date?
+
+ [@timeline_text, get_actual_date_time]
+ end
+
+ private
+
+ def get_text
+ @timeline_event_arg.split('|')[0]&.strip
+ end
+
+ def get_raw_date_string
+ @timeline_event_arg.split('|')[1]&.strip
+ end
+
+ def get_current_date_time
+ DateTime.current.strftime("%Y-%m-%d %H:%M:00 UTC")
+ end
+
+ def get_actual_date_time
+ DateTime.parse(@timeline_date_string)
+ end
+
+ def date_time_present?
+ DATETIME_REGEX =~ @timeline_date_string || TIME_REGEX =~ @timeline_date_string
+ end
+
+ def date_contains_mixed_delimiters?
+ MIXED_DELIMITER =~ @timeline_date_string
+ end
+
+ def valid_date?
+ get_actual_date_time
+ rescue Date::Error
+ nil
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/reactive_cache_set_cache.rb b/lib/gitlab/reactive_cache_set_cache.rb
index 7ccbeadfd8a..2de3c07712f 100644
--- a/lib/gitlab/reactive_cache_set_cache.rb
+++ b/lib/gitlab/reactive_cache_set_cache.rb
@@ -15,8 +15,10 @@ module Gitlab
keys = read(key).map { |value| "#{cache_namespace}:#{value}" }
keys << cache_key(key)
- redis.pipelined do
- keys.each_slice(1000) { |subset| redis.unlink(*subset) }
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ redis.pipelined do |pipeline|
+ keys.each_slice(1000) { |subset| pipeline.unlink(*subset) }
+ end
end
end
end
diff --git a/lib/gitlab/redis.rb b/lib/gitlab/redis.rb
new file mode 100644
index 00000000000..8857b544364
--- /dev/null
+++ b/lib/gitlab/redis.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Redis
+ # List all Gitlab::Redis::Wrapper descendants that are backed by an actual
+ # separate redis instance here.
+ #
+ # This will make sure the connection pool is initialized on application boot in
+ # config/initializers/7_redis.rb, instrumented, and used in health- & readiness checks.
+ ALL_CLASSES = [
+ Gitlab::Redis::Cache,
+ Gitlab::Redis::Queues,
+ Gitlab::Redis::RateLimiting,
+ Gitlab::Redis::Sessions,
+ Gitlab::Redis::SharedState,
+ Gitlab::Redis::TraceChunks
+ ].freeze
+ end
+end
diff --git a/lib/gitlab/redis/cache.rb b/lib/gitlab/redis/cache.rb
index 4ab1024d528..043f14630d5 100644
--- a/lib/gitlab/redis/cache.rb
+++ b/lib/gitlab/redis/cache.rb
@@ -12,7 +12,7 @@ module Gitlab
redis: pool,
compress: Gitlab::Utils.to_boolean(ENV.fetch('ENABLE_REDIS_CACHE_COMPRESSION', '1')),
namespace: CACHE_NAMESPACE,
- expires_in: ENV.fetch('GITLAB_RAILS_CACHE_DEFAULT_TTL_SECONDS', 2.weeks).to_i # Cache should not grow forever
+ expires_in: ENV.fetch('GITLAB_RAILS_CACHE_DEFAULT_TTL_SECONDS', 8.hours).to_i # Cache should not grow forever
}
end
end
diff --git a/lib/gitlab/redis/multi_store.rb b/lib/gitlab/redis/multi_store.rb
index cdd2ac6100e..a7c36786d2d 100644
--- a/lib/gitlab/redis/multi_store.rb
+++ b/lib/gitlab/redis/multi_store.rb
@@ -267,7 +267,7 @@ module Gitlab
def same_redis_store?
strong_memoize(:same_redis_store) do
- # <Redis client v4.4.0 for redis:///path_to/redis/redis.socket/5>"
+ # <Redis client v4.7.1 for unix:///path_to/redis/redis.socket/5>"
primary_store.inspect == secondary_store.inspect
end
end
diff --git a/lib/gitlab/repository_hash_cache.rb b/lib/gitlab/repository_hash_cache.rb
index 430f3e8d162..1ecdf506208 100644
--- a/lib/gitlab/repository_hash_cache.rb
+++ b/lib/gitlab/repository_hash_cache.rb
@@ -83,14 +83,14 @@ module Gitlab
full_key = cache_key(key)
with do |redis|
- results = redis.pipelined do
+ results = redis.pipelined do |pipeline|
# Set each hash key to the provided value
hash.each do |h_key, h_value|
- redis.hset(full_key, h_key, h_value)
+ pipeline.hset(full_key, h_key, h_value)
end
# Update the expiry time for this hset
- redis.expire(full_key, expires_in)
+ pipeline.expire(full_key, expires_in)
end
results.all?
diff --git a/lib/gitlab/repository_set_cache.rb b/lib/gitlab/repository_set_cache.rb
index 3061fb96190..33c7d96c45b 100644
--- a/lib/gitlab/repository_set_cache.rb
+++ b/lib/gitlab/repository_set_cache.rb
@@ -21,14 +21,14 @@ module Gitlab
full_key = cache_key(key)
with do |redis|
- redis.multi do
- redis.unlink(full_key)
+ redis.multi do |multi|
+ multi.unlink(full_key)
# Splitting into groups of 1000 prevents us from creating a too-long
# Redis command
- value.each_slice(1000) { |subset| redis.sadd(full_key, subset) }
+ value.each_slice(1000) { |subset| multi.sadd(full_key, subset) }
- redis.expire(full_key, expires_in)
+ multi.expire(full_key, expires_in)
end
end
@@ -39,9 +39,9 @@ module Gitlab
full_key = cache_key(key)
smembers, exists = with do |redis|
- redis.multi do
- redis.smembers(full_key)
- redis.exists(full_key)
+ redis.multi do |multi|
+ multi.smembers(full_key)
+ multi.exists(full_key)
end
end
diff --git a/lib/gitlab/request_forgery_protection.rb b/lib/gitlab/request_forgery_protection.rb
index a84a6ac2d14..258c904290d 100644
--- a/lib/gitlab/request_forgery_protection.rb
+++ b/lib/gitlab/request_forgery_protection.rb
@@ -6,6 +6,7 @@
module Gitlab
module RequestForgeryProtection
+ # rubocop:disable Rails/ApplicationController
class Controller < ActionController::Base
protect_from_forgery with: :exception, prepend: true
@@ -31,5 +32,6 @@ module Gitlab
rescue ActionController::InvalidAuthenticityToken
false
end
+ # rubocop:enable Rails/ApplicationController
end
end
diff --git a/lib/gitlab/seeder.rb b/lib/gitlab/seeder.rb
index 2450ad88bbb..ec514adafc8 100644
--- a/lib/gitlab/seeder.rb
+++ b/lib/gitlab/seeder.rb
@@ -151,48 +151,6 @@ module Gitlab
model.logger = old_loggers[connection_name]
end
end
-
- module Ci
- class DailyBuildGroupReportResult
- DEFAULT_BRANCH = 'master'
- COUNT_OF_DAYS = 5
-
- def initialize(project)
- @project = project
- @last_pipeline = project.last_pipeline
- end
-
- def seed
- COUNT_OF_DAYS.times do |count|
- date = Time.now.utc - count.day
- create_report(date)
- end
- end
-
- private
-
- attr_reader :project, :last_pipeline
-
- def create_report(date)
- last_pipeline.builds.uniq(&:group_name).each do |build|
- ::Ci::DailyBuildGroupReportResult.create(
- project: project,
- last_pipeline: last_pipeline,
- date: date,
- ref_path: last_pipeline.source_ref_path,
- group_name: build.group_name,
- data: {
- 'coverage' => rand(20..99)
- },
- group: project.group,
- default_branch: last_pipeline.default_branch?
- )
- rescue ActiveRecord::RecordNotUnique
- return false
- end
- end
- end
- end
end
end
# :nocov:
diff --git a/lib/gitlab/seeders/ci/daily_build_group_report_result.rb b/lib/gitlab/seeders/ci/daily_build_group_report_result.rb
new file mode 100644
index 00000000000..10ec65f6bf4
--- /dev/null
+++ b/lib/gitlab/seeders/ci/daily_build_group_report_result.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Seeders
+ module Ci
+ class DailyBuildGroupReportResult
+ DEFAULT_BRANCH = 'master'
+ COUNT_OF_DAYS = 5
+
+ def initialize(project)
+ @project = project
+ @last_pipeline = project.last_pipeline
+ end
+
+ def seed
+ COUNT_OF_DAYS.times do |count|
+ date = Time.now.utc - count.day
+ create_report(date)
+ end
+ end
+
+ private
+
+ attr_reader :project, :last_pipeline
+
+ def create_report(date)
+ last_pipeline.builds.uniq(&:group_name).each do |build|
+ ::Ci::DailyBuildGroupReportResult.create(
+ project: project,
+ last_pipeline: last_pipeline,
+ date: date,
+ ref_path: last_pipeline.source_ref_path,
+ group_name: build.group_name,
+ data: {
+ 'coverage' => rand(20..99)
+ },
+ group: project.group,
+ default_branch: last_pipeline.default_branch?
+ )
+ rescue ActiveRecord::RecordNotUnique
+ return false
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/set_cache.rb b/lib/gitlab/set_cache.rb
index 896e7e3f65e..23c23393bc8 100644
--- a/lib/gitlab/set_cache.rb
+++ b/lib/gitlab/set_cache.rb
@@ -33,10 +33,10 @@ module Gitlab
def write(key, value)
with do |redis|
- redis.pipelined do
- redis.sadd(cache_key(key), value)
+ redis.pipelined do |pipeline|
+ pipeline.sadd(cache_key(key), value)
- redis.expire(cache_key(key), expires_in)
+ pipeline.expire(cache_key(key), expires_in)
end
end
@@ -57,9 +57,9 @@ module Gitlab
full_key = cache_key(key)
with do |redis|
- redis.multi do
- redis.sismember(full_key, value)
- redis.exists(full_key)
+ redis.multi do |multi|
+ multi.sismember(full_key, value)
+ multi.exists(full_key)
end
end
end
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index d26e1a34a9f..b167afe589a 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -70,7 +70,9 @@ module Gitlab
link_path = File.join(shell_path, '.gitlab_shell_secret')
if File.exist?(shell_path) && !File.exist?(link_path)
- FileUtils.symlink(secret_file, link_path)
+ # It could happen that link_path is a broken symbolic link.
+ # In that case !File.exist?(link_path) is true, but we still want to overwrite the (broken) symbolic link.
+ FileUtils.ln_sf(secret_file, link_path)
end
end
end
diff --git a/lib/gitlab/sidekiq_daemon/memory_killer.rb b/lib/gitlab/sidekiq_daemon/memory_killer.rb
index ca92fed9c40..24e2eca420e 100644
--- a/lib/gitlab/sidekiq_daemon/memory_killer.rb
+++ b/lib/gitlab/sidekiq_daemon/memory_killer.rb
@@ -41,11 +41,11 @@ module Gitlab
def init_metrics
{
- sidekiq_current_rss: ::Gitlab::Metrics.gauge(:sidekiq_current_rss, 'Current RSS of Sidekiq Worker'),
+ sidekiq_current_rss: ::Gitlab::Metrics.gauge(:sidekiq_current_rss, 'Current RSS of Sidekiq Worker'),
sidekiq_memory_killer_soft_limit_rss: ::Gitlab::Metrics.gauge(:sidekiq_memory_killer_soft_limit_rss, 'Current soft_limit_rss of Sidekiq Worker'),
sidekiq_memory_killer_hard_limit_rss: ::Gitlab::Metrics.gauge(:sidekiq_memory_killer_hard_limit_rss, 'Current hard_limit_rss of Sidekiq Worker'),
- sidekiq_memory_killer_phase: ::Gitlab::Metrics.gauge(:sidekiq_memory_killer_phase, 'Current phase of Sidekiq Worker'),
- sidekiq_memory_killer_running_jobs: ::Gitlab::Metrics.counter(:sidekiq_memory_killer_running_jobs_total, 'Current running jobs when limit was reached')
+ sidekiq_memory_killer_phase: ::Gitlab::Metrics.gauge(:sidekiq_memory_killer_phase, 'Current phase of Sidekiq Worker'),
+ sidekiq_memory_killer_running_jobs: ::Gitlab::Metrics.counter(:sidekiq_memory_killer_running_jobs_total, 'Current running jobs when limit was reached')
}
end
diff --git a/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb b/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb
index 7533770e254..ab126ea4749 100644
--- a/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb
+++ b/lib/gitlab/sidekiq_middleware/duplicate_jobs/duplicate_job.rb
@@ -112,10 +112,12 @@ module Gitlab
end
def delete!
- with_redis do |redis|
- redis.multi do |multi|
- multi.del(idempotency_key, deduplicated_flag_key)
- delete_wal_locations!(multi)
+ Gitlab::Instrumentation::RedisClusterValidator.allow_cross_slot_commands do
+ with_redis do |redis|
+ redis.multi do |multi|
+ multi.del(idempotency_key, deduplicated_flag_key)
+ delete_wal_locations!(multi)
+ end
end
end
end
diff --git a/lib/gitlab/sidekiq_middleware/server_metrics.rb b/lib/gitlab/sidekiq_middleware/server_metrics.rb
index 180cdad916b..3dd5355d3a3 100644
--- a/lib/gitlab/sidekiq_middleware/server_metrics.rb
+++ b/lib/gitlab/sidekiq_middleware/server_metrics.rb
@@ -22,21 +22,21 @@ module Gitlab
def metrics
{
- sidekiq_jobs_cpu_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_cpu_seconds, 'Seconds this Sidekiq job spent on the CPU', {}, SIDEKIQ_LATENCY_BUCKETS),
- sidekiq_jobs_completion_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_completion_seconds, 'Seconds to complete Sidekiq job', {}, SIDEKIQ_JOB_DURATION_BUCKETS),
- sidekiq_jobs_db_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_db_seconds, 'Seconds of database time to run Sidekiq job', {}, SIDEKIQ_LATENCY_BUCKETS),
- sidekiq_jobs_gitaly_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_gitaly_seconds, 'Seconds of Gitaly time to run Sidekiq job', {}, SIDEKIQ_LATENCY_BUCKETS),
- sidekiq_jobs_queue_duration_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_queue_duration_seconds, 'Duration in seconds that a Sidekiq job was queued before being executed', {}, SIDEKIQ_QUEUE_DURATION_BUCKETS),
+ sidekiq_jobs_cpu_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_cpu_seconds, 'Seconds this Sidekiq job spent on the CPU', {}, SIDEKIQ_LATENCY_BUCKETS),
+ sidekiq_jobs_completion_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_completion_seconds, 'Seconds to complete Sidekiq job', {}, SIDEKIQ_JOB_DURATION_BUCKETS),
+ sidekiq_jobs_db_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_db_seconds, 'Seconds of database time to run Sidekiq job', {}, SIDEKIQ_LATENCY_BUCKETS),
+ sidekiq_jobs_gitaly_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_gitaly_seconds, 'Seconds of Gitaly time to run Sidekiq job', {}, SIDEKIQ_LATENCY_BUCKETS),
+ sidekiq_jobs_queue_duration_seconds: ::Gitlab::Metrics.histogram(:sidekiq_jobs_queue_duration_seconds, 'Duration in seconds that a Sidekiq job was queued before being executed', {}, SIDEKIQ_QUEUE_DURATION_BUCKETS),
sidekiq_redis_requests_duration_seconds: ::Gitlab::Metrics.histogram(:sidekiq_redis_requests_duration_seconds, 'Duration in seconds that a Sidekiq job spent requests a Redis server', {}, Gitlab::Instrumentation::Redis::QUERY_TIME_BUCKETS),
sidekiq_elasticsearch_requests_duration_seconds: ::Gitlab::Metrics.histogram(:sidekiq_elasticsearch_requests_duration_seconds, 'Duration in seconds that a Sidekiq job spent in requests to an Elasticsearch server', {}, SIDEKIQ_LATENCY_BUCKETS),
- sidekiq_jobs_failed_total: ::Gitlab::Metrics.counter(:sidekiq_jobs_failed_total, 'Sidekiq jobs failed'),
- sidekiq_jobs_retried_total: ::Gitlab::Metrics.counter(:sidekiq_jobs_retried_total, 'Sidekiq jobs retried'),
- sidekiq_jobs_interrupted_total: ::Gitlab::Metrics.counter(:sidekiq_jobs_interrupted_total, 'Sidekiq jobs interrupted'),
- sidekiq_redis_requests_total: ::Gitlab::Metrics.counter(:sidekiq_redis_requests_total, 'Redis requests during a Sidekiq job execution'),
- sidekiq_elasticsearch_requests_total: ::Gitlab::Metrics.counter(:sidekiq_elasticsearch_requests_total, 'Elasticsearch requests during a Sidekiq job execution'),
- sidekiq_running_jobs: ::Gitlab::Metrics.gauge(:sidekiq_running_jobs, 'Number of Sidekiq jobs running', {}, :all),
- sidekiq_concurrency: ::Gitlab::Metrics.gauge(:sidekiq_concurrency, 'Maximum number of Sidekiq jobs', {}, :all),
- sidekiq_mem_total_bytes: ::Gitlab::Metrics.gauge(:sidekiq_mem_total_bytes, 'Number of bytes allocated for both objects consuming an object slot and objects that required a malloc', {}, :all)
+ sidekiq_jobs_failed_total: ::Gitlab::Metrics.counter(:sidekiq_jobs_failed_total, 'Sidekiq jobs failed'),
+ sidekiq_jobs_retried_total: ::Gitlab::Metrics.counter(:sidekiq_jobs_retried_total, 'Sidekiq jobs retried'),
+ sidekiq_jobs_interrupted_total: ::Gitlab::Metrics.counter(:sidekiq_jobs_interrupted_total, 'Sidekiq jobs interrupted'),
+ sidekiq_redis_requests_total: ::Gitlab::Metrics.counter(:sidekiq_redis_requests_total, 'Redis requests during a Sidekiq job execution'),
+ sidekiq_elasticsearch_requests_total: ::Gitlab::Metrics.counter(:sidekiq_elasticsearch_requests_total, 'Elasticsearch requests during a Sidekiq job execution'),
+ sidekiq_running_jobs: ::Gitlab::Metrics.gauge(:sidekiq_running_jobs, 'Number of Sidekiq jobs running', {}, :all),
+ sidekiq_concurrency: ::Gitlab::Metrics.gauge(:sidekiq_concurrency, 'Maximum number of Sidekiq jobs', {}, :all),
+ sidekiq_mem_total_bytes: ::Gitlab::Metrics.gauge(:sidekiq_mem_total_bytes, 'Number of bytes allocated for both objects consuming an object slot and objects that required a malloc', {}, :all)
}
end
diff --git a/lib/gitlab/sidekiq_versioning.rb b/lib/gitlab/sidekiq_versioning.rb
index 80c0b7650f3..28c9714f82f 100644
--- a/lib/gitlab/sidekiq_versioning.rb
+++ b/lib/gitlab/sidekiq_versioning.rb
@@ -10,11 +10,7 @@ module Gitlab
if queues.any?
Sidekiq.redis do |conn|
- conn.pipelined do
- queues.each do |queue|
- conn.sadd('queues', queue)
- end
- end
+ conn.sadd('queues', queues)
end
end
rescue ::Redis::BaseError, SocketError, Errno::ENOENT, Errno::EADDRNOTAVAIL, Errno::EAFNOSUPPORT, Errno::ECONNRESET, Errno::ECONNREFUSED
diff --git a/lib/gitlab/slash_commands/presenters/base.rb b/lib/gitlab/slash_commands/presenters/base.rb
index d28b5fb509a..55497c5e365 100644
--- a/lib/gitlab/slash_commands/presenters/base.rb
+++ b/lib/gitlab/slash_commands/presenters/base.rb
@@ -87,16 +87,16 @@ module Gitlab
{
attachments: [
{
- title: "#{issue.title} · #{issue.to_reference}",
- title_link: resource_url,
- author_name: author.name,
- author_icon: author.avatar_url(only_path: false),
- fallback: fallback_message,
- pretext: custom_pretext,
- text: text,
- color: color(resource),
- fields: fields,
- mrkdwn_in: fields_with_markdown
+ title: "#{issue.title} · #{issue.to_reference}",
+ title_link: resource_url,
+ author_name: author.name,
+ author_icon: author.avatar_url(only_path: false),
+ fallback: fallback_message,
+ pretext: custom_pretext,
+ text: text,
+ color: color(resource),
+ fields: fields,
+ mrkdwn_in: fields_with_markdown
}
]
}
diff --git a/lib/gitlab/spamcheck/client.rb b/lib/gitlab/spamcheck/client.rb
index 40b01552244..0b9f3baa4de 100644
--- a/lib/gitlab/spamcheck/client.rb
+++ b/lib/gitlab/spamcheck/client.rb
@@ -33,33 +33,50 @@ module Gitlab
@endpoint_url = @endpoint_url.sub(URL_SCHEME_REGEX, '')
end
- def issue_spam?(spam_issue:, user:, context: {})
- issue = build_issue_protobuf(issue: spam_issue, user: user, context: context)
+ def spam?(spammable:, user:, context: {}, extra_features: {})
+ metadata = { 'authorization' => Gitlab::CurrentSettings.spam_check_api_key || '' }
+ protobuf_args = { spammable: spammable, user: user, context: context, extra_features: extra_features }
+
+ pb, grpc_method = build_protobuf(**protobuf_args)
+ response = grpc_method.call(pb, metadata: metadata)
- response = grpc_client.check_for_spam_issue(issue,
- metadata: { 'authorization' =>
- Gitlab::CurrentSettings.spam_check_api_key })
verdict = convert_verdict_to_gitlab_constant(response.verdict)
[verdict, response.extra_attributes.to_h, response.error]
end
private
+ def get_spammable_mappings(spammable)
+ case spammable
+ when Issue
+ [::Spamcheck::Issue, grpc_client.method(:check_for_spam_issue)]
+ when Snippet
+ [::Spamcheck::Snippet, grpc_client.method(:check_for_spam_snippet)]
+ else
+ raise ArgumentError, "Not a spammable type: #{spammable.class.name}"
+ end
+ end
+
def convert_verdict_to_gitlab_constant(verdict)
VERDICT_MAPPING.fetch(::Spamcheck::SpamVerdict::Verdict.resolve(verdict), verdict)
end
- def build_issue_protobuf(issue:, user:, context:)
- issue_pb = ::Spamcheck::Issue.new
- issue_pb.title = issue.spam_title || ''
- issue_pb.description = issue.spam_description || ''
- issue_pb.created_at = convert_to_pb_timestamp(issue.created_at) if issue.created_at
- issue_pb.updated_at = convert_to_pb_timestamp(issue.updated_at) if issue.updated_at
- issue_pb.user_in_project = user.authorized_project?(issue.project)
- issue_pb.project = build_project_protobuf(issue)
- issue_pb.action = ACTION_MAPPING.fetch(context.fetch(:action)) if context.has_key?(:action)
- issue_pb.user = build_user_protobuf(user)
- issue_pb
+ def build_protobuf(spammable:, user:, context:, extra_features:)
+ protobuf_class, grpc_method = get_spammable_mappings(spammable)
+ pb = protobuf_class.new(**extra_features)
+ pb.title = spammable.spam_title || ''
+ pb.description = spammable.spam_description || ''
+ pb.created_at = convert_to_pb_timestamp(spammable.created_at) if spammable.created_at
+ pb.updated_at = convert_to_pb_timestamp(spammable.updated_at) if spammable.updated_at
+ pb.action = ACTION_MAPPING.fetch(context.fetch(:action)) if context.has_key?(:action)
+ pb.user = build_user_protobuf(user)
+
+ unless spammable.project.nil?
+ pb.user_in_project = user.authorized_project?(spammable.project)
+ pb.project = build_project_protobuf(spammable)
+ end
+
+ [pb, grpc_method]
end
def build_user_protobuf(user)
diff --git a/lib/gitlab/subscription_portal.rb b/lib/gitlab/subscription_portal.rb
index 7ef1be6ff44..7494f0584d0 100644
--- a/lib/gitlab/subscription_portal.rb
+++ b/lib/gitlab/subscription_portal.rb
@@ -22,6 +22,10 @@ module Gitlab
"payment_method_validation"
end
+ def self.registration_validation_form_id
+ "cc_registration_validation"
+ end
+
def self.registration_validation_form_url
"#{self.subscriptions_url}/payment_forms/cc_registration_validation"
end
@@ -90,3 +94,4 @@ Gitlab::SubscriptionPortal::PAYMENT_FORM_URL = Gitlab::SubscriptionPortal.paymen
Gitlab::SubscriptionPortal::PAYMENT_VALIDATION_FORM_ID = Gitlab::SubscriptionPortal.payment_validation_form_id.freeze
Gitlab::SubscriptionPortal::RENEWAL_SERVICE_EMAIL = Gitlab::SubscriptionPortal.renewal_service_email.freeze
Gitlab::SubscriptionPortal::REGISTRATION_VALIDATION_FORM_URL = Gitlab::SubscriptionPortal.registration_validation_form_url.freeze
+Gitlab::SubscriptionPortal::REGISTRATION_VALIDATION_FORM_ID = Gitlab::SubscriptionPortal.registration_validation_form_id.freeze
diff --git a/lib/gitlab/template/gitignore_template.rb b/lib/gitlab/template/gitignore_template.rb
index 72a1b7460c2..d8e0ec82410 100644
--- a/lib/gitlab/template/gitignore_template.rb
+++ b/lib/gitlab/template/gitignore_template.rb
@@ -11,7 +11,7 @@ module Gitlab
def categories
{
"Languages" => '',
- "Global" => 'Global'
+ "Global" => 'Global'
}
end
diff --git a/lib/gitlab/tracking.rb b/lib/gitlab/tracking.rb
index 3b46b4c5498..45f836f10d3 100644
--- a/lib/gitlab/tracking.rb
+++ b/lib/gitlab/tracking.rb
@@ -10,6 +10,8 @@ module Gitlab
def event(category, action, label: nil, property: nil, value: nil, context: [], project: nil, user: nil, namespace: nil, **extra) # rubocop:disable Metrics/ParameterLists
contexts = [Tracking::StandardContext.new(project: project, user: user, namespace: namespace, **extra).to_context, *context]
+ action = action.to_s
+
tracker.event(category, action, label: label, property: property, value: value, context: contexts)
rescue StandardError => error
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(error, snowplow_category: category, snowplow_action: action)
diff --git a/lib/gitlab/tree_summary.rb b/lib/gitlab/tree_summary.rb
index 72df8b423df..ba3176ca6e7 100644
--- a/lib/gitlab/tree_summary.rb
+++ b/lib/gitlab/tree_summary.rb
@@ -6,7 +6,7 @@ module Gitlab
include ::MarkupHelper
CACHE_EXPIRE_IN = 1.hour
- MAX_OFFSET = 2**31
+ MAX_OFFSET = 2**31 - 1
attr_reader :commit, :project, :path, :offset, :limit, :user, :resolved_commits
@@ -35,6 +35,8 @@ module Gitlab
# - commit_path: URI of the commit in the web interface
# - commit_title_html: Rendered commit title
def summarize
+ return [] if offset < 0
+
commits_hsh = fetch_last_cached_commits_list
prerender_commit_full_titles!(commits_hsh.values)
diff --git a/lib/gitlab/uploads/migration_helper.rb b/lib/gitlab/uploads/migration_helper.rb
index deab2cd43a6..712512d0e02 100644
--- a/lib/gitlab/uploads/migration_helper.rb
+++ b/lib/gitlab/uploads/migration_helper.rb
@@ -5,27 +5,10 @@ module Gitlab
class MigrationHelper
attr_reader :logger
- CATEGORIES = [%w(AvatarUploader Project :avatar),
- %w(AvatarUploader Group :avatar),
- %w(AvatarUploader User :avatar),
- %w(AttachmentUploader Note :attachment),
- %w(AttachmentUploader Appearance :logo),
- %w(AttachmentUploader Appearance :header_logo),
- %w(FaviconUploader Appearance :favicon),
- %w(FileUploader Project),
- %w(PersonalFileUploader Snippet),
- %w(NamespaceFileUploader Snippet),
- %w(DesignManagement::DesignV432x230Uploader DesignManagement::Action :image_v432x230),
- %w(FileUploader MergeRequest)].freeze
-
def initialize(args, logger)
prepare_variables(args, logger)
end
- def self.categories
- CATEGORIES
- end
-
def migrate_to_remote_storage
@to_store = ObjectStorage::Store::REMOTE
@@ -45,17 +28,14 @@ module Gitlab
end
def prepare_variables(args, logger)
- @mounted_as = args.mounted_as&.gsub(':', '')&.to_sym
- @uploader_class = args.uploader_class.constantize
- @model_class = args.model_class.constantize
+ @mounted_as = args.mounted_as&.gsub(':', '')
+ @uploader_class = args.uploader_class
+ @model_class = args.model_class&.constantize
@logger = logger
end
def enqueue_batch(batch, index)
- job = ObjectStorage::MigrateUploadsWorker.enqueue!(batch,
- @model_class,
- @mounted_as,
- @to_store)
+ job = ObjectStorage::MigrateUploadsWorker.enqueue!(batch, @to_store)
logger.info(message: "[Uploads migration] Enqueued upload migration job", index: index, job_id: job)
rescue ObjectStorage::MigrateUploadsWorker::SanityCheckError => e
# continue for the next batch
@@ -66,10 +46,12 @@ module Gitlab
def uploads(store_type = [nil, ObjectStorage::Store::LOCAL])
Upload.class_eval { include EachBatch } unless Upload < EachBatch
- Upload
- .where(store: store_type,
- uploader: @uploader_class.to_s,
- model_type: @model_class.base_class.sti_name)
+ uploads = Upload.where(store: store_type)
+ uploads = uploads.where(uploader: @uploader_class) if @uploader_class.present?
+ uploads = uploads.where(model_type: @model_class.base_class.sti_name) if @model_class.present?
+ uploads = uploads.where(mount_point: @mounted_as) if @mounted_as.present?
+
+ uploads
end
# rubocop:enable CodeReuse/ActiveRecord
end
diff --git a/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric.rb b/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric.rb
index c0d53b1b21a..67dc1455b23 100644
--- a/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric.rb
+++ b/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric.rb
@@ -20,15 +20,20 @@ module Gitlab
private
def relation
- return super.where(source_type: source_type) if source_type.present? # rubocop: disable CodeReuse/ActiveRecord
-
- super
+ scope = super
+ scope = scope.where(source_type: source_type) if source_type.present?
+ scope = scope.where(status: status) if status.present?
+ scope
end
def source_type
options[:source_type].to_s
end
+ def status
+ options[:status]
+ end
+
def allowed_source_types
BulkImports::Entity.source_types.keys.map(&:to_s)
end
diff --git a/lib/gitlab/usage/metrics/instrumentations/count_user_auth_metric.rb b/lib/gitlab/usage/metrics/instrumentations/count_user_auth_metric.rb
new file mode 100644
index 00000000000..1de93ce6dfa
--- /dev/null
+++ b/lib/gitlab/usage/metrics/instrumentations/count_user_auth_metric.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Usage
+ module Metrics
+ module Instrumentations
+ class CountUserAuthMetric < DatabaseMetric
+ operation :distinct_count, column: :user_id
+
+ relation do
+ AuthenticationEvent.success
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb b/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb
index a25bad2436b..26d963e2407 100644
--- a/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb
+++ b/lib/gitlab/usage/metrics/instrumentations/redis_metric.rb
@@ -11,37 +11,49 @@ module Gitlab
# instrumentation_class: RedisMetric
# options:
# event: pushes
- # counter_class: SourceCodeCounter
+ # prefix: source_code
#
class RedisMetric < BaseMetric
+ include Gitlab::UsageDataCounters::RedisCounter
+
+ USAGE_PREFIX = "USAGE_"
+
def initialize(time_frame:, options: {})
super
raise ArgumentError, "'event' option is required" unless metric_event.present?
- raise ArgumentError, "'counter class' option is required" unless counter_class.present?
+ raise ArgumentError, "'prefix' option is required" unless prefix.present?
end
def metric_event
options[:event]
end
- def counter_class_name
- options[:counter_class]
+ def prefix
+ options[:prefix]
end
- def counter_class
- "Gitlab::UsageDataCounters::#{counter_class_name}".constantize
+ def include_usage_prefix?
+ options.fetch(:include_usage_prefix, true)
end
def value
redis_usage_data do
- counter_class.read(metric_event)
+ total_count(redis_key)
end
end
def suggested_name
Gitlab::Usage::Metrics::NameSuggestion.for(:redis)
end
+
+ private
+
+ def redis_key
+ key = "#{prefix}_#{metric_event}".upcase
+ key.prepend(USAGE_PREFIX) if include_usage_prefix?
+ key
+ end
end
end
end
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index 6f36a09fe48..e2232dc5e2a 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -137,7 +137,7 @@ module Gitlab
projects_with_error_tracking_enabled: count(::ErrorTracking::ProjectErrorTrackingSetting.where(enabled: true)),
projects_with_alerts_created: distinct_count(::AlertManagement::Alert, :project_id),
projects_with_enabled_alert_integrations: distinct_count(::AlertManagement::HttpIntegration.active, :project_id),
- projects_with_terraform_reports: distinct_count(::Ci::JobArtifact.terraform_reports, :project_id),
+ projects_with_terraform_reports: distinct_count(::Ci::JobArtifact.of_report_type(:terraform), :project_id),
projects_with_terraform_states: distinct_count(::Terraform::State, :project_id),
protected_branches: count(ProtectedBranch),
protected_branches_except_default: count(ProtectedBranch.where.not(name: ['main', 'master', Gitlab::CurrentSettings.default_branch_name])),
@@ -146,7 +146,7 @@ module Gitlab
personal_snippets: count(PersonalSnippet),
project_snippets: count(ProjectSnippet),
suggestions: count(Suggestion),
- terraform_reports: count(::Ci::JobArtifact.terraform_reports),
+ terraform_reports: count(::Ci::JobArtifact.of_report_type(:terraform)),
terraform_states: count(::Terraform::State),
todos: count(Todo),
uploads: count(Upload),
@@ -268,7 +268,7 @@ module Gitlab
# @return [Array<#totals>] An array of objects that respond to `#totals`
def usage_data_counters
- Gitlab::UsageDataCounters.counters
+ Gitlab::UsageDataCounters.unmigrated_counters
end
def components_usage_data
diff --git a/lib/gitlab/usage_data_counters.rb b/lib/gitlab/usage_data_counters.rb
index 224897ed758..eae1c593a8f 100644
--- a/lib/gitlab/usage_data_counters.rb
+++ b/lib/gitlab/usage_data_counters.rb
@@ -3,29 +3,38 @@
module Gitlab
module UsageDataCounters
COUNTERS = [
- PackageEventCounter,
WikiPageCounter,
- WebIdeCounter,
NoteCounter,
SnippetCounter,
SearchCounter,
CycleAnalyticsCounter,
ProductivityAnalyticsCounter,
SourceCodeCounter,
+ KubernetesAgentCounter,
+ MergeRequestWidgetExtensionCounter
+ ].freeze
+
+ COUNTERS_MIGRATED_TO_INSTRUMENTATION_CLASSES = [
+ PackageEventCounter,
MergeRequestCounter,
DesignsCounter,
- KubernetesAgentCounter,
DiffsCounter,
ServiceUsageDataCounter,
- MergeRequestWidgetExtensionCounter
+ WebIdeCounter
].freeze
UsageDataCounterError = Class.new(StandardError)
UnknownEvent = Class.new(UsageDataCounterError)
class << self
+ def unmigrated_counters
+ # we are using the #counters method instead of the COUNTERS const
+ # to make sure it's working correctly for `ee` version of UsageDataCounters
+ counters - self::COUNTERS_MIGRATED_TO_INSTRUMENTATION_CLASSES
+ end
+
def counters
- self::COUNTERS
+ self::COUNTERS + self::COUNTERS_MIGRATED_TO_INSTRUMENTATION_CLASSES
end
def count(event_name)
diff --git a/lib/gitlab/usage_data_counters/base_counter.rb b/lib/gitlab/usage_data_counters/base_counter.rb
index 4ab310a2519..5d2ab5eaf74 100644
--- a/lib/gitlab/usage_data_counters/base_counter.rb
+++ b/lib/gitlab/usage_data_counters/base_counter.rb
@@ -10,7 +10,9 @@ module Gitlab::UsageDataCounters
def redis_key(event)
require_known_event(event)
- "USAGE_#{prefix}_#{event}".upcase
+ usage_prefix = Gitlab::Usage::Metrics::Instrumentations::RedisMetric::USAGE_PREFIX
+
+ "#{usage_prefix}#{prefix}_#{event}".upcase
end
def count(event)
diff --git a/lib/gitlab/usage_data_counters/hll_redis_counter.rb b/lib/gitlab/usage_data_counters/hll_redis_counter.rb
index a5db8ba4dcc..f0cb9bcbe94 100644
--- a/lib/gitlab/usage_data_counters/hll_redis_counter.rb
+++ b/lib/gitlab/usage_data_counters/hll_redis_counter.rb
@@ -20,11 +20,7 @@ module Gitlab
CATEGORIES_FOR_TOTALS = %w[
analytics
- code_review
compliance
- deploy_token_packages
- ecosystem
- epic_boards_usage
epics_usage
error_tracking
ide_edit
@@ -32,11 +28,13 @@ module Gitlab
issues_edit
pipeline_authoring
quickactions
- user_packages
].freeze
CATEGORIES_COLLECTED_FROM_METRICS_DEFINITIONS = %w[
ci_users
+ deploy_token_packages
+ code_review
+ ecosystem
error_tracking
ide_edit
importer
@@ -49,6 +47,7 @@ module Gitlab
source_code
terraform
testing
+ user_packages
work_items
].freeze
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 316d9bb3dc1..dda72f7fa3b 100644
--- a/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb
+++ b/lib/gitlab/usage_data_counters/issue_activity_unique_counter.rb
@@ -36,95 +36,118 @@ module Gitlab
ISSUE_COMMENT_REMOVED = 'g_project_management_issue_comment_removed'
class << self
- def track_issue_created_action(author:)
+ def track_issue_created_action(author:, project:)
+ track_snowplow_action(ISSUE_CREATED, author, project)
track_unique_action(ISSUE_CREATED, author)
end
- def track_issue_title_changed_action(author:)
+ def track_issue_title_changed_action(author:, project:)
+ track_snowplow_action(ISSUE_TITLE_CHANGED, author, project)
track_unique_action(ISSUE_TITLE_CHANGED, author)
end
- def track_issue_description_changed_action(author:)
+ def track_issue_description_changed_action(author:, project:)
+ track_snowplow_action(ISSUE_DESCRIPTION_CHANGED, author, project)
track_unique_action(ISSUE_DESCRIPTION_CHANGED, author)
end
- def track_issue_assignee_changed_action(author:)
+ def track_issue_assignee_changed_action(author:, project:)
+ track_snowplow_action(ISSUE_ASSIGNEE_CHANGED, author, project)
track_unique_action(ISSUE_ASSIGNEE_CHANGED, author)
end
- def track_issue_made_confidential_action(author:)
+ def track_issue_made_confidential_action(author:, project:)
+ track_snowplow_action(ISSUE_MADE_CONFIDENTIAL, author, project)
track_unique_action(ISSUE_MADE_CONFIDENTIAL, author)
end
- def track_issue_made_visible_action(author:)
+ def track_issue_made_visible_action(author:, project:)
+ track_snowplow_action(ISSUE_MADE_VISIBLE, author, project)
track_unique_action(ISSUE_MADE_VISIBLE, author)
end
- def track_issue_closed_action(author:)
+ def track_issue_closed_action(author:, project:)
+ track_snowplow_action(ISSUE_CLOSED, author, project)
track_unique_action(ISSUE_CLOSED, author)
end
- def track_issue_reopened_action(author:)
+ def track_issue_reopened_action(author:, project:)
+ track_snowplow_action(ISSUE_REOPENED, author, project)
track_unique_action(ISSUE_REOPENED, author)
end
- def track_issue_label_changed_action(author:)
+ def track_issue_label_changed_action(author:, project:)
+ track_snowplow_action(ISSUE_LABEL_CHANGED, author, project)
track_unique_action(ISSUE_LABEL_CHANGED, author)
end
- def track_issue_milestone_changed_action(author:)
+ def track_issue_milestone_changed_action(author:, project:)
+ track_snowplow_action(ISSUE_MILESTONE_CHANGED, author, project)
track_unique_action(ISSUE_MILESTONE_CHANGED, author)
end
- def track_issue_cross_referenced_action(author:)
+ def track_issue_cross_referenced_action(author:, project:)
+ track_snowplow_action(ISSUE_CROSS_REFERENCED, author, project)
track_unique_action(ISSUE_CROSS_REFERENCED, author)
end
- def track_issue_moved_action(author:)
+ def track_issue_moved_action(author:, project:)
+ track_snowplow_action(ISSUE_MOVED, author, project)
track_unique_action(ISSUE_MOVED, author)
end
- def track_issue_related_action(author:)
+ def track_issue_related_action(author:, project:)
+ track_snowplow_action(ISSUE_RELATED, author, project)
track_unique_action(ISSUE_RELATED, author)
end
- def track_issue_unrelated_action(author:)
+ def track_issue_unrelated_action(author:, project:)
+ track_snowplow_action(ISSUE_UNRELATED, author, project)
track_unique_action(ISSUE_UNRELATED, author)
end
- def track_issue_marked_as_duplicate_action(author:)
+ def track_issue_marked_as_duplicate_action(author:, project:)
+ track_snowplow_action(ISSUE_MARKED_AS_DUPLICATE, author, project)
track_unique_action(ISSUE_MARKED_AS_DUPLICATE, author)
end
- def track_issue_locked_action(author:)
+ def track_issue_locked_action(author:, project:)
+ track_snowplow_action(ISSUE_LOCKED, author, project)
track_unique_action(ISSUE_LOCKED, author)
end
- def track_issue_unlocked_action(author:)
+ def track_issue_unlocked_action(author:, project:)
+ track_snowplow_action(ISSUE_UNLOCKED, author, project)
track_unique_action(ISSUE_UNLOCKED, author)
end
- def track_issue_designs_added_action(author:)
+ def track_issue_designs_added_action(author:, project:)
+ track_snowplow_action(ISSUE_DESIGNS_ADDED, author, project)
track_unique_action(ISSUE_DESIGNS_ADDED, author)
end
- def track_issue_designs_modified_action(author:)
+ def track_issue_designs_modified_action(author:, project:)
+ track_snowplow_action(ISSUE_DESIGNS_MODIFIED, author, project)
track_unique_action(ISSUE_DESIGNS_MODIFIED, author)
end
- def track_issue_designs_removed_action(author:)
+ def track_issue_designs_removed_action(author:, project:)
+ track_snowplow_action(ISSUE_DESIGNS_REMOVED, author, project)
track_unique_action(ISSUE_DESIGNS_REMOVED, author)
end
- def track_issue_due_date_changed_action(author:)
+ def track_issue_due_date_changed_action(author:, project:)
+ track_snowplow_action(ISSUE_DUE_DATE_CHANGED, author, project)
track_unique_action(ISSUE_DUE_DATE_CHANGED, author)
end
- def track_issue_time_estimate_changed_action(author:)
+ def track_issue_time_estimate_changed_action(author:, project:)
+ track_snowplow_action(ISSUE_TIME_ESTIMATE_CHANGED, author, project)
track_unique_action(ISSUE_TIME_ESTIMATE_CHANGED, author)
end
- def track_issue_time_spent_changed_action(author:)
+ def track_issue_time_spent_changed_action(author:, project:)
+ track_snowplow_action(ISSUE_TIME_SPENT_CHANGED, author, project)
track_unique_action(ISSUE_TIME_SPENT_CHANGED, author)
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 a8f1bab1f20..10e36a75a3a 100644
--- a/lib/gitlab/usage_data_counters/known_events/ci_templates.yml
+++ b/lib/gitlab/usage_data_counters/known_events/ci_templates.yml
@@ -139,6 +139,10 @@
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
+- name: p_ci_templates_security_container_scanning_latest
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
- name: p_ci_templates_security_api_fuzzing
category: ci_templates
redis_slot: ci_templates
@@ -231,6 +235,10 @@
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
+- name: p_ci_templates_katalon
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
- name: p_ci_templates_mono
category: ci_templates
redis_slot: ci_templates
@@ -319,6 +327,10 @@
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
+- name: p_ci_templates_jobs_license_scanning_latest
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
- name: p_ci_templates_jobs_deploy
category: ci_templates
redis_slot: ci_templates
@@ -331,6 +343,10 @@
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
+- name: p_ci_templates_jobs_dependency_scanning_latest
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
- name: p_ci_templates_jobs_test
category: ci_templates
redis_slot: ci_templates
@@ -523,6 +539,10 @@
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
+- name: p_ci_templates_implicit_jobs_license_scanning_latest
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
- name: p_ci_templates_implicit_jobs_deploy
category: ci_templates
redis_slot: ci_templates
@@ -535,6 +555,10 @@
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
+- name: p_ci_templates_implicit_jobs_dependency_scanning_latest
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
- name: p_ci_templates_implicit_jobs_test
category: ci_templates
redis_slot: ci_templates
@@ -635,6 +659,10 @@
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
+- name: p_ci_templates_implicit_security_container_scanning_latest
+ category: ci_templates
+ redis_slot: ci_templates
+ aggregation: weekly
- name: p_ci_templates_implicit_security_api_fuzzing
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 c21b99ba834..0bd809f8aa5 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
@@ -1,9 +1,29 @@
---
-- name: i_code_review_mr_diffs
+- name: i_code_review_create_note_in_ipynb_diff
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+- name: i_code_review_create_note_in_ipynb_diff_mr
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+- name: i_code_review_create_note_in_ipynb_diff_commit
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+- name: i_code_review_user_create_note_in_ipynb_diff
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+- name: i_code_review_user_create_note_in_ipynb_diff_mr
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+- name: i_code_review_user_create_note_in_ipynb_diff_commit
redis_slot: code_review
category: code_review
aggregation: weekly
-- name: i_code_review_mr_with_invalid_approvers
+- name: i_code_review_mr_diffs
redis_slot: code_review
category: code_review
aggregation: weekly
@@ -135,12 +155,10 @@
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_jetbrains_api_request
- name: i_code_review_user_gitlab_cli_api_request
redis_slot: code_review
category: code_review
aggregation: weekly
- feature_flag: usage_data_i_code_review_user_gitlab_cli_api_request
- name: i_code_review_user_create_mr_from_issue
redis_slot: code_review
category: code_review
@@ -177,30 +195,6 @@
redis_slot: code_review
category: code_review
aggregation: weekly
-- name: i_code_review_create_note_in_ipynb_diff
- redis_slot: code_review
- category: code_review
- aggregation: weekly
-- name: i_code_review_user_create_note_in_ipynb_diff
- redis_slot: code_review
- category: code_review
- aggregation: weekly
-- name: i_code_review_create_note_in_ipynb_diff_mr
- redis_slot: code_review
- category: code_review
- aggregation: weekly
-- name: i_code_review_user_create_note_in_ipynb_diff_mr
- redis_slot: code_review
- category: code_review
- aggregation: weekly
-- name: i_code_review_create_note_in_ipynb_diff_commit
- redis_slot: code_review
- category: code_review
- aggregation: weekly
-- name: i_code_review_user_create_note_in_ipynb_diff_commit
- redis_slot: code_review
- category: code_review
- aggregation: weekly
# Diff settings events
- name: i_code_review_click_diff_view_setting
redis_slot: code_review
@@ -400,53 +394,36 @@
redis_slot: code_review
category: code_review
aggregation: weekly
-## Metrics
-- name: i_code_review_merge_request_widget_metrics_view
- redis_slot: code_review
- category: code_review
- aggregation: weekly
-- name: i_code_review_merge_request_widget_metrics_full_report_clicked
- redis_slot: code_review
- category: code_review
- aggregation: weekly
-- name: i_code_review_merge_request_widget_metrics_expand
- redis_slot: code_review
- category: code_review
- aggregation: weekly
-- name: i_code_review_merge_request_widget_metrics_expand_success
- redis_slot: code_review
- category: code_review
- aggregation: weekly
-- name: i_code_review_merge_request_widget_metrics_expand_warning
+- name: i_code_review_submit_review_approve
redis_slot: code_review
category: code_review
aggregation: weekly
-- name: i_code_review_merge_request_widget_metrics_expand_failed
+- name: i_code_review_submit_review_comment
redis_slot: code_review
category: code_review
aggregation: weekly
-## Status Checks
-- name: i_code_review_merge_request_widget_status_checks_view
+## License Compliance
+- name: i_code_review_merge_request_widget_license_compliance_view
redis_slot: code_review
category: code_review
aggregation: weekly
-- name: i_code_review_merge_request_widget_status_checks_full_report_clicked
+- name: i_code_review_merge_request_widget_license_compliance_full_report_clicked
redis_slot: code_review
category: code_review
aggregation: weekly
-- name: i_code_review_merge_request_widget_status_checks_expand
+- name: i_code_review_merge_request_widget_license_compliance_expand
redis_slot: code_review
category: code_review
aggregation: weekly
-- name: i_code_review_merge_request_widget_status_checks_expand_success
+- name: i_code_review_merge_request_widget_license_compliance_expand_success
redis_slot: code_review
category: code_review
aggregation: weekly
-- name: i_code_review_merge_request_widget_status_checks_expand_warning
+- name: i_code_review_merge_request_widget_license_compliance_expand_warning
redis_slot: code_review
category: code_review
aggregation: weekly
-- name: i_code_review_merge_request_widget_status_checks_expand_failed
+- name: i_code_review_merge_request_widget_license_compliance_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 6c4754ae19f..29b231f88f8 100644
--- a/lib/gitlab/usage_data_counters/known_events/common.yml
+++ b/lib/gitlab/usage_data_counters/known_events/common.yml
@@ -146,6 +146,11 @@
category: testing
redis_slot: testing
aggregation: weekly
+- name: i_testing_test_report_uploaded
+ category: testing
+ redis_slot: testing
+ aggregation: weekly
+ feature_flag: usage_data_ci_i_testing_test_report_uploaded
# Project Management group
- name: g_project_management_issue_title_changed
category: issues_edit
@@ -332,11 +337,6 @@
redis_slot: testing
category: testing
aggregation: weekly
-# Container Security - Network Policies
-- name: clusters_using_network_policies_ui
- redis_slot: network_policies
- category: network_policies
- aggregation: weekly
# Geo group
- name: g_geo_proxied_requests
category: geo
@@ -352,3 +352,8 @@
category: manage
aggregation: weekly
expiry: 42
+# Environments page
+- name: users_visiting_environments_pages
+ category: environments
+ redis_slot: users
+ aggregation: weekly
diff --git a/lib/gitlab/usage_data_counters/known_events/ecosystem.yml b/lib/gitlab/usage_data_counters/known_events/ecosystem.yml
index f594c6a1b7c..7f7c9166086 100644
--- a/lib/gitlab/usage_data_counters/known_events/ecosystem.yml
+++ b/lib/gitlab/usage_data_counters/known_events/ecosystem.yml
@@ -8,14 +8,6 @@
category: ecosystem
redis_slot: ecosystem
aggregation: weekly
-- name: i_ecosystem_jira_service_list_issues
- category: ecosystem
- redis_slot: ecosystem
- aggregation: weekly
-- name: i_ecosystem_jira_service_create_issue
- category: ecosystem
- redis_slot: ecosystem
- aggregation: weekly
- name: i_ecosystem_slack_service_issue_notification
category: ecosystem
redis_slot: ecosystem
diff --git a/lib/gitlab/usage_data_counters/known_events/epic_board_events.yml b/lib/gitlab/usage_data_counters/known_events/epic_board_events.yml
deleted file mode 100644
index 3879c561cc4..00000000000
--- a/lib/gitlab/usage_data_counters/known_events/epic_board_events.yml
+++ /dev/null
@@ -1,19 +0,0 @@
-# Epic board events
-#
-# We are using the same slot of issue events 'project_management' for
-# epic events to allow data aggregation.
-# More information in: https://gitlab.com/gitlab-org/gitlab/-/issues/322405
-- name: g_project_management_users_creating_epic_boards
- category: epic_boards_usage
- redis_slot: project_management
- aggregation: daily
-
-- name: g_project_management_users_viewing_epic_boards
- category: epic_boards_usage
- redis_slot: project_management
- aggregation: daily
-
-- name: g_project_management_users_updating_epic_board_names
- category: epic_boards_usage
- redis_slot: project_management
- aggregation: daily
diff --git a/lib/gitlab/usage_data_counters/known_events/kubernetes_agent.yml b/lib/gitlab/usage_data_counters/known_events/kubernetes_agent.yml
index e1de74a3d07..966e6c584c7 100644
--- a/lib/gitlab/usage_data_counters/known_events/kubernetes_agent.yml
+++ b/lib/gitlab/usage_data_counters/known_events/kubernetes_agent.yml
@@ -2,4 +2,3 @@
category: kubernetes_agent
redis_slot: agent
aggregation: weekly
- feature_flag: track_agent_users_using_ci_tunnel
diff --git a/lib/gitlab/usage_data_counters/known_events/quickactions.yml b/lib/gitlab/usage_data_counters/known_events/quickactions.yml
index f980503b4bf..58a0c0695af 100644
--- a/lib/gitlab/usage_data_counters/known_events/quickactions.yml
+++ b/lib/gitlab/usage_data_counters/known_events/quickactions.yml
@@ -127,6 +127,10 @@
category: quickactions
redis_slot: quickactions
aggregation: weekly
+- name: i_quickactions_timeline
+ category: quickactions
+ redis_slot: quickactions
+ aggregation: weekly
- name: i_quickactions_page
category: quickactions
redis_slot: quickactions
@@ -303,11 +307,3 @@
category: quickactions
redis_slot: quickactions
aggregation: weekly
-- name: i_quickactions_attention
- category: quickactions
- redis_slot: quickactions
- aggregation: weekly
-- name: i_quickactions_remove_attention
- category: quickactions
- redis_slot: quickactions
- aggregation: weekly
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 fbb03a31a6f..93137b762ec 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
@@ -49,6 +49,8 @@ module Gitlab
MR_LOAD_CONFLICT_UI_ACTION = 'i_code_review_user_load_conflict_ui'
MR_RESOLVE_CONFLICT_ACTION = 'i_code_review_user_resolve_conflict'
MR_RESOLVE_THREAD_IN_ISSUE_ACTION = 'i_code_review_user_resolve_thread_in_issue'
+ MR_SUBMIT_REVIEW_APPROVE = 'i_code_review_submit_review_approve'
+ MR_SUBMIT_REVIEW_COMMENT = 'i_code_review_submit_review_comment'
class << self
def track_mr_diffs_action(merge_request:)
@@ -230,6 +232,14 @@ module Gitlab
track_unique_action_by_user(MR_RESOLVE_THREAD_IN_ISSUE_ACTION, user)
end
+ def track_submit_review_approve(user:)
+ track_unique_action_by_user(MR_SUBMIT_REVIEW_APPROVE, user)
+ end
+
+ def track_submit_review_comment(user:)
+ track_unique_action_by_user(MR_SUBMIT_REVIEW_COMMENT, user)
+ end
+
private
def track_unique_action_by_merge_request(action, merge_request)
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 dafc36ab7ce..f88bbc41c70 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,7 @@ 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 status_checks terraform test_summary metrics].freeze
+ WIDGETS = %w[accessibility code_quality license_compliance status_checks terraform test_summary metrics].freeze
class << self
private
diff --git a/lib/gitlab/utils/deep_size.rb b/lib/gitlab/utils/deep_size.rb
index e185786e638..20f2d699e2b 100644
--- a/lib/gitlab/utils/deep_size.rb
+++ b/lib/gitlab/utils/deep_size.rb
@@ -25,10 +25,6 @@ module Gitlab
!too_big? && !too_deep?
end
- def self.human_default_max_size
- ActiveSupport::NumberHelper.number_to_human_size(DEFAULT_MAX_SIZE)
- end
-
private
def evaluate
diff --git a/lib/gitlab/utils/execution_tracker.rb b/lib/gitlab/utils/execution_tracker.rb
new file mode 100644
index 00000000000..6d48658853c
--- /dev/null
+++ b/lib/gitlab/utils/execution_tracker.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Utils
+ class ExecutionTracker
+ MAX_RUNTIME = 30.seconds
+
+ ExecutionTimeOutError = Class.new(StandardError)
+
+ delegate :monotonic_time, to: :'Gitlab::Metrics::System'
+
+ def initialize
+ @start_time = monotonic_time
+ end
+
+ def over_limit?
+ monotonic_time - start_time >= MAX_RUNTIME
+ end
+
+ private
+
+ attr_reader :start_time
+ end
+ end
+end
diff --git a/lib/gitlab/view/presenter/base.rb b/lib/gitlab/view/presenter/base.rb
index a2d217fb42f..2a57ca9ae02 100644
--- a/lib/gitlab/view/presenter/base.rb
+++ b/lib/gitlab/view/presenter/base.rb
@@ -46,6 +46,13 @@ module Gitlab
url_builder.build(__subject__, only_path: true)
end
+ def path_with_line_numbers(path, start_line, end_line)
+ path.tap do |complete_path|
+ complete_path << "#L#{start_line}"
+ complete_path << "-#{end_line}" if end_line && end_line != start_line
+ end
+ end
+
class_methods do
def presenter?
true
diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb
index 049e3befe64..7360585df43 100644
--- a/lib/gitlab/visibility_level.rb
+++ b/lib/gitlab/visibility_level.rb
@@ -47,17 +47,17 @@ module Gitlab
def options
{
- s_('VisibilityLevel|Private') => PRIVATE,
+ s_('VisibilityLevel|Private') => PRIVATE,
s_('VisibilityLevel|Internal') => INTERNAL,
- s_('VisibilityLevel|Public') => PUBLIC
+ s_('VisibilityLevel|Public') => PUBLIC
}
end
def string_options
{
- 'private' => PRIVATE,
+ 'private' => PRIVATE,
'internal' => INTERNAL,
- 'public' => PUBLIC
+ 'public' => PUBLIC
}
end
diff --git a/lib/gitlab/web_hooks/recursion_detection.rb b/lib/gitlab/web_hooks/recursion_detection.rb
index 1b5350d4a4e..031d9ec6ec4 100644
--- a/lib/gitlab/web_hooks/recursion_detection.rb
+++ b/lib/gitlab/web_hooks/recursion_detection.rb
@@ -40,9 +40,9 @@ module Gitlab
cache_key = cache_key_for_hook(hook)
::Gitlab::Redis::SharedState.with do |redis|
- redis.multi do
- redis.sadd(cache_key, hook.id)
- redis.expire(cache_key, TOUCH_CACHE_TTL)
+ redis.multi do |multi|
+ multi.sadd(cache_key, hook.id)
+ multi.expire(cache_key, TOUCH_CACHE_TTL)
end
end
end
diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb
index e81670ce89a..906439d5e71 100644
--- a/lib/gitlab/workhorse.rb
+++ b/lib/gitlab/workhorse.rb
@@ -12,7 +12,7 @@ module Gitlab
VERSION_FILE = 'GITLAB_WORKHORSE_VERSION'
INTERNAL_API_CONTENT_TYPE = 'application/vnd.gitlab-workhorse+json'
INTERNAL_API_REQUEST_HEADER = 'Gitlab-Workhorse-Api-Request'
- NOTIFICATION_CHANNEL = 'workhorse:notifications'
+ NOTIFICATION_PREFIX = 'workhorse:notifications:'
ALLOWED_GIT_HTTP_ACTIONS = %w[git_receive_pack git_upload_pack info_refs].freeze
DETECT_HEADER = 'Gitlab-Workhorse-Detect-Content-Type'
ARCHIVE_FORMATS = %w(zip tar.gz tar.bz2 tar).freeze
@@ -217,7 +217,8 @@ module Gitlab
Gitlab::Redis::SharedState.with do |redis|
result = redis.set(key, value, ex: expire, nx: !overwrite)
if result
- redis.publish(NOTIFICATION_CHANNEL, "#{key}=#{value}")
+ redis.publish(NOTIFICATION_PREFIX + key, value)
+
value
else
redis.get(key)
diff --git a/lib/gitlab_edition.rb b/lib/gitlab_edition.rb
index 02006148a34..5e3ed35ace4 100644
--- a/lib/gitlab_edition.rb
+++ b/lib/gitlab_edition.rb
@@ -7,6 +7,21 @@ module GitlabEdition
Pathname.new(File.expand_path('..', __dir__))
end
+ def self.path_glob(path)
+ "#{root}/#{extension_path_prefixes}#{path}"
+ end
+
+ def self.extension_path_prefixes
+ path_prefixes = extensions
+ return '' if path_prefixes.empty?
+
+ path_prefixes.map! { "#{_1}/" }
+ path_prefixes.unshift ''
+
+ # For example `{,ee/,jh/}`
+ "{#{path_prefixes.join(',')}}"
+ end
+
def self.extensions
if jh?
%w[ee jh]
diff --git a/lib/google_api/cloud_platform/client.rb b/lib/google_api/cloud_platform/client.rb
index 39cf994ca3f..38a1a968aec 100644
--- a/lib/google_api/cloud_platform/client.rb
+++ b/lib/google_api/cloud_platform/client.rb
@@ -22,7 +22,7 @@ module GoogleApi
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring"
].freeze
- ROLES_LIST = %w[roles/iam.serviceAccountUser roles/artifactregistry.admin roles/cloudbuild.builds.builder roles/run.admin roles/storage.admin roles/cloudsql.admin roles/browser].freeze
+ ROLES_LIST = %w[roles/iam.serviceAccountUser roles/artifactregistry.admin roles/cloudbuild.builds.builder roles/run.admin roles/storage.admin roles/cloudsql.client roles/browser].freeze
REVOKE_URL = 'https://oauth2.googleapis.com/revoke'
class << self
diff --git a/lib/learn_gitlab/onboarding.rb b/lib/learn_gitlab/onboarding.rb
deleted file mode 100644
index 54af01a21fe..00000000000
--- a/lib/learn_gitlab/onboarding.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-# frozen_string_literal: true
-
-module LearnGitlab
- class Onboarding
- include Gitlab::Utils::StrongMemoize
- include Gitlab::Experiment::Dsl
-
- ACTION_ISSUE_IDS = {
- pipeline_created: 7,
- trial_started: 2,
- required_mr_approvals_enabled: 11,
- code_owners_enabled: 10
- }.freeze
-
- ACTION_PATHS = [
- :issue_created,
- :git_write,
- :merge_request_created,
- :user_added
- ].freeze
-
- def initialize(namespace, current_user = nil)
- @namespace = namespace
- @current_user = current_user
- end
-
- def completed_percentage
- return 0 unless onboarding_progress
-
- attributes = onboarding_progress.attributes.symbolize_keys
-
- total_actions = action_columns.count
- completed_actions = action_columns.count { |column| attributes[column].present? }
-
- (completed_actions.to_f / total_actions.to_f * 100).round
- end
-
- private
-
- def onboarding_progress
- strong_memoize(:onboarding_progress) do
- OnboardingProgress.find_by(namespace: namespace) # rubocop: disable CodeReuse/ActiveRecord
- end
- end
-
- def action_columns
- strong_memoize(:action_columns) do
- tracked_actions.map { |action_key| OnboardingProgress.column_name(action_key) }
- end
- end
-
- def tracked_actions
- ACTION_ISSUE_IDS.keys + ACTION_PATHS + deploy_section_tracked_actions
- end
-
- def deploy_section_tracked_actions
- experiment(:security_actions_continuous_onboarding,
- namespace: namespace,
- user: current_user,
- sticky_to: current_user
- ) do |e|
- e.control { [:security_scan_enabled] }
- e.candidate { [:license_scanning_run, :secure_dependency_scanning_run, :secure_dast_run] }
- end.run
- end
-
- attr_reader :namespace, :current_user
- end
-end
diff --git a/lib/learn_gitlab/project.rb b/lib/learn_gitlab/project.rb
deleted file mode 100644
index 64f91dcf1a8..00000000000
--- a/lib/learn_gitlab/project.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-# frozen_string_literal: true
-
-module LearnGitlab
- class Project
- PROJECT_NAME = 'Learn GitLab'
- PROJECT_NAME_ULTIMATE_TRIAL = 'Learn GitLab - Ultimate trial'
- BOARD_NAME = 'GitLab onboarding'
- LABEL_NAME = 'Novice'
-
- def initialize(current_user)
- @current_user = current_user
- end
-
- def available?
- project && board && label
- end
-
- def project
- @project ||= current_user.projects.find_by_name([PROJECT_NAME, PROJECT_NAME_ULTIMATE_TRIAL])
- end
-
- def board
- return unless project
-
- @board ||= project.boards.find_by_name(BOARD_NAME)
- end
-
- def label
- return unless project
-
- @label ||= project.labels.find_by_name(LABEL_NAME)
- end
-
- private
-
- attr_reader :current_user
- end
-end
diff --git a/lib/object_storage/direct_upload.rb b/lib/object_storage/direct_upload.rb
index a8b51a95e59..d092cd56e46 100644
--- a/lib/object_storage/direct_upload.rb
+++ b/lib/object_storage/direct_upload.rb
@@ -206,7 +206,7 @@ module ObjectStorage
def requires_multipart_upload?
return false unless config.aws?
- return false if use_workhorse_s3_client? && Feature.enabled?(:s3_omit_multipart_urls)
+ return false if use_workhorse_s3_client?
!has_length
end
diff --git a/lib/omni_auth/strategies/bitbucket.rb b/lib/omni_auth/strategies/bitbucket.rb
index 6c914b4222a..d64f3dd987d 100644
--- a/lib/omni_auth/strategies/bitbucket.rb
+++ b/lib/omni_auth/strategies/bitbucket.rb
@@ -40,7 +40,7 @@ module OmniAuth
end
def callback_url
- options[:redirect_uri] || (full_host + script_name + callback_path)
+ options[:redirect_uri] || (full_host + callback_path)
end
end
end
diff --git a/lib/peek/views/redis_detailed.rb b/lib/peek/views/redis_detailed.rb
index 44ec0ec0f68..76c283bf802 100644
--- a/lib/peek/views/redis_detailed.rb
+++ b/lib/peek/views/redis_detailed.rb
@@ -16,7 +16,11 @@ module Peek
private
def format_call_details(call)
- super.merge(cmd: format_command(call[:cmd]),
+ cmd = call[:commands].map do |command|
+ format_command(command)
+ end.join(', ')
+
+ super.merge(cmd: cmd,
instance: call[:storage])
end
diff --git a/lib/product_analytics/event_params.rb b/lib/product_analytics/event_params.rb
index 07e0bc8b43a..6cb3d462384 100644
--- a/lib/product_analytics/event_params.rb
+++ b/lib/product_analytics/event_params.rb
@@ -11,41 +11,41 @@ module ProductAnalytics
class EventParams
def self.parse_event_params(params)
{
- project_id: params['aid'],
- platform: params['p'],
- collector_tstamp: Time.zone.now,
- event_id: params['eid'],
- v_tracker: params['tv'],
- v_collector: Gitlab::VERSION,
- v_etl: Gitlab::VERSION,
- os_timezone: params['tz'],
- name_tracker: params['tna'],
- br_lang: params['lang'],
- doc_charset: params['cs'],
- br_features_pdf: Gitlab::Utils.to_boolean(params['f_pdf']),
- br_features_flash: Gitlab::Utils.to_boolean(params['f_fla']),
- br_features_java: Gitlab::Utils.to_boolean(params['f_java']),
- br_features_director: Gitlab::Utils.to_boolean(params['f_dir']),
- br_features_quicktime: Gitlab::Utils.to_boolean(params['f_qt']),
- br_features_realplayer: Gitlab::Utils.to_boolean(params['f_realp']),
+ project_id: params['aid'],
+ platform: params['p'],
+ collector_tstamp: Time.zone.now,
+ event_id: params['eid'],
+ v_tracker: params['tv'],
+ v_collector: Gitlab::VERSION,
+ v_etl: Gitlab::VERSION,
+ os_timezone: params['tz'],
+ name_tracker: params['tna'],
+ br_lang: params['lang'],
+ doc_charset: params['cs'],
+ br_features_pdf: Gitlab::Utils.to_boolean(params['f_pdf']),
+ br_features_flash: Gitlab::Utils.to_boolean(params['f_fla']),
+ br_features_java: Gitlab::Utils.to_boolean(params['f_java']),
+ br_features_director: Gitlab::Utils.to_boolean(params['f_dir']),
+ br_features_quicktime: Gitlab::Utils.to_boolean(params['f_qt']),
+ br_features_realplayer: Gitlab::Utils.to_boolean(params['f_realp']),
br_features_windowsmedia: Gitlab::Utils.to_boolean(params['f_wma']),
- br_features_gears: Gitlab::Utils.to_boolean(params['f_gears']),
- br_features_silverlight: Gitlab::Utils.to_boolean(params['f_ag']),
- br_colordepth: params['cd'],
- br_cookies: Gitlab::Utils.to_boolean(params['cookie']),
- dvce_created_tstamp: params['dtm'],
- br_viewheight: params['vp'],
- domain_sessionidx: params['vid'],
- domain_sessionid: params['sid'],
- domain_userid: params['duid'],
- user_fingerprint: params['fp'],
- page_referrer: params['refr'],
- page_url: params['url'],
- se_category: params['se_ca'],
- se_action: params['se_ac'],
- se_label: params['se_la'],
- se_property: params['se_pr'],
- se_value: params['se_va']
+ br_features_gears: Gitlab::Utils.to_boolean(params['f_gears']),
+ br_features_silverlight: Gitlab::Utils.to_boolean(params['f_ag']),
+ br_colordepth: params['cd'],
+ br_cookies: Gitlab::Utils.to_boolean(params['cookie']),
+ dvce_created_tstamp: params['dtm'],
+ br_viewheight: params['vp'],
+ domain_sessionidx: params['vid'],
+ domain_sessionid: params['sid'],
+ domain_userid: params['duid'],
+ user_fingerprint: params['fp'],
+ page_referrer: params['refr'],
+ page_url: params['url'],
+ se_category: params['se_ca'],
+ se_action: params['se_ac'],
+ se_label: params['se_la'],
+ se_property: params['se_pr'],
+ se_value: params['se_va']
}
end
diff --git a/lib/security/weak_passwords.rb b/lib/security/weak_passwords.rb
new file mode 100644
index 00000000000..42b02132933
--- /dev/null
+++ b/lib/security/weak_passwords.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+module Security
+ module WeakPasswords
+ # These words are predictable in GitLab's specific context, and
+ # therefore cannot occur anywhere within a password.
+ FORBIDDEN_WORDS = Set['gitlab', 'devops'].freeze
+
+ # Substrings shorter than this may appear legitimately in a truly
+ # random password.
+ MINIMUM_SUBSTRING_SIZE = 4
+
+ class << self
+ # Returns true when the password is on a list of weak passwords,
+ # or contains predictable substrings derived from user attributes.
+ # Case insensitive.
+ def weak_for_user?(password, user)
+ forbidden_word_appears_in_password?(password) ||
+ name_appears_in_password?(password, user) ||
+ username_appears_in_password?(password, user) ||
+ email_appears_in_password?(password, user) ||
+ password_on_weak_list?(password)
+ end
+
+ private
+
+ def forbidden_word_appears_in_password?(password)
+ contains_predicatable_substring?(password, FORBIDDEN_WORDS)
+ end
+
+ def name_appears_in_password?(password, user)
+ return false if user.name.blank?
+
+ # Check for the full name
+ substrings = [user.name]
+ # Also check parts of their name
+ substrings += user.name.split(/[^\p{Alnum}]/)
+
+ contains_predicatable_substring?(password, substrings)
+ end
+
+ def username_appears_in_password?(password, user)
+ return false if user.username.blank?
+
+ # Check for the full username
+ substrings = [user.username]
+ # Also check sub-strings in the username
+ substrings += user.username.split(/[^\p{Alnum}]/)
+
+ contains_predicatable_substring?(password, substrings)
+ end
+
+ def email_appears_in_password?(password, user)
+ return false if user.email.blank?
+
+ # Check for the full email
+ substrings = [user.email]
+ # Also check full first part and full domain name
+ substrings += user.email.split("@")
+ # And any parts of non-word characters (e.g. firstname.lastname+tag@...)
+ substrings += user.email.split(/[^\p{Alnum}]/)
+
+ contains_predicatable_substring?(password, substrings)
+ end
+
+ def password_on_weak_list?(password)
+ # Our weak list stores SHA2 hashes of passwords, not the weak
+ # passwords themselves.
+ digest = Digest::SHA256.base64digest(password.downcase)
+ Settings.gitlab.weak_passwords_digest_set.include?(digest)
+ end
+
+ # 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.
+ def contains_predicatable_substring?(password, substrings)
+ substrings = substrings.filter_map do |substring|
+ substring.downcase if substring.length >= MINIMUM_SUBSTRING_SIZE
+ end
+
+ password = password.downcase
+
+ # Returns true when a predictable substring occurs anywhere
+ # in the password.
+ substrings.any? { |word| password.include?(word) }
+ end
+ end
+ end
+end
diff --git a/lib/sidebars/groups/menus/observability_menu.rb b/lib/sidebars/groups/menus/observability_menu.rb
new file mode 100644
index 00000000000..b479ff3c492
--- /dev/null
+++ b/lib/sidebars/groups/menus/observability_menu.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Sidebars
+ module Groups
+ module Menus
+ class ObservabilityMenu < ::Sidebars::Menu
+ override :link
+ def link
+ group_observability_index_path(context.group)
+ end
+
+ override :title
+ def title
+ _('Observability')
+ end
+
+ override :sprite_icon
+ def sprite_icon
+ 'monitor'
+ end
+
+ override :render?
+ def render?
+ can?(context.current_user, :read_observability, context.group)
+ end
+
+ override :active_routes
+ def active_routes
+ { controller: :observability, path: 'groups#observability' }
+ end
+ end
+ end
+ end
+end
diff --git a/lib/sidebars/groups/menus/packages_registries_menu.rb b/lib/sidebars/groups/menus/packages_registries_menu.rb
index fda90406e0a..61cd81711f8 100644
--- a/lib/sidebars/groups/menus/packages_registries_menu.rb
+++ b/lib/sidebars/groups/menus/packages_registries_menu.rb
@@ -15,7 +15,7 @@ module Sidebars
override :title
def title
- _('Packages & Registries')
+ _('Packages and registries')
end
override :sprite_icon
@@ -50,7 +50,9 @@ module Sidebars
end
def harbor_registry__menu_item
- return nil_menu_item(:harbor_registry) if Feature.disabled?(:harbor_registry_integration)
+ if Feature.disabled?(:harbor_registry_integration) || context.group.harbor_integration.nil?
+ return nil_menu_item(:harbor_registry)
+ end
::Sidebars::MenuItem.new(
title: _('Harbor Registry'),
diff --git a/lib/sidebars/groups/menus/settings_menu.rb b/lib/sidebars/groups/menus/settings_menu.rb
index 18ff3ebc714..df170670aab 100644
--- a/lib/sidebars/groups/menus/settings_menu.rb
+++ b/lib/sidebars/groups/menus/settings_menu.rb
@@ -6,18 +6,23 @@ module Sidebars
class SettingsMenu < ::Sidebars::Menu
override :configure_menu_items
def configure_menu_items
- return false unless can?(context.current_user, :admin_group, context.group)
-
- add_item(general_menu_item)
- add_item(integrations_menu_item)
- add_item(access_tokens_menu_item)
- add_item(group_projects_menu_item)
- add_item(repository_menu_item)
- add_item(ci_cd_menu_item)
- add_item(applications_menu_item)
- add_item(packages_and_registries_menu_item)
-
- true
+ if can?(context.current_user, :admin_group, context.group)
+ add_item(general_menu_item)
+ add_item(integrations_menu_item)
+ add_item(access_tokens_menu_item)
+ add_item(group_projects_menu_item)
+ add_item(repository_menu_item)
+ add_item(ci_cd_menu_item)
+ add_item(applications_menu_item)
+ add_item(packages_and_registries_menu_item)
+ return true
+ elsif Gitlab.ee? && can?(context.current_user, :change_push_rules, context.group)
+ # Push Rules are the only group setting that can also be edited by maintainers.
+ # Create an empty sub-menu here and EE adds Repository menu item (with only Push Rules).
+ return true
+ end
+
+ false
end
override :title
@@ -112,7 +117,7 @@ module Sidebars
end
::Sidebars::MenuItem.new(
- title: _('Packages & Registries'),
+ title: _('Packages and registries'),
link: group_settings_packages_and_registries_path(context.group),
active_routes: { controller: :packages_and_registries },
item_id: :packages_and_registries
diff --git a/lib/sidebars/groups/panel.rb b/lib/sidebars/groups/panel.rb
index 463c2571b14..e8b815bdce7 100644
--- a/lib/sidebars/groups/panel.rb
+++ b/lib/sidebars/groups/panel.rb
@@ -12,6 +12,7 @@ module Sidebars
add_menu(Sidebars::Groups::Menus::MergeRequestsMenu.new(context))
add_menu(Sidebars::Groups::Menus::CiCdMenu.new(context))
add_menu(Sidebars::Groups::Menus::KubernetesMenu.new(context))
+ add_menu(Sidebars::Groups::Menus::ObservabilityMenu.new(context))
add_menu(Sidebars::Groups::Menus::PackagesRegistriesMenu.new(context))
add_menu(Sidebars::Groups::Menus::CustomerRelationsMenu.new(context))
add_menu(Sidebars::Groups::Menus::SettingsMenu.new(context))
diff --git a/lib/sidebars/projects/menus/infrastructure_menu.rb b/lib/sidebars/projects/menus/infrastructure_menu.rb
index 1c04a7b117d..63eea0ea500 100644
--- a/lib/sidebars/projects/menus/infrastructure_menu.rb
+++ b/lib/sidebars/projects/menus/infrastructure_menu.rb
@@ -54,12 +54,12 @@ module Sidebars
{ disabled: true,
data: { trigger: 'manual',
- container: 'body',
- placement: 'right',
- highlight: Users::CalloutsHelper::GKE_CLUSTER_INTEGRATION,
- highlight_priority: Users::Callout.feature_names[:GKE_CLUSTER_INTEGRATION],
- dismiss_endpoint: callouts_path,
- auto_devops_help_path: help_page_path('topics/autodevops/index.md') } }
+ container: 'body',
+ placement: 'right',
+ highlight: Users::CalloutsHelper::GKE_CLUSTER_INTEGRATION,
+ highlight_priority: Users::Callout.feature_names[:GKE_CLUSTER_INTEGRATION],
+ dismiss_endpoint: callouts_path,
+ auto_devops_help_path: help_page_path('topics/autodevops/index.md') } }
end
def terraform_menu_item
diff --git a/lib/sidebars/projects/menus/learn_gitlab_menu.rb b/lib/sidebars/projects/menus/learn_gitlab_menu.rb
index d2bc2fa0681..b6fae2af93d 100644
--- a/lib/sidebars/projects/menus/learn_gitlab_menu.rb
+++ b/lib/sidebars/projects/menus/learn_gitlab_menu.rb
@@ -29,10 +29,10 @@ module Sidebars
override :pill_count
def pill_count
strong_memoize(:pill_count) do
- percentage = LearnGitlab::Onboarding.new(
+ percentage = Onboarding::Completion.new(
context.project.namespace,
context.current_user
- ).completed_percentage
+ ).percentage
"#{percentage}%"
end
diff --git a/lib/sidebars/projects/menus/merge_requests_menu.rb b/lib/sidebars/projects/menus/merge_requests_menu.rb
index fe501667d37..3e543872d36 100644
--- a/lib/sidebars/projects/menus/merge_requests_menu.rb
+++ b/lib/sidebars/projects/menus/merge_requests_menu.rb
@@ -59,9 +59,9 @@ module Sidebars
override :active_routes
def active_routes
if context.project.issues_enabled?
- { controller: :merge_requests }
+ { controller: 'projects/merge_requests' }
else
- { controller: [:merge_requests, :milestones] }
+ { controller: ['projects/merge_requests', :milestones] }
end
end
end
diff --git a/lib/sidebars/projects/menus/monitor_menu.rb b/lib/sidebars/projects/menus/monitor_menu.rb
index 23e1a95c401..ecd062f333e 100644
--- a/lib/sidebars/projects/menus/monitor_menu.rb
+++ b/lib/sidebars/projects/menus/monitor_menu.rb
@@ -6,7 +6,7 @@ module Sidebars
class MonitorMenu < ::Sidebars::Menu
override :configure_menu_items
def configure_menu_items
- return false unless context.project.feature_available?(:operations, context.current_user)
+ return false unless feature_enabled?
add_item(metrics_dashboard_menu_item)
add_item(error_tracking_menu_item)
@@ -41,6 +41,14 @@ 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
+ end
+
def metrics_dashboard_menu_item
unless can?(context.current_user, :metrics_dashboard, context.project)
return ::Sidebars::NilMenuItem.new(item_id: :metrics)
diff --git a/lib/sidebars/projects/menus/packages_registries_menu.rb b/lib/sidebars/projects/menus/packages_registries_menu.rb
index 914368e6fec..2ddffe42899 100644
--- a/lib/sidebars/projects/menus/packages_registries_menu.rb
+++ b/lib/sidebars/projects/menus/packages_registries_menu.rb
@@ -15,7 +15,7 @@ module Sidebars
override :title
def title
- _('Packages & Registries')
+ _('Packages and registries')
end
override :sprite_icon
@@ -66,7 +66,9 @@ module Sidebars
end
def harbor_registry__menu_item
- return ::Sidebars::NilMenuItem.new(item_id: :harbor_registry) if Feature.disabled?(:harbor_registry_integration)
+ if Feature.disabled?(:harbor_registry_integration, context.project) || context.project.harbor_integration.nil?
+ return ::Sidebars::NilMenuItem.new(item_id: :harbor_registry)
+ end
::Sidebars::MenuItem.new(
title: _('Harbor Registry'),
@@ -77,7 +79,8 @@ module Sidebars
end
def packages_registry_disabled?
- !::Gitlab.config.packages.enabled || !can?(context.current_user, :read_package, context.project)
+ !::Gitlab.config.packages.enabled ||
+ !can?(context.current_user, :read_package, context.project&.packages_policy_subject)
end
end
end
diff --git a/lib/sidebars/projects/menus/settings_menu.rb b/lib/sidebars/projects/menus/settings_menu.rb
index 85931e63ebc..11d5f4d59c7 100644
--- a/lib/sidebars/projects/menus/settings_menu.rb
+++ b/lib/sidebars/projects/menus/settings_menu.rb
@@ -13,6 +13,7 @@ module Sidebars
add_item(webhooks_menu_item)
add_item(access_tokens_menu_item)
add_item(repository_menu_item)
+ add_item(merge_requests_menu_item)
add_item(ci_cd_menu_item)
add_item(packages_and_registries_menu_item)
add_item(pages_menu_item)
@@ -109,9 +110,9 @@ module Sidebars
end
::Sidebars::MenuItem.new(
- title: _('Packages & Registries'),
+ title: _('Packages and registries'),
link: project_settings_packages_and_registries_path(context.project),
- active_routes: { path: 'packages_and_registries#show' },
+ active_routes: { controller: :packages_and_registries },
item_id: :packages_and_registries
)
end
@@ -150,6 +151,17 @@ module Sidebars
item_id: :usage_quotas
)
end
+
+ def merge_requests_menu_item
+ return unless context.project.merge_requests_enabled?
+
+ ::Sidebars::MenuItem.new(
+ title: _('Merge requests'),
+ link: project_settings_merge_requests_path(context.project),
+ active_routes: { path: 'projects/settings/merge_requests#show' },
+ item_id: :merge_requests
+ )
+ end
end
end
end
diff --git a/lib/sidebars/projects/panel.rb b/lib/sidebars/projects/panel.rb
index 1af8e14f034..8ae8f931aab 100644
--- a/lib/sidebars/projects/panel.rb
+++ b/lib/sidebars/projects/panel.rb
@@ -51,8 +51,7 @@ module Sidebars
end
def third_party_wiki_menu
- wiki_menu_list = [::Sidebars::Projects::Menus::ConfluenceMenu]
- wiki_menu_list << ::Sidebars::Projects::Menus::ShimoMenu if Feature.enabled?(:shimo_integration, context.project)
+ wiki_menu_list = [::Sidebars::Projects::Menus::ConfluenceMenu, ::Sidebars::Projects::Menus::ShimoMenu]
wiki_menu_list.find { |wiki_menu| wiki_menu.new(context).render? }
end
diff --git a/lib/tasks/gitlab/assets.rake b/lib/tasks/gitlab/assets.rake
index 0b70dba5c05..76ee5379213 100644
--- a/lib/tasks/gitlab/assets.rake
+++ b/lib/tasks/gitlab/assets.rake
@@ -40,6 +40,7 @@ module Tasks
asset_files
end
+
private_class_method :assets_impacting_webpack_compilation
end
end
@@ -84,9 +85,17 @@ namespace :gitlab do
if head_assets_sha256 != master_assets_sha256 || !public_assets_webpack_dir_exists
FileUtils.rm_r(Tasks::Gitlab::Assets::PUBLIC_ASSETS_WEBPACK_DIR) if public_assets_webpack_dir_exists
- unless system('yarn webpack')
+ log_path = ENV['WEBPACK_COMPILE_LOG_PATH']
+
+ cmd = 'yarn webpack'
+ cmd += " > #{log_path}" if log_path
+
+ unless system(cmd)
abort 'Error: Unable to compile webpack production bundle.'.color(:red)
end
+
+ puts "Written webpack stdout log to #{log_path}" if log_path
+ puts "You can inspect the webpack log here: #{ENV['CI_JOB_URL']}/artifacts/file/#{log_path}" if log_path && ENV['CI_JOB_URL']
end
end
diff --git a/lib/tasks/gitlab/db/truncate_legacy_tables.rake b/lib/tasks/gitlab/db/truncate_legacy_tables.rake
new file mode 100644
index 00000000000..9c3d7c3876d
--- /dev/null
+++ b/lib/tasks/gitlab/db/truncate_legacy_tables.rake
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+namespace :gitlab do
+ namespace :db do
+ namespace :truncate_legacy_tables do
+ desc "GitLab | DB | Truncate CI Tables on Main"
+ task :main, [:min_batch_size] => [:environment, 'gitlab:db:validate_config'] do |_t, args|
+ args.with_defaults(min_batch_size: 5)
+ Gitlab::Database::TablesTruncate.new(
+ database_name: 'main',
+ min_batch_size: args.min_batch_size.to_i,
+ logger: Logger.new($stdout),
+ dry_run: ENV['DRY_RUN'] == 'true',
+ until_table: ENV['UNTIL_TABLE']
+ ).execute
+ end
+
+ desc "GitLab | DB | Truncate Main Tables on CI"
+ task :ci, [:min_batch_size] => [:environment, 'gitlab:db:validate_config'] do |_t, args|
+ args.with_defaults(min_batch_size: 5)
+ Gitlab::Database::TablesTruncate.new(
+ database_name: 'ci',
+ min_batch_size: args.min_batch_size.to_i,
+ logger: Logger.new($stdout),
+ dry_run: ENV['DRY_RUN'] == 'true',
+ until_table: ENV['UNTIL_TABLE']
+ ).execute
+ end
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/db/validate_config.rake b/lib/tasks/gitlab/db/validate_config.rake
index 2a3a54b5351..bf9ebc56486 100644
--- a/lib/tasks/gitlab/db/validate_config.rake
+++ b/lib/tasks/gitlab/db/validate_config.rake
@@ -144,7 +144,7 @@ namespace :gitlab do
rescue ActiveRecord::StatementInvalid => err
raise unless err.cause.is_a?(PG::ReadOnlySqlTransaction)
- warn "WARNING: Could not write to the database #{db_config.name}: #{err.message}"
+ warn "WARNING: Could not write to the database #{db_config.name}: cannot execute UPSERT in a read-only transaction"
end
def get_db_identifier(db_config)
diff --git a/lib/tasks/gitlab/import_export/export.rake b/lib/tasks/gitlab/import_export/export.rake
index 4bdc62c9319..3cefdcc1aaf 100644
--- a/lib/tasks/gitlab/import_export/export.rake
+++ b/lib/tasks/gitlab/import_export/export.rake
@@ -27,9 +27,9 @@ namespace :gitlab do
task = Gitlab::ImportExport::Project::ExportTask.new(
namespace_path: args.namespace_path,
- project_path: args.project_path,
- username: args.username,
- file_path: args.archive_path,
+ project_path: args.project_path,
+ username: args.username,
+ file_path: args.archive_path,
logger: logger
)
diff --git a/lib/tasks/gitlab/import_export/import.rake b/lib/tasks/gitlab/import_export/import.rake
index 2702b530334..fc727eda380 100644
--- a/lib/tasks/gitlab/import_export/import.rake
+++ b/lib/tasks/gitlab/import_export/import.rake
@@ -31,9 +31,9 @@ namespace :gitlab do
task = Gitlab::ImportExport::Project::ImportTask.new(
namespace_path: args.namespace_path,
- project_path: args.project_path,
- username: args.username,
- file_path: args.archive_path,
+ project_path: args.project_path,
+ username: args.username,
+ file_path: args.archive_path,
logger: logger
)
diff --git a/lib/tasks/gitlab/tw/codeowners.rake b/lib/tasks/gitlab/tw/codeowners.rake
index f6c518784a9..148801254bf 100644
--- a/lib/tasks/gitlab/tw/codeowners.rake
+++ b/lib/tasks/gitlab/tw/codeowners.rake
@@ -19,16 +19,15 @@ namespace :tw do
end
CODE_OWNER_RULES = [
- CodeOwnerRule.new('Activation', '@kpaizee'),
- CodeOwnerRule.new("Adoption", '@kpaizee'),
- CodeOwnerRule.new('Activation', '@kpaizee'),
- CodeOwnerRule.new('Adoption', '@kpaizee'),
+ CodeOwnerRule.new('Activation', '@phillipwells'),
+ CodeOwnerRule.new('Acquisition', '@phillipwells'),
+ CodeOwnerRule.new('Anti-Abuse', '@phillipwells'),
CodeOwnerRule.new('Authentication and Authorization', '@eread'),
CodeOwnerRule.new('Certify', '@msedlakjakubowski'),
CodeOwnerRule.new('Code Review', '@aqualls'),
CodeOwnerRule.new('Compliance', '@eread'),
CodeOwnerRule.new('Composition Analysis', '@rdickenson'),
- CodeOwnerRule.new('Configure', '@sselhorn'),
+ CodeOwnerRule.new('Configure', '@phillipwells'),
CodeOwnerRule.new('Container Security', '@claytoncornell'),
CodeOwnerRule.new('Contributor Experience', '@eread'),
CodeOwnerRule.new('Conversion', '@kpaizee'),
@@ -41,7 +40,6 @@ namespace :tw do
CodeOwnerRule.new('Dynamic Analysis', '@rdickenson'),
CodeOwnerRule.new('Ecosystem', '@kpaizee'),
CodeOwnerRule.new('Editor', '@aqualls'),
- CodeOwnerRule.new('Expansion', '@kpaizee'),
CodeOwnerRule.new('Foundations', '@rdickenson'),
CodeOwnerRule.new('Fuzz Testing', '@rdickenson'),
CodeOwnerRule.new('Geo', '@axil'),
diff --git a/lib/tasks/gitlab/uploads/migrate.rake b/lib/tasks/gitlab/uploads/migrate.rake
index 80290f95e8e..2a91fd1646c 100644
--- a/lib/tasks/gitlab/uploads/migrate.rake
+++ b/lib/tasks/gitlab/uploads/migrate.rake
@@ -2,15 +2,8 @@
namespace :gitlab do
namespace :uploads do
- namespace :migrate do
- desc "GitLab | Uploads | Migrate all uploaded files to object storage"
- task all: :environment do
- Gitlab::Uploads::MigrationHelper.categories.each do |args|
- Rake::Task["gitlab:uploads:migrate"].invoke(*args)
- Rake::Task["gitlab:uploads:migrate"].reenable
- end
- end
- end
+ desc "GitLab | Uploads | Migrate all uploaded files to object storage"
+ task 'migrate:all' => :migrate
# The following is the actual rake task that migrates uploads of specified
# category to object storage
@@ -19,15 +12,8 @@ namespace :gitlab do
Gitlab::Uploads::MigrationHelper.new(args, Logger.new($stdout)).migrate_to_remote_storage
end
- namespace :migrate_to_local do
- desc "GitLab | Uploads | Migrate all uploaded files to local storage"
- task all: :environment do
- Gitlab::Uploads::MigrationHelper.categories.each do |args|
- Rake::Task["gitlab:uploads:migrate_to_local"].invoke(*args)
- Rake::Task["gitlab:uploads:migrate_to_local"].reenable
- end
- end
- end
+ desc "GitLab | Uploads | Migrate all uploaded files to local storage"
+ task 'migrate_to_local:all' => :migrate_to_local
desc 'GitLab | Uploads | Migrate the uploaded files of specified type to local storage'
task :migrate_to_local, [:uploader_class, :model_class, :mounted_as] => :environment do |_t, args|
diff --git a/lib/tasks/gitlab/usage_data.rake b/lib/tasks/gitlab/usage_data.rake
index 9f064ef4c0c..73a79427da3 100644
--- a/lib/tasks/gitlab/usage_data.rake
+++ b/lib/tasks/gitlab/usage_data.rake
@@ -17,11 +17,16 @@ namespace :gitlab do
puts Gitlab::Json.pretty_generate(Gitlab::Usage::ServicePingReport.for(output: :all_metrics_values))
end
+ desc 'GitLab | UsageData | Generate non SQL data for usage ping in JSON'
+ task dump_non_sql_in_json: :environment do
+ puts Gitlab::Json.pretty_generate(Gitlab::Usage::ServicePingReport.for(output: :non_sql_metrics_values))
+ end
+
desc 'GitLab | UsageData | Generate usage ping and send it to Versions Application'
task generate_and_send: :environment do
- result = ServicePing::SubmitService.new.execute
+ response = GitlabServicePingWorker.new.perform('triggered_from_cron' => false)
- puts Gitlab::Json.pretty_generate(result.attributes)
+ puts response.body, response.code, response.message, response.headers.inspect
end
desc 'GitLab | UsageDataMetrics | Generate usage ping from metrics definition YAML files in JSON'
@@ -51,6 +56,19 @@ namespace :gitlab do
File.write(Gitlab::UsageDataCounters::CiTemplateUniqueCounter::KNOWN_EVENTS_FILE_PATH, banner + YAML.dump(all_includes).gsub(/ *$/m, ''))
end
+ desc 'GitLab | UsageDataMetrics | Generate raw SQL metrics queries for RSpec'
+ task generate_sql_metrics_queries: :environment do
+ path = Rails.root.join('tmp', 'test')
+
+ queries = Timecop.freeze(2021, 1, 1) do
+ Gitlab::Usage::ServicePingReport.for(output: :metrics_queries)
+ end
+
+ FileUtils.mkdir_p(path)
+ FileUtils.chdir(path)
+ File.write('sql_metrics_queries.json', Gitlab::Json.pretty_generate(queries))
+ end
+
def ci_template_includes_hash(source, template_directory = nil)
Gitlab::UsageDataCounters::CiTemplateUniqueCounter.ci_templates("lib/gitlab/ci/templates/#{template_directory}").map do |template|
expanded_template_name = Gitlab::UsageDataCounters::CiTemplateUniqueCounter.expand_template_name("#{template_directory}/#{template}")
diff --git a/lib/tasks/haml-lint.rake b/lib/tasks/haml-lint.rake
deleted file mode 100644
index 29589571344..00000000000
--- a/lib/tasks/haml-lint.rake
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-unless Rails.env.production?
- require 'haml_lint/rake_task'
-
- HamlLint::RakeTask.new
-end
diff --git a/lib/tasks/rubocop.rake b/lib/tasks/rubocop.rake
index e993035aa65..0c257585bd0 100644
--- a/lib/tasks/rubocop.rake
+++ b/lib/tasks/rubocop.rake
@@ -6,6 +6,19 @@ unless Rails.env.production?
RuboCop::RakeTask.new
namespace :rubocop do
+ namespace :check do
+ desc 'Run RuboCop check gracefully'
+ task :graceful do |_task, args|
+ require_relative '../../rubocop/check_graceful_task'
+
+ # Don't reveal TODOs in this run.
+ ENV.delete('REVEAL_RUBOCOP_TODO')
+
+ result = RuboCop::CheckGracefulTask.new($stdout).run(args.extras)
+ exit result if result.nonzero?
+ end
+ end
+
namespace :todo do
desc 'Generate RuboCop todos'
task :generate do |_task, args|
diff --git a/lib/tasks/tanuki_emoji.rake b/lib/tasks/tanuki_emoji.rake
index 0dc7dd4e701..b02d7a532c4 100644
--- a/lib/tasks/tanuki_emoji.rake
+++ b/lib/tasks/tanuki_emoji.rake
@@ -148,11 +148,11 @@ namespace :tanuki_emoji do
SpriteFactory.run!(tmpdir, {
output_style: style_path,
output_image: "app/assets/images/emoji.png",
- selector: '.emoji-',
- style: :scss,
- nocomments: true,
- pngcrush: true,
- layout: :packed
+ selector: '.emoji-',
+ style: :scss,
+ nocomments: true,
+ pngcrush: true,
+ layout: :packed
})
# SpriteFactory's SCSS is a bit too verbose for our purposes here, so
@@ -215,10 +215,10 @@ namespace :tanuki_emoji do
# Combine the resized assets into a packed sprite and re-generate the SCSS
SpriteFactory.run!(tmpdir, {
output_image: "app/assets/images/emoji@2x.png",
- style: false,
- nocomments: true,
- pngcrush: true,
- layout: :packed
+ style: false,
+ nocomments: true,
+ pngcrush: true,
+ layout: :packed
})
end
diff --git a/locale/am_ET/gitlab.po b/locale/am_ET/gitlab.po
index f1cb18db5ab..3b28edfd3e2 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-08-12 18:07\n"
+"PO-Revision-Date: 2022-09-11 06:24\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr " (ከ %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] "%d መዋጮ"
msgstr[1] "%d መዋጮዎች"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/ar_SA/gitlab.po b/locale/ar_SA/gitlab.po
index 480680d3f12..1fa3ac2174f 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-08-12 18:04\n"
+"PO-Revision-Date: 2022-09-11 06:22\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,15 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -199,6 +208,15 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -325,6 +343,15 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -595,6 +622,15 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -658,15 +694,6 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -760,9 +787,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -1231,12 +1255,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1363,6 +1381,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1387,6 +1408,15 @@ msgstr[5] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1438,9 +1468,6 @@ msgstr[5] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1660,15 +1687,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
msgid "(this user)"
msgstr ""
@@ -2110,6 +2128,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -2173,6 +2197,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -2341,6 +2371,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2656,7 +2689,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2683,6 +2716,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2722,6 +2758,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2773,6 +2812,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2944,6 +2989,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2959,9 +3007,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -3160,15 +3205,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -3208,7 +3253,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -3232,6 +3277,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -3397,7 +3445,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3622,6 +3673,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -4396,9 +4450,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -4483,6 +4534,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4534,6 +4588,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4672,6 +4729,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4813,9 +4873,6 @@ msgstr[5] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4825,13 +4882,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -5404,6 +5461,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5881,6 +5941,15 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5896,10 +5965,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5917,7 +5992,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5938,6 +6013,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5947,9 +6025,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6748,12 +6823,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6769,12 +6838,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6826,9 +6889,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6874,9 +6934,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6916,6 +6973,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -7132,6 +7192,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -7258,6 +7327,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -7423,6 +7498,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7681,21 +7759,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7705,9 +7774,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8971,6 +9037,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8998,12 +9070,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -9058,6 +9142,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9748,10 +9835,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9922,6 +10009,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9988,6 +10078,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -10114,6 +10207,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10699,6 +10795,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10807,6 +10906,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10981,7 +11086,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -11584,9 +11689,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -12130,6 +12232,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -12364,6 +12469,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -12442,15 +12553,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -12463,6 +12565,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -12538,6 +12643,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -12565,16 +12673,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner name"
-msgstr ""
-
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -12595,16 +12700,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12673,13 +12775,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12871,7 +12973,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -13057,6 +13159,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -13108,6 +13213,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13690,6 +13798,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13927,6 +14041,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -14020,6 +14137,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -14596,6 +14716,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -14617,6 +14740,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -14632,6 +14761,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14854,6 +14989,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14869,9 +15007,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14896,9 +15031,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -15046,6 +15178,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -15130,9 +15265,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -15574,6 +15706,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -15610,12 +15745,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15682,18 +15811,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15730,6 +15850,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15763,9 +15886,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15841,6 +15961,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -16276,6 +16399,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -17038,9 +17164,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -17251,13 +17374,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -17407,6 +17530,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -17479,6 +17605,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -17491,17 +17620,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -17656,9 +17776,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17854,6 +17971,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -18061,6 +18184,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -18223,6 +18349,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -18592,6 +18721,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -18604,6 +18736,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -18622,6 +18760,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -18634,6 +18781,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -18661,6 +18811,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18931,9 +19087,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -19369,9 +19522,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -19399,6 +19549,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19762,13 +19915,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19807,8 +19963,8 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -19816,8 +19972,8 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -19825,7 +19981,7 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19837,43 +19993,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
-msgstr ""
-
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19882,6 +20032,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -20032,6 +20185,9 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -20167,9 +20323,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -20203,6 +20356,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -20326,9 +20482,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -20341,15 +20503,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -20371,6 +20554,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -20440,9 +20626,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -20713,9 +20896,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -21505,6 +21685,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -21523,6 +21706,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -21556,7 +21742,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -21697,9 +21883,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21973,6 +22165,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -22285,6 +22480,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -22300,6 +22498,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -22321,9 +22525,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -22348,9 +22549,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -22468,12 +22666,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22792,9 +22996,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22840,6 +23041,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22855,12 +23059,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22918,9 +23116,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22930,9 +23125,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22945,9 +23137,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -23242,9 +23431,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -23683,7 +23869,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -23698,6 +23884,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23872,9 +24061,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23926,6 +24112,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -24682,6 +24871,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24970,10 +25162,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -25465,6 +25654,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -25552,6 +25744,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -25627,6 +25822,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -26311,16 +26530,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -26335,6 +26557,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -26377,9 +26602,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -26470,12 +26692,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -27007,6 +27223,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -27019,9 +27238,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -27172,6 +27388,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27193,9 +27412,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -27388,6 +27604,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -27538,6 +27757,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -27547,6 +27769,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -27562,6 +27802,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -27592,9 +27844,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -27613,6 +27874,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -27721,6 +27988,9 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27940,6 +28210,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27961,6 +28237,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -28084,6 +28363,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -28243,9 +28525,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -28459,6 +28747,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -28495,6 +28786,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -28519,6 +28813,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -28741,6 +29038,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28777,9 +29077,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -28789,12 +29086,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28900,7 +29191,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -29149,9 +29440,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -29227,6 +29515,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29932,25 +30226,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Longest queued job"
-msgstr ""
-
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29962,18 +30247,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -30031,12 +30310,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -30067,9 +30340,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -30169,7 +30439,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -30385,6 +30655,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -30406,10 +30679,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -30433,6 +30706,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -30454,9 +30730,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30868,9 +31141,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30901,6 +31171,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -31588,6 +31861,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -31600,6 +31876,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -31618,6 +31897,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -31759,6 +32041,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -32569,7 +32854,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -32587,7 +32872,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -32770,9 +33055,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32896,9 +33178,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -33133,6 +33412,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -34561,6 +34843,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -34576,6 +34861,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -34624,6 +34915,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -34675,6 +34969,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -34699,6 +34996,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -34744,24 +35044,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34912,9 +35203,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -35002,9 +35290,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -35023,42 +35320,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -35152,6 +35425,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -35197,12 +35473,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -35212,6 +35503,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -35836,6 +36139,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -36091,6 +36397,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -37483,12 +37792,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -37762,6 +38080,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -37852,6 +38173,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -38236,6 +38560,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -38251,9 +38578,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38992,6 +39316,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -40291,6 +40618,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -40387,6 +40717,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -40399,6 +40735,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -40447,10 +40786,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -40555,9 +40894,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -40909,6 +41245,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -40933,7 +41272,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -41314,6 +41653,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -41326,9 +41668,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -41431,6 +41770,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -41641,6 +41983,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -41668,6 +42013,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -41686,6 +42049,12 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -41740,6 +42109,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -41902,9 +42274,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -41929,6 +42298,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -41938,6 +42319,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -42046,9 +42430,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -42073,9 +42466,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -42244,39 +42649,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -42382,21 +42760,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -42976,6 +43351,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -43003,6 +43381,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -43099,10 +43480,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -43168,7 +43549,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -43330,6 +43711,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -43735,7 +44119,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -44029,10 +44413,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -44740,6 +45127,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -44821,6 +45214,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -44863,6 +45259,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -44920,6 +45319,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -44938,6 +45340,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -44989,6 +45394,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -45208,9 +45616,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -45235,9 +45640,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -45325,10 +45727,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -45346,7 +45748,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -45361,16 +45763,13 @@ msgstr[5] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -45379,19 +45778,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
+msgstr ""
+
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -45400,19 +45811,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -45427,31 +45841,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -45679,6 +46102,15 @@ 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 ""
@@ -45739,15 +46171,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -45853,15 +46276,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46030,7 +46444,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -46093,6 +46507,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -46204,9 +46621,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -46246,6 +46660,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -46396,9 +46816,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -46468,6 +46885,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -46483,7 +46903,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -46702,9 +47122,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -46804,6 +47221,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -46849,6 +47269,9 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+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 ""
@@ -46897,7 +47320,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -47113,7 +47536,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -47242,9 +47665,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -47281,6 +47701,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -47413,6 +47836,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -47638,6 +48064,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -47746,7 +48175,7 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -47767,7 +48196,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -47938,6 +48367,9 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -48073,9 +48505,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -48661,6 +49090,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/as_IN/gitlab.po b/locale/as_IN/gitlab.po
index 309ebf04f2a..e9768c881e3 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-08-12 18:06\n"
+"PO-Revision-Date: 2022-09-11 06:24\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/az_AZ/gitlab.po b/locale/az_AZ/gitlab.po
index 4fd1b55aea9..90da7dcc69b 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-08-12 18:03\n"
+"PO-Revision-Date: 2022-09-11 06:21\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/ba_RU/gitlab.po b/locale/ba_RU/gitlab.po
index c4de825f476..51525dcc90b 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-08-12 18:07\n"
+"PO-Revision-Date: 2022-09-11 06:24\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,10 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -129,6 +133,10 @@ msgid "%d approver (you've approved)"
msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -185,6 +193,10 @@ msgid "%d contribution"
msgid_plural "%d contributions"
msgstr[0] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -305,6 +317,10 @@ msgid "%d project selected"
msgid_plural "%d projects selected"
msgstr[0] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -333,10 +349,6 @@ msgid "%d tag per image name"
msgid_plural "%d tags per image name"
msgstr[0] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -385,9 +397,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -806,12 +815,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -913,6 +916,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -932,6 +938,10 @@ msgstr[0] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -958,9 +968,6 @@ msgstr[0] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1165,10 +1172,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-
msgid "(this user)"
msgstr ""
@@ -1475,6 +1478,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1538,6 +1547,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1706,6 +1721,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2021,7 +2039,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2048,6 +2066,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2087,6 +2108,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2138,6 +2162,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2309,6 +2339,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2324,9 +2357,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2525,15 +2555,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2573,7 +2603,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2597,6 +2627,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2762,7 +2795,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -2987,6 +3023,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3761,9 +3800,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3848,6 +3884,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -3899,6 +3938,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4037,6 +4079,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4173,9 +4218,6 @@ msgstr[0] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4185,13 +4227,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4729,6 +4771,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5191,6 +5236,10 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5206,10 +5255,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5227,7 +5282,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5248,6 +5303,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5257,9 +5315,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6058,12 +6113,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6079,12 +6128,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6136,9 +6179,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6179,9 +6219,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6216,6 +6253,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6422,6 +6462,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6548,6 +6597,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6713,6 +6768,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -6966,21 +7024,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -6990,9 +7039,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8241,6 +8287,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8268,12 +8320,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8328,6 +8392,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9013,10 +9080,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9187,6 +9254,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9253,6 +9323,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9374,6 +9447,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -9949,6 +10025,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10052,6 +10131,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10226,7 +10311,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10829,9 +10914,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11375,6 +11457,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11604,6 +11689,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11682,15 +11773,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11703,6 +11785,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11778,6 +11863,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11805,16 +11893,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11835,16 +11920,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -11913,13 +11995,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12106,7 +12188,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12292,6 +12374,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12343,6 +12428,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -12895,6 +12983,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13122,6 +13216,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13215,6 +13312,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13771,6 +13871,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13792,6 +13895,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13807,6 +13916,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14029,6 +14144,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14044,9 +14162,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14071,9 +14186,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14221,6 +14333,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14305,9 +14420,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14749,6 +14861,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14785,12 +14900,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -14857,18 +14966,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -14905,6 +15005,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -14938,9 +15041,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15016,6 +15116,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15446,6 +15549,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16193,9 +16299,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16406,13 +16509,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16562,6 +16665,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16629,6 +16735,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16641,12 +16750,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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"
@@ -16786,9 +16891,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -16984,6 +17086,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17191,6 +17299,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17353,6 +17464,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17722,6 +17836,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17734,6 +17851,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17752,6 +17875,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17764,6 +17896,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17791,6 +17926,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18061,9 +18202,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18499,9 +18637,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18529,6 +18664,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -18892,13 +19030,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -18937,15 +19078,15 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -18957,43 +19098,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19002,6 +19137,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19147,6 +19285,9 @@ msgid "Hide chart"
msgid_plural "Hide charts"
msgstr[0] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19277,9 +19418,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19313,6 +19451,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19436,9 +19577,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19451,15 +19598,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19481,6 +19649,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19550,9 +19721,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19808,9 +19976,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20600,6 +20765,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20618,6 +20786,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20651,7 +20822,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20792,9 +20963,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21063,6 +21240,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21375,6 +21555,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21390,6 +21573,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21406,9 +21595,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21433,9 +21619,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21553,12 +21736,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -21877,9 +22066,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -21925,6 +22111,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -21940,12 +22129,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22003,9 +22186,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22015,9 +22195,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22030,9 +22207,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22327,9 +22501,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22763,7 +22934,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22778,6 +22949,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -22947,9 +23121,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23001,6 +23172,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23727,6 +23901,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24015,10 +24192,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24500,6 +24674,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24587,6 +24764,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24662,6 +24842,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25336,16 +25540,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25360,6 +25567,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25402,9 +25612,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25495,12 +25702,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26022,6 +26223,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26034,9 +26238,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26187,6 +26388,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26208,9 +26412,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26398,6 +26599,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26528,6 +26732,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26537,6 +26744,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26552,6 +26777,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26582,9 +26819,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26603,6 +26849,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26706,6 +26958,9 @@ msgid "On %{end_date}, your trial will end and %{namespace_name} will be limited
msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be limited to %{free_user_limit} members"
msgstr[0] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -26925,6 +27180,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -26946,6 +27207,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27069,6 +27333,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27223,9 +27490,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27439,6 +27712,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27475,6 +27751,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27499,6 +27778,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27716,6 +27998,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27747,9 +28032,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27759,12 +28041,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -27865,7 +28141,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28114,9 +28390,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28192,6 +28465,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -28897,25 +29176,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -28927,18 +29197,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -28996,12 +29260,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29032,9 +29290,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29134,7 +29389,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29350,6 +29605,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29371,10 +29629,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29398,6 +29656,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29419,9 +29680,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -29833,9 +30091,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -29866,6 +30121,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30553,6 +30811,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30565,6 +30826,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30583,6 +30847,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30724,6 +30991,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31534,7 +31804,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31552,7 +31822,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31735,9 +32005,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -31861,9 +32128,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32093,6 +32357,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33446,6 +33713,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33461,6 +33731,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33509,6 +33785,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33555,6 +33834,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33579,6 +33861,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33624,24 +33909,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -33787,9 +34063,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -33872,9 +34145,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -33893,42 +34175,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34022,6 +34280,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34067,12 +34328,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34082,6 +34358,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34651,6 +34939,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -34906,6 +35197,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36288,12 +36582,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36567,6 +36870,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36657,6 +36963,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37041,6 +37350,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37056,9 +37368,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -37797,6 +38106,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39046,6 +39358,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39142,6 +39457,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39154,6 +39475,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39202,10 +39526,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39310,9 +39634,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39664,6 +39985,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39688,7 +40012,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40069,6 +40393,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40081,9 +40408,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40186,6 +40510,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40396,6 +40723,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40423,6 +40753,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40431,6 +40779,12 @@ msgid "Time|min"
msgid_plural "Time|mins"
msgstr[0] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40485,6 +40839,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40647,9 +41004,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40674,6 +41028,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40683,6 +41049,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -40791,9 +41160,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -40818,9 +41196,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -40989,39 +41379,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41127,21 +41490,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41721,6 +42081,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41748,6 +42111,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -41844,10 +42210,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -41913,7 +42279,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42075,6 +42441,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42475,7 +42844,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -42769,10 +43138,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43465,6 +43837,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43546,6 +43924,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43588,6 +43969,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43645,6 +44029,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43663,6 +44050,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43714,6 +44104,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -43923,9 +44316,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -43950,9 +44340,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44040,10 +44427,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44061,7 +44448,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44071,16 +44458,13 @@ msgstr[0] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44089,19 +44473,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
+msgstr ""
+
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44110,19 +44506,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44137,31 +44536,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
+msgstr ""
+
+msgid "WorkItem|Turn off confidentiality"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44384,6 +44792,10 @@ 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 ""
@@ -44444,10 +44856,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44553,10 +44961,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44720,7 +45124,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -44783,6 +45187,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -44894,9 +45301,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -44936,6 +45340,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45086,9 +45496,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45148,6 +45555,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45163,7 +45573,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45367,9 +45777,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45464,6 +45871,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45504,6 +45914,9 @@ msgid "change"
msgid_plural "changes"
msgstr[0] ""
+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 ""
@@ -45552,7 +45965,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -45748,7 +46161,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -45872,9 +46285,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -45911,6 +46321,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46033,6 +46446,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46238,6 +46654,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46336,7 +46755,7 @@ msgid "merge request"
msgid_plural "merge requests"
msgstr[0] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46357,7 +46776,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46518,6 +46937,9 @@ msgid "mrWidget|Mentions issue"
msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46653,9 +47075,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47211,6 +47630,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/bg/gitlab.po b/locale/bg/gitlab.po
index 02a44448c21..5463671b026 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-08-12 18:04\n"
+"PO-Revision-Date: 2022-09-11 06:22\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "Клони"
@@ -6855,6 +6914,9 @@ msgstr "Разглеждане на файловете"
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr "ОÑвежаването започна уÑпешно"
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr "ÐÑма хранилище"
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr "Ð¡ÑŠÐ±Ð¸Ñ‚Ð¸Ñ Ð·Ð° извеÑÑ‚Ñване"
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "чаÑ"
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] "мин"
msgstr[1] "мин"
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr "Ñек"
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/bn_BD/gitlab.po b/locale/bn_BD/gitlab.po
index b7c7fd055e3..56037de2b5e 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-08-12 18:03\n"
+"PO-Revision-Date: 2022-09-11 06:21\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/bn_IN/gitlab.po b/locale/bn_IN/gitlab.po
index 25ec416ed92..97d2549fdfd 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-08-12 18:07\n"
+"PO-Revision-Date: 2022-09-11 06:24\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/br_FR/gitlab.po b/locale/br_FR/gitlab.po
index 90218484012..d31db85434b 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-08-12 18:09\n"
+"PO-Revision-Date: 2022-09-11 06:25\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,14 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -185,6 +193,14 @@ msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -297,6 +313,14 @@ msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -537,6 +561,14 @@ msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -593,14 +625,6 @@ msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -685,9 +709,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -1146,12 +1167,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1273,6 +1288,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1296,6 +1314,14 @@ msgstr[4] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1342,9 +1368,6 @@ msgstr[4] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1561,14 +1584,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-
msgid "(this user)"
msgstr ""
@@ -1983,6 +1998,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -2046,6 +2067,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -2214,6 +2241,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2529,7 +2559,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2556,6 +2586,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2595,6 +2628,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2646,6 +2682,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2817,6 +2859,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2832,9 +2877,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -3033,15 +3075,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -3081,7 +3123,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -3105,6 +3147,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -3270,7 +3315,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3495,6 +3543,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -4269,9 +4320,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -4356,6 +4404,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4407,6 +4458,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4545,6 +4599,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4685,9 +4742,6 @@ msgstr[4] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4697,13 +4751,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -5269,6 +5323,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5743,6 +5800,14 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5758,10 +5823,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5779,7 +5850,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5800,6 +5871,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5809,9 +5883,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6610,12 +6681,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6631,12 +6696,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6688,9 +6747,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6735,9 +6791,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6776,6 +6829,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6990,6 +7046,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -7116,6 +7181,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -7281,6 +7352,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7538,21 +7612,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7562,9 +7627,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8825,6 +8887,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8852,12 +8920,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8912,6 +8992,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9601,10 +9684,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9775,6 +9858,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9841,6 +9927,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9966,6 +10055,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10549,6 +10641,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10656,6 +10751,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10830,7 +10931,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -11433,9 +11534,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11979,6 +12077,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -12212,6 +12313,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -12290,15 +12397,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -12311,6 +12409,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -12386,6 +12487,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -12413,16 +12517,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -12443,16 +12544,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12521,13 +12619,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12718,7 +12816,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12904,6 +13002,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12955,6 +13056,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13531,6 +13635,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13766,6 +13876,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13859,6 +13972,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -14431,6 +14547,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -14452,6 +14571,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -14467,6 +14592,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14689,6 +14820,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14704,9 +14838,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14731,9 +14862,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14881,6 +15009,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14965,9 +15096,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -15409,6 +15537,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -15445,12 +15576,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15517,18 +15642,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15565,6 +15681,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15598,9 +15717,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15676,6 +15792,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -16110,6 +16229,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16869,9 +16991,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -17082,13 +17201,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -17238,6 +17357,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -17309,6 +17431,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -17321,16 +17446,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -17482,9 +17599,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17680,6 +17794,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17887,6 +18007,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -18049,6 +18172,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -18418,6 +18544,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -18430,6 +18559,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -18448,6 +18583,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -18460,6 +18604,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -18487,6 +18634,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18757,9 +18910,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -19195,9 +19345,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -19225,6 +19372,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19588,13 +19738,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19633,23 +19786,23 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19661,43 +19814,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|The image repository could not be found."
-msgstr ""
-
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19706,6 +19853,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19855,6 +20005,9 @@ msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19989,9 +20142,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -20025,6 +20175,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -20148,9 +20301,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -20163,15 +20322,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -20193,6 +20373,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -20262,9 +20445,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -20532,9 +20712,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -21324,6 +21501,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -21342,6 +21522,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -21375,7 +21558,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -21516,9 +21699,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21791,6 +21980,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -22103,6 +22295,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -22118,6 +22313,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -22138,9 +22339,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -22165,9 +22363,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -22285,12 +22480,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22609,9 +22810,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22657,6 +22855,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22672,12 +22873,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22735,9 +22930,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22747,9 +22939,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22762,9 +22951,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -23059,9 +23245,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -23499,7 +23682,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -23514,6 +23697,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23687,9 +23873,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23741,6 +23924,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -24491,6 +24677,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24779,10 +24968,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -25272,6 +25458,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -25359,6 +25548,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -25434,6 +25626,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -26116,16 +26332,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -26140,6 +26359,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -26182,9 +26404,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -26275,12 +26494,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26810,6 +27023,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26822,9 +27038,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26975,6 +27188,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26996,9 +27212,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -27190,6 +27403,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -27336,6 +27552,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -27345,6 +27564,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -27360,6 +27597,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -27390,9 +27639,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -27411,6 +27669,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -27518,6 +27782,9 @@ msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27737,6 +28004,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27758,6 +28031,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27881,6 +28157,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -28039,9 +28318,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -28255,6 +28540,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -28291,6 +28579,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -28315,6 +28606,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -28536,6 +28830,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28571,9 +28868,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -28583,12 +28877,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28693,7 +28981,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28942,9 +29230,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -29020,6 +29305,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29725,25 +30016,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29755,18 +30037,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29824,12 +30100,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29860,9 +30130,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29962,7 +30229,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -30178,6 +30445,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -30199,10 +30469,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -30226,6 +30496,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -30247,9 +30520,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30661,9 +30931,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30694,6 +30961,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -31381,6 +31651,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -31393,6 +31666,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -31411,6 +31687,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -31552,6 +31831,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -32362,7 +32644,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -32380,7 +32662,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -32563,9 +32845,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32689,9 +32968,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32925,6 +33201,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -34338,6 +34617,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -34353,6 +34635,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -34401,6 +34689,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -34451,6 +34742,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -34475,6 +34769,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -34520,24 +34817,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34687,9 +34975,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34776,9 +35061,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34797,42 +35091,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34926,6 +35196,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34971,12 +35244,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34986,6 +35274,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -35599,6 +35899,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35854,6 +36157,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -37244,12 +37550,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -37523,6 +37838,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -37613,6 +37931,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37997,6 +38318,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -38012,9 +38336,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38753,6 +39074,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -40042,6 +40366,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -40138,6 +40465,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -40150,6 +40483,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -40198,10 +40534,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -40306,9 +40642,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -40660,6 +40993,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -40684,7 +41020,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -41065,6 +41401,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -41077,9 +41416,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -41182,6 +41518,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -41392,6 +41731,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -41419,6 +41761,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -41435,6 +41795,12 @@ msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -41489,6 +41855,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -41651,9 +42020,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -41678,6 +42044,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -41687,6 +42065,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41795,9 +42176,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41822,9 +42212,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41993,39 +42395,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -42131,21 +42506,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -42725,6 +43097,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -42752,6 +43127,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42848,10 +43226,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42917,7 +43295,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -43079,6 +43457,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -43483,7 +43864,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43777,10 +44158,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -44485,6 +44869,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -44566,6 +44956,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -44608,6 +45001,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -44665,6 +45061,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -44683,6 +45082,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -44734,6 +45136,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44951,9 +45356,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44978,9 +45380,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -45068,10 +45467,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -45089,7 +45488,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -45103,16 +45502,13 @@ msgstr[4] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -45121,19 +45517,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -45142,19 +45550,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -45169,31 +45580,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
+msgstr ""
+
+msgid "WorkItem|Turn off confidentiality"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -45420,6 +45840,14 @@ 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 ""
@@ -45480,14 +45908,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -45593,14 +46013,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -45768,7 +46180,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45831,6 +46243,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45942,9 +46357,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45984,6 +46396,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -46134,9 +46552,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -46204,6 +46619,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -46219,7 +46637,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -46435,9 +46853,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -46536,6 +46951,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -46580,6 +46998,9 @@ msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
+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 ""
@@ -46628,7 +47049,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46840,7 +47261,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46968,9 +47389,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -47007,6 +47425,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -47137,6 +47558,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -47358,6 +47782,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -47464,7 +47891,7 @@ msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -47485,7 +47912,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -47654,6 +48081,9 @@ msgstr[2] ""
msgstr[3] ""
msgstr[4] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -47789,9 +48219,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -48371,6 +48798,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/bs_BA/gitlab.po b/locale/bs_BA/gitlab.po
index 8df0fb8d47f..b20cd8353be 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-08-12 18:09\n"
+"PO-Revision-Date: 2022-09-11 06:25\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,12 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid " Collected %{time}"
msgstr " prikupljeno %{time}"
@@ -157,6 +163,12 @@ msgstr[0] "%d odobravatelj (ti si odobrio/la)"
msgstr[1] "%d odobravatelja (ti si odobrio/la)"
msgstr[2] "%d odobravatelja (ti si odobrio/la)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -241,6 +253,12 @@ msgstr[0] "%d doprinos"
msgstr[1] "%d doprinosa"
msgstr[2] "%d doprinosa"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d dan"
@@ -421,6 +439,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -463,12 +487,6 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -535,9 +553,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -976,12 +991,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1093,6 +1102,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1114,6 +1126,12 @@ msgstr[2] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1150,9 +1168,6 @@ msgstr[2] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1363,12 +1378,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "(this user)"
msgstr ""
@@ -1729,6 +1738,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1792,6 +1807,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1960,6 +1981,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2275,7 +2299,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2302,6 +2326,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2341,6 +2368,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2392,6 +2422,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2563,6 +2599,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2578,9 +2617,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2779,15 +2815,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2827,7 +2863,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2851,6 +2887,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -3016,7 +3055,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3241,6 +3283,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -4015,9 +4060,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -4102,6 +4144,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4153,6 +4198,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4291,6 +4339,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4429,9 +4480,6 @@ msgstr[2] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4441,13 +4489,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4999,6 +5047,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5467,6 +5518,12 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5482,10 +5539,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5503,7 +5566,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5524,6 +5587,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5533,9 +5599,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6334,12 +6397,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6355,12 +6412,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6412,9 +6463,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6457,9 +6505,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6496,6 +6541,9 @@ msgstr "Blokirani zadaci"
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6706,6 +6754,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6832,6 +6889,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "Grane"
@@ -6997,6 +7060,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7252,21 +7318,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7276,9 +7333,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8533,6 +8587,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8560,12 +8620,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8620,6 +8692,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9307,10 +9382,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9481,6 +9556,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9547,6 +9625,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9670,6 +9751,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10249,6 +10333,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10354,6 +10441,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10528,7 +10621,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -11131,9 +11224,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11677,6 +11767,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11908,6 +12001,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11986,15 +12085,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -12007,6 +12097,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -12082,6 +12175,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -12109,16 +12205,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner name"
-msgstr ""
-
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -12139,16 +12232,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12217,13 +12307,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12412,7 +12502,7 @@ msgstr "Dani"
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12598,6 +12688,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12649,6 +12742,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13213,6 +13309,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13444,6 +13546,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13537,6 +13642,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -14101,6 +14209,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -14122,6 +14233,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -14137,6 +14254,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14359,6 +14482,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14374,9 +14500,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14401,9 +14524,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14551,6 +14671,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14635,9 +14758,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -15079,6 +15199,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -15115,12 +15238,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr "Jesi li siguran da želiš ukloniti %{bStart}%{targetIssueTitle}%{bEnd} iz %{bStart}%{parentEpicTitle}%{bEnd}?"
@@ -15187,18 +15304,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15235,6 +15343,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15268,9 +15379,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15346,6 +15454,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15778,6 +15889,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16531,9 +16645,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16744,13 +16855,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16900,6 +17011,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16969,6 +17083,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr "Petak"
@@ -16981,14 +17098,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -17134,9 +17245,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17332,6 +17440,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17539,6 +17653,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17701,6 +17818,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -18070,6 +18190,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -18082,6 +18205,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -18100,6 +18229,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -18112,6 +18250,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -18139,6 +18280,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18409,9 +18556,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18847,9 +18991,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18877,6 +19018,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19240,13 +19384,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19285,19 +19432,19 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19309,43 +19456,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19354,6 +19495,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19501,6 +19645,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19633,9 +19780,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19669,6 +19813,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19792,9 +19939,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19807,15 +19960,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19837,6 +20011,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19906,9 +20083,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -20170,9 +20344,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20962,6 +21133,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20980,6 +21154,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -21013,7 +21190,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -21154,9 +21331,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21427,6 +21610,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21739,6 +21925,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21754,6 +21943,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21772,9 +21967,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21799,9 +21991,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21919,12 +22108,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22243,9 +22438,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22291,6 +22483,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22306,12 +22501,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22369,9 +22558,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22381,9 +22567,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22396,9 +22579,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22693,9 +22873,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -23131,7 +23308,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -23146,6 +23323,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23317,9 +23497,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23371,6 +23548,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -24109,6 +24289,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24397,10 +24580,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24886,6 +25066,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24973,6 +25156,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -25048,6 +25234,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25726,16 +25936,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr "Ponedjeljak"
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25750,6 +25963,9 @@ msgstr ""
msgid "Months"
msgstr "Mjeseci"
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25792,9 +26008,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25885,12 +26098,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26416,6 +26623,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26428,9 +26638,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26581,6 +26788,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26602,9 +26812,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26794,6 +27001,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26932,6 +27142,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26941,6 +27154,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26956,6 +27187,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26986,9 +27229,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -27007,6 +27259,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -27112,6 +27370,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27331,6 +27592,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27352,6 +27619,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27475,6 +27745,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27631,9 +27904,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27847,6 +28126,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27883,6 +28165,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27907,6 +28192,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -28126,6 +28414,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28159,9 +28450,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -28171,12 +28459,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28279,7 +28561,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28528,9 +28810,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28606,6 +28885,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29311,25 +29596,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29341,18 +29617,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29410,12 +29680,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29446,9 +29710,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29548,7 +29809,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29764,6 +30025,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29785,10 +30049,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29812,6 +30076,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29833,9 +30100,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30247,9 +30511,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30280,6 +30541,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30967,6 +31231,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30979,6 +31246,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30997,6 +31267,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -31138,6 +31411,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31948,7 +32224,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31966,7 +32242,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -32149,9 +32425,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32275,9 +32548,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32509,6 +32779,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33892,6 +34165,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33907,6 +34183,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33955,6 +34237,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -34003,6 +34288,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -34027,6 +34315,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -34072,24 +34363,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34237,9 +34519,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34324,9 +34603,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34345,42 +34633,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34474,6 +34738,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34519,12 +34786,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34534,6 +34816,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -35125,6 +35419,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35380,6 +35677,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36766,12 +37066,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -37045,6 +37354,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -37135,6 +37447,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37519,6 +37834,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37534,9 +37852,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38275,6 +38590,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr "Nedjelja"
@@ -39544,6 +39862,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39640,6 +39961,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39652,6 +39979,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39700,10 +40030,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39808,9 +40138,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -40162,6 +40489,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -40186,7 +40516,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40567,6 +40897,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40579,9 +40912,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40684,6 +41014,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr "ÄŒetvrtak"
@@ -40894,6 +41227,9 @@ msgstr "maloprije"
msgid "Timeago|right now"
msgstr "upravo sada"
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40921,6 +41257,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40933,6 +41287,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40987,6 +41347,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -41149,9 +41512,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -41176,6 +41536,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -41185,6 +41557,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41293,9 +41668,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41320,9 +41704,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41491,39 +41887,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41629,21 +41998,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
+msgid "Tue"
+msgstr ""
+
msgid "Tuesday"
msgstr "Utorak"
msgid "Turn off"
msgstr ""
-msgid "Turn off notifications"
-msgstr ""
-
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -42223,6 +42589,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -42250,6 +42619,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42346,10 +42718,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42415,7 +42787,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42577,6 +42949,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42979,7 +43354,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43273,10 +43648,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43975,6 +44353,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -44056,6 +44440,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -44098,6 +44485,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -44155,6 +44545,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -44173,6 +44566,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr "Srijeda"
@@ -44224,6 +44620,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44437,9 +44836,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44464,9 +44860,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44554,10 +44947,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44575,7 +44968,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44587,16 +44980,13 @@ msgstr[2] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44605,19 +44995,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44626,19 +45028,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44653,31 +45058,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44902,6 +45316,12 @@ 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 ""
@@ -44962,12 +45382,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -45073,12 +45487,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -45244,7 +45652,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45307,6 +45715,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45418,9 +45829,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45460,6 +45868,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45610,9 +46024,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45676,6 +46087,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45691,7 +46105,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45901,9 +46315,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -46000,6 +46411,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -46042,6 +46456,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+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 ""
@@ -46090,7 +46507,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46294,7 +46711,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46420,9 +46837,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46459,6 +46873,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46585,6 +47002,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46798,6 +47218,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46900,7 +47323,7 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46921,7 +47344,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -47086,6 +47509,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -47221,9 +47647,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47791,6 +48214,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/ca_ES/gitlab.po b/locale/ca_ES/gitlab.po
index 1b709efa5dc..17a3c3c8096 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-08-12 18:04\n"
+"PO-Revision-Date: 2022-09-11 06:22\n"
msgid " %{start} to %{end}"
msgstr " Des de %{start} fins %{end}"
@@ -22,6 +22,11 @@ msgstr " Des de %{start} fins %{end}"
msgid " (from %{timeoutSource})"
msgstr " (des de %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr " Recollit %{time}"
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr "%{size} bytes"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr "Afegeix una taula"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr "Afegeix el comentari"
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "Branques"
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr "feb."
msgid "February"
msgstr "febrer"
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr "Mai"
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr "Versió del Git"
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr "Grup"
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr "Peticions de fusió"
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr "Mesos"
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr "Mou"
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/cs_CZ/gitlab.po b/locale/cs_CZ/gitlab.po
index 3ef93060c95..fe142536833 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-08-12 18:04\n"
+"PO-Revision-Date: 2022-09-11 06:22\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,13 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -171,6 +178,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -269,6 +283,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -479,6 +500,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -528,13 +556,6 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -610,9 +631,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -1061,12 +1079,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1183,6 +1195,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr "%{sourceBranch} do %{targetBranch}"
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1205,6 +1220,13 @@ msgstr[3] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1246,9 +1268,6 @@ msgstr[3] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1462,13 +1481,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "(this user)"
msgstr ""
@@ -1856,6 +1868,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1919,6 +1937,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -2087,6 +2111,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2402,7 +2429,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2429,6 +2456,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2468,6 +2498,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2519,6 +2552,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2690,6 +2729,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2705,9 +2747,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2906,15 +2945,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2954,7 +2993,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2978,6 +3017,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -3143,7 +3185,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3368,6 +3413,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -4142,9 +4190,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -4229,6 +4274,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4280,6 +4328,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4418,6 +4469,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4557,9 +4611,6 @@ msgstr[3] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4569,13 +4620,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -5134,6 +5185,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5605,6 +5659,13 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5620,10 +5681,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5641,7 +5708,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5662,6 +5729,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5671,9 +5741,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6472,12 +6539,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6493,12 +6554,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6550,9 +6605,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6596,9 +6648,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6636,6 +6685,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6848,6 +6900,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6974,6 +7035,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "Větve"
@@ -7139,6 +7206,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7395,21 +7465,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7419,9 +7480,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8679,6 +8737,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8706,12 +8770,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8766,6 +8842,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9454,10 +9533,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9628,6 +9707,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9694,6 +9776,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9818,6 +9903,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10399,6 +10487,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10505,6 +10596,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10679,7 +10776,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -11282,9 +11379,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11828,6 +11922,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -12060,6 +12157,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -12138,15 +12241,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -12159,6 +12253,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -12234,6 +12331,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -12261,16 +12361,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -12291,16 +12388,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site name"
-msgstr ""
-
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12369,13 +12463,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12565,7 +12659,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12751,6 +12845,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12802,6 +12899,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13372,6 +13472,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13605,6 +13711,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13698,6 +13807,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -14266,6 +14378,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -14287,6 +14402,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -14302,6 +14423,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14524,6 +14651,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14539,9 +14669,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14566,9 +14693,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14716,6 +14840,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14800,9 +14927,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -15244,6 +15368,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -15280,12 +15407,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15352,18 +15473,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15400,6 +15512,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15433,9 +15548,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15511,6 +15623,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15944,6 +16059,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16700,9 +16818,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16913,13 +17028,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -17069,6 +17184,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -17139,6 +17257,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -17151,15 +17272,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -17308,9 +17422,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17506,6 +17617,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17713,6 +17830,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17875,6 +17995,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -18244,6 +18367,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -18256,6 +18382,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -18274,6 +18406,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -18286,6 +18427,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -18313,6 +18457,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18583,9 +18733,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -19021,9 +19168,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -19051,6 +19195,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19414,13 +19561,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19459,21 +19609,21 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19485,43 +19635,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19530,6 +19674,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19678,6 +19825,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19811,9 +19961,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19847,6 +19994,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19970,9 +20120,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19985,15 +20141,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -20015,6 +20192,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -20084,9 +20264,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -20351,9 +20528,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -21143,6 +21317,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -21161,6 +21338,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -21194,7 +21374,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -21335,9 +21515,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21609,6 +21795,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21921,6 +22110,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21936,6 +22128,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21955,9 +22153,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21982,9 +22177,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -22102,12 +22294,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22426,9 +22624,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22474,6 +22669,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22489,12 +22687,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22552,9 +22744,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22564,9 +22753,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22579,9 +22765,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22876,9 +23059,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -23315,7 +23495,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -23330,6 +23510,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23502,9 +23685,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23556,6 +23736,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -24300,6 +24483,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24588,10 +24774,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -25079,6 +25262,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -25166,6 +25352,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -25241,6 +25430,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25921,16 +26134,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25945,6 +26161,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25987,9 +26206,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -26080,12 +26296,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26613,6 +26823,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26625,9 +26838,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26778,6 +26988,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26799,9 +27012,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26992,6 +27202,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -27134,6 +27347,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -27143,6 +27359,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -27158,6 +27392,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -27188,9 +27434,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -27209,6 +27464,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -27315,6 +27576,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27534,6 +27798,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27555,6 +27825,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27678,6 +27951,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27835,9 +28111,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -28051,6 +28333,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -28087,6 +28372,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -28111,6 +28399,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -28331,6 +28622,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28365,9 +28659,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -28377,12 +28668,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28486,7 +28771,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28735,9 +29020,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28813,6 +29095,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29518,25 +29806,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29548,18 +29827,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29617,12 +29890,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29653,9 +29920,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29755,7 +30019,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29971,6 +30235,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29992,10 +30259,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -30019,6 +30286,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -30040,9 +30310,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30454,9 +30721,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30487,6 +30751,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -31174,6 +31441,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -31186,6 +31456,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -31204,6 +31477,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -31345,6 +31621,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -32155,7 +32434,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -32173,7 +32452,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -32356,9 +32635,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32482,9 +32758,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32717,6 +32990,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -34115,6 +34391,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -34130,6 +34409,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -34178,6 +34463,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -34227,6 +34515,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -34251,6 +34542,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -34296,24 +34590,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34462,9 +34747,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34550,9 +34832,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34571,42 +34862,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34700,6 +34967,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34745,12 +35015,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34760,6 +35045,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -35362,6 +35659,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35617,6 +35917,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -37005,12 +37308,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -37284,6 +37596,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -37374,6 +37689,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37758,6 +38076,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37773,9 +38094,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38514,6 +38832,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39793,6 +40114,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39889,6 +40213,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39901,6 +40231,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39949,10 +40282,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -40057,9 +40390,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -40411,6 +40741,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -40435,7 +40768,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40816,6 +41149,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40828,9 +41164,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40933,6 +41266,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -41143,6 +41479,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -41170,6 +41509,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -41184,6 +41541,12 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -41238,6 +41601,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -41400,9 +41766,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -41427,6 +41790,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -41436,6 +41811,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41544,9 +41922,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41571,9 +41958,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41742,39 +42141,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41880,21 +42252,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -42474,6 +42843,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -42501,6 +42873,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42597,10 +42972,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42666,7 +43041,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42828,6 +43203,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -43231,7 +43609,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43525,10 +43903,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -44230,6 +44611,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -44311,6 +44698,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -44353,6 +44743,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -44410,6 +44803,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -44428,6 +44824,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -44479,6 +44878,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44694,9 +45096,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44721,9 +45120,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44811,10 +45207,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44832,7 +45228,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44845,16 +45241,13 @@ msgstr[3] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44863,19 +45256,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
+msgstr ""
+
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44884,19 +45289,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44911,31 +45319,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -45161,6 +45578,13 @@ 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 ""
@@ -45221,13 +45645,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -45333,13 +45750,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -45506,7 +45916,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45569,6 +45979,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45680,9 +46093,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45722,6 +46132,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45872,9 +46288,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45940,6 +46353,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45955,7 +46371,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -46168,9 +46584,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -46268,6 +46681,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -46311,6 +46727,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+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 ""
@@ -46359,7 +46778,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46567,7 +46986,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46694,9 +47113,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46733,6 +47149,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46861,6 +47280,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -47078,6 +47500,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -47182,7 +47607,7 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -47203,7 +47628,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -47370,6 +47795,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -47505,9 +47933,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -48081,6 +48506,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/cy_GB/gitlab.po b/locale/cy_GB/gitlab.po
index 620d1c0859c..65c2c634404 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-08-12 18:09\n"
+"PO-Revision-Date: 2022-09-11 06:25\n"
msgid " %{start} to %{end}"
msgstr " %{start} i %{end}"
@@ -22,6 +22,15 @@ msgstr " %{start} i %{end}"
msgid " (from %{timeoutSource})"
msgstr " (oddi wrth %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid " Collected %{time}"
msgstr " Casglwyd %{time}"
@@ -199,6 +208,15 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -325,6 +343,15 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -595,6 +622,15 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -658,15 +694,6 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -760,9 +787,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -1231,12 +1255,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1363,6 +1381,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1387,6 +1408,15 @@ msgstr[5] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1438,9 +1468,6 @@ msgstr[5] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1660,15 +1687,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
msgid "(this user)"
msgstr ""
@@ -2110,6 +2128,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -2173,6 +2197,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -2341,6 +2371,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2656,7 +2689,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2683,6 +2716,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2722,6 +2758,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2773,6 +2812,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2944,6 +2989,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2959,9 +3007,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -3160,15 +3205,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -3208,7 +3253,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -3232,6 +3277,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -3397,7 +3445,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3622,6 +3673,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -4396,9 +4450,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -4483,6 +4534,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4534,6 +4588,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4672,6 +4729,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4813,9 +4873,6 @@ msgstr[5] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4825,13 +4882,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -5404,6 +5461,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5881,6 +5941,15 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5896,10 +5965,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5917,7 +5992,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5938,6 +6013,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5947,9 +6025,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6748,12 +6823,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6769,12 +6838,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6826,9 +6889,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6874,9 +6934,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6916,6 +6973,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -7132,6 +7192,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -7258,6 +7327,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -7423,6 +7498,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7681,21 +7759,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7705,9 +7774,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8971,6 +9037,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8998,12 +9070,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -9058,6 +9142,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9748,10 +9835,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9922,6 +10009,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9988,6 +10078,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -10114,6 +10207,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10699,6 +10795,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10807,6 +10906,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10981,7 +11086,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -11584,9 +11689,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -12130,6 +12232,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -12364,6 +12469,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -12442,15 +12553,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -12463,6 +12565,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -12538,6 +12643,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -12565,16 +12673,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner name"
-msgstr ""
-
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -12595,16 +12700,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12673,13 +12775,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12871,7 +12973,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -13057,6 +13159,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -13108,6 +13213,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13690,6 +13798,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13927,6 +14041,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -14020,6 +14137,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -14596,6 +14716,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -14617,6 +14740,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -14632,6 +14761,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14854,6 +14989,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14869,9 +15007,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14896,9 +15031,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -15046,6 +15178,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -15130,9 +15265,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -15574,6 +15706,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -15610,12 +15745,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15682,18 +15811,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15730,6 +15850,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15763,9 +15886,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15841,6 +15961,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -16276,6 +16399,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -17038,9 +17164,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -17251,13 +17374,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -17407,6 +17530,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -17479,6 +17605,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -17491,17 +17620,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -17656,9 +17776,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17854,6 +17971,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -18061,6 +18184,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -18223,6 +18349,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -18592,6 +18721,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -18604,6 +18736,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -18622,6 +18760,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -18634,6 +18781,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -18661,6 +18811,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18931,9 +19087,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -19369,9 +19522,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -19399,6 +19549,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19762,13 +19915,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19807,8 +19963,8 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -19816,8 +19972,8 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
@@ -19825,7 +19981,7 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19837,43 +19993,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
-msgstr ""
-
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19882,6 +20032,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -20032,6 +20185,9 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -20167,9 +20323,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -20203,6 +20356,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -20326,9 +20482,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -20341,15 +20503,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -20371,6 +20554,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -20440,9 +20626,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -20713,9 +20896,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -21505,6 +21685,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -21523,6 +21706,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -21556,7 +21742,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -21697,9 +21883,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21973,6 +22165,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -22285,6 +22480,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -22300,6 +22498,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -22321,9 +22525,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -22348,9 +22549,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -22468,12 +22666,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22792,9 +22996,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22840,6 +23041,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22855,12 +23059,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22918,9 +23116,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22930,9 +23125,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22945,9 +23137,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -23242,9 +23431,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -23683,7 +23869,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -23698,6 +23884,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23872,9 +24061,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23926,6 +24112,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -24682,6 +24871,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24970,10 +25162,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -25465,6 +25654,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -25552,6 +25744,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -25627,6 +25822,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -26311,16 +26530,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -26335,6 +26557,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -26377,9 +26602,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -26470,12 +26692,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -27007,6 +27223,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -27019,9 +27238,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -27172,6 +27388,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -27193,9 +27412,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -27388,6 +27604,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -27538,6 +27757,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -27547,6 +27769,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -27562,6 +27802,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -27592,9 +27844,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -27613,6 +27874,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -27721,6 +27988,9 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27940,6 +28210,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27961,6 +28237,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -28084,6 +28363,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -28243,9 +28525,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -28459,6 +28747,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -28495,6 +28786,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -28519,6 +28813,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -28741,6 +29038,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28777,9 +29077,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -28789,12 +29086,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28900,7 +29191,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -29149,9 +29440,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -29227,6 +29515,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29932,25 +30226,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Longest queued job"
-msgstr ""
-
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29962,18 +30247,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -30031,12 +30310,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -30067,9 +30340,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -30169,7 +30439,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -30385,6 +30655,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -30406,10 +30679,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -30433,6 +30706,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -30454,9 +30730,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30868,9 +31141,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30901,6 +31171,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -31588,6 +31861,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -31600,6 +31876,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -31618,6 +31897,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -31759,6 +32041,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -32569,7 +32854,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -32587,7 +32872,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -32770,9 +33055,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32896,9 +33178,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -33133,6 +33412,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -34561,6 +34843,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -34576,6 +34861,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -34624,6 +34915,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -34675,6 +34969,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -34699,6 +34996,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -34744,24 +35044,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34912,9 +35203,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -35002,9 +35290,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -35023,42 +35320,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -35152,6 +35425,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -35197,12 +35473,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -35212,6 +35503,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -35836,6 +36139,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -36091,6 +36397,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -37483,12 +37792,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -37762,6 +38080,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -37852,6 +38173,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -38236,6 +38560,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -38251,9 +38578,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38992,6 +39316,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -40291,6 +40618,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -40387,6 +40717,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -40399,6 +40735,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -40447,10 +40786,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -40555,9 +40894,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -40909,6 +41245,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -40933,7 +41272,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -41314,6 +41653,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -41326,9 +41668,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -41431,6 +41770,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -41641,6 +41983,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -41668,6 +42013,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -41686,6 +42049,12 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -41740,6 +42109,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -41902,9 +42274,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -41929,6 +42298,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -41938,6 +42319,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -42046,9 +42430,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -42073,9 +42466,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -42244,39 +42649,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -42382,21 +42760,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -42976,6 +43351,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -43003,6 +43381,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -43099,10 +43480,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -43168,7 +43549,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -43330,6 +43711,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -43735,7 +44119,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -44029,10 +44413,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -44740,6 +45127,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -44821,6 +45214,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -44863,6 +45259,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -44920,6 +45319,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -44938,6 +45340,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -44989,6 +45394,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -45208,9 +45616,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -45235,9 +45640,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -45325,10 +45727,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -45346,7 +45748,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -45361,16 +45763,13 @@ msgstr[5] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -45379,19 +45778,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
+msgstr ""
+
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -45400,19 +45811,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -45427,31 +45841,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -45679,6 +46102,15 @@ 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 ""
@@ -45739,15 +46171,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -45853,15 +46276,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-msgstr[4] ""
-msgstr[5] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -46030,7 +46444,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -46093,6 +46507,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -46204,9 +46621,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -46246,6 +46660,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -46396,9 +46816,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -46468,6 +46885,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -46483,7 +46903,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -46702,9 +47122,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -46804,6 +47221,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -46849,6 +47269,9 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+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 ""
@@ -46897,7 +47320,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -47113,7 +47536,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -47242,9 +47665,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -47281,6 +47701,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -47413,6 +47836,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -47638,6 +48064,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -47746,7 +48175,7 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -47767,7 +48196,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -47938,6 +48367,9 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -48073,9 +48505,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -48661,6 +49090,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/da_DK/gitlab.po b/locale/da_DK/gitlab.po
index aad3f354200..bf4795ccc80 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-08-12 18:04\n"
+"PO-Revision-Date: 2022-09-11 06:22\n"
msgid " %{start} to %{end}"
msgstr " %{start} til %{end}"
@@ -22,6 +22,11 @@ msgstr " %{start} til %{end}"
msgid " (from %{timeoutSource})"
msgstr " (fra %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr " Indsamlet %{time}"
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] "%d godkender (som du har godkendt)"
msgstr[1] "%d godkendere (som du har godkendt)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] "%d bidrag"
msgstr[1] "%d bidrag"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d dag"
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] "%d projekt valgt"
msgstr[1] "%d projekter valgt"
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] "%d mærkat pr. billednavn"
msgstr[1] "%d mærkater pr. billednavn"
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] "%d utildelt problemstilling"
-msgstr[1] "%d utildelte problemstillinger"
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] "%d uløst tråd"
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr "%{address} er et ugyldigt IP-adresseområde"
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr "%{author_link} klonede %{original_issue} til %{new_issue}."
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr "%{size} byte"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr "%{sourceBranch} ind i %{targetBranch}"
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr "%{spammable_titlecase} blev indsendt til Akismet."
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr "%{strongOpen}Advarsel:%{strongClose} SAML-gruppelinks kan få GitLab til automatisk at fjerne medlemmer fra grupper."
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] "%{strong_start}%{count} medlemmer%{strong_end} skal godkende for at s
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] "%{strong_start}%{release_count}%{strong_end} udgivelse"
@@ -1264,11 +1275,6 @@ msgstr "(fjernet)"
msgid "(revoked)"
msgstr "(tilbagekaldt)"
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr "En grundlæggende skabelon til at udvikle Linux-programmer med Kotlin Na
msgid "A complete DevOps platform"
msgstr "En komplet DevOps-platform"
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr "En standardgren kan ikke vælges til et tomt projekt."
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr "En ikke-fortrolig epic kan ikke tildeles til en fortrolig forælderepic"
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr "mappe/openapi.json"
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr "Tilføj en kommentar til linjen eller træk for flere linjer"
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr "Tilføj en tabel"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr "Tilføj kommentar nu"
msgid "Add comment to design"
msgstr "Tilføj kommentar til design"
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr "Tilføj nøgle"
msgid "Add label(s)"
msgstr "Tilføj etiketter"
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr "Tilføj liste"
@@ -2436,6 +2469,9 @@ msgstr "Tilføjer %{labels} %{label_text}."
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr "Tilføjer et gøremål."
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr "Administrator"
@@ -2652,15 +2685,15 @@ msgstr "Alle nye projekter kan bruge instansens delte runnere som standard."
msgid "AdminSettings|Auto DevOps domain"
msgstr "Auto DevOps-domæne"
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr "Konfigurer Let's Encrypt"
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr "Feedtoken"
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr "Problemstillinger som er forfattet af brugeren skjules fra andre brugere
msgid "AdminUsers|It's you!"
msgstr "Det er dig!"
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr "Lær mere om %{link_start}udelukkede brugere%{link_end}."
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr "Tilladt"
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr "Tilladte tegn: +, 0-9, - og mellemrum."
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr "Et program kaldet %{link_to_client} anmoder om adgang til din GitLab-kon
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr "En e-mail-underretning blev for nyligt sendt fra administratorpanelet. Vent venligst %{wait_time_in_words} inden der prøves på at sende en ny besked."
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr "Et tomt GitLab-brugerfelt tilføjer FogBugz-brugerens fulde navn (f.eks. \"Af Ronni Ræv\") i beskrivelsen på alle problemstillinger og kommentarer. Det vil også tilknytte og/eller tildele problemstillingerne og kommentarerne med projektopretteren."
@@ -4026,6 +4068,9 @@ msgstr "Der opstod en fejl under tilføjelse af godkendere"
msgid "An error occurred while adding formatted title for epic"
msgstr "Der opstod en fejl under tilføjelse af formateret titel til epic"
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr "Der opstod en fejl under godkendelse af din rolle"
@@ -4164,6 +4209,9 @@ msgstr "Der opstod en fejl under indlæsning af et afsnit på siden."
msgid "An error occurred while loading all the files."
msgstr "Der opstod en fejl under indlæsning af alle filerne."
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr "Der opstod en fejl under indlæsning af diagramdata"
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr "Der opstod en fejl under abonnering på underretninger."
-
msgid "An error occurred while triggering the job."
msgstr "Der opstod en fejl under udløsning af jobbet."
@@ -4313,15 +4358,15 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr "Der opstod en fejl under forsøg på at generere rapporten. Prøv venligst igen senere."
+msgid "An error occurred while trying to render the content editor. Please try again."
+msgstr ""
+
msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr "Der opstod en fejl under forsøg på at køre en ny pipeline til sammenlægningsanmodningen."
msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
-msgstr "Der opstod en fejl under fjernelse af abonnering på underretninger."
-
msgid "An error occurred while updating approvers"
msgstr "Der opstod en fejl under opdatering af godkendere"
@@ -4864,6 +4909,9 @@ msgstr "Godkend"
msgid "Approve a merge request"
msgstr "Godkend en sammenlægningsanmodning"
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr "Godkend den nuværende sammenlægningsanmodning."
@@ -5329,6 +5377,11 @@ msgstr "Denne måned"
msgid "AuditLogs|User Events"
msgstr "Brugerbegivenheder"
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr "Delte runnere kan ikke aktiveres indtil et gyldigt kreditkort er blevet registreret."
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr "Validér konto"
msgid "Billings|Validate user account"
msgstr "Validér brugerkonto"
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr "Eksportér liste"
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr "Gruppeinvitation"
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr "Blokeret problemstilling"
msgid "Blocking"
msgstr "Blokerende"
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr "Blokerende problemstillinger"
@@ -6564,6 +6608,15 @@ msgstr "Udfold"
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr "Kunne ikke hente blokerende %{issuableType}s"
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "Grene"
@@ -6855,6 +6914,9 @@ msgstr "Gennemse filer"
msgid "Browse templates"
msgstr "Gennemse skabeloner"
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr "Der opstod en fejl under hentning af artefakterne"
@@ -7109,21 +7171,12 @@ msgstr "Udgivelsesstatistik"
msgid "CICDAnalytics|Releases"
msgstr "Udgivelser"
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr "Noget gik galt under hentning af udgivelsesstatistik"
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr "Lukker denne %{quick_action_target}."
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr "Sammenfold problemstillinger"
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr "Sammenfold milepæle"
@@ -9400,6 +9474,9 @@ msgstr "Kommentér og genåbn løst tråd"
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr "Placering af kommentarformular"
@@ -9522,6 +9599,9 @@ msgstr "Sammenlign GitLab-udgaver"
msgid "Compare Revisions"
msgstr "Sammenlign revisioner"
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr "Sammenlign ændringer"
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr "Aktivér udløbsregelsæt"
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr "%{created_count} oprettet, %{closed_count} lukket."
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr "%{created_count} oprettet, %{merged_count} sammenlagt, %{closed_count} lukket."
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr "Opret din gruppe"
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr "Opret/importér dit første projekt"
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr "Du har ikke tilladelse til at oprette en undergruppe i gruppen."
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr "Fejlfindingsmeddelelser"
msgid "DastProfiles|Delete profile"
msgstr "Slet profil"
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr "Vil du forkaste skannerprofilen?"
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr "Vil du forkaste webstedsprofilen?"
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr "Vil du forkaste dine ændringer?"
-
msgid "DastProfiles|Edit profile"
msgstr "Rediger profil"
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr "Indtast URL'er i en kommasepareret liste."
@@ -11930,6 +12019,9 @@ msgstr "Adgangskode"
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner Profiles"
-msgstr "Skannerprofiler"
-
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr "Vis fejlfindingsmeddelelser"
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr "Dage"
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr "Slet korpus"
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr "Slet fil"
@@ -12496,6 +12585,9 @@ msgstr "Slet udklip?"
msgid "Delete source branch"
msgstr "Slet kildegren"
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr "Slet abonnement"
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr "Designs"
msgid "DesignManagement|Discard comment"
msgstr "Forkast kommentar"
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr "Download design"
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr "Enheder (valgfrit)"
@@ -13936,6 +14040,9 @@ msgstr "Udkast"
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr "Træk dine designs hertil eller %{linkStart}klik for at uploade%{linkEnd}."
@@ -13957,6 +14064,12 @@ msgstr "Der opstod en fejl under hentning af den tildelte %{issuableAttribute} f
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr "Tildel %{issuableAttribute}"
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr "Kunne ikke hente %{issuableAttribute} for denne %{issuableType}. Prøv venligst igen."
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr "Forfaldsdato"
@@ -14194,6 +14313,9 @@ msgstr "Rediger wikiside"
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr "Redigering"
msgid "Elapsed time"
msgstr "Forløbet tid"
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr "Aktivér Auto DevOps"
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr "Aktivér Gitpod"
@@ -14470,9 +14589,6 @@ msgstr "Aktivér grupperunnere"
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr "Epic"
msgid "Epic Boards"
msgstr "Epictavler"
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr "Kan ikke finde epic."
@@ -14950,12 +15069,6 @@ msgstr "Tilføj en ny epic"
msgid "Epics|Add an existing epic"
msgstr "Tilføj en eksisterende epic"
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr "Der opstod en fejl under gemning af %{epicDateType}-dato"
-
-msgid "Epics|An error occurred while updating labels."
-msgstr "Der opstod en fejl under opdatering af etiketter."
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr "Er du sikker på, at du vil fjerne %{bStart}%{targetIssueTitle}%{bEnd} fra %{bStart}%{parentEpicTitle}%{bEnd}?"
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr "Det vil også fjerne efterkommere af %{bStart}%{targetEpicTitle}%{bEnd} fra %{bStart}%{parentEpicTitle}%{bEnd}. Er du sikker?"
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr "Kan ikke gemme epic. Prøv venligst igen"
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr "start"
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr "Fejl ved sletning af projekt. Tjek loggene for fejldetaljer."
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr "Fejl ved indlæsning af grene."
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr "Fejl ved indlæsning af landedata."
-
msgid "Error loading file viewer."
msgstr "Fejl ved indlæsning af filfremviser."
@@ -15181,6 +15285,9 @@ msgstr "Der opstod en fejl. Brugeren blev ikke oplåst"
msgid "Error parsing CSV file. Please make sure it has"
msgstr "Fejl ved fortolkning af CSV-fil. Sørg venligst for at den har"
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr "Fejl ved gengivelse af Markdown-forhåndsvisning"
@@ -15612,6 +15719,9 @@ msgstr "Udfold problemstillinger"
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr "Udfold milepæle"
@@ -16362,9 +16472,6 @@ msgstr "Feb."
msgid "February"
msgstr "Februar"
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,14 +16682,14 @@ msgstr "Fast:"
msgid "Flags"
msgstr "Flag"
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
-msgstr "Aktivér FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
+msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
-msgstr "Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
+msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
msgstr ""
@@ -16731,6 +16838,9 @@ msgstr "Offentlig"
msgid "ForkProject|Select a namespace"
msgstr "Vælg et navnerum"
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr "Projektet kan tilgås af alle brugere som er logget ind."
@@ -16799,6 +16909,9 @@ msgstr "Hyppighed"
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr "Fredag"
@@ -16811,13 +16924,8 @@ msgstr "Fra %{code_open}%{source_title}%{code_close} ind i"
msgid "From %{providerTitle}"
msgstr "Fra %{providerTitle}"
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr "Tilføj websted"
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr "Aldrig"
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr "Webstedet er i øjeblikket %{minutes_behind} bagefter det primære webst
msgid "Geo|There are no %{replicable_type} to show"
msgstr "Der er ingen %{replicable_type} at vise"
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr "Igangværende Git-overførsel"
msgid "Git version"
msgstr "Git-version"
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr "Globale genveje"
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr "Globale underretningsindstillinger"
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr "Gravatar aktiveret"
msgid "Group"
msgstr "Gruppe"
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr "Gruppen %{group_name} kunne ikke eksporteres."
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr "Overholdelsesframeworks"
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr "Tilpassede projektskabeloner"
@@ -19066,13 +19207,16 @@ msgstr "Retningslinje"
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] "Skjul diagram"
msgstr[1] "Skjul diagrammer"
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr "Skjul kommentarer på filen"
@@ -19455,9 +19599,6 @@ msgstr "Husarbejde startet"
msgid "How do I configure Akismet?"
msgstr "Hvordan konfigurerer jeg Akismet?"
-msgid "How do I configure runners?"
-msgstr "Hvordan konfigurerer jeg runnere?"
-
msgid "How do I configure this integration?"
msgstr "Hvordan konfigurerer jeg integreringen?"
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr "Jeg accepterer %{terms_link}"
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr "Hvis du tilføjer %{codeStart}behov%{codeEnd} til job i din pipeline, så vil du være i stand til at vise %{codeStart}behovrelationer%{codeEnd} mellem job i fanebladet som en %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr "Forbedr kundesupport med serviceskranke"
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr "MÃ¥linger"
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr "Opsummering"
@@ -20832,7 +21006,7 @@ msgstr "Medtag navnet på forfatteren til problemstillingen, sammenlægningsanmo
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr "Medtag brugernavnet i URL'en hvis det kræves: %{code_open}https://brugernavn@gitlab.company.com/gruppe/projekt.git%{code_close}."
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr "Indsæt række før"
msgid "Insert suggestion"
msgstr "Indsæt forslag"
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr "Send underretninger om projektbegivenheder til Unify Circuit."
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr "Vælg en rolle"
msgid "InviteMembersModal|Select members or type email addresses"
msgstr "Vælg medlemmer eller skriv e-mailadresser"
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr "Noget gik galt"
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr "Du inviterer medlemmer til projektet %{strongStart}%{name}%{strongEnd}."
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr "Inviter en gruppe"
@@ -21736,12 +21922,18 @@ msgstr "Bruger licenssæde:"
msgid "Is using seat"
msgstr "Bruger sæde"
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr "Lukket"
msgid "IssuableStatus|Closed (%{link})"
msgstr "Lukket (%{link})"
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr "duplikeret"
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr "Kadencenavn"
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr "Rediger gennemløb"
msgid "Iterations|Edit iteration cadence"
msgstr "Rediger gennemløbskadence"
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr "Det vil slette kadencen samt alle de gennemløb der er i den."
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr "Titel"
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr "Jobbet mangler argumentet `model_type`."
-
msgid "Job is stuck. Check runners."
msgstr "Jobbet sidder fast. Tjek runnere."
@@ -22947,7 +23121,7 @@ msgstr "Etiketter kan anvendes på %{features}. Gruppeetiketter er tilgængelige
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."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr "Forfrem etiket"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr "Sprog"
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr "Lær GitLab"
-msgid "Learn GitLab - Ultimate trial"
-msgstr "Lær GitLab - Ultimate-prøveperiode"
-
msgid "Learn More"
msgstr "Lær mere"
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr "Mailgun-begivenheder"
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr "Vedligeholdelsestilstand"
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr "Sammenlægningsanmodninger"
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr "Der opstod en fejl under gemning af kladdekommentaren."
msgid "MergeRequests|Create issue to resolve thread"
msgstr "Opret problemstilling for at løse alle tråde"
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr "Kunne ikke gemme kommentaren"
@@ -24855,6 +25038,30 @@ msgstr "Ingen filer fundet"
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr "Sammenlagt"
@@ -25531,17 +25738,20 @@ msgstr "Rediger commit-meddelelser"
msgid "Modify merge commit"
msgstr "Rediger sammenlægningscommit"
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr "Mandag"
msgid "Monitor"
msgstr "Overvågning"
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
-msgstr "Overvåg helbredet og ydelsen af GitLab med Prometheus."
+msgid "Monitor Settings"
+msgstr ""
msgid "Monitor your errors by integrating with Sentry."
msgstr ""
@@ -25555,6 +25765,9 @@ msgstr "MÃ¥ned"
msgid "Months"
msgstr "MÃ¥neder"
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr "Mest relevante"
msgid "Most stars"
msgstr "Flest stjerner"
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr "Monteringspunktet %{mounted_as} ikke fundet i %{model_class}."
-
msgid "Move"
msgstr "Flyt"
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr "Ingen data fundet"
@@ -26231,9 +26438,6 @@ msgstr "Ingen udsendelser registreret. Brug miljøer til at styre din softwares
msgid "No deployments found"
msgstr "Ingen udsendelser fundet"
-msgid "No due date"
-msgstr "Ingen forfaldsdato"
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr "Intet depot"
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr "Ingen startdato"
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr "Intet at forhåndsvise."
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr "Underretningsbegivenheder"
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr "Beskrivelse (valgfrit)"
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr "HÃ¥ndter skannerprofiler"
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr "Ã…ben: %{open}"
msgid "OpenAPI"
msgstr "OpenAPI"
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr "Filsti eller URL for OpenAPI-specifikation"
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr "Pakken findes allerede"
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr "Pakketype skal være NuGet"
msgid "Package type must be PyPi"
msgstr "Pakketype skal være PyPi"
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr "Pakketype skal være RubyGems"
@@ -27703,6 +27985,9 @@ msgstr "Tilføj composer-register"
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr "Fjern pakke"
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr "Indstillinger for generiske pakker"
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr "Indstillinger for Maven-pakker"
-
msgid "PackageRegistry|Show Composer commands"
msgstr "Vis Composer-kommandoer"
@@ -28072,8 +28351,8 @@ msgstr "npm"
msgid "PackageRegistry|published by %{author}"
msgstr "udgivet af %{author}"
-msgid "Packages & Registries"
-msgstr "Pakker og registre"
+msgid "Packages and registries"
+msgstr ""
msgid "Page not found"
msgstr "Siden blev ikke fundet"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr "Udfør almindelige operationer på GitLab-projekt"
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr "Ydelsesoptimering"
@@ -28399,6 +28675,12 @@ msgstr "væg"
msgid "Period in seconds"
msgstr "Periode i sekunder"
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr "Dato"
-msgid "Pipeline|Detached merge request pipeline"
-msgstr "Løsrevet sammenlægningsanmodningspipeline"
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr "I gang"
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr "Afventer"
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr "Pipeline"
@@ -29203,12 +29470,6 @@ msgstr "Mærkatnavn"
msgid "Pipeline|Test coverage"
msgstr "Testdækning"
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr "Variabler"
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr "Vis pipeline"
@@ -29341,7 +29599,7 @@ msgstr "Tjek venligst din e-mail %{email} for at bekræfte din konto"
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr "Tjek venligst din e-mail (%{email}) for at bekræfte at du ejer adressen og lås op for styrken i CI/CD. Har du ikke modtages den? %{resend_link}. Forkert e-mailadresse? %{update_link}."
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr "Port"
msgid "Postman collection"
msgstr "Postman-samling"
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr "Filsti eller URL for Postman-samling"
@@ -29578,12 +29839,12 @@ msgstr "Opførsel"
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr "Vælg mellem fast (maks. 1280px) og flydende (%{percentage}) programlayout."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
+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 what content you want to see on your homepage."
-msgstr "Vælg det indhold du vil se på din startside."
-
msgid "Preferences|Color for added lines"
msgstr ""
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr "F.eks.: 30 minutter siden."
msgid "Preferences|Gitpod"
msgstr "Gitpod"
-msgid "Preferences|Homepage content"
-msgstr "Indhold for startside"
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr "Vis kun én fil ad gangen frem for alle de ændrede filer. Brug filvælgeren til at skifte mellem filerne."
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr "Nøgle"
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr "Ingen fil valgt."
msgid "Profiles|Notification email"
msgstr "Underretnings e-mail"
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr "Organisation"
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr "Hver sammenlægning opretter en sammenlægningscommit."
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr "Alle"
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr "Krav"
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr "%{environment_name} vil være skrivbar for udviklere. Er du sikker?"
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr "Push-begivenheder"
msgid "Push project from command line"
msgstr "Push projekt fra kommandolinje"
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr "Hurtighjælp"
msgid "Quick range"
msgstr "Hurtigområde"
-msgid "Quickly and easily edit multiple files in your project."
-msgstr "Hurtig of let redigering af flere filer i dit projekt."
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr "Tilmeld med totrinsprogram"
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr "Aktiv"
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr "Der er opstået en fejl under hentning af instruktioner"
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr "Arkitektur"
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr "Sidste kontakt"
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr "LÃ¥st til projektet"
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr "Offline"
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr "Online"
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr "Noget fik galt under hentning af mærkatforslagene"
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr "Værdi"
msgid "Runners|Version"
msgstr "Version"
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr "Vis installationsinstruktioner"
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr "gruppe"
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr "sat på pause"
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr "delt"
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr "Opfyldt"
@@ -34293,12 +34557,27 @@ msgstr "Gemmer"
msgid "Saving project."
msgstr "Gemmer projekt."
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr "Handlinger"
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr "Tilføj regel"
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr "URL for udlogningsside"
msgid "Sign-up restrictions"
msgstr "Begrænsninger for tilmelding"
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr "Ved at klikke på %{button_text} accepterer jeg at jeg har læst og accepteret %{link_start}vilkår for tjeneste og privatlivspolitik%{link_end}"
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr "Ved at klikke på %{button_text} accepterer jeg at jeg har læst og accepteret GitLabs %{link_start}vilkår for tjeneste og privatlivspolitik%{link_end}"
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr "Fornavn er for langt (maksimum er %{max_length} tegn)."
@@ -36806,6 +37112,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 while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr "Noget gik galt under tilføjelse af din belønning. Prøv venligst igen."
@@ -36896,6 +37205,9 @@ msgstr "Noget gik galt under indhentelse af Let's Encrypt-certifikatet."
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."
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr "Noget gik galt under genåbning af et krav."
@@ -37280,6 +37592,9 @@ msgstr "Squash commit-meddelelse"
msgid "Squash commits"
msgstr "Squash commits"
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr "Stakspor"
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr "Standard"
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr "Søndag"
@@ -39295,6 +39610,9 @@ msgstr "Siden kunne ikke vises fordi den fik timeout."
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr "Forælderepicen er fortrolig og kan kun indeholde fortrolige epics og problemstillinger"
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr "Adgangskoden til Jenkins-serveren."
@@ -39391,6 +39709,12 @@ msgstr "Udklippet er synligt for alle brugere som er logget ind undtagen ekstern
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr "Det angivne faneblad er ugyldigt. Vælg venligst et andet"
@@ -39403,6 +39727,9 @@ msgstr "Emnet vil blive brugt som titlen på den nye problemstilling og meddelel
msgid "The tag name can't be changed for an existing release."
msgstr "Mærkatnavnet kan ikke ændres for en eksisterende udgivelse."
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,12 +39778,12 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
+msgid "There are currently no mirrored repositories."
+msgstr ""
+
msgid "There are merge conflicts"
msgstr "Der er sammenlægningskonflikter"
-msgid "There are no %{replicableTypeName} to show"
-msgstr "Der er ingen %{replicableTypeName} at vise"
-
msgid "There are no GPG keys associated with this account."
msgstr "Der er ingen GPG-nøgler tilknyttet til kontoen."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr "Diagrammet kunne ikke vises"
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr "Projektet er licenseret under %{strong_start}%{license_name}%{strong_end
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr "Projektet håndterer sine afhængigheder med %{strong_start}%{manager_name}%{strong_end}"
@@ -40330,9 +40660,6 @@ msgstr "Projektet slettes %{date}"
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr "Projektet vil blive slettet %{date} eftersom dets forældergruppe '%{parent_group_name}' er blevet planlagt til sletning."
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr "Torsdag"
@@ -40645,6 +40975,9 @@ msgstr "lige nu"
msgid "Timeago|right now"
msgstr "lige nu"
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr "Tidszone"
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "t"
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] "m"
msgstr[1] "m"
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr "s"
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr "Gøremålsliste"
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr "GÃ¥ tilbage"
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr "I alt"
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr "Virksomhedsnavn"
-
msgid "Trial|Continue"
msgstr "Fortsæt"
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr "Fortsæt med at bruge de grundlæggende funktioner i GitLab gratis."
-
-msgid "Trial|Country"
-msgstr "Land"
-
msgid "Trial|Dismiss"
msgstr "Afskedig"
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr "GitLab Ultimate-prøveperiode (valgfrit)"
-
-msgid "Trial|Number of employees"
-msgstr "Antal ansatte"
-
-msgid "Trial|Please select a country"
-msgstr "Vælg venligst et land"
-
-msgid "Trial|Telephone number"
-msgstr "Telefonnummer"
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr "Opgrader til Ultimate for at blive ved med at bruge GitLab med avancerede funktioner."
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr "Din GitLab Ultimate-prøveperiode varer 30 dage, men du kan beholde din gratis GitLab-konto for evigt. Vi har blot brug for nogle yderligere informationer for at aktivere din prøveperiode."
@@ -41378,21 +41744,18 @@ msgstr "Prøver at kommunikere med din enhed. Tilslut den (hvis det er nødvendi
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
+msgid "Tue"
+msgstr ""
+
msgid "Tuesday"
msgstr "Tirsdag"
msgid "Turn off"
msgstr "Sluk"
-msgid "Turn off notifications"
-msgstr ""
-
msgid "Turn on"
msgstr "Tænd"
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr "Twitter"
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr "Kodepakker og beholderaftryk."
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr "Forbrug af ressourcer på tværs af dine projekter"
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr "Brug én linje pr. URI"
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr "Brugere kan starte et udviklingsmiljø fra et GitLab-browserfaneblad når %{linkStart}Gitpod%{linkEnd}-integreringen er aktiveret."
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr "Vis alertbeskeddetaljer."
msgid "View all environments."
msgstr "Vis alle miljøer."
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr "Vis alle problemstillinger"
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr "GÃ¥ til forgrening"
msgid "WebIDE|Merge request"
msgstr "Sammenlægningsanmodning"
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr "Hændelser for funktionsflag"
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr "Hændelser for problemstillinger"
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr "Websted:"
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr "Onsdag"
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr "Hvad gør kommandoen?"
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr "Hvad er Markdown?"
@@ -44180,9 +44576,6 @@ msgstr "Slet siden %{pageTitle}?"
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr "Annuller"
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr "Sidetitel"
-msgid "WikiPage|Retry"
-msgstr "Prøv igen"
-
msgid "WikiPage|Save changes"
msgstr "Gem ændringer"
@@ -44297,10 +44687,10 @@ msgstr "Igangværende arbejde (åbne og tildelte)"
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
+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|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
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."
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr "Du kunne ikke oprette en ny udløser."
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr "Du skal uploade et GitLab-projekteksportarkiv (slutter med .gz)."
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr "Du skal bekræfte din primære e-mail inden aktivering af totrinsgodkendelse."
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr "Du har afvist invitationen"
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr "Du har afvist %{user}"
@@ -45198,6 +45604,12 @@ msgstr "Din CSV-eksport af %{written_count} fra projektet %{project_name} (%{pro
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr "Din fil skal indeholde en kolonne ved navn %{codeStart}title%{codeEnd}. En kolonne med %{codeStart}description%{codeEnd} er valgfrit. Den maksimale tilladte filstørrelse er 10 MB."
-msgid "Your first project"
-msgstr "Dit første projekt"
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr "Din nye kommentar"
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr "Din primære e-mail bruges til registrering af avatar. Du kan ændre den
msgid "Your profile"
msgstr "Din profil"
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr "tildel dig selv"
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr "kl."
@@ -45732,6 +46141,9 @@ msgstr "kan ikke ændres hvis et personligt projekt har beholderregistermærkate
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr "HÃ¥ndter licenser"
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr "committed"
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr "oprettet %{timeAgo} af %{author}"
msgid "created by"
msgstr "oprettet af"
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr "data"
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr "er skrivebeskyttet"
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr "er for lang (%{current_value}). Den maksimale størrelse er %{max_size}."
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr "er for langt (maksimum er %{count} tegn)"
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] "sammenlægningsanmodning"
msgstr[1] "sammenlægningsanmodninger"
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,8 +47060,8 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
-msgstr "1 sammenlægningscommit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
+msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
msgstr ""
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] "Nævner problemstilling"
msgstr[1] "Nævner problemstillinger"
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr "Afskediget"
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr "wikiside"
diff --git a/locale/de/gitlab.po b/locale/de/gitlab.po
index 1bdf999e7f5..98c63b4cf46 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-08-12 18:04\n"
+"PO-Revision-Date: 2022-09-11 06:22\n"
msgid " %{start} to %{end}"
msgstr " %{start} bis %{end}"
@@ -22,6 +22,11 @@ msgstr " %{start} bis %{end}"
msgid " (from %{timeoutSource})"
msgstr " (von %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr " Gesammelt %{time}"
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] "%d Genehmigungsberechtigte(r) (Von dir genehmigt)"
msgstr[1] "%d Genehmigende (von dir genehmigt)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] "%d zugewiesenes Ticket"
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] "%d Beitrag"
msgstr[1] "%d Beiträge"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d Tag"
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] "%d Projekt ausgewählt"
msgstr[1] "%d Projekte ausgewählt"
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] "%d verbleibend"
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] "%d Tag pro Image-Name"
msgstr[1] "%d Tags pro Image-Name"
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] "%d nicht zugewiesenes Ticket"
-msgstr[1] "%d nicht zugewiesene Tickets"
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] "%d ungelöster Thread"
@@ -460,9 +475,6 @@ msgstr "%{actionText} & wieder öffnen %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr "%{address} ist ein ungültiger IP-Adressbereich"
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr "%{attribute} muss zwischen %{min} und %{max} liegen"
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr "%{author_link} hat %{original_issue} zu %{new_issue} geklont."
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr "%{size} Bytes"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr "%{sourceBranch} in %{targetBranch}"
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr "%{spammable_titlecase} wurde erfolgreich an Akismet übermittelt."
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr "%{strongOpen}Warnung:%{strongClose} SAML Gruppen-Links können GitLab dazu veranlassen, automatisch Mitglieder aus Gruppen zu entfernen."
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr "%{strongStart}Tipp:%{strongEnd} Du kannst Merge Requests auch lokal auschecken. %{linkStart}Mehr erfahren. %{linkEnd}"
@@ -1054,9 +1068,6 @@ msgstr[1] "%{strong_start}%{count} Mitglieder%{strong_end} müssen die Zusammenf
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] "%{strong_start}%{release_count}%{strong_end} Veröffentlichung"
@@ -1264,11 +1275,6 @@ msgstr "(entfernt)"
msgid "(revoked)"
msgstr "(widerrufen)"
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr "Eine komplette DevOps-Plattform"
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr "Ein Default-Branch kann nicht für ein leeres Projekt ausgewählt werden."
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr "AWS Zugangsschlüssel"
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr "AWS Geheimer Zugangsschlüssel"
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr "Tabelle hinzufügen"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr "Jetzt kommentieren"
msgid "Add comment to design"
msgstr "Kommentar zum Design hinzufügen"
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr "Schlüssel hinzufügen"
msgid "Add label(s)"
msgstr "Label(s) hinzufügen"
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr "Liste hinzufügen"
@@ -2436,6 +2469,9 @@ msgstr "Fügt %{labels} %{label_text} hinzu."
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr "Auto-DevOps-Domain"
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr "Automatisches Sperren von Benutzern, die mehr als eine festgelegte Anzahl von Repositories in einem bestimmten Zeitraum herunterladen."
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr "Das bist du!"
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr "Erlaube Benutzer(innen), jede Anwendung zur Nutzung von GitLab als OAuth
msgid "Allowed"
msgstr "Erlaubt"
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr "Eine Anwendung namens %{link_to_client} fordert Zugriff auf dein GitLab-
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr "Bei einem leeren GitLab-Benutzerfeld wird der vollständigen Namen des FogBugz-Benutzers/-Benutzerin (z. B. \"Von John Smith\") in die Beschreibung aller Tickets und Kommentare eingefügt. Außerdem werden diese Tickets und Kommentare auch mit dem/der Projektersteller(in) assoziiert und/oder ihm/ihr zugewiesen."
@@ -4026,6 +4068,9 @@ msgstr "Konnte Genehmigungsberechtigte nicht hinzufügen."
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr "Beim Laden der Diagrammdaten ist ein Fehler aufgetreten"
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr "Beim Speichern deiner Einstellungen ist ein Fehler aufgetreten. Versuche erneut, sie zu speichern."
-msgid "An error occurred while subscribing to notifications."
-msgstr "Beim Abonnieren von Benachrichtigungen ist ein Fehler aufgetreten."
-
msgid "An error occurred while triggering the job."
msgstr "Beim Starten des Jobs ist ein Fehler aufgetreten."
@@ -4313,15 +4358,15 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
+msgid "An error occurred while trying to render the content editor. Please try again."
+msgstr ""
+
msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
-msgstr "Beim Abbestellen der Benachrichtigungen ist ein Fehler aufgetreten."
-
msgid "An error occurred while updating approvers"
msgstr "Beim Ändern der Genehmigungsberechtigten ist ein Fehler aufgetreten"
@@ -4864,6 +4909,9 @@ msgstr "Genehmigen"
msgid "Approve a merge request"
msgstr "Einen Merge-Request genehmigen"
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr "Aktuellen Merge-Request genehmigen."
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr "Benutzerdefinierten Wert hinzufügen"
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr "Beim Aktualisieren des Ziels des externen Audit-Ereignisstreams ist ein
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr "Externes Stream-Ziel speichern"
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr "Stream erfolgreich hinzugefügt"
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr "Stream erfolgreich gelöscht"
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr "Ab dem 19. Oktober 2022 sind kostenlose Gruppen auf 5 Mitglieder begrenzt"
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr "Du kannst jetzt damit beginnen, Mitglieder in %{namespaceName} zu verschieben. Ein Mitglied verliert den Zugriff auf die Gruppe, wenn du %{strongStart}In a seat%{strongEnd} deaktivierst. Wenn nach dem 19. Oktober 2022 mehr als 5 Mitglieder %{strongStart}In a seat%{strongEnd} aktiviert haben, wählen wir die 5 Mitglieder aus, die weiterhin Zugriff haben. Wir zählen zuerst Mitglieder mit Eigentümer- und Betreuerrollen, dann die zuletzt aktiven Mitglieder, bis wir 5 Mitglieder erreicht haben. Die verbleibenden Mitglieder erhalten den Status Limit überschritten und verlieren den Zugriff auf die Gruppe."
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr "Blockiertes Ticket"
msgid "Blocking"
msgstr "Blockieren"
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr "Neues Board"
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "Branches"
@@ -6855,6 +6914,9 @@ msgstr "Dateien durchsuchen"
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr "Schließt dieses %{quick_action_target}."
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr "Konfiguration"
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr "Kommentiere & starte Diskussion erneut"
msgid "Comment '%{label}' position"
msgstr "Kommentar '%{label}' Position"
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr "Position des Kommentarformulars"
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr "Vergleiche Revisionen"
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr "Änderungen vergleichen"
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr "Codeausschnitt löschen?"
msgid "Delete source branch"
msgstr "Quellbranch löschen"
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr "Kommentar verwerfen"
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr "Entwurf"
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr "Designs hierherziehen oder %{linkStart}zum Hochladen klicken%{linkEnd}."
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr "Wiki-Seite bearbeiten"
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr "Auto-DevOps aktivieren"
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr "Epic"
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr "Epic wurde nicht gefunden."
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr "Um dein Epics %{epicDateType} basierend auf den Meilensteinen zu planen, weise einen Meilenstein mit einem %{epicDateType} einem beliebigen Ticket im Epic zu."
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr "fällig"
-
-msgid "Epics|start"
-msgstr "starten"
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr "Fehler beim Erstellen der Schwachstellensuche: %{errors}"
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr "Fehler beim Laden der Branches."
msgid "Error loading burndown chart data"
msgstr "Fehler beim Laden der Burndown-Diagrammdaten"
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr "Fehler beim Laden des Dateibetrachters."
@@ -15181,6 +15285,9 @@ msgstr "Fehler aufgetreten. Nutzer:in wurde nicht entsperrt"
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr "Feb"
msgid "February"
msgstr "Februar"
-msgid "Feedback issue"
-msgstr "Feedback-Problem"
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr "Behoben:"
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr "Häufigkeit"
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr "Freitag"
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr "Nie"
msgid "Geo|Next sync scheduled at"
msgstr "Nächste Synchronisierung geplant um"
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr "Keine Geo-Seite gefunden"
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr "Git-Version"
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr "Ausgeschlossene Benutzer"
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr "Projekte"
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr "Gruppe"
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr "Entschuldigung, dein Filter hat keine Ergebnisse geliefert."
-msgid "HarborRegistry|The filter returned no results"
-msgstr "Der Filter hat keine Ergebnisse zurückgegeben"
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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 ""
+msgid "HarborRegistry|The filter returned no results"
+msgstr "Der Filter hat keine Ergebnisse zurückgegeben"
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ msgstr "Um deine Suche zu erweitern, änder oder entferne die obigen Filter."
msgid "HarborRegistry|We are having trouble connecting to the Harbor Registry. Please try refreshing the page. If this error persists, please review %{docLinkStart}the troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr "Bereinigung erfolgreich gestartet"
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr "Ich akzeptiere die %{terms_link}"
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr "Wenn du GitHub verwendest, siehst du den Pipeline-Status für deine Comm
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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."
@@ -20781,6 +20949,9 @@ msgstr "Fehler beim Erstellen des Ereignisses auf der Zeitachse des Vorfalls: %{
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr "Fehler beim Löschen des Ereignisses auf der Zeitachse des Vorfalls: %{error}"
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr "Beim Löschen des Vorfall-Zeitachsenereignisses ist ein Fehler aufgetret
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
msgstr "Einblicke"
+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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr "Einladungsfehler überprüfen und erneut versuchen:"
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr "Verwendet eine Lizenz"
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr "Rollover aktivieren"
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr "Iterationen sollen am %{weekday}s beginnen."
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr "Dies entfernt die Iteration von allen Problemen, die ihr zugewiesen sind
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,8 +23121,8 @@ msgstr "Labels können auf %{features} angewendet werden. Gruppenlabels stehen f
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."
-msgstr "Labels können auf Tickets und Merge-Requests angewendet werden."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
+msgstr ""
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -22962,6 +23136,9 @@ msgstr "Label hochstufen"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr "Sprache"
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr "Merge-Requests"
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr "Merge-Requests dienen dazu, deine Änderungsvorschläge für ein Projekt einzureichen und sie mit anderen zu diskutieren"
@@ -24780,6 +24960,9 @@ msgstr "Beim Hinzufügen des Kommentarentwurfs ist ein Fehler aufgetreten."
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr "Speichern des Kommentars fehlgeschlagen"
@@ -24855,6 +25038,30 @@ msgstr "Keine Dateien gefunden"
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr "Zusammengeführt"
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr "Montag"
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr "Monate"
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr "Favoriten"
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr "Verschieben"
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr "Keine Kreditkarte erforderlich."
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr "Kein Fälligkeitsdatum"
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr "Kein Repository"
msgid "No results"
msgstr "Keine Ergebnisse"
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr "Das angegebene Erstellungsdatum liegt zu weit in der Vergangenheit."
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr "Benachrichtigungsereignisse"
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr "%{invite_email}, jetzt bekannt als %{user_name}, hat deine Einladung angenommen, dem %{target_name} %{target_model_name} beizutreten."
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr "%{member_link} hat %{member_role} Zugriff auf %{target_source_link} %{target_type} angefordert."
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr "Die automatische DevOps-Pipeline wurde für %{project} deaktiviert"
msgid "Notify|CI/CD project settings"
msgstr "CI/CD-Projekteinstellungen"
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr "Deine Anfrage, dem %{target_to_join} %{target_type} beizutreten, wurde %{denied_tag}."
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr "Geöffnet"
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,8 +28351,8 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
-msgstr "Pakete & Registrierungen"
+msgid "Packages and registries"
+msgstr ""
msgid "Page not found"
msgstr "Seite nicht gefunden"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr "Leistungseinblicke"
-
msgid "Performance optimization"
msgstr "Leistungsoptimierung"
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,27 +29386,18 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr "Getrennte Pipeline für Zusammenführungsanforderungen"
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr "Die fünf langsamsten Jobs"
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr "Zuletzt ausgeführter Job"
-
-msgid "Pipeline|Longest queued job"
-msgstr "Längster Job in der Warteschlange"
-
msgid "Pipeline|Manual"
msgstr ""
+msgid "Pipeline|Merge request pipeline"
+msgstr ""
+
msgid "Pipeline|Merge train pipeline"
msgstr ""
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr "Es können nur die ersten 100 Ergebnisse angezeigt werden"
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr "Leistungseinblicke"
-
msgid "Pipeline|Pipeline"
msgstr "Pipeline"
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr "Der zuletzt ausgeführte Job ist der letzte Job, der in der Pipeline gestartet wird."
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr "Der Job mit der längsten Warteschlange ist der Job, der die längste Zeit im ausstehenden Zustand verbracht hat und darauf wartete, von einem Runner abgeholt zu werden"
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr "Variabeln"
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr "Abhängigkeit anzeigen"
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,12 +29839,12 @@ msgstr "Verhalten"
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
+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 what content you want to see on your homepage."
-msgstr ""
-
msgid "Preferences|Color for added lines"
msgstr ""
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr "Beispiel: Vor 30 Minuten."
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr "Schlüssel"
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr "Benachrichtigungs-E-Mail"
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr "Organisation"
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr "Fast-Forward-Merge"
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr "%{environment_name} wird für Entwickler änderbar sein. Bist du sicher?"
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr "Push-Ereignisse"
msgid "Push project from command line"
msgstr "Pushe das Projekt von der Kommandozeile aus"
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr "Schnell und einfach mehrere Dateien in deinem Projekt bearbeiten."
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr "Upgrade verfügbar"
-
-msgid "Runners|upgrade recommended"
-msgstr "Upgrade empfohlen"
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr "Dieser %{namespaceType} enthält keine Sicherheitsrichtlinien."
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "Registrierungsbeschränkungen"
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr "Commits squashen"
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr "Stack-Trace"
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr "Standard"
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr "Markiere ein Label, um es zu einem priorisierten Label zu machen. Ordne die priorisierten Labels durch Ziehen an, um ihre relative Priorität zu ändern."
-
msgid "Star labels to start sorting by priority"
msgstr "Versehe Labels mit Sternen, um die Sortierung nach Priorität zu starten"
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr "Sonntag"
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr "Das Quellprojekt dieser Zusammenführungsanforderung wurde entfernt."
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr "Der Umfang dieses Boards ist reduziert"
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr "Diese Version wurde mit einem Datum in der Vergangenheit erstellt. Eine Beweissammlung zum Zeitpunkt der Veröffentlichung ist nicht verfügbar."
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr "Donnerstag"
@@ -40645,6 +40975,9 @@ msgstr "vor kurzem"
msgid "Timeago|right now"
msgstr "gerade jetzt"
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "Std."
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] "Min."
msgstr[1] "Min."
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr "Sek."
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr "Um deine Suche zu erweitern, änder oder entferne die obigen Filter."
msgid "To widen your search, change or remove filters above."
msgstr "Um deine Suche zu erweitern, änder oder entferne die obigen Filter."
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr "To-Do-Liste"
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr "Nichts steht auf Ihrer To-do-Liste. Gute Arbeit!"
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
+msgid "Tue"
+msgstr ""
+
msgid "Tuesday"
msgstr "Dienstag"
msgid "Turn off"
msgstr ""
-msgid "Turn off notifications"
-msgstr ""
-
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr "Twitter"
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr "Umfasst Artefakte, Repositories, Wiki, Uploads und andere Elemente."
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr "Nutzung von Ressourcen in deinen Projekten"
msgid "UsageQuota|Usage quotas help link"
msgstr "Hilfelink zu Nutzungsquoten"
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr "Verwende eine Zeile pro URI"
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr "Alle Tickets anzeigen"
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr "Zum Fork gehen"
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr "Mittwoch"
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr "Was bewirkt dieser Befehl?"
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr "Seite %{pageTitle} löschen?"
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,11 +44687,11 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
-msgstr "Eine Aufgabe bietet die Möglichkeit, deine Arbeit in kleinere Teile zu zerlegen, die mit einem Problem verbunden sind. Aufgaben sind die ersten Elemente, die unsere neuen %{workItemsLink}-Objekte verwenden. Weitere Arbeitselementtypen werden in Kürze verfügbar sein."
+msgid "WorkItem|%{workItemType} deleted"
+msgstr ""
-msgid "WorkItem|Add a task"
-msgstr "EIne Aufgabe hinzufügen"
+msgid "WorkItem|Add"
+msgstr ""
msgid "WorkItem|Add a title"
msgstr ""
@@ -44318,8 +44708,8 @@ msgstr "Aufgabe hinzufügen"
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
-msgstr "Möchtest du die Aufgabe wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
+msgstr ""
msgid "WorkItem|Assignee"
msgid_plural "WorkItem|Assignees"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
-msgstr "Aufgabe löschen"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
+msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr "Aufgaben vorstellen"
-msgid "WorkItem|Learn about tasks"
-msgstr "Aufgaben kennenlernen"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
+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|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
-msgstr "Beim Erstellen einer Aufgabe ist etwas schiefgelaufen. Bitte erneut versuchen"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
+msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr "Beim Löschen der Aufgabe ist etwas schief gelaufen. Bitteerneut versuchen"
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,33 +44797,42 @@ msgstr "Beim Versuch, ein child hinzuzufügen, ist ein Fehler aufgetreten. Bitte
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr "Aufgabe gelöscht"
+msgid "WorkItem|Tasks"
+msgstr ""
+
+msgid "WorkItem|Test case"
+msgstr ""
+
msgid "WorkItem|Turn off confidentiality"
msgstr ""
msgid "WorkItem|Turn on confidentiality"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Undo"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work item"
msgstr ""
-msgid "WorkItem|work items"
-msgstr "Arbeitsgegenstände"
-
msgid "Would you like to create a new branch?"
msgstr ""
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
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."
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr "Du hast noch keine Abonnements"
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr "Du musst zuerst deine primäre E-Mail-Adresse bestätigen, bevor du die Zwei-Faktor-Authentifizierung aktivierst."
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr "Du hast %{user} abgelehnt"
@@ -45198,6 +45604,12 @@ msgstr "Der CSV-Export von %{written_count} aus dem Projekt %{project_name} (%{p
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr "Deine primäre E-Mail-Adresse wird für die Avatar-Erkennung verwendet.
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr "%{reportType}: Beim Laden trat ein Fehler auf"
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr "Lizenzen verwalten"
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] "Merge-Request"
msgstr[1] "Merge-Requests"
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,8 +47060,8 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
-msgstr "1 Merge-Commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
+msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
msgstr ""
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr "verworfen"
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr "Wiki-Seite"
diff --git a/locale/el_GR/gitlab.po b/locale/el_GR/gitlab.po
index faef3f3d1d6..2b4d8d5cec7 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-08-12 18:05\n"
+"PO-Revision-Date: 2022-09-11 06:22\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/en_GB/gitlab.po b/locale/en_GB/gitlab.po
index adb778cec8f..ba3aa1d928d 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-08-12 18:08\n"
+"PO-Revision-Date: 2022-09-11 06:25\n"
msgid " %{start} to %{end}"
msgstr " %{start} to %{end}"
@@ -22,6 +22,11 @@ msgstr " %{start} to %{end}"
msgid " (from %{timeoutSource})"
msgstr " (from %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr " Collected %{time}"
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] "%d approver (you've approved)"
msgstr[1] "%d approvers (you've approved)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] "%d assigned issue"
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] "%d contribution"
msgstr[1] "%d contributions"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d day"
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] "%d project selected"
msgstr[1] "%d projects selected"
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] "%d remaining"
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] "%d tag per image name"
msgstr[1] "%d tags per image name"
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] "%d unassigned issue"
-msgstr[1] "%d unassigned issues"
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] "%d unresolved thread"
@@ -460,9 +475,6 @@ msgstr "%{actionText} & reopen %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr "%{address} is an invalid IP address range"
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr "%{attribute} must be between %{min} and %{max}"
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr "%{author_link} cloned %{original_issue} to %{new_issue}."
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr "Automatically ban users who download more than a specified number of repositories in a given time."
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr "An error occurred while saving your settings. Try saving them again."
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr "A header with this name already exists."
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,8 +5424,8 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
-msgstr "Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
+msgstr ""
msgid "AuditStreams|Delete %{link}"
msgstr ""
@@ -5386,6 +5445,9 @@ msgstr "Header"
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr "Maximum of %{number} HTTP headers has been reached."
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr "Time to restore service"
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr "Cloud SQL for Postgres"
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr "Cloud SQL for SQL Server"
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr "CloudSQL Instance"
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr "Create cluster"
@@ -8474,6 +8542,9 @@ msgstr "Fully managed relational database service for SQL Server"
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr "Generated database instance is linked to the selected branch or tag"
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr "Google Cloud Project"
@@ -9160,11 +9231,11 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
-msgstr "The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
-msgstr "The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
msgstr ""
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr "Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr "Enable GitLab Error Tracking"
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr "Excluded users"
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr "Groups"
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr "Projects"
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr "in %{scope}"
msgid "GlobalSearch|project"
msgstr "project"
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr "Group \"%{group_name}\" was successfully updated."
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr "Before you sign in, we need to verify your identity. Enter the following
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr "For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr "Help us protect your account"
@@ -19629,15 +19779,36 @@ msgstr "If you have not recently tried to sign into GitLab, we recommend changin
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr "If you have lost access to the email address associated with this account, or are having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr "Maximum login attempts exceeded. Wait %{interval} and try again."
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr "Please enter a valid code"
msgid "IdentityVerification|Resend code"
msgstr "Resend code"
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr "The code has expired. Resend a new code and try again."
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr "You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr "Error creating incident timeline event: %{error}"
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr "Error deleting incident timeline event: %{error}"
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr "Something went wrong while deleting the incident timeline event."
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr "Something went wrong while fetching incident timeline events."
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr "Review the invite errors and try again:"
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr "Search (e.g. *.vue) (%{modifier_key}P)"
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr "No results"
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr "%{commit_link} in %{mr_link}"
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr "Assignee changed from %{fromNames} to %{toNames}"
@@ -26754,6 +26982,18 @@ msgstr "Auto DevOps pipeline was disabled for %{project}"
msgid "Notify|CI/CD project settings"
msgstr "CI/CD project settings"
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr "If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
@@ -26784,9 +27024,18 @@ msgstr "Merge request %{mr_link} was closed by %{closed_by}"
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr "Milestone changed to %{milestone}"
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr "Unless you verify your domain by %{time_start}%{time}%{time_end} it will
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr "Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr "Additional metadata"
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr "Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr "Reject packages with the same name and version"
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr "Customise the colour of GitLab."
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr "Customise the colours of removed and added lines in diffs."
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr "Diff colours"
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr "Get started with runners"
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr "upgrade available"
-
-msgid "Runners|upgrade recommended"
-msgstr "upgrade recommended"
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr "Summary / note"
msgid "Summary comment (optional)"
msgstr "Summary comment (optional)"
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr "Too many references. Quick actions are limited to at most %{max_count} u
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr "Too many users found. Quick actions are limited to at most %{max_count} users"
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr "Are you sure you want to cancel editing?"
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] "Assignees"
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr "Closed"
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr "Create task"
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
+msgstr ""
+
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr "Open"
msgid "WorkItem|Remove"
msgstr "Remove"
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
-msgstr "Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
+msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr "Something went wrong when deleting the task. Please try again."
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,32 +44797,41 @@ msgstr "Something went wrong when trying to add a child. Please try again."
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr "Task deleted"
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr "Undo"
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
-msgstr "work items"
+msgid "WorkItem|Work item"
+msgstr ""
msgid "Would you like to create a new branch?"
msgstr ""
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,8 +45839,8 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
-msgstr "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
+msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
msgstr ""
@@ -45634,9 +46046,6 @@ msgstr "artefacts"
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr "Manage Licences"
msgid "ciReport|Manage licenses"
msgstr "Manage licences"
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr "is too long (%{current_value}). The maximum size is %{max_size}."
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr "Merge blocked: All required approvals must be given."
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/eo/gitlab.po b/locale/eo/gitlab.po
index ae440d85a4c..6075d6b3320 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-08-12 18:09\n"
+"PO-Revision-Date: 2022-09-11 06:25\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "Branĉoj"
@@ -6855,6 +6914,9 @@ msgstr "Elekti dosierojn"
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr "La refreÅigo komenciÄis sukcese"
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr "Ne estas deponejo"
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr "Sciigaj eventoj"
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "h"
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] "min"
msgstr[1] "min"
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr "s"
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/es/gitlab.po b/locale/es/gitlab.po
index 9058121a8ad..4b43727c308 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-08-12 18:04\n"
+"PO-Revision-Date: 2022-09-11 06:22\n"
msgid " %{start} to %{end}"
msgstr " %{start} hasta %{end}"
@@ -22,6 +22,11 @@ msgstr " %{start} hasta %{end}"
msgid " (from %{timeoutSource})"
msgstr " (desde %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr " Capturado el %{time}"
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] "%d aprobador (has aprobado)"
msgstr[1] "%d aprobadores (has aprobado)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] "%d colaboración"
msgstr[1] "%d colaboraciones"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d día"
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] "%d proyecto seleccionado"
msgstr[1] "%d proyectos seleccionados"
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] "%d restante"
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] "%d etiqueta por nombre de imagen"
msgstr[1] "%d etiquetas por nombre de imagen"
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] "%d incidencia sin asignar"
-msgstr[1] "%d incidencias sin asignar"
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] "%d hilo sin resolver"
@@ -460,9 +475,6 @@ msgstr "%{actionText} y reabrir %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr "%{address} no es en un rango de direcciones IP válido"
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr "%{attribute} debe estar entre %{min} y %{max}"
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr "%{author_link} ha clonado %{original_issue} a %{new_issue}."
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr "%{percentageUsed}%% usado"
@@ -1003,6 +1009,9 @@ msgstr "%{size} bytes"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr "%{sourceBranch} en %{targetBranch}"
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr "%{spammable_titlecase} se envió correctamente a Akismet."
@@ -1023,6 +1032,11 @@ msgstr[1] "%{strongOpen}%{errors}%{strongClose} puntos"
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] "%{strong_start}%{release_count}%{strong_end} Versión"
@@ -1264,11 +1275,6 @@ msgstr "(eliminado)"
msgid "(revoked)"
msgstr "(revocado)"
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr "(este usuario)"
@@ -1602,6 +1608,12 @@ msgstr "Una plantilla básica para desarrollar programas de Linux usando Kotlin
msgid "A complete DevOps platform"
msgstr "Una plataforma DevOps completa"
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr "No se puede elegir una rama por defecto para un proyecto vacío."
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr "Una tarea épica no confidencial no se puede asignar a una tarea épica padre confidencial"
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr "folder/openapi.json"
msgid "AWS Access Key"
msgstr "AWS Access Key"
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr "AWS Secret Access Key"
@@ -2148,7 +2169,7 @@ msgstr "Añada un comentario a esta línea o arrastre para varias líneas"
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr "Añadir tabla"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr "Añadir un título..."
@@ -2214,6 +2238,9 @@ msgstr "Añadir comentario ahora"
msgid "Add comment to design"
msgstr "Añadir comentario al diseño"
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr "Añadir clave"
msgid "Add label(s)"
msgstr "Añadir etiqueta(s)"
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr "Añadir lista"
@@ -2436,6 +2469,9 @@ msgstr "Agrega %{labels} %{label_text}."
msgid "Adds a Zoom meeting."
msgstr "Añadir una reunión de Zoom."
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr "Añade una tarea pendiente."
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr "Ajusta la frecuencia con la que la interfaz de usuario de GitLab busca actualizaciones."
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr "Ajuste sus filtros/criterios de búsqueda arriba. Si cree que puede tratarse de un error, consulte la documentación %{linkStart}Geo Troubleshooting%{linkEnd} para obtener más información."
-
msgid "Admin"
msgstr "Administrador"
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr "Dominio Auto DevOps"
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr "Configurar Let's Encrypt"
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr "Habilitado"
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr "¡Es usted!"
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr "Permitir a los usuarios registrar cualquier aplicación para usar GitLab
msgid "Allowed"
msgstr "Permitido"
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr "Caracteres permitidos: +, 0-9, - y espacios."
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr "Sólo los grupos de nivel superior pueden restringir los dominios de correo electrónico permitidos"
@@ -3975,6 +4014,9 @@ msgstr "Una aplicación llamada %{link_to_client} está solicitando el acceso a
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr "Se agregará a un campo de usuario de Gitlab el nombre completo del usuario de FogBugz (por ejemplo, \"John Smith\") en la descripción de todos los errores y los comentarios. También se asociarán o asignarán estos errores y comentarios al creador del proyecto."
@@ -4026,6 +4068,9 @@ msgstr "Se ha producido un error al añadir los aprobadores."
msgid "An error occurred while adding formatted title for epic"
msgstr "Se ha producido un error al añadir el título formateado para la tarea épica"
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr "Se ha producido un error al autorizar su rol"
@@ -4164,6 +4209,9 @@ msgstr "Se ha producido un error al cargar una sección de esta página."
msgid "An error occurred while loading all the files."
msgstr "Se ha producido un error al cargar todos los archivos."
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr "Se ha producido un error al cargar los datos del gráfico."
@@ -4301,9 +4349,6 @@ msgstr[1] "Se han producido un error al guardar los ajustes."
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr "Se ha producido un error al suscribirse a las notificaciones."
-
msgid "An error occurred while triggering the job."
msgstr "Se ha producido un error al ejecutar el trabajo."
@@ -4313,15 +4358,15 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
+msgid "An error occurred while trying to render the content editor. Please try again."
+msgstr ""
+
msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
-msgstr "Se ha producido un error al cancelar la suscripción a las notificaciones."
-
msgid "An error occurred while updating approvers"
msgstr "Se ha producido un error al actualizar los aprobadores"
@@ -4864,6 +4909,9 @@ msgstr "Aprobar"
msgid "Approve a merge request"
msgstr "Aprobar el merge request"
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr "Aprobar el merge request actual."
@@ -5329,6 +5377,11 @@ msgstr "Este mes"
msgid "AuditLogs|User Events"
msgstr "Eventos de usuario"
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr "AuditStreams|Ya existe un cabecera con este nombre."
@@ -5344,10 +5397,16 @@ msgstr "Agregar un valor personalizado"
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,8 +5424,8 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr "Cancelar edición"
-msgid "AuditStreams|Custom HTTP headers"
-msgstr "AuditStreams|Cabeceras HTTP personalizadas"
+msgid "AuditStreams|Custom HTTP headers (optional)"
+msgstr ""
msgid "AuditStreams|Delete %{link}"
msgstr "Eliminar %{link}"
@@ -5386,6 +5445,9 @@ msgstr "AuditStreams|Cabecera"
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr "Se alcanzó el número máximo de %{number} encabezados HTTP."
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr "No se pueden habilitar los ejecutores compartidos hasta que se registre una tarjeta de crédito válida."
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr "Validar la cuenta"
msgid "Billings|Validate user account"
msgstr "Validar la cuenta de usuario"
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr "Explorar todos los planes"
msgid "Billing|Export list"
msgstr "Exportar lista"
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr "A partir del 19 de octubre de 2022, los grupos gratuitos estarán limitados a 5 miembros"
-
msgid "Billing|Group invite"
msgstr "Invitación de grupo"
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr "Está a punto de eliminar al usuario %{username} de su suscripción. Si continua, el usuario será eliminado del grupo %{namespace} y todos sus subgrupos y proyectos. Esta acción no se puede deshacer."
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr "Incidencia bloqueada"
msgid "Blocking"
msgstr "Bloqueando"
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr "Incidencias bloqueantes"
@@ -6564,6 +6608,15 @@ msgstr "Expandir"
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr "Nuevo tablero"
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "Ramas"
@@ -6855,6 +6914,9 @@ msgstr "Examinar archivos"
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr "Se ha producido un error mientras al recuperar los artefactos"
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr "Uso de los ejecutores compartidos"
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr "Uso compartido del ejecutor"
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr "El uso compartido del ejecutor es el tiempo de ejecución total de todos los trabajos que se ejecutaron en ejecutores compartidos"
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr "Tiempo para restaurar el servicio"
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-msgstr "¿Qué es el uso compartido del ejecutor?"
-
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 ""
@@ -8387,6 +8437,12 @@ msgstr "Cierra este %{quick_action_target}."
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr "CloudSeed|Cloud SQL para Postgres"
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr "CloudSeed|Cloud SQL para SQL Server"
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr "CloudSeed|Instancia de CloudSQL"
msgid "CloudSeed|Configuration"
msgstr "Configuración"
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr "CloudSeed|Crear clúster"
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr "CloudSeed|Google Cloud Project"
@@ -9160,10 +9231,10 @@ msgstr "El certificado Kubernetes utilizado para autenticarse en el clúster."
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr "La URL utilizada para acceder a la API de Kubernetes."
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr "Contraer las incidencias"
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr "Contraer hitos"
@@ -9400,6 +9474,9 @@ msgstr "Comentar y dejar el hilo sin resolver"
msgid "Comment '%{label}' position"
msgstr "Comentario '%{label}' en la posición"
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr "Posición del formulario de comentarios"
@@ -9522,6 +9599,9 @@ msgstr "Compare las ediciones de GitLab"
msgid "Compare Revisions"
msgstr "Comparar revisiones"
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr "Comparar cambios"
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr "Error de conexión de Docker"
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr "Cree su grupo"
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr "Cree/importe su primer proyecto"
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr "No tiene permiso para crear un subgrupo en este grupo."
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ msgstr "Deshabilitado"
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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr "Mensajes de depuración"
msgid "DastProfiles|Delete profile"
msgstr "Eliminar perfil"
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr "¿Desea descartar este perfil de análisis?"
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr "¿Desea descartar este perfil del sitio?"
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr "¿Desea descartar sus cambios?"
-
msgid "DastProfiles|Edit profile"
msgstr "Editar perfil"
@@ -11855,6 +11941,9 @@ msgstr "Editar perfil del sitio"
msgid "DastProfiles|Enable Authentication"
msgstr "Habilitar autenticación"
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr "Introduzca las URLs en una lista separada por comas."
@@ -11930,6 +12019,9 @@ msgstr "Contraseña"
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr "Modo de análisis"
-msgid "DastProfiles|Scanner Profile"
-msgstr "Perfil de escaneo"
-
-msgid "DastProfiles|Scanner Profiles"
-msgstr "Perfiles de escaneo"
-
msgid "DastProfiles|Scanner name"
msgstr "Nombre del análisis"
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profile"
+msgstr ""
+
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr "Mostrar mensajes de depuración"
-msgid "DastProfiles|Site Profile"
-msgstr "Perfil del sitio"
-
-msgid "DastProfiles|Site Profiles"
-msgstr "Perfiles del sitio"
-
msgid "DastProfiles|Site name"
msgstr "Nombre del sitio"
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profile"
+msgstr ""
+
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr "Días"
msgid "Days to merge"
msgstr "Días para hacer merge"
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr "Eliminar el archivo"
@@ -12496,6 +12585,9 @@ msgstr "¿Eliminar fragmento de código?"
msgid "Delete source branch"
msgstr "Eliminar rama origen"
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr "Eliminar suscripción"
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr "Diseños"
msgid "DesignManagement|Discard comment"
msgstr "Descartar comentario"
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr "Descargar diseño"
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr "Adopción DevOps"
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr "Dispositivos (opcional)"
@@ -13936,6 +14040,9 @@ msgstr "Borrador"
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr "Fecha de vencimiento"
@@ -14194,6 +14313,9 @@ msgstr "Editar página wiki"
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr "Edite su comentario más reciente en un hilo (desde un cuadro de texto vacío)"
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr "Editando"
msgid "Elapsed time"
msgstr "Tiempo transcurrido"
-msgid "Elasticsearch AWS IAM credentials"
-msgstr "Credenciales IAM de AWS Elasticsearch"
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr "Región de Elasticsearch."
-
msgid "Elastic|None. Select namespaces to index."
msgstr "Ninguno. Seleccione los espacios de nombres a indexar."
@@ -14386,6 +14502,9 @@ msgstr "Activar Auto DevOps"
msgid "Enable GitLab Error Tracking"
msgstr "Habilitar el seguimiento de errores de GitLab"
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr "Habilitar Gitpod"
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr "Habilitar el encabezado y pie de página en los correos electrónicos"
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr "Habilitar correos electrónicos de marketing en el producto"
@@ -14914,6 +15030,9 @@ msgstr "Épica"
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr "No es posible encontrar la tarea épica."
@@ -14950,12 +15069,6 @@ msgstr "Añadir una nueva tarea épica"
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr "Se ha producido un error mientras se guardaba la fecha tipo %{epicDateType}"
-
-msgid "Epics|An error occurred while updating labels."
-msgstr "Se ha producido un error al actualizar las etiquetas."
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr "¿Está seguro de que desea eliminar %{bStart}%{targetIssueTitle}%{bEnd} de %{bStart}%{parentEpicTitle}%{bEnd}?"
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr "Al hacer esto también eliminará cualquier descendiente de %{bStart}%{targetEpicTitle}%{bEnd} desde %{bStart}%{parentEpicTitle}%{bEnd}. ¿Está seguro de que desea continuar?"
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr "Para programar la fecha %{epicDateType} de su épica basada en hitos, asigne un hito con una fecha %{epicDateType} a cualquier incidencia de la épica."
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr "vencimiento"
-
-msgid "Epics|start"
-msgstr "Inicio"
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr "Error al cargar ramas."
msgid "Error loading burndown chart data"
msgstr "Se ha producido un error al cargar los datos del gráfico de burndown"
-msgid "Error loading countries data."
-msgstr "Se ha producido un error al cargar los datos de los países."
-
msgid "Error loading file viewer."
msgstr "Se ha producido un error al cargar el visor de archivos."
@@ -15181,6 +15285,9 @@ msgstr "Se ha producido un error. El usuario no estaba desbloqueado"
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr "Expandir incidencias"
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr "Expandir hitos"
@@ -16362,9 +16472,6 @@ msgstr "Feb"
msgid "February"
msgstr "Febrero"
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr "Arreglado:"
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr "Público"
msgid "ForkProject|Select a namespace"
msgstr "Seleccione un espacio de nombres"
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr "Frecuencia"
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr "Viernes"
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr "De %{providerTitle}"
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr "Añadir sitio"
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr "Todo"
@@ -17158,6 +17263,12 @@ msgstr "Nunca"
msgid "Geo|Next sync scheduled at"
msgstr "Próxima sincronización programada en"
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr "No hay %{replicable_type} para mostrar"
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr "Transferencia de Git en progreso"
msgid "Git version"
msgstr "Versión de Git"
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr "Usuarios excluidos"
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr "Accesos directos globales"
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr "Configuración global de las notificaciones"
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr "Grupos"
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr "Incidencias que he creado"
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr "Proyectos"
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr "Se están cargando los resultados de la búsqueda"
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr "en %{scope}"
msgid "GlobalSearch|project"
msgstr "proyecto"
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr "Gravatar habilitado"
msgid "Group"
msgstr "Grupo"
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr "El grupo %{group_name} se actualizó correctamente."
-
msgid "Group %{group_name} couldn't be exported."
msgstr "No se puede exportar el grupo %{group_name} ."
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr "El pipeline Auto DevOps se actualizó para el grupo"
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr "Plantillas de proyecto personalizadas"
@@ -19066,14 +19207,17 @@ msgstr "Guía"
msgid "HAR (HTTP Archive)"
msgstr "HAR (archivo HTTP)"
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr "Ruta del archivo HAR o URL"
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
-msgstr "HTTP básico: Acceso denegado\\nDebe utilizar un token de acceso personal con alcance 'api' para Git a través de HTTP.\\nPuedes generar uno a %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
+msgstr ""
msgid "Harbor Registry"
msgstr ""
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] "Ocultar gráfico"
msgstr[1] "Ocultar gráficos"
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr "Ocultar los comentarios en este archivo"
@@ -19455,9 +19599,6 @@ msgstr "Servicio de limpieza iniciado con éxito"
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr "¿Cómo configuro los ejecutores?"
-
msgid "How do I configure this integration?"
msgstr "¿Cómo configuro esta integración?"
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr "Aceptar los %{terms_link}"
@@ -19614,9 +19758,15 @@ msgstr "Antes de iniciar sesión, necesitamos verificar su identidad. Introduzca
msgid "IdentityVerification|Create a project"
msgstr "Crear un proyecto"
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr "Ayúdenos a proteger su cuenta"
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr "Si ha perdido el acceso al correo electrónico asociado a esta cuenta o tiene problemas con el código, %{link_start}aquí se indican algunos pasos que puede dar.%{link_end}"
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr "Se han superado el número máximo de intentos de inicio de sesión. Espere %{interval} y vuelva a intentarlo."
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr "Por favor, introduzca un código válido"
msgid "IdentityVerification|Resend code"
msgstr "IdentityVerification|Reenviar código"
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr "IdentityVerification|El código ha caducado. Vuelve a enviar un nuevo código e inténtalo de nuevo."
@@ -19659,6 +19830,9 @@ msgstr "Verifique su identidad"
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr "Ha alcanzado la cantidad máxima de intentos. Espere %{interval} o reenvíe un nuevo código e inténtelo de nuevo."
@@ -19728,9 +19902,6 @@ msgstr "Si utiliza GitHub, verá los estados del pipeline en GitHub para sus com
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr "Se ha producido un error al crear el evento de la linea de tiempo del in
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr "Se ha producido un error al eliminar el evento de la línea de tiempo del incidente: %{error}"
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr "Métricas"
@@ -20799,6 +20970,9 @@ msgstr "Se ha producido un error al eliminar el evento de la línea de tiempo de
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr "Resumen"
@@ -20832,8 +21006,8 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr "En caso de que sea necesario incluya el nombre de usuario en la URL: %{code_open}https://nombredeusuario@gitlab.empresa.com/grupo/proyecto.git%{code_close}."
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
-msgstr "Incluye objetos LFS. Se puede sobreescribir a nivel de grupo o de proyecto. Puede establecerse a 0 para hacer que este valor sea ilimitado."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
+msgstr ""
msgid "Includes an MVC structure to help you get started"
msgstr ""
@@ -20973,9 +21147,15 @@ msgstr "Insertar fila antes"
msgid "Insert suggestion"
msgstr "Insertar sugerencia"
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
msgstr "Insights"
+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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ 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"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr "Revise los errores de invitación y vuelva a intentarlo:"
@@ -21572,6 +21758,12 @@ msgstr "Seleccione un rol"
msgid "InviteMembersModal|Select members or type email addresses"
msgstr "Seleccione miembros o escriba las direcciones de correo electrónico"
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr "Algo salió mal"
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr "Está usando el asiento de licencia:"
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr "Cerrada"
msgid "IssuableStatus|Closed (%{link})"
msgstr "Cerrada (%{link})"
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr "duplicada"
@@ -22060,9 +22252,6 @@ msgstr "La configuración de la cadencia no es válida."
msgid "Iterations|Cadence name"
msgstr "Nombre de la cadencia"
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr "Título"
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr "¡El trabajo se ha borrado correctamente!"
msgid "Job has wrong arguments format."
msgstr "El trabajo tiene el formato de argumentos erróneo."
-msgid "Job is missing the `model_type` argument."
-msgstr "El trabajo no tiene definido el argumento `model_type`."
-
msgid "Job is stuck. Check runners."
msgstr "El trabajo está bloqueado. Por favor, compruebe los ejecutores."
@@ -22947,8 +23121,8 @@ msgstr "Las etiquetas se pueden aplicar a %{features}. Las etiquetas de los grup
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."
-msgstr "Las etiquetas se pueden aplicar a las incidencias y a los merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
+msgstr ""
msgid "Labels with no issues in this iteration:"
msgstr "Etiquetas sin incidencias en esta iteración:"
@@ -22962,6 +23136,9 @@ msgstr "Promocionar etiqueta"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr "Idioma"
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr "Aprenda GitLab"
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr "Obtener mas información"
@@ -23186,6 +23360,9 @@ msgstr "Obtenga más información sobre las plantillas de proyectos a nivel de g
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr "Modo mantenimiento"
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr "Merge requests"
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr "Los merge request son un lugar para proponer los cambios que ha realizado en un proyecto y discutir esos cambios con otros miembros"
@@ -24780,6 +24960,9 @@ msgstr "Se ha producido un error al guardar el borrador del comentario."
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr "Se ha producido un error al guardar el comentario"
@@ -24855,6 +25038,30 @@ msgstr "No se encontraron archivos"
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr "Fusionado"
@@ -25531,16 +25738,19 @@ msgstr "Modificar los mensajes de commit"
msgid "Modify merge commit"
msgstr "Modificar el merge commit"
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr "Lunes"
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr "Mes"
msgid "Months"
msgstr "Meses"
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr "Más estrellas"
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr "No es posible encontrar el punto de montaje %{mounted_as} en %{model_class}."
-
msgid "Move"
msgstr "Mover"
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr "Se encontraron varios tipos de modelo: %{model_types}"
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr "Se encontraron varios tipos de cargadores: %{uploader_types}"
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr "No se necesita tarjeta de crédito."
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr "No se han encontrado datos"
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr "No se han encontrado despliegues"
-msgid "No due date"
-msgstr "Sin fecha límite"
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr "No hay repositorio"
msgid "No results"
msgstr "No hay resultados"
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr "No hay ningún favorito que coincida con su búsqueda"
-msgid "No start date"
-msgstr "Sin fecha de inicio"
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr "Nada para previsualizar."
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr "Eventos de notificación"
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr "%{commit_link} en %{mr_link}"
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr "Hito cambiado a %{milestone}"
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr "Eliminar perfil"
msgid "OnDemandScans|Description (optional)"
msgstr "Descripción (opcional)"
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr "Ver resultados"
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr "Abierto: %{open}"
msgid "OpenAPI"
msgstr "OpenAPI"
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr "URL o ruta del archivo de especificación de OpenAPI"
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr "Abierto"
@@ -27643,6 +27919,9 @@ msgstr "Registro de paquetes: solicitudes del API no autenticadas"
msgid "Package already exists"
msgstr "El paquete ya existe"
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr "Paquete eliminado correctamente"
@@ -27679,6 +27958,9 @@ msgstr "El tipo de paquete debe ser NuGet"
msgid "Package type must be PyPi"
msgstr "El tipo de paquete debe ser PyPi"
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr "El tipo de paquete debe ser RubyGems"
@@ -27703,6 +27985,9 @@ msgstr "Añadir registro de Composer"
msgid "PackageRegistry|Additional metadata"
msgstr "PackageRegistry|Metadatos adicionales"
+msgid "PackageRegistry|Allow duplicates"
+msgstr "Permitir duplicados"
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr "Paquete eliminado correctamente"
msgid "PackageRegistry|Package file deleted successfully"
msgstr "Archivo de paquete eliminado correctamente"
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] "El paquete tiene %{updatesCount} actualizacion archivada"
@@ -27953,9 +28241,6 @@ msgstr "Receta: %{recipe}"
msgid "PackageRegistry|Registry setup"
msgstr "Configuración del registro"
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr "Eliminar paquete"
@@ -27965,12 +28250,6 @@ msgstr "Python requerido: %{pythonVersion}"
msgid "PackageRegistry|RubyGems"
msgstr "RubyGems"
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr "Configuración para paquetes genéricos"
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr "Configuración para paquetes de Maven"
-
msgid "PackageRegistry|Show Composer commands"
msgstr "Mostrar comandos de Composer"
@@ -28072,8 +28351,8 @@ msgstr "npm"
msgid "PackageRegistry|published by %{author}"
msgstr "publicado por %{author}"
-msgid "Packages & Registries"
-msgstr "Paquetes y registros"
+msgid "Packages and registries"
+msgstr ""
msgid "Page not found"
msgstr "Página no encontrada"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr "Realice operaciones comunes en el proyecto GitLab"
-msgid "Performance insights"
-msgstr "Performance insights"
-
msgid "Performance optimization"
msgstr "Optimización del rendimiento"
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr "Período en segundos"
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr "Enlace permanente"
@@ -29104,27 +29386,18 @@ msgstr ""
msgid "Pipeline|Date"
msgstr "Fecha"
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr "Fallido"
-msgid "Pipeline|Five slowest jobs"
-msgstr "Los cinco trabajos más lentos"
-
msgid "Pipeline|In progress"
msgstr "En progreso"
-msgid "Pipeline|Last executed job"
-msgstr "Último trabajo ejecutado"
-
-msgid "Pipeline|Longest queued job"
-msgstr "El trabajo más largo en cola"
-
msgid "Pipeline|Manual"
msgstr "Manual"
+msgid "Pipeline|Merge request pipeline"
+msgstr ""
+
msgid "Pipeline|Merge train pipeline"
msgstr ""
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr "Sólo se pueden mostrar los 100 primeros resultados"
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr "Pendiente"
-msgid "Pipeline|Performance insights"
-msgstr "Performance insights"
-
msgid "Pipeline|Pipeline"
msgstr "Pipeline"
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr "El último trabajo ejecutado es el último trabajo que se inicia en el pipeline."
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr "El trabajo más largo en cola es el que más tiempo pasó en el estado pendiente, esperando a ser elegido por un ejecutor de Gitlab"
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr "Variables"
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr "Ver dependencia"
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr "Por favor revise su correo electrónico (%{email}) para verificar que es el propietario de esta dirección y desbloquear la potencia de CI/CD. ¿No lo ha recibido? %{resend_link}. ¿Ha utilizado una dirección de correo electrónico incorrecta? %{update_link}."
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr "Puerto"
msgid "Postman collection"
msgstr "Colección de Postman"
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr "Ruta o URL del archivo de colección de Postman"
@@ -29578,12 +29839,12 @@ msgstr "Comportamiento"
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr "Elija entre el diseño de aplicación fijo (máximo 1280px) y fluido (%{percentage})."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
+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 what content you want to see on your homepage."
-msgstr "Seleccione qué contenido desea ver en su página de inicio."
-
msgid "Preferences|Color for added lines"
msgstr ""
@@ -29605,6 +29866,9 @@ msgstr "Personalizar el color de GitLab."
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr "Por ejemplo: hace 30 minutos."
msgid "Preferences|Gitpod"
msgstr "Gitpod"
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr "Clave"
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr "Dirección de correo electrónico de notificación"
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr "Organización"
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr "Todos"
@@ -30790,6 +31057,9 @@ msgstr "Fast-forward merge"
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr "Requisitos"
msgid "ProjectSettings|Requirements management system."
msgstr "Sistema de gestión de requisitos."
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr "Por defecto"
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr "%{environment_name} podrá modificarse por los desarrolladores. ¿Estás seguro de que desea continuar?"
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr "Eventos Push"
msgid "Push project from command line"
msgstr "Hacer push al proyecto desde línea de comando"
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr "Rango rápido"
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr "Registrarse con la aplicación de dos factores"
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr "Runners|Empezar con los ejecutores"
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr "Obsoleto"
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr "Ha utilizado %{quotaUsed} de %{quotaLimit} de sus minutos de ejecución
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr "disponible"
-
msgid "Runners|group"
msgstr "grupo"
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr "recomendado"
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr "Runners|actualización disponible"
-
-msgid "Runners|upgrade recommended"
-msgstr "Runners|actualización recomendada"
-
msgid "Runner|Owner"
msgstr "Propietario"
@@ -34248,6 +34509,9 @@ msgstr "Verificación SSL:"
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr "Guardando"
msgid "Saving project."
msgstr "Guardar proyecto."
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr "Este %{namespaceType} no contiene ninguna política de seguridad."
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr "URL de la página de cierre de sesión"
msgid "Sign-up restrictions"
msgstr "Restricciones de registro"
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,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 while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr "Se ha producido un error al añadir su premio. Por favor, inténtalo de nuevo."
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr "Se ha producido un error al reabrir un requisito."
@@ -37280,6 +37592,9 @@ msgstr "Modificar el mensaje de commit"
msgid "Squash commits"
msgstr "Squash commits"
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr "Traza de la pila"
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr "Estándar"
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr "Resalte una etiqueta para convertirla en una etiqueta con prioridad. Ordene las etiquetas priorizadas para cambiar su prioridad relativa, arrastrando."
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr "Resumen / nota"
msgid "Summary comment (optional)"
msgstr "Comentario de resumen (opcional)"
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr "Domingo"
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr "La contraseña para el servidor de Jenkins."
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr "La pestaña especificada no es válida, por favor seleccione otra"
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,11 +39778,11 @@ msgstr "Tema"
msgid "There are currently no events."
msgstr "Actualmente no hay eventos."
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
-msgstr "No hay %{replicableTypeName} para mostrar"
+msgid "There are merge conflicts"
+msgstr ""
msgid "There are no GPG keys associated with this account."
msgstr "No hay claves GPG asociadas con esta cuenta."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr "Ya hay un repositorio con ese nombre en el disco"
@@ -39913,6 +40237,9 @@ msgstr "Este bloque es auto-referencial"
msgid "This board's scope is reduced"
msgstr "El alcance de este tablero es limitado"
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr "No se puede mostrar este gráfico"
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr "Este commit fue firmado con la firma verificada de un usuario diferente."
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr "Este proyecto será eliminado el %{date}"
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr "Este proyecto se eliminará el %{date} , ya que se programó la eliminación de su grupo principal '%{parent_group_name}'."
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr "Jueves"
@@ -40645,6 +40975,9 @@ msgstr "solo ahora"
msgid "Timeago|right now"
msgstr "justo ahora"
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr "Zona horaria"
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "hr"
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] "min"
msgstr[1] "mins"
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr "s"
@@ -40736,6 +41093,9 @@ 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."
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr "Para ampliar la búsqueda, cambie o elimine filtros"
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr "Lista de tareas pendientes"
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr "Demasiadas referencias. Las acciones rápidas están limitadas a un máx
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr "Se encontraron demasiados usuarios. Las acciones rápidas están limitadas a un máximo de %{max_count} usuarios"
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr "Total"
@@ -41240,39 +41633,12 @@ msgstr "Su prueba termina el %{boldStart}%{trialEndDate}%{boldEnd}. Esperamos qu
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr "Nombre de la empresa"
-
msgid "Trial|Continue"
msgstr "Continuar"
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr "Continúe utilizando las funciones básicas de GitLab de forma gratuita."
-
-msgid "Trial|Country"
-msgstr "País"
-
msgid "Trial|Dismiss"
msgstr "Descartar"
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr "GitLab Ultimate (opcional)"
-
-msgid "Trial|Number of employees"
-msgstr "Número de empleados"
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr "Número de teléfono"
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr "Actualicese a Ultimate para seguir utilizando GitLab con funciones avanzadas."
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr "Intente comunicarse con tu dispositivo. Enchúfelo (si es necesario) y p
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr "Intentando comunicarse con su dispositivo. Conéctelo (si aún no lo ha hecho) y presione el botón en el dispositivo ahora."
+msgid "Tue"
+msgstr ""
+
msgid "Tuesday"
msgstr "Martes"
msgid "Turn off"
msgstr "Desactivar"
-msgid "Turn off notifications"
-msgstr ""
-
msgid "Turn on"
msgstr "Activar"
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr "Twitter"
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr "Utilice una línea por URI"
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,11 +43393,14 @@ msgstr "Ver los detalles de la alerta."
msgid "View all environments."
msgstr "Ver todos los entornos."
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr "Ver todas las incidencias"
-msgid "View all personal projects"
-msgstr "Ver todos sus proyectos personales"
+msgid "View all projects"
+msgstr ""
msgid "View blame"
msgstr ""
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr "Error al conectar"
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr "Eventos de las incidencias"
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr "Sitio web"
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr "Miércoles"
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr "¿Qué hace este comando?"
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr "¿Qué es Markdown?"
@@ -44180,9 +44576,6 @@ msgstr "¿Desea eliminar la página %{pageTitle}?"
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr "Cancelar"
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr "Título de la página"
-msgid "WikiPage|Retry"
-msgstr "Reintentar"
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,11 +44687,11 @@ msgstr "Trabajo en curso (abierto y sin asignar)"
msgid "Work in progress Limit"
msgstr "Límite de trabajo en progreso"
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
-msgstr "Una tarea proporciona la capacidad de dividir su trabajo en piezas más pequeñas ligadas a una incidencia. Las tareas son los primeros elementos que utilizan nuestros nuevos objetos %{workItemsLink} . Pronto habrá más tipos de objetos de trabajo."
+msgid "WorkItem|%{workItemType} deleted"
+msgstr ""
-msgid "WorkItem|Add a task"
-msgstr "Añadir una tarea"
+msgid "WorkItem|Add"
+msgstr ""
msgid "WorkItem|Add a title"
msgstr ""
@@ -44318,8 +44708,8 @@ msgstr "Añadir tarea"
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr "¿Está seguro que desea cancelar la edición?"
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
-msgstr "¿Está seguro que desea eliminar esta tarea? Esta acción no se puede deshacer."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
+msgstr ""
msgid "WorkItem|Assignee"
msgid_plural "WorkItem|Assignees"
@@ -44329,16 +44719,13 @@ msgstr[1] "Asignados"
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr "Elemento hijo eliminado"
msgid "WorkItem|Closed"
msgstr "Cerrado"
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr "Crear tarea"
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
-msgstr "Eliminar tarea"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
+msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr "Introducción de tareas"
-msgid "WorkItem|Learn about tasks"
-msgstr "Más información sobre las tareas"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
+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|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr "Abrir"
msgid "WorkItem|Remove"
msgstr "Eliminar"
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
-msgstr "Se ha producido un error al actualizar el crear una tarea. Por favor, inténtelo de nuevo"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
+msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr "Se ha producido un error aleliminar una tarea. Por favor, inténtelo de nuevo."
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,32 +44797,41 @@ msgstr "Se ha producido un error al intentar añadir un trabajo hijo. Por favor,
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr "Tarea eliminada"
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr "Deshacer"
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
-msgstr "Elementos de trabajo"
+msgid "WorkItem|Work item"
+msgstr ""
msgid "Would you like to create a new branch?"
msgstr ""
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr "No es posible crear un nuevo disparador."
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr "Aún no tiene ninguna suscripción"
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr "Es necesario subir un archivo de exportación de proyecto de GitLab (en
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr "Ha rechazado con éxito la invitación"
@@ -45156,9 +45565,6 @@ msgstr ""
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 "Ya ha habilitado la autenticación de dos pasos utilizando una contraseña de un solo uso. Para registrar un dispositivo diferente, primero debe desactivar la autenticación de dos factores."
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr "Ha rechazado a %{user}"
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr "¡El dispositivo se ha configurado correctamente!. Por favor, asígnele
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr "Su primer proyecto"
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr "Su token para restablecer la contraseña ha caducado."
@@ -45427,8 +45839,8 @@ msgstr ""
msgid "Your profile"
msgstr "Su perfil"
-msgid "Your project has limited quotas and features"
-msgstr "Su proyecto tiene cuotas y características limitadas"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
+msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
msgstr "¡Su límite de proyecto es %{limit} proyectos! Por favor, contacte con su administrador para aumentarlo"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr "asignar a ti mismo"
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr "en"
@@ -45732,6 +46141,9 @@ msgstr "no se puede modificar si un proyecto personal ya contiene etiquetas de r
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,8 +46236,8 @@ msgstr "%{reportType}: La carga finalizó con errores"
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
-msgstr ""
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
+msgstr "ciReport|%{scanner} detectó %{number} nuevo potencial %{vulnStr}"
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
@@ -46021,7 +46436,7 @@ msgstr "Administrar licencias"
msgid "ciReport|Manage licenses"
msgstr "Administrar licencias"
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr "dato"
@@ -46309,6 +46724,9 @@ msgstr "no se puede descartar el hallazgo asociado (id =%{finding_id}): %{messag
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr "es demasiado largo (%{current_value}). El tamaño máximo permitido es de %{max_size}."
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] "merge request"
msgstr[1] "merge requests"
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,8 +47060,8 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
-msgstr "1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
+msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
msgstr ""
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr "Descartado"
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr "la página del wiki"
diff --git a/locale/et_EE/gitlab.po b/locale/et_EE/gitlab.po
index 5ece8fd290c..1daa290c56b 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-08-12 18:03\n"
+"PO-Revision-Date: 2022-09-11 06:21\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/fa_IR/gitlab.po b/locale/fa_IR/gitlab.po
index 33d40ab8f70..bd627e90002 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-08-12 18:03\n"
+"PO-Revision-Date: 2022-09-11 06:21\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/fi_FI/gitlab.po b/locale/fi_FI/gitlab.po
index 93f5adc9513..c0ba0e81cc9 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-08-12 18:05\n"
+"PO-Revision-Date: 2022-09-11 06:22\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/fil_PH/gitlab.po b/locale/fil_PH/gitlab.po
index 9d50bc223d1..89b966c88fb 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-08-12 18:09\n"
+"PO-Revision-Date: 2022-09-11 06:25\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/fr/gitlab.po b/locale/fr/gitlab.po
index 058e600554b..609bb34fc8b 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-08-12 18:04\n"
+"PO-Revision-Date: 2022-09-11 06:22\n"
msgid " %{start} to %{end}"
msgstr " %{start} à %{end}"
@@ -22,6 +22,11 @@ msgstr " %{start} à %{end}"
msgid " (from %{timeoutSource})"
msgstr " (depuis %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr " Collecté %{time}"
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] "%d approbateur (vous avez approuvé)"
msgstr[1] "%d approbateurs (vous avez approuvé)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] "%d ticket assigné"
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] "%d contribution"
msgstr[1] "%d contributions"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d jour"
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] "%d projet sélectionné"
msgstr[1] "%d projets sélectionnés"
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] "%d restant"
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] "%d étiquette par nom d'image"
msgstr[1] "%d étiquettes par nom d'image"
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] "%d ticket non assigné"
-msgstr[1] "%d tickets non assignés"
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] "%d fil de conversation non résolu"
@@ -460,9 +475,6 @@ msgstr "%{actionText} et réouvrir %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr "%{address} est une plage d'adresses IP non valide"
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr "%{author_link} a cloné %{original_issue} vers %{new_issue}."
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr "%{percentageUsed} %% utilisé(s)"
@@ -1003,6 +1009,9 @@ msgstr "%{size} octets"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr "Une branche par défaut ne peut pas être choisie pour un projet vide."
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr "Une page avec ce titre existe déjà"
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr "Ajouter un tableau"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr "Ajouter un titre..."
@@ -2214,6 +2238,9 @@ msgstr "Ajouter un commentaire"
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr "Ajouter des étiquettes"
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr "Ajoute %{labels} %{label_text}."
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr "Domaine de DevOps automatique"
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr "Une application appelée %{link_to_client} demande l’accès à votre c
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr "Un champ utilisateur Gitlab vide ajoutera le nom complet de l’utilisateur FogBugz (p. ex., « Par John Smith ») dans la description de tous les tickets et commentaires. Il associera ou assignera ces tickets et commentaires au créateur du projet."
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr "Une erreur est survenue lors du chargement des données du graphique"
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr "Une erreur est survenue lors de l’abonnement aux notifications."
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,15 +4358,15 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
+msgid "An error occurred while trying to render the content editor. Please try again."
+msgstr ""
+
msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
-msgstr "Une erreur est survenue lors du désabonnement aux notifications."
-
msgid "An error occurred while updating approvers"
msgstr ""
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5218,7 +5266,7 @@ msgid "Assigned to me"
msgstr "Assigné à moi"
msgid "Assigned to you"
-msgstr ""
+msgstr "Assigné à vous"
msgid "Assignee"
msgid_plural "%d Assignees"
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "Branches"
@@ -6855,6 +6914,9 @@ msgstr "Parcourir les fichiers"
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr "Position du formulaire de commentaire"
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr "Comparer les révisions"
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr "Modifier le profil"
@@ -11855,6 +11941,9 @@ msgstr "Modifier le profil de site"
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site name"
-msgstr ""
-
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
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}."
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr "Activer Auto DevOps"
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr "Épopée"
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr "Afin de planifier la date de %{epicDateType} de votre épopée en fonction des jalons, attribuez un jalon avec une date de %{epicDateType} à tous les tickets concernant l’épopée."
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr "Échéance"
-
-msgid "Epics|start"
-msgstr "commence"
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr "Erreur lors du chargement des branches."
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr "févr."
msgid "February"
msgstr "février"
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr "Corrigé :"
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr "Fréquemment recherché"
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr "Jamais"
msgid "Geo|Next sync scheduled at"
msgstr "Prochaine synchro programmée à"
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr "Version de Git"
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr "%{count} résultats par défaut fournis. Utilisez les touches fléchées
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr "Tickets que j'ai créés"
@@ -17926,6 +18052,15 @@ msgstr "Demandes de fusion dont je suis un relecteur"
msgid "GlobalSearch|Projects"
msgstr "Projets"
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 "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."
@@ -17938,6 +18073,9 @@ msgstr "Rechercher des projets, des tickets etc."
msgid "GlobalSearch|Search results are loading"
msgstr "Les résultats de la recherche sont en cours de chargement"
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr "Une erreur s'est produite lors de la récupération des suggestions d'autocomplétion de recherche."
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr "projet"
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr "Groupe"
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr "Le groupe « %{group_name} » a été mis à jour avec succès."
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr "Désolé, aucun résultat ne correspond à vos critères de recherche."
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ msgstr "Pour élargir votre recherche, modifiez ou supprimez les filtres ci-dess
msgid "HarborRegistry|We are having trouble connecting to the Harbor Registry. Please try refreshing the page. If this error persists, please review %{docLinkStart}the troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr "Maintenance démarrée avec succès"
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr "J’accepte les %{terms_link}"
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr "Si vous utilisez GitHub, vous verrez les statuts des pipelines sur GitHu
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr "Inviter un groupe"
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr "Fermé"
msgid "IssuableStatus|Closed (%{link})"
msgstr "Fermé (%{link})"
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr "Titre"
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,8 +23121,8 @@ msgstr "Des étiquettes peuvent être appliquées à %{features}. Les étiquette
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."
-msgstr "Les étiquettes peuvent être appliquées aux tickets et aux demandes de fusion."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
+msgstr ""
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -22962,6 +23136,9 @@ msgstr "Promouvoir l’étiquette"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr "Langue"
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23625,7 +23802,7 @@ msgid "Linked incidents or issues"
msgstr ""
msgid "Linked items"
-msgstr ""
+msgstr "Tickets liés"
msgid "LinkedIn"
msgstr "LinkedIn"
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr "Demandes de fusion"
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr "Les demandes de fusion permettent de proposer les modifications que vous avez apportées à un projet et de discuter de ces modifications avec les autres"
@@ -24780,6 +24960,9 @@ msgstr "Une erreur est survenue lors de l’enregistrement du brouillon du comme
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr "L’enregistrement du commentaire a échoué"
@@ -24855,6 +25038,30 @@ msgstr "Aucun fichier trouvé"
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr "Fusionnée"
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr "Mois"
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr "Les plus étoilés"
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr "Déplacer"
@@ -25658,7 +25868,7 @@ msgid "MrDeploymentActions|Stop environment"
msgstr ""
msgid "MrList|Assigned to %{name}"
-msgstr ""
+msgstr "Assigné à %{name}"
msgid "MrList|Review requested from %{name}"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr "Aucune carte de crédit n’est nécessaire."
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr "Aucune date d’échéance"
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr "Aucun dépôt"
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr "Rien à prévisualiser."
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr "Événement de notifications"
@@ -26730,6 +26937,9 @@ msgstr "Le ticket de %{author_link} arrive bientôt à échéance %{issue_refere
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr "%{commit_link} dans %{mr_link}"
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr "L'assignation est passée de %{fromNames} à %{toNames}"
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr "Notify|La demande de fusion %{mr_link} a été fermée par %{closed_by}"
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr "URL de la demande de fusion : %{merge_request_url}"
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr "Le jalon a été changé à %{milestone}"
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr "Nouveau ticket : %{project_issue_url}"
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr "Vous n'avez pas accès au projet."
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr "Ouvert"
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr "Optimisation des performances"
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr "Pipeline"
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr "Variables"
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr "Personnaliser les couleurs de GitLab."
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr "Personnaliser les couleurs de l'affichage des diffs."
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr "Couleurs des diff"
@@ -29626,9 +29890,6 @@ msgstr "Par exemple : il y a 30 minutes."
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr "%{environment_name} sera accessible en écriture aux développeurs. Êtesâ€vous sûr de vouloir cela ?"
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr "Événements de poussée"
msgid "Push project from command line"
msgstr "Pousser le projet en ligne de commande"
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "Restrictions d’inscription"
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr "Combiner (squash) les commits"
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr "Mettez une étoile sur une étiquette pour en faire une étiquette prioritaire. Ordonnez les étiquettes prioritaires pour changer leurs priorités relatives en les faisant glisser."
-
msgid "Star labels to start sorting by priority"
msgstr "Mettez des étoiles sur les étiquettes pour les classer par priorité"
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr "La portée de ce tableau est réduite"
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr "à l’instant"
msgid "Timeago|right now"
msgstr "immédiatement"
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "hr"
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] "min"
msgstr[1] "mins"
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr "s"
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr "Pour élargir votre recherche, modifiez ou supprimez les filtres ci-dess
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"
-msgstr ""
-
msgid "To-Do List"
msgstr "Liste de tâches"
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr "Tout marquer comme terminé"
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 !"
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr "Annuler tout marquer comme fait"
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr "Twitter"
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr "Utilisez une ligne par URI"
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr "Supprimer la page %{pageTitle} ?"
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44313,12 +44703,12 @@ msgid "WorkItem|Add assignees"
msgstr ""
msgid "WorkItem|Add task"
-msgstr ""
+msgstr "Ajouter une tâche"
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr "Fermé"
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
+msgstr ""
+
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
+msgstr ""
+
+msgid "WorkItem|Turn off confidentiality"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr "Vous n’avez souscrit à aucun abonnement pour le moment"
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr "%{reportType} : le chargement a généré une erreur"
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr "Gérer les licences"
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] "demande de fusion"
msgstr[1] "demandes de fusion"
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 66a7012190a..8654dc7f0ed 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -148,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -218,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -368,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -888,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1000,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1056,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1144,10 +1153,13 @@ msgstr ""
msgid "%{user_name} profile page"
msgstr ""
-msgid "%{username} changed the draft status of merge request %{mr_link}"
+msgid "%{username} has asked for a GitLab account on your instance %{host}:"
msgstr ""
-msgid "%{username} has asked for a GitLab account on your instance %{host}:"
+msgid "%{username} marked merge request %{mr_link} as draft"
+msgstr ""
+
+msgid "%{username} marked merge request %{mr_link} as ready"
msgstr ""
msgid "%{username}'s avatar"
@@ -1171,6 +1183,12 @@ msgstr ""
msgid "%{verb} %{time_spent_value} spent time."
msgstr ""
+msgid "%{verb} this %{noun} as a draft."
+msgstr ""
+
+msgid "%{verb} this %{noun} as ready."
+msgstr ""
+
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 ""
@@ -1599,6 +1617,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1662,6 +1686,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1830,6 +1860,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2172,6 +2205,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2211,6 +2247,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2262,6 +2301,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2433,6 +2478,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2448,9 +2496,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2649,15 +2694,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2697,7 +2742,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2721,15 +2766,24 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
msgid "AdminSettings|Git abuse rate limit"
msgstr ""
+msgid "AdminSettings|Group runners expiration"
+msgstr ""
+
msgid "AdminSettings|I have read and agree to the Let's Encrypt %{link_start}Terms of Service%{link_end} (PDF)."
msgstr ""
+msgid "AdminSettings|If no unit is written, it defaults to seconds. For example, these are all equivalent: %{oneDayInSeconds}, %{oneDayInHoursHumanReadable}, or %{oneDayHumanReadable}. Minimum value is two hours. %{linkStart}Learn more.%{linkEnd}"
+msgstr ""
+
msgid "AdminSettings|If not specified at the group or instance level, the default is %{default_initial_branch_name}. Does not affect existing repositories."
msgstr ""
@@ -2742,6 +2796,9 @@ msgstr ""
msgid "AdminSettings|Inactive project deletion"
msgstr ""
+msgid "AdminSettings|Instance runners expiration"
+msgstr ""
+
msgid "AdminSettings|Keep the latest artifacts for all jobs in the latest successful pipelines"
msgstr ""
@@ -2763,6 +2820,9 @@ msgstr ""
msgid "AdminSettings|Maximum number of active pipelines per project"
msgstr ""
+msgid "AdminSettings|Maximum number of custom domains per project"
+msgstr ""
+
msgid "AdminSettings|Maximum number of jobs in a single pipeline"
msgstr ""
@@ -2799,6 +2859,9 @@ msgstr ""
msgid "AdminSettings|Project export"
msgstr ""
+msgid "AdminSettings|Project runners expiration"
+msgstr ""
+
msgid "AdminSettings|Protect CI/CD variables by default"
msgstr ""
@@ -2850,9 +2913,21 @@ 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 ""
+
+msgid "AdminSettings|Set the expiration time of authentication tokens of newly registered instance runners. Authentication tokens are automatically reset at these intervals."
+msgstr ""
+
+msgid "AdminSettings|Set the expiration time of authentication tokens of newly registered project runners."
+msgstr ""
+
msgid "AdminSettings|Set the initial name and protections for the default branch of new repositories created in the instance."
msgstr ""
+msgid "AdminSettings|Set the maximum number of GitLab Pages custom domains per project (0 for unlimited). %{link_start}Learn more.%{link_end}"
+msgstr ""
+
msgid "AdminSettings|Set the maximum size of GitLab Pages per project (0 for unlimited). %{link_start}Learn more.%{link_end}"
msgstr ""
@@ -2886,7 +2961,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3036,6 +3114,9 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Bot"
+msgstr ""
+
msgid "AdminUsers|Can create group"
msgstr ""
@@ -3111,6 +3192,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3885,9 +3969,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -4026,6 +4107,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4301,9 +4385,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4394,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4945,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -6207,12 +6291,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6228,12 +6306,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6279,7 +6351,7 @@ msgstr ""
msgid "Billing|Enter at least three characters to search."
msgstr ""
-msgid "Billing|Explore all plans"
+msgid "Billing|Explore paid plans"
msgstr ""
msgid "Billing|Export list"
@@ -6317,6 +6389,9 @@ msgstr ""
msgid "Billing|Type %{username} to confirm"
msgstr ""
+msgid "Billing|Unlimited members during your trial"
+msgstr ""
+
msgid "Billing|User was successfully removed"
msgstr ""
@@ -6361,6 +6436,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6569,6 +6647,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6623,6 +6710,9 @@ msgstr ""
msgid "Bold text"
msgstr ""
+msgid "Bot"
+msgstr ""
+
msgid "Both SSH and HTTP(S)"
msgstr ""
@@ -6695,6 +6785,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6860,6 +6956,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7168,7 +7267,7 @@ msgstr ""
msgid "CICD|Limit CI_JOB_TOKEN access"
msgstr ""
-msgid "CICD|Select projects that can be accessed by API requests authenticated with this project's CI_JOB_TOKEN CI/CD variable."
+msgid "CICD|Select the projects that can be accessed by API requests authenticated with this project's CI_JOB_TOKEN CI/CD variable. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
msgid "CICD|The Auto DevOps pipeline runs by default in all projects with no CI/CD configuration file. %{link_start}What is Auto DevOps?%{link_end}"
@@ -7624,6 +7723,9 @@ msgstr ""
msgid "Changes to the title have not been saved"
msgstr ""
+msgid "Changes:"
+msgstr ""
+
msgid "Changing any setting here requires an application restart"
msgstr ""
@@ -8380,6 +8482,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8407,12 +8515,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8467,6 +8587,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -8617,6 +8740,9 @@ msgstr ""
msgid "ClusterAgents|Are you sure you want to revoke this token? You cannot undo this action."
msgstr ""
+msgid "ClusterAgents|CI/CD workflow with restricted access"
+msgstr ""
+
msgid "ClusterAgents|Certificate"
msgstr ""
@@ -8716,6 +8842,9 @@ msgstr ""
msgid "ClusterAgents|Install using Helm (recommended)"
msgstr ""
+msgid "ClusterAgents|Integration Status"
+msgstr ""
+
msgid "ClusterAgents|Last connected %{timeAgo}."
msgstr ""
@@ -8742,6 +8871,9 @@ msgid_plural "ClusterAgents|No activity occurred in the past %d days"
msgstr[0] ""
msgstr[1] ""
+msgid "ClusterAgents|No agent access token"
+msgstr ""
+
msgid "ClusterAgents|No agents"
msgstr ""
@@ -8751,6 +8883,9 @@ msgstr ""
msgid "ClusterAgents|Not connected"
msgstr ""
+msgid "ClusterAgents|Premium"
+msgstr ""
+
msgid "ClusterAgents|Recommended"
msgstr ""
@@ -8817,6 +8952,9 @@ msgstr ""
msgid "ClusterAgents|Unknown user"
msgstr ""
+msgid "ClusterAgents|Valid access token"
+msgstr ""
+
msgid "ClusterAgents|View all %{number} agents"
msgstr ""
@@ -9153,10 +9291,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9327,6 +9465,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9393,6 +9534,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9479,6 +9623,9 @@ msgstr ""
msgid "Commits you select appear here. Go to the first tab and select commits to add to this merge request."
msgstr ""
+msgid "Commits:"
+msgstr ""
+
msgid "Commits|An error occurred while fetching merge requests data."
msgstr ""
@@ -9515,6 +9662,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -9581,6 +9731,9 @@ msgstr ""
msgid "Complete verification to sign in."
msgstr ""
+msgid "Complete verification to sign up."
+msgstr ""
+
msgid "Completed"
msgstr ""
@@ -10092,6 +10245,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10196,6 +10352,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10370,7 +10532,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10415,13 +10577,13 @@ msgstr ""
msgid "Control emails linked to your account"
msgstr ""
-msgid "Control how the GitLab Package Registry functions."
+msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
-msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
+msgid "Control how the GitLab Package Registry functions."
msgstr ""
-msgid "Control which projects can be accessed by API requests authenticated with this project's CI_JOB_TOKEN CI/CD variable. It is a security risk to disable this feature, because unauthorized projects might attempt to retrieve an active token and access the API."
+msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
msgid "Cookie domain"
@@ -10973,9 +11135,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11519,6 +11678,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11749,6 +11911,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11827,15 +11995,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11848,6 +12007,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11923,6 +12085,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11950,16 +12115,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11980,16 +12142,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12058,13 +12217,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12231,6 +12390,9 @@ msgstr ""
msgid "Date range must be shorter than %{max_range} days."
msgstr ""
+msgid "DateRange|%{start_date}–%{end_date}"
+msgstr ""
+
msgid "Day of month"
msgstr ""
@@ -12252,7 +12414,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12417,6 +12579,9 @@ msgstr ""
msgid "Delete artifacts"
msgstr ""
+msgid "Delete asset"
+msgstr ""
+
msgid "Delete audio"
msgstr ""
@@ -12438,6 +12603,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12489,6 +12657,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -12555,6 +12726,9 @@ msgstr ""
msgid "Deleted chat nickname: %{chat_name}!"
msgstr ""
+msgid "Deleted commits:"
+msgstr ""
+
msgid "Deleted projects cannot be restored!"
msgstr ""
@@ -13047,6 +13221,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13154,18 +13334,9 @@ msgstr ""
msgid "Deprecated API rate limits"
msgstr ""
-msgid "Deprecations|Feature deprecation and removal"
-msgstr ""
-
msgid "Deprecations|For information on a possible replacement %{epicStart} learn more about Opstrace %{epicEnd}."
msgstr ""
-msgid "Deprecations|For information on a possible replacement, %{opstrace_link_start} learn more about Opstrace %{link_end}."
-msgstr ""
-
-msgid "Deprecations|The logs and tracing features were also deprecated in GitLab 14.7, and are %{removal_link_start} scheduled for removal %{link_end} in GitLab 15.0."
-msgstr ""
-
msgid "Deprecations|The metrics feature was deprecated in GitLab 14.7."
msgstr ""
@@ -13276,6 +13447,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13369,6 +13543,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13836,6 +14013,18 @@ msgstr ""
msgid "Domain Name"
msgstr ""
+msgid "Domain Verification"
+msgstr ""
+
+msgid "DomainVerification|How do I configure a domain?"
+msgstr ""
+
+msgid "DomainVerification|No domains configured. Create a domain in a project in this group hierarchy."
+msgstr ""
+
+msgid "DomainVerification|The following domains are configured for projects in this group. Users with email addresses that match a verified domain do not need to confirm their account."
+msgstr ""
+
msgid "Don't have a group?"
msgstr ""
@@ -13953,6 +14142,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13968,6 +14163,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14190,6 +14391,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14205,9 +14409,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14232,9 +14433,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14331,9 +14529,6 @@ msgstr ""
msgid "Emails"
msgstr ""
-msgid "Emails sent from Service Desk have this name."
-msgstr ""
-
msgid "Emails sent to %{email} are also supported."
msgstr ""
@@ -14382,6 +14577,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14466,9 +14664,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14910,6 +15105,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14946,12 +15144,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15018,18 +15210,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15066,6 +15249,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15099,9 +15285,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15177,6 +15360,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15608,6 +15794,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -15960,9 +16149,6 @@ msgstr ""
msgid "Failed to load deploy keys."
msgstr ""
-msgid "Failed to load emoji list."
-msgstr ""
-
msgid "Failed to load error details from Sentry."
msgstr ""
@@ -16358,9 +16544,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16571,13 +16754,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16649,6 +16832,9 @@ msgstr ""
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 ""
+msgid "For faster browsing, not all history is shown."
+msgstr ""
+
msgid "For files larger than this limit, only index the file name. The file content is neither indexed nor searchable."
msgstr ""
@@ -16727,6 +16913,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16795,6 +16984,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16812,11 +17004,6 @@ msgid_plural "From October 19, 2022, free private groups will be limited to %d m
msgstr[0] ""
msgstr[1] ""
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "From issue creation until deploy to production"
msgstr ""
@@ -16956,9 +17143,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17154,6 +17338,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17361,6 +17551,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17523,6 +17716,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17892,6 +18088,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17904,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17922,6 +18127,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17934,6 +18148,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17961,6 +18178,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18666,9 +18889,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18696,6 +18916,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19059,6 +19282,9 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
@@ -19104,17 +19330,20 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
+msgstr ""
+
+msgid "HarborRegistry|-- tags"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19126,49 +19355,46 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|To widen your search, change or remove the filters above."
msgstr ""
-msgid "HarborRegistry|To widen your search, change or remove the filters above."
+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 ""
-msgid "HarborRegistry|We are having trouble connecting to the Harbor Registry. Please try refreshing the page. If this error persists, please review %{docLinkStart}the troubleshooting documentation%{docLinkEnd}."
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
msgstr ""
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
@@ -19317,6 +19543,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19481,6 +19710,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19592,21 +19824,48 @@ msgstr ""
msgid "Identities"
msgstr ""
+msgid "IdentityVerification|A new code has been sent."
+msgstr ""
+
msgid "IdentityVerification|Before you create your first project, we need you to verify your identity with a valid payment method. You will not be charged during this step. If we ever need to charge you, we will let you know."
msgstr ""
msgid "IdentityVerification|Before you create your group, we need you to verify your identity with a valid payment method. You will not be charged during this step. If we ever need to charge you, we will let you know."
msgstr ""
+msgid "IdentityVerification|Before you finish creating your account, we need to verify your identity. On the verification page, enter the following code."
+msgstr ""
+
msgid "IdentityVerification|Before you sign in, we need to verify your identity. Enter the following code on the sign-in page."
msgstr ""
+msgid "IdentityVerification|Confirm your email address"
+msgstr ""
+
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|Didn't receive a code?"
+msgstr ""
+
+msgid "IdentityVerification|Enter a code."
+msgstr ""
+
+msgid "IdentityVerification|Enter a valid code."
+msgstr ""
+
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|GitLab will not charge or store your payment information, it will only be used for verification."
+msgstr ""
+
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19619,19 +19878,46 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
-msgid "IdentityVerification|The code has expired. Resend a new code and try again."
+msgid "IdentityVerification|Send a new code"
+msgstr ""
+
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Something went wrong. Please try again."
msgstr ""
-msgid "IdentityVerification|The code is incorrect. Enter it again, or resend a new code."
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
+msgid "IdentityVerification|The code has expired. Send a new code and try again."
+msgstr ""
+
+msgid "IdentityVerification|The code is incorrect. Enter it again, or send a new code."
msgstr ""
msgid "IdentityVerification|Verification code"
@@ -19643,16 +19929,28 @@ msgstr ""
msgid "IdentityVerification|Verify code"
msgstr ""
+msgid "IdentityVerification|Verify email address"
+msgstr ""
+
+msgid "IdentityVerification|Verify payment method"
+msgstr ""
+
msgid "IdentityVerification|Verify your identity"
msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
-msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
msgstr ""
-msgid "IdentityVerification|Your account has been successfully verified. You'll be redirected to your account in just a moment or %{redirect_url_start}click here%{redirect_url_end} to refresh."
+msgid "IdentityVerification|You've reached the maximum amount of resends. Wait %{interval} and try again."
+msgstr ""
+
+msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or send a new code and try again."
+msgstr ""
+
+msgid "IdentityVerification|Your account has been successfully verified. You'll be redirected to your account in just a moment. You can also %{redirect_url_start}refresh the page%{redirect_url_end}."
msgstr ""
msgid "IdentityVerification|Your verification code expires after %{expires_in_minutes} minutes."
@@ -19718,9 +20016,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19979,9 +20274,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20771,6 +21063,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20789,6 +21084,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20822,7 +21120,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20963,6 +21261,9 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
msgstr ""
@@ -21070,6 +21371,9 @@ msgstr ""
msgid "Integrations|Add namespace"
msgstr ""
+msgid "Integrations|Adding a namespace only works in browsers that allow cross-site cookies. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "Integrations|All details"
msgstr ""
@@ -21130,12 +21434,18 @@ msgstr ""
msgid "Integrations|Enable comments"
msgstr ""
+msgid "Integrations|Ensure your instance URL is correct and your instance is configured correctly. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "Integrations|Enter your alias"
msgstr ""
msgid "Integrations|Failed to link namespace. Please try again."
msgstr ""
+msgid "Integrations|Failed to load Jira Connect Application ID. Please try again."
+msgstr ""
+
msgid "Integrations|Failed to load namespaces. Please try again."
msgstr ""
@@ -21148,6 +21458,9 @@ msgstr ""
msgid "Integrations|Failed to unlink namespace. Please try again."
msgstr ""
+msgid "Integrations|Failed to update GitLab version. Please try again."
+msgstr ""
+
msgid "Integrations|GitLab administrators can set up integrations that all groups and projects inherit and use by default. These integrations apply to all groups and projects that don't already use custom settings. You can override custom settings for a group or project if the settings are necessary at that level. Learn more about %{integrations_link_start}instance-level integration management%{link_end}."
msgstr ""
@@ -21238,6 +21551,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21550,6 +21866,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21588,9 +21907,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21615,9 +21931,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21735,6 +22048,12 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableEvents|removed review request for"
+msgstr ""
+
+msgid "IssuableEvents|requested review from"
+msgstr ""
+
msgid "IssuableStatus|%{wi_type} created %{created_at} by "
msgstr ""
@@ -22065,9 +22384,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22113,6 +22429,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22128,12 +22447,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22191,9 +22504,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22203,9 +22513,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22218,9 +22525,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22515,9 +22819,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22967,6 +23268,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23137,9 +23441,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23155,7 +23456,7 @@ msgstr ""
msgid "Learn more"
msgstr ""
-msgid "Learn more about %{username}"
+msgid "Learn more about %{name}"
msgstr ""
msgid "Learn more about Auto DevOps"
@@ -23191,6 +23492,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23923,6 +24227,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24079,6 +24386,9 @@ msgstr ""
msgid "MarkdownToolbar|Supports %{markdownDocsLinkStart}Markdown%{markdownDocsLinkEnd}"
msgstr ""
+msgid "Marked"
+msgstr ""
+
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
@@ -24088,9 +24398,6 @@ msgstr ""
msgid "Marked as ready. Merging is now allowed."
msgstr ""
-msgid "Marked this %{noun} as a draft."
-msgstr ""
-
msgid "Marked this %{noun} as ready."
msgstr ""
@@ -24103,7 +24410,7 @@ msgstr ""
msgid "Marked to do as done."
msgstr ""
-msgid "Marks this %{noun} as a draft."
+msgid "Marks"
msgstr ""
msgid "Marks this %{noun} as ready."
@@ -24211,10 +24518,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24698,6 +25002,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24785,6 +25092,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24860,6 +25170,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25443,6 +25777,9 @@ msgstr ""
msgid "Milestones|Use milestones to track issues and merge requests over a fixed period of time"
msgstr ""
+msgid "Milestone|%{percentage}%{percent} complete"
+msgstr ""
+
msgid "Min Value"
msgstr ""
@@ -25536,16 +25873,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25560,6 +25900,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25602,9 +25945,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25695,12 +26035,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -25734,6 +26068,9 @@ msgstr ""
msgid "Name new label"
msgstr ""
+msgid "Name to be used as the sender for emails from Service Desk."
+msgstr ""
+
msgid "Name:"
msgstr ""
@@ -26224,6 +26561,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26236,9 +26576,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26389,6 +26726,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26410,9 +26750,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26601,6 +26938,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26732,18 +27072,63 @@ msgstr ""
msgid "Notify|%{author_link}'s issue %{issue_reference_link} is due soon."
msgstr ""
+msgid "Notify|%{author_name} %{action_name} %{ref_type} %{ref_name} at %{project_link}"
+msgstr ""
+
+msgid "Notify|%{changed_files}:"
+msgstr ""
+
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
+msgid "Notify|%{committed_by_start} by %{author_name} %{committed_by_end} %{committed_at_start} at %{committed_date} %{committed_at_end}"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
msgid "Notify|%{invited_user} has %{highlight_start}declined%{highlight_end} your invitation to join the %{target_link} %{target_name}."
msgstr ""
+msgid "Notify|%{issues} imported."
+msgstr ""
+
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{reviewer_highlight}was approved by%{highlight_end} %{reviewer_avatar} %{reviewer_link}"
+msgstr ""
+
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{reviewer_highlight}was unapproved by%{highlight_end} %{reviewer_avatar} %{reviewer_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{update_at_start} Last update at %{update_at_mid} %{last_update_at} %{update_at_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|A remote mirror update has failed."
+msgstr ""
+
+msgid "Notify|After it expires, you can %{a_start} request a new one %{a_end}."
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26759,6 +27144,36 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Click here to set your password"
+msgstr ""
+
+msgid "Notify|Commit Author"
+msgstr ""
+
+msgid "Notify|Committed by"
+msgstr ""
+
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Error parsing CSV file. Please make sure it has the correct format: a delimited text file that uses a comma to separate values."
+msgstr ""
+
+msgid "Notify|Errors found on %{singular_or_plural_line}: %{error_lines}. Please check if these lines have an issue title."
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{username}!"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26771,6 +27186,9 @@ msgstr ""
msgid "Notify|Learn more about Auto DevOps"
msgstr ""
+msgid "Notify|Logs may contain sensitive data. Please consider before forwarding this email."
+msgstr ""
+
msgid "Notify|Merge request %{merge_request} can no longer be merged due to conflict."
msgstr ""
@@ -26789,33 +27207,96 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
+msgid "Notify|Merge request was unapproved"
+msgstr ""
+
+msgid "Notify|Merge request was unapproved (%{approvals_count}/%{approvals_required})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
+msgid "Notify|No preview for this file type"
+msgstr ""
+
msgid "Notify|Pipeline %{pipeline_link} triggered by"
msgstr ""
+msgid "Notify|Pipeline has been fixed and #%{pipeline_id} has passed!"
+msgstr ""
+
+msgid "Notify|Remote mirror"
+msgstr ""
+
+msgid "Notify|The Administrator created an account for you. Now you are a member of the company GitLab application."
+msgstr ""
+
msgid "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}."
msgstr ""
+msgid "Notify|The diff for this file was not included because it is too large."
+msgstr ""
+
+msgid "Notify|The diff was not included because it is too large."
+msgstr ""
+
+msgid "Notify|The push did not contain any new commits, but force pushed to delete the commits and changes below."
+msgstr ""
+
msgid "Notify|This issue is due on: %{issue_due_date}"
msgstr ""
+msgid "Notify|This link is valid for %{password_reset_token_valid_time}."
+msgstr ""
+
msgid "Notify|Unless you verify your domain by %{time_start}%{time}%{time_end} it will be removed from your GitLab project."
msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|Your CSV import for project %{project_link} has been completed."
+msgstr ""
+
+msgid "Notify|Your account has been created successfully."
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
msgid "Notify|currently supported languages"
msgstr ""
+msgid "Notify|deleted"
+msgstr ""
+
+msgid "Notify|login.........................................."
+msgstr ""
+
+msgid "Notify|pushed new"
+msgstr ""
+
+msgid "Notify|pushed to"
+msgstr ""
+
msgid "Notify|successfully completed %{jobs} in %{stages}."
msgstr ""
@@ -26876,6 +27357,9 @@ msgstr ""
msgid "Object does not exist on the server or you don't have permissions to access it"
msgstr ""
+msgid "Observability"
+msgstr ""
+
msgid "Oct"
msgstr ""
@@ -26914,6 +27398,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27133,6 +27620,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27154,6 +27647,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27277,6 +27773,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27432,9 +27931,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27648,6 +28153,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27684,6 +28192,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27708,6 +28219,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27809,15 +28323,15 @@ msgid_plural "PackageRegistry|Delete %d assets"
msgstr[0] ""
msgstr[1] ""
-msgid "PackageRegistry|Delete Package File"
-msgstr ""
-
msgid "PackageRegistry|Delete Package Version"
msgstr ""
msgid "PackageRegistry|Delete package"
msgstr ""
+msgid "PackageRegistry|Delete package asset"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -27917,13 +28431,16 @@ msgstr ""
msgid "PackageRegistry|Package Registry"
msgstr ""
+msgid "PackageRegistry|Package asset deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Package assets deleted successfully"
msgstr ""
msgid "PackageRegistry|Package deleted successfully"
msgstr ""
-msgid "PackageRegistry|Package file deleted successfully"
+msgid "PackageRegistry|Package formats"
msgstr ""
msgid "PackageRegistry|Package has %{updatesCount} archived update"
@@ -27958,9 +28475,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27970,12 +28484,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -27994,10 +28502,10 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
-msgid "PackageRegistry|Something went wrong while deleting the package assets."
+msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
-msgid "PackageRegistry|Something went wrong while deleting the package file."
+msgid "PackageRegistry|Something went wrong while deleting the package assets."
msgstr ""
msgid "PackageRegistry|Something went wrong while deleting the package."
@@ -28077,7 +28585,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28326,9 +28834,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28404,6 +28909,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29109,25 +29620,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29139,18 +29641,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29208,12 +29704,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29244,9 +29734,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29346,7 +29833,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29520,6 +30007,9 @@ msgstr ""
msgid "Please wait while we import the repository for you. Refresh at will."
msgstr ""
+msgid "Please wait while we prepare for verification."
+msgstr ""
+
msgid "Pods in use"
msgstr ""
@@ -29562,6 +30052,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29583,10 +30076,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29610,6 +30103,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29631,9 +30127,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -29901,15 +30394,9 @@ msgstr ""
msgid "Profiles|Add key"
msgstr ""
-msgid "Profiles|Add status emoji"
-msgstr ""
-
msgid "Profiles|An error occurred while updating your username, please try again."
msgstr ""
-msgid "Profiles|An indicator appears next to your name and avatar."
-msgstr ""
-
msgid "Profiles|Avatar cropper"
msgstr ""
@@ -29922,9 +30409,6 @@ msgstr ""
msgid "Profiles|Bio"
msgstr ""
-msgid "Profiles|Busy"
-msgstr ""
-
msgid "Profiles|Change username"
msgstr ""
@@ -29940,9 +30424,6 @@ msgstr ""
msgid "Profiles|City, country"
msgstr ""
-msgid "Profiles|Clear status"
-msgstr ""
-
msgid "Profiles|Commit email"
msgstr ""
@@ -29955,6 +30436,9 @@ msgstr ""
msgid "Profiles|Connected Accounts"
msgstr ""
+msgid "Profiles|Created%{time_ago}"
+msgstr ""
+
msgid "Profiles|Current path: %{path}"
msgstr ""
@@ -30045,9 +30529,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30078,6 +30559,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30192,9 +30676,6 @@ msgstr ""
msgid "Profiles|Website url"
msgstr ""
-msgid "Profiles|What's your status?"
-msgstr ""
-
msgid "Profiles|Who you represent or work for."
msgstr ""
@@ -30240,9 +30721,6 @@ msgstr ""
msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you."
msgstr ""
-msgid "Profiles|Your status"
-msgstr ""
-
msgid "Profiles|https://website.com"
msgstr ""
@@ -30732,6 +31210,9 @@ msgstr ""
msgid "ProjectSettings|Choose your merge method, options, checks, and squash options."
msgstr ""
+msgid "ProjectSettings|Combine git tags with release notes, release evidence, and assets to create a release."
+msgstr ""
+
msgid "ProjectSettings|Configure your project resources and monitor their health."
msgstr ""
@@ -30801,6 +31282,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30882,6 +31366,9 @@ msgstr ""
msgid "ProjectSettings|Merging is only allowed when the source branch is up-to-date with its target."
msgstr ""
+msgid "ProjectSettings|Monitor"
+msgstr ""
+
msgid "ProjectSettings|No merge commits are created."
msgstr ""
@@ -30924,6 +31411,9 @@ msgstr ""
msgid "ProjectSettings|Public"
msgstr ""
+msgid "ProjectSettings|Releases"
+msgstr ""
+
msgid "ProjectSettings|Repository"
msgstr ""
@@ -30942,6 +31432,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31686,6 +32179,9 @@ msgstr ""
msgid "ProtectedBranch|Allowed to push:"
msgstr ""
+msgid "ProtectedBranch|An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "ProtectedBranch|Branch"
msgstr ""
@@ -31722,6 +32218,9 @@ msgstr ""
msgid "ProtectedBranch|Protected branches"
msgstr ""
+msgid "ProtectedBranch|Protected branches, merge request approvals, and status checks will appear here once configured."
+msgstr ""
+
msgid "ProtectedBranch|Reject code pushes that change files listed in the CODEOWNERS file."
msgstr ""
@@ -31752,7 +32251,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31770,7 +32269,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31953,9 +32452,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -31974,18 +32470,45 @@ msgstr ""
msgid "PushRules|All committed filenames cannot match this %{wiki_syntax_link_start}regular expression%{wiki_syntax_link_end}. If empty, any filename is allowed."
msgstr ""
+msgid "PushRules|Branch name"
+msgstr ""
+
+msgid "PushRules|Check whether the commit author is a GitLab user"
+msgstr ""
+
+msgid "PushRules|Commit author's email"
+msgstr ""
+
msgid "PushRules|Commit messages cannot match this %{wiki_syntax_link_start}regular expression%{wiki_syntax_link_end}. If empty, commit messages are not rejected based on any expression."
msgstr ""
msgid "PushRules|Do not allow users to remove Git tags with %{code_block_start}git push%{code_block_end}"
msgstr ""
+msgid "PushRules|Maximum file size (MB)"
+msgstr ""
+
+msgid "PushRules|Prevent pushing secret files"
+msgstr ""
+
+msgid "PushRules|Prohibited file names"
+msgstr ""
+
msgid "PushRules|Reject any files likely to contain secrets. %{secret_files_link_start}What secret files are rejected?%{secret_files_link_end}"
msgstr ""
+msgid "PushRules|Reject expression in commit messages"
+msgstr ""
+
msgid "PushRules|Reject file sizes equal to or greater than this size. If set to 0, files of any size are allowed. This rule does not apply to files tracked by Git LFS."
msgstr ""
+msgid "PushRules|Reject unsigned commits"
+msgstr ""
+
+msgid "PushRules|Require expression in commit messages"
+msgstr ""
+
msgid "PushRules|Restrict commits to existing GitLab users."
msgstr ""
@@ -32309,6 +32832,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33677,6 +34203,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33692,6 +34221,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33740,6 +34275,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33787,6 +34325,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33811,6 +34352,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33835,6 +34379,9 @@ msgstr ""
msgid "Runners|Never contacted:"
msgstr ""
+msgid "Runners|Never expires"
+msgstr ""
+
msgid "Runners|New group runners view"
msgstr ""
@@ -33856,24 +34403,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -33939,6 +34477,12 @@ msgstr ""
msgid "Runners|Runner assigned to project."
msgstr ""
+msgid "Runners|Runner authentication token expiration"
+msgstr ""
+
+msgid "Runners|Runner authentication tokens will expire based on a set interval. They will automatically rotate once expired."
+msgstr ""
+
msgid "Runners|Runner cannot be deleted, please contact your administrator"
msgstr ""
@@ -34020,9 +34564,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34073,6 +34614,9 @@ msgstr ""
msgid "Runners|To register them, go to the %{link_start}group's Runners page%{link_end}."
msgstr ""
+msgid "Runners|Token expiry"
+msgstr ""
+
msgid "Runners|Up to date"
msgstr ""
@@ -34106,9 +34650,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34127,42 +34680,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34256,6 +34785,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34301,15 +34833,24 @@ msgstr ""
msgid "Saving project."
msgstr ""
-msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
msgstr ""
msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
msgstr ""
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34319,6 +34860,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -35157,6 +35710,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36006,6 +36562,9 @@ msgstr ""
msgid "Set target branch to %{branch_name}."
msgstr ""
+msgid "Set the Draft status"
+msgstr ""
+
msgid "Set the Ready status"
msgstr ""
@@ -36541,12 +37100,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36697,6 +37265,39 @@ 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."
+msgstr ""
+
+msgid "Slack|%{asterisk}Step 2.%{asterisk} Try it out!"
+msgstr ""
+
+msgid "Slack|%{emoji}Connected to GitLab account %{account}"
+msgstr ""
+
+msgid "Slack|%{emoji}Welcome to GitLab for Slack!"
+msgstr ""
+
+msgid "Slack|Connect your GitLab account"
+msgstr ""
+
+msgid "Slack|Create a new issue"
+msgstr ""
+
+msgid "Slack|Create new issues from Slack: %{command}"
+msgstr ""
+
+msgid "Slack|Run a CI/CD job"
+msgstr ""
+
+msgid "Slack|See a list of available commands: %{command})"
+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}."
+msgstr ""
+
msgid "Slice multiplier"
msgstr ""
@@ -36820,6 +37421,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36910,6 +37514,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37039,6 +37646,9 @@ msgstr ""
msgid "SortOptions|Last created"
msgstr ""
+msgid "SortOptions|Latest version"
+msgstr ""
+
msgid "SortOptions|Least popular"
msgstr ""
@@ -37099,6 +37709,9 @@ msgstr ""
msgid "SortOptions|Oldest updated"
msgstr ""
+msgid "SortOptions|Oldest version"
+msgstr ""
+
msgid "SortOptions|Popularity"
msgstr ""
@@ -37294,6 +37907,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37996,7 +38612,7 @@ msgstr ""
msgid "SuggestedColors|Green-cyan"
msgstr ""
-msgid "SuggestedColors|Lavendar"
+msgid "SuggestedColors|Lavender"
msgstr ""
msgid "SuggestedColors|Magenta-pink"
@@ -38047,6 +38663,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -38056,7 +38675,13 @@ msgstr ""
msgid "SuperSonics|Activation code"
msgstr ""
-msgid "SuperSonics|An error occurred while adding your subscription."
+msgid "SuperSonics|Activation not possible due to seat mismatch"
+msgstr ""
+
+msgid "SuperSonics|Activation not possible due to true-up value mismatch"
+msgstr ""
+
+msgid "SuperSonics|An error occurred while adding your subscription"
msgstr ""
msgid "SuperSonics|Billable users"
@@ -38065,7 +38690,7 @@ msgstr ""
msgid "SuperSonics|Buy subscription"
msgstr ""
-msgid "SuperSonics|Cannot activate instance due to a connectivity issue."
+msgid "SuperSonics|Cannot activate instance due to a connectivity issue"
msgstr ""
msgid "SuperSonics|Cloud licensing"
@@ -38181,25 +38806,37 @@ msgstr ""
msgid "SuperSonics|You have added a license that activates on %{date}. Please see the subscription history table below for more details."
msgstr ""
+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 ""
+
msgid "SuperSonics|You have successfully added a license that activates on %{date}. Please see the subscription history table below for more details."
msgstr ""
+msgid "SuperSonics|You may have entered an expired or ineligible activation code. To request a new activation code, %{purchaseSubscriptionLinkStart}purchase a new subscription%{purchaseSubscriptionLinkEnd} or %{supportLinkStart}contact GitLab Support%{supportLinkEnd} for further assistance."
+msgstr ""
+
msgid "SuperSonics|You'll be charged for %{trueUpLinkStart}users over license%{trueUpLinkEnd} on a quarterly or annual basis, depending on the terms of your agreement."
msgstr ""
msgid "SuperSonics|Your %{subscriptionEntryName} cannot be displayed at the moment. Please refresh the page to try again."
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 ""
+
msgid "SuperSonics|Your future dated license was successfully added"
msgstr ""
msgid "SuperSonics|Your subscription"
msgstr ""
+msgid "SuperSonics|Your subscription cannot be located"
+msgstr ""
+
msgid "SuperSonics|Your subscription details will sync shortly."
msgstr ""
-msgid "SuperSonics|Your subscription is expired."
+msgid "SuperSonics|Your subscription is expired"
msgstr ""
msgid "SuperSonics|Your subscription was successfully activated. You can see the details below."
@@ -39306,6 +39943,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39387,6 +40027,9 @@ msgstr ""
msgid "The scan has been created."
msgstr ""
+msgid "The secret is only available when you first create the application."
+msgstr ""
+
msgid "The snippet can be accessed without any authentication."
msgstr ""
@@ -39402,6 +40045,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39414,6 +40063,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39462,10 +40114,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39921,6 +40573,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39963,6 +40618,9 @@ msgstr ""
msgid "This deployment is not waiting for approvals."
msgstr ""
+msgid "This deployment job does not run automatically and must be started manually, but it's older than the latest deployment, and therefore can't run."
+msgstr ""
+
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 ""
@@ -40107,6 +40765,9 @@ msgstr ""
msgid "This is the number of %{billable_users_link_start}billable users%{link_end} on your installation, and this is the minimum number you need to purchase when you renew your license."
msgstr ""
+msgid "This is the only time the secret is accessible. Copy the secret and store it securely."
+msgstr ""
+
msgid "This is your current session"
msgstr ""
@@ -40326,19 +40987,22 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
msgid "This project path either does not exist or you do not have access."
msgstr ""
-msgid "This project will be deleted on %{date}"
+msgid "This project reached the limit of custom domains. (Max %d)"
msgstr ""
-msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
+msgid "This project will be deleted on %{date}"
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
+msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
@@ -40443,6 +41107,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40653,6 +41320,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40680,6 +41350,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40690,6 +41378,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40741,9 +41435,15 @@ 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 ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40906,9 +41606,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40933,6 +41630,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40942,6 +41651,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41002,9 +41714,6 @@ msgstr ""
msgid "Toggle sidebar"
msgstr ""
-msgid "Toggle the Draft status"
-msgstr ""
-
msgid "Toggle the Performance Bar"
msgstr ""
@@ -41050,9 +41759,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41077,9 +41795,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41095,6 +41825,9 @@ msgstr ""
msgid "Total cores (CPUs)"
msgstr ""
+msgid "Total issue weight"
+msgstr ""
+
msgid "Total memory (GB)"
msgstr ""
@@ -41248,39 +41981,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41386,21 +42092,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41980,6 +42683,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -42007,6 +42713,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42103,10 +42812,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42172,7 +42881,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42334,6 +43043,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42441,6 +43153,9 @@ msgstr ""
msgid "User identity was successfully updated."
msgstr ""
+msgid "User is blocked"
+msgstr ""
+
msgid "User is not allowed to resolve thread"
msgstr ""
@@ -42735,7 +43450,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43029,10 +43744,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43064,6 +43782,9 @@ msgstr ""
msgid "View eligible approvers"
msgstr ""
+msgid "View entire blame"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -43509,9 +44230,6 @@ msgstr ""
msgid "Vulnerability|Information related to how the vulnerability was discovered and its impact on the system."
msgstr ""
-msgid "Vulnerability|Learn more about this vulnerability and the best way to resolve it."
-msgstr ""
-
msgid "Vulnerability|Links"
msgstr ""
@@ -43524,6 +44242,9 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
+msgid "Vulnerability|Project:"
+msgstr ""
+
msgid "Vulnerability|Remove identifier row"
msgstr ""
@@ -43551,6 +44272,9 @@ msgstr ""
msgid "Vulnerability|Severity"
msgstr ""
+msgid "Vulnerability|Severity:"
+msgstr ""
+
msgid "Vulnerability|Status"
msgstr ""
@@ -43575,6 +44299,9 @@ msgstr ""
msgid "Vulnerability|View training"
msgstr ""
+msgid "WARNING:"
+msgstr ""
+
msgid "WARNING: This snippet contains hidden files which might be used to mask malicious behavior. Exercise caution if cloning and executing code from this snippet."
msgstr ""
@@ -43815,6 +44542,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43857,6 +44587,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43914,6 +44647,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43932,6 +44668,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -44197,9 +44936,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44224,9 +44960,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44314,10 +45047,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44329,13 +45062,19 @@ msgstr ""
msgid "WorkItem|Add assignees"
msgstr ""
+msgid "WorkItem|Add due date"
+msgstr ""
+
+msgid "WorkItem|Add start date"
+msgstr ""
+
msgid "WorkItem|Add task"
msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44346,16 +45085,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44364,10 +45100,16 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Dates"
+msgstr ""
+
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Due date"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
msgid "WorkItem|Incident"
@@ -44379,10 +45121,10 @@ msgstr ""
msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Learn about tasks."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
msgid "WorkItem|None"
@@ -44403,19 +45145,16 @@ msgstr ""
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
-msgstr ""
-
-msgid "WorkItem|Something went wrong when fetching the items list. Please refresh this page."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44430,15 +45169,24 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Start date"
+msgstr ""
+
msgid "WorkItem|Task"
msgstr ""
msgid "WorkItem|Task deleted"
msgstr ""
+msgid "WorkItem|Tasks"
+msgstr ""
+
msgid "WorkItem|Test case"
msgstr ""
@@ -44448,19 +45196,16 @@ msgstr ""
msgid "WorkItem|Turn on confidentiality"
msgstr ""
-msgid "WorkItem|Type"
-msgstr ""
-
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44749,11 +45494,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44859,11 +45599,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -45028,7 +45763,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45091,6 +45826,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45202,9 +45940,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45400,9 +46135,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45464,6 +46196,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45479,7 +46214,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45633,6 +46368,9 @@ msgstr ""
msgid "added %{emails}"
msgstr ""
+msgid "added a %{link_type} link"
+msgstr ""
+
msgid "added a Zoom call to this issue"
msgstr ""
@@ -45686,9 +46424,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45784,6 +46519,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45825,6 +46563,9 @@ msgid_plural "changes"
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 ""
@@ -46073,7 +46814,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46186,6 +46927,9 @@ msgstr ""
msgid "comment"
msgstr ""
+msgid "commented"
+msgstr ""
+
msgid "commented on %{link_to_project}"
msgstr ""
@@ -46198,9 +46942,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46237,6 +46978,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46361,6 +47105,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46642,6 +47389,11 @@ msgstr ""
msgid "limit of %{project_limit} reached"
msgstr ""
+msgid "line"
+msgid_plural "lines"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "load it anyway"
msgstr ""
@@ -46857,6 +47609,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46992,9 +47747,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47046,6 +47798,9 @@ msgstr ""
msgid "must match %{association}.project_id"
msgstr ""
+msgid "must not contain commonly used combinations of words and letters"
+msgstr ""
+
msgid "my-awesome-group"
msgstr ""
@@ -47273,6 +48028,9 @@ msgstr ""
msgid "removed"
msgstr ""
+msgid "removed a %{link_type} link"
+msgstr ""
+
msgid "removed a Zoom call from this issue"
msgstr ""
@@ -47305,6 +48063,11 @@ msgstr ""
msgid "scan-execution-policy: policy not applied, %{policy_path} file is missing"
msgstr ""
+msgid "seat"
+msgid_plural "seats"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "security Reports|There was an error creating the merge request"
msgstr ""
@@ -47479,6 +48242,11 @@ msgstr ""
msgid "uploads"
msgstr ""
+msgid "user"
+msgid_plural "users"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "user avatar"
msgstr ""
@@ -47556,6 +48324,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/gl_ES/gitlab.po b/locale/gl_ES/gitlab.po
index cac341bddcf..9ea0836a034 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-08-12 18:02\n"
+"PO-Revision-Date: 2022-09-11 06:20\n"
msgid " %{start} to %{end}"
msgstr " %{start} a %{end}"
@@ -22,6 +22,11 @@ msgstr " %{start} a %{end}"
msgid " (from %{timeoutSource})"
msgstr " (a partir de %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr " Recollido %{time}"
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/he_IL/gitlab.po b/locale/he_IL/gitlab.po
index c6071a9b352..0b74df4c695 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-08-12 18:05\n"
+"PO-Revision-Date: 2022-09-11 06:22\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,13 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr " (מתוך %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid " Collected %{time}"
msgstr " × ×ספה ב־%{time}"
@@ -171,6 +178,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -269,6 +283,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -479,6 +500,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -528,13 +556,6 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -610,9 +631,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -1061,12 +1079,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1183,6 +1195,9 @@ msgstr "%{size} בתי×"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr "%{sourceBranch} לתוך %{targetBranch}"
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1205,6 +1220,13 @@ msgstr[3] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1246,9 +1268,6 @@ msgstr[3] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1462,13 +1481,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "(this user)"
msgstr ""
@@ -1856,6 +1868,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1919,6 +1937,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -2087,6 +2111,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2402,7 +2429,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2429,6 +2456,9 @@ msgstr ""
msgid "Add a table"
msgstr "הוספת טבלה"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2468,6 +2498,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2519,6 +2552,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2690,6 +2729,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2705,9 +2747,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr "ניהול"
@@ -2906,15 +2945,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2954,7 +2993,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2978,6 +3017,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -3143,7 +3185,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3368,6 +3413,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -4142,9 +4190,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -4229,6 +4274,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4280,6 +4328,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4418,6 +4469,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4557,9 +4611,6 @@ msgstr[3] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4569,13 +4620,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -5134,6 +5185,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5605,6 +5659,13 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5620,10 +5681,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5641,7 +5708,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5662,6 +5729,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5671,9 +5741,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6472,12 +6539,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6493,12 +6554,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6550,9 +6605,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6596,9 +6648,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6636,6 +6685,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6848,6 +6900,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6974,6 +7035,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -7139,6 +7206,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7395,21 +7465,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7419,9 +7480,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8679,6 +8737,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8706,12 +8770,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8766,6 +8842,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9454,10 +9533,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9628,6 +9707,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9694,6 +9776,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9818,6 +9903,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10399,6 +10487,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10505,6 +10596,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10679,7 +10776,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -11282,9 +11379,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11828,6 +11922,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -12060,6 +12157,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -12138,15 +12241,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -12159,6 +12253,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -12234,6 +12331,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -12261,16 +12361,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -12291,16 +12388,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site name"
-msgstr ""
-
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12369,13 +12463,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12565,7 +12659,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12751,6 +12845,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12802,6 +12899,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13372,6 +13472,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13605,6 +13711,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13698,6 +13807,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -14266,6 +14378,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -14287,6 +14402,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -14302,6 +14423,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14524,6 +14651,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14539,9 +14669,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14566,9 +14693,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14716,6 +14840,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14800,9 +14927,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -15244,6 +15368,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -15280,12 +15407,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15352,18 +15473,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15400,6 +15512,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15433,9 +15548,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15511,6 +15623,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15944,6 +16059,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16700,9 +16818,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16913,13 +17028,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -17069,6 +17184,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -17139,6 +17257,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -17151,15 +17272,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -17308,9 +17422,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17506,6 +17617,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17713,6 +17830,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17875,6 +17995,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -18244,6 +18367,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -18256,6 +18382,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -18274,6 +18406,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -18286,6 +18427,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -18313,6 +18457,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18583,9 +18733,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -19021,9 +19168,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -19051,6 +19195,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19414,13 +19561,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19459,21 +19609,21 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19485,43 +19635,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19530,6 +19674,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19678,6 +19825,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19811,9 +19961,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19847,6 +19994,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19970,9 +20120,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19985,15 +20141,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -20015,6 +20192,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -20084,9 +20264,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -20351,9 +20528,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -21143,6 +21317,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -21161,6 +21338,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -21194,7 +21374,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -21335,9 +21515,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21609,6 +21795,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21921,6 +22110,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21936,6 +22128,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21955,9 +22153,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21982,9 +22177,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -22102,12 +22294,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22426,9 +22624,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22474,6 +22669,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22489,12 +22687,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22552,9 +22744,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22564,9 +22753,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22579,9 +22765,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22876,9 +23059,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -23315,7 +23495,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -23330,6 +23510,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23502,9 +23685,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23556,6 +23736,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -24300,6 +24483,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24588,10 +24774,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -25079,6 +25262,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -25166,6 +25352,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -25241,6 +25430,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25921,16 +26134,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25945,6 +26161,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25987,9 +26206,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -26080,12 +26296,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26613,6 +26823,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26625,9 +26838,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26778,6 +26988,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26799,9 +27012,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26992,6 +27202,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -27134,6 +27347,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -27143,6 +27359,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -27158,6 +27392,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -27188,9 +27434,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -27209,6 +27464,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -27315,6 +27576,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27534,6 +27798,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27555,6 +27825,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27678,6 +27951,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27835,9 +28111,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -28051,6 +28333,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -28087,6 +28372,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -28111,6 +28399,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -28331,6 +28622,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28365,9 +28659,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -28377,12 +28668,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28486,7 +28771,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28735,9 +29020,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28813,6 +29095,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29518,25 +29806,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29548,18 +29827,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29617,12 +29890,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29653,9 +29920,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29755,7 +30019,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29971,6 +30235,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29992,10 +30259,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -30019,6 +30286,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -30040,9 +30310,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30454,9 +30721,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30487,6 +30751,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -31174,6 +31441,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -31186,6 +31456,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -31204,6 +31477,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -31345,6 +31621,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -32155,7 +32434,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -32173,7 +32452,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -32356,9 +32635,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32482,9 +32758,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32717,6 +32990,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -34115,6 +34391,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -34130,6 +34409,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -34178,6 +34463,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -34227,6 +34515,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -34251,6 +34542,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -34296,24 +34590,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34462,9 +34747,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34550,9 +34832,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34571,42 +34862,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34700,6 +34967,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34745,12 +35015,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34760,6 +35045,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -35362,6 +35659,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35617,6 +35917,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -37005,12 +37308,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -37284,6 +37596,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -37374,6 +37689,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37758,6 +38076,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37773,9 +38094,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38514,6 +38832,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39793,6 +40114,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39889,6 +40213,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39901,6 +40231,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39949,10 +40282,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -40057,9 +40390,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -40411,6 +40741,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -40435,7 +40768,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40816,6 +41149,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40828,9 +41164,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40933,6 +41266,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -41143,6 +41479,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -41170,6 +41509,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -41184,6 +41541,12 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -41238,6 +41601,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -41400,9 +41766,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -41427,6 +41790,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -41436,6 +41811,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41544,9 +41922,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41571,9 +41958,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41742,39 +42141,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41880,21 +42252,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -42474,6 +42843,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -42501,6 +42873,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42597,10 +42972,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42666,7 +43041,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42828,6 +43203,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -43231,7 +43609,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43525,10 +43903,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -44230,6 +44611,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -44311,6 +44698,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -44353,6 +44743,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -44410,6 +44803,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -44428,6 +44824,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -44479,6 +44878,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44694,9 +45096,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44721,9 +45120,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44811,10 +45207,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44832,7 +45228,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44845,16 +45241,13 @@ msgstr[3] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44863,19 +45256,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
+msgstr ""
+
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44884,19 +45289,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44911,31 +45319,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -45161,6 +45578,13 @@ 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 ""
@@ -45221,13 +45645,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -45333,13 +45750,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -45506,7 +45916,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45569,6 +45979,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45680,9 +46093,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45722,6 +46132,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45872,9 +46288,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45940,6 +46353,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45955,7 +46371,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -46168,9 +46584,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -46268,6 +46681,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -46311,6 +46727,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+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 ""
@@ -46359,7 +46778,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46567,7 +46986,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46694,9 +47113,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46733,6 +47149,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46861,6 +47280,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -47078,6 +47500,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -47182,7 +47607,7 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -47203,7 +47628,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -47370,6 +47795,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -47505,9 +47933,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -48081,6 +48506,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/hi_IN/gitlab.po b/locale/hi_IN/gitlab.po
index cf8a3e8806b..a7e63580e0e 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-08-12 18:06\n"
+"PO-Revision-Date: 2022-09-11 06:24\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/hr_HR/gitlab.po b/locale/hr_HR/gitlab.po
index ac59f344271..f34e6425ded 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-08-12 18:03\n"
+"PO-Revision-Date: 2022-09-11 06:21\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,12 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -157,6 +163,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -241,6 +253,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -421,6 +439,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -463,12 +487,6 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -535,9 +553,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -976,12 +991,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1093,6 +1102,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1114,6 +1126,12 @@ msgstr[2] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1150,9 +1168,6 @@ msgstr[2] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1363,12 +1378,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "(this user)"
msgstr ""
@@ -1729,6 +1738,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1792,6 +1807,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1960,6 +1981,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2275,7 +2299,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2302,6 +2326,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2341,6 +2368,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2392,6 +2422,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2563,6 +2599,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2578,9 +2617,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2779,15 +2815,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2827,7 +2863,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2851,6 +2887,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -3016,7 +3055,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3241,6 +3283,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -4015,9 +4060,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -4102,6 +4144,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4153,6 +4198,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4291,6 +4339,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4429,9 +4480,6 @@ msgstr[2] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4441,13 +4489,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4999,6 +5047,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5467,6 +5518,12 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5482,10 +5539,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5503,7 +5566,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5524,6 +5587,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5533,9 +5599,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6334,12 +6397,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6355,12 +6412,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6412,9 +6463,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6457,9 +6505,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6496,6 +6541,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6706,6 +6754,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6832,6 +6889,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6997,6 +7060,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7252,21 +7318,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7276,9 +7333,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8533,6 +8587,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8560,12 +8620,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8620,6 +8692,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9307,10 +9382,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9481,6 +9556,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9547,6 +9625,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9670,6 +9751,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10249,6 +10333,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10354,6 +10441,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10528,7 +10621,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -11131,9 +11224,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11677,6 +11767,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11908,6 +12001,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11986,15 +12085,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -12007,6 +12097,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -12082,6 +12175,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -12109,16 +12205,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner name"
-msgstr ""
-
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -12139,16 +12232,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12217,13 +12307,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12412,7 +12502,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12598,6 +12688,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12649,6 +12742,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13213,6 +13309,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13444,6 +13546,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13537,6 +13642,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -14101,6 +14209,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -14122,6 +14233,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -14137,6 +14254,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14359,6 +14482,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14374,9 +14500,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14401,9 +14524,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14551,6 +14671,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14635,9 +14758,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -15079,6 +15199,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -15115,12 +15238,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15187,18 +15304,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15235,6 +15343,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15268,9 +15379,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15346,6 +15454,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15778,6 +15889,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16531,9 +16645,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16744,13 +16855,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16900,6 +17011,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16969,6 +17083,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16981,14 +17098,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -17134,9 +17245,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17332,6 +17440,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17539,6 +17653,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17701,6 +17818,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -18070,6 +18190,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -18082,6 +18205,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -18100,6 +18229,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -18112,6 +18250,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -18139,6 +18280,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18409,9 +18556,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18847,9 +18991,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18877,6 +19018,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19240,13 +19384,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19285,19 +19432,19 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19309,43 +19456,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19354,6 +19495,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19501,6 +19645,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19633,9 +19780,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19669,6 +19813,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19792,9 +19939,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19807,15 +19960,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19837,6 +20011,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19906,9 +20083,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -20170,9 +20344,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20962,6 +21133,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20980,6 +21154,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -21013,7 +21190,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -21154,9 +21331,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21427,6 +21610,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21739,6 +21925,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21754,6 +21943,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21772,9 +21967,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21799,9 +21991,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21919,12 +22108,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22243,9 +22438,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22291,6 +22483,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22306,12 +22501,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22369,9 +22558,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22381,9 +22567,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22396,9 +22579,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22693,9 +22873,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -23131,7 +23308,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -23146,6 +23323,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23317,9 +23497,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23371,6 +23548,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -24109,6 +24289,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24397,10 +24580,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24886,6 +25066,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24973,6 +25156,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -25048,6 +25234,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25726,16 +25936,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25750,6 +25963,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25792,9 +26008,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25885,12 +26098,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26416,6 +26623,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26428,9 +26638,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26581,6 +26788,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26602,9 +26812,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26794,6 +27001,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26932,6 +27142,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26941,6 +27154,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26956,6 +27187,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26986,9 +27229,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -27007,6 +27259,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -27112,6 +27370,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27331,6 +27592,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27352,6 +27619,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27475,6 +27745,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27631,9 +27904,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27847,6 +28126,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27883,6 +28165,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27907,6 +28192,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -28126,6 +28414,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28159,9 +28450,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -28171,12 +28459,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28279,7 +28561,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28528,9 +28810,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28606,6 +28885,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29311,25 +29596,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29341,18 +29617,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29410,12 +29680,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29446,9 +29710,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29548,7 +29809,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29764,6 +30025,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29785,10 +30049,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29812,6 +30076,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29833,9 +30100,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30247,9 +30511,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30280,6 +30541,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30967,6 +31231,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30979,6 +31246,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30997,6 +31267,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -31138,6 +31411,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31948,7 +32224,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31966,7 +32242,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -32149,9 +32425,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32275,9 +32548,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32509,6 +32779,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33892,6 +34165,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33907,6 +34183,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33955,6 +34237,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -34003,6 +34288,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -34027,6 +34315,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -34072,24 +34363,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34237,9 +34519,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34324,9 +34603,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34345,42 +34633,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34474,6 +34738,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34519,12 +34786,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34534,6 +34816,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -35125,6 +35419,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35380,6 +35677,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36766,12 +37066,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -37045,6 +37354,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -37135,6 +37447,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37519,6 +37834,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37534,9 +37852,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38275,6 +38590,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39544,6 +39862,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39640,6 +39961,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39652,6 +39979,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39700,10 +40030,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39808,9 +40138,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -40162,6 +40489,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -40186,7 +40516,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40567,6 +40897,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40579,9 +40912,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40684,6 +41014,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40894,6 +41227,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40921,6 +41257,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40933,6 +41287,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40987,6 +41347,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -41149,9 +41512,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -41176,6 +41536,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -41185,6 +41557,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41293,9 +41668,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41320,9 +41704,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41491,39 +41887,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41629,21 +41998,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -42223,6 +42589,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -42250,6 +42619,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42346,10 +42718,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42415,7 +42787,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42577,6 +42949,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42979,7 +43354,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43273,10 +43648,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43975,6 +44353,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -44056,6 +44440,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -44098,6 +44485,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -44155,6 +44545,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -44173,6 +44566,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -44224,6 +44620,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44437,9 +44836,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44464,9 +44860,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44554,10 +44947,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44575,7 +44968,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44587,16 +44980,13 @@ msgstr[2] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44605,19 +44995,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44626,19 +45028,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44653,31 +45058,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44902,6 +45316,12 @@ 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 ""
@@ -44962,12 +45382,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -45073,12 +45487,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -45244,7 +45652,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45307,6 +45715,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45418,9 +45829,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45460,6 +45868,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45610,9 +46024,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45676,6 +46087,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45691,7 +46105,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45901,9 +46315,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -46000,6 +46411,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -46042,6 +46456,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+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 ""
@@ -46090,7 +46507,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46294,7 +46711,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46420,9 +46837,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46459,6 +46873,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46585,6 +47002,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46798,6 +47218,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46900,7 +47323,7 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46921,7 +47344,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -47086,6 +47509,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -47221,9 +47647,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47791,6 +48214,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/hu_HU/gitlab.po b/locale/hu_HU/gitlab.po
index 2189e0ed07b..fea93c01ceb 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-08-12 18:04\n"
+"PO-Revision-Date: 2022-09-11 06:22\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/hy_AM/gitlab.po b/locale/hy_AM/gitlab.po
index dcf9f697d6f..c3c7a482471 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-08-12 18:05\n"
+"PO-Revision-Date: 2022-09-11 06:23\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/id_ID/gitlab.po b/locale/id_ID/gitlab.po
index b2cfc89ce46..ab5f599ae40 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-08-12 18:03\n"
+"PO-Revision-Date: 2022-09-11 06:21\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,10 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -129,6 +133,10 @@ msgid "%d approver (you've approved)"
msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -185,6 +193,10 @@ msgid "%d contribution"
msgid_plural "%d contributions"
msgstr[0] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -305,6 +317,10 @@ msgid "%d project selected"
msgid_plural "%d projects selected"
msgstr[0] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -333,10 +349,6 @@ msgid "%d tag per image name"
msgid_plural "%d tags per image name"
msgstr[0] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -385,9 +397,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -806,12 +815,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -913,6 +916,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -932,6 +938,10 @@ msgstr[0] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -958,9 +968,6 @@ msgstr[0] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1165,10 +1172,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-
msgid "(this user)"
msgstr ""
@@ -1475,6 +1478,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1538,6 +1547,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1706,6 +1721,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2021,7 +2039,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2048,6 +2066,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2087,6 +2108,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2138,6 +2162,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2309,6 +2339,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2324,9 +2357,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2525,15 +2555,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2573,7 +2603,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2597,6 +2627,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2762,7 +2795,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -2987,6 +3023,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3761,9 +3800,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3848,6 +3884,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -3899,6 +3938,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4037,6 +4079,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4173,9 +4218,6 @@ msgstr[0] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4185,13 +4227,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4729,6 +4771,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5191,6 +5236,10 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5206,10 +5255,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5227,7 +5282,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5248,6 +5303,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5257,9 +5315,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6058,12 +6113,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6079,12 +6128,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6136,9 +6179,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6179,9 +6219,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6216,6 +6253,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6422,6 +6462,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6548,6 +6597,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6713,6 +6768,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -6966,21 +7024,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -6990,9 +7039,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8241,6 +8287,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8268,12 +8320,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8328,6 +8392,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9013,10 +9080,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9187,6 +9254,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9253,6 +9323,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9374,6 +9447,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -9949,6 +10025,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10052,6 +10131,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10226,7 +10311,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10829,9 +10914,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11375,6 +11457,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11604,6 +11689,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11682,15 +11773,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11703,6 +11785,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11778,6 +11863,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11805,16 +11893,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11835,16 +11920,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -11913,13 +11995,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12106,7 +12188,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12292,6 +12374,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12343,6 +12428,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -12895,6 +12983,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13122,6 +13216,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13215,6 +13312,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13771,6 +13871,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13792,6 +13895,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13807,6 +13916,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14029,6 +14144,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14044,9 +14162,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14071,9 +14186,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14221,6 +14333,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14305,9 +14420,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14749,6 +14861,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14785,12 +14900,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -14857,18 +14966,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -14905,6 +15005,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -14938,9 +15041,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15016,6 +15116,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15446,6 +15549,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16193,9 +16299,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16406,13 +16509,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16562,6 +16665,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16629,6 +16735,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16641,12 +16750,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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"
@@ -16786,9 +16891,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -16984,6 +17086,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17191,6 +17299,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17353,6 +17464,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17722,6 +17836,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17734,6 +17851,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17752,6 +17875,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17764,6 +17896,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17791,6 +17926,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18061,9 +18202,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18499,9 +18637,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18529,6 +18664,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -18892,13 +19030,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -18937,15 +19078,15 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -18957,43 +19098,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19002,6 +19137,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19147,6 +19285,9 @@ msgid "Hide chart"
msgid_plural "Hide charts"
msgstr[0] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19277,9 +19418,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19313,6 +19451,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19436,9 +19577,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19451,15 +19598,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19481,6 +19649,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19550,9 +19721,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19808,9 +19976,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20600,6 +20765,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20618,6 +20786,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20651,7 +20822,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20792,9 +20963,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21063,6 +21240,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21375,6 +21555,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21390,6 +21573,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21406,9 +21595,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21433,9 +21619,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21553,12 +21736,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -21877,9 +22066,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -21925,6 +22111,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -21940,12 +22129,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22003,9 +22186,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22015,9 +22195,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22030,9 +22207,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22327,9 +22501,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22763,7 +22934,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22778,6 +22949,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -22947,9 +23121,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23001,6 +23172,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23727,6 +23901,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24015,10 +24192,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24500,6 +24674,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24587,6 +24764,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24662,6 +24842,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25336,16 +25540,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25360,6 +25567,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25402,9 +25612,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25495,12 +25702,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26022,6 +26223,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26034,9 +26238,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26187,6 +26388,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26208,9 +26412,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26398,6 +26599,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26528,6 +26732,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26537,6 +26744,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26552,6 +26777,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26582,9 +26819,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26603,6 +26849,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26706,6 +26958,9 @@ msgid "On %{end_date}, your trial will end and %{namespace_name} will be limited
msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be limited to %{free_user_limit} members"
msgstr[0] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -26925,6 +27180,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -26946,6 +27207,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27069,6 +27333,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27223,9 +27490,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27439,6 +27712,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27475,6 +27751,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27499,6 +27778,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27716,6 +27998,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27747,9 +28032,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27759,12 +28041,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -27865,7 +28141,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28114,9 +28390,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28192,6 +28465,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -28897,25 +29176,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -28927,18 +29197,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -28996,12 +29260,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29032,9 +29290,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29134,7 +29389,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29350,6 +29605,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29371,10 +29629,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29398,6 +29656,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29419,9 +29680,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -29833,9 +30091,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -29866,6 +30121,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30553,6 +30811,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30565,6 +30826,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30583,6 +30847,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30724,6 +30991,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31534,7 +31804,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31552,7 +31822,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31735,9 +32005,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -31861,9 +32128,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32093,6 +32357,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33446,6 +33713,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33461,6 +33731,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33509,6 +33785,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33555,6 +33834,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33579,6 +33861,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33624,24 +33909,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -33787,9 +34063,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -33872,9 +34145,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -33893,42 +34175,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34022,6 +34280,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34067,12 +34328,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34082,6 +34358,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34651,6 +34939,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -34906,6 +35197,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36288,12 +36582,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36567,6 +36870,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36657,6 +36963,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37041,6 +37350,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37056,9 +37368,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -37797,6 +38106,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39046,6 +39358,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39142,6 +39457,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39154,6 +39475,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39202,10 +39526,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39310,9 +39634,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39664,6 +39985,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39688,7 +40012,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40069,6 +40393,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40081,9 +40408,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40186,6 +40510,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40396,6 +40723,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40423,6 +40753,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40431,6 +40779,12 @@ msgid "Time|min"
msgid_plural "Time|mins"
msgstr[0] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40485,6 +40839,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40647,9 +41004,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40674,6 +41028,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40683,6 +41049,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -40791,9 +41160,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -40818,9 +41196,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -40989,39 +41379,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41127,21 +41490,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41721,6 +42081,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41748,6 +42111,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -41844,10 +42210,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -41913,7 +42279,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42075,6 +42441,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42475,7 +42844,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -42769,10 +43138,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43465,6 +43837,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43546,6 +43924,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43588,6 +43969,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43645,6 +44029,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43663,6 +44050,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43714,6 +44104,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -43923,9 +44316,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -43950,9 +44340,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44040,10 +44427,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44061,7 +44448,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44071,16 +44458,13 @@ msgstr[0] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44089,19 +44473,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
+msgstr ""
+
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44110,19 +44506,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44137,31 +44536,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
+msgstr ""
+
+msgid "WorkItem|Turn off confidentiality"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44384,6 +44792,10 @@ 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 ""
@@ -44444,10 +44856,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44553,10 +44961,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44720,7 +45124,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -44783,6 +45187,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -44894,9 +45301,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -44936,6 +45340,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45086,9 +45496,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45148,6 +45555,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45163,7 +45573,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45367,9 +45777,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45464,6 +45871,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45504,6 +45914,9 @@ msgid "change"
msgid_plural "changes"
msgstr[0] ""
+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 ""
@@ -45552,7 +45965,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -45748,7 +46161,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -45872,9 +46285,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -45911,6 +46321,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46033,6 +46446,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46238,6 +46654,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46336,7 +46755,7 @@ msgid "merge request"
msgid_plural "merge requests"
msgstr[0] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46357,7 +46776,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46518,6 +46937,9 @@ msgid "mrWidget|Mentions issue"
msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46653,9 +47075,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47211,6 +47630,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/ig_NG/gitlab.po b/locale/ig_NG/gitlab.po
index b71ff51ef79..eb8be053df0 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-08-12 18:06\n"
+"PO-Revision-Date: 2022-09-11 06:24\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,10 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -129,6 +133,10 @@ msgid "%d approver (you've approved)"
msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -185,6 +193,10 @@ msgid "%d contribution"
msgid_plural "%d contributions"
msgstr[0] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -305,6 +317,10 @@ msgid "%d project selected"
msgid_plural "%d projects selected"
msgstr[0] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -333,10 +349,6 @@ msgid "%d tag per image name"
msgid_plural "%d tags per image name"
msgstr[0] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -385,9 +397,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -806,12 +815,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -913,6 +916,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -932,6 +938,10 @@ msgstr[0] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -958,9 +968,6 @@ msgstr[0] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1165,10 +1172,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-
msgid "(this user)"
msgstr ""
@@ -1475,6 +1478,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1538,6 +1547,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1706,6 +1721,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2021,7 +2039,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2048,6 +2066,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2087,6 +2108,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2138,6 +2162,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2309,6 +2339,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2324,9 +2357,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2525,15 +2555,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2573,7 +2603,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2597,6 +2627,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2762,7 +2795,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -2987,6 +3023,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3761,9 +3800,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3848,6 +3884,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -3899,6 +3938,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4037,6 +4079,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4173,9 +4218,6 @@ msgstr[0] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4185,13 +4227,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4729,6 +4771,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5191,6 +5236,10 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5206,10 +5255,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5227,7 +5282,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5248,6 +5303,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5257,9 +5315,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6058,12 +6113,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6079,12 +6128,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6136,9 +6179,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6179,9 +6219,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6216,6 +6253,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6422,6 +6462,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6548,6 +6597,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6713,6 +6768,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -6966,21 +7024,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -6990,9 +7039,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8241,6 +8287,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8268,12 +8320,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8328,6 +8392,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9013,10 +9080,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9187,6 +9254,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9253,6 +9323,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9374,6 +9447,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -9949,6 +10025,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10052,6 +10131,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10226,7 +10311,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10829,9 +10914,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11375,6 +11457,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11604,6 +11689,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11682,15 +11773,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11703,6 +11785,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11778,6 +11863,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11805,16 +11893,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11835,16 +11920,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -11913,13 +11995,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12106,7 +12188,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12292,6 +12374,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12343,6 +12428,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -12895,6 +12983,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13122,6 +13216,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13215,6 +13312,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13771,6 +13871,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13792,6 +13895,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13807,6 +13916,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14029,6 +14144,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14044,9 +14162,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14071,9 +14186,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14221,6 +14333,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14305,9 +14420,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14749,6 +14861,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14785,12 +14900,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -14857,18 +14966,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -14905,6 +15005,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -14938,9 +15041,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15016,6 +15116,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15446,6 +15549,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16193,9 +16299,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16406,13 +16509,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16562,6 +16665,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16629,6 +16735,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16641,12 +16750,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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"
@@ -16786,9 +16891,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -16984,6 +17086,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17191,6 +17299,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17353,6 +17464,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17722,6 +17836,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17734,6 +17851,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17752,6 +17875,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17764,6 +17896,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17791,6 +17926,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18061,9 +18202,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18499,9 +18637,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18529,6 +18664,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -18892,13 +19030,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -18937,15 +19078,15 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -18957,43 +19098,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19002,6 +19137,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19147,6 +19285,9 @@ msgid "Hide chart"
msgid_plural "Hide charts"
msgstr[0] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19277,9 +19418,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19313,6 +19451,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19436,9 +19577,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19451,15 +19598,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19481,6 +19649,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19550,9 +19721,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19808,9 +19976,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20600,6 +20765,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20618,6 +20786,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20651,7 +20822,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20792,9 +20963,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21063,6 +21240,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21375,6 +21555,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21390,6 +21573,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21406,9 +21595,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21433,9 +21619,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21553,12 +21736,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -21877,9 +22066,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -21925,6 +22111,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -21940,12 +22129,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22003,9 +22186,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22015,9 +22195,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22030,9 +22207,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22327,9 +22501,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22763,7 +22934,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22778,6 +22949,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -22947,9 +23121,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23001,6 +23172,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23727,6 +23901,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24015,10 +24192,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24500,6 +24674,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24587,6 +24764,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24662,6 +24842,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25336,16 +25540,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25360,6 +25567,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25402,9 +25612,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25495,12 +25702,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26022,6 +26223,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26034,9 +26238,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26187,6 +26388,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26208,9 +26412,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26398,6 +26599,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26528,6 +26732,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26537,6 +26744,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26552,6 +26777,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26582,9 +26819,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26603,6 +26849,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26706,6 +26958,9 @@ msgid "On %{end_date}, your trial will end and %{namespace_name} will be limited
msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be limited to %{free_user_limit} members"
msgstr[0] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -26925,6 +27180,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -26946,6 +27207,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27069,6 +27333,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27223,9 +27490,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27439,6 +27712,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27475,6 +27751,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27499,6 +27778,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27716,6 +27998,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27747,9 +28032,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27759,12 +28041,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -27865,7 +28141,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28114,9 +28390,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28192,6 +28465,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -28897,25 +29176,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -28927,18 +29197,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -28996,12 +29260,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29032,9 +29290,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29134,7 +29389,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29350,6 +29605,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29371,10 +29629,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29398,6 +29656,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29419,9 +29680,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -29833,9 +30091,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -29866,6 +30121,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30553,6 +30811,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30565,6 +30826,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30583,6 +30847,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30724,6 +30991,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31534,7 +31804,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31552,7 +31822,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31735,9 +32005,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -31861,9 +32128,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32093,6 +32357,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33446,6 +33713,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33461,6 +33731,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33509,6 +33785,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33555,6 +33834,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33579,6 +33861,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33624,24 +33909,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -33787,9 +34063,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -33872,9 +34145,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -33893,42 +34175,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34022,6 +34280,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34067,12 +34328,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34082,6 +34358,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34651,6 +34939,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -34906,6 +35197,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36288,12 +36582,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36567,6 +36870,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36657,6 +36963,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37041,6 +37350,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37056,9 +37368,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -37797,6 +38106,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39046,6 +39358,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39142,6 +39457,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39154,6 +39475,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39202,10 +39526,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39310,9 +39634,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39664,6 +39985,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39688,7 +40012,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40069,6 +40393,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40081,9 +40408,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40186,6 +40510,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40396,6 +40723,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40423,6 +40753,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40431,6 +40779,12 @@ msgid "Time|min"
msgid_plural "Time|mins"
msgstr[0] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40485,6 +40839,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40647,9 +41004,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40674,6 +41028,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40683,6 +41049,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -40791,9 +41160,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -40818,9 +41196,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -40989,39 +41379,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41127,21 +41490,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41721,6 +42081,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41748,6 +42111,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -41844,10 +42210,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -41913,7 +42279,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42075,6 +42441,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42475,7 +42844,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -42769,10 +43138,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43465,6 +43837,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43546,6 +43924,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43588,6 +43969,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43645,6 +44029,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43663,6 +44050,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43714,6 +44104,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -43923,9 +44316,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -43950,9 +44340,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44040,10 +44427,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44061,7 +44448,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44071,16 +44458,13 @@ msgstr[0] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44089,19 +44473,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
+msgstr ""
+
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44110,19 +44506,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44137,31 +44536,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
+msgstr ""
+
+msgid "WorkItem|Turn off confidentiality"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44384,6 +44792,10 @@ 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 ""
@@ -44444,10 +44856,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44553,10 +44961,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44720,7 +45124,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -44783,6 +45187,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -44894,9 +45301,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -44936,6 +45340,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45086,9 +45496,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45148,6 +45555,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45163,7 +45573,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45367,9 +45777,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45464,6 +45871,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45504,6 +45914,9 @@ msgid "change"
msgid_plural "changes"
msgstr[0] ""
+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 ""
@@ -45552,7 +45965,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -45748,7 +46161,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -45872,9 +46285,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -45911,6 +46321,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46033,6 +46446,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46238,6 +46654,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46336,7 +46755,7 @@ msgid "merge request"
msgid_plural "merge requests"
msgstr[0] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46357,7 +46776,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46518,6 +46937,9 @@ msgid "mrWidget|Mentions issue"
msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46653,9 +47075,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47211,6 +47630,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/is_IS/gitlab.po b/locale/is_IS/gitlab.po
index f353eb5c427..a9fbeb17260 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-08-12 18:03\n"
+"PO-Revision-Date: 2022-09-11 06:21\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/it/gitlab.po b/locale/it/gitlab.po
index 62cc12c7a2e..0b7127ad4ca 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-08-12 18:05\n"
+"PO-Revision-Date: 2022-09-11 06:23\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr " (da %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] "%d contribution"
msgstr[1] "%d contribution"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr "%{size} bytes"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr "%{spammable_titlecase} è stato inviato ad Akismet con successo."
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr "Un ramo predefinito non può essere scelto per un progetto vuoto."
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "Branch"
@@ -6855,6 +6914,9 @@ msgstr "Guarda i files"
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr "Feb"
msgid "February"
msgstr "Febbraio"
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr "Housekeeping iniziato con successo"
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr "Nessuna Repository"
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr "Notifica eventi"
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr "poco fa"
msgid "Timeago|right now"
msgstr "adesso"
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "hr"
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] "min"
msgstr[1] "mins"
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr "s"
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/ja/gitlab.po b/locale/ja/gitlab.po
index c935b49294b..df943c8cfb5 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-08-12 18:05\n"
+"PO-Revision-Date: 2022-09-11 06:23\n"
msgid " %{start} to %{end}"
msgstr " %{start} ã‹ã‚‰ %{end} ã¾ã§"
@@ -22,6 +22,10 @@ msgstr " %{start} ã‹ã‚‰ %{end} ã¾ã§"
msgid " (from %{timeoutSource})"
msgstr " (%{timeoutSource} ã‹ã‚‰)"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+
msgid " Collected %{time}"
msgstr "åŽé›†æ—¥æ™‚: %{time}"
@@ -129,6 +133,10 @@ msgid "%d approver (you've approved)"
msgid_plural "%d approvers (you've approved)"
msgstr[0] "%d 人ã®æ‰¿èªè€… (ã‚ãªãŸã‚‚承èªæ¸ˆ)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] "%d 件ã®ã‚¢ãƒ¼ãƒ†ã‚£ãƒ•ã‚¡ã‚¯ãƒˆ"
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] "%d 件ã®å‰²ã‚Šå½“ã¦æ¸ˆã¿ã®ã‚¤ã‚·ãƒ¥ãƒ¼"
@@ -185,6 +193,10 @@ msgid "%d contribution"
msgid_plural "%d contributions"
msgstr[0] "貢献 %d件"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d æ—¥"
@@ -305,6 +317,10 @@ msgid "%d project selected"
msgid_plural "%d projects selected"
msgstr[0] "%d 件ã®ã‚°ãƒ«ãƒ¼ãƒ—ã‚’é¸æŠžæ¸ˆã¿"
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -333,10 +349,6 @@ msgid "%d tag per image name"
msgid_plural "%d tags per image name"
msgstr[0] "ç”»åƒåã”ã¨ã«%d 件ã®ã‚¿ã‚°"
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] "%d 件ã®ã‚¤ã‚·ãƒ¥ãƒ¼ãŒæœªå‰²å½“"
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] "%d 件ã®æœªè§£æ±ºã‚¹ãƒ¬ãƒƒãƒ‰"
@@ -385,9 +397,6 @@ msgstr "%{actionText} ãã—㦠%{noteable} ã‚’å†ã³é–‹ã"
msgid "%{address} is an invalid IP address range"
msgstr "%{address} ã¯ç„¡åŠ¹ãªIPアドレス範囲ã§ã™"
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr "%{author_link} ㌠%{original_issue} ã‚’ %{new_issue} ã«è¤‡è£½ã—ã¾ã—ãŸã€‚"
@@ -806,12 +815,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -913,6 +916,9 @@ msgstr "%{size} ãƒã‚¤ãƒˆ"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr "%{sourceBranch} ã‚’ %{targetBranch} ã«"
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr "%{spammable_titlecase} ã¯Akismetã«æ­£å¸¸ã«é€ä¿¡ã•ã‚Œã¾ã—ãŸã€‚"
@@ -932,6 +938,10 @@ msgstr[0] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr "%{strongOpen}警告:%{strongClose} SAML グループ リンクã«ã‚ˆã‚Šã€GitLab ã¯ã‚°ãƒ«ãƒ¼ãƒ—ã‹ã‚‰ãƒ¡ãƒ³ãƒãƒ¼ã‚’自動的ã«å‰Šé™¤ã—ã¾ã™ã€‚"
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -958,9 +968,6 @@ msgstr[0] "マージã™ã‚‹ãŸã‚ã« %{strong_start}%{count} 人ã®ãƒ¡ãƒ³ãƒãƒ¼%{
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] "%{strong_start}%{release_count}%{strong_end} リリース"
@@ -1165,10 +1172,6 @@ msgstr "(除去ã—ã¾ã—ãŸ)"
msgid "(revoked)"
msgstr "(失効)"
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] "(%d 個ã®ã‚³ãƒŸãƒƒãƒˆã‚’スカッシュ)"
-
msgid "(this user)"
msgstr "(ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼)"
@@ -1475,6 +1478,12 @@ msgstr "Kotlin Native を使用ã—㦠Linux プログラムを開発ã™ã‚‹ãŸã‚
msgid "A complete DevOps platform"
msgstr "完全㪠DevOps ã®ãƒ—ラットフォーム"
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr "プロジェクトãŒç©ºã®å ´åˆã¯ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã®ãƒ–ランãƒã‚’é¸æŠžã§ãã¾ã›ã‚“。"
@@ -1538,6 +1547,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr "公開エピックã®è¦ªã‚¨ãƒ”ックã«éžå…¬é–‹ã‚¨ãƒ”ックã«å‰²ã‚Šå½“ã¦ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“"
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1706,6 +1721,9 @@ msgstr "folder/openapi.json"
msgid "AWS Access Key"
msgstr "AWS アクセスキー"
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr "AWS 秘密アクセスキー"
@@ -1809,7 +1827,7 @@ msgid "AccessTokens|Copy feed token"
msgstr ""
msgid "AccessTokens|Copy incoming email token"
-msgstr ""
+msgstr "å—信メールトークンをコピー"
msgid "AccessTokens|Copy static object token"
msgstr ""
@@ -2021,8 +2039,8 @@ msgstr "ã“ã®è¡Œã«ã‚³ãƒ¡ãƒ³ãƒˆã‚’追加ã™ã‚‹ã‹ã€è¤‡æ•°è¡Œã‚’ドラッグã—
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
-msgstr "インスタンスã®å…±æœ‰ãƒ©ãƒ³ãƒŠãƒ¼ã«ã¤ã„ã¦ã®è©³ç´°ã‚’記載ã—ãŸã‚«ã‚¹ã‚¿ãƒ ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’追加ã—ã¾ã™ã€‚ ã“ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã¯ã€ã‚°ãƒ«ãƒ¼ãƒ—ãŠã‚ˆã³ãƒ—ロジェクト CI/CD ã®è¨­å®šã‚„ Runner セクションã§è¡¨ç¤ºã•ã‚Œã¾ã™ã€‚Markdown ãŒã‚µãƒãƒ¼ãƒˆã•ã‚Œã¦ã„ã¾ã™ã€‚"
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
+msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
msgstr "ã“ã® %{noteableDisplayName} ã¸ä¸€èˆ¬çš„ãªã‚³ãƒ¡ãƒ³ãƒˆã‚’追加。"
@@ -2048,6 +2066,9 @@ msgstr "サービスデスクã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã«ã‚µãƒ•ã‚£ãƒƒã‚¯ã‚¹ã‚’追
msgid "Add a table"
msgstr "テーブルを追加ã™ã‚‹"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr "タイトルを追加..."
@@ -2087,6 +2108,9 @@ msgstr "コメントã™ã‚‹"
msgid "Add comment to design"
msgstr "デザインã«ã‚³ãƒ¡ãƒ³ãƒˆã‚’追加"
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr "コメントを追加..."
@@ -2138,6 +2162,12 @@ msgstr "キーを追加"
msgid "Add label(s)"
msgstr "ラベルを追加"
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr "リストを追加"
@@ -2309,6 +2339,9 @@ msgstr "%{labels} %{label_text} を追加。"
msgid "Adds a Zoom meeting."
msgstr "Zoom ミーティングを追加。"
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr "Todoを追加ã—ã¾ã™ã€‚"
@@ -2324,9 +2357,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr "GitLab UI ã®æ›´æ–°é »åº¦ã‚’調整ã—ã¾ã™ã€‚"
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr "上記ã®ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼/検索æ¡ä»¶ã‚’調整ã—ã¦ãã ã•ã„。ã“ã‚ŒãŒã‚¨ãƒ©ãƒ¼ã¨è€ƒãˆã‚‰ã‚Œã‚‹å ´åˆã¯ã€ %{linkStart}Geo Troubleshooting%{linkEnd} ドキュメントをå‚ç…§ã—ã¦ãã ã•ã„。"
-
msgid "Admin"
msgstr "管ç†è€…"
@@ -2525,15 +2555,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr "Auto DevOps ドメイン"
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2573,7 +2603,7 @@ msgstr "登録機能を有効ã«ã™ã‚‹"
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2597,6 +2627,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2762,7 +2795,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -2987,6 +3023,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr "ã‚ãªãŸã§ã™ï¼"
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3761,9 +3800,6 @@ msgstr "ユーザーãŒGitLabã‚’OAuthプロãƒã‚¤ãƒ€ãƒ¼ã¨ã—ã¦ä½¿ç”¨ã™ã‚‹ã‚ã
msgid "Allowed"
msgstr "許å¯"
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr "メールドメイン制é™ã¯ã€æœ€ä¸Šä½ã‚°ãƒ«ãƒ¼ãƒ—ã«ã®ã¿è¨±å¯ã•ã‚Œã¾ã™"
@@ -3848,6 +3884,9 @@ msgstr "アプリケーション㮠%{link_to_client} ãŒã‚ãªãŸã® GitLab ã‚¢
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr "GitLab ユーザフィールドãŒç©ºã®å ´åˆã€ã™ã¹ã¦ã®ã‚¤ã‚·ãƒ¥ãƒ¼ã¨ã‚³ãƒ¡ãƒ³ãƒˆã®èª¬æ˜Žã« FogBugz ユーザã®ãƒ•ãƒ«ãƒãƒ¼ãƒ  (例ãˆã°ã€By John Smith)を追加ã—ã¾ã™ã€‚ã¾ãŸã€ã“れらã®å•é¡Œã‚„コメントをプロジェクト作æˆè€…ã«é–¢é€£ä»˜ã‘ã‚‹ã‹ã€ã¾ãŸã¯å‰²ã‚Šå½“ã¦ã¾ã™ã€‚"
@@ -3899,6 +3938,9 @@ msgstr "承èªè€…ã®è¿½åŠ ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred while adding formatted title for epic"
msgstr "エピックã®ãƒ•ã‚©ãƒ¼ãƒžãƒƒãƒˆæ¸ˆã¿ã‚¿ã‚¤ãƒˆãƒ«ã‚’追加ã™ã‚‹éš›ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4037,6 +4079,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr "ã™ã¹ã¦ã®ãƒ•ã‚¡ã‚¤ãƒ«ã®èª­è¾¼ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr "ブランãƒã®ãƒ«ãƒ¼ãƒ«ã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
+
msgid "An error occurred while loading chart data"
msgstr "グラフデータã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
@@ -4173,9 +4218,6 @@ msgstr[0] "設定ã®ä¿å­˜ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr "通知ã®è³¼èª­ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
-
msgid "An error occurred while triggering the job."
msgstr "ジョブã®ãƒˆãƒªã‚¬ãƒ¼ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
@@ -4185,15 +4227,15 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
+msgid "An error occurred while trying to render the content editor. Please try again."
+msgstr ""
+
msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
-msgstr "通知ã®è³¼èª­ã‚’解除中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
-
msgid "An error occurred while updating approvers"
msgstr "承èªè€…ã®æ›´æ–°ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
@@ -4729,6 +4771,9 @@ msgstr "承èªã™ã‚‹"
msgid "Approve a merge request"
msgstr "マージリクエストを承èªã™ã‚‹"
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr "ç¾åœ¨ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’承èªã™ã‚‹ã€‚"
@@ -5191,6 +5236,10 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr "ユーザーイベント"
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5206,10 +5255,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5227,7 +5282,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5248,6 +5303,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5257,9 +5315,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6058,12 +6113,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6079,12 +6128,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6136,9 +6179,6 @@ msgstr "Explore all plans"
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6179,9 +6219,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6216,6 +6253,9 @@ msgstr "ブロックã•ã‚ŒãŸã‚¤ã‚·ãƒ¥ãƒ¼"
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6422,6 +6462,15 @@ msgstr "å…¨ã¦è¡¨ç¤º"
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6548,6 +6597,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "ブランãƒ"
@@ -6713,6 +6768,9 @@ msgstr "ファイルを表示"
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr "アーティファクトã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
@@ -6966,21 +7024,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr "共有ランナーã®åˆ©ç”¨æ™‚é–“ã¯ã€å…±æœ‰ãƒ©ãƒ³ãƒŠãƒ¼ã§å®Ÿè¡Œã•ã‚ŒãŸã™ã¹ã¦ã®ã‚¸ãƒ§ãƒ–ã®å®Ÿè¡Œæ™‚é–“ã®åˆè¨ˆã§ã™"
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -6990,9 +7039,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr "共有ランナーã®åˆ©ç”¨æ™‚é–“ã¨ã¯?"
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8241,6 +8287,12 @@ msgstr "ã“ã® %{quick_action_target} ã‚’é–‰ã˜ã‚‹ã€‚"
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8268,12 +8320,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8328,6 +8392,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9013,10 +9080,10 @@ msgstr "クラスターã®èªè¨¼ã«ä½¿ç”¨ã•ã‚Œã‚‹Kubernetes証明書。"
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr "Kubernetes APIã¸ã®ã‚¢ã‚¯ã‚»ã‚¹ã«ä½¿ç”¨ã•ã‚Œã‚‹URL。"
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9187,6 +9254,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr "ジョブを折りãŸãŸã‚€"
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr "マイルストーンを折りãŸãŸã‚€"
@@ -9253,6 +9323,9 @@ msgstr "コメントã—ã¦ã€ã‚¹ãƒ¬ãƒƒãƒ‰ã‚’未解決ã«ã™ã‚‹"
msgid "Comment '%{label}' position"
msgstr "コメント '%{label}' ã®ä½ç½®"
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr "コメントフォームã®ä½ç½®"
@@ -9374,6 +9447,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr "リビジョンを比較"
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr "変更を比較"
@@ -9949,6 +10025,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr "Docker接続エラー"
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr "有効期é™ãƒãƒªã‚·ãƒ¼ã‚’有効ã«ã™ã‚‹"
@@ -10052,6 +10131,12 @@ msgstr "クリーンアップã®å®Ÿè¡Œ:"
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr "コンテナレジストリã‹ã‚‰ã‚¿ã‚°ã‚’自動的ã«å‰Šé™¤ã—ã€å¿…è¦ãªã‚¿ã‚°ã‚’ä¿æŒã™ã‚‹ã“ã¨ã§ã‚¹ãƒˆãƒ¬ãƒ¼ã‚¸ã‚¹ãƒšãƒ¼ã‚¹ã‚’節約ã§ãã¾ã™ã€‚ %{linkStart}クリーンアップã¯ã©ã®ã‚ˆã†ã«æ©Ÿèƒ½ã—ã¾ã™ã‹ï¼Ÿ%{linkEnd}ã‚’å‚ç…§ã—ã¦ãã ã•ã„。"
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10226,7 +10311,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10829,9 +10914,6 @@ msgstr "グループを作æˆ"
msgid "Create, update, or delete a merge request."
msgstr "マージリクエストを作æˆã€æ›´æ–°ã€ã¾ãŸã¯å‰Šé™¤ã—ã¾ã™ã€‚"
-msgid "Create/import your first project"
-msgstr "最åˆã®ãƒ—ロジェクトを作æˆ/インãƒãƒ¼ãƒˆ"
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr "ã‚ãªãŸã¯ã€ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã«ã‚µãƒ–グループを作æˆã™ã‚‹æ¨©é™ãŒã‚ã‚Šã¾ã›ã‚“。"
@@ -11375,6 +11457,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr "完了ã¾ã§ã®å¹³å‡æ™‚é–“ (æ—¥)"
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11604,6 +11689,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11682,15 +11773,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11703,6 +11785,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11778,6 +11863,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11805,16 +11893,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11835,16 +11920,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site name"
-msgstr ""
-
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -11913,13 +11995,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12106,7 +12188,7 @@ msgstr "æ—¥"
msgid "Days to merge"
msgstr "マージã¾ã§ã®æ—¥æ•°"
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12292,6 +12374,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12343,6 +12428,9 @@ msgstr "スニペットを削除?"
msgid "Delete source branch"
msgstr "ソースブランãƒã‚’削除"
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -12895,6 +12983,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13122,6 +13216,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr "コメントã®ç ´æ£„"
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13215,6 +13312,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13771,6 +13871,9 @@ msgstr "ドラフト"
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13792,6 +13895,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13807,6 +13916,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14029,6 +14144,9 @@ msgstr "Wikiページã®ç·¨é›†"
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr "スレッド内ã®æœ€æ–°ã®ã‚ãªãŸã®ã‚³ãƒ¡ãƒ³ãƒˆã‚’編集 (空ã®ãƒ†ã‚­ã‚¹ãƒˆé ˜åŸŸã‹ã‚‰)"
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14044,9 +14162,6 @@ msgstr "編集中"
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr "Elasticsearch AWS IAMèªè¨¼æƒ…å ±"
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14071,9 +14186,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr "ãªã—。インデックスã«åå‰ç©ºé–“ã‚’é¸æŠžã—ã¦ãã ã•ã„"
@@ -14221,6 +14333,9 @@ msgstr "Auto DevOps を有効ã«ã™ã‚‹"
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14305,9 +14420,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr "メールã®ãƒ˜ãƒƒãƒ€ãƒ¼ã¨ãƒ•ãƒƒã‚¿ãƒ¼ã‚’有効ã«ã™ã‚‹"
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14749,6 +14861,9 @@ msgstr "エピック"
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr "エピックãŒã‚ã‚Šã¾ã›ã‚“。"
@@ -14785,12 +14900,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr "%{epicDateType} ã®ä¿å­˜ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr "本当㫠%{bStart}%{parentEpicTitle}%{bEnd} ã‹ã‚‰ %{bStart}%{targetIssueTitle}%{bEnd} を削除ã—ã¾ã™ã‹ï¼Ÿ"
@@ -14857,18 +14966,9 @@ msgstr "ã“ã®ã‚¨ãƒ”ックã¨ã“ã‚Œå«ã¾ã‚Œã¦ã„ã‚‹å­ã‚¨ãƒ”ックã¯éžå…¬é–‹
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} 以下ã®å…¨ã¦ã®èª²é¡Œã‚’削除ã—ã¾ã™ã€‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr "マイルストーンã«åŸºã¥ã„ãŸã‚¨ãƒ”ック㮠%{epicDateType} をスケジューリングã™ã‚‹ã«ã¯ã€ã‚¨ãƒ”ックã®ã‚¤ã‚·ãƒ¥ãƒ¼ã« %{epicDateType} ã®ãƒžã‚¤ãƒ«ã‚¹ãƒˆãƒ¼ãƒ³ã‚’割り当ã¦ã¾ã™ã€‚"
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr "期日"
-
-msgid "Epics|start"
-msgstr "開始"
-
msgid "Erased"
msgstr ""
@@ -14905,6 +15005,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr "プロジェクトã®å‰Šé™¤ã§ã‚¨ãƒ©ãƒ¼ãŒã‚ã‚Šã¾ã™ã€‚エラーã®è©³ç´°ã«ã¤ã„ã¦ã¯ãƒ­ã‚°ã‚’確èªã—ã¦ãã ã•ã„。"
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -14938,9 +15041,6 @@ msgstr "ブランãƒã®ãƒ­ãƒ¼ãƒ‰ã«å¤±æ•—ã—ã¾ã—ãŸã€‚"
msgid "Error loading burndown chart data"
msgstr "ãƒãƒ¼ãƒ³ãƒ€ã‚¦ãƒ³ãƒãƒ£ãƒ¼ãƒˆãƒ‡ãƒ¼ã‚¿ã®èª­ã¿è¾¼ã¿ã‚¨ãƒ©ãƒ¼"
-msgid "Error loading countries data."
-msgstr "国データã®èª­ã¿è¾¼ã¿ã‚¨ãƒ©ãƒ¼ã€‚"
-
msgid "Error loading file viewer."
msgstr "ファイルビューワーã®èª­ã¿è¾¼ã¿ã«å¤±æ•—ã—ã¾ã—ãŸã€‚"
@@ -15016,6 +15116,9 @@ msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ユーザーã¯ãƒ­ãƒƒã‚¯è§£é™¤ã•ã‚Œã¾
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15446,6 +15549,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr "マイルストーンを展開"
@@ -16193,9 +16299,6 @@ msgstr "2月"
msgid "February"
msgstr "2月"
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16406,13 +16509,13 @@ msgstr "固定:"
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16562,6 +16665,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16629,6 +16735,9 @@ msgstr "頻度"
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr "金曜日"
@@ -16641,12 +16750,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr "%{providerTitle} ã‹ã‚‰"
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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"
@@ -16786,9 +16891,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -16984,6 +17086,12 @@ msgstr "未確èª"
msgid "Geo|Next sync scheduled at"
msgstr "次回ã®åŒæœŸäºˆå®šæ—¥æ™‚"
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17191,6 +17299,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17353,6 +17464,9 @@ msgstr ""
msgid "Git version"
msgstr "Git ãƒãƒ¼ã‚¸ãƒ§ãƒ³"
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17722,6 +17836,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr "グローãƒãƒ« ショートカット"
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr "グローãƒãƒ«é€šçŸ¥è¨­å®š"
@@ -17734,6 +17851,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17752,6 +17875,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17764,6 +17896,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17791,6 +17926,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18061,9 +18202,6 @@ msgstr "Gravatar を有効化"
msgid "Group"
msgstr "グループ"
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18499,9 +18637,6 @@ msgstr "グループオーナーã«ã‚ˆã£ã¦ä¸Šæ›¸ãã•ã‚Œãªã„é™ã‚Šã€ã™ã¹
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr "Auto DevOps パイプラインãŒã‚°ãƒ«ãƒ¼ãƒ—用ã«æ›´æ–°ã•ã‚Œã¾ã—ãŸ"
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr "トップレベルã®ã‚°ãƒ«ãƒ¼ãƒ—ã®ã¿ã§ä½¿ç”¨ã§ãã¾ã™ã€‚ã™ã¹ã¦ã®ã‚µãƒ–グループã«é©ç”¨ã•ã‚Œã¾ã™ã€‚%{group}ã®å¤–ã®ã‚°ãƒ«ãƒ¼ãƒ—ã¨æ—¢ã«å…±æœ‰ã—ã¦ã„るグループã¯ã€æ‰‹å‹•ã§å‰Šé™¤ã—ãªã„é™ã‚Šã€å…±æœ‰ã•ã‚ŒãŸã¾ã¾ã§ã™ã€‚"
@@ -18529,6 +18664,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr "カスタムプロジェクトテンプレート"
@@ -18892,14 +19030,17 @@ msgstr "ガイドライン"
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
-msgstr "HTTP Basic: ã‚¢ã‚¯ã‚»ã‚¹æ‹’å¦ \\n Git over HTTPã«ã¯ 'api' スコープã®ãƒ‘ーソナルアクセストークンを使用ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚\\n %{profile_personal_access_tokens_url} ã§ç”Ÿæˆã§ãã¾ã™"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
+msgstr ""
msgid "Harbor Registry"
msgstr ""
@@ -18937,73 +19078,70 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
+msgstr[0] "%d 件ã®ã‚¢ãƒ¼ãƒ†ã‚£ãƒ•ã‚¡ã‚¯ãƒˆ"
+
msgid "HarborRegistry|%{count} Image repository"
msgid_plural "HarborRegistry|%{count} Image repositories"
-msgstr[0] ""
+msgstr[0] "%{count} 件ã®ã‚¤ãƒ¡ãƒ¼ã‚¸ãƒªãƒã‚¸ãƒˆãƒª"
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
-msgstr[0] ""
-
-msgid "HarborRegistry|Configuration digest: %{digest}"
-msgstr ""
+msgid "HarborRegistry|-- artifacts"
+msgstr "-- アーティファクト"
msgid "HarborRegistry|Digest: %{imageId}"
-msgstr ""
+msgstr "ダイジェスト: %{imageId}"
msgid "HarborRegistry|Harbor Registry"
-msgstr ""
+msgstr "Harbor Registry"
msgid "HarborRegistry|Harbor connection error"
-msgstr ""
-
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
-msgstr ""
-
-msgid "HarborRegistry|Manifest digest: %{digest}"
-msgstr ""
+msgstr "Harbor 接続エラー"
msgid "HarborRegistry|Please try different search criteria"
-msgstr ""
+msgstr "検索æ¡ä»¶ã‚’変ãˆã¦è©¦ã—ã¦ãã ã•ã„"
msgid "HarborRegistry|Published %{timeInfo}"
-msgstr ""
-
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
-msgstr ""
+msgstr "%{timeInfo} ã«å…¬é–‹"
msgid "HarborRegistry|Root image"
-msgstr ""
+msgstr "ルートイメージ"
-msgid "HarborRegistry|Sorry, your filter produced no results."
-msgstr ""
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
+msgstr "アーティファクトã®ãƒªã‚¹ãƒˆã®å–得中ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
+msgstr "リãƒã‚¸ãƒˆãƒªãƒªã‚¹ãƒˆã®å–得中ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
-msgid "HarborRegistry|The image repository could not be found."
-msgstr ""
+msgid "HarborRegistry|Something went wrong while fetching the tags."
+msgstr "ã‚¿ã‚°ã®å–得中ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
-msgid "HarborRegistry|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 ""
+msgid "HarborRegistry|Sorry, your filter produced no results."
+msgstr "申ã—訳ã‚ã‚Šã¾ã›ã‚“。ã‚ãªãŸãŒæŒ‡å®šã—ãŸãƒ•ã‚£ãƒ«ã‚¿ãƒ¼ã§ã¯è©²å½“ã™ã‚‹çµæžœãŒã‚ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚"
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
-msgstr ""
+msgid "HarborRegistry|Tag"
+msgstr "ã‚¿ã‚°"
+
+msgid "HarborRegistry|The filter returned no results"
+msgstr "ã“ã®ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼ã«è©²å½“ã™ã‚‹çµæžœã¯ã‚ã‚Šã¾ã›ã‚“。"
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
+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 troubleshooting documentation%{docLinkEnd}."
-msgstr ""
+msgstr "Harbor Registryã¸ã®æŽ¥ç¶šã§å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ページを更新ã—ã¦ã¿ã¦ãã ã•ã„。 ã“ã®ã‚¨ãƒ©ãƒ¼ãŒè§£æ±ºã—ãªã„å ´åˆã¯ã€ %{docLinkStart}トラブルシューティングã®æ–‡æ›¸%{docLinkEnd}ã‚’ã”覧ãã ã•ã„。"
+
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr "Harbor Registry ã§ã¯ã€å„プロジェクト㯠Harbor 空間ã«æŽ¥ç¶šã—ã¦ãã® Dockerイメージを格ç´ã§ãã¾ã™ã€‚"
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
-msgstr ""
+msgstr "Harbor Registry ã§ã¯ã€å„プロジェクトã¯ã‚¤ãƒ¡ãƒ¼ã‚¸ã‚’æ ¼ç´ã™ã‚‹å ´æ‰€ã‚’æŒã¦ã¾ã™ã€‚%{docLinkStart}詳細ã¯ã“ã¡ã‚‰%{docLinkEnd}"
msgid "Hashed Storage must be enabled to use Geo"
msgstr ""
@@ -19147,6 +19285,9 @@ msgid "Hide chart"
msgid_plural "Hide charts"
msgstr[0] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19277,9 +19418,6 @@ msgstr "ãƒã‚¦ã‚¹ã‚­ãƒ¼ãƒ”ングã¯æ­£å¸¸ã«èµ·å‹•ã—ã¾ã—ãŸã€‚"
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19313,6 +19451,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr "%{terms_link} ã«åŒæ„ã™ã‚‹"
@@ -19436,9 +19577,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19451,15 +19598,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19481,6 +19649,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19550,9 +19721,6 @@ msgstr "GitHub を使用ã—ã¦ã„ã‚‹å ´åˆã€GitHub 上ã®ã‚³ãƒŸãƒƒãƒˆã‚„プルã
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19808,9 +19976,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20600,6 +20765,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20618,6 +20786,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20651,8 +20822,8 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
-msgstr "LFSオブジェクトをå«ã¿ã¾ã™ã€‚グループã”ã¨ã€ã¾ãŸã¯ãƒ—ロジェクトã”ã¨ã«ã‚ªãƒ¼ãƒãƒ¼ãƒ©ã‚¤ãƒ‰ã§ãã¾ã™ã€‚無制é™ã‚’指定ã™ã‚‹å ´åˆã¯0ã«ã—ã¾ã™ã€‚"
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
+msgstr ""
msgid "Includes an MVC structure to help you get started"
msgstr ""
@@ -20792,9 +20963,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr "候補を挿入ã™ã‚‹"
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr "プロジェクト㌠insights.yml ファイル内ã§é™¤å¤–ã•ã‚Œã„ã‚‹ãŸã‚ã€ã„ãã¤ã‹ã®é …ç›®ãŒè¡¨ç¤ºã•ã‚Œã¾ã›ã‚“(詳細ã«ã¤ã„ã¦ã¯ projects.only設定をå‚ç…§ã—ã¦ãã ã•ã„)。"
@@ -21063,6 +21240,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21375,6 +21555,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21390,6 +21573,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21406,9 +21595,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21433,9 +21619,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21553,12 +21736,18 @@ msgstr "ライセンスシートã®ä½¿ç”¨:"
msgid "Is using seat"
msgstr "シート利用中"
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr "完了"
msgid "IssuableStatus|Closed (%{link})"
msgstr "完了(%{link})"
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr "é‡è¤‡"
@@ -21877,9 +22066,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -21925,6 +22111,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -21940,12 +22129,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22003,9 +22186,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22015,9 +22195,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22030,9 +22207,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22327,9 +22501,6 @@ msgstr "ジョブã®å‰Šé™¤ã«æˆåŠŸã—ã¾ã—ãŸã€‚"
msgid "Job has wrong arguments format."
msgstr "ジョブã®å¼•æ•°ãƒ•ã‚©ãƒ¼ãƒžãƒƒãƒˆé–“é•ã£ã¦ã„ã¾ã™"
-msgid "Job is missing the `model_type` argument."
-msgstr "ジョブ㫠`model_type`引数ãŒã‚ã‚Šã¾ã›ã‚“。"
-
msgid "Job is stuck. Check runners."
msgstr "ジョブãŒæºœã¾ã£ã¦ã„ã¾ã™ã€‚Runner を確èªã—ã¦ãã ã•ã„。"
@@ -22763,8 +22934,8 @@ 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."
-msgstr "ラベルã¯ã‚¤ã‚·ãƒ¥ãƒ¼ã¨ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã«é©ç”¨ã§ãã¾ã™ã€‚"
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
+msgstr ""
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -22778,6 +22949,9 @@ msgstr "ラベルã®æ˜‡æ ¼"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr "%{labelTitle} を宣言ã™ã‚‹ã¨ %{groupName} 内ã®ã™ã¹ã¦ã®ãƒ—ロジェクト内ã§åˆ©ç”¨å¯èƒ½ã«ãªã‚Šã¾ã™ã€‚åŒã˜ã‚¿ã‚¤ãƒˆãƒ«ã®æ—¢å­˜ã®ãƒ—ロジェクトラベルã¯ãƒžãƒ¼ã‚¸ã•ã‚Œã¾ã™ã€‚ã‚‚ã—åŒã˜ã‚¿ã‚¤ãƒˆãƒ«ã®ã‚°ãƒ«ãƒ¼ãƒ—ラベルãŒã‚ã‚‹å ´åˆã€ãã‚Œã¯ãƒžãƒ¼ã‚¸ã•ã‚Œã¾ã™ã€‚ã“ã®æ“作ã¯å…ƒã«æˆ»ã™ã“ã¨ã¯ã§ãã¾ã›ã‚“。"
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr "言語"
@@ -22947,9 +23121,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr "GitLab ã‚’å­¦ã¶"
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23001,6 +23172,9 @@ msgstr "グループレベルプロジェクトテンプレートã®è©³ç´°"
msgid "Learn more about groups."
msgstr "グループã«ã¤ã„ã¦è©³ã—ã知る"
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23727,6 +23901,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24015,10 +24192,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr "パーソナルアクセストークンã®æœ€é•·è¨±å®¹å¯¿å‘½(日数)"
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24500,6 +24674,9 @@ msgstr ""
msgid "Merge requests"
msgstr "マージリクエスト"
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr "マージリクエストã¨ã¯ã€ãƒ—ロジェクトã«åŠ ãˆãŸå¤‰æ›´ã‚’æ示ã—ã€ãã®å¤‰æ›´ã«ã¤ã„ã¦ä»–ã®ãƒ¡ãƒ³ãƒãƒ¼ã¨è©±ã—åˆã†ãŸã‚ã®å ´æ‰€ã§ã™"
@@ -24587,6 +24764,9 @@ msgstr "下書ãコメントã®ä¿å­˜ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr "コメントã®ä¿å­˜ã«å¤±æ•—ã—ã¾ã—ãŸ"
@@ -24662,6 +24842,30 @@ msgstr "ファイルãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr "マージ済ã¿"
@@ -25336,16 +25540,19 @@ msgstr "コミットメッセージを修正"
msgid "Modify merge commit"
msgstr "マージコミットを変更"
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr "月曜日"
msgid "Monitor"
msgstr "監視"
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25360,6 +25567,9 @@ msgstr ""
msgid "Months"
msgstr "月"
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25402,9 +25612,6 @@ msgstr ""
msgid "Most stars"
msgstr "スター数ã®å¤šã„é †"
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr "%{mounted_as} ã®ãƒžã‚¦ãƒ³ãƒˆãƒã‚¤ãƒ³ãƒˆãŒ%{model_class} 内ã«è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
-
msgid "Move"
msgstr "移動"
@@ -25495,12 +25702,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr "複数ã®ãƒ¢ãƒ‡ãƒ«ã‚¿ã‚¤ãƒ—ãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸ: %{model_types}"
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr "複数ã®ã‚¢ãƒƒãƒ—ローダーãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸ: %{uploader_types}"
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26022,6 +26223,9 @@ msgstr ""
msgid "No credit card required."
msgstr "クレジット カードã¯å¿…è¦ã‚ã‚Šã¾ã›ã‚“"
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr "データãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
@@ -26034,9 +26238,6 @@ msgstr ""
msgid "No deployments found"
msgstr "デプロイãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
-msgid "No due date"
-msgstr "期é™ãªã—"
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26187,6 +26388,9 @@ msgstr "リãƒã‚¸ãƒˆãƒªãŒã‚ã‚Šã¾ã›ã‚“"
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26208,9 +26412,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr "ã‚ãªãŸã®æ¤œç´¢ã«ãƒžãƒƒãƒã—ãŸãŠæ°—ã«å…¥ã‚Šã¯ã‚ã‚Šã¾ã›ã‚“"
-msgid "No start date"
-msgstr "開始日ãªã—"
-
msgid "No suggestions found"
msgstr ""
@@ -26398,6 +26599,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr "プレビューã§ãã‚‹ã‚‚ã®ã¯ä½•ã‚‚ã‚ã‚Šã¾ã›ã‚“。"
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr "イベント通知"
@@ -26528,6 +26732,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26537,6 +26744,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26552,6 +26777,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26582,9 +26819,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26603,6 +26849,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26706,6 +26958,9 @@ msgid "On %{end_date}, your trial will end and %{namespace_name} will be limited
msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be limited to %{free_user_limit} members"
msgstr[0] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr "順調"
@@ -26925,6 +27180,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -26946,6 +27207,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr "例:ログインページã§SQLインジェクションã®ãƒ†ã‚¹ãƒˆã‚’ã—ã¾ã™"
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27069,6 +27333,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27223,9 +27490,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr "オープン"
@@ -27439,6 +27712,9 @@ msgstr ""
msgid "Package already exists"
msgstr "パッケージã¯ã™ã§ã«ã‚ã‚Šã¾ã™"
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr "パッケージã¯æ­£å¸¸ã«å‰Šé™¤ã•ã‚Œã¾ã—ãŸ"
@@ -27475,6 +27751,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27499,6 +27778,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27716,6 +27998,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27747,9 +28032,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr "パッケージを削除"
@@ -27759,12 +28041,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr "RubyGems"
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -27865,8 +28141,8 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
-msgstr "パッケージã¨ãƒ¬ã‚¸ã‚¹ãƒˆãƒª"
+msgid "Packages and registries"
+msgstr ""
msgid "Page not found"
msgstr "ページãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
@@ -28114,9 +28390,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr "GitLabプロジェクトã§ä¸€èˆ¬çš„ãªæ“作を実施"
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr "パフォーマンスã®æœ€é©åŒ–"
@@ -28192,6 +28465,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -28897,25 +29176,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -28927,18 +29197,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr "マージçµæžœãƒ‘イプライン"
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr "パイプライン"
@@ -28996,12 +29260,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29032,9 +29290,6 @@ msgstr "変数"
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29134,7 +29389,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr "é›»å­ãƒ¡ãƒ¼ãƒ«ï¼ˆ%{email})をãƒã‚§ãƒƒã‚¯ã—ã¦ã€ã“ã®ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’所有ã—ã¦ã„ã‚‹ã“ã¨ã‚’確èªã—ã€CI/CDã®ãƒ­ãƒƒã‚¯ã‚’解除ã—ã¦ãã ã•ã„。電å­ãƒ¡ãƒ¼ãƒ«ã‚’å—ã‘å–ã£ã¦ã„ãªã„? %{resend_link} メールアドレスを間é•ã£ã¦ã„ã¾ã›ã‚“ã‹ï¼Ÿ %{update_link}"
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29350,6 +29605,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29371,12 +29629,12 @@ msgstr "挙動"
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr "固定(最大1280ピクセル)ã¾ãŸã¯æµå‹•çš„(%{percentage})ã®ã„ãšã‚Œã‹ã®ã‚¢ãƒ—リケーションレイアウトをé¸æŠžã—ã¦ãã ã•ã„。"
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
+msgstr ""
+
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr "プロジェクトã®æ¦‚è¦ãƒšãƒ¼ã‚¸ã«è¡¨ç¤ºã—ãŸã„コンテンツをé¸æŠžã—ã¾ã™ã€‚"
-msgid "Preferences|Choose what content you want to see on your homepage."
-msgstr "ホームページã§è¡¨ç¤ºã—ãŸã„コンテンツをé¸æŠžã—ã¾ã™ã€‚"
-
msgid "Preferences|Color for added lines"
msgstr ""
@@ -29398,6 +29656,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29419,9 +29680,6 @@ msgstr "例: 30分å‰"
msgid "Preferences|Gitpod"
msgstr "Gitpod"
-msgid "Preferences|Homepage content"
-msgstr "ホームページã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„"
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr "ã™ã¹ã¦ã®å¤‰æ›´ã®ã‚るファイルを表示ã™ã‚‹ä»£ã‚ã‚Šã«ã€ä¸€åº¦ã«1ã¤ã®ãƒ•ã‚¡ã‚¤ãƒ«ã®ã¿ã‚’表示ã—ã¾ã™ã€‚ファイルを切り替ãˆã‚‹ã«ã¯ã€ãƒ•ã‚¡ã‚¤ãƒ«ãƒ–ラウザを使用ã—ã¾ã™ã€‚"
@@ -29833,9 +30091,6 @@ msgstr "å½¹è·"
msgid "Profiles|Key"
msgstr "キー"
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -29866,6 +30121,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr "通知メール"
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr "組織"
@@ -30553,6 +30811,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30565,6 +30826,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr "全員"
@@ -30583,6 +30847,9 @@ msgstr "æ—©é€ã‚Šãƒžãƒ¼ã‚¸"
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30724,6 +30991,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31534,7 +31804,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr "%{environment_name} ã¯é–‹ç™ºè€…権é™ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒæ›¸ãè¾¼ã¿å¯èƒ½ã«ãªã‚Šã¾ã™ã€‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31552,7 +31822,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31735,9 +32005,6 @@ msgstr "プッシュイベント"
msgid "Push project from command line"
msgstr "コマンドラインã‹ã‚‰ãƒ—ロジェクトをプッシュ"
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -31861,9 +32128,6 @@ msgstr ""
msgid "Quick range"
msgstr "クイックレンジ"
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32093,6 +32357,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr "2è¦ç´ èªè¨¼ã‚¢ãƒ—リã§ç™»éŒ²"
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -32781,7 +33048,7 @@ msgid "Reports|Test summary results are being parsed"
msgstr "テストè¦ç´„ã®çµæžœã¯ãƒ‘ースã•ã‚Œã¦ã„ã¾ã™"
msgid "Reports|Test summary results are loading"
-msgstr ""
+msgstr "テストサマリーã®çµæžœã‚’読ã¿è¾¼ã‚“ã§ã„ã¾ã™ã€‚"
msgid "Reports|Tool"
msgstr ""
@@ -33425,11 +33692,11 @@ msgstr ""
msgid "Runners|%{strongStart}%{count}%{strongEnd} runner selected"
msgid_plural "Runners|%{strongStart}%{count}%{strongEnd} runners selected"
-msgstr[0] ""
+msgstr[0] "%{strongStart}%{count}%{strongEnd} 件㮠Runner ãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã™"
msgid "Runners|%{strongStart}%{count}%{strongEnd} runner will be permanently deleted and no longer available for projects or groups in the instance. Are you sure you want to continue?"
msgid_plural "Runners|%{strongStart}%{count}%{strongEnd} runners will be permanently deleted and no longer available for projects or groups in the instance. Are you sure you want to continue?"
-msgstr[0] ""
+msgstr[0] "%{strongStart}%{count}%{strongEnd} 件㮠Runners を永久ã«å‰Šé™¤ã—ã€ã“ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã®ãƒ—ロジェクトやグループã§ã¯åˆ©ç”¨ã§ããªããªã‚Šã¾ã™ã€‚本当ã«ã¤ã¥ã‘ã¾ã™ã‹?"
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 ""
@@ -33446,6 +33713,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33461,6 +33731,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33489,13 +33765,13 @@ msgid "Runners|Capacity of 1 enables warm HA through Auto Scaling group re-spawn
msgstr ""
msgid "Runners|Checkbox"
-msgstr ""
+msgstr "ãƒã‚§ãƒƒã‚¯ãƒœãƒƒã‚¯ã‚¹"
msgid "Runners|Choose your preferred GitLab Runner"
msgstr ""
msgid "Runners|Clear selection"
-msgstr ""
+msgstr "é¸æŠžã‚’クリア"
msgid "Runners|Command to register runner"
msgstr ""
@@ -33509,6 +33785,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33555,6 +33834,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr "Runners を始ã‚ã¾ã—ょã†"
@@ -33579,6 +33861,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33592,7 +33877,7 @@ msgid "Runners|Members of the %{type} can register runners"
msgstr ""
msgid "Runners|Multi-project runners cannot be deleted"
-msgstr ""
+msgstr "マルãƒãƒ—ロジェクトランナーã¯å‰Šé™¤ã§ãã¾ã›ã‚“"
msgid "Runners|Name"
msgstr ""
@@ -33624,24 +33909,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -33787,9 +34063,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -33872,9 +34145,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -33893,42 +34175,18 @@ msgstr "共有ランナーパイプラインã®%{quotaLimit} ã®ã†ã¡%{quotaUse
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr "利用å¯èƒ½ãªã‚¢ãƒƒãƒ—グレード"
-
-msgid "Runners|upgrade recommended"
-msgstr "アップグレードをãŠå‹§ã‚ã—ã¾ã™"
-
msgid "Runner|Owner"
msgstr ""
@@ -34022,6 +34280,9 @@ msgstr "SSL ã®æ¤œè¨¼:"
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34067,12 +34328,27 @@ msgstr "ä¿å­˜ä¸­"
msgid "Saving project."
msgstr "プロジェクトをä¿å­˜"
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34082,6 +34358,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34651,6 +34939,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -34906,6 +35197,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36288,12 +36582,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "サインアップã®åˆ¶é™"
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36567,6 +36870,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr "絵文字リアクション中ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
@@ -36657,6 +36963,9 @@ msgstr "Let's Encrypt ã®è¨¼æ˜Žæ›¸ã‚’å–å¾—ã™ã‚‹éš›ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ã
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37041,6 +37350,9 @@ msgstr "コミットメッセージをスカッシュ"
msgid "Squash commits"
msgstr "コミットをスカッシュ"
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr "スタックトレース"
@@ -37056,9 +37368,6 @@ msgstr ""
msgid "Standard"
msgstr "標準"
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr "優先ラベルã«ã™ã‚‹ã«ã¯ã€ãƒ©ãƒ™ãƒ«ã«ã‚¹ã‚¿ãƒ¼ã‚’付ã‘ã¾ã™ã€‚ドラッグã—ã¦ã€å„ªå…ˆé †ä½ã‚’並ã¹æ›¿ãˆã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
-
msgid "Star labels to start sorting by priority"
msgstr "ラベルをãŠæ°—ã«ã„ã‚Šã«ã—ã¦ã‚½ãƒ¼ãƒˆã®å„ªå…ˆé †ä½ã‚’ã¤ã‘ã‚‹"
@@ -37797,6 +38106,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr "日曜日"
@@ -39046,6 +39358,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr "親エピックã¯éžå…¬é–‹ã§ã€éžå…¬é–‹ã®ã‚¨ãƒ”ックã¨ã‚¤ã‚·ãƒ¥ãƒ¼ã ã‘ã‚’å«ã‚ã‚‹ã“ã¨ãŒã§ãã¾ã™"
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39142,6 +39457,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr "指定ã—ãŸã‚¿ãƒ–ã¯ç„¡åŠ¹ã§ã™ã€‚別ã®ã‚¿ãƒ–ã‚’é¸æŠžã—ã¦ãã ã•ã„"
@@ -39154,6 +39475,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39202,10 +39526,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39310,9 +39634,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr "ãã®åå‰ã®ãƒªãƒã‚¸ãƒˆãƒªã¯ã™ã§ã«ãƒ‡ã‚£ã‚¹ã‚¯ä¸Šã«ã‚ã‚Šã¾ã™"
@@ -39664,6 +39985,9 @@ msgstr "ã“ã®ãƒ–ロックã¯è‡ªå·±å‚ç…§ã—ã¦ã„ã¾ã™ã€‚"
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr "ã“ã®ãƒãƒ£ãƒ¼ãƒˆã¯è¡¨ç¤ºã§ãã¾ã›ã‚“"
@@ -39688,8 +40012,8 @@ msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã¯ %{strong_open}検証済ã¿%{strong_close}ã®ç½²å
msgid "This commit was signed with a different user's verified signature."
msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã¯åˆ¥ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æ¤œè¨¼æ¸ˆã¿ç½²åã§ã‚µã‚¤ãƒ³ã•ã‚Œã¦ã„ã¾ã™ã€‚"
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
-msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã¯æ¤œè¨¼æ¸ˆã¿ã®ç½²åã§ã‚µã‚¤ãƒ³ã•ã‚Œã¦ã„ã¾ã™ã€‚ã—ã‹ã—コミッターã®ãƒ¡ãƒ¼ãƒ«ã¯ã€ åŒã˜ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ã‚‚ã®ã¨%{strong_open}検証ã•ã‚Œã¦ã„ã¾ã›ã‚“%{strong_close} 。"
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
+msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã¯%{strong_open}検証ã•ã‚Œã¦ã„ãªã„%{strong_close}ç½²åã§ã‚µã‚¤ãƒ³ã—ã¦ã„ã¾ã—ãŸã€‚"
@@ -40069,6 +40393,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40081,9 +40408,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40186,6 +40510,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr "木曜日"
@@ -40396,6 +40723,9 @@ msgstr "ãŸã£ãŸä»Š"
msgid "Timeago|right now"
msgstr "今"
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40423,6 +40753,24 @@ msgstr ""
msgid "Timezone"
msgstr "タイムゾーン"
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "時間"
@@ -40431,6 +40779,12 @@ msgid "Time|min"
msgid_plural "Time|mins"
msgstr[0] "分"
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr "秒"
@@ -40485,6 +40839,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr "エントリーを手動ã§è¿½åŠ ã™ã‚‹ã«ã¯ã€ã‚¹ãƒžãƒ¼ãƒˆãƒ•ã‚©ãƒ³ã®ã‚¢ãƒ—リケーションã«æ¬¡ã®è©³ç´°ã‚’入力ã—ã¦ãã ã•ã„"
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40647,9 +41004,6 @@ msgstr "検索範囲を広ã’ã‚‹ã«ã¯ã€ä¸Šã®ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼ã‚’変更ã¾ãŸã¯
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr "To-Do リスト"
@@ -40674,6 +41028,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40683,6 +41049,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -40791,9 +41160,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -40818,9 +41196,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr "åˆè¨ˆ"
@@ -40989,39 +41379,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41127,21 +41490,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr "デãƒã‚¤ã‚¹ã¨é€šä¿¡ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚(ã¾ã æº–å‚™ã§ãã¦ã„ãªã„å ´åˆã¯) デãƒã‚¤ã‚¹ã‚’接続ã—ã¦ã€ãƒœã‚¿ãƒ³ã‚’押ã—ã¾ã™ã€‚"
+msgid "Tue"
+msgstr ""
+
msgid "Tuesday"
msgstr "ç«æ›œæ—¥"
msgid "Turn off"
msgstr ""
-msgid "Turn off notifications"
-msgstr ""
-
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr "Twitter"
@@ -41721,6 +42081,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41748,6 +42111,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr "グループ設定 &gt; 使用クォータ"
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -41844,10 +42210,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -41913,7 +42279,7 @@ msgstr "ã‚ãªãŸã®ãƒ—ロジェクトã§ã®ãƒªã‚½ãƒ¼ã‚¹ã®ä½¿ç”¨çŠ¶æ³"
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42075,6 +42441,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr "URI ã”ã¨ã«1行使用"
@@ -42475,7 +42844,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -42769,10 +43138,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr "ã™ã¹ã¦ã®ã‚¤ã‚·ãƒ¥ãƒ¼ã‚’表示"
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43465,6 +43837,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr "マージリクエスト"
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43546,6 +43924,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43588,6 +43969,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr "Webhook ã«ç§»å‹•"
+
msgid "Webhooks|Issues events"
msgstr "イシューイベント"
@@ -43645,6 +44029,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr "Webhookã¯ç„¡åŠ¹"
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43658,11 +44045,14 @@ msgid "Webhooks|Wiki page events"
msgstr ""
msgid "Website"
-msgstr ""
+msgstr "ウェブサイト"
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr "水曜日"
@@ -43714,6 +44104,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -43923,9 +44316,6 @@ msgstr "%{pageTitle} ページを削除ã—ã¾ã™ã‹ï¼Ÿ"
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -43950,9 +44340,6 @@ msgstr "詳細ã¯ã“ã¡ã‚‰"
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr "変更をä¿å­˜"
@@ -44040,10 +44427,10 @@ msgstr "作業中(オープンã‹ã¤æœªå‰²ã‚Šå½“ã¦ï¼‰"
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44061,7 +44448,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44071,16 +44458,13 @@ msgstr[0] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44089,19 +44473,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
+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|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44110,19 +44506,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr "種類をé¸æŠž"
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44137,31 +44536,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44384,6 +44792,10 @@ 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 ""
@@ -44444,10 +44856,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr "æ–°ã—ã„メンãƒãƒ¼ã‚’%{project_name} ã«æ‹›å¾…ã™ã‚‹ã‹ã€åˆ¥ã®ã‚°ãƒ«ãƒ¼ãƒ—を招待ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
@@ -44553,10 +44961,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr "æ–°ã—ã„トリガーを作æˆã§ãã¾ã›ã‚“ã§ã—ãŸã€‚"
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-
msgid "You do not have any subscriptions yet"
msgstr "ã‚ãªãŸã«ã¯ã‚µãƒ–スクリプションãŒã‚ã‚Šã¾ã›ã‚“"
@@ -44720,7 +45124,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -44783,6 +45187,9 @@ msgstr "GitLab プロジェクトã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã‚¢ãƒ¼ã‚«ã‚¤ãƒ–(.gz ã§ç
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -44894,9 +45301,6 @@ msgstr ""
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 "ワンタイムパスワードèªè¨¼ã‚’使用ã—ãŸ2è¦ç´ èªè¨¼ã¯æ—¢ã«æœ‰åŠ¹ã«ãªã£ã¦ã„ã¾ã™ã€‚別ã®ãƒ‡ãƒã‚¤ã‚¹ã‚’登録ã™ã‚‹ã«ã¯ã€ã¾ãš2è¦ç´ èªè¨¼ã‚’無効ã«ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -44936,6 +45340,12 @@ msgstr "%{project_name} (%{project_url}) プロジェクトã‹ã‚‰ã‚¨ã‚¯ã‚¹ãƒãƒ¼
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45086,9 +45496,6 @@ msgstr "ã‚ãªãŸã®ãƒ‡ãƒã‚¤ã‚¹ã¯æ­£å¸¸ã«è¨­å®šã•ã‚Œã¾ã—ãŸã€‚åå‰ã‚’付
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45148,6 +45555,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr "パスワードリセットトークンã¯æœ‰åŠ¹æœŸé™åˆ‡ã‚Œã§ã™ã€‚"
@@ -45163,7 +45573,7 @@ msgstr ""
msgid "Your profile"
msgstr "ã‚ãªãŸã®ãƒ—ロフィール"
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45367,9 +45777,6 @@ msgstr ""
msgid "assign yourself"
msgstr "自分ã«å‰²ã‚Šå½“ã¦"
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45464,6 +45871,9 @@ msgstr "個人用プロジェクトã«ã‚³ãƒ³ãƒ†ãƒŠãƒ¬ã‚¸ã‚¹ãƒˆãƒªã‚¿ã‚°ãŒã‚ã‚‹
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45504,6 +45914,9 @@ msgid "change"
msgid_plural "changes"
msgstr[0] ""
+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 ""
@@ -45552,7 +45965,7 @@ msgstr "%{reportType}:読ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -45748,7 +46161,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr "ライセンスã®ç®¡ç†"
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -45872,9 +46285,6 @@ msgstr "コミット済ã¿"
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -45911,6 +46321,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46033,6 +46446,9 @@ msgstr "関連付ã‘られãŸæ¤œå‡ºçµæžœã‚’破棄ã§ãã¾ã›ã‚“ã§ã—ãŸ(id =%
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46238,6 +46654,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr "é•·ã™ãŽã¾ã™(%{current_value})。最大サイズ㯠%{max_size} ã§ã™ã€‚"
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46330,13 +46749,13 @@ msgstr "ã“ã® math ブロックã®ãƒ¬ãƒ³ãƒ€ãƒªãƒ³ã‚°ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—
msgid "member"
msgid_plural "members"
-msgstr[0] ""
+msgstr[0] "メンãƒãƒ¼"
msgid "merge request"
msgid_plural "merge requests"
msgstr[0] "マージリクエスト"
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46357,8 +46776,8 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
-msgstr "1マージコミット"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
+msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
msgstr ""
@@ -46518,6 +46937,9 @@ msgid "mrWidget|Mentions issue"
msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46653,9 +47075,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47211,6 +47630,9 @@ msgstr "å´ä¸‹ã•ã‚Œã¾ã—ãŸ"
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr "wiki ページ"
diff --git a/locale/ka_GE/gitlab.po b/locale/ka_GE/gitlab.po
index 38635719feb..3cce8abe02e 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-08-12 18:05\n"
+"PO-Revision-Date: 2022-09-11 06:23\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/kab/gitlab.po b/locale/kab/gitlab.po
index 097e1d4e0e7..d4eeadfda24 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-08-12 18:09\n"
+"PO-Revision-Date: 2022-09-11 06:26\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/ko/gitlab.po b/locale/ko/gitlab.po
index b579cdb1eb5..3c55fab6892 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-08-12 18:05\n"
+"PO-Revision-Date: 2022-09-11 06:23\n"
msgid " %{start} to %{end}"
msgstr " %{start}부터 %{end}까지"
@@ -22,6 +22,10 @@ msgstr " %{start}부터 %{end}까지"
msgid " (from %{timeoutSource})"
msgstr " (%{timeoutSource} 로부터)"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+
msgid " Collected %{time}"
msgstr " %{time}ì— ìˆ˜ì§‘í–ˆìŠµë‹ˆë‹¤"
@@ -44,16 +48,16 @@ msgid " or "
msgstr " ë˜ëŠ” "
msgid " or %{emphasisStart}!merge request id%{emphasisEnd}"
-msgstr ""
+msgstr " ë˜ëŠ” %{emphasisStart}!머지 리퀘스트 id%{emphasisEnd}"
msgid " or %{emphasisStart}#id%{emphasisEnd}"
msgstr " ë˜ëŠ” %{emphasisStart}#id%{emphasisEnd}"
msgid " or %{emphasisStart}#issue id%{emphasisEnd}"
-msgstr ""
+msgstr " ë˜ëŠ” %{emphasisStart}#ì´ìŠˆ id%{emphasisEnd}"
msgid " or %{emphasisStart}&epic id%{emphasisEnd}"
-msgstr ""
+msgstr " ë˜ëŠ” %{emphasisStart}&ì—픽 id%{emphasisEnd}"
msgid " or references (e.g. path/to/project!merge_request_id)"
msgstr " ë˜ëŠ” 참조 (예: path/to/project!merge_request_id)"
@@ -129,6 +133,10 @@ msgid "%d approver (you've approved)"
msgid_plural "%d approvers (you've approved)"
msgstr[0] "%dëª…ì˜ ìŠ¹ì¸ìž (ë‚´ê°€ 승ì¸í•¨)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] "%dê°œì˜ í• ë‹¹ëœ ì´ìŠˆ"
@@ -185,6 +193,10 @@ msgid "%d contribution"
msgid_plural "%d contributions"
msgstr[0] "%d 기여"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%dì¼"
@@ -247,7 +259,7 @@ msgstr[0] "%dê±´ì˜ ì´ìŠˆë¥¼ ë¼ë²¨ì„ 통해 성공ì ìœ¼ë¡œ 가져왔습니ë‹
msgid "%d job"
msgid_plural "%d jobs"
-msgstr[0] ""
+msgstr[0] "%dê°œì˜ ìž‘ì—…"
msgid "%d layer"
msgid_plural "%d layers"
@@ -255,7 +267,7 @@ msgstr[0] "%d ë ˆì´ì–´"
msgid "%d merge request"
msgid_plural "%d merge requests"
-msgstr[0] "머지 리퀘스트(MR) %d개"
+msgstr[0] "%dê°œì˜ ë¨¸ì§€ 리퀘스트(MR)"
msgid "%d merge request that you don't have access to."
msgid_plural "%d merge requests that you don't have access to."
@@ -305,13 +317,17 @@ msgid "%d project selected"
msgid_plural "%d projects selected"
msgstr[0] "%dê°œì˜ í”„ë¡œì íŠ¸ê°€ ì„ íƒë¨"
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] "%d 남ìŒ"
msgid "%d reply"
msgid_plural "%d replies"
-msgstr[0] ""
+msgstr[0] "%dê°œì˜ ë‹µê¸€"
msgid "%d second"
msgid_plural "%d seconds"
@@ -333,10 +349,6 @@ msgid "%d tag per image name"
msgid_plural "%d tags per image name"
msgstr[0] "ì´ë¯¸ì§€ ì´ë¦„ 당 %dê°œì˜ íƒœê·¸"
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] "%dê°œì˜ í• ë‹¹ í•´ì œëœ ì´ìŠˆ"
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] "%dê°œì˜ í•´ê²°ë˜ì§€ ì•Šì€ ìŠ¤ë ˆë“œ"
@@ -385,9 +397,6 @@ msgstr "%{actionText} & %{noteable} 다시 열기"
msgid "%{address} is an invalid IP address range"
msgstr "%{address}ì€(는) 유효하지 ì•Šì€ IP 주소 범위입니다"
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr "%{attribute} ì€ %{min} ê³¼ %{max}사ì´ì—¬ì•¼ 합니다."
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr "%{author_link}(ì´)ê°€ %{original_issue}를 %{new_issue}ì— ë³µì œí–ˆìŠµë‹ˆë‹¤."
@@ -428,7 +437,7 @@ msgid "%{commit_author_link} authored %{commit_authored_timeago}"
msgstr "%{commit_authored_timeago} ì— %{commit_author_link} ë‹˜ì´ ì»¤ë°‹í–ˆìŠµë‹ˆë‹¤."
msgid "%{commit_author_link} authored %{commit_authored_timeago} and %{commit_committer_avatar} %{commit_committer_link} committed %{commit_committer_timeago}"
-msgstr ""
+msgstr "%{commit_author_link}ê°€ %{commit_authored_timeago}ì— ìž‘ì„±í•˜ê³  %{commit_committer_avatar} %{commit_committer_link}ê°€ %{commit_committer_timeago}ì— ì»¤ë°‹í•¨"
msgid "%{completedCount} completed weight"
msgstr ""
@@ -438,10 +447,10 @@ msgid_plural "%{completedCount} of %{count} checklist items completed"
msgstr[0] "%{count}ê°œ 중 %{completedCount}ê°œì˜ ì²´í¬ ë¦¬ìŠ¤íŠ¸ ì•„ì´í…œ 완료"
msgid "%{completedWeight} of %{totalWeight} weight completed"
-msgstr ""
+msgstr "가중치 %{totalWeight} 중 %{completedWeight} 완료"
msgid "%{completed} of %{total} issues closed"
-msgstr ""
+msgstr "%{total}중 %{completed}ê°œì˜ ì´ìŠˆ 닫힘"
msgid "%{completed} of %{total} weight completed"
msgstr ""
@@ -484,7 +493,7 @@ msgstr "%{count}ëª…ì˜ ë‹´ë‹¹ìž"
msgid "%{count} more release"
msgid_plural "%{count} more releases"
-msgstr[0] "%{count} 보다 릴리즈"
+msgstr[0] "ì´ì™¸ %{count}ê°œì˜ ë¦´ë¦¬ìŠ¤"
msgid "%{count} of %{required} approvals from %{name}"
msgstr "%{name}ë‹˜ì´ %{required} 중 %{count} 승ì¸"
@@ -584,7 +593,7 @@ msgid "%{group_name} activity"
msgstr "%{group_name} 활ë™"
msgid "%{group_name} group members"
-msgstr "%{group_name} 그룹 회ì›"
+msgstr "%{group_name} 그룹 구성ì›"
msgid "%{group_name} is approaching the limit of available seats"
msgstr ""
@@ -611,10 +620,10 @@ msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType}ì´ ì‚­ì œë©ë‹ˆë‹¤! 확실합니까?"
msgid "%{issuable_class_name} doesn't exist or you don't have permission to add timelog to it."
-msgstr ""
+msgstr "%{issuable_class_name}ì´ ì¡´ìž¬í•˜ì§€ 않거나 시간 기ë¡ì„ 추가할 ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤."
msgid "%{issuable}(s) already assigned"
-msgstr ""
+msgstr "%{issuable}는 ì´ë¯¸ 할당ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "%{issueType} actions"
msgstr "%{issueType} ì•¡ì…˜"
@@ -665,7 +674,7 @@ msgid "%{labelStart}Namespace:%{labelEnd} %{namespace}"
msgstr "%{labelStart}네임 스페ì´ìŠ¤ :%{labelEnd} %{namespace}"
msgid "%{labelStart}Project:%{labelEnd} %{project}"
-msgstr ""
+msgstr "%{labelStart}프로ì íŠ¸:%{labelEnd} %{project}"
msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}"
msgstr "%{labelStart}스ìºë„ˆ:%{labelEnd} %{scanner}"
@@ -680,7 +689,7 @@ msgid "%{labelStart}Tool:%{labelEnd} %{reportType}"
msgstr ""
msgid "%{labelStart}URL:%{labelEnd} %{url}"
-msgstr ""
+msgstr "%{labelStart}URL:%{labelEnd} %{url}"
msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}"
msgstr ""
@@ -728,10 +737,10 @@ msgid "%{milestone} (expired)"
msgstr "%{milestone} (만료ë¨)"
msgid "%{milliseconds}ms"
-msgstr ""
+msgstr "%{milliseconds}ms"
msgid "%{minutesUsed} minutes"
-msgstr ""
+msgstr "%{minutesUsed} 분"
msgid "%{model_name} not found"
msgstr "%{model_name} ì°¾ì„ ìˆ˜ ì—†ìŒ"
@@ -761,7 +770,7 @@ msgid "%{name} is reserved for %{type} report type"
msgstr ""
msgid "%{name} is scheduled for %{action}"
-msgstr ""
+msgstr "%{name}ì´ %{action}으로 예약ë©ë‹ˆë‹¤."
msgid "%{name}'s avatar"
msgstr "%{name} ì˜ ì•„ë°”íƒ€"
@@ -773,10 +782,10 @@ msgid "%{name}(%{url}) namespace has run out of Shared Runner Pipeline minutes s
msgstr ""
msgid "%{name}, confirm your email address now!"
-msgstr ""
+msgstr "%{name}님, 지금 ì´ë©”ì¼ ì£¼ì†Œë¥¼ 확ì¸í•´ 주세요!"
msgid "%{name}: %{resultsString}"
-msgstr ""
+msgstr "%{name}: %{resultsString}"
msgid "%{no_of_days} day"
msgid_plural "%{no_of_days} days"
@@ -792,7 +801,7 @@ msgid "%{oneWeekAgo} - %{today}"
msgstr "%{oneWeekAgo} - %{today}"
msgid "%{oneYearAgo} - %{today}"
-msgstr ""
+msgstr "%{oneYearAgo} - %{today}"
msgid "%{openedEpics} open, %{closedEpics} closed"
msgstr "%{openedEpics}개 열림, %{closedEpics}개 닫힘"
@@ -806,20 +815,14 @@ msgstr ""
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}ë¡œ 업그레ì´ë“œí•˜ì‹­ì‹œì˜¤."
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
-msgstr ""
+msgstr "%{percentageUsed}%% 사용ë¨"
msgid "%{percentage}%% issues closed"
-msgstr ""
+msgstr "%{percentage}%% ì˜ ì´ìŠˆê°€ 닫힘"
msgid "%{percentage}%% weight completed"
-msgstr ""
+msgstr "%{percentage}%%만í¼ì˜ 가중치 완료"
msgid "%{percent}%% complete"
msgstr "%{percent}%% 완료"
@@ -828,7 +831,7 @@ msgid "%{percent}%{percentSymbol} complete"
msgstr "%{percent}%{percentSymbol} 완료"
msgid "%{placeholder} is not a valid color scheme"
-msgstr ""
+msgstr "%{placeholder} ì€(는) 유효한 테마가 아닙니다."
msgid "%{placeholder} is not a valid theme"
msgstr "%{placeholder}(ì€)는 유효한 테마가 아닙니다."
@@ -837,7 +840,7 @@ msgid "%{policy_link} (notifying after %{elapsed_time} minutes unless %{status})
msgstr ""
msgid "%{project_name}"
-msgstr ""
+msgstr "%{project_name}"
msgid "%{project_path} is a project that you can use to add a README to your GitLab profile. Create a public project and initialize the repository with a README to get started. %{help_link_start}Learn more.%{help_link_end}"
msgstr ""
@@ -847,7 +850,7 @@ msgstr "%{ref}(ì„)를 추가 í•  수 없습니다: %{error}"
msgid "%{releases} release"
msgid_plural "%{releases} releases"
-msgstr[0] "%{releases} 릴리즈"
+msgstr[0] "%{releases} 릴리스"
msgid "%{remaining_approvals} left"
msgstr "%{remaining_approvals} 남ìŒ"
@@ -913,6 +916,9 @@ msgstr "%{size} bytes"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr "%{sourceBranch} ì—ì„œ %{targetBranch}"
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr "%{spammable_titlecase} (ì´)ê°€ Akismetì— ì„±ê³µì ìœ¼ë¡œ 제출ë˜ì—ˆìŠµë‹ˆë‹¤."
@@ -932,6 +938,10 @@ msgstr[0] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr "%{strongOpen}경고:%{strongClose} SAML 그룹 ë§í¬ë¡œ ì¸í•´ GitLabì´ ê·¸ë£¹ì—ì„œ 구성ì›ì„ ìžë™ìœ¼ë¡œ 제거할 수 있습니다."
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] "%{strongStart}%{count}%{strongEnd} 커밋"
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -958,12 +968,9 @@ msgstr[0] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
-msgstr[0] "%{strong_start}%{release_count}%{strong_end}ê°œì˜ ë¦´ë¦¬ì¦ˆ"
+msgstr[0] "%{strong_start}%{release_count}%{strong_end}ê°œì˜ ë¦´ë¦¬ìŠ¤"
msgid "%{strong_start}%{tag_count}%{strong_end} Tag"
msgid_plural "%{strong_start}%{tag_count}%{strong_end} Tags"
@@ -997,7 +1004,7 @@ msgid "%{timebox_type} must have a start and due date"
msgstr "%{timebox_type} ì€(는) 시작날짜와 종료날짜가 있어야합니다."
msgid "%{time} UTC"
-msgstr ""
+msgstr "%{time} UTC(협정 세계시)"
msgid "%{title} %{operator} %{threshold}"
msgstr "%{title} %{operator} %{threshold}"
@@ -1018,13 +1025,13 @@ msgid "%{total_warnings} warning(s) found:"
msgstr "%{total_warnings}ê°œì˜ ê²½ê³  발견ë¨:"
msgid "%{total} remaining issue weight"
-msgstr ""
+msgstr "%{total} ë‚¨ì€ ì´ìŠˆ 가중치"
msgid "%{total} warnings found: showing first %{warningsDisplayed}"
msgstr ""
msgid "%{type} only supports %{name} name"
-msgstr ""
+msgstr "%{type} ì€ %{name} ì´ë¦„만 지ì›í•©ë‹ˆë‹¤."
msgid "%{userName} (cannot merge)"
msgstr "%{userName} (머지할 수 ì—†ìŒ)"
@@ -1105,7 +1112,7 @@ msgid "'%{level}' is not a valid visibility level"
msgstr "'%{level}' ìˆ˜ì¤€ì€ ì˜¬ë°”ë¥¸ 공개 ìˆ˜ì¤€ì´ ì•„ë‹™ë‹ˆë‹¤"
msgid "'%{name}' Value Stream created"
-msgstr ""
+msgstr "'%{name}' 밸류 ìŠ¤íŠ¸ë¦¼ì´ ìƒì„±ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "'%{name}' Value Stream deleted"
msgstr ""
@@ -1127,7 +1134,7 @@ msgid "(%{mrCount} merged)"
msgstr "(%{mrCount} 병합)"
msgid "(%{value}) has already been taken"
-msgstr ""
+msgstr "(%{value}) ì€(는) ì´ë¯¸ 사용ë˜ê³  있습니다."
msgid "(+%{count}&nbsp;rules)"
msgstr "(+%{count}&nbsp;규칙)"
@@ -1151,13 +1158,13 @@ msgid "(expired)"
msgstr "(만료ë¨)"
msgid "(leave blank if you don't want to change it)"
-msgstr ""
+msgstr "(변경하지 않으려면 비워 둡니다)"
msgid "(max size 15 MB)"
msgstr "(최대 사ì´ì¦ˆ 15MB)"
msgid "(optional)"
-msgstr ""
+msgstr "(ì„ íƒì‚¬í•­)"
msgid "(removed)"
msgstr "(ì‚­ì œë¨)"
@@ -1165,15 +1172,11 @@ msgstr "(ì‚­ì œë¨)"
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-
msgid "(this user)"
msgstr "(ì´ ì‚¬ìš©ìž)"
msgid "(we need your current password to confirm your changes)"
-msgstr ""
+msgstr "(변경 ì‚¬í•­ì„ í™•ì¸í•˜ë ¤ë©´ 현재 비밀번호가 필요합니다)"
msgid "* All times are in UTC unless specified"
msgstr "* 지정하지 않으면 모든 ì‹œê°„ì€ UTC 기준입니다"
@@ -1201,7 +1204,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}"
@@ -1222,13 +1225,13 @@ msgid "- %{policy_name} (notifying after %{elapsed_time} minutes unless %{status
msgstr ""
msgid "- Add or remove a user."
-msgstr ""
+msgstr "- 사용ìžë¥¼ 추가하거나 제거합니다."
msgid "- Available to run jobs."
msgstr ""
msgid "- Create or close an issue."
-msgstr ""
+msgstr "- ì´ìŠˆë¥¼ ìƒì„±í•˜ê±°ë‚˜ 닫습니다."
msgid "- Create, update, or delete a merge request."
msgstr ""
@@ -1247,7 +1250,7 @@ msgid "- Not available to run jobs."
msgstr ""
msgid "- Push code to the repository."
-msgstr ""
+msgstr "- ì €ìž¥ì†Œì— ì½”ë“œë¥¼ 푸시합니다."
msgid "- Select -"
msgstr "- ì„ íƒ -"
@@ -1257,10 +1260,10 @@ msgid_plural "- Users"
msgstr[0] "- 사용ìž"
msgid "- View the last_activity_at attribute for %{project_name} using the Project API %{projects_api_link}."
-msgstr ""
+msgstr "- Project API %{projects_api_link}ì„ ì‚¬ìš©í•˜ì—¬ %{project_name} ì˜ last_activity_at ì†ì„±ì„ 봅니다."
msgid "- of - issues closed"
-msgstr ""
+msgstr "-ê°œ 중 -ê°œì˜ ì´ìŠˆ 닫힘"
msgid "- of - weight completed"
msgstr ""
@@ -1275,10 +1278,10 @@ msgid "/"
msgstr "/"
msgid "/day"
-msgstr ""
+msgstr "/ì¼"
msgid "0 bytes"
-msgstr ""
+msgstr "0 ë°”ì´íŠ¸"
msgid "1 Code quality finding"
msgid_plural "%d Code quality findings"
@@ -1314,7 +1317,7 @@ msgstr[0] "%dì¼ ì„ íƒë¨"
msgid "1 deploy key"
msgid_plural "%d deploy keys"
-msgstr[0] ""
+msgstr[0] "%dê°œì˜ ë°°í¬ í‚¤"
msgid "1 follower"
msgid_plural "%{count} followers"
@@ -1346,7 +1349,7 @@ msgstr[0] "%d분"
msgid "1 month remaining"
msgid_plural "%d months remaining"
-msgstr[0] ""
+msgstr[0] "%d 개월 남ìŒ"
msgid "1 open issue"
msgid_plural "%{issues} open issues"
@@ -1370,11 +1373,11 @@ msgstr[0] "%{num}ëª…ì˜ ì‚¬ìš©ìž"
msgid "1 week remaining"
msgid_plural "%d weeks remaining"
-msgstr[0] ""
+msgstr[0] "%d 주 남ìŒ"
msgid "1 year remaining"
msgid_plural "%d years remaining"
-msgstr[0] ""
+msgstr[0] "%d ë…„ 남ìŒ"
msgid "1-9 contributions"
msgstr "1 ê°œ ì´ìƒ 참여"
@@ -1386,7 +1389,7 @@ msgid "1000+"
msgstr "1000+"
msgid "192.168.0.0/24 or 2001:0DB8:1234::/48"
-msgstr ""
+msgstr "192.168.0.0/24 ë˜ëŠ” 2001:0DB8:1234::/48"
msgid "1st contribution!"
msgstr "첫번째 기여!"
@@ -1398,7 +1401,7 @@ msgid "2FA"
msgstr "2단계 ì¸ì¦(2FA)"
msgid "2FADevice|Registered On"
-msgstr ""
+msgstr "등ë¡ë¨"
msgid "3 hours"
msgstr "3 시간"
@@ -1425,7 +1428,7 @@ msgid "404|Please contact your GitLab administrator if you think this is a mista
msgstr "ì´ê²ƒì´ ì‹¤ìˆ˜ì— ì˜í•œ 것ì´ë¼ê³  ìƒê°í•œë‹¤ë©´ GitLab 관리ìžì—게 문ì˜í•˜ì„¸ìš”."
msgid "409|Please contact your GitLab administrator if you think this is a mistake."
-msgstr ""
+msgstr "ì´ê²ƒì´ 실수ë¼ê³  ìƒê°í•œë‹¤ë©´ GitLab 관리ìžì—게 문ì˜í•˜ì„¸ìš”."
msgid "409|There was a conflict with your request."
msgstr ""
@@ -1475,6 +1478,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr "완벽한 DevOps 플랫í¼"
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr "빈 프로ì íŠ¸ì—서는 기본 브랜치를 ì„ íƒí•  수 없습니다."
@@ -1494,7 +1503,7 @@ msgid "A file with '%{file_name}' already exists in %{branch} branch"
msgstr "'%{file_name}' 파ì¼ì€ %{branch} ë¸Œëžœì¹˜ì— ì´ë¯¸ 존재합니다"
msgid "A file with this name already exists."
-msgstr ""
+msgstr "ê°™ì€ ì´ë¦„ì„ ê°€ì§„ 파ì¼ì´ ì´ë¯¸ 존재합니다"
msgid "A group is a collection of several projects"
msgstr "ê·¸ë£¹ì€ ì—¬ëŸ¬ 프로ì íŠ¸ë“¤ì˜ 모ìŒìž…니다."
@@ -1503,7 +1512,7 @@ msgid "A group represents your organization in GitLab. Groups allow you to manag
msgstr "ê·¸ë£¹ì€ GitLabì˜ ì¡°ì§ì„ 나타냅니다. ê·¸ë£¹ì„ ì‚¬ìš©í•˜ë©´ 사용ìžë¥¼ 관리하고, ì—¬ëŸ¬ê°œì˜ í”„ë¡œì íŠ¸ì—ì„œ ê³µë™ìœ¼ë¡œ ìž‘ì—…í•  수 있습니다."
msgid "A job artifact is an archive of files and directories saved by a job when it finishes."
-msgstr ""
+msgstr "ìž‘ì—… 아티팩트는 ìž‘ì—…ì´ ì™„ë£Œë  ë•Œ ìž‘ì—…ì— ì˜í•´ 저장ë˜ëŠ” íŒŒì¼ ë° ë””ë ‰í† ë¦¬ì˜ ì•„ì¹´ì´ë¸Œìž…니다."
msgid "A limit of %{ci_project_subscriptions_limit} subscriptions to or from a project applies."
msgstr ""
@@ -1518,7 +1527,7 @@ msgid "A new Auto DevOps pipeline has been created, go to the Pipelines page for
msgstr ""
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 "새 릴리즈 %{tag}ê°€ %{name}으로 공개ë˜ì—ˆìŠµë‹ˆë‹¤. %{release_link_start}릴리즈 페ì´ì§€%{release_link_end}를 방문하여 ë” ìžì„¸ížˆ 알아보세요."
+msgstr "새 릴리스 %{tag}ê°€ %{name}으로 공개ë˜ì—ˆìŠµë‹ˆë‹¤. %{release_link_start}릴리스 페ì´ì§€%{release_link_end}를 방문하여 ë” ìžì„¸ížˆ 알아보세요."
msgid "A new Release %{tag} for %{name} was published. Visit the Releases page to read more about it:"
msgstr "새 릴리스 %{tag}(ì´)ê°€ %{name}(으)ë¡œ 공개ë˜ì—ˆìŠµë‹ˆë‹¤. 릴리스 페ì´ì§€ë¥¼ 방문해 ìžì„¸ížˆ 알아보세요."
@@ -1530,14 +1539,20 @@ msgid "A new impersonation token has been created."
msgstr ""
msgid "A new personal access token has been created"
-msgstr ""
+msgstr "새 ê°œì¸ ì•¡ì„¸ìŠ¤ 토í°ì´ ìƒì„±ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "A new personal access token, named %{token_name}, has been created."
-msgstr ""
+msgstr "%{token_name} 새 ê°œì¸ ì•¡ì„¸ìŠ¤ 토í°ì´ ìƒì„±ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr "ë™ì¼í•œ ì œëª©ì˜ íŽ˜ì´ì§€ê°€ ì´ë¯¸ 존재합니다"
@@ -1581,7 +1596,7 @@ msgid "A user with write access to the source branch selected this option"
msgstr "소스 ë¸Œëžœì¹˜ì— ëŒ€í•œ 쓰기 ê¶Œí•œì´ ìžˆëŠ” 사용ìžê°€ ì´ ì˜µì…˜ì„ ì„ íƒí•˜ì˜€ìŠµë‹ˆë‹¤."
msgid "ACTION REQUIRED: Something went wrong while obtaining the Let's Encrypt certificate for GitLab Pages domain '%{domain}'"
-msgstr ""
+msgstr "필요한 조치 : GitLab Pages '%{domain}'ì— ëŒ€í•œ Let 's Encrypt ì¸ì¦ì„œë¥¼ 가져 오는 ì¤‘ì— ë¬¸ì œê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "API"
msgstr "API"
@@ -1605,16 +1620,16 @@ msgid "API?"
msgstr "API?"
msgid "APIFuzzing|$VARIABLE_WITH_PASSWORD"
-msgstr ""
+msgstr "$VARIABLE_WITH_PASSWORD"
msgid "APIFuzzing|$VARIABLE_WITH_USERNAME"
-msgstr ""
+msgstr "$VARIABLE_WITH_USERNAME"
msgid "APIFuzzing|API Fuzzing Configuration"
msgstr "API í¼ì§• 설정"
msgid "APIFuzzing|Base URL of API testing target. For example, http://www.example.com."
-msgstr ""
+msgstr "API 테스트 대ìƒì˜ 기본 URL입니다. 예: http://www.example.com."
msgid "APIFuzzing|Choose a method"
msgstr "방법 ì„ íƒ"
@@ -1674,7 +1689,7 @@ msgid "APIFuzzing|There are three ways to perform scans."
msgstr "ìŠ¤ìº”ì„ ìˆ˜í–‰í•˜ëŠ” 세 가지 ë°©ë²•ì´ ìžˆìŠµë‹ˆë‹¤."
msgid "APIFuzzing|Tip: Insert the following variables anywhere below stages and include"
-msgstr ""
+msgstr "íŒ: 스테ì´ì§€ì™€ ì¸í´ë£¨ë“œ ì•„ëž˜ì— ë‹¤ìŒ ë³€ìˆ˜ë“¤ì„ ì‚½ìž…í•˜ì„¸ìš”."
msgid "APIFuzzing|Tip: Insert this part below all include"
msgstr ""
@@ -1706,14 +1721,17 @@ msgstr ""
msgid "AWS Access Key"
msgstr "AWS 엑세스 키"
+msgid "AWS OpenSearch IAM credentials"
+msgstr "AWS OpenSearch IAM ìžê²© ì¦ëª…"
+
msgid "AWS Secret Access Key"
msgstr "AWS 비밀 액세스 키"
msgid "AWS access key ID (Optional)"
-msgstr ""
+msgstr "AWS 액세스 키 ID(ì„ íƒ ì‚¬í•­)"
msgid "AWS secret access key (Optional)"
-msgstr ""
+msgstr "AWS secret 액세스 키(ì„ íƒ ì‚¬í•­)"
msgid "AWS service error: %{error}"
msgstr "AWS 서비스 ì—러 : %{error}"
@@ -1731,7 +1749,7 @@ msgid "About this feature"
msgstr "ì´ ê¸°ëŠ¥ì— ëŒ€í•´ì„œ"
msgid "About your company"
-msgstr ""
+msgstr "회사 정보"
msgid "Abuse Reports"
msgstr "오남용 리í¬íŠ¸"
@@ -1773,7 +1791,7 @@ msgid "Access forbidden. Check your access level."
msgstr "액세스 금지. ì ‘ê·¼ ê¶Œí•œì„ í™•ì¸í•˜ì„¸ìš”."
msgid "Access granted"
-msgstr ""
+msgstr "엑세스 권한 부여 ë¨"
msgid "Access requests"
msgstr ""
@@ -1782,7 +1800,7 @@ msgid "Access to '%{classification_label}' not allowed"
msgstr "%{classification_label} ì— ëŒ€í•œ 액세스가 허용ë˜ì§€ 않습니다."
msgid "AccessDropdown|Deploy Keys"
-msgstr ""
+msgstr "ë°°í¬ í‚¤"
msgid "AccessDropdown|Groups"
msgstr "그룹"
@@ -1806,10 +1824,10 @@ 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 ""
+msgstr "수신 ì´ë©”ì¼ í† í° ë³µì‚¬"
msgid "AccessTokens|Copy static object token"
msgstr ""
@@ -1899,7 +1917,7 @@ msgid "AccountValidation|Fix your pipelines by validating your account"
msgstr ""
msgid "AccountValidation|I'll bring my own runners"
-msgstr ""
+msgstr "내가 소유한 러너를 사용하겠습니다."
msgid "AccountValidation|In order to use free CI/CD minutes on shared runners, you'll need to validate your account using one of our verification options. If you prefer not to, you can run pipelines by bringing your own runners and disabling shared runners for your project."
msgstr ""
@@ -1932,7 +1950,7 @@ msgid "Activate Service Desk"
msgstr "서비스 ë°ìŠ¤í¬ 활성화"
msgid "Activated on"
-msgstr ""
+msgstr "활성화 ë¨"
msgid "Active"
msgstr "활성"
@@ -1962,7 +1980,7 @@ msgid "Add \"%{value}\""
msgstr "\"%{value}\" 추가"
msgid "Add %{linkStart}assets%{linkEnd} to your Release. GitLab automatically includes read-only assets, like source code and release evidence."
-msgstr "%{linkStart}ì—ì…‹%{linkEnd}ì„ ë¦´ë¦¬ì¦ˆì— ì¶”ê°€í•˜ì„¸ìš”. GitLabì´ ìžë™ìœ¼ë¡œ 소스 코드나 릴리즈 ì¦ê±°ì²˜ëŸ¼ ìžë™ìœ¼ë¡œ í¬í•¨í•©ë‹ˆë‹¤."
+msgstr "%{linkStart}ì—ì…‹%{linkEnd}ì„ ë¦´ë¦¬ìŠ¤ì— ì¶”ê°€í•˜ì„¸ìš”. GitLabì´ ìžë™ìœ¼ë¡œ 소스 코드나 릴리즈 ì¦ê±°ì²˜ëŸ¼ ìžë™ìœ¼ë¡œ í¬í•¨í•©ë‹ˆë‹¤."
msgid "Add CHANGELOG"
msgstr "ë³€ê²½ëœ ë¡œê·¸ 추가"
@@ -1977,7 +1995,7 @@ msgid "Add LICENSE"
msgstr "LICENSE 추가"
msgid "Add License"
-msgstr ""
+msgstr "ë¼ì´ì„ ìŠ¤ 추가"
msgid "Add New Site"
msgstr "새 사ì´íŠ¸ 추가"
@@ -1998,7 +2016,7 @@ msgid "Add a GPG key"
msgstr "GPG 키 추가"
msgid "Add a GPG key for secure access to GitLab. %{help_link_start}Learn more.%{help_link_end}"
-msgstr ""
+msgstr "보안 ì—°ê²°ì„ ìœ„í•´ GitLabì— GPG 키를 추가하세요. %{help_link_start}ë” ì•Œì•„ë³´ê¸°%{help_link_end}"
msgid "Add a Terms of Service agreement and Privacy Policy for users of this GitLab instance."
msgstr ""
@@ -2007,7 +2025,7 @@ msgid "Add a bullet list"
msgstr ""
msgid "Add a checklist"
-msgstr "ì²´í¬ë¦¬ìŠ¤íŠ¸ì— 추가하기"
+msgstr "ì²´í¬ë¦¬ìŠ¤íŠ¸ 추가"
msgid "Add a collapsible section"
msgstr ""
@@ -2021,7 +2039,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2037,7 +2055,7 @@ msgid "Add a numbered list"
msgstr "번호 매기기 ëª©ë¡ ì¶”ê°€"
msgid "Add a related epic"
-msgstr ""
+msgstr "관련 ì—픽 추가"
msgid "Add a related issue"
msgstr "관련 ì´ìŠˆ 추가"
@@ -2048,17 +2066,20 @@ msgstr ""
msgid "Add a table"
msgstr "í…Œì´ë¸” 추가"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
msgid "Add a to do"
-msgstr ""
+msgstr "í•  ì¼ ì¶”ê°€"
msgid "Add an SSH key"
msgstr "SSH 키 추가"
msgid "Add an SSH key for secure access to GitLab. %{help_link_start}Learn more.%{help_link_end}"
-msgstr ""
+msgstr "보안 ì—°ê²°ì„ ìœ„í•´ GitLabì— SSH 키를 추가하세요. %{help_link_start}ë” ì•Œì•„ë³´ê¸°%{help_link_end}"
msgid "Add an existing issue"
msgstr "기존 ì´ìŠˆ 추가"
@@ -2085,16 +2106,19 @@ msgid "Add comment now"
msgstr "댓글 추가"
msgid "Add comment to design"
-msgstr ""
+msgstr "ë””ìžì¸ì— 댓글 추가"
+
+msgid "Add comment to incident timeline"
+msgstr "ì¸ì‹œë˜íŠ¸ 타임ë¼ì¸ì— 댓글 추가"
msgid "Add comment..."
-msgstr ""
+msgstr "댓글 추가..."
msgid "Add commit messages as comments to Asana tasks. %{docs_link}"
-msgstr ""
+msgstr "커밋 메시지를 Asana 태스í¬ì— 댓글로 추가하기 %{docs_link}"
msgid "Add commit messages as comments to Pivotal Tracker stories. %{docs_link}"
-msgstr ""
+msgstr "커밋 메시지를 Pivotal Tracker ìŠ¤í† ë¦¬ì— ëŒ“ê¸€ë¡œ 추가하기 %{docs_link}"
msgid "Add customer relation contact(s)."
msgstr ""
@@ -2118,10 +2142,10 @@ msgid "Add environment"
msgstr ""
msgid "Add existing confidential %{issuableType}"
-msgstr ""
+msgstr "기존 대외비 %{issuableType} 추가"
msgid "Add existing issue"
-msgstr ""
+msgstr "기존 ì´ìŠˆ 추가"
msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
msgstr "ë©”ì¼ ë¨¸ë¦¬ë§ ë° ê¼¬ë¦¬ë§ì„ 추가하십시오. 단, ìƒ‰ìƒ ì„¤ì •ì€ ì• í”Œë¦¬ì¼€ì´ì…˜ ì¸í„°íŽ˜ì´ìŠ¤ 안ì—서만 ì ìš©ë˜ëŠ” ì ì„ 참고해 주십시오."
@@ -2138,6 +2162,12 @@ msgstr "키 추가"
msgid "Add label(s)"
msgstr "ë ˆì´ë¸” 추가"
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr "ëª©ë¡ ì¶”ê°€"
@@ -2151,7 +2181,7 @@ msgid "Add or remove a user."
msgstr ""
msgid "Add or remove previously merged commits"
-msgstr ""
+msgstr "ì´ì „ì— ë¨¸ì§€ëœ ì»¤ë°‹ 추가 ë˜ëŠ” 제거"
msgid "Add or subtract spent time"
msgstr ""
@@ -2202,7 +2232,7 @@ msgid "Add to tree"
msgstr "íŠ¸ë¦¬ì— ì¶”ê°€"
msgid "Add topics to projects to help users find them."
-msgstr ""
+msgstr "사용ìžê°€ 쉽게 ì°¾ì„ ìˆ˜ 있ë„ë¡ í”„ë¡œì íŠ¸ì— 주제를 추가합니다"
msgid "Add trigger"
msgstr ""
@@ -2217,7 +2247,7 @@ msgid "Add variable"
msgstr "변수 추가"
msgid "Add vulnerability finding"
-msgstr ""
+msgstr "발견한 ì·¨ì•½ì  ì¶”ê°€"
msgid "Add webhook"
msgstr "웹훅 추가"
@@ -2241,7 +2271,7 @@ msgid "AddMember|Invite limit of %{daily_invites} per day exceeded"
msgstr "ì¼ì¼ 초대 í•œë„ %{daily_invites} 초과"
msgid "AddMember|Invites cannot be blank"
-msgstr ""
+msgstr "초대 í•­ëª©ì€ ë¹„ì›Œë‘˜ 수 없습니다."
msgid "AddMember|No invite source provided."
msgstr "초대 소스가 제공ë˜ì§€ 않았습니다."
@@ -2256,10 +2286,10 @@ msgid "Added"
msgstr "추가ë¨"
msgid "Added %{epic_ref} as a child epic."
-msgstr ""
+msgstr "하위 ì—픽으로 %{epic_ref} 추가했습니다."
msgid "Added %{label_references} %{label_text}."
-msgstr ""
+msgstr "%{label_references} %{label_text} 추가ë¨."
msgid "Added a to do."
msgstr "í• ì¼ì— 추가ë¨."
@@ -2268,7 +2298,7 @@ msgid "Added an issue to an epic."
msgstr "ì´ìŠˆë¥¼ ì—í”½ì— ì¶”ê°€í–ˆìŠµë‹ˆë‹¤."
msgid "Added for this merge request"
-msgstr ""
+msgstr "머지 ë¦¬í€˜ìŠ¤íŠ¸ì— ì¶”ê°€ë¨"
msgid "Added in this version"
msgstr "ì´ ë²„ì „ì— ì¶”ê°€ë¨"
@@ -2280,7 +2310,7 @@ msgid "Additional minutes"
msgstr "추가 시간(분)"
msgid "Additional minutes:"
-msgstr ""
+msgstr "추가 시간(분):"
msgid "Additional text"
msgstr "추가 í…스트"
@@ -2292,7 +2322,7 @@ msgid "Additional text to show on the Help page"
msgstr "ë„ì›€ë§ íŽ˜ì´ì§€ì— 표시할 추가 í…스트"
msgid "Additional text to show on the sign-in page"
-msgstr ""
+msgstr "ë¡œê·¸ì¸ íŽ˜ì´ì§€ì— 표시할 추가 í…스트"
msgid "Address"
msgstr "주소"
@@ -2301,22 +2331,25 @@ msgid "Adds"
msgstr "추가하기"
msgid "Adds %{epic_ref} as child epic."
-msgstr ""
+msgstr "하위 ì—픽으로 %{epic_ref} 추가 하기"
msgid "Adds %{labels} %{label_text}."
msgstr "%{labels} %{label_text} 추가"
msgid "Adds a Zoom meeting."
+msgstr "Zoom íšŒì˜ ì¶”ê°€í•˜ê¸°"
+
+msgid "Adds a timeline event to incident."
msgstr ""
msgid "Adds a to do."
-msgstr ""
+msgstr "í•  ì¼ ì¶”ê°€."
msgid "Adds an issue to an epic."
msgstr "ì´ìŠˆë¥¼ ì—í”½ì— ì¶”ê°€í•©ë‹ˆë‹¤."
msgid "Adds email participant(s)."
-msgstr ""
+msgstr "ì´ë©”ì¼ ì°¸ê°€ìž ì¶”ê°€í•˜ê¸°"
msgid "Adds this %{issuable_type} as related to the %{issuable_type} it was created from"
msgstr ""
@@ -2324,9 +2357,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr "관리ìž"
@@ -2349,16 +2379,16 @@ msgid "Admin Section"
msgstr "ê´€ë¦¬ìž ì˜ì—­"
msgid "Admin mode already enabled"
-msgstr ""
+msgstr "ê´€ë¦¬ìž ëª¨ë“œ ì´ë¯¸ 활성화 ë¨"
msgid "Admin mode disabled"
-msgstr ""
+msgstr "ê´€ë¦¬ìž ëª¨ë“œ 비활성화"
msgid "Admin mode enabled"
-msgstr ""
+msgstr "ê´€ë¦¬ìž ëª¨ë“œ 활성화"
msgid "Admin navigation"
-msgstr ""
+msgstr "ê´€ë¦¬ìž ë„¤ë¹„ê²Œì´ì…˜"
msgid "Admin notes"
msgstr "관리 노트"
@@ -2367,13 +2397,13 @@ msgid "AdminArea|%{billable_users_link_start}Learn more%{billable_users_link_end
msgstr ""
msgid "AdminArea|Active users"
-msgstr ""
+msgstr "활성 사용ìž"
msgid "AdminArea|All users created in the instance, including users who are not %{billable_users_link_start}billable users%{billable_users_link_end}."
msgstr ""
msgid "AdminArea|Billable users"
-msgstr ""
+msgstr "청구 가능한 사용ìž"
msgid "AdminArea|Blocked users"
msgstr "ì°¨ë‹¨ëœ ì‚¬ìš©ìž"
@@ -2397,7 +2427,7 @@ msgid "AdminArea|Groups"
msgstr "그룹"
msgid "AdminArea|Guest"
-msgstr ""
+msgstr "게스트"
msgid "AdminArea|Included Free in license"
msgstr ""
@@ -2409,10 +2439,10 @@ msgid "AdminArea|Latest groups"
msgstr "최근 그룹"
msgid "AdminArea|Latest projects"
-msgstr ""
+msgstr "최근 프로ì íŠ¸"
msgid "AdminArea|Latest users"
-msgstr ""
+msgstr "최근 사용ìž"
msgid "AdminArea|Maintainer"
msgstr ""
@@ -2421,7 +2451,7 @@ msgid "AdminArea|Manage applications for your instance that can use GitLab as an
msgstr ""
msgid "AdminArea|Minimal access"
-msgstr ""
+msgstr "최소 액세스"
msgid "AdminArea|New group"
msgstr "새 그룹"
@@ -2442,7 +2472,7 @@ msgid "AdminArea|Projects"
msgstr "프로ì íŠ¸"
msgid "AdminArea|Reporter"
-msgstr ""
+msgstr "리í¬í„°"
msgid "AdminArea|Sign up for the GitLab Security Newsletter to get notified for security updates."
msgstr "보안 ì—…ë°ì´íŠ¸ì— 대한 ì•Œë¦¼ì„ ë°›ìœ¼ë ¤ë©´ GitLab 보안 ë‰´ìŠ¤ë ˆí„°ì— ê°€ìž…í•˜ì„¸ìš”."
@@ -2499,10 +2529,10 @@ msgid "AdminLabels|Labels created here will be automatically added to new projec
msgstr ""
msgid "AdminLabels|They can be used to categorize issues and merge requests."
-msgstr ""
+msgstr "ì´ìŠˆ ë° ë¨¸ì§€ 리퀘스트 ë¶„ë¥˜ì— ì‚¬ìš©í•  수 있습니다."
msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources, including issues and merge requests. After you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
-msgstr ""
+msgstr "ë‹¹ì‹ ì€ %{projectName} 프로ì íŠ¸, ë ˆí¬ì§€í† ë¦¬ì™€ ì´ìŠˆì™€ 머지 리퀘스트를 í¬í•¨í•œ 모든 ê´€ë ¨ëœ ë¦¬ì†ŒìŠ¤ë¥¼ 삭제하려고 합니다. ë‹¹ì‹ ì´ í™•ì¸í•œ 후 %{strong_start}프로ì íŠ¸ ì‚­ì œ%{strong_end}를 누르면 ë˜ëŒë¦¬ê±°ë‚˜ 복구할 수 없습니다."
msgid "AdminProjects|Delete"
msgstr "삭제"
@@ -2525,20 +2555,20 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr "Auto DevOps ë„ë©”ì¸"
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr "Let's Encrypt 설정"
-msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
msgstr ""
+msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
+msgstr "비활성 프로ì íŠ¸ê°€ ìžë™ìœ¼ë¡œ ì‚­ì œë˜ëŠ” ì‹œê°„ì„ ì„¤ì •í•©ë‹ˆë‹¤. %{linkStart}비활성 프로ì íŠ¸ëž€ 무엇입니까?%{linkEnd}"
+
msgid "AdminSettings|Delete inactive projects"
-msgstr ""
+msgstr "비활성 프로ì íŠ¸ ì‚­ì œ"
msgid "AdminSettings|Delete inactive projects that exceed"
msgstr ""
@@ -2556,7 +2586,7 @@ msgid "AdminSettings|Disable public access to Pages sites"
msgstr ""
msgid "AdminSettings|Display a banner on merge requests in projects with no pipelines to initiate steps to add a .gitlab-ci.yml file."
-msgstr ""
+msgstr "파ì´í”„ë¼ì¸ì´ 없는 프로ì íŠ¸ì˜ 머지 ë¦¬í€˜ìŠ¤íŠ¸ì— ë°°ë„ˆë¥¼ 표시하여 .gitlab-ci.yml 파ì¼ì„ 추가하는 단계를 시작합니다."
msgid "AdminSettings|Domain verification is an essential security measure for public GitLab sites. Users are required to demonstrate they control a domain before it is enabled. %{link_start}Learn more.%{link_end}"
msgstr ""
@@ -2568,12 +2598,12 @@ msgid "AdminSettings|Email from GitLab - email users right from the Admin Area.
msgstr ""
msgid "AdminSettings|Enable Registration Features"
-msgstr ""
+msgstr "ë“±ë¡ ê¸°ëŠ¥ 활성화"
msgid "AdminSettings|Enable Service Ping"
-msgstr ""
+msgstr "서비스 핑 활성화"
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2597,6 +2627,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr "그룹 ë° í”„ë¡œì íŠ¸ì— 대한 초대 프로세스 실행"
+
msgid "AdminSettings|Feed token"
msgstr "피드 토í°"
@@ -2613,7 +2646,7 @@ msgid "AdminSettings|If there isn't any existing index, GitLab creates one."
msgstr ""
msgid "AdminSettings|Import sources"
-msgstr ""
+msgstr "소스 가져오기"
msgid "AdminSettings|Inactive project deletion"
msgstr ""
@@ -2661,7 +2694,7 @@ msgid "AdminSettings|New CI/CD variables in projects and groups default to prote
msgstr "프로ì íŠ¸ ë° ê·¸ë£¹ì˜ ìƒˆ CI/CD 변수는 기본ì ìœ¼ë¡œ 보호ë¨ìœ¼ë¡œ 설정ë©ë‹ˆë‹¤."
msgid "AdminSettings|No required pipeline"
-msgstr ""
+msgstr "필수 파ì´í”„ë¼ì¸ ì—†ìŒ"
msgid "AdminSettings|Only enable search after installing the plugin, enabling indexing, and recreating the index."
msgstr ""
@@ -2670,10 +2703,10 @@ msgid "AdminSettings|Pause Elasticsearch indexing"
msgstr ""
msgid "AdminSettings|Preview payload"
-msgstr ""
+msgstr "페ì´ë¡œë“œ 미리보기"
msgid "AdminSettings|Project export"
-msgstr ""
+msgstr "프로ì íŠ¸ 내보내기"
msgid "AdminSettings|Protect CI/CD variables by default"
msgstr ""
@@ -2685,13 +2718,13 @@ msgid "AdminSettings|Require users to prove ownership of custom domains"
msgstr ""
msgid "AdminSettings|Required pipeline configuration"
-msgstr ""
+msgstr "필수 파ì´í”„ë¼ì¸ 설정"
msgid "AdminSettings|Requires %{linkStart}email notifications%{linkEnd}"
msgstr ""
msgid "AdminSettings|Restrict group access by IP address. %{link_start}Learn more%{link_end}."
-msgstr ""
+msgstr "IP 주소로 그룹 액세스를 제한합니다. %{link_start}ìžì„¸ížˆ 알아보기%{link_end}."
msgid "AdminSettings|Save %{name} limits"
msgstr ""
@@ -2712,7 +2745,7 @@ msgid "AdminSettings|Send email to maintainers after project is inactive for"
msgstr ""
msgid "AdminSettings|Send warning email"
-msgstr ""
+msgstr "경고 ì´ë©”ì¼ ë³´ë‚´ê¸°"
msgid "AdminSettings|Service ping is disabled in your configuration file, and cannot be enabled through this form. For more information, see the documentation on %{link_start}deactivating service ping%{link_end}."
msgstr ""
@@ -2724,7 +2757,7 @@ msgid "AdminSettings|Set a CI/CD template as the required pipeline configuration
msgstr ""
msgid "AdminSettings|Set limit to 0 to disable it."
-msgstr ""
+msgstr "ì´ë¥¼ 비활성화하려면 제한 ê°’ì„ 0으로 설정하십시오."
msgid "AdminSettings|Set the initial name and protections for the default branch of new repositories created in the instance."
msgstr ""
@@ -2762,8 +2795,11 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
-msgstr ""
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr "AdminSettings|IAM ìžê²© ì¦ëª…으로 AWS OpenSearch Service 사용"
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
+msgstr "ì‚¬ìš©ìž ë° ê·¸ë£¹ì€ ê·¸ë£¹ ë˜ëŠ” 프로ì íŠ¸ì— 추가ë˜ê¸° ì „ì— ì´ˆëŒ€ë¥¼ 수ë½í•´ì•¼ 합니다."
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
msgstr ""
@@ -2838,7 +2874,7 @@ msgid "AdminUsers|Access Git repositories"
msgstr ""
msgid "AdminUsers|Access level"
-msgstr ""
+msgstr "액세스 레벨"
msgid "AdminUsers|Access the API"
msgstr ""
@@ -2868,7 +2904,7 @@ msgid "AdminUsers|Approve"
msgstr "승ì¸"
msgid "AdminUsers|Approve user %{username}?"
-msgstr ""
+msgstr "%{username} 사용ìžë¥¼ 승ì¸í• ê¹Œìš”?"
msgid "AdminUsers|Approved users can:"
msgstr ""
@@ -2967,7 +3003,7 @@ msgid "AdminUsers|External users cannot see internal or private projects unless
msgstr ""
msgid "AdminUsers|For more information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
-msgstr ""
+msgstr "ìžì„¸í•œ ë‚´ìš©ì€ %{link_start}ì‚¬ìš©ìž ê³„ì • ì‚­ì œ 문서%{link_end}를 참조하십시오."
msgid "AdminUsers|Here are some helpful links to help you manage your instance:"
msgstr ""
@@ -2987,11 +3023,14 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr "ì´ê²ƒì€ 나!"
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
msgid "AdminUsers|Limits"
-msgstr ""
+msgstr "í•œë„"
msgid "AdminUsers|Linkedin"
msgstr "LinkedIn"
@@ -3042,7 +3081,7 @@ msgid "AdminUsers|Reject user %{username}?"
msgstr ""
msgid "AdminUsers|Rejected users:"
-msgstr ""
+msgstr "ê±°ë¶€ëœ ì‚¬ìš©ìž:"
msgid "AdminUsers|Reset link will be generated and sent to the user. User will be forced to set the password on first sign in."
msgstr ""
@@ -3060,7 +3099,7 @@ msgid "AdminUsers|Send email to users"
msgstr "유저ì—게 ì´ë©”ì¼ ë³´ë‚´ê¸°"
msgid "AdminUsers|Skype"
-msgstr ""
+msgstr "Skype"
msgid "AdminUsers|Sort by"
msgstr "정렬 기준"
@@ -3072,19 +3111,19 @@ msgid "AdminUsers|The user can't access git repositories."
msgstr ""
msgid "AdminUsers|The user can't log in."
-msgstr ""
+msgstr "사용ìžëŠ” 로그ì¸í•  수 없습니다."
msgid "AdminUsers|The user has unlimited access to all groups, projects, users, and features."
msgstr ""
msgid "AdminUsers|The user will be logged out"
-msgstr ""
+msgstr "사용ìžëŠ” 로그아웃ë©ë‹ˆë‹¤."
msgid "AdminUsers|The user will not be able to access git repositories"
msgstr ""
msgid "AdminUsers|The user will not be able to access the API"
-msgstr ""
+msgstr "사용ìžëŠ” APIì— ì ‘ê·¼í•  수 없습니다."
msgid "AdminUsers|The user will not be able to use slash commands"
msgstr ""
@@ -3114,13 +3153,13 @@ msgid "AdminUsers|Unlock user %{username}?"
msgstr ""
msgid "AdminUsers|User administration"
-msgstr ""
+msgstr "ì‚¬ìš©ìž ê´€ë¦¬"
msgid "AdminUsers|User will not be able to access git repositories"
msgstr ""
msgid "AdminUsers|User will not be able to login"
-msgstr ""
+msgstr "사용ìžëŠ” 로그ì¸í•  수 없습니다."
msgid "AdminUsers|Users"
msgstr "사용ìž"
@@ -3132,13 +3171,13 @@ msgid "AdminUsers|Validate user account"
msgstr ""
msgid "AdminUsers|View pending member requests"
-msgstr ""
+msgstr "대기 ì¤‘ì¸ íšŒì› ìš”ì²­ 보기"
msgid "AdminUsers|Website URL"
msgstr "웹사ì´íŠ¸ URL"
msgid "AdminUsers|What can I do?"
-msgstr ""
+msgstr "ë‚´ê°€ ë¬´ì—‡ì„ í•  수 있습니까?"
msgid "AdminUsers|What does this mean?"
msgstr ""
@@ -3156,7 +3195,7 @@ msgid "AdminUsers|Without projects"
msgstr "프로ì íŠ¸ ì—†ì´"
msgid "AdminUsers|You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strongStart}block user%{strongEnd} feature instead. Once you %{strongStart}Delete user%{strongEnd}, it cannot be undone or recovered."
-msgstr ""
+msgstr "%{username}ì„ ì˜êµ¬ì ìœ¼ë¡œ 삭제하려고 합니다. 해당 사용ìžì™€ ì—°ê´€ëœ ì´ìŠˆ, 머지 리퀘스트(MR) ë° ê·¸ë£¹ì´ ì „ì—­ì ìœ¼ë¡œ \"Ghost-user\"으로 변경ë©ë‹ˆë‹¤. ë°ì´í„° ì†ì‹¤ì„ 방지하기 위해 %{strongStart}ì‚¬ìš©ìž ì°¨ë‹¨%{strongEnd} ê¸°ëŠ¥ì„ ëŒ€ì‹  사용해보세요. %{strongStart}ì‚¬ìš©ìž ì‚­ì œ%{strongEnd}ì‹œ ë˜ëŒë¦¬ê±°ë‚˜, 복구할 수 없습니다."
msgid "AdminUsers|You are about to permanently delete the user %{username}. This will delete all of the issues, merge requests, and groups linked to them. To avoid data loss, consider using the %{strongStart}block user%{strongEnd} feature instead. Once you %{strongStart}Delete user%{strongEnd}, it cannot be undone or recovered."
msgstr ""
@@ -3186,7 +3225,7 @@ msgid "AdminUsers|You must transfer ownership or delete the groups owned by this
msgstr ""
msgid "AdminUsers|Your GitLab instance has reached the maximum allowed %{user_doc_link} set by an instance admin."
-msgstr ""
+msgstr "GitLab ì¸ìŠ¤í„´ìŠ¤ 갯수가 ì¸ìŠ¤í„´ìŠ¤ 관리ìžê°€ 설정한 제한값 %{user_doc_link}ê°œì— ë„달했습니다."
msgid "AdminUsers|approve them"
msgstr ""
@@ -3195,7 +3234,7 @@ msgid "AdminUsers|contact our support team"
msgstr "지ì›íŒ€ì— 문ì˜"
msgid "AdminUsers|docs"
-msgstr ""
+msgstr "문서"
msgid "AdminUsers|user cap"
msgstr ""
@@ -3228,10 +3267,10 @@ msgid "Admin|The number of max users in your instance exceeds the number of user
msgstr ""
msgid "Admin|View pending user approvals"
-msgstr ""
+msgstr "ìŠ¹ì¸ ëŒ€ê¸° ì¤‘ì¸ ì‚¬ìš©ìž ë³´ê¸°"
msgid "Admin|Your instance has reached its user cap"
-msgstr ""
+msgstr "ì¸ìŠ¤í„´ìŠ¤ê°€ ì‚¬ìš©ìž í•œë„ì— ë„달했습니다."
msgid "Advanced"
msgstr "고급"
@@ -3273,13 +3312,13 @@ msgid "After you've reviewed these contribution guidelines, you'll be all set to
msgstr ""
msgid "Akismet"
-msgstr ""
+msgstr "Akismet"
msgid "Akismet API Key"
-msgstr ""
+msgstr "Akismet API 키"
msgid "Akismet helps prevent the creation of spam issues in public projects."
-msgstr ""
+msgstr "Akismetì€ ê³µê°œ 프로ì íŠ¸ì—ì„œ ìŠ¤íŒ¸ì´ ë°œìƒí•˜ëŠ” ê²ƒì„ ë°©ì§€í•©ë‹ˆë‹¤."
msgid "Alert"
msgstr ""
@@ -3288,7 +3327,7 @@ msgid "AlertManagement|Acknowledged"
msgstr ""
msgid "AlertManagement|Activity feed"
-msgstr ""
+msgstr "í™œë™ í”¼ë“œ"
msgid "AlertManagement|Alert"
msgstr ""
@@ -3312,10 +3351,10 @@ msgid "AlertManagement|All alerts"
msgstr ""
msgid "AlertManagement|Assign status"
-msgstr ""
+msgstr "ìƒíƒœ 지정"
msgid "AlertManagement|Assignees"
-msgstr ""
+msgstr "담당ìž"
msgid "AlertManagement|Authorize external service"
msgstr ""
@@ -3330,10 +3369,10 @@ msgid "AlertManagement|Edit"
msgstr "편집"
msgid "AlertManagement|Environment"
-msgstr ""
+msgstr "환경"
msgid "AlertManagement|Events"
-msgstr ""
+msgstr "ì´ë²¤íŠ¸"
msgid "AlertManagement|Incident"
msgstr ""
@@ -3348,7 +3387,7 @@ msgid "AlertManagement|Metrics weren't available in the alerts payload."
msgstr ""
msgid "AlertManagement|More information"
-msgstr ""
+msgstr "추가 정보"
msgid "AlertManagement|No alert data to display."
msgstr ""
@@ -3360,13 +3399,13 @@ msgid "AlertManagement|No alerts to display."
msgstr ""
msgid "AlertManagement|None"
-msgstr ""
+msgstr "ì—†ìŒ"
msgid "AlertManagement|Open"
msgstr ""
msgid "AlertManagement|Please try again."
-msgstr ""
+msgstr "다시 ì‹œë„해주세요."
msgid "AlertManagement|Reported %{when}"
msgstr ""
@@ -3375,22 +3414,22 @@ msgid "AlertManagement|Reported %{when} by %{tool}"
msgstr ""
msgid "AlertManagement|Resolved"
-msgstr ""
+msgstr "í•´ê²°ë¨"
msgid "AlertManagement|Runbook"
-msgstr ""
+msgstr "런ë¶"
msgid "AlertManagement|Service"
-msgstr ""
+msgstr "서비스"
msgid "AlertManagement|Severity"
-msgstr ""
+msgstr "심ê°ë„"
msgid "AlertManagement|Start time"
-msgstr ""
+msgstr "시작 시간"
msgid "AlertManagement|Status"
-msgstr ""
+msgstr "ìƒíƒœ"
msgid "AlertManagement|Surface alerts in GitLab"
msgstr ""
@@ -3402,7 +3441,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 ""
@@ -3417,13 +3456,13 @@ msgid "AlertManagement|This assignee cannot be assigned to this alert."
msgstr ""
msgid "AlertManagement|Tool"
-msgstr ""
+msgstr "ë„구"
msgid "AlertManagement|Triggered"
msgstr ""
msgid "AlertManagement|Value"
-msgstr ""
+msgstr "ê°’"
msgid "AlertManagement|View incident"
msgstr ""
@@ -3441,7 +3480,7 @@ msgid "AlertMappingBuilder|Payload alert key"
msgstr ""
msgid "AlertMappingBuilder|Select key"
-msgstr ""
+msgstr "키 ì„ íƒ"
msgid "AlertMappingBuilder|Title is a required field for alerts in GitLab. Should the payload field you specified not be available, specifiy which field we should use instead. "
msgstr ""
@@ -3456,10 +3495,10 @@ msgid "AlertSettings|Alert settings"
msgstr ""
msgid "AlertSettings|Authorization key"
-msgstr ""
+msgstr "ì¸ì¦ 키"
msgid "AlertSettings|Configure details"
-msgstr ""
+msgstr "ìƒì„¸ 설정"
msgid "AlertSettings|Current integrations"
msgstr ""
@@ -3519,7 +3558,7 @@ msgid "AlertSettings|Prometheus API base URL"
msgstr ""
msgid "AlertSettings|Reset Key"
-msgstr ""
+msgstr "키 리셋"
msgid "AlertSettings|Reset the mapping"
msgstr ""
@@ -3528,7 +3567,7 @@ msgid "AlertSettings|Sample payload has been parsed. You can now map the fields.
msgstr ""
msgid "AlertSettings|Save & create test alert"
-msgstr ""
+msgstr "저장 & 테스트 알람 ìƒì„±"
msgid "AlertSettings|Save integration"
msgstr ""
@@ -3552,7 +3591,7 @@ msgid "AlertSettings|The form has unsaved changes. How would you like to proceed
msgstr ""
msgid "AlertSettings|To create a custom mapping, enter an example payload from your monitoring tool, in JSON format. Select the \"Parse payload fields\" button to continue."
-msgstr ""
+msgstr "ì‚¬ìš©ìž ì •ì˜ ë§µí•‘ì„ ìƒì„±í•˜ë ¤ë©´ ëª¨ë‹ˆí„°ë§ ë„구를 통해 예시 페ì´ë¡œë“œë¥¼ JSON형ì‹ìœ¼ë¡œ 입력하세요. 계ì†í•˜ë ¤ë©´ \"페ì´ë¡œë“œ í•„ë“œ 분ì„\"ë²„íŠ¼ì„ ì„ íƒí•˜ì„¸ìš”."
msgid "AlertSettings|URL cannot be blank and must start with http: or https:."
msgstr ""
@@ -3633,13 +3672,13 @@ msgid "All %{replicableType} are being scheduled for %{action}"
msgstr ""
msgid "All (default)"
-msgstr ""
+msgstr "ëª¨ë‘ (기본값)"
msgid "All GitLab"
msgstr "모든 GitLab"
msgid "All Members"
-msgstr "모든 맵버"
+msgstr "모든 구성ì›"
msgid "All branches"
msgstr "모든 브랜치"
@@ -3654,13 +3693,13 @@ msgid "All email addresses will be used to identify your commits."
msgstr "모든 ì´ë©”ì¼ ì£¼ì†ŒëŠ” ì»¤ë°‹ì„ ì‹ë³„하는 ë° ì‚¬ìš©ë©ë‹ˆë‹¤."
msgid "All environments"
-msgstr ""
+msgstr "모든 환경"
msgid "All groups and projects"
msgstr "모든 그룹과 프로ì íŠ¸"
msgid "All issues"
-msgstr ""
+msgstr "모든 ì´ìŠˆ"
msgid "All issues for this milestone are closed."
msgstr "ì´ ë§ˆì¼ìŠ¤í†¤ì— 대한 모든 ì´ìŠˆê°€ 닫혔습니다."
@@ -3684,16 +3723,16 @@ msgid "All projects selected"
msgstr "모든 프로ì íŠ¸ê°€ ì„ íƒë¨"
msgid "All protected branches"
-msgstr ""
+msgstr "모든 ë³´í˜¸ëœ ë¸Œëžœì¹˜"
msgid "All threads resolved!"
-msgstr ""
+msgstr "모든 스레드가 í•´ê²°ë˜ì—ˆìŠµë‹ˆë‹¤!"
msgid "All users in this group must set up two-factor authentication"
msgstr ""
msgid "All users must accept the Terms of Service and Privacy Policy to access GitLab"
-msgstr ""
+msgstr "GitLabì— ì•¡ì„¸ìŠ¤í•˜ë ¤ë©´ 모든 사용ìžê°€ 서비스 약관 ë° ê°œì¸ ì •ë³´ 보호 ì •ì±…ì— ë™ì˜í•´ì•¼ 합니다."
msgid "All users must have a name."
msgstr "모든 사용ìžëŠ” ì´ë¦„ì´ ìžˆì–´ì•¼í•©ë‹ˆë‹¤."
@@ -3705,28 +3744,28 @@ msgid "Allow \"%{group_name}\" to sign you in"
msgstr "\"%{group_name}\"ì„ ìž…ë ¥í•˜ë©´ ë¡œê·¸ì¸ í•  수 있습니다."
msgid "Allow access to members of the following group"
-msgstr ""
+msgstr "ë‹¤ìŒ ê·¸ë£¹ì˜ êµ¬ì„±ì›ì— 대한 액세스 허용"
msgid "Allow access to the following IP addresses"
-msgstr ""
+msgstr "ë‹¤ìŒ IP ì£¼ì†Œì— ì ‘ê·¼ì„ í—ˆìš©"
msgid "Allow commits from members who can merge to the target branch. %{link_start}About this feature.%{link_end}"
-msgstr ""
+msgstr "ëŒ€ìƒ ë¸Œëžœì¹˜ì— ë¨¸ì§€í•  수 있는 구성ì›ì˜ ì»¤ë°‹ì„ í—ˆìš©í•©ë‹ˆë‹¤. %{link_start}ì´ ê¸°ëŠ¥ì— ëŒ€í•´ 알아보기.%{link_end}"
msgid "Allow group owners to manage LDAP-related settings"
msgstr "그룹 소유ìžê°€ LDAP 관련 ì„¤ì •ì„ ê´€ë¦¬í•˜ë„ë¡ í—ˆìš©"
msgid "Allow non-administrators access to the performance bar"
-msgstr ""
+msgstr "관리ìžê°€ ì•„ë‹Œ ìžê°€ 성능 í‘œì‹œì¤„ì— ì•¡ì„¸ìŠ¤í•  수 있ë„ë¡ í—ˆìš©"
msgid "Allow only the selected protocols to be used for Git access."
-msgstr ""
+msgstr "ì„ íƒí•œ 프로토콜만 Git ì ‘ê·¼ì„ í—ˆìš©"
msgid "Allow owners to manage default branch protection per group."
-msgstr ""
+msgstr "소유ìžê°€ 그룹별로 기본 브랜치 보호를 관리하ë„ë¡ í—ˆìš©í•©ë‹ˆë‹¤."
msgid "Allow owners to manually add users outside of LDAP"
-msgstr ""
+msgstr "소유ìžê°€ LDAP ì™¸ë¶€ì— ì‚¬ìš©ìžë¥¼ 수ë™ìœ¼ë¡œ 추가하ë„ë¡ í—ˆìš©"
msgid "Allow password authentication for Git over HTTP(S)"
msgstr ""
@@ -3750,7 +3789,7 @@ msgid "Allow this key to push to this repository"
msgstr ""
msgid "Allow use of licensed EE features"
-msgstr ""
+msgstr "ë¼ì´ì„ ìŠ¤ëœ EE 기능 사용 허용"
msgid "Allow users to dismiss the broadcast message"
msgstr ""
@@ -3761,9 +3800,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3795,7 +3831,7 @@ msgid "Already blocked"
msgstr "ì´ë¯¸ 차단ë¨"
msgid "Already have login and password?"
-msgstr ""
+msgstr "ì´ë¯¸ ë¡œê·¸ì¸ ë¹„ë°€ë²ˆí˜¸ê°€ 있습니까?"
msgid "Also called \"Issuer\" or \"Relying party trust identifier\""
msgstr "\"issuer\" ë˜ëŠ” \"Relying party trust identifier\" ë¼ê³ ë„ 합니다."
@@ -3831,21 +3867,24 @@ msgid "An Administrator has set the maximum expiration date to %{maxDate}. %{hel
msgstr ""
msgid "An Enterprise User GitLab account has been created for you by your organization:"
-msgstr ""
+msgstr "ë‹¹ì‹ ì˜ ì¡°ì§ì—ì„œ ë‹¹ì‹ ì˜ GitLab 엔터프ë¼ì´ì¦ˆ ì‚¬ìš©ìž ê³„ì •ì„ ìƒì„±í–ˆìŠµë‹ˆë‹¤."
msgid "An administrator changed the password for your GitLab account on %{link_to}."
msgstr ""
msgid "An alert has been resolved in %{project_path}."
-msgstr ""
+msgstr "%{project_path}ì—ì„œ 경고가 í•´ê²°ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "An alert has been triggered in %{project_path}."
-msgstr ""
+msgstr "%{project_path}ì—ì„œ 경고가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
msgstr "%{link_to_client} ì´ë¼ëŠ” ì‘ìš© í”„ë¡œê·¸ëž¨ì´ GitLab ê³„ì •ì— ëŒ€í•œ 액세스를 요청하고 있습니다."
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
+msgstr "최근 ê´€ë¦¬ìž íŒ¨ë„ì—ì„œ ì´ë©”ì¼ ì•Œë¦¼ì´ ì „ì†¡ë˜ì—ˆìŠµë‹ˆë‹¤. 다른 메시지를 보내기 ì „ì— %{wait_time_in_words} 기다리십시오."
+
+msgid "An email will be sent with the report attached after it is generated."
msgstr ""
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
@@ -3876,10 +3915,10 @@ msgid "An error occurred fetching the dropdown data."
msgstr "드롭다운 ë°ì´í„°ë¥¼ 가져오는 ë„중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred fetching the project authors."
-msgstr ""
+msgstr "프로ì íŠ¸ 작성ìžë¥¼ 가져오는 ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred fetching the public deploy keys. Please try again."
-msgstr ""
+msgstr "공개 ë°°í¬ í‚¤ë¥¼ 가져오는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„í•´ 주세요."
msgid "An error occurred previewing the blob"
msgstr "BLOB 미리보기 ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
@@ -3888,25 +3927,28 @@ msgid "An error occurred when loading the user verification challenge. Refresh t
msgstr ""
msgid "An error occurred when updating the title"
-msgstr ""
+msgstr "ì œëª©ì„ ì—…ë°ì´íŠ¸í•˜ëŠ” ë„중 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while acknowledging the notification. Refresh the page and try again."
msgstr ""
msgid "An error occurred while adding approvers"
-msgstr ""
+msgstr "승ì¸ìžë¥¼ 추가하는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
msgid "An error occurred while checking group path. Please refresh and try again."
-msgstr ""
+msgstr "그룹 경로를 확ì¸í•˜ëŠ” ë„중 ì—러가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 새로고침 후 다시 ì‹œë„하십시오."
msgid "An error occurred while decoding the file."
-msgstr ""
+msgstr "파ì¼ì„ 불러오는 ë„중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while deleting the approvers group"
msgstr ""
@@ -3915,7 +3957,7 @@ msgid "An error occurred while deleting the comment"
msgstr "ëŒ“ê¸€ì„ ì‚­ì œí•˜ëŠ” 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while deleting the pipeline."
-msgstr ""
+msgstr "파ì´í”„ë¼ì¸ì„ 삭제하는 ë„중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while detecting host keys"
msgstr "호스트 í‚¤ë“¤ì„ ì‚­ì œí•˜ë˜ ì¤‘ 오류가 ë°œìƒ í–ˆìŠµë‹ˆë‹¤."
@@ -3936,22 +3978,22 @@ msgid "An error occurred while enabling Service Desk."
msgstr "서비스 ë°ìŠ¤í¬ë¥¼ 활성화하는 ë„중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤"
msgid "An error occurred while fetching Markdown preview"
-msgstr ""
+msgstr "마í¬ë‹¤ìš´ 미리보기를 볼러오는 중 ì—러가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
msgid "An error occurred while fetching ancestors"
-msgstr ""
+msgstr "ìƒìœ„ í•­ëª©ì„ ê°€ì ¸ì˜¤ëŠ” ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while fetching branches. Retry the search."
-msgstr ""
+msgstr "브랜치를 가져오는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. ê²€ìƒ‰ì„ ë‹¤ì‹œ ì‹œë„하십시오."
msgid "An error occurred while fetching codequality mr diff reports."
msgstr ""
msgid "An error occurred while fetching commit data."
-msgstr ""
+msgstr "커밋 ë°ì´í„°ë¥¼ 가져오는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while fetching commits. Retry the search."
-msgstr ""
+msgstr "ì»¤ë°‹ì„ ê°€ì ¸ì˜¤ëŠ” ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. ê²€ìƒ‰ì„ ë‹¤ì‹œ ì‹œë„하십시오."
msgid "An error occurred while fetching coverage reports."
msgstr ""
@@ -3981,7 +4023,7 @@ msgid "An error occurred while fetching pending comments"
msgstr "ëŒ“ê¸€ì„ ì—…ë°ì´íŠ¸í•˜ëŠ” 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while fetching projects autocomplete."
-msgstr ""
+msgstr "프로ì íŠ¸ ìžë™ ì™„ì„±ì„ ê°€ì ¸ì˜¤ëŠ” ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while fetching reference"
msgstr ""
@@ -3993,13 +4035,13 @@ msgid "An error occurred while fetching tags. Retry the search."
msgstr "태그를 가져오는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 검색해 보세요."
msgid "An error occurred while fetching terraform reports."
-msgstr ""
+msgstr "Terraform 보고서를 가져오는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while fetching the job log."
msgstr "ìž‘ì—… 로그를 가져 오는 ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while fetching the job logs."
-msgstr ""
+msgstr "ìž‘ì—… 로그를 가져오는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while fetching the job."
msgstr "ìž‘ì—…ì„ ê°€ì ¸ 오는 ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
@@ -4011,13 +4053,13 @@ msgid "An error occurred while fetching the latest pipeline."
msgstr "가장 ìµœê·¼ì˜ íŒŒì´í”„ ë¼ì¸ì„ 가져오는 ë„중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while fetching the pipelines jobs."
-msgstr ""
+msgstr "파ì´í”„ë¼ì¸ ìž‘ì—…ì„ ê°€ì ¸ì˜¤ëŠ” ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while fetching the releases. Please try again."
-msgstr ""
+msgstr "릴리스를 가져오는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„í•´ 주세요."
msgid "An error occurred while fetching this tab."
-msgstr ""
+msgstr "ì´ íƒ­ì„ ê°€ì ¸ì˜¤ëŠ” ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while getting files for - %{branchId}"
msgstr ""
@@ -4032,22 +4074,25 @@ msgid "An error occurred while initializing path locks"
msgstr "경로 ìž ê¸ˆì„ ì´ˆê¸°í™”í•˜ë˜ ì¤‘ 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
msgid "An error occurred while loading a section of this page."
-msgstr ""
+msgstr "ì´ íŽ˜ì´ì§€ì˜ ì„¹ì…˜ì„ ë¡œë“œí•˜ëŠ” ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while loading all the files."
msgstr "모든 파ì¼ì„ 불러오는 ë„ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr "차트 ë°ì´í„°ë¥¼ ë¡œë“œí•˜ë˜ ë„중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while loading code owners."
-msgstr ""
+msgstr "코드 소유ìžë¥¼ 로드하는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while loading commit signatures"
msgstr "커밋 시그너ì³ë¥¼ 불러오는 ë„ì¤‘ì— ì—러가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while loading designs. Please try again."
-msgstr ""
+msgstr "ë””ìžì¸ì„ 로드하는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„í•´ 주세요."
msgid "An error occurred while loading diff"
msgstr "ì°¨ì´ì ì„ 로드하는 ë™ì•ˆ 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
@@ -4056,19 +4101,19 @@ msgid "An error occurred while loading filenames"
msgstr "íŒŒì¼ ì´ë¦„ì„ ë¡œë“œí•˜ëŠ” 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while loading group members."
-msgstr ""
+msgstr "그룹 구성ì›ì„ 로드하는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while loading issues"
-msgstr ""
+msgstr "ì´ìŠˆë¥¼ 불러오는 ë„중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while loading merge requests."
-msgstr ""
+msgstr "머지 리퀘스트를 불러오는 ë„중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while loading projects."
-msgstr ""
+msgstr "프로ì íŠ¸ë¥¼ 불러오는 ë„중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while loading the Jobs tab."
-msgstr ""
+msgstr "ìž‘ì—… íƒ­ì„ ë¡œë“œí•˜ëŠ” ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while loading the Needs tab."
msgstr ""
@@ -4077,7 +4122,7 @@ msgid "An error occurred while loading the Test Reports tab."
msgstr ""
msgid "An error occurred while loading the blob controls."
-msgstr ""
+msgstr "Blob ì»¨íŠ¸ë¡¤ì„ ë¡œë“œí•˜ëŠ” ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while loading the data. Please try again."
msgstr ""
@@ -4128,10 +4173,10 @@ msgid "An error occurred while parsing the file."
msgstr ""
msgid "An error occurred while pasting text in the editor. Please try again."
-msgstr ""
+msgstr "편집기ì—ì„œ í…스트를 붙여넣는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„í•´ 주세요."
msgid "An error occurred while performing this action."
-msgstr ""
+msgstr "ì´ ìž‘ì—…ì„ ìˆ˜í–‰í•˜ëŠ” ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while removing epics."
msgstr "ì—í”½ì„ ì‚­ì œí•˜ëŠ” ë„중 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
@@ -4173,9 +4218,6 @@ msgstr[0] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr "ì„¤ì •ì„ ì €ìž¥í•˜ëŠ” ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 저장해 보세요."
-msgid "An error occurred while subscribing to notifications."
-msgstr "ì•Œë¦¼ì„ êµ¬ë…하는 ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
-
msgid "An error occurred while triggering the job."
msgstr "ìž‘ì—…ì„ íŠ¸ë¦¬ê±°í•˜ëŠ” 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
@@ -4185,15 +4227,15 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
+msgid "An error occurred while trying to render the content editor. Please try again."
+msgstr ""
+
msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
-msgstr "알림 구ë…ì„ í•´ì œí•˜ëŠ” 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
-
msgid "An error occurred while updating approvers"
msgstr "승ì¸ìžë¥¼ ì—…ë°ì´íŠ¸í•˜ëŠ” ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤"
@@ -4204,7 +4246,7 @@ msgid "An error occurred while updating configuration."
msgstr ""
msgid "An error occurred while updating labels."
-msgstr ""
+msgstr "ë¼ë²¨ì„ ì—…ë°ì´íŠ¸í•˜ëŠ” ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while updating the comment"
msgstr "ëŒ“ê¸€ì„ ì—…ë°ì´íŠ¸í•˜ëŠ” 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
@@ -4219,7 +4261,7 @@ msgid "An error occurred while uploading the file. Please try again."
msgstr "파ì¼ì„ 업로드하는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„í•´ 주세요."
msgid "An error occurred while validating group path"
-msgstr ""
+msgstr "그룹 경로 í™•ì¸ ì¤‘ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while validating username"
msgstr "ì‚¬ìš©ìž ì´ë¦„ì„ í™•ì¸í•˜ëŠ” 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤"
@@ -4231,7 +4273,7 @@ msgid "An error occurred. Please try again."
msgstr "오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„í•´ 주세요."
msgid "An example project for managing Kubernetes clusters integrated with GitLab"
-msgstr ""
+msgstr "GitLabê³¼ ì—°ë™í•˜ì—¬ 쿠버네티스(Kubernetes) í´ëŸ¬ìŠ¤í„°ë¥¼ 관리하는 프로ì íŠ¸ 예시"
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 ""
@@ -4249,10 +4291,10 @@ msgid "An integer value is required for seconds"
msgstr ""
msgid "An issue already exists"
-msgstr ""
+msgstr "ì´ë¯¸ 존재하는 ì´ìŠˆìž…니다."
msgid "An unauthenticated user"
-msgstr ""
+msgstr "ì¸ì¦ë˜ì§€ ì•Šì€ ì‚¬ìš©ìž"
msgid "An unexpected error occurred"
msgstr ""
@@ -4303,13 +4345,13 @@ msgid "Ancestors"
msgstr ""
msgid "And this registration token:"
-msgstr ""
+msgstr "그리고 ì´ ë“±ë¡ í† í°:"
msgid "Anonymous"
msgstr "ìµëª…"
msgid "Another action is currently in progress"
-msgstr ""
+msgstr "현재 다른 ìž‘ì—…ì´ ì§„í–‰ 중입니다"
msgid "Another issue tracker is already in use. Only one issue tracker service can be active at a time"
msgstr ""
@@ -4327,7 +4369,7 @@ msgid "Any %{header}"
msgstr ""
msgid "Any Author"
-msgstr ""
+msgstr "모든 작성ìž"
msgid "Any Milestone"
msgstr ""
@@ -4402,7 +4444,7 @@ msgid "ApplicationSettings|A Metrics Dashboard menu item appears in the Monitori
msgstr ""
msgid "ApplicationSettings|Add a link to Grafana"
-msgstr ""
+msgstr "Grafanaì— ëŒ€í•œ ë§í¬ 추가"
msgid "ApplicationSettings|After sign-up text"
msgstr ""
@@ -4424,7 +4466,7 @@ msgid_plural "ApplicationSettings|Approve %d users"
msgstr[0] ""
msgid "ApplicationSettings|Approve users"
-msgstr ""
+msgstr "ì‚¬ìš©ìž ìŠ¹ì¸"
msgid "ApplicationSettings|Approve users in the pending approval status?"
msgstr ""
@@ -4491,7 +4533,7 @@ msgid "ApplicationSettings|Restricts sign-ups for email addresses that match the
msgstr ""
msgid "ApplicationSettings|Save changes"
-msgstr ""
+msgstr "변경 사항 저장"
msgid "ApplicationSettings|See %{linkStart}password policy guidelines%{linkEnd}."
msgstr ""
@@ -4607,7 +4649,7 @@ msgstr[0] ""
msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
-msgstr[0] ""
+msgstr[0] "%{membersCount} 부터 %{count}ê°œì˜ ìŠ¹ì¸ í•„ìš”"
msgid "ApprovalRule|A merge request author collaborating with a merge request approver"
msgstr ""
@@ -4646,28 +4688,28 @@ msgid "ApprovalRule|Learn more about merge request approval rules."
msgstr ""
msgid "ApprovalRule|Name"
-msgstr ""
+msgstr "ì´ë¦„"
msgid "ApprovalRule|Newly detected"
-msgstr ""
+msgstr "새로 ê°ì§€ë¨"
msgid "ApprovalRule|Previously detected"
-msgstr ""
+msgstr "ì´ì „ì— ê°ì§€ë¨"
msgid "ApprovalRule|Reduce your time to merge."
msgstr ""
msgid "ApprovalRule|Resolved"
-msgstr ""
+msgstr "í•´ê²°ë¨"
msgid "ApprovalRule|Rule name"
-msgstr ""
+msgstr "규칙 ì´ë¦„"
msgid "ApprovalRule|Select eligible approvers by expertise or files changed."
msgstr ""
msgid "ApprovalRule|Target branch"
-msgstr ""
+msgstr "ëŒ€ìƒ ë¸Œëžœì¹˜"
msgid "ApprovalRule|Try for free"
msgstr ""
@@ -4694,7 +4736,7 @@ msgid "ApprovalSettings|Prevent editing approval rules in projects and merge req
msgstr ""
msgid "ApprovalSettings|Remove all approvals"
-msgstr ""
+msgstr "모든 ìŠ¹ì¸ ì œê±°"
msgid "ApprovalSettings|Remove approvals by Code Owners if their files changed"
msgstr ""
@@ -4727,6 +4769,9 @@ msgid "Approve"
msgstr "승ì¸"
msgid "Approve a merge request"
+msgstr "머지 리퀘스트 승ì¸"
+
+msgid "Approve merge request"
msgstr ""
msgid "Approve the current merge request."
@@ -4736,22 +4781,22 @@ msgid "Approved"
msgstr "승ì¸ë¨"
msgid "Approved MRs"
-msgstr ""
+msgstr "승ì¸ëœ MR"
msgid "Approved the current merge request."
msgstr "ì´ ë¨¸ì§€ 리퀘스트를 승ì¸í–ˆìŠµë‹ˆë‹¤."
msgid "Approved-By"
-msgstr ""
+msgstr "승ì¸ìž"
msgid "Approver"
msgstr "승ì¸ìž"
msgid "Approvers"
-msgstr ""
+msgstr "승ì¸ìž"
msgid "Approvers from private group(s) not shown"
-msgstr ""
+msgstr "비공개 ê·¸ë£¹ì˜ ìŠ¹ì¸ìžê°€ 표시ë˜ì§€ ì•ŠìŒ"
msgid "Apr"
msgstr "4ì›”"
@@ -4760,7 +4805,7 @@ msgid "April"
msgstr "4ì›”"
msgid "Architecture not found for OS"
-msgstr ""
+msgstr "OSì— ëŒ€í•œ 아키í…처를 ì°¾ì„ ìˆ˜ 없습니다."
msgid "Archive"
msgstr ""
@@ -4775,13 +4820,13 @@ msgid "Archive test case"
msgstr ""
msgid "Archived"
-msgstr ""
+msgstr "ë³´ê´€ë¨"
msgid "Archived (%{movedToStart}moved%{movedToEnd})"
-msgstr ""
+msgstr "ë³´ê´€ë¨ (%{movedToStart}ê°€ %{movedToEnd}ë¡œ ì´ë™ë¨)"
msgid "Archived in this version"
-msgstr ""
+msgstr "ì´ ë²„ì „ì—ì„œ ë³´ê´€ë¨"
msgid "Archived project! Repository and other project resources are read-only"
msgstr "ë³´ê´€ëœ í”„ë¡œì íŠ¸! 저장소 ë° ê¸°íƒ€ 프로ì íŠ¸ 리소스는 ì½ê¸° 전용입니다."
@@ -4793,7 +4838,7 @@ msgid "Archiving the project makes it entirely read-only. It is hidden from the
msgstr ""
msgid "Are you ABSOLUTELY SURE you wish to remove this group?"
-msgstr ""
+msgstr "ì´ ê·¸ë£¹ì„ ì œê±°í•˜ëŠ” ê²ƒì´ í™•ì‹¤í•©ë‹ˆê¹Œ?"
msgid "Are you absolutely sure?"
msgstr ""
@@ -4826,22 +4871,22 @@ msgid "Are you sure you want to delete %{name}?"
msgstr ""
msgid "Are you sure you want to delete these artifacts?"
-msgstr ""
+msgstr "ì •ë§ë¡œ ì´ ì•„í‹°íŒ©íŠ¸ë¥¼ 삭제하시겠습니까?"
msgid "Are you sure you want to delete this %{commentType}?"
-msgstr ""
+msgstr "ì´ %{commentType}ì„ ì‚­ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
msgid "Are you sure you want to delete this SSH key?"
msgstr ""
msgid "Are you sure you want to delete this comment?"
-msgstr ""
+msgstr "ì´ ëŒ“ê¸€ì„ ì‚­ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
msgid "Are you sure you want to delete this deploy key?"
msgstr ""
msgid "Are you sure you want to delete this device? This action cannot be undone."
-msgstr ""
+msgstr "ì´ ê¸°ê¸°ë¥¼ 삭제하시겠습니까? ì´ ìž‘ì—…ì€ ì·¨ì†Œí•  수 없습니다."
msgid "Are you sure you want to delete this label?"
msgstr ""
@@ -4850,7 +4895,7 @@ msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "ì´ íŒŒì´í”„ë¼ì¸ ìŠ¤ì¼€ì¥´ì„ ì‚­ì œ 하시겠습니까?"
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 ""
+msgstr "ì´ íŒŒì´í”„ë¼ì¸ì„ 삭제하시겠습니까? 그렇게 하면 모든 파ì´í”„ë¼ì¸ ìºì‹œê°€ 만료ë˜ê³  빌드, 로그, 아티팩트 ë° íŠ¸ë¦¬ê±°ì™€ ê°™ì€ ëª¨ë“  관련 개체가 ì‚­ì œë©ë‹ˆë‹¤. ì´ ìž‘ì—…ì€ ì·¨ì†Œí•  수 없습니다."
msgid "Are you sure you want to deploy this environment?"
msgstr ""
@@ -4908,7 +4953,7 @@ msgid "Are you sure you want to remove this identity?"
msgstr "ì •ë§ë¡œ 해당 í•­ëª©ì„ ì‚­ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
msgid "Are you sure you want to remove this list?"
-msgstr ""
+msgstr "ì´ ëª©ë¡ì„ 삭제하시겠습니까?"
msgid "Are you sure you want to remove this nickname?"
msgstr ""
@@ -4923,13 +4968,13 @@ msgid "Are you sure you want to reset the registration token?"
msgstr ""
msgid "Are you sure you want to retry this migration?"
-msgstr ""
+msgstr "ì´ ë§ˆì´ê·¸ë ˆì´ì…˜ì„ 다시 ì‹œë„하시겠습니까?"
msgid "Are you sure you want to revoke this %{accessTokenType}? This action cannot be undone."
msgstr ""
msgid "Are you sure you want to revoke this %{type}? This action cannot be undone."
-msgstr ""
+msgstr "ì´ %{type}ì„ ì·¨ì†Œí•˜ì‹œê² ìŠµë‹ˆê¹Œ? ì´ ìž‘ì—…ì€ ë˜ëŒë¦´ 수 없습니다."
msgid "Are you sure you want to revoke this personal access token? This action cannot be undone."
msgstr ""
@@ -4968,10 +5013,10 @@ msgid "Arrange charts"
msgstr "차트 정렬"
msgid "Artifact"
-msgstr ""
+msgstr "아티팩트"
msgid "Artifact could not be deleted."
-msgstr ""
+msgstr "아티팩트를 삭제할 수 없습니다."
msgid "Artifact was successfully deleted."
msgstr ""
@@ -5019,7 +5064,7 @@ msgid "Assign"
msgstr "할당"
msgid "Assign Iteration"
-msgstr ""
+msgstr "ì´í„°ë ˆì´ì…˜ 지정"
msgid "Assign To"
msgstr ""
@@ -5034,7 +5079,7 @@ msgid "Assign milestone"
msgstr "마ì¼ìŠ¤í†¤ 지정"
msgid "Assign myself"
-msgstr ""
+msgstr "나ì—게 할당하기"
msgid "Assign reviewer"
msgstr "리뷰어 지정하기"
@@ -5043,7 +5088,7 @@ msgid "Assign reviewer(s)"
msgstr "리뷰어 지정하기"
msgid "Assign severity"
-msgstr ""
+msgstr "심ê°ë„ 지정"
msgid "Assign some issues to this milestone."
msgstr "ì´ ë§ˆì¼ë“œìŠ¤í†¤ì— ì´ìŠˆë¥¼ 할당합니다."
@@ -5052,31 +5097,31 @@ msgid "Assign to"
msgstr "담당ìž"
msgid "Assign to commenting user"
-msgstr ""
+msgstr "댓글 사용ìžì—게 할당"
msgid "Assign to me"
-msgstr ""
+msgstr "나ì—게 할당"
msgid "Assigned %{assignee_users_sentence}."
-msgstr ""
+msgstr "%{assignee_users_sentence} 할당ë¨."
msgid "Assigned %{reviewer_users_sentence} as %{reviewer_text}."
-msgstr ""
+msgstr "%{reviewer_users_sentence} ì„ %{reviewer_text}ë¡œ 지정했습니다."
msgid "Assigned Issues"
msgstr "í• ë‹¹ëœ ì´ìŠˆ"
msgid "Assigned merge requests"
-msgstr ""
+msgstr "í• ë‹¹ëœ ë¨¸ì§€ 리퀘스트"
msgid "Assigned projects"
-msgstr ""
+msgstr "í• ë‹¹ëœ í”„ë¡œì íŠ¸"
msgid "Assigned to %{assigneeName}"
-msgstr ""
+msgstr "%{assigneeName}ì—게 할당ë¨"
msgid "Assigned to %{assignee_name}"
-msgstr ""
+msgstr "%{assignee_name}ì—게 할당ë¨"
msgid "Assigned to me"
msgstr "나ì—게 할당 ë¨"
@@ -5089,7 +5134,7 @@ msgid_plural "%d Assignees"
msgstr[0] ""
msgid "Assignee has no permissions"
-msgstr ""
+msgstr "담당ìžì—게 ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤."
msgid "Assignee lists not available with your current license"
msgstr "ë‹´ë‹¹ìž ëª©ë¡ì€ 현재 ë¼ì´ì„¼ìŠ¤ë¡œëŠ” 사용할 수 없습니다."
@@ -5098,7 +5143,7 @@ msgid "Assignee(s)"
msgstr "담당ìž"
msgid "Assignees"
-msgstr ""
+msgstr "담당ìž"
msgid "Assigns %{assignee_users_sentence}."
msgstr ""
@@ -5132,7 +5177,7 @@ msgstr ""
msgid "Attaching a file"
msgid_plural "Attaching %d files"
-msgstr[0] ""
+msgstr[0] "íŒŒì¼ %d ê°œ 첨부"
msgid "Attaching the file failed."
msgstr "íŒŒì¼ ì²¨ë¶€ê°€ 실패했습니다."
@@ -5150,7 +5195,7 @@ msgid "AuditLogs|Action"
msgstr ""
msgid "AuditLogs|Author"
-msgstr ""
+msgstr "작성ìž"
msgid "AuditLogs|Date"
msgstr ""
@@ -5162,13 +5207,13 @@ msgid "AuditLogs|Failed to find %{type}. Please try again."
msgstr ""
msgid "AuditLogs|Group Events"
-msgstr ""
+msgstr "그룹 ì´ë²¤íŠ¸"
msgid "AuditLogs|IP Address"
-msgstr ""
+msgstr "IP 주소"
msgid "AuditLogs|Log"
-msgstr ""
+msgstr "로그"
msgid "AuditLogs|Member Events"
msgstr ""
@@ -5180,16 +5225,20 @@ msgid "AuditLogs|Object"
msgstr ""
msgid "AuditLogs|Project Events"
-msgstr ""
+msgstr "프로ì íŠ¸ ì´ë²¤íŠ¸"
msgid "AuditLogs|Target"
msgstr ""
msgid "AuditLogs|This month"
-msgstr ""
+msgstr "ì´ë²ˆ 달"
msgid "AuditLogs|User Events"
-msgstr ""
+msgstr "ì‚¬ìš©ìž ì´ë²¤íŠ¸"
+
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] "%d ê°œì˜ ëŒ€ìƒ"
msgid "AuditStreams|A header with this name already exists."
msgstr "ì´ ì´ë¦„ì„ ê°€ì§„ í—¤ë”ê°€ ì´ë¯¸ 존재합니다."
@@ -5201,16 +5250,22 @@ msgid "AuditStreams|Add a custom header"
msgstr "ì‚¬ìš©ìž ì •ì˜ í—¤ë” ì¶”ê°€"
msgid "AuditStreams|Add a custom value"
-msgstr ""
+msgstr "ì‚¬ìš©ìž ì •ì˜ ê°’ 추가"
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr "다른 ì‚¬ìš©ìž ì •ì˜ í—¤ë” ì¶”ê°€"
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
-msgstr ""
+msgid "AuditStreams|Add header"
+msgstr "í—¤ë” ì¶”ê°€"
+
+msgid "AuditStreams|Add streaming destination"
+msgstr "ìŠ¤íŠ¸ë¦¬ë° ëŒ€ìƒ ì¶”ê°€"
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
msgstr ""
@@ -5225,29 +5280,32 @@ msgid "AuditStreams|An error occurred when updating external audit event stream
msgstr ""
msgid "AuditStreams|Cancel editing"
-msgstr ""
+msgstr "편집 취소"
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
msgstr ""
msgid "AuditStreams|Destination URL"
-msgstr ""
+msgstr "ëŒ€ìƒ URL"
msgid "AuditStreams|Destinations receive all audit event data"
msgstr ""
msgid "AuditStreams|Edit %{link}"
-msgstr ""
+msgstr "%{link} 편집"
msgid "AuditStreams|Header"
-msgstr ""
+msgstr "í—¤ë”"
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5257,9 +5315,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -5276,10 +5331,10 @@ msgid "AuditStreams|This is great for keeping everything one place."
msgstr ""
msgid "AuditStreams|Value"
-msgstr ""
+msgstr "ê°’"
msgid "AuditStreams|Verification token"
-msgstr ""
+msgstr "ì¸ì¦ 토í°"
msgid "Aug"
msgstr "8ì›”"
@@ -5327,7 +5382,7 @@ msgid "Authentication Log"
msgstr "ì¸ì¦ 로그"
msgid "Authentication error: enable 2FA in your profile settings to continue using GitLab: %{mfa_help_page}"
-msgstr ""
+msgstr "ì¸ì¦ 오류: 프로필 설정ì—ì„œ 2FA를 활성화하여 GitLabì„ ê³„ì† ì‚¬ìš©í•˜ì„¸ìš”: %{mfa_help_page}"
msgid "Authentication failed: %{error_message}"
msgstr "ì¸ì¦ 실패: %{error_message}"
@@ -5363,10 +5418,10 @@ msgid "Authorization code:"
msgstr "ì¸ì¦ 코드:"
msgid "Authorization required"
-msgstr ""
+msgstr "ì¸ì¦ í•„ìš”"
msgid "Authorization token duration (minutes)"
-msgstr ""
+msgstr "ì¸ì¦ í† í° ìœ íš¨ 기간 (분)"
msgid "Authorization was granted by entering your username and password in the application."
msgstr "ì‘ìš© í”„ë¡œê·¸ëž¨ì— ì‚¬ìš©ìž ì´ë¦„ê³¼ 암호를 입력하여 ê¶Œí•œì´ ë¶€ì—¬ë˜ì—ˆìŠµë‹ˆë‹¤."
@@ -5411,7 +5466,7 @@ msgid "Auto-cancel redundant pipelines"
msgstr ""
msgid "Auto-close referenced issues on default branch"
-msgstr ""
+msgstr "기본 브랜치ì—ì„œ ì°¸ì¡°ëœ ë¬¸ì œ ìžë™ 닫기"
msgid "AutoDevOps|%{auto_devops_start}Automate building, testing, and deploying%{auto_devops_end} your applications based on your continuous integration and delivery configuration. %{quickstart_start}How do I get started?%{quickstart_end}"
msgstr ""
@@ -5516,7 +5571,7 @@ msgid "Available on-demand"
msgstr ""
msgid "Available shared runners:"
-msgstr ""
+msgstr "사용 가능한 공유 러너:"
msgid "Available specific runners"
msgstr "사용 가능한 지정 러너"
@@ -5531,7 +5586,7 @@ msgid "Average per day: %{average}"
msgstr "하루 í‰ê· : %{average}"
msgid "Awaiting user signup"
-msgstr ""
+msgstr "ì‚¬ìš©ìž ê°€ìž… 대기 중"
msgid "Award added"
msgstr ""
@@ -5540,7 +5595,7 @@ msgid "Award removed"
msgstr ""
msgid "AwardEmoji|No emojis found."
-msgstr ""
+msgstr "ì´ëª¨ì§€ë¥¼ ì°¾ì„ ìˆ˜ 없습니다."
msgid "Back"
msgstr ""
@@ -5555,31 +5610,31 @@ msgid "Background Jobs"
msgstr "백그ë¼ìš´ë“œ ìž‘ì—…"
msgid "Background Migrations"
-msgstr ""
+msgstr "백그ë¼ìš´ë“œ 마ì´ê·¸ë ˆì´ì…˜"
msgid "Background color"
msgstr "배경 색"
msgid "BackgroundMigrations|Background Migrations"
-msgstr ""
+msgstr "백그ë¼ìš´ë“œ 마ì´ê·¸ë ˆì´ì…˜"
msgid "BackgroundMigrations|Background migrations are used to perform data migrations whenever a migration exceeds the time limits in our guidelines. %{linkStart}Learn more%{linkEnd}"
-msgstr ""
+msgstr "백그ë¼ìš´ë“œ 마ì´ê·¸ë ˆì´ì…˜ì€ 마ì´ê·¸ë ˆì´ì…˜ì´ ê°€ì´ë“œë¼ì¸ì˜ 시간 ì œí•œì„ ì´ˆê³¼í•  때마다 ë°ì´í„° 마ì´ê·¸ë ˆì´ì…˜ì„ 수행하는 ë° ì‚¬ìš©ë©ë‹ˆë‹¤. %{linkStart}ìžì„¸ížˆ 알아보기%{linkEnd}"
msgid "BackgroundMigrations|Batch size"
-msgstr ""
+msgstr "배치 í¬ê¸°"
msgid "BackgroundMigrations|Database"
-msgstr ""
+msgstr "ë°ì´í„°ë² ì´ìŠ¤"
msgid "BackgroundMigrations|Failed jobs:"
-msgstr ""
+msgstr "실패한 작업:"
msgid "BackgroundMigrations|Finished at"
-msgstr ""
+msgstr "완료 시간"
msgid "BackgroundMigrations|Started at"
-msgstr ""
+msgstr "시작 시간"
msgid "Badges"
msgstr "배지"
@@ -5597,7 +5652,7 @@ msgid "Badges|Badge image preview"
msgstr "배지 ì´ë¯¸ì§€ 미리 보기"
msgid "Badges|Badge saved."
-msgstr ""
+msgstr "배지가 저장ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "Badges|Delete badge?"
msgstr "배지를 삭제할까요?"
@@ -5606,10 +5661,10 @@ msgid "Badges|Deleting the badge failed, please try again."
msgstr "배지 ì‚­ì œ 실패, 다시 ì‹œë„하십시오."
msgid "Badges|Enter a valid URL"
-msgstr ""
+msgstr "유효한 URLì„ ìž…ë ¥í•˜ì„¸ìš”"
msgid "Badges|Example: %{exampleUrl}"
-msgstr ""
+msgstr "예: %{exampleUrl}"
msgid "Badges|Group Badge"
msgstr "그룹 배지"
@@ -5621,7 +5676,7 @@ msgid "Badges|Name"
msgstr "ì´ë¦„"
msgid "Badges|New badge added."
-msgstr ""
+msgstr "새 배지가 추가ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "Badges|No badge image"
msgstr "ìž˜ëª»ëœ ë°°ì§€ ì´ë¯¸ì§€"
@@ -5660,10 +5715,10 @@ msgid "Badges|Your badges"
msgstr "내 배지"
msgid "BambooService|Atlassian Bamboo"
-msgstr ""
+msgstr "Atlassian Bamboo"
msgid "BambooService|Bamboo URL"
-msgstr ""
+msgstr "Bamboo URL"
msgid "BambooService|Bamboo build plan key."
msgstr ""
@@ -5672,7 +5727,7 @@ msgid "BambooService|Bamboo service root URL."
msgstr ""
msgid "BambooService|Enter new build key"
-msgstr ""
+msgstr "새 빌드 키 입력"
msgid "BambooService|Leave blank to use your current build key."
msgstr ""
@@ -5687,19 +5742,19 @@ msgid "BambooService|The user with API access to the Bamboo server."
msgstr ""
msgid "Banned"
-msgstr ""
+msgstr "차단ë¨"
msgid "Banner message"
-msgstr ""
+msgstr "배너 메시지"
msgid "Based on"
msgstr ""
msgid "Batch size"
-msgstr ""
+msgstr "배치 í¬ê¸°"
msgid "Batched Job|Background Migrations"
-msgstr ""
+msgstr "백그ë¼ìš´ë“œ 마ì´ê·¸ë ˆì´ì…˜"
msgid "Batched Job|Batched Job (Id: %{id})"
msgstr ""
@@ -5720,13 +5775,13 @@ msgid "BatchedJob|Created at"
msgstr ""
msgid "BatchedJob|Exception Class"
-msgstr ""
+msgstr "예외 í´ëž˜ìŠ¤"
msgid "BatchedJob|Exception Message"
-msgstr ""
+msgstr "예외 메시지"
msgid "BatchedJob|Exception class"
-msgstr ""
+msgstr "예외 í´ëž˜ìŠ¤"
msgid "BatchedJob|Exception message"
msgstr ""
@@ -5801,7 +5856,7 @@ msgid "Bi-weekly code coverage"
msgstr ""
msgid "Billable Users"
-msgstr ""
+msgstr "청구 가능한 사용ìž"
msgid "Billing"
msgstr "결제"
@@ -5870,7 +5925,7 @@ msgid "BillingPlans|Free forever features for individual users"
msgstr ""
msgid "BillingPlans|Free guest users"
-msgstr ""
+msgstr "무료 게스트 사용ìž"
msgid "BillingPlans|Free upgrade!"
msgstr ""
@@ -5924,7 +5979,7 @@ msgid "BillingPlans|Recommended"
msgstr ""
msgid "BillingPlans|Release controls"
-msgstr ""
+msgstr "릴리스 관리"
msgid "BillingPlans|Security risk mitigation"
msgstr ""
@@ -5999,7 +6054,7 @@ msgid "BillingPlans|frequently asked questions"
msgstr "ìžì£¼ 묻는 질문"
msgid "BillingPlans|group"
-msgstr ""
+msgstr "그룹"
msgid "BillingPlans|monthly"
msgstr "ì›”"
@@ -6014,7 +6069,7 @@ msgid "BillingPlan|Upgrade"
msgstr "업그레ì´ë“œ"
msgid "BillingPlan|Upgrade for free"
-msgstr ""
+msgstr "무료 업그레ì´ë“œ"
msgid "Billings|%{planName} plan"
msgstr ""
@@ -6058,12 +6113,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6079,12 +6128,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6136,9 +6179,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6179,9 +6219,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6201,7 +6238,7 @@ msgid "BlobViewer|View on %{environmentName}"
msgstr ""
msgid "Block user"
-msgstr ""
+msgstr "ì‚¬ìš©ìž ì°¨ë‹¨"
msgid "Blocked"
msgstr "차단ë¨"
@@ -6216,6 +6253,9 @@ msgstr "ì°¨ë‹¨ëœ ì´ìŠˆ"
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6232,31 +6272,31 @@ msgid "Board scope affects which issues are displayed for anyone who visits this
msgstr ""
msgid "BoardNewEpic|Groups"
-msgstr ""
+msgstr "그룹"
msgid "BoardNewEpic|Loading groups"
msgstr ""
msgid "BoardNewEpic|No matching results"
-msgstr ""
+msgstr "ì¼ì¹˜í•˜ëŠ” 결과가 없습니다"
msgid "BoardNewEpic|Search groups"
-msgstr ""
+msgstr "그룹 검색"
msgid "BoardNewEpic|Select a group"
-msgstr ""
+msgstr "그룹 ì„ íƒ"
msgid "BoardNewIssue|No matching results"
-msgstr ""
+msgstr "ì¼ì¹˜í•˜ëŠ” 결과가 없습니다"
msgid "BoardNewIssue|Projects"
-msgstr ""
+msgstr "프로ì íŠ¸"
msgid "BoardNewIssue|Search projects"
msgstr ""
msgid "BoardNewIssue|Select a project"
-msgstr ""
+msgstr "프로ì íŠ¸ ì„ íƒ"
msgid "BoardScope|%{iterationTitle} iteration in %{iterationCadence}"
msgstr ""
@@ -6265,7 +6305,7 @@ msgid "BoardScope|An error occurred while getting iterations. Please try again."
msgstr ""
msgid "BoardScope|An error occurred while getting milestones, please try again."
-msgstr ""
+msgstr "마ì¼ìŠ¤í†¤ì„ 가져오는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„하십시오."
msgid "BoardScope|An error occurred while searching for labels, please try again."
msgstr ""
@@ -6277,7 +6317,7 @@ msgid "BoardScope|Any Milestone"
msgstr ""
msgid "BoardScope|Any assignee"
-msgstr ""
+msgstr "모든 담당ìž"
msgid "BoardScope|Any iteration"
msgstr ""
@@ -6286,7 +6326,7 @@ msgid "BoardScope|Any label"
msgstr ""
msgid "BoardScope|Assignee"
-msgstr ""
+msgstr "담당ìž"
msgid "BoardScope|Choose labels"
msgstr ""
@@ -6298,7 +6338,7 @@ msgid "BoardScope|Don't filter milestone"
msgstr ""
msgid "BoardScope|Edit"
-msgstr ""
+msgstr "편집"
msgid "BoardScope|Iteration"
msgstr ""
@@ -6307,34 +6347,34 @@ msgid "BoardScope|Labels"
msgstr ""
msgid "BoardScope|Milestone"
-msgstr ""
+msgstr "마ì¼ìŠ¤í†¤"
msgid "BoardScope|No iteration"
msgstr ""
msgid "BoardScope|No milestone"
-msgstr ""
+msgstr "마ì¼ìŠ¤í†¤ ì—†ìŒ"
msgid "BoardScope|Search iterations"
msgstr ""
msgid "BoardScope|Search milestones"
-msgstr ""
+msgstr "마ì¼ìŠ¤í†¤ 검색"
msgid "BoardScope|Select assignee"
-msgstr ""
+msgstr "ë‹´ë‹¹ìž ì„ íƒ"
msgid "BoardScope|Select iteration"
-msgstr ""
+msgstr "ì´í„°ë ˆì´ì…˜ ì„ íƒ"
msgid "BoardScope|Select labels"
-msgstr ""
+msgstr "ë¼ë²¨ ì„ íƒ"
msgid "BoardScope|Select milestone"
-msgstr ""
+msgstr "마ì¼ìŠ¤í†¤ ì„ íƒ"
msgid "BoardScope|Select weight"
-msgstr ""
+msgstr "가중치 ì„ íƒ"
msgid "BoardScope|Started"
msgstr ""
@@ -6343,13 +6383,13 @@ msgid "BoardScope|Upcoming"
msgstr ""
msgid "BoardScope|Weight"
-msgstr ""
+msgstr "가중치"
msgid "Boards"
msgstr "보드"
msgid "Boards and board lists"
-msgstr ""
+msgstr "ë³´ë“œ ë° ë³´ë“œ 목ë¡"
msgid "Boards|+ %{displayedIssuablesCount} more %{issuableType}"
msgid_plural "Boards|+ %{displayedIssuablesCount} more %{issuableType}s"
@@ -6422,11 +6462,20 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr "ì¹´ë“œ ì´ë™"
+
+msgid "Boards|Move to end of list"
+msgstr "ëª©ë¡ ë으로 ì´ë™"
+
+msgid "Boards|Move to start of list"
+msgstr "목ë¡ì˜ 처ìŒìœ¼ë¡œ ì´ë™"
+
msgid "Boards|New board"
-msgstr ""
+msgstr "새 보드"
msgid "Boards|New epic"
-msgstr ""
+msgstr "새 ì—픽"
msgid "Boards|Retrieving blocking %{issuableType}s"
msgstr ""
@@ -6447,7 +6496,7 @@ msgid "Board|Board scope"
msgstr ""
msgid "Board|Create board"
-msgstr ""
+msgstr "ë³´ë“œ ìƒì„±"
msgid "Board|Create new board"
msgstr "새 ë³´ë“œ ìƒì„±"
@@ -6462,13 +6511,13 @@ msgid "Board|Enter board name"
msgstr "ë³´ë“œ ì´ë¦„ ìž…ë ¥"
msgid "Board|Failed to delete board. Please try again."
-msgstr ""
+msgstr "보드를 삭제하지 못했습니다. 다시 ì‹œë„í•´ 주세요."
msgid "Board|Load more epics"
-msgstr ""
+msgstr "추가 ì—픽 로드"
msgid "Board|Load more issues"
-msgstr ""
+msgstr "추가 ì´ìŠˆ 로드"
msgid "Board|Loading epics"
msgstr ""
@@ -6477,7 +6526,7 @@ msgid "Bold text"
msgstr ""
msgid "Both SSH and HTTP(S)"
-msgstr ""
+msgstr "SSH ë° HTTP(S) 모ë‘"
msgid "Both project and dashboard_path are required"
msgstr ""
@@ -6510,34 +6559,34 @@ msgid "Branch not loaded - %{branchId}"
msgstr ""
msgid "Branch rules"
-msgstr ""
+msgstr "브랜치 규칙"
msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/* are supported."
-msgstr ""
+msgstr "*-stable ë˜ëŠ” production/*ê³¼ ê°™ì€ %{linkStart}와ì¼ë“œì¹´ë“œ%{linkEnd}ê°€ 지ì›ë©ë‹ˆë‹¤."
msgid "BranchRules|Allow all users with push access to %{linkStart}force push%{linkEnd}."
-msgstr ""
+msgstr "푸시 액세스 ê¶Œí•œì´ ìžˆëŠ” 모든 사용ìžì—게 %{linkStart}ê°•ì œ 푸시%{linkEnd}를 허용합니다."
msgid "BranchRules|Allowed to merge"
-msgstr ""
+msgstr "머지 허용"
msgid "BranchRules|Allowed to push"
-msgstr ""
+msgstr "푸시 허용"
msgid "BranchRules|An error occurred while fetching branches."
-msgstr ""
+msgstr "브랜치를 가져오는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "BranchRules|Branch"
-msgstr ""
+msgstr "브랜치"
msgid "BranchRules|Create wildcard: %{searchTerm}"
-msgstr ""
+msgstr "와ì¼ë“œì¹´ë“œ ìƒì„±: %{searchTerm}"
msgid "BranchRules|Keep stable branches secure and force developers to use merge requests. %{linkStart}What are protected branches?%{linkEnd}"
msgstr ""
msgid "BranchRules|No matching results"
-msgstr ""
+msgstr "ì¼ì¹˜í•˜ëŠ” 결과가 없습니다"
msgid "BranchRules|Protections"
msgstr ""
@@ -6546,6 +6595,12 @@ msgid "BranchRules|Reject code pushes that change files listed in the CODEOWNERS
msgstr ""
msgid "BranchRules|Require approval from code owners."
+msgstr "코드 소유ìžì˜ 승ì¸ì´ 필요합니다."
+
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
msgstr ""
msgid "Branches"
@@ -6570,7 +6625,7 @@ msgid "Branches|All"
msgstr "모ë‘"
msgid "Branches|Cancel, keep branch"
-msgstr ""
+msgstr "취소, 브랜치 유지"
msgid "Branches|Can’t find HEAD commit for this branch"
msgstr ""
@@ -6585,7 +6640,7 @@ msgid "Branches|Delete branch"
msgstr "브랜치 지우기"
msgid "Branches|Delete branch. Are you ABSOLUTELY SURE?"
-msgstr ""
+msgstr "브랜치를 삭제합니다. ì •ë§ í™•ì‹¤í•©ë‹ˆê¹Œ?"
msgid "Branches|Delete merged branches"
msgstr "ë¨¸ì§€ëœ ë¸Œëžœì¹˜ 지우기"
@@ -6594,10 +6649,10 @@ msgid "Branches|Delete protected branch"
msgstr "ë³´í˜¸ëœ ë¸Œëžœì¹˜ 지우기"
msgid "Branches|Delete protected branch. Are you ABSOLUTELY SURE?"
-msgstr ""
+msgstr "ë³´í˜¸ëœ ë¸Œëžœì¹˜ë¥¼ 삭제합니다. ì •ë§ í™•ì‹¤í•©ë‹ˆê¹Œ?"
msgid "Branches|Deleting the %{strongStart}%{branchName}%{strongEnd} branch cannot be undone. Are you sure?"
-msgstr ""
+msgstr "%{strongStart}%{branchName}%{strongEnd} 브랜치 삭제는 취소할 수 없습니다. 확실합니까?"
msgid "Branches|Deleting the merged branches cannot be undone. Are you sure?"
msgstr "ë¨¸ì§€ëœ ë¸Œëžœì¹˜ë¥¼ 삭제하면 ë˜ëŒë¦´ 수 없습니다. 괜찮ì„까요?"
@@ -6639,13 +6694,13 @@ msgid "Branches|Show overview of the branches"
msgstr "브랜치 개요 보기"
msgid "Branches|Show stale branches"
-msgstr ""
+msgstr "ì˜¤ëž˜ëœ ë¸Œëžœì¹˜ 보기"
msgid "Branches|Stale"
msgstr "오래ë¨"
msgid "Branches|Stale branches"
-msgstr ""
+msgstr "ì˜¤ëž˜ëœ ë¸Œëžœì¹˜"
msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
msgstr "ì´ ë¸Œëžœì¹˜ëŠ” 업스트림ì—ì„œ 분기 중ì´ë¯€ë¡œ ìžë™ìœ¼ë¡œ ì—…ë°ì´íŠ¸í•  수 없습니다."
@@ -6660,19 +6715,19 @@ msgid "Branches|To discard the local changes and overwrite the branch with the u
msgstr "로컬 ë³€ê²½ì‚¬í•­ì„ ì·¨ì†Œí•˜ê³  업스트림 버전으로 브랜치를 ë®ì–´ì“°ë ¤ë©´ 여기서 삭제하고 '지금 ì—…ë°ì´íŠ¸'를 ì„ íƒí•˜ì„¸ìš”."
msgid "Branches|Unable to load branches"
-msgstr ""
+msgstr "브랜치를 로드할 수 없습니다."
msgid "Branches|Yes, delete branch"
-msgstr ""
+msgstr "예, 브랜치를 삭제합니다."
msgid "Branches|Yes, delete protected branch"
-msgstr ""
+msgstr "예, ë³´í˜¸ëœ ë¸Œëžœì¹˜ë¥¼ 삭제합니다."
msgid "Branches|You're about to permanently delete the branch %{branchName}."
-msgstr ""
+msgstr "%{branchName} 브랜치를 ì˜êµ¬ì ìœ¼ë¡œ 삭제하려고 합니다."
msgid "Branches|You're about to permanently delete the protected branch %{branchName}."
-msgstr ""
+msgstr "ë³´í˜¸ëœ ë¸Œëžœì¹˜ %{branchName}ì„ ì˜êµ¬ì ìœ¼ë¡œ 삭제하려고 합니다."
msgid "Branches|diverged from upstream"
msgstr "업스트림ì—ì„œ 분기ë¨"
@@ -6684,7 +6739,7 @@ msgid "Branches|protected"
msgstr "보호ë¨"
msgid "Breadcrumbs"
-msgstr ""
+msgstr "íƒìƒ‰ 경로"
msgid "Broadcast Message was successfully created."
msgstr ""
@@ -6705,7 +6760,7 @@ msgid "Browse Files"
msgstr "íŒŒì¼ ì°¾ì•„ë³´ê¸°"
msgid "Browse artifacts"
-msgstr ""
+msgstr "아티팩트 찾아보기"
msgid "Browse files"
msgstr "íŒŒì¼ ì°¾ì•„ë³´ê¸°"
@@ -6713,6 +6768,9 @@ msgstr "íŒŒì¼ ì°¾ì•„ë³´ê¸°"
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -6726,7 +6784,7 @@ msgid "Bulk request concurrency"
msgstr ""
msgid "Bulk update"
-msgstr ""
+msgstr "대량 ì—…ë°ì´íŠ¸"
msgid "BulkImport| %{host} is running outdated GitLab version (v%{version})"
msgstr ""
@@ -6744,7 +6802,7 @@ msgid "BulkImport|Existing groups"
msgstr ""
msgid "BulkImport|Filter by source group"
-msgstr ""
+msgstr "소스 그룹으로 í•„í„°ë§"
msgid "BulkImport|Following data will not be migrated: %{bullets} Contact system administrator of %{host} to upgrade GitLab if you need this data in your migration"
msgstr ""
@@ -6864,13 +6922,13 @@ msgid "Business metrics (Custom)"
msgstr "비즈니스 지표 (ì‚¬ìš©ìž ì •ì˜)"
msgid "Busy"
-msgstr ""
+msgstr "ë°”ì¨"
msgid "Buy CI Minutes"
-msgstr ""
+msgstr "CI 시간 구매"
msgid "Buy Storage"
-msgstr ""
+msgstr "스토리지 구매"
msgid "Buy more Pipeline minutes"
msgstr ""
@@ -6885,13 +6943,13 @@ msgid "By default, all projects and groups will use the global notifications set
msgstr ""
msgid "By month"
-msgstr ""
+msgstr "월별"
msgid "By quarter"
-msgstr ""
+msgstr "분기별"
msgid "By week"
-msgstr ""
+msgstr "주별"
msgid "ByAuthor|by"
msgstr "작성ìž"
@@ -6909,7 +6967,7 @@ msgid "CI settings"
msgstr "CI 설정"
msgid "CI variables"
-msgstr ""
+msgstr "CI 변수"
msgid "CI/CD"
msgstr "CI/CD"
@@ -6930,7 +6988,7 @@ msgid "CI/CD limits"
msgstr ""
msgid "CI/CD minutes"
-msgstr ""
+msgstr "CI/CD 실행시간(분)"
msgid "CI/CD|No projects have been added to the scope"
msgstr ""
@@ -6954,20 +7012,17 @@ msgid "CICDAnalytics|No shared runner minute usage data available"
msgstr ""
msgid "CICDAnalytics|Projects with releases"
-msgstr ""
+msgstr "ë¦´ë¦¬ìŠ¤ëœ í”„ë¡œì íŠ¸"
msgid "CICDAnalytics|Release"
msgid_plural "CICDAnalytics|Releases"
-msgstr[0] ""
+msgstr[0] "릴리스"
msgid "CICDAnalytics|Release statistics"
-msgstr ""
+msgstr "릴리스 통계"
msgid "CICDAnalytics|Releases"
-msgstr ""
-
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
+msgstr "릴리스"
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
@@ -6975,23 +7030,14 @@ msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
-msgstr ""
+msgstr "릴리스 통계를 가져오는 ë° ë¬¸ì œê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "CICDAnalytics|Time to restore service"
msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
-msgstr ""
-
-msgid "CICDAnalytics|What is shared runner usage?"
-msgstr ""
+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 ""
@@ -7060,7 +7106,7 @@ msgid "CLOSED"
msgstr ""
msgid "CLOSED (MOVED)"
-msgstr ""
+msgstr "닫힘 (ì´ë™ë¨)"
msgid "CODEOWNERS rule violation"
msgstr ""
@@ -7207,7 +7253,7 @@ msgid "Cancel"
msgstr "취소"
msgid "Cancel and close"
-msgstr ""
+msgstr "취소 후 닫기"
msgid "Cancel downstream pipeline"
msgstr ""
@@ -7240,7 +7286,7 @@ msgid "Cancelling Preview"
msgstr ""
msgid "Cannot assign a confidential epic to a non-confidential issue. Make the issue confidential and try again"
-msgstr ""
+msgstr "대외비가 ì•„ë‹Œ ì´ìŠˆì— 대외비 ì—í”½ì„ í• ë‹¹í•  수 없습니다. ì´ìŠˆë¥¼ 대외비로 설정하고 다시 ì‹œë„하세요."
msgid "Cannot assign an issue that does not belong under the same group (or descendant) as the epic."
msgstr ""
@@ -7270,10 +7316,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 "대외비가 ì•„ë‹Œ 하위 ì—í”½ì´ í¬í•¨ëœ ì—í”½ì„ ëŒ€ì™¸ë¹„ë¡œ 지정할 수 없습니다."
msgid "Cannot make the epic confidential if it contains non-confidential issues"
-msgstr ""
+msgstr "대외비가 ì•„ë‹Œ ì´ìŠˆê°€ í¬í•¨ëœ ì—í”½ì„ ëŒ€ì™¸ë¹„ë¡œ 지정할 수 없습니다."
msgid "Cannot merge"
msgstr "머지할 수 ì—†ìŒ"
@@ -7354,10 +7400,10 @@ msgid "Change Failure Rate"
msgstr ""
msgid "Change assignee"
-msgstr ""
+msgstr "ë‹´ë‹¹ìž ë³€ê²½"
msgid "Change assignee(s)"
-msgstr ""
+msgstr "ë‹´ë‹¹ìž ë³€ê²½"
msgid "Change assignee(s)."
msgstr ""
@@ -7456,7 +7502,7 @@ msgid "Changed"
msgstr ""
msgid "Changed assignee(s)."
-msgstr ""
+msgstr "ë‹´ë‹¹ìž ë³€ê²½"
msgid "Changed merge method to %{merge_method}"
msgstr ""
@@ -7513,7 +7559,7 @@ msgid "ChatMessage|Commit"
msgstr ""
msgid "ChatMessage|Failed job"
-msgstr ""
+msgstr "실패한 작업"
msgid "ChatMessage|Failed stage"
msgstr ""
@@ -8158,13 +8204,13 @@ msgid "Clone repository"
msgstr "저장소 í´ë¡ "
msgid "Clone this issue"
-msgstr ""
+msgstr "ì´ ì´ìŠˆë¥¼ 복제"
msgid "Clone with %{http_label}"
msgstr "%{http_label} clone"
msgid "Clone with %{protocol}"
-msgstr ""
+msgstr "%{protocol}으로 복제"
msgid "Clone with KRB5"
msgstr "KRB5로 clone"
@@ -8182,7 +8228,7 @@ msgid "CloneIssue|Cannot clone issues of '%{issue_type}' type."
msgstr ""
msgid "Cloned this issue to %{path_to_project}."
-msgstr ""
+msgstr "ì´ ì´ìŠˆë¥¼ %{path_to_project}으로 복제했습니다."
msgid "Clones this issue, without comments, to %{project}."
msgstr ""
@@ -8191,7 +8237,7 @@ msgid "Close"
msgstr "닫기"
msgid "Close %{issueType}"
-msgstr ""
+msgstr "%{issueType} 닫기"
msgid "Close %{noteable}"
msgstr ""
@@ -8200,7 +8246,7 @@ msgid "Close %{tabname}"
msgstr ""
msgid "Close design"
-msgstr ""
+msgstr "ë””ìžì¸ 닫기"
msgid "Close epic"
msgstr "ì—픽 닫기"
@@ -8221,7 +8267,7 @@ msgid "Closed %{epicTimeagoDate}"
msgstr ""
msgid "Closed MRs"
-msgstr ""
+msgstr "닫힌 MR"
msgid "Closed date"
msgstr "마ê°ì¼"
@@ -8233,7 +8279,7 @@ msgid "Closed this %{quick_action_target}."
msgstr ""
msgid "Closed: %{closed}"
-msgstr ""
+msgstr "닫힘 : %{closed}"
msgid "Closes this %{quick_action_target}."
msgstr ""
@@ -8241,6 +8287,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr "Cloud SQL ì¸ìŠ¤í„´ìŠ¤ëŠ” 완전히 관리ë˜ëŠ”, 관계형 PostgreSQL ë°ì´í„°ë² ì´ìŠ¤ìž…니다. êµ¬ê¸€ì€ ê°€ìš©ì„±ê³¼ ì„±ëŠ¥ì„ ë³´ìž¥í•˜ê¸° 위해 복제, 패치 관리 ë° ë°ì´í„°ë² ì´ìŠ¤ 관리를 처리합니다."
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr "Cloud SQL ì¸ìŠ¤í„´ìŠ¤ëŠ” 완전히 관리ë˜ëŠ”, 관계형 SQL 서버 ë°ì´í„°ë² ì´ìŠ¤ìž…니다. êµ¬ê¸€ì€ ê°€ìš©ì„±ê³¼ ì„±ëŠ¥ì„ ë³´ìž¥í•˜ê¸° 위해 복제, 패치 관리 ë° ë°ì´í„°ë² ì´ìŠ¤ 관리를 처리합니다."
+
msgid "Cloud Storage"
msgstr ""
@@ -8254,46 +8306,58 @@ msgid "CloudSeed|Available database services through which instances may be crea
msgstr ""
msgid "CloudSeed|Cancel"
-msgstr ""
+msgstr "취소"
msgid "CloudSeed|Cloud Firestore"
msgstr ""
msgid "CloudSeed|Cloud SQL for MySQL"
-msgstr ""
+msgstr "MySQLìš© Cloud SQL"
msgid "CloudSeed|Cloud SQL for Postgres"
-msgstr ""
+msgstr "Postgresìš© Cloud SQL"
msgid "CloudSeed|Cloud SQL for SQL Server"
-msgstr ""
+msgstr "SQL Serverìš© Cloud SQL"
+
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr "Cloud SQL ì¸ìŠ¤í„´ìŠ¤ ìƒì„± ìš”ì²­ì´ ì„±ê³µí–ˆìŠµë‹ˆë‹¤. ì˜ˆìƒ í•´ê²° ì‹œê°„ì€ ~5분입니다."
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr "Cloud SQL ì¸ìŠ¤í„´ìŠ¤ëŠ” 완전히 관리ë˜ëŠ”, 관계형 PostgreSQL ë°ì´í„°ë² ì´ìŠ¤ìž…니다. êµ¬ê¸€ì€ ê°€ìš©ì„±ê³¼ ì„±ëŠ¥ì„ ë³´ìž¥í•˜ê¸° 위해 복제, 패치 관리 ë° ë°ì´í„°ë² ì´ìŠ¤ 관리를 처리합니다."
msgid "CloudSeed|CloudSQL Instance"
-msgstr ""
+msgstr "CloudSQL ì¸ìŠ¤í„´ìŠ¤"
msgid "CloudSeed|Configuration"
msgstr "설정"
+msgid "CloudSeed|Create MySQL Instance"
+msgstr "MySQL ì¸ìŠ¤í„´ìŠ¤ ìƒì„±"
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr "Postgres ì¸ìŠ¤í„´ìŠ¤ ìƒì„±"
+
msgid "CloudSeed|Create cluster"
-msgstr ""
+msgstr "í´ëŸ¬ìŠ¤í„° ìƒì„±"
msgid "CloudSeed|Create database"
msgstr "ë°ì´í„°ë² ì´ìŠ¤ ìƒì„±"
msgid "CloudSeed|Create instance"
-msgstr ""
+msgstr "ì¸ìŠ¤í„´ìŠ¤ ìƒì„±"
msgid "CloudSeed|Database instance is generated within the selected Google Cloud project"
-msgstr ""
+msgstr "ë°ì´í„°ë² ì´ìŠ¤ ì¸ìŠ¤í„´ìŠ¤ëŠ” ì„ íƒëœ 구글 í´ë¼ìš°ë“œ 프로ì íŠ¸ ë‚´ì—ì„œ ìƒì„±ë©ë‹ˆë‹¤."
msgid "CloudSeed|Database instances associated with this project"
-msgstr ""
+msgstr "ì´ í”„ë¡œì íŠ¸ì™€ ì—°ê²°ëœ ë°ì´í„°ë² ì´ìŠ¤ ì¸ìŠ¤í„´ìŠ¤"
msgid "CloudSeed|Database version"
-msgstr ""
+msgstr "ë°ì´í„°ë² ì´ìŠ¤ 버전"
msgid "CloudSeed|Databases"
-msgstr ""
+msgstr "ë°ì´í„°ë² ì´ìŠ¤"
msgid "CloudSeed|Deployments"
msgstr ""
@@ -8328,17 +8392,20 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr "구글 í´ë¼ìš°ë“œ 오류 - %{message}"
+
msgid "CloudSeed|Google Cloud Project"
-msgstr ""
+msgstr "구글 í´ë¼ìš°ë“œ 프로ì íŠ¸"
msgid "CloudSeed|Google Cloud project"
-msgstr ""
+msgstr "구글 í´ë¼ìš°ë“œ 프로ì íŠ¸"
msgid "CloudSeed|I accept Google Cloud pricing and responsibilities involved with managing database instances"
-msgstr ""
+msgstr "ë°ì´í„°ë² ì´ìŠ¤ ì¸ìŠ¤í„´ìŠ¤ 관리와 ê´€ë ¨ëœ êµ¬ê¸€ í´ë¼ìš°ë“œ 가격 ì •ì±… ë° ì˜ë¬´ì‚¬í•­ì— ë™ì˜í•©ë‹ˆë‹¤."
msgid "CloudSeed|Instances"
-msgstr ""
+msgstr "ì¸ìŠ¤í„´ìŠ¤"
msgid "CloudSeed|Learn more about pricing for %{cloudsqlPricingStart}Cloud SQL%{cloudsqlPricingEnd}, %{alloydbPricingStart}Alloy DB%{alloydbPricingEnd}, %{memorystorePricingStart}Memorystore%{memorystorePricingEnd} and %{firestorePricingStart}Firestore%{firestorePricingEnd}."
msgstr ""
@@ -8356,25 +8423,25 @@ msgid "CloudSeed|Refs"
msgstr ""
msgid "CloudSeed|Regions"
-msgstr ""
+msgstr "지역"
msgid "CloudSeed|Scalable, secure, and highly available in-memory service for Redis"
-msgstr ""
+msgstr "Redis를 위한 확장 가능하고 안전하며 고가용성 ì¸ë©”모리 서비스"
msgid "CloudSeed|Service"
msgstr "서비스"
msgid "CloudSeed|Service Account"
-msgstr ""
+msgstr "서비스 계정"
msgid "CloudSeed|Services"
-msgstr ""
+msgstr "서비스"
msgid "CloudSeed|There are no instances to display."
-msgstr ""
+msgstr "표시할 ì¸ìŠ¤í„´ìŠ¤ê°€ 없습니다."
msgid "CloudSeed|Version"
-msgstr ""
+msgstr "버전"
msgid "Cluster"
msgstr ""
@@ -8416,10 +8483,10 @@ msgid "ClusterAgents|%{titleIcon}Not connected"
msgstr ""
msgid "ClusterAgents|%{tokenName} created"
-msgstr ""
+msgstr "%{tokenName} ìƒì„±ë¨"
msgid "ClusterAgents|%{tokenName} revoked"
-msgstr ""
+msgstr "%{tokenName} 취소ë¨"
msgid "ClusterAgents|Access tokens"
msgstr ""
@@ -8672,7 +8739,7 @@ msgid "ClusterAgents|Token created by %{userName}"
msgstr ""
msgid "ClusterAgents|Token revoked by %{userName}"
-msgstr ""
+msgstr "%{userName}ì— ì˜í•´ 토í°ì´ 취소ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "ClusterAgents|Unknown user"
msgstr ""
@@ -8789,7 +8856,7 @@ msgid "ClusterIntegration|Choose which of your environments will use this cluste
msgstr "ì´ Kubernetes í´ëŸ¬ìŠ¤í„°ë¥¼ 사용할 프로ì íŠ¸ 환경 ì„ íƒ"
msgid "ClusterIntegration|Civo Kubernetes"
-msgstr ""
+msgstr "Civo 쿠버네티스"
msgid "ClusterIntegration|Clear cluster cache"
msgstr ""
@@ -9013,10 +9080,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9119,7 +9186,7 @@ msgid "Code Review Analytics displays a table of open merge requests considered
msgstr ""
msgid "Code block"
-msgstr ""
+msgstr "코드 블ë¡"
msgid "Code can be imported from enabled sources during project creation. OmniAuth must be configured for GitHub"
msgstr ""
@@ -9187,6 +9254,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr "머지 세부정보 축소"
+
msgid "Collapse milestones"
msgstr ""
@@ -9209,16 +9279,16 @@ msgid "Color"
msgstr "색ìƒ"
msgid "ColorWidget|An error occurred while updating color."
-msgstr ""
+msgstr "색ìƒì„ ì—…ë°ì´íŠ¸í•˜ëŠ” ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "ColorWidget|Assign epic color"
-msgstr ""
+msgstr "ì—픽 ìƒ‰ìƒ ì§€ì •"
msgid "ColorWidget|Color"
-msgstr ""
+msgstr "색ìƒ"
msgid "ColorWidget|Error fetching epic color."
-msgstr ""
+msgstr "ì—픽 색ìƒì„ 가져오는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "Colorize messages"
msgstr ""
@@ -9253,6 +9323,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr "타임ë¼ì¸ì— ëŒ“ê¸€ì´ ì¶”ê°€ë˜ì—ˆìŠµë‹ˆë‹¤."
+
msgid "Comment form position"
msgstr "댓글 ì–‘ì‹ ìœ„ì¹˜"
@@ -9288,10 +9361,10 @@ msgid "Commit Message"
msgstr "커밋 메시지"
msgid "Commit SHA"
-msgstr ""
+msgstr "커밋 SHA"
msgid "Commit changes"
-msgstr ""
+msgstr "변경 사항 커밋"
msgid "Commit deleted"
msgstr "ì»¤ë°‹ì´ ì‚­ì œë¨"
@@ -9300,7 +9373,7 @@ msgid "Commit message"
msgstr "커밋 메시지"
msgid "Commit message (optional)"
-msgstr ""
+msgstr "커밋 메시지 (ì„ íƒ ì‚¬í•­)"
msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
msgstr "%{ref} ì— ëŒ€í•œ 커밋 통계 %{start_time} - %{end_time}"
@@ -9363,7 +9436,7 @@ msgid "Compare"
msgstr "비êµ"
msgid "Compare %{oldCommitId}...%{newCommitId}"
-msgstr ""
+msgstr "ë¹„êµ %{oldCommitId}...%{newCommitId}"
msgid "Compare Git revisions"
msgstr "Git 리비전 비êµ"
@@ -9374,6 +9447,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr "리비전 비êµ"
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr "변경 사항 비êµ"
@@ -9555,16 +9631,16 @@ msgid "Confidence"
msgstr ""
msgid "Confidential"
-msgstr "비밀"
+msgstr "비공개"
msgid "Confidential issue"
-msgstr ""
+msgstr "비공개 ì´ìŠˆ"
msgid "Confidential note"
-msgstr ""
+msgstr "비공개 메모"
msgid "Confidentiality"
-msgstr "비밀"
+msgstr "대외비"
msgid "Configuration"
msgstr ""
@@ -9949,6 +10025,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10052,6 +10131,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10226,7 +10311,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10341,7 +10426,7 @@ msgid "Copy commands"
msgstr ""
msgid "Copy commit SHA"
-msgstr ""
+msgstr "커밋 SHA 복사"
msgid "Copy environment"
msgstr ""
@@ -10479,7 +10564,7 @@ msgid "CorpusManagement|Total Size: %{totalSize}"
msgstr ""
msgid "Could not add admins as members"
-msgstr ""
+msgstr "관리ìžë¥¼ 구성ì›ìœ¼ë¡œ 추가할 수 없습니다."
msgid "Could not apply %{name} command."
msgstr ""
@@ -10644,7 +10729,7 @@ msgid "Create a group"
msgstr ""
msgid "Create a merge request"
-msgstr ""
+msgstr "머지 리퀘스트 만들기"
msgid "Create a new %{codeStart}.gitlab-ci.yml%{codeEnd} file at the root of the repository to get started."
msgstr ""
@@ -10692,10 +10777,10 @@ msgid "Create common files more quickly, and standardize their format."
msgstr ""
msgid "Create confidential merge request"
-msgstr ""
+msgstr "비공개 머지 리퀘스트 ìƒì„±"
msgid "Create confidential merge request and branch"
-msgstr ""
+msgstr "비공개 머지 리퀘스트와 브랜치 ìƒì„±"
msgid "Create custom type"
msgstr ""
@@ -10761,7 +10846,7 @@ msgid "Create new branch"
msgstr "새 브랜치 ìƒì„±"
msgid "Create new confidential %{issuableType}"
-msgstr ""
+msgstr "새 대외비 %{issuableType} 만들기"
msgid "Create new directory"
msgstr "새 디렉토리 만들기"
@@ -10797,7 +10882,7 @@ msgid "Create project label"
msgstr "새로운 프로ì íŠ¸ ë¼ë²¨"
msgid "Create release"
-msgstr ""
+msgstr "릴리스 ìƒì„±"
msgid "Create requirement"
msgstr ""
@@ -10829,9 +10914,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11375,6 +11457,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr "CycleAnalytics|í‰ê·  완료 시간(ì¼)"
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11413,7 +11498,7 @@ msgid_plural "CycleAnalytics|%d projects selected"
msgstr[0] ""
msgid "CycleAnalytics|Select labels"
-msgstr ""
+msgstr "ë¼ë²¨ ì„ íƒ"
msgid "CycleAnalytics|Show"
msgstr ""
@@ -11464,10 +11549,10 @@ msgid "DAST configuration not found"
msgstr "DAST ì„¤ì •ì„ ì°¾ì„ ìˆ˜ 없습니다."
msgid "DAST profile not found: %{name}"
-msgstr ""
+msgstr "DAST í”„ë¡œí•„ì„ ì°¾ì„ ìˆ˜ ì—†ìŒ: %{name}"
msgid "DAST profiles"
-msgstr ""
+msgstr "DAST 프로필"
msgid "DNS"
msgstr "DNS"
@@ -11581,13 +11666,13 @@ msgid "DastConfig|Customize DAST settings to suit your requirements. Configurati
msgstr ""
msgid "DastConfig|DAST CI/CD configuration"
-msgstr ""
+msgstr "DAST CI/CD 설정"
msgid "DastConfig|Enable DAST to automatically test for vulnerabilities in your project's running application, website, or API, in the CI/CD pipeline. Configuration changes must be applied to your .gitlab-ci.yml file to take effect. For details of all configuration options, see the %{linkStart}GitLab DAST documentation%{linkEnd}."
msgstr ""
msgid "DastConfig|Enabled"
-msgstr ""
+msgstr "활성화ë¨"
msgid "DastConfig|Generate code snippet"
msgstr ""
@@ -11599,11 +11684,17 @@ msgid "DastConfig|No previous scans found for this project"
msgstr ""
msgid "DastConfig|Not enabled"
-msgstr ""
+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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11682,15 +11773,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11703,6 +11785,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr "DastProfiles|기본 ì¸ì¦ 사용"
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11731,7 +11816,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 ""
@@ -11778,6 +11863,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11805,16 +11893,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11824,7 +11909,7 @@ msgid "DastProfiles|Select a site profile to run a DAST scan"
msgstr ""
msgid "DastProfiles|Select branch"
-msgstr ""
+msgstr "브랜치 ì„ íƒ"
msgid "DastProfiles|Select scanner profile"
msgstr ""
@@ -11835,16 +11920,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -11913,14 +11995,14 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
-msgstr ""
+msgid "DastProfiles|https://example.com/dast_example.har"
+msgstr "DastProfiles|https://example.com/dast_example.har"
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
-msgstr ""
+msgid "DastProfiles|https://example.com/openapi.json"
+msgstr "DastProfiles|https://example.com/openapi.json"
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
-msgstr ""
+msgid "DastProfiles|https://example.com/postman_collection.json"
+msgstr "DastProfiles|https://example.com/postman_collection.json"
msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
@@ -11944,7 +12026,7 @@ msgid "DastSiteValidation|Meta tag validation"
msgstr ""
msgid "DastSiteValidation|Not validated"
-msgstr ""
+msgstr "ê²€ì¦ë˜ì§€ ì•ŠìŒ"
msgid "DastSiteValidation|Retry validation"
msgstr ""
@@ -12106,7 +12188,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12218,7 +12300,7 @@ msgid "Definition"
msgstr ""
msgid "Delay 2FA enforcement (hours)"
-msgstr ""
+msgstr "2FA 시행 지연 (시간)"
msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after its timer finishes."
msgstr ""
@@ -12269,7 +12351,7 @@ msgid "Delete account"
msgstr ""
msgid "Delete artifacts"
-msgstr ""
+msgstr "아티팩트 삭제"
msgid "Delete audio"
msgstr ""
@@ -12292,6 +12374,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr "ì—픽 ì‚­ì œ"
+
msgid "Delete file"
msgstr ""
@@ -12343,6 +12428,9 @@ msgstr ""
msgid "Delete source branch"
msgstr "소스 브랜치 삭제"
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -12356,7 +12444,7 @@ msgid "Delete this epic and all descendants?"
msgstr ""
msgid "Delete this project"
-msgstr ""
+msgstr "ì´ í”„ë¡œì íŠ¸ ì‚­ì œ"
msgid "Delete user list"
msgstr ""
@@ -12368,19 +12456,19 @@ msgid "Delete video"
msgstr ""
msgid "DeleteProject|Failed to remove events. Please try again or contact administrator."
-msgstr ""
+msgstr "ì´ë²¤íŠ¸ë¥¼ 제거하지 못했습니다. 다시 ì‹œë„하거나 관리ìžì—게 문ì˜í•˜ì„¸ìš”."
msgid "DeleteProject|Failed to remove project repository. Please try again or contact administrator."
msgstr ""
msgid "DeleteProject|Failed to remove project snippets. Please try again or contact administrator."
-msgstr ""
+msgstr "프로ì íŠ¸ ìŠ¤ë‹ˆíŽ«ì„ ì œê±°í•˜ëŠ” ë° ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„하거나 관리ìžì—게 문ì˜í•˜ì„¸ìš”."
msgid "DeleteProject|Failed to remove some tags in project container registry. Please try again or contact administrator."
-msgstr ""
+msgstr "프로ì íŠ¸ 컨테ì´ë„ˆ 레지스트리ì—ì„œ ì¼ë¶€ 태그를 제거하지 못했습니다. 다시 ì‹œë„하거나 관리ìžì—게 문ì˜í•˜ì„¸ìš”."
msgid "DeleteProject|Failed to remove webhooks. Please try again or contact administrator."
-msgstr ""
+msgstr "ì›¹í›…ì„ ì œê±°í•˜ì§€ 못했습니다. 다시 ì‹œë„하거나 관리ìžì—게 문ì˜í•˜ì„¸ìš”."
msgid "DeleteProject|Failed to remove wiki repository. Please try again or contact administrator."
msgstr ""
@@ -12410,7 +12498,7 @@ msgid "Deleted chat nickname: %{chat_name}!"
msgstr ""
msgid "Deleted projects cannot be restored!"
-msgstr ""
+msgstr "ì‚­ì œëœ í”„ë¡œì íŠ¸ëŠ” ë³µì›í•  수 없습니다!"
msgid "Deleted the source branch."
msgstr ""
@@ -12446,7 +12534,7 @@ msgid "DeletionSettings|Keep deleted projects for 1 day"
msgstr ""
msgid "DeletionSettings|None, delete immediately"
-msgstr ""
+msgstr "ì—†ìŒ, 즉시 ì‚­ì œ"
msgid "DeletionSettings|Only administrators can delete projects."
msgstr ""
@@ -12509,7 +12597,7 @@ msgid "Dependencies|Export as JSON"
msgstr ""
msgid "Dependencies|Job failed to generate the dependency list"
-msgstr ""
+msgstr "ì˜ì¡´ì„± 목ë¡ì„ 만들어내는 ìž‘ì—…ì„ ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤"
msgid "Dependencies|Learn more about dependency paths"
msgstr ""
@@ -12761,7 +12849,7 @@ msgid "DeployTokens|Copy deploy token"
msgstr ""
msgid "DeployTokens|Copy username"
-msgstr ""
+msgstr "ì‚¬ìš©ìž ì´ë¦„ 복사"
msgid "DeployTokens|Create a new deploy token for all projects in this group. %{link_start}What are deploy tokens?%{link_end}"
msgstr ""
@@ -12779,10 +12867,10 @@ msgid "DeployTokens|Deploy tokens allow access to packages, your repository, and
msgstr ""
msgid "DeployTokens|Enter a unique name for your deploy token."
-msgstr ""
+msgstr "ë°°í¬ í† í°ì˜ 고유한 ì´ë¦„ì„ ìž…ë ¥í•˜ì‹­ì‹œì˜¤."
msgid "DeployTokens|Enter a username for your token. Defaults to %{code_start}gitlab+deploy-token-{n}%{code_end}."
-msgstr ""
+msgstr "토í°ì˜ ì‚¬ìš©ìž ì´ë¦„ì„ ìž…ë ¥í•˜ì‹­ì‹œì˜¤. ê¸°ë³¸ê°’ì€ %{code_start}gitlab+deploy-token-{n}%{code_end}입니다."
msgid "DeployTokens|Enter an expiration date for your token. Defaults to never expire."
msgstr ""
@@ -12851,10 +12939,10 @@ msgid "Deploying to AWS is easy with GitLab"
msgstr ""
msgid "Deployment"
-msgstr ""
+msgstr "ë°°í¬"
msgid "Deployment Frequency"
-msgstr ""
+msgstr "ë°°í¬ ë¹ˆë„"
msgid "Deployment Target|%{linkStart}How to provision or deploy to Kubernetes clusters from GitLab?%{linkEnd}"
msgstr ""
@@ -12866,7 +12954,7 @@ msgid "Deployment Target|Select the deployment target"
msgstr ""
msgid "Deployment frequency"
-msgstr ""
+msgstr "ë°°í¬ ë¹ˆë„"
msgid "DeploymentApproval| Current approvals: %{current}"
msgstr ""
@@ -12895,23 +12983,29 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr "DeploymentApproval|ê±°ë¶€ë¨ %{time}"
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr "DeploymentApproval|사용ìžì—ì˜í•´ ê±°ë¶€ë¨ %{time}"
+
msgid "DeploymentTarget|GitLab Pages"
-msgstr ""
+msgstr "GitLab 페ì´ì§€"
msgid "DeploymentTarget|Heroku"
-msgstr ""
+msgstr "Heroku"
msgid "DeploymentTarget|Infrastructure provider (Terraform, Cloudformation, and so on)"
-msgstr ""
+msgstr "ì¸í”„ë¼ ì œê³µìž(Terraform, Cloudformation 등)"
msgid "DeploymentTarget|Kubernetes (GKE, EKS, OpenShift, and so on)"
-msgstr ""
+msgstr "Kubernetes (GKE, EKS, OpenShift 등)"
msgid "DeploymentTarget|Managed container runtime (Fargate, Cloud Run, DigitalOcean App)"
msgstr ""
msgid "DeploymentTarget|Mobile app store"
-msgstr ""
+msgstr "ëª¨ë°”ì¼ ì•± 스토어"
msgid "DeploymentTarget|No deployment planned"
msgstr ""
@@ -12932,14 +13026,14 @@ msgid "DeploymentTarget|Virtual machine (for example, EC2)"
msgstr ""
msgid "Deployments"
-msgstr ""
+msgstr "ë°°í¬"
msgid "Deployments|%{deployments} environment impacted."
msgid_plural "Deployments|%{deployments} environments impacted."
msgstr[0] ""
msgid "Deployment|API"
-msgstr ""
+msgstr "API"
msgid "Deployment|Cancelled"
msgstr ""
@@ -12948,7 +13042,7 @@ msgid "Deployment|Created"
msgstr ""
msgid "Deployment|Deployment ID"
-msgstr ""
+msgstr "ë°°í¬ ID"
msgid "Deployment|Failed"
msgstr ""
@@ -13122,6 +13216,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13215,6 +13312,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13405,7 +13505,7 @@ msgid "DevopsReport|Your usage"
msgstr ""
msgid "Diagram (%{language})"
-msgstr ""
+msgstr "다ì´ì–´ê·¸ëž¨ (%{language})"
msgid "Did not delete the source branch."
msgstr ""
@@ -13724,7 +13824,7 @@ msgid "Download PDF"
msgstr ""
msgid "Download artifacts"
-msgstr ""
+msgstr "아티팩트 다운로드"
msgid "Download codes"
msgstr "코드 다운로드"
@@ -13771,6 +13871,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13792,6 +13895,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13807,6 +13916,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -13838,7 +13953,7 @@ msgid "Duration|%s hours"
msgstr ""
msgid "Duration|%s minutes"
-msgstr ""
+msgstr "%s 분"
msgid "Duration|%s months"
msgstr ""
@@ -13889,7 +14004,7 @@ msgid "Edit %{name}"
msgstr "%{name} 편집"
msgid "Edit %{profileType} profile"
-msgstr ""
+msgstr "%{profileType} 프로필 수정"
msgid "Edit Comment"
msgstr "댓글 편집"
@@ -13919,7 +14034,7 @@ msgid "Edit Pipeline Schedule"
msgstr ""
msgid "Edit Release"
-msgstr ""
+msgstr "릴리즈 편집"
msgid "Edit Requirement"
msgstr ""
@@ -13943,7 +14058,7 @@ msgid "Edit comment"
msgstr "댓글 편집"
msgid "Edit commit message"
-msgstr ""
+msgstr "커밋 메시지 수정"
msgid "Edit deploy freeze"
msgstr ""
@@ -13994,7 +14109,7 @@ msgid "Edit link"
msgstr ""
msgid "Edit merge requests"
-msgstr ""
+msgstr "머지 리퀘스트 수정"
msgid "Edit public deploy key"
msgstr "공개 ë°°í¬ í‚¤ 편집"
@@ -14009,7 +14124,7 @@ msgid "Edit this file only."
msgstr ""
msgid "Edit this release"
-msgstr ""
+msgstr "ì´ ë¦´ë¦¬ìŠ¤ 편집"
msgid "Edit title and description"
msgstr ""
@@ -14029,6 +14144,9 @@ msgstr "위키 페ì´ì§€ 편집"
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14044,9 +14162,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14071,9 +14186,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14219,6 +14331,9 @@ msgid "Enable Auto DevOps"
msgstr "ìžë™ DevOps 활성화"
msgid "Enable GitLab Error Tracking"
+msgstr "GitLab 오류 ì¶”ì  í™œì„±í™”"
+
+msgid "Enable GitLab Prometheus metrics endpoint"
msgstr ""
msgid "Enable Gitpod"
@@ -14305,9 +14420,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr "ë©”ì¼ ë¨¸ë¦¬ë§ ë° ê¼¬ë¦¬ë§ ì‚¬ìš©"
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14681,7 +14793,7 @@ msgid "Environments|Open live environment"
msgstr ""
msgid "Environments|Re-deploy environment"
-msgstr ""
+msgstr "ìž¬ë°°í¬ í™˜ê²½"
msgid "Environments|Re-deploy environment %{name}?"
msgstr ""
@@ -14720,7 +14832,7 @@ msgid "Environments|Upcoming"
msgstr ""
msgid "Environments|Upcoming deployment"
-msgstr ""
+msgstr "ì˜ˆì •ëœ ë°°í¬"
msgid "Environments|Updated"
msgstr "갱신ë¨"
@@ -14749,6 +14861,9 @@ msgstr "ì—픽"
msgid "Epic Boards"
msgstr "ì—픽 ë³´ë“œ"
+msgid "Epic actions"
+msgstr "ì—픽 ë™ìž‘"
+
msgid "Epic cannot be found."
msgstr ""
@@ -14785,12 +14900,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -14852,23 +14961,14 @@ msgid "Epics|The color for the epic when it's visualized, such as on roadmap tim
msgstr ""
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 "ì´ ì—픽과 하위 ì—í”½ë“¤ì„ í¬í•¨í•œ ê²ƒë“¤ì€ ë¹„ê³µê°œì´ë©°, ì ì–´ë„ 리í¬í„° ì´ìƒì˜ ê¶Œí•œì´ ìžˆëŠ” 팀 사용ìžë“¤ì—게만 보여집니다."
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr "시작"
-
msgid "Erased"
msgstr ""
@@ -14905,6 +15005,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -14938,9 +15041,6 @@ msgstr "브랜치 로드 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15016,6 +15116,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr "메모를 타임ë¼ì¸ ì´ë²¤íŠ¸ë¡œ 승격하는 중 오류 ë°œìƒ: %{error}"
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15446,6 +15549,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr "머지 세부정보 확장"
+
msgid "Expand milestones"
msgstr ""
@@ -15679,7 +15785,7 @@ msgstr "작업 실패"
msgid "Failed job"
msgid_plural "Failed jobs"
-msgstr[0] ""
+msgstr[0] "실패한 작업"
msgid "Failed on"
msgstr ""
@@ -16193,9 +16299,6 @@ msgstr "2ì›”"
msgid "February"
msgstr "2ì›”"
-msgid "Feedback issue"
-msgstr "ì´ìŠˆ 피드백"
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16266,7 +16369,7 @@ msgid "Files breadcrumb"
msgstr ""
msgid "Files with large changes are collapsed by default."
-msgstr ""
+msgstr "변경 ì‚¬í•­ì´ í° íŒŒì¼ì€ 기본ì ìœ¼ë¡œ 접혀있습니다."
msgid "Files, directories, and submodules in the path %{path} for commit reference %{ref}"
msgstr ""
@@ -16293,7 +16396,7 @@ 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 ""
@@ -16302,7 +16405,7 @@ msgid "Filter by merge requests that are currently merged."
msgstr ""
msgid "Filter by milestone"
-msgstr ""
+msgstr "마ì¼ìŠ¤í†¤ìœ¼ë¡œ í•„í„°ë§"
msgid "Filter by milestone name"
msgstr "마ì¼ìŠ¤í†¤ ì´ë¦„으로 í•„í„°ë§"
@@ -16317,7 +16420,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 ""
@@ -16371,7 +16474,7 @@ msgid "Finish editing this message first!"
msgstr ""
msgid "Finish review"
-msgstr ""
+msgstr "리뷰 완료"
msgid "Finish setting up your dedicated account for %{group_name}."
msgstr ""
@@ -16406,13 +16509,13 @@ msgstr "수정ë¨:"
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16467,7 +16570,7 @@ msgid "Footer message"
msgstr "바닥글 메시지"
msgid "For a faster browsing experience, some files are collapsed by default."
-msgstr ""
+msgstr "ë” ë¹ ë¥¸ 브ë¼ìš°ì§• ê²½í—˜ì„ ìœ„í•´, ì¼ë¶€ 파ì¼ì€ 기본ì ìœ¼ë¡œ 접혀 있습니다."
msgid "For additional information, review your %{link_to} or contact your group owner."
msgstr ""
@@ -16562,6 +16665,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16629,6 +16735,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr "금"
+
msgid "Friday"
msgstr "금요ì¼"
@@ -16641,13 +16750,9 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
-msgstr[0] ""
+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 "ì´ìŠˆ ìƒì„±ì—ì„œ 프로ë•ì…˜ ë°°í¬ê¹Œì§€"
@@ -16707,7 +16812,7 @@ msgid "Generate site and private keys at"
msgstr ""
msgid "Generated with JSON data"
-msgstr ""
+msgstr "JSON ë°ì´í„°ë¡œ ìƒì„±"
msgid "Generic"
msgstr ""
@@ -16786,9 +16891,6 @@ msgstr "새 사ì´íŠ¸ 추가"
msgid "Geo|Add site"
msgstr "사ì´íŠ¸ 추가"
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -16865,7 +16967,7 @@ msgid "Geo|Discover GitLab Geo"
msgstr "GitLab Geo 알아보기"
msgid "Geo|Does not match the primary storage configuration"
-msgstr ""
+msgstr "주 스토리지 설정과 ì¼ì¹˜í•˜ì§€ 않습니다."
msgid "Geo|Edit %{nodeType} site"
msgstr ""
@@ -16892,7 +16994,7 @@ msgid "Geo|Filter by name"
msgstr "ì´ë¦„으로 í•„í„°ë§"
msgid "Geo|Filter by status"
-msgstr ""
+msgstr "ìƒíƒœë¡œ í•„í„°ë§"
msgid "Geo|Full details"
msgstr ""
@@ -16984,6 +17086,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17191,6 +17299,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17353,9 +17464,12 @@ msgstr ""
msgid "Git version"
msgstr "Git 버전"
-msgid "GitAbuse|Excluded users"
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
msgstr ""
+msgid "GitAbuse|Excluded users"
+msgstr "ì œì™¸ëœ ì‚¬ìš©ìž"
+
msgid "GitAbuse|Number of repositories"
msgstr ""
@@ -17405,7 +17519,7 @@ msgid "GitLab (self-managed)"
msgstr ""
msgid "GitLab / Unsubscribe"
-msgstr ""
+msgstr "GitLab / êµ¬ë… ì·¨ì†Œ"
msgid "GitLab API"
msgstr ""
@@ -17417,7 +17531,7 @@ msgid "GitLab Billing Team."
msgstr ""
msgid "GitLab Error Tracking"
-msgstr ""
+msgstr "GitLab 오류 추ì "
msgid "GitLab Import"
msgstr "GitLab 가져오기"
@@ -17570,7 +17684,7 @@ msgid "GitLabPages|GitLab Pages are disabled for this project. You can enable th
msgstr ""
msgid "GitLabPages|Maximum size (MB)"
-msgstr ""
+msgstr "최대 í¬ê¸°(MB)"
msgid "GitLabPages|New Domain"
msgstr ""
@@ -17722,17 +17836,26 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
msgid "GlobalSearch| %{search} %{description} %{scope}"
-msgstr ""
+msgstr "%{search} %{description} %{scope}"
msgid "GlobalSearch|%{count} default results provided. Use the up and down arrow keys to navigate search results list."
msgstr "%{count}ê°œì˜ ê¸°ë³¸ 결과가 제공ë˜ì—ˆìŠµë‹ˆë‹¤. 위/아래 화살표 키를 사용하여 검색 결과를 íƒìƒ‰í•˜ì„¸ìš”."
msgid "GlobalSearch|Groups"
-msgstr ""
+msgstr "그룹"
+
+msgid "GlobalSearch|Help"
+msgstr "GlobalSearch|ë„움ë§"
+
+msgid "GlobalSearch|In this project"
+msgstr "GlobalSearch|ì´ í”„ë¡œì íŠ¸ì—ì„œ"
msgid "GlobalSearch|Issues I've created"
msgstr "ë‚´ê°€ 만든 ì´ìŠˆ"
@@ -17750,7 +17873,16 @@ msgid "GlobalSearch|Merge requests that I'm a reviewer"
msgstr "ë‚´ê°€ 검토ìžì¸ 머지 리퀘스트"
msgid "GlobalSearch|Projects"
-msgstr ""
+msgstr "프로ì íŠ¸"
+
+msgid "GlobalSearch|Recent epics"
+msgstr "GlobalSearch|최근 ì—픽"
+
+msgid "GlobalSearch|Recent issues"
+msgstr "GlobalSearch|최근 ì´ìŠˆ"
+
+msgid "GlobalSearch|Recent merge requests"
+msgstr "GlobalSearch|최근 머지 리퀘스트"
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}ê°œì˜ ê²°ê³¼ë¥¼ 사용할 수 있습니다. 위/아래 화살표 키를 사용하여 검색 결과를 íƒìƒ‰í•˜ê±°ë‚˜ 엔터 키를 눌러 제출하세요."
@@ -17764,6 +17896,9 @@ msgstr "프로ì íŠ¸, ì´ìŠˆ, 기타 검색"
msgid "GlobalSearch|Search results are loading"
msgstr "검색 결과가 로드 중입니다."
+msgid "GlobalSearch|Settings"
+msgstr "GlobalSearch|설정"
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr "검색 ìžë™ 완성 ì œì•ˆì„ ê°€ì ¸ì˜¤ëŠ” ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
@@ -17774,7 +17909,7 @@ msgid "GlobalSearch|Type for new suggestions to appear below."
msgstr "ì•„ëž˜ì— í‘œì‹œí•  새 ì œì•ˆì„ ìž…ë ¥í•˜ì„¸ìš”."
msgid "GlobalSearch|Use the shortcut key %{kbdOpen}/%{kbdClose} to start a search"
-msgstr ""
+msgstr "단축키 %{kbdOpen}/%{kbdClose}ì„(를) ì´ìš©í•´ ê²€ìƒ‰ì„ ì‹œìž‘í•´ 보세요."
msgid "GlobalSearch|What are you searching for?"
msgstr "ë¬´ì—‡ì„ ì°¾ê³  있나요?"
@@ -17783,12 +17918,18 @@ msgid "GlobalSearch|all GitLab"
msgstr ""
msgid "GlobalSearch|group"
-msgstr ""
+msgstr "그룹"
msgid "GlobalSearch|in %{scope}"
msgstr ""
msgid "GlobalSearch|project"
+msgstr "프로ì íŠ¸"
+
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
msgstr ""
msgid "Globally-allowed IP ranges"
@@ -17816,7 +17957,7 @@ msgid "Go to %{source_name}"
msgstr ""
msgid "Go to commits"
-msgstr ""
+msgstr "커밋으로 ì´ë™"
msgid "Go to definition"
msgstr ""
@@ -17888,7 +18029,7 @@ msgid "Go to project"
msgstr ""
msgid "Go to releases"
-msgstr ""
+msgstr "릴리스로 ì´ë™"
msgid "Go to repository charts"
msgstr ""
@@ -17954,7 +18095,7 @@ msgid "Google Cloud"
msgstr ""
msgid "Google Cloud Error - %{error}"
-msgstr "Google í´ë¼ìš°ë“œ 오류 - %{error}"
+msgstr "구글 í´ë¼ìš°ë“œ 오류 - %{error}"
msgid "Google Cloud Project"
msgstr ""
@@ -17996,7 +18137,7 @@ msgid "GoogleCloud|Revoke authorizations"
msgstr ""
msgid "GoogleCloud|Revoke authorizations granted to GitLab. This does not invalidate service accounts."
-msgstr ""
+msgstr "GitLabì— ë¶€ì—¬ëœ ìŠ¹ì¸ì„ 취소합니다. ì´ê²ƒì´ 서비스 ê³„ì •ì„ ë¬´íš¨í™”í•˜ì§€ëŠ” 않습니다."
msgid "Got it"
msgstr ""
@@ -18061,9 +18202,6 @@ msgstr ""
msgid "Group"
msgstr "그룹"
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18158,7 +18296,7 @@ msgid "Group jobs by"
msgstr ""
msgid "Group members"
-msgstr ""
+msgstr "그룹 구성ì›"
msgid "Group membership expiration date changed"
msgstr "그룹 멤버십 만료ì¼ì´ 변경ë¨"
@@ -18215,7 +18353,7 @@ msgid "Group wikis"
msgstr ""
msgid "Group-level wiki is disabled."
-msgstr ""
+msgstr "그룹 수준 위키가 비활성화ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "Group: %{group_name}"
msgstr "그룹: %{group_name}"
@@ -18227,7 +18365,7 @@ msgid "GroupActivityMetrics|Issues created"
msgstr ""
msgid "GroupActivityMetrics|Last 30 days"
-msgstr ""
+msgstr "최근 30ì¼"
msgid "GroupActivityMetrics|Members added"
msgstr ""
@@ -18499,9 +18637,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr "GroupSettings|주어진 ì‹œê°„ì— ì§€ì •ëœ ìˆ˜ ì´ìƒì˜ 리í¬ì§€í† ë¦¬ë¥¼ 다운로드하는 사용ìžë¥¼ ìžë™ìœ¼ë¡œ 차단합니다."
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18529,11 +18664,14 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
msgid "GroupSettings|Customer relations is enabled"
-msgstr ""
+msgstr "ê³ ê° ê´€ë¦¬ê°€ 활성화ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "GroupSettings|Customize this group's badges."
msgstr ""
@@ -18542,7 +18680,7 @@ msgid "GroupSettings|Default to Auto DevOps pipeline for all projects within thi
msgstr ""
msgid "GroupSettings|Email notifications are disabled"
-msgstr ""
+msgstr "ì´ë©”ì¼ ì•Œë¦¼ì´ ë¹„í™œì„±í™”ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "GroupSettings|Export group"
msgstr ""
@@ -18566,7 +18704,7 @@ msgid "GroupSettings|Members cannot invite groups outside of %{group} and its su
msgstr ""
msgid "GroupSettings|Organizations and contacts can be created and associated with issues."
-msgstr ""
+msgstr "ì¡°ì§ ë° ì—°ë½ì²˜ë¥¼ 만들고 ì´ìŠˆì™€ ì—°ê²°í•  수 있습니다."
msgid "GroupSettings|Overrides user notification preferences for all members of the group, subgroups, and projects."
msgstr ""
@@ -18749,7 +18887,7 @@ msgid "GroupsNew|Create new group"
msgstr ""
msgid "GroupsNew|Create subgroup"
-msgstr ""
+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 ""
@@ -18878,13 +19016,13 @@ msgid "Groups|Save changes"
msgstr ""
msgid "Groups|Subgroup URL"
-msgstr ""
+msgstr "하위 그룹 URL"
msgid "Groups|Subgroup name"
-msgstr ""
+msgstr "하위 그룹 ì´ë¦„"
msgid "Groups|Subgroup slug"
-msgstr ""
+msgstr "하위 그룹 슬러그"
msgid "Guideline"
msgstr "ê°€ì´ë“œë¼ì¸"
@@ -18892,14 +19030,17 @@ msgstr "ê°€ì´ë“œë¼ì¸"
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr "HAR íŒŒì¼ URL"
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
-msgstr "HTTP Basic: ì ‘ê·¼ 거부\\nGit over HTTPì—서는 'api' 스코프를 가진 ê°œì¸ ì—‘ì„¸ìŠ¤ 토í°ì„ 사용해야 합니다.\\n%{profile_personal_access_tokens_url}ì—ì„œ ìƒì„±í•  수 있습니다."
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
+msgstr ""
msgid "Harbor Registry"
msgstr ""
@@ -18937,15 +19078,15 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -18957,43 +19098,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19002,6 +19137,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19060,7 +19198,7 @@ msgid "Health status"
msgstr ""
msgid "Health status cannot be edited because this issue is closed"
-msgstr ""
+msgstr "ì´ ì´ìŠˆê°€ 닫혀있으므로 ìƒíƒœë¥¼ 수정할 수 없습니다."
msgid "HealthCheck|Access token is"
msgstr "엑세스 토í°: "
@@ -19147,9 +19285,12 @@ msgid "Hide chart"
msgid_plural "Hide charts"
msgstr[0] ""
-msgid "Hide comments on this file"
+msgid "Hide comments"
msgstr ""
+msgid "Hide comments on this file"
+msgstr "ì´ íŒŒì¼ì˜ 댓글 숨기기"
+
msgid "Hide details"
msgstr ""
@@ -19175,7 +19316,7 @@ msgid "Hide shared projects"
msgstr ""
msgid "Hide thread"
-msgstr ""
+msgstr "스레드 숨기기"
msgid "Hide tooltips or popovers"
msgstr ""
@@ -19277,9 +19418,6 @@ msgstr "정리가 성공ì ìœ¼ë¡œ 시작ë˜ì—ˆìŠµë‹ˆë‹¤"
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19313,6 +19451,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr "%{terms_link}ì— ë™ì˜í•©ë‹ˆë‹¤."
@@ -19380,7 +19521,7 @@ msgid "IDE|Review"
msgstr "리뷰"
msgid "IDE|Start a new merge request"
-msgstr ""
+msgstr "새 머지 리퀘스트 만들기"
msgid "IDE|Successful commit"
msgstr ""
@@ -19436,9 +19577,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19451,15 +19598,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19481,6 +19649,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19521,7 +19692,7 @@ msgid "If no options are selected, only administrators can register runners."
msgstr ""
msgid "If none of the options work, try contacting a GitLab administrator."
-msgstr ""
+msgstr "ì˜µì…˜ì´ ìž‘ë™í•˜ì§€ 않으면 GitLab 관리ìžì—게 문ì˜í•˜ì‹­ì‹œì˜¤."
msgid "If the number of active users exceeds the user limit, you will be charged for the number of %{users_over_license_link} at your next license reconciliation."
msgstr ""
@@ -19550,9 +19721,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19572,7 +19740,7 @@ msgid "If you get a lot of false alarms from repository checks, you can clear al
msgstr ""
msgid "If you lose your recovery codes you can generate new ones, invalidating all previous codes."
-msgstr ""
+msgstr "복구 코드를 분실한 경우 새 코드를 ìƒì„±í•˜ì—¬ ì´ì „ 코드를 ëª¨ë‘ ë¬´íš¨í™”í•  수 있습니다."
msgid "If you recently signed in and recognize the IP address, you may disregard this email."
msgstr ""
@@ -19808,9 +19976,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20457,7 +20622,7 @@ msgid "IncidentManagement|No incidents to display."
msgstr ""
msgid "IncidentManagement|None"
-msgstr ""
+msgstr "ì—†ìŒ"
msgid "IncidentManagement|Open"
msgstr ""
@@ -20600,6 +20765,9 @@ msgstr "Incident|ì¸ì‹œë˜íŠ¸ 타임ë¼ì¸ ì´ë²¤íŠ¸ ìƒì„± 오류: %{error}"
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20618,6 +20786,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20651,7 +20822,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20697,13 +20868,13 @@ msgid "Inform users without uploaded SSH keys that they can't push over SSH unti
msgstr ""
msgid "Infrastructure"
-msgstr ""
+msgstr "ì¸í”„ë¼"
msgid "Infrastructure Registry"
-msgstr ""
+msgstr "ì¸í”„ë¼ ë ˆì§€ìŠ¤íŠ¸ë¦¬"
msgid "Infrastructure as Code (IaC) Scanning"
-msgstr ""
+msgstr "IaC(Infrastructure as Code) 스ìºë‹"
msgid "InfrastructureRegistry|Copy Terraform Command"
msgstr "Terraform 커맨드 복사"
@@ -20792,14 +20963,20 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr "í…Œì´ë¸” 삽입"
+
msgid "Insights"
+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)."
msgstr ""
msgid "Insights|This project is filtered out in the insights.yml file (see the projects.only config for more information)."
-msgstr ""
+msgstr "ì´ í”„ë¡œì íŠ¸ëŠ” insights.yml 파ì¼ì—ì„œ í•„í„°ë§ë©ë‹ˆë‹¤. (ìžì„¸í•œ ë‚´ìš©ì€ projects.only ì„¤ì •ì„ ì°¸ê³ í•˜ì„¸ìš”.)"
msgid "Install GitLab Runner and ensure it's running."
msgstr ""
@@ -20905,7 +21082,7 @@ msgid "Integrations|An error occurred while loading projects using custom settin
msgstr ""
msgid "Integrations|An event will be triggered when one of the following items happen."
-msgstr ""
+msgstr "ë‹¤ìŒ í•­ëª© 중 하나가 ë°œìƒí•˜ë©´ ì´ë²¤íŠ¸ê°€ 트리거ë©ë‹ˆë‹¤."
msgid "Integrations|Branches for which notifications are to be sent"
msgstr ""
@@ -21063,6 +21240,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21375,6 +21555,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr "InviteMembersModal|회ì›ì„ ì„ íƒí•˜ê±°ë‚˜ 초대할 ì´ë©”ì¼ ì£¼ì†Œë¥¼ 입력하십시오"
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21390,12 +21573,18 @@ msgstr "ì—­í•  ì„ íƒ"
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
msgid "InviteMembersModal|The following member couldn't be invited"
msgid_plural "InviteMembersModal|The following %d members couldn't be invited"
-msgstr[0] ""
+msgstr[0] "ë‹¤ìŒ %d ëª…ì˜ íšŒì›ì€ 초대할 수 없습니다"
msgid "InviteMembersModal|This feature is disabled until this group has space for more members."
msgstr ""
@@ -21406,9 +21595,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21433,9 +21619,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21553,12 +21736,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -21758,7 +21947,7 @@ msgid "Issues with label %{label}"
msgstr ""
msgid "Issues with no epic assigned"
-msgstr ""
+msgstr "ì—í”½ì´ í• ë‹¹ë˜ì§€ ì•Šì€ ì´ìŠˆ"
msgid "Issues, merge requests, pushes, and comments."
msgstr ""
@@ -21877,9 +22066,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -21925,6 +22111,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr "Iterations|ìžë™ ìŠ¤ì¼€ì¤„ë§ í™œì„±í™”"
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -21940,12 +22129,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22003,9 +22186,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22015,9 +22195,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22030,9 +22207,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22327,14 +22501,11 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
msgid "Job logs and artifacts"
-msgstr ""
+msgstr "ìž‘ì—… 로그 ë° ì•„í‹°íŒ©íŠ¸"
msgid "Job to create self-monitoring project is in progress"
msgstr ""
@@ -22763,8 +22934,8 @@ 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."
-msgstr "ë¼ë²¨ì€ ì´ìŠˆì™€ 머지 리퀘스트(MR)ì— ì‚¬ìš©í•  수 있습니다."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
+msgstr ""
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -22778,6 +22949,9 @@ msgstr "ë ˆì´ë¸” 승격"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr "언어"
@@ -22912,7 +23086,7 @@ msgid "Last year"
msgstr ""
msgid "LastCommit|authored"
-msgstr ""
+msgstr "작성ë¨"
msgid "LastPushEvent|You pushed to"
msgstr "푸쉬: "
@@ -22947,14 +23121,11 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
msgid "Learn More."
-msgstr ""
+msgstr "ë” ì•Œì•„ë³´ê¸°."
msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}"
msgstr ""
@@ -23001,6 +23172,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr "ì´ìŠˆì— 대해 ìžì„¸ížˆ 알아보세요."
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23017,7 +23191,7 @@ msgid "Learn more in the"
msgstr "ë” ìžì„¸ížˆ 알아보기"
msgid "Learn more."
-msgstr ""
+msgstr "ë” ì•Œì•„ë³´ê¸°."
msgid "LearnGitLab|%{percentage}%{percentSymbol} completed"
msgstr ""
@@ -23119,7 +23293,7 @@ msgid "LearnGitLab|Your team is growing! You've successfully invited new team me
msgstr ""
msgid "LearnGitlab|- Included in trial"
-msgstr ""
+msgstr "- í‰ê°€íŒì— í¬í•¨ë¨"
msgid "LearnGitlab|Contact your administrator to start a free Ultimate trial."
msgstr ""
@@ -23176,7 +23350,7 @@ msgid "License Compliance"
msgstr ""
msgid "License Compliance| Used by %{dependencies}"
-msgstr ""
+msgstr "%{dependencies}ì—ì„œ 사용ë¨"
msgid "License compliance"
msgstr ""
@@ -23716,7 +23890,7 @@ msgid "MRDiff|Show full file"
msgstr ""
msgid "Made this issue confidential."
-msgstr ""
+msgstr "ì´ ì´ìŠˆë¥¼ 비공개로 설정하였습니다."
msgid "Mailgun"
msgstr ""
@@ -23727,6 +23901,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -23740,7 +23917,7 @@ msgid "Make everyone on your team more productive regardless of their location.
msgstr ""
msgid "Make issue confidential"
-msgstr ""
+msgstr "ì´ìŠˆë¥¼ 비공개로 설정"
msgid "Make sure you have the correct permissions to link your project."
msgstr ""
@@ -23749,7 +23926,7 @@ msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
msgid "Makes this issue confidential."
-msgstr ""
+msgstr "ì´ ì´ìŠˆë¥¼ 비공개로 설정하기"
msgid "Manage %{workspace} labels"
msgstr ""
@@ -23845,7 +24022,7 @@ msgid "Mark this issue as related to another issue"
msgstr ""
msgid "Mark to do as done"
-msgstr ""
+msgstr "í•  ì¼ì„ 완료로 표시"
msgid "Markdown Help"
msgstr ""
@@ -23881,7 +24058,7 @@ msgid "MarkdownEditor|Add strikethrough text (%{modifier_key}⇧X)"
msgstr ""
msgid "MarkdownToolbar|Supports %{markdownDocsLinkStart}Markdown%{markdownDocsLinkEnd}"
-msgstr ""
+msgstr "%{markdownDocsLinkStart}마í¬ë‹¤ìš´%{markdownDocsLinkEnd} 지ì›"
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
@@ -23905,7 +24082,7 @@ msgid "Marked this issue as related to %{issue_ref}."
msgstr ""
msgid "Marked to do as done."
-msgstr ""
+msgstr "í•  ì¼ì„ 완료로 표시했습니다."
msgid "Marks this %{noun} as a draft."
msgstr ""
@@ -23920,7 +24097,7 @@ msgid "Marks this issue as related to %{issue_ref}."
msgstr ""
msgid "Marks to do as done."
-msgstr ""
+msgstr "í•  ì¼ì„ 완료로 표시합니다."
msgid "Mask variable"
msgstr ""
@@ -24015,10 +24192,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24103,7 +24277,7 @@ msgid "Maximum import size (MB)"
msgstr ""
msgid "Maximum job artifact size"
-msgstr ""
+msgstr "최대 ìž‘ì—… 아티팩트 í¬ê¸°"
msgid "Maximum job timeout"
msgstr "최대 작업 시간 초과"
@@ -24288,7 +24462,7 @@ msgid "Members|%{userName} is currently an LDAP user. Editing their permissions
msgstr ""
msgid "Members|2FA"
-msgstr ""
+msgstr "2FA"
msgid "Members|An error occurred while trying to enable LDAP override, please try again."
msgstr ""
@@ -24414,7 +24588,7 @@ msgid "Merge Requests in Review"
msgstr "검토 ì¤‘ì¸ ë¨¸ì§€ 리퀘스트"
msgid "Merge Requests merged"
-msgstr ""
+msgstr "ë¨¸ì§€ëœ ë¨¸ì§€ 리퀘스트"
msgid "Merge automatically (%{strategy})"
msgstr ""
@@ -24483,7 +24657,7 @@ msgid "Merge request events"
msgstr ""
msgid "Merge request locked."
-msgstr ""
+msgstr "잠긴 머지 리퀘스트"
msgid "Merge request not merged"
msgstr ""
@@ -24492,7 +24666,7 @@ msgid "Merge request reports"
msgstr ""
msgid "Merge request unlocked."
-msgstr ""
+msgstr "머지 리퀘스트가 잠금 í•´ì œë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "Merge request was scheduled to merge after pipeline succeeds"
msgstr ""
@@ -24500,6 +24674,9 @@ msgstr ""
msgid "Merge requests"
msgstr "머지 리퀘스트(MR)"
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr "머지 리퀘스트(MR)는 프로ì íŠ¸ì˜ 변경 ì‚¬í•­ì„ ì œì•ˆí•˜ê³  변경 ì‚¬í•­ì„ ë‹¤ë¥¸ 사람들과 ë…¼ì˜ í•˜ëŠ” 곳입니다."
@@ -24587,6 +24764,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr "댓글 저장 실패"
@@ -24662,6 +24842,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr "MergeTopics|%{sourceTopic} ì´ ì œê±°ë©ë‹ˆë‹¤"
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr "MergeTopics|í• ë‹¹ëœ ëª¨ë“  프로ì íŠ¸ëŠ” %{targetTopic}으로 ì´ë™ë©ë‹ˆë‹¤."
+
+msgid "MergeTopics|Merge topics"
+msgstr "MergeTopics|주제 병합"
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr "MergeTopics|주제를 병합하면 다ìŒì´ ë°œìƒí•©ë‹ˆë‹¤."
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr "MergeTopics|í• ë‹¹ëœ ëª¨ë“  프로ì íŠ¸ë¥¼ ì›ë³¸ 주제ì—ì„œ ëŒ€ìƒ ì£¼ì œë¡œ ì´ë™í•˜ê³  ì›ë³¸ 주제를 제거합니다."
+
+msgid "MergeTopics|Source topic"
+msgstr "MergeTopics|소스 주제"
+
+msgid "MergeTopics|Target topic"
+msgstr "MergeTopics|ëŒ€ìƒ ì£¼ì œ"
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr "MergeTopics|ì´ ìž‘ì—…ì€ ì·¨ì†Œí•  수 없습니다."
+
msgid "Merged"
msgstr "머지ë¨"
@@ -24687,7 +24891,7 @@ msgid "Merging immediately isn't recommended as it may negatively impact the exi
msgstr ""
msgid "Mermaid diagram"
-msgstr ""
+msgstr "머메ì´ë“œ 다ì´ì–´ê·¸ëž¨"
msgid "Message"
msgstr ""
@@ -25151,7 +25355,7 @@ msgid "MilestoneSidebar|No start date"
msgstr ""
msgid "MilestoneSidebar|None"
-msgstr ""
+msgstr "ì—†ìŒ"
msgid "MilestoneSidebar|Open:"
msgstr ""
@@ -25184,7 +25388,7 @@ msgid "Milestones|Close Milestone"
msgstr ""
msgid "Milestones|Completed Issues (closed)"
-msgstr ""
+msgstr "ì™„ë£Œëœ ì´ìŠˆ (닫힘)"
msgid "Milestones|Create a milestone to better track your issues and merge requests. %{learn_more_link}"
msgstr ""
@@ -25316,7 +25520,7 @@ msgid "MissingSSHKeyWarningLink|You won't be able to pull or push repositories v
msgstr ""
msgid "ModalButton|Add projects"
-msgstr ""
+msgstr "프로ì íŠ¸ 추가"
msgid "Modal|Close"
msgstr "닫기"
@@ -25336,16 +25540,19 @@ msgstr "커밋 메시지 수정"
msgid "Modify merge commit"
msgstr "머지 커밋 수정"
+msgid "Mon"
+msgstr "ì›”"
+
msgid "Monday"
msgstr "월요ì¼"
msgid "Monitor"
-msgstr ""
+msgstr "모니터ë§"
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25360,6 +25567,9 @@ msgstr ""
msgid "Months"
msgstr "달"
+msgid "More"
+msgstr "ë” ë³´ê¸°"
+
msgid "More Details"
msgstr ""
@@ -25402,9 +25612,6 @@ msgstr ""
msgid "Most stars"
msgstr "ë§Žì€ ë³„"
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr "ì´ë™"
@@ -25454,10 +25661,10 @@ msgid "Moves this issue to %{path_to_project}."
msgstr ""
msgid "MrDeploymentActions|Deploy"
-msgstr ""
+msgstr "ë°°í¬"
msgid "MrDeploymentActions|Re-deploy"
-msgstr ""
+msgstr "재배í¬"
msgid "MrDeploymentActions|Stop environment"
msgstr ""
@@ -25472,16 +25679,16 @@ msgid "MrSurvey|By continuing, you acknowledge that responses will be used to im
msgstr ""
msgid "MrSurvey|How satisfied are you with %{strongStart}speed/performance%{strongEnd} of merge requests?"
-msgstr ""
+msgstr "머지 ë¦¬í€˜ìŠ¤íŠ¸ì˜ %{strongStart}ì†ë„/성능%{strongEnd}ì— ì–¼ë§ˆë‚˜ 만족하십니까?"
msgid "MrSurvey|Merge request experience survey"
-msgstr ""
+msgstr "머지 리퀘스트 사용 경험 설문조사"
msgid "MrSurvey|Overall, how satisfied are you with merge requests?"
-msgstr ""
+msgstr "ì „ì²´ì ìœ¼ë¡œ 머지 ë¦¬í€˜ìŠ¤íŠ¸ì— ëŒ€í•´ 얼마나 만족하십니까?"
msgid "MrSurvey|Thank you for your feedback!"
-msgstr ""
+msgstr "소중한 ì˜ê²¬ ê°ì‚¬í•©ë‹ˆë‹¤!"
msgid "Multi-project"
msgstr ""
@@ -25495,12 +25702,6 @@ msgstr "여러 IP 주소 범위가 지ì›ë©ë‹ˆë‹¤. 그룹 ì„¤ì •ì— ëŒ€í•œ ì•¡ì
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -25632,7 +25833,7 @@ msgid "NavigationTheme|Dark Mode (alpha)"
msgstr ""
msgid "NavigationTheme|Gray"
-msgstr ""
+msgstr "회색"
msgid "NavigationTheme|Green"
msgstr ""
@@ -25644,7 +25845,7 @@ msgid "NavigationTheme|Light Blue"
msgstr ""
msgid "NavigationTheme|Light Gray"
-msgstr ""
+msgstr "ë°ì€ 회색"
msgid "NavigationTheme|Light Green"
msgstr ""
@@ -25768,10 +25969,10 @@ msgid "New branch unavailable"
msgstr "새로운 브랜치 사용 불가능"
msgid "New confidential epic title "
-msgstr ""
+msgstr "새로운 비공개 ì—픽 제목"
msgid "New confidential issue title"
-msgstr ""
+msgstr "새 비공개 ì´ìŠˆ 제목"
msgid "New deploy key"
msgstr ""
@@ -25861,7 +26062,7 @@ msgid "New related %{issueType}"
msgstr ""
msgid "New release"
-msgstr ""
+msgstr "새 릴리스"
msgid "New requirement"
msgstr ""
@@ -25942,7 +26143,7 @@ msgid "No Epic"
msgstr "ì—픽 ì—†ìŒ"
msgid "No Google Cloud projects - You need at least one Google Cloud project"
-msgstr "Google Cloud 프로ì íŠ¸ ì—†ìŒ - Google Cloud 프로ì íŠ¸ê°€ 하나 ì´ìƒ 필요합니다."
+msgstr "구글 í´ë¼ìš°ë“œ 프로ì íŠ¸ ì—†ìŒ - 구글 í´ë¼ìš°ë“œ 프로ì íŠ¸ê°€ 하나 ì´ìƒ 필요합니다."
msgid "No Matching Results"
msgstr ""
@@ -25969,7 +26170,7 @@ msgid "No approvers"
msgstr ""
msgid "No artifacts found"
-msgstr ""
+msgstr "ë°œê²¬ëœ ì•„í‹°íŒ©íŠ¸ê°€ 없습니다."
msgid "No assignee"
msgstr ""
@@ -26022,6 +26223,9 @@ msgstr ""
msgid "No credit card required."
msgstr "신용 카드가 필요하지 않습니다."
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26034,9 +26238,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr "기한 ì—†ìŒ"
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26086,7 +26287,7 @@ msgid "No job log"
msgstr ""
msgid "No jobs to show"
-msgstr ""
+msgstr "표시할 ìž‘ì—…ì´ ì—†ìŠµë‹ˆë‹¤."
msgid "No label"
msgstr "ë¼ë²¨ ì—†ìŒ"
@@ -26187,6 +26388,9 @@ msgstr "저장소 ì—†ìŒ"
msgid "No results"
msgstr "ê²°ê³¼ ì—†ìŒ"
+msgid "No results found"
+msgstr "검색 결과가 없습니다"
+
msgid "No runner executable"
msgstr ""
@@ -26208,9 +26412,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr "ì‹œìž‘ì¼ ì—†ìŒ"
-
msgid "No suggestions found"
msgstr ""
@@ -26267,7 +26468,7 @@ msgid "None"
msgstr "ì—†ìŒ"
msgid "None of the group milestones have the same project as the release"
-msgstr ""
+msgstr "그룹 마ì¼ìŠ¤í†¤ 중 릴리스와 ë™ì¼í•œ 프로ì íŠ¸ê°€ 없습니다."
msgid "Normal text"
msgstr ""
@@ -26294,7 +26495,7 @@ msgid "Not available for protected branches"
msgstr "프로í…íŠ¸ëœ ë¸Œëžœì¹˜ì—는 사용할 수 없습니다."
msgid "Not confidential"
-msgstr "비밀 아님"
+msgstr "공개ë¨"
msgid "Not found"
msgstr ""
@@ -26345,7 +26546,7 @@ msgid "NoteForm|Note"
msgstr ""
msgid "NoteToolbar|Supports %{markdownDocsLinkStart}Markdown%{markdownDocsLinkEnd}. For %{quickActionsDocsLinkStart}quick actions%{quickActionsDocsLinkEnd}, type %{keyboardStart}/%{keyboardEnd}."
-msgstr ""
+msgstr "%{markdownDocsLinkStart}마í¬ë‹¤ìš´%{markdownDocsLinkEnd} 지ì›. %{quickActionsDocsLinkStart}퀵 ì•¡ì…˜%{quickActionsDocsLinkEnd}ì„ ì‚¬ìš©í•˜ë ¤ë©´ %{keyboardStart}/%{keyboardEnd}를 입력하세요."
msgid "Notes"
msgstr ""
@@ -26366,7 +26567,7 @@ msgid "Notes|Expand replies"
msgstr ""
msgid "Notes|Internal notes are only visible to the author, assignees, and members with the role of Reporter or higher"
-msgstr ""
+msgstr "내부 메모는 작성ìž, ë‹´ë‹¹ìž ë° ë¦¬í¬í„° ì´ìƒì˜ ì—­í• ì„ ê°€ì§„ 구성ì›ë§Œ ë³¼ 수 있습니다."
msgid "Notes|Last reply by %{name}"
msgstr ""
@@ -26387,7 +26588,7 @@ msgid "Notes|This comment has changed since you started editing, please review t
msgstr ""
msgid "Notes|This internal note will always remain confidential"
-msgstr ""
+msgstr "ì´ ë‚´ë¶€ 메모는 í•­ìƒ ë¹„ê³µê°œë¡œ 유지ë©ë‹ˆë‹¤."
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 ""
@@ -26396,6 +26597,9 @@ msgid "Note|The created date provided is too far in the past."
msgstr ""
msgid "Nothing to preview."
+msgstr "미리 ë³¼ ë‚´ìš©ì´ ì—†ìŠµë‹ˆë‹¤."
+
+msgid "Notification Email"
msgstr ""
msgid "Notification events"
@@ -26463,7 +26667,7 @@ msgid "NotificationEvent|New note"
msgstr "새 노트"
msgid "NotificationEvent|New release"
-msgstr ""
+msgstr "새 릴리스"
msgid "NotificationEvent|Push to merge request"
msgstr ""
@@ -26514,10 +26718,10 @@ msgid "Notifications on"
msgstr "알림 켬"
msgid "Notifications turned off."
-msgstr ""
+msgstr "알림 사용 중지ë¨"
msgid "Notifications turned on."
-msgstr ""
+msgstr "알림 사용 설정ë¨"
msgid "Notify users by email when sign-in location is not recognized."
msgstr ""
@@ -26528,6 +26732,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26537,12 +26744,30 @@ msgstr "Notify|%{invited_user} ì€ %{highlight_start}ì—ì„œ %{highlight_end} ì„
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
-msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
msgstr ""
-msgid "Notify|Assignee changed to %{toNames}"
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr "Notify|%{paragraph_start}안녕하세요 %{name}!%{paragraph_end} %{paragraph_start}새 공개 키가 ê·€í•˜ì˜ ê³„ì •ì— ì¶”ê°€ë˜ì—ˆìŠµë‹ˆë‹¤:%{paragraph_end} %{paragraph_start}제목: %{key_title}%{paragraph_end} %{paragraph_start}ì´ í‚¤ê°€ 실수로 ì¶”ê°€ëœ ê²½ìš° 아래 %{removal_link}ì—ì„œ 제거할 수 있습니다.%{paragraph_end}"
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
msgstr ""
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
+msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
+msgstr "담당ìžê°€ %{fromNames} ì—ì„œ %{toNames}ë¡œ 변경ë¨"
+
+msgid "Notify|Assignee changed to %{toNames}"
+msgstr "담당ìžê°€ %{toNames}ë¡œ 변경ë¨"
+
msgid "Notify|Author: %{author_name}"
msgstr ""
@@ -26550,6 +26775,18 @@ msgid "Notify|Auto DevOps pipeline was disabled for %{project}"
msgstr ""
msgid "Notify|CI/CD project settings"
+msgstr "CI/CD 프로ì íŠ¸ 설정"
+
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
msgstr ""
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
@@ -26562,7 +26799,7 @@ msgid "Notify|Issue was moved to another project."
msgstr ""
msgid "Notify|Learn more about Auto DevOps"
-msgstr ""
+msgstr "Auto DevOpsì— ëŒ€í•´ ë” ì•Œì•„ë³´ê¸°"
msgid "Notify|Merge request %{merge_request} can no longer be merged due to conflict."
msgstr ""
@@ -26582,9 +26819,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26592,7 +26838,7 @@ msgid "Notify|Pipeline %{pipeline_link} triggered by"
msgstr ""
msgid "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}."
-msgstr ""
+msgstr "ìžë™ DevOps 파ì´í”„ë¼ì¸ì€ 파ì´í”„ë¼ì¸ %{pipeline_link} ì— ëŒ€í•´ 실패했으며 %{project_link}ì— ëŒ€í•´ 비활성화ë˜ì—ˆìŠµë‹ˆë‹¤. 프로ì íŠ¸ì—ì„œ Auto DevOps 파ì´í”„ë¼ì¸ì„ 사용하려면 %{supported_langs_link}를 검토하고 ê·¸ì— ë”°ë¼ í”„ë¡œì íŠ¸ë¥¼ ì¡°ì •í•œ ë‹¤ìŒ %{settings_link}ë‚´ì—ì„œ Auto DevOps 파ì´í”„ë¼ì¸ì„ 켜십시오."
msgid "Notify|This issue is due on: %{issue_due_date}"
msgstr ""
@@ -26603,9 +26849,15 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
-msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
+msgid "Notify|You have been mentioned in an issue."
msgstr ""
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr "Notify|ë‹¹ì‹ ì´ ë¨¸ì§€ 리퀘스트 %{mr_link}ì—ì„œ 언급ë˜ì—ˆìŠµë‹ˆë‹¤"
+
+msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
+msgstr "%{target_to_join}ì˜ %{target_type} 가입 ìš”ì²­ì´ %{denied_tag} ë˜ì—ˆìŠµë‹ˆë‹¤."
+
msgid "Notify|currently supported languages"
msgstr ""
@@ -26706,6 +26958,9 @@ msgid "On %{end_date}, your trial will end and %{namespace_name} will be limited
msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be limited to %{free_user_limit} members"
msgstr[0] "%{end_date}ì— í‰ê°€íŒì´ 종료ë˜ê³  %{namespace_name} ì€ %{free_user_limit} 명으로 제한ë©ë‹ˆë‹¤."
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -26925,6 +27180,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr "OnDemandScans|변경 사항 취소"
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr "OnDemandScans|변경 ì‚¬í•­ì„ ì·¨ì†Œí•˜ì‹œê² ìŠµë‹ˆê¹Œ, 아니면 ì´ í”„ë¡œí•„ì„ ê³„ì† íŽ¸ì§‘í•˜ì‹œê² ìŠµë‹ˆê¹Œ? 저장하지 ì•Šì€ ë³€ê²½ ì‚¬í•­ì€ ì†ì‹¤ë©ë‹ˆë‹¤."
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -26946,6 +27207,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr "OnDemandScans|ê³„ì† íŽ¸ì§‘"
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27069,6 +27333,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr "OnDemandScans|저장ë˜ì§€ ì•Šì€ ë³€ê²½ ì‚¬í•­ì´ ìžˆìŠµë‹ˆë‹¤"
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27125,7 +27392,7 @@ msgid "Only 1 appearances row can exist"
msgstr ""
msgid "Only HTTP(S)"
-msgstr ""
+msgstr "HTTP(S)만"
msgid "Only Issue ID or merge request ID is required"
msgstr ""
@@ -27167,7 +27434,7 @@ msgid "Only project members can comment."
msgstr "프로ì íŠ¸ 구성ì›ë§Œ ëŒ“ê¸€ì„ ë‹¬ 수 있습니다."
msgid "Only projects created under a Ultimate license are available in Security Dashboards."
-msgstr ""
+msgstr "Ultimate ë¼ì´ì„ ìŠ¤ë¡œ ìƒì„±ëœ 프로ì íŠ¸ë§Œ 보안 대시보드ì—ì„œ 사용할 수 있습니다."
msgid "Only reCAPTCHA v2 is supported:"
msgstr "reCAPTCHA v2만 지ì›ë©ë‹ˆë‹¤:"
@@ -27197,7 +27464,7 @@ msgid "Open errors"
msgstr ""
msgid "Open in Gitpod"
-msgstr ""
+msgstr "Gitpodì—ì„œ 열기"
msgid "Open in Web IDE"
msgstr "웹 IDEì—ì„œ 열기"
@@ -27223,9 +27490,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr "OpenAPI 스펙 íŒŒì¼ URL"
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr "OpenSearchì˜ ì§€ì—­."
+
msgid "Opened"
msgstr "열림"
@@ -27254,16 +27527,16 @@ msgid "Operation timed out. Check pod logs for %{pod_name} for more details."
msgstr ""
msgid "Operations"
-msgstr ""
+msgstr "ìš´ì˜"
msgid "Operations Dashboard"
-msgstr "작업 대시보드"
+msgstr "ìš´ì˜ ëŒ€ì‹œë³´ë“œ"
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr "ëŒ€ì‹œë³´ë“œì— í”„ë¡œì íŠ¸ 추가"
msgid "OperationsDashboard|Add projects"
-msgstr ""
+msgstr "프로ì íŠ¸ 추가"
msgid "OperationsDashboard|More information"
msgstr ""
@@ -27272,10 +27545,10 @@ msgid "OperationsDashboard|Operations Dashboard"
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 "OperationsDashboard|ìš´ì˜ ë° í™˜ê²½ 대시보드는 ë™ì¼í•œ 프로ì íŠ¸ 목ë¡ì„ 공유합니다. í•œ 프로ì íŠ¸ì—ì„œ 프로ì íŠ¸ë¥¼ 추가하거나 제거하면 GitLabì€ ë‹¤ë¥¸ 프로ì íŠ¸ì—ì„œ 프로ì íŠ¸ë¥¼ 추가하거나 제거합니다. %{linkStart}추가 ì •ë³´%{linkEnd}"
+msgstr "ìš´ì˜ ë° í™˜ê²½ 대시보드는 ë™ì¼í•œ 프로ì íŠ¸ 목ë¡ì„ 공유합니다. í•œ 프로ì íŠ¸ì—ì„œ 프로ì íŠ¸ë¥¼ 추가하거나 제거하면 GitLabì€ ë‹¤ë¥¸ 프로ì íŠ¸ì—ì„œ 프로ì íŠ¸ë¥¼ 추가하거나 제거합니다. %{linkStart}추가 ì •ë³´%{linkEnd}"
msgid "OperationsDashboard|The operations dashboard provides a summary of each project's operational health, including pipeline and alert statuses."
-msgstr "ìž‘ì—… 대시보드는 파ì´í”„ë¼ì¸ê³¼ 경고 ìƒíƒœë¥¼ í¬í•¨í•˜ì—¬ ê° í”„ë¡œì íŠ¸ì˜ ìš´ì˜ ìƒíƒœì— 대한 ìš”ì•½ì„ ì œê³µí•©ë‹ˆë‹¤."
+msgstr "ìš´ì˜ ëŒ€ì‹œë³´ë“œëŠ” 파ì´í”„ë¼ì¸ê³¼ 경고 ìƒíƒœë¥¼ í¬í•¨í•˜ì—¬ ê° í”„ë¡œì íŠ¸ì˜ ìš´ì˜ ìƒíƒœì— 대한 ìš”ì•½ì„ ì œê³µí•©ë‹ˆë‹¤."
msgid "Opstrace endpoint for Error Tracking integration"
msgstr ""
@@ -27439,6 +27712,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27475,6 +27751,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27499,6 +27778,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27716,6 +27998,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27747,9 +28032,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27759,12 +28041,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -27865,8 +28141,8 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
-msgstr "패키지 & 레지스트리"
+msgid "Packages and registries"
+msgstr ""
msgid "Page not found"
msgstr ""
@@ -28019,10 +28295,10 @@ msgid "Paste a public key here. %{link_start}How do I generate it?%{link_end}"
msgstr ""
msgid "Paste confidential epic link"
-msgstr ""
+msgstr "비공개 ì—픽 ë§í¬ 붙여넣기"
msgid "Paste confidential issue link"
-msgstr ""
+msgstr "비공개 ì´ìŠˆ ë§í¬ 붙여넣기"
msgid "Paste epic link"
msgstr ""
@@ -28031,7 +28307,7 @@ msgid "Paste issue link"
msgstr ""
msgid "Paste link"
-msgstr ""
+msgstr "ë§í¬ 붙여넣기"
msgid "Paste project path (i.e. gitlab-org/gitlab)"
msgstr ""
@@ -28114,9 +28390,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr "성능 개선 ë„우미"
-
msgid "Performance optimization"
msgstr "성능 최ì í™”"
@@ -28192,6 +28465,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -28391,19 +28670,19 @@ msgid "PipelineEditorTutorial|🚀 Run your first pipeline"
msgstr ""
msgid "PipelineEditor|Configuration content has changed. Re-run validation for updated results."
-msgstr ""
+msgstr "설정 ë‚´ìš©ì´ ë³€ê²½ë˜ì—ˆìŠµë‹ˆë‹¤. ì—…ë°ì´íŠ¸ëœ ê²°ê³¼ì— ëŒ€í•œ 유효성 검사를 다시 실행합니다."
msgid "PipelineEditor|Current content in the Edit tab will be used for the simulation."
-msgstr ""
+msgstr "편집 íƒ­ì˜ í˜„ìž¬ 콘í…츠가 시뮬레ì´ì…˜ì— 사용ë©ë‹ˆë‹¤."
msgid "PipelineEditor|Git push event to the default branch"
-msgstr ""
+msgstr "기본 ë¸Œëžœì¹˜ì— ëŒ€í•œ Git push ì´ë²¤íŠ¸"
msgid "PipelineEditor|Other pipeline sources are not available yet."
-msgstr ""
+msgstr "다른 파ì´í”„ë¼ì¸ 소스는 ì•„ì§ ì‚¬ìš©í•  수 없습니다."
msgid "PipelineEditor|Pipeline Source"
-msgstr ""
+msgstr "파ì´í”„ë¼ì¸ 소스"
msgid "PipelineEditor|Pipeline behavior will be simulated including the %{codeStart}rules%{codeEnd} %{codeStart}only%{codeEnd} %{codeStart}except%{codeEnd} and %{codeStart}needs%{codeEnd} job dependencies."
msgstr ""
@@ -28418,7 +28697,7 @@ msgid "PipelineEditor|Simulated a %{codeStart}git push%{codeEnd} event for a def
msgstr ""
msgid "PipelineEditor|Simulation completed successfully"
-msgstr ""
+msgstr "시뮬레ì´ì…˜ì´ 성공ì ìœ¼ë¡œ 완료ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "PipelineEditor|The CI/CD configuration is continuously validated. Errors and warnings are displayed when the CI/CD configuration file is not empty."
msgstr ""
@@ -28433,19 +28712,19 @@ 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 ""
+msgstr "ì„ íƒí•œ ì¡°ê±´ì—ì„œ 파ì´í”„ë¼ì¸ 유효성 ê²€ì¦"
msgid "PipelineEditor|Validate pipeline under simulated conditions"
-msgstr ""
+msgstr "시뮬레ì´ì…˜ëœ ì¡°ê±´ì—ì„œ 파ì´í”„ë¼ì¸ 유효성 ê²€ì¦"
msgid "PipelineEditor|Validating pipeline... It can take up to a minute."
-msgstr ""
+msgstr "파ì´í”„ë¼ì¸ 유효성 검사 중... 최대 1ë¶„ì´ ì†Œìš”ë  ìˆ˜ 있습니다."
msgid "PipelineEditor|Waiting for CI content to load..."
-msgstr ""
+msgstr "CI 콘í…츠가 로드ë˜ê¸°ë¥¼ 기다리는 중..."
msgid "PipelineScheduleIntervalPattern|Custom (%{linkStart}Cron syntax%{linkEnd})"
msgstr ""
@@ -28709,7 +28988,7 @@ msgid "Pipelines|Loading pipelines"
msgstr ""
msgid "Pipelines|Merged YAML is view only"
-msgstr ""
+msgstr "ë¨¸ì§€ëœ YAMLì€ ë³´ê¸° 전용입니다."
msgid "Pipelines|More Information"
msgstr ""
@@ -28757,7 +29036,7 @@ msgid "Pipelines|The %{namespace_name} namespace has %{percentage}%% or less Sha
msgstr ""
msgid "Pipelines|The %{namespace_name} namespace has exceeded its pipeline minutes quota. Buy additional pipeline minutes, or no new jobs or pipelines in its projects will run."
-msgstr ""
+msgstr "%{namespace_name} 네임스페ì´ìŠ¤ê°€ 파ì´í”„ë¼ì¸ í• ë‹¹ëŸ‰ì„ ì´ˆê³¼í–ˆìŠµë‹ˆë‹¤. 추가 파ì´í”„ë¼ì¸ 실행시간(분)ì„ êµ¬ë§¤í•˜ì§€ 않으면 프로ì íŠ¸ì˜ 새 ìž‘ì—…ì´ë‚˜ 파ì´í”„ë¼ì¸ì´ 실행ë˜ì§€ 않습니다."
msgid "Pipelines|The CI configuration was not loaded, please try again."
msgstr ""
@@ -28829,7 +29108,7 @@ msgid "Pipelines|Validating GitLab CI configuration…"
msgstr ""
msgid "Pipelines|View merged YAML"
-msgstr ""
+msgstr "ë¨¸ì§€ëœ YAML 보기"
msgid "Pipelines|Visualize"
msgstr ""
@@ -28897,27 +29176,18 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr "Pipeline|가장 ëŠë¦° 5ê°œì˜ ìž‘ì—…"
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr "Pipeline|마지막으로 ì‹¤í–‰ëœ ìž‘ì—…"
-
-msgid "Pipeline|Longest queued job"
-msgstr "Pipeline|ëŒ€ê¸°ì‹œê°„ì´ ê°€ìž¥ 긴 ìž‘ì—…"
-
msgid "Pipeline|Manual"
msgstr ""
+msgid "Pipeline|Merge request pipeline"
+msgstr ""
+
msgid "Pipeline|Merge train pipeline"
msgstr ""
@@ -28927,18 +29197,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr "Pipeline|ì²˜ìŒ 100ê°œì˜ ê²°ê³¼ë§Œ 표시할 수 있ìŒ"
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr "Pipeline|성능개선 ë„우미"
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -28996,12 +29260,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr "Pipeline|마지막으로 ì‹¤í–‰ëœ ìž‘ì—…ì€ íŒŒì´í”„ë¼ì¸ì—ì„œ 시작할 마지막 작업입니다."
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr "Pipelines|대기 ì‹œê°„ì´ ê°€ìž¥ 긴 ìž‘ì—…ì€ ëŒ€ê¸° ìƒíƒœì—ì„œ 가장 오랜 ì‹œê°„ì„ ë³´ë‚¸ 작업으로 ëŸ¬ë„ˆì— ì˜í•´ ì„ íƒë˜ê¸°ë¥¼ 기다리고 있습니다."
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29030,13 +29288,10 @@ msgid "Pipeline|Variables"
msgstr "변수"
msgid "Pipeline|View commit"
-msgstr ""
-
-msgid "Pipeline|View dependency"
-msgstr "Pipeline|종ì†ì„± 보기"
+msgstr "커밋 보기"
msgid "Pipeline|View pipeline"
-msgstr ""
+msgstr "파ì´í”„ë¼ì¸ 보기"
msgid "Pipeline|We are currently unable to fetch pipeline data"
msgstr ""
@@ -29134,8 +29389,8 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
-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 your profile with email address"
msgstr ""
@@ -29350,6 +29605,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr "Postman 컬렉션 íŒŒì¼ URL"
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29371,10 +29629,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29398,6 +29656,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29419,9 +29680,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr "Gitpod"
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -29525,7 +29783,7 @@ msgid "Previous Artifacts"
msgstr ""
msgid "Previous commit"
-msgstr ""
+msgstr "ì´ì „ 커밋"
msgid "Previous design"
msgstr ""
@@ -29561,19 +29819,19 @@ msgid "Private"
msgstr ""
msgid "Private - Guest users are not allowed to view detailed release information like title and source code."
-msgstr ""
+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 ""
msgid "Private - The group and its projects can only be viewed by members."
-msgstr "Private - 그룹과 ê·¸ë£¹ì˜ í”„ë¡œì íŠ¸ë“¤ì€ ë©¤ë²„ë“¤ì´ ì—´ëžŒí•  수 있습니다."
+msgstr "Private - 그룹과 ê·¸ë£¹ì˜ í”„ë¡œì íŠ¸ë“¤ì€ 구성ì›ë“¤ë§Œ 열람할 수 있습니다."
msgid "Private group(s)"
msgstr ""
msgid "Private profile"
-msgstr ""
+msgstr "비공개 프로필"
msgid "Private projects Minutes cost factor"
msgstr ""
@@ -29711,7 +29969,7 @@ msgid "Profiles|Bio"
msgstr ""
msgid "Profiles|Busy"
-msgstr ""
+msgstr "ë°”ì¨"
msgid "Profiles|Change username"
msgstr "사용ìžëª… 변경"
@@ -29833,9 +30091,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr "키"
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -29866,6 +30121,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr "알림 ì´ë©”ì¼"
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr "ì¡°ì§"
@@ -29888,10 +30146,10 @@ msgid "Profiles|Pronouns"
msgstr ""
msgid "Profiles|Pronunciation"
-msgstr ""
+msgstr "ë°œìŒ"
msgid "Profiles|Public avatar"
-msgstr ""
+msgstr "공개 아바타"
msgid "Profiles|Public email"
msgstr "공개 ì´ë©”ì¼"
@@ -29912,7 +30170,7 @@ msgid "Profiles|Set new profile picture"
msgstr "새 프로필 사진 설정"
msgid "Profiles|Set your local time zone."
-msgstr ""
+msgstr "현지 시간대를 설정합니다."
msgid "Profiles|Some options are unavailable for LDAP accounts"
msgstr "ì¼ë¶€ ì˜µì…˜ì€ LDAP 계정ì—ì„œ 사용할 수 없습니다."
@@ -29939,7 +30197,7 @@ msgid "Profiles|This emoji and message will appear on your profile and throughou
msgstr "ì´ ê·¸ë¦¼ê³¼ 메시지는 프로필과 ì¸í„°íŽ˜ì´ìŠ¤ ì „ì²´ì— ë‚˜íƒ€ë‚©ë‹ˆë‹¤."
msgid "Profiles|This information will appear on your profile."
-msgstr ""
+msgstr "ì´ ì •ë³´ëŠ” í”„ë¡œí•„ì— í‘œì‹œë©ë‹ˆë‹¤."
msgid "Profiles|Time settings"
msgstr "시간 설정"
@@ -29984,7 +30242,7 @@ msgid "Profiles|What's your status?"
msgstr "ìƒíƒœëŠ” 어떤가요?"
msgid "Profiles|Who you represent or work for."
-msgstr ""
+msgstr "대표로 있는 ê³³ ë˜ëŠ” ì¼í•˜ëŠ” ê³³"
msgid "Profiles|You can change your avatar here"
msgstr "여기ì—ì„œ 아바타를 변경할 수 있습니다."
@@ -30089,7 +30347,7 @@ msgid "Project '%{project_name}' will be deleted on %{date}"
msgstr ""
msgid "Project Access Tokens"
-msgstr ""
+msgstr "프로ì íŠ¸ 액세스 토í°"
msgid "Project Badges"
msgstr "프로ì íŠ¸ 배지"
@@ -30113,7 +30371,7 @@ msgid "Project access token creation is disabled in this group. You can still us
msgstr ""
msgid "Project already deleted"
-msgstr ""
+msgstr "프로ì íŠ¸ê°€ ì´ë¯¸ ì‚­ì œë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "Project and wiki repositories"
msgstr ""
@@ -30173,7 +30431,7 @@ msgid "Project is required when cluster_type is :project"
msgstr ""
msgid "Project members"
-msgstr ""
+msgstr "프로ì íŠ¸ 구성ì›"
msgid "Project milestone"
msgstr "프로ì íŠ¸ 마ì¼ìŠ¤í†¤"
@@ -30209,7 +30467,7 @@ msgid "Project visibility level will be changed to match namespace rules when tr
msgstr ""
msgid "Project was not found or you do not have permission to add this project to Security Dashboards."
-msgstr ""
+msgstr "프로ì íŠ¸ë¥¼ ì°¾ì„ ìˆ˜ 없거나 ì´ í”„ë¡œì íŠ¸ë¥¼ 보안 ëŒ€ì‹œë³´ë“œì— ì¶”ê°€í•  수 있는 ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤."
msgid "Project: %{name}"
msgstr ""
@@ -30521,7 +30779,7 @@ msgid "ProjectSettings|Choose your merge method, options, checks, and squash opt
msgstr ""
msgid "ProjectSettings|Configure your project resources and monitor their health."
-msgstr ""
+msgstr "프로ì íŠ¸ 리소스를 설정하고 ìƒíƒœë¥¼ 모니터ë§í•©ë‹ˆë‹¤."
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr "ì´ ì„¤ì •ì„ ë³€ê²½í•˜ë ¤ë©´ 관리ìžì—게 문ì˜í•˜ì„¸ìš”."
@@ -30553,6 +30811,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30565,6 +30826,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30583,6 +30847,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30599,7 +30866,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 ""
@@ -30724,6 +30991,9 @@ msgstr "요구 사항"
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31016,7 +31286,7 @@ msgid "Projects with write access"
msgstr ""
msgid "ProjectsDropdown|Frequently visited"
-msgstr "ìžì£¼ 방문"
+msgstr "ìžì£¼ 방문하는 프로ì íŠ¸"
msgid "ProjectsDropdown|Loading projects"
msgstr "프로ì íŠ¸ 로딩"
@@ -31211,13 +31481,13 @@ msgid "PrometheusService|Waiting for your first deployment to an environment to
msgstr "공통 매트릭스를 찾기 위하여 첫 환경 ë°°í¬ë¥¼ 기다리는 중입니다"
msgid "PrometheusService|You have a cluster with the Prometheus integration enabled."
-msgstr ""
+msgstr "Prometheus ì¸í…Œê·¸ë ˆì´ì…˜ì´ í™œì„±í™”ëœ í´ëŸ¬ìŠ¤í„°ê°€ 있습니다."
msgid "PrometheusService|https://prometheus.example.com/"
-msgstr ""
+msgstr "https://prometheus.example.com/"
msgid "PrometheusService|{ \"type\": \"service_account\", \"project_id\": ... }"
-msgstr ""
+msgstr "{ \"type\": \"service_account\", \"project_id\": ... }"
msgid "Promote"
msgstr "승격"
@@ -31490,7 +31760,7 @@ msgid "ProtectedBranch|Keep stable branches secure and force developers to use m
msgstr ""
msgid "ProtectedBranch|Learn more."
-msgstr ""
+msgstr "ë” ì•Œì•„ë³´ê¸°."
msgid "ProtectedBranch|Protect"
msgstr ""
@@ -31534,8 +31804,8 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
-msgstr "ProtectedEnvironment|아래 ë°°í¬ ê³„ì¸µìœ¼ë¡œ ì§€ì •ëœ ëª¨ë“  í™˜ê²½ì€ ìƒìœ„ ê·¸ë£¹ì— ì˜í•´ 보호ë©ë‹ˆë‹¤. %{link_start}ìžì„¸ížˆ 알아보기%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
msgstr ""
@@ -31552,8 +31822,8 @@ msgstr "ProtectedEnvironment|ë³´í˜¸ëœ ì—…ìŠ¤íŠ¸ë¦¼ 환경"
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr "ProtectedEnvironment|ì´ ê·¸ë£¹ì— ëŒ€í•œ 세부 정보를 로드하지 못했습니다."
-msgid "ProtectedEnvironment|No environments in this project are projected."
-msgstr "ProtectedEnvironment|ì´ í”„ë¡œì íŠ¸ì—는 ë³´í˜¸ëœ í™˜ê²½ì´ ì—†ìŠµë‹ˆë‹¤."
+msgid "ProtectedEnvironment|No environments in this project are protected."
+msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
msgstr ""
@@ -31610,7 +31880,7 @@ msgid "ProtectedTag|By default, protected tags restrict who can modify the tag."
msgstr ""
msgid "ProtectedTag|Learn more."
-msgstr ""
+msgstr "ë” ì•Œì•„ë³´ê¸°."
msgid "ProtectedTag|Limit access to creating and updating tags."
msgstr ""
@@ -31735,9 +32005,6 @@ msgstr "푸쉬 ì´ë²¤íŠ¸"
msgid "Push project from command line"
msgstr "명령줄로 프로ì íŠ¸ 푸쉬"
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -31748,7 +32015,7 @@ msgid "PushRules|All branch names must match this %{wiki_syntax_link_start}regul
msgstr ""
msgid "PushRules|All commit author's email must match this %{wiki_syntax_link_start}regular expression%{wiki_syntax_link_end}. If empty, any email is allowed."
-msgstr ""
+msgstr "모든 커밋 작성ìžì˜ ì´ë©”ì¼ì€ ì´ %{wiki_syntax_link_start}ì •ê·œì‹%{wiki_syntax_link_end}ê³¼ ì¼ì¹˜í•´ì•¼ 합니다. 만약 비어 있다면, 모든 ì´ë©”ì¼ì´ 허용ë©ë‹ˆë‹¤."
msgid "PushRules|All commit messages must match this %{wiki_syntax_link_start}regular expression%{wiki_syntax_link_end}. If empty, commit messages are not required to match any expression."
msgstr ""
@@ -31861,9 +32128,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -31949,7 +32213,7 @@ msgid "Rebase"
msgstr ""
msgid "Rebase completed"
-msgstr ""
+msgstr "리베ì´ìŠ¤ 완료"
msgid "Rebase in progress"
msgstr ""
@@ -32000,7 +32264,7 @@ msgid "Reconfigure"
msgstr ""
msgid "Recover password"
-msgstr ""
+msgstr "비밀번호 복구"
msgid "Recovery Codes"
msgstr "복구 코드"
@@ -32093,6 +32357,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr "등ë¡:"
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -32182,7 +32449,7 @@ msgstr ""
msgid "Release"
msgid_plural "Releases"
-msgstr[0] ""
+msgstr[0] "릴리스"
msgid "Release %{deletedRelease} has been successfully deleted."
msgstr "릴리스 %{deletedRelease} ì´ ì„±ê³µì ìœ¼ë¡œ ì‚­ì œë˜ì—ˆìŠµë‹ˆë‹¤."
@@ -32197,82 +32464,82 @@ msgid "Release date"
msgstr ""
msgid "Release does not have the same project as the milestone"
-msgstr ""
+msgstr "ë¦´ë¦¬ìŠ¤ì— ë§ˆì¼ìŠ¤í†¤ê³¼ ë™ì¼í•œ 프로ì íŠ¸ê°€ 없습니다."
msgid "Release notes"
-msgstr ""
+msgstr "릴리스 노트"
msgid "Release notes:"
-msgstr ""
+msgstr "릴리스 노트:"
msgid "Release title"
-msgstr ""
+msgstr "릴리스 제목"
msgid "Release with tag \"%{tag}\" was not found"
-msgstr ""
+msgstr "태그가 \"%{tag}\"ì¸ ë¦´ë¦¬ìŠ¤ë¥¼ ì°¾ì„ ìˆ˜ 없습니다."
msgid "ReleaseAssetLinkType|Image"
-msgstr ""
+msgstr "ì´ë¯¸ì§€"
msgid "ReleaseAssetLinkType|Images"
-msgstr ""
+msgstr "ì´ë¯¸ì§€"
msgid "ReleaseAssetLinkType|Other"
-msgstr ""
+msgstr "기타"
msgid "ReleaseAssetLinkType|Package"
-msgstr ""
+msgstr "패키지"
msgid "ReleaseAssetLinkType|Packages"
-msgstr ""
+msgstr "패키지"
msgid "ReleaseAssetLinkType|Runbook"
-msgstr ""
+msgstr "런ë¶"
msgid "ReleaseAssetLinkType|Runbooks"
-msgstr ""
+msgstr "런ë¶"
msgid "Released date"
-msgstr ""
+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 ""
+msgstr "릴리스는 Git 태그를 기반으로 하며 프로ì íŠ¸ 개발 기ë¡ì˜ 특정 지ì ì„ 표시합니다. 여기ì—는 변경 ìœ í˜•ì— ëŒ€í•œ ì •ë³´ê°€ í¬í•¨ë  수 있으며 ì†Œí”„íŠ¸ì›¨ì–´ì˜ ì»´íŒŒì¼ëœ 버전과 ê°™ì€ ë°”ì´ë„ˆë¦¬ë„ 제공할 수 있습니다."
msgid "Releases are based on Git tags. We recommend tags that use semantic versioning, for example %{codeStart}v1.0.0%{codeEnd}, %{codeStart}v2.1.0-pre%{codeEnd}."
-msgstr ""
+msgstr "릴리스는 Git 태그를 기반으로 합니다. 시맨틱 버전 관리를 사용하는 태그(예: %{codeStart}v1.0.0%{codeEnd}, %{codeStart}v2.1.0-pre%{codeEnd})를 권장합니다."
msgid "Releases documentation"
-msgstr ""
+msgstr "릴리스 문서"
msgid "Releases|New Release"
-msgstr ""
+msgstr "새 릴리스"
msgid "Releases|Tag message"
-msgstr ""
+msgstr "태그 메시지"
msgid "Release|Include message from the annotated tag."
msgstr ""
msgid "Release|Something went wrong while creating a new release."
-msgstr ""
+msgstr "새 릴리스를 만드는 ë™ì•ˆ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "Release|Something went wrong while deleting the release."
-msgstr "Release|릴리스를 삭제하는 ë™ì•ˆ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
+msgstr ""
msgid "Release|Something went wrong while getting the release details."
-msgstr ""
+msgstr "릴리스 세부 정보를 가져오는 ë™ì•ˆ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "Release|Something went wrong while saving the release details."
-msgstr ""
+msgstr "릴리스 세부 정보를 저장하는 ë™ì•ˆ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "Release|Unable to fetch the tag notes."
msgstr ""
msgid "Release|You can edit the content later by editing the release. %{linkStart}How do I edit a release?%{linkEnd}"
-msgstr ""
+msgstr "ë‚˜ì¤‘ì— ë¦´ë¦¬ìŠ¤ë¥¼ 편집하여 콘í…츠를 편집할 수 있습니다. %{linkStart}릴리스를 편집하려면 어떻게 해야 합니까?%{linkEnd}"
msgid "Reload page"
msgstr ""
@@ -32641,7 +32908,7 @@ msgid "Report version not provided, %{report_type} report type supports versions
msgstr ""
msgid "Report your license usage data to GitLab"
-msgstr ""
+msgstr "ë¼ì´ì„ ìŠ¤ 사용 ë°ì´í„°ë¥¼ GitLabì— ë³´ê³ "
msgid "Reported %{timeAgo} by %{reportedBy}"
msgstr ""
@@ -32650,7 +32917,7 @@ msgid "Reported by"
msgstr ""
msgid "Reported by %{reporter}"
-msgstr ""
+msgstr "%{reporter}ì— ì˜í•´ ë³´ê³ ë¨"
msgid "Reporting"
msgstr "ë³´ê³ "
@@ -33002,7 +33269,7 @@ msgid "Request time"
msgstr ""
msgid "Request to link SAML account must be authorized"
-msgstr ""
+msgstr "SAML 계정 ì—°ê²° ìš”ì²­ì´ ìŠ¹ì¸ë˜ì–´ì•¼ 합니다."
msgid "Requested"
msgstr ""
@@ -33109,7 +33376,7 @@ msgid "Reset"
msgstr ""
msgid "Reset error tracking access token"
-msgstr ""
+msgstr "오류 ì¶”ì  ì•¡ì„¸ìŠ¤ í† í° ìž¬ì„¤ì •"
msgid "Reset file"
msgstr ""
@@ -33214,7 +33481,7 @@ msgid "Restoring the project will prevent the project from being removed on this
msgstr ""
msgid "Restrict access by IP address"
-msgstr ""
+msgstr "IP 주소로 액세스 제한"
msgid "Restrict membership by email domain"
msgstr ""
@@ -33418,7 +33685,7 @@ msgstr "Runners 페ì´ì§€."
msgid "Runners|%d selected runner deleted"
msgid_plural "Runners|%d selected runners deleted"
-msgstr[0] ""
+msgstr[0] "%d ê°œì˜ ì„ íƒëœ 러너 ì‚­ì œë¨"
msgid "Runners|%{percentage} spot."
msgstr ""
@@ -33429,7 +33696,7 @@ msgstr[0] ""
msgid "Runners|%{strongStart}%{count}%{strongEnd} runner will be permanently deleted and no longer available for projects or groups in the instance. Are you sure you want to continue?"
msgid_plural "Runners|%{strongStart}%{count}%{strongEnd} runners will be permanently deleted and no longer available for projects or groups in the instance. Are you sure you want to continue?"
-msgstr[0] ""
+msgstr[0] "%{strongStart}%{count}%{strongEnd} 러너는 ì˜êµ¬ì ìœ¼ë¡œ ì‚­ì œë˜ë©° ì¸ìŠ¤í„´ìŠ¤ì˜ 프로ì íŠ¸ ë˜ëŠ” 그룹ì—ì„œ ë” ì´ìƒ 사용할 수 없습니다. ê³„ì† ì§„í–‰í•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
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 ""
@@ -33446,6 +33713,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33461,6 +33731,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33474,7 +33750,7 @@ msgid "Runners|Associated with one or more projects"
msgstr ""
msgid "Runners|Available"
-msgstr "Runners|가용"
+msgstr ""
msgid "Runners|Available to all projects"
msgstr ""
@@ -33509,6 +33785,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33555,6 +33834,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33579,6 +33861,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33624,24 +33909,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -33665,7 +33941,7 @@ msgid "Runners|Protected"
msgstr ""
msgid "Runners|Recommended"
-msgstr "Runners|추천ë¨"
+msgstr ""
msgid "Runners|Register a group runner"
msgstr ""
@@ -33787,9 +34063,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -33809,7 +34082,7 @@ msgid "Runners|The new view gives you more space and better visibility into your
msgstr ""
msgid "Runners|The runner will be permanently deleted and no longer available for projects or groups in the instance. Are you sure you want to continue?"
-msgstr ""
+msgstr "러너는 ì˜êµ¬ì ìœ¼ë¡œ ì‚­ì œë˜ë©° ì¸ìŠ¤í„´ìŠ¤ì˜ 프로ì íŠ¸ ë˜ëŠ” 그룹ì—ì„œ ë” ì´ìƒ 사용할 수 없습니다. ê³„ì† ì§„í–‰í•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
msgid "Runners|This group currently has 1 stale runner."
msgid_plural "Runners|This group currently has %d stale runners."
@@ -33840,19 +34113,19 @@ msgid "Runners|To register them, go to the %{link_start}group's Runners page%{li
msgstr "Runners|등ë¡í•˜ë ¤ë©´ %{link_start}ê·¸ë£¹ì˜ ëŸ¬ë„ˆ 페ì´ì§€%{link_end}ë¡œ ì´ë™í•˜ì‹­ì‹œì˜¤."
msgid "Runners|Up to date"
-msgstr "Runners|최신"
+msgstr ""
msgid "Runners|Upgrade GitLab Runner to match the version of GitLab you're running. Both %{linkStart}major and minor versions%{linkEnd} should match."
msgstr ""
msgid "Runners|Upgrade Status"
-msgstr "Runners|업그레ì´ë“œ ìƒíƒœ"
+msgstr ""
msgid "Runners|Upgrade available"
-msgstr ""
+msgstr "업그레ì´ë“œ 가능"
msgid "Runners|Upgrade recommended"
-msgstr ""
+msgstr "업그레ì´ë“œ 권장"
msgid "Runners|Use Group runners when you want all projects in a group to have access to a set of runners."
msgstr ""
@@ -33872,9 +34145,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -33893,42 +34175,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr "Runner|소유ìž"
@@ -34022,6 +34280,9 @@ msgstr ""
msgid "SSL verification"
msgstr "SSL ê²€ì¦"
+msgid "Sat"
+msgstr "토"
+
msgid "Satisfied"
msgstr ""
@@ -34067,12 +34328,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr "ScanExecutionPolicy|%{ifLabelStart}ì´ë©´%{ifLabelEnd} %{rules} %{scopes} %{branches}ì— ëŒ€í•œ ìž‘ì—…"
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr "ScanExecutionPolicy| %{time}ì—ì„œ%{period} %{days}"
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34082,6 +34358,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr "ScanExecutionPolicy|브랜치"
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34191,7 +34479,7 @@ msgid "Search assignees"
msgstr ""
msgid "Search authors"
-msgstr ""
+msgstr "ìž‘ì„±ìž ê²€ìƒ‰"
msgid "Search branches"
msgstr "브랜치 검색"
@@ -34206,7 +34494,7 @@ msgid "Search by Git revision"
msgstr ""
msgid "Search by author"
-msgstr ""
+msgstr "작성ìžë¡œ 검색"
msgid "Search by commit title or SHA"
msgstr ""
@@ -34460,7 +34748,7 @@ msgid "Security Finding not found"
msgstr ""
msgid "Security dashboard"
-msgstr ""
+msgstr "보안 대시보드"
msgid "Security navigation"
msgstr ""
@@ -34568,7 +34856,7 @@ msgid "SecurityConfiguration|Manage corpus files used as seed inputs with covera
msgstr ""
msgid "SecurityConfiguration|Manage profiles"
-msgstr ""
+msgstr "프로필 관리"
msgid "SecurityConfiguration|Manage profiles for use by DAST scans."
msgstr ""
@@ -34651,6 +34939,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -34906,6 +35197,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr "SecurityOrchestration|ì´ %{namespaceType} ì—는 보안 ì •ì±…ì´ í¬í•¨ë˜ì–´ 있지 않습니다."
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -34994,16 +35288,16 @@ msgid "SecurityReports|%{firstProject}, %{secondProject}, and %{rest}"
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 ""
+msgstr "보안 ì˜ì—­ì—ì„œ 모니터ë§í•  프로ì íŠ¸ë¥¼ 추가하거나 제거합니다. ì´ ëª©ë¡ì— í¬í•¨ëœ 프로ì íŠ¸ì˜ 결과는 보안 대시보드 ë° ì·¨ì•½ì  ë³´ê³ ì„œì— í‘œì‹œë©ë‹ˆë‹¤."
msgid "SecurityReports|Add projects"
-msgstr ""
+msgstr "프로ì íŠ¸ 추가"
msgid "SecurityReports|All activity"
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 ""
+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."
msgstr ""
@@ -35015,7 +35309,7 @@ msgid "SecurityReports|Check the messages generated while parsing the following
msgstr ""
msgid "SecurityReports|Cluster"
-msgstr ""
+msgstr "í´ëŸ¬ìŠ¤í„°"
msgid "SecurityReports|Comment added to '%{vulnerabilityName}'"
msgstr ""
@@ -35057,7 +35351,7 @@ msgid "SecurityReports|Download scanned URLs"
msgstr ""
msgid "SecurityReports|Either you don't have permission to view this dashboard or the dashboard has not been setup. Please check your permission settings with your administrator or check your dashboard configurations to proceed."
-msgstr ""
+msgstr "ì´ ëŒ€ì‹œë³´ë“œë¥¼ ë³¼ 수 있는 ê¶Œí•œì´ ì—†ê±°ë‚˜ 대시보드가 설정ë˜ì§€ 않았습니다. 계ì†í•˜ë ¤ë©´ 관리ìžì—게 권한 ì„¤ì •ì„ í™•ì¸í•˜ê±°ë‚˜ 대시보드 ì„¤ì •ì„ í™•ì¸í•˜ì„¸ìš”."
msgid "SecurityReports|Ensure that %{trackingStart}issue tracking%{trackingEnd} is enabled for this project and you have %{permissionsStart}permission to create new issues%{permissionsEnd}."
msgstr ""
@@ -35090,7 +35384,7 @@ msgid "SecurityReports|Issues created from a vulnerability cannot be removed."
msgstr ""
msgid "SecurityReports|Learn more about setting up your dashboard"
-msgstr ""
+msgstr "대시보드 ì„¤ì •ì— ëŒ€í•´ ìžì„¸ížˆ 알아보기"
msgid "SecurityReports|Manage and track vulnerabilities identified in projects within your group. Vulnerabilities in projects are shown here when security testing is configured."
msgstr ""
@@ -35150,13 +35444,13 @@ msgid "SecurityReports|Project"
msgstr ""
msgid "SecurityReports|Project was not found or you do not have permission to add this project to Security Dashboards."
-msgstr ""
+msgstr "프로ì íŠ¸ë¥¼ ì°¾ì„ ìˆ˜ 없거나 ì´ í”„ë¡œì íŠ¸ë¥¼ 보안 ëŒ€ì‹œë³´ë“œì— ì¶”ê°€í•  수 있는 ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤."
msgid "SecurityReports|Projects added"
msgstr ""
msgid "SecurityReports|Remove project from dashboard"
-msgstr ""
+msgstr "대시보드ì—ì„œ 프로ì íŠ¸ 제거"
msgid "SecurityReports|Report has expired"
msgstr ""
@@ -35168,7 +35462,7 @@ msgid "SecurityReports|Scan details"
msgstr ""
msgid "SecurityReports|Security Dashboard"
-msgstr ""
+msgstr "보안 대시보드"
msgid "SecurityReports|Security reports can only be accessed by authorized users."
msgstr ""
@@ -35333,10 +35627,10 @@ msgid "Select a branch"
msgstr ""
msgid "Select a branch to compare"
-msgstr ""
+msgstr "비êµí•  브랜치 ì„ íƒ"
msgid "Select a color"
-msgstr ""
+msgstr "ìƒ‰ìƒ ì„ íƒ"
msgid "Select a compliance framework to apply to this project. %{linkStart}How are these added?%{linkEnd}"
msgstr ""
@@ -35390,7 +35684,7 @@ msgid "Select assignee"
msgstr ""
msgid "Select assignee(s)"
-msgstr ""
+msgstr "ë‹´ë‹¹ìž ì„ íƒ"
msgid "Select branch"
msgstr ""
@@ -35417,7 +35711,7 @@ msgid "Select iteration"
msgstr ""
msgid "Select label"
-msgstr ""
+msgstr "ë¼ë²¨ ì„ íƒ"
msgid "Select labels"
msgstr "ë¼ë²¨ ì„ íƒ"
@@ -35453,7 +35747,7 @@ msgid "Select source branch"
msgstr "소스 브랜치 ì„ íƒ"
msgid "Select source project"
-msgstr ""
+msgstr "소스 프로ì íŠ¸ ì„ íƒ"
msgid "Select start date"
msgstr ""
@@ -35477,7 +35771,7 @@ msgid "Select target branch or tag"
msgstr ""
msgid "Select target project"
-msgstr ""
+msgstr "ëŒ€ìƒ í”„ë¡œì íŠ¸ ì„ íƒ"
msgid "Select timezone"
msgstr ""
@@ -35516,10 +35810,10 @@ msgid "Self-monitoring project does not exist. Please check logs for any error m
msgstr ""
msgid "Self-monitoring project has been successfully deleted"
-msgstr ""
+msgstr "셀프 ëª¨ë‹ˆí„°ë§ í”„ë¡œì íŠ¸ê°€ 성공ì ìœ¼ë¡œ ì‚­ì œë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "Self-monitoring project was not deleted. Please check logs for any error messages"
-msgstr ""
+msgstr "셀프 ëª¨ë‹ˆí„°ë§ í”„ë¡œì íŠ¸ê°€ ì‚­ì œë˜ì§€ 않았습니다. 오류 메시지가 있는지 로그를 확ì¸í•˜ì‹­ì‹œì˜¤."
msgid "SelfMonitoring|Activate or deactivate instance self monitoring."
msgstr ""
@@ -35543,7 +35837,7 @@ msgid "SelfMonitoring|Self monitoring project successfully created."
msgstr ""
msgid "SelfMonitoring|Self monitoring project successfully deleted."
-msgstr ""
+msgstr "셀프 ëª¨ë‹ˆí„°ë§ í”„ë¡œì íŠ¸ê°€ 성공ì ìœ¼ë¡œ ì‚­ì œë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "Send"
msgstr ""
@@ -35687,7 +35981,7 @@ msgid "Session ID"
msgstr ""
msgid "Session duration (minutes)"
-msgstr ""
+msgstr "세션 시간 (분)"
msgid "Set %{epic_ref} as the parent epic."
msgstr ""
@@ -35831,16 +36125,16 @@ msgid "Set weight to %{weight}."
msgstr ""
msgid "SetStatusModal|An indicator appears next to your name and avatar"
-msgstr ""
+msgstr "ì´ë¦„ê³¼ 아바타 ì˜†ì— ìƒíƒœê°€ 표시ë©ë‹ˆë‹¤."
msgid "SetStatusModal|Busy"
-msgstr ""
+msgstr "ë°”ì¨"
msgid "SetStatusModal|Clear status"
msgstr "ìƒíƒœ 지우기"
msgid "SetStatusModal|Clear status after"
-msgstr ""
+msgstr "ë‹¤ìŒ ì´í›„ ìƒíƒœë¥¼ 지움:"
msgid "SetStatusModal|Edit status"
msgstr "ìƒíƒœ 수정"
@@ -36081,13 +36375,13 @@ msgid "Show the Open list"
msgstr ""
msgid "Show thread"
-msgstr ""
+msgstr "스레드 보기"
msgid "Show whitespace changes"
msgstr ""
msgid "ShowcaseSecurity|Access a dedicated area for vulnerability management. This includes a security dashboard, vulnerability report, and settings."
-msgstr ""
+msgstr "ì·¨ì•½ì  ê´€ë¦¬ë¥¼ 위한 ì „ìš© ì˜ì—­ì— 액세스합니다. 여기ì—는 보안 대시보드, ì·¨ì•½ì  ë³´ê³ ì„œ ë° ì„¤ì •ì´ í¬í•¨ë©ë‹ˆë‹¤."
msgid "ShowcaseSecurity|Audit your Docker-based app. Scan for known vulnerabilities in the Docker images where your code is shipped."
msgstr ""
@@ -36181,7 +36475,7 @@ msgid "Showing version #%{versionNumber}"
msgstr ""
msgid "Shows issues for group '%{group_name}' from Nov 1, 2019 to Dec 31, 2019"
-msgstr ""
+msgstr "2019ë…„ 11ì›” 1ì¼ë¶€í„° 2019ë…„ 12ì›” 31ì¼ê¹Œì§€ 그룹 '%{group_name}'ì— ëŒ€í•œ ì´ìŠˆë“¤ì„ 표시합니다."
msgid "Side-by-side"
msgstr "나란히"
@@ -36268,7 +36562,7 @@ msgid "Sign-in count:"
msgstr ""
msgid "Sign-in failed because %{error}."
-msgstr ""
+msgstr "%{error}으로 ì¸í•´ 로그ì¸ì— 실패했습니다."
msgid "Sign-in page"
msgstr ""
@@ -36288,12 +36582,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "가입 제한"
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr "SignUp| %{button_text} ì„ í´ë¦­í•˜ê±°ë‚˜ ì œ3ìžë¥¼ 통해 등ë¡í•˜ë©´ GitLab%{link_start} ì´ìš© ì•½ê´€ì— ë™ì˜í•˜ê³  ê°œì¸ ì •ë³´ 보호 ì •ì±… ë° ì¿ í‚¤ ì •ì±…%{link_end}ì— ë™ì˜í•˜ëŠ” 것입니다."
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr "SignUp| %{button_text} ì„ í´ë¦­í•˜ê±°ë‚˜ ì œ3ìžë¥¼ 통해 등ë¡í•˜ë©´ GitLab%{link_start} ì´ìš© ì•½ê´€ì— ë™ì˜í•˜ê³  ê°œì¸ ì •ë³´ 보호 ì •ì±… ë° ì¿ í‚¤ ì •ì±…%{link_end}ì— ë™ì˜í•˜ëŠ” 것입니다."
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr "로그ì¸í•˜ë©´ %{link_start}ì´ìš©ì•½ê´€ ë° ê°œì¸ì •ë³´ 보호정책 ë° ì¿ í‚¤ ì •ì±…%{link_end}ì— ë™ì˜í•˜ê²Œ ë©ë‹ˆë‹¤."
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36517,7 +36820,7 @@ msgid "Solution"
msgstr ""
msgid "Some changes are not shown"
-msgstr ""
+msgstr "ì¼ë¶€ 변경 ì‚¬í•­ì´ í‘œì‹œë˜ì§€ 않습니다."
msgid "Some child epics may be hidden due to applied filters"
msgstr ""
@@ -36567,6 +36870,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36657,6 +36963,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr "메모를 타임ë¼ì¸ ì´ë²¤íŠ¸ë¡œ 승격하는 ë™ì•ˆ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -36673,7 +36982,7 @@ msgid "Something went wrong while setting %{issuableType} %{dateType} date."
msgstr ""
msgid "Something went wrong while setting %{issuableType} confidentiality."
-msgstr ""
+msgstr "%{issuableType}ì„(를) 대외비로 설정하는 ë™ì•ˆ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "Something went wrong while setting %{issuableType} health status."
msgstr ""
@@ -36973,7 +37282,7 @@ msgid "SourcegraphAdmin|Enable code intelligence powered by %{link_start}Sourceg
msgstr ""
msgid "SourcegraphAdmin|Learn more."
-msgstr ""
+msgstr "ë” ì•Œì•„ë³´ê¸°."
msgid "SourcegraphAdmin|Only public projects have code intelligence enabled and communicate with Sourcegraph."
msgstr ""
@@ -37041,6 +37350,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37056,9 +37368,6 @@ msgstr ""
msgid "Standard"
msgstr "표준"
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -37078,7 +37387,7 @@ msgid "Starred projects"
msgstr "ë³„í‘œëœ í”„ë¡œì íŠ¸"
msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
-msgstr ""
+msgstr "프로ì íŠ¸ 페ì´ì§€ë¥¼ 방문하고 별 ì•„ì´ì½˜ì„ 누르십시오. 그러면, ì´ íŽ˜ì´ì§€ì—ì„œ 프로ì íŠ¸ë¥¼ ì°¾ì„ ìˆ˜ 있습니다."
msgid "StarredProjectsEmptyState|You don't have starred projects yet."
msgstr ""
@@ -37333,7 +37642,7 @@ msgid "StatusPage|your status page frontend."
msgstr ""
msgid "Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments."
-msgstr ""
+msgstr "ë°°í¬ë¥¼ 모니터ë§í•˜ë„ë¡ Prometheus를 설정하여 í™˜ê²½ì˜ ì„±ëŠ¥ ë° ìƒíƒœì— 대한 최신 정보를 얻으십시오."
msgid "Step %{currentStep} of %{stepCount}"
msgstr ""
@@ -37797,6 +38106,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr "ì¼"
+
msgid "Sunday"
msgstr ""
@@ -38039,7 +38351,7 @@ msgid "SynthaxHighlightingTheme|Monokai"
msgstr ""
msgid "SynthaxHighlightingTheme|None"
-msgstr ""
+msgstr "ì—†ìŒ"
msgid "SynthaxHighlightingTheme|Solarized Dark"
msgstr ""
@@ -38201,7 +38513,7 @@ msgid "TagsPage|Please type the following to confirm:"
msgstr ""
msgid "TagsPage|Release notes"
-msgstr "릴리즈 노트"
+msgstr "릴리스 노트"
msgid "TagsPage|Repository has no tags yet."
msgstr "ì €ìž¥ì†Œì— ì•„ì§ íƒœê·¸ê°€ 없습니다."
@@ -38216,7 +38528,7 @@ msgid "TagsPage|Tags give the ability to mark specific points in history as bein
msgstr "태그는 특정 지ì ì„ 중요하다고 표시하는 기능입니다."
msgid "TagsPage|This tag has no release notes."
-msgstr "ì´ íƒœê·¸ëŠ” 릴리즈 노트가 없습니다."
+msgstr "ì´ íƒœê·¸ëŠ” 릴리스 노트가 없습니다."
msgid "TagsPage|Unable to load tags"
msgstr ""
@@ -38225,7 +38537,7 @@ msgid "TagsPage|Use git tag command to add a new one:"
msgstr "git tag ëª…ë ¹ì„ ì‚¬ìš©í•˜ì—¬ 새 태그를 추가하십시오:"
msgid "TagsPage|Write your release notes or drag files here…"
-msgstr ""
+msgstr "릴리스 노트를 작성하거나 여기로 파ì¼ì„ ëŒì–´ë‹¤ 놓으십시오…"
msgid "TagsPage|Yes, delete protected tag"
msgstr ""
@@ -38594,7 +38906,7 @@ msgid "TestHooks|Ensure the project has notes."
msgstr ""
msgid "TestHooks|Ensure the project has releases."
-msgstr ""
+msgstr "프로ì íŠ¸ì— 릴리스가 있는지 확ì¸í•˜ì‹­ì‹œì˜¤."
msgid "TestHooks|Ensure the wiki is enabled and has pages."
msgstr ""
@@ -38703,7 +39015,7 @@ msgid_plural "The %{type} contains the following errors:"
msgstr[0] "%{type} ì—는 다ìŒê³¼ ê°™ì€ ì˜¤ë¥˜ê°€ 있습니다."
msgid "The API key used by GitLab for accessing the Spam Check service endpoint."
-msgstr ""
+msgstr "스팸 í™•ì¸ ì„œë¹„ìŠ¤ 엔드í¬ì¸íŠ¸ì— 액세스하기 위해 GitLabì—ì„œ 사용하는 API 키입니다."
msgid "The CSV export will be created in the background. Once finished, it will be sent to %{email} in an attachment."
msgstr ""
@@ -38724,7 +39036,7 @@ msgid "The Issue Tracker is the place to add things that need to be improved or
msgstr "ì´ìŠˆ 트래커는 프로ì íŠ¸ì—ì„œ 개선해야하거나 해결해야할 ì‚¬í•­ì„ ì¶”ê°€í•  수 있는 곳입니다. ì´ í”„ë¡œì íŠ¸ì—ì„œ ì´ìŠˆë¥¼ 만들려면 가입하거나 로그ì¸í•˜ì‹­ì‹œì˜¤."
msgid "The Prometheus server responded with \"bad request\". Please check your queries are correct and are supported in your Prometheus version. %{documentationLink}"
-msgstr ""
+msgstr "Prometheus 서버가 \"ìž˜ëª»ëœ ìš”ì²­\"으로 ì‘답했습니다. 쿼리가 정확한지, 그리고 해당 Prometheus 버전ì—ì„œ 지ì›ë˜ëŠ”지 확ì¸í•˜ì„¸ìš”. %{documentationLink}"
msgid "The Snowplow cookie domain."
msgstr ""
@@ -38799,7 +39111,7 @@ msgid "The current epic"
msgstr ""
msgid "The current incident"
-msgstr ""
+msgstr "í˜„ìž¬ì˜ ì¸ì‹œë˜íŠ¸"
msgid "The current issue"
msgstr "현재 ì´ìŠˆ"
@@ -38996,7 +39308,7 @@ msgid "The maximum file size allowed is %{size}."
msgstr ""
msgid "The maximum file size for job artifacts."
-msgstr ""
+msgstr "ìž‘ì—… ì•„í‹°íŒ©íŠ¸ì˜ ìµœëŒ€ íŒŒì¼ í¬ê¸°ìž…니다."
msgid "The maximum file size in megabytes for individual job artifacts."
msgstr ""
@@ -39046,6 +39358,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39071,7 +39386,7 @@ msgid "The project has already been added to your dashboard."
msgstr ""
msgid "The project is still being deleted. Please try again later."
-msgstr ""
+msgstr "프로ì íŠ¸ëŠ” ì•„ì§ ì‚­ì œ 중입니다. ë‚˜ì¤‘ì— ë‹¤ì‹œ ì‹œë„ í•´ì£¼ì‹­ì‹œì˜¤."
msgid "The project size exceeds the export limit."
msgstr ""
@@ -39142,6 +39457,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39152,6 +39473,9 @@ msgid "The subject will be used as the title of the new issue, and the message w
msgstr ""
msgid "The tag name can't be changed for an existing release."
+msgstr "기존 ë¦´ë¦¬ìŠ¤ì˜ íƒœê·¸ ì´ë¦„ì€ ë³€ê²½í•  수 없습니다."
+
+msgid "The target topic is not a topic."
msgstr ""
msgid "The time period in seconds that the maximum requests per project limit applies to."
@@ -39202,10 +39526,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39263,7 +39587,7 @@ msgid "There are no issues to show"
msgstr "표시할 ì´ìŠˆê°€ 없습니다"
msgid "There are no issues with the selected labels"
-msgstr ""
+msgstr "ì„ íƒëœ ë¼ë²¨ì—는 문제가 없습니다."
msgid "There are no matching files"
msgstr "ì¼ì¹˜í•˜ëŠ” 파ì¼ì´ 없습니다"
@@ -39310,9 +39634,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39341,10 +39662,10 @@ msgid "There was a problem communicating with your device."
msgstr ""
msgid "There was a problem fetching CRM contacts."
-msgstr ""
+msgstr "CRM ì—°ë½ì²˜ë¥¼ 가져오는 ì¤‘ì— ë¬¸ì œê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "There was a problem fetching CRM organizations."
-msgstr ""
+msgstr "CRM ì¡°ì§ì„ 가져오는 ì¤‘ì— ë¬¸ì œê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "There was a problem fetching branches."
msgstr ""
@@ -39386,7 +39707,7 @@ msgid "There was a problem fetching recent projects."
msgstr ""
msgid "There was a problem fetching releases."
-msgstr ""
+msgstr "릴리스를 가져오는 ì¤‘ì— ë¬¸ì œê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "There was a problem fetching the job token scope value"
msgstr ""
@@ -39440,7 +39761,7 @@ msgid "There was an error fetching data for the tasks by type chart"
msgstr ""
msgid "There was an error fetching label data for the selected group"
-msgstr ""
+msgstr "ì„ íƒí•œ ê·¸ë£¹ì˜ ë¼ë²¨ ë°ì´í„°ë¥¼ 가져오는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "There was an error fetching median data for stages"
msgstr ""
@@ -39464,7 +39785,7 @@ msgid "There was an error fetching the jobs for your project."
msgstr ""
msgid "There was an error fetching the top labels for the selected group"
-msgstr ""
+msgstr "ì„ íƒí•œ ê·¸ë£¹ì˜ ìƒìœ„ ë¼ë²¨ì„ 가져오는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "There was an error fetching the variables."
msgstr "변수를 가져오는 ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
@@ -39664,6 +39985,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr "ì´ ë³´ë“œì˜ ë²”ìœ„ê°€ 축소ë˜ì—ˆìŠµë‹ˆë‹¤."
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39688,7 +40012,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -39854,19 +40178,19 @@ msgid "This is your current session"
msgstr ""
msgid "This issue cannot be assigned to a confidential epic because it is public."
-msgstr ""
+msgstr "ì´ ì´ìŠˆëŠ” ê³µê°œëœ ìƒíƒœì´ë¯€ë¡œ 대외비 ì—í”½ì— í• ë‹¹í•  수 없습니다."
msgid "This issue cannot be made public because it belongs to a confidential epic."
msgstr ""
msgid "This issue is confidential and should only be visible to team members with at least Reporter access."
-msgstr ""
+msgstr "ì´ ì´ìŠˆëŠ” 비공개ì´ë©°, ì ì–´ë„ 리í¬í„° ì´ìƒì˜ ê¶Œí•œì´ ìžˆëŠ” 팀 구성ì›ë“¤ì—게만 보여집니다."
msgid "This issue is currently blocked by the following issues:"
msgstr ""
msgid "This issue is hidden because its author has been banned"
-msgstr ""
+msgstr "작성ìžê°€ 차단ë˜ì—ˆê¸° ë•Œë¬¸ì— ì´ ì´ìŠˆëŠ” 숨겨졌습니다."
msgid "This issue is in a child epic of the filtered epic"
msgstr ""
@@ -39953,10 +40277,10 @@ msgid "This job is performing tasks that must complete before it can start"
msgstr ""
msgid "This job is preparing to start"
-msgstr ""
+msgstr "ì´ ìž‘ì—…ì€ ì‹œìž‘ 준비 중입니다."
msgid "This job is waiting for resource: "
-msgstr ""
+msgstr "ì´ ìž‘ì—…ì€ ë¦¬ì†ŒìŠ¤ë¥¼ 기다리고 있습니다. "
msgid "This job requires a manual action"
msgstr "ì´ ìž‘ì—…ì—는 ìˆ˜ë™ ìž‘ì—…ì´ í•„ìš”í•©ë‹ˆë‹¤."
@@ -40069,6 +40393,9 @@ msgstr ""
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 "ì´ í”„ë¡œì íŠ¸ëŠ” 공개입니다. 서비스 ë°ìŠ¤í¬ ì´ë©”ì¼ ì£¼ì†Œì—는 그룹 ë° í”„ë¡œì íŠ¸ ì´ë¦„ì´ í¬í•¨ë˜ì–´ 있기 ë•Œë¬¸ì— ë¹„íšŒì›ë„ 서비스 ë°ìŠ¤í¬ ì´ë©”ì¼ ì£¼ì†Œë¥¼ 추측할 수 있습니다. %{linkStart}커스텀 ì´ë©”ì¼ ì£¼ì†ŒëŠ” 어떻게 만드나요?%{linkEnd}"
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40081,9 +40408,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40186,6 +40510,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr "목"
+
msgid "Thursday"
msgstr "목요ì¼"
@@ -40396,6 +40723,9 @@ msgstr "방금 전"
msgid "Timeago|right now"
msgstr "지금"
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40423,6 +40753,24 @@ msgstr ""
msgid "Timezone"
msgstr "시간대"
+msgid "Time|A"
+msgstr "Time|A"
+
+msgid "Time|AM"
+msgstr "AM"
+
+msgid "Time|P"
+msgstr "Time|P"
+
+msgid "Time|PM"
+msgstr "PM"
+
+msgid "Time|a"
+msgstr "Time|a"
+
+msgid "Time|am"
+msgstr "am"
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "시간"
@@ -40431,6 +40779,12 @@ msgid "Time|min"
msgid_plural "Time|mins"
msgstr[0] "분"
+msgid "Time|p"
+msgstr "Time|p"
+
+msgid "Time|pm"
+msgstr "pm"
+
msgid "Time|s"
msgstr "ì´ˆ"
@@ -40485,6 +40839,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40555,7 +40912,7 @@ msgid "To keep this project going, create a new issue"
msgstr ""
msgid "To keep this project going, create a new merge request"
-msgstr ""
+msgstr "ì´ í”„ë¡œì íŠ¸ë¥¼ ê³„ì† ì§„í–‰í•˜ë ¤ë©´ 새 머지 리퀘스트를 만드세요."
msgid "To learn more about this project, read %{link_to_wiki}"
msgstr ""
@@ -40582,10 +40939,10 @@ msgid "To preserve performance only %{strong_open}%{display_size} of %{real_size
msgstr ""
msgid "To protect this issue's confidentiality, %{linkStart}fork this project%{linkEnd} and set the fork's visibility to private."
-msgstr ""
+msgstr "ì´ ì´ìŠˆë¥¼ 대외비로 유지하기 위해 %{linkStart}ì´ í”„ë¡œì íŠ¸ fork하기%{linkEnd} 그리고 private forkë¡œ 설정합니다. "
msgid "To protect this issue's confidentiality, a private fork of this project was selected."
-msgstr ""
+msgstr "ì´ ì´ìŠˆë¥¼ 대외비로 유지하기 위해, ì´ í”„ë¡œì íŠ¸ì—는 private forkê°€ ì„ íƒ ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "To reactivate your account, %{gitlab_link_start}sign in to GitLab.%{link_end}"
msgstr ""
@@ -40647,9 +41004,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr "í•  ì¼ ëª©ë¡"
@@ -40660,40 +41014,55 @@ msgid "Today"
msgstr "오늘"
msgid "Todos count"
-msgstr ""
+msgstr "í•  ì¼ì˜ìˆ˜"
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 ""
msgid "Todos|Filter by author"
-msgstr ""
+msgstr "작성ìžë¡œ í•„í„°ë§"
msgid "Todos|Filter by group"
-msgstr ""
+msgstr "그룹으로 í•„í„°ë§"
msgid "Todos|Filter by project"
+msgstr "프로ì íŠ¸ë¡œ í•„í„°ë§"
+
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
msgstr ""
msgid "Todos|It's how you always know what to work on next."
msgstr ""
msgid "Todos|Mark all as done"
-msgstr ""
+msgstr "ëª¨ë‘ ì™„ë£Œë¡œ 표시"
msgid "Todos|Nothing is on your to-do list. Nice work!"
+msgstr "í•  ì¼ ëª©ë¡ì— ì•„ë¬´ê²ƒë„ ì—†ìŠµë‹ˆë‹¤. 잘 하셨어요!"
+
+msgid "Todos|Nothing left to do. High five!"
msgstr ""
msgid "Todos|Undo mark all as done"
-msgstr ""
+msgstr "ëª¨ë‘ ì™„ë£Œë¡œ 표시한 ê²ƒì„ ì·¨ì†Œ"
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 ""
msgid "Todos|You're all done!"
-msgstr ""
+msgstr "ëª¨ë‘ ì™„ë£Œí•˜ì˜€ìŠµë‹ˆë‹¤!"
msgid "Todos|Your To-Do List shows what to work on next"
-msgstr ""
+msgstr "ë‹¹ì‹ ì˜ í•  ì¼ ëª©ë¡ì€ 다ìŒì— 해야 í•  ì¼ì„ ë³´ì—¬ì¤ë‹ˆë‹¤."
msgid "Toggle GitLab Next"
msgstr ""
@@ -40759,10 +41128,10 @@ msgid "Token"
msgstr ""
msgid "Token Access"
-msgstr ""
+msgstr "í† í° ì•¡ì„¸ìŠ¤"
msgid "Token name"
-msgstr ""
+msgstr "토í°ëª…"
msgid "Token valid until revoked"
msgstr ""
@@ -40791,9 +41160,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr "토픽 %{source_topic} ì´ %{target_topic} í† í”½ì— ì„±ê³µì ìœ¼ë¡œ 병합ë˜ì—ˆìŠµë‹ˆë‹¤."
@@ -40810,7 +41188,7 @@ msgid "Topic avatar for %{name} will be removed. This cannot be undone."
msgstr ""
msgid "Topic slug (name)"
-msgstr ""
+msgstr "토픽 슬러그(ì´ë¦„)"
msgid "Topic title"
msgstr ""
@@ -40818,9 +41196,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr "topicSelect|ì¼ì¹˜í•˜ëŠ” 결과가 없습니다"
+
+msgid "TopicSelect|Search topics"
+msgstr "TopicSelect|주제 검색"
+
+msgid "TopicSelect|Select a topic"
+msgstr "TopicSelect|주제 ì„ íƒ"
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -40846,7 +41236,7 @@ msgid "Total users"
msgstr ""
msgid "Total weight"
-msgstr ""
+msgstr "ì´ ê°€ì¤‘ì¹˜"
msgid "Total: %{total}"
msgstr "합계: %{total}"
@@ -40989,39 +41379,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41127,21 +41490,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
+msgid "Tue"
+msgstr "í™”"
+
msgid "Tuesday"
msgstr ""
msgid "Turn off"
msgstr ""
-msgid "Turn off notifications"
-msgstr ""
-
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr "Twitter"
@@ -41191,7 +41551,7 @@ msgid "Type"
msgstr ""
msgid "Type to search"
-msgstr ""
+msgstr "입력하여 검색"
msgid "U2F Devices (%{length})"
msgstr ""
@@ -41266,7 +41626,7 @@ msgid "Unable to collect memory info"
msgstr ""
msgid "Unable to connect to Prometheus server"
-msgstr ""
+msgstr "Prometheus ì„œë²„ì— ì—°ê²°í•  수 없습니다."
msgid "Unable to connect to server: %{error}"
msgstr ""
@@ -41368,7 +41728,7 @@ msgid "Unassign from commenting user"
msgstr ""
msgid "Unassigned"
-msgstr ""
+msgstr "할당ë˜ì§€ ì•ŠìŒ"
msgid "Unauthenticated API rate limit period in seconds"
msgstr ""
@@ -41380,7 +41740,7 @@ msgid "Unauthenticated web rate limit period in seconds"
msgstr ""
msgid "Unban"
-msgstr ""
+msgstr "차단 해제"
msgid "Uncommitted changes will be lost if you change branches. Do you want to continue?"
msgstr ""
@@ -41533,7 +41893,7 @@ msgid "Upcoming"
msgstr ""
msgid "Upcoming Release"
-msgstr ""
+msgstr "ì˜ˆì •ëœ ë¦´ë¦¬ìŠ¤"
msgid "Update"
msgstr "ì—…ë°ì´íŠ¸"
@@ -41587,7 +41947,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 ""
@@ -41721,6 +42081,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr "UsageQuota| %{usageSince}ì´í›„ì˜ CI/CD 분 사용량"
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41748,6 +42111,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -41844,10 +42210,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
-msgstr "UsageQuota|네임스페ì´ìŠ¤ëŠ” 현재 %{strong_start}%{used_storage}%{strong_end} 네임스페ì´ìŠ¤ 저장소를 사용하고 있습니다. 그룹 소유ìžëŠ” 네임스페ì´ìŠ¤ 스토리지 ì‚¬ìš©ëŸ‰ì„ ë³´ê³  %{strong_start}그룹 설정 &gt; 사용량 할당량%{strong_end}ì—ì„œ 추가로 구매할 수 있습니다. %{docs_link_start}ìžì„¸ížˆ 알아보십시오.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
+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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -41913,7 +42279,7 @@ msgstr "ë‚´ 프로ì íŠ¸ì˜ 리소스 사용량"
msgid "UsageQuota|Usage quotas help link"
msgstr "사용량 제한 ë„움 ë§í¬"
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42075,6 +42441,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr "ì´ìŠˆë¥¼ 사용하여 ì•„ì´ë””ì–´ì— ëŒ€í•œ ê³µë™ ìž‘ì—…, 문제 í•´ê²° ë° ìž‘ì—… 계íš"
+
msgid "Use one line per URI"
msgstr ""
@@ -42122,7 +42491,7 @@ msgstr ""
msgid "Used by %d package"
msgid_plural "Used by %d packages"
-msgstr[0] ""
+msgstr[0] "%d ê°œì˜ íŒ¨í‚¤ì§€ì—ì„œ 사용"
msgid "Used by members to sign in to your group in GitLab"
msgstr ""
@@ -42218,7 +42587,7 @@ msgid "User was successfully removed from project."
msgstr ""
msgid "User was successfully unbanned."
-msgstr ""
+msgstr "사용ìžì˜ 차단 해제가 완료ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "User was successfully updated."
msgstr ""
@@ -42227,13 +42596,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 ""
@@ -42311,7 +42680,7 @@ msgid "UserList|created %{timeago}"
msgstr "%{timeago} ì „ ìƒì„±ë¨"
msgid "UserProfile|(Busy)"
-msgstr ""
+msgstr "(ë°”ì¨)"
msgid "UserProfile|Activity"
msgstr "활ë™"
@@ -42329,7 +42698,7 @@ msgid "UserProfile|Contributed projects"
msgstr "기여한 프로ì íŠ¸"
msgid "UserProfile|Copy user ID"
-msgstr ""
+msgstr "ì‚¬ìš©ìž ID 복사"
msgid "UserProfile|Edit profile"
msgstr "프로필 수정"
@@ -42365,7 +42734,7 @@ msgid "UserProfile|Personal projects"
msgstr "ê°œì¸ í”„ë¡œì íŠ¸"
msgid "UserProfile|Pronounced as: %{pronunciation}"
-msgstr ""
+msgstr "%{pronunciation} ë¡œ ë°œìŒ"
msgid "UserProfile|Report abuse"
msgstr "악용사례 신고"
@@ -42413,7 +42782,7 @@ msgid "UserProfile|Unconfirmed user"
msgstr ""
msgid "UserProfile|User ID: %{id}"
-msgstr ""
+msgstr "ì‚¬ìš©ìž ID: %{id}"
msgid "UserProfile|View all"
msgstr "ëª¨ë‘ ë³´ê¸°"
@@ -42475,7 +42844,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -42485,7 +42854,7 @@ msgid "Users can request access (if visibility is public or internal)"
msgstr ""
msgid "Users cannot be added to projects in this group"
-msgstr ""
+msgstr "ì´ ê·¸ë£¹ì˜ í”„ë¡œì íŠ¸ì— 사용ìžë¥¼ 추가할 수 없습니다."
msgid "Users in License"
msgstr ""
@@ -42769,10 +43138,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -42829,7 +43201,7 @@ msgid "View group labels"
msgstr "그룹 ë¼ë²¨ 보기"
msgid "View group pipeline usage quota"
-msgstr ""
+msgstr "그룹 파ì´í”„ë¼ì¸ 사용 할당량 보기"
msgid "View incident details at"
msgstr ""
@@ -42850,10 +43222,10 @@ msgid "View it on GitLab"
msgstr "GitLabì—ì„œ 보기"
msgid "View job"
-msgstr ""
+msgstr "작업 보기"
msgid "View job log"
-msgstr ""
+msgstr "작업 로그 보기"
msgid "View jobs"
msgstr "작업 보기"
@@ -42926,7 +43298,7 @@ msgid "Viewed"
msgstr ""
msgid "Viewing commit"
-msgstr ""
+msgstr "커밋 보기"
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 ""
@@ -43184,7 +43556,7 @@ msgid "Vulnerability|Code Review"
msgstr ""
msgid "Vulnerability|Comments"
-msgstr ""
+msgstr "댓글"
msgid "Vulnerability|Crash address"
msgstr ""
@@ -43370,7 +43742,7 @@ msgid "We couldn't find any %{scope} matching %{term} in project %{project}"
msgstr ""
msgid "We couldn't reach the Prometheus server. Either the server no longer exists or the configuration details need updating."
-msgstr ""
+msgstr "Prometheus ì„œë²„ì— ì—°ê²°í•  수 없습니다. 서버가 ë” ì´ìƒ 존재하지 않거나 설정 세부 정보를 ì—…ë°ì´íŠ¸í•´ì•¼ 합니다."
msgid "We created a sandbox project that will help you learn the basics of GitLab. You’ll be guided by issues in an issue board. You can go through the issues at your own pace."
msgstr ""
@@ -43382,7 +43754,7 @@ msgid "We don't have enough data to show this stage."
msgstr "ì´ ë‹¨ê³„ë¥¼ ë³´ì—¬ì£¼ê¸°ì— ì¶©ë¶„í•œ ë°ì´í„°ê°€ 없습니다."
msgid "We have found the following errors:"
-msgstr ""
+msgstr "ë‹¤ìŒ ì˜¤ë¥˜ë¥¼ 발견했습니다:"
msgid "We heard back from your device. You have been authenticated."
msgstr ""
@@ -43465,6 +43837,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43541,11 +43919,14 @@ msgid "Webhooks|A pipeline's status changes."
msgstr ""
msgid "Webhooks|A release is created or updated."
-msgstr ""
+msgstr "릴리스가 ìƒì„±ë˜ê±°ë‚˜ ì—…ë°ì´íŠ¸ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43562,13 +43943,13 @@ msgid "Webhooks|Are you sure you want to delete this webhook?"
msgstr ""
msgid "Webhooks|Comments"
-msgstr ""
+msgstr "댓글"
msgid "Webhooks|Confidential comments"
-msgstr ""
+msgstr "비공개 댓글"
msgid "Webhooks|Confidential issues events"
-msgstr ""
+msgstr "비공개 ì´ìŠˆ ì´ë²¤íŠ¸"
msgid "Webhooks|Delete webhook"
msgstr ""
@@ -43588,17 +43969,20 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
msgid "Webhooks|Job events"
-msgstr ""
+msgstr "ìž‘ì—… ì´ë²¤íŠ¸"
msgid "Webhooks|Member events"
msgstr ""
msgid "Webhooks|Merge request events"
-msgstr ""
+msgstr "머지 리퀘스트 ì´ë²¤íŠ¸"
msgid "Webhooks|Pipeline events"
msgstr ""
@@ -43610,7 +43994,7 @@ msgid "Webhooks|Push to the repository."
msgstr ""
msgid "Webhooks|Releases events"
-msgstr ""
+msgstr "릴리스 ì´ë²¤íŠ¸"
msgid "Webhooks|SSL verification"
msgstr ""
@@ -43645,6 +44029,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43663,6 +44050,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr "수"
+
msgid "Wednesday"
msgstr "수요ì¼"
@@ -43673,10 +44063,10 @@ msgid "Weeks"
msgstr "주"
msgid "Weight"
-msgstr ""
+msgstr "가중치"
msgid "Weight %{weight}"
-msgstr ""
+msgstr "가중치 %{weight}"
msgid "Welcome back! Your account had been deactivated due to inactivity but is now reactivated."
msgstr ""
@@ -43714,6 +44104,9 @@ msgstr "ì„¤ì •ì€ ì–´ë–¤ ì˜í–¥ì„ 미칩니까?"
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr "GitLab Runner가 무엇입니까?"
+
msgid "What is Markdown?"
msgstr ""
@@ -43727,7 +44120,7 @@ msgid "What is squashing?"
msgstr ""
msgid "What templates can I create?"
-msgstr ""
+msgstr "ì–´ë–¤ í…œí”Œë¦¿ì„ ë§Œë“¤ 수 있습니까?"
msgid "What will you use this group for?"
msgstr ""
@@ -43813,7 +44206,7 @@ msgid "Wiki Page|Rich text"
msgstr ""
msgid "Wiki Page|Source"
-msgstr ""
+msgstr "소스"
msgid "Wiki page"
msgstr ""
@@ -43923,14 +44316,11 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr "취소"
msgid "WikiPage|Commit message"
-msgstr ""
+msgstr "커밋 메시지"
msgid "WikiPage|Content"
msgstr ""
@@ -43945,14 +44335,11 @@ msgid "WikiPage|Format"
msgstr ""
msgid "WikiPage|Learn more."
-msgstr ""
+msgstr "ë” ì•Œì•„ë³´ê¸°."
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr "재시ë„"
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44020,7 +44407,7 @@ msgid "Will deploy to"
msgstr ""
msgid "Wireframe"
-msgstr ""
+msgstr "와ì´ì–´í”„레임"
msgid "With requirements, you can set criteria to check your products against."
msgstr ""
@@ -44040,47 +44427,44 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
-msgstr "WorkItem|ìž‘ì—…ì€ ìž‘ì—…ì„ ë¬¸ì œì™€ ì—°ê²°ëœ ë” ìž‘ì€ ì¡°ê°ìœ¼ë¡œ 나누는 ê¸°ëŠ¥ì„ ì œê³µí•©ë‹ˆë‹¤. ìž‘ì—…ì€ ìƒˆë¡œìš´ %{workItemsLink} 개체를 사용하는 첫 번째 항목입니다. 추가 ìž‘ì—… 항목 ìœ í˜•ì´ ê³§ ì œê³µë  ì˜ˆì •ìž…ë‹ˆë‹¤."
+msgid "WorkItem|%{workItemType} deleted"
+msgstr ""
-msgid "WorkItem|Add a task"
-msgstr "WorkItem|작업 추가"
+msgid "WorkItem|Add"
+msgstr ""
msgid "WorkItem|Add a title"
-msgstr "WorkItem|제목 추가"
+msgstr ""
msgid "WorkItem|Add assignee"
-msgstr ""
+msgstr "ë‹´ë‹¹ìž ì¶”ê°€"
msgid "WorkItem|Add assignees"
-msgstr ""
+msgstr "ë‹´ë‹¹ìž ì¶”ê°€"
msgid "WorkItem|Add task"
-msgstr "WorkItem|작업 추가"
+msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
-msgstr ""
+msgstr "ì •ë§ íŽ¸ì§‘ì„ ì·¨ì†Œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
-msgstr "WorkItem|ì •ë§ ìž‘ì—…ì„ ì‚­ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ? ì´ ìž‘ì—…ì€ ë˜ëŒë¦´ 수 없습니다."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
+msgstr "ì •ë§ë¡œ %{workItemType}ì„ ì‚­ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ? ì´ ìž‘ì—…ì€ ë˜ëŒë¦´ 수 없습니다."
msgid "WorkItem|Assignee"
msgid_plural "WorkItem|Assignees"
-msgstr[0] ""
+msgstr[0] "담당ìž"
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44089,40 +44473,55 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
-msgstr "WorkItem|작업 삭제"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr "%{workItemType} 삭제"
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
msgstr ""
+msgid "WorkItem|Incident"
+msgstr "ì¸ì‹œë˜íŠ¸"
+
msgid "WorkItem|Introducing tasks"
-msgstr "WorkItem|작업 소개"
+msgstr ""
-msgid "WorkItem|Learn about tasks"
-msgstr "WorkItem|ìž‘ì—…ì— ëŒ€í•´ 알아보기"
+msgid "WorkItem|Issue"
+msgstr "ì´ìŠˆ"
+
+msgid "WorkItem|Learn about tasks."
+msgstr "태스í¬ì— 대해 알아보기"
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
+msgid "WorkItem|None"
+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 "최소 리í¬í„° ì—­í•  ì´ìƒì˜ 프로ì íŠ¸ 구성ì›ê³¼ 작성ìž, 그리고 담당ìžë§Œ ì´ ìž‘ì—…ì„ ë³´ê±°ë‚˜ ì•Œë¦¼ì„ ë°›ì„ ìˆ˜ 있습니다."
+
msgid "WorkItem|Open"
msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr "요구 사항"
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
-msgstr "WorkItem|ìž‘ì—…ì„ ìƒì„±í•  ë•Œ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„하십시오"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
+msgstr "%{workItemType} ìƒì„± ì‹œ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„í•´ 주세요."
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
-msgstr ""
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
+msgstr "%{workItemType}ì„ ì‚­ì œí•  ë•Œ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„í•´ 주세요."
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
-msgstr "WorkItem|ìž‘ì—…ì„ ì‚­ì œí•  ë•Œ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„í•´ 주세요."
+msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44135,35 +44534,44 @@ msgid "WorkItem|Something went wrong when trying to add a child. Please try agai
msgstr "WorkItem|하위 ìž‘ì—…ì„ ì¶”ê°€í•˜ëŠ” ë™ì•ˆ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„í•´ 주세요."
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
-msgstr "WorkItem|ìžì‹ì„ ìƒì„±í•˜ëŠ” ë™ì•ˆ 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다시 ì‹œë„í•´ 주세요."
+msgstr ""
+
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
-msgstr "WorkItem|ìž‘ì—… ì‚­ì œë¨"
+msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
+msgid "WorkItem|Test case"
+msgstr "테스트 ì¼€ì´ìŠ¤"
+
+msgid "WorkItem|Turn off confidentiality"
+msgstr "WorkItem|기밀성 해제"
+
msgid "WorkItem|Turn on confidentiality"
-msgstr ""
+msgstr "WorkItem|기밀성 활성화"
-msgid "WorkItem|Type"
+msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Undo"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work item"
msgstr ""
-msgid "WorkItem|work items"
-msgstr "WorkItem|작업 항목"
-
msgid "Would you like to create a new branch?"
msgstr ""
@@ -44192,7 +44600,7 @@ msgid "Write milestone description..."
msgstr ""
msgid "Write your release notes or drag your files here…"
-msgstr ""
+msgstr "릴리스 노트를 작성하거나 여기로 파ì¼ì„ ëŒì–´ë‹¤ 놓으십시오…"
msgid "Wrong extern UID provided. Make sure Auth0 is configured correctly."
msgstr ""
@@ -44213,7 +44621,7 @@ msgid "Yes, add it"
msgstr "네, 추가합니다."
msgid "Yes, close issue"
-msgstr ""
+msgstr "예, ì´ìŠˆë¥¼ 닫습니다."
msgid "Yes, delete project"
msgstr "예, 프로ì íŠ¸ë¥¼ 삭제합니다."
@@ -44225,7 +44633,7 @@ msgid "You"
msgstr ""
msgid "You already have pending todo for this alert"
-msgstr ""
+msgstr "ì´ ì•Œë¦¼ì— ëŒ€í•´ ì´ë¯¸ 보류 ì¤‘ì¸ ìž‘ì—…ì´ ìžˆìŠµë‹ˆë‹¤."
msgid "You are about to add %{usersTag} people to the discussion. They will all receive a notification."
msgstr ""
@@ -44262,13 +44670,13 @@ msgid "You are billed if you exceed this number. %{qsrOverageLinkStart}How does
msgstr ""
msgid "You are connected to the Prometheus server, but there is currently no data to display."
-msgstr ""
+msgstr "Prometheus ì„œë²„ì— ì—°ê²°ë˜ì–´ 있지만 현재 표시할 ë°ì´í„°ê°€ 없습니다."
msgid "You are currently offline, or the GitLab instance is not reachable."
msgstr ""
msgid "You are going to delete %{project_full_name}. Deleted projects CANNOT be restored! Are you ABSOLUTELY sure?"
-msgstr ""
+msgstr "%{project_full_name} 프로ì íŠ¸ë¥¼ 삭제하려고 합니다. ì‚­ì œëœ í”„ë¡œì íŠ¸ëŠ” 절대 복구할 수 없습니다! ì •ë§ ì‚­ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
msgid "You are going to remove %{group_name}. This will also delete all of its subgroups and projects. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?"
msgstr "%{group_name}ì„ ì œê±°í•©ë‹ˆë‹¤. ì´ì— ë”°ë¼ ëª¨ë“  하위 그룹과 프로ì íŠ¸ë„ ì‚­ì œë©ë‹ˆë‹¤. ì œê±°ëœ ê·¸ë£¹ì€ ë³µì›í•  수 없습니다! ì •ë§ë¡œ 제거하시겠습니까?"
@@ -44286,7 +44694,7 @@ msgid "You are going to turn off the confidentiality. This means %{strongStart}e
msgstr ""
msgid "You are going to turn on confidentiality. Only %{context} members with %{strongStart}%{permissions}%{strongEnd} can view or be notified about this %{issuableType}."
-msgstr ""
+msgstr "대외비 ê¸°ëŠ¥ì´ ì¼œì§‘ë‹ˆë‹¤. %{strongStart}%{permissions}%{strongEnd} ì„ ê°€ì§„ %{context} ëª…ì˜ íšŒì›ë§Œì´ ì´ %{issuableType} ì„(를) 보거나 ì•Œë¦¼ì„ ë°›ì„ ìˆ˜ 있습니다."
msgid "You are not allowed to %{action} a user"
msgstr ""
@@ -44384,6 +44792,10 @@ 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 ""
@@ -44444,10 +44856,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr "%{project_name} ì— ìƒˆ 회ì›ì„ 초대하거나 다른 ê·¸ë£¹ì„ ì´ˆëŒ€í•  수 있습니다."
@@ -44467,7 +44875,7 @@ msgid "You can now close this window."
msgstr ""
msgid "You can now export your security dashboard to a CSV report."
-msgstr ""
+msgstr "ì´ì œ 보안 대시보드를 CSV 보고서로 내보낼 수 있습니다."
msgid "You can now submit a merge request to get this change into the original branch."
msgstr ""
@@ -44553,10 +44961,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44652,7 +45056,7 @@ msgid "You have been redirected to the only result; see the %{a_start}search res
msgstr ""
msgid "You have been unsubscribed from this thread."
-msgstr ""
+msgstr "ì´ ìŠ¤ë ˆë“œì—ì„œ 구ë…ì´ ì·¨ì†Œë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "You have declined the invitation to join %{title} %{name}."
msgstr ""
@@ -44720,7 +45124,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -44739,7 +45143,7 @@ msgid "You must be logged in to search across all of GitLab"
msgstr ""
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' variable to 'false' in your pipeline configuration or CI/CD settings. If you need to view this job log, a project maintainer must add you to the project with developer permissions or higher."
-msgstr ""
+msgstr "디버그 추ì ì´ í™œì„±í™”ëœ ê²½ìš° ìž‘ì—… 로그를 보려면 ì—°ê²°ëœ í”„ë¡œì íŠ¸ì—ì„œ ê°œë°œìž ì´ìƒì˜ ê¶Œí•œì´ ìžˆì–´ì•¼ 합니다. 디버그 추ì ì„ 비활성화하려면 파ì´í”„ë¼ì¸ 구성 ë˜ëŠ” CI/CD 설정ì—ì„œ 'CI_DEBUG_TRACE' 변수를 'false'ë¡œ 설정하십시오. ì´ ìž‘ì—… 로그를 확ì¸í•´ì•¼ 하는 경우 프로ì íŠ¸ 관리ìžê°€ ê°œë°œìž ê¶Œí•œ ì´ìƒìœ¼ë¡œ 사용ìžë¥¼ 프로ì íŠ¸ì— 추가해야 합니다."
msgid "You must have maintainer access to force delete a lock"
msgstr "강제로 ìž ê¸ˆì„ ì œê±°í•˜ë ¤ë©´ ê´€ë¦¬ìž ê¶Œí•œì´ ìžˆì–´ì•¼í•©ë‹ˆë‹¤."
@@ -44783,6 +45187,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -44802,7 +45209,7 @@ msgid "You will lose all changes you've made to this file. This action cannot be
msgstr ""
msgid "You will lose all uncommitted changes you've made in this project. This action cannot be undone."
-msgstr ""
+msgstr "ì´ í”„ë¡œì íŠ¸ì—ì„œ 수행한 커밋ë˜ì§€ ì•Šì€ ëª¨ë“  변경 ì‚¬í•­ì„ ìžƒê²Œ ë©ë‹ˆë‹¤. ì´ ìž‘ì—…ì€ ì·¨ì†Œí•  수 없습니다."
msgid "You will need to update your local repositories to point to the new location."
msgstr ""
@@ -44841,10 +45248,10 @@ msgid "You're about to reduce the visibility of the project %{strong_start}%{pro
msgstr ""
msgid "You're at the first commit"
-msgstr ""
+msgstr "ë‹¹ì‹ ì€ ì²« 번째 ì»¤ë°‹ì— ìžˆìŠµë‹ˆë‹¤"
msgid "You're at the last commit"
-msgstr ""
+msgstr "ë‹¹ì‹ ì€ ë§ˆì§€ë§‰ ì»¤ë°‹ì— ìžˆìŠµë‹ˆë‹¤"
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 ""
@@ -44894,9 +45301,6 @@ msgstr ""
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 "ì´ë¯¸ ì¼íšŒìš© ì¸ì¦ê¸°ë¥¼ ì´ìš©í•˜ì—¬ ì´ì¤‘ ì¸ì¦ì„ 활성화했습니다. 다른 기기를 등ë¡í•˜ë ¤ë©´ ì´ì¤‘ ì¸ì¦ì„ 먼저 비활성화해야 합니다."
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -44916,7 +45320,7 @@ msgid "Your %{spammable_entity_type} has been recognized as spam. Please, change
msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription expires on %{strong}%{expires_on}%{strong_close}. If you do not renew, you will lose access to your paid features on %{strong}%{downgrades_on}%{strong_close}. After that date, you can't create issues or merge requests, or use many other features."
-msgstr "ê·€í•˜ì˜ %{strong}%{plan_name}%{strong_close} 구ë…ì€ %{strong}%{expires_on}%{strong_close}ì— ë§Œë£Œë©ë‹ˆë‹¤. 갱신하지 않으면 %{strong}%{downgrades_on}%{strong_close}ì— ìœ ë£Œ ê¸°ëŠ¥ì— ì•¡ì„¸ìŠ¤í•  수 없게 ë©ë‹ˆë‹¤. ê·¸ 날짜 ì´í›„ì—는 ì´ìŠˆë¥¼ ìƒì„±í•˜ê±°ë‚˜ 병합 요청 ë“±ì˜ ë‹¤ë¥¸ ë§Žì€ ê¸°ëŠ¥ì„ ì‚¬ìš©í•  수 없습니다."
+msgstr ""
msgid "Your %{strong}%{plan_name}%{strong_close} subscription for %{strong}%{namespace_name}%{strong_close} will expire on %{strong}%{expires_on}%{strong_close}."
msgstr ""
@@ -44936,6 +45340,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45042,7 +45452,7 @@ msgid "Your changes have been saved"
msgstr ""
msgid "Your changes have been successfully committed."
-msgstr ""
+msgstr "변경 ì‚¬í•­ì´ ì„±ê³µì ìœ¼ë¡œ 커밋ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "Your comment could not be submitted because %{error}"
msgstr ""
@@ -45086,9 +45496,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] "무료 ê·¸ë£¹ì€ ì´ì œ %d 명으로 제한ë©ë‹ˆë‹¤."
@@ -45148,6 +45555,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr "비밀번호 재설정 토í°ì´ 만료ë˜ì—ˆìŠµë‹ˆë‹¤."
@@ -45163,7 +45573,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45182,7 +45592,7 @@ msgid "Your request for access could not be processed: %{error_message}"
msgstr ""
msgid "Your request for access has been queued for review."
-msgstr ""
+msgstr "액세스 ìš”ì²­ì´ ê²€í† ë¥¼ 위해 대기 중입니다."
msgid "Your request to join %{host} has been rejected."
msgstr ""
@@ -45194,10 +45604,10 @@ msgid "Your requirements will be imported in the background. After it's finished
msgstr ""
msgid "Your search didn't match any commits."
-msgstr ""
+msgstr "검색어와 ì¼ì¹˜í•˜ëŠ” ì»¤ë°‹ì´ ì—†ìŠµë‹ˆë‹¤."
msgid "Your search didn't match any commits. Try a different query."
-msgstr ""
+msgstr "검색어와 ì¼ì¹˜í•˜ëŠ” ì»¤ë°‹ì´ ì—†ìŠµë‹ˆë‹¤. 다른 쿼리를 ì‹œë„하십시오."
msgid "Your search timed out"
msgstr ""
@@ -45367,9 +45777,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr "ì—°ê²°ëœ ë¶€ëª¨ëŠ” 기밀ì´ë©° ê¸°ë°€ì´ ì•„ë‹Œ ìžë…€ë¥¼ 가질 수 없습니다."
-
msgid "at"
msgstr ""
@@ -45377,7 +45784,7 @@ msgid "at least the Reporter role"
msgstr ""
msgid "at least the Reporter role, the author, and assignees"
-msgstr ""
+msgstr "최소한 리í¬í„° ì—­í• , ì €ìž‘ìž ë° ë‹´ë‹¹ìž"
msgid "at risk"
msgstr ""
@@ -45386,7 +45793,7 @@ msgid "attach a new file"
msgstr ""
msgid "authored"
-msgstr ""
+msgstr "작성ë¨"
msgid "banned user already exists"
msgstr ""
@@ -45464,6 +45871,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45504,6 +45914,9 @@ msgid "change"
msgid_plural "changes"
msgstr[0] ""
+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 ""
@@ -45552,8 +45965,8 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
-msgstr "ciReport|%{scanner}ê°€ 새로운 잠재 %{vulnStr}를 %{boldStart}%{number}%{boldEnd} ì—ì„œ íƒì§€í•¨"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
+msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr ""
@@ -45748,8 +46161,8 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
-msgstr ""
+msgid "ciReport|Manually added"
+msgstr "ciReport|수ë™ìœ¼ë¡œ 추가ë¨"
msgid "ciReport|New"
msgstr ""
@@ -45852,13 +46265,13 @@ msgid "closed %{timeago}"
msgstr ""
msgid "closed issue"
-msgstr ""
+msgstr "닫힌 ì´ìŠˆ"
msgid "collect usage information"
msgstr ""
msgid "comment"
-msgstr ""
+msgstr "댓글"
msgid "commented on %{link_to_project}"
msgstr "%{link_to_project}ì— ëŒ“ê¸€ì„ ë‹¬ì•˜ìŠµë‹ˆë‹¤."
@@ -45872,9 +46285,6 @@ msgstr "커밋ë¨"
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr "ê¸°ë°€ì´ ì•„ë‹Œ ìžì‹ì´ 있는 경우 기밀 부모를 사용할 수 없습니다."
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -45911,6 +46321,9 @@ msgstr "%{timeAgo} ì „ %{author}ì— ì˜í•´ ìƒì„±ë¨"
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr "ì¼ë³„"
+
msgid "data"
msgstr ""
@@ -46033,6 +46446,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46044,7 +46460,7 @@ msgid "finding is not found or is already attached to a vulnerability"
msgstr ""
msgid "following"
-msgstr ""
+msgstr "팔로잉"
msgid "for"
msgstr ""
@@ -46087,16 +46503,16 @@ msgid "group access tokens"
msgstr ""
msgid "group members"
-msgstr ""
+msgstr "그룹 구성ì›"
msgid "groups"
msgstr "그룹"
msgid "groups and projects"
-msgstr ""
+msgstr "그룹과 프로ì íŠ¸"
msgid "groups only"
-msgstr ""
+msgstr "그룹만"
msgid "had %{count} failed job"
msgid_plural "had %{count} failed jobs"
@@ -46238,6 +46654,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46263,7 +46682,7 @@ msgid "it is larger than %{limit}"
msgstr ""
msgid "it is stored as a job artifact"
-msgstr ""
+msgstr "ìž‘ì—… 아티팩트로 저장ë©ë‹ˆë‹¤."
msgid "it is stored externally"
msgstr ""
@@ -46336,7 +46755,7 @@ msgid "merge request"
msgid_plural "merge requests"
msgstr[0] "머지 리퀘스트(MR)"
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46357,7 +46776,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46518,11 +46937,14 @@ msgid "mrWidget|Mentions issue"
msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
-msgstr "mrWidget|병합 차단ë¨: 요구ë˜ëŠ” 모든 승ì¸ì´ 제공ë˜ì–´ì•¼ 합니다."
+msgstr "머지 차단ë¨: 요구ë˜ëŠ” 모든 승ì¸ì´ 필요합니다."
msgid "mrWidget|Merge blocked: all threads must be resolved."
-msgstr ""
+msgstr "머지 차단ë¨: 모든 스레드를 해결해야 합니다."
msgid "mrWidget|Merge blocked: denied licenses must be removed."
msgstr ""
@@ -46534,7 +46956,7 @@ msgid "mrWidget|Merge blocked: merge conflicts must be resolved."
msgstr ""
msgid "mrWidget|Merge blocked: pipeline must succeed. It's waiting for a manual action to continue."
-msgstr ""
+msgstr "머지 차단ë¨: 파ì´í”„ë¼ì¸ì´ 성공해야 합니다. ìˆ˜ë™ ìž‘ì—…ì´ ê³„ì†ë˜ê¸°ë¥¼ 기다리고 있습니다."
msgid "mrWidget|Merge blocked: pipeline must succeed. Push a commit that fixes the failure, or %{linkStart}learn about other solutions.%{linkEnd}"
msgstr ""
@@ -46653,9 +47075,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -46815,16 +47234,16 @@ msgid "password"
msgstr "패스워드"
msgid "pending comment"
-msgstr ""
+msgstr "ëŒ€ê¸°ì¤‘ì¸ ëŒ“ê¸€"
msgid "pending deletion"
msgstr "삭제 대기 중"
msgid "personal access token"
-msgstr ""
+msgstr "ê°œì¸ ì•¡ì„¸ìŠ¤ 토í°"
msgid "personal access tokens"
-msgstr ""
+msgstr "ê°œì¸ ì•¡ì„¸ìŠ¤ 토í°"
msgid "pipeline"
msgstr "파ì´í”„ë¼ì¸"
@@ -46852,7 +47271,7 @@ msgid_plural "points"
msgstr[0] ""
msgid "previously merged commits"
-msgstr ""
+msgstr "ì´ì „ì— ë¨¸ì§€ëœ ì»¤ë°‹"
msgid "private"
msgstr ""
@@ -46871,7 +47290,7 @@ msgid "project access token"
msgstr ""
msgid "project access tokens"
-msgstr ""
+msgstr "프로ì íŠ¸ 액세스 토í°"
msgid "project bots cannot be added to other groups / projects"
msgstr "프로ì íŠ¸ ë´‡ì€ ë‹¤ë¥¸ 그룹 / 프로ì íŠ¸ì— 추가할 수 없습니다."
@@ -46880,7 +47299,7 @@ msgid "project is read-only"
msgstr ""
msgid "project members"
-msgstr ""
+msgstr "프로ì íŠ¸ 구성ì›"
msgid "project name"
msgstr "프로ì íŠ¸ ì´ë¦„"
@@ -46992,7 +47411,7 @@ msgid "severity|Minor"
msgstr ""
msgid "severity|None"
-msgstr ""
+msgstr "ì—†ìŒ"
msgid "severity|Unknown"
msgstr ""
@@ -47055,19 +47474,19 @@ msgid "success"
msgstr "성공"
msgid "suggestPipeline|1/2: Choose a template"
-msgstr ""
+msgstr "1/2: 템플릿 ì„ íƒ"
msgid "suggestPipeline|2/2: Commit your changes"
-msgstr ""
+msgstr "2/2: 변경 사항 커밋"
msgid "suggestPipeline|Choose %{boldStart}Code Quality%{boldEnd} to add a pipeline that tests the quality of your code."
msgstr ""
msgid "suggestPipeline|The template is ready! You can now commit it to create your first pipeline."
-msgstr ""
+msgstr "í…œí”Œë¦¿ì´ ì¤€ë¹„ë˜ì—ˆìŠµë‹ˆë‹¤! ì´ì œ ì´ë¥¼ 커밋하여 첫 번째 파ì´í”„ë¼ì¸ì„ ìƒì„±í•  수 있습니다."
msgid "suggestPipeline|We’re adding a GitLab CI configuration file to add a pipeline to the project. You could create it manually, but we recommend that you start with a GitLab template that works out of the box."
-msgstr ""
+msgstr "프로ì íŠ¸ì— 파ì´í”„ë¼ì¸ì„ 추가하기 위해 GitLab CI 구성 파ì¼ì„ 추가하고 있습니다. 수ë™ìœ¼ë¡œ ìƒì„±í•  ìˆ˜ë„ ìžˆì§€ë§Œ 기본ì ìœ¼ë¡œ ìž‘ë™í•˜ëŠ” GitLab 템플릿으로 시작하는 ê²ƒì´ ì¢‹ìŠµë‹ˆë‹¤."
msgid "tag name"
msgstr ""
@@ -47211,6 +47630,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr "ë‹¤ìŒ íŒŒì´í”„ë¼ì¸ì´ 성공한 ë’¤ 머지ë˜ë„ë¡ ì˜ˆì•½ë˜ì—ˆìŠµë‹ˆë‹¤."
+msgid "weekly"
+msgstr "매주"
+
msgid "wiki page"
msgstr "위키 페ì´ì§€"
@@ -47227,7 +47649,7 @@ msgid "yaml invalid"
msgstr "유효하지 ì•Šì€ yaml"
msgid "your GitLab instance"
-msgstr ""
+msgstr "ë‹¹ì‹ ì˜ GitLab ì¸ìŠ¤í„´ìŠ¤"
msgid "your group (%{group_name})"
msgstr ""
diff --git a/locale/ku_TR/gitlab.po b/locale/ku_TR/gitlab.po
index 3ddac00b5c3..283bda8ab85 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-08-12 18:05\n"
+"PO-Revision-Date: 2022-09-11 06:23\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/ky_KG/gitlab.po b/locale/ky_KG/gitlab.po
index 3d2e13e3701..688b66f734f 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-08-12 18:06\n"
+"PO-Revision-Date: 2022-09-11 06:24\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/lt_LT/gitlab.po b/locale/lt_LT/gitlab.po
index 1ab9d4fac70..f7a8f2e11f1 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-08-12 18:06\n"
+"PO-Revision-Date: 2022-09-11 06:23\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,13 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -171,6 +178,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -269,6 +283,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -479,6 +500,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -528,13 +556,6 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -610,9 +631,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -1061,12 +1079,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1183,6 +1195,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1205,6 +1220,13 @@ msgstr[3] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1246,9 +1268,6 @@ msgstr[3] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1462,13 +1481,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "(this user)"
msgstr ""
@@ -1856,6 +1868,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1919,6 +1937,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -2087,6 +2111,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2402,7 +2429,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2429,6 +2456,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2468,6 +2498,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2519,6 +2552,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2690,6 +2729,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2705,9 +2747,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2906,15 +2945,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2954,7 +2993,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2978,6 +3017,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -3143,7 +3185,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3368,6 +3413,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -4142,9 +4190,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -4229,6 +4274,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4280,6 +4328,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4418,6 +4469,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4557,9 +4611,6 @@ msgstr[3] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4569,13 +4620,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -5134,6 +5185,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5605,6 +5659,13 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5620,10 +5681,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5641,7 +5708,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5662,6 +5729,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5671,9 +5741,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6472,12 +6539,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6493,12 +6554,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6550,9 +6605,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6596,9 +6648,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6636,6 +6685,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6848,6 +6900,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6974,6 +7035,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -7139,6 +7206,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7395,21 +7465,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7419,9 +7480,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8679,6 +8737,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8706,12 +8770,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8766,6 +8842,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9454,10 +9533,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9628,6 +9707,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9694,6 +9776,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9818,6 +9903,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10399,6 +10487,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10505,6 +10596,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10679,7 +10776,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -11282,9 +11379,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11828,6 +11922,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -12060,6 +12157,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -12138,15 +12241,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -12159,6 +12253,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -12234,6 +12331,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -12261,16 +12361,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -12291,16 +12388,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site name"
-msgstr ""
-
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12369,13 +12463,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12565,7 +12659,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12751,6 +12845,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12802,6 +12899,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13372,6 +13472,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13605,6 +13711,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13698,6 +13807,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -14266,6 +14378,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -14287,6 +14402,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -14302,6 +14423,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14524,6 +14651,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14539,9 +14669,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14566,9 +14693,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14716,6 +14840,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14800,9 +14927,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -15244,6 +15368,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -15280,12 +15407,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15352,18 +15473,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15400,6 +15512,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15433,9 +15548,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15511,6 +15623,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15944,6 +16059,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16700,9 +16818,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16913,13 +17028,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -17069,6 +17184,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -17139,6 +17257,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -17151,15 +17272,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -17308,9 +17422,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17506,6 +17617,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17713,6 +17830,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17875,6 +17995,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -18244,6 +18367,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -18256,6 +18382,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -18274,6 +18406,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -18286,6 +18427,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -18313,6 +18457,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18583,9 +18733,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -19021,9 +19168,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -19051,6 +19195,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19414,13 +19561,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19459,21 +19609,21 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19485,43 +19635,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19530,6 +19674,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19678,6 +19825,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19811,9 +19961,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19847,6 +19994,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19970,9 +20120,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19985,15 +20141,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -20015,6 +20192,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -20084,9 +20264,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -20351,9 +20528,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -21143,6 +21317,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -21161,6 +21338,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -21194,7 +21374,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -21335,9 +21515,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21609,6 +21795,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21921,6 +22110,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21936,6 +22128,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21955,9 +22153,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21982,9 +22177,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -22102,12 +22294,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22426,9 +22624,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22474,6 +22669,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22489,12 +22687,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22552,9 +22744,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22564,9 +22753,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22579,9 +22765,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22876,9 +23059,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -23315,7 +23495,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -23330,6 +23510,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23502,9 +23685,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23556,6 +23736,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -24300,6 +24483,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24588,10 +24774,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -25079,6 +25262,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -25166,6 +25352,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -25241,6 +25430,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25921,16 +26134,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25945,6 +26161,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25987,9 +26206,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -26080,12 +26296,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26613,6 +26823,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26625,9 +26838,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26778,6 +26988,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26799,9 +27012,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26992,6 +27202,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -27134,6 +27347,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -27143,6 +27359,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -27158,6 +27392,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -27188,9 +27434,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -27209,6 +27464,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -27315,6 +27576,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27534,6 +27798,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27555,6 +27825,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27678,6 +27951,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27835,9 +28111,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -28051,6 +28333,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -28087,6 +28372,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -28111,6 +28399,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -28331,6 +28622,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28365,9 +28659,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -28377,12 +28668,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28486,7 +28771,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28735,9 +29020,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28813,6 +29095,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29518,25 +29806,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29548,18 +29827,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29617,12 +29890,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29653,9 +29920,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29755,7 +30019,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29971,6 +30235,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29992,10 +30259,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -30019,6 +30286,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -30040,9 +30310,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30454,9 +30721,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30487,6 +30751,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -31174,6 +31441,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -31186,6 +31456,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -31204,6 +31477,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -31345,6 +31621,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -32155,7 +32434,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -32173,7 +32452,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -32356,9 +32635,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32482,9 +32758,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32717,6 +32990,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -34115,6 +34391,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -34130,6 +34409,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -34178,6 +34463,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -34227,6 +34515,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -34251,6 +34542,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -34296,24 +34590,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34462,9 +34747,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34550,9 +34832,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34571,42 +34862,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34700,6 +34967,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34745,12 +35015,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34760,6 +35045,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -35362,6 +35659,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35617,6 +35917,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -37005,12 +37308,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -37284,6 +37596,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -37374,6 +37689,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37758,6 +38076,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37773,9 +38094,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38514,6 +38832,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39793,6 +40114,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39889,6 +40213,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39901,6 +40231,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39949,10 +40282,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -40057,9 +40390,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -40411,6 +40741,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -40435,7 +40768,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40816,6 +41149,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40828,9 +41164,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40933,6 +41266,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -41143,6 +41479,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -41170,6 +41509,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -41184,6 +41541,12 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -41238,6 +41601,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -41400,9 +41766,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -41427,6 +41790,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -41436,6 +41811,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41544,9 +41922,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41571,9 +41958,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41742,39 +42141,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41880,21 +42252,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -42474,6 +42843,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -42501,6 +42873,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42597,10 +42972,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42666,7 +43041,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42828,6 +43203,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -43231,7 +43609,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43525,10 +43903,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -44230,6 +44611,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -44311,6 +44698,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -44353,6 +44743,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -44410,6 +44803,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -44428,6 +44824,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -44479,6 +44878,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44694,9 +45096,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44721,9 +45120,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44811,10 +45207,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44832,7 +45228,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44845,16 +45241,13 @@ msgstr[3] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44863,19 +45256,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
+msgstr ""
+
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44884,19 +45289,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44911,31 +45319,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -45161,6 +45578,13 @@ 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 ""
@@ -45221,13 +45645,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -45333,13 +45750,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -45506,7 +45916,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45569,6 +45979,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45680,9 +46093,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45722,6 +46132,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45872,9 +46288,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45940,6 +46353,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45955,7 +46371,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -46168,9 +46584,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -46268,6 +46681,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -46311,6 +46727,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+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 ""
@@ -46359,7 +46778,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46567,7 +46986,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46694,9 +47113,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46733,6 +47149,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46861,6 +47280,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -47078,6 +47500,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -47182,7 +47607,7 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -47203,7 +47628,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -47370,6 +47795,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -47505,9 +47933,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -48081,6 +48506,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/mk_MK/gitlab.po b/locale/mk_MK/gitlab.po
index bb0d0640262..126ff9ad4c0 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-08-12 18:06\n"
+"PO-Revision-Date: 2022-09-11 06:23\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/ml_IN/gitlab.po b/locale/ml_IN/gitlab.po
index ee614356863..d3a3b2fdf39 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-08-12 18:09\n"
+"PO-Revision-Date: 2022-09-11 06:25\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/mn_MN/gitlab.po b/locale/mn_MN/gitlab.po
index 4b41257f19f..55de11a66a6 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-08-12 18:06\n"
+"PO-Revision-Date: 2022-09-11 06:23\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/nb_NO/gitlab.po b/locale/nb_NO/gitlab.po
index 309a49f40b7..33065af8318 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-08-12 18:07\n"
+"PO-Revision-Date: 2022-09-11 06:24\n"
msgid " %{start} to %{end}"
msgstr " %{start} til %{end}"
@@ -22,6 +22,11 @@ msgstr " %{start} til %{end}"
msgid " (from %{timeoutSource})"
msgstr " (fra %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr " Samlet inn %{time}"
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] "%d godkjenner (du har godkjent)"
msgstr[1] "%d godkjennere (du har godkjent)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] "%d tildelt sak"
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] "%d bidrag"
msgstr[1] "%d bidrag"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d dag"
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] "%d prosjekt valgt"
msgstr[1] "%d prosjekter valgt"
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] "%d gjenstående"
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] "%d etikett per billednavn"
msgstr[1] "%d etiketter per billednavn"
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] "%d utildelt sak"
-msgstr[1] "%d utildelte saker"
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] "%d uoppklart tråd"
@@ -460,9 +475,6 @@ msgstr "%{actionText} og gjenåpne %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr "%{address} er et ugyldig IP-adresseområde"
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr "%{author_link} klonet %{original_issue} til %{new_issue}."
@@ -824,7 +836,7 @@ msgid "%{mrText}, this issue will be closed automatically."
msgstr "%{mrText}, denne saken vil bli automatisk lukket."
msgid "%{name_with_link} namespace has %{percent} or less Shared Runner Pipeline minutes remaining. Once it runs out, no new jobs or pipelines in its projects will run."
-msgstr ""
+msgstr "%{name_with_link} har %{percent} eller mindre felles 'Runner'-rørledningsminutter igjen. Når den har gått tom, vil ingen nye jobber eller rørledninger i dens tilhørende prosjekter kjøre."
msgid "%{name_with_link} namespace has run out of Shared Runner Pipeline minutes. No new jobs or pipelines in its projects will run."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr "%{percentageUsed}%% brukt"
@@ -1003,6 +1009,9 @@ msgstr "%{size} bytes"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr "%{sourceBranch} til %{targetBranch}"
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr "%{spammable_titlecase} ble vellykket sendt til Akismet."
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr "%{strongOpen}!%{strongClose} SAML-gruppelenker kan føre til at GitLab automatisk fjerner medlemmer fra grupper."
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] "%{strong_start}%{count} medlemmer%{strong_end} må godkjenne sammensl
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr "%{strong_start}%{human_size}%{strong_end} prosjektlagring"
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] "%{strong_start}%{release_count}%{strong_end} utgivelse"
@@ -1264,11 +1275,6 @@ msgstr "(fjernet)"
msgid "(revoked)"
msgstr "(tilbakekalt)"
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] "(squash %d commit)"
-msgstr[1] "(squasher %d commits)"
-
msgid "(this user)"
msgstr "(denne brukeren)"
@@ -1326,13 +1332,13 @@ msgid "- Add or remove a user."
msgstr ""
msgid "- Available to run jobs."
-msgstr ""
+msgstr "- Tilgjengelig for å kjøre jobber."
msgid "- Create or close an issue."
msgstr "- Opprett eller lukk en sak"
msgid "- Create, update, or delete a merge request."
-msgstr ""
+msgstr "- Opprett, oppdater eller slett en fletteforespørsel."
msgid "- Event"
msgid_plural "- Events"
@@ -1340,16 +1346,16 @@ msgstr[0] "- Hendelse"
msgstr[1] "- Hendelser"
msgid "- Go to the Activity page for %{project_name}."
-msgstr ""
+msgstr "- GÃ¥ til Aktivitet-siden for %{project_name}."
msgid "- List the visible events for %{project_name} using the Events API %{events_api_link}."
msgstr ""
msgid "- Not available to run jobs."
-msgstr ""
+msgstr "- Ikke tilgjengelig for å kjøre jobber."
msgid "- Push code to the repository."
-msgstr ""
+msgstr "- Push kode til kodelageret."
msgid "- Select -"
msgstr "- Velg -"
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr "En komplett DevOps-plattform"
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr "En standardgren kan ikke velges for et tomt prosjekt."
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr "En side med den tittelen finnes allerede"
@@ -1833,6 +1851,9 @@ msgstr "folder/openapi.json"
msgid "AWS Access Key"
msgstr "AWS tilgangsnøkkel"
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr "AWS hemmelig tilgangsnøkkel"
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr "Legg til ny tabell"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr "Legg til en tittel …"
@@ -2214,6 +2238,9 @@ msgstr "Legg til kommentar nå"
msgid "Add comment to design"
msgstr "Legg til kommentar til design"
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr "Legg til kommentar …"
@@ -2265,6 +2292,12 @@ msgstr "Legg til nøkkel"
msgid "Add label(s)"
msgstr "Legg til stempel(er)"
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr "Legg til liste"
@@ -2436,6 +2469,9 @@ msgstr "Legger til %{labels} %{label_text}."
msgid "Adds a Zoom meeting."
msgstr "Legger til et Zoom-møte."
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr "Legger til en oppgave."
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr "Juster dine filtre/søkekriterier ovenfor. Hvis du mener at dette kan være en feil, kan du se dokumentasjonen for %{linkStart}Geo-feilsøking%{linkEnd} for mer informasjon."
-
msgid "Admin"
msgstr "Admin"
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr "Auto DevOps-domene"
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr "Konfigurer «Let's Encrypt»"
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr "Det er deg!"
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr "Tillatt"
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr "Tillatte tegn: +, 0-9, -, og mellomrom."
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr "En applikasjon kalt %{link_to_client} ber om tilgang til din GitLab-kont
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr "Et tomt GitLab-brukerfelt vil legge til FogBugz-brukerens fulle navn (f.eks. \"Av John Smith\") i beskrivelsen av alle saker og kommentarer. Det vil også knytte og/eller tildele disse sakene og kommentarene til prosjektskaperen."
@@ -4026,6 +4068,9 @@ msgstr "En feil oppstod under tillegging av godkjennere"
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr "En feil oppstod under innlasting av alle filene."
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr "En feil oppstod under innlasting av diagramdata"
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr "En feil oppstod under abonnering på varsler."
-
msgid "An error occurred while triggering the job."
msgstr "En feil oppstod under trigging av jobben."
@@ -4313,15 +4358,15 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
+msgid "An error occurred while trying to render the content editor. Please try again."
+msgstr ""
+
msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
-msgstr "En feil oppstod under avabonnering på varsler."
-
msgid "An error occurred while updating approvers"
msgstr "En feil oppstod under oppdatering av godkjennere"
@@ -4864,6 +4909,9 @@ msgstr "Godkjenn"
msgid "Approve a merge request"
msgstr "Godkjenn en fletteforespørsel"
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr "Godkjenn den nåværende fletteforespørselen."
@@ -5329,6 +5377,11 @@ msgstr "Denne måneden"
msgid "AuditLogs|User Events"
msgstr "Brukerhendelser"
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr "Valider konto"
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr "Eksporter liste"
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr "Gruppeinvitasjon"
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr "Blokkert sak"
msgid "Blocking"
msgstr "Blokkering"
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr "Hindrende saker"
@@ -6564,6 +6608,15 @@ msgstr "Utvid"
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "Grener"
@@ -6855,6 +6914,9 @@ msgstr "Bla gjennom filer"
msgid "Browse templates"
msgstr "Bla gjennom maler"
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr "Utgivelser"
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr "Lukker denne %{quick_action_target}."
msgid "Cloud Run"
msgstr "Cloud Run"
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr "URL-en som brukes til å få tilgang til Kubernetes-API-en."
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr "Klapp sammen saker"
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr "Klapp sammen milepæler"
@@ -9400,6 +9474,9 @@ msgstr "Kommenter og uoppklar tråden"
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr "Sammenlign GitLab-utgaver"
msgid "Compare Revisions"
msgstr "Sammenlign revisjoner"
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr "Sammenlign endringer"
@@ -10099,6 +10179,9 @@ msgstr "Sammendrag: %{imageId}"
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,8 +10466,8 @@ msgstr "%{created_count} opprettet, %{closed_count} lukket."
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
-msgstr "%{pushes} pushinger, mer enn %{commits} commiter fra %{people} bidragsytere."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
+msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
msgstr ""
@@ -10980,9 +11069,6 @@ msgstr "Opprett gruppen din"
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr "Opprett/importer ditt første prosjekt"
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr "Du har ikke tillatelse til å opprette en undergruppe i denne gruppen."
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr "Avlusingsmeldinger"
msgid "DastProfiles|Delete profile"
msgstr "Slett profil"
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr "Vil du forkaste denne skannerprofilen?"
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr "Vil du forkaste denne nettstedsprofilen?"
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr "Vil du forkaste dine endringer?"
-
msgid "DastProfiles|Edit profile"
msgstr "Rediger profil"
@@ -11855,6 +11941,9 @@ msgstr "Rediger nettstedsprofilen"
msgid "DastProfiles|Enable Authentication"
msgstr "Skru på autentisering"
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr "Passord"
msgid "DastProfiles|Password form field"
msgstr "Passord-skjemafelt"
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr "Skannemodus"
-msgid "DastProfiles|Scanner Profile"
-msgstr "Skannerprofil"
-
-msgid "DastProfiles|Scanner Profiles"
-msgstr "Skanner-profiler"
-
msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profile"
+msgstr ""
+
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr "Nettstedsprofil"
-
-msgid "DastProfiles|Site Profiles"
-msgstr "Nettstedsprofiler"
-
msgid "DastProfiles|Site name"
msgstr "Nettstedsnavn"
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profile"
+msgstr ""
+
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr "Dager"
msgid "Days to merge"
msgstr "Dager igjen til sammenslåing"
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr "Slett fil"
@@ -12496,6 +12585,9 @@ msgstr "Vil du slette utvalget?"
msgid "Delete source branch"
msgstr "Slett kildegrenen"
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr "Slett abonnement"
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr "Design"
msgid "DesignManagement|Discard comment"
msgstr "Forkast kommentar"
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13332,7 +13433,7 @@ msgid "DesignManagement|There was an error moving your designs. Please upload yo
msgstr ""
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 ""
+msgstr "For å laste opp design, må du aktivere LFS og ha en administrator som aktiverer hashet lagring. %{requirements_link_start}Mer informasjon%{requirements_link_end}"
msgid "DesignManagement|Unresolve thread"
msgstr "Uoppklar tråden"
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr "DevOps-adopsjon"
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr "Enheter (valgfritt)"
@@ -13936,6 +14040,9 @@ msgstr "Utkast"
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr "Dra designene dine hit eller %{linkStart}klikk for å laste opp%{linkEnd}."
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr "Tilordne %{issuableAttribute}"
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr "MÃ¥ldato"
@@ -14194,6 +14313,9 @@ msgstr "Rediger wiki-side"
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr "Redigerer"
msgid "Elapsed time"
msgstr "Forløpt tid"
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr "Ingen. Velg navneområder å indeksere."
@@ -14386,6 +14502,9 @@ msgstr "Aktiver Auto DevOps"
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr "Skru på Gitpod"
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr "Epos"
msgid "Epic Boards"
msgstr "Epose-bord"
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr "Eposen ble ikke funnet."
@@ -14950,12 +15069,6 @@ msgstr "Legg til et nytt epos"
msgid "Epics|Add an existing epic"
msgstr "Legg til et eksisterende epos"
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr "For å planlegge en epos sin %{epicDateType} dato basert på milepæler, tilordne en milepæl med %{epicDateType}-dato til enhver sak i eposen."
-
msgid "Epics|Unable to save epic. Please try again"
msgstr "Klarte ikke å lagre eposet. Vennligst prøv igjen"
-msgid "Epics|due"
-msgstr "forfall"
-
-msgid "Epics|start"
-msgstr "start"
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr "Feil under innlasting av grener."
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr "Feil under innlasting av filfremviser."
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr "Utvid saker"
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr "Utvid milepæler"
@@ -16362,9 +16472,6 @@ msgstr "Feb"
msgid "February"
msgstr "Februar"
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr "Rettet opp i:"
msgid "Flags"
msgstr "Flagg"
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr "Offentlig"
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr "Frekvens"
msgid "Frequently searched"
msgstr "Ofte søkt etter"
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr "Fredag"
@@ -16811,13 +16924,8 @@ msgstr "Fra %{code_open}%{source_title}%{code_close} til"
msgid "From %{providerTitle}"
msgstr "Fra %{providerTitle}"
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr "Legg til nettsted"
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr "Juster dine filtre/søkekriterier ovenfor. Hvis du mener at dette kan være en feil, kan du se dokumentasjonen for %{linkStart}Geo-feilsøking%{linkEnd} for mer informasjon."
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr "Aldri"
msgid "Geo|Next sync scheduled at"
msgstr "Neste synkronisering er planlagt kl."
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr "Det er ingen %{replicable_type} å vise"
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr "Git-versjon"
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr "Globale hurtigtaster"
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr "Universelle varslingsinnstillinger"
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr "Saker jeg har opprettet"
@@ -17926,11 +18052,20 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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."
msgid "GlobalSearch|Search GitLab"
-msgstr "GlobalSearch|Søk på GitLab"
+msgstr "Søk på GitLab"
msgid "GlobalSearch|Search for projects, issues, etc."
msgstr "GlobalSearch|Søk etter prosjekter, problemer, osv."
@@ -17938,6 +18073,9 @@ msgstr "GlobalSearch|Søk etter prosjekter, problemer, osv."
msgid "GlobalSearch|Search results are loading"
msgstr "GlobalSearch|Søkeresultater lastes inn"
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr "Det oppsto en feil under henting av forslag til autofullføring av søk."
@@ -17965,6 +18103,12 @@ msgstr "GlobalSearch|i %{scope}"
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr "Gravatar er skrudd på"
msgid "Group"
msgstr "Gruppe"
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr "Tilpassede prosjektmaler"
@@ -19066,13 +19207,16 @@ msgstr "Retningslinje"
msgid "HAR (HTTP Archive)"
msgstr "HAR (HTTP-arkiv)"
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|The image repository could not be found."
-msgstr ""
-
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19206,7 +19347,7 @@ msgid "HeaderAction|incident"
msgstr ""
msgid "HeaderAction|issue"
-msgstr ""
+msgstr "Sak"
msgid "Headers"
msgstr "Meldingshoder"
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] "Skjul diagram"
msgstr[1] "Skjul diagrammer"
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr "Skjul kommentarer på denne filen"
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr "Hvordan konfigurerer jeg denne integrasjonen?"
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr "Jeg aksepterer %{terms_link}"
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr "MÃ¥ltall"
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr "Oppsummering"
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr "Sett inn rad før"
msgid "Insert suggestion"
msgstr "Sett inn forslag"
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
msgstr "Innsikter"
+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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr "Velg en rolle"
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr "Noe gikk galt"
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr "Bruker lisenssetet:"
msgid "Is using seat"
msgstr "Benytter sete"
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr "Lukket"
msgid "IssuableStatus|Closed (%{link})"
msgstr "Lukket (%{link})"
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr "duplisert"
@@ -21953,7 +22145,7 @@ msgid "IssuesAnalytics|Avg/Month:"
msgstr "Gj.snitt/måned:"
msgid "IssuesAnalytics|Issues created"
-msgstr ""
+msgstr "Saker åpnet"
msgid "IssuesAnalytics|Issues created per month"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr "Rediger iterasjon"
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr "Tittel"
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr "Datoer kan ikke overlappe med andre eksisterende iterasjoner i denne gruppen"
@@ -22510,9 +22687,6 @@ msgstr "Jobben har blitt vellykket slettet!"
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22918,7 +23092,7 @@ msgstr[0] ""
msgstr[1] ""
msgid "Label priority"
-msgstr ""
+msgstr "Stempelprioritet"
msgid "Label was created"
msgstr "Stempel ble opprettet"
@@ -22947,8 +23121,8 @@ msgstr "Stempler kan brukes på %{features}. Gruppestempler er tilgjengelige for
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
-msgstr "Stempler kan brukes på saksrapporter og fletteforespørsler."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
+msgstr ""
msgid "Labels with no issues in this iteration:"
msgstr "Stempler uten saker i denne iterasjonen:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr "Å forfremme %{labelTitle} vil gjøre den tilgjengelig for alle prosjekter innenfor %{groupName}. Eksisterende prosjektstempler med samme tittel vil bli slått sammen. Hvis det finnes et gruppestempel med samme tittel, blir den også slått sammen. Denne handlingen kan ikke reverseres."
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr "Språk"
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr "Lær GitLab"
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr "Lær mer"
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23625,7 +23802,7 @@ msgid "Linked incidents or issues"
msgstr ""
msgid "Linked items"
-msgstr ""
+msgstr "Tilknyttede gjenstander"
msgid "LinkedIn"
msgstr "Linkedin"
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr "Mailgun-hendelser"
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr "Vedlikeholdsmodus"
@@ -24066,13 +24246,13 @@ msgid "MarkdownEditor|Add italic text (%{modifier_key}I)"
msgstr "Legg til kursiv tekst (%{modifier_key}I)"
msgid "MarkdownEditor|Add strikethrough text (%{modifierKey}⇧X)"
-msgstr ""
+msgstr "Legg til gjennomstreket tekst (%{modifierKey}⇧X)"
msgid "MarkdownEditor|Add strikethrough text (%{modifier_key}⇧X)"
-msgstr ""
+msgstr "Legg til gjennomstreket tekst (%{modifier_key}⇧X)"
msgid "MarkdownToolbar|Supports %{markdownDocsLinkStart}Markdown%{markdownDocsLinkEnd}"
-msgstr ""
+msgstr "Støtter %{markdownDocsLinkStart}Markdown%{markdownDocsLinkEnd}"
msgid "Marked For Deletion At - %{deletion_time}"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr "Maks antall brukere"
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24658,7 +24835,7 @@ msgid "Merge request %{mr_link} was reviewed by %{mr_author}"
msgstr "Fletteforespørselen %{mr_link} ble gjennomgått av %{mr_author}"
msgid "Merge request actions"
-msgstr ""
+msgstr "Fletteforespørselshandlinger"
msgid "Merge request analytics"
msgstr ""
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr "Fletteforespørsel"
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr "Lagring av kommentaren mislyktes"
@@ -24855,6 +25038,30 @@ msgstr "Ingen filer ble funnet"
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr "Flettet"
@@ -25531,18 +25738,21 @@ msgstr "Modifiser commit-meldinger"
msgid "Modify merge commit"
msgstr "Modifiser innflettings-commiten"
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr "Mandag"
msgid "Monitor"
msgstr "Observer"
+msgid "Monitor GitLab with Prometheus."
+msgstr ""
+
msgid "Monitor Settings"
msgstr "Innstillinger for observering"
-msgid "Monitor the health and performance of GitLab with Prometheus."
-msgstr ""
-
msgid "Monitor your errors by integrating with Sentry."
msgstr "Overvåk feilene dine ved å integrere med Sentry."
@@ -25555,6 +25765,9 @@ msgstr "MÃ¥ned"
msgid "Months"
msgstr "MÃ¥neder"
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr "Mer detaljer"
@@ -25597,9 +25810,6 @@ msgstr "Mest relevant"
msgid "Most stars"
msgstr "Flest stjerner"
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr "Flytt"
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26055,7 +26259,7 @@ msgid "New public deploy key"
msgstr ""
msgid "New related %{issueType}"
-msgstr ""
+msgstr "Ny relatert %{issueType}"
msgid "New release"
msgstr "Ny utgivelse"
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr "Ingen kredittkort nødvendig."
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr "Ingen data funnet"
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr "Ingen forfallsdato"
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr "Ingen kodelager"
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr "Ingen startdato"
-
msgid "No suggestions found"
msgstr ""
@@ -26543,7 +26747,7 @@ msgid "NoteForm|Note"
msgstr "Notis"
msgid "NoteToolbar|Supports %{markdownDocsLinkStart}Markdown%{markdownDocsLinkEnd}. For %{quickActionsDocsLinkStart}quick actions%{quickActionsDocsLinkEnd}, type %{keyboardStart}/%{keyboardEnd}."
-msgstr ""
+msgstr "NoteToolbar|Støtter %{markdownDocsLinkStart}Markdown%{markdownDocsLinkEnd}. For %{quickActionsDocsLinkStart}hurtighandlinger%{quickActionsDocsLinkEnd}, tast %{keyboardEnd}%{keyboardStart}."
msgid "Notes"
msgstr ""
@@ -26570,7 +26774,7 @@ msgid "Notes|Last reply by %{name}"
msgstr ""
msgid "Notes|Make this an internal note"
-msgstr ""
+msgstr "Gjør dette til et internt notat"
msgid "Notes|Show all activity"
msgstr "Vis all historikk"
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr "Ingenting å forhåndsvise."
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr "Varslingshendelser"
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr "Notify |%{member_link} ba om %{member_role}-tilgang til %{target_source_link} %{target_type}."
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr "I rute"
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr "Beskrivelse (valgfritt)"
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr "Bruk eksisterende sideprofil"
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr "Ã…pen: %{open}"
msgid "OpenAPI"
msgstr "OpenAPI"
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr "Ã…pnet"
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr "Pakken finnes allerede"
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr "Pakken ble vellykket slettet"
@@ -27679,6 +27958,9 @@ msgstr "Pakketypen må være NuGet"
msgid "Package type must be PyPi"
msgstr "Pakketypen må være PyPi"
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr "Tillat duplikater"
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr "Fjern pakke"
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr "RubyGems"
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,8 +28351,8 @@ msgstr "npm"
msgid "PackageRegistry|published by %{author}"
msgstr "publisert av %{author}"
-msgid "Packages & Registries"
-msgstr "Pakker og registre"
+msgid "Packages and registries"
+msgstr ""
msgid "Page not found"
msgstr "Siden ble ikke funnet"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr "Optimalisering av ytelsen"
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr "Periode i sekunder"
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr "Permalenke"
@@ -29104,27 +29386,18 @@ msgstr ""
msgid "Pipeline|Date"
msgstr "Dato"
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr "Mislyktes"
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr "Pågår"
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
-msgstr ""
-
msgid "Pipeline|Manual"
msgstr "Manuelt"
+msgid "Pipeline|Merge request pipeline"
+msgstr ""
+
msgid "Pipeline|Merge train pipeline"
msgstr ""
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr "Bestått"
msgid "Pipeline|Pending"
msgstr "Avventer"
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr "Rørledning"
@@ -29203,12 +29470,6 @@ msgstr "Etikettnavn"
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr "Variabler"
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr "Vis rørledning"
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr "Port"
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,12 +29839,12 @@ msgstr "Oppførsel"
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr "Velg mellom fastsatt (maks 1280px) og flytende (%{percentage}) applikasjonsutseende."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
+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 what content you want to see on your homepage."
-msgstr "Velg hva slags innhold du vil se på hjemmesiden din."
-
msgid "Preferences|Color for added lines"
msgstr ""
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr "For eksempel: 30 minutter siden."
msgid "Preferences|Gitpod"
msgstr "Gitpod"
-msgid "Preferences|Homepage content"
-msgstr "Hjemmesideinnhold"
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr "I stedet for alle endrede filer, vis kun én fil om gangen. For å bytte mellom filer, bruk filutforskeren."
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr "Nøkkel"
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr "Ingen fil er valgt."
msgid "Profiles|Notification email"
msgstr "Varsels-E-post"
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr "Organisasjon"
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr "Oppmuntre til"
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr "Alle"
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr "Krav"
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr "%{environment_name} vil være skrivbar for utviklere. Er du sikker?"
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr "Push-hendelser"
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr "Push-regler"
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr "Hurtighjelp"
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr "Registrer med 2-trinnsapp"
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr "Aktiv"
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr "Arkitektur"
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr "Siste kontakt"
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr "Frakoblet"
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr "PÃ¥ nett"
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr "Verdi"
msgid "Runners|Version"
msgstr "Versjon"
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr "gruppe"
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr "satt på pause"
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr "delt"
msgid "Runners|specific"
msgstr "spesifikk"
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr "SSL-verifisering:"
msgid "SSL verification"
msgstr "SSL-verifisering"
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr "Fornøyd"
@@ -34293,12 +34557,27 @@ msgstr "Lagrer"
msgid "Saving project."
msgstr "Lagrer prosjekt."
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "Registreringsbegrensninger"
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr "Noe gikk galt under gjenåpning av et krav."
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr "Standard"
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr "Søndag"
@@ -38894,7 +39209,7 @@ msgid "TestReports|There are no test suites to show."
msgstr ""
msgid "TestReports|There are no tests to display"
-msgstr ""
+msgstr "Det er ingen tester å vise"
msgid "TestReports|There was an error fetching the summary."
msgstr "En feil oppstod under innhenting av oppsummeringen."
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr "Passordet til Jenkins-tjeneren."
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,12 +39778,12 @@ msgstr ""
msgid "There are currently no events."
msgstr "Det er ingen hendelser for øyeblikket."
+msgid "There are currently no mirrored repositories."
+msgstr ""
+
msgid "There are merge conflicts"
msgstr "Flettekonflikter er til stede"
-msgid "There are no %{replicableTypeName} to show"
-msgstr ""
-
msgid "There are no GPG keys associated with this account."
msgstr "Det er ingen GPG-nøkler tilknyttet denne kontoen."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr "Denne blokken refererer til seg selv"
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr "Dette prosjektet er lisensiert som %{strong_start}%{license_name}%{stron
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr "Dette prosjektet vil bli slettet den %{date}"
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40373,7 +40700,7 @@ msgid "This suggestion already matches its content."
msgstr ""
msgid "This title already exists."
-msgstr ""
+msgstr "Denne tittelen finnes allerede."
msgid "This user cannot be unlocked manually from GitLab"
msgstr "Denne brukeren kan ikke låses opp manuelt fra GitLab"
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr "Gjennomstrømming"
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr "Torsdag"
@@ -40645,6 +40975,9 @@ msgstr "nå nettopp"
msgid "Timeago|right now"
msgstr "akkurat nå"
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr "Tidssone"
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "t"
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] "min"
msgstr[1] "mins"
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr "sek"
@@ -40695,7 +41052,7 @@ msgid "Title"
msgstr "Tittel"
msgid "Title (required)"
-msgstr ""
+msgstr "Tittel (påkrevd)"
msgid "Title:"
msgstr "Tittel:"
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr "For å utvide søket, endre eller fjerne filtrene ovenfor"
msgid "To widen your search, change or remove filters above."
msgstr "For å utvide søket, endre eller fjerne filtrene ovenfor."
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr "Gjøremålsliste"
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr "GÃ¥ tilbake"
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr "Emner"
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr "Totalt"
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr "Firmanavn"
-
msgid "Trial|Continue"
msgstr "Fortsett"
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr "Land"
-
msgid "Trial|Dismiss"
msgstr "Avvis"
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr "Antall ansatte"
-
-msgid "Trial|Please select a country"
-msgstr "Vennligst velg et land"
-
-msgid "Trial|Telephone number"
-msgstr "Telefonnummer"
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
+msgid "Tue"
+msgstr ""
+
msgid "Tuesday"
msgstr "Tirsdag"
msgid "Turn off"
msgstr "Slå av"
-msgid "Turn off notifications"
-msgstr ""
-
msgid "Turn on"
msgstr "Slå på"
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr "Twitter"
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr "Ressursbruk på tvers av prosjektene dine"
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr "Bruk en linje per URI"
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr "Brukere kan starte opp et utviklingsmiljø fra en GitLab-nettleserfane hvis %{linkStart}Gitpod%{linkEnd}-integrasjonen er skrudd på."
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr "Vis alle saker"
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr "GÃ¥ til utgreiningen"
msgid "WebIDE|Merge request"
msgstr "Fletteforespørsel"
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr "Sakshendelser"
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr "Nettsted"
msgid "Website:"
msgstr "Nettsted:"
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr "Onsdag"
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr "Hva er Markdown?"
@@ -44180,9 +44576,6 @@ msgstr "Vil du slette siden %{pageTitle}?"
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr "Avbryt"
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr "Sidetittel"
-msgid "WikiPage|Retry"
-msgstr "Prøv igjen"
-
msgid "WikiPage|Save changes"
msgstr "Lagre endringer"
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr "Sak"
+
+msgid "WorkItem|Learn about tasks."
+msgstr ""
+
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
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."
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr "Du har ikke noen abonnementer enda"
@@ -44982,8 +45388,8 @@ 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."
-msgstr "Du har vellykket kjøpt et abonnement på %{plan}-plan for %{seats}. Du mottar en kvittering via e-post."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
+msgstr ""
msgid "You have unsaved changes"
msgstr ""
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 "Du har allerede aktivert 2-trinnsautentisering ved hjelp av éngangspassord-autentikatorer. For å kunne registrere en annen enhet, må du først deaktivere 2-trinnsautentisering."
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr "Du har avvist %{user}"
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr "Ditt første prosjekt"
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr "Din nye kommentar"
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr "Din profil"
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr "tildel deg selv"
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr "kl."
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
msgstr[0] "endre"
msgstr[1] "forandringer"
+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 ""
@@ -45821,7 +46236,7 @@ msgstr "%{reportType}: Innlasting førte til en feil"
msgid "ciReport|%{sameNum} same"
msgstr "%{sameNum} samme"
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr "Administrer lisenser"
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr "forpliktet"
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr "opprettet for %{timeAgo} av %{author}"
msgid "created by"
msgstr "laget av"
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr "data"
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr "er skrivebeskyttet"
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr "er for lang (%{current_value}). Maksstørrelsen er %{max_size}."
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] "fletteforespørsel"
msgstr[1] "fletteforespørsler"
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,8 +47060,8 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
-msgstr "1 innflettings-commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
+msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
msgstr ""
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr "mrWidget|Brukere som kan skrive til kilde- eller målgrenene kan oppklare konfliktene."
@@ -47501,6 +47922,9 @@ msgstr "avfeid"
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr "wikiside"
diff --git a/locale/nl_NL/gitlab.po b/locale/nl_NL/gitlab.po
index 73809756b5a..2a3f0347766 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-08-12 18:06\n"
+"PO-Revision-Date: 2022-09-11 06:23\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "Branches"
@@ -6855,6 +6914,9 @@ msgstr "Door bestanden bladeren"
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr "s"
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/or_IN/gitlab.po b/locale/or_IN/gitlab.po
index feb57e1c1c0..422403bddf4 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-08-12 18:08\n"
+"PO-Revision-Date: 2022-09-11 06:24\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/pa_IN/gitlab.po b/locale/pa_IN/gitlab.po
index 06a0933a0d1..ef106bedba5 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-08-12 18:06\n"
+"PO-Revision-Date: 2022-09-11 06:23\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/pl_PL/gitlab.po b/locale/pl_PL/gitlab.po
index a5d6f8e9f0f..00514d46bf4 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-08-12 18:05\n"
+"PO-Revision-Date: 2022-09-11 06:23\n"
msgid " %{start} to %{end}"
msgstr " %{start} do %{end}"
@@ -22,6 +22,13 @@ msgstr " %{start} do %{end}"
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid " Collected %{time}"
msgstr " Zebrano %{time}"
@@ -171,6 +178,13 @@ msgstr[1] "%d zatwierdzających (zatwierdziłeś)"
msgstr[2] "%d zatwierdzających (zatwierdziłeś)"
msgstr[3] "%d zatwierdzającego (zatwierdziłeś)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -269,6 +283,13 @@ msgstr[1] "%d wkłady"
msgstr[2] "%d wkładów"
msgstr[3] "%d wkładu"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d dzień"
@@ -479,6 +500,13 @@ msgstr[1] "%d wybrane projekty"
msgstr[2] "%d wybranych projektów"
msgstr[3] "%d wybranego projektu"
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -528,13 +556,6 @@ msgstr[1] "%d tagi na nazwÄ™ obrazu"
msgstr[2] "%d tagów na nazwę obrazu"
msgstr[3] "%d tagu na nazwÄ™ obrazu"
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] "%d nieprzypisane zgłoszenie"
-msgstr[1] "%d nieprzypisane zgłoszenia"
-msgstr[2] "%d nieprzypisanych zgłoszeń"
-msgstr[3] "%d nieprzypisanego zgłoszenia"
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -610,9 +631,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr "%{address} nie jest prawidłowym zakresem adresów IP"
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr "%{author_link} sklonował(a) %{original_issue} do %{new_issue}."
@@ -1061,12 +1079,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1183,6 +1195,9 @@ msgstr "%{size} bajtów"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr "%{sourceBranch} do %{targetBranch}"
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr "%{spammable_titlecase} został pomyślnie wysłany do Akisment."
@@ -1205,6 +1220,13 @@ msgstr[3] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1246,9 +1268,6 @@ msgstr[3] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] "%{strong_start}%{release_count}%{strong_end} Wydanie"
@@ -1462,13 +1481,6 @@ msgstr "(usunięto)"
msgid "(revoked)"
msgstr "(unieważnione)"
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "(this user)"
msgstr ""
@@ -1856,6 +1868,12 @@ msgstr "Podstawowy szablon do tworzenia programów Linuksowych przy pomocy Kotli
msgid "A complete DevOps platform"
msgstr "Kompletna platforma DevOps"
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr "Domyślny branch nie może być wybrany dla pustego projektu."
@@ -1919,6 +1937,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr "Niepoufny epik nie może być przypisany do poufnego epika nadrzędnego"
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -2087,6 +2111,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr "Klucz dostępu AWS"
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr "Tajny klucz dostępu AWS"
@@ -2402,7 +2429,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2429,6 +2456,9 @@ msgstr ""
msgid "Add a table"
msgstr "Dodaj tabelÄ™"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2468,6 +2498,9 @@ msgstr "Dodaj teraz komentarz"
msgid "Add comment to design"
msgstr "Dodaj komentarz do projektu"
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2519,6 +2552,12 @@ msgstr "Dodaj klucz"
msgid "Add label(s)"
msgstr "Dodaj etykietÄ™(y)"
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr "Dodaj listÄ™"
@@ -2690,6 +2729,9 @@ msgstr "Dodaje %{labels} %{label_text}."
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr "Dodaje zadanie."
@@ -2705,9 +2747,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr "Dostosuj filtry / kryteria wyszukiwania powyżej. Jeśli uważasz, że może to być błąd, zapoznaj się z %{linkStart}Geo Rozwiązywanie problemów%{linkEnd} dokumentacji, aby uzyskać więcej informacji."
-
msgid "Admin"
msgstr "Administrator"
@@ -2906,15 +2945,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr "Automatyczna domena DevOps"
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2954,7 +2993,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2978,6 +3017,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr "Token kanału informacyjnego"
@@ -3143,7 +3185,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3368,6 +3413,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -4142,9 +4190,6 @@ msgstr "Pozwól użytkownikom na zarejestrowanie GitLaba jako dostawcy OAuth w d
msgid "Allowed"
msgstr "Dozwolone"
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr "Dozwolone znaki: +, 0-9, - i spacje."
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr "Ograniczenia domeny e-mail są dozwolone tylko dla grup najwyższego poziomu"
@@ -4229,6 +4274,9 @@ msgstr "Aplikacja o nazwie %{link_to_client} prosi o dostęp do Twojego konta w
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr "Powiadomienie e-mail zostało niedawno wysłane z panelu administratora. Poczekaj %{wait_time_in_words} przed próbą wysłania kolejnej wiadomości."
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr "Puste pole Użytkownika GitLabu doda imię i nazwisko użytkownika FogBugz (np. \"John Smith\") w opisie wszystkich zgłoszeń i komentarzy. Będzie również kojarzyć te zgłoszenia i komentarze z twórcą projektu i/lub mu je przypisywać."
@@ -4280,6 +4328,9 @@ msgstr "Wystąpił błąd podczas dodawania osób zatwierdzających"
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr "Wystąpił błąd podczas autoryzacji Twojej roli"
@@ -4418,6 +4469,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr "Wystąpił błąd podczas ładowania wszystkich plików."
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr "Wystąpił błąd podczas ładowania danych wykresu"
@@ -4557,9 +4611,6 @@ msgstr[3] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr "Wystąpił błąd podczas subskrybowania powiadomień."
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4569,13 +4620,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -5134,6 +5185,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5605,6 +5659,13 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5620,10 +5681,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5641,7 +5708,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5662,6 +5729,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5671,9 +5741,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6472,12 +6539,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6493,12 +6554,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6550,9 +6605,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6596,9 +6648,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6636,6 +6685,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6848,6 +6900,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6974,6 +7035,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -7139,6 +7206,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7395,21 +7465,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7419,9 +7480,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8679,6 +8737,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8706,12 +8770,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8766,6 +8842,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9454,10 +9533,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9628,6 +9707,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9694,6 +9776,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9818,6 +9903,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10399,6 +10487,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10505,6 +10596,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10679,7 +10776,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -11282,9 +11379,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11828,6 +11922,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -12060,6 +12157,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -12138,15 +12241,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -12159,6 +12253,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -12234,6 +12331,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -12261,16 +12361,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner name"
-msgstr ""
-
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -12291,16 +12388,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12369,13 +12463,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12565,7 +12659,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12751,6 +12845,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12802,6 +12899,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13372,6 +13472,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13605,6 +13711,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13698,6 +13807,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -14266,6 +14378,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -14287,6 +14402,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -14302,6 +14423,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14524,6 +14651,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14539,9 +14669,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14566,9 +14693,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14716,6 +14840,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14800,9 +14927,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -15244,6 +15368,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -15280,12 +15407,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15352,18 +15473,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15400,6 +15512,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15433,9 +15548,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15511,6 +15623,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15944,6 +16059,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16700,9 +16818,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16913,13 +17028,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -17069,6 +17184,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -17139,6 +17257,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -17151,15 +17272,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -17308,9 +17422,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17506,6 +17617,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17713,6 +17830,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17875,6 +17995,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -18244,6 +18367,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -18256,6 +18382,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -18274,6 +18406,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -18286,6 +18427,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -18313,6 +18457,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18583,9 +18733,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -19021,9 +19168,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -19051,6 +19195,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19414,13 +19561,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19459,21 +19609,21 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19485,43 +19635,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19530,6 +19674,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19678,6 +19825,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19811,9 +19961,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19847,6 +19994,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19970,9 +20120,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19985,15 +20141,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -20015,6 +20192,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -20084,9 +20264,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -20351,9 +20528,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -21143,6 +21317,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -21161,6 +21338,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -21194,7 +21374,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -21335,9 +21515,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21609,6 +21795,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21921,6 +22110,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21936,6 +22128,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21955,9 +22153,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21982,9 +22177,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -22102,12 +22294,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22426,9 +22624,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22474,6 +22669,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22489,12 +22687,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22552,9 +22744,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22564,9 +22753,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22579,9 +22765,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22876,9 +23059,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -23315,7 +23495,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -23330,6 +23510,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23502,9 +23685,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23556,6 +23736,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -24300,6 +24483,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24588,10 +24774,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -25079,6 +25262,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -25166,6 +25352,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -25241,6 +25430,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25921,16 +26134,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25945,6 +26161,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25987,9 +26206,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -26080,12 +26296,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26613,6 +26823,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26625,9 +26838,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26778,6 +26988,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26799,9 +27012,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26992,6 +27202,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -27134,6 +27347,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -27143,6 +27359,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -27158,6 +27392,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -27188,9 +27434,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -27209,6 +27464,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -27315,6 +27576,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27534,6 +27798,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27555,6 +27825,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27678,6 +27951,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27835,9 +28111,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -28051,6 +28333,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -28087,6 +28372,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -28111,6 +28399,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -28331,6 +28622,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28365,9 +28659,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -28377,12 +28668,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28486,7 +28771,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28735,9 +29020,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28813,6 +29095,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29518,25 +29806,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29548,18 +29827,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29617,12 +29890,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29653,9 +29920,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29755,7 +30019,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29971,6 +30235,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29992,10 +30259,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -30019,6 +30286,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -30040,9 +30310,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30454,9 +30721,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30487,6 +30751,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -31174,6 +31441,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -31186,6 +31456,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -31204,6 +31477,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -31345,6 +31621,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -32155,7 +32434,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -32173,7 +32452,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -32356,9 +32635,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32482,9 +32758,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32717,6 +32990,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -34115,6 +34391,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -34130,6 +34409,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -34178,6 +34463,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -34227,6 +34515,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -34251,6 +34542,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -34296,24 +34590,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34462,9 +34747,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34550,9 +34832,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34571,42 +34862,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34700,6 +34967,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34745,12 +35015,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34760,6 +35045,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -35362,6 +35659,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35617,6 +35917,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -37005,12 +37308,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -37284,6 +37596,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -37374,6 +37689,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37758,6 +38076,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37773,9 +38094,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr "Oznacz etykietę gwiazdką, by nadać jej status priorytetowy. Zorganizuj etykiety priorytetowe celem zmiany ich względnej ważności, poprzez przeciągnięcie."
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38514,6 +38832,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39793,6 +40114,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39889,6 +40213,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39901,6 +40231,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39949,10 +40282,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -40057,9 +40390,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -40411,6 +40741,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -40435,7 +40768,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40816,6 +41149,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40828,9 +41164,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40933,6 +41266,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -41143,6 +41479,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -41170,6 +41509,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -41184,6 +41541,12 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -41238,6 +41601,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -41400,9 +41766,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -41427,6 +41790,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -41436,6 +41811,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41544,9 +41922,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41571,9 +41958,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41742,39 +42141,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41880,21 +42252,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -42474,6 +42843,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -42501,6 +42873,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42597,10 +42972,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42666,7 +43041,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42828,6 +43203,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -43231,7 +43609,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43525,10 +43903,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -44230,6 +44611,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -44311,6 +44698,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -44353,6 +44743,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -44410,6 +44803,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -44428,6 +44824,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -44479,6 +44878,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44694,9 +45096,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44721,9 +45120,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44811,10 +45207,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44832,7 +45228,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44845,16 +45241,13 @@ msgstr[3] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44863,19 +45256,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
+msgstr ""
+
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44884,19 +45289,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44911,31 +45319,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
+msgstr ""
+
+msgid "WorkItem|Turn off confidentiality"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -45161,6 +45578,13 @@ 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 ""
@@ -45221,13 +45645,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -45333,13 +45750,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -45506,7 +45916,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45569,6 +45979,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45680,9 +46093,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45722,6 +46132,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45872,9 +46288,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45940,6 +46353,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45955,7 +46371,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -46168,9 +46584,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -46268,6 +46681,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -46311,6 +46727,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+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 ""
@@ -46359,7 +46778,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46567,7 +46986,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46694,9 +47113,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46733,6 +47149,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46861,6 +47280,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -47078,6 +47500,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -47182,7 +47607,7 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -47203,7 +47628,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -47370,6 +47795,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -47505,9 +47933,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -48081,6 +48506,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po
index a7542a71150..66f0f9f7698 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-08-12 18:03\n"
+"PO-Revision-Date: 2022-09-11 06:21\n"
msgid " %{start} to %{end}"
msgstr " %{start} até %{end}"
@@ -22,6 +22,11 @@ msgstr " %{start} até %{end}"
msgid " (from %{timeoutSource})"
msgstr " (de %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr " Coletada %{time}"
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] "%d aprovador (você aprovou)"
msgstr[1] "%d aprovadores (você aprovou)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] "%d artefato"
+msgstr[1] "%d artefatos"
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] "%d tarefa não atribuído"
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] "%d contribuição"
msgstr[1] "%d contribuições"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] "%d contribuidor"
+msgstr[1] "%d contribuidores"
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d dia"
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] "%d projeto selecionado"
msgstr[1] "%d projetos selecionados"
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] "%d restantes"
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] "%d tag por nome de imagem"
msgstr[1] "%d tags por nome de imagem"
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] "%d issue não atribuída"
-msgstr[1] "%d issues não atribuídas"
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] "%d tópico não resolvido"
@@ -460,9 +475,6 @@ msgstr "%{actionText} e reabrir %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr "%{address} é uma faixa de IP inválida"
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr "%{author_link} clonou %{original_issue} para %{new_issue}."
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr "%{percentageUsed}%% usados"
@@ -1003,6 +1009,9 @@ msgstr "%{size} bytes"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr "%{sourceBranch} em %{targetBranch}"
+msgid "%{source} %{copyButton} into %{target}"
+msgstr "%{source} %{copyButton} em %{target}"
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr "%{spammable_titlecase} foi enviado para Akismet com sucesso."
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr "%{strong_start}%{human_size}%{strong_end} Armazenamento do projeto"
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr "(removido)"
msgid "(revoked)"
msgstr "(revogado)"
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr "(este usuário)"
@@ -1602,6 +1608,12 @@ msgstr "Um modelo básico para o desenvolvimento de programas Linux usando o Kot
msgid "A complete DevOps platform"
msgstr "Uma plataforma completa de DevOps"
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr "Uma issue confidencial não pode ter um pai que já tenha filhos não confidenciais."
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr "Um branch padrão não pode ser escolhido para um projeto vazio."
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr "Um épico não confidencial não pode ser atribuído a um épico pai confidencial"
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr "Já existe uma página com este nome."
@@ -1833,6 +1851,9 @@ msgstr "folder/openapi.json"
msgid "AWS Access Key"
msgstr "Chave de acesso AWS"
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr "Chave de acesso secreta da AWS"
@@ -2148,8 +2169,8 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr "Adicione uma nota interna confidencial a este %{noteableDisplayName}."
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
-msgstr ""
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
+msgstr "Adicione uma mensagem personalizada com detalhes sobre os executores compartilhados da instância. A mensagem fica visível quando você visualiza executores de projetos e grupos. Markdown é suportado."
msgid "Add a general comment to this %{noteableDisplayName}."
msgstr "Adicionar um comentário geral para este %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr "Adicione um sufixo ao endereço de e-mail da Central de serviços. %{lin
msgid "Add a table"
msgstr "Adicionar uma tabela"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr "Adicionar um título..."
@@ -2214,6 +2238,9 @@ msgstr "Adicionar comentário agora"
msgid "Add comment to design"
msgstr "Adicionar comentário ao design"
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr "Adicionar comentário..."
@@ -2265,6 +2292,12 @@ msgstr "Adicionar chave"
msgid "Add label(s)"
msgstr "Adicionar etiqueta(s)"
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr "Adicionar lista"
@@ -2436,6 +2469,9 @@ msgstr "Adiciona %{labels} %{label_text}."
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr "Adiciona uma tarefa pendente."
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr "Ajuste a frequência com que a interface do usuário do GitLab para atualizações."
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr "Administrador"
@@ -2652,15 +2685,15 @@ msgstr "Todos os novos projetos podem usar os executores compartilhados da instÃ
msgid "AdminSettings|Auto DevOps domain"
msgstr "Domínio de Auto DevOps"
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr "Banir automaticamente usuários que baixam mais do que um número especificado de repositórios em um determinado momento."
-
msgid "AdminSettings|CI/CD limits"
msgstr "Limites de CI/CD"
msgid "AdminSettings|Configure Let's Encrypt"
msgstr "Configurar Let's Encrypt"
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr "Configure limites no número de repositórios que os usuários podem baixar em um determinado momento."
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr "Configurar quando projetos inativos devem ser excluídos automaticamente. %{linkStart}O que são projetos inativos?%{linkEnd}"
@@ -2700,8 +2733,8 @@ msgstr "Ativar recursos de registro"
msgid "AdminSettings|Enable Service Ping"
msgstr "Ativar ping de serviço"
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
-msgstr "Ativar um endpoint do Prometheus que exponha estatísticas de integridade e desempenho. O item de menu Verificação de integridade aparece na seção Monitoramento da Ãrea de administração. É necessário reiniciar. %{link_start}Saiba mais.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
+msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
msgstr "Ativar analisador personalizado de kuromoji: indexação"
@@ -2724,6 +2757,9 @@ msgstr "Ativar analisador personalizado de smartcn: Pesquisa"
msgid "AdminSettings|Enabled"
msgstr "Ativado"
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr "Aplicar fluxo de convite para grupos e projetos"
+
msgid "AdminSettings|Feed token"
msgstr "Token de feed"
@@ -2848,7 +2884,7 @@ msgid "AdminSettings|Session duration for Git operations when 2FA is enabled (mi
msgstr "Duração da sessão para operações do Git quando 2FA está ativado (minutos)"
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 ""
+msgstr "Defina um modelo de CI/CD como a configuração de pipeline necessária para todos os projetos na instância. A configuração de CI/CD do projeto mescla-se com a configuração de pipeline necessária quando o pipeline é executado. %{link_start}O que é uma configuração de pipeline necessária?%{link_end}"
msgid "AdminSettings|Set limit to 0 to disable it."
msgstr "Defina o limite como 0 para desativá-lo."
@@ -2889,8 +2925,11 @@ msgstr "Para ajudar a melhorar o GitLab e sua experiência de usuário, o GitLab
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr "Número total de tarefas em pipelines ativos no momento"
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
-msgstr "Use o Elasticsearch hospedado na AWS com credenciais do IAM"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr "Usar o AWS OpenSearch Service com credenciais do IAM"
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
+msgstr "Usuários e grupos devem aceitar o convite antes de serem adicionados a um grupo ou projeto."
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
msgstr "Quando pausado, o GitLab ainda acompanha as alterações. Isso é útil para migrações de cluster/índice."
@@ -3114,6 +3153,9 @@ msgstr "As issues criadas por este usuário são ocultadas de outros usuários."
msgid "AdminUsers|It's you!"
msgstr "É você!"
+msgid "AdminUsers|LDAP Blocked"
+msgstr "LDAP bloqueado"
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr "Saiba mais sobre %{link_start}usuários banidos.%{link_end}"
@@ -3888,9 +3930,6 @@ msgstr "Permitir que os usuários registrem qualquer aplicativo para usar o GitL
msgid "Allowed"
msgstr "Permitido"
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr "Caracteres permitidos: +, 0-9, - e espaços."
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr "Um aplicativo chamado %{link_to_client} está solicitando acesso à sua
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr "Uma notificação de e-mail foi enviada recentemente do painel de administrador. Aguarde %{wait_time_in_words} antes de tentar enviar outra mensagem."
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr "Um campo vazio do usuário do GitLab adicionará o nome completo do usuário do FogBugz (por exemplo, \"Por John Smith\") na descrição de todos os issues e comentários. Ele também irá associar e/ou atribuir essas issues e comentários ao criador do projeto."
@@ -4026,6 +4068,9 @@ msgstr "Ocorreu um erro ao adicionar aprovadores"
msgid "An error occurred while adding formatted title for epic"
msgstr "Ocorreu um erro ao adicionar o título formatado para épico"
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr "Ocorreu um erro ao autorizar sua função"
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr "Ocorreu um erro ao carregar todos os arquivos."
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr "Ocorreu um erro ao carregar os dados do gráfico"
@@ -4301,9 +4349,6 @@ msgstr[1] "Ocorreu um erro ao salvar as configurações"
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr "Ocorreu um erro ao inscrever às notificações."
-
msgid "An error occurred while triggering the job."
msgstr "Ocorreu um erro ao disparar a tarefa."
@@ -4313,15 +4358,15 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr "Ocorreu um erro ao tentar gerar o relatório. Por favor, tente novamente mais tarde."
+msgid "An error occurred while trying to render the content editor. Please try again."
+msgstr ""
+
msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
-msgstr "Ocorreu um erro ao desinscrever às notificações."
-
msgid "An error occurred while updating approvers"
msgstr "Ocorreu um erro ao atualizar os aprovadores"
@@ -4864,6 +4909,9 @@ msgstr "Aprovar"
msgid "Approve a merge request"
msgstr "Aprovar uma solicitação de mesclagem"
+msgid "Approve merge request"
+msgstr "Aprovar solicitação de mesclagem"
+
msgid "Approve the current merge request."
msgstr "Aprovar o merge request atual."
@@ -5329,6 +5377,11 @@ msgstr "Este mês"
msgid "AuditLogs|User Events"
msgstr "Eventos do Usuário"
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] "%d destino"
+msgstr[1] "%d destinos"
+
msgid "AuditStreams|A header with this name already exists."
msgstr "Já existe um cabeçalho com este nome."
@@ -5344,11 +5397,17 @@ msgstr "Adicionar um valor personalizado"
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr "Adicione um HTTP final para gerenciar registros de auditoria em sistemas de terceiros."
+msgid "AuditStreams|Add another custom header"
+msgstr "Adicionar outro cabeçalho personalizado"
+
msgid "AuditStreams|Add external stream destination"
msgstr "Adicionar destino para stream externo"
-msgid "AuditStreams|Add stream"
-msgstr "Adicionar stream"
+msgid "AuditStreams|Add header"
+msgstr "Adicionar cabeçalho"
+
+msgid "AuditStreams|Add streaming destination"
+msgstr "Adicionar destino de streaming"
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
msgstr "Ocorreu um erro ao criar um destino para fluxo de eventos de auditoria externo. Por favor, tente novamente."
@@ -5365,8 +5424,8 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr "Cancelar edição"
-msgid "AuditStreams|Custom HTTP headers"
-msgstr "Cabeçalhos HTTP personalizados"
+msgid "AuditStreams|Custom HTTP headers (optional)"
+msgstr "Cabeçalhos HTTP personalizados (opcional)"
msgid "AuditStreams|Delete %{link}"
msgstr "Excluir %{link}"
@@ -5386,6 +5445,9 @@ msgstr "Cabeçalho"
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr "Remover cabeçalho personalizado"
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr "Configurar streaming para eventos de auditoria"
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6179,7 +6238,7 @@ msgid "Billings|Free groups are limited to %{number} seats."
msgstr "Grupos gratuitos são limitados a %{number} assentos."
msgid "Billings|Free tier and trial groups can invite a maximum of 20 members per day."
-msgstr ""
+msgstr "Grupos de nível gratuito e de avaliação podem convidar no máximo 20 membros por dia."
msgid "Billings|In a seat"
msgstr "Em um assento"
@@ -6196,12 +6255,6 @@ msgstr "Assentos em uso / Assentos na assinatura"
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr "Executores compartilhados não podem ser ativados até que um cartão de crédito válido seja registrado."
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr "O último proprietário não pode ser removido de um assento."
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr "Validar conta"
msgid "Billings|Validate user account"
msgstr "Validar conta de usuário"
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr "Você não pode alterar o status da licença de um usuário que foi convidado por meio de um grupo ou projeto."
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr "Explorar todos os planos"
msgid "Billing|Export list"
msgstr "Exportar lista"
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr "A partir de 19 de outubro de 2022, os grupos gratuitos serão limitados a 5 membros"
-
msgid "Billing|Group invite"
msgstr "Convite de grupo"
@@ -6318,11 +6362,8 @@ msgstr "Ver aprovações pendentes"
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr "Você está prestes a remover o usuário %{username} de sua assinatura. Se você continuar, o usuário será removido do %{namespace} grupo e de todos os seus subgrupos e projetos. Esta ação não pode ser desfeita."
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
-msgstr ""
+msgstr "Seu grupo mudou recentemente para usar o plano Gratuito. %{over_limit_message} Você pode liberar espaço para novos membros removendo aqueles que não precisam mais de acesso ou alternando-os para exceder o limite. Para obter um número ilimitado de membros, você pode %{link_start}atualizar%{link_end} para um nível pago."
msgid "Bitbucket Server Import"
msgstr "Importação de Servidores Bitbucket"
@@ -6356,6 +6397,9 @@ msgstr "Issue bloqueada"
msgid "Blocking"
msgstr "Bloqueando"
+msgid "Blocking epics"
+msgstr "Bloqueando épicos"
+
msgid "Blocking issues"
msgstr "Issues bloqueadas"
@@ -6564,6 +6608,15 @@ msgstr "Expandir"
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr "Mover cartão"
+
+msgid "Boards|Move to end of list"
+msgstr "Mover para o final da lista"
+
+msgid "Boards|Move to start of list"
+msgstr "Mover para o início da lista"
+
msgid "Boards|New board"
msgstr "Novo painel"
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "Ramificações"
@@ -6855,6 +6914,9 @@ msgstr "Navegar pelos arquivos"
msgid "Browse templates"
msgstr "Navegar por modelos"
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr "Estatísticas de versão"
msgid "CICDAnalytics|Releases"
msgstr "Versões"
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr "Uso de executores compartilhados"
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr "Duração do executor compartilhado é o tempo de execução total de todas as tarefas executadas em executores compartilhados"
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr "Duração de minutos do pipeline do executor compartilhado por mês"
-msgid "CICDAnalytics|Shared runner usage"
-msgstr "Uso de executor compartilhado"
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr "Uso do executor compartilhado é o tempo de execução total de todas as tarefas executadas em executores compartilhados"
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr "Algo deu errado ao obter estatísticas da versão"
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr "Qual é a duração do corredor compartilhado?"
-msgid "CICDAnalytics|What is shared runner usage?"
-msgstr "Uso de executor compartilhado?"
-
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 "Adicione um %{base_domain_link_start}domínio base%{link_end} para o seu %{kubernetes_cluster_link_start}cluster Kubernetes%{link_end} para que a sua estratégia de deploy funcione."
@@ -8387,6 +8437,12 @@ msgstr "Fecha este %{quick_action_target}."
msgid "Cloud Run"
msgstr "Cloud Run"
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr "Armazenamento em nuvem"
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr "Configuração"
+msgid "CloudSeed|Create MySQL Instance"
+msgstr "Criar instância do MySQL"
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr "Criar instância do Postgres"
+
msgid "CloudSeed|Create cluster"
msgstr "Criar cluster"
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr "O certificado do Kubernetes usado para autenticar no cluster."
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr "A URL usada para acessar a API do Kubernetes."
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr "Ocultar issues"
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr "Ocultar marcos"
@@ -9400,6 +9474,9 @@ msgstr "Comentar & reverter resolução do tópico"
msgid "Comment '%{label}' position"
msgstr "Comentário '%{label}' na posição"
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr "Posição do formulário de comentários"
@@ -9522,6 +9599,9 @@ msgstr "Comparar as edições do GitLab"
msgid "Compare Revisions"
msgstr "Comparar revisões"
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr "Comparar alterações"
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr "Erro de conexão do Docker"
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr "Ativar política de expiração"
@@ -10203,6 +10286,12 @@ msgstr "Executar limpeza:"
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr "Economize espaço de armazenamento excluindo automaticamente as tags do registro do contêiner e mantendo as que você deseja. %{linkStart}Como funciona a limpeza%{linkEnd}"
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr "Configurar limpeza"
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr "Criar seu grupo"
msgid "Create, update, or delete a merge request."
msgstr "Criar, atualizar ou excluir uma solicitação de mesclagem."
-msgid "Create/import your first project"
-msgstr "Criar/importar seu primeiro projeto"
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr "Você não tem permissão para criar um subgrupo neste grupo."
@@ -11526,6 +11612,9 @@ msgstr "'%{name}' está coletando os dados. Isso pode levar alguns minutos."
msgid "CycleAnalytics|Average time to completion"
msgstr "Média de dias para conclusão"
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr "Média de dias para conclusão (dias)"
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11536,7 +11625,7 @@ msgid "CycleAnalytics|Create a custom value stream…"
msgstr "Criar um fluxo de valor personalizado…"
msgid "CycleAnalytics|Custom value streams to measure your DevSecOps lifecycle"
-msgstr ""
+msgstr "Fluxos de valor personalizados para medir seu ciclo de vida do DevSecOps"
msgid "CycleAnalytics|Data is collecting and loading."
msgstr "Os dados estão sendo coletados e carregados."
@@ -11586,7 +11675,7 @@ msgid "CycleAnalytics|The total time items spent across each value stream stage.
msgstr "Os itens de tempo médio gastos neste estágio. Dados limitados a itens concluídos neste intervalo de datas."
msgid "CycleAnalytics|There is no data for 'Stage time' available. Adjust the current filters."
-msgstr ""
+msgstr "Não há dados disponíveis para 'Tempo de estágio'. Ajuste os filtros atuais."
msgid "CycleAnalytics|There is no data for 'Total time' available. Adjust the current filters."
msgstr "Não há dados disponíveis para 'Tempo total'. Ajuste os filtros atuais."
@@ -11754,6 +11843,12 @@ msgid "DastConfig|Not enabled"
msgstr "Não ativado"
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."
+
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
msgstr ""
msgid "DastProfiles|AJAX spider"
@@ -11772,7 +11867,7 @@ msgid "DastProfiles|Additional request headers (optional)"
msgstr "Cabeçalhos adicionais de requisição (opcional)"
msgid "DastProfiles|Are you sure you want to delete this profile?"
-msgstr ""
+msgstr "Você tem certeza que deseja excluir este perfil?"
msgid "DastProfiles|Attacks the target to find potential vulnerabilities. Active scans are potentially harmful to the site being scanned."
msgstr "Ataca o alvo para encontrar vulnerabilidades em potencial. As verificações ativas são potencialmente prejudiciais ao site que está sendo verificado."
@@ -11799,25 +11894,25 @@ msgid "DastProfiles|Could not create the scanner profile. Please try again."
msgstr "Não foi possível criar o perfil da verificação. Por favor, tente novamente."
msgid "DastProfiles|Could not create the site profile. Please try again."
-msgstr ""
+msgstr "Não foi possível criar o perfil do site. Por favor, tente novamente."
msgid "DastProfiles|Could not delete scanner profile. Please refresh the page, or try again later."
msgstr ""
msgid "DastProfiles|Could not delete scanner profiles:"
-msgstr ""
+msgstr "Não foi possível excluir os perfis da verificação:"
msgid "DastProfiles|Could not delete site profile. Please refresh the page, or try again later."
msgstr ""
msgid "DastProfiles|Could not delete site profiles:"
-msgstr ""
+msgstr "Não foi possível excluir perfis de site:"
msgid "DastProfiles|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
-msgstr ""
+msgstr "Não foi possível buscar perfis de site. Por favor, atualize a página ou tente novamente mais tarde."
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr "Mensagens de depuração"
msgid "DastProfiles|Delete profile"
msgstr "Excluir perfil"
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr "Editar perfil"
@@ -11855,6 +11941,9 @@ msgstr "Editar perfil de site"
msgid "DastProfiles|Enable Authentication"
msgstr "Ativar autenticação"
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr "Ativar autenticação básica"
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr "Insira as URLs em uma lista separada por vírgulas."
@@ -11883,7 +11972,7 @@ msgid "DastProfiles|Include debug messages in the DAST console output."
msgstr "Incluir mensagens de depuração (debug) no console de saída do DAST."
msgid "DastProfiles|Manage %{profileType} profiles"
-msgstr ""
+msgstr "Gerenciar %{profileType} perfis"
msgid "DastProfiles|Manage profiles"
msgstr "Gerenciar perfis"
@@ -11892,10 +11981,10 @@ msgid "DastProfiles|Manage site profiles"
msgstr "Gerenciar perfis de site"
msgid "DastProfiles|Minimum = 0 (no timeout enabled), Maximum = 2880 minutes"
-msgstr ""
+msgstr "Mínimo = 0 (sem tempo limite habilitado), Máximo = 2880 minutos"
msgid "DastProfiles|Minimum = 1 second, Maximum = 3600 seconds"
-msgstr ""
+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."
@@ -11930,6 +12019,9 @@ msgstr "Senha"
msgid "DastProfiles|Password form field"
msgstr "Campo de formulário de senha"
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr "Perfil em uso e não pode ser renomeado"
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr "O perfil está sendo usado por esta verificação sob demanda"
@@ -11943,7 +12035,7 @@ msgid "DastProfiles|Request headers"
msgstr "Cabeçalhos de requisição"
msgid "DastProfiles|Run the AJAX spider, in addition to the traditional spider, to crawl the target site."
-msgstr ""
+msgstr "Executar o spider AJAX, além do spider tradicional, para desmantelar a página alvo."
msgid "DastProfiles|Save commonly used configurations for target sites and scan specifications as profiles. Use these with an on-demand scan."
msgstr "Salvar configurações comumente usadas para sites de destino e especificações de verificação como perfis. Use-os com uma verificação sob demanda."
@@ -11957,17 +12049,14 @@ msgstr "Método de verificação"
msgid "DastProfiles|Scan mode"
msgstr "Modo de verificação"
-msgid "DastProfiles|Scanner Profile"
-msgstr "Perfil de verificação"
-
-msgid "DastProfiles|Scanner Profiles"
-msgstr "Perfis de verificação"
-
msgid "DastProfiles|Scanner name"
msgstr "Nome da verificação"
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
-msgstr "Os perfis de verificação definem os detalhes de configuração de uma verificação de segurança. %{linkStart}Saiba mais%{linkEnd}."
+msgid "DastProfiles|Scanner profile"
+msgstr ""
+
+msgid "DastProfiles|Scanner profiles"
+msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
msgstr "Selecione um perfil de verificação para executar uma verificação de DAST"
@@ -11987,23 +12076,20 @@ msgstr "Selecione o perfil do site"
msgid "DastProfiles|Show debug messages"
msgstr "Mostrar mensagens de depuração"
-msgid "DastProfiles|Site Profile"
-msgstr "Perfil de site"
-
-msgid "DastProfiles|Site Profiles"
-msgstr "Perfis de site"
-
msgid "DastProfiles|Site name"
msgstr "Nome do site"
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
-msgstr "Os perfis do site definem os atributos e detalhes de configuração do seu aplicativo implantado, site ou API. %{linkStart}Saiba mais%{linkEnd}."
+msgid "DastProfiles|Site profile"
+msgstr ""
+
+msgid "DastProfiles|Site profiles"
+msgstr ""
msgid "DastProfiles|Site type"
msgstr "Tipo de site"
msgid "DastProfiles|Spider timeout"
-msgstr ""
+msgstr "Tempo limite do Spider"
msgid "DastProfiles|Submit button"
msgstr ""
@@ -12018,10 +12104,10 @@ msgid "DastProfiles|Target timeout"
msgstr "Tempo limite de destino"
msgid "DastProfiles|The maximum number of minutes allowed for the spider to traverse the site."
-msgstr ""
+msgstr "O número máximo de minutos permitidos para o spider atravessar o site."
msgid "DastProfiles|The maximum number of seconds allowed for the site under test to respond to a request."
-msgstr ""
+msgstr "O número máximo de segundos permitidos para o site em teste responder a uma solicitação."
msgid "DastProfiles|This profile is currently being used in a policy."
msgstr "Este perfil está atualmente sendo usado em uma política."
@@ -12033,13 +12119,13 @@ msgid "DastProfiles|This site profile is currently being used by a policy. To ma
msgstr ""
msgid "DastProfiles|Turn on AJAX spider"
-msgstr ""
+msgstr "Ativar o spider AJAX"
msgid "DastProfiles|URL"
msgstr "URL"
msgid "DastProfiles|URLs to skip during the authenticated scan."
-msgstr ""
+msgstr "URLs para ignorar durante o escaneamento autenticado."
msgid "DastProfiles|Username"
msgstr "Nome de usuário"
@@ -12057,7 +12143,7 @@ msgid "DastProfiles|Website"
msgstr "Site"
msgid "DastProfiles|What does each method do?"
-msgstr ""
+msgstr "O que cada método faz?"
msgid "DastProfiles|You can either choose a passive scan or validate the target site from the site profile management page. %{docsLinkStart}Learn more about site validation.%{docsLinkEnd}"
msgstr ""
@@ -12065,14 +12151,14 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
-msgstr ""
+msgid "DastProfiles|https://example.com/dast_example.har"
+msgstr "https://example.com/dast_example.har"
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
-msgstr ""
+msgid "DastProfiles|https://example.com/openapi.json"
+msgstr "https://example.com/openapi.json"
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
-msgstr ""
+msgid "DastProfiles|https://example.com/postman_collection.json"
+msgstr "https://example.com/postman_collection.json"
msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr ""
@@ -12259,8 +12345,8 @@ msgstr "Dias"
msgid "Days to merge"
msgstr "Dias para mesclar"
-msgid "Deactivate dormant users after 90 days of inactivity"
-msgstr "Desativar usuários inativos após 90 dias de inatividade"
+msgid "Deactivate dormant users after a period of inactivity"
+msgstr "Desativar usuários inativos após um período de inatividade"
msgid "Dear Administrator,"
msgstr "Caro Administrador,"
@@ -12445,6 +12531,9 @@ msgstr "Excluir corpus"
msgid "Delete deploy key"
msgstr "Excluir chave de implantação"
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr "Excluir arquivo"
@@ -12496,6 +12585,9 @@ msgstr "Excluir snippet?"
msgid "Delete source branch"
msgstr "Excluir ramificação de origem"
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr "Excluir assinatura"
@@ -12689,10 +12781,10 @@ msgid "Dependencies|The %{codeStartTag}dependency_scanning%{codeEndTag} job has
msgstr "A tarefa %{codeStartTag}dependency_scanning%{codeEndTag} falhou e não pode gerar a lista. Certifique-se de que a tarefa esteja funcionando corretamente e execute o pipeline novamente."
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 ""
+msgstr "O caminho de dependência do componente é baseado no arquivo de bloqueio. Pode haver vários caminhos. Nesses casos, o caminho mais longo é exibido."
msgid "Dependencies|There may be multiple paths"
-msgstr ""
+msgstr "Pode haver vários caminhos"
msgid "Dependencies|Toggle vulnerability list"
msgstr "Alternar lista de vulnerabilidades"
@@ -12719,7 +12811,7 @@ msgid "Dependency list"
msgstr "Lista de dependências"
msgid "DependencyProxy|All items in the cache are scheduled for removal."
-msgstr ""
+msgstr "Todos os itens no cache estão agendados para remoção."
msgid "DependencyProxy|Cached %{time}"
msgstr ""
@@ -13034,7 +13126,7 @@ msgid "DeploymentApproval|Approval options"
msgstr "Opções de aprovação"
msgid "DeploymentApproval|Approve or reject deployment #%{deploymentIid}"
-msgstr ""
+msgstr "Aprovar ou rejeitar a implantação #%{deploymentIid}"
msgid "DeploymentApproval|Approved %{time}"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr "Ambiente: %{environment}"
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr "Tarefa manual: %{jobName}"
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr "Rejeitado %{time}"
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr "Rejeitado por você %{time}"
+
msgid "DeploymentTarget|GitLab Pages"
msgstr "GitLab Pages"
@@ -13283,6 +13381,9 @@ msgstr "Designs"
msgid "DesignManagement|Discard comment"
msgstr "Descartar comentário"
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr "Baixar design"
@@ -13335,7 +13436,7 @@ msgid "DesignManagement|To upload designs, you'll need to enable LFS and have an
msgstr ""
msgid "DesignManagement|Unresolve thread"
-msgstr ""
+msgstr "Tópico não resolvido"
msgid "DesignManagement|Upload designs"
msgstr "Enviar designs"
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr "Adoção de DevOps"
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr "Dispositivos (opcional)"
@@ -13383,7 +13487,7 @@ msgid "DevopsAdoption|%{adoptedCount}/%{featuresCount} %{title} features adopted
msgstr "%{adoptedCount}/%{featuresCount} %{title} recursos adotados"
msgid "DevopsAdoption|Add a group to get started"
-msgstr ""
+msgstr "Adicione um grupo para começar"
msgid "DevopsAdoption|Add or remove groups"
msgstr "Adicionar ou remover grupos"
@@ -13410,7 +13514,7 @@ msgid "DevopsAdoption|Approvals"
msgstr "Aprovações"
msgid "DevopsAdoption|Are you sure that you would like to remove %{name} from the table?"
-msgstr ""
+msgstr "Tem certeza de que deseja remover %{name} da tabela?"
msgid "DevopsAdoption|At least one approval on a merge request"
msgstr "Pelo menos uma aprovação em uma solicitação de mesclagem"
@@ -13476,7 +13580,7 @@ msgid "DevopsAdoption|Issues"
msgstr "Issues"
msgid "DevopsAdoption|MRs"
-msgstr ""
+msgstr "MRs"
msgid "DevopsAdoption|No results…"
msgstr "Sem resultados…"
@@ -13536,7 +13640,7 @@ msgid "DevopsAdoption|You cannot remove the group you are currently in."
msgstr ""
msgid "DevopsReport|DevOps Score"
-msgstr ""
+msgstr "Pontuação de DevOps"
msgid "DevopsReport|DevOps score metrics are based on usage over the last 30 days. Last updated: %{timestamp}."
msgstr ""
@@ -13740,7 +13844,7 @@ msgid "Discuss a specific suggestion or question internally that needs to be res
msgstr ""
msgid "Discuss a specific suggestion or question internally."
-msgstr ""
+msgstr "Discuta uma sugestão ou pergunta específica internamente."
msgid "Discuss a specific suggestion or question that needs to be resolved."
msgstr "Discutir uma sugestão ou pergunta específica que precisa ser resolvida."
@@ -13856,7 +13960,7 @@ msgid "Don't paste the private part of the GPG key. Paste the public part which
msgstr "Não cole a parte privada da chave GPG. Cole a parte pública que começa com '-----BEGIN PGP PUBLIC KEY BLOCK-----'."
msgid "Don't send service data"
-msgstr ""
+msgstr "Não enviar dados de serviço"
msgid "Don't show again"
msgstr "Não exibir novamente"
@@ -13936,6 +14040,9 @@ msgstr "Rascunho"
msgid "Draft: %{filename}"
msgstr "Rascunho: %{filename}"
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr "Arraste para reordenar as etiquetas priorizadas e alterar sua prioridade relativa."
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr "Arraste seus projetos aqui ou %{linkStart}clique para enviar%{linkEnd}."
@@ -13955,6 +14062,12 @@ msgid "DropdownWidget|An error occurred while fetching the assigned %{issuableAt
msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
+msgstr "Atribuir %{issuableAttribute}"
+
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
msgstr ""
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr "Validade"
@@ -13985,7 +14104,7 @@ msgid "Due to inactivity, the %{project_name} (%{project_link}) project is sched
msgstr ""
msgid "Due to inactivity, this project is scheduled to be deleted on %{deletion_date}. %{link_start}Why is this scheduled?%{link_end}"
-msgstr ""
+msgstr "Devido à inatividade, este projeto está programado para ser excluído em %{deletion_date}. %{link_start}Por que isso está programado?%{link_end}"
msgid "Duplicate page: %{error_message}"
msgstr "Página duplicada: %{error_message}"
@@ -14081,7 +14200,7 @@ msgid "Edit Password"
msgstr "Editar senha"
msgid "Edit Pipeline Schedule"
-msgstr ""
+msgstr "Alterar agendamento do pipeline"
msgid "Edit Release"
msgstr "Editar versão"
@@ -14186,7 +14305,7 @@ msgid "Edit user: %{user_name}"
msgstr "Editar usuário: %{user_name}"
msgid "Edit video description"
-msgstr ""
+msgstr "Editar descrição do vídeo"
msgid "Edit wiki page"
msgstr "Editar página da wiki"
@@ -14194,6 +14313,9 @@ msgstr "Editar página da wiki"
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr "Editar seu comentário mais recente em um tópico (a partir de uma área de texto vazia)"
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr "Editando"
msgid "Elapsed time"
msgstr "Tempo decorrido"
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr "Nenhum. Selecione espaços de nome para indexar."
@@ -14386,6 +14502,9 @@ msgstr "Ativar Auto DevOps"
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr "Ativar Gitpod"
@@ -14408,7 +14527,7 @@ msgid "Enable SSL verification"
msgstr "Ativar verificação SSL"
msgid "Enable Sentry error tracking"
-msgstr ""
+msgstr "Ativar rastreamento de erros do Sentry"
msgid "Enable Snowplow tracking"
msgstr ""
@@ -14450,7 +14569,7 @@ msgid "Enable container expiration and retention policies for projects created e
msgstr ""
msgid "Enable container expiration caching."
-msgstr ""
+msgstr "Ativar o cache de expiração de contêiner."
msgid "Enable email notification"
msgstr "Ativar notificações por e-mail"
@@ -14470,14 +14589,11 @@ msgstr "Ativar executores de grupo"
msgid "Enable header and footer in emails"
msgstr "Ativar cabeçalho e rodapé em e-mails"
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
-msgstr ""
+msgstr "Ativar e-mails de marketing no produto"
msgid "Enable incident management inbound alert limit"
-msgstr ""
+msgstr "Ativar limite de alerta de entrada de gerenciamento de incidentes"
msgid "Enable integration"
msgstr "Ativar integração"
@@ -14914,6 +15030,9 @@ msgstr "Épico"
msgid "Epic Boards"
msgstr "Quadros épicos"
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr "Épico não pode ser encontrado."
@@ -14950,12 +15069,6 @@ msgstr "Adicionar um novo épico"
msgid "Epics|Add an existing epic"
msgstr "Adicionar um épico existente"
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr "Ocorreu um erro ao salvar a data de %{epicDateType}"
-
-msgid "Epics|An error occurred while updating labels."
-msgstr "Ocorreu um erro ao atualizar as etiquetas."
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr "Você tem certeza de que quer remover %{bStart}%{targetIssueTitle}%{bEnd} de %{bStart}%{parentEpicTitle}%{bEnd}?"
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr "Isto também irá remover quaisquer descendentes de %{bStart}%{targetEpicTitle}%{bEnd} de %{bStart}%{parentEpicTitle}%{bEnd}. Você tem certeza?"
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr "Para agendar o seu épico %{epicDateType} baseado em marcos temporais, atribua um marco com uma data %{epicDateType} a qualquer issue no épico."
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr "prazo"
-
-msgid "Epics|start"
-msgstr "Iniciar"
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr "Erro ao carregar as ramificações."
msgid "Error loading burndown chart data"
msgstr "Erro ao carregar dados do gráfico de burndown"
-msgid "Error loading countries data."
-msgstr "Erro ao carregar dados do país."
-
msgid "Error loading file viewer."
msgstr "Erro ao carregar o visualizador de arquivos."
@@ -15181,6 +15285,9 @@ msgstr "Ocorreu um erro. O usuário não foi desbloqueado"
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr "Erro ao renderizar a pré-visualização do Markdown"
@@ -15410,7 +15517,7 @@ msgid "EscalationPolicies|This policy has no escalation rules."
msgstr ""
msgid "EscalationPolicies|mins"
-msgstr ""
+msgstr "mins"
msgid "Estimate"
msgstr "Estimativa"
@@ -15452,7 +15559,7 @@ msgid "Events"
msgstr "Eventos"
msgid "Events API"
-msgstr ""
+msgstr "API de eventos"
msgid "Every %{action} attempt has failed: %{job_error_message}. Please try again."
msgstr "Cada tentativa de %{action} falhou: %{job_error_message}. Por favor, tente novamente."
@@ -15461,13 +15568,13 @@ msgid "Every 3 months"
msgstr "A cada 3 meses"
msgid "Every 3 months on the %{day} at %{time} %{timezone}"
-msgstr ""
+msgstr "A cada 3 meses no %{day} às %{time} %{timezone}"
msgid "Every 6 months"
msgstr "A cada 6 meses"
msgid "Every 6 months on the %{day} at %{time} %{timezone}"
-msgstr ""
+msgstr "A cada 6 meses no %{day} às %{time} %{timezone}"
msgid "Every day"
msgstr "Todos os dias"
@@ -15476,7 +15583,7 @@ msgid "Every day (at %{time})"
msgstr "Todos os dias (às %{time})"
msgid "Every day at %{time} %{timezone}"
-msgstr ""
+msgstr "Todos os dias às %{time} %{timezone}"
msgid "Every month"
msgstr "Todo mês"
@@ -15485,7 +15592,7 @@ msgid "Every month (Day %{day} at %{time})"
msgstr "Todos os meses (dia %{day} em %{time})"
msgid "Every month on the %{day} at %{time} %{timezone}"
-msgstr ""
+msgstr "Todo mês em %{day} às %{time} %{timezone}"
msgid "Every three months"
msgstr "A cada três meses"
@@ -15502,7 +15609,7 @@ msgid "Every week (%{weekday} at %{time})"
msgstr "Todas as semanas (%{weekday} em %{time})"
msgid "Every week on %{day} at %{time} %{timezone}"
-msgstr ""
+msgstr "Toda semana em %{day} ás %{time} %{timezone}"
msgid "Every year"
msgstr "Todos os anos"
@@ -15612,6 +15719,9 @@ msgstr "Expandir issues"
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr "Expandir marcos"
@@ -16362,9 +16472,6 @@ msgstr "Fev"
msgid "February"
msgstr "Fevereiro"
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16441,7 +16548,7 @@ msgid "Files, directories, and submodules in the path %{path} for commit referen
msgstr "Arquivos, diretórios e submódulos no caminho %{path} para referência de commit %{ref}"
msgid "Fill in the fields below, turn on %{strong_open}Enable SAML authentication for this group%{strong_close}, and press %{strong_open}Save changes%{strong_close}"
-msgstr ""
+msgstr "Preencha os campos abaixo, ative %{strong_open}Ativiar autenticação SAML para este grupo%{strong_close}e pressione %{strong_open}Salvar alterações%{strong_close}"
msgid "Filter"
msgstr "Filtro"
@@ -16465,7 +16572,7 @@ msgid "Filter by label"
msgstr "Filtrar por etiqueta"
msgid "Filter by merge requests that are currently closed and unmerged."
-msgstr ""
+msgstr "Filtrar por solicitações de mesclagem que estão atualmente fechadas e não mescladas."
msgid "Filter by merge requests that are currently merged."
msgstr "Filtrar por solicitações de mesclagem atualmente mescladas."
@@ -16480,10 +16587,10 @@ msgid "Filter by name"
msgstr "Filtrar por nome"
msgid "Filter by test cases that are currently archived."
-msgstr ""
+msgstr "Filtre por casos de teste que estão arquivados no momento."
msgid "Filter by test cases that are currently open."
-msgstr ""
+msgstr "Filtre por casos de teste que estão abertos no momento."
msgid "Filter by user"
msgstr "Filtrar por usuário"
@@ -16575,17 +16682,17 @@ msgstr "Fixo:"
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
-msgstr "Ativar FloC (Aprendizagem Federada de grupo)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
+msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
-msgstr "Aprendizagem federada de grupo (FLC)"
+msgid "FloC|Participate in FLoC"
+msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
-msgstr ""
+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."
@@ -16639,7 +16746,7 @@ msgid "For a faster browsing experience, some files are collapsed by default."
msgstr ""
msgid "For additional information, review your %{link_to} or contact your group owner."
-msgstr ""
+msgstr "Para obter informações adicionais, revise seu %{link_to} ou entre em contato com o proprietário do grupo."
msgid "For additional information, review your group membership: %{link_to} or contact your group owner."
msgstr "Para obter informações adicionais, revise sua associação ao grupo: %{link_to} ou entre em contato com o proprietário do grupo."
@@ -16660,7 +16767,7 @@ msgid "For general work"
msgstr "Para trabalhos gerais"
msgid "For individual use, create a separate account under your personal email address, not tied to the Enterprise email domain or group."
-msgstr ""
+msgstr "Para uso individual, crie uma conta separada em seu endereço de e-mail pessoal, não vinculada ao domínio ou grupo de e-mail corporativo."
msgid "For investigating IT service disruptions or outages"
msgstr "Para investigar interrupções ou interrupções de serviço de TI"
@@ -16669,7 +16776,7 @@ msgid "For more info, read the documentation."
msgstr "Para mais informações, leia a documentação."
msgid "For more information on how the number of active users is calculated, see the %{self_managed_subscriptions_doc_link} documentation."
-msgstr ""
+msgstr "Para obter mais informações sobre como o número de usuários ativos é calculado, consulte a documentação %{self_managed_subscriptions_doc_link}."
msgid "For more information, go to the "
msgstr "Para mais informações, vá para o "
@@ -16731,6 +16838,9 @@ msgstr "Público"
msgid "ForkProject|Select a namespace"
msgstr "Selecionar um espaço nominal"
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr "Algo deu errado ao carregar os dados. Atualize a página para tentar novamente."
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr "O projeto pode ser acessado por qualquer usuário logado."
@@ -16799,6 +16909,9 @@ msgstr "Frequência"
msgid "Frequently searched"
msgstr "Frequentemente pesquisado"
+msgid "Fri"
+msgstr "Sex"
+
msgid "Friday"
msgstr "Sexta-feira"
@@ -16811,15 +16924,10 @@ msgstr "De %{code_open}%{source_title}%{code_close} em"
msgid "From %{providerTitle}"
msgstr "Do %{providerTitle}"
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] "A partir de 19 de outubro de 2022, os grupos gratuitos serão limitados a %d membros"
-msgstr[1] "A partir de 19 de outubro de 2022, os grupos gratuitos serão limitados a %d membros"
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
-msgstr[0] "A partir de 19 de outubro de 2022, você poderá ter no máximo %d membro único em todos os seus projetos pessoais"
-msgstr[1] "A partir de 19 de outubro de 2022, você poderá ter no máximo %d membros únicos em todos os seus projetos pessoais"
+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"
@@ -16913,8 +17021,8 @@ msgstr ""
msgid "Geo|%d group selected"
msgid_plural "%d groups selected"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d grupo selecionado"
+msgstr[1] "%d grupos selecionados"
msgid "Geo|%d shard selected"
msgid_plural "%d shards selected"
@@ -16955,14 +17063,11 @@ msgid "Geo|%{title} checksum progress"
msgstr ""
msgid "Geo|Add New Site"
-msgstr ""
+msgstr "Adicionar um novo site"
msgid "Geo|Add site"
msgstr "Adicionar site"
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr "Todos"
@@ -16997,7 +17102,7 @@ msgid "Geo|Checksummed"
msgstr ""
msgid "Geo|Choose specific groups or storage shards"
-msgstr ""
+msgstr "Escolha grupos específicos ou fragmentos de armazenamento"
msgid "Geo|Comma-separated, e.g. '1.1.1.1, 2.2.2.0/24'"
msgstr ""
@@ -17006,16 +17111,16 @@ msgid "Geo|Configure various settings for your %{siteType} site. %{linkStart}Lea
msgstr ""
msgid "Geo|Connection timeout"
-msgstr ""
+msgstr "Tempo limite de conexão"
msgid "Geo|Connection timeout can't be blank"
msgstr "O tempo limite de conexão não pode ficar em branco"
msgid "Geo|Connection timeout must be a number"
-msgstr ""
+msgstr "Tempo limite da conexão deve ser um número"
msgid "Geo|Connection timeout should be between 1-120"
-msgstr ""
+msgstr "Tempo limite da conexão deve ser entre 1-120"
msgid "Geo|Consult Geo troubleshooting information"
msgstr ""
@@ -17027,7 +17132,7 @@ msgid "Geo|Could not remove tracking entry for an existing project."
msgstr "Não foi possível remover o registro de rastreamento de um projeto existente."
msgid "Geo|Data replication lag"
-msgstr ""
+msgstr "Atraso de replicação de dados"
msgid "Geo|Data type"
msgstr "Tipo de dados"
@@ -17051,7 +17156,7 @@ msgid "Geo|Edit your search and try again."
msgstr ""
msgid "Geo|Errors:"
-msgstr ""
+msgstr "Erros:"
msgid "Geo|Failed"
msgstr "Falha"
@@ -17069,7 +17174,7 @@ msgid "Geo|Filter by status"
msgstr "Filtrar por status"
msgid "Geo|Full details"
-msgstr ""
+msgstr "Detalhes completos"
msgid "Geo|Geo Settings"
msgstr ""
@@ -17093,7 +17198,7 @@ msgid "Geo|Go to the primary site"
msgstr "Ir para o site primário"
msgid "Geo|Groups to synchronize"
-msgstr ""
+msgstr "Grupos para sincronizar"
msgid "Geo|Healthy"
msgstr "Saudável"
@@ -17102,7 +17207,7 @@ msgid "Geo|If enabled, GitLab will handle Object Storage replication using Geo."
msgstr ""
msgid "Geo|If you want to make changes, you must visit the primary site."
-msgstr ""
+msgstr "Se você quiser fazer alterações, você deve visitar o site principal."
msgid "Geo|In progress"
msgstr "Em andamento"
@@ -17111,13 +17216,13 @@ msgid "Geo|Internal URL"
msgstr "URL interna"
msgid "Geo|Internal URL (optional)"
-msgstr ""
+msgstr "URL interna (opcional)"
msgid "Geo|Last event ID from primary"
-msgstr ""
+msgstr "Último ID de evento do primário"
msgid "Geo|Last event ID processed by cursor"
-msgstr ""
+msgstr "Último ID de evento processado pelo cursor"
msgid "Geo|Last repository check run"
msgstr "Última verificação de repositório executada"
@@ -17144,7 +17249,7 @@ msgid "Geo|Make everyone on your team more productive regardless of their locati
msgstr ""
msgid "Geo|Minimum interval in days"
-msgstr ""
+msgstr "Intervalo mínimo em dias"
msgid "Geo|Must match with the %{codeStart}external_url%{codeEnd} in %{codeStart}/etc/gitlab/gitlab.rb%{codeEnd}."
msgstr ""
@@ -17158,14 +17263,20 @@ msgstr "Nunca"
msgid "Geo|Next sync scheduled at"
msgstr "Próxima sincronização programada às"
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
msgid "Geo|No available replication slots"
-msgstr ""
+msgstr "Nenhum slot de replicação disponível"
msgid "Geo|Nothing found…"
-msgstr ""
+msgstr "Nada encontrado…"
msgid "Geo|Nothing to checksum"
msgstr ""
@@ -17189,7 +17300,7 @@ msgid "Geo|Pending verification"
msgstr "Verificação pendente"
msgid "Geo|Primary"
-msgstr ""
+msgstr "Primário"
msgid "Geo|Primary site"
msgstr "Site principal"
@@ -17207,7 +17318,7 @@ msgid "Geo|Queued"
msgstr "Na fila"
msgid "Geo|Re-verification interval"
-msgstr ""
+msgstr "Intervalo de nova verificação"
msgid "Geo|Redownload"
msgstr "Baixar novamente"
@@ -17225,7 +17336,7 @@ msgid "Geo|Remove site"
msgstr "Remover site"
msgid "Geo|Remove tracking database entry"
-msgstr ""
+msgstr "Remover entrada do banco de dados de rastreamento"
msgid "Geo|Removing a Geo site stops the synchronization to and from that site. Are you sure?"
msgstr ""
@@ -17285,19 +17396,19 @@ msgid "Geo|Reverify project"
msgstr ""
msgid "Geo|Review replication status, and resynchronize and reverify items with the primary site."
-msgstr ""
+msgstr "Reveja o status da replicação e ressincronize e verifique novamente os itens com o site principal."
msgid "Geo|Secondary"
-msgstr ""
+msgstr "Secundário"
msgid "Geo|Secondary site"
msgstr "Site secundário"
msgid "Geo|Select groups to replicate"
-msgstr ""
+msgstr "Selecionar grupos para replicar"
msgid "Geo|Select shards to replicate"
-msgstr ""
+msgstr "Seleccione fragmentos para replicar"
msgid "Geo|Selective (%{syncLabel})"
msgstr "Seletivo (%{syncLabel})"
@@ -17318,7 +17429,7 @@ msgid "Geo|Shards to synchronize"
msgstr ""
msgid "Geo|Show more"
-msgstr ""
+msgstr "Mostrar mais"
msgid "Geo|Site name can't be blank"
msgstr "O nome do site não pode ficar em branco"
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17387,7 +17501,7 @@ msgid "Geo|This will resync all %{replicableType}. It may take some time to comp
msgstr ""
msgid "Geo|This will resync all projects. It may take some time to complete. Are you sure you want to continue?"
-msgstr ""
+msgstr "Isso irá ressincronizar todos os projetos. Pode levar algum tempo para ser concluído. Você tem certeza que quer continuar?"
msgid "Geo|This will reverify all projects. It may take some time to complete. Are you sure you want to continue?"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr "Versão do Git"
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr "Banir automaticamente os usuários deste %{scope} quando eles excederem os limites especificados"
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17624,7 +17741,7 @@ msgid "GitLab User"
msgstr "Usuário GitLab"
msgid "GitLab Workhorse"
-msgstr ""
+msgstr "GitLab Workhorse"
msgid "GitLab account request rejected"
msgstr "Solicitação de conta do GitLab rejeitada"
@@ -17645,7 +17762,7 @@ msgid "GitLab group: %{source_link}"
msgstr "Grupo do GitLab: %{source_link}"
msgid "GitLab informs you if a new version is available. %{link_start}What information does GitLab Inc. collect?%{link_end}"
-msgstr ""
+msgstr "O GitLab informa se uma nova versão está disponível. %{link_start}Quais informações o GitLab Inc. coleta?%{link_end}"
msgid "GitLab is a complete DevOps platform, delivered as a single application, fundamentally changing the way Development, Security, and Ops teams collaborate"
msgstr "GitLab é uma plataforma completa de DevOps, entregue como uma única aplicação, mudando fundamentalmente o caminho de desenvolvimento, segurança e equipes de Ops colaboram"
@@ -17654,7 +17771,7 @@ msgid "GitLab is a complete DevOps platform, delivered as a single application,
msgstr "GitLab é uma plataforma completa de DevOps, entregue como uma única aplicação, mudando fundamentalmente o caminho de%{br_tag}desenvolvimento, segurança e equipes de Ops colaboram"
msgid "GitLab is a single application for the entire software development lifecycle. From project planning and source code management to CI/CD, monitoring, and security."
-msgstr ""
+msgstr "O GitLab é um aplicativo único para todo o ciclo de vida de desenvolvimento de software. Desde planejamento de projetos e gerenciamento de código-fonte até CI/CD, monitoramento e segurança."
msgid "GitLab is free to use. Many features for larger teams are part of our %{link_start}paid products%{link_end}. You can try Ultimate for free without any obligation or payment details."
msgstr "O GitLab é gratuito para uso. Muitos recursos para equipes maiores fazem parte de nossos %{link_start}produtos pagos%{link_end}. Você pode experimentar o Ultimate gratuitamente sem nenhuma obrigação ou detalhes de pagamento."
@@ -17663,7 +17780,7 @@ msgid "GitLab is obtaining a Let's Encrypt SSL certificate for this domain. This
msgstr "O GitLab está obtendo um certificado SSL Let's Encrypt para este domínio. Este processo pode levar algum tempo. Por favor, tente novamente mais tarde."
msgid "GitLab is open source software to collaborate on code."
-msgstr ""
+msgstr "O GitLab é um software de código aberto para colaborar no código."
msgid "GitLab is undergoing maintenance"
msgstr "GitLab está passando por manutenção"
@@ -17678,13 +17795,13 @@ msgid "GitLab project export"
msgstr "Exportação do projeto GitLab"
msgid "GitLab single sign-on URL"
-msgstr ""
+msgstr "URL de entrada única do GitLab"
msgid "GitLab username"
msgstr "Nome de usuário do GitLab"
msgid "GitLab uses %{linkStart}Sidekiq%{linkEnd} to process background jobs"
-msgstr ""
+msgstr "GitLab usa %{linkStart}Sidekiq%{linkEnd} para processar trabalhos em segundo plano"
msgid "GitLab version"
msgstr "Versão do GitLab"
@@ -17696,7 +17813,7 @@ msgid "GitLab.com"
msgstr "GitLab.com"
msgid "GitLab.com (SaaS)"
-msgstr ""
+msgstr "GitLab.com (SaaS)"
msgid "GitLab.com import"
msgstr "Importação do GitLab.com"
@@ -17759,10 +17876,10 @@ msgid "GitLabPages|Remove"
msgstr "Remover"
msgid "GitLabPages|Remove certificate"
-msgstr ""
+msgstr "Remover certificado"
msgid "GitLabPages|Remove domain"
-msgstr ""
+msgstr "Remover domínio"
msgid "GitLabPages|Remove pages"
msgstr "Remover páginas"
@@ -17780,7 +17897,7 @@ msgid "GitLabPages|Start over"
msgstr "Começar novamente"
msgid "GitLabPages|Support for domains and certificates is disabled. Ask your system's administrator to enable it."
-msgstr ""
+msgstr "O suporte a domínios e certificados está desativado. Peça ao administrador do seu sistema para ativá-lo."
msgid "GitLabPages|Unverified"
msgstr "Não verificado"
@@ -17896,6 +18013,9 @@ msgstr "A pesquisa global está desativada para este escopo"
msgid "Global Shortcuts"
msgstr "Atalhos Globais"
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr "Configurações de notificação global"
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr "Grupos"
+msgid "GlobalSearch|Help"
+msgstr "Ajuda"
+
+msgid "GlobalSearch|In this project"
+msgstr "Neste projeto"
+
msgid "GlobalSearch|Issues I've created"
msgstr "Issues que eu criei"
@@ -17926,6 +18052,15 @@ msgstr "Solicitações de mesclagem que eu seja um revisor"
msgid "GlobalSearch|Projects"
msgstr "Projetos"
+msgid "GlobalSearch|Recent epics"
+msgstr "Épicos recentes"
+
+msgid "GlobalSearch|Recent issues"
+msgstr "Edições recentes"
+
+msgid "GlobalSearch|Recent merge requests"
+msgstr "Solicitações de mesclagem recentes"
+
msgid "GlobalSearch|Results updated. %{count} results available. Use the up and down arrow keys to navigate search results list, or ENTER to submit."
msgstr ""
@@ -17938,14 +18073,17 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr "Os resultados da pesquisa estão sendo carregados"
+msgid "GlobalSearch|Settings"
+msgstr "Configurações"
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
msgid "GlobalSearch|Type and press the enter key to submit search."
-msgstr ""
+msgstr "Digite e pressione a tecla Enter para enviar a pesquisa."
msgid "GlobalSearch|Type for new suggestions to appear below."
-msgstr ""
+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"
@@ -17965,9 +18103,15 @@ msgstr "em %{scope}"
msgid "GlobalSearch|project"
msgstr "projeto"
-msgid "Globally-allowed IP ranges"
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
msgstr ""
+msgid "Globally-allowed IP ranges"
+msgstr "Intervalos de IP permitidos globalmente"
+
msgid "Go Back"
msgstr "Voltar"
@@ -17999,7 +18143,7 @@ msgid "Go to environments"
msgstr "Ir para ambientes"
msgid "Go to environments page to approve or reject"
-msgstr ""
+msgstr "Vá para a página de ambientes para aprovar ou rejeitar"
msgid "Go to epic"
msgstr "Ir para o épico"
@@ -18038,10 +18182,10 @@ msgid "Go to next page"
msgstr "Ir para a próxima página"
msgid "Go to next unresolved thread"
-msgstr ""
+msgstr "Ir para o próximo tópico não resolvido"
msgid "Go to page %{page}"
-msgstr ""
+msgstr "Ir para a página %{page}"
msgid "Go to parent"
msgstr ""
@@ -18053,7 +18197,7 @@ msgid "Go to previous page"
msgstr "Voltar para a página anterior"
msgid "Go to previous unresolved thread"
-msgstr ""
+msgstr "Ir para o tópico anterior não resolvido"
msgid "Go to primary site"
msgstr "Ir para o site primário"
@@ -18137,13 +18281,13 @@ msgid "Google Cloud authorizations required"
msgstr ""
msgid "GoogleCloud|Cancel"
-msgstr ""
+msgstr "Cancelar"
msgid "GoogleCloud|Configured region is linked to the selected branch or tag"
msgstr ""
msgid "GoogleCloud|Create service account"
-msgstr ""
+msgstr "Criar conta de serviço"
msgid "GoogleCloud|Generated service account is linked to the selected branch or tag"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr "Gravatar habilitado"
msgid "Group"
msgstr "Grupo"
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr "O grupo \"%{group_name}\" foi atualizado com sucesso."
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18548,7 +18689,7 @@ msgid "GroupSAML|Identifier"
msgstr "Identificador"
msgid "GroupSAML|Identity provider single sign-on URL"
-msgstr ""
+msgstr "URL de entrada única de provedor de identidade"
msgid "GroupSAML|Make sure you save this token — you won't be able to access it again."
msgstr "Certifique-se de salvar este token — você não poderá acessá-lo novamente."
@@ -18673,9 +18814,6 @@ msgstr "Aplicado a todos os subgrupos, a menos que seja substituído por um prop
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr "Pipeline de Auto DevOps foi atualizado para o grupo"
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr "Banir automaticamente usuários que baixam mais do que um número especificado de repositórios em um determinado momento."
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr "Disponível apenas no grupo de nível superior. Aplica-se a todos os subgrupos. Grupos já compartilhados com um grupo fora de %{group} ainda são compartilhados, a menos que sejam removidos manualmente."
@@ -18703,6 +18841,9 @@ msgstr "Frameworks de conformidade"
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr "Configure estruturas de conformidade para disponibilizá-las para projetos neste grupo. %{linkStart}Saiba mais.%{linkEnd}"
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr "Configure limites no número de repositórios que os usuários podem baixar em um determinado momento."
+
msgid "GroupSettings|Custom project templates"
msgstr "Modelos de projetos personalizados"
@@ -18926,7 +19067,7 @@ 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 ""
+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"
@@ -18935,7 +19076,7 @@ msgid "GroupsNew|Groups can also be nested by creating %{linkStart}subgroups%{li
msgstr "Grupos também podem ser aninhados criando %{linkStart}subgrupos%{linkEnd}."
msgid "GroupsNew|Import a group and related data from another GitLab instance."
-msgstr ""
+msgstr "Importar um grupo e dados relacionados de outra instância do GitLab."
msgid "GroupsNew|Import group"
msgstr "Importar grupo"
@@ -18974,7 +19115,7 @@ msgid "GroupsNew|You can also %{linkStart}import an existing group%{linkEnd}."
msgstr "Você também pode %{linkStart}importar um grupo existente%{linkEnd}."
msgid "GroupsNew|e.g. h8d3f016698e..."
-msgstr ""
+msgstr "por exemplo, h8d3f016698e..."
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
msgstr "Tem certeza que deseja sair do grupo \"%{fullName}\"?"
@@ -19066,13 +19207,16 @@ msgstr "Diretriz"
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr "URL do arquivo HAR"
+
msgid "HAR file path or URL"
msgstr "Caminho do arquivo HAR ou URL"
msgid "HTTP Archive (HAR)"
msgstr "Arquivo HTTP (HAR)"
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19082,7 +19226,7 @@ msgid "HarborIntegration|After the Harbor integration is activated, global varia
msgstr ""
msgid "HarborIntegration|Base URL of the Harbor instance."
-msgstr ""
+msgstr "URL base da instância do Harbor."
msgid "HarborIntegration|Enter new Harbor password"
msgstr ""
@@ -19103,25 +19247,25 @@ msgid "HarborIntegration|Leave blank to use your current password."
msgstr ""
msgid "HarborIntegration|Password for your Harbor username."
-msgstr ""
+msgstr "Senha para seu nome de usuário no Harbor."
msgid "HarborIntegration|The name of the project in Harbor."
-msgstr ""
+msgstr "O nome do projeto no Harbor."
msgid "HarborIntegration|Use Harbor as this project's container registry."
-msgstr ""
+msgstr "Use Harbour como registro de contêineres deste projeto."
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
-msgstr "Última atualização em %{time}"
-
-msgid "HarborRegistry|Manifest digest: %{digest}"
-msgstr ""
-
msgid "HarborRegistry|Please try different search criteria"
msgstr ""
msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Sorry, your filter produced no results."
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|Tag"
+msgstr "Tag"
+
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] "Ocultar gráfico"
msgstr[1] "Ocultar gráficos"
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr "Ocultar comentários nesse arquivo"
@@ -19455,9 +19599,6 @@ msgstr "Manutenção iniciada com sucesso"
msgid "How do I configure Akismet?"
msgstr "Como eu configuro o Akismet?"
-msgid "How do I configure runners?"
-msgstr "Como eu configuro os executores?"
-
msgid "How do I configure this integration?"
msgstr "Como configuro esta integração?"
@@ -19471,7 +19612,7 @@ msgid "How do I rename an environment?"
msgstr "Como faço para renomear um ambiente?"
msgid "How do I set up a Google Chat webhook?"
-msgstr ""
+msgstr "Como configuro um webhook do Google Chat?"
msgid "How do I set up this service?"
msgstr "Como configuro este serviço?"
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr "Eu aceito o %{terms_link}"
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr "Criar um projeto"
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr "Verificar sua identidade"
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."
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19687,7 +19861,7 @@ msgid "If checked, new group memberships and permissions can only be added via L
msgstr "Se marcado, novas inscrições de grupo e permissões só podem ser adicionadas através da sincronização LDAP"
msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored. %{link_start}Learn more.%{link_end}"
-msgstr ""
+msgstr "Se desativada, uma ramificação local divergente não será atualizada automaticamente com confirmações de sua contraparte remota, para evitar a perda de dados locais. Se a ramificação padrão (%{default_branch}) tiver divergido e não puder ser atualizada, o espelhamento falhará. Outrss ramificações divergentes são ignoradas silenciosamente. %{link_start}Saiba mais.%{link_end}"
msgid "If disabled, only administrators can configure repository mirroring."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr "Se estiver usando o GitHub, você verá os status do pipeline no GitHub
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr "Se você for adicionado a um projeto, ele será exibido aqui."
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr "Se você não iniciou essas tentativas de entrada, entre em contato com seu administrador ou ative a autenticação de dois fatores (2FA) em sua conta."
@@ -19798,7 +19969,7 @@ msgid "ImageViewerDimensions|W"
msgstr ""
msgid "Images with incorrect dimensions are not resized automatically, and may result in unexpected behavior."
-msgstr ""
+msgstr "Imagens com dimensões incorretas não são redimensionadas automaticamente e podem resultar em comportamento inesperado."
msgid "Impersonate"
msgstr ""
@@ -19989,9 +20160,6 @@ 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 "In a seat"
-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."
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr "Métricas"
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr "Resumo"
@@ -20832,8 +21006,8 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
-msgstr "Inclui objetos LFS. Pode ser substituído individualmente por grupo ou por projeto. 0 para ilimitado."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
+msgstr ""
msgid "Includes an MVC structure to help you get started"
msgstr "Inclui uma estrutura MVC para ajudar você a começar"
@@ -20973,9 +21147,15 @@ msgstr "Inserir linha abaixo"
msgid "Insert suggestion"
msgstr "Inserir sugestão"
+msgid "Insert table"
+msgstr "Inserir tabela"
+
msgid "Insights"
msgstr "Insights"
+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 "Configure um relatório personalizado para obter informações sobre os processos do seu grupo, como quantidade de issues, bugs e solicitações de mesclagem por mês. %{linkStart}Como configuro um relatório de insights?%{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 ""
@@ -21030,31 +21210,31 @@ msgid "Integration Settings"
msgstr "Configurações de integração"
msgid "IntegrationEvents|A comment is added on a confidential issue"
-msgstr ""
+msgstr "Um comentário é adicionado em uma issue confidencial"
msgid "IntegrationEvents|A comment is added on an issue"
-msgstr ""
+msgstr "Um comentário é adicionado em uma issue"
msgid "IntegrationEvents|A confidential issue is created, updated, or closed"
-msgstr ""
+msgstr "Uma issue confidencial é criada, atualizada ou fechada"
msgid "IntegrationEvents|A merge request is created, updated, or merged"
-msgstr ""
+msgstr "Uma solicitação de mesclagem é criada, atualizada ou mesclada"
msgid "IntegrationEvents|A pipeline status changes"
-msgstr ""
+msgstr "Um status de pipeline muda"
msgid "IntegrationEvents|A push is made to the repository"
-msgstr ""
+msgstr "Um push é feito para o repositório"
msgid "IntegrationEvents|A tag is pushed to the repository"
-msgstr ""
+msgstr "Uma tag é enviada para o repositório"
msgid "IntegrationEvents|A wiki page is created or updated"
-msgstr ""
+msgstr "Uma página de wiki é criada ou atualizada"
msgid "IntegrationEvents|An issue is created, updated, or closed"
-msgstr ""
+msgstr "Uma issue é criada, atualizada ou fechada"
msgid "Integrations"
msgstr "Integrações"
@@ -21087,13 +21267,13 @@ msgid "Integrations|An error occurred while loading projects using custom settin
msgstr "Ocorreu um erro ao carregar projetos usando configurações personalizadas."
msgid "Integrations|An event will be triggered when one of the following items happen."
-msgstr ""
+msgstr "Um evento será disparado quando um dos seguintes itens acontecer."
msgid "Integrations|Branches for which notifications are to be sent"
msgstr "Ramificações para as quais as notificações devem ser enviadas"
msgid "Integrations|Clear if using a self-signed certificate."
-msgstr ""
+msgstr "Limpar se estiver usando um certificado autoassinado."
msgid "Integrations|Comment detail:"
msgstr "Detalhe do comentário:"
@@ -21102,7 +21282,7 @@ msgid "Integrations|Comment settings:"
msgstr "Configurações de comentário:"
msgid "Integrations|Configure the scope of notifications."
-msgstr ""
+msgstr "Configurar o escopo das notificações."
msgid "Integrations|Connection details"
msgstr "Detalhes da conexão"
@@ -21245,6 +21425,9 @@ msgstr "Envie notificações sobre eventos de projeto para Unify Circuit."
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr "Envie notificações sobre eventos do projeto para uma conversa do Unify Circuit. %{docs_link}"
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr "Entrar no GitLab"
@@ -21557,6 +21740,9 @@ 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|Review the invite errors and try again:"
msgstr "Revise os erros de convite e tente novamente:"
@@ -21572,6 +21758,12 @@ msgstr "Selecione um cargo"
msgid "InviteMembersModal|Select members or type email addresses"
msgstr "Selecione membros ou digite o endereço de e-mail"
+msgid "InviteMembersModal|Show less"
+msgstr "Mostrar menos"
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr "Mostrar mais (%{count})"
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr "Para obter mais membros, um proprietário do grupo pode %{trialLinkStart}iniciar uma avaliação%{trialLinkEnd} ou %{upgradeLinkStart}atualizar%{upgradeLinkEnd} para um nível pago."
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr "Você está convidando membros para o projeto %{strongStart}%{name}%{str
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr "Convidar grupo"
@@ -21736,12 +21922,18 @@ msgstr "Está usando o assento de licença:"
msgid "Is using seat"
msgstr "Está usando licença"
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr "%{wi_type} criado %{created_at} por "
+
msgid "IssuableStatus|Closed"
msgstr "Fechada"
msgid "IssuableStatus|Closed (%{link})"
msgstr "Fechada (%{link})"
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr "Criado %{created_at} por"
+
msgid "IssuableStatus|duplicated"
msgstr "duplicada"
@@ -22007,7 +22199,7 @@ msgid "Iteration"
msgstr "Iteração"
msgid "Iteration cannot be created for cadence"
-msgstr ""
+msgstr "A iteração não pode ser criada para cadência"
msgid "Iteration changed to"
msgstr "Interação alterada para"
@@ -22025,10 +22217,10 @@ msgid "Iterations"
msgstr "Iterações"
msgid "Iterations cadence not found"
-msgstr ""
+msgstr "Cadência de iterações não encontrada"
msgid "Iterations cannot be manually added to cadences that use automatic scheduling"
-msgstr ""
+msgstr "As iterações não podem ser adicionadas manualmente a cadências que usam agendamento automático"
msgid "IterationsCadence|The automation start date must come after the active iteration %{iteration_dates}."
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr "A configuração de cadência é inválida."
msgid "Iterations|Cadence name"
msgstr "Nome da cadência"
-msgid "Iterations|Can be converted"
-msgstr "Pode ser convertida"
-
msgid "Iterations|Cancel"
msgstr "Cancelar"
@@ -22108,6 +22297,9 @@ msgstr "Editar iteração"
msgid "Iterations|Edit iteration cadence"
msgstr "Editar cadência de iteração"
+msgid "Iterations|Enable automatic scheduling"
+msgstr "Ativar agendamento automático"
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr "Saiba mais sobre agendamento automático"
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr "A duração de cada iteração (em semanas)."
msgid "Iterations|The iteration has been deleted."
msgstr "A iteração foi excluída."
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr "Isso removerá a iteração de quaisquer issues atribuídos a ela."
msgid "Iterations|Title"
msgstr "Título"
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr "Para converter esta cadência para agendamento automático, adicione uma duração e número de próximas iterações. A atualização é irreversível."
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr "A tarefa foi apagada com sucesso!"
msgid "Job has wrong arguments format."
msgstr "A tarefa possui formato de argumentos errado."
-msgid "Job is missing the `model_type` argument."
-msgstr "A tarefa não possui o argumento `model_type`."
-
msgid "Job is stuck. Check runners."
msgstr "A tarefa travou. Verifique os executores."
@@ -22947,8 +23121,8 @@ msgstr "Etiquetas podem ser aplicadas à %{features}. Etiquetas de grupo estão
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."
-msgstr "Etiquetas podem ser aplicadas a issues e solicitações de mesclagem."
+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 with no issues in this iteration:"
msgstr "Etiquetas sem issues nesta iteração:"
@@ -22962,6 +23136,9 @@ msgstr "Promover etiqueta"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr "Idioma"
@@ -23132,9 +23309,6 @@ msgstr "Tempo de espera"
msgid "Learn GitLab"
msgstr "Aprenda GitLab"
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr "Saiba mais"
@@ -23186,6 +23360,9 @@ msgstr "Saiba mais sobre os modelos de projeto de nível de grupo"
msgid "Learn more about groups."
msgstr "Saiba mais sobre os grupos."
+msgid "Learn more about issues."
+msgstr "Saiba mais sobre as issues."
+
msgid "Learn more about max seats used"
msgstr "Saiba mais sobre o máximo de assentos usados"
@@ -23625,7 +23802,7 @@ msgid "Linked incidents or issues"
msgstr "Incidentes ou issues vinculadas"
msgid "Linked items"
-msgstr ""
+msgstr "Itens vinculados"
msgid "LinkedIn"
msgstr "LinkedIn"
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr "Usuários máximos"
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr "Solicitações de mesclagem"
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr "A tela de solicitação de mesclagem é um lugar para propor mudanças em um projeto e discutir essas mudanças com outros"
@@ -24778,7 +24958,10 @@ msgid "MergeRequests|An error occurred while saving the draft comment."
msgstr "Ocorreu um erro ao salvar o rascunho do comentário."
msgid "MergeRequests|Create issue to resolve thread"
-msgstr ""
+msgstr "Criar issue para resolver o tópico"
+
+msgid "MergeRequests|Reference copied"
+msgstr "Referência copiada"
msgid "MergeRequests|Saving the comment failed"
msgstr "Falha ao salvar comentário"
@@ -24855,6 +25038,30 @@ msgstr "Nenhum arquivo encontrado"
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr "%{sourceTopic} será removido"
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr "Todos os projetos atribuídos serão movidos para %{targetTopic}"
+
+msgid "MergeTopics|Merge topics"
+msgstr "Mesclar tópicos"
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr "Mesclar tópicos causará o seguinte:"
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr "Esta ação não pode ser desfeita."
+
msgid "Merged"
msgstr "Mesclado"
@@ -25531,18 +25738,21 @@ msgstr "Modificar mensagens de commit"
msgid "Modify merge commit"
msgstr "Modificar merge commit"
+msgid "Mon"
+msgstr "Seg"
+
msgid "Monday"
msgstr "Segunda-feira"
msgid "Monitor"
msgstr "Monitor"
+msgid "Monitor GitLab with Prometheus."
+msgstr ""
+
msgid "Monitor Settings"
msgstr "Configurações do monitor"
-msgid "Monitor the health and performance of GitLab with Prometheus."
-msgstr ""
-
msgid "Monitor your errors by integrating with Sentry."
msgstr "Monitore seus erros através da integração com o Sentry."
@@ -25555,6 +25765,9 @@ msgstr "Mês"
msgid "Months"
msgstr "Meses"
+msgid "More"
+msgstr "Mais"
+
msgid "More Details"
msgstr "Mais detalhes"
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr "Mais estrelas"
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr "Mover"
@@ -25690,12 +25900,6 @@ msgstr "Vários intervalos de endereços IP são suportados. Não afeta o acesso
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr "Vários tipos de modelos encontrados: %{model_types}"
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr "Não é necessário cartão de crédito."
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr "Nenhum dado encontrado"
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr "Nenhuma implantação encontrada"
-msgid "No due date"
-msgstr "Sem validade"
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr "Nenhum repositório"
msgid "No results"
msgstr "Nenhum resultado"
+msgid "No results found"
+msgstr "Nenhum resultado encontrado"
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr "Nenhum favorito corresponde à sua pesquisa"
-msgid "No start date"
-msgstr "Nenhuma data de início"
-
msgid "No suggestions found"
msgstr "Nenhuma sugestão encontrada"
@@ -26596,6 +26800,9 @@ msgstr "A data de criação fornecida está muito distante no passado."
msgid "Nothing to preview."
msgstr "Nada para pré-visualizar."
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr "Eventos de notificação"
@@ -26730,13 +26937,34 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr "%{commit_link} em %{mr_link}"
-msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
msgstr ""
+msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
+msgstr "%{invite_email}, agora conhecido como %{user_name}, aceitou seu convite para se juntar ao %{target_name} %{target_model_name}."
+
msgid "Notify|%{invited_user} has %{highlight_start}declined%{highlight_end} your invitation to join the %{target_link} %{target_name}."
-msgstr ""
+msgstr "%{invited_user} %{highlight_start}recusou%{highlight_end} seu convite para participar do %{target_link} %{target_name}."
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
+msgstr "%{member_link} solicitou %{member_role} acesso ao %{target_source_link} %{target_type}."
+
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr "%{mr_highlight}A solicitação de mesclagem%{highlight_end} %{mr_link} %{approved_highlight}foi aprovada por%{highlight_end} %{approver_avatar} %{approver_link}"
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr "Uma nova chave GPG foi adicionada à sua conta:"
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
msgstr ""
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
@@ -26749,13 +26977,25 @@ msgid "Notify|Author: %{author_name}"
msgstr "Autor: %{author_name}"
msgid "Notify|Auto DevOps pipeline was disabled for %{project}"
-msgstr ""
+msgstr "O pipeline de DevOps automático foi desativado para %{project}"
msgid "Notify|CI/CD project settings"
+msgstr "Configurações do projeto CI/CD"
+
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
msgstr ""
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr "Impressão digital: %{fingerprint}"
+
+msgid "Notify|Hi %{user}!"
+msgstr "Olá %{user}!"
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr "Se esta chave foi adicionada por engano, você pode removê-la em %{removal_link}"
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
-msgstr ""
+msgstr "Se você não quiser mais usar este domínio com o GitLab Pages, remova-o do seu projeto GitLab e exclua todos os registros DNS relacionados."
msgid "Notify|Issue was %{issue_status} by %{updated_by}"
msgstr "A issue foi %{issue_status} por %{updated_by}"
@@ -26764,7 +27004,7 @@ msgid "Notify|Issue was moved to another project."
msgstr "A issue foi movida para outro projeto."
msgid "Notify|Learn more about Auto DevOps"
-msgstr ""
+msgstr "Saiba mais sobre Auto DevOps"
msgid "Notify|Merge request %{merge_request} can no longer be merged due to conflict."
msgstr "A solicitação de mesclagem %{merge_request} não pode mais ser mesclada devido a conflito."
@@ -26784,9 +27024,18 @@ msgstr "A solicitação de mesclagem %{mr_link} foi fechada por %{closed_by}"
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr "URL de solicitação de mesclagem: %{merge_request_url}"
+msgid "Notify|Merge request was approved"
+msgstr "A solicitação de mesclagem foi aprovada"
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr "A solicitação de mesclagem foi aprovada (%{approvals}%{required_approvals}"
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr "Marco alterado para %{milestone}"
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr "Nova issue: %{project_issue_url}"
@@ -26800,11 +27049,17 @@ msgid "Notify|This issue is due on: %{issue_due_date}"
msgstr "Essa issue vence em: %{issue_due_date}"
msgid "Notify|Unless you verify your domain by %{time_start}%{time}%{time_end} it will be removed from your GitLab project."
-msgstr ""
+msgstr "A menos que você verifique seu domínio até %{time_start}%{time}%{time_end} , ele será removido do seu projeto no GitLab."
msgid "Notify|You don't have access to the project."
msgstr "Você não tem acesso ao projeto."
+msgid "Notify|You have been mentioned in an issue."
+msgstr "Você foi mencionado em uma issue."
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr "Você foi mencionado na solicitação de mesclagem %{mr_link}"
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr "Sua solicitação para participar do %{target_to_join} %{target_type} foi %{denied_tag}."
@@ -26812,7 +27067,7 @@ msgid "Notify|currently supported languages"
msgstr ""
msgid "Notify|successfully completed %{jobs} in %{stages}."
-msgstr ""
+msgstr "concluído com sucesso %{jobs} em %{stages}."
msgid "Nov"
msgstr "Nov"
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr "Sob controle"
@@ -27128,6 +27386,12 @@ msgstr "Excluir perfil"
msgid "OnDemandScans|Description (optional)"
msgstr "Descrição (opcional)"
+msgid "OnDemandScans|Discard changes"
+msgstr "Descartar alterações"
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr "Deseja descartar as alterações ou continuar editando este perfil? As alterações não salvas serão perdidas."
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr "Ativar agendamento de verificação"
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr "Por exemplo: testa a página de login para injeções de SQL"
+msgid "OnDemandScans|Keep editing"
+msgstr "Continuar editando"
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr "Gerenciar perfis de verificação"
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr "Ver resultados"
+msgid "OnDemandScans|You have unsaved changes"
+msgstr "Você tem alterações não salvas"
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr "Aberto: %{open}"
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr "Região do OpenSearch."
+
msgid "Opened"
msgstr "Aberto"
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr "Metadados adicionais"
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr "Permitir que pacotes com o mesmo nome e versão sejam carregados no registro. A versão mais recente de um pacote é sempre usada durante a instalação."
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr "Formatos de pacote"
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr "Rejeitar pacotes com o mesmo nome e versão"
-
msgid "PackageRegistry|Remove package"
msgstr "Remover pacote"
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr "RubyGems"
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr "Configurações para pacotes genéricos"
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr "Configurações para pacotes Maven"
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,8 +28351,8 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr "publicado por %{author}"
-msgid "Packages & Registries"
-msgstr "Pacotes e registros"
+msgid "Packages and registries"
+msgstr ""
msgid "Page not found"
msgstr "Página não encontrada"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr "Insights de desempenho"
-
msgid "Performance optimization"
msgstr "Otimização de performance"
@@ -28399,6 +28675,12 @@ msgstr "parede"
msgid "Period in seconds"
msgstr "Período em segundos"
+msgid "Period of inactivity (days)"
+msgstr "Período de inatividade (dias)"
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr "Link permanente"
@@ -28502,7 +28784,7 @@ msgid "Pipeline subscriptions"
msgstr "Assinaturas de pipeline"
msgid "Pipeline subscriptions trigger a new pipeline on the default branch of this project when a pipeline successfully completes for a new tag on the %{default_branch_docs} of the subscribed project."
-msgstr ""
+msgstr "As assinaturas de pipeline acionam um novo pipeline na ramificação padrão deste projeto quando um pipeline é concluído com êxito para uma nova tag no %{default_branch_docs} do projeto inscrito."
msgid "Pipeline triggers"
msgstr "Gatilho de pipeline"
@@ -29104,27 +29386,18 @@ msgstr ""
msgid "Pipeline|Date"
msgstr "Data"
-msgid "Pipeline|Detached merge request pipeline"
-msgstr "Pipeline de solicitação de mesclagem desanexada"
-
msgid "Pipeline|Failed"
msgstr "Falhou"
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr "Em progresso"
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
-msgstr ""
-
msgid "Pipeline|Manual"
msgstr "Manual"
+msgid "Pipeline|Merge request pipeline"
+msgstr ""
+
msgid "Pipeline|Merge train pipeline"
msgstr "Pipeline de merge train"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr "Pipeline de resultado mesclado"
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr "Passou"
msgid "Pipeline|Pending"
msgstr "Pendente"
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr "Pipeline"
@@ -29203,12 +29470,6 @@ msgstr "Nome da tag"
msgid "Pipeline|Test coverage"
msgstr "Cobertura de teste"
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr "Variáveis"
msgid "Pipeline|View commit"
msgstr "Ver commit"
-msgid "Pipeline|View dependency"
-msgstr "Ver dependência"
-
msgid "Pipeline|View pipeline"
msgstr "Ver pipeline"
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr "Porta"
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,12 +29839,12 @@ msgstr "Comportamento"
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr "Escolha entre layout do aplicativo fixo (máx. 1280px) ou fluido (%{percentage})."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
+msgstr ""
+
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 what content you want to see on your homepage."
-msgstr "Escolha o conteúdo que você quer ver na página inicial."
-
msgid "Preferences|Color for added lines"
msgstr "Cor para linhas adicionadas"
@@ -29605,6 +29866,9 @@ msgstr "Personalize a cor do GitLab."
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr "Personalize as cores das linhas removidas e adicionadas em diffs."
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr "Cores em Diff"
@@ -29626,9 +29890,6 @@ msgstr "Por exemplo: 30 minutos atrás."
msgid "Preferences|Gitpod"
msgstr "Gitpod"
-msgid "Preferences|Homepage content"
-msgstr "Conteúdo da página inicial"
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr "Em vez de todos os arquivos alterados, mostre apenas um arquivo de cada vez. Para alternar entre arquivos, use o navegador de arquivos."
@@ -30040,9 +30301,6 @@ msgstr "Título do trabalho"
msgid "Profiles|Key"
msgstr "Chave"
-msgid "Profiles|Key becomes invalid on this date."
-msgstr "A chave torna-se inválida nesta data."
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr "Nenhum arquivo escolhido."
msgid "Profiles|Notification email"
msgstr "E-mail de notificação"
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr "Opcional, mas recomendado. Se definido, a chave se torna inválida na data especificada."
+
msgid "Profiles|Organization"
msgstr "Organização"
@@ -30575,7 +30836,7 @@ msgid "ProjectSelect|There was an error fetching the projects. Please try again.
msgstr ""
msgid "ProjectService|Drone server URL"
-msgstr ""
+msgstr "URL do servidor Drone"
msgid "ProjectService|Enter new API key"
msgstr ""
@@ -30626,10 +30887,10 @@ msgid "ProjectService|Run CI/CD pipelines with JetBrains TeamCity."
msgstr "Execute pipelines CI/CD com TeamCity."
msgid "ProjectService|TeamCity server URL"
-msgstr ""
+msgstr "URL do servidor do TeamCity"
msgid "ProjectService|The build configuration ID of the TeamCity project."
-msgstr ""
+msgstr "O ID de configuração de compilação do projeto TeamCity."
msgid "ProjectService|The token you get after you create a Buildkite pipeline with a GitLab repository."
msgstr "O token que você obtém depois de criar um pipeline Buildkite com um repositório GitLab."
@@ -30638,7 +30899,7 @@ msgid "ProjectService|To configure this integration, you should:"
msgstr ""
msgid "ProjectService|Token for the Drone project."
-msgstr ""
+msgstr "Token para o projeto Drone."
msgid "ProjectService|Trigger event for new comments on confidential issues."
msgstr "Evento de gatilho para novos comentários sobre issues confidenciais."
@@ -30760,6 +31021,9 @@ msgstr "Ativar pipelines de resultados mesclados"
msgid "ProjectSettings|Encourage"
msgstr "Incentivar"
+msgid "ProjectSettings|Environments"
+msgstr "Ambientes"
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr "Cada merge cria um commit de mesclagem."
@@ -30772,6 +31036,9 @@ 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."
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr "Todos"
@@ -30790,6 +31057,9 @@ msgstr "Executar mesclagem com fast-forward"
msgid "ProjectSettings|Fast-forward merges only."
msgstr "Merges com fast-forward apenas."
+msgid "ProjectSettings|Feature flags"
+msgstr "Feature flags"
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr "Ferramenta flexível para desenvolver ideias e planejar as tarefas nesse projeto."
@@ -30931,6 +31201,9 @@ msgstr "Requisitos"
msgid "ProjectSettings|Requirements management system."
msgstr "Sistema de gerenciamento de requisitos."
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr "Pesquisar tópico"
@@ -31741,7 +32014,7 @@ msgstr "padrão"
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr "%{environment_name} poderá ser editado por desenvolvedores. Tem certeza disso?"
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31898,10 +32171,10 @@ msgid "Puma is running with a thread count above 1 and the Rugged service is ena
msgstr ""
msgid "PumbleIntegration|Send notifications about project events to Pumble."
-msgstr ""
+msgstr "Enviar notificações sobre eventos do projeto para o Pumble."
msgid "PumbleIntegration|Send notifications about project events to Pumble. %{docs_link}"
-msgstr ""
+msgstr "Enviar notificações sobre eventos do projeto para o Pumble. %{docs_link}"
msgid "Purchase more minutes"
msgstr "Comprar mais minutos"
@@ -31942,9 +32215,6 @@ msgstr "Eventos de push"
msgid "Push project from command line"
msgstr "Fazer push do projeto por linha de comando"
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr "Ajuda rápida"
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr "Edite rapidamente e facilmente vários arquivos em seu projeto."
-
msgid "Quota of CI/CD minutes"
msgstr "Cota de minutos de CI/CD"
@@ -32301,6 +32568,9 @@ msgstr "Registre o executor com este URL:"
msgid "Register with two-factor app"
msgstr "Registre-se com aplicativo de dois fatores"
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr "Ativo"
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr "Todos"
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr "Uma atualização está disponível para este executor"
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr "Recomenda-se uma atualização para este executor"
+
msgid "Runners|Architecture"
msgstr "Arquitetura"
@@ -33732,6 +34011,9 @@ msgstr "Copiar instruções"
msgid "Runners|Copy registration token"
msgstr "Copiar token de registro"
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr "Executor"
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr "Comece com os executores"
@@ -33803,6 +34088,9 @@ msgstr "Tarefas"
msgid "Runners|Last contact"
msgstr "Último contato"
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr "Último contato: %{timeAgo}"
+
msgid "Runners|Locked to this project"
msgstr "Travado para este projeto"
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr "Executores offline"
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr "Executores online"
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr "Desatualizado"
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr "Obsoleto"
-msgid "Runners|Stale runners"
-msgstr "Executores obsoletos"
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr "Valor"
msgid "Runners|Version"
msgstr "Versão"
+msgid "Runners|Version %{version}"
+msgstr "Versão %{version}"
+
msgid "Runners|View installation instructions"
msgstr "Ver instruções de instalação do executor"
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr "Você usou %{quotaUsed} dos seus %{quotaLimit} minutos dos executores co
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr "disponível"
-
msgid "Runners|group"
msgstr "grupo"
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr "pausado"
-msgid "Runners|recommended"
-msgstr "recomendado"
-
msgid "Runners|shared"
msgstr "compartilhado"
msgid "Runners|specific"
msgstr "específicos"
-msgid "Runners|stale"
-msgstr "obsoleto"
-
-msgid "Runners|upgrade available"
-msgstr "atualização disponível"
-
-msgid "Runners|upgrade recommended"
-msgstr "atualização recomendada"
-
msgid "Runner|Owner"
msgstr "Proprietário"
@@ -34248,6 +34509,9 @@ msgstr "Verificação SSL:"
msgid "SSL verification"
msgstr "Verificação SSL"
+msgid "Sat"
+msgstr "Sáb"
+
msgid "Satisfied"
msgstr "Satisfeito"
@@ -34293,12 +34557,27 @@ msgstr "Salvando"
msgid "Saving project."
msgstr "Salvando projeto."
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr "%{ifLabelStart}se%{ifLabelEnd} %{rules} ações para os %{scopes} %{branches}"
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr "%{period} %{days} a %{time}"
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr "ramificação"
+
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}"
@@ -34426,7 +34717,7 @@ msgid "Search branches and tags"
msgstr "Procurar ramificação e tags"
msgid "Search branches, tags, and commits"
-msgstr ""
+msgstr "Pesquisar ramificações, tags e commits"
msgid "Search by Git revision"
msgstr "Pesquisar por revisão do Git"
@@ -34435,7 +34726,7 @@ msgid "Search by author"
msgstr "Pesquisar por autor"
msgid "Search by commit title or SHA"
-msgstr ""
+msgstr "Pesquisar por título de commit ou SHA"
msgid "Search by message"
msgstr "Pesquisar por mensagem"
@@ -34459,7 +34750,7 @@ msgid "Search for a user"
msgstr "Pesquisar por um usuário"
msgid "Search for an emoji"
-msgstr ""
+msgstr "Pesquisar por um emoji"
msgid "Search for projects, issues, etc."
msgstr "Pesquise por projetos, issues, etc."
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr "Ações"
+msgid "SecurityOrchestration|Add action"
+msgstr "Adicionar ação"
+
msgid "SecurityOrchestration|Add rule"
msgstr "Adicionar regra"
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr "Este %{namespaceType} não está vinculado a um projeto de política de segurança"
+
msgid "SecurityOrchestration|This group"
msgstr "Este grupo"
@@ -36208,7 +36505,7 @@ msgid "Shimo|Go to Shimo Workspace"
msgstr ""
msgid "Shimo|Link to a Shimo Workspace from the sidebar."
-msgstr ""
+msgstr "Link para um espaço de trabalho Shimo na barra lateral."
msgid "Shimo|Shimo"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr "URL da página de saída"
msgid "Sign-up restrictions"
msgstr "Restrições de cadastro"
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr "O primeiro nome é muito longo (o máximo é %{max_length} caracteres)."
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr "Algo deu errado ao adicionar seu prêmio. Por favor, tente novamente."
@@ -36896,6 +37205,9 @@ msgstr "Ocorreu um erro ao obter o certificado Let's Encrypt."
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr "Squash commits"
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr "Stack trace"
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr "Padrão"
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr "Coloque uma estrela em uma etiqueta para dar prioridade. Altere a ordem das etiquetas priorizadas arrastando-as para alterar sua prioridade."
-
msgid "Star labels to start sorting by priority"
msgstr "Favorite etiquetas para começar a classificar por prioridade"
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr "Dom"
+
msgid "Sunday"
msgstr "Domingo"
@@ -38528,10 +38843,10 @@ msgid "Team domain"
msgstr "Domínio da equipe"
msgid "TeamcityIntegration|Trigger TeamCity CI after a merge request has been created or updated"
-msgstr ""
+msgstr "Disparar o TeamCity CI após a criação ou atualização de uma solicitação de mesclagem"
msgid "TeamcityIntegration|Trigger TeamCity CI after every push to the repository, except branch delete"
-msgstr ""
+msgstr "Dispare o TeamCity CI após cada push para o repositório, exceto a exclusão de ramificação"
msgid "Telephone number"
msgstr "Número de telefone"
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr "O YAML analisado é muito grande"
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39368,7 +39686,7 @@ msgid "The repository must be accessible over %{code_open}http://%{code_close},
msgstr "O repositório deve ser acessível em %{code_open}http://%{code_close}, %{code_open}https://%{code_close}, %{code_open}ssh://%{code_close} ou %{code_open}git://%{code_close}."
msgid "The resource that you are attempting to access does not exist or you don't have permission to perform this action."
-msgstr ""
+msgstr "O recurso que você está tentando acessar não existe ou você não tem permissão para realizar esta ação."
msgid "The same shared runner executes code from multiple projects, unless you configure autoscaling with %{link} set to 1 (which it is on GitLab.com)."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,12 +39778,12 @@ msgstr "Tema"
msgid "There are currently no events."
msgstr ""
+msgid "There are currently no mirrored repositories."
+msgstr "Atualmente, não há repositórios espelhados."
+
msgid "There are merge conflicts"
msgstr "Existem conflitos de merge"
-msgid "There are no %{replicableTypeName} to show"
-msgstr ""
-
msgid "There are no GPG keys associated with this account."
msgstr "Não há chaves GPG associadas a esta conta."
@@ -39539,7 +39866,7 @@ 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 ""
+msgstr "Ainda não há arquivos seguros."
msgid "There are no topics to show."
msgstr ""
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr "Existem vários limites de tamanho em vigor."
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr "Já existe um repositório com esse nome no disco"
@@ -39878,10 +40202,10 @@ msgid "This action will %{strongOpen}permanently remove%{strongClose} %{codeOpen
msgstr ""
msgid "This also resolves all related threads"
-msgstr ""
+msgstr "Isso também resolve todos os tópicos relacionados"
msgid "This also resolves this thread"
-msgstr ""
+msgstr "Isso também resolve este tópico"
msgid "This application was created by %{user_link}."
msgstr "Essa aplicação foi criada por %{user_link}."
@@ -39913,6 +40237,9 @@ msgstr "Esse bloco é autorreferencial"
msgid "This board's scope is reduced"
msgstr "O escopo deste painel está reduzido"
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,8 +40264,8 @@ msgstr "Este commit foi assinado com uma assinatura %{strong_open}confirmada%{st
msgid "This commit was signed with a different user's verified signature."
msgstr "Este commit foi assinado com uma assinatura verificada de um usuário diferente."
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
-msgstr "Este commit foi assinado com uma assinatura verificada, mas o email do committer é %{strong_open}não verificado%{strong_close} para pertencer ao mesmo usuário."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
+msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr "Este commit foi assinado com uma assinatura %{strong_open}não verificada%{strong_close}."
@@ -40318,6 +40645,9 @@ msgstr "Este projeto está licenciado sob o %{strong_start}%{license_name}%{stro
msgid "This project is not subscribed to any project pipelines."
msgstr "Este projeto não está inscrito em nenhum pipeline de projeto."
+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 "Este projeto é público. Não-membros podem adivinhar o endereço de e-mail da Central de serviços, pois ele contém o nome do grupo e do projeto. %{linkStart}Como crio um endereço de e-mail personalizado?%{linkEnd}"
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr "Este projeto será excluído em %{date}"
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr "Esta versão foi criada com uma data no passado. A coleta de evidências no momento da liberação não está disponível."
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr "Taxa de transferência"
+msgid "Thu"
+msgstr "Qui"
+
msgid "Thursday"
msgstr "Quinta-feira"
@@ -40645,6 +40975,9 @@ msgstr "agora"
msgid "Timeago|right now"
msgstr "agora mesmo"
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr "Fuso horário"
+msgid "Time|A"
+msgstr "A"
+
+msgid "Time|AM"
+msgstr "AM"
+
+msgid "Time|P"
+msgstr "P"
+
+msgid "Time|PM"
+msgstr "PM"
+
+msgid "Time|a"
+msgstr "a"
+
+msgid "Time|am"
+msgstr "am"
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "h"
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] "min"
msgstr[1] "mins"
+msgid "Time|p"
+msgstr "p"
+
+msgid "Time|pm"
+msgstr "pm"
+
msgid "Time|s"
msgstr "s"
@@ -40736,6 +41093,9 @@ 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."
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr "Para ampliar sua pesquisa, altere ou remova os filtros acima"
msgid "To widen your search, change or remove filters above."
msgstr "Para ampliar sua pesquisa, altere ou remova os filtros acima."
-msgid "To-Do"
-msgstr "Tarefa"
-
msgid "To-Do List"
msgstr "Lista de tarefas pendentes"
@@ -40925,6 +41282,18 @@ msgstr "Filtrar por grupo"
msgid "Todos|Filter by project"
msgstr "Filtrar por projeto"
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr "É assim que você sempre sabe no que trabalhar a seguir."
@@ -40934,6 +41303,9 @@ msgstr "Marcar tudo como concluído"
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr "Muitas referências. Ações rápidas são limitadas a no máximo %{max_
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr "Muitos usuários encontrados. Ações rápidas são limitadas a no máximo %{max_count} usuários"
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr "Voltar"
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr "O tópico %{source_topic} foi mesclado com sucesso no tópico %{target_topic}."
@@ -41069,9 +41450,21 @@ msgstr "Título do tópico"
msgid "Topic was successfully updated."
msgstr "Trópico foi atualizando com sucesso"
+msgid "TopicSelect|No matching results"
+msgstr "Nenhum resultado correspondente"
+
+msgid "TopicSelect|Search topics"
+msgstr "Pesquisar tópicos"
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr "Tópicos"
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr "Total"
@@ -41240,39 +41633,12 @@ msgstr "Sua avaliação termina em %{boldStart}%{trialEndDate}%{boldEnd}. Espera
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr "Caracteres permitidos: +, 0-9, - e espaços."
-msgid "Trial|Company name"
-msgstr "Nome da empresa"
-
msgid "Trial|Continue"
msgstr "Continuar"
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr "Continue usando os recursos básicos do GitLab gratuitamente."
-
-msgid "Trial|Country"
-msgstr "País"
-
msgid "Trial|Dismiss"
msgstr "Dispensar"
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr "Avaliação do GitLab Ultimate (opcional)"
-
-msgid "Trial|Number of employees"
-msgstr "Número de funcionários"
-
-msgid "Trial|Please select a country"
-msgstr "Por favor selecione um país"
-
-msgid "Trial|Telephone number"
-msgstr "Número de telefone"
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr "Atualize para o Ultimate para continuar usando o GitLab com recursos avançados."
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr "Ativaremos sua avaliação em seu grupo após concluir esta etapa. Após 30 dias, você pode:"
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr "Sua avaliação do GitLab Ultimate dura 30 dias, mas você pode manter sua conta GitLab gratuita para sempre. Precisamos apenas de algumas informações adicionais para ativar sua avaliação."
@@ -41378,21 +41744,18 @@ msgstr "Tentando se comunicar com seu dispositivo. Conecte (se necessário) e pr
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr "Tentando se comunicar com seu dispositivo. Conecte (se você ainda não fez) e pressione o botão no dispositivo agora."
+msgid "Tue"
+msgstr "Ter"
+
msgid "Tuesday"
msgstr "Terça-feira"
msgid "Turn off"
msgstr "Desligar"
-msgid "Turn off notifications"
-msgstr "Desativar as notificações"
-
msgid "Turn on"
msgstr "Ativar"
-msgid "Turn on notifications"
-msgstr "Ativar as notificações"
-
msgid "Twitter"
msgstr "Twitter"
@@ -41913,7 +42276,7 @@ msgid "Upload object map"
msgstr "Enviar mapa de objetos"
msgid "Uploaded date"
-msgstr ""
+msgstr "Data do carregamento"
msgid "Uploading changes to terminal"
msgstr "Enviando mudanças ao terminal"
@@ -41972,6 +42335,9 @@ 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}"
+
msgid "UsageQuota|Code packages and container images."
msgstr "Pacotes de código e imagens de contêiner."
@@ -41999,6 +42365,9 @@ msgstr "Docker de registro de contêiner integrado ao Gitlab para armazenar imag
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr "Docker de registro de contêiner integrado ao Gitlab para armazenar imagens do Docker. %{linkStart}Mais informações%{linkEnd}"
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr "Configurações do grupo &gt; Cotas de uso"
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr "Inclui artefatos, repositórios, wiki, envios e outros itens."
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,8 +42533,8 @@ msgstr "Uso de recursos em seus projetos"
msgid "UsageQuota|Usage quotas help link"
msgstr "Link de ajuda de cotas de uso"
-msgid "UsageQuota|Usage since %{usageSince}"
-msgstr "Uso desde %{usageSince}"
+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."
msgstr ""
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr "Use as issues para colaborar em ideias, resolver problemas e planejar o trabalho"
+
msgid "Use one line per URI"
msgstr "Use uma linha por URI"
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr "Usuários podem iniciar um ambiente de desenvolvimento a partir de uma guia do GitLab quando a integração %{linkStart}Gitpod%{linkEnd} está ativada."
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr "Veja os detalhes do alerta."
msgid "View all environments."
msgstr "Ver todos os ambientes."
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr "Ver todas as issues"
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr "Ir para o fork"
msgid "WebIDE|Merge request"
msgstr "Solicitação de mesclagem"
+msgid "WebIDE|Quickly and easily edit multiple files in your project."
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr "Uma versão é criada ou atualizada."
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr "Um webhook neste projeto foi desativado automaticamente após ser repetido várias vezes."
+
msgid "Webhooks|A wiki page is created or updated."
msgstr "Uma página de wiki é criada ou atualizada."
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr "Eventos de feature flag"
+msgid "Webhooks|Go to webhooks"
+msgstr "Ir para webhooks"
+
msgid "Webhooks|Issues events"
msgstr "Eventos de issues"
@@ -43900,6 +44287,9 @@ msgstr "URL deve ser codificado por porcentagem se contiver um ou mais caractere
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr "Usado para validar as cargas recebidas. Enviado com a solicitação no cabeçalho %{code_start}X-Gitlab-Token HTTP%{code_end}."
+msgid "Webhooks|Webhook disabled"
+msgstr "Webhook desativado"
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr "Site"
msgid "Website:"
msgstr "Site:"
+msgid "Wed"
+msgstr "Qua"
+
msgid "Wednesday"
msgstr "Quarta-feira"
@@ -43969,6 +44362,9 @@ msgstr "O que a configuração afeta?"
msgid "What does this command do?"
msgstr "O que este comando faz?"
+msgid "What is GitLab Runner?"
+msgstr "O que é o GitLab Runner?"
+
msgid "What is Markdown?"
msgstr "O que é Markdown?"
@@ -44180,9 +44576,6 @@ msgstr "Excluir página %{pageTitle}?"
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr "Cancelar"
@@ -44207,9 +44600,6 @@ msgstr "Saiba mais."
msgid "WikiPage|Page title"
msgstr "Título da página"
-msgid "WikiPage|Retry"
-msgstr "Tentar novamente"
-
msgid "WikiPage|Save changes"
msgstr "Salvar alterações"
@@ -44297,11 +44687,11 @@ msgstr "Trabalho em andamento (aberto e não atribuído)"
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
-msgstr ""
+msgid "WorkItem|%{workItemType} deleted"
+msgstr "%{workItemType} excluído"
-msgid "WorkItem|Add a task"
-msgstr ""
+msgid "WorkItem|Add"
+msgstr "Adicionar"
msgid "WorkItem|Add a title"
msgstr "Adicionar um título"
@@ -44318,8 +44708,8 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
-msgstr ""
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
+msgstr "Tem certeza de que deseja excluir o %{workItemType}? Esta ação não pode ser revertida."
msgid "WorkItem|Assignee"
msgid_plural "WorkItem|Assignees"
@@ -44329,17 +44719,14 @@ msgstr[1] "Responsáveis"
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr "Fechado"
-msgid "WorkItem|Collapse child items"
-msgstr ""
+msgid "WorkItem|Collapse tasks"
+msgstr "Recolher tarefas"
msgid "WorkItem|Create task"
msgstr "Criar tarefa"
@@ -44347,20 +44734,32 @@ msgstr "Criar tarefa"
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
-msgstr "Excluir tarefa"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr "Excluir %{workItemType}"
-msgid "WorkItem|Expand child items"
-msgstr ""
+msgid "WorkItem|Expand tasks"
+msgstr "Expandir tarefas"
+
+msgid "WorkItem|Incident"
+msgstr "Incidente"
msgid "WorkItem|Introducing tasks"
msgstr "Introdução de tarefas"
-msgid "WorkItem|Learn about tasks"
-msgstr "Saiba mais sobre tarefas"
+msgid "WorkItem|Issue"
+msgstr "Issue"
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
-msgstr ""
+msgid "WorkItem|Learn about tasks."
+msgstr "Saiba mais sobre tarefas."
+
+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|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."
msgid "WorkItem|Open"
msgstr "Abrir"
@@ -44368,19 +44767,22 @@ msgstr "Abrir"
msgid "WorkItem|Remove"
msgstr "Remover"
+msgid "WorkItem|Requirements"
+msgstr "Requisitos"
+
msgid "WorkItem|Select type"
msgstr "Selecione o tipo"
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,32 +44797,41 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr "Tarefa"
+
msgid "WorkItem|Task deleted"
msgstr "Tarefa excluída"
-msgid "WorkItem|Turn off confidentiality"
-msgstr ""
+msgid "WorkItem|Tasks"
+msgstr "Tarefas"
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
-msgstr ""
+msgid "WorkItem|Turn off confidentiality"
+msgstr "Desativar confidencialidade"
+
+msgid "WorkItem|Turn on confidentiality"
+msgstr "Ativar confidencialidade"
msgid "WorkItem|Undo"
msgstr "Desfazer"
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
-msgstr "itens de trabalho"
+msgid "WorkItem|Work item"
+msgstr "Item de trabalho"
msgid "Would you like to create a new branch?"
msgstr ""
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
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."
@@ -44813,11 +45224,6 @@ 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 currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr "Você ainda não tem nenhuma inscrição"
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr "Você precisa enviar um arquivo de exportação de projeto no GitLab (te
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr "Você vê projetos aqui quando é adicionado a um grupo ou projeto."
+
msgid "You successfully declined the invitation"
msgstr "Você recusou o convite com sucesso."
@@ -45156,9 +45565,6 @@ msgstr ""
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 "Você já ativou a autenticação de dois fatores usando autenticadores de senha única. Para registrar um dispositivo diferente, você deve primeiro desativar a autenticação de dois fatores."
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr "Seu dispositivo foi configurado com sucesso! Dê um nome e registre-o no
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr "Seu arquivo deve conter uma coluna chamada %{codeStart}title%{codeEnd}. Uma coluna de %{codeStart}description%{codeEnd} é opcional. O tamanho máximo de arquivo permitido é 10 MB."
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] "Seu grupo gratuito agora está limitado a %d membro"
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr "Sua senha"
+
msgid "Your password reset token has expired."
msgstr "Seu token de redefinição de senha expirou."
@@ -45427,7 +45839,7 @@ msgstr "Seu endereço de e-mail primário é usado para detecção de avatar. Vo
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr "artefatos"
msgid "assign yourself"
msgstr "atribuir a si mesmo"
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr "%{reportType}: Carregamento resultou em um erro"
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr "Gerenciar licenças"
msgid "ciReport|Manage licenses"
msgstr "Gerenciar licenças"
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr "Adicionado manualmente"
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr "criado %{timeAgo} por %{author}"
msgid "created by"
msgstr "criado por"
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr "é somente leitura"
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] "solicitação de mesclagem"
msgstr[1] "solicitações de mesclagem"
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,8 +47060,8 @@ msgstr "%{commitCount} e %{mergeCommitCount} será adicionado à %{targetBranch}
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr "%{commitCount} será adicionado à %{targetBranch}."
-msgid "mrWidgetCommitsAdded|1 merge commit"
-msgstr "1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
+msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
msgstr "Alterações mescladas em %{targetBranch} com %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] "Issue mencionada"
msgstr[1] "Issues mencionadas"
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr "Mesclagem bloqueada: todas as aprovações necessárias devem ser dadas."
@@ -46937,9 +47361,6 @@ msgstr "Para alterar esta mensagem padrão, edite o modelo para mensagens de com
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr "página da wiki"
diff --git a/locale/pt_PT/gitlab.po b/locale/pt_PT/gitlab.po
index dca9287dbf0..fd2d8ebe308 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-08-12 18:04\n"
+"PO-Revision-Date: 2022-09-11 06:22\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr " (de %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] "%d contribuição"
msgstr[1] "%d contribuições"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr "%{size} bytes"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr "%{spammable_titlecase} foi enviado ao Akismet com sucesso."
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr "Um ramo padrão não pode ser escolhido para um projeto vazio."
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr "Adicionar uma tabela"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr "Adicionar comentário agora"
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr "Adicionar etiqueta(s)"
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr "Adicionar lista"
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr "Domínio Auto DevOps"
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr "És tu!"
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr "Permitir que os utilizadores se registem a qualquer aplicação para usa
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr "Uma aplicação chamada %{link_to_client} está a pedir acesso à tua co
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr "Um campo vazio do Utilizador do GitLab adicionará o nome completo do utilizador do FogBugz (por exemplo, \"Por John Smith\") na descrição de todos os problemas e comentários. Ele também irá associar e/ou atribuir estes problemas e comentários ao criador do projeto."
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr "Ocorreu um erro ao carregar os dados do gráfico"
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr "Ocorreu um erro ao assinar as notificações."
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,15 +4358,15 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
+msgid "An error occurred while trying to render the content editor. Please try again."
+msgstr ""
+
msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
-msgstr "Ocorreu um erro ao cancelar a assinatura das notificações."
-
msgid "An error occurred while updating approvers"
msgstr "Ocorreu um erro ao atualizar aprovadores"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr "Expandir"
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "Ramos"
@@ -6855,6 +6914,9 @@ msgstr "Procurar ficheiros"
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr "O certificado Kubernetes usado para autenticar no cluster."
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr "A URL usada para acessar a API Kubernetes."
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr "Para agendar o teu épico da data de %{epicDateType}, com base em objetivos, atribui um objetivo com uma data de %{epicDateType} para qualquer problema no épico."
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr "Definições globais de notificação"
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr "Nenhum ficheiro encontrado"
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr "Nenhum dado encontrado"
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
+msgstr ""
+
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
+msgstr ""
+
+msgid "WorkItem|Turn off confidentiality"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr "dispensado"
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/ro_RO/gitlab.po b/locale/ro_RO/gitlab.po
index 7ed3e9ccf4e..723e138f9eb 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-08-12 18:19\n"
+"PO-Revision-Date: 2022-09-11 06:20\n"
msgid " %{start} to %{end}"
msgstr " de la %{start} până la %{end}"
@@ -22,6 +22,12 @@ msgstr " de la %{start} până la %{end}"
msgid " (from %{timeoutSource})"
msgstr " (de la %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] " (faceți squash de %{strongStart}%{count}%{strongEnd} commit)"
+msgstr[1] " (faceți squash de %{strongStart}%{count}%{strongEnd} commit-uri)"
+msgstr[2] " (faceți squash de %{strongStart}%{count}%{strongEnd} de commit-uri)"
+
msgid " Collected %{time}"
msgstr " Colectat %{time}"
@@ -157,6 +163,12 @@ msgstr[0] "%d aprobator (ați aprobat)"
msgstr[1] "%d aprobatori (ați aprobat)"
msgstr[2] "%d de aprobatori (ați aprobat)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] "%d artefact"
+msgstr[1] "%d artefacte"
+msgstr[2] "%d de artefacte"
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] "%d problemă atribuită"
@@ -241,6 +253,12 @@ msgstr[0] "%d contribuție"
msgstr[1] "%d contribuții"
msgstr[2] "%d de contribuții"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] "%d colaborator"
+msgstr[1] "%d colaboratori"
+msgstr[2] "%d de colaboratori"
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d zi"
@@ -421,6 +439,12 @@ msgstr[0] "%d proiect selectat"
msgstr[1] "%d proiecte selectate"
msgstr[2] "%d de proiecte selectate"
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] "%d împingere"
+msgstr[1] "%d împingeri"
+msgstr[2] "%d de împingeri"
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] "%d rămas"
@@ -463,12 +487,6 @@ msgstr[0] "%d etichetă pe nume de imagine"
msgstr[1] "%d etichete pe nume de imagine"
msgstr[2] "%d de etichete pe nume de imagine"
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] "%d problemă neatribuită"
-msgstr[1] "%d probleme neatribuite"
-msgstr[2] "%d de probleme neatribuite"
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] "%d subiect nerezolvat"
@@ -535,9 +553,6 @@ msgstr "%{actionText} & redeschide %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr "%{address} este un interval de adrese IP invalid"
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr "%{attribute} trebuie să fie între %{min} și %{max}"
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr "%{author_link} a clonat %{original_issue} în %{new_issue}."
@@ -674,7 +689,7 @@ msgid "%{count} total weight"
msgstr "%{count} greutate totală"
msgid "%{criticalStart}%{critical} Critical%{criticalEnd} %{highStart}%{high} High%{highEnd} and %{otherStart}%{otherMessage}%{otherEnd}"
-msgstr "%{criticalStart}%{critical} Critic%{criticalEnd} %{highStart}%{high} Înalt%{highEnd} și %{otherStart}%{otherMessage}%{otherEnd}"
+msgstr "%{criticalStart}%{critical} Critice%{criticalEnd} %{highStart}%{high} Ridicate%{highEnd} și %{otherStart}%{otherMessage}%{otherEnd}"
msgid "%{dashboard_path} could not be found."
msgstr "%{dashboard_path} nu a putut fi găsită."
@@ -976,12 +991,6 @@ msgstr "%{over_limit_message} Pentru obținerea mai multor membri, proprietarul
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}."
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr "%{over_limit_message} Dacă doriți să vizualizați și să gestionați membrii, consultați pagina membrilor fiecărui proiect personal. Este recomandat să vă %{link_start}mutați proiectele într-un grup%{link_end} pentru a putea administra cu ușurință utilizatorii și funcțiile."
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr "%{over_limit_message} Dacă doriți să vizualizați și să gestionați membrii, consultați pagina membrilor fiecărui proiect personal din spațiul de nume personal. Este recomandat să vă %{link_start}mutați proiectele într-un grup%{link_end} pentru a putea administra cu ușurință utilizatorii și funcțiile."
-
msgid "%{percentageUsed}%% used"
msgstr "%{percentageUsed}%% utilizat"
@@ -1093,6 +1102,9 @@ msgstr "%{size} octeți"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr "%{sourceBranch} în %{targetBranch}"
+msgid "%{source} %{copyButton} into %{target}"
+msgstr "%{source} %{copyButton} în %{target}"
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr "%{spammable_titlecase} a fost trimis la Akismet cu succes."
@@ -1114,6 +1126,12 @@ msgstr[2] "%{strongOpen}%{errors}%{strongClose} de puncte"
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr "%{strongOpen}Avertisment:%{strongClose} Linkurile de grup SAML pot face ca GitLab să înlăture automat membrii din grupuri."
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] "%{strongStart}%{count}%{strongEnd} commit"
+msgstr[1] "%{strongStart}%{count}%{strongEnd} commit-uri"
+msgstr[2] "%{strongStart}%{count}%{strongEnd} de commit-uri"
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr "%{strongStart}Sfat:%{strongEnd} De asemenea, puteți verifica merge request-urile la nivel local. %{linkStart}Aflați mai multe.%{linkEnd}"
@@ -1150,9 +1168,6 @@ msgstr[2] "%{strong_start}%{count} de membri%{strong_end} trebuie să aprobe îm
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr "%{strong_start}%{human_size}%{strong_end} Stocare pentru proiect"
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr "Deoarece %{strong_start}%{project_name}%{strong_end} este un proiect personal, nu puteți face upgrade la un plan plătit sau începe o încercare gratuită pentru a elimina aceste limite. Vă recomandăm să %{move_to_group_link}mutați acest proiect într-un grup%{end_link} pentru a debloca aceste opțiuni. Puteți %{manage_members_link}gestiona membrii acestui proiect%{end_link}, dar nu uitați că toți membrii unici din spațiul de nume personal %{strong_start}%{namespace_name}%{strong_end} contează pentru numărul total de seat-uri utilizate."
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] "%{strong_start}%{release_count}%{strong_end} Lansare"
@@ -1363,12 +1378,6 @@ msgstr "(înlăturat)"
msgid "(revoked)"
msgstr "(revocat)"
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] "(squash de %d commit)"
-msgstr[1] "(squash de %d commit-uri)"
-msgstr[2] "(squash de %d de commit-uri"
-
msgid "(this user)"
msgstr "(acest utilizator)"
@@ -1729,6 +1738,12 @@ msgstr "Un șablon de bază pentru dezvoltarea de programe Linux folosind Kotlin
msgid "A complete DevOps platform"
msgstr "O platformă DevOps completă"
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr "O problemă confidențială nu poate avea un părinte care are deja copii neconfidențiali."
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr "Un element de lucru confidențial nu poate avea un părinte care are deja copii neconfidențiali."
+
msgid "A default branch cannot be chosen for an empty project."
msgstr "O ramură implicită nu poate fi aleasă pentru un proiect gol."
@@ -1792,6 +1807,12 @@ msgstr "A fost creat un nou token de acces personal, numit %{token_name}."
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr "O epică neconfidențială nu poate fi atribuită unei epice părinte confidențiale"
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr "O problemă neconfidențială nu poate avea un părinte confidențial."
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr "Un element de lucru neconfidențial nu poate avea un părinte confidențial."
+
msgid "A page with that title already exists"
msgstr "O pagină cu acest titlu există deja"
@@ -1960,6 +1981,9 @@ msgstr "dosar/openapi.json"
msgid "AWS Access Key"
msgstr "Cheia de acces AWS"
+msgid "AWS OpenSearch IAM credentials"
+msgstr "Acreditive AWS OpenSearch IAM"
+
msgid "AWS Secret Access Key"
msgstr "Cheia de acces secret AWS"
@@ -2275,8 +2299,8 @@ msgstr "Adăugați un comentariu la această linie sau glisați pentru mai multe
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr "Adăugați o notă internă confidențială la acest / această %{noteableDisplayName}."
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
-msgstr "Adăugați un mesaj personalizat cu detalii despre executorii partajați ai instanței. Mesajul este afișat în setările de grup și CI/CD ale proiectului în secțiunea Executori. Markdown este acceptat."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
+msgstr "Adăugați un mesaj personalizat cu detalii despre executorii partajați ai instanței. Mesajul este vizibil atunci când vizualizați executorii pentru proiecte și grupuri. Se acceptă Markdown."
msgid "Add a general comment to this %{noteableDisplayName}."
msgstr "Adăugați un comentariu general la acest /această %{noteableDisplayName}."
@@ -2302,6 +2326,9 @@ msgstr "Adăugați un sufix la adresa de e-mail Service Desk. %{linkStart}Aflaț
msgid "Add a table"
msgstr "Adăugați un tabel"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr "Adăugați un titlu..."
@@ -2341,6 +2368,9 @@ msgstr "Adăugați comentariu acum"
msgid "Add comment to design"
msgstr "Adăugare comentariu la design"
+msgid "Add comment to incident timeline"
+msgstr "Adăugați un comentariu la cronologia incidentului"
+
msgid "Add comment..."
msgstr "Adăugare comentariu..."
@@ -2392,6 +2422,12 @@ msgstr "Adăugare cheie"
msgid "Add label(s)"
msgstr "Adăugare etichetă(e)"
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr "Adăugare listă"
@@ -2563,6 +2599,9 @@ msgstr "Adaugă %{labels} %{label_text}."
msgid "Adds a Zoom meeting."
msgstr "Adaugă o reuniune Zoom."
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr "Adăugă o sarcină de făcut."
@@ -2578,9 +2617,6 @@ msgstr "Adaugă acest / această %{issuable_type} ca fiind legat(ă) de %{issuab
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr "Reglați frecvența cu care GitLab UI verifică pentru actualizări."
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr "Ajustați filtrele/criteriile de căutare de mai sus. În cazul în care credeți că este vorba de o eroare, consultați documentația de %{linkStart}depanare Geo%{linkEnd} pentru mai multe informații."
-
msgid "Admin"
msgstr "Admin"
@@ -2779,15 +2815,15 @@ msgstr "Toate proiectele noi pot folosi executorii partajaÈ›i ai acestei instanÈ
msgid "AdminSettings|Auto DevOps domain"
msgstr "Domeniul Auto DevOps"
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr "Interziceți automat utilizatorii care descarcă mai mult de un anumit număr de repozitorii într-un anumit interval de timp."
-
msgid "AdminSettings|CI/CD limits"
msgstr "Limitele CI/CD"
msgid "AdminSettings|Configure Let's Encrypt"
msgstr "Configurați Let's Encrypt"
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr "Configurați limitele numărului de repozitorii care pot fi descărcate de utilizatori într-un anumit interval de timp."
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr "Configurați când proiectele inactive trebuie să fie șterse automat. %{linkStart}Ce sunt proiectele inactive?%{linkEnd}"
@@ -2827,8 +2863,8 @@ msgstr "Activați funcțiile de înregistrare"
msgid "AdminSettings|Enable Service Ping"
msgstr "Activați serviciul Ping"
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
-msgstr "Activați un punct final Prometheus care expune statisticile de sănătate și performanță. Elementul de meniu Verificare de sănătate apare în secțiunea Monitorizare din Zona Admin. Este necesară repornirea. %{link_start}Aflați mai multe%{link_end}."
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
+msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
msgstr "Activați analizatorul personalizat kuromoji: Indexare"
@@ -2851,6 +2887,9 @@ msgstr "Activați analizatorul personalizat smartcn: Căutare"
msgid "AdminSettings|Enabled"
msgstr "Activat"
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr "Implementați impunerea fluxului de invitații pentru grupuri și proiecte "
+
msgid "AdminSettings|Feed token"
msgstr "Token de fluxuri"
@@ -3016,8 +3055,11 @@ msgstr "Pentru a ajuta la îmbunătățirea GitLab și a experienței utilizator
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr "Numărul total de joburi în pipeline-urile active în prezent"
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
-msgstr "Utilizați Elasticsearch găzduit de AWS cu acreditări IAM"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr "Utilizați serviciul AWS OpenSearch cu acreditările IAM"
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
+msgstr "Atât utilizatorii, cât și grupurile trebuie să accepte invitația înainte de a fi adăugați la un grup sau la un proiect."
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
msgstr "Atunci când este în pauză, GitLab continuă să urmărească modificările. Acest lucru este util pentru migrările de clustere/indexuri."
@@ -3241,6 +3283,9 @@ msgstr "Problemele create de acest utilizator sunt ascunse celorlalți utilizato
msgid "AdminUsers|It's you!"
msgstr "Sunteți d-voastră!"
+msgid "AdminUsers|LDAP Blocked"
+msgstr "LDAP blocat"
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr "Aflați mai multe despre %{link_start}utilizatori interziși.%{link_end}"
@@ -4015,9 +4060,6 @@ msgstr "Permiteți utilizatorilor să înregistreze orice aplicație pentru a ut
msgid "Allowed"
msgstr "Permis"
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr "Caractere permise: +, 0-9, - și spații."
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr "Restricția domeniului de e-mail autorizat se permite numai grupurilor de nivel superior"
@@ -4102,6 +4144,9 @@ msgstr "O aplicație numită %{link_to_client} solicită acces la contul dvs. Gi
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr "O notificare prin e-mail a fost trimisă recent din panoul de administrare. Vă rugăm să așteptați %{wait_time_in_words} înainte de a încerca să trimiteți un alt mesaj."
+msgid "An email will be sent with the report attached after it is generated."
+msgstr "Un e-mail va fi trimis cu raportul atașat după ce este generat."
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr "Un câmp gol de utilizator GitLab va adăuga numele complet al utilizatorului FogBugz (de ex. „de John Smithâ€) în descrierea tuturor problemelor È™i comentariilor. De asemenea, va asocia È™i/sau atribui aceste probleme È™i comentarii cu creatorul proiectului."
@@ -4153,6 +4198,9 @@ msgstr "A apărut o eroare în timp ce se adăugau aprobatori"
msgid "An error occurred while adding formatted title for epic"
msgstr "A apărut o eroare la adăugarea titlului formatat pentru epică"
+msgid "An error occurred while approving, please try again."
+msgstr "A survenit o eroare la aprobare, încercați din nou."
+
msgid "An error occurred while authorizing your role"
msgstr "A apărut o eroare în timp ce se autoriza rolul dumneavoastră"
@@ -4291,6 +4339,9 @@ msgstr "A apărut o eroare în timpul încărcării unei secțiuni a acestei pag
msgid "An error occurred while loading all the files."
msgstr "A apărut o eroare în timpul încărcării tuturor fișierelor."
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr "S-a produs o eroare la încărcarea regulilor ramurii. Vă rugăm să încercați din nou."
+
msgid "An error occurred while loading chart data"
msgstr "A apărut o eroare la încărcarea datelor graficului"
@@ -4429,9 +4480,6 @@ msgstr[2] "A apărut o eroare în timpul salvării setărilor"
msgid "An error occurred while saving your settings. Try saving them again."
msgstr "S-a produs o eroare în timpul salvării setărilor. Încercați să le salvați din nou."
-msgid "An error occurred while subscribing to notifications."
-msgstr "A apărut o eroare în timp ce vă abonați la notificări."
-
msgid "An error occurred while triggering the job."
msgstr "S-a produs o eroare la declanșarea jobului."
@@ -4441,15 +4489,15 @@ msgstr "A apărut o eroare la încercarea de a urmări acest utilizator, vă rug
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr "A apărut o eroare în timpul încercării de generare a raportului. Vă rugăm să încercați din nou mai târziu."
+msgid "An error occurred while trying to render the content editor. Please try again."
+msgstr ""
+
msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr "A apărut o eroare în timpul încercării de a rula un nou pipeline pentru acest merge request."
msgid "An error occurred while trying to unfollow this user, please try again."
msgstr "A apărut o eroare la încercarea de a dezactiva urmărirea acestui utilizator, vă rugăm să încercați din nou."
-msgid "An error occurred while unsubscribing to notifications."
-msgstr "A apărut o eroare în timpul dezabonării de la notificări."
-
msgid "An error occurred while updating approvers"
msgstr "A apărut o eroare în timp ce se actualizau aprobatorii"
@@ -4999,6 +5047,9 @@ msgstr "Aprobați"
msgid "Approve a merge request"
msgstr "Aprobați un merge request"
+msgid "Approve merge request"
+msgstr "Aprobați merge request-ul"
+
msgid "Approve the current merge request."
msgstr "Aprobați merge request-ul actual."
@@ -5467,6 +5518,12 @@ msgstr "Luna aceasta"
msgid "AuditLogs|User Events"
msgstr "Evenimentele utilizatorului"
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] "%d destinație"
+msgstr[1] "%d destinații"
+msgstr[2] "%d de destinații"
+
msgid "AuditStreams|A header with this name already exists."
msgstr "Un antet cu acest nume există deja."
@@ -5482,11 +5539,17 @@ msgstr "Adăugați o valoare personalizată"
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr "Adăugați un punct final HTTP pentru a gestiona jurnalele de audit în sistemele terților."
+msgid "AuditStreams|Add another custom header"
+msgstr "Adăugați un alt antet personalizat"
+
msgid "AuditStreams|Add external stream destination"
msgstr "Adăugați o destinație de flux extern"
-msgid "AuditStreams|Add stream"
-msgstr "Adăugare flux"
+msgid "AuditStreams|Add header"
+msgstr "Adăugați un antet"
+
+msgid "AuditStreams|Add streaming destination"
+msgstr "Adăugați o destinație de flux"
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
msgstr "S-a produs o eroare la crearea destinației fluxului de evenimente de audit extern. Vă rugăm să încercați din nou."
@@ -5503,8 +5566,8 @@ msgstr "A apărut o eroare la actualizarea destinației fluxului de evenimente d
msgid "AuditStreams|Cancel editing"
msgstr "Anulați editarea"
-msgid "AuditStreams|Custom HTTP headers"
-msgstr "Anteturi HTTP personalizate"
+msgid "AuditStreams|Custom HTTP headers (optional)"
+msgstr "Anteturi HTTP personalizate (opțional)"
msgid "AuditStreams|Delete %{link}"
msgstr "Ștergeți %{link}"
@@ -5524,6 +5587,9 @@ msgstr "Antet"
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr "A fost atins numărul maxim de %{number} (de) anteturi HTTP."
+msgid "AuditStreams|Remove custom header"
+msgstr "Înlăturați antetul personalizat"
+
msgid "AuditStreams|Save external stream destination"
msgstr "Salvați destinația fluxului extern"
@@ -5533,9 +5599,6 @@ msgstr "Configurați streamingul pentru evenimentele de audit"
msgid "AuditStreams|Stream added successfully"
msgstr "Flux adăugat cu succes"
-msgid "AuditStreams|Stream count icon"
-msgstr "Pictograma numărului de fluxuri"
-
msgid "AuditStreams|Stream deleted successfully"
msgstr "Flux șters cu succes"
@@ -6065,7 +6128,7 @@ msgid "Begin with the selected commit"
msgstr "Începeți cu commit-ul selectat"
msgid "Below are the fingerprints for the current instance SSH host keys."
-msgstr "Mai jos sunt amprentele digitale pentru cheile gazdă SSH ale instanței curente."
+msgstr "Mai jos sunt amprentele pentru cheile gazdă SSH ale instanței curente."
msgid "Below are the settings for %{link_to_gitlab_pages}."
msgstr "Mai jos sunt setările pentru %{link_to_gitlab_pages}."
@@ -6334,12 +6397,6 @@ msgstr "Seats în uz / Seats în abonament"
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr "Executorii partajați nu pot fi activați până când nu este înregistrat un card de credit valabil."
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr "Ultimul proprietar nu poate fi înlăturat de pe un seat."
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr "Pentru ca acest membru să devină activ, trebuie mai întâi să înlăturați un membru activ existent sau să-l comutați la depășirea limitei."
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr "Pentru a utiliza minutele CI/CD gratuite pe executorul partajat, trebuie să vă validați contul cu un card de credit. Dacă preferați să nu furnizați unul, puteți rula pipeline-uri aducându-vă propriii executori și dezactivând executorii partajați pentru proiectul dumneavoastră. Acest lucru este necesar pentru a descuraja și a reduce abuzurile asupra infrastructurii GitLab. %{strongStart}GitLab nu vă va debita cardul, acesta va fi folosit doar pentru validare.%{strongEnd} %{linkStart}Aflați mai multe%{linkEnd}."
@@ -6355,12 +6412,6 @@ msgstr "Validați contul"
msgid "Billings|Validate user account"
msgstr "Validați contul de utilizator"
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr "Nu puteți modifica starea seat-ului unui utilizator care a fost invitat prin intermediul unui grup sau proiect."
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr "Nu vă puteți înlătura singur de pe un seat, dar puteți părăsi grupul."
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr "Acum veți putea să profitați de minutele gratuite de CI/CD pe executorii partajați."
@@ -6412,9 +6463,6 @@ msgstr "Explorați toate planurile"
msgid "Billing|Export list"
msgstr "Exportați lista"
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr "Începând cu 19 octombrie 2022, grupurile gratuite vor fi limitate la 5 membri."
-
msgid "Billing|Group invite"
msgstr "Invitație de grup"
@@ -6457,9 +6505,6 @@ msgstr "Vizualizați aprobările în așteptare"
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr "Sunteți pe cale să înlăturați utilizatorul %{username} din abonament. Dacă veți continua, utilizatorul va fi înlăturat din grupul %{namespace} și din toate subgrupurile și proiectele acestuia. Această acțiune nu poate fi anulată."
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr "Puteți începe acum să mutați membrii în %{namespaceName}. Un membru își pierde accesul la grup atunci când dezactivați %{strongStart}Într-un seat%{strongEnd}. Dacă mai mult de 5 membri au activat %{strongStart}Într-un seat%{strongEnd} după 19 octombrie 2022, vom selecta cei 5 membri care își păstrează accesul. Vom număra mai întâi membrii care au roluri Proprietar și Întreținător, apoi pe cei mai recent activi membri până când vom ajunge la 5 membri. Membrii rămași vor primi un statut de „Peste limită†și își vor pierde accesul la grup."
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr "Recent, grupul dvs. a retrogradat la planul gratuit. %{over_limit_message} Puteți elibera spațiu pentru noii membri, înlăturându-i pe cei care nu mai au nevoie de acces sau trecându-i la peste-limită. %{link_start}Actualizați%{link_end} la un nivel plătit pentru a profita de un număr nelimitat de membri."
@@ -6496,6 +6541,9 @@ msgstr "Problemă blocată"
msgid "Blocking"
msgstr "Blocare"
+msgid "Blocking epics"
+msgstr "Blocarea epicelor"
+
msgid "Blocking issues"
msgstr "Probleme de blocare"
@@ -6706,6 +6754,15 @@ msgstr "Extindeți"
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr "Nu s-a reușit preluarea %{issuableType}s care blochează"
+msgid "Boards|Move card"
+msgstr "Mutați cardul"
+
+msgid "Boards|Move to end of list"
+msgstr "Mutați la sfârșitul listei"
+
+msgid "Boards|Move to start of list"
+msgstr "Mutați la începutul listei"
+
msgid "Boards|New board"
msgstr "Bord nou"
@@ -6832,6 +6889,12 @@ msgstr "Refuzați împingerile de cod care modifică fișierele enumerate în fi
msgid "BranchRules|Require approval from code owners."
msgstr "Necesită aprobarea proprietarilor de coduri."
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "Ramuri"
@@ -6997,6 +7060,9 @@ msgstr "Răsfoiți fișierele"
msgid "Browse templates"
msgstr "Răsfoiți șabloanele"
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr "A apărut o eroare la preluarea artefactelor"
@@ -7252,21 +7318,12 @@ msgstr "Statistici de lansare"
msgid "CICDAnalytics|Releases"
msgstr "Lansări"
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr "Utilizarea executorilor partajați"
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr "Durata executorului partajat este durata totală de execuție a tuturor joburilor care au rulat pe executorii partajați."
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr "Durata în minute a pipeline-ului executorului partajat pe lună"
-msgid "CICDAnalytics|Shared runner usage"
-msgstr "Utilizarea executorului partajat"
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr "Utilizarea executorului partajat este timpul total de execuție al tuturor joburilor care au rulat pe executorii partajați."
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr "Ceva nu a funcționat la preluarea statisticilor de lansare"
@@ -7276,9 +7333,6 @@ msgstr "Timpul necesar pentru restabilirea serviciului"
msgid "CICDAnalytics|What is shared runner duration?"
msgstr "Ce este durata executorului partajat?"
-msgid "CICDAnalytics|What is shared runner usage?"
-msgstr "Ce este utilizarea executorului partajat?"
-
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 "Adaugați un %{base_domain_link_start}domeniu de bază%{link_end} la clusterul dvs. %{kubernetes_cluster_link_start}Kubernetes%{link_end} pentru ca strategia dvs. de implementare să funcționeze."
@@ -8533,6 +8587,12 @@ msgstr "Închideți această %{quick_action_target}."
msgid "Cloud Run"
msgstr "Cloud Run"
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr "Instanțele Cloud SQL sunt baze de date relaționale MySQL complet gestionate. Google se ocupă de replicare, de gestionarea patch-urilor și de gestionarea bazei de date pentru a asigura disponibilitatea și performanța."
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr "Instanțele Cloud SQL sunt baze de date relaționale SQL Server complet gestionate. Google se ocupă de replicare, de gestionarea patch-urilor și de gestionarea bazei de date pentru a asigura disponibilitatea și performanța."
+
msgid "Cloud Storage"
msgstr "Stocare în cloud"
@@ -8560,12 +8620,24 @@ msgstr "Cloud SQL pentru Postgres"
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr "Cloud SQL pentru SQL Server"
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr "Solicitarea de creare a instanței Cloud SQL a reușit. Timpul de rezolvare preconizat este de ~5 minute."
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr "Instanțele Cloud SQL sunt baze de date relaționale PostgreSQL complet gestionate. Google se ocupă de replicare, de gestionarea patch-urilor și de gestionarea bazei de date pentru a asigura disponibilitatea și performanța."
+
msgid "CloudSeed|CloudSQL Instance"
msgstr "Instanța CloudSQL"
msgid "CloudSeed|Configuration"
msgstr "Configurare"
+msgid "CloudSeed|Create MySQL Instance"
+msgstr "Creați o instanță MySQL"
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr "Creați o instanță Postgres"
+
msgid "CloudSeed|Create cluster"
msgstr "Creați un cluster"
@@ -8620,6 +8692,9 @@ msgstr "Serviciu de baze de date relaționale complet gestionat pentru SQL Serve
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr "Instanța bazei de date generată este legată de ramura sau eticheta selectată"
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr "Eroare Google Cloud - %{message}"
+
msgid "CloudSeed|Google Cloud Project"
msgstr "Proiectul Google Cloud"
@@ -9307,11 +9382,11 @@ msgstr "Certificatul Kubernetes folosit pentru autentificarea în cluster."
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr "URL-ul utilizat pentru a accesa API-ul Kubernetes."
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
-msgstr "Integrarea Kubernetes bazată pe certificate a fost depreciată și va fi dezactivată la sfârșitul lunii noiembrie 2022. Vă rugăm să %{linkStart}migrați la agentul GitLab pentru Kubernetes%{linkEnd} sau să contactați asistența GitLab."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
-msgstr "Integrarea Kubernetes bazată pe certificate a fost depreciată și va fi dezactivată la sfârșitul lunii noiembrie 2022. Vă rugăm să %{linkStart}migrați la agentul GitLab pentru Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
msgstr "Metoda bazată pe certificate pentru conectarea clusterelor la GitLab a fost %{linkStart}depreciată%{linkEnd} în GitLab 14.5."
@@ -9481,6 +9556,9 @@ msgstr "Restrângeți problemele"
msgid "Collapse jobs"
msgstr "Restrângeți joburile"
+msgid "Collapse merge details"
+msgstr "Restrângere detalii îmbinare"
+
msgid "Collapse milestones"
msgstr "Restrângeți obiectivul"
@@ -9547,6 +9625,9 @@ msgstr "Comentați și marcați subiectul ca nerezolvat"
msgid "Comment '%{label}' position"
msgstr "PoziÈ›ia comentariului „%{label}â€"
+msgid "Comment added to the timeline."
+msgstr "Comentariul a fost adăugat la cronologie."
+
msgid "Comment form position"
msgstr "Poziția formularului de comentariu"
@@ -9670,6 +9751,9 @@ msgstr "Comparați edițiile GitLab"
msgid "Compare Revisions"
msgstr "Comparați revizuirile"
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr "Comparați modificările"
@@ -10249,6 +10333,9 @@ msgstr "Digest: %{imageId}"
msgid "ContainerRegistry|Docker connection error"
msgstr "Eroare de conexiune Docker"
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr "Activați politica de expirare"
@@ -10354,6 +10441,12 @@ msgstr "Executați curățarea:"
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr "Economisiți spațiu de stocare prin ștergerea automată a etichetelor din registrul container și păstrarea celor pe care le doriți. %{linkStart}Cum funcționează curățarea?%{linkEnd}"
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr "Configurați curățarea"
@@ -10528,8 +10621,8 @@ msgstr "%{created_count} create, %{closed_count} închise."
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr "%{created_count} create, %{merged_count} îmbinate, %{closed_count} închise."
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
-msgstr "%{pushes} (de) împingeri, mai mult de %{commits} (de) commit-uri de la %{people} care au contribuit."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
+msgstr "%{pushes}, mai mult de %{commits} făcute de %{contributors}."
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
msgstr "Analiza contribuțiilor pentru probleme, merge request-uri și evenimente push de la %{start_date}"
@@ -11131,9 +11224,6 @@ msgstr "Creați-vă grupul"
msgid "Create, update, or delete a merge request."
msgstr "Creați, actualizați sau ștergeți un merge request."
-msgid "Create/import your first project"
-msgstr "Creați/importați primul dvs. proiect"
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr "Nu aveți permisiunea de a crea un subgrup în acest grup."
@@ -11677,6 +11767,9 @@ msgstr "„%{name}†colectează datele. Acest lucru poate dura câteva minute.
msgid "CycleAnalytics|Average time to completion"
msgstr "Durata medie de finalizare"
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr "Durata medie de finalizare (zile)"
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr "Rata eșecului schimbărilor"
@@ -11908,6 +12001,12 @@ msgstr "Neactivat"
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."
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr "Spiderul AJAX"
@@ -11986,15 +12085,6 @@ msgstr "Mesaje de depanare"
msgid "DastProfiles|Delete profile"
msgstr "Ștergeți profilul"
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr "Doriți să renunțați la acest profil de scaner?"
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr "Doriți să renunțați la acest profil de site?"
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr "Doriți să renunțați la modificările efectuate?"
-
msgid "DastProfiles|Edit profile"
msgstr "Editați profilul"
@@ -12007,6 +12097,9 @@ msgstr "Editați profilul site-ului"
msgid "DastProfiles|Enable Authentication"
msgstr "Activați autentificarea"
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr "Activați autentificarea de bază"
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr "Introduceți URL-urile într-o listă separată prin virgulă."
@@ -12082,6 +12175,9 @@ msgstr "Parolă"
msgid "DastProfiles|Password form field"
msgstr "Câmp de formular pentru parolă"
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr "Profilul este în uz și nu poate fi redenumit"
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr "Profilul este utilizat de această scanare la cerere"
@@ -12109,17 +12205,14 @@ msgstr "Metoda de scanare"
msgid "DastProfiles|Scan mode"
msgstr "Modul de scanare"
-msgid "DastProfiles|Scanner Profile"
-msgstr "Profilul scanerului"
-
-msgid "DastProfiles|Scanner Profiles"
-msgstr "Profilurile scanerului"
-
msgid "DastProfiles|Scanner name"
msgstr "Numele scanerului"
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
-msgstr "Profilurile de scanare definesc detaliile de configurare ale unui scaner de securitate. %{linkStart}Aflați mai multe%{linkEnd}."
+msgid "DastProfiles|Scanner profile"
+msgstr ""
+
+msgid "DastProfiles|Scanner profiles"
+msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
msgstr "Selectați un profil de scaner pentru a rula o scanare DAST"
@@ -12139,17 +12232,14 @@ msgstr "Selectați profilul site-ului"
msgid "DastProfiles|Show debug messages"
msgstr "Afișați mesajele de depanare"
-msgid "DastProfiles|Site Profile"
-msgstr "Profilul site-ului"
-
-msgid "DastProfiles|Site Profiles"
-msgstr "Profilurile site-ului"
-
msgid "DastProfiles|Site name"
msgstr "Numele site-ului"
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
-msgstr "Profilurile site-ului definesc atributele și detaliile de configurare ale aplicației, site-ului web sau API-ului implementat. %{linkStart}Aflați mai multe%{linkEnd}."
+msgid "DastProfiles|Site profile"
+msgstr ""
+
+msgid "DastProfiles|Site profiles"
+msgstr ""
msgid "DastProfiles|Site type"
msgstr "Tipul site-ului"
@@ -12217,14 +12307,14 @@ msgstr "Puteți alege o scanare pasivă sau puteți valida site-ul țintă din p
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr "Nu puteți efectua o scanare activă pe un site nevalidat."
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
-msgstr "folder/dast_example.har sau https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
+msgstr "https://example.com/dast_example.har"
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
-msgstr "folder/example.postman_collection.json sau https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
+msgstr "https://example.com/openapi.json"
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
-msgstr "folder/openapi.json sau https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
+msgstr "https://example.com/postman_collection.json"
msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr "Copiați antetul HTTP în clipboard"
@@ -12412,8 +12502,8 @@ msgstr "Zile"
msgid "Days to merge"
msgstr "Zile până la îmbinare"
-msgid "Deactivate dormant users after 90 days of inactivity"
-msgstr "Dezactivați utilizatorii inactivi după 90 de zile de inactivitate"
+msgid "Deactivate dormant users after a period of inactivity"
+msgstr "Dezactivați utilizatorii inactivi după o perioadă de inactivitate"
msgid "Dear Administrator,"
msgstr "Stimate administrator,"
@@ -12598,6 +12688,9 @@ msgstr "Ștergerea corpusului"
msgid "Delete deploy key"
msgstr "Ștergeți cheia de implementare"
+msgid "Delete epic"
+msgstr "Ștergeți epica"
+
msgid "Delete file"
msgstr "Ștergeți fișierul"
@@ -12649,6 +12742,9 @@ msgstr "Ștergeți fragment de cod?"
msgid "Delete source branch"
msgstr "Ștergeți ramura sursă"
+msgid "Delete source branch when merge request is accepted."
+msgstr "Ștergeți ramura sursă atunci când merge request-ul este acceptat."
+
msgid "Delete subscription"
msgstr "Ștergeți abonamentul"
@@ -13213,6 +13309,12 @@ msgstr "Mediu: %{environment}"
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr "Job manual: %{jobName}"
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr "Respins %{time}"
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr "Respins de dvs. %{time}"
+
msgid "DeploymentTarget|GitLab Pages"
msgstr "GitLab Pages"
@@ -13444,6 +13546,9 @@ msgstr "Design"
msgid "DesignManagement|Discard comment"
msgstr "Înlăturați comentariul"
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr "Descărcați elementul de design"
@@ -13537,6 +13642,9 @@ msgstr "Rapoarte DevOps"
msgid "DevOps adoption"
msgstr "Adoptarea DevOps"
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr "Dispozitive (opțional)"
@@ -14101,6 +14209,9 @@ msgstr "Draft"
msgid "Draft: %{filename}"
msgstr "Draft: %{filename}"
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr "Trageți pentru a reordona etichetele prioritizate și a modifica prioritatea lor relativă."
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr "Glisați design-urile aici sau %{linkStart}faceți clic pentru a încărca%{linkEnd}."
@@ -14122,6 +14233,12 @@ msgstr "S-a produs o eroare în timpul preluării %{issuableAttribute} atribuit
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr "Atribuiți %{issuableAttribute}"
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr "Nu s-a putut prelua %{issuableAttribute} pentru acest/această %{issuableType}. Vă rugăm să încercați din nou."
@@ -14137,6 +14254,12 @@ msgstr "Nu s-a găsit niciun/nicio %{issuableAttribute}"
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr "Nu s-a găsit niciun/nicio %{issuableAttribute} deschis(ă)"
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr "Data scadentă"
@@ -14359,6 +14482,9 @@ msgstr "Editați pagina wiki"
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr "Editați cel mai recent comentariu al dvs. dintr-un subiect (dintr-o zonă de text goală)"
+msgid "Edit your search filter and try again."
+msgstr "Editați filtrul de căutare și încercați din nou"
+
msgid "Edit, lint, and visualize your pipeline."
msgstr "Editați, verificați cu lint și vizualizați-vă pipeline-ul."
@@ -14374,9 +14500,6 @@ msgstr "Editare"
msgid "Elapsed time"
msgstr "Timpul scurs"
-msgid "Elasticsearch AWS IAM credentials"
-msgstr "Acreditările IAM pentru Elasticsearch AWS"
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr "Valoarea timeout-ului clientului HTTP Elasticsearch, în secunde."
@@ -14401,9 +14524,6 @@ msgstr "Reindexarea Elasticsearch nu a fost inițiată: %{errors}"
msgid "Elasticsearch zero-downtime reindexing"
msgstr "Reindexarea Elasticsearch cu zero-downtime"
-msgid "Elasticsearch's region."
-msgstr "Regiunea Elasticsearch."
-
msgid "Elastic|None. Select namespaces to index."
msgstr "Niciunul. Selectați spațiile de nume pentru indexare."
@@ -14551,6 +14671,9 @@ msgstr "Activați Auto DevOps"
msgid "Enable GitLab Error Tracking"
msgstr "Activați urmărirea erorilor GitLab"
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr "Activați Gitpod"
@@ -14635,9 +14758,6 @@ msgstr "Activați executorii de grup"
msgid "Enable header and footer in emails"
msgstr "Activați antetul și subsolul în e-mailuri"
-msgid "Enable health and performance metrics endpoint"
-msgstr "Activați punctul final de metrici de sănătate și performanță"
-
msgid "Enable in-product marketing emails"
msgstr "Activați e-mailurile de marketing în produs"
@@ -15079,6 +15199,9 @@ msgstr "Epică"
msgid "Epic Boards"
msgstr "Borduri de epice"
+msgid "Epic actions"
+msgstr "Acțiunile epicei"
+
msgid "Epic cannot be found."
msgstr "Epica nu poate fi găsită."
@@ -15115,12 +15238,6 @@ msgstr "Adăugați o nouă epică"
msgid "Epics|Add an existing epic"
msgstr "Adăugați o epică existentă"
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr "A apărut o eroare la salvarea datei %{epicDateType}"
-
-msgid "Epics|An error occurred while updating labels."
-msgstr "A apărut o eroare în timp ce se actualizau etichetele."
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr "Sunteți sigur că doriți să înlăturați %{bStart}%{targetIssueTitle}%{bEnd} din %{bStart}%{parentEpicTitle}%{bEnd}?"
@@ -15187,18 +15304,9 @@ msgstr "Această epică È™i orice epice copil pe care le conÈ›ine sunt confidenÈ
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr "Această operațiune va elimina, de asemenea, orice descendenți ai %{bStart}%{targetEpicTitle}%{bEnd} din %{bStart}%{parentEpicTitle}%{bEnd}. Sunteți sigur?"
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr "Pentru a planifica data %{epicDateType} a epicei pe baza obiectivelor, atribuiți un obiectiv cu o dată %{epicDateType} oricărei probleme din epică."
-
msgid "Epics|Unable to save epic. Please try again"
msgstr "Nu se poate salva epica. Vă rugăm să încercați din nou"
-msgid "Epics|due"
-msgstr "scadență"
-
-msgid "Epics|start"
-msgstr "de început"
-
msgid "Erased"
msgstr "Șters"
@@ -15235,6 +15343,9 @@ msgstr "Eroare de creare a detectării vulnerabilității: %{errors}"
msgid "Error deleting project. Check logs for error details."
msgstr "Eroare la ștergerea proiectului. Verificați jurnalele pentru detalii despre eroare."
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr "Eroare la preluarea datelor graficului burnup"
@@ -15268,9 +15379,6 @@ msgstr "Eroare la încărcarea ramurilor."
msgid "Error loading burndown chart data"
msgstr "Eroare la încărcarea datelor graficului burndown"
-msgid "Error loading countries data."
-msgstr "Eroare la încărcarea datelor țărilor."
-
msgid "Error loading file viewer."
msgstr "Eroare la încărcarea vizualizatorului de fișiere."
@@ -15346,6 +15454,9 @@ msgstr "A apărut o eroare. Utilizatorul nu a fost deblocat"
msgid "Error parsing CSV file. Please make sure it has"
msgstr "Eroare la analizarea fișierului CSV. Vă rugăm să vă asigurați că are"
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr "Eroare la promovarea notei la evenimentul cronologic: %{error}"
+
msgid "Error rendering Markdown preview"
msgstr "Eroare de redare a previzualizării Markdown"
@@ -15778,6 +15889,9 @@ msgstr "Extindeți problemele"
msgid "Expand jobs"
msgstr "Extindeți joburile"
+msgid "Expand merge details"
+msgstr "Extindere detalii îmbinare"
+
msgid "Expand milestones"
msgstr "Extindeți obiectivele"
@@ -16531,9 +16645,6 @@ msgstr "Feb"
msgid "February"
msgstr "Februarie"
-msgid "Feedback issue"
-msgstr "Problemă de feedback"
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr "Obțineți și verificați această ramură de caracteristică a merge request-ului:"
@@ -16744,14 +16855,14 @@ msgstr "Fixată:"
msgid "Flags"
msgstr "Semnalizatori"
-msgid "FloC|Configure whether you want to participate in FloC."
-msgstr "Configurați dacă doriți să participați la FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
+msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
-msgstr "Activați FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
+msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
-msgstr "Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
+msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
msgstr "Introduceți tokenul Flowdock."
@@ -16900,6 +17011,9 @@ msgstr "Public"
msgid "ForkProject|Select a namespace"
msgstr "Selectați un spațiu de nume"
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr "Ceva nu a mers bine în timpul încărcării datelor. Vă rugăm să reîmprospătați pagina pentru a încerca din nou."
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr "Proiectul poate fi accesat de orice utilizator conectat."
@@ -16969,6 +17083,9 @@ msgstr "Frecvență"
msgid "Frequently searched"
msgstr "Căutate frecvent"
+msgid "Fri"
+msgstr "Vin"
+
msgid "Friday"
msgstr "Vineri"
@@ -16981,17 +17098,11 @@ msgstr "De la %{code_open}%{source_title}%{code_close} în"
msgid "From %{providerTitle}"
msgstr "De la %{providerTitle}"
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] "Începând cu 19 octombrie 2022, grupurile gratuite vor fi limitate la %d membru."
-msgstr[1] "Începând cu 19 octombrie 2022, grupurile gratuite vor fi limitate la %d membri."
-msgstr[2] "Începând cu 19 octombrie 2022, grupurile gratuite vor fi limitate la %d membri."
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
-msgstr[0] "Începând cu 19 octombrie 2022, puteți avea maximum %d membru unic în toate proiectele personale."
-msgstr[1] "Începând cu 19 octombrie 2022, puteți avea maximum %d membri unici în toate proiectele personale."
-msgstr[2] "Începând cu 19 octombrie 2022, puteți avea maximum %d de membri unici în toate proiectele personale."
+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"
@@ -17134,9 +17245,6 @@ msgstr "Adăugare site nou"
msgid "Geo|Add site"
msgstr "Adăugare site"
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr "Ajustați filtrele/criteriile de căutare de mai sus. în cazul în care credeți că este vorba de o eroare, consultați documentația %{linkStart}Depanare Geo%{linkEnd} pentru mai multe informații."
-
msgid "Geo|All"
msgstr "Toate"
@@ -17332,6 +17440,12 @@ msgstr "Niciodată"
msgid "Geo|Next sync scheduled at"
msgstr "Următoarea sincronizare programată la"
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr "Nu au fost găsite %{replicable_type}. Dacă sunteți de părere că aceasta este o eroare, consultați documentația de %{linkStart}depanare Geo%{linkEnd} pentru mai multe informații."
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr "Nu au fost găsite %{replicable}. Dacă sunteți de părere că aceasta este o eroare, consultați documentația de %{linkStart}depanare Geo%{linkEnd} pentru mai multe informații."
+
msgid "Geo|No Geo site found"
msgstr "Nu s-a găsit niciun site Geo"
@@ -17539,6 +17653,9 @@ msgstr "Site-ul se află în prezent cu %{minutes_behind} în urma site-ului pri
msgid "Geo|There are no %{replicable_type} to show"
msgstr "Nu există %{replicable_type} de afișat"
+msgid "Geo|There are no %{replicable} to show"
+msgstr "Nu există %{replicable} de afișat"
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr "A apărut o eroare la ștergerea site-ului Geo"
@@ -17701,6 +17818,9 @@ msgstr "Transferul Git în curs"
msgid "Git version"
msgstr "Versiunea Git"
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr "Interziceți automat utilizatorii la acest/această %{scope} atunci când depășesc limitele specificate"
+
msgid "GitAbuse|Excluded users"
msgstr "Utilizatori excluși"
@@ -18070,6 +18190,9 @@ msgstr "Căutarea globală este dezactivată pentru acest scop"
msgid "Global Shortcuts"
msgstr "Comenzi rapide globale"
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr "Setări de notificare globale"
@@ -18082,6 +18205,12 @@ msgstr "%{count} (de) rezultate implicite furnizate. UtilizaÈ›i tastele săgeatÄ
msgid "GlobalSearch|Groups"
msgstr "Grupuri"
+msgid "GlobalSearch|Help"
+msgstr "Ajutor"
+
+msgid "GlobalSearch|In this project"
+msgstr "ÃŽn acest proiect"
+
msgid "GlobalSearch|Issues I've created"
msgstr "Problemele pe care le-am creat"
@@ -18100,6 +18229,15 @@ msgstr "Merge request-uri pentru care sunt un recenzent"
msgid "GlobalSearch|Projects"
msgstr "Proiecte"
+msgid "GlobalSearch|Recent epics"
+msgstr "Epice recente"
+
+msgid "GlobalSearch|Recent issues"
+msgstr "Probleme recente"
+
+msgid "GlobalSearch|Recent merge requests"
+msgstr "Merge request-uri recente"
+
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."
@@ -18112,6 +18250,9 @@ msgstr "Căutați proiecte, probleme etc."
msgid "GlobalSearch|Search results are loading"
msgstr "Rezultatele căutării se încarcă"
+msgid "GlobalSearch|Settings"
+msgstr "Setări"
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr "A apărut o eroare la preluarea sugestiilor de autocompletare a căutării."
@@ -18139,6 +18280,12 @@ msgstr "în %{scope}"
msgid "GlobalSearch|project"
msgstr "proiect"
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr "Intervale IP permise la nivel global"
@@ -18409,9 +18556,6 @@ msgstr "Gravatar activat"
msgid "Group"
msgstr "Grup"
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr "Grupul „%{group_name}†a fost actualizat cu succes."
-
msgid "Group %{group_name} couldn't be exported."
msgstr "Grupul %{group_name} nu a putut fi exportat."
@@ -18686,7 +18830,7 @@ msgid "GroupSAML|Before enforcing SSO-only authentication for Git activity of al
msgstr "Înainte de a impune autentificarea exclusiv SSO pentru activitatea Git pentru toți utilizatorii, autentificarea exclusiv SSO trebuie să fie activată pentru activitatea web."
msgid "GroupSAML|Certificate fingerprint"
-msgstr "Amprenta digitală a certificatului"
+msgstr "Amprenta certificatului"
msgid "GroupSAML|Configuration"
msgstr "Configurare"
@@ -18785,7 +18929,7 @@ msgid "GroupSAML|SCIM Token"
msgstr "Token SCIM"
msgid "GroupSAML|SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
-msgstr "Amprenta digitală SHA1 a certificatului de semnare a tokenului SAML. ObÈ›ineÈ›i acest lucru de la furnizorul de identitate, unde poate fi numit È™i „Thumbprintâ€."
+msgstr "Amprenta SHA1 a certificatului de semnare a tokenului SAML. ObÈ›ineÈ›i acest lucru de la furnizorul de identitate, unde poate fi numit È™i „Thumbprintâ€."
msgid "GroupSAML|The SCIM token is now hidden. To see the value of the token again, you need to %{linkStart}reset it%{linkEnd}."
msgstr "Tokenul SCIM este acum ascuns. Pentru a vedea din nou valoarea tokenului, trebuie să %{linkStart}îl resetați%{linkEnd}."
@@ -18847,9 +18991,6 @@ msgstr "Se aplică tuturor subgrupurilor, cu excepția cazului în care este în
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr "Pipeline-ul Auto DevOps a fost actualizat pentru grup"
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr "Interziceți automat utilizatorii care descarcă mai mult de un anumit număr de repozitorii într-un anumit interval de timp."
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr "Este disponibil numai pentru grupul de nivel superior. Se aplică la toate subgrupurile. Grupurile deja partajate cu un grup din afara %{group} sunt în continuare partajate, doar dacă nu sunt eliminate manual."
@@ -18877,6 +19018,9 @@ msgstr "Framework-uri de conformitate"
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr "Configurați framework-urile de conformitate pentru a le face disponibile proiectelor din acest grup. %{linkStart}Aflați mai multe.%{linkEnd}"
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr "Configurați limitele numărului de repozitorii care pot fi descărcate de utilizatori într-un anumit interval de timp."
+
msgid "GroupSettings|Custom project templates"
msgstr "Șabloane de proiect personalizate"
@@ -19240,14 +19384,17 @@ msgstr "Ghid"
msgid "HAR (HTTP Archive)"
msgstr "HAR (arhivă HTTP)"
+msgid "HAR file URL"
+msgstr "URL-ul fișierului HAR"
+
msgid "HAR file path or URL"
msgstr "Calea sau URL-ul fișierului HAR"
msgid "HTTP Archive (HAR)"
msgstr "Arhiva HTTP (HAR)"
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
-msgstr "HTTP Basic: Acces refuzat\\n Trebuie să folosiți un token de acces personal cu domeniul „api†pentru Git over HTTP.\\n Puteți genera unul la %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
+msgstr "HTTP Basic: Acces refuzat. Parola sau tokenul furnizat este incorect sau aveți activată în cont funcția 2FA și trebuie să utilizați un token de acces personal în loc de parolă. Consultați %{help_page_url}"
msgid "Harbor Registry"
msgstr "Registrul Harbor"
@@ -19285,20 +19432,20 @@ msgstr "Numele proiectului în Harbor."
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr "Utilizați Harbor ca registru de containere al acestui proiect."
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
+msgstr[0] "%d artefact"
+msgstr[1] "%d artefacte"
+msgstr[2] "%d de artefacte"
+
msgid "HarborRegistry|%{count} Image repository"
msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] "%{count} Repozitoriu de imagini"
msgstr[1] "%{count} Repozitorii de imagini"
msgstr[2] "%{count} de Repozitorii de imagini"
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
-msgstr[0] "%{count} Etichetă"
-msgstr[1] "%{count} Etichete"
-msgstr[2] "%{count} de Etichete"
-
-msgid "HarborRegistry|Configuration digest: %{digest}"
-msgstr "Digestul de configurare: %{digest}"
+msgid "HarborRegistry|-- artifacts"
+msgstr "-- artefacte"
msgid "HarborRegistry|Digest: %{imageId}"
msgstr "Digest: %{imageId}"
@@ -19309,44 +19456,38 @@ msgstr "Registrul Harbor"
msgid "HarborRegistry|Harbor connection error"
msgstr "Eroare de conectare la Harbor"
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr "Etichetă nevalidă: lipsește digestul manifestului"
-
-msgid "HarborRegistry|Last updated %{time}"
-msgstr "Ultima actualizare %{time}"
-
-msgid "HarborRegistry|Manifest digest: %{digest}"
-msgstr "Digestul manifestului: %{digest}"
-
msgid "HarborRegistry|Please try different search criteria"
msgstr "Vă rugăm să încercați alte criterii de căutare"
msgid "HarborRegistry|Published %{timeInfo}"
msgstr "Publicat %{timeInfo}"
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
-msgstr "Publicat în depozitul de imagini %{repositoryPath} la ora %{time} pe %{date}"
-
msgid "HarborRegistry|Root image"
msgstr "Imaginea rădăcină"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
+msgstr "Ceva nu a mers bine la preluarea listei de artefacte."
+
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
+msgstr "Ceva nu a mers bine la preluarea listei de repozitorii."
+
+msgid "HarborRegistry|Something went wrong while fetching the tags."
+msgstr "Ceva nu a mers bine la preluarea etichetelor."
+
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr "Ne pare rău, filtrul dvs. nu a produs niciun rezultat."
+msgid "HarborRegistry|Tag"
+msgstr " Etichetă"
+
msgid "HarborRegistry|The filter returned no results"
msgstr "Filtrul nu a generat niciun rezultat"
-msgid "HarborRegistry|The image repository could not be found."
-msgstr "Repozitoriul de imagini nu a putut fi găsit."
-
-msgid "HarborRegistry|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 "Ultima etichetă legată de această imagine a fost recent îndepărtată. Această imagine goală și toate datele asociate vor fi îndepărtate automat în cadrul procesului obișnuit de Colectare a gunoiului. Dacă aveți întrebări, contactați administratorul dumneavoastră."
-
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
-msgstr "Repozitoriul de imagini solicitat nu există sau a fost șters. Dacă sunteți de părere că este vorba de o eroare, încercați să reîmprospătați pagina."
+msgid "HarborRegistry|There are no harbor images stored for this project"
+msgstr "Nu există imagini harbor stocate pentru acest proiect."
-msgid "HarborRegistry|This image has no active tags"
-msgstr "Această imagine nu are etichete active"
+msgid "HarborRegistry|This image has no artifacts"
+msgstr "Această imagine nu are artefacte"
msgid "HarborRegistry|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."
@@ -19354,6 +19495,9 @@ msgstr "Pentru a vă extinde căutarea, modificați sau eliminați filtrele de m
msgid "HarborRegistry|We are having trouble connecting to the Harbor Registry. Please try refreshing the page. If this error persists, please review %{docLinkStart}the troubleshooting documentation%{docLinkEnd}."
msgstr "Avem probleme cu conectarea la Registrul Harbor. Vă rugăm să încercați să reîmprospătați pagina. Dacă această eroare persistă, vă rugăm să consultați %{docLinkStart}documentația de depanare.%{docLinkEnd}"
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr "Cu Harbor Registry, fiecare proiect se poate conecta la un spațiu harbor pentru a-și stoca imaginile Docker."
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr "Cu Registrul Harbor, fiecare proiect poate avea propriul său spațiu pentru a stoca imagini. %{docLinkStart}Mai multe informații%{docLinkEnd}"
@@ -19501,6 +19645,9 @@ msgstr[0] "Ascundeți graficul"
msgstr[1] "Ascundeți graficele"
msgstr[2] "Ascundeți graficele"
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr "Ascundeți comentariile la acest fișier"
@@ -19633,9 +19780,6 @@ msgstr "Menajul a început cu succes"
msgid "How do I configure Akismet?"
msgstr "Cum pot configura Akismet?"
-msgid "How do I configure runners?"
-msgstr "Cum să configurez executorii?"
-
msgid "How do I configure this integration?"
msgstr "Cum pot configura această integrare?"
@@ -19669,6 +19813,9 @@ msgstr "Câte secunde contează un IP pentru limita de adrese IP."
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr "Modul în care limitatorul de joburi gestionează joburile care depășesc pragurile specificate mai jos. Modul „urmărire†înregistrează doar joburile. Modul „comprimare†comprimă joburile și generează o excepție în cazul în care dimensiunea comprimată depășește limita."
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr "Accept %{terms_link}"
@@ -19792,9 +19939,15 @@ msgstr "Înainte de a vă autentifica, trebuie să vă verificăm identitatea. I
msgid "IdentityVerification|Create a project"
msgstr "Creați un proiect"
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr "Pentru mai multă siguranță, va trebui să vă verificați identitatea. V-am trimis un cod de verificare la %{email}."
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr "Ajutați-ne să vă protejăm contul"
@@ -19807,15 +19960,36 @@ msgstr "Dacă nu ați încercat recent să vă conectați la GitLab, vă recoman
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr "Dacă ați pierdut accesul la adresa de e-mail asociată acestui cont sau aveți probleme cu codul, %{link_start}există și alte măsuri pe care le puteți lua.%{link_end}"
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr "Numărul maxim de încercări de autentificare a fost depășit. Așteptați %{interval} și încercați din nou."
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr "Vă rugăm să introduceți un cod valid"
msgid "IdentityVerification|Resend code"
msgstr "Retrimiteți codul"
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr "Codul a expirat. Retrimiteți un nou cod și încercați din nou."
@@ -19837,6 +20011,9 @@ msgstr "Verificați-vă identitatea"
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."
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr "Ați atins numărul maxim de încercări. Așteptați %{interval} sau retrimiteți un nou cod și încercați din nou."
@@ -19906,9 +20083,6 @@ msgstr "Dacă folosiți GitHub, veți vedea pe GitHub stările pipeline-ului pen
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr "Dacă adăugați %{codeStart}needs%{codeEnd} la joburile din pipeline-ul dvs., veți putea vizualiza relațiile %{codeStart}needs%{codeEnd} între joburi în această filă sub forma unui %{linkStart}Grafic Aciclic Direcționat (DAG)%{linkEnd}."
-msgid "If you are added to a project, it will be displayed here."
-msgstr "Dacă sunteți adăugat la un proiect, acesta va fi afișat aici."
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr "Dacă nu ați inițiat aceste încercări de conectare, vă rugăm să contactați administratorul dvs. sau să activați autentificarea cu doi factori (2FA) în contul dvs."
@@ -20170,9 +20344,6 @@ 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 "In a seat"
-msgstr "Pe un seat"
-
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."
@@ -20962,6 +21133,9 @@ msgstr "Eroare la crearea unui eveniment cronologic al incidentului: %{error}"
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr "Eroare la ștergerea evenimentului cronologic al incidentului: %{error}"
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr "Eroare la actualizarea evenimentului din cronologia incidentului: %{error}"
+
msgid "Incident|Metrics"
msgstr "Metrici"
@@ -20980,6 +21154,9 @@ msgstr "Ceva nu a mers bine la ștergerea evenimentului cronologic al incidentul
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr "Ceva nu a mers bine în timpul preluării evenimentelor din cronologia incidentelor."
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr "Ceva nu a mers bine în timp ce se actualiza evenimentul cronologic al incidentului."
+
msgid "Incident|Summary"
msgstr "Rezumat"
@@ -21013,8 +21190,8 @@ msgstr "Includeți numele autorului problemei, al merge request-ului sau al come
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr "Includeți numele de utilizator în URL, dacă este necesar: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
-msgstr "Include obiecte LFS. Se poate suprascrie pentru fiecare grup sau proiect. 0 pentru nelimitat."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
+msgstr ""
msgid "Includes an MVC structure to help you get started"
msgstr "Include o structură MVC pentru a vă ajuta să începeți"
@@ -21154,9 +21331,15 @@ msgstr "Inserați rândul înainte"
msgid "Insert suggestion"
msgstr "Inserați sugestia"
+msgid "Insert table"
+msgstr "Inserare tabel"
+
msgid "Insights"
msgstr "Insights"
+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 "Configurați un raport insights personalizat asupra proceselor grupului, cum ar fi numărul de probleme, bug-uri și merge request-uri pe lună. %{linkStart}Cum se configurează un raport insights?%{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 "Unele elemente nu sunt vizibile pentru că proiectul a fost filtrat în fișierul insights.yml (consultați configurația projects.only pentru mai multe informații)."
@@ -21427,6 +21610,9 @@ msgstr "Trimiteți notificări despre evenimentele proiectului către Unify Circ
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr "Trimiteți notificări despre evenimentele proiectului către o conversație Unify Circuit. %{docs_link}"
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr "Autentificați-vă în GitLab"
@@ -21739,6 +21925,9 @@ 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|Review the invite errors and try again:"
msgstr "Examinați erorile de invitație și încercați din nou:"
@@ -21754,6 +21943,12 @@ msgstr "Selectați un rol"
msgid "InviteMembersModal|Select members or type email addresses"
msgstr "Selectați membrii sau tastați adresele de e-mail"
+msgid "InviteMembersModal|Show less"
+msgstr "Afișați mai puțin"
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr "Afișați mai mult (%{count})"
+
msgid "InviteMembersModal|Something went wrong"
msgstr "Ceva nu a mers bine"
@@ -21772,9 +21967,6 @@ msgstr "Pentru a atribui probleme unui nou membru al echipei, aveți nevoie de u
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr "Se pot obține mai mulți membri dacă un proprietar al grupului %{trialLinkStart}începe o perioadă de probă%{trialLinkEnd} sau %{upgradeLinkStart}actualizează%{upgradeLinkEnd} la un nivel plătit."
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr "Pentru a face mai mult spațiu, puteți înlătura membrii care nu mai au nevoie de acces."
-
msgid "InviteMembersModal|Username or email address"
msgstr "Nume de utilizator sau adresă de e-mail"
@@ -21799,9 +21991,6 @@ msgstr "Invitați membri la proiectul %{strongStart}%{name}%{strongEnd}."
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr "Ați atins limita de %{count} %{members} pentru %{name}"
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr "Ați atins limita de %{count} %{members} pentru proiectele dvs. personale"
-
msgid "InviteMembers|Invite a group"
msgstr "Invitați un grup"
@@ -21919,12 +22108,18 @@ msgstr "Folosește seat de licență:"
msgid "Is using seat"
msgstr "Folosește seat"
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr "%{wi_type} creat(ă) la %{created_at} de "
+
msgid "IssuableStatus|Closed"
msgstr "Închis(ă)"
msgid "IssuableStatus|Closed (%{link})"
msgstr "Închis(ă) (%{link})"
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr "Creat(ă) la %{created_at} de"
+
msgid "IssuableStatus|duplicated"
msgstr "duplicat"
@@ -22243,9 +22438,6 @@ msgstr "Configurația cadenței nu este valabilă."
msgid "Iterations|Cadence name"
msgstr "Numele cadenței"
-msgid "Iterations|Can be converted"
-msgstr "Poate fi convertit(ă)"
-
msgid "Iterations|Cancel"
msgstr "Anulare"
@@ -22291,6 +22483,9 @@ msgstr "Editați iterația"
msgid "Iterations|Edit iteration cadence"
msgstr "Editați cadența de iterație"
+msgid "Iterations|Enable automatic scheduling"
+msgstr "Activați programarea automată"
+
msgid "Iterations|Enable roll over"
msgstr "Activați roll over"
@@ -22306,12 +22501,6 @@ msgstr "Iterațiile sunt o modalitate de a urmări problemele pe un interval de
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr "Iterațiile sunt programate să înceapă în zilele de %{weekday}."
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr "Aflați mai multe despre programarea automată"
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr "Gestionarea manuală a iterațiilor va fi eliminată în GitLab 15.6. Convertiți-vă cadența manuală pentru a utiliza programarea automată atunci când sunteți pregătit."
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr "Mutați problemele incomplete la următoarea iterație."
@@ -22369,9 +22558,6 @@ msgstr "Durata fiecărei iterații (în săptămâni)."
msgid "Iterations|The iteration has been deleted."
msgstr "Iterația a fost ștearsă."
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr "Această cadență poate fi convertită pentru a utiliza programarea automată"
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr "Acest lucru va șterge cadența, precum și toate iterațiile din cadrul acesteia."
@@ -22381,9 +22567,6 @@ msgstr "Acest lucru va elimina iterația din toate problemele care îi sunt atri
msgid "Iterations|Title"
msgstr "Titlu"
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr "Pentru a converti această cadență în programare automată, adăugați o durată și un număr de iterații viitoare. Actualizarea este ireversibilă."
-
msgid "Iterations|Unable to find iteration cadence."
msgstr "Nu se poate găsi cadența iterațiilor."
@@ -22396,9 +22579,6 @@ msgstr "Nu se poate salva cadența. Vă rugăm să încercați din nou."
msgid "Iterations|Upcoming iterations"
msgstr "Iterațiile viitoare"
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr "Cadența dvs. manuală poate fi convertită pentru a utiliza programarea automată."
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr "Datele nu se pot suprapune cu alte Iterații existente în cadrul acestui grup."
@@ -22693,9 +22873,6 @@ msgstr "Jobul a fost șters cu succes!"
msgid "Job has wrong arguments format."
msgstr "Jobul are un format greșit de argumente."
-msgid "Job is missing the `model_type` argument."
-msgstr "Jobului îi lipseÈ™te argumentul „model_typeâ€."
-
msgid "Job is stuck. Check runners."
msgstr "Jobul este blocat. Verificați executorii."
@@ -23131,8 +23308,8 @@ msgstr "Etichetele pot fi aplicate la %{features}. Etichetele de grup sunt dispo
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."
-msgstr "Etichetele pot fi aplicate problemelor și merge request-urilor."
+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 with no issues in this iteration:"
msgstr "Etichete fără probleme în această iterație:"
@@ -23146,6 +23323,9 @@ msgstr "Promovați eticheta"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr "Promovarea etichetei %{labelTitle} o va face disponibilă pentru toate proiectele din cadrul %{groupName}. Etichetele de proiect existente cu același titlu vor fi îmbinate. Dacă există o etichetă de grup cu același titlu, aceasta va fi, de asemenea, îmbinată. Această acțiune este ireversibilă."
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr "Limbaj"
@@ -23317,9 +23497,6 @@ msgstr "Durata execuției"
msgid "Learn GitLab"
msgstr "Învățați GitLab"
-msgid "Learn GitLab - Ultimate trial"
-msgstr "Învățați GitLab - Versiunea de încercare Ultimate"
-
msgid "Learn More"
msgstr "Aflați mai multe"
@@ -23371,6 +23548,9 @@ msgstr "Aflați mai multe despre șabloanele de proiect la nivel de grup"
msgid "Learn more about groups."
msgstr "Aflați mai multe despre grupuri."
+msgid "Learn more about issues."
+msgstr "Aflați mai multe despre probleme."
+
msgid "Learn more about max seats used"
msgstr "Aflați mai multe despre numărul maxim de seat-uri utilizate"
@@ -24109,6 +24289,9 @@ msgstr "Cheia de semnare a webhook-ului HTTP Mailgun"
msgid "Mailgun events"
msgstr "Evenimente Mailgun"
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr "Modul de întreținere"
@@ -24397,11 +24580,8 @@ msgstr "Numărul maxim de utilizatori"
msgid "Maximum allowable lifetime for access token (days)"
msgstr "Durata de viață maximă permisă pentru tokenul de acces (zile)"
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr "Durata de viață maximă permisă pentru tokenul de acces personal (zile)"
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
-msgstr "Durata de viață maximă permisă pentru cheile SSH (în zile)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
+msgstr ""
msgid "Maximum artifacts size"
msgstr "Mărimea maximă a artefactelor"
@@ -24886,6 +25066,9 @@ msgstr "Merge request-ul a fost programat pentru îmbinare după ce pipeline-ul
msgid "Merge requests"
msgstr "Merge request-uri"
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr "Merge request-urile sunt un loc în care puteți propune modificările pe care le-ați făcut la un proiect și să discutați aceste modificări cu alții."
@@ -24973,11 +25156,14 @@ msgstr "A apărut o eroare în timpul salvării draftului de comentariu."
msgid "MergeRequests|Create issue to resolve thread"
msgstr "Creați o problemă pentru a rezolva subiectul"
+msgid "MergeRequests|Reference copied"
+msgstr "Referință copiată"
+
msgid "MergeRequests|Saving the comment failed"
msgstr "Salvarea comentariului a eșuat"
msgid "MergeRequests|Squashing failed: Squash the commits locally, resolve any conflicts, then push the branch."
-msgstr "Squashing-ul a eșuat: Faceți local un squash al commit-urilor, rezolvați orice conflicte, apoi faceți un push al ramurii."
+msgstr "Squashing-ul a eșuat: Faceți squash de commit-uri la nivel local, rezolvați orice conflicte, apoi faceți push la ramură."
msgid "MergeRequests|Squashing not allowed: This project doesn't allow you to squash commits when merging."
msgstr "Squashing-ul nu este permis: Acest proiect nu vă permite să faceți squash de commit-uri la îmbinare."
@@ -25048,6 +25234,30 @@ msgstr "Nu au fost găsite fișiere"
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr "Căutare (de exemplu, *.vue) (%{modifier_key}P)"
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr "%{sourceTopic} va fi înlăturat"
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr "Toate proiectele atribuite vor fi mutate în %{targetTopic}"
+
+msgid "MergeTopics|Merge topics"
+msgstr "Îmbinați subiectele"
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr "Îmbinarea subiectelor va avea ca rezultat următoarele:"
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr "Mutați toate proiectele atribuite de la subiectul sursă la subiectul țintă și se înlătură subiectul sursă."
+
+msgid "MergeTopics|Source topic"
+msgstr "Subiectul sursă"
+
+msgid "MergeTopics|Target topic"
+msgstr "Subiectul țintă"
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr "Această acțiune este ireversibilă."
+
msgid "Merged"
msgstr "ÃŽmbinat"
@@ -25726,18 +25936,21 @@ msgstr "Modificați mesajele commit-urilor"
msgid "Modify merge commit"
msgstr "Modificați commit-ul îmbinării"
+msgid "Mon"
+msgstr "Lun"
+
msgid "Monday"
msgstr "Luni"
msgid "Monitor"
msgstr "Monitor"
+msgid "Monitor GitLab with Prometheus."
+msgstr ""
+
msgid "Monitor Settings"
msgstr "Setări monitor"
-msgid "Monitor the health and performance of GitLab with Prometheus."
-msgstr "Monitorizați sănătatea și performanța GitLab cu Prometheus."
-
msgid "Monitor your errors by integrating with Sentry."
msgstr "Monitorizați erorile prin integrarea cu Sentry."
@@ -25750,6 +25963,9 @@ msgstr "Lună"
msgid "Months"
msgstr "Luni"
+msgid "More"
+msgstr "Mai mult"
+
msgid "More Details"
msgstr "Mai multe detalii"
@@ -25792,9 +26008,6 @@ msgstr "Cel mai relevant"
msgid "Most stars"
msgstr "Cele mai multe stele"
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr "Punctul de montare %{mounted_as} nu a fost găsit în %{model_class}."
-
msgid "Move"
msgstr "Mutare"
@@ -25885,12 +26098,6 @@ msgstr "Sunt acceptate mai multe intervale de adrese IP. Nu afectează accesul l
msgid "Multiple Prometheus integrations are not supported"
msgstr "Integrările multiple Prometheus nu sunt acceptate"
-msgid "Multiple model types found: %{model_types}"
-msgstr "Au fost găsite mai multe tipuri de modele: %{model_types}"
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr "Au fost găsite mai multe încărcătoare: %{uploader_types}"
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr "Multiplicator aplicabil intervalelor de sondare. Sunt acceptate valorile zecimale. Valoarea implicită este 1."
@@ -26416,6 +26623,9 @@ msgstr "Nu există date de card de credit pentru potrivire"
msgid "No credit card required."
msgstr "Nu este nevoie de card de credit."
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr "Nu s-au găsit date"
@@ -26428,9 +26638,6 @@ msgstr "Nu a fost detectată nicio implementare. Utilizați mediile pentru a con
msgid "No deployments found"
msgstr "Nu s-au găsit implementări"
-msgid "No due date"
-msgstr "Fără dată scadentă"
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr "Nu au fost adăugați participanți prin e-mail. Fie nu a fost furnizat niciunul, fie aceștia există deja."
@@ -26581,6 +26788,9 @@ msgstr "Niciun repozitoriu"
msgid "No results"
msgstr "Niciun rezultat"
+msgid "No results found"
+msgstr "Nu s-a găsit niciun rezultat"
+
msgid "No runner executable"
msgstr "Niciun executor disponibil"
@@ -26602,9 +26812,6 @@ msgstr "Nicio urmă de stivă pentru această eroare"
msgid "No starrers matched your search"
msgstr "Niciun marcator cu stele nu a corespuns căutării dvs."
-msgid "No start date"
-msgstr "Fără dată de început"
-
msgid "No suggestions found"
msgstr "Nu s-au găsit sugestii"
@@ -26794,6 +27001,9 @@ msgstr "Data de creare furnizată este cu mult prea îndepărtată în trecut."
msgid "Nothing to preview."
msgstr "Nimic de previzualizat."
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr "Evenimente de notificare"
@@ -26932,6 +27142,9 @@ msgstr "Problema creată de %{author_link} %{issue_reference_link} va expira în
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr "%{commit_link} în %{mr_link}"
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr "%{invite_email}, cunoscut acum sub numele de %{user_name}, a acceptat invitația de a se alătura la %{target_name} %{target_model_name}."
@@ -26941,6 +27154,24 @@ msgstr "%{invited_user} a %{highlight_start}refuzat%{highlight_end} invitația d
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr "%{member_link} a solicitat %{member_role} acces la %{target_source_link} %{target_type}."
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr "%{mr_highlight}Merge request-ul%{highlight_end} %{mr_link} %{approved_highlight}a fost aprobată de%{highlight_end} %{approver_avatar} %{approver_link}"
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr "%{paragraph_start}Bună, %{name}!%{paragraph_end} %{paragraph_start}A fost adăugată o nouă cheie publică în contul dvs.:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}În cazul în care această cheie a fost adăugată din greșeală, o puteți elimina la %{removal_link}%{paragraph_end}"
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr "O nouă cheie GPG a fost adăugată în contul dvs.:"
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr "Responsabilul a fost schimbat de la %{fromNames} la %{toNames}"
@@ -26956,6 +27187,18 @@ msgstr "Pipeline-ul Auto DevOps a fost dezactivat pentru %{project}"
msgid "Notify|CI/CD project settings"
msgstr "Setări de proiect CI/CD"
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr "Amprenta: %{fingerprint}"
+
+msgid "Notify|Hi %{user}!"
+msgstr "Salut %{user}!"
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr "Dacă această cheie a fost adăugată din greșeală, o puteți înlătura prin %{removal_link}"
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr "Dacă nu mai doriți să utilizați acest domeniu cu GitLab Pages, vă rugăm să-l înlăturați din proiectul dumneavoastră GitLab și să ștergeți toate înregistrările DNS aferente."
@@ -26986,9 +27229,18 @@ msgstr "Merge request-ul %{mr_link} a fost închis de %{closed_by}"
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr "URL-ul merge request-ului: %{merge_request_url}"
+msgid "Notify|Merge request was approved"
+msgstr "Merge request-ul a fost aprobat"
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr "Merge request-ul a fost aprobat (%{approvals}/%{required_approvals})"
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr "Obiectivul a fost schimbat in %{milestone}"
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr "Problema nouă: %{project_issue_url}"
@@ -27007,6 +27259,12 @@ msgstr "Dacă nu vă verificați domeniul până la %{time_start}%{time}%{time_e
msgid "Notify|You don't have access to the project."
msgstr "Nu aveți acces la proiect."
+msgid "Notify|You have been mentioned in an issue."
+msgstr "Ați fost menționat într-o problemă."
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr "Ați fost menționat în merge request-ul %{mr_link}"
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr "Solicitarea dvs. de a vă alătura la %{target_to_join} %{target_type} a fost %{denied_tag}."
@@ -27112,6 +27370,9 @@ msgstr[0] "La %{end_date}, perioada de probă se va încheia și %{namespace_nam
msgstr[1] "La %{end_date}, perioada de probă se va încheia, iar %{namespace_name} va fi limitat la %{free_user_limit} membri."
msgstr[2] "La %{end_date}, perioada de probă se va încheia, iar %{namespace_name} va fi limitat la %{free_user_limit} de membri."
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr "Conform planului"
@@ -27331,6 +27592,12 @@ msgstr "Ștergeți profilul"
msgid "OnDemandScans|Description (optional)"
msgstr "Descriere (opțional)"
+msgid "OnDemandScans|Discard changes"
+msgstr "Renunțați la modificări"
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr "Preferați să renunțați la modificări sau să continuați cu editarea acestui profil? Modificările nesalvate vor fi pierdute."
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr "Testarea dinamică a securității aplicațiilor (DAST)"
@@ -27352,6 +27619,9 @@ msgstr "Activați programul de scanare"
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr "De exemplu: Testarea unei pagini de autentificare pentru injecții SQL"
+msgid "OnDemandScans|Keep editing"
+msgstr "Continuați să editați"
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr "Gestionați profilurile de scanare"
@@ -27475,6 +27745,9 @@ msgstr "Utilizați profilul site-ului existent"
msgid "OnDemandScans|View results"
msgstr "Vizualizați rezultatul"
+msgid "OnDemandScans|You have unsaved changes"
+msgstr "Aveți modificări nesalvate"
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr "Trebuie să creați un repozitoriu în cadrul proiectului dvs. pentru a rula o scanare la cerere."
@@ -27631,9 +27904,15 @@ msgstr "Deschis: %{open}"
msgid "OpenAPI"
msgstr "OpenAPI"
+msgid "OpenAPI Specification file URL"
+msgstr "URL-ul fișierului de specificații OpenAPI"
+
msgid "OpenAPI Specification file path or URL"
msgstr "Calea sau URL-ul fișierului de specificații OpenAPI"
+msgid "OpenSearch's region."
+msgstr "Regiunea OpenSearch."
+
msgid "Opened"
msgstr "Deschis"
@@ -27847,6 +28126,9 @@ msgstr "Registrul de pachete: solicitări API neautentificate"
msgid "Package already exists"
msgstr "Pachetul există deja"
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr "Pachetul a fost șters cu succes"
@@ -27883,6 +28165,9 @@ msgstr "Tipul pachetului trebuie să fie NuGet"
msgid "Package type must be PyPi"
msgstr "Tipul pachetului trebuie să fie PyPi"
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr "Tipul pachetului trebuie să fie RubyGems"
@@ -27907,6 +28192,9 @@ msgstr "Adăugați registrul compozitorului"
msgid "PackageRegistry|Additional metadata"
msgstr "Metadate suplimentare"
+msgid "PackageRegistry|Allow duplicates"
+msgstr "Permiteți duplicatele"
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr "Permiteți încărcarea în registru a pachetelor cu același nume și versiune. Cea mai nouă versiune a unui pachet este întotdeauna utilizată la instalare."
@@ -28126,6 +28414,9 @@ msgstr "Pachetul a fost șters cu succes"
msgid "PackageRegistry|Package file deleted successfully"
msgstr "Fișierul pachet a fost șters cu succes"
+msgid "PackageRegistry|Package formats"
+msgstr "Formatele pachetelor"
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] "Pachetul are %{updatesCount} actualizare arhivată"
@@ -28159,9 +28450,6 @@ msgstr "Rețetă: %{recipe}"
msgid "PackageRegistry|Registry setup"
msgstr "Configurarea registrului"
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr "Respingeți pachetele cu același nume și versiune"
-
msgid "PackageRegistry|Remove package"
msgstr "Înlăturarea pachetului"
@@ -28171,12 +28459,6 @@ msgstr "Python necesar: %{pythonVersion}"
msgid "PackageRegistry|RubyGems"
msgstr "RubyGems"
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr "Setări pentru pachetele generice"
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr "Setări pentru pachetele Maven"
-
msgid "PackageRegistry|Show Composer commands"
msgstr "Afișați comenzile Composer"
@@ -28279,8 +28561,8 @@ msgstr "npm"
msgid "PackageRegistry|published by %{author}"
msgstr "publicat de %{author}"
-msgid "Packages & Registries"
-msgstr "Pachete și registre"
+msgid "Packages and registries"
+msgstr ""
msgid "Page not found"
msgstr "Pagina nu a fost găsită"
@@ -28528,9 +28810,6 @@ msgstr "Efectuați revizuiri de cod și îmbunătățiți colaborarea cu merge r
msgid "Perform common operations on GitLab project"
msgstr "Efectuați operații comune în proiectul GitLab"
-msgid "Performance insights"
-msgstr "Perspective despre performanță"
-
msgid "Performance optimization"
msgstr "Optimizarea performanțelor"
@@ -28606,6 +28885,12 @@ msgstr "perete"
msgid "Period in seconds"
msgstr "Perioada în secunde"
+msgid "Period of inactivity (days)"
+msgstr "Perioada de inactivitate (zile)"
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr "Permalink"
@@ -29311,27 +29596,18 @@ msgstr "Se creează pipeline-ul."
msgid "Pipeline|Date"
msgstr "Data"
-msgid "Pipeline|Detached merge request pipeline"
-msgstr "Pipeline de merge request detașat"
-
msgid "Pipeline|Failed"
msgstr "Eșuat"
-msgid "Pipeline|Five slowest jobs"
-msgstr "Cinci dintre cele mai lente joburi"
-
msgid "Pipeline|In progress"
msgstr "ÃŽn curs"
-msgid "Pipeline|Last executed job"
-msgstr "Ultimul job executat"
-
-msgid "Pipeline|Longest queued job"
-msgstr "Cel mai lung job din coadă"
-
msgid "Pipeline|Manual"
msgstr "Manual"
+msgid "Pipeline|Merge request pipeline"
+msgstr ""
+
msgid "Pipeline|Merge train pipeline"
msgstr "Pipeline de merge train"
@@ -29341,18 +29617,12 @@ msgstr "Joburile pipeline de merge train nu pot fi reîncercate"
msgid "Pipeline|Merged result pipeline"
msgstr "Pipeline de rezultate îmbinate"
-msgid "Pipeline|Only able to show first 100 results"
-msgstr "Nu se pot afișa decât primele 100 de rezultate"
-
msgid "Pipeline|Passed"
msgstr "A trecut"
msgid "Pipeline|Pending"
msgstr "În așteptare"
-msgid "Pipeline|Performance insights"
-msgstr "Perspective despre performanță"
-
msgid "Pipeline|Pipeline"
msgstr "Pipeline"
@@ -29410,12 +29680,6 @@ msgstr "Numele etichetei"
msgid "Pipeline|Test coverage"
msgstr "Coverage de testare"
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr "Ultimul job executat este ultimul job care începe în pipeline."
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr "Cel mai lung job din coadă este jobul care a petrecut cel mai mult timp în stare de așteptare, așteptând să fie preluat de un executor"
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr "Această modificare va reduce gradul general de coverage de teste dacă va fi îmbinată."
@@ -29446,9 +29710,6 @@ msgstr "Variabile"
msgid "Pipeline|View commit"
msgstr "Vizualizați commit-ul"
-msgid "Pipeline|View dependency"
-msgstr "Vizualizați dependența"
-
msgid "Pipeline|View pipeline"
msgstr "Vizualizați pipeline-ul"
@@ -29548,8 +29809,8 @@ msgstr "Vă rugăm să vă verificați e-mailul %{email} pentru a vă confirma c
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr "Vă rugăm să vă verificați e-mailul (%{email}) pentru a confirma că sunteți proprietarul acestei adrese și pentru a debloca puterea CI/CD. Nu l-ați primit? %{resend_link}. Adresa de e-mail greșită? %{update_link}."
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
-msgstr "Faceți clic pe linkul din e-mailul de confirmare înainte de a continua. Acesta a fost trimis la "
+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 your profile with email address"
msgstr "Vă rugăm să completați profilul dvs. cu adresa de e-mail"
@@ -29764,6 +30025,9 @@ msgstr "Portul"
msgid "Postman collection"
msgstr "Colecția Postman"
+msgid "Postman collection file URL"
+msgstr "URL-ul fișierului de colectare Postman"
+
msgid "Postman collection file path or URL"
msgstr "Calea sau URL-ul fișierului de colecție Postman"
@@ -29785,12 +30049,12 @@ msgstr "Comportament"
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr "Alegeți între aspectul fix (max. 1280px) și fluid (%{percentage}) al aplicației."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
+msgstr ""
+
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 what content you want to see on your homepage."
-msgstr "Alegeți ce conținut doriți să vedeți pe pagina dvs. de pornire."
-
msgid "Preferences|Color for added lines"
msgstr "Culoare pentru linii adăugate"
@@ -29812,6 +30076,9 @@ msgstr "Personalizați culoarea GitLab."
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr "Personalizați culorile liniilor eliminate și adăugate în diff-uri."
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr "Culorile diff-ului"
@@ -29833,9 +30100,6 @@ msgstr "De exemplu: Acum 30 de minute."
msgid "Preferences|Gitpod"
msgstr "Gitpod"
-msgid "Preferences|Homepage content"
-msgstr "Conținutul paginii de start"
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr "În loc să afișați toate fișierele modificate, afișați doar un singur fișier la un moment dat. Pentru a trece de la un fișier la altul, utilizați browserul de fișiere."
@@ -30247,9 +30511,6 @@ msgstr "Titlul jobului"
msgid "Profiles|Key"
msgstr "Cheie"
-msgid "Profiles|Key becomes invalid on this date."
-msgstr "Cheia devine invalidă la această dată."
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr "Cheia devine invalidă la această dată. Durata maximă de viață pentru cheile SSH este de %{max_ssh_key_lifetime} de zile"
@@ -30280,6 +30541,9 @@ msgstr "Nu a fost ales niciun fișier."
msgid "Profiles|Notification email"
msgstr "E-mail de notificare"
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr "Opțional, dar recomandat. Dacă este setată, cheia devine nevalabilă la data specificată."
+
msgid "Profiles|Organization"
msgstr "Organizație"
@@ -30967,6 +31231,9 @@ msgstr "Activați pipeline-urile de rezultate combinate"
msgid "ProjectSettings|Encourage"
msgstr "Încurajați"
+msgid "ProjectSettings|Environments"
+msgstr "Medii"
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr "Fiecare îmbinare creează un commit de îmbinare."
@@ -30979,6 +31246,9 @@ msgstr "Fiecare proiect poate avea propriul spațiu de depozitare a pachetelor s
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."
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr "Fiecare proiect poate efectua implementări în medii fie prin CI/CD, fie prin apeluri API. Membrii din afara proiectului au acces doar în citire."
+
msgid "ProjectSettings|Everyone"
msgstr "Toată lumea"
@@ -30997,11 +31267,14 @@ msgstr "ÃŽmbinare fast-forward"
msgid "ProjectSettings|Fast-forward merges only."
msgstr "Numai îmbinări fast-forward."
+msgid "ProjectSettings|Feature flags"
+msgstr "Feature Flags"
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr "Instrument flexibil pentru dezvoltarea în colaborare a ideilor și planificarea activității în cadrul acestui proiect."
msgid "ProjectSettings|Forks"
-msgstr "Fork-uri"
+msgstr "Forkuri"
msgid "ProjectSettings|Git Large File Storage (LFS)"
msgstr "Git Large File Storage (LFS)"
@@ -31138,6 +31411,9 @@ msgstr "Cerințe"
msgid "ProjectSettings|Requirements management system."
msgstr "Sistemul de gestionare a cerințelor."
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr "Lansați noi caracteristici fără a redistribui cu feature flags."
+
msgid "ProjectSettings|Search for topic"
msgstr "Căutați un subiect"
@@ -31948,8 +32224,8 @@ msgstr "implicit"
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr "%{environment_name} va putea fi editat de dezvoltatori. Sunteți sigur?"
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
-msgstr "Toate mediile specificate cu nivelurile de implementare de mai jos sunt protejate de un grup părinte. %{link_start}Aflați mai multe%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
msgstr "Autorizat să desfășoare"
@@ -31966,8 +32242,8 @@ msgstr "Medii protejate în amonte"
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr "Încărcarea detaliilor pentru acest grup a eșuat."
-msgid "ProtectedEnvironment|No environments in this project are projected."
-msgstr "Acest proiect nu are niciun mediu protejat."
+msgid "ProtectedEnvironment|No environments in this project are protected."
+msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
msgstr "Numai grupurile specificate pot executa implementări în medii protejate."
@@ -32149,9 +32425,6 @@ msgstr "Evenimente push"
msgid "Push project from command line"
msgstr "Faceți push proiectului din linia de comandă"
-msgid "Push rules"
-msgstr "Reguli pentru push"
-
msgid "Push the target branch up to GitLab."
msgstr "Faceți push ramurii țintă către GitLab."
@@ -32275,9 +32548,6 @@ msgstr "Ajutor rapid"
msgid "Quick range"
msgstr "Interval rapid"
-msgid "Quickly and easily edit multiple files in your project."
-msgstr "Editați rapid și ușor mai multe fișiere din proiectul dumneavoastră."
-
msgid "Quota of CI/CD minutes"
msgstr "Cotă de minute CI/CD"
@@ -32509,6 +32779,9 @@ msgstr "Înregistrați executorul cu acest URL:"
msgid "Register with two-factor app"
msgstr "Înregistrați-vă cu aplicația cu doi factori"
+msgid "Register with:"
+msgstr "Înregistrați-vă cu:"
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr "Activați serviciul Ping și înregistrați-vă pentru această funcție."
@@ -33892,6 +34165,9 @@ msgstr "Activ"
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr "Adăugați note, cum ar fi cine este proprietarul executorului sau la ce ar trebui să fie folosit."
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr "Toți"
@@ -33907,6 +34183,12 @@ 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 upgrade is available for this runner"
+msgstr "Este disponibil un upgrade pentru acest executor"
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr "Se recomandă un upgrade pentru acest executor"
+
msgid "Runners|Architecture"
msgstr "Arhitectură"
@@ -33955,6 +34237,9 @@ msgstr "Copiați instrucțiunile"
msgid "Runners|Copy registration token"
msgstr "Copiați tokenul de înregistrare"
+msgid "Runners|Created %{timeAgo}"
+msgstr "Creat %{timeAgo}"
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] "Ștergeți %d executor"
@@ -34003,6 +34288,9 @@ msgstr "Introduceți numărul de secunde. Acest timp de expirare are prioritate
msgid "Runners|Executor"
msgstr "Executor"
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr "Începeți cu executorii"
@@ -34027,6 +34315,9 @@ msgstr "Joburi"
msgid "Runners|Last contact"
msgstr "Ultimul contact"
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr "Ultimul contact: %{timeAgo}"
+
msgid "Runners|Locked to this project"
msgstr "Blocat pentru acest proiect"
@@ -34072,24 +34363,15 @@ msgstr "Nu acceptă joburi"
msgid "Runners|Offline"
msgstr "Offline"
-msgid "Runners|Offline runners"
-msgstr "Executori offline"
-
msgid "Runners|Offline:"
msgstr "Offline:"
msgid "Runners|Online"
msgstr "Online"
-msgid "Runners|Online runners"
-msgstr "Executori online"
-
msgid "Runners|Online:"
msgstr "Online:"
-msgid "Runners|Outdated"
-msgstr "ÃŽnvechit"
-
msgid "Runners|Pause from accepting jobs"
msgstr "Întrerupeți acceptarea de joburi"
@@ -34237,9 +34519,6 @@ msgstr "Ceva nu a mers bine la preluarea sugestiilor de etichete"
msgid "Runners|Stale"
msgstr "Inactiv"
-msgid "Runners|Stale runners"
-msgstr "Executori inactivi"
-
msgid "Runners|Stale:"
msgstr "Inactiv:"
@@ -34324,9 +34603,18 @@ msgstr "Valoare"
msgid "Runners|Version"
msgstr "Versiune"
+msgid "Runners|Version %{version}"
+msgstr "Versiune %{version}"
+
msgid "Runners|View installation instructions"
msgstr "Vizualizați instrucțiunile de instalare"
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr "Windows 2019 Shell cu scalare manuală și programare opțională. Spot %{percentage}."
@@ -34345,42 +34633,18 @@ msgstr "Ați folosit %{quotaUsed} din cele %{quotaLimit} de minute ale dvs. de p
msgid "Runners|active"
msgstr "activ"
-msgid "Runners|available"
-msgstr "disponibil"
-
msgid "Runners|group"
msgstr "grup"
-msgid "Runners|never contacted"
-msgstr "niciodată contactat"
-
-msgid "Runners|offline"
-msgstr "offline"
-
-msgid "Runners|online"
-msgstr "online"
-
msgid "Runners|paused"
msgstr "în pauză"
-msgid "Runners|recommended"
-msgstr "recomandat"
-
msgid "Runners|shared"
msgstr "partajat"
msgid "Runners|specific"
msgstr "specific"
-msgid "Runners|stale"
-msgstr "inactiv"
-
-msgid "Runners|upgrade available"
-msgstr "actualizare disponibilă"
-
-msgid "Runners|upgrade recommended"
-msgstr "upgrade recomandat"
-
msgid "Runner|Owner"
msgstr "Proprietar"
@@ -34474,6 +34738,9 @@ msgstr "Verificare SSL:"
msgid "SSL verification"
msgstr "Verificare SSL"
+msgid "Sat"
+msgstr "Sâm"
+
msgid "Satisfied"
msgstr "Mulțumit"
@@ -34519,12 +34786,27 @@ msgstr "Se salvează"
msgid "Saving project."
msgstr "Se salvează proiectul."
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr "%{ifLabelStart}dacă%{ifLabelEnd} acțiunile %{rules} pentru %{scopes} %{branches}"
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr "%{ifLabelStart}dacă%{ifLabelEnd} %{rules} pentru ramura/ramurile %{branches}"
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr "ScanExecutionPolicy|%{period} %{days} la %{time}"
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr "%{thenLabelStart}Atunci%{thenLabelEnd} necesită executarea unei scanări %{scan}"
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr "Se execută un pipeline"
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr "Programare"
@@ -34534,6 +34816,18 @@ msgstr "Elementul regulii de programare"
msgid "ScanExecutionPolicy|Select branches"
msgstr "Selectați ramurile"
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr "ramura"
+
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}"
@@ -35125,6 +35419,9 @@ msgstr "Previzualizare .yaml"
msgid "SecurityOrchestration|Actions"
msgstr "Acțiuni"
+msgid "SecurityOrchestration|Add action"
+msgstr "Adăugare acțiune"
+
msgid "SecurityOrchestration|Add rule"
msgstr "Adăugare regulă"
@@ -35380,6 +35677,9 @@ msgstr "A apărut o problemă în timpul creării noii politici de securitate"
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr "Acest %{namespaceType} nu conține nicio politică de securitate."
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr "Acest %{namespaceType} nu este legat de un proiect de politică de securitate"
+
msgid "SecurityOrchestration|This group"
msgstr "Acest grup"
@@ -36766,11 +37066,20 @@ msgstr "URL-ul paginii de deconectare"
msgid "Sign-up restrictions"
msgstr "Restricții de înscriere"
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr "Făcând clic pe %{button_text} sau înregistrându-vă prin intermediul unei terțe părți, sunteți de acord cu%{link_start} Termenii de utilizare și acceptați Politica de confidențialitate și Politica privind cookie-urile%{link_end} a GitLab"
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr "Făcând clic pe %{button_text} sau înregistrându-vă prin intermediul unei terțe părți, sunteți de acord cu%{link_start} Termenii de utilizare și acceptați Politica de confidențialitate și Politica privind cookie-urile%{link_end}"
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr "Făcând clic pe %{button_text}, sunt de acord că am citit și acceptat %{link_start}Termenii de utilizare și Politica de confidențialitate%{link_end}"
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
-msgstr "Făcând clic pe %{button_text}, sunt de acord că am citit și acceptat Termenii de utilizare și %{link_start}Politica de confidențialitate%{link_end} GitLab"
+msgstr "Făcând clic pe %{button_text}, sunt de acord că am citit și acceptat %{link_start}Termenii de utilizare și Politica de confidențialitate%{link_end} 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 "Prin conectare, acceptați Termenii de utilizare și recunoașteți %{link_start}Politica de confidențialitate și Politica privind cookie-urile%{link_end}."
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr "Prenumele este prea lung (maximum este de %{max_length} de caractere)."
@@ -37045,6 +37354,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 while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr "Ceva nu a mers bine la adăugarea premiului dumneavoastră. Vă rugăm să încercați din nou."
@@ -37135,6 +37447,9 @@ msgstr "Ceva nu a mers bine la obținerea certificatului Let's Encrypt."
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."
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr "Ceva nu a mers bine în timp ce se promova nota la evenimentul cronologic."
+
msgid "Something went wrong while reopening a requirement."
msgstr "Ceva nu a mers bine în timpul redeschiderii unei cerințe."
@@ -37519,6 +37834,9 @@ msgstr "Mesajul commit-ului de squash"
msgid "Squash commits"
msgstr "Squash de commit-uri"
+msgid "Squash commits when merge request is accepted."
+msgstr "Faceți squash de commit-uri atunci când merge request-ul este acceptat."
+
msgid "Stack trace"
msgstr "Urmărire stivă"
@@ -37534,9 +37852,6 @@ msgstr "Etape:"
msgid "Standard"
msgstr "Standard"
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr "Marcați cu stea o etichetă pentru a o transforma într-o etichetă prioritară. Așezați în ordine etichetele prioritizate pentru a le modifica prioritatea relativă, prin glisare."
-
msgid "Star labels to start sorting by priority"
msgstr "Marcați etichetele cu stele pentru a începe sortarea după prioritate"
@@ -38275,6 +38590,9 @@ msgstr "Rezumat / notă"
msgid "Summary comment (optional)"
msgstr "Comentariu sumar (opțional)"
+msgid "Sun"
+msgstr "Dum"
+
msgid "Sunday"
msgstr "Duminică"
@@ -39544,6 +39862,9 @@ msgstr "Pagina nu a putut fi afișată deoarece a expirat."
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr "Epica părinte este confidențială și poate conține numai epice și probleme confidențiale."
+msgid "The parsed YAML is too big"
+msgstr "YAML-ul analizat este prea mare"
+
msgid "The password for the Jenkins server."
msgstr "Parola pentru serverul Jenkins."
@@ -39640,6 +39961,12 @@ msgstr "Fragmentul de cod este vizibil pentru orice utilizator conectat, cu exce
msgid "The source project of this merge request has been removed."
msgstr "Proiectul sursă al acestui merge request a fost înlăturat."
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr "Fila specificată nu este validă, vă rugăm să selectați alta"
@@ -39652,6 +39979,9 @@ msgstr "Subiectul va fi folosit ca titlu al noii probleme, iar mesajul va fi des
msgid "The tag name can't be changed for an existing release."
msgstr "Numele etichetei nu poate fi modificat pentru o versiune existentă."
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr "Perioada de timp în secunde la care se aplică limita maximă de solicitări pe proiect."
@@ -39700,12 +40030,12 @@ msgstr "Tema"
msgid "There are currently no events."
msgstr "În prezent nu există evenimente."
+msgid "There are currently no mirrored repositories."
+msgstr "În prezent, nu există repozitorii replicate."
+
msgid "There are merge conflicts"
msgstr "Există conflicte de îmbinare"
-msgid "There are no %{replicableTypeName} to show"
-msgstr "Nu există niciun/nicio %{replicableTypeName} de afișat"
-
msgid "There are no GPG keys associated with this account."
msgstr "Nu există chei GPG asociate cu acest cont."
@@ -39808,9 +40138,6 @@ msgstr "Există mai multe limite de rată pentru a proteja sistemul."
msgid "There are several size limits in place."
msgstr "Există mai multe limite de mărime în vigoare."
-msgid "There are unsubmitted review comments."
-msgstr "Există comentarii de revizuire nedepuse."
-
msgid "There is already a repository with that name on disk"
msgstr "Există deja un repozitoriu cu acest nume pe disc"
@@ -40162,6 +40489,9 @@ msgstr "Acest bloc este autoreferențial"
msgid "This board's scope is reduced"
msgstr "Acest bord a fost restrâns în scop"
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr "Această modificare va înlătura %{strongOpen}TOATE%{strongClose} caracteristicile Premium și Ultimate pentru %{strongOpen}TOȚI%{strongClose} clienții SaaS și va face ca testele să înceapă să eșueze."
+
msgid "This chart could not be displayed"
msgstr "Acest grafic nu a putut fi afișat"
@@ -40186,8 +40516,8 @@ msgstr "Acest commit a fost semnat cu o semnătură %{strong_open}verificată%{s
msgid "This commit was signed with a different user's verified signature."
msgstr "Acest commit a fost semnat cu semnătura verificată a unui alt utilizator."
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
-msgstr "Acest commit a fost semnat cu o semnătură verificată, dar nu s-a %{strong_open}verificat%{strong_close} dacă adresa de e-mail a comitentului aparține aceluiași utilizator."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
+msgstr "Acest commit a fost semnat cu o semnătură verificată, dar adresa de e-mail a autorului nu este legată de cheia GPG."
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr "Acest commit a fost semnat cu o semnătură %{strong_open}neverificată%{strong_close}."
@@ -40567,6 +40897,9 @@ msgstr "Acest proiect este licențiat sub %{strong_start}%{license_name}%{strong
msgid "This project is not subscribed to any project pipelines."
msgstr "Acest proiect nu este abonat la niciun pipeline de proiecte."
+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 "Acest proiect este public. Non-membrii pot ghici adresa de e-mail Service Desk, deoarece conține grupul și numele proiectului. %{linkStart}Cum pot crea o adresă de e-mail personalizată?%{linkEnd}"
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr "Acest proiect își gestionează dependențele folosind %{strong_start}%{manager_name}%{strong_end}"
@@ -40579,9 +40912,6 @@ msgstr "Acest proiect va fi șters pe %{date}"
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr "Acest proiect va fi șters la %{date}, deoarece grupul său părinte „%{parent_group_name}†a fost programat pentru ștergere."
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr "Acest proiect va fi găzduit în grupul dvs. %{strong_open}%{namespace}%{strong_close}. Un proiect este locul unde vă stocați fișierele (repozitoriu), vă planificați activitatea (probleme), vă publicați documentația (wiki) și multe altele."
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr "Această versiune a fost creată cu o dată din trecut. Colectarea probelor la momentul lansării nu este disponibilă."
@@ -40684,6 +41014,9 @@ msgstr "Limita de modificări (ramuri sau etichete) într-un singur push, peste
msgid "Throughput"
msgstr "Debit"
+msgid "Thu"
+msgstr "Joi"
+
msgid "Thursday"
msgstr "Joi"
@@ -40894,6 +41227,9 @@ msgstr "chiar acum"
msgid "Timeago|right now"
msgstr "chiar acum"
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr "Dezactivați afișarea actualizărilor recente"
@@ -40921,6 +41257,24 @@ msgstr "Timeout pentru cele mai rapide operațiuni Gitaly (în secunde)."
msgid "Timezone"
msgstr "Fusul orar"
+msgid "Time|A"
+msgstr "A"
+
+msgid "Time|AM"
+msgstr "AM"
+
+msgid "Time|P"
+msgstr "P"
+
+msgid "Time|PM"
+msgstr "PM"
+
+msgid "Time|a"
+msgstr "a"
+
+msgid "Time|am"
+msgstr "am"
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "oră"
@@ -40933,6 +41287,12 @@ msgstr[0] "min."
msgstr[1] "min."
msgstr[2] "min."
+msgid "Time|p"
+msgstr "p"
+
+msgid "Time|pm"
+msgstr "pm"
+
msgid "Time|s"
msgstr "s"
@@ -40987,6 +41347,9 @@ msgstr "Pentru a adăuga un sufix personalizat, configurați o adresă de e-mail
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."
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr "Pentru a aproba acest merge request, introduceți parola. Acest proiect necesită ca toate aprobările să fie autentificate."
+
msgid "To complete registration, we need additional details from you."
msgstr "Pentru a finaliza înregistrarea, avem nevoie de detalii suplimentare de la dumneavoastră."
@@ -41149,9 +41512,6 @@ msgstr "Pentru a vă extinde căutarea, modificați sau eliminați filtrele de m
msgid "To widen your search, change or remove filters above."
msgstr "Pentru a vă extinde căutarea, modificați sau înlăturați filtrele de mai sus"
-msgid "To-Do"
-msgstr "De-Făcut"
-
msgid "To-Do List"
msgstr "Lista De-Făcut"
@@ -41176,6 +41536,18 @@ msgstr "Filtrați după grup"
msgid "Todos|Filter by project"
msgstr "Filtrați după proiect"
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr "În acest fel știți totdeauna la ce să lucrați în continuare."
@@ -41185,6 +41557,9 @@ msgstr "Marcați totul ca terminat"
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr "Nu aveți nimic pe lista lucrurilor de-făcut. Bine lucrat!"
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr "Anulați marcarea tuturor ca fiind făcute"
@@ -41293,9 +41668,18 @@ msgstr "Prea multe referințe. Acțiunile rapide sunt limitate la cel mult %{max
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr "S-au găsit prea mulți utilizatori. Acțiunile rapide sunt limitate la cel mult %{max_count} utilizatori"
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr "ÃŽnapoi"
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr "Îmbinarea subiectului %{source_topic} în subiectul %{target_topic} a reușit."
@@ -41320,9 +41704,21 @@ msgstr "Titlul subiectului"
msgid "Topic was successfully updated."
msgstr "Subiectul a fost actualizat cu succes."
+msgid "TopicSelect|No matching results"
+msgstr "Nicio potrivire de rezultate"
+
+msgid "TopicSelect|Search topics"
+msgstr "Căutare subiecte"
+
+msgid "TopicSelect|Select a topic"
+msgstr "Selectați un subiect"
+
msgid "Topics"
msgstr "Subiecte"
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr "Total"
@@ -41491,39 +41887,12 @@ msgstr "Perioada dvs. de încercare se încheie la %{boldStart}%{trialEndDate}%{
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr "Caractere permise: +, 0-9, - și spații."
-msgid "Trial|Company name"
-msgstr "Numele companiei"
-
msgid "Trial|Continue"
msgstr "Continuați"
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr "Continuați să folosiți gratuit funcțiile de bază ale GitLab."
-
-msgid "Trial|Country"
-msgstr "Èšara"
-
msgid "Trial|Dismiss"
msgstr "Respingeți"
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr "Versiunea de încercare GitLab Ultimate (opțional)"
-
-msgid "Trial|Number of employees"
-msgstr "Număr de angajați"
-
-msgid "Trial|Please select a country"
-msgstr "Vă rugăm să selectați o țară"
-
-msgid "Trial|Telephone number"
-msgstr "Număr de telefon"
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr "Faceți upgrade la Ultimate pentru a continua să utilizați GitLab cu funcții avansate."
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr "După finalizarea acestei etape, vă vom activa versiunea de încercare pentru grupul dumneavoastră. După 30 de zile, puteți:"
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr "Versiunea dvs. de încercare GitLab Ultimate durează 30 de zile, dar vă puteți păstra contul gratuit GitLab pentru totdeauna. Avem nevoie doar de câteva informații suplimentare pentru a vă activa versiunea de încercare."
@@ -41629,21 +41998,18 @@ msgstr "Încercați să comunicați cu dispozitivul dumneavoastră. Conectați-l
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr "Încercați să comunicați cu dispozitivul dvs. Conectați-l ( dacă nu ați făcut-o deja) și apăsați butonul de pe dispozitiv acum."
+msgid "Tue"
+msgstr "Ma"
+
msgid "Tuesday"
msgstr "Marți"
msgid "Turn off"
msgstr "Opriți"
-msgid "Turn off notifications"
-msgstr "Dezactivați notificările"
-
msgid "Turn on"
msgstr "Porniți"
-msgid "Turn on notifications"
-msgstr "Activați notificările"
-
msgid "Twitter"
msgstr "Twitter"
@@ -42223,6 +42589,9 @@ 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}"
+
msgid "UsageQuota|Code packages and container images."
msgstr "Pachete de cod și imagini de containere."
@@ -42250,6 +42619,9 @@ msgstr "Registrul de container Docker integrat în Gitlab pentru stocarea imagin
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr "Registrul de container Docker integrat în Gitlab pentru stocarea imaginilor Docker. %{linkStart}Mai multe informații%{linkEnd}"
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr "Setări de grup &gt; Cote de utilizare"
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr "Include artefacte, depozite, wiki, încărcări și alte elemente."
@@ -42346,11 +42718,11 @@ msgstr "Grupul %{strong_start}%{context_name}%{strong_end} va fi afectat de aces
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr "Proiectul %{strong_start}%{context_name}%{strong_end} va fi afectat de acest lucru. "
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
-msgstr "Spațiul de nume utilizează în prezent %{strong_start}%{used_storage}%{strong_end} de stocare a spațiului de nume. Proprietarii grupurilor pot vizualiza utilizarea spațiului de stocare al spațiului de nume și pot achiziționa mai mult accesând %{strong_start}Setări grup &gt; Cotele de utilizare%{strong_end}. %{docs_link_start}Aflați mai multe%{link_end}."
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
+msgstr "Spațiul de nume utilizează în prezent %{strong_start}%{used_storage}%{strong_end} de spațiu de stocare al spațiului de nume. Proprietarii de grupuri pot vizualiza utilizarea spațiului de stocare al spațiului de nume și pot achiziționa mai mult din %{strong_start}%{usage_quotas_nav_instruction}%{strong_end} %{docs_link_start}Aflați mai multe%{link_end}."
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}User settings &gt; Usage quotas%{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 stocare a spațiului de nume. Vizualizați și gestionați consumul prin %{strong_start}Setări utilizator &gt; Cotele de consum%{strong_end}. %{docs_link_start}Aflați mai multe%{link_end} despre cum să vă reduceți spațiul de stocare."
+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ă"
@@ -42415,8 +42787,8 @@ msgstr "Utilizarea resurselor în cadrul proiectelor dvs."
msgid "UsageQuota|Usage quotas help link"
msgstr "Link de ajutor pentru cotele de utilizare"
-msgid "UsageQuota|Usage since %{usageSince}"
-msgstr "Utilizare de la %{usageSince}"
+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}."
@@ -42577,6 +42949,9 @@ msgstr "Utilizați numărul de probleme"
msgid "Use issue weight"
msgstr "Utilizați greutate de problemă"
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr "Folosește problemele pentru a colabora la idei, a rezolva probleme și a planifica munca"
+
msgid "Use one line per URI"
msgstr "Folosiți o linie pentru fiecare URI"
@@ -42979,8 +43354,8 @@ msgstr "Limita ratei API a utilizatorilor"
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr "Utilizatorii pot lansa un mediu de dezvoltare dintr-o filă de browser GitLab atunci când este activată integrarea %{linkStart}Gitpod%{linkEnd}"
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
-msgstr "Utilizatorii își pot reactiva conturile conectându-se. %{link_start}Aflați mai multe%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
+msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
msgstr "Utilizatorii pot reda diagrame în documente AsciiDoc, Markdown, reStructuredText, și Textile utilizând Kroki."
@@ -43273,11 +43648,14 @@ msgstr "Vizualizați detaliile alertei."
msgid "View all environments."
msgstr "Vizualizați toate mediile."
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr "Vizualizați toate problemele"
-msgid "View all personal projects"
-msgstr "Vedeți toate proiectele personale"
+msgid "View all projects"
+msgstr ""
msgid "View blame"
msgstr "Vizualizați blame"
@@ -43975,6 +44353,12 @@ msgstr "Mergeți la fork"
msgid "WebIDE|Merge request"
msgstr "Merge request"
+msgid "WebIDE|Quickly and easily edit multiple files in your project."
+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|This project does not accept unsigned commits."
msgstr "Acest proiect nu acceptă commit-uri nesemnate."
@@ -44056,6 +44440,9 @@ msgstr "Se creează sau se actualizează o versiune."
msgid "Webhooks|A subgroup is created or removed."
msgstr "Se creează sau se înlătură un subgrup."
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr "Un webhook din acest proiect, după ce a fost încercat de mai multe ori, a fost dezactivat automat."
+
msgid "Webhooks|A wiki page is created or updated."
msgstr "Se creează sau se actualizează o pagină wiki."
@@ -44098,6 +44485,9 @@ msgstr "Nu se conectează"
msgid "Webhooks|Feature flag events"
msgstr "Evenimentele Feature Flag"
+msgid "Webhooks|Go to webhooks"
+msgstr "Mergeți la webhook-uri"
+
msgid "Webhooks|Issues events"
msgstr "Evenimente de probleme"
@@ -44155,6 +44545,9 @@ msgstr "URL-ul trebuie să fie codificat-procentual atunci când conține unul s
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr "Utilizat pentru a valida payload-urile primite. Trimis împreună cu cererea în antetul %{code_start}X-Gitlab-Token HTTP%{code_end}."
+msgid "Webhooks|Webhook disabled"
+msgstr "Webhook dezactivat"
+
msgid "Webhooks|Webhook failed to connect"
msgstr "Webhook-ul nu a reușit să se conecteze"
@@ -44173,6 +44566,9 @@ msgstr "Site web"
msgid "Website:"
msgstr "Site web:"
+msgid "Wed"
+msgstr "Mi"
+
msgid "Wednesday"
msgstr "Miercuri"
@@ -44224,6 +44620,9 @@ msgstr "Ce anume influențează setarea?"
msgid "What does this command do?"
msgstr "Ce face această comandă?"
+msgid "What is GitLab Runner?"
+msgstr "Ce este GitLab Runner?"
+
msgid "What is Markdown?"
msgstr "Ce este Markdown?"
@@ -44437,9 +44836,6 @@ msgstr "Ștergeți pagina %{pageTitle}?"
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr "Cineva a editat pagina în același timp cu dumneavoastră. Vă rugăm să verificați %{wikiLinkStart}pagina%{wikiLinkEnd} și să vă asigurați că modificările dvs. nu le vor șterge involuntar pe ale lor."
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr "A apărut o eroare în timp ce se încerca redarea editorului de conținut. Vă rugăm să încercați din nou mai târziu."
-
msgid "WikiPage|Cancel"
msgstr "Anulați"
@@ -44464,9 +44860,6 @@ msgstr "Aflați mai multe."
msgid "WikiPage|Page title"
msgstr "Titlul paginii"
-msgid "WikiPage|Retry"
-msgstr "Reîncercați"
-
msgid "WikiPage|Save changes"
msgstr "Salvați modificările"
@@ -44554,11 +44947,11 @@ msgstr "Lucrări în curs (deschise și neatribuite)"
msgid "Work in progress Limit"
msgstr "Limita de lucrări în curs"
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
-msgstr "O sarcină vă oferă posibilitatea de a împărți munca în bucăți mai mici legate de o problemă. Sarcinile sunt primele elemente care utilizează noile noastre obiecte %{workItemsLink}. Alte tipuri de elemente de lucru vor fi disponibile în curând."
+msgid "WorkItem|%{workItemType} deleted"
+msgstr "%{workItemType}șters"
-msgid "WorkItem|Add a task"
-msgstr "Adăugați o sarcină"
+msgid "WorkItem|Add"
+msgstr "Adăugați"
msgid "WorkItem|Add a title"
msgstr "Adăugați un titlu"
@@ -44575,8 +44968,8 @@ msgstr "Adăugați sarcina"
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr "Sunteți sigur că doriți să anulați editarea?"
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
-msgstr "Sunteți sigur că doriți să ștergeți sarcina? Această acțiune nu poate fi anulată."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
+msgstr "Sunteți sigur că doriți să ștergeți acest %{workItemType}? Această acțiune este ireversibilă."
msgid "WorkItem|Assignee"
msgid_plural "WorkItem|Assignees"
@@ -44587,17 +44980,14 @@ msgstr[2] "Responsabili"
msgid "WorkItem|Cancel"
msgstr "Anulare"
-msgid "WorkItem|Child items"
-msgstr "Elemente copil"
-
msgid "WorkItem|Child removed"
msgstr "Copilul a fost înlăturat"
msgid "WorkItem|Closed"
msgstr "ÃŽnchis"
-msgid "WorkItem|Collapse child items"
-msgstr "Restrângeți elementele copil"
+msgid "WorkItem|Collapse tasks"
+msgstr "Restrângeți sarcinile"
msgid "WorkItem|Create task"
msgstr "Creați o sarcină"
@@ -44605,20 +44995,32 @@ msgstr "Creați o sarcină"
msgid "WorkItem|Create work item"
msgstr "Creați elementul de lucru"
-msgid "WorkItem|Delete task"
-msgstr "Ștergeți sarcina"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr "Ștergeți %{workItemType}"
+
+msgid "WorkItem|Expand tasks"
+msgstr "Extindeți sarcinile"
-msgid "WorkItem|Expand child items"
-msgstr "Extindeți elementele copil"
+msgid "WorkItem|Incident"
+msgstr "Incident"
msgid "WorkItem|Introducing tasks"
msgstr "Introducerea sarcinilor"
-msgid "WorkItem|Learn about tasks"
-msgstr "Aflați mai multe despre sarcini"
+msgid "WorkItem|Issue"
+msgstr "Problemă"
+
+msgid "WorkItem|Learn about tasks."
+msgstr "Învățați despre sarcini."
+
+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|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
-msgstr "În prezent nu este atribuit niciun element copil. Utilizați elementele copil pentru a acorda prioritate sarcinilor pe care echipa dvs. trebuie să le completeze, pentru a vă îndeplini obiectivele!"
+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ă."
msgid "WorkItem|Open"
msgstr "Deschis"
@@ -44626,20 +45028,23 @@ msgstr "Deschis"
msgid "WorkItem|Remove"
msgstr "Înlăturați"
+msgid "WorkItem|Requirements"
+msgstr "Cerințe"
+
msgid "WorkItem|Select type"
msgstr "Selectați tipul"
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
-msgstr "Ceva nu a mers bine la crearea unei sarcini. Vă rugăm să încercați din nou"
+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."
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
-msgstr "Ceva nu a mers bine la crearea unui element de lucru. Vă rugăm să încercați din nou"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
+msgstr "Ceva nu a mers bine la ștergerea %{workItemType}. Vă rugăm să încercați din nou."
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr "Ceva nu a mers bine la ștergerea sarcinii. Vă rugăm să încercați din nou."
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
-msgstr "Ceva nu a mers bine la ștergerea elementului de lucru. Vă rugăm să încercați din nou."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
+msgstr "Ceva nu a mers bine la preluarea sarcinilor. Vă rugăm să reîmprospătați această pagină."
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
msgstr "Ceva nu a mers bine la preluarea elementului de lucru. Vă rugăm să încercați din nou."
@@ -44653,32 +45058,41 @@ msgstr "Ceva nu a mers bine atunci când s-a încercat adăugarea unui copil. VÄ
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr "Ceva nu a mers bine atunci când ați încercat să creați un copil. Încercați din nou."
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr "Ceva nu a mers bine în timp ce se actualiza %{workItemType}. Vă rugăm să încercați din nou."
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr "Ceva nu a mers bine în timp ce se actualiza elementul de lucru. Vă rugăm să încercați din nou."
+msgid "WorkItem|Task"
+msgstr "Sarcină"
+
msgid "WorkItem|Task deleted"
msgstr "Sarcină ștearsă"
+msgid "WorkItem|Tasks"
+msgstr "Sarcini"
+
+msgid "WorkItem|Test case"
+msgstr "Caz de testare"
+
msgid "WorkItem|Turn off confidentiality"
msgstr "Opriți confidențialitatea"
msgid "WorkItem|Turn on confidentiality"
msgstr "Porniți confidențialitatea"
-msgid "WorkItem|Type"
-msgstr "Tipul"
-
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|Work Items"
msgstr "Elemente de lucru"
-msgid "WorkItem|Work item deleted"
-msgstr "Element de lucru șters"
-
-msgid "WorkItem|work items"
-msgstr "elemente de lucru"
+msgid "WorkItem|Work item"
+msgstr "Element de lucru"
msgid "Would you like to create a new branch?"
msgstr "Doriți să creați o nouă ramură?"
@@ -44902,6 +45316,12 @@ 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}."
@@ -44962,12 +45382,6 @@ msgstr "Puteți începe prin a clona repozitoriul sau puteți începe să adăug
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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] "Puteți avea cel mult %{free_user_limit} membru unic în toate proiectele personale."
-msgstr[1] "Puteți avea maximum %{free_user_limit} membri unici în toate proiectele personale."
-msgstr[2] "Puteți avea maximum %{free_user_limit} de membri unici în toate proiectele personale."
-
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."
@@ -45073,12 +45487,6 @@ 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 currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] "Aveți în prezent mai mult de %{free_user_limit} membru în toate proiectele personale. Începând cu 19 octombrie 2022, cel mai recent %{free_user_limit} membru activ va rămâne activ, iar ceilalți membri vor avea statutul %{link_start}Peste limită%{link_end} și-și vor pierde accesul."
-msgstr[1] "Aveți în prezent mai mult de %{free_user_limit} membri în toate proiectele personale. Începând cu 19 octombrie 2022, cei mai recent activi %{free_user_limit} membri vor rămâne activi, iar ceilalți membri vor avea statutul %{link_start}Peste limită%{link_end} și-și vor pierde accesul."
-msgstr[2] "Aveți în prezent mai mult de %{free_user_limit} de membri în toate proiectele personale. Începând cu 19 octombrie 2022, cei mai recent activi %{free_user_limit} membri vor rămâne activi, iar ceilalți membri vor avea statutul %{link_start}Peste limită%{link_end} și-și vor pierde accesul."
-
msgid "You do not have any subscriptions yet"
msgstr "Nu aveți încă niciun abonament"
@@ -45244,8 +45652,8 @@ 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."
-msgstr "Ați achiziționat cu succes un abonament la planul %{plan} pentru %{seats}. Veți primi o chitanță prin e-mail."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
+msgstr "Ați achiziționat cu succes un abonament %{plan} pentru %{seats}. Factura o veți primi prin e-mail. S-ar putea să dureze un moment până când GitLab.com va reflecta complet achiziția dvs."
msgid "You have unsaved changes"
msgstr "Aveți modificări nesalvate"
@@ -45307,6 +45715,9 @@ msgstr "Trebuie să încărcați o arhivă de export a proiectului GitLab (care
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr "Trebuie să vă verificați mai întâi e-mailul principal înainte de a activa Autentificarea cu doi factori."
+msgid "You see projects here when you're added to a group or project."
+msgstr "Proiectele apar aici atunci când sunteți adăugat la un grup sau la un proiect."
+
msgid "You successfully declined the invitation"
msgstr "Ați refuzat cu succes invitația"
@@ -45418,9 +45829,6 @@ msgstr "Vizualizați membrii grupului %{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 "Ați activat deja autentificarea cu doi factori folosind autentificatoare cu o parolă unică. Pentru a înregistra un alt dispozitiv, trebuie mai întâi să dezactivați autentificarea cu doi factori."
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr "Ați atins limita de %{free_limit} (de) membri în toate proiectele personale."
-
msgid "You've rejected %{user}"
msgstr "Ați respins %{user}"
@@ -45460,6 +45868,12 @@ msgstr "Exportul dvs. CSV de %{written_count} din proiectul %{project_name} (%{p
msgid "Your CSV import for project"
msgstr "Importul dvs. CSV pentru proiect"
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr "Exportul raportului CSV al lanțului de custodie pentru grupul %{group_link} a fost adăugat la acest e-mail ca atașament."
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr "Exportul raportului CSV al lanțului de custodie pentru grupul %{group_name} a fost adăugat la acest e-mail ca atașament."
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
@@ -45610,9 +46024,6 @@ msgstr "Dispozitivul dvs. a fost configurat cu succes! Dați-i un nume și înre
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr "Fișierul dvs. trebuie să conțină o coloană numită %{codeStart}titlu%{codeEnd}. O coloană de %{codeStart}descriere%{codeEnd} este opțională. Mărimea maximă permisă a fișierului este de 10 MB."
-msgid "Your first project"
-msgstr "Primul dvs. proiect"
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] "Grupul dvs. gratuit este acum limitat la %d membru"
@@ -45676,6 +46087,9 @@ msgstr "Noul dvs. token de acces a fost creat."
msgid "Your new comment"
msgstr "Noul dvs. comentariu"
+msgid "Your password"
+msgstr "Parola dvs."
+
msgid "Your password reset token has expired."
msgstr "Tokenul dvs. de resetare a parolei a expirat."
@@ -45691,8 +46105,8 @@ msgstr "Adresa dvs. de e-mail principală este folosită pentru înregistrarea a
msgid "Your profile"
msgstr "Profilul d-voastră"
-msgid "Your project has limited quotas and features"
-msgstr "Proiectul dvs. are cote și caracteristici limitate"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
+msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
msgstr "Limita proiectelor dvs. este de %{limit} (de) proiecte! Vă rugăm să contactați administratorul dvs. pentru a o mări"
@@ -45901,9 +46315,6 @@ msgstr "artefacte"
msgid "assign yourself"
msgstr "atribuiți-vă singur"
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr "părintele asociat este confidențial și nu poate avea copii neconfidențiali."
-
msgid "at"
msgstr "la"
@@ -46000,6 +46411,9 @@ msgstr "nu se poate modifica dacă un proiect personal are etichete de registru
msgid "cannot be changed if shared runners are enabled"
msgstr "nu se poate modifica dacă executorii partajați sunt activați"
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr "nu se poate activa"
@@ -46042,6 +46456,9 @@ msgstr[0] "modificare"
msgstr[1] "modificări"
msgstr[2] "de modificări"
+msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
+msgstr "%{criticalStart}critice%{criticalEnd}, %{highStart}ridicate%{highEnd} și %{otherStart}altele%{otherEnd}"
+
msgid "ciReport|%{danger_start}%{degradedNum} degraded%{danger_end}, %{same_start}%{sameNum} same%{same_end}, and %{success_start}%{improvedNum} improved%{success_end}"
msgstr "%{danger_start}%{degradedNum} degradată(e)%{danger_end}, %{same_start}%{sameNum} la fel%{same_end} și %{success_start}%{improvedNum} îmbunătățită(e)%{success_end}"
@@ -46090,8 +46507,8 @@ msgstr "%{reportType}: Încărcarea a avut ca rezultat o eroare"
msgid "ciReport|%{sameNum} same"
msgstr "%{sameNum} la fel"
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
-msgstr "%{scanner} a detectat altă/alte %{boldStart}%{number}%{boldEnd} (de) nouă/noi potențială/potențiale %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
+msgstr "%{scanner} a detectat %{number} (de) nouă / noi potențială(e) %{vulnStr}"
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}"
@@ -46294,7 +46711,7 @@ msgstr "Gestionați licențele"
msgid "ciReport|Manage licenses"
msgstr "Gestionați licențele"
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr "Adăugat manual"
msgid "ciReport|New"
@@ -46420,9 +46837,6 @@ msgstr "comis"
msgid "compliance violation has already been recorded"
msgstr "încălcarea conformității a fost deja înregistrată"
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr "părintele confidențial nu poate fi utilizat în cazul în care există copii neconfidențiali."
-
msgid "contacts can only be added to root groups"
msgstr "contactele pot fi adăugate numai la grupurile rădăcină"
@@ -46459,6 +46873,9 @@ msgstr "creat(ă) %{timeAgo} de %{author}"
msgid "created by"
msgstr "creat(ă) de"
+msgid "daily"
+msgstr "zilnic"
+
msgid "data"
msgstr "date"
@@ -46585,6 +47002,9 @@ msgstr "nu a putut fi respinsă detectarea asociată (id=%{finding_id}): %{messa
msgid "failed to dismiss finding: %{message}"
msgstr "nu a putut fi respinsă detectarea: %{message}"
+msgid "failed to dismiss security finding: %{message}"
+msgstr "respingerea constatării de securitate a eșuat: %{message}"
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr "nu a reușit să revină descoperirea asociată (id=%{finding_id}) la cea detectată"
@@ -46798,6 +47218,9 @@ msgstr "este numai în citire"
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr "este prea lung (%{current_value}). Valoarea maximă este de %{max_size}."
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr "este prea lung(ă) (%{size}). Mărimea maximă este %{max_size}."
+
msgid "is too long (maximum is %{count} characters)"
msgstr "este prea lung(ă) (maximum este %{count} de caractere)"
@@ -46900,8 +47323,8 @@ msgstr[0] "merge request"
msgstr[1] "merge request-uri"
msgstr[2] "de merge request-uri"
-msgid "mergedCommitsAdded|(commits were squashed)"
-msgstr "(squashing-ul de commit-uri s-a efectuat)"
+msgid "mergedCommitsAdded| (commits were squashed)"
+msgstr "(squashing-ul commit-urilor s-a efectuat)"
msgid "metric_id must be unique across a project"
msgstr "metric_id trebuie să fie unic în cadrul unui proiect"
@@ -46921,8 +47344,8 @@ msgstr "%{commitCount} și %{mergeCommitCount} vor fi adăugate la %{targetBranc
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr "%{commitCount} se va / vor adăuga la %{targetBranch}."
-msgid "mrWidgetCommitsAdded|1 merge commit"
-msgstr "1 commit de îmbinare"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
+msgstr "%{strongStart}1%{strongEnd} commit de îmbinare"
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
msgstr "Modificări îmbinate în %{targetBranch} cu %{mergeCommitSha}%{squashedCommits}."
@@ -47086,6 +47509,9 @@ msgstr[0] "Problemă menționată"
msgstr[1] "Probleme menționate"
msgstr[2] "Probleme menționate"
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr "Fuziune blocată: în titlu sau în descriere trebuie să se menționeze cheia unei probleme Jira."
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr "Îmbinare blocată: toate aprobările necesare trebuie să fie acordate."
@@ -47221,9 +47647,6 @@ msgstr "Pentru a modifica acest mesaj implicit, editați șablonul pentru mesaje
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr "Pentru a schimba acest mesaj implicit, editați șablonul mesajelor commit-urilor squash. %{linkStart}Aflați mai multe.%{linkEnd}"
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr "Pentru îmbinare, o cheie de problemă Jira trebuie să fie menționată în titlu sau în descriere."
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr "Utilizatorii care pot scrie în ramurile sursă sau țintă pot rezolva conflictele."
@@ -47791,6 +48214,9 @@ msgstr "respins"
msgid "was scheduled to merge after pipeline succeeds by"
msgstr "a fost programată pentru îmbinare după ce pipeline-ul reușește de"
+msgid "weekly"
+msgstr "săptămânal"
+
msgid "wiki page"
msgstr "pagina wiki"
diff --git a/locale/ru/gitlab.po b/locale/ru/gitlab.po
index 9c4b2b468a7..cc42983a61c 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-08-12 18:03\n"
+"PO-Revision-Date: 2022-09-11 06:21\n"
msgid " %{start} to %{end}"
msgstr " %{start} по %{end}"
@@ -22,6 +22,13 @@ msgstr " %{start} по %{end}"
msgid " (from %{timeoutSource})"
msgstr " (из %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid " Collected %{time}"
msgstr " Получено %{time}"
@@ -171,6 +178,13 @@ msgstr[1] "%d утверждающих (вы утвердили)"
msgstr[2] "%d утверждающих (вы утвердили)"
msgstr[3] "%d утверждающих (вы утвердили)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -269,6 +283,13 @@ msgstr[1] "%d вклада"
msgstr[2] "%d вкладов"
msgstr[3] "%d вкладов"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d день"
@@ -479,6 +500,13 @@ msgstr[1] "%d проекта выбрано"
msgstr[2] "%d проектов выбрано"
msgstr[3] "%d проектов выбрано"
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -528,13 +556,6 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] "%d Ð½ÐµÑ€ÐµÑˆÑ‘Ð½Ð½Ð°Ñ Ñ‚ÐµÐ¼Ð°"
@@ -610,9 +631,6 @@ msgstr "%{actionText} и переоткрыть %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr "%{address} — недопуÑтимый диапазон IP-адреÑов"
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr "%{author_link} клонировал %{original_issue} в %{new_issue}."
@@ -1061,12 +1079,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1183,6 +1195,9 @@ msgstr "%{size} байт"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr "%{sourceBranch} в %{targetBranch}"
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr "%{spammable_titlecase} было уÑпешно отправлено в Akismet."
@@ -1205,6 +1220,13 @@ msgstr[3] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1246,9 +1268,6 @@ msgstr[3] "%{strong_start}%{count} учаÑтников%{strong_end} должнÑ
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] "%{strong_start}%{release_count}%{strong_end} Релиз"
@@ -1462,13 +1481,6 @@ msgstr "(удалено)"
msgid "(revoked)"
msgstr "(отозван)"
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] "(объединÑет иÑторию %d коммита)"
-msgstr[1] "(объединÑет иÑторию %d коммитов)"
-msgstr[2] "(объединÑет иÑторию %d коммитов)"
-msgstr[3] "(объединÑет иÑторию %d коммитов)"
-
msgid "(this user)"
msgstr "(Ñтот пользователь)"
@@ -1856,6 +1868,12 @@ msgstr "Базовый шаблон Ð´Ð»Ñ Ñ€Ð°Ð·Ñ€Ð°Ð±Ð¾Ñ‚ÐºÐ¸ программ
msgid "A complete DevOps platform"
msgstr "КомплекÑÐ½Ð°Ñ DevOps-платформа"
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr "Ð”Ð»Ñ Ð¿ÑƒÑтого проекта Ð½ÐµÐ»ÑŒÐ·Ñ Ð²Ñ‹Ð±Ñ€Ð°Ñ‚ÑŒ ветку по умолчанию."
@@ -1919,6 +1937,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr "ÐÐµÐºÐ¾Ð½Ñ„Ð¸Ð´ÐµÐ½Ñ†Ð¸Ð°Ð»ÑŒÐ½Ð°Ñ Ñ†ÐµÐ»ÑŒ не может иметь конфиденциальную цель в качеÑтве родителÑ"
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -2087,6 +2111,9 @@ msgstr "folder/openapi.json"
msgid "AWS Access Key"
msgstr "Ключ доÑтупа AWS"
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr "Секретный ключ доÑтупа AWS"
@@ -2402,7 +2429,7 @@ msgstr "Добавьте комментарий к Ñтой Ñтроке или
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2429,6 +2456,9 @@ msgstr ""
msgid "Add a table"
msgstr "Добавить таблицу"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2468,6 +2498,9 @@ msgstr "Добавить комментарий"
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2519,6 +2552,12 @@ msgstr "Добавить ключ"
msgid "Add label(s)"
msgstr "Добавить метку(и)"
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr "Добавить ÑпиÑок"
@@ -2690,6 +2729,9 @@ msgstr "ДобавлÑетÑÑ %{labels}%{label_text}."
msgid "Adds a Zoom meeting."
msgstr "ДобавлÑет вÑтречу в Zoom."
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr "ДобавлÑет задачу."
@@ -2705,9 +2747,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr "ÐаÑтройте чаÑтоту обновлений GitLab UI."
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr "ÐаÑтройте критерии фильтров и поиÑка выше. ЕÑли вы Ñчитаете, что здеÑÑŒ может быть ошибка, обратитеÑÑŒ к документации %{linkStart}по уÑтранению неполадок Geo%{linkEnd} Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ð¹ информации."
-
msgid "Admin"
msgstr "ÐдминиÑтратор"
@@ -2906,15 +2945,15 @@ msgstr "Ð’Ñе новые проекты могут иÑпользовать оÐ
msgid "AdminSettings|Auto DevOps domain"
msgstr "Домен Auto DevOps"
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr "ÐаÑтроить Let's Encrypt"
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2954,7 +2993,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2978,6 +3017,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr "Токен канала"
@@ -3143,7 +3185,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3368,6 +3413,9 @@ msgstr "ОбÑуждениÑ, Ñозданные Ñтим пользовател
msgid "AdminUsers|It's you!"
msgstr "Это вы!"
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr "Узнать больше о %{link_start}забаненных пользователÑÑ….%{link_end}"
@@ -4142,9 +4190,6 @@ msgstr "Разрешить пользователÑм региÑтрироват
msgid "Allowed"
msgstr "Разрешено"
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr "ДопуÑтимые Ñимволы: +, 0-9, -, и пробелы."
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr "Разрешено ограничение доменов Ñлектронной почты только Ð´Ð»Ñ Ð³Ñ€ÑƒÐ¿Ð¿ верхнего уровнÑ"
@@ -4229,6 +4274,9 @@ msgstr "Приложение под названием %{link_to_client} запÑ
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr "ПуÑтое значение в поле Пользователь Gitlab добавит полное Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ FogBugz (напр., \"От Ивана Кузнецова\") в опиÑание вÑех обÑуждений и комментариев, а также ÑвÑжет и/или назначит Ñти обÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð¸ комментарии Ñ Ñоздателем проекта."
@@ -4280,6 +4328,9 @@ msgstr "Произошла ошибка при добавлении утверж
msgid "An error occurred while adding formatted title for epic"
msgstr "Произошла ошибка при Ñоздании форматированного заголовка Ð´Ð»Ñ Ñ†ÐµÐ»Ð¸."
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr "Произошла ошибка при авторизации вашей роли"
@@ -4418,6 +4469,9 @@ msgstr "Произошла ошибка при загрузке раздела Ñ
msgid "An error occurred while loading all the files."
msgstr "Произошла ошибка при загрузке вÑех файлов."
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr "Произошла ошибка при загрузке данных диаграммы"
@@ -4557,9 +4611,6 @@ msgstr[3] "Произошла ошибка при Ñохранении параÐ
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr "При подпиÑке на ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð¾ÑˆÐ»Ð° ошибка."
-
msgid "An error occurred while triggering the job."
msgstr "Произошла ошибка при запуÑке заданиÑ."
@@ -4569,15 +4620,15 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr "Произошла ошибка при попытке Ñоздать отчёт. ПожалуйÑта, повторите попытку позже."
+msgid "An error occurred while trying to render the content editor. Please try again."
+msgstr ""
+
msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr "Произошла ошибка при попытке запуÑтить новую Ñборочную линию Ð´Ð»Ñ Ñтого запроÑа на ÑлиÑние."
msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
-msgstr "При отпиÑке от уведомлений произошла ошибка."
-
msgid "An error occurred while updating approvers"
msgstr "Произошла ошибка при обновлении утверждающих"
@@ -5134,6 +5185,9 @@ msgstr "Одобрить"
msgid "Approve a merge request"
msgstr "Утвердить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr "Утвердить текущий Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние."
@@ -5605,6 +5659,13 @@ msgstr "Этот меÑÑц"
msgid "AuditLogs|User Events"
msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5620,10 +5681,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5641,7 +5708,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5662,6 +5729,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5671,9 +5741,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6472,12 +6539,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6493,12 +6554,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6550,9 +6605,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr "Приглашение в группу"
@@ -6596,9 +6648,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr "Ð’Ñ‹ ÑобираетеÑÑŒ удалить %{username} из вашей подпиÑки. ЕÑли вы продолжите, пользователь будет удалён из группы %{namespace} и вÑех её подгрупп и проектов. ДейÑтвие не может быть отменено."
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6636,6 +6685,9 @@ msgstr "Заблокированное обÑуждение"
msgid "Blocking"
msgstr "Блокировка"
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6848,6 +6900,15 @@ msgstr "Развернуть"
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6974,6 +7035,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "Ветки"
@@ -7139,6 +7206,9 @@ msgstr "ПроÑмотр файлов"
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr "Произошла ошибка при получении артефактов"
@@ -7395,21 +7465,12 @@ msgstr "СтатиÑтика релизов"
msgid "CICDAnalytics|Releases"
msgstr "Релизы"
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7419,9 +7480,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8679,6 +8737,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8706,12 +8770,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8766,6 +8842,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9454,10 +9533,10 @@ msgstr "SSL-Ñертификат Kubernetes, иÑпользуемый Ð´Ð»Ñ Ð°Ñ
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr "URL, иÑпользуемый Ð´Ð»Ñ Ð´Ð¾Ñтупа к API Kubernetes."
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9628,6 +9707,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9694,6 +9776,9 @@ msgstr "Комментировать & открыть тему"
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9818,6 +9903,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr "Сравнить верÑии"
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr "Сравнить изменениÑ"
@@ -10399,6 +10487,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr "Ошибка Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Docker"
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10505,6 +10596,12 @@ msgstr "ЗапуÑтить очиÑтку:"
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10679,7 +10776,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -11282,9 +11379,6 @@ msgstr "Создать группу"
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr "Создать/импортировать Ñвой первый проект"
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr "У Ð²Ð°Ñ Ð½ÐµÑ‚ прав на Ñоздание подгруппы в Ñтой группе."
@@ -11828,6 +11922,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -12060,6 +12157,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -12138,15 +12241,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr "Редактировать профиль"
@@ -12159,6 +12253,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -12234,6 +12331,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -12261,16 +12361,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -12291,16 +12388,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12369,13 +12463,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12565,8 +12659,8 @@ msgstr "Дней"
msgid "Days to merge"
msgstr "Дней до ÑлиÑниÑ"
-msgid "Deactivate dormant users after 90 days of inactivity"
-msgstr "Деактивировать бездейÑтвующих пользователей поÑле 90 дней бездейÑтвиÑ"
+msgid "Deactivate dormant users after a period of inactivity"
+msgstr ""
msgid "Dear Administrator,"
msgstr "Уважаемый админиÑтратор,"
@@ -12751,6 +12845,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr "Удалить файл"
@@ -12802,6 +12899,9 @@ msgstr "Удалить Ñниппет?"
msgid "Delete source branch"
msgstr "Удалить иÑходную ветку"
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr "Удалить подпиÑку"
@@ -13372,6 +13472,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13605,6 +13711,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13698,6 +13807,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -14266,6 +14378,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -14287,6 +14402,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -14302,6 +14423,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14524,6 +14651,9 @@ msgstr "Изменить wiki Ñтраницу"
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr "Редактировать Ñвой поÑледний комментарий в теме (в пуÑтом текÑтовом поле)"
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14539,9 +14669,6 @@ msgstr "Редактирование"
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14566,9 +14693,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr "ПуÑто. Выберите проÑтранÑтва имен Ð´Ð»Ñ Ð¸Ð½Ð´ÐµÐºÑации."
@@ -14716,6 +14840,9 @@ msgstr "Включить Auto DevOps"
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr "Включить Gitpod"
@@ -14800,9 +14927,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -15244,6 +15368,9 @@ msgstr "Цель"
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr "Цель не может быть найдена."
@@ -15280,12 +15407,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr "Произошла ошибка при Ñохранении %{epicDateType} даты"
-
-msgid "Epics|An error occurred while updating labels."
-msgstr "Произошла ошибка при обновлении меток."
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr "Ð’Ñ‹ дейÑтвительно хотите удалить %{bStart}%{targetIssueTitle}%{bEnd} из %{bStart}%{parentEpicTitle}%{bEnd}?"
@@ -15352,18 +15473,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr "до"
-
-msgid "Epics|start"
-msgstr "начать"
-
msgid "Erased"
msgstr ""
@@ -15400,6 +15512,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr "Ошибка при удалении проекта. Проверьте журналы Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð½Ð¾Ñтей об ошибке."
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15433,9 +15548,6 @@ msgstr "Ошибка при загрузке веток."
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr "Ошибка при загрузке данных Ñтран."
-
msgid "Error loading file viewer."
msgstr "Ошибка загрузки программы проÑмотра файлов."
@@ -15511,6 +15623,9 @@ msgstr "Произошла ошибка. Пользователь не был р
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr "Ошибка предпроÑмотра Markdown"
@@ -15944,6 +16059,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16700,9 +16818,6 @@ msgstr "Фев."
msgid "February"
msgstr "Февраль"
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16913,13 +17028,13 @@ msgstr ""
msgid "Flags"
msgstr "Флаги"
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -17069,6 +17184,9 @@ msgstr "Публичный"
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -17139,6 +17257,9 @@ msgstr "ЧаÑтота"
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr "ПÑтница"
@@ -17151,15 +17272,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -17308,9 +17422,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17506,6 +17617,12 @@ msgstr "Ðикогда"
msgid "Geo|Next sync scheduled at"
msgstr "Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ ÑÐ¸Ð½Ñ…Ñ€Ð¾Ð½Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð·Ð°Ð¿Ð»Ð°Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð° на"
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17713,6 +17830,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17875,6 +17995,9 @@ msgstr ""
msgid "Git version"
msgstr "ВерÑÐ¸Ñ Git"
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -18244,6 +18367,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr "Глобальные ÑÐ¾Ñ‡ÐµÑ‚Ð°Ð½Ð¸Ñ ÐºÐ»Ð°Ð²Ð¸Ñˆ"
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr "Глобальные наÑтройки уведомлений"
@@ -18256,6 +18382,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -18274,6 +18406,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -18286,6 +18427,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -18313,6 +18457,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18583,9 +18733,6 @@ msgstr "Gravatar включён"
msgid "Group"
msgstr "Группа"
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -19021,9 +19168,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr "Ð”Ð»Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹ была обновлена ÑÐ±Ð¾Ñ€Ð¾Ñ‡Ð½Ð°Ñ Ð»Ð¸Ð½Ð¸Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑкого DevOps"
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -19051,6 +19195,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr "ПользовательÑкие шаблоны проекта"
@@ -19414,14 +19561,17 @@ msgstr "РуководÑтво"
msgid "HAR (HTTP Archive)"
msgstr "HAR (HTTP Ðрхив)"
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
-msgstr "HTTP Basic: доÑтуп запрещен\\nÐ’Ñ‹ должны иÑпользовать личный токен Ñ Ð´Ð¾Ñтупом к «api» Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Git через HTTP.\\nÐ’Ñ‹ можете Ñоздать его на %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
+msgstr ""
msgid "Harbor Registry"
msgstr ""
@@ -19459,21 +19609,21 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19485,43 +19635,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19530,6 +19674,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19678,6 +19825,9 @@ msgstr[1] "Скрыть графики"
msgstr[2] "Скрыть графики"
msgstr[3] "Скрыть графики"
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19811,9 +19961,6 @@ msgstr "ОчиÑтка уÑпешно запущена"
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19847,6 +19994,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19970,9 +20120,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19985,15 +20141,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -20015,6 +20192,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -20084,9 +20264,6 @@ msgstr "ЕÑли вы иÑпользуете GitHub, вы увидите ÑтаÑ
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr "ЕÑли вы не делали попыток войти, пожалуйÑта, ÑвÑжитеÑÑŒ Ñ Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñтратором или включите двухфакторную аутентификацию (2FA) Ð´Ð»Ñ Ñвоей учётной запиÑи."
@@ -20351,9 +20528,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -21143,6 +21317,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -21161,6 +21338,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -21194,7 +21374,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -21335,9 +21515,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
msgstr "СтатиÑтика (Insights)"
+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)."
msgstr ""
@@ -21609,6 +21795,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21921,6 +22110,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr "УчаÑтники уÑпешно добавлены"
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21936,6 +22128,12 @@ msgstr "Выберите роль"
msgid "InviteMembersModal|Select members or type email addresses"
msgstr "Выберите учаÑтников или введите адреÑа Ñлектронной почты"
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr "Что-то пошло не так"
@@ -21955,9 +22153,6 @@ msgstr "Чтобы назначить обÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð½Ð¾Ð²Ð¾Ð¼Ñƒ учаÑ
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21982,9 +22177,6 @@ msgstr "Ð’Ñ‹ приглашаете учаÑтников в проект %{stron
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr "ПриглаÑить группу"
@@ -22102,12 +22294,18 @@ msgstr "ИÑпользует лицензионное меÑто:"
msgid "Is using seat"
msgstr "ИÑпользует меÑто"
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr "Закрыто"
msgid "IssuableStatus|Closed (%{link})"
msgstr "Закрыто (%{link})"
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22426,9 +22624,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22474,6 +22669,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22489,12 +22687,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22552,9 +22744,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22564,9 +22753,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22579,9 +22765,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22876,9 +23059,6 @@ msgstr "Задание уÑпешно удалено!"
msgid "Job has wrong arguments format."
msgstr "Задание имеет неправильный формат аргументов."
-msgid "Job is missing the `model_type` argument."
-msgstr "Ð’ задании отÑутÑтвует аргумент `model_type`."
-
msgid "Job is stuck. Check runners."
msgstr "Задание завиÑло. Проверьте обработчик заданий."
@@ -23315,8 +23495,8 @@ 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."
-msgstr "Метки могут назначатьÑÑ Ð¾Ð±ÑуждениÑм и запроÑам на ÑлиÑние."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
+msgstr ""
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -23330,6 +23510,9 @@ msgstr "ПеренеÑти Метку"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr "Язык"
@@ -23502,9 +23685,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr "Узнайте о GitLab"
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23556,6 +23736,9 @@ msgstr "Узнать больше о шаблонах проекта группÐ
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -24300,6 +24483,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr "Режим обÑлуживаниÑ"
@@ -24588,10 +24774,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -25079,6 +25262,9 @@ msgstr ""
msgid "Merge requests"
msgstr "ЗапроÑÑ‹ на ÑлиÑние"
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr "ЗапроÑÑ‹ на ÑлиÑние- Ñто меÑто, где можно предлагать Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð²Ð½Ð¾Ñимые в проект, и обÑуждать Ñти Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼Ð¸"
@@ -25166,6 +25352,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -25241,6 +25430,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr "Слито"
@@ -25921,16 +26134,19 @@ msgstr "Изменить опиÑÐ°Ð½Ð¸Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚Ð°"
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr "Понедельник"
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25945,6 +26161,9 @@ msgstr "МеÑÑц"
msgid "Months"
msgstr "МеÑÑцы"
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25987,9 +26206,6 @@ msgstr ""
msgid "Most stars"
msgstr "По рейтингу"
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr "Точка Ð¼Ð¾Ð½Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ %{mounted_as} не найдена в %{model_class}."
-
msgid "Move"
msgstr "ПеремеÑтить"
@@ -26080,12 +26296,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr "Ðайдено неÑколько типов моделей: %{model_types}"
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr "Ðайдено неÑколько загрузчиков: %{uploader_types}"
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26613,6 +26823,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26625,9 +26838,6 @@ msgstr ""
msgid "No deployments found"
msgstr "Ð Ð°Ð·Ð²Ñ‘Ñ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð½Ðµ найдены"
-msgid "No due date"
-msgstr "Дата Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ð½Ðµ указана"
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26778,6 +26988,9 @@ msgstr "Ðет репозиториÑ"
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26799,9 +27012,6 @@ msgstr "Ð”Ð»Ñ Ð´Ð°Ð½Ð½Ð¾Ð¹ ошибки недоÑтуна траÑÑировк
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26992,6 +27202,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr "Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¾ ÑобытиÑÑ…"
@@ -27134,6 +27347,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -27143,6 +27359,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -27158,6 +27392,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -27188,9 +27434,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -27209,6 +27464,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -27315,6 +27576,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr "По плану"
@@ -27534,6 +27798,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27555,6 +27825,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27678,6 +27951,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27835,9 +28111,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr "Открыто"
@@ -28051,6 +28333,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -28087,6 +28372,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -28111,6 +28399,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -28331,6 +28622,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28365,9 +28659,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr "Удалить пакет"
@@ -28377,12 +28668,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28486,8 +28771,8 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr "опубликовано %{author}"
-msgid "Packages & Registries"
-msgstr "Пакеты и рееÑтры"
+msgid "Packages and registries"
+msgstr ""
msgid "Page not found"
msgstr "Страница не найдена"
@@ -28735,9 +29020,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr "ВыполнÑйте обычные дейÑÑ‚Ð²Ð¸Ñ Ð½Ð°Ð´ проектом GitLab"
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr "ÐžÐ¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñти"
@@ -28813,6 +29095,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29518,25 +29806,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr "Дата"
-msgid "Pipeline|Detached merge request pipeline"
-msgstr "Ð¡Ð±Ð¾Ñ€Ð¾Ñ‡Ð½Ð°Ñ Ð»Ð¸Ð½Ð¸Ñ Ð¾Ñ‚ÑоединившегоÑÑ Ð·Ð°Ð¿Ñ€Ð¾Ñа на ÑлиÑние"
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29548,18 +29827,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr "Ð¡Ð±Ð¾Ñ€Ð¾Ñ‡Ð½Ð°Ñ Ð»Ð¸Ð½Ð¸Ñ"
@@ -29617,12 +29890,6 @@ msgstr "Ðазвание тега"
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29653,9 +29920,6 @@ msgstr "Переменные"
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29755,7 +30019,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr "ПожалуйÑта, проверьте почту (%{email}), чтобы подтвердить, что вы владеете Ñтим адреÑом и открыть Ð´Ð»Ñ ÑÐµÐ±Ñ Ñилу CI/CD. Ðе получили пиÑьмо? %{resend_link}. Ðеверный Ð°Ð´Ñ€ÐµÑ Ð¿Ð¾Ñ‡Ñ‚Ñ‹? %{update_link}."
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29971,6 +30235,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29992,12 +30259,12 @@ msgstr "Поведение"
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr "Выберете между фикÑированным (макÑ. 1280px) и гибким (%{percentage}) макетом."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
+msgstr ""
+
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr "Выберите, какое Ñодержимое вы хотите видеть на Ñтранице обзора проекта."
-msgid "Preferences|Choose what content you want to see on your homepage."
-msgstr "Выберите Ñодержимое, которое вы хотите видеть на вашей главной Ñтранице."
-
msgid "Preferences|Color for added lines"
msgstr ""
@@ -30019,6 +30286,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -30040,9 +30310,6 @@ msgstr "Ðапример: 30 минут назад."
msgid "Preferences|Gitpod"
msgstr "Gitpod"
-msgid "Preferences|Homepage content"
-msgstr "Содержимое главной Ñтраницы"
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr "ВмеÑто того чтобы показывать вÑе изменённые файлы, показывать только один файл. Чтобы переключатьÑÑ Ð¼ÐµÐ¶Ð´Ñƒ файлами, нужно будет иÑпользовать браузер файлов."
@@ -30454,9 +30721,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr "Ключ"
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30487,6 +30751,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr "Ð­Ð»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð°Ñ Ð¿Ð¾Ñ‡Ñ‚Ð° уведомлений"
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr "ОрганизациÑ"
@@ -31174,6 +31441,9 @@ msgstr "Включить Ñборочные линии Ð´Ð»Ñ Ñ€ÐµÐ·ÑƒÐ»ÑŒÑ‚Ð°Ñ‚
msgid "ProjectSettings|Encourage"
msgstr "ПоощрÑÑ‚ÑŒ"
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr "Каждое ÑлиÑние Ñоздает коммит ÑлиÑниÑ."
@@ -31186,6 +31456,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr "Каждый"
@@ -31204,6 +31477,9 @@ msgstr "БыÑтрое ÑлиÑние"
msgid "ProjectSettings|Fast-forward merges only."
msgstr "Только быÑтрые ÑлиÑниÑ."
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr "Гибкий инÑтрумент Ð´Ð»Ñ ÑовмеÑтной разработки идей и Ð¿Ð»Ð°Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ€Ð°Ð±Ð¾Ñ‚Ñ‹ в Ñтом проекте."
@@ -31345,6 +31621,9 @@ msgstr "ТребованиÑ"
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -32155,7 +32434,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr "%{environment_name} будет доÑтупно Ð´Ð»Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñ€Ð°Ð·Ñ€Ð°Ð±Ð¾Ñ‚Ñ‡Ð¸ÐºÐ°Ð¼Ð¸. Ð’Ñ‹ уверены?"
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -32173,7 +32452,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -32356,9 +32635,6 @@ msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²ÐºÐ¸"
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32482,9 +32758,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32717,6 +32990,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr "РегиÑÑ‚Ñ€Ð°Ñ†Ð¸Ñ Ð¿Ñ€Ð¸ помощи приложениÑ"
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -34115,6 +34391,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -34130,6 +34409,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -34178,6 +34463,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -34227,6 +34515,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -34251,6 +34542,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -34296,24 +34590,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34462,9 +34747,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34550,9 +34832,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34571,42 +34862,18 @@ msgstr "Ð’Ñ‹ иÑпользовали %{quotaUsed} из %{quotaLimit} ваших
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34700,6 +34967,9 @@ msgstr "Ð’ÐµÑ€Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Ð¿Ð¾ SSL:"
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34745,12 +35015,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34760,6 +35045,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -35362,6 +35659,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35617,6 +35917,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -37005,12 +37308,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "ÐžÐ³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ñ€ÐµÐ³Ð¸Ñтрации"
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -37284,6 +37596,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr "Что-то пошло не так при добавлении вашей награды. ПожалуйÑта, попробуйте ещё раз."
@@ -37374,6 +37689,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr "Что-то пошло не так при повторном открытии требованиÑ."
@@ -37758,6 +38076,9 @@ msgstr "ОпиÑание Ð´Ð»Ñ Ð¾Ð±ÑŠÐµÐ´Ð¸Ð½ÐµÐ½Ð½Ð¾Ð³Ð¾ (squash) коммит
msgid "Squash commits"
msgstr "Объединить иÑторию коммитов"
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr "ТраÑÑировка Ñтека"
@@ -37773,9 +38094,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr "ПоÑтавьте звёздочку, чтобы Ñделать метку приоритетной. УпорÑдочивайте приоритетные метки перетаÑкиванием Ð´Ð»Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸Ñ… отноÑительного приоритета."
-
msgid "Star labels to start sorting by priority"
msgstr "ПоÑтавьте звёздочки меткам, чтобы начать Ñортировать их по приоритету"
@@ -38514,6 +38832,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr "ВоÑкреÑенье"
@@ -39793,6 +40114,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39889,6 +40213,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39901,6 +40231,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39949,10 +40282,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -40057,9 +40390,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr "Ðа диÑке уже еÑÑ‚ÑŒ репозиторий Ñ Ñ‚Ð°ÐºÐ¸Ð¼ именем"
@@ -40411,6 +40741,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr "Эта диаграмма не может быть отображена"
@@ -40435,7 +40768,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr "Этот коммит был подпиÑан верифицированной подпиÑью другого пользователÑ."
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40816,6 +41149,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40828,9 +41164,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40933,6 +41266,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr "Четверг"
@@ -41143,6 +41479,9 @@ msgstr "Только что"
msgid "Timeago|right now"
msgstr "прÑмо ÑейчаÑ"
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -41170,6 +41509,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "ч"
@@ -41184,6 +41541,12 @@ msgstr[1] "мин"
msgstr[2] "мин"
msgstr[3] "мин"
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr "Ñ"
@@ -41238,6 +41601,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr "Чтобы добавить запиÑÑŒ вручную, предоÑтавьте Ñледующую информацию в приложении на Ñвоем телефоне."
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -41400,9 +41766,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr "СпиÑок задач"
@@ -41427,6 +41790,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -41436,6 +41811,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41544,9 +41922,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41571,9 +41958,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr "Ð’Ñего"
@@ -41742,39 +42141,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr "Пробный период GitLab Ultimate ÑоÑтавлÑет 30 дней, а беÑплатными возможноÑÑ‚Ñми GitLab вы можете пользоватьÑÑ Ñколько угодно. Ðам проÑто нужна Ð´Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ, чтобы активировать ваш пробный период."
@@ -41880,21 +42252,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
+msgid "Tue"
+msgstr ""
+
msgid "Tuesday"
msgstr "Вторник"
msgid "Turn off"
msgstr ""
-msgid "Turn off notifications"
-msgstr ""
-
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr "Twitter"
@@ -42474,6 +42843,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -42501,6 +42873,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42597,10 +42972,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42666,7 +43041,7 @@ msgstr "ИÑпользование реÑурÑов в ваших проекта
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42828,6 +43203,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -43231,7 +43609,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43525,10 +43903,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -44230,6 +44611,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
+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|This project does not accept unsigned commits."
msgstr ""
@@ -44311,6 +44698,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -44353,6 +44743,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -44410,6 +44803,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -44428,6 +44824,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr "Среда"
@@ -44479,6 +44878,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44694,9 +45096,6 @@ msgstr "Удалить Ñтраницу %{pageTitle}?"
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44721,9 +45120,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44811,10 +45207,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44832,7 +45228,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44845,16 +45241,13 @@ msgstr[3] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44863,19 +45256,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
+msgstr ""
+
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44884,19 +45289,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44911,31 +45319,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -45161,6 +45578,13 @@ 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 ""
@@ -45221,13 +45645,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr "Ð’Ñ‹ можете приглаÑить нового учаÑтника в %{project_name} или приглаÑить другую группу."
@@ -45333,13 +45750,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You do not have any subscriptions yet"
msgstr "У Ð²Ð°Ñ Ð¿Ð¾ÐºÐ° нет подпиÑок"
@@ -45506,8 +45916,8 @@ 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."
-msgstr "Ð’Ñ‹ уÑпешно приобрели подпиÑку на план %{plan} на %{seats} меÑÑ‚. Ð’Ñ‹ получите чек по Ñлектронной почте."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
+msgstr ""
msgid "You have unsaved changes"
msgstr ""
@@ -45569,6 +45979,9 @@ msgstr "Вам необходимо загрузить ÑкÑпортироваÐ
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45680,9 +46093,6 @@ msgstr ""
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 "Ð’Ñ‹ уже включили двухфакторную аутентификацию Ñ Ð¸Ñпользованием аутентификаторов Ñ Ð¾Ð´Ð½Ð¾Ñ€Ð°Ð·Ð¾Ð²Ñ‹Ð¼ паролем. Чтобы зарегиÑтрировать другое уÑтройÑтво, Ñначала необходимо отключить двухфакторную аутентификацию."
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45722,6 +46132,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45872,9 +46288,6 @@ msgstr "Ваше уÑтройÑтво было уÑпешно наÑтроено
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45940,6 +46353,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr "Ваш токен ÑброÑа Ð¿Ð°Ñ€Ð¾Ð»Ñ Ð¸Ñтек."
@@ -45955,7 +46371,7 @@ msgstr "Ваш оÑновной Ð°Ð´Ñ€ÐµÑ Ñлектронной почты иÑ
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -46168,9 +46584,6 @@ msgstr ""
msgid "assign yourself"
msgstr "назначить ÑебÑ"
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -46268,6 +46681,9 @@ msgstr "невозможно изменить, еÑли в личном прое
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -46311,6 +46727,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+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 ""
@@ -46359,7 +46778,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46567,7 +46986,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46694,9 +47113,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46733,6 +47149,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr "данные"
@@ -46861,6 +47280,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -47078,6 +47500,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -47182,7 +47607,7 @@ msgstr[1] "запроÑов на ÑлиÑние"
msgstr[2] "запроÑов на ÑлиÑние"
msgstr[3] "запроÑов на ÑлиÑние"
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -47203,8 +47628,8 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
-msgstr "1 коммит cлиÑниÑ"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
+msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
msgstr ""
@@ -47370,6 +47795,9 @@ msgstr[1] "Упоминает обÑуждениÑ"
msgstr[2] "Упоминает обÑуждениÑ"
msgstr[3] "Упоминает обÑуждениÑ"
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -47505,9 +47933,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -48081,6 +48506,9 @@ msgstr "отклонено"
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr "wiki-Ñтраница"
diff --git a/locale/si_LK/gitlab.po b/locale/si_LK/gitlab.po
index 7e227eee335..ef9794cfc14 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-08-12 18:08\n"
+"PO-Revision-Date: 2022-09-11 06:25\n"
msgid " %{start} to %{end}"
msgstr " %{start} සිට %{end}"
@@ -22,6 +22,11 @@ msgstr " %{start} සිට %{end}"
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] "අනුමතකරුවන් %d (ඔබ අනුමත කළà·)"
msgstr[1] "අනුමතකරුවන් %d (ඔබ අනුමත කළà·)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] "දà·à¶ºà¶šà¶­à·Šâ€à·€ %d"
msgstr[1] "දà·à¶ºà¶šà¶­à·Šâ€à·€ %d"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "දවස් %d"
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] "තේරූ ව්â€à¶ºà·à¶´à·˜à¶­à·’ %d යි"
msgstr[1] "තේරූ ව්â€à¶ºà·à¶´à·˜à¶­à·’ %d යි"
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr "භà·à·€à·’තය %{percentageUsed}%%"
@@ -1003,6 +1009,9 @@ msgstr "බයිට %{size}"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr "(ඉවතල෠ඇත)"
msgid "(revoked)"
msgstr "(අහà·à·ƒà·’යි)"
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr "(මෙම පරිà·à·“ලකයà·)"
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr "හිස් ව්â€à¶ºà·à¶´à·˜à¶­à·’යක් සඳහ෠පෙරනිමි à·à·à¶›à·à·€à¶šà·Š තේරීමට නොහà·à¶šà·’ය."
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr "මෙම සිරà·à·ƒà·’ය සහිත පිටුවක් පවතී"
@@ -1833,6 +1851,9 @@ msgstr "බහà·à¶½à·”ම/openapi.Json"
msgid "AWS Access Key"
msgstr "AWS ප්â€à¶»à·€à·šà· යතුර"
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr "AWS රහස් ප්â€à¶»à·€à·šà· යතුර"
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr "වගුවක් එකතු කරන්න"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr "සිරà·à·ƒà·’යක් එක්කරන්න…"
@@ -2214,6 +2238,9 @@ msgstr "දà·à¶±à·Š අදහස එකතු කරන්න"
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr "අදහසක් යොදන්න..."
@@ -2265,6 +2292,12 @@ msgstr "යතුර යොදන්න"
msgid "Add label(s)"
msgstr "නම්පත් යොදන්න"
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr "ලේඛනය යොදන්න"
@@ -2436,6 +2469,9 @@ msgstr "%{labels} %{label_text} එක් කරයි."
msgid "Adds a Zoom meeting."
msgstr "සූම් රà·à·ƒà·Šà·€à·“මක් එක් කරයි"
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr "පරිපà·à¶½à¶š"
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr "CI/CD සීමà·"
msgid "AdminSettings|Configure Let's Encrypt"
msgstr "ලෙට්'ස් එන්ක්â€à¶»à·’ප්ට් වින්â€à¶ºà·à·ƒà¶œà¶­ කරන්න"
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr "සබලයි"
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr "ඒ ඔබයි!"
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr "%{link_start}තහනම් කළ පරිà·à·“ලකයින්%{link_end} ගà·à¶± තව දà·à¶±à¶œà¶±à·Šà¶±."
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr "ඉඩ දී ඇත"
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr "මෙම පිටුවේ කොටසක් පූරණය කිà¶
msgid "An error occurred while loading all the files."
msgstr "සියළුම ගොනු පූරණය කිරීමේදී දà·à·‚යක් ඇති විය."
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr "ප්â€à¶»à·ƒà·Šà¶®à·à¶» දත්ත පූරණය කිරීමේදී දà·à·‚යක් ඇති විය"
@@ -4301,9 +4349,6 @@ msgstr[1] "à·ƒà·à¶šà·ƒà·”ම් සුරà·à¶šà·“මේදී දà·à·‚යකà
msgid "An error occurred while saving your settings. Try saving them again."
msgstr "ඔබගේ à·ƒà·à¶šà·ƒà·”ම් සුරà·à¶šà·“මේදී දà·à·‚යක් ඇති විය. යළි සුරà·à¶šà·“මට බලන්න."
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr "අනුමත"
msgid "Approve a merge request"
msgstr "සංයුක්ත ඉල්ලීම අනුමà·à¶­à·’ය"
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr "වත්මන් සංයුක්ත ඉල්ලීමට අනුමà·à¶­à·’ය."
@@ -5329,6 +5377,11 @@ msgstr "මෙම මà·à·ƒà¶º"
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,8 +5424,8 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
-msgstr "අභිරුචි HTTP à·à·Šâ€à¶»à·“ර්ෂ"
+msgid "AuditStreams|Custom HTTP headers (optional)"
+msgstr ""
msgid "AuditStreams|Delete %{link}"
msgstr "%{link} මකන්න"
@@ -5386,6 +5445,9 @@ msgstr "à·à·Šâ€à¶»à·“ර්ෂය"
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr "උපරිම HTTP à·à·Šâ€à¶»à·“ර්ෂ %{number} වෙත ළඟ෠වී ඇත."
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr "දිගහරින්න"
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "à·à·à¶›à·"
@@ -6855,6 +6914,9 @@ msgstr "ගොනු පිරික්සන්න"
msgid "Browse templates"
msgstr "අච්චු පිරික්සන්න"
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr "නිකුතු සංඛ්â€à¶ºà·à¶½à·šà¶›à¶±"
msgid "CICDAnalytics|Releases"
msgstr "නිකුතු"
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr "බෙදà·à¶œà¶­à·Š ධà·à·€à¶šà¶º භà·à·€à·’තය"
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr "සේවà·à·€ ප්â€à¶»à¶­à·Šâ€à¶ºà¶»à·Šà¶´à¶«à¶ºà¶§ කà·à¶½à¶º"
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr "මේචආචයනය"
@@ -8414,12 +8470,24 @@ msgstr "පà·à·ƒà·Šà¶§à·Šà¶œà·Šâ€à¶»à·™à·ƒà·Š සඳහ෠ක්ලවුඩà
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr "SQL සේවà·à¶¯à·à¶ºà¶šà¶º සඳහ෠ක්ලවුඩ් SQL"
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr "වෙනස්කම් සසඳන්න"
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr "ගිට්ලà·à¶¶à·Š දà·à·‚ ලුහුබà·à¶³à·“ම සබල කරන්න"
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr "ගිට්පොඩ් සබල කරන්න"
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr "නවම්"
msgid "February"
msgstr "නවම්"
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr "සිකුරà·à¶¯à·"
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr "බà·à·„à·à¶» කළ පුද්ගලයින්"
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr "සමූහ"
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’"
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’ය"
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr "\"%{group_name}\" සමූහය යà·à·€à¶­à·Šà¶šà·à¶½ කෙරිණි."
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr "කේතය යළි යවන්න"
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr "කේතය කල් ඉකුත්ය. නව කේතයක් යව෠යළි උත්සà·à·„ කරන්න."
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr "උපරිම තà·à¶­à·Š කිරීම් ගණනට ළඟ෠වී ඇත. %{interval} ක් රà·à¶³à·“ සිටින්න හ෠නව කේතයක් යව෠උත්සà·à·„ කරන්න."
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr "සංයුක්ත ඉල්ලීම්"
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr "සඳුදà·"
msgid "Monitor"
msgstr "නිරීක්â€à·‚ණය"
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr "ප්â€à¶»à·à¶¸à·’තියස් අනුකලන කිහිපයකට සහය නොදක්වයි"
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr "ප්â€à¶»à¶­à·’ඵල නà·à¶­"
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr "අතිරේක පà·à¶»à¶¯à¶­à·Šà¶­"
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr "ධà·à·€à¶š සමඟ පටන් ගන්න"
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr "උත්à·à·Šâ€à¶»à·šà¶«à·’ කිරීමක් තිබේ"
-
-msgid "Runners|upgrade recommended"
-msgstr "උත්à·à·Šâ€à¶»à·šà¶«à·’ කිරීම නිර්දේà·à·’තයි"
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr "සම්මත"
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr "à·ƒà·à¶»à·à¶‚à·à¶º / සටහන"
msgid "Summary comment (optional)"
msgstr "à·ƒà·à¶»à·à¶‚෠අදහස (විකල්ප)"
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr "ඉරිදà·"
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr "ජෙන්කින්ස් සේවà·à¶¯à·à¶ºà¶šà¶º සඳහ෠මුරපදය."
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr "මෙම සංයුක්ත ඉල්ලීමේ මූලà·à·à·Šâ€à¶» ව්â€à¶ºà·à¶´à·˜à¶­à·’ය ඉවත් කර ඇත."
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,12 +39778,12 @@ msgstr "තේමà·à·€"
msgid "There are currently no events."
msgstr "දà·à¶±à¶§ සිදුවීම් නà·à¶­."
+msgid "There are currently no mirrored repositories."
+msgstr ""
+
msgid "There are merge conflicts"
msgstr "සංයුක්ත à¶à¶§à·Šà¶§à¶± තිබේ"
-msgid "There are no %{replicableTypeName} to show"
-msgstr "පෙන්වීමට කිසිදු %{replicableTypeName} නà·à¶­"
-
msgid "There are no GPG keys associated with this account."
msgstr ""
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr "තà·à¶§à·’යේ එම නම සහිත කà·à·‚්ඨයක් දà·à¶±à¶§à¶¸à¶­à·Š ඇත"
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr "මෙම ව්â€à¶ºà·à¶´à·˜à¶­à·’ය %{date} දී මකනු ඇ
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr "බ්â€à¶»à·„ස්පතින්දà·"
@@ -40645,6 +40975,9 @@ msgstr "මේ දà·à¶±à·Š"
msgid "Timeago|right now"
msgstr "හරියටම දà·à¶±à·Š"
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr "වේල෠කලà·à¶´à¶º"
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "පà·à¶º"
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] "විනà·à¶©à·’"
msgstr[1] "විනà·à¶©à·’"
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr "තත්."
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr "කළ යුතු"
-
msgid "To-Do List"
msgstr "කළ යුතු ලේඛනය"
@@ -40925,6 +41282,18 @@ msgstr "සමූහය අනුව පෙරන්න"
msgid "Todos|Filter by project"
msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’ය අනුව පෙරන්න"
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr "සියල්ල අහවර බව යොදන්න"
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr "බොහ෠පරිà·à·“ලකයින් හමු විය. ඉක්මන් ක්â€à¶»à·’යà·à¶¸à·à¶»à·Šà¶œ බොහ෠දුරට %{max_count} දෙනෙකුට සීම෠වේ"
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr "ආපසු යන්න"
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr "මà·à¶­à·˜à¶šà·à·€à·š සිරà·à·ƒà·’ය"
msgid "Topic was successfully updated."
msgstr "මà·à¶­à·˜à¶šà·à·€ à·ƒà·à¶»à·Šà¶®à¶šà·€ යà·à·€à¶­à·Šà¶šà·à¶½ කෙරිණි."
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr "මà·à¶­à·˜à¶šà·"
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr "මුළු"
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr "සමà·à¶œà¶¸à·š නම"
-
msgid "Trial|Continue"
msgstr "ඉදිරියට"
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr "ගිට්ලà·à¶¶à·Š à·„à·’ මූලික විà·à·šà·‚à·à¶‚ග දිගටම නොමිලේ භà·à·€à·’ත෠කරන්න."
-
-msgid "Trial|Country"
-msgstr "රට"
-
msgid "Trial|Dismiss"
msgstr "ඉවත ලන්න"
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr "සේවක සංඛ්â€à¶ºà·à·€"
-
-msgid "Trial|Please select a country"
-msgstr "රටක් තà·à¶»à¶±à·Šà¶±"
-
-msgid "Trial|Telephone number"
-msgstr "දුරකථන අංකය"
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
+msgid "Tue"
+msgstr ""
+
msgid "Tuesday"
msgstr "අඟහරුවà·à¶¯à·"
msgid "Turn off"
msgstr "අක්â€à¶»à·’ය කරන්න"
-msgid "Turn off notifications"
-msgstr "දà·à¶±à·”ම්දීම් අක්â€à¶»à·’ය කරන්න"
-
msgid "Turn on"
msgstr "සක්â€à¶»à·’ය කරන්න"
-msgid "Turn on notifications"
-msgstr "දà·à¶±à·”ම්දීම් සක්â€à¶»à·’ය කරන්න"
-
msgid "Twitter"
msgstr "ට්විටර්"
@@ -41972,6 +42335,9 @@ msgstr "CI/CD විනà·à¶©à·’ භà·à·€à·’තය"
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,8 +43099,8 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr "%{linkStart}ගිට්පොඩ්%{linkEnd} අනුකලනය සබල කර ඇති විට පුද්ගලයින්ට ගිට්ලà·à¶¶à·Š අතිරික්සු පටිත්තකින් සංවර්ධන පරිසරයක් දියත් කළ à·„à·à¶šà·’ය."
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
-msgstr "ඇතුළු වීමෙන් පුද්ගලයින්ට ඔවුන්ගේ ගිණුම යළි සක්â€à¶»à·’ය කළ à·„à·à¶šà·’ය. %{link_start}තව දà·à¶±à¶œà¶±à·Šà¶±%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
+msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
msgstr ""
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr "සියළු පරිසර බලන්න."
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr "සංයුක්ත ඉල්ලීම"
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr "සබà·à¶³à·“මට අසමත්!"
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr "වියමන අඩවිය"
msgid "Website:"
msgstr "වියමන අඩවිය:"
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr "බදà·à¶¯à·"
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr "මෙම විධà·à¶±à¶º කරන්නේ කුමක්ද?"
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr "අවලංගු කරන්න"
@@ -44207,9 +44600,6 @@ msgstr "තව දà·à¶±à¶œà¶±à·Šà¶±."
msgid "WikiPage|Page title"
msgstr "පිටුවේ සිරà·à·ƒà·’ය"
-msgid "WikiPage|Retry"
-msgstr "යළි උත්සà·à·„ය"
-
msgid "WikiPage|Save changes"
msgstr "වෙනස්කම් සුරකින්න"
@@ -44297,11 +44687,11 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
-msgstr ""
+msgid "WorkItem|Add"
+msgstr "එකතු"
msgid "WorkItem|Add a title"
msgstr ""
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr "අවලංගු"
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr "වස෠ඇත"
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr "කà·à¶»à·Šà¶ºà¶ºà¶šà·Š à·ƒà·à¶¯à¶±à·Šà¶±"
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
+msgstr ""
+
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr "විවෘත"
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr "වර්ගය තà·à¶»à¶±à·Šà¶±"
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
+msgid "WorkItem|Tasks"
+msgstr ""
+
+msgid "WorkItem|Test case"
+msgstr ""
+
msgid "WorkItem|Turn off confidentiality"
msgstr ""
msgid "WorkItem|Turn on confidentiality"
msgstr ""
-msgid "WorkItem|Type"
-msgstr "වර්ගය"
-
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr "ගිට්ලà·à¶¶à·Š ව්â€à¶ºà·à¶´à·˜à¶­à·’ නිර්යà·à
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr "ඔබ %{user} ප්â€à¶»à¶­à·’ක්â€à·‚ේප කර ඇත"
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr "ව්â€à¶ºà·à¶´à·˜à¶­à·’යට ඔබගේ CSV ආයà·à¶­à¶º"
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr "උපà·à¶‚ගය à·ƒà·à¶»à·Šà¶®à¶šà·€ පිහිටුව෠ඇත
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr "ඔබගේ ගොනුවේ %{codeStart}සිරà·à·ƒà·’ය%{codeEnd} නම් තීරුවක් අඩංගු විය යුතුය. %{codeStart}සවිස්තරය%{codeEnd} තීරුව විකල්පයකි. ගොනුවකට ඉඩ දෙන උපරිම ප්â€à¶»à¶¸à·à¶«à¶º මෙ.බ. 10 කි."
-msgid "Your first project"
-msgstr "ඔබගේ පළමු ව්â€à¶ºà·à¶´à·˜à¶­à·’ය"
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr "ඔබගේ නව අදහස"
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr "මූලික වි-තà·à¶´à·‘ල ප්â€à¶»à¶­à·’රූපය à¶
msgid "Your profile"
msgstr "ඔබගේ පà·à¶­à·’කඩ"
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr "හවුල් ධà·à·€à¶š සබල කර ඇත්නම් වෙනස් කළ නොහà·à¶šà·’ය"
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr "සබල කළ නොහà·à¶šà·’ය"
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr "%{reportType}: පූරණය දà·à·‚යකට ලක් විය"
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,8 +46436,8 @@ msgstr "බලපත්â€à¶» කළමනà·à¶šà¶»à¶«à¶º"
msgid "ciReport|Manage licenses"
msgstr "බලපත්â€à¶» කළමනà·à¶šà¶»à¶«à¶º"
-msgid "ciReport|Manually Added"
-msgstr "අතින් එක් කෙරිණි"
+msgid "ciReport|Manually added"
+msgstr ""
msgid "ciReport|New"
msgstr "නව"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr "දත්ත"
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr "කියවීමට පමණි"
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr "ඉත෠දිගුය (%{current_value}). උප. ප්â€à¶»à¶¸à·à¶«à¶º %{max_size} කි."
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr "ඉත෠දිගුය (උප. අකුරු %{count})"
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] "සංයුක්ත ඉල්ලීම"
msgstr[1] "සංයුක්ත ඉල්ලීම්"
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr "%{targetBranch}%{squashedCommits} වෙත %{commitCount} සහ %{merg
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr "%{targetBranch} වෙත %{commitCount} එකතු වේ."
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/sk_SK/gitlab.po b/locale/sk_SK/gitlab.po
index 152b26a1b31..4e16188b54b 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-08-12 18:03\n"
+"PO-Revision-Date: 2022-09-11 06:21\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,13 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -171,6 +178,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -269,6 +283,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -479,6 +500,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -528,13 +556,6 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -610,9 +631,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -1061,12 +1079,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1183,6 +1195,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1205,6 +1220,13 @@ msgstr[3] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1246,9 +1268,6 @@ msgstr[3] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1462,13 +1481,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "(this user)"
msgstr ""
@@ -1856,6 +1868,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1919,6 +1937,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -2087,6 +2111,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2402,7 +2429,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2429,6 +2456,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2468,6 +2498,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2519,6 +2552,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2690,6 +2729,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2705,9 +2747,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2906,15 +2945,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2954,7 +2993,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2978,6 +3017,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -3143,7 +3185,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3368,6 +3413,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -4142,9 +4190,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -4229,6 +4274,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4280,6 +4328,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4418,6 +4469,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4557,9 +4611,6 @@ msgstr[3] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4569,13 +4620,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -5134,6 +5185,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5605,6 +5659,13 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5620,10 +5681,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5641,7 +5708,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5662,6 +5729,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5671,9 +5741,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6472,12 +6539,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6493,12 +6554,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6550,9 +6605,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6596,9 +6648,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6636,6 +6685,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6848,6 +6900,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6974,6 +7035,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -7139,6 +7206,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7395,21 +7465,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7419,9 +7480,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8679,6 +8737,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8706,12 +8770,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8766,6 +8842,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9454,10 +9533,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9628,6 +9707,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9694,6 +9776,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9818,6 +9903,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10399,6 +10487,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10505,6 +10596,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10679,7 +10776,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -11282,9 +11379,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11828,6 +11922,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -12060,6 +12157,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -12138,15 +12241,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -12159,6 +12253,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -12234,6 +12331,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -12261,16 +12361,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -12291,16 +12388,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site name"
-msgstr ""
-
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12369,13 +12463,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12565,7 +12659,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12751,6 +12845,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12802,6 +12899,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13372,6 +13472,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13605,6 +13711,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13698,6 +13807,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -14266,6 +14378,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -14287,6 +14402,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -14302,6 +14423,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14524,6 +14651,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14539,9 +14669,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14566,9 +14693,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14716,6 +14840,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14800,9 +14927,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -15244,6 +15368,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -15280,12 +15407,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15352,18 +15473,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15400,6 +15512,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15433,9 +15548,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15511,6 +15623,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15944,6 +16059,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16700,9 +16818,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16913,13 +17028,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -17069,6 +17184,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -17139,6 +17257,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -17151,15 +17272,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -17308,9 +17422,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17506,6 +17617,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17713,6 +17830,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17875,6 +17995,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -18244,6 +18367,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -18256,6 +18382,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -18274,6 +18406,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -18286,6 +18427,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -18313,6 +18457,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18583,9 +18733,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -19021,9 +19168,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -19051,6 +19195,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19414,13 +19561,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19459,21 +19609,21 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19485,43 +19635,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19530,6 +19674,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19678,6 +19825,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19811,9 +19961,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19847,6 +19994,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19970,9 +20120,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19985,15 +20141,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -20015,6 +20192,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -20084,9 +20264,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -20351,9 +20528,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -21143,6 +21317,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -21161,6 +21338,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -21194,7 +21374,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -21335,9 +21515,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21609,6 +21795,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21921,6 +22110,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21936,6 +22128,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21955,9 +22153,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21982,9 +22177,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -22102,12 +22294,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22426,9 +22624,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22474,6 +22669,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22489,12 +22687,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22552,9 +22744,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22564,9 +22753,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22579,9 +22765,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22876,9 +23059,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -23315,7 +23495,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -23330,6 +23510,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23502,9 +23685,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23556,6 +23736,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -24300,6 +24483,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24588,10 +24774,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -25079,6 +25262,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -25166,6 +25352,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -25241,6 +25430,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25921,16 +26134,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25945,6 +26161,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25987,9 +26206,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -26080,12 +26296,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26613,6 +26823,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26625,9 +26838,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26778,6 +26988,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26799,9 +27012,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26992,6 +27202,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -27134,6 +27347,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -27143,6 +27359,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -27158,6 +27392,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -27188,9 +27434,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -27209,6 +27464,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -27315,6 +27576,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27534,6 +27798,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27555,6 +27825,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27678,6 +27951,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27835,9 +28111,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -28051,6 +28333,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -28087,6 +28372,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -28111,6 +28399,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -28331,6 +28622,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28365,9 +28659,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -28377,12 +28668,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28486,7 +28771,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28735,9 +29020,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28813,6 +29095,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29518,25 +29806,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29548,18 +29827,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29617,12 +29890,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29653,9 +29920,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29755,7 +30019,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29971,6 +30235,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29992,10 +30259,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -30019,6 +30286,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -30040,9 +30310,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30454,9 +30721,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30487,6 +30751,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -31174,6 +31441,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -31186,6 +31456,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -31204,6 +31477,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -31345,6 +31621,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -32155,7 +32434,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -32173,7 +32452,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -32356,9 +32635,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32482,9 +32758,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32717,6 +32990,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -34115,6 +34391,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -34130,6 +34409,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -34178,6 +34463,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -34227,6 +34515,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -34251,6 +34542,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -34296,24 +34590,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34462,9 +34747,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34550,9 +34832,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34571,42 +34862,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34700,6 +34967,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34745,12 +35015,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34760,6 +35045,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -35362,6 +35659,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35617,6 +35917,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -37005,12 +37308,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -37284,6 +37596,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -37374,6 +37689,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37758,6 +38076,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37773,9 +38094,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38514,6 +38832,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39793,6 +40114,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39889,6 +40213,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39901,6 +40231,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39949,10 +40282,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -40057,9 +40390,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -40411,6 +40741,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -40435,7 +40768,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40816,6 +41149,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40828,9 +41164,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40933,6 +41266,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -41143,6 +41479,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -41170,6 +41509,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -41184,6 +41541,12 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -41238,6 +41601,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -41400,9 +41766,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -41427,6 +41790,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -41436,6 +41811,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41544,9 +41922,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41571,9 +41958,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41742,39 +42141,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41880,21 +42252,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -42474,6 +42843,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -42501,6 +42873,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42597,10 +42972,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42666,7 +43041,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42828,6 +43203,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -43231,7 +43609,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43525,10 +43903,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -44230,6 +44611,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -44311,6 +44698,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -44353,6 +44743,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -44410,6 +44803,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -44428,6 +44824,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -44479,6 +44878,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44694,9 +45096,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44721,9 +45120,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44811,10 +45207,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44832,7 +45228,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44845,16 +45241,13 @@ msgstr[3] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44863,19 +45256,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
+msgstr ""
+
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44884,19 +45289,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44911,31 +45319,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -45161,6 +45578,13 @@ 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 ""
@@ -45221,13 +45645,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -45333,13 +45750,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -45506,7 +45916,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45569,6 +45979,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45680,9 +46093,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45722,6 +46132,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45872,9 +46288,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45940,6 +46353,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45955,7 +46371,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -46168,9 +46584,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -46268,6 +46681,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -46311,6 +46727,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+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 ""
@@ -46359,7 +46778,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46567,7 +46986,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46694,9 +47113,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46733,6 +47149,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46861,6 +47280,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -47078,6 +47500,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -47182,7 +47607,7 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -47203,7 +47628,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -47370,6 +47795,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -47505,9 +47933,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -48081,6 +48506,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/sl_SI/gitlab.po b/locale/sl_SI/gitlab.po
index 62d3c8f78fa..241db225abd 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-08-12 18:01\n"
+"PO-Revision-Date: 2022-09-11 06:20\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,13 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -171,6 +178,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -269,6 +283,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -479,6 +500,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -528,13 +556,6 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -610,9 +631,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -1061,12 +1079,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1183,6 +1195,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1205,6 +1220,13 @@ msgstr[3] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1246,9 +1268,6 @@ msgstr[3] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1462,13 +1481,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "(this user)"
msgstr ""
@@ -1856,6 +1868,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1919,6 +1937,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -2087,6 +2111,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2402,7 +2429,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2429,6 +2456,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2468,6 +2498,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2519,6 +2552,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2690,6 +2729,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2705,9 +2747,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2906,15 +2945,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2954,7 +2993,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2978,6 +3017,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -3143,7 +3185,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3368,6 +3413,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -4142,9 +4190,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -4229,6 +4274,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4280,6 +4328,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4418,6 +4469,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4557,9 +4611,6 @@ msgstr[3] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4569,13 +4620,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -5134,6 +5185,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5605,6 +5659,13 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5620,10 +5681,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5641,7 +5708,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5662,6 +5729,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5671,9 +5741,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6472,12 +6539,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6493,12 +6554,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6550,9 +6605,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6596,9 +6648,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6636,6 +6685,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6848,6 +6900,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6974,6 +7035,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -7139,6 +7206,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7395,21 +7465,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7419,9 +7480,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8679,6 +8737,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8706,12 +8770,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8766,6 +8842,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9454,10 +9533,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9628,6 +9707,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9694,6 +9776,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9818,6 +9903,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10399,6 +10487,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10505,6 +10596,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10679,7 +10776,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -11282,9 +11379,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11828,6 +11922,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -12060,6 +12157,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -12138,15 +12241,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -12159,6 +12253,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -12234,6 +12331,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -12261,16 +12361,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -12291,16 +12388,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site name"
-msgstr ""
-
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12369,13 +12463,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12565,7 +12659,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12751,6 +12845,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12802,6 +12899,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13372,6 +13472,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13605,6 +13711,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13698,6 +13807,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -14266,6 +14378,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -14287,6 +14402,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -14302,6 +14423,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14524,6 +14651,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14539,9 +14669,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14566,9 +14693,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14716,6 +14840,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14800,9 +14927,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -15244,6 +15368,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -15280,12 +15407,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15352,18 +15473,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15400,6 +15512,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15433,9 +15548,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15511,6 +15623,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15944,6 +16059,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16700,9 +16818,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16913,13 +17028,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -17069,6 +17184,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -17139,6 +17257,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -17151,15 +17272,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -17308,9 +17422,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17506,6 +17617,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17713,6 +17830,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17875,6 +17995,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -18244,6 +18367,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -18256,6 +18382,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -18274,6 +18406,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -18286,6 +18427,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -18313,6 +18457,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18583,9 +18733,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -19021,9 +19168,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -19051,6 +19195,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19414,13 +19561,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19459,21 +19609,21 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19485,43 +19635,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19530,6 +19674,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19678,6 +19825,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19811,9 +19961,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19847,6 +19994,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19970,9 +20120,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19985,15 +20141,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -20015,6 +20192,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -20084,9 +20264,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -20351,9 +20528,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -21143,6 +21317,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -21161,6 +21338,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -21194,7 +21374,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -21335,9 +21515,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21609,6 +21795,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21921,6 +22110,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21936,6 +22128,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21955,9 +22153,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21982,9 +22177,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -22102,12 +22294,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22426,9 +22624,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22474,6 +22669,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22489,12 +22687,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22552,9 +22744,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22564,9 +22753,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22579,9 +22765,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22876,9 +23059,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -23315,7 +23495,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -23330,6 +23510,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23502,9 +23685,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23556,6 +23736,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -24300,6 +24483,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24588,10 +24774,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -25079,6 +25262,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -25166,6 +25352,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -25241,6 +25430,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25921,16 +26134,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25945,6 +26161,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25987,9 +26206,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -26080,12 +26296,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26613,6 +26823,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26625,9 +26838,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26778,6 +26988,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26799,9 +27012,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26992,6 +27202,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -27134,6 +27347,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -27143,6 +27359,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -27158,6 +27392,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -27188,9 +27434,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -27209,6 +27464,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -27315,6 +27576,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27534,6 +27798,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27555,6 +27825,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27678,6 +27951,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27835,9 +28111,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -28051,6 +28333,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -28087,6 +28372,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -28111,6 +28399,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -28331,6 +28622,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28365,9 +28659,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -28377,12 +28668,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28486,7 +28771,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28735,9 +29020,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28813,6 +29095,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29518,25 +29806,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29548,18 +29827,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29617,12 +29890,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29653,9 +29920,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29755,7 +30019,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29971,6 +30235,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29992,10 +30259,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -30019,6 +30286,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -30040,9 +30310,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30454,9 +30721,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30487,6 +30751,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -31174,6 +31441,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -31186,6 +31456,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -31204,6 +31477,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -31345,6 +31621,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -32155,7 +32434,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -32173,7 +32452,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -32356,9 +32635,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32482,9 +32758,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32717,6 +32990,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -34115,6 +34391,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -34130,6 +34409,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -34178,6 +34463,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -34227,6 +34515,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -34251,6 +34542,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -34296,24 +34590,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34462,9 +34747,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34550,9 +34832,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34571,42 +34862,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34700,6 +34967,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34745,12 +35015,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34760,6 +35045,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -35362,6 +35659,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35617,6 +35917,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -37005,12 +37308,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -37284,6 +37596,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -37374,6 +37689,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37758,6 +38076,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37773,9 +38094,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38514,6 +38832,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39793,6 +40114,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39889,6 +40213,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39901,6 +40231,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39949,10 +40282,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -40057,9 +40390,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -40411,6 +40741,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -40435,7 +40768,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40816,6 +41149,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40828,9 +41164,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40933,6 +41266,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -41143,6 +41479,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -41170,6 +41509,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -41184,6 +41541,12 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -41238,6 +41601,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -41400,9 +41766,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -41427,6 +41790,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -41436,6 +41811,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41544,9 +41922,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41571,9 +41958,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41742,39 +42141,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41880,21 +42252,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -42474,6 +42843,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -42501,6 +42873,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42597,10 +42972,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42666,7 +43041,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42828,6 +43203,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -43231,7 +43609,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43525,10 +43903,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -44230,6 +44611,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -44311,6 +44698,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -44353,6 +44743,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -44410,6 +44803,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -44428,6 +44824,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -44479,6 +44878,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44694,9 +45096,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44721,9 +45120,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44811,10 +45207,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44832,7 +45228,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44845,16 +45241,13 @@ msgstr[3] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44863,19 +45256,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
+msgstr ""
+
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44884,19 +45289,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44911,31 +45319,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -45161,6 +45578,13 @@ 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 ""
@@ -45221,13 +45645,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -45333,13 +45750,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -45506,7 +45916,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45569,6 +45979,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45680,9 +46093,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45722,6 +46132,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45872,9 +46288,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45940,6 +46353,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45955,7 +46371,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -46168,9 +46584,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -46268,6 +46681,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -46311,6 +46727,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+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 ""
@@ -46359,7 +46778,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46567,7 +46986,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46694,9 +47113,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46733,6 +47149,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46861,6 +47280,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -47078,6 +47500,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -47182,7 +47607,7 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -47203,7 +47628,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -47370,6 +47795,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -47505,9 +47933,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -48081,6 +48506,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/sq_AL/gitlab.po b/locale/sq_AL/gitlab.po
index 951df618961..9a41ad24ce8 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-08-12 18:02\n"
+"PO-Revision-Date: 2022-09-11 06:20\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/sr_CS/gitlab.po b/locale/sr_CS/gitlab.po
index b88558fa656..24e5e42c6ae 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-08-12 18:07\n"
+"PO-Revision-Date: 2022-09-11 06:24\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,12 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -157,6 +163,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -241,6 +253,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -421,6 +439,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -463,12 +487,6 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -535,9 +553,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -976,12 +991,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1093,6 +1102,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1114,6 +1126,12 @@ msgstr[2] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1150,9 +1168,6 @@ msgstr[2] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1363,12 +1378,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "(this user)"
msgstr ""
@@ -1729,6 +1738,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1792,6 +1807,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1960,6 +1981,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2275,7 +2299,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2302,6 +2326,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2341,6 +2368,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2392,6 +2422,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2563,6 +2599,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2578,9 +2617,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2779,15 +2815,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2827,7 +2863,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2851,6 +2887,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -3016,7 +3055,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3241,6 +3283,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -4015,9 +4060,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -4102,6 +4144,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4153,6 +4198,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4291,6 +4339,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4429,9 +4480,6 @@ msgstr[2] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4441,13 +4489,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4999,6 +5047,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5467,6 +5518,12 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5482,10 +5539,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5503,7 +5566,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5524,6 +5587,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5533,9 +5599,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6334,12 +6397,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6355,12 +6412,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6412,9 +6463,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6457,9 +6505,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6496,6 +6541,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6706,6 +6754,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6832,6 +6889,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6997,6 +7060,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7252,21 +7318,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7276,9 +7333,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8533,6 +8587,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8560,12 +8620,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8620,6 +8692,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9307,10 +9382,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9481,6 +9556,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9547,6 +9625,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9670,6 +9751,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10249,6 +10333,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10354,6 +10441,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10528,7 +10621,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -11131,9 +11224,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11677,6 +11767,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11908,6 +12001,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11986,15 +12085,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -12007,6 +12097,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -12082,6 +12175,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -12109,16 +12205,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner name"
-msgstr ""
-
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -12139,16 +12232,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12217,13 +12307,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12412,7 +12502,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12598,6 +12688,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12649,6 +12742,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13213,6 +13309,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13444,6 +13546,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13537,6 +13642,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -14101,6 +14209,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -14122,6 +14233,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -14137,6 +14254,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14359,6 +14482,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14374,9 +14500,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14401,9 +14524,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14551,6 +14671,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14635,9 +14758,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -15079,6 +15199,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -15115,12 +15238,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15187,18 +15304,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15235,6 +15343,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15268,9 +15379,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15346,6 +15454,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15778,6 +15889,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16531,9 +16645,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16744,13 +16855,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16900,6 +17011,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16969,6 +17083,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16981,14 +17098,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -17134,9 +17245,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17332,6 +17440,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17539,6 +17653,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17701,6 +17818,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -18070,6 +18190,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -18082,6 +18205,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -18100,6 +18229,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -18112,6 +18250,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -18139,6 +18280,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18409,9 +18556,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18847,9 +18991,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18877,6 +19018,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19240,13 +19384,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19285,19 +19432,19 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19309,43 +19456,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19354,6 +19495,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19501,6 +19645,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19633,9 +19780,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19669,6 +19813,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19792,9 +19939,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19807,15 +19960,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19837,6 +20011,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19906,9 +20083,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -20170,9 +20344,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20962,6 +21133,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20980,6 +21154,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -21013,7 +21190,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -21154,9 +21331,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21427,6 +21610,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21739,6 +21925,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21754,6 +21943,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21772,9 +21967,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21799,9 +21991,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21919,12 +22108,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22243,9 +22438,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22291,6 +22483,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22306,12 +22501,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22369,9 +22558,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22381,9 +22567,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22396,9 +22579,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22693,9 +22873,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -23131,7 +23308,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -23146,6 +23323,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23317,9 +23497,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23371,6 +23548,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -24109,6 +24289,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24397,10 +24580,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24886,6 +25066,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24973,6 +25156,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -25048,6 +25234,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25726,16 +25936,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25750,6 +25963,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25792,9 +26008,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25885,12 +26098,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26416,6 +26623,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26428,9 +26638,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26581,6 +26788,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26602,9 +26812,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26794,6 +27001,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26932,6 +27142,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26941,6 +27154,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26956,6 +27187,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26986,9 +27229,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -27007,6 +27259,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -27112,6 +27370,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27331,6 +27592,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27352,6 +27619,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27475,6 +27745,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27631,9 +27904,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27847,6 +28126,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27883,6 +28165,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27907,6 +28192,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -28126,6 +28414,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28159,9 +28450,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -28171,12 +28459,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28279,7 +28561,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28528,9 +28810,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28606,6 +28885,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29311,25 +29596,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29341,18 +29617,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29410,12 +29680,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29446,9 +29710,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29548,7 +29809,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29764,6 +30025,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29785,10 +30049,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29812,6 +30076,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29833,9 +30100,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30247,9 +30511,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30280,6 +30541,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30967,6 +31231,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30979,6 +31246,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30997,6 +31267,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -31138,6 +31411,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31948,7 +32224,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31966,7 +32242,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -32149,9 +32425,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32275,9 +32548,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32509,6 +32779,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33892,6 +34165,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33907,6 +34183,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33955,6 +34237,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -34003,6 +34288,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -34027,6 +34315,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -34072,24 +34363,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34237,9 +34519,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34324,9 +34603,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34345,42 +34633,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34474,6 +34738,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34519,12 +34786,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34534,6 +34816,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -35125,6 +35419,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35380,6 +35677,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36766,12 +37066,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -37045,6 +37354,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -37135,6 +37447,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37519,6 +37834,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37534,9 +37852,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38275,6 +38590,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39544,6 +39862,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39640,6 +39961,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39652,6 +39979,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39700,10 +40030,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39808,9 +40138,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -40162,6 +40489,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -40186,7 +40516,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40567,6 +40897,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40579,9 +40912,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40684,6 +41014,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40894,6 +41227,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40921,6 +41257,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40933,6 +41287,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40987,6 +41347,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -41149,9 +41512,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -41176,6 +41536,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -41185,6 +41557,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41293,9 +41668,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41320,9 +41704,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41491,39 +41887,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41629,21 +41998,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -42223,6 +42589,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -42250,6 +42619,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42346,10 +42718,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42415,7 +42787,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42577,6 +42949,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42979,7 +43354,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43273,10 +43648,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43975,6 +44353,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -44056,6 +44440,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -44098,6 +44485,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -44155,6 +44545,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -44173,6 +44566,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -44224,6 +44620,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44437,9 +44836,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44464,9 +44860,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44554,10 +44947,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44575,7 +44968,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44587,16 +44980,13 @@ msgstr[2] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44605,19 +44995,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44626,19 +45028,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44653,31 +45058,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44902,6 +45316,12 @@ 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 ""
@@ -44962,12 +45382,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -45073,12 +45487,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -45244,7 +45652,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45307,6 +45715,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45418,9 +45829,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45460,6 +45868,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45610,9 +46024,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45676,6 +46087,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45691,7 +46105,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45901,9 +46315,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -46000,6 +46411,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -46042,6 +46456,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+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 ""
@@ -46090,7 +46507,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46294,7 +46711,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46420,9 +46837,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46459,6 +46873,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46585,6 +47002,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46798,6 +47218,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46900,7 +47323,7 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46921,7 +47344,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -47086,6 +47509,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -47221,9 +47647,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47791,6 +48214,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/sr_SP/gitlab.po b/locale/sr_SP/gitlab.po
index 155ea63dbd4..94dcd2c7a51 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-08-12 18:02\n"
+"PO-Revision-Date: 2022-09-11 06:20\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,12 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -157,6 +163,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -241,6 +253,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -421,6 +439,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -463,12 +487,6 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -535,9 +553,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -976,12 +991,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1093,6 +1102,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1114,6 +1126,12 @@ msgstr[2] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1150,9 +1168,6 @@ msgstr[2] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1363,12 +1378,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "(this user)"
msgstr ""
@@ -1729,6 +1738,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1792,6 +1807,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1960,6 +1981,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2275,7 +2299,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2302,6 +2326,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2341,6 +2368,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2392,6 +2422,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2563,6 +2599,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2578,9 +2617,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2779,15 +2815,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2827,7 +2863,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2851,6 +2887,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -3016,7 +3055,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3241,6 +3283,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -4015,9 +4060,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -4102,6 +4144,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4153,6 +4198,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4291,6 +4339,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4429,9 +4480,6 @@ msgstr[2] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4441,13 +4489,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4999,6 +5047,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5467,6 +5518,12 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5482,10 +5539,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5503,7 +5566,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5524,6 +5587,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5533,9 +5599,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6334,12 +6397,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6355,12 +6412,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6412,9 +6463,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6457,9 +6505,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6496,6 +6541,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6706,6 +6754,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6832,6 +6889,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6997,6 +7060,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7252,21 +7318,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7276,9 +7333,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8533,6 +8587,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8560,12 +8620,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8620,6 +8692,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9307,10 +9382,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9481,6 +9556,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9547,6 +9625,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9670,6 +9751,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10249,6 +10333,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10354,6 +10441,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10528,7 +10621,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -11131,9 +11224,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11677,6 +11767,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11908,6 +12001,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11986,15 +12085,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -12007,6 +12097,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -12082,6 +12175,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -12109,16 +12205,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner name"
-msgstr ""
-
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -12139,16 +12232,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12217,13 +12307,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12412,7 +12502,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12598,6 +12688,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12649,6 +12742,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13213,6 +13309,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13444,6 +13546,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13537,6 +13642,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -14101,6 +14209,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -14122,6 +14233,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -14137,6 +14254,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14359,6 +14482,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14374,9 +14500,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14401,9 +14524,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14551,6 +14671,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14635,9 +14758,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -15079,6 +15199,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -15115,12 +15238,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15187,18 +15304,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15235,6 +15343,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15268,9 +15379,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15346,6 +15454,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15778,6 +15889,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16531,9 +16645,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16744,13 +16855,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16900,6 +17011,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16969,6 +17083,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16981,14 +17098,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -17134,9 +17245,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17332,6 +17440,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17539,6 +17653,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17701,6 +17818,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -18070,6 +18190,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -18082,6 +18205,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -18100,6 +18229,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -18112,6 +18250,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -18139,6 +18280,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18409,9 +18556,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18847,9 +18991,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18877,6 +19018,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19240,13 +19384,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19285,19 +19432,19 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19309,43 +19456,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19354,6 +19495,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19501,6 +19645,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19633,9 +19780,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19669,6 +19813,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19792,9 +19939,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19807,15 +19960,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19837,6 +20011,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19906,9 +20083,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -20170,9 +20344,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20962,6 +21133,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20980,6 +21154,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -21013,7 +21190,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -21154,9 +21331,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21427,6 +21610,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21739,6 +21925,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21754,6 +21943,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21772,9 +21967,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21799,9 +21991,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21919,12 +22108,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22243,9 +22438,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22291,6 +22483,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22306,12 +22501,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22369,9 +22558,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22381,9 +22567,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22396,9 +22579,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22693,9 +22873,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -23131,7 +23308,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -23146,6 +23323,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23317,9 +23497,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23371,6 +23548,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -24109,6 +24289,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24397,10 +24580,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24886,6 +25066,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24973,6 +25156,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -25048,6 +25234,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25726,16 +25936,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25750,6 +25963,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25792,9 +26008,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25885,12 +26098,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26416,6 +26623,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26428,9 +26638,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26581,6 +26788,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26602,9 +26812,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26794,6 +27001,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26932,6 +27142,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26941,6 +27154,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26956,6 +27187,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26986,9 +27229,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -27007,6 +27259,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -27112,6 +27370,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27331,6 +27592,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27352,6 +27619,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27475,6 +27745,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27631,9 +27904,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27847,6 +28126,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27883,6 +28165,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27907,6 +28192,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -28126,6 +28414,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28159,9 +28450,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -28171,12 +28459,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28279,7 +28561,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28528,9 +28810,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28606,6 +28885,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29311,25 +29596,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29341,18 +29617,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29410,12 +29680,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29446,9 +29710,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29548,7 +29809,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29764,6 +30025,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29785,10 +30049,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29812,6 +30076,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29833,9 +30100,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30247,9 +30511,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30280,6 +30541,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30967,6 +31231,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30979,6 +31246,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30997,6 +31267,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -31138,6 +31411,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31948,7 +32224,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31966,7 +32242,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -32149,9 +32425,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32275,9 +32548,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32509,6 +32779,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33892,6 +34165,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33907,6 +34183,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33955,6 +34237,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -34003,6 +34288,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -34027,6 +34315,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -34072,24 +34363,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34237,9 +34519,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34324,9 +34603,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34345,42 +34633,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34474,6 +34738,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34519,12 +34786,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34534,6 +34816,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -35125,6 +35419,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35380,6 +35677,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36766,12 +37066,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -37045,6 +37354,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -37135,6 +37447,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37519,6 +37834,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37534,9 +37852,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38275,6 +38590,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39544,6 +39862,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39640,6 +39961,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39652,6 +39979,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39700,10 +40030,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39808,9 +40138,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -40162,6 +40489,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -40186,7 +40516,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40567,6 +40897,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40579,9 +40912,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40684,6 +41014,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40894,6 +41227,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40921,6 +41257,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40933,6 +41287,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40987,6 +41347,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -41149,9 +41512,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -41176,6 +41536,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -41185,6 +41557,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41293,9 +41668,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41320,9 +41704,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41491,39 +41887,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41629,21 +41998,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -42223,6 +42589,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -42250,6 +42619,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42346,10 +42718,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42415,7 +42787,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42577,6 +42949,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42979,7 +43354,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43273,10 +43648,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43975,6 +44353,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -44056,6 +44440,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -44098,6 +44485,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -44155,6 +44545,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -44173,6 +44566,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -44224,6 +44620,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44437,9 +44836,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44464,9 +44860,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44554,10 +44947,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44575,7 +44968,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44587,16 +44980,13 @@ msgstr[2] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44605,19 +44995,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44626,19 +45028,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44653,31 +45058,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44902,6 +45316,12 @@ 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 ""
@@ -44962,12 +45382,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -45073,12 +45487,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -45244,7 +45652,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45307,6 +45715,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45418,9 +45829,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45460,6 +45868,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45610,9 +46024,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45676,6 +46087,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45691,7 +46105,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45901,9 +46315,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -46000,6 +46411,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -46042,6 +46456,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+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 ""
@@ -46090,7 +46507,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46294,7 +46711,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46420,9 +46837,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46459,6 +46873,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46585,6 +47002,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46798,6 +47218,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46900,7 +47323,7 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46921,7 +47344,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -47086,6 +47509,9 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -47221,9 +47647,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47791,6 +48214,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/sv_SE/gitlab.po b/locale/sv_SE/gitlab.po
index 55df14500e9..84b72c691f8 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-08-12 18:02\n"
+"PO-Revision-Date: 2022-09-11 06:20\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr " (från %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr " Insamlad %{time}"
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] "%d godkännare (du har godkänt)"
msgstr[1] "%d godkännare (du har godkänt)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d dag"
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr "%{sourceBranch} till %{targetBranch}"
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr "(borttagen)"
msgid "(revoked)"
msgstr "(Ã¥terkallad)"
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr "Lägg till en tabell"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr "Lägg till nyckel"
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr "Lägg till lista"
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr "Gravatar aktiverat"
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/sw_KE/gitlab.po b/locale/sw_KE/gitlab.po
index d0a593a9951..3f250958394 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-08-12 18:08\n"
+"PO-Revision-Date: 2022-09-11 06:24\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/ta_IN/gitlab.po b/locale/ta_IN/gitlab.po
index 318300fa996..707fad3d5f7 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-08-12 18:03\n"
+"PO-Revision-Date: 2022-09-11 06:21\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/th_TH/gitlab.po b/locale/th_TH/gitlab.po
index 34b8c1c5f65..061a04124da 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-08-12 18:03\n"
+"PO-Revision-Date: 2022-09-11 06:21\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,10 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -129,6 +133,10 @@ msgid "%d approver (you've approved)"
msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -185,6 +193,10 @@ msgid "%d contribution"
msgid_plural "%d contributions"
msgstr[0] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -305,6 +317,10 @@ msgid "%d project selected"
msgid_plural "%d projects selected"
msgstr[0] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -333,10 +349,6 @@ msgid "%d tag per image name"
msgid_plural "%d tags per image name"
msgstr[0] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -385,9 +397,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -806,12 +815,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -913,6 +916,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -932,6 +938,10 @@ msgstr[0] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -958,9 +968,6 @@ msgstr[0] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1165,10 +1172,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-
msgid "(this user)"
msgstr ""
@@ -1475,6 +1478,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1538,6 +1547,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1706,6 +1721,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2021,7 +2039,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2048,6 +2066,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2087,6 +2108,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2138,6 +2162,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2309,6 +2339,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2324,9 +2357,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2525,15 +2555,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2573,7 +2603,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2597,6 +2627,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2762,7 +2795,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -2987,6 +3023,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3761,9 +3800,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3848,6 +3884,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -3899,6 +3938,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4037,6 +4079,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4173,9 +4218,6 @@ msgstr[0] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4185,13 +4227,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4729,6 +4771,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5191,6 +5236,10 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5206,10 +5255,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5227,7 +5282,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5248,6 +5303,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5257,9 +5315,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6058,12 +6113,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6079,12 +6128,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6136,9 +6179,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6179,9 +6219,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6216,6 +6253,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6422,6 +6462,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6548,6 +6597,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6713,6 +6768,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -6966,21 +7024,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -6990,9 +7039,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8241,6 +8287,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8268,12 +8320,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8328,6 +8392,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9013,10 +9080,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9187,6 +9254,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9253,6 +9323,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9374,6 +9447,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -9949,6 +10025,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10052,6 +10131,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10226,7 +10311,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10829,9 +10914,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11375,6 +11457,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11604,6 +11689,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11682,15 +11773,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11703,6 +11785,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11778,6 +11863,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11805,16 +11893,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11835,16 +11920,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -11913,13 +11995,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12106,7 +12188,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12292,6 +12374,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12343,6 +12428,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -12895,6 +12983,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13122,6 +13216,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13215,6 +13312,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13771,6 +13871,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13792,6 +13895,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13807,6 +13916,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14029,6 +14144,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14044,9 +14162,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14071,9 +14186,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14221,6 +14333,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14305,9 +14420,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14749,6 +14861,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14785,12 +14900,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -14857,18 +14966,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -14905,6 +15005,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -14938,9 +15041,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15016,6 +15116,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15446,6 +15549,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16193,9 +16299,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16406,13 +16509,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16562,6 +16665,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16629,6 +16735,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16641,12 +16750,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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"
@@ -16786,9 +16891,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -16984,6 +17086,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17191,6 +17299,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17353,6 +17464,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17722,6 +17836,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17734,6 +17851,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17752,6 +17875,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17764,6 +17896,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17791,6 +17926,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18061,9 +18202,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18499,9 +18637,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18529,6 +18664,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -18892,13 +19030,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -18937,15 +19078,15 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -18957,43 +19098,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19002,6 +19137,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19147,6 +19285,9 @@ msgid "Hide chart"
msgid_plural "Hide charts"
msgstr[0] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19277,9 +19418,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19313,6 +19451,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19436,9 +19577,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19451,15 +19598,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19481,6 +19649,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19550,9 +19721,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19808,9 +19976,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20600,6 +20765,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20618,6 +20786,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20651,7 +20822,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20792,9 +20963,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21063,6 +21240,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21375,6 +21555,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21390,6 +21573,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21406,9 +21595,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21433,9 +21619,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21553,12 +21736,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -21877,9 +22066,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -21925,6 +22111,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -21940,12 +22129,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22003,9 +22186,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22015,9 +22195,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22030,9 +22207,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22327,9 +22501,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22763,7 +22934,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22778,6 +22949,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -22947,9 +23121,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23001,6 +23172,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23727,6 +23901,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24015,10 +24192,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24500,6 +24674,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24587,6 +24764,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24662,6 +24842,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25336,16 +25540,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25360,6 +25567,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25402,9 +25612,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25495,12 +25702,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26022,6 +26223,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26034,9 +26238,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26187,6 +26388,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26208,9 +26412,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26398,6 +26599,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26528,6 +26732,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26537,6 +26744,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26552,6 +26777,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26582,9 +26819,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26603,6 +26849,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26706,6 +26958,9 @@ msgid "On %{end_date}, your trial will end and %{namespace_name} will be limited
msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be limited to %{free_user_limit} members"
msgstr[0] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -26925,6 +27180,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -26946,6 +27207,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27069,6 +27333,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27223,9 +27490,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27439,6 +27712,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27475,6 +27751,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27499,6 +27778,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27716,6 +27998,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27747,9 +28032,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27759,12 +28041,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -27865,7 +28141,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28114,9 +28390,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28192,6 +28465,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -28897,25 +29176,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -28927,18 +29197,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -28996,12 +29260,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29032,9 +29290,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29134,7 +29389,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29350,6 +29605,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29371,10 +29629,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29398,6 +29656,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29419,9 +29680,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -29833,9 +30091,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -29866,6 +30121,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30553,6 +30811,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30565,6 +30826,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30583,6 +30847,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30724,6 +30991,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31534,7 +31804,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31552,7 +31822,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31735,9 +32005,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -31861,9 +32128,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32093,6 +32357,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33446,6 +33713,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33461,6 +33731,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33509,6 +33785,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33555,6 +33834,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33579,6 +33861,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33624,24 +33909,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -33787,9 +34063,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -33872,9 +34145,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -33893,42 +34175,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34022,6 +34280,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34067,12 +34328,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34082,6 +34358,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34651,6 +34939,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -34906,6 +35197,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36288,12 +36582,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36567,6 +36870,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36657,6 +36963,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37041,6 +37350,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37056,9 +37368,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -37797,6 +38106,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39046,6 +39358,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39142,6 +39457,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39154,6 +39475,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39202,10 +39526,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39310,9 +39634,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39664,6 +39985,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39688,7 +40012,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40069,6 +40393,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40081,9 +40408,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40186,6 +40510,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40396,6 +40723,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40423,6 +40753,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40431,6 +40779,12 @@ msgid "Time|min"
msgid_plural "Time|mins"
msgstr[0] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40485,6 +40839,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40647,9 +41004,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40674,6 +41028,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40683,6 +41049,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -40791,9 +41160,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -40818,9 +41196,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -40989,39 +41379,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41127,21 +41490,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41721,6 +42081,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41748,6 +42111,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -41844,10 +42210,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -41913,7 +42279,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42075,6 +42441,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42475,7 +42844,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -42769,10 +43138,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43465,6 +43837,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43546,6 +43924,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43588,6 +43969,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43645,6 +44029,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43663,6 +44050,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43714,6 +44104,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -43923,9 +44316,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -43950,9 +44340,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44040,10 +44427,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44061,7 +44448,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44071,16 +44458,13 @@ msgstr[0] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44089,19 +44473,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
+msgstr ""
+
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44110,19 +44506,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44137,31 +44536,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
+msgstr ""
+
+msgid "WorkItem|Turn off confidentiality"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44384,6 +44792,10 @@ 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 ""
@@ -44444,10 +44856,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44553,10 +44961,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44720,7 +45124,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -44783,6 +45187,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -44894,9 +45301,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -44936,6 +45340,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45086,9 +45496,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45148,6 +45555,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45163,7 +45573,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45367,9 +45777,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45464,6 +45871,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45504,6 +45914,9 @@ msgid "change"
msgid_plural "changes"
msgstr[0] ""
+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 ""
@@ -45552,7 +45965,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -45748,7 +46161,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -45872,9 +46285,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -45911,6 +46321,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46033,6 +46446,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46238,6 +46654,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46336,7 +46755,7 @@ msgid "merge request"
msgid_plural "merge requests"
msgstr[0] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46357,7 +46776,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46518,6 +46937,9 @@ msgid "mrWidget|Mentions issue"
msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46653,9 +47075,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47211,6 +47630,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/tr_TR/gitlab.po b/locale/tr_TR/gitlab.po
index 428a636471d..eebae2c8cfe 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-08-12 18:02\n"
+"PO-Revision-Date: 2022-09-11 06:20\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr " (%{timeoutSource} üzerinden)"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] "%d onaylayan (onayladınız)"
msgstr[1] "%d onaylayan (onayladınız)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] "%d katkı"
msgstr[1] "%d katkı"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d gün"
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] "%d proje seçildi"
msgstr[1] "%d proje seçildi"
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] "%d çözümlenmemiş konu"
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr "%{address} geçersiz bir IP adresi aralığıdır"
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr "%{size} bayt"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr "%{spammable_titlecase} başarıyla Akismet'e gönderildi."
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] "%{strong_start}%{release_count}%{strong_end} Sürüm"
@@ -1264,11 +1275,6 @@ msgstr "(silindi)"
msgid "(revoked)"
msgstr "(iptal edildi)"
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr "Boş bir proje için bir varsayılan dal seçilemez."
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr "AWS Erişim Anahtarı"
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr "AWS Gizli Erişim Anahtarı"
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr "Tablo ekle"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr "Åžimdi yorum ekle"
msgid "Add comment to design"
msgstr "Tasarıma yorum ekle"
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr "Anahtar ekle"
msgid "Add label(s)"
msgstr "Etiket(ler) ekleyin"
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr "Liste ekle"
@@ -2436,6 +2469,9 @@ msgstr "%{labels} %{label_text} ekler."
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr "Yönetici"
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr "Otomatik DevOps etki alanı"
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr "Bu sensin!"
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr "Ä°zin verildi"
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr "%{link_to_client} adlı uygulama, GitLab hesabınıza erişim talep ediy
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr "Epik için biçimlendirilmiş başlık eklenirken bir hata oluştu"
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr "Tüm dosyalar yüklenirken bir hata oluştu."
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr "Tablo verileri alınırken bir hata oluştu"
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr "Bildirimlere abone olunurken bir hata oluÅŸtu."
-
msgid "An error occurred while triggering the job."
msgstr "Ä°ÅŸ tetiklenirken bir hata oluÅŸtu."
@@ -4313,15 +4358,15 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
+msgid "An error occurred while trying to render the content editor. Please try again."
+msgstr ""
+
msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
-msgstr "Bildirim aboneliÄŸi iptal edilirken bir hata oluÅŸtu."
-
msgid "An error occurred while updating approvers"
msgstr "Onaylama sırasında güncelleme yapılırken bir hata oluştu"
@@ -4864,6 +4909,9 @@ msgstr "Onayla"
msgid "Approve a merge request"
msgstr "Bir birleÅŸtirme talebini onayla"
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr "Geçerli birleştirme talebini onayla."
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr "Kullanıcı Etkinlikleri"
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr "Engellenen sorun"
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr "GeniÅŸlet"
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "Dallar"
@@ -6855,6 +6914,9 @@ msgstr "Dosyalara gözat"
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr "%{quick_action_target} bunu kapatır."
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr "Yorum formu konumu"
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr "Düzeltmeleri karşılaştır"
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr "Değişiklikleri karşılaştır"
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr "Bir grup oluÅŸtur"
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr "İlk projenizi oluşturun/içe aktarın"
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr "Profili sil"
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr "Åžifre"
msgid "DastProfiles|Password form field"
msgstr "Şifre form alanı"
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site name"
-msgstr ""
-
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr "Kod parçacığı silinsin mi?"
msgid "Delete source branch"
msgstr "Kaynak dalı sil"
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr "Viki sayfasını düzenle"
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr "Bir konudaki en son yorumunu düzenle (boş bir metin alanından)"
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr "Düzenleme"
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr "E-postalarda üstbilgi ve altbilgiyi etkinleştir"
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr "Epik"
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr "Epik bulunamıyor."
@@ -14950,12 +15069,6 @@ msgstr "Yeni epik ekle"
msgid "Epics|Add an existing epic"
msgstr "Mevcut bir epiÄŸi ekle"
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr "%{epicDateType} tarihini kaydederken bir hata oluÅŸtu"
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr "Bu aynı zamanda %{bStart}%{targetEpicTitle}%{bEnd} epiğinin alt öğelerini %{bStart}%{parentEpicTitle}%{bEnd} epiğinden kaldırır. Emin misiniz?"
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr "süresi dolmuş"
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr "Dallar yüklenirken hata oluştu."
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr "Ülkeler verisi yüklenirken hata oluştu."
-
msgid "Error loading file viewer."
msgstr "Dosya görüntüleyici yüklenirken hata oluştu."
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr "Dönüm noktalarını genişlet"
@@ -16362,9 +16472,6 @@ msgstr "Åžub"
msgid "February"
msgstr "Åžubat"
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr "Düzeltildi:"
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr "Sıklık"
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr "Asla"
msgid "Geo|Next sync scheduled at"
msgstr "Sonraki senkronizasyon planlandı"
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr "Git sürümü"
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr "Genel Kısayollar"
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr "Genel bildirim ayarları"
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr "Gravatar etkinleÅŸtirildi"
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr "Grup için Otomatik DevOps iş hattı güncellendi"
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr "Temizlik hizmeti başarıyla başlatıldı"
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr "Öneri ekle"
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
msgstr "Görüşler"
+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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr "Kapatıldı"
msgid "IssuableStatus|Closed (%{link})"
msgstr "Kapatıldı (%{link})"
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,8 +23121,8 @@ 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."
-msgstr "Etiketler sorunlara ve birleÅŸtirme taleplerine uygulanabilir."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
+msgstr ""
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -22962,6 +23136,9 @@ msgstr "Etiket Tanıtımı"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr "Dil"
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr "BirleÅŸtirme istekleri"
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr "Taslak yorum kaydedilirken bir hata oluÅŸtu."
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr "Yorum kaydedilemedi"
@@ -24855,6 +25038,30 @@ msgstr "Dosya bulunamadı"
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr "BirleÅŸtirildi"
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr "Pazartesi"
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr "Ay"
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr "Popüler Olanlar"
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr "Taşı"
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr "Kredi kartı gerektirmez."
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr "Dağıtım bulunamadı"
-msgid "No due date"
-msgstr "BitiÅŸ tarihi yok"
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr "Depo bulunamadı"
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr "Ön izlenecek bir şey yok."
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr "Bildirim etkinlikleri"
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr "Açıldı"
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,8 +28351,8 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
-msgstr "Paketler ve Kayıtlar"
+msgid "Packages and registries"
+msgstr ""
msgid "Page not found"
msgstr ""
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,12 +29839,12 @@ msgstr "Davranış"
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr "Sabit (maks. 1280px) ve değişken (%{percentage}) uygulama düzeni arasından seçim yapın."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
+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 what content you want to see on your homepage."
-msgstr "Ana sayfanızda görmek istediğiniz içeriği seçin."
-
msgid "Preferences|Color for added lines"
msgstr ""
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr "Örneğin: 30 dakika önce."
msgid "Preferences|Gitpod"
msgstr "Gitpod"
-msgid "Preferences|Homepage content"
-msgstr "Ana sayfa içeriği"
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr "Değiştirilen tüm dosyalar yerine, bir seferde yalnızca bir dosya gösterin. Dosyalar arasında geçiş yapmak için dosya tarayıcısını kullanın."
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr "Anahtar"
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr "Dosya seçilmedi."
msgid "Profiles|Notification email"
msgstr "Bildirim e-postası"
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr "Organizasyon"
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr "Herkes"
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr "%{environment_name} geliştiriciler için yazılabilir olacak. Emin misiniz?"
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr "Yollama etkinlikleri"
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "Kayıt kısıtlamaları"
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr "Standart"
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr "Önceliğe göre sıralamaya başlamak için etiketlere yıldız ekleyin"
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr "Pazar"
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr "ÅŸimdi"
msgid "Timeago|right now"
msgstr "hemen ÅŸimdi"
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr "Saat dilimi"
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "saat"
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] "dak"
msgstr[1] "dak"
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr "sn"
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr "Aramanızı genişletmek için yukarıdaki filtreleri değiştirin veya
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr "Yapılacaklar Listesi"
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr "Twitter"
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr "Projelerinizdeki kaynakların kullanımı"
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr "%{pageTitle} sayfası silinsin mi?"
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
+msgstr ""
+
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr "Profiliniz"
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr "kendinizi atayın"
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr "Lisansları yönet"
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] "birleÅŸtirme isteÄŸi"
msgstr[1] "birleÅŸtirme isteÄŸi"
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,8 +47060,8 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
-msgstr "1 iÅŸlemi birleÅŸtir"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
+msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
msgstr ""
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr "güvenlik açığı kapatıldı"
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr "viki sayfası"
diff --git a/locale/uk/gitlab.po b/locale/uk/gitlab.po
index 80434d31f2d..08fec90b133 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-08-12 18:02\n"
+"PO-Revision-Date: 2022-09-11 08:06\n"
msgid " %{start} to %{end}"
msgstr " %{start} до %{end}"
@@ -22,6 +22,13 @@ msgstr " %{start} до %{end}"
msgid " (from %{timeoutSource})"
msgstr " (з %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid " Collected %{time}"
msgstr " Зібрано %{time}"
@@ -171,6 +178,13 @@ msgstr[1] "%d затверджуючі оÑоби (ви затвердили)"
msgstr[2] "%d затверджуючих оÑіб (ви затвердили)"
msgstr[3] "%d затверджуючих оÑіб (ви затвердили)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] "%d артефакт"
+msgstr[1] "%d артефакти"
+msgstr[2] "%d артефактів"
+msgstr[3] "%d артефактів"
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] "%d призначена задача"
@@ -269,6 +283,13 @@ msgstr[1] "%d внеÑки"
msgstr[2] "%d внеÑків"
msgstr[3] "%d внеÑків"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d день"
@@ -479,6 +500,13 @@ msgstr[1] "%d проєкти обрано"
msgstr[2] "%d проєктів обрано"
msgstr[3] "%d проєктів обрано"
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -528,13 +556,6 @@ msgstr[1] "%d теги відповідно до імені образу"
msgstr[2] "%d тегів відповідно до імені образу"
msgstr[3] "%d тегів відповідно до імені образу"
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] "%d непризначена задача"
-msgstr[1] "%d непризначені задачі"
-msgstr[2] "%d непризначених задач"
-msgstr[3] "%d непризначених задач"
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] "%d невирішена тема"
@@ -610,9 +631,6 @@ msgstr "%{actionText} і повторно відкрити %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr "%{address} - недійÑний діапазон IP-адреÑ"
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr "%{attribute} має бути між %{min} і %{max}"
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr "%{author_link} клонув(-ла) %{original_issue} до %{new_issue}."
@@ -656,7 +674,7 @@ msgid "%{code_open}Masked:%{code_close} Hidden in job logs. Must match masking r
msgstr "%{code_open}ЗамаÑковано:%{code_close} Сховано в журналах завдань. Повинні відповідати вимогам до маÑкуваннÑ."
msgid "%{code_open}Protected:%{code_close} Only exposed to protected branches or protected tags."
-msgstr ""
+msgstr "%{code_open}Захищений:%{code_close} Відкритий лише захищеними гілками або тегами."
msgid "%{commit_author_link} authored %{commit_authored_timeago}"
msgstr "%{commit_author_link} автор %{commit_authored_timeago}"
@@ -1059,13 +1077,7 @@ msgid "%{over_limit_message} To get more members, an owner of the group can star
msgstr "%{over_limit_message} Щоб отримати більше учаÑників, влаÑник групи може почати пробну верÑÑ–ÑŽ або перейти на платний рівень."
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
+msgstr "%{over_limit_message}, Щоб отримати більше міÑць, %{link_start}перейдіть на платний рівень%{link_end}."
msgid "%{percentageUsed}%% used"
msgstr "%{percentageUsed}%% викориÑтано"
@@ -1183,6 +1195,9 @@ msgstr "%{size} байт"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr "%{sourceBranch} в %{targetBranch}"
+msgid "%{source} %{copyButton} into %{target}"
+msgstr "%{source} %{copyButton} в %{target}"
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr "%{spammable_titlecase} було уÑпішно відправлено до Akismet."
@@ -1205,6 +1220,13 @@ msgstr[3] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr "%{strongOpen}ПопередженнÑ:%{strongClose} поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð³Ñ€ÑƒÐ¿Ð¸ SAML може Ñпричинити автоматичне Ð²Ð¸Ð»ÑƒÑ‡ÐµÐ½Ð½Ñ ÑƒÑ‡Ð°Ñників із груп."
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr "%{strongStart}Порада:%{strongEnd}Ви також можете перевірÑти запити на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¾, %{linkStart}ДізнатиÑÑŒ більше%{linkEnd}"
@@ -1246,9 +1268,6 @@ msgstr[3] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr "%{strong_start}%{human_size}%{strong_end} Сховище проєкту"
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] "%{strong_start}%{release_count}%{strong_end} Реліз"
@@ -1291,7 +1310,7 @@ msgid "%{timebox_type} must have a start and due date"
msgstr "%{timebox_type} повинні мати дату початку Ñ– завершеннÑ"
msgid "%{time} UTC"
-msgstr ""
+msgstr "%{time} UTC"
msgid "%{title} %{operator} %{threshold}"
msgstr "%{title} %{operator} %{threshold}"
@@ -1462,13 +1481,6 @@ msgstr "(видалено)"
msgid "(revoked)"
msgstr "(відкликано)"
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] "(об'єднано %d коміт)"
-msgstr[1] "(об'єднано %d коміта)"
-msgstr[2] "(об'єднано %d комітів)"
-msgstr[3] "(об'єднано %d комітів)"
-
msgid "(this user)"
msgstr "(цей кориÑтувач)"
@@ -1856,6 +1868,12 @@ msgstr "ОÑновний шаблон розробки програм Linux за
msgid "A complete DevOps platform"
msgstr "Повноцінна DevOps платформа"
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr "Ðе можна обирати уÑтавну гілку Ð´Ð»Ñ Ð¿Ð¾Ñ€Ð¾Ð¶Ð½ÑŒÐ¾Ð³Ð¾ проєкту."
@@ -1919,6 +1937,12 @@ msgstr "Ðовий перÑональний токен доÑтупу, назвÐ
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr "Ðеконфіденційний епік не може бути призначеним Ð´Ð»Ñ ÐºÐ¾Ð½Ñ„Ñ–Ð´ÐµÐ½Ñ†Ñ–Ð¹Ð½Ð¾Ð³Ð¾ батьківÑького епіка"
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr "Сторінка з таким заголовком вже Ñ–Ñнує"
@@ -2087,6 +2111,9 @@ msgstr "folder/openapi.json"
msgid "AWS Access Key"
msgstr "Ключ доÑтупу AWS"
+msgid "AWS OpenSearch IAM credentials"
+msgstr "Облікові дані AWS OpenSearch IAM"
+
msgid "AWS Secret Access Key"
msgstr "Секретний ключ доÑтупу AWS"
@@ -2259,7 +2286,7 @@ msgid "Accessible by any user who is logged in."
msgstr "ДоÑтупно Ð´Ð»Ñ Ð±ÑƒÐ´ÑŒ-Ñкого кориÑтувача, Ñкий увійшов у ÑиÑтему."
msgid "Accessible by anyone, regardless of authentication."
-msgstr ""
+msgstr "ДоÑтупно будь-кому, незалежно від автентифікації."
msgid "Account"
msgstr "Обліковий запиÑ"
@@ -2402,8 +2429,8 @@ msgstr "Додати коментар до цього Ñ€Ñдка або пере
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
-msgstr "Додайте перÑоналізоване Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð· детальними відомоÑÑ‚Ñми про Ñпільні runner'и інÑтанÑа. ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶Ð°Ñ”Ñ‚ÑŒÑÑ Ð² налаштуваннÑÑ… групи та CI/CD проєкту в розділі Runner'и. Markdown підтримуєтьÑÑ."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
+msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
msgstr "Додайте загальний коментар до цього %{noteableDisplayName}."
@@ -2429,6 +2456,9 @@ msgstr "Додати ÑÑƒÑ„Ñ–ÐºÑ Ð´Ð¾ електронної адреÑи ÑлÑ
msgid "Add a table"
msgstr "Додати таблицю"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr "Додати назву..."
@@ -2468,6 +2498,9 @@ msgstr "Додати коментар"
msgid "Add comment to design"
msgstr "Додати коментар до дизайну"
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr "Додати коментар..."
@@ -2502,7 +2535,7 @@ msgid "Add existing confidential %{issuableType}"
msgstr "Додати Ñ–Ñнуючий конфіденційний %{issuableType}"
msgid "Add existing issue"
-msgstr ""
+msgstr "Додати Ñ–Ñнуючу задачу"
msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
msgstr "Додати заголовок Ñ– футер в електронні лиÑти. Будь лаÑка, зверніть увагу, що Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð»ÑŒÐ¾Ñ€Ñƒ будуть викориÑтовуватиÑÑ Ñ‚Ñ–Ð»ÑŒÐºÐ¸ в інтерфейÑÑ– заÑтоÑунку"
@@ -2519,6 +2552,12 @@ msgstr "Додати ключ"
msgid "Add label(s)"
msgstr "Додати мітку(-ки)"
+msgid "Add labels"
+msgstr "Додати мітки"
+
+msgid "Add license"
+msgstr "Додати ліцензію"
+
msgid "Add list"
msgstr "Додати ÑпиÑок"
@@ -2690,6 +2729,9 @@ msgstr "Додає %{labels} %{label_text}."
msgid "Adds a Zoom meeting."
msgstr "Додає Zoom-зуÑтріч."
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr "Додати нагадуваннÑ."
@@ -2705,9 +2747,6 @@ msgstr "Додайте цей %{issuable_type} Ñк пов'Ñзаний з %{iss
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr "Ðалаштувати чаÑтоту оновлень інтерфейÑу Gitlab."
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr "Ðалаштуйте фільтри / умови пошуку, наведені вище. Якщо ви думаєте, що це помилка, переглÑньте документацію на Ñторінці %{linkStart}Geo Troubleshooting%{linkEnd}, щоб отримати додаткову інформацію."
-
msgid "Admin"
msgstr "ÐдмініÑтратор"
@@ -2906,15 +2945,15 @@ msgstr "Ð’ÑÑ– нові проєкти за замовчуваннÑм можуÑ
msgid "AdminSettings|Auto DevOps domain"
msgstr "Домен Auto DevOps"
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr "ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ CI/CD"
msgid "AdminSettings|Configure Let's Encrypt"
msgstr "Ðалаштувати Let's Encrypt"
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2954,7 +2993,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2978,11 +3017,14 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr "Увімкнено"
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr "Токен доÑтупу до каналів"
msgid "AdminSettings|Git abuse rate limit"
-msgstr ""
+msgstr "Ліміт кількоÑÑ‚Ñ– порушень Git"
msgid "AdminSettings|I have read and agree to the Let's Encrypt %{link_start}Terms of Service%{link_end} (PDF)."
msgstr "Я прочитав Ñ– погоджуюÑÑŒ %{link_start}умовами викориÑтаннÑ%{link_end} (PDF)."
@@ -3114,7 +3156,7 @@ msgid "AdminSettings|Set the maximum size of GitLab Pages per project (0 for unl
msgstr "Ð’Ñтановіть макÑимальний розмір GitLab Pages Ð´Ð»Ñ ÐºÐ¾Ð¶Ð½Ð¾Ð³Ð¾ проєкту (0 Ð´Ð»Ñ Ð½ÐµÐ¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð¾Ñ— кількоÑÑ‚Ñ–). %{link_start}ДовідайтеÑÑŒ більше.%{link_end}"
msgid "AdminSettings|Setting must be greater than 0."
-msgstr ""
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¼Ð°Ñ” бути більше 0."
msgid "AdminSettings|Size and domain settings for Pages static sites."
msgstr "Розмір Ñ– параметри Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ð¼ÐµÐ½Ñƒ Ð´Ð»Ñ Ñтатичних Ñайтів Сторінок."
@@ -3143,7 +3185,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr "ВикориÑтовуйте Ñлужбу AWS OpenSearch з обліковими даними IAM"
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3210,7 +3255,7 @@ msgid "AdminUsers|2FA Enabled"
msgstr "2FA увімкнено"
msgid "AdminUsers|A user can validate themselves by inputting a credit/debit card, or an admin can manually validate a user. Validated users can use free CI minutes on shared runners."
-msgstr ""
+msgstr "КориÑтувач може підтвердити Ñебе, ввівши кредитну/дебетову картку, або адмініÑтратор може вручну підтвердити кориÑтувача. Перевірені кориÑтувачі можуть викориÑтовувати безкоштовні CI хвилини у Ñпільних runner'ах."
msgid "AdminUsers|Access"
msgstr "ДоÑтуп"
@@ -3264,7 +3309,7 @@ msgid "AdminUsers|Automatically marked as default internal user"
msgstr "Ðвтоматично позначено Ñк внутрішній кориÑтувач за замовчуваннÑм"
msgid "AdminUsers|Avatar"
-msgstr ""
+msgstr "аватар"
msgid "AdminUsers|Ban user"
msgstr "Забанити кориÑтувача"
@@ -3368,14 +3413,17 @@ msgstr "Задачи, Ñтворені цим кориÑтувачем, прих
msgid "AdminUsers|It's you!"
msgstr "Це ви!"
+msgid "AdminUsers|LDAP Blocked"
+msgstr "LDAP заблоковано"
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr "Докладніше про %{link_start}забанених кориÑтувачів.%{link_end}"
msgid "AdminUsers|Limits"
-msgstr ""
+msgstr "ОбмеженнÑ"
msgid "AdminUsers|Linkedin"
-msgstr ""
+msgstr "Linkedin"
msgid "AdminUsers|Locked"
msgstr "Заблоковано"
@@ -3405,7 +3453,7 @@ msgid "AdminUsers|Personal projects, group and user history will be left intact"
msgstr "ПерÑональні проєкти, групи, та Ñ–Ñторію кориÑтувача буде залишено без змін"
msgid "AdminUsers|Quota of CI/CD minutes"
-msgstr ""
+msgstr "Квота CI/CD хвилин"
msgid "AdminUsers|Reactivating a user will:"
msgstr "Повторна Ð°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ñ–Ñ ÐºÐ¾Ñ€Ð¸Ñтувача зробить:"
@@ -3447,7 +3495,7 @@ msgid "AdminUsers|Sort by"
msgstr "Сортувати за"
msgid "AdminUsers|The maximum number of CI/CD minutes on shared runners that a group can use each month. Set 0 for unlimited. Set empty to inherit the global setting of %{minutes}"
-msgstr ""
+msgstr "МакÑимальна кількіÑÑ‚ÑŒ CI/CD хвилин на Ñпільних runner'ах, Ñкі група може викориÑтовувати щоміÑÑцÑ. Ð’Ñтановіть 0 Ð´Ð»Ñ Ð½ÐµÐ¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð¾Ñ— кількоÑÑ‚Ñ–. Ð’Ñтановіть пуÑтим, щоб уÑпадкувати глобальне Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ %{minutes}"
msgid "AdminUsers|The user can't access git repositories."
msgstr "КориÑтувач не може отримати доÑтуп до репозиторіїв git."
@@ -4071,7 +4119,7 @@ msgid "All threads resolved!"
msgstr "УÑÑ– Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð²Ð¸Ñ€Ñ–ÑˆÐµÐ½Ð¾!"
msgid "All users in this group must set up two-factor authentication"
-msgstr ""
+msgstr "УÑÑ– кориÑтувачі цієї групи повинні налаштувати двофакторну автентифікацію"
msgid "All users must accept the Terms of Service and Privacy Policy to access GitLab"
msgstr "УÑÑ– кориÑтувачі мають прийнÑти умови Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð¿Ð¾Ñлуг Ñ– Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð½Ñ Ð¿Ð¾Ð»Ñ–Ñ‚Ð¸ÐºÐ¸ конфіденційноÑÑ‚Ñ– Ð´Ð»Ñ Ð´Ð¾Ñтупу до GitLab"
@@ -4142,9 +4190,6 @@ msgstr "Дозволити кориÑтувачам реєÑтрувати буÐ
msgid "Allowed"
msgstr "Дозволено"
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr "Дозволені Ñимволи: +, 0-9, - Ñ– пробіли."
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr "Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ð¾Ð·Ð²Ð¾Ð»ÐµÐ½Ð¸Ñ… доменів Ð°Ð´Ñ€ÐµÑ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾Ñ— пошти допуÑкаєтьÑÑ Ð»Ð¸ÑˆÐµ Ð´Ð»Ñ Ð³Ñ€ÑƒÐ¿ найвищого рівнÑ"
@@ -4152,7 +4197,7 @@ msgid "Allowed to create:"
msgstr "Дозволено Ñтворити:"
msgid "Allowed to delete projects"
-msgstr ""
+msgstr "Дозволено видалÑти проєкти"
msgid "Allowed to fail"
msgstr "Ðевдача дозволена"
@@ -4229,6 +4274,9 @@ msgstr "Додаток під назвою %{link_to_client} запитує до
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾ÑŽ поштою нещодавно надіÑлано з панелі адмініÑтратора. Зачекайте, будь лаÑка, %{wait_time_in_words}, перш ніж намагатиÑÑ Ð½Ð°Ð´Ñ–Ñлати інше повідомленнÑ."
+msgid "An email will be sent with the report attached after it is generated."
+msgstr "ПіÑÐ»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð²Ñ–Ñ‚Ñƒ буде надіÑлано електронний лиÑÑ‚ із вкладеннÑм."
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr "Порожнє поле Gitlab-кориÑтувача буде заповнено іменем кориÑтувача з FogBugz (наприклад \"John Smith\") в опиÑÑ– вÑÑ–Ñ… задач та коментарів. Крім того ці задачі та коментарі будуть аÑоційовані з та/або призначені на автора проєкту."
@@ -4260,7 +4308,7 @@ msgid "An error occurred fetching the project authors."
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð°Ð²Ñ‚Ð¾Ñ€Ñ–Ð² проєкту."
msgid "An error occurred fetching the public deploy keys. Please try again."
-msgstr ""
+msgstr "Помилка при отриманні публічних ключів Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ. Будь лаÑка, Ñпробуйте знову."
msgid "An error occurred previewing the blob"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð½ÑŒÐ¾Ð³Ð¾ переглÑду об'єкта"
@@ -4280,6 +4328,9 @@ msgstr "Помилка при додаванні затверджуючих оÑ
msgid "An error occurred while adding formatted title for epic"
msgstr "Помилка під Ñ‡Ð°Ñ Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð²Ñ–Ð´Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚Ð¾Ð²Ð°Ð½Ð¾Ð³Ð¾ заголовка Ð´Ð»Ñ ÐµÐ¿Ñ–ÐºÐ°"
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr "Під Ñ‡Ð°Ñ Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ñ–Ñ— вашої ролі ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°"
@@ -4418,6 +4469,9 @@ msgstr "При завантаженні розділу цієї Ñторінки
msgid "An error occurred while loading all the files."
msgstr "Помилка при завантаженні вÑÑ–Ñ… файлів."
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr "Під Ñ‡Ð°Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°. Будь лаÑка, Ñпробуйте ще раз."
+
msgid "An error occurred while loading chart data"
msgstr "Виникла помилка під Ñ‡Ð°Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… діаграми"
@@ -4557,9 +4611,6 @@ msgstr[3] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при збереженні ваших параметрів. Спробуйте зберегти Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð½Ð¾Ð²Ñƒ."
-msgid "An error occurred while subscribing to notifications."
-msgstr "Помилка при підпиÑці на ÑповіщеннÑ."
-
msgid "An error occurred while triggering the job."
msgstr "Помилка при запуÑку завданнÑ."
@@ -4569,15 +4620,15 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr "Під Ñ‡Ð°Ñ Ñпроби Ñтворити звіт ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°. Будь лаÑка, Ñпробуйте пізніше."
+msgid "An error occurred while trying to render the content editor. Please try again."
+msgstr ""
+
msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr "Помилка при запуÑку нового конвеєру Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ запиту на злиттÑ."
msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
-msgstr "Помилка при відпиÑці від Ñповіщень."
-
msgid "An error occurred while updating approvers"
msgstr "Помилка при оновленні затверджуючих оÑіб"
@@ -4765,7 +4816,7 @@ msgid "Application settings saved successfully"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°ÑтоÑунку уÑпішно збережено"
msgid "Application settings saved successfully."
-msgstr ""
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°ÑтоÑунку уÑпішно збережено."
msgid "Application settings update failed"
msgstr "ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½ÑŒ програми не вдалоÑÑ"
@@ -4866,16 +4917,16 @@ msgid "ApplicationSettings|Require admin approval for new sign-ups"
msgstr "Потрібне ÑÑ…Ð²Ð°Ð»ÐµÐ½Ð½Ñ Ð°Ð´Ð¼Ñ–Ð½Ñ–Ñтратора Ð´Ð»Ñ Ð½Ð¾Ð²Ð¸Ñ… реєÑтрацій"
msgid "ApplicationSettings|Require lowercase letters"
-msgstr ""
+msgstr "Потрібні літери нижнього регіÑтру"
msgid "ApplicationSettings|Require numbers"
-msgstr ""
+msgstr "Потрібні чиÑла"
msgid "ApplicationSettings|Require symbols"
-msgstr ""
+msgstr "Потрібні Ñимволи"
msgid "ApplicationSettings|Require uppercase letters"
-msgstr ""
+msgstr "Потрібні літери верхнього регіÑтру"
msgid "ApplicationSettings|Restricts sign-ups for email addresses that match the given regex. %{linkStart}What is the supported syntax?%{linkEnd}"
msgstr ""
@@ -4884,7 +4935,7 @@ msgid "ApplicationSettings|Save changes"
msgstr "Зберегти зміни"
msgid "ApplicationSettings|See %{linkStart}password policy guidelines%{linkEnd}."
-msgstr ""
+msgstr "Див. %{linkStart}правила політики паролів%{linkEnd}."
msgid "ApplicationSettings|Send confirmation email on sign-up"
msgstr "ÐадіÑлати лиÑта на пошту з підтвердженнÑм під Ñ‡Ð°Ñ Ñ€ÐµÑ”Ñтрації"
@@ -4911,16 +4962,16 @@ msgid "ApplicationSettings|Users with e-mail addresses that match these domain(s
msgstr ""
msgid "ApplicationSettings|When enabled, new passwords must contain at least one lowercase letter (a-z)."
-msgstr ""
+msgstr "Коли увімкнено, нові паролі повинні міÑтити принаймні одну малу літеру (a-z)."
msgid "ApplicationSettings|When enabled, new passwords must contain at least one number (0-9)."
-msgstr ""
+msgstr "Коли увімкнено, нові паролі повинні міÑтити принаймні одну цифру (0-9)."
msgid "ApplicationSettings|When enabled, new passwords must contain at least one symbol."
-msgstr ""
+msgstr "Коли увімкнено, нові паролі повинні міÑтити хоча б один Ñимвол."
msgid "ApplicationSettings|When enabled, new passwords must contain at least one uppercase letter (A-Z)."
-msgstr ""
+msgstr "Коли увімкнено, нові паролі повинні міÑтити принаймні одну велику літеру (A-Z)."
msgid "ApplicationSettings|domain.com"
msgstr "domain.com"
@@ -5120,7 +5171,7 @@ msgid "ApprovalSettings|This setting is configured in %{groupName} and can only
msgstr "Цей параметр налаштовано в %{groupName} Ñ– може бути змінене в налаштуваннÑÑ… групи лише адмініÑтратором або влаÑником групи."
msgid "ApprovalSettings|When a commit is added:"
-msgstr ""
+msgstr "Коли додаєтьÑÑ ÐºÐ¾Ð¼Ñ–Ñ‚:"
msgid "Approvals are optional."
msgstr "Ð—Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð½ÐµÐ¾Ð±Ð¾Ð²'Ñзкові."
@@ -5134,6 +5185,9 @@ msgstr "Затвердити"
msgid "Approve a merge request"
msgstr "Затвердити запит на злиттÑ"
+msgid "Approve merge request"
+msgstr "Затвердити запит на злиттÑ"
+
msgid "Approve the current merge request."
msgstr "Затвердити поточний запит на злиттÑ."
@@ -5527,10 +5581,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 ""
+msgstr "Щонайменше один із ваших ПерÑональних Токенів ДоÑтупу проÑтрочено. %{generate_new}"
msgid "At least one of your Personal Access Tokens will expire soon. %{generate_new}"
-msgstr ""
+msgstr "Щонайменше один із ваших ПерÑональних Токенів ДоÑтупу незабаром буде проÑтрочено. %{generate_new}"
msgid "At risk"
msgstr "З ризиком"
@@ -5605,26 +5659,39 @@ msgstr "Цього міÑÑцÑ"
msgid "AuditLogs|User Events"
msgstr "Події кориÑтувача"
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] "%d призначеннÑ"
+msgstr[1] "%d призначеннÑ"
+msgstr[2] "%d призначень"
+msgstr[3] "%d призначень"
+
msgid "AuditStreams|A header with this name already exists."
-msgstr ""
+msgstr "Заголовок з таким іменем вже Ñ–Ñнує."
msgid "AuditStreams|Active"
msgstr "Ðктивний"
msgid "AuditStreams|Add a custom header"
-msgstr ""
+msgstr "Додати влаÑний заголовок"
msgid "AuditStreams|Add a custom value"
-msgstr ""
+msgstr "Додати влаÑне значеннÑ"
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
-msgstr "Додати потік"
+msgid "AuditStreams|Add header"
+msgstr "Додати заголовок"
+
+msgid "AuditStreams|Add streaming destination"
+msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
msgstr ""
@@ -5641,7 +5708,7 @@ msgstr "Помилка при оновленні зовнішнього ауди
msgid "AuditStreams|Cancel editing"
msgstr "СкаÑувати редагуваннÑ"
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5662,6 +5729,9 @@ msgstr "Заголовок"
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr "Видалити Ñпеціальний заголовок"
+
msgid "AuditStreams|Save external stream destination"
msgstr "Зберегти Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½ÑŒÐ¾Ð³Ð¾ потоку"
@@ -5671,9 +5741,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -5987,13 +6054,13 @@ msgid "BackgroundMigrations|Database"
msgstr "База даних"
msgid "BackgroundMigrations|Failed jobs:"
-msgstr ""
+msgstr "Ðевдалі завданнÑ:"
msgid "BackgroundMigrations|Finished at"
-msgstr ""
+msgstr "Завершено о"
msgid "BackgroundMigrations|Started at"
-msgstr ""
+msgstr "Розпочато о"
msgid "Badges"
msgstr "Значки"
@@ -6472,12 +6539,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr "ДоÑтупні Runner'и будуть ввімкнені піÑÐ»Ñ Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð´Ñ–Ð¹Ñної кредитної картки."
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr "Ð”Ð»Ñ Ð²Ð¸ÐºÐ¾Ñ€Ð¸ÑÑ‚Ð°Ð½Ð½Ñ Ð²Ñ–Ð»ÑŒÐ½Ð¸Ñ… хвилин CI/CD на Ñпільних runner'ах, вам необхідно підтвердити ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð·Ð° допомогою кредитної картки. Якщо ви не бажаєте Ñ—Ñ— надавати, ви можете запуÑкати конвеєри, додаючи ваші влаÑні runner'и та вимикаючи Ñпільні runner'и Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ проєкту. Це необхідно Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð±Ñ–Ð³Ð°Ð½Ð½Ñ Ñ‚Ð° Ð·Ð¼ÐµÐ½ÑˆÐµÐ½Ð½Ñ Ð·Ð»Ð¾Ð²Ð¶Ð¸Ð²Ð°Ð½Ð½Ñ Ñ–Ð½Ñ„Ñ€Ð°Ñтруктурою GitLab. %{strongStart} Gitlab не буде ÑÑ‚Ñгувати кошти з вашої картки, вона необхідна лише Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу. %{strongEnd}%{linkStart}ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ%{linkEnd}."
@@ -6493,12 +6554,6 @@ msgstr "Підтвердити обліковий запиÑ"
msgid "Billings|Validate user account"
msgstr "Перевірити обліковий Ð·Ð°Ð¿Ð¸Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr "Тепер ви зможете ÑкориÑтатиÑÑ Ð²Ñ–Ð»ÑŒÐ½Ð¸Ð¼Ð¸ хвилинами CI/CD на Ñпільних runner'ах."
@@ -6550,9 +6605,6 @@ msgstr "ПереглÑнути вÑÑ– плани"
msgid "Billing|Export list"
msgstr "ЕкÑпортувати ÑпиÑок"
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr "З 19 Ð¶Ð¾Ð²Ñ‚Ð½Ñ 2022 року безкоштовні групи будуть обмежені до 5 учаÑників"
-
msgid "Billing|Group invite"
msgstr "Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð² групу"
@@ -6596,9 +6648,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr "Ви збираєтеÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ кориÑтувача %{username} з вашої підпиÑки. Якщо ви продовжите, кориÑтувача буде видалено з групи %{namespace} та вÑÑ–Ñ… Ñ—Ñ— підгруп та проєктів. Цю дію не можна буде відмінити."
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6636,6 +6685,9 @@ msgstr "Заблокована задача"
msgid "Blocking"
msgstr "БлокуваннÑ"
+msgid "Blocking epics"
+msgstr "Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ ÐµÐ¿Ñ–ÐºÑ–Ð²"
+
msgid "Blocking issues"
msgstr "Заблоковані задачі"
@@ -6848,6 +6900,15 @@ msgstr "Розгорнути"
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr "Помилка при отриманні Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ %{issuableType}s"
+msgid "Boards|Move card"
+msgstr "ПереміÑтити картку"
+
+msgid "Boards|Move to end of list"
+msgstr "ПереміÑтити до ÐºÑ–Ð½Ñ†Ñ ÑпиÑку"
+
+msgid "Boards|Move to start of list"
+msgstr "ПереміÑтити на початок ÑпиÑку"
+
msgid "Boards|New board"
msgstr "Ðова дошка"
@@ -6903,7 +6964,7 @@ msgid "Bold text"
msgstr "Жирний текÑÑ‚"
msgid "Both SSH and HTTP(S)"
-msgstr ""
+msgstr "SSH Ñ– HTTP(S)"
msgid "Both project and dashboard_path are required"
msgstr ""
@@ -6942,13 +7003,13 @@ msgid "BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or productio
msgstr "ПідтримуютьÑÑ%{linkStart}шаблони%{linkEnd}, такі Ñк *-stable або production/*."
msgid "BranchRules|Allow all users with push access to %{linkStart}force push%{linkEnd}."
-msgstr ""
+msgstr "Дозволити вÑім кориÑтувачам з push-доÑтупом %{linkStart}примуÑово push%{linkEnd}."
msgid "BranchRules|Allowed to merge"
msgstr "Дозволено злиттÑ"
msgid "BranchRules|Allowed to push"
-msgstr ""
+msgstr "Дозволено здійÑнити push"
msgid "BranchRules|An error occurred while fetching branches."
msgstr "Під Ñ‡Ð°Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð³Ñ–Ð»Ð¾Ðº ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°."
@@ -6960,7 +7021,7 @@ msgid "BranchRules|Create wildcard: %{searchTerm}"
msgstr "Створити шаблон: %{searchTerm}"
msgid "BranchRules|Keep stable branches secure and force developers to use merge requests. %{linkStart}What are protected branches?%{linkEnd}"
-msgstr ""
+msgstr "Захищайте Ñтабільні гілки та змушуйте розробників викориÑтовувати запити на злиттÑ. %{linkStart}Що таке захищені гілки?%{linkEnd}"
msgid "BranchRules|No matching results"
msgstr "Ðемає відповідних результатів"
@@ -6974,6 +7035,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr "Вимагати Ð·Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ влаÑників коду."
+msgid "BranchRules|default"
+msgstr "за умовчаннÑм"
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "Гілки"
@@ -7139,6 +7206,9 @@ msgstr "ПереглÑд файлів"
msgid "Browse templates"
msgstr "ОглÑд шаблонів"
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr "Помилка при отриманні артефактів"
@@ -7269,7 +7339,7 @@ msgid "BulkImport|must be a group"
msgstr "повинен бути групою"
msgid "Bullet list"
-msgstr ""
+msgstr "Маркований ÑпиÑок"
msgid "Burndown chart"
msgstr "Графік виконаннÑ"
@@ -7395,33 +7465,21 @@ msgstr "СтатиÑтика релізів"
msgid "CICDAnalytics|Releases"
msgstr "Релізів"
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ Ñпільних runner'ів"
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ Ñпільних runner'ів"
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ Ñпільних runner'ів - це загальний Ñ‡Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð²ÑÑ–Ñ… завдань, що виконувалиÑÑŒ на Ñпільних runner'ах"
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
msgid "CICDAnalytics|Time to restore service"
-msgstr ""
+msgstr "Ð§Ð°Ñ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÑервіÑу"
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-msgstr "Що таке викориÑÑ‚Ð°Ð½Ð½Ñ Ñпільних runner'ів?"
-
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 "Додайти %{base_domain_link_start}базовий домен%{link_end} у Ñвій %{kubernetes_cluster_link_start} клаÑтер Kubernetes%{link_end}, щоб ваша ÑÑ‚Ñ€Ð°Ñ‚ÐµÐ³Ñ–Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð¿Ñ€Ð°Ñ†ÑŽÐ²Ð°Ð»Ð°."
@@ -7741,7 +7799,7 @@ msgid "Card number:"
msgstr "Ðомер картки:"
msgid "CascadingSettings|Enforce deletion protection for all subgroups"
-msgstr ""
+msgstr "ЗаÑтоÑувати захиÑÑ‚ від Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð²ÑÑ–Ñ… підгруп"
msgid "CascadingSettings|Enforce for all subgroups"
msgstr ""
@@ -8166,7 +8224,7 @@ msgid "Checkout|Group"
msgstr "Група"
msgid "Checkout|Must be %{minimumNumberOfUsers} (your seats in use) or more."
-msgstr ""
+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 ""
@@ -8662,7 +8720,7 @@ msgid "Closed MRs"
msgstr "Закриті запити"
msgid "Closed date"
-msgstr ""
+msgstr "Дата закриттÑ"
msgid "Closed issues"
msgstr "Закриті задачі"
@@ -8679,11 +8737,17 @@ msgstr "Закриває %{quick_action_target}."
msgid "Cloud Run"
msgstr "Cloud Run"
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr "Хмарне Ñховище даних"
msgid "CloudSeed|All"
-msgstr ""
+msgstr "Ð’ÑÑ–"
msgid "CloudSeed|AlloyDB for Postgres"
msgstr ""
@@ -8706,12 +8770,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr "ÐалаштуваннÑ"
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr "Створити клаÑтер"
@@ -8746,7 +8822,7 @@ msgid "CloudSeed|Enhance security by storing database variables in secret manage
msgstr ""
msgid "CloudSeed|Environment"
-msgstr ""
+msgstr "Середовище"
msgid "CloudSeed|Flexible, scalable NoSQL cloud database for client- and server-side development"
msgstr ""
@@ -8766,6 +8842,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -8791,7 +8870,7 @@ msgid "CloudSeed|No instances"
msgstr ""
msgid "CloudSeed|Refs"
-msgstr ""
+msgstr "ПоÑиланнÑ"
msgid "CloudSeed|Regions"
msgstr "Регіони"
@@ -9454,10 +9533,10 @@ msgstr "Сертифікат Kubernetes, що викориÑтовуєтьÑÑ Ð
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr "URL-адреÑа, що викориÑтовуєтьÑÑ Ð´Ð»Ñ Ð´Ð¾Ñтупу до Kubernetes API."
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9628,6 +9707,9 @@ msgstr "Згорнути задачі"
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr "Приховати деталі злиттÑ"
+
msgid "Collapse milestones"
msgstr "Згорнути етапи"
@@ -9653,7 +9735,7 @@ msgid "ColorWidget|An error occurred while updating color."
msgstr "Під Ñ‡Ð°Ñ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð»ÑŒÐ¾Ñ€Ñƒ ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°."
msgid "ColorWidget|Assign epic color"
-msgstr ""
+msgstr "Призначити колір епіку"
msgid "ColorWidget|Color"
msgstr "Колір"
@@ -9694,6 +9776,9 @@ msgstr "Коментувати та повторно відкрити обгов
msgid "Comment '%{label}' position"
msgstr "Коментувати позицію \"%{label}\""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr "Ð Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð¾Ñ€Ð¼Ð¸ коментарÑ"
@@ -9818,6 +9903,9 @@ msgstr "ПорівнÑйте редакції GitLab"
msgid "Compare Revisions"
msgstr "ПорівнÑÐ½Ð½Ñ Ñ€ÐµÐ´Ð°ÐºÑ†Ñ–Ð¹"
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr "ПорівнÑти зміни"
@@ -10399,6 +10487,9 @@ msgstr "ДайджеÑÑ‚: %{imageId}"
msgid "ContainerRegistry|Docker connection error"
msgstr "Помилка Ð·â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Docker"
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr "Увімкнути політику Ð·Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ Ñ‚ÐµÑ€Ð¼Ñ–Ð½Ñƒ дії"
@@ -10505,6 +10596,12 @@ msgstr "Виконати очищеннÑ:"
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10679,8 +10776,8 @@ msgstr "%{created_count} Ñтворено, %{closed_count} закрито."
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr "%{created_count} Ñтворено, %{merged_count} об’єднано, %{closed_count} закрито."
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
-msgstr ""
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
+msgstr "%{pushes}, більш ніж %{commits} від %{contributors}."
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
msgstr "Ðналітика внеÑків Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ñ‡, запитів на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ‚Ð° подій відправки з %{start_date}"
@@ -11282,9 +11379,6 @@ msgstr "Створити групи"
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr "Створіть / імпортуйте Ñвій перший проєкт"
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” дозволу Ñтворювати підгрупу в цій групі."
@@ -11526,7 +11620,7 @@ msgid "Critical vulnerabilities present"
msgstr "ПриÑутні критичні вразливоÑÑ‚Ñ–"
msgid "Crm|Active"
-msgstr ""
+msgstr "Ðктивний"
msgid "Crm|Contact"
msgstr ""
@@ -11828,6 +11922,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr "Середній Ñ‡Ð°Ñ Ð´Ð¾ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ (дні)"
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -12001,10 +12098,10 @@ msgid "DORA4Metrics|The chart displays the median time between a merge request b
msgstr "Графік відображає Ñередній Ñ‡Ð°Ñ Ð¼Ñ–Ð¶ об’єднаннÑм запиту на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ‚Ð° його розгортаннÑм у production Ñередовищі, що базуютьÑÑ Ð½Ð° значенні %{linkStart}deployment_tier%{linkEnd}."
msgid "DORA4Metrics|Time to restore service"
-msgstr ""
+msgstr "Ð§Ð°Ñ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÑервіÑу"
msgid "DORA4Metrics|Time to restore service (median days)"
-msgstr ""
+msgstr "Ð§Ð°Ñ Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÑервіÑу (Ñереднє чиÑло днів)"
msgid "DSN"
msgstr "DSN"
@@ -12060,6 +12157,12 @@ 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-Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ (запити та відповіді), надіÑлані цільовому об’єкту. Ðктивне ÑÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ñ‚Ð°ÐºÑƒÑ” цільовий об’єкт, щоб знайти потенційні вразливоÑÑ‚Ñ–."
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr "AJAX spider"
@@ -12138,15 +12241,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr "Видалити профіль"
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr "Ви хочете відхилити цей профіль Ñканера?"
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr "Ви хочете відхилити цей профіль Ñайту?"
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr "Ви хочете ÑкаÑувати ваші зміни?"
-
msgid "DastProfiles|Edit profile"
msgstr "Редагувати профіль"
@@ -12159,6 +12253,9 @@ msgstr "Редагувати профіль Ñайту"
msgid "DastProfiles|Enable Authentication"
msgstr "Увімкнути автентифікацію"
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr "Увімкнути базову автентифікацію"
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr "Введіть URL-адреÑи у ÑпиÑку, розділений комою."
@@ -12234,6 +12331,9 @@ msgstr "Пароль"
msgid "DastProfiles|Password form field"
msgstr "Поле форми паролÑ"
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -12261,16 +12361,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr "Режим ÑкануваннÑ"
-msgid "DastProfiles|Scanner Profile"
-msgstr "Профіль Ñканера"
-
-msgid "DastProfiles|Scanner Profiles"
-msgstr "Сканувальні профілі"
-
msgid "DastProfiles|Scanner name"
msgstr "Ім'Ñ Ñканера"
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profile"
+msgstr ""
+
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -12291,16 +12388,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr "Профіль Ñайту"
-
-msgid "DastProfiles|Site Profiles"
-msgstr "Профілі Ñайту"
-
msgid "DastProfiles|Site name"
msgstr "Ім'Ñ Ñайту"
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profile"
+msgstr ""
+
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12369,14 +12463,14 @@ msgstr "Ви можете вибрати паÑивне ÑÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr "Ви не можете запуÑтити активне ÑÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð½ÐµÐ¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐµÐ½Ð¾Ð³Ð¾ Ñайту."
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
-msgstr ""
+msgid "DastProfiles|https://example.com/dast_example.har"
+msgstr "https://example.com/dast_example.har"
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
-msgstr ""
+msgid "DastProfiles|https://example.com/openapi.json"
+msgstr "https://example.com/openapi.json"
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
-msgstr ""
+msgid "DastProfiles|https://example.com/postman_collection.json"
+msgstr "https://example.com/postman_collection.json"
msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr "Скопіюйте заголовок HTTP в буфер обміну"
@@ -12400,7 +12494,7 @@ msgid "DastSiteValidation|Meta tag validation"
msgstr "Перевірка мета-тегу"
msgid "DastSiteValidation|Not validated"
-msgstr ""
+msgstr "Ðе перевірено"
msgid "DastSiteValidation|Retry validation"
msgstr "Повторити перевірку"
@@ -12565,7 +12659,7 @@ msgstr "Днів"
msgid "Days to merge"
msgstr "Днів до злиттÑ"
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12751,6 +12845,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr "Видалити ключ Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ"
+msgid "Delete epic"
+msgstr "Видалити епік"
+
msgid "Delete file"
msgstr "Видалити файл"
@@ -12764,7 +12861,7 @@ msgid "Delete image repository"
msgstr "Видалити репозиторій образів"
msgid "Delete internal note"
-msgstr ""
+msgstr "Видалити внутрішню нотатку"
msgid "Delete label"
msgstr "Видалити мітку"
@@ -12802,6 +12899,9 @@ msgstr "Видалити Ñніпет?"
msgid "Delete source branch"
msgstr "Видалити гілку-джерело"
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr "Видалити підпиÑку"
@@ -12872,7 +12972,7 @@ msgid "Deleted projects cannot be restored!"
msgstr "Видалені проєкти неможливо відновити!"
msgid "Deleted the source branch."
-msgstr ""
+msgstr "Видалено гілку джерела."
msgid "Deletes the source branch"
msgstr "ВидалÑÑ” гілку-джерело"
@@ -13372,6 +13472,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr "Відхилено %{time}"
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr "Відхилено вами %{time}"
+
msgid "DeploymentTarget|GitLab Pages"
msgstr "GitLab Pages"
@@ -13379,10 +13485,10 @@ msgid "DeploymentTarget|Heroku"
msgstr "Heroku"
msgid "DeploymentTarget|Infrastructure provider (Terraform, Cloudformation, and so on)"
-msgstr ""
+msgstr "ПоÑтачальник інфраÑтруктури (Terraform, Cloudformation тощо)"
msgid "DeploymentTarget|Kubernetes (GKE, EKS, OpenShift, and so on)"
-msgstr ""
+msgstr "Kubernetes (GKE, EKS, OpenShift і т. д.)"
msgid "DeploymentTarget|Managed container runtime (Fargate, Cloud Run, DigitalOcean App)"
msgstr ""
@@ -13437,7 +13543,7 @@ msgid "Deployment|Latest Deployed"
msgstr "ОÑтанній розгорнутий"
msgid "Deployment|Needs Approval"
-msgstr ""
+msgstr "Ðеобхідне затвердженнÑ"
msgid "Deployment|Running"
msgstr "ВиконуєтьÑÑ"
@@ -13605,6 +13711,9 @@ msgstr "Дизайни"
msgid "DesignManagement|Discard comment"
msgstr "Відхилити коментар"
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13698,6 +13807,9 @@ msgstr "Звіти DevOps"
msgid "DevOps adoption"
msgstr "ÐŸÑ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ DevOps"
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr "ПриÑтрої (необов’Ñзково)"
@@ -13954,10 +14066,10 @@ msgstr ""
msgid "Diffs|Showing %{dropdownStart}%{count} changed file%{dropdownEnd}"
msgid_plural "Diffs|Showing %{dropdownStart}%{count} changed files%{dropdownEnd}"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "Показано %{dropdownStart}%{count} змінений файл%{dropdownEnd}"
+msgstr[1] "Показано %{dropdownStart}%{count} зміненого файла%{dropdownEnd}"
+msgstr[2] "Показано %{dropdownStart}%{count} змінених файлів%{dropdownEnd}"
+msgstr[3] "Показано %{dropdownStart}%{count} змінених файлів%{dropdownEnd}"
msgid "Diffs|Something went wrong while fetching diff lines."
msgstr "Проблема при отриманні Ñ€Ñдків відмінноÑтей."
@@ -14266,6 +14378,9 @@ msgstr "Чернетка"
msgid "Draft: %{filename}"
msgstr "Чернетка: %{filename}"
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr "ПеретÑгніть, щоб змінити порÑдок пріоритетних міток Ñ– змінити Ñ—Ñ… відноÑний пріоритет."
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr "ПеретÑгніть Ñвої проєкти Ñюди або %{linkStart}, щоб завантажити%{linkEnd}."
@@ -14287,6 +14402,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr "Призначити %{issuableAttribute}"
+msgid "DropdownWidget|Cancel"
+msgstr "СкаÑувати"
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr "Ðе вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ %{issuableAttribute} Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ %{issuableType}. Будь лаÑка, Ñпробуйте ще раз."
@@ -14302,8 +14423,14 @@ msgstr "Ðе знайдено %{issuableAttribute}"
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
-msgstr "Дата виконаннÑ"
+msgstr "Дату завершеннÑ"
msgid "Due date"
msgstr "Запланована дата завершеннÑ"
@@ -14384,7 +14511,7 @@ msgid "Edit %{name}"
msgstr "Редагувати %{name}"
msgid "Edit %{profileType} profile"
-msgstr ""
+msgstr "Редагувати профіль %{profileType}"
msgid "Edit Comment"
msgstr "Редагувати коментар"
@@ -14524,6 +14651,9 @@ msgstr "Редагувати wiki-Ñторінку"
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr "Редагувати ваш оÑтанній коментар в обговоренні (в порожньому текÑтовому полі)"
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14539,9 +14669,6 @@ msgstr "РедагуваннÑ"
msgid "Elapsed time"
msgstr "Витрачений чаÑ"
-msgid "Elasticsearch AWS IAM credentials"
-msgstr "Параметри доÑтупу IAM Ð´Ð»Ñ Elasticsearch AWS"
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14566,9 +14693,6 @@ msgstr "ПереіндекÑÐ°Ñ†Ñ–Ñ Elasticsearch не була розпочаÑ
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr "Ðемає. Виберіть проÑтори імен Ð´Ð»Ñ Ñ–Ð½Ð´ÐµÐºÑуваннÑ."
@@ -14716,6 +14840,9 @@ msgstr "Увімкнути Auto DevOps"
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr "Увімкнути Gitpod"
@@ -14800,9 +14927,6 @@ msgstr "Увімкнути групові Runner'и"
msgid "Enable header and footer in emails"
msgstr "Увімкнути заголовок та футер в електронних лиÑтах"
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -15176,7 +15300,7 @@ msgid "Environments|Open live environment"
msgstr "Відкрити працююче Ñередовище"
msgid "Environments|Re-deploy environment"
-msgstr ""
+msgstr "Повторно розгорнути Ñередовище"
msgid "Environments|Re-deploy environment %{name}?"
msgstr "Повторно розгорнути в Ñередовищі %{name}?"
@@ -15244,6 +15368,9 @@ msgstr "Епік"
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr "Дії епіку"
+
msgid "Epic cannot be found."
msgstr "Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ епік."
@@ -15280,12 +15407,6 @@ msgstr "Додати новий епік"
msgid "Epics|Add an existing epic"
msgstr "Додати Ñ–Ñнуючий епік"
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при збереженні дати %{epicDateType}"
-
-msgid "Epics|An error occurred while updating labels."
-msgstr "Помилка під Ñ‡Ð°Ñ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¼Ñ–Ñ‚Ð¾Ðº."
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr "Ви впевнені, що хочете видалити %{bStart}%{targetIssueTitle}%{bEnd} із %{bStart}%{parentEpicTitle}%{bEnd}?"
@@ -15352,18 +15473,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr "Це також видалить уÑÑ– підлеглі елементи %{bStart}%{targetEpicTitle}%{bEnd} з %{bStart}%{parentEpicTitle}%{bEnd}. Ви впевнені?"
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr "Щоб запланувати %{epicDateType} дату вашого епіку на оÑнові етапів, призначте етап з %{epicDateType} датою Ð´Ð»Ñ Ð±ÑƒÐ´ÑŒ-Ñкої задачі в цьому епіку."
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr "закінченнÑ"
-
-msgid "Epics|start"
-msgstr "початок"
-
msgid "Erased"
msgstr ""
@@ -15400,6 +15512,9 @@ msgstr "Помилка ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ вразливоÑÑ‚Ñ–: %
msgid "Error deleting project. Check logs for error details."
msgstr "Помилка при видаленні проєкту. Перевірте журнали Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ€Ð¾Ð±Ð¸Ñ†ÑŒ про помилку."
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15433,9 +15548,6 @@ msgstr "Помилка при завантаженні гілок."
msgid "Error loading burndown chart data"
msgstr "Помилка при завантаженні даних графіка виконаннÑ"
-msgid "Error loading countries data."
-msgstr "Помилка при завантаженні даних про країни."
-
msgid "Error loading file viewer."
msgstr "Помилка при Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñдача файлів."
@@ -15511,6 +15623,9 @@ msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°. КориÑтувач не був розб
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr "Помилка при попередньому переглÑді Markdown"
@@ -15944,6 +16059,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr "Показати деталі злиттÑ"
+
msgid "Expand milestones"
msgstr ""
@@ -16632,7 +16750,7 @@ msgid "FeatureFlags|Percent of users"
msgstr "ВідÑоток кориÑтувачів"
msgid "FeatureFlags|Percent rollout"
-msgstr ""
+msgstr "ВідÑоток розгортаннÑ"
msgid "FeatureFlags|Percent rollout must be an integer number between 0 and 100"
msgstr "ВідÑоток Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð¼Ð°Ñ” бути цілим чиÑлом від 0 до 100"
@@ -16700,9 +16818,6 @@ msgstr "лют."
msgid "February"
msgstr "лютий"
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16878,7 +16993,7 @@ msgid "Finish editing this message first!"
msgstr "Спочатку завершіть Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ повідомленнÑ!"
msgid "Finish review"
-msgstr ""
+msgstr "Завершити перевірку"
msgid "Finish setting up your dedicated account for %{group_name}."
msgstr ""
@@ -16913,13 +17028,13 @@ msgstr "Виправлено:"
msgid "Flags"
msgstr "Прапори"
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
-msgstr "Увімкнути FloC (Федеративне Ð½Ð°Ð²Ñ‡Ð°Ð½Ð½Ñ Ð½Ð° оÑнові когорт)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
+msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -17069,6 +17184,9 @@ msgstr "Публічний"
msgid "ForkProject|Select a namespace"
msgstr "Виберіть проÑÑ‚Ñ–Ñ€ імен"
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr "Проєкт може бути доÑтупний будь-Ñким зареєÑтрованим кориÑтувачем."
@@ -17139,6 +17257,9 @@ msgstr "ЧаÑтота"
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr "П'ÑÑ‚"
+
msgid "Friday"
msgstr "П'ÑтницÑ"
@@ -17151,19 +17272,12 @@ msgstr "З %{code_open}%{source_title}%{code_close} в"
msgid "From %{providerTitle}"
msgstr "З %{providerTitle}"
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+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"
@@ -17223,7 +17337,7 @@ msgid "Generate site and private keys at"
msgstr ""
msgid "Generated with JSON data"
-msgstr ""
+msgstr "Згенеровано за допомогою даних JSON"
msgid "Generic"
msgstr ""
@@ -17308,9 +17422,6 @@ msgstr "Додати новий Ñайт"
msgid "Geo|Add site"
msgstr "Додати Ñайт"
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr "Ð’ÑÑ–"
@@ -17506,6 +17617,12 @@ msgstr "Ðіколи"
msgid "Geo|Next sync scheduled at"
msgstr "ÐаÑтупна ÑÐ¸Ð½Ñ…Ñ€Ð¾Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ Ð·Ð°Ð¿Ð»Ð°Ð½Ð¾Ð²Ð°Ð½Ð° на"
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17713,6 +17830,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr "Ðемає %{replicable} Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ"
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17875,6 +17995,9 @@ msgstr "Триває перенеÑÐµÐ½Ð½Ñ Git"
msgid "Git version"
msgstr "Git-верÑÑ–Ñ"
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr "Виключені кориÑтувачі"
@@ -18244,6 +18367,9 @@ msgstr "Глобальний пошук Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— облаÑÑ‚Ñ– вимкÐ
msgid "Global Shortcuts"
msgstr "Глобальні комбінації клавіш"
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr "Загальні Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñповіщень"
@@ -18256,6 +18382,12 @@ msgstr "%{count} надано результати за замовчуваннÑ
msgid "GlobalSearch|Groups"
msgstr "Групи"
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr "Створені мною задачі"
@@ -18274,6 +18406,15 @@ msgstr "Запити на злиттÑ, Ñкі Ñ Ð¾Ð³Ð»Ñдаю"
msgid "GlobalSearch|Projects"
msgstr "Проєкти"
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -18286,6 +18427,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr "Результати пошуку завантажуютьÑÑ"
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -18308,11 +18452,17 @@ msgid "GlobalSearch|group"
msgstr "група"
msgid "GlobalSearch|in %{scope}"
-msgstr ""
+msgstr "в %{scope}"
msgid "GlobalSearch|project"
msgstr "проєкт"
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18583,9 +18733,6 @@ msgstr "Gravatar увімкнено"
msgid "Group"
msgstr "Група"
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -19021,9 +19168,6 @@ msgstr "ЗаÑтоÑовуєтьÑÑ Ð´Ð¾ вÑÑ–Ñ… підгруп, Ñкщо не
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr "Конвеєр Auto DevOps оновлено Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— групи"
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -19051,6 +19195,9 @@ msgstr "Фреймворки відповідноÑÑ‚Ñ–"
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr "Ðалаштуйте рамки відповідноÑÑ‚Ñ–, щоб зробити Ñ—Ñ… доÑтупними Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ñ–Ð² цієї групи. %{linkStart}ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ.%{linkEnd}"
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr "ВлаÑні шаблони проєктів"
@@ -19414,14 +19561,17 @@ msgstr "Керівництво"
msgid "HAR (HTTP Archive)"
msgstr "HAR (архів HTTP)"
+msgid "HAR file URL"
+msgstr "URL-адреÑа файлу HAR"
+
msgid "HAR file path or URL"
msgstr "ШлÑÑ… до файлу HAR або URL-адреÑа"
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
-msgstr "Базовий HTTP: відмовлено у доÑтупі\\nВи повинні викориÑтовувати перÑональний токен доÑтупу із облаÑÑ‚ÑŽ дії 'api' Ð´Ð»Ñ Git через HTTP.\\nВи можете згенерувати його в %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
+msgstr ""
msgid "Harbor Registry"
msgstr ""
@@ -19459,22 +19609,22 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
-msgstr ""
+msgid "HarborRegistry|-- artifacts"
+msgstr "- артефакти"
msgid "HarborRegistry|Digest: %{imageId}"
msgstr ""
@@ -19485,43 +19635,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
-msgstr ""
-
-msgid "HarborRegistry|Manifest digest: %{digest}"
-msgstr ""
-
msgid "HarborRegistry|Please try different search criteria"
msgstr ""
msgid "HarborRegistry|Published %{timeInfo}"
msgstr "Опубліковано %{timeInfo}"
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Sorry, your filter produced no results."
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|Tag"
+msgstr "Тег"
+
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19530,6 +19674,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19678,6 +19825,9 @@ msgstr[1] "Сховати графіка"
msgstr[2] "Сховати графіки"
msgstr[3] "Сховати графіки"
+msgid "Hide comments"
+msgstr "Приховати коментарі"
+
msgid "Hide comments on this file"
msgstr ""
@@ -19811,9 +19961,6 @@ msgstr "ÐžÑ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ ÑƒÑпішно розпочато"
msgid "How do I configure Akismet?"
msgstr "Як налаштувати Akismet?"
-msgid "How do I configure runners?"
-msgstr "Як налаштувати runner'и?"
-
msgid "How do I configure this integration?"
msgstr "Як налаштувати цю інтеграцію?"
@@ -19847,6 +19994,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr "Я приймаю %{terms_link}"
@@ -19970,9 +20120,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr "Створити проєкт"
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19985,15 +20141,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -20015,6 +20192,9 @@ msgstr "Підтвердити Ñвою оÑобу"
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -20084,9 +20264,6 @@ msgstr "При викориÑтанні GitHub, ви побачите Ñтату
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -20351,9 +20528,6 @@ msgstr "За цією URL-адреÑою немає дійÑного репозÐ
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20937,7 +21111,7 @@ msgid "IncidentManagement|Achieved SLA"
msgstr "Укладена угода про рівень поÑлуг (SLA)"
msgid "IncidentManagement|Acknowledged"
-msgstr ""
+msgstr "Підтверджено"
msgid "IncidentManagement|All"
msgstr "Ð’ÑÑ–"
@@ -21143,6 +21317,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr "Метрики"
@@ -21161,6 +21338,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr "ПідÑумок"
@@ -21194,8 +21374,8 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
-msgstr "Включає об'єкти LFS. Може бути перевизначено в групі або проєкті. 0 — необмежено."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
+msgstr ""
msgid "Includes an MVC structure to help you get started"
msgstr "Включає Ñтруктуру MVC, щоб допомогти вам розпочати роботу"
@@ -21335,9 +21515,15 @@ msgstr "Ð’Ñтавити Ñ€Ñдок перед"
msgid "Insert suggestion"
msgstr "Додати пропозицію"
+msgid "Insert table"
+msgstr "Ð’Ñтавити таблицю"
+
msgid "Insights"
msgstr "СтатиÑтика (Insights)"
+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)."
msgstr ""
@@ -21565,7 +21751,7 @@ msgid "Integrations|No linked namespaces"
msgstr ""
msgid "Integrations|Notification settings"
-msgstr ""
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñповіщень"
msgid "Integrations|Projects using custom settings"
msgstr ""
@@ -21609,6 +21795,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr "Увійти до GitLab"
@@ -21688,7 +21877,7 @@ msgid "Internal - The group and any internal projects can be viewed by any logge
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 "Під Ñ‡Ð°Ñ Ð´Ð¾Ñтавки цього вебхука ÑталаÑÑ Ð²Ð½ÑƒÑ‚Ñ€Ñ–ÑˆÐ½Ñ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°."
@@ -21921,6 +22110,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr "УчаÑники були уÑпішно додані"
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr "Будь лаÑка, виберіть учаÑників або введіть адреÑи електронної пошти Ð´Ð»Ñ Ð·Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ"
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21936,6 +22128,12 @@ msgstr "Вибрати роль"
msgid "InviteMembersModal|Select members or type email addresses"
msgstr "Виберіть учаÑників або введіть адреÑи електронної пошти"
+msgid "InviteMembersModal|Show less"
+msgstr "Показати менше"
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr "ЩоÑÑŒ пішло не так"
@@ -21955,9 +22153,6 @@ msgstr "Ð”Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб призначити задачі новому Ñ
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21982,9 +22177,6 @@ msgstr "Ви запрошуєте учаÑників до проєкту %{stron
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr "ЗапроÑити групу"
@@ -22102,12 +22294,18 @@ msgstr "ВикориÑтовує міÑце в ліцензії:"
msgid "Is using seat"
msgstr "ВикориÑтовує міÑце"
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr "Закрито"
msgid "IssuableStatus|Closed (%{link})"
msgstr "Закрито (%{link})"
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr "Ñтворено %{created_at}"
+
msgid "IssuableStatus|duplicated"
msgstr "продубльовано"
@@ -22426,9 +22624,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr "СкаÑувати"
@@ -22474,6 +22669,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr "Увімкнути автоматичне плануваннÑ"
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22489,12 +22687,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr "Ітерації починаютьÑÑ Ð·Ð° розкладом по %{weekday}"
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22552,9 +22744,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr "Ð†Ñ‚ÐµÑ€Ð°Ñ†Ñ–Ñ Ð±ÑƒÐ»Ð° видалена."
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22564,9 +22753,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr "Заголовок"
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22579,9 +22765,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22876,9 +23059,6 @@ msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ ÑƒÑпішно видалено!"
msgid "Job has wrong arguments format."
msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð¼Ð°Ñ” неправильний формат аргументів."
-msgid "Job is missing the `model_type` argument."
-msgstr "У завданні відÑутній аргумент \"model_type\"."
-
msgid "Job is stuck. Check runners."
msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ðµ. Перевірте runner'и."
@@ -22943,7 +23123,7 @@ msgid "Jobs|There was a problem fetching the failed jobs."
msgstr ""
msgid "Jobs|Use jobs to automate your tasks"
-msgstr ""
+msgstr "ВикориÑтовуйте Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ— ваших завдань"
msgid "Jobs|You're about to retry a job that failed because it attempted to deploy code that is older than the latest deployment. Retrying this job could result in overwriting the environment with the older source code."
msgstr ""
@@ -22952,10 +23132,10 @@ msgid "Job|%{boldStart}Pipeline%{boldEnd} %{id}"
msgstr ""
msgid "Job|%{boldStart}Pipeline%{boldEnd} %{id} for %{mrId} with %{source}"
-msgstr ""
+msgstr "ЗавданнÑ%{boldStart}Конвеєр%{boldEnd} %{id} Ð´Ð»Ñ %{mrId} з %{source}"
msgid "Job|%{boldStart}Pipeline%{boldEnd} %{id} for %{mrId} with %{source} into %{target}"
-msgstr ""
+msgstr "ЗавданнÑ%{boldStart}Конвеєр%{boldEnd} %{id} Ð´Ð»Ñ %{mrId} з %{source} на %{target}"
msgid "Job|%{boldStart}Pipeline%{boldEnd} %{id} for %{ref}"
msgstr ""
@@ -23315,8 +23495,8 @@ 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."
-msgstr "Мітки можуть бути заÑтоÑовані до задач та запитів на злиттÑ."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
+msgstr "Мітки можуть бути заÑтоÑовані до задач та запитів на злиттÑ. Позначте мітку, щоб зробити Ñ—Ñ— пріоритетною."
msgid "Labels with no issues in this iteration:"
msgstr "Мітки без задач у цій ітерації:"
@@ -23330,6 +23510,9 @@ msgstr "ПеренеÑти мітку"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr "Виконавець"
+
msgid "Language"
msgstr "Мова"
@@ -23502,9 +23685,6 @@ msgstr "Ð§Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ"
msgid "Learn GitLab"
msgstr "ДізнатиÑÑ Ð¿Ñ€Ð¾ GitLab"
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr "ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ"
@@ -23556,6 +23736,9 @@ msgstr "Докладніше про шаблони проєктів на рівÐ
msgid "Learn more about groups."
msgstr "ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про групи."
+msgid "Learn more about issues."
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про задачі."
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23587,7 +23770,7 @@ msgid "LearnGitLab|Complete these tasks first so you can enjoy GitLab's features
msgstr "Спочатку завершіть ці завданнÑ, аби наÑолоджуватиÑÑ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñми GitLab у повній мірі:"
msgid "LearnGitLab|Create a repository"
-msgstr ""
+msgstr "Створити репозиторій"
msgid "LearnGitLab|Create a workflow for your new workspace, and learn how GitLab features work together:"
msgstr ""
@@ -24004,7 +24187,7 @@ msgid "Linked epics"
msgstr ""
msgid "Linked incidents or issues"
-msgstr ""
+msgstr "Пов'Ñзані інциденти або задачі"
msgid "Linked items"
msgstr "Пов'Ñзані елементи"
@@ -24022,7 +24205,7 @@ msgid "LinkedResources|Add"
msgstr "Додати"
msgid "LinkedResources|Add a resource link"
-msgstr ""
+msgstr "Додати поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° реÑурÑ"
msgid "LinkedResources|Cancel"
msgstr "СкаÑувати"
@@ -24040,7 +24223,7 @@ msgid "LinkedResources|Link"
msgstr "ПоÑиланнÑ"
msgid "LinkedResources|Linked resources"
-msgstr ""
+msgstr "Пов'Ñзані реÑурÑи"
msgid "LinkedResources|Remove"
msgstr "Видалити"
@@ -24300,6 +24483,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr "Головне меню"
+
msgid "Maintenance mode"
msgstr "Режим обÑлуговуваннÑ"
@@ -24588,10 +24774,7 @@ msgstr "МакÑимальна кількіÑÑ‚ÑŒ кориÑтувачів"
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr "МакÑимально допуÑтимий термін дії токена перÑонального доÑтупу (днів)"
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -25079,6 +25262,9 @@ msgstr ""
msgid "Merge requests"
msgstr "Запити на злиттÑ"
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ â€” це ÑпоÑіб запропонувати Ñвої зміни до проєкту Ñ– обговорити Ñ—Ñ… із іншими"
@@ -25166,6 +25352,9 @@ msgstr "Виникла помилка під Ñ‡Ð°Ñ Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ñ‡ÐµÑ€Ð
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr "ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ñкопійовано"
+
msgid "MergeRequests|Saving the comment failed"
msgstr "Помилка при збереженні коментарÑ"
@@ -25241,6 +25430,30 @@ msgstr "Файлів не знайдено"
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr "%{sourceTopic} буде видалено"
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr "Об’єднати теми"
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr "ÐžÐ±â€™Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ñ‚ÐµÐ¼ може викликати наÑтупне:"
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr "Вихідна тема"
+
+msgid "MergeTopics|Target topic"
+msgstr "Цільова тема"
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr "Цю дію не можна ÑкаÑувати."
+
msgid "Merged"
msgstr "Злито"
@@ -25691,7 +25904,7 @@ msgid "MilestoneCombobox|No milestone"
msgstr "Ðемає етапу"
msgid "MilestoneCombobox|Project milestones"
-msgstr ""
+msgstr "Етап проєкту"
msgid "MilestoneCombobox|Search Milestones"
msgstr ""
@@ -25754,7 +25967,7 @@ msgid "MilestoneSidebar|Until"
msgstr "До"
msgid "MilestoneSidebar|complete"
-msgstr ""
+msgstr "завершено"
msgid "Milestones"
msgstr "Етапи"
@@ -25784,7 +25997,7 @@ msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
msgstr "Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ етап %{milestoneTitle}"
msgid "Milestones|Group Milestone"
-msgstr ""
+msgstr "Етап групи"
msgid "Milestones|Milestone %{milestoneTitle} was not found"
msgstr "Етап %{milestoneTitle} не знайдено"
@@ -25796,7 +26009,7 @@ msgid "Milestones|Organize issues and merge requests into a cohesive group, and
msgstr ""
msgid "Milestones|Project Milestone"
-msgstr ""
+msgstr "Етап проєкту"
msgid "Milestones|Promote %{milestoneTitle} to group milestone?"
msgstr "ПеренеÑти %{milestoneTitle} на рівень групи?"
@@ -25805,7 +26018,7 @@ msgid "Milestones|Promote Milestone"
msgstr "ПеренеÑти етап"
msgid "Milestones|Promote to Group Milestone"
-msgstr ""
+msgstr "Підвищити до етапу групи"
msgid "Milestones|Promoting %{milestoneTitle} will make it available for all projects inside %{groupName}. Existing project milestones with the same title will be merged."
msgstr "ПеренеÑÐµÐ½Ð½Ñ %{milestoneTitle} на рівень групи зробить його доÑтупним Ð´Ð»Ñ Ð²ÑÑ–Ñ… проєктів в групі %{groupName}. ІÑнуючі проєктні етапи з такими ж заголовками будуть об'єднані."
@@ -25814,7 +26027,7 @@ msgid "Milestones|Reopen Milestone"
msgstr "Повторно відкрити етап"
msgid "Milestones|There are no closed milestones"
-msgstr ""
+msgstr "Ðемає закритих етапів"
msgid "Milestones|There are no open milestones"
msgstr "Ðемає відкритих етапів"
@@ -25826,7 +26039,7 @@ msgid "Milestones|Unstarted Issues (open and unassigned)"
msgstr "Ðерозпочаті задачі (відкриті та не призначені)"
msgid "Milestones|Use milestones to track issues and merge requests over a fixed period of time"
-msgstr ""
+msgstr "ВикориÑтовуйте етапи Ð´Ð»Ñ Ð²Ñ–Ð´ÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡ та запитів на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿Ñ€Ð¾Ñ‚Ñгом фікÑованого періоду чаÑу"
msgid "Min Value"
msgstr "Мінімальне значеннÑ"
@@ -25921,16 +26134,19 @@ msgstr "Змінити Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð´Ð»Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñ–Ð²"
msgid "Modify merge commit"
msgstr "Змінити коміт-злиттÑ"
+msgid "Mon"
+msgstr "Пон"
+
msgid "Monday"
msgstr "Понеділок"
msgid "Monitor"
msgstr "Моніторинг"
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25945,6 +26161,9 @@ msgstr "МіÑÑць"
msgid "Months"
msgstr "МіÑÑці"
+msgid "More"
+msgstr "Більше"
+
msgid "More Details"
msgstr "Детальніше"
@@ -25987,9 +26206,6 @@ msgstr "Ðайбільш актуальні"
msgid "Most stars"
msgstr "Ðайбільше в обраних"
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr "Точку Ð¼Ð¾Ð½Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ %{mounted_as} не знайдено в %{model_class}."
-
msgid "Move"
msgstr "ПереміÑтити"
@@ -26080,12 +26296,6 @@ msgstr "ПідтримуєтьÑÑ ÐºÑ–Ð»ÑŒÐºÐ° діапазонів IP-адре
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr "Знайдено декілька типів моделей: %{model_types}"
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr "Знайдено декілька завантажувачів: %{uploader_types}"
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26449,7 +26659,7 @@ msgid "New public deploy key"
msgstr "Ðовий публічний ключ Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ"
msgid "New related %{issueType}"
-msgstr ""
+msgstr "Ðова пов'Ñзана %{issueType}"
msgid "New release"
msgstr "Ðовий реліз"
@@ -26613,6 +26823,9 @@ msgstr ""
msgid "No credit card required."
msgstr "Ðе потрібна кредитна картка."
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr "Даних не знайдено"
@@ -26625,9 +26838,6 @@ msgstr ""
msgid "No deployments found"
msgstr "Ðе знайдено розгортань"
-msgid "No due date"
-msgstr "Ðемає"
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26758,7 +26968,7 @@ msgid "No projects found"
msgstr "Проєкти не знайдені"
msgid "No public deploy keys"
-msgstr ""
+msgstr "Ðемає публічних ключів Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ"
msgid "No public groups"
msgstr "Ðемає публічних груп"
@@ -26776,7 +26986,10 @@ msgid "No repository"
msgstr "Ðемає репозиторію"
msgid "No results"
-msgstr ""
+msgstr "Жодних результатів"
+
+msgid "No results found"
+msgstr "Ðічого не знайдено"
msgid "No runner executable"
msgstr ""
@@ -26799,9 +27012,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr "Ðіхто з тих, хто додав в обране не задовільнÑÑ” вашому пошуку"
-msgid "No start date"
-msgstr "Ðемає дати початку"
-
msgid "No suggestions found"
msgstr "Пропозицій не знайдено"
@@ -26992,6 +27202,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr "Дані Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду відÑутні."
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ події"
@@ -27134,6 +27347,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr "%{commit_link} в %{mr_link}"
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr "%{invite_email}, тепер відомий Ñк %{user_name}, прийнÑв ваше Ð·Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð¿Ñ€Ð¸Ñ”Ð´Ð½Ð°Ñ‚Ð¸ÑÑ Ð´Ð¾ %{target_name} %{target_model_name}."
@@ -27143,6 +27359,24 @@ msgstr "%{invited_user} %{highlight_start}відхилив%{highlight_end} ваÑ
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr "%{member_link} запроÑив %{member_role} доÑтуп до %{target_source_link} %{target_type}."
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -27158,6 +27392,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ñƒ CI/CD"
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr "Відбиток: %{fingerprint}"
+
+msgid "Notify|Hi %{user}!"
+msgstr "Привіт %{user}!"
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -27188,9 +27434,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð±ÑƒÐ² Ñхвалений"
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -27209,6 +27464,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr "Ви не маєте доÑтупу до цього проєкту"
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr "Ð’Ð°Ñ Ð·Ð³Ð°Ð´Ð°Ð»Ð¸ у запиті на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ %{mr_link}"
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -27315,6 +27576,9 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27534,6 +27798,12 @@ msgstr "Видалити профіль"
msgid "OnDemandScans|Description (optional)"
msgstr "ÐžÐ¿Ð¸Ñ (необов'Ñзково)"
+msgid "OnDemandScans|Discard changes"
+msgstr "СкаÑувати зміни"
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr "Ви хочете ÑкаÑувати зміни або продовжити Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ профілю? Ðезбережені зміни будуть втрачені."
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27555,6 +27825,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr "Продовжити редагуваннÑ"
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27678,6 +27951,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr "У Ð²Ð°Ñ Ñ” незбережені зміни"
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27737,7 +28013,7 @@ msgid "Only 1 appearances row can exist"
msgstr ""
msgid "Only HTTP(S)"
-msgstr ""
+msgstr "Тільки HTTP(S)"
msgid "Only Issue ID or merge request ID is required"
msgstr ""
@@ -27746,7 +28022,7 @@ msgid "Only Project Members"
msgstr "Тільки учаÑники проєкту"
msgid "Only SSH"
-msgstr ""
+msgstr "Тільки SSH"
msgid "Only accessible by %{membersPageLinkStart}project members%{membersPageLinkEnd}. Membership must be explicitly granted to each user."
msgstr ""
@@ -27835,9 +28111,15 @@ msgstr "Відкрити: %{open}"
msgid "OpenAPI"
msgstr "OpenAPI"
+msgid "OpenAPI Specification file URL"
+msgstr "URL-адреÑа файлу Ñпецифікації OpenAPI"
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr "Регіон OpenSearch."
+
msgid "Opened"
msgstr "Відкрито"
@@ -28051,6 +28333,9 @@ msgstr "РеєÑÑ‚Ñ€ пакетів: API запити без автентифіÐ
msgid "Package already exists"
msgstr "Пакет вже Ñ–Ñнує"
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr "Пакет уÑпішно видалено"
@@ -28087,6 +28372,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -28111,6 +28399,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr "Додаткові метадані"
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -28209,10 +28500,10 @@ msgstr "Debian"
msgid "PackageRegistry|Delete 1 asset"
msgid_plural "PackageRegistry|Delete %d assets"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "Видалити 1 реÑурÑ"
+msgstr[1] "Видалити %d реÑурÑи"
+msgstr[2] "Видалити %d реÑурÑів"
+msgstr[3] "Видалити %d реÑурÑів"
msgid "PackageRegistry|Delete Package File"
msgstr ""
@@ -28224,7 +28515,7 @@ msgid "PackageRegistry|Delete package"
msgstr "Видалити пакет"
msgid "PackageRegistry|Delete selected"
-msgstr ""
+msgstr "Видалити вибране"
msgid "PackageRegistry|Delete this package"
msgstr "Видалити цей пакет"
@@ -28331,6 +28622,9 @@ msgstr "Пакет уÑпішно видалено"
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr "Формати пакетів"
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -28365,9 +28659,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr "Параметри реєÑтру"
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr "Видалити пакет"
@@ -28377,12 +28668,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28486,8 +28771,8 @@ msgstr "npm"
msgid "PackageRegistry|published by %{author}"
msgstr "опубліковано %{author}"
-msgid "Packages & Registries"
-msgstr "Пакети та реєÑтри"
+msgid "Packages and registries"
+msgstr ""
msgid "Page not found"
msgstr "Сторінку не знайдено"
@@ -28735,9 +29020,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr "Виконати звичайні операції на проєкті GitLab"
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr "ÐžÐ¿Ñ‚Ð¸Ð¼Ñ–Ð·Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾Ð´ÑƒÐºÑ‚Ð¸Ð²Ð½Ð¾ÑÑ‚Ñ–"
@@ -28813,6 +29095,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29450,7 +29738,7 @@ msgid "Pipelines|Validating GitLab CI configuration…"
msgstr "Перевірка конфігурації GitLab CI…"
msgid "Pipelines|View merged YAML"
-msgstr ""
+msgstr "ПереглÑнути об’єднаний YAML"
msgid "Pipelines|Visualize"
msgstr "ВізуалізаціÑ"
@@ -29518,27 +29806,18 @@ msgstr ""
msgid "Pipeline|Date"
msgstr "Дата"
-msgid "Pipeline|Detached merge request pipeline"
-msgstr "Відділений конвеєр запиту на злиттÑ"
-
msgid "Pipeline|Failed"
msgstr "Ðевдало"
-msgid "Pipeline|Five slowest jobs"
-msgstr "П'ÑÑ‚ÑŒ найповільніших завдань"
-
msgid "Pipeline|In progress"
msgstr "ВиконуєтьÑÑ"
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
-msgstr ""
-
msgid "Pipeline|Manual"
msgstr "Вручну"
+msgid "Pipeline|Merge request pipeline"
+msgstr ""
+
msgid "Pipeline|Merge train pipeline"
msgstr "Конвеєр ланцюжка змін"
@@ -29548,18 +29827,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr "Конвеєр результату злиттÑ"
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr "Виконано"
msgid "Pipeline|Pending"
msgstr "Очікує"
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr "Конвеєр"
@@ -29617,12 +29890,6 @@ msgstr "Ім'Ñ Ñ‚ÐµÐ³Ñƒ"
msgid "Pipeline|Test coverage"
msgstr "ТеÑтове покриттÑ"
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29651,10 +29918,7 @@ msgid "Pipeline|Variables"
msgstr "Змінні"
msgid "Pipeline|View commit"
-msgstr ""
-
-msgid "Pipeline|View dependency"
-msgstr ""
+msgstr "ПереглÑнути коміт"
msgid "Pipeline|View pipeline"
msgstr "ПереглÑнути конвеєр"
@@ -29755,8 +30019,8 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr "Будь лаÑка, перевірте Ñвою поштову Ñкриньку (%{email}) Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ñ‚Ð¾Ð³Ð¾, що ви нею володієте, щоб розкрити потенціал CI/CD. Ðе отримали? %{resend_link}. Ðеправильна адреÑа? %{update_link}."
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
-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 your profile with email address"
msgstr "Будь лаÑка, доповніть Ñвій профіль адреÑою електронної пошти"
@@ -29971,6 +30235,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr "ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° файл колекції Postman"
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29992,12 +30259,12 @@ msgstr "Поведінка"
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr "Оберіть між фікÑованим (макÑ. 1280 пікÑ.) та адаптивним (%{percentage}) макетом."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
+msgstr ""
+
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr "Вибрати вміÑÑ‚ оглÑдової Ñторінки проєкту."
-msgid "Preferences|Choose what content you want to see on your homepage."
-msgstr "Виберіть, Ñкий вміÑÑ‚ ви хочете бачити на Ñвоїй домашній Ñторінці."
-
msgid "Preferences|Color for added lines"
msgstr "Колір Ð´Ð»Ñ Ð´Ð¾Ð´Ð°Ð½Ð¸Ñ… Ñ€Ñдків"
@@ -30019,6 +30286,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr "Ðалаштувати кольори вилучених Ñ– доданих Ñ€Ñдків у відмінноÑÑ‚ÑÑ…."
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr "Колір різниці"
@@ -30029,7 +30299,7 @@ msgid "Preferences|Enable Gitpod integration"
msgstr "Увімкнути інтеграцію з Gitpod"
msgid "Preferences|Enable integrated code intelligence on code views"
-msgstr ""
+msgstr "Увімкнути інтегрований аналіз коду в режимах переглÑду коду"
msgid "Preferences|Failed to save preferences."
msgstr "Ðе вдалоÑÑ Ð·Ð±ÐµÑ€ÐµÐ³Ñ‚Ð¸ налаштуваннÑ."
@@ -30040,9 +30310,6 @@ msgstr "Ðаприклад: 30 хвилин тому."
msgid "Preferences|Gitpod"
msgstr "Gitpod"
-msgid "Preferences|Homepage content"
-msgstr "ВміÑÑ‚ домашньої Ñторінки"
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr "ЗаміÑÑ‚ÑŒ уÑÑ–Ñ… змінених файлів показувати лише по одному файлу за раз. Ð”Ð»Ñ Ð¿ÐµÑ€ÐµÐ¼Ð¸ÐºÐ°Ð½Ð½Ñ Ð¼Ñ–Ð¶ файлами викориÑтовуйте файловий браузер."
@@ -30146,7 +30413,7 @@ msgid "Previous Artifacts"
msgstr "Попередні артефакти"
msgid "Previous commit"
-msgstr ""
+msgstr "Попередній коміт"
msgid "Previous design"
msgstr ""
@@ -30454,9 +30721,6 @@ msgstr "ПоÑада"
msgid "Profiles|Key"
msgstr "Ключ"
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30487,6 +30751,9 @@ msgstr "Файл не вибрано."
msgid "Profiles|Notification email"
msgstr "ÐдреÑа електронної пошти Ð´Ð»Ñ Ñповіщень"
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr "ОрганізаціÑ"
@@ -30950,7 +31217,7 @@ msgid "ProjectQualitySummary|Set up test runs (opens in a new tab)"
msgstr ""
msgid "ProjectQualitySummary|Skipped"
-msgstr ""
+msgstr "Пропущено"
msgid "ProjectQualitySummary|Success"
msgstr ""
@@ -31174,6 +31441,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr "Середовища"
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr "Кожне Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñтворить окремий коміт."
@@ -31186,6 +31456,9 @@ 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 "Кожен проєкт може мати влаÑний проÑÑ‚Ñ–Ñ€ Ð´Ð»Ñ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Ð¹Ð¾Ð³Ð¾ пакетів. Примітка: РеєÑÑ‚Ñ€ пакунків завжди видно, коли проєкт Ñ” публічним."
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr "Ð’ÑÑ–"
@@ -31204,6 +31477,9 @@ msgstr "Fast-forward злиттÑ"
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr "Перемикачі функцій"
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr "Гнучкий інÑтрумент Ð´Ð»Ñ Ñпільної розробки ідей та Ð¿Ð»Ð°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€Ð¾Ð±Ð¾Ñ‚Ð¸ в цьому проєкті."
@@ -31211,7 +31487,7 @@ msgid "ProjectSettings|Forks"
msgstr "Форки"
msgid "ProjectSettings|Git Large File Storage (LFS)"
-msgstr ""
+msgstr "Сховище великих файлів Git (LFS)"
msgid "ProjectSettings|Global"
msgstr "Глобальні"
@@ -31345,6 +31621,9 @@ msgstr "Вимоги"
msgid "ProjectSettings|Requirements management system."
msgstr "СиÑтема ÑƒÐ¿Ñ€Ð°Ð²Ð»Ñ–Ð½Ð½Ñ Ð²Ð¸Ð¼Ð¾Ð³Ð°Ð¼Ð¸."
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr "Пошук теми"
@@ -31388,7 +31667,7 @@ msgid "ProjectSettings|Submit changes to be merged upstream."
msgstr "Відправити зміни Ð´Ð»Ñ Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð² батьківÑькі репозиторії."
msgid "ProjectSettings|Target project"
-msgstr ""
+msgstr "Цільовий проєкт"
msgid "ProjectSettings|The commit message used when applying merge request suggestions."
msgstr ""
@@ -32123,7 +32402,7 @@ msgid "ProtectedBranch|Protected branch (%{protected_branches_count})"
msgstr "Захищена гілка (%{protected_branches_count})"
msgid "ProtectedBranch|Protected branches"
-msgstr ""
+msgstr "Захищені гілки"
msgid "ProtectedBranch|Reject code pushes that change files listed in the CODEOWNERS file."
msgstr ""
@@ -32155,7 +32434,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr "%{environment_name} буде доÑупне Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñу Ð´Ð»Ñ Ñ€Ð¾Ð·Ñ€Ð¾Ð±Ð½Ð¸ÐºÑ–Ð². Ви впевнені?"
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -32173,7 +32452,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -32183,7 +32462,7 @@ msgid "ProtectedEnvironment|Only specified users can execute deployments in a pr
msgstr ""
msgid "ProtectedEnvironment|Parent group"
-msgstr ""
+msgstr "БатьківÑька група"
msgid "ProtectedEnvironment|Protect"
msgstr "ЗахиÑтити"
@@ -32279,7 +32558,7 @@ msgid "Public Access Help"
msgstr ""
msgid "Public deploy keys"
-msgstr ""
+msgstr "Публічні ключі Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ"
msgid "Public pipelines"
msgstr "Публічні конвеєри"
@@ -32315,7 +32594,7 @@ msgid "PumbleIntegration|Send notifications about project events to Pumble."
msgstr ""
msgid "PumbleIntegration|Send notifications about project events to Pumble. %{docs_link}"
-msgstr ""
+msgstr "ÐадÑилати Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ події проєкту у Pumble. %{docs_link}"
msgid "Purchase more minutes"
msgstr "Придбати більше хвилин"
@@ -32356,9 +32635,6 @@ msgstr "Події Ð²Ñ–Ð´Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ (push)"
msgid "Push project from command line"
msgstr "Виконати push проєкту за допомогою командного Ñ€Ñдка"
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32482,9 +32758,6 @@ msgstr "Швидка довідка"
msgid "Quick range"
msgstr "Швидкий діапазон"
-msgid "Quickly and easily edit multiple files in your project."
-msgstr "Швидко і легко редагувати декілька файлів у вашому проєкті."
-
msgid "Quota of CI/CD minutes"
msgstr "Квота хвилин CI/CD"
@@ -32717,6 +32990,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr "ЗареєÑтрувати за допомогою двофакторного заÑтоÑунку"
+msgid "Register with:"
+msgstr "ЗареєÑтруватиÑÑ Ð²:"
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33232,7 +33508,7 @@ msgid "Replication"
msgstr ""
msgid "Reply"
-msgstr ""
+msgstr "ВідповіÑти"
msgid "Reply by email"
msgstr "ВідповіÑти по електнонній пошті"
@@ -33506,7 +33782,7 @@ msgid "RepositoriesAnalytics|Last 30 days"
msgstr ""
msgid "RepositoriesAnalytics|Last Update"
-msgstr ""
+msgstr "ОÑтаннє оновленнÑ"
msgid "RepositoriesAnalytics|Last updated %{timeAgo}"
msgstr ""
@@ -33614,10 +33890,10 @@ msgid "Repository storage"
msgstr "Сховище репозиторію"
msgid "Repository update events"
-msgstr ""
+msgstr "Події Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–ÑŽ"
msgid "Repository usage recalculation started"
-msgstr ""
+msgstr "Розпочато перерахунок викориÑÑ‚Ð°Ð½Ð½Ñ Ñховища"
msgid "Repository: %{counter_repositories} / Wikis: %{counter_wikis} / Build Artifacts: %{counter_build_artifacts} / Pipeline Artifacts: %{counter_pipeline_artifacts} / LFS: %{counter_lfs_objects} / Snippets: %{counter_snippets} / Packages: %{counter_packages} / Uploads: %{counter_uploads}"
msgstr ""
@@ -33701,7 +33977,7 @@ msgid "Requirement %{reference} has been archived"
msgstr "Вимога %{reference} була архівована"
msgid "Requirement %{reference} has been reopened"
-msgstr ""
+msgstr "Вимога %{reference} була відкрита знову"
msgid "Requirement %{reference} has been updated"
msgstr "Вимогу %{reference} було оновлено"
@@ -33745,7 +34021,7 @@ msgid "Resend Request"
msgstr "Повторно надіÑлати запит"
msgid "Resend confirmation e-mail"
-msgstr ""
+msgstr "Повторно надіÑлати лиÑта з підтвердженнÑм"
msgid "Resend confirmation email"
msgstr "Повторно відіÑлати Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð¿Ð¾ електронній пошті"
@@ -34115,6 +34391,9 @@ msgstr "Ðктивні"
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr "Ð’ÑÑ–"
@@ -34130,6 +34409,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr "Ðрхітектура"
@@ -34178,6 +34463,9 @@ msgstr "Скопіювати інÑтрукції"
msgid "Runners|Copy registration token"
msgstr "Скопіювати токен Ð´Ð»Ñ Ñ€ÐµÑ”Ñтрації"
+msgid "Runners|Created %{timeAgo}"
+msgstr "Створено %{timeAgo}"
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] "Видалити %d runner"
@@ -34227,6 +34515,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr "Розпочати роботу з runner'ами"
@@ -34251,6 +34542,9 @@ msgstr "ЗавданнÑ"
msgid "Runners|Last contact"
msgstr "ОÑтанній контакт"
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr "ОÑтанній контакт: %{timeAgo}"
+
msgid "Runners|Locked to this project"
msgstr "Закріплено за цим проєктом"
@@ -34296,24 +34590,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr "Офлайн"
-msgid "Runners|Offline runners"
-msgstr "Офлайн runner'и"
-
msgid "Runners|Offline:"
msgstr "Офлайн:"
msgid "Runners|Online"
msgstr "Онлайн"
-msgid "Runners|Online runners"
-msgstr "Онлайн runner'и"
-
msgid "Runners|Online:"
msgstr "Онлайн:"
-msgid "Runners|Outdated"
-msgstr "ЗаÑтарілі"
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34462,9 +34747,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr "ЗаÑтарілі"
-msgid "Runners|Stale runners"
-msgstr "ЗаÑтарілі runner'и"
-
msgid "Runners|Stale:"
msgstr "ЗаÑтарілі:"
@@ -34518,7 +34800,7 @@ msgid "Runners|To register them, go to the %{link_start}group's Runners page%{li
msgstr ""
msgid "Runners|Up to date"
-msgstr ""
+msgstr "Ð’ актуальному Ñтані"
msgid "Runners|Upgrade GitLab Runner to match the version of GitLab you're running. Both %{linkStart}major and minor versions%{linkEnd} should match."
msgstr ""
@@ -34550,9 +34832,18 @@ msgstr "ЗначеннÑ"
msgid "Runners|Version"
msgstr "ВерÑÑ–Ñ"
+msgid "Runners|Version %{version}"
+msgstr "ВерÑÑ–Ñ %{version}"
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34571,42 +34862,18 @@ msgstr "Ви викориÑтали %{quotaUsed} із ваших %{quotaLimit} Ñ
msgid "Runners|active"
msgstr "активні"
-msgid "Runners|available"
-msgstr "ДоÑтупні"
-
msgid "Runners|group"
msgstr "Група"
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr "офлайн"
-
-msgid "Runners|online"
-msgstr "онлайн"
-
msgid "Runners|paused"
msgstr "пауза"
-msgid "Runners|recommended"
-msgstr "рекомендовано"
-
msgid "Runners|shared"
msgstr "Ñпільний"
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr "заÑтарілі"
-
-msgid "Runners|upgrade available"
-msgstr "доÑтупне оновленнÑ"
-
-msgid "Runners|upgrade recommended"
-msgstr "РекомендуєтьÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸"
-
msgid "Runner|Owner"
msgstr "ВлаÑник"
@@ -34700,6 +34967,9 @@ msgstr "Перевірка SSL:"
msgid "SSL verification"
msgstr "Перевірка SSL"
+msgid "Sat"
+msgstr "Суб"
+
msgid "Satisfied"
msgstr ""
@@ -34745,12 +35015,27 @@ msgstr "ЗбереженнÑ"
msgid "Saving project."
msgstr "Ð—Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ñƒ."
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr "%{ifLabelStart}Ñкщо%{ifLabelEnd} %{rules} дії Ð´Ð»Ñ %{scopes} %{branches}"
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr "%{period} %{days} на %{time}"
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr "Конвеєр запущений"
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34760,6 +35045,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr "Виберіть гілки"
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr "гілка"
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -35105,7 +35402,7 @@ msgid "SearchToken|Assignee"
msgstr "Виконавець"
msgid "SearchToken|Reviewer"
-msgstr ""
+msgstr "ОглÑдач"
msgid "Searching by both author and message is currently not supported."
msgstr ""
@@ -35288,7 +35585,7 @@ msgid "SecurityConfiguration|More scan types, including Container Scanning, DAST
msgstr ""
msgid "SecurityConfiguration|Not enabled"
-msgstr ""
+msgstr "Ðе увімкнено"
msgid "SecurityConfiguration|Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
@@ -35362,6 +35659,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr "Дії"
+msgid "SecurityOrchestration|Add action"
+msgstr "Додати дію"
+
msgid "SecurityOrchestration|Add rule"
msgstr "Додати правило"
@@ -35483,7 +35783,7 @@ msgid "SecurityOrchestration|No rules defined - policy will not run."
msgstr ""
msgid "SecurityOrchestration|Not enabled"
-msgstr ""
+msgstr "Ðе увімкнено"
msgid "SecurityOrchestration|Only owners can update Security Policy Project"
msgstr ""
@@ -35617,6 +35917,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -35666,7 +35969,7 @@ msgid "SecurityOrchestration|a"
msgstr ""
msgid "SecurityOrchestration|all branches"
-msgstr ""
+msgstr "уÑÑ– гілки"
msgid "SecurityOrchestration|an"
msgstr ""
@@ -35675,7 +35978,7 @@ msgid "SecurityOrchestration|branch"
msgstr ""
msgid "SecurityOrchestration|branches"
-msgstr ""
+msgstr "гілки"
msgid "SecurityOrchestration|scanner finds"
msgstr ""
@@ -35726,7 +36029,7 @@ msgid "SecurityReports|Check the messages generated while parsing the following
msgstr ""
msgid "SecurityReports|Cluster"
-msgstr ""
+msgstr "КлаÑтер"
msgid "SecurityReports|Comment added to '%{vulnerabilityName}'"
msgstr "Коментар додано до \"%{vulnerabilityName}\""
@@ -36044,7 +36347,7 @@ msgid "Select a branch"
msgstr "Вибрати гілку"
msgid "Select a branch to compare"
-msgstr ""
+msgstr "Виберіть гілку Ð´Ð»Ñ Ð¿Ð¾Ñ€Ñ–Ð²Ð½ÑннÑ"
msgid "Select a color"
msgstr "Виберіть колір"
@@ -36110,7 +36413,7 @@ msgid "Select branches"
msgstr ""
msgid "Select due date"
-msgstr ""
+msgstr "Виберіть заплановану дату завершеннÑ"
msgid "Select epic"
msgstr ""
@@ -36146,7 +36449,7 @@ msgid "Select project"
msgstr "Вибрати проєкт"
msgid "Select project to create %{type}"
-msgstr ""
+msgstr "Вибрати проєкт Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ %{type}"
msgid "Select project to create issue"
msgstr "Виберіть проєкт Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡Ñ–"
@@ -36185,7 +36488,7 @@ msgid "Select target branch"
msgstr "Вибір цільової гілки"
msgid "Select target branch or tag"
-msgstr ""
+msgstr "Виберіть цільову гілку або тег"
msgid "Select target project"
msgstr ""
@@ -36807,22 +37110,22 @@ msgid "ShowcaseSecurity|Audit your Docker-based app. Scan for known vulnerabilit
msgstr ""
msgid "ShowcaseSecurity|Container scanning"
-msgstr ""
+msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ñ‚ÐµÐ¹Ð½ÐµÑ€Ñ–Ð²"
msgid "ShowcaseSecurity|Dependency scanning"
-msgstr ""
+msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¾Ñтей"
msgid "ShowcaseSecurity|Dynamic Application Security Testing (DAST)"
-msgstr ""
+msgstr "Динамічне теÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÐµÐ·Ð¿ÐµÐºÐ¸ заÑтоÑунків (DAST)"
msgid "ShowcaseSecurity|Dynamically examine your application for vulnerabilities in deployed environments."
msgstr ""
msgid "ShowcaseSecurity|Enable SAST"
-msgstr ""
+msgstr "Увімкнути SAST"
msgid "ShowcaseSecurity|Enable Secret Detection"
-msgstr ""
+msgstr "Увімкнути Ñекретне виÑвленнÑ"
msgid "ShowcaseSecurity|Enable Static Application Security Testing (SAST)"
msgstr ""
@@ -36843,7 +37146,7 @@ msgid "ShowcaseSecurity|Start a free 30-day Ultimate trial or upgrade your insta
msgstr ""
msgid "ShowcaseSecurity|Start a free trial"
-msgstr ""
+msgstr "Розпочати безкоштовну пробну верÑÑ–ÑŽ"
msgid "ShowcaseSecurity|Take your security to the next level"
msgstr ""
@@ -36874,7 +37177,7 @@ msgid "Showing %{limit} of %{total_count} issues. "
msgstr "Показано %{limit} з %{total_count} задач. "
msgid "Showing %{pageSize} of %{total} %{issuableType}"
-msgstr ""
+msgstr "Показано %{pageSize} з %{total} %{issuableType}"
msgid "Showing all epics"
msgstr "Показати вÑÑ– епіки"
@@ -36985,7 +37288,7 @@ msgid "Sign-in count:"
msgstr ""
msgid "Sign-in failed because %{error}."
-msgstr ""
+msgstr "Помилка входу, тому що %{error}."
msgid "Sign-in page"
msgstr ""
@@ -37005,12 +37308,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr "ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ€ÐµÑ”Ñтрації"
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr "ÐатиÑнувши %{button_text} або зареєÑтрувавшиÑÑŒ через третю Ñторону, ви приймаєте Умови викориÑÑ‚Ð°Ð½Ð½Ñ GitLab%{link_start} Ñ– погоджуєтеÑÑ Ð· Політикою конфіденційноÑÑ‚Ñ– та Політикою файлів cookie%{link_end}"
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr "ÐатиÑнувши %{button_text} або зареєÑтрувавшиÑÑŒ через третю Ñторону, ви приймаєте%{link_start} Умови викориÑÑ‚Ð°Ð½Ð½Ñ Ñ‚Ð° погоджуєтеÑÑ Ð· Політикою конфіденційноÑÑ‚Ñ– та Політикою щодо файлів cookie%{link_end}"
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr "Увійшовши, ви приймаєте %{link_start}Умови викориÑÑ‚Ð°Ð½Ð½Ñ Ñ‚Ð° погоджуєтеÑÑ Ð· Політикою конфіденційноÑÑ‚Ñ– та Політикою щодо файлів cookie%{link_end}."
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -37284,6 +37596,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr "Проблема при додаванні вашої нагороди. Будь лаÑка, Ñпробуйте знову."
@@ -37374,6 +37689,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37758,6 +38076,9 @@ msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð¾Ð±'єднаного (squash) коміÑ
msgid "Squash commits"
msgstr "Виконати об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ (squash) комітів"
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr "ТраÑÑƒÐ²Ð°Ð½Ð½Ñ Ñтеку"
@@ -37773,9 +38094,6 @@ msgstr "СтадіÑ:"
msgid "Standard"
msgstr "Стандарт"
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr "Позначте мітку, щоб зробити Ñ—Ñ— пріоритетною. ПеретÑгуйте пріоритетні мітки Ð´Ð»Ñ Ð·Ð¼Ñ–Ð½Ð¸ Ñ—Ñ… відноÑного пріоритету."
-
msgid "Star labels to start sorting by priority"
msgstr "Додавайте мітки в обране, щоб Ñортувати за пріоритетом"
@@ -38509,11 +38827,14 @@ msgid "Summary"
msgstr "Звіт"
msgid "Summary / note"
-msgstr ""
+msgstr "ПідÑумок / примітка"
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr "Ðед"
+
msgid "Sunday"
msgstr "ÐеділÑ"
@@ -38642,7 +38963,7 @@ msgid "SuperSonics|You can start a free trial of GitLab Ultimate without any obl
msgstr ""
msgid "SuperSonics|You do not have an active subscription"
-msgstr ""
+msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” активної підпиÑки"
msgid "SuperSonics|You have a future dated license"
msgstr ""
@@ -38663,7 +38984,7 @@ msgid "SuperSonics|Your future dated license was successfully added"
msgstr ""
msgid "SuperSonics|Your subscription"
-msgstr ""
+msgstr "Ваша підпиÑка"
msgid "SuperSonics|Your subscription details will sync shortly."
msgstr ""
@@ -38990,7 +39311,7 @@ msgid "Task ID: %{elastic_task}"
msgstr "Ідентифікатор завданнÑ: %{elastic_task}"
msgid "Task list"
-msgstr ""
+msgstr "СпиÑок завдань"
msgid "TasksToBeDone|Create/import code into a project (repository)"
msgstr ""
@@ -39793,6 +40114,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39809,7 +40133,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 "ДоÑтуп до проєкту можливий без будь-Ñкої перевірки автентичноÑÑ‚Ñ–."
@@ -39889,6 +40213,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr "Вказана вкладка недійÑна. Будь лаÑка, виберіть іншу"
@@ -39901,6 +40231,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39949,10 +40282,10 @@ msgstr "Тема"
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
-msgstr ""
+msgid "There are currently no mirrored repositories."
+msgstr "Ðаразі немає віддзеркалених репозиторіїв."
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39992,7 +40325,7 @@ msgid "There are no charts configured for this page"
msgstr "Ð”Ð»Ñ Ñ†Ñ–Ñ”Ñ— Ñторінки не налаштовано графіків"
msgid "There are no closed epics"
-msgstr ""
+msgstr "Закритих епіків не Ñ–Ñнує"
msgid "There are no closed issues"
msgstr "Закритих задач немає"
@@ -40057,9 +40390,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr "Ðа диÑку вже Ñ–Ñнує репозиторій за таким ім’Ñм"
@@ -40411,6 +40741,9 @@ msgstr "Цей блок поÑилаєтьÑÑ Ñам на Ñебе"
msgid "This board's scope is reduced"
msgstr "ВидиміÑÑ‚ÑŒ цієї дошки обмежена"
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr "Ð¦Ñ Ð´Ñ–Ð°Ð³Ñ€Ð°Ð¼Ð° не може бути відображена"
@@ -40435,8 +40768,8 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr "Цей коміт підпиÑано перевіреним підпиÑом іншого кориÑтувача."
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
-msgstr ""
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
+msgstr "Цей коміт було підпиÑано перевіреним підпиÑом, але електронна адреÑа комітента не пов’Ñзана з ключем GPG."
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr ""
@@ -40816,6 +41149,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40828,9 +41164,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40933,6 +41266,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr "Чет"
+
msgid "Thursday"
msgstr "Четвер"
@@ -40988,7 +41324,7 @@ msgid "Time tracking"
msgstr "ВідÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ñ‡Ð°Ñу"
msgid "Time tracking report"
-msgstr ""
+msgstr "Звіт про відÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ñ‡Ð°Ñу"
msgid "Time until first merge request"
msgstr "Ð§Ð°Ñ Ð´Ð¾ першого запиту на злиттÑ"
@@ -41000,7 +41336,7 @@ msgid "TimeTrackingEstimated|Est"
msgstr "Запланований чаÑ"
msgid "TimeTracking|%{spentStart}Spent: %{spentEnd}"
-msgstr ""
+msgstr "%{spentStart}Витрачено: %{spentEnd}"
msgid "TimeTracking|An error occurred while removing the timelog."
msgstr ""
@@ -41143,6 +41479,9 @@ msgstr "щойно"
msgid "Timeago|right now"
msgstr "зараз"
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -41170,6 +41509,24 @@ msgstr ""
msgid "Timezone"
msgstr "ЧаÑовий поÑÑ"
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "година"
@@ -41184,6 +41541,12 @@ msgstr[1] "хвилини"
msgstr[2] "хвилин"
msgstr[3] "хвилин"
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr "Ñекунд(а)"
@@ -41238,6 +41601,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr "Щоб додати Ð·Ð°Ð¿Ð¸Ñ Ð²Ñ€ÑƒÑ‡Ð½Ñƒ, надайте наÑтупні відомоÑÑ‚Ñ– заÑтоÑунку у вашому телефоні."
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -41400,9 +41766,6 @@ msgstr "Щоб розширити пошук, змініть або видалі
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr "СпиÑок нагадувань"
@@ -41419,12 +41782,24 @@ msgid "Todos|Are you looking for things to do? Take a look at %{strongStart}%{op
msgstr ""
msgid "Todos|Filter by author"
-msgstr ""
+msgstr "Фільтрувати за автором"
msgid "Todos|Filter by group"
-msgstr ""
+msgstr "Фільтрувати за групою"
msgid "Todos|Filter by project"
+msgstr "Фільтрувати за проєктом"
+
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
msgstr ""
msgid "Todos|It's how you always know what to work on next."
@@ -41436,6 +41811,9 @@ msgstr "Позначити вÑе Ñк виконане"
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr "Відмінити позначити вÑе Ñк виконано"
@@ -41544,9 +41922,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41571,9 +41958,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr "Тему уÑпішно оновлено."
+msgid "TopicSelect|No matching results"
+msgstr "Ðемає відповідних результатів"
+
+msgid "TopicSelect|Search topics"
+msgstr "Пошук тем"
+
+msgid "TopicSelect|Select a topic"
+msgstr "Оберіть тему"
+
msgid "Topics"
msgstr "Теми"
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr "Ð’Ñього"
@@ -41742,39 +42141,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr "Ðазва компанії"
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41880,21 +42252,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr "ВідбуваєтьÑÑ Ð·'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ñ–Ð· вашим приÑтроєм. Підключіть його (Ñкщо ви цього ще не зробили) Ñ– натиÑніть кнопку на ньому зараз."
+msgid "Tue"
+msgstr "Вів"
+
msgid "Tuesday"
msgstr "Вівторок"
msgid "Turn off"
msgstr "Вимкнути"
-msgid "Turn off notifications"
-msgstr "Вимкнути ÑповіщеннÑ"
-
msgid "Turn on"
msgstr "Увімкнути"
-msgid "Turn on notifications"
-msgstr "Увімкнути ÑповіщеннÑ"
-
msgid "Twitter"
msgstr "Twitter"
@@ -42430,7 +42799,7 @@ msgid "Upvotes"
msgstr "Лайки"
msgid "Usage Trends"
-msgstr ""
+msgstr "Тенденції викориÑтаннÑ"
msgid "Usage statistics"
msgstr "СтатиÑтика викориÑтаннÑ"
@@ -42474,17 +42843,20 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ Ñ…Ð²Ð¸Ð»Ð¸Ð½ CI/CD із %{usageSince}"
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
msgid "UsageQuota|Container Registry"
-msgstr ""
+msgstr "РеєÑÑ‚Ñ€ контейнерів"
msgid "UsageQuota|Current period usage"
msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ Ð·Ð° поточний період"
msgid "UsageQuota|Dependency proxy"
-msgstr ""
+msgstr "ПрокÑÑ– залежноÑтей"
msgid "UsageQuota|Effective %{storage_enforcement_date}, namespace storage limits will apply to the %{strong_start}%{namespace_name}%{strong_end} namespace. %{extra_message}View the %{rollout_link_start}rollout schedule for this change%{link_end}."
msgstr ""
@@ -42493,7 +42865,7 @@ msgid "UsageQuota|File attachments and smaller design graphics."
msgstr ""
msgid "UsageQuota|Git repository."
-msgstr ""
+msgstr "Git репозиторій."
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images."
msgstr ""
@@ -42501,6 +42873,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð³Ñ€ÑƒÐ¿Ð¸ &gt; Квоти викориÑтаннÑ"
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42559,7 +42934,7 @@ msgid "UsageQuota|Repository"
msgstr "Репозиторій"
msgid "UsageQuota|Seats"
-msgstr ""
+msgstr "МіÑцÑ"
msgid "UsageQuota|See our %{faq_link_start}FAQ%{link_end} for more information."
msgstr ""
@@ -42597,11 +42972,11 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
-msgstr ""
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
+msgstr "ПроÑÑ‚Ñ–Ñ€ імен наразі викориÑтовує %{strong_start}%{used_storage}%{strong_end} пам’ÑÑ‚Ñ– проÑтору імен. ВлаÑники груп можуть переглÑдати викориÑÑ‚Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ñтору імен Ñ– купувати більше в %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ%{link_end}."
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
-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 ""
@@ -42646,13 +43021,13 @@ msgid "UsageQuota|Usage Quotas"
msgstr "Квоти на викориÑтаннÑ"
msgid "UsageQuota|Usage breakdown"
-msgstr ""
+msgstr "Розподіл викориÑтаннÑ"
msgid "UsageQuota|Usage by month"
-msgstr ""
+msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ Ð·Ð° міÑÑць"
msgid "UsageQuota|Usage by project"
-msgstr ""
+msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ Ð¿Ð¾ проєкту"
msgid "UsageQuota|Usage of group resources across the projects in the %{strong_start}%{group_name}%{strong_end} group"
msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ Ð³Ñ€ÑƒÐ¿Ð¾Ð²Ð¸Ñ… реÑурÑів у проєктах групи %{strong_start}%{group_name}%{strong_end}"
@@ -42666,8 +43041,8 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
-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 ""
@@ -42676,7 +43051,7 @@ msgid "UsageQuota|Wiki"
msgstr "Вікі"
msgid "UsageQuota|Wiki content."
-msgstr ""
+msgstr "ВміÑÑ‚ Вікі."
msgid "UsageQuota|You have consumed all of your additional storage, please purchase more to unlock your projects over the free %{actualRepositorySizeLimit} limit."
msgstr ""
@@ -42685,7 +43060,7 @@ msgid "UsageQuota|You have reached the free storage limit of %{actualRepositoryS
msgstr ""
msgid "UsageQuota|You used: %{usage} %{limit}"
-msgstr ""
+msgstr "Ви викориÑтали: %{usage} %{limit}"
msgid "UsageQuota|Your purchased storage is running low. To avoid locked projects, please purchase more storage."
msgstr ""
@@ -42709,13 +43084,13 @@ msgid "UsageTrends|Issues"
msgstr "Задачі"
msgid "UsageTrends|Issues & merge requests"
-msgstr ""
+msgstr "Задачі та запити на злиттÑ"
msgid "UsageTrends|Items"
-msgstr ""
+msgstr "Елементи"
msgid "UsageTrends|Merge requests"
-msgstr ""
+msgstr "Запити на злиттÑ"
msgid "UsageTrends|Month"
msgstr "МіÑÑць"
@@ -42730,7 +43105,7 @@ msgid "UsageTrends|Pipelines canceled"
msgstr "Конвеєри ÑкаÑовано"
msgid "UsageTrends|Pipelines failed"
-msgstr ""
+msgstr "Конвеєри невдалі"
msgid "UsageTrends|Pipelines skipped"
msgstr "Конвеєри пропущені"
@@ -42828,6 +43203,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr "ВикориÑтовуйте один Ñ€Ñдок Ð´Ð»Ñ ÐºÐ¾Ð¶Ð½Ð¾Ð³Ð¾ URI"
@@ -42914,7 +43292,7 @@ msgid "User Settings"
msgstr "Параметри кориÑтувача"
msgid "User and IP rate limits"
-msgstr ""
+msgstr "Ліміти чаÑтоти Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувачів та IP"
msgid "User cap"
msgstr ""
@@ -43231,7 +43609,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43525,11 +43903,14 @@ msgstr "ПереглÑнути деталі попередженнÑ."
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr "ПереглÑнути вÑÑ– групи"
+
msgid "View all issues"
msgstr "ПроглÑнути вÑÑ– задачі"
-msgid "View all personal projects"
-msgstr ""
+msgid "View all projects"
+msgstr "ПереглÑнути вÑÑ– проєкти"
msgid "View blame"
msgstr ""
@@ -44111,7 +44492,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 ""
@@ -44230,6 +44611,12 @@ msgstr "Перейти до форку"
msgid "WebIDE|Merge request"
msgstr "Запит на злиттÑ"
+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|This project does not accept unsigned commits."
msgstr ""
@@ -44311,6 +44698,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -44342,7 +44732,7 @@ msgid "Webhooks|Deployment events"
msgstr ""
msgid "Webhooks|Enable SSL verification"
-msgstr ""
+msgstr "Увімкнути перевірку SSL"
msgid "Webhooks|Failed to connect"
msgstr ""
@@ -44353,6 +44743,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -44410,6 +44803,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -44428,6 +44824,9 @@ msgstr ""
msgid "Website:"
msgstr "Сайт:"
+msgid "Wed"
+msgstr "Сер"
+
msgid "Wednesday"
msgstr "Середа"
@@ -44479,6 +44878,9 @@ msgstr "Ðа що впливає налаштуваннÑ?"
msgid "What does this command do?"
msgstr "Що робить Ñ†Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð°?"
+msgid "What is GitLab Runner?"
+msgstr "Що таке GitLab Runner?"
+
msgid "What is Markdown?"
msgstr ""
@@ -44694,14 +45096,11 @@ msgstr "Видалити Ñторінку %{pageTitle}?"
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr "СкаÑувати"
msgid "WikiPage|Commit message"
-msgstr ""
+msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñƒ"
msgid "WikiPage|Content"
msgstr ""
@@ -44721,9 +45120,6 @@ msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ."
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr "Зберегти зміни"
@@ -44811,10 +45207,10 @@ msgstr "ВиконуєтьÑÑ Ñ€Ð¾Ð±Ð¾Ñ‚Ð° (відкрита та не приз
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44832,7 +45228,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44845,16 +45241,13 @@ msgstr[3] "Виконавців"
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr "Дочірній елемент видалено"
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44863,41 +45256,56 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
msgstr ""
+msgid "WorkItem|Incident"
+msgstr "Інцидент"
+
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr "Задача"
+
+msgid "WorkItem|Learn about tasks."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
+msgid "WorkItem|None"
+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 "Лише учаÑники проєкту з принаймні роллю Репортера, автор Ñ– виконавці можуть переглÑдати це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð°Ð±Ð¾ отримувати ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ нього."
+
msgid "WorkItem|Open"
msgstr ""
msgid "WorkItem|Remove"
msgstr "Видалити"
+msgid "WorkItem|Requirements"
+msgstr "Вимоги"
+
msgid "WorkItem|Select type"
msgstr "Вибрати тип"
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
-msgstr ""
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
+msgstr "ЩоÑÑŒ пішло не так при отриманні ÑпиÑку елементів. Будь лаÑка, оновіть цю Ñторінку."
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
msgstr ""
@@ -44911,31 +45319,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
msgstr ""
+msgid "WorkItem|Turn on confidentiality"
+msgstr "Увімкнути конфіденційніÑÑ‚ÑŒ"
+
msgid "WorkItem|Undo"
msgstr "СкаÑувати"
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -45161,6 +45578,13 @@ 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 ""
@@ -45221,13 +45645,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr "Ви можете запроÑити нового учаÑника до %{project_name} або запроÑити іншу групу."
@@ -45333,13 +45750,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr "Ви не змогли Ñтворити новий тригер."
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
-
msgid "You do not have any subscriptions yet"
msgstr "У Ð²Ð°Ñ Ñ‰Ðµ немає підпиÑок"
@@ -45506,8 +45916,8 @@ 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."
-msgstr "Ви уÑпішно придбали підпиÑку на план %{plan} на %{seats}. Ви отримаєте чек електронною поштою."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
+msgstr ""
msgid "You have unsaved changes"
msgstr ""
@@ -45569,6 +45979,9 @@ msgstr "Ви повинні завантажити екÑпортований а
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45680,9 +46093,6 @@ msgstr ""
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 "Ви уже увімкнули двофакторну автентифікацію за допомогою автентифікаторів одноразових паролів. Ð”Ð»Ñ Ñ€ÐµÑ”Ñтрації іншого приÑтрою ви повинні Ñпочатку вимкнути двофакторну автентифікацію."
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45722,6 +46132,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45872,9 +46288,6 @@ msgstr "Ваш приÑтрій уÑпішно налаштовано! ДайтÐ
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr "Ваш перший проєкт"
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45940,6 +46353,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr "Ваш пароль"
+
msgid "Your password reset token has expired."
msgstr "Ваш токен Ð´Ð»Ñ ÑÐºÐ¸Ð´Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»ÑŽ заÑтарів."
@@ -45955,7 +46371,7 @@ msgstr ""
msgid "Your profile"
msgstr "Ваш профіль"
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -46168,9 +46584,6 @@ msgstr ""
msgid "assign yourself"
msgstr "призначити Ñамому Ñобі"
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -46268,6 +46681,9 @@ msgstr "не може бути змінено Ñкщо в реєÑтрі конÑ
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -46311,6 +46727,9 @@ 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}"
+
msgid "ciReport|%{danger_start}%{degradedNum} degraded%{danger_end}, %{same_start}%{sameNum} same%{same_end}, and %{success_start}%{improvedNum} improved%{success_end}"
msgstr ""
@@ -46359,7 +46778,7 @@ msgstr "%{reportType}: Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¸Ð·Ð²ÐµÐ»Ð¾ до помил
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46381,7 +46800,7 @@ msgid "ciReport|API fuzzing"
msgstr ""
msgid "ciReport|All clusters"
-msgstr ""
+msgstr "Ð’ÑÑ– клаÑтери"
msgid "ciReport|All images"
msgstr "УÑÑ– образи"
@@ -46567,8 +46986,8 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð»Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñми"
-msgid "ciReport|Manually Added"
-msgstr ""
+msgid "ciReport|Manually added"
+msgstr "Додано вручну"
msgid "ciReport|New"
msgstr ""
@@ -46694,9 +47113,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46733,6 +47149,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr "щоднÑ"
+
msgid "data"
msgstr "дані"
@@ -46829,7 +47248,7 @@ msgid "error"
msgstr "помилка"
msgid "estimateCommand|%{slash_command} overwrites the total estimated time."
-msgstr ""
+msgstr "%{slash_command} перезапиÑує загальний запланований чаÑ."
msgid "example.com"
msgstr ""
@@ -46861,6 +47280,9 @@ msgstr "не вдалоÑÑ Ð²Ñ–Ð´Ñ…Ð¸Ð»Ð¸Ñ‚Ð¸ пов’Ñзану знахідÐ
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -47078,6 +47500,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr "Ñ” занадто довгим (%{current_value}). МакÑимальний розмір Ñкладає %{max_size}."
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -47182,7 +47607,7 @@ msgstr[1] "запити на злиттÑ"
msgstr[2] "запитів на злиттÑ"
msgstr[3] "запитів на злиттÑ"
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -47203,8 +47628,8 @@ msgstr "%{commitCount} і %{mergeCommitCount} буде додано до %{targe
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr "%{commitCount} буде додано в %{targetBranch}."
-msgid "mrWidgetCommitsAdded|1 merge commit"
-msgstr "1 коміт-злиттÑ"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
+msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
msgstr "Зміни злиті в %{targetBranch} з %{mergeCommitSha}%{squashedCommits}."
@@ -47370,6 +47795,9 @@ msgstr[1] "Задачі зі згадками"
msgstr[2] "Задач зі згадками"
msgstr[3] "Задач зі згадками"
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -47505,9 +47933,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47898,7 +48323,7 @@ msgid "specified top is not part of the tree"
msgstr "вказана вершина не Ñ” чаÑтиною дерева"
msgid "spendCommand|%{slash_command} adds or subtracts time already spent."
-msgstr ""
+msgstr "%{slash_command} додає або віднімає чаÑ."
msgid "ssh:"
msgstr "ssh:"
@@ -47952,7 +48377,7 @@ msgid "the following incidents or issues"
msgstr ""
msgid "the following issues"
-msgstr ""
+msgstr "наÑтупні задачі"
msgid "the wiki"
msgstr ""
@@ -48081,6 +48506,9 @@ msgstr "відхилено"
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr "щотижнÑ"
+
msgid "wiki page"
msgstr "вікі-Ñторінка"
diff --git a/locale/ur_PK/gitlab.po b/locale/ur_PK/gitlab.po
index 4af40d1d57c..99668716f67 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-08-12 18:02\n"
+"PO-Revision-Date: 2022-09-11 06:20\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/uz_UZ/gitlab.po b/locale/uz_UZ/gitlab.po
index e829736ebd2..41cfb1907e9 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-08-12 18:08\n"
+"PO-Revision-Date: 2022-09-11 06:25\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,11 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+msgstr[1] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -143,6 +148,11 @@ msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
msgstr[1] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -213,6 +223,11 @@ msgid_plural "%d contributions"
msgstr[0] ""
msgstr[1] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -363,6 +378,11 @@ msgid_plural "%d projects selected"
msgstr[0] ""
msgstr[1] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -398,11 +418,6 @@ msgid_plural "%d tags per image name"
msgstr[0] ""
msgstr[1] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -460,9 +475,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -891,12 +903,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -1003,6 +1009,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -1023,6 +1032,11 @@ msgstr[1] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -1054,9 +1068,6 @@ msgstr[1] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1264,11 +1275,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "(this user)"
msgstr ""
@@ -1602,6 +1608,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1665,6 +1677,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1833,6 +1851,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2148,7 +2169,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2175,6 +2196,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2214,6 +2238,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2265,6 +2292,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2436,6 +2469,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2451,9 +2487,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2652,15 +2685,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2700,7 +2733,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2724,6 +2757,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2889,7 +2925,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -3114,6 +3153,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3888,9 +3930,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3975,6 +4014,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -4026,6 +4068,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4164,6 +4209,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4301,9 +4349,6 @@ msgstr[1] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4313,13 +4358,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4864,6 +4909,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5329,6 +5377,11 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5344,10 +5397,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5365,7 +5424,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5386,6 +5445,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5395,9 +5457,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6196,12 +6255,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6217,12 +6270,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6274,9 +6321,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6318,9 +6362,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6356,6 +6397,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6564,6 +6608,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6690,6 +6743,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6855,6 +6914,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -7109,21 +7171,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -7133,9 +7186,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8387,6 +8437,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8414,12 +8470,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8474,6 +8542,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9160,10 +9231,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9334,6 +9405,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9400,6 +9474,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9522,6 +9599,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -10099,6 +10179,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10203,6 +10286,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10377,7 +10466,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10980,9 +11069,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11526,6 +11612,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11756,6 +11845,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11834,15 +11929,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11855,6 +11941,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11930,6 +12019,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11957,16 +12049,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11987,16 +12076,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -12065,13 +12151,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12259,7 +12345,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12445,6 +12531,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12496,6 +12585,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -13054,6 +13146,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13283,6 +13381,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13376,6 +13477,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13936,6 +14040,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13957,6 +14064,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13972,6 +14085,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14194,6 +14313,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14209,9 +14331,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14236,9 +14355,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14386,6 +14502,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14470,9 +14589,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14914,6 +15030,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14950,12 +15069,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -15022,18 +15135,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -15070,6 +15174,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -15103,9 +15210,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15181,6 +15285,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15612,6 +15719,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16362,9 +16472,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16575,13 +16682,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16731,6 +16838,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16799,6 +16909,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16811,13 +16924,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-msgstr[1] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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] ""
@@ -16960,9 +17068,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -17158,6 +17263,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17365,6 +17476,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17527,6 +17641,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17896,6 +18013,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17908,6 +18028,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17926,6 +18052,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17938,6 +18073,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17965,6 +18103,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18235,9 +18379,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18673,9 +18814,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18703,6 +18841,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -19066,13 +19207,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -19111,17 +19255,17 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
msgstr[1] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -19133,43 +19277,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19178,6 +19316,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19324,6 +19465,9 @@ msgid_plural "Hide charts"
msgstr[0] ""
msgstr[1] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19455,9 +19599,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19491,6 +19632,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19614,9 +19758,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19629,15 +19779,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19659,6 +19830,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19728,9 +19902,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19989,9 +20160,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20781,6 +20949,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20799,6 +20970,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20832,7 +21006,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20973,9 +21147,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21245,6 +21425,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21557,6 +21740,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21572,6 +21758,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21589,9 +21781,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21616,9 +21805,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21736,12 +21922,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -22060,9 +22252,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -22108,6 +22297,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -22123,12 +22315,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22186,9 +22372,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22198,9 +22381,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22213,9 +22393,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22510,9 +22687,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22947,7 +23121,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22962,6 +23136,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -23132,9 +23309,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23186,6 +23360,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23918,6 +24095,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24206,10 +24386,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24693,6 +24870,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24780,6 +24960,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24855,6 +25038,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25531,16 +25738,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25555,6 +25765,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25597,9 +25810,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25690,12 +25900,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26219,6 +26423,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26231,9 +26438,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26384,6 +26588,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26405,9 +26612,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26596,6 +26800,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26730,6 +26937,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26739,6 +26949,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26754,6 +26982,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26784,9 +27024,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26805,6 +27054,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26909,6 +27164,9 @@ msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be
msgstr[0] ""
msgstr[1] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -27128,6 +27386,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -27149,6 +27413,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27272,6 +27539,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27427,9 +27697,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27643,6 +27919,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27679,6 +27958,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27703,6 +27985,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27921,6 +28206,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27953,9 +28241,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27965,12 +28250,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -28072,7 +28351,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28321,9 +28600,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28399,6 +28675,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -29104,25 +29386,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -29134,18 +29407,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -29203,12 +29470,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29239,9 +29500,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29341,7 +29599,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29557,6 +29815,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29578,10 +29839,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29605,6 +29866,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29626,9 +29890,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -30040,9 +30301,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -30073,6 +30331,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30760,6 +31021,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30772,6 +31036,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30790,6 +31057,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30931,6 +31201,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31741,7 +32014,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31759,7 +32032,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31942,9 +32215,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -32068,9 +32338,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32301,6 +32568,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33669,6 +33939,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33684,6 +33957,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33732,6 +34011,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33779,6 +34061,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33803,6 +34088,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33848,24 +34136,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -34012,9 +34291,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -34098,9 +34374,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -34119,42 +34404,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34248,6 +34509,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34293,12 +34557,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34308,6 +34587,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34888,6 +35179,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -35143,6 +35437,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36527,12 +36824,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36806,6 +37112,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36896,6 +37205,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37280,6 +37592,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37295,9 +37610,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -38036,6 +38348,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39295,6 +39610,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39391,6 +39709,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39403,6 +39727,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39451,10 +39778,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39559,9 +39886,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39913,6 +40237,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39937,7 +40264,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40318,6 +40645,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40330,9 +40660,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40435,6 +40762,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40645,6 +40975,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40672,6 +41005,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40682,6 +41033,12 @@ msgid_plural "Time|mins"
msgstr[0] ""
msgstr[1] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40736,6 +41093,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40898,9 +41258,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40925,6 +41282,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40934,6 +41303,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -41042,9 +41414,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -41069,9 +41450,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -41240,39 +41633,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41378,21 +41744,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41972,6 +42335,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41999,6 +42365,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -42095,10 +42464,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -42164,7 +42533,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42326,6 +42695,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42727,7 +43099,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -43021,10 +43393,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43720,6 +44095,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43801,6 +44182,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43843,6 +44227,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43900,6 +44287,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43918,6 +44308,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43969,6 +44362,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -44180,9 +44576,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -44207,9 +44600,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44297,10 +44687,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44318,7 +44708,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44329,16 +44719,13 @@ msgstr[1] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44347,19 +44734,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr ""
+
+msgid "WorkItem|Expand tasks"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|Learn about tasks."
+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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
msgstr ""
msgid "WorkItem|Open"
@@ -44368,19 +44767,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44395,31 +44797,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn off confidentiality"
+msgstr ""
+
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44643,6 +45054,11 @@ 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 ""
@@ -44703,11 +45119,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44813,11 +45224,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-msgstr[1] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44982,7 +45388,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -45045,6 +45451,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -45156,9 +45565,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -45198,6 +45604,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45348,9 +45760,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45412,6 +45821,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45427,7 +45839,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45634,9 +46046,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45732,6 +46141,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45773,6 +46185,9 @@ msgid_plural "changes"
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 ""
@@ -45821,7 +46236,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -46021,7 +46436,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -46146,9 +46561,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -46185,6 +46597,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46309,6 +46724,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46518,6 +46936,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46618,7 +47039,7 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46639,7 +47060,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46802,6 +47223,9 @@ msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46937,9 +47361,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47501,6 +47922,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/vi_VN/gitlab.po b/locale/vi_VN/gitlab.po
index 008d0e5b4af..b2d6bde058d 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-08-12 18:01\n"
+"PO-Revision-Date: 2022-09-11 06:20\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,10 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -129,6 +133,10 @@ msgid "%d approver (you've approved)"
msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -185,6 +193,10 @@ msgid "%d contribution"
msgid_plural "%d contributions"
msgstr[0] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -305,6 +317,10 @@ msgid "%d project selected"
msgid_plural "%d projects selected"
msgstr[0] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -333,10 +349,6 @@ msgid "%d tag per image name"
msgid_plural "%d tags per image name"
msgstr[0] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -385,9 +397,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -806,12 +815,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -913,6 +916,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -932,6 +938,10 @@ msgstr[0] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -958,9 +968,6 @@ msgstr[0] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1165,10 +1172,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-
msgid "(this user)"
msgstr ""
@@ -1475,6 +1478,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1538,6 +1547,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1706,6 +1721,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2021,7 +2039,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2048,6 +2066,9 @@ msgstr ""
msgid "Add a table"
msgstr ""
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2087,6 +2108,9 @@ msgstr ""
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2138,6 +2162,12 @@ msgstr ""
msgid "Add label(s)"
msgstr ""
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2309,6 +2339,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2324,9 +2357,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2525,15 +2555,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2573,7 +2603,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2597,6 +2627,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2762,7 +2795,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -2987,6 +3023,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3761,9 +3800,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3848,6 +3884,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -3899,6 +3938,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4037,6 +4079,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4173,9 +4218,6 @@ msgstr[0] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr ""
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4185,13 +4227,13 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
-msgid "An error occurred while trying to run a new pipeline for this merge request."
+msgid "An error occurred while trying to render the content editor. Please try again."
msgstr ""
-msgid "An error occurred while trying to unfollow this user, please try again."
+msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
+msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
msgid "An error occurred while updating approvers"
@@ -4729,6 +4771,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5191,6 +5236,10 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5206,10 +5255,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5227,7 +5282,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5248,6 +5303,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5257,9 +5315,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6058,12 +6113,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6079,12 +6128,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6136,9 +6179,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6179,9 +6219,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6216,6 +6253,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6422,6 +6462,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6548,6 +6597,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr ""
@@ -6713,6 +6768,9 @@ msgstr ""
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -6966,21 +7024,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -6990,9 +7039,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8241,6 +8287,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8268,12 +8320,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8328,6 +8392,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9013,10 +9080,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9187,6 +9254,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9253,6 +9323,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr ""
@@ -9374,6 +9447,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -9949,6 +10025,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10052,6 +10131,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10226,7 +10311,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10829,9 +10914,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11375,6 +11457,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11604,6 +11689,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11682,15 +11773,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11703,6 +11785,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11778,6 +11863,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11805,16 +11893,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11835,16 +11920,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -11913,13 +11995,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12106,7 +12188,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12292,6 +12374,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12343,6 +12428,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -12895,6 +12983,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13122,6 +13216,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13215,6 +13312,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13771,6 +13871,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13792,6 +13895,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13807,6 +13916,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14029,6 +14144,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14044,9 +14162,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14071,9 +14186,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14221,6 +14333,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14305,9 +14420,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14749,6 +14861,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14785,12 +14900,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -14857,18 +14966,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr ""
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -14905,6 +15005,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -14938,9 +15041,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15016,6 +15116,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15446,6 +15549,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16193,9 +16299,6 @@ msgstr ""
msgid "February"
msgstr ""
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16406,13 +16509,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16562,6 +16665,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16629,6 +16735,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16641,12 +16750,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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"
@@ -16786,9 +16891,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -16984,6 +17086,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17191,6 +17299,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17353,6 +17464,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17722,6 +17836,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17734,6 +17851,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17752,6 +17875,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17764,6 +17896,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17791,6 +17926,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18061,9 +18202,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18499,9 +18637,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18529,6 +18664,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -18892,13 +19030,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -18937,15 +19078,15 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -18957,43 +19098,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19002,6 +19137,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19147,6 +19285,9 @@ msgid "Hide chart"
msgid_plural "Hide charts"
msgstr[0] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19277,9 +19418,6 @@ msgstr ""
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19313,6 +19451,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19436,9 +19577,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19451,15 +19598,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19481,6 +19649,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19550,9 +19721,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19808,9 +19976,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20600,6 +20765,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20618,6 +20786,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20651,7 +20822,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20792,9 +20963,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21063,6 +21240,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21375,6 +21555,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21390,6 +21573,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21406,9 +21595,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21433,9 +21619,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21553,12 +21736,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -21877,9 +22066,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -21925,6 +22111,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -21940,12 +22129,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22003,9 +22186,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22015,9 +22195,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22030,9 +22207,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22327,9 +22501,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22763,7 +22934,7 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
-msgid "Labels can be applied to issues and merge requests."
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
msgstr ""
msgid "Labels with no issues in this iteration:"
@@ -22778,6 +22949,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -22947,9 +23121,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23001,6 +23172,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23727,6 +23901,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24015,10 +24192,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24500,6 +24674,9 @@ msgstr ""
msgid "Merge requests"
msgstr ""
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24587,6 +24764,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24662,6 +24842,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr ""
@@ -25336,16 +25540,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr ""
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr ""
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25360,6 +25567,9 @@ msgstr ""
msgid "Months"
msgstr ""
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25402,9 +25612,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25495,12 +25702,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26022,6 +26223,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26034,9 +26238,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr ""
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26187,6 +26388,9 @@ msgstr ""
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26208,9 +26412,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26398,6 +26599,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr ""
@@ -26528,6 +26732,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26537,6 +26744,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26552,6 +26777,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26582,9 +26819,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26603,6 +26849,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26706,6 +26958,9 @@ msgid "On %{end_date}, your trial will end and %{namespace_name} will be limited
msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be limited to %{free_user_limit} members"
msgstr[0] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -26925,6 +27180,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -26946,6 +27207,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27069,6 +27333,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27223,9 +27490,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27439,6 +27712,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27475,6 +27751,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27499,6 +27778,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27716,6 +27998,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27747,9 +28032,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27759,12 +28041,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -27865,7 +28141,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28114,9 +28390,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28192,6 +28465,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -28897,25 +29176,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -28927,18 +29197,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -28996,12 +29260,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29032,9 +29290,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29134,7 +29389,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29350,6 +29605,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29371,10 +29629,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29398,6 +29656,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29419,9 +29680,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -29833,9 +30091,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -29866,6 +30121,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30553,6 +30811,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30565,6 +30826,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30583,6 +30847,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30724,6 +30991,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31534,7 +31804,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31552,7 +31822,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31735,9 +32005,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -31861,9 +32128,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32093,6 +32357,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33446,6 +33713,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33461,6 +33731,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33509,6 +33785,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33555,6 +33834,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33579,6 +33861,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33624,24 +33909,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -33787,9 +34063,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -33872,9 +34145,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -33893,42 +34175,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34022,6 +34280,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34067,12 +34328,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34082,6 +34358,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34651,6 +34939,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -34906,6 +35197,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36288,12 +36582,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36567,6 +36870,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36657,6 +36963,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37041,6 +37350,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37056,9 +37368,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -37797,6 +38106,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr ""
@@ -39046,6 +39358,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39142,6 +39457,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39154,6 +39475,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39202,10 +39526,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39310,9 +39634,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39664,6 +39985,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39688,7 +40012,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40069,6 +40393,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40081,9 +40408,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40186,6 +40510,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40396,6 +40723,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40423,6 +40753,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] ""
@@ -40431,6 +40779,12 @@ msgid "Time|min"
msgid_plural "Time|mins"
msgstr[0] ""
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr ""
@@ -40485,6 +40839,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40647,9 +41004,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40674,6 +41028,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40683,6 +41049,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -40791,9 +41160,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -40818,9 +41196,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -40989,39 +41379,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41127,21 +41490,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41721,6 +42081,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41748,6 +42111,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -41844,10 +42210,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -41913,7 +42279,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42075,6 +42441,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42475,7 +42844,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -42769,10 +43138,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43465,6 +43837,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43546,6 +43924,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43588,6 +43969,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43645,6 +44029,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43663,6 +44050,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43714,6 +44104,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -43923,9 +44316,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -43950,9 +44340,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44040,10 +44427,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44061,7 +44448,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44071,16 +44458,13 @@ msgstr[0] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44089,19 +44473,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
+msgstr ""
+
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44110,19 +44506,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44137,31 +44536,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
+msgstr ""
+
+msgid "WorkItem|Turn off confidentiality"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44384,6 +44792,10 @@ 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 ""
@@ -44444,10 +44856,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44553,10 +44961,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44720,7 +45124,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -44783,6 +45187,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -44894,9 +45301,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -44936,6 +45340,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45086,9 +45496,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45148,6 +45555,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45163,7 +45573,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45367,9 +45777,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45464,6 +45871,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45504,6 +45914,9 @@ msgid "change"
msgid_plural "changes"
msgstr[0] ""
+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 ""
@@ -45552,7 +45965,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -45748,7 +46161,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -45872,9 +46285,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -45911,6 +46321,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46033,6 +46446,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46238,6 +46654,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46336,7 +46755,7 @@ msgid "merge request"
msgid_plural "merge requests"
msgstr[0] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46357,7 +46776,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46518,6 +46937,9 @@ msgid "mrWidget|Mentions issue"
msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46653,9 +47075,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47211,6 +47630,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po
index f26612efe9b..e37d40a61ff 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-08-12 18:02\n"
+"PO-Revision-Date: 2022-09-11 06:20\n"
msgid " %{start} to %{end}"
msgstr "从%{start}到%{end}"
@@ -22,6 +22,10 @@ msgstr "从%{start}到%{end}"
msgid " (from %{timeoutSource})"
msgstr " (æ¥è‡ª %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] "(压缩 %{strongStart}%{count}%{strongEnd} 个æ交)"
+
msgid " Collected %{time}"
msgstr "收集于 %{time}"
@@ -129,6 +133,10 @@ msgid "%d approver (you've approved)"
msgid_plural "%d approvers (you've approved)"
msgstr[0] "%dä½æ ¸å‡†äºº(您已批准)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] "%d 个产物"
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] "%d 个已分é…的议题"
@@ -185,6 +193,10 @@ msgid "%d contribution"
msgid_plural "%d contributions"
msgstr[0] "%d个贡献"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] "%d 个贡献者"
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d天"
@@ -305,6 +317,10 @@ msgid "%d project selected"
msgid_plural "%d projects selected"
msgstr[0] "选择了%d个项目"
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] "%d 次推é€"
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] "剩余 %d"
@@ -333,10 +349,6 @@ msgid "%d tag per image name"
msgid_plural "%d tags per image name"
msgstr[0] "æ¯ä¸ªé•œåƒå称有%d个标签"
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] "%d个未分é…的议题"
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] "%d个未解决的主题"
@@ -385,9 +397,6 @@ msgstr "%{actionText} & é‡æ–°æ‰“å¼€ %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr "%{address}为无效的IP地å€èŒƒå›´"
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr "%{attribute} 必须介于 %{min} 和 %{max} 之间"
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr "%{author_link}将%{original_issue}克隆到%{new_issue}。"
@@ -806,12 +815,6 @@ 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}。"
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr "%{over_limit_message} è¦æŸ¥çœ‹å’Œç®¡ç†æˆå‘˜ï¼Œè¯·æŸ¥çœ‹æ¯ä¸ªä¸ªäººé¡¹ç›®çš„æˆå‘˜é¡µé¢ã€‚我们建议您%{link_start}将项目移动到群组%{link_end},以便您å¯ä»¥è½»æ¾ç®¡ç†ç”¨æˆ·å’Œç›¸å…³åŠŸèƒ½ã€‚"
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr "%{over_limit_message} è¦æŸ¥çœ‹å’Œç®¡ç†æˆå‘˜ï¼Œè¯·æ£€æŸ¥å‘½å空间中æ¯ä¸ªé¡¹ç›®çš„æˆå‘˜é¡µé¢ã€‚我们建议您%{link_start}将项目移动到群组%{link_end},以便您å¯ä»¥è½»æ¾ç®¡ç†ç”¨æˆ·å’Œç›¸å…³åŠŸèƒ½ã€‚"
-
msgid "%{percentageUsed}%% used"
msgstr "%{percentageUsed}%% 已被使用"
@@ -913,6 +916,9 @@ msgstr "%{size}字节"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr "%{sourceBranch}到%{targetBranch}"
+msgid "%{source} %{copyButton} into %{target}"
+msgstr "%{source} %{copyButton} 至 %{target}"
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr "%{spammable_titlecase} æˆåŠŸåœ°æ交给 Akismet。"
@@ -932,6 +938,10 @@ msgstr[0] "%{strongOpen}%{errors}%{strongClose} 点"
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr "%{strongOpen}警告:%{strongClose} SAML 群组链接会导致 GitLab 自动从群组中移除æˆå‘˜ã€‚"
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] "%{strongStart}%{count}%{strongEnd} æ交"
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr "%{strongStart}æ示:%{strongEnd} 您也å¯ä»¥åœ¨æœ¬åœ°æ£€å‡ºåˆå¹¶è¯·æ±‚。%{linkStart}了解更多。%{linkEnd}"
@@ -958,9 +968,6 @@ msgstr[0] "%{strong_start}%{count}ä½æˆå‘˜%{strong_end}必须核准åŽæ‰å¯ä»¥
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr "%{strong_start}%{human_size}%{strong_end} 项目存储"
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr "%{strong_start}%{project_name}%{strong_end} 是个人项目,因此您无法å‡çº§åˆ°ä»˜è´¹ç‰ˆæˆ–开始å…费试用æ¥è§£é™¤è¿™äº›é™åˆ¶ã€‚我们建议 %{move_to_group_link}将此项目移至群组%{end_link} æ¥è§£é”这些选项。您å¯ä»¥ %{manage_members_link}管ç†æ­¤é¡¹ç›®çš„æˆå‘˜%{end_link},但ä¸è¦å¿˜è®°æ‚¨ä¸ªäººå‘½å空间 %{strong_start}%{namespace_name}%{strong_end} 中的全部独有æˆå‘˜éƒ½è®¡å…¥ä½¿ç”¨çš„总席ä½ã€‚"
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] "%{strong_start}%{release_count}%{strong_end}个å‘布"
@@ -1165,10 +1172,6 @@ msgstr "(已删除)"
msgid "(revoked)"
msgstr "(已撤销)"
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] "(压缩 %d 个æ交)"
-
msgid "(this user)"
msgstr "(此用户)"
@@ -1475,6 +1478,12 @@ msgstr "使用Kotlin Nativeå¼€å‘Linux程åºçš„基本模æ¿"
msgid "A complete DevOps platform"
msgstr "一个完整的DevOpså¹³å°"
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr "ç§å¯†è®®é¢˜ä¸èƒ½è¢«åŒ…å«åœ¨å·²å«æœ‰éžç§å¯†å­é¡¹çš„父项中。"
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr "ç§å¯†å·¥ä½œé¡¹ä¸èƒ½è¢«åŒ…å«åœ¨å·²å«æœ‰éžç§å¯†å­é¡¹çš„父项中。"
+
msgid "A default branch cannot be chosen for an empty project."
msgstr "无法为空项目选择默认分支。"
@@ -1538,6 +1547,12 @@ msgstr "已创建一个新的个人访问令牌,å称为 %{token_name}。"
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr "éžç§å¯†å²è¯—ä¸èƒ½åˆ†é…ç»™ç§å¯†çš„父å²è¯—。"
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr "éžç§å¯†è®®é¢˜ä¸èƒ½æœ‰ç§å¯†çˆ¶é¡¹ã€‚"
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr "éžç§å¯†å·¥ä½œé¡¹ä¸èƒ½æœ‰ç§å¯†çˆ¶é¡¹ã€‚"
+
msgid "A page with that title already exists"
msgstr "该标题页é¢å·²å­˜åœ¨"
@@ -1706,6 +1721,9 @@ msgstr "文件夹/openapi.json"
msgid "AWS Access Key"
msgstr "AWS访问密钥"
+msgid "AWS OpenSearch IAM credentials"
+msgstr "AWS OpenSearch IAM 凭æ®"
+
msgid "AWS Secret Access Key"
msgstr "AWS秘密访问密钥"
@@ -2021,8 +2039,8 @@ msgstr "在此行添加评论或拖动以编辑多行"
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr "添加内部ç§å¯†å¤‡æ³¨åˆ°%{noteableDisplayName}。"
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
-msgstr "添加一个自定义消æ¯ï¼ŒåŒ…å«å®žä¾‹å…±äº«Runner的详细信æ¯ã€‚ 消æ¯åœ¨ç¾¤ç»„和项目 CI/CD 设置中å¯è§ï¼Œåœ¨Runner部分。支æŒMarkdown。"
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
+msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
msgstr "添加一般评论 %{noteableDisplayName}。"
@@ -2048,6 +2066,9 @@ msgstr "为æœåŠ¡å°ç”µå­é‚®ä»¶åœ°å€æ·»åŠ åŽç¼€ã€‚ %{linkStart}了解更多ã€
msgid "Add a table"
msgstr "添加表格"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr "添加标题..."
@@ -2087,6 +2108,9 @@ msgstr "ç«‹å³æ·»åŠ è¯„论"
msgid "Add comment to design"
msgstr "添加注释到设计"
+msgid "Add comment to incident timeline"
+msgstr "å‘事件时间线添加评论"
+
msgid "Add comment..."
msgstr "添加评论"
@@ -2121,7 +2145,7 @@ msgid "Add existing confidential %{issuableType}"
msgstr "添加现有的ç§å¯†%{issuableType}"
msgid "Add existing issue"
-msgstr ""
+msgstr "添加现有议题"
msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
msgstr "添加页眉和页脚到电å­é‚®ä»¶ã€‚请注æ„,颜色设置仅适用于应用程åºç•Œé¢"
@@ -2138,6 +2162,12 @@ msgstr "添加密钥"
msgid "Add label(s)"
msgstr "添加标记"
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr "添加列表"
@@ -2309,6 +2339,9 @@ msgstr "添加%{labels}%{label_text}。"
msgid "Adds a Zoom meeting."
msgstr "添加 Zoom 会议。"
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr "添加一个待办事项."
@@ -2324,9 +2357,6 @@ msgstr "添加与创建它的 %{issuable_type} å…³è”çš„æ­¤ %{issuable_type}"
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr "调整 GitLab UI 轮询更新的频率。"
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr "请您调整上é¢çš„筛选器/æœç´¢æ¡ä»¶ã€‚如果您认为此处有误,请å‚阅 %{linkStart}Geo故障排除%{linkEnd}文档以获å–更多信æ¯ã€‚"
-
msgid "Admin"
msgstr "管ç†å‘˜"
@@ -2525,15 +2555,15 @@ msgstr "默认情况下,所有新项目都å¯ä»¥ä½¿ç”¨å®žä¾‹çš„共享Runner。
msgid "AdminSettings|Auto DevOps domain"
msgstr "Auto DevOps 域"
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr "自动å°ç¦åœ¨ç‰¹å®šæ—¶é—´å†…下载超过指定数é‡çš„仓库的用户。"
-
msgid "AdminSettings|CI/CD limits"
msgstr "CI/CD é™åˆ¶"
msgid "AdminSettings|Configure Let's Encrypt"
msgstr "é…ç½® Let's Encrypt"
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr "é…置何时自动删除ä¸æ´»è·ƒçš„项目。%{linkStart}什么是ä¸æ´»è·ƒçš„项目?%{linkEnd}"
@@ -2573,8 +2603,8 @@ msgstr "å¯ç”¨æ³¨å†ŒåŠŸèƒ½"
msgid "AdminSettings|Enable Service Ping"
msgstr "å¯ç”¨æœåŠ¡Ping"
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
-msgstr "å¯ç”¨å…¬å¼€è¿è¡ŒçŠ¶å†µå’Œæ€§èƒ½ç»Ÿè®¡ä¿¡æ¯çš„ Prometheus 端点。å¥åº·æ£€æŸ¥èœå•é¡¹å‡ºçŽ°åœ¨ç®¡ç†ä¸­å¿ƒçš„监控部分。需è¦é‡æ–°å¯åŠ¨ã€‚%{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
+msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
msgstr "å¯ç”¨ kuromoji 自定义分æžå™¨ï¼šç´¢å¼•"
@@ -2597,6 +2627,9 @@ msgstr "å¯ç”¨smartcn自定义分æžå™¨ï¼šæœç´¢"
msgid "AdminSettings|Enabled"
msgstr "å·²å¯ç”¨"
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr "ä¿¡æ¯æµä»¤ç‰Œ"
@@ -2762,8 +2795,11 @@ msgstr "为了帮助改善 GitLab åŠå…¶ç”¨æˆ·ä½“验,GitLab 会定期收集使
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr "当å‰æ´»åŠ¨æµæ°´çº¿ä¸­çš„作业总数"
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
-msgstr "使用 AWS 托管的 IAM 凭æ®çš„ Elasticsearch"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
+msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
msgstr "当暂åœæ—¶ï¼Œç³»ç»Ÿä»åœ¨è·Ÿè¸ªæ›´æ”¹ï¼Œå¯¹äºŽé›†ç¾¤/索引è¿ç§»æ˜¯æœ‰ç”¨çš„。"
@@ -2987,6 +3023,9 @@ msgstr "由该用户创建的议题对其他用户ä¸å¯è§ã€‚"
msgid "AdminUsers|It's you!"
msgstr "自己ï¼"
+msgid "AdminUsers|LDAP Blocked"
+msgstr "å·²ç¦ç”¨ LDAP"
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr "了解更多关于 %{link_start}å°ç¦ç”¨æˆ·ã€‚%{link_end}"
@@ -3761,9 +3800,6 @@ msgstr "å…许用户注册任何应用程åºï¼Œä½¿ç”¨ GitLab 作为 OAuth æä¾›
msgid "Allowed"
msgstr "å·²å…许"
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr "å…许的字符:+ã€0-9ã€- 和空格。"
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr "å…许使用电å­é‚®ä»¶åŸŸåé™åˆ¶ä»…å¯ç”¨äºŽé¡¶çº§ç¾¤ç»„"
@@ -3848,6 +3884,9 @@ msgstr "应用%{link_to_client}请求访问您的 GitLab å¸æˆ·ã€‚"
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr "已从管ç†é¢æ¿å‘é€è¿‡äº†ä¸€å°ç”µå­é‚®ä»¶é€šçŸ¥ã€‚请等待 %{wait_time_in_words} åŽå†æ¬¡å°è¯•å‘é€ã€‚"
+msgid "An email will be sent with the report attached after it is generated."
+msgstr "报告生æˆåŽï¼Œå°†å‘é€ä¸€å°é™„有此报告的电å­é‚®ä»¶ã€‚"
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr "空GitLab用户字段将在所有问题和注释的æ述中添加FogBugz用户的全å(例如“By John Smithâ€ï¼‰ã€‚它还将与项目创建者关è”å’Œ/或分é…这些问题和评论。"
@@ -3899,6 +3938,9 @@ msgstr "添加核准人时å‘生错误"
msgid "An error occurred while adding formatted title for epic"
msgstr "为å²è¯—添加格å¼åŒ–标题时å‘生错误"
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr "授æƒæ‚¨çš„角色时出错"
@@ -4037,6 +4079,9 @@ msgstr "加载此页é¢çš„æŸéƒ¨åˆ†æ—¶å‡ºé”™ã€‚"
msgid "An error occurred while loading all the files."
msgstr "加载所有文件时å‘生错误。"
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr "加载图表数æ®æ—¶å‘生错误"
@@ -4173,9 +4218,6 @@ msgstr[0] "ä¿å­˜è®¾ç½®æ—¶å‘生错误。"
msgid "An error occurred while saving your settings. Try saving them again."
msgstr "ä¿å­˜æ‚¨çš„设置时å‘生错误。请å°è¯•å†æ¬¡ä¿å­˜ã€‚"
-msgid "An error occurred while subscribing to notifications."
-msgstr "订阅通知时å‘生错误。"
-
msgid "An error occurred while triggering the job."
msgstr "触å‘作业时å‘生错误。"
@@ -4185,15 +4227,15 @@ msgstr "试图关注这个用户时出错,请é‡è¯•ã€‚"
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr "å°è¯•ç”ŸæˆæŠ¥å‘Šæ—¶å‡ºé”™ã€‚请ç¨åŽå†è¯•ã€‚"
+msgid "An error occurred while trying to render the content editor. Please try again."
+msgstr ""
+
msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr "å°è¯•ä¸ºæ­¤åˆå¹¶è¯·æ±‚è¿è¡Œæ–°æµæ°´çº¿æ—¶å‘生错误。"
msgid "An error occurred while trying to unfollow this user, please try again."
msgstr "试图å–消关注这个用户时出错,请é‡è¯•ã€‚"
-msgid "An error occurred while unsubscribing to notifications."
-msgstr "å–消订阅通知时å‘生错误。"
-
msgid "An error occurred while updating approvers"
msgstr "更新核准人时å‘生错误"
@@ -4729,6 +4771,9 @@ msgstr "批准"
msgid "Approve a merge request"
msgstr "批准åˆå¹¶è¯·æ±‚"
+msgid "Approve merge request"
+msgstr "批准åˆå¹¶è¯·æ±‚"
+
msgid "Approve the current merge request."
msgstr "批准当å‰åˆå¹¶è¯·æ±‚。"
@@ -5191,6 +5236,10 @@ msgstr "本月"
msgid "AuditLogs|User Events"
msgstr "用户事件"
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] "%d 个目的地"
+
msgid "AuditStreams|A header with this name already exists."
msgstr "æ­¤å称的 header 已存在。"
@@ -5206,11 +5255,17 @@ msgstr "添加自定义值"
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr "添加一个 HTTP 端点æ¥ç®¡ç†ç¬¬ä¸‰æ–¹ç³»ç»Ÿä¸­çš„审计日志。"
+msgid "AuditStreams|Add another custom header"
+msgstr "添加å¦ä¸€ä¸ªè‡ªå®šä¹‰ header"
+
msgid "AuditStreams|Add external stream destination"
msgstr "添加外部事件æµç›®çš„地"
-msgid "AuditStreams|Add stream"
-msgstr "添加事件æµ"
+msgid "AuditStreams|Add header"
+msgstr "添加 header"
+
+msgid "AuditStreams|Add streaming destination"
+msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
msgstr "创建外部审计事件æµç›®çš„地时出错。请å†è¯•ä¸€æ¬¡ã€‚"
@@ -5227,8 +5282,8 @@ msgstr "创建外部审计事件æµç›®çš„地时出错。请å†è¯•ä¸€æ¬¡ã€‚"
msgid "AuditStreams|Cancel editing"
msgstr "å–消编辑"
-msgid "AuditStreams|Custom HTTP headers"
-msgstr "自定义 HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
+msgstr ""
msgid "AuditStreams|Delete %{link}"
msgstr "删除 %{link}"
@@ -5248,6 +5303,9 @@ msgstr "Header"
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr "已达到最多 %{number} 个 HTTP headers。"
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr "添加外部事件æµç›®çš„地"
@@ -5257,9 +5315,6 @@ msgstr "为审计事件设置事件æµ"
msgid "AuditStreams|Stream added successfully"
msgstr "添加事件æµæˆåŠŸ"
-msgid "AuditStreams|Stream count icon"
-msgstr "事件æµè®¡æ•°"
-
msgid "AuditStreams|Stream deleted successfully"
msgstr "å·²æˆåŠŸåˆ é™¤äº‹ä»¶æµ"
@@ -5807,7 +5862,7 @@ msgid "Billing"
msgstr "计费"
msgid "BillingPlans|%{group_name} is currently using the %{plan_name}"
-msgstr "%{group_name} 正在使用 %{plan_name} 方案"
+msgstr "%{group_name} 正在使用 %{plan_name}"
msgid "BillingPlans|10,000 CI/CD minutes per month"
msgstr "æ¯æœˆ 10,000 CI/CD 分钟数"
@@ -5981,7 +6036,7 @@ msgid "BillingPlans|You'll have to %{move_link_start}move this project%{move_lin
msgstr "æ‚¨éœ€è¦ %{move_link_start}将此项目移至%{move_link_end} 您的其中一个群组。"
msgid "BillingPlans|Your GitLab.com %{plan} trial will %{strong_open}expire after %{expiration_date}%{strong_close}. You can retain access to the %{plan} features by upgrading below."
-msgstr "您的GitLab.com%{plan}试用将在%{strong_open}%{expiration_date}过期%{strong_close}。您å¯ä»¥é€šè¿‡ä»¥ä¸‹æ–¹å¼å‡çº§ä»¥ä¿ç•™å¯¹%{plan}功能的访问æƒé™ã€‚"
+msgstr "您的%{plan}试用将在%{strong_open}%{expiration_date}过期%{strong_close}。您å¯ä»¥é€šè¿‡ä»¥ä¸‹æ–¹å¼å‡çº§ä»¥ä¿ç•™å¯¹%{plan}功能的访问æƒé™ã€‚"
msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. You can restore access to the features at any time by upgrading below."
msgstr "您的GitLab.com试用已于%{expiration_date}到期。您å¯ä»¥é€šè¿‡ä»¥ä¸‹å‡çº§éšæ—¶æ¢å¤å¯¹åŠŸèƒ½çš„访问。"
@@ -6058,12 +6113,6 @@ msgstr "正在使用的席ä½æ•° / 订阅的席ä½æ•°"
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr "Shared runners 无法å¯ç”¨ï¼Œç›´åˆ°æœ‰æ•ˆçš„信用å¡åœ¨æ¡£ã€‚"
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr "ä¸èƒ½ç§»é™¤å¸­ä½ä¸­çš„最åŽä¸€ä½æ‹¥æœ‰è€…。"
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr "è¦ä½¿æ­¤æˆå‘˜å¤„于å¯ç”¨çŠ¶æ€ï¼Œæ‚¨å¿…须先删除现有的活动æˆå‘˜ï¼Œæˆ–将其切æ¢ä¸ºè¶…出é™åˆ¶ã€‚"
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr "è¦åœ¨ shared runners上使用å…è´¹ CI/CD 分钟,您需è¦ä½¿ç”¨ä¿¡ç”¨å¡éªŒè¯æ‚¨çš„å¸æˆ·ã€‚如果您ä¸æƒ³æ供,您å¯ä»¥é€šè¿‡ä¸ºæ‚¨çš„项目带æ¥è‡ªå·±çš„ runner 并ç¦ç”¨ shared runners æ¥è¿è¡Œæµæ°´çº¿ã€‚这是阻止和å‡å°‘对 GitLab 基础设施的滥用所必需的。 %{strongStart}GitLab ä¸ä¼šå¯¹æ‚¨çš„å¡å·æ‰£è´¹ï¼Œå®ƒåªä¼šç”¨äºŽéªŒè¯æ‚¨çš„身份。%{strongEnd} %{linkStart}了解更多%{linkEnd}"
@@ -6079,12 +6128,6 @@ msgstr "验è¯å¸æˆ·"
msgid "Billings|Validate user account"
msgstr "验è¯ç”¨æˆ·å¸æˆ·"
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr "您无法更改通过群组或项目邀请的用户的席ä½çŠ¶æ€ã€‚"
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr "您无法将自己从席ä½ä¸Šç§»é™¤ï¼Œä½†æ‚¨å¯ä»¥ç¦»å¼€ç¾¤ç»„。"
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr "您现在å¯ä»¥åˆ©ç”¨å…±äº« runner çš„å…è´¹ CI/CD 分钟。"
@@ -6136,9 +6179,6 @@ msgstr "æµè§ˆæ‰€æœ‰æ–¹æ¡ˆ"
msgid "Billing|Export list"
msgstr "导出列表"
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr "从 2022 å¹´ 10 月 19 日起,å…费群组将é™åˆ¶æœ€å¤š5åæˆå‘˜åŠ å…¥"
-
msgid "Billing|Group invite"
msgstr "群组邀请"
@@ -6179,9 +6219,6 @@ msgstr "查看待审批"
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr "您将è¦ä»Žè®¢é˜…中删除用户%{username}。如果继续,该用户将从 %{namespace} 群组åŠå…¶æ‰€æœ‰å­ç»„和项目中删除。此æ“作无法撤消。"
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr "您现在å¯ä»¥å¼€å§‹ç§»åŠ¨ %{namespaceName} 中的æˆå‘˜ã€‚当您关闭 %{strongStart}ä¸€ä¸ªå¸­ä½ %{strongEnd} 时,æˆå‘˜å°†å¤±åŽ»å¯¹ç¾¤ç»„çš„å­˜å–æƒé™ã€‚如果 2022 å¹´10月 19 日之å‰å¯ç”¨äº†è¶…过 5 åæˆå‘˜çš„ %{strongStart}席ä½%{strongEnd},我们将选择 5 åæˆå‘˜ä¿æŒå­˜å–æƒé™ã€‚我们将首先ä¿ç•™å…·æœ‰æ‹¥æœ‰è€…和维护者角色的æˆå‘˜ï¼ŒæŽ¥ç€æ˜¯æœ€è¿‘活跃的æˆå‘˜ï¼Œç›´åˆ°è¾¾åˆ° 5 个æˆå‘˜ã€‚其余æˆå‘˜å°†å˜ä¸ºâ€œè¶…出é™åˆ¶â€çš„状æ€å¹¶å¤±åŽ»å¯¹ç¾¤ç»„的访问æƒé™ã€‚"
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr "您的群组最近更改为使用å…费版。%{over_limit_message}您å¯ä»¥é€šè¿‡åˆ é™¤ä¸å†éœ€è¦è®¿é—®æƒé™çš„æˆå‘˜ï¼Œæˆ–将其切æ¢ä¸ºè¶…é™çŠ¶æ€ï¼Œæ¥ä¸ºæ–°æˆå‘˜é‡Šæ”¾å¸­ä½ã€‚è¦èŽ·å¾—æ— é™æ•°é‡çš„会员,您å¯ä»¥%{link_start}å‡çº§%{link_end}到付费版。"
@@ -6216,6 +6253,9 @@ msgstr "å—阻的议题"
msgid "Blocking"
msgstr "阻塞"
+msgid "Blocking epics"
+msgstr "ç¦ç”¨å²è¯—"
+
msgid "Blocking issues"
msgstr "阻塞议题"
@@ -6422,6 +6462,15 @@ msgstr "展开"
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr "无法获å–ç¦ç”¨çš„ %{issuableType}"
+msgid "Boards|Move card"
+msgstr "移动å¡ç‰‡"
+
+msgid "Boards|Move to end of list"
+msgstr "移动至列表末尾"
+
+msgid "Boards|Move to start of list"
+msgstr "移动至列表开头"
+
msgid "Boards|New board"
msgstr "新建看æ¿"
@@ -6548,6 +6597,12 @@ msgstr "æ‹’ç»ä»£ç æŽ¨é€æ›´æ”¹ CODEOWNERS 文件中列出的文件。"
msgid "BranchRules|Require approval from code owners."
msgstr "需è¦ä»£ç æ‰€æœ‰è€…的核准。"
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "分支"
@@ -6713,6 +6768,9 @@ msgstr "æµè§ˆæ–‡ä»¶"
msgid "Browse templates"
msgstr "æµè§ˆæ¨¡æ¿"
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr "获å–产物时出错"
@@ -6966,21 +7024,12 @@ msgstr "å‘布统计"
msgid "CICDAnalytics|Releases"
msgstr "å‘布"
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr "共享 Runner 的使用情况"
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr "共享 Runner 的时长是在共享 Runner 上è¿è¡Œçš„所有作业的总è¿è¡Œæ—¶é—´"
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr "共享 runner æµæ°´çº¿åˆ†é’ŸæŒç»­æ—¶é—´ï¼ˆæŒ‰æœˆï¼‰"
-msgid "CICDAnalytics|Shared runner usage"
-msgstr "共享 Runner 的使用情况"
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr "共享 Runner 的使用é‡æ˜¯åœ¨å…±äº« Runner 上è¿è¡Œçš„所有作业的总è¿è¡Œæ—¶é—´"
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr "获å–å‘布统计信æ¯æ—¶å‡ºçŽ°é”™è¯¯"
@@ -6990,9 +7039,6 @@ msgstr "æ¢å¤æœåŠ¡çš„时间"
msgid "CICDAnalytics|What is shared runner duration?"
msgstr "什么是共享 Runner 的时长?"
-msgid "CICDAnalytics|What is shared runner usage?"
-msgstr "什么是共享 Runner 的使用情况?"
-
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 "请将%{base_domain_link_start}基础域å%{link_end}添加到%{kubernetes_cluster_link_start}Kubernetes集群%{link_end},æ‰èƒ½ä½¿æ‚¨çš„部署策略正常工作。"
@@ -8241,6 +8287,12 @@ msgstr "关闭此%{quick_action_target}."
msgid "Cloud Run"
msgstr "Cloud Run"
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr "云存储"
@@ -8268,12 +8320,24 @@ msgstr "Cloud SQL for Postgres"
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr "Cloud SQL for SQL Server"
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr "CloudSQL 实例"
msgid "CloudSeed|Configuration"
msgstr "é…ç½®"
+msgid "CloudSeed|Create MySQL Instance"
+msgstr "创建 MySQL 实例"
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr "创建 Postgres 实例"
+
msgid "CloudSeed|Create cluster"
msgstr "创建集群"
@@ -8328,6 +8392,9 @@ msgstr "é¢å‘ SQL Server 的全托管关系数æ®åº“æœåŠ¡"
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr "已生æˆçš„æœåŠ¡å¸æˆ·å·²é“¾æŽ¥åˆ°æ‰€é€‰åˆ†æ”¯æˆ–标签"
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr "Google Cloud å‘生错误 - %{message}"
+
msgid "CloudSeed|Google Cloud Project"
msgstr "Google Cloud Project"
@@ -9013,11 +9080,11 @@ msgstr "用于对群集进行身份验è¯çš„ Kubernetes è¯ä¹¦ã€‚"
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr "用于访问 Kubernetes API 的 URL。"
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
-msgstr "基于è¯ä¹¦çš„ Kubernetes 集æˆå·²è¢«å¼ƒç”¨ï¼Œå°†äºŽ 2022 å¹´ 11 月关闭,请%{linkStart}è¿ç§»åˆ°é€‚用于 Kubernetes 的代ç†%{linkEnd}或è”系技术支æŒã€‚"
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
-msgstr "基于è¯ä¹¦çš„ Kubernetes 集æˆå·²è¢«å¼ƒç”¨ï¼Œå°†äºŽ 2022 å¹´ 11 月关闭,请%{linkStart}è¿ç§»åˆ°é€‚用于 Kubernetes 的代ç†%{linkEnd}。"
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
msgstr "将集群连接到 GitLab 的基于è¯ä¹¦çš„方法%{linkStart}弃用%{linkEnd}于 14.5 版本。"
@@ -9187,6 +9254,9 @@ msgstr "折å è®®é¢˜"
msgid "Collapse jobs"
msgstr "折å ä½œä¸š"
+msgid "Collapse merge details"
+msgstr "折å åˆå¹¶è¯¦æƒ…"
+
msgid "Collapse milestones"
msgstr "折å é‡Œç¨‹ç¢‘"
@@ -9253,6 +9323,9 @@ msgstr "评论并将主题置为未解决"
msgid "Comment '%{label}' position"
msgstr "评论%{label}'çš„ä½ç½®"
+msgid "Comment added to the timeline."
+msgstr "评论已添加到时间线。"
+
msgid "Comment form position"
msgstr "评论框ä½ç½®"
@@ -9374,6 +9447,9 @@ msgstr "比较 GitLab 版本"
msgid "Compare Revisions"
msgstr "比较版本"
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr "比较å˜æ›´"
@@ -9949,6 +10025,9 @@ msgstr "摘è¦: %{imageId}"
msgid "ContainerRegistry|Docker connection error"
msgstr "Docker连接错误"
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr "å¯ç”¨è¿‡æœŸç­–ç•¥"
@@ -10052,6 +10131,12 @@ msgstr "è¿è¡Œæ¸…ç†ï¼š"
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr "通过自动从容器镜åƒåº“中删除标签,并ä¿ç•™æ‚¨æƒ³è¦çš„标签æ¥èŠ‚çœå­˜å‚¨ç©ºé—´ã€‚%{linkStart}清ç†å·¥ä½œå¦‚何进行?%{linkEnd}"
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr "设置清ç†"
@@ -10226,8 +10311,8 @@ msgstr "创建%{created_count}个,关闭%{closed_count}个。"
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr "已创建%{created_count}个,已åˆå¹¶%{merged_count}个,已关闭%{closed_count}个。"
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
-msgstr "%{pushes}次推é€ï¼Œè¶…过%{people}个贡献者的%{commits}次æ交。"
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
+msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
msgstr "自%{start_date}起的议题,åˆå¹¶è¯·æ±‚和推é€äº‹ä»¶çš„贡献分æž"
@@ -10829,9 +10914,6 @@ msgstr "创建您的群组"
msgid "Create, update, or delete a merge request."
msgstr "创建ã€æ›´æ–°æˆ–删除åˆå¹¶è¯·æ±‚。"
-msgid "Create/import your first project"
-msgstr "创建/导入您的第一个项目"
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr "您无æƒåœ¨æ­¤ç¾¤ç»„中创建å­ç¾¤ç»„。"
@@ -11073,7 +11155,7 @@ msgid "Critical vulnerabilities present"
msgstr "存在严é‡æ¼æ´ž"
msgid "Crm|Active"
-msgstr ""
+msgstr "活动"
msgid "Crm|Contact"
msgstr "è”系人"
@@ -11375,6 +11457,9 @@ msgstr "'%{name}' 正在收集数æ®ã€‚è¿™å¯èƒ½éœ€è¦å‡ åˆ†é’Ÿã€‚"
msgid "CycleAnalytics|Average time to completion"
msgstr "å¹³å‡å®Œæˆæ—¶é—´"
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr "更改失败率"
@@ -11604,6 +11689,12 @@ 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消æ¯(请求和å“应)。主动扫æ会对目标进行攻击以å‘现潜在æ¼æ´žã€‚"
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr "AJAX爬虫"
@@ -11682,15 +11773,6 @@ msgstr "调试消æ¯"
msgid "DastProfiles|Delete profile"
msgstr "删除é…ç½®"
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr "您è¦æ”¾å¼ƒæ­¤æ‰«æ工具é…ç½®å—?"
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr "您è¦æ”¾å¼ƒæ­¤ç«™ç‚¹é…ç½®å—?"
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr "您è¦æ”¾å¼ƒæ›´æ”¹å—?"
-
msgid "DastProfiles|Edit profile"
msgstr "编辑é…置文件"
@@ -11703,6 +11785,9 @@ msgstr "编辑站点é…ç½®"
msgid "DastProfiles|Enable Authentication"
msgstr "å¯ç”¨èº«ä»½éªŒè¯"
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr "å¯ç”¨åŸºæœ¬èº«ä»½éªŒè¯"
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr "在逗å·åˆ†éš”列表中输入 URL。"
@@ -11778,6 +11863,9 @@ msgstr "密ç "
msgid "DastProfiles|Password form field"
msgstr "密ç è¡¨å•å­—段"
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr "个人资料正在使用中,无法é‡å‘½å"
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr "此按需扫æ正在使用é…置文件"
@@ -11805,17 +11893,14 @@ msgstr "扫æ方法"
msgid "DastProfiles|Scan mode"
msgstr "扫æ模å¼"
-msgid "DastProfiles|Scanner Profile"
-msgstr "扫æ工具é…ç½®"
-
-msgid "DastProfiles|Scanner Profiles"
-msgstr "扫æ工具é…ç½®"
-
msgid "DastProfiles|Scanner name"
msgstr "扫æ工具å称"
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
-msgstr "扫æ器é…置文件定义了安全扫æ器的é…置详细信æ¯ã€‚%{linkStart}了解更多%{linkEnd}。"
+msgid "DastProfiles|Scanner profile"
+msgstr ""
+
+msgid "DastProfiles|Scanner profiles"
+msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
msgstr "选择扫æ器é…置文件æ¥è¿è¡Œ DAST 扫æ"
@@ -11835,17 +11920,14 @@ msgstr "选择站点é…置文件"
msgid "DastProfiles|Show debug messages"
msgstr "显示调试消æ¯"
-msgid "DastProfiles|Site Profile"
-msgstr "站点é…ç½®"
-
-msgid "DastProfiles|Site Profiles"
-msgstr "站点é…ç½®"
-
msgid "DastProfiles|Site name"
msgstr "站点å称"
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
-msgstr "站点é…置文件定义您部署的应用程åºã€ç½‘站或 API 的属性和é…置详细信æ¯ã€‚%{linkStart}了解更多%{linkEnd}。"
+msgid "DastProfiles|Site profile"
+msgstr ""
+
+msgid "DastProfiles|Site profiles"
+msgstr ""
msgid "DastProfiles|Site type"
msgstr "站点类型"
@@ -11913,14 +11995,14 @@ msgstr "您å¯ä»¥é€‰æ‹©è¢«åŠ¨æ‰«æ或从站点é…置文件管ç†é¡µé¢éªŒè¯ç›®
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr "ä¸èƒ½å¯¹æœªç»éªŒè¯çš„站点è¿è¡Œä¸»åŠ¨æ‰«æ。"
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
-msgstr "folder/dast_example.har 或 https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
+msgstr "https://example.com/dast_example.har"
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
-msgstr "folder/example.postman_collection.json 或 https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
+msgstr "https://exemple.com/openapi.json"
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
-msgstr "folder/openapi.json 或 https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
+msgstr "https://example.com/postman_collection.json"
msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr "å¤åˆ¶HTTP报头到剪贴æ¿"
@@ -12106,8 +12188,8 @@ msgstr "天"
msgid "Days to merge"
msgstr "åˆå¹¶æ‰€éœ€å¤©æ•°"
-msgid "Deactivate dormant users after 90 days of inactivity"
-msgstr "闲置 90 天åŽå†»ç»“休眠用户"
+msgid "Deactivate dormant users after a period of inactivity"
+msgstr "无活动一段时间冻结休眠用户"
msgid "Dear Administrator,"
msgstr "亲爱的管ç†å‘˜ï¼Œ"
@@ -12292,6 +12374,9 @@ msgstr "删除语料库"
msgid "Delete deploy key"
msgstr "删除部署密钥"
+msgid "Delete epic"
+msgstr "删除å²è¯—"
+
msgid "Delete file"
msgstr "删除文件"
@@ -12343,6 +12428,9 @@ msgstr "删除代ç ç‰‡æ®µå—?"
msgid "Delete source branch"
msgstr "删除æºåˆ†æ”¯"
+msgid "Delete source branch when merge request is accepted."
+msgstr "接å—åˆå¹¶è¯·æ±‚时删除æºåˆ†æ”¯ã€‚"
+
msgid "Delete subscription"
msgstr "删除订阅"
@@ -12895,6 +12983,12 @@ msgstr "环境:%{environment}"
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr "手动作业:%{jobName}"
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr "GitLab Pages"
@@ -13122,6 +13216,9 @@ msgstr "设计"
msgid "DesignManagement|Discard comment"
msgstr "放弃评论"
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr "下载设计"
@@ -13215,6 +13312,9 @@ msgstr "DevOps 报告"
msgid "DevOps adoption"
msgstr "DevOps adoption"
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr "设备(å¯é€‰ï¼‰"
@@ -13771,6 +13871,9 @@ msgstr "è‰ç¨¿"
msgid "Draft: %{filename}"
msgstr "Draft:%{filename}"
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr "在列表中上下拖动优先标记以更改它们的优先级。"
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr "将您的设计拖到此处,或 %{linkStart}点击上传%{linkEnd}。"
@@ -13792,6 +13895,12 @@ msgstr "获å–所选%{issuableType}çš„%{issuableAttribute}æ—¶å‘生错误。"
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr "åˆ†é… %{issuableAttribute}"
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr "无法为此%{issuableType}获å–%{issuableAttribute},请é‡è¯•ã€‚"
@@ -13807,6 +13916,12 @@ msgstr "未找到 %{issuableAttribute}"
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr "找ä¸åˆ°å¼€æ”¾çš„ %{issuableAttribute}"
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr "截止日期"
@@ -14029,6 +14144,9 @@ msgstr "编辑Wiki页é¢"
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr "编辑你在最近主题中的评论(从空白文本区)"
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr "编辑ã€æ•´ç†å’Œå¯è§†åŒ–您的æµæ°´çº¿ã€‚"
@@ -14044,9 +14162,6 @@ msgstr "编辑中"
msgid "Elapsed time"
msgstr "已用时间"
-msgid "Elasticsearch AWS IAM credentials"
-msgstr "Elasticsearch AWS IAM凭æ®"
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr "Elasticsearch HTTP客户端超时值,以秒为å•ä½ã€‚"
@@ -14071,9 +14186,6 @@ msgstr "Elasticsearch é‡å»ºç´¢å¼•å°šæœªå¼€å§‹ï¼š%{errors}"
msgid "Elasticsearch zero-downtime reindexing"
msgstr "Elasticsearchä¸åœæœºé‡å»ºç´¢å¼•"
-msgid "Elasticsearch's region."
-msgstr "Elasticsearch 的区域。"
-
msgid "Elastic|None. Select namespaces to index."
msgstr "无。请选择è¦å»ºç«‹ç´¢å¼•çš„命å空间。"
@@ -14221,6 +14333,9 @@ msgstr "å¯ç”¨Auto DevOps"
msgid "Enable GitLab Error Tracking"
msgstr "å¯ç”¨é”™è¯¯è·Ÿè¸ª"
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr "å¯ç”¨Gitpod"
@@ -14305,9 +14420,6 @@ msgstr "å¯ç”¨ç¾¤ç»„Runner"
msgid "Enable header and footer in emails"
msgstr "在电å­é‚®ä»¶ä¸­å¯ç”¨é¡µçœ‰å’Œé¡µè„š"
-msgid "Enable health and performance metrics endpoint"
-msgstr "å¯ç”¨è¿è¡ŒçŠ¶å†µå’Œæ€§èƒ½æŒ‡æ ‡ç«¯ç‚¹"
-
msgid "Enable in-product marketing emails"
msgstr "接收产å“è¥é”€ç”µå­é‚®ä»¶"
@@ -14749,6 +14861,9 @@ msgstr "å²è¯—"
msgid "Epic Boards"
msgstr "å²è¯—看æ¿"
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr "找ä¸åˆ°å²è¯—。"
@@ -14785,12 +14900,6 @@ msgstr "添加一个新的å²è¯—。"
msgid "Epics|Add an existing epic"
msgstr "添加一个现有的å²è¯—。"
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr "ä¿å­˜ %{epicDateType} 日期时å‘生错误"
-
-msgid "Epics|An error occurred while updating labels."
-msgstr "更新标记时出错。"
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr "确定è¦ä»Ž%{bStart}%{parentEpicTitle}%{bEnd}删除%{bStart}%{targetIssueTitle}%{bEnd}å—?"
@@ -14857,18 +14966,9 @@ msgstr "这个å²è¯—和任何包å«å­å²è¯—çš„ä¿¡æ¯å‡ä¸ºç§å¯†ï¼Œåªèƒ½å¯¹æ‹¥
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}的所有级别å­å²è¯—。确定继续å—?"
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr "如需根æ®é‡Œç¨‹ç¢‘æ¥å®‰æŽ’å²è¯—çš„ %{epicDateType} 日期,请为å²è¯—中的任一议题指定带有 %{epicDateType} 日期的里程碑。"
-
msgid "Epics|Unable to save epic. Please try again"
msgstr "无法ä¿å­˜å²è¯—。请å†è¯•ä¸€æ¬¡"
-msgid "Epics|due"
-msgstr "到期"
-
-msgid "Epics|start"
-msgstr "开始"
-
msgid "Erased"
msgstr "已删除"
@@ -14905,6 +15005,9 @@ msgstr "创建æ¼æ´žå‘现结果时出错:%{errors}"
msgid "Error deleting project. Check logs for error details."
msgstr "删除项目时出错。请检查错误详细信æ¯ã€‚"
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr "æå–燃烧图数æ®æ—¶å‡ºé”™"
@@ -14938,9 +15041,6 @@ msgstr "加载分支时出错。"
msgid "Error loading burndown chart data"
msgstr "加载燃尽图数æ®æ—¶å‡ºé”™"
-msgid "Error loading countries data."
-msgstr "加载国家/地区数æ®æ—¶å‡ºé”™ã€‚"
-
msgid "Error loading file viewer."
msgstr "加载文件查看器时出错。"
@@ -15016,6 +15116,9 @@ msgstr "å‘生了错误。用户未解除é”定"
msgid "Error parsing CSV file. Please make sure it has"
msgstr "解æžCSV文件时出错。请确认它是å¦åŒ…å«"
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr "渲染Markdown预览出错"
@@ -15446,6 +15549,9 @@ msgstr "展开议题"
msgid "Expand jobs"
msgstr "展开作业"
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr "展开里程碑"
@@ -16193,9 +16299,6 @@ msgstr "2月"
msgid "February"
msgstr "2月"
-msgid "Feedback issue"
-msgstr "å馈问题"
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr "获å–并检出这个åˆå¹¶è¯·æ±‚的功能分支:"
@@ -16406,14 +16509,14 @@ msgstr "固定:"
msgid "Flags"
msgstr "标记"
-msgid "FloC|Configure whether you want to participate in FloC."
-msgstr "é…置是å¦è¦å‚与FloC。"
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
+msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
-msgstr "å¯ç”¨ FloC(Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
+msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
-msgstr "FloC(Federated Learning of Cohorts)"
+msgid "FloC|Participate in FLoC"
+msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
msgstr "输入您的 Flowdock 令牌。"
@@ -16562,6 +16665,9 @@ msgstr "公开"
msgid "ForkProject|Select a namespace"
msgstr "选择命å空间"
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr "任何登录用户都å¯ä»¥è®¿é—®è¯¥é¡¹ç›®ã€‚"
@@ -16629,6 +16735,9 @@ msgstr "频率"
msgid "Frequently searched"
msgstr "ç»å¸¸æœç´¢çš„"
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr "星期五"
@@ -16641,13 +16750,9 @@ msgstr "从%{code_open}%{source_title}%{code_close}到"
msgid "From %{providerTitle}"
msgstr "%{providerTitle}æºåœ°å€"
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] "自 2022 å¹´ 10 月 19 日起,å…费群组将é™åˆ¶ä¸º %d åæˆå‘˜"
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
-msgstr[0] "从 2022 å¹´ 10 月 19 日起,您的所有个人项目中最多å¯ä»¥æœ‰ %d 个唯一æˆå‘˜"
+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 "从创建议题到部署至生产环境"
@@ -16786,9 +16891,6 @@ msgstr "添加新站点"
msgid "Geo|Add site"
msgstr "添加站点"
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr "请您调整上é¢çš„筛选器/æœç´¢æ¡ä»¶ã€‚如果您认为此处有误,请å‚阅 %{linkStart}Geo Troubleshow%{linkEnd} 文档以获å–更多信æ¯ã€‚"
-
msgid "Geo|All"
msgstr "所有"
@@ -16984,6 +17086,12 @@ msgstr "从ä¸"
msgid "Geo|Next sync scheduled at"
msgstr "下一次åŒæ­¥å®‰æŽ’在"
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr "未找到Geo站点"
@@ -17191,6 +17299,9 @@ msgstr "站点当å‰ä½äºŽä¸»ç«™ç‚¹åŽé¢ %{minutes_behind}。"
msgid "Geo|There are no %{replicable_type} to show"
msgstr "没有%{replicable_type}å¯æ˜¾ç¤º"
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr "删除Geo站点时出错"
@@ -17353,6 +17464,9 @@ msgstr "Git传输正在进行中"
msgid "Git version"
msgstr "Git 版本"
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr "排除的用户"
@@ -17722,6 +17836,9 @@ msgstr "此范围的全局æœç´¢å·²ç¦ç”¨"
msgid "Global Shortcuts"
msgstr "全局快æ·é”®"
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr "全局通知设置"
@@ -17734,6 +17851,12 @@ msgstr "æä¾› %{count} 个默认结果,使用上下箭头键导航æœç´¢ç»“æž
msgid "GlobalSearch|Groups"
msgstr "群组"
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr "我创建的议题"
@@ -17752,6 +17875,15 @@ msgstr "我作为审核者的åˆå¹¶è¯·æ±‚"
msgid "GlobalSearch|Projects"
msgstr "项目"
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 é”®æ交。"
@@ -17764,6 +17896,9 @@ msgstr "æœç´¢é¡¹ç›®ã€è®®é¢˜ç­‰ã€‚"
msgid "GlobalSearch|Search results are loading"
msgstr "æœç´¢ç»“果正在加载中"
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr "获å–æœç´¢è‡ªåŠ¨å®Œæˆå»ºè®®æ—¶å‡ºé”™ã€‚"
@@ -17791,6 +17926,12 @@ msgstr "在%{scope}中"
msgid "GlobalSearch|project"
msgstr "项目"
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr "全局å…许的 IP 范围"
@@ -18061,9 +18202,6 @@ msgstr "å¯ç”¨ Gravatar"
msgid "Group"
msgstr "群组"
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr "群组“%{group_name}â€å·²æˆåŠŸæ›´æ–°ã€‚"
-
msgid "Group %{group_name} couldn't be exported."
msgstr "群组%{group_name}无法导出。"
@@ -18335,7 +18473,7 @@ msgid "GroupSAML|Before enforcing SSO, enable SAML authentication."
msgstr "在执行SSO之å‰ï¼Œå¯ç”¨SAML身份验è¯ã€‚"
msgid "GroupSAML|Before enforcing SSO-only authentication for Git activity of all users, enable SSO-only authentication for web activity."
-msgstr ""
+msgstr "对所有用户的 Git 活动实施仅 SSO 身份验è¯å‰ï¼Œä¸º Web 活动å¯ç”¨ä»… SSO 身份验è¯ã€‚"
msgid "GroupSAML|Certificate fingerprint"
msgstr "è¯ä¹¦æŒ‡çº¹"
@@ -18499,9 +18637,6 @@ msgstr "应用到所有å­ç¾¤ç»„,除éžè¢«ç¾¤ç»„所有者覆盖。已添加到
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr "已为群组更新 Auto DevOps æµæ°´çº¿"
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr "自动å°ç¦åœ¨ç‰¹å®šæ—¶é—´å†…下载超过指定数é‡çš„仓库的用户。"
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr "仅在顶级群组中å¯ç”¨ã€‚适用于所有å­ç»„。除éžæ‰‹åŠ¨åˆ é™¤ï¼Œå¦åˆ™ %{group} 以外的群组所共享的群组ä»ä¼šå…±äº«ã€‚"
@@ -18529,6 +18664,9 @@ msgstr "åˆè§„框架"
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr "é…ç½®åˆè§„框架以æ供给此群组中的项目。 %{linkStart}了解更多信æ¯ã€‚%{linkEnd}"
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr "自定义项目模æ¿"
@@ -18728,7 +18866,7 @@ 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 "%{linkStart}群组%{linkEnd}å…许您在多个项目之间进行管ç†ä¸Žå作。群组的æˆå‘˜æ‹¥æœ‰è®¿é—®å…¶ä¸­æ‰€æœ‰é¡¹ç›®çš„æƒé™ã€‚"
@@ -18892,20 +19030,23 @@ msgstr "å‚考"
msgid "HAR (HTTP Archive)"
msgstr "HAR(HTTP 存档)"
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr "HAR 文件路径或 URL"
msgid "HTTP Archive (HAR)"
msgstr "HTTP 存档(HAR)"
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
-msgstr "HTTP Basic:访问被拒ç»\\n您必须使用具有'api'æƒé™çš„个人访问令牌。\\n您å¯ä»¥åœ¨ %{profile_personal_access_tokens_url}中生æˆä¸€ä¸ª"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
+msgstr ""
msgid "Harbor Registry"
msgstr "Harbor é•œåƒåº“"
msgid "HarborIntegration|After the Harbor integration is activated, global variables '$HARBOR_USERNAME', '$HARBOR_HOST', '$HARBOR_OCI', '$HARBOR_PASSWORD', '$HARBOR_URL' and '$HARBOR_PROJECT' will be created for CI/CD use."
-msgstr ""
+msgstr "Harbor 集æˆæ¿€æ´»åŽï¼Œå°†åˆ›å»ºå…¨å±€å˜é‡â€œ$HARBOR_USERNAMEâ€ã€â€œ$HARBOR_HOSTâ€ã€â€œ$HARBOR_OCIâ€ã€â€œ$HARBOR_PASSWORDâ€ã€â€œ$HARBOR_URLâ€å’Œâ€œ$HARBOR_PROJECTâ€ä»¥ä¾› CI/CD 使用。"
msgid "HarborIntegration|Base URL of the Harbor instance."
msgstr "Harbor 实例的基础 URL。"
@@ -18937,16 +19078,16 @@ msgstr "Harbor 中的项目å称。"
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr "使用 Harbor 作为该项目的容器镜åƒåº“。"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
+msgstr[0] ""
+
msgid "HarborRegistry|%{count} Image repository"
msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] "%{count} 个镜åƒä»“库"
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
-msgstr[0] "%{count} 个标签"
-
-msgid "HarborRegistry|Configuration digest: %{digest}"
-msgstr "é…置摘è¦: %{digest}"
+msgid "HarborRegistry|-- artifacts"
+msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
msgstr "摘è¦: %{imageId}"
@@ -18957,44 +19098,38 @@ msgstr "Harbor Registry"
msgid "HarborRegistry|Harbor connection error"
msgstr "Harbor 连接错误"
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr "无效标签:缺少 manifest 摘è¦"
-
-msgid "HarborRegistry|Last updated %{time}"
-msgstr "最近更新于%{time}"
-
-msgid "HarborRegistry|Manifest digest: %{digest}"
-msgstr "Manifest 摘è¦: %{digest}"
-
msgid "HarborRegistry|Please try different search criteria"
msgstr "请å°è¯•ä¸åŒçš„æœç´¢æ¡ä»¶"
msgid "HarborRegistry|Published %{timeInfo}"
msgstr "å‘布于%{timeInfo}"
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
-msgstr "于%{date}%{time}å‘布到%{repositoryPath}é•œåƒä»“库。"
-
msgid "HarborRegistry|Root image"
msgstr "根镜åƒ"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
+msgstr ""
+
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
+msgstr ""
+
+msgid "HarborRegistry|Something went wrong while fetching the tags."
+msgstr ""
+
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr "对ä¸èµ·ï¼Œæ‚¨çš„过滤器没有产生任何结果。"
+msgid "HarborRegistry|Tag"
+msgstr ""
+
msgid "HarborRegistry|The filter returned no results"
msgstr "过滤器没有返回结果"
-msgid "HarborRegistry|The image repository could not be found."
-msgstr "找ä¸åˆ°é•œåƒä»“库。"
-
-msgid "HarborRegistry|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 "与此镜åƒç›¸å…³çš„最åŽä¸€ä¸ªæ ‡ç­¾æœ€è¿‘已被删除。空镜åƒå’Œæ‰€æœ‰ç›¸å…³æ•°æ®å°†ä½œä¸ºå¸¸è§„垃圾收集过程的一部分自动清除。如有任何疑问,请与管ç†å‘˜è”系。"
-
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
-msgstr "请求的镜åƒä»“库ä¸å­˜åœ¨æˆ–已被删除。如果您认为这是一个错误,请å°è¯•åˆ·æ–°é¡µé¢ã€‚"
+msgid "HarborRegistry|There are no harbor images stored for this project"
+msgstr ""
-msgid "HarborRegistry|This image has no active tags"
-msgstr "此镜åƒæ²¡æœ‰æœ‰æ•ˆæ ‡ç­¾"
+msgid "HarborRegistry|This image has no artifacts"
+msgstr "该镜åƒæœªæœ‰äº§ç‰©"
msgid "HarborRegistry|To widen your search, change or remove the filters above."
msgstr "è¦æ‰©å¤§æœç´¢èŒƒå›´ï¼Œè¯·æ›´æ”¹æˆ–删除上é¢çš„过滤器。"
@@ -19002,6 +19137,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr "连接到 Harbor Registry 出错。请å°è¯•åˆ·æ–°é¡µé¢ã€‚如果此错误ä»ç„¶å­˜åœ¨ï¼Œè¯·æŸ¥çœ‹ %{docLinkStart}故障排查文档%{docLinkEnd}。"
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr "使用 Harbor Registry,æ¯ä¸ªé¡¹ç›®éƒ½æœ‰è‡ªå·±çš„空间æ¥å­˜å‚¨å®¹å™¨é•œåƒã€‚"
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr "使用 Harbor Registry,æ¯ä¸ªé¡¹ç›®éƒ½æœ‰è‡ªå·±çš„空间æ¥å­˜å‚¨å®¹å™¨é•œåƒã€‚%{docLinkStart}更多信æ¯%{docLinkEnd}"
@@ -19147,6 +19285,9 @@ msgid "Hide chart"
msgid_plural "Hide charts"
msgstr[0] "éšè—图表"
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr "éšè—对此文件的评论"
@@ -19277,9 +19418,6 @@ msgstr "已开始维护"
msgid "How do I configure Akismet?"
msgstr "如何é…ç½® Akismet ?"
-msgid "How do I configure runners?"
-msgstr "如何é…ç½® Runner?"
-
msgid "How do I configure this integration?"
msgstr "如何é…置此集æˆï¼Ÿ"
@@ -19313,6 +19451,9 @@ msgstr "IP 计入 IP 地å€é™åˆ¶çš„秒数。"
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr "作业é™åˆ¶å™¨å¦‚何处ç†è¶…出以下指定阈值的作业。 “trackâ€æ¨¡å¼åªè®°å½•ä½œä¸šã€‚“compressâ€æ¨¡å¼åŽ‹ç¼©ä½œä¸šå¹¶åœ¨åŽ‹ç¼©å¤§å°è¶…过é™åˆ¶æ—¶å¼•å‘异常。"
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr "æˆ‘æŽ¥å— %{terms_link}"
@@ -19436,9 +19577,15 @@ msgstr "在您登录之å‰ï¼Œæˆ‘们需è¦éªŒè¯æ‚¨çš„身份。请在登录页é¢
msgid "IdentityVerification|Create a project"
msgstr "创建项目"
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr "为了增加安全性,您需è¦éªŒè¯æ‚¨çš„èº«ä»½ã€‚æˆ‘ä»¬å·²å‘ %{email} å‘é€éªŒè¯ç "
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr "帮助我们ä¿æŠ¤æ‚¨çš„å¸æˆ·"
@@ -19451,15 +19598,36 @@ msgstr "如果您最近未å°è¯•ç™»å½•ï¼Œæˆ‘们建议您修改密ç ï¼ˆ%{passwor
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr "如果您无法访问与此å¸æˆ·å…³è”的电å­é‚®ä»¶æˆ–验è¯ç æœ‰é—®é¢˜ï¼Œ%{link_start}这里是您å¯ä»¥é‡‡å–的其他一些步骤。%{link_end}"
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr "超过最大登录å°è¯•æ¬¡æ•°ã€‚等待 %{interval} åŽé‡è¯•ã€‚"
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr "请输入验è¯ç "
msgid "IdentityVerification|Resend code"
msgstr "é‡æ–°å‘é€éªŒè¯ç "
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr "验è¯ç å·²è¿‡æœŸã€‚é‡æ–°å‘é€æ–°éªŒè¯ç ç„¶åŽé‡è¯•ã€‚"
@@ -19481,6 +19649,9 @@ msgstr "验è¯æ‚¨çš„身份"
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr "您å¯ä»¥ç¨åŽéšæ—¶éªŒè¯æ‚¨çš„å¸æˆ·ï¼Œæ¥åˆ›å»ºä¸€ä¸ªç¾¤ç»„。"
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr "您已达到最大å°è¯•æ¬¡æ•°ã€‚等待 %{interval} 或é‡æ–°å‘é€æ–°ä»£ç ï¼Œç„¶åŽé‡è¯•ã€‚"
@@ -19550,9 +19721,6 @@ msgstr "如使用 GitHub,GitHubçš„æ交(commits)和拉å–请求(pull request)
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr "如果将%{codeStart}needs%{codeEnd}加到æµæ°´çº¿çš„作业里é¢ï¼Œæ‚¨å°†å¯ä»¥åœ¨%{linkStart}有å‘无环图 (DAG)%{linkEnd}页é¢çœ‹åˆ°ä½œä¸šä¹‹é—´çš„%{codeStart}needs%{codeEnd}关系。"
-msgid "If you are added to a project, it will be displayed here."
-msgstr "如果您被添加到项目中,将显示在此处。"
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr "如果您没有å‘起这些登录å°è¯•ï¼Œè¯·è”系您的管ç†å‘˜æˆ–在您的å¸æˆ·ä¸Šå¯ç”¨åŒé‡èº«ä»½éªŒè¯ï¼ˆ2FA)。"
@@ -19808,9 +19976,6 @@ msgstr "在此 URL 上没有有效的 Git 仓库。如果您的 HTTP 仓库ä¸èƒ
msgid "Improve customer support with Service Desk"
msgstr "通过æœåŠ¡å°æ”¹å–„客户支æŒ"
-msgid "In a seat"
-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 "在拉å–é•œåƒçš„情况下,您的用户将æˆä¸ºæ´»åŠ¨æè¦ä¸­æ‰€æœ‰ä½œä¸ºæ›´æ–°ç»“果的事件的作者,例如创建新分支或将新æ交推é€åˆ°çŽ°æœ‰åˆ†æ”¯ã€‚"
@@ -20600,6 +20765,9 @@ msgstr "创建事件时间线事件时出错: %{error}"
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr "删除事件时间线事件时出错: %{error}"
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr "指标"
@@ -20618,6 +20786,9 @@ msgstr "删除事件时间线事件时出错。"
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr "获å–事件(incident)时间线事件时出了错。"
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr "摘è¦"
@@ -20651,8 +20822,8 @@ msgstr "在邮件正文中包括议题ã€åˆå¹¶è¯·æ±‚或评论的作者å称。
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr "如果需è¦ï¼Œè¯·åœ¨URL中包å«ç”¨æˆ·å:%{code_open}https://username@gitlab.company.com/group/project.git%{code_close}。"
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
-msgstr "åŒ…å« LFS 对象。它å¯ä»¥æŒ‰ç¾¤ç»„或项目覆盖。 0 表示无é™åˆ¶ã€‚"
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
+msgstr ""
msgid "Includes an MVC structure to help you get started"
msgstr "åŒ…å« MVC 结构以帮助您入门"
@@ -20792,9 +20963,15 @@ msgstr "上方æ’入行"
msgid "Insert suggestion"
msgstr "æ’入建议"
+msgid "Insert table"
+msgstr "æ’入表格"
+
msgid "Insights"
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)."
msgstr "有些æ¡ç›®ä¸å¯è§ï¼Œç”±äºŽå·²åœ¨insights.yml文件中筛选掉(è§projects.onlyé…置以了解更多信æ¯)。"
@@ -21063,6 +21240,9 @@ msgstr "将有关项目事件的通知å‘é€åˆ° Unify Circuit。"
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr "将有关项目事件的通知å‘é€åˆ° Unify Circuit 对è¯ã€‚ %{docs_link}"
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr "登录到 GitLab"
@@ -21328,7 +21508,7 @@ msgid "InviteMembersBanner|We noticed that you haven't invited anyone to this gr
msgstr "ç›®å‰æ‚¨è¿˜æ²¡æœ‰é‚€è¯·ä»»ä½•äººåŠ å…¥è¿™ä¸ªç¾¤ç»„。 您å¯ä»¥é‚€è¯·æ‚¨çš„åŒäº‹åˆ°ç¾¤ç»„ç§ï¼Œä»¥æ–¹ä¾¿è®¨è®ºé—®é¢˜ï¼Œåœ¨åˆå¹¶è¯·æ±‚中进行å作,以åŠåˆ†äº«æ‚¨çš„知识。"
msgid "InviteMembersModal| To get more members and access to additional paid features, an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
+msgstr "è¦èŽ·å¾—更多æˆå‘˜å¹¶è®¿é—®å…¶ä»–付费功能,群组的所有者å¯ä»¥å¼€å§‹è¯•ç”¨æˆ–å‡çº§åˆ°ä»˜è´¹ç­‰çº§ã€‚"
msgid "InviteMembersModal|%{linkStart}Read more%{linkEnd} about role permissions"
msgstr "%{linkStart}了解更多%{linkEnd}关于角色æƒé™çš„ä¿¡æ¯"
@@ -21375,6 +21555,9 @@ msgstr "管ç†æˆå‘˜"
msgid "InviteMembersModal|Members were successfully added"
msgstr "æˆå‘˜å·²æˆåŠŸæ·»åŠ "
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr "请检查邀请错误并é‡è¯•ï¼š"
@@ -21390,6 +21573,12 @@ msgstr "选择角色"
msgid "InviteMembersModal|Select members or type email addresses"
msgstr "选择æˆå‘˜æˆ–输入电å­é‚®ä»¶åœ°å€"
+msgid "InviteMembersModal|Show less"
+msgstr "显示较少"
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr "出现错误"
@@ -21404,10 +21593,7 @@ msgid "InviteMembersModal|To assign issues to a new team member, you need a proj
msgstr "è¦å‘新团队æˆå‘˜åˆ†é…议题,您需è¦ä¸€ä¸ªè®®é¢˜æ‰€å±žçš„项目。 %{linkStart}从创建一个项目开始。%{linkEnd}"
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
-msgstr ""
-
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr "为了创造更多空间,您å¯ä»¥ç§»é™¤ä¸å†éœ€è¦è®¿é—®çš„æˆå‘˜ã€‚"
+msgstr "è¦èŽ·å¾—更多æˆå‘˜ï¼Œç¾¤ç»„的所有者å¯ä»¥ %{trialLinkStart}开始试用%{trialLinkEnd} 或 %{upgradeLinkStart}å‡çº§%{upgradeLinkEnd} 到付费等级。"
msgid "InviteMembersModal|Username or email address"
msgstr "用户å或电å­é‚®ä»¶åœ°å€"
@@ -21433,9 +21619,6 @@ msgstr "您正在邀请æˆå‘˜åŠ å…¥ %{strongStart}%{name}%{strongEnd} 项目。"
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr "您已达到 %{name} çš„ %{count} 个 %{members} é™åˆ¶"
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr "您已ç»è¾¾åˆ°äº†æ‚¨ä¸ªäººé¡¹ç›®çš„ %{count} 个 %{members} çš„é™åˆ¶"
-
msgid "InviteMembers|Invite a group"
msgstr "邀请群组"
@@ -21553,12 +21736,18 @@ msgstr "正在使用许å¯è¯ï¼š"
msgid "Is using seat"
msgstr "正在使用许å¯å¸­ä½"
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr "已关闭"
msgid "IssuableStatus|Closed (%{link})"
msgstr "关闭(%{link})"
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr "创建于 %{created_at}"
+
msgid "IssuableStatus|duplicated"
msgstr "é‡å¤"
@@ -21824,7 +22013,7 @@ msgid "Iteration"
msgstr "迭代"
msgid "Iteration cannot be created for cadence"
-msgstr ""
+msgstr "无法为周期创建迭代"
msgid "Iteration changed to"
msgstr "迭代更改为"
@@ -21842,10 +22031,10 @@ msgid "Iterations"
msgstr "迭代"
msgid "Iterations cadence not found"
-msgstr ""
+msgstr "未找到迭代周期"
msgid "Iterations cannot be manually added to cadences that use automatic scheduling"
-msgstr ""
+msgstr "无法手动将迭代添加到使用自动调度的周期中"
msgid "IterationsCadence|The automation start date must come after the active iteration %{iteration_dates}."
msgstr "自动化起始日期必须在活动迭代 %{iteration_dates} 之åŽã€‚"
@@ -21877,9 +22066,6 @@ msgstr "周期é…置无效。"
msgid "Iterations|Cadence name"
msgstr "周期å称"
-msgid "Iterations|Can be converted"
-msgstr "å¯ä»¥è½¬æ¢"
-
msgid "Iterations|Cancel"
msgstr "å–消"
@@ -21925,6 +22111,9 @@ msgstr "编辑迭代"
msgid "Iterations|Edit iteration cadence"
msgstr "编辑迭代周期"
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr "å¯ç”¨è½®æ¢"
@@ -21938,13 +22127,7 @@ msgid "Iterations|Iterations are a way to track issues over a period of time, al
msgstr "迭代是在一段时间内跟踪议题的一ç§æ–¹æ³•ï¼Œä½¿å°ç»„也能够跟踪速度和波动指标。"
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
-msgstr ""
-
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr "了解更多关于自动计划的信æ¯"
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr "手动管ç†è¿­ä»£å°†åœ¨ 15.6 版本中被废弃。在准备好时,将手动æ“作转æ¢ä¸ºä½¿ç”¨è‡ªåŠ¨è®¡åˆ’方法。"
+msgstr "迭代计划在%{weekday}开始。"
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr "将未完æˆçš„议题移至下一次迭代。"
@@ -22003,9 +22186,6 @@ msgstr "æ¯æ¬¡è¿­ä»£çš„æŒç»­æ—¶é—´ï¼ˆä»¥å‘¨ä¸ºå•ä½ï¼‰"
msgid "Iterations|The iteration has been deleted."
msgstr "迭代已被删除。"
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr "å¯ä»¥å°†æ­¤å‘¨æœŸè½¬æ¢ä¸ºä½¿ç”¨è‡ªåŠ¨è®¡åˆ’"
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr "这将删除周期以åŠå…¶ä¸­çš„所有迭代。"
@@ -22015,9 +22195,6 @@ msgstr "这将从分é…给它的任何议题中删除迭代。"
msgid "Iterations|Title"
msgstr "标题"
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr "è¦å°†æ­¤å‘¨æœŸè½¬æ¢ä¸ºè‡ªåŠ¨è®¡åˆ’,请添加æŒç»­æ—¶é—´å’Œå³å°†è¿›è¡Œçš„迭代次数。å‡çº§æ˜¯ä¸å¯é€†çš„。"
-
msgid "Iterations|Unable to find iteration cadence."
msgstr "无法找到迭代周期。"
@@ -22030,9 +22207,6 @@ msgstr "无法ä¿å­˜å‘¨æœŸï¼Œè¯·é‡è¯•ã€‚"
msgid "Iterations|Upcoming iterations"
msgstr "å³å°†è¿›è¡Œçš„迭代"
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr "您的手动周期å¯ä»¥è½¬æ¢ä¸ºä½¿ç”¨è‡ªåŠ¨è®¡åˆ’"
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr "日期ä¸èƒ½ä¸Žè¯¥ç¾¤ç»„内的其它现有迭代é‡å "
@@ -22327,9 +22501,6 @@ msgstr "作业已æˆåŠŸåˆ é™¤!"
msgid "Job has wrong arguments format."
msgstr "作业的å‚æ•°æ ¼å¼é”™è¯¯ã€‚"
-msgid "Job is missing the `model_type` argument."
-msgstr "作业缺少 `model_type` å‚数。"
-
msgid "Job is stuck. Check runners."
msgstr "作业已阻塞。请检查Runner。"
@@ -22763,8 +22934,8 @@ 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."
-msgstr "标记å¯ç”¨äºŽè®®é¢˜å’Œåˆå¹¶è¯·æ±‚。"
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
+msgstr "标记å¯ä»¥åˆ†é…给议题和åˆå¹¶è¯·æ±‚。为标记加星标å¯ä»¥ä½¿ä¹‹æˆä¸ºä¼˜å…ˆæ ‡è®°ã€‚"
msgid "Labels with no issues in this iteration:"
msgstr "此迭代中没有议题的标记:"
@@ -22778,6 +22949,9 @@ msgstr "å‡çº§æ ‡è®°"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr "å‡çº§%{labelTitle}将使其å¯ç”¨äºŽ%{groupName}内的所有项目。现有的åŒå项目标记将被åˆå¹¶ã€‚现有的åŒå群组标记也将被åˆå¹¶ã€‚该æ“作ä¸å¯æ’¤é”€ã€‚"
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr "语言"
@@ -22947,9 +23121,6 @@ msgstr "å‰ç½®æ—¶é—´"
msgid "Learn GitLab"
msgstr "学习GitLab"
-msgid "Learn GitLab - Ultimate trial"
-msgstr "学习 GitLab - 旗舰版试用"
-
msgid "Learn More"
msgstr "了解更多"
@@ -23001,6 +23172,9 @@ msgstr "了解更多关于群组级项目模æ¿"
msgid "Learn more about groups."
msgstr "了解关于群组的更多信æ¯ã€‚"
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr "了解更多关于最大席ä½æ•°çš„ä¿¡æ¯"
@@ -23727,6 +23901,9 @@ msgstr "Mailgun HTTP webhook ç­¾å密钥"
msgid "Mailgun events"
msgstr "Mailgun事件"
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr "维护模å¼"
@@ -24015,11 +24192,8 @@ msgstr "最大用户数"
msgid "Maximum allowable lifetime for access token (days)"
msgstr "访问令牌的最长有效期(天)"
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr "个人访问令牌的最长有效期(天)"
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
-msgstr "SSH 密钥的最长å…许生命周期(以天为å•ä½ï¼‰"
+msgid "Maximum allowed lifetime for SSH keys (days)"
+msgstr ""
msgid "Maximum artifacts size"
msgstr "最大工件大å°"
@@ -24500,6 +24674,9 @@ msgstr "åˆå¹¶è¯·æ±‚已安排在æµæ°´çº¿æˆåŠŸåŽåˆå¹¶"
msgid "Merge requests"
msgstr "åˆå¹¶è¯·æ±‚"
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr "åˆå¹¶è¯·æ±‚用于æ出对项目的更改并与他人进行讨论"
@@ -24587,6 +24764,9 @@ msgstr "ä¿å­˜è¯„论è‰ç¨¿æ—¶å‘生错误。"
msgid "MergeRequests|Create issue to resolve thread"
msgstr "创建议题以解决主题"
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr "ä¿å­˜è¯„论失败"
@@ -24662,6 +24842,30 @@ msgstr "未找到任何文件"
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr "æœç´¢ï¼ˆä¾‹å¦‚ *.vue)(%{modifier_key}P)"
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr "åˆå¹¶ä¸»é¢˜"
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr "åˆå¹¶ä¸»é¢˜å°†å¯¼è‡´ä»¥ä¸‹æƒ…况:"
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr "æºä¸»é¢˜"
+
+msgid "MergeTopics|Target topic"
+msgstr "目标主题"
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr "æ­¤æ“作无法撤消。"
+
msgid "Merged"
msgstr "å·²åˆå¹¶"
@@ -25336,18 +25540,21 @@ msgstr "修改æ交消æ¯"
msgid "Modify merge commit"
msgstr "修改åˆå¹¶æ交"
+msgid "Mon"
+msgstr "星期一"
+
msgid "Monday"
msgstr "星期一"
msgid "Monitor"
msgstr "监控"
+msgid "Monitor GitLab with Prometheus."
+msgstr ""
+
msgid "Monitor Settings"
msgstr "监控设置"
-msgid "Monitor the health and performance of GitLab with Prometheus."
-msgstr "用Prometheus监控 GitLab çš„å¥åº·å’Œæ€§èƒ½ã€‚"
-
msgid "Monitor your errors by integrating with Sentry."
msgstr "通过与Sentry集æˆæ¥ç›‘控您的错误。"
@@ -25360,6 +25567,9 @@ msgstr "月"
msgid "Months"
msgstr "月"
+msgid "More"
+msgstr "更多"
+
msgid "More Details"
msgstr "更多详情"
@@ -25402,9 +25612,6 @@ msgstr "相关度最高"
msgid "Most stars"
msgstr "最多星标"
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr "%{model_class} 中未找到挂载点 %{mounted_as}。"
-
msgid "Move"
msgstr "移动"
@@ -25495,12 +25702,6 @@ msgstr "支æŒå¤šä¸ª IP 地å€èŒƒå›´ã€‚ä¸å½±å“对群组设置的访问。"
msgid "Multiple Prometheus integrations are not supported"
msgstr "ä¸æ”¯æŒå¤šä¸ªPrometheus集æˆ"
-msgid "Multiple model types found: %{model_types}"
-msgstr "找到多个模型类型: %{model_types}"
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr "找到多个上传器: %{uploader_types}"
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr "应用于轮询间隔的乘数。支æŒå进制值。默认为 1。"
@@ -26022,6 +26223,9 @@ msgstr "没有用于匹é…的信用å¡æ•°æ®"
msgid "No credit card required."
msgstr "无需信用å¡ã€‚"
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr "未找到数æ®"
@@ -26034,9 +26238,6 @@ msgstr "未检测到部署。使用环境æ¥æŽ§åˆ¶è½¯ä»¶çš„æŒç»­éƒ¨ç½²ã€‚ %{lin
msgid "No deployments found"
msgstr "没有找到部署"
-msgid "No due date"
-msgstr "无截止日期"
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr "没有添加电å­é‚®ä»¶å‚与者。没有æ供或它们已ç»å­˜åœ¨ã€‚"
@@ -26187,6 +26388,9 @@ msgstr "无仓库"
msgid "No results"
msgstr "没有结果"
+msgid "No results found"
+msgstr "未找到结果"
+
msgid "No runner executable"
msgstr "æ— runnerå¯æ‰§è¡Œæ–‡ä»¶"
@@ -26208,9 +26412,6 @@ msgstr "没有此错误的堆栈跟踪"
msgid "No starrers matched your search"
msgstr "没有符åˆæ‚¨æœç´¢æ¡ä»¶çš„星标用户"
-msgid "No start date"
-msgstr "没有开始日期"
-
msgid "No suggestions found"
msgstr "未找到任何建议"
@@ -26398,6 +26599,9 @@ msgstr "指定的创建日期太早。"
msgid "Nothing to preview."
msgstr "没有å¯é¢„览的内容。"
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr "通知事件"
@@ -26528,6 +26732,9 @@ msgstr "%{author_link} 的议题 %{issue_reference_link} å³å°†åˆ°æœŸã€‚"
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr "%{mr_link} 中的 %{commit_link}"
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr "%{invite_email},现在称为 %{user_name},已接å—了您的邀请加入 %{target_name}%{target_model_name} 。"
@@ -26537,6 +26744,24 @@ msgstr "%{invited_user} å·²%{highlight_start}æ‹’ç»%{highlight_end}您加入%{ta
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr "%{member_link} 请求访问 %{target_source_link} %{target_type} çš„ %{member_role} æƒé™ã€‚"
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr "指派人从 %{fromNames} 更改为 %{toNames}"
@@ -26552,6 +26777,18 @@ msgstr "%{project} çš„ Auto DevOps æµæ°´çº¿è¢«ç¦ç”¨"
msgid "Notify|CI/CD project settings"
msgstr "CI/CD 项目设置"
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr "指纹:%{fingerprint}"
+
+msgid "Notify|Hi %{user}!"
+msgstr "您好 %{user}ï¼"
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr "如果您ä¸å†æƒ³åœ¨ Pages 中使用这个域å,请从您的项目中删除它,并删除任何相关的DNS记录。"
@@ -26582,9 +26819,18 @@ msgstr "åˆå¹¶è¯·æ±‚ %{mr_link} 被 %{closed_by} 关闭"
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr "åˆå¹¶è¯·æ±‚ URL:%{merge_request_url}"
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr "里程碑已更改为 %{milestone}"
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr "新建议题:%{project_issue_url}"
@@ -26603,6 +26849,12 @@ msgstr "除éžæ‚¨é€šè¿‡ %{time_start}%{time}%{time_end} 验è¯æ‚¨çš„域,å¦åˆ™
msgid "Notify|You don't have access to the project."
msgstr "您没有访问此项目的æƒé™ã€‚"
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr "您加入 %{target_to_join} %{target_type} 的请求状æ€ä¸º %{denied_tag}。"
@@ -26706,6 +26958,9 @@ msgid "On %{end_date}, your trial will end and %{namespace_name} will be limited
msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be limited to %{free_user_limit} members"
msgstr[0] "在 %{end_date},您的试用期将结æŸï¼Œ%{namespace_name} 将被é™åˆ¶ä¸º %{free_user_limit} 个æˆå‘˜"
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr "进度正常"
@@ -26925,6 +27180,12 @@ msgstr "删除é…置文件"
msgid "OnDemandScans|Description (optional)"
msgstr "æè¿° (å¯é€‰)"
+msgid "OnDemandScans|Discard changes"
+msgstr "放弃更改"
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr "动æ€åº”用程åºå®‰å…¨æ€§æµ‹è¯•(DAST)"
@@ -26946,6 +27207,9 @@ msgstr "å¯ç”¨æ‰«æ计划"
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr "例如:测试SQL注入的登录页é¢"
+msgid "OnDemandScans|Keep editing"
+msgstr "继续编辑"
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr "管ç†æ‰«æ工具é…置文件"
@@ -27069,6 +27333,9 @@ msgstr "使用现有的站点é…置文件"
msgid "OnDemandScans|View results"
msgstr "查看结果"
+msgid "OnDemandScans|You have unsaved changes"
+msgstr "您有未ä¿å­˜çš„更改"
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr "您必须在项目中创建仓库æ‰èƒ½è¿è¡ŒæŒ‰éœ€æ‰«æ。"
@@ -27223,9 +27490,15 @@ msgstr "å¼€å¯: %{open}"
msgid "OpenAPI"
msgstr "OpenAPI"
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr "OpenAPI 规范文件路径或 URL"
+msgid "OpenSearch's region."
+msgstr "OpenSearch 的区域。"
+
msgid "Opened"
msgstr "已打开"
@@ -27439,6 +27712,9 @@ msgstr "软件包库:未ç»èº«ä»½éªŒè¯çš„ API 请求"
msgid "Package already exists"
msgstr "软件包已存在"
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr "包已æˆåŠŸåˆ é™¤"
@@ -27475,6 +27751,9 @@ msgstr "包类型必须是Nuget"
msgid "Package type must be PyPi"
msgstr "包类型必须是PyPi"
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr "包类型必须是 RubyGems"
@@ -27499,6 +27778,9 @@ msgstr "添加composer注册表"
msgid "PackageRegistry|Additional metadata"
msgstr "其他元数æ®"
+msgid "PackageRegistry|Allow duplicates"
+msgstr "å…许é‡å¤"
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr "å…许具有相åŒå称和版本的软件包上传到库。安装时总是使用最新版本的软件包。"
@@ -27708,7 +27990,7 @@ msgid "PackageRegistry|Package Registry"
msgstr "软件包注册表"
msgid "PackageRegistry|Package assets deleted successfully"
-msgstr ""
+msgstr "软件包 assets 删除æˆåŠŸ"
msgid "PackageRegistry|Package deleted successfully"
msgstr "æˆåŠŸåˆ é™¤è½¯ä»¶åŒ…"
@@ -27716,6 +27998,9 @@ msgstr "æˆåŠŸåˆ é™¤è½¯ä»¶åŒ…"
msgid "PackageRegistry|Package file deleted successfully"
msgstr "æˆåŠŸåˆ é™¤è½¯ä»¶åŒ…文件"
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] "软件包有 %{updatesCount} 个存档更新"
@@ -27724,7 +28009,7 @@ msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, bu
msgstr "软件包由分支%{branch}上的%{link}æ交所更新,由æµæ°´çº¿%{pipeline}构建并于%{datetime}å‘布到库"
msgid "PackageRegistry|Permanently delete assets"
-msgstr ""
+msgstr "永久删除 assets"
msgid "PackageRegistry|Pip Command"
msgstr "Pip命令"
@@ -27747,9 +28032,6 @@ msgstr "æž„æˆ: %{recipe}"
msgid "PackageRegistry|Registry setup"
msgstr "仓库设置"
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr "æ‹’ç»å…·æœ‰ç›¸åŒå称和版本的软件包"
-
msgid "PackageRegistry|Remove package"
msgstr "删除软件包"
@@ -27759,12 +28041,6 @@ msgstr "éœ€è¦ Python: %{pythonVersion}"
msgid "PackageRegistry|RubyGems"
msgstr "RubyGems"
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr "通用包设置"
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr "Maven 包的设置"
-
msgid "PackageRegistry|Show Composer commands"
msgstr "显示 Composer 命令"
@@ -27784,7 +28060,7 @@ msgid "PackageRegistry|Show Yarn commands"
msgstr "显示 Yarn 命令"
msgid "PackageRegistry|Something went wrong while deleting the package assets."
-msgstr ""
+msgstr "删除软件包 assets 时出错。"
msgid "PackageRegistry|Something went wrong while deleting the package file."
msgstr "删除包文件时出错。"
@@ -27838,7 +28114,7 @@ msgid "PackageRegistry|Unable to load package"
msgstr "无法加载软件包"
msgid "PackageRegistry|When a package with same name and version is uploaded to the registry, more assets are added to the package. To save storage space, keep only the most recent assets."
-msgstr ""
+msgstr "当一个åŒååŒç‰ˆæœ¬çš„包被上传到仓库时,更多的 assets 被添加到包中。为了节çœå­˜å‚¨ç©ºé—´ï¼Œè¯·ä»…ä¿ç•™æœ€æ–°çš„ assets。"
msgid "PackageRegistry|You are about to delete %{filename}. This is a destructive action that may render your package unusable. Are you sure?"
msgstr "您将è¦åˆ é™¤ %{filename}。这是一ç§ç ´å性æ“作,å¯èƒ½ä¼šä½¿æ‚¨çš„包无法使用。您确定å—?"
@@ -27848,7 +28124,7 @@ msgstr "您将è¦åˆ é™¤%{name},此æ“作ä¸å¯é€†ï¼Œç¡®å®šç»§ç»­å—?"
msgid "PackageRegistry|You are about to delete 1 asset. This operation is irreversible."
msgid_plural "PackageRegistry|You are about to delete %d assets. This operation is irreversible."
-msgstr[0] ""
+msgstr[0] "您将è¦åˆ é™¤ %d 项 assets,此æ“作是ä¸å¯é€†çš„。"
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr "å³å°†åˆ é™¤%{name}çš„%{version}版本。确定继续å—?"
@@ -27857,7 +28133,7 @@ msgid "PackageRegistry|You may also need to setup authentication using an auth t
msgstr "您å¯èƒ½è¿˜éœ€è¦ä½¿ç”¨ä»¤ç‰Œè®¾ç½®èº«ä»½éªŒè¯ã€‚%{linkStart}请å‚阅文档%{linkEnd}以了解更多信æ¯ã€‚"
msgid "PackageRegistry|You will need a %{linkStart}personal access token%{linkEnd}."
-msgstr ""
+msgstr "您将需è¦ä¸€ä¸ª%{linkStart}个人访问令牌%{linkEnd}。"
msgid "PackageRegistry|npm"
msgstr "npm"
@@ -27865,8 +28141,8 @@ msgstr "npm"
msgid "PackageRegistry|published by %{author}"
msgstr "ç”±%{author}å‘布"
-msgid "Packages & Registries"
-msgstr "软件包与镜åƒåº“"
+msgid "Packages and registries"
+msgstr ""
msgid "Page not found"
msgstr "找ä¸åˆ°é¡µé¢"
@@ -28114,9 +28390,6 @@ msgstr "执行代ç å®¡æŸ¥å¹¶é€šè¿‡åˆå¹¶è¯·æ±‚增强å作。"
msgid "Perform common operations on GitLab project"
msgstr "在GitLab项目上执行常è§æ“作"
-msgid "Performance insights"
-msgstr "性能洞察"
-
msgid "Performance optimization"
msgstr "性能优化"
@@ -28192,6 +28465,12 @@ msgstr "墙"
msgid "Period in seconds"
msgstr "周期(秒)"
+msgid "Period of inactivity (days)"
+msgstr "ä¸æ´»åŠ¨æ—¶é•¿ï¼ˆå¤©æ•°ï¼‰"
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr "永久链接"
@@ -28897,27 +29176,18 @@ msgstr "正在创建æµæ°´çº¿ã€‚"
msgid "Pipeline|Date"
msgstr "日期"
-msgid "Pipeline|Detached merge request pipeline"
-msgstr "游离的åˆå¹¶è¯·æ±‚æµæ°´çº¿"
-
msgid "Pipeline|Failed"
msgstr "失败"
-msgid "Pipeline|Five slowest jobs"
-msgstr "五个最慢的作业"
-
msgid "Pipeline|In progress"
msgstr "进行中"
-msgid "Pipeline|Last executed job"
-msgstr "上次执行的作业"
-
-msgid "Pipeline|Longest queued job"
-msgstr "排队最长的作业"
-
msgid "Pipeline|Manual"
msgstr "手动"
+msgid "Pipeline|Merge request pipeline"
+msgstr ""
+
msgid "Pipeline|Merge train pipeline"
msgstr "åˆå¹¶é˜Ÿåˆ—æµæ°´çº¿"
@@ -28927,18 +29197,12 @@ msgstr "åˆå¹¶é˜Ÿåˆ—æµæ°´çº¿ä½œä¸šæ— æ³•é‡è¯•"
msgid "Pipeline|Merged result pipeline"
msgstr "åˆå¹¶ç»“æžœæµæ°´çº¿"
-msgid "Pipeline|Only able to show first 100 results"
-msgstr "åªèƒ½æ˜¾ç¤ºå‰ 100 个结果"
-
msgid "Pipeline|Passed"
msgstr "已通过"
msgid "Pipeline|Pending"
msgstr "等待中"
-msgid "Pipeline|Performance insights"
-msgstr "性能洞察"
-
msgid "Pipeline|Pipeline"
msgstr "æµæ°´çº¿"
@@ -28996,12 +29260,6 @@ msgstr "标签å称"
msgid "Pipeline|Test coverage"
msgstr "测试覆盖率"
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr "最åŽæ‰§è¡Œçš„作业是在æµæ°´çº¿ä¸­å¯åŠ¨çš„最åŽä¸€ä¸ªä½œä¸šã€‚"
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr "排队时间最长的作业是在 pending 状æ€ä¸­ç­‰å¾…时间最长的作业,等待被一个 Runner 选中"
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr "如果åˆå¹¶ï¼Œæ­¤æ›´æ”¹å°†é™ä½Žæ•´ä½“测试覆盖率。"
@@ -29032,9 +29290,6 @@ msgstr "å˜é‡"
msgid "Pipeline|View commit"
msgstr "查看æ交"
-msgid "Pipeline|View dependency"
-msgstr "查看ä¾èµ–"
-
msgid "Pipeline|View pipeline"
msgstr "查看æµæ°´çº¿"
@@ -29134,8 +29389,8 @@ msgstr "请检查您的电å­é‚®ä»¶ %{email} 以确认您的å¸æˆ·"
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr "请检查您的电å­é‚®ä»¶(%{email})以确认您拥有此电å­é‚®ç®±å¹¶è§£é”CI/CD的强大功能。还没收到邮件? %{resend_link}。或是电å­é‚®ä»¶åœ°å€é”™è¯¯ï¼Ÿ%{update_link}。"
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
-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 your profile with email address"
msgstr "请在您的个人资料中填写电å­é‚®ä»¶åœ°å€"
@@ -29350,6 +29605,9 @@ msgstr "端å£"
msgid "Postman collection"
msgstr "Postman 集åˆ"
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr "Postman 集åˆæ–‡ä»¶è·¯å¾„或 URL"
@@ -29371,12 +29629,12 @@ msgstr "个性化"
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr "为应用程åºé€‰æ‹©å›ºå®šæ–¹å¼(最大值1280px)或æµåŠ¨æ–¹å¼(%{percentage})布局。"
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
+msgstr ""
+
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr "选择项目概览页é¢ä¸­æ‚¨æƒ³çœ‹åˆ°çš„内容。"
-msgid "Preferences|Choose what content you want to see on your homepage."
-msgstr "选择您想è¦åœ¨é¦–页上看到的内容。"
-
msgid "Preferences|Color for added lines"
msgstr "添加行的颜色"
@@ -29398,6 +29656,9 @@ msgstr "自定义系统颜色。"
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr "自定义差异中删除行和添加行的颜色。"
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr "差异颜色"
@@ -29419,9 +29680,6 @@ msgstr "例如:30分钟å‰"
msgid "Preferences|Gitpod"
msgstr "Gitpod"
-msgid "Preferences|Homepage content"
-msgstr "首页内容"
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr "一次仅显示一个文件,而ä¸æ˜¯æ‰€æœ‰æ›´æ”¹çš„文件。è¦åœ¨æ–‡ä»¶ä¹‹é—´åˆ‡æ¢ï¼Œè¯·ä½¿ç”¨æ–‡ä»¶æµè§ˆå™¨ã€‚"
@@ -29833,9 +30091,6 @@ msgstr "èŒä½"
msgid "Profiles|Key"
msgstr "密钥"
-msgid "Profiles|Key becomes invalid on this date."
-msgstr "密钥在此日期无效。"
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr "密钥在此日期无效,SSH 密钥的最大有效期为 %{max_ssh_key_lifetime} 天"
@@ -29866,6 +30121,9 @@ msgstr "未选择文件。"
msgid "Profiles|Notification email"
msgstr "通知邮件"
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr "组织"
@@ -30553,6 +30811,9 @@ msgstr "å¯ç”¨åˆå¹¶ç»“æžœæµæ°´çº¿"
msgid "ProjectSettings|Encourage"
msgstr "建议"
+msgid "ProjectSettings|Environments"
+msgstr "环境"
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr "æ¯æ¬¡åˆå¹¶éƒ½ä¼šåˆ›å»ºä¸€ä¸ªåˆå¹¶æ交。"
@@ -30565,6 +30826,9 @@ 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 "æ¯ä¸ªé¡¹ç›®éƒ½å¯ä»¥æœ‰è‡ªå·±çš„空间æ¥å­˜å‚¨å®ƒçš„软件包。注æ„:当一个项目是公开时,软件包库总是å¯è§çš„。"
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr "所有人"
@@ -30583,6 +30847,9 @@ msgstr "å¿«è¿›å¼(Fast-forward)åˆå¹¶"
msgid "ProjectSettings|Fast-forward merges only."
msgstr "ä»…å¿«è¿›å¼ï¼ˆFast-forward)åˆå¹¶ã€‚"
+msgid "ProjectSettings|Feature flags"
+msgstr "功能标志"
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr "用于在此项目中å作开å‘想法和计划工作的çµæ´»å·¥å…·ã€‚"
@@ -30724,6 +30991,9 @@ msgstr "需求"
msgid "ProjectSettings|Requirements management system."
msgstr "需求管ç†ç³»ç»Ÿã€‚"
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr "æœç´¢ä¸»é¢˜"
@@ -31534,8 +31804,8 @@ msgstr "默认"
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr "%{environment_name} 将对开å‘人员å¯å†™ã€‚确定继续å—?"
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
-msgstr "指定了以下部署级别的所有环境å‡å—父组ä¿æŠ¤ã€‚%{link_start}了解更多%{link_end}。"
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
msgstr "å…许部署"
@@ -31552,8 +31822,8 @@ msgstr "上游ä¿æŠ¤çš„环境"
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr "无法加载此群组的详细信æ¯ã€‚"
-msgid "ProtectedEnvironment|No environments in this project are projected."
-msgstr "此项目中没有环境å—到ä¿æŠ¤ã€‚"
+msgid "ProtectedEnvironment|No environments in this project are protected."
+msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
msgstr "åªæœ‰æŒ‡å®šçš„群组æ‰èƒ½åœ¨å—ä¿æŠ¤çš„环境中执行部署。"
@@ -31691,10 +31961,10 @@ msgid "Puma is running with a thread count above 1 and the Rugged service is ena
msgstr "Pumaè¿è¡Œçš„线程数大于1,并且å¯ç”¨äº†RuggedæœåŠ¡ã€‚在æŸäº›çŽ¯å¢ƒä¸­ï¼Œè¿™å¯èƒ½ä¼šå¯¼è‡´æ€§èƒ½é™ä½Žã€‚有关此问题的详细信æ¯ï¼Œè¯·å‚è§æˆ‘们的%{link_start}文档%{link_end}。"
msgid "PumbleIntegration|Send notifications about project events to Pumble."
-msgstr ""
+msgstr "å‘ Pumble å‘é€æœ‰å…³é¡¹ç›®äº‹ä»¶çš„通知。"
msgid "PumbleIntegration|Send notifications about project events to Pumble. %{docs_link}"
-msgstr ""
+msgstr "å‘ Pumble å‘é€æœ‰å…³é¡¹ç›®äº‹ä»¶çš„通知。%{docs_link}"
msgid "Purchase more minutes"
msgstr "购买更多时间"
@@ -31735,9 +32005,6 @@ msgstr "推é€äº‹ä»¶"
msgid "Push project from command line"
msgstr "从命令行推é€é¡¹ç›®"
-msgid "Push rules"
-msgstr "推é€è§„则"
-
msgid "Push the target branch up to GitLab."
msgstr "将目标分支推é€åˆ° GitLab。"
@@ -31861,9 +32128,6 @@ msgstr "快速帮助"
msgid "Quick range"
msgstr "å¿«æ·èŒƒå›´"
-msgid "Quickly and easily edit multiple files in your project."
-msgstr "快速轻æ¾åœ°ç¼–辑您项目中的多个文件。"
-
msgid "Quota of CI/CD minutes"
msgstr "CI/CD 分钟数é…é¢"
@@ -32093,6 +32357,9 @@ msgstr "使用此网å€æ³¨å†ŒRunner:"
msgid "Register with two-factor app"
msgstr "使用åŒé‡è®¤è¯åº”用注册"
+msgid "Register with:"
+msgstr "通过以下方å¼æ³¨å†Œï¼š"
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr "å¯ç”¨æœåŠ¡ Ping 并注册此功能。"
@@ -32638,7 +32905,7 @@ msgid "Report for the scan has been removed from the database."
msgstr "扫æ报告已从数æ®åº“中删除。"
msgid "Report version not provided, %{report_type} report type supports versions: %{supported_schema_versions}. GitLab will attempt to validate this report against the earliest supported versions of this report type, to show all the errors but will not ingest the report"
-msgstr ""
+msgstr "未æ供报告版本, %{report_type} 报告类型支æŒç‰ˆæœ¬ï¼š %{supported_schema_versions}。系统将å°è¯•æ ¹æ®æ­¤æŠ¥å‘Šç±»åž‹çš„最早支æŒç‰ˆæœ¬éªŒè¯æ­¤æŠ¥å‘Šï¼Œä»¥æ˜¾ç¤ºæ‰€æœ‰é”™è¯¯ï¼Œä½†ä¸ä¼šæå–报告"
msgid "Report your license usage data to GitLab"
msgstr "报告您的许å¯è¯ä½¿ç”¨æ•°æ®"
@@ -33418,7 +33685,7 @@ msgstr "Runner页é¢."
msgid "Runners|%d selected runner deleted"
msgid_plural "Runners|%d selected runners deleted"
-msgstr[0] ""
+msgstr[0] "%d 个选定的 runners 已删除"
msgid "Runners|%{percentage} spot."
msgstr "%{percentage} 点。"
@@ -33446,6 +33713,9 @@ msgstr "å¯ç”¨"
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr "添加备注,比如è°æ‹¥æœ‰ runner,或者它应该用于什么目的。"
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr "全部"
@@ -33461,6 +33731,12 @@ msgstr "具有手动缩放和å¯é€‰è°ƒåº¦åŠŸèƒ½çš„ Amazon Linux 2 Docker HA。No
msgid "Runners|An error has occurred fetching instructions"
msgstr "获å–指令时å‘生错误"
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr "架构"
@@ -33509,6 +33785,9 @@ msgstr "å¤åˆ¶è¯´æ˜Ž"
msgid "Runners|Copy registration token"
msgstr "å¤åˆ¶æ³¨å†Œä»¤ç‰Œ"
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] "删除 %d 个 runner"
@@ -33555,6 +33834,9 @@ msgstr "输入秒数。此超时时间优先级高于为项目设定的较低超
msgid "Runners|Executor"
msgstr "执行器"
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr "Runner 入门"
@@ -33579,6 +33861,9 @@ msgstr "作业"
msgid "Runners|Last contact"
msgstr "最åŽè”ç³»"
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr "é”定到此项目"
@@ -33624,24 +33909,15 @@ msgstr "ä¸æŽ¥å—作业"
msgid "Runners|Offline"
msgstr "离线"
-msgid "Runners|Offline runners"
-msgstr "离线 Runners"
-
msgid "Runners|Offline:"
msgstr "离线:"
msgid "Runners|Online"
msgstr "在线"
-msgid "Runners|Online runners"
-msgstr "在线 Runners"
-
msgid "Runners|Online:"
msgstr "在线:"
-msgid "Runners|Outdated"
-msgstr "已过期"
-
msgid "Runners|Pause from accepting jobs"
msgstr "æš‚åœæŽ¥å—作业"
@@ -33776,7 +34052,7 @@ msgid "Runners|Show runner installation instructions"
msgstr "显示 Runner 安装说明"
msgid "Runners|Something went wrong while deleting. Please refresh the page to try again."
-msgstr ""
+msgstr "删除时出错。请刷新页é¢é‡è¯•ã€‚"
msgid "Runners|Something went wrong while fetching runner data."
msgstr "获å–Runneræ•°æ®æ—¶å‡ºé”™ã€‚"
@@ -33787,9 +34063,6 @@ msgstr "获å–标签建议时出现问题"
msgid "Runners|Stale"
msgstr "Stale"
-msgid "Runners|Stale runners"
-msgstr "过期 runners"
-
msgid "Runners|Stale:"
msgstr "过期:"
@@ -33872,9 +34145,18 @@ msgstr "值"
msgid "Runners|Version"
msgstr "版本"
+msgid "Runners|Version %{version}"
+msgstr "版本 %{version}"
+
msgid "Runners|View installation instructions"
msgstr "查看安装说明"
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr "Windows 2019 Shell,具有手动缩放和å¯é€‰è°ƒåº¦åŠŸèƒ½ã€‚ %{percentage} 点。"
@@ -33893,42 +34175,18 @@ msgstr "您已使用了%{quotaUsed},超出了共享æµæ°´çº¿æ—¶é—´é…é¢é™åˆ¶
msgid "Runners|active"
msgstr "å¯ç”¨"
-msgid "Runners|available"
-msgstr "å¯ç”¨"
-
msgid "Runners|group"
msgstr "群组"
-msgid "Runners|never contacted"
-msgstr "从未连接过"
-
-msgid "Runners|offline"
-msgstr "离线"
-
-msgid "Runners|online"
-msgstr "在线"
-
msgid "Runners|paused"
msgstr "已暂åœ"
-msgid "Runners|recommended"
-msgstr "推è"
-
msgid "Runners|shared"
msgstr "共享"
msgid "Runners|specific"
msgstr "特定"
-msgid "Runners|stale"
-msgstr "stale"
-
-msgid "Runners|upgrade available"
-msgstr "å‡çº§å¯ç”¨"
-
-msgid "Runners|upgrade recommended"
-msgstr "推èå‡çº§"
-
msgid "Runner|Owner"
msgstr "所有者"
@@ -34022,6 +34280,9 @@ msgstr "SSL验è¯ï¼š"
msgid "SSL verification"
msgstr "SSL 验è¯"
+msgid "Sat"
+msgstr "星期六"
+
msgid "Satisfied"
msgstr "满足"
@@ -34067,12 +34328,27 @@ msgstr "ä¿å­˜ä¸­"
msgid "Saving project."
msgstr "正在ä¿å­˜é¡¹ç›®ã€‚"
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
+msgstr "%{ifLabelStart}如果%{ifLabelEnd} %{rules} 用于 %{branches} 分支"
+
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
msgstr ""
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr "æµæ°´çº¿è¿è¡Œä¸­"
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr "计划"
@@ -34082,6 +34358,18 @@ msgstr "计划规则组件"
msgid "ScanExecutionPolicy|Select branches"
msgstr "选择分支"
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+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} çš„æ¼æ´ž"
@@ -34651,6 +34939,9 @@ msgstr ".yaml 预览"
msgid "SecurityOrchestration|Actions"
msgstr "行动"
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr "添加规则"
@@ -34906,6 +35197,9 @@ msgstr "创建新的安全策略时出现问题"
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr "æ­¤ %{namespaceType} ä¸åŒ…å«ä»»ä½•å®‰å…¨ç­–略。"
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr "此群组"
@@ -36288,12 +36582,21 @@ msgstr "退出页é¢URL"
msgid "Sign-up restrictions"
msgstr "注册é™åˆ¶"
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr "通过点击 %{button_text},我åŒæ„æˆ‘å·²é˜…è¯»å¹¶æŽ¥å— %{link_start}使用æ¡æ¬¾å’Œéšç§æ”¿ç­–%{link_end}"
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr "点击 %{button_text},我åŒæ„æˆ‘å·²é˜…è¯»å¹¶æŽ¥å— GitLab %{link_start}使用æ¡æ¬¾å’Œéšç§æ”¿ç­–%{link_end}"
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr "å字太长(最多%{max_length}字符)。"
@@ -36567,6 +36870,9 @@ msgstr "å°è¯•åŠ è½½è®®é¢˜è”系人时出错。"
msgid "Something went wrong when reordering designs. Please try again"
msgstr "é‡æ–°æŽ’åºè®¾è®¡æ—¶å‡ºäº†ç‚¹é—®é¢˜ã€‚请å†è¯•ä¸€æ¬¡"
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr "添加赞èµæ—¶å‡ºé”™ã€‚请å†è¯•ä¸€æ¬¡ã€‚"
@@ -36657,6 +36963,9 @@ msgstr "获å–Let's Encryptè¯ä¹¦æ—¶å‡ºäº†é”™ã€‚"
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr "将议题å‡çº§åˆ°å²è¯—时出错。请å†è¯•ä¸€æ¬¡ã€‚"
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr "é‡æ–°æ‰“开需求时出了错。"
@@ -37041,6 +37350,9 @@ msgstr "压缩æ交消æ¯"
msgid "Squash commits"
msgstr "压缩æ交"
+msgid "Squash commits when merge request is accepted."
+msgstr "接å—åˆå¹¶è¯·æ±‚时压缩æ交"
+
msgid "Stack trace"
msgstr "堆栈跟踪"
@@ -37056,9 +37368,6 @@ msgstr "阶段:"
msgid "Standard"
msgstr "标准"
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr "把一个标记加上星标,å¯å°†å…¶å˜ä¸ºä¼˜å…ˆæ ‡è®°ã€‚通过拖放æ¥è°ƒæ•´ä¼˜å…ˆæ ‡è®°çš„顺åºï¼Œå¯ä»¥æ”¹å˜ä»–们的相对优先级。"
-
msgid "Star labels to start sorting by priority"
msgstr "星标以优先级开始排åº"
@@ -37795,7 +38104,10 @@ msgid "Summary / note"
msgstr "摘è¦/备注"
msgid "Summary comment (optional)"
-msgstr "摘è¦è¯„论(å¯é€‰ï¼‰"
+msgstr "添加评审摘è¦ï¼ˆå¯é€‰ï¼‰"
+
+msgid "Sun"
+msgstr "星期日"
msgid "Sunday"
msgstr "星期日"
@@ -39046,6 +39358,9 @@ msgstr "页é¢å·²è¶…时,无法显示。"
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr "父å²è¯—是ç§å¯†çš„,åªèƒ½åŒ…å«ç§å¯†å²è¯—和议题"
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr "Jenkins æœåŠ¡å™¨çš„密ç ã€‚"
@@ -39142,6 +39457,12 @@ msgstr "该代ç ç‰‡æ®µå¯¹é™¤å¤–部用户外的所有登录用户å¯è§ã€‚"
msgid "The source project of this merge request has been removed."
msgstr "æ­¤åˆå¹¶è¯·æ±‚çš„æºé¡¹ç›®å·²è¢«åˆ é™¤ã€‚"
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr "指定标签页无效,请选择å¦ä¸€ä¸ª"
@@ -39154,6 +39475,9 @@ msgstr "该主题将用作新议题的标题,信æ¯å°†ä½œä¸ºè¯´æ˜Žã€‚ æ”¯æŒ %
msgid "The tag name can't be changed for an existing release."
msgstr "对于现有å‘布,ä¸èƒ½æ›´æ”¹æ ‡ç­¾å称。"
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr "æ¯ä¸ªé¡¹ç›®æœ€å¤§è¯·æ±‚所适用的时间间隔(秒)。"
@@ -39202,12 +39526,12 @@ msgstr "主题"
msgid "There are currently no events."
msgstr "当å‰æ²¡æœ‰äº‹ä»¶ã€‚"
+msgid "There are currently no mirrored repositories."
+msgstr ""
+
msgid "There are merge conflicts"
msgstr "存在åˆå¹¶å†²çª"
-msgid "There are no %{replicableTypeName} to show"
-msgstr "没有å¯æ˜¾ç¤ºçš„%{replicableTypeName}"
-
msgid "There are no GPG keys associated with this account."
msgstr "没有与此账户关è”çš„GPG密钥。"
@@ -39310,9 +39634,6 @@ msgstr "为了ä¿æŠ¤è¯¥ç³»ç»Ÿï¼Œå®žè¡Œäº†å‡ ç§é€ŸçŽ‡é™åˆ¶ã€‚"
msgid "There are several size limits in place."
msgstr "有几个大å°é™åˆ¶ã€‚"
-msgid "There are unsubmitted review comments."
-msgstr "有未æ交的审核评论。"
-
msgid "There is already a repository with that name on disk"
msgstr "ç£ç›˜ä¸Šå·²å­˜åœ¨å…·æœ‰è¯¥å称的仓库"
@@ -39664,6 +39985,9 @@ msgstr "该阻塞为自我引用"
msgid "This board's scope is reduced"
msgstr "此看æ¿èŒƒå›´å·²ç¼©å°"
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr "无法显示此图表"
@@ -39688,8 +40012,8 @@ msgstr "æ­¤æ交使用%{strong_open}已验è¯çš„%{strong_close}ç­¾å进行签ç
msgid "This commit was signed with a different user's verified signature."
msgstr "æ­¤æ交使用其他用户的已验è¯ç­¾å进行签å。"
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
-msgstr "æ­¤æ交已使用ç»è¿‡éªŒè¯çš„ç­¾å进行签署,但%{strong_open}尚未验è¯%{strong_close}æ交者电å­é‚®ä»¶å±žäºŽåŒä¸€ç”¨æˆ·ã€‚"
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
+msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr "æ­¤æ交使用%{strong_open}未ç»éªŒè¯çš„%{strong_close}ç­¾å进行签署。"
@@ -39725,7 +40049,7 @@ msgid "This domain is not verified. You will need to verify ownership before acc
msgstr "此域å未ç»è¿‡éªŒè¯ã€‚在å¯ç”¨è®¿é—®æƒé™ä¹‹å‰ï¼Œæ‚¨éœ€è¦éªŒè¯æ‰€æœ‰æƒã€‚"
msgid "This email address does not look right, are you sure you typed it correctly?"
-msgstr "此电å­é‚®ä»¶åœ°å€çœ‹èµ·æ¥ä¸å¤§å¤ªåŠ²ã€‚请检查您是å¦è¾“入正确?"
+msgstr "此电å­é‚®ä»¶åœ°å€çœ‹èµ·æ¥ä¸æ­£ç¡®ï¼Œæ‚¨ç¡®å®šè¾“入正确å—?"
msgid "This email supersedes any previous emails about scheduled deletion you may have received for %{project_link}."
msgstr "此电å­é‚®ä»¶å–代了以å‰ä»»ä½•æ‚¨å¯èƒ½å·²ç»æ”¶åˆ°çš„ %{project_link} 计划删除的电å­é‚®ä»¶ã€‚"
@@ -40069,6 +40393,9 @@ msgstr "该项目在 %{strong_start}%{license_name}%{strong_end}下获得许å¯ã
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr "此项目使用%{strong_start}%{manager_name}%{strong_end}管ç†å…¶ä¾èµ–关系"
@@ -40081,14 +40408,11 @@ msgstr "此项目将于 %{date} 删除"
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr "此项目将于 %{date} 删除,因为它的父组 '%{parent_group_name}'已计划删除。"
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr "该项目将存在于您的群组%{strong_open}%{namespace}%{strong_close}中。在项目中,您å¯ä»¥å­˜æ”¾æ–‡ä»¶(仓库),计划工作(议题),å‘布文档(Wiki)等等。"
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr "æ­¤å‘布是使用过去的日期创建的,当时 Evidence 收集功能ä¸å¯ç”¨ã€‚"
msgid "This report uses a supported MAJOR.MINOR schema version but the PATCH version doesn't match any vendored schema version. Validation will be attempted against version %{find_latest_patch_version}"
-msgstr ""
+msgstr "此报告使用å—支æŒçš„ MAJOR.MINOR 架构版本,但 PATCH 版本与任何供应商的架构版本都ä¸åŒ¹é…。将å°è¯•é’ˆå¯¹ç‰ˆæœ¬ %{find_latest_patch_version}进行验è¯"
msgid "This repository"
msgstr "当å‰ä»“库"
@@ -40186,6 +40510,9 @@ msgstr "å•ä¸ªæŽ¨é€ä¸­æ›´æ”¹ï¼ˆåˆ†æ”¯æˆ–标签)的阈值数é‡ï¼Œé«˜äºŽè¯¥é˜ˆ
msgid "Throughput"
msgstr "åžåé‡"
+msgid "Thu"
+msgstr "星期四"
+
msgid "Thursday"
msgstr "星期四"
@@ -40396,6 +40723,9 @@ msgstr "刚刚"
msgid "Timeago|right now"
msgstr "刚刚"
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr "关闭最近更新视图"
@@ -40423,6 +40753,24 @@ msgstr "最快的 Gitaly æ“作超时(以秒为å•ä½ï¼‰ã€‚"
msgid "Timezone"
msgstr "时区"
+msgid "Time|A"
+msgstr "上åˆ"
+
+msgid "Time|AM"
+msgstr "上åˆ"
+
+msgid "Time|P"
+msgstr "下åˆ"
+
+msgid "Time|PM"
+msgstr "下åˆ"
+
+msgid "Time|a"
+msgstr "上åˆ"
+
+msgid "Time|am"
+msgstr "上åˆ"
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "å°æ—¶"
@@ -40431,6 +40779,12 @@ msgid "Time|min"
msgid_plural "Time|mins"
msgstr[0] "分钟"
+msgid "Time|p"
+msgstr "下åˆ"
+
+msgid "Time|pm"
+msgstr "下åˆ"
+
msgid "Time|s"
msgstr "秒"
@@ -40485,6 +40839,9 @@ msgstr "è¦æ·»åŠ è‡ªå®šä¹‰åŽç¼€ï¼Œè¯·è®¾ç½®æœåŠ¡å°ç”µå­é‚®ä»¶åœ°å€ã€‚%{link
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr "如需手动添加æ¡ç›®ï¼Œè¯·åœ¨æ‰‹æœºåº”用中æ供以下信æ¯ã€‚"
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr "è¦å®Œæˆæ³¨å†Œï¼Œæˆ‘们需è¦æ‚¨æ供更多信æ¯ã€‚"
@@ -40647,9 +41004,6 @@ msgstr "è¦æ‰©å¤§æœç´¢èŒƒå›´ï¼Œè¯·æ›´æ”¹æˆ–删除上é¢çš„筛选器"
msgid "To widen your search, change or remove filters above."
msgstr "è¦æ‰©å¤§æœç´¢èŒƒå›´ï¼Œè¯·æ›´æ”¹æˆ–删除上é¢çš„筛选器。"
-msgid "To-Do"
-msgstr "待办"
-
msgid "To-Do List"
msgstr "待办事项列表"
@@ -40674,6 +41028,18 @@ msgstr "按群组筛选"
msgid "Todos|Filter by project"
msgstr "按项目筛选"
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr "这是您始终知é“下一步该åšä»€ä¹ˆçš„æ–¹å¼ã€‚"
@@ -40683,6 +41049,9 @@ msgstr "标记全部完æˆ"
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr "您的待办事项列表中没有任何事项。"
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr "撤销标记全部完æˆ"
@@ -40791,9 +41160,18 @@ msgstr "引用太多。快速æ“作仅é™äºŽæœ€å¤š %{max_count} 个用户引用"
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr "找到了太多用户。快速æ“作仅é™äºŽæœ€å¤š %{max_count} 个用户"
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr "返回"
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr "主题 %{source_topic} å·²æˆåŠŸåˆå¹¶åˆ°ä¸»é¢˜ %{target_topic} 中。"
@@ -40818,9 +41196,21 @@ msgstr "主题标题"
msgid "Topic was successfully updated."
msgstr "主题已æˆåŠŸæ›´æ–°ã€‚"
+msgid "TopicSelect|No matching results"
+msgstr "没有匹é…的结果"
+
+msgid "TopicSelect|Search topics"
+msgstr "æœç´¢ä¸»é¢˜"
+
+msgid "TopicSelect|Select a topic"
+msgstr "选择一个主题"
+
msgid "Topics"
msgstr "主题"
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr "全部"
@@ -40989,39 +41379,12 @@ msgstr "您的试用版在 %{boldStart}%{trialEndDate}%{boldEnd}结æŸï¼Œæˆ‘们å
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr "å…许的字符:+ã€0-9ã€- 和空格。"
-msgid "Trial|Company name"
-msgstr "å…¬å¸å称"
-
msgid "Trial|Continue"
msgstr "继续"
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr "继续å…费使用基本功能。"
-
-msgid "Trial|Country"
-msgstr "国家"
-
msgid "Trial|Dismiss"
msgstr "驳回"
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr "GitLab Ultimate 试用版(å¯é€‰ï¼‰"
-
-msgid "Trial|Number of employees"
-msgstr "员工人数"
-
-msgid "Trial|Please select a country"
-msgstr "请选择国家"
-
-msgid "Trial|Telephone number"
-msgstr "电è¯å·ç "
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr "å‡çº§ä¸ºæ——舰版以ä¿æŒä½¿ç”¨ GitLab 高级功能。"
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr "在您完æˆæ­¤æ­¥éª¤åŽï¼Œæˆ‘们将在您的群组中激活您的试用。 30 天åŽï¼Œæ‚¨å¯ä»¥ï¼š"
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr "您的 GitLab Ultimate 试用期为 30 天,但您å¯ä»¥æ°¸ä¹…ä¿ç•™å…费的 GitLab å¸æˆ·ã€‚我们åªéœ€è¦ä¸€äº›é¢å¤–çš„ä¿¡æ¯æ¥æ¿€æ´»æ‚¨çš„试用版。"
@@ -41127,21 +41490,18 @@ msgstr "å°è¯•ä¸Žæ‚¨çš„设备通信。请将其æ’å…¥(如需è¦)并立å³æŒ‰ä¸‹ä
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr "å°è¯•ä¸Žæ‚¨çš„设备通信。请将其æ’入并立å³æŒ‰ä¸‹ä¸Šçš„按钮。"
+msgid "Tue"
+msgstr "星期二"
+
msgid "Tuesday"
msgstr "星期二"
msgid "Turn off"
msgstr "关闭"
-msgid "Turn off notifications"
-msgstr "关闭通知"
-
msgid "Turn on"
msgstr "打开"
-msgid "Turn on notifications"
-msgstr "打开通知"
-
msgid "Twitter"
msgstr "Twitter"
@@ -41721,6 +42081,9 @@ msgstr "CI/CD 分钟数使用é‡"
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr "%{timeElapsed} 以æ¥çš„ CI/CD 分钟数使用é‡"
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr "代ç åŒ…和容器镜åƒã€‚"
@@ -41748,6 +42111,9 @@ msgstr "存储 Docker é•œåƒçš„ Gitlab-integrated Docker Container Registry。"
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr "存储 Docker é•œåƒçš„ Gitlab-integrated Docker Container Registry。%{linkStart}更多信æ¯%{linkEnd}"
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr "包括产物ã€ä»“库ã€wikiã€ä¸Šä¼ æ–‡ä»¶å’Œå…¶å®ƒäº‹é¡¹ã€‚"
@@ -41844,10 +42210,10 @@ msgstr "%{strong_start}%{context_name}%{strong_end} 群组将å—此影å“。"
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr "%{strong_start}%{context_name}%{strong_end} 项目将å—此影å“。"
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
-msgstr "命å空间当å‰æ­£åœ¨ä½¿ç”¨ %{strong_start}%{used_storage}%{strong_end} 的命å空间存储。群组所有者å¯ä»¥ä»Ž %{strong_start}群组设置 &gt; 使用é…é¢%{strong_end}查看命å空间存储使用情况并购买更多存储。%{docs_link_start}详è§%{link_end}有关如何å‡å°‘存储空间的信æ¯ã€‚"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
+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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -41913,8 +42279,8 @@ msgstr "您的项目中资æºä½¿ç”¨æƒ…况"
msgid "UsageQuota|Usage quotas help link"
msgstr "使用é…é¢å¸®åŠ©é“¾æŽ¥"
-msgid "UsageQuota|Usage since %{usageSince}"
-msgstr "从 %{usageSince} 以æ¥çš„使用情况"
+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."
msgstr "当您购买更多的存储空间时,我们会自动解é”达到%{actualRepositorySizeLimit}é™åˆ¶æ—¶è¢«é”定的项目。"
@@ -42075,6 +42441,9 @@ msgstr "使用议题计数"
msgid "Use issue weight"
msgstr "使用议题æƒé‡"
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr "æ¯ä¸ªURIå ä¸€è¡Œ"
@@ -42475,8 +42844,8 @@ msgstr "用户 API 调用速率é™åˆ¶"
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr "%{linkStart}Gitpod%{linkEnd} 集æˆåŽï¼Œç”¨æˆ·å¯ä»¥ä»Ž GitLab æµè§ˆå™¨é€‰é¡¹å¡å¯åŠ¨å¼€å‘环境。"
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
-msgstr "用户å¯ä»¥é€šè¿‡ç™»å½•é‡æ–°æ¿€æ´»ä»–们的å¸æˆ·ã€‚ %{link_start}了解更多的%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
+msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
msgstr "用户å¯ä»¥ä½¿ç”¨ Kroki 在 AsciiDocã€Markdownã€reStructuredText å’Œ Textile 文档中渲染图表。"
@@ -42635,10 +43004,10 @@ msgid "ValueStreamAnalytics|Percentage of deployments that cause an incident in
msgstr "导致生产事故的部署百分比。"
msgid "ValueStreamAnalytics|Shows %{selectedSubjectFilterText} and %{labelsCount} for group '%{groupName}' and %{projectsCount} from %{createdAfter} to %{createdBefore}"
-msgstr ""
+msgstr "显示 %{selectedSubjectFilterText},和群组 \"%{groupName}\" çš„ %{labelsCount},以åŠä»Ž %{createdAfter} 到 %{createdBefore} çš„ %{projectsCount}。"
msgid "ValueStreamAnalytics|Shows %{selectedSubjectFilterText} and %{labelsCount} for group '%{groupName}' from %{createdAfter} to %{createdBefore}"
-msgstr ""
+msgstr "显示 %{selectedSubjectFilterText},和群组 \"%{groupName}\" 的 %{labelsCount},从 %{createdAfter} 到 %{createdBefore}。"
msgid "ValueStreamAnalytics|Shows %{selectedSubjectFilterText} for group '%{groupName}' and %{projectsCount} from %{createdAfter} to %{createdBefore}"
msgstr "显示群组 “%{groupName}†的 %{selectedSubjectFilterText} 和从 %{createdAfter} 到 %{createdBefore} 的 %{projectsCount}。"
@@ -42734,10 +43103,10 @@ msgid "Version"
msgstr "版本"
msgid "Version %{report_version} for report type %{report_type} has been deprecated, supported versions for this report type are: %{supported_schema_versions}. GitLab will attempt to parse and ingest this report if valid."
-msgstr ""
+msgstr "报告类型 %{report_type} 的版本 %{report_version} 已被弃用,此报告类型支æŒçš„版本是: %{supported_schema_versions}。如果有效,GitLab å°†å°è¯•è§£æžå’Œæ‘„å–此报告。"
msgid "Version %{report_version} for report type %{report_type} is unsupported, supported versions for this report type are: %{supported_schema_versions}. GitLab will attempt to validate this report against the earliest supported versions of this report type, to show all the errors but will not ingest the report"
-msgstr ""
+msgstr "ä¸æ”¯æŒæŠ¥å‘Šç±»åž‹ %{report_type} 的版本 %{report_version} ,此报告类型支æŒçš„版本是:%{supported_schema_versions}。系统将å°è¯•æ ¹æ®æ­¤æŠ¥å‘Šç±»åž‹çš„最早支æŒç‰ˆæœ¬éªŒè¯æ­¤æŠ¥å‘Šï¼Œä»¥æ˜¾ç¤ºæ‰€æœ‰é”™è¯¯ï¼Œä½†ä¸ä¼šæå–报告"
msgid "Version %{versionNumber}"
msgstr "版本 %{versionNumber}"
@@ -42769,11 +43138,14 @@ msgstr "查看警报详细信æ¯ã€‚"
msgid "View all environments."
msgstr "查看所有环境。"
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr "查看所有议题"
-msgid "View all personal projects"
-msgstr "查看所有个人项目"
+msgid "View all projects"
+msgstr ""
msgid "View blame"
msgstr "查看 blame"
@@ -43465,6 +43837,12 @@ msgstr "转到派生"
msgid "WebIDE|Merge request"
msgstr "åˆå¹¶è¯·æ±‚"
+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|This project does not accept unsigned commits."
msgstr "此项目ä¸æŽ¥å—未签åçš„æ交。"
@@ -43546,6 +43924,9 @@ msgstr "已创建或更新å‘布。"
msgid "Webhooks|A subgroup is created or removed."
msgstr "已创建或删除å­ç»„。"
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr "已创建或更新 wiki 页é¢ã€‚"
@@ -43588,6 +43969,9 @@ msgstr "连接失败"
msgid "Webhooks|Feature flag events"
msgstr "功能标志事件"
+msgid "Webhooks|Go to webhooks"
+msgstr "跳转到 Webhooks"
+
msgid "Webhooks|Issues events"
msgstr "议题事件"
@@ -43645,6 +44029,9 @@ msgstr "如果 URL 包å«ä¸€ä¸ªæˆ–多个特殊字符,则必须进行百分å·ç
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr "用于验è¯æŽ¥æ”¶åˆ°çš„有效载è·ã€‚与 %{code_start}X-Gitlab-Token HTTP%{code_end} 标头中的请求一起å‘é€ã€‚"
+msgid "Webhooks|Webhook disabled"
+msgstr "Webhook å·²ç¦ç”¨"
+
msgid "Webhooks|Webhook failed to connect"
msgstr "Webhook 连接失败"
@@ -43663,6 +44050,9 @@ msgstr "网站"
msgid "Website:"
msgstr "网站:"
+msgid "Wed"
+msgstr "星期三"
+
msgid "Wednesday"
msgstr "星期三"
@@ -43714,6 +44104,9 @@ msgstr "设置有什么影å“?"
msgid "What does this command do?"
msgstr "这个命令有什么作用?"
+msgid "What is GitLab Runner?"
+msgstr "什么是 Runner?"
+
msgid "What is Markdown?"
msgstr "什么是 Markdown?"
@@ -43923,9 +44316,6 @@ msgstr "删除页é¢%{pageTitle}?"
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr "有人在您编辑页é¢çš„åŒæ—¶ç¼–辑了页é¢ã€‚请查看%{wikiLinkStart}页é¢%{wikiLinkEnd}并确ä¿æ‚¨çš„更改ä¸ä¼šæ— æ„中删除它们的更改。"
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr "å°è¯•æ¸²æŸ“内容编辑器时å‘生错误,请ç¨åŽå†è¯•ã€‚"
-
msgid "WikiPage|Cancel"
msgstr "å–消"
@@ -43950,9 +44340,6 @@ msgstr "了解更多。"
msgid "WikiPage|Page title"
msgstr "页é¢æ ‡é¢˜"
-msgid "WikiPage|Retry"
-msgstr "é‡è¯•"
-
msgid "WikiPage|Save changes"
msgstr "ä¿å­˜æ›´æ”¹"
@@ -44040,11 +44427,11 @@ msgstr "正在进行中(开放和未分é…)"
msgid "Work in progress Limit"
msgstr "“进行中â€é™åˆ¶"
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
-msgstr "任务æ供了将您的工作分解æˆä¸Žè®®é¢˜ç›¸å…³çš„多个部分的能力。任务使用我们新的 %{workItemsLink} 对象。更多的工作项类型将很快到æ¥ã€‚"
+msgid "WorkItem|%{workItemType} deleted"
+msgstr "%{workItemType} 已删除"
-msgid "WorkItem|Add a task"
-msgstr "添加任务"
+msgid "WorkItem|Add"
+msgstr "添加"
msgid "WorkItem|Add a title"
msgstr "添加标题"
@@ -44061,8 +44448,8 @@ msgstr "添加任务"
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr "您确定è¦å–消编辑å—?"
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
-msgstr "确定è¦åˆ é™¤æ­¤ä»»åŠ¡å—?此æ“作无法撤消。"
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
+msgstr ""
msgid "WorkItem|Assignee"
msgid_plural "WorkItem|Assignees"
@@ -44071,17 +44458,14 @@ msgstr[0] "指派人"
msgid "WorkItem|Cancel"
msgstr "å–消"
-msgid "WorkItem|Child items"
-msgstr "å­é¡¹"
-
msgid "WorkItem|Child removed"
msgstr "已删除å­é¡¹"
msgid "WorkItem|Closed"
msgstr "已关闭"
-msgid "WorkItem|Collapse child items"
-msgstr "折å å­é¡¹"
+msgid "WorkItem|Collapse tasks"
+msgstr "折å ä»»åŠ¡"
msgid "WorkItem|Create task"
msgstr "创建任务"
@@ -44089,20 +44473,32 @@ msgstr "创建任务"
msgid "WorkItem|Create work item"
msgstr "创建工作项"
-msgid "WorkItem|Delete task"
-msgstr "删除任务"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr "删除 %{workItemType}"
+
+msgid "WorkItem|Expand tasks"
+msgstr "展开任务"
-msgid "WorkItem|Expand child items"
-msgstr "展开å­é¡¹"
+msgid "WorkItem|Incident"
+msgstr "事件"
msgid "WorkItem|Introducing tasks"
msgstr "介ç»ä»»åŠ¡"
-msgid "WorkItem|Learn about tasks"
-msgstr "了解任务"
+msgid "WorkItem|Issue"
+msgstr "议题"
+
+msgid "WorkItem|Learn about tasks."
+msgstr "了解任务详情。"
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
-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|Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task."
+msgstr ""
msgid "WorkItem|Open"
msgstr "开放"
@@ -44110,20 +44506,23 @@ msgstr "开放"
msgid "WorkItem|Remove"
msgstr "删除"
+msgid "WorkItem|Requirements"
+msgstr "è¦æ±‚"
+
msgid "WorkItem|Select type"
msgstr "选择类型"
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
-msgstr "创建任务时出错。请é‡è¯•"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
+msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
-msgstr "创建工作项时出现错误,请å†è¯•ä¸€æ¬¡ã€‚"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
+msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr "删除任务时出错。请é‡è¯•ã€‚"
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
-msgstr "删除工作项时出错。请å†è¯•ä¸€æ¬¡ã€‚"
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
+msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
msgstr "获å–工作项时出现错误,请å†è¯•ä¸€æ¬¡ã€‚"
@@ -44137,31 +44536,40 @@ msgstr "å°è¯•æ·»åŠ å­é¡¹æ—¶å‡ºé”™ã€‚请é‡è¯•ã€‚"
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr "å°è¯•åˆ›å»ºå­é¡¹æ—¶å‡ºé”™ï¼Œè¯·å†è¯•ä¸€æ¬¡ã€‚"
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr "更新工作项时出现错误,请å†è¯•ä¸€æ¬¡ã€‚"
+msgid "WorkItem|Task"
+msgstr "任务"
+
msgid "WorkItem|Task deleted"
msgstr "任务已删除"
+msgid "WorkItem|Tasks"
+msgstr "任务"
+
+msgid "WorkItem|Test case"
+msgstr "测试用例"
+
msgid "WorkItem|Turn off confidentiality"
-msgstr ""
+msgstr "关闭ç§å¯†"
msgid "WorkItem|Turn on confidentiality"
-msgstr ""
-
-msgid "WorkItem|Type"
-msgstr "类型"
+msgstr "å¼€å¯ç§å¯†"
msgid "WorkItem|Undo"
msgstr "撤消"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
+msgstr ""
+
msgid "WorkItem|Work Items"
msgstr "工作事项"
-msgid "WorkItem|Work item deleted"
-msgstr "已删除工作项"
-
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr "工作项"
msgid "Would you like to create a new branch?"
@@ -44384,6 +44792,10 @@ 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} 中查看它。"
@@ -44444,10 +44856,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] "在您的所有个人项目中,您最多å¯ä»¥æœ‰ %{free_user_limit} 个æˆå‘˜ã€‚"
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr "您å¯ä»¥é‚€è¯·ä¸€ä¸ªæ–°æˆå‘˜æˆ–å¦ä¸€ä¸ªç¾¤ç»„加入%{project_name}。"
@@ -44509,7 +44917,7 @@ msgid "You can view the source or %{linkStart}%{cloneIcon} clone the repository%
msgstr "您å¯ä»¥æŸ¥çœ‹æºä»£ç æˆ–%{linkStart}%{cloneIcon}克隆仓库%{linkEnd}"
msgid "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. To get more members an owner of the group can start a trial or upgrade to a paid tier."
-msgstr ""
+msgstr "您无法å†æ·»åŠ ï¼Œä½†å¯ä»¥ç®¡ç†çŽ°æœ‰æˆå‘˜ï¼Œä¾‹å¦‚,通过删除éžæ´»åŠ¨æˆå‘˜å¹¶å°†å…¶æ›¿æ¢ä¸ºæ–°æˆå‘˜ã€‚为了获得更多æˆå‘˜ï¼Œç¾¤ç»„的所有者å¯ä»¥å¼€å§‹è¯•ç”¨æˆ–å‡çº§åˆ°ä»˜è´¹ç‰ˆã€‚"
msgid "You cannot %{action} %{state} users."
msgstr "您ä¸èƒ½ %{action} %{state} 用户。"
@@ -44553,10 +44961,6 @@ msgstr "您ä¸èƒ½åœ¨è¿™ä¸ªé¡¹ç›®ä¸­ç›´æŽ¥ç¼–辑文件,请派生(Fork)这ä¸
msgid "You could not create a new trigger."
msgstr "您无法创建新的触å‘器。"
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-
msgid "You do not have any subscriptions yet"
msgstr "您当å‰å°šæœªè®¢é˜…任何计划"
@@ -44720,8 +45124,8 @@ 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."
-msgstr "您已æˆåŠŸè´­ä¹°äº†%{seats}用户的%{plan}计划订阅。收æ®å°†é€šè¿‡ç”µå­é‚®ä»¶å‘é€ç»™æ‚¨ã€‚"
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
+msgstr ""
msgid "You have unsaved changes"
msgstr "您有未ä¿å­˜çš„更改"
@@ -44783,6 +45187,9 @@ msgstr "您需è¦ä¸Šä¼ GitLab项目导出文件(以.gz结尾)."
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr "您需è¦å…ˆéªŒè¯æ‚¨çš„主è¦ç”µå­é‚®ä»¶ï¼Œç„¶åŽæ‰èƒ½å¯ç”¨åŒé‡èº«ä»½éªŒè¯ã€‚"
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr "您æˆåŠŸæ‹’ç»äº†é‚€è¯·"
@@ -44894,9 +45301,6 @@ msgstr "您正在查看 %{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 "您已ç»ä½¿ç”¨ä¸€æ¬¡å¯†ç éªŒè¯å™¨å¯ç”¨äº†åŒé‡è®¤è¯ã€‚如果您è¦æ³¨å†Œä¸åŒçš„设备,您必须先ç¦ç”¨åŒé‡è®¤è¯ã€‚"
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr "您已ç»è¾¾åˆ°æ‚¨æ‰€æœ‰ä¸ªäººé¡¹ç›®çš„ %{free_limit} æˆå‘˜é™åˆ¶"
-
msgid "You've rejected %{user}"
msgstr "您拒ç»äº† %{user}"
@@ -44936,6 +45340,12 @@ msgstr "您从项目%{project_name}(%{project_url})导出包å«%{written_count}ç
msgid "Your CSV import for project"
msgstr "您的项目CSV导入"
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr "您的 DevOps 报告从功能角度概述了您如何使用 GitLab。使用它们æ¥æŸ¥çœ‹æ‚¨ä¸Žå…¶ä»–组织的比较情况,以åŠæ‚¨çš„团队之间的比较情况。"
@@ -45086,9 +45496,6 @@ msgstr "您的设备已æˆåŠŸè®¾ç½®ï¼è¯·ç»™å®ƒå‘½å并将其注册到GitLabæœ
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr "您的文件必须包å«å为%{codeStart}title%{codeEnd}的列。%{codeStart}description%{codeEnd}列为å¯é€‰çš„。å…许的最大文件大å°ä¸º10 MB。"
-msgid "Your first project"
-msgstr "您的第一个项目"
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] "您的å…费群组现在é™åˆ¶ä¸º %d åæˆå‘˜"
@@ -45148,6 +45555,9 @@ msgstr "您的新访问令牌已创建。"
msgid "Your new comment"
msgstr "您的新评论"
+msgid "Your password"
+msgstr "您的密ç "
+
msgid "Your password reset token has expired."
msgstr "您的密ç é‡ç½®ä»¤ç‰Œå·²è¿‡æœŸã€‚"
@@ -45163,8 +45573,8 @@ msgstr "您的主è¦ç”µå­é‚®ä»¶ç”¨äºŽå¤´åƒæ£€æµ‹ã€‚您å¯ä»¥åœ¨æ‚¨çš„ %{openin
msgid "Your profile"
msgstr "个人资料"
-msgid "Your project has limited quotas and features"
-msgstr "您的项目的é…é¢å’ŒåŠŸèƒ½æœ‰é™"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
+msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
msgstr "您的被é™åˆ¶ä¸ºæœ€å¤§ %{limit} 个项目ï¼è¯·ä¸Žæ‚¨çš„管ç†å‘˜è”系以增加它"
@@ -45367,9 +45777,6 @@ msgstr "产物"
msgid "assign yourself"
msgstr "分é…给自己"
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr "å…³è”的父级是ç§å¯†çš„,ä¸èƒ½æœ‰éžç§å¯†çš„å­çº§ã€‚"
-
msgid "at"
msgstr "于"
@@ -45464,6 +45871,9 @@ msgstr "如果个人项目具有容器镜åƒåº“标签,则无法更改。"
msgid "cannot be changed if shared runners are enabled"
msgstr "如果å¯ç”¨å…±äº«Runner,则无法更改"
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr "无法å¯ç”¨"
@@ -45504,6 +45914,9 @@ msgid "change"
msgid_plural "changes"
msgstr[0] "å˜æ›´"
+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 "%{danger_start}%{degradedNum} é™ä½Ž%{danger_end}ã€%{same_start}%{sameNum} 相åŒ%{same_end} å’Œ %{success_start}%{improvedNum} 改善%{success_end}"
@@ -45552,8 +45965,8 @@ msgstr "%{reportType}:å‘生一个错误"
msgid "ciReport|%{sameNum} same"
msgstr "相åŒ%{sameNum}"
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
-msgstr "%{scanner} 检测到 %{boldStart}%{number} 个%{boldEnd}新的潜在%{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
+msgstr "%{scanner} 检测到 %{number} 新的潜在 %{vulnStr}"
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr "%{scanner} 检测到 %{strong_start}%{number} 个%{strong_end}新的潜在%{vulnStr}"
@@ -45748,7 +46161,7 @@ msgstr "管ç†è®¸å¯è¯"
msgid "ciReport|Manage licenses"
msgstr "管ç†è®¸å¯è¯"
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr "已手动添加"
msgid "ciReport|New"
@@ -45872,9 +46285,6 @@ msgstr "å·²æ交"
msgid "compliance violation has already been recorded"
msgstr "è¿è§„行为已被记录"
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr "如果有éžç§å¯†å­çº§ï¼Œåˆ™ä¸èƒ½ä½¿ç”¨ç§å¯†çˆ¶çº§ã€‚"
-
msgid "contacts can only be added to root groups"
msgstr "è”系人åªèƒ½æ·»åŠ åˆ°æ ¹ç¾¤ç»„"
@@ -45911,6 +46321,9 @@ msgstr "由 %{author} 创建于 %{timeAgo}"
msgid "created by"
msgstr "创建人:"
+msgid "daily"
+msgstr "æ¯æ—¥"
+
msgid "data"
msgstr "æ•°æ®"
@@ -46033,6 +46446,9 @@ msgstr "无法忽略关è”çš„å‘现(id=%{finding_id}): %{message}"
msgid "failed to dismiss finding: %{message}"
msgstr "忽略å‘现失败:%{message}"
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr "无法将关è”çš„å‘现(id=%{finding_id})æ¢å¤åˆ°ä¸ºæ£€æµ‹åˆ°"
@@ -46238,6 +46654,9 @@ msgstr "åªè¯»"
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr "太长(%{current_value})。最大值为%{max_size}。"
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr "过长(最长为 %{count} 个字符)"
@@ -46336,8 +46755,8 @@ msgid "merge request"
msgid_plural "merge requests"
msgstr[0] "åˆå¹¶è¯·æ±‚"
-msgid "mergedCommitsAdded|(commits were squashed)"
-msgstr "(æ交被压缩)"
+msgid "mergedCommitsAdded| (commits were squashed)"
+msgstr ""
msgid "metric_id must be unique across a project"
msgstr "metric_id必须是整个项目唯一的"
@@ -46357,8 +46776,8 @@ msgstr "%{commitCount} 和 %{mergeCommitCount} 将被添加到 %{targetBranch}%{
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr "%{commitCount} 将被添加到 %{targetBranch}。"
-msgid "mrWidgetCommitsAdded|1 merge commit"
-msgstr "1个åˆå¹¶æ交"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
+msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
msgstr "å·²åˆå¹¶åˆ° %{targetBranch} çš„ %{mergeCommitSha}%{squashedCommits} çš„å˜æ›´ã€‚"
@@ -46518,6 +46937,9 @@ msgid "mrWidget|Mentions issue"
msgid_plural "mrWidget|Mentions issues"
msgstr[0] "æåŠè®®é¢˜"
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr "åˆå¹¶è¢«é˜»æ­¢ï¼šå¿…须获得所有è¦æ±‚的批准。"
@@ -46653,9 +47075,6 @@ msgstr "è¦æ›´æ”¹æ­¤é»˜è®¤æ¶ˆæ¯ï¼Œè¯·ç¼–辑åˆå¹¶æ交消æ¯çš„模æ¿ã€‚ %{lin
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr "è¦æ›´æ”¹æ­¤é»˜è®¤æ¶ˆæ¯ï¼Œè¯·ç¼–辑压缩æ交消æ¯çš„模æ¿ã€‚ %{linkStart}了解更多信æ¯ã€‚%{linkEnd}"
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr "è¦åˆå¹¶ï¼Œå¿…须在标题或æ述中æ到Jira议题的key。"
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr "å¯ä»¥å†™å…¥æºæˆ–目标分支的用户å¯ä»¥è§£å†³å†²çªã€‚"
@@ -47211,6 +47630,9 @@ msgstr "已忽略"
msgid "was scheduled to merge after pipeline succeeds by"
msgstr "åˆå¹¶è¯·æ±‚已安排在æµæ°´çº¿æˆåŠŸåŽåˆå¹¶ã€‚åˆå¹¶äºº: "
+msgid "weekly"
+msgstr "æ¯å‘¨"
+
msgid "wiki page"
msgstr "wiki页é¢"
diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po
index c79e5a80e81..68349c33650 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-08-12 18:09\n"
+"PO-Revision-Date: 2022-09-11 06:25\n"
msgid " %{start} to %{end}"
msgstr ""
@@ -22,6 +22,10 @@ msgstr ""
msgid " (from %{timeoutSource})"
msgstr ""
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] ""
+
msgid " Collected %{time}"
msgstr ""
@@ -129,6 +133,10 @@ msgid "%d approver (you've approved)"
msgid_plural "%d approvers (you've approved)"
msgstr[0] ""
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] ""
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] ""
@@ -185,6 +193,10 @@ msgid "%d contribution"
msgid_plural "%d contributions"
msgstr[0] ""
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] ""
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] ""
@@ -305,6 +317,10 @@ msgid "%d project selected"
msgid_plural "%d projects selected"
msgstr[0] ""
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] ""
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] ""
@@ -333,10 +349,6 @@ msgid "%d tag per image name"
msgid_plural "%d tags per image name"
msgstr[0] ""
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] ""
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] ""
@@ -385,9 +397,6 @@ msgstr ""
msgid "%{address} is an invalid IP address range"
msgstr ""
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr ""
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr ""
@@ -806,12 +815,6 @@ msgstr ""
msgid "%{over_limit_message} To get more seats, %{link_start}upgrade to a paid tier%{link_end}."
msgstr ""
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr ""
-
msgid "%{percentageUsed}%% used"
msgstr ""
@@ -913,6 +916,9 @@ msgstr ""
msgid "%{sourceBranch} into %{targetBranch}"
msgstr ""
+msgid "%{source} %{copyButton} into %{target}"
+msgstr ""
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr ""
@@ -932,6 +938,10 @@ msgstr[0] ""
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr ""
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] ""
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
@@ -958,9 +968,6 @@ msgstr[0] ""
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr ""
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr ""
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] ""
@@ -1165,10 +1172,6 @@ msgstr ""
msgid "(revoked)"
msgstr ""
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] ""
-
msgid "(this user)"
msgstr ""
@@ -1475,6 +1478,12 @@ msgstr ""
msgid "A complete DevOps platform"
msgstr ""
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr ""
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr ""
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
@@ -1538,6 +1547,12 @@ msgstr ""
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr ""
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr ""
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr ""
+
msgid "A page with that title already exists"
msgstr ""
@@ -1706,6 +1721,9 @@ msgstr ""
msgid "AWS Access Key"
msgstr ""
+msgid "AWS OpenSearch IAM credentials"
+msgstr ""
+
msgid "AWS Secret Access Key"
msgstr ""
@@ -2021,7 +2039,7 @@ msgstr ""
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr ""
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
msgstr ""
msgid "Add a general comment to this %{noteableDisplayName}."
@@ -2048,6 +2066,9 @@ msgstr ""
msgid "Add a table"
msgstr "新增表格"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr ""
@@ -2087,6 +2108,9 @@ msgstr "ç«‹å³ç•™è¨€"
msgid "Add comment to design"
msgstr ""
+msgid "Add comment to incident timeline"
+msgstr ""
+
msgid "Add comment..."
msgstr ""
@@ -2138,6 +2162,12 @@ msgstr ""
msgid "Add label(s)"
msgstr "新增標籤"
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
+
msgid "Add list"
msgstr ""
@@ -2309,6 +2339,9 @@ msgstr ""
msgid "Adds a Zoom meeting."
msgstr ""
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr ""
@@ -2324,9 +2357,6 @@ msgstr ""
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr ""
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Admin"
msgstr ""
@@ -2525,15 +2555,15 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "AdminSettings|CI/CD limits"
msgstr ""
msgid "AdminSettings|Configure Let's Encrypt"
msgstr ""
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr ""
@@ -2573,7 +2603,7 @@ msgstr ""
msgid "AdminSettings|Enable Service Ping"
msgstr ""
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
@@ -2597,6 +2627,9 @@ msgstr ""
msgid "AdminSettings|Enabled"
msgstr ""
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr ""
+
msgid "AdminSettings|Feed token"
msgstr ""
@@ -2762,7 +2795,10 @@ msgstr ""
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr ""
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr ""
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
msgstr ""
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
@@ -2987,6 +3023,9 @@ msgstr ""
msgid "AdminUsers|It's you!"
msgstr ""
+msgid "AdminUsers|LDAP Blocked"
+msgstr ""
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr ""
@@ -3761,9 +3800,6 @@ msgstr ""
msgid "Allowed"
msgstr ""
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr ""
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr ""
@@ -3848,6 +3884,9 @@ msgstr ""
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr ""
+msgid "An email will be sent with the report attached after it is generated."
+msgstr ""
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr ""
@@ -3899,6 +3938,9 @@ msgstr ""
msgid "An error occurred while adding formatted title for epic"
msgstr ""
+msgid "An error occurred while approving, please try again."
+msgstr ""
+
msgid "An error occurred while authorizing your role"
msgstr ""
@@ -4037,6 +4079,9 @@ msgstr ""
msgid "An error occurred while loading all the files."
msgstr ""
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr ""
+
msgid "An error occurred while loading chart data"
msgstr ""
@@ -4173,9 +4218,6 @@ msgstr[0] ""
msgid "An error occurred while saving your settings. Try saving them again."
msgstr ""
-msgid "An error occurred while subscribing to notifications."
-msgstr "訂閱通知時出錯"
-
msgid "An error occurred while triggering the job."
msgstr ""
@@ -4185,15 +4227,15 @@ msgstr ""
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr ""
+msgid "An error occurred while trying to render the content editor. Please try again."
+msgstr ""
+
msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr ""
msgid "An error occurred while trying to unfollow this user, please try again."
msgstr ""
-msgid "An error occurred while unsubscribing to notifications."
-msgstr "å–消訂閱通知時出錯"
-
msgid "An error occurred while updating approvers"
msgstr ""
@@ -4729,6 +4771,9 @@ msgstr ""
msgid "Approve a merge request"
msgstr ""
+msgid "Approve merge request"
+msgstr ""
+
msgid "Approve the current merge request."
msgstr ""
@@ -5191,6 +5236,10 @@ msgstr ""
msgid "AuditLogs|User Events"
msgstr ""
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] ""
+
msgid "AuditStreams|A header with this name already exists."
msgstr ""
@@ -5206,10 +5255,16 @@ msgstr ""
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr ""
+msgid "AuditStreams|Add another custom header"
+msgstr ""
+
msgid "AuditStreams|Add external stream destination"
msgstr ""
-msgid "AuditStreams|Add stream"
+msgid "AuditStreams|Add header"
+msgstr ""
+
+msgid "AuditStreams|Add streaming destination"
msgstr ""
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
@@ -5227,7 +5282,7 @@ msgstr ""
msgid "AuditStreams|Cancel editing"
msgstr ""
-msgid "AuditStreams|Custom HTTP headers"
+msgid "AuditStreams|Custom HTTP headers (optional)"
msgstr ""
msgid "AuditStreams|Delete %{link}"
@@ -5248,6 +5303,9 @@ msgstr ""
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr ""
+msgid "AuditStreams|Remove custom header"
+msgstr ""
+
msgid "AuditStreams|Save external stream destination"
msgstr ""
@@ -5257,9 +5315,6 @@ msgstr ""
msgid "AuditStreams|Stream added successfully"
msgstr ""
-msgid "AuditStreams|Stream count icon"
-msgstr ""
-
msgid "AuditStreams|Stream deleted successfully"
msgstr ""
@@ -6058,12 +6113,6 @@ msgstr ""
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr ""
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr ""
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr ""
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr ""
@@ -6079,12 +6128,6 @@ msgstr ""
msgid "Billings|Validate user account"
msgstr ""
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr ""
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr ""
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr ""
@@ -6136,9 +6179,6 @@ msgstr ""
msgid "Billing|Export list"
msgstr ""
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr ""
-
msgid "Billing|Group invite"
msgstr ""
@@ -6179,9 +6219,6 @@ msgstr ""
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr ""
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr ""
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr ""
@@ -6216,6 +6253,9 @@ msgstr ""
msgid "Blocking"
msgstr ""
+msgid "Blocking epics"
+msgstr ""
+
msgid "Blocking issues"
msgstr ""
@@ -6422,6 +6462,15 @@ msgstr ""
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr ""
+msgid "Boards|Move card"
+msgstr ""
+
+msgid "Boards|Move to end of list"
+msgstr ""
+
+msgid "Boards|Move to start of list"
+msgstr ""
+
msgid "Boards|New board"
msgstr ""
@@ -6548,6 +6597,12 @@ msgstr ""
msgid "BranchRules|Require approval from code owners."
msgstr ""
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "分支"
@@ -6713,6 +6768,9 @@ msgstr "ç€è¦½æ–‡ä»¶"
msgid "Browse templates"
msgstr ""
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr ""
@@ -6966,21 +7024,12 @@ msgstr ""
msgid "CICDAnalytics|Releases"
msgstr ""
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr ""
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr ""
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr ""
-msgid "CICDAnalytics|Shared runner usage"
-msgstr ""
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr ""
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr ""
@@ -6990,9 +7039,6 @@ msgstr ""
msgid "CICDAnalytics|What is shared runner duration?"
msgstr ""
-msgid "CICDAnalytics|What is shared runner usage?"
-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 ""
@@ -8241,6 +8287,12 @@ msgstr ""
msgid "Cloud Run"
msgstr ""
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "Cloud Storage"
msgstr ""
@@ -8268,12 +8320,24 @@ msgstr ""
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr ""
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr ""
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr ""
+
msgid "CloudSeed|CloudSQL Instance"
msgstr ""
msgid "CloudSeed|Configuration"
msgstr ""
+msgid "CloudSeed|Create MySQL Instance"
+msgstr ""
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr ""
+
msgid "CloudSeed|Create cluster"
msgstr ""
@@ -8328,6 +8392,9 @@ msgstr ""
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr ""
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr ""
+
msgid "CloudSeed|Google Cloud Project"
msgstr ""
@@ -9013,10 +9080,10 @@ msgstr ""
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
@@ -9187,6 +9254,9 @@ msgstr ""
msgid "Collapse jobs"
msgstr ""
+msgid "Collapse merge details"
+msgstr ""
+
msgid "Collapse milestones"
msgstr ""
@@ -9253,6 +9323,9 @@ msgstr ""
msgid "Comment '%{label}' position"
msgstr ""
+msgid "Comment added to the timeline."
+msgstr ""
+
msgid "Comment form position"
msgstr "留言框ä½ç½®"
@@ -9374,6 +9447,9 @@ msgstr ""
msgid "Compare Revisions"
msgstr ""
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr ""
@@ -9949,6 +10025,9 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error"
msgstr ""
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr ""
@@ -10052,6 +10131,12 @@ msgstr ""
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr ""
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr ""
@@ -10226,7 +10311,7 @@ msgstr ""
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr ""
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
msgstr ""
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
@@ -10829,9 +10914,6 @@ msgstr ""
msgid "Create, update, or delete a merge request."
msgstr ""
-msgid "Create/import your first project"
-msgstr ""
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr ""
@@ -11375,6 +11457,9 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr ""
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr ""
@@ -11604,6 +11689,12 @@ 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 ""
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr ""
@@ -11682,15 +11773,6 @@ msgstr ""
msgid "DastProfiles|Delete profile"
msgstr ""
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr ""
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr ""
-
msgid "DastProfiles|Edit profile"
msgstr ""
@@ -11703,6 +11785,9 @@ msgstr ""
msgid "DastProfiles|Enable Authentication"
msgstr ""
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr ""
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr ""
@@ -11778,6 +11863,9 @@ msgstr ""
msgid "DastProfiles|Password form field"
msgstr ""
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr ""
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr ""
@@ -11805,16 +11893,13 @@ msgstr ""
msgid "DastProfiles|Scan mode"
msgstr ""
-msgid "DastProfiles|Scanner Profile"
-msgstr ""
-
-msgid "DastProfiles|Scanner Profiles"
+msgid "DastProfiles|Scanner name"
msgstr ""
-msgid "DastProfiles|Scanner name"
+msgid "DastProfiles|Scanner profile"
msgstr ""
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Scanner profiles"
msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
@@ -11835,16 +11920,13 @@ msgstr ""
msgid "DastProfiles|Show debug messages"
msgstr ""
-msgid "DastProfiles|Site Profile"
-msgstr ""
-
-msgid "DastProfiles|Site Profiles"
+msgid "DastProfiles|Site name"
msgstr ""
-msgid "DastProfiles|Site name"
+msgid "DastProfiles|Site profile"
msgstr ""
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgid "DastProfiles|Site profiles"
msgstr ""
msgid "DastProfiles|Site type"
@@ -11913,13 +11995,13 @@ msgstr ""
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr ""
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
msgstr ""
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
msgstr ""
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard"
@@ -12106,7 +12188,7 @@ msgstr ""
msgid "Days to merge"
msgstr ""
-msgid "Deactivate dormant users after 90 days of inactivity"
+msgid "Deactivate dormant users after a period of inactivity"
msgstr ""
msgid "Dear Administrator,"
@@ -12292,6 +12374,9 @@ msgstr ""
msgid "Delete deploy key"
msgstr ""
+msgid "Delete epic"
+msgstr ""
+
msgid "Delete file"
msgstr ""
@@ -12343,6 +12428,9 @@ msgstr ""
msgid "Delete source branch"
msgstr ""
+msgid "Delete source branch when merge request is accepted."
+msgstr ""
+
msgid "Delete subscription"
msgstr ""
@@ -12895,6 +12983,12 @@ msgstr ""
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr ""
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr ""
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr ""
+
msgid "DeploymentTarget|GitLab Pages"
msgstr ""
@@ -13122,6 +13216,9 @@ msgstr ""
msgid "DesignManagement|Discard comment"
msgstr ""
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr ""
@@ -13215,6 +13312,9 @@ msgstr ""
msgid "DevOps adoption"
msgstr ""
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr ""
@@ -13771,6 +13871,9 @@ msgstr ""
msgid "Draft: %{filename}"
msgstr ""
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr ""
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr ""
@@ -13792,6 +13895,12 @@ msgstr ""
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr ""
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr ""
@@ -13807,6 +13916,12 @@ msgstr ""
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr ""
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr ""
@@ -14029,6 +14144,9 @@ msgstr ""
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr ""
+msgid "Edit your search filter and try again."
+msgstr ""
+
msgid "Edit, lint, and visualize your pipeline."
msgstr ""
@@ -14044,9 +14162,6 @@ msgstr ""
msgid "Elapsed time"
msgstr ""
-msgid "Elasticsearch AWS IAM credentials"
-msgstr ""
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr ""
@@ -14071,9 +14186,6 @@ msgstr ""
msgid "Elasticsearch zero-downtime reindexing"
msgstr ""
-msgid "Elasticsearch's region."
-msgstr ""
-
msgid "Elastic|None. Select namespaces to index."
msgstr ""
@@ -14221,6 +14333,9 @@ msgstr ""
msgid "Enable GitLab Error Tracking"
msgstr ""
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr ""
@@ -14305,9 +14420,6 @@ msgstr ""
msgid "Enable header and footer in emails"
msgstr ""
-msgid "Enable health and performance metrics endpoint"
-msgstr ""
-
msgid "Enable in-product marketing emails"
msgstr ""
@@ -14749,6 +14861,9 @@ msgstr ""
msgid "Epic Boards"
msgstr ""
+msgid "Epic actions"
+msgstr ""
+
msgid "Epic cannot be found."
msgstr ""
@@ -14785,12 +14900,6 @@ msgstr ""
msgid "Epics|Add an existing epic"
msgstr ""
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
-
-msgid "Epics|An error occurred while updating labels."
-msgstr ""
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr ""
@@ -14857,18 +14966,9 @@ msgstr ""
msgid "Epics|This will also remove any descendents of %{bStart}%{targetEpicTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}. Are you sure?"
msgstr ""
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr "è‹¥è¦æŽ’程基於里程碑的 Epic %{epicDateType} 日期,請å°ä»»ä½• Epic 中的議題指定 %{epicDateType} 日期的里程碑。"
-
msgid "Epics|Unable to save epic. Please try again"
msgstr ""
-msgid "Epics|due"
-msgstr ""
-
-msgid "Epics|start"
-msgstr ""
-
msgid "Erased"
msgstr ""
@@ -14905,6 +15005,9 @@ msgstr ""
msgid "Error deleting project. Check logs for error details."
msgstr ""
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr ""
@@ -14938,9 +15041,6 @@ msgstr ""
msgid "Error loading burndown chart data"
msgstr ""
-msgid "Error loading countries data."
-msgstr ""
-
msgid "Error loading file viewer."
msgstr ""
@@ -15016,6 +15116,9 @@ msgstr ""
msgid "Error parsing CSV file. Please make sure it has"
msgstr ""
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr ""
+
msgid "Error rendering Markdown preview"
msgstr ""
@@ -15446,6 +15549,9 @@ msgstr ""
msgid "Expand jobs"
msgstr ""
+msgid "Expand merge details"
+msgstr ""
+
msgid "Expand milestones"
msgstr ""
@@ -16193,9 +16299,6 @@ msgstr "二月"
msgid "February"
msgstr "二月"
-msgid "Feedback issue"
-msgstr ""
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr ""
@@ -16406,13 +16509,13 @@ msgstr ""
msgid "Flags"
msgstr ""
-msgid "FloC|Configure whether you want to participate in FloC."
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
+msgid "FloC|Participate in FLoC"
msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
@@ -16562,6 +16665,9 @@ msgstr ""
msgid "ForkProject|Select a namespace"
msgstr ""
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr ""
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr ""
@@ -16629,6 +16735,9 @@ msgstr ""
msgid "Frequently searched"
msgstr ""
+msgid "Fri"
+msgstr ""
+
msgid "Friday"
msgstr ""
@@ -16641,12 +16750,8 @@ msgstr ""
msgid "From %{providerTitle}"
msgstr ""
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] ""
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
+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"
@@ -16786,9 +16891,6 @@ msgstr ""
msgid "Geo|Add site"
msgstr ""
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr ""
-
msgid "Geo|All"
msgstr ""
@@ -16984,6 +17086,12 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr ""
+
msgid "Geo|No Geo site found"
msgstr ""
@@ -17191,6 +17299,9 @@ msgstr ""
msgid "Geo|There are no %{replicable_type} to show"
msgstr ""
+msgid "Geo|There are no %{replicable} to show"
+msgstr ""
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr ""
@@ -17353,6 +17464,9 @@ msgstr ""
msgid "Git version"
msgstr ""
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr ""
+
msgid "GitAbuse|Excluded users"
msgstr ""
@@ -17722,6 +17836,9 @@ msgstr ""
msgid "Global Shortcuts"
msgstr ""
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr ""
@@ -17734,6 +17851,12 @@ msgstr ""
msgid "GlobalSearch|Groups"
msgstr ""
+msgid "GlobalSearch|Help"
+msgstr ""
+
+msgid "GlobalSearch|In this project"
+msgstr ""
+
msgid "GlobalSearch|Issues I've created"
msgstr ""
@@ -17752,6 +17875,15 @@ msgstr ""
msgid "GlobalSearch|Projects"
msgstr ""
+msgid "GlobalSearch|Recent epics"
+msgstr ""
+
+msgid "GlobalSearch|Recent issues"
+msgstr ""
+
+msgid "GlobalSearch|Recent merge requests"
+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 ""
@@ -17764,6 +17896,9 @@ msgstr ""
msgid "GlobalSearch|Search results are loading"
msgstr ""
+msgid "GlobalSearch|Settings"
+msgstr ""
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr ""
@@ -17791,6 +17926,12 @@ msgstr ""
msgid "GlobalSearch|project"
msgstr ""
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr ""
@@ -18061,9 +18202,6 @@ msgstr ""
msgid "Group"
msgstr ""
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr ""
-
msgid "Group %{group_name} couldn't be exported."
msgstr ""
@@ -18499,9 +18637,6 @@ msgstr ""
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr ""
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr ""
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr ""
@@ -18529,6 +18664,9 @@ msgstr ""
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr ""
+
msgid "GroupSettings|Custom project templates"
msgstr ""
@@ -18892,13 +19030,16 @@ msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
+msgid "HAR file URL"
+msgstr ""
+
msgid "HAR file path or URL"
msgstr ""
msgid "HTTP Archive (HAR)"
msgstr ""
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
msgstr ""
msgid "Harbor Registry"
@@ -18937,15 +19078,15 @@ msgstr ""
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr ""
-msgid "HarborRegistry|%{count} Image repository"
-msgid_plural "HarborRegistry|%{count} Image repositories"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
msgstr[0] ""
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
+msgid "HarborRegistry|%{count} Image repository"
+msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] ""
-msgid "HarborRegistry|Configuration digest: %{digest}"
+msgid "HarborRegistry|-- artifacts"
msgstr ""
msgid "HarborRegistry|Digest: %{imageId}"
@@ -18957,43 +19098,37 @@ msgstr ""
msgid "HarborRegistry|Harbor connection error"
msgstr ""
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr ""
-
-msgid "HarborRegistry|Last updated %{time}"
+msgid "HarborRegistry|Please try different search criteria"
msgstr ""
-msgid "HarborRegistry|Manifest digest: %{digest}"
+msgid "HarborRegistry|Published %{timeInfo}"
msgstr ""
-msgid "HarborRegistry|Please try different search criteria"
+msgid "HarborRegistry|Root image"
msgstr ""
-msgid "HarborRegistry|Published %{timeInfo}"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
msgstr ""
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
msgstr ""
-msgid "HarborRegistry|Root image"
+msgid "HarborRegistry|Something went wrong while fetching the tags."
msgstr ""
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr ""
-msgid "HarborRegistry|The filter returned no results"
-msgstr ""
-
-msgid "HarborRegistry|The image repository could not be found."
+msgid "HarborRegistry|Tag"
msgstr ""
-msgid "HarborRegistry|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."
+msgid "HarborRegistry|The filter returned no results"
msgstr ""
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
+msgid "HarborRegistry|There are no harbor images stored for this project"
msgstr ""
-msgid "HarborRegistry|This image has no active tags"
+msgid "HarborRegistry|This image has no artifacts"
msgstr ""
msgid "HarborRegistry|To widen your search, change or remove the filters above."
@@ -19002,6 +19137,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr ""
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr ""
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr ""
@@ -19147,6 +19285,9 @@ msgid "Hide chart"
msgid_plural "Hide charts"
msgstr[0] ""
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr ""
@@ -19277,9 +19418,6 @@ msgstr "已開始維護"
msgid "How do I configure Akismet?"
msgstr ""
-msgid "How do I configure runners?"
-msgstr ""
-
msgid "How do I configure this integration?"
msgstr ""
@@ -19313,6 +19451,9 @@ msgstr ""
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr ""
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr ""
@@ -19436,9 +19577,15 @@ msgstr ""
msgid "IdentityVerification|Create a project"
msgstr ""
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr ""
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr ""
@@ -19451,15 +19598,36 @@ msgstr ""
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr ""
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr ""
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr ""
msgid "IdentityVerification|Resend code"
msgstr ""
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr ""
@@ -19481,6 +19649,9 @@ msgstr ""
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr ""
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr ""
@@ -19550,9 +19721,6 @@ msgstr ""
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr ""
-msgid "If you are added to a project, it will be displayed here."
-msgstr ""
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr ""
@@ -19808,9 +19976,6 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
-msgid "In a seat"
-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 ""
@@ -20600,6 +20765,9 @@ msgstr ""
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr ""
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr ""
+
msgid "Incident|Metrics"
msgstr ""
@@ -20618,6 +20786,9 @@ msgstr ""
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr ""
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr ""
+
msgid "Incident|Summary"
msgstr ""
@@ -20651,7 +20822,7 @@ msgstr ""
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr ""
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
msgstr ""
msgid "Includes an MVC structure to help you get started"
@@ -20792,9 +20963,15 @@ msgstr ""
msgid "Insert suggestion"
msgstr ""
+msgid "Insert table"
+msgstr ""
+
msgid "Insights"
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)."
msgstr ""
@@ -21063,6 +21240,9 @@ msgstr ""
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr ""
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr ""
@@ -21375,6 +21555,9 @@ msgstr ""
msgid "InviteMembersModal|Members were successfully added"
msgstr ""
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr ""
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr ""
@@ -21390,6 +21573,12 @@ msgstr ""
msgid "InviteMembersModal|Select members or type email addresses"
msgstr ""
+msgid "InviteMembersModal|Show less"
+msgstr ""
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr ""
+
msgid "InviteMembersModal|Something went wrong"
msgstr ""
@@ -21406,9 +21595,6 @@ msgstr ""
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr ""
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr ""
-
msgid "InviteMembersModal|Username or email address"
msgstr ""
@@ -21433,9 +21619,6 @@ msgstr ""
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr ""
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr ""
-
msgid "InviteMembers|Invite a group"
msgstr ""
@@ -21553,12 +21736,18 @@ msgstr ""
msgid "Is using seat"
msgstr ""
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr ""
+
msgid "IssuableStatus|Closed"
msgstr ""
msgid "IssuableStatus|Closed (%{link})"
msgstr ""
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr ""
+
msgid "IssuableStatus|duplicated"
msgstr ""
@@ -21877,9 +22066,6 @@ msgstr ""
msgid "Iterations|Cadence name"
msgstr ""
-msgid "Iterations|Can be converted"
-msgstr ""
-
msgid "Iterations|Cancel"
msgstr ""
@@ -21925,6 +22111,9 @@ msgstr ""
msgid "Iterations|Edit iteration cadence"
msgstr ""
+msgid "Iterations|Enable automatic scheduling"
+msgstr ""
+
msgid "Iterations|Enable roll over"
msgstr ""
@@ -21940,12 +22129,6 @@ msgstr ""
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr ""
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr ""
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr ""
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr ""
@@ -22003,9 +22186,6 @@ msgstr ""
msgid "Iterations|The iteration has been deleted."
msgstr ""
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
@@ -22015,9 +22195,6 @@ msgstr ""
msgid "Iterations|Title"
msgstr ""
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr ""
-
msgid "Iterations|Unable to find iteration cadence."
msgstr ""
@@ -22030,9 +22207,6 @@ msgstr ""
msgid "Iterations|Upcoming iterations"
msgstr ""
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr ""
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr ""
@@ -22327,9 +22501,6 @@ msgstr ""
msgid "Job has wrong arguments format."
msgstr ""
-msgid "Job is missing the `model_type` argument."
-msgstr ""
-
msgid "Job is stuck. Check runners."
msgstr ""
@@ -22763,8 +22934,8 @@ 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."
-msgstr "標籤å¯ä»¥ç”¨æ–¼è­°é¡Œå’Œåˆä½µè«‹æ±‚。"
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
+msgstr ""
msgid "Labels with no issues in this iteration:"
msgstr ""
@@ -22778,6 +22949,9 @@ msgstr "æå‡æ¨™ç±¤"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr ""
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr ""
@@ -22947,9 +23121,6 @@ msgstr ""
msgid "Learn GitLab"
msgstr ""
-msgid "Learn GitLab - Ultimate trial"
-msgstr ""
-
msgid "Learn More"
msgstr ""
@@ -23001,6 +23172,9 @@ msgstr ""
msgid "Learn more about groups."
msgstr ""
+msgid "Learn more about issues."
+msgstr ""
+
msgid "Learn more about max seats used"
msgstr ""
@@ -23727,6 +23901,9 @@ msgstr ""
msgid "Mailgun events"
msgstr ""
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr ""
@@ -24015,10 +24192,7 @@ msgstr ""
msgid "Maximum allowable lifetime for access token (days)"
msgstr ""
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr ""
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
+msgid "Maximum allowed lifetime for SSH keys (days)"
msgstr ""
msgid "Maximum artifacts size"
@@ -24500,6 +24674,9 @@ msgstr ""
msgid "Merge requests"
msgstr "åˆä½µè«‹æ±‚"
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -24587,6 +24764,9 @@ msgstr ""
msgid "MergeRequests|Create issue to resolve thread"
msgstr ""
+msgid "MergeRequests|Reference copied"
+msgstr ""
+
msgid "MergeRequests|Saving the comment failed"
msgstr ""
@@ -24662,6 +24842,30 @@ msgstr ""
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr ""
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr ""
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr ""
+
+msgid "MergeTopics|Merge topics"
+msgstr ""
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr ""
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr ""
+
+msgid "MergeTopics|Source topic"
+msgstr ""
+
+msgid "MergeTopics|Target topic"
+msgstr ""
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr ""
+
msgid "Merged"
msgstr "å·²åˆä½µ"
@@ -25336,16 +25540,19 @@ msgstr ""
msgid "Modify merge commit"
msgstr "修改åˆä½µæ交"
+msgid "Mon"
+msgstr ""
+
msgid "Monday"
msgstr "星期一"
msgid "Monitor"
msgstr ""
-msgid "Monitor Settings"
+msgid "Monitor GitLab with Prometheus."
msgstr ""
-msgid "Monitor the health and performance of GitLab with Prometheus."
+msgid "Monitor Settings"
msgstr ""
msgid "Monitor your errors by integrating with Sentry."
@@ -25360,6 +25567,9 @@ msgstr ""
msgid "Months"
msgstr "月"
+msgid "More"
+msgstr ""
+
msgid "More Details"
msgstr ""
@@ -25402,9 +25612,6 @@ msgstr ""
msgid "Most stars"
msgstr ""
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr ""
-
msgid "Move"
msgstr ""
@@ -25495,12 +25702,6 @@ msgstr ""
msgid "Multiple Prometheus integrations are not supported"
msgstr ""
-msgid "Multiple model types found: %{model_types}"
-msgstr ""
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr ""
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr ""
@@ -26022,6 +26223,9 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr ""
@@ -26034,9 +26238,6 @@ msgstr ""
msgid "No deployments found"
msgstr ""
-msgid "No due date"
-msgstr "沒有截止日期"
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr ""
@@ -26187,6 +26388,9 @@ msgstr "沒有存儲庫"
msgid "No results"
msgstr ""
+msgid "No results found"
+msgstr ""
+
msgid "No runner executable"
msgstr ""
@@ -26208,9 +26412,6 @@ msgstr ""
msgid "No starrers matched your search"
msgstr ""
-msgid "No start date"
-msgstr ""
-
msgid "No suggestions found"
msgstr ""
@@ -26398,6 +26599,9 @@ msgstr ""
msgid "Nothing to preview."
msgstr ""
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr "通知事件"
@@ -26528,6 +26732,9 @@ msgstr ""
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr ""
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr ""
@@ -26537,6 +26744,24 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr ""
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr ""
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr ""
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr ""
@@ -26552,6 +26777,18 @@ msgstr ""
msgid "Notify|CI/CD project settings"
msgstr ""
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr ""
+
+msgid "Notify|Hi %{user}!"
+msgstr ""
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr ""
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr ""
@@ -26582,9 +26819,18 @@ msgstr ""
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr ""
+msgid "Notify|Merge request was approved"
+msgstr ""
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr ""
@@ -26603,6 +26849,12 @@ msgstr ""
msgid "Notify|You don't have access to the project."
msgstr ""
+msgid "Notify|You have been mentioned in an issue."
+msgstr ""
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr ""
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr ""
@@ -26706,6 +26958,9 @@ msgid "On %{end_date}, your trial will end and %{namespace_name} will be limited
msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be limited to %{free_user_limit} members"
msgstr[0] ""
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr ""
@@ -26925,6 +27180,12 @@ msgstr ""
msgid "OnDemandScans|Description (optional)"
msgstr ""
+msgid "OnDemandScans|Discard changes"
+msgstr ""
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr ""
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr ""
@@ -26946,6 +27207,9 @@ msgstr ""
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr ""
+msgid "OnDemandScans|Keep editing"
+msgstr ""
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr ""
@@ -27069,6 +27333,9 @@ msgstr ""
msgid "OnDemandScans|View results"
msgstr ""
+msgid "OnDemandScans|You have unsaved changes"
+msgstr ""
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr ""
@@ -27223,9 +27490,15 @@ msgstr ""
msgid "OpenAPI"
msgstr ""
+msgid "OpenAPI Specification file URL"
+msgstr ""
+
msgid "OpenAPI Specification file path or URL"
msgstr ""
+msgid "OpenSearch's region."
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -27439,6 +27712,9 @@ msgstr ""
msgid "Package already exists"
msgstr ""
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr ""
@@ -27475,6 +27751,9 @@ msgstr ""
msgid "Package type must be PyPi"
msgstr ""
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr ""
@@ -27499,6 +27778,9 @@ msgstr ""
msgid "PackageRegistry|Additional metadata"
msgstr ""
+msgid "PackageRegistry|Allow duplicates"
+msgstr ""
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr ""
@@ -27716,6 +27998,9 @@ msgstr ""
msgid "PackageRegistry|Package file deleted successfully"
msgstr ""
+msgid "PackageRegistry|Package formats"
+msgstr ""
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
@@ -27747,9 +28032,6 @@ msgstr ""
msgid "PackageRegistry|Registry setup"
msgstr ""
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr ""
-
msgid "PackageRegistry|Remove package"
msgstr ""
@@ -27759,12 +28041,6 @@ msgstr ""
msgid "PackageRegistry|RubyGems"
msgstr ""
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr ""
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr ""
-
msgid "PackageRegistry|Show Composer commands"
msgstr ""
@@ -27865,7 +28141,7 @@ msgstr ""
msgid "PackageRegistry|published by %{author}"
msgstr ""
-msgid "Packages & Registries"
+msgid "Packages and registries"
msgstr ""
msgid "Page not found"
@@ -28114,9 +28390,6 @@ msgstr ""
msgid "Perform common operations on GitLab project"
msgstr ""
-msgid "Performance insights"
-msgstr ""
-
msgid "Performance optimization"
msgstr ""
@@ -28192,6 +28465,12 @@ msgstr ""
msgid "Period in seconds"
msgstr ""
+msgid "Period of inactivity (days)"
+msgstr ""
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr ""
@@ -28897,25 +29176,16 @@ msgstr ""
msgid "Pipeline|Date"
msgstr ""
-msgid "Pipeline|Detached merge request pipeline"
-msgstr ""
-
msgid "Pipeline|Failed"
msgstr ""
-msgid "Pipeline|Five slowest jobs"
-msgstr ""
-
msgid "Pipeline|In progress"
msgstr ""
-msgid "Pipeline|Last executed job"
-msgstr ""
-
-msgid "Pipeline|Longest queued job"
+msgid "Pipeline|Manual"
msgstr ""
-msgid "Pipeline|Manual"
+msgid "Pipeline|Merge request pipeline"
msgstr ""
msgid "Pipeline|Merge train pipeline"
@@ -28927,18 +29197,12 @@ msgstr ""
msgid "Pipeline|Merged result pipeline"
msgstr ""
-msgid "Pipeline|Only able to show first 100 results"
-msgstr ""
-
msgid "Pipeline|Passed"
msgstr ""
msgid "Pipeline|Pending"
msgstr ""
-msgid "Pipeline|Performance insights"
-msgstr ""
-
msgid "Pipeline|Pipeline"
msgstr ""
@@ -28996,12 +29260,6 @@ msgstr ""
msgid "Pipeline|Test coverage"
msgstr ""
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr ""
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr ""
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr ""
@@ -29032,9 +29290,6 @@ msgstr ""
msgid "Pipeline|View commit"
msgstr ""
-msgid "Pipeline|View dependency"
-msgstr ""
-
msgid "Pipeline|View pipeline"
msgstr ""
@@ -29134,7 +29389,7 @@ msgstr ""
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr ""
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
+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 your profile with email address"
@@ -29350,6 +29605,9 @@ msgstr ""
msgid "Postman collection"
msgstr ""
+msgid "Postman collection file URL"
+msgstr ""
+
msgid "Postman collection file path or URL"
msgstr ""
@@ -29371,10 +29629,10 @@ msgstr ""
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr ""
-msgid "Preferences|Choose what content you want to see on a project’s overview page."
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
msgstr ""
-msgid "Preferences|Choose what content you want to see on your homepage."
+msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr ""
msgid "Preferences|Color for added lines"
@@ -29398,6 +29656,9 @@ msgstr ""
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr ""
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
msgstr ""
@@ -29419,9 +29680,6 @@ msgstr ""
msgid "Preferences|Gitpod"
msgstr ""
-msgid "Preferences|Homepage content"
-msgstr ""
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr ""
@@ -29833,9 +30091,6 @@ msgstr ""
msgid "Profiles|Key"
msgstr ""
-msgid "Profiles|Key becomes invalid on this date."
-msgstr ""
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr ""
@@ -29866,6 +30121,9 @@ msgstr ""
msgid "Profiles|Notification email"
msgstr ""
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr ""
+
msgid "Profiles|Organization"
msgstr ""
@@ -30553,6 +30811,9 @@ msgstr ""
msgid "ProjectSettings|Encourage"
msgstr ""
+msgid "ProjectSettings|Environments"
+msgstr ""
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr ""
@@ -30565,6 +30826,9 @@ 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 ""
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr ""
+
msgid "ProjectSettings|Everyone"
msgstr ""
@@ -30583,6 +30847,9 @@ msgstr ""
msgid "ProjectSettings|Fast-forward merges only."
msgstr ""
+msgid "ProjectSettings|Feature flags"
+msgstr ""
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr ""
@@ -30724,6 +30991,9 @@ msgstr ""
msgid "ProjectSettings|Requirements management system."
msgstr ""
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr ""
+
msgid "ProjectSettings|Search for topic"
msgstr ""
@@ -31534,7 +31804,7 @@ msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
@@ -31552,7 +31822,7 @@ msgstr ""
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr ""
-msgid "ProtectedEnvironment|No environments in this project are projected."
+msgid "ProtectedEnvironment|No environments in this project are protected."
msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
@@ -31735,9 +32005,6 @@ msgstr ""
msgid "Push project from command line"
msgstr ""
-msgid "Push rules"
-msgstr ""
-
msgid "Push the target branch up to GitLab."
msgstr ""
@@ -31861,9 +32128,6 @@ msgstr ""
msgid "Quick range"
msgstr ""
-msgid "Quickly and easily edit multiple files in your project."
-msgstr ""
-
msgid "Quota of CI/CD minutes"
msgstr ""
@@ -32093,6 +32357,9 @@ msgstr ""
msgid "Register with two-factor app"
msgstr ""
+msgid "Register with:"
+msgstr ""
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr ""
@@ -33446,6 +33713,9 @@ msgstr ""
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr ""
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr ""
@@ -33461,6 +33731,12 @@ msgstr ""
msgid "Runners|An error has occurred fetching instructions"
msgstr ""
+msgid "Runners|An upgrade is available for this runner"
+msgstr ""
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr ""
+
msgid "Runners|Architecture"
msgstr ""
@@ -33509,6 +33785,9 @@ msgstr ""
msgid "Runners|Copy registration token"
msgstr ""
+msgid "Runners|Created %{timeAgo}"
+msgstr ""
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] ""
@@ -33555,6 +33834,9 @@ msgstr ""
msgid "Runners|Executor"
msgstr ""
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr ""
@@ -33579,6 +33861,9 @@ msgstr ""
msgid "Runners|Last contact"
msgstr ""
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr ""
+
msgid "Runners|Locked to this project"
msgstr ""
@@ -33624,24 +33909,15 @@ msgstr ""
msgid "Runners|Offline"
msgstr ""
-msgid "Runners|Offline runners"
-msgstr ""
-
msgid "Runners|Offline:"
msgstr ""
msgid "Runners|Online"
msgstr ""
-msgid "Runners|Online runners"
-msgstr ""
-
msgid "Runners|Online:"
msgstr ""
-msgid "Runners|Outdated"
-msgstr ""
-
msgid "Runners|Pause from accepting jobs"
msgstr ""
@@ -33787,9 +34063,6 @@ msgstr ""
msgid "Runners|Stale"
msgstr ""
-msgid "Runners|Stale runners"
-msgstr ""
-
msgid "Runners|Stale:"
msgstr ""
@@ -33872,9 +34145,18 @@ msgstr ""
msgid "Runners|Version"
msgstr ""
+msgid "Runners|Version %{version}"
+msgstr ""
+
msgid "Runners|View installation instructions"
msgstr ""
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr ""
@@ -33893,42 +34175,18 @@ msgstr ""
msgid "Runners|active"
msgstr ""
-msgid "Runners|available"
-msgstr ""
-
msgid "Runners|group"
msgstr ""
-msgid "Runners|never contacted"
-msgstr ""
-
-msgid "Runners|offline"
-msgstr ""
-
-msgid "Runners|online"
-msgstr ""
-
msgid "Runners|paused"
msgstr ""
-msgid "Runners|recommended"
-msgstr ""
-
msgid "Runners|shared"
msgstr ""
msgid "Runners|specific"
msgstr ""
-msgid "Runners|stale"
-msgstr ""
-
-msgid "Runners|upgrade available"
-msgstr ""
-
-msgid "Runners|upgrade recommended"
-msgstr ""
-
msgid "Runner|Owner"
msgstr ""
@@ -34022,6 +34280,9 @@ msgstr ""
msgid "SSL verification"
msgstr ""
+msgid "Sat"
+msgstr ""
+
msgid "Satisfied"
msgstr ""
@@ -34067,12 +34328,27 @@ msgstr ""
msgid "Saving project."
msgstr ""
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr ""
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr ""
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr ""
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr ""
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr ""
@@ -34082,6 +34358,18 @@ msgstr ""
msgid "ScanExecutionPolicy|Select branches"
msgstr ""
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+msgstr ""
+
msgid "ScanResultPolicy|%{ifLabelStart}if%{ifLabelEnd} %{scanners} find(s) more than %{vulnerabilitiesAllowed} %{severities} %{vulnerabilityStates} vulnerabilities in an open merge request targeting %{branches}"
msgstr ""
@@ -34651,6 +34939,9 @@ msgstr ""
msgid "SecurityOrchestration|Actions"
msgstr ""
+msgid "SecurityOrchestration|Add action"
+msgstr ""
+
msgid "SecurityOrchestration|Add rule"
msgstr ""
@@ -34906,6 +35197,9 @@ msgstr ""
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr ""
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr ""
+
msgid "SecurityOrchestration|This group"
msgstr ""
@@ -36288,12 +36582,21 @@ msgstr ""
msgid "Sign-up restrictions"
msgstr ""
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr ""
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr ""
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr ""
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr ""
@@ -36567,6 +36870,9 @@ msgstr ""
msgid "Something went wrong when reordering designs. Please try again"
msgstr ""
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr ""
@@ -36657,6 +36963,9 @@ msgstr ""
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr ""
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr ""
+
msgid "Something went wrong while reopening a requirement."
msgstr ""
@@ -37041,6 +37350,9 @@ msgstr ""
msgid "Squash commits"
msgstr ""
+msgid "Squash commits when merge request is accepted."
+msgstr ""
+
msgid "Stack trace"
msgstr ""
@@ -37056,9 +37368,6 @@ msgstr ""
msgid "Standard"
msgstr ""
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr ""
-
msgid "Star labels to start sorting by priority"
msgstr ""
@@ -37797,6 +38106,9 @@ msgstr ""
msgid "Summary comment (optional)"
msgstr ""
+msgid "Sun"
+msgstr ""
+
msgid "Sunday"
msgstr "星期日"
@@ -39046,6 +39358,9 @@ msgstr ""
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr ""
+msgid "The parsed YAML is too big"
+msgstr ""
+
msgid "The password for the Jenkins server."
msgstr ""
@@ -39142,6 +39457,12 @@ msgstr ""
msgid "The source project of this merge request has been removed."
msgstr ""
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr ""
@@ -39154,6 +39475,9 @@ msgstr ""
msgid "The tag name can't be changed for an existing release."
msgstr ""
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr ""
@@ -39202,10 +39526,10 @@ msgstr ""
msgid "There are currently no events."
msgstr ""
-msgid "There are merge conflicts"
+msgid "There are currently no mirrored repositories."
msgstr ""
-msgid "There are no %{replicableTypeName} to show"
+msgid "There are merge conflicts"
msgstr ""
msgid "There are no GPG keys associated with this account."
@@ -39310,9 +39634,6 @@ msgstr ""
msgid "There are several size limits in place."
msgstr ""
-msgid "There are unsubmitted review comments."
-msgstr ""
-
msgid "There is already a repository with that name on disk"
msgstr ""
@@ -39664,6 +39985,9 @@ msgstr ""
msgid "This board's scope is reduced"
msgstr ""
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr ""
+
msgid "This chart could not be displayed"
msgstr ""
@@ -39688,7 +40012,7 @@ msgstr ""
msgid "This commit was signed with a different user's verified signature."
msgstr ""
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
msgstr ""
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
@@ -40069,6 +40393,9 @@ msgstr ""
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 ""
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr ""
@@ -40081,9 +40408,6 @@ msgstr ""
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr ""
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr ""
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr ""
@@ -40186,6 +40510,9 @@ msgstr ""
msgid "Throughput"
msgstr ""
+msgid "Thu"
+msgstr ""
+
msgid "Thursday"
msgstr ""
@@ -40396,6 +40723,9 @@ msgstr ""
msgid "Timeago|right now"
msgstr ""
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr ""
@@ -40423,6 +40753,24 @@ msgstr ""
msgid "Timezone"
msgstr ""
+msgid "Time|A"
+msgstr ""
+
+msgid "Time|AM"
+msgstr ""
+
+msgid "Time|P"
+msgstr ""
+
+msgid "Time|PM"
+msgstr ""
+
+msgid "Time|a"
+msgstr ""
+
+msgid "Time|am"
+msgstr ""
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "å°æ™‚"
@@ -40431,6 +40779,12 @@ msgid "Time|min"
msgid_plural "Time|mins"
msgstr[0] "分é˜"
+msgid "Time|p"
+msgstr ""
+
+msgid "Time|pm"
+msgstr ""
+
msgid "Time|s"
msgstr "秒"
@@ -40485,6 +40839,9 @@ msgstr ""
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr ""
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr ""
+
msgid "To complete registration, we need additional details from you."
msgstr ""
@@ -40647,9 +41004,6 @@ msgstr ""
msgid "To widen your search, change or remove filters above."
msgstr ""
-msgid "To-Do"
-msgstr ""
-
msgid "To-Do List"
msgstr ""
@@ -40674,6 +41028,18 @@ msgstr ""
msgid "Todos|Filter by project"
msgstr ""
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr ""
@@ -40683,6 +41049,9 @@ msgstr ""
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr ""
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr ""
@@ -40791,9 +41160,18 @@ msgstr ""
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr ""
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr ""
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr ""
@@ -40818,9 +41196,21 @@ msgstr ""
msgid "Topic was successfully updated."
msgstr ""
+msgid "TopicSelect|No matching results"
+msgstr ""
+
+msgid "TopicSelect|Search topics"
+msgstr ""
+
+msgid "TopicSelect|Select a topic"
+msgstr ""
+
msgid "Topics"
msgstr ""
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr ""
@@ -40989,39 +41379,12 @@ msgstr ""
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr ""
-msgid "Trial|Company name"
-msgstr ""
-
msgid "Trial|Continue"
msgstr ""
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr ""
-
-msgid "Trial|Country"
-msgstr ""
-
msgid "Trial|Dismiss"
msgstr ""
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr ""
-
-msgid "Trial|Number of employees"
-msgstr ""
-
-msgid "Trial|Please select a country"
-msgstr ""
-
-msgid "Trial|Telephone number"
-msgstr ""
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr ""
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr ""
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr ""
@@ -41127,21 +41490,18 @@ msgstr ""
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr ""
-msgid "Tuesday"
+msgid "Tue"
msgstr ""
-msgid "Turn off"
+msgid "Tuesday"
msgstr ""
-msgid "Turn off notifications"
+msgid "Turn off"
msgstr ""
msgid "Turn on"
msgstr ""
-msgid "Turn on notifications"
-msgstr ""
-
msgid "Twitter"
msgstr ""
@@ -41721,6 +42081,9 @@ msgstr ""
msgid "UsageQuota|CI/CD minutes usage since %{timeElapsed}"
msgstr ""
+msgid "UsageQuota|CI/CD minutes usage since %{usageSince}"
+msgstr ""
+
msgid "UsageQuota|Code packages and container images."
msgstr ""
@@ -41748,6 +42111,9 @@ msgstr ""
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr ""
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr ""
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr ""
@@ -41844,10 +42210,10 @@ msgstr ""
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr ""
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
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}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your 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 ""
msgid "UsageQuota|The table below shows current period usage"
@@ -41913,7 +42279,7 @@ msgstr ""
msgid "UsageQuota|Usage quotas help link"
msgstr ""
-msgid "UsageQuota|Usage since %{usageSince}"
+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."
@@ -42075,6 +42441,9 @@ msgstr ""
msgid "Use issue weight"
msgstr ""
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr ""
+
msgid "Use one line per URI"
msgstr ""
@@ -42475,7 +42844,7 @@ msgstr ""
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr ""
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
@@ -42769,10 +43138,13 @@ msgstr ""
msgid "View all environments."
msgstr ""
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr ""
-msgid "View all personal projects"
+msgid "View all projects"
msgstr ""
msgid "View blame"
@@ -43465,6 +43837,12 @@ msgstr ""
msgid "WebIDE|Merge request"
msgstr ""
+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|This project does not accept unsigned commits."
msgstr ""
@@ -43546,6 +43924,9 @@ msgstr ""
msgid "Webhooks|A subgroup is created or removed."
msgstr ""
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr ""
+
msgid "Webhooks|A wiki page is created or updated."
msgstr ""
@@ -43588,6 +43969,9 @@ msgstr ""
msgid "Webhooks|Feature flag events"
msgstr ""
+msgid "Webhooks|Go to webhooks"
+msgstr ""
+
msgid "Webhooks|Issues events"
msgstr ""
@@ -43645,6 +44029,9 @@ msgstr ""
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr ""
+msgid "Webhooks|Webhook disabled"
+msgstr ""
+
msgid "Webhooks|Webhook failed to connect"
msgstr ""
@@ -43663,6 +44050,9 @@ msgstr ""
msgid "Website:"
msgstr ""
+msgid "Wed"
+msgstr ""
+
msgid "Wednesday"
msgstr ""
@@ -43714,6 +44104,9 @@ msgstr ""
msgid "What does this command do?"
msgstr ""
+msgid "What is GitLab Runner?"
+msgstr ""
+
msgid "What is Markdown?"
msgstr ""
@@ -43923,9 +44316,6 @@ msgstr ""
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr ""
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr ""
-
msgid "WikiPage|Cancel"
msgstr ""
@@ -43950,9 +44340,6 @@ msgstr ""
msgid "WikiPage|Page title"
msgstr ""
-msgid "WikiPage|Retry"
-msgstr ""
-
msgid "WikiPage|Save changes"
msgstr ""
@@ -44040,10 +44427,10 @@ msgstr ""
msgid "Work in progress Limit"
msgstr ""
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
+msgid "WorkItem|%{workItemType} deleted"
msgstr ""
-msgid "WorkItem|Add a task"
+msgid "WorkItem|Add"
msgstr ""
msgid "WorkItem|Add a title"
@@ -44061,7 +44448,7 @@ msgstr ""
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr ""
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
msgstr ""
msgid "WorkItem|Assignee"
@@ -44071,16 +44458,13 @@ msgstr[0] ""
msgid "WorkItem|Cancel"
msgstr ""
-msgid "WorkItem|Child items"
-msgstr ""
-
msgid "WorkItem|Child removed"
msgstr ""
msgid "WorkItem|Closed"
msgstr ""
-msgid "WorkItem|Collapse child items"
+msgid "WorkItem|Collapse tasks"
msgstr ""
msgid "WorkItem|Create task"
@@ -44089,19 +44473,31 @@ msgstr ""
msgid "WorkItem|Create work item"
msgstr ""
-msgid "WorkItem|Delete task"
+msgid "WorkItem|Delete %{workItemType}"
msgstr ""
-msgid "WorkItem|Expand child items"
+msgid "WorkItem|Expand tasks"
+msgstr ""
+
+msgid "WorkItem|Incident"
msgstr ""
msgid "WorkItem|Introducing tasks"
msgstr ""
-msgid "WorkItem|Learn about tasks"
+msgid "WorkItem|Issue"
+msgstr ""
+
+msgid "WorkItem|Learn about tasks."
+msgstr ""
+
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
msgstr ""
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
+msgid "WorkItem|None"
+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 ""
msgid "WorkItem|Open"
@@ -44110,19 +44506,22 @@ msgstr ""
msgid "WorkItem|Remove"
msgstr ""
+msgid "WorkItem|Requirements"
+msgstr ""
+
msgid "WorkItem|Select type"
msgstr ""
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
msgstr ""
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr ""
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
msgstr ""
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
@@ -44137,31 +44536,40 @@ msgstr ""
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr ""
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr ""
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr ""
+msgid "WorkItem|Task"
+msgstr ""
+
msgid "WorkItem|Task deleted"
msgstr ""
-msgid "WorkItem|Turn off confidentiality"
+msgid "WorkItem|Tasks"
msgstr ""
-msgid "WorkItem|Turn on confidentiality"
+msgid "WorkItem|Test case"
+msgstr ""
+
+msgid "WorkItem|Turn off confidentiality"
msgstr ""
-msgid "WorkItem|Type"
+msgid "WorkItem|Turn on confidentiality"
msgstr ""
msgid "WorkItem|Undo"
msgstr ""
-msgid "WorkItem|Work Items"
+msgid "WorkItem|Use tasks to break down your work in an issue into smaller pieces. %{learnMoreLink}"
msgstr ""
-msgid "WorkItem|Work item deleted"
+msgid "WorkItem|Work Items"
msgstr ""
-msgid "WorkItem|work items"
+msgid "WorkItem|Work item"
msgstr ""
msgid "Would you like to create a new branch?"
@@ -44384,6 +44792,10 @@ 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 ""
@@ -44444,10 +44856,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] ""
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr ""
@@ -44553,10 +44961,6 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] ""
-
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -44720,7 +45124,7 @@ 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."
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
msgstr ""
msgid "You have unsaved changes"
@@ -44783,6 +45187,9 @@ msgstr ""
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr ""
+msgid "You see projects here when you're added to a group or project."
+msgstr ""
+
msgid "You successfully declined the invitation"
msgstr ""
@@ -44894,9 +45301,6 @@ msgstr ""
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 ""
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr ""
-
msgid "You've rejected %{user}"
msgstr ""
@@ -44936,6 +45340,12 @@ msgstr ""
msgid "Your CSV import for project"
msgstr ""
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr ""
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr ""
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr ""
@@ -45086,9 +45496,6 @@ msgstr ""
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr ""
-msgid "Your first project"
-msgstr ""
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] ""
@@ -45148,6 +45555,9 @@ msgstr ""
msgid "Your new comment"
msgstr ""
+msgid "Your password"
+msgstr ""
+
msgid "Your password reset token has expired."
msgstr ""
@@ -45163,7 +45573,7 @@ msgstr ""
msgid "Your profile"
msgstr ""
-msgid "Your project has limited quotas and features"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
@@ -45367,9 +45777,6 @@ msgstr ""
msgid "assign yourself"
msgstr ""
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr ""
-
msgid "at"
msgstr ""
@@ -45464,6 +45871,9 @@ msgstr ""
msgid "cannot be changed if shared runners are enabled"
msgstr ""
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr ""
@@ -45504,6 +45914,9 @@ msgid "change"
msgid_plural "changes"
msgstr[0] ""
+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 ""
@@ -45552,7 +45965,7 @@ msgstr ""
msgid "ciReport|%{sameNum} same"
msgstr ""
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
msgstr ""
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
@@ -45748,7 +46161,7 @@ msgstr ""
msgid "ciReport|Manage licenses"
msgstr ""
-msgid "ciReport|Manually Added"
+msgid "ciReport|Manually added"
msgstr ""
msgid "ciReport|New"
@@ -45872,9 +46285,6 @@ msgstr ""
msgid "compliance violation has already been recorded"
msgstr ""
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr ""
-
msgid "contacts can only be added to root groups"
msgstr ""
@@ -45911,6 +46321,9 @@ msgstr ""
msgid "created by"
msgstr ""
+msgid "daily"
+msgstr ""
+
msgid "data"
msgstr ""
@@ -46033,6 +46446,9 @@ msgstr ""
msgid "failed to dismiss finding: %{message}"
msgstr ""
+msgid "failed to dismiss security finding: %{message}"
+msgstr ""
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr ""
@@ -46238,6 +46654,9 @@ msgstr ""
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr ""
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr ""
+
msgid "is too long (maximum is %{count} characters)"
msgstr ""
@@ -46336,7 +46755,7 @@ msgid "merge request"
msgid_plural "merge requests"
msgstr[0] ""
-msgid "mergedCommitsAdded|(commits were squashed)"
+msgid "mergedCommitsAdded| (commits were squashed)"
msgstr ""
msgid "metric_id must be unique across a project"
@@ -46357,7 +46776,7 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
-msgid "mrWidgetCommitsAdded|1 merge commit"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
msgstr ""
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
@@ -46518,6 +46937,9 @@ msgid "mrWidget|Mentions issue"
msgid_plural "mrWidget|Mentions issues"
msgstr[0] ""
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr ""
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr ""
@@ -46653,9 +47075,6 @@ msgstr ""
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr ""
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr ""
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr ""
@@ -47211,6 +47630,9 @@ msgstr ""
msgid "was scheduled to merge after pipeline succeeds by"
msgstr ""
+msgid "weekly"
+msgstr ""
+
msgid "wiki page"
msgstr ""
diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po
index 8b5ac668122..0a2a37cba2a 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-08-12 19:01\n"
+"PO-Revision-Date: 2022-09-11 06:20\n"
msgid " %{start} to %{end}"
msgstr " %{start} 到 %{end}"
@@ -22,6 +22,10 @@ msgstr " %{start} 到 %{end}"
msgid " (from %{timeoutSource})"
msgstr " (來自 %{timeoutSource})"
+msgid " (squashes %{strongStart}%{count}%{strongEnd} commit)"
+msgid_plural " (squashes %{strongStart}%{count}%{strongEnd} commits)"
+msgstr[0] "(壓縮 %{strongStart}%{count}%{strongEnd} 次æ交)"
+
msgid " Collected %{time}"
msgstr " 已收集 %{time}"
@@ -129,6 +133,10 @@ msgid "%d approver (you've approved)"
msgid_plural "%d approvers (you've approved)"
msgstr[0] "%d ä½æ ¸å‡†è€… (你已批准)"
+msgid "%d artifact"
+msgid_plural "%d artifacts"
+msgstr[0] "%d 個產物"
+
msgid "%d assigned issue"
msgid_plural "%d assigned issues"
msgstr[0] "%d 個已指派的議題"
@@ -185,6 +193,10 @@ msgid "%d contribution"
msgid_plural "%d contributions"
msgstr[0] "%d 個貢ç»"
+msgid "%d contributor"
+msgid_plural "%d contributors"
+msgstr[0] "%d 個貢ç»è€…"
+
msgid "%d day"
msgid_plural "%d days"
msgstr[0] "%d 天"
@@ -305,6 +317,10 @@ msgid "%d project selected"
msgid_plural "%d projects selected"
msgstr[0] "é¸æ“‡äº†%d 個專案"
+msgid "%d push"
+msgid_plural "%d pushes"
+msgstr[0] "%d 次推é€"
+
msgid "%d remaining"
msgid_plural "%d remaining"
msgstr[0] "剩餘 %d"
@@ -333,10 +349,6 @@ msgid "%d tag per image name"
msgid_plural "%d tags per image name"
msgstr[0] "æ¯å€‹åœ–片å稱 %d 標籤"
-msgid "%d unassigned issue"
-msgid_plural "%d unassigned issues"
-msgstr[0] "%d 個尚未分é…çš„è­°é¡Œ"
-
msgid "%d unresolved thread"
msgid_plural "%d unresolved threads"
msgstr[0] "%d 個未解決話題"
@@ -385,9 +397,6 @@ msgstr "%{actionText} & é‡æ–°é–‹å•Ÿ %{noteable}"
msgid "%{address} is an invalid IP address range"
msgstr "%{address}是無效的 IP ä½å€ç¯„åœ"
-msgid "%{attribute} must be between %{min} and %{max}"
-msgstr "%{attribute} 必須介於 %{min} 和 %{max}之間"
-
msgid "%{author_link} cloned %{original_issue} to %{new_issue}."
msgstr "%{author_link}將 %{original_issue}複製到 %{new_issue}。"
@@ -806,12 +815,6 @@ 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}以ç²å¾—更多席次。"
-msgid "%{over_limit_message} To view and manage members, check the members page for each personal project. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr "%{over_limit_message} è¦æª¢è¦–和管ç†æˆå“¡ï¼Œè«‹æŸ¥çœ‹æ¯å€‹å€‹äººå°ˆæ¡ˆçš„æˆå“¡é é¢ã€‚我們建議您%{link_start}將您的專案移動到一個群組%{link_end},以便您å¯ä»¥è¼•é¬†ç®¡ç†ä½¿ç”¨è€…和功能。"
-
-msgid "%{over_limit_message} To view and manage members, check the members page for each project in your namespace. We recommend you %{link_start}move your projects to a group%{link_end} so you can easily manage users and features."
-msgstr "%{over_limit_message} è¦æª¢è¦–和管ç†æˆå“¡ï¼Œè«‹æª¢æŸ¥æ‚¨å‘½å空間中æ¯å€‹å°ˆæ¡ˆçš„æˆå“¡é é¢ã€‚我們建議您%{link_start}將您的專案移動到一個群組%{link_end},以便您å¯ä»¥è¼•é¬†ç®¡ç†ä½¿ç”¨è€…和功能。"
-
msgid "%{percentageUsed}%% used"
msgstr "%{percentageUsed}%% 已使用"
@@ -913,6 +916,9 @@ msgstr "%{size} ä½å…ƒçµ„"
msgid "%{sourceBranch} into %{targetBranch}"
msgstr "%{sourceBranch} 到 %{targetBranch}"
+msgid "%{source} %{copyButton} into %{target}"
+msgstr "%{source} %{copyButton} 到 %{target}"
+
msgid "%{spammable_titlecase} was submitted to Akismet successfully."
msgstr "%{spammable_titlecase} æˆåŠŸåœ°æ交給 Akismet。"
@@ -932,6 +938,10 @@ msgstr[0] "%{strongOpen}%{errors}%{strongClose} 點"
msgid "%{strongOpen}Warning:%{strongClose} SAML group links can cause GitLab to automatically remove members from groups."
msgstr "%{strongOpen}警告:%{strongClose} SAML 群組連çµæœƒä½¿ GitLab 自動從群組中移除æˆå“¡ã€‚"
+msgid "%{strongStart}%{count}%{strongEnd} commit"
+msgid_plural "%{strongStart}%{count}%{strongEnd} commits"
+msgstr[0] "%{strongStart}%{count}%{strongEnd} 次æ交"
+
msgid "%{strongStart}Tip:%{strongEnd} You can also check out merge requests locally. %{linkStart}Learn more.%{linkEnd}"
msgstr "%{strongStart}æ示:%{strongEnd} 您也å¯ä»¥åœ¨æœ¬åœ°ç«¯æŸ¥çœ‹åˆä½µè«‹æ±‚。%{linkStart}了解更多%{linkEnd}"
@@ -958,9 +968,6 @@ msgstr[0] "%{strong_start}%{count}ä½æˆå“¡%{strong_end}需核准æ‰å¯åˆä½µã€‚
msgid "%{strong_start}%{human_size}%{strong_end} Project Storage"
msgstr "%{strong_start}%{human_size}%{strong_end} 專案儲存"
-msgid "%{strong_start}%{project_name}%{strong_end} is a personal project, so you can’t upgrade to a paid plan or start a free trial to lift these limits. We recommend %{move_to_group_link}moving this project to a group%{end_link} to unlock these options. You can %{manage_members_link}manage the members of this project%{end_link}, but don’t forget that all unique members in your personal namespace %{strong_start}%{namespace_name}%{strong_end} count towards total seats in use."
-msgstr "%{strong_start}%{project_name}%{strong_end} 是個人專案,因此您無法å‡ç´šåˆ°ä»˜è²»è¨ˆåŠƒæˆ–開始å…費試用來解除這些é™åˆ¶ã€‚我們建議 %{move_to_group_link}將此專案移至群組%{end_link} 以解鎖這些é¸é …。您å¯ä»¥ %{manage_members_link}管ç†æ­¤å°ˆæ¡ˆçš„æˆå“¡%{end_link},但ä¸è¦å¿˜è¨˜æ‚¨å€‹äººå‘½å空間 %{strong_start}%{namespace_name}%{strong_end} 內的所有唯一æˆå“¡éƒ½æœƒè¨ˆå…¥ä½¿ç”¨çš„總席次。"
-
msgid "%{strong_start}%{release_count}%{strong_end} Release"
msgid_plural "%{strong_start}%{release_count}%{strong_end} Releases"
msgstr[0] "%{strong_start}%{release_count}%{strong_end} 個發行版"
@@ -1165,10 +1172,6 @@ msgstr "(已刪除)"
msgid "(revoked)"
msgstr "(已撤回)"
-msgid "(squashes %d commit)"
-msgid_plural "(squashes %d commits)"
-msgstr[0] "(åˆä½µ%d個æ交)"
-
msgid "(this user)"
msgstr "(此使用者)"
@@ -1475,6 +1478,12 @@ msgstr "一個使用 Kotlin Native 為開發 Linux 程å¼çš„基本範本"
msgid "A complete DevOps platform"
msgstr "一個完整的 DevOps å¹³å°"
+msgid "A confidential issue cannot have a parent that already has non-confidential children."
+msgstr "機密性議題ä¸èƒ½æœ‰ä¸€å€‹å·²æœ‰éžæ©Ÿå¯†å­é …的父級。"
+
+msgid "A confidential work item cannot have a parent that already has non-confidential children."
+msgstr "機密性工作項ä¸èƒ½æœ‰ä¸€å€‹å·²æœ‰éžæ©Ÿå¯†å­é …的父級。"
+
msgid "A default branch cannot be chosen for an empty project."
msgstr "無法設定空專案的é è¨­åˆ†æ”¯ã€‚"
@@ -1530,7 +1539,7 @@ msgid "A new impersonation token has been created."
msgstr "已建立新的身份模擬權æ–。"
msgid "A new personal access token has been created"
-msgstr "已建立一個新的個人存å–權æ–"
+msgstr "已建立一個新的個人存å–權æ–(令牌)"
msgid "A new personal access token, named %{token_name}, has been created."
msgstr "已建立一個新的個人存å–權æ–,命å為%{token_name}。"
@@ -1538,6 +1547,12 @@ msgstr "已建立一個新的個人存å–權æ–,命å為%{token_name}。"
msgid "A non-confidential epic cannot be assigned to a confidential parent epic"
msgstr "éžç§äººå²è©©ä¸å¯è¢«æŒ‡æ´¾åˆ°ç§äººçˆ¶å²è©©"
+msgid "A non-confidential issue cannot have a confidential parent."
+msgstr "éžæ©Ÿå¯†æ€§è­°é¡Œä¸èƒ½æœ‰æ©Ÿå¯†æ€§çš„父級。"
+
+msgid "A non-confidential work item cannot have a confidential parent."
+msgstr "éžæ©Ÿå¯†æ€§å·¥ä½œé …ä¸èƒ½æœ‰æ©Ÿå¯†æ€§çš„父級。"
+
msgid "A page with that title already exists"
msgstr "該標題é é¢å·²å­˜åœ¨"
@@ -1706,6 +1721,9 @@ msgstr "folder/openapi.json"
msgid "AWS Access Key"
msgstr "AWS å­˜å–金鑰"
+msgid "AWS OpenSearch IAM credentials"
+msgstr "AWS OpenSearch IAM 憑證"
+
msgid "AWS Secret Access Key"
msgstr "AWS 密碼存å–金鑰"
@@ -2021,8 +2039,8 @@ msgstr "在此行新增評論或拖放多行"
msgid "Add a confidential internal note to this %{noteableDisplayName}."
msgstr "å‘æ­¤ %{noteableDisplayName} 添加機密內部註釋。"
-msgid "Add a custom message with details about the instance's shared runners. The message is visible in group and project CI/CD settings, in the Runners section. Markdown is supported."
-msgstr "加入自訂訊æ¯ï¼Œè¨Šæ¯åŒ…å«å¯¦é«”的共享執行器細節。該訊æ¯æœƒåœ¨ç¾¤çµ„和專案的 CI / CD çµ„æ…‹åŸ·è¡Œå™¨ä¸­é¡¯ç¤ºã€‚æ”¯æŒ Markdown æ ¼å¼ã€‚"
+msgid "Add a custom message with details about the instance's shared runners. The message is visible when you view runners for projects and groups. Markdown is supported."
+msgstr "增加一æ¢æœ‰é—œå¯¦ä¾‹å…±äº«åŸ·è¡Œå™¨çš„詳細自訂訊æ¯ã€‚當您查看專案和群組的執行器時,å¯æª¢è¦–到該訊æ¯ã€‚æ”¯æ´ Markdown 語法。"
msgid "Add a general comment to this %{noteableDisplayName}."
msgstr "加入一般留言至 %{noteableDisplayName}。"
@@ -2048,6 +2066,9 @@ msgstr "為æœå‹™å°é›»å­éƒµä»¶åœ°å€æ·»åŠ å¾Œç¶´ã€‚ %{linkStart}了解詳情ã€
msgid "Add a table"
msgstr "加入表格"
+msgid "Add a timeline event to incident"
+msgstr ""
+
msgid "Add a title..."
msgstr "新增標題..."
@@ -2087,6 +2108,9 @@ msgstr "ç«‹å³åŠ å…¥ç•™è¨€"
msgid "Add comment to design"
msgstr "新增設計的評論"
+msgid "Add comment to incident timeline"
+msgstr "加入留言至事故時間線"
+
msgid "Add comment..."
msgstr "新增留言..."
@@ -2136,7 +2160,13 @@ msgid "Add key"
msgstr "新增金鑰"
msgid "Add label(s)"
-msgstr "加入標籤"
+msgstr "加入標記 (label)"
+
+msgid "Add labels"
+msgstr ""
+
+msgid "Add license"
+msgstr ""
msgid "Add list"
msgstr "加入列表"
@@ -2309,6 +2339,9 @@ msgstr "加入 %{labels}%{label_text}。"
msgid "Adds a Zoom meeting."
msgstr "加入 Zoom 會議."
+msgid "Adds a timeline event to incident."
+msgstr ""
+
msgid "Adds a to do."
msgstr "新增待辦事項。"
@@ -2324,9 +2357,6 @@ msgstr "將 %{issuable_type} 新增為與創建它的 %{issuable_type} 相關"
msgid "Adjust how frequently the GitLab UI polls for updates."
msgstr "調整 GitLab UI 輪詢的更新頻率。"
-msgid "Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr "在上方調整您的篩é¸/æœå°‹æ¢ä»¶ã€‚如果您èªç‚ºé€™å¯èƒ½æ˜¯ä¸€å€‹éŒ¯èª¤ï¼Œè«‹åƒè€ƒ %{linkStart}Geo故障排除%{linkEnd} 文件以了解更多資訊。"
-
msgid "Admin"
msgstr "管ç†å“¡"
@@ -2525,15 +2555,15 @@ msgstr "é è¨­æ‰€æœ‰æ–°å°ˆæ¡ˆéƒ½å¯ä»¥ä½¿ç”¨å¯¦ä¾‹çš„共享執行器。"
msgid "AdminSettings|Auto DevOps domain"
msgstr "Auto DevOps 網域"
-msgid "AdminSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr "自動å°éŽ–在時間內下載超éŽæŒ‡å®šæ•¸é‡å­˜å„²åº«çš„用戶。"
-
msgid "AdminSettings|CI/CD limits"
msgstr "CI/CD é™åˆ¶"
msgid "AdminSettings|Configure Let's Encrypt"
msgstr "é…ç½® Let's Encrypt"
+msgid "AdminSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr "設定使用者在給定時間內å¯ä»¥ä¸‹è¼‰çš„版本庫數é‡é™åˆ¶ã€‚"
+
msgid "AdminSettings|Configure when inactive projects should be automatically deleted. %{linkStart}What are inactive projects?%{linkEnd}"
msgstr "設定何時應自動刪除ä¸æ´»èºçš„專案。 %{linkStart}什麼是ä¸æ´»èºå°ˆæ¡ˆï¼Ÿ%{linkEnd}"
@@ -2573,8 +2603,8 @@ msgstr "啟用註冊功能"
msgid "AdminSettings|Enable Service Ping"
msgstr "啟用 Service Ping"
-msgid "AdminSettings|Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required. %{link_start}Learn more.%{link_end}"
-msgstr "啟用公開é‹è¡Œç‹€æ³å’Œæ€§èƒ½çµ±è¨ˆä¿¡æ¯çš„ Prometheus 端點。å¥åº·æª¢æŸ¥é¸å–®é …出ç¾åœ¨ç®¡ç†å€åŸŸçš„監控部分。需è¦é‡æ–°å•Ÿå‹•ã€‚ %{link_start}了解更多。%{link_end}"
+msgid "AdminSettings|Enable collection of application metrics. Restart required. %{link_start}Learn how to export metrics to Prometheus%{link_end}."
+msgstr ""
msgid "AdminSettings|Enable kuromoji custom analyzer: Indexing"
msgstr "啟用 kuromoji 自訂分æžå™¨: 索引建立中"
@@ -2597,6 +2627,9 @@ msgstr "啟用 smartcn 自訂分æžå™¨: 查詢"
msgid "AdminSettings|Enabled"
msgstr "已啟用"
+msgid "AdminSettings|Enforce invitation flow for groups and projects"
+msgstr "強制執行群組和專案的邀請æµç¨‹"
+
msgid "AdminSettings|Feed token"
msgstr "動態權æ–"
@@ -2762,8 +2795,11 @@ msgstr "為了幫助改善 GitLab åŠå…¶ä½¿ç”¨è€…體驗,GitLab 會定期收集
msgid "AdminSettings|Total number of jobs in currently active pipelines"
msgstr "ç›®å‰æ´»å‹•ä¸­çš„æµæ°´ç®¡ç·šä½œæ¥­ç¸½æ•¸"
-msgid "AdminSettings|Use AWS hosted Elasticsearch with IAM credentials"
-msgstr "使用 AWS 託管具有 IAM 憑證的 Elasticsearch"
+msgid "AdminSettings|Use AWS OpenSearch Service with IAM credentials"
+msgstr "使用帶有 IAM 憑證的 AWS OpenSearch æœå‹™"
+
+msgid "AdminSettings|Users and groups must accept the invitation before they're added to a group or project."
+msgstr "使用者和群組在被加入到群組或專案之å‰å¿…須接å—邀請。"
msgid "AdminSettings|When paused, GitLab still tracks the changes. This is useful for cluster/index migrations."
msgstr "æš«åœæ™‚,Gitlab ä»ç„¶æœƒè¿½è¹¤è®Šæ›´ã€‚這有助於 å¢é›†/索引 çš„å‡ç´šã€‚"
@@ -2987,6 +3023,9 @@ msgstr "此使用者所著的議題已å‘其他使用者隱è—。"
msgid "AdminUsers|It's you!"
msgstr "這就是你ï¼"
+msgid "AdminUsers|LDAP Blocked"
+msgstr "å·²å°éŽ– LDAP "
+
msgid "AdminUsers|Learn more about %{link_start}banned users.%{link_end}"
msgstr "了解更多有關%{link_start}被å°éŽ–使用者。%{link_end}"
@@ -3761,9 +3800,6 @@ msgstr "å…許使用者註冊任何應用程å¼ï¼Œä½¿ç”¨ GitLab 作為 OAuth æ
msgid "Allowed"
msgstr "å·²å…許"
-msgid "Allowed characters: +, 0-9, -, and spaces."
-msgstr "å…許的字符:+ã€0-9ã€- 和空格。"
-
msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr "åªå…許頂層群組使用電å­éƒµä»¶ç¶²åŸŸé™åˆ¶"
@@ -3848,6 +3884,9 @@ msgstr "æ‡‰ç”¨ç¨‹å¼ %{link_to_client} 請求存å–您的 GitLab 帳號。"
msgid "An email notification was recently sent from the admin panel. Please wait %{wait_time_in_words} before attempting to send another message."
msgstr "管ç†é¢æ¿å‰›æ‰ç™¼é€äº†ä¸€å°é›»å­éƒµä»¶é€šçŸ¥ã€‚請等待 %{wait_time_in_words} ,然後å†å˜—試發é€å¦ä¸€æ¢è¨Šæ¯ã€‚"
+msgid "An email will be sent with the report attached after it is generated."
+msgstr "報告產生後,將發é€ä¸€å°é™„有該報告的電å­éƒµä»¶ã€‚"
+
msgid "An empty GitLab User field will add the FogBugz user's full name (e.g. \"By John Smith\") in the description of all issues and comments. It will also associate and/or assign these issues and comments with the project creator."
msgstr "空 GitLab 使用者欄ä½å°‡åœ¨æ‰€æœ‰è­°é¡ŒåŠç•™è¨€çš„æ述中加入 FogBugz 使用者的全å(例如「由 John Smithã€ï¼‰ã€‚其還會與專案建立者關è¯å’Œï¼æˆ–分é…這些議題或留言。"
@@ -3899,6 +3938,9 @@ msgstr "新增核准者時發生錯誤"
msgid "An error occurred while adding formatted title for epic"
msgstr "為å²è©©(epic)添加格å¼åŒ–標題時發生錯誤"
+msgid "An error occurred while approving, please try again."
+msgstr "核准時發生錯誤,請é‡è©¦ã€‚"
+
msgid "An error occurred while authorizing your role"
msgstr "授權您的角色時發生錯誤"
@@ -3969,7 +4011,7 @@ msgid "An error occurred while fetching issues."
msgstr "抓å–議題時發生錯誤。"
msgid "An error occurred while fetching label colors."
-msgstr "抓å–標籤é¡è‰²æ™‚發生錯誤。"
+msgstr "抓å–標記é¡è‰²æ™‚發生錯誤。"
msgid "An error occurred while fetching participants"
msgstr "ç²å–åƒèˆ‡è€…時發生錯誤"
@@ -4037,6 +4079,9 @@ msgstr "加載此é é¢çš„æŸéƒ¨åˆ†æ™‚發生錯誤。"
msgid "An error occurred while loading all the files."
msgstr "載入所有檔案時發生錯誤。"
+msgid "An error occurred while loading branch rules. Please try again."
+msgstr "載入分支è¦å‰‡æ™‚發生錯誤,請å†è©¦ä¸€æ¬¡ã€‚"
+
msgid "An error occurred while loading chart data"
msgstr "載入圖表資料時發生錯誤"
@@ -4173,9 +4218,6 @@ msgstr[0] "儲存設定時發生錯誤"
msgid "An error occurred while saving your settings. Try saving them again."
msgstr "儲存您的設定時發生錯誤。試著å†æ¬¡å„²å­˜æ‚¨çš„設定。"
-msgid "An error occurred while subscribing to notifications."
-msgstr "訂閱通知時發生錯誤。"
-
msgid "An error occurred while triggering the job."
msgstr "觸發作業時發生錯誤。"
@@ -4185,15 +4227,15 @@ msgstr "嘗試跟隨此使用者時發生錯誤,請é‡è©¦ã€‚"
msgid "An error occurred while trying to generate the report. Please try again later."
msgstr "試圖製作報告時發生錯誤,請ç¨å¾Œé‡è©¦ã€‚"
+msgid "An error occurred while trying to render the content editor. Please try again."
+msgstr ""
+
msgid "An error occurred while trying to run a new pipeline for this merge request."
msgstr "嘗試執行此åˆä½µè«‹æ±‚çš„æ–°æµæ°´ç·šæ™‚發生錯誤。"
msgid "An error occurred while trying to unfollow this user, please try again."
msgstr "嘗試å–消關注此使用者發生錯誤,請é‡è©¦ã€‚"
-msgid "An error occurred while unsubscribing to notifications."
-msgstr "å–消訂閱通知時發生錯誤。"
-
msgid "An error occurred while updating approvers"
msgstr "更新核准者時發生錯誤"
@@ -4549,7 +4591,7 @@ msgid_plural "Apply %d suggestions"
msgstr[0] "套用 %d 個建議"
msgid "Apply a label"
-msgstr "套用標籤"
+msgstr "套用標記 (label)"
msgid "Apply a template"
msgstr "套用範本"
@@ -4729,6 +4771,9 @@ msgstr "核准"
msgid "Approve a merge request"
msgstr "核准åˆä½µè«‹æ±‚"
+msgid "Approve merge request"
+msgstr "核准åˆä½µè«‹æ±‚"
+
msgid "Approve the current merge request."
msgstr "核准目å‰åˆä½µè«‹æ±‚。"
@@ -5028,7 +5073,7 @@ msgid "Assign custom color like #FF0000"
msgstr "分é…自訂é¡è‰²ï¼Œå¦‚FF0000"
msgid "Assign labels"
-msgstr "指派標記"
+msgstr "指派標記 (label)"
msgid "Assign milestone"
msgstr "分é…里程碑"
@@ -5086,7 +5131,7 @@ msgstr "已指派給您"
msgid "Assignee"
msgid_plural "%d Assignees"
-msgstr[0] "%dä½è¢«æŒ‡æ´¾äºº"
+msgstr[0] "%d ä½è¢«æŒ‡æ´¾äºº"
msgid "Assignee has no permissions"
msgstr "被指派人沒有權é™"
@@ -5191,6 +5236,10 @@ msgstr "當月"
msgid "AuditLogs|User Events"
msgstr "使用者事件"
+msgid "AuditStreams|%d destination"
+msgid_plural "AuditStreams|%d destinations"
+msgstr[0] "%d 個目的地"
+
msgid "AuditStreams|A header with this name already exists."
msgstr "已存在åŒå稱的標頭。"
@@ -5206,11 +5255,17 @@ msgstr "添加自定義值"
msgid "AuditStreams|Add an HTTP endpoint to manage audit logs in third-party systems."
msgstr "新增一個 HTTP 端點來管ç†ç¬¬ä¸‰æ–¹ç³»çµ±ä¸­çš„紀錄。"
+msgid "AuditStreams|Add another custom header"
+msgstr "增加å¦ä¸€å€‹è‡ªè¨‚標頭"
+
msgid "AuditStreams|Add external stream destination"
msgstr "新增外部事件æµç¨‹ç›®çš„地"
-msgid "AuditStreams|Add stream"
-msgstr "新增事件æµç¨‹"
+msgid "AuditStreams|Add header"
+msgstr "增加標頭"
+
+msgid "AuditStreams|Add streaming destination"
+msgstr "增加串æµåª’體目的地"
msgid "AuditStreams|An error occurred when creating external audit event stream destination. Please try it again."
msgstr "創建外部審計事件æµç¨‹ç›®çš„地時發生錯誤,請å†è©¦ä¸€æ¬¡ã€‚"
@@ -5227,8 +5282,8 @@ msgstr "更新外部審計事件æµç›®æ¨™æ™‚發生錯誤。請å†è©¦ä¸€æ¬¡ã€‚"
msgid "AuditStreams|Cancel editing"
msgstr "å–消編輯"
-msgid "AuditStreams|Custom HTTP headers"
-msgstr "自定義 HTTP 標題"
+msgid "AuditStreams|Custom HTTP headers (optional)"
+msgstr "自訂 HTTP 標頭(é¸å¡«ï¼‰"
msgid "AuditStreams|Delete %{link}"
msgstr "刪除 %{link}"
@@ -5248,6 +5303,9 @@ msgstr "標題"
msgid "AuditStreams|Maximum of %{number} HTTP headers has been reached."
msgstr "å·²é”到最大 %{number} 個 HTTP 標題。"
+msgid "AuditStreams|Remove custom header"
+msgstr "移除自訂標頭"
+
msgid "AuditStreams|Save external stream destination"
msgstr "儲存外部æµç›®çš„地"
@@ -5257,9 +5315,6 @@ msgstr "為審計事件設置事件æµç¨‹"
msgid "AuditStreams|Stream added successfully"
msgstr "å·²æˆåŠŸåŠ å…¥æµ"
-msgid "AuditStreams|Stream count icon"
-msgstr "æµç¨‹è¨ˆæ•¸åœ–標"
-
msgid "AuditStreams|Stream deleted successfully"
msgstr "å·²æˆåŠŸåˆªé™¤æµ"
@@ -5324,7 +5379,7 @@ msgid "Authentication Failure"
msgstr "èªè­‰å¤±æ•—"
msgid "Authentication Log"
-msgstr "èªè­‰æ—¥èªŒ"
+msgstr "登入紀錄"
msgid "Authentication error: enable 2FA in your profile settings to continue using GitLab: %{mfa_help_page}"
msgstr "Authentication error: 在您的個人資料設置中啟用 2FA 以繼續使用 GitLab:%{mfa_help_page}"
@@ -5333,7 +5388,7 @@ msgid "Authentication failed: %{error_message}"
msgstr "身份驗證失敗: %{error_message}"
msgid "Authentication log"
-msgstr "èªè­‰æ—¥èªŒ"
+msgstr "登入紀錄"
msgid "Authentication method"
msgstr "驗證方å¼"
@@ -6058,12 +6113,6 @@ msgstr "使用中席次 / 訂閱席次"
msgid "Billings|Shared runners cannot be enabled until a valid credit card is on file."
msgstr "在有效的信用å¡å­˜æª”之å‰ï¼Œç„¡æ³•å•Ÿç”¨å…±ç”¨åŸ·è¡Œå™¨ã€‚"
-msgid "Billings|The last owner cannot be removed from a seat."
-msgstr "ä¸èƒ½ç§»é™¤å¸­æ¬¡ä¸Šçš„最後一ä½æ“有者"
-
-msgid "Billings|To make this member active, you must first remove an existing active member, or toggle them to over limit."
-msgstr "è¦ä½¿è©²æˆå“¡è™•æ–¼å•Ÿç”¨ç‹€æ…‹ï¼Œæ‚¨å¿…須首先刪除ç¾æœ‰çš„活動æˆå“¡ï¼Œæˆ–將其切æ›ç‚ºè¶…出é™åˆ¶ã€‚"
-
msgid "Billings|To use free CI/CD minutes on shared runners, you’ll need to validate your account with a credit card. If you prefer not to provide one, you can run pipelines by bringing your own runners and disabling shared runners for your project. This is required to discourage and reduce abuse on GitLab infrastructure. %{strongStart}GitLab will not charge your card, it will only be used for validation.%{strongEnd} %{linkStart}Learn more%{linkEnd}."
msgstr "è¦åœ¨å…±äº«çš„執行器(runners)上使用å…è²» CI/CD 分é˜ï¼Œæ‚¨éœ€è¦ä½¿ç”¨ä¿¡ç”¨å¡é©—證您的帳號。如果您ä¸æƒ³æ供,您å¯ä»¥é€šéŽç‚ºæ‚¨çš„項目帶來自己的 runner 並ç¦ç”¨ shared runners 來é‹è¡Œæµæ°´ç·šã€‚é€™æ˜¯é˜»æ­¢å’Œæ¸›å°‘å° GitLab 基礎設施的濫用所必需的。 %{strongStart}GitLab ä¸æœƒå°æ‚¨çš„å¡è™Ÿæ‰£è²»ï¼Œå®ƒåªæœƒç”¨æ–¼é©—證您的身份。%{strongEnd} %{linkStart}瞭解更多%{linkEnd}"
@@ -6079,12 +6128,6 @@ msgstr "驗證帳號"
msgid "Billings|Validate user account"
msgstr "驗證使用者帳號"
-msgid "Billings|You can't change the seat status of a user who was invited via a group or project."
-msgstr "您無法更改通éŽç¾¤çµ„或專案邀請的使用者席次狀態。"
-
-msgid "Billings|You can't remove yourself from a seat, but you can leave the group."
-msgstr "您無法將自己從座次上移除,但您å¯ä»¥é›¢é–‹ç¾¤çµ„。"
-
msgid "Billings|You'll now be able to take advantage of free CI/CD minutes on shared runners."
msgstr "您ç¾åœ¨å¯ä»¥åˆ©ç”¨å…±äº«åŸ·è¡Œå™¨çš„å…è²» CI/CD 分é˜æ•¸ã€‚"
@@ -6136,9 +6179,6 @@ msgstr "查看所有計劃"
msgid "Billing|Export list"
msgstr "匯出清單"
-msgid "Billing|From October 19, 2022, free groups will be limited to 5 members"
-msgstr "從 2022 å¹´ 10 月 19 日起,å…費群組將é™åˆ¶ç‚º 5 åæˆå“¡"
-
msgid "Billing|Group invite"
msgstr "群組邀請"
@@ -6179,9 +6219,6 @@ msgstr "檢視等待中的核准"
msgid "Billing|You are about to remove user %{username} from your subscription. If you continue, the user will be removed from the %{namespace} group and all its subgroups and projects. This action can't be undone."
msgstr "您將è¦å¾žè¨‚閱中移除使用者%{username}。如果繼續,該使用者將從 %{namespace} 群組åŠå…¶æ‰€æœ‰å­çµ„和項目中刪除。此æ“作無法撤消。"
-msgid "Billing|You can begin moving members in %{namespaceName} now. A member loses access to the group when you turn off %{strongStart}In a seat%{strongEnd}. If over 5 members have %{strongStart}In a seat%{strongEnd} enabled after October 19, 2022, we'll select the 5 members who maintain access. We'll first count members that have Owner and Maintainer roles, then the most recently active members until we reach 5 members. The remaining members will get a status of Over limit and lose access to the group."
-msgstr "您ç¾åœ¨å¯ä»¥é–‹å§‹ç§»å‹• %{namespaceName} 中的æˆå“¡äº†ï¼Œç•¶æ‚¨é—œé–‰ %{strongStart}席次%{strongEnd} 後,æˆå“¡å°‡ç„¡æ³•å­˜å–該群組。如果在 2022 å¹´ 10 月 19 æ—¥ä¹‹å¾Œå•Ÿç”¨äº†è¶…éŽ 5 åæˆå“¡çš„ %{strongStart}席次%{strongEnd},我們將é¸æ“‡ 5 åæˆå“¡ç¶­æŒå­˜å–權é™ã€‚我們將首先計算具有æ“者和維護者角色的æˆå“¡ï¼Œç„¶å¾Œæ˜¯æœ€è¿‘較為活èºçš„æˆå“¡ï¼Œç›´åˆ°æˆ‘們é”到 5 個æˆå“¡ï¼Œå…¶é¤˜æˆå“¡å°‡è®Šç‚ºâ€œè¶…出é™åˆ¶â€ç‹€æ…‹ä¸¦å¤±åŽ»å°è©²ç¾¤çµ„çš„å­˜å–權é™ã€‚"
-
msgid "Billing|Your group recently changed to use the Free plan. %{over_limit_message} You can free up space for new members by removing those who no longer need access or toggling them to over-limit. To get an unlimited number of members, you can %{link_start}upgrade%{link_end} to a paid tier."
msgstr "您的群組最近更改為使用å…費計劃。 %{over_limit_message} 您å¯ä»¥é€šéŽåˆªé™¤ä¸å†éœ€è¦è¨ªå•æ¬Šé™çš„æˆå“¡æˆ–將其切æ›ç‚ºè¶…é™ä¾†ç‚ºæ–°æˆå“¡é‡‹æ”¾ç©ºé–“。è¦ç²å¾—ç„¡é™æ•¸é‡çš„會員,您å¯ä»¥ %{link_start}å‡ç´š%{link_end} 到付費等級。"
@@ -6216,6 +6253,9 @@ msgstr "被å°éŽ–çš„è­°é¡Œ"
msgid "Blocking"
msgstr "å°éŽ–中"
+msgid "Blocking epics"
+msgstr "å°éŽ–å²è©©"
+
msgid "Blocking issues"
msgstr "å°éŽ–中議題"
@@ -6422,6 +6462,15 @@ msgstr "展開"
msgid "Boards|Failed to fetch blocking %{issuableType}s"
msgstr "無法抓å–ç¦ç”¨çš„ %{issuableType}"
+msgid "Boards|Move card"
+msgstr "移動å¡ç‰‡"
+
+msgid "Boards|Move to end of list"
+msgstr "移動到列表末尾"
+
+msgid "Boards|Move to start of list"
+msgstr "移動到列表的開頭"
+
msgid "Boards|New board"
msgstr "新建看æ¿"
@@ -6548,6 +6597,12 @@ msgstr "拒絕程å¼ç¢¼æŽ¨é€è®Šæ›´åˆ—æ–¼ CODEOWNERS 檔案中的檔案。"
msgid "BranchRules|Require approval from code owners."
msgstr "需è¦ç¨‹å¼ç¢¼æ“有者的批准。"
+msgid "BranchRules|default"
+msgstr ""
+
+msgid "BranchRules|protected"
+msgstr ""
+
msgid "Branches"
msgstr "分支"
@@ -6713,6 +6768,9 @@ msgstr "ç€è¦½æ–‡ä»¶"
msgid "Browse templates"
msgstr "ç€è¦½ç¯„本"
+msgid "Build cannot be erased"
+msgstr ""
+
msgid "BuildArtifacts|An error occurred while fetching the artifacts"
msgstr "抓å–產物時發生錯誤"
@@ -6966,21 +7024,12 @@ msgstr "發布統計"
msgid "CICDAnalytics|Releases"
msgstr "發布"
-msgid "CICDAnalytics|Shared Runners Usage"
-msgstr "CICDAnalytics|共享執行器使用情æ³"
-
msgid "CICDAnalytics|Shared runner duration is the total runtime of all jobs that ran on shared runners"
msgstr "共享執行器æŒçºŒæ™‚間是所有作業在共享執行器上的總é‹è¡Œæ™‚é–“"
msgid "CICDAnalytics|Shared runner pipeline minute duration by month"
msgstr "CICDAnalytics|共享執行器æµæ°´ç·šåˆ†é˜æŒçºŒæœŸé–“(按月)"
-msgid "CICDAnalytics|Shared runner usage"
-msgstr "CICDAnalytics|共享執行器使用情æ³"
-
-msgid "CICDAnalytics|Shared runner usage is the total runtime of all jobs that ran on shared runners"
-msgstr "共享執行器的使用é‡æ˜¯åœ¨å…±äº«çš„執行器上é‹è¡Œçš„所有作業的總é‹è¡Œæ™‚é–“"
-
msgid "CICDAnalytics|Something went wrong while fetching release statistics"
msgstr "抓å–發布統計信æ¯æ™‚出ç¾éŒ¯èª¤"
@@ -6990,9 +7039,6 @@ msgstr "æœå‹™æ¢å¾©æ™‚é–“"
msgid "CICDAnalytics|What is shared runner duration?"
msgstr "什麼是共享執行器æŒçºŒæ™‚間?"
-msgid "CICDAnalytics|What is shared runner usage?"
-msgstr "CICDAnalytics|共享的執行器使用情æ³ï¼Ÿ"
-
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 "è«‹å°‡%{base_domain_link_start}基礎域å%{link_end}添加到%{kubernetes_cluster_link_start}Kuberneteså¢é›†%{link_end},æ‰èƒ½ä½¿æ‚¨çš„部署策略正常工作。"
@@ -7336,7 +7382,7 @@ msgid "CascadingSettings|cannot be nil when locking the attribute"
msgstr "鎖定屬性時ä¸èƒ½ç‚ºç„¡"
msgid "Certain user content will be moved to a system-wide \"Ghost User\" in order to maintain content for posterity. For further information, please refer to the %{link_start}user account deletion documentation.%{link_end}"
-msgstr "此使用者內容將被移動到系統級「幽éˆä½¿ç”¨è€…ã€ï¼Œä»¥ä¿ç•™ä¾›æœªä¾†éœ€è¦æ™‚使用。 欲瞭解更多信æ¯ï¼Œè«‹åƒé–±%{link_start}用戶帳號刪除文檔%{link_end}。"
+msgstr "此使用者內容將被移動到系統級別的「幽éˆä½¿ç”¨è€…ã€ï¼Œä»¥ä¿ç•™ä¾›æœªä¾†éœ€è¦æ™‚使用。 欲瞭解更多信æ¯ï¼Œè«‹åƒé–±%{link_start}使用者帳號刪除文件%{link_end}。"
msgid "Certificate"
msgstr "憑證"
@@ -7941,7 +7987,7 @@ msgid "CiStatusLabel|waiting for resource"
msgstr "等待資æº"
msgid "CiStatusText|blocked"
-msgstr "已阻塞"
+msgstr "å·²å°ç¦"
msgid "CiStatusText|canceled"
msgstr "å·²å–消"
@@ -7980,61 +8026,61 @@ msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr "ç›®å‰æ•¸å€¼ç„¡æ³•ä½¿ç”¨è®Šæ•¸é®ç½©"
msgid "CiVariables|Environments"
-msgstr "Ci變數|環境"
+msgstr "環境"
msgid "CiVariables|Input variable key"
-msgstr "Ci變數|輸入變數的å稱"
+msgstr "輸入變數的å稱"
msgid "CiVariables|Input variable value"
-msgstr "Ci變數|輸入變數的值"
+msgstr "輸入變數的值"
msgid "CiVariables|Key"
-msgstr "Ci變數|éµ"
+msgstr "éµ"
msgid "CiVariables|Masked"
-msgstr "Ci變數|é®ç½©"
+msgstr "é®ç½©"
msgid "CiVariables|Protected"
-msgstr "Ci變數|å—ä¿è­·"
+msgstr "å—ä¿è­·"
msgid "CiVariables|Remove variable"
-msgstr "Ci變數|移除變數"
+msgstr "移除變數"
msgid "CiVariables|Remove variable row"
-msgstr "Ci變數|移除變數行"
+msgstr "移除變數行"
msgid "CiVariables|Scope"
-msgstr "Ci變數|範åœ"
+msgstr "範åœ"
msgid "CiVariables|Specify variable values to be used in this run. The values specified in %{linkStart}CI/CD settings%{linkEnd} will be used as default"
-msgstr "Ci變數|指定è¦åœ¨æ­¤åŸ·è¡Œä¸­ä½¿ç”¨çš„變數值。%{linkStart}CI/CD設定%{linkEnd}中指定的值將用作é è¨­å€¼"
+msgstr "指定è¦åœ¨æ­¤åŸ·è¡Œä¸­ä½¿ç”¨çš„變數值。%{linkStart}CI/CD設定%{linkEnd}中指定的值將用作é è¨­å€¼"
msgid "CiVariables|State"
-msgstr "Ci變數|狀態"
+msgstr "狀態"
msgid "CiVariables|Type"
-msgstr "Ci變數|類型"
+msgstr "é¡žåž‹"
msgid "CiVariables|Value"
-msgstr "Ci變數|值"
+msgstr "值"
msgid "CiVariables|Variables"
-msgstr "Ci變數|變數"
+msgstr "變數"
msgid "CiVariable|* (All environments)"
-msgstr "Ci變數|* (所有環境)"
+msgstr "* (所有環境)"
msgid "CiVariable|All environments"
-msgstr "Ci變數|所有環境"
+msgstr "所有環境"
msgid "CiVariable|Create wildcard"
-msgstr "Ci變數|建立è¬ç”¨å­—å…ƒ"
+msgstr "建立è¬ç”¨å­—å…ƒ"
msgid "CiVariable|New environment"
-msgstr "Ci變數|建立新環境"
+msgstr "建立新環境"
msgid "CiVariable|Search environments"
-msgstr "Ci變數|æœå°‹ç’°å¢ƒ"
+msgstr "æœå°‹ç’°å¢ƒ"
msgid "Classification Label (optional)"
msgstr "分類標籤(å¯é¸)"
@@ -8241,6 +8287,12 @@ msgstr "關閉此%{quick_action_target}."
msgid "Cloud Run"
msgstr "雲端執行"
+msgid "Cloud SQL instances are fully managed, relational MySQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr "Cloud SQL 實例是完全被託管的關è¯å¼ MySQL 資料庫。 Google 處ç†è¤‡è£½ã€è£œä¸ç®¡ç†å’Œè³‡æ–™åº«ç®¡ç†ä»¥ç¢ºä¿å…¶å¯ç”¨æ€§å’Œæ€§èƒ½ã€‚"
+
+msgid "Cloud SQL instances are fully managed, relational SQL Server databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr "Cloud SQL 實例是完全被託管的關è¯å¼ SQL Server 資料庫。 Google 處ç†è¤‡è£½ã€è£œä¸ç®¡ç†å’Œè³‡æ–™åº«ç®¡ç†ä»¥ç¢ºä¿å…¶å¯ç”¨æ€§å’Œæ€§èƒ½ã€‚"
+
msgid "Cloud Storage"
msgstr "雲端儲存"
@@ -8268,12 +8320,24 @@ msgstr "Cloud SQL for Postgres"
msgid "CloudSeed|Cloud SQL for SQL Server"
msgstr "é©ç”¨æ–¼ SQL Server çš„ Cloud SQL"
+msgid "CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes."
+msgstr "Cloud SQL 實例建立請求æˆåŠŸï¼Œé è¨ˆè™•ç†æ™‚間約為 5 分é˜"
+
+msgid "CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. Google handles replication, patch management, and database management to ensure availability and performance."
+msgstr "Cloud SQL 實例是完全被託管的關è¯å¼ PostgreSQL 資料庫。 Google 處ç†è¤‡è£½ã€è£œä¸ç®¡ç†å’Œè³‡æ–™åº«ç®¡ç†ä»¥ç¢ºä¿å…¶å¯ç”¨æ€§å’Œæ€§èƒ½ã€‚"
+
msgid "CloudSeed|CloudSQL Instance"
msgstr "CloudSQL 實例"
msgid "CloudSeed|Configuration"
msgstr "é…ç½®"
+msgid "CloudSeed|Create MySQL Instance"
+msgstr "建立 MySQL 實例"
+
+msgid "CloudSeed|Create Postgres Instance"
+msgstr "建立 Postgres 實例"
+
msgid "CloudSeed|Create cluster"
msgstr "建立å¢é›†"
@@ -8328,6 +8392,9 @@ msgstr "完全託管的 SQL Server é—œè¯å¼è³‡æ–™åº«æœå‹™"
msgid "CloudSeed|Generated database instance is linked to the selected branch or tag"
msgstr "產生的資料庫實例已éˆæŽ¥åˆ°é¸å®šçš„分支或標籤"
+msgid "CloudSeed|Google Cloud Error - %{message}"
+msgstr "Google Cloud 錯誤 - %{message}"
+
msgid "CloudSeed|Google Cloud Project"
msgstr "Google Cloud 專案"
@@ -9013,11 +9080,11 @@ msgstr "用於å°å¢é›†é€²è¡Œèº«ä»½é©—證的 Kubernetes 憑證。"
msgid "ClusterIntegration|The URL used to access the Kubernetes API."
msgstr "ç”¨æ–¼å­˜å– Kubernetes API çš„ URL。"
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
-msgstr "基於證書的 Kubernetes æ•´åˆå·²è¢«æ£„用,將於 2022 å¹´ 11 月關閉。請 %{linkStart}é·ç§»åˆ° GitLab 代ç†çš„ Kubernetes%{linkEnd} 或è¯ç¹« GitLab 支æ´æœå‹™ã€‚"
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support."
+msgstr ""
-msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
-msgstr "基於證書的 Kubernetes æ•´åˆå·²è¢«æ£„用,將於 2022 å¹´ 11 月關閉。請 %{linkStart}é·ç§»åˆ° GitLab 代ç†çš„ Kubernetes%{linkEnd}。"
+msgid "ClusterIntegration|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}."
+msgstr ""
msgid "ClusterIntegration|The certificate-based method to connect clusters to GitLab was %{linkStart}deprecated%{linkEnd} in GitLab 14.5."
msgstr "採用憑證將å¢é›†é€£æŽ¥åˆ° GitLab 的方法將於 14.5 版本%{linkStart}棄用%{linkEnd}。"
@@ -9187,6 +9254,9 @@ msgstr "收疊議題"
msgid "Collapse jobs"
msgstr "收疊作業"
+msgid "Collapse merge details"
+msgstr "收摺åˆä½µè©³ç´°è³‡è¨Šæ¯"
+
msgid "Collapse milestones"
msgstr "收疊里程碑"
@@ -9253,6 +9323,9 @@ msgstr "留言但未確èª"
msgid "Comment '%{label}' position"
msgstr "留言%{label}'çš„ä½ç½®"
+msgid "Comment added to the timeline."
+msgstr "留言已加入至時間線。"
+
msgid "Comment form position"
msgstr "留言框ä½ç½®"
@@ -9374,6 +9447,9 @@ msgstr "比較GitLab版本"
msgid "Compare Revisions"
msgstr "比較版本"
+msgid "Compare branches and continue"
+msgstr ""
+
msgid "Compare changes"
msgstr "比較變更"
@@ -9949,6 +10025,9 @@ msgstr "ContainerRegistry|摘è¦ï¼š%{imageId}"
msgid "ContainerRegistry|Docker connection error"
msgstr "Docker連接錯誤"
+msgid "ContainerRegistry|Edit cleanup rules"
+msgstr ""
+
msgid "ContainerRegistry|Enable expiration policy"
msgstr "ContainerRegistry|啟用逾期策略"
@@ -9968,7 +10047,7 @@ msgid "ContainerRegistry|Image repository not found"
msgstr "ContainerRegistry|未找到映åƒæª”版本庫"
msgid "ContainerRegistry|Image repository temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}"
-msgstr "暫時無法將映åƒå­˜å„²åº«æ¨™è¨˜ç‚ºåˆªé™¤ã€‚ 請在幾分é˜å¾Œå†è©¦ä¸€æ¬¡ã€‚ %{docLinkStart}更多詳情%{docLinkEnd}"
+msgstr "暫時無法將映åƒå­˜å„²åº«æ¨™ç¤ºç‚ºåˆªé™¤ã€‚ 請在幾分é˜å¾Œå†è©¦ä¸€æ¬¡ã€‚ %{docLinkStart}更多詳情%{docLinkEnd}"
msgid "ContainerRegistry|Image repository will be deleted"
msgstr "ContainerRegistry|映åƒæª”版本庫將被刪除"
@@ -10052,6 +10131,12 @@ msgstr "執行清ç†ï¼š"
msgid "ContainerRegistry|Save storage space by automatically deleting tags from the container registry and keeping the ones you want. %{linkStart}How does cleanup work?%{linkEnd}"
msgstr "通éŽè‡ªå‹•å¾žå®¹å™¨è¨»å†Šåº«ä¸­åˆªé™¤ä¸¦ä¿ç•™æ‚¨æƒ³è¦çš„標籤來節çœå„²å­˜ç©ºé–“。%{linkStart}清ç†å·¥ä½œå¦‚何進行?%{linkEnd}"
+msgid "ContainerRegistry|Set cleanup rules"
+msgstr ""
+
+msgid "ContainerRegistry|Set rules to automatically remove unused packages to save storage space."
+msgstr ""
+
msgid "ContainerRegistry|Set up cleanup"
msgstr "設置清ç†"
@@ -10074,10 +10159,10 @@ msgid "ContainerRegistry|Something went wrong while fetching the tags list."
msgstr "å–得標籤列表時發生錯誤。"
msgid "ContainerRegistry|Something went wrong while marking the tag for deletion."
-msgstr "將標籤標記為待刪除時發生錯誤。"
+msgstr "將標籤標示為待刪除時發生錯誤。"
msgid "ContainerRegistry|Something went wrong while marking the tags for deletion."
-msgstr "將標籤標記為待刪除時發生錯誤。"
+msgstr "將標籤標示為待刪除時發生錯誤。"
msgid "ContainerRegistry|Something went wrong while scheduling %{title} for deletion. Please try again."
msgstr "安排刪除%{title}時發生錯誤。請å†è©¦ä¸€æ¬¡ã€‚"
@@ -10092,13 +10177,13 @@ msgid "ContainerRegistry|Sorry, your filter produced no results."
msgstr "å°ä¸èµ·ï¼Œæ²’有符åˆç¯©é¸å™¨çš„任何çµæžœ."
msgid "ContainerRegistry|Tag successfully marked for deletion."
-msgstr "標籤已æˆåŠŸè¢«æ¨™è¨˜ç‚ºå¾…刪除。"
+msgstr "標籤已æˆåŠŸè¢«æ¨™ç¤ºç‚ºå¾…刪除。"
msgid "ContainerRegistry|Tags successfully marked for deletion."
-msgstr "標籤已æˆåŠŸè¢«æ¨™è¨˜ç‚ºå¾…刪除。"
+msgstr "標籤已æˆåŠŸè¢«æ¨™ç¤ºç‚ºå¾…刪除。"
msgid "ContainerRegistry|Tags temporarily cannot be marked for deletion. Please try again in a few minutes. %{docLinkStart}More details%{docLinkEnd}."
-msgstr "標籤暫時ä¸èƒ½è¢«æ¨™è¨˜ç‚ºåˆªé™¤ã€‚請幾分é˜å¾Œå†è©¦ã€‚%{docLinkStart}更多詳情%{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}標籤總是會被ä¿ç•™ã€‚"
@@ -10226,8 +10311,8 @@ msgstr "建立%{created_count}個,關閉%{closed_count}個。"
msgid "ContributionAnalytics|%{created_count} created, %{merged_count} merged, %{closed_count} closed."
msgstr "已建立%{created_count}個,已åˆä½µ%{merged_count}個,已關閉%{closed_count}個。"
-msgid "ContributionAnalytics|%{pushes} pushes, more than %{commits} commits by %{people} contributors."
-msgstr "%{pushes}次推é€ï¼Œè¶…éŽäº†%{people}個貢ç»è€…çš„%{commits}次æ交。"
+msgid "ContributionAnalytics|%{pushes}, more than %{commits} by %{contributors}."
+msgstr "%{pushes}ï¼Œè¶…éŽ %{contributors} çš„ %{commits}。"
msgid "ContributionAnalytics|Contribution analytics for issues, merge requests and push events since %{start_date}"
msgstr "從 %{start_date} 開始å°è­°é¡Œã€åˆä½µè«‹æ±‚åŠæŽ¨é€äº‹ä»¶çš„è²¢ç»çµ±è¨ˆ"
@@ -10719,7 +10804,7 @@ msgid "Create group"
msgstr "建立群組"
msgid "Create group label"
-msgstr "建立群組標籤"
+msgstr "建立群組標記 (label)"
msgid "Create issue"
msgstr "建立議題"
@@ -10731,7 +10816,7 @@ msgid "Create iteration"
msgstr "建立迭代"
msgid "Create label"
-msgstr "建立標記"
+msgstr "建立標記 (label)"
msgid "Create list"
msgstr "建立清單"
@@ -10740,7 +10825,7 @@ msgid "Create lists from labels. Issues with that label appear in that list."
msgstr "從標記建立列表,å«æœ‰è©²æ¨™è¨˜çš„議題將出ç¾åœ¨ç›¸æ‡‰çš„列中。"
msgid "Create merge request"
-msgstr "建立åˆä½µè¦æ±‚"
+msgstr "建立åˆä½µè«‹æ±‚"
msgid "Create merge request and branch"
msgstr "建立åˆä½µè«‹æ±‚åŠåˆ†æ”¯"
@@ -10773,7 +10858,7 @@ msgid "Create new file or directory"
msgstr "建立新文件或目錄"
msgid "Create new label"
-msgstr "建立新標記"
+msgstr "建立新標記 (label)"
msgid "Create new project"
msgstr "建立新專案"
@@ -10794,7 +10879,7 @@ msgid "Create project"
msgstr "建立專案"
msgid "Create project label"
-msgstr "建立專案標記"
+msgstr "建立專案標記 (label)"
msgid "Create release"
msgstr "建立版本發布"
@@ -10829,9 +10914,6 @@ msgstr "建立您的群組"
msgid "Create, update, or delete a merge request."
msgstr "建立ã€æ›´æ–°æˆ–刪除åˆä½µè«‹æ±‚。"
-msgid "Create/import your first project"
-msgstr "建立/匯入您的第一個專案"
-
msgid "CreateGroup|You don’t have permission to create a subgroup in this group."
msgstr "您無權在此群組中建立å­ç¾¤çµ„。"
@@ -10890,7 +10972,7 @@ msgid "CreateValueStreamForm|End event"
msgstr "çµæŸäº‹ä»¶"
msgid "CreateValueStreamForm|End event label"
-msgstr "çµæŸäº‹ä»¶æ¨™è¨˜"
+msgstr "çµæŸäº‹ä»¶æ¨™è¨˜ (label)"
msgid "CreateValueStreamForm|End event: "
msgstr "çµæŸäº‹ä»¶ï¼š "
@@ -10956,7 +11038,7 @@ msgid "CreateValueStreamForm|Start event changed, please select a valid end even
msgstr "開始事件發生變更,請é¸æ“‡ä¸€å€‹æœ‰æ•ˆçš„çµæŸäº‹ä»¶"
msgid "CreateValueStreamForm|Start event label"
-msgstr "開始事件標記"
+msgstr "開始事件標記 (label)"
msgid "CreateValueStreamForm|Start event: "
msgstr "開始事件: "
@@ -11277,7 +11359,7 @@ msgid "CycleAnalyticsEvent|%{label_reference} label was added to the merge reque
msgstr "%{label_reference} 標記已添加到åˆä½µè«‹æ±‚中"
msgid "CycleAnalyticsEvent|%{label_reference} label was removed from the issue"
-msgstr "%{label_reference} 標記已從議題(issue)中移除"
+msgstr "%{label_reference} 標記已從議題中移除"
msgid "CycleAnalyticsEvent|%{label_reference} label was removed from the merge request"
msgstr "%{label_reference} 標記已從åˆä½µè«‹æ±‚中移除"
@@ -11375,6 +11457,9 @@ msgstr "'%{name}'正在收集資料,這å¯èƒ½éœ€è¦å¹¾åˆ†é˜ã€‚"
msgid "CycleAnalytics|Average time to completion"
msgstr "å¹³å‡å®Œæˆæ™‚é–“"
+msgid "CycleAnalytics|Average time to completion (days)"
+msgstr "å¹³å‡å®Œæˆæ™‚間(天)"
+
msgid "CycleAnalytics|Change Failure Rate"
msgstr "變更失敗率"
@@ -11604,6 +11689,12 @@ 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 消æ¯ï¼ˆè«‹æ±‚和回應),主動掃æ則攻擊目標以發ç¾æ½›åœ¨æ¼æ´žã€‚"
+msgid "DastProfiles|A scanner profile defines the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
+msgid "DastProfiles|A site profile defines the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
+msgstr ""
+
msgid "DastProfiles|AJAX spider"
msgstr "AJAX 爬蟲"
@@ -11682,15 +11773,6 @@ msgstr "除錯訊æ¯"
msgid "DastProfiles|Delete profile"
msgstr "刪除設定檔"
-msgid "DastProfiles|Do you want to discard this scanner profile?"
-msgstr "是å¦è¦æ¨æ£„此掃æ器設定檔?"
-
-msgid "DastProfiles|Do you want to discard this site profile?"
-msgstr "是å¦è¦æ¨æ£„此站點設定檔?"
-
-msgid "DastProfiles|Do you want to discard your changes?"
-msgstr "是å¦è¦æ¨æ£„您的變更?"
-
msgid "DastProfiles|Edit profile"
msgstr "編輯個人資料"
@@ -11703,6 +11785,9 @@ msgstr "編輯站點設定檔"
msgid "DastProfiles|Enable Authentication"
msgstr "啟用èªè­‰"
+msgid "DastProfiles|Enable Basic Authentication"
+msgstr "啟用基本身份驗證"
+
msgid "DastProfiles|Enter URLs in a comma-separated list."
msgstr "在用逗號分隔的清單中輸入 URL。"
@@ -11778,6 +11863,9 @@ msgstr "密碼"
msgid "DastProfiles|Password form field"
msgstr "密碼表單欄ä½"
+msgid "DastProfiles|Profile in use and cannot be renamed"
+msgstr "個人é…置文件使用中,無法é‡æ–°å‘½å"
+
msgid "DastProfiles|Profile is being used by this on-demand scan"
msgstr "é…置已被用於該按需掃æ"
@@ -11805,17 +11893,14 @@ msgstr "掃æ方法"
msgid "DastProfiles|Scan mode"
msgstr "掃æ模å¼"
-msgid "DastProfiles|Scanner Profile"
-msgstr "掃æ器設定檔"
-
-msgid "DastProfiles|Scanner Profiles"
-msgstr "掃æ器設定檔"
-
msgid "DastProfiles|Scanner name"
msgstr "掃æ器å稱"
-msgid "DastProfiles|Scanner profiles define the configuration details of a security scanner. %{linkStart}Learn more%{linkEnd}."
-msgstr "掃æé…置文件定義了安全掃æ的組態細節。 %{linkStart}了解更多%{linkEnd}。"
+msgid "DastProfiles|Scanner profile"
+msgstr ""
+
+msgid "DastProfiles|Scanner profiles"
+msgstr ""
msgid "DastProfiles|Select a scanner profile to run a DAST scan"
msgstr "é¸æ“‡æŽƒæé…置文件以執行 DAST 掃æ"
@@ -11835,17 +11920,14 @@ msgstr "é¸æ“‡ç«™é»žé…ç½®"
msgid "DastProfiles|Show debug messages"
msgstr "顯示除錯訊æ¯"
-msgid "DastProfiles|Site Profile"
-msgstr "站點設定檔"
-
-msgid "DastProfiles|Site Profiles"
-msgstr "站點設定檔"
-
msgid "DastProfiles|Site name"
msgstr "站點å稱"
-msgid "DastProfiles|Site profiles define the attributes and configuration details of your deployed application, website, or API. %{linkStart}Learn more%{linkEnd}."
-msgstr "站點é…置定義了已部署的應用程åºã€ç¶²ç«™æˆ– API 的屬性和組態詳細資訊。 %{linkStart}了解更多%{linkEnd}。"
+msgid "DastProfiles|Site profile"
+msgstr ""
+
+msgid "DastProfiles|Site profiles"
+msgstr ""
msgid "DastProfiles|Site type"
msgstr "站點類型"
@@ -11913,14 +11995,14 @@ msgstr "您å¯ä»¥é¸æ“‡è¢«å‹•æŽƒæ或從站點é…置文件管ç†é é¢é©—證目
msgid "DastProfiles|You cannot run an active scan against an unvalidated site."
msgstr "ä¸èƒ½å°æœªç¶“驗證的站點é‹è¡Œä¸»å‹•æŽƒæ。"
-msgid "DastProfiles|folder/dast_example.har or https://example.com/dast_example.har"
-msgstr "folder/dast_example.har 或 https://example.com/dast_example.har"
+msgid "DastProfiles|https://example.com/dast_example.har"
+msgstr "https://example.com/dast_example.har"
-msgid "DastProfiles|folder/example.postman_collection.json or https://example.com/"
-msgstr "folder/example.postman_collection.json 或 https://example.com/"
+msgid "DastProfiles|https://example.com/openapi.json"
+msgstr "https://example.com/dast_example.har"
-msgid "DastProfiles|folder/openapi.json or https://example.com/openapi.json"
-msgstr "folder/openapi.json 或 https://example.com/openapi.json"
+msgid "DastProfiles|https://example.com/postman_collection.json"
+msgstr "https://example.com/postman_collection.json"
msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr "複製HTTP標頭到剪貼簿"
@@ -12106,8 +12188,8 @@ msgstr "天"
msgid "Days to merge"
msgstr "åˆä½µæ‰€éœ€å¤©æ•¸"
-msgid "Deactivate dormant users after 90 days of inactivity"
-msgstr "åœç”¨é–’ç½® 90 天後的休眠使用者"
+msgid "Deactivate dormant users after a period of inactivity"
+msgstr "在一段時間未活動後åœç”¨ä¼‘眠使用者"
msgid "Dear Administrator,"
msgstr "親愛的管ç†å“¡ï¼Œ"
@@ -12292,6 +12374,9 @@ msgstr "刪除語料庫"
msgid "Delete deploy key"
msgstr "刪除部署金鑰"
+msgid "Delete epic"
+msgstr "刪除å²è©©"
+
msgid "Delete file"
msgstr "刪除檔案"
@@ -12343,6 +12428,9 @@ msgstr "刪除程å¼ç¢¼ç‰‡æ®µå—Žï¼Ÿ"
msgid "Delete source branch"
msgstr "刪除æºåˆ†æ”¯"
+msgid "Delete source branch when merge request is accepted."
+msgstr "當åˆä½µè«‹æ±‚被接å—時,刪除來æºåˆ†æ”¯ã€‚"
+
msgid "Delete subscription"
msgstr "刪除訂閱"
@@ -12773,13 +12861,13 @@ msgid "DeployTokens|Created"
msgstr "建立時間"
msgid "DeployTokens|Deploy tokens"
-msgstr "部署令牌|部署令牌(權æ–)"
+msgstr "部署令牌(權æ–)"
msgid "DeployTokens|Deploy tokens allow access to packages, your repository, and registry images."
-msgstr "部署令牌|部署令牌(權æ–)å…許存å–軟體套件ã€æ‚¨çš„版本庫和註冊庫映åƒæª”。"
+msgstr "部署令牌(權æ–)å…許存å–軟體套件ã€æ‚¨çš„版本庫和註冊庫映åƒæª”。"
msgid "DeployTokens|Enter a unique name for your deploy token."
-msgstr "部署令牌|為您的部署令牌(權æ–)輸入一個唯一的å稱。"
+msgstr "為您的部署令牌(權æ–)輸入一個唯一的å稱。"
msgid "DeployTokens|Enter a username for your token. Defaults to %{code_start}gitlab+deploy-token-{n}%{code_end}."
msgstr "為您的令牌輸入使用者å稱。é è¨­ç‚º %{code_start}gitlab+deploy-token-{n}%{code_end}。"
@@ -12791,13 +12879,13 @@ msgid "DeployTokens|Expires"
msgstr "éŽæœŸ"
msgid "DeployTokens|Group deploy tokens allow access to the packages, repositories, and registry images within the group."
-msgstr "部署令牌|群組部署令牌å…許存å–群組內的軟體套件ã€ç‰ˆæœ¬åº«å’Œè¨»å†Šåº«æ˜ åƒæª”。"
+msgstr "群組部署令牌(權æ–)å…許存å–群組內的軟體套件ã€ç‰ˆæœ¬åº«å’Œè¨»å†Šåº«æ˜ åƒæª”。"
msgid "DeployTokens|Name"
msgstr "å稱"
msgid "DeployTokens|New deploy token"
-msgstr "部署令牌|新建部署令牌"
+msgstr "新建部署令牌(權æ–)"
msgid "DeployTokens|Revoke"
msgstr "撤銷"
@@ -12809,7 +12897,7 @@ msgid "DeployTokens|Scopes"
msgstr "有效範åœ"
msgid "DeployTokens|This %{entity_type} has no active Deploy Tokens."
-msgstr "部署令牌|此 %{entity_type} 沒有啟用的部署令牌。"
+msgstr "æ­¤ %{entity_type} 沒有啟用的部署令牌(權æ–)。"
msgid "DeployTokens|This action cannot be undone."
msgstr "該動作作無法復原。"
@@ -12824,10 +12912,10 @@ msgid "DeployTokens|Username"
msgstr "使用者å稱"
msgid "DeployTokens|Your new Deploy Token username"
-msgstr "部署令牌|您的新部署令牌使用者å稱"
+msgstr "您的新部署令牌(權æ–)使用者å稱"
msgid "DeployTokens|Your new group deploy token has been created."
-msgstr "部署令牌|您的新群組佈署令牌已經建立。"
+msgstr "您的新群組佈署令牌(權æ–)已經建立。"
msgid "DeployTokens|Your new project deploy token has been created."
msgstr "新項目部署令牌(權æ–)已建立。"
@@ -12895,6 +12983,12 @@ msgstr "環境: %{environment}"
msgid "DeploymentApproval|Manual job: %{jobName}"
msgstr "手動作業: %{jobName}"
+msgid "DeploymentApproval|Rejected %{time}"
+msgstr "已拒絕於 %{time} "
+
+msgid "DeploymentApproval|Rejected by you %{time}"
+msgstr "被您拒絕於 %{time}"
+
msgid "DeploymentTarget|GitLab Pages"
msgstr "GitLab Pages"
@@ -13122,6 +13216,9 @@ msgstr "設計"
msgid "DesignManagement|Discard comment"
msgstr "æ¨æ£„留言"
+msgid "DesignManagement|Discussion"
+msgstr ""
+
msgid "DesignManagement|Download design"
msgstr "下載設計"
@@ -13215,6 +13312,9 @@ msgstr "DevOps 報告"
msgid "DevOps adoption"
msgstr "DevOps adoption"
+msgid "Development"
+msgstr ""
+
msgid "Devices (optional)"
msgstr "設備(å¯é¸ï¼‰"
@@ -13771,6 +13871,9 @@ msgstr "è‰ç¨¿"
msgid "Draft: %{filename}"
msgstr "è‰ç¨¿ï¼š%{filename}"
+msgid "Drag to reorder prioritized labels and change their relative priority."
+msgstr "拖動以é‡æ–°æŽ’列優先標籤並更改它們的相å°å„ªå…ˆåºã€‚"
+
msgid "Drag your designs here or %{linkStart}click to upload%{linkEnd}."
msgstr "將您的設計拖到此處或 %{linkStart} 點擊上傳 %{linkEnd}。"
@@ -13792,6 +13895,12 @@ msgstr "å–得所é¸%{issuableType}çš„%{issuableAttribute}時發生錯誤。"
msgid "DropdownWidget|Assign %{issuableAttribute}"
msgstr "分派 %{issuableAttribute}"
+msgid "DropdownWidget|Cancel"
+msgstr ""
+
+msgid "DropdownWidget|Edit %{issuableAttribute}"
+msgstr ""
+
msgid "DropdownWidget|Failed to fetch the %{issuableAttribute} for this %{issuableType}. Please try again."
msgstr "無法為此%{issuableType}å–å¾—%{issuableAttribute},請é‡è©¦ã€‚"
@@ -13807,6 +13916,12 @@ msgstr "未找到 %{issuableAttribute}"
msgid "DropdownWidget|No open %{issuableAttribute} found"
msgstr "找ä¸åˆ°é–‹æ”¾çš„ %{issuableAttribute}"
+msgid "DropdownWidget|You do not have permission to view the currently assigned %{issuableAttribute} and will not be able to choose it again if you reassign it."
+msgstr ""
+
+msgid "DropdownWidget|You don't have permission to view this %{issuableAttribute}."
+msgstr ""
+
msgid "Due Date"
msgstr "截止日期"
@@ -13916,7 +14031,7 @@ msgid "Edit Password"
msgstr "編輯密碼"
msgid "Edit Pipeline Schedule"
-msgstr "編輯æµæ°´ç·šè¨ˆåŠƒ"
+msgstr "編輯æµæ°´ç·šæŽ’程"
msgid "Edit Release"
msgstr "編輯發佈"
@@ -14029,6 +14144,9 @@ msgstr "編輯Wikié é¢"
msgid "Edit your most recent comment in a thread (from an empty textarea)"
msgstr "編輯你在最近話題中的留言(從空白文字å€)"
+msgid "Edit your search filter and try again."
+msgstr "編輯您的查詢éŽæ¿¾å™¨ä¸¦é‡è©¦ã€‚"
+
msgid "Edit, lint, and visualize your pipeline."
msgstr "編輯ã€æ•´ç†å’Œå¯è¦–化您的æµæ°´ç·šã€‚"
@@ -14044,9 +14162,6 @@ msgstr "編輯中"
msgid "Elapsed time"
msgstr "已經éŽæ™‚é–“"
-msgid "Elasticsearch AWS IAM credentials"
-msgstr "Elasticsearch AWS IAM 憑證"
-
msgid "Elasticsearch HTTP client timeout value in seconds."
msgstr "Elasticsearch HTTP 客戶端超時數值(以秒為單ä½ï¼‰ã€‚"
@@ -14071,9 +14186,6 @@ msgstr "Elasticsearch é‡å»ºç´¢å¼•å°šæœªé–‹å§‹ï¼š%{errors}"
msgid "Elasticsearch zero-downtime reindexing"
msgstr "Elasticsearch ä¸åœæ©Ÿé‡å»ºç´¢å¼•ä¸­"
-msgid "Elasticsearch's region."
-msgstr "Elasticsearch çš„å€åŸŸã€‚"
-
msgid "Elastic|None. Select namespaces to index."
msgstr "無。請é¸æ“‡è¦å»ºç«‹ç´¢å¼•çš„命å空間。"
@@ -14221,6 +14333,9 @@ msgstr "啟用 Auto DevOps"
msgid "Enable GitLab Error Tracking"
msgstr "啟用 GitLab 錯誤追踪"
+msgid "Enable GitLab Prometheus metrics endpoint"
+msgstr ""
+
msgid "Enable Gitpod"
msgstr "啟用 Gitpod"
@@ -14291,7 +14406,7 @@ msgid "Enable email notification"
msgstr "啟用電å­éƒµä»¶é€šçŸ¥"
msgid "Enable error tracking"
-msgstr "啟用錯誤跟蹤"
+msgstr "啟用錯誤追蹤"
msgid "Enable feature to choose access level"
msgstr "啟用功能以é¸æ“‡å­˜å–級別"
@@ -14305,9 +14420,6 @@ msgstr "啟用群組執行器"
msgid "Enable header and footer in emails"
msgstr "在電å­éƒµä»¶ä¸­å•Ÿç”¨é é¦–å’Œé å°¾"
-msgid "Enable health and performance metrics endpoint"
-msgstr "啟用é‹è¡Œç‹€æ³å’Œæ€§èƒ½æŒ‡æ¨™ç«¯é»ž"
-
msgid "Enable in-product marketing emails"
msgstr "接收產å“營銷電å­éƒµä»¶"
@@ -14749,6 +14861,9 @@ msgstr "å²è©©"
msgid "Epic Boards"
msgstr "å²è©©çœ‹æ¿"
+msgid "Epic actions"
+msgstr "å²è©©å‹•ä½œ"
+
msgid "Epic cannot be found."
msgstr "找ä¸åˆ°å²è©©ã€‚"
@@ -14785,12 +14900,6 @@ msgstr "加入一個新的å²è©©ã€‚"
msgid "Epics|Add an existing epic"
msgstr "加入一個ç¾æœ‰çš„å²è©©ã€‚"
-msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr "ä¿å­˜ %{epicDateType} 日期時發生錯誤"
-
-msgid "Epics|An error occurred while updating labels."
-msgstr "更新標記時出錯。"
-
msgid "Epics|Are you sure you want to remove %{bStart}%{targetIssueTitle}%{bEnd} from %{bStart}%{parentEpicTitle}%{bEnd}?"
msgstr "確定è¦å¾ž%{bStart}%{parentEpicTitle}%{bEnd}刪除%{bStart}%{targetIssueTitle}%{bEnd}å—Ž?"
@@ -14857,18 +14966,9 @@ msgstr "這個å²è©©å’Œä»»ä½•åŒ…å«å­å²è©©çš„訊æ¯å‡ç‚ºç§å¯†ï¼Œåªèƒ½å°æ“
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}的所有級別å­å²è©©ã€‚確定繼續嗎?"
-msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr "如需根據里程碑來安排å²è©©çš„ %{epicDateType} 日期,請為å²è©©ä¸­çš„任一議題指定帶有 %{epicDateType} 日期的里程碑。"
-
msgid "Epics|Unable to save epic. Please try again"
msgstr "無法ä¿å­˜å²è©©ã€‚è«‹é‡è©¦"
-msgid "Epics|due"
-msgstr "到期"
-
-msgid "Epics|start"
-msgstr "開始"
-
msgid "Erased"
msgstr "已刪除"
@@ -14879,7 +14979,7 @@ msgid "Error Details"
msgstr "錯誤詳情"
msgid "Error Tracking"
-msgstr "錯誤跟蹤"
+msgstr "錯誤追蹤"
msgid "Error creating epic"
msgstr "建立 å²è©© 時發生錯誤"
@@ -14905,6 +15005,9 @@ msgstr "建立æ¼æ´žæœå°‹æ™‚發生錯誤:%{errors}"
msgid "Error deleting project. Check logs for error details."
msgstr "刪除專案時發生錯誤。請檢視日誌以å–得錯誤詳細資訊。"
+msgid "Error fetching branches"
+msgstr ""
+
msgid "Error fetching burnup chart data"
msgstr "æå–燃起圖資料時發生錯誤"
@@ -14938,9 +15041,6 @@ msgstr "載入分支時發生錯誤。"
msgid "Error loading burndown chart data"
msgstr "載入燃盡圖資料時發生錯誤"
-msgid "Error loading countries data."
-msgstr "載入國家/地å€è³‡æ–™æ™‚發生錯誤。"
-
msgid "Error loading file viewer."
msgstr "載入文件查看器時發生錯誤。"
@@ -15016,6 +15116,9 @@ msgstr "發生了錯誤。使用者未解除鎖定"
msgid "Error parsing CSV file. Please make sure it has"
msgstr "è§£æž CSV 檔案時出錯。請確ä¿å®ƒæœ‰"
+msgid "Error promoting the note to timeline event: %{error}"
+msgstr "將備註æå‡è‡³æ™‚間線事件時發生錯誤:%{error}"
+
msgid "Error rendering Markdown preview"
msgstr "查看 Markdown é è¦½æ™‚生錯誤"
@@ -15446,6 +15549,9 @@ msgstr "展開議題"
msgid "Expand jobs"
msgstr "展開作業"
+msgid "Expand merge details"
+msgstr "展開åˆä½µè©³ç´°è³‡è¨Š"
+
msgid "Expand milestones"
msgstr "展開里程碑"
@@ -15848,7 +15954,7 @@ msgid "Failed to move this issue because label was not found."
msgstr "無法移動此議題,因為相關標記ä¸å­˜åœ¨ã€‚"
msgid "Failed to move this issue because only a single label can be provided."
-msgstr "無法移動此議題,因為åªèƒ½åªå¯æ供一個標記。"
+msgstr "無法移動此議題,因為åªèƒ½æ供一個標記。"
msgid "Failed to move this issue because target project doesn't exist."
msgstr "無法移動此議題,因為目標專案ä¸å­˜åœ¨ã€‚"
@@ -15878,7 +15984,7 @@ msgid "Failed to remove mirror."
msgstr "移除é¡åƒå¤±æ•—。"
msgid "Failed to remove the pipeline schedule"
-msgstr "無法刪除æµæ°´ç·šè¨ˆåŠƒ"
+msgstr "無法刪除æµæ°´ç·šæŽ’程"
msgid "Failed to remove timelog"
msgstr "無法移除時間日誌"
@@ -15971,13 +16077,13 @@ 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"
@@ -16193,9 +16299,6 @@ msgstr "2月"
msgid "February"
msgstr "2月"
-msgid "Feedback issue"
-msgstr "å•é¡Œå›žé¥‹"
-
msgid "Fetch and check out this merge request's feature branch:"
msgstr "å–得並簽出此åˆä½µè«‹æ±‚的功能分支:"
@@ -16406,14 +16509,14 @@ msgstr "固定:"
msgid "Flags"
msgstr "旗幟"
-msgid "FloC|Configure whether you want to participate in FloC."
-msgstr "FloC|設定您是å¦æƒ³åƒèˆ‡ FloC。"
+msgid "FloC|Configure whether you want to participate in FLoC. %{floc_link_start}What is FLoC?%{floc_link_end}"
+msgstr ""
-msgid "FloC|Enable FloC (Federated Learning of Cohorts)"
-msgstr "啟用 FloC(Federated Learning of Cohorts)"
+msgid "FloC|Federated Learning of Cohorts (FLoC)"
+msgstr ""
-msgid "FloC|Federated Learning of Cohorts"
-msgstr "FloC(Federated Learning of Cohorts)"
+msgid "FloC|Participate in FLoC"
+msgstr ""
msgid "FlowdockService|Enter your Flowdock token."
msgstr "輸入您的 Flowdock 令牌(權æ–)"
@@ -16562,6 +16665,9 @@ msgstr "公開"
msgid "ForkProject|Select a namespace"
msgstr "é¸æ“‡ä¸€å€‹å‘½å空間"
+msgid "ForkProject|Something went wrong while loading data. Please refresh the page to try again."
+msgstr "載入資料時發生錯誤,請刷新é é¢é‡è©¦ã€‚"
+
msgid "ForkProject|The project can be accessed by any logged in user."
msgstr "任何登入的使用者都å¯ä»¥å­˜å–該專案。"
@@ -16629,6 +16735,9 @@ msgstr "頻率"
msgid "Frequently searched"
msgstr "經常æœå°‹çš„"
+msgid "Fri"
+msgstr "星期五"
+
msgid "Friday"
msgstr "星期五"
@@ -16641,13 +16750,9 @@ msgstr "從%{code_open}%{source_title}%{code_close}到"
msgid "From %{providerTitle}"
msgstr "%{providerTitle}來æºåœ°å€"
-msgid "From October 19, 2022, free groups will be limited to %d member"
-msgid_plural "From October 19, 2022, free groups will be limited to %d members"
-msgstr[0] "從 2022 å¹´ 10 月 19 日起,å…費群組將é™åˆ¶ç‚º %d åæˆå“¡"
-
-msgid "From October 19, 2022, you can have a maximum of %d unique member across all of your personal projects"
-msgid_plural "From October 19, 2022, you can have a maximum of %d unique members across all of your personal projects"
-msgstr[0] "從 2022 å¹´ 10 月 19 日起,您個人所有專案中最多å¯ä»¥æ“有 %d ä½å”¯ä¸€æˆå“¡"
+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 "從建立議題到部署至正å¼ç’°å¢ƒ"
@@ -16680,7 +16785,7 @@ msgid "GPG signature (loading...)"
msgstr "GPGç°½å(載入中...)"
msgid "General"
-msgstr "通用"
+msgstr "一般"
msgid "General Settings"
msgstr "一般設定"
@@ -16710,7 +16815,7 @@ msgid "Generated with JSON data"
msgstr "使用JSON 資料生æˆ"
msgid "Generic"
-msgstr "通用"
+msgstr "一般"
msgid "Generic package file size in bytes"
msgstr "通用文件包大å°ï¼ˆå­—節)"
@@ -16786,9 +16891,6 @@ msgstr "新增站點"
msgid "Geo|Add site"
msgstr "加入站點"
-msgid "Geo|Adjust your filters/search criteria above. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
-msgstr "請您調整上é¢çš„éŽæ¿¾å™¨/æœå°‹æ¢ä»¶ã€‚如果您èªç‚ºæ­¤è™•æœ‰èª¤ï¼Œè«‹åƒé–± %{linkStart}Geo Troubleshow%{linkEnd} 文件以å–得更多訊æ¯ã€‚"
-
msgid "Geo|All"
msgstr "全部"
@@ -16984,6 +17086,12 @@ msgstr "從ä¸"
msgid "Geo|Next sync scheduled at"
msgstr "下一次åŒæ­¥å®‰æŽ’在"
+msgid "Geo|No %{replicable_type} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr "æœªç™¼ç¾ %{replicable_type}。如果您èªç‚ºé€™å¯èƒ½æ˜¯ä¸€å€‹éŒ¯èª¤ï¼Œè«‹åƒè€ƒ %{linkStart}Geo 故障排除%{linkEnd} 文件以了解更多資訊。"
+
+msgid "Geo|No %{replicable} were found. If you believe this may be an error, please refer to the %{linkStart}Geo Troubleshooting%{linkEnd} documentation for more information."
+msgstr "æœªç™¼ç¾ %{replicable}。如果您èªç‚ºé€™å¯èƒ½æ˜¯ä¸€å€‹éŒ¯èª¤ï¼Œè«‹åƒè€ƒ %{linkStart}Geo 故障排除%{linkEnd} 文件以了解更多資訊。"
+
msgid "Geo|No Geo site found"
msgstr "未發ç¾Geo站點"
@@ -17191,6 +17299,9 @@ msgstr "站點當å‰ä½æ–¼ä¸»ç«™é»žå¾Œé¢ %{minutes_behind}。"
msgid "Geo|There are no %{replicable_type} to show"
msgstr "沒有%{replicable_type}å¯é¡¯ç¤º"
+msgid "Geo|There are no %{replicable} to show"
+msgstr "沒有å¯é¡¯ç¤ºçš„%{replicable} "
+
msgid "Geo|There was an error deleting the Geo Site"
msgstr "刪除Geo站點時發生錯誤"
@@ -17303,7 +17414,7 @@ msgid "Get started with GitLab"
msgstr "開始使用 GitLab"
msgid "Get started with error tracking"
-msgstr "開始使用錯誤跟蹤"
+msgstr "開始使用錯誤追蹤"
msgid "Get started with performance monitoring"
msgstr "開始進行效能監控"
@@ -17353,6 +17464,9 @@ msgstr "正在進行 Git 傳輸"
msgid "Git version"
msgstr "Git 版本"
+msgid "GitAbuse|Automatically ban users from this %{scope} when they exceed the specified limits"
+msgstr "當使用者超éŽæŒ‡å®šé™åˆ¶æ™‚,自動å°éŽ–該 %{scope} 使用者"
+
msgid "GitAbuse|Excluded users"
msgstr "已排除的使用者"
@@ -17722,6 +17836,9 @@ msgstr "此範åœçš„全域æœå°‹å·²ç¦ç”¨"
msgid "Global Shortcuts"
msgstr "全域快æ·éµ"
+msgid "Global notification level"
+msgstr ""
+
msgid "Global notification settings"
msgstr "全域通知設定"
@@ -17734,6 +17851,12 @@ msgstr "æä¾› %{count} 個é è¨­çµæžœï¼Œä½¿ç”¨ä¸Šä¸‹ç®­é ­éµå°Žèˆªæœå°‹çµæž
msgid "GlobalSearch|Groups"
msgstr "群組"
+msgid "GlobalSearch|Help"
+msgstr "幫助"
+
+msgid "GlobalSearch|In this project"
+msgstr "在該專案內"
+
msgid "GlobalSearch|Issues I've created"
msgstr "我建立的議題"
@@ -17752,6 +17875,15 @@ msgstr "我作為審核者的åˆä½µè«‹æ±‚"
msgid "GlobalSearch|Projects"
msgstr "專案"
+msgid "GlobalSearch|Recent epics"
+msgstr "近期的å²è©©"
+
+msgid "GlobalSearch|Recent issues"
+msgstr "近期的議題"
+
+msgid "GlobalSearch|Recent merge requests"
+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 éµæ交。"
@@ -17764,6 +17896,9 @@ msgstr "æœå°‹å°ˆæ¡ˆã€è­°é¡Œç­‰ã€‚"
msgid "GlobalSearch|Search results are loading"
msgstr "æœå°‹çµæžœæ­£åœ¨è¼‰å…¥ä¸­"
+msgid "GlobalSearch|Settings"
+msgstr "設定"
+
msgid "GlobalSearch|There was an error fetching search autocomplete suggestions."
msgstr "å–å¾—æœå°‹è‡ªå‹•å®Œæˆå»ºè­°æ™‚發生錯誤。"
@@ -17791,6 +17926,12 @@ msgstr "在 %{scope}"
msgid "GlobalSearch|project"
msgstr "專案"
+msgid "GlobalShortcuts|Copied source branch name to clipboard."
+msgstr ""
+
+msgid "GlobalShortcuts|Unable to copy the source branch name at this time."
+msgstr ""
+
msgid "Globally-allowed IP ranges"
msgstr "å…¨çƒå…許的 IP 範åœ"
@@ -18061,9 +18202,6 @@ msgstr "啟用 Gravatar"
msgid "Group"
msgstr "群組"
-msgid "Group \"%{group_name}\" was successfully updated."
-msgstr "群組 \"%{group_name}\" å·²æˆåŠŸæ›´æ–°ã€‚"
-
msgid "Group %{group_name} couldn't be exported."
msgstr "無法匯出群組 %{group_name}。"
@@ -18499,9 +18637,6 @@ msgstr "應用於所有å­ç¾¤çµ„,除éžè¢«ç¾¤çµ„所有者覆寫。已加到專
msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
msgstr "已為群組更新 Auto DevOps æµæ°´ç·š"
-msgid "GroupSettings|Automatically ban users who download more than a specified number of repositories in a given time."
-msgstr "自動å°éŽ–在給定時間內下載超éŽæŒ‡å®šæ•¸é‡çš„版本庫的使用者。"
-
msgid "GroupSettings|Available only on the top-level group. Applies to all subgroups. Groups already shared with a group outside %{group} are still shared unless removed manually."
msgstr "GroupSettings|僅在頂級群組上å¯ç”¨ã€‚é©ç”¨æ–¼æ‰€æœ‰å­ç¾¤çµ„。除éžæ‰‹å‹•ç§»é™¤ï¼Œå¦å‰‡å·²èˆ‡ %{group} 以外的群組共享的群組ä»æœƒå…±äº«ã€‚"
@@ -18529,6 +18664,9 @@ msgstr "åˆè¦æ¡†æž¶"
msgid "GroupSettings|Configure compliance frameworks to make them available to projects in this group. %{linkStart}Learn more.%{linkEnd}"
msgstr "設定åˆè¦æ¡†æž¶ä»¥æ供給此群組中的專案。 %{linkStart}瞭解更多訊æ¯ã€‚%{linkEnd}"
+msgid "GroupSettings|Configure limits on the number of repositories users can download in a given time."
+msgstr "設定使用者在給定時間內å¯ä»¥ä¸‹è¼‰çš„版本庫數é‡é™åˆ¶ã€‚"
+
msgid "GroupSettings|Custom project templates"
msgstr "自訂專案範本"
@@ -18677,10 +18815,10 @@ 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 "載入群組中"
@@ -18892,14 +19030,17 @@ msgstr "åƒè€ƒ"
msgid "HAR (HTTP Archive)"
msgstr "HAR(HTTP 存檔)"
+msgid "HAR file URL"
+msgstr "HAR 文件 URL"
+
msgid "HAR file path or URL"
msgstr "HAR 文件路徑或 URL"
msgid "HTTP Archive (HAR)"
msgstr "HTTP 存檔 (HAR)"
-msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
-msgstr "HTTP Basic:存å–被拒絕\\n您必須使用具有'api'權é™çš„個人存å–令牌(權æ–)。\\n您å¯ä»¥åœ¨ %{profile_personal_access_tokens_url}中生æˆä¸€å€‹"
+msgid "HTTP Basic: Access denied. The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See %{help_page_url}"
+msgstr "基於 HTTP:存å–被拒絕。æ供的密碼或令牌ä¸æ­£ç¢ºï¼Œæˆ–者您的帳戶啟用了 2FA,您必須使用個人存å–令牌而ä¸æ˜¯å¯†ç¢¼ã€‚è«‹åƒé–± %{help_page_url}"
msgid "Harbor Registry"
msgstr "Harbor 映åƒåº«"
@@ -18937,16 +19078,16 @@ msgstr "Harbor 中的專案å稱。"
msgid "HarborIntegration|Use Harbor as this project's container registry."
msgstr "使用 Harbor 作為該專案的容器映åƒåº«ã€‚"
+msgid "HarborRegistry|%d artifact"
+msgid_plural "HarborRegistry|%d artifacts"
+msgstr[0] "%d 個產物"
+
msgid "HarborRegistry|%{count} Image repository"
msgid_plural "HarborRegistry|%{count} Image repositories"
msgstr[0] "%{count} 個映åƒå€‰åº«"
-msgid "HarborRegistry|%{count} Tag"
-msgid_plural "HarborRegistry|%{count} Tags"
-msgstr[0] "%{count} 個標籤"
-
-msgid "HarborRegistry|Configuration digest: %{digest}"
-msgstr "é…置摘è¦: %{digest}"
+msgid "HarborRegistry|-- artifacts"
+msgstr "-- 產物"
msgid "HarborRegistry|Digest: %{imageId}"
msgstr "摘è¦: %{imageId}"
@@ -18957,44 +19098,38 @@ msgstr "Harbor Registry"
msgid "HarborRegistry|Harbor connection error"
msgstr "Harbor 連接錯誤"
-msgid "HarborRegistry|Invalid tag: missing manifest digest"
-msgstr "無效標籤:缺少 manifest 摘è¦"
-
-msgid "HarborRegistry|Last updated %{time}"
-msgstr "最近更新於%{time}"
-
-msgid "HarborRegistry|Manifest digest: %{digest}"
-msgstr "Manifest 摘è¦: %{digest}"
-
msgid "HarborRegistry|Please try different search criteria"
msgstr "請嘗試ä¸åŒçš„æœå°‹æ¢ä»¶"
msgid "HarborRegistry|Published %{timeInfo}"
msgstr "發布於%{timeInfo}"
-msgid "HarborRegistry|Published to the %{repositoryPath} image repository at %{time} on %{date}"
-msgstr "æ–¼%{date}%{time}發布到%{repositoryPath}映åƒå€‰åº«ã€‚"
-
msgid "HarborRegistry|Root image"
msgstr "HarborRegistry|根映åƒæª”"
+msgid "HarborRegistry|Something went wrong while fetching the artifact list."
+msgstr "讀å–產物列表時發生錯誤。"
+
+msgid "HarborRegistry|Something went wrong while fetching the repository list."
+msgstr "讀å–版本庫列表時發生錯誤。"
+
+msgid "HarborRegistry|Something went wrong while fetching the tags."
+msgstr "讀å–標籤時發生錯誤。"
+
msgid "HarborRegistry|Sorry, your filter produced no results."
msgstr "HarborRegistry|抱歉,您的éŽæ¿¾å™¨æ²’有產生任何çµæžœã€‚"
+msgid "HarborRegistry|Tag"
+msgstr "標籤"
+
msgid "HarborRegistry|The filter returned no results"
msgstr "HarborRegistry|éŽæ¿¾å™¨æœªè¿”回任何çµæžœ"
-msgid "HarborRegistry|The image repository could not be found."
-msgstr "HarborRegistry|找ä¸åˆ°æ˜ åƒæª”版本庫。"
-
-msgid "HarborRegistry|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 "與此映åƒç›¸é—œçš„最後一個標籤最近已被刪除。空映åƒå’Œæ‰€æœ‰ç›¸é—œè³‡æ–™å°‡ä½œç‚ºå¸¸è¦åžƒåœ¾æ”¶é›†éŽç¨‹çš„一部分自動清除。如有任何疑å•ï¼Œè«‹èˆ‡ç®¡ç†å“¡è¯ç¹«ã€‚"
+msgid "HarborRegistry|There are no harbor images stored for this project"
+msgstr "該專案沒有已儲存的 harbor 映åƒæª”"
-msgid "HarborRegistry|The requested image repository does not exist or has been deleted. If you think this is an error, try refreshing the page."
-msgstr "請求的映åƒå€‰åº«ä¸å­˜åœ¨æˆ–已被刪除。如果您èªç‚ºé€™æ˜¯ä¸€å€‹éŒ¯èª¤ï¼Œè«‹å˜—試é‡æ–°æ•´ç†é é¢ã€‚"
-
-msgid "HarborRegistry|This image has no active tags"
-msgstr "此映åƒæª”沒有有效標籤"
+msgid "HarborRegistry|This image has no artifacts"
+msgstr "該映åƒæª”沒有產物"
msgid "HarborRegistry|To widen your search, change or remove the filters above."
msgstr "è¦æ“´å¤§æœå°‹ç¯„åœï¼Œè«‹æ›´æ”¹æˆ–移除上é¢çš„éŽæ¿¾å™¨ã€‚"
@@ -19002,6 +19137,9 @@ 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 troubleshooting documentation%{docLinkEnd}."
msgstr "連接到 Harbor Registry 發生錯誤。請嘗試é‡æ–°æ•´ç†é é¢ã€‚如果此錯誤ä»ç„¶å­˜åœ¨ï¼Œè«‹æŸ¥çœ‹ %{docLinkStart}故障排查文件%{docLinkEnd}。"
+msgid "HarborRegistry|With the Harbor Registry, every project can connect to a harbor space to store its Docker images."
+msgstr "使用 Harbor Registry,æ¯å€‹å°ˆæ¡ˆéƒ½å¯ä»¥é€£æŽ¥åˆ° Harbor 空間來儲存其 Docker 映åƒã€‚"
+
msgid "HarborRegistry|With the Harbor Registry, every project can have its own space to store images. %{docLinkStart}More information%{docLinkEnd}"
msgstr "使用 Harbor Registry,æ¯å€‹å°ˆæ¡ˆéƒ½æœ‰è‡ªå·±çš„空間來儲存容器映åƒã€‚%{docLinkStart}更多訊æ¯%{docLinkEnd}"
@@ -19147,6 +19285,9 @@ msgid "Hide chart"
msgid_plural "Hide charts"
msgstr[0] "éš±è—圖表"
+msgid "Hide comments"
+msgstr ""
+
msgid "Hide comments on this file"
msgstr "éš±è—在此檔案的留言"
@@ -19277,9 +19418,6 @@ msgstr "已開始維護"
msgid "How do I configure Akismet?"
msgstr "如何é…ç½® Akismet ?"
-msgid "How do I configure runners?"
-msgstr "如何é…置執行器?"
-
msgid "How do I configure this integration?"
msgstr "如何設定此整åˆï¼Ÿ"
@@ -19313,6 +19451,9 @@ msgstr "IP 計入 IP 地å€é™åˆ¶çš„秒數。"
msgid "How the job limiter handles jobs exceeding the thresholds specified below. The 'track' mode only logs the jobs. The 'compress' mode compresses the jobs and raises an exception if the compressed size exceeds the limit."
msgstr "作業é™è£½å™¨å¦‚何處ç†è¶…出以下指定閾值的作業。 'track'模å¼åªè¨˜éŒ„作業。'compress'模å¼å£“縮作業並在壓縮大å°è¶…éŽé™åˆ¶æ™‚引發異常。"
+msgid "How to track time"
+msgstr ""
+
msgid "I accept the %{terms_link}"
msgstr "æˆ‘æŽ¥å— %{terms_link}"
@@ -19436,9 +19577,15 @@ msgstr "在您登入之å‰ï¼Œæˆ‘們需è¦é©—證您的身份,請在登入é é¢
msgid "IdentityVerification|Create a project"
msgstr "建立專案"
+msgid "IdentityVerification|For added security, you'll need to verify your identity in a few quick steps."
+msgstr ""
+
msgid "IdentityVerification|For added security, you'll need to verify your identity. We've sent a verification code to %{email}"
msgstr "為了增加安全性,您需è¦é©—證您的身份。我們已發é€é©—證碼到 %{email}"
+msgid "IdentityVerification|Help us keep GitLab secure"
+msgstr ""
+
msgid "IdentityVerification|Help us protect your account"
msgstr "幫助我們ä¿è­·æ‚¨çš„帳號"
@@ -19451,15 +19598,36 @@ msgstr "如果您最近沒有åšè©¦ç™»å…¥ GitLab,我們建議更改您的密ç¢
msgid "IdentityVerification|If you've lost access to the email associated to this account or having trouble with the code, %{link_start}here are some other steps you can take.%{link_end}"
msgstr "如果您無法存å–與該帳號關è¯çš„é›»å­éƒµä»¶æˆ–驗證碼有å•é¡Œï¼Œ %{link_start}這裡是您å¯ä»¥æŽ¡å–的其他一些步驟。%{link_end}"
+msgid "IdentityVerification|International dial code"
+msgstr ""
+
msgid "IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again."
msgstr "已超éŽæœ€å¤§ç™»å…¥å˜—試次數。等待 %{interval} 後å†é‡è©¦ã€‚"
+msgid "IdentityVerification|Phone number"
+msgstr ""
+
+msgid "IdentityVerification|Phone number can't be blank."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must be %{maxLength} digits or fewer."
+msgstr ""
+
+msgid "IdentityVerification|Phone number must contain only digits."
+msgstr ""
+
msgid "IdentityVerification|Please enter a valid code"
msgstr "請輸入有效的驗證碼"
msgid "IdentityVerification|Resend code"
msgstr "é‡æ–°ç™¼é€é©—證碼"
+msgid "IdentityVerification|Send code"
+msgstr ""
+
+msgid "IdentityVerification|Step 1: Verify phone number"
+msgstr ""
+
msgid "IdentityVerification|The code has expired. Resend a new code and try again."
msgstr "驗證碼已éŽæœŸï¼Œé‡æ–°ç™¼é€æ–°é©—證碼並é‡è©¦ã€‚"
@@ -19481,6 +19649,9 @@ msgstr "驗證您的身份"
msgid "IdentityVerification|You can always verify your account at a later time to create a group."
msgstr "您å¯ä»¥ç¨å¾Œéš¨æ™‚驗證您的帳號,來建立一個群組。"
+msgid "IdentityVerification|You will receive a text containing a code. Standard charges may apply."
+msgstr ""
+
msgid "IdentityVerification|You've reached the maximum amount of tries. Wait %{interval} or resend a new code and try again."
msgstr "您已é”到最大嘗試次數,等待 %{interval} 或é‡æ–°ç™¼é€æ–°é©—證碼並é‡è©¦ã€‚"
@@ -19550,9 +19721,6 @@ msgstr "如使用 GitHub,GitHub çš„æ交 (commits) å’Œæå–è¦æ±‚ (pull requ
msgid "If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}."
msgstr "如果將%{codeStart}needs%{codeEnd}加到æµæ°´ç·šçš„作業裡é¢ï¼Œæ‚¨å°‡å¯ä»¥åœ¨%{linkStart}有å‘無環圖 (DAG)%{linkEnd}é é¢çœ‹åˆ°ä½œæ¥­ä¹‹é–“çš„%{codeStart}needs%{codeEnd}關係。"
-msgid "If you are added to a project, it will be displayed here."
-msgstr "如果您被加入到專案中,將顯示在此處。"
-
msgid "If you did not initiate these sign-in attempts, please reach out to your administrator or enable two-factor authentication (2FA) on your account."
msgstr "如果您沒有發起這些登入嘗試,請è¯ç¹«æ‚¨çš„管ç†å“¡æˆ–在您的帳號上啟用雙é‡èº«ä»½é©—證(2FA)。"
@@ -19730,7 +19898,7 @@ msgid "Import timed out. Import took longer than %{import_jobs_expiration} secon
msgstr "åŒ¯å…¥è¶…æ™‚ã€‚è€—æ™‚å·²è¶…éŽ %{import_jobs_expiration} 秒"
msgid "ImportAProjectModal|Import from a project"
-msgstr "從一個專案匯入"
+msgstr "從其他專案匯入"
msgid "ImportAProjectModal|Import members from another project"
msgstr "從å¦ä¸€å€‹å°ˆæ¡ˆåŒ¯å…¥æˆå“¡"
@@ -19808,9 +19976,6 @@ msgstr "在此 URL 上沒有有效的 Git 倉庫。如果您的 HTTP 倉庫ä¸èƒ
msgid "Improve customer support with Service Desk"
msgstr "通éŽæœå‹™å°æ”¹å–„客戶支æ´"
-msgid "In a seat"
-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 "在拉å–映åƒçš„情æ³ä¸‹ï¼Œæ‚¨çš„使用者將æˆç‚ºæ´»å‹•æè¦ä¸­æ‰€æœ‰ä½œç‚ºæ›´æ–°çµæžœçš„事件作者,例如建立新分支或將新æ交推é€åˆ°ç¾æœ‰åˆ†æ”¯ã€‚"
@@ -20430,7 +20595,7 @@ msgid "IncidentManagement|Date created"
msgstr "建立日期"
msgid "IncidentManagement|Display your incidents in a dedicated view"
-msgstr "在專用視圖中顯示您的事件"
+msgstr "在專用視圖中顯示您的事故"
msgid "IncidentManagement|High - S2"
msgstr "高 - S2"
@@ -20600,6 +20765,9 @@ msgstr "建立事故時間線事件發生錯誤:%{error}"
msgid "Incident|Error deleting incident timeline event: %{error}"
msgstr "刪除事故時間線事件發生錯誤:%{error}"
+msgid "Incident|Error updating incident timeline event: %{error}"
+msgstr "更新事故時間線事件時發生錯誤:%{error}"
+
msgid "Incident|Metrics"
msgstr "指標"
@@ -20618,6 +20786,9 @@ msgstr "刪除事故時間線事件時發生錯誤。"
msgid "Incident|Something went wrong while fetching incident timeline events."
msgstr "æå–事故時間線事件時發生錯誤。"
+msgid "Incident|Something went wrong while updating the incident timeline event."
+msgstr "更新事故時間線事件時發生錯誤。"
+
msgid "Incident|Summary"
msgstr "摘è¦"
@@ -20651,8 +20822,8 @@ msgstr "在郵件正文中包括議題ã€åˆä½µè«‹æ±‚或評論的作者å稱。
msgid "Include the username in the URL if required: %{code_open}https://username@gitlab.company.com/group/project.git%{code_close}."
msgstr "如果需è¦ï¼Œè«‹åœ¨URL中包å«ä½¿ç”¨è€…å:%{code_open}https://username@gitlab.company.com/group/project.git%{code_close}。"
-msgid "Includes LFS objects. It can be overridden per group, or per project. 0 for unlimited."
-msgstr "åŒ…å« LFS 物件。它å¯ä»¥æŒ‰ç¾¤çµ„或項目覆蓋。0 表示無é™åˆ¶ã€‚"
+msgid "Includes LFS objects. It can be overridden per group, or per project. Set to 0 for no limit."
+msgstr ""
msgid "Includes an MVC structure to help you get started"
msgstr "åŒ…å« MVC çµæ§‹ä»¥å¹«åŠ©æ‚¨å…¥é–€"
@@ -20792,9 +20963,15 @@ msgstr "上方æ’入行"
msgid "Insert suggestion"
msgstr "æ’入建議"
+msgid "Insert table"
+msgstr "æ’入表格"
+
msgid "Insights"
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 "設定有關團隊æµç¨‹çš„自定義洞察報告,例如æ¯æœˆçš„è­°é¡Œã€éŒ¯èª¤èˆ‡åˆä½µè«‹æ±‚數é‡ã€‚ %{linkStart}如何設定洞察報告?%{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 "有些æ¢ç›®ä¸å¯è¦‹ï¼Œç”±æ–¼å·²åœ¨insights.yml文件中éŽæ¿¾æŽ‰(見projects.onlyé…置以瞭解更多訊æ¯)。"
@@ -21063,6 +21240,9 @@ msgstr "將有關專案事件的通知發é€åˆ° Unify Circuit。"
msgid "Integrations|Send notifications about project events to a Unify Circuit conversation. %{docs_link}"
msgstr "將有關專案事件的通知發é€åˆ° Unify Circuit å°è©±ã€‚ %{docs_link}"
+msgid "Integrations|Sign in to %{url}"
+msgstr ""
+
msgid "Integrations|Sign in to GitLab"
msgstr "登入到 GitLab"
@@ -21375,6 +21555,9 @@ msgstr "管ç†æˆå“¡"
msgid "InviteMembersModal|Members were successfully added"
msgstr "æˆå“¡å·²æˆåŠŸåŠ å…¥"
+msgid "InviteMembersModal|Please select members or type email addresses to invite"
+msgstr "è«‹é¸æ“‡è¦é‚€è«‹çš„æˆå“¡æˆ–輸入電å­éƒµä»¶åœ°å€"
+
msgid "InviteMembersModal|Review the invite errors and try again:"
msgstr "查看邀請錯誤並é‡è©¦ï¼š"
@@ -21390,6 +21573,12 @@ msgstr "é¸æ“‡è§’色"
msgid "InviteMembersModal|Select members or type email addresses"
msgstr "é¸æ“‡æˆå“¡æˆ–輸入電å­éƒµä»¶åœ°å€"
+msgid "InviteMembersModal|Show less"
+msgstr "較少顯示"
+
+msgid "InviteMembersModal|Show more (%{count})"
+msgstr "顯示更多 (%{count})"
+
msgid "InviteMembersModal|Something went wrong"
msgstr "出ç¾éŒ¯èª¤"
@@ -21406,9 +21595,6 @@ msgstr "è¦å‘新團隊æˆå“¡åˆ†é…議題,您需è¦ä¸€å€‹è­°é¡Œæ‰€å±¬çš„專案
msgid "InviteMembersModal|To get more members an owner of the group can %{trialLinkStart}start a trial%{trialLinkEnd} or %{upgradeLinkStart}upgrade%{upgradeLinkEnd} to a paid tier."
msgstr "è¦å–得更多æˆå“¡ï¼Œç¾¤çµ„çš„æ“有者å¯ä»¥ %{trialLinkStart}開始試用%{trialLinkEnd} 或 %{upgradeLinkStart}å‡ç´š%{upgradeLinkEnd} 到付費等級。"
-msgid "InviteMembersModal|To make more space, you can remove members who no longer need access."
-msgstr "è¦ç²å¾—更多空間,您å¯ä»¥ç§»é™¤ä¸å†éœ€è¦å­˜å–權é™çš„æˆå“¡ã€‚"
-
msgid "InviteMembersModal|Username or email address"
msgstr "使用者å稱或郵箱地å€"
@@ -21433,9 +21619,6 @@ msgstr "您正在邀請æˆå“¡åŠ å…¥ %{strongStart}%{name}%{strongEnd} 專案。"
msgid "InviteMembersModal|You've reached your %{count} %{members} limit for %{name}"
msgstr "您已é”到 %{name} çš„ %{count} 個 %{members} é™åˆ¶"
-msgid "InviteMembersModal|You've reached your %{count} %{members} limit for your personal projects"
-msgstr "您的個人專案已é”到 %{count} %{members} 個é™åˆ¶"
-
msgid "InviteMembers|Invite a group"
msgstr "邀請群組"
@@ -21545,7 +21728,7 @@ msgid "IrkerService|irker daemon port (defaults to 6659)."
msgstr "irker 守護進程端å£ï¼ˆé è¨­ç‚º 6659)。"
msgid "Is blocked by"
-msgstr "已被å°éŽ–"
+msgstr "被以下議題堵塞"
msgid "Is using license seat:"
msgstr "正在使用授權æ¢æ¬¾ï¼š"
@@ -21553,12 +21736,18 @@ msgstr "正在使用授權æ¢æ¬¾ï¼š"
msgid "Is using seat"
msgstr "正在使用許å¯å¸­æ¬¡"
+msgid "IssuableStatus|%{wi_type} created %{created_at} by "
+msgstr "%{wi_type} 創建者 %{created_at}"
+
msgid "IssuableStatus|Closed"
msgstr "已關閉"
msgid "IssuableStatus|Closed (%{link})"
msgstr "關閉(%{link})"
+msgid "IssuableStatus|Created %{created_at} by"
+msgstr "創建者 %{created_at}"
+
msgid "IssuableStatus|duplicated"
msgstr "é‡è¤‡"
@@ -21877,9 +22066,6 @@ msgstr "週期設定無效。"
msgid "Iterations|Cadence name"
msgstr "週期å稱"
-msgid "Iterations|Can be converted"
-msgstr "å¯ä»¥è½‰æ›"
-
msgid "Iterations|Cancel"
msgstr "å–消"
@@ -21925,6 +22111,9 @@ msgstr "編輯迭代"
msgid "Iterations|Edit iteration cadence"
msgstr "編輯迭代週期"
+msgid "Iterations|Enable automatic scheduling"
+msgstr "啟用自動排程"
+
msgid "Iterations|Enable roll over"
msgstr "啟用 roll over"
@@ -21940,12 +22129,6 @@ msgstr "迭代是在一段時間內追蹤議題的一種方法,使å°çµ„也能
msgid "Iterations|Iterations are scheduled to start on %{weekday}s."
msgstr "迭代排程從 %{weekday} 開始。"
-msgid "Iterations|Learn more about automatic scheduling"
-msgstr "瞭解更多關於自動計劃的訊æ¯"
-
-msgid "Iterations|Manual management of iterations will be deprecated in GitLab 15.6. Convert your manual cadence to use automated scheduling when you are ready."
-msgstr "GitLab 15.6 將棄用手動迭代管ç†ã€‚準備好後,將您的手動週期轉æ›ç‚ºä½¿ç”¨è‡ªå‹•èª¿åº¦ã€‚"
-
msgid "Iterations|Move incomplete issues to the next iteration."
msgstr "將未完æˆçš„議題移至下一次迭代。"
@@ -22003,9 +22186,6 @@ msgstr "æ¯æ¬¡è¿­ä»£çš„æŒçºŒæ™‚間(以周為單ä½ï¼‰ã€‚"
msgid "Iterations|The iteration has been deleted."
msgstr "迭代已被刪除。"
-msgid "Iterations|This cadence can be converted to use automated scheduling"
-msgstr "å¯ä»¥å°‡æ­¤é€±æœŸè½‰æ›ç‚ºä½¿ç”¨è‡ªå‹•èª¿åº¦"
-
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr "這將刪除週期以åŠå…¶ä¸­çš„所有迭代。"
@@ -22015,9 +22195,6 @@ msgstr "這將從分é…給它的任何議題中刪除迭代。"
msgid "Iterations|Title"
msgstr "標題"
-msgid "Iterations|To convert this cadence to automatic scheduling, add a duration and number of upcoming iterations. The upgrade is irreversible."
-msgstr "è¦å°‡æ­¤é€±æœŸè½‰æ›ç‚ºè‡ªå‹•èª¿åº¦ï¼Œè«‹å¢žåŠ æŒçºŒæ™‚é–“å’Œå³å°‡é€²è¡Œçš„迭代次數。該æ昇是ä¸å¯é€†çš„。"
-
msgid "Iterations|Unable to find iteration cadence."
msgstr "無法找到迭代週期。"
@@ -22030,9 +22207,6 @@ msgstr "無法ä¿å­˜é€±æœŸï¼Œè«‹é‡è©¦ã€‚"
msgid "Iterations|Upcoming iterations"
msgstr "å³å°‡åˆ°ä¾†çš„迭代"
-msgid "Iterations|Your manual cadence can be converted to use automated scheduling"
-msgstr "您的手動週期å¯ä»¥è½‰æ›ç‚ºä½¿ç”¨è‡ªå‹•èª¿åº¦"
-
msgid "Iteration|Dates cannot overlap with other existing Iterations within this group"
msgstr "日期ä¸èƒ½èˆ‡è©²ç¾¤çµ„內的其它ç¾æœ‰è¿­ä»£é‡ç–Š"
@@ -22327,9 +22501,6 @@ msgstr "作業已æˆåŠŸåˆªé™¤!"
msgid "Job has wrong arguments format."
msgstr "作業的åƒæ•¸æ ¼å¼éŒ¯èª¤ã€‚"
-msgid "Job is missing the `model_type` argument."
-msgstr "作業缺少 `model_type` åƒæ•¸ã€‚"
-
msgid "Job is stuck. Check runners."
msgstr "作業已阻塞。請檢查Runner。"
@@ -22763,8 +22934,8 @@ 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."
-msgstr "標記å¯ç”¨æ–¼è­°é¡Œå’Œåˆä½µè«‹æ±‚。"
+msgid "Labels can be applied to issues and merge requests. Star a label to make it a priority label."
+msgstr "標籤å¯ä»¥æ‡‰ç”¨åœ¨è­°é¡Œå’Œåˆä½µè«‹æ±‚,為標籤加上星標å¯ä»¥ä½¿å…¶æˆç‚ºå„ªå…ˆæ¨™ç±¤ã€‚"
msgid "Labels with no issues in this iteration:"
msgstr "此迭代中沒有議題的標記:"
@@ -22778,6 +22949,9 @@ msgstr "å‡ç´šæ¨™è¨˜"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. If a group label with the same title exists, it will also be merged. This action cannot be reversed."
msgstr "å‡ç´š%{labelTitle}將使其å¯ç”¨æ–¼%{groupName}內的所有專案。ç¾æœ‰çš„åŒå專案標記將被åˆä½µã€‚ç¾æœ‰çš„åŒå群組標記也將被åˆä½µã€‚該æ“作ä¸å¯æ’¤éŠ·ã€‚"
+msgid "Label|Assignee"
+msgstr ""
+
msgid "Language"
msgstr "語言"
@@ -22947,9 +23121,6 @@ msgstr "å‰ç½®æ™‚é–“"
msgid "Learn GitLab"
msgstr "學習GitLab"
-msgid "Learn GitLab - Ultimate trial"
-msgstr "學習 GitLab - 旗艦版試用"
-
msgid "Learn More"
msgstr "瞭解更多"
@@ -23001,6 +23172,9 @@ msgstr "了解更多關於群組級專案範本"
msgid "Learn more about groups."
msgstr "了解關於群組的更多資訊。"
+msgid "Learn more about issues."
+msgstr "了解更多關於議題的資訊。"
+
msgid "Learn more about max seats used"
msgstr "了解更多關於最大席ä½æ•¸çš„資訊"
@@ -23727,6 +23901,9 @@ msgstr "Mailgun HTTP webhook ç°½å金鑰"
msgid "Mailgun events"
msgstr "Mailgun事件"
+msgid "Main menu"
+msgstr ""
+
msgid "Maintenance mode"
msgstr "維護模å¼"
@@ -23785,7 +23962,7 @@ msgid "Manage milestones"
msgstr "管ç†é‡Œç¨‹ç¢‘"
msgid "Manage project labels"
-msgstr "管ç†é …目標記"
+msgstr "管ç†å°ˆæ¡ˆæ¨™è¨˜ (label)"
msgid "Manage projects."
msgstr "管ç†å°ˆæ¡ˆã€‚"
@@ -23980,7 +24157,7 @@ msgid "Max file size is 200 KB."
msgstr "檔案大å°ä¸Šé™ç‚º200 KB。"
msgid "Max role"
-msgstr "最大角色"
+msgstr "最高權é™è§’色"
msgid "Max seats used"
msgstr "使用的最大席次"
@@ -24015,11 +24192,8 @@ msgstr "最大使用者數"
msgid "Maximum allowable lifetime for access token (days)"
msgstr "å­˜å–令牌(權æ–)的最大å…許壽命(天)"
-msgid "Maximum allowable lifetime for personal access token (days)"
-msgstr "個人存å–令牌(權æ–)的最長有效期(天)"
-
-msgid "Maximum allowed lifetime for SSH keys (in days)"
-msgstr "SSH 金鑰的最長å…許生命週期(以天為單ä½ï¼‰"
+msgid "Maximum allowed lifetime for SSH keys (days)"
+msgstr ""
msgid "Maximum artifacts size"
msgstr "最大工件大å°"
@@ -24408,7 +24582,7 @@ msgid "Merge Requests"
msgstr "åˆä½µè«‹æ±‚"
msgid "Merge Requests created"
-msgstr "建立åˆä½µè«‹æ±‚"
+msgstr "已建立åˆä½µè«‹æ±‚"
msgid "Merge Requests in Review"
msgstr "審閱中的åˆä½µè«‹æ±‚"
@@ -24500,6 +24674,9 @@ msgstr "åˆä½µè«‹æ±‚已安排在æµæ°´ç·šæˆåŠŸå¾Œåˆä½µ"
msgid "Merge requests"
msgstr "åˆä½µè«‹æ±‚"
+msgid "Merge requests and approvals settings have moved."
+msgstr ""
+
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr "åˆä½µè«‹æ±‚用於æ出å°å°ˆæ¡ˆçš„變更並與他人進行討論"
@@ -24587,6 +24764,9 @@ msgstr "儲存留言è‰ç¨¿æ™‚發生錯誤。"
msgid "MergeRequests|Create issue to resolve thread"
msgstr "建立議題以解決主題"
+msgid "MergeRequests|Reference copied"
+msgstr "已復制åƒç…§"
+
msgid "MergeRequests|Saving the comment failed"
msgstr "儲存留言失敗"
@@ -24662,6 +24842,30 @@ msgstr "未找到任何文件"
msgid "MergeRequest|Search (e.g. *.vue) (%{modifier_key}P)"
msgstr "æœå°‹ï¼ˆä¾‹å¦‚ *.vue)(%{modifier_key}P)"
+msgid "MergeTopics|%{sourceTopic} will be removed"
+msgstr "%{sourceTopic} 將被移除"
+
+msgid "MergeTopics|All assigned projects will be moved to %{targetTopic}"
+msgstr "所有已指派的專案將被移動到 %{targetTopic}"
+
+msgid "MergeTopics|Merge topics"
+msgstr "åˆä½µä¸»é¡Œ"
+
+msgid "MergeTopics|Merging topics will cause the following:"
+msgstr "åˆä½µä¸»é¡Œå°‡å°Žè‡´ä»¥ä¸‹æƒ…æ³ï¼š"
+
+msgid "MergeTopics|Move all assigned projects from the source topic to the target topic and remove the source topic."
+msgstr "將所有已指派的專案從æºä¸»é¡Œç§»å‹•åˆ°ç›®æ¨™ä¸»é¡Œä¸¦åˆªé™¤æºä¸»é¡Œã€‚"
+
+msgid "MergeTopics|Source topic"
+msgstr "來æºä¸»é¡Œ"
+
+msgid "MergeTopics|Target topic"
+msgstr "目標主題"
+
+msgid "MergeTopics|This action cannot be undone."
+msgstr "該動作無法還原。"
+
msgid "Merged"
msgstr "å·²åˆä½µ"
@@ -25336,18 +25540,21 @@ msgstr "修改æ交消æ¯"
msgid "Modify merge commit"
msgstr "修改åˆä½µæ交"
+msgid "Mon"
+msgstr "星期一"
+
msgid "Monday"
msgstr "星期一"
msgid "Monitor"
msgstr "監控"
+msgid "Monitor GitLab with Prometheus."
+msgstr ""
+
msgid "Monitor Settings"
msgstr "監控設定"
-msgid "Monitor the health and performance of GitLab with Prometheus."
-msgstr "用Prometheus監控 GitLab çš„å¥åº·å’Œæ€§èƒ½ã€‚"
-
msgid "Monitor your errors by integrating with Sentry."
msgstr "通éŽèˆ‡Sentryæ•´åˆä¾†ç›£æŽ§æ‚¨çš„錯誤。"
@@ -25360,6 +25567,9 @@ msgstr "月"
msgid "Months"
msgstr "月"
+msgid "More"
+msgstr "更多"
+
msgid "More Details"
msgstr "更多詳情"
@@ -25402,9 +25612,6 @@ msgstr "相關度最高"
msgid "Most stars"
msgstr "最多星號"
-msgid "Mount point %{mounted_as} not found in %{model_class}."
-msgstr "%{model_class} 中未找到掛載點 %{mounted_as}。"
-
msgid "Move"
msgstr "移動"
@@ -25495,12 +25702,6 @@ msgstr "支æ´å¤šå€‹ IP 地å€ç¯„åœã€‚ä¸æœƒå½±éŸ¿å°ç¾¤çµ„設定的存å–。"
msgid "Multiple Prometheus integrations are not supported"
msgstr "ä¸æ”¯æ´å¤šå€‹Prometheusæ•´åˆ"
-msgid "Multiple model types found: %{model_types}"
-msgstr "找到多個模型類型: %{model_types}"
-
-msgid "Multiple uploaders found: %{uploader_types}"
-msgstr "找到多個上傳器: %{uploader_types}"
-
msgid "Multiplier to apply to polling intervals. Decimal values are supported. Defaults to 1."
msgstr "應用於輪詢間隔的乘數。支æ´å進制值。é è¨­ç‚º 1。"
@@ -25713,7 +25914,7 @@ msgid "New File"
msgstr "新增文件"
msgid "New Group"
-msgstr "建立群組"
+msgstr "新建群組"
msgid "New Group Name"
msgstr "新增群組å稱"
@@ -25741,7 +25942,7 @@ msgid "New Password"
msgstr "新增密碼"
msgid "New Pipeline Schedule"
-msgstr "新增æµæ°´ç·šè¨ˆåŠƒ"
+msgstr "新增æµæ°´ç·šæŽ’程"
msgid "New Project"
msgstr "新增專案"
@@ -25801,7 +26002,7 @@ msgid "New file"
msgstr "新增文件"
msgid "New group"
-msgstr "建立群組"
+msgstr "新建群組"
msgid "New health check access token has been generated!"
msgstr "已生æˆæ–°çš„執行狀æ³æª¢æŸ¥å­˜å–令牌(權æ–)ï¼"
@@ -25828,7 +26029,7 @@ msgid "New list"
msgstr "新增列表"
msgid "New merge request"
-msgstr "建立åˆä½µè«‹æ±‚"
+msgstr "新建åˆä½µè«‹æ±‚"
msgid "New milestone"
msgstr "新增里程碑"
@@ -25873,7 +26074,7 @@ msgid "New runners registration token has been generated!"
msgstr "已生æˆæ–°çš„執行器(Runner)註冊令牌(權æ–)ï¼"
msgid "New schedule"
-msgstr "新建計劃"
+msgstr "新增排程"
msgid "New snippet"
msgstr "新建程å¼ç¢¼ç‰‡æ®µ"
@@ -26022,6 +26223,9 @@ msgstr "沒有相符的信用å¡è³‡æ–™"
msgid "No credit card required."
msgstr "無需信用å¡ã€‚"
+msgid "No data available"
+msgstr ""
+
msgid "No data found"
msgstr "未找到資料"
@@ -26034,9 +26238,6 @@ msgstr "未檢測到部署。使用環境來控制軟體的æŒçºŒéƒ¨ç½²ã€‚ %{lin
msgid "No deployments found"
msgstr "沒有找到部署"
-msgid "No due date"
-msgstr "無截止日期"
-
msgid "No email participants were added. Either none were provided, or they already exist."
msgstr "沒有加入電å­éƒµä»¶åƒèˆ‡è€…。沒有æ供或它們已經存在。"
@@ -26047,7 +26248,7 @@ msgid "No errors to display."
msgstr "沒有è¦é¡¯ç¤ºçš„錯誤."
msgid "No estimate or time spent"
-msgstr "ç„¡é è¨ˆæˆ–已用時間"
+msgstr "沒有é ä¼°æˆ–已花費時間"
msgid "No file chosen."
msgstr "未é¸å®šä»»ä½•æ–‡ä»¶ã€‚"
@@ -26187,11 +26388,14 @@ msgstr "無版本庫"
msgid "No results"
msgstr "沒有çµæžœ"
+msgid "No results found"
+msgstr "沒有çµæžœ"
+
msgid "No runner executable"
msgstr "沒有執行器å¯åŸ·è¡Œæ–‡ä»¶"
msgid "No schedules"
-msgstr "無計劃"
+msgstr "沒有排程"
msgid "No service accounts"
msgstr "沒有æœå‹™å¸³è™Ÿ"
@@ -26208,9 +26412,6 @@ msgstr "沒有此錯誤的堆棧追蹤"
msgid "No starrers matched your search"
msgstr "沒有符åˆæ‚¨æœå°‹æ¢ä»¶çš„收è—使用者"
-msgid "No start date"
-msgstr "沒有開始日期"
-
msgid "No suggestions found"
msgstr "未找到任何建議"
@@ -26294,7 +26495,7 @@ msgid "Not available for protected branches"
msgstr "å°å—ä¿è­·çš„分支ä¸å¯ç”¨"
msgid "Not confidential"
-msgstr "éžç§å¯†"
+msgstr "éžæ©Ÿå¯†"
msgid "Not found"
msgstr "未找到"
@@ -26398,6 +26599,9 @@ msgstr "指定的建立日期太舊。"
msgid "Nothing to preview."
msgstr "沒有å¯é è¦½çš„內容。"
+msgid "Notification Email"
+msgstr ""
+
msgid "Notification events"
msgstr "通知事件"
@@ -26528,6 +26732,9 @@ msgstr "%{author_link} çš„è­°é¡Œ %{issue_reference_link} å³å°‡åˆ°æœŸã€‚"
msgid "Notify|%{commit_link} in %{mr_link}"
msgstr "%{commit_link} 在 %{mr_link}"
+msgid "Notify|%{commits_text} from branch `%{target_branch}`"
+msgstr ""
+
msgid "Notify|%{invite_email}, now known as %{user_name}, has accepted your invitation to join the %{target_name} %{target_model_name}."
msgstr "%{invite_email},ç¾åœ¨ç¨±ç‚º %{user_name},已接å—您的線請加入 %{target_name} %{target_model_name} 。"
@@ -26537,6 +26744,24 @@ msgstr "%{invited_user} å·² %{highlight_start}拒絕%{highlight_end} 您的加å…
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr "%{member_link} 已請求 %{member_role} å­˜å– %{target_source_link} %{target_type}。"
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgstr "%{mr_highlight}åˆä½µè«‹æ±‚%{highlight_end} %{mr_link} %{approved_highlight}已被%{highlight_end} %{approver_avatar} %{approver_link}核准"
+
+msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
+msgstr "%{paragraph_start}å—¨ %{name}!%{paragraph_end} %{paragraph_start}新的公鑰已加入到您的帳號:%{paragraph_end} %{paragraph_start}title:%{key_title}%{paragraph_end} %{paragraph_start}如果該公鑰加入錯誤,您å¯ä»¥åœ¨ %{removal_link}%{paragraph_end} 下將其移除"
+
+msgid "Notify|%{updated_by_user_name} pushed new commits to merge request %{mr_link}"
+msgstr ""
+
+msgid "Notify|A new GPG key was added to your account:"
+msgstr "有支新的 GPG 金鑰已加至您的帳號:"
+
+msgid "Notify|All discussions on merge request %{mr_link} were resolved by %{name}"
+msgstr ""
+
+msgid "Notify|And %{total_stripped_new_commits_count} more"
+msgstr ""
+
msgid "Notify|Assignee changed from %{fromNames} to %{toNames}"
msgstr "被指派人從 %{fromNames} 更改為 %{toNames}"
@@ -26552,11 +26777,23 @@ msgstr "%{project} 的自動 DevOps æµæ°´ç·šå·²ç¦ç”¨"
msgid "Notify|CI/CD project settings"
msgstr "CI/CD 專案設定"
+msgid "Notify|Don't want to receive updates from GitLab administrators?"
+msgstr ""
+
+msgid "Notify|Fingerprint: %{fingerprint}"
+msgstr "指紋:%{fingerprint}"
+
+msgid "Notify|Hi %{user}!"
+msgstr "%{user},歡迎使用ï¼"
+
+msgid "Notify|If this key was added in error, you can remove it under %{removal_link}"
+msgstr "如果這支金鑰是誤加的,您å¯ä»¥åœ¨ %{removal_link} 處移除"
+
msgid "Notify|If you no longer wish to use this domain with GitLab Pages, please remove it from your GitLab project and delete any related DNS records."
msgstr "如果您ä¸å†å¸Œæœ›å°‡æ­¤ç¶²åŸŸèˆ‡ GitLab Pages 一起使用,請將其從您的 GitLab 專案中移除並刪除任何相關的 DNS 記錄。"
msgid "Notify|Issue was %{issue_status} by %{updated_by}"
-msgstr "å•é¡Œæ˜¯ %{updated_by} çš„ %{issue_status}"
+msgstr "此議題由 %{updated_by} 更新為 %{issue_status}"
msgid "Notify|Issue was moved to another project."
msgstr "議題已移至其他專案"
@@ -26582,9 +26819,18 @@ msgstr "åˆä½µè«‹æ±‚ %{mr_link} 已被 %{closed_by} 關閉"
msgid "Notify|Merge request URL: %{merge_request_url}"
msgstr "åˆä½µè«‹æ±‚URL: %{merge_request_url}"
+msgid "Notify|Merge request was approved"
+msgstr "åˆä½µè«‹æ±‚已被核准"
+
+msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
+msgstr "åˆä½µè«‹æ±‚已被 (%{approvals}/%{required_approvals}) 核准"
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr "里程碑已更改為 %{milestone}"
+msgid "Notify|Milestone removed"
+msgstr ""
+
msgid "Notify|New issue: %{project_issue_url}"
msgstr "新議題: %{project_issue_url}"
@@ -26603,6 +26849,12 @@ msgstr "除éžæ‚¨é€šéŽ %{time_start}%{time}%{time_end} 驗證您的網域,å¦
msgid "Notify|You don't have access to the project."
msgstr "您沒有存å–該專案的權é™"
+msgid "Notify|You have been mentioned in an issue."
+msgstr "您在一個議題中被æåŠã€‚"
+
+msgid "Notify|You have been mentioned in merge request %{mr_link}"
+msgstr "您在 %{mr_link} åˆä½µè«‹æ±‚中被æåŠ"
+
msgid "Notify|Your request to join the %{target_to_join} %{target_type} has been %{denied_tag}."
msgstr "您加入 %{target_to_join} %{target_type} 的請求已被 %{denied_tag}。"
@@ -26706,6 +26958,9 @@ msgid "On %{end_date}, your trial will end and %{namespace_name} will be limited
msgid_plural "On %{end_date}, your trial will end and %{namespace_name} will be limited to %{free_user_limit} members"
msgstr[0] "在您的試用版於 %{end_date} çµæŸæ™‚,%{namespace_name} å°‡é™åˆ¶ç‚º %{free_user_limit} åæˆå“¡ã€‚"
+msgid "On the left sidebar, select %{merge_requests_link} to view them."
+msgstr ""
+
msgid "On track"
msgstr "如期進行"
@@ -26925,6 +27180,12 @@ msgstr "刪除設定"
msgid "OnDemandScans|Description (optional)"
msgstr "æè¿° (å¯é¸)"
+msgid "OnDemandScans|Discard changes"
+msgstr "放棄更改"
+
+msgid "OnDemandScans|Do you want to discard the changes or keep editing this profile? Unsaved changes will be lost."
+msgstr "您è¦æ”¾æ£„更改還是繼續編輯此é…置文件? 未儲存的更改將被丟失。"
+
msgid "OnDemandScans|Dynamic Application Security Testing (DAST)"
msgstr "動態應用程å¼å®‰å…¨æ¸¬è©¦ (DAST)"
@@ -26946,6 +27207,9 @@ msgstr "啟用掃æ排程"
msgid "OnDemandScans|For example: Tests the login page for SQL injections"
msgstr "例如:測試SQL注入的登入é é¢"
+msgid "OnDemandScans|Keep editing"
+msgstr "繼續編輯"
+
msgid "OnDemandScans|Manage scanner profiles"
msgstr "管ç†æŽƒæ工具設定"
@@ -27069,6 +27333,9 @@ msgstr "使用ç¾æœ‰çš„站點設定"
msgid "OnDemandScans|View results"
msgstr "查看çµæžœ"
+msgid "OnDemandScans|You have unsaved changes"
+msgstr "您有未儲存的更改"
+
msgid "OnDemandScans|You must create a repository within your project to run an on-demand scan."
msgstr "您必須在專案中建立倉庫æ‰èƒ½é‹è¡ŒæŒ‰éœ€æŽƒæ。"
@@ -27223,9 +27490,15 @@ msgstr "é–‹å•Ÿ: %{open}"
msgid "OpenAPI"
msgstr "OpenAPI"
+msgid "OpenAPI Specification file URL"
+msgstr "OpenAPI è¦æ ¼æ–‡ä»¶ URL"
+
msgid "OpenAPI Specification file path or URL"
msgstr "OpenAPI è¦ç¯„文件路徑或 URL"
+msgid "OpenSearch's region."
+msgstr "OpenSearch çš„å€åŸŸã€‚"
+
msgid "Opened"
msgstr "已開啟"
@@ -27439,6 +27712,9 @@ msgstr "軟體套件(Package)庫:未經身份驗證的 API 請求"
msgid "Package already exists"
msgstr "軟體套件(Package)已存在"
+msgid "Package and registry settings"
+msgstr ""
+
msgid "Package deleted successfully"
msgstr "軟體套件(Package)å·²æˆåŠŸåˆªé™¤"
@@ -27475,6 +27751,9 @@ msgstr "軟體套件(Package)類型必須是Nuget"
msgid "Package type must be PyPi"
msgstr "軟體套件(Package)類型必須是PyPi"
+msgid "Package type must be RPM"
+msgstr ""
+
msgid "Package type must be RubyGems"
msgstr "軟體套件(Package)類型必須是 RubyGems"
@@ -27499,6 +27778,9 @@ msgstr "加入composer註冊表"
msgid "PackageRegistry|Additional metadata"
msgstr "其他元數據"
+msgid "PackageRegistry|Allow duplicates"
+msgstr "å…許é‡è¤‡"
+
msgid "PackageRegistry|Allow packages with the same name and version to be uploaded to the registry. The newest version of a package is always used when installing."
msgstr "å…許將具有相åŒå稱和版本的套件上傳到註冊表,套件安è£æ™‚始終使用最新版本號。"
@@ -27642,7 +27924,7 @@ msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}se
msgstr "關於PyPi註冊表的更多訊æ¯ï¼Œ%{linkStart}請見文件%{linkEnd}。"
msgid "PackageRegistry|Generic"
-msgstr "通用"
+msgstr "一般"
msgid "PackageRegistry|Gradle Groovy DSL"
msgstr "Gradle Groovy DSL"
@@ -27716,6 +27998,9 @@ msgstr "æˆåŠŸåˆªé™¤è»Ÿé«”套件"
msgid "PackageRegistry|Package file deleted successfully"
msgstr "æˆåŠŸåˆªé™¤è»Ÿé«”套件檔"
+msgid "PackageRegistry|Package formats"
+msgstr "軟體套件格å¼"
+
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] "軟體套件有 %{updatesCount} 個存檔更新"
@@ -27747,9 +28032,6 @@ msgstr "構æˆ: %{recipe}"
msgid "PackageRegistry|Registry setup"
msgstr "倉庫設定"
-msgid "PackageRegistry|Reject packages with the same name and version"
-msgstr "拒絕具有相åŒå稱與版本的套件"
-
msgid "PackageRegistry|Remove package"
msgstr "刪除軟體套件"
@@ -27759,12 +28041,6 @@ msgstr "éœ€è¦ Python: %{pythonVersion}"
msgid "PackageRegistry|RubyGems"
msgstr "RubyGems"
-msgid "PackageRegistry|Settings for Generic packages"
-msgstr "通用軟體套件設定"
-
-msgid "PackageRegistry|Settings for Maven packages"
-msgstr "Maven 軟體套件的設定"
-
msgid "PackageRegistry|Show Composer commands"
msgstr "顯示 Composer 指令"
@@ -27857,7 +28133,7 @@ msgid "PackageRegistry|You may also need to setup authentication using an auth t
msgstr "您å¯èƒ½é‚„需è¦ä½¿ç”¨ä»¤ç‰Œè¨­å®šèº«ä»½é©—證。%{linkStart}è«‹åƒé–±æ–‡ä»¶%{linkEnd}以了解更多訊æ¯ã€‚"
msgid "PackageRegistry|You will need a %{linkStart}personal access token%{linkEnd}."
-msgstr "您將需è¦ä¸€å€‹%{linkStart} 個人存å–æ¬Šæ– %{linkEnd}。"
+msgstr "您將需è¦ä¸€å€‹%{linkStart} 個人存å–權æ–(令牌) %{linkEnd}。"
msgid "PackageRegistry|npm"
msgstr "npm"
@@ -27865,8 +28141,8 @@ msgstr "npm"
msgid "PackageRegistry|published by %{author}"
msgstr "由%{author}發布"
-msgid "Packages & Registries"
-msgstr "軟體套件與映åƒåº«"
+msgid "Packages and registries"
+msgstr ""
msgid "Page not found"
msgstr "找ä¸åˆ°é é¢"
@@ -28114,9 +28390,6 @@ msgstr "執行程å¼ç¢¼å¯©æŸ¥ä¸¦é€šéŽåˆä½µè«‹æ±‚增強å”作。"
msgid "Perform common operations on GitLab project"
msgstr "在GitLab專案上執行常見æ“作"
-msgid "Performance insights"
-msgstr "性能洞察"
-
msgid "Performance optimization"
msgstr "性能優化"
@@ -28192,6 +28465,12 @@ msgstr "牆"
msgid "Period in seconds"
msgstr "週期(秒)"
+msgid "Period of inactivity (days)"
+msgstr "未活動時間(天)"
+
+msgid "Period of inactivity before deactivation."
+msgstr ""
+
msgid "Permalink"
msgstr "永久連çµ"
@@ -28271,10 +28550,10 @@ msgid "Pipeline IID"
msgstr "æµæ°´ç·šIID"
msgid "Pipeline Schedule"
-msgstr "æµæ°´ç·šè¨ˆåŠƒ"
+msgstr "æµæ°´ç·šæŽ’程"
msgid "Pipeline Schedules"
-msgstr "æµæ°´ç·šè¨ˆåŠƒ"
+msgstr "æµæ°´ç·šæŽ’程"
msgid "Pipeline URL"
msgstr "æµæ°´ç·š URL"
@@ -28897,27 +29176,18 @@ msgstr "正在建立æµæ°´ç·šã€‚"
msgid "Pipeline|Date"
msgstr "日期"
-msgid "Pipeline|Detached merge request pipeline"
-msgstr "游離的åˆä½µè«‹æ±‚æµæ°´ç·š"
-
msgid "Pipeline|Failed"
msgstr "失敗"
-msgid "Pipeline|Five slowest jobs"
-msgstr "五個最慢的作業"
-
msgid "Pipeline|In progress"
msgstr "進行中"
-msgid "Pipeline|Last executed job"
-msgstr "最後執行的作業"
-
-msgid "Pipeline|Longest queued job"
-msgstr "排隊時間最長的作業"
-
msgid "Pipeline|Manual"
msgstr "手動"
+msgid "Pipeline|Merge request pipeline"
+msgstr ""
+
msgid "Pipeline|Merge train pipeline"
msgstr "åˆä½µä½‡åˆ—æµæ°´ç·š"
@@ -28927,18 +29197,12 @@ msgstr "åˆä½µä½‡åˆ—æµæ°´ç·šä½œæ¥­ç„¡æ³•é‡è©¦"
msgid "Pipeline|Merged result pipeline"
msgstr "åˆä½µçµæžœæµæ°´ç·š"
-msgid "Pipeline|Only able to show first 100 results"
-msgstr "åªèƒ½é¡¯ç¤ºå‰ 100 個çµæžœ"
-
msgid "Pipeline|Passed"
msgstr "已通éŽ"
msgid "Pipeline|Pending"
msgstr "等待中"
-msgid "Pipeline|Performance insights"
-msgstr "效能檢視"
-
msgid "Pipeline|Pipeline"
msgstr "æµæ°´ç·š"
@@ -28996,12 +29260,6 @@ msgstr "標籤å稱"
msgid "Pipeline|Test coverage"
msgstr "測試覆蓋率"
-msgid "Pipeline|The last executed job is the last job to start in the pipeline."
-msgstr "最後執行的作業是在æµæ°´ç·šä¸­æœ€å¾Œé–‹å§‹çš„作業。"
-
-msgid "Pipeline|The longest queued job is the job that spent the longest time in the pending state, waiting to be picked up by a Runner"
-msgstr "排隊時間最長的作業是指在待處ç†ç‹€æ…‹ä¸­èŠ±è²»æœ€é•·æ™‚間,等待被執行器æ€é¸çš„作業"
-
msgid "Pipeline|This change will decrease the overall test coverage if merged."
msgstr "如果åˆä½µï¼Œæ­¤æ›´æ”¹å°‡é™ä½Žæ•´é«”測試覆蓋率。"
@@ -29032,9 +29290,6 @@ msgstr "變數"
msgid "Pipeline|View commit"
msgstr "查看æ交"
-msgid "Pipeline|View dependency"
-msgstr "檢視ä¾è³´"
-
msgid "Pipeline|View pipeline"
msgstr "查看æµæ°´ç·š"
@@ -29134,8 +29389,8 @@ msgstr "請檢查您的電å­éƒµä»¶ %{email} 以確èªæ‚¨çš„帳號"
msgid "Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}."
msgstr "請檢查您的電å­éƒµä»¶(%{email})以確èªæ‚¨æ“有此電å­ä¿¡ç®±ä¸¦è§£éŽ–CI/CD的強大功能。還沒收到郵件? %{resend_link}。或是電å­éƒµä»¶åœ°å€éŒ¯èª¤ï¼Ÿ%{update_link}。"
-msgid "Please click the link in the confirmation email before continuing. It was sent to "
-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 your profile with email address"
msgstr "請在您的個人資料中填寫電å­éƒµä»¶åœ°å€"
@@ -29350,6 +29605,9 @@ msgstr "埠號"
msgid "Postman collection"
msgstr "Postman 集åˆ"
+msgid "Postman collection file URL"
+msgstr "Postman 集åˆæ–‡ä»¶ URL"
+
msgid "Postman collection file path or URL"
msgstr "Postman 集åˆæ–‡ä»¶è·¯å¾‘或 URL"
@@ -29371,12 +29629,12 @@ msgstr "個人化"
msgid "Preferences|Choose between fixed (max. 1280px) and fluid (%{percentage}) application layout."
msgstr "為應用程å¼é¸æ“‡å›ºå®šæ–¹å¼(最大值1280px)或æµå‹•æ–¹å¼(%{percentage})佈局。"
+msgid "Preferences|Choose what content you want to see by default on your dashboard."
+msgstr ""
+
msgid "Preferences|Choose what content you want to see on a project’s overview page."
msgstr "é¸æ“‡å°ˆæ¡ˆæ¦‚覽é é¢ä¸­æ‚¨æƒ³çœ‹åˆ°çš„內容。"
-msgid "Preferences|Choose what content you want to see on your homepage."
-msgstr "é¸æ“‡æ‚¨æƒ³è¦åœ¨é¦–é ä¸Šçœ‹åˆ°çš„內容。"
-
msgid "Preferences|Color for added lines"
msgstr "為已加入行著色"
@@ -29398,8 +29656,11 @@ msgstr "自定義 GitLab çš„é¡è‰²ã€‚"
msgid "Preferences|Customize the colors of removed and added lines in diffs."
msgstr "自定義差異中的刪除和加入行的é¡è‰²ã€‚"
+msgid "Preferences|Dashboard"
+msgstr ""
+
msgid "Preferences|Diff colors"
-msgstr "差異的é¡è‰²"
+msgstr "差異比å°é¡è‰²"
msgid "Preferences|Display time in 24-hour format"
msgstr "以24å°æ™‚æ ¼å¼é¡¯ç¤ºæ™‚é–“"
@@ -29419,9 +29680,6 @@ msgstr "例如:30分é˜å‰"
msgid "Preferences|Gitpod"
msgstr "Gitpod"
-msgid "Preferences|Homepage content"
-msgstr "首é å…§å®¹"
-
msgid "Preferences|Instead of all the files changed, show only one file at a time. To switch between files, use the file browser."
msgstr "一次僅顯示一個文件,而ä¸æ˜¯æ‰€æœ‰æ›´æ”¹çš„文件。è¦åœ¨æ–‡ä»¶ä¹‹é–“切æ›ï¼Œè«‹ä½¿ç”¨æ–‡ä»¶ç€è¦½å™¨ã€‚"
@@ -29573,7 +29831,7 @@ msgid "Private group(s)"
msgstr "ç§æœ‰ç¾¤çµ„"
msgid "Private profile"
-msgstr "éžå…¬é–‹è³‡æ–™"
+msgstr "ç§äººè³‡è¨Š"
msgid "Private projects Minutes cost factor"
msgstr "ç§æœ‰å°ˆæ¡ˆåˆ†é˜æˆæœ¬ä¿‚數"
@@ -29768,7 +30026,7 @@ msgid "Profiles|Do not show on profile"
msgstr "ä¸åœ¨å€‹äººè³‡æ–™ä¸­é¡¯ç¤º"
msgid "Profiles|Don't display activity-related personal information on your profile."
-msgstr "ä¸è¦åœ¨å€‹äººæª”案上顯示與活動相關的個人資訊."
+msgstr "ä¸è¦åœ¨å€‹äººæª”案上顯示與活動相關的ç§äººè³‡è¨Š."
msgid "Profiles|Edit Profile"
msgstr "編輯個人資料"
@@ -29813,7 +30071,7 @@ msgid "Profiles|If after setting a password, the option to delete your account i
msgstr "如果設定密碼後,刪除帳號的é¸é …ä»ç„¶ä¸å¯ç”¨ï¼Œè«‹%{link_start}æ交請求%{link_end}來開始帳號刪除程åºã€‚"
msgid "Profiles|Include private contributions on my profile"
-msgstr "在個人資料中包å«éžå…¬é–‹è²¢ç»"
+msgstr "在個人資料中包å«ç§äººè²¢ç»"
msgid "Profiles|Incoming email token was successfully reset"
msgstr "接收郵件令牌(權æ–)å·²é‡è¨­"
@@ -29833,9 +30091,6 @@ msgstr "è·ä½"
msgid "Profiles|Key"
msgstr "金鑰"
-msgid "Profiles|Key becomes invalid on this date."
-msgstr "金鑰在此日期無效。"
-
msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr "金鑰在此日期無效,SSH 金鑰的最大有效期為 %{max_ssh_key_lifetime} 天"
@@ -29866,6 +30121,9 @@ msgstr "未é¸æ“‡æ–‡ä»¶ã€‚"
msgid "Profiles|Notification email"
msgstr "通知郵件"
+msgid "Profiles|Optional but recommended. If set, key becomes invalid on the specified date."
+msgstr "å¯é¸çš„,但推薦。 如果設置,密鑰將在指定日期失效。"
+
msgid "Profiles|Organization"
msgstr "組織"
@@ -29879,7 +30137,7 @@ msgid "Profiles|Primary email"
msgstr "主郵件"
msgid "Profiles|Private contributions"
-msgstr "éžå…¬é–‹è²¢ç»"
+msgstr "ç§äººè²¢ç»"
msgid "Profiles|Profile was successfully updated"
msgstr "個人資料已æˆåŠŸæ›´æ–°"
@@ -30122,7 +30380,7 @@ msgid "Project audit events"
msgstr "專案審計事件"
msgid "Project avatar"
-msgstr "專案頭åƒ"
+msgstr "專案圖åƒ"
msgid "Project cannot be shared with the group it is in or one of its ancestors."
msgstr "專案ä¸èƒ½åˆ†äº«çµ¦ç•¶å‰æ‰€åœ¨çš„群組或其任一級父群組。"
@@ -30164,10 +30422,10 @@ msgid "Project import requests"
msgstr "專案匯入請求"
msgid "Project info:"
-msgstr "專案訊æ¯ï¼š"
+msgstr "專案資訊:"
msgid "Project information"
-msgstr "專案訊æ¯"
+msgstr "專案資訊"
msgid "Project is required when cluster_type is :project"
msgstr "cluster_type為 :project 時專案為必需"
@@ -30553,6 +30811,9 @@ msgstr "啟用åˆä½µçµæžœæµæ°´ç·š"
msgid "ProjectSettings|Encourage"
msgstr "激勵"
+msgid "ProjectSettings|Environments"
+msgstr "環境"
+
msgid "ProjectSettings|Every merge creates a merge commit."
msgstr "æ¯æ¬¡åˆä½µéƒ½æœƒå»ºç«‹ä¸€å€‹åˆä½µæ交。"
@@ -30565,6 +30826,9 @@ 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 "æ¯å€‹å°ˆæ¡ˆéƒ½å¯ä»¥æœ‰è‡ªå·±çš„空間來儲存它的軟體套件。注æ„:當一個專案是公開時,軟體套件庫總是å¯è¦‹çš„。"
+msgid "ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access."
+msgstr "æ¯å€‹å°ˆæ¡ˆéƒ½å¯ä»¥ç¶“ç”± CI/CD 或 API 調用å°ç’°å¢ƒé€²è¡Œéƒ¨ç½²ï¼Œéžå°ˆæ¡ˆæˆå“¡åƒ…具唯讀權é™ã€‚"
+
msgid "ProjectSettings|Everyone"
msgstr "所有人"
@@ -30583,6 +30847,9 @@ msgstr "快進å¼(Fast-forward)åˆä½µ"
msgid "ProjectSettings|Fast-forward merges only."
msgstr "僅快進å¼ï¼ˆFast-forward)åˆä½µã€‚"
+msgid "ProjectSettings|Feature flags"
+msgstr "功能標誌"
+
msgid "ProjectSettings|Flexible tool to collaboratively develop ideas and plan work in this project."
msgstr "用於在此專案中å”作開發想法和計劃工作的éˆæ´»å·¥å…·ã€‚"
@@ -30724,6 +30991,9 @@ msgstr "需求"
msgid "ProjectSettings|Requirements management system."
msgstr "需求管ç†ç³»çµ±ã€‚"
+msgid "ProjectSettings|Roll out new features without redeploying with feature flags."
+msgstr "無需使用功能標誌é‡æ–°éƒ¨ç½²å³å¯æŽ¨å‡ºæ–°åŠŸèƒ½ã€‚"
+
msgid "ProjectSettings|Search for topic"
msgstr "æœå°‹ä¸»é¡Œ"
@@ -31016,13 +31286,13 @@ 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 "æœå°‹æ‚¨çš„專案"
@@ -31534,8 +31804,8 @@ msgstr "é è¨­"
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr "%{environment_name} å°‡å°é–‹ç™¼äººå“¡å¯å¯«å…¥ï¼Œç¢ºå®šç¹¼çºŒå—Žï¼Ÿ"
-msgid "ProtectedEnvironment|All environments specivied with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
-msgstr "使用以下部署層級指定的所有環境å‡å—父群組ä¿è­·ã€‚ %{link_start}了解更多%{link_end}。"
+msgid "ProtectedEnvironment|All environments specified with the deployment tiers below are protected by a parent group. %{link_start}Learn More%{link_end}."
+msgstr ""
msgid "ProtectedEnvironment|Allowed to deploy"
msgstr "å…許部署"
@@ -31552,8 +31822,8 @@ msgstr "上游å—ä¿è­·çš„環境"
msgid "ProtectedEnvironment|Failed to load details for this group."
msgstr "該群組的詳細資訊載入失敗。"
-msgid "ProtectedEnvironment|No environments in this project are projected."
-msgstr "該專案中沒有å—ä¿è­·çš„環境。"
+msgid "ProtectedEnvironment|No environments in this project are protected."
+msgstr ""
msgid "ProtectedEnvironment|Only specified groups can execute deployments in protected environments."
msgstr "åªæœ‰æŒ‡å®šçš„使用者æ‰èƒ½åœ¨å—ä¿è­·çš„環境中執行部署。"
@@ -31735,9 +32005,6 @@ msgstr "推é€äº‹ä»¶"
msgid "Push project from command line"
msgstr "從命令列推é€å°ˆæ¡ˆ"
-msgid "Push rules"
-msgstr "推é€è¦å‰‡"
-
msgid "Push the target branch up to GitLab."
msgstr "將目標分支推é€åˆ° GitLab。"
@@ -31861,9 +32128,6 @@ msgstr "快速幫助"
msgid "Quick range"
msgstr "å¿«æ·ç¯„åœ"
-msgid "Quickly and easily edit multiple files in your project."
-msgstr "快速輕鬆地編輯您專案中的多個文件。"
-
msgid "Quota of CI/CD minutes"
msgstr "CI/CD 分é˜æ•¸é…é¡"
@@ -32093,6 +32357,9 @@ msgstr "使用此 URL 註冊執行器:"
msgid "Register with two-factor app"
msgstr "使用雙因å­èªè­‰æ‡‰ç”¨ç¨‹å¼è¨»å†Š"
+msgid "Register with:"
+msgstr "註冊:"
+
msgid "RegistrationFeatures|Enable Service Ping and register for this feature."
msgstr "啟用æœå‹™ Ping 並註冊此功能。"
@@ -32326,7 +32593,7 @@ msgid "Remove assignee"
msgstr "移除指派人"
msgid "Remove avatar"
-msgstr "移除頭åƒ"
+msgstr "移除圖åƒ"
msgid "Remove card"
msgstr "刪除å¡ç‰‡"
@@ -33446,6 +33713,9 @@ msgstr "啟用"
msgid "Runners|Add notes, like who owns the runner or what it should be used for."
msgstr "增加註釋,例如執行器的æ“有者或執行器的用途。"
+msgid "Runners|Add your feedback in the issue"
+msgstr ""
+
msgid "Runners|All"
msgstr "全部"
@@ -33461,6 +33731,12 @@ msgstr "Amazon Linux 2 Docker HA 具有手動縮放和å¯é¸çš„排程。Non-spot
msgid "Runners|An error has occurred fetching instructions"
msgstr "å–得指令時發生錯誤"
+msgid "Runners|An upgrade is available for this runner"
+msgstr "該執行器有å¯ç”¨çš„æ›´æ–°"
+
+msgid "Runners|An upgrade is recommended for this runner"
+msgstr "建議å°è©²åŸ·è¡Œå™¨é€²è¡Œæ›´æ–°"
+
msgid "Runners|Architecture"
msgstr "架構"
@@ -33509,6 +33785,9 @@ msgstr "複製說明"
msgid "Runners|Copy registration token"
msgstr "複製註冊令牌(權æ–)"
+msgid "Runners|Created %{timeAgo}"
+msgstr "%{timeAgo} 已建立 "
+
msgid "Runners|Delete %d runner"
msgid_plural "Runners|Delete %d runners"
msgstr[0] "刪除 %d 個 執行器(runner)"
@@ -33555,6 +33834,9 @@ msgstr "輸入秒數。此逾時優先於為專案設定的較低逾時。"
msgid "Runners|Executor"
msgstr "執行者"
+msgid "Runners|Filter projects"
+msgstr ""
+
msgid "Runners|Get started with runners"
msgstr "執行器入門"
@@ -33579,6 +33861,9 @@ msgstr "作業"
msgid "Runners|Last contact"
msgstr "最後一次è¯çµ¡"
+msgid "Runners|Last contact: %{timeAgo}"
+msgstr "最後è¯ç¹«ï¼š%{timeAgo}"
+
msgid "Runners|Locked to this project"
msgstr "已鎖定此專案"
@@ -33624,24 +33909,15 @@ msgstr "ä¸æŽ¥å—作業"
msgid "Runners|Offline"
msgstr "離線"
-msgid "Runners|Offline runners"
-msgstr "離線執行器"
-
msgid "Runners|Offline:"
msgstr "離線:"
msgid "Runners|Online"
msgstr "在線"
-msgid "Runners|Online runners"
-msgstr "在線執行器"
-
msgid "Runners|Online:"
msgstr "在線:"
-msgid "Runners|Outdated"
-msgstr "å·²éŽæ™‚"
-
msgid "Runners|Pause from accepting jobs"
msgstr "æš«åœæŽ¥å—作業"
@@ -33787,9 +34063,6 @@ msgstr "å–得標籤建議時出ç¾å•é¡Œ"
msgid "Runners|Stale"
msgstr "éŽæœŸ"
-msgid "Runners|Stale runners"
-msgstr "éŽæœŸçš„執行器(runners)"
-
msgid "Runners|Stale:"
msgstr "éŽæœŸï¼š"
@@ -33872,9 +34145,18 @@ msgstr "值"
msgid "Runners|Version"
msgstr "版本"
+msgid "Runners|Version %{version}"
+msgstr "版本 %{version}"
+
msgid "Runners|View installation instructions"
msgstr "檢視安è£èªªæ˜Ž"
+msgid "Runners|We want you to be able to manage your runners easily and efficiently from this page, and we are making changes to get there. Give us feedback on how we're doing!"
+msgstr ""
+
+msgid "Runners|We've made some changes and want your feedback"
+msgstr ""
+
msgid "Runners|Windows 2019 Shell with manual scaling and optional scheduling. %{percentage} spot."
msgstr "Windows 2019 Shell,具有手動縮放和å¯é¸æŽ’程的功能。 %{percentage} 點。"
@@ -33893,42 +34175,18 @@ msgstr "您已使用了%{quotaUsed},超出了共享æµæ°´ç·šæ™‚é–“é…é¡é™åˆ¶
msgid "Runners|active"
msgstr "啟用"
-msgid "Runners|available"
-msgstr "å¯ç”¨çš„"
-
msgid "Runners|group"
msgstr "群組"
-msgid "Runners|never contacted"
-msgstr "從未連接éŽ"
-
-msgid "Runners|offline"
-msgstr "離線"
-
-msgid "Runners|online"
-msgstr "在線"
-
msgid "Runners|paused"
msgstr "æš«åœ"
-msgid "Runners|recommended"
-msgstr "推薦的"
-
msgid "Runners|shared"
msgstr "共用"
msgid "Runners|specific"
msgstr "特定"
-msgid "Runners|stale"
-msgstr "éŽæœŸ"
-
-msgid "Runners|upgrade available"
-msgstr "å¯ç”¨å‡ç´š"
-
-msgid "Runners|upgrade recommended"
-msgstr "建議å‡ç´š"
-
msgid "Runner|Owner"
msgstr "所有者"
@@ -34022,6 +34280,9 @@ msgstr "SSL驗證:"
msgid "SSL verification"
msgstr "SSL é©—è­‰"
+msgid "Sat"
+msgstr "星期六"
+
msgid "Satisfied"
msgstr "滿æ„"
@@ -34059,7 +34320,7 @@ msgid "Save password"
msgstr "儲存密碼"
msgid "Save pipeline schedule"
-msgstr "儲存æµæ°´ç·šè¨ˆåŠƒ"
+msgstr "儲存æµæ°´ç·šæŽ’程"
msgid "Saving"
msgstr "儲存中"
@@ -34067,12 +34328,27 @@ msgstr "儲存中"
msgid "Saving project."
msgstr "正在儲存專案。"
+msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} actions for the %{scopes} %{branches}"
+msgstr "%{ifLabelStart}å‡è¨­%{ifLabelEnd} %{rules} 動作為 %{scopes} %{branches}"
+
msgid "ScanExecutionPolicy|%{ifLabelStart}if%{ifLabelEnd} %{rules} for the %{branches} branch(es)"
msgstr "%{ifLabelStart}如果%{ifLabelEnd} %{rules} 用於 %{branches} 分支"
+msgid "ScanExecutionPolicy|%{period} %{days} at %{time}"
+msgstr "%{period} %{days} æ–¼ %{time}"
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run"
+msgstr "%{thenLabelStart}然後%{thenLabelEnd} éœ€è¦ %{scan} 掃ææ‰èƒ½é‹è¡Œ"
+
+msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}"
+msgstr ""
+
msgid "ScanExecutionPolicy|A pipeline is run"
msgstr "æµæ°´ç·šåŸ·è¡Œä¸­"
+msgid "ScanExecutionPolicy|Scanner profile"
+msgstr ""
+
msgid "ScanExecutionPolicy|Schedule"
msgstr "排程"
@@ -34082,6 +34358,18 @@ msgstr "排程è¦å‰‡å…ƒä»¶"
msgid "ScanExecutionPolicy|Select branches"
msgstr "é¸æ“‡åˆ†æ”¯"
+msgid "ScanExecutionPolicy|Select scanner profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Select site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|Site profile"
+msgstr ""
+
+msgid "ScanExecutionPolicy|branch"
+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} çš„æ¼æ´ž"
@@ -34137,7 +34425,7 @@ msgid "Schedules to merge this merge request (%{strategy})."
msgstr "計劃åˆä½µæ­¤åˆä½µè«‹æ±‚ (%{strategy})。"
msgid "Scheduling Pipelines"
-msgstr "æµæ°´ç·šè¨ˆåŠƒ"
+msgstr "æµæ°´ç·šæŽ’程"
msgid "Scope"
msgstr "範åœ"
@@ -34651,6 +34939,9 @@ msgstr ".yaml é è¦½"
msgid "SecurityOrchestration|Actions"
msgstr "行動"
+msgid "SecurityOrchestration|Add action"
+msgstr "增加動作"
+
msgid "SecurityOrchestration|Add rule"
msgstr "加入è¦å‰‡"
@@ -34906,6 +35197,9 @@ msgstr "建立新的安全政策時出ç¾å•é¡Œ"
msgid "SecurityOrchestration|This %{namespaceType} does not contain any security policies."
msgstr "該 %{namespaceType} ä¸åŒ…å«ä»»ä½•å®‰å…¨æ”¿ç­–。"
+msgid "SecurityOrchestration|This %{namespaceType} is not linked to a security policy project"
+msgstr "這個 %{namespaceType} 未連çµè‡³å®‰å…¨æ”¿ç­–專案"
+
msgid "SecurityOrchestration|This group"
msgstr "該群組"
@@ -35840,7 +36134,7 @@ msgid "SetStatusModal|Clear status"
msgstr "清除狀態"
msgid "SetStatusModal|Clear status after"
-msgstr "清除狀態之後"
+msgstr "外少時間之後清除狀態"
msgid "SetStatusModal|Edit status"
msgstr "編輯狀態"
@@ -36288,12 +36582,21 @@ msgstr "退出é é¢URL"
msgid "Sign-up restrictions"
msgstr "註冊é™åˆ¶"
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr "點擊 %{button_text} 或通éŽç¬¬ä¸‰æ–¹è¨»å†Šï¼Œæ‚¨æŽ¥å— GitLab%{link_start} 使用æ¢æ¬¾ä¸¦ç¢ºèªéš±ç§æ”¿ç­–å’Œ Cookie 政策 %{link_end}"
+
+msgid "SignUp|By clicking %{button_text} or registering through a third party you accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}"
+msgstr "點擊%{button_text}或通éŽç¬¬ä¸‰æ–¹è¨»å†Šå³è¡¨ç¤ºæ‚¨æŽ¥å—%{link_start}使用æ¢æ¬¾ä¸¦æ‰¿èªéš±ç§æ”¿ç­–å’ŒCookie政策%{link_end}"
+
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr "點擊 %{button_text},我åŒæ„æˆ‘å·²é–±è®€ä¸¦æŽ¥å— %{link_start}使用æ¢æ¬¾å’Œéš±ç§æ”¿ç­–%{link_end}"
msgid "SignUp|By clicking %{button_text}, I agree that I have read and accepted the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}"
msgstr "點擊 %{button_text},我åŒæ„æˆ‘å·²é–±è®€ä¸¦æŽ¥å— GitLab %{link_start}使用æ¢æ¬¾å’Œéš±ç§æ”¿ç­–%{link_end}"
+msgid "SignUp|By signing in you accept the %{link_start}Terms of Use and acknowledge the Privacy Policy and Cookie Policy%{link_end}."
+msgstr "登入å³è¡¨ç¤ºæ‚¨æŽ¥å— %{link_start}使用æ¢æ¬¾ä¸¦ç¢ºèªéš±ç§æ”¿ç­–å’Œ Cookie 政策%{link_end}。"
+
msgid "SignUp|First name is too long (maximum is %{max_length} characters)."
msgstr "å字太長(最多%{max_length}å­—å…ƒ)。"
@@ -36567,6 +36870,9 @@ msgstr "嘗試載入議題è¯çµ¡äººæ™‚發生錯誤。"
msgid "Something went wrong when reordering designs. Please try again"
msgstr "é‡æ–°æŽ’åºè¨­è¨ˆæ™‚出了點å•é¡Œã€‚è«‹å†è©¦ä¸€æ¬¡"
+msgid "Something went wrong while adding timeline event."
+msgstr ""
+
msgid "Something went wrong while adding your award. Please try again."
msgstr "加入讚賞時發生錯誤。請å†è©¦ä¸€æ¬¡ã€‚"
@@ -36657,6 +36963,9 @@ msgstr "å–å¾—Let's Encrypt憑證時發生錯誤。"
msgid "Something went wrong while promoting the issue to an epic. Please try again."
msgstr "將議題å‡ç´šåˆ°å²è©©æ™‚發生錯誤。請å†è©¦ä¸€æ¬¡ã€‚"
+msgid "Something went wrong while promoting the note to timeline event."
+msgstr "將備註æå‡åˆ°æ™‚間線事件時發生錯誤。"
+
msgid "Something went wrong while reopening a requirement."
msgstr "é‡æ–°æ‰“開需求時發生錯誤。"
@@ -36922,7 +37231,7 @@ msgid "Source branch: %{source_branch_open}%{source_branch}%{source_branch_close
msgstr "來æºåˆ†æ”¯: %{source_branch_open}%{source_branch}%{source_branch_close}"
msgid "Source code (%{fileExtension})"
-msgstr "來æºç¨‹å¼ç¢¼(%{fileExtension})"
+msgstr "版本程å¼ç¢¼(%{fileExtension})"
msgid "Source is not available"
msgstr "來æºä¸å¯ç”¨"
@@ -37041,6 +37350,9 @@ msgstr "壓縮æ交訊æ¯"
msgid "Squash commits"
msgstr "壓縮æ交"
+msgid "Squash commits when merge request is accepted."
+msgstr "當åˆä½µè«‹æ±‚被接å—時,壓縮åˆä½µæ交。"
+
msgid "Stack trace"
msgstr "堆疊追蹤"
@@ -37048,17 +37360,14 @@ msgid "Stacktrace snippet"
msgstr "堆疊追蹤程å¼ç¢¼ç‰‡æ®µ"
msgid "Stage"
-msgstr "æš«å­˜"
+msgstr "階段"
msgid "Stage:"
-msgstr "æš«å­˜:"
+msgstr "階段:"
msgid "Standard"
msgstr "標準"
-msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
-msgstr "把一個標記加上星號,å¯å°‡å…¶è®Šç‚ºå„ªå…ˆæ¨™è¨˜ã€‚é€éŽæ‹–放來調整優先標記的順åºï¼Œå¯ä»¥æ”¹è®Šä»–們的相å°å„ªå…ˆåº¦ã€‚"
-
msgid "Star labels to start sorting by priority"
msgstr "標記星號以優先排åº"
@@ -37075,7 +37384,7 @@ msgid "Starred Projects' Activity"
msgstr "收è—(星號)專案的動態"
msgid "Starred projects"
-msgstr "星號專案"
+msgstr "收è—(星號)專案"
msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
msgstr "å­˜å–æŸä¸€å°ˆæ¡ˆé é¢ä¸¦é»žæ“Šæ˜Ÿå½¢åœ–標後å¯ä»¥åœ¨æ­¤é é¢ä¸Šæ‰¾åˆ°è©²å°ˆæ¡ˆã€‚"
@@ -37797,6 +38106,9 @@ msgstr "摘è¦/註釋"
msgid "Summary comment (optional)"
msgstr "摘è¦è©•è«–(å¯é¸ï¼‰"
+msgid "Sun"
+msgstr "星期日"
+
msgid "Sunday"
msgstr "星期日"
@@ -39046,6 +39358,9 @@ msgstr "é é¢å·²é€¾æ™‚,無法顯示。"
msgid "The parent epic is confidential and can only contain confidential epics and issues"
msgstr "父å²è©©æ˜¯æ©Ÿå¯†çš„,åªèƒ½åŒ…å«æ©Ÿå¯†å²è©©å’Œè­°é¡Œ"
+msgid "The parsed YAML is too big"
+msgstr "被解æžçš„ YAML 文件太大"
+
msgid "The password for the Jenkins server."
msgstr "Jenkins 伺æœå™¨çš„密碼。"
@@ -39142,6 +39457,12 @@ msgstr "該程å¼ç¢¼ç‰‡æ®µå°é™¤å¤–部使用者外的所有登入使用者å¯è¦‹
msgid "The source project of this merge request has been removed."
msgstr "æ­¤åˆä½µè«‹æ±‚的來æºå°ˆæ¡ˆå·²è¢«ç§»é™¤ã€‚"
+msgid "The source topic and the target topic are identical."
+msgstr ""
+
+msgid "The source topic is not a topic."
+msgstr ""
+
msgid "The specified tab is invalid, please select another"
msgstr "指定é ç±¤ç„¡æ•ˆï¼Œè«‹é¸æ“‡å¦ä¸€å€‹"
@@ -39154,6 +39475,9 @@ msgstr "該主題將用作新議題的標題,訊æ¯å°‡ä½œç‚ºèªªæ˜Žã€‚ æ”¯æ´ %
msgid "The tag name can't be changed for an existing release."
msgstr "å°æ–¼ç¾æœ‰ç™¼å¸ƒï¼Œä¸èƒ½æ›´æ”¹æ¨™ç±¤å稱。"
+msgid "The target topic is not a topic."
+msgstr ""
+
msgid "The time period in seconds that the maximum requests per project limit applies to."
msgstr "æ¯å€‹å°ˆæ¡ˆæœ€å¤§è«‹æ±‚所é©ç”¨çš„時間間隔(秒)。"
@@ -39202,12 +39526,12 @@ msgstr "主題"
msgid "There are currently no events."
msgstr "當å‰æ²’有事件。"
+msgid "There are currently no mirrored repositories."
+msgstr "當å‰æ²’有已é¡åƒçš„版本庫。"
+
msgid "There are merge conflicts"
msgstr "存在åˆä½µè¡çª"
-msgid "There are no %{replicableTypeName} to show"
-msgstr "沒有å¯é¡¯ç¤ºçš„%{replicableTypeName}"
-
msgid "There are no GPG keys associated with this account."
msgstr "沒有與此帳號關è¯çš„GPG金鑰。"
@@ -39310,9 +39634,6 @@ msgstr "為了ä¿è­·è©²ç³»çµ±ï¼Œå¯¦è¡Œäº†å¹¾ç¨®é€ŸçŽ‡é™åˆ¶ã€‚"
msgid "There are several size limits in place."
msgstr "有幾個大å°é™åˆ¶ã€‚"
-msgid "There are unsubmitted review comments."
-msgstr "有未æ交的審閱留言。"
-
msgid "There is already a repository with that name on disk"
msgstr "ç£ç¢Ÿä¸Šå·²å­˜åœ¨å…·æœ‰è©²å稱的版本庫"
@@ -39664,6 +39985,9 @@ msgstr "該å€å¡Šç‚ºè‡ªæˆ‘引用"
msgid "This board's scope is reduced"
msgstr "此看æ¿ç¯„åœç¸®å°äº†"
+msgid "This change will remove %{strongOpen}ALL%{strongClose} Premium and Ultimate features for %{strongOpen}ALL%{strongClose} SaaS customers and make tests start failing."
+msgstr "此變更將移除 %{strongOpen}所有%{strongClose} SaaS 客戶的 %{strongOpen}全部%{strongClose} Premium å’Œ Ultimate 功能,並開始造æˆæ¸¬è©¦å¤±æ•—。"
+
msgid "This chart could not be displayed"
msgstr "無法顯示此圖表"
@@ -39688,8 +40012,8 @@ msgstr "æ­¤æ交使用%{strong_open}已驗證的%{strong_close}ç°½å進行簽ç
msgid "This commit was signed with a different user's verified signature."
msgstr "æ­¤æ交使用其他使用者的已驗證簽å進行簽å。"
-msgid "This commit was signed with a verified signature, but the committer email is %{strong_open}not verified%{strong_close} to belong to the same user."
-msgstr "æ­¤æ交已使用經éŽé©—證的簽å進行簽署,但%{strong_open}尚未驗證%{strong_close}æ交者電å­éƒµä»¶å±¬æ–¼åŒä¸€ä½¿ç”¨è€…。"
+msgid "This commit was signed with a verified signature, but the committer email is not associated with the GPG Key."
+msgstr "該æ交使用已驗證的簽章進行簽å,但æ交者電å­éƒµä»¶æœªèˆ‡ GPG 密鑰關è¯ã€‚"
msgid "This commit was signed with an %{strong_open}unverified%{strong_close} signature."
msgstr "æ­¤æ交使用%{strong_open}未經驗證的%{strong_close}ç°½å進行簽署。"
@@ -40069,6 +40393,9 @@ msgstr "該專案在 %{strong_start}%{license_name}%{strong_end}下å–得授權è
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 "該專案是公開的,éžæˆå“¡å¯ä»¥çŒœå‡ºæœå‹™å°é›»å­éƒµä»¶åœ°å€ï¼Œå› ç‚ºå®ƒåŒ…å«ç¾¤çµ„和專案å稱。 %{linkStart}如何建立自訂電å­éƒµä»¶åœ°å€ï¼Ÿ%{linkEnd}"
+
msgid "This project manages its dependencies using %{strong_start}%{manager_name}%{strong_end}"
msgstr "該專案使用%{strong_start}%{manager_name}%{strong_end}管ç†å…¶ä¾è³´é—œä¿‚"
@@ -40081,9 +40408,6 @@ msgstr "該專案將於 %{date} 刪除"
msgid "This project will be deleted on %{date} since its parent group '%{parent_group_name}' has been scheduled for deletion."
msgstr "該專案將於 %{date} 刪除,因為它的父群組 '%{parent_group_name}' 已排入刪除排程。"
-msgid "This project will live in your group %{strong_open}%{namespace}%{strong_close}. A project is where you store your files (repository), plan your work (issues), publish your documentation (wiki), and so much more."
-msgstr "該專案將存在於您的群組 %{strong_open}%{namespace}%{strong_close} 中。專案是您儲存文件(版本庫)ã€å·¥ä½œè¨ˆåŠƒï¼ˆè­°é¡Œï¼‰ã€ç™¼å¸ƒæ–‡æª”(wiki)等等的地方。"
-
msgid "This release was created with a date in the past. Evidence collection at the moment of the release is unavailable."
msgstr "該發布版本是使用éŽåŽ»çš„日期建立的, 無法在發布時收集證據。"
@@ -40186,6 +40510,9 @@ msgstr "單次推é€ä¸­æ›´æ”¹ï¼ˆåˆ†æ”¯æˆ–標籤)的閾值數é‡ï¼Œé«˜æ–¼è©²é–¾
msgid "Throughput"
msgstr "åžåé‡"
+msgid "Thu"
+msgstr "星期四"
+
msgid "Thursday"
msgstr "星期四"
@@ -40396,6 +40723,9 @@ msgstr "ç¾åœ¨"
msgid "Timeago|right now"
msgstr "ç«‹å³"
+msgid "Timeline event added successfully."
+msgstr ""
+
msgid "Timeline|Turn recent updates view off"
msgstr "關閉最近更新視圖"
@@ -40423,6 +40753,24 @@ msgstr "最快的 Gitaly æ“作逾時(以秒為單ä½ï¼‰ã€‚"
msgid "Timezone"
msgstr "時å€"
+msgid "Time|A"
+msgstr "A"
+
+msgid "Time|AM"
+msgstr "AM"
+
+msgid "Time|P"
+msgstr "P"
+
+msgid "Time|PM"
+msgstr "PM"
+
+msgid "Time|a"
+msgstr "a"
+
+msgid "Time|am"
+msgstr "am"
+
msgid "Time|hr"
msgid_plural "Time|hrs"
msgstr[0] "å°æ™‚"
@@ -40431,6 +40779,12 @@ msgid "Time|min"
msgid_plural "Time|mins"
msgstr[0] "分é˜"
+msgid "Time|p"
+msgstr "p"
+
+msgid "Time|pm"
+msgstr "pm"
+
msgid "Time|s"
msgstr "秒"
@@ -40485,6 +40839,9 @@ msgstr "è¦åŠ å…¥è‡ªè¨‚後綴,請設定æœå‹™å°é›»å­éƒµä»¶åœ°å€ã€‚ %{linkSt
msgid "To add the entry manually, provide the following details to the application on your phone."
msgstr "如需手動加入æ¢ç›®ï¼Œè«‹åœ¨æ‰‹æ©Ÿæ‡‰ç”¨ä¸­æ供以下訊æ¯ã€‚"
+msgid "To approve this merge request, please enter your password. This project requires all approvals to be authenticated."
+msgstr "è¦æ ¸å‡†è©²åˆä½µè«‹æ±‚,請輸入您的密碼,該專案需è¦å°æ‰€æœ‰æ ¸å‡†é€²è¡Œé©—證。"
+
msgid "To complete registration, we need additional details from you."
msgstr "è¦å®Œæˆè¨»å†Šï¼Œæˆ‘們需è¦æ‚¨æ供更多詳細信æ¯ã€‚"
@@ -40647,9 +41004,6 @@ msgstr "è¦æ“´å¤§æœå°‹ç¯„åœï¼Œè«‹æ›´æ”¹æˆ–移除上é¢çš„éŽæ¿¾å™¨"
msgid "To widen your search, change or remove filters above."
msgstr "è¦æ“´å¤§æœå°‹ç¯„åœï¼Œè«‹æ›´æ”¹æˆ–移除上é¢çš„éŽæ¿¾å™¨ã€‚"
-msgid "To-Do"
-msgstr "待辦"
-
msgid "To-Do List"
msgstr "待辦事項列表"
@@ -40674,6 +41028,18 @@ msgstr "ä¾ç¾¤çµ„éŽæ¿¾"
msgid "Todos|Filter by project"
msgstr "ä¾å°ˆæ¡ˆéŽæ¿¾"
+msgid "Todos|Give yourself a pat on the back!"
+msgstr ""
+
+msgid "Todos|Good job! Looks like you don't have anything left on your To-Do List"
+msgstr ""
+
+msgid "Todos|Henceforth, you shall be known as \"To-Do Destroyer\""
+msgstr ""
+
+msgid "Todos|Isn't an empty To-Do List beautiful?"
+msgstr ""
+
msgid "Todos|It's how you always know what to work on next."
msgstr "這是您總是知é“下一步è¦åšä»€éº¼çš„æ–¹å¼ã€‚"
@@ -40683,6 +41049,9 @@ msgstr "標示為全部完æˆ"
msgid "Todos|Nothing is on your to-do list. Nice work!"
msgstr "您的待辦事項列表中沒有任何事項。"
+msgid "Todos|Nothing left to do. High five!"
+msgstr ""
+
msgid "Todos|Undo mark all as done"
msgstr "撤銷標示為全部完æˆ"
@@ -40791,9 +41160,18 @@ msgstr "åƒè€ƒè³‡æ–™å¤ªå¤šã€‚快速æ“作僅é™æ–¼æœ€å¤š %{max_count} 個使用è
msgid "Too many users found. Quick actions are limited to at most %{max_count} users"
msgstr "發ç¾å¤ªå¤šä½¿ç”¨è€…。快速æ“作僅é™æ–¼æœ€å¤š %{max_count} 個使用者"
+msgid "TopNav|Explore"
+msgstr ""
+
msgid "TopNav|Go back"
msgstr "返回"
+msgid "TopNav|Switch to"
+msgstr ""
+
+msgid "TopNav|Your dashboards"
+msgstr ""
+
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
msgstr "主題 %{source_topic} å·²æˆåŠŸåˆä½µè‡³ä¸»é¡Œ %{target_topic}。"
@@ -40818,9 +41196,21 @@ msgstr "主題標題"
msgid "Topic was successfully updated."
msgstr "主題已æˆåŠŸæ›´æ–°ã€‚"
+msgid "TopicSelect|No matching results"
+msgstr "沒有符åˆçš„çµæžœ"
+
+msgid "TopicSelect|Search topics"
+msgstr "æœå°‹ä¸»é¡Œ"
+
+msgid "TopicSelect|Select a topic"
+msgstr "é¸æ“‡ä¸€å€‹ä¸»é¡Œ"
+
msgid "Topics"
msgstr "主題"
+msgid "Topics could not be merged!"
+msgstr ""
+
msgid "Total"
msgstr "全部"
@@ -40989,39 +41379,12 @@ msgstr "您的試用版在 %{boldStart}%{trialEndDate}%{boldEnd}çµæŸï¼Œæˆ‘們å
msgid "Trial|Allowed characters: +, 0-9, -, and spaces."
msgstr "å…許的字元:+ã€0-9ã€- 和空格。"
-msgid "Trial|Company name"
-msgstr "å…¬å¸å稱"
-
msgid "Trial|Continue"
msgstr "繼續"
-msgid "Trial|Continue using the basic features of GitLab for free."
-msgstr "繼續å…費使用基本功能。"
-
-msgid "Trial|Country"
-msgstr "國家"
-
msgid "Trial|Dismiss"
msgstr "撤回"
-msgid "Trial|GitLab Ultimate trial (optional)"
-msgstr "GitLab Ultimate 試用版(å¯é¸ï¼‰"
-
-msgid "Trial|Number of employees"
-msgstr "員工人數"
-
-msgid "Trial|Please select a country"
-msgstr "è«‹é¸æ“‡åœ‹å®¶"
-
-msgid "Trial|Telephone number"
-msgstr "電話號碼"
-
-msgid "Trial|Upgrade to Ultimate to keep using GitLab with advanced features."
-msgstr "å‡ç´šç‚ºæ——艦版以ä¿æŒä½¿ç”¨ GitLab 進階功能。"
-
-msgid "Trial|We will activate your trial on your group after you complete this step. After 30 days, you can:"
-msgstr "在您完æˆæ­¤æ­¥é©Ÿå¾Œï¼Œæˆ‘們將在您的群組中啟用您的試用。 30 天後,您å¯ä»¥ï¼š"
-
msgid "Trial|Your GitLab Ultimate trial lasts for 30 days, but you can keep your free GitLab account forever. We just need some additional information to activate your trial."
msgstr "您的 GitLab Ultimate 試用期為 30 天,但您å¯ä»¥æ°¸ä¹…ä¿ç•™å…費的 GitLab 帳號。我們åªéœ€è¦ä¸€äº›é¡å¤–的訊æ¯ä¾†å•Ÿç”¨æ‚¨çš„試用版。"
@@ -41127,21 +41490,18 @@ msgstr "嘗試與您的設備通信。請將其æ’å…¥(如需è¦)並立å³æŒ‰ä¸‹è
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
msgstr "嘗試與您的設備通信。請將其æ’入並立å³æŒ‰ä¸‹è¨­å‚™ä¸Šçš„按鈕。"
+msgid "Tue"
+msgstr "星期二"
+
msgid "Tuesday"
msgstr "星期二"
msgid "Turn off"
msgstr "關閉"
-msgid "Turn off notifications"
-msgstr "關閉通知"
-
msgid "Turn on"
msgstr "é–‹å•Ÿ"
-msgid "Turn on notifications"
-msgstr "開啟通知"
-
msgid "Twitter"
msgstr "Twitter"
@@ -41721,6 +42081,9 @@ 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 分é˜ä½¿ç”¨é‡"
+
msgid "UsageQuota|Code packages and container images."
msgstr "程å¼ç¢¼å¥—件和容器映åƒã€‚"
@@ -41748,6 +42111,9 @@ msgstr "Gitlab æ•´åˆçš„ Docker 容器註冊表,用於儲存 Docker 映åƒæª”ã
msgid "UsageQuota|Gitlab-integrated Docker Container Registry for storing Docker Images. %{linkStart}More information%{linkEnd}"
msgstr "Gitlab æ•´åˆçš„ Docker 容器註冊表,用於儲存 Docker 映åƒæª”。 %{linkStart}更多信æ¯%{linkEnd}"
+msgid "UsageQuota|Group settings &gt; Usage quotas"
+msgstr "群組設定 &gt; 使用é¡åº¦"
+
msgid "UsageQuota|Includes artifacts, repositories, wiki, uploads, and other items."
msgstr "包括產物ã€ç‰ˆæœ¬åº«ã€wikiã€ä¸Šå‚³æ–‡ä»¶å’Œå…¶å®ƒäº‹é …。"
@@ -41844,11 +42210,11 @@ msgstr "%{strong_start}%{context_name}%{strong_end} 群組將å—此影響。"
msgid "UsageQuota|The %{strong_start}%{context_name}%{strong_end} project will be affected by this. "
msgstr "%{strong_start}%{context_name}%{strong_end} 專案將å—此影響。"
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}Group settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more.%{link_end}"
-msgstr "命å空間目å‰æ­£åœ¨ä½¿ç”¨ %{strong_start}%{used_storage}%{strong_end} 的命å空間儲存,群組所有者å¯ä»¥å¾ž %{strong_start}群組設定 &gt; 使用é…é¡%{strong_end} 查看命å空間儲存使用情æ³ä»¥åŠè³¼è²·ï¼Œ%{docs_link_start}了解更多。%{link_end}"
+msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. Group owners can view namespace storage usage and purchase more from %{strong_start}%{usage_quotas_nav_instruction}%{strong_end}. %{docs_link_start}Learn more%{link_end}."
+msgstr "該命å空間目å‰æ­£åœ¨ä½¿ç”¨ %{strong_start}%{used_storage}%{strong_end} 的命å空間儲存。群組æ“有者å¯ä»¥å¾ž %{strong_start}%{usage_quotas_nav_instruction}%{strong_end} 查看命å空間儲存使用情æ³ä¸¦è³¼è²·æ›´å¤šã€‚ %{docs_link_start}更多資訊%{link_end}。"
-msgid "UsageQuota|The namespace is currently using %{strong_start}%{used_storage}%{strong_end} of namespace storage. View and manage your usage from %{strong_start}User settings &gt; Usage quotas%{strong_end}. %{docs_link_start}Learn more%{link_end} about how to reduce your storage."
-msgstr "命å空間目å‰æ­£åœ¨ä½¿ç”¨ %{strong_start}%{used_storage}%{strong_end} 的命å空間儲存。從 %{strong_start}使用者設定 &gt; 使用é¡åº¦%{strong_end}檢視和管ç†æ‚¨çš„使用情æ³ã€‚ %{docs_link_start}詳細了解%{link_end}如何減少您的儲存空間。"
+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 "下表顯示了目å‰å€é–“的使用情æ³"
@@ -41913,8 +42279,8 @@ msgstr "您的專案中資æºä½¿ç”¨æƒ…æ³"
msgid "UsageQuota|Usage quotas help link"
msgstr "使用é…é¡å¹«åŠ©é€£çµ"
-msgid "UsageQuota|Usage since %{usageSince}"
-msgstr "自 %{usageSince} 以來的使用情æ³"
+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}é™åˆ¶æ™‚被鎖定的專案。"
@@ -42075,6 +42441,9 @@ msgstr "使用議題計數"
msgid "Use issue weight"
msgstr "使用議題權é‡"
+msgid "Use issues to collaborate on ideas, solve problems, and plan work"
+msgstr "使用議題就想法進行å”作ã€è§£æ±ºå•é¡Œå’Œè¨ˆåŠƒå·¥ä½œ"
+
msgid "Use one line per URI"
msgstr "æ¯å€‹URIå ä¸€è¡Œ"
@@ -42475,8 +42844,8 @@ msgstr "使用者 API 速率é™åˆ¶"
msgid "Users can launch a development environment from a GitLab browser tab when the %{linkStart}Gitpod%{linkEnd} integration is enabled."
msgstr "%{linkStart}Gitpod%{linkEnd} æ•´åˆå¾Œï¼Œä½¿ç”¨è€…å¯ä»¥å¾ž GitLab ç€è¦½å™¨é¸é …é å•Ÿå‹•é–‹ç™¼ç’°å¢ƒã€‚"
-msgid "Users can reactivate their account by signing in. %{link_start}Learn more%{link_end}"
-msgstr "使用者å¯ä»¥é€šéŽç™»å…¥é‡æ–°å•Ÿç”¨ä»–們的帳號。%{link_start}了解更多%{link_end}"
+msgid "Users can reactivate their account by signing in. %{link_start}Learn more.%{link_end}"
+msgstr ""
msgid "Users can render diagrams in AsciiDoc, Markdown, reStructuredText, and Textile documents using Kroki."
msgstr "使用者å¯ä»¥ä½¿ç”¨ Kroki 在 AsciiDocã€Markdownã€reStructuredText å’Œ Textile 文件中渲染圖表。"
@@ -42602,7 +42971,7 @@ msgid "ValueStreamAnalytics|Average number of deployments to production per day.
msgstr "æ¯å¤©éƒ¨ç½²åˆ°æ­£å¼ç’°å¢ƒçš„å¹³å‡æ•¸ã€‚"
msgid "ValueStreamAnalytics|DORA metrics"
-msgstr "DORA 指標"
+msgstr "DORA(DevOps Research and Assessment) 指標"
msgid "ValueStreamAnalytics|Dashboard"
msgstr "儀表æ¿"
@@ -42769,11 +43138,14 @@ msgstr "查看警報詳細訊æ¯ã€‚"
msgid "View all environments."
msgstr "查看所有環境。"
+msgid "View all groups"
+msgstr ""
+
msgid "View all issues"
msgstr "查看所有議題"
-msgid "View all personal projects"
-msgstr "查看所有個人的專案"
+msgid "View all projects"
+msgstr ""
msgid "View blame"
msgstr "查看責任歸屬(blame)"
@@ -43465,6 +43837,12 @@ msgstr "å‰å¾€åˆ†å‰"
msgid "WebIDE|Merge request"
msgstr "åˆä½µè«‹æ±‚"
+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|This project does not accept unsigned commits."
msgstr "此專案ä¸æŽ¥å—未簽åçš„æ交。"
@@ -43546,6 +43924,9 @@ msgstr "已建立或更新發布。"
msgid "Webhooks|A subgroup is created or removed."
msgstr "已建立或移除å­ç¾¤çµ„。"
+msgid "Webhooks|A webhook in this project was automatically disabled after being retried multiple times."
+msgstr "該專案內的 webhook 在多次é‡è©¦å¾Œå°‡è‡ªå‹•è¢«åœç”¨ã€‚"
+
msgid "Webhooks|A wiki page is created or updated."
msgstr "已建立或更新 wiki é é¢ã€‚"
@@ -43588,6 +43969,9 @@ msgstr "連接失敗"
msgid "Webhooks|Feature flag events"
msgstr "功能標誌事件"
+msgid "Webhooks|Go to webhooks"
+msgstr "到 webhooks"
+
msgid "Webhooks|Issues events"
msgstr "議題事件"
@@ -43645,6 +44029,9 @@ msgstr "如果 URL 包å«ä¸€å€‹æˆ–多個特殊字元,則必須進行百分號ç
msgid "Webhooks|Used to validate received payloads. Sent with the request in the %{code_start}X-Gitlab-Token HTTP%{code_end} header."
msgstr "用於驗證接收到的有效資訊。與 %{code_start}X-Gitlab-Token HTTP%{code_end} 標頭中的請求一起發é€ã€‚"
+msgid "Webhooks|Webhook disabled"
+msgstr "Webhook å·²åœç”¨"
+
msgid "Webhooks|Webhook failed to connect"
msgstr "Webhook 連接失敗"
@@ -43663,6 +44050,9 @@ msgstr "網站"
msgid "Website:"
msgstr "網站:"
+msgid "Wed"
+msgstr "星期三"
+
msgid "Wednesday"
msgstr "星期三"
@@ -43714,6 +44104,9 @@ msgstr "設定有什麼影響?"
msgid "What does this command do?"
msgstr "這個指令有什麼作用?"
+msgid "What is GitLab Runner?"
+msgstr "何謂 GitLab 執行器 (Runner) ?"
+
msgid "What is Markdown?"
msgstr "什麼是 Markdown?"
@@ -43923,9 +44316,6 @@ msgstr "刪除é é¢%{pageTitle}?"
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
msgstr "有人在您編輯é é¢çš„åŒæ™‚編輯了é é¢ã€‚請查看%{wikiLinkStart}é é¢%{wikiLinkEnd}並確ä¿æ‚¨çš„更改ä¸æœƒç„¡æ„中刪除它們的更改。"
-msgid "WikiPage|An error occurred while trying to render the content editor. Please try again later."
-msgstr "嘗試顯示內容編輯器時發生錯誤,請ç¨å¾Œå†è©¦ã€‚"
-
msgid "WikiPage|Cancel"
msgstr "å–消"
@@ -43950,9 +44340,6 @@ msgstr "了解更多。"
msgid "WikiPage|Page title"
msgstr "é é¢æ¨™é¡Œ"
-msgid "WikiPage|Retry"
-msgstr "é‡è©¦"
-
msgid "WikiPage|Save changes"
msgstr "ä¿å­˜æ›´æ”¹"
@@ -44040,11 +44427,11 @@ msgstr "正在進行中(開啟且未指派)"
msgid "Work in progress Limit"
msgstr "「進行中(Work in progress, WIP)ã€çš„é™åˆ¶"
-msgid "WorkItem|A task provides the ability to break down your work into smaller pieces tied to an issue. Tasks are the first items using our new %{workItemsLink} objects. Additional work item types will be coming soon."
-msgstr "任務æ供了將您的工作分解為å°å¡Šèˆ‡è­°é¡Œç›¸é—œçš„能力,任務是使用我們新的 %{workItemsLink} å°è±¡çš„第一個項目,其他更多的工作項類型將陸續推出。"
+msgid "WorkItem|%{workItemType} deleted"
+msgstr "%{workItemType} 已刪除"
-msgid "WorkItem|Add a task"
-msgstr "增加任務"
+msgid "WorkItem|Add"
+msgstr "新增"
msgid "WorkItem|Add a title"
msgstr "增加標題"
@@ -44061,8 +44448,8 @@ msgstr "增加任務"
msgid "WorkItem|Are you sure you want to cancel editing?"
msgstr "您確定è¦å–消編輯嗎?"
-msgid "WorkItem|Are you sure you want to delete the task? This action cannot be reversed."
-msgstr "您確定è¦åˆªé™¤è©²ä»»å‹™å—Žï¼Ÿæ­¤æ“作無法回復。"
+msgid "WorkItem|Are you sure you want to delete the %{workItemType}? This action cannot be reversed."
+msgstr "您確定è¦åˆªé™¤ %{workItemType} 嗎?該æ“作無法撤消。"
msgid "WorkItem|Assignee"
msgid_plural "WorkItem|Assignees"
@@ -44071,17 +44458,14 @@ msgstr[0] "指派者"
msgid "WorkItem|Cancel"
msgstr "å–消"
-msgid "WorkItem|Child items"
-msgstr "å­é …"
-
msgid "WorkItem|Child removed"
msgstr "已移除å­é …"
msgid "WorkItem|Closed"
msgstr "已關閉"
-msgid "WorkItem|Collapse child items"
-msgstr "收摺å­é …"
+msgid "WorkItem|Collapse tasks"
+msgstr "收折任務"
msgid "WorkItem|Create task"
msgstr "建立任務"
@@ -44089,20 +44473,32 @@ msgstr "建立任務"
msgid "WorkItem|Create work item"
msgstr "建立工作項"
-msgid "WorkItem|Delete task"
-msgstr "刪除任務"
+msgid "WorkItem|Delete %{workItemType}"
+msgstr "刪除 %{workItemType}"
+
+msgid "WorkItem|Expand tasks"
+msgstr "展開任務"
-msgid "WorkItem|Expand child items"
-msgstr "擴展å­é …"
+msgid "WorkItem|Incident"
+msgstr "事故"
msgid "WorkItem|Introducing tasks"
msgstr "任務介紹"
-msgid "WorkItem|Learn about tasks"
-msgstr "了解任務"
+msgid "WorkItem|Issue"
+msgstr "議題"
+
+msgid "WorkItem|Learn about tasks."
+msgstr "了解任務。"
+
+msgid "WorkItem|No tasks are currently assigned. Use tasks to break down this issue into smaller parts."
+msgstr "ç›®å‰æœªè¢«æŒ‡æ´¾ä»»å‹™ï¼Œä½¿ç”¨ä»»å‹™å°‡è©²è­°é¡Œæ‹†åˆ†æˆæ›´å°çš„部分。"
-msgid "WorkItem|No child items are currently assigned. Use child items to prioritize tasks that your team should complete in order to accomplish your goals!"
-msgstr "ç›®å‰æœªåˆ†é…å­é …。使用å­é …目來確定您的團隊應該完æˆçš„任務優先級,以實ç¾æ‚¨çš„目標ï¼"
+msgid "WorkItem|None"
+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 "åªæœ‰è‡³å°‘具有報告者角色的專案æˆå“¡ã€ä½œè€…å’Œå—指派者å¯ä»¥æŸ¥çœ‹æˆ–收到有關此任務的通知。"
msgid "WorkItem|Open"
msgstr "é–‹å•Ÿ"
@@ -44110,20 +44506,23 @@ msgstr "é–‹å•Ÿ"
msgid "WorkItem|Remove"
msgstr "移除"
+msgid "WorkItem|Requirements"
+msgstr "需求"
+
msgid "WorkItem|Select type"
msgstr "é¸æ“‡é¡žåž‹"
-msgid "WorkItem|Something went wrong when creating a task. Please try again"
-msgstr "建立任務時發生å•é¡Œï¼Œè«‹é‡è©¦"
+msgid "WorkItem|Something went wrong when creating %{workItemType}. Please try again."
+msgstr "建立 %{workItemType} 時發生錯誤,請é‡è©¦ã€‚"
-msgid "WorkItem|Something went wrong when creating a work item. Please try again"
-msgstr "建立工作項時出ç¾éŒ¯èª¤ï¼Œè«‹å†è©¦ä¸€æ¬¡ã€‚"
+msgid "WorkItem|Something went wrong when deleting the %{workItemType}. Please try again."
+msgstr "刪除 %{workItemType} 時發生錯誤,請é‡è©¦ã€‚"
msgid "WorkItem|Something went wrong when deleting the task. Please try again."
msgstr "刪除任務時發生錯誤,請é‡è©¦ã€‚"
-msgid "WorkItem|Something went wrong when deleting the work item. Please try again."
-msgstr "刪除作項時出ç¾éŒ¯èª¤ï¼Œè«‹å†è©¦ä¸€æ¬¡ã€‚"
+msgid "WorkItem|Something went wrong when fetching tasks. Please refresh this page."
+msgstr "å–得任務時發生錯誤,請刷新該é é¢ã€‚"
msgid "WorkItem|Something went wrong when fetching the work item. Please try again."
msgstr "å–得工作項時出ç¾éŒ¯èª¤ï¼Œè«‹å†è©¦ä¸€æ¬¡ã€‚"
@@ -44137,32 +44536,41 @@ msgstr "嘗試加入å­é …時發生錯誤,請é‡è©¦ã€‚"
msgid "WorkItem|Something went wrong when trying to create a child. Please try again."
msgstr "嘗試建立å­é …時發生錯誤,請å†è©¦ä¸€æ¬¡ã€‚"
+msgid "WorkItem|Something went wrong while updating the %{workItemType}. Please try again."
+msgstr "æ›´æ–° %{workItemType} 時發生錯誤,請é‡è©¦ã€‚"
+
msgid "WorkItem|Something went wrong while updating the work item. Please try again."
msgstr "更新工作項時出ç¾éŒ¯èª¤ï¼Œè«‹å†è©¦ä¸€æ¬¡ã€‚"
+msgid "WorkItem|Task"
+msgstr "任務"
+
msgid "WorkItem|Task deleted"
msgstr "任務已移除"
+msgid "WorkItem|Tasks"
+msgstr "任務"
+
+msgid "WorkItem|Test case"
+msgstr "測試案例"
+
msgid "WorkItem|Turn off confidentiality"
msgstr "關閉機密性"
msgid "WorkItem|Turn on confidentiality"
msgstr "開啟機密性"
-msgid "WorkItem|Type"
-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|Work Items"
msgstr "工作事項"
-msgid "WorkItem|Work item deleted"
-msgstr "已刪除的工作項"
-
-msgid "WorkItem|work items"
-msgstr "工作項"
+msgid "WorkItem|Work item"
+msgstr "工作項目"
msgid "Would you like to create a new branch?"
msgstr "您è¦å»ºç«‹ä¸€å€‹æ–°åˆ†æ”¯å—Žï¼Ÿ"
@@ -44384,6 +44792,10 @@ 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} 中查看它。"
@@ -44444,10 +44856,6 @@ 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 have a maximum of %{free_user_limit} unique member across all of your personal projects."
-msgid_plural "You can have a maximum of %{free_user_limit} unique members across all of your personal projects."
-msgstr[0] "在您的個人所有專案中,您最多å¯ä»¥æ“有 %{free_user_limit} ä½å”¯ä¸€æˆå“¡ã€‚"
-
msgid "You can invite a new member to %{project_name} or invite another group."
msgstr "您å¯ä»¥é‚€è«‹æ–°æˆå“¡æˆ–å¦ä¸€å€‹ç¾¤çµ„加入%{project_name} 。"
@@ -44533,7 +44941,7 @@ msgid "You cannot impersonate an internal user"
msgstr "您無法摸擬使用內部的使用者"
msgid "You cannot play this scheduled pipeline at the moment. Please wait a minute."
-msgstr "您目å‰ç„¡æ³•åŸ·è¡Œæ­¤æµæ°´ç·šè¨ˆåŠƒã€‚è«‹ç¨å€™ã€‚"
+msgstr "您目å‰ç„¡æ³•åŸ·è¡Œæ­¤æµæ°´ç·šæŽ’程,請ç¨å€™ã€‚"
msgid "You cannot rename an environment after it's created."
msgstr "環境建立後ä¸èƒ½é‡æ–°å‘½å。"
@@ -44553,10 +44961,6 @@ msgstr "您ä¸èƒ½åœ¨é€™å€‹å°ˆæ¡ˆä¸­ç›´æŽ¥ç·¨è¼¯æ–‡ä»¶ï¼Œè«‹åˆ†å‰ï¼ˆFork)這å€
msgid "You could not create a new trigger."
msgstr "您無法建立新的觸發器。"
-msgid "You currently have more than %{free_user_limit} member across all your personal projects. 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."
-msgid_plural "You currently have more than %{free_user_limit} members across all your personal projects. 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."
-msgstr[0] "您目å‰åœ¨å€‹äººæ‰€æœ‰å°ˆæ¡ˆä¸­æ“æœ‰è¶…éŽ %{free_user_limit} ä½æˆå“¡ã€‚從 2022 å¹´ 10 月 19 日起,%{free_user_limit} 最近活èºçš„æˆå“¡å°‡ä¿æŒæ´»èºç‹€æ…‹ï¼Œå…¶é¤˜æˆå“¡å°‡è™•æ–¼ %{link_start}超é™ç‹€æ…‹%{link_end} 並失去存å–權é™ã€‚"
-
msgid "You do not have any subscriptions yet"
msgstr "您目å‰å°šæœªæœ‰ä»»ä½•è¨‚é–±"
@@ -44720,8 +45124,8 @@ 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."
-msgstr "您已æˆåŠŸè³¼è²· %{seats} ä½ä½¿ç”¨è€…çš„ %{plan} 訂閱計劃。收據將通éŽé›»å­éƒµä»¶ç™¼é€çµ¦æ‚¨ã€‚"
+msgid "You have successfully purchased a %{plan} plan subscription for %{seats}. You’ll receive a receipt via email. It might take a moment for GitLab.com to fully reflect your purchase."
+msgstr "您已æˆåŠŸè³¼è²· %{seats} çš„ %{plan} 訂閱計劃。您將通éŽé›»å­éƒµä»¶æ”¶åˆ°æ”¶æ“šã€‚ GitLab.com å¯èƒ½éœ€è¦ä¸€é»žæ™‚é–“æ‰èƒ½å®Œå…¨å應您的購買情æ³ã€‚"
msgid "You have unsaved changes"
msgstr "您有未儲存的變更"
@@ -44783,6 +45187,9 @@ msgstr "您需è¦ä¸Šå‚³GitLab專案匯出文件(以.gzçµå°¾)."
msgid "You need to verify your primary email first before enabling Two-Factor Authentication."
msgstr "您需è¦å…ˆé©—證您的主è¦é›»å­éƒµä»¶ï¼Œç„¶å¾Œæ‰èƒ½å•Ÿç”¨é›™å› å­é©—證。"
+msgid "You see projects here when you're added to a group or project."
+msgstr "當您被加入到群組或專案時,您會在此處看到專案。"
+
msgid "You successfully declined the invitation"
msgstr "您已æˆåŠŸæ‹’絕邀請"
@@ -44894,9 +45301,6 @@ msgstr "您正在查看 %{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 "您已經使用一次密碼驗證器來啟用了雙因å­èªè­‰ã€‚如果您è¦è¨»å†Šä¸åŒçš„設備,您必須先åœç”¨é›™å› å­èªè­‰ã€‚"
-msgid "You've reached your %{free_limit} member limit across all of your personal projects"
-msgstr "您已é”到所有個人專案的 %{free_limit} æˆå“¡é™åˆ¶"
-
msgid "You've rejected %{user}"
msgstr "您拒絕了 %{user}"
@@ -44936,6 +45340,12 @@ msgstr "您從專案%{project_name}(%{project_url})匯出包å«%{written_count}ç
msgid "Your CSV import for project"
msgstr "您的專案CSV匯入"
+msgid "Your Chain of Custody CSV export for the group %{group_link} has been added to this email as an attachment."
+msgstr "您å°ç¾¤çµ„ %{group_link} çš„ç›£ç®¡éˆ CSV 匯出已作為附件加入該電å­éƒµä»¶ä¸­ã€‚"
+
+msgid "Your Chain of Custody CSV export for the group %{group_name} has been added to this email as an attachment."
+msgstr "æ‚¨å° %{group_name} ç¾¤çµ„çš„ç›£ç®¡éˆ CSV 匯出已作為附件加入該電å­éƒµä»¶ä¸­ã€‚"
+
msgid "Your DevOps Reports give an overview of how you are using GitLab from a feature perspective. Use them to view how you compare with other organizations, and how your teams compare against each other."
msgstr "您的 DevOps 報告從功能角度概述了您如何使用 GitLab。使用它們來查看您與其他組織的比較情æ³ï¼Œä»¥åŠæ‚¨çš„團隊之間的比較情æ³ã€‚"
@@ -45086,9 +45496,6 @@ msgstr "您的設備已æˆåŠŸè¨­å®šï¼è«‹çµ¦å®ƒå‘½å並將其註冊到GitLab伺
msgid "Your file must contain a column named %{codeStart}title%{codeEnd}. A %{codeStart}description%{codeEnd} column is optional. The maximum file size allowed is 10 MB."
msgstr "您的文件必須包å«å為%{codeStart}title%{codeEnd}的列。%{codeStart}description%{codeEnd}列為å¯é¸çš„。å…許的最大文件大å°ç‚º10 MB。"
-msgid "Your first project"
-msgstr "您的第一個專案"
-
msgid "Your free group is now limited to %d member"
msgid_plural "Your free group is now limited to %d members"
msgstr[0] "您的å…費群組目å‰åƒ…é™æ–¼ %d åæˆå“¡"
@@ -45148,6 +45555,9 @@ msgstr "您的新存å–令牌已建立。"
msgid "Your new comment"
msgstr "您的新留言"
+msgid "Your password"
+msgstr "您的密碼"
+
msgid "Your password reset token has expired."
msgstr "您的密碼é‡è¨­ä»¤ç‰Œå·²éŽæœŸã€‚"
@@ -45163,8 +45573,8 @@ msgstr "您的主è¦é›»å­éƒµä»¶ç”¨æ–¼é ­åƒæª¢æ¸¬ã€‚您å¯ä»¥åœ¨æ‚¨çš„ %{openin
msgid "Your profile"
msgstr "個人資料"
-msgid "Your project has limited quotas and features"
-msgstr "您專案的é…é¡èˆ‡åŠŸèƒ½æ˜¯æœ‰é™åˆ¶çš„"
+msgid "Your project is no longer receiving GitLab Ultimate benefits as of 2022-07-01. As notified in-app previously, public open source projects on the Free tier can apply to the GitLab for Open Source Program to receive GitLab Ultimate benefits. Please refer to the %{faq_link_start}FAQ%{link_end} for more details."
+msgstr ""
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
msgstr "您的專案é™åˆ¶ç‚º %{limit} 個專案ï¼è«‹èˆ‡æ‚¨çš„管ç†å“¡è¯çµ¡ä»¥å¢žåŠ å®ƒ"
@@ -45367,9 +45777,6 @@ msgstr "產物"
msgid "assign yourself"
msgstr "指派給自己"
-msgid "associated parent is confidential and can not have non-confidential children."
-msgstr "é—œè¯çš„上層是機密的,因此ä¸èƒ½å»ºç«‹éžæ©Ÿå¯†å­é …目。"
-
msgid "at"
msgstr "æ–¼"
@@ -45392,7 +45799,7 @@ msgid "banned user already exists"
msgstr "ç¦æ­¢ä½¿ç”¨è€…已存在"
msgid "blocks"
-msgstr "å€å¡Š"
+msgstr "阻塞"
msgid "branch"
msgid_plural "branches"
@@ -45464,6 +45871,9 @@ msgstr "如果個人專案具有容器映åƒåº«æ¨™ç±¤ï¼Œå‰‡ç„¡æ³•æ›´æ”¹ã€‚"
msgid "cannot be changed if shared runners are enabled"
msgstr "如果啟用共用執行器(runners),則無法更改"
+msgid "cannot be changed since member is associated with a custom role"
+msgstr ""
+
msgid "cannot be enabled"
msgstr "無法啟用"
@@ -45504,6 +45914,9 @@ msgid "change"
msgid_plural "changes"
msgstr[0] "變更"
+msgid "ciReport|%{criticalStart}critical%{criticalEnd}, %{highStart}high%{highEnd} and %{otherStart}others%{otherEnd}"
+msgstr "%{criticalStart}åš´é‡%{criticalEnd}ã€%{highStart}高%{highEnd} å’Œ %{otherStart}其他%{otherEnd}"
+
msgid "ciReport|%{danger_start}%{degradedNum} degraded%{danger_end}, %{same_start}%{sameNum} same%{same_end}, and %{success_start}%{improvedNum} improved%{success_end}"
msgstr "%{danger_start}%{degradedNum} é™ä½Ž%{danger_end}ã€%{same_start}%{sameNum} 相åŒ%{same_end} å’Œ %{success_start}%{improvedNum} 改善%{success_end}"
@@ -45552,8 +45965,8 @@ msgstr "%{reportType}:發生一個錯誤"
msgid "ciReport|%{sameNum} same"
msgstr "相åŒ%{sameNum}"
-msgid "ciReport|%{scanner} detected %{boldStart}%{number}%{boldEnd} new potential %{vulnStr}"
-msgstr "%{scanner} 檢測到 %{boldStart}%{number}%{boldEnd} 個新的潛在 %{vulnStr}"
+msgid "ciReport|%{scanner} detected %{number} new potential %{vulnStr}"
+msgstr "%{scanner} 檢測到 %{number} 個新的潛在 %{vulnStr}"
msgid "ciReport|%{scanner} detected %{strong_start}%{number}%{strong_end} new potential %{vulnStr}"
msgstr "%{scanner} 檢測到 %{strong_start} %{number} %{strong_end} 個新的潛在 %{vulnStr}"
@@ -45748,8 +46161,8 @@ msgstr "管ç†æŽˆæ¬Šè¨±å¯"
msgid "ciReport|Manage licenses"
msgstr "管ç†æŽˆæ¬Šè¨±å¯"
-msgid "ciReport|Manually Added"
-msgstr "已手動加入"
+msgid "ciReport|Manually added"
+msgstr "已手動增加"
msgid "ciReport|New"
msgstr "新增"
@@ -45872,9 +46285,6 @@ msgstr "å·²æ交"
msgid "compliance violation has already been recorded"
msgstr "é•è¦è¡Œç‚ºå·²è¢«è¨˜éŒ„"
-msgid "confidential parent can not be used if there are non-confidential children."
-msgstr "如果上層有éžæ©Ÿå¯†å­é …目,則ä¸èƒ½è¨­ç‚ºæ©Ÿå¯†ã€‚"
-
msgid "contacts can only be added to root groups"
msgstr "è¯çµ¡äººåªèƒ½åŠ å…¥åˆ°æ ¹ç¾¤çµ„"
@@ -45909,7 +46319,10 @@ msgid "created %{timeAgo} by %{author}"
msgstr "由 %{author} 建立於 %{timeAgo}"
msgid "created by"
-msgstr "建立人:"
+msgstr "建立者:"
+
+msgid "daily"
+msgstr "日常"
msgid "data"
msgstr "資料"
@@ -46033,6 +46446,9 @@ msgstr "無法忽略關è¯çš„發ç¾(id=%{finding_id}): %{message}"
msgid "failed to dismiss finding: %{message}"
msgstr "忽略發ç¾å¤±æ•—:%{message}"
+msgid "failed to dismiss security finding: %{message}"
+msgstr "解除安全查尋失敗:%{message}"
+
msgid "failed to revert associated finding(id=%{finding_id}) to detected"
msgstr "無法將關è¯çš„發ç¾(id=%{finding_id})回復為檢測到"
@@ -46182,7 +46598,7 @@ msgid "is an invalid IP address range"
msgstr "是無效的 IP ä½å€ç¯„åœ"
msgid "is blocked by"
-msgstr "已經由下述人員å°éŽ–"
+msgstr "被以下議題堵塞"
msgid "is forbidden by a top-level group"
msgstr "被最上層群組ç¦æ­¢"
@@ -46238,6 +46654,9 @@ msgstr "是唯讀"
msgid "is too long (%{current_value}). The maximum size is %{max_size}."
msgstr "太長(%{current_value})。最大值為%{max_size}。"
+msgid "is too long (%{size}). The maximum size is %{max_size}."
+msgstr " (%{size}) 太長,最大值為 %{max_size}。"
+
msgid "is too long (maximum is %{count} characters)"
msgstr "éŽé•·ï¼ˆæœ€é•·ç‚º %{count} 個字元)"
@@ -46336,8 +46755,8 @@ msgid "merge request"
msgid_plural "merge requests"
msgstr[0] "åˆä½µè«‹æ±‚"
-msgid "mergedCommitsAdded|(commits were squashed)"
-msgstr "(æ交被壓縮)"
+msgid "mergedCommitsAdded| (commits were squashed)"
+msgstr "(æ交已被壓縮)"
msgid "metric_id must be unique across a project"
msgstr "metric_id必須是整個專案唯一的"
@@ -46357,8 +46776,8 @@ msgstr "%{commitCount} 和 %{mergeCommitCount} 將被加入到 %{targetBranch}%{
msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr "%{commitCount} 將被加入到 %{targetBranch}。"
-msgid "mrWidgetCommitsAdded|1 merge commit"
-msgstr "1個åˆä½µæ交"
+msgid "mrWidgetCommitsAdded|%{strongStart}1%{strongEnd} merge commit"
+msgstr "%{strongStart}1%{strongEnd} 個åˆä½µæ交"
msgid "mrWidgetCommitsAdded|Changes merged into %{targetBranch} with %{mergeCommitSha}%{squashedCommits}."
msgstr "變更以 %{mergeCommitSha}%{squashedCommits} åˆä½µåˆ° %{targetBranch}。"
@@ -46518,6 +46937,9 @@ msgid "mrWidget|Mentions issue"
msgid_plural "mrWidget|Mentions issues"
msgstr[0] "æåŠçš„è­°é¡Œ"
+msgid "mrWidget|Merge blocked: a Jira issue key must be mentioned in the title or description."
+msgstr "阻止åˆä½µï¼šæ¨™é¡Œæˆ–æ述中應æåŠ Jira 議題代號。"
+
msgid "mrWidget|Merge blocked: all required approvals must be given."
msgstr "åˆä½µè¢«é˜»æ­¢ï¼šå¿…é ˆæ供所有必è¦çš„核准。"
@@ -46653,9 +47075,6 @@ msgstr "è¦æ›´æ”¹æ­¤é è¨­è¨Šæ¯ï¼Œè«‹ç·¨è¼¯åˆä½µæ交訊æ¯çš„範本。 %{lin
msgid "mrWidget|To change this default message, edit the template for squash commit messages. %{linkStart}Learn more.%{linkEnd}"
msgstr "è¦æ›´æ”¹æ­¤é è¨­è¨Šæ¯ï¼Œè«‹ç·¨è¼¯å£“縮æ交訊æ¯çš„範本。 %{linkStart}了解更多訊æ¯ã€‚%{linkEnd}"
-msgid "mrWidget|To merge, a Jira issue key must be mentioned in the title or description."
-msgstr "è¦åˆä½µï¼Œå¿…須在標題或æ述中æ到Jira議題的key。"
-
msgid "mrWidget|Users who can write to the source or target branches can resolve the conflicts."
msgstr "å¯ä»¥å¯«å…¥ä¾†æºæˆ–目標分支的使用者å¯ä»¥è§£æ±ºè¡çªã€‚"
@@ -46729,7 +47148,7 @@ msgid "never expires"
msgstr "æ°¸ä¸éŽæœŸ"
msgid "new merge request"
-msgstr "建立åˆä½µè«‹æ±‚"
+msgstr "新建åˆä½µè«‹æ±‚"
msgid "no expiration"
msgstr "ç„¡éŽæœŸ"
@@ -46830,7 +47249,7 @@ msgid "pipeline"
msgstr "æµæ°´ç·š"
msgid "pipeline schedules documentation"
-msgstr "æµæ°´ç·šè¨ˆåŠƒæ–‡ä»¶"
+msgstr "æµæ°´ç·šæŽ’程文件"
msgid "pipelineEditorWalkthrough|Let's do this!"
msgstr "讓我們這樣åšï¼"
@@ -47211,6 +47630,9 @@ msgstr "已忽略"
msgid "was scheduled to merge after pipeline succeeds by"
msgstr "åˆä½µè«‹æ±‚已安排在æµæ°´ç·šæˆåŠŸå¾Œåˆä½µã€‚åˆä½µäºº: "
+msgid "weekly"
+msgstr "æ¯é€±"
+
msgid "wiki page"
msgstr "wikié é¢"
diff --git a/package.json b/package.json
index 083b8b83fdf..5242ede8a09 100644
--- a/package.json
+++ b/package.json
@@ -14,7 +14,7 @@
"jest": "jest --config jest.config.js",
"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 tmp/changed_files.txt) --passWithNoTests --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:integration": "jest --config jest.config.integration.js",
"lint:eslint": "node scripts/frontend/eslint.js",
"lint:eslint:fix": "node scripts/frontend/eslint.js --fix",
@@ -52,9 +52,10 @@
"@codesandbox/sandpack-client": "^1.2.2",
"@gitlab/at.js": "1.5.7",
"@gitlab/favicon-overlay": "2.0.0",
- "@gitlab/svgs": "3.1.0",
- "@gitlab/ui": "43.6.0",
+ "@gitlab/svgs": "3.3.0",
+ "@gitlab/ui": "43.16.0",
"@gitlab/visual-review-tools": "1.7.3",
+ "@gitlab/web-ide": "0.0.1-dev-20220815034418",
"@rails/actioncable": "6.1.4-7",
"@rails/ujs": "6.1.4-7",
"@sentry/browser": "5.30.0",
@@ -62,6 +63,7 @@
"@tiptap/core": "^2.0.0-beta.182",
"@tiptap/extension-blockquote": "^2.0.0-beta.29",
"@tiptap/extension-bold": "^2.0.0-beta.28",
+ "@tiptap/extension-bubble-menu": "^2.0.0-beta.61",
"@tiptap/extension-bullet-list": "^2.0.0-beta.29",
"@tiptap/extension-code": "^2.0.0-beta.28",
"@tiptap/extension-code-block-lowlight": "2.0.0-beta.73",
@@ -101,7 +103,7 @@
"codesandbox-api": "0.0.23",
"compression-webpack-plugin": "^5.0.2",
"copy-webpack-plugin": "^6.4.1",
- "core-js": "^3.24.1",
+ "core-js": "^3.25.1",
"cron-validator": "^1.1.1",
"cronstrue": "^1.122.0",
"cropper": "^2.3.0",
@@ -112,7 +114,7 @@
"dateformat": "^5.0.1",
"deckar01-task_list": "^2.3.1",
"diff": "^3.4.0",
- "dompurify": "^2.3.10",
+ "dompurify": "^2.4.0",
"dropzone": "^4.2.0",
"editorconfig": "^0.15.3",
"emoji-regex": "^10.0.0",
@@ -130,7 +132,6 @@
"js-cookie": "^3.0.0",
"js-yaml": "^3.13.1",
"jszip": "^3.1.3",
- "jszip-utils": "^0.0.2",
"katex": "^0.13.2",
"lodash": "^4.17.20",
"lowlight": "^2.6.1",
@@ -151,7 +152,6 @@
"popper.js": "^1.16.1",
"portal-vue": "^2.1.7",
"postcss": "8.4.14",
- "prismjs": "^1.21.0",
"prosemirror-markdown": "1.9.1",
"prosemirror-model": "^1.18.1",
"prosemirror-state": "^1.4.1",
@@ -169,28 +169,27 @@
"string-hash": "1.1.3",
"style-loader": "^2.0.0",
"swagger-ui-dist": "4.12.0",
- "three": "^0.84.0",
- "three-orbit-controls": "^82.1.0",
- "three-stl-loader": "^1.0.4",
+ "three": "^0.143.0",
"timeago.js": "^4.0.2",
"unified": "^10.1.2",
+ "unist-builder": "^3.0.0",
"unist-util-visit-parents": "^5.1.0",
"url-loader": "^4.1.1",
"uuid": "8.1.0",
"visibilityjs": "^1.2.4",
- "vue": "^2.6.12",
+ "vue": "2.6.14",
"vue-apollo": "^3.0.7",
- "vue-loader": "^15.9.6",
+ "vue-loader": "15.9.6",
"vue-observe-visibility": "^1.0.0",
"vue-resize": "^1.0.1",
"vue-router": "3.4.9",
- "vue-template-compiler": "^2.6.12",
+ "vue-template-compiler": "2.6.14",
"vue-virtual-scroll-list": "^1.4.7",
"vuedraggable": "^2.23.0",
- "vuex": "^3.6.0",
+ "vuex": "^3.6.2",
"web-vitals": "^0.2.4",
"webpack": "^4.46.0",
- "webpack-bundle-analyzer": "^4.5.0",
+ "webpack-bundle-analyzer": "^4.6.1",
"webpack-cli": "^4.10.0",
"webpack-stats-plugin": "^0.3.1",
"worker-loader": "^2.0.0",
@@ -198,7 +197,7 @@
"yaml": "^2.0.0-10"
},
"devDependencies": {
- "@gitlab/eslint-plugin": "16.0.0",
+ "@gitlab/eslint-plugin": "17.0.0",
"@gitlab/stylelint-config": "4.1.0",
"@graphql-eslint/eslint-plugin": "3.10.7",
"@testing-library/dom": "^7.16.2",
@@ -213,10 +212,12 @@
"cheerio": "^1.0.0-rc.9",
"commander": "^2.20.3",
"custom-jquery-matchers": "^2.1.0",
- "eslint": "8.22.0",
+ "eslint": "8.23.1",
"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",
"gettext-extractor": "^3.5.3",
"gettext-extractor-vue": "^5.0.0",
"glob": "^7.1.6",
@@ -232,7 +233,7 @@
"jest-raw-loader": "^1.0.1",
"jest-transform-graphql": "^2.1.0",
"jest-util": "^27.5.1",
- "markdownlint-cli": "0.31.0",
+ "markdownlint-cli": "0.32.2",
"miragejs": "^0.1.40",
"mock-apollo-client": "1.2.0",
"nodemon": "^2.0.19",
@@ -245,10 +246,10 @@
"sass": "^1.49.9",
"stylelint": "^14.9.1",
"timezone-mock": "^1.0.8",
- "webpack-dev-server": "4.10.0",
+ "webpack-dev-server": "4.11.0",
"xhr-mock": "^2.5.1",
"yarn-check-webpack-plugin": "^1.2.0",
- "yarn-deduplicate": "^5.0.2"
+ "yarn-deduplicate": "^6.0.0"
},
"blockedDependencies": {
"bootstrap-vue": "https://docs.gitlab.com/ee/development/fe_guide/dependencies.html#bootstrapvue"
diff --git a/qa/.confiner/master.yml b/qa/.confiner/master.yml
index bfb44facd7d..e6fc3e68747 100644
--- a/qa/.confiner/master.yml
+++ b/qa/.confiner/master.yml
@@ -4,7 +4,7 @@
args:
threshold: 3 # 3 failures
private_token: $QA_GITLAB_CI_TOKEN
- project_id: gitlab-org/gitlab-qa-mirror # https://gitlab.com/gitlab-org/gitlab-qa-mirror/
+ project_id: gitlab-org/gitlab
target_project: gitlab-org/gitlab
failure_issue_labels: QA,Quality
failure_issue_prefix: "Failure in "
diff --git a/qa/Gemfile b/qa/Gemfile
index 7c46d35bb48..cf939f8e301 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -2,7 +2,7 @@
source 'https://rubygems.org'
-gem 'gitlab-qa', '~> 7', require: 'gitlab/qa'
+gem 'gitlab-qa', '~> 8', require: 'gitlab/qa'
gem 'activesupport', '~> 6.1.4.7' # This should stay in sync with the root's Gemfile
gem 'allure-rspec', '~> 2.16.0'
gem 'capybara', '~> 3.35.0'
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index ff382788c5a..dd14b675769 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -11,8 +11,8 @@ GEM
adamantium (0.2.0)
ice_nine (~> 0.11.0)
memoizable (~> 0.4.0)
- addressable (2.8.0)
- public_suffix (>= 2.0.2, < 5.0)
+ addressable (2.8.1)
+ public_suffix (>= 2.0.2, < 6.0)
airborne (0.3.4)
activesupport
rack
@@ -118,13 +118,14 @@ GEM
gitlab (4.18.0)
httparty (~> 0.18)
terminal-table (>= 1.5.1)
- gitlab-qa (7.33.0)
+ gitlab-qa (8.4.2)
activesupport (~> 6.1)
gitlab (~> 4.18.0)
http (~> 5.0)
nokogiri (~> 1.10)
rainbow (~> 3.0.0)
table_print (= 1.5.7)
+ zeitwerk (~> 2.4)
google-apis-compute_v1 (0.21.0)
google-apis-core (>= 0.4, < 2.a)
google-apis-core (0.4.1)
@@ -157,7 +158,7 @@ GEM
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
- http (5.0.4)
+ http (5.1.0)
addressable (~> 2.8)
http-cookie (~> 1.0)
http-form_data (~> 2.2)
@@ -170,7 +171,7 @@ GEM
mime-types (~> 3.0)
multi_xml (>= 0.5.2)
httpclient (2.8.3)
- i18n (1.10.0)
+ i18n (1.12.0)
concurrent-ruby (~> 1.0)
ice_nine (0.11.2)
influxdb-client (1.17.0)
@@ -193,7 +194,7 @@ GEM
mime-types-data (3.2022.0105)
mini_mime (1.1.0)
mini_portile2 (2.8.0)
- minitest (5.15.0)
+ minitest (5.16.3)
multi_json (1.15.0)
multi_xml (0.6.0)
multipart-post (2.1.1)
@@ -209,7 +210,7 @@ GEM
parallel (1.19.2)
parallel_tests (2.29.0)
parallel
- parser (3.0.3.2)
+ parser (3.1.2.1)
ast (~> 2.4.1)
proc_to_ast (0.1.0)
coderay
@@ -222,7 +223,7 @@ GEM
pry-byebug (3.5.1)
byebug (~> 9.1)
pry (~> 0.10)
- public_suffix (4.0.7)
+ public_suffix (5.0.0)
racc (1.6.0)
rack (2.2.3.1)
rack-test (1.1.0)
@@ -290,13 +291,13 @@ GEM
thread_safe (0.3.6)
timecop (0.9.1)
trailblazer-option (0.1.2)
- tzinfo (2.0.4)
+ tzinfo (2.0.5)
concurrent-ruby (~> 1.0)
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2)
- unicode-display_width (2.1.0)
+ unicode-display_width (2.2.0)
unparser (0.4.7)
abstract_type (~> 0.0.7)
adamantium (~> 0.2.0)
@@ -335,7 +336,7 @@ DEPENDENCIES
deprecation_toolkit (~> 1.5.1)
faker (~> 2.19, >= 2.19.0)
fog-google (~> 1.17)
- gitlab-qa (~> 7)
+ gitlab-qa (~> 8)
influxdb-client (~> 1.17)
knapsack (~> 4.0)
nokogiri (~> 1.12)
diff --git a/qa/README.md b/qa/README.md
index dbc70f55f1c..564beb4c6e8 100644
--- a/qa/README.md
+++ b/qa/README.md
@@ -1,12 +1,12 @@
# GitLab QA - End-to-end tests for GitLab
-This directory contains [end-to-end tests](../../../doc/development/testing_guide/end_to_end/index.md)
+This directory contains [end-to-end tests](../doc/development/testing_guide/end_to_end/index.md)
for GitLab. It includes the test framework and the tests themselves.
The tests can be found in `qa/specs/features` (not to be confused with the unit
tests for the test framework, which are in `spec/`).
-It is part of the [GitLab QA project](https://gitlab.com/gitlab-org/gitlab-qa).
+Tests use [GitLab QA project](https://gitlab.com/gitlab-org/gitlab-qa) for environment orchestration in CI jobs.
## What is it?
@@ -46,7 +46,7 @@ Note that tests are using `Chrome` web browser by default so it should be instal
Tests are executed in merge request pipelines as part of the development lifecycle.
- [Review app environment](../doc/development/testing_guide/review_apps.md)
-- [package-and-qa](../doc/development/testing_guide/end_to_end/index.md#testing-code-in-merge-requests)
+- [e2e:package-and-test](../doc/development/testing_guide/end_to_end/index.md#testing-code-in-merge-requests)
### Logging
diff --git a/qa/Rakefile b/qa/Rakefile
index d3e39d8ed1e..ada27596ae4 100644
--- a/qa/Rakefile
+++ b/qa/Rakefile
@@ -4,23 +4,18 @@ require_relative "qa"
Dir['tasks/*.rake'].each { |file| load file }
-desc "Revokes all personal access tokens"
-task :revoke_personal_access_tokens do
- QA::Tools::RevokeAllPersonalAccessTokens.new.run
-end
-
desc "Deletes subgroups within a provided group"
task :delete_subgroups do
QA::Tools::DeleteSubgroups.new.run
end
desc "Initialize GitLab with an access token"
-task :initialize_gitlab_auth, [:address] do |t, args|
+task :initialize_gitlab_auth, [:address] do |_, args|
QA::Tools::InitializeGitLabAuth.new(args).run
end
desc "Generate Performance Testdata"
-task :generate_perf_testdata, :type do |t, args|
+task :generate_perf_testdata, :type do |_, args|
args.with_defaults(type: :all)
QA::Tools::GeneratePerfTestdata.new.method(args[:type]).call
end
@@ -50,7 +45,7 @@ desc "Generate data and run load tests"
task generate_data_and_run_load_test: [:generate_perf_testdata, :run_artillery_load_tests]
desc "Deletes test ssh keys a user"
-task :delete_test_ssh_keys, [:title_portion, :delete_before, :dry_run] do |t, args|
+task :delete_test_ssh_keys, [:title_portion, :delete_before, :dry_run] do |_, args|
QA::Tools::DeleteTestSSHKeys.new(args).run
end
@@ -60,33 +55,38 @@ task :delete_projects do
end
desc "Deletes test users"
-task :delete_test_users, [:delete_before, :dry_run, :exclude_users] do |t, args|
+task :delete_test_users, [:delete_before, :dry_run, :exclude_users] do |_, args|
QA::Tools::DeleteTestUsers.new(args).run
end
desc "Deletes snippets"
-task :delete_test_snippets, [:delete_before, :dry_run] do |t, args|
+task :delete_test_snippets, [:delete_before, :dry_run] do |_, args|
QA::Tools::DeleteTestSnippets.new(args).run
end
namespace :test_resources do
desc "Deletes resources created during E2E test runs"
- task :delete, [:file_pattern] do |t, args|
+ task :delete, [:file_pattern] do |_, args|
QA::Tools::TestResourcesHandler.new(args[:file_pattern]).run_delete
end
desc "Upload test resources JSON files to GCS"
- task :upload, [:file_pattern, :ci_project_name] do |t, args|
+ task :upload, [:file_pattern, :ci_project_name] do |_, args|
QA::Tools::TestResourcesHandler.new(args[:file_pattern]).upload(args[:ci_project_name])
end
desc "Download test resources JSON files from GCS"
- task :download, [:ci_project_name] do |t, args|
+ task :download, [:ci_project_name] do |_, args|
QA::Tools::TestResourcesHandler.new.download(args[:ci_project_name])
end
end
desc "Deletes user's projects"
-task :delete_user_projects, [:delete_before, :dry_run] do |t, args|
+task :delete_user_projects, [:delete_before, :dry_run] do |_, args|
QA::Tools::DeleteUserProjects.new(args).run
end
+
+desc "Revokes user's personal access tokens"
+task :revoke_user_pats, [:revoke_before, :dry_run] do |_, args|
+ QA::Tools::RevokeUserPersonalAccessTokens.new(args).run
+end
diff --git a/qa/lib/gitlab/page/admin/subscription.rb b/qa/lib/gitlab/page/admin/subscription.rb
index 1538384f6ed..ef73bad2879 100644
--- a/qa/lib/gitlab/page/admin/subscription.rb
+++ b/qa/lib/gitlab/page/admin/subscription.rb
@@ -10,8 +10,8 @@ module Gitlab
text_field :activation_code
button :activate
label :terms_of_services, text: /I agree that/
- link :remove_license, 'data-testid': 'license-remove-action'
- button :confirm_ok_button
+ button :remove_license
+ button :confirm_remove_license
p :plan
p :started
p :name
@@ -30,6 +30,11 @@ module Gitlab
terms_of_services_element.click # workaround for hidden checkbox
end
+ def remove_license_file
+ remove_license
+ confirm_remove_license
+ end
+
# Checks if a subscription record exists in subscription history table
#
# @param plan [Hash] Name of the plan
diff --git a/qa/qa.rb b/qa/qa.rb
index dd6462cfe27..99a8a34d6d8 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -51,7 +51,6 @@ module QA
"repo_by_url" => "RepoByURL",
"oauth" => "OAuth",
"saml_sso_sign_in" => "SamlSSOSignIn",
- "saml_sso_sign_up" => "SamlSSOSignUp",
"group_saml" => "GroupSAML",
"instance_saml" => "InstanceSAML",
"saml_sso" => "SamlSSO",
diff --git a/qa/qa/fixtures/auto_devops_rack/Dockerfile b/qa/qa/fixtures/auto_devops_rack/Dockerfile
deleted file mode 100644
index 6ab2795dd40..00000000000
--- a/qa/qa/fixtures/auto_devops_rack/Dockerfile
+++ /dev/null
@@ -1,9 +0,0 @@
-FROM ruby:2.6.5-alpine
-ADD ./ /app/
-WORKDIR /app
-ENV RACK_ENV production
-ENV PORT 5000
-EXPOSE 5000
-
-RUN bundle install
-CMD ["bundle","exec", "rackup", "-p", "5000"]
diff --git a/qa/qa/fixtures/auto_devops_rack/Gemfile b/qa/qa/fixtures/auto_devops_rack/Gemfile
deleted file mode 100644
index 2c7c77adf94..00000000000
--- a/qa/qa/fixtures/auto_devops_rack/Gemfile
+++ /dev/null
@@ -1,5 +0,0 @@
-# frozen_string_literal: true
-
-source 'https://rubygems.org'
-gem 'rack'
-gem 'rake'
diff --git a/qa/qa/fixtures/auto_devops_rack/Gemfile.lock b/qa/qa/fixtures/auto_devops_rack/Gemfile.lock
deleted file mode 100644
index 04a85be4b2f..00000000000
--- a/qa/qa/fixtures/auto_devops_rack/Gemfile.lock
+++ /dev/null
@@ -1,15 +0,0 @@
-GEM
- remote: https://rubygems.org/
- specs:
- rack (2.2.3)
- rake (12.3.3)
-
-PLATFORMS
- ruby
-
-DEPENDENCIES
- rack
- rake
-
-BUNDLED WITH
- 1.17.3
diff --git a/qa/qa/fixtures/auto_devops_rack/Rakefile b/qa/qa/fixtures/auto_devops_rack/Rakefile
deleted file mode 100644
index a6d08103d55..00000000000
--- a/qa/qa/fixtures/auto_devops_rack/Rakefile
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-require 'rake/testtask'
-
-task default: %w[test]
-
-task :test do
- puts "ok"
-end
diff --git a/qa/qa/fixtures/auto_devops_rack/config.ru b/qa/qa/fixtures/auto_devops_rack/config.ru
deleted file mode 100644
index aea28ef1893..00000000000
--- a/qa/qa/fixtures/auto_devops_rack/config.ru
+++ /dev/null
@@ -1,3 +0,0 @@
-# frozen_string_literal: true
-
-run lambda { |env| [200, { 'Content-Type' => 'text/plain' }, StringIO.new("Hello World! #{ENV['OPTIONAL_MESSAGE']}\n")] }
diff --git a/qa/qa/fixtures/package_managers/terraform/module_upload.yaml.erb b/qa/qa/fixtures/package_managers/terraform/module_upload.yaml.erb
new file mode 100644
index 00000000000..3a7313b0712
--- /dev/null
+++ b/qa/qa/fixtures/package_managers/terraform/module_upload.yaml.erb
@@ -0,0 +1,21 @@
+stages:
+ - upload
+
+upload:
+ stage: upload
+ image: curlimages/curl:latest
+ variables:
+ TERRAFORM_MODULE_DIR: ${CI_PROJECT_DIR} # The path to your Terraform module
+ TERRAFORM_MODULE_NAME: ${CI_PROJECT_NAME} # The name of your Terraform module
+ TERRAFORM_MODULE_SYSTEM: local # The system or provider your Terraform module targets (ex. local, aws, google)
+ TERRAFORM_MODULE_VERSION: ${CI_COMMIT_TAG} # Tag commits with SemVer for the version of your Terraform module to be published
+ 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}"
+ --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
+ tags:
+ - runner-for-<%= imported_project.name %> \ No newline at end of file
diff --git a/qa/qa/flow/login.rb b/qa/qa/flow/login.rb
index 05f114acbc5..ec205e0aa86 100644
--- a/qa/qa/flow/login.rb
+++ b/qa/qa/flow/login.rb
@@ -3,7 +3,7 @@
module QA
module Flow
module Login
- module_function
+ extend self
def while_signed_in(as: nil, address: :gitlab, admin: false)
sign_in(as: as, address: address, admin: admin)
@@ -52,3 +52,5 @@ module QA
end
end
end
+
+QA::Flow::Login.prepend_mod_with('Flow::Login', namespace: QA)
diff --git a/qa/qa/flow/merge_request.rb b/qa/qa/flow/merge_request.rb
index f1cab2c7d1a..24abfa9e356 100644
--- a/qa/qa/flow/merge_request.rb
+++ b/qa/qa/flow/merge_request.rb
@@ -3,11 +3,10 @@
module QA
module Flow
module MergeRequest
- module_function
+ extend self
def enable_merge_trains
- Page::Project::Menu.perform(&:go_to_general_settings)
- Page::Project::Settings::Main.perform(&:expand_merge_requests_settings)
+ Page::Project::Menu.perform(&:go_to_merge_request_settings)
Page::Project::Settings::MergeRequest.perform(&:enable_merge_train)
end
@@ -33,3 +32,5 @@ module QA
end
end
end
+
+QA::Flow::MergeRequest.prepend_mod_with('Flow::MergeRequest', namespace: QA)
diff --git a/qa/qa/flow/pipeline.rb b/qa/qa/flow/pipeline.rb
index d19b2530bb8..fb6a5425a6e 100644
--- a/qa/qa/flow/pipeline.rb
+++ b/qa/qa/flow/pipeline.rb
@@ -3,7 +3,7 @@
module QA
module Flow
module Pipeline
- module_function
+ extend self
# Acceptable statuses:
# canceled, created, failed, manual, passed
@@ -27,3 +27,5 @@ module QA
end
end
end
+
+QA::Flow::Pipeline.prepend_mod_with('Flow::Pipeline', namespace: QA)
diff --git a/qa/qa/flow/project.rb b/qa/qa/flow/project.rb
index 397806b33a3..70bdcfcb719 100644
--- a/qa/qa/flow/project.rb
+++ b/qa/qa/flow/project.rb
@@ -3,7 +3,7 @@
module QA
module Flow
module Project
- module_function
+ extend self
def go_to_create_project_from_template
Page::Project::New.perform(&:click_create_from_template_link)
@@ -11,3 +11,5 @@ module QA
end
end
end
+
+QA::Flow::Project.prepend_mod_with('Flow::Project', namespace: QA)
diff --git a/qa/qa/flow/purchase.rb b/qa/qa/flow/purchase.rb
index e0efa8a8178..c07e03c104d 100644
--- a/qa/qa/flow/purchase.rb
+++ b/qa/qa/flow/purchase.rb
@@ -5,7 +5,7 @@ module QA
module Purchase
include QA::Support::Helpers::Plan
- module_function
+ extend self
def upgrade_subscription(plan: PREMIUM)
Page::Group::Menu.perform(&:go_to_billing)
diff --git a/qa/qa/flow/saml.rb b/qa/qa/flow/saml.rb
index 1280f59c3c2..8a0ebb9f551 100644
--- a/qa/qa/flow/saml.rb
+++ b/qa/qa/flow/saml.rb
@@ -3,7 +3,7 @@
module QA
module Flow
module Saml
- module_function
+ extend self
def page
Capybara.current_session
@@ -72,3 +72,5 @@ module QA
end
end
end
+
+QA::Flow::Saml.prepend_mod_with('Flow::Saml', namespace: QA)
diff --git a/qa/qa/flow/settings.rb b/qa/qa/flow/settings.rb
index 775b7686c10..8e6ce667475 100644
--- a/qa/qa/flow/settings.rb
+++ b/qa/qa/flow/settings.rb
@@ -3,7 +3,7 @@
module QA
module Flow
module Settings
- module_function
+ extend self
def disable_snowplow
Flow::Login.while_signed_in_as_admin do
@@ -23,3 +23,5 @@ module QA
end
end
end
+
+QA::Flow::Settings.prepend_mod_with('Flow::Settings', namespace: QA)
diff --git a/qa/qa/flow/sign_up.rb b/qa/qa/flow/sign_up.rb
index ec7886ef969..52c92293bad 100644
--- a/qa/qa/flow/sign_up.rb
+++ b/qa/qa/flow/sign_up.rb
@@ -3,7 +3,7 @@
module QA
module Flow
module SignUp
- module_function
+ extend self
def page
Capybara.current_session
@@ -55,3 +55,5 @@ module QA
end
end
end
+
+QA::Flow::SignUp.prepend_mod_with('Flow::SignUp', namespace: QA)
diff --git a/qa/qa/flow/user.rb b/qa/qa/flow/user.rb
index c0bd475adb7..8f11d4ef26f 100644
--- a/qa/qa/flow/user.rb
+++ b/qa/qa/flow/user.rb
@@ -3,7 +3,7 @@
module QA
module Flow
module User
- module_function
+ extend self
def page
Capybara.current_session
@@ -24,3 +24,5 @@ module QA
end
end
end
+
+QA::Flow::User.prepend_mod_with('Flow::User', namespace: QA)
diff --git a/qa/qa/flow/user_onboarding.rb b/qa/qa/flow/user_onboarding.rb
index 066e1878869..62397d5641d 100644
--- a/qa/qa/flow/user_onboarding.rb
+++ b/qa/qa/flow/user_onboarding.rb
@@ -3,7 +3,7 @@
module QA
module Flow
module UserOnboarding
- module_function
+ extend self
def onboard_user
Page::Registration::Welcome.perform do |welcome_page|
@@ -17,3 +17,5 @@ module QA
end
end
end
+
+QA::Flow::UserOnboarding.prepend_mod_with('Flow::UserOnboarding', namespace: QA)
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index d7e0101ff2c..03f753b1d61 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -500,3 +500,5 @@ module QA
end
end
end
+
+QA::Page::Base.prepend_mod_with('Page::Base', namespace: QA)
diff --git a/qa/qa/page/component/access_tokens.rb b/qa/qa/page/component/access_tokens.rb
index 36c0f8c2f00..3c8a608cdc2 100644
--- a/qa/qa/page/component/access_tokens.rb
+++ b/qa/qa/page/component/access_tokens.rb
@@ -18,6 +18,10 @@ module QA
element :expiry_date_field
end
+ base.view 'app/views/shared/access_tokens/_created_container.html.haml' do
+ element :created_access_token_field
+ end
+
base.view 'app/views/shared/access_tokens/_form.html.haml' do
element :access_token_name_field
element :create_token_button
@@ -32,7 +36,7 @@ module QA
end
base.view 'app/assets/javascripts/access_tokens/components/new_access_token_app.vue' do
- element :created_access_token
+ element :created_access_token_field
end
base.view 'app/assets/javascripts/access_tokens/components/access_token_table_app.vue' do
@@ -53,7 +57,7 @@ module QA
end
def created_access_token
- find_element(:created_access_token, wait: 30).value
+ find_element(:created_access_token_field, wait: 30).value
end
def fill_expiry_date(date)
diff --git a/qa/qa/page/component/ci_badge_link.rb b/qa/qa/page/component/ci_badge_link.rb
index 2ba198621fc..485e363d960 100644
--- a/qa/qa/page/component/ci_badge_link.rb
+++ b/qa/qa/page/component/ci_badge_link.rb
@@ -32,12 +32,12 @@ module QA
super
base.view 'app/assets/javascripts/vue_shared/components/ci_badge_link.vue' do
- element :status_badge
+ element :status_badge_link
end
end
def status_badge
- find_element(:status_badge).text
+ find_element(:status_badge_link).text
end
def completed?(timeout: 60)
diff --git a/qa/qa/page/component/content_editor.rb b/qa/qa/page/component/content_editor.rb
index b3a42634fe7..f7b055b6052 100644
--- a/qa/qa/page/component/content_editor.rb
+++ b/qa/qa/page/component/content_editor.rb
@@ -21,6 +21,10 @@ module QA
base.view 'app/assets/javascripts/content_editor/components/toolbar_image_button.vue' do
element :file_upload_field
end
+
+ base.view 'app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue' do
+ element :wiki_hidden_content
+ end
end
def add_heading(heading, text)
@@ -41,6 +45,11 @@ module QA
text_area.send_keys(:return)
find_element(:file_upload_field, visible: false).send_keys(image_path)
end
+
+ QA::Support::Retrier.retry_on_exception do
+ source = find_element(:wiki_hidden_content, visible: false)
+ source.value =~ %r{uploads/.*#{::File.basename(image_path)}}
+ end
end
private
diff --git a/qa/qa/page/component/note.rb b/qa/qa/page/component/note.rb
index 8494518723d..37c833e77c2 100644
--- a/qa/qa/page/component/note.rb
+++ b/qa/qa/page/component/note.rb
@@ -28,7 +28,7 @@ module QA
end
base.view 'app/assets/javascripts/notes/components/discussion_filter.vue' do
- element :discussion_filter_dropdown, required: true
+ element :discussion_preferences_dropdown, required: true
element :filter_menu_item
end
@@ -169,7 +169,7 @@ module QA
def select_filter_with_text(text)
retry_on_exception do
click_element(:title_content)
- click_element :discussion_filter_dropdown
+ click_element :discussion_preferences_dropdown
find_element(:filter_menu_item, text: text).click
wait_for_requests
diff --git a/qa/qa/page/component/project/templates.rb b/qa/qa/page/component/project/templates.rb
index 8baf15acdff..6e86455fc52 100644
--- a/qa/qa/page/component/project/templates.rb
+++ b/qa/qa/page/component/project/templates.rb
@@ -5,7 +5,7 @@ module QA
module Project
module Templates
def use_template_for_project(project_name)
- within find_element(:template_option_row, text: project_name) do
+ within find_element(:template_option_container, text: project_name) do
click_element :use_template_button
end
end
diff --git a/qa/qa/page/component/web_ide/modal/create_new_file.rb b/qa/qa/page/component/web_ide/modal/create_new_file.rb
index 7c55f775476..2869bb9c331 100644
--- a/qa/qa/page/component/web_ide/modal/create_new_file.rb
+++ b/qa/qa/page/component/web_ide/modal/create_new_file.rb
@@ -9,7 +9,7 @@ module QA
view 'app/assets/javascripts/ide/components/new_dropdown/modal.vue' do
element :file_name_field, required: true
element :new_file_modal, required: true
- element :template_list
+ element :template_list_content
end
end
end
diff --git a/qa/qa/page/component/wiki_page_form.rb b/qa/qa/page/component/wiki_page_form.rb
index 22b9a4c8b0d..9e558844469 100644
--- a/qa/qa/page/component/wiki_page_form.rb
+++ b/qa/qa/page/component/wiki_page_form.rb
@@ -35,6 +35,10 @@ module QA
end
def click_submit
+ # In case any changes were just made, wait for the hidden content field to be updated via a deferred call
+ # before clicking submit. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97693#note_1098728562
+ sleep 0.5
+
click_element(:wiki_submit_button)
QA::Support::Retrier.retry_on_exception do
diff --git a/qa/qa/page/file/shared/commit_message.rb b/qa/qa/page/file/shared/commit_message.rb
index a3658fa11af..12154cdb728 100644
--- a/qa/qa/page/file/shared/commit_message.rb
+++ b/qa/qa/page/file/shared/commit_message.rb
@@ -14,6 +14,10 @@ module QA
element :commit_message_field
end
+ base.view 'app/assets/javascripts/repository/components/last_commit.vue' do
+ element :commit_content
+ end
+
base.view 'app/views/shared/_commit_message_container.html.haml' do
element :commit_message_field
end
diff --git a/qa/qa/page/file/show.rb b/qa/qa/page/file/show.rb
index 581aa835ada..31899d9a0c7 100644
--- a/qa/qa/page/file/show.rb
+++ b/qa/qa/page/file/show.rb
@@ -14,11 +14,6 @@ module QA
element :lock_button
end
- view 'app/helpers/blob_helper.rb' do
- element :edit_button, "_('Edit')" # rubocop:disable QA/ElementWithPattern
- element :delete_button, '_("Delete")' # rubocop:disable QA/ElementWithPattern
- end
-
view 'app/assets/javascripts/vue_shared/components/web_ide_link.vue' do
element :edit_button
end
diff --git a/qa/qa/page/group/members.rb b/qa/qa/page/group/members.rb
index c80bdadb11f..58febaddfa0 100644
--- a/qa/qa/page/group/members.rb
+++ b/qa/qa/page/group/members.rb
@@ -9,7 +9,7 @@ module QA
include Page::Component::MembersFilter
view 'app/assets/javascripts/members/components/modals/remove_member_modal.vue' do
- element :remove_member_modal_content
+ element :remove_member_modal
end
view 'app/assets/javascripts/pages/groups/group_members/index.js' do
@@ -45,7 +45,7 @@ module QA
click_element :delete_member_button
end
- within_element(:remove_member_modal_content) do
+ within_element(:remove_member_modal) do
click_button("Remove member")
end
end
diff --git a/qa/qa/page/group/menu.rb b/qa/qa/page/group/menu.rb
index 783fbd25929..de065ca187d 100644
--- a/qa/qa/page/group/menu.rb
+++ b/qa/qa/page/group/menu.rb
@@ -47,7 +47,7 @@ module QA
def go_to_package_settings
hover_group_settings do
within_submenu do
- click_element(:sidebar_menu_item_link, menu_item: 'Packages & Registries')
+ click_element(:sidebar_menu_item_link, menu_item: 'Packages and registries')
end
end
end
@@ -122,8 +122,8 @@ module QA
def hover_group_packages
within_sidebar do
- scroll_to_element(:sidebar_menu_link, menu_item: 'Packages & Registries')
- find_element(:sidebar_menu_link, menu_item: 'Packages & Registries').hover
+ scroll_to_element(:sidebar_menu_link, menu_item: 'Packages and registries')
+ find_element(:sidebar_menu_link, menu_item: 'Packages and registries').hover
yield
end
diff --git a/qa/qa/page/group/settings/package_registries.rb b/qa/qa/page/group/settings/package_registries.rb
index 35186159dc9..bcf51f0f223 100644
--- a/qa/qa/page/group/settings/package_registries.rb
+++ b/qa/qa/page/group/settings/package_registries.rb
@@ -8,8 +8,7 @@ module QA
view 'app/assets/javascripts/packages_and_registries/settings/group/components/packages_settings.vue' do
element :package_registry_settings_content
- element :reject_duplicates_toggle
- element :reject_duplicates_label
+ element :allow_duplicates_toggle
end
view 'app/assets/javascripts/packages_and_registries/settings/group/components/dependency_proxy_settings.vue' do
@@ -17,32 +16,32 @@ module QA
element :dependency_proxy_setting_toggle
end
- def set_reject_duplicates_disabled
+ def set_allow_duplicates_disabled
within_element :package_registry_settings_content do
- click_on_reject_duplicates_button if duplicates_disabled?
+ click_on_allow_duplicates_button if duplicates_enabled?
end
end
- def set_reject_duplicates_enabled
+ def set_allow_duplicates_enabled
within_element :package_registry_settings_content do
- click_on_reject_duplicates_button unless duplicates_disabled?
+ click_on_allow_duplicates_button unless duplicates_enabled?
end
end
- def click_on_reject_duplicates_button
- with_reject_duplicates_button do |button|
+ def click_on_allow_duplicates_button
+ with_allow_duplicates_button do |button|
button.click
end
end
- def duplicates_disabled?
- with_reject_duplicates_button do |button|
+ def duplicates_enabled?
+ with_allow_duplicates_button do |button|
button[:class].include?('is-checked')
end
end
- def with_reject_duplicates_button
- within_element :reject_duplicates_toggle do
+ def with_allow_duplicates_button
+ within_element :allow_duplicates_toggle do
toggle = find('button.gl-toggle:not(.is-disabled)')
yield(toggle)
end
diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb
index 90b419f8cef..aaf10e12e82 100644
--- a/qa/qa/page/main/menu.rb
+++ b/qa/qa/page/main/menu.rb
@@ -53,28 +53,45 @@ module QA
element :search_term_field
end
+ view 'app/views/layouts/header/_new_dropdown.html.haml' do
+ element :new_menu_toggle
+ end
+
+ view 'app/helpers/nav/new_dropdown_helper.rb' do
+ element :global_new_group_link
+ element :global_new_project_link
+ end
+
def go_to_groups
within_groups_menu do
- click_element(:menu_item_link, title: 'Your groups')
+ # Remove if statement once :remove_extra_primary_submenu_options ff is enabled by default
+ if has_element?(:menu_item_link, title: 'Your groups')
+ click_element(:menu_item_link, title: 'Your groups')
+ else
+ click_element(:menu_item_link, title: 'View all groups')
+ end
end
end
def go_to_create_group
- within_groups_menu do
- click_element(:menu_item_link, title: 'Create group')
- end
+ click_element(:new_menu_toggle)
+ click_element(:global_new_group_link)
end
def go_to_projects
within_projects_menu do
- click_element(:menu_item_link, title: 'Your projects')
+ # Remove if statement once :remove_extra_primary_submenu_options ff is enabled by default
+ if has_element?(:menu_item_link, title: 'Your projects')
+ click_element(:menu_item_link, title: 'Your projects')
+ else
+ click_element(:menu_item_link, title: 'View all projects')
+ end
end
end
def go_to_create_project
- within_projects_menu do
- click_element(:menu_item_link, title: 'Create new project')
- end
+ click_element(:new_menu_toggle)
+ click_element(:global_new_project_link)
end
def go_to_menu_dropdown_option(option_name)
diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb
index 9fc0cf0ccf8..2587241ed18 100644
--- a/qa/qa/page/merge_request/show.rb
+++ b/qa/qa/page/merge_request/show.rb
@@ -363,7 +363,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).has_text?("when pipeline succeeds") }
+ wait_until { !find_element(:merge_button).text.include?('when pipeline succeeds') }
click_element(:merge_button)
end
diff --git a/qa/qa/page/profile/ssh_keys.rb b/qa/qa/page/profile/ssh_keys.rb
index db71062cec6..0653df62560 100644
--- a/qa/qa/page/profile/ssh_keys.rb
+++ b/qa/qa/page/profile/ssh_keys.rb
@@ -5,12 +5,15 @@ module QA
module Profile
class SSHKeys < Page::Base
view 'app/views/profiles/keys/_form.html.haml' do
- element :key_expiry_date_field
element :key_title_field
element :key_public_key_field
element :add_key_button
end
+ view 'app/assets/javascripts/access_tokens/components/expires_at_field.vue' do
+ element :expiry_date_field
+ end
+
view 'app/helpers/ssh_keys_helper.rb' do
element :delete_ssh_key_button
element :ssh_key_delete_modal
@@ -25,19 +28,21 @@ module QA
fill_element(:key_title_field, title)
# Expire in 2 days just in case the key is created just before midnight
fill_expiry_date(Date.today + 2)
+ # Close the datepicker
+ find_element(:expiry_date_field).find('input').send_keys(:enter)
click_element(:add_key_button)
end
def fill_expiry_date(date)
- date = date.strftime('%m/%d/%Y') if date.is_a?(Date)
+ date = date.strftime('%Y-%m-%d') if date.is_a?(Date)
begin
- Date.strptime(date, '%m/%d/%Y')
+ Date.strptime(date, '%Y-%m-%d')
rescue ArgumentError
- raise "Expiry date must be in mm/dd/yyyy format"
+ raise "Expiry date must be in YYYY-MM-DD format"
end
- fill_element(:key_expiry_date_field, date)
+ fill_element(:expiry_date_field, date)
end
def remove_key(title)
diff --git a/qa/qa/page/project/fork/new.rb b/qa/qa/page/project/fork/new.rb
index e1b5e47dd0b..b622b341685 100644
--- a/qa/qa/page/project/fork/new.rb
+++ b/qa/qa/page/project/fork/new.rb
@@ -6,19 +6,40 @@ module QA
module Fork
class New < Page::Base
view 'app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue' do
- element :fork_namespace_dropdown
element :fork_project_button
element :fork_privacy_button
end
+ view 'app/assets/javascripts/pages/projects/forks/new/components/project_namespace.vue' do
+ element :select_namespace_dropdown
+ element :select_namespace_dropdown_item
+ element :select_namespace_dropdown_search_field
+ element :select_namespace_dropdown_item
+ end
+
def fork_project(namespace = Runtime::Namespace.path)
- select_element(:fork_namespace_dropdown, namespace)
+ choose_namespace(namespace)
click_element(:fork_privacy_button, privacy_level: 'public')
click_element(:fork_project_button)
end
- def fork_namespace_dropdown_values
- find_element(:fork_namespace_dropdown).all(:option).map { |option| option.text.tr("\n", '').strip }
+ def get_list_of_namespaces
+ click_element(:select_namespace_dropdown)
+ wait_until(reload: false) do
+ has_element?(:select_namespace_dropdown_item)
+ end
+ all_elements(:select_namespace_dropdown_item, minimum: 1).map(&:text)
+ end
+
+ def choose_namespace(namespace)
+ retry_on_exception do
+ click_element(:select_namespace_dropdown)
+ fill_element(:select_namespace_dropdown_search_field, namespace)
+ wait_until(reload: false) do
+ has_element?(:select_namespace_dropdown_item, text: namespace)
+ end
+ click_button(namespace)
+ end
end
end
end
diff --git a/qa/qa/page/project/infrastructure/kubernetes/index.rb b/qa/qa/page/project/infrastructure/kubernetes/index.rb
index 34d2ad55429..4c759a049e1 100644
--- a/qa/qa/page/project/infrastructure/kubernetes/index.rb
+++ b/qa/qa/page/project/infrastructure/kubernetes/index.rb
@@ -10,18 +10,13 @@ module QA
element :clusters_actions_button
end
- def connect_existing_cluster
- within_element(:clusters_actions_button) { click_button(class: 'dropdown-toggle-split') }
- click_link 'Connect a cluster (certificate - deprecated)'
+ def connect_cluster
+ click_element(:clusters_actions_button)
end
def has_cluster?(cluster)
has_element?(:cluster, cluster_name: cluster.to_s)
end
-
- def click_on_cluster(cluster)
- click_on cluster.cluster_name
- end
end
end
end
diff --git a/qa/qa/page/project/job/show.rb b/qa/qa/page/project/job/show.rb
index 2fb925b3930..5506c5ed4d9 100644
--- a/qa/qa/page/project/job/show.rb
+++ b/qa/qa/page/project/job/show.rb
@@ -11,15 +11,15 @@ module QA
element :job_log_content
end
- view 'app/assets/javascripts/jobs/components/stages_dropdown.vue' do
+ view 'app/assets/javascripts/jobs/components/job/sidebar/stages_dropdown.vue' do
element :pipeline_path, required: true
end
- view 'app/assets/javascripts/jobs/components/sidebar.vue' do
+ view 'app/assets/javascripts/jobs/components/job/sidebar/legacy_sidebar_header.vue' do
element :retry_button
end
- view 'app/assets/javascripts/jobs/components/artifacts_block.vue' do
+ view 'app/assets/javascripts/jobs/components/job/sidebar/artifacts_block.vue' do
element :browse_artifacts_button
end
diff --git a/qa/qa/page/project/monitor/metrics/show.rb b/qa/qa/page/project/monitor/metrics/show.rb
index 70ebc795595..59602d0fcf7 100644
--- a/qa/qa/page/project/monitor/metrics/show.rb
+++ b/qa/qa/page/project/monitor/metrics/show.rb
@@ -10,7 +10,7 @@ module QA
LOADING_MESSAGE = 'Waiting for performance data'
view 'app/assets/javascripts/monitoring/components/dashboard.vue' do
- element :prometheus_graphs
+ element :prometheus_graphs_content
end
view 'app/assets/javascripts/monitoring/components/dashboard_header.vue' do
@@ -54,7 +54,7 @@ module QA
end
def has_metrics?
- within_element :prometheus_graphs do
+ within_element :prometheus_graphs_content do
has_text?(EXPECTED_TITLE)
end
end
@@ -102,7 +102,7 @@ module QA
end
def has_custom_metric?(metric)
- within_element :prometheus_graphs do
+ within_element :prometheus_graphs_content do
has_text?(metric)
end
end
@@ -114,7 +114,7 @@ module QA
end
def has_template_metric?(metric)
- within_element :prometheus_graphs do
+ within_element :prometheus_graphs_content do
has_text?(metric)
end
end
diff --git a/qa/qa/page/project/new.rb b/qa/qa/page/project/new.rb
index bb4fb84f2d2..fd650d8ca20 100644
--- a/qa/qa/page/project/new.rb
+++ b/qa/qa/page/project/new.rb
@@ -23,7 +23,7 @@ module QA
view 'app/views/projects/project_templates/_template.html.haml' do
element :use_template_button
- element :template_option_row
+ element :template_option_container
end
view 'app/assets/javascripts/projects/new/components/new_project_url_select.vue' do
diff --git a/qa/qa/page/project/packages/index.rb b/qa/qa/page/project/packages/index.rb
index 86a86c05c12..e58ffba3cd5 100644
--- a/qa/qa/page/project/packages/index.rb
+++ b/qa/qa/page/project/packages/index.rb
@@ -6,7 +6,10 @@ module QA
module Packages
class Index < QA::Page::Base
view 'app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue' do
- element :package_row
+ element :package_link
+ end
+
+ view 'app/assets/javascripts/packages_and_registries/infrastructure_registry/shared/package_list_row.vue' do
element :package_link
end
@@ -18,6 +21,10 @@ module QA
has_element?(:package_link, text: name, wait: 20)
end
+ def has_module?(name)
+ has_element?(:package_link, text: name, wait: 20)
+ end
+
def has_no_package?(name)
has_no_element?(:package_link, text: name)
end
diff --git a/qa/qa/page/project/pipeline/new.rb b/qa/qa/page/project/pipeline/new.rb
index 6cf5c3b1134..742fcad5c07 100644
--- a/qa/qa/page/project/pipeline/new.rb
+++ b/qa/qa/page/project/pipeline/new.rb
@@ -5,7 +5,7 @@ module QA
module Project
module Pipeline
class New < QA::Page::Base
- view 'app/assets/javascripts/pipeline_new/components/pipeline_new_form.vue' do
+ view 'app/assets/javascripts/pipeline_new/components/legacy_pipeline_new_form.vue' do
element :run_pipeline_button, required: true
element :ci_variable_row_container
element :ci_variable_key_field
diff --git a/qa/qa/page/project/settings/main.rb b/qa/qa/page/project/settings/main.rb
index 52ed630ac66..ca5d13abdae 100644
--- a/qa/qa/page/project/settings/main.rb
+++ b/qa/qa/page/project/settings/main.rb
@@ -13,11 +13,14 @@ module QA
view 'app/views/projects/edit.html.haml' do
element :advanced_settings_content
- element :merge_request_settings_content
element :visibility_features_permissions_content
element :badges_settings_content
end
+ view 'app/views/projects/settings/merge_requests/show.html.haml' do
+ element :merge_request_settings_content
+ end
+
view 'app/views/projects/settings/_general.html.haml' do
element :project_name_field
element :save_naming_topics_avatar_button
@@ -42,12 +45,6 @@ module QA
end
end
- def expand_merge_requests_settings(&block)
- expand_content(:merge_request_settings_content) do
- MergeRequest.perform(&block)
- end
- end
-
def expand_visibility_project_features_permissions(&block)
expand_content(:visibility_features_permissions_content) do
VisibilityFeaturesPermissions.perform(&block)
diff --git a/qa/qa/page/project/settings/merge_request.rb b/qa/qa/page/project/settings/merge_request.rb
index dd9c94ebbb7..d862979aeec 100644
--- a/qa/qa/page/project/settings/merge_request.rb
+++ b/qa/qa/page/project/settings/merge_request.rb
@@ -7,7 +7,7 @@ module QA
class MergeRequest < QA::Page::Base
include QA::Page::Settings::Common
- view 'app/views/projects/edit.html.haml' do
+ view 'app/views/projects/settings/merge_requests/show.html.haml' do
element :save_merge_request_changes_button
end
diff --git a/qa/qa/page/project/settings/mirroring_repositories.rb b/qa/qa/page/project/settings/mirroring_repositories.rb
index 501b31f8a95..7eeeeefdae6 100644
--- a/qa/qa/page/project/settings/mirroring_repositories.rb
+++ b/qa/qa/page/project/settings/mirroring_repositories.rb
@@ -13,6 +13,9 @@ module QA
view 'app/views/projects/mirrors/_mirror_repos.html.haml' do
element :mirror_repository_url_input
element :mirror_repository_button
+ end
+
+ view 'app/views/projects/mirrors/_mirror_repos_list.html.haml' do
element :mirror_repository_url_cell
element :mirror_last_update_at_cell
element :mirror_error_badge
diff --git a/qa/qa/page/project/settings/pages.rb b/qa/qa/page/project/settings/pages.rb
index 1417f7ec1b5..c5b8560ba9a 100644
--- a/qa/qa/page/project/settings/pages.rb
+++ b/qa/qa/page/project/settings/pages.rb
@@ -14,6 +14,7 @@ module QA
def go_to_access_page
within_element(:access_page_container) do
find('a').click
+ page.driver.browser.switch_to.window(page.driver.browser.window_handles.last)
end
end
end
diff --git a/qa/qa/page/project/settings/protected_branches.rb b/qa/qa/page/project/settings/protected_branches.rb
index 35fc87f717c..a78d8a6ccf4 100644
--- a/qa/qa/page/project/settings/protected_branches.rb
+++ b/qa/qa/page/project/settings/protected_branches.rb
@@ -11,14 +11,10 @@ module QA
end
view 'app/views/projects/protected_branches/_create_protected_branch.html.haml' do
- element :allowed_to_push_select
element :allowed_to_push_dropdown
- element :allowed_to_merge_select
+ element :allowed_to_push_dropdown_content
element :allowed_to_merge_dropdown
- end
-
- view 'app/views/projects/protected_branches/shared/_branches_list.html.haml' do
- element :protected_branches_list
+ element :allowed_to_merge_dropdown_content
end
view 'app/views/projects/protected_branches/shared/_create_protected_branch.html.haml' do
@@ -49,11 +45,11 @@ module QA
private
def select_allowed(action, allowed)
- click_element :"allowed_to_#{action}_select"
+ click_element :"allowed_to_#{action}_dropdown"
allowed[:roles] = Resource::ProtectedBranch::Roles::NO_ONE unless allowed.key?(:roles)
- within_element(:"allowed_to_#{action}_dropdown") do
+ within_element(:"allowed_to_#{action}_dropdown_content") do
click_on allowed[:roles][:description]
allowed[:users].each { |user| click_on user.username } if allowed.key?(:users)
allowed[:groups].each { |group| click_on group.name } if allowed.key?(:groups)
diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb
index e048afee8b3..26c2da07b34 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -58,8 +58,8 @@ module QA
end
view 'app/assets/javascripts/repository/components/breadcrumbs.vue' do
- element :add_to_tree
- element :new_file_option
+ element :add_to_tree_dropdown
+ element :new_file_menu_item
end
view 'app/assets/javascripts/vue_shared/components/web_ide_link.vue' do
@@ -90,8 +90,8 @@ module QA
end
def create_new_file!
- click_element :add_to_tree
- click_element :new_file_option
+ click_element :add_to_tree_dropdown
+ click_element :new_file_menu_item
end
def fork_project
diff --git a/qa/qa/page/project/sub_menus/packages.rb b/qa/qa/page/project/sub_menus/packages.rb
index 88e2101a86d..9600540c5bc 100644
--- a/qa/qa/page/project/sub_menus/packages.rb
+++ b/qa/qa/page/project/sub_menus/packages.rb
@@ -23,12 +23,20 @@ module QA
end
end
+ def go_to_infrastructure_registry
+ hover_registry do
+ within_submenu do
+ click_link('Infrastructure Registry')
+ end
+ end
+ end
+
private
def hover_registry
within_sidebar do
- scroll_to_element(:sidebar_menu_link, menu_item: 'Packages & Registries')
- find_element(:sidebar_menu_link, menu_item: 'Packages & Registries').hover
+ scroll_to_element(:sidebar_menu_link, menu_item: 'Packages and registries')
+ find_element(:sidebar_menu_link, menu_item: 'Packages and registries').hover
yield
end
diff --git a/qa/qa/page/project/sub_menus/repository.rb b/qa/qa/page/project/sub_menus/repository.rb
index e35828ecd6a..f9d55c0009c 100644
--- a/qa/qa/page/project/sub_menus/repository.rb
+++ b/qa/qa/page/project/sub_menus/repository.rb
@@ -37,6 +37,14 @@ module QA
end
end
+ def go_to_repository_contributors
+ hover_repository do
+ within_submenu do
+ click_element(:sidebar_menu_item_link, menu_item: 'Contributors')
+ end
+ end
+ end
+
private
def hover_repository
diff --git a/qa/qa/page/project/sub_menus/settings.rb b/qa/qa/page/project/sub_menus/settings.rb
index 53a5eaf60c5..2ed4c28afb7 100644
--- a/qa/qa/page/project/sub_menus/settings.rb
+++ b/qa/qa/page/project/sub_menus/settings.rb
@@ -77,6 +77,14 @@ module QA
end
end
+ def go_to_merge_request_settings
+ hover_settings do
+ within_submenu do
+ click_element(:sidebar_menu_item_link, menu_item: 'Merge requests')
+ end
+ end
+ end
+
private
def hover_settings
diff --git a/qa/qa/page/project/web_ide/edit.rb b/qa/qa/page/project/web_ide/edit.rb
index e572569e496..293fcd1e676 100644
--- a/qa/qa/page/project/web_ide/edit.rb
+++ b/qa/qa/page/project/web_ide/edit.rb
@@ -163,7 +163,7 @@ module QA
def create_new_file_from_template(file_name, template)
click_element(:new_file_button, Page::Component::WebIDE::Modal::CreateNewFile)
- within_element(:template_list) do
+ within_element(:template_list_content) do
click_on file_name
rescue Capybara::ElementNotFound
raise ElementNotFound, %Q(Couldn't find file template named "#{file_name}". Please confirm that it is a valid option.)
diff --git a/qa/qa/resource/base.rb b/qa/qa/resource/base.rb
index ba1b581b100..6025dd6ec40 100644
--- a/qa/qa/resource/base.rb
+++ b/qa/qa/resource/base.rb
@@ -12,6 +12,8 @@ module QA
NoValueError = Class.new(RuntimeError)
+ attr_reader :retrieved_from_cache
+
class << self
# Initialize new instance of class without fabrication
#
@@ -81,7 +83,7 @@ module QA
Support::FabricationTracker.start_fabrication
result = yield.tap do
fabrication_time = Time.now - start
- fabrication_http_method = if resource.api_fabrication_http_method == :get
+ fabrication_http_method = if resource.api_fabrication_http_method == :get || resource.retrieved_from_cache
if include?(Reusable)
"Retrieved for reuse"
else
@@ -92,24 +94,28 @@ module QA
end
Support::FabricationTracker.save_fabrication(:"#{fabrication_method}_fabrication", fabrication_time)
- Tools::TestResourceDataProcessor.collect(
- resource: resource,
- info: resource.identifier,
- fabrication_method: fabrication_method,
- fabrication_time: fabrication_time
- )
+
+ unless resource.retrieved_from_cache
+ Tools::TestResourceDataProcessor.collect(
+ resource: resource,
+ info: resource.identifier,
+ fabrication_method: fabrication_method,
+ fabrication_time: fabrication_time
+ )
+ end
Runtime::Logger.info do
msg = ["==#{'=' * parents.size}>"]
msg << "#{fabrication_http_method} a #{Rainbow(name).black.bg(:white)}"
msg << resource.identifier
msg << "as a dependency of #{parents.last}" if parents.any?
- msg << "via #{fabrication_method}"
+ msg << "via #{resource.retrieved_from_cache ? 'cache' : fabrication_method}"
msg << "in #{fabrication_time.round(2)} seconds"
msg.compact.join(' ')
end
end
+
Support::FabricationTracker.finish_fabrication
result
@@ -263,6 +269,8 @@ module QA
end
def log_having_both_api_result_and_block(name, api_value)
+ api_value = "[MASKED]" if name == :token
+
QA::Runtime::Logger.debug(<<~MSG.strip)
<#{self.class}> Attribute #{name.inspect} has both API response `#{api_value}` and a block. API response will be picked. Block will be ignored.
MSG
diff --git a/qa/qa/resource/ci_variable.rb b/qa/qa/resource/ci_variable.rb
index ef663bb613f..b632446623d 100644
--- a/qa/qa/resource/ci_variable.rb
+++ b/qa/qa/resource/ci_variable.rb
@@ -3,7 +3,7 @@
module QA
module Resource
class CiVariable < Base
- attr_accessor :key, :value, :masked, :protected
+ attr_accessor :key, :value, :masked, :protected, :variable_type
attribute :project do
Project.fabricate! do |resource|
@@ -15,6 +15,7 @@ module QA
def initialize
@masked = false
@protected = false
+ @variable_type = 'env_var'
end
def fabricate!
@@ -55,7 +56,8 @@ module QA
key: key,
value: value,
masked: masked,
- protected: protected
+ protected: protected,
+ variable_type: variable_type
}
end
end
diff --git a/qa/qa/resource/clusters/agent.rb b/qa/qa/resource/clusters/agent.rb
index b190634f357..9574289a2ed 100644
--- a/qa/qa/resource/clusters/agent.rb
+++ b/qa/qa/resource/clusters/agent.rb
@@ -26,25 +26,18 @@ module QA
end
def api_get_path
- "gid://gitlab/Clusters::Agent/#{id}"
+ "/projects/#{project.id}/cluster_agents/#{id}"
end
def api_post_path
- "/graphql"
+ "/projects/#{project.id}/cluster_agents"
end
def api_post_body
- <<~GQL
- mutation createAgent {
- createClusterAgent(input: { projectPath: "#{project.full_path}", name: "#{@name}" }) {
- clusterAgent {
- id
- name
- }
- errors
- }
+ {
+ id: project.id,
+ name: name
}
- GQL
end
end
end
diff --git a/qa/qa/resource/clusters/agent_token.rb b/qa/qa/resource/clusters/agent_token.rb
index c1cf5c2f37b..cbd2964c31d 100644
--- a/qa/qa/resource/clusters/agent_token.rb
+++ b/qa/qa/resource/clusters/agent_token.rb
@@ -5,7 +5,7 @@ module QA
module Clusters
class AgentToken < QA::Resource::Base
attribute :id
- attribute :secret
+ attribute :token
attribute :agent do
QA::Resource::Clusters::Agent.fabricate_via_api!
end
@@ -20,26 +20,19 @@ module QA
end
def api_get_path
- "gid://gitlab/Clusters::AgentToken/#{id}"
+ "/projects/#{agent.project.id}/cluster_agents/#{agent.id}/tokens/#{id}"
end
def api_post_path
- "/graphql"
+ "/projects/#{agent.project.id}/cluster_agents/#{agent.id}/tokens"
end
def api_post_body
- <<~GQL
- mutation createToken {
- clusterAgentTokenCreate(input: { clusterAgentId: "gid://gitlab/Clusters::Agent/#{agent.id}" name: "token-#{agent.id}" }) {
- secret # This is the value you need to use on the next step
- token {
- createdAt
- id
- }
- errors
- }
+ {
+ id: agent.project.id,
+ agent_id: agent.id,
+ name: agent.name
}
- GQL
end
end
end
diff --git a/qa/qa/resource/fork.rb b/qa/qa/resource/fork.rb
index 0e6dd626312..2016b1d948d 100644
--- a/qa/qa/resource/fork.rb
+++ b/qa/qa/resource/fork.rb
@@ -36,7 +36,7 @@ module QA
def fabricate!
populate(:upstream, :user)
- namespace_path ||= user.name
+ namespace_path ||= user.username
# Sign out as admin and sign is as the fork user
Flow::Login.sign_in(as: user)
diff --git a/qa/qa/resource/group.rb b/qa/qa/resource/group.rb
index 60f6cbdfc51..84416c0600d 100644
--- a/qa/qa/resource/group.rb
+++ b/qa/qa/resource/group.rb
@@ -26,6 +26,8 @@ module QA
def initialize
@description = "QA test run at #{Runtime::Namespace.time}"
@require_two_factor_authentication = false
+ # Add visibility to enable create private group
+ @visibility = 'public'
end
def fabricate!
@@ -39,6 +41,7 @@ module QA
Page::Group::New.perform do |group_new|
group_new.set_path(path)
+ group_new.set_visibility(@visibility)
group_new.create_subgroup
end
@@ -73,7 +76,7 @@ module QA
parent_id: sandbox.id,
path: path,
name: name,
- visibility: 'public',
+ visibility: @visibility.downcase,
require_two_factor_authentication: @require_two_factor_authentication,
avatar: avatar
}
diff --git a/qa/qa/resource/group_access_token.rb b/qa/qa/resource/group_access_token.rb
index 934348a1737..a1079b16971 100644
--- a/qa/qa/resource/group_access_token.rb
+++ b/qa/qa/resource/group_access_token.rb
@@ -8,30 +8,50 @@ module QA
attr_writer :name
attribute :id
+ attribute :user_id
+ attribute :expires_at
+ attribute :token
+
attribute :group do
Group.fabricate!
end
- attribute :token do
- Page::Group::Settings::AccessTokens.perform(&:created_access_token)
+ def api_get_path
+ "/groups/#{group.id}/access_tokens/#{id}"
+ rescue NoValueError
+ token = parse_body(api_get_from("/groups/#{group.id}/access_tokens")).find { |t| t[:name] == name }
+
+ raise ResourceNotFoundError unless token
+
+ @id = token[:id]
+ retry
end
- def api_get_path
- "/groups/#{group.id}/access_tokens"
+ def identifier
+ "with name '#{name}', token's bot username '#{token_user[:username]}'"
end
def api_post_path
- api_get_path
+ "/groups/#{group.id}/access_tokens"
+ end
+
+ def api_user_path
+ "/users/#{user_id}"
+ end
+
+ def token_user
+ parse_body(api_get_from(api_user_path))
end
def name
- @name || 'api-group-access-token'
+ @name ||= "api-group-access-token-#{Faker::Alphanumeric.alphanumeric(number: 8)}"
end
def api_post_body
{
name: name,
- scopes: ["api"]
+ scopes: ["api"],
+ expires_at: expires_at.to_s
}
end
@@ -51,6 +71,11 @@ module QA
end
end
+ # Expire in 2 days just in case the token is created just before midnight
+ def expires_at
+ @expires_at || Time.now.utc.to_date + 2
+ end
+
def fabricate!
Flow::Login.sign_in_unless_signed_in
@@ -59,12 +84,14 @@ module QA
Page::Group::Menu.perform(&:go_to_access_token_settings)
Page::Group::Settings::AccessTokens.perform do |token_page|
- token_page.fill_token_name(name || 'api-project-access-token')
+ token_page.fill_token_name(name)
token_page.check_api
- # Expire in 2 days just in case the token is created just before midnight
- token_page.fill_expiry_date(Time.now.utc.to_date + 2)
+ token_page.fill_expiry_date(expires_at)
token_page.click_create_token_button
+ self.token = token_page.created_access_token
end
+
+ reload!
end
end
end
diff --git a/qa/qa/resource/group_base.rb b/qa/qa/resource/group_base.rb
index bda72703906..f6d1aacca0a 100644
--- a/qa/qa/resource/group_base.rb
+++ b/qa/qa/resource/group_base.rb
@@ -14,7 +14,9 @@ module QA
attributes :id,
:runners_token,
:name,
- :full_path
+ :full_path,
+ # Add visibility to enable create private group
+ :visibility
# Get group projects
#
diff --git a/qa/qa/resource/impersonation_token.rb b/qa/qa/resource/impersonation_token.rb
index 3bd356b5e9b..99a9cd7bbab 100644
--- a/qa/qa/resource/impersonation_token.rb
+++ b/qa/qa/resource/impersonation_token.rb
@@ -22,7 +22,7 @@ module QA
end
def api_post_path
- api_get_path
+ "/users/#{user.id}/impersonation_tokens"
end
def name
diff --git a/qa/qa/resource/job.rb b/qa/qa/resource/job.rb
index 96c502e567c..5b0dac9b2df 100644
--- a/qa/qa/resource/job.rb
+++ b/qa/qa/resource/job.rb
@@ -22,6 +22,10 @@ module QA
"/projects/#{project.id}/jobs/#{id}"
end
+ def api_trace_path
+ "#{api_get_path}/trace"
+ end
+
def api_post_path
end
@@ -30,6 +34,11 @@ module QA
artifacts: artifacts
}
end
+
+ # Job log
+ def trace
+ get(request_url(api_trace_path))
+ end
end
end
end
diff --git a/qa/qa/resource/kubernetes_cluster/project_cluster.rb b/qa/qa/resource/kubernetes_cluster/project_cluster.rb
deleted file mode 100644
index 0443b26064e..00000000000
--- a/qa/qa/resource/kubernetes_cluster/project_cluster.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- module Resource
- module KubernetesCluster
- # TODO: This resource is currently broken, since one-click apps have been removed.
- # See https://gitlab.com/gitlab-org/gitlab/-/issues/333818
- class ProjectCluster < Base
- attr_writer :cluster,
- :install_ingress, :install_prometheus, :install_runner, :domain
-
- attribute :project do
- Resource::Project.fabricate!
- end
-
- attribute :ingress_ip do
- @cluster.fetch_external_ip_for_ingress
- end
-
- def fabricate!
- project.visit!
-
- Page::Project::Menu.perform(
- &:go_to_infrastructure_kubernetes)
-
- Page::Project::Infrastructure::Kubernetes::Index.perform(
- &:connect_existing_cluster)
-
- Page::Project::Infrastructure::Kubernetes::AddExisting.perform do |cluster_page|
- cluster_page.set_cluster_name(@cluster.cluster_name)
- cluster_page.set_api_url(@cluster.api_url)
- cluster_page.set_ca_certificate(@cluster.ca_certificate)
- cluster_page.set_token(@cluster.token)
- cluster_page.uncheck_rbac! unless @cluster.rbac
- cluster_page.add_cluster!
- end
-
- Page::Project::Infrastructure::Kubernetes::Show.perform do |show|
- if @install_ingress
- ingress_ip
-
- show.set_domain("#{@ingress_ip}.nip.io")
- show.save_domain
- end
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/resource/personal_access_token.rb b/qa/qa/resource/personal_access_token.rb
index ad0f183c603..33044774c59 100644
--- a/qa/qa/resource/personal_access_token.rb
+++ b/qa/qa/resource/personal_access_token.rb
@@ -5,12 +5,13 @@ require 'date'
module QA
module Resource
class PersonalAccessToken < Base
- attr_accessor :name
+ attr_writer :name
# The user for which the personal access token is to be created
# This *could* be different than the api_client.user or the api_user provided by the QA::Resource::ApiFabricator
attr_writer :user
+ attribute :id
attribute :token
# Only Admins can create PAT via the API.
@@ -41,13 +42,35 @@ module QA
end
def api_get_path
- '/personal_access_tokens'
+ "/personal_access_tokens/#{id}"
+ rescue NoValueError
+ user.reload! unless user.id
+
+ api_client = Runtime::API::Client.new(:gitlab,
+ is_new_session: false,
+ user: user,
+ personal_access_token: self.token)
+ request_url = Runtime::API::Request.new(api_client,
+ "/personal_access_tokens?user_id=#{user.id}",
+ per_page: '100').url
+
+ token = auto_paginated_response(request_url).find { |t| t[:name] == name }
+
+ raise ResourceNotFoundError unless token
+
+ @id = token[:id]
+ retry
+ end
+
+ def name
+ @name ||= "api-pat-#{user.username}-#{Faker::Alphanumeric.alphanumeric(number: 8)}"
end
def api_post_body
{
- name: name || 'api-test-token',
- scopes: ["api"]
+ name: name,
+ scopes: ["api"],
+ expires_at: expires_at.to_s
}
end
@@ -59,12 +82,20 @@ module QA
def find_and_set_value
@token ||= QA::Resource::PersonalAccessTokenCache.get_token_for_username(user.username)
+ @retrieved_from_cache = true if @token
+
+ @token
end
def cache_token
QA::Resource::PersonalAccessTokenCache.set_token_for_username(user.username, self.token) if @user && self.token
end
+ # Expire in 2 days just in case the token is created just before midnight
+ def expires_at
+ @expires_at || Time.now.utc.to_date + 2
+ end
+
def fabricate!
return if find_and_set_value
@@ -76,8 +107,7 @@ module QA
Page::Profile::PersonalAccessTokens.perform do |token_page|
token_page.fill_token_name(name || 'api-test-token')
token_page.check_api
- # Expire in 2 days just in case the token is created just before midnight
- token_page.fill_expiry_date(Time.now.utc.to_date + 2)
+ token_page.fill_expiry_date(expires_at)
token_page.click_create_token_button
self.token = Page::Profile::PersonalAccessTokens.perform(&:created_access_token)
diff --git a/qa/qa/resource/project_access_token.rb b/qa/qa/resource/project_access_token.rb
index 58cb3e667c0..1ccf2103d1d 100644
--- a/qa/qa/resource/project_access_token.rb
+++ b/qa/qa/resource/project_access_token.rb
@@ -8,29 +8,50 @@ module QA
attr_writer :name
attribute :id
+ attribute :user_id
+ attribute :expires_at
+ attribute :token
+
attribute :project do
Project.fabricate!
end
- attribute :token do
- Page::Project::Settings::AccessTokens.perform(&:created_access_token)
- end
def api_get_path
- "/projects/#{project.api_resource[:id]}/access_tokens"
+ "/projects/#{project.id}/access_tokens/#{id}"
+ rescue NoValueError
+ token = parse_body(api_get_from("/projects/#{project.id}/access_tokens")).find { |t| t[:name] == name }
+
+ raise ResourceNotFoundError unless token
+
+ @id = token[:id]
+ retry
+ end
+
+ def identifier
+ "with name '#{name}', token's bot username '#{token_user[:username]}'"
end
def api_post_path
- api_get_path
+ "/projects/#{project.id}/access_tokens"
+ end
+
+ def api_user_path
+ "/users/#{user_id}"
+ end
+
+ def token_user
+ parse_body(api_get_from(api_user_path))
end
def name
- @name || 'api-project-access-token'
+ @name ||= "api-project-access-token-#{Faker::Alphanumeric.alphanumeric(number: 8)}"
end
def api_post_body
{
name: name,
- scopes: ["api"]
+ scopes: ["api"],
+ expires_at: expires_at.to_s
}
end
@@ -50,6 +71,11 @@ module QA
end
end
+ # Expire in 2 days just in case the token is created just before midnight
+ def expires_at
+ @expires_at || Time.now.utc.to_date + 2
+ end
+
def fabricate!
Flow::Login.sign_in_unless_signed_in
@@ -58,12 +84,14 @@ module QA
Page::Project::Menu.perform(&:go_to_access_token_settings)
Page::Project::Settings::AccessTokens.perform do |token_page|
- token_page.fill_token_name(name || 'api-project-access-token')
+ token_page.fill_token_name(name)
token_page.check_api
- # Expire in 2 days just in case the token is created just before midnight
- token_page.fill_expiry_date(Time.now.utc.to_date + 2)
+ token_page.fill_expiry_date(expires_at)
token_page.click_create_token_button
+ self.token = token_page.created_access_token
end
+
+ reload!
end
end
end
diff --git a/qa/qa/resource/runner.rb b/qa/qa/resource/runner.rb
index 6d5ff71b2ba..da4021f89b7 100644
--- a/qa/qa/resource/runner.rb
+++ b/qa/qa/resource/runner.rb
@@ -32,7 +32,10 @@ module QA
def fabricate_via_api!
@docker_container = Service::DockerRun::GitlabRunner.new(name).tap do |runner|
- runner.pull
+ 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
diff --git a/qa/qa/resource/sandbox.rb b/qa/qa/resource/sandbox.rb
index 12ecce1ce9a..2080e279e99 100644
--- a/qa/qa/resource/sandbox.rb
+++ b/qa/qa/resource/sandbox.rb
@@ -20,6 +20,8 @@ module QA
def initialize
@path = Runtime::Namespace.sandbox_name
+ # Visibility should be public by default for backward compatibility
+ @visibility = 'public'
end
alias_method :full_path, :path
@@ -38,7 +40,7 @@ module QA
Page::Group::New.perform do |group|
group.click_create_group
group.set_path(path)
- group.set_visibility('Public')
+ group.set_visibility(@visibility)
group.create
end
@@ -68,7 +70,7 @@ module QA
{
path: path,
name: path,
- visibility: 'public',
+ visibility: @visibility.downcase,
avatar: avatar
}
end
diff --git a/qa/qa/resource/user.rb b/qa/qa/resource/user.rb
index 2fb8b18b71f..a974446b3cb 100644
--- a/qa/qa/resource/user.rb
+++ b/qa/qa/resource/user.rb
@@ -44,7 +44,7 @@ module QA
alias_method :ldap_username, :username
def password
- @password || 'password'
+ @password ||= SecureRandom.hex(8)
end
alias_method :ldap_password, :password
@@ -232,3 +232,5 @@ module QA
end
end
end
+
+QA::Resource::User.prepend_mod_with('Resource::User', namespace: QA)
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index f705fe54b97..0dbc3cdf09d 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -95,6 +95,14 @@ module QA
# Disable /dev/shm use in CI. See https://gitlab.com/gitlab-org/gitlab/issues/4252
capabilities['goog:chromeOptions'][:args] << 'disable-dev-shm-usage' if QA::Runtime::Env.disable_dev_shm?
+ # Set chrome default download path
+ if QA::Runtime::Env.chrome_default_download_path
+ capabilities['goog:chromeOptions'][:prefs] = {
+ 'download.default_directory' => File.expand_path(QA::Runtime::Env.chrome_default_download_path),
+ 'download.prompt_for_download' => false
+ }
+ end
+
# Specify the user-agent to allow challenges to be bypassed
# See https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues/11938
if QA::Runtime::Env.user_agent
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index 1fd097d0acf..b1a912ac43e 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -263,22 +263,6 @@ module QA
ENV['GITLAB_QA_PASSWORD_6']
end
- def gitlab_qa_1p_email
- ENV['GITLAB_QA_1P_EMAIL']
- end
-
- def gitlab_qa_1p_password
- ENV['GITLAB_QA_1P_PASSWORD']
- end
-
- def gitlab_qa_1p_secret
- ENV['GITLAB_QA_1P_SECRET']
- end
-
- def gitlab_qa_1p_github_uuid
- ENV['GITLAB_QA_1P_GITHUB_UUID']
- end
-
def jira_admin_username
ENV['JIRA_ADMIN_USERNAME']
end
@@ -496,6 +480,10 @@ module QA
enabled?(ENV['QA_USE_PUBLIC_IP_API'], default: false)
end
+ def chrome_default_download_path
+ ENV['DEFAULT_CHROME_DOWNLOAD_PATH']
+ end
+
private
def remote_grid_credentials
diff --git a/qa/qa/runtime/fixtures.rb b/qa/qa/runtime/fixtures.rb
index 41d7ce5d178..d56af9b4872 100644
--- a/qa/qa/runtime/fixtures.rb
+++ b/qa/qa/runtime/fixtures.rb
@@ -49,3 +49,5 @@ module QA
end
end
end
+
+QA::Runtime::Fixtures.prepend_mod_with('Runtime::Fixtures', namespace: QA)
diff --git a/qa/qa/runtime/user.rb b/qa/qa/runtime/user.rb
index e4eeca2000f..cb2a86276f9 100644
--- a/qa/qa/runtime/user.rb
+++ b/qa/qa/runtime/user.rb
@@ -6,7 +6,10 @@ module QA
extend self
def admin
- Struct.new(:username, :password).new(admin_username, admin_password)
+ QA::Resource::User.init do |user|
+ user.username = admin_username
+ user.password = admin_password
+ end
end
def default_username
diff --git a/qa/qa/scenario/test/instance/cloud_activation.rb b/qa/qa/scenario/test/instance/cloud_activation.rb
new file mode 100644
index 00000000000..0f61cab582a
--- /dev/null
+++ b/qa/qa/scenario/test/instance/cloud_activation.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class CloudActivation < All
+ tags :cloud_activation
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/integrations.rb b/qa/qa/scenario/test/instance/integrations.rb
new file mode 100644
index 00000000000..b943b95c51e
--- /dev/null
+++ b/qa/qa/scenario/test/instance/integrations.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class Integrations < All
+ tags :integrations
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/jira.rb b/qa/qa/scenario/test/instance/jira.rb
new file mode 100644
index 00000000000..784a71515cb
--- /dev/null
+++ b/qa/qa/scenario/test/instance/jira.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class Jira < All
+ tags :jira
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/large_setup.rb b/qa/qa/scenario/test/instance/large_setup.rb
new file mode 100644
index 00000000000..934c841c619
--- /dev/null
+++ b/qa/qa/scenario/test/instance/large_setup.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class LargeSetup < All
+ tags :can_use_large_setup
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/metrics.rb b/qa/qa/scenario/test/instance/metrics.rb
new file mode 100644
index 00000000000..7643aff095b
--- /dev/null
+++ b/qa/qa/scenario/test/instance/metrics.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class Metrics < All
+ tags :metrics
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/object_storage.rb b/qa/qa/scenario/test/instance/object_storage.rb
new file mode 100644
index 00000000000..6d54b50a505
--- /dev/null
+++ b/qa/qa/scenario/test/instance/object_storage.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class ObjectStorage < All
+ tags :object_storage
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/packages.rb b/qa/qa/scenario/test/instance/packages.rb
new file mode 100644
index 00000000000..7648a2b5e80
--- /dev/null
+++ b/qa/qa/scenario/test/instance/packages.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class Packages < All
+ tags :packages
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/repository_storage.rb b/qa/qa/scenario/test/instance/repository_storage.rb
new file mode 100644
index 00000000000..c648335c353
--- /dev/null
+++ b/qa/qa/scenario/test/instance/repository_storage.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class RepositoryStorage < All
+ tags :repository_storage
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/review_blocking.rb b/qa/qa/scenario/test/instance/review_blocking.rb
new file mode 100644
index 00000000000..cb1b6c9cf10
--- /dev/null
+++ b/qa/qa/scenario/test/instance/review_blocking.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class ReviewBlocking < All
+ tags :reliable,
+ :sanity_feature_flags,
+ :"~orchestrated",
+ :"~skip_signup_disabled"
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/scenario/test/instance/review_non_blocking.rb b/qa/qa/scenario/test/instance/review_non_blocking.rb
new file mode 100644
index 00000000000..e295171eb0b
--- /dev/null
+++ b/qa/qa/scenario/test/instance/review_non_blocking.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module QA
+ module Scenario
+ module Test
+ module Instance
+ class ReviewNonBlocking < All
+ tags :"~reliable",
+ :"~smoke",
+ :"~skip_signup_disabled",
+ *Specs::Runner::DEFAULT_SKIPPED_TAGS.map { |tag| :"~#{tag}" }
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/service/cluster_provider/gcloud.rb b/qa/qa/service/cluster_provider/gcloud.rb
index 77677745f7a..14c13eecb8d 100644
--- a/qa/qa/service/cluster_provider/gcloud.rb
+++ b/qa/qa/service/cluster_provider/gcloud.rb
@@ -33,14 +33,32 @@ module QA
delete_cluster
end
- def install_ingress
- QA::Runtime::Logger.info "Attempting to install Ingress on cluster #{cluster_name}"
- shell 'kubectl create -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-0.31.0/deploy/static/provider/cloud/deploy.yaml'
- wait_for_ingress
+ # kas is hardcoded to staging since this test should only run in staging for now
+ def install_kubernetes_agent(agent_token)
+ install_helm
+
+ shell <<~CMD.tr("\n", ' ')
+ helm repo add gitlab https://charts.gitlab.io &&
+ helm repo update &&
+ helm upgrade --install test gitlab/gitlab-agent
+ --namespace gitlab-agent
+ --create-namespace
+ --set image.tag=#{Runtime::Env.gitlab_agentk_version}
+ --set config.token=#{agent_token}
+ --set config.kasAddress=wss://kas.staging.gitlab.com
+ CMD
end
private
+ def install_helm
+ shell <<~CMD.tr("\n", ' ')
+ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 &&
+ chmod 700 get_helm.sh &&
+ ./get_helm.sh
+ CMD
+ end
+
def login_if_not_already_logged_in
if Runtime::Env.has_gcloud_credentials?
attempt_login_with_env_vars
@@ -104,18 +122,6 @@ module QA
def get_region
Runtime::Env.gcloud_region || @available_regions.delete(@available_regions.sample)
end
-
- def wait_for_ingress
- QA::Runtime::Logger.info 'Waiting for Ingress controller pod to be initialized'
-
- Support::Retrier.retry_until(max_attempts: 60, sleep_interval: 1) do
- service_available?('kubectl get pods --all-namespaces -l app.kubernetes.io/component=controller | grep -o "ingress-nginx-controller.*1/1"')
- end
- end
-
- def service_available?(command)
- system("#{command} > /dev/null 2>&1")
- end
end
end
end
diff --git a/qa/qa/service/kubernetes_cluster.rb b/qa/qa/service/kubernetes_cluster.rb
index dafce4acc33..59bfacf9195 100644
--- a/qa/qa/service/kubernetes_cluster.rb
+++ b/qa/qa/service/kubernetes_cluster.rb
@@ -41,8 +41,8 @@ module QA
cluster_name
end
- def install_ingress
- @provider.install_ingress
+ def install_kubernetes_agent(agent_token)
+ @provider.install_kubernetes_agent(agent_token)
end
def create_secret(secret, secret_name)
@@ -73,16 +73,6 @@ module QA
shell('kubectl apply -f -', stdin_data: network_policy)
end
- def fetch_external_ip_for_ingress
- install_ingress
-
- # need to wait since the ingress-nginx service has an initial delay set of 10 seconds
- sleep 12
- ingress_ip = `kubectl get svc --all-namespaces --no-headers=true -l app.kubernetes.io/name=ingress-nginx -o custom-columns=:'status.loadBalancer.ingress[0].ip' | grep -v 'none'`
- QA::Runtime::Logger.debug "Has ingress address set to: #{ingress_ip}"
- ingress_ip
- end
-
private
def fetch_api_url
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
new file mode 100644
index 00000000000..2058d58d5d6
--- /dev/null
+++ b/qa/qa/specs/features/api/12_systems/gitaly/automatic_failover_and_recovery_spec.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Systems' do
+ context 'with Gitaly automatic failover and recovery', :orchestrated, :gitaly_cluster do
+ # Variables shared between contexts. They're used and shared between
+ # contexts so they can't be `let` variables.
+ praefect_manager = Service::PraefectManager.new
+ project = nil
+
+ let(:intial_commit_message) { 'Initial commit' }
+ let(:first_added_commit_message) { 'first_added_commit_message to primary gitaly node' }
+ let(:second_added_commit_message) { 'second_added_commit_message to failover node' }
+
+ before(:context) do
+ praefect_manager.start_all_nodes
+
+ project = Resource::Project.fabricate! do |project|
+ project.name = "gitaly_cluster"
+ project.initialize_with_readme = true
+ end
+ # We need to ensure that the the project is replicated to all nodes before proceeding with this test
+ praefect_manager.wait_for_replication(project.id)
+ end
+
+ 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
+
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.commit_message = first_added_commit_message
+ push.new_branch = false
+ push.file_content = 'This file created on gitaly1 while gitaly2/gitaly3 not running'
+ end
+
+ praefect_manager.start_all_nodes
+ praefect_manager.wait_for_replication(project.id)
+
+ # 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
+
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = second_added_commit_message
+ commit.add_files([
+ {
+ file_path: "file-#{SecureRandom.hex(8)}",
+ content: 'This is created on gitaly2/gitaly3 while gitaly1 is unavailable'
+ }
+ ])
+ end
+
+ # Confirm that we have access to the repo after failover,
+ # including the commit we just added
+ expect(project.commits.map { |commit| commit[:message].chomp })
+ .to include(intial_commit_message)
+ .and include(first_added_commit_message)
+ .and include(second_added_commit_message)
+ end
+
+ 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
+
+ # Confirm automatic reconciliation
+ expect(praefect_manager.replicated?(project.id)).to be true
+
+ # Confirm that all commits are available after reconciliation
+ expect(project.commits.map { |commit| commit[:message].chomp })
+ .to include(intial_commit_message)
+ .and include(first_added_commit_message)
+ .and include(second_added_commit_message)
+
+ # Restore the original primary node
+ praefect_manager.start_all_nodes
+
+ # Check that all commits are still available even though the primary
+ # node was offline when one was made
+ expect(project.commits.map { |commit| commit[:message].chomp })
+ .to include(intial_commit_message)
+ .and include(first_added_commit_message)
+ .and include(second_added_commit_message)
+ end
+ end
+ end
+ end
+end
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
new file mode 100644
index 00000000000..0b4bdf550f8
--- /dev/null
+++ b/qa/qa/specs/features/api/12_systems/gitaly/backend_node_recovery_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Systems' do
+ describe 'Gitaly backend node recovery', :orchestrated, :gitaly_cluster, :skip_live_env do
+ let(:praefect_manager) { Service::PraefectManager.new }
+ let(:project) do
+ Resource::Project.fabricate! do |project|
+ project.name = "gitaly_cluster"
+ project.initialize_with_readme = true
+ end
+ end
+
+ before do
+ # Reset the cluster in case previous tests left it in a bad state
+ praefect_manager.start_all_nodes
+ end
+
+ after do
+ # Leave the cluster in a suitable state for subsequent tests
+ praefect_manager.start_all_nodes
+ end
+
+ it 'recovers from dataloss', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347832' do
+ # Create a new project with a commit and wait for it to replicate
+ praefect_manager.wait_for_replication(project.id)
+
+ # 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
+
+ # Push a commit to the new primary
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.new_branch = false
+ push.commit_message = 'pushed after failover'
+ push.file_name = 'new_file'
+ push.file_content = 'new file'
+ end
+
+ # Confirm that the commit is waiting to be replicated
+ expect(praefect_manager).to be_replication_pending
+
+ # Start the old primary node again
+ praefect_manager.start_primary_node
+ praefect_manager.wait_for_health_check_all_nodes
+
+ # Wait for automatic replication
+ praefect_manager.wait_for_replication(project.id)
+
+ # Force switch to the old primary node
+ # This ensures that the commit was replicated
+ praefect_manager.stop_secondary_node
+ praefect_manager.stop_tertiary_node
+
+ # Confirm that both commits are available
+ expect(project.commits.map { |commit| commit[:message].chomp })
+ .to include("Initial commit").and include("pushed after failover")
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/12_systems/gitaly/changing_repository_storage_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/changing_repository_storage_spec.rb
new file mode 100644
index 00000000000..18ec8e0a8b4
--- /dev/null
+++ b/qa/qa/specs/features/api/12_systems/gitaly/changing_repository_storage_spec.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Systems' do
+ describe 'Changing Gitaly repository storage', :requires_admin, except: { job: 'review-qa-*' } do
+ praefect_manager = Service::PraefectManager.new
+
+ shared_examples 'repository storage move' do
+ it 'confirms a `finished` status after moving project repository storage' do
+ expect(project).to have_file('README.md')
+ expect { project.change_repository_storage(destination_storage[:name]) }.not_to raise_error
+ expect { praefect_manager.verify_storage_move(source_storage, destination_storage, repo_type: :project) }
+ .not_to raise_error
+
+ Support::Retrier.retry_on_exception(sleep_interval: 5) do
+ # For a short period of time after migrating, the repository can be 'read only' which may lead to errors
+ # 'The repository is temporarily read-only. Please try again later.'
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add new file'
+ commit.add_files([
+ { file_path: 'new_file', content: '# This is a new file' }
+ ])
+ end
+ end
+
+ expect(project).to have_file('README.md')
+ expect(project).to have_file('new_file')
+ end
+ end
+
+ context 'when moving from one Gitaly storage to another', :orchestrated, :repository_storage,
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347827' do
+ let(:source_storage) { { type: :gitaly, name: 'default' } }
+ let(:destination_storage) { { type: :gitaly, name: QA::Runtime::Env.additional_repository_storage } }
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'repo-storage-move-status'
+ project.initialize_with_readme = true
+ project.api_client = Runtime::API::Client.as_admin
+ end
+ end
+
+ before do
+ praefect_manager.gitlab = 'gitlab'
+ end
+
+ it_behaves_like 'repository storage move'
+ end
+
+ # Note: This test doesn't have the :orchestrated tag because it runs in the Test::Integration::Praefect
+ # scenario with other tests that aren't considered orchestrated.
+ # It also runs on staging using nfs-file07 as non-cluster storage and nfs-file22 as cluster/praefect storage
+ context 'when moving from Gitaly to Gitaly Cluster', :requires_praefect,
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347828' do
+ let(:source_storage) { { type: :gitaly, name: QA::Runtime::Env.non_cluster_repository_storage } }
+ let(:destination_storage) { { type: :praefect, name: QA::Runtime::Env.praefect_repository_storage } }
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'repo-storage-move'
+ project.initialize_with_readme = true
+ project.repository_storage = source_storage[:name]
+ project.api_client = Runtime::API::Client.as_admin
+ end
+ end
+
+ before do
+ praefect_manager.gitlab = 'gitlab-gitaly-cluster'
+ end
+
+ it_behaves_like 'repository storage move'
+ end
+
+ # Note: This test doesn't have the :orchestrated tag because it runs in the Test::Integration::Praefect
+ # scenario with other tests that aren't considered orchestrated.
+ # It also runs on staging using nfs-file07 as non-cluster storage and nfs-file22 as cluster/praefect storage
+ context 'when moving from Gitaly Cluster to Gitaly', :requires_praefect,
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/369204' do
+ let(:source_storage) { { type: :praefect, name: QA::Runtime::Env.praefect_repository_storage } }
+ let(:destination_storage) { { type: :gitaly, name: QA::Runtime::Env.non_cluster_repository_storage } }
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'repo-storage-move'
+ project.initialize_with_readme = true
+ project.repository_storage = source_storage[:name]
+ project.api_client = Runtime::API::Client.as_admin
+ end
+ end
+
+ before do
+ praefect_manager.gitlab = 'gitlab-gitaly-cluster'
+ end
+
+ it_behaves_like 'repository storage move'
+ end
+ end
+ end
+end
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
new file mode 100644
index 00000000000..692297e40ce
--- /dev/null
+++ b/qa/qa/specs/features/api/12_systems/gitaly/distributed_reads_spec.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+require 'parallel'
+
+module QA
+ RSpec.describe 'Systems' do
+ describe 'Gitaly distributed reads', :orchestrated, :gitaly_cluster, :skip_live_env, :requires_admin do
+ let(:number_of_reads_per_loop) { 9 }
+ let(:praefect_manager) { Service::PraefectManager.new }
+ let(:project) do
+ Resource::Project.fabricate! do |project|
+ project.name = "gitaly_cluster"
+ project.initialize_with_readme = true
+ end
+ end
+
+ before do
+ praefect_manager.start_all_nodes
+ praefect_manager.wait_for_replication(project.id)
+ end
+
+ it 'reads from each node', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347833' do
+ pre_read_data = praefect_manager.query_read_distribution
+
+ wait_for_reads_to_increase(project, number_of_reads_per_loop, pre_read_data)
+
+ aggregate_failures "each gitaly node" do
+ praefect_manager.query_read_distribution.each_with_index do |data, index|
+ pre_read_count = praefect_manager.value_for_node(pre_read_data, data[:node])
+ QA::Runtime::Logger.debug("Node: #{data[:node]}; before: #{pre_read_count}; now: #{data[:value]}")
+ expect(data[:value]).to be > pre_read_count,
+ "Read counts did not differ for node #{data[:node]}"
+ end
+ end
+ end
+
+ context 'when a node is unhealthy' do
+ before do
+ praefect_manager.stop_secondary_node
+ end
+
+ after do
+ # Leave the cluster in a suitable state for subsequent tests
+ praefect_manager.start_secondary_node
+ end
+
+ it 'does not read from the unhealthy node',
+ 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)
+
+ praefect_manager.wait_for_read_count_change(pre_read_data)
+
+ post_read_data = praefect_manager.query_read_distribution
+
+ aggregate_failures "each gitaly node" do
+ expect(praefect_manager.value_for_node(post_read_data, 'gitaly1'))
+ .to be > praefect_manager.value_for_node(pre_read_data, 'gitaly1')
+ expect(praefect_manager.value_for_node(post_read_data, 'gitaly2'))
+ .to eq praefect_manager.value_for_node(pre_read_data, 'gitaly2')
+ expect(praefect_manager.value_for_node(post_read_data, 'gitaly3'))
+ .to be > praefect_manager.value_for_node(pre_read_data, 'gitaly3')
+ end
+ end
+ end
+
+ def read_from_project(project, number_of_reads)
+ QA::Runtime::Logger.info('Reading from the repository')
+ Parallel.each((1..number_of_reads)) do
+ Git::Repository.perform do |repository|
+ repository.uri = project.repository_http_location.uri
+ repository.use_default_credentials
+ repository.clone
+ end
+ end
+ end
+
+ def wait_for_reads_to_increase(project, number_of_reads, pre_read_data)
+ diff_found = pre_read_data
+
+ Support::Waiter.wait_until(sleep_interval: 5, raise_on_failure: false) do
+ read_from_project(project, number_of_reads)
+
+ praefect_manager.query_read_distribution.each_with_index do |data, index|
+ diff_found[index] = {} unless diff_found[index]
+ if data[:value] > praefect_manager.value_for_node(pre_read_data, data[:node])
+ diff_found[index][:diff] = true
+ end
+ end
+ diff_found.all? { |node| node.key?(:diff) && node[:diff] }
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/12_systems/gitaly/gitaly_mtls_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/gitaly_mtls_spec.rb
new file mode 100644
index 00000000000..a4b39554453
--- /dev/null
+++ b/qa/qa/specs/features/api/12_systems/gitaly/gitaly_mtls_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Systems' do
+ describe 'Gitaly using mTLS', :orchestrated, :mtls do
+ let(:intial_commit_message) { 'Initial commit' }
+ let(:first_added_commit_message) { 'commit over git' }
+ let(:second_added_commit_message) { 'commit over api' }
+
+ it 'pushes to gitaly', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347677' do
+ project = Resource::Project.fabricate! do |project|
+ project.name = "mTLS"
+ project.initialize_with_readme = true
+ end
+
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.new_branch = false
+ push.commit_message = first_added_commit_message
+ push.file_content = 'First commit'
+ end
+
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = second_added_commit_message
+ commit.add_files(
+ [{
+ file_path: "file-#{SecureRandom.hex(8)}",
+ content: 'Second commit'
+ }]
+ )
+ end
+
+ expect(project.commits.map { |commit| commit[:message].chomp })
+ .to include(intial_commit_message)
+ .and include(first_added_commit_message)
+ .and include(second_added_commit_message)
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/12_systems/gitaly/praefect_connectivity_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/praefect_connectivity_spec.rb
new file mode 100644
index 00000000000..f25b50f584d
--- /dev/null
+++ b/qa/qa/specs/features/api/12_systems/gitaly/praefect_connectivity_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Systems' do
+ describe 'Praefect connectivity commands', :orchestrated, :gitaly_cluster do
+ praefect_manager = Service::PraefectManager.new
+
+ before do
+ praefect_manager.start_all_nodes
+ end
+
+ context 'in a healthy environment' do
+ it 'confirms healthy connection to database',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349937' do
+ expect(praefect_manager.praefect_sql_ping_healthy?).to be true
+ end
+
+ it 'confirms healthy connection to gitaly nodes',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349938' do
+ expect(praefect_manager.wait_for_dial_nodes_successful).to be true
+ end
+ end
+
+ context 'in an unhealthy environment' do
+ it 'diagnoses unhealthy connection to database',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349939' do
+ praefect_manager.stop_node(praefect_manager.postgres)
+ expect(praefect_manager.praefect_sql_ping_healthy?).to be false
+ end
+
+ it 'diagnoses connection issues to gitaly nodes',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349940' do
+ praefect_manager.stop_node(praefect_manager.primary_node)
+ praefect_manager.stop_node(praefect_manager.tertiary_node)
+ expect(praefect_manager.praefect_dial_nodes_status?(praefect_manager.primary_node, false)).to be true
+ expect(praefect_manager.praefect_dial_nodes_status?(praefect_manager.secondary_node)).to be true
+ expect(praefect_manager.praefect_dial_nodes_status?(praefect_manager.tertiary_node, false)).to be true
+
+ praefect_manager.stop_node(praefect_manager.secondary_node)
+ expect(praefect_manager.praefect_dial_nodes_status?(praefect_manager.secondary_node, false)).to be true
+ end
+ end
+ end
+ end
+end
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
new file mode 100644
index 00000000000..944c58ebc83
--- /dev/null
+++ b/qa/qa/specs/features/api/12_systems/gitaly/praefect_dataloss_spec.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Systems' do
+ describe 'Praefect dataloss commands', :orchestrated, :gitaly_cluster do
+ let(:praefect_manager) { Service::PraefectManager.new }
+
+ let(:project) do
+ Resource::Project.fabricate! do |project|
+ project.name = 'gitaly_cluster-dataloss-project'
+ project.initialize_with_readme = true
+ end
+ end
+
+ before do
+ praefect_manager.start_all_nodes
+ end
+
+ it 'confirms that changes are synced across all storages',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352691' do
+ expect { praefect_manager.praefect_dataloss_information(project.id) }
+ .to(eventually_include('All repositories are fully available on all assigned storages!')
+ .within(max_duration: 60))
+ end
+
+ it 'identifies how many changes are not in sync across storages',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352692' do
+ # Ensure our test repository is replicated and in a consistent state prior to test
+ praefect_manager.wait_for_project_synced_across_all_storages(project.id)
+
+ # testing for gitaly2 'out of sync'
+ praefect_manager.stop_secondary_node
+
+ number_of_changes = 3
+ 1.upto(number_of_changes) do |i|
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.branch = "newbranch-#{SecureRandom.hex(8)}"
+ commit.start_branch = project.default_branch
+ commit.commit_message = 'Add new file'
+ commit.add_files(
+ [{
+ file_path: "new_file-#{SecureRandom.hex(8)}.txt", content: 'new file'
+ }]
+ )
+ end
+ end
+
+ # testing for gitaly3 'in sync' but marked unhealthy
+ praefect_manager.stop_tertiary_node
+
+ project_data_loss = praefect_manager.praefect_dataloss_information(project.id)
+ aggregate_failures "validate dataloss identified" do
+ expect(project_data_loss).to include('gitaly1, assigned host')
+ expect(project_data_loss)
+ .to include("gitaly2 is behind by #{number_of_changes} changes or less, assigned host, unhealthy")
+ expect(project_data_loss).to include('gitaly3, assigned host, unhealthy')
+ end
+ end
+
+ it 'allows admin resolve scenario where data cannot be recovered',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352708' do
+ # Ensure everything is in sync before begining test
+ praefect_manager.wait_for_project_synced_across_all_storages(project.id)
+
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'accept-dataloss-1'
+ commit.add_files(
+ [{
+ file_path: "new_file-#{SecureRandom.hex(8)}.txt", content: 'Add a commit to gitaly1,gitaly2,gitaly3'
+ }]
+ )
+ end
+
+ praefect_manager.wait_for_replication_to_node(project.id, praefect_manager.primary_node)
+ praefect_manager.stop_primary_node
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'accept-dataloss-2'
+ commit.add_files(
+ [{
+ file_path: "new_file-#{SecureRandom.hex(8)}.txt", content: 'Add a commit to gitaly2,gitaly3'
+ }]
+ )
+ end
+
+ praefect_manager.wait_for_replication_to_node(project.id, praefect_manager.secondary_node)
+ praefect_manager.stop_secondary_node
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'accept-dataloss-3'
+ commit.add_files([
+ { file_path: "new_file-#{SecureRandom.hex(8)}.txt", content: 'Add a commit to gitaly3' }
+ ])
+ end
+
+ # Confirms that they want to accept dataloss, using gitaly2 as authoritative storage to use as a base
+ praefect_manager.accept_dataloss_for_project(project.id, praefect_manager.secondary_node)
+
+ # Restart nodes, and allow replication to apply dataloss changes
+ praefect_manager.start_all_nodes
+ praefect_manager.wait_for_project_synced_across_all_storages(project.id)
+
+ # Validate that gitaly2 was accepted as the authorative storage
+ aggregate_failures "validate correct set of commits available" do
+ expect(project.commits.map { |commit| commit[:message].chomp }).to include('accept-dataloss-1')
+ expect(project.commits.map { |commit| commit[:message].chomp }).to include('accept-dataloss-2')
+ expect(project.commits.map { |commit| commit[:message].chomp }).not_to include('accept-dataloss-3')
+ end
+ end
+ end
+ end
+end
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
new file mode 100644
index 00000000000..f4efcf74956
--- /dev/null
+++ b/qa/qa/specs/features/api/12_systems/gitaly/praefect_replication_queue_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'parallel'
+
+module QA
+ RSpec.describe 'Systems' do
+ describe 'Gitaly Cluster replication queue', :orchestrated, :gitaly_cluster, :skip_live_env do
+ let(:praefect_manager) { Service::PraefectManager.new }
+ let(:project) do
+ Resource::Project.fabricate! do |project|
+ project.name = "gitaly_cluster"
+ project.initialize_with_readme = true
+ end
+ end
+
+ before do
+ praefect_manager.start_all_nodes
+ end
+
+ after do
+ praefect_manager.start_all_nodes
+ praefect_manager.clear_replication_queue
+ end
+
+ it 'allows replication of different repository after interruption',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347829' do
+ # We want to fill the replication queue with 10 `in_progress` jobs,
+ # while a lock has been acquired, which is when the problem occurred
+ # as reported in https://gitlab.com/gitlab-org/gitaly/-/issues/2801
+ #
+ # We'll do this by creating 10 branches and pushing them all at once,
+ # and then stop Praefect when a lock is acquired, set all the jobs
+ # to `in_progress`, and create a job lock for each one.
+ queue_size_target = 10
+
+ # During normal operations we avoid create a replication event
+ # https://gitlab.com/groups/gitlab-org/-/epics/7741
+ praefect_manager.stop_secondary_node
+ Git::Repository.perform do |repository|
+ repository.uri = project.repository_http_location.uri
+ repository.use_default_credentials
+ repository.clone
+ repository.configure_identity('GitLab QA', 'root@gitlab.com')
+ 1.upto(queue_size_target) do |i|
+ repository.checkout("branch#{i}", new_branch: true)
+ repository.commit_file("file#{i}", SecureRandom.random_bytes(10000000), "Add file#{i}")
+ end
+ repository.push_all_branches
+ end
+ praefect_manager.start_secondary_node
+
+ Support::Retrier.retry_until(max_duration: 60) do
+ count = praefect_manager.replication_queue_lock_count
+ QA::Runtime::Logger.debug("Lock count: #{count}")
+ count >= 1
+ end
+
+ praefect_manager.stop_praefect
+ praefect_manager.create_stalled_replication_queue
+
+ praefect_manager.start_praefect
+
+ # Create a new project, and check that replication occurs
+ new_project = Resource::Project.fabricate! do |project|
+ project.initialize_with_readme = true
+ end
+
+ expect(praefect_manager.replicated?(new_project.id, new_project.name)).to be true
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/12_systems/gitaly/praefect_repo_sync_spec.rb b/qa/qa/specs/features/api/12_systems/gitaly/praefect_repo_sync_spec.rb
new file mode 100644
index 00000000000..064743ae469
--- /dev/null
+++ b/qa/qa/specs/features/api/12_systems/gitaly/praefect_repo_sync_spec.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Systems' do
+ describe 'Praefect repository commands', :orchestrated, :gitaly_cluster do
+ let(:praefect_manager) { Service::PraefectManager.new }
+
+ let(:repo1) do
+ { "relative_path" => "@hashed/repo1.git", "storage" => "gitaly1", "virtual_storage" => "default" }
+ end
+
+ let(:repo2) do
+ { "relative_path" => "@hashed/path/to/repo2.git", "storage" => "gitaly3", "virtual_storage" => "default" }
+ end
+
+ before do
+ praefect_manager.start_all_nodes
+ praefect_manager.add_repo_to_disk(praefect_manager.primary_node, repo1["relative_path"])
+ praefect_manager.add_repo_to_disk(praefect_manager.tertiary_node, repo2["relative_path"])
+ end
+
+ after do
+ praefect_manager.remove_repo_from_disk(repo1["relative_path"])
+ praefect_manager.remove_repo_from_disk(repo2["relative_path"])
+ praefect_manager.remove_repository_from_praefect_database(repo1["relative_path"])
+ praefect_manager.remove_repository_from_praefect_database(repo2["relative_path"])
+ end
+
+ it 'allows admin to manage difference between praefect database and disk state',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347606' do
+ # Some repos are on disk that praefect is not aware of
+ untracked_repositories = praefect_manager.list_untracked_repositories
+ expect(untracked_repositories).to include(repo1)
+ expect(untracked_repositories).to include(repo2)
+
+ # admin manually adds the first repo to the praefect database
+ praefect_manager
+ .track_repository_in_praefect(repo1["relative_path"], repo1["storage"], repo1["virtual_storage"])
+ untracked_repositories = praefect_manager.list_untracked_repositories
+ expect(untracked_repositories).not_to include(repo1)
+ expect(untracked_repositories).to include(repo2)
+ expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager.primary_node, repo1["relative_path"]))
+ .to be true
+ expect(praefect_manager.praefect_database_tracks_repo?(repo1["relative_path"])).to be true
+
+ # admin manually adds the second repo to the praefect database
+ praefect_manager
+ .track_repository_in_praefect(repo2["relative_path"], repo2["storage"], repo2["virtual_storage"])
+ untracked_repositories = praefect_manager.list_untracked_repositories
+ expect(untracked_repositories).not_to include(repo2)
+ expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager.tertiary_node, repo2["relative_path"]))
+ .to be true
+ expect(praefect_manager.praefect_database_tracks_repo?(repo2["relative_path"])).to be true
+
+ # admin ensures replication to other nodes occurs
+ expect(praefect_manager.repository_replicated_to_disk?(praefect_manager.secondary_node, repo1["relative_path"]))
+ .to be true
+ expect(praefect_manager.repository_replicated_to_disk?(praefect_manager.tertiary_node, repo1["relative_path"]))
+ .to be true
+ expect(praefect_manager.repository_replicated_to_disk?(praefect_manager.primary_node, repo2["relative_path"]))
+ .to be true
+ expect(praefect_manager.repository_replicated_to_disk?(praefect_manager.secondary_node, repo2["relative_path"]))
+ .to be true
+
+ # admin chooses to remove the first repo completely from praefect and disk
+ praefect_manager.remove_tracked_praefect_repository(repo1["relative_path"], repo1["virtual_storage"])
+ expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager.primary_node, repo1["relative_path"]))
+ .to be false
+ expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager
+ .secondary_node, repo1["relative_path"])).to be false
+ expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager.tertiary_node, repo1["relative_path"]))
+ .to be false
+ expect(praefect_manager.praefect_database_tracks_repo?(repo1["relative_path"])).to be false
+
+ untracked_repositories = praefect_manager.list_untracked_repositories
+ expect(untracked_repositories).not_to include(repo1)
+ end
+
+ it 'allows admin to control the number of replicas of data',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347566' do
+ praefect_manager
+ .track_repository_in_praefect(repo1['relative_path'], repo1['storage'], repo1['virtual_storage'])
+
+ praefect_manager.set_replication_factor(repo1['relative_path'], repo1['virtual_storage'], 2)
+ replication_storages = praefect_manager
+ .get_replication_storages(repo1['relative_path'], repo1['virtual_storage'])
+ expect(replication_storages).to have_attributes(size: 2)
+
+ praefect_manager.set_replication_factor(repo1['relative_path'], repo1['virtual_storage'], 3)
+ replication_storages = praefect_manager
+ .get_replication_storages(repo1['relative_path'], repo1['virtual_storage'])
+ expect(replication_storages).to eq(%w[gitaly1 gitaly2 gitaly3])
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb b/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb
index de460a39ccf..e6b60a5b090 100644
--- a/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb
@@ -88,14 +88,19 @@ module QA
let(:gh_issue_comments) do
logger.debug("= Fetching issue comments =")
github_client.issues_comments(github_repo).each_with_object(Hash.new { |h, k| h[k] = [] }) do |c, hash|
- hash[c.html_url.gsub(/\#\S+/, "")] << c.body&.gsub(gh_link_pattern, dummy_url) # use base html url as key
+ # use base html url as key
+ hash[c.html_url.gsub(/\#\S+/, "")] << c.body&.gsub(gh_link_pattern, dummy_url)
end
end
let(:gh_pr_comments) do
logger.debug("= Fetching pr comments =")
github_client.pull_requests_comments(github_repo).each_with_object(Hash.new { |h, k| h[k] = [] }) do |c, hash|
- hash[c.html_url.gsub(/\#\S+/, "")] << c.body&.gsub(gh_link_pattern, dummy_url) # use base html url as key
+ # use base html url as key
+ hash[c.html_url.gsub(/\#\S+/, "")] << c.body
+ # some suggestions can contain extra whitespaces which gitlab will remove
+ &.gsub(/suggestion\s+\r/, "suggestion\r")
+ &.gsub(gh_link_pattern, dummy_url)
end
end
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 444d86f63d3..9f0e2664213 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
@@ -71,7 +71,12 @@ 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'
+ 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'
+ }
) do
expect do
Resource::Repository::Commit.fabricate_via_api! do |commit|
diff --git a/qa/qa/specs/features/api/3_create/gitaly/automatic_failover_and_recovery_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/automatic_failover_and_recovery_spec.rb
deleted file mode 100644
index 55ae0d215cf..00000000000
--- a/qa/qa/specs/features/api/3_create/gitaly/automatic_failover_and_recovery_spec.rb
+++ /dev/null
@@ -1,93 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- RSpec.describe 'Create' do
- context 'Gitaly automatic failover and recovery', :orchestrated, :gitaly_cluster do
- # Variables shared between contexts. They're used and shared between
- # contexts so they can't be `let` variables.
- praefect_manager = Service::PraefectManager.new
- project = nil
-
- let(:intial_commit_message) { 'Initial commit' }
- let(:first_added_commit_message) { 'first_added_commit_message to primary gitaly node' }
- let(:second_added_commit_message) { 'second_added_commit_message to failover node' }
-
- before(:context) do
- praefect_manager.start_all_nodes
-
- project = Resource::Project.fabricate! do |project|
- project.name = "gitaly_cluster"
- project.initialize_with_readme = true
- end
- # We need to ensure that the the project is replicated to all nodes before proceeding with this test
- praefect_manager.wait_for_replication(project.id)
- end
-
- 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
-
- Resource::Repository::ProjectPush.fabricate! do |push|
- push.project = project
- push.commit_message = first_added_commit_message
- push.new_branch = false
- push.file_content = 'This file created on gitaly1 while gitaly2/gitaly3 not running'
- end
-
- praefect_manager.start_all_nodes
- praefect_manager.wait_for_replication(project.id)
-
- # 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
-
- Resource::Repository::Commit.fabricate_via_api! do |commit|
- commit.project = project
- commit.commit_message = second_added_commit_message
- commit.add_files([
- {
- file_path: "file-#{SecureRandom.hex(8)}",
- content: 'This is created on gitaly2/gitaly3 while gitaly1 is unavailable'
- }
- ])
- end
-
- # Confirm that we have access to the repo after failover,
- # including the commit we just added
- expect(project.commits.map { |commit| commit[:message].chomp })
- .to include(intial_commit_message)
- .and include(first_added_commit_message)
- .and include(second_added_commit_message)
- end
-
- 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
-
- # Confirm automatic reconciliation
- expect(praefect_manager.replicated?(project.id)).to be true
-
- # Confirm that all commits are available after reconciliation
- expect(project.commits.map { |commit| commit[:message].chomp })
- .to include(intial_commit_message)
- .and include(first_added_commit_message)
- .and include(second_added_commit_message)
-
- # Restore the original primary node
- praefect_manager.start_all_nodes
-
- # Check that all commits are still available even though the primary
- # node was offline when one was made
- expect(project.commits.map { |commit| commit[:message].chomp })
- .to include(intial_commit_message)
- .and include(first_added_commit_message)
- .and include(second_added_commit_message)
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/api/3_create/gitaly/backend_node_recovery_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/backend_node_recovery_spec.rb
deleted file mode 100644
index 25e860b4f6d..00000000000
--- a/qa/qa/specs/features/api/3_create/gitaly/backend_node_recovery_spec.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- RSpec.describe 'Create' do
- context 'Gitaly' do
- describe 'Backend node recovery', :orchestrated, :gitaly_cluster, :skip_live_env do
- let(:praefect_manager) { Service::PraefectManager.new }
- let(:project) do
- Resource::Project.fabricate! do |project|
- project.name = "gitaly_cluster"
- project.initialize_with_readme = true
- end
- end
-
- before do
- # Reset the cluster in case previous tests left it in a bad state
- praefect_manager.start_all_nodes
- end
-
- after do
- # Leave the cluster in a suitable state for subsequent tests
- praefect_manager.start_all_nodes
- end
-
- it 'recovers from dataloss', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347832' do
- # Create a new project with a commit and wait for it to replicate
- praefect_manager.wait_for_replication(project.id)
-
- # 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
-
- # Push a commit to the new primary
- Resource::Repository::ProjectPush.fabricate! do |push|
- push.project = project
- push.new_branch = false
- push.commit_message = 'pushed after failover'
- push.file_name = 'new_file'
- push.file_content = 'new file'
- end
-
- # Confirm that the commit is waiting to be replicated
- expect(praefect_manager).to be_replication_pending
-
- # Start the old primary node again
- praefect_manager.start_primary_node
- praefect_manager.wait_for_health_check_all_nodes
-
- # Wait for automatic replication
- praefect_manager.wait_for_replication(project.id)
-
- # Force switch to the old primary node
- # This ensures that the commit was replicated
- praefect_manager.stop_secondary_node
- praefect_manager.stop_tertiary_node
-
- # Confirm that both commits are available
- expect(project.commits.map { |commit| commit[:message].chomp })
- .to include("Initial commit").and include("pushed after failover")
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/api/3_create/gitaly/changing_repository_storage_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/changing_repository_storage_spec.rb
deleted file mode 100644
index 5ee6dfdb8d8..00000000000
--- a/qa/qa/specs/features/api/3_create/gitaly/changing_repository_storage_spec.rb
+++ /dev/null
@@ -1,94 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- RSpec.describe 'Create' do
- describe 'Changing Gitaly repository storage', :requires_admin, except: { job: 'review-qa-*' } do
- praefect_manager = Service::PraefectManager.new
-
- shared_examples 'repository storage move' do
- it 'confirms a `finished` status after moving project repository storage' do
- expect(project).to have_file('README.md')
- expect { project.change_repository_storage(destination_storage[:name]) }.not_to raise_error
- expect { praefect_manager.verify_storage_move(source_storage, destination_storage, repo_type: :project) }.not_to raise_error
-
- Support::Retrier.retry_on_exception(sleep_interval: 5) do
- # For a short period of time after migrating, the repository can be 'read only' which may lead to errors
- # 'The repository is temporarily read-only. Please try again later.'
- Resource::Repository::Commit.fabricate_via_api! do |commit|
- commit.project = project
- commit.commit_message = 'Add new file'
- commit.add_files([
- { file_path: 'new_file', content: '# This is a new file' }
- ])
- end
- end
-
- expect(project).to have_file('README.md')
- expect(project).to have_file('new_file')
- end
- end
-
- context 'when moving from one Gitaly storage to another', :orchestrated, :repository_storage, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347827' do
- let(:source_storage) { { type: :gitaly, name: 'default' } }
- let(:destination_storage) { { type: :gitaly, name: QA::Runtime::Env.additional_repository_storage } }
- let(:project) do
- Resource::Project.fabricate_via_api! do |project|
- project.name = 'repo-storage-move-status'
- project.initialize_with_readme = true
- project.api_client = Runtime::API::Client.as_admin
- end
- end
-
- before do
- praefect_manager.gitlab = 'gitlab'
- end
-
- it_behaves_like 'repository storage move'
- end
-
- # Note: This test doesn't have the :orchestrated tag because it runs in the Test::Integration::Praefect
- # scenario with other tests that aren't considered orchestrated.
- # It also runs on staging using nfs-file07 as non-cluster storage and nfs-file22 as cluster/praefect storage
- context 'when moving from Gitaly to Gitaly Cluster', :requires_praefect, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347828' do
- let(:source_storage) { { type: :gitaly, name: QA::Runtime::Env.non_cluster_repository_storage } }
- let(:destination_storage) { { type: :praefect, name: QA::Runtime::Env.praefect_repository_storage } }
- let(:project) do
- Resource::Project.fabricate_via_api! do |project|
- project.name = 'repo-storage-move'
- project.initialize_with_readme = true
- project.repository_storage = source_storage[:name]
- project.api_client = Runtime::API::Client.as_admin
- end
- end
-
- before do
- praefect_manager.gitlab = 'gitlab-gitaly-cluster'
- end
-
- it_behaves_like 'repository storage move'
- end
-
- # Note: This test doesn't have the :orchestrated tag because it runs in the Test::Integration::Praefect
- # scenario with other tests that aren't considered orchestrated.
- # It also runs on staging using nfs-file07 as non-cluster storage and nfs-file22 as cluster/praefect storage
- context 'when moving from Gitaly Cluster to Gitaly', :requires_praefect, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/369204' do
- let(:source_storage) { { type: :praefect, name: QA::Runtime::Env.praefect_repository_storage } }
- let(:destination_storage) { { type: :gitaly, name: QA::Runtime::Env.non_cluster_repository_storage } }
- let(:project) do
- Resource::Project.fabricate_via_api! do |project|
- project.name = 'repo-storage-move'
- project.initialize_with_readme = true
- project.repository_storage = source_storage[:name]
- project.api_client = Runtime::API::Client.as_admin
- end
- end
-
- before do
- praefect_manager.gitlab = 'gitlab-gitaly-cluster'
- end
-
- it_behaves_like 'repository storage move'
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb
deleted file mode 100644
index 2b96c35415e..00000000000
--- a/qa/qa/specs/features/api/3_create/gitaly/distributed_reads_spec.rb
+++ /dev/null
@@ -1,92 +0,0 @@
-# frozen_string_literal: true
-
-require 'parallel'
-
-module QA
- RSpec.describe 'Create' do
- context 'Gitaly' do
- describe 'Distributed reads', :orchestrated, :gitaly_cluster, :skip_live_env, :requires_admin do
- let(:number_of_reads_per_loop) { 9 }
- let(:praefect_manager) { Service::PraefectManager.new }
- let(:project) do
- Resource::Project.fabricate! do |project|
- project.name = "gitaly_cluster"
- project.initialize_with_readme = true
- end
- end
-
- before do
- praefect_manager.start_all_nodes
- praefect_manager.wait_for_replication(project.id)
- end
-
- it 'reads from each node', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347833' do
- pre_read_data = praefect_manager.query_read_distribution
-
- wait_for_reads_to_increase(project, number_of_reads_per_loop, pre_read_data)
-
- aggregate_failures "each gitaly node" do
- praefect_manager.query_read_distribution.each_with_index do |data, index|
- pre_read_count = praefect_manager.value_for_node(pre_read_data, data[:node])
- QA::Runtime::Logger.debug("Node: #{data[:node]}; before: #{pre_read_count}; now: #{data[:value]}")
- expect(data[:value]).to be > pre_read_count,
- "Read counts did not differ for node #{data[:node]}"
- end
- end
- end
-
- context 'when a node is unhealthy' do
- before do
- praefect_manager.stop_secondary_node
- end
-
- after do
- # Leave the cluster in a suitable state for subsequent tests
- praefect_manager.start_secondary_node
- end
-
- it 'does not read from the unhealthy node', 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)
-
- praefect_manager.wait_for_read_count_change(pre_read_data)
-
- post_read_data = praefect_manager.query_read_distribution
-
- aggregate_failures "each gitaly node" do
- expect(praefect_manager.value_for_node(post_read_data, 'gitaly1')).to be > praefect_manager.value_for_node(pre_read_data, 'gitaly1')
- expect(praefect_manager.value_for_node(post_read_data, 'gitaly2')).to eq praefect_manager.value_for_node(pre_read_data, 'gitaly2')
- expect(praefect_manager.value_for_node(post_read_data, 'gitaly3')).to be > praefect_manager.value_for_node(pre_read_data, 'gitaly3')
- end
- end
- end
-
- def read_from_project(project, number_of_reads)
- QA::Runtime::Logger.info('Reading from the repository')
- Parallel.each((1..number_of_reads)) do
- Git::Repository.perform do |repository|
- repository.uri = project.repository_http_location.uri
- repository.use_default_credentials
- repository.clone
- end
- end
- end
-
- def wait_for_reads_to_increase(project, number_of_reads, pre_read_data)
- diff_found = pre_read_data
-
- Support::Waiter.wait_until(sleep_interval: 5, raise_on_failure: false) do
- read_from_project(project, number_of_reads)
-
- praefect_manager.query_read_distribution.each_with_index do |data, index|
- diff_found[index] = {} unless diff_found[index]
- diff_found[index][:diff] = true if data[:value] > praefect_manager.value_for_node(pre_read_data, data[:node])
- end
- diff_found.all? { |node| node.key?(:diff) && node[:diff] }
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/api/3_create/gitaly/gitaly_mtls_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/gitaly_mtls_spec.rb
deleted file mode 100644
index 5000c273578..00000000000
--- a/qa/qa/specs/features/api/3_create/gitaly/gitaly_mtls_spec.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- RSpec.describe 'Create' do
- context 'Gitaly', :orchestrated, :mtls do
- describe 'Using mTLS' do
- let(:intial_commit_message) { 'Initial commit' }
- let(:first_added_commit_message) { 'commit over git' }
- let(:second_added_commit_message) { 'commit over api' }
-
- it 'pushes to gitaly', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347677' do
- project = Resource::Project.fabricate! do |project|
- project.name = "mTLS"
- project.initialize_with_readme = true
- end
-
- Resource::Repository::ProjectPush.fabricate! do |push|
- push.project = project
- push.new_branch = false
- push.commit_message = first_added_commit_message
- push.file_content = 'First commit'
- end
-
- Resource::Repository::Commit.fabricate_via_api! do |commit|
- commit.project = project
- commit.commit_message = second_added_commit_message
- commit.add_files([
- {
- file_path: "file-#{SecureRandom.hex(8)}",
- content: 'Second commit'
- }
- ])
- end
-
- expect(project.commits.map { |commit| commit[:message].chomp })
- .to include(intial_commit_message)
- .and include(first_added_commit_message)
- .and include(second_added_commit_message)
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/api/3_create/gitaly/praefect_connectivity_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/praefect_connectivity_spec.rb
deleted file mode 100644
index 28469b99d04..00000000000
--- a/qa/qa/specs/features/api/3_create/gitaly/praefect_connectivity_spec.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- RSpec.describe 'Create' do
- context 'Praefect connectivity commands', :orchestrated, :gitaly_cluster do
- praefect_manager = Service::PraefectManager.new
-
- before do
- praefect_manager.start_all_nodes
- end
-
- context 'in a healthy environment' do
- it 'confirms healthy connection to database', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349937' do
- expect(praefect_manager.praefect_sql_ping_healthy?).to be true
- end
-
- it 'confirms healthy connection to gitaly nodes', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349938' do
- expect(praefect_manager.wait_for_dial_nodes_successful).to be true
- end
- end
-
- context 'in an unhealthy environment' do
- it 'diagnoses unhealthy connection to database', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349939' do
- praefect_manager.stop_node(praefect_manager.postgres)
- expect(praefect_manager.praefect_sql_ping_healthy?).to be false
- end
-
- it 'diagnoses connection issues to gitaly nodes', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/349940' do
- praefect_manager.stop_node(praefect_manager.primary_node)
- praefect_manager.stop_node(praefect_manager.tertiary_node)
- expect(praefect_manager.praefect_dial_nodes_status?(praefect_manager.primary_node, false)).to be true
- expect(praefect_manager.praefect_dial_nodes_status?(praefect_manager.secondary_node)).to be true
- expect(praefect_manager.praefect_dial_nodes_status?(praefect_manager.tertiary_node, false)).to be true
-
- praefect_manager.stop_node(praefect_manager.secondary_node)
- expect(praefect_manager.praefect_dial_nodes_status?(praefect_manager.secondary_node, false)).to be true
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/api/3_create/gitaly/praefect_dataloss_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/praefect_dataloss_spec.rb
deleted file mode 100644
index 5b02cc4646c..00000000000
--- a/qa/qa/specs/features/api/3_create/gitaly/praefect_dataloss_spec.rb
+++ /dev/null
@@ -1,104 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- RSpec.describe 'Create' do
- context 'Praefect dataloss commands', :orchestrated, :gitaly_cluster do
- let(:praefect_manager) { Service::PraefectManager.new }
-
- let(:project) do
- Resource::Project.fabricate! do |project|
- project.name = 'gitaly_cluster-dataloss-project'
- project.initialize_with_readme = true
- end
- end
-
- before do
- praefect_manager.start_all_nodes
- end
-
- it 'confirms that changes are synced across all storages', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352691' do
- expect { praefect_manager.praefect_dataloss_information(project.id) }
- .to(eventually_include('All repositories are fully available on all assigned storages!')
- .within(max_duration: 60))
- end
-
- it 'identifies how many changes are not in sync across storages', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352692' do
- # Ensure our test repository is replicated and in a consistent state prior to test
- praefect_manager.wait_for_project_synced_across_all_storages(project.id)
-
- # testing for gitaly2 'out of sync'
- praefect_manager.stop_secondary_node
-
- number_of_changes = 3
- 1.upto(number_of_changes) do |i|
- Resource::Repository::Commit.fabricate_via_api! do |commit|
- commit.project = project
- commit.branch = "newbranch-#{SecureRandom.hex(8)}"
- commit.start_branch = project.default_branch
- commit.commit_message = 'Add new file'
- commit.add_files([
- { file_path: "new_file-#{SecureRandom.hex(8)}.txt", content: 'new file' }
- ])
- end
- end
-
- # testing for gitaly3 'in sync' but marked unhealthy
- praefect_manager.stop_tertiary_node
-
- project_data_loss = praefect_manager.praefect_dataloss_information(project.id)
- aggregate_failures "validate dataloss identified" do
- expect(project_data_loss).to include('gitaly1, assigned host')
- expect(project_data_loss).to include("gitaly2 is behind by #{number_of_changes} changes or less, assigned host, unhealthy")
- expect(project_data_loss).to include('gitaly3, assigned host, unhealthy')
- end
- end
-
- it 'allows admin resolve scenario where data cannot be recovered', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/352708' do
- # Ensure everything is in sync before begining test
- praefect_manager.wait_for_project_synced_across_all_storages(project.id)
-
- Resource::Repository::Commit.fabricate_via_api! do |commit|
- commit.project = project
- commit.commit_message = 'accept-dataloss-1'
- commit.add_files([
- { file_path: "new_file-#{SecureRandom.hex(8)}.txt", content: 'Add a commit to gitaly1,gitaly2,gitaly3' }
- ])
- end
-
- praefect_manager.wait_for_replication_to_node(project.id, praefect_manager.primary_node)
- praefect_manager.stop_primary_node
- Resource::Repository::Commit.fabricate_via_api! do |commit|
- commit.project = project
- commit.commit_message = 'accept-dataloss-2'
- commit.add_files([
- { file_path: "new_file-#{SecureRandom.hex(8)}.txt", content: 'Add a commit to gitaly2,gitaly3' }
- ])
- end
-
- praefect_manager.wait_for_replication_to_node(project.id, praefect_manager.secondary_node)
- praefect_manager.stop_secondary_node
- Resource::Repository::Commit.fabricate_via_api! do |commit|
- commit.project = project
- commit.commit_message = 'accept-dataloss-3'
- commit.add_files([
- { file_path: "new_file-#{SecureRandom.hex(8)}.txt", content: 'Add a commit to gitaly3' }
- ])
- end
-
- # Confirms that they want to accept dataloss, using gitaly2 as authoritative storage to use as a base
- praefect_manager.accept_dataloss_for_project(project.id, praefect_manager.secondary_node)
-
- # Restart nodes, and allow replication to apply dataloss changes
- praefect_manager.start_all_nodes
- praefect_manager.wait_for_project_synced_across_all_storages(project.id)
-
- # Validate that gitaly2 was accepted as the authorative storage
- aggregate_failures "validate correct set of commits available" do
- expect(project.commits.map { |commit| commit[:message].chomp }).to include('accept-dataloss-1')
- expect(project.commits.map { |commit| commit[:message].chomp }).to include('accept-dataloss-2')
- expect(project.commits.map { |commit| commit[:message].chomp }).not_to include('accept-dataloss-3')
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/api/3_create/gitaly/praefect_replication_queue_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/praefect_replication_queue_spec.rb
deleted file mode 100644
index a53614960cd..00000000000
--- a/qa/qa/specs/features/api/3_create/gitaly/praefect_replication_queue_spec.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-# frozen_string_literal: true
-
-require 'parallel'
-
-module QA
- RSpec.describe 'Create' do
- context 'Gitaly Cluster replication queue', :orchestrated, :gitaly_cluster, :skip_live_env do
- let(:praefect_manager) { Service::PraefectManager.new }
- let(:project) do
- Resource::Project.fabricate! do |project|
- project.name = "gitaly_cluster"
- project.initialize_with_readme = true
- end
- end
-
- before do
- praefect_manager.start_all_nodes
- end
-
- after do
- praefect_manager.start_all_nodes
- praefect_manager.clear_replication_queue
- end
-
- it 'allows replication of different repository after interruption', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347829' do
- # We want to fill the replication queue with 10 `in_progress` jobs,
- # while a lock has been acquired, which is when the problem occurred
- # as reported in https://gitlab.com/gitlab-org/gitaly/-/issues/2801
- #
- # We'll do this by creating 10 branches and pushing them all at once,
- # and then stop Praefect when a lock is acquired, set all the jobs
- # to `in_progress`, and create a job lock for each one.
- queue_size_target = 10
-
- # During normal operations we avoid create a replication event
- # https://gitlab.com/groups/gitlab-org/-/epics/7741
- praefect_manager.stop_secondary_node
- Git::Repository.perform do |repository|
- repository.uri = project.repository_http_location.uri
- repository.use_default_credentials
- repository.clone
- repository.configure_identity('GitLab QA', 'root@gitlab.com')
- 1.upto(queue_size_target) do |i|
- repository.checkout("branch#{i}", new_branch: true)
- repository.commit_file("file#{i}", SecureRandom.random_bytes(10000000), "Add file#{i}")
- end
- repository.push_all_branches
- end
- praefect_manager.start_secondary_node
-
- Support::Retrier.retry_until(max_duration: 60) do
- count = praefect_manager.replication_queue_lock_count
- QA::Runtime::Logger.debug("Lock count: #{count}")
- count >= 1
- end
-
- praefect_manager.stop_praefect
- praefect_manager.create_stalled_replication_queue
-
- praefect_manager.start_praefect
-
- # Create a new project, and check that replication occurs
- new_project = Resource::Project.fabricate! do |project|
- project.initialize_with_readme = true
- end
-
- expect(praefect_manager.replicated?(new_project.id, new_project.name)).to be true
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/api/3_create/gitaly/praefect_repo_sync_spec.rb b/qa/qa/specs/features/api/3_create/gitaly/praefect_repo_sync_spec.rb
deleted file mode 100644
index 47be7e0620b..00000000000
--- a/qa/qa/specs/features/api/3_create/gitaly/praefect_repo_sync_spec.rb
+++ /dev/null
@@ -1,75 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- RSpec.describe 'Create' do
- context 'Praefect repository commands', :orchestrated, :gitaly_cluster do
- let(:praefect_manager) { Service::PraefectManager.new }
-
- let(:repo1) { { "relative_path" => "@hashed/repo1.git", "storage" => "gitaly1", "virtual_storage" => "default" } }
- let(:repo2) { { "relative_path" => "@hashed/path/to/repo2.git", "storage" => "gitaly3", "virtual_storage" => "default" } }
-
- before do
- praefect_manager.start_all_nodes
- praefect_manager.add_repo_to_disk(praefect_manager.primary_node, repo1["relative_path"])
- praefect_manager.add_repo_to_disk(praefect_manager.tertiary_node, repo2["relative_path"])
- end
-
- after do
- praefect_manager.remove_repo_from_disk(repo1["relative_path"])
- praefect_manager.remove_repo_from_disk(repo2["relative_path"])
- praefect_manager.remove_repository_from_praefect_database(repo1["relative_path"])
- praefect_manager.remove_repository_from_praefect_database(repo2["relative_path"])
- end
-
- it 'allows admin to manage difference between praefect database and disk state', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347606' do
- # Some repos are on disk that praefect is not aware of
- untracked_repositories = praefect_manager.list_untracked_repositories
- expect(untracked_repositories).to include(repo1)
- expect(untracked_repositories).to include(repo2)
-
- # admin manually adds the first repo to the praefect database
- praefect_manager.track_repository_in_praefect(repo1["relative_path"], repo1["storage"], repo1["virtual_storage"])
- untracked_repositories = praefect_manager.list_untracked_repositories
- expect(untracked_repositories).not_to include(repo1)
- expect(untracked_repositories).to include(repo2)
- expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager.primary_node, repo1["relative_path"])).to be true
- expect(praefect_manager.praefect_database_tracks_repo?(repo1["relative_path"])).to be true
-
- # admin manually adds the second repo to the praefect database
- praefect_manager.track_repository_in_praefect(repo2["relative_path"], repo2["storage"], repo2["virtual_storage"])
- untracked_repositories = praefect_manager.list_untracked_repositories
- expect(untracked_repositories).not_to include(repo2)
- expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager.tertiary_node, repo2["relative_path"])).to be true
- expect(praefect_manager.praefect_database_tracks_repo?(repo2["relative_path"])).to be true
-
- # admin ensures replication to other nodes occurs
- expect(praefect_manager.repository_replicated_to_disk?(praefect_manager.secondary_node, repo1["relative_path"])).to be true
- expect(praefect_manager.repository_replicated_to_disk?(praefect_manager.tertiary_node, repo1["relative_path"])).to be true
- expect(praefect_manager.repository_replicated_to_disk?(praefect_manager.primary_node, repo2["relative_path"])).to be true
- expect(praefect_manager.repository_replicated_to_disk?(praefect_manager.secondary_node, repo2["relative_path"])).to be true
-
- # admin chooses to remove the first repo completely from praefect and disk
- praefect_manager.remove_tracked_praefect_repository(repo1["relative_path"], repo1["virtual_storage"])
- expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager.primary_node, repo1["relative_path"])).to be false
- expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager.secondary_node, repo1["relative_path"])).to be false
- expect(praefect_manager.repository_exists_on_node_disk?(praefect_manager.tertiary_node, repo1["relative_path"])).to be false
- expect(praefect_manager.praefect_database_tracks_repo?(repo1["relative_path"])).to be false
-
- untracked_repositories = praefect_manager.list_untracked_repositories
- expect(untracked_repositories).not_to include(repo1)
- end
-
- it 'allows admin to control the number of replicas of data', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347566' do
- praefect_manager.track_repository_in_praefect(repo1['relative_path'], repo1['storage'], repo1['virtual_storage'])
-
- praefect_manager.set_replication_factor(repo1['relative_path'], repo1['virtual_storage'], 2)
- replication_storages = praefect_manager.get_replication_storages(repo1['relative_path'], repo1['virtual_storage'])
- expect(replication_storages).to have_attributes(size: 2)
-
- praefect_manager.set_replication_factor(repo1['relative_path'], repo1['virtual_storage'], 3)
- replication_storages = praefect_manager.get_replication_storages(repo1['relative_path'], repo1['virtual_storage'])
- expect(replication_storages).to eq(%w(gitaly1 gitaly2 gitaly3))
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb b/qa/qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb
index c06912e0367..9d47872a774 100644
--- a/qa/qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/commit_to_templated_project_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Create a new project from a template' do
+ describe 'Create a new project from a template', product_group: :source_code do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'templated-project'
diff --git a/qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb b/qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb
index cba563ef85a..123a4a625ab 100644
--- a/qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/default_branch_name_setting_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Default branch name instance setting', :requires_admin, :skip_live_env do
+ describe 'Default branch name instance setting', :requires_admin, :skip_live_env, product_group: :source_code do
before(:context) do
Runtime::ApplicationSettings.set_application_settings(default_branch_name: 'main')
end
diff --git a/qa/qa/specs/features/api/3_create/repository/files_spec.rb b/qa/qa/specs/features/api/3_create/repository/files_spec.rb
index 151fd0fffe3..71bd03fab17 100644
--- a/qa/qa/specs/features/api/3_create/repository/files_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/files_spec.rb
@@ -4,7 +4,7 @@ require 'airborne'
module QA
RSpec.describe 'Create' do
- describe 'API basics' do
+ describe 'API basics', product_group: :source_code do
before(:context) do
@api_client = Runtime::API::Client.new(:gitlab)
end
diff --git a/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb b/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb
index 6f175272d91..a211eb6042d 100644
--- a/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/project_archive_compare_spec.rb
@@ -5,7 +5,8 @@ require 'digest'
module QA
RSpec.describe 'Create' do
- describe 'Compare archives of different user projects with the same name and check they\'re different' do
+ describe 'Compare archives of different user projects with the same name and check they\'re different',
+ product_group: :source_code do
include Support::API
let(:project_name) { "project-archive-download-#{SecureRandom.hex(8)}" }
@@ -52,12 +53,11 @@ module QA
project.api_client = api_client
end
- Resource::Repository::ProjectPush.fabricate! do |push|
- push.project = project
- push.file_name = 'README.md'
- push.file_content = '# This is a test project'
- push.commit_message = 'Add README.md'
- push.user = user
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.add_files([{ file_path: 'README.md', content: '# This is a test project' }])
+ commit.commit_message = 'Add README.md'
+ commit.api_client = api_client
end
project
@@ -65,7 +65,7 @@ module QA
def download_project_archive_via_api(api_client, project, type = 'tar.gz')
get_project_archive_zip = Runtime::API::Request.new(api_client, project.api_get_archive_path(type))
- project_archive_download = get(get_project_archive_zip.url, raw_response: true)
+ project_archive_download = Support::API.get(get_project_archive_zip.url, raw_response: true)
expect(project_archive_download.code).to eq(200)
project_archive_download.file
diff --git a/qa/qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb b/qa/qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb
index 1a2a1679cca..1d41184df98 100644
--- a/qa/qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/push_postreceive_idempotent_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'PostReceive idempotent' do
+ describe 'PostReceive idempotent', product_group: :source_code do
# Tests that a push does not result in multiple changes from repeated PostReceive executions.
# One of the consequences would be duplicate push events
diff --git a/qa/qa/specs/features/api/3_create/repository/storage_size_spec.rb b/qa/qa/specs/features/api/3_create/repository/storage_size_spec.rb
index 406ff191f95..df3b5a4e7fb 100644
--- a/qa/qa/specs/features/api/3_create/repository/storage_size_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/storage_size_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Repository Usage Quota', :skip_live_env, feature_flag: {
+ describe 'Repository Usage Quota', :skip_live_env, product_group: :source_code, feature_flag: {
name: 'gitaly_revlist_for_repo_size',
scope: :global
} do
diff --git a/qa/qa/specs/features/api/3_create/repository/tag_revision_trigger_prereceive_hook_spec.rb b/qa/qa/specs/features/api/3_create/repository/tag_revision_trigger_prereceive_hook_spec.rb
index 98612d84b21..b34b4208337 100644
--- a/qa/qa/specs/features/api/3_create/repository/tag_revision_trigger_prereceive_hook_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/tag_revision_trigger_prereceive_hook_spec.rb
@@ -2,29 +2,32 @@
module QA
RSpec.describe 'Create' do
- let(:project) do
- Resource::Project.fabricate_via_api! do |project|
- project.initialize_with_readme = true
+ describe 'Prereceive hook', product_group: :source_code do
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.initialize_with_readme = true
+ end
end
- end
- context 'when creating a tag for a ref' do
- context 'when it triggers a prereceive hook configured with a custom error' do
- before do
- # The configuration test prereceive hook must match a specific naming pattern
- # In this test we create a project with a different name and then change the path.
- # Otherwise we wouldn't be able create any commits to be tagged due to the hook.
- project.change_path("project-reject-prereceive-#{SecureRandom.hex(8)}")
- end
+ context 'when creating a tag for a ref' do
+ context 'when it triggers a prereceive hook configured with a custom error' do
+ before do
+ # The configuration test prereceive hook must match a specific naming pattern
+ # In this test we create a project with a different name and then change the path.
+ # Otherwise we wouldn't be able create any commits to be tagged due to the hook.
+ project.change_path("project-reject-prereceive-#{SecureRandom.hex(8)}")
+ end
- it 'returns a custom server hook error',
- :skip_live_env,
- except: { job: 'review-qa-*' },
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/369053' do
- expect { project.create_repository_tag('v1.2.3') }.to raise_error
- .with_message(
- /rejecting prereceive hook for projects with GL_PROJECT_PATH matching pattern reject-prereceive/
- )
+ it 'returns a custom server hook error',
+ :skip_live_env,
+ except: { job: 'review-qa-*' },
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/369053' do
+ expect { project.create_repository_tag('v1.2.3') }
+ .to raise_error
+ .with_message(
+ /rejecting prereceive hook for projects with GL_PROJECT_PATH matching pattern reject-prereceive/
+ )
+ end
end
end
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
new file mode 100644
index 00000000000..9722f62d5a7
--- /dev/null
+++ b/qa/qa/specs/features/api/4_verify/file_variable_spec.rb
@@ -0,0 +1,142 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Verify', :runner, feature_flag: {
+ name: 'ci_stop_expanding_file_vars_for_runners',
+ scope: :project
+ } do
+ describe 'Pipeline with project file variables' do
+ let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(number: 8)}" }
+
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-with-file-variables'
+ end
+ end
+
+ let(:runner) do
+ Resource::Runner.fabricate! do |runner|
+ runner.project = project
+ runner.name = executor
+ runner.tags = [executor]
+ end
+ end
+
+ let(:add_ci_file) do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.add_files(
+ [
+ {
+ file_path: '.gitlab-ci.yml',
+ content: <<~YAML
+ variables:
+ EXTRA_ARGS: "-f $TEST_FILE"
+ DOCKER_REMOTE_ARGS: --tlscacert="$DOCKER_CA_CERT"
+ EXTRACTED_CRT_FILE: ${DOCKER_CA_CERT}.crt
+ MY_FILE_VAR: $TEST_FILE
+
+ test:
+ tags: [#{executor}]
+ script:
+ - echo "run something $EXTRA_ARGS"
+ - echo "docker run $DOCKER_REMOTE_ARGS"
+ - echo "run --output=$EXTRACTED_CRT_FILE"
+ - echo "Will read private key from $MY_FILE_VAR"
+ YAML
+ }
+ ]
+ )
+ end
+ end
+
+ let(:add_file_variables) do
+ {
+ 'TEST_FILE' => 'hello, this is test',
+ 'DOCKER_CA_CERT' => 'This is secret'
+ }.each do |file_name, content|
+ add_file_variable_to_project(file_name, content)
+ end
+ end
+
+ after do
+ runner.remove_via_api!
+ end
+
+ shared_examples 'variables are read correctly' do
+ it 'shows in job log accordingly' do
+ job = Resource::Job.fabricate_via_api! do |job|
+ job.project = project
+ job.id = project.job_by_name('test')[:id]
+ end
+
+ aggregate_failures do
+ trace = job.trace
+ expect(trace).to have_content('run something -f hello, this is test')
+ expect(trace).to have_content('docker run --tlscacert="This is secret"')
+ expect(trace).to have_content('run --output=This is secret.crt')
+ expect(trace).to have_content('Will read private key from hello, this is test')
+ end
+ end
+ end
+
+ # FF does not change current behavior
+ # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/94198#note_1057609893
+ #
+ # TODO: Remove when FF is removed
+ # TODO: Archive testcase issue when FF is removed
+ # Rollout issue: https://gitlab.com/gitlab-org/gitlab/-/issues/369907
+ context 'when FF is on', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/370787' do
+ before do
+ Runtime::Feature.enable(:ci_stop_expanding_file_vars_for_runners, project: project)
+
+ runner
+ add_file_variables
+ add_ci_file
+ trigger_pipeline
+ wait_for_pipeline
+ end
+
+ it_behaves_like 'variables are read correctly'
+ end
+
+ # TODO: Refactor when FF is removed
+ # TODO: Update testcase issue title and description to not refer to FF status
+ context 'when FF is off', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/370791' do
+ before do
+ runner
+ add_file_variables
+ add_ci_file
+ trigger_pipeline
+ wait_for_pipeline
+ end
+
+ it_behaves_like 'variables are read correctly'
+ end
+
+ private
+
+ def add_file_variable_to_project(key, value)
+ Resource::CiVariable.fabricate_via_api! do |ci_variable|
+ ci_variable.project = project
+ ci_variable.key = key
+ ci_variable.value = value
+ ci_variable.variable_type = 'file'
+ end
+ end
+
+ def trigger_pipeline
+ Resource::Pipeline.fabricate_via_api! do |pipeline|
+ pipeline.project = project
+ end
+ end
+
+ def wait_for_pipeline
+ Support::Waiter.wait_until do
+ project.pipelines.present? && project.pipelines.first[:status] == 'success'
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb
index c5efa833f04..ad90df4b90d 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/log_in_with_2fa_spec.rb
@@ -36,7 +36,11 @@ module QA
group.add_member(developer_user, Resource::Members::AccessLevel::DEVELOPER)
end
- it 'allows enforcing 2FA via UI and logging in with 2FA', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347931' do
+ it(
+ 'allows enforcing 2FA via UI and logging in with 2FA',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347931',
+ quarantine: { type: :flaky, issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/369516' }
+ ) do
enforce_two_factor_authentication_on_group(group)
enable_two_factor_authentication_for_user(developer_user)
diff --git a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
index 8cc772ed022..295702aa328 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/login/register_spec.rb
@@ -66,7 +66,7 @@ module QA
end
# TODO: Remove retry_on_exception once https://gitlab.com/gitlab-org/gitlab/-/issues/24294 is resolved
- Support::Waiter.wait_until(retry_on_exception: true, sleep_interval: 3) { !user.exists? }
+ Support::Waiter.wait_until(max_duration: 120, retry_on_exception: true, sleep_interval: 3) { !user.exists? }
end
it 'allows recreating with same credentials', :reliable, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347868' do
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb
index b1d59b90e9c..d299997dd3c 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb
@@ -20,8 +20,10 @@ module QA
Flow::Login.sign_in(as: user)
Page::Dashboard::Welcome.perform do |welcome|
- expect(welcome).to have_welcome_title("Welcome to GitLab")
-
+ Support::Waiter.wait_until(sleep_interval: 2, max_duration: 60, reload_page: page,
+ retry_on_exception: true) do
+ expect(welcome).to have_welcome_title("Welcome to GitLab")
+ end
# This would be better if it were a visual validation test
expect(welcome).to have_loaded_all_images
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
index ed271533f87..7e4a391c390 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
@@ -64,7 +64,7 @@ module QA
it(
'comments on an issue with an attachment',
testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347946',
- except: { job: 'review-qa-smoke' }
+ except: { job: 'review-qa-*' }
) do
Page::Project::Issue::Show.perform do |show|
show.comment('See attached image for scale', attachment: file_to_attach)
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb
index d198d79c5fe..109cf7b73c3 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/merge_merge_request_from_fork_spec.rb
@@ -17,7 +17,11 @@ module QA
merge_request.fork.remove_via_api!
end
- it 'can merge feature branch fork to mainline', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347818' do
+ it 'can merge feature branch fork to mainline', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347818', quarantine: {
+ only: :production,
+ type: :investigating,
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/372258'
+ } do
merge_request.visit!
Page::MergeRequest::Show.perform do |merge_request|
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 2280cc971a7..c7296b6eea2 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
@@ -12,11 +12,9 @@ module QA
it 'user rebases source branch of merge request', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347735' do
merge_request.project.visit!
- Page::Project::Menu.perform(&:go_to_general_settings)
- Page::Project::Settings::Main.perform do |main|
- main.expand_merge_requests_settings do |settings|
- settings.enable_ff_only
- end
+ Page::Project::Menu.perform(&:go_to_merge_request_settings)
+ Page::Project::Settings::MergeRequest.perform do |settings|
+ settings.enable_ff_only
end
Resource::Repository::ProjectPush.fabricate! do |push|
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
new file mode 100644
index 00000000000..9c912080c5f
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/pages/new_static_page_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+module QA
+ RSpec.describe 'Create', :runner, only: { subdomain: :staging } do
+ # TODO: Convert back to :smoke once proved to be stable. Related issue: https://gitlab.com/gitlab-org/gitlab/-/issues/300906
+ describe 'Pages' do
+ let!(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'jekyll-pages-project'
+ project.template_name = :jekyll
+ end
+ end
+
+ let(:pipeline) do
+ Resource::Pipeline.fabricate_via_api! do |pipeline|
+ pipeline.project = project
+ pipeline.variables =
+ { key: :CI_PAGES_DOMAIN, value: 'nip.io', variable_type: :env_var },
+ { key: :CI_PAGES_URL, value: 'http://127.0.0.1.nip.io', variable_type: :env_var }
+ end
+ end
+
+ before do
+ Flow::Login.sign_in
+
+ Resource::Runner.fabricate_via_api! do |runner|
+ runner.project = project
+ runner.executor = :docker
+ end
+ pipeline.visit!
+ end
+
+ it 'creates a Pages website',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347669' do
+ Page::Project::Pipeline::Show.perform do |show|
+ expect(show).to have_job(:pages)
+ show.click_job(:pages)
+ end
+
+ Page::Project::Job::Show.perform do |show|
+ expect(show).to have_passed(timeout: 300)
+ end
+
+ Page::Project::Show.perform(&:go_to_pages_settings)
+ QA::Page::Project::Settings::Pages.perform do |pages|
+ 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.')
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/pages/pages_pipeline_spec.rb b/qa/qa/specs/features/browser_ui/3_create/pages/pages_pipeline_spec.rb
deleted file mode 100644
index 191c4a096e7..00000000000
--- a/qa/qa/specs/features/browser_ui/3_create/pages/pages_pipeline_spec.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- RSpec.describe 'Release', :runner do
- # TODO: Convert back to :smoke once proved to be stable. Related issue: https://gitlab.com/gitlab-org/gitlab/-/issues/300906
- describe 'Pages' do
- let!(:project) do
- Resource::Project.fabricate_via_api! do |project|
- project.name = 'jekyll-pages-project'
- project.template_name = :jekyll
- end
- end
-
- let(:pipeline) do
- Resource::Pipeline.fabricate_via_api! do |pipeline|
- pipeline.project = project
- pipeline.variables =
- { key: :CI_PAGES_DOMAIN, value: 'nip.io', variable_type: :env_var },
- { key: :CI_PAGES_URL, value: 'http://127.0.0.1.nip.io', variable_type: :env_var }
- end
- end
-
- before do
- Flow::Login.sign_in
-
- Resource::Runner.fabricate_via_api! do |runner|
- runner.project = project
- runner.executor = :docker
- end
-
- pipeline.visit!
- end
-
- it('runs a Pages-specific pipeline',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347669') do
- Page::Project::Pipeline::Show.perform do |show|
- expect(show).to have_job(:pages)
- show.click_job(:pages)
- end
-
- Page::Project::Job::Show.perform do |show|
- expect(show).to have_passed(timeout: 300)
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb
index 107d72a9724..b4103bd0976 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/add_file_template_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'File templates' do
+ describe 'File templates', product_group: :source_code do
include Runtime::Fixtures
let(:project) do
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb
index 0bd470fcb77..849022f5a93 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/add_list_delete_branches_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Create, list, and delete branches via web', :requires_admin do
+ describe 'Create, list, and delete branches via web', :requires_admin, product_group: :source_code do
master_branch = nil
second_branch = 'second-branch'
third_branch = 'third-branch'
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 9e77fd228da..aa332a76c94 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,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Branch with unusual name' do
+ describe 'Branch with unusual name', product_group: :source_code do
let(:branch_name) { 'unUsually/named#br--anch' }
let(:project) do
Resource::Project.fabricate_via_api! do |resource|
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb
index d12fb05af77..b7df22fc2c2 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/clone_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Git clone over HTTP' do
+ describe 'Git clone over HTTP', product_group: :source_code do
let(:project) do
Resource::Project.fabricate_via_api! do |scenario|
scenario.name = 'project-with-code'
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb
index 095444d99f1..d5f30c19738 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/file/create_file_via_web_spec.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', :reliable do
- context 'File management' do
+ RSpec.describe 'Create', :reliable, product_group: :source_code do
+ describe 'File management' do
file_name = 'QA Test - File name'
file_content = 'QA Test - File content'
commit_message_for_create = 'QA Test - Create new file'
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb
index 02ecff22840..76243066476 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/file/delete_file_via_web_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- context 'File management' do
+ describe 'File management', product_group: :source_code do
let(:file) { Resource::File.fabricate_via_api! }
commit_message_for_delete = 'QA Test - Delete file'
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb
index 95e7a2a12d0..397796b76e5 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/file/edit_file_via_web_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create', :reliable do
- context 'File management' do
+ describe 'File management', product_group: :source_code do
let(:file) { Resource::File.fabricate_via_api! }
updated_file_content = 'QA Test - Updated file content'
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/file/file_with_unusual_name_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/file/file_with_unusual_name_spec.rb
index eb6449181b5..d7da29219e6 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/file/file_with_unusual_name_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/file/file_with_unusual_name_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'File with unusual name' do
+ describe 'File with unusual name', product_group: :source_code do
let(:file_name) { '-un:usually;named#file?.md' }
let(:project) do
Resource::Project.fabricate_via_api! do |resource|
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/license_detecton_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/license_detecton_spec.rb
index 3db8128bc6d..1ae1dd87c07 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/license_detecton_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/license_detecton_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Repository License Detection' do
+ describe 'Repository License Detection', product_group: :source_code do
after do
project.remove_via_api!
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb
index 153bfd292aa..a391e6313a6 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/move_project_create_fork_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', :orchestrated, :repository_storage, :requires_admin do
+ RSpec.describe 'Create', :orchestrated, :repository_storage, :requires_admin, product_group: :source_code do
describe 'Gitaly repository storage' do
let(:user) { Resource::User.fabricate_or_use(Runtime::Env.gitlab_qa_username_1, Runtime::Env.gitlab_qa_password_1) }
let(:parent_project) do
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb
index fb87ca864f4..65a15ce96a5 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/protected_tags_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Repository tags', :reliable do
+ describe 'Repository tags', :reliable, product_group: :source_code do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'project-for-tags'
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb
index 34439042796..557a27c002d 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Push over HTTP using Git protocol version 2', :requires_git_protocol_v2 do
+ describe 'Push over HTTP using Git protocol version 2', :requires_git_protocol_v2, product_group: :source_code do
it 'user pushes to the repository', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347760' do
Flow::Login.sign_in
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb
index 25d4da95dd9..2472d1cdf25 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Push over SSH using Git protocol version 2', :requires_git_protocol_v2 do
+ describe 'Push over SSH using Git protocol version 2', :requires_git_protocol_v2, product_group: :source_code do
# Note: If you run this test against GDK make sure you've enabled sshd and
# enabled setting the Git protocol by adding `AcceptEnv GIT_PROTOCOL` to
# `sshd_config`
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb
index 042fee38188..a785e4ae764 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_http_private_token_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Git push over HTTP', :smoke do
+ describe 'Git push over HTTP', :smoke, product_group: :source_code do
it 'user using a personal access token pushes code to the repository', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347749' do
Flow::Login.sign_in
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb
index 9ab322df824..815a8696ff7 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_lfs_over_http_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', quarantine: {
+ RSpec.describe 'Create', product_group: :source_code, quarantine: {
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/352525',
type: :test_environment,
only: { job: 'review-qa-*' }
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb
index d644a7ead1e..b87c47761bd 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Push mirror a repository over HTTP' do
+ describe 'Push mirror a repository over HTTP', product_group: :source_code do
it 'configures and syncs a (push) mirrored repository', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347741' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.perform(&:sign_in_using_credentials)
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb
index 0e4aa67162f..324dbbc46ef 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb
@@ -3,7 +3,7 @@
module QA
# This test modifies an instance level setting,
# so skipping on live envs to avoid random transient issues
- RSpec.describe 'Create', :requires_admin, :skip_live_env do
+ RSpec.describe 'Create', :requires_admin, :skip_live_env, product_group: :source_code do
describe 'push after setting the file size limit via admin/application_settings' do
include Support::API
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb
index a782a50b55d..e8f7cb252a0 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Git push over HTTP' do
+ describe 'Git push over HTTP', product_group: :source_code do
it 'user pushes code to the repository', :smoke, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347747' do
Flow::Login.sign_in
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb
index 0323448878b..4fb52f1e54d 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_ssh_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'SSH key support' do
+ describe 'SSH key support', product_group: :source_code do
# Note: If you run these tests against GDK make sure you've enabled sshd
# See: https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/run_qa_against_gdk.md
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb
index 91b0940f137..e097e1fd2bb 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', :reliable do
+ RSpec.describe 'Create', :reliable, product_group: :source_code do
describe 'Protected branch support' do
let(:branch_name) { 'protected-branch' }
let(:commit_message) { 'Protected push commit message' }
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_to_canary_gitaly_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_to_canary_gitaly_spec.rb
index 78abdb94dfe..d95a880c305 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_to_canary_gitaly_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_to_canary_gitaly_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Create', only: { subdomain: %i[staging staging-canary] } do
+ RSpec.describe 'Create', only: { subdomain: %i[staging staging-canary] }, product_group: :source_code do
describe 'Git push to canary Gitaly node over HTTP' do
it 'pushes to a project using a canary specific Gitaly repository storage', :smoke, :requires_admin, testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/351116' do
Flow::Login.sign_in_as_admin
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb
index 55df1615f5c..64858287285 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/ssh_key_support_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'SSH keys support', :smoke do
+ describe 'SSH keys support', :smoke, product_group: :source_code do
let(:key_title) { "key for ssh tests #{Time.now.to_f}" }
key = nil
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb
index bbf6c3ca37a..13b42209114 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb
@@ -2,7 +2,7 @@
module QA
RSpec.describe 'Create' do
- describe 'Commit data' do
+ describe 'Commit data', product_group: :source_code do
before(:context) do
# Get the user's details to confirm they're included in the email patch
@user = Resource::User.fabricate_via_api! do |user|
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
new file mode 100644
index 00000000000..8352ad6aa33
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/prefill_variables_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Verify' do
+ describe 'Pipeline with prefill variables' do
+ let(:prefill_variable_description1) { Faker::Lorem.sentence }
+ let(:prefill_variable_value1) { Faker::Lorem.word }
+ let(:prefill_variable_description2) { Faker::Lorem.sentence }
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-with-prefill-variables'
+ end
+ end
+
+ let!(:commit) do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add .gitlab-ci.yml'
+ commit.add_files(
+ [
+ {
+ file_path: '.gitlab-ci.yml',
+ content: <<~YAML
+ variables:
+ TEST1:
+ value: #{prefill_variable_value1}
+ description: #{prefill_variable_description1}
+ TEST2:
+ description: #{prefill_variable_description2}
+ TEST3:
+ value: test 3 value
+ TEST4: test 4 value
+
+ test:
+ script: echo "$FOO"
+ YAML
+ }
+ ]
+ )
+ end
+ end
+
+ before do
+ Flow::Login.sign_in
+ project.visit!
+
+ # Navigate to Run Pipeline page
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
+ end
+
+ it(
+ 'shows only variables with description as prefill variables on the run pipeline page',
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/371204'
+ ) 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_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')
+ end
+ end
+ end
+ end
+ end
+end
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 412498476f0..f9113573295 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
@@ -1,12 +1,7 @@
# frozen_string_literal: true
module QA
- # TODO: remove feature flag upon rollout completion
- # FF rollout issue: https://gitlab.com/gitlab-org/gitlab/-/issues/363186
- RSpec.describe 'Verify', :runner, feature_flag: {
- name: 'ci_docker_image_pull_policy',
- scope: :global
- } do
+ RSpec.describe 'Verify', :runner do
describe 'Pipeline with image:pull_policy' do
let(:runner_name) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(number: 8)}" }
let(:job_name) { "test-job-#{pull_policies.join('-')}" }
@@ -27,10 +22,6 @@ module QA
end
before do
- Runtime::Feature.enable(:ci_docker_image_pull_policy)
- # Give the feature some time to switch
- sleep(30)
-
update_runner_policy(allowed_policies)
add_ci_file
Flow::Login.sign_in
@@ -39,12 +30,13 @@ module QA
end
after do
- Runtime::Feature.disable(:ci_docker_image_pull_policy)
-
runner.remove_via_api!
end
- context 'when policy is allowed' do
+ context(
+ 'when policy is allowed',
+ quarantine: { type: :flaky, issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/369397' }
+ ) do
let(:allowed_policies) { %w[if-not-present always never] }
where do
@@ -102,7 +94,8 @@ module QA
it(
'fails job with policy not allowed message',
- testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/368853'
+ testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/368853',
+ quarantine: { issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/371420', type: :stale }
) do
visit_job
diff --git a/qa/qa/specs/features/browser_ui/5_package/infrastructure_registry/terraform_module_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/infrastructure_registry/terraform_module_registry_spec.rb
new file mode 100644
index 00000000000..6ce395affc7
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/5_package/infrastructure_registry/terraform_module_registry_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+module QA
+ RSpec.describe 'Package', :orchestrated, :packages do
+ describe 'Terraform Module Registry' do
+ include Runtime::Fixtures
+
+ let(:group) { Resource::Group.fabricate_via_api! }
+
+ let(:imported_project) do
+ Resource::ProjectImportedFromURL.fabricate_via_browser_ui! do |project|
+ project.name = 'terraform-module-test'
+ project.group = group
+ project.gitlab_repository_path = 'https://gitlab.com/mattkasa/terraform-module-test.git'
+ end
+ end
+
+ let(:runner) do
+ Resource::Runner.fabricate! do |runner|
+ runner.name = "qa-runner-#{Time.now.to_i}"
+ runner.tags = ["runner-for-#{imported_project.name}"]
+ runner.executor = :docker
+ runner.project = imported_project
+ runner.token = group.reload!.runners_token
+ end
+ end
+
+ before do
+ Flow::Login.sign_in
+
+ imported_project
+ runner
+ end
+
+ after do
+ runner.remove_via_api!
+ end
+
+ it 'publishes a module', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/371583' do
+ Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ terraform_module_yaml = ERB.new(
+ read_fixture('package_managers/terraform', 'module_upload.yaml.erb')
+ ).result(binding)
+ commit.project = imported_project
+ commit.commit_message = 'Add gitlab-ci.yaml file'
+ commit.update_files([
+ {
+ file_path: '.gitlab-ci.yml',
+ content: terraform_module_yaml
+ }
+ ]
+ )
+ end
+ end
+
+ Resource::Tag.fabricate_via_api! do |tag|
+ tag.project = imported_project
+ tag.ref = imported_project.default_branch
+ tag.name = "1.0.0"
+ end
+
+ Flow::Pipeline.visit_latest_pipeline
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.click_job('upload')
+ end
+
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 800)
+ end
+
+ Page::Project::Menu.perform(&:go_to_infrastructure_registry)
+
+ Page::Project::Packages::Index.perform do |index|
+ expect(index).to have_module("#{imported_project.name}/local")
+ end
+ end
+ end
+ end
+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 180910e85a0..921b36b34af 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
@@ -142,7 +142,7 @@ module QA
Page::Group::Menu.perform(&:go_to_package_settings)
end
- context 'when enabled' do
+ context 'when disabled' do
where do
{
'using a personal access token' => {
@@ -176,7 +176,7 @@ module QA
end
before do
- Page::Group::Settings::PackageRegistries.perform(&:set_reject_duplicates_enabled)
+ Page::Group::Settings::PackageRegistries.perform(&:set_allow_duplicates_disabled)
end
it 'prevents users from publishing group level Maven packages duplicates', testcase: params[:testcase] do
@@ -195,7 +195,7 @@ module QA
end
end
- context 'when disabled' do
+ context 'when enabled' do
where do
{
'using a personal access token' => {
@@ -229,7 +229,7 @@ module QA
end
before do
- Page::Group::Settings::PackageRegistries.perform(&:set_reject_duplicates_disabled)
+ Page::Group::Settings::PackageRegistries.perform(&:set_allow_duplicates_enabled)
end
it 'allows users to publish group level Maven packages duplicates', testcase: params[:testcase] do
diff --git a/qa/qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb
index 22d76d684e5..012a03ca115 100644
--- a/qa/qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/package_registry/pypi_repository_spec.rb
@@ -105,10 +105,6 @@ module QA
QA::Runtime::Logger.debug('Visiting the secondary Geo site')
QA::Flow::Login.while_signed_in(address: :geo_secondary) do
- EE::Page::Main::Banner.perform do |banner|
- expect(banner).to have_secondary_read_only_banner
- end
-
Page::Main::Menu.perform(&:go_to_projects)
Page::Dashboard::Projects.perform do |dashboard|
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 f1a2eb71390..b839855c500 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,78 +1,67 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Configure',
- only: { subdomain: %i[staging staging-canary] },
- quarantine: {
- issue: 'https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/1198',
- type: :waiting_on
- } do
- let(:project) do
- Resource::Project.fabricate_via_api! do |project|
- project.name = 'autodevops-project'
- project.auto_devops_enabled = true
+ RSpec.describe 'Configure', only: { subdomain: %i[staging staging-canary] } do
+ describe 'Auto DevOps with a Kubernetes Agent' do
+ let!(:app_project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'autodevops-app-project'
+ project.template_name = 'express'
+ project.auto_devops_enabled = true
+ end
end
- end
- before do
- set_kube_ingress_base_domain(project)
- disable_optional_jobs(project)
- end
+ let!(:cluster) { Service::KubernetesCluster.new(provider_class: Service::ClusterProvider::Gcloud).create! }
- describe 'Auto DevOps support' do
- context 'when rbac is enabled' do
- let(:cluster) { Service::KubernetesCluster.new.create! }
+ let!(:kubernetes_agent) do
+ Resource::Clusters::Agent.fabricate_via_api! do |agent|
+ agent.name = 'agent1'
+ agent.project = app_project
+ end
+ end
- after do
- cluster&.remove!
- project.remove_via_api!
+ let!(:agent_token) do
+ Resource::Clusters::AgentToken.fabricate_via_api! do |token|
+ token.agent = kubernetes_agent
end
+ end
- it 'runs auto devops', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348061' do
- Flow::Login.sign_in
-
- Resource::KubernetesCluster::ProjectCluster.fabricate! do |k8s_cluster|
- k8s_cluster.project = project
- k8s_cluster.cluster = cluster
- k8s_cluster.install_ingress = true
- end
-
- Resource::Repository::ProjectPush.fabricate! do |push|
- push.project = project
- push.directory = Pathname
- .new(__dir__)
- .join('../../../../../fixtures/auto_devops_rack')
- push.commit_message = 'Create Auto DevOps compatible rack application'
- end
-
- Flow::Pipeline.visit_latest_pipeline
-
- Page::Project::Pipeline::Show.perform do |pipeline|
- pipeline.click_job('build')
- end
- Page::Project::Job::Show.perform do |job|
- expect(job).to be_successful(timeout: 600)
-
- job.click_element(:pipeline_path)
- end
-
- Page::Project::Pipeline::Show.perform do |pipeline|
- pipeline.click_job('test')
- end
- Page::Project::Job::Show.perform do |job|
- expect(job).to be_successful(timeout: 600)
-
- job.click_element(:pipeline_path)
- end
-
- Page::Project::Pipeline::Show.perform do |pipeline|
- pipeline.click_job('production')
- end
- Page::Project::Job::Show.perform do |job|
- expect(job).to be_successful(timeout: 1200)
-
- job.click_element(:pipeline_path)
- end
+ before do
+ cluster.install_kubernetes_agent(agent_token.token)
+ upload_agent_config(app_project, kubernetes_agent.name)
+
+ set_kube_ingress_base_domain(app_project)
+ set_kube_context(app_project)
+ disable_optional_jobs(app_project)
+ end
+
+ after do
+ cluster&.remove!
+ end
+
+ it 'runs auto devops', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348061' do
+ Flow::Login.sign_in
+
+ app_project.visit!
+
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ Page::Project::Pipeline::Index.perform(&:click_run_pipeline_button)
+ Page::Project::Pipeline::New.perform(&:click_run_pipeline_button)
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.click_job('build')
+ end
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 600)
+
+ job.click_element(:pipeline_path)
+ end
+
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.click_job('production')
+ end
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 600)
end
end
end
@@ -88,12 +77,43 @@ module QA
end
end
+ def set_kube_context(project)
+ Resource::CiVariable.fabricate_via_api! do |resource|
+ resource.project = project
+ resource.key = 'KUBE_CONTEXT'
+ resource.value = "#{project.path_with_namespace}:#{kubernetes_agent.name}"
+ resource.masked = false
+ end
+ end
+
+ def upload_agent_config(project, agent)
+ Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
+ Resource::Repository::Commit.fabricate_via_api! do |commit|
+ commit.project = project
+ commit.commit_message = 'Add kubernetes agent configuration'
+ commit.add_files(
+ [
+ {
+ file_path: ".gitlab/agents/#{agent}/config.yaml",
+ content: <<~YAML
+ ci_access:
+ projects:
+ - id: #{project.path_with_namespace}
+ YAML
+ }
+ ]
+ )
+ end
+ end
+ end
+
def disable_optional_jobs(project)
%w[
- CODE_QUALITY_DISABLED LICENSE_MANAGEMENT_DISABLED
- SAST_DISABLED DAST_DISABLED DEPENDENCY_SCANNING_DISABLED
- CONTAINER_SCANNING_DISABLED BROWSER_PERFORMANCE_DISABLED
- SECRET_DETECTION_DISABLED
+ TEST_DISABLED CODE_QUALITY_DISABLED LICENSE_MANAGEMENT_DISABLED
+ BROWSER_PERFORMANCE_DISABLED LOAD_PERFORMANCE_DISABLED
+ SAST_DISABLED SECRET_DETECTION_DISABLED DEPENDENCY_SCANNING_DISABLED
+ CONTAINER_SCANNING_DISABLED DAST_DISABLED REVIEW_DISABLED
+ CODE_INTELLIGENCE_DISABLED CLUSTER_IMAGE_SCANNING_DISABLED
].each do |key|
Resource::CiVariable.fabricate_via_api! do |resource|
resource.project = project
diff --git a/qa/qa/specs/features/browser_ui/7_configure/kubernetes/kubernetes_integration_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/kubernetes/kubernetes_integration_spec.rb
deleted file mode 100644
index 94f9e9ec1f6..00000000000
--- a/qa/qa/specs/features/browser_ui/7_configure/kubernetes/kubernetes_integration_spec.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-# frozen_string_literal: true
-
-module QA
- RSpec.describe 'Configure', except: { job: 'review-qa-*' } do
- describe 'Kubernetes Cluster Integration', :orchestrated, :requires_admin, :skip_live_env do
- context 'Project Clusters' do
- let!(:cluster) { Service::KubernetesCluster.new(provider_class: Service::ClusterProvider::K3s).create! }
- let(:project) do
- Resource::Project.fabricate_via_api! do |project|
- project.name = 'project-with-k8s'
- project.description = 'Project with Kubernetes cluster integration'
- end
- end
-
- before do
- Flow::Login.sign_in_as_admin
- end
-
- after do
- cluster.remove!
- end
-
- it 'can create and associate a project cluster', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/348062' do
- Resource::KubernetesCluster::ProjectCluster.fabricate_via_browser_ui! do |k8s_cluster|
- k8s_cluster.project = project
- k8s_cluster.cluster = cluster
- end.project.visit!
-
- Page::Project::Menu.perform(&:go_to_infrastructure_kubernetes)
-
- Page::Project::Infrastructure::Kubernetes::Index.perform do |index|
- expect(index).to have_cluster(cluster)
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/specs/features/shared_examples/merge_with_code_owner_shared_examples.rb b/qa/qa/specs/features/shared_examples/merge_with_code_owner_shared_examples.rb
index 4bbad9bf3e5..01b229192cc 100644
--- a/qa/qa/specs/features/shared_examples/merge_with_code_owner_shared_examples.rb
+++ b/qa/qa/specs/features/shared_examples/merge_with_code_owner_shared_examples.rb
@@ -8,11 +8,9 @@ module QA
# Require one approval from any eligible user on any branch
# This will confirm that this type of unrestricted approval is
# also satisfied when a code owner grants approval
- Page::Project::Menu.perform(&:go_to_general_settings)
- Page::Project::Settings::Main.perform do |main|
- main.expand_merge_request_approvals_settings do |settings|
- settings.set_default_number_of_approvals_required(1)
- end
+ Page::Project::Menu.perform(&:go_to_merge_request_settings)
+ Page::Project::Settings::MergeRequest.perform do |settings|
+ settings.set_default_number_of_approvals_required(1)
end
Resource::Repository::Commit.fabricate_via_api! do |commit|
diff --git a/qa/qa/specs/helpers/context_selector.rb b/qa/qa/specs/helpers/context_selector.rb
index 9ac79ad6196..3608fa7c581 100644
--- a/qa/qa/specs/helpers/context_selector.rb
+++ b/qa/qa/specs/helpers/context_selector.rb
@@ -30,6 +30,8 @@ module QA
next unless option.is_a?(Hash)
+ opts.merge!(option)
+
if option[:pipeline].present?
return true if Runtime::Env.ci_project_name.blank?
@@ -41,8 +43,6 @@ module QA
return job_matches?(option[:job])
elsif option[:subdomain].present?
- opts.merge!(option)
-
opts[:subdomain] = case option[:subdomain]
when Array
"(#{option[:subdomain].join("|")})\\."
diff --git a/qa/qa/specs/runner.rb b/qa/qa/specs/runner.rb
index 801b9b222a4..c46b6300200 100644
--- a/qa/qa/specs/runner.rb
+++ b/qa/qa/specs/runner.rb
@@ -86,8 +86,7 @@ module QA
File.open(filename, 'w') { |f| f.write(total_examples) } if total_examples.to_i > 0
- saved_file_msg = total_examples.to_i > 0 ? ". Saved to file: #{filename}" : ''
- $stdout.puts "Total examples in #{Runtime::Scenario.klass}: #{total_examples}#{saved_file_msg}"
+ $stdout.puts total_examples
end
def test_metadata_only(args)
diff --git a/qa/qa/specs/spec_helper.rb b/qa/qa/specs/spec_helper.rb
index e1c08515521..b9e67c2fa72 100644
--- a/qa/qa/specs/spec_helper.rb
+++ b/qa/qa/specs/spec_helper.rb
@@ -12,6 +12,9 @@ QA::Runtime::Browser.configure! unless QA::Runtime::Env.dry_run
QA::Runtime::AllureReport.configure!
QA::Runtime::Scenario.from_env(QA::Runtime::Env.runtime_scenario_attributes)
+# Enable zero monkey patching mode before loading any other RSpec code.
+RSpec.configure(&:disable_monkey_patching!)
+
Dir[::File.join(__dir__, "features/shared_examples/*.rb")].sort.each { |f| require f }
Dir[::File.join(__dir__, "features/shared_contexts/*.rb")].sort.each { |f| require f }
@@ -119,7 +122,6 @@ RSpec.configure do |config|
end
config.shared_context_metadata_behavior = :apply_to_host_groups
- config.disable_monkey_patching!
config.expose_dsl_globally = true
config.profile_examples = 10
config.order = :random
diff --git a/qa/qa/support/api.rb b/qa/qa/support/api.rb
index a1bbe9f378a..8fa6d3f23dc 100644
--- a/qa/qa/support/api.rb
+++ b/qa/qa/support/api.rb
@@ -3,10 +3,13 @@
module QA
module Support
module API
+ extend self
+
HTTP_STATUS_OK = 200
HTTP_STATUS_CREATED = 201
HTTP_STATUS_NO_CONTENT = 204
HTTP_STATUS_ACCEPTED = 202
+ HTTP_STATUS_PERMANENT_REDIRECT = 308
HTTP_STATUS_NOT_FOUND = 404
HTTP_STATUS_TOO_MANY_REQUESTS = 429
HTTP_STATUS_SERVER_ERROR = 500
@@ -21,7 +24,7 @@ module QA
}
RestClient::Request.execute(default_args.merge(args))
- rescue RestClient::ExceptionWithResponse => e
+ rescue StandardError => e
return_response_or_raise(e)
end
end
@@ -37,7 +40,7 @@ module QA
RestClient::Request.execute(
default_args.merge(args)
)
- rescue RestClient::ExceptionWithResponse => e
+ rescue StandardError => e
return_response_or_raise(e)
end
end
@@ -49,7 +52,7 @@ module QA
url: url,
payload: payload,
verify_ssl: false)
- rescue RestClient::ExceptionWithResponse => e
+ rescue StandardError => e
return_response_or_raise(e)
end
end
@@ -64,7 +67,7 @@ module QA
}
RestClient::Request.execute(default_args.merge(args))
- rescue RestClient::ExceptionWithResponse => e
+ rescue StandardError => e
return_response_or_raise(e)
end
end
@@ -75,7 +78,7 @@ module QA
method: :delete,
url: url,
verify_ssl: false)
- rescue RestClient::ExceptionWithResponse => e
+ rescue StandardError => e
return_response_or_raise(e)
end
end
@@ -86,11 +89,15 @@ module QA
method: :head,
url: url,
verify_ssl: false)
- rescue RestClient::ExceptionWithResponse => e
+ rescue StandardError => e
return_response_or_raise(e)
end
end
+ def masked_url(url)
+ url.sub(/private_token=[^&]*/, "private_token=[****]")
+ end
+
def with_retry_on_too_many_requests
response = nil
@@ -115,7 +122,7 @@ module QA
end
def return_response_or_raise(error)
- raise error unless error.respond_to?(:response) && error.response
+ raise error, masked_url(error.to_s) unless error.respond_to?(:response) && error.response
error.response
end
@@ -129,7 +136,7 @@ module QA
def with_paginated_response_body(url, attempts: 0)
not_ok_error = lambda do |resp|
- raise "Failed to GET #{QA::Runtime::API::Request.masked_url(url)} - (#{resp.code}): `#{resp}`."
+ raise "Failed to GET #{masked_url(url)} - (#{resp.code}): `#{resp}`."
end
loop do
diff --git a/qa/qa/support/json_formatter.rb b/qa/qa/support/json_formatter.rb
index 0b805cd9eec..252ccfe73d3 100644
--- a/qa/qa/support/json_formatter.rb
+++ b/qa/qa/support/json_formatter.rb
@@ -51,6 +51,7 @@ module QA
testcase: example.metadata[:testcase],
quarantine: example.metadata[:quarantine],
screenshot: example.metadata[:screenshot],
+ product_group: example.metadata[:product_group],
ci_job_url: QA::Runtime::Env.ci_job_url
}
end
diff --git a/qa/qa/support/matchers/eventually_matcher.rb b/qa/qa/support/matchers/eventually_matcher.rb
index 01d07585f57..3f451f89246 100644
--- a/qa/qa/support/matchers/eventually_matcher.rb
+++ b/qa/qa/support/matchers/eventually_matcher.rb
@@ -21,6 +21,7 @@ module QA
eq
be
include
+ match
be_truthy
be_falsey
be_empty
diff --git a/qa/qa/tools/ci/ff_changes.rb b/qa/qa/tools/ci/ff_changes.rb
new file mode 100644
index 00000000000..67e52633833
--- /dev/null
+++ b/qa/qa/tools/ci/ff_changes.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require "yaml"
+
+module QA
+ module Tools
+ module Ci
+ class FfChanges
+ include Helpers
+
+ def initialize(mr_diff)
+ @mr_diff = mr_diff
+ end
+
+ # Return list of feature flags changed in mr with inverse or deleted state
+ #
+ # @return [String]
+ def fetch
+ logger.info("Detecting feature flag changes")
+ ff_toggles = mr_diff.map do |change|
+ ff_yaml = ff_yaml_for_file(change)
+ next unless ff_yaml
+
+ state = if ff_yaml[:deleted]
+ "deleted"
+ else
+ ff_yaml[:default_enabled] ? 'disabled' : 'enabled'
+ end
+
+ logger.info(" found changes in feature flag '#{ff_yaml[:name]}'")
+ "#{ff_yaml[:name]}=#{state}"
+ end.compact
+
+ if ff_toggles.empty?
+ logger.info(" no changes to feature flags detected, skipping!")
+ return
+ end
+
+ logger.info(" constructed feature flag states: '#{ff_toggles}'")
+ ff_toggles.join(",")
+ end
+
+ private
+
+ attr_reader :mr_diff
+
+ # Loads the YAML feature flag definition based on changed files in merge requests.
+ # The definition is loaded from the definition file itself.
+ #
+ # @param [Hash] change mr file change
+ # @return [Hash] a hash containing the YAML data for the feature flag definition
+ def ff_yaml_for_file(change)
+ return unless change[:path] =~ %r{/feature_flags/(development|ops)/.*\.yml}
+ if change[:deleted_file]
+ return { name: change[:path].split("/").last.gsub(/\.(yml|yaml)/, ""), deleted: true }
+ end
+
+ YAML.safe_load(
+ File.read(File.expand_path("../#{change[:path]}", QA::Runtime::Path.qa_root)),
+ symbolize_names: true
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/tools/ci/helpers.rb b/qa/qa/tools/ci/helpers.rb
new file mode 100644
index 00000000000..55bb123de20
--- /dev/null
+++ b/qa/qa/tools/ci/helpers.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module QA
+ module Tools
+ module Ci
+ # Helpers for CI related tasks
+ #
+ module Helpers
+ include Support::API
+
+ # Logger instance
+ #
+ # @return [Logger]
+ def logger
+ @logger ||= Gitlab::QA::TestLogger.logger(
+ level: Gitlab::QA::Runtime::Env.log_level,
+ source: "CI Tools"
+ )
+ end
+
+ # Api get request
+ #
+ # @param [String] path
+ # @param [Hash] args
+ # @return [Hash, Array]
+ def api_get(path, **args)
+ response = get("#{api_url}/#{path}", { headers: { "PRIVATE-TOKEN" => access_token }, **args })
+ response = response.follow_redirection if response.code == Support::API::HTTP_STATUS_PERMANENT_REDIRECT
+ raise "Request failed: '#{response.body}'" unless response.code == Support::API::HTTP_STATUS_OK
+
+ args[:raw_response] ? response : parse_body(response)
+ end
+
+ # Gitlab api url
+ #
+ # @return [String]
+ def api_url
+ @api_url ||= ENV.fetch('CI_API_V4_URL', 'https://gitlab.com/api/v4')
+ end
+
+ # Api access token
+ #
+ # @return [String]
+ def access_token
+ @access_token ||= ENV.fetch('QA_GITLAB_CI_TOKEN') { raise('Variable QA_GITLAB_CI_TOKEN missing') }
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/tools/ci/non_empty_suites.rb b/qa/qa/tools/ci/non_empty_suites.rb
new file mode 100644
index 00000000000..687c11a3e62
--- /dev/null
+++ b/qa/qa/tools/ci/non_empty_suites.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+require 'open3'
+
+module QA
+ module Tools
+ module Ci
+ # Run count commands for scenarios and detect which ones have more than 0 examples to run
+ #
+ class NonEmptySuites
+ include Helpers
+
+ # rubocop:disable Layout/LineLength
+ SCENARIOS = [
+ { klass: "Test::Instance::All" },
+ { klass: "Test::Instance::Smoke" },
+ { klass: "Test::Instance::Reliable" },
+ { klass: "Test::Instance::ReviewBlocking" },
+ { klass: "Test::Instance::ReviewNonBlocking" },
+ { klass: "Test::Instance::CloudActivation" },
+ { klass: "Test::Instance::Integrations" },
+ { klass: "Test::Instance::Jira" },
+ { klass: "Test::Instance::LargeSetup" },
+ { klass: "Test::Instance::Metrics" },
+ { klass: "Test::Instance::ObjectStorage" },
+ { klass: "Test::Instance::Packages" },
+ { klass: "Test::Instance::RepositoryStorage" },
+ { klass: "Test::Integration::ServicePingDisabled" },
+ { klass: "Test::Integration::LDAPNoTLS" },
+ { klass: "Test::Integration::LDAPTLS" },
+ { klass: "Test::Integration::LDAPNoServer" },
+ { klass: "Test::Integration::InstanceSAML" },
+ { klass: "Test::Integration::RegistryWithCDN" },
+ { klass: "Test::Integration::RegistryTLS" },
+ { klass: "Test::Integration::Registry" },
+ { klass: "Test::Integration::SMTP" },
+ { klass: "QA::EE::Scenario::Test::Integration::Elasticsearch" },
+ { klass: "QA::EE::Scenario::Test::Integration::GroupSAML" },
+ {
+ klass: "QA::EE::Scenario::Test::Geo",
+ args: "--primary-address http://dummy1.test --primary-name gitlab-primary --secondary-address http://dummy2.test --secondary-name gitlab-secondary --without-setup"
+ },
+ {
+ klass: "Test::Integration::Mattermost",
+ args: "--mattermost-address http://mattermost.test"
+ }
+ ].freeze
+ # rubocop:enable Layout/LineLength
+
+ def initialize(qa_tests)
+ @qa_tests = qa_tests
+ end
+
+ # Run counts and return runnable scenario list
+ #
+ # @return [String]
+ def fetch
+ logger.info("Checking for runnable suites")
+ scenarios = SCENARIOS.each_with_object([]) do |scenario, runnable_scenarios|
+ logger.info(" fetching runnable specs for '#{scenario[:klass]}'")
+
+ out, err, status = run_command(**scenario)
+
+ unless status.success?
+ logger.error(" example count failed!\n#{err}")
+ next
+ end
+
+ count = out.split("\n").last.to_i
+ logger.info(" found #{count} examples to run")
+ runnable_scenarios << scenario[:klass] if count > 0
+ end
+
+ scenarios.join(",")
+ end
+
+ private
+
+ attr_reader :qa_tests
+
+ # Run scenario count command
+ #
+ # @param [String] klass
+ # @param [String] args
+ # @return [String]
+ def run_command(klass:, args: nil)
+ cmd = ["bundle exec bin/qa"]
+ cmd << klass
+ cmd << "--count-examples-only --address http://dummy1.test"
+ cmd << args if args
+ cmd << "-- #{qa_tests}" unless qa_tests.blank?
+
+ Open3.capture3(cmd.join(" "))
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/tools/ci/qa_changes.rb b/qa/qa/tools/ci/qa_changes.rb
new file mode 100644
index 00000000000..75274961efe
--- /dev/null
+++ b/qa/qa/tools/ci/qa_changes.rb
@@ -0,0 +1,116 @@
+# frozen_string_literal: true
+
+require "pathname"
+
+module QA
+ module Tools
+ module Ci
+ # Determine specific qa specs or paths to execute based on changes
+ class QaChanges
+ include Helpers
+
+ QA_PATTERN = %r{^qa/}.freeze
+ SPEC_PATTERN = %r{^qa/qa/specs/features/}.freeze
+
+ def initialize(mr_diff, mr_labels)
+ @mr_diff = mr_diff
+ @mr_labels = mr_labels
+ end
+
+ # Specific specs to run
+ #
+ # @return [String]
+ def qa_tests
+ return if mr_diff.empty?
+ # make paths relative to qa directory
+ return changed_files&.map { |path| path.delete_prefix("qa/") }&.join(" ") if only_spec_changes?
+ return qa_spec_directories_for_devops_stage&.join(" ") if non_qa_changes? && mr_labels.any?
+ end
+
+ # Qa framework changes
+ #
+ # @return [Boolean]
+ def framework_changes?
+ return false if mr_diff.empty?
+ return false if only_spec_changes?
+
+ changed_files
+ # TODO: expand pattern to other non spec paths that shouldn't trigger full suite
+ .select { |file_path| file_path.match?(QA_PATTERN) && !file_path.match?(SPEC_PATTERN) }
+ .any?
+ end
+
+ def quarantine_changes?
+ return false if mr_diff.empty?
+ return false if mr_diff.any? { |change| change[:new_file] || change[:deleted_file] }
+
+ files_count = 0
+ specs_count = 0
+ quarantine_specs_count = 0
+
+ mr_diff.each do |change|
+ path = change[:path]
+ next if File.directory?(File.expand_path("../#{path}", QA::Runtime::Path.qa_root))
+
+ files_count += 1
+ next unless path.match?(SPEC_PATTERN) && path.end_with?('_spec.rb')
+
+ specs_count += 1
+ quarantine_specs_count += 1 if change[:diff].match?(/^\+.*,? quarantine:/)
+ end
+
+ return false if specs_count == 0
+ return true if quarantine_specs_count == specs_count && quarantine_specs_count == files_count
+
+ false
+ end
+
+ private
+
+ # @return [Array]
+ attr_reader :mr_diff
+
+ # @return [Array]
+ attr_reader :mr_labels
+
+ # Are the changed files only qa specs?
+ #
+ # @return [Boolean] whether the changes files are only qa specs
+ def only_spec_changes?
+ changed_files.all? { |file_path| file_path =~ SPEC_PATTERN }
+ end
+
+ # Are the changed files only outside the qa directory?
+ #
+ # @return [Boolean] whether the changes files are outside of qa directory
+ def non_qa_changes?
+ changed_files.none? { |file_path| file_path =~ QA_PATTERN }
+ end
+
+ # Extract devops stage from MR labels
+ #
+ # @return [String] a devops stage
+ def devops_stage_from_mr_labels
+ mr_labels.find { |label| label =~ /^devops::/ }&.delete_prefix('devops::')
+ end
+
+ # Get qa spec directories for devops stage
+ #
+ # @return [Array] qa spec directories
+ def qa_spec_directories_for_devops_stage
+ devops_stage = devops_stage_from_mr_labels
+ return unless devops_stage
+
+ Dir.glob("qa/specs/**/*/").select { |dir| dir =~ %r{\d+_#{devops_stage}/$} }
+ end
+
+ # Change files in merge request
+ #
+ # @return [Array<String>]
+ def changed_files
+ @changed_files ||= mr_diff.map { |change| change[:path] } # rubocop:disable Rails/Pluck
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/tools/ci/test_results.rb b/qa/qa/tools/ci/test_results.rb
new file mode 100644
index 00000000000..635b69f6ca0
--- /dev/null
+++ b/qa/qa/tools/ci/test_results.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+module QA
+ module Tools
+ module Ci
+ class TestResults
+ include Helpers
+
+ def initialize(pipeline_name, test_report_job_name, report_path)
+ @pipeline_name = pipeline_name
+ @test_report_job_name = test_report_job_name
+ @report_path = report_path
+ end
+
+ # Get test report artifacts from downstream pipeline
+ #
+ # @param [String] pipeline_name
+ # @param [String] test_report_job_name
+ # @param [String] report_path
+ # @return [void]
+ def self.get(pipeline_name, test_report_job_name, report_path)
+ new(pipeline_name, test_report_job_name, report_path).download_test_results
+ end
+
+ # Download test results from child pipeline
+ #
+ # @return [void]
+ def download_test_results
+ logger.info("Fetching test results for '#{pipeline_name}'")
+
+ logger.debug(" fetching pipeline id of '#{pipeline_name}' child pipeline")
+ downstream_pipeline_id = api_get("#{pipelines_url(pipeline_id)}/bridges")
+ .find { |bridge| bridge[:name] == pipeline_name }
+ &.dig(:downstream_pipeline, :id)
+ return logger.error("Child pipeline '#{pipeline_name}' not found!") unless downstream_pipeline_id
+
+ logger.debug(" fetching job id of test report job")
+ job_id = api_get("#{pipelines_url(downstream_pipeline_id)}/jobs")
+ .find { |job| job[:name] == test_report_job_name }
+ &.fetch(:id)
+ return logger.error("Test report job '#{test_report_job_name}' not found!") unless job_id
+
+ logger.debug(" fetching test results artifact archive")
+ response = api_get("/projects/#{project_id}/jobs/#{job_id}/artifacts", raw_response: true)
+
+ logger.info("Extracting test result archive")
+ system("unzip", "-o", "-d", report_path, response.file.path)
+ end
+
+ private
+
+ attr_reader :pipeline_name, :test_report_job_name, :report_path
+
+ # Base get pipeline url
+ #
+ # @param [Integer] id
+ # @return [String]
+ def pipelines_url(id)
+ "/projects/#{project_id}/pipelines/#{id}"
+ end
+
+ # Current pipeline id
+ #
+ # @return [String]
+ def pipeline_id
+ ENV["CI_PIPELINE_ID"]
+ end
+
+ # Current project id
+ #
+ # @return [String]
+ def project_id
+ ENV["CI_PROJECT_ID"]
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/tools/revoke_all_personal_access_tokens.rb b/qa/qa/tools/revoke_all_personal_access_tokens.rb
deleted file mode 100644
index b4fa02a36d4..00000000000
--- a/qa/qa/tools/revoke_all_personal_access_tokens.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen_string_literal: true
-
-require 'net/protocol'
-
-# This script revokes all personal access tokens with the name of 'api-test-token' on the host specified by GITLAB_ADDRESS
-# Required environment variables: GITLAB_USERNAME, GITLAB_PASSWORD and GITLAB_ADDRESS
-# Run `rake revoke_personal_access_tokens`
-
-module QA
- module Tools
- class RevokeAllPersonalAccessTokens
- def run
- do_run
- rescue Net::ReadTimeout
- $stdout.puts 'Net::ReadTimeout during run. Trying again'
- run
- end
-
- private
-
- def do_run
- raise ArgumentError, "Please provide GITLAB_USERNAME" unless ENV['GITLAB_USERNAME']
- raise ArgumentError, "Please provide GITLAB_PASSWORD" unless ENV['GITLAB_PASSWORD']
- raise ArgumentError, "Please provide GITLAB_ADDRESS" unless ENV['GITLAB_ADDRESS']
-
- $stdout.puts 'Running...'
-
- Runtime::Browser.visit(ENV['GITLAB_ADDRESS'], Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
- Page::Main::Menu.perform(&:click_edit_profile_link)
- Page::Profile::Menu.perform(&:click_access_tokens)
-
- token_name = 'api-test-token'
-
- Page::Profile::PersonalAccessTokens.perform do |tokens_page|
- while tokens_page.has_token_row_for_name?(token_name)
- tokens_page.revoke_first_token_with_name(token_name)
- print "\e[32m.\e[0m"
- end
- end
- end
- end
- end
-end
diff --git a/qa/qa/tools/revoke_user_personal_access_tokens.rb b/qa/qa/tools/revoke_user_personal_access_tokens.rb
new file mode 100644
index 00000000000..2854241f420
--- /dev/null
+++ b/qa/qa/tools/revoke_user_personal_access_tokens.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+# This script revokes all active personal access tokens owned by a given USER_ID
+# up to a given date (Date.today - 1 by default)
+# Required environment variables: USER_ID, GITLAB_QA_ACCESS_TOKEN and GITLAB_ADDRESS
+# Run `rake revoke_user_pats`
+
+module QA
+ module Tools
+ class RevokeUserPersonalAccessTokens
+ include Support::API
+
+ def initialize(revoke_before: (Date.today - 1).to_s, dry_run: false)
+ raise ArgumentError, "Please provide GITLAB_ADDRESS environment variable" unless ENV['GITLAB_ADDRESS']
+
+ unless ENV['GITLAB_QA_ACCESS_TOKEN']
+ raise ArgumentError, "Please provide GITLAB_QA_ACCESS_TOKEN environment variable"
+ end
+
+ raise ArgumentError, "Please provide USER_ID environment variable" unless ENV['USER_ID']
+
+ @revoke_before = Date.parse(revoke_before)
+ @dry_run = dry_run
+ @api_client = Runtime::API::Client.new(ENV['GITLAB_ADDRESS'],
+ personal_access_token: ENV['GITLAB_QA_ACCESS_TOKEN'])
+ end
+
+ def run
+ $stdout.puts 'Running...'
+
+ tokens_head_response = head Runtime::API::Request.new(@api_client,
+ "/personal_access_tokens?user_id=#{ENV['USER_ID']}",
+ per_page: "100").url
+
+ total_token_pages = tokens_head_response.headers[:x_total_pages]
+ total_tokens = tokens_head_response.headers[:x_total]
+
+ $stdout.puts "Total tokens: #{total_tokens}. Total pages: #{total_token_pages}"
+
+ tokens = fetch_tokens
+
+ revoke_tokens(tokens, @api_client, @dry_run) unless tokens.empty?
+ $stdout.puts "\nDone"
+ end
+
+ private
+
+ def fetch_tokens
+ fetched_tokens = []
+
+ page_no = 1
+
+ while page_no > 0
+ tokens_response = get Runtime::API::Request.new(@api_client,
+ "/personal_access_tokens?user_id=#{ENV['USER_ID']}",
+ page: page_no.to_s, per_page: "100").url
+
+ fetched_tokens
+ .concat(JSON.parse(tokens_response.body)
+ .select { |token| Date.parse(token["created_at"]) < @revoke_before && token['active'] }
+ .map { |token| { id: token["id"], name: token["name"], created_at: token["created_at"] } }
+ )
+
+ page_no = tokens_response.headers[:x_next_page].to_i
+ end
+
+ fetched_tokens
+ end
+
+ def revoke_tokens(tokens, api_client, dry_run = false)
+ if dry_run
+ $stdout.puts "Following #{tokens.count} tokens would be revoked:"
+ else
+ $stdout.puts "Revoking #{tokens.count} tokens..."
+ end
+
+ tokens.each do |token|
+ if dry_run
+ $stdout.puts "Token name: #{token[:name]}, id: #{token[:id]}, created at: #{token[:created_at]}"
+ else
+ request_url = Runtime::API::Request.new(api_client, "/personal_access_tokens/#{token[:id]}").url
+
+ $stdout.puts "\nRevoking token with name: #{token[:name]}, " \
+ "id: #{token[:id]}, created at: #{token[:created_at]}"
+
+ delete_response = delete(request_url)
+ dot_or_f = delete_response.code == 204 ? "\e[32m.\e[0m" : "\e[31mF - #{delete_response}\e[0m"
+ print dot_or_f
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/tools/test_resources_handler.rb b/qa/qa/tools/test_resources_handler.rb
index 0030a47ed55..60c6dbfc16c 100644
--- a/qa/qa/tools/test_resources_handler.rb
+++ b/qa/qa/tools/test_resources_handler.rb
@@ -27,7 +27,6 @@ module QA
include Support::API
IGNORED_RESOURCES = [
- 'QA::Resource::PersonalAccessToken',
'QA::Resource::CiVariable',
'QA::Resource::Repository::Commit',
'QA::EE::Resource::GroupIteration',
diff --git a/qa/spec/fixtures/ff/bulk_import_projects.yml b/qa/spec/fixtures/ff/bulk_import_projects.yml
new file mode 100644
index 00000000000..853389577cf
--- /dev/null
+++ b/qa/spec/fixtures/ff/bulk_import_projects.yml
@@ -0,0 +1,8 @@
+---
+name: bulk_import_projects
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68873
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339941
+milestone: '14.3'
+type: development
+group: group::import
+default_enabled: false
diff --git a/qa/spec/resource/base_spec.rb b/qa/spec/resource/base_spec.rb
index f23f4aa728b..195e497f290 100644
--- a/qa/spec/resource/base_spec.rb
+++ b/qa/spec/resource/base_spec.rb
@@ -24,6 +24,10 @@ RSpec.describe QA::Resource::Base do
'block'
end
+ attribute :token do
+ 'token_value'
+ end
+
attribute :username do
'qa'
end
@@ -208,6 +212,24 @@ RSpec.describe QA::Resource::Base do
.to have_received(:debug).with(/api_with_block/)
end
end
+
+ context 'when the attribute is token and has a block' do
+ let(:api_resource) { { token: 'another_token_value' } }
+
+ before do
+ allow(QA::Runtime::Logger).to receive(:debug)
+ end
+
+ it 'emits a masked debug log entry' do
+ result = subject.fabricate!(resource: resource)
+
+ expect(result).to be_a(described_class)
+ expect(result.token).to eq('another_token_value')
+
+ expect(QA::Runtime::Logger)
+ .to have_received(:debug).with(/MASKED/)
+ end
+ end
end
context 'when the attribute is populated via direct assignment' do
diff --git a/qa/spec/resource/user_spec.rb b/qa/spec/resource/user_spec.rb
index e7397d9c0bf..c0140abf298 100644
--- a/qa/spec/resource/user_spec.rb
+++ b/qa/spec/resource/user_spec.rb
@@ -23,14 +23,15 @@ RSpec.describe QA::Resource::User do
end
describe '#password' do
- it 'generates a default password' do
- expect(subject.password).to eq('password')
+ it 'generates a random 16 character password by default' do
+ expect(subject.password).to match(/\w{16}/)
end
it 'is possible to set the password' do
- subject.password = 'secret'
+ new_password = "21c7a808"
+ subject.password = new_password
- expect(subject.password).to eq('secret')
+ expect(subject.password).to eq(new_password)
end
end
diff --git a/qa/spec/specs/helpers/context_selector_spec.rb b/qa/spec/specs/helpers/context_selector_spec.rb
index 5a320cde71f..7541bb45d82 100644
--- a/qa/spec/specs/helpers/context_selector_spec.rb
+++ b/qa/spec/specs/helpers/context_selector_spec.rb
@@ -57,6 +57,26 @@ RSpec.describe QA::Specs::Helpers::ContextSelector do
expect(described_class.context_matches?(:production)).to be_truthy
end
+ it 'matches domain' do
+ QA::Runtime::Scenario.define(:gitlab_address, 'https://jihulab.com')
+
+ aggregate_failures do
+ expect(described_class.context_matches?(:production)).to be_falsey
+ expect(described_class.context_matches?(domain: 'gitlab')).to be_falsey
+ expect(described_class.context_matches?(domain: 'jihulab')).to be_truthy
+ end
+ end
+
+ it 'matches tld' do
+ QA::Runtime::Scenario.define(:gitlab_address, 'https://gitlab.cn')
+
+ aggregate_failures do
+ expect(described_class.context_matches?).to be_falsey
+ expect(described_class.context_matches?(tld: 'net')).to be_falsey
+ expect(described_class.context_matches?(tld: 'cn')).to be_truthy
+ end
+ end
+
it 'doesnt match with mismatching switches' do
QA::Runtime::Scenario.define(:gitlab_address, 'https://gitlab.test')
diff --git a/qa/spec/tools/ci/ff_changes_spec.rb b/qa/spec/tools/ci/ff_changes_spec.rb
new file mode 100644
index 00000000000..71ca26867e0
--- /dev/null
+++ b/qa/spec/tools/ci/ff_changes_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+RSpec.describe QA::Tools::Ci::FfChanges do
+ subject(:ff_changes) { described_class.new(mr_diff) }
+
+ before do
+ allow(Gitlab::QA::TestLogger).to receive(:logger).and_return(Logger.new(StringIO.new))
+ end
+
+ context "with merge request pipeline" do
+ let(:deleted_file) { false }
+ let(:mr_diff) do
+ [
+ {
+ path: "config/feature_flags/development/bulk_import_projects.yml",
+ deleted_file: deleted_file
+ }
+ ]
+ end
+
+ before do
+ allow(File).to receive(:read)
+ .with(File.expand_path("../#{mr_diff.first[:path]}", QA::Runtime::Path.qa_root))
+ .and_return(File.read("spec/fixtures/ff/bulk_import_projects.yml"))
+ end
+
+ context "with changed feature flag" do
+ it "returns inverse ff state option" do
+ expect(ff_changes.fetch).to eq("bulk_import_projects=enabled")
+ end
+ end
+
+ context "with deleted feature flag" do
+ let(:deleted_file) { true }
+
+ it "returns deleted ff state option" do
+ expect(ff_changes.fetch).to eq("bulk_import_projects=deleted")
+ end
+ end
+ end
+
+ context "without merge request pipeline" do
+ let(:mr_diff) { [] }
+
+ context "with empty mr diff" do
+ it "doesn't return any ff options" do
+ expect(ff_changes.fetch).to be_nil
+ end
+ end
+ end
+end
diff --git a/qa/spec/tools/ci/non_empty_suites_spec.rb b/qa/spec/tools/ci/non_empty_suites_spec.rb
new file mode 100644
index 00000000000..d9bfe1eebe7
--- /dev/null
+++ b/qa/spec/tools/ci/non_empty_suites_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+RSpec.describe QA::Tools::Ci::NonEmptySuites do
+ let(:non_empty_suites) { described_class.new(nil) }
+
+ let(:status) { instance_double(Process::Status, success?: true) }
+
+ before do
+ allow(Gitlab::QA::TestLogger).to receive(:logger).and_return(Logger.new(StringIO.new))
+ allow(Open3).to receive(:capture3).and_return(["output\n0", "", status])
+ allow(Open3).to receive(:capture3)
+ .with("bundle exec bin/qa Test::Instance::All --count-examples-only --address http://dummy1.test")
+ .and_return(["output\n1", "", status])
+ end
+
+ it "returns runnable test suites" do
+ expect(non_empty_suites.fetch).to eq("Test::Instance::All")
+ end
+end
diff --git a/qa/spec/tools/ci/qa_changes_spec.rb b/qa/spec/tools/ci/qa_changes_spec.rb
new file mode 100644
index 00000000000..bc98ec16d7f
--- /dev/null
+++ b/qa/spec/tools/ci/qa_changes_spec.rb
@@ -0,0 +1,87 @@
+# frozen_string_literal: true
+
+RSpec.describe QA::Tools::Ci::QaChanges do
+ subject(:qa_changes) { described_class.new(mr_diff, mr_labels) }
+
+ let(:mr_labels) { [] }
+
+ before do
+ allow(File).to receive(:directory?).and_return(false)
+ end
+
+ context "with spec only changes" do
+ let(:mr_diff) do
+ [
+ { path: "qa/qa/specs/features/test_spec.rb", diff: "" },
+ { path: "qa/qa/specs/features/another_test_spec.rb", diff: "" }
+ ]
+ end
+
+ it ".qa_tests return changed specs" do
+ expect(qa_changes.qa_tests).to eq(
+ "qa/specs/features/test_spec.rb qa/specs/features/another_test_spec.rb"
+ )
+ end
+
+ it ".framework_changes? return false" do
+ expect(qa_changes.framework_changes?).to eq(false)
+ end
+
+ it ".quarantine_changes? return false" do
+ expect(qa_changes.quarantine_changes?).to eq(false)
+ end
+ end
+
+ context "with framework changes" do
+ let(:mr_diff) { [{ path: "qa/qa.rb" }] }
+
+ it ".qa_tests do not return specifix specs" do
+ expect(qa_changes.qa_tests).to be_nil
+ end
+
+ it ".framework_changes? return true" do
+ expect(qa_changes.framework_changes?).to eq(true)
+ end
+
+ it ".quarantine_changes? return false" do
+ expect(qa_changes.quarantine_changes?).to eq(false)
+ end
+ end
+
+ context "with non qa changes" do
+ let(:mr_diff) { [{ path: "Gemfile" }] }
+
+ it ".framework_changes? return false" do
+ expect(qa_changes.framework_changes?).to eq(false)
+ end
+
+ it ".quarantine_changes? return false" do
+ expect(qa_changes.quarantine_changes?).to eq(false)
+ end
+
+ context "without mr labels" do
+ it ".qa_tests do not return any specific specs" do
+ expect(qa_changes.qa_tests).to be_nil
+ end
+ end
+
+ context "with mr label" do
+ let(:mr_labels) { ["devops::manage"] }
+
+ it ".qa_tests return specs for devops stage" do
+ expect(qa_changes.qa_tests.split(" ")).to include(
+ "qa/specs/features/browser_ui/1_manage/",
+ "qa/specs/features/api/1_manage/"
+ )
+ end
+ end
+ end
+
+ context "with quarantine changes" do
+ let(:mr_diff) { [{ path: "qa/qa/specs/features/test_spec.rb", diff: "+ , quarantine: true" }] }
+
+ it ".quarantine_changes? return true" do
+ expect(qa_changes.quarantine_changes?).to eq(true)
+ end
+ end
+end
diff --git a/qa/tasks/ci.rake b/qa/tasks/ci.rake
new file mode 100644
index 00000000000..44a794d9f94
--- /dev/null
+++ b/qa/tasks/ci.rake
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require_relative "helpers/util"
+
+# rubocop:disable Rails/RakeEnvironment
+namespace :ci do
+ include Task::Helpers::Util
+
+ desc "Detect changes and populate test variables for selective test execution and feature flag testing"
+ task :detect_changes, [:env_file] do |_, args|
+ env_file = args[:env_file]
+ abort("ERROR: Path for environment file must be provided") unless env_file
+
+ diff = mr_diff
+ labels = mr_labels
+
+ qa_changes = QA::Tools::Ci::QaChanges.new(diff, labels)
+ logger = qa_changes.logger
+
+ logger.info("Analyzing merge request changes")
+ # skip running tests when only quarantine changes detected
+ if qa_changes.quarantine_changes?
+ logger.info(" merge request contains only quarantine changes, e2e test execution will be skipped!")
+ append_to_file(env_file, <<~TXT)
+ QA_SKIP_ALL_TESTS=true
+ TXT
+ next
+ end
+
+ tests = qa_changes.qa_tests
+ if qa_changes.framework_changes? # run all tests when framework changes detected
+ logger.info(" merge request contains qa framework changes, full test suite will be executed")
+ append_to_file(env_file, <<~TXT)
+ QA_FRAMEWORK_CHANGES=true
+ TXT
+ elsif tests
+ logger.info(" detected following specs to execute: '#{tests}'")
+ else
+ logger.info(" no specific specs to execute detected")
+ end
+
+ # always check all test suites in case a suite is defined but doesn't have any runnable specs
+ suites = QA::Tools::Ci::NonEmptySuites.new(tests).fetch
+ append_to_file(env_file, <<~TXT)
+ QA_TESTS='#{tests}'
+ QA_SUITES='#{suites}'
+ TXT
+
+ # check if mr contains feature flag changes
+ feature_flags = QA::Tools::Ci::FfChanges.new(diff).fetch
+ append_to_file(env_file, <<~TXT)
+ QA_FEATURE_FLAGS='#{feature_flags}'
+ TXT
+ end
+
+ desc "Download test results from downstream pipeline"
+ task :download_test_results, [:trigger_name, :test_report_job_name, :report_path] do |_, args|
+ QA::Tools::Ci::TestResults.get(args[:trigger_name], args[:test_report_job_name], args[:report_path])
+ end
+end
+# rubocop:enable Rails/RakeEnvironment
diff --git a/qa/tasks/helpers/util.rb b/qa/tasks/helpers/util.rb
new file mode 100644
index 00000000000..f8eb9b02f72
--- /dev/null
+++ b/qa/tasks/helpers/util.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+module Task
+ module Helpers
+ module Util
+ include ::QA::Support::API
+
+ # Append text to file
+ #
+ # @param [String] path
+ # @param [String] text
+ # @return [void]
+ def append_to_file(path, text)
+ File.open(path, "a") { |f| f.write(text) }
+ end
+
+ # Merge request labels
+ #
+ # @return [Array]
+ def mr_labels
+ ENV["CI_MERGE_REQUEST_LABELS"]&.split(',') || []
+ end
+
+ # Merge request changes
+ #
+ # @return [Array<Hash>]
+ def mr_diff
+ mr_iid = ENV["CI_MERGE_REQUEST_IID"]
+ return [] unless mr_iid
+
+ gitlab_endpoint = ENV["CI_API_V4_URL"]
+ gitlab_token = ENV["PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE"]
+ project_id = ENV["CI_MERGE_REQUEST_PROJECT_ID"]
+
+ response = get(
+ "#{gitlab_endpoint}/projects/#{project_id}/merge_requests/#{mr_iid}/changes",
+ headers: { "PRIVATE-TOKEN" => gitlab_token }
+ )
+
+ parse_body(response).fetch(:changes, []).map do |change|
+ {
+ path: change[:new_path],
+ **change.slice(:new_file, :deleted_file, :diff)
+ }
+ end
+ end
+ end
+ end
+end
diff --git a/rubocop/check_graceful_task.rb b/rubocop/check_graceful_task.rb
new file mode 100644
index 00000000000..7ae74e79e38
--- /dev/null
+++ b/rubocop/check_graceful_task.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: true
+
+require_relative 'formatter/graceful_formatter'
+require_relative '../lib/gitlab/popen'
+
+module RuboCop
+ class CheckGracefulTask
+ def initialize(output)
+ @output = output
+ end
+
+ def run(args)
+ options = %w[
+ --parallel
+ --format RuboCop::Formatter::GracefulFormatter
+ ]
+
+ available_cops = RuboCop::Cop::Registry.global.to_h
+
+ cop_names, paths = args.partition { available_cops.key?(_1) }
+
+ if cop_names.any?
+ list = cop_names.sort.join(',')
+ options.concat ['--only', list]
+ end
+
+ options.concat(paths)
+
+ puts <<~MSG
+ Running RuboCop in graceful mode:
+ rubocop #{options.join(' ')}
+
+ This might take a while...
+ MSG
+
+ status_orig = RuboCop::CLI.new.run(options)
+ status = RuboCop::Formatter::GracefulFormatter.adjusted_exit_status(status_orig)
+
+ # We had to adjust the status which means we have silenced offenses. Notify Slack!
+ notify_slack unless status_orig == status
+
+ status
+ end
+
+ private
+
+ def env_values(*keys)
+ env = ENV.slice(*keys)
+
+ missing_keys = keys - env.keys
+
+ if missing_keys.any?
+ puts "Missing ENV keys: #{missing_keys.join(', ')}"
+ return
+ end
+
+ env.values
+ end
+
+ def notify_slack
+ job_name, job_url, _ = env_values('CI_JOB_NAME', 'CI_JOB_URL', 'CI_SLACK_WEBHOOK_URL')
+
+ unless job_name
+ puts 'Skipping Slack notification.'
+ return
+ end
+
+ channel = 'f_rubocop'
+ message = ":warning: `#{job_name}` passed :green: but contained silenced offenses. See #{job_url}"
+ emoji = 'rubocop'
+ user_name = 'GitLab Bot'
+
+ puts "Notifying Slack ##{channel}."
+
+ _output, result = Gitlab::Popen.popen(['scripts/slack', channel, message, emoji, user_name])
+ puts "Failed to notify Slack channel ##{channel}." if result.nonzero?
+ end
+
+ def puts(...)
+ @output.puts(...)
+ end
+ end
+end
diff --git a/rubocop/code_reuse_helpers.rb b/rubocop/code_reuse_helpers.rb
index 2769da2389c..245bbc31cbd 100644
--- a/rubocop/code_reuse_helpers.rb
+++ b/rubocop/code_reuse_helpers.rb
@@ -81,12 +81,6 @@ module RuboCop
in_app_directory?(node, 'graphql')
end
- # Returns true if the given node resides in app/graphql/types,
- # ee/app/graphql/types, or ee/app/graphql/ee/types.
- def in_graphql_types?(node)
- in_graphql_directory?(node, 'types')
- end
-
# Returns true if the given node resides in lib/api or ee/lib/api.
def in_api?(node)
in_lib_directory?(node, 'api')
@@ -169,7 +163,7 @@ module RuboCop
each_send_node(node) do |send_node|
next unless send_receiver_name_ends_with?(send_node, suffix)
- add_offense(send_node, location: :expression, message: message)
+ add_offense(send_node, message: message)
end
end
diff --git a/rubocop/cop/active_model_errors_direct_manipulation.rb b/rubocop/cop/active_model_errors_direct_manipulation.rb
index e1e2840f37c..0a3af067743 100644
--- a/rubocop/cop/active_model_errors_direct_manipulation.rb
+++ b/rubocop/cop/active_model_errors_direct_manipulation.rb
@@ -6,7 +6,7 @@ module RuboCop
# in preparation to upgrade to Rails 6.1
#
# See https://gitlab.com/gitlab-org/gitlab/-/issues/225874
- class ActiveModelErrorsDirectManipulation < RuboCop::Cop::Cop
+ class ActiveModelErrorsDirectManipulation < RuboCop::Cop::Base
MSG = 'Avoid manipulating errors hash directly. For more details check https://gitlab.com/gitlab-org/gitlab/-/issues/225874'
MANIPULATIVE_METHODS = ":<< :append :clear :collect! :compact! :concat :delete :delete_at :delete_if :drop :drop_while :fill :filter! :keep_if :flatten! :insert :map! :pop :prepend :push :reject! :replace :reverse! :rotate! :select! :shift :shuffle! :slice! :sort! :sort_by! :uniq! :unshift"
@@ -50,10 +50,10 @@ module RuboCop
PATTERN
def on_send(node)
- add_offense(node, location: :expression) if active_model_errors_root_assignment?(node)
- add_offense(node, location: :expression) if active_model_errors_root_manipulation?(node)
- add_offense(node, location: :expression) if active_model_errors_manipulation?(node)
- add_offense(node, location: :expression) if active_model_errors_assignment?(node)
+ add_offense(node) if active_model_errors_root_assignment?(node)
+ add_offense(node) if active_model_errors_root_manipulation?(node)
+ add_offense(node) if active_model_errors_manipulation?(node)
+ add_offense(node) if active_model_errors_assignment?(node)
end
end
end
diff --git a/rubocop/cop/active_record_association_reload.rb b/rubocop/cop/active_record_association_reload.rb
index eb9fc8a0246..9a1e9674904 100644
--- a/rubocop/cop/active_record_association_reload.rb
+++ b/rubocop/cop/active_record_association_reload.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
# Cop that blacklists the use of `reload`.
- class ActiveRecordAssociationReload < RuboCop::Cop::Cop
+ class ActiveRecordAssociationReload < RuboCop::Cop::Base
MSG = 'Use reset instead of reload. ' \
'For more details check the https://gitlab.com/gitlab-org/gitlab-foss/issues/60218.'
@@ -14,7 +14,7 @@ module RuboCop
def on_send(node)
return unless reload?(node)
- add_offense(node, location: :selector)
+ add_offense(node.loc.selector)
end
end
end
diff --git a/rubocop/cop/api/base.rb b/rubocop/cop/api/base.rb
index 85b19e9a833..c10115c8265 100644
--- a/rubocop/cop/api/base.rb
+++ b/rubocop/cop/api/base.rb
@@ -3,7 +3,9 @@
module RuboCop
module Cop
module API
- class Base < RuboCop::Cop::Cop
+ class Base < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
# This cop checks that APIs subclass API::Base.
#
# @example
@@ -38,13 +40,9 @@ module RuboCop
def on_class(node)
grape_api_definition(node) do
- add_offense(node.children[1])
- end
- end
-
- def autocorrect(node)
- lambda do |corrector|
- corrector.replace(node, '::API::Base')
+ add_offense(node.children[1]) do |corrector|
+ corrector.replace(node.children[1], '::API::Base')
+ end
end
end
end
diff --git a/rubocop/cop/api/grape_array_missing_coerce.rb b/rubocop/cop/api/grape_array_missing_coerce.rb
index 3d7a6a72d81..5cb80e113a4 100644
--- a/rubocop/cop/api/grape_array_missing_coerce.rb
+++ b/rubocop/cop/api/grape_array_missing_coerce.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
module API
- class GrapeArrayMissingCoerce < RuboCop::Cop::Cop
+ class GrapeArrayMissingCoerce < RuboCop::Cop::Base
# This cop checks that Grape API parameters using an Array type
# implement a coerce_with method:
#
diff --git a/rubocop/cop/avoid_becomes.rb b/rubocop/cop/avoid_becomes.rb
index cfd6b3d2164..20df394c32c 100644
--- a/rubocop/cop/avoid_becomes.rb
+++ b/rubocop/cop/avoid_becomes.rb
@@ -9,7 +9,7 @@ module RuboCop
# problems, even when a developer eager loaded all necessary associations.
#
# See https://gitlab.com/gitlab-org/gitlab/-/issues/23182 for more information.
- class AvoidBecomes < RuboCop::Cop::Cop
+ class AvoidBecomes < RuboCop::Cop::Base
MSG = 'Avoid the use of becomes(SomeConstant), as this creates a ' \
'new object and throws away any eager loaded associations. ' \
'When creating URLs in views, just use the path helpers directly. ' \
@@ -23,7 +23,7 @@ module RuboCop
PATTERN
def on_send(node)
- add_offense(node, location: :expression) if becomes?(node)
+ add_offense(node) if becomes?(node)
end
end
end
diff --git a/rubocop/cop/avoid_break_from_strong_memoize.rb b/rubocop/cop/avoid_break_from_strong_memoize.rb
index a3a8d0c3990..1a657a99966 100644
--- a/rubocop/cop/avoid_break_from_strong_memoize.rb
+++ b/rubocop/cop/avoid_break_from_strong_memoize.rb
@@ -20,7 +20,7 @@ module RuboCop
# do_an_heavy_calculation
# end
#
- class AvoidBreakFromStrongMemoize < RuboCop::Cop::Cop
+ class AvoidBreakFromStrongMemoize < RuboCop::Cop::Base
MSG = 'Do not use break inside strong_memoize, use next instead.'
def on_block(node)
diff --git a/rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers.rb b/rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers.rb
index da3cac073ad..ea7cdd5bf9d 100644
--- a/rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers.rb
+++ b/rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
# Cop that blacklists keyword arguments usage in Sidekiq workers
- class AvoidKeywordArgumentsInSidekiqWorkers < RuboCop::Cop::Cop
+ class AvoidKeywordArgumentsInSidekiqWorkers < RuboCop::Cop::Base
MSG = "Do not use keyword arguments in Sidekiq workers. " \
"For details, check https://github.com/mperham/sidekiq/issues/2372"
OBSERVED_METHOD = :perform
@@ -13,7 +13,7 @@ module RuboCop
node.arguments.each do |argument|
if argument.type == :kwarg || argument.type == :kwoptarg
- add_offense(node, location: :expression)
+ add_offense(node)
end
end
end
diff --git a/rubocop/cop/avoid_return_from_blocks.rb b/rubocop/cop/avoid_return_from_blocks.rb
index cc1868f10a4..61edfd0a789 100644
--- a/rubocop/cop/avoid_return_from_blocks.rb
+++ b/rubocop/cop/avoid_return_from_blocks.rb
@@ -20,7 +20,7 @@ module RuboCop
# do_something_else
# end
#
- class AvoidReturnFromBlocks < RuboCop::Cop::Cop
+ class AvoidReturnFromBlocks < RuboCop::Cop::Base
MSG = 'Do not return from a block, use next or break instead.'
DEF_METHODS = %i[define_method lambda].freeze
WHITELISTED_METHODS = %i[each each_filename times loop].freeze
diff --git a/rubocop/cop/avoid_route_redirect_leading_slash.rb b/rubocop/cop/avoid_route_redirect_leading_slash.rb
index 0b0dc7d3d33..0535ae16a33 100644
--- a/rubocop/cop/avoid_route_redirect_leading_slash.rb
+++ b/rubocop/cop/avoid_route_redirect_leading_slash.rb
@@ -13,7 +13,9 @@ module RuboCop
# root to: redirect('-/autocomplete/users')
#
- class AvoidRouteRedirectLeadingSlash < RuboCop::Cop::Cop
+ class AvoidRouteRedirectLeadingSlash < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
MSG = 'Do not use a leading "/" in route redirects'
def_node_matcher :leading_slash_in_redirect?, <<~PATTERN
@@ -24,7 +26,9 @@ module RuboCop
return unless in_routes?(node)
return unless leading_slash_in_redirect?(node)
- add_offense(node)
+ add_offense(node) do |corrector|
+ corrector.replace(node.loc.expression, remove_leading_slash(node))
+ end
end
def has_leading_slash?(str)
@@ -38,12 +42,6 @@ module RuboCop
dirname.end_with?('config/routes') || filename.end_with?('routes.rb')
end
- def autocorrect(node)
- lambda do |corrector|
- corrector.replace(node.loc.expression, remove_leading_slash(node))
- end
- end
-
def remove_leading_slash(node)
node.source.sub('/', '')
end
diff --git a/rubocop/cop/ban_catch_throw.rb b/rubocop/cop/ban_catch_throw.rb
index 42301d5512f..3f215eb3140 100644
--- a/rubocop/cop/ban_catch_throw.rb
+++ b/rubocop/cop/ban_catch_throw.rb
@@ -19,7 +19,7 @@ module RuboCop
# # ...
# end
#
- class BanCatchThrow < RuboCop::Cop::Cop
+ class BanCatchThrow < RuboCop::Cop::Base
MSG = "Do not use catch or throw unless a gem's API demands it."
def on_send(node)
@@ -27,7 +27,7 @@ module RuboCop
return unless receiver.nil? && %i[catch throw].include?(method_name)
- add_offense(node, location: :expression)
+ add_offense(node)
end
end
end
diff --git a/rubocop/cop/code_reuse/finder.rb b/rubocop/cop/code_reuse/finder.rb
index 1d70befe79b..ed008f613a4 100644
--- a/rubocop/cop/code_reuse/finder.rb
+++ b/rubocop/cop/code_reuse/finder.rb
@@ -6,7 +6,7 @@ module RuboCop
module Cop
module CodeReuse
# Cop that enforces various code reuse rules for Finders.
- class Finder < RuboCop::Cop::Cop
+ class Finder < RuboCop::Cop::Base
include CodeReuseHelpers
IN_FINDER = 'Finders can not be used inside a Finder.'
diff --git a/rubocop/cop/code_reuse/presenter.rb b/rubocop/cop/code_reuse/presenter.rb
index 6eef5e5a4b0..9ceb7bde54e 100644
--- a/rubocop/cop/code_reuse/presenter.rb
+++ b/rubocop/cop/code_reuse/presenter.rb
@@ -6,7 +6,7 @@ module RuboCop
module Cop
module CodeReuse
# Cop that enforces various code reuse rules for Presenter classes.
- class Presenter < RuboCop::Cop::Cop
+ class Presenter < RuboCop::Cop::Base
include CodeReuseHelpers
IN_SERVICE = 'Presenters can not be used in a Service class.'
diff --git a/rubocop/cop/code_reuse/serializer.rb b/rubocop/cop/code_reuse/serializer.rb
index 17a84ec31f7..493432c69da 100644
--- a/rubocop/cop/code_reuse/serializer.rb
+++ b/rubocop/cop/code_reuse/serializer.rb
@@ -6,7 +6,7 @@ module RuboCop
module Cop
module CodeReuse
# Cop that enforces various code reuse rules for Serializer classes.
- class Serializer < RuboCop::Cop::Cop
+ class Serializer < RuboCop::Cop::Base
include CodeReuseHelpers
IN_SERVICE = 'Serializers can not be used in a Service class.'
diff --git a/rubocop/cop/code_reuse/service_class.rb b/rubocop/cop/code_reuse/service_class.rb
index e403a87093c..e9002f232a2 100644
--- a/rubocop/cop/code_reuse/service_class.rb
+++ b/rubocop/cop/code_reuse/service_class.rb
@@ -6,7 +6,7 @@ module RuboCop
module Cop
module CodeReuse
# Cop that enforces various code reuse rules for Service classes.
- class ServiceClass < RuboCop::Cop::Cop
+ class ServiceClass < RuboCop::Cop::Base
include CodeReuseHelpers
IN_FINDER = 'Service classes can not be used in a Finder.'
diff --git a/rubocop/cop/code_reuse/worker.rb b/rubocop/cop/code_reuse/worker.rb
index 3a1120ac2a1..f39ffbe9c6f 100644
--- a/rubocop/cop/code_reuse/worker.rb
+++ b/rubocop/cop/code_reuse/worker.rb
@@ -6,7 +6,7 @@ module RuboCop
module Cop
module CodeReuse
# Cop that enforces various code reuse rules for workers.
- class Worker < RuboCop::Cop::Cop
+ class Worker < RuboCop::Cop::Base
include CodeReuseHelpers
IN_CONTROLLER = 'Workers can not be used in a controller.'
diff --git a/rubocop/cop/database/disable_referential_integrity.rb b/rubocop/cop/database/disable_referential_integrity.rb
index 80d52678011..9e201e2d143 100644
--- a/rubocop/cop/database/disable_referential_integrity.rb
+++ b/rubocop/cop/database/disable_referential_integrity.rb
@@ -4,7 +4,7 @@ module RuboCop
module Cop
module Database
# Cop that checks if 'disable_referential_integrity' method is called.
- class DisableReferentialIntegrity < RuboCop::Cop::Cop
+ class DisableReferentialIntegrity < RuboCop::Cop::Base
MSG = <<~TEXT
Do not use `disable_referential_integrity`, disable triggers in a safe
transaction instead. Follow the format:
diff --git a/rubocop/cop/database/establish_connection.rb b/rubocop/cop/database/establish_connection.rb
index 20454887ce2..3681974640b 100644
--- a/rubocop/cop/database/establish_connection.rb
+++ b/rubocop/cop/database/establish_connection.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
module Database
- class EstablishConnection < RuboCop::Cop::Cop
+ class EstablishConnection < RuboCop::Cop::Base
MSG = "Don't establish new database connections, as this slows down " \
'tests and may result in new connections using an incorrect configuration'
@@ -12,7 +12,7 @@ module RuboCop
PATTERN
def on_send(node)
- add_offense(node, location: :expression) if establish_connection?(node)
+ add_offense(node) if establish_connection?(node)
end
end
end
diff --git a/rubocop/cop/database/multiple_databases.rb b/rubocop/cop/database/multiple_databases.rb
index 1ac9bb4473b..33ff8acd4d8 100644
--- a/rubocop/cop/database/multiple_databases.rb
+++ b/rubocop/cop/database/multiple_databases.rb
@@ -10,7 +10,7 @@ module RuboCop
# # good
# ApplicationRecord.connection
#
- class MultipleDatabases < RuboCop::Cop::Cop
+ class MultipleDatabases < RuboCop::Cop::Base
AR_BASE_MESSAGE = <<~EOF
Do not use methods from ActiveRecord::Base, use the ApplicationRecord class instead
For fixing offenses related to the ActiveRecord::Base.transaction method, see our guidelines:
@@ -32,7 +32,7 @@ module RuboCop
active_record_base_method = node.children[1]
return if method_is_allowed?(active_record_base_method)
- add_offense(node, location: :expression, message: AR_BASE_MESSAGE)
+ add_offense(node, message: AR_BASE_MESSAGE)
end
private
diff --git a/rubocop/cop/database/rescue_query_canceled.rb b/rubocop/cop/database/rescue_query_canceled.rb
index 1238f9ed911..01f3ba272c2 100644
--- a/rubocop/cop/database/rescue_query_canceled.rb
+++ b/rubocop/cop/database/rescue_query_canceled.rb
@@ -20,7 +20,7 @@ module RuboCop
# # good
#
# run_cheap_queries_with_each_batch
- class RescueQueryCanceled < RuboCop::Cop::Cop
+ class RescueQueryCanceled < RuboCop::Cop::Base
MSG = <<~EOF
Avoid rescuing the `ActiveRecord::QueryCanceled` class.
diff --git a/rubocop/cop/database/rescue_statement_timeout.rb b/rubocop/cop/database/rescue_statement_timeout.rb
index 0b75a441e29..a0e9396e8f4 100644
--- a/rubocop/cop/database/rescue_statement_timeout.rb
+++ b/rubocop/cop/database/rescue_statement_timeout.rb
@@ -20,7 +20,7 @@ module RuboCop
# # good
#
# run_cheap_queries_with_each_batch
- class RescueStatementTimeout < RuboCop::Cop::Cop
+ class RescueStatementTimeout < RuboCop::Cop::Base
MSG = <<~EOF
Avoid rescuing the `ActiveRecord::StatementTimeout` class.
diff --git a/rubocop/cop/default_scope.rb b/rubocop/cop/default_scope.rb
index 39f8c8e9ed0..930a69be881 100644
--- a/rubocop/cop/default_scope.rb
+++ b/rubocop/cop/default_scope.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
# Cop that blacklists the use of `default_scope`.
- class DefaultScope < RuboCop::Cop::Cop
+ class DefaultScope < RuboCop::Cop::Base
MSG = <<~EOF
Do not use `default_scope`, as it does not follow the principle of
least surprise. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/33847
@@ -17,7 +17,7 @@ module RuboCop
def on_send(node)
return unless default_scope?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
end
end
diff --git a/rubocop/cop/destroy_all.rb b/rubocop/cop/destroy_all.rb
index 38b6cb40f91..78e5d0f25f3 100644
--- a/rubocop/cop/destroy_all.rb
+++ b/rubocop/cop/destroy_all.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
# Cop that blacklists the use of `destroy_all`.
- class DestroyAll < RuboCop::Cop::Cop
+ class DestroyAll < RuboCop::Cop::Base
MSG = 'Use `delete_all` instead of `destroy_all`. ' \
'`destroy_all` will load the rows into memory, then execute a ' \
'`DELETE` for every individual row.'
@@ -15,7 +15,7 @@ module RuboCop
def on_send(node)
return unless destroy_all?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
end
end
diff --git a/rubocop/cop/file_decompression.rb b/rubocop/cop/file_decompression.rb
index 44813244028..e1e645e6d62 100644
--- a/rubocop/cop/file_decompression.rb
+++ b/rubocop/cop/file_decompression.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
# Check for symlinks when extracting files to avoid arbitrary file reading.
- class FileDecompression < RuboCop::Cop::Cop
+ class FileDecompression < RuboCop::Cop::Base
MSG = <<~EOF
While extracting files check for symlink to avoid arbitrary file reading.
https://gitlab.com/gitlab-com/gl-infra/production/-/issues/6132
@@ -29,7 +29,7 @@ module RuboCop
def on_send(node)
system?(node) do |match|
- add_offense(node, location: :expression, message: MSG) if forbidden_command?(match)
+ add_offense(node, message: MSG) if forbidden_command?(match)
end
end
diff --git a/rubocop/cop/filename_length.rb b/rubocop/cop/filename_length.rb
index 815db7de3f5..948f69cc88c 100644
--- a/rubocop/cop/filename_length.rb
+++ b/rubocop/cop/filename_length.rb
@@ -2,7 +2,7 @@
module RuboCop
module Cop
- class FilenameLength < Cop
+ class FilenameLength < RuboCop::Cop::Base
include RangeHelp
FILEPATH_MAX_BYTES = 256
@@ -10,14 +10,14 @@ module RuboCop
MSG_FILEPATH_LEN = "This file path is too long. It should be #{FILEPATH_MAX_BYTES} or less"
MSG_FILENAME_LEN = "This file name is too long. It should be #{FILENAME_MAX_BYTES} or less"
- def investigate(processed_source)
+ def on_new_investigation
file_path = processed_source.file_path
return if config.file_to_exclude?(file_path)
if file_path.bytesize > FILEPATH_MAX_BYTES
- add_offense(nil, location: source_range(processed_source.buffer, 1, 0, 1), message: MSG_FILEPATH_LEN)
+ add_offense(source_range(processed_source.buffer, 1, 0), message: MSG_FILEPATH_LEN)
elsif File.basename(file_path).bytesize > FILENAME_MAX_BYTES
- add_offense(nil, location: source_range(processed_source.buffer, 1, 0, 1), message: MSG_FILENAME_LEN)
+ add_offense(source_range(processed_source.buffer, 1, 0), message: MSG_FILENAME_LEN)
end
end
end
diff --git a/rubocop/cop/gitlab/avoid_feature_category_not_owned.rb b/rubocop/cop/gitlab/avoid_feature_category_not_owned.rb
index fb790f44a96..05d7824f92e 100644
--- a/rubocop/cop/gitlab/avoid_feature_category_not_owned.rb
+++ b/rubocop/cop/gitlab/avoid_feature_category_not_owned.rb
@@ -5,7 +5,7 @@ require_relative '../../code_reuse_helpers'
module RuboCop
module Cop
module Gitlab
- class AvoidFeatureCategoryNotOwned < RuboCop::Cop::Cop
+ class AvoidFeatureCategoryNotOwned < RuboCop::Cop::Base
include ::RuboCop::CodeReuseHelpers
MSG = 'Avoid adding new endpoints with `feature_category :not_owned`. See https://docs.gitlab.com/ee/development/feature_categorization'
@@ -25,7 +25,7 @@ module RuboCop
return unless file_needs_feature_category?(node)
return unless setting_not_owned?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
private
diff --git a/rubocop/cop/gitlab/avoid_feature_get.rb b/rubocop/cop/gitlab/avoid_feature_get.rb
index 1bce89268d9..68aaff8aeff 100644
--- a/rubocop/cop/gitlab/avoid_feature_get.rb
+++ b/rubocop/cop/gitlab/avoid_feature_get.rb
@@ -3,23 +3,35 @@
module RuboCop
module Cop
module Gitlab
- # Cop that blacklists the use of `Feature.get`.
- class AvoidFeatureGet < RuboCop::Cop::Cop
+ # Bans the use of `Feature.get`.
+ #
+ # @example
+ #
+ # # bad
+ #
+ # Feature.get(:x).enable
+ # Feature.get(:x).enable_percentage_of_time(100)
+ # Feature.get(:x).remove
+ #
+ # # good
+ #
+ # stub_feature_flags(x: true)
+ # Feature.enable(:x)
+ # Feature.enable_percentage_of_time(:x, 100)
+ # Feature.remove(:x)
+ #
+ class AvoidFeatureGet < RuboCop::Cop::Base
MSG = 'Use `stub_feature_flags` method instead of `Feature.get`. ' \
'See doc/development/feature_flags/index.md#feature-flags-in-tests for more information.'
def_node_matcher :feature_get?, <<~PATTERN
- (send (const nil? :Feature) :get ...)
- PATTERN
-
- def_node_matcher :global_feature_get?, <<~PATTERN
- (send (const (cbase) :Feature) :get ...)
+ (send (const {nil? cbase} :Feature) :get ...)
PATTERN
def on_send(node)
- return unless feature_get?(node) || global_feature_get?(node)
+ return unless feature_get?(node)
- add_offense(node, location: :selector)
+ add_offense(node.loc.selector)
end
end
end
diff --git a/rubocop/cop/gitlab/avoid_uploaded_file_from_params.rb b/rubocop/cop/gitlab/avoid_uploaded_file_from_params.rb
index 0795f96732d..cb3382c1b75 100644
--- a/rubocop/cop/gitlab/avoid_uploaded_file_from_params.rb
+++ b/rubocop/cop/gitlab/avoid_uploaded_file_from_params.rb
@@ -33,7 +33,7 @@ module RuboCop
# uploaded_file = declared_params[:file]
# end
# end
- class AvoidUploadedFileFromParams < RuboCop::Cop::Cop
+ class AvoidUploadedFileFromParams < RuboCop::Cop::Base
MSG = 'Use the `UploadedFile` set by `multipart.rb` instead of calling `UploadedFile.from_params` directly. See https://docs.gitlab.com/ee/development/uploads/working_with_uploads.html'
def_node_matcher :calling_uploaded_file_from_params?, <<~PATTERN
@@ -43,7 +43,7 @@ module RuboCop
def on_send(node)
return unless calling_uploaded_file_from_params?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
end
end
diff --git a/rubocop/cop/gitlab/bulk_insert.rb b/rubocop/cop/gitlab/bulk_insert.rb
index baaefc2533c..f88dcde2dc2 100644
--- a/rubocop/cop/gitlab/bulk_insert.rb
+++ b/rubocop/cop/gitlab/bulk_insert.rb
@@ -5,7 +5,7 @@ module RuboCop
module Gitlab
# Cop that disallows the use of `legacy_bulk_insert`, in favour of using
# the `BulkInsertSafe` module.
- class BulkInsert < RuboCop::Cop::Cop
+ class BulkInsert < RuboCop::Cop::Base
MSG = 'Use the `BulkInsertSafe` concern, instead of using `LegacyBulkInsert.bulk_insert`. See https://docs.gitlab.com/ee/development/insert_into_tables_in_batches.html'
def_node_matcher :raw_union?, <<~PATTERN
@@ -15,7 +15,7 @@ module RuboCop
def on_send(node)
return unless raw_union?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
end
end
diff --git a/rubocop/cop/gitlab/change_timezone.rb b/rubocop/cop/gitlab/change_timezone.rb
index c30a057d51c..a5d9393b6c3 100644
--- a/rubocop/cop/gitlab/change_timezone.rb
+++ b/rubocop/cop/gitlab/change_timezone.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
module Gitlab
- class ChangeTimezone < RuboCop::Cop::Cop
+ class ChangeTimezone < RuboCop::Cop::Base
MSG = "Do not change timezone in the runtime (application or rspec), " \
"it could result in silently modifying other behavior."
diff --git a/rubocop/cop/gitlab/const_get_inherit_false.rb b/rubocop/cop/gitlab/const_get_inherit_false.rb
index 3d3bbc4c8d3..e44097a8d9e 100644
--- a/rubocop/cop/gitlab/const_get_inherit_false.rb
+++ b/rubocop/cop/gitlab/const_get_inherit_false.rb
@@ -6,7 +6,9 @@ module RuboCop
# Cop that encourages usage of inherit=false for 2nd argument when using const_get.
#
# See https://gitlab.com/gitlab-org/gitlab/issues/27678
- class ConstGetInheritFalse < RuboCop::Cop::Cop
+ class ConstGetInheritFalse < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
MSG = 'Use inherit=false when using const_get.'
def_node_matcher :const_get?, <<~PATTERN
@@ -17,11 +19,7 @@ module RuboCop
return unless const_get?(node)
return if second_argument(node)&.false_type?
- add_offense(node, location: :selector)
- end
-
- def autocorrect(node)
- lambda do |corrector|
+ add_offense(node.loc.selector) do |corrector|
if arg = second_argument(node)
corrector.replace(arg.source_range, 'false')
else
diff --git a/rubocop/cop/gitlab/delegate_predicate_methods.rb b/rubocop/cop/gitlab/delegate_predicate_methods.rb
index 43b5184faab..9e6a2823719 100644
--- a/rubocop/cop/gitlab/delegate_predicate_methods.rb
+++ b/rubocop/cop/gitlab/delegate_predicate_methods.rb
@@ -21,7 +21,7 @@ module RuboCop
# def is_foo?
# !!bar&.is_foo?
# end
- class DelegatePredicateMethods < RuboCop::Cop::Cop
+ class DelegatePredicateMethods < RuboCop::Cop::Base
MSG = "Using `delegate` with `allow_nil` on the following predicate methods is discouraged: %s."
RESTRICT_ON_SEND = %i[delegate].freeze
def_node_matcher :predicate_allow_nil_option, <<~PATTERN
diff --git a/rubocop/cop/gitlab/deprecate_track_redis_hll_event.rb b/rubocop/cop/gitlab/deprecate_track_redis_hll_event.rb
index 3e30f3aa4d0..58411357eeb 100644
--- a/rubocop/cop/gitlab/deprecate_track_redis_hll_event.rb
+++ b/rubocop/cop/gitlab/deprecate_track_redis_hll_event.rb
@@ -14,7 +14,7 @@ module RuboCop
#
# # good
# track_event :show, name: 'g_analytics_valuestream', destinations: [:redis_hll]
- class DeprecateTrackRedisHLLEvent < RuboCop::Cop::Cop
+ class DeprecateTrackRedisHLLEvent < RuboCop::Cop::Base
MSG = '`track_redis_hll_event` is deprecated. Use `track_event` helper instead. ' \
'See https://docs.gitlab.com/ee/development/service_ping/implement.html#add-new-events'
@@ -25,7 +25,7 @@ module RuboCop
def on_send(node)
return unless track_redis_hll_event_used?(node)
- add_offense(node, location: :selector)
+ add_offense(node.loc.selector)
end
end
end
diff --git a/rubocop/cop/gitlab/event_store_subscriber.rb b/rubocop/cop/gitlab/event_store_subscriber.rb
index 0b2dd94bc42..7e4cc3e66cc 100644
--- a/rubocop/cop/gitlab/event_store_subscriber.rb
+++ b/rubocop/cop/gitlab/event_store_subscriber.rb
@@ -30,7 +30,7 @@ module RuboCop
# end
# end
#
- class EventStoreSubscriber < RuboCop::Cop::Cop
+ class EventStoreSubscriber < RuboCop::Cop::Base
SUBSCRIBER_MODULE_NAME = 'Gitlab::EventStore::Subscriber'
FORBID_PERFORM_OVERRIDE = "Do not override `perform` in a `#{SUBSCRIBER_MODULE_NAME}`."
REQUIRE_HANDLE_EVENT = "A `#{SUBSCRIBER_MODULE_NAME}` must implement `#handle_event(event)`."
diff --git a/rubocop/cop/gitlab/except.rb b/rubocop/cop/gitlab/except.rb
index 24da6962457..d20cf47b473 100644
--- a/rubocop/cop/gitlab/except.rb
+++ b/rubocop/cop/gitlab/except.rb
@@ -5,7 +5,7 @@ module RuboCop
module Gitlab
# Cop that disallows the use of `Gitlab::SQL::Except`, in favour of using
# the `FromExcept` module.
- class Except < RuboCop::Cop::Cop
+ class Except < RuboCop::Cop::Base
MSG = 'Use the `FromExcept` concern, instead of using `Gitlab::SQL::Except` directly'
def_node_matcher :raw_except?, <<~PATTERN
@@ -15,7 +15,7 @@ module RuboCop
def on_send(node)
return unless raw_except?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
end
end
diff --git a/rubocop/cop/gitlab/feature_available_usage.rb b/rubocop/cop/gitlab/feature_available_usage.rb
index f748b7d9111..4dba4baf1e7 100644
--- a/rubocop/cop/gitlab/feature_available_usage.rb
+++ b/rubocop/cop/gitlab/feature_available_usage.rb
@@ -4,7 +4,7 @@ module RuboCop
module Cop
module Gitlab
# Cop that checks for correct calling of #feature_available?
- class FeatureAvailableUsage < RuboCop::Cop::Cop
+ class FeatureAvailableUsage < RuboCop::Cop::Base
OBSERVED_METHOD = :feature_available?
LICENSED_FEATURE_LITERAL_ARG_MSG = '`feature_available?` should not be called for features that can be licensed (`%s` given), use `licensed_feature_available?(feature)` instead.'
LICENSED_FEATURE_DYNAMIC_ARG_MSG = "`feature_available?` should not be called for features that can be licensed (`%s` isn't a literal so we cannot say if it's legit or not), using `licensed_feature_available?(feature)` may be more appropriate."
@@ -21,6 +21,7 @@ module RuboCop
metrics_dashboard
analytics
operations
+ monitor
security_and_compliance
container_registry
environments
@@ -38,9 +39,9 @@ module RuboCop
return if ALL_FEATURES.include?(feature_name(node)) && args_count(node) == 2
if !ALL_FEATURES.include?(feature_name(node))
- add_offense(node, location: :expression, message: licensed_feature_message(node))
+ add_offense(node, message: licensed_feature_message(node))
elsif args_count(node) < 2
- add_offense(node, location: :expression, message: NOT_ENOUGH_ARGS_MSG)
+ add_offense(node, message: NOT_ENOUGH_ARGS_MSG)
end
end
diff --git a/rubocop/cop/gitlab/finder_with_find_by.rb b/rubocop/cop/gitlab/finder_with_find_by.rb
index 8fa9fe4a2f9..ac454398f9c 100644
--- a/rubocop/cop/gitlab/finder_with_find_by.rb
+++ b/rubocop/cop/gitlab/finder_with_find_by.rb
@@ -3,8 +3,10 @@
module RuboCop
module Cop
module Gitlab
- class FinderWithFindBy < RuboCop::Cop::Cop
- FIND_PATTERN = /\Afind(_by\!?)?\z/.freeze
+ class FinderWithFindBy < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
+ FIND_PATTERN = /\Afind(_by!?)?\z/.freeze
ALLOWED_MODULES = ['FinderMethods'].freeze
def message(used_method)
@@ -18,13 +20,9 @@ module RuboCop
end
def on_send(node)
- if find_on_execute?(node) && !allowed_module?(node)
- add_offense(node, location: :selector, message: message(node.method_name))
- end
- end
+ return unless find_on_execute?(node) && !allowed_module?(node)
- def autocorrect(node)
- lambda do |corrector|
+ add_offense(node.loc.selector, message: message(node.method_name)) do |corrector|
upto_including_execute = node.descendants.first.source_range
before_execute = node.descendants[1].source_range
range_to_remove = node.source_range
diff --git a/rubocop/cop/gitlab/httparty.rb b/rubocop/cop/gitlab/httparty.rb
index 20f0c381e11..f57c605ce91 100644
--- a/rubocop/cop/gitlab/httparty.rb
+++ b/rubocop/cop/gitlab/httparty.rb
@@ -3,7 +3,9 @@
module RuboCop
module Cop
module Gitlab
- class HTTParty < RuboCop::Cop::Cop
+ class HTTParty < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
MSG_SEND = <<~EOL
Avoid calling `HTTParty` directly. Instead, use the Gitlab::HTTP
wrapper. To allow request to localhost or the private network set
@@ -25,31 +27,18 @@ module RuboCop
PATTERN
def on_send(node)
- add_offense(node, location: :expression, message: MSG_SEND) if httparty_node?(node)
- add_offense(node, location: :expression, message: MSG_INCLUDE) if includes_httparty?(node)
- end
-
- def autocorrect(node)
- if includes_httparty?(node)
- autocorrect_includes_httparty(node)
- else
- autocorrect_httparty_node(node)
- end
- end
-
- def autocorrect_includes_httparty(node)
- lambda do |corrector|
- corrector.remove(node.source_range)
- end
- end
-
- def autocorrect_httparty_node(node)
- _, method_name, *arg_nodes = *node
-
- replacement = "Gitlab::HTTP.#{method_name}(#{arg_nodes.map(&:source).join(', ')})"
-
- lambda do |corrector|
- corrector.replace(node.source_range, replacement)
+ if httparty_node?(node)
+ add_offense(node, message: MSG_SEND) do |corrector|
+ _, method_name, *arg_nodes = *node
+
+ replacement = "Gitlab::HTTP.#{method_name}(#{arg_nodes.map(&:source).join(', ')})"
+
+ corrector.replace(node.source_range, replacement)
+ end
+ elsif includes_httparty?(node)
+ add_offense(node, message: MSG_INCLUDE) do |corrector|
+ corrector.remove(node.source_range)
+ end
end
end
end
diff --git a/rubocop/cop/gitlab/intersect.rb b/rubocop/cop/gitlab/intersect.rb
index 4b61073b804..e608b25cbe1 100644
--- a/rubocop/cop/gitlab/intersect.rb
+++ b/rubocop/cop/gitlab/intersect.rb
@@ -5,7 +5,7 @@ module RuboCop
module Gitlab
# Cop that disallows the use of `Gitlab::SQL::Intersect`, in favour of using
# the `FromIntersect` module.
- class Intersect < RuboCop::Cop::Cop
+ class Intersect < RuboCop::Cop::Base
MSG = 'Use the `FromIntersect` concern, instead of using `Gitlab::SQL::Intersect` directly'
def_node_matcher :raw_intersect?, <<~PATTERN
@@ -15,7 +15,7 @@ module RuboCop
def on_send(node)
return unless raw_intersect?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
end
end
diff --git a/rubocop/cop/gitlab/json.rb b/rubocop/cop/gitlab/json.rb
index d2ba0012ca0..56846e3c276 100644
--- a/rubocop/cop/gitlab/json.rb
+++ b/rubocop/cop/gitlab/json.rb
@@ -3,7 +3,9 @@
module RuboCop
module Cop
module Gitlab
- class Json < RuboCop::Cop::Cop
+ class Json < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
MSG = <<~EOL
Avoid calling `JSON` directly. Instead, use the `Gitlab::Json`
wrapper. This allows us to alter the JSON parser being used.
@@ -14,19 +16,13 @@ module RuboCop
PATTERN
def on_send(node)
- add_offense(node) if json_node?(node)
- end
-
- def autocorrect(node)
- autocorrect_json_node(node)
- end
+ return unless json_node?(node)
- def autocorrect_json_node(node)
- _, method_name, *arg_nodes = *node
+ add_offense(node) do |corrector|
+ _, method_name, *arg_nodes = *node
- replacement = "Gitlab::Json.#{method_name}(#{arg_nodes.map(&:source).join(', ')})"
+ replacement = "Gitlab::Json.#{method_name}(#{arg_nodes.map(&:source).join(', ')})"
- lambda do |corrector|
corrector.replace(node.source_range, replacement)
end
end
diff --git a/rubocop/cop/gitlab/keys_first_and_values_first.rb b/rubocop/cop/gitlab/keys_first_and_values_first.rb
index e9bf266cdd7..0a1d525f07f 100644
--- a/rubocop/cop/gitlab/keys_first_and_values_first.rb
+++ b/rubocop/cop/gitlab/keys_first_and_values_first.rb
@@ -3,53 +3,54 @@
module RuboCop
module Cop
module Gitlab
- class KeysFirstAndValuesFirst < RuboCop::Cop::Cop
- FIRST_PATTERN = /\Afirst\z/.freeze
-
- def message(used_method)
- <<~MSG
- Don't use `.keys.first` and `.values.first`.
- Instead use `.each_key.first` and `.each_value.first` (or `.first.first` and `first.second`)
-
- This will reduce memory usage and execution time.
- MSG
- end
+ # Detects the use of `.keys.first` or `.values.first` and suggests a
+ # change to `.each_key.first` or `.each_value.first`. This reduces
+ # memory usage and execution time.
+ #
+ # @example
+ #
+ # # bad
+ #
+ # hash.keys.first
+ # hash.values.first
+ #
+ # # good
+ #
+ # hash.each_key.first
+ # hash.each_value.first
+ #
+ class KeysFirstAndValuesFirst < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
+ MSG = 'Prefer `.%{autocorrect_method}.first` over `.%{current_method}.first`. ' \
+ 'This reduces memory usage and execution time.'
+
+ AUTOCORRECT_METHOD = {
+ keys: 'each_key',
+ values: 'each_value'
+ }.freeze
+
+ def_node_matcher :keys_or_values_first, <<~PATTERN
+ (send (send ({send const hash lvar} ...) ${:keys :values}) :first)
+ PATTERN
def on_send(node)
- if find_on_keys_or_values?(node)
- add_offense(node, location: :selector, message: message(node.method_name))
- end
- end
+ current_method = keys_or_values_first(node)
+ return unless current_method
+
+ autocorrect_method = AUTOCORRECT_METHOD.fetch(current_method)
+ msg = format(MSG, autocorrect_method: autocorrect_method, current_method: current_method)
- def autocorrect(node)
- lambda do |corrector|
- replace_with = if node.descendants.first.method_name == :values
- '.each_value'
- elsif node.descendants.first.method_name == :keys
- '.each_key'
- else
- throw("Expect '.values.first' or '.keys.first', but get #{node.descendants.first.method_name}.first") # rubocop:disable Cop/BanCatchThrow
- end
-
- upto_including_keys_or_values = node.descendants.first.source_range
- before_keys_or_values = node.descendants[1].source_range
- range_to_replace = node.source_range
- .with(begin_pos: before_keys_or_values.end_pos,
- end_pos: upto_including_keys_or_values.end_pos)
- corrector.replace(range_to_replace, replace_with)
+ add_offense(node.loc.selector, message: msg) do |corrector|
+ replacement = "#{autocorrect_expression(node)}.#{autocorrect_method}.first"
+ corrector.replace(node, replacement)
end
end
- def find_on_keys_or_values?(node)
- chained_on_node = node.descendants.first
- node.method_name.to_s =~ FIRST_PATTERN &&
- chained_on_node.is_a?(RuboCop::AST::SendNode) &&
- [:keys, :values].include?(chained_on_node.method_name) &&
- node.descendants[1]
- end
+ private
- def method_name_for_node(node)
- children[1].to_s
+ def autocorrect_expression(node)
+ node.receiver.receiver.source
end
end
end
diff --git a/rubocop/cop/gitlab/mark_used_feature_flags.rb b/rubocop/cop/gitlab/mark_used_feature_flags.rb
index 63bccec31c0..8d8c84e78f5 100644
--- a/rubocop/cop/gitlab/mark_used_feature_flags.rb
+++ b/rubocop/cop/gitlab/mark_used_feature_flags.rb
@@ -9,7 +9,7 @@ module RuboCop
#
# The files set in `tmp/feature_flags/*.used` can then be used for verification purpose.
#
- class MarkUsedFeatureFlags < RuboCop::Cop::Cop
+ class MarkUsedFeatureFlags < RuboCop::Cop::Base
include RuboCop::CodeReuseHelpers
FEATURE_METHODS = %i[enabled? disabled?].freeze
@@ -26,9 +26,6 @@ module RuboCop
data_consistency
deduplicate
].freeze
- GRAPHQL_METHODS = %i[
- field
- ].freeze
SELF_METHODS = %i[
push_frontend_feature_flag
push_force_frontend_feature_flag
@@ -36,11 +33,12 @@ module RuboCop
limit_feature_flag_for_override=
].freeze + EXPERIMENT_METHODS + RUGGED_METHODS + WORKER_METHODS
- RESTRICT_ON_SEND = FEATURE_METHODS + EXPERIMENTATION_METHODS + GRAPHQL_METHODS + SELF_METHODS
+ RESTRICT_ON_SEND = FEATURE_METHODS + EXPERIMENTATION_METHODS + SELF_METHODS
USAGE_DATA_COUNTERS_EVENTS_YAML_GLOBS = [
File.expand_path("../../../config/metrics/aggregates/*.yml", __dir__),
- File.expand_path("../../../lib/gitlab/usage_data_counters/known_events/*.yml", __dir__)
+ File.expand_path("../../../lib/gitlab/usage_data_counters/known_events/*.yml", __dir__),
+ File.expand_path("../../../ee/lib/ee/gitlab/usage_data_counters/known_events/*.yml", __dir__)
].freeze
class << self
@@ -86,12 +84,12 @@ module RuboCop
# Additionally, mark experiment-related feature flag as used as well
matching_feature_flags = defined_feature_flags.select { |flag| flag == "#{flag_value}_experiment_percentage" }
matching_feature_flags.each do |matching_feature_flag|
- puts_if_ci(node, "The '#{matching_feature_flag}' feature flag tracks the #{flag_value} experiment, which is still in use, so we'll mark it as used.")
+ puts_if_debug(node, "The '#{matching_feature_flag}' feature flag tracks the #{flag_value} experiment, which is still in use, so we'll mark it as used.")
save_used_feature_flag(matching_feature_flag)
end
end
elsif flag_arg_is_send_type?(flag_arg)
- puts_if_ci(node, "Feature flag is dynamic: '#{flag_value}.")
+ puts_if_debug(node, "Feature flag is dynamic: '#{flag_value}.")
elsif flag_arg_is_dstr_or_dsym?(flag_arg)
str_prefix = flag_arg.children[0]
rest_children = flag_arg.children[1..]
@@ -99,21 +97,23 @@ module RuboCop
if rest_children.none? { |child| child.str_type? }
matching_feature_flags = defined_feature_flags.select { |flag| flag.start_with?(str_prefix.value) }
matching_feature_flags.each do |matching_feature_flag|
- puts_if_ci(node, "The '#{matching_feature_flag}' feature flag starts with '#{str_prefix.value}', so we'll optimistically mark it as used.")
+ puts_if_debug(node, "The '#{matching_feature_flag}' feature flag starts with '#{str_prefix.value}', so we'll optimistically mark it as used.")
save_used_feature_flag(matching_feature_flag)
end
else
- puts_if_ci(node, "Interpolated feature flag name has multiple static string parts, we won't track it.")
+ puts_if_debug(node, "Interpolated feature flag name has multiple static string parts, we won't track it.")
end
else
- puts_if_ci(node, "Feature flag has an unknown type: #{flag_arg.type}.")
+ puts_if_debug(node, "Feature flag has an unknown type: #{flag_arg.type}.")
end
end
private
- def puts_if_ci(node, text)
- puts "#{text} (call: `#{node.source}`, source: #{node.location.expression.source_buffer.name})" if ENV['CI']
+ def puts_if_debug(node, text)
+ return unless RuboCop::ConfigLoader.debug
+
+ warn "#{text} (call: `#{node.source}`, source: #{node.location.expression.source_buffer.name})"
end
def save_used_feature_flag(feature_flag_name)
@@ -138,15 +138,6 @@ module RuboCop
node.children[3].each_pair.find do |pair|
pair.key.value == :feature_flag
end&.value
- elsif graphql_method?(node)
- return unless node.children.size > 3
-
- opts_index = node.children[3].hash_type? ? 3 : 4
- return unless node.children[opts_index]
-
- node.children[opts_index].each_pair.find do |pair|
- pair.key.value == :_deprecated_feature_flag
- end&.value
else
arg_index = rugged_method?(node) ? 3 : 2
@@ -178,7 +169,7 @@ module RuboCop
end
def caller_is_feature?(node)
- class_caller(node) == "Feature"
+ %w[Feature YamlProcessor::FeatureFlags].include?(class_caller(node))
end
def caller_is_feature_gitaly?(node)
@@ -209,16 +200,12 @@ module RuboCop
WORKER_METHODS.include?(method_name(node))
end
- def graphql_method?(node)
- GRAPHQL_METHODS.include?(method_name(node)) && in_graphql_types?(node)
- end
-
def self_method?(node)
SELF_METHODS.include?(method_name(node)) && class_caller(node).empty?
end
def trackable_flag?(node)
- feature_method?(node) || experimentation_method?(node) || graphql_method?(node) || self_method?(node)
+ feature_method?(node) || experimentation_method?(node) || self_method?(node)
end
# Marking all event's feature flags as used as Gitlab::UsageDataCounters::HLLRedisCounter.track_event{,context}
diff --git a/rubocop/cop/gitlab/module_with_instance_variables.rb b/rubocop/cop/gitlab/module_with_instance_variables.rb
index 40cdc0d3a57..e43ede61b7e 100644
--- a/rubocop/cop/gitlab/module_with_instance_variables.rb
+++ b/rubocop/cop/gitlab/module_with_instance_variables.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
module Gitlab
- class ModuleWithInstanceVariables < RuboCop::Cop::Cop
+ class ModuleWithInstanceVariables < RuboCop::Cop::Base
MSG = <<~EOL
Do not use instance variables in a module. Please read this
for the rationale behind it:
@@ -32,12 +32,12 @@ module RuboCop
if only_ivar_or_assignment?(definition)
# We don't allow if any other ivar is used
definition.each_descendant(:ivar) do |offense|
- add_offense(offense, location: :expression)
+ add_offense(offense)
end
# We allow initialize method and single ivar
elsif !initialize_method?(definition) && !single_ivar?(definition)
definition.each_descendant(:ivar, :ivasgn) do |offense|
- add_offense(offense, location: :expression)
+ add_offense(offense)
end
end
end
diff --git a/rubocop/cop/gitlab/policy_rule_boolean.rb b/rubocop/cop/gitlab/policy_rule_boolean.rb
index ca69eebab6e..ddf100fc8a1 100644
--- a/rubocop/cop/gitlab/policy_rule_boolean.rb
+++ b/rubocop/cop/gitlab/policy_rule_boolean.rb
@@ -22,7 +22,7 @@ module RuboCop
# # good
# rule { conducts_electricity & can?(:magnetize) }.enable :motor
# rule { ~conducts_electricity & batteries }.enable :motor
- class PolicyRuleBoolean < RuboCop::Cop::Cop
+ class PolicyRuleBoolean < RuboCop::Cop::Base
def_node_search :has_and_operator?, <<~PATTERN
(and ...)
PATTERN
diff --git a/rubocop/cop/gitlab/predicate_memoization.rb b/rubocop/cop/gitlab/predicate_memoization.rb
index 4c851f90238..3fbd004655f 100644
--- a/rubocop/cop/gitlab/predicate_memoization.rb
+++ b/rubocop/cop/gitlab/predicate_memoization.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
module Gitlab
- class PredicateMemoization < RuboCop::Cop::Cop
+ class PredicateMemoization < RuboCop::Cop::Base
MSG = <<~EOL
Avoid using `@value ||= query` inside predicate methods in order to
properly memoize `false` or `nil` values.
diff --git a/rubocop/cop/gitlab/rails_logger.rb b/rubocop/cop/gitlab/rails_logger.rb
index 5a1695ce56e..ae05491ca7d 100644
--- a/rubocop/cop/gitlab/rails_logger.rb
+++ b/rubocop/cop/gitlab/rails_logger.rb
@@ -38,7 +38,7 @@ module RuboCop
def on_send(node)
return unless rails_logger_log?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
end
end
diff --git a/rubocop/cop/gitlab/union.rb b/rubocop/cop/gitlab/union.rb
index c44c847657b..36ec6bb9b7f 100644
--- a/rubocop/cop/gitlab/union.rb
+++ b/rubocop/cop/gitlab/union.rb
@@ -5,7 +5,7 @@ module RuboCop
module Gitlab
# Cop that disallows the use of `Gitlab::SQL::Union`, in favour of using
# the `FromUnion` module.
- class Union < RuboCop::Cop::Cop
+ class Union < RuboCop::Cop::Base
MSG = 'Use the `FromUnion` concern, instead of using `Gitlab::SQL::Union` directly'
def_node_matcher :raw_union?, <<~PATTERN
@@ -15,7 +15,7 @@ module RuboCop
def on_send(node)
return unless raw_union?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
end
end
diff --git a/rubocop/cop/graphql/authorize_types.rb b/rubocop/cop/graphql/authorize_types.rb
index c96919343d6..7bd2cd9f7ef 100644
--- a/rubocop/cop/graphql/authorize_types.rb
+++ b/rubocop/cop/graphql/authorize_types.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
module Graphql
- class AuthorizeTypes < RuboCop::Cop::Cop
+ class AuthorizeTypes < RuboCop::Cop::Base
MSG = 'Add an `authorize :ability` call to the type: '\
'https://docs.gitlab.com/ee/development/graphql_guide/authorization.html#type-authorization'
@@ -19,7 +19,7 @@ module RuboCop
return if allowed?(class_constant(node))
return if allowed?(superclass_constant(node))
- add_offense(node, location: :expression) unless authorize?(node)
+ add_offense(node) unless authorize?(node)
end
private
diff --git a/rubocop/cop/graphql/descriptions.rb b/rubocop/cop/graphql/descriptions.rb
index 0d69fd55931..3c945507699 100644
--- a/rubocop/cop/graphql/descriptions.rb
+++ b/rubocop/cop/graphql/descriptions.rb
@@ -42,7 +42,9 @@
module RuboCop
module Cop
module Graphql
- class Descriptions < RuboCop::Cop::Cop
+ class Descriptions < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
MSG_STYLE_GUIDE_LINK = 'See the description style guide: https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#description-style-guide'
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}"
@@ -74,15 +76,17 @@ module RuboCop
description = locate_description(node)
- return add_offense(node, location: :expression, message: MSG_NO_DESCRIPTION) unless description
+ message = if description.nil?
+ MSG_NO_DESCRIPTION
+ elsif no_period?(description)
+ MSG_NO_PERIOD
+ elsif bad_start?(description)
+ MSG_BAD_START
+ end
- add_offense(node, location: :expression, message: MSG_NO_PERIOD) if no_period?(description)
- add_offense(node, location: :expression, message: MSG_BAD_START) if bad_start?(description)
- end
+ return unless message
- # Autocorrect missing periods at end of description.
- def autocorrect(node)
- lambda do |corrector|
+ add_offense(node, message: message) do |corrector|
description = locate_description(node)
next unless description
diff --git a/rubocop/cop/graphql/gid_expected_type.rb b/rubocop/cop/graphql/gid_expected_type.rb
index 354c5516752..7e802e6d2db 100644
--- a/rubocop/cop/graphql/gid_expected_type.rb
+++ b/rubocop/cop/graphql/gid_expected_type.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
module Graphql
- class GIDExpectedType < RuboCop::Cop::Cop
+ class GIDExpectedType < RuboCop::Cop::Base
MSG = 'Add an expected_type parameter to #object_from_id calls if possible.'
def_node_search :id_from_object?, <<~PATTERN
diff --git a/rubocop/cop/graphql/graphql_name_position.rb b/rubocop/cop/graphql/graphql_name_position.rb
index f18d65588cc..b876fb5b088 100644
--- a/rubocop/cop/graphql/graphql_name_position.rb
+++ b/rubocop/cop/graphql/graphql_name_position.rb
@@ -20,7 +20,7 @@
module RuboCop
module Cop
module Graphql
- class GraphqlNamePosition < RuboCop::Cop::Cop
+ class GraphqlNamePosition < RuboCop::Cop::Base
MSG = '`graphql_name` should be the first line of the class: '\
'https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#naming-conventions'
@@ -32,7 +32,7 @@ module RuboCop
return unless graphql_name?(node)
return if node.body.single_line?
- add_offense(node, location: :expression) unless graphql_name?(node.body.children.first)
+ add_offense(node) unless graphql_name?(node.body.children.first)
end
end
end
diff --git a/rubocop/cop/graphql/id_type.rb b/rubocop/cop/graphql/id_type.rb
index ba973242efa..eb677aa4507 100644
--- a/rubocop/cop/graphql/id_type.rb
+++ b/rubocop/cop/graphql/id_type.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
module Graphql
- class IDType < RuboCop::Cop::Cop
+ class IDType < RuboCop::Cop::Base
MSG = 'Do not use GraphQL::Types::ID, use a specific GlobalIDType instead'
WHITELISTED_ARGUMENTS = %i[iid full_path project_path group_path target_project_path namespace_path].freeze
diff --git a/rubocop/cop/graphql/json_type.rb b/rubocop/cop/graphql/json_type.rb
index 8518a771cbd..2525dc427b8 100644
--- a/rubocop/cop/graphql/json_type.rb
+++ b/rubocop/cop/graphql/json_type.rb
@@ -18,7 +18,7 @@
module RuboCop
module Cop
module Graphql
- class JSONType < RuboCop::Cop::Cop
+ class JSONType < RuboCop::Cop::Base
MSG = 'Avoid using GraphQL::Types::JSON. See: ' \
'https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#json'
@@ -32,7 +32,7 @@ module RuboCop
PATTERN
def on_send(node)
- add_offense(node, location: :expression) if has_json_type?(node)
+ add_offense(node) if has_json_type?(node)
end
end
end
diff --git a/rubocop/cop/graphql/old_types.rb b/rubocop/cop/graphql/old_types.rb
index 61e8ac92dc7..9bbda4732dd 100644
--- a/rubocop/cop/graphql/old_types.rb
+++ b/rubocop/cop/graphql/old_types.rb
@@ -19,7 +19,7 @@
module RuboCop
module Cop
module Graphql
- class OldTypes < RuboCop::Cop::Cop
+ class OldTypes < RuboCop::Cop::Base
MSG_ID = 'Avoid using GraphQL::ID_TYPE. Use GraphQL::Types::ID instead'
MSG_INT = 'Avoid using GraphQL::INT_TYPE. Use GraphQL::Types::Int instead'
MSG_STRING = 'Avoid using GraphQL::STRING_TYPE. Use GraphQL::Types::String instead'
@@ -37,7 +37,7 @@ module RuboCop
old_constant = has_old_type?(node)
return unless old_constant
- add_offense(node, location: :expression, message: "#{self.class}::MSG_#{old_constant[0..-6]}".constantize)
+ add_offense(node, message: "#{self.class}::MSG_#{old_constant[0..-6]}".constantize)
end
end
end
diff --git a/rubocop/cop/graphql/resolver_type.rb b/rubocop/cop/graphql/resolver_type.rb
index e9fa768fd3e..34f1d43fc2a 100644
--- a/rubocop/cop/graphql/resolver_type.rb
+++ b/rubocop/cop/graphql/resolver_type.rb
@@ -23,7 +23,7 @@
module RuboCop
module Cop
module Graphql
- class ResolverType < RuboCop::Cop::Cop
+ class ResolverType < RuboCop::Cop::Base
MSG = 'Missing type annotation: Please add `type` DSL method call. ' \
'e.g: type UserType.connection_type, null: true'
@@ -32,7 +32,7 @@ module RuboCop
PATTERN
def on_class(node)
- add_offense(node, location: :expression) if resolver?(node) && !typed?(node)
+ add_offense(node) if resolver?(node) && !typed?(node)
end
private
diff --git a/rubocop/cop/group_public_or_visible_to_user.rb b/rubocop/cop/group_public_or_visible_to_user.rb
index beda0b7f8ba..d3aa230680b 100644
--- a/rubocop/cop/group_public_or_visible_to_user.rb
+++ b/rubocop/cop/group_public_or_visible_to_user.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
# Cop that blacklists the usage of Group.public_or_visible_to_user
- class GroupPublicOrVisibleToUser < RuboCop::Cop::Cop
+ class GroupPublicOrVisibleToUser < RuboCop::Cop::Base
MSG = '`Group.public_or_visible_to_user` should be used with extreme care. ' \
'Please ensure that you are not using it on its own and that the amount ' \
'of rows being filtered is reasonable.'
@@ -15,7 +15,7 @@ module RuboCop
def on_send(node)
return unless public_or_visible_to_user?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
end
end
diff --git a/rubocop/cop/ignored_columns.rb b/rubocop/cop/ignored_columns.rb
index 4a6f1e4f2d9..542d78c59e9 100644
--- a/rubocop/cop/ignored_columns.rb
+++ b/rubocop/cop/ignored_columns.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
# Cop that blacklists the usage of `ActiveRecord::Base.ignored_columns=` directly
- class IgnoredColumns < RuboCop::Cop::Cop
+ class IgnoredColumns < RuboCop::Cop::Base
USE_CONCERN_MSG = 'Use `IgnoredColumns` concern instead of adding to `self.ignored_columns`.'
WRONG_MODEL_MSG = 'If the model exists in CE and EE, the column has to be ignored ' \
'in the CE model. If the model only exists in EE, then it has to be added there.'
@@ -22,11 +22,11 @@ module RuboCop
def on_send(node)
if ignored_columns?(node)
- add_offense(node, location: :expression, message: USE_CONCERN_MSG)
+ add_offense(node, message: USE_CONCERN_MSG)
end
if using_ignore?(node) && used_in_wrong_model?
- add_offense(node, location: :expression, message: WRONG_MODEL_MSG)
+ add_offense(node, message: WRONG_MODEL_MSG)
end
end
diff --git a/rubocop/cop/include_sidekiq_worker.rb b/rubocop/cop/include_sidekiq_worker.rb
index e39b8bf92c2..1a42de64aa9 100644
--- a/rubocop/cop/include_sidekiq_worker.rb
+++ b/rubocop/cop/include_sidekiq_worker.rb
@@ -3,7 +3,9 @@
module RuboCop
module Cop
# Cop that makes sure workers include `ApplicationWorker`, not `Sidekiq::Worker`.
- class IncludeSidekiqWorker < RuboCop::Cop::Cop
+ class IncludeSidekiqWorker < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
MSG = 'Include `ApplicationWorker`, not `Sidekiq::Worker`.'
def_node_matcher :includes_sidekiq_worker?, <<~PATTERN
@@ -13,12 +15,8 @@ module RuboCop
def on_send(node)
return unless includes_sidekiq_worker?(node)
- add_offense(node.arguments.first, location: :expression)
- end
-
- def autocorrect(node)
- lambda do |corrector|
- corrector.replace(node.source_range, 'ApplicationWorker')
+ add_offense(node.arguments.first) do |corrector|
+ corrector.replace(node.arguments.first, 'ApplicationWorker')
end
end
end
diff --git a/rubocop/cop/inject_enterprise_edition_module.rb b/rubocop/cop/inject_enterprise_edition_module.rb
index 785f1494354..7e3c44cc5fd 100644
--- a/rubocop/cop/inject_enterprise_edition_module.rb
+++ b/rubocop/cop/inject_enterprise_edition_module.rb
@@ -4,7 +4,9 @@ module RuboCop
module Cop
# Cop that blacklists the injecting of extension specific modules before any lines which are not already injecting another module.
# It allows multiple module injections as long as they're all at the end.
- class InjectEnterpriseEditionModule < RuboCop::Cop::Cop
+ class InjectEnterpriseEditionModule < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
INVALID_LINE = 'Injecting extension modules must be done on the last line of this file' \
', outside of any class or module definitions'
@@ -34,7 +36,7 @@ module RuboCop
return unless check_method?(node)
if DISALLOW_METHODS.include?(node.children[1])
- add_offense(node, message: DISALLOWED_METHOD)
+ add_offense(node, message: DISALLOWED_METHOD, &corrector(node))
else
verify_line_number(node)
verify_argument_type(node)
@@ -52,7 +54,7 @@ module RuboCop
if allowed_line
ignore_node(node)
elsif line < last_line
- add_offense(node, message: INVALID_LINE)
+ add_offense(node, message: INVALID_LINE, &corrector(node))
end
end
@@ -61,7 +63,7 @@ module RuboCop
return if argument.str_type?
- add_offense(argument, message: INVALID_ARGUMENT)
+ add_offense(argument, message: INVALID_ARGUMENT, &corrector(argument))
end
def check_method?(node)
@@ -74,10 +76,12 @@ module RuboCop
end
end
+ private
+
# Automatically correcting these offenses is not always possible, as
# sometimes code needs to be refactored to make this work. As such, we
# only allow developers to easily blacklist existing offenses.
- def autocorrect(node)
+ def corrector(node)
lambda do |corrector|
corrector.insert_after(
node.source_range,
diff --git a/rubocop/cop/lint/last_keyword_argument.rb b/rubocop/cop/lint/last_keyword_argument.rb
index 80f4660eeb8..3f5ad7e20d7 100644
--- a/rubocop/cop/lint/last_keyword_argument.rb
+++ b/rubocop/cop/lint/last_keyword_argument.rb
@@ -9,7 +9,9 @@ module RuboCop
# 1. Running specs with RECORD_DEPRECATIONS=1
# 1. Downloading the complete set of deprecations/ files from a CI
# pipeline (see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47720)
- class LastKeywordArgument < Cop
+ class LastKeywordArgument < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
MSG = 'Using the last argument as keyword parameters is deprecated'
DEPRECATIONS_GLOB = File.expand_path('../../../deprecations/**/*.yml', __dir__)
@@ -26,11 +28,7 @@ module RuboCop
# parser thinks `a: :b, c: :d` is hash type, it's actually kwargs
return if arg.hash_type? && !arg.source.match(/\A{/)
- add_offense(arg)
- end
-
- def autocorrect(arg)
- lambda do |corrector|
+ add_offense(arg) do |corrector|
if arg.hash_type?
kwarg = arg.source.sub(/\A{\s*/, '').sub(/\s*}\z/, '')
corrector.replace(arg, kwarg)
diff --git a/rubocop/cop/migration/add_column_with_default.rb b/rubocop/cop/migration/add_column_with_default.rb
index afd7d93cd47..36603e09263 100644
--- a/rubocop/cop/migration/add_column_with_default.rb
+++ b/rubocop/cop/migration/add_column_with_default.rb
@@ -5,7 +5,7 @@ require_relative '../../migration_helpers'
module RuboCop
module Cop
module Migration
- class AddColumnWithDefault < RuboCop::Cop::Cop
+ class AddColumnWithDefault < RuboCop::Cop::Base
include MigrationHelpers
MSG = '`add_column_with_default` is deprecated, use `add_column` instead'
@@ -15,7 +15,7 @@ module RuboCop
name = node.children[1]
- add_offense(node, location: :selector) if name == :add_column_with_default
+ add_offense(node.loc.selector) if name == :add_column_with_default
end
end
end
diff --git a/rubocop/cop/migration/add_columns_to_wide_tables.rb b/rubocop/cop/migration/add_columns_to_wide_tables.rb
index 41056379515..98dd605faef 100644
--- a/rubocop/cop/migration/add_columns_to_wide_tables.rb
+++ b/rubocop/cop/migration/add_columns_to_wide_tables.rb
@@ -6,7 +6,7 @@ module RuboCop
module Cop
module Migration
# Cop that prevents adding columns to wide tables.
- class AddColumnsToWideTables < RuboCop::Cop::Cop
+ class AddColumnsToWideTables < RuboCop::Cop::Base
include MigrationHelpers
MSG = '`%s` is a wide table with several columns, adding more should be avoided unless absolutely necessary.' \
@@ -26,7 +26,7 @@ module RuboCop
return unless offense?(method_name, table_name)
- add_offense(node, location: :selector, message: format(MSG, table_name.value))
+ add_offense(node.loc.selector, message: format(MSG, table_name.value))
end
private
diff --git a/rubocop/cop/migration/add_concurrent_foreign_key.rb b/rubocop/cop/migration/add_concurrent_foreign_key.rb
index ebab6aa653e..f538207d336 100644
--- a/rubocop/cop/migration/add_concurrent_foreign_key.rb
+++ b/rubocop/cop/migration/add_concurrent_foreign_key.rb
@@ -7,7 +7,7 @@ module RuboCop
module Migration
# Cop that checks if `add_concurrent_foreign_key` is used instead of
# `add_foreign_key`.
- class AddConcurrentForeignKey < RuboCop::Cop::Cop
+ class AddConcurrentForeignKey < RuboCop::Cop::Base
include MigrationHelpers
MSG = '`add_foreign_key` requires downtime, use `add_concurrent_foreign_key` instead'
@@ -29,7 +29,7 @@ module RuboCop
return if in_with_lock_retries?(node)
return if not_valid_fk?(node)
- add_offense(node, location: :selector)
+ add_offense(node.loc.selector)
end
def method_name(node)
diff --git a/rubocop/cop/migration/add_concurrent_index.rb b/rubocop/cop/migration/add_concurrent_index.rb
index bfe7c15bfdf..77b8ced4778 100644
--- a/rubocop/cop/migration/add_concurrent_index.rb
+++ b/rubocop/cop/migration/add_concurrent_index.rb
@@ -7,7 +7,7 @@ module RuboCop
module Migration
# Cop that checks if `add_concurrent_index` is used with `up`/`down` methods
# and not `change`.
- class AddConcurrentIndex < RuboCop::Cop::Cop
+ class AddConcurrentIndex < RuboCop::Cop::Base
include MigrationHelpers
MSG = '`add_concurrent_index` is not reversible so you must manually define ' \
@@ -21,15 +21,11 @@ module RuboCop
return unless name == :add_concurrent_index
node.each_ancestor(:def) do |def_node|
- next unless method_name(def_node) == :change
+ next unless def_node.method_name == :change
- add_offense(def_node, location: :name)
+ add_offense(def_node.loc.name)
end
end
-
- def method_name(node)
- node.children.first
- end
end
end
end
diff --git a/rubocop/cop/migration/add_index.rb b/rubocop/cop/migration/add_index.rb
index 327e89fb040..3920fd19c98 100644
--- a/rubocop/cop/migration/add_index.rb
+++ b/rubocop/cop/migration/add_index.rb
@@ -6,7 +6,7 @@ module RuboCop
module Cop
module Migration
# Cop that checks if indexes are added in a concurrent manner.
- class AddIndex < RuboCop::Cop::Cop
+ class AddIndex < RuboCop::Cop::Base
include MigrationHelpers
MSG = '`add_index` requires downtime, use `add_concurrent_index` instead'
@@ -29,7 +29,7 @@ module RuboCop
# data in these tables yet.
next if new_tables.include?(first_arg)
- add_offense(send_node, location: :selector)
+ add_offense(send_node.loc.selector)
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 a47fbe0bf16..5c71fbbfaa2 100644
--- a/rubocop/cop/migration/add_limit_to_text_columns.rb
+++ b/rubocop/cop/migration/add_limit_to_text_columns.rb
@@ -10,7 +10,7 @@ module RuboCop
# Text columns starting with `encrypted_` are very likely used
# by `attr_encrypted` which controls the text length. Those columns
# should not add a text limit.
- class AddLimitToTextColumns < RuboCop::Cop::Cop
+ class AddLimitToTextColumns < RuboCop::Cop::Base
include MigrationHelpers
TEXT_LIMIT_ATTRIBUTE_ALLOWED_SINCE = 2021_09_10_00_00_00
@@ -43,11 +43,11 @@ module RuboCop
next unless text_operation?(send_node)
if text_operation_with_limit?(send_node)
- add_offense(send_node, location: :selector, message: TEXT_LIMIT_ATTRIBUTE_NOT_ALLOWED) if version(node) < TEXT_LIMIT_ATTRIBUTE_ALLOWED_SINCE
+ add_offense(send_node.loc.selector, message: TEXT_LIMIT_ATTRIBUTE_NOT_ALLOWED) if version(node) < TEXT_LIMIT_ATTRIBUTE_ALLOWED_SINCE
else
# 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, location: :selector)
+ add_offense(send_node.loc.selector)
end
end
end
diff --git a/rubocop/cop/migration/add_reference.rb b/rubocop/cop/migration/add_reference.rb
index 33840fc7caf..02a0ef899b4 100644
--- a/rubocop/cop/migration/add_reference.rb
+++ b/rubocop/cop/migration/add_reference.rb
@@ -6,7 +6,7 @@ module RuboCop
module Migration
# add_reference can only be used with newly created tables.
# Additionally, the cop here checks that we create an index for the foreign key, too.
- class AddReference < RuboCop::Cop::Cop
+ class AddReference < RuboCop::Cop::Base
include MigrationHelpers
MSG = '`add_reference` requires downtime for existing tables, use `add_concurrent_foreign_key` instead. When used for new tables, `index: true` or `index: { options... } is required.`'
@@ -28,12 +28,12 @@ module RuboCop
# Using "add_reference" is fine for newly created tables as there's no
# data in these tables yet.
if existing_table?(new_tables, first_arg)
- add_offense(send_node, location: :selector)
+ add_offense(send_node.loc.selector)
end
# We require an index on the foreign key column.
if index_missing?(node)
- add_offense(send_node, location: :selector)
+ add_offense(send_node.loc.selector)
end
end
end
diff --git a/rubocop/cop/migration/add_timestamps.rb b/rubocop/cop/migration/add_timestamps.rb
index 01d3f01ef4f..8b09d730cbc 100644
--- a/rubocop/cop/migration/add_timestamps.rb
+++ b/rubocop/cop/migration/add_timestamps.rb
@@ -6,7 +6,7 @@ module RuboCop
module Cop
module Migration
# Cop that checks if 'add_timestamps' method is called with timezone information.
- class AddTimestamps < RuboCop::Cop::Cop
+ class AddTimestamps < RuboCop::Cop::Base
include MigrationHelpers
MSG = 'Do not use `add_timestamps`, use `add_timestamps_with_timezone` instead'
@@ -15,7 +15,7 @@ module RuboCop
def on_send(node)
return unless in_migration?(node)
- add_offense(node, location: :selector) if method_name(node) == :add_timestamps
+ add_offense(node.loc.selector) if method_name(node) == :add_timestamps
end
def method_name(node)
diff --git a/rubocop/cop/migration/background_migration_base_class.rb b/rubocop/cop/migration/background_migration_base_class.rb
index 50cbe3a69c3..9ec8403a607 100644
--- a/rubocop/cop/migration/background_migration_base_class.rb
+++ b/rubocop/cop/migration/background_migration_base_class.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
module Migration
- class BackgroundMigrationBaseClass < RuboCop::Cop::Cop
+ class BackgroundMigrationBaseClass < RuboCop::Cop::Base
MSG = 'Batched background migration jobs should inherit from Gitlab::BackgroundMigration::BatchedMigrationJob'
def_node_search :top_level_module?, <<~PATTERN
@@ -25,7 +25,7 @@ module RuboCop
return if top_level_class_node.nil? || inherits_batched_migration_job?(top_level_class_node)
- add_offense(top_level_class_node, location: :expression)
+ add_offense(top_level_class_node)
end
end
end
diff --git a/rubocop/cop/migration/background_migration_record.rb b/rubocop/cop/migration/background_migration_record.rb
index 2ee6b9f7b42..ee6f3fd140b 100644
--- a/rubocop/cop/migration/background_migration_record.rb
+++ b/rubocop/cop/migration/background_migration_record.rb
@@ -5,7 +5,7 @@ require_relative '../../migration_helpers'
module RuboCop
module Cop
module Migration
- class BackgroundMigrationRecord < RuboCop::Cop::Cop
+ class BackgroundMigrationRecord < RuboCop::Cop::Base
include MigrationHelpers
MSG = <<~MSG
@@ -26,14 +26,14 @@ module RuboCop
return unless in_background_migration?(node)
return unless inherits_from_active_record_base?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
def on_send(node)
return unless in_background_migration?(node)
return unless class_new_active_record_base?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
end
end
diff --git a/rubocop/cop/migration/background_migrations.rb b/rubocop/cop/migration/background_migrations.rb
index 39c5496e4d3..7dcc2101fb1 100644
--- a/rubocop/cop/migration/background_migrations.rb
+++ b/rubocop/cop/migration/background_migrations.rb
@@ -5,7 +5,7 @@ require_relative '../../migration_helpers'
module RuboCop
module Cop
module Migration
- class BackgroundMigrations < RuboCop::Cop::Cop
+ class BackgroundMigrations < RuboCop::Cop::Base
include MigrationHelpers
MSG = 'Background migrations are deprecated. Please use a Batched Background Migration instead.'\
@@ -20,7 +20,7 @@ module RuboCop
migrate_in
)
- add_offense(node, location: :selector) if disabled_methods.include? name
+ add_offense(node.loc.selector) if disabled_methods.include? name
end
end
end
diff --git a/rubocop/cop/migration/complex_indexes_require_name.rb b/rubocop/cop/migration/complex_indexes_require_name.rb
index 82deb36716d..771537f2576 100644
--- a/rubocop/cop/migration/complex_indexes_require_name.rb
+++ b/rubocop/cop/migration/complex_indexes_require_name.rb
@@ -5,7 +5,7 @@ require_relative '../../migration_helpers'
module RuboCop
module Cop
module Migration
- class ComplexIndexesRequireName < RuboCop::Cop::Cop
+ class ComplexIndexesRequireName < RuboCop::Cop::Base
include MigrationHelpers
MSG = 'indexes added with custom options must be explicitly named'
@@ -32,7 +32,7 @@ module RuboCop
node.each_descendant(:send) do |send_node|
next unless create_table_with_index_offense?(send_node) || add_index_offense?(send_node)
- add_offense(send_node, location: :selector)
+ add_offense(send_node.loc.selector)
end
end
diff --git a/rubocop/cop/migration/create_table_with_foreign_keys.rb b/rubocop/cop/migration/create_table_with_foreign_keys.rb
index 382a2d6f65b..e3039ac76a9 100644
--- a/rubocop/cop/migration/create_table_with_foreign_keys.rb
+++ b/rubocop/cop/migration/create_table_with_foreign_keys.rb
@@ -5,7 +5,7 @@ require_relative '../../migration_helpers'
module RuboCop
module Cop
module Migration
- class CreateTableWithForeignKeys < RuboCop::Cop::Cop
+ class CreateTableWithForeignKeys < RuboCop::Cop::Base
include MigrationHelpers
MSG = 'Creating a table with more than one foreign key at once violates our migration style guide. ' \
diff --git a/rubocop/cop/migration/datetime.rb b/rubocop/cop/migration/datetime.rb
index c605c8e1b6e..9cdb2d83ab5 100644
--- a/rubocop/cop/migration/datetime.rb
+++ b/rubocop/cop/migration/datetime.rb
@@ -6,7 +6,7 @@ module RuboCop
module Cop
module Migration
# Cop that checks if datetime data type is added with timezone information.
- class Datetime < RuboCop::Cop::Cop
+ class Datetime < RuboCop::Cop::Base
include MigrationHelpers
MSG = 'Do not use the `%s` data type, use `datetime_with_timezone` instead'
@@ -19,7 +19,7 @@ module RuboCop
method_name = send_node.children[1]
if method_name == :datetime || method_name == :timestamp
- add_offense(send_node, location: :selector, message: format(MSG, method_name))
+ add_offense(send_node.loc.selector, message: format(MSG, method_name))
end
end
end
@@ -34,7 +34,7 @@ module RuboCop
last_argument = descendant.children.last
if last_argument == :datetime || last_argument == :timestamp
- add_offense(node, location: :expression, message: format(MSG, last_argument))
+ add_offense(node, message: format(MSG, last_argument))
end
end
end
diff --git a/rubocop/cop/migration/drop_table.rb b/rubocop/cop/migration/drop_table.rb
index 531cbb14021..62dea79cbe6 100644
--- a/rubocop/cop/migration/drop_table.rb
+++ b/rubocop/cop/migration/drop_table.rb
@@ -7,7 +7,7 @@ module RuboCop
module Migration
# Cop that checks if `drop_table` is called in deployment migrations.
# Calling it in deployment migrations can cause downtimes as there still may be code using the target tables.
- class DropTable < RuboCop::Cop::Cop
+ class DropTable < RuboCop::Cop::Base
include MigrationHelpers
MSG = <<-MESSAGE.delete("\n").squeeze
@@ -22,7 +22,7 @@ module RuboCop
node.each_descendant(:send) do |send_node|
next unless offensible?(send_node)
- add_offense(send_node, location: :selector)
+ add_offense(send_node.loc.selector)
end
end
diff --git a/rubocop/cop/migration/migration_record.rb b/rubocop/cop/migration/migration_record.rb
index 291644f10e3..1543d2fff08 100644
--- a/rubocop/cop/migration/migration_record.rb
+++ b/rubocop/cop/migration/migration_record.rb
@@ -5,7 +5,7 @@ require_relative '../../migration_helpers'
module RuboCop
module Cop
module Migration
- class MigrationRecord < RuboCop::Cop::Cop
+ class MigrationRecord < RuboCop::Cop::Base
include MigrationHelpers
ENFORCED_SINCE = 2022_04_26_00_00_00
@@ -27,7 +27,7 @@ module RuboCop
return unless relevant_migration?(node)
return unless inherits_from_active_record_base?(node) || inherits_from_application_record?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
private
diff --git a/rubocop/cop/migration/prevent_global_enable_lock_retries_with_disable_ddl_transaction.rb b/rubocop/cop/migration/prevent_global_enable_lock_retries_with_disable_ddl_transaction.rb
index f5343f973e1..9556a16dc01 100644
--- a/rubocop/cop/migration/prevent_global_enable_lock_retries_with_disable_ddl_transaction.rb
+++ b/rubocop/cop/migration/prevent_global_enable_lock_retries_with_disable_ddl_transaction.rb
@@ -6,7 +6,7 @@ module RuboCop
module Cop
module Migration
# Cop that prevents usage of `enable_lock_retries!` within the `disable_ddl_transaction!` method.
- class PreventGlobalEnableLockRetriesWithDisableDdlTransaction < RuboCop::Cop::Cop
+ class PreventGlobalEnableLockRetriesWithDisableDdlTransaction < RuboCop::Cop::Base
include MigrationHelpers
MSG = '`enable_lock_retries!` cannot be used with `disable_ddl_transaction!`. Use the `with_lock_retries` helper method to define retriable code blocks.'
diff --git a/rubocop/cop/migration/prevent_index_creation.rb b/rubocop/cop/migration/prevent_index_creation.rb
index c383466f73b..185f36cb24b 100644
--- a/rubocop/cop/migration/prevent_index_creation.rb
+++ b/rubocop/cop/migration/prevent_index_creation.rb
@@ -5,7 +5,7 @@ module RuboCop
module Cop
module Migration
# Cop that checks if new indexes are introduced to forbidden tables.
- class PreventIndexCreation < RuboCop::Cop::Cop
+ class PreventIndexCreation < RuboCop::Cop::Base
include MigrationHelpers
FORBIDDEN_TABLES = %i[ci_builds].freeze
@@ -41,7 +41,7 @@ module RuboCop
return unless in_migration?(node)
node.each_descendant(:send) do |send_node|
- add_offense(send_node, location: :selector) if offense?(send_node)
+ add_offense(send_node.loc.selector) if offense?(send_node)
end
end
diff --git a/rubocop/cop/migration/prevent_strings.rb b/rubocop/cop/migration/prevent_strings.rb
index 57e29bf74ae..89659050f64 100644
--- a/rubocop/cop/migration/prevent_strings.rb
+++ b/rubocop/cop/migration/prevent_strings.rb
@@ -6,7 +6,7 @@ module RuboCop
module Cop
module Migration
# Cop that enforces using text instead of the string data type
- class PreventStrings < RuboCop::Cop::Cop
+ class PreventStrings < RuboCop::Cop::Base
include MigrationHelpers
MSG = 'Do not use the `string` data type, use `text` instead. ' \
@@ -26,7 +26,7 @@ module RuboCop
node.each_descendant(:send) do |send_node|
next unless string_operation?(send_node)
- add_offense(send_node, location: :selector)
+ add_offense(send_node.loc.selector)
end
end
diff --git a/rubocop/cop/migration/refer_to_index_by_name.rb b/rubocop/cop/migration/refer_to_index_by_name.rb
index fbf3c0d7030..78619472487 100644
--- a/rubocop/cop/migration/refer_to_index_by_name.rb
+++ b/rubocop/cop/migration/refer_to_index_by_name.rb
@@ -5,7 +5,7 @@ require_relative '../../migration_helpers'
module RuboCop
module Cop
module Migration
- class ReferToIndexByName < RuboCop::Cop::Cop
+ class ReferToIndexByName < RuboCop::Cop::Base
include MigrationHelpers
MSG = 'migration methods that refer to existing indexes must do so by name'
@@ -32,7 +32,7 @@ module RuboCop
node.each_descendant(:send) do |send_node|
next unless index_exists_offense?(send_node) || removing_index_offense?(send_node)
- add_offense(send_node, location: :selector)
+ add_offense(send_node.loc.selector)
end
end
diff --git a/rubocop/cop/migration/remove_column.rb b/rubocop/cop/migration/remove_column.rb
index 6a171ac948f..e8477a211e4 100644
--- a/rubocop/cop/migration/remove_column.rb
+++ b/rubocop/cop/migration/remove_column.rb
@@ -7,7 +7,7 @@ module RuboCop
module Migration
# Cop that checks if remove_column is used in a regular (not
# post-deployment) migration.
- class RemoveColumn < RuboCop::Cop::Cop
+ class RemoveColumn < RuboCop::Cop::Base
include MigrationHelpers
MSG = '`remove_column` must only be used in post-deployment migrations'
@@ -22,7 +22,7 @@ module RuboCop
send_method = send_node.children[1]
if send_method == :remove_column
- add_offense(send_node, location: :selector)
+ add_offense(send_node.loc.selector)
end
end
end
diff --git a/rubocop/cop/migration/remove_concurrent_index.rb b/rubocop/cop/migration/remove_concurrent_index.rb
index 30dd59d97bc..459f474d185 100644
--- a/rubocop/cop/migration/remove_concurrent_index.rb
+++ b/rubocop/cop/migration/remove_concurrent_index.rb
@@ -7,7 +7,7 @@ module RuboCop
module Migration
# Cop that checks if `remove_concurrent_index` is used with `up`/`down` methods
# and not `change`.
- class RemoveConcurrentIndex < RuboCop::Cop::Cop
+ class RemoveConcurrentIndex < RuboCop::Cop::Base
include MigrationHelpers
MSG = '`remove_concurrent_index` is not reversible so you must manually define ' \
@@ -18,13 +18,9 @@ module RuboCop
return unless node.children[1] == :remove_concurrent_index
node.each_ancestor(:def) do |def_node|
- add_offense(def_node, location: :name) if method_name(def_node) == :change
+ add_offense(def_node.loc.name) if def_node.method_name == :change
end
end
-
- def method_name(node)
- node.children[0]
- end
end
end
end
diff --git a/rubocop/cop/migration/remove_index.rb b/rubocop/cop/migration/remove_index.rb
index ca5d4af1520..26d271207dc 100644
--- a/rubocop/cop/migration/remove_index.rb
+++ b/rubocop/cop/migration/remove_index.rb
@@ -6,7 +6,7 @@ module RuboCop
module Cop
module Migration
# Cop that checks if indexes are removed in a concurrent manner.
- class RemoveIndex < RuboCop::Cop::Cop
+ class RemoveIndex < RuboCop::Cop::Base
include MigrationHelpers
MSG = '`remove_index` requires downtime, use `remove_concurrent_index` instead'
@@ -15,7 +15,7 @@ module RuboCop
return unless in_migration?(node)
node.each_descendant(:send) do |send_node|
- add_offense(send_node, location: :selector) if method_name(send_node) == :remove_index
+ add_offense(send_node.loc.selector) if method_name(send_node) == :remove_index
end
end
diff --git a/rubocop/cop/migration/safer_boolean_column.rb b/rubocop/cop/migration/safer_boolean_column.rb
index 1d780d96afa..d3d77b16357 100644
--- a/rubocop/cop/migration/safer_boolean_column.rb
+++ b/rubocop/cop/migration/safer_boolean_column.rb
@@ -18,7 +18,7 @@ module RuboCop
#
# See https://gitlab.com/gitlab-org/gitlab/issues/2750 for more
# information.
- class SaferBooleanColumn < RuboCop::Cop::Cop
+ 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`.'
@@ -52,7 +52,7 @@ module RuboCop
NULL_OFFENSE
end
- add_offense(node, location: :expression, message: format(offense, table)) if offense
+ add_offense(node, message: format(offense, table)) if offense
end
def no_default?(opts)
diff --git a/rubocop/cop/migration/schedule_async.rb b/rubocop/cop/migration/schedule_async.rb
index 4fdedecdf64..bc98d45b9c6 100644
--- a/rubocop/cop/migration/schedule_async.rb
+++ b/rubocop/cop/migration/schedule_async.rb
@@ -5,7 +5,7 @@ require_relative '../../migration_helpers'
module RuboCop
module Cop
module Migration
- class ScheduleAsync < RuboCop::Cop::Cop
+ class ScheduleAsync < RuboCop::Cop::Base
include MigrationHelpers
ENFORCED_SINCE = 2020_02_12_00_00_00
@@ -32,7 +32,7 @@ module RuboCop
return if version(node) < ENFORCED_SINCE
return unless calls_background_migration_worker?(node) || calls_ci_database_worker?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
end
end
diff --git a/rubocop/cop/migration/sidekiq_queue_migrate.rb b/rubocop/cop/migration/sidekiq_queue_migrate.rb
index 134bda590da..4a7aaa353a3 100644
--- a/rubocop/cop/migration/sidekiq_queue_migrate.rb
+++ b/rubocop/cop/migration/sidekiq_queue_migrate.rb
@@ -7,7 +7,7 @@ module RuboCop
module Migration
# Cop that checks if sidekiq_queue_migrate is used in a regular
# (not post-deployment) migration.
- class SidekiqQueueMigrate < RuboCop::Cop::Cop
+ class SidekiqQueueMigrate < RuboCop::Cop::Base
include MigrationHelpers
MSG = '`sidekiq_queue_migrate` must only be used in post-deployment migrations'
@@ -19,7 +19,7 @@ module RuboCop
send_method = send_node.children[1]
if send_method == :sidekiq_queue_migrate
- add_offense(send_node, location: :selector)
+ add_offense(send_node.loc.selector)
end
end
end
diff --git a/rubocop/cop/migration/timestamps.rb b/rubocop/cop/migration/timestamps.rb
index 44baf17d968..5cc78a8821a 100644
--- a/rubocop/cop/migration/timestamps.rb
+++ b/rubocop/cop/migration/timestamps.rb
@@ -6,7 +6,7 @@ module RuboCop
module Cop
module Migration
# Cop that checks if 'timestamps' method is called with timezone information.
- class Timestamps < RuboCop::Cop::Cop
+ class Timestamps < RuboCop::Cop::Base
include MigrationHelpers
MSG = 'Do not use `timestamps`, use `timestamps_with_timezone` instead'
@@ -16,7 +16,7 @@ module RuboCop
return unless in_migration?(node)
node.each_descendant(:send) do |send_node|
- add_offense(send_node, location: :selector) if method_name(send_node) == :timestamps
+ add_offense(send_node.loc.selector) if method_name(send_node) == :timestamps
end
end
diff --git a/rubocop/cop/migration/update_column_in_batches.rb b/rubocop/cop/migration/update_column_in_batches.rb
index e23042e1b9f..7f4479c62e3 100644
--- a/rubocop/cop/migration/update_column_in_batches.rb
+++ b/rubocop/cop/migration/update_column_in_batches.rb
@@ -7,7 +7,7 @@ module RuboCop
module Migration
# Cop that checks if a spec file exists for any migration using
# `update_column_in_batches`.
- class UpdateColumnInBatches < RuboCop::Cop::Cop
+ class UpdateColumnInBatches < RuboCop::Cop::Base
include MigrationHelpers
MSG = 'Migration running `update_column_in_batches` must have a spec file at' \
@@ -19,9 +19,9 @@ module RuboCop
spec_path = spec_filename(node)
- unless File.exist?(File.expand_path(spec_path, rails_root))
- add_offense(node, location: :expression, message: format(MSG, spec_path))
- end
+ return if File.exist?(File.expand_path(spec_path, rails_root))
+
+ add_offense(node, message: format(MSG, spec_path))
end
private
diff --git a/rubocop/cop/migration/versioned_migration_class.rb b/rubocop/cop/migration/versioned_migration_class.rb
index f2e4550c691..648782f1735 100644
--- a/rubocop/cop/migration/versioned_migration_class.rb
+++ b/rubocop/cop/migration/versioned_migration_class.rb
@@ -5,7 +5,7 @@ require_relative '../../migration_helpers'
module RuboCop
module Cop
module Migration
- class VersionedMigrationClass < RuboCop::Cop::Cop
+ class VersionedMigrationClass < RuboCop::Cop::Base
include MigrationHelpers
ENFORCED_SINCE = 2021_09_02_00_00_00
@@ -26,13 +26,13 @@ module RuboCop
return unless relevant_migration?(node)
return unless activerecord_migration_class?(node)
- add_offense(node, location: :expression, message: MSG_INHERIT)
+ add_offense(node, message: MSG_INHERIT)
end
def on_send(node)
return unless relevant_migration?(node)
- add_offense(node, location: :expression, message: MSG_INCLUDE) if includes_helpers?(node)
+ add_offense(node, message: MSG_INCLUDE) if includes_helpers?(node)
end
private
diff --git a/rubocop/cop/migration/with_lock_retries_disallowed_method.rb b/rubocop/cop/migration/with_lock_retries_disallowed_method.rb
index b3d05ad1a6d..5c96b38dcdc 100644
--- a/rubocop/cop/migration/with_lock_retries_disallowed_method.rb
+++ b/rubocop/cop/migration/with_lock_retries_disallowed_method.rb
@@ -5,7 +5,7 @@ require_relative '../../migration_helpers'
module RuboCop
module Cop
module Migration
- class WithLockRetriesDisallowedMethod < RuboCop::Cop::Cop
+ class WithLockRetriesDisallowedMethod < RuboCop::Cop::Base
include MigrationHelpers
ALLOWED_MIGRATION_METHODS = %i[
@@ -60,8 +60,8 @@ module RuboCop
return unless send_node?(node)
name = node.children[1]
- add_offense(node, location: :expression) unless ALLOWED_MIGRATION_METHODS.include?(name)
- add_offense(node, location: :selector, message: MSG_ONLY_ONE_FK_ALLOWED) if multiple_fks?(node)
+ add_offense(node) unless ALLOWED_MIGRATION_METHODS.include?(name)
+ add_offense(node.loc.selector, message: MSG_ONLY_ONE_FK_ALLOWED) if multiple_fks?(node)
end
def multiple_fks?(node)
diff --git a/rubocop/cop/migration/with_lock_retries_with_change.rb b/rubocop/cop/migration/with_lock_retries_with_change.rb
index 9d11edcb6a1..59dbd6f22d2 100644
--- a/rubocop/cop/migration/with_lock_retries_with_change.rb
+++ b/rubocop/cop/migration/with_lock_retries_with_change.rb
@@ -6,7 +6,7 @@ module RuboCop
module Cop
module Migration
# Cop that prevents usage of `with_lock_retries` within the `change` method.
- class WithLockRetriesWithChange < RuboCop::Cop::Cop
+ class WithLockRetriesWithChange < RuboCop::Cop::Base
include MigrationHelpers
MSG = '`with_lock_retries` cannot be used within `change` so you must manually define ' \
@@ -17,13 +17,9 @@ module RuboCop
return unless node.children[1] == :with_lock_retries
node.each_ancestor(:def) do |def_node|
- add_offense(def_node, location: :name) if method_name(def_node) == :change
+ add_offense(def_node.loc.name) if def_node.method_name == :change
end
end
-
- def method_name(node)
- node.children.first
- end
end
end
end
diff --git a/rubocop/cop/performance/active_record_subtransaction_methods.rb b/rubocop/cop/performance/active_record_subtransaction_methods.rb
index 3b89d3ab858..1789e20ce45 100644
--- a/rubocop/cop/performance/active_record_subtransaction_methods.rb
+++ b/rubocop/cop/performance/active_record_subtransaction_methods.rb
@@ -5,7 +5,7 @@ module RuboCop
module Performance
# Cop that disallows certain methods that rely on subtransactions in their implementation.
# Companion to Performance/ActiveRecordSubtransactions, which bans direct usage of subtransactions.
- class ActiveRecordSubtransactionMethods < RuboCop::Cop::Cop
+ class ActiveRecordSubtransactionMethods < RuboCop::Cop::Base
MSG = 'Methods that rely on subtransactions should not be used. ' \
'For more information see: https://gitlab.com/gitlab-org/gitlab/-/issues/338346'
@@ -21,7 +21,7 @@ module RuboCop
def on_send(node)
return unless DISALLOWED_METHODS.include?(node.method_name)
- add_offense(node, location: :selector)
+ add_offense(node.loc.selector)
end
end
end
diff --git a/rubocop/cop/performance/active_record_subtransactions.rb b/rubocop/cop/performance/active_record_subtransactions.rb
index a550b558e52..165efee7c6b 100644
--- a/rubocop/cop/performance/active_record_subtransactions.rb
+++ b/rubocop/cop/performance/active_record_subtransactions.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
module Performance
- class ActiveRecordSubtransactions < RuboCop::Cop::Cop
+ class ActiveRecordSubtransactions < RuboCop::Cop::Base
MSG = 'Subtransactions should not be used. ' \
'For more information see: https://gitlab.com/gitlab-org/gitlab/-/issues/338346'
diff --git a/rubocop/cop/performance/ar_count_each.rb b/rubocop/cop/performance/ar_count_each.rb
index 2fe8e549872..1dae473d87d 100644
--- a/rubocop/cop/performance/ar_count_each.rb
+++ b/rubocop/cop/performance/ar_count_each.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
module Performance
- class ARCountEach < RuboCop::Cop::Cop
+ class ARCountEach < RuboCop::Cop::Base
def message(ivar)
"If #{ivar} is AR relation, avoid `#{ivar}.count ...; #{ivar}.each... `, this will trigger two queries. " \
"Use `#{ivar}.load.size ...; #{ivar}.each... ` instead. If #{ivar} is an array, try to use #{ivar}.size."
@@ -35,7 +35,7 @@ module RuboCop
begin_node.each_descendant do |n|
ivar_each = each_match(n)
- add_offense(node, location: :expression, message: message(ivar_count)) if ivar_each == ivar_count
+ add_offense(node, message: message(ivar_count)) if ivar_each == ivar_count
end
end
end
diff --git a/rubocop/cop/performance/ar_exists_and_present_blank.rb b/rubocop/cop/performance/ar_exists_and_present_blank.rb
index 54c2d6bf95a..c2ba6c2a206 100644
--- a/rubocop/cop/performance/ar_exists_and_present_blank.rb
+++ b/rubocop/cop/performance/ar_exists_and_present_blank.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
module Performance
- class ARExistsAndPresentBlank < RuboCop::Cop::Cop
+ class ARExistsAndPresentBlank < RuboCop::Cop::Base
def message_present(ivar)
"Avoid `#{ivar}.present?`, because it will generate database query 'Select TABLE.*' which is expensive. "\
"Suggest to use `#{ivar}.any?` to replace `#{ivar}.present?`"
@@ -46,8 +46,8 @@ module RuboCop
ivar_exists = exists_match(n)
next unless ivar_exists
- add_offense(node, location: :expression, message: message_present(ivar_exists)) if ivar_exists == ivar_present
- add_offense(node, location: :expression, message: message_blank(ivar_exists)) if ivar_exists == ivar_blank
+ add_offense(node, message: message_present(ivar_exists)) if ivar_exists == ivar_present
+ add_offense(node, message: message_blank(ivar_exists)) if ivar_exists == ivar_blank
end
end
end
diff --git a/rubocop/cop/performance/readlines_each.rb b/rubocop/cop/performance/readlines_each.rb
index cb4ffaca6e9..7a3a15020db 100644
--- a/rubocop/cop/performance/readlines_each.rb
+++ b/rubocop/cop/performance/readlines_each.rb
@@ -3,7 +3,9 @@
module RuboCop
module Cop
module Performance
- class ReadlinesEach < RuboCop::Cop::Cop
+ class ReadlinesEach < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
MESSAGE = 'Avoid `IO.readlines.each`, since it reads contents into memory in full. ' \
'Use `IO.each_line` or `IO.each` instead.'
@@ -17,12 +19,9 @@ module RuboCop
PATTERN
def on_send(node)
- full_file_read_via_class?(node) { add_offense(node, location: :selector, message: MESSAGE) }
- full_file_read_via_instance?(node) { add_offense(node, location: :selector, message: MESSAGE) }
- end
+ return unless full_file_read_via_class?(node) || full_file_read_via_instance?(node)
- def autocorrect(node)
- lambda do |corrector|
+ add_offense(node.loc.selector, message: MESSAGE) do |corrector|
corrector.replace(node.loc.expression, node.source.gsub('readlines.each', 'each_line'))
end
end
diff --git a/rubocop/cop/prefer_class_methods_over_module.rb b/rubocop/cop/prefer_class_methods_over_module.rb
index 39b65073477..d865cc6c003 100644
--- a/rubocop/cop/prefer_class_methods_over_module.rb
+++ b/rubocop/cop/prefer_class_methods_over_module.rb
@@ -26,7 +26,8 @@ module RuboCop
# end
# end
#
- class PreferClassMethodsOverModule < RuboCop::Cop::Cop
+ class PreferClassMethodsOverModule < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
include RangeHelp
MSG = 'Do not use module ClassMethods, use class_methods block instead.'
@@ -36,17 +37,20 @@ module RuboCop
PATTERN
def on_module(node)
- add_offense(node) if node.defined_module_name == 'ClassMethods' && module_extends_activesupport_concern?(node)
- end
+ return unless class_methods_module_in_activesupport_concern?(node)
- def autocorrect(node)
- lambda do |corrector|
+ add_offense(node) do |corrector|
corrector.replace(module_range(node), 'class_methods do')
end
end
private
+ def class_methods_module_in_activesupport_concern?(node)
+ node.defined_module_name == 'ClassMethods' &&
+ module_extends_activesupport_concern?(node)
+ end
+
def module_extends_activesupport_concern?(node)
container_module = container_module_of(node)
return false unless container_module
diff --git a/rubocop/cop/project_path_helper.rb b/rubocop/cop/project_path_helper.rb
index 0d12f2d2b12..104a352949f 100644
--- a/rubocop/cop/project_path_helper.rb
+++ b/rubocop/cop/project_path_helper.rb
@@ -2,7 +2,9 @@
module RuboCop
module Cop
- class ProjectPathHelper < RuboCop::Cop::Cop
+ class ProjectPathHelper < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
MSG = 'Use short project path helpers without explicitly passing the namespace: ' \
'`foo_project_bar_path(project, bar)` instead of ' \
'`foo_namespace_project_bar_path(project.namespace, project, bar)`.'
@@ -19,18 +21,14 @@ module RuboCop
return unless method_name(namespace_expr) == :namespace
return unless receiver(namespace_expr) == project_expr
- add_offense(node, location: :selector)
- end
-
- def autocorrect(node)
- helper_name = method_name(node).to_s.sub('namespace_project', 'project')
+ add_offense(node.loc.selector) do |corrector|
+ helper_name = method_name(node).to_s.sub('namespace_project', 'project')
- arguments = arguments(node)
- arguments.shift # Remove namespace argument
+ arguments = arguments(node)
+ arguments.shift # Remove namespace argument
- replacement = "#{helper_name}(#{arguments.map(&:source).join(', ')})"
+ replacement = "#{helper_name}(#{arguments.map(&:source).join(', ')})"
- lambda do |corrector|
corrector.replace(node.source_range, replacement)
end
end
diff --git a/rubocop/cop/put_group_routes_under_scope.rb b/rubocop/cop/put_group_routes_under_scope.rb
index 9adec044da8..83afd112b99 100644
--- a/rubocop/cop/put_group_routes_under_scope.rb
+++ b/rubocop/cop/put_group_routes_under_scope.rb
@@ -6,7 +6,7 @@ module RuboCop
module Cop
# Checks for a group routes outside '/-/' scope.
# For more information see: https://gitlab.com/gitlab-org/gitlab/issues/29572
- class PutGroupRoutesUnderScope < RuboCop::Cop::Cop
+ class PutGroupRoutesUnderScope < RuboCop::Cop::Base
include RoutesUnderScope
MSG = 'Put new group routes under /-/ scope'
diff --git a/rubocop/cop/put_project_routes_under_scope.rb b/rubocop/cop/put_project_routes_under_scope.rb
index cddb147324f..d3b86bad947 100644
--- a/rubocop/cop/put_project_routes_under_scope.rb
+++ b/rubocop/cop/put_project_routes_under_scope.rb
@@ -6,7 +6,7 @@ module RuboCop
module Cop
# Checks for a project routes outside '/-/' scope.
# For more information see: https://gitlab.com/gitlab-org/gitlab/issues/29572
- class PutProjectRoutesUnderScope < RuboCop::Cop::Cop
+ class PutProjectRoutesUnderScope < RuboCop::Cop::Base
include RoutesUnderScope
MSG = 'Put new project routes under /-/ scope'
diff --git a/rubocop/cop/qa/ambiguous_page_object_name.rb b/rubocop/cop/qa/ambiguous_page_object_name.rb
index a4a2c04f61f..a331851bcc5 100644
--- a/rubocop/cop/qa/ambiguous_page_object_name.rb
+++ b/rubocop/cop/qa/ambiguous_page_object_name.rb
@@ -16,7 +16,7 @@ module RuboCop
# # good
# Page::Object.perform do |object| do ...
# Page::Another.perform { |another| ... }
- class AmbiguousPageObjectName < RuboCop::Cop::Cop
+ class AmbiguousPageObjectName < RuboCop::Cop::Base
include QAHelpers
MESSAGE = "Don't use 'page' as a name for a Page Object. Use `%s` instead."
diff --git a/rubocop/cop/qa/element_with_pattern.rb b/rubocop/cop/qa/element_with_pattern.rb
index d0a42497960..387ca3eb517 100644
--- a/rubocop/cop/qa/element_with_pattern.rb
+++ b/rubocop/cop/qa/element_with_pattern.rb
@@ -16,7 +16,7 @@ module RuboCop
# # good
# element :some_element
# element :some_element, required: true
- class ElementWithPattern < RuboCop::Cop::Cop
+ class ElementWithPattern < RuboCop::Cop::Base
include QAHelpers
MESSAGE = "Don't use a pattern for element, create a corresponding `%s` instead."
diff --git a/rubocop/cop/qa/selector_usage.rb b/rubocop/cop/qa/selector_usage.rb
index 568b1c30851..d615bd2926c 100644
--- a/rubocop/cop/qa/selector_usage.rb
+++ b/rubocop/cop/qa/selector_usage.rb
@@ -16,7 +16,7 @@ module RuboCop
# # good
# find('[data-testid="the_selector"]')
# find('#selector')
- class SelectorUsage < RuboCop::Cop::Cop
+ class SelectorUsage < RuboCop::Cop::Base
include QAHelpers
include CodeReuseHelpers
diff --git a/rubocop/cop/rspec/any_instance_of.rb b/rubocop/cop/rspec/any_instance_of.rb
index a939af36c13..e1cacfebfd3 100644
--- a/rubocop/cop/rspec/any_instance_of.rb
+++ b/rubocop/cop/rspec/any_instance_of.rb
@@ -24,7 +24,9 @@ module RuboCop
# expect(instance).to receive(:invalidate_issue_cache_counts)
# end
#
- class AnyInstanceOf < RuboCop::Cop::Cop
+ class AnyInstanceOf < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
MESSAGE_EXPECT = 'Do not use `expect_any_instance_of` method, use `expect_next_instance_of` instead.'
MESSAGE_ALLOW = 'Do not use `allow_any_instance_of` method, use `allow_next_instance_of` instead.'
@@ -37,22 +39,19 @@ module RuboCop
def on_send(node)
if expect_any_instance_of?(node)
- add_offense(node, location: :expression, message: MESSAGE_EXPECT)
+ add_offense(node, message: MESSAGE_EXPECT) do |corrector|
+ corrector.replace(
+ node.loc.expression,
+ replacement_any_instance_of(node, 'expect')
+ )
+ end
elsif allow_any_instance_of?(node)
- add_offense(node, location: :expression, message: MESSAGE_ALLOW)
- end
- end
-
- def autocorrect(node)
- replacement =
- if expect_any_instance_of?(node)
- replacement_any_instance_of(node, 'expect')
- elsif allow_any_instance_of?(node)
- replacement_any_instance_of(node, 'allow')
+ add_offense(node, message: MESSAGE_ALLOW) do |corrector|
+ corrector.replace(
+ node.loc.expression,
+ replacement_any_instance_of(node, 'allow')
+ )
end
-
- lambda do |corrector|
- corrector.replace(node.loc.expression, replacement)
end
end
diff --git a/rubocop/cop/rspec/be_success_matcher.rb b/rubocop/cop/rspec/be_success_matcher.rb
index dce9604b3d7..5a011845075 100644
--- a/rubocop/cop/rspec/be_success_matcher.rb
+++ b/rubocop/cop/rspec/be_success_matcher.rb
@@ -21,7 +21,9 @@ module RuboCop
#
# it { is_expected.to be_successful }
#
- class BeSuccessMatcher < RuboCop::Cop::Cop
+ class BeSuccessMatcher < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
MESSAGE = 'Do not use deprecated `success?` method, use `successful?` instead.'
def_node_search :expect_to_be_success?, <<~PATTERN
@@ -39,11 +41,7 @@ module RuboCop
def on_send(node)
return unless be_success_usage?(node)
- add_offense(node, location: :expression, message: MESSAGE)
- end
-
- def autocorrect(node)
- lambda do |corrector|
+ add_offense(node, message: MESSAGE) do |corrector|
corrector.insert_after(node.loc.expression, 'ful')
end
end
diff --git a/rubocop/cop/rspec/env_assignment.rb b/rubocop/cop/rspec/env_assignment.rb
index e3075e7bd90..add7897c624 100644
--- a/rubocop/cop/rspec/env_assignment.rb
+++ b/rubocop/cop/rspec/env_assignment.rb
@@ -16,7 +16,9 @@ module RuboCop
# before do
# stub_env('FOO', 'bar')
# end
- class EnvAssignment < RuboCop::Cop::Cop
+ class EnvAssignment < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
MESSAGE = "Don't assign to ENV, use `stub_env` instead."
def_node_search :env_assignment?, <<~PATTERN
@@ -28,11 +30,7 @@ module RuboCop
def on_send(node)
return unless env_assignment?(node)
- add_offense(node, location: :expression, message: MESSAGE)
- end
-
- def autocorrect(node)
- lambda do |corrector|
+ add_offense(node, message: MESSAGE) do |corrector|
corrector.replace(node.loc.expression, stub_env(env_key(node), env_value(node)))
end
end
diff --git a/rubocop/cop/rspec/expect_gitlab_tracking.rb b/rubocop/cop/rspec/expect_gitlab_tracking.rb
index e3f790f851c..4f92980baa4 100644
--- a/rubocop/cop/rspec/expect_gitlab_tracking.rb
+++ b/rubocop/cop/rspec/expect_gitlab_tracking.rb
@@ -29,7 +29,7 @@ module RuboCop
# it 'does not expect a snowplow event', :snowplow do
# expect_no_snowplow_event
# end
- class ExpectGitlabTracking < RuboCop::Cop::Cop
+ class ExpectGitlabTracking < RuboCop::Cop::Base
MSG = 'Do not expect directly on `Gitlab::Tracking#event`, add the `snowplow` annotation and use ' \
'`expect_snowplow_event` instead. ' \
'See https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#test-snowplow-events'
diff --git a/rubocop/cop/rspec/factories_in_migration_specs.rb b/rubocop/cop/rspec/factories_in_migration_specs.rb
index f29bbf68cdc..6dde3d4524c 100644
--- a/rubocop/cop/rspec/factories_in_migration_specs.rb
+++ b/rubocop/cop/rspec/factories_in_migration_specs.rb
@@ -13,7 +13,7 @@ module RuboCop
# # good
# let(:users) { table(:users) }
# let(:user) { users.create!(name: 'User 1', username: 'user1') }
- class FactoriesInMigrationSpecs < RuboCop::Cop::Cop
+ class FactoriesInMigrationSpecs < RuboCop::Cop::Base
MESSAGE = "Don't use FactoryBot.%s in migration specs, use `table` instead."
FORBIDDEN_METHODS = %i[build build_list create create_list attributes_for].freeze
@@ -29,7 +29,7 @@ module RuboCop
method = node.children[1]
- add_offense(node, location: :expression, message: MESSAGE % method)
+ add_offense(node, message: MESSAGE % method)
end
end
end
diff --git a/rubocop/cop/rspec/factory_bot/inline_association.rb b/rubocop/cop/rspec/factory_bot/inline_association.rb
index 1c2b8b55b46..ccc6364fb73 100644
--- a/rubocop/cop/rspec/factory_bot/inline_association.rb
+++ b/rubocop/cop/rspec/factory_bot/inline_association.rb
@@ -43,7 +43,9 @@ module RuboCop
#
# creator_id { create(:user).id }
#
- class InlineAssociation < RuboCop::Cop::Cop
+ class InlineAssociation < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
MSG = 'Prefer inline `association` over `%{type}`. ' \
'See https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#factories'
@@ -81,11 +83,7 @@ module RuboCop
return if chained_call?(node.parent)
return unless inside_assocation_definition?(node)
- add_offense(node, message: format(MSG, type: type))
- end
-
- def autocorrect(node)
- lambda do |corrector|
+ add_offense(node, message: format(MSG, type: type)) do |corrector|
receiver, type = create_or_build(node)
receiver = "#{receiver.source}." if receiver
expression = "#{receiver}#{type}"
diff --git a/rubocop/cop/rspec/have_gitlab_http_status.rb b/rubocop/cop/rspec/have_gitlab_http_status.rb
index d61fb9f2368..86ece72b4f5 100644
--- a/rubocop/cop/rspec/have_gitlab_http_status.rb
+++ b/rubocop/cop/rspec/have_gitlab_http_status.rb
@@ -22,7 +22,9 @@ module RuboCop
# expect(response).to have_gitlab_http_status(:ok)
# expect(response).not_to have_gitlab_http_status(:ok)
#
- class HaveGitlabHttpStatus < RuboCop::Cop::Cop
+ class HaveGitlabHttpStatus < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
CODE_TO_SYMBOL = Rack::Utils::SYMBOL_TO_STATUS_CODE.invert
MSG_MATCHER_NAME =
@@ -66,13 +68,6 @@ module RuboCop
offense_for_matcher(node) || offense_for_response_status(node)
end
- def autocorrect(node)
- lambda do |corrector|
- replacement = replace_matcher(node) || replace_response_status(node)
- corrector.replace(node.source_range, replacement)
- end
- end
-
private
def offense_for_matcher(node)
@@ -85,13 +80,20 @@ module RuboCop
return if offenses.empty?
- add_offense(node, message: message_for(*offenses))
+ add_offense(node, message: message_for(*offenses), &corrector(node))
end
def offense_for_response_status(node)
return unless response_status_eq?(node)
- add_offense(node, message: message_for(MSG_RESPONSE_STATUS))
+ add_offense(node, message: message_for(MSG_RESPONSE_STATUS), &corrector(node))
+ end
+
+ def corrector(node)
+ lambda do |corrector|
+ replacement = replace_matcher(node) || replace_response_status(node)
+ corrector.replace(node.source_range, replacement)
+ end
end
def replace_matcher(node)
diff --git a/rubocop/cop/rspec/httparty_basic_auth.rb b/rubocop/cop/rspec/httparty_basic_auth.rb
index c6b52ac9781..1e0f7ae7af0 100644
--- a/rubocop/cop/rspec/httparty_basic_auth.rb
+++ b/rubocop/cop/rspec/httparty_basic_auth.rb
@@ -12,7 +12,9 @@ module RuboCop
#
# # good
# HTTParty.get(url, basic_auth: { username: 'foo' })
- class HTTPartyBasicAuth < RuboCop::Cop::Cop
+ class HTTPartyBasicAuth < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
MESSAGE = "`basic_auth: { user: ... }` does not work - replace `user:` with `username:`"
RESTRICT_ON_SEND = %i(get put post delete).freeze
@@ -35,12 +37,8 @@ module RuboCop
def on_send(node)
return unless m = httparty_basic_auth?(node)
- add_offense(m, location: :expression, message: MESSAGE)
- end
-
- def autocorrect(node)
- lambda do |corrector|
- corrector.replace(node.loc.expression, 'username')
+ add_offense(m, message: MESSAGE) do |corrector|
+ corrector.replace(m, 'username')
end
end
end
diff --git a/rubocop/cop/rspec/modify_sidekiq_middleware.rb b/rubocop/cop/rspec/modify_sidekiq_middleware.rb
index c38f074eb3a..78e3ba223b0 100644
--- a/rubocop/cop/rspec/modify_sidekiq_middleware.rb
+++ b/rubocop/cop/rspec/modify_sidekiq_middleware.rb
@@ -20,7 +20,9 @@ module RuboCop
# end
#
#
- class ModifySidekiqMiddleware < RuboCop::Cop::Cop
+ class ModifySidekiqMiddleware < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
MSG = <<~MSG
Don't modify global sidekiq middleware, use the `#with_sidekiq_server_middleware`
helper instead
@@ -35,11 +37,7 @@ module RuboCop
def on_send(node)
return unless modifies_sidekiq_middleware?(node)
- add_offense(node, location: :expression)
- end
-
- def autocorrect(node)
- -> (corrector) do
+ add_offense(node) do |corrector|
corrector.replace(node.loc.expression,
'with_sidekiq_server_middleware')
end
diff --git a/rubocop/cop/rspec/timecop_freeze.rb b/rubocop/cop/rspec/timecop_freeze.rb
index 508b5df7c7f..70e37ecfa55 100644
--- a/rubocop/cop/rspec/timecop_freeze.rb
+++ b/rubocop/cop/rspec/timecop_freeze.rb
@@ -13,7 +13,9 @@ module RuboCop
# # good
# freeze_time(Time.current) { example.run }
#
- class TimecopFreeze < RuboCop::Cop::Cop
+ 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.'
@@ -25,11 +27,7 @@ module RuboCop
def on_send(node)
return unless timecop_freeze?(node)
- add_offense(node, location: :expression, message: MESSAGE)
- end
-
- def autocorrect(node)
- -> (corrector) do
+ 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
diff --git a/rubocop/cop/rspec/timecop_travel.rb b/rubocop/cop/rspec/timecop_travel.rb
index e5416953af7..586567fa0cd 100644
--- a/rubocop/cop/rspec/timecop_travel.rb
+++ b/rubocop/cop/rspec/timecop_travel.rb
@@ -13,7 +13,9 @@ module RuboCop
# # good
# travel_to(1.day.ago) { create(:issue) }
#
- class TimecopTravel < RuboCop::Cop::Cop
+ 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.'
@@ -25,11 +27,7 @@ module RuboCop
def on_send(node)
return unless timecop_travel?(node)
- add_offense(node, location: :expression, message: MESSAGE)
- end
-
- def autocorrect(node)
- -> (corrector) do
+ 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
diff --git a/rubocop/cop/rspec/web_mock_enable.rb b/rubocop/cop/rspec/web_mock_enable.rb
index bcf7f95dbbd..0bef16a16b0 100644
--- a/rubocop/cop/rspec/web_mock_enable.rb
+++ b/rubocop/cop/rspec/web_mock_enable.rb
@@ -3,7 +3,9 @@
module RuboCop
module Cop
module RSpec
- class WebMockEnable < RuboCop::Cop::Cop
+ class WebMockEnable < RuboCop::Cop::Base
+ extend RuboCop::Cop::AutoCorrector
+
# This cop checks for `WebMock.disable_net_connect!` usage in specs and
# replaces it with `webmock_enable!`
#
@@ -24,13 +26,9 @@ module RuboCop
def on_send(node)
if webmock_disable_net_connect?(node)
- add_offense(node, location: :expression, message: MESSAGE)
- end
- end
-
- def autocorrect(node)
- lambda do |corrector|
- corrector.replace(node, 'webmock_enable!')
+ add_offense(node, message: MESSAGE) do |corrector|
+ corrector.replace(node, 'webmock_enable!')
+ end
end
end
end
diff --git a/rubocop/cop/ruby_interpolation_in_translation.rb b/rubocop/cop/ruby_interpolation_in_translation.rb
index c431b4a1977..fec550bf7c6 100644
--- a/rubocop/cop/ruby_interpolation_in_translation.rb
+++ b/rubocop/cop/ruby_interpolation_in_translation.rb
@@ -2,7 +2,7 @@
module RuboCop
module Cop
- class RubyInterpolationInTranslation < RuboCop::Cop::Cop
+ class RubyInterpolationInTranslation < RuboCop::Cop::Base
MSG = "Don't use ruby interpolation \#{} inside translated strings, instead use \%{}"
TRANSLATION_METHODS = ':_ :s_ :N_ :n_'
diff --git a/rubocop/cop/safe_params.rb b/rubocop/cop/safe_params.rb
index 2720732c161..78c40ee548e 100644
--- a/rubocop/cop/safe_params.rb
+++ b/rubocop/cop/safe_params.rb
@@ -2,7 +2,7 @@
module RuboCop
module Cop
- class SafeParams < RuboCop::Cop::Cop
+ class SafeParams < RuboCop::Cop::Base
MSG = 'Use `safe_params` instead of `params` in url_for.'
METHOD_NAME_PATTERN = :url_for
@@ -11,7 +11,7 @@ module RuboCop
def on_send(node)
return unless method_name(node) == METHOD_NAME_PATTERN
- add_offense(node, location: :expression) unless safe_params?(node)
+ add_offense(node) unless safe_params?(node)
end
private
diff --git a/rubocop/cop/scalability/bulk_perform_with_context.rb b/rubocop/cop/scalability/bulk_perform_with_context.rb
index bb944b2ad62..a31c20d149b 100644
--- a/rubocop/cop/scalability/bulk_perform_with_context.rb
+++ b/rubocop/cop/scalability/bulk_perform_with_context.rb
@@ -6,7 +6,7 @@ require_relative '../../code_reuse_helpers'
module RuboCop
module Cop
module Scalability
- class BulkPerformWithContext < RuboCop::Cop::Cop
+ class BulkPerformWithContext < RuboCop::Cop::Base
include RuboCop::MigrationHelpers
include RuboCop::CodeReuseHelpers
@@ -34,7 +34,7 @@ module RuboCop
return unless schedules_in_batch_without_context?(node)
return if scheduled_for_background_migration?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
private
diff --git a/rubocop/cop/scalability/cron_worker_context.rb b/rubocop/cop/scalability/cron_worker_context.rb
index 3cc0d42d7bc..ae8b0b328e2 100644
--- a/rubocop/cop/scalability/cron_worker_context.rb
+++ b/rubocop/cop/scalability/cron_worker_context.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
module Scalability
- class CronWorkerContext < RuboCop::Cop::Cop
+ class CronWorkerContext < RuboCop::Cop::Base
MSG = <<~MSG
Manually define an ApplicationContext for cronjob-workers. The context
is required to add metadata to our logs.
@@ -31,7 +31,7 @@ module RuboCop
return if defines_contexts?(node.parent)
return if schedules_with_batch_context?(node.parent)
- add_offense(node.arguments.first, location: :expression)
+ add_offense(node.arguments.first)
end
end
end
diff --git a/rubocop/cop/scalability/file_uploads.rb b/rubocop/cop/scalability/file_uploads.rb
index 83017217e32..3ccb9110e79 100644
--- a/rubocop/cop/scalability/file_uploads.rb
+++ b/rubocop/cop/scalability/file_uploads.rb
@@ -25,7 +25,7 @@ module RuboCop
# optional :file, type: ::API::Validations::Types::WorkhorseFile
# end
#
- class FileUploads < RuboCop::Cop::Cop
+ class FileUploads < RuboCop::Cop::Base
MSG = 'Do not upload files without workhorse acceleration. Please refer to https://docs.gitlab.com/ee/development/uploads.html'
def_node_search :file_type_params?, <<~PATTERN
@@ -43,7 +43,7 @@ module RuboCop
def on_send(node)
return unless be_file_param_usage?(node)
- add_offense(find_file_param(node), location: :expression)
+ add_offense(find_file_param(node))
end
private
diff --git a/rubocop/cop/scalability/idempotent_worker.rb b/rubocop/cop/scalability/idempotent_worker.rb
index 7abde54ce7e..88b5d796def 100644
--- a/rubocop/cop/scalability/idempotent_worker.rb
+++ b/rubocop/cop/scalability/idempotent_worker.rb
@@ -23,7 +23,7 @@ module RuboCop
# end
# end
#
- class IdempotentWorker < RuboCop::Cop::Cop
+ class IdempotentWorker < RuboCop::Cop::Base
include CodeReuseHelpers
HELP_LINK = 'https://github.com/mperham/sidekiq/wiki/Best-Practices#2-make-your-job-idempotent-and-transactional'
@@ -51,7 +51,7 @@ module RuboCop
return unless in_worker?(node)
return if idempotent?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
end
end
diff --git a/rubocop/cop/sidekiq_load_balancing/worker_data_consistency.rb b/rubocop/cop/sidekiq_load_balancing/worker_data_consistency.rb
index 7a2e7ee00b4..34c3767ad11 100644
--- a/rubocop/cop/sidekiq_load_balancing/worker_data_consistency.rb
+++ b/rubocop/cop/sidekiq_load_balancing/worker_data_consistency.rb
@@ -23,7 +23,7 @@ module RuboCop
# end
# end
#
- class WorkerDataConsistency < RuboCop::Cop::Cop
+ class WorkerDataConsistency < RuboCop::Cop::Base
include CodeReuseHelpers
HELP_LINK = 'https://docs.gitlab.com/ee/development/sidekiq_style_guide.html#job-data-consistency-strategies'
@@ -57,7 +57,7 @@ module RuboCop
return unless in_worker?(node) && application_worker?(node)
return if data_consistency_defined?(node)
- add_offense(node, location: :expression)
+ add_offense(node)
end
end
end
diff --git a/rubocop/cop/sidekiq_options_queue.rb b/rubocop/cop/sidekiq_options_queue.rb
index 2574a229ec2..3eee59e69fd 100644
--- a/rubocop/cop/sidekiq_options_queue.rb
+++ b/rubocop/cop/sidekiq_options_queue.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
# Cop that prevents manually setting a queue in Sidekiq workers.
- class SidekiqOptionsQueue < RuboCop::Cop::Cop
+ class SidekiqOptionsQueue < RuboCop::Cop::Base
MSG = 'Do not manually set a queue; `ApplicationWorker` sets one automatically.'
def_node_matcher :sidekiq_options?, <<~PATTERN
@@ -16,7 +16,7 @@ module RuboCop
node.arguments.first.each_node(:pair) do |pair|
key_name = pair.key.children[0]
- add_offense(pair, location: :expression) if key_name == :queue
+ add_offense(pair) if key_name == :queue
end
end
end
diff --git a/rubocop/cop/static_translation_definition.rb b/rubocop/cop/static_translation_definition.rb
index 121b0c18770..aea4dd6ae34 100644
--- a/rubocop/cop/static_translation_definition.rb
+++ b/rubocop/cop/static_translation_definition.rb
@@ -51,7 +51,7 @@ module RuboCop
# end
# end
#
- class StaticTranslationDefinition < RuboCop::Cop::Cop
+ class StaticTranslationDefinition < RuboCop::Cop::Base
MSG = <<~TEXT.tr("\n", ' ')
Translation is defined in static scope.
Keep translations dynamic. See https://docs.gitlab.com/ee/development/i18n/externalization.html#keep-translations-dynamic
diff --git a/rubocop/cop/usage_data/distinct_count_by_large_foreign_key.rb b/rubocop/cop/usage_data/distinct_count_by_large_foreign_key.rb
index 3aad089d961..a083318288b 100644
--- a/rubocop/cop/usage_data/distinct_count_by_large_foreign_key.rb
+++ b/rubocop/cop/usage_data/distinct_count_by_large_foreign_key.rb
@@ -12,7 +12,7 @@ module RuboCop
# # bad because pipeline_id points to a large table
# distinct_count(Ci::Build, :commit_id)
#
- class DistinctCountByLargeForeignKey < RuboCop::Cop::Cop
+ class DistinctCountByLargeForeignKey < RuboCop::Cop::Base
MSG = 'Avoid doing `%s` on foreign keys for large tables having above 100 million rows.'
def_node_matcher :distinct_count?, <<-PATTERN
@@ -25,7 +25,7 @@ module RuboCop
next if batch_set_to_false?(method_arguments[2])
next if allowed_foreign_key?(method_arguments[1])
- add_offense(node, location: :selector, message: format(MSG, method_name))
+ add_offense(node.loc.selector, message: format(MSG, method_name))
end
end
diff --git a/rubocop/cop/usage_data/histogram_with_large_table.rb b/rubocop/cop/usage_data/histogram_with_large_table.rb
index 961773df55c..35ec568792c 100644
--- a/rubocop/cop/usage_data/histogram_with_large_table.rb
+++ b/rubocop/cop/usage_data/histogram_with_large_table.rb
@@ -11,7 +11,7 @@ module RuboCop
# # bad
# histogram(Issue, buckets: 1..100)
# histogram(User.active, buckets: 1..100)
- class HistogramWithLargeTable < RuboCop::Cop::Cop
+ class HistogramWithLargeTable < RuboCop::Cop::Base
MSG = 'Avoid histogram method on %{model_name}'
# Match one level const as Issue, Gitlab
@@ -38,9 +38,9 @@ module RuboCop
class_name = two_level_matches ? two_level_matches.join('::').to_sym : one_level_matches
- if large_table?(class_name)
- add_offense(node, location: :expression, message: format(MSG, model_name: class_name))
- end
+ return unless large_table?(class_name)
+
+ add_offense(node, message: format(MSG, model_name: class_name))
end
private
diff --git a/rubocop/cop/usage_data/instrumentation_superclass.rb b/rubocop/cop/usage_data/instrumentation_superclass.rb
index 2ff2ed47a23..601f2340582 100644
--- a/rubocop/cop/usage_data/instrumentation_superclass.rb
+++ b/rubocop/cop/usage_data/instrumentation_superclass.rb
@@ -16,7 +16,7 @@ module RuboCop
# class CountIssues < BaseMetric
# # ...
# end
- class InstrumentationSuperclass < RuboCop::Cop::Cop
+ class InstrumentationSuperclass < RuboCop::Cop::Base
MSG = "Instrumentation classes should subclass one of the following: %{allowed_classes}."
BASE_PATTERN = "(const nil? !#allowed_class?)"
diff --git a/rubocop/cop/usage_data/large_table.rb b/rubocop/cop/usage_data/large_table.rb
index d9d44f74d26..7d50eebaa83 100644
--- a/rubocop/cop/usage_data/large_table.rb
+++ b/rubocop/cop/usage_data/large_table.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
module UsageData
- class LargeTable < RuboCop::Cop::Cop
+ class LargeTable < RuboCop::Cop::Base
# This cop checks that batch count and distinct_count are used in usage_data.rb files in metrics based on ActiveRecord models.
#
# @example
@@ -56,7 +56,7 @@ module RuboCop
counters_used = node.ancestors.any? { |ancestor| allowed_method?(ancestor) }
unless counters_used
- add_offense(node, location: :expression, message: format(MSG, count_methods: count_methods.join(', '), class_name: class_name))
+ add_offense(node, message: format(MSG, count_methods: count_methods.join(', '), class_name: class_name))
end
end
diff --git a/rubocop/cop/user_admin.rb b/rubocop/cop/user_admin.rb
index 3ba0e770ec1..643e620666b 100644
--- a/rubocop/cop/user_admin.rb
+++ b/rubocop/cop/user_admin.rb
@@ -3,7 +3,7 @@
module RuboCop
module Cop
# Cop that rejects the usage of `User#admin?`
- class UserAdmin < RuboCop::Cop::Cop
+ class UserAdmin < RuboCop::Cop::Base
MSG = 'Direct calls to `User#admin?` to determine admin status should be ' \
'avoided as they will not take into account the policies framework ' \
'and will ignore Admin Mode if enabled. Please use a policy check ' \
@@ -26,7 +26,7 @@ module RuboCop
def on_handler(node)
return unless admin_call?(node)
- add_offense(node, location: :selector)
+ add_offense(node.loc.selector)
end
end
end
diff --git a/rubocop/cop_todo.rb b/rubocop/cop_todo.rb
index 42e2f9fbe13..a36afc08673 100644
--- a/rubocop/cop_todo.rb
+++ b/rubocop/cop_todo.rb
@@ -1,8 +1,10 @@
# frozen_string_literal: true
+require_relative 'formatter/graceful_formatter'
+
module RuboCop
class CopTodo
- attr_accessor :previously_disabled
+ attr_accessor :previously_disabled, :grace_period
attr_reader :cop_name, :files, :offense_count
@@ -12,6 +14,7 @@ module RuboCop
@offense_count = 0
@cop_class = self.class.find_cop_by_name(cop_name)
@previously_disabled = false
+ @grace_period = false
end
def record(file, offense_count)
@@ -35,6 +38,7 @@ module RuboCop
yaml << ' Enabled: false'
end
+ yaml << " #{RuboCop::Formatter::GracefulFormatter.grace_period_key_value}" if grace_period
yaml << ' Exclude:'
yaml.concat files.sort.map { |file| " - '#{file}'" }
yaml << ''
diff --git a/rubocop/formatter/graceful_formatter.rb b/rubocop/formatter/graceful_formatter.rb
new file mode 100644
index 00000000000..b5db7c6c192
--- /dev/null
+++ b/rubocop/formatter/graceful_formatter.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+require 'rubocop'
+
+module RuboCop
+ module Formatter
+ class GracefulFormatter < ::RuboCop::Formatter::ProgressFormatter
+ CONFIG_DETAILS_KEY = 'Details'
+ CONFIG_DETAILS_VALUE = 'grace period'
+
+ class << self
+ attr_accessor :active_offenses
+ end
+
+ def started(...)
+ super
+
+ self.class.active_offenses = 0
+
+ @silenced_offenses_for_files = {}
+ @config = RuboCop::ConfigStore.new.for_pwd
+ end
+
+ def file_finished(file, offenses)
+ silenced_offenses, active_offenses = offenses.partition { silenced?(_1) }
+
+ @silenced_offenses_for_files[file] = silenced_offenses if silenced_offenses.any?
+
+ super(file, active_offenses)
+ end
+
+ def finished(inspected_files)
+ # See the note below why are using this ivar in the first place.
+ unless defined?(@total_offense_count)
+ raise <<~MESSAGE
+ RuboCop has changed its internals and the instance variable
+ `@total_offense_count` is no longer defined but we were relying on it.
+
+ Please change the implementation.
+
+ See https://github.com/rubocop/rubocop/blob/65a757b0f/lib/rubocop/formatter/simple_text_formatter.rb#L24
+ MESSAGE
+ end
+
+ super
+
+ # Internally, RuboCop has no notion of "silenced offenses". We cannot
+ # override this meaning in a formatter that's why we track what we
+ # consider to be an active offense.
+ # This is needed for `adjusted_exit_status` method below.
+ self.class.active_offenses = @total_offense_count
+
+ report_silenced_offenses(inspected_files)
+ end
+
+ # We consider this run a success without any active offenses.
+ def self.adjusted_exit_status(status)
+ return status unless status == RuboCop::CLI::STATUS_OFFENSES
+ return RuboCop::CLI::STATUS_SUCCESS if active_offenses == 0
+
+ status
+ end
+
+ def self.grace_period?(cop_name, config)
+ details = config[CONFIG_DETAILS_KEY]
+ return false unless details
+ return true if details == CONFIG_DETAILS_VALUE
+
+ warn "#{cop_name}: Unhandled value #{details.inspect} for `Details` key."
+
+ false
+ end
+
+ def self.grace_period_key_value
+ "#{CONFIG_DETAILS_KEY}: #{CONFIG_DETAILS_VALUE}"
+ end
+
+ private
+
+ def silenced?(offense)
+ cop_config = @config.for_cop(offense.cop_name)
+
+ self.class.grace_period?(offense.cop_name, cop_config)
+ end
+
+ def report_silenced_offenses(inspected_files)
+ return if @silenced_offenses_for_files.empty?
+
+ output.puts
+ output.puts 'Silenced offenses:'
+ output.puts
+
+ @silenced_offenses_for_files.each do |file, offenses|
+ report_file(file, offenses)
+ end
+
+ silenced_offense_count = @silenced_offenses_for_files.values.sum(&:size)
+ silenced_text = colorize("#{silenced_offense_count} offenses", :yellow)
+
+ output.puts
+ output.puts "#{inspected_files.size} files inspected, #{silenced_text} silenced"
+ end
+
+ def report_file_as_mark(_offenses)
+ # Skip progress bar. No dots. No C/Ws.
+ end
+ end
+ end
+end
diff --git a/rubocop/formatter/todo_formatter.rb b/rubocop/formatter/todo_formatter.rb
index 789d0418f96..b1c6d1c1688 100644
--- a/rubocop/formatter/todo_formatter.rb
+++ b/rubocop/formatter/todo_formatter.rb
@@ -6,6 +6,7 @@ require 'yaml'
require_relative '../todo_dir'
require_relative '../cop_todo'
+require_relative '../formatter/graceful_formatter'
module RuboCop
module Formatter
@@ -47,6 +48,8 @@ module RuboCop
def finished(_inspected_files)
@todos.values.sort_by(&:cop_name).each do |todo|
todo.previously_disabled = previously_disabled?(todo)
+ todo.grace_period = grace_period?(todo)
+ validate_todo!(todo)
path = @todo_dir.write(todo.cop_name, todo.to_yaml)
output.puts "Written to #{relative_path(path)}\n"
@@ -79,16 +82,31 @@ module RuboCop
raise "Multiple configurations found for cops:\n#{list}\n"
end
- def previously_disabled?(todo)
+ def config_for(todo)
cop_name = todo.cop_name
- config = @config_old_todo_yml[cop_name] ||
- @config_inspect_todo_dir[cop_name] || {}
+ @config_old_todo_yml[cop_name] || @config_inspect_todo_dir[cop_name] || {}
+ end
+
+ def previously_disabled?(todo)
+ config = config_for(todo)
return false if config.empty?
config['Enabled'] == false
end
+ def grace_period?(todo)
+ config = config_for(todo)
+
+ GracefulFormatter.grace_period?(todo.cop_name, config)
+ end
+
+ def validate_todo!(todo)
+ return unless todo.previously_disabled && todo.grace_period
+
+ raise "#{todo.cop_name}: Cop must be enabled to use `#{GracefulFormatter.grace_period_key_value}`."
+ end
+
def load_config_inspect_todo_dir
@todo_dir.list_inspect.each_with_object({}) do |path, combined|
config = YAML.load_file(path)
diff --git a/scripts/build_assets_image b/scripts/build_assets_image
index 60bd9190b74..8aa6526061a 100755
--- a/scripts/build_assets_image
+++ b/scripts/build_assets_image
@@ -19,12 +19,8 @@ cp -r public/assets assets_container.build/public/
cp Dockerfile.assets assets_container.build/
COMMIT_REF_SLUG_DESTINATION=${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_SLUG}
-# Use CI_MERGE_REQUEST_SOURCE_BRANCH_SHA (MR HEAD commit) so that the image is in sync with Omnibus/CNG images.
-# Background: Due to the fact that we cannot retrieve the Merged Commit in the downstream omnibus/CNG pipelines,
-# we're building the Omnibus/CNG images for the MR HEAD commit.
-# In turn, the assets image also needs to be built from the MR HEAD commit, so that everything is build from the same commit.
-# For non-MR commits, we fallback to $CI_COMMIT_SHA.
-COMMIT_SHA_DESTINATION=${ASSETS_IMAGE_PATH}:${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA:-$CI_COMMIT_SHA}
+
+COMMIT_SHA_DESTINATION=${ASSETS_IMAGE_PATH}:${CI_COMMIT_SHA}
COMMIT_REF_NAME_DESTINATION=${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_NAME}
DESTINATIONS="--destination=$COMMIT_REF_SLUG_DESTINATION --destination=$COMMIT_SHA_DESTINATION"
diff --git a/scripts/bundle_size_review b/scripts/bundle_size_review
new file mode 100755
index 00000000000..5067c3c3f2c
--- /dev/null
+++ b/scripts/bundle_size_review
@@ -0,0 +1,68 @@
+#!/usr/bin/env bash
+set -euo pipefail
+IFS=$'\n\t'
+
+#
+# # How does this work in general?
+#
+# 1. We run webpack in a production like mode and enable the BundleAnalyzerPlugin
+# 2. The Plugin builds a index.html for human consumption _and_ a stats.json
+# 3. This script creates a smaller analysis.json from the gargantuan stats.json
+# 4. In Merge Requests:
+# - compare that smaller to analysis.json to the one from the base commit on master
+# - report the comparison results via danger
+
+source scripts/utils.sh
+
+# For now we only want bundle-size-review to run in CI
+# Maybe we could create a "local mode"
+if [[ -z "${CI:-}" ]]; then
+ echo 'Not running in a CI context, skipping bundle analysis'
+ exit "0"
+fi
+
+# Get the _current_ commit sha
+if [[ -z "${CI_MERGE_REQUEST_IID:-}" ]]; then
+ echo 'Not in a merge request, setting COMMIT_SHA to $CI_COMMIT_SHA'
+ COMMIT_SHA="${CI_COMMIT_SHA}"
+else
+ echo 'In a merge request, setting COMMIT_SHA to $CI_MERGE_REQUEST_SOURCE_BRANCH_SHA'
+ COMMIT_SHA="${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}"
+fi
+
+# Create output directory
+mkdir -p bundle-size-review
+
+# Running webpack
+export WEBPACK_REPORT="true"
+run_timed_command "yarn run webpack-prod > bundle-size-review/webpack-output.log"
+
+# Copy results from stats plugin
+cp webpack-report/index.html bundle-size-review/bundle-report.html
+
+# Run comparison in danger
+if [[ -z "${DANGER_GITLAB_API_TOKEN:-}" ]]; then
+ echo 'No Danger token available, skipping bundle analysis'
+ exit "0"
+fi
+
+# TODO: Make this a dependency of GitLab itself after a proper release
+yarn global add https://gitlab.com/gitlab-org/frontend/playground/webpack-memory-metrics.git
+
+# Create smaller analysis.json
+run_timed_command "webpack-entry-point-analyser --from-file ./webpack-report/stats.json --json ./bundle-size-review/analysis.json --sha ${COMMIT_SHA}"
+rm -rf webpack-report
+
+if [[ -z "${CI_MERGE_REQUEST_IID:-}" ]]; then
+ echo 'Not in a merge request, skipping comparison'
+ exit "0"
+fi
+
+# Run comparison
+run_timed_command "webpack-compare-reports --job ${CI_JOB_ID} --to-file ./bundle-size-review/analysis.json --html ./bundle-size-review/comparison.html --markdown ./bundle-size-review/comparison.md"
+
+# Execute danger
+danger_id=$(echo -n "${DANGER_GITLAB_API_TOKEN}" | md5sum | awk '{print $1}' | cut -c5-10)
+run_timed_command "danger --dangerfile=danger/Dangerfile-bundle_size --fail-on-errors=true --verbose --danger_id=bundle-size-review-${danger_id}"
+
+exit "0"
diff --git a/scripts/checkout-mr-source-sha b/scripts/checkout-mr-source-sha
deleted file mode 100755
index 962e3f1348d..00000000000
--- a/scripts/checkout-mr-source-sha
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-
-if [ -n "$CI_MERGE_REQUEST_SOURCE_BRANCH_SHA" ]; then
- echo "Checking out \$CI_MERGE_REQUEST_SOURCE_BRANCH_SHA ($CI_MERGE_REQUEST_SOURCE_BRANCH_SHA) instead of \$CI_COMMIT_SHA (merge result commit $CI_COMMIT_SHA) so that code is in sync with gitlab images built upstream."
- echo "See https://docs.gitlab.com/ee/development/testing_guide/end_to_end/index.html#with-pipeline-for-merged-results for more details."
- git checkout -f ${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}
-fi
diff --git a/scripts/determine-qa-tests b/scripts/determine-qa-tests
deleted file mode 100755
index b1e9d8e9312..00000000000
--- a/scripts/determine-qa-tests
+++ /dev/null
@@ -1,106 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-require 'optparse'
-
-# This script returns end-to-end tests or test directories. The returned value is stored in QA_TESTS
-# variable for executing only those tests.
-
-class DetermineQATests # rubocop:disable Gitlab/NamespacedClass
- def initialize(options)
- @changed_files = options.delete(:changed_files)
- @mr_labels = options.delete(:mr_labels) || []
- end
-
- def execute
- # If only e2e test files have changed, run only those tests
- qa_tests = if has_qa_spec_only_changes?
- changed_files
-
- # If only non qa files have changed, use the devops MR label to run the files in the related directory
- # However, if a feature flag file has changed, do not return any specific test/test directory
- elsif has_non_qa_only_changes? && mr_labels.any? && !has_dev_ops_feature_flag_changes?
- devops_stage = devops_stage_from_mr_labels
-
- qa_spec_directories_for_devops_stage(devops_stage) if devops_stage
- end
-
- trim_path(qa_tests).join(' ') if qa_tests
- end
-
- private
-
- attr_reader :changed_files, :mr_labels
-
- # Are the changed files only qa specs?
- #
- # @return [Boolean] whether the changes files are only qa specs
- def has_qa_spec_only_changes?
- changed_files.all? { |file_path| file_path =~ %r{^qa/qa/specs/features/} }
- end
-
- # Are the changed files only outside the qa directory?
- #
- # @return [Boolean] whether the changes files are outside of qa directory
- def has_non_qa_only_changes?
- changed_files.none? { |file_path| file_path =~ %r{^qa/} }
- end
-
- # Are the changed files for development and ops feature flags?
- #
- # @return [Boolean] whether the changes files are for development and ops feature flags
- def has_dev_ops_feature_flag_changes?
- changed_files.any? { |file_path| file_path =~ %r{/feature_flags/(development|ops)/.*\.yml} }
- end
-
- # Remove the leading `qa/` from the file or directory paths
- #
- # @param [Array] paths Array of file or directory paths
- # @return [Array] Array of files or directories with the first occurance of `qa/` removed
- def trim_path(paths)
- paths.map { |path| path.delete_prefix("qa/") }
- end
-
- # Extract devops stage from MR labels
- #
- # @return [String] a devops stage
- def devops_stage_from_mr_labels
- mr_labels.find { |label| label =~ /^devops::/ }&.delete_prefix('devops::')
- end
-
- # Get qa spec directories for devops stage
- #
- # @param [String] devops_stage a devops stage
- # @return [Array] qa spec directories
- def qa_spec_directories_for_devops_stage(devops_stage)
- Dir.glob("qa/qa/specs/**/*/").select { |dir| dir =~ %r{\d+_#{devops_stage}/$} }
- end
-end
-
-if $0 == __FILE__
- options = {}
-
- OptionParser.new do |opts|
- opts.on("-f", "--files CHANGED_FILES_PATH", String,
- "A path to a file containing a list of changed files") do |value|
- changed_files_path = value
- abort("ERROR: The specified changed files path does not exist") unless File.exist?(changed_files_path)
-
- changed_files = File.read(changed_files_path).split(' ')
- abort("ERROR: There are no changed files") if changed_files.empty?
-
- options[:changed_files] = changed_files
- end
-
- opts.on("-l", "--labels MR_LABELS", String, "A comma separated list of MR labels") do |value|
- options[:mr_labels] = Array(value&.split(',')).compact
- end
-
- opts.on("-h", "--help", "Prints this help") do
- puts opts
- exit
- end
- end.parse!
-
- puts DetermineQATests.new(options).execute
-end
diff --git a/scripts/frontend/startup_css/constants.js b/scripts/frontend/startup_css/constants.js
index 10d60657e09..5143c04dc37 100644
--- a/scripts/frontend/startup_css/constants.js
+++ b/scripts/frontend/startup_css/constants.js
@@ -41,6 +41,7 @@ const ROOT_RAILS = IS_EE ? path.join(ROOT, 'ee') : ROOT;
const FIXTURES_FOLDER_NAME = IS_EE ? 'fixtures-ee' : 'fixtures';
const FIXTURES_ROOT = path.join(ROOT, 'tmp/tests/frontend', FIXTURES_FOLDER_NAME);
const PATH_SIGNIN_HTML = path.join(FIXTURES_ROOT, 'startup_css/sign-in.html');
+const PATH_SIGNIN_OLD_HTML = path.join(FIXTURES_ROOT, 'startup_css/sign-in-old.html');
const PATH_ASSETS = path.join(ROOT, 'tmp/startup_css_assets');
const PATH_STARTUP_SCSS = path.join(ROOT_RAILS, 'app/assets/stylesheets/startup');
@@ -80,7 +81,7 @@ const OUTPUTS = [
}),
{
outFile: 'startup-signin',
- htmlPaths: [PATH_SIGNIN_HTML],
+ htmlPaths: [PATH_SIGNIN_HTML, PATH_SIGNIN_OLD_HTML],
cssKeys: [APPLICATION_CSS_PREFIX, UTILITIES_CSS_PREFIX],
purgeOptions: {
safelist: {
diff --git a/scripts/generate-e2e-pipeline b/scripts/generate-e2e-pipeline
new file mode 100755
index 00000000000..f541ae6665c
--- /dev/null
+++ b/scripts/generate-e2e-pipeline
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+set -e
+
+# Script to generate e2e test child pipeline
+# This is required because environment variables that are generated dynamically are not picked up by rules in child pipelines
+
+source $ENV_FILE
+
+echo "Generating child pipeline yml definitions for review-app and package-and-test child pipelines"
+
+if [ "$QA_SKIP_ALL_TESTS" == "true" ]; then
+ skip_pipeline=".gitlab/ci/_skip.yml"
+
+ echo "Using ${skip_pipeline} due to QA_SKIP_ALL_TESTS set to 'true'"
+ cp $skip_pipeline "$OMNIBUS_PIPELINE_YML"
+ cp $skip_pipeline "$REVIEW_PIPELINE_YML"
+ exit
+fi
+
+variables=$(cat <<YML
+variables:
+ GITLAB_VERSION: "$(cat VERSION)"
+ COLORIZED_LOGS: "true"
+ QA_TESTS: "$QA_TESTS"
+ QA_FEATURE_FLAGS: "${QA_FEATURE_FLAGS}"
+ QA_FRAMEWORK_CHANGES: "${QA_FRAMEWORK_CHANGES:-false}"
+ QA_SUITES: "$QA_SUITES"
+YML
+)
+
+echo "Using .gitlab/ci/review-apps/main.gitlab-ci.yml and .gitlab/ci/package-and-test/main.gitlab-ci.yml"
+cp .gitlab/ci/review-apps/main.gitlab-ci.yml "$REVIEW_PIPELINE_YML"
+echo "$variables" >>"$REVIEW_PIPELINE_YML"
+cp .gitlab/ci/package-and-test/main.gitlab-ci.yml "$OMNIBUS_PIPELINE_YML"
+echo "$variables" >>"$OMNIBUS_PIPELINE_YML"
+
+echo "Successfully generated review-app and package-and-test pipeline with following variables section:"
+echo "$variables"
diff --git a/scripts/glfm/run-snapshot-tests.sh b/scripts/glfm/run-snapshot-tests.sh
index 59a7c8f06b0..6a66d8fbd9a 100755
--- a/scripts/glfm/run-snapshot-tests.sh
+++ b/scripts/glfm/run-snapshot-tests.sh
@@ -28,8 +28,12 @@ printf "\n${BBlue}Running frontend 'yarn jest spec/frontend/content_editor/markd
yarn jest spec/frontend/content_editor/markdown_snapshot_spec.js
printf "\n${BBlue}'yarn jest spec/frontend/content_editor/markdown_snapshot_spec.js' passed!${Color_Off}\n\n"
-printf "\n${BBlue}Running backend 'bundle exec rspec spec/requests/api/markdown_snapshot_spec.rb'...${Color_Off}\n\n"
+printf "\n${BBlue}Running CE backend 'bundle exec rspec spec/requests/api/markdown_snapshot_spec.rb'...${Color_Off}\n\n"
bundle exec rspec spec/requests/api/markdown_snapshot_spec.rb
printf "\n${BBlue}'bundle exec rspec spec/requests/api/markdown_snapshot_spec.rb' passed!${Color_Off}\n\n"
+printf "\n${BBlue}Running EE backend 'bundle exec rspec ee/spec/requests/api/markdown_snapshot_spec.rb'...${Color_Off}\n\n"
+bundle exec rspec ee/spec/requests/api/markdown_snapshot_spec.rb
+printf "\n${BBlue}'bundle exec rspec ee/spec/requests/api/markdown_snapshot_spec.rb' passed!${Color_Off}\n\n"
+
printf "\n✅✅✅ ${BGreen}All GLFM snapshot example tests passed successfully!${Color_Off} ✅✅✅\n"
diff --git a/scripts/lib/glfm/constants.rb b/scripts/lib/glfm/constants.rb
index 42977248c0d..e5790bbdd88 100644
--- a/scripts/lib/glfm/constants.rb
+++ b/scripts/lib/glfm/constants.rb
@@ -18,6 +18,9 @@ module Glfm
GLFM_INTRO_TXT_PATH = specification_input_glfm_path.join('glfm_intro.txt')
GLFM_EXAMPLES_TXT_PATH = specification_input_glfm_path.join('glfm_canonical_examples.txt')
GLFM_EXAMPLE_STATUS_YML_PATH = specification_input_glfm_path.join('glfm_example_status.yml')
+ GLFM_EXAMPLE_METADATA_YML_PATH =
+ specification_input_glfm_path.join('glfm_example_metadata.yml')
+ GLFM_EXAMPLE_NORMALIZATIONS_YML_PATH = specification_input_glfm_path.join('glfm_example_normalizations.yml')
GLFM_SPEC_TXT_PATH = specification_path.join('output/spec.txt')
# Example Snapshot (ES) files
@@ -28,15 +31,16 @@ module Glfm
ES_PROSEMIRROR_JSON_YML_PATH = File.join(es_fixtures_path, 'prosemirror_json.yml')
# Other constants used for processing files
- GLFM_SPEC_TXT_HEADER = <<~GLFM_SPEC_TXT_HEADER
+ GLFM_SPEC_TXT_HEADER = <<~MARKDOWN
---
title: GitLab Flavored Markdown (GLFM) Spec
version: alpha
...
- GLFM_SPEC_TXT_HEADER
+ MARKDOWN
INTRODUCTION_HEADER_LINE_TEXT = /\A# Introduction\Z/.freeze
END_TESTS_COMMENT_LINE_TEXT = /\A<!-- END TESTS -->\Z/.freeze
MARKDOWN_TEMPFILE_BASENAME = %w[MARKDOWN_TEMPFILE_ .yml].freeze
+ METADATA_TEMPFILE_BASENAME = %w[METADATA_TEMPFILE_ .yml].freeze
STATIC_HTML_TEMPFILE_BASENAME = %w[STATIC_HTML_TEMPFILE_ .yml].freeze
WYSIWYG_HTML_AND_JSON_TEMPFILE_BASENAME = %w[WYSIWYG_HTML_AND_JSON_TEMPFILE_ .yml].freeze
end
diff --git a/scripts/lib/glfm/parse_examples.rb b/scripts/lib/glfm/parse_examples.rb
index 14634bcfb3e..a15a6ecc47b 100644
--- a/scripts/lib/glfm/parse_examples.rb
+++ b/scripts/lib/glfm/parse_examples.rb
@@ -26,7 +26,7 @@ module Glfm
EXAMPLE_BACKTICKS_LENGTH = 32
EXAMPLE_BACKTICKS_STRING = '`' * EXAMPLE_BACKTICKS_LENGTH
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
def parse_examples(spec_txt_lines)
line_number = 0
start_line = 0
@@ -41,6 +41,7 @@ module Glfm
h1_regex = /\A# / # new logic compared to original Python code
h2_regex = /\A## / # new logic compared to original Python code
+ h3_regex = /\A### / # new logic compared to original Python code
header_regex = /\A#+ / # Added beginning of line anchor to original Python code
spec_txt_lines.each do |line|
@@ -102,12 +103,24 @@ module Glfm
# reset the headers array if we found a new H1
headers = [] if line =~ h1_regex
- # headers should be size 2 or less [<H1_headertext>, <H2_headertext>]
- # pop the last entry from the headers array if we are in an H2 and found a new H2
- headers.pop if headers.length == 2 && line =~ h2_regex
+ # headers should be size 3 or less [<H1_headertext>, <H2_headertext>, <H3_headertext>]
+
+ if headers.length == 1 && line =~ h3_regex
+ errmsg = "Error: The H3 '#{headertext}' may not be nested directly within the H1 '#{headers[0]}'. " \
+ " Add an H2 header before the H3 header."
+ raise errmsg
+ end
+
+ if (headers.length == 2 || headers.length == 3) && line =~ h2_regex
+ # drop the everything but first entry from the headers array if we are in an H2 and found a new H2
+ headers = [headers[0]]
+ elsif headers.length == 3 && line =~ h3_regex
+ # pop the last entry from the headers array if we are in an H3 and found a new H3
+ headers.pop
+ end
# push the new header text to the headers array
- headers << headertext if line =~ h1_regex || line =~ h2_regex
+ headers << headertext if line =~ h1_regex || line =~ h2_regex || line =~ h3_regex
else
# Else if we are in regular text...
@@ -119,7 +132,7 @@ module Glfm
# no-op - skips any other non-header regular text lines
end
end
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize
tests
end
diff --git a/scripts/lib/glfm/render_static_html.rb b/scripts/lib/glfm/render_static_html.rb
index a0bda05b41e..8d72aec7c3b 100644
--- a/scripts/lib/glfm/render_static_html.rb
+++ b/scripts/lib/glfm/render_static_html.rb
@@ -1,80 +1,73 @@
# frozen_string_literal: true
+require 'spec_helper'
require_relative 'constants'
require_relative 'shared'
# Purpose:
# - Reads a set of markdown examples from a hash which has been serialized to disk
-# - Converts each example to static HTML using the `markdown` helper
+# - Sets up the appropriate fixture data for the markdown examples
+# - Converts each example to static HTML using the appropriate API markdown endpoint
# - Writes the HTML for each example to a hash which is serialized to disk
#
-# It should be invoked via `rails runner` from the Rails root directory.
-# It is intended to be invoked from the `update_example_snapshots.rb` script class.
-module Glfm
- class RenderStaticHtml
- include Constants
- include Shared
-
- def process
- markdown_yml_path = ARGV[0]
- markdown_hash = YAML.load_file(markdown_yml_path)
+# Requirements:
+# The input and output files are specified via these environment variables:
+# - INPUT_MARKDOWN_YML_PATH
+# - OUTPUT_STATIC_HTML_TEMPFILE_PATH
+#
+# Although it is implemented as an RSpec test, it is not a unit test. We use
+# RSpec because that is the simplest environment in which we can use the
+# Factorybot factory methods to create persisted model objects with stable
+# and consistent data values, to ensure consistent example snapshot HTML
+# across various machines and environments. RSpec also makes it easy to invoke
+# 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.
+RSpec.describe 'Render Static HTML', :api, type: :request do # rubocop:disable RSpec/TopLevelDescribePath
+ include Glfm::Constants
+ include Glfm::Shared
- context = build_context
+ # noinspection RailsParamDefResolve (RubyMine can't find the shared context from this file location)
+ include_context 'with GLFM example snapshot fixtures'
- # NOTE: We COULD parallelize this loop like the Javascript WYSIWYG example generation does,
- # but it wouldn't save much time. Most of the time is spent loading the Rails environment
- # via `rails runner`. In initial testing, this loop only took ~7 seconds while the entire
- # script took ~20 seconds. Unfortunately, there's no easy way to execute
- # `Banzai.render_and_post_process` without using `rails runner`
- static_html_hash = markdown_hash.transform_values do |markdown|
- Banzai.render_and_post_process(markdown, context)
- end
+ it 'can create a project dependency graph using factories' do
+ markdown_hash = YAML.safe_load(File.open(ENV.fetch('INPUT_MARKDOWN_YML_PATH')), symbolize_names: true)
+ metadata_hash = YAML.safe_load(File.open(ENV.fetch('INPUT_METADATA_YML_PATH')), symbolize_names: true)
- static_html_tempfile_path = Dir::Tmpname.create(STATIC_HTML_TEMPFILE_BASENAME) do |path|
- tmpfile = File.open(path, 'w')
- YAML.dump(static_html_hash, tmpfile)
- tmpfile.close
- end
+ # NOTE: We cannot parallelize this loop like the Javascript WYSIWYG example generation does,
+ # because the rspec `post` API cannot be parallized (it is not thread-safe, it can't find
+ # the controller).
+ static_html_hash = markdown_hash.transform_values.with_index do |markdown, index|
+ name = markdown_hash.keys[index]
+ api_url = metadata_hash.dig(name, :api_request_override_path) || (api "/markdown")
- # Write the path to the output file to stdout
- print static_html_tempfile_path
- end
+ post api_url, params: { text: markdown, gfm: true }
+ # noinspection RubyResolve
+ expect(response).to be_successful
- private
+ returned_html_value =
+ begin
+ parsed_response = Gitlab::Json.parse(response.body, symbolize_names: true)
+ # Some responses have the HTML in the `html` key, others in the `body` key.
+ parsed_response[:body] || parsed_response[:html]
+ rescue JSON::ParserError
+ # if we got a parsing error, just return the raw response body for debugging purposes.
+ response.body
+ end
- def build_context
- user_username = 'glfm_user_username'
- user = User.find_by_username(user_username) ||
- User.create!(
- email: "glfm_user_email@example.com",
- name: "glfm_user_name",
- password: "glfm_user_password",
- username: user_username
- )
+ returned_html_value
+ end
- # Ensure that we never try to hit Gitaly, even if we
- # reload the project
- Project.define_method(:skip_disk_validation) do
- true
- end
+ write_output_file(static_html_hash)
+ end
- project_name = 'glfm_project_name'
- project = Project.find_by_name(project_name) ||
- Project.create!(
- creator: user,
- description: "glfm_project_description",
- name: project_name,
- namespace: user.namespace,
- path: 'glfm_project_path'
- )
+ private
- {
- only_path: false,
- current_user: user,
- project: project
- }
- end
+ def write_output_file(static_html_hash)
+ tmpfile = File.open(ENV.fetch('OUTPUT_STATIC_HTML_TEMPFILE_PATH'), 'w')
+ yaml_string = dump_yaml_with_formatting(static_html_hash)
+ write_file(tmpfile, yaml_string)
end
end
-
-Glfm::RenderStaticHtml.new.process
diff --git a/scripts/lib/glfm/shared.rb b/scripts/lib/glfm/shared.rb
index f11c66eb8be..b529d9ba94f 100644
--- a/scripts/lib/glfm/shared.rb
+++ b/scripts/lib/glfm/shared.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'fileutils'
require 'open3'
+require 'active_support/core_ext/hash/keys'
module Glfm
module Shared
@@ -39,5 +40,38 @@ module Glfm
warn(stdout_and_stderr_str)
raise
end
+
+ # Construct an AST so we can control YAML formatting for
+ # YAML block scalar literals and key quoting.
+ #
+ # Note that when Psych dumps the markdown to YAML, it will
+ # automatically use the default "clip" behavior of the Block Chomping Indicator (`|`)
+ # https://yaml.org/spec/1.2.2/#8112-block-chomping-indicator,
+ # when the markdown strings contain a trailing newline. The type of
+ # Block Chomping Indicator is automatically determined, you cannot specify it
+ # manually.
+ def dump_yaml_with_formatting(hash, literal_scalars: false)
+ stringified_keys_hash = hash.deep_stringify_keys
+ visitor = Psych::Visitors::YAMLTree.create
+ visitor << stringified_keys_hash
+ ast = visitor.tree
+
+ # Force all scalars to have literal formatting (using Block Chomping Indicator instead of quotes)
+ if literal_scalars
+ ast.grep(Psych::Nodes::Scalar).each do |node|
+ node.style = Psych::Nodes::Scalar::LITERAL
+ end
+ end
+
+ # Do not quote the keys
+ ast.grep(Psych::Nodes::Mapping).each do |node|
+ node.children.each_slice(2) do |k, _|
+ k.quoted = false
+ k.style = Psych::Nodes::Scalar::PLAIN
+ end
+ end
+
+ ast.to_yaml
+ end
end
end
diff --git a/scripts/lib/glfm/update_example_snapshots.rb b/scripts/lib/glfm/update_example_snapshots.rb
index d8d6cf3cdbc..7dc0d0f7c4b 100644
--- a/scripts/lib/glfm/update_example_snapshots.rb
+++ b/scripts/lib/glfm/update_example_snapshots.rb
@@ -29,8 +29,6 @@ module Glfm
def process(skip_static_and_wysiwyg: false)
output('Updating example snapshots...')
- setup_environment
-
output('(Skipping static HTML generation)') if skip_static_and_wysiwyg
output("Reading #{GLFM_SPEC_TXT_PATH}...")
@@ -49,19 +47,11 @@ module Glfm
private
- def setup_environment
- # Set 'GITLAB_TEST_FOOTNOTE_ID' in order to override random number generation in
- # Banzai::Filter::FootnoteFilter#random_number, and thus avoid the need to
- # perform normalization on the value. See:
- # https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#normalization
- ENV['GITLAB_TEST_FOOTNOTE_ID'] = '42'
- end
-
def add_example_names(all_examples)
# NOTE: This method and the parse_examples method assume:
# 1. Section 2 is the first section which contains examples
- # 2. Examples are always nested exactly 2 levels deep in an H2
- # 3. There may exist H3 headings with no examples (e.g. "Motivation" in the GLFM spec.txt)
+ # 2. Examples are always nested in an H2 or an H3, never directly in an H1
+ # 3. There may exist headings with no examples (e.g. "Motivation" in the GLFM spec.txt)
# 4. The Appendix doesn't ever contain any examples, so it doesn't show up
# in the H1 header count. So, even though due to the concatenation it appears before the
# GitLab examples sections, it doesn't result in their header counts being off by +1.
@@ -70,35 +60,49 @@ module Glfm
# GFM `spec_test.py` script (but it's NOT in the original CommonMark `spec_test.py`).
# 6. If a section contains ONLY disabled examples, the section numbering will still be
# incremented to match the rendered HTML specification section numbering.
- # 7. Every H2 must contain at least one example, but it is allowed that they are all disabled.
+ # 7. Every H2 or H3 must contain at least one example, but it is allowed that they are
+ # all disabled.
h1_count = 1 # examples start in H1 section 2; section 1 is the overview with no examples.
h2_count = 0
+ h3_count = 0
previous_h1 = ''
previous_h2 = ''
- index_within_h2 = 0
+ previous_h3 = ''
+ index_within_current_heading = 0
all_examples.each do |example|
headers = example[:headers]
if headers[0] != previous_h1
h1_count += 1
h2_count = 0
+ h3_count = 0
previous_h1 = headers[0]
end
if headers[1] != previous_h2
h2_count += 1
+ h3_count = 0
previous_h2 = headers[1]
- index_within_h2 = 0
+ index_within_current_heading = 0
end
- index_within_h2 += 1
+ if headers[2] && headers[2] != previous_h3
+ h3_count += 1
+ previous_h3 = headers[2]
+ index_within_current_heading = 0
+ end
+
+ index_within_current_heading += 1
# convert headers array to lowercase string with underscores, and double underscores between headers
formatted_headers_text = headers.join('__').tr('-', '_').tr(' ', '_').downcase
- hierarchy_level = "#{h1_count.to_s.rjust(2, '0')}_#{h2_count.to_s.rjust(2, '0')}"
- position_within_section = index_within_h2.to_s.rjust(3, '0')
+ hierarchy_level =
+ "#{h1_count.to_s.rjust(2, '0')}_" \
+ "#{h2_count.to_s.rjust(2, '0')}_" \
+ "#{h3_count.to_s.rjust(2, '0')}"
+ position_within_section = index_within_current_heading.to_s.rjust(3, '0')
name = "#{hierarchy_level}__#{formatted_headers_text}__#{position_within_section}"
converted_name = name.tr('(', '').tr(')', '') # remove any parens from the name
example[:name] = converted_name
@@ -111,7 +115,7 @@ module Glfm
def write_snapshot_example_files(all_examples, skip_static_and_wysiwyg:)
output("Reading #{GLFM_EXAMPLE_STATUS_YML_PATH}...")
- glfm_examples_statuses = YAML.safe_load(File.open(GLFM_EXAMPLE_STATUS_YML_PATH))
+ glfm_examples_statuses = YAML.safe_load(File.open(GLFM_EXAMPLE_STATUS_YML_PATH), symbolize_names: true)
validate_glfm_example_status_yml(glfm_examples_statuses)
write_examples_index_yml(all_examples)
@@ -123,9 +127,13 @@ module Glfm
return
end
- markdown_yml_tempfile_path = write_markdown_yml_tempfile
- static_html_hash = generate_static_html(markdown_yml_tempfile_path)
- wysiwyg_html_and_json_hash = generate_wysiwyg_html_and_json(markdown_yml_tempfile_path)
+ # NOTE: We pass the INPUT_MARKDOWN_YML_PATH and INPUT_METADATA_YML_PATH via
+ # environment variables to the static/wysiwyg HTML generation scripts. This is because they
+ # are implemented as subprocesses which invoke rspec/jest scripts, and rspec/jest do not make
+ # it straightforward to pass arguments via the command line.
+ ENV['INPUT_MARKDOWN_YML_PATH'], ENV['INPUT_METADATA_YML_PATH'] = copy_tempfiles_for_subprocesses
+ static_html_hash = generate_static_html
+ wysiwyg_html_and_json_hash = generate_wysiwyg_html_and_json
write_html_yml(all_examples, static_html_hash, wysiwyg_html_and_json_hash, glfm_examples_statuses)
@@ -135,8 +143,8 @@ module Glfm
def validate_glfm_example_status_yml(glfm_examples_statuses)
glfm_examples_statuses.each do |example_name, statuses|
next unless statuses &&
- statuses['skip_update_example_snapshots'] &&
- statuses.any? { |key, value| key.include?('skip_update_example_snapshot_') && !!value }
+ statuses[:skip_update_example_snapshots] &&
+ statuses.any? { |key, value| key.to_s.include?('skip_update_example_snapshot_') && !!value }
raise "Error: '#{example_name}' must not have any 'skip_update_example_snapshot_*' values specified " \
"if 'skip_update_example_snapshots' is truthy"
@@ -147,66 +155,90 @@ module Glfm
generate_and_write_for_all_examples(
all_examples, ES_EXAMPLES_INDEX_YML_PATH, literal_scalars: false
) do |example, hash|
- hash[example.fetch(:name)] = {
+ name = example.fetch(:name).to_sym
+ hash[name] = {
'spec_txt_example_position' => example.fetch(:example),
- 'source_specification' =>
- if example[:extensions].empty?
- 'commonmark'
- elsif example[:extensions].include?('gitlab')
- 'gitlab'
- else
- 'github'
- end
+ 'source_specification' => source_specification_for_extensions(example.fetch(:extensions))
}
end
end
+ def source_specification_for_extensions(extensions)
+ unprocessed_extensions = extensions.map(&:to_sym)
+ unprocessed_extensions.delete(:disabled)
+
+ source_specification =
+ if unprocessed_extensions.empty?
+ 'commonmark'
+ elsif unprocessed_extensions.include?(:gitlab)
+ unprocessed_extensions.delete(:gitlab)
+ 'gitlab'
+ else
+ 'github'
+ end
+
+ # We should only be left with at most one extension, which is an optional name for the example
+ raise "Error: Invalid extension(s) found: #{unprocessed_extensions.join(', ')}" if unprocessed_extensions.size > 1
+
+ source_specification
+ end
+
def write_markdown_yml(all_examples)
generate_and_write_for_all_examples(all_examples, ES_MARKDOWN_YML_PATH) do |example, hash|
- hash[example.fetch(:name)] = example.fetch(:markdown)
+ name = example.fetch(:name).to_sym
+ hash[name] = example.fetch(:markdown)
end
end
- def write_markdown_yml_tempfile
- # NOTE: We must copy the markdown YAML file to a separate temporary file for the
- # `render_static_html.rb` script to read it, because the script is run in a
- # separate process, and during unit testing we are unable to substitute the mock
- # StringIO when reading the input file in the subprocess.
- Dir::Tmpname.create(MARKDOWN_TEMPFILE_BASENAME) do |path|
- io = File.open(ES_MARKDOWN_YML_PATH)
- io.seek(0) # rewind the file. This is necessary when testing with a mock StringIO
- contents = io.read
- write_file(path, contents)
+ def copy_tempfiles_for_subprocesses
+ # NOTE: We must copy the input YAML files used by the `render_static_html.rb`
+ # and `render_wysiwyg_html_and_json.js` scripts to a separate temporary file in order for
+ # the scripts to read them, because the scripts are run in
+ # separate subprocesses, and during unit testing we are unable to substitute the mock
+ # StringIO when reading the input files in the subprocess.
+ {
+ ES_MARKDOWN_YML_PATH => MARKDOWN_TEMPFILE_BASENAME,
+ GLFM_EXAMPLE_METADATA_YML_PATH => METADATA_TEMPFILE_BASENAME
+ }.map do |original_file_path, tempfile_basename|
+ Dir::Tmpname.create(tempfile_basename) do |path|
+ io = File.open(original_file_path)
+ io.seek(0) # rewind the file. This is necessary when testing with a mock StringIO
+ contents = io.read
+ write_file(path, contents)
+ end
end
end
- def generate_static_html(markdown_yml_tempfile_path)
+ def generate_static_html
output("Generating static HTML from markdown examples...")
- # NOTE 1: We shell out to perform the conversion of markdown to static HTML via the internal Rails app
- # helper method. This allows us to avoid using the Rails API or environment in this script,
- # which makes developing and running the unit tests for this script much faster,
+ # NOTE 1: We shell out to perform the conversion of markdown to static HTML by invoking a
+ # separate subprocess. This allows us to avoid using the Rails API or environment in this
+ # script, which makes developing and running the unit tests for this script much faster,
# because they can use 'fast_spec_helper' which does not require the entire Rails environment.
- # NOTE 2: We pass the input file path as a command line argument, and receive the output
- # tempfile path as a return value. This is simplest in the case where we are invoking Ruby.
- cmd = %(rails runner #{__dir__}/render_static_html.rb #{markdown_yml_tempfile_path})
- cmd_output = run_external_cmd(cmd)
- # NOTE: Running under a debugger can add extra output, only take the last line
- static_html_tempfile_path = cmd_output.split("\n").last
+ # NOTE 2: We run this as an RSpec process, for the same reasons we run via Jest process below:
+ # because that's the easiest way to ensure a reliable, fully-configured environment in which
+ # to execute the markdown-generation logic. Also, in the static/backend case, Rspec
+ # provides the easiest and most reliable way to generate example data via Factorybot
+ # creation of stable model records. This ensures consistent snapshot values across
+ # machines/environments.
+
+ # Dir::Tmpname.create requires a block, but we are using the non-block form to get the path
+ # via the return value, so we pass an empty block to avoid an error.
+ static_html_tempfile_path = Dir::Tmpname.create(STATIC_HTML_TEMPFILE_BASENAME) {}
+ ENV['OUTPUT_STATIC_HTML_TEMPFILE_PATH'] = static_html_tempfile_path
+
+ cmd = %(bin/rspec #{__dir__}/render_static_html.rb)
+ run_external_cmd(cmd)
output("Reading generated static HTML from tempfile #{static_html_tempfile_path}...")
- YAML.load_file(static_html_tempfile_path)
+ YAML.safe_load(File.open(static_html_tempfile_path), symbolize_names: true)
end
- def generate_wysiwyg_html_and_json(markdown_yml_tempfile_path)
+ def generate_wysiwyg_html_and_json
output("Generating WYSIWYG HTML and prosemirror JSON from markdown examples...")
- # NOTE: Unlike when we invoke a Ruby script, here we pass the input and output file paths
- # via environment variables. This is because it's not straightforward/clean to pass command line
- # arguments when we are invoking `yarn jest ...`
- ENV['INPUT_MARKDOWN_YML_PATH'] = markdown_yml_tempfile_path
-
# Dir::Tmpname.create requires a block, but we are using the non-block form to get the path
# via the return value, so we pass an empty block to avoid an error.
wysiwyg_html_and_json_tempfile_path = Dir::Tmpname.create(WYSIWYG_HTML_AND_JSON_TEMPFILE_BASENAME) {}
@@ -217,26 +249,26 @@ module Glfm
output("Reading generated WYSIWYG HTML and prosemirror JSON from tempfile " \
"#{wysiwyg_html_and_json_tempfile_path}...")
- YAML.load_file(wysiwyg_html_and_json_tempfile_path)
+ YAML.safe_load(File.open(wysiwyg_html_and_json_tempfile_path), symbolize_names: true)
end
def write_html_yml(all_examples, static_html_hash, wysiwyg_html_and_json_hash, glfm_examples_statuses)
generate_and_write_for_all_examples(
- all_examples, ES_HTML_YML_PATH, glfm_examples_statuses
+ all_examples, ES_HTML_YML_PATH, glfm_examples_statuses: glfm_examples_statuses
) do |example, hash, existing_hash|
- name = example.fetch(:name)
+ name = example.fetch(:name).to_sym
example_statuses = glfm_examples_statuses[name] || {}
- static = if example_statuses['skip_update_example_snapshot_html_static']
- existing_hash.dig(name, 'static')
+ static = if example_statuses[:skip_update_example_snapshot_html_static]
+ existing_hash.dig(name, :static)
else
static_html_hash[name]
end
- wysiwyg = if example_statuses['skip_update_example_snapshot_html_wysiwyg']
- existing_hash.dig(name, 'wysiwyg')
+ wysiwyg = if example_statuses[:skip_update_example_snapshot_html_wysiwyg]
+ existing_hash.dig(name, :wysiwyg)
else
- wysiwyg_html_and_json_hash.dig(name, 'html')
+ wysiwyg_html_and_json_hash.dig(name, :html)
end
hash[name] = {
@@ -249,14 +281,14 @@ module Glfm
def write_prosemirror_json_yml(all_examples, wysiwyg_html_and_json_hash, glfm_examples_statuses)
generate_and_write_for_all_examples(
- all_examples, ES_PROSEMIRROR_JSON_YML_PATH, glfm_examples_statuses
+ all_examples, ES_PROSEMIRROR_JSON_YML_PATH, glfm_examples_statuses: glfm_examples_statuses
) do |example, hash, existing_hash|
- name = example.fetch(:name)
+ name = example.fetch(:name).to_sym
- json = if glfm_examples_statuses.dig(name, 'skip_update_example_snapshot_prosemirror_json')
+ json = if glfm_examples_statuses.dig(name, :skip_update_example_snapshot_prosemirror_json)
existing_hash[name]
else
- wysiwyg_html_and_json_hash.dig(name, 'json')
+ wysiwyg_html_and_json_hash.dig(name, :json)
end
# Do not assign nil values
@@ -265,15 +297,15 @@ module Glfm
end
def generate_and_write_for_all_examples(
- all_examples, output_file_path, glfm_examples_statuses = {}, literal_scalars: true
+ all_examples, output_file_path, glfm_examples_statuses: {}, literal_scalars: true
)
preserve_existing = !glfm_examples_statuses.empty?
output("#{preserve_existing ? 'Creating/Updating' : 'Creating/Overwriting'} #{output_file_path}...")
- existing_hash = preserve_existing ? YAML.safe_load(File.open(output_file_path)) : {}
+ existing_hash = preserve_existing ? YAML.safe_load(File.open(output_file_path), symbolize_names: true) : {}
output_hash = all_examples.each_with_object({}) do |example, hash|
- name = example.fetch(:name)
- if (reason = glfm_examples_statuses.dig(name, 'skip_update_example_snapshots'))
+ name = example.fetch(:name).to_sym
+ if (reason = glfm_examples_statuses.dig(name, :skip_update_example_snapshots))
# Output the reason for skipping the example, but only once, not multiple times for each file
output("Skipping '#{name}'. Reason: #{reason}") unless glfm_examples_statuses.dig(name, :already_printed)
# We just store the `:already_printed` flag in the hash entry itself. Then we
@@ -293,37 +325,5 @@ module Glfm
yaml_string = dump_yaml_with_formatting(output_hash, literal_scalars: literal_scalars)
write_file(output_file_path, yaml_string)
end
-
- # Construct an AST so we can control YAML formatting for
- # YAML block scalar literals and key quoting.
- #
- # Note that when Psych dumps the markdown to YAML, it will
- # automatically use the default "clip" behavior of the Block Chomping Indicator (`|`)
- # https://yaml.org/spec/1.2.2/#8112-block-chomping-indicator,
- # when the markdown strings contain a trailing newline. The type of
- # Block Chomping Indicator is automatically determined, you cannot specify it
- # manually.
- def dump_yaml_with_formatting(hash, literal_scalars:)
- visitor = Psych::Visitors::YAMLTree.create
- visitor << hash
- ast = visitor.tree
-
- # Force all scalars to have literal formatting (using Block Chomping Indicator instead of quotes)
- if literal_scalars
- ast.grep(Psych::Nodes::Scalar).each do |node|
- node.style = Psych::Nodes::Scalar::LITERAL
- end
- end
-
- # Do not quote the keys
- ast.grep(Psych::Nodes::Mapping).each do |node|
- node.children.each_slice(2) do |k, _|
- k.quoted = false
- k.style = Psych::Nodes::Scalar::ANY
- end
- end
-
- ast.to_yaml
- end
end
end
diff --git a/scripts/lint-doc.sh b/scripts/lint-doc.sh
index afc04da19a7..f954b2d8106 100755
--- a/scripts/lint-doc.sh
+++ b/scripts/lint-doc.sh
@@ -128,7 +128,7 @@ function run_locally_or_in_docker() {
$cmd $args
elif hash docker 2>/dev/null
then
- docker run -t -v ${PWD}:/gitlab -w /gitlab --rm registry.gitlab.com/gitlab-org/gitlab-docs/lint-markdown:alpine-3.15-vale-2.15.5-markdownlint-0.31.1 ${cmd} ${args}
+ docker run -t -v ${PWD}:/gitlab -w /gitlab --rm registry.gitlab.com/gitlab-org/gitlab-docs/lint-markdown:alpine-3.16-vale-2.20.1-markdownlint-0.32.2 ${cmd} ${args}
else
echo
echo " ✖ ERROR: '${cmd}' not found. Install '${cmd}' or Docker to proceed." >&2
diff --git a/scripts/rspec_check_order_dependence b/scripts/rspec_check_order_dependence
new file mode 100755
index 00000000000..91d4c5938a7
--- /dev/null
+++ b/scripts/rspec_check_order_dependence
@@ -0,0 +1,76 @@
+#!/usr/bin/env bash
+
+## Usage: scripts/rspec_check_order_dependence <files...>
+#
+# List of RSpec files to be checked for their order dependency.
+#
+# If the files pass the following checks it's likely they are not
+# order-dependent and are removed from `spec/support/rspec_order_todo.yml`
+# to make them run in random order.
+#
+# The following checks are available:
+# * Run specs in _defined_ order
+# * Run specs in _reverse_ order
+# * Run specs in _random_ order
+
+if [ $# -eq 0 ]; then
+ echo "Usage: $0 <files...>"
+ exit
+fi
+
+TODO_YAML='./spec/support/rspec_order_todo.yml'
+RSPEC_ARGS=(--format progress)
+
+abort() {
+ echo "$@"
+ echo "Aborting..."
+ exit 1
+}
+
+for file in "$@"
+do
+ # Drop potential file prefix `./`
+ file=${file#./}
+
+ # Match only the prefix so we can specify a directory to match all the files
+ # under it. For example, `spec/rubocop` will match, test and remove all TODO
+ # entries starting with `./spec/rubocop`.
+ grep -E -- "- './$file" "$TODO_YAML" > /dev/null || abort "Could not find '$file' in '$TODO_YAML'"
+done
+
+set -xe
+
+bin/rspec --order defined "${RSPEC_ARGS[@]}" "$@"
+RSPEC_ORDER=reverse bin/rspec "${RSPEC_ARGS[@]}" "$@"
+bin/rspec --order random "${RSPEC_ARGS[@]}" "$@"
+
+set +xe
+
+green='\033[0;32m'
+clear='\033[0m' # No Color
+
+echo -e "$green"
+echo "
+The files passed all checks!
+
+They are likely not order-dependent and can be run in random order and thus
+are being removed from 'spec/support/rspec_order_todo.yml':
+"
+
+for file in "$@"
+do
+ # Drop potential file prefix `./`
+ file=${file#./}
+
+ echo " * Removing '$file'"
+
+ # Escape forward slashes to make it compatible with sed below
+ escaped_file=${file//\//\\/}
+
+ # We must use -i.bak to make sed work on Linux and MacOS.
+ # See https://riptutorial.com/sed/topic/9436/bsd-macos-sed-vs--gnu-sed-vs--the-posix-sed-specification
+ sed -i.bak "/- '.\/$escaped_file/d" "$TODO_YAML"
+ rm "$TODO_YAML.bak"
+done
+
+echo -e "$clear"
diff --git a/scripts/rspec_helpers.sh b/scripts/rspec_helpers.sh
index b31e3663eaa..5d7bd844c2c 100644
--- a/scripts/rspec_helpers.sh
+++ b/scripts/rspec_helpers.sh
@@ -269,7 +269,7 @@ function rspec_paralellized_job() {
debug_rspec_variables
if [[ -n $RSPEC_TESTS_MAPPING_ENABLED ]]; then
- tooling/bin/parallel_rspec --rspec_args "$(rspec_args "${rspec_opts}")" --filter "tmp/matching_tests.txt" || rspec_run_status=$?
+ tooling/bin/parallel_rspec --rspec_args "$(rspec_args "${rspec_opts}")" --filter "${RSPEC_MATCHING_TESTS_PATH}" || rspec_run_status=$?
else
tooling/bin/parallel_rspec --rspec_args "$(rspec_args "${rspec_opts}")" || rspec_run_status=$?
fi
@@ -360,9 +360,22 @@ function rspec_fail_fast() {
function rspec_matched_foss_tests() {
local test_file_count_threshold=20
local matching_tests_file=${1}
+ local foss_matching_tests_file="${matching_tests_file}-foss"
+
+ # Keep only files that exists (i.e. exclude EE speficic files)
+ cat ${matching_tests_file} | ruby -e 'puts $stdin.read.split(" ").select { |f| File.exist?(f) && f.include?("spec/") }.join(" ")' > "${foss_matching_tests_file}"
+
+ echo "Matching tests file:"
+ cat ${matching_tests_file}
+ echo -e "\n\n"
+
+ echo "FOSS matching tests file:"
+ cat ${foss_matching_tests_file}
+ echo -e "\n\n"
+
local rspec_opts=${2}
- local test_files="$(cat "${matching_tests_file}")"
- local test_file_count=$(wc -w "${matching_tests_file}" | awk {'print $1'})
+ local test_files="$(cat ${foss_matching_tests_file})"
+ local test_file_count=$(wc -w "${foss_matching_tests_file}" | awk {'print $1'})
if [[ "${test_file_count}" -gt "${test_file_count_threshold}" ]]; then
echo "This job is intentionally failed because there are more than ${test_file_count_threshold} FOSS test files matched,"
diff --git a/scripts/rubocop-parse b/scripts/rubocop-parse
new file mode 100755
index 00000000000..4c82be5934b
--- /dev/null
+++ b/scripts/rubocop-parse
@@ -0,0 +1,73 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+# Emit AST from parsed Ruby code by RuboCop.
+#
+# This is an alternative to `ruby-parser` shipped with `parser` gem.
+#
+# Usage:
+# rubocop-parse -e 'puts "hello"'
+# (send nil :puts
+# (str "hello"))
+#
+# rubocop-parse -e 'puts "hello"' -v 3.0
+# (send nil :puts
+# (str "hello"))
+#
+# rubocop-parse app/models/project.rb
+# (begin
+# (send nil :require
+# (str "carrierwave/orm/activerecord"))
+# (class
+# (const nil :Project)
+# (const nil :ApplicationRecord)
+# (begin
+# (send nil :include
+# ...
+
+require_relative '../config/bundler_setup'
+
+require 'rubocop'
+require 'optparse'
+
+def print_ast(file, source, version)
+ version ||= RuboCop::ConfigStore.new.for_file(file).target_ruby_version
+ puts RuboCop::AST::ProcessedSource.new(source, version).ast.to_s
+end
+
+options = Struct.new(:eval, :ruby_version, :print_help, keyword_init: true).new
+
+parser = OptionParser.new do |opts|
+ opts.banner = "Usage: #{$0} [-e code] [FILE...]"
+
+ opts.on('-e FRAGMENT', '--eval FRAGMENT', 'Process a fragment of Ruby code') do |code|
+ options.eval = code
+ end
+
+ opts.on('-v RUBY_VERSION', '--ruby-version RUBY_VERSION',
+ 'Parse as Ruby would. Defaults to RuboCop TargetRubyVersion setting.') do |ruby_version|
+ options.ruby_version = Float(ruby_version)
+ end
+
+ opts.on('-h', '--help') do
+ options.print_help = true
+ end
+end
+
+args = parser.parse!
+
+if options.print_help
+ puts parser
+ exit
+end
+
+print_ast('', options.eval, options.ruby_version) if options.eval
+
+args.each do |arg|
+ if File.file?(arg)
+ source = File.read(arg)
+ print_ast(arg, source, options.ruby_version)
+ else
+ warn "Skipping non-file #{arg.inspect}"
+ end
+end
diff --git a/scripts/static-analysis b/scripts/static-analysis
index 1e9fe1cc724..53f84c19ac6 100755
--- a/scripts/static-analysis
+++ b/scripts/static-analysis
@@ -46,11 +46,11 @@ class StaticAnalysis
# around this we will only enable this task on EE installations.
TASKS_WITH_DURATIONS_SECONDS = [
(Gitlab.ee? ? Task.new(%w[bin/rake gettext:updated_check], 360) : nil),
- Task.new(%w[yarn run lint:prettier], 160),
- Task.new(%w[bin/rake gettext:lint], 85),
- Task.new(%W[scripts/license-check.sh #{project_path}], 20),
- Task.new(%w[bin/rake lint:static_verification], 35),
- Task.new(%w[scripts/rubocop-max-files-in-cache-check], 20),
+ Task.new(%w[yarn run lint:prettier], 200),
+ 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),
diff --git a/scripts/trigger-build.rb b/scripts/trigger-build.rb
index 57cc6a8551e..b368bbdb1f1 100755
--- a/scripts/trigger-build.rb
+++ b/scripts/trigger-build.rb
@@ -144,12 +144,10 @@ module Trigger
end
def base_variables
- # Use CI_MERGE_REQUEST_SOURCE_BRANCH_SHA for omnibus checkouts due to pipeline for merged results,
- # and fallback to CI_COMMIT_SHA for the `detached` pipelines.
{
'GITLAB_REF_SLUG' => ENV['CI_COMMIT_TAG'] ? ENV['CI_COMMIT_REF_NAME'] : ENV['CI_COMMIT_REF_SLUG'],
'TRIGGERED_USER' => ENV['TRIGGERED_USER'] || ENV['GITLAB_USER_NAME'],
- 'TOP_UPSTREAM_SOURCE_SHA' => Trigger.non_empty_variable_value('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA') || ENV['CI_COMMIT_SHA']
+ 'TOP_UPSTREAM_SOURCE_SHA' => ENV['CI_COMMIT_SHA']
}
end
@@ -161,53 +159,6 @@ module Trigger
end
end
- class Omnibus < Base
- def self.access_token
- # Default to "Multi-pipeline (from 'gitlab-org/gitlab' 'package-and-qa' job)" at https://gitlab.com/gitlab-org/build/omnibus-gitlab-mirror/-/settings/access_tokens
- ENV['OMNIBUS_GITLAB_PROJECT_ACCESS_TOKEN'] || super
- end
-
- private
-
- def downstream_project_path
- ENV.fetch('OMNIBUS_PROJECT_PATH', 'gitlab-org/build/omnibus-gitlab-mirror')
- end
-
- def ref_param_name
- 'OMNIBUS_BRANCH'
- end
-
- def primary_ref
- 'master'
- end
-
- def trigger_stable_branch_if_detected?
- true
- end
-
- def extra_variables
- # Use CI_MERGE_REQUEST_SOURCE_BRANCH_SHA (MR HEAD commit) so that the image is in sync with the assets and QA images.
- # See https://docs.gitlab.com/ee/development/testing_guide/end_to_end/index.html#with-pipeline-for-merged-results.
- # We also set IMAGE_TAG so the GitLab Docker image is tagged with that SHA.
- source_sha = Trigger.non_empty_variable_value('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA') || ENV['CI_COMMIT_SHA']
-
- {
- 'GITLAB_VERSION' => source_sha,
- 'IMAGE_TAG' => source_sha,
- 'QA_IMAGE' => ENV['QA_IMAGE'],
- 'SKIP_QA_DOCKER' => 'true',
- 'ALTERNATIVE_SOURCES' => 'true',
- 'SECURITY_SOURCES' => Trigger.security? ? 'true' : 'false',
- 'ee' => Trigger.ee? ? 'true' : 'false',
- 'QA_BRANCH' => ENV['QA_BRANCH'] || 'master',
- 'CACHE_UPDATE' => ENV['OMNIBUS_GITLAB_CACHE_UPDATE'],
- 'GITLAB_QA_OPTIONS' => ENV['GITLAB_QA_OPTIONS'],
- 'QA_TESTS' => ENV['QA_TESTS'],
- 'ALLURE_JOB_NAME' => ENV['ALLURE_JOB_NAME']
- }
- end
- end
-
class CNG < Base
def variables
# Delete variables that aren't useful when using native triggers.
@@ -232,14 +183,11 @@ module Trigger
end
def extra_variables
- # Use CI_MERGE_REQUEST_SOURCE_BRANCH_SHA (MR HEAD commit) so that the image is in sync with the assets and QA images.
- source_sha = Trigger.non_empty_variable_value('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA') || ENV['CI_COMMIT_SHA']
-
{
"TRIGGER_BRANCH" => ref,
- "GITLAB_VERSION" => source_sha,
+ "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'] : source_sha,
+ "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.
@@ -403,10 +351,9 @@ module Trigger
def extra_variables
{
- # Use CI_MERGE_REQUEST_SOURCE_BRANCH_SHA for omnibus checkouts due to pipeline for merged results
- # and fallback to CI_COMMIT_SHA for the `detached` pipelines.
'GITLAB_COMMIT_SHA' => Trigger.non_empty_variable_value('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA') || ENV['CI_COMMIT_SHA'],
- 'TRIGGERED_USER_LOGIN' => ENV['GITLAB_USER_LOGIN']
+ 'TRIGGERED_USER_LOGIN' => ENV['GITLAB_USER_LOGIN'],
+ 'TOP_UPSTREAM_SOURCE_SHA' => Trigger.non_empty_variable_value('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA') || ENV['CI_COMMIT_SHA']
}
end
@@ -482,8 +429,6 @@ end
if $0 == __FILE__
case ARGV[0]
- when 'omnibus'
- Trigger::Omnibus.new.invoke!(downstream_job_name: 'Trigger:qa-test').wait!
when 'cng'
Trigger::CNG.new.invoke!.wait!
when 'gitlab-com-database-testing'
diff --git a/scripts/utils.sh b/scripts/utils.sh
index 8db525abc93..10b7f856ee6 100644
--- a/scripts/utils.sh
+++ b/scripts/utils.sh
@@ -38,6 +38,8 @@ function bundle_install_script() {
exit 1;
fi;
+ echo -e "section_start:`date +%s`:bundle-install[collapsed=true]\r\e[0KInstalling gems"
+
gem --version
bundle --version
gem install bundler --no-document --conservative --version 2.3.15
@@ -48,7 +50,7 @@ function bundle_install_script() {
echo "${BUNDLE_WITHOUT}"
bundle config
- run_timed_command "bundle install ${BUNDLE_INSTALL_FLAGS} ${extra_install_args} && bundle check"
+ run_timed_command "bundle install ${BUNDLE_INSTALL_FLAGS} ${extra_install_args}"
if [[ $(bundle info pg) ]]; then
# When we test multiple versions of PG in the same pipeline, we have a single `setup-test-env`
@@ -56,6 +58,8 @@ function bundle_install_script() {
# Uncomment the following line if multiple versions of PG are tested in the same pipeline.
run_timed_command "bundle pristine pg"
fi
+
+ echo -e "section_end:`date +%s`:bundle-install\r\e[0K"
}
function setup_db_user_only() {
diff --git a/scripts/verify-tff-mapping b/scripts/verify-tff-mapping
index 9eb1d43c65b..b4974f71ebf 100755
--- a/scripts/verify-tff-mapping
+++ b/scripts/verify-tff-mapping
@@ -116,6 +116,18 @@ tests = [
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: 'Spec for every sidekiq worker',
+ source: 'app/workers/new_worker.rb',
+ expected: ['spec/workers/every_sidekiq_worker_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']
}
]
diff --git a/spec/benchmarks/banzai_benchmark.rb b/spec/benchmarks/banzai_benchmark.rb
index 86f7ee7e90b..7a60825c1e6 100644
--- a/spec/benchmarks/banzai_benchmark.rb
+++ b/spec/benchmarks/banzai_benchmark.rb
@@ -82,6 +82,11 @@ RSpec.describe 'GitLab Markdown Benchmark', :aggregate_failures do
it 'benchmarks all filters in the PlainMarkdownPipeline' do
benchmark_pipeline_filters(:plain_markdown)
end
+
+ it 'benchmarks specified filters in the FullPipeline' do
+ filter_klass_list = [Banzai::Filter::MathFilter]
+ benchmark_pipeline_filters(:full, filter_klass_list)
+ end
end
# build up the source text for each filter
@@ -105,7 +110,7 @@ RSpec.describe 'GitLab Markdown Benchmark', :aggregate_failures do
filter_source
end
- def benchmark_pipeline_filters(pipeline_type)
+ def benchmark_pipeline_filters(pipeline_type, filter_klass_list = nil)
pipeline = Banzai::Pipeline[pipeline_type]
filter_source = build_filter_text(pipeline, markdown_text)
@@ -114,7 +119,8 @@ RSpec.describe 'GitLab Markdown Benchmark', :aggregate_failures do
Benchmark.ips do |x|
x.config(time: 10, warmup: 2)
- pipeline.filters.each do |filter_klass|
+ filters = filter_klass_list || pipeline.filters
+ filters.each do |filter_klass|
label = filter_klass.name.demodulize.delete_suffix('Filter').truncate(20)
x.report(label) do
diff --git a/spec/components/layouts/horizontal_section_component_spec.rb b/spec/components/layouts/horizontal_section_component_spec.rb
new file mode 100644
index 00000000000..efc48213911
--- /dev/null
+++ b/spec/components/layouts/horizontal_section_component_spec.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe Layouts::HorizontalSectionComponent, type: :component do
+ let(:title) { 'Naming, visibility' }
+ let(:description) { 'Update your group name, description, avatar, and visibility.' }
+ let(:body) { 'This is where the settings go' }
+
+ describe 'slots' do
+ it 'renders title' do
+ render_inline described_class.new do |c|
+ c.title { title }
+ c.body { body }
+ end
+
+ expect(page).to have_css('h4', text: title)
+ end
+
+ it 'renders body slot' do
+ render_inline described_class.new do |c|
+ c.title { title }
+ c.body { body }
+ end
+
+ expect(page).to have_content(body)
+ end
+
+ context 'when description slot is provided' do
+ before do
+ render_inline described_class.new do |c|
+ c.title { title }
+ c.description { description }
+ c.body { body }
+ end
+ end
+
+ it 'renders description' do
+ expect(page).to have_css('p', text: description)
+ end
+ end
+
+ context 'when description slot is not provided' do
+ before do
+ render_inline described_class.new do |c|
+ c.title { title }
+ c.body { body }
+ end
+ end
+
+ it 'does not render description' do
+ expect(page).not_to have_css('p', text: description)
+ end
+ end
+ end
+
+ describe 'arguments' do
+ describe 'border' do
+ it 'defaults to true and adds gl-border-b CSS class' do
+ render_inline described_class.new do |c|
+ c.title { title }
+ c.body { body }
+ end
+
+ expect(page).to have_css('.gl-border-b')
+ end
+
+ it 'does not add gl-border-b CSS class when set to false' do
+ render_inline described_class.new(border: false) do |c|
+ c.title { title }
+ c.body { body }
+ end
+
+ expect(page).not_to have_css('.gl-border-b')
+ end
+ end
+
+ describe 'options' do
+ it 'adds options to wrapping element' do
+ render_inline described_class.new(options: { data: { testid: 'foo-bar' }, class: 'foo-bar' }) do |c|
+ c.title { title }
+ c.body { body }
+ end
+
+ expect(page).to have_css('.foo-bar[data-testid="foo-bar"]')
+ end
+ end
+ end
+end
diff --git a/spec/components/pajamas/badge_component_spec.rb b/spec/components/pajamas/badge_component_spec.rb
new file mode 100644
index 00000000000..4c564121ba2
--- /dev/null
+++ b/spec/components/pajamas/badge_component_spec.rb
@@ -0,0 +1,148 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe Pajamas::BadgeComponent, type: :component do
+ let(:text) { "Hello" }
+ let(:options) { {} }
+ let(:html_options) { {} }
+
+ before do
+ render_inline(described_class.new(text, **options, **html_options))
+ end
+
+ describe "text param" do
+ it "is shown inside the badge" do
+ expect(page).to have_css ".gl-badge", text: text
+ end
+ end
+
+ describe "content slot" do
+ it "can be used instead of the text param" do
+ render_inline(described_class.new) do
+ "Slot content"
+ end
+ expect(page).to have_css ".gl-badge", text: "Slot content"
+ end
+
+ it "takes presendence over the text param" do
+ render_inline(described_class.new(text)) do
+ "Slot wins."
+ end
+ expect(page).to have_css ".gl-badge", text: "Slot wins."
+ end
+ end
+
+ describe "options" do
+ describe "icon" do
+ let(:options) { { icon: :tanuki } }
+
+ it "adds the correct icon and margin" do
+ expect(page).to have_css ".gl-icon.gl-badge-icon.gl-mr-2[data-testid='tanuki-icon']"
+ end
+ end
+
+ describe "icon_classes" do
+ let(:options) { { icon: :tanuki, icon_classes: icon_classes } }
+
+ context "as string" do
+ let(:icon_classes) { "js-special-badge-icon js-extra-special" }
+
+ it "combines custom classes and component classes" do
+ expect(page).to have_css \
+ ".gl-icon.gl-badge-icon.gl-mr-2.js-special-badge-icon.js-extra-special[data-testid='tanuki-icon']"
+ end
+ end
+
+ context "as array" do
+ let(:icon_classes) { %w[js-special-badge-icon js-extra-special] }
+
+ it "combines custom classes and component classes" do
+ expect(page).to have_css \
+ ".gl-icon.gl-badge-icon.gl-mr-2.js-special-badge-icon.js-extra-special[data-testid='tanuki-icon']"
+ end
+ end
+ end
+
+ describe "icon_only" do
+ let(:options) { { icon: :tanuki, icon_only: true } }
+
+ it "adds no extra margin to the icon" do
+ expect(page).not_to have_css ".gl-icon.gl-mr-2"
+ end
+
+ it "adds the text as ARIA label" do
+ expect(page).to have_css ".gl-badge[aria-label='#{text}'][role='img']"
+ end
+ end
+
+ describe "href" do
+ let(:options) { { href: "/foo" } }
+
+ it "makes the a badge a link" do
+ expect(page).to have_link text, class: "gl-badge", href: "/foo"
+ end
+ end
+
+ describe "size" do
+ where(:size) { [:sm, :md, :lg] }
+
+ with_them do
+ let(:options) { { size: size } }
+
+ it "adds size class" do
+ expect(page).to have_css ".gl-badge.#{size}"
+ end
+ end
+
+ context "with unknown size" do
+ let(:options) { { size: :xxl } }
+
+ it "adds the default size class" do
+ expect(page).to have_css ".gl-badge.md"
+ end
+ end
+ end
+
+ describe "variant" do
+ where(:variant) { [:muted, :neutral, :info, :success, :warning, :danger] }
+
+ with_them do
+ let(:options) { { variant: variant } }
+
+ it "adds variant class" do
+ expect(page).to have_css ".gl-badge.badge-#{variant}"
+ end
+ end
+
+ context "with unknown variant" do
+ let(:options) { { variant: :foo } }
+
+ it "adds the default variant class" do
+ expect(page).to have_css ".gl-badge.badge-muted"
+ end
+ end
+ end
+ end
+
+ describe "HTML options" do
+ let(:html_options) { { id: "badge-33", data: { foo: "bar" } } }
+
+ it "get added as HTML attributes" do
+ expect(page).to have_css ".gl-badge#badge-33[data-foo='bar']"
+ end
+
+ it "can be combined with component options in no particular order" do
+ render_inline(described_class.new(text, id: "badge-34", variant: :success, data: { foo: "baz" }, size: :sm))
+ expect(page).to have_css ".gl-badge.badge-success.sm#badge-34[data-foo='baz']"
+ end
+
+ context "with custom CSS classes" do
+ let(:html_options) { { id: "badge-35", class: "js-special-badge" } }
+
+ it "combines custom classes and component classes" do
+ expect(page).to have_css ".gl-badge.js-special-badge#badge-35"
+ end
+ end
+ end
+end
diff --git a/spec/components/previews/layouts/horizontal_section_component_preview.rb b/spec/components/previews/layouts/horizontal_section_component_preview.rb
new file mode 100644
index 00000000000..cc7e8c8c2b1
--- /dev/null
+++ b/spec/components/previews/layouts/horizontal_section_component_preview.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Layouts
+ class HorizontalSectionComponentPreview < ViewComponent::Preview
+ # @param border toggle
+ # @param title text
+ # @param description text
+ # @param body text
+ def default(
+ border: true,
+ title: 'Naming, visibility',
+ description: 'Update your group name, description, avatar, and visibility.',
+ body: 'Settings fields here.'
+ )
+ render(::Layouts::HorizontalSectionComponent.new(border: border, options: { class: 'gl-mb-6 gl-pb-3' })) do |c|
+ c.title { title }
+ c.description { description }
+ c.body { body }
+ end
+ end
+ end
+end
diff --git a/spec/components/previews/pajamas/badge_component_preview.rb b/spec/components/previews/pajamas/badge_component_preview.rb
new file mode 100644
index 00000000000..e740a4a38aa
--- /dev/null
+++ b/spec/components/previews/pajamas/badge_component_preview.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+module Pajamas
+ class BadgeComponentPreview < ViewComponent::Preview
+ # Badge
+ # ---
+ #
+ # See its design reference [here](https://design.gitlab.com/components/badge).
+ #
+ # @param icon select [~, star-o, issue-closed, tanuki]
+ # @param icon_only toggle
+ # @param href url
+ # @param size select [sm, md, lg]
+ # @param text text
+ # @param variant select [muted, neutral, info, success, warning, danger]
+ def default(icon: :tanuki, icon_only: false, href: nil, size: :md, text: "Tanuki", variant: :muted)
+ render Pajamas::BadgeComponent.new(
+ text,
+ icon: icon,
+ icon_only: icon_only,
+ href: href,
+ size: size,
+ variant: variant
+ )
+ end
+
+ # Using the content slot
+ # ---
+ #
+ # Use the content slot instead of the `text` param when things get more complicated than a plain string.
+ # All other options (`icon`, `size`, etc.) work as usual.
+ def slot
+ render Pajamas::BadgeComponent.new(size: :lg, variant: :info) do
+ "!ereht olleh".reverse.capitalize
+ end
+ end
+
+ # Custom HTML attributes and icon classes
+ # ---
+ #
+ # Any extra options passed into the component are treated as HTML attributes.
+ # This makes adding data or an id easy.
+ #
+ # CSS classes provided with the `class:` option are combined with the component classes.
+ #
+ # It is also possible to set custom `icon_classes:`.
+ #
+ # The order in which you provide these keywords doesn't matter.
+ def custom
+ render Pajamas::BadgeComponent.new(
+ "I'm special.",
+ class: "js-special-badge",
+ data: { count: 1 },
+ icon: :tanuki,
+ icon_classes: ["js-special-badge-icon"],
+ id: "special-badge-22",
+ variant: :success
+ )
+ end
+ end
+end
diff --git a/spec/config/metrics/aggregates/aggregated_metrics_spec.rb b/spec/config/metrics/aggregates/aggregated_metrics_spec.rb
index b5f8d363d40..1984aff01db 100644
--- a/spec/config/metrics/aggregates/aggregated_metrics_spec.rb
+++ b/spec/config/metrics/aggregates/aggregated_metrics_spec.rb
@@ -54,7 +54,7 @@ RSpec.describe 'aggregated metrics' do
expect(aggregated_metrics).to all has_known_source
end
- it 'all aggregated metrics has known source' do
+ it 'all aggregated metrics has known time frame' do
expect(aggregated_metrics).to all have_known_time_frame
end
@@ -66,7 +66,7 @@ RSpec.describe 'aggregated metrics' do
expect(aggregate[:time_frame]).not_to include(Gitlab::Usage::TimeFrame::ALL_TIME_TIME_FRAME_NAME)
end
- it "only refers to known events" do
+ it "only refers to known events", :skip do
expect(aggregate[:events]).to all be_known_event
end
diff --git a/spec/config/object_store_settings_spec.rb b/spec/config/object_store_settings_spec.rb
index 1555124fe03..8ddb5652dae 100644
--- a/spec/config/object_store_settings_spec.rb
+++ b/spec/config/object_store_settings_spec.rb
@@ -200,33 +200,6 @@ RSpec.describe ObjectStoreSettings do
expect(settings.external_diffs['object_store']).to be_nil
end
end
-
- context 'with legacy config and legacy background upload is enabled' do
- let(:legacy_settings) do
- {
- 'enabled' => true,
- 'remote_directory' => 'some-bucket',
- 'proxy_download' => false
- }
- end
-
- before do
- stub_env(ObjectStoreSettings::LEGACY_BACKGROUND_UPLOADS_ENV, 'lfs')
- settings.lfs['object_store'] = described_class.legacy_parse(legacy_settings, 'lfs')
- end
-
- it 'enables background_upload and disables direct_upload' do
- subject
-
- 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)
- # Enable background_upload if the environment variable is available
- expect(settings.lfs['object_store']['direct_upload']).to eq(false)
- expect(settings.lfs['object_store']['background_upload']).to eq(true)
- expect(settings.external_diffs['object_store']).to be_nil
- end
- end
end
end
@@ -266,48 +239,6 @@ RSpec.describe ObjectStoreSettings do
expect(settings['remote_directory']).to eq 'gitlab'
expect(settings['bucket_prefix']).to eq 'artifacts'
end
-
- context 'legacy background upload environment variable is enabled' do
- before do
- stub_env(ObjectStoreSettings::LEGACY_BACKGROUND_UPLOADS_ENV, 'artifacts,lfs')
- end
-
- it 'enables background_upload and disables direct_upload' do
- original_settings = Settingslogic.new({
- 'enabled' => true,
- 'remote_directory' => 'artifacts'
- })
-
- settings = described_class.legacy_parse(original_settings, 'artifacts')
-
- expect(settings['enabled']).to be true
- expect(settings['direct_upload']).to be false
- expect(settings['background_upload']).to be true
- expect(settings['remote_directory']).to eq 'artifacts'
- expect(settings['bucket_prefix']).to eq nil
- end
- end
-
- context 'legacy background upload environment variable is enabled for other types' do
- before do
- stub_env(ObjectStoreSettings::LEGACY_BACKGROUND_UPLOADS_ENV, 'uploads,lfs')
- end
-
- it 'enables direct_upload and disables background_upload' do
- original_settings = Settingslogic.new({
- 'enabled' => true,
- 'remote_directory' => 'artifacts'
- })
-
- settings = described_class.legacy_parse(original_settings, 'artifacts')
-
- 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 eq nil
- end
- end
end
describe '.split_bucket_prefix' do
diff --git a/spec/config/settings_spec.rb b/spec/config/settings_spec.rb
index 1de0e7e6c26..9b721d8cfca 100644
--- a/spec/config/settings_spec.rb
+++ b/spec/config/settings_spec.rb
@@ -116,7 +116,7 @@ RSpec.describe Settings do
describe '.cron_for_service_ping' do
it 'returns correct crontab for some manually calculated example' do
allow(Gitlab::CurrentSettings)
- .to receive(:uuid) { 'd9e2f4e8-db1f-4e51-b03d-f427e1965c4a'}
+ .to receive(:uuid) { 'd9e2f4e8-db1f-4e51-b03d-f427e1965c4a' }
expect(described_class.send(:cron_for_service_ping)).to eq('44 10 * * 4')
end
@@ -150,4 +150,30 @@ RSpec.describe Settings do
expect(Settings.encrypted('tmp/tests/test.enc').read).to be_empty
end
end
+
+ describe '.build_sidekiq_routing_rules' do
+ [
+ [nil, [['*', 'default']]],
+ [[], [['*', 'default']]],
+ [[['name=foobar', 'foobar']], [['name=foobar', 'foobar']]]
+ ].each do |input_rules, output_rules|
+ context "Given input routing_rules #{input_rules}" do
+ it "returns output routing_rules #{output_rules}" do
+ expect(described_class.send(:build_sidekiq_routing_rules, input_rules)).to eq(output_rules)
+ end
+ end
+ end
+ end
+
+ describe '.microsoft_graph_mailer' do
+ it 'defaults' do
+ expect(described_class.microsoft_graph_mailer.enabled).to be false
+ expect(described_class.microsoft_graph_mailer.user_id).to be_nil
+ expect(described_class.microsoft_graph_mailer.tenant).to be_nil
+ expect(described_class.microsoft_graph_mailer.client_id).to be_nil
+ expect(described_class.microsoft_graph_mailer.client_secret).to be_nil
+ expect(described_class.microsoft_graph_mailer.azure_ad_endpoint).to eq('https://login.microsoftonline.com')
+ expect(described_class.microsoft_graph_mailer.graph_endpoint).to eq('https://graph.microsoft.com')
+ end
+ end
end
diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb
index e02589ddc83..ab0cad989cb 100644
--- a/spec/controllers/admin/application_settings_controller_spec.rb
+++ b/spec/controllers/admin/application_settings_controller_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Admin::ApplicationSettingsController, :do_not_mock_admin_mode_set
let(:group) { create(:group) }
let(:project) { create(:project, namespace: group) }
let(:admin) { create(:admin) }
- let(:user) { create(:user)}
+ let(:user) { create(:user) }
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
@@ -362,6 +362,17 @@ RSpec.describe Admin::ApplicationSettingsController, :do_not_mock_admin_mode_set
expect(application_settings.reload.pipeline_limit_per_project_user_sha).to eq(25)
end
end
+
+ context 'invitation flow enforcement setting' do
+ let(:application_settings) { ApplicationSetting.current }
+
+ it 'updates invitation_flow_enforcement setting' do
+ put :update, params: { application_setting: { invitation_flow_enforcement: true } }
+
+ expect(response).to redirect_to(general_admin_application_settings_path)
+ expect(application_settings.reload.invitation_flow_enforcement).to eq(true)
+ end
+ end
end
describe 'PUT #reset_registration_token' do
diff --git a/spec/controllers/admin/applications_controller_spec.rb b/spec/controllers/admin/applications_controller_spec.rb
index 6c423097e70..bf7707f177c 100644
--- a/spec/controllers/admin/applications_controller_spec.rb
+++ b/spec/controllers/admin/applications_controller_spec.rb
@@ -39,17 +39,43 @@ RSpec.describe Admin::ApplicationsController do
end
describe 'POST #create' do
- it 'creates the application' do
- create_params = attributes_for(:application, trusted: true, confidential: false, scopes: ['api'])
+ context 'with hash_oauth_secrets flag off' do
+ before do
+ stub_feature_flags(hash_oauth_secrets: false)
+ end
- expect do
- post :create, params: { doorkeeper_application: create_params }
- end.to change { Doorkeeper::Application.count }.by(1)
+ it 'creates the application' do
+ create_params = attributes_for(:application, trusted: true, confidential: false, scopes: ['api'])
+
+ expect do
+ post :create, params: { doorkeeper_application: create_params }
+ end.to change { Doorkeeper::Application.count }.by(1)
- application = Doorkeeper::Application.last
+ application = Doorkeeper::Application.last
- expect(response).to redirect_to(admin_application_path(application))
- expect(application).to have_attributes(create_params.except(:uid, :owner_type))
+ expect(response).to redirect_to(admin_application_path(application))
+ expect(application).to have_attributes(create_params.except(:uid, :owner_type))
+ end
+ end
+
+ context 'with hash_oauth_secrets flag on' do
+ before do
+ stub_feature_flags(hash_oauth_secrets: true)
+ end
+
+ it 'creates the application' do
+ create_params = attributes_for(:application, trusted: true, confidential: false, scopes: ['api'])
+
+ expect do
+ post :create, params: { doorkeeper_application: create_params }
+ end.to change { Doorkeeper::Application.count }.by(1)
+
+ application = Doorkeeper::Application.last
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template :show
+ expect(application).to have_attributes(create_params.except(:uid, :owner_type))
+ end
end
it 'renders the application form on errors' do
@@ -62,17 +88,43 @@ RSpec.describe Admin::ApplicationsController do
end
context 'when the params are for a confidential application' do
- it 'creates a confidential application' do
- create_params = attributes_for(:application, confidential: true, scopes: ['read_user'])
+ context 'with hash_oauth_secrets flag off' do
+ before do
+ stub_feature_flags(hash_oauth_secrets: false)
+ end
- expect do
- post :create, params: { doorkeeper_application: create_params }
- end.to change { Doorkeeper::Application.count }.by(1)
+ it 'creates a confidential application' do
+ create_params = attributes_for(:application, confidential: true, scopes: ['read_user'])
- application = Doorkeeper::Application.last
+ expect do
+ post :create, params: { doorkeeper_application: create_params }
+ end.to change { Doorkeeper::Application.count }.by(1)
- expect(response).to redirect_to(admin_application_path(application))
- expect(application).to have_attributes(create_params.except(:uid, :owner_type))
+ application = Doorkeeper::Application.last
+
+ expect(response).to redirect_to(admin_application_path(application))
+ expect(application).to have_attributes(create_params.except(:uid, :owner_type))
+ end
+ end
+
+ context 'with hash_oauth_secrets flag on' do
+ before do
+ stub_feature_flags(hash_oauth_secrets: true)
+ end
+
+ it 'creates a confidential application' do
+ create_params = attributes_for(:application, confidential: true, scopes: ['read_user'])
+
+ expect do
+ post :create, params: { doorkeeper_application: create_params }
+ end.to change { Doorkeeper::Application.count }.by(1)
+
+ application = Doorkeeper::Application.last
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template :show
+ expect(application).to have_attributes(create_params.except(:uid, :owner_type))
+ end
end
end
diff --git a/spec/controllers/admin/cohorts_controller_spec.rb b/spec/controllers/admin/cohorts_controller_spec.rb
index d271276a3e4..766073977c6 100644
--- a/spec/controllers/admin/cohorts_controller_spec.rb
+++ b/spec/controllers/admin/cohorts_controller_spec.rb
@@ -13,5 +13,17 @@ RSpec.describe Admin::CohortsController do
it_behaves_like 'tracking unique visits', :index do
let(:target_id) { 'i_analytics_cohorts' }
end
+
+ it_behaves_like 'Snowplow event tracking' do
+ subject { get :index }
+
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:category) { described_class.name }
+ let(:action) { 'perform_analytics_usage_action' }
+ let(:label) { 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly' }
+ let(:property) { 'i_analytics_cohorts' }
+ let(:namespace) { nil }
+ let(:project) { nil }
+ end
end
end
diff --git a/spec/controllers/admin/runners_controller_spec.rb b/spec/controllers/admin/runners_controller_spec.rb
index fea59969400..9e852cb28dd 100644
--- a/spec/controllers/admin/runners_controller_spec.rb
+++ b/spec/controllers/admin/runners_controller_spec.rb
@@ -74,7 +74,7 @@ RSpec.describe Admin::RunnersController do
context 'with update succeeding' do
before do
expect_next_instance_of(Ci::Runners::UpdateRunnerService, runner) do |service|
- expect(service).to receive(:update).with(anything).and_call_original
+ expect(service).to receive(:execute).with(anything).and_call_original
end
end
@@ -91,7 +91,7 @@ RSpec.describe Admin::RunnersController do
context 'with update failing' do
before do
expect_next_instance_of(Ci::Runners::UpdateRunnerService, runner) do |service|
- expect(service).to receive(:update).with(anything).and_return(false)
+ expect(service).to receive(:execute).with(anything).and_return(ServiceResponse.error(message: 'failure'))
end
end
diff --git a/spec/controllers/admin/spam_logs_controller_spec.rb b/spec/controllers/admin/spam_logs_controller_spec.rb
index 13038339d08..48221f496fb 100644
--- a/spec/controllers/admin/spam_logs_controller_spec.rb
+++ b/spec/controllers/admin/spam_logs_controller_spec.rb
@@ -27,13 +27,34 @@ RSpec.describe Admin::SpamLogsController do
expect(response).to have_gitlab_http_status(:ok)
end
- it 'removes user and their spam logs when removing the user', :sidekiq_might_not_need_inline do
- delete :destroy, params: { id: first_spam.id, remove_user: true }
+ context 'when user_destroy_with_limited_execution_time_worker is enabled' do
+ it 'initiates user removal', :sidekiq_inline do
+ expect do
+ delete :destroy, params: { id: first_spam.id, remove_user: true }
+ end.not_to change { SpamLog.count }
- expect(flash[:notice]).to eq "User #{user.username} was successfully removed."
- expect(response).to have_gitlab_http_status(:found)
- expect(SpamLog.count).to eq(0)
- expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ expect(response).to have_gitlab_http_status(:found)
+ expect(
+ Users::GhostUserMigration.where(user: user,
+ initiator_user: admin)
+ ).to be_exists
+ expect(flash[:notice]).to eq("User #{user.username} was successfully removed.")
+ end
+ end
+
+ context 'when user_destroy_with_limited_execution_time_worker is disabled' do
+ before do
+ stub_feature_flags(user_destroy_with_limited_execution_time_worker: false)
+ end
+
+ it 'removes user and their spam logs when removing the user', :sidekiq_inline do
+ delete :destroy, params: { id: first_spam.id, remove_user: true }
+
+ expect(flash[:notice]).to eq "User #{user.username} was successfully removed."
+ expect(response).to have_gitlab_http_status(:found)
+ expect(SpamLog.count).to eq(0)
+ expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
end
end
diff --git a/spec/controllers/admin/topics_controller_spec.rb b/spec/controllers/admin/topics_controller_spec.rb
index 87093e0263b..111fdcc3be6 100644
--- a/spec/controllers/admin/topics_controller_spec.rb
+++ b/spec/controllers/admin/topics_controller_spec.rb
@@ -194,7 +194,7 @@ RSpec.describe Admin::TopicsController do
end
it 'renders a 400 error for identical topic ids' do
- post :merge, params: { source_topic_id: topic, target_topic_id: topic.id }
+ post :merge, params: { source_topic_id: topic.id, target_topic_id: topic.id }
expect(response).to have_gitlab_http_status(:bad_request)
expect { topic.reload }.not_to raise_error
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index 515ad9daf36..682399f4dd9 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -73,51 +73,120 @@ RSpec.describe Admin::UsersController do
project.add_developer(user)
end
- it 'deletes user and ghosts their contributions' do
- delete :destroy, params: { id: user.username }, format: :json
+ context 'when user_destroy_with_limited_execution_time_worker is enabled' do
+ it 'initiates user removal' do
+ delete :destroy, params: { id: user.username }, format: :json
- expect(response).to have_gitlab_http_status(:ok)
- expect(User.exists?(user.id)).to be_falsy
- expect(issue.reload.author).to be_ghost
- end
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(
+ Users::GhostUserMigration.where(user: user,
+ initiator_user: admin,
+ hard_delete: false)
+ ).to be_exists
+ end
- it 'deletes the user and their contributions when hard delete is specified' do
- delete :destroy, params: { id: user.username, hard_delete: true }, format: :json
+ it 'initiates user removal and passes hard delete option' do
+ delete :destroy, params: { id: user.username, hard_delete: true }, format: :json
- expect(response).to have_gitlab_http_status(:ok)
- expect(User.exists?(user.id)).to be_falsy
- expect(Issue.exists?(issue.id)).to be_falsy
- end
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(
+ Users::GhostUserMigration.where(user: user,
+ initiator_user: admin,
+ hard_delete: true)
+ ).to be_exists
+ end
- context 'prerequisites for account deletion' do
- context 'solo-owned groups' do
- let(:group) { create(:group) }
+ context 'prerequisites for account deletion' do
+ context 'solo-owned groups' do
+ let(:group) { create(:group) }
- context 'if the user is the sole owner of at least one group' do
- before do
- create(:group_member, :owner, group: group, user: user)
- end
+ context 'if the user is the sole owner of at least one group' do
+ before do
+ create(:group_member, :owner, group: group, user: user)
+ end
+
+ context 'soft-delete' do
+ it 'fails' do
+ delete :destroy, params: { id: user.username }
- context 'soft-delete' do
- it 'fails' do
- delete :destroy, params: { id: user.username }
+ message = s_('AdminUsers|You must transfer ownership or delete the groups owned by this user before you can delete their account')
- message = s_('AdminUsers|You must transfer ownership or delete the groups owned by this user before you can delete their account')
+ expect(flash[:alert]).to eq(message)
+ expect(response).to have_gitlab_http_status(:see_other)
+ expect(response).to redirect_to admin_user_path(user)
+ expect(Users::GhostUserMigration).not_to exist
+ end
+ end
- expect(flash[:alert]).to eq(message)
- expect(response).to have_gitlab_http_status(:see_other)
- expect(response).to redirect_to admin_user_path(user)
- expect(User.exists?(user.id)).to be_truthy
+ context 'hard-delete' do
+ it 'succeeds' do
+ delete :destroy, params: { id: user.username, hard_delete: true }
+
+ expect(response).to redirect_to(admin_users_path)
+ expect(flash[:notice]).to eq(_('The user is being deleted.'))
+ expect(
+ Users::GhostUserMigration.where(user: user,
+ initiator_user: admin,
+ hard_delete: true)
+ ).to be_exists
+ end
end
end
+ end
+ end
+ end
+
+ context 'when user_destroy_with_limited_execution_time_worker is disabled' do
+ before do
+ stub_feature_flags(user_destroy_with_limited_execution_time_worker: false)
+ end
+
+ it 'deletes user and ghosts their contributions' do
+ delete :destroy, params: { id: user.username }, format: :json
- context 'hard-delete' do
- it 'succeeds' do
- delete :destroy, params: { id: user.username, hard_delete: true }
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(User.exists?(user.id)).to be_falsy
+ expect(issue.reload.author).to be_ghost
+ end
+
+ it 'deletes the user and their contributions when hard delete is specified' do
+ delete :destroy, params: { id: user.username, hard_delete: true }, format: :json
- expect(response).to redirect_to(admin_users_path)
- expect(flash[:notice]).to eq(_('The user is being deleted.'))
- expect(User.exists?(user.id)).to be_falsy
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(User.exists?(user.id)).to be_falsy
+ expect(Issue.exists?(issue.id)).to be_falsy
+ end
+
+ context 'prerequisites for account deletion' do
+ context 'solo-owned groups' do
+ let(:group) { create(:group) }
+
+ context 'if the user is the sole owner of at least one group' do
+ before do
+ create(:group_member, :owner, group: group, user: user)
+ end
+
+ context 'soft-delete' do
+ it 'fails' do
+ delete :destroy, params: { id: user.username }
+
+ message = s_('AdminUsers|You must transfer ownership or delete the groups owned by this user before you can delete their account')
+
+ expect(flash[:alert]).to eq(message)
+ expect(response).to have_gitlab_http_status(:see_other)
+ expect(response).to redirect_to admin_user_path(user)
+ expect(User.exists?(user.id)).to be_truthy
+ end
+ end
+
+ context 'hard-delete' do
+ it 'succeeds' do
+ delete :destroy, params: { id: user.username, hard_delete: true }
+
+ expect(response).to redirect_to(admin_users_path)
+ expect(flash[:notice]).to eq(_('The user is being deleted.'))
+ expect(User.exists?(user.id)).to be_falsy
+ end
end
end
end
@@ -131,10 +200,27 @@ RSpec.describe Admin::UsersController do
context 'when rejecting a pending user' do
let(:user) { create(:user, :blocked_pending_approval) }
- it 'hard deletes the user', :sidekiq_inline do
- subject
+ context 'when user_destroy_with_limited_execution_time_worker is enabled' do
+ it 'initiates user removal', :sidekiq_inline do
+ subject
- expect(User.exists?(user.id)).to be_falsy
+ expect(
+ Users::GhostUserMigration.where(user: user,
+ initiator_user: admin)
+ ).to be_exists
+ end
+ end
+
+ context 'when user_destroy_with_limited_execution_time_worker is disabled' do
+ before do
+ stub_feature_flags(user_destroy_with_limited_execution_time_worker: false)
+ end
+
+ it 'hard deletes the user', :sidekiq_inline do
+ subject
+
+ expect(User.exists?(user.id)).to be_falsy
+ end
end
it 'displays the rejection message' do
@@ -270,19 +356,19 @@ RSpec.describe Admin::UsersController do
let(:user) { create(:user, **activity) }
context 'with no recent activity' do
- let(:activity) { { last_activity_on: ::User::MINIMUM_INACTIVE_DAYS.next.days.ago } }
+ let(:activity) { { last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.next.days.ago } }
it_behaves_like 'a request that deactivates the user'
end
context 'with recent activity' do
- let(:activity) { { last_activity_on: ::User::MINIMUM_INACTIVE_DAYS.pred.days.ago } }
+ let(:activity) { { last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.pred.days.ago } }
it 'does not deactivate the user' do
put :deactivate, params: { id: user.username }
user.reload
expect(user.deactivated?).to be_falsey
- expect(flash[:notice]).to eq("The user you are trying to deactivate has been active in the past #{::User::MINIMUM_INACTIVE_DAYS} days and cannot be deactivated")
+ expect(flash[:notice]).to eq("The user you are trying to deactivate has been active in the past #{Gitlab::CurrentSettings.deactivate_dormant_users_period} days and cannot be deactivated")
end
end
end
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 1e28ef4ba93..f1adb9020fa 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -1006,7 +1006,7 @@ RSpec.describe ApplicationController do
end
describe '.endpoint_id_for_action' do
- controller(described_class) { }
+ controller(described_class) {}
it 'returns an expected endpoint id' do
expect(controller.class.endpoint_id_for_action('hello')).to eq('AnonymousController#hello')
diff --git a/spec/controllers/concerns/continue_params_spec.rb b/spec/controllers/concerns/continue_params_spec.rb
index c010e8ffbd0..ba600b8156a 100644
--- a/spec/controllers/concerns/continue_params_spec.rb
+++ b/spec/controllers/concerns/continue_params_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe ContinueParams do
let(:controller_class) do
+ # rubocop:disable Rails/ApplicationController
Class.new(ActionController::Base) do
include ContinueParams
@@ -11,6 +12,7 @@ RSpec.describe ContinueParams do
@request ||= Struct.new(:host, :port).new('test.host', 80)
end
end
+ # rubocop:enable Rails/ApplicationController
end
subject(:controller) { controller_class.new }
diff --git a/spec/controllers/concerns/product_analytics_tracking_spec.rb b/spec/controllers/concerns/product_analytics_tracking_spec.rb
index 250cc3cf2cf..2e734d81ea0 100644
--- a/spec/controllers/concerns/product_analytics_tracking_spec.rb
+++ b/spec/controllers/concerns/product_analytics_tracking_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe ProductAnalyticsTracking, :snowplow do
skip_before_action :authenticate_user!, only: :show
track_event(:index, :show, name: 'g_analytics_valuestream', destinations: [:redis_hll, :snowplow],
- conditions: [:custom_condition_one?, :custom_condition_two?]) { |controller| controller.get_custom_id }
+ conditions: [:custom_condition_one?, :custom_condition_two?]) { |controller| controller.get_custom_id }
def index
render html: 'index'
diff --git a/spec/controllers/concerns/redis_tracking_spec.rb b/spec/controllers/concerns/redis_tracking_spec.rb
index 178684ae2d0..0ad8fa79e5e 100644
--- a/spec/controllers/concerns/redis_tracking_spec.rb
+++ b/spec/controllers/concerns/redis_tracking_spec.rb
@@ -11,7 +11,8 @@ RSpec.describe RedisTracking do
include RedisTracking
skip_before_action :authenticate_user!, only: :show
- track_redis_hll_event(:index, :show, name: 'g_compliance_approval_rules',
+ track_redis_hll_event(:index, :show,
+ name: 'g_compliance_approval_rules',
if: [:custom_condition_one?, :custom_condition_two?]) { |controller| controller.get_custom_id }
def index
diff --git a/spec/controllers/confirmations_controller_spec.rb b/spec/controllers/confirmations_controller_spec.rb
index 5b137ada141..111bfb24c7e 100644
--- a/spec/controllers/confirmations_controller_spec.rb
+++ b/spec/controllers/confirmations_controller_spec.rb
@@ -129,6 +129,10 @@ RSpec.describe ConfirmationsController do
subject(:perform_request) { post(:create, params: { user: { email: user.email } }) }
+ before do
+ stub_feature_flags(identity_verification: false)
+ end
+
context 'when reCAPTCHA is disabled' do
before do
stub_application_setting(recaptcha_enabled: false)
diff --git a/spec/controllers/graphql_controller_spec.rb b/spec/controllers/graphql_controller_spec.rb
index 1d2f1085d3c..7c9236704ec 100644
--- a/spec/controllers/graphql_controller_spec.rb
+++ b/spec/controllers/graphql_controller_spec.rb
@@ -88,10 +88,11 @@ RSpec.describe GraphqlController do
post :execute, params: { _json: multiplex }
expect(response).to have_gitlab_http_status(:ok)
- expect(json_response).to eq([
- { 'data' => { '__typename' => 'Query' } },
- { 'data' => { '__typename' => 'Query' } }
- ])
+ expect(json_response).to eq(
+ [
+ { 'data' => { '__typename' => 'Query' } },
+ { 'data' => { '__typename' => 'Query' } }
+ ])
end
it 'sets a limit on the total query size' do
diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb
index c6fd184ede0..a3659ae9163 100644
--- a/spec/controllers/groups/group_members_controller_spec.rb
+++ b/spec/controllers/groups/group_members_controller_spec.rb
@@ -97,6 +97,25 @@ RSpec.describe Groups::GroupMembersController do
expect(assigns(:members).map(&:user_id)).to contain_exactly(user.id)
end
end
+
+ context 'when webui_members_inherited_users is disabled' do
+ let_it_be(:shared_group) { create(:group) }
+ let_it_be(:shared_group_user) { create(:user) }
+ let_it_be(:group_link) { create(:group_group_link, shared_group: shared_group, shared_with_group: group) }
+
+ before do
+ group.add_owner(user)
+ shared_group.add_owner(shared_group_user)
+ stub_feature_flags(webui_members_inherited_users: false)
+ sign_in(user)
+ end
+
+ it 'lists inherited group members only' do
+ get :index, params: { group_id: shared_group }
+
+ expect(assigns(:members).map(&:user_id)).to contain_exactly(shared_group_user.id)
+ end
+ end
end
describe 'PUT update' do
diff --git a/spec/controllers/groups/labels_controller_spec.rb b/spec/controllers/groups/labels_controller_spec.rb
index 90da40cd5f0..37db26096d3 100644
--- a/spec/controllers/groups/labels_controller_spec.rb
+++ b/spec/controllers/groups/labels_controller_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe Groups::LabelsController do
it 'returns group and project labels by default' do
get :index, params: { group_id: group }, format: :json
- label_ids = json_response.map {|label| label['title']}
+ label_ids = json_response.map { |label| label['title'] }
expect(label_ids).to match_array([label_1.title, group_label_1.title])
end
@@ -36,7 +36,7 @@ RSpec.describe Groups::LabelsController do
params = { group_id: subgroup, only_group_labels: true }
get :index, params: params, format: :json
- label_ids = json_response.map {|label| label['title']}
+ label_ids = json_response.map { |label| label['title'] }
expect(label_ids).to match_array([group_label_1.title, subgroup_label_1.title])
end
end
diff --git a/spec/controllers/groups/releases_controller_spec.rb b/spec/controllers/groups/releases_controller_spec.rb
index 9d372114d62..7dd0bc6206a 100644
--- a/spec/controllers/groups/releases_controller_spec.rb
+++ b/spec/controllers/groups/releases_controller_spec.rb
@@ -42,7 +42,7 @@ RSpec.describe Groups::ReleasesController do
end
it 'does not return any releases' do
- expect(json_response.map {|r| r['tag'] } ).to be_empty
+ expect(json_response.map { |r| r['tag'] } ).to be_empty
end
it 'returns OK' do
@@ -56,7 +56,7 @@ RSpec.describe Groups::ReleasesController do
index
- expect(json_response.map {|r| r['tag'] } ).to match_array(%w(p2 p1 v2 v1))
+ expect(json_response.map { |r| r['tag'] } ).to match_array(%w(p2 p1 v2 v1))
end
end
diff --git a/spec/controllers/groups/settings/applications_controller_spec.rb b/spec/controllers/groups/settings/applications_controller_spec.rb
index 0804a5536e0..b9457770ed6 100644
--- a/spec/controllers/groups/settings/applications_controller_spec.rb
+++ b/spec/controllers/groups/settings/applications_controller_spec.rb
@@ -71,17 +71,43 @@ RSpec.describe Groups::Settings::ApplicationsController do
group.add_owner(user)
end
- it 'creates the application' do
- create_params = attributes_for(:application, trusted: false, confidential: false, scopes: ['api'])
+ context 'with hash_oauth_secrets flag on' do
+ before do
+ stub_feature_flags(hash_oauth_secrets: true)
+ end
- expect do
- post :create, params: { group_id: group, doorkeeper_application: create_params }
- end.to change { Doorkeeper::Application.count }.by(1)
+ it 'creates the application' do
+ create_params = attributes_for(:application, trusted: false, confidential: false, scopes: ['api'])
+
+ expect do
+ post :create, params: { group_id: group, doorkeeper_application: create_params }
+ end.to change { Doorkeeper::Application.count }.by(1)
- application = Doorkeeper::Application.last
+ application = Doorkeeper::Application.last
- expect(response).to redirect_to(group_settings_application_path(group, application))
- expect(application).to have_attributes(create_params.except(:uid, :owner_type))
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template :show
+ expect(application).to have_attributes(create_params.except(:uid, :owner_type))
+ end
+ end
+
+ context 'with hash_oauth_secrets flag off' do
+ before do
+ stub_feature_flags(hash_oauth_secrets: false)
+ end
+
+ it 'creates the application' do
+ create_params = attributes_for(:application, trusted: false, confidential: false, scopes: ['api'])
+
+ expect do
+ post :create, params: { group_id: group, doorkeeper_application: create_params }
+ end.to change { Doorkeeper::Application.count }.by(1)
+
+ application = Doorkeeper::Application.last
+
+ expect(response).to redirect_to(group_settings_application_path(group, application))
+ expect(application).to have_attributes(create_params.except(:uid, :owner_type))
+ end
end
it 'renders the application form on errors' do
@@ -94,17 +120,43 @@ RSpec.describe Groups::Settings::ApplicationsController do
end
context 'when the params are for a confidential application' do
- it 'creates a confidential application' do
- create_params = attributes_for(:application, confidential: true, scopes: ['read_user'])
+ context 'with hash_oauth_secrets flag off' do
+ before do
+ stub_feature_flags(hash_oauth_secrets: false)
+ end
- expect do
- post :create, params: { group_id: group, doorkeeper_application: create_params }
- end.to change { Doorkeeper::Application.count }.by(1)
+ it 'creates a confidential application' do
+ create_params = attributes_for(:application, confidential: true, scopes: ['read_user'])
- application = Doorkeeper::Application.last
+ expect do
+ post :create, params: { group_id: group, doorkeeper_application: create_params }
+ end.to change { Doorkeeper::Application.count }.by(1)
- expect(response).to redirect_to(group_settings_application_path(group, application))
- expect(application).to have_attributes(create_params.except(:uid, :owner_type))
+ application = Doorkeeper::Application.last
+
+ expect(response).to redirect_to(group_settings_application_path(group, application))
+ expect(application).to have_attributes(create_params.except(:uid, :owner_type))
+ end
+ end
+
+ context 'with hash_oauth_secrets flag on' do
+ before do
+ stub_feature_flags(hash_oauth_secrets: true)
+ end
+
+ it 'creates a confidential application' do
+ create_params = attributes_for(:application, confidential: true, scopes: ['read_user'])
+
+ expect do
+ post :create, params: { group_id: group, doorkeeper_application: create_params }
+ end.to change { Doorkeeper::Application.count }.by(1)
+
+ application = Doorkeeper::Application.last
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template :show
+ expect(application).to have_attributes(create_params.except(:uid, :owner_type))
+ end
end
end
diff --git a/spec/controllers/help_controller_spec.rb b/spec/controllers/help_controller_spec.rb
index 26e65711e9f..2375146f346 100644
--- a/spec/controllers/help_controller_spec.rb
+++ b/spec/controllers/help_controller_spec.rb
@@ -139,6 +139,39 @@ RSpec.describe HelpController do
end
end
+ describe 'GET #drawers' do
+ subject { get :drawers, params: { markdown_file: path } }
+
+ context 'when requested file exists' do
+ let(:path) { 'user/ssh' }
+ let(:file_name) { "#{path}.md" }
+
+ before do
+ subject
+ end
+
+ it 'assigns variables', :aggregate_failures do
+ expect(assigns[:path]).not_to be_empty
+ expect(assigns[:clean_path]).not_to be_empty
+ end
+
+ it 'renders HTML', :aggregate_failures do
+ is_expected.to render_template('help/drawers')
+ expect(response.media_type).to eq 'text/html'
+ end
+ end
+
+ context 'when requested file is missing' do
+ let(:path) { 'foo/bar' }
+
+ it 'renders not found' do
+ subject
+
+ expect(response).to be_not_found
+ end
+ end
+ end
+
describe 'GET #show' do
context 'for Markdown formats' do
subject { get :show, params: { path: path }, format: :md }
diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb
index af220e2d515..e73e61b6ec5 100644
--- a/spec/controllers/import/bitbucket_controller_spec.rb
+++ b/spec/controllers/import/bitbucket_controller_spec.rb
@@ -49,10 +49,10 @@ RSpec.describe Import::BitbucketController do
let(:expires_in) { 1.day }
let(:access_token) do
double(token: token,
- secret: secret,
- expires_at: expires_at,
- expires_in: expires_in,
- refresh_token: refresh_token)
+ secret: secret,
+ expires_at: expires_at,
+ expires_in: expires_in,
+ refresh_token: refresh_token)
end
before do
diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb
index 46160aac0c1..269eb62cae6 100644
--- a/spec/controllers/import/github_controller_spec.rb
+++ b/spec/controllers/import/github_controller_spec.rb
@@ -134,7 +134,7 @@ RSpec.describe Import::GithubController do
it 'fetches repos using legacy client' do
expect_next_instance_of(Gitlab::LegacyGithubImport::Client) do |client|
- expect(client).to receive(:repos)
+ expect(client).to receive(:repos).and_return([])
end
get :status
@@ -164,8 +164,8 @@ RSpec.describe Import::GithubController do
end
it 'fetches repos using latest github client' do
- expect_next_instance_of(Octokit::Client) do |client|
- expect(client).to receive(:repos).and_return([].to_enum)
+ expect_next_instance_of(Gitlab::GithubImport::Client) do |client|
+ expect(client).to receive(:repos).and_return([])
end
get :status
@@ -184,8 +184,8 @@ RSpec.describe Import::GithubController do
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(:repos).with(nil, { page: 1, per_page: 25 }).and_return([].to_enum)
+ 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
diff --git a/spec/controllers/import/manifest_controller_spec.rb b/spec/controllers/import/manifest_controller_spec.rb
index 0111ad9501f..6f805b44e89 100644
--- a/spec/controllers/import/manifest_controller_spec.rb
+++ b/spec/controllers/import/manifest_controller_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Import::ManifestController, :clean_gitlab_redis_shared_state do
include ImportSpecHelper
let_it_be(:user) { create(:user) }
- let_it_be(:group) { create(:group)}
+ let_it_be(:group) { create(:group) }
before(:all) do
group.add_maintainer(user)
diff --git a/spec/controllers/oauth/applications_controller_spec.rb b/spec/controllers/oauth/applications_controller_spec.rb
index 5bf3b4c48bf..9b16dc9a463 100644
--- a/spec/controllers/oauth/applications_controller_spec.rb
+++ b/spec/controllers/oauth/applications_controller_spec.rb
@@ -113,11 +113,30 @@ RSpec.describe Oauth::ApplicationsController do
subject { post :create, params: oauth_params }
- it 'creates an application' do
- subject
+ context 'when hash_oauth_tokens flag set' do
+ before do
+ stub_feature_flags(hash_oauth_secrets: true)
+ end
- expect(response).to have_gitlab_http_status(:found)
- expect(response).to redirect_to(oauth_application_path(Doorkeeper::Application.last))
+ it 'creates an application' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template :show
+ end
+ end
+
+ context 'when hash_oauth_tokens flag not set' do
+ before do
+ stub_feature_flags(hash_oauth_secrets: false)
+ end
+
+ it 'creates an application' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:found)
+ expect(response).to redirect_to(oauth_application_path(Doorkeeper::Application.last))
+ end
end
it 'redirects back to profile page if OAuth applications are disabled' do
diff --git a/spec/controllers/oauth/token_info_controller_spec.rb b/spec/controllers/oauth/token_info_controller_spec.rb
index b66fff4d4e9..3cd952d4935 100644
--- a/spec/controllers/oauth/token_info_controller_spec.rb
+++ b/spec/controllers/oauth/token_info_controller_spec.rb
@@ -24,12 +24,12 @@ RSpec.describe Oauth::TokenInfoController do
expect(response).to have_gitlab_http_status(:ok)
expect(Gitlab::Json.parse(response.body)).to eq(
- 'scope' => %w[api],
- 'scopes' => %w[api],
- 'created_at' => access_token.created_at.to_i,
- 'expires_in' => access_token.expires_in,
- 'application' => { 'uid' => application.uid },
- 'resource_owner_id' => access_token.resource_owner_id,
+ 'scope' => %w[api],
+ 'scopes' => %w[api],
+ 'created_at' => access_token.created_at.to_i,
+ 'expires_in' => access_token.expires_in,
+ 'application' => { 'uid' => application.uid },
+ 'resource_owner_id' => access_token.resource_owner_id,
'expires_in_seconds' => access_token.expires_in
)
end
diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb
index 9ecef8b7450..df5da29495e 100644
--- a/spec/controllers/omniauth_callbacks_controller_spec.rb
+++ b/spec/controllers/omniauth_callbacks_controller_spec.rb
@@ -406,7 +406,7 @@ RSpec.describe OmniauthCallbacksController, type: :controller do
before do
stub_last_request_id(last_request_id)
stub_omniauth_saml_config(enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'],
- providers: [saml_config])
+ providers: [saml_config])
mock_auth_hash_with_saml_xml('saml', +'my-uid', user.email, mock_saml_response)
request.env['devise.mapping'] = Devise.mappings[:user]
request.env['omniauth.auth'] = Rails.application.env_config['omniauth.auth']
diff --git a/spec/controllers/profiles/personal_access_tokens_controller_spec.rb b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb
index 0e531dbaf4b..99e9644da66 100644
--- a/spec/controllers/profiles/personal_access_tokens_controller_spec.rb
+++ b/spec/controllers/profiles/personal_access_tokens_controller_spec.rb
@@ -48,8 +48,8 @@ RSpec.describe Profiles::PersonalAccessTokensController do
end
it "only includes details of the active personal access token" do
- active_personal_access_tokens_detail = ::API::Entities::PersonalAccessTokenWithDetails
- .represent([active_personal_access_token])
+ active_personal_access_tokens_detail =
+ ::PersonalAccessTokenSerializer.new.represent([active_personal_access_token])
expect(assigns(:active_personal_access_tokens).to_json).to eq(active_personal_access_tokens_detail.to_json)
end
@@ -100,8 +100,8 @@ RSpec.describe Profiles::PersonalAccessTokensController do
get :index
first_token = assigns(:active_personal_access_tokens).first.as_json
- expect(first_token[:name]).to eq("Token1")
- expect(first_token[:expires_at]).to eq(expires_1_day_from_now.strftime("%Y-%m-%d"))
+ expect(first_token['name']).to eq("Token1")
+ expect(first_token['expires_at']).to eq(expires_1_day_from_now.strftime("%Y-%m-%d"))
end
it "orders tokens on id in case token has same expires_at" do
@@ -110,12 +110,12 @@ RSpec.describe Profiles::PersonalAccessTokensController do
get :index
first_token = assigns(:active_personal_access_tokens).first.as_json
- expect(first_token[:name]).to eq("Token3")
- expect(first_token[:expires_at]).to eq(expires_1_day_from_now.strftime("%Y-%m-%d"))
+ expect(first_token['name']).to eq("Token3")
+ expect(first_token['expires_at']).to eq(expires_1_day_from_now.strftime("%Y-%m-%d"))
second_token = assigns(:active_personal_access_tokens).second.as_json
- expect(second_token[:name]).to eq("Token1")
- expect(second_token[:expires_at]).to eq(expires_1_day_from_now.strftime("%Y-%m-%d"))
+ expect(second_token['name']).to eq("Token1")
+ expect(second_token['expires_at']).to eq(expires_1_day_from_now.strftime("%Y-%m-%d"))
end
end
diff --git a/spec/controllers/profiles_controller_spec.rb b/spec/controllers/profiles_controller_spec.rb
index 89185a8f856..aa92ff6be33 100644
--- a/spec/controllers/profiles_controller_spec.rb
+++ b/spec/controllers/profiles_controller_spec.rb
@@ -82,13 +82,17 @@ RSpec.describe ProfilesController, :request_store do
expect(ldap_user.location).to eq('City, Country')
end
- it 'allows setting a user status' do
+ it 'allows setting a user status', :freeze_time do
sign_in(user)
- put :update, params: { user: { status: { message: 'Working hard!', availability: 'busy' } } }
+ put(
+ :update,
+ params: { user: { status: { message: 'Working hard!', availability: 'busy', clear_status_after: '8_hours' } } }
+ )
expect(user.reload.status.message).to eq('Working hard!')
expect(user.reload.status.availability).to eq('busy')
+ expect(user.reload.status.clear_status_after).to eq(8.hours.from_now)
expect(response).to have_gitlab_http_status(:found)
end
diff --git a/spec/controllers/projects/analytics/cycle_analytics/stages_controller_spec.rb b/spec/controllers/projects/analytics/cycle_analytics/stages_controller_spec.rb
index 8903592ba15..b9e569b1647 100644
--- a/spec/controllers/projects/analytics/cycle_analytics/stages_controller_spec.rb
+++ b/spec/controllers/projects/analytics/cycle_analytics/stages_controller_spec.rb
@@ -16,7 +16,6 @@ RSpec.describe Projects::Analytics::CycleAnalytics::StagesController do
end
before do
- stub_feature_flags(use_vsa_aggregated_tables: false)
sign_in(user)
end
diff --git a/spec/controllers/projects/artifacts_controller_spec.rb b/spec/controllers/projects/artifacts_controller_spec.rb
index 958fcd4360c..263f488ddbf 100644
--- a/spec/controllers/projects/artifacts_controller_spec.rb
+++ b/spec/controllers/projects/artifacts_controller_spec.rb
@@ -488,7 +488,7 @@ RSpec.describe Projects::ArtifactsController do
context 'with regular branch' do
before do
pipeline.update!(ref: 'master',
- sha: project.commit('master').sha)
+ sha: project.commit('master').sha)
get :latest_succeeded, params: params_from_ref('master')
end
@@ -499,7 +499,7 @@ RSpec.describe Projects::ArtifactsController do
context 'with branch name containing slash' do
before do
pipeline.update!(ref: 'improve/awesome',
- sha: project.commit('improve/awesome').sha)
+ sha: project.commit('improve/awesome').sha)
get :latest_succeeded, params: params_from_ref('improve/awesome')
end
@@ -510,7 +510,7 @@ RSpec.describe Projects::ArtifactsController do
context 'with branch name and path containing slashes' do
before do
pipeline.update!(ref: 'improve/awesome',
- sha: project.commit('improve/awesome').sha)
+ sha: project.commit('improve/awesome').sha)
get :latest_succeeded, params: params_from_ref('improve/awesome', job.name, 'file/README.md')
end
diff --git a/spec/controllers/projects/blame_controller_spec.rb b/spec/controllers/projects/blame_controller_spec.rb
index bf475f6135a..62a544bb3fc 100644
--- a/spec/controllers/projects/blame_controller_spec.rb
+++ b/spec/controllers/projects/blame_controller_spec.rb
@@ -41,7 +41,7 @@ RSpec.describe Projects::BlameController do
end
context "invalid branch, valid file" do
- let(:id) { 'invalid-branch/files/ruby/missing_file.rb'}
+ let(:id) { 'invalid-branch/files/ruby/missing_file.rb' }
it { is_expected.to respond_with(:not_found) }
end
diff --git a/spec/controllers/projects/ci/daily_build_group_report_results_controller_spec.rb b/spec/controllers/projects/ci/daily_build_group_report_results_controller_spec.rb
index 3c4376909f8..e5bffb7c265 100644
--- a/spec/controllers/projects/ci/daily_build_group_report_results_controller_spec.rb
+++ b/spec/controllers/projects/ci/daily_build_group_report_results_controller_spec.rb
@@ -59,12 +59,13 @@ RSpec.describe Projects::Ci::DailyBuildGroupReportResultsController do
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers['Content-Type']).to eq('text/csv; charset=utf-8')
- expect(csv_response).to eq([
- %w[date group_name coverage],
- ['2020-03-09', 'rspec', '79.0'],
- ['2020-03-08', 'rspec', '77.0'],
- ['2019-12-10', 'karma', '81.0']
- ])
+ expect(csv_response).to eq(
+ [
+ %w[date group_name coverage],
+ ['2020-03-09', 'rspec', '79.0'],
+ ['2020-03-08', 'rspec', '77.0'],
+ ['2019-12-10', 'karma', '81.0']
+ ])
end
context 'when given date range spans more than 90 days' do
@@ -72,12 +73,13 @@ RSpec.describe Projects::Ci::DailyBuildGroupReportResultsController do
let(:end_date) { '2020-03-09' }
it 'limits the result to 90 days from the given start_date' do
- expect(csv_response).to eq([
- %w[date group_name coverage],
- ['2020-03-09', 'rspec', '79.0'],
- ['2020-03-08', 'rspec', '77.0'],
- ['2019-12-10', 'karma', '81.0']
- ])
+ expect(csv_response).to eq(
+ [
+ %w[date group_name coverage],
+ ['2020-03-09', 'rspec', '79.0'],
+ ['2020-03-08', 'rspec', '77.0'],
+ ['2019-12-10', 'karma', '81.0']
+ ])
end
end
@@ -89,29 +91,8 @@ RSpec.describe Projects::Ci::DailyBuildGroupReportResultsController do
it 'serves the results in JSON' do
expect(response).to have_gitlab_http_status(:ok)
- expect(json_response).to eq([
- {
- 'group_name' => 'rspec',
- 'data' => [
- { 'date' => '2020-03-09', 'coverage' => 79.0 },
- { 'date' => '2020-03-08', 'coverage' => 77.0 }
- ]
- },
- {
- 'group_name' => 'karma',
- 'data' => [
- { 'date' => '2019-12-10', 'coverage' => 81.0 }
- ]
- }
- ])
- end
-
- context 'when given date range spans more than 90 days' do
- let(:start_date) { '2019-12-09' }
- let(:end_date) { '2020-03-09' }
-
- it 'limits the result to 90 days from the given start_date' do
- expect(json_response).to eq([
+ expect(json_response).to eq(
+ [
{
'group_name' => 'rspec',
'data' => [
@@ -126,6 +107,29 @@ RSpec.describe Projects::Ci::DailyBuildGroupReportResultsController do
]
}
])
+ end
+
+ context 'when given date range spans more than 90 days' do
+ let(:start_date) { '2019-12-09' }
+ let(:end_date) { '2020-03-09' }
+
+ it 'limits the result to 90 days from the given start_date' do
+ expect(json_response).to eq(
+ [
+ {
+ 'group_name' => 'rspec',
+ 'data' => [
+ { 'date' => '2020-03-09', 'coverage' => 79.0 },
+ { 'date' => '2020-03-08', 'coverage' => 77.0 }
+ ]
+ },
+ {
+ 'group_name' => 'karma',
+ 'data' => [
+ { 'date' => '2019-12-10', 'coverage' => 81.0 }
+ ]
+ }
+ ])
end
end
diff --git a/spec/controllers/projects/ci/lints_controller_spec.rb b/spec/controllers/projects/ci/lints_controller_spec.rb
index d778739be38..403f99145fc 100644
--- a/spec/controllers/projects/ci/lints_controller_spec.rb
+++ b/spec/controllers/projects/ci/lints_controller_spec.rb
@@ -143,10 +143,11 @@ RSpec.describe Projects::Ci::LintsController do
it_behaves_like 'returns a successful validation'
it 'assigns result with errors' do
- expect(parsed_body['errors']).to match_array([
- 'jobs rubocop config should implement a script: or a trigger: keyword',
- 'jobs config should contain at least one visible job'
- ])
+ expect(parsed_body['errors']).to match_array(
+ [
+ 'jobs rubocop config should implement a script: or a trigger: keyword',
+ 'jobs config should contain at least one visible job'
+ ])
end
context 'with dry_run mode' do
diff --git a/spec/controllers/projects/cycle_analytics_controller_spec.rb b/spec/controllers/projects/cycle_analytics_controller_spec.rb
index ccd213fdffa..f5dd8abd67b 100644
--- a/spec/controllers/projects/cycle_analytics_controller_spec.rb
+++ b/spec/controllers/projects/cycle_analytics_controller_spec.rb
@@ -30,6 +30,18 @@ RSpec.describe Projects::CycleAnalyticsController do
let(:request_params) { { namespace_id: project.namespace, project_id: project } }
let(:target_id) { 'p_analytics_valuestream' }
end
+
+ it_behaves_like 'Snowplow event tracking' do
+ subject { get :show, params: request_params, format: :html }
+
+ let(:request_params) { { namespace_id: project.namespace, project_id: project } }
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:category) { described_class.name }
+ let(:action) { 'perform_analytics_usage_action' }
+ let(:namespace) { project.namespace }
+ let(:label) { 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly' }
+ let(:property) { 'p_analytics_valuestream' }
+ end
end
include_examples GracefulTimeoutHandling
diff --git a/spec/controllers/projects/deploy_keys_controller_spec.rb b/spec/controllers/projects/deploy_keys_controller_spec.rb
index 821f7fca73d..308146ce792 100644
--- a/spec/controllers/projects/deploy_keys_controller_spec.rb
+++ b/spec/controllers/projects/deploy_keys_controller_spec.rb
@@ -27,8 +27,8 @@ RSpec.describe Projects::DeployKeysController do
end
context 'when json requested' do
- let(:project2) { create(:project, :internal)}
- let(:project_private) { create(:project, :private)}
+ let(:project2) { create(:project, :internal) }
+ let(:project_private) { create(:project, :private) }
let(:deploy_key_internal) { create(:deploy_key) }
let(:deploy_key_actual) { create(:deploy_key) }
diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb
index 1a6edab795d..16a43bae674 100644
--- a/spec/controllers/projects/environments_controller_spec.rb
+++ b/spec/controllers/projects/environments_controller_spec.rb
@@ -32,6 +32,11 @@ RSpec.describe Projects::EnvironmentsController do
get :index, params: environment_params
end
+
+ it_behaves_like 'tracking unique visits', :index do
+ let(:request_params) { environment_params }
+ let(:target_id) { 'users_visiting_environments_pages' }
+ end
end
context 'when requesting JSON response for folders' do
@@ -68,6 +73,24 @@ RSpec.describe Projects::EnvironmentsController do
expect(json_response['stopped_count']).to eq 1
end
+ it 'handles search option properly' do
+ get :index, params: environment_params(format: :json, search: 'staging/r')
+
+ expect(environments.map { |env| env['name'] } ).to contain_exactly('staging/review-1', 'staging/review-2')
+ expect(json_response['available_count']).to eq 2
+ expect(json_response['stopped_count']).to eq 1
+ end
+
+ it 'ignores search option if is shorter than a minimum' do
+ get :index, params: environment_params(format: :json, search: 'st')
+
+ expect(environments.map { |env| env['name'] } ).to contain_exactly('production',
+ 'staging/review-1',
+ 'staging/review-2')
+ expect(json_response['available_count']).to eq 3
+ expect(json_response['stopped_count']).to eq 1
+ end
+
it 'sets the polling interval header' do
subject
@@ -149,29 +172,43 @@ RSpec.describe Projects::EnvironmentsController do
end
describe 'GET folder' do
- before do
- create(:environment, project: project,
- name: 'staging-1.0/review',
- state: :available)
- create(:environment, project: project,
- name: 'staging-1.0/zzz',
- state: :available)
- end
-
context 'when using default format' do
it 'responds with HTML' do
get :folder, params: {
- namespace_id: project.namespace,
- project_id: project,
- id: 'staging-1.0'
- }
+ namespace_id: project.namespace,
+ project_id: project,
+ id: 'staging-1.0'
+ }
- expect(response).to be_ok
+ expect(response).to have_gitlab_http_status(:ok)
expect(response).to render_template 'folder'
end
+
+ it_behaves_like 'tracking unique visits', :folder do
+ let(:request_params) do
+ {
+ namespace_id: project.namespace,
+ project_id: project,
+ id: 'staging-1.0'
+ }
+ end
+
+ let(:target_id) { 'users_visiting_environments_pages' }
+ end
end
context 'when using JSON format' do
+ before do
+ create(:environment, project: project,
+ name: 'staging-1.0/review',
+ state: :available)
+ create(:environment, project: project,
+ name: 'staging-1.0/zzz',
+ state: :available)
+ end
+
+ let(:environments) { json_response['environments'] }
+
it 'sorts the subfolders lexicographically' do
get :folder, params: {
namespace_id: project.namespace,
@@ -187,6 +224,19 @@ RSpec.describe Projects::EnvironmentsController do
expect(json_response['environments'][1])
.to include('name' => 'staging-1.0/zzz', 'name_without_type' => 'zzz')
end
+
+ it 'handles search option properly' do
+ get(:folder, params: {
+ namespace_id: project.namespace,
+ project_id: project,
+ id: 'staging-1.0',
+ search: 'staging-1.0/z'
+ }, format: :json)
+
+ expect(environments.map { |env| env['name'] } ).to eq(['staging-1.0/zzz'])
+ expect(json_response['available_count']).to eq 1
+ expect(json_response['stopped_count']).to eq 0
+ end
end
end
@@ -197,6 +247,11 @@ RSpec.describe Projects::EnvironmentsController do
expect(response).to be_ok
end
+
+ it_behaves_like 'tracking unique visits', :show do
+ let(:request_params) { environment_params }
+ let(:target_id) { 'users_visiting_environments_pages' }
+ end
end
context 'with invalid id' do
@@ -210,12 +265,30 @@ RSpec.describe Projects::EnvironmentsController do
end
end
+ describe 'GET new' do
+ it 'responds with a status code 200' do
+ get :new, params: environment_params
+
+ expect(response).to be_ok
+ end
+
+ it_behaves_like 'tracking unique visits', :new do
+ let(:request_params) { environment_params }
+ let(:target_id) { 'users_visiting_environments_pages' }
+ end
+ end
+
describe 'GET edit' do
it 'responds with a status code 200' do
get :edit, params: environment_params
expect(response).to be_ok
end
+
+ it_behaves_like 'tracking unique visits', :edit do
+ let(:request_params) { environment_params }
+ let(:target_id) { 'users_visiting_environments_pages' }
+ end
end
describe 'PATCH #update' do
@@ -230,6 +303,11 @@ RSpec.describe Projects::EnvironmentsController do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['path']).to eq("/#{project.full_path}/-/environments/#{environment.id}")
end
+
+ it_behaves_like 'tracking unique visits', :update do
+ let(:request_params) { params }
+ let(:target_id) { 'users_visiting_environments_pages' }
+ end
end
context "when environment params are invalid" do
@@ -294,6 +372,11 @@ RSpec.describe Projects::EnvironmentsController do
{ 'redirect_url' =>
project_environment_url(project, environment) })
end
+
+ it_behaves_like 'tracking unique visits', :stop do
+ let(:request_params) { environment_params(format: :json) }
+ let(:target_id) { 'users_visiting_environments_pages' }
+ end
end
context 'when no stop action' do
@@ -321,6 +404,11 @@ RSpec.describe Projects::EnvironmentsController do
it_behaves_like 'successful response for #cancel_auto_stop'
+ it_behaves_like 'tracking unique visits', :cancel_auto_stop do
+ let(:request_params) { environment_params }
+ let(:target_id) { 'users_visiting_environments_pages' }
+ end
+
context 'when user is reporter' do
let(:user) { reporter }
@@ -357,6 +445,11 @@ RSpec.describe Projects::EnvironmentsController do
get :terminal, params: environment_params
end
+
+ it_behaves_like 'tracking unique visits', :terminal do
+ let(:request_params) { environment_params }
+ let(:target_id) { 'users_visiting_environments_pages' }
+ end
end
context 'with invalid id' do
@@ -859,6 +952,11 @@ RSpec.describe Projects::EnvironmentsController do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['path']).to eq("/#{project.full_path}/-/environments/#{json_response['environment']['id']}")
end
+
+ it_behaves_like 'tracking unique visits', :create do
+ let(:request_params) { params }
+ let(:target_id) { 'users_visiting_environments_pages' }
+ end
end
context "when environment params are invalid" do
diff --git a/spec/controllers/projects/feature_flags_controller_spec.rb b/spec/controllers/projects/feature_flags_controller_spec.rb
index fd95aa44568..29ad51d590f 100644
--- a/spec/controllers/projects/feature_flags_controller_spec.rb
+++ b/spec/controllers/projects/feature_flags_controller_spec.rb
@@ -194,7 +194,7 @@ RSpec.describe Projects::FeatureFlagsController do
other_project = create(:project)
other_project.add_developer(user)
other_feature_flag = create(:operations_feature_flag, project: other_project,
- name: 'other_flag')
+ name: 'other_flag')
params = {
namespace_id: other_project.namespace,
project_id: other_project,
@@ -208,7 +208,7 @@ RSpec.describe Projects::FeatureFlagsController do
end
context 'when feature flag is not found' do
- let!(:feature_flag) { }
+ let!(:feature_flag) {}
let(:params) do
{
@@ -486,7 +486,7 @@ RSpec.describe Projects::FeatureFlagsController do
context 'when creating a version 2 feature flag with a gitlabUserList strategy' do
let!(:user_list) do
create(:operations_feature_flag_user_list, project: project,
- name: 'My List', user_xids: 'user1,user2')
+ name: 'My List', user_xids: 'user1,user2')
end
let(:params) do
diff --git a/spec/controllers/projects/grafana_api_controller_spec.rb b/spec/controllers/projects/grafana_api_controller_spec.rb
index baee9705127..2e25b0271ce 100644
--- a/spec/controllers/projects/grafana_api_controller_spec.rb
+++ b/spec/controllers/projects/grafana_api_controller_spec.rb
@@ -52,8 +52,8 @@ RSpec.describe Projects::GrafanaApiController do
.with(project, '1', 'api/v1/query_range',
{ 'query' => params[:query],
'start' => params[:start_time],
- 'end' => params[:end_time],
- 'step' => params[:step] })
+ 'end' => params[:end_time],
+ 'step' => params[:step] })
expect(response).to have_gitlab_http_status(:ok)
expect(json_response).to eq({})
diff --git a/spec/controllers/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb
index be89fa0d361..9227c7dd70a 100644
--- a/spec/controllers/projects/graphs_controller_spec.rb
+++ b/spec/controllers/projects/graphs_controller_spec.rb
@@ -89,6 +89,21 @@ RSpec.describe Projects::GraphsController do
let(:request_params) { { namespace_id: project.namespace.path, project_id: project.path, id: 'master' } }
let(:target_id) { 'p_analytics_repo' }
end
+
+ it_behaves_like 'Snowplow event tracking' do
+ subject do
+ sign_in(user)
+ get :charts, params: request_params, format: :html
+ end
+
+ let(:request_params) { { namespace_id: project.namespace.path, project_id: project.path, id: 'master' } }
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:category) { described_class.name }
+ let(:action) { 'perform_analytics_usage_action' }
+ let(:namespace) { project.namespace }
+ let(:label) { 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly' }
+ let(:property) { 'p_analytics_repo' }
+ end
end
context 'when languages were previously detected' do
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index e4e3151dd12..556dd23c135 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -5,9 +5,25 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
include ApiHelpers
include HttpIOHelpers
- let(:project) { create(:project, :public, :repository) }
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:owner) { create(:owner) }
+ let_it_be(:admin) { create(:admin) }
+ let_it_be(:maintainer) { create(:user) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:reporter) { create(:user) }
+ let_it_be(:guest) { create(:user) }
+
+ before_all do
+ project.add_owner(owner)
+ project.add_maintainer(maintainer)
+ project.add_developer(developer)
+ project.add_reporter(reporter)
+ project.add_guest(guest)
+ end
+
+ let(:user) { developer }
+
let(:pipeline) { create(:ci_pipeline, project: project) }
- let(:user) { create(:user) }
before do
stub_feature_flags(ci_enable_live_trace: true)
@@ -136,9 +152,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
context 'when requesting JSON' do
let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:user) { developer }
before do
- project.add_developer(user)
sign_in(user)
allow_any_instance_of(Ci::Build)
@@ -307,9 +323,10 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
let(:environment) { create(:environment, project: project, name: 'staging', state: :available) }
let(:job) { create(:ci_build, :running, environment: environment.name, pipeline: pipeline) }
+ let(:user) { maintainer }
+
before do
create(:deployment, :success, :on_cluster, environment: environment, project: project)
- project.add_maintainer(user) # Need to be a maintianer to view cluster.path
end
it 'exposes the deployment information' do
@@ -330,9 +347,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
context 'that belongs to the project' do
let(:runner) { create(:ci_runner, :project, projects: [project]) }
let(:job) { create(:ci_build, :success, pipeline: pipeline, runner: runner) }
+ let(:user) { maintainer }
before do
- project.add_maintainer(user)
sign_in(user)
end
@@ -349,10 +366,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
let(:group) { create(:group) }
let(:runner) { create(:ci_runner, :group, groups: [group]) }
let(:job) { create(:ci_build, :success, pipeline: pipeline, runner: runner) }
- let(:user) { create(:user, :admin) }
+ let(:user) { maintainer }
before do
- project.add_maintainer(user)
sign_in(user)
end
@@ -368,10 +384,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
context 'that belongs to instance' do
let(:runner) { create(:ci_runner, :instance) }
let(:job) { create(:ci_build, :success, pipeline: pipeline, runner: runner) }
- let(:user) { create(:user, :admin) }
+ let(:user) { maintainer }
before do
- project.add_maintainer(user)
sign_in(user)
end
@@ -421,6 +436,8 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
context 'when user is developer' do
+ let(:user) { developer }
+
it 'settings_path is not available' do
expect(response).to have_gitlab_http_status(:ok)
expect(response).to match_response_schema('job/job_details')
@@ -429,10 +446,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
context 'when user is maintainer' do
- let(:user) { create(:user, :admin) }
+ let(:user) { admin }
before do
- project.add_maintainer(user)
sign_in(user)
end
@@ -499,9 +515,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
let(:trigger) { create(:ci_trigger, project: project) }
let(:trigger_request) { create(:ci_trigger_request, pipeline: pipeline, trigger: trigger) }
let(:job) { create(:ci_build, pipeline: pipeline, trigger_request: trigger_request) }
+ let(:user) { developer }
before do
- project.add_developer(user)
sign_in(user)
allow_any_instance_of(Ci::Build)
@@ -526,9 +542,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
context 'user is a maintainer' do
- before do
- project.add_maintainer(user)
+ let(:user) { maintainer }
+ before do
get_show_json
end
@@ -579,7 +595,7 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
def get_show_json
expect { get_show(id: job.id, format: :json) }
- .to change { Gitlab::GitalyClient.get_request_count }.by_at_most(2)
+ .to change { Gitlab::GitalyClient.get_request_count }.by_at_most(3)
end
def get_show(**extra_params)
@@ -617,8 +633,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
let!(:variable) { create(:ci_instance_variable, key: 'CI_DEBUG_TRACE', value: 'true') }
context 'with proper permissions on a project' do
+ let(:user) { developer }
+
before do
- project.add_developer(user)
sign_in(user)
end
@@ -630,8 +647,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
context 'without proper permissions for debug logging' do
+ let(:user) { guest }
+
before do
- project.add_guest(user)
sign_in(user)
end
@@ -700,7 +718,7 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
expect(response).to match_response_schema('job/build_trace')
expect(json_response['id']).to eq job.id
expect(json_response['status']).to eq job.status
- expect(json_response['lines'].flat_map {|l| l['content'].map { |c| c['text'] } }).to include("ヾ(´༎ຶД༎ຶ`)ノ")
+ expect(json_response['lines'].flat_map { |l| l['content'].map { |c| c['text'] } }).to include("ヾ(´༎ຶД༎ຶ`)ノ")
end
end
@@ -753,8 +771,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
describe 'POST retry' do
+ let(:user) { developer }
+
before do
- project.add_developer(user)
sign_in(user)
end
@@ -817,12 +836,13 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
describe 'POST play' do
let(:variable_attributes) { [] }
+ let(:user) { developer }
before do
project.add_developer(user)
create(:protected_branch, :developers_can_merge,
- name: 'master', project: project)
+ name: 'protected-branch', project: project)
sign_in(user)
end
@@ -899,8 +919,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
describe 'POST cancel' do
context 'when user is authorized to cancel the build' do
+ let(:user) { developer }
+
before do
- project.add_developer(user)
sign_in(user)
end
@@ -965,8 +986,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
context 'when user is not authorized to cancel the build' do
let!(:job) { create(:ci_build, :cancelable, pipeline: pipeline) }
+ let(:user) { guest }
+
before do
- project.add_reporter(user)
sign_in(user)
post_cancel
@@ -990,12 +1012,13 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
describe 'POST unschedule' do
before do
- create(:protected_branch, :developers_can_merge, name: 'master', project: project)
+ create(:protected_branch, :developers_can_merge, name: 'protected-branch', project: project)
end
context 'when user is authorized to unschedule the build' do
+ let(:user) { developer }
+
before do
- project.add_developer(user)
sign_in(user)
post_unschedule
@@ -1025,9 +1048,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
context 'when user is not authorized to unschedule the build' do
let(:job) { create(:ci_build, :scheduled, pipeline: pipeline) }
+ let(:user) { guest }
before do
- project.add_reporter(user)
sign_in(user)
post_unschedule
@@ -1048,10 +1071,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
describe 'POST erase' do
- let(:role) { :maintainer }
+ let(:user) { maintainer }
before do
- project.add_role(user, role)
sign_in(user)
end
@@ -1097,7 +1119,7 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
context 'when user is developer' do
- let(:role) { :developer }
+ let(:user) { developer }
let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline, user: triggered_by) }
context 'when triggered by same user' do
@@ -1109,7 +1131,7 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
context 'when triggered by different user' do
- let(:triggered_by) { create(:user) }
+ let(:triggered_by) { maintainer }
it 'does not have successful status' do
expect(response).not_to have_gitlab_http_status(:found)
@@ -1168,8 +1190,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
context 'with proper permissions for debug logging on a project' do
+ let(:user) { developer }
+
before do
- project.add_developer(user)
sign_in(user)
end
@@ -1181,8 +1204,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
context 'without proper permissions for debug logging on a project' do
+ let(:user) { reporter }
+
before do
- project.add_reporter(user)
sign_in(user)
end
@@ -1218,37 +1242,6 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
end
- context "when job has a trace in database" do
- let(:job) { create(:ci_build, pipeline: pipeline) }
-
- before do
- job.update_column(:trace, "Sample trace")
- end
-
- it 'sends a trace file' do
- response = subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.headers['Content-Type']).to eq('text/plain; charset=utf-8')
- expect(response.headers['Content-Disposition']).to match(/^inline/)
- expect(response.body).to eq('Sample trace')
- end
-
- context 'when trace format is not text/plain' do
- before do
- job.update_column(:trace, '<html></html>')
- end
-
- it 'sets content disposition to attachment' do
- response = subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.headers['Content-Type']).to eq('text/plain; charset=utf-8')
- expect(response.headers['Content-Disposition']).to match(/^attachment/)
- end
- end
- end
-
context 'when job does not have a trace file' do
let(:job) { create(:ci_build, pipeline: pipeline) }
@@ -1274,8 +1267,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
describe 'GET #terminal' do
+ let(:user) { developer }
+
before do
- project.add_developer(user)
sign_in(user)
end
@@ -1323,8 +1317,9 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
describe 'GET #terminal_websocket_authorize' do
let!(:job) { create(:ci_build, :running, :with_runner_session, pipeline: pipeline, user: user) }
+ let(:user) { developer }
+
before do
- project.add_developer(user)
sign_in(user)
end
@@ -1375,14 +1370,6 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
end
describe 'GET #proxy_websocket_authorize' do
- let_it_be(:owner) { create(:owner) }
- let_it_be(:admin) { create(:admin) }
- let_it_be(:maintainer) { create(:user) }
- let_it_be(:developer) { create(:user) }
- let_it_be(:reporter) { create(:user) }
- let_it_be(:guest) { create(:user) }
- let_it_be(:project) { create(:project, :private, :repository, namespace: owner.namespace) }
-
let(:user) { maintainer }
let(:pipeline) { create(:ci_pipeline, project: project, source: :webide, config_source: :webide_source, user: user) }
let(:job) { create(:ci_build, :running, :with_runner_session, pipeline: pipeline, user: user) }
@@ -1407,11 +1394,6 @@ RSpec.describe Projects::JobsController, :clean_gitlab_redis_shared_state do
stub_feature_flags(build_service_proxy: true)
allow(job).to receive(:has_terminal?).and_return(true)
- project.add_maintainer(maintainer)
- project.add_developer(developer)
- project.add_reporter(reporter)
- project.add_guest(guest)
-
sign_in(user)
end
diff --git a/spec/controllers/projects/labels_controller_spec.rb b/spec/controllers/projects/labels_controller_spec.rb
index 776ed9774b1..a5259522fe2 100644
--- a/spec/controllers/projects/labels_controller_spec.rb
+++ b/spec/controllers/projects/labels_controller_spec.rb
@@ -25,10 +25,10 @@ RSpec.describe Projects::LabelsController do
let_it_be(:group_label_3) { create(:group_label, group: group, title: 'Group Label 3') }
let_it_be(:group_label_4) { create(:group_label, group: group, title: 'Group Label 4') }
- let_it_be(:group_labels) { [group_label_3, group_label_4]}
- let_it_be(:project_labels) { [label_4, label_5]}
- let_it_be(:group_priority_labels) { [group_label_1, group_label_2]}
- let_it_be(:project_priority_labels) { [label_1, label_2, label_3]}
+ let_it_be(:group_labels) { [group_label_3, group_label_4] }
+ let_it_be(:project_labels) { [label_4, label_5] }
+ let_it_be(:group_priority_labels) { [group_label_1, group_label_2] }
+ let_it_be(:project_priority_labels) { [label_1, label_2, label_3] }
before do
create(:label_priority, project: project, label: group_label_1, priority: 3)
diff --git a/spec/controllers/projects/merge_requests/drafts_controller_spec.rb b/spec/controllers/projects/merge_requests/drafts_controller_spec.rb
index b9ede84157d..182d654aaa8 100644
--- a/spec/controllers/projects/merge_requests/drafts_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/drafts_controller_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe Projects::MergeRequests::DraftsController do
include RepoHelpers
let(:project) { create(:project, :repository) }
- let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project) }
+ let(:merge_request) { create(:merge_request_with_diffs, target_project: project, source_project: project, author: create(:user)) }
let(:user) { project.first_owner }
let(:user2) { create(:user) }
@@ -404,6 +404,11 @@ RSpec.describe Projects::MergeRequests::DraftsController do
end
context 'when feature flag is enabled' do
+ before do
+ allow(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
+ .to receive(:track_submit_review_comment)
+ end
+
it 'creates note' do
post :publish, params: params.merge!(note: 'Hello world')
@@ -415,6 +420,72 @@ RSpec.describe Projects::MergeRequests::DraftsController do
expect(merge_request.notes.reload.size).to be(1)
end
+
+ it 'tracks merge request activity' do
+ post :publish, params: params.merge!(note: 'Hello world')
+
+ expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
+ .to have_received(:track_submit_review_comment).with(user: user)
+ end
+ end
+ end
+
+ context 'approve merge request' do
+ before do
+ create(:draft_note, merge_request: merge_request, author: user)
+ end
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(mr_review_submit_comment: false)
+ end
+
+ it 'does not approve' do
+ post :publish, params: params.merge!(approve: true)
+
+ expect(merge_request.approvals.reload.size).to be(0)
+ end
+ end
+
+ context 'when feature flag is enabled' do
+ before do
+ allow(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
+ .to receive(:track_submit_review_approve)
+ end
+
+ it 'approves merge request' do
+ post :publish, params: params.merge!(approve: true)
+
+ expect(merge_request.approvals.reload.size).to be(1)
+ end
+
+ it 'does not approve merge request' do
+ post :publish, params: params.merge!(approve: false)
+
+ expect(merge_request.approvals.reload.size).to be(0)
+ end
+
+ it 'tracks merge request activity' do
+ post :publish, params: params.merge!(approve: true)
+
+ expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
+ .to have_received(:track_submit_review_approve).with(user: user)
+ end
+
+ context 'when merge request is already approved by user' do
+ before do
+ create(:approval, merge_request: merge_request, user: user)
+ end
+
+ it 'does return 200' do
+ post :publish, params: params.merge!(approve: true)
+
+ expect(response).to have_gitlab_http_status(:ok)
+
+ expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
+ .to have_received(:track_submit_review_approve).with(user: user)
+ end
+ end
end
end
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index ed5e32df8ea..9c4baeae836 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -896,12 +896,13 @@ RSpec.describe Projects::MergeRequestsController do
end
subject do
- get :exposed_artifacts, params: {
- namespace_id: project.namespace.to_param,
- project_id: project,
- id: merge_request.iid
- },
- format: :json
+ get :exposed_artifacts,
+ params: {
+ namespace_id: project.namespace.to_param,
+ project_id: project,
+ id: merge_request.iid
+ },
+ format: :json
end
describe 'permissions on a public project with private CI/CD' do
@@ -1031,12 +1032,13 @@ RSpec.describe Projects::MergeRequestsController do
end
subject do
- get :coverage_reports, params: {
- namespace_id: project.namespace.to_param,
- project_id: project,
- id: merge_request.iid
- },
- format: :json
+ get :coverage_reports,
+ params: {
+ namespace_id: project.namespace.to_param,
+ project_id: project,
+ id: merge_request.iid
+ },
+ format: :json
end
describe 'permissions on a public project with private CI/CD' do
@@ -1161,12 +1163,13 @@ RSpec.describe Projects::MergeRequestsController do
end
subject(:get_codequality_mr_diff_reports) do
- get :codequality_mr_diff_reports, params: {
- namespace_id: project.namespace.to_param,
- project_id: project,
- id: merge_request.iid
- },
- format: :json
+ get :codequality_mr_diff_reports,
+ params: {
+ namespace_id: project.namespace.to_param,
+ project_id: project,
+ id: merge_request.iid
+ },
+ format: :json
end
context 'permissions on a public project with private CI/CD' do
@@ -1264,12 +1267,13 @@ RSpec.describe Projects::MergeRequestsController do
end
subject do
- get :terraform_reports, params: {
- namespace_id: project.namespace.to_param,
- project_id: project,
- id: merge_request.iid
- },
- format: :json
+ get :terraform_reports,
+ params: {
+ namespace_id: project.namespace.to_param,
+ project_id: project,
+ id: merge_request.iid
+ },
+ format: :json
end
describe 'permissions on a public project with private CI/CD' do
@@ -1394,12 +1398,13 @@ RSpec.describe Projects::MergeRequestsController do
end
subject do
- get :test_reports, params: {
- namespace_id: project.namespace.to_param,
- project_id: project,
- id: merge_request.iid
- },
- format: :json
+ get :test_reports,
+ params: {
+ namespace_id: project.namespace.to_param,
+ project_id: project,
+ id: merge_request.iid
+ },
+ format: :json
end
before do
@@ -1522,12 +1527,13 @@ RSpec.describe Projects::MergeRequestsController do
end
subject do
- get :accessibility_reports, params: {
- namespace_id: project.namespace.to_param,
- project_id: project,
- id: merge_request.iid
- },
- format: :json
+ get :accessibility_reports,
+ params: {
+ namespace_id: project.namespace.to_param,
+ project_id: project,
+ id: merge_request.iid
+ },
+ format: :json
end
context 'permissions on a public project with private CI/CD' do
@@ -1642,12 +1648,13 @@ RSpec.describe Projects::MergeRequestsController do
end
subject do
- get :codequality_reports, params: {
- namespace_id: project.namespace.to_param,
- project_id: project,
- id: merge_request.iid
- },
- format: :json
+ get :codequality_reports,
+ params: {
+ namespace_id: project.namespace.to_param,
+ project_id: project,
+ id: merge_request.iid
+ },
+ format: :json
end
context 'permissions on a public project with private CI/CD' do
diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb
index 9050765afd6..1f8e96258ca 100644
--- a/spec/controllers/projects/notes_controller_spec.rb
+++ b/spec/controllers/projects/notes_controller_spec.rb
@@ -659,7 +659,7 @@ RSpec.describe Projects::NotesController do
context 'when target_id and noteable_id do not match' do
let(:locked_issue) { create(:issue, :locked, project: project) }
- let(:issue) {create(:issue, project: project)}
+ let(:issue) { create(:issue, project: project) }
it 'uses target_id and ignores noteable_id' do
request_params = {
diff --git a/spec/controllers/projects/pipeline_schedules_controller_spec.rb b/spec/controllers/projects/pipeline_schedules_controller_spec.rb
index 77acd5fe13c..5bcfae4227c 100644
--- a/spec/controllers/projects/pipeline_schedules_controller_spec.rb
+++ b/spec/controllers/projects/pipeline_schedules_controller_spec.rb
@@ -354,7 +354,8 @@ RSpec.describe Projects::PipelineSchedulesController do
end
def go
- put :update, params: {
+ put :update,
+ params: {
namespace_id: project.namespace.to_param,
project_id: project,
id: pipeline_schedule,
diff --git a/spec/controllers/projects/pipelines/tests_controller_spec.rb b/spec/controllers/projects/pipelines/tests_controller_spec.rb
index ddcab8b048e..07e3c28f685 100644
--- a/spec/controllers/projects/pipelines/tests_controller_spec.rb
+++ b/spec/controllers/projects/pipelines/tests_controller_spec.rb
@@ -86,11 +86,12 @@ RSpec.describe Projects::Pipelines::TestsController do
# Each test failure in this pipeline has a matching failure in the default branch
recent_failures = json_response['test_cases'].map { |tc| tc['recent_failures'] }
- expect(recent_failures).to eq([
- { 'count' => 1, 'base_branch' => 'master' },
- { 'count' => 1, 'base_branch' => 'master' },
- { 'count' => 1, 'base_branch' => 'master' }
- ])
+ expect(recent_failures).to eq(
+ [
+ { 'count' => 1, 'base_branch' => 'master' },
+ { 'count' => 1, 'base_branch' => 'master' },
+ { 'count' => 1, 'base_branch' => 'master' }
+ ])
end
end
end
diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb
index 06930d8727b..b9acaf65892 100644
--- a/spec/controllers/projects/pipelines_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_controller_spec.rb
@@ -270,9 +270,13 @@ RSpec.describe Projects::PipelinesController do
user: user,
merge_request: merge_request)
- create_build(pipeline, 'build', 1, 'build', user)
- create_build(pipeline, 'test', 2, 'test', user)
- create_build(pipeline, 'deploy', 3, 'deploy', user)
+ build_stage = create(:ci_stage, name: 'build', pipeline: pipeline)
+ test_stage = create(:ci_stage, name: 'test', pipeline: pipeline)
+ deploy_stage = create(:ci_stage, name: 'deploy', pipeline: pipeline)
+
+ create_build(pipeline, build_stage, 1, 'build', user)
+ create_build(pipeline, test_stage, 2, 'test', user)
+ create_build(pipeline, deploy_stage, 3, 'deploy', user)
pipeline
end
@@ -284,7 +288,7 @@ RSpec.describe Projects::PipelinesController do
:artifacts,
artifacts_expire_at: 2.days.from_now,
pipeline: pipeline,
- stage: stage,
+ ci_stage: stage,
stage_idx: stage_idx,
name: name,
status: status,
@@ -327,21 +331,24 @@ RSpec.describe Projects::PipelinesController do
render_views
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:build_stage) { create(:ci_stage, name: 'build', pipeline: pipeline) }
+ let_it_be(:test_stage) { create(:ci_stage, name: 'test', pipeline: pipeline) }
+ let_it_be(:deploy_stage) { create(:ci_stage, name: 'deploy', pipeline: pipeline) }
def create_build_with_artifacts(stage, stage_idx, name, status)
- create(:ci_build, :artifacts, :tags, status, user: user, pipeline: pipeline, stage: stage, stage_idx: stage_idx, name: name)
+ create(:ci_build, :artifacts, :tags, status, user: user, pipeline: pipeline, ci_stage: stage, stage_idx: stage_idx, name: name)
end
def create_bridge(stage, stage_idx, name, status)
- create(:ci_bridge, status, pipeline: pipeline, stage: stage, stage_idx: stage_idx, name: name)
+ create(:ci_bridge, status, pipeline: pipeline, ci_stage: stage, stage_idx: stage_idx, name: name)
end
before do
- create_build_with_artifacts('build', 0, 'job1', :failed)
- create_build_with_artifacts('build', 0, 'job2', :running)
- create_build_with_artifacts('build', 0, 'job3', :pending)
- create_bridge('deploy', 1, 'deploy-a', :failed)
- create_bridge('deploy', 1, 'deploy-b', :created)
+ create_build_with_artifacts(build_stage, 0, 'job1', :failed)
+ create_build_with_artifacts(build_stage, 0, 'job2', :running)
+ create_build_with_artifacts(build_stage, 0, 'job3', :pending)
+ create_bridge(deploy_stage, 1, 'deploy-a', :failed)
+ create_bridge(deploy_stage, 1, 'deploy-b', :created)
end
it 'avoids N+1 database queries', :request_store, :use_sql_query_cache do
@@ -354,11 +361,11 @@ RSpec.describe Projects::PipelinesController do
expect(response).to have_gitlab_http_status(:ok)
end
- create_build_with_artifacts('build', 0, 'job4', :failed)
- create_build_with_artifacts('build', 0, 'job5', :running)
- create_build_with_artifacts('build', 0, 'job6', :pending)
- create_bridge('deploy', 1, 'deploy-c', :failed)
- create_bridge('deploy', 1, 'deploy-d', :created)
+ create_build_with_artifacts(build_stage, 0, 'job4', :failed)
+ create_build_with_artifacts(build_stage, 0, 'job5', :running)
+ create_build_with_artifacts(build_stage, 0, 'job6', :pending)
+ create_bridge(deploy_stage, 1, 'deploy-c', :failed)
+ create_bridge(deploy_stage, 1, 'deploy-d', :created)
expect do
get_pipeline_html
@@ -402,11 +409,16 @@ RSpec.describe Projects::PipelinesController do
sha: project.commit.id)
end
+ let(:build_stage) { create(:ci_stage, name: 'build', pipeline: pipeline) }
+ let(:test_stage) { create(:ci_stage, name: 'test', pipeline: pipeline) }
+ let(:deploy_stage) { create(:ci_stage, name: 'deploy', pipeline: pipeline) }
+ let(:post_deploy_stage) { create(:ci_stage, name: 'post deploy', pipeline: pipeline) }
+
before do
- create_build('build', 0, 'build')
- create_build('test', 1, 'rspec 0')
- create_build('deploy', 2, 'production')
- create_build('post deploy', 3, 'pages 0')
+ create_build(build_stage, 0, 'build')
+ create_build(test_stage, 1, 'rspec 0')
+ create_build(deploy_stage, 2, 'production')
+ create_build(post_deploy_stage, 3, 'pages 0')
end
it 'does not perform N + 1 queries' do
@@ -612,7 +624,9 @@ RSpec.describe Projects::PipelinesController do
def create_pipeline(project)
create(:ci_empty_pipeline, project: project).tap do |pipeline|
- create(:ci_build, pipeline: pipeline, stage: 'test', name: 'rspec')
+ create(:ci_build, pipeline: pipeline,
+ ci_stage: create(:ci_stage, name: 'test', pipeline: pipeline),
+ name: 'rspec')
end
end
@@ -642,7 +656,7 @@ RSpec.describe Projects::PipelinesController do
end
def create_build(stage, stage_idx, name)
- create(:ci_build, pipeline: pipeline, stage: stage, stage_idx: stage_idx, name: name)
+ create(:ci_build, pipeline: pipeline, ci_stage: stage, stage_idx: stage_idx, name: name)
end
end
@@ -654,10 +668,12 @@ RSpec.describe Projects::PipelinesController do
describe 'GET dag.json' do
let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build_stage) { create(:ci_stage, name: 'build', pipeline: pipeline) }
+ let(:test_stage) { create(:ci_stage, name: 'test', pipeline: pipeline) }
before do
- create_build('build', 1, 'build')
- create_build('test', 2, 'test', scheduling_type: 'dag').tap do |job|
+ create_build(build_stage, 1, 'build')
+ create_build(test_stage, 2, 'test', scheduling_type: 'dag').tap do |job|
create(:ci_build_need, build: job, name: 'build')
end
end
@@ -681,7 +697,7 @@ RSpec.describe Projects::PipelinesController do
end
def create_build(stage, stage_idx, name, params = {})
- create(:ci_build, pipeline: pipeline, stage: stage, stage_idx: stage_idx, name: name, **params)
+ create(:ci_build, pipeline: pipeline, ci_stage: stage, stage_idx: stage_idx, name: name, **params)
end
end
@@ -730,11 +746,12 @@ RSpec.describe Projects::PipelinesController do
describe 'GET stages.json' do
let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build_stage) { create(:ci_stage, name: 'build', pipeline: pipeline) }
context 'when accessing existing stage' do
before do
- create(:ci_build, :retried, :failed, pipeline: pipeline, stage: 'build')
- create(:ci_build, pipeline: pipeline, stage: 'build')
+ create(:ci_build, :retried, :failed, pipeline: pipeline, ci_stage: build_stage)
+ create(:ci_build, pipeline: pipeline, ci_stage: build_stage)
end
context 'without retried' do
@@ -841,6 +858,18 @@ RSpec.describe Projects::PipelinesController do
let(:request_params) { { namespace_id: project.namespace, project_id: project, id: pipeline.id, chart: tab[:chart_param] } }
let(:target_id) { ['p_analytics_pipelines', tab[:event]] }
end
+
+ it_behaves_like 'Snowplow event tracking' do
+ subject { get :charts, params: request_params, format: :html }
+
+ let(:request_params) { { namespace_id: project.namespace, project_id: project, id: pipeline.id, chart: tab[:chart_param] } }
+ let(:feature_flag_name) { :route_hll_to_snowplow_phase2 }
+ let(:category) { described_class.name }
+ let(:action) { 'perform_analytics_usage_action' }
+ let(:namespace) { project.namespace }
+ let(:label) { 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly' }
+ let(:property) { 'p_analytics_pipelines' }
+ end
end
end
@@ -965,8 +994,8 @@ RSpec.describe Projects::PipelinesController do
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['errors']).to eq([
- 'test job: chosen stage does not exist; available stages are .pre, build, test, deploy, .post'
- ])
+ 'test job: chosen stage does not exist; available stages are .pre, build, test, deploy, .post'
+ ])
expect(json_response['warnings'][0]).to include(
'jobs:build may allow multiple pipelines to run for a single action due to `rules:when`'
)
diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb
index 46eb340cbba..fb27fe58cd9 100644
--- a/spec/controllers/projects/project_members_controller_spec.rb
+++ b/spec/controllers/projects/project_members_controller_spec.rb
@@ -5,6 +5,7 @@ require('spec_helper')
RSpec.describe Projects::ProjectMembersController do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group, :public) }
+ let_it_be(:sub_group) { create(:group, parent: group) }
let_it_be(:project, reload: true) { create(:project, :public) }
before do
@@ -52,7 +53,36 @@ RSpec.describe Projects::ProjectMembersController do
end
end
- context 'when invited members are present' do
+ context 'when project belongs to a sub-group' do
+ let_it_be(:user_in_group) { create(:user) }
+ let_it_be(:project_in_group) { create(:project, :public, group: sub_group) }
+
+ before do
+ group.add_owner(user_in_group)
+ project_in_group.add_maintainer(user)
+ sign_in(user)
+ end
+
+ it 'lists inherited project members by default' do
+ get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group }
+
+ expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id, user_in_group.id)
+ end
+
+ it 'lists direct project members only' do
+ get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'exclude' }
+
+ expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user.id)
+ end
+
+ it 'lists inherited project members only' do
+ get :index, params: { namespace_id: project_in_group.namespace, project_id: project_in_group, with_inherited_permissions: 'only' }
+
+ expect(assigns(:project_members).map(&:user_id)).to contain_exactly(user_in_group.id)
+ end
+ end
+
+ context 'when invited project members are present' do
let!(:invited_member) { create(:project_member, :invited, project: project) }
before do
diff --git a/spec/controllers/projects/registry/tags_controller_spec.rb b/spec/controllers/projects/registry/tags_controller_spec.rb
index c03a280d2cd..7b786f4a8af 100644
--- a/spec/controllers/projects/registry/tags_controller_spec.rb
+++ b/spec/controllers/projects/registry/tags_controller_spec.rb
@@ -167,7 +167,7 @@ RSpec.describe Projects::Registry::TagsController do
repository_id: repository,
ids: names
},
- format: :json
+ format: :json
end
end
diff --git a/spec/controllers/projects/releases_controller_spec.rb b/spec/controllers/projects/releases_controller_spec.rb
index ad6682601f3..b307bb357fa 100644
--- a/spec/controllers/projects/releases_controller_spec.rb
+++ b/spec/controllers/projects/releases_controller_spec.rb
@@ -312,7 +312,7 @@ RSpec.describe Projects::ReleasesController do
end
context 'suffix path abuse' do
- let(:suffix_path) { 'downloads/zips/../../../../../../../robots.txt'}
+ let(:suffix_path) { 'downloads/zips/../../../../../../../robots.txt' }
it 'raises attack error' do
expect do
diff --git a/spec/controllers/projects/service_desk_controller_spec.rb b/spec/controllers/projects/service_desk_controller_spec.rb
index 1c4d6665414..e078bf9461e 100644
--- a/spec/controllers/projects/service_desk_controller_spec.rb
+++ b/spec/controllers/projects/service_desk_controller_spec.rb
@@ -4,8 +4,9 @@ require 'spec_helper'
RSpec.describe Projects::ServiceDeskController do
let_it_be(:project) do
- create(:project, :private, :custom_repo, service_desk_enabled: true,
- files: { '.gitlab/issue_templates/service_desk.md' => 'template' })
+ create(:project, :private, :custom_repo,
+ service_desk_enabled: true,
+ files: { '.gitlab/issue_templates/service_desk.md' => 'template' })
end
let_it_be(:user) { create(:user) }
diff --git a/spec/controllers/projects/settings/integrations_controller_spec.rb b/spec/controllers/projects/settings/integrations_controller_spec.rb
index 8ee9f22aa7f..b76269f6f93 100644
--- a/spec/controllers/projects/settings/integrations_controller_spec.rb
+++ b/spec/controllers/projects/settings/integrations_controller_spec.rb
@@ -50,7 +50,7 @@ RSpec.describe Projects::Settings::IntegrationsController do
end
end
- context 'when validations fail' do
+ context 'when validations fail', :clean_gitlab_redis_rate_limiting do
let(:integration_params) { { active: 'true', url: '' } }
it 'returns error messages in JSON response' do
@@ -62,7 +62,7 @@ RSpec.describe Projects::Settings::IntegrationsController do
end
end
- context 'when successful' do
+ context 'when successful', :clean_gitlab_redis_rate_limiting do
context 'with empty project' do
let_it_be(:project) { create(:project) }
@@ -200,8 +200,8 @@ RSpec.describe Projects::Settings::IntegrationsController do
2.times { post :test, params: project_params(service: integration_params) }
- expect(response.body).to eq(_('This endpoint has been requested too many times. Try again later.'))
- expect(response).to have_gitlab_http_status(:too_many_requests)
+ expect(response.body).to include(_('This endpoint has been requested too many times. Try again later.'))
+ expect(response).to have_gitlab_http_status(:ok)
end
end
end
diff --git a/spec/controllers/projects/settings/merge_requests_controller_spec.rb b/spec/controllers/projects/settings/merge_requests_controller_spec.rb
new file mode 100644
index 00000000000..106ec62bea0
--- /dev/null
+++ b/spec/controllers/projects/settings/merge_requests_controller_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::Settings::MergeRequestsController do
+ let(:project) { create(:project_empty_repo, :public) }
+ let(:user) { create(:user) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+
+ describe 'GET show' do
+ it 'renders show with 200 status code' do
+ get :show, params: { namespace_id: project.namespace, project_id: project }
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template(:show)
+ end
+ end
+
+ describe '#update', :enable_admin_mode do
+ render_views
+
+ let(:admin) { create(:admin) }
+
+ before do
+ sign_in(admin)
+ end
+
+ it 'updates Fast Forward Merge attributes' do
+ controller.instance_variable_set(:@project, project)
+
+ params = {
+ merge_method: :ff
+ }
+
+ put :update,
+ params: {
+ namespace_id: project.namespace,
+ project_id: project.id,
+ project: params
+ }
+
+ expect(response).to redirect_to project_settings_merge_requests_path(project)
+ params.each do |param, value|
+ expect(project.public_send(param)).to eq(value)
+ end
+ end
+ end
+end
diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb
index 143516e4712..9bc3065b6da 100644
--- a/spec/controllers/projects/tree_controller_spec.rb
+++ b/spec/controllers/projects/tree_controller_spec.rb
@@ -163,8 +163,8 @@ RSpec.describe Projects::TreeController do
end
context 'successful creation' do
- let(:path) { 'files/new_dir'}
- let(:branch_name) { 'master-test'}
+ let(:path) { 'files/new_dir' }
+ let(:branch_name) { 'master-test' }
it 'redirects to the new directory' do
expect(subject)
@@ -175,7 +175,7 @@ RSpec.describe Projects::TreeController do
context 'unsuccessful creation' do
let(:path) { 'README.md' }
- let(:branch_name) { 'master'}
+ let(:branch_name) { 'master' }
it 'does not allow overwriting of existing files' do
expect(subject)
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 94d75ab8d7d..b30610d98d7 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -920,6 +920,7 @@ RSpec.describe ProjectsController do
environments_access_level
feature_flags_access_level
releases_access_level
+ monitor_access_level
]
end
@@ -947,6 +948,7 @@ RSpec.describe ProjectsController do
where(:feature_access_level) do
%i[
environments_access_level feature_flags_access_level
+ monitor_access_level
]
end
@@ -1217,6 +1219,40 @@ RSpec.describe ProjectsController do
expect(json_response["Commits"]).to include("123456")
end
+ it 'uses gitaly pagination' do
+ expected_params = ActionController::Parameters.new(ref: '123456', per_page: 100).permit!
+
+ expect_next_instance_of(BranchesFinder, project.repository, expected_params) do |finder|
+ expect(finder).to receive(:execute).with(gitaly_pagination: true).and_call_original
+ end
+
+ expect_next_instance_of(TagsFinder, project.repository, expected_params) do |finder|
+ expect(finder).to receive(:execute).with(gitaly_pagination: true).and_call_original
+ end
+
+ get :refs, params: { namespace_id: project.namespace, id: project, ref: "123456" }
+ end
+
+ context 'when use_gitaly_pagination_for_refs is disabled' do
+ before do
+ stub_feature_flags(use_gitaly_pagination_for_refs: false)
+ end
+
+ it 'does not use gitaly pagination' do
+ expected_params = ActionController::Parameters.new(ref: '123456', per_page: 100).permit!
+
+ expect_next_instance_of(BranchesFinder, project.repository, expected_params) do |finder|
+ expect(finder).to receive(:execute).with(gitaly_pagination: false).and_call_original
+ end
+
+ expect_next_instance_of(TagsFinder, project.repository, expected_params) do |finder|
+ expect(finder).to receive(:execute).with(gitaly_pagination: false).and_call_original
+ end
+
+ get :refs, params: { namespace_id: project.namespace, id: project, ref: "123456" }
+ end
+ end
+
context 'when gitaly is unavailable' do
before do
expect_next_instance_of(TagsFinder) do |finder|
diff --git a/spec/controllers/registrations/welcome_controller_spec.rb b/spec/controllers/registrations/welcome_controller_spec.rb
index 8a5a8490a23..14e88d469ba 100644
--- a/spec/controllers/registrations/welcome_controller_spec.rb
+++ b/spec/controllers/registrations/welcome_controller_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe Registrations::WelcomeController do
sign_in(user)
end
- it { is_expected.to redirect_to(dashboard_projects_path)}
+ it { is_expected.to redirect_to(dashboard_projects_path) }
end
context 'when role is set and setup_for_company is not set' do
@@ -78,7 +78,7 @@ RSpec.describe Registrations::WelcomeController do
sign_in(user)
end
- it { is_expected.to redirect_to(dashboard_projects_path)}
+ it { is_expected.to redirect_to(dashboard_projects_path) }
context 'when the new user already has any accepted group membership' do
let!(:member1) { create(:group_member, user: user) }
diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb
index 70d4559edc1..637c774c38b 100644
--- a/spec/controllers/registrations_controller_spec.rb
+++ b/spec/controllers/registrations_controller_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe RegistrationsController do
before do
stub_application_setting(require_admin_approval_after_user_signup: false)
+ stub_feature_flags(arkose_labs_signup_challenge: false)
end
describe '#new' do
@@ -121,6 +122,7 @@ RSpec.describe RegistrationsController do
context 'when `send_user_confirmation_email` is true' do
before do
stub_application_setting(send_user_confirmation_email: true)
+ stub_feature_flags(identity_verification: false)
end
it 'sends a confirmation email' do
@@ -133,6 +135,10 @@ RSpec.describe RegistrationsController do
end
context 'email confirmation' do
+ before do
+ stub_feature_flags(identity_verification: false)
+ end
+
context 'when send_user_confirmation_email is false' do
it 'signs the user in' do
stub_application_setting(send_user_confirmation_email: false)
@@ -492,6 +498,33 @@ RSpec.describe RegistrationsController do
end
end
end
+
+ context 'when the password is weak' do
+ render_views
+ let_it_be(:new_user_params) { { new_user: base_user_params.merge({ password: "password" }) } }
+
+ subject { 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)
+
+ 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
+ end
+
+ context 'when block_weak_passwords is disabled' do
+ before do
+ stub_feature_flags(block_weak_passwords: false)
+ end
+
+ it 'permits weak passwords' do
+ expect { subject }.to change(User, :count).by(1)
+ end
+ end
+ end
end
describe '#destroy' do
diff --git a/spec/controllers/repositories/git_http_controller_spec.rb b/spec/controllers/repositories/git_http_controller_spec.rb
index 448587c937a..da62acb1fda 100644
--- a/spec/controllers/repositories/git_http_controller_spec.rb
+++ b/spec/controllers/repositories/git_http_controller_spec.rb
@@ -55,11 +55,11 @@ RSpec.describe Repositories::GitHttpController do
let_it_be(:namespace) { project.namespace }
before do
- OnboardingProgress.onboard(namespace)
+ Onboarding::Progress.onboard(namespace)
send_request
end
- subject { OnboardingProgress.completed?(namespace, :git_pull) }
+ subject { Onboarding::Progress.completed?(namespace, :git_pull) }
it { is_expected.to be(true) }
end
diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb
index 14b198dbefe..4131bd148da 100644
--- a/spec/controllers/search_controller_spec.rb
+++ b/spec/controllers/search_controller_spec.rb
@@ -270,6 +270,59 @@ RSpec.describe SearchController do
get(:show, params: { search: 'foo@bar.com', scope: 'users' })
end
end
+
+ it 'increments the custom search sli apdex' do
+ expect(Gitlab::Metrics::GlobalSearchSlis).to receive(:record_apdex).with(
+ elapsed: a_kind_of(Numeric),
+ search_scope: 'issues',
+ search_type: 'basic',
+ search_level: 'global'
+ )
+
+ get :show, params: { scope: 'issues', search: 'hello world' }
+ end
+
+ context 'custom search sli error rate' do
+ context 'when the search is successful' do
+ it 'increments the custom search sli error rate with error: false' do
+ expect(Gitlab::Metrics::GlobalSearchSlis).to receive(:record_error_rate).with(
+ error: false,
+ search_scope: 'issues',
+ search_type: 'basic',
+ search_level: 'global'
+ )
+
+ get :show, params: { scope: 'issues', search: 'hello world' }
+ end
+ end
+
+ context 'when the search raises an error' do
+ before do
+ allow_next_instance_of(SearchService) do |service|
+ allow(service).to receive(:search_results).and_raise(ActiveRecord::QueryCanceled)
+ end
+ end
+
+ it 'increments the custom search sli error rate with error: true' do
+ expect(Gitlab::Metrics::GlobalSearchSlis).to receive(:record_error_rate).with(
+ error: true,
+ search_scope: 'issues',
+ search_type: 'basic',
+ search_level: 'global'
+ )
+
+ get :show, params: { scope: 'issues', search: 'hello world' }
+ end
+ end
+
+ context 'when something goes wrong before a search is done' do
+ it 'does not increment the error rate' do
+ expect(Gitlab::Metrics::GlobalSearchSlis).not_to receive(:record_error_rate)
+
+ get :show, params: { scope: 'issues' } # no search query
+ end
+ end
+ end
end
describe 'GET #count', :aggregate_failures do
diff --git a/spec/controllers/snippets/notes_controller_spec.rb b/spec/controllers/snippets/notes_controller_spec.rb
index 8e85e283b31..00d99b46d0b 100644
--- a/spec/controllers/snippets/notes_controller_spec.rb
+++ b/spec/controllers/snippets/notes_controller_spec.rb
@@ -312,7 +312,7 @@ RSpec.describe Snippets::NotesController do
describe 'POST toggle_award_emoji' do
let(:note) { create(:note_on_personal_snippet, noteable: public_snippet) }
- let(:emoji_name) { 'thumbsup'}
+ let(:emoji_name) { 'thumbsup' }
before do
sign_in(user)
diff --git a/spec/db/development/add_security_training_providers_spec.rb b/spec/db/development/add_security_training_providers_spec.rb
new file mode 100644
index 00000000000..276fa690898
--- /dev/null
+++ b/spec/db/development/add_security_training_providers_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Create security training providers in development' do
+ subject { load Rails.root.join('db', 'fixtures', 'development', '044_add_security_training_providers.rb') }
+
+ it_behaves_like 'security training providers importer'
+end
diff --git a/spec/db/migration_spec.rb b/spec/db/migration_spec.rb
index 7987c78b423..7751bfd989d 100644
--- a/spec/db/migration_spec.rb
+++ b/spec/db/migration_spec.rb
@@ -8,10 +8,10 @@ RSpec.describe 'Migrations Validation' do
# The range describes the timestamps that given migration helper can be used
let(:all_migration_classes) do
{
- 2022_01_26_21_06_58.. => Gitlab::Database::Migration[2.0],
+ 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],
- ..2021_05_31_05_39_16 => ActiveRecord::Migration[6.0]
+ ..2021_05_31_05_39_16 => ActiveRecord::Migration[6.0]
}
end
diff --git a/spec/db/production/add_security_training_providers_spec.rb b/spec/db/production/add_security_training_providers_spec.rb
new file mode 100644
index 00000000000..50d0653e7a2
--- /dev/null
+++ b/spec/db/production/add_security_training_providers_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Create security training providers in production' do
+ subject { load Rails.root.join('db', 'fixtures', 'production', '004_add_security_training_providers.rb') }
+
+ it_behaves_like 'security training providers importer'
+end
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
index bd13f86034a..4aeafed5712 100644
--- a/spec/db/schema_spec.rb
+++ b/spec/db/schema_spec.rb
@@ -31,9 +31,14 @@ 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_builds: %w[erased_by_id trigger_request_id],
+ ci_builds: %w[erased_by_id trigger_request_id partition_id],
+ ci_builds_metadata: %w[partition_id],
+ ci_job_artifacts: %w[partition_id],
ci_namespace_monthly_usages: %w[namespace_id],
+ ci_pipeline_variables: %w[partition_id],
+ ci_pipelines: %w[partition_id],
ci_runner_projects: %w[runner_id],
+ ci_stages: %w[partition_id],
ci_trigger_requests: %w[commit_id],
cluster_providers_aws: %w[security_group_id vpc_id access_key_id],
cluster_providers_gcp: %w[gcp_project_id operation_id],
@@ -50,6 +55,7 @@ RSpec.describe 'Database schema' do
geo_node_statuses: %w[last_event_id cursor_last_event_id],
geo_nodes: %w[oauth_application_id],
geo_repository_deleted_events: %w[project_id],
+ ghost_user_migrations: %w[initiator_user_id],
gitlab_subscription_histories: %w[gitlab_subscription_id hosted_plan_id namespace_id],
identities: %w[user_id],
import_failures: %w[project_id],
diff --git a/spec/dependencies/omniauth_saml_spec.rb b/spec/dependencies/omniauth_saml_spec.rb
index 8956fa44b7a..470b1512a83 100644
--- a/spec/dependencies/omniauth_saml_spec.rb
+++ b/spec/dependencies/omniauth_saml_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe 'processing of SAMLResponse in dependencies' do
allow_next_instance_of(OneLogin::RubySaml::Response) do |instance|
allow(instance).to receive(:is_valid?).and_return(true)
end
- saml_strategy.send(:handle_response, mock_saml_response, {}, settings ) { }
+ saml_strategy.send(:handle_response, mock_saml_response, {}, settings ) {}
end
it 'can extract AuthnContextClassRef from SAMLResponse param' do
diff --git a/spec/deprecation_toolkit_env.rb b/spec/deprecation_toolkit_env.rb
index fa4fdf805ec..94bd88fbc75 100644
--- a/spec/deprecation_toolkit_env.rb
+++ b/spec/deprecation_toolkit_env.rb
@@ -58,7 +58,7 @@ module DeprecationToolkitEnv
# See https://gitlab.com/gitlab-org/gitlab/-/commit/aea37f506bbe036378998916d374966c031bf347#note_647515736
def self.allowed_kwarg_warning_paths
%w[
- ]
+ ]
end
def self.configure!
diff --git a/spec/experiments/application_experiment_spec.rb b/spec/experiments/application_experiment_spec.rb
index a39890dd530..b144e6f77d2 100644
--- a/spec/experiments/application_experiment_spec.rb
+++ b/spec/experiments/application_experiment_spec.rb
@@ -21,7 +21,7 @@ RSpec.describe ApplicationExperiment, :experiment do
# them optional there.
expect(experiment(:example)).to register_behavior(:control).with(nil)
- expect { experiment(:example) { } }.not_to raise_error
+ expect { experiment(:example) {} }.not_to raise_error
end
describe "#publish" do
@@ -129,7 +129,7 @@ RSpec.describe ApplicationExperiment, :experiment do
expect_snowplow_event(
category: 'namespaced/stub',
- action: 'action',
+ action: :action,
property: '_property_',
context: [
{
@@ -162,7 +162,7 @@ RSpec.describe ApplicationExperiment, :experiment do
expect_snowplow_event(
category: 'namespaced/stub',
- action: 'action',
+ action: :action,
user: user,
project: project,
namespace: namespace,
@@ -177,7 +177,7 @@ RSpec.describe ApplicationExperiment, :experiment do
expect_snowplow_event(
category: 'namespaced/stub',
- action: 'action',
+ action: :action,
user: user,
project: project,
namespace: group,
@@ -193,7 +193,7 @@ RSpec.describe ApplicationExperiment, :experiment do
expect_snowplow_event(
category: 'namespaced/stub',
- action: 'action',
+ action: :action,
user: actor,
project: project,
namespace: namespace,
@@ -208,7 +208,7 @@ RSpec.describe ApplicationExperiment, :experiment do
expect_snowplow_event(
category: 'namespaced/stub',
- action: 'action',
+ action: :action,
project: project,
namespace: namespace,
context: an_instance_of(Array)
@@ -289,15 +289,15 @@ RSpec.describe ApplicationExperiment, :experiment do
end
it "doesn't raise an exception" do
- expect { experiment(:top) { |e| e.control { experiment(:nested) { } } } }.not_to raise_error
+ expect { experiment(:top) { |e| e.control { experiment(:nested) {} } } }.not_to raise_error
end
it "tracks an event", :snowplow do
- experiment(:top) { |e| e.control { experiment(:nested) { } } }
+ experiment(:top) { |e| e.control { experiment(:nested) {} } }
expect(Gitlab::Tracking).to have_received(:event).with( # rubocop:disable RSpec/ExpectGitlabTracking
'top',
- 'nested',
+ :nested,
hash_including(label: 'nested')
)
end
@@ -311,8 +311,8 @@ RSpec.describe ApplicationExperiment, :experiment do
cache.clear(key: application_experiment.name)
- application_experiment.control { }
- application_experiment.candidate { }
+ application_experiment.control {}
+ application_experiment.candidate {}
end
it "caches the variant determined by the variant resolver" do
@@ -378,7 +378,7 @@ RSpec.describe ApplicationExperiment, :experiment do
it "doesn't warn on non dev/test environments" do
allow(Gitlab).to receive(:dev_or_test_env?).and_return(false)
- expect { experiment(:example) { |e| e.use { } } }.not_to raise_error
+ expect { experiment(:example) { |e| e.use {} } }.not_to raise_error
expect(ActiveSupport::Deprecation).not_to have_received(:new).with(anything, 'Gitlab::Experiment')
end
@@ -387,7 +387,7 @@ RSpec.describe ApplicationExperiment, :experiment do
# This will eventually raise an ActiveSupport::Deprecation exception,
# it's ok to change it when that happens.
- expect { experiment(:example) { |e| e.use { } } }.not_to raise_error
+ expect { experiment(:example) { |e| e.use {} } }.not_to raise_error
expect(ActiveSupport::Deprecation).to have_received(:new).with(anything, 'Gitlab::Experiment')
end
diff --git a/spec/factories/ci/build_trace_chunks.rb b/spec/factories/ci/build_trace_chunks.rb
index 115eb32111c..64a297932de 100644
--- a/spec/factories/ci/build_trace_chunks.rb
+++ b/spec/factories/ci/build_trace_chunks.rb
@@ -23,7 +23,7 @@ FactoryBot.define do
end
trait :database_with_data do
- data_store { :database}
+ data_store { :database }
transient do
initial_data { 'test data' }
@@ -55,7 +55,7 @@ FactoryBot.define do
end
trait :persisted do
- data_store { :database}
+ data_store { :database }
transient do
initial_data { 'test data' }
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index d684f79a518..8c2edc8cd9f 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -129,8 +129,8 @@ FactoryBot.define do
{
script: %w(ls),
environment: { name: 'staging',
- action: 'stop',
- url: 'http://staging.example.com/$CI_JOB_NAME' }
+ action: 'stop',
+ url: 'http://staging.example.com/$CI_JOB_NAME' }
}
end
end
@@ -141,9 +141,9 @@ FactoryBot.define do
{
script: %w(ls),
environment: { name: 'test_portal',
- action: 'start',
- url: 'http://staging.example.com/$CI_JOB_NAME',
- deployment_tier: 'testing' }
+ action: 'start',
+ url: 'http://staging.example.com/$CI_JOB_NAME',
+ deployment_tier: 'testing' }
}
end
end
@@ -155,7 +155,7 @@ FactoryBot.define do
{
script: %w(ls),
environment: { name: 'production',
- url: 'http://prd.example.com/$CI_JOB_NAME' }
+ url: 'http://prd.example.com/$CI_JOB_NAME' }
}
end
end
@@ -167,8 +167,8 @@ FactoryBot.define do
{
script: %w(ls),
environment: { name: 'review/$CI_COMMIT_REF_NAME',
- url: 'http://staging.example.com/$CI_JOB_NAME',
- on_stop: 'stop_review_app' }
+ url: 'http://staging.example.com/$CI_JOB_NAME',
+ on_stop: 'stop_review_app' }
}
end
end
@@ -181,8 +181,8 @@ FactoryBot.define do
{
script: %w(ls),
environment: { name: 'review/$CI_COMMIT_REF_NAME',
- url: 'http://staging.example.com/$CI_JOB_NAME',
- action: 'stop' }
+ url: 'http://staging.example.com/$CI_JOB_NAME',
+ action: 'stop' }
}
end
end
diff --git a/spec/factories/ci/job_artifacts.rb b/spec/factories/ci/job_artifacts.rb
index 114ad3a5847..f8b964cf8e0 100644
--- a/spec/factories/ci/job_artifacts.rb
+++ b/spec/factories/ci/job_artifacts.rb
@@ -15,7 +15,7 @@ FactoryBot.define do
end
trait :remote_store do
- file_store { JobArtifactUploader::Store::REMOTE}
+ file_store { JobArtifactUploader::Store::REMOTE }
end
after :build do |artifact|
@@ -102,28 +102,6 @@ FactoryBot.define do
end
end
- trait :zip_with_single_file do
- file_type { :archive }
- file_format { :zip }
-
- after(:build) do |artifact, evaluator|
- artifact.file = fixture_file_upload(
- Rails.root.join('spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/single_file.zip'),
- 'application/zip')
- end
- end
-
- trait :zip_with_multiple_files do
- file_type { :archive }
- file_format { :zip }
-
- after(:build) do |artifact, evaluator|
- artifact.file = fixture_file_upload(
- Rails.root.join('spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/multiple_files.zip'),
- 'application/zip')
- end
- end
-
trait :junit do
file_type { :junit }
file_format { :gzip }
@@ -384,6 +362,15 @@ FactoryBot.define do
end
end
+ trait :common_security_report_without_top_level_scanner do
+ common_security_report
+
+ after(:build) do |artifact, _|
+ artifact.file = fixture_file_upload(
+ Rails.root.join('spec/fixtures/security_reports/master/gl-common-scanning-report-without-top-level-scanner.json'), 'application/json')
+ end
+ end
+
trait :common_security_report_with_blank_names do
file_format { :raw }
file_type { :dependency_scanning }
diff --git a/spec/factories/ci/pipeline_artifacts.rb b/spec/factories/ci/pipeline_artifacts.rb
index 85277ce6fbf..d096f149c3a 100644
--- a/spec/factories/ci/pipeline_artifacts.rb
+++ b/spec/factories/ci/pipeline_artifacts.rb
@@ -30,7 +30,7 @@ FactoryBot.define do
end
trait :remote_store do
- file_store { ::ObjectStorage::Store::REMOTE}
+ file_store { ::ObjectStorage::Store::REMOTE }
end
trait :with_coverage_report do
diff --git a/spec/factories/ci/reports/sbom/components.rb b/spec/factories/ci/reports/sbom/components.rb
new file mode 100644
index 00000000000..317e1c863cf
--- /dev/null
+++ b/spec/factories/ci/reports/sbom/components.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :ci_reports_sbom_component, class: '::Gitlab::Ci::Reports::Sbom::Component' do
+ type { :library }
+ sequence(:name) { |n| "component-#{n}" }
+ sequence(:version) { |n| "v0.0.#{n}" }
+
+ skip_create
+
+ initialize_with do
+ ::Gitlab::Ci::Reports::Sbom::Component.new(
+ type: type,
+ name: name,
+ version: version
+ )
+ end
+ end
+end
diff --git a/spec/factories/ci/reports/sbom/sources.rb b/spec/factories/ci/reports/sbom/sources.rb
new file mode 100644
index 00000000000..9093aba86a6
--- /dev/null
+++ b/spec/factories/ci/reports/sbom/sources.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :ci_reports_sbom_source, class: '::Gitlab::Ci::Reports::Sbom::Source' do
+ type { :dependency_scanning }
+
+ transient do
+ sequence(:input_file_path) { |n| "subproject-#{n}/package-lock.json" }
+ sequence(:source_file_path) { |n| "subproject-#{n}/package.json" }
+ end
+
+ data do
+ {
+ 'category' => 'development',
+ 'input_file' => { 'path' => input_file_path },
+ 'source_file' => { 'path' => source_file_path },
+ 'package_manager' => { 'name' => 'npm' },
+ 'language' => { 'name' => 'JavaScript' }
+ }
+ end
+
+ fingerprint { Digest::SHA256.hexdigest(data.to_json) }
+
+ skip_create
+
+ initialize_with do
+ ::Gitlab::Ci::Reports::Sbom::Source.new(
+ type: type,
+ data: data,
+ fingerprint: fingerprint
+ )
+ end
+ end
+end
diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb
index fa10b37cdbf..a60f0a3879a 100644
--- a/spec/factories/commit_statuses.rb
+++ b/spec/factories/commit_statuses.rb
@@ -6,10 +6,10 @@ FactoryBot.define do
stage { 'test' }
stage_idx { 0 }
status { 'success' }
- description { 'commit status'}
+ description { 'commit status' }
pipeline factory: :ci_pipeline
- started_at { 'Tue, 26 Jan 2016 08:21:42 +0100'}
- finished_at { 'Tue, 26 Jan 2016 08:23:42 +0100'}
+ started_at { 'Tue, 26 Jan 2016 08:21:42 +0100' }
+ finished_at { 'Tue, 26 Jan 2016 08:23:42 +0100' }
trait :success do
status { 'success' }
@@ -56,12 +56,7 @@ FactoryBot.define do
end
after(:build) do |build, evaluator|
- build.project = build.pipeline.project
- end
-
- factory :generic_commit_status, class: 'GenericCommitStatus' do
- name { 'generic' }
- description { 'external commit status' }
+ build.project ||= build.pipeline.project
end
end
end
diff --git a/spec/factories/design_management/designs.rb b/spec/factories/design_management/designs.rb
index 56a1b55b969..3d95c754a96 100644
--- a/spec/factories/design_management/designs.rb
+++ b/spec/factories/design_management/designs.rb
@@ -109,7 +109,7 @@ FactoryBot.define do
repository = project.design_repository
commit_version = ->(action) do
- repository.multi_action(
+ repository.commit_files(
evaluator.author,
branch_name: 'master',
message: "#{action.action} for #{design.filename}",
diff --git a/spec/factories/design_management/versions.rb b/spec/factories/design_management/versions.rb
index e505a77d6bd..9d965c6e86c 100644
--- a/spec/factories/design_management/versions.rb
+++ b/spec/factories/design_management/versions.rb
@@ -102,7 +102,7 @@ FactoryBot.define do
end
if actions.present?
- repository.multi_action(
+ repository.commit_files(
evaluator.author,
branch_name: 'master',
message: "created #{actions.size} files",
@@ -123,7 +123,7 @@ FactoryBot.define do
end
end
- sha = repository.multi_action(
+ sha = repository.commit_files(
evaluator.author,
branch_name: 'master',
message: "edited #{version_actions.size} files",
diff --git a/spec/factories/emails.rb b/spec/factories/emails.rb
index b30fa8a5896..e51c3358a9b 100644
--- a/spec/factories/emails.rb
+++ b/spec/factories/emails.rb
@@ -6,6 +6,6 @@ FactoryBot.define do
email { generate(:email_alias) }
trait(:confirmed) { confirmed_at { Time.now } }
- trait(:skip_validate) { to_create {|instance| instance.save!(validate: false) } }
+ trait(:skip_validate) { to_create { |instance| instance.save!(validate: false) } }
end
end
diff --git a/spec/factories/environments.rb b/spec/factories/environments.rb
index ccd2011eb8d..34843dab0fe 100644
--- a/spec/factories/environments.rb
+++ b/spec/factories/environments.rb
@@ -47,7 +47,7 @@ FactoryBot.define do
pipeline = create(:ci_pipeline, project: environment.project)
deployable = create(:ci_build, :success, name: "#{environment.name}:deploy",
- pipeline: pipeline)
+ pipeline: pipeline)
deployment = create(:deployment,
:success,
diff --git a/spec/factories/external_pull_requests.rb b/spec/factories/external_pull_requests.rb
index 7a6e77f8572..470814f4360 100644
--- a/spec/factories/external_pull_requests.rb
+++ b/spec/factories/external_pull_requests.rb
@@ -12,6 +12,6 @@ FactoryBot.define do
target_sha { 'a09386439ca39abe575675ffd4b89ae824fec22f' }
status { :open }
- trait(:closed) { status { 'closed'} }
+ trait(:closed) { status { 'closed' } }
end
end
diff --git a/spec/factories/generic_commit_statuses.rb b/spec/factories/generic_commit_statuses.rb
new file mode 100644
index 00000000000..ea86dddcaf8
--- /dev/null
+++ b/spec/factories/generic_commit_statuses.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :generic_commit_status, class: 'GenericCommitStatus', parent: :commit_status do
+ name { 'generic' }
+ description { 'external commit status' }
+ end
+end
diff --git a/spec/factories/gitlab/database/postgres_index.rb b/spec/factories/gitlab/database/postgres_index.rb
index 54bbb738512..8492b44c404 100644
--- a/spec/factories/gitlab/database/postgres_index.rb
+++ b/spec/factories/gitlab/database/postgres_index.rb
@@ -13,7 +13,7 @@ FactoryBot.define do
exclusion { false }
expression { false }
partial { false }
- definition { "CREATE INDEX #{identifier} ON #{tablename} (bar)"}
+ definition { "CREATE INDEX #{identifier} ON #{tablename} (bar)" }
ondisk_size_bytes { 100.megabytes }
end
end
diff --git a/spec/factories/group_members.rb b/spec/factories/group_members.rb
index 4b1bf9a7d11..702db45554e 100644
--- a/spec/factories/group_members.rb
+++ b/spec/factories/group_members.rb
@@ -4,6 +4,7 @@ FactoryBot.define do
factory :group_member do
access_level { GroupMember::OWNER }
source { association(:group) }
+ member_namespace_id { source.id }
user
trait(:guest) { access_level { GroupMember::GUEST } }
diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb
index 152ae061605..6f9cf0ef895 100644
--- a/spec/factories/groups.rb
+++ b/spec/factories/groups.rb
@@ -106,9 +106,9 @@ FactoryBot.define do
end
create_graph(
- parent: group,
+ parent: group,
children: evaluator.children,
- depth: evaluator.depth
+ depth: evaluator.depth
)
end
end
diff --git a/spec/factories/ml/candidates.rb b/spec/factories/ml/candidates.rb
new file mode 100644
index 00000000000..b5644ee3841
--- /dev/null
+++ b/spec/factories/ml/candidates.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+FactoryBot.define do
+ factory :ml_candidates, class: '::Ml::Candidate' do
+ association :experiment, factory: :ml_experiments
+ association :user
+ end
+end
diff --git a/spec/factories/ml/experiments.rb b/spec/factories/ml/experiments.rb
new file mode 100644
index 00000000000..043ca712e60
--- /dev/null
+++ b/spec/factories/ml/experiments.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+FactoryBot.define do
+ factory :ml_experiments, class: '::Ml::Experiment' do
+ sequence(:name) { |n| "experiment#{n}" }
+ association :project
+ association :user
+ end
+end
diff --git a/spec/factories/namespaces/sync_events.rb b/spec/factories/namespaces/sync_events.rb
new file mode 100644
index 00000000000..63bb16958a3
--- /dev/null
+++ b/spec/factories/namespaces/sync_events.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :sync_event, class: 'Namespaces::SyncEvent' do
+ association :namespace, factory: :namespace, strategy: :build
+ end
+end
diff --git a/spec/factories/onboarding/progresses.rb b/spec/factories/onboarding/progresses.rb
new file mode 100644
index 00000000000..15f58b482d3
--- /dev/null
+++ b/spec/factories/onboarding/progresses.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :onboarding_progress, class: 'Onboarding::Progress' do
+ namespace
+ end
+end
diff --git a/spec/factories/onboarding_progresses.rb b/spec/factories/onboarding_progresses.rb
deleted file mode 100644
index e39bad91b19..00000000000
--- a/spec/factories/onboarding_progresses.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-FactoryBot.define do
- factory :onboarding_progress do
- namespace
- end
-end
diff --git a/spec/factories/packages/debian/component_file.rb b/spec/factories/packages/debian/component_file.rb
index eeba64ba5d2..a2422e4a126 100644
--- a/spec/factories/packages/debian/component_file.rb
+++ b/spec/factories/packages/debian/component_file.rb
@@ -30,10 +30,18 @@ FactoryBot.define do
trait(:sources) do
file_type { :sources }
architecture { nil }
+ file_fixture { 'spec/fixtures/packages/debian/distribution/Sources' }
end
trait(:di_packages) do
file_type { :di_packages }
+ file_fixture { 'spec/fixtures/packages/debian/distribution/D-I-Packages' }
+ end
+
+ trait(:older_sha256) do
+ created_at { '2020-01-24T08:00:00Z' }
+ file_sha256 { '157a1ad2b9102038560eea56771913b312ebf25093f5ef3b9842021c639c880d' }
+ file_fixture { 'spec/fixtures/packages/debian/distribution/OtherSHA256' }
end
trait(:object_storage) do
diff --git a/spec/factories/packages/dependencies.rb b/spec/factories/packages/dependencies.rb
index a62d48c2e73..c99f11d6db3 100644
--- a/spec/factories/packages/dependencies.rb
+++ b/spec/factories/packages/dependencies.rb
@@ -2,11 +2,11 @@
FactoryBot.define do
factory :packages_dependency, class: 'Packages::Dependency' do
- sequence(:name) { |n| "@test/package-#{n}"}
+ sequence(:name) { |n| "@test/package-#{n}" }
sequence(:version_pattern) { |n| "~6.2.#{n}" }
trait(:rubygems) do
- sequence(:name) { |n| "gem-dependency-#{n}"}
+ sequence(:name) { |n| "gem-dependency-#{n}" }
end
end
end
diff --git a/spec/factories/packages/package_files.rb b/spec/factories/packages/package_files.rb
index 5eac0036b91..7d3dd274777 100644
--- a/spec/factories/packages/package_files.rb
+++ b/spec/factories/packages/package_files.rb
@@ -337,6 +337,14 @@ FactoryBot.define do
size { 3989.bytes }
end
+ trait(:rpm) do
+ package
+ file_fixture { 'spec/fixtures/packages/rpm/hello-0.0.1-1.fc29.x86_64.rpm' }
+ file_name { 'hello-0.0.1-1.fc29.x86_64.rpm' }
+ file_sha1 { '5fe852b2a6abd96c22c11fa1ff2fb19d9ce58b57' }
+ size { 115.kilobytes }
+ end
+
trait(:object_storage) do
file_store { Packages::PackageFileUploader::Store::REMOTE }
end
diff --git a/spec/factories/packages/package_tags.rb b/spec/factories/packages/package_tags.rb
index 3d2eea4a73b..1a40d5d600f 100644
--- a/spec/factories/packages/package_tags.rb
+++ b/spec/factories/packages/package_tags.rb
@@ -3,6 +3,6 @@
FactoryBot.define do
factory :packages_tag, class: 'Packages::Tag' do
package
- sequence(:name) { |n| "tag-#{n}"}
+ sequence(:name) { |n| "tag-#{n}" }
end
end
diff --git a/spec/factories/packages/packages.rb b/spec/factories/packages/packages.rb
index 90c68074a3b..8074e505243 100644
--- a/spec/factories/packages/packages.rb
+++ b/spec/factories/packages/packages.rb
@@ -24,6 +24,10 @@ FactoryBot.define do
status { :pending_destruction }
end
+ trait :last_downloaded_at do
+ last_downloaded_at { 2.days.ago }
+ end
+
factory :maven_package do
maven_metadatum
@@ -55,6 +59,12 @@ FactoryBot.define do
end
end
+ factory :rpm_package do
+ sequence(:name) { |n| "package-#{n}" }
+ sequence(:version) { |n| "v1.0.#{n}" }
+ package_type { :rpm }
+ end
+
factory :debian_package do
sequence(:name) { |n| "package-#{n}" }
sequence(:version) { |n| "1.0-#{n}" }
@@ -115,7 +125,7 @@ FactoryBot.define do
end
factory :npm_package do
- sequence(:name) { |n| "@#{project.root_namespace.path}/package-#{n}"}
+ sequence(:name) { |n| "@#{project.root_namespace.path}/package-#{n}" }
sequence(:version) { |n| "1.0.#{n}" }
package_type { :npm }
@@ -153,7 +163,7 @@ FactoryBot.define do
end
factory :nuget_package do
- sequence(:name) { |n| "NugetPackage#{n}"}
+ sequence(:name) { |n| "NugetPackage#{n}" }
sequence(:version) { |n| "1.0.#{n}" }
package_type { :nuget }
@@ -175,7 +185,7 @@ FactoryBot.define do
end
factory :pypi_package do
- sequence(:name) { |n| "pypi-package-#{n}"}
+ sequence(:name) { |n| "pypi-package-#{n}" }
sequence(:version) { |n| "1.0.#{n}" }
package_type { :pypi }
@@ -193,7 +203,7 @@ FactoryBot.define do
end
factory :composer_package do
- sequence(:name) { |n| "composer-package-#{n}"}
+ sequence(:name) { |n| "composer-package-#{n}" }
sequence(:version) { |n| "1.0.#{n}" }
package_type { :composer }
@@ -210,7 +220,7 @@ FactoryBot.define do
end
factory :golang_package do
- sequence(:name) { |n| "golang.org/x/pkg-#{n}"}
+ sequence(:name) { |n| "golang.org/x/pkg-#{n}" }
sequence(:version) { |n| "v1.0.#{n}" }
package_type { :golang }
end
diff --git a/spec/factories/packages/rpm/metadata.rb b/spec/factories/packages/rpm/metadata.rb
new file mode 100644
index 00000000000..5ee85aed3bb
--- /dev/null
+++ b/spec/factories/packages/rpm/metadata.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :rpm_metadatum, class: 'Packages::Rpm::Metadatum' do
+ package { association(:rpm_package) }
+ release { "#{rand(10)}.#{rand(10)}" }
+ summary { FFaker::Lorem.sentences(2).join }
+ description { FFaker::Lorem.sentences(4).join }
+ arch { FFaker::Lorem.word }
+ epoch { 0 }
+ end
+end
diff --git a/spec/factories/project_members.rb b/spec/factories/project_members.rb
index ab1e45acc15..57f228650a1 100644
--- a/spec/factories/project_members.rb
+++ b/spec/factories/project_members.rb
@@ -4,6 +4,7 @@ FactoryBot.define do
factory :project_member do
user
source { association(:project) }
+ member_namespace_id { source.id }
maintainer
trait(:guest) { access_level { ProjectMember::GUEST } }
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 95b72648cf5..871917a725e 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -35,6 +35,7 @@ FactoryBot.define do
end
metrics_dashboard_access_level { ProjectFeature::PRIVATE }
operations_access_level { ProjectFeature::ENABLED }
+ monitor_access_level { ProjectFeature::ENABLED }
container_registry_access_level { ProjectFeature::ENABLED }
security_and_compliance_access_level { ProjectFeature::PRIVATE }
environments_access_level { ProjectFeature::ENABLED }
diff --git a/spec/factories/prometheus_alert.rb b/spec/factories/prometheus_alert.rb
index ad3868c38ed..14fdd993c7a 100644
--- a/spec/factories/prometheus_alert.rb
+++ b/spec/factories/prometheus_alert.rb
@@ -15,7 +15,7 @@ FactoryBot.define do
end
trait :with_runbook_url do
- runbook_url { 'https://runbooks.gitlab.com/metric_gt_1'}
+ runbook_url { 'https://runbooks.gitlab.com/metric_gt_1' }
end
end
end
diff --git a/spec/factories/prometheus_metrics.rb b/spec/factories/prometheus_metrics.rb
index 503d392a524..e785bf2ac9d 100644
--- a/spec/factories/prometheus_metrics.rb
+++ b/spec/factories/prometheus_metrics.rb
@@ -9,7 +9,7 @@ FactoryBot.define do
group { :business }
project
legend { 'legend' }
- dashboard_path { '.gitlab/dashboards/dashboard_path.yml'}
+ dashboard_path { '.gitlab/dashboards/dashboard_path.yml' }
trait :common do
common { true }
diff --git a/spec/factories/protected_branches.rb b/spec/factories/protected_branches.rb
index bac1cf21596..425352783dd 100644
--- a/spec/factories/protected_branches.rb
+++ b/spec/factories/protected_branches.rb
@@ -6,11 +6,25 @@ FactoryBot.define do
project
transient do
+ ee { false }
default_push_level { true }
default_merge_level { true }
default_access_level { true }
end
+ after(:create) do |protected_branch, evaluator|
+ break unless protected_branch.project&.persisted?
+
+ ProtectedBranches::CacheService.new(protected_branch.project).refresh
+ end
+
+ after(:build) do |obj, ctx|
+ next if ctx.ee || !ctx.default_access_level
+
+ obj.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER) if ctx.default_push_level
+ obj.merge_access_levels.new(access_level: Gitlab::Access::MAINTAINER) if ctx.default_merge_level
+ end
+
trait :create_branch_on_repository do
association :project, factory: [:project, :repository]
@@ -25,59 +39,63 @@ FactoryBot.define do
end
end
- trait :developers_can_push do
+ trait :maintainers_can_push do
transient do
default_push_level { false }
end
after(:build) do |protected_branch|
- protected_branch.push_access_levels.new(access_level: Gitlab::Access::DEVELOPER)
+ protected_branch.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
end
- trait :developers_can_merge do
+ trait :maintainers_can_merge do
transient do
- default_merge_level { false }
+ default_push_level { false }
end
after(:build) do |protected_branch|
- protected_branch.merge_access_levels.new(access_level: Gitlab::Access::DEVELOPER)
+ protected_branch.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
end
end
- trait :no_one_can_push do
+ trait :developers_can_push do
transient do
default_push_level { false }
end
after(:build) do |protected_branch|
- protected_branch.push_access_levels.new(access_level: Gitlab::Access::NO_ACCESS)
+ protected_branch.push_access_levels.new(access_level: Gitlab::Access::DEVELOPER)
end
end
- trait :maintainers_can_push do
+ trait :developers_can_merge do
transient do
- default_push_level { false }
+ default_merge_level { false }
end
after(:build) do |protected_branch|
- protected_branch.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
+ protected_branch.merge_access_levels.new(access_level: Gitlab::Access::DEVELOPER)
end
end
- after(:build) do |protected_branch, evaluator|
- if evaluator.default_access_level && evaluator.default_push_level
- protected_branch.push_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
+ trait :no_one_can_push do
+ transient do
+ default_push_level { false }
end
- if evaluator.default_access_level && evaluator.default_merge_level
- protected_branch.merge_access_levels.new(access_level: Gitlab::Access::MAINTAINER)
+ after(:build) do |protected_branch|
+ protected_branch.push_access_levels.new(access_level: Gitlab::Access::NO_ACCESS)
end
end
trait :no_one_can_merge do
- after(:create) do |protected_branch|
- protected_branch.merge_access_levels.first.update!(access_level: Gitlab::Access::NO_ACCESS)
+ transient do
+ default_merge_level { false }
+ end
+
+ after(:build) do |protected_branch|
+ protected_branch.merge_access_levels.new(access_level: Gitlab::Access::NO_ACCESS)
end
end
end
diff --git a/spec/factories/users/ghost_user_migrations.rb b/spec/factories/users/ghost_user_migrations.rb
new file mode 100644
index 00000000000..0fe7cded4f3
--- /dev/null
+++ b/spec/factories/users/ghost_user_migrations.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :ghost_user_migration, class: 'Users::GhostUserMigration' do
+ association :user
+ initiator_user { association(:user) }
+ hard_delete { false }
+ end
+end
diff --git a/spec/factories/work_items.rb b/spec/factories/work_items.rb
index 267ea9710b3..205b071a5d4 100644
--- a/spec/factories/work_items.rb
+++ b/spec/factories/work_items.rb
@@ -23,5 +23,9 @@ FactoryBot.define do
issue_type { :incident }
association :work_item_type, :default, :incident
end
+
+ trait :last_edited_by_user do
+ association :last_edited_by, factory: :user
+ end
end
end
diff --git a/spec/fast_spec_helper.rb b/spec/fast_spec_helper.rb
index 34ab48f67a8..db4d9125e6e 100644
--- a/spec/fast_spec_helper.rb
+++ b/spec/fast_spec_helper.rb
@@ -11,6 +11,9 @@ require_relative '../config/bundler_setup'
ENV['GITLAB_ENV'] = 'test'
ENV['IN_MEMORY_APPLICATION_SETTINGS'] = 'true'
+# Enable zero monkey patching mode before loading any other RSpec code.
+RSpec.configure(&:disable_monkey_patching!)
+
require 'active_support/dependencies'
require_relative '../config/initializers/0_inject_enterprise_edition_module'
require_relative '../config/settings'
@@ -29,13 +32,6 @@ end
ActiveSupport::XmlMini.backend = 'Nokogiri'
RSpec.configure do |config|
- unless ENV['CI']
- # Allow running `:focus` examples locally,
- # falling back to all tests when there is no `:focus` example.
- config.filter_run focus: true
- config.run_all_when_everything_filtered = true
- end
-
# Makes diffs show entire non-truncated values.
config.before(:each, unlimited_max_formatted_output_length: true) do |_example|
config.expect_with :rspec do |c|
diff --git a/spec/features/admin/admin_mode/workers_spec.rb b/spec/features/admin/admin_mode/workers_spec.rb
index 0caa883fb5b..12f5e20e176 100644
--- a/spec/features/admin/admin_mode/workers_spec.rb
+++ b/spec/features/admin/admin_mode/workers_spec.rb
@@ -37,26 +37,56 @@ RSpec.describe 'Admin mode for workers', :request_store do
gitlab_enable_admin_mode_sign_in(user)
end
- it 'can delete user', :js do
- visit admin_user_path(user_to_delete)
+ context 'when user_destroy_with_limited_execution_time_worker is enabled' do
+ it 'can delete user', :js do
+ visit admin_user_path(user_to_delete)
+
+ click_action_in_user_dropdown(user_to_delete.id, 'Delete user')
+
+ page.within '.modal-dialog' do
+ find("input[name='username']").send_keys(user_to_delete.name)
+ click_button 'Delete user'
+
+ wait_for_requests
+ end
- click_action_in_user_dropdown(user_to_delete.id, 'Delete user')
+ expect(page).to have_content('The user is being deleted.')
- page.within '.modal-dialog' do
- find("input[name='username']").send_keys(user_to_delete.name)
- click_button 'Delete user'
+ # Perform jobs while logged out so that admin mode is only enabled in job metadata
+ execute_jobs_signed_out(user)
+
+ visit admin_user_path(user_to_delete)
+
+ expect(find('h1.page-title')).to have_content('(Blocked)')
+ end
+ end
- wait_for_requests
+ context 'when user_destroy_with_limited_execution_time_worker is disabled' do
+ before do
+ stub_feature_flags(user_destroy_with_limited_execution_time_worker: false)
end
- expect(page).to have_content('The user is being deleted.')
+ it 'can delete user', :js do
+ visit admin_user_path(user_to_delete)
- # Perform jobs while logged out so that admin mode is only enabled in job metadata
- execute_jobs_signed_out(user)
+ click_action_in_user_dropdown(user_to_delete.id, 'Delete user')
- visit admin_user_path(user_to_delete)
+ page.within '.modal-dialog' do
+ find("input[name='username']").send_keys(user_to_delete.name)
+ click_button 'Delete user'
+
+ wait_for_requests
+ end
+
+ expect(page).to have_content('The user is being deleted.')
- expect(page).to have_title('Not Found')
+ # Perform jobs while logged out so that admin mode is only enabled in job metadata
+ execute_jobs_signed_out(user)
+
+ visit admin_user_path(user_to_delete)
+
+ expect(page).to have_title('Not Found')
+ end
end
end
end
diff --git a/spec/features/admin/admin_mode_spec.rb b/spec/features/admin/admin_mode_spec.rb
index 24a10d3677d..33cf0e8c4f8 100644
--- a/spec/features/admin/admin_mode_spec.rb
+++ b/spec/features/admin/admin_mode_spec.rb
@@ -33,7 +33,7 @@ RSpec.describe 'Admin mode', :js do
open_top_nav_projects
within_top_nav do
- click_link('Your projects')
+ click_link('View all projects')
end
expect(page).to have_current_path(dashboard_projects_path)
@@ -99,7 +99,7 @@ RSpec.describe 'Admin mode', :js do
open_top_nav_projects
within_top_nav do
- click_link('Your projects')
+ click_link('View all projects')
end
expect(page).to have_current_path(dashboard_projects_path)
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index 44fd21e510a..fe9fd01d3d5 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -34,7 +34,7 @@ RSpec.describe "Admin Runners" do
context "when there are runners" do
context "with an instance runner" do
- let!(:instance_runner) { create(:ci_runner, :instance) }
+ let_it_be(:instance_runner) { create(:ci_runner, :instance) }
before do
visit admin_runners_path
@@ -50,7 +50,7 @@ RSpec.describe "Admin Runners" do
it 'shows an instance badge' do
within_runner_row(instance_runner.id) do
- expect(page).to have_selector '.badge', text: 'shared'
+ expect(page).to have_selector '.badge', text: s_('Runners|Instance')
end
end
end
@@ -66,9 +66,9 @@ RSpec.describe "Admin Runners" do
it 'has all necessary texts' do
expect(page).to have_text "Register an instance runner"
- expect(page).to have_text "Online runners 1"
- expect(page).to have_text "Offline runners 2"
- expect(page).to have_text "Stale runners 1"
+ expect(page).to have_text "#{s_('Runners|Online')} 1"
+ expect(page).to have_text "#{s_('Runners|Offline')} 2"
+ expect(page).to have_text "#{s_('Runners|Stale')} 1"
end
end
@@ -81,15 +81,17 @@ RSpec.describe "Admin Runners" do
visit admin_runners_path
within_runner_row(runner.id) do
- expect(find("[data-label='Jobs']")).to have_content '2'
+ expect(find("[data-testid='job-count']")).to have_content '2'
end
end
describe 'search' do
- before do
+ before_all do
create(:ci_runner, :instance, description: 'runner-foo')
create(:ci_runner, :instance, description: 'runner-bar')
+ end
+ before do
visit admin_runners_path
end
@@ -130,10 +132,12 @@ RSpec.describe "Admin Runners" do
end
describe 'filter by paused' do
- before do
+ before_all do
create(:ci_runner, :instance, description: 'runner-active')
create(:ci_runner, :instance, description: 'runner-paused', active: false)
+ end
+ before do
visit admin_runners_path
end
@@ -145,7 +149,7 @@ RSpec.describe "Admin Runners" do
end
it 'shows paused runners' do
- input_filtered_search_filter_is_only('Paused', 'Yes')
+ input_filtered_search_filter_is_only(s_('Runners|Paused'), 'Yes')
expect(page).to have_link('All 1')
@@ -154,7 +158,7 @@ RSpec.describe "Admin Runners" do
end
it 'shows active runners' do
- input_filtered_search_filter_is_only('Paused', 'No')
+ input_filtered_search_filter_is_only(s_('Runners|Paused'), 'No')
expect(page).to have_link('All 1')
@@ -164,15 +168,17 @@ RSpec.describe "Admin Runners" do
end
describe 'filter by status' do
- let!(:never_contacted) do
+ let_it_be(:never_contacted) do
create(:ci_runner, :instance, description: 'runner-never-contacted', contacted_at: nil)
end
- before do
+ before_all do
create(:ci_runner, :instance, description: 'runner-1', contacted_at: Time.zone.now)
create(:ci_runner, :instance, description: 'runner-2', contacted_at: Time.zone.now)
create(:ci_runner, :instance, description: 'runner-offline', contacted_at: 1.week.ago)
+ end
+ before do
visit admin_runners_path
end
@@ -186,7 +192,7 @@ RSpec.describe "Admin Runners" do
end
it 'shows correct runner when status matches' do
- input_filtered_search_filter_is_only('Status', 'Online')
+ input_filtered_search_filter_is_only('Status', s_('Runners|Online'))
expect(page).to have_link('All 2')
@@ -197,7 +203,7 @@ RSpec.describe "Admin Runners" do
end
it 'shows correct runner when status is selected and search term is entered' do
- input_filtered_search_filter_is_only('Status', 'Online')
+ input_filtered_search_filter_is_only('Status', s_('Runners|Online'))
input_filtered_search_keys('runner-1')
expect(page).to have_link('All 1')
@@ -220,7 +226,7 @@ RSpec.describe "Admin Runners" do
expect(page).to have_content 'runner-never-contacted'
within_runner_row(never_contacted.id) do
- expect(page).to have_selector '.badge', text: 'never contacted'
+ expect(page).to have_selector '.badge', text: s_('Runners|Never contacted')
end
end
@@ -238,7 +244,7 @@ RSpec.describe "Admin Runners" do
end
describe 'filter by type' do
- before do
+ before_all do
create(:ci_runner, :project, description: 'runner-project', projects: [project])
create(:ci_runner, :group, description: 'runner-group', groups: [group])
end
@@ -308,7 +314,7 @@ RSpec.describe "Admin Runners" do
visit admin_runners_path
- input_filtered_search_filter_is_only('Paused', 'No')
+ input_filtered_search_filter_is_only(s_('Runners|Paused'), 'No')
expect(page).to have_content 'runner-project'
expect(page).to have_content 'runner-group'
@@ -345,7 +351,7 @@ RSpec.describe "Admin Runners" do
end
describe 'filter by tag' do
- before do
+ before_all do
create(:ci_runner, :instance, description: 'runner-blue', tag_list: ['blue'])
create(:ci_runner, :instance, description: 'runner-red', tag_list: ['red'])
end
@@ -464,7 +470,7 @@ RSpec.describe "Admin Runners" do
end
describe "Runner show page", :js do
- let(:runner) do
+ let_it_be(:runner) do
create(
:ci_runner,
description: 'runner-foo',
@@ -520,20 +526,25 @@ RSpec.describe "Admin Runners" do
end
describe "Runner edit page" do
- let(:runner) { create(:ci_runner, :project) }
- let!(:project1) { create(:project) }
- let!(:project2) { create(:project) }
+ let_it_be(:project1) { create(:project) }
+ let_it_be(:project2) { create(:project) }
+ let_it_be(:project_runner) { create(:ci_runner, :project) }
before do
- visit edit_admin_runner_path(runner)
+ visit edit_admin_runner_path(project_runner)
wait_for_requests
end
+ it_behaves_like 'submits edit runner form' do
+ let(:runner) { project_runner }
+ let(:runner_page_path) { admin_runner_path(project_runner) }
+ end
+
describe 'breadcrumbs' do
it 'contains the current runner id and token' do
page.within '[data-testid="breadcrumb-links"]' do
- expect(page).to have_link("##{runner.id} (#{runner.short_sha})")
+ expect(page).to have_link("##{project_runner.id} (#{project_runner.short_sha})")
expect(page.find('[data-testid="breadcrumb-current-link"]')).to have_content("Edit")
end
end
@@ -541,7 +552,7 @@ RSpec.describe "Admin Runners" do
describe 'runner header', :js do
it 'contains the runner status, type and id' do
- expect(page).to have_content("never contacted specific Runner ##{runner.id} created")
+ expect(page).to have_content("#{s_('Runners|Never contacted')} Project Runner ##{project_runner.id} created")
end
end
@@ -556,7 +567,7 @@ RSpec.describe "Admin Runners" do
end
it 'redirects to runner page' do
- expect(current_url).to match(admin_runner_path(runner))
+ expect(current_url).to match(admin_runner_path(project_runner))
end
end
@@ -583,7 +594,7 @@ RSpec.describe "Admin Runners" do
describe 'enable/create' do
shared_examples 'assignable runner' do
it 'enables a runner for a project' do
- within '[data-testid="unassigned-projects"]' do
+ within find('[data-testid="unassigned-projects"] tr', text: project2.full_name) do
click_on 'Enable'
end
@@ -594,21 +605,21 @@ RSpec.describe "Admin Runners" do
end
end
- context 'with specific runner' do
- let(:runner) { create(:ci_runner, :project, projects: [project1]) }
+ context 'with project runner' do
+ let(:project_runner) { create(:ci_runner, :project, projects: [project1]) }
before do
- visit edit_admin_runner_path(runner)
+ visit edit_admin_runner_path(project_runner)
end
it_behaves_like 'assignable runner'
end
context 'with locked runner' do
- let(:runner) { create(:ci_runner, :project, projects: [project1], locked: true) }
+ let(:locked_runner) { create(:ci_runner, :project, projects: [project1], locked: true) }
before do
- visit edit_admin_runner_path(runner)
+ visit edit_admin_runner_path(locked_runner)
end
it_behaves_like 'assignable runner'
diff --git a/spec/features/admin/admin_sees_background_migrations_spec.rb b/spec/features/admin/admin_sees_background_migrations_spec.rb
index faf13374719..d72259d91b3 100644
--- a/spec/features/admin/admin_sees_background_migrations_spec.rb
+++ b/spec/features/admin/admin_sees_background_migrations_spec.rb
@@ -189,7 +189,7 @@ RSpec.describe "Admin > Admin sees background migrations" do
visit admin_background_migrations_path
within '#content-body' do
- expect(page).to have_link('Learn more', href: help_page_path('development/database/batched_background_migrations'))
+ expect(page).to have_link('Learn more', href: help_page_path('user/admin_area/monitoring/background_migrations'))
end
end
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 8843e13026b..a5df142d188 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -102,7 +102,7 @@ RSpec.describe 'Admin updates settings' do
end
it 'change Account and Limit Settings' do
- page.within('.as-account-limit') do
+ page.within(find('[data-testid="account-limit"]')) do
uncheck 'Gravatar enabled'
click_button 'Save changes'
end
@@ -112,7 +112,7 @@ RSpec.describe 'Admin updates settings' do
end
it 'change Maximum export size' do
- page.within('.as-account-limit') do
+ page.within(find('[data-testid="account-limit"]')) do
fill_in 'Maximum export size (MB)', with: 25
click_button 'Save changes'
end
@@ -122,7 +122,7 @@ RSpec.describe 'Admin updates settings' do
end
it 'change Maximum import size' do
- page.within('.as-account-limit') do
+ page.within(find('[data-testid="account-limit"]')) do
fill_in 'Maximum import size (MB)', with: 15
click_button 'Save changes'
end
@@ -150,16 +150,20 @@ RSpec.describe 'Admin updates settings' do
it 'does not expose the setting' do
expect(page).to have_no_selector('#application_setting_deactivate_dormant_users')
end
+
+ it 'does not expose the setting' do
+ expect(page).to have_no_selector('#application_setting_deactivate_dormant_users_period')
+ end
end
context 'when not Gitlab.com' do
let(:dot_com?) { false }
- it 'change Dormant users' do
- expect(page).to have_unchecked_field('Deactivate dormant users after 90 days of inactivity')
+ it 'changes Dormant users' do
+ expect(page).to have_unchecked_field('Deactivate dormant users after a period of inactivity')
expect(current_settings.deactivate_dormant_users).to be_falsey
- page.within('.as-account-limit') do
+ page.within(find('[data-testid="account-limit"]')) do
check 'application_setting_deactivate_dormant_users'
click_button 'Save changes'
end
@@ -169,7 +173,22 @@ RSpec.describe 'Admin updates settings' do
page.refresh
expect(current_settings.deactivate_dormant_users).to be_truthy
- expect(page).to have_checked_field('Deactivate dormant users after 90 days of inactivity')
+ expect(page).to have_checked_field('Deactivate dormant users after a period of inactivity')
+ end
+
+ it 'change Dormant users period' do
+ expect(page).to have_field _('Period of inactivity (days)')
+
+ page.within(find('[data-testid="account-limit"]')) do
+ fill_in _('application_setting_deactivate_dormant_users_period'), with: '35'
+ click_button 'Save changes'
+ end
+
+ expect(page).to have_content "Application settings saved successfully"
+
+ page.refresh
+
+ expect(page).to have_field _('Period of inactivity (days)'), with: '35'
end
end
end
@@ -543,7 +562,7 @@ RSpec.describe 'Admin updates settings' do
it 'change Prometheus settings' do
page.within('.as-prometheus') do
- check 'Enable health and performance metrics endpoint'
+ check 'Enable GitLab Prometheus metrics endpoint'
click_button 'Save changes'
end
diff --git a/spec/features/admin/users/user_spec.rb b/spec/features/admin/users/user_spec.rb
index bc88b90a2dd..86acf5a05d4 100644
--- a/spec/features/admin/users/user_spec.rb
+++ b/spec/features/admin/users/user_spec.rb
@@ -205,7 +205,7 @@ RSpec.describe 'Admin::Users::User' do
it 'logs in as the user when impersonate is clicked' do
subject
- find('[data-qa-selector="user_menu"]').click # rubocop:disable QA/SelectorUsage
+ find('[data-testid="user-menu"]').click
expect(page.find(:css, '[data-testid="user-profile-link"]')['data-user']).to eql(another_user.username)
end
@@ -241,7 +241,7 @@ RSpec.describe 'Admin::Users::User' do
it 'logs out of impersonated user back to original user' do
subject
- find('[data-qa-selector="user_menu"]').click # rubocop:disable QA/SelectorUsage
+ find('[data-testid="user-menu"]').click
expect(page.find(:css, '[data-testid="user-profile-link"]')['data-user']).to eq(current_user.username)
end
diff --git a/spec/features/clusters/create_agent_spec.rb b/spec/features/clusters/create_agent_spec.rb
index c44741b756b..b19e57c550c 100644
--- a/spec/features/clusters/create_agent_spec.rb
+++ b/spec/features/clusters/create_agent_spec.rb
@@ -12,10 +12,11 @@ RSpec.describe 'Cluster agent registration', :js do
allow(Gitlab::Kas).to receive(:internal_url).and_return('kas.example.internal')
allow_next_instance_of(Gitlab::Kas::Client) do |client|
- allow(client).to receive(:list_agent_config_files).and_return([
- double(agent_name: 'example-agent-1', path: '.gitlab/agents/example-agent-1/config.yaml'),
- double(agent_name: 'example-agent-2', path: '.gitlab/agents/example-agent-2/config.yaml')
- ])
+ allow(client).to receive(:list_agent_config_files).and_return(
+ [
+ double(agent_name: 'example-agent-1', path: '.gitlab/agents/example-agent-1/config.yaml'),
+ double(agent_name: 'example-agent-2', path: '.gitlab/agents/example-agent-2/config.yaml')
+ ])
allow(client).to receive(:get_connected_agents).and_return([])
end
diff --git a/spec/features/commits/user_view_commits_spec.rb b/spec/features/commits/user_view_commits_spec.rb
index 5907534220d..f7fd3a6e209 100644
--- a/spec/features/commits/user_view_commits_spec.rb
+++ b/spec/features/commits/user_view_commits_spec.rb
@@ -3,14 +3,10 @@
require 'spec_helper'
RSpec.describe 'Commit > User view commits' do
- let_it_be(:project) { create(:project, :public, :repository) }
- let_it_be(:user) { project.creator }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group, :public) }
- before do
- visit project_commits_path(project)
- end
-
- describe 'Commits List' do
+ shared_examples 'can view commits' do
it 'displays the correct number of commits per day in the header' do
expect(first('.js-commit-header').find('.commits-count').text).to eq('1 commit')
end
@@ -19,4 +15,51 @@ RSpec.describe 'Commit > User view commits' do
expect(page).to have_selector('#commits-list > li:nth-child(2) > ul', count: 1)
end
end
+
+ describe 'Commits List' do
+ context 'when project is public' do
+ let(:project) { create(:project, :public, :repository, group: group) }
+
+ before do
+ visit project_commits_path(project)
+ end
+
+ it_behaves_like 'can view commits'
+ end
+
+ context 'when project is public with private repository' do
+ let(:project) { create(:project, :public, :repository, :repository_private, group: group) }
+
+ context 'and user is an inherited member from the group' do
+ context 'and user is a guest' do
+ before do
+ group.add_guest(user)
+ sign_in(user)
+ visit project_commits_path(project)
+ end
+
+ it_behaves_like 'can view commits'
+ end
+ end
+ end
+
+ context 'when project is private' do
+ let(:project) { create(:project, :private, :repository, group: group) }
+
+ context 'and user is an inherited member from the group' do
+ context 'and user is a guest' do
+ before do
+ group.add_guest(user)
+ sign_in(user)
+ visit project_commits_path(project)
+ end
+
+ it 'renders not found' do
+ expect(page).to have_title('Not Found')
+ expect(page).to have_content('Page Not Found')
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb
index 7714783172f..488a4f84297 100644
--- a/spec/features/cycle_analytics_spec.rb
+++ b/spec/features/cycle_analytics_spec.rb
@@ -36,10 +36,6 @@ RSpec.describe 'Value Stream Analytics', :js do
wait_for_all_requests
end
- before do
- stub_feature_flags(use_vsa_aggregated_tables: false)
- end
-
context 'as an allowed user' do
context 'when project is new' do
before do
diff --git a/spec/features/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb
index bf9f6895d24..48a6976f263 100644
--- a/spec/features/dashboard/datetime_on_tooltips_spec.rb
+++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe 'Tooltips on .timeago dates', :js do
context 'on the activity tab' do
before do
Event.create!( project: project, author_id: user.id, action: :joined,
- updated_at: created_date, created_at: created_date)
+ updated_at: created_date, created_at: created_date)
sign_in user
visit user_activity_path(user)
diff --git a/spec/features/dashboard/milestones_spec.rb b/spec/features/dashboard/milestones_spec.rb
index 3f89955b12b..08cb95979ac 100644
--- a/spec/features/dashboard/milestones_spec.rb
+++ b/spec/features/dashboard/milestones_spec.rb
@@ -41,7 +41,12 @@ RSpec.describe 'Dashboard > Milestones' do
first('.select2-result-label').click
end
- find('.js-new-project-item-link').click
+ a_el = find('.js-new-project-item-link')
+
+ expect(a_el).to have_content('New Milestone in ')
+ expect(a_el).to have_no_content('New New Milestone in ')
+
+ a_el.click
expect(page).to have_current_path(new_group_milestone_path(group), ignore_query: true)
end
diff --git a/spec/features/dashboard/todos/todos_sorting_spec.rb b/spec/features/dashboard/todos/todos_sorting_spec.rb
index d593031590e..a0fa53b761b 100644
--- a/spec/features/dashboard/todos/todos_sorting_spec.rb
+++ b/spec/features/dashboard/todos/todos_sorting_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe 'Dashboard > User sorts todos' do
create(:todo, user: user, project: project, target: issue_3, created_at: 3.hours.ago, updated_at: 2.minutes.ago)
create(:todo, user: user, project: project, target: issue_1, created_at: 2.hours.ago, updated_at: 2.hours.ago)
create(:todo, user: user, project: project, target: merge_request_1, created_at: 1.hour.ago,
- updated_at: 1.hour.ago)
+ updated_at: 1.hour.ago)
merge_request_1.labels << label_1
issue_3.labels << label_1
diff --git a/spec/features/discussion_comments/issue_spec.rb b/spec/features/discussion_comments/issue_spec.rb
index ebb57b37918..0bb43343ecd 100644
--- a/spec/features/discussion_comments/issue_spec.rb
+++ b/spec/features/discussion_comments/issue_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe 'Thread Comments Issue', :js do
let(:issue) { create(:issue, project: project) }
before do
+ stub_feature_flags(remove_user_attributes_projects: false)
project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/discussion_comments/merge_request_spec.rb b/spec/features/discussion_comments/merge_request_spec.rb
index a90ff3721d3..4fa82de3b4b 100644
--- a/spec/features/discussion_comments/merge_request_spec.rb
+++ b/spec/features/discussion_comments/merge_request_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe 'Thread Comments Merge Request', :js do
let(:merge_request) { create(:merge_request, source_project: project) }
before do
+ stub_feature_flags(remove_user_attributes_projects: false)
project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/groups/group_runners_spec.rb b/spec/features/groups/group_runners_spec.rb
index b98c94b030d..ada03726c97 100644
--- a/spec/features/groups/group_runners_spec.rb
+++ b/spec/features/groups/group_runners_spec.rb
@@ -61,7 +61,7 @@ RSpec.describe "Group Runners" do
it 'shows a group badge' do
within_runner_row(group_runner.id) do
- expect(page).to have_selector '.badge', text: 'group'
+ expect(page).to have_selector '.badge', text: s_('Runners|Group')
end
end
@@ -101,9 +101,9 @@ RSpec.describe "Group Runners" do
let(:runner) { project_runner }
end
- it 'shows a project (specific) badge' do
+ it 'shows a project badge' do
within_runner_row(project_runner.id) do
- expect(page).to have_selector '.badge', text: 'specific'
+ expect(page).to have_selector '.badge', text: s_('Runners|Project')
end
end
@@ -137,52 +137,38 @@ RSpec.describe "Group Runners" do
focus_filtered_search
page.within(search_bar_selector) do
- expect(page).to have_link('Paused')
+ expect(page).to have_link(s_('Runners|Paused'))
expect(page).to have_content('Status')
end
end
end
end
- describe "Group runner edit page", :js do
- let!(:runner) do
- create(:ci_runner, :group, groups: [group], description: 'runner-foo', contacted_at: Time.zone.now)
+ describe "Group runner show page", :js do
+ let!(:group_runner) do
+ create(:ci_runner, :group, groups: [group], description: 'runner-foo')
end
it 'user views runner details' do
- visit group_runner_path(group, runner)
+ visit group_runner_path(group, group_runner)
expect(page).to have_content "#{s_('Runners|Description')} runner-foo"
end
+ end
- it 'user edits the runner to be protected' do
- visit edit_group_runner_path(group, runner)
-
- expect(page.find_field('runner[access_level]')).not_to be_checked
-
- check 'runner_access_level'
- click_button _('Save changes')
-
- expect(page).to have_content "#{s_('Runners|Configuration')} #{s_('Runners|Protected')}"
+ describe "Group runner edit page", :js do
+ let!(:group_runner) do
+ create(:ci_runner, :group, groups: [group])
end
- context 'when a runner has a tag' do
- before do
- runner.update!(tag_list: ['tag1'])
- end
-
- it 'user edits runner not to run untagged jobs' do
- visit edit_group_runner_path(group, runner)
-
- page.find_field('runner[tag_list]').set('tag1, tag2')
-
- uncheck 'runner_run_untagged'
- click_button _('Save changes')
+ before do
+ visit edit_group_runner_path(group, group_runner)
+ wait_for_requests
+ end
- # Tags can be in any order
- expect(page).to have_content /#{s_('Runners|Tags')}.*tag1/
- expect(page).to have_content /#{s_('Runners|Tags')}.*tag2/
- end
+ it_behaves_like 'submits edit runner form' do
+ let(:runner) { group_runner }
+ let(:runner_page_path) { group_runner_path(group, group_runner) }
end
end
end
diff --git a/spec/features/groups/navbar_spec.rb b/spec/features/groups/navbar_spec.rb
index b4faa3ce0dd..b3fb563a202 100644
--- a/spec/features/groups/navbar_spec.rb
+++ b/spec/features/groups/navbar_spec.rb
@@ -14,10 +14,12 @@ RSpec.describe 'Group navbar' do
before do
insert_package_nav(_('Kubernetes'))
+ insert_after_nav_item(_('Analytics'), new_nav_item: settings_for_maintainer_nav_item) if Gitlab.ee?
stub_config(dependency_proxy: { enabled: false })
stub_config(registry: { enabled: false })
stub_feature_flags(harbor_registry_integration: false)
+ stub_feature_flags(observability_group_tab: false)
stub_group_wikis(false)
group.add_maintainer(user)
sign_in(user)
@@ -48,7 +50,7 @@ RSpec.describe 'Group navbar' do
if Gitlab.ee?
insert_customer_relations_nav(_('Analytics'))
else
- insert_customer_relations_nav(_('Packages & Registries'))
+ insert_customer_relations_nav(_('Packages and registries'))
end
visit group_path(group)
@@ -80,7 +82,11 @@ RSpec.describe 'Group navbar' do
end
context 'when harbor registry is available' do
+ let(:harbor_integration) { create(:harbor_integration, group: group, project: nil) }
+
before do
+ group.update!(harbor_integration: harbor_integration)
+
stub_feature_flags(harbor_registry_integration: true)
insert_harbor_registry_nav(_('Package Registry'))
@@ -90,4 +96,16 @@ RSpec.describe 'Group navbar' do
it_behaves_like 'verified navigation bar'
end
+
+ context 'when observability tab is enabled' do
+ before do
+ stub_feature_flags(observability_group_tab: true)
+
+ insert_observability_nav
+
+ visit group_path(group)
+ end
+
+ it_behaves_like 'verified navigation bar'
+ end
end
diff --git a/spec/features/groups/settings/packages_and_registries_spec.rb b/spec/features/groups/settings/packages_and_registries_spec.rb
index 98dc534f54e..7f3f5775559 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 Packages & Registries settings' do
+RSpec.describe 'Group Package and registry settings' do
include WaitForRequests
let(:user) { create(:user) }
@@ -25,7 +25,7 @@ RSpec.describe 'Group Packages & Registries settings' do
settings_menu = find_settings_menu
- expect(settings_menu).not_to have_content 'Packages & Registries'
+ expect(settings_menu).not_to have_content 'Packages and registries'
end
it 'renders 404 when navigating to page' do
@@ -40,57 +40,58 @@ RSpec.describe 'Group Packages & Registries settings' do
visit group_path(group)
settings_menu = find_settings_menu
- expect(settings_menu).to have_content 'Packages & Registries'
+ expect(settings_menu).to have_content 'Packages and registries'
end
it 'has a page title set' do
visit_settings_page
- expect(page).to have_title _('Packages & Registries')
+ expect(page).to have_title _('Package and registry settings')
end
it 'sidebar menu is open' do
visit_settings_page
sidebar = find('.nav-sidebar')
- expect(sidebar).to have_link _('Packages & Registries')
+ expect(sidebar).to have_link _('Packages and registries')
end
it 'has a Duplicate packages section', :js do
visit_settings_page
expect(page).to have_content('Duplicate packages')
+ expect(page).to have_content('Allow duplicates')
+ expect(page).to have_content('Exceptions')
end
it 'automatically saves changes to the server', :js do
visit_settings_page
+ wait_for_requests
within '[data-testid="maven-settings"]' do
- expect(page).to have_content('Reject packages with the same name and version')
- expect(page).not_to have_content('Exceptions')
+ expect(page).to have_field _('Exceptions'), disabled: true
- find('.gl-toggle').click
+ click_button class: 'gl-toggle'
- expect(page).to have_content('Exceptions')
+ expect(page).to have_field _('Exceptions'), disabled: false
visit_settings_page
- expect(page).to have_content('Exceptions')
+ expect(page).to have_field _('Exceptions'), disabled: false
end
end
it 'shows an error on wrong regex', :js do
visit_settings_page
+ wait_for_requests
within '[data-testid="maven-settings"]' do
- expect(page).to have_content('Reject packages with the same name and version')
+ click_button class: 'gl-toggle'
- find('.gl-toggle').click
-
- fill_in 'Exceptions', with: ')'
+ fill_in _('Exceptions'), with: ')'
# simulate blur event
- find('#maven-duplicated-settings-regex-input').native.send_keys(:tab)
+ send_keys(:tab)
end
expect(page).to have_content('is an invalid regexp')
@@ -99,13 +100,16 @@ RSpec.describe 'Group Packages & Registries settings' do
context 'in a sub group' do
it 'works correctly', :js do
visit_sub_group_settings_page
+ wait_for_requests
within '[data-testid="maven-settings"]' do
- expect(page).to have_content('Reject packages with the same name and version')
+ expect(page).to have_content('Allow duplicates')
+
+ expect(page).to have_field _('Exceptions'), disabled: true
- find('.gl-toggle').click
+ click_button class: 'gl-toggle'
- expect(page).to have_content('Exceptions')
+ expect(page).to have_field _('Exceptions'), disabled: false
end
end
end
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 998c3d2ca3f..fe0dd7cec9a 100644
--- a/spec/features/groups/settings/user_searches_in_settings_spec.rb
+++ b/spec/features/groups/settings/user_searches_in_settings_spec.rb
@@ -43,7 +43,7 @@ RSpec.describe 'User searches group settings', :js do
it_behaves_like 'can search settings', 'Variables', 'Auto DevOps'
end
- context 'in Packages & Registries page' do
+ context 'in Packages and registries page' do
before do
visit group_settings_packages_and_registries_path(group)
end
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index c93ed01b873..4e02f6f7ca2 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -517,75 +517,4 @@ RSpec.describe 'Group' do
fill_in 'confirm_name_input', with: confirm_with
click_button 'Confirm'
end
-
- describe 'storage_enforcement_banner', :js do
- let_it_be(:group) { create(:group) }
- let_it_be_with_refind(:user) { create(:user) }
-
- before do
- group.add_owner(user)
- sign_in(user)
- end
-
- context 'with storage_enforcement_date set' do
- let_it_be(:storage_enforcement_date) { Date.today + 30 }
-
- before do
- allow_next_found_instance_of(Group) do |group|
- allow(group).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
- end
- end
-
- it 'displays the banner in the group page' do
- visit group_path(group)
- expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
- end
-
- it 'does not display the banner in a paid group page' do
- allow_next_found_instance_of(Group) do |group|
- allow(group).to receive(:paid?).and_return(true)
- end
- visit group_path(group)
- expect_page_not_to_have_storage_enforcement_banner
- end
-
- it 'does not display the banner if user has previously closed unless threshold has changed' do
- visit group_path(group)
- expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
- find('.js-storage-enforcement-banner [data-testid="close-icon"]').click
- wait_for_requests
- page.refresh
- expect_page_not_to_have_storage_enforcement_banner
-
- storage_enforcement_date = Date.today + 13
- allow_next_found_instance_of(Group) do |group|
- allow(group).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
- end
- page.refresh
- expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
- end
- end
-
- context 'with storage_enforcement_date not set' do
- before do
- allow_next_found_instance_of(Group) do |group|
- allow(group).to receive(:storage_enforcement_date).and_return(nil)
- end
- end
-
- it 'does not display the banner in the group page' do
- stub_feature_flags(namespace_storage_limit_bypass_date_check: false)
- visit group_path(group)
- expect_page_not_to_have_storage_enforcement_banner
- end
- end
- end
-
- def expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
- expect(page).to have_text "Effective #{storage_enforcement_date}, namespace storage limits will apply"
- end
-
- def expect_page_not_to_have_storage_enforcement_banner
- expect(page).not_to have_text "namespace storage limits will apply"
- end
end
diff --git a/spec/features/help_dropdown_spec.rb b/spec/features/help_dropdown_spec.rb
index db98f58240d..e64c19d4708 100644
--- a/spec/features/help_dropdown_spec.rb
+++ b/spec/features/help_dropdown_spec.rb
@@ -51,7 +51,8 @@ RSpec.describe "Help Dropdown", :js do
visit root_path
end
- it 'renders correct version badge variant' do
+ it 'renders correct version badge variant',
+ quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/369850' do
page.within '.header-help' do
find('.header-help-dropdown-toggle').click
diff --git a/spec/features/ide/user_commits_changes_spec.rb b/spec/features/ide/user_commits_changes_spec.rb
index e1e586a4f18..04b215710b3 100644
--- a/spec/features/ide/user_commits_changes_spec.rb
+++ b/spec/features/ide/user_commits_changes_spec.rb
@@ -9,6 +9,8 @@ RSpec.describe 'IDE user commits changes', :js do
let(:user) { project.first_owner }
before do
+ stub_feature_flags(vscode_web_ide: false)
+
sign_in(user)
ide_visit(project)
diff --git a/spec/features/ide/user_opens_merge_request_spec.rb b/spec/features/ide/user_opens_merge_request_spec.rb
index 8f4668d49ee..8a95d7c5544 100644
--- a/spec/features/ide/user_opens_merge_request_spec.rb
+++ b/spec/features/ide/user_opens_merge_request_spec.rb
@@ -8,6 +8,8 @@ RSpec.describe 'IDE merge request', :js do
let(:user) { project.first_owner }
before do
+ stub_feature_flags(vscode_web_ide: false)
+
sign_in(user)
visit(merge_request_path(merge_request))
diff --git a/spec/features/ide_spec.rb b/spec/features/ide_spec.rb
index 2505ab0afee..c7c740c2293 100644
--- a/spec/features/ide_spec.rb
+++ b/spec/features/ide_spec.rb
@@ -4,12 +4,14 @@ require 'spec_helper'
RSpec.describe 'IDE', :js do
describe 'sub-groups' do
+ let(:ide_iframe_selector) { '#ide iframe' }
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:subgroup) { create(:group, parent: group) }
let(:subgroup_project) { create(:project, :repository, namespace: subgroup) }
before do
+ stub_feature_flags(vscode_web_ide: vscode_ff)
subgroup_project.add_maintainer(user)
sign_in(user)
@@ -20,8 +22,28 @@ RSpec.describe 'IDE', :js do
wait_for_requests
end
- it 'loads project in web IDE' do
- expect(page).to have_selector('.context-header', text: subgroup_project.name)
+ context 'with vscode feature flag on' do
+ let(:vscode_ff) { true }
+
+ it 'loads project in Web IDE' do
+ iframe = find(ide_iframe_selector)
+
+ page.within_frame(iframe) do
+ expect(page).to have_selector('.title', text: subgroup_project.name.upcase)
+ end
+ end
+ end
+
+ context 'with vscode feature flag off' do
+ let(:vscode_ff) { false }
+
+ it 'loads project in legacy Web IDE' do
+ expect(page).to have_selector('.context-header', text: subgroup_project.name)
+ end
+
+ it 'does not load new Web IDE' do
+ expect(page).not_to have_selector(ide_iframe_selector)
+ end
end
end
end
diff --git a/spec/features/incidents/incident_timeline_events_spec.rb b/spec/features/incidents/incident_timeline_events_spec.rb
index e39f348013c..6db9f87d6f2 100644
--- a/spec/features/incidents/incident_timeline_events_spec.rb
+++ b/spec/features/incidents/incident_timeline_events_spec.rb
@@ -17,20 +17,20 @@ RSpec.describe 'Incident timeline events', :js do
visit project_issues_incident_path(project, incident)
wait_for_requests
- click_link 'Timeline'
+ click_link s_('Incident|Timeline')
end
context 'when add event is clicked' do
it 'submits event data when save is clicked' do
- click_button 'Add new timeline event'
+ click_button s_('Incident|Add new timeline event')
expect(page).to have_selector('.common-note-form')
- fill_in 'Description', with: 'Event note goes here'
+ fill_in _('Description'), with: 'Event note goes here'
fill_in 'timeline-input-hours', with: '07'
fill_in 'timeline-input-minutes', with: '25'
- click_button 'Save'
+ click_button _('Save')
expect(page).to have_selector('.incident-timeline-events')
@@ -41,30 +41,62 @@ RSpec.describe 'Incident timeline events', :js do
end
end
- context 'when delete event is clicked' do
+ context 'when edit is clicked' do
before do
click_button 'Add new timeline event'
- fill_in 'Description', with: 'Event note to delete'
- click_button 'Save'
+ fill_in 'Description', with: 'Event note to edit'
+ click_button _('Save')
+ end
+
+ it 'shows the confirmation modal and edits the event' do
+ click_button _('More actions')
+
+ page.within '.gl-new-dropdown-contents' do
+ expect(page).to have_content(_('Edit'))
+ page.find('.gl-new-dropdown-item-text-primary', text: _('Edit')).click
+ end
+
+ expect(page).to have_selector('.common-note-form')
+
+ fill_in _('Description'), with: 'Event note goes here'
+ fill_in 'timeline-input-hours', with: '07'
+ fill_in 'timeline-input-minutes', with: '25'
+
+ click_button _('Save')
+
+ wait_for_requests
+
+ page.within '.timeline-event-note' do
+ expect(page).to have_content('Event note goes here')
+ expect(page).to have_content('07:25')
+ end
+ end
+ end
+
+ context 'when delete is clicked' do
+ before do
+ click_button s_('Incident|Add new timeline event')
+ fill_in _('Description'), with: 'Event note to delete'
+ click_button _('Save')
end
it 'shows the confirmation modal and deletes the event' do
- click_button 'More actions'
+ click_button _('More actions')
- page.within '.gl-new-dropdown-item-text-wrapper' do
- expect(page).to have_content('Delete')
+ page.within '.gl-new-dropdown-contents' do
+ expect(page).to have_content(_('Delete'))
page.find('.gl-new-dropdown-item-text-primary', text: 'Delete').click
end
page.within '.modal' do
- expect(page).to have_content('Delete event')
+ expect(page).to have_content(s_('Incident|Delete event'))
end
- click_button 'Delete event'
+ click_button s_('Incident|Delete event')
wait_for_requests
- expect(page).to have_content('No timeline items have been added yet.')
+ expect(page).to have_content(s_('Incident|No timeline items have been added yet.'))
end
end
end
diff --git a/spec/features/incidents/user_uses_quick_actions_spec.rb b/spec/features/incidents/user_uses_quick_actions_spec.rb
new file mode 100644
index 00000000000..fce9eadd42f
--- /dev/null
+++ b/spec/features/incidents/user_uses_quick_actions_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Incidents > User uses quick actions', :js do
+ include Spec::Support::Helpers::Features::NotesHelpers
+
+ describe 'incident-only commands' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:issue, reload: true) { create(:incident, project: project) }
+
+ before do
+ project.add_developer(user)
+ sign_in(user)
+ visit project_issue_path(project, issue)
+ wait_for_all_requests
+ end
+
+ after do
+ wait_for_requests
+ end
+
+ it_behaves_like 'timeline quick action'
+ end
+end
diff --git a/spec/features/invites_spec.rb b/spec/features/invites_spec.rb
index 1baa97096d9..34990a53b51 100644
--- a/spec/features/invites_spec.rb
+++ b/spec/features/invites_spec.rb
@@ -10,6 +10,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do
let(:group_invite) { group.group_members.invite.last }
before do
+ stub_feature_flags(arkose_labs_signup_challenge: false)
stub_application_setting(require_admin_approval_after_user_signup: false)
project.add_maintainer(owner)
group.add_owner(owner)
@@ -245,6 +246,7 @@ RSpec.describe 'Group or Project invitations', :aggregate_failures do
before do
stub_feature_flags(soft_email_confirmation: false)
allow(User).to receive(:allow_unconfirmed_access_for).and_return 0
+ stub_feature_flags(identity_verification: false)
end
it 'signs up and redirects to the group activity page' do
diff --git a/spec/features/issuables/markdown_references/jira_spec.rb b/spec/features/issuables/markdown_references/jira_spec.rb
index ae9c8d31c02..9d46b3a274e 100644
--- a/spec/features/issuables/markdown_references/jira_spec.rb
+++ b/spec/features/issuables/markdown_references/jira_spec.rb
@@ -15,6 +15,8 @@ RSpec.describe "Jira", :js do
before do
remotelink = double(:remotelink, all: [], build: double(save!: true))
+ stub_feature_flags(remove_user_attributes_projects: false)
+
stub_request(:get, "https://jira.example.com/rest/api/2/issue/JIRA-5")
stub_request(:post, "https://jira.example.com/rest/api/2/issue/JIRA-5/comment")
allow_next_instance_of(JIRA::Resource::Issue) do |instance|
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 1e8b9b6b60b..a385e8a5fd0 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
@@ -18,6 +18,10 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j
end
end
+ before do
+ stub_feature_flags(moved_mr_sidebar: false)
+ end
+
describe 'as a user with access to the project' do
before do
project.add_maintainer(user)
@@ -26,14 +30,16 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j
end
it 'shows a button to resolve all threads by creating a new issue' do
+ find('.discussions-counter .dropdown-toggle').click
+
within('.discussions-counter') do
- expect(page).to have_selector resolve_all_discussions_link_selector( title: "Create issue to resolve all threads" )
+ expect(page).to have_link(_("Create issue to resolve all threads"), href: new_project_issue_path(project, merge_request_to_resolve_discussions_of: merge_request.iid))
end
end
context 'resolving the thread' do
before do
- find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage
+ find('button[data-testid="resolve-discussion-button"]').click
end
it 'hides the link for creating a new issue' do
@@ -44,6 +50,7 @@ RSpec.describe 'Resolving all open threads in a merge request from an issue', :j
context 'creating an issue for threads' do
before do
+ find('.discussions-counter .dropdown-toggle').click
find(resolve_all_discussions_link_selector).click
end
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 0de15d3d304..5ff61a52b21 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
@@ -35,7 +35,7 @@ RSpec.describe 'Resolve an open thread in a merge request by creating an issue',
context 'resolving the thread' do
before do
- find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage
+ find('button[data-testid="resolve-discussion-button"]').click
end
it 'hides the link for creating a new issue' do
diff --git a/spec/features/issues/issue_detail_spec.rb b/spec/features/issues/issue_detail_spec.rb
index 2af54e51bb7..a253e6f4c86 100644
--- a/spec/features/issues/issue_detail_spec.rb
+++ b/spec/features/issues/issue_detail_spec.rb
@@ -68,8 +68,12 @@ RSpec.describe 'Issue Detail', :js do
end
context 'when edited by a user who is later deleted' do
+ let(:user_to_be_deleted) { create(:user) }
+
before do
- sign_in(user)
+ project.add_developer(user_to_be_deleted)
+
+ sign_in(user_to_be_deleted)
visit project_issue_path(project, issue)
wait_for_requests
@@ -78,8 +82,9 @@ RSpec.describe 'Issue Detail', :js do
click_button 'Save changes'
wait_for_requests
- Users::DestroyService.new(user).execute(user)
+ Users::DestroyService.new(user_to_be_deleted).execute(user_to_be_deleted)
+ sign_in(user)
visit project_issue_path(project, issue)
end
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index 8819f085a5f..6fa8a52a9c5 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -302,7 +302,9 @@ RSpec.describe 'Issue Sidebar' do
context 'sidebar', :js do
it 'finds issue copy forwarding email' do
- expect(find('[data-qa-selector="copy-forward-email"]').text).to eq "Issue email: #{issue.creatable_note_email_address(user)}" # rubocop:disable QA/SelectorUsage
+ expect(
+ find('[data-testid="copy-forward-email"]').text
+ ).to eq "Issue email: #{issue.creatable_note_email_address(user)}"
end
end
@@ -338,7 +340,7 @@ RSpec.describe 'Issue Sidebar' do
end
it 'does not find issue email' do
- expect(page).not_to have_selector('[data-qa-selector="copy-forward-email"]') # rubocop:disable QA/SelectorUsage
+ expect(page).not_to have_selector('[data-testid="copy-forward-email"]')
end
end
end
diff --git a/spec/features/issues/resource_label_events_spec.rb b/spec/features/issues/resource_label_events_spec.rb
index e08410efc0b..e4da2f67516 100644
--- a/spec/features/issues/resource_label_events_spec.rb
+++ b/spec/features/issues/resource_label_events_spec.rb
@@ -14,6 +14,7 @@ RSpec.describe 'List issue resource label events', :js do
let!(:event) { create(:resource_label_event, user: user, issue: issue, label: label) }
before do
+ stub_feature_flags(remove_user_attributes_projects: false)
visit project_issue_path(project, issue)
wait_for_requests
end
diff --git a/spec/features/issues/user_creates_issue_spec.rb b/spec/features/issues/user_creates_issue_spec.rb
index e29911e3263..b96490bd7e7 100644
--- a/spec/features/issues/user_creates_issue_spec.rb
+++ b/spec/features/issues/user_creates_issue_spec.rb
@@ -269,7 +269,7 @@ RSpec.describe "User creates issue" do
end
it 'hides the weight input' do
- expect(page).not_to have_selector('.qa-issuable-weight-input') # rubocop:disable QA/SelectorUsage
+ expect(page).not_to have_selector('[data-testid="issuable-weight-input"]')
end
it 'shows the incident help text' do
diff --git a/spec/features/issues/user_interacts_with_awards_spec.rb b/spec/features/issues/user_interacts_with_awards_spec.rb
index c86a2c32e2d..8ed56108f00 100644
--- a/spec/features/issues/user_interacts_with_awards_spec.rb
+++ b/spec/features/issues/user_interacts_with_awards_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe 'User interacts with awards' do
+ include MobileHelpers
+
let(:user) { create(:user) }
describe 'User interacts with awards in an issue', :js do
@@ -130,6 +132,7 @@ RSpec.describe 'User interacts with awards' do
end
it 'allows adding a new emoji' do
+ resize_window(1200, 800)
page.within('.note-actions') do
find('.note-emoji-button').click
end
@@ -140,6 +143,7 @@ RSpec.describe 'User interacts with awards' do
expect(page).to have_emoji('8ball')
end
expect(note.reload.award_emoji.size).to eq(2)
+ restore_window_size
end
context 'when the project is archived' do
diff --git a/spec/features/issues/user_sees_empty_state_spec.rb b/spec/features/issues/user_sees_empty_state_spec.rb
index b43ba01606a..0e2a7cb4358 100644
--- a/spec/features/issues/user_sees_empty_state_spec.rb
+++ b/spec/features/issues/user_sees_empty_state_spec.rb
@@ -40,8 +40,8 @@ RSpec.describe 'Issues > User sees empty state', :js do
it 'user sees empty state' do
visit project_issues_path(project)
- expect(page).to have_content('The Issue Tracker is the place to add things that need to be improved or solved in a project')
- expect(page).to have_content('Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable.')
+ expect(page).to have_content('Use issues to collaborate on ideas, solve problems, and plan work')
+ expect(page).to have_content('Learn more about issues.')
expect(page).to have_content('New issue')
end
diff --git a/spec/features/issues/user_sorts_issue_comments_spec.rb b/spec/features/issues/user_sorts_issue_comments_spec.rb
index 555f8827374..4b38ce329b8 100644
--- a/spec/features/issues/user_sorts_issue_comments_spec.rb
+++ b/spec/features/issues/user_sorts_issue_comments_spec.rb
@@ -16,7 +16,9 @@ RSpec.describe 'Comment sort direction' do
it 'saves sort order' do
# open dropdown, and select 'Newest first'
page.within('.issuable-details') do
+ find('#discussion-preferences-dropdown').click
click_button('Oldest first')
+ find('#discussion-preferences-dropdown').click
click_button('Newest first')
end
diff --git a/spec/features/jira_oauth_provider_authorize_spec.rb b/spec/features/jira_oauth_provider_authorize_spec.rb
index a216d2d44b2..eb26440aff9 100644
--- a/spec/features/jira_oauth_provider_authorize_spec.rb
+++ b/spec/features/jira_oauth_provider_authorize_spec.rb
@@ -10,10 +10,10 @@ RSpec.describe 'JIRA OAuth Provider' do
sign_in(user)
visit oauth_jira_dvcs_authorize_path(client_id: application.uid,
- redirect_uri: oauth_jira_dvcs_callback_url,
- response_type: 'code',
- state: 'my_state',
- scope: 'read_user')
+ redirect_uri: oauth_jira_dvcs_callback_url,
+ response_type: 'code',
+ state: 'my_state',
+ scope: 'read_user')
end
it_behaves_like 'Secure OAuth Authorizations'
diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb
index 9eff02a8c1b..08f9b8eda13 100644
--- a/spec/features/markdown/markdown_spec.rb
+++ b/spec/features/markdown/markdown_spec.rb
@@ -263,6 +263,10 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do
expect(doc).to parse_task_lists
end
+ aggregate_failures 'MathFilter' do
+ expect(doc).to parse_math
+ end
+
aggregate_failures 'InlineDiffFilter' do
expect(doc).to parse_inline_diffs
end
diff --git a/spec/features/merge_request/batch_comments_spec.rb b/spec/features/merge_request/batch_comments_spec.rb
index fafaea8ac68..bccdc3c4c62 100644
--- a/spec/features/merge_request/batch_comments_spec.rb
+++ b/spec/features/merge_request/batch_comments_spec.rb
@@ -13,6 +13,8 @@ RSpec.describe 'Merge request > Batch comments', :js do
end
before do
+ stub_feature_flags(moved_mr_sidebar: false)
+
project.add_maintainer(user)
sign_in(user)
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 f21929e5275..fd33731cb7b 100644
--- a/spec/features/merge_request/user_comments_on_diff_spec.rb
+++ b/spec/features/merge_request/user_comments_on_diff_spec.rb
@@ -14,6 +14,7 @@ RSpec.describe 'User comments on a diff', :js do
let(:user) { create(:user) }
before do
+ stub_feature_flags(remove_user_attributes_projects: false)
project.add_maintainer(user)
sign_in(user)
@@ -256,9 +257,7 @@ RSpec.describe 'User comments on a diff', :js do
click_button('Delete comment', match: :first)
end
- page.within('.merge-request-tabs') do
- find('.notes-tab').click
- end
+ find('.notes-tab', visible: true).click
wait_for_requests
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..ec1e2fea851 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
@@ -10,6 +10,7 @@ RSpec.describe 'User comments on a merge request', :js do
let(:user) { create(:user) }
before do
+ stub_feature_flags(remove_user_attributes_projects: false)
project.add_maintainer(user)
sign_in(user)
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 c8b22bb3125..0ae4ef18649 100644
--- a/spec/features/merge_request/user_creates_merge_request_spec.rb
+++ b/spec/features/merge_request/user_creates_merge_request_spec.rb
@@ -1,111 +1,164 @@
# frozen_string_literal: true
-require "spec_helper"
+require 'spec_helper'
-RSpec.describe "User creates a merge request", :js do
+RSpec.describe 'User creates a merge request', :js do
include ProjectForksHelper
- let_it_be(:project) { create(:project, :repository) }
- let_it_be(:user) { create(:user) }
-
- let(:title) { "Some feature" }
-
- before do
- project.add_maintainer(user)
- sign_in(user)
- end
-
- it "creates a merge request" do
- visit(project_new_merge_request_path(project))
+ shared_examples 'creates a merge request' do
+ specify do
+ visit(project_new_merge_request_path(project))
- find(".js-source-branch").click
- click_link("fix")
+ compare_source_and_target('fix', 'feature')
- find(".js-target-branch").click
- click_link("feature")
+ page.within('.merge-request-form') do
+ expect(page.find('#merge_request_description')['placeholder']).to eq 'Describe the goal of the changes and what reviewers should be aware of.'
+ end
- click_button("Compare branches")
+ fill_in('Title', with: title)
+ click_button('Create merge request')
- page.within('.merge-request-form') do
- expect(page.find('#merge_request_description')['placeholder']).to eq 'Describe the goal of the changes and what reviewers should be aware of.'
+ page.within('.merge-request') do
+ expect(page).to have_content(title)
+ end
end
+ end
- fill_in("Title", with: title)
- click_button("Create merge request")
+ shared_examples 'renders not found' do
+ specify do
+ visit project_new_merge_request_path(project)
- page.within(".merge-request") do
- expect(page).to have_content(title)
+ expect(page).to have_title('Not Found')
+ expect(page).to have_content('Page Not Found')
end
end
- context "XSS branch name exists" do
+ context 'when user is a direct project member' do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
+
+ let(:title) { 'Some feature' }
+
before do
- project.repository.create_branch("<img/src='x'/onerror=alert('oops')>", "master")
+ project.add_maintainer(user)
+ sign_in(user)
end
- it "doesn't execute the dodgy branch name" do
- visit(project_new_merge_request_path(project))
+ it_behaves_like 'creates a merge request'
- find(".js-source-branch").click
- click_link("<img/src='x'/onerror=alert('oops')>")
+ context 'with XSS branch name' do
+ before do
+ project.repository.create_branch("<img/src='x'/onerror=alert('oops')>", 'master')
+ end
- find(".js-target-branch").click
- click_link("feature")
+ it 'does not execute the suspicious branch name' do
+ visit(project_new_merge_request_path(project))
- click_button("Compare branches")
+ compare_source_and_target("<img/src='x'/onerror=alert('oops')>", 'feature')
- expect { page.driver.browser.switch_to.alert }.to raise_error(Selenium::WebDriver::Error::NoSuchAlertError)
+ expect { page.driver.browser.switch_to.alert }.to raise_error(Selenium::WebDriver::Error::NoSuchAlertError)
+ end
end
- end
- context "to a forked project" do
- let(:forked_project) { fork_project(project, user, namespace: user.namespace, repository: true) }
+ context 'to a forked project' do
+ let(:forked_project) { fork_project(project, user, namespace: user.namespace, repository: true) }
+
+ it 'creates a merge request', :sidekiq_might_not_need_inline do
+ visit(project_new_merge_request_path(forked_project))
+
+ expect(page).to have_content('Source branch').and have_content('Target branch')
+ expect(find('#merge_request_target_project_id', visible: false).value).to eq(project.id.to_s)
- it "creates a merge request", :sidekiq_might_not_need_inline do
- visit(project_new_merge_request_path(forked_project))
+ click_button('Compare branches and continue')
- expect(page).to have_content("Source branch").and have_content("Target branch")
- expect(find("#merge_request_target_project_id", visible: false).value).to eq(project.id.to_s)
+ expect(page).to have_content('You must select source and target branch')
- click_button("Compare branches and continue")
+ first('.js-source-project').click
+ first('.dropdown-source-project a', text: forked_project.full_path)
- expect(page).to have_content("You must select source and target branch")
+ first('.js-target-project').click
+ first('.dropdown-target-project a', text: project.full_path)
- first(".js-source-project").click
- first(".dropdown-source-project a", text: forked_project.full_path)
+ first('.js-source-branch').click
- first(".js-target-project").click
- first(".dropdown-target-project a", text: project.full_path)
+ wait_for_requests
- first(".js-source-branch").click
+ source_branch = 'fix'
- wait_for_requests
+ first('.js-source-branch-dropdown .dropdown-content a', text: source_branch).click
- source_branch = "fix"
+ click_button('Compare branches and continue')
- first(".js-source-branch-dropdown .dropdown-content a", text: source_branch).click
+ expect(page).to have_text _('New merge request')
- click_button("Compare branches and continue")
+ page.within('form#new_merge_request') do
+ fill_in('Title', with: title)
+ end
- expect(page).to have_text _('New merge request')
+ expect(find('.js-assignee-search')['data-project-id']).to eq(project.id.to_s)
+ find('.js-assignee-search').click
- page.within("form#new_merge_request") do
- fill_in("Title", with: title)
+ page.within('.dropdown-menu-user') do
+ expect(page).to have_content('Unassigned')
+ .and have_content(user.name)
+ .and have_content(project.users.first.name)
+ end
+ find('.js-assignee-search').click
+
+ click_button('Create merge request')
+
+ expect(page).to have_content(title).and have_content("requested to merge #{forked_project.full_path}:#{source_branch} into master")
+ end
+ end
+ end
+
+ context 'when user is an inherited member from the group' do
+ let_it_be(:group) { create(:group, :public) }
+
+ let(:user) { create(:user) }
+
+ context 'when project is public and merge requests are private' do
+ let_it_be(:project) do
+ create(:project,
+ :public,
+ :repository,
+ group: group,
+ merge_requests_access_level: ProjectFeature::DISABLED)
end
- expect(find(".js-assignee-search")["data-project-id"]).to eq(project.id.to_s)
- find('.js-assignee-search').click
+ context 'and user is a guest' do
+ before do
+ group.add_guest(user)
+ sign_in(user)
+ end
- page.within(".dropdown-menu-user") do
- expect(page).to have_content("Unassigned")
- .and have_content(user.name)
- .and have_content(project.users.first.name)
+ it_behaves_like 'renders not found'
end
- find('.js-assignee-search').click
+ end
+
+ context 'when project is private' do
+ let_it_be(:project) { create(:project, :private, :repository, group: group) }
- click_button("Create merge request")
+ context 'and user is a guest' do
+ before do
+ group.add_guest(user)
+ sign_in(user)
+ end
- expect(page).to have_content(title).and have_content("requested to merge #{forked_project.full_path}:#{source_branch} into master")
+ it_behaves_like 'renders not found'
+ end
end
end
+
+ private
+
+ def compare_source_and_target(source_branch, target_branch)
+ find('.js-source-branch').click
+ click_link(source_branch)
+
+ find('.js-target-branch').click
+ click_link(target_branch)
+
+ click_button('Compare branches')
+ end
end
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 0b4b9d7452a..4ac25ea7ae0 100644
--- a/spec/features/merge_request/user_edits_merge_request_spec.rb
+++ b/spec/features/merge_request/user_edits_merge_request_spec.rb
@@ -3,8 +3,6 @@
require 'spec_helper'
RSpec.describe 'User edits a merge request', :js do
- include Select2Helper
-
let(:project) { create(:project, :repository) }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
let(:user) { create(:user) }
@@ -89,7 +87,12 @@ RSpec.describe 'User edits a merge request', :js do
it 'allows user to change target branch' do
expect(page).to have_content('From master into feature')
- select2('merge-test', from: '#merge_request_target_branch')
+ first('.js-target-branch').click
+
+ wait_for_requests
+
+ first('.js-target-branch-dropdown a', text: 'merge-test').click
+
click_button('Save changes')
expect(page).to have_content("requested to merge #{merge_request.source_branch} into merge-test")
@@ -101,7 +104,7 @@ RSpec.describe 'User edits a merge request', :js do
it 'does not allow user to change target branch' do
expect(page).to have_content('From master into feature')
- expect(page).not_to have_selector('.select2-container')
+ expect(page).not_to have_selector('.js-target-branch.js-compare-dropdown')
end
end
end
diff --git a/spec/features/merge_request/user_manages_subscription_spec.rb b/spec/features/merge_request/user_manages_subscription_spec.rb
index c64c761b8d1..9fb85957979 100644
--- a/spec/features/merge_request/user_manages_subscription_spec.rb
+++ b/spec/features/merge_request/user_manages_subscription_spec.rb
@@ -45,21 +45,21 @@ RSpec.describe 'User manages subscription', :js do
click_button 'Toggle dropdown'
- expect(page).to have_content('Turn on notifications')
- click_button 'Turn on notifications'
+ expect(page).to have_selector('.gl-toggle:not(.is-checked)')
+ find('[data-testid="notifications-toggle"] .gl-toggle').click
wait_for_requests
click_button 'Toggle dropdown'
- expect(page).to have_content('Turn off notifications')
- click_button 'Turn off notifications'
+ expect(page).to have_selector('.gl-toggle.is-checked')
+ find('[data-testid="notifications-toggle"] .gl-toggle').click
wait_for_requests
click_button 'Toggle dropdown'
- expect(page).to have_content('Turn on notifications')
+ expect(page).to have_selector('.gl-toggle:not(.is-checked)')
end
end
end
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 e09ec11f095..332426de07e 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
@@ -15,6 +15,10 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
diff_refs: merge_request.diff_refs)
end
+ before do
+ stub_feature_flags(moved_mr_sidebar: false)
+ end
+
context 'no threads' do
before do
project.add_maintainer(user)
@@ -62,7 +66,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'allows user to mark thread as resolved' do
page.within '.diff-content' do
- find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage
+ find('button[data-testid="resolve-discussion-button"]').click
end
expect(page).to have_selector('.discussion-body', visible: false)
@@ -78,7 +82,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'allows user to unresolve thread' do
page.within '.diff-content' do
- find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage
+ find('button[data-testid="resolve-discussion-button"]').click
click_button 'Unresolve thread'
end
@@ -90,7 +94,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
describe 'resolved thread' do
before do
page.within '.diff-content' do
- find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage
+ find('button[data-testid="resolve-discussion-button"]').click
end
visit_merge_request
@@ -190,7 +194,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'allows user to resolve from reply form without a comment' do
page.within '.diff-content' do
- find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage
+ find('button[data-testid="resolve-discussion-button"]').click
end
page.within '.discussions-counter' do
@@ -225,7 +229,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'hides jump to next button when all resolved' do
page.within '.diff-content' do
- find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage
+ find('button[data-testid="resolve-discussion-button"]').click
end
expect(page).to have_selector('.discussion-next-btn', visible: false)
@@ -320,7 +324,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'allows user to mark all threads as resolved' do
page.all('.discussion-reply-holder', count: 2).each do |reply_holder|
page.within reply_holder do
- find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage
+ find('button[data-testid="resolve-discussion-button"]').click
end
end
@@ -331,7 +335,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'allows user to quickly scroll to next unresolved thread' do
page.within('.discussion-reply-holder', match: :first) do
- find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage
+ find('button[data-testid="resolve-discussion-button"]').click
end
page.within '.discussions-counter' do
@@ -402,7 +406,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'allows user to mark thread as resolved' do
page.within '.diff-content' do
- find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage
+ find('button[data-testid="resolve-discussion-button"]').click
end
page.within '.diff-content .note' do
@@ -416,7 +420,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'allows user to unresolve thread' do
page.within '.diff-content' do
- find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage
+ find('button[data-testid="resolve-discussion-button"]').click
click_button 'Unresolve thread'
end
@@ -443,7 +447,7 @@ RSpec.describe 'Merge request > User resolves diff notes and threads', :js do
it 'allows user to comment & unresolve thread' do
page.within '.diff-content' do
- find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage
+ find('button[data-testid="resolve-discussion-button"]').click
find_field('Reply…').click
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 c02149eed87..63ac7862b06 100644
--- a/spec/features/merge_request/user_sees_deployment_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_deployment_widget_spec.rb
@@ -111,7 +111,7 @@ RSpec.describe 'Merge request > User sees deployment widget', :js do
context 'with stop action' do
let(:manual) do
create(:ci_build, :manual, pipeline: pipeline,
- name: 'close_app', environment: environment.name)
+ name: 'close_app', environment: environment.name)
end
before do
diff --git a/spec/features/merge_request/user_sees_merge_widget_spec.rb b/spec/features/merge_request/user_sees_merge_widget_spec.rb
index 1d3effd4a2a..c2a0e528ea7 100644
--- a/spec/features/merge_request/user_sees_merge_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb
@@ -219,7 +219,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do
shared_examples 'pipeline widget' do
it 'shows head pipeline information', :sidekiq_might_not_need_inline do
within '.ci-widget-content' do
- expect(page).to have_content("Detached merge request pipeline ##{pipeline.id} pending for #{pipeline.short_sha}")
+ expect(page).to have_content("Merge request pipeline ##{pipeline.id} pending for #{pipeline.short_sha}")
end
end
end
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 563120fc8b7..ca102913369 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,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 'Merge request > User uses quick actions', :js do
+RSpec.describe 'Merge request > User uses quick actions', :js, :use_clean_rails_redis_caching do
include Spec::Support::Helpers::Features::NotesHelpers
let(:project) { create(:project, :public, :repository) }
diff --git a/spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb b/spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb
index b214486b3c1..d3ea8b955f2 100644
--- a/spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb
+++ b/spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb
@@ -8,6 +8,10 @@ RSpec.describe 'Project > Merge request > View user status' do
create(:merge_request, source_project: project, target_project: project, author: create(:user))
end
+ before do
+ stub_feature_flags(remove_user_attributes_projects: false)
+ end
+
subject { visit merge_request_path(merge_request) }
context 'for notes', :js do
diff --git a/spec/features/monitor_sidebar_link_spec.rb b/spec/features/monitor_sidebar_link_spec.rb
index b888e2f4171..f612956600f 100644
--- a/spec/features/monitor_sidebar_link_spec.rb
+++ b/spec/features/monitor_sidebar_link_spec.rb
@@ -4,39 +4,59 @@ require 'spec_helper'
RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
let_it_be_with_reload(:project) { create(:project, :internal, :repository) }
+ let_it_be(:user) { create(:user) }
- let(:user) { create(:user) }
- let(:access_level) { ProjectFeature::PUBLIC }
let(:role) { nil }
before do
project.add_role(user, role) if role
- project.project_feature.update_attribute(:operations_access_level, access_level)
-
sign_in(user)
- visit project_issues_path(project)
end
shared_examples 'shows Monitor menu based on the access level' do
- context 'when operations project feature is PRIVATE' do
- let(:access_level) { ProjectFeature::PRIVATE }
-
- it 'shows the `Monitor` menu' do
- expect(page).to have_selector('a.shortcuts-monitor', text: 'Monitor')
- end
+ using RSpec::Parameterized::TableSyntax
+
+ 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
end
- context 'when operations project feature is DISABLED' do
- let(:access_level) { ProjectFeature::DISABLED }
+ 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)
- it 'does not show the `Monitor` menu' do
- expect(page).not_to have_selector('a.shortcuts-monitor')
+ if render
+ expect(page).to have_selector('a.shortcuts-monitor', text: 'Monitor')
+ else
+ expect(page).not_to have_selector('a.shortcuts-monitor')
+ end
end
end
end
- context 'user is not a member' do
+ context 'when user is not a member' 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
+
it 'has the correct `Monitor` menu items', :aggregate_failures do
+ visit project_issues_path(project)
expect(page).to have_selector('a.shortcuts-monitor', text: 'Monitor')
expect(page).to have_link('Incidents', href: project_incidents_path(project))
expect(page).to have_link('Environments', href: project_environments_path(project))
@@ -48,27 +68,50 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
expect(page).not_to have_link('Kubernetes', href: project_clusters_path(project))
end
- context 'when operations project feature is PRIVATE' do
- let(:access_level) { ProjectFeature::PRIVATE }
+ context 'with new monitor visiblity flag disabled' do
+ stub_feature_flags(split_operations_visibility_permissions: false)
- it 'does not show the `Monitor` menu' do
- expect(page).not_to have_selector('a.shortcuts-monitor')
+ 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
end
end
- context 'when operations project feature is DISABLED' do
- let(:access_level) { ProjectFeature::DISABLED }
+ context 'with new monitor visiblity flag enabled' do
+ context 'when monitor 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')
+ it 'does not show the `Operations` menu' do
+ expect(page).not_to have_selector('a.shortcuts-monitor')
+ end
end
end
end
- context 'user has guest role' do
+ context 'when user has guest role' do
let(:role) { :guest }
it 'has the correct `Monitor` menu items' do
+ visit project_issues_path(project)
expect(page).to have_selector('a.shortcuts-monitor', text: 'Monitor')
expect(page).to have_link('Incidents', href: project_incidents_path(project))
expect(page).to have_link('Environments', href: project_environments_path(project))
@@ -83,10 +126,11 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
it_behaves_like 'shows Monitor menu based on the access level'
end
- context 'user has reporter role' do
+ context 'when user has reporter role' do
let(:role) { :reporter }
it 'has the correct `Monitor` menu items' do
+ visit project_issues_path(project)
expect(page).to have_link('Metrics', href: project_metrics_dashboard_path(project))
expect(page).to have_link('Incidents', href: project_incidents_path(project))
expect(page).to have_link('Environments', href: project_environments_path(project))
@@ -100,10 +144,11 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
it_behaves_like 'shows Monitor menu based on the access level'
end
- context 'user has developer role' do
+ context 'when user has developer role' do
let(:role) { :developer }
it 'has the correct `Monitor` menu items' do
+ visit project_issues_path(project)
expect(page).to have_link('Metrics', href: project_metrics_dashboard_path(project))
expect(page).to have_link('Alerts', href: project_alert_management_index_path(project))
expect(page).to have_link('Incidents', href: project_incidents_path(project))
@@ -116,10 +161,11 @@ RSpec.describe 'Monitor dropdown sidebar', :aggregate_failures do
it_behaves_like 'shows Monitor menu based on the access level'
end
- context 'user has maintainer role' do
+ context 'when user has maintainer role' do
let(:role) { :maintainer }
it 'has the correct `Monitor` menu items' do
+ visit project_issues_path(project)
expect(page).to have_link('Metrics', href: project_metrics_dashboard_path(project))
expect(page).to have_link('Alerts', href: project_alert_management_index_path(project))
expect(page).to have_link('Incidents', href: project_incidents_path(project))
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 744543d1252..75fa8561235 100644
--- a/spec/features/populate_new_pipeline_vars_with_params_spec.rb
+++ b/spec/features/populate_new_pipeline_vars_with_params_spec.rb
@@ -7,24 +7,42 @@ RSpec.describe "Populate new pipeline CI variables with url params", :js do
let(:project) { create(:project) }
let(:page_path) { new_project_pipeline_path(project) }
- before do
- sign_in(user)
- project.add_maintainer(user)
+ shared_examples 'form pre-filled with URL params' do
+ before do
+ sign_in(user)
+ project.add_maintainer(user)
- visit "#{page_path}?var[key1]=value1&file_var[key2]=value2"
+ 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
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')
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(run_pipeline_graphql: false)
end
+
+ it_behaves_like 'form pre-filled with URL params'
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')
+ context 'when feature flag is enabled' do
+ before do
+ stub_feature_flags(run_pipeline_graphql: true)
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 2836ac2f801..913c375f909 100644
--- a/spec/features/profile_spec.rb
+++ b/spec/features/profile_spec.rb
@@ -27,17 +27,41 @@ RSpec.describe 'Profile account page', :js do
expect(User.exists?(user.id)).to be_truthy
end
- it 'deletes user', :js, :sidekiq_might_not_need_inline do
- click_button 'Delete account'
+ context 'when user_destroy_with_limited_execution_time_worker is enabled' do
+ it 'deletes user', :js, :sidekiq_inline do
+ click_button 'Delete account'
- fill_in 'password', with: user.password
+ fill_in 'password', with: user.password
- page.within '.modal' do
- click_button 'Delete account'
+ page.within '.modal' do
+ click_button 'Delete account'
+ end
+
+ expect(page).to have_content('Account scheduled for removal')
+ expect(
+ Users::GhostUserMigration.where(user: user,
+ initiator_user: user)
+ ).to be_exists
end
+ end
- expect(page).to have_content('Account scheduled for removal')
- expect(User.exists?(user.id)).to be_falsy
+ context 'when user_destroy_with_limited_execution_time_worker is disabled' do
+ before do
+ stub_feature_flags(user_destroy_with_limited_execution_time_worker: false)
+ end
+
+ it 'deletes user', :js, :sidekiq_inline do
+ click_button 'Delete account'
+
+ fill_in 'password', with: user.password
+
+ page.within '.modal' do
+ click_button 'Delete account'
+ end
+
+ expect(page).to have_content('Account scheduled for removal')
+ expect(User.exists?(user.id)).to be_falsy
+ end
end
it 'shows invalid password flash message', :js do
diff --git a/spec/features/profiles/active_sessions_spec.rb b/spec/features/profiles/active_sessions_spec.rb
index 24c9225532b..d0819bb5363 100644
--- a/spec/features/profiles/active_sessions_spec.rb
+++ b/spec/features/profiles/active_sessions_spec.rb
@@ -59,7 +59,7 @@ RSpec.describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
expect(page).to(
have_selector('ul.list-group li.list-group-item', text: 'Signed in on',
- count: 2))
+ count: 2))
expect(page).to have_content(
'127.0.0.1 ' \
diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb
index 2f7b722f553..d887a367fcb 100644
--- a/spec/features/profiles/user_edit_profile_spec.rb
+++ b/spec/features/profiles/user_edit_profile_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe 'User edit profile' do
let(:user) { create(:user) }
before do
+ stub_feature_flags(remove_user_attributes_projects: false)
sign_in(user)
visit(profile_path)
end
@@ -179,7 +180,7 @@ RSpec.describe 'User edit profile' do
end
it 'adds emoji to user status' do
- emoji = 'biohazard'
+ emoji = 'basketball'
select_emoji(emoji)
submit_settings
@@ -192,7 +193,7 @@ RSpec.describe 'User edit profile' do
it 'adds message to user status' do
message = 'I have something to say'
- fill_in 'js-status-message-field', with: message
+ fill_in s_("SetStatusModal|What's your status?"), with: message
submit_settings
visit_user
@@ -207,7 +208,7 @@ RSpec.describe 'User edit profile' do
emoji = '8ball'
message = 'Playing outside'
select_emoji(emoji)
- fill_in 'js-status-message-field', with: message
+ fill_in s_("SetStatusModal|What's your status?"), with: message
submit_settings
visit_user
@@ -229,7 +230,7 @@ RSpec.describe 'User edit profile' do
end
visit(profile_path)
- click_button 'js-clear-user-status-button'
+ click_button s_('SetStatusModal|Clear status')
submit_settings
visit_user
@@ -239,9 +240,9 @@ RSpec.describe 'User edit profile' do
it 'displays a default emoji if only message is entered' do
message = 'a status without emoji'
- fill_in 'js-status-message-field', with: message
+ fill_in s_("SetStatusModal|What's your status?"), with: message
- within('.js-toggle-emoji-menu') do
+ within('.emoji-menu-toggle-button') do
expect(page).to have_emoji('speech_balloon')
end
end
@@ -405,7 +406,7 @@ RSpec.describe 'User edit profile' do
it 'adds message to user status' do
message = 'I have something to say'
open_user_status_modal
- find('.js-status-message-field').native.send_keys(message)
+ find_field(s_("SetStatusModal|What's your status?")).native.send_keys(message)
set_user_status_in_modal
visit_user
@@ -421,7 +422,7 @@ RSpec.describe 'User edit profile' do
message = 'Playing outside'
open_user_status_modal
select_emoji(emoji, true)
- find('.js-status-message-field').native.send_keys(message)
+ find_field(s_("SetStatusModal|What's your status?")).native.send_keys(message)
set_user_status_in_modal
visit_user
@@ -445,7 +446,7 @@ RSpec.describe 'User edit profile' do
open_edit_status_modal
- find('.js-clear-user-status-button').click
+ click_button s_('SetStatusModal|Clear status')
set_user_status_in_modal
visit_user
@@ -490,7 +491,7 @@ RSpec.describe 'User edit profile' do
it 'displays a default emoji if only message is entered' do
message = 'a status without emoji'
open_user_status_modal
- find('.js-status-message-field').native.send_keys(message)
+ find_field(s_("SetStatusModal|What's your status?")).native.send_keys(message)
expect(page).to have_emoji('speech_balloon')
end
diff --git a/spec/features/profiles/user_visits_profile_spec.rb b/spec/features/profiles/user_visits_profile_spec.rb
index 7dd2e6aafa3..df096c2f151 100644
--- a/spec/features/profiles/user_visits_profile_spec.rb
+++ b/spec/features/profiles/user_visits_profile_spec.rb
@@ -87,61 +87,4 @@ RSpec.describe 'User visits their profile' do
end
end
end
-
- describe 'storage_enforcement_banner', :js do
- before do
- stub_feature_flags(namespace_storage_limit_bypass_date_check: false)
- end
-
- context 'with storage_enforcement_date set' do
- let_it_be(:storage_enforcement_date) { Date.today + 30 }
-
- before do
- allow_next_found_instance_of(Namespaces::UserNamespace) do |user_namespace|
- allow(user_namespace).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
- end
- end
-
- it 'displays the banner in the profile page' do
- visit(profile_path)
- expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
- end
-
- it 'does not display the banner if user has previously closed unless threshold has changed' do
- visit(profile_path)
- expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
- find('.js-storage-enforcement-banner [data-testid="close-icon"]').click
- page.refresh
- expect_page_not_to_have_storage_enforcement_banner
-
- storage_enforcement_date = Date.today + 13
- allow_next_found_instance_of(Namespaces::UserNamespace) do |user_namespace|
- allow(user_namespace).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
- end
- page.refresh
- expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
- end
- end
-
- context 'with storage_enforcement_date not set' do
- before do
- allow_next_found_instance_of(Namespaces::UserNamespace) do |user_namespace|
- allow(user_namespace).to receive(:storage_enforcement_date).and_return(nil)
- end
- end
-
- it 'does not display the banner in the group page' do
- visit(profile_path)
- expect_page_not_to_have_storage_enforcement_banner
- end
- end
- end
-
- def expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
- expect(page).to have_text "Effective #{storage_enforcement_date}, namespace storage limits will apply"
- end
-
- def expect_page_not_to_have_storage_enforcement_banner
- expect(page).not_to have_text "namespace storage limits will apply"
- end
end
diff --git a/spec/features/project_variables_spec.rb b/spec/features/project_variables_spec.rb
index 89dbd1afc6b..d3bedbf3a75 100644
--- a/spec/features/project_variables_spec.rb
+++ b/spec/features/project_variables_spec.rb
@@ -14,8 +14,6 @@ RSpec.describe 'Project variables', :js do
project.variables << variable
end
- # TODO: Add same tests but with FF enabled context when
- # the new graphQL app for variable settings is enabled.
context 'with disabled ff `ci_variable_settings_graphql' do
before do
stub_feature_flags(ci_variable_settings_graphql: false)
@@ -44,4 +42,32 @@ RSpec.describe 'Project variables', :js do
end
end
end
+
+ context 'with enabled ff `ci_variable_settings_graphql' do
+ before do
+ visit page_path
+ end
+
+ it_behaves_like 'variable list'
+
+ it 'adds a new variable with an environment scope' do
+ click_button('Add variable')
+
+ page.within('#add-ci-variable') do
+ fill_in 'Key', with: 'akey'
+ find('#ci-variable-value').set('akey_value')
+ find('[data-testid="environment-scope"]').click
+ find('[data-testid="ci-environment-search"]').set('review/*')
+ find('[data-testid="create-wildcard-button"]').click
+
+ click_button('Add variable')
+ end
+
+ wait_for_requests
+
+ page.within('[data-testid="ci-variable-table"]') do
+ expect(find('.js-ci-variable-row:first-child [data-label="Environments"]').text).to eq('review/*')
+ end
+ end
+ end
end
diff --git a/spec/features/projects/badges/coverage_spec.rb b/spec/features/projects/badges/coverage_spec.rb
index 5c1bc1ad239..7555e567c37 100644
--- a/spec/features/projects/badges/coverage_spec.rb
+++ b/spec/features/projects/badges/coverage_spec.rb
@@ -191,7 +191,7 @@ RSpec.describe 'test coverage badge' do
def show_test_coverage_badge(job: nil, min_good: nil, min_acceptable: nil, min_medium: nil)
visit coverage_project_badges_path(project, ref: :master, job: job, min_good: min_good,
- min_acceptable: min_acceptable, min_medium: min_medium, format: :svg)
+ min_acceptable: min_acceptable, min_medium: min_medium, format: :svg)
end
def expect_coverage_badge(coverage)
diff --git a/spec/features/projects/blobs/blame_spec.rb b/spec/features/projects/blobs/blame_spec.rb
index 3b2b74b469e..5287d5e4f7d 100644
--- a/spec/features/projects/blobs/blame_spec.rb
+++ b/spec/features/projects/blobs/blame_spec.rb
@@ -14,11 +14,32 @@ RSpec.describe 'File blame', :js do
wait_for_all_requests
end
+ context 'as a developer' do
+ let(:user) { create(:user) }
+ let(:role) { :developer }
+
+ before do
+ project.add_role(user, role)
+ sign_in(user)
+ end
+
+ it 'does not display lock, replace and delete buttons' do
+ visit_blob_blame(path)
+
+ expect(page).not_to have_button("Lock")
+ expect(page).not_to have_button("Replace")
+ expect(page).not_to have_button("Delete")
+ end
+ end
+
it 'displays the blame page without pagination' do
visit_blob_blame(path)
- expect(page).to have_css('.blame-commit')
- expect(page).not_to have_css('.gl-pagination')
+ within '[data-testid="blob-content-holder"]' do
+ expect(page).to have_css('.blame-commit')
+ expect(page).not_to have_css('.gl-pagination')
+ expect(page).not_to have_link _('View entire blame')
+ end
end
context 'when blob length is over the blame range limit' do
@@ -29,12 +50,15 @@ RSpec.describe 'File blame', :js do
it 'displays two first lines of the file with pagination' do
visit_blob_blame(path)
- expect(page).to have_css('.blame-commit')
- expect(page).to have_css('.gl-pagination')
+ within '[data-testid="blob-content-holder"]' do
+ expect(page).to have_css('.blame-commit')
+ expect(page).to have_css('.gl-pagination')
+ expect(page).to have_link _('View entire blame')
- expect(page).to have_css('#L1')
- expect(page).not_to have_css('#L3')
- expect(find('.page-link.active')).to have_text('1')
+ expect(page).to have_css('#L1')
+ expect(page).not_to have_css('#L3')
+ expect(find('.page-link.active')).to have_text('1')
+ end
end
context 'when user clicks on the next button' do
@@ -45,15 +69,35 @@ RSpec.describe 'File blame', :js do
end
it 'displays next two lines of the file with pagination' do
- expect(page).not_to have_css('#L1')
- expect(page).to have_css('#L3')
- expect(find('.page-link.active')).to have_text('2')
+ within '[data-testid="blob-content-holder"]' do
+ expect(page).not_to have_css('#L1')
+ expect(page).to have_css('#L3')
+ expect(find('.page-link.active')).to have_text('2')
+ end
end
it 'correctly redirects to the prior blame page' do
- find('.version-link').click
+ within '[data-testid="blob-content-holder"]' do
+ find('.version-link').click
+
+ expect(find('.page-link.active')).to have_text('2')
+ end
+ end
+ end
+
+ context 'when user clicks on View entire blame button' do
+ before do
+ visit_blob_blame(path)
+ end
+
+ it 'displays the blame page without pagination' do
+ within '[data-testid="blob-content-holder"]' do
+ click_link _('View entire blame')
- expect(find('.page-link.active')).to have_text('2')
+ expect(page).to have_css('#L1')
+ expect(page).to have_css('#L3')
+ expect(page).not_to have_css('.gl-pagination')
+ end
end
end
@@ -65,8 +109,11 @@ RSpec.describe 'File blame', :js do
it 'displays the blame page without pagination' do
visit_blob_blame(path)
- expect(page).to have_css('.blame-commit')
- expect(page).not_to have_css('.gl-pagination')
+ within '[data-testid="blob-content-holder"]' do
+ expect(page).to have_css('.blame-commit')
+ expect(page).not_to have_css('.gl-pagination')
+ expect(page).not_to have_link _('View entire blame')
+ end
end
end
end
@@ -81,25 +128,29 @@ RSpec.describe 'File blame', :js do
it 'displays two hundred lines of the file with pagination' do
visit_blob_blame(path)
- expect(page).to have_css('.blame-commit')
- expect(page).to have_css('.gl-pagination')
+ within '[data-testid="blob-content-holder"]' do
+ expect(page).to have_css('.blame-commit')
+ expect(page).to have_css('.gl-pagination')
- expect(page).to have_css('#L1')
- expect(page).not_to have_css('#L201')
- expect(find('.page-link.active')).to have_text('1')
+ expect(page).to have_css('#L1')
+ expect(page).not_to have_css('#L201')
+ expect(find('.page-link.active')).to have_text('1')
+ end
end
context 'when user clicks on the next button' do
before do
visit_blob_blame(path)
-
- find('.js-next-button').click
end
it 'displays next two hundred lines of the file with pagination' do
- expect(page).not_to have_css('#L1')
- expect(page).to have_css('#L201')
- expect(find('.page-link.active')).to have_text('2')
+ within '[data-testid="blob-content-holder"]' do
+ find('.js-next-button').click
+
+ expect(page).not_to have_css('#L1')
+ expect(page).to have_css('#L201')
+ expect(find('.page-link.active')).to have_text('2')
+ end
end
end
end
diff --git a/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb b/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb
index 484f740faee..d2774aa74c9 100644
--- a/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb
+++ b/spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb
@@ -8,6 +8,10 @@ RSpec.describe 'User creates new blob', :js do
let(:user) { create(:user) }
let(:project) { create(:project, :empty_repo) }
+ before do
+ stub_feature_flags(vscode_web_ide: false)
+ end
+
shared_examples 'creating a file' do
it 'allows the user to add a new file in Web IDE' do
visit project_path(project)
diff --git a/spec/features/projects/branches/user_creates_branch_spec.rb b/spec/features/projects/branches/user_creates_branch_spec.rb
index 18d083f7d88..be236b7ca7e 100644
--- a/spec/features/projects/branches/user_creates_branch_spec.rb
+++ b/spec/features/projects/branches/user_creates_branch_spec.rb
@@ -1,48 +1,129 @@
# frozen_string_literal: true
-require "spec_helper"
+require 'spec_helper'
-RSpec.describe "User creates branch", :js do
+RSpec.describe 'User creates branch', :js do
include Spec::Support::Helpers::Features::BranchesHelpers
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
+ let_it_be(:group) { create(:group, :public) }
+ let_it_be(:user) { create(:user) }
- before do
- project.add_developer(user)
- sign_in(user)
+ shared_examples 'creates new branch' do
+ specify do
+ branch_name = "deploy_keys_#{SecureRandom.hex(4)}"
- visit(new_project_branch_path(project))
+ create_branch(branch_name)
+
+ expect(page).to have_content(branch_name)
+ end
+ end
+
+ shared_examples 'renders not found page' do
+ specify do
+ expect(page).to have_title('Not Found')
+ expect(page).to have_content('Page Not Found')
+ end
end
- it "creates new branch" do
- branch_name = "deploy_keys"
+ context 'when project is public with private repository' do
+ let_it_be(:project) { create(:project, :public, :repository, :repository_private, group: group) }
- create_branch(branch_name)
+ context 'when user is an inherited member from the group' do
+ context 'and user is a guest' do
+ before do
+ group.add_guest(user)
+ sign_in(user)
- expect(page).to have_content(branch_name)
- end
+ visit(new_project_branch_path(project))
+ end
- context "when branch name is invalid" do
- it "does not create new branch" do
- invalid_branch_name = "1.0 stable"
+ it_behaves_like 'renders not found page'
+ end
- fill_in("branch_name", with: invalid_branch_name)
- page.find("body").click # defocus the branch_name input
+ context 'and user is a developer' do
+ before do
+ group.add_developer(user)
+ sign_in(user)
- select_branch("master")
- click_button("Create branch")
+ visit(new_project_branch_path(project))
+ end
- expect(page).to have_content("Branch name is invalid")
- expect(page).to have_content("can't contain spaces")
+ it_behaves_like 'creates new branch'
+ end
end
end
- context "when branch name already exists" do
- it "does not create new branch" do
- create_branch("master")
+ context 'when project is private' do
+ let_it_be(:project) { create(:project, :private, :repository, group: group) }
+
+ context 'when user is a direct project member' do
+ context 'and user is a developer' do
+ before do
+ project.add_developer(user)
+ sign_in(user)
+
+ visit(new_project_branch_path(project))
+ end
+
+ context 'when on new branch page' do
+ it 'renders I18n supported text' do
+ page.within('#new-branch-form') do
+ expect(page).to have_content(_('Branch name'))
+ expect(page).to have_content(_('Create from'))
+ expect(page).to have_content(_('Existing branch name, tag, or commit SHA'))
+ end
+ end
+ end
+
+ it_behaves_like 'creates new branch'
+
+ context 'when branch name is invalid' 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')
+
+ expect(page).to have_content('Branch name is invalid')
+ expect(page).to have_content("can't contain spaces")
+ end
+ end
+
+ context 'when branch name already exists' do
+ it 'does not create new branch' do
+ create_branch('master')
+
+ expect(page).to have_content('Branch already exists')
+ end
+ end
+ end
+ end
+
+ context 'when user is an inherited member from the group' do
+ context 'and user is a guest' do
+ before do
+ group.add_guest(user)
+ sign_in(user)
+
+ visit(new_project_branch_path(project))
+ end
+
+ it_behaves_like 'renders not found page'
+ end
+
+ context 'and user is a developer' do
+ before do
+ group.add_developer(user)
+ sign_in(user)
+
+ visit(new_project_branch_path(project))
+ end
- expect(page).to have_content("Branch already exists")
+ it_behaves_like 'creates new branch'
+ end
end
end
end
diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
index e472cff38ce..417e14e2376 100644
--- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb
+++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js do
end
it 'displays a mini pipeline graph' do
- expect(page).to have_selector('[data-testid="commit-box-mini-graph"]')
+ expect(page).to have_selector('[data-testid="commit-box-pipeline-mini-graph"]')
first('[data-testid="mini-pipeline-graph-dropdown"]').click
@@ -35,7 +35,7 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js do
page.within '.js-builds-dropdown-list' do
expect(page).to have_selector('.ci-status-icon-running')
- expect(page).to have_content(build.stage)
+ expect(page).to have_content(build.stage_name)
end
build.drop
diff --git a/spec/features/projects/commits/multi_view_diff_spec.rb b/spec/features/projects/commits/multi_view_diff_spec.rb
index 5af2e367aed..c0e48b7b86c 100644
--- a/spec/features/projects/commits/multi_view_diff_spec.rb
+++ b/spec/features/projects/commits/multi_view_diff_spec.rb
@@ -11,8 +11,9 @@ RSpec.shared_examples "no multiple viewers" do |commit_ref|
end
RSpec.describe 'Multiple view Diffs', :js do
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :repository, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
+
let(:ref) { '5d6ed1503801ca9dc28e95eeb85a7cf863527aee' }
let(:path) { project_commit_path(project, ref) }
let(:feature_flag_on) { false }
diff --git a/spec/features/projects/environments/environment_metrics_spec.rb b/spec/features/projects/environments/environment_metrics_spec.rb
index f5f4d13dd58..bf0949443de 100644
--- a/spec/features/projects/environments/environment_metrics_spec.rb
+++ b/spec/features/projects/environments/environment_metrics_spec.rb
@@ -30,9 +30,9 @@ RSpec.describe 'Environment > Metrics' do
click_link 'Monitoring'
expect(page).to have_current_path(project_metrics_dashboard_path(project, environment: environment.id))
- expect(page).to have_css('[data-qa-selector="environments_dropdown"]') # rubocop:disable QA/SelectorUsage
+ expect(page).to have_css('[data-testid="environments-dropdown"]')
- within('[data-qa-selector="environments_dropdown"]') do # rubocop:disable QA/SelectorUsage
+ within('[data-testid="environments-dropdown"]') do
# Click on the dropdown
click_on(environment.name)
@@ -59,7 +59,7 @@ RSpec.describe 'Environment > Metrics' do
visit_environment(environment)
click_link 'Monitoring'
- expect(page).to have_css('[data-qa-selector="prometheus_graphs"]') # rubocop:disable QA/SelectorUsage
+ expect(page).to have_css('[data-testid="prometheus-graphs"]')
end
it_behaves_like 'has environment selector'
diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb
index a53e8beb555..be4b21dfff4 100644
--- a/spec/features/projects/environments/environment_spec.rb
+++ b/spec/features/projects/environments/environment_spec.rb
@@ -264,9 +264,7 @@ RSpec.describe 'Environment' do
let(:build) { create(:ci_build, :success, pipeline: pipeline, environment: environment.name) }
let(:action) do
- create(:ci_build, :manual, pipeline: pipeline,
- name: 'close_app',
- environment: environment.name)
+ create(:ci_build, :manual, pipeline: pipeline, name: 'close_app', environment: environment.name)
end
let(:deployment) do
@@ -278,8 +276,7 @@ RSpec.describe 'Environment' do
context 'when user has ability to stop environment' do
let(:permissions) do
- create(:protected_branch, :developers_can_merge,
- name: action.ref, project: project)
+ create(:protected_branch, :developers_can_merge, name: action.ref, project: project)
end
it 'allows to stop environment', :js do
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 221f07a2f75..949e530f86d 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
@@ -30,8 +30,7 @@ RSpec.describe 'User sees feature flag list', :js do
create(:operations_scope, strategy: strat, environment_scope: 'production')
end
end
- create(:operations_feature_flag, :new_version_flag, project: project,
- name: 'my_flag', active: false)
+ create(:operations_feature_flag, :new_version_flag, project: project, name: 'my_flag', active: false)
end
it 'shows the user the first flag' do
@@ -91,7 +90,7 @@ RSpec.describe 'User sees feature flag list', :js do
it 'shows the empty page' do
expect(page).to have_text 'Get started with feature flags'
expect(page).to have_selector('.btn-confirm', text: 'New feature flag')
- expect(page).to have_selector('[data-qa-selector="configure_feature_flags_button"]', text: 'Configure') # rubocop:disable QA/SelectorUsage
+ expect(page).to have_selector('[data-testid="ff-configure-button"]', text: 'Configure')
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 71c9d89fbde..eb9ac078662 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
@@ -18,13 +18,12 @@ RSpec.describe 'User updates feature flag', :js do
context 'with a new version feature flag' do
let!(:feature_flag) do
- create_flag(project, 'test_flag', false, version: Operations::FeatureFlag.versions['new_version_flag'],
- description: 'For testing')
+ create_flag(project, 'test_flag', false,
+ version: Operations::FeatureFlag.versions['new_version_flag'], description: 'For testing')
end
let!(:strategy) do
- create(:operations_strategy, feature_flag: feature_flag,
- name: 'default', parameters: {})
+ create(:operations_strategy, feature_flag: feature_flag, name: 'default', parameters: {})
end
let!(:scope) do
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 0ad44f31a52..52686469243 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
@@ -9,6 +9,8 @@ RSpec.describe 'Projects > Files > Project owner sees a link to create a license
let(:project_maintainer) { project.first_owner }
before do
+ stub_feature_flags(vscode_web_ide: false)
+
sign_in(project_maintainer)
end
diff --git a/spec/features/projects/files/user_edits_files_spec.rb b/spec/features/projects/files/user_edits_files_spec.rb
index d7460538be9..1a9c5483218 100644
--- a/spec/features/projects/files/user_edits_files_spec.rb
+++ b/spec/features/projects/files/user_edits_files_spec.rb
@@ -14,6 +14,8 @@ RSpec.describe 'Projects > Files > User edits files', :js do
let(:user) { create(:user) }
before do
+ stub_feature_flags(vscode_web_ide: false)
+
sign_in(user)
end
@@ -102,6 +104,21 @@ RSpec.describe 'Projects > Files > User edits files', :js do
expect(page).to have_content('*.rbca')
end
+ it 'shows loader on commit changes' do
+ set_default_button('edit')
+ click_link('.gitignore')
+ click_link_or_button('Edit')
+
+ # why: We don't want the form to actually submit, so that we can assert the button's changed state
+ page.execute_script("document.querySelector('.js-edit-blob-form').addEventListener('submit', e => e.preventDefault())")
+
+ find('.file-editor', match: :first)
+ editor_set_value('*.rbca')
+ click_button('Commit changes')
+
+ expect(page).to have_button('Commit changes', disabled: true, class: 'js-commit-button-loading')
+ end
+
it 'shows the diff of an edited file' do
set_default_button('edit')
click_link('.gitignore')
diff --git a/spec/features/projects/fork_spec.rb b/spec/features/projects/fork_spec.rb
index fb27f0961b6..b8c127f0078 100644
--- a/spec/features/projects/fork_spec.rb
+++ b/spec/features/projects/fork_spec.rb
@@ -126,7 +126,10 @@ RSpec.describe 'Project fork' do
let(:user) { create(:group_member, :maintainer, user: create(:user), group: group ).user }
def submit_form
- select(group.name)
+ find('[data-testid="select_namespace_dropdown"]').click
+ find('[data-testid="select_namespace_dropdown_search_field"]').fill_in(with: group.name)
+ click_button group.name
+
click_button 'Fork project'
end
diff --git a/spec/features/projects/jobs/user_browses_jobs_spec.rb b/spec/features/projects/jobs/user_browses_jobs_spec.rb
index 289ab8cffa5..995f4a1e3d2 100644
--- a/spec/features/projects/jobs/user_browses_jobs_spec.rb
+++ b/spec/features/projects/jobs/user_browses_jobs_spec.rb
@@ -58,8 +58,7 @@ RSpec.describe 'User browses jobs' do
context 'when a job can be canceled' do
let!(:job) do
- create(:ci_build, pipeline: pipeline,
- stage: 'test')
+ create(:ci_build, pipeline: pipeline, stage: 'test')
end
before do
@@ -81,7 +80,7 @@ RSpec.describe 'User browses jobs' do
context 'when a job can be retried' do
let!(:job) do
create(:ci_build, pipeline: pipeline,
- stage: 'test')
+ stage: 'test')
end
before do
@@ -190,7 +189,7 @@ RSpec.describe 'User browses jobs' do
context 'column links' do
let!(:job) do
create(:ci_build, pipeline: pipeline,
- stage: 'test')
+ stage: 'test')
end
before do
diff --git a/spec/features/projects/milestones/milestones_sorting_spec.rb b/spec/features/projects/milestones/milestones_sorting_spec.rb
index 2ad820e4a06..c47350fb663 100644
--- a/spec/features/projects/milestones/milestones_sorting_spec.rb
+++ b/spec/features/projects/milestones/milestones_sorting_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'Milestones sorting', :js do
let(:milestones_for_sort_by) do
{
'Due later' => %w[b c a],
- 'Name, ascending' => %w[a b c],
+ 'Name, ascending' => %w[a b c],
'Name, descending' => %w[c b a],
'Start later' => %w[a c b],
'Start soon' => %w[b c a],
diff --git a/spec/features/projects/navbar_spec.rb b/spec/features/projects/navbar_spec.rb
index e07a5d09405..5b5f7860e43 100644
--- a/spec/features/projects/navbar_spec.rb
+++ b/spec/features/projects/navbar_spec.rb
@@ -49,7 +49,7 @@ RSpec.describe 'Project navbar' do
stub_config(pages: { enabled: true })
insert_after_sub_nav_item(
- _('Packages & Registries'),
+ _('Packages and registries'),
within: _('Settings'),
new_sub_nav_item_name: _('Pages')
)
@@ -83,6 +83,8 @@ RSpec.describe 'Project navbar' do
end
context 'when harbor registry is available' do
+ let_it_be(:harbor_integration) { create(:harbor_integration, project: project) }
+
before do
stub_feature_flags(harbor_registry_integration: true)
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index f45025d079a..7cf05242a23 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -424,9 +424,10 @@ RSpec.describe 'New project', :js do
it 'keeps "Import project" tab open after form validation error' do
collision_project = create(:project, name: 'test-name-collision', namespace: user.namespace)
- stub_request(:get, "http://foo/bar/info/refs?service=git-upload-pack").to_return({ status: 200,
- body: '001e# service=git-upload-pack',
- headers: { 'Content-Type': 'application/x-git-upload-pack-advertisement' } })
+ stub_request(:get, "http://foo/bar/info/refs?service=git-upload-pack").to_return(
+ { status: 200,
+ body: '001e# service=git-upload-pack',
+ headers: { 'Content-Type': 'application/x-git-upload-pack-advertisement' } })
fill_in 'project_import_url', with: 'http://foo/bar'
fill_in 'project_name', with: collision_project.name
@@ -465,9 +466,10 @@ RSpec.describe 'New project', :js do
end
it 'initiates import when valid repo url is provided' do
- stub_request(:get, "http://foo/bar/info/refs?service=git-upload-pack").to_return({ status: 200,
- body: '001e# service=git-upload-pack',
- headers: { 'Content-Type': 'application/x-git-upload-pack-advertisement' } })
+ stub_request(:get, "http://foo/bar/info/refs?service=git-upload-pack").to_return(
+ { status: 200,
+ body: '001e# service=git-upload-pack',
+ headers: { 'Content-Type': 'application/x-git-upload-pack-advertisement' } })
fill_in 'project_import_url', with: 'http://foo/bar'
diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb
index 0711a30e974..dcc46f5d223 100644
--- a/spec/features/projects/pipeline_schedules_spec.rb
+++ b/spec/features/projects/pipeline_schedules_spec.rb
@@ -95,7 +95,7 @@ RSpec.describe 'Pipeline Schedules', :js do
it 'displays the required information description' do
page.within('.pipeline-schedule-table-row') do
expect(page).to have_content('pipeline schedule')
- expect(find(".next-run-cell time")['title'])
+ expect(find("[data-testid='next-run-cell'] time")['title'])
.to include(pipeline_schedule.real_next_run.strftime('%b %-d, %Y'))
expect(page).to have_link('master')
expect(page).to have_link("##{pipeline.id}")
@@ -259,7 +259,7 @@ RSpec.describe 'Pipeline Schedules', :js do
click_button 'Save pipeline schedule'
page.within('.pipeline-schedule-table-row:nth-child(1)') do
- expect(page).to have_css(".next-run-cell time")
+ expect(page).to have_css("[data-testid='next-run-cell'] time")
end
end
end
diff --git a/spec/features/projects/pipelines/legacy_pipeline_spec.rb b/spec/features/projects/pipelines/legacy_pipeline_spec.rb
index 14f60dfe061..250a336469c 100644
--- a/spec/features/projects/pipelines/legacy_pipeline_spec.rb
+++ b/spec/features/projects/pipelines/legacy_pipeline_spec.rb
@@ -73,9 +73,9 @@ RSpec.describe 'Pipeline', :js 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('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
@@ -668,9 +668,9 @@ RSpec.describe 'Pipeline', :js do
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('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
@@ -769,13 +769,17 @@ RSpec.describe 'Pipeline', :js do
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)
+ 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)
+ 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
@@ -873,8 +877,14 @@ RSpec.describe 'Pipeline', :js do
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)
+ 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
@@ -895,8 +905,14 @@ RSpec.describe 'Pipeline', :js do
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)
+ 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
@@ -1207,7 +1223,7 @@ RSpec.describe 'Pipeline', :js do
subject
expect(page).to have_content(failed_build.name)
- expect(page).to have_content(failed_build.stage)
+ expect(page).to have_content(failed_build.stage_name)
end
it 'shows build failure logs' do
@@ -1253,7 +1269,7 @@ RSpec.describe 'Pipeline', :js do
subject
expect(page).to have_content(failed_build.name)
- expect(page).to have_content(failed_build.stage)
+ expect(page).to have_content(failed_build.stage_name)
end
it 'does not show log' do
diff --git a/spec/features/projects/pipelines/legacy_pipelines_spec.rb b/spec/features/projects/pipelines/legacy_pipelines_spec.rb
index eb8f2de3aba..2e0ea695ab3 100644
--- a/spec/features/projects/pipelines/legacy_pipelines_spec.rb
+++ b/spec/features/projects/pipelines/legacy_pipelines_spec.rb
@@ -546,8 +546,8 @@ RSpec.describe 'Pipelines', :js do
context 'for a failed pipeline' do
let!(:build) do
create(:ci_build, :failed, pipeline: pipeline,
- stage: 'build',
- name: 'build')
+ stage: 'build',
+ name: 'build')
end
it 'displays the failure reason' do
@@ -652,10 +652,10 @@ RSpec.describe 'Pipelines', :js do
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')
+ 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')
@@ -674,6 +674,7 @@ RSpec.describe 'Pipelines', :js do
let(:project) { create(:project, :repository) }
before do
+ stub_feature_flags(run_pipeline_graphql: false)
visit new_project_pipeline_path(project)
end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index cfdd851cb80..51a6fbc4d36 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -72,9 +72,9 @@ RSpec.describe 'Pipeline', :js 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('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
@@ -793,9 +793,9 @@ RSpec.describe 'Pipeline', :js do
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('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
@@ -895,12 +895,12 @@ RSpec.describe 'Pipeline', :js do
let!(:test_job) do
create(:ci_build, :pending, stage: 'test', name: 'test',
- stage_idx: 1, pipeline: pipeline, project: project)
+ 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)
+ stage_idx: 2, pipeline: pipeline, project: project, resource_group: resource_group)
end
describe 'GET /:project/-/pipelines/:id' do
@@ -998,8 +998,14 @@ RSpec.describe 'Pipeline', :js do
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)
+ 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
@@ -1126,7 +1132,7 @@ RSpec.describe 'Pipeline', :js do
subject
expect(page).to have_content(failed_build.name)
- expect(page).to have_content(failed_build.stage)
+ expect(page).to have_content(failed_build.stage_name)
end
it 'shows build failure logs' do
@@ -1172,7 +1178,7 @@ RSpec.describe 'Pipeline', :js do
subject
expect(page).to have_content(failed_build.name)
- expect(page).to have_content(failed_build.stage)
+ expect(page).to have_content(failed_build.stage_name)
end
it 'does not show log' do
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index bf521971ae0..404e51048bc 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -536,8 +536,8 @@ RSpec.describe 'Pipelines', :js do
context 'for a failed pipeline' do
let!(:build) do
create(:ci_build, :failed, pipeline: pipeline,
- stage: 'build',
- name: 'build')
+ stage: 'build',
+ name: 'build')
end
it 'displays the failure reason' do
@@ -635,10 +635,10 @@ RSpec.describe 'Pipelines', :js do
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')
+ 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')
@@ -656,19 +656,7 @@ RSpec.describe 'Pipelines', :js do
describe 'POST /:project/-/pipelines' do
let(:project) { create(:project, :repository) }
- 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
-
+ shared_examples 'run pipeline form with gitlab-ci.yml' do
context 'with gitlab-ci.yml', :js do
before do
stub_ci_pipeline_to_return_yaml_file
@@ -702,7 +690,9 @@ 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'
@@ -722,6 +712,51 @@ RSpec.describe 'Pipelines', :js do
end
end
end
+
+ # Run Pipeline form with REST endpoints
+ # TODO: Clean up tests when run_pipeline_graphql is enabled
+ 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
diff --git a/spec/features/projects/releases/user_creates_release_spec.rb b/spec/features/projects/releases/user_creates_release_spec.rb
index 10c4395da81..d82c4229b71 100644
--- a/spec/features/projects/releases/user_creates_release_spec.rb
+++ b/spec/features/projects/releases/user_creates_release_spec.rb
@@ -47,7 +47,7 @@ RSpec.describe 'User creates release', :js do
fill_out_form_and_submit
end
- it 'creates a new release when "Create release" is clicked and redirects to the release\'s dedicated page', :aggregate_failures do
+ it 'creates a new release when "Create release" is clicked and redirects to the release\'s dedicated page', :aggregate_failures, :sidekiq_inline do
release = project.releases.last
expect(release.tag).to eq(tag_name)
@@ -66,6 +66,11 @@ RSpec.describe 'User creates release', :js do
expect(link.url).to eq(link_2[:url])
expect(link.name).to eq(link_2[:title])
+ expect(release).not_to be_historical_release
+ expect(release).not_to be_upcoming_release
+
+ expect(release.evidences.length).to eq(1)
+
expect(page).to have_current_path(project_release_path(project, release))
end
end
diff --git a/spec/features/projects/settings/merge_requests_settings_spec.rb b/spec/features/projects/settings/merge_requests_settings_spec.rb
new file mode 100644
index 00000000000..ba84d8b6d1a
--- /dev/null
+++ b/spec/features/projects/settings/merge_requests_settings_spec.rb
@@ -0,0 +1,261 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Projects > Settings > Merge requests' do
+ include ProjectForksHelper
+
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public, namespace: user.namespace, path: 'gitlab', name: 'sample') }
+
+ before do
+ sign_in(user)
+
+ visit(project_settings_merge_requests_path(project))
+ end
+
+ it 'shows "Merge commit" strategy' do
+ page.within '.merge-request-settings-form' do
+ expect(page).to have_content 'Merge commit'
+ end
+ end
+
+ it 'shows "Merge commit with semi-linear history " strategy' do
+ page.within '.merge-request-settings-form' do
+ expect(page).to have_content 'Merge commit with semi-linear history'
+ end
+ end
+
+ it 'shows "Fast-forward merge" strategy' do
+ page.within '.merge-request-settings-form' do
+ expect(page).to have_content 'Fast-forward merge'
+ end
+ end
+
+ it 'shows Squash commit options', :aggregate_failures do
+ page.within '.merge-request-settings-form' do
+ expect(page).to have_content 'Do not allow'
+ expect(page).to have_content 'Squashing is never performed and the checkbox is hidden.'
+
+ expect(page).to have_content 'Allow'
+ expect(page).to have_content 'Checkbox is visible and unselected by default.'
+
+ expect(page).to have_content 'Encourage'
+ expect(page).to have_content 'Checkbox is visible and selected by default.'
+
+ expect(page).to have_content 'Require'
+ end
+ end
+
+ context 'when Merge Request and Pipelines are initially enabled', :js do
+ context 'when Pipelines are initially enabled' do
+ it 'shows the Merge Requests settings' do
+ expect(page).to have_content 'Pipelines must succeed'
+ expect(page).to have_content 'All threads must be resolved'
+
+ visit edit_project_path(project)
+
+ within('.sharing-permissions-form') do
+ within('[data-for="project[project_feature_attributes][merge_requests_access_level]"]') do
+ find('.gl-toggle').click
+ end
+ end
+
+ find('[data-testid="project-features-save-button"]').send_keys(:return)
+
+ visit project_settings_merge_requests_path(project)
+
+ expect(page).to have_content('Not Found')
+ end
+ end
+
+ context 'when Pipelines are initially disabled', :js do
+ before do
+ project.project_feature.update_attribute('builds_access_level', ProjectFeature::DISABLED)
+
+ visit project_settings_merge_requests_path(project)
+ end
+
+ it 'shows the Merge Requests settings that do not depend on Builds feature' do
+ expect(page).to have_content 'Pipelines must succeed'
+ expect(page).to have_content 'All threads must be resolved'
+
+ visit edit_project_path(project)
+
+ within('.sharing-permissions-form') do
+ within('.project-feature-controls[data-for="project[project_feature_attributes][builds_access_level]"]') do
+ find('.gl-toggle').click
+ end
+ end
+
+ find('[data-testid="project-features-save-button"]').send_keys(:return)
+
+ visit project_settings_merge_requests_path(project)
+
+ expect(page).to have_content 'Pipelines must succeed'
+ expect(page).to have_content 'All threads must be resolved'
+ end
+ end
+ end
+
+ context 'when Merge Request are initially disabled', :js do
+ before do
+ project.project_feature.update_attribute('merge_requests_access_level', ProjectFeature::DISABLED)
+
+ visit(project_settings_merge_requests_path(project))
+ end
+
+ it 'does not show the Merge Requests settings' do
+ expect(page).to have_content('Not Found')
+
+ visit edit_project_path(project)
+
+ within('.sharing-permissions-form') do
+ within('[data-for="project[project_feature_attributes][merge_requests_access_level]"]') do
+ find('.gl-toggle').click
+ end
+ end
+
+ find('[data-testid="project-features-save-button"]').send_keys(:return)
+
+ visit project_settings_merge_requests_path(project)
+
+ expect(page).to have_content 'Pipelines must succeed'
+ expect(page).to have_content 'All threads must be resolved'
+ end
+ end
+
+ describe 'Checkbox to enable merge request link', :js do
+ it 'is initially checked' do
+ checkbox = find_field('project_printing_merge_request_link_enabled')
+ expect(checkbox).to be_checked
+ end
+
+ it 'when unchecked sets :printing_merge_request_link_enabled to false' do
+ uncheck('project_printing_merge_request_link_enabled')
+ within('.merge-request-settings-form') do
+ find('.rspec-save-merge-request-changes')
+ click_on('Save changes')
+ end
+
+ find('.flash-notice')
+ checkbox = find_field('project_printing_merge_request_link_enabled')
+
+ expect(checkbox).not_to be_checked
+
+ project.reload
+ expect(project.printing_merge_request_link_enabled).to be(false)
+ end
+ end
+
+ describe 'Checkbox to remove source branch after merge', :js do
+ it 'is initially checked' do
+ checkbox = find_field('project_remove_source_branch_after_merge')
+ expect(checkbox).to be_checked
+ end
+
+ it 'when unchecked sets :remove_source_branch_after_merge to false' do
+ uncheck('project_remove_source_branch_after_merge')
+ within('.merge-request-settings-form') do
+ find('.rspec-save-merge-request-changes')
+ click_on('Save changes')
+ end
+
+ find('.flash-notice')
+ checkbox = find_field('project_remove_source_branch_after_merge')
+
+ expect(checkbox).not_to be_checked
+
+ project.reload
+ expect(project.remove_source_branch_after_merge).to be(false)
+ end
+ end
+
+ describe 'Squash commits when merging', :js do
+ it 'initially has :squash_option set to :default_off' do
+ radio = find_field('project_project_setting_attributes_squash_option_default_off')
+ expect(radio).to be_checked
+ end
+
+ it 'allows :squash_option to be set to :default_on' do
+ choose('project_project_setting_attributes_squash_option_default_on')
+
+ within('.merge-request-settings-form') do
+ find('.rspec-save-merge-request-changes')
+ click_on('Save changes')
+ end
+
+ wait_for_requests
+
+ radio = find_field('project_project_setting_attributes_squash_option_default_on')
+
+ expect(radio).to be_checked
+ expect(project.reload.project_setting.squash_option).to eq('default_on')
+ end
+
+ it 'allows :squash_option to be set to :always' do
+ choose('project_project_setting_attributes_squash_option_always')
+
+ within('.merge-request-settings-form') do
+ find('.rspec-save-merge-request-changes')
+ click_on('Save changes')
+ end
+
+ wait_for_requests
+
+ radio = find_field('project_project_setting_attributes_squash_option_always')
+
+ expect(radio).to be_checked
+ expect(project.reload.project_setting.squash_option).to eq('always')
+ end
+
+ it 'allows :squash_option to be set to :never' do
+ choose('project_project_setting_attributes_squash_option_never')
+
+ within('.merge-request-settings-form') do
+ find('.rspec-save-merge-request-changes')
+ click_on('Save changes')
+ end
+
+ wait_for_requests
+
+ radio = find_field('project_project_setting_attributes_squash_option_never')
+
+ expect(radio).to be_checked
+ expect(project.reload.project_setting.squash_option).to eq('never')
+ end
+ end
+
+ describe 'target project settings' do
+ context 'when project is a fork' do
+ let_it_be(:upstream) { create(:project, :public) }
+
+ let(:project) { fork_project(upstream, user) }
+
+ it 'allows to change merge request target project behavior' do
+ expect(page).to have_content 'The default target project for merge requests'
+
+ radio = find_field('project_project_setting_attributes_mr_default_target_self_false')
+ expect(radio).to be_checked
+
+ choose('project_project_setting_attributes_mr_default_target_self_true')
+
+ within('.merge-request-settings-form') do
+ find('.rspec-save-merge-request-changes')
+ click_on('Save changes')
+ end
+
+ wait_for_requests
+
+ radio = find_field('project_project_setting_attributes_mr_default_target_self_true')
+
+ expect(radio).to be_checked
+ expect(project.reload.project_setting.mr_default_target_self).to be_truthy
+ end
+ end
+
+ it 'does not show target project section' do
+ expect(page).not_to have_content 'The default target project for merge requests'
+ end
+ end
+end
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 5a50b3de772..477c4c2e1ba 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,7 @@
require 'spec_helper'
-RSpec.describe 'Project > Settings > Packages & Registries > Container registry tag expiration policy' do
+RSpec.describe 'Project > Settings > Packages and registries > Container registry tag expiration policy' do
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, namespace: user.namespace) }
@@ -20,10 +20,89 @@ RSpec.describe 'Project > Settings > Packages & Registries > Container registry
end
context 'as owner', :js do
+ it 'shows active tab on sidebar' do
+ subject
+
+ expect(find('.sidebar-top-level-items > li.active')).to have_content('Settings')
+ expect(find('.sidebar-sub-level-items > li.active:not(.fly-out-top-item)'))
+ .to have_content('Packages and registries')
+ end
+
it 'shows available section' do
subject
expect(find('.breadcrumbs')).to have_content('Clean up image tags')
+
+ section = find('[data-testid="container-expiration-policy-project-settings"]')
+ expect(section).to have_text 'Clean up image tags'
+ end
+
+ it 'saves cleanup policy submit the form' do
+ subject
+
+ within '[data-testid="container-expiration-policy-project-settings"]' do
+ select('Every day', from: 'Run cleanup')
+ select('50 tags per image name', from: 'Keep the most recent:')
+ fill_in('Keep tags matching:', with: 'stable')
+ select('7 days', from: 'Remove tags older than:')
+ fill_in('Remove tags matching:', with: '.*-production')
+
+ submit_button = find('[data-testid="save-button"')
+ expect(submit_button).not_to be_disabled
+ submit_button.click
+ end
+
+ expect(page).to have_current_path(project_settings_packages_and_registries_path(project))
+ expect(find('.gl-alert-body')).to have_content('Cleanup policy successfully saved.')
+ end
+
+ it 'does not save cleanup policy submit form with invalid regex' do
+ subject
+
+ within '[data-testid="container-expiration-policy-project-settings"]' do
+ fill_in('Remove tags matching:', with: '*-production')
+
+ submit_button = find('[data-testid="save-button"')
+ expect(submit_button).not_to be_disabled
+ submit_button.click
+ end
+
+ expect(find('.gl-toast')).to have_content('Something went wrong while updating the cleanup policy.')
+ end
+ end
+
+ context 'with a project without expiration policy', :js do
+ before do
+ project.container_expiration_policy.destroy!
+ end
+
+ context 'with container_expiration_policies_enable_historic_entries enabled' do
+ before do
+ stub_application_setting(container_expiration_policies_enable_historic_entries: true)
+ end
+
+ it 'displays the related section' do
+ subject
+
+ within '[data-testid="container-expiration-policy-project-settings"]' do
+ expect(find('[data-testid="enable-toggle"]'))
+ .to have_content('Disabled - Tags will not be automatically deleted.')
+ end
+ end
+ end
+
+ context 'with container_expiration_policies_enable_historic_entries disabled' do
+ before do
+ stub_application_setting(container_expiration_policies_enable_historic_entries: false)
+ end
+
+ it 'does not display the related section' do
+ subject
+
+ within '[data-testid="container-expiration-policy-project-settings"]' do
+ expect(find('.gl-alert-title')).to have_content('Cleanup policy for tags is disabled')
+ end
+ end
end
end
diff --git a/spec/features/projects/settings/registry_settings_spec.rb b/spec/features/projects/settings/registry_settings_spec.rb
index 1fb46c669e7..d64570cd5cc 100644
--- a/spec/features/projects/settings/registry_settings_spec.rb
+++ b/spec/features/projects/settings/registry_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project > Settings > Packages & Registries > Container registry tag expiration policy', :js do
+RSpec.describe 'Project > Settings > Packages and registries > Container registry tag expiration policy' do
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, namespace: user.namespace) }
@@ -19,48 +19,30 @@ RSpec.describe 'Project > Settings > Packages & Registries > Container registry
stub_container_registry_config(enabled: container_registry_enabled)
end
- context 'as owner' do
- it 'shows available section' do
+ context 'as owner', :js do
+ it 'shows active tab on sidebar' do
subject
- settings_block = find('[data-testid="container-expiration-policy-project-settings"]')
- expect(settings_block).to have_text 'Clean up image tags'
+ expect(find('.sidebar-top-level-items > li.active')).to have_content('Settings')
+ expect(find('.sidebar-sub-level-items > li.active:not(.fly-out-top-item)'))
+ .to have_content('Packages and registries')
end
- it 'saves cleanup policy submit the form' do
+ it 'shows available section' do
subject
- within '[data-testid="container-expiration-policy-project-settings"]' do
- select('Every day', from: 'Run cleanup')
- select('50 tags per image name', from: 'Keep the most recent:')
- fill_in('Keep tags matching:', with: 'stable')
- select('7 days', from: 'Remove tags older than:')
- fill_in('Remove tags matching:', with: '.*-production')
-
- submit_button = find('[data-testid="save-button"')
- expect(submit_button).not_to be_disabled
- submit_button.click
- end
-
- expect(find('.gl-toast')).to have_content('Cleanup policy successfully saved.')
+ settings_block = find('[data-testid="container-expiration-policy-project-settings"]')
+ expect(settings_block).to have_text 'Clean up image tags'
end
- it 'does not save cleanup policy submit form with invalid regex' do
+ it 'contains link to clean up image tags page' do
subject
- within '[data-testid="container-expiration-policy-project-settings"]' do
- fill_in('Remove tags matching:', with: '*-production')
-
- submit_button = find('[data-testid="save-button"')
- expect(submit_button).not_to be_disabled
- submit_button.click
- end
-
- expect(find('.gl-toast')).to have_content('Something went wrong while updating the cleanup policy.')
+ expect(page).to have_link('Edit cleanup rules', href: cleanup_image_tags_project_settings_packages_and_registries_path(project))
end
end
- context 'with a project without expiration policy' do
+ context 'with a project without expiration policy', :js do
before do
project.container_expiration_policy.destroy!
end
@@ -74,7 +56,7 @@ RSpec.describe 'Project > Settings > Packages & Registries > Container registry
subject
within '[data-testid="container-expiration-policy-project-settings"]' do
- expect(find('[data-testid="enable-toggle"]')).to have_content('Disabled - Tags will not be automatically deleted.')
+ expect(page).to have_link('Set cleanup rules', href: cleanup_image_tags_project_settings_packages_and_registries_path(project))
end
end
end
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 6aa59f72d2a..c76b4d0af88 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
@@ -9,29 +9,29 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
before do
sign_in(user)
- visit edit_project_path(project)
+ visit project_settings_merge_requests_path(project)
end
it 'shows "Merge commit" strategy' do
- page.within '#js-merge-request-settings' do
+ page.within '.merge-request-settings-form' do
expect(page).to have_content 'Merge commit'
end
end
it 'shows "Merge commit with semi-linear history " strategy' do
- page.within '#js-merge-request-settings' do
+ page.within '.merge-request-settings-form' do
expect(page).to have_content 'Merge commit with semi-linear history'
end
end
it 'shows "Fast-forward merge" strategy' do
- page.within '#js-merge-request-settings' do
+ page.within '.merge-request-settings-form' do
expect(page).to have_content 'Fast-forward merge'
end
end
it 'shows Squash commit options', :aggregate_failures do
- page.within '#js-merge-request-settings' do
+ page.within '.merge-request-settings-form' do
expect(page).to have_content 'Do not allow'
expect(page).to have_content 'Squashing is never performed and the checkbox is hidden.'
@@ -52,30 +52,33 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
expect(page).to have_content 'Pipelines must succeed'
expect(page).to have_content 'All threads must be resolved'
- within('.sharing-permissions-form') do
- find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .gl-toggle').click
- find('[data-testid="project-features-save-button"]').send_keys(:return)
- end
+ visit edit_project_path(project)
+
+ find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .gl-toggle').click
+ find('[data-testid="project-features-save-button"]').send_keys(:return)
+
+ visit project_settings_merge_requests_path(project)
- expect(page).not_to have_content 'Pipelines must succeed'
- expect(page).not_to have_content 'All threads must be resolved'
+ expect(page).to have_content "Page Not Found"
end
end
context 'when Pipelines are initially disabled', :js do
before do
project.project_feature.update_attribute('builds_access_level', ProjectFeature::DISABLED)
- visit edit_project_path(project)
+ visit project_settings_merge_requests_path(project)
end
it 'shows the Merge Requests settings that do not depend on Builds feature' do
expect(page).to have_content 'Pipelines must succeed'
expect(page).to have_content 'All threads must be resolved'
- within('.sharing-permissions-form') do
- find('.project-feature-controls[data-for="project[project_feature_attributes][builds_access_level]"] .gl-toggle').click
- find('[data-testid="project-features-save-button"]').send_keys(:return)
- end
+ visit edit_project_path(project)
+
+ find('.project-feature-controls[data-for="project[project_feature_attributes][builds_access_level]"] .gl-toggle').click
+ find('[data-testid="project-features-save-button"]').send_keys(:return)
+
+ visit project_settings_merge_requests_path(project)
expect(page).to have_content 'Pipelines must succeed'
expect(page).to have_content 'All threads must be resolved'
@@ -86,18 +89,22 @@ RSpec.describe 'Projects > Settings > User manages merge request settings' do
context 'when Merge Request are initially disabled', :js do
before do
project.project_feature.update_attribute('merge_requests_access_level', ProjectFeature::DISABLED)
- visit edit_project_path(project)
+ visit project_settings_merge_requests_path(project)
end
it 'does not show the Merge Requests settings' do
expect(page).not_to have_content 'Pipelines must succeed'
expect(page).not_to have_content 'All threads must be resolved'
+ visit edit_project_path(project)
+
within('.sharing-permissions-form') do
find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .gl-toggle').click
find('[data-testid="project-features-save-button"]').send_keys(:return)
end
+ visit project_settings_merge_requests_path(project)
+
expect(page).to have_content 'Pipelines must succeed'
expect(page).to have_content 'All threads must be resolved'
end
diff --git a/spec/features/projects/settings/visibility_settings_spec.rb b/spec/features/projects/settings/visibility_settings_spec.rb
index fc78b5b5769..5cb12544066 100644
--- a/spec/features/projects/settings/visibility_settings_spec.rb
+++ b/spec/features/projects/settings/visibility_settings_spec.rb
@@ -28,26 +28,12 @@ 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 'merge requests select' do
- it 'hides merge requests section' do
- find('.project-feature-controls[data-for="project[project_feature_attributes][merge_requests_access_level]"] .gl-toggle').click
-
- expect(page).to have_selector('.merge-requests-feature', visible: false)
- end
-
- context 'given project with merge_requests_disabled access level' do
- let(:project) { create(:project, :merge_requests_disabled, namespace: user.namespace) }
-
- it 'hides merge requests section' do
- expect(page).to have_selector('.merge-requests-feature', visible: false)
- end
- end
- 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
@@ -55,6 +41,8 @@ RSpec.describe 'Projects > Settings > Visibility settings', :js 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
diff --git a/spec/features/projects/settings/webhooks_settings_spec.rb b/spec/features/projects/settings/webhooks_settings_spec.rb
index c84de7fc03f..d525544ac15 100644
--- a/spec/features/projects/settings/webhooks_settings_spec.rb
+++ b/spec/features/projects/settings/webhooks_settings_spec.rb
@@ -139,6 +139,12 @@ RSpec.describe 'Projects > Settings > Webhook Settings' do
expect(page).to have_current_path(edit_project_hook_path(project, hook), ignore_query: true)
end
+
+ it 'does not show search settings on the hook log details' do
+ visit project_hook_hook_log_path(project, hook, hook_log)
+
+ expect(page).not_to have_field(placeholder: 'Search settings', disabled: true)
+ end
end
end
end
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 aa61b629d92..e0dd4f65010 100644
--- a/spec/features/projects/show/user_interacts_with_stars_spec.rb
+++ b/spec/features/projects/show/user_interacts_with_stars_spec.rb
@@ -14,14 +14,36 @@ RSpec.describe 'Projects > Show > User interacts with project stars' do
end
it 'toggles the star' do
- find('.star-btn').click
+ star_project
expect(page).to have_css('.star-count', text: 1)
- find('.star-btn').click
+ unstar_project
expect(page).to have_css('.star-count', text: 0)
end
+
+ it 'validates starring a project' do
+ project.add_owner(user)
+
+ star_project
+
+ visit(dashboard_projects_path)
+
+ expect(page).to have_css('.stars', text: 1)
+ end
+
+ it 'validates un-starring a project' do
+ project.add_owner(user)
+
+ star_project
+
+ unstar_project
+
+ visit(dashboard_projects_path)
+
+ expect(page).to have_css('.stars', text: 0)
+ end
end
context 'when user is not signed in' do
@@ -38,3 +60,15 @@ RSpec.describe 'Projects > Show > User interacts with project stars' do
end
end
end
+
+private
+
+def star_project
+ click_button(_('Star'))
+ wait_for_requests
+end
+
+def unstar_project
+ click_button(_('Unstar'))
+ wait_for_requests
+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 fb2f0539558..1440db141a6 100644
--- a/spec/features/projects/show/user_sees_collaboration_links_spec.rb
+++ b/spec/features/projects/show/user_sees_collaboration_links_spec.rb
@@ -39,7 +39,7 @@ RSpec.describe 'Projects > Show > Collaboration links', :js do
# The dropdown above the tree
page.within('.repo-breadcrumb') do
- find('.qa-add-to-tree').click # rubocop:disable QA/SelectorUsage
+ find('[data-testid="add-to-tree"]').click
aggregate_failures 'dropdown links above the repo tree' do
expect(page).to have_link('New file')
@@ -71,7 +71,7 @@ RSpec.describe 'Projects > Show > Collaboration links', :js do
find_new_menu_toggle.click
end
- expect(page).not_to have_selector('.qa-add-to-tree') # rubocop:disable QA/SelectorUsage
+ expect(page).not_to have_selector('[data-testid="add-to-tree"]')
expect(page).not_to have_link('Web IDE')
end
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 89f6b4237a4..5056e245fed 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
@@ -288,6 +288,17 @@ RSpec.describe 'Projects > Show > User sees setup shortcut buttons' do
end
end
+ it 'no Auto DevOps button if builds feature is disabled' do
+ project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED)
+
+ visit project_path(project)
+
+ page.within('.project-buttons') do
+ expect(page).not_to have_link('Enable Auto DevOps')
+ expect(page).not_to have_link('Auto DevOps enabled')
+ end
+ end
+
it 'no "Enable Auto DevOps" button when .gitlab-ci.yml already exists' do
Files::CreateService.new(
project,
diff --git a/spec/features/projects/tree/create_directory_spec.rb b/spec/features/projects/tree/create_directory_spec.rb
index 074469a9b55..9c950cfee6e 100644
--- a/spec/features/projects/tree/create_directory_spec.rb
+++ b/spec/features/projects/tree/create_directory_spec.rb
@@ -7,6 +7,8 @@ RSpec.describe 'Multi-file editor new directory', :js do
let(:project) { create(:project, :repository) }
before do
+ stub_feature_flags(vscode_web_ide: false)
+
project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/projects/tree/create_file_spec.rb b/spec/features/projects/tree/create_file_spec.rb
index 85c644fa528..c0567ed4580 100644
--- a/spec/features/projects/tree/create_file_spec.rb
+++ b/spec/features/projects/tree/create_file_spec.rb
@@ -7,6 +7,8 @@ RSpec.describe 'Multi-file editor new file', :js do
let(:project) { create(:project, :repository) }
before do
+ stub_feature_flags(vscode_web_ide: false)
+
project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/projects/tree/tree_show_spec.rb b/spec/features/projects/tree/tree_show_spec.rb
index 163e347d03d..eb0ef756b30 100644
--- a/spec/features/projects/tree/tree_show_spec.rb
+++ b/spec/features/projects/tree/tree_show_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe 'Projects tree', :js do
expect(page).to have_selector('.tree-item')
expect(page).to have_content('add tests for .gitattributes custom highlighting')
expect(page).not_to have_selector('[data-testid="alert-danger"]')
- expect(page).not_to have_selector('[data-qa-selector="label-lfs"]', text: 'LFS') # rubocop:disable QA/SelectorUsage
+ expect(page).not_to have_selector('[data-testid="label-lfs"]', text: 'LFS')
end
it 'renders tree table for a subtree without errors' do
@@ -35,7 +35,7 @@ RSpec.describe 'Projects tree', :js do
expect(page).to have_selector('.tree-item')
expect(page).to have_content('add spaces in whitespace file')
- expect(page).not_to have_selector('[data-qa-selector="label-lfs"]', text: 'LFS') # rubocop:disable QA/SelectorUsage
+ expect(page).not_to have_selector('[data-testid="label-lfs"]', text: 'LFS')
expect(page).not_to have_selector('[data-testid="alert-danger"]')
end
@@ -112,11 +112,15 @@ RSpec.describe 'Projects tree', :js do
it 'renders LFS badge on blob item' do
visit project_tree_path(project, File.join('master', 'files/lfs'))
- expect(page).to have_selector('[data-qa-selector="label-lfs"]', text: 'LFS') # rubocop:disable QA/SelectorUsage
+ expect(page).to have_selector('[data-testid="label-lfs"]', text: 'LFS')
end
end
context 'web IDE' do
+ before do
+ stub_feature_flags(vscode_web_ide: false)
+ end
+
it 'opens folder in IDE' do
visit project_tree_path(project, File.join('master', 'bar'))
diff --git a/spec/features/projects/tree/upload_file_spec.rb b/spec/features/projects/tree/upload_file_spec.rb
index ce00483bc91..f32141d6051 100644
--- a/spec/features/projects/tree/upload_file_spec.rb
+++ b/spec/features/projects/tree/upload_file_spec.rb
@@ -9,6 +9,8 @@ RSpec.describe 'Multi-file editor upload file', :js do
let(:img_file) { File.join(Rails.root, 'spec', 'fixtures', 'dk.png') }
before do
+ stub_feature_flags(vscode_web_ide: false)
+
project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/projects/user_sorts_projects_spec.rb b/spec/features/projects/user_sorts_projects_spec.rb
index 7c970f7ee3d..b9b28398279 100644
--- a/spec/features/projects/user_sorts_projects_spec.rb
+++ b/spec/features/projects/user_sorts_projects_spec.rb
@@ -24,6 +24,7 @@ RSpec.describe 'User sorts projects and order persists' do
end
it "is set on the group_canonical_path" do
+ stub_feature_flags(group_overview_tabs_vue: false)
visit(group_canonical_path(group))
within '[data-testid=group_sort_by_dropdown]' do
@@ -32,6 +33,7 @@ RSpec.describe 'User sorts projects and order persists' do
end
it "is set on the details_group_path" do
+ stub_feature_flags(group_overview_tabs_vue: false)
visit(details_group_path(group))
within '[data-testid=group_sort_by_dropdown]' do
@@ -64,6 +66,7 @@ RSpec.describe 'User sorts projects and order persists' do
context 'from group homepage', :js do
before do
+ stub_feature_flags(group_overview_tabs_vue: false)
sign_in(user)
visit(group_canonical_path(group))
within '[data-testid=group_sort_by_dropdown]' do
@@ -77,6 +80,7 @@ RSpec.describe 'User sorts projects and order persists' do
context 'from group details', :js do
before do
+ stub_feature_flags(group_overview_tabs_vue: false)
sign_in(user)
visit(details_group_path(group))
within '[data-testid=group_sort_by_dropdown]' do
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index d228fb084c3..cbd9340b737 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -418,8 +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]"]' },
- { form: '.rspec-merge-request-settings', input: '#project_printing_merge_request_link_enabled' }]
+ it_behaves_like 'dirty submit form', [{ form: '.js-general-settings-form', input: 'input[name="project[name]"]' }]
end
describe 'view for a user without an access to a repo' do
@@ -440,103 +439,6 @@ RSpec.describe 'Project' do
end
end
- describe 'storage_enforcement_banner', :js do
- let_it_be(:group) { create(:group) }
- let_it_be_with_refind(:user) { create(:user) }
- let_it_be(:project) { create(:project, group: group) }
-
- before do
- group.add_maintainer(user)
- sign_in(user)
- end
-
- context 'with storage_enforcement_date set' do
- let_it_be(:storage_enforcement_date) { Date.today + 30 }
-
- before do
- allow_next_found_instance_of(Group) do |group|
- allow(group).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
- end
- end
-
- it 'displays the banner in the project page' do
- visit project_path(project)
- expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
- end
-
- context 'when in a subgroup project page' do
- let_it_be(:subgroup) { create(:group, parent: group) }
- let_it_be(:project) { create(:project, namespace: subgroup) }
-
- it 'displays the banner' do
- visit project_path(project)
- expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
- end
- end
-
- context 'when in a user namespace project page' do
- let_it_be(:project) { create(:project, namespace: user.namespace) }
-
- before do
- allow_next_found_instance_of(Namespaces::UserNamespace) do |user_namespace|
- allow(user_namespace).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
- end
- end
-
- it 'displays the banner' do
- visit project_path(project)
- expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
- end
- end
-
- it 'does not display the banner in a paid group project page' do
- allow_next_found_instance_of(Group) do |group|
- allow(group).to receive(:paid?).and_return(true)
- end
- visit project_path(project)
- expect_page_not_to_have_storage_enforcement_banner
- end
-
- it 'does not display the banner if user has previously closed unless threshold has changed' do
- visit project_path(project)
- expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
- find('.js-storage-enforcement-banner [data-testid="close-icon"]').click
- wait_for_requests
- page.refresh
- expect_page_not_to_have_storage_enforcement_banner
-
- storage_enforcement_date = Date.today + 13
- allow_next_found_instance_of(Group) do |group|
- allow(group).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
- end
- page.refresh
- expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
- end
- end
-
- context 'with storage_enforcement_date not set' do
- before do
- allow_next_found_instance_of(Group) do |group|
- allow(group).to receive(:storage_enforcement_date).and_return(nil)
- end
- end
-
- it 'does not display the banner in the group page' do
- stub_feature_flags(namespace_storage_limit_bypass_date_check: false)
- visit project_path(project)
- expect_page_not_to_have_storage_enforcement_banner
- end
- end
- end
-
- def expect_page_to_have_storage_enforcement_banner(storage_enforcement_date)
- expect(page).to have_text "Effective #{storage_enforcement_date}, namespace storage limits will apply"
- end
-
- def expect_page_not_to_have_storage_enforcement_banner
- expect(page).not_to have_text "namespace storage limits will apply"
- end
-
def remove_with_confirm(button_text, confirm_with, confirm_button_text = 'Confirm')
click_button button_text
fill_in 'confirm_name_input', with: confirm_with
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index 2600c00346e..482f3d62f36 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe 'Runners' do
- let(:user) { create(:user) }
+ let_it_be(:user) { create(:user) }
before do
sign_in(user)
@@ -24,25 +24,25 @@ RSpec.describe 'Runners' do
end
context 'when a project has enabled shared_runners' do
- let(:project) { create(:project) }
+ let_it_be(:project) { create(:project) }
before do
project.add_maintainer(user)
end
context 'when a project_type runner is activated on the project' do
- let!(:specific_runner) { create(:ci_runner, :project, projects: [project]) }
+ let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project]) }
it 'user sees the specific runner' do
visit project_runners_path(project)
within '.activated-specific-runners' do
- expect(page).to have_content(specific_runner.display_name)
+ expect(page).to have_content(project_runner.display_name)
end
- click_on specific_runner.short_sha
+ click_on project_runner.short_sha
- expect(page).to have_content(specific_runner.platform)
+ expect(page).to have_content(project_runner.platform)
end
it 'user can pause and resume the specific runner' do
@@ -72,7 +72,7 @@ RSpec.describe 'Runners' do
click_on 'Remove runner'
end
- expect(page).not_to have_content(specific_runner.display_name)
+ expect(page).not_to have_content(project_runner.display_name)
end
it 'user edits the runner to be protected' do
@@ -92,7 +92,7 @@ RSpec.describe 'Runners' do
context 'when a runner has a tag' do
before do
- specific_runner.update!(tag_list: ['tag'])
+ project_runner.update!(tag_list: ['tag'])
end
it 'user edits runner not to run untagged jobs' do
@@ -120,24 +120,23 @@ RSpec.describe 'Runners' do
expect(page.find('.available-shared-runners')).to have_content(shared_runner.display_name)
end
end
- end
- context 'when multiple runners are configured' do
- let!(:specific_runner) { create(:ci_runner, :project, projects: [project]) }
- let!(:specific_runner_2) { create(:ci_runner, :project, projects: [project]) }
+ context 'when multiple runners are configured' do
+ let!(:project_runner_2) { create(:ci_runner, :project, projects: [project]) }
- it 'adds pagination to the runner list' do
- stub_const('Projects::Settings::CiCdController::NUMBER_OF_RUNNERS_PER_PAGE', 1)
+ it 'adds pagination to the runner list' do
+ stub_const('Projects::Settings::CiCdController::NUMBER_OF_RUNNERS_PER_PAGE', 1)
- visit project_runners_path(project)
+ visit project_runners_path(project)
- expect(find('.pagination')).not_to be_nil
+ expect(find('.pagination')).not_to be_nil
+ end
end
end
context 'when a specific runner exists in another project' do
let(:another_project) { create(:project) }
- let!(:specific_runner) { create(:ci_runner, :project, projects: [another_project]) }
+ let!(:project_runner) { create(:ci_runner, :project, projects: [another_project]) }
before do
another_project.add_maintainer(user)
@@ -150,13 +149,13 @@ RSpec.describe 'Runners' do
click_on 'Enable for this project'
end
- expect(page.find('.activated-specific-runners')).to have_content(specific_runner.display_name)
+ expect(page.find('.activated-specific-runners')).to have_content(project_runner.display_name)
within '.activated-specific-runners' do
click_on 'Disable for this project'
end
- expect(page.find('.available-specific-runners')).to have_content(specific_runner.display_name)
+ expect(page.find('.available-specific-runners')).to have_content(project_runner.display_name)
end
end
@@ -255,7 +254,8 @@ RSpec.describe 'Runners' do
project.add_maintainer(user)
end
- let(:group) { create :group }
+ let_it_be(:group) { create :group }
+ let_it_be(:project) { create :project, group: group }
context 'as project and group maintainer' do
before do
@@ -263,8 +263,6 @@ RSpec.describe 'Runners' do
end
context 'project with a group but no group runner' do
- let(:project) { create :project, group: group }
-
it 'group runners are not available' do
visit project_runners_path(project)
@@ -280,8 +278,6 @@ RSpec.describe 'Runners' do
end
context 'project with a group but no group runner' do
- let(:project) { create :project, group: group }
-
it 'group runners are available' do
visit project_runners_path(project)
@@ -304,44 +300,46 @@ RSpec.describe 'Runners' do
end
end
- context 'project with a group but no group runner' do
- let(:group) { create(:group) }
- let(:project) { create(:project, group: group) }
+ context 'with group project' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
- it 'group runners are not available' do
- visit project_runners_path(project)
+ context 'project with a group but no group runner' do
+ it 'group runners are not available' do
+ visit project_runners_path(project)
- expect(page).to have_content 'This group does not have any group runners yet.'
+ expect(page).to have_content 'This group does not have any group runners yet.'
- expect(page).not_to have_content 'To register them, go to the group\'s Runners page.'
- expect(page).to have_content 'Ask your group owner to set up a group runner.'
+ expect(page).not_to have_content 'To register them, go to the group\'s Runners page.'
+ expect(page).to have_content 'Ask your group owner to set up a group runner.'
+ end
end
- end
- context 'project with a group and a group runner' do
- let(:group) { create(:group) }
- let(:project) { create(:project, group: group) }
- let!(:ci_runner) { create(:ci_runner, :group, groups: [group], description: 'group-runner') }
+ context 'project with a group and a group runner' do
+ let_it_be(:ci_runner) do
+ create(:ci_runner, :group, groups: [group], description: 'group-runner')
+ end
- it 'group runners are available' do
- visit project_runners_path(project)
+ it 'group runners are available' do
+ visit project_runners_path(project)
- expect(page).to have_content 'Available group runners: 1'
- expect(page).to have_content 'group-runner'
- end
+ expect(page).to have_content 'Available group runners: 1'
+ expect(page).to have_content 'group-runner'
+ end
- it 'group runners may be disabled for a project' do
- visit project_runners_path(project)
+ it 'group runners may be disabled for a project' do
+ visit project_runners_path(project)
- click_on 'Disable group runners'
+ click_on 'Disable group runners'
- expect(page).to have_content 'Enable group runners'
- expect(project.reload.group_runners_enabled).to be false
+ expect(page).to have_content 'Enable group runners'
+ expect(project.reload.group_runners_enabled).to be false
- click_on 'Enable group runners'
+ click_on 'Enable group runners'
- expect(page).to have_content 'Disable group runners'
- expect(project.reload.group_runners_enabled).to be true
+ expect(page).to have_content 'Disable group runners'
+ expect(project.reload.group_runners_enabled).to be true
+ 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 53c95b4a446..e2c8708be78 100644
--- a/spec/features/search/user_searches_for_code_spec.rb
+++ b/spec/features/search/user_searches_for_code_spec.rb
@@ -69,8 +69,12 @@ RSpec.describe 'User searches for code' do
expect(page).to have_selector('.results', text: expected_result)
- find('.js-project-refs-dropdown').click
- find('.dropdown-page-one .dropdown-content').click_link('v1.0.0')
+ find('.ref-selector').click
+ wait_for_requests
+
+ page.within('.ref-selector') do
+ find('li', text: 'v1.0.0').click
+ end
expect(page).to have_selector('.results', text: expected_result)
@@ -96,36 +100,41 @@ RSpec.describe 'User searches for code' do
end
it 'shows ref switcher in code result summary' do
- expect(find('.js-project-refs-dropdown')).to have_text(ref_name)
+ expect(find('.ref-selector')).to have_text(ref_name)
end
it 'persists branch name across search' do
find('.gl-search-box-by-click-search-button').click
- expect(find('.js-project-refs-dropdown')).to have_text(ref_name)
+ expect(find('.ref-selector')).to have_text(ref_name)
end
# this example is use to test the desgine that the refs is not
# only repersent the branch as well as the tags.
it 'ref swither list all the branchs and tags' do
- find('.js-project-refs-dropdown').click
- expect(find('.dropdown-page-one .dropdown-content')).to have_link('sha-starting-with-large-number')
- expect(find('.dropdown-page-one .dropdown-content')).to have_link('v1.0.0')
+ find('.ref-selector').click
+ wait_for_requests
+
+ page.within('.ref-selector') do
+ expect(page).to have_selector('li', text: 'add-ipython-files')
+ expect(page).to have_selector('li', text: 'v1.0.0')
+ end
end
it 'search result changes when refs switched' do
+ ref = 'master'
expect(find('.results')).not_to have_content('path = gitlab-grack')
- find('.js-project-refs-dropdown').click
- find('.dropdown-page-one .dropdown-content').click_link('master')
+ find('.ref-selector').click
+ wait_for_requests
- expect(page).to have_selector('.results', text: 'path = gitlab-grack')
- end
+ page.within('.ref-selector') do
+ fill_in _('Search by Git revision'), with: ref
+ wait_for_requests
+
+ find('li', text: ref).click
+ end
- it 'persist refs over browser tabs' do
- ref = 'feature'
- find('.js-project-refs-dropdown').click
- link = find_link(ref)[:href]
- expect(link.include?("repository_ref=" + ref)).to be(true)
+ expect(page).to have_selector('.results', text: 'path = gitlab-grack')
end
end
end
@@ -146,36 +155,41 @@ RSpec.describe 'User searches for code' do
end
it 'shows ref switcher in code result summary' do
- expect(find('.js-project-refs-dropdown')).to have_text(ref_name)
+ expect(find('.ref-selector')).to have_text(ref_name)
end
it 'persists branch name across search' do
find('.gl-search-box-by-click-search-button').click
- expect(find('.js-project-refs-dropdown')).to have_text(ref_name)
+ expect(find('.ref-selector')).to have_text(ref_name)
end
# this example is use to test the desgine that the refs is not
# only repersent the branch as well as the tags.
it 'ref swither list all the branchs and tags' do
- find('.js-project-refs-dropdown').click
- expect(find('.dropdown-page-one .dropdown-content')).to have_link('sha-starting-with-large-number')
- expect(find('.dropdown-page-one .dropdown-content')).to have_link('v1.0.0')
+ find('.ref-selector').click
+ wait_for_requests
+
+ page.within('.ref-selector') do
+ expect(page).to have_selector('li', text: 'add-ipython-files')
+ expect(page).to have_selector('li', text: 'v1.0.0')
+ end
end
it 'search result changes when refs switched' do
+ ref = 'master'
expect(find('.results')).not_to have_content('path = gitlab-grack')
- find('.js-project-refs-dropdown').click
- find('.dropdown-page-one .dropdown-content').click_link('master')
+ find('.ref-selector').click
+ wait_for_requests
- expect(page).to have_selector('.results', text: 'path = gitlab-grack')
- end
+ page.within('.ref-selector') do
+ fill_in _('Search by Git revision'), with: ref
+ wait_for_requests
+
+ find('li', text: ref).click
+ end
- it 'persist refs over browser tabs' do
- ref = 'feature'
- find('.js-project-refs-dropdown').click
- link = find_link(ref)[:href]
- expect(link.include?("repository_ref=" + ref)).to be(true)
+ expect(page).to have_selector('.results', text: 'path = gitlab-grack')
end
end
end
@@ -187,12 +201,12 @@ RSpec.describe 'User searches for code' do
submit_search('test')
select_search_scope('Code')
- expect(page).to have_selector('.js-project-refs-dropdown')
+ expect(page).to have_selector('.ref-selector')
select_search_scope('Issues')
expect(find(:css, '.results')).to have_link(issue.title)
- expect(page).not_to have_selector('.js-project-refs-dropdown')
+ expect(page).not_to have_selector('.ref-selector')
end
end
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 1523586ab26..41288a34fb2 100644
--- a/spec/features/search/user_uses_header_search_field_spec.rb
+++ b/spec/features/search/user_uses_header_search_field_spec.rb
@@ -17,6 +17,7 @@ RSpec.describe 'User uses header search field', :js do
end
before do
+ allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).and_return(0)
allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit).and_return(1000)
allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit_unauthenticated).and_return(1000)
sign_in(user)
diff --git a/spec/features/snippets/user_creates_snippet_spec.rb b/spec/features/snippets/user_creates_snippet_spec.rb
index 628468a2abe..fd95516090a 100644
--- a/spec/features/snippets/user_creates_snippet_spec.rb
+++ b/spec/features/snippets/user_creates_snippet_spec.rb
@@ -80,7 +80,7 @@ RSpec.describe 'User creates snippet', :js do
context 'when snippets default visibility level is restricted' do
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PRIVATE],
- default_snippet_visibility: Gitlab::VisibilityLevel::PRIVATE)
+ default_snippet_visibility: Gitlab::VisibilityLevel::PRIVATE)
end
it 'creates a snippet using the lowest available visibility level as default' do
diff --git a/spec/features/tags/developer_creates_tag_spec.rb b/spec/features/tags/developer_creates_tag_spec.rb
index b0219cb546d..ca76a94092e 100644
--- a/spec/features/tags/developer_creates_tag_spec.rb
+++ b/spec/features/tags/developer_creates_tag_spec.rb
@@ -60,7 +60,7 @@ RSpec.describe 'Developer creates tag' do
it 'opens dropdown for ref', :js do
click_link 'New tag'
- ref_row = find('.form-group:nth-of-type(2) .col-sm-10')
+ ref_row = find('.form-group:nth-of-type(2) .col-sm-12')
page.within ref_row do
ref_input = find('[name="ref"]', visible: false)
expect(ref_input.value).to eq 'master'
diff --git a/spec/features/user_opens_link_to_comment_spec.rb b/spec/features/user_opens_link_to_comment_spec.rb
index ae84f69f432..3fb1505ff5b 100644
--- a/spec/features/user_opens_link_to_comment_spec.rb
+++ b/spec/features/user_opens_link_to_comment_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe 'User opens link to comment', :js do
wait_for_requests
- expect(page.find('#discussion-filter-dropdown')).to have_content('Show all activity')
+ expect(find('#discussion-preferences-dropdown')).to have_content('Sort or filter')
expect(page).not_to have_content('Something went wrong while fetching comments')
# Auto-switching to show all notes shouldn't be persisted
diff --git a/spec/features/users/email_verification_on_login_spec.rb b/spec/features/users/email_verification_on_login_spec.rb
index c8301c2fc91..f7102eaf9b7 100644
--- a/spec/features/users/email_verification_on_login_spec.rb
+++ b/spec/features/users/email_verification_on_login_spec.rb
@@ -118,7 +118,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
# Expect an error message
expect_log_message('Failed Attempt', reason: 'rate_limited')
expect(page).to have_content("You've reached the maximum amount of tries. "\
- 'Wait 10 minutes or resend a new code and try again.')
+ 'Wait 10 minutes or send a new code and try again.')
# Wait for 10 minutes
travel 10.minutes
@@ -138,7 +138,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
# Expect an error message
expect_log_message('Failed Attempt', reason: 'invalid')
- expect(page).to have_content('The code is incorrect. Enter it again, or resend a new code.')
+ expect(page).to have_content('The code is incorrect. Enter it again, or send a new code.')
end
it 'verifies expired codes' do
@@ -150,12 +150,12 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
code = expect_instructions_email_and_extract_code
# Wait for the code to expire before verifying
- travel VerifiesWithEmail::TOKEN_VALID_FOR_MINUTES.minutes + 1.second
+ travel Users::EmailVerification::ValidateTokenService::TOKEN_VALID_FOR_MINUTES.minutes + 1.second
verify_code(code)
# Expect an error message
expect_log_message('Failed Attempt', reason: 'expired')
- expect(page).to have_content('The code has expired. Resend a new code and try again.')
+ expect(page).to have_content('The code has expired. Send a new code and try again.')
end
end
end
@@ -255,7 +255,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
perform_enqueued_jobs do
# The user is prompted for a verification code
gitlab_sign_in(user)
- expect(page).to have_content('Help us protect your account')
+ expect(page).to have_content(s_('IdentityVerification|Help us protect your account'))
code = expect_instructions_email_and_extract_code
# We toggle the feature flag off
@@ -266,12 +266,13 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
new_code = expect_instructions_email_and_extract_code
verify_code(code)
- expect(page).to have_content('The code is incorrect. Enter it again, or resend a new code.')
+ expect(page)
+ .to have_content(s_('IdentityVerification|The code is incorrect. Enter it again, or send a new code.'))
- travel VerifiesWithEmail::TOKEN_VALID_FOR_MINUTES.minutes + 1.second
+ travel Users::EmailVerification::ValidateTokenService::TOKEN_VALID_FOR_MINUTES.minutes + 1.second
verify_code(new_code)
- expect(page).to have_content('The code has expired. Resend a new code and try again.')
+ expect(page).to have_content(s_('IdentityVerification|The code has expired. Send a new code and try again.'))
click_link 'Resend code'
another_code = expect_instructions_email_and_extract_code
@@ -296,7 +297,7 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
it 'the unlock link still works' do
# The user is locked and unlock instructions are sent
- expect(page).to have_content('Invalid login or password.')
+ expect(page).to have_content(_('Invalid login or password.'))
user.reload
expect(user.locked_at).not_to be_nil
expect(user.unlock_token).not_to be_nil
@@ -334,24 +335,24 @@ RSpec.describe 'Email Verification On Login', :clean_gitlab_redis_rate_limiting
def expect_instructions_email_and_extract_code
mail = find_email_for(user)
expect(mail.to).to match_array([user.email])
- expect(mail.subject).to eq('Verify your identity')
- code = mail.body.parts.first.to_s[/\d{#{VerifiesWithEmail::TOKEN_LENGTH}}/o]
+ expect(mail.subject).to eq(s_('IdentityVerification|Verify your identity'))
+ code = mail.body.parts.first.to_s[/\d{#{Users::EmailVerification::GenerateTokenService::TOKEN_LENGTH}}/o]
reset_delivered_emails!
code
end
def verify_code(code)
- fill_in 'Verification code', with: code
- click_button 'Verify code'
+ fill_in s_('IdentityVerification|Verification code'), with: code
+ click_button s_('IdentityVerification|Verify code')
end
def expect_log_message(event = nil, times = 1, reason: '', message: nil)
expect(Gitlab::AppLogger).to have_received(:info)
.exactly(times).times
.with(message || hash_including(message: 'Email Verification',
- event: event,
- username: user.username,
- ip: '127.0.0.1',
- reason: reason))
+ event: event,
+ username: user.username,
+ ip: '127.0.0.1',
+ reason: reason))
end
end
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index b875dbe1340..5ca5bd72b79 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -105,6 +105,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
before do
stub_application_setting(send_user_confirmation_email: true)
allow(User).to receive(:allow_unconfirmed_access_for).and_return grace_period
+ stub_feature_flags(identity_verification: false)
end
context 'within the grace period' do
@@ -862,7 +863,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
context 'when the user already enabled 2FA' do
before do
user.update!(otp_required_for_login: true,
- otp_secret: User.generate_otp_secret(32))
+ otp_secret: User.generate_otp_secret(32))
end
it 'asks the user to accept the terms' do
@@ -954,6 +955,7 @@ RSpec.describe 'Login', :clean_gitlab_redis_sessions do
before do
stub_application_setting(send_user_confirmation_email: true)
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
end
diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb
index 068e1fd4243..bbf5882f89f 100644
--- a/spec/features/users/show_spec.rb
+++ b/spec/features/users/show_spec.rb
@@ -339,7 +339,7 @@ RSpec.describe 'User page' do
subject
- page.within '.navbar-nav' do
+ page.within '.navbar-gitlab' do
expect(page).to have_link('Sign in')
end
end
@@ -351,7 +351,7 @@ RSpec.describe 'User page' do
subject
- page.within '.navbar-nav' do
+ page.within '.navbar-gitlab' do
expect(page).to have_link('Sign in / Register')
end
end
diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb
index f2381e41de8..de53e722603 100644
--- a/spec/features/users/signup_spec.rb
+++ b/spec/features/users/signup_spec.rb
@@ -66,6 +66,7 @@ RSpec.describe 'Signup' do
flag_values = [true, false]
flag_values.each do |val|
before do
+ stub_feature_flags(arkose_labs_signup_challenge: false)
stub_feature_flags(restyle_login_page: val)
stub_application_setting(require_admin_approval_after_user_signup: false)
end
@@ -202,6 +203,7 @@ RSpec.describe 'Signup' do
context 'when soft email confirmation is not enabled' do
before do
stub_feature_flags(soft_email_confirmation: false)
+ stub_feature_flags(identity_verification: false)
end
it 'creates the user account and sends a confirmation email, and pre-fills email address after confirming' do
@@ -297,9 +299,8 @@ RSpec.describe 'Signup' do
enforce_terms
end
- it 'renders text that the user confirms terms by clicking register' do
+ it 'renders text that the user confirms terms by signing in' do
visit new_user_registration_path
-
expect(page).to have_content(/By clicking Register, I agree that I have read and accepted the Terms of Use and Privacy Policy/)
fill_in_signup_form
@@ -391,7 +392,7 @@ RSpec.describe 'Signup' do
enforce_terms
end
- it 'renders text that the user confirms terms by clicking register' do
+ it 'renders text that the user confirms terms by signing in' do
visit new_user_registration_path
expect(page).to have_content(/By clicking Register, I agree that I have read and accepted the Terms of Use and Privacy Policy/)
diff --git a/spec/features/work_items/work_item_children_spec.rb b/spec/features/work_items/work_item_children_spec.rb
new file mode 100644
index 00000000000..95774680a2b
--- /dev/null
+++ b/spec/features/work_items/work_item_children_spec.rb
@@ -0,0 +1,110 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Work item children', :js do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :public, namespace: group) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:issue) { create(:issue, project: project) }
+
+ context 'for signed in user' do
+ before do
+ project.add_developer(user)
+
+ sign_in(user)
+
+ stub_feature_flags(work_items: true)
+ stub_feature_flags(work_items_hierarchy: true)
+
+ visit project_issue_path(project, issue)
+
+ wait_for_requests
+ end
+
+ it 'are not displayed when issue does not have work item children', :aggregate_failures do
+ page.within('[data-testid="work-item-links"]') do
+ expect(find('[data-testid="links-empty"]')).to have_content(_('No tasks are currently assigned.'))
+ expect(page).not_to have_selector('[data-testid="add-links-form"]')
+ expect(page).not_to have_selector('[data-testid="links-child"]')
+ end
+ end
+
+ it 'toggles widget body', :aggregate_failures do
+ page.within('[data-testid="work-item-links"]') do
+ expect(page).to have_selector('[data-testid="links-body"]')
+
+ click_button 'Collapse tasks'
+
+ expect(page).not_to have_selector('[data-testid="links-body"]')
+
+ click_button 'Expand tasks'
+
+ expect(page).to have_selector('[data-testid="links-body"]')
+ end
+ end
+
+ it 'toggles form', :aggregate_failures do
+ page.within('[data-testid="work-item-links"]') do
+ expect(page).not_to have_selector('[data-testid="add-links-form"]')
+
+ click_button 'Add'
+
+ expect(page).to have_selector('[data-testid="add-links-form"]')
+
+ click_button 'Cancel'
+
+ expect(page).not_to have_selector('[data-testid="add-links-form"]')
+ end
+ end
+
+ it 'addss a child task', :aggregate_failures do
+ page.within('[data-testid="work-item-links"]') do
+ click_button 'Add'
+
+ expect(page).to have_button('Create task', disabled: true)
+ fill_in 'Add a title', with: 'Task 1'
+
+ expect(page).to have_button('Create task', disabled: false)
+
+ click_button 'Create task'
+
+ wait_for_all_requests
+
+ expect(find('[data-testid="links-child"]')).to have_content('Task 1')
+ end
+ end
+
+ it 'removes a child task and undoing', :aggregate_failures do
+ page.within('[data-testid="work-item-links"]') do
+ click_button 'Add'
+ fill_in 'Add a title', with: 'Task 1'
+ click_button 'Create task'
+ wait_for_all_requests
+
+ expect(find('[data-testid="links-child"]')).to have_content('Task 1')
+ expect(find('[data-testid="children-count"]')).to have_content('1')
+
+ find('[data-testid="links-menu"]').click
+ click_button 'Remove'
+
+ wait_for_all_requests
+
+ expect(page).not_to have_content('Task 1')
+ expect(find('[data-testid="children-count"]')).to have_content('0')
+ end
+
+ page.within('.gl-toast') do
+ expect(find('.toast-body')).to have_content(_('Child removed'))
+ find('.b-toaster a', text: 'Undo').click
+ end
+
+ wait_for_all_requests
+
+ page.within('[data-testid="work-item-links"]') do
+ expect(find('[data-testid="links-child"]')).to have_content('Task 1')
+ expect(find('[data-testid="children-count"]')).to have_content('1')
+ end
+ end
+ end
+end
diff --git a/spec/finders/bulk_imports/entities_finder_spec.rb b/spec/finders/bulk_imports/entities_finder_spec.rb
index 54c792cb4d8..9e8d04400eb 100644
--- a/spec/finders/bulk_imports/entities_finder_spec.rb
+++ b/spec/finders/bulk_imports/entities_finder_spec.rb
@@ -88,10 +88,11 @@ RSpec.describe BulkImports::EntitiesFinder do
let(:order) { :asc }
it 'returns entities sorted ascending' do
- expect(subject.execute).to eq([
- started_entity_1, finished_entity_1, failed_entity_1,
- started_entity_2, finished_entity_2, failed_entity_2
- ])
+ expect(subject.execute).to eq(
+ [
+ started_entity_1, finished_entity_1, failed_entity_1,
+ started_entity_2, finished_entity_2, failed_entity_2
+ ])
end
end
@@ -99,10 +100,11 @@ RSpec.describe BulkImports::EntitiesFinder do
let(:order) { :desc }
it 'returns entities sorted descending' do
- expect(subject.execute).to eq([
- failed_entity_2, finished_entity_2, started_entity_2,
- failed_entity_1, finished_entity_1, started_entity_1
- ])
+ expect(subject.execute).to eq(
+ [
+ failed_entity_2, finished_entity_2, started_entity_2,
+ failed_entity_1, finished_entity_1, started_entity_1
+ ])
end
end
end
diff --git a/spec/finders/ci/daily_build_group_report_results_finder_spec.rb b/spec/finders/ci/daily_build_group_report_results_finder_spec.rb
index 5352cfe5238..add941089b0 100644
--- a/spec/finders/ci/daily_build_group_report_results_finder_spec.rb
+++ b/spec/finders/ci/daily_build_group_report_results_finder_spec.rb
@@ -50,12 +50,13 @@ RSpec.describe Ci::DailyBuildGroupReportResultsFinder do
let(:current_user) { user_with_permission }
it 'returns matching coverages within the given date range' do
- expect(coverages).to match_array([
- karma_coverage_2,
- rspec_coverage_2,
- karma_coverage_1,
- rspec_coverage_1
- ])
+ expect(coverages).to match_array(
+ [
+ karma_coverage_2,
+ rspec_coverage_2,
+ karma_coverage_1,
+ rspec_coverage_1
+ ])
end
context 'when ref_path is nil' do
@@ -73,10 +74,11 @@ RSpec.describe Ci::DailyBuildGroupReportResultsFinder do
let(:limit) { 2 }
it 'returns limited number of matching coverages within the given date range' do
- expect(coverages).to match_array([
- karma_coverage_2,
- rspec_coverage_2
- ])
+ expect(coverages).to match_array(
+ [
+ karma_coverage_2,
+ rspec_coverage_2
+ ])
end
end
diff --git a/spec/finders/ci/jobs_finder_spec.rb b/spec/finders/ci/jobs_finder_spec.rb
index 45e8cf5a582..dd3ba9721e4 100644
--- a/spec/finders/ci/jobs_finder_spec.rb
+++ b/spec/finders/ci/jobs_finder_spec.rb
@@ -56,7 +56,7 @@ RSpec.describe Ci::JobsFinder, '#execute' do
context 'scope is an array' do
let(:jobs) { [pending_job, running_job, successful_job, canceled_job] }
- let(:params) {{ scope: %w'running success' }}
+ 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)
diff --git a/spec/finders/ci/pipelines_for_merge_request_finder_spec.rb b/spec/finders/ci/pipelines_for_merge_request_finder_spec.rb
index a7cf041f553..6e218db1254 100644
--- a/spec/finders/ci/pipelines_for_merge_request_finder_spec.rb
+++ b/spec/finders/ci/pipelines_for_merge_request_finder_spec.rb
@@ -126,7 +126,7 @@ RSpec.describe Ci::PipelinesForMergeRequestFinder do
let!(:pipeline) do
create(:ci_empty_pipeline, project: project,
- sha: merge_request.diff_head_sha, ref: merge_request.source_branch)
+ sha: merge_request.diff_head_sha, ref: merge_request.source_branch)
end
it 'returns pipelines from diff_head_sha' do
@@ -140,7 +140,7 @@ RSpec.describe Ci::PipelinesForMergeRequestFinder do
let!(:branch_pipeline) do
create(:ci_pipeline, source: :push, project: project,
- ref: source_ref, sha: merge_request.merge_request_diff.head_commit_sha)
+ ref: source_ref, sha: merge_request.merge_request_diff.head_commit_sha)
end
let!(:tag_pipeline) do
@@ -149,12 +149,12 @@ RSpec.describe Ci::PipelinesForMergeRequestFinder do
let!(:detached_merge_request_pipeline) do
create(:ci_pipeline, source: :merge_request_event, project: project,
- ref: source_ref, sha: shas.second, merge_request: merge_request)
+ ref: source_ref, sha: shas.second, merge_request: merge_request)
end
let(:merge_request) do
create(:merge_request, source_project: project, source_branch: source_ref,
- target_project: project, target_branch: target_ref)
+ target_project: project, target_branch: target_ref)
end
let(:project) { create(:project, :repository) }
@@ -167,12 +167,12 @@ RSpec.describe Ci::PipelinesForMergeRequestFinder do
context 'when there are a branch pipeline and a merge request pipeline' do
let!(:branch_pipeline_2) do
create(:ci_pipeline, source: :push, project: project,
- ref: source_ref, sha: shas.first)
+ ref: source_ref, sha: shas.first)
end
let!(:detached_merge_request_pipeline_2) do
create(:ci_pipeline, source: :merge_request_event, project: project,
- ref: source_ref, sha: shas.first, merge_request: merge_request)
+ ref: source_ref, sha: shas.first, merge_request: merge_request)
end
it 'returns merge request pipelines first' do
@@ -184,7 +184,7 @@ RSpec.describe Ci::PipelinesForMergeRequestFinder do
context 'when there are multiple merge request pipelines from the same branch' do
let!(:branch_pipeline_2) do
create(:ci_pipeline, source: :push, project: project,
- ref: source_ref, sha: shas.first)
+ ref: source_ref, sha: shas.first)
end
let!(:branch_pipeline_with_sha_not_belonging_to_merge_request) do
@@ -193,12 +193,12 @@ RSpec.describe Ci::PipelinesForMergeRequestFinder do
let!(:detached_merge_request_pipeline_2) do
create(:ci_pipeline, source: :merge_request_event, project: project,
- ref: source_ref, sha: shas.first, merge_request: merge_request_2)
+ ref: source_ref, sha: shas.first, merge_request: merge_request_2)
end
let(:merge_request_2) do
create(:merge_request, source_project: project, source_branch: source_ref,
- target_project: project, target_branch: 'stable')
+ target_project: project, target_branch: 'stable')
end
before do
@@ -220,7 +220,7 @@ RSpec.describe Ci::PipelinesForMergeRequestFinder do
context 'when detached merge request pipeline is run on head ref of the merge request' do
let!(:detached_merge_request_pipeline) do
create(:ci_pipeline, source: :merge_request_event, project: project,
- ref: merge_request.ref_path, sha: shas.second, merge_request: merge_request)
+ ref: merge_request.ref_path, sha: shas.second, merge_request: merge_request)
end
it 'sets the head ref of the merge request to the pipeline ref' do
diff --git a/spec/finders/ci/runners_finder_spec.rb b/spec/finders/ci/runners_finder_spec.rb
index 96412c1e371..8d3c375385a 100644
--- a/spec/finders/ci/runners_finder_spec.rb
+++ b/spec/finders/ci/runners_finder_spec.rb
@@ -260,13 +260,13 @@ RSpec.describe Ci::RunnersFinder do
let_it_be(:runner_sub_group_2) { create(:ci_runner, :group, contacted_at: 10.minutes.ago) }
let_it_be(:runner_sub_group_3) { create(:ci_runner, :group, contacted_at: 9.minutes.ago) }
let_it_be(:runner_sub_group_4) { create(:ci_runner, :group, contacted_at: 8.minutes.ago) }
- let_it_be(:runner_project_1) { create(:ci_runner, :project, contacted_at: 7.minutes.ago, projects: [project])}
- let_it_be(:runner_project_2) { create(:ci_runner, :project, contacted_at: 6.minutes.ago, projects: [project_2])}
- let_it_be(:runner_project_3) { create(:ci_runner, :project, contacted_at: 5.minutes.ago, description: 'runner_project_search', projects: [project, project_2])}
- let_it_be(:runner_project_4) { create(:ci_runner, :project, contacted_at: 4.minutes.ago, projects: [project_3])}
- let_it_be(:runner_project_5) { create(:ci_runner, :project, contacted_at: 3.minutes.ago, tag_list: %w[runner_tag], projects: [project_4])}
- let_it_be(:runner_project_6) { create(:ci_runner, :project, contacted_at: 2.minutes.ago, projects: [project_5])}
- let_it_be(:runner_project_7) { create(:ci_runner, :project, contacted_at: 1.minute.ago, projects: [project_6])}
+ let_it_be(:runner_project_1) { create(:ci_runner, :project, contacted_at: 7.minutes.ago, projects: [project]) }
+ let_it_be(:runner_project_2) { create(:ci_runner, :project, contacted_at: 6.minutes.ago, projects: [project_2]) }
+ let_it_be(:runner_project_3) { create(:ci_runner, :project, contacted_at: 5.minutes.ago, description: 'runner_project_search', projects: [project, project_2]) }
+ let_it_be(:runner_project_4) { create(:ci_runner, :project, contacted_at: 4.minutes.ago, projects: [project_3]) }
+ let_it_be(:runner_project_5) { create(:ci_runner, :project, contacted_at: 3.minutes.ago, tag_list: %w[runner_tag], projects: [project_4]) }
+ let_it_be(:runner_project_6) { create(:ci_runner, :project, contacted_at: 2.minutes.ago, projects: [project_5]) }
+ let_it_be(:runner_project_7) { create(:ci_runner, :project, contacted_at: 1.minute.ago, projects: [project_6]) }
let(:target_group) { nil }
let(:membership) { nil }
diff --git a/spec/finders/concerns/packages/finder_helper_spec.rb b/spec/finders/concerns/packages/finder_helper_spec.rb
index e8648d131ff..94bcec6163e 100644
--- a/spec/finders/concerns/packages/finder_helper_spec.rb
+++ b/spec/finders/concerns/packages/finder_helper_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe ::Packages::FinderHelper do
subject { finder.execute(project1) }
- it { is_expected.to eq [package1]}
+ it { is_expected.to eq [package1] }
end
describe '#packages_visible_to_user' do
@@ -61,7 +61,7 @@ RSpec.describe ::Packages::FinderHelper do
end
shared_examples 'returning package1' do
- it { is_expected.to eq [package1]}
+ it { is_expected.to eq [package1] }
end
shared_examples 'returning no packages' do
@@ -165,7 +165,7 @@ RSpec.describe ::Packages::FinderHelper do
end
shared_examples 'returning project1' do
- it { is_expected.to eq [project1]}
+ it { is_expected.to eq [project1] }
end
shared_examples 'returning no project' do
diff --git a/spec/finders/container_repositories_finder_spec.rb b/spec/finders/container_repositories_finder_spec.rb
index 5d449d1b811..472c39d1f23 100644
--- a/spec/finders/container_repositories_finder_spec.rb
+++ b/spec/finders/container_repositories_finder_spec.rb
@@ -28,9 +28,9 @@ RSpec.describe ContainerRepositoriesFinder do
context "with name set to #{name}" do
let(:params) { { name: name } }
- it { is_expected.to contain_exactly(project_repository)}
+ it { is_expected.to contain_exactly(project_repository) }
- it { is_expected.not_to include(not_searched_repository)}
+ it { is_expected.not_to include(not_searched_repository) }
end
end
end
@@ -50,7 +50,7 @@ RSpec.describe ContainerRepositoriesFinder do
context "with sort set to #{order}" do
let(:params) { { sort: order } }
- it { is_expected.to eq([sort_repository2, sort_repository])}
+ it { is_expected.to eq([sort_repository2, sort_repository]) }
end
end
@@ -58,7 +58,7 @@ RSpec.describe ContainerRepositoriesFinder do
context "with sort set to #{order}" do
let(:params) { { sort: order } }
- it { is_expected.to eq([sort_repository, sort_repository2])}
+ it { is_expected.to eq([sort_repository, sort_repository2]) }
end
end
end
diff --git a/spec/finders/context_commits_finder_spec.rb b/spec/finders/context_commits_finder_spec.rb
index 95c685aea24..c22675bc67d 100644
--- a/spec/finders/context_commits_finder_spec.rb
+++ b/spec/finders/context_commits_finder_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe ContextCommitsFinder do
describe "#execute" do
let(:project) { create(:project, :repository) }
- let(:merge_request) { create(:merge_request) }
+ let(:merge_request) { create(:merge_request, source_branch: 'feature', target_branch: 'master') }
let(:commit) { create(:commit, id: '6d394385cf567f80a8fd85055db1ab4c5295806f') }
it 'filters commits by valid sha/commit message' do
@@ -24,5 +24,29 @@ RSpec.describe ContextCommitsFinder do
expect(commits).to be_empty
end
+
+ it 'returns commits based in author filter' do
+ params = { search: 'test text', author: 'Job van der Voort' }
+ commits = described_class.new(project, merge_request, params).execute
+
+ expect(commits.length).to eq(1)
+ expect(commits[0].id).to eq('b83d6e391c22777fca1ed3012fce84f633d7fed0')
+ end
+
+ it 'returns commits based in before filter' do
+ params = { search: 'test text', committed_before: 1474828200 }
+ commits = described_class.new(project, merge_request, params).execute
+
+ expect(commits.length).to eq(1)
+ expect(commits[0].id).to eq('498214de67004b1da3d820901307bed2a68a8ef6')
+ end
+
+ it 'returns commits based in after filter' do
+ params = { search: 'test text', committed_after: 1474828200 }
+ commits = described_class.new(project, merge_request, params).execute
+
+ expect(commits.length).to eq(1)
+ expect(commits[0].id).to eq('b83d6e391c22777fca1ed3012fce84f633d7fed0')
+ end
end
end
diff --git a/spec/finders/crm/organizations_finder_spec.rb b/spec/finders/crm/organizations_finder_spec.rb
index f227fcd3748..c89ac3b1cb5 100644
--- a/spec/finders/crm/organizations_finder_spec.rb
+++ b/spec/finders/crm/organizations_finder_spec.rb
@@ -128,5 +128,76 @@ RSpec.describe Crm::OrganizationsFinder do
end
end
end
+
+ context 'when sorting' do
+ let_it_be(:group) { create(:group, :crm_enabled) }
+
+ let_it_be(:sort_test_a) do
+ create(
+ :organization,
+ group: group,
+ name: "ABC",
+ description: "1"
+ )
+ end
+
+ let_it_be(:sort_test_b) do
+ create(
+ :organization,
+ group: group,
+ name: "DEF",
+ description: "2",
+ default_rate: 10
+ )
+ end
+
+ let_it_be(:sort_test_c) do
+ create(
+ :organization,
+ group: group,
+ name: "GHI",
+ default_rate: 20
+ )
+ end
+
+ before do
+ group.add_developer(user)
+ end
+
+ it 'returns the organiztions sorted by name in ascending order' do
+ finder = described_class.new(user, group: group, sort: { field: 'name', direction: :asc })
+
+ expect(finder.execute).to eq([sort_test_a, sort_test_b, sort_test_c])
+ end
+
+ it 'returns the organizations sorted by description in descending order' do
+ finder = described_class.new(user, group: group, sort: { field: 'description', direction: :desc })
+
+ expect(finder.execute).to eq([sort_test_b, sort_test_a, sort_test_c])
+ end
+
+ it 'returns the contacts sorted by default_rate in ascending order' do
+ finder = described_class.new(user, group: group, sort: { field: 'default_rate', direction: :asc })
+
+ expect(finder.execute).to eq([sort_test_b, sort_test_c, sort_test_a])
+ end
+ end
+ end
+
+ describe '.counts_by_state' do
+ let_it_be(:group) { create(:group, :crm_enabled) }
+ let_it_be(:active_organizations) { create_list(:organization, 3, group: group, state: :active) }
+ let_it_be(:inactive_organizations) { create_list(:organization, 2, group: group, state: :inactive) }
+
+ before do
+ group.add_developer(user)
+ end
+
+ it 'returns correct counts' do
+ counts = described_class.counts_by_state(user, group: group)
+
+ expect(counts["active"]).to eq(3)
+ expect(counts["inactive"]).to eq(2)
+ end
end
end
diff --git a/spec/finders/database/batched_background_migrations_finder_spec.rb b/spec/finders/database/batched_background_migrations_finder_spec.rb
new file mode 100644
index 00000000000..bd88be72fa5
--- /dev/null
+++ b/spec/finders/database/batched_background_migrations_finder_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Database::BatchedBackgroundMigrationsFinder do
+ let!(:migration_1) { create(:batched_background_migration, created_at: Time.now - 2) }
+ let!(:migration_2) { create(:batched_background_migration, created_at: Time.now - 1) }
+ let!(:migration_3) { create(:batched_background_migration, created_at: Time.now - 3) }
+
+ let(:finder) { described_class.new(connection: connection) }
+
+ describe '#execute' do
+ let(:connection) { ApplicationRecord.connection }
+
+ subject { finder.execute }
+
+ it 'returns migrations order by created_at (DESC)' do
+ is_expected.to eq([migration_2, migration_1, migration_3])
+ end
+
+ it 'limits the number of returned migrations' do
+ stub_const('Database::BatchedBackgroundMigrationsFinder::RETURNED_MIGRATIONS', 2)
+
+ is_expected.to eq([migration_2, migration_1])
+ end
+ end
+end
diff --git a/spec/finders/deploy_tokens/tokens_finder_spec.rb b/spec/finders/deploy_tokens/tokens_finder_spec.rb
index 7f19c5bf11b..4c72a2ced7c 100644
--- a/spec/finders/deploy_tokens/tokens_finder_spec.rb
+++ b/spec/finders/deploy_tokens/tokens_finder_spec.rb
@@ -30,24 +30,26 @@ RSpec.describe DeployTokens::TokensFinder do
it 'returns all deploy tokens' do
expect(subject.size).to eq(6)
- is_expected.to match_array([
- project_deploy_token,
- revoked_project_deploy_token,
- expired_project_deploy_token,
- group_deploy_token,
- revoked_group_deploy_token,
- expired_group_deploy_token
- ])
+ is_expected.to match_array(
+ [
+ project_deploy_token,
+ revoked_project_deploy_token,
+ expired_project_deploy_token,
+ group_deploy_token,
+ revoked_group_deploy_token,
+ expired_group_deploy_token
+ ])
end
context 'and active filter is applied' do
let(:params) { { active: true } }
it 'returns only active tokens' do
- is_expected.to match_array([
- project_deploy_token,
- group_deploy_token
- ])
+ is_expected.to match_array(
+ [
+ project_deploy_token,
+ group_deploy_token
+ ])
end
end
@@ -68,11 +70,12 @@ RSpec.describe DeployTokens::TokensFinder do
end
it 'returns all deploy tokens for the project' do
- is_expected.to match_array([
- project_deploy_token,
- revoked_project_deploy_token,
- expired_project_deploy_token
- ])
+ is_expected.to match_array(
+ [
+ project_deploy_token,
+ revoked_project_deploy_token,
+ expired_project_deploy_token
+ ])
end
context 'and active filter is applied' do
@@ -100,11 +103,12 @@ RSpec.describe DeployTokens::TokensFinder do
end
it 'returns all deploy tokens for the group' do
- is_expected.to match_array([
- group_deploy_token,
- revoked_group_deploy_token,
- expired_group_deploy_token
- ])
+ is_expected.to match_array(
+ [
+ group_deploy_token,
+ revoked_group_deploy_token,
+ expired_group_deploy_token
+ ])
end
context 'and active filter is applied' do
diff --git a/spec/finders/deployments_finder_spec.rb b/spec/finders/deployments_finder_spec.rb
index 51c293bcfd1..efb739c3d2f 100644
--- a/spec/finders/deployments_finder_spec.rb
+++ b/spec/finders/deployments_finder_spec.rb
@@ -32,7 +32,17 @@ RSpec.describe DeploymentsFinder do
it 'raises an error' do
expect { subject }.to raise_error(
described_class::InefficientQueryError,
- '`finished_at` filter and `finished_at` sorting must be paired')
+ '`finished_at` filter requires `finished_at` sort.')
+ end
+ end
+
+ context 'when running status filter and finished_at sorting' do
+ let(:params) { { status: :running, order_by: :finished_at } }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(
+ described_class::InefficientQueryError,
+ '`finished_at` sort requires `finished_at` filter or a filter with at least one of the finished statuses.')
end
end
@@ -52,7 +62,17 @@ RSpec.describe DeploymentsFinder do
it 'raises an error' do
expect { subject }.to raise_error(
described_class::InefficientQueryError,
- '`environment` filter must be combined with `project` scope.')
+ '`environment` name filter must be combined with `project` scope.')
+ end
+ end
+
+ context 'when status filter with mixed finished and upcoming statuses' do
+ let(:params) { { status: [:success, :running] } }
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(
+ described_class::InefficientQueryError,
+ 'finished statuses and upcoming statuses must be separately queried.')
end
end
end
@@ -103,6 +123,24 @@ RSpec.describe DeploymentsFinder do
end
end
+ context 'when the environment ID is specified' do
+ let!(:environment1) { create(:environment, project: project) }
+ let!(:environment2) { create(:environment, project: project) }
+ let!(:deployment1) do
+ create(:deployment, project: project, environment: environment1)
+ end
+
+ let!(:deployment2) do
+ create(:deployment, project: project, environment: environment2)
+ end
+
+ let(:params) { { environment: environment1.id } }
+
+ it 'returns deployments for the given environment' do
+ is_expected.to match_array([deployment1])
+ end
+ end
+
context 'when the deployment status is specified' do
let!(:deployment1) { create(:deployment, :success, project: project) }
let!(:deployment2) { create(:deployment, :failed, project: project) }
diff --git a/spec/finders/design_management/versions_finder_spec.rb b/spec/finders/design_management/versions_finder_spec.rb
index 0d606ef46f1..7dafdcfda97 100644
--- a/spec/finders/design_management/versions_finder_spec.rb
+++ b/spec/finders/design_management/versions_finder_spec.rb
@@ -71,13 +71,13 @@ RSpec.describe DesignManagement::VersionsFinder do
describe 'returning versions earlier or equal to a version' do
context 'when argument is the first version' do
- let(:params) { { earlier_or_equal_to: version_1 }}
+ let(:params) { { earlier_or_equal_to: version_1 } }
it { is_expected.to eq([version_1]) }
end
context 'when argument is the second version' do
- let(:params) { { earlier_or_equal_to: version_2 }}
+ let(:params) { { earlier_or_equal_to: version_2 } }
it { is_expected.to contain_exactly(version_1, version_2) }
end
diff --git a/spec/finders/environments/environments_finder_spec.rb b/spec/finders/environments/environments_finder_spec.rb
index 71d10ceb5d3..04fbd4067b4 100644
--- a/spec/finders/environments/environments_finder_spec.rb
+++ b/spec/finders/environments/environments_finder_spec.rb
@@ -6,8 +6,8 @@ RSpec.describe Environments::EnvironmentsFinder do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.creator }
let_it_be(:environment) { create(:environment, :available, project: project) }
- let_it_be(:environment_stopped) { create(:environment, :stopped, name: 'test2', project: project) }
- let_it_be(:environment_available) { create(:environment, :available, name: 'test3', project: project) }
+ let_it_be(:environment_stopped) { create(:environment, :stopped, name: 'test/test2', project: project) }
+ let_it_be(:environment_available) { create(:environment, :available, name: 'test/test3', project: project) }
before do
project.add_maintainer(user)
@@ -65,5 +65,11 @@ RSpec.describe Environments::EnvironmentsFinder do
expect(result).to contain_exactly(environment_available)
end
end
+
+ it 'filters environments by type' do
+ result = described_class.new(project, user, type: 'test').execute
+
+ expect(result).to contain_exactly(environment_stopped, environment_available)
+ end
end
end
diff --git a/spec/finders/group_descendants_finder_spec.rb b/spec/finders/group_descendants_finder_spec.rb
index 5c5db874e85..2a9e887450c 100644
--- a/spec/finders/group_descendants_finder_spec.rb
+++ b/spec/finders/group_descendants_finder_spec.rb
@@ -131,7 +131,7 @@ RSpec.describe GroupDescendantsFinder do
project = create(:project, namespace: group)
other_project = create(:project)
other_project.project_group_links.create!(group: group,
- group_access: Gitlab::Access::MAINTAINER)
+ group_access: Gitlab::Access::MAINTAINER)
expect(finder.execute).to contain_exactly(project)
end
@@ -231,8 +231,8 @@ RSpec.describe GroupDescendantsFinder do
other_subgroup.add_developer(other_user)
finder = described_class.new(current_user: other_user,
- parent_group: group,
- params: params)
+ parent_group: group,
+ params: params)
expect(finder.execute).to contain_exactly(other_subgroup, public_subgroup, other_subsubgroup)
end
diff --git a/spec/finders/group_members_finder_spec.rb b/spec/finders/group_members_finder_spec.rb
index 00aa14209a2..0d1b58e2636 100644
--- a/spec/finders/group_members_finder_spec.rb
+++ b/spec/finders/group_members_finder_spec.rb
@@ -21,10 +21,10 @@ RSpec.describe GroupMembersFinder, '#execute' do
let(:groups) do
{
- group: group,
- sub_group: sub_group,
- sub_sub_group: sub_sub_group,
- public_shared_group: public_shared_group,
+ group: group,
+ sub_group: sub_group,
+ sub_sub_group: sub_sub_group,
+ public_shared_group: public_shared_group,
private_shared_group: private_shared_group
}
end
@@ -32,26 +32,27 @@ RSpec.describe GroupMembersFinder, '#execute' do
context 'relations' do
let!(:members) do
{
- user1_sub_sub_group: create(:group_member, :maintainer, group: sub_sub_group, user: user1),
- user1_sub_group: create(:group_member, :developer, group: sub_group, user: user1),
- user1_group: create(:group_member, :reporter, group: group, user: user1),
- user1_public_shared_group: create(:group_member, :maintainer, group: public_shared_group, user: user1),
+ user1_sub_sub_group: create(:group_member, :maintainer, group: sub_sub_group, user: user1),
+ user1_sub_group: create(:group_member, :developer, group: sub_group, user: user1),
+ user1_group: create(:group_member, :reporter, group: group, user: user1),
+ user1_public_shared_group: create(:group_member, :maintainer, group: public_shared_group, user: user1),
user1_private_shared_group: create(:group_member, :maintainer, group: private_shared_group, user: user1),
- user2_sub_sub_group: create(:group_member, :reporter, group: sub_sub_group, user: user2),
- user2_sub_group: create(:group_member, :developer, group: sub_group, user: user2),
- user2_group: create(:group_member, :maintainer, group: group, user: user2),
- user2_public_shared_group: create(:group_member, :developer, group: public_shared_group, user: user2),
- user2_private_shared_group: create(:group_member, :developer, group: private_shared_group, user: user2),
- user3_sub_sub_group: create(:group_member, :developer, group: sub_sub_group, user: user3, expires_at: 1.day.from_now),
- user3_sub_group: create(:group_member, :developer, group: sub_group, user: user3, expires_at: 2.days.from_now),
- user3_group: create(:group_member, :reporter, group: group, user: user3),
- user3_public_shared_group: create(:group_member, :reporter, group: public_shared_group, user: user3),
- user3_private_shared_group: create(:group_member, :reporter, group: private_shared_group, user: user3),
- user4_sub_sub_group: create(:group_member, :reporter, group: sub_sub_group, user: user4),
- user4_sub_group: create(:group_member, :developer, group: sub_group, user: user4, expires_at: 1.day.from_now),
- user4_group: create(:group_member, :developer, group: group, user: user4, expires_at: 2.days.from_now),
- user4_public_shared_group: create(:group_member, :developer, group: public_shared_group, user: user4),
- user4_private_shared_group: create(:group_member, :developer, group: private_shared_group, user: user4)
+ user2_sub_sub_group: create(:group_member, :reporter, group: sub_sub_group, user: user2),
+ user2_sub_group: create(:group_member, :developer, group: sub_group, user: user2),
+ user2_group: create(:group_member, :maintainer, group: group, user: user2),
+ user2_public_shared_group: create(:group_member, :developer, group: public_shared_group, user: user2),
+ user2_private_shared_group: create(:group_member, :developer, group: private_shared_group, user: user2),
+ user3_sub_sub_group: create(:group_member, :developer, group: sub_sub_group, user: user3, expires_at: 1.day.from_now),
+ user3_sub_group: create(:group_member, :developer, group: sub_group, user: user3, expires_at: 2.days.from_now),
+ user3_group: create(:group_member, :reporter, group: group, user: user3),
+ user3_public_shared_group: create(:group_member, :reporter, group: public_shared_group, user: user3),
+ user3_private_shared_group: create(:group_member, :reporter, group: private_shared_group, user: user3),
+ user4_sub_sub_group: create(:group_member, :reporter, group: sub_sub_group, user: user4),
+ user4_sub_group: create(:group_member, :developer, group: sub_group, user: user4, expires_at: 1.day.from_now),
+ user4_group: create(:group_member, :developer, group: group, user: user4, expires_at: 2.days.from_now),
+ user4_public_shared_group: create(:group_member, :developer, group: public_shared_group, user: user4),
+ user4_private_shared_group: create(:group_member, :developer, group: private_shared_group, user: user4),
+ user5_private_shared_group: create(:group_member, :developer, group: private_shared_group, user: user5)
}
end
@@ -76,15 +77,15 @@ RSpec.describe GroupMembersFinder, '#execute' do
[:direct] | :sub_group | [:user1_sub_group, :user2_sub_group, :user3_sub_group, :user4_sub_group]
[:inherited] | :sub_group | [:user1_group, :user2_group, :user3_group, :user4_group]
[:descendants] | :sub_group | [:user1_sub_sub_group, :user2_sub_sub_group, :user3_sub_sub_group, :user4_sub_sub_group]
- [:shared_from_groups] | :sub_group | []
- [:direct, :inherited, :descendants, :shared_from_groups] | :sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_group]
+ [:shared_from_groups] | :sub_group | [:user1_public_shared_group, :user2_public_shared_group, :user3_public_shared_group, :user4_public_shared_group]
+ [:direct, :inherited, :descendants, :shared_from_groups] | :sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_public_shared_group]
[] | :sub_sub_group | []
GroupMembersFinder::DEFAULT_RELATIONS | :sub_sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_group]
[:direct] | :sub_sub_group | [:user1_sub_sub_group, :user2_sub_sub_group, :user3_sub_sub_group, :user4_sub_sub_group]
[:inherited] | :sub_sub_group | [:user1_sub_group, :user2_group, :user3_sub_group, :user4_group]
[:descendants] | :sub_sub_group | []
- [:shared_from_groups] | :sub_sub_group | []
- [:direct, :inherited, :descendants, :shared_from_groups] | :sub_sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_group]
+ [:shared_from_groups] | :sub_sub_group | [:user1_public_shared_group, :user2_public_shared_group, :user3_public_shared_group, :user4_public_shared_group]
+ [:direct, :inherited, :descendants, :shared_from_groups] | :sub_sub_group | [:user1_sub_sub_group, :user2_group, :user3_sub_group, :user4_public_shared_group]
end
with_them do
diff --git a/spec/finders/groups/accepting_group_transfers_finder_spec.rb b/spec/finders/groups/accepting_group_transfers_finder_spec.rb
new file mode 100644
index 00000000000..1a6c6f9243b
--- /dev/null
+++ b/spec/finders/groups/accepting_group_transfers_finder_spec.rb
@@ -0,0 +1,135 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Groups::AcceptingGroupTransfersFinder do
+ let_it_be(:current_user) { create(:user) }
+
+ let_it_be(:great_grandparent_group) do
+ create(:group, name: 'great grandparent group', path: 'great-grandparent-group')
+ end
+
+ let_it_be(:grandparent_group) { create(:group, parent: great_grandparent_group) }
+ let_it_be(:parent_group) { create(:group, parent: grandparent_group) }
+ let_it_be(:child_group) { create(:group, parent: parent_group) }
+ let_it_be(:grandchild_group) { create(:group, parent: child_group) }
+ let_it_be(:group_where_user_has_owner_access) do
+ create(:group, name: 'owner access group', path: 'owner-access-group').tap do |group|
+ group.add_owner(current_user)
+ end
+ end
+
+ let_it_be(:subgroup_of_group_where_user_has_owner_access) do
+ create(:group, parent: group_where_user_has_owner_access)
+ end
+
+ let_it_be(:group_where_user_has_developer_access) do
+ create(:group).tap do |group|
+ group.add_developer(current_user)
+ end
+ end
+
+ let_it_be(:shared_with_group_where_direct_owner_as_guest) { create(:group) }
+ let_it_be(:shared_with_group_where_direct_owner_as_owner) { create(:group) }
+ let_it_be(:subgroup_of_shared_with_group_where_direct_owner_as_owner) do
+ create(:group, parent: shared_with_group_where_direct_owner_as_owner)
+ end
+
+ let(:params) { {} }
+
+ describe '#execute' do
+ before_all do
+ create(:group_group_link, :owner,
+ shared_with_group: group_where_user_has_owner_access,
+ shared_group: shared_with_group_where_direct_owner_as_owner
+ )
+
+ create(:group_group_link, :guest,
+ shared_with_group: group_where_user_has_owner_access,
+ shared_group: shared_with_group_where_direct_owner_as_guest
+ )
+ end
+
+ let(:group_to_be_transferred) { parent_group }
+
+ subject(:result) do
+ described_class.new(current_user, group_to_be_transferred, params).execute
+ end
+
+ context 'when the user does not have the rights to transfer the group' do
+ before do
+ group_to_be_transferred.root_ancestor.add_developer(current_user)
+ end
+
+ it 'returns empty result' do
+ expect(result).to be_empty
+ end
+ end
+
+ context 'when the user has the rights to transfer the group' do
+ before do
+ group_to_be_transferred.root_ancestor.add_owner(current_user)
+ end
+
+ it 'does not return empty result' do
+ expect(result).not_to be_empty
+ end
+
+ it 'excludes the descendants of the group to be transferred' do
+ expect(result).not_to include(child_group, grandchild_group)
+ end
+
+ it 'excludes the immediate parent of the group to be transferred' do
+ expect(result).not_to include(grandparent_group)
+ end
+
+ it 'excludes the groups where the user does not have OWNER access' do
+ expect(result).not_to include(group_where_user_has_developer_access)
+ end
+
+ it 'excludes the groups arising from group shares where the user does not have OWNER access' do
+ expect(result).not_to include(shared_with_group_where_direct_owner_as_guest)
+ end
+
+ it 'includes ancestors, except immediate parent of the group to be transferred' do
+ expect(result).to include(great_grandparent_group)
+ end
+
+ it 'includes the other groups where the user has OWNER access' do
+ expect(result).to include(group_where_user_has_owner_access)
+ end
+
+ it 'includes the other groups where the user has OWNER access through inherited membership' do
+ expect(result).to include(subgroup_of_group_where_user_has_owner_access)
+ end
+
+ it 'includes the groups where the user has OWNER access through group shares' do
+ expect(result).to include(
+ shared_with_group_where_direct_owner_as_owner,
+ subgroup_of_shared_with_group_where_direct_owner_as_owner
+ )
+ end
+
+ context 'on searching with a specific term' do
+ let(:params) { { search: 'great grandparent group' } }
+
+ it 'includes only the groups where the term matches the group name or path' do
+ expect(result).to contain_exactly(great_grandparent_group)
+ end
+ end
+
+ context 'when the feature flag `include_groups_from_group_shares_in_group_transfer_locations` is turned off' do
+ before do
+ stub_feature_flags(include_groups_from_group_shares_in_group_transfer_locations: false)
+ end
+
+ it 'excludes the groups where the user has OWNER access through group shares' do
+ expect(result).not_to include(
+ shared_with_group_where_direct_owner_as_owner,
+ subgroup_of_shared_with_group_where_direct_owner_as_owner
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/spec/finders/groups/projects_requiring_authorizations_refresh/on_direct_membership_finder_spec.rb b/spec/finders/groups/projects_requiring_authorizations_refresh/on_direct_membership_finder_spec.rb
index 8cdfa13ba3a..985b132ee8b 100644
--- a/spec/finders/groups/projects_requiring_authorizations_refresh/on_direct_membership_finder_spec.rb
+++ b/spec/finders/groups/projects_requiring_authorizations_refresh/on_direct_membership_finder_spec.rb
@@ -54,14 +54,14 @@ RSpec.describe Groups::ProjectsRequiringAuthorizationsRefresh::OnDirectMembershi
it 'includes only the expected projects' do
expected_projects = Project.id_in(
[
- project_b_subgroup_1, # direct member of Group B gets access to this project due to group hierarchy
- project_b_subgroup_2, # direct member of Group B gets access to this project due to group hierarchy
- project_c, # direct member of Group B gets access to this project via project-group share
- project_a_subgroup_1, # direct member of Group B gets access to this project via group share
- project_a_subgroup_2, # direct member of Group B gets access to this project via group share
+ project_b_subgroup_1, # direct member of Group B gets access to this project due to group hierarchy
+ project_b_subgroup_2, # direct member of Group B gets access to this project due to group hierarchy
+ project_c, # direct member of Group B gets access to this project via project-group share
+ project_a_subgroup_1, # direct member of Group B gets access to this project via group share
+ project_a_subgroup_2, # direct member of Group B gets access to this project via group share
- # direct member of Group B gets access to any projects shared with groups within its shared groups.
- project_x_subgroup_1
+ # direct member of Group B gets access to any projects shared with groups within its shared groups.
+ project_x_subgroup_1
]
)
# project_c_subgroup_1 is not included in the list because only 'direct' members of
diff --git a/spec/finders/groups/projects_requiring_authorizations_refresh/on_transfer_finder_spec.rb b/spec/finders/groups/projects_requiring_authorizations_refresh/on_transfer_finder_spec.rb
index 103cef44c94..1a0e8c5b9e6 100644
--- a/spec/finders/groups/projects_requiring_authorizations_refresh/on_transfer_finder_spec.rb
+++ b/spec/finders/groups/projects_requiring_authorizations_refresh/on_transfer_finder_spec.rb
@@ -46,9 +46,9 @@ RSpec.describe Groups::ProjectsRequiringAuthorizationsRefresh::OnTransferFinder
it 'includes only the expected projects' do
expected_projects = Project.id_in(
[
- project_b_subgroup_1,
- project_b_subgroup_2,
- project_c
+ project_b_subgroup_1,
+ project_b_subgroup_2,
+ project_c
]
)
diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb
index a4cbee6a124..123df418f8d 100644
--- a/spec/finders/groups_finder_spec.rb
+++ b/spec/finders/groups_finder_spec.rb
@@ -261,6 +261,108 @@ RSpec.describe GroupsFinder do
end
end
end
+
+ context 'with include_ancestors' do
+ let_it_be(:user) { create(:user) }
+
+ let_it_be(:parent_group) { create(:group, :public) }
+ let_it_be(:public_subgroup) { create(:group, :public, parent: parent_group) }
+ let_it_be(:public_subgroup2) { create(:group, :public, parent: parent_group) }
+ let_it_be(:private_subgroup1) { create(:group, :private, parent: parent_group) }
+ let_it_be(:internal_sub_subgroup) { create(:group, :internal, parent: public_subgroup) }
+ let_it_be(:public_sub_subgroup) { create(:group, :public, parent: public_subgroup) }
+ let_it_be(:private_subgroup2) { create(:group, :private, parent: parent_group) }
+ let_it_be(:private_sub_subgroup) { create(:group, :private, parent: private_subgroup2) }
+ let_it_be(:private_sub_sub_subgroup) { create(:group, :private, parent: private_sub_subgroup) }
+
+ context 'if include_ancestors is true' do
+ let(:params) { { include_ancestors: true } }
+
+ it 'returns ancestors of user groups' do
+ private_sub_subgroup.add_developer(user)
+
+ expect(described_class.new(user, params).execute).to contain_exactly(
+ parent_group,
+ public_subgroup,
+ public_subgroup2,
+ internal_sub_subgroup,
+ public_sub_subgroup,
+ private_subgroup2,
+ private_sub_subgroup,
+ private_sub_sub_subgroup
+ )
+ end
+
+ it 'returns subgroup if user is member of project of subgroup' do
+ project = create(:project, :private, namespace: private_sub_subgroup)
+ project.add_developer(user)
+
+ expect(described_class.new(user, params).execute).to contain_exactly(
+ parent_group,
+ public_subgroup,
+ public_subgroup2,
+ internal_sub_subgroup,
+ public_sub_subgroup,
+ private_subgroup2,
+ private_sub_subgroup
+ )
+ end
+
+ it 'returns only groups related to user groups if all_available is false' do
+ params[:all_available] = false
+ private_sub_subgroup.add_developer(user)
+
+ expect(described_class.new(user, params).execute).to contain_exactly(
+ parent_group,
+ private_subgroup2,
+ private_sub_subgroup,
+ private_sub_sub_subgroup
+ )
+ end
+ end
+
+ context 'if include_ancestors is false' do
+ let(:params) { { include_ancestors: false } }
+
+ it 'does not return private ancestors of user groups' do
+ private_sub_subgroup.add_developer(user)
+
+ expect(described_class.new(user, params).execute).to contain_exactly(
+ parent_group,
+ public_subgroup,
+ public_subgroup2,
+ internal_sub_subgroup,
+ public_sub_subgroup,
+ private_sub_subgroup,
+ private_sub_sub_subgroup
+ )
+ end
+
+ it "returns project's parent group if user is member of project" do
+ project = create(:project, :private, namespace: private_sub_subgroup)
+ project.add_developer(user)
+
+ expect(described_class.new(user, params).execute).to contain_exactly(
+ parent_group,
+ public_subgroup,
+ public_subgroup2,
+ internal_sub_subgroup,
+ public_sub_subgroup,
+ private_sub_subgroup
+ )
+ end
+
+ it 'returns only user groups and their descendants if all_available is false' do
+ params[:all_available] = false
+ private_sub_subgroup.add_developer(user)
+
+ expect(described_class.new(user, params).execute).to contain_exactly(
+ private_sub_subgroup,
+ private_sub_sub_subgroup
+ )
+ end
+ end
+ end
end
describe '#execute' do
diff --git a/spec/finders/merge_requests_finder/params_spec.rb b/spec/finders/merge_requests_finder/params_spec.rb
deleted file mode 100644
index 8c285972f48..00000000000
--- a/spec/finders/merge_requests_finder/params_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe MergeRequestsFinder::Params do
- let(:user) { create(:user) }
-
- subject { described_class.new(params, user, MergeRequest) }
-
- describe 'attention' do
- context 'attention param exists' do
- let(:params) { { attention: user.username } }
-
- it { expect(subject.attention).to eq(user) }
- end
-
- context 'attention param does not exist' do
- let(:params) { { attention: nil } }
-
- it { expect(subject.attention).to eq(nil) }
- end
- end
-end
diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb
index 96466e99105..deeca6132e0 100644
--- a/spec/finders/merge_requests_finder_spec.rb
+++ b/spec/finders/merge_requests_finder_spec.rb
@@ -408,25 +408,6 @@ RSpec.describe MergeRequestsFinder do
end
end
- context 'attention' do
- subject { described_class.new(user, params).execute }
-
- before do
- reviewer = merge_request1.find_reviewer(user2)
- reviewer.update!(state: :reviewed)
-
- merge_request2.find_reviewer(user2).update!(state: :attention_requested)
- merge_request3.find_assignee(user2).update!(state: :attention_requested)
- end
-
- context 'by username' do
- let(:params) { { attention: user2.username } }
- let(:expected_mr) { [merge_request2, merge_request3] }
-
- it { is_expected.to contain_exactly(*expected_mr) }
- end
- end
-
context 'reviewer filtering' do
subject { described_class.new(user, params).execute }
@@ -512,7 +493,7 @@ RSpec.describe MergeRequestsFinder do
end
end
- context 'filtering by approved by' do
+ context 'filtering by approved by username' do
let(:params) { { approved_by_usernames: user2.username } }
before do
@@ -525,6 +506,16 @@ RSpec.describe MergeRequestsFinder do
expect(merge_requests).to contain_exactly(merge_request3)
end
+ context 'with sorting by milestone' do
+ let(:params) { { approved_by_usernames: user2.username, sort: 'milestone' } }
+
+ it 'returns merge requests approved by that user' do
+ merge_requests = described_class.new(user, params).execute
+
+ expect(merge_requests).to contain_exactly(merge_request3)
+ end
+ end
+
context 'not filter' do
let(:params) { { not: { approved_by_usernames: user2.username } } }
@@ -550,6 +541,30 @@ RSpec.describe MergeRequestsFinder do
end
end
+ context 'filtering by approved by user ID' do
+ let(:params) { { approved_by_ids: user2.id } }
+
+ before do
+ create(:approval, merge_request: merge_request3, user: user2)
+ end
+
+ it 'returns merge requests approved by that user' do
+ merge_requests = described_class.new(user, params).execute
+
+ expect(merge_requests).to contain_exactly(merge_request3)
+ end
+
+ context 'with sorting by milestone' do
+ let(:params) { { approved_by_usernames: user2.username, sort: 'milestone' } }
+
+ it 'returns merge requests approved by that user' do
+ merge_requests = described_class.new(user, params).execute
+
+ expect(merge_requests).to contain_exactly(merge_request3)
+ end
+ end
+ end
+
context 'filtering by created_at/updated_at' do
let(:new_project) { create(:project, forked_from_project: project1) }
@@ -751,7 +766,8 @@ RSpec.describe MergeRequestsFinder do
release_tag: 'none',
label_names: 'none',
my_reaction_emoji: 'none',
- draft: 'no'
+ draft: 'no',
+ sort: 'milestone'
}
merge_requests = described_class.new(user, params).execute
diff --git a/spec/finders/milestones_finder_spec.rb b/spec/finders/milestones_finder_spec.rb
index 8b26599cbfa..8dd83df3a28 100644
--- a/spec/finders/milestones_finder_spec.rb
+++ b/spec/finders/milestones_finder_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe MilestonesFinder do
end
context 'milestones for groups and project' do
- let(:extra_params) {{}}
+ let(:extra_params) { {} }
let(:result) do
described_class.new({ project_ids: [project_1.id, project_2.id], group_ids: group.id, state: 'all' }.merge(extra_params)).execute
end
diff --git a/spec/finders/packages/group_packages_finder_spec.rb b/spec/finders/packages/group_packages_finder_spec.rb
index 90a8cd3c57f..f78a356b13d 100644
--- a/spec/finders/packages/group_packages_finder_spec.rb
+++ b/spec/finders/packages/group_packages_finder_spec.rb
@@ -217,7 +217,7 @@ RSpec.describe Packages::GroupPackagesFinder do
context 'group is nil' do
subject { described_class.new(user, nil).execute }
- it { is_expected.to be_empty}
+ it { is_expected.to be_empty }
end
context 'package type is nil' do
@@ -225,7 +225,7 @@ RSpec.describe Packages::GroupPackagesFinder do
subject { described_class.new(user, group, package_type: nil).execute }
- it { is_expected.to match_array([package1])}
+ it { is_expected.to match_array([package1]) }
end
context 'with invalid package_type' do
diff --git a/spec/finders/packages/npm/package_finder_spec.rb b/spec/finders/packages/npm/package_finder_spec.rb
index 7fabb3eed86..8c9149a5a2d 100644
--- a/spec/finders/packages/npm/package_finder_spec.rb
+++ b/spec/finders/packages/npm/package_finder_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe ::Packages::Npm::PackageFinder do
- let_it_be_with_reload(:project) { create(:project)}
+ let_it_be_with_reload(:project) { create(:project) }
let_it_be_with_refind(:package) { create(:npm_package, project: project) }
let(:project) { package.project }
diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb
index 3bef4d85b33..1fa2a975ec3 100644
--- a/spec/finders/projects_finder_spec.rb
+++ b/spec/finders/projects_finder_spec.rb
@@ -62,7 +62,7 @@ RSpec.describe ProjectsFinder do
describe 'with id_after' do
context 'only returns projects with a project id greater than given' do
- let(:params) { { id_after: internal_project.id }}
+ let(:params) { { id_after: internal_project.id } }
it { is_expected.to eq([public_project]) }
end
@@ -70,7 +70,7 @@ RSpec.describe ProjectsFinder do
describe 'with id_before' do
context 'only returns projects with a project id less than given' do
- let(:params) { { id_before: public_project.id }}
+ let(:params) { { id_before: public_project.id } }
it { is_expected.to eq([internal_project]) }
end
@@ -79,7 +79,7 @@ RSpec.describe ProjectsFinder do
describe 'with both id_before and id_after' do
context 'only returns projects with a project id less than given' do
let!(:projects) { create_list(:project, 5, :public) }
- let(:params) { { id_after: projects.first.id, id_before: projects.last.id }}
+ let(:params) { { id_after: projects.first.id, id_before: projects.last.id } }
it { is_expected.to contain_exactly(*projects[1..-2]) }
end
@@ -89,7 +89,7 @@ RSpec.describe ProjectsFinder do
context 'only returns projects with a project id less than given and matching search' do
subject { finder.execute.joins(:route) }
- let(:params) { { id_before: public_project.id }}
+ let(:params) { { id_before: public_project.id } }
it { is_expected.to eq([internal_project]) }
end
@@ -97,7 +97,7 @@ RSpec.describe ProjectsFinder do
context 'only returns projects with a project id greater than given and matching search' do
subject { finder.execute.joins(:route) }
- let(:params) { { id_after: internal_project.id }}
+ let(:params) { { id_after: internal_project.id } }
it { is_expected.to eq([public_project]) }
end
diff --git a/spec/finders/template_finder_spec.rb b/spec/finders/template_finder_spec.rb
index 8e2426e697b..21fea7863ff 100644
--- a/spec/finders/template_finder_spec.rb
+++ b/spec/finders/template_finder_spec.rb
@@ -7,10 +7,10 @@ RSpec.describe TemplateFinder do
let_it_be(:template_files) do
{
- "Dockerfile/project_dockerfiles_template.dockerfile" => "project_dockerfiles_template content",
- "gitignore/project_gitignores_template.gitignore" => "project_gitignores_template content",
- "gitlab-ci/project_gitlab_ci_ymls_template.yml" => "project_gitlab_ci_ymls_template content",
- ".gitlab/issue_templates/project_issues_template.md" => "project_issues_template content",
+ "Dockerfile/project_dockerfiles_template.dockerfile" => "project_dockerfiles_template content",
+ "gitignore/project_gitignores_template.gitignore" => "project_gitignores_template content",
+ "gitlab-ci/project_gitlab_ci_ymls_template.yml" => "project_gitlab_ci_ymls_template content",
+ ".gitlab/issue_templates/project_issues_template.md" => "project_issues_template content",
".gitlab/merge_request_templates/project_merge_requests_template.md" => "project_merge_requests_template content"
}
end
diff --git a/spec/fixtures/api/schemas/entities/merge_request_noteable.json b/spec/fixtures/api/schemas/entities/merge_request_noteable.json
index 4ef19ed32c2..4b790a2c34b 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_noteable.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_noteable.json
@@ -12,6 +12,8 @@
"state": { "type": "string" },
"source_branch": { "type": "string" },
"target_branch": { "type": "string" },
+ "source_branch_path": { "type": "string" },
+ "target_branch_path": { "type": "string" },
"diff_head_sha": { "type": "string" },
"create_note_path": { "type": ["string", "null"] },
"preview_note_path": { "type": ["string", "null"] },
@@ -22,11 +24,13 @@
"type": "object",
"required": [
"can_create_note",
- "can_update"
+ "can_update",
+ "can_approve"
],
"properties": {
"can_create_note": { "type": "boolean" },
- "can_update": { "type": "boolean" }
+ "can_update": { "type": "boolean" },
+ "can_approve": { "type": "boolean" }
},
"additionalProperties": false
},
diff --git a/spec/fixtures/api/schemas/external_validation.json b/spec/fixtures/api/schemas/external_validation.json
index 4a2538a020e..411c2ed591b 100644
--- a/spec/fixtures/api/schemas/external_validation.json
+++ b/spec/fixtures/api/schemas/external_validation.json
@@ -3,6 +3,7 @@
"required" : [
"project",
"user",
+ "credit_card",
"pipeline",
"builds",
"total_builds_count"
@@ -43,6 +44,17 @@
"sign_in_count": { "type": "integer" }
}
},
+ "credit_card": {
+ "type": "object",
+ "required": [
+ "similar_cards_count",
+ "similar_holder_names_count"
+ ],
+ "properties": {
+ "similar_cards_count": { "type": "integer" },
+ "similar_holder_names_count": { "type": "integer" }
+ }
+ },
"pipeline": {
"type": "object",
"required": [
diff --git a/spec/fixtures/api/schemas/graphql/packages/package_details.json b/spec/fixtures/api/schemas/graphql/packages/package_details.json
index 50e52a7bb87..33eb67a0280 100644
--- a/spec/fixtures/api/schemas/graphql/packages/package_details.json
+++ b/spec/fixtures/api/schemas/graphql/packages/package_details.json
@@ -13,7 +13,8 @@
"pipelines",
"versions",
"status",
- "canDestroy"
+ "canDestroy",
+ "lastDownloadedAt"
],
"properties": {
"id": {
@@ -173,6 +174,9 @@
},
"composerConfigRepositoryUrl": {
"type": "string"
+ },
+ "lastDownloadedAt": {
+ "type": ["string", "null"]
}
}
}
diff --git a/spec/fixtures/api/schemas/ml/get_experiment.json b/spec/fixtures/api/schemas/ml/get_experiment.json
new file mode 100644
index 00000000000..cf8da7f999f
--- /dev/null
+++ b/spec/fixtures/api/schemas/ml/get_experiment.json
@@ -0,0 +1,23 @@
+{
+ "type": "object",
+ "required": [
+ "experiment"
+ ],
+ "properties": {
+ "experiment": {
+ "type": "object",
+ "required" : [
+ "experiment_id",
+ "name",
+ "artifact_location",
+ "lifecycle_stage"
+ ],
+ "properties" : {
+ "experiment_id": { "type": "string" },
+ "name": { "type": "string" },
+ "artifact_location": { "type": "string" },
+ "lifecycle_stage": { "type": { "enum" : ["active", "deleted"] } }
+ }
+ }
+ }
+}
diff --git a/spec/fixtures/api/schemas/ml/run.json b/spec/fixtures/api/schemas/ml/run.json
new file mode 100644
index 00000000000..2418f44b21f
--- /dev/null
+++ b/spec/fixtures/api/schemas/ml/run.json
@@ -0,0 +1,47 @@
+{
+ "type": "object",
+ "required": [
+ "run"
+ ],
+ "properties": {
+ "run": {
+ "type": "object",
+ "required": [
+ "info",
+ "data"
+ ],
+ "properties": {
+ "info": {
+ "type": "object",
+ "required": [
+ "run_id",
+ "run_uuid",
+ "user_id",
+ "experiment_id",
+ "status",
+ "start_time",
+ "artifact_uri",
+ "lifecycle_stage"
+ ],
+ "optional": [
+ "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": "",
+ "status": { "type": { "enum" : ["RUNNING", "SCHEDULED", "FINISHED", "FAILED", "KILLED"] } },
+ "lifecycle_stage": { "type": { "enum" : ["active"] } }
+ }
+ },
+ "data": {
+ "type": "object"
+ }
+ }
+ }
+ }
+}
diff --git a/spec/fixtures/api/schemas/ml/update_run.json b/spec/fixtures/api/schemas/ml/update_run.json
new file mode 100644
index 00000000000..b429444120f
--- /dev/null
+++ b/spec/fixtures/api/schemas/ml/update_run.json
@@ -0,0 +1,35 @@
+{
+ "type": "object",
+ "required": [
+ "run_info"
+ ],
+ "properties": {
+ "run_info": {
+ "type": "object",
+ "required": [
+ "run_id",
+ "run_uuid",
+ "user_id",
+ "experiment_id",
+ "status",
+ "start_time",
+ "artifact_uri",
+ "lifecycle_stage"
+ ],
+ "optional": [
+ "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"] } }
+ }
+ }
+ }
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/job.json b/spec/fixtures/api/schemas/public_api/v4/job.json
index afed4f23017..f6b12d3a1c0 100644
--- a/spec/fixtures/api/schemas/public_api/v4/job.json
+++ b/spec/fixtures/api/schemas/public_api/v4/job.json
@@ -20,7 +20,8 @@
"artifacts",
"artifacts_expire_at",
"tag_list",
- "runner"
+ "runner",
+ "project"
],
"properties": {
"id": { "type": "integer" },
@@ -64,6 +65,9 @@
{ "type": "null" },
{ "$ref": "runner.json" }
]
+ },
+ "project": {
+ "ci_job_token_scope_enabled": { "type": "boolean" }
}
},
"additionalProperties":false
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 607e0df1886..5d0d5f63aa9 100644
--- a/spec/fixtures/api/schemas/public_api/v4/packages/package.json
+++ b/spec/fixtures/api/schemas/public_api/v4/packages/package.json
@@ -6,7 +6,9 @@
"package_type",
"status",
"_links",
- "versions"
+ "versions",
+ "created_at",
+ "last_downloaded_at"
],
"properties": {
"name": {
@@ -38,6 +40,9 @@
"created_at": {
"type": "string"
},
+ "last_downloaded_at": {
+ "type": ["string", "null"]
+ },
"versions": {
"type": "array",
"items": {
diff --git a/spec/fixtures/blockquote_fence_after.md b/spec/fixtures/blockquote_fence_after.md
index 555905bf07e..18500d94c7a 100644
--- a/spec/fixtures/blockquote_fence_after.md
+++ b/spec/fixtures/blockquote_fence_after.md
@@ -129,3 +129,27 @@ Double `>>>` inside HTML inside blockquote:
>
> Quote
+
+Requires a leading blank line
+>>>
+Not a quote
+>>>
+
+Requires a trailing blank line
+
+>>>
+Not a quote
+>>>
+Lorem
+
+Triple quoting is not our blockquote
+
+>>> foo
+>>> bar
+>>>
+> baz
+
+> boo
+>>> far
+>>>
+>>> faz
diff --git a/spec/fixtures/blockquote_fence_before.md b/spec/fixtures/blockquote_fence_before.md
index d52eec72896..895bff73404 100644
--- a/spec/fixtures/blockquote_fence_before.md
+++ b/spec/fixtures/blockquote_fence_before.md
@@ -129,3 +129,27 @@ Quote
Quote
>>>
+
+Requires a leading blank line
+>>>
+Not a quote
+>>>
+
+Requires a trailing blank line
+
+>>>
+Not a quote
+>>>
+Lorem
+
+Triple quoting is not our blockquote
+
+>>> foo
+>>> bar
+>>>
+> baz
+
+> boo
+>>> far
+>>>
+>>> faz
diff --git a/spec/fixtures/cdn/google_cloud.json b/spec/fixtures/cdn/google_cloud.json
new file mode 100644
index 00000000000..8c3f25d805f
--- /dev/null
+++ b/spec/fixtures/cdn/google_cloud.json
@@ -0,0 +1,17 @@
+{
+ "syncToken": "1661533328840",
+ "creationTime": "2022-08-26T10:02:08.840384",
+ "prefixes": [{
+ "ipv4Prefix": "34.80.0.0/15",
+ "service": "Google Cloud",
+ "scope": "asia-east1"
+ }, {
+ "ipv4Prefix": "34.137.0.0/16",
+ "service": "Google Cloud",
+ "scope": "asia-east1"
+ }, {
+ "ipv6Prefix": "2600:1900:4180::/44",
+ "service": "Google Cloud",
+ "scope": "us-west4"
+ }]
+}
diff --git a/spec/fixtures/lib/generators/gitlab/usage_metric_generator/sample_metric_test.rb b/spec/fixtures/lib/generators/gitlab/usage_metric_generator/sample_metric_test.rb
index bc7df779a58..e15336f586e 100644
--- a/spec/fixtures/lib/generators/gitlab/usage_metric_generator/sample_metric_test.rb
+++ b/spec/fixtures/lib/generators/gitlab/usage_metric_generator/sample_metric_test.rb
@@ -3,5 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountFooMetric do
- it_behaves_like 'a correct instrumented metric value', {}, 1
+ let(:expected_value) { 1 }
+
+ it_behaves_like 'a correct instrumented metric value', { time_frame: 'all', data_source: 'database' }
end
diff --git a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/100_files.zip b/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/100_files.zip
deleted file mode 100644
index 31124abc0e5..00000000000
--- a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/100_files.zip
+++ /dev/null
Binary files differ
diff --git a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/200_mb_decompressed.zip b/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/200_mb_decompressed.zip
deleted file mode 100644
index 8c56cce641a..00000000000
--- a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/200_mb_decompressed.zip
+++ /dev/null
Binary files differ
diff --git a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/multiple_files.zip b/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/multiple_files.zip
deleted file mode 100644
index 09ac4e5df51..00000000000
--- a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/multiple_files.zip
+++ /dev/null
Binary files differ
diff --git a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/single_file.zip b/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/single_file.zip
deleted file mode 100644
index 81768a9f2b3..00000000000
--- a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/single_file.zip
+++ /dev/null
Binary files differ
diff --git a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/with_directory.zip b/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/with_directory.zip
deleted file mode 100644
index 6de321ea86a..00000000000
--- a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/with_directory.zip
+++ /dev/null
Binary files differ
diff --git a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/zipbomb.zip b/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/zipbomb.zip
deleted file mode 100644
index b8cfcef9739..00000000000
--- a/spec/fixtures/lib/gitlab/ci/build/artifacts/adapters/zip_stream/zipbomb.zip
+++ /dev/null
Binary files differ
diff --git a/spec/fixtures/lib/gitlab/import_export/complex/project.json b/spec/fixtures/lib/gitlab/import_export/complex/project.json
index 5bcf6521471..f3fc69e4936 100644
--- a/spec/fixtures/lib/gitlab/import_export/complex/project.json
+++ b/spec/fixtures/lib/gitlab/import_export/complex/project.json
@@ -394,7 +394,41 @@
"id": 1,
"issue_id": 40,
"sentry_issue_identifier": 1234567891
- }
+ },
+ "resource_milestone_events": [
+ {
+ "user_id": 1,
+ "action": "add",
+ "state": "opened",
+ "created_at": "2022-08-17T13:06:53.547Z",
+ "milestone": {
+ "title": "v4.0",
+ "description": "Totam quam laborum id magnam natus eaque aspernatur.",
+ "created_at": "2016-06-14T15:02:04.590Z",
+ "updated_at": "2016-06-14T15:02:04.590Z",
+ "state": "active",
+ "iid": 5
+ }
+ }
+ ],
+ "resource_state_events": [
+ {
+ "user_id": 1,
+ "created_at": "2022-08-17T13:08:16.838Z",
+ "state": "closed",
+ "source_commit": null,
+ "close_after_error_tracking_resolve": false,
+ "close_auto_resolve_prometheus_alert": false
+ },
+ {
+ "user_id": 1,
+ "created_at": "2022-08-17T13:08:17.702Z",
+ "state": "reopened",
+ "source_commit": null,
+ "close_after_error_tracking_resolve": false,
+ "close_auto_resolve_prometheus_alert": false
+ }
+ ]
},
{
"id": 39,
@@ -3164,7 +3198,7 @@
{
"user_id": 16,
"created_at": "2020-01-09T11:21:21.235Z",
- "state": "attention_requested"
+ "state": "reviewed"
},
{
"user_id": 6,
@@ -3186,7 +3220,7 @@
{
"user_id": 16,
"created_at": "2020-01-09T11:21:21.235Z",
- "state": "attention_requested"
+ "state": "reviewed"
},
{
"user_id": 6,
@@ -3215,6 +3249,40 @@
"created_at": "2020-01-07T11:21:21.235Z",
"updated_at": "2020-01-08T11:21:21.235Z"
}
+ ],
+ "resource_milestone_events": [
+ {
+ "user_id": 1,
+ "action": "add",
+ "state": "opened",
+ "created_at": "2022-08-17T13:06:53.547Z",
+ "milestone": {
+ "title": "v4.0",
+ "description": "Totam quam laborum id magnam natus eaque aspernatur.",
+ "created_at": "2016-06-14T15:02:04.590Z",
+ "updated_at": "2016-06-14T15:02:04.590Z",
+ "state": "active",
+ "iid": 5
+ }
+ }
+ ],
+ "resource_state_events": [
+ {
+ "user_id": 1,
+ "created_at": "2022-08-17T13:08:16.838Z",
+ "state": "closed",
+ "source_commit": null,
+ "close_after_error_tracking_resolve": false,
+ "close_auto_resolve_prometheus_alert": false
+ },
+ {
+ "user_id": 1,
+ "created_at": "2022-08-17T13:08:17.702Z",
+ "state": "reopened",
+ "source_commit": null,
+ "close_after_error_tracking_resolve": false,
+ "close_auto_resolve_prometheus_alert": false
+ }
]
},
{
diff --git a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/issues.ndjson b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/issues.ndjson
index 2ebd1a78783..3955107865d 100644
--- a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/issues.ndjson
+++ b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/issues.ndjson
@@ -1,4 +1,4 @@
-{"id":40,"title":"Voluptatem","author_id":22,"project_id":5,"created_at":"2016-06-14T15:02:08.340Z","updated_at":"2016-06-14T15:02:47.967Z","position":0,"branch_name":null,"description":"Aliquam enim illo et possimus.","state":"opened","iid":10,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"test_ee_field":"test","issue_assignees":[{"user_id":1,"issue_id":40},{"user_id":15,"issue_id":40},{"user_id":16,"issue_id":40},{"user_id":16,"issue_id":40},{"user_id":6,"issue_id":40}],"award_emoji":[{"id":1,"name":"musical_keyboard","user_id":1,"awardable_type":"Issue","awardable_id":40,"created_at":"2020-01-07T11:55:22.234Z","updated_at":"2020-01-07T11:55:22.234Z"}],"zoom_meetings":[{"id":1,"project_id":5,"issue_id":40,"url":"https://zoom.us/j/123456789","issue_status":1,"created_at":"2016-06-14T15:02:04.418Z","updated_at":"2016-06-14T15:02:04.418Z"}],"milestone":{"id":1,"title":"test milestone","project_id":8,"description":"test milestone","due_date":null,"created_at":"2016-06-14T15:02:04.415Z","updated_at":"2016-06-14T15:02:04.415Z","state":"active","iid":1,"events":[{"id":487,"target_type":"Milestone","target_id":1,"project_id":46,"created_at":"2016-06-14T15:02:04.418Z","updated_at":"2016-06-14T15:02:04.418Z","action":1,"author_id":18}]},"label_links":[{"id":2,"label_id":2,"target_id":40,"target_type":"Issue","created_at":"2016-07-22T08:57:02.840Z","updated_at":"2016-07-22T08:57:02.840Z","label":{"id":2,"title":"test2","color":"#428bca","project_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","type":"ProjectLabel"}},{"id":3,"label_id":3,"target_id":40,"target_type":"Issue","created_at":"2016-07-22T08:57:02.841Z","updated_at":"2016-07-22T08:57:02.841Z","label":{"id":3,"title":"test3","color":"#428bca","group_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","project_id":null,"type":"GroupLabel","priorities":[{"id":1,"project_id":5,"label_id":1,"priority":1,"created_at":"2016-10-18T09:35:43.338Z","updated_at":"2016-10-18T09:35:43.338Z"}]}}],"notes":[{"id":351,"note":"Quo reprehenderit aliquam qui dicta impedit cupiditate eligendi.","note_html":"<p>something else entirely</p>","cached_markdown_version":917504,"noteable_type":"Issue","author_id":26,"created_at":"2016-06-14T15:02:47.770Z","updated_at":"2016-06-14T15:02:47.770Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[],"award_emoji":[{"id":1,"name":"clapper","user_id":1,"awardable_type":"Note","awardable_id":351,"created_at":"2020-01-07T11:55:22.234Z","updated_at":"2020-01-07T11:55:22.234Z"}]},{"id":352,"note":"Est reprehenderit quas aut aspernatur autem recusandae voluptatem.","noteable_type":"Issue","author_id":25,"created_at":"2016-06-14T15:02:47.795Z","updated_at":"2016-06-14T15:02:47.795Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":353,"note":"Perspiciatis suscipit voluptates in eius nihil.","noteable_type":"Issue","author_id":22,"created_at":"2016-06-14T15:02:47.823Z","updated_at":"2016-06-14T15:02:47.823Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":354,"note":"Aut vel voluptas corrupti nisi provident laboriosam magnam aut.","noteable_type":"Issue","author_id":20,"created_at":"2016-06-14T15:02:47.850Z","updated_at":"2016-06-14T15:02:47.850Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":355,"note":"Officia dolore consequatur in saepe cum magni.","noteable_type":"Issue","author_id":16,"created_at":"2016-06-14T15:02:47.876Z","updated_at":"2016-06-14T15:02:47.876Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":356,"note":"Cum ipsum rem voluptas eaque et ea.","noteable_type":"Issue","author_id":15,"created_at":"2016-06-14T15:02:47.908Z","updated_at":"2016-06-14T15:02:47.908Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":357,"note":"Recusandae excepturi asperiores suscipit autem nostrum.","noteable_type":"Issue","author_id":6,"created_at":"2016-06-14T15:02:47.937Z","updated_at":"2016-06-14T15:02:47.937Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":358,"note":"Et hic est id similique et non nesciunt voluptate.","noteable_type":"Issue","author_id":1,"created_at":"2016-06-14T15:02:47.965Z","updated_at":"2016-06-14T15:02:47.965Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"resource_label_events":[{"id":244,"action":"remove","issue_id":40,"merge_request_id":null,"label_id":2,"user_id":1,"created_at":"2018-08-28T08:24:00.494Z","label":{"id":2,"title":"test2","color":"#428bca","project_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","type":"ProjectLabel"}}],"sentry_issue":{"id":1,"issue_id":40,"sentry_issue_identifier":1234567891}}
+{"id":40,"title":"Voluptatem","author_id":22,"project_id":5,"created_at":"2016-06-14T15:02:08.340Z","updated_at":"2016-06-14T15:02:47.967Z","position":0,"branch_name":null,"description":"Aliquam enim illo et possimus.","state":"opened","iid":10,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"test_ee_field":"test","issue_assignees":[{"user_id":1,"issue_id":40},{"user_id":15,"issue_id":40},{"user_id":16,"issue_id":40},{"user_id":16,"issue_id":40},{"user_id":6,"issue_id":40}],"award_emoji":[{"id":1,"name":"musical_keyboard","user_id":1,"awardable_type":"Issue","awardable_id":40,"created_at":"2020-01-07T11:55:22.234Z","updated_at":"2020-01-07T11:55:22.234Z"}],"zoom_meetings":[{"id":1,"project_id":5,"issue_id":40,"url":"https://zoom.us/j/123456789","issue_status":1,"created_at":"2016-06-14T15:02:04.418Z","updated_at":"2016-06-14T15:02:04.418Z"}],"milestone":{"id":1,"title":"test milestone","project_id":8,"description":"test milestone","due_date":null,"created_at":"2016-06-14T15:02:04.415Z","updated_at":"2016-06-14T15:02:04.415Z","state":"active","iid":1,"events":[{"id":487,"target_type":"Milestone","target_id":1,"project_id":46,"created_at":"2016-06-14T15:02:04.418Z","updated_at":"2016-06-14T15:02:04.418Z","action":1,"author_id":18}]},"label_links":[{"id":2,"label_id":2,"target_id":40,"target_type":"Issue","created_at":"2016-07-22T08:57:02.840Z","updated_at":"2016-07-22T08:57:02.840Z","label":{"id":2,"title":"test2","color":"#428bca","project_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","type":"ProjectLabel"}},{"id":3,"label_id":3,"target_id":40,"target_type":"Issue","created_at":"2016-07-22T08:57:02.841Z","updated_at":"2016-07-22T08:57:02.841Z","label":{"id":3,"title":"test3","color":"#428bca","group_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","project_id":null,"type":"GroupLabel","priorities":[{"id":1,"project_id":5,"label_id":1,"priority":1,"created_at":"2016-10-18T09:35:43.338Z","updated_at":"2016-10-18T09:35:43.338Z"}]}}],"notes":[{"id":351,"note":"Quo reprehenderit aliquam qui dicta impedit cupiditate eligendi.","note_html":"<p>something else entirely</p>","cached_markdown_version":917504,"noteable_type":"Issue","author_id":26,"created_at":"2016-06-14T15:02:47.770Z","updated_at":"2016-06-14T15:02:47.770Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[],"award_emoji":[{"id":1,"name":"clapper","user_id":1,"awardable_type":"Note","awardable_id":351,"created_at":"2020-01-07T11:55:22.234Z","updated_at":"2020-01-07T11:55:22.234Z"}]},{"id":352,"note":"Est reprehenderit quas aut aspernatur autem recusandae voluptatem.","noteable_type":"Issue","author_id":25,"created_at":"2016-06-14T15:02:47.795Z","updated_at":"2016-06-14T15:02:47.795Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":353,"note":"Perspiciatis suscipit voluptates in eius nihil.","noteable_type":"Issue","author_id":22,"created_at":"2016-06-14T15:02:47.823Z","updated_at":"2016-06-14T15:02:47.823Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":354,"note":"Aut vel voluptas corrupti nisi provident laboriosam magnam aut.","noteable_type":"Issue","author_id":20,"created_at":"2016-06-14T15:02:47.850Z","updated_at":"2016-06-14T15:02:47.850Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":355,"note":"Officia dolore consequatur in saepe cum magni.","noteable_type":"Issue","author_id":16,"created_at":"2016-06-14T15:02:47.876Z","updated_at":"2016-06-14T15:02:47.876Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":356,"note":"Cum ipsum rem voluptas eaque et ea.","noteable_type":"Issue","author_id":15,"created_at":"2016-06-14T15:02:47.908Z","updated_at":"2016-06-14T15:02:47.908Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":357,"note":"Recusandae excepturi asperiores suscipit autem nostrum.","noteable_type":"Issue","author_id":6,"created_at":"2016-06-14T15:02:47.937Z","updated_at":"2016-06-14T15:02:47.937Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":358,"note":"Et hic est id similique et non nesciunt voluptate.","noteable_type":"Issue","author_id":1,"created_at":"2016-06-14T15:02:47.965Z","updated_at":"2016-06-14T15:02:47.965Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":40,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"resource_label_events":[{"id":244,"action":"remove","issue_id":40,"merge_request_id":null,"label_id":2,"user_id":1,"created_at":"2018-08-28T08:24:00.494Z","label":{"id":2,"title":"test2","color":"#428bca","project_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","type":"ProjectLabel"}}],"sentry_issue":{"id":1,"issue_id":40,"sentry_issue_identifier":1234567891},"resource_milestone_events":[{"user_id":1,"action":"add","state":"opened","created_at":"2022-08-17T13:06:53.547Z","milestone":{"title":"v4.0","description":"Totam quam laborum id magnam natus eaque aspernatur.","created_at":"2016-06-14T15:02:04.590Z","updated_at":"2016-06-14T15:02:04.590Z","state":"active","iid":5}}],"resource_state_events":[{"user_id":1,"created_at":"2022-08-17T13:08:16.838Z","state":"closed","source_commit":null,"close_after_error_tracking_resolve":false,"close_auto_resolve_prometheus_alert":false},{"user_id":1,"created_at":"2022-08-17T13:08:17.702Z","state":"reopened","source_commit":null,"close_after_error_tracking_resolve":false,"close_auto_resolve_prometheus_alert":false}]}
{"id":39,"title":"Issue without assignees","author_id":22,"project_id":5,"created_at":"2016-06-14T15:02:08.233Z","updated_at":"2016-06-14T15:02:48.194Z","position":0,"branch_name":null,"description":"Voluptate vel reprehenderit facilis omnis voluptas magnam tenetur.","state":"opened","iid":9,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"issue_assignees":[],"milestone":{"id":1,"title":"test milestone","project_id":8,"description":"test milestone","due_date":null,"created_at":"2016-06-14T15:02:04.415Z","updated_at":"2016-06-14T15:02:04.415Z","state":"active","iid":1,"events":[{"id":487,"target_type":"Milestone","target_id":1,"project_id":46,"created_at":"2016-06-14T15:02:04.418Z","updated_at":"2016-06-14T15:02:04.418Z","action":1,"author_id":18}]},"notes":[{"id":359,"note":"Quo eius velit quia et id quam.","noteable_type":"Issue","author_id":26,"created_at":"2016-06-14T15:02:48.009Z","updated_at":"2016-06-14T15:02:48.009Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":360,"note":"Nulla commodi ratione cumque id autem.","noteable_type":"Issue","author_id":25,"created_at":"2016-06-14T15:02:48.032Z","updated_at":"2016-06-14T15:02:48.032Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":361,"note":"Illum non ea sed dolores corrupti.","noteable_type":"Issue","author_id":22,"created_at":"2016-06-14T15:02:48.056Z","updated_at":"2016-06-14T15:02:48.056Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":362,"note":"Facere dolores ipsum dolorum maiores omnis occaecati ab.","noteable_type":"Issue","author_id":20,"created_at":"2016-06-14T15:02:48.082Z","updated_at":"2016-06-14T15:02:48.082Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":363,"note":"Quod laudantium similique sint aut est ducimus.","noteable_type":"Issue","author_id":16,"created_at":"2016-06-14T15:02:48.113Z","updated_at":"2016-06-14T15:02:48.113Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":364,"note":"Aut omnis eos esse incidunt vero reiciendis.","noteable_type":"Issue","author_id":15,"created_at":"2016-06-14T15:02:48.139Z","updated_at":"2016-06-14T15:02:48.139Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":365,"note":"Beatae dolore et doloremque asperiores sunt.","noteable_type":"Issue","author_id":6,"created_at":"2016-06-14T15:02:48.162Z","updated_at":"2016-06-14T15:02:48.162Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":366,"note":"Doloribus ipsam ex delectus rerum libero recusandae modi repellendus.","noteable_type":"Issue","author_id":1,"created_at":"2016-06-14T15:02:48.192Z","updated_at":"2016-06-14T15:02:48.192Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":39,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}]}
{"id":38,"title":"Quasi adipisci non cupiditate dolorem quo qui earum sed.","author_id":6,"project_id":5,"created_at":"2016-06-14T15:02:08.154Z","updated_at":"2016-06-14T15:02:48.614Z","position":0,"branch_name":null,"description":"Ea recusandae neque autem tempora.","state":"closed","iid":8,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"label_links":[{"id":99,"label_id":2,"target_id":38,"target_type":"Issue","created_at":"2016-07-22T08:57:02.840Z","updated_at":"2016-07-22T08:57:02.840Z","label":{"id":2,"title":"test2","color":"#428bca","project_id":8,"created_at":"2016-07-22T08:55:44.161Z","updated_at":"2016-07-22T08:55:44.161Z","template":false,"description":"","type":"ProjectLabel"}}],"notes":[{"id":367,"note":"Accusantium fugiat et eaque quisquam esse corporis.","noteable_type":"Issue","author_id":26,"created_at":"2016-06-14T15:02:48.235Z","updated_at":"2016-06-14T15:02:48.235Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":368,"note":"Ea labore eum nam qui laboriosam.","noteable_type":"Issue","author_id":25,"created_at":"2016-06-14T15:02:48.261Z","updated_at":"2016-06-14T15:02:48.261Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":369,"note":"Accusantium quis sed molestiae et.","noteable_type":"Issue","author_id":22,"created_at":"2016-06-14T15:02:48.294Z","updated_at":"2016-06-14T15:02:48.294Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":370,"note":"Corporis numquam a voluptatem pariatur asperiores dolorem delectus autem.","noteable_type":"Issue","author_id":20,"created_at":"2016-06-14T15:02:48.523Z","updated_at":"2016-06-14T15:02:48.523Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":371,"note":"Ea accusantium maxime voluptas rerum.","noteable_type":"Issue","author_id":16,"created_at":"2016-06-14T15:02:48.546Z","updated_at":"2016-06-14T15:02:48.546Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":372,"note":"Pariatur iusto et et excepturi similique ipsam eum.","noteable_type":"Issue","author_id":15,"created_at":"2016-06-14T15:02:48.569Z","updated_at":"2016-06-14T15:02:48.569Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":373,"note":"Aliquam et culpa officia iste eius.","noteable_type":"Issue","author_id":6,"created_at":"2016-06-14T15:02:48.591Z","updated_at":"2016-06-14T15:02:48.591Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":374,"note":"Ab id velit id unde laborum.","noteable_type":"Issue","author_id":1,"created_at":"2016-06-14T15:02:48.613Z","updated_at":"2016-06-14T15:02:48.613Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":38,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}]}
{"id":37,"title":"Cupiditate quo aut ducimus minima molestiae vero numquam possimus.","author_id":20,"project_id":5,"created_at":"2016-06-14T15:02:08.051Z","updated_at":"2016-06-14T15:02:48.854Z","position":0,"branch_name":null,"description":"Maiores architecto quos in dolorem.","state":"opened","iid":7,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"notes":[{"id":375,"note":"Quasi fugit qui sed eligendi aut quia.","noteable_type":"Issue","author_id":26,"created_at":"2016-06-14T15:02:48.647Z","updated_at":"2016-06-14T15:02:48.647Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":376,"note":"Esse nesciunt voluptatem ex vero est consequatur.","noteable_type":"Issue","author_id":25,"created_at":"2016-06-14T15:02:48.674Z","updated_at":"2016-06-14T15:02:48.674Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":377,"note":"Similique qui quas non aut et velit sequi in.","noteable_type":"Issue","author_id":22,"created_at":"2016-06-14T15:02:48.696Z","updated_at":"2016-06-14T15:02:48.696Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":378,"note":"Eveniet ut cupiditate repellendus numquam in esse eius.","noteable_type":"Issue","author_id":20,"created_at":"2016-06-14T15:02:48.720Z","updated_at":"2016-06-14T15:02:48.720Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":379,"note":"Velit est dolorem adipisci rerum sed iure.","noteable_type":"Issue","author_id":16,"created_at":"2016-06-14T15:02:48.755Z","updated_at":"2016-06-14T15:02:48.755Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":380,"note":"Voluptatem ullam ab ut illo ut quo.","noteable_type":"Issue","author_id":15,"created_at":"2016-06-14T15:02:48.793Z","updated_at":"2016-06-14T15:02:48.793Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":381,"note":"Voluptatem impedit beatae quasi ipsa earum consectetur.","noteable_type":"Issue","author_id":6,"created_at":"2016-06-14T15:02:48.823Z","updated_at":"2016-06-14T15:02:48.823Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":382,"note":"Nihil officiis eaque incidunt sunt voluptatum excepturi.","noteable_type":"Issue","author_id":1,"created_at":"2016-06-14T15:02:48.852Z","updated_at":"2016-06-14T15:02:48.852Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":37,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}]}
diff --git a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson
index c14221adc1c..887d7ab658b 100644
--- a/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson
+++ b/spec/fixtures/lib/gitlab/import_export/complex/tree/project/merge_requests.ndjson
@@ -1,4 +1,4 @@
-{"id":27,"target_branch":"feature","source_branch":"feature_conflict","source_project_id":2147483547,"author_id":1,"assignee_id":null,"title":"MR1","created_at":"2016-06-14T15:02:36.568Z","updated_at":"2016-06-14T15:02:56.815Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":9,"description":null,"position":0,"updated_by_id":null,"merge_error":null,"diff_head_sha":"HEAD","source_branch_sha":"ABCD","target_branch_sha":"DCBA","merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":true,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":669,"note":"added 3 commits\n\n<ul><li>16ea4e20...074a2a32 - 2 commits from branch <code>master</code></li><li>ca223a02 - readme: fix typos</li></ul>\n\n[Compare with previous version](/group/project/merge_requests/1/diffs?diff_id=1189&start_sha=16ea4e207fb258fe4e9c73185a725207c9a4f3e1)","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4789,"commit_count":3,"action":"commit","created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z"},"events":[],"suggestions":[]},{"id":670,"note":"unmarked as a **Work In Progress**","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4790,"commit_count":null,"action":"title","created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z"},"events":[],"suggestions":[]},{"id":671,"note":"Sit voluptatibus eveniet architecto quidem.","note_html":"<p>something else entirely</p>","cached_markdown_version":917504,"noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:56.632Z","updated_at":"2016-06-14T15:02:56.632Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[],"award_emoji":[{"id":1,"name":"tada","user_id":1,"awardable_type":"Note","awardable_id":1,"created_at":"2019-11-05T15:37:21.287Z","updated_at":"2019-11-05T15:37:21.287Z"}]},{"id":672,"note":"Odio maxime ratione voluptatibus sed.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:56.656Z","updated_at":"2016-06-14T15:02:56.656Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":673,"note":"Et deserunt et omnis nihil excepturi accusantium.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:56.679Z","updated_at":"2016-06-14T15:02:56.679Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":674,"note":"Saepe asperiores exercitationem non dignissimos laborum reiciendis et ipsum.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:56.700Z","updated_at":"2016-06-14T15:02:56.700Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[],"suggestions":[{"id":1,"note_id":674,"relative_order":0,"applied":false,"commit_id":null,"from_content":"Original line\n","to_content":"New line\n","lines_above":0,"lines_below":0,"outdated":false}]},{"id":675,"note":"Numquam est at dolor quo et sed eligendi similique.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:56.720Z","updated_at":"2016-06-14T15:02:56.720Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":676,"note":"Et perferendis aliquam sunt nisi labore delectus.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:56.742Z","updated_at":"2016-06-14T15:02:56.742Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":677,"note":"Aut ex rerum et in.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:56.791Z","updated_at":"2016-06-14T15:02:56.791Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":678,"note":"Dolor laborum earum ut exercitationem.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:56.814Z","updated_at":"2016-06-14T15:02:56.814Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"resource_label_events":[{"id":243,"action":"add","issue_id":null,"merge_request_id":27,"label_id":null,"user_id":1,"created_at":"2018-08-28T08:24:00.494Z"}],"merge_request_diff":{"id":27,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":27,"relative_order":0,"sha":"bb5206fee213d983da88c47f9cf4cc6caf9c66dc","message":"Feature conflict added\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-08-06T08:35:52.000+02:00","committed_date":"2014-08-06T08:35:52.000+02:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":1,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T10:01:38.000+01:00","committed_date":"2014-02-27T10:01:38.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":2,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:57:31.000+01:00","committed_date":"2014-02-27T09:57:31.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":3,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:54:21.000+01:00","committed_date":"2014-02-27T09:54:21.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":4,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:49:50.000+01:00","committed_date":"2014-02-27T09:49:50.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":5,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:48:32.000+01:00","committed_date":"2014-02-27T09:48:32.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":27,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":3,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,4 @@\n+# This file was changed in feature branch\n+# We put different code here to make merge conflict\n+class Conflict\n+end\n","new_path":"files/ruby/feature.rb","old_path":"files/ruby/feature.rb","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":5,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":6,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":8,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":27,"created_at":"2016-06-14T15:02:36.572Z","updated_at":"2016-06-14T15:02:36.658Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"9"},"events":[{"id":221,"target_type":"MergeRequest","target_id":27,"project_id":36,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1},{"id":187,"target_type":"MergeRequest","target_id":27,"project_id":5,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1}],"approvals_before_merge":1,"award_emoji":[{"id":1,"name":"thumbsup","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"},{"id":2,"name":"drum","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"}],"merge_request_assignees":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","state":"unreviewed"},{"user_id":15,"created_at":"2020-01-08T11:21:21.235Z","state":"reviewed"},{"user_id":16,"created_at":"2020-01-09T11:21:21.235Z","state":"attention_requested"},{"user_id":6,"created_at":"2020-01-10T11:21:21.235Z","state":"unreviewed"}],"merge_request_reviewers":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","state":"unreviewed"},{"user_id":15,"created_at":"2020-01-08T11:21:21.235Z","state":"reviewed"},{"user_id":16,"created_at":"2020-01-09T11:21:21.235Z","state":"attention_requested"},{"user_id":6,"created_at":"2020-01-10T11:21:21.235Z","state":"unreviewed"}],"approvals":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":15,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":16,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":6,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"}]}
+{"id":27,"target_branch":"feature","source_branch":"feature_conflict","source_project_id":2147483547,"author_id":1,"assignee_id":null,"title":"MR1","created_at":"2016-06-14T15:02:36.568Z","updated_at":"2016-06-14T15:02:56.815Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":9,"description":null,"position":0,"updated_by_id":null,"merge_error":null,"diff_head_sha":"HEAD","source_branch_sha":"ABCD","target_branch_sha":"DCBA","merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":true,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":669,"note":"added 3 commits\n\n<ul><li>16ea4e20...074a2a32 - 2 commits from branch <code>master</code></li><li>ca223a02 - readme: fix typos</li></ul>\n\n[Compare with previous version](/group/project/merge_requests/1/diffs?diff_id=1189&start_sha=16ea4e207fb258fe4e9c73185a725207c9a4f3e1)","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4789,"commit_count":3,"action":"commit","created_at":"2020-03-28T12:47:33.461Z","updated_at":"2020-03-28T12:47:33.461Z"},"events":[],"suggestions":[]},{"id":670,"note":"unmarked as a **Work In Progress**","noteable_type":"MergeRequest","author_id":26,"created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"system":true,"st_diff":null,"updated_by_id":null,"position":null,"original_position":null,"resolved_at":null,"resolved_by_id":null,"discussion_id":null,"change_position":null,"resolved_by_push":null,"confidential":null,"type":null,"author":{"name":"User 4"},"award_emoji":[],"system_note_metadata":{"id":4790,"commit_count":null,"action":"title","created_at":"2020-03-28T12:48:36.951Z","updated_at":"2020-03-28T12:48:36.951Z"},"events":[],"suggestions":[]},{"id":671,"note":"Sit voluptatibus eveniet architecto quidem.","note_html":"<p>something else entirely</p>","cached_markdown_version":917504,"noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:56.632Z","updated_at":"2016-06-14T15:02:56.632Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[],"award_emoji":[{"id":1,"name":"tada","user_id":1,"awardable_type":"Note","awardable_id":1,"created_at":"2019-11-05T15:37:21.287Z","updated_at":"2019-11-05T15:37:21.287Z"}]},{"id":672,"note":"Odio maxime ratione voluptatibus sed.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:56.656Z","updated_at":"2016-06-14T15:02:56.656Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":673,"note":"Et deserunt et omnis nihil excepturi accusantium.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:56.679Z","updated_at":"2016-06-14T15:02:56.679Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":674,"note":"Saepe asperiores exercitationem non dignissimos laborum reiciendis et ipsum.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:56.700Z","updated_at":"2016-06-14T15:02:56.700Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[],"suggestions":[{"id":1,"note_id":674,"relative_order":0,"applied":false,"commit_id":null,"from_content":"Original line\n","to_content":"New line\n","lines_above":0,"lines_below":0,"outdated":false}]},{"id":675,"note":"Numquam est at dolor quo et sed eligendi similique.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:56.720Z","updated_at":"2016-06-14T15:02:56.720Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":676,"note":"Et perferendis aliquam sunt nisi labore delectus.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:56.742Z","updated_at":"2016-06-14T15:02:56.742Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":677,"note":"Aut ex rerum et in.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:56.791Z","updated_at":"2016-06-14T15:02:56.791Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":678,"note":"Dolor laborum earum ut exercitationem.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:56.814Z","updated_at":"2016-06-14T15:02:56.814Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":27,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"resource_label_events":[{"id":243,"action":"add","issue_id":null,"merge_request_id":27,"label_id":null,"user_id":1,"created_at":"2018-08-28T08:24:00.494Z"}],"merge_request_diff":{"id":27,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":27,"relative_order":0,"sha":"bb5206fee213d983da88c47f9cf4cc6caf9c66dc","message":"Feature conflict added\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-08-06T08:35:52.000+02:00","committed_date":"2014-08-06T08:35:52.000+02:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":1,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T10:01:38.000+01:00","committed_date":"2014-02-27T10:01:38.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":2,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:57:31.000+01:00","committed_date":"2014-02-27T09:57:31.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":3,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:54:21.000+01:00","committed_date":"2014-02-27T09:54:21.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":4,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:49:50.000+01:00","committed_date":"2014-02-27T09:49:50.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":27,"relative_order":5,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:48:32.000+01:00","committed_date":"2014-02-27T09:48:32.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":27,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":3,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":27,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,4 @@\n+# This file was changed in feature branch\n+# We put different code here to make merge conflict\n+class Conflict\n+end\n","new_path":"files/ruby/feature.rb","old_path":"files/ruby/feature.rb","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":5,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":6,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":27,"relative_order":8,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":27,"created_at":"2016-06-14T15:02:36.572Z","updated_at":"2016-06-14T15:02:36.658Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"9"},"events":[{"id":221,"target_type":"MergeRequest","target_id":27,"project_id":36,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1},{"id":187,"target_type":"MergeRequest","target_id":27,"project_id":5,"created_at":"2016-06-14T15:02:36.703Z","updated_at":"2016-06-14T15:02:36.703Z","action":1,"author_id":1}],"approvals_before_merge":1,"award_emoji":[{"id":1,"name":"thumbsup","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"},{"id":2,"name":"drum","user_id":1,"awardable_type":"MergeRequest","awardable_id":27,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-07T11:21:21.235Z"}],"merge_request_assignees":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","state":"unreviewed"},{"user_id":15,"created_at":"2020-01-08T11:21:21.235Z","state":"reviewed"},{"user_id":16,"created_at":"2020-01-09T11:21:21.235Z","state":"reviewed"},{"user_id":6,"created_at":"2020-01-10T11:21:21.235Z","state":"unreviewed"}],"merge_request_reviewers":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","state":"unreviewed"},{"user_id":15,"created_at":"2020-01-08T11:21:21.235Z","state":"reviewed"},{"user_id":16,"created_at":"2020-01-09T11:21:21.235Z","state":"reviewed"},{"user_id":6,"created_at":"2020-01-10T11:21:21.235Z","state":"unreviewed"}],"approvals":[{"user_id":1,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":15,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":16,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"},{"user_id":6,"created_at":"2020-01-07T11:21:21.235Z","updated_at":"2020-01-08T11:21:21.235Z"}],"resource_milestone_events":[{"user_id":1,"action":"add","state":"opened","created_at":"2022-08-17T13:06:53.547Z","milestone":{"title":"v4.0","description":"Totam quam laborum id magnam natus eaque aspernatur.","created_at":"2016-06-14T15:02:04.590Z","updated_at":"2016-06-14T15:02:04.590Z","state":"active","iid":5}}],"resource_state_events":[{"user_id":1,"created_at":"2022-08-17T13:08:16.838Z","state":"closed","source_commit":null,"close_after_error_tracking_resolve":false,"close_auto_resolve_prometheus_alert":false},{"user_id":1,"created_at":"2022-08-17T13:08:17.702Z","state":"reopened","source_commit":null,"close_after_error_tracking_resolve":false,"close_auto_resolve_prometheus_alert":false}]}
{"id":26,"target_branch":"master","source_branch":"feature","source_project_id":4,"author_id":1,"assignee_id":null,"title":"MR2","created_at":"2016-06-14T15:02:36.418Z","updated_at":"2016-06-14T15:02:57.013Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":8,"description":null,"position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":679,"note":"Qui rerum totam nisi est.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:56.848Z","updated_at":"2016-06-14T15:02:56.848Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":680,"note":"Pariatur magni corrupti consequatur debitis minima error beatae voluptatem.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:56.871Z","updated_at":"2016-06-14T15:02:56.871Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":681,"note":"Qui quis ut modi eos rerum ratione.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:56.895Z","updated_at":"2016-06-14T15:02:56.895Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":682,"note":"Illum quidem expedita mollitia fugit.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:56.918Z","updated_at":"2016-06-14T15:02:56.918Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":683,"note":"Consectetur voluptate sit sint possimus veritatis quod.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:56.942Z","updated_at":"2016-06-14T15:02:56.942Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":684,"note":"Natus libero quibusdam rem assumenda deleniti accusamus sed earum.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:56.966Z","updated_at":"2016-06-14T15:02:56.966Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":685,"note":"Tenetur autem nihil rerum odit.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:56.989Z","updated_at":"2016-06-14T15:02:56.989Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":686,"note":"Quia maiores et odio sed.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:57.012Z","updated_at":"2016-06-14T15:02:57.012Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":26,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":26,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":26,"sha":"0b4bc9a49b562e85de7cc9e834518ea6828729b9","relative_order":0,"message":"Feature added\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:26:01.000+01:00","committed_date":"2014-02-27T09:26:01.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":26,"relative_order":0,"utf8_diff":"--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,5 @@\n+class Feature\n+ def foo\n+ puts 'bar'\n+ end\n+end\n","new_path":"files/ruby/feature.rb","old_path":"files/ruby/feature.rb","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":26,"created_at":"2016-06-14T15:02:36.421Z","updated_at":"2016-06-14T15:02:36.474Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"1"},"events":[{"id":222,"target_type":"MergeRequest","target_id":26,"project_id":36,"created_at":"2016-06-14T15:02:36.496Z","updated_at":"2016-06-14T15:02:36.496Z","action":1,"author_id":1},{"id":186,"target_type":"MergeRequest","target_id":26,"project_id":5,"created_at":"2016-06-14T15:02:36.496Z","updated_at":"2016-06-14T15:02:36.496Z","action":1,"author_id":1}],"merge_request_assignees":[],"merge_request_reviewers":[],"approvals":[]}
{"id":15,"target_branch":"test-7","source_branch":"test-1","source_project_id":5,"author_id":22,"assignee_id":16,"title":"Qui accusantium et inventore facilis doloribus occaecati officiis.","created_at":"2016-06-14T15:02:25.168Z","updated_at":"2016-06-14T15:02:59.521Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":7,"description":"Et commodi deserunt aspernatur vero rerum. Ut non dolorum alias in odit est libero. Voluptatibus eos in et vitae repudiandae facilis ex mollitia.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":777,"note":"Pariatur voluptas placeat aspernatur culpa suscipit soluta.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:59.348Z","updated_at":"2016-06-14T15:02:59.348Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":778,"note":"Alias et iure mollitia suscipit molestiae voluptatum nostrum asperiores.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:59.372Z","updated_at":"2016-06-14T15:02:59.372Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":779,"note":"Laudantium qui eum qui sunt.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:59.395Z","updated_at":"2016-06-14T15:02:59.395Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":780,"note":"Quas rem est iusto ut delectus fugiat recusandae mollitia.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:59.418Z","updated_at":"2016-06-14T15:02:59.418Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":781,"note":"Repellendus ab et qui nesciunt.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:59.444Z","updated_at":"2016-06-14T15:02:59.444Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":782,"note":"Non possimus voluptatum odio qui ut.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:59.469Z","updated_at":"2016-06-14T15:02:59.469Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":783,"note":"Dolores repellendus eum ducimus quam ab dolorem quia.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:59.494Z","updated_at":"2016-06-14T15:02:59.494Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":784,"note":"Facilis dolorem aut corrupti id ratione occaecati.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:59.520Z","updated_at":"2016-06-14T15:02:59.520Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":15,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":15,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":15,"relative_order":0,"sha":"94b8d581c48d894b86661718582fecbc5e3ed2eb","message":"fixes #10\n","authored_date":"2016-01-19T13:22:56.000+01:00","committed_date":"2016-01-19T13:22:56.000+01:00","commit_author":{"name":"James Lopez","email":"james@jameslopez.es"},"committer":{"name":"James Lopez","email":"james@jameslopez.es"}}],"merge_request_diff_files":[{"merge_request_diff_id":15,"relative_order":0,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":15,"created_at":"2016-06-14T15:02:25.171Z","updated_at":"2016-06-14T15:02:25.230Z","base_commit_sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","real_size":"1"},"events":[{"id":223,"target_type":"MergeRequest","target_id":15,"project_id":36,"created_at":"2016-06-14T15:02:25.262Z","updated_at":"2016-06-14T15:02:25.262Z","action":1,"author_id":1},{"id":175,"target_type":"MergeRequest","target_id":15,"project_id":5,"created_at":"2016-06-14T15:02:25.262Z","updated_at":"2016-06-14T15:02:25.262Z","action":1,"author_id":22}]}
{"id":14,"target_branch":"fix","source_branch":"test-3","source_project_id":5,"author_id":20,"assignee_id":20,"title":"In voluptas aut sequi voluptatem ullam vel corporis illum consequatur.","created_at":"2016-06-14T15:02:24.760Z","updated_at":"2016-06-14T15:02:59.749Z","state":"opened","merge_status":"unchecked","target_project_id":5,"iid":6,"description":"Dicta magnam non voluptates nam dignissimos nostrum deserunt. Dolorum et suscipit iure quae doloremque. Necessitatibus saepe aut labore sed.","position":0,"updated_by_id":null,"merge_error":null,"merge_params":{"force_remove_source_branch":null},"merge_when_pipeline_succeeds":false,"merge_user_id":null,"merge_commit_sha":null,"notes":[{"id":785,"note":"Atque cupiditate necessitatibus deserunt minus natus odit.","noteable_type":"MergeRequest","author_id":26,"created_at":"2016-06-14T15:02:59.559Z","updated_at":"2016-06-14T15:02:59.559Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 4"},"events":[]},{"id":786,"note":"Non dolorem provident mollitia nesciunt optio ex eveniet.","noteable_type":"MergeRequest","author_id":25,"created_at":"2016-06-14T15:02:59.587Z","updated_at":"2016-06-14T15:02:59.587Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 3"},"events":[]},{"id":787,"note":"Similique officia nemo quasi commodi accusantium quae qui.","noteable_type":"MergeRequest","author_id":22,"created_at":"2016-06-14T15:02:59.621Z","updated_at":"2016-06-14T15:02:59.621Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"User 0"},"events":[]},{"id":788,"note":"Et est et alias ad dolor qui.","noteable_type":"MergeRequest","author_id":20,"created_at":"2016-06-14T15:02:59.650Z","updated_at":"2016-06-14T15:02:59.650Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ottis Schuster II"},"events":[]},{"id":789,"note":"Numquam temporibus ratione voluptatibus aliquid.","noteable_type":"MergeRequest","author_id":16,"created_at":"2016-06-14T15:02:59.675Z","updated_at":"2016-06-14T15:02:59.675Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Rhett Emmerich IV"},"events":[]},{"id":790,"note":"Ut ex aliquam consectetur perferendis est hic aut quia.","noteable_type":"MergeRequest","author_id":15,"created_at":"2016-06-14T15:02:59.703Z","updated_at":"2016-06-14T15:02:59.703Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Burdette Bernier"},"events":[]},{"id":791,"note":"Esse eos quam quaerat aut ut asperiores officiis.","noteable_type":"MergeRequest","author_id":6,"created_at":"2016-06-14T15:02:59.726Z","updated_at":"2016-06-14T15:02:59.726Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Ari Wintheiser"},"events":[]},{"id":792,"note":"Sint facilis accusantium iure blanditiis.","noteable_type":"MergeRequest","author_id":1,"created_at":"2016-06-14T15:02:59.748Z","updated_at":"2016-06-14T15:02:59.748Z","project_id":5,"attachment":{"url":null},"line_code":null,"commit_id":null,"noteable_id":14,"system":false,"st_diff":null,"updated_by_id":null,"author":{"name":"Administrator"},"events":[]}],"merge_request_diff":{"id":14,"state":"collected","merge_request_diff_commits":[{"merge_request_diff_id":14,"relative_order":0,"sha":"ddd4ff416a931589c695eb4f5b23f844426f6928","message":"fixes #10\n","authored_date":"2016-01-19T14:14:43.000+01:00","committed_date":"2016-01-19T14:14:43.000+01:00","commit_author":{"name":"James Lopez","email":"james@jameslopez.es"},"committer":{"name":"James Lopez","email":"james@jameslopez.es"}},{"merge_request_diff_id":14,"relative_order":1,"sha":"be93687618e4b132087f430a4d8fc3a609c9b77c","message":"Merge branch 'master' into 'master'\r\n\r\nLFS object pointer.\r\n\r\n\r\n\r\nSee merge request !6","authored_date":"2015-12-07T12:52:12.000+01:00","committed_date":"2015-12-07T12:52:12.000+01:00","commit_author":{"name":"Marin Jankovski","email":"marin@gitlab.com"},"committer":{"name":"Marin Jankovski","email":"marin@gitlab.com"}},{"merge_request_diff_id":14,"relative_order":2,"sha":"048721d90c449b244b7b4c53a9186b04330174ec","message":"LFS object pointer.\n","authored_date":"2015-12-07T11:54:28.000+01:00","committed_date":"2015-12-07T11:54:28.000+01:00","commit_author":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"},"committer":{"name":"Marin Jankovski","email":"maxlazio@gmail.com"}},{"merge_request_diff_id":14,"relative_order":3,"sha":"5f923865dde3436854e9ceb9cdb7815618d4e849","message":"GitLab currently doesn't support patches that involve a merge commit: add a commit here\n","authored_date":"2015-11-13T16:27:12.000+01:00","committed_date":"2015-11-13T16:27:12.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":4,"sha":"d2d430676773caa88cdaf7c55944073b2fd5561a","message":"Merge branch 'add-svg' into 'master'\r\n\r\nAdd GitLab SVG\r\n\r\nAdded to test preview of sanitized SVG images\r\n\r\nSee merge request !5","authored_date":"2015-11-13T08:50:17.000+01:00","committed_date":"2015-11-13T08:50:17.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":5,"sha":"2ea1f3dec713d940208fb5ce4a38765ecb5d3f73","message":"Add GitLab SVG\n","authored_date":"2015-11-13T08:39:43.000+01:00","committed_date":"2015-11-13T08:39:43.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":6,"sha":"59e29889be61e6e0e5e223bfa9ac2721d31605b8","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd whitespace test file\r\n\r\nSorry, I did a mistake.\r\nGit ignore empty files.\r\nSo I add a new whitespace test file.\r\n\r\nSee merge request !4","authored_date":"2015-11-13T07:21:40.000+01:00","committed_date":"2015-11-13T07:21:40.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":7,"sha":"66eceea0db202bb39c4e445e8ca28689645366c5","message":"add spaces in whitespace file\n","authored_date":"2015-11-13T06:01:27.000+01:00","committed_date":"2015-11-13T06:01:27.000+01:00","commit_author":{"name":"윤민ì‹","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민ì‹","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":8,"sha":"08f22f255f082689c0d7d39d19205085311542bc","message":"remove empty file.(beacase git ignore empty file)\nadd whitespace test file.\n","authored_date":"2015-11-13T06:00:16.000+01:00","committed_date":"2015-11-13T06:00:16.000+01:00","commit_author":{"name":"윤민ì‹","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민ì‹","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":9,"sha":"19e2e9b4ef76b422ce1154af39a91323ccc57434","message":"Merge branch 'whitespace' into 'master'\r\n\r\nadd spaces\r\n\r\nTo test this pull request.(https://github.com/gitlabhq/gitlabhq/pull/9757)\r\nJust add whitespaces.\r\n\r\nSee merge request !3","authored_date":"2015-11-13T05:23:14.000+01:00","committed_date":"2015-11-13T05:23:14.000+01:00","commit_author":{"name":"Stan Hu","email":"stanhu@gmail.com"},"committer":{"name":"Stan Hu","email":"stanhu@gmail.com"}},{"merge_request_diff_id":14,"relative_order":10,"sha":"c642fe9b8b9f28f9225d7ea953fe14e74748d53b","message":"add whitespace in empty\n","authored_date":"2015-11-13T05:08:45.000+01:00","committed_date":"2015-11-13T05:08:45.000+01:00","commit_author":{"name":"윤민ì‹","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민ì‹","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":11,"sha":"9a944d90955aaf45f6d0c88f30e27f8d2c41cec0","message":"add empty file\n","authored_date":"2015-11-13T05:08:04.000+01:00","committed_date":"2015-11-13T05:08:04.000+01:00","commit_author":{"name":"윤민ì‹","email":"minsik.yoon@samsung.com"},"committer":{"name":"윤민ì‹","email":"minsik.yoon@samsung.com"}},{"merge_request_diff_id":14,"relative_order":12,"sha":"c7fbe50c7c7419d9701eebe64b1fdacc3df5b9dd","message":"Add ISO-8859 test file\n","authored_date":"2015-08-25T17:53:12.000+02:00","committed_date":"2015-08-25T17:53:12.000+02:00","commit_author":{"name":"Stan Hu","email":"stanhu@packetzoom.com"},"committer":{"name":"Stan Hu","email":"stanhu@packetzoom.com"}},{"merge_request_diff_id":14,"relative_order":13,"sha":"e56497bb5f03a90a51293fc6d516788730953899","message":"Merge branch 'tree_helper_spec' into 'master'\n\nAdd directory structure for tree_helper spec\n\nThis directory structure is needed for a testing the method flatten_tree(tree) in the TreeHelper module\n\nSee [merge request #275](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/275#note_732774)\n\nSee merge request !2\n","authored_date":"2015-01-10T22:23:29.000+01:00","committed_date":"2015-01-10T22:23:29.000+01:00","commit_author":{"name":"Sytse Sijbrandij","email":"sytse@gitlab.com"},"committer":{"name":"Sytse Sijbrandij","email":"sytse@gitlab.com"}},{"merge_request_diff_id":14,"relative_order":14,"sha":"4cd80ccab63c82b4bad16faa5193fbd2aa06df40","message":"add directory structure for tree_helper spec\n","authored_date":"2015-01-10T21:28:18.000+01:00","committed_date":"2015-01-10T21:28:18.000+01:00","commit_author":{"name":"marmis85","email":"marmis85@gmail.com"},"committer":{"name":"marmis85","email":"marmis85@gmail.com"}},{"merge_request_diff_id":14,"relative_order":15,"sha":"5937ac0a7beb003549fc5fd26fc247adbce4a52e","message":"Add submodule from gitlab.com\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T10:01:38.000+01:00","committed_date":"2014-02-27T10:01:38.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":16,"sha":"570e7b2abdd848b95f2f578043fc23bd6f6fd24d","message":"Change some files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:57:31.000+01:00","committed_date":"2014-02-27T09:57:31.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":17,"sha":"6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9","message":"More submodules\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:54:21.000+01:00","committed_date":"2014-02-27T09:54:21.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":18,"sha":"d14d6c0abdd253381df51a723d58691b2ee1ab08","message":"Remove ds_store files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:49:50.000+01:00","committed_date":"2014-02-27T09:49:50.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}},{"merge_request_diff_id":14,"relative_order":19,"sha":"c1acaa58bbcbc3eafe538cb8274ba387047b69f8","message":"Ignore DS files\n\nSigned-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>\n","authored_date":"2014-02-27T09:48:32.000+01:00","committed_date":"2014-02-27T09:48:32.000+01:00","commit_author":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"},"committer":{"name":"Dmitriy Zaporozhets","email":"dmitriy.zaporozhets@gmail.com"}}],"merge_request_diff_files":[{"merge_request_diff_id":14,"relative_order":0,"utf8_diff":"Binary files a/.DS_Store and /dev/null differ\n","new_path":".DS_Store","old_path":".DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":14,"relative_order":1,"utf8_diff":"--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n","new_path":".gitignore","old_path":".gitignore","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":2,"utf8_diff":"--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n","new_path":".gitmodules","old_path":".gitmodules","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":3,"utf8_diff":"--- a/CHANGELOG\n+++ b/CHANGELOG\n@@ -1,4 +1,6 @@\n-v 6.7.0\n+v6.8.0\n+\n+v6.7.0\n - Add support for Gemnasium as a Project Service (Olivier Gonzalez)\n - Add edit file button to MergeRequest diff\n - Public groups (Jason Hollingsworth)\n","new_path":"CHANGELOG","old_path":"CHANGELOG","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":4,"utf8_diff":"--- /dev/null\n+++ b/encoding/iso8859.txt\n@@ -0,0 +1 @@\n+Äü\n","new_path":"encoding/iso8859.txt","old_path":"encoding/iso8859.txt","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":5,"utf8_diff":"Binary files a/files/.DS_Store and /dev/null differ\n","new_path":"files/.DS_Store","old_path":"files/.DS_Store","a_mode":"100644","b_mode":"0","new_file":false,"renamed_file":false,"deleted_file":true,"too_large":false},{"merge_request_diff_id":14,"relative_order":6,"utf8_diff":"--- /dev/null\n+++ b/files/images/wm.svg\n@@ -0,0 +1,78 @@\n+<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n+<svg width=\"1300px\" height=\"680px\" viewBox=\"0 0 1300 680\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:sketch=\"http://www.bohemiancoding.com/sketch/ns\">\n+ <!-- Generator: Sketch 3.2.2 (9983) - http://www.bohemiancoding.com/sketch -->\n+ <title>wm</title>\n+ <desc>Created with Sketch.</desc>\n+ <defs>\n+ <path id=\"path-1\" d=\"M-69.8,1023.54607 L1675.19996,1023.54607 L1675.19996,0 L-69.8,0 L-69.8,1023.54607 L-69.8,1023.54607 Z\"></path>\n+ </defs>\n+ <g id=\"Page-1\" stroke=\"none\" stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" sketch:type=\"MSPage\">\n+ <path d=\"M1300,680 L0,680 L0,0 L1300,0 L1300,680 L1300,680 Z\" id=\"bg\" fill=\"#30353E\" sketch:type=\"MSShapeGroup\"></path>\n+ <g id=\"gitlab_logo\" sketch:type=\"MSLayerGroup\" transform=\"translate(-262.000000, -172.000000)\">\n+ <g id=\"g10\" transform=\"translate(872.500000, 512.354581) scale(1, -1) translate(-872.500000, -512.354581) translate(0.000000, 0.290751)\">\n+ <g id=\"g12\" transform=\"translate(1218.022652, 440.744871)\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\">\n+ <path d=\"M-50.0233338,141.900706 L-69.07059,141.900706 L-69.0100967,0.155858152 L8.04444805,0.155858152 L8.04444805,17.6840847 L-49.9628405,17.6840847 L-50.0233338,141.900706 L-50.0233338,141.900706 Z\" id=\"path14\"></path>\n+ </g>\n+ <g id=\"g16\">\n+ <g id=\"g18-Clipped\">\n+ <mask id=\"mask-2\" sketch:name=\"path22\" fill=\"white\">\n+ <use xlink:href=\"#path-1\"></use>\n+ </mask>\n+ <g id=\"path22\"></g>\n+ <g id=\"g18\" mask=\"url(#mask-2)\">\n+ <g transform=\"translate(382.736659, 312.879425)\">\n+ <g id=\"g24\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(852.718192, 124.992771)\">\n+ <path d=\"M63.9833317,27.9148929 C59.2218085,22.9379001 51.2134221,17.9597442 40.3909323,17.9597442 C25.8888194,17.9597442 20.0453962,25.1013043 20.0453962,34.4074318 C20.0453962,48.4730484 29.7848226,55.1819277 50.5642821,55.1819277 C54.4602853,55.1819277 60.7364685,54.7492469 63.9833317,54.1002256 L63.9833317,27.9148929 L63.9833317,27.9148929 Z M44.2869356,113.827628 C28.9053426,113.827628 14.7975996,108.376082 3.78897657,99.301416 L10.5211864,87.6422957 C18.3131929,92.1866076 27.8374026,96.7320827 41.4728323,96.7320827 C57.0568452,96.7320827 63.9833317,88.7239978 63.9833317,75.3074024 L63.9833317,68.3821827 C60.9528485,69.0312039 54.6766653,69.4650479 50.7806621,69.4650479 C17.4476729,69.4650479 0.565379986,57.7791759 0.565379986,33.3245665 C0.565379986,11.4683685 13.9844297,0.43151772 34.3299658,0.43151772 C48.0351955,0.43151772 61.1692285,6.70771614 65.7143717,16.8780421 L69.1776149,3.02876588 L82.5978279,3.02876588 L82.5978279,75.5237428 C82.5978279,98.462806 72.6408582,113.827628 44.2869356,113.827628 L44.2869356,113.827628 Z\" id=\"path26\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g28\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(959.546624, 124.857151)\">\n+ <path d=\"M37.2266657,17.4468081 C30.0837992,17.4468081 23.8064527,18.3121698 19.0449295,20.4767371 L19.0449295,79.2306079 L19.0449295,86.0464943 C25.538656,91.457331 33.5470425,95.3526217 43.7203922,95.3526217 C62.1173451,95.3526217 69.2602116,82.3687072 69.2602116,61.3767077 C69.2602116,31.5135879 57.7885819,17.4468081 37.2266657,17.4468081 M45.2315622,113.963713 C28.208506,113.963713 19.0449295,102.384849 19.0449295,102.384849 L19.0449295,120.67143 L18.9844362,144.908535 L10.3967097,144.908535 L0.371103324,144.908535 L0.431596656,6.62629771 C9.73826309,2.73100702 22.5081728,0.567602823 36.3611458,0.567602823 C71.8579349,0.567602823 88.9566078,23.2891625 88.9566078,62.4584098 C88.9566078,93.4043948 73.1527248,113.963713 45.2315622,113.963713\" id=\"path30\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g32\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(509.576747, 125.294950)\">\n+ <path d=\"M68.636665,129.10638 C85.5189579,129.10638 96.3414476,123.480366 103.484314,117.853189 L111.669527,132.029302 C100.513161,141.811145 85.5073245,147.06845 69.5021849,147.06845 C29.0274926,147.06845 0.673569983,122.3975 0.673569983,72.6252464 C0.673569983,20.4709215 31.2622559,0.12910638 66.2553217,0.12910638 C83.7879179,0.12910638 98.7227909,4.24073748 108.462217,8.35236859 L108.063194,64.0763105 L108.063194,70.6502677 L108.063194,81.6057001 L56.1168719,81.6057001 L56.1168719,64.0763105 L89.2323178,64.0763105 L89.6313411,21.7701271 C85.3025779,19.6055598 77.7269514,17.8748364 67.554765,17.8748364 C39.4172223,17.8748364 20.5863462,35.5717154 20.5863462,72.8415868 C20.5863462,110.711628 40.0663623,129.10638 68.636665,129.10638\" id=\"path34\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g36\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(692.388992, 124.376085)\">\n+ <path d=\"M19.7766662,145.390067 L1.16216997,145.390067 L1.2226633,121.585642 L1.2226633,111.846834 L1.2226633,106.170806 L1.2226633,96.2656714 L1.2226633,39.5681976 L1.2226633,39.3518572 C1.2226633,16.4127939 11.1796331,1.04797161 39.5335557,1.04797161 C43.4504989,1.04797161 47.2836822,1.40388649 51.0051854,2.07965952 L51.0051854,18.7925385 C48.3109055,18.3796307 45.4351455,18.1446804 42.3476589,18.1446804 C26.763646,18.1446804 19.8371595,26.1516022 19.8371595,39.5681976 L19.8371595,96.2656714 L51.0051854,96.2656714 L51.0051854,111.846834 L19.8371595,111.846834 L19.7766662,145.390067 L19.7766662,145.390067 Z\" id=\"path38\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <path d=\"M646.318899,128.021188 L664.933395,128.021188 L664.933395,236.223966 L646.318899,236.223966 L646.318899,128.021188 L646.318899,128.021188 Z\" id=\"path40\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\"></path>\n+ <path d=\"M646.318899,251.154944 L664.933395,251.154944 L664.933395,269.766036 L646.318899,269.766036 L646.318899,251.154944 L646.318899,251.154944 Z\" id=\"path42\" fill=\"#8C929D\" sketch:type=\"MSShapeGroup\"></path>\n+ <g id=\"g44\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(0.464170, 0.676006)\">\n+ <path d=\"M429.269989,169.815599 L405.225053,243.802859 L357.571431,390.440955 C355.120288,397.984955 344.444378,397.984955 341.992071,390.440955 L294.337286,243.802859 L136.094873,243.802859 L88.4389245,390.440955 C85.9877812,397.984955 75.3118715,397.984955 72.8595648,390.440955 L25.2059427,243.802859 L1.16216997,169.815599 C-1.03187664,163.067173 1.37156997,155.674379 7.11261982,151.503429 L215.215498,0.336141836 L423.319539,151.503429 C429.060589,155.674379 431.462873,163.067173 429.269989,169.815599\" id=\"path46\" fill=\"#FC6D26\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g48\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(135.410135, 1.012147)\">\n+ <path d=\"M80.269998,0 L80.269998,0 L159.391786,243.466717 L1.14820997,243.466717 L80.269998,0 L80.269998,0 Z\" id=\"path50\" fill=\"#E24329\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g52\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(215.680133, 1.012147)\">\n+ <g id=\"path54\"></g>\n+ </g>\n+ <g id=\"g56\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(24.893471, 1.012613)\">\n+ <path d=\"M190.786662,0 L111.664874,243.465554 L0.777106647,243.465554 L190.786662,0 L190.786662,0 Z\" id=\"path58\" fill=\"#FC6D26\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g60\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(215.680133, 1.012613)\">\n+ <g id=\"path62\"></g>\n+ </g>\n+ <g id=\"g64\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(0.077245, 0.223203)\">\n+ <path d=\"M25.5933327,244.255313 L25.5933327,244.255313 L1.54839663,170.268052 C-0.644486651,163.519627 1.75779662,156.126833 7.50000981,151.957046 L215.602888,0.789758846 L25.5933327,244.255313 L25.5933327,244.255313 Z\" id=\"path66\" fill=\"#FCA326\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g68\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(215.680133, 1.012147)\">\n+ <g id=\"path70\"></g>\n+ </g>\n+ <g id=\"g72\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(25.670578, 244.478283)\">\n+ <path d=\"M0,0 L110.887767,0 L63.2329818,146.638096 C60.7806751,154.183259 50.1047654,154.183259 47.6536221,146.638096 L0,0 L0,0 Z\" id=\"path74\" fill=\"#E24329\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g76\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(215.680133, 1.012613)\">\n+ <path d=\"M0,0 L79.121788,243.465554 L190.009555,243.465554 L0,0 L0,0 Z\" id=\"path78\" fill=\"#FC6D26\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g80\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(214.902910, 0.223203)\">\n+ <path d=\"M190.786662,244.255313 L190.786662,244.255313 L214.831598,170.268052 C217.024481,163.519627 214.622198,156.126833 208.879985,151.957046 L0.777106647,0.789758846 L190.786662,244.255313 L190.786662,244.255313 Z\" id=\"path82\" fill=\"#FCA326\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ <g id=\"g84\" stroke-width=\"1\" fill=\"none\" sketch:type=\"MSLayerGroup\" transform=\"translate(294.009575, 244.478283)\">\n+ <path d=\"M111.679997,0 L0.79222998,0 L48.4470155,146.638096 C50.8993221,154.183259 61.5752318,154.183259 64.0263751,146.638096 L111.679997,0 L111.679997,0 Z\" id=\"path86\" fill=\"#E24329\" sketch:type=\"MSShapeGroup\"></path>\n+ </g>\n+ </g>\n+ </g>\n+ </g>\n+ </g>\n+ </g>\n+ </g>\n+ </g>\n+</svg>\n\\ No newline at end of file\n","new_path":"files/images/wm.svg","old_path":"files/images/wm.svg","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":7,"utf8_diff":"--- /dev/null\n+++ b/files/lfs/lfs_object.iso\n@@ -0,0 +1,4 @@\n+version https://git-lfs.github.com/spec/v1\n+oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897\n+size 1575078\n+\n","new_path":"files/lfs/lfs_object.iso","old_path":"files/lfs/lfs_object.iso","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":8,"utf8_diff":"--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n def popen(cmd, path=nil)\n unless cmd.is_a?(Array)\n- raise \"System commands must be given as an array of strings\"\n+ raise RuntimeError, \"System commands must be given as an array of strings\"\n end\n \n path ||= Dir.pwd\n- vars = { \"PWD\" => path }\n- options = { chdir: path }\n+\n+ vars = {\n+ \"PWD\" => path\n+ }\n+\n+ options = {\n+ chdir: path\n+ }\n \n unless File.directory?(path)\n FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n @cmd_output = \"\"\n @cmd_status = 0\n+\n Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n @cmd_output << stdout.read\n @cmd_output << stderr.read\n","new_path":"files/ruby/popen.rb","old_path":"files/ruby/popen.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":9,"utf8_diff":"--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n end\n \n def archive_formats_regex\n- #|zip|tar| tar.gz | tar.bz2 |\n- /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+ /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n end\n \n def git_reference_regex\n # Valid git ref regex, see:\n # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n %r{\n (?!\n (?# doesn't begins with)\n","new_path":"files/ruby/regex.rb","old_path":"files/ruby/regex.rb","a_mode":"100644","b_mode":"100644","new_file":false,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":10,"utf8_diff":"--- /dev/null\n+++ b/files/whitespace\n@@ -0,0 +1 @@\n+test \n","new_path":"files/whitespace","old_path":"files/whitespace","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":11,"utf8_diff":"--- /dev/null\n+++ b/foo/bar/.gitkeep\n","new_path":"foo/bar/.gitkeep","old_path":"foo/bar/.gitkeep","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":12,"utf8_diff":"--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n","new_path":"gitlab-grack","old_path":"gitlab-grack","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":13,"utf8_diff":"--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n","new_path":"gitlab-shell","old_path":"gitlab-shell","a_mode":"0","b_mode":"160000","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false},{"merge_request_diff_id":14,"relative_order":14,"utf8_diff":"--- /dev/null\n+++ b/test\n","new_path":"test","old_path":"test","a_mode":"0","b_mode":"100644","new_file":true,"renamed_file":false,"deleted_file":false,"too_large":false}],"merge_request_id":14,"created_at":"2016-06-14T15:02:24.770Z","updated_at":"2016-06-14T15:02:25.007Z","base_commit_sha":"ae73cb07c9eeaf35924a10f713b364d32b2dd34f","real_size":"15"},"events":[{"id":224,"target_type":"MergeRequest","target_id":14,"project_id":36,"created_at":"2016-06-14T15:02:25.113Z","updated_at":"2016-06-14T15:02:25.113Z","action":1,"author_id":1},{"id":174,"target_type":"MergeRequest","target_id":14,"project_id":5,"created_at":"2016-06-14T15:02:25.113Z","updated_at":"2016-06-14T15:02:25.113Z","action":1,"author_id":20}]}
diff --git a/spec/fixtures/lib/gitlab/import_export/group/project.json b/spec/fixtures/lib/gitlab/import_export/group/project.json
index e8e1e53a86a..671ff92087b 100644
--- a/spec/fixtures/lib/gitlab/import_export/group/project.json
+++ b/spec/fixtures/lib/gitlab/import_export/group/project.json
@@ -205,6 +205,18 @@
"iid": 1,
"group_id": 100
},
+ "iteration": {
+ "created_at": "2022-08-15T12:55:42.607Z",
+ "updated_at": "2022-08-15T12:56:19.269Z",
+ "start_date": "2022-08-15",
+ "due_date": "2022-08-21",
+ "group_id": 260,
+ "iid": 5,
+ "description": "iteration description",
+ "iterations_cadence": {
+ "title": "iterations cadence"
+ }
+ },
"epic_issue": {
"id": 78,
"relative_position": 1073740323,
@@ -239,7 +251,26 @@
"due_date_sourcing_epic_id": null,
"milestone_id": null
}
- }
+ },
+ "resource_iteration_events": [
+ {
+ "user_id": 1,
+ "created_at": "2022-08-17T13:04:02.495Z",
+ "action": "add",
+ "iteration": {
+ "created_at": "2022-08-15T12:55:42.607Z",
+ "updated_at": "2022-08-15T12:56:19.269Z",
+ "start_date": "2022-08-15",
+ "due_date": "2022-08-21",
+ "group_id": 260,
+ "iid": 5,
+ "description": "iteration description",
+ "iterations_cadence": {
+ "title": "iterations cadence"
+ }
+ }
+ }
+ ]
}
],
"snippets": [
diff --git a/spec/fixtures/lib/gitlab/import_export/group/tree/project/issues.ndjson b/spec/fixtures/lib/gitlab/import_export/group/tree/project/issues.ndjson
index 4759e97228f..9596986dca0 100644
--- a/spec/fixtures/lib/gitlab/import_export/group/tree/project/issues.ndjson
+++ b/spec/fixtures/lib/gitlab/import_export/group/tree/project/issues.ndjson
@@ -1,3 +1,3 @@
{"id":1,"title":"Fugiat est minima quae maxime non similique.","assignee_id":null,"project_id":8,"author_id":1,"created_at":"2017-07-07T18:13:01.138Z","updated_at":"2017-08-15T18:37:40.807Z","branch_name":null,"description":"Quam totam fuga numquam in eveniet.","state":"opened","iid":1,"updated_by_id":1,"confidential":false,"due_date":null,"moved_to_id":null,"lock_version":null,"time_estimate":0,"closed_at":null,"last_edited_at":null,"last_edited_by_id":null,"group_milestone_id":null,"milestone":{"id":1,"title":"Project milestone","project_id":8,"description":"Project-level milestone","due_date":null,"created_at":"2016-06-14T15:02:04.415Z","updated_at":"2016-06-14T15:02:04.415Z","state":"active","iid":1,"group_id":null},"label_links":[{"id":11,"label_id":6,"target_id":1,"target_type":"Issue","created_at":"2017-08-15T18:37:40.795Z","updated_at":"2017-08-15T18:37:40.795Z","label":{"id":6,"title":"group label","color":"#A8D695","project_id":null,"created_at":"2017-08-15T18:37:19.698Z","updated_at":"2017-08-15T18:37:19.698Z","template":false,"description":"","group_id":5,"type":"GroupLabel","priorities":[]}},{"id":11,"label_id":2,"target_id":1,"target_type":"Issue","created_at":"2017-08-15T18:37:40.795Z","updated_at":"2017-08-15T18:37:40.795Z","label":{"id":6,"title":"A project label","color":"#A8D695","project_id":null,"created_at":"2017-08-15T18:37:19.698Z","updated_at":"2017-08-15T18:37:19.698Z","template":false,"description":"","group_id":5,"type":"ProjectLabel","priorities":[]}}]}
{"id":2,"title":"Fugiat est minima quae maxime non similique.","assignee_id":null,"project_id":8,"author_id":1,"created_at":"2017-07-07T18:13:01.138Z","updated_at":"2017-08-15T18:37:40.807Z","branch_name":null,"description":"Quam totam fuga numquam in eveniet.","state":"closed","iid":2,"updated_by_id":1,"confidential":false,"due_date":null,"moved_to_id":null,"lock_version":null,"time_estimate":0,"closed_at":null,"last_edited_at":null,"last_edited_by_id":null,"group_milestone_id":null,"milestone":{"id":2,"title":"A group milestone","description":"Group-level milestone","due_date":null,"created_at":"2016-06-14T15:02:04.415Z","updated_at":"2016-06-14T15:02:04.415Z","state":"active","iid":1,"group_id":100},"label_links":[{"id":11,"label_id":2,"target_id":1,"target_type":"Issue","created_at":"2017-08-15T18:37:40.795Z","updated_at":"2017-08-15T18:37:40.795Z","label":{"id":2,"title":"A project label","color":"#A8D695","project_id":null,"created_at":"2017-08-15T18:37:19.698Z","updated_at":"2017-08-15T18:37:19.698Z","template":false,"description":"","group_id":5,"type":"ProjectLabel","priorities":[]}}]}
-{"id":3,"title":"Issue with Epic","author_id":1,"project_id":8,"created_at":"2019-12-08T19:41:11.233Z","updated_at":"2019-12-08T19:41:53.194Z","position":0,"branch_name":null,"description":"Donec at nulla vitae sem molestie rutrum ut at sem.","state":"opened","iid":3,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"issue_assignees":[],"notes":[],"milestone":{"id":2,"title":"A group milestone","description":"Group-level milestone","due_date":null,"created_at":"2016-06-14T15:02:04.415Z","updated_at":"2016-06-14T15:02:04.415Z","state":"active","iid":1,"group_id":100},"epic_issue":{"id":78,"relative_position":1073740323,"epic":{"id":1,"group_id":5,"author_id":1,"assignee_id":null,"iid":1,"updated_by_id":null,"last_edited_by_id":null,"lock_version":0,"start_date":null,"end_date":null,"last_edited_at":null,"created_at":"2019-12-08T19:37:07.098Z","updated_at":"2019-12-08T19:43:11.568Z","title":"An epic","description":null,"start_date_sourcing_milestone_id":null,"due_date_sourcing_milestone_id":null,"start_date_fixed":null,"due_date_fixed":null,"start_date_is_fixed":null,"due_date_is_fixed":null,"closed_by_id":null,"closed_at":null,"parent_id":null,"relative_position":null,"state_id":"opened","start_date_sourcing_epic_id":null,"due_date_sourcing_epic_id":null,"milestone_id":null}}}
+{"id":3,"title":"Issue with Epic","author_id":1,"project_id":8,"created_at":"2019-12-08T19:41:11.233Z","updated_at":"2019-12-08T19:41:53.194Z","position":0,"branch_name":null,"description":"Donec at nulla vitae sem molestie rutrum ut at sem.","state":"opened","iid":3,"updated_by_id":null,"confidential":false,"due_date":null,"moved_to_id":null,"issue_assignees":[],"notes":[],"milestone":{"id":2,"title":"A group milestone","description":"Group-level milestone","due_date":null,"created_at":"2016-06-14T15:02:04.415Z","updated_at":"2016-06-14T15:02:04.415Z","state":"active","iid":1,"group_id":100},"iteration":{"created_at":"2022-08-15T12:55:42.607Z","updated_at":"2022-08-15T12:56:19.269Z","start_date":"2022-08-15","due_date":"2022-08-21","group_id":260,"iid":5,"description":"iteration description","iterations_cadence":{"title":"iterations cadence"}},"epic_issue":{"id":78,"relative_position":1073740323,"epic":{"id":1,"group_id":5,"author_id":1,"assignee_id":null,"iid":1,"updated_by_id":null,"last_edited_by_id":null,"lock_version":0,"start_date":null,"end_date":null,"last_edited_at":null,"created_at":"2019-12-08T19:37:07.098Z","updated_at":"2019-12-08T19:43:11.568Z","title":"An epic","description":null,"start_date_sourcing_milestone_id":null,"due_date_sourcing_milestone_id":null,"start_date_fixed":null,"due_date_fixed":null,"start_date_is_fixed":null,"due_date_is_fixed":null,"closed_by_id":null,"closed_at":null,"parent_id":null,"relative_position":null,"state_id":"opened","start_date_sourcing_epic_id":null,"due_date_sourcing_epic_id":null,"milestone_id":null}},"resource_iteration_events":[{"user_id":1,"created_at":"2022-08-17T13:04:02.495Z","action":"add","iteration":{"created_at":"2022-08-15T12:55:42.607Z","updated_at":"2022-08-15T12:56:19.269Z","start_date":"2022-08-15","due_date":"2022-08-21","group_id":260,"iid":5,"description":"iteration description","iterations_cadence":{"title":"iterations cadence"}}}]}
diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb
index 18cd63b7bcb..14885813d93 100644
--- a/spec/fixtures/markdown.md.erb
+++ b/spec/fixtures/markdown.md.erb
@@ -283,7 +283,23 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e
- [x] Complete sub-task 1
- [X] Complete task 2
-#### Gollum Tags
+### Math
+
+- Dollar math: $a^2 + b^2 = c^2$
+- Dollar math and snippet reference: $d^2 + e^2 = f^2$ and <%= snippet.to_reference %>
+- Dollar math and snippet in another project: <%= xsnippet.to_reference(project) %> and $g^2 + h^2 = i^2$
+- Not dollar math: $20,000 and $30,000
+- Dollar-backtick math: $`j^2 + k^2 = l^2`$
+- Dollar display math: $$m^2 + n^2 = o^2$$
+- Dollar display math and snippet reference: $$p^2 + q^2 = r^2$$ and <%= snippet.to_reference %>
+- Dollar math and snippet in another project: <%= xsnippet.to_reference(project) %> and $$s^2 + t^2 = u^2$$
+- Display math using a block
+
+ ```math
+ v^2 + w^2 = x^2
+ ```
+
+### Gollum Tags
- [[linked-resource]]
- [[link-text|linked-resource]]
@@ -326,15 +342,15 @@ However the wrapping tags cannot be mixed as such:
### Colors
-`#F00`
-`#F00A`
-`#FF0000`
-`#FF0000AA`
-`RGB(0,255,0)`
-`RGB(0%,100%,0%)`
-`RGBA(0,255,0,0.7)`
-`HSL(540,70%,50%)`
-`HSLA(540,70%,50%,0.7)`
+`#F00`
+`#F00A`
+`#FF0000`
+`#FF0000AA`
+`RGB(0,255,0)`
+`RGB(0%,100%,0%)`
+`RGBA(0,255,0,0.7)`
+`HSL(540,70%,50%)`
+`HSLA(540,70%,50%,0.7)`
### Mermaid
diff --git a/spec/fixtures/markdown/markdown_golden_master_examples.yml b/spec/fixtures/markdown/markdown_golden_master_examples.yml
index a1ad88ef69c..495d00026d7 100644
--- a/spec/fixtures/markdown/markdown_golden_master_examples.yml
+++ b/spec/fixtures/markdown/markdown_golden_master_examples.yml
@@ -773,7 +773,7 @@
markdown: |-
Hi @gfm_user - thank you for reporting this bug (#1) we hope to fix it in %1.1 as part of !1
html: |-
- <p data-sourcepos="1:1-1:92" dir="auto">Hi <a href="/gfm_user" data-user="1" data-reference-type="user" data-container="body" data-placement="top" class="gfm gfm-project_member js-user-link" title="John Doe1">@gfm_user</a> - thank you for reporting this bug (<a href="/group1/project1/-/issues/1" data-original="#1" data-link="false" data-link-reference="false" data-project="11" data-issue="11" data-project-path="group1/project1" data-iid="1" data-issue-type="issue" data-reference-type="issue" data-container="body" data-placement="top" title="My title 1" class="gfm gfm-issue">#1</a>) we hope to fix it in <a href="/group1/project1/-/milestones/1" data-original="%1.1" data-link="false" data-link-reference="false" data-project="11" data-milestone="11" data-reference-type="milestone" data-container="body" data-placement="top" title="" class="gfm gfm-milestone has-tooltip">%1.1</a> as part of <a href="/group1/project1/-/merge_requests/1" data-original="!1" data-link="false" data-link-reference="false" data-project="11" data-merge-request="11" data-project-path="group1/project1" data-iid="1" data-reference-type="merge_request" data-container="body" data-placement="top" title="My title 2" class="gfm gfm-merge_request">!1</a></p>
+ <p data-sourcepos="1:1-1:92" dir="auto">Hi <a href="/gfm_user" data-reference-type="user" data-user="1" data-container="body" data-placement="top" class="gfm gfm-project_member js-user-link" title="John Doe1">@gfm_user</a> - thank you for reporting this bug (<a href="/group1/project1/-/issues/1" data-reference-type="issue" data-original="#1" data-link="false" data-link-reference="false" data-project="11" data-issue="11" data-project-path="group1/project1" data-iid="1" data-issue-type="issue" data-container="body" data-placement="top" title="My title 1" class="gfm gfm-issue">#1</a>) we hope to fix it in <a href="/group1/project1/-/milestones/1" data-reference-type="milestone" data-original="%1.1" data-link="false" data-link-reference="false" data-project="11" data-milestone="11" data-container="body" data-placement="top" title="" class="gfm gfm-milestone has-tooltip">%1.1</a> as part of <a href="/group1/project1/-/merge_requests/1" data-reference-type="merge_request" data-original="!1" data-link="false" data-link-reference="false" data-project="11" data-merge-request="11" data-project-path="group1/project1" data-iid="1" data-container="body" data-placement="top" title="My title 2" class="gfm gfm-merge_request">!1</a></p>
- name: strike
markdown: |-
~~del~~
diff --git a/spec/fixtures/packages/debian/distribution/D-I-Packages b/spec/fixtures/packages/debian/distribution/D-I-Packages
new file mode 100644
index 00000000000..80272e3a12c
--- /dev/null
+++ b/spec/fixtures/packages/debian/distribution/D-I-Packages
@@ -0,0 +1,2 @@
+Package: example-package
+Description: This is an incomplete D-I Packages file
diff --git a/spec/fixtures/packages/debian/distribution/OtherSHA256 b/spec/fixtures/packages/debian/distribution/OtherSHA256
new file mode 100644
index 00000000000..5c282d72c11
--- /dev/null
+++ b/spec/fixtures/packages/debian/distribution/OtherSHA256
@@ -0,0 +1 @@
+Other SHA256 \ No newline at end of file
diff --git a/spec/fixtures/packages/debian/distribution/Sources b/spec/fixtures/packages/debian/distribution/Sources
new file mode 100644
index 00000000000..1097f1b1aff
--- /dev/null
+++ b/spec/fixtures/packages/debian/distribution/Sources
@@ -0,0 +1,2 @@
+Package: example-package
+Description: This is an incomplete Sources file
diff --git a/spec/fixtures/packages/rpm/hello-0.0.1-1.fc29.x86_64.rpm b/spec/fixtures/packages/rpm/hello-0.0.1-1.fc29.x86_64.rpm
new file mode 100644
index 00000000000..bff3193a9e8
--- /dev/null
+++ b/spec/fixtures/packages/rpm/hello-0.0.1-1.fc29.x86_64.rpm
Binary files differ
diff --git a/spec/fixtures/security_reports/deprecated/gl-sast-report.json b/spec/fixtures/security_reports/deprecated/gl-sast-report.json
index 2f7e47281e2..c5b0148fe3e 100644
--- a/spec/fixtures/security_reports/deprecated/gl-sast-report.json
+++ b/spec/fixtures/security_reports/deprecated/gl-sast-report.json
@@ -961,4 +961,4 @@
"url": "https://cwe.mitre.org/data/definitions/120.html",
"tool": "flawfinder"
}
-]
+] \ No newline at end of file
diff --git a/spec/fixtures/security_reports/feature-branch/gl-sast-report.json b/spec/fixtures/security_reports/feature-branch/gl-sast-report.json
index f93233e0ebb..51761583c70 100644
--- a/spec/fixtures/security_reports/feature-branch/gl-sast-report.json
+++ b/spec/fixtures/security_reports/feature-branch/gl-sast-report.json
@@ -174,4 +174,4 @@
"start_time": "placeholder-value",
"end_time": "placeholder-value"
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/security_reports/feature-branch/gl-secret-detection-report.json b/spec/fixtures/security_reports/feature-branch/gl-secret-detection-report.json
index 538364f84a2..4862a504cec 100644
--- a/spec/fixtures/security_reports/feature-branch/gl-secret-detection-report.json
+++ b/spec/fixtures/security_reports/feature-branch/gl-secret-detection-report.json
@@ -2,4 +2,4 @@
"version": "14.1.2",
"vulnerabilities": [],
"remediations": []
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/security_reports/master/gl-common-scanning-report-names.json b/spec/fixtures/security_reports/master/gl-common-scanning-report-names.json
index 3cfb3e51ef7..ef2ff7443d3 100644
--- a/spec/fixtures/security_reports/master/gl-common-scanning-report-names.json
+++ b/spec/fixtures/security_reports/master/gl-common-scanning-report-names.json
@@ -165,4 +165,4 @@
"end_time": "placeholder-value",
"status": "success"
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/security_reports/master/gl-common-scanning-report-without-top-level-scanner.json b/spec/fixtures/security_reports/master/gl-common-scanning-report-without-top-level-scanner.json
new file mode 100644
index 00000000000..417dc960aff
--- /dev/null
+++ b/spec/fixtures/security_reports/master/gl-common-scanning-report-without-top-level-scanner.json
@@ -0,0 +1,50 @@
+{
+ "vulnerabilities": [
+ {
+ "category": "dependency_scanning",
+ "name": "Vulnerability for remediation testing 1",
+ "message": "This vulnerability should have ONE remediation",
+ "description": "",
+ "cve": "CVE-2137",
+ "severity": "High",
+ "solution": "Upgrade to latest version.",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
+ },
+ "location": {},
+ "identifiers": [
+ {
+ "type": "GitLab",
+ "name": "Foo vulnerability",
+ "value": "foo"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2137"
+ }
+ ],
+ "details": {
+ "commit": {
+ "name": [
+ {
+ "lang": "en",
+ "value": "The Commit"
+ }
+ ],
+ "description": [
+ {
+ "lang": "en",
+ "value": "Commit where the vulnerability was identified"
+ }
+ ],
+ "type": "commit",
+ "value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
+ }
+ }
+ }
+ ],
+ "dependency_files": [],
+ "version": "14.0.2"
+} \ No newline at end of file
diff --git a/spec/fixtures/security_reports/master/gl-common-scanning-report.json b/spec/fixtures/security_reports/master/gl-common-scanning-report.json
index 787573301bb..1295b44d4df 100644
--- a/spec/fixtures/security_reports/master/gl-common-scanning-report.json
+++ b/spec/fixtures/security_reports/master/gl-common-scanning-report.json
@@ -1,5 +1,6 @@
{
- "vulnerabilities": [{
+ "vulnerabilities": [
+ {
"category": "dependency_scanning",
"name": "Vulnerability for remediation testing 1",
"message": "This vulnerability should have ONE remediation",
@@ -12,24 +13,32 @@
"name": "Gemnasium"
},
"location": {},
- "identifiers": [{
- "type": "GitLab",
- "name": "Foo vulnerability",
- "value": "foo"
- }],
- "links": [{
- "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2137"
- }],
+ "identifiers": [
+ {
+ "type": "GitLab",
+ "name": "Foo vulnerability",
+ "value": "foo"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2137"
+ }
+ ],
"details": {
"commit": {
- "name": [{
- "lang": "en",
- "value": "The Commit"
- }],
- "description": [{
- "lang": "en",
- "value": "Commit where the vulnerability was identified"
- }],
+ "name": [
+ {
+ "lang": "en",
+ "value": "The Commit"
+ }
+ ],
+ "description": [
+ {
+ "lang": "en",
+ "value": "Commit where the vulnerability was identified"
+ }
+ ],
"type": "commit",
"value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
}
@@ -48,24 +57,32 @@
"name": "Gemnasium"
},
"location": {},
- "identifiers": [{
- "type": "GitLab",
- "name": "Foo vulnerability",
- "value": "foo"
- }],
- "links": [{
- "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2138"
- }],
+ "identifiers": [
+ {
+ "type": "GitLab",
+ "name": "Foo vulnerability",
+ "value": "foo"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2138"
+ }
+ ],
"details": {
"commit": {
- "name": [{
- "lang": "en",
- "value": "The Commit"
- }],
- "description": [{
- "lang": "en",
- "value": "Commit where the vulnerability was identified"
- }],
+ "name": [
+ {
+ "lang": "en",
+ "value": "The Commit"
+ }
+ ],
+ "description": [
+ {
+ "lang": "en",
+ "value": "Commit where the vulnerability was identified"
+ }
+ ],
"type": "commit",
"value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
}
@@ -84,24 +101,32 @@
"name": "Gemnasium"
},
"location": {},
- "identifiers": [{
- "type": "GitLab",
- "name": "Foo vulnerability",
- "value": "foo"
- }],
- "links": [{
- "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2139"
- }],
+ "identifiers": [
+ {
+ "type": "GitLab",
+ "name": "Foo vulnerability",
+ "value": "foo"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2139"
+ }
+ ],
"details": {
"commit": {
- "name": [{
- "lang": "en",
- "value": "The Commit"
- }],
- "description": [{
- "lang": "en",
- "value": "Commit where the vulnerability was identified"
- }],
+ "name": [
+ {
+ "lang": "en",
+ "value": "The Commit"
+ }
+ ],
+ "description": [
+ {
+ "lang": "en",
+ "value": "Commit where the vulnerability was identified"
+ }
+ ],
"type": "commit",
"value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
}
@@ -120,24 +145,32 @@
"name": "Gemnasium"
},
"location": {},
- "identifiers": [{
- "type": "GitLab",
- "name": "Foo vulnerability",
- "value": "foo"
- }],
- "links": [{
- "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2140"
- }],
+ "identifiers": [
+ {
+ "type": "GitLab",
+ "name": "Foo vulnerability",
+ "value": "foo"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-2140"
+ }
+ ],
"details": {
"commit": {
- "name": [{
- "lang": "en",
- "value": "The Commit"
- }],
- "description": [{
- "lang": "en",
- "value": "Commit where the vulnerability was identified"
- }],
+ "name": [
+ {
+ "lang": "en",
+ "value": "The Commit"
+ }
+ ],
+ "description": [
+ {
+ "lang": "en",
+ "value": "Commit where the vulnerability was identified"
+ }
+ ],
"type": "commit",
"value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
}
@@ -162,30 +195,37 @@
},
"summary": "The Origin header was changed to an invalid value of http://peachapisecurity.com and the response contained an Access-Control-Allow-Origin header which included this invalid Origin, indicating that the CORS configuration on the server is overly permissive.\n\n\n",
"request": {
- "headers": [{
- "name": "Host",
- "value": "127.0.0.1:7777"
- }],
+ "headers": [
+ {
+ "name": "Host",
+ "value": "127.0.0.1:7777"
+ }
+ ],
"method": "GET",
"url": "http://127.0.0.1:7777/api/users",
"body": ""
},
"response": {
- "headers": [{
- "name": "Server",
- "value": "TwistedWeb/20.3.0"
- }],
+ "headers": [
+ {
+ "name": "Server",
+ "value": "TwistedWeb/20.3.0"
+ }
+ ],
"reason_phrase": "OK",
"status_code": 200,
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
},
- "supporting_messages": [{
+ "supporting_messages": [
+ {
"name": "Origional",
"request": {
- "headers": [{
- "name": "Host",
- "value": "127.0.0.1:7777"
- }],
+ "headers": [
+ {
+ "name": "Host",
+ "value": "127.0.0.1:7777"
+ }
+ ],
"method": "GET",
"url": "http://127.0.0.1:7777/api/users",
"body": ""
@@ -194,19 +234,23 @@
{
"name": "Recorded",
"request": {
- "headers": [{
- "name": "Host",
- "value": "127.0.0.1:7777"
- }],
+ "headers": [
+ {
+ "name": "Host",
+ "value": "127.0.0.1:7777"
+ }
+ ],
"method": "GET",
"url": "http://127.0.0.1:7777/api/users",
"body": ""
},
"response": {
- "headers": [{
- "name": "Server",
- "value": "TwistedWeb/20.3.0"
- }],
+ "headers": [
+ {
+ "name": "Server",
+ "value": "TwistedWeb/20.3.0"
+ }
+ ],
"reason_phrase": "OK",
"status_code": 200,
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
@@ -215,24 +259,32 @@
]
},
"location": {},
- "identifiers": [{
- "type": "GitLab",
- "name": "Foo vulnerability",
- "value": "foo"
- }],
- "links": [{
- "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1020"
- }],
+ "identifiers": [
+ {
+ "type": "GitLab",
+ "name": "Foo vulnerability",
+ "value": "foo"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1020"
+ }
+ ],
"details": {
"commit": {
- "name": [{
- "lang": "en",
- "value": "The Commit"
- }],
- "description": [{
- "lang": "en",
- "value": "Commit where the vulnerability was identified"
- }],
+ "name": [
+ {
+ "lang": "en",
+ "value": "The Commit"
+ }
+ ],
+ "description": [
+ {
+ "lang": "en",
+ "value": "Commit where the vulnerability was identified"
+ }
+ ],
"type": "commit",
"value": "41df7b7eb3be2b5be2c406c2f6d28cd6631eeb19"
}
@@ -258,30 +310,37 @@
},
"summary": "The Origin header was changed to an invalid value of http://peachapisecurity.com and the response contained an Access-Control-Allow-Origin header which included this invalid Origin, indicating that the CORS configuration on the server is overly permissive.\n\n\n",
"request": {
- "headers": [{
- "name": "Host",
- "value": "127.0.0.1:7777"
- }],
+ "headers": [
+ {
+ "name": "Host",
+ "value": "127.0.0.1:7777"
+ }
+ ],
"method": "GET",
"url": "http://127.0.0.1:7777/api/users",
"body": ""
},
"response": {
- "headers": [{
- "name": "Server",
- "value": "TwistedWeb/20.3.0"
- }],
+ "headers": [
+ {
+ "name": "Server",
+ "value": "TwistedWeb/20.3.0"
+ }
+ ],
"reason_phrase": "OK",
"status_code": 200,
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
},
- "supporting_messages": [{
+ "supporting_messages": [
+ {
"name": "Origional",
"request": {
- "headers": [{
- "name": "Host",
- "value": "127.0.0.1:7777"
- }],
+ "headers": [
+ {
+ "name": "Host",
+ "value": "127.0.0.1:7777"
+ }
+ ],
"method": "GET",
"url": "http://127.0.0.1:7777/api/users",
"body": ""
@@ -290,19 +349,23 @@
{
"name": "Recorded",
"request": {
- "headers": [{
- "name": "Host",
- "value": "127.0.0.1:7777"
- }],
+ "headers": [
+ {
+ "name": "Host",
+ "value": "127.0.0.1:7777"
+ }
+ ],
"method": "GET",
"url": "http://127.0.0.1:7777/api/users",
"body": ""
},
"response": {
- "headers": [{
- "name": "Server",
- "value": "TwistedWeb/20.3.0"
- }],
+ "headers": [
+ {
+ "name": "Server",
+ "value": "TwistedWeb/20.3.0"
+ }
+ ],
"reason_phrase": "OK",
"status_code": 200,
"body": "[{\"user_id\":1,\"user\":\"admin\",\"first\":\"Joe\",\"last\":\"Smith\",\"password\":\"Password!\"}]"
@@ -311,15 +374,19 @@
]
},
"location": {},
- "identifiers": [{
- "type": "GitLab",
- "name": "Bar vulnerability",
- "value": "bar"
- }],
- "links": [{
- "name": "CVE-1030",
- "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1030"
- }]
+ "identifiers": [
+ {
+ "type": "GitLab",
+ "name": "Bar vulnerability",
+ "value": "bar"
+ }
+ ],
+ "links": [
+ {
+ "name": "CVE-1030",
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-1030"
+ }
+ ]
},
{
"category": "dependency_scanning",
@@ -338,57 +405,73 @@
"links": []
}
],
- "remediations": [{
- "fixes": [{
- "cve": "CVE-2137"
- }],
+ "remediations": [
+ {
+ "fixes": [
+ {
+ "cve": "CVE-2137"
+ }
+ ],
"summary": "this remediates CVE-2137",
"diff": "dG90YWxseSBsZWdpdCBkaWZm"
},
{
- "fixes": [{
- "cve": "CVE-2138"
- }],
+ "fixes": [
+ {
+ "cve": "CVE-2138"
+ }
+ ],
"summary": "this remediates CVE-2138",
"diff": "dG90YWxseSBsZWdpdCBkaWZm"
},
{
- "fixes": [{
- "cve": "CVE-2139"
- }, {
- "cve": "CVE-2140"
- }],
+ "fixes": [
+ {
+ "cve": "CVE-2139"
+ },
+ {
+ "cve": "CVE-2140"
+ }
+ ],
"summary": "this remediates CVE-2139 and CVE-2140",
"diff": "dG90YWxseSBsZWdpdGltYXRlIGRpZmYsIDEwLzEwIHdvdWxkIGFwcGx5"
},
{
- "fixes": [{
- "cve": "CVE-1020"
- }],
+ "fixes": [
+ {
+ "cve": "CVE-1020"
+ }
+ ],
"summary": "",
"diff": ""
},
{
- "fixes": [{
- "cve": "CVE",
- "id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3"
- }],
+ "fixes": [
+ {
+ "cve": "CVE",
+ "id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3"
+ }
+ ],
"summary": "",
"diff": ""
},
{
- "fixes": [{
- "cve": "CVE",
- "id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3"
- }],
+ "fixes": [
+ {
+ "cve": "CVE",
+ "id": "bb2fbeb1b71ea360ce3f86f001d4e84823c3ffe1a1f7d41ba7466b14cfa953d3"
+ }
+ ],
"summary": "",
"diff": ""
},
{
- "fixes": [{
- "id": "2134",
- "cve": "CVE-1"
- }],
+ "fixes": [
+ {
+ "id": "2134",
+ "cve": "CVE-1"
+ }
+ ],
"summary": "",
"diff": ""
}
@@ -406,7 +489,7 @@
},
"scanner": {
"id": "gemnasium",
- "name": "Gemnasium",
+ "name": "Gemnasium top-level",
"url": "https://gitlab.com/gitlab-org/security-products/analyzers/gemnasium-maven",
"vendor": {
"name": "GitLab"
@@ -419,4 +502,4 @@
"status": "success"
},
"version": "14.0.2"
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/security_reports/master/gl-sast-missing-scanner.json b/spec/fixtures/security_reports/master/gl-sast-missing-scanner.json
index ab3ee348263..fcfd9b831f4 100644
--- a/spec/fixtures/security_reports/master/gl-sast-missing-scanner.json
+++ b/spec/fixtures/security_reports/master/gl-sast-missing-scanner.json
@@ -799,4 +799,4 @@
"url": "https://cwe.mitre.org/data/definitions/120.html"
}
]
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/security_reports/master/gl-sast-report-bandit.json b/spec/fixtures/security_reports/master/gl-sast-report-bandit.json
index a80833354ed..d0346479b85 100644
--- a/spec/fixtures/security_reports/master/gl-sast-report-bandit.json
+++ b/spec/fixtures/security_reports/master/gl-sast-report-bandit.json
@@ -40,4 +40,4 @@
"end_time": "2022-03-11T00:21:50",
"status": "success"
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/security_reports/master/gl-sast-report-gosec.json b/spec/fixtures/security_reports/master/gl-sast-report-gosec.json
index 42986ea1045..4c385326c8c 100644
--- a/spec/fixtures/security_reports/master/gl-sast-report-gosec.json
+++ b/spec/fixtures/security_reports/master/gl-sast-report-gosec.json
@@ -65,4 +65,4 @@
"end_time": "2022-03-15T20:33:17",
"status": "success"
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/security_reports/master/gl-sast-report-minimal.json b/spec/fixtures/security_reports/master/gl-sast-report-minimal.json
index 60a67453c9b..5e9273d43b1 100644
--- a/spec/fixtures/security_reports/master/gl-sast-report-minimal.json
+++ b/spec/fixtures/security_reports/master/gl-sast-report-minimal.json
@@ -65,4 +65,4 @@
"start_time": "placeholder-value",
"end_time": "placeholder-value"
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-bandit.json b/spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-bandit.json
index 2a60a75366e..037b9fb8d3e 100644
--- a/spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-bandit.json
+++ b/spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-bandit.json
@@ -68,4 +68,4 @@
"end_time": "2022-03-11T18:48:22",
"status": "success"
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-gosec.json b/spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-gosec.json
index 3d8c65d5823..f01d26a69c9 100644
--- a/spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-gosec.json
+++ b/spec/fixtures/security_reports/master/gl-sast-report-semgrep-for-gosec.json
@@ -67,4 +67,4 @@
"end_time": "2022-03-15T20:37:05",
"status": "success"
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/security_reports/master/gl-sast-report.json b/spec/fixtures/security_reports/master/gl-sast-report.json
index 63504e6fccc..1aa8db1a65f 100644
--- a/spec/fixtures/security_reports/master/gl-sast-report.json
+++ b/spec/fixtures/security_reports/master/gl-sast-report.json
@@ -197,4 +197,4 @@
"start_time": "placeholder-value",
"end_time": "placeholder-value"
}
-}
+} \ No newline at end of file
diff --git a/spec/fixtures/security_reports/master/gl-secret-detection-report.json b/spec/fixtures/security_reports/master/gl-secret-detection-report.json
index 9b0b2a19beb..21d4f3f1798 100644
--- a/spec/fixtures/security_reports/master/gl-secret-detection-report.json
+++ b/spec/fixtures/security_reports/master/gl-secret-detection-report.json
@@ -30,4 +30,4 @@
}
],
"remediations": []
-}
+} \ No newline at end of file
diff --git a/spec/frontend/__helpers__/datetime_helpers.js b/spec/frontend/__helpers__/datetime_helpers.js
index 25dbd1d477d..cbe627b7968 100644
--- a/spec/frontend/__helpers__/datetime_helpers.js
+++ b/spec/frontend/__helpers__/datetime_helpers.js
@@ -1,4 +1,4 @@
-import dateFormat from 'dateformat';
+import dateFormat from '~/lib/dateformat';
/**
* Returns a date object corresponding to the given date string.
diff --git a/spec/frontend/__helpers__/dl_locator_helper.js b/spec/frontend/__helpers__/dl_locator_helper.js
index b507dcd599d..591c034be9b 100644
--- a/spec/frontend/__helpers__/dl_locator_helper.js
+++ b/spec/frontend/__helpers__/dl_locator_helper.js
@@ -19,10 +19,13 @@ import { createWrapper, ErrorWrapper } from '@vue/test-utils';
* @returns Wrapper
*/
export const findDd = (dtLabel, wrapper) => {
- const dt = wrapper.findByText(dtLabel).element;
- const dd = dt.nextElementSibling;
- if (dt.tagName === 'DT' && dd.tagName === 'DD') {
- return createWrapper(dd, {});
+ const dtw = wrapper.findByText(dtLabel);
+ if (dtw.exists()) {
+ const dt = dtw.element;
+ const dd = dt.nextElementSibling;
+ if (dt.tagName === 'DT' && dd.tagName === 'DD') {
+ return createWrapper(dd, {});
+ }
}
- return ErrorWrapper(dtLabel);
+ return new ErrorWrapper(dtLabel);
};
diff --git a/spec/frontend/__helpers__/keep_alive_component_helper_spec.js b/spec/frontend/__helpers__/keep_alive_component_helper_spec.js
index dcccc14f396..54d397d0997 100644
--- a/spec/frontend/__helpers__/keep_alive_component_helper_spec.js
+++ b/spec/frontend/__helpers__/keep_alive_component_helper_spec.js
@@ -17,16 +17,16 @@ describe('keepAlive', () => {
});
it('converts a component to a keep-alive component', async () => {
- const { element } = wrapper.find(component);
+ const { element } = wrapper.findComponent(component);
await wrapper.vm.deactivate();
- expect(wrapper.find(component).exists()).toBe(false);
+ expect(wrapper.findComponent(component).exists()).toBe(false);
await wrapper.vm.activate();
// assert that when the component is destroyed and re-rendered, the
// newly rendered component has the reference to the old component
// (i.e. the old component was deactivated and activated)
- expect(wrapper.find(component).element).toBe(element);
+ expect(wrapper.findComponent(component).element).toBe(element);
});
});
diff --git a/spec/frontend/__helpers__/matchers/to_validate_json_schema_spec.js b/spec/frontend/__helpers__/matchers/to_validate_json_schema_spec.js
index fd42c710c65..e6096221528 100644
--- a/spec/frontend/__helpers__/matchers/to_validate_json_schema_spec.js
+++ b/spec/frontend/__helpers__/matchers/to_validate_json_schema_spec.js
@@ -38,7 +38,7 @@ describe('custom matcher toValidateJsonSchema', () => {
});
it('throws if not matching', () => {
- expect(() => expect(null).toValidateJsonSchema(schema)).toThrowError(
+ expect(() => expect(null).toValidateJsonSchema(schema)).toThrow(
`Expected the given data to pass the schema validation, but found that it was considered invalid. Errors:
Error with item : must be object`,
);
@@ -57,7 +57,7 @@ Error with item : must be object`,
});
it('throws if matching', () => {
- expect(() => expect({ fruit: 'apple' }).not.toValidateJsonSchema(schema)).toThrowError(
+ expect(() => expect({ fruit: 'apple' }).not.toValidateJsonSchema(schema)).toThrow(
'Expected the given data not to pass the schema validation, but found that it was considered valid.',
);
});
diff --git a/spec/frontend/__helpers__/shared_test_setup.js b/spec/frontend/__helpers__/shared_test_setup.js
index 4d6486544ca..45a7b8e0352 100644
--- a/spec/frontend/__helpers__/shared_test_setup.js
+++ b/spec/frontend/__helpers__/shared_test_setup.js
@@ -48,9 +48,6 @@ testUtilsConfig.deprecationWarningHandler = (method, message) => {
const ALLOWED_DEPRECATED_METHODS = [
// https://gitlab.com/gitlab-org/gitlab/-/issues/295679
'finding components with `find` or `get`',
-
- // https://gitlab.com/gitlab-org/gitlab/-/issues/295680
- 'finding components with `findAll`',
];
if (!ALLOWED_DEPRECATED_METHODS.includes(method)) {
global.console.error(message);
diff --git a/spec/frontend/__mocks__/sortablejs/index.js b/spec/frontend/__mocks__/sortablejs/index.js
index 5039af54542..d8bc8ae9bda 100644
--- a/spec/frontend/__mocks__/sortablejs/index.js
+++ b/spec/frontend/__mocks__/sortablejs/index.js
@@ -1,4 +1,4 @@
-const Sortablejs = jest.genMockFromModule('sortablejs');
+const Sortablejs = jest.createMockFromModule('sortablejs');
export default Sortablejs;
export const Sortable = Sortablejs;
diff --git a/spec/frontend/access_tokens/components/access_token_table_app_spec.js b/spec/frontend/access_tokens/components/access_token_table_app_spec.js
index 6013fa3ec39..aed3db4aa4c 100644
--- a/spec/frontend/access_tokens/components/access_token_table_app_spec.js
+++ b/spec/frontend/access_tokens/components/access_token_table_app_spec.js
@@ -190,6 +190,21 @@ describe('~/access_tokens/components/access_token_table_app', () => {
expect(button.props('category')).toBe('tertiary');
});
+ describe('revoke path', () => {
+ beforeEach(() => {
+ createComponent({ showRole: true });
+ });
+
+ it.each([{ revoke_path: null }, { revoke_path: undefined }])(
+ 'with %p, does not show revoke button',
+ async (input) => {
+ await triggerSuccess(defaultActiveAccessTokens.map((data) => ({ ...data, ...input })));
+
+ expect(findCells().at(6).findComponent(GlButton).exists()).toBe(false);
+ },
+ );
+ });
+
it('sorts rows alphabetically', async () => {
createComponent({ showRole: true });
await triggerSuccess();
diff --git a/spec/frontend/access_tokens/components/expires_at_field_spec.js b/spec/frontend/access_tokens/components/expires_at_field_spec.js
index 646dc0d703f..491d2a0e323 100644
--- a/spec/frontend/access_tokens/components/expires_at_field_spec.js
+++ b/spec/frontend/access_tokens/components/expires_at_field_spec.js
@@ -58,4 +58,20 @@ describe('~/access_tokens/components/expires_at_field', () => {
expect(findDatepicker().props('defaultDate')).toStrictEqual(future);
});
+
+ it('should set the default expiration date to be 365 days', () => {
+ const offset = 365;
+ const today = new Date();
+ const future = getDateInFuture(today, offset);
+ createComponent({ defaultDateOffset: offset });
+
+ expect(findDatepicker().props('defaultDate')).toStrictEqual(future);
+ });
+
+ it('should set the default expiration date to maxDate, ignoring defaultDateOffset', () => {
+ const maxDate = new Date();
+ createComponent({ maxDate, defaultDateOffset: 2 });
+
+ expect(findDatepicker().props('defaultDate')).toStrictEqual(maxDate);
+ });
});
diff --git a/spec/frontend/access_tokens/components/new_access_token_app_spec.js b/spec/frontend/access_tokens/components/new_access_token_app_spec.js
index 9ccadbebf7a..d12d200d214 100644
--- a/spec/frontend/access_tokens/components/new_access_token_app_spec.js
+++ b/spec/frontend/access_tokens/components/new_access_token_app_spec.js
@@ -23,18 +23,27 @@ describe('~/access_tokens/components/new_access_token_app', () => {
};
const triggerSuccess = async (newToken = 'new token') => {
- wrapper.find(DomElementListener).vm.$emit(EVENT_SUCCESS, { detail: [{ new_token: newToken }] });
+ wrapper
+ .findComponent(DomElementListener)
+ .vm.$emit(EVENT_SUCCESS, { detail: [{ new_token: newToken }] });
await nextTick();
};
const triggerError = async (errors = ['1', '2']) => {
- wrapper.find(DomElementListener).vm.$emit(EVENT_ERROR, { detail: [{ errors }] });
+ wrapper.findComponent(DomElementListener).vm.$emit(EVENT_ERROR, { detail: [{ errors }] });
await nextTick();
};
beforeEach(() => {
// NewAccessTokenApp observes a form element
- setHTMLFixture(`<form id="${FORM_SELECTOR.slice(1)}"><input type="submit"/></form>`);
+ setHTMLFixture(
+ `<form id="${FORM_SELECTOR.slice(1)}">
+ <input type="text" id="expires_at" value="2022-01-01"/>
+ <input type="text" value='1'/>
+ <input type="checkbox" checked/>
+ <input type="submit" value="Create"/>
+ </form>`,
+ );
createComponent();
});
@@ -78,7 +87,6 @@ describe('~/access_tokens/components/new_access_token_app', () => {
.findByLabelText(sprintf(__('Your new %{accessTokenType}'), { accessTokenType }))
.attributes();
expect(inputAttributes).toMatchObject({
- class: expect.stringContaining('qa-created-access-token'),
'data-qa-selector': 'created_access_token_field',
});
});
@@ -94,12 +102,29 @@ describe('~/access_tokens/components/new_access_token_app', () => {
});
});
- it('should reset the form', async () => {
- const resetSpy = jest.spyOn(wrapper.vm.form, 'reset');
+ describe('when resetting the form', () => {
+ it('should reset selectively some input fields', async () => {
+ expect(document.querySelector('input[type=text]:not([id$=expires_at])').value).toBe('1');
+ expect(document.querySelector('input[type=checkbox]').checked).toBe(true);
+ await triggerSuccess();
- await triggerSuccess();
+ expect(document.querySelector('input[type=text]:not([id$=expires_at])').value).toBe('');
+ expect(document.querySelector('input[type=checkbox]').checked).toBe(false);
+ });
- expect(resetSpy).toHaveBeenCalled();
+ it('should not reset the date field', async () => {
+ expect(document.querySelector('input[type=text][id$=expires_at]').value).toBe('2022-01-01');
+ await triggerSuccess();
+
+ expect(document.querySelector('input[type=text][id$=expires_at]').value).toBe('2022-01-01');
+ });
+
+ it('should not reset the submit button value', async () => {
+ expect(document.querySelector('input[type=submit]').value).toBe('Create');
+ await triggerSuccess();
+
+ expect(document.querySelector('input[type=submit]').value).toBe('Create');
+ });
});
});
diff --git a/spec/frontend/access_tokens/index_spec.js b/spec/frontend/access_tokens/index_spec.js
index 0c611a4a512..55575ab25fc 100644
--- a/spec/frontend/access_tokens/index_spec.js
+++ b/spec/frontend/access_tokens/index_spec.js
@@ -182,7 +182,7 @@ describe('access tokens', () => {
});
describe('initTokensApp', () => {
- it('mounts the component and provides`tokenTypes` ', () => {
+ it('mounts the component and provides`tokenTypes`', () => {
const tokensData = {
[FEED_TOKEN]: FEED_TOKEN,
[INCOMING_EMAIL_TOKEN]: INCOMING_EMAIL_TOKEN,
diff --git a/spec/frontend/add_context_commits_modal/components/add_context_commits_modal_spec.js b/spec/frontend/add_context_commits_modal/components/add_context_commits_modal_spec.js
index bffadbde087..1d57473943b 100644
--- a/spec/frontend/add_context_commits_modal/components/add_context_commits_modal_spec.js
+++ b/spec/frontend/add_context_commits_modal/components/add_context_commits_modal_spec.js
@@ -48,8 +48,8 @@ describe('AddContextCommitsModal', () => {
return wrapper;
};
- const findModal = () => wrapper.find(GlModal);
- const findSearch = () => wrapper.find(GlSearchBoxByType);
+ const findModal = () => wrapper.findComponent(GlModal);
+ const findSearch = () => wrapper.findComponent(GlSearchBoxByType);
beforeEach(() => {
wrapper = createWrapper();
@@ -75,7 +75,7 @@ describe('AddContextCommitsModal', () => {
it('when user starts entering text in search box, it calls action "searchCommits" after waiting for 500s', () => {
const searchText = 'abcd';
findSearch().vm.$emit('input', searchText);
- expect(searchCommits).not.toBeCalled();
+ expect(searchCommits).not.toHaveBeenCalled();
jest.advanceTimersByTime(500);
expect(searchCommits).toHaveBeenCalledWith(expect.anything(), searchText);
});
@@ -107,12 +107,12 @@ describe('AddContextCommitsModal', () => {
it('a disabled ok button in first tab, when row is selected in second tab', () => {
createWrapper({ selectedContextCommits: [commit] });
- expect(wrapper.find(GlModal).attributes('ok-disabled')).toBe('true');
+ expect(wrapper.findComponent(GlModal).attributes('ok-disabled')).toBe('true');
});
});
describe('has an ok button when clicked calls action', () => {
- it('"createContextCommits" when only new commits to be added ', async () => {
+ it('"createContextCommits" when only new commits to be added', async () => {
wrapper.vm.$store.state.selectedCommits = [{ ...commit, isSelected: true }];
findModal().vm.$emit('ok');
await nextTick();
@@ -121,7 +121,7 @@ describe('AddContextCommitsModal', () => {
forceReload: true,
});
});
- it('"removeContextCommits" when only added commits are to be removed ', async () => {
+ it('"removeContextCommits" when only added commits are to be removed', async () => {
wrapper.vm.$store.state.toRemoveCommits = [commit.short_id];
findModal().vm.$emit('ok');
await nextTick();
diff --git a/spec/frontend/add_context_commits_modal/components/review_tab_container_spec.js b/spec/frontend/add_context_commits_modal/components/review_tab_container_spec.js
index 85ecb4313c2..f679576182f 100644
--- a/spec/frontend/add_context_commits_modal/components/review_tab_container_spec.js
+++ b/spec/frontend/add_context_commits_modal/components/review_tab_container_spec.js
@@ -32,7 +32,7 @@ describe('ReviewTabContainer', () => {
it('shows loading icon when commits are being loaded', () => {
createWrapper({ isLoading: true });
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
it('shows loading error text when API call fails', () => {
@@ -46,6 +46,6 @@ describe('ReviewTabContainer', () => {
it('renders all passed commits as list', () => {
createWrapper({ commits: [commit] });
- expect(wrapper.findAll(CommitItem).length).toBe(1);
+ expect(wrapper.findAllComponents(CommitItem).length).toBe(1);
});
});
diff --git a/spec/frontend/admin/analytics/devops_score/components/devops_score_spec.js b/spec/frontend/admin/analytics/devops_score/components/devops_score_spec.js
index 534af2a3033..de56e843eb9 100644
--- a/spec/frontend/admin/analytics/devops_score/components/devops_score_spec.js
+++ b/spec/frontend/admin/analytics/devops_score/components/devops_score_spec.js
@@ -34,7 +34,7 @@ describe('DevopsScore', () => {
createComponent({ devopsScoreMetrics: {} });
});
- it('includes the DevopsScoreCallout component ', () => {
+ it('includes the DevopsScoreCallout component', () => {
expect(bannerExists()).toBe(true);
});
@@ -67,7 +67,7 @@ describe('DevopsScore', () => {
createComponent();
});
- it('includes the DevopsScoreCallout component ', () => {
+ it('includes the DevopsScoreCallout component', () => {
expect(bannerExists()).toBe(true);
});
diff --git a/spec/frontend/admin/topics/components/topic_select_spec.js b/spec/frontend/admin/topics/components/topic_select_spec.js
new file mode 100644
index 00000000000..f61af6203f0
--- /dev/null
+++ b/spec/frontend/admin/topics/components/topic_select_spec.js
@@ -0,0 +1,91 @@
+import { GlAvatarLabeled, GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import TopicSelect from '~/admin/topics/components/topic_select.vue';
+
+const mockTopics = [
+ { id: 1, name: 'topic1', title: 'Topic 1', avatarUrl: 'avatar.com/topic1.png' },
+ { id: 2, name: 'GitLab', title: 'GitLab', avatarUrl: 'avatar.com/GitLab.png' },
+];
+
+describe('TopicSelect', () => {
+ let wrapper;
+
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
+ const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
+
+ function createComponent(props = {}) {
+ wrapper = shallowMount(TopicSelect, {
+ propsData: props,
+ data() {
+ return {
+ topics: mockTopics,
+ search: '',
+ };
+ },
+ mocks: {
+ $apollo: {
+ queries: {
+ topics: { loading: false },
+ },
+ },
+ },
+ });
+ }
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('mounts', () => {
+ createComponent();
+
+ expect(wrapper.exists()).toBe(true);
+ });
+
+ it('`selectedTopic` prop defaults to `{}`', () => {
+ createComponent();
+
+ expect(wrapper.props('selectedTopic')).toEqual({});
+ });
+
+ it('`labelText` prop defaults to `null`', () => {
+ createComponent();
+
+ expect(wrapper.props('labelText')).toBe(null);
+ });
+
+ it('renders default text if no selected topic', () => {
+ createComponent();
+
+ expect(findDropdown().props('text')).toBe('Select a topic');
+ });
+
+ it('renders selected topic', () => {
+ createComponent({ selectedTopic: mockTopics[0] });
+
+ expect(findDropdown().props('text')).toBe('topic1');
+ });
+
+ it('renders label', () => {
+ createComponent({ labelText: 'my label' });
+
+ expect(wrapper.find('label').text()).toBe('my label');
+ });
+
+ it('renders dropdown items', () => {
+ createComponent();
+
+ const dropdownItems = findAllDropdownItems();
+
+ expect(dropdownItems.at(0).findComponent(GlAvatarLabeled).props('label')).toBe('Topic 1');
+ expect(dropdownItems.at(1).findComponent(GlAvatarLabeled).props('label')).toBe('GitLab');
+ });
+
+ it('emits `click` event when topic selected', () => {
+ createComponent();
+
+ findAllDropdownItems().at(0).vm.$emit('click');
+
+ expect(wrapper.emitted('click')).toEqual([[mockTopics[0]]]);
+ });
+});
diff --git a/spec/frontend/alert_management/components/alert_management_empty_state_spec.js b/spec/frontend/alert_management/components/alert_management_empty_state_spec.js
index c2bf90e7635..0d6bc1b74fb 100644
--- a/spec/frontend/alert_management/components/alert_management_empty_state_spec.js
+++ b/spec/frontend/alert_management/components/alert_management_empty_state_spec.js
@@ -25,7 +25,7 @@ describe('AlertManagementEmptyState', () => {
}
});
- const EmptyState = () => wrapper.find(GlEmptyState);
+ const EmptyState = () => wrapper.findComponent(GlEmptyState);
describe('Empty state', () => {
it('shows empty state', () => {
diff --git a/spec/frontend/alert_management/components/alert_management_list_wrapper_spec.js b/spec/frontend/alert_management/components/alert_management_list_wrapper_spec.js
index bba5fcbbf08..3a5fb99fdf1 100644
--- a/spec/frontend/alert_management/components/alert_management_list_wrapper_spec.js
+++ b/spec/frontend/alert_management/components/alert_management_list_wrapper_spec.js
@@ -28,8 +28,8 @@ describe('AlertManagementList', () => {
describe('Alert List Wrapper', () => {
it('should show the empty state when alerts are not enabled', () => {
- expect(wrapper.find(AlertManagementEmptyState).exists()).toBe(true);
- expect(wrapper.find(AlertManagementTable).exists()).toBe(false);
+ expect(wrapper.findComponent(AlertManagementEmptyState).exists()).toBe(true);
+ expect(wrapper.findComponent(AlertManagementTable).exists()).toBe(false);
});
it('should show the alerts table when alerts are enabled', () => {
@@ -39,8 +39,8 @@ describe('AlertManagementList', () => {
},
});
- expect(wrapper.find(AlertManagementEmptyState).exists()).toBe(false);
- expect(wrapper.find(AlertManagementTable).exists()).toBe(true);
+ expect(wrapper.findComponent(AlertManagementEmptyState).exists()).toBe(false);
+ expect(wrapper.findComponent(AlertManagementTable).exists()).toBe(true);
});
});
});
diff --git a/spec/frontend/alert_management/components/alert_management_table_spec.js b/spec/frontend/alert_management/components/alert_management_table_spec.js
index 5b823694b99..3e1438c37d6 100644
--- a/spec/frontend/alert_management/components/alert_management_table_spec.js
+++ b/spec/frontend/alert_management/components/alert_management_table_spec.js
@@ -172,8 +172,8 @@ describe('AlertManagementTable', () => {
await nextTick();
- expect(wrapper.find(GlTable).exists()).toBe(true);
- expect(findAlertsTable().find(GlIcon).classes('icon-critical')).toBe(true);
+ expect(wrapper.findComponent(GlTable).exists()).toBe(true);
+ expect(findAlertsTable().findComponent(GlIcon).classes('icon-critical')).toBe(true);
});
it('renders severity text', () => {
@@ -200,7 +200,7 @@ describe('AlertManagementTable', () => {
loading: false,
});
- const avatar = findAssignees().at(1).find(GlAvatar);
+ const avatar = findAssignees().at(1).findComponent(GlAvatar);
const { src, label } = avatar.attributes();
const { name, avatarUrl } = mockAlerts[1].assignees.nodes[0];
diff --git a/spec/frontend/alerts_settings/components/alert_mapping_builder_spec.js b/spec/frontend/alerts_settings/components/alert_mapping_builder_spec.js
index dba9c8be669..1e125bdfd3a 100644
--- a/spec/frontend/alerts_settings/components/alert_mapping_builder_spec.js
+++ b/spec/frontend/alerts_settings/components/alert_mapping_builder_spec.js
@@ -47,7 +47,7 @@ describe('AlertMappingBuilder', () => {
expect(findColumnInRow(0, 2).text()).toContain(i18n.columns.payloadKeyTitle);
expect(findColumnInRow(0, 3).text()).toContain(i18n.columns.fallbackKeyTitle);
- const fallbackColumnIcon = findColumnInRow(0, 3).find(GlIcon);
+ const fallbackColumnIcon = findColumnInRow(0, 3).findComponent(GlIcon);
expect(fallbackColumnIcon.exists()).toBe(true);
expect(fallbackColumnIcon.attributes('name')).toBe('question');
expect(fallbackColumnIcon.attributes('title')).toBe(i18n.fallbackTooltip);
@@ -55,7 +55,7 @@ describe('AlertMappingBuilder', () => {
it('renders disabled form input for each mapped field', () => {
alertFields.forEach((field, index) => {
- const input = findColumnInRow(index + 1, 0).find(GlFormInput);
+ const input = findColumnInRow(index + 1, 0).findComponent(GlFormInput);
const types = field.types.map((t) => capitalizeFirstCharacter(t.toLowerCase())).join(' or ');
expect(input.attributes('value')).toBe(`${field.label} (${types})`);
expect(input.attributes('disabled')).toBe('');
@@ -71,7 +71,7 @@ describe('AlertMappingBuilder', () => {
it('renders mapping dropdown for each field', () => {
alertFields.forEach(({ types }, index) => {
- const dropdown = findColumnInRow(index + 1, 2).find(GlDropdown);
+ const dropdown = findColumnInRow(index + 1, 2).findComponent(GlDropdown);
const { searchBox, dropdownItems, mappingOptions } = getDropdownContent(dropdown, types);
expect(dropdown.exists()).toBe(true);
@@ -82,7 +82,7 @@ describe('AlertMappingBuilder', () => {
it('renders fallback dropdown only for the fields that have fallback', () => {
alertFields.forEach(({ types, numberOfFallbacks }, index) => {
- const dropdown = findColumnInRow(index + 1, 3).find(GlDropdown);
+ const dropdown = findColumnInRow(index + 1, 3).findComponent(GlDropdown);
expect(dropdown.exists()).toBe(Boolean(numberOfFallbacks));
if (numberOfFallbacks) {
@@ -96,8 +96,8 @@ describe('AlertMappingBuilder', () => {
it('emits event with selected mapping', () => {
const mappingToSave = { fieldName: 'TITLE', mapping: 'PARSED_TITLE' };
jest.spyOn(transformationUtils, 'transformForSave').mockReturnValue(mappingToSave);
- const dropdown = findColumnInRow(1, 2).find(GlDropdown);
- const option = dropdown.find(GlDropdownItem);
+ const dropdown = findColumnInRow(1, 2).findComponent(GlDropdown);
+ const option = dropdown.findComponent(GlDropdownItem);
option.vm.$emit('click');
expect(wrapper.emitted('onMappingUpdate')[0]).toEqual([mappingToSave]);
});
diff --git a/spec/frontend/alerts_settings/components/alerts_form_spec.js b/spec/frontend/alerts_settings/components/alerts_form_spec.js
index a045954dfb8..33098282bf8 100644
--- a/spec/frontend/alerts_settings/components/alerts_form_spec.js
+++ b/spec/frontend/alerts_settings/components/alerts_form_spec.js
@@ -5,7 +5,7 @@ describe('Alert integration settings form', () => {
let wrapper;
const service = { updateSettings: jest.fn().mockResolvedValue() };
- const findForm = () => wrapper.find({ ref: 'settingsForm' });
+ const findForm = () => wrapper.findComponent({ ref: 'settingsForm' });
beforeEach(() => {
wrapper = shallowMount(AlertsSettingsForm, {
diff --git a/spec/frontend/alerts_settings/components/alerts_integrations_list_spec.js b/spec/frontend/alerts_settings/components/alerts_integrations_list_spec.js
index 3ffbb7ab60a..9983af873c2 100644
--- a/spec/frontend/alerts_settings/components/alerts_integrations_list_spec.js
+++ b/spec/frontend/alerts_settings/components/alerts_integrations_list_spec.js
@@ -53,8 +53,8 @@ describe('AlertIntegrationsList', () => {
mountComponent();
});
- const findTableComponent = () => wrapper.find(GlTable);
- const findTableComponentRows = () => wrapper.find(GlTable).findAll('table tbody tr');
+ const findTableComponent = () => wrapper.findComponent(GlTable);
+ const findTableComponentRows = () => wrapper.findComponent(GlTable).findAll('table tbody tr');
const finsStatusCell = () => wrapper.findAll('[data-testid="integration-activated-status"]');
it('renders a table', () => {
@@ -67,7 +67,7 @@ describe('AlertIntegrationsList', () => {
});
it('renders an an edit and delete button for each integration', () => {
- expect(findTableComponent().findAll(GlButton).length).toBe(4);
+ expect(findTableComponent().findAllComponents(GlButton).length).toBe(4);
});
it('renders an highlighted row when a current integration is selected to edit', () => {
@@ -78,7 +78,7 @@ describe('AlertIntegrationsList', () => {
describe('integration status', () => {
it('enabled', () => {
const cell = finsStatusCell().at(0);
- const activatedIcon = cell.find(GlIcon);
+ const activatedIcon = cell.findComponent(GlIcon);
expect(cell.text()).toBe(i18n.status.enabled.name);
expect(activatedIcon.attributes('name')).toBe('check');
expect(activatedIcon.attributes('title')).toBe(i18n.status.enabled.tooltip);
@@ -86,7 +86,7 @@ describe('AlertIntegrationsList', () => {
it('disabled', () => {
const cell = finsStatusCell().at(1);
- const notActivatedIcon = cell.find(GlIcon);
+ const notActivatedIcon = cell.findComponent(GlIcon);
expect(cell.text()).toBe(i18n.status.disabled.name);
expect(notActivatedIcon.attributes('name')).toBe('warning-solid');
expect(notActivatedIcon.attributes('title')).toBe(i18n.status.disabled.tooltip);
diff --git a/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js b/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js
index 7d9d2875cf8..fb9e97e7505 100644
--- a/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js
+++ b/spec/frontend/alerts_settings/components/alerts_settings_form_spec.js
@@ -325,9 +325,9 @@ describe('AlertsSettingsForm', () => {
});
await nextTick();
- expect(findSamplePayloadSection().find(GlFormTextarea).attributes('disabled')).toBe(
- disabled,
- );
+ expect(
+ findSamplePayloadSection().findComponent(GlFormTextarea).attributes('disabled'),
+ ).toBe(disabled);
});
});
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 ed185c11732..0266adeb6c7 100644
--- a/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
+++ b/spec/frontend/alerts_settings/components/alerts_settings_wrapper_spec.js
@@ -63,14 +63,14 @@ describe('AlertsSettingsWrapper', () => {
const findLoader = () => wrapper.findComponent(IntegrationsList).findComponent(GlLoadingIcon);
const findIntegrationsList = () => wrapper.findComponent(IntegrationsList);
- const findIntegrations = () => wrapper.find(IntegrationsList).findAll('table tbody tr');
+ const findIntegrations = () => wrapper.findComponent(IntegrationsList).findAll('table tbody tr');
const findAddIntegrationBtn = () => wrapper.findByTestId('add-integration-btn');
const findAlertsSettingsForm = () => wrapper.findComponent(AlertsSettingsForm);
const findAlert = () => wrapper.findComponent(GlAlert);
function destroyHttpIntegration(localWrapper) {
localWrapper
- .find(IntegrationsList)
+ .findComponent(IntegrationsList)
.vm.$emit('delete-integration', { id: integrationToDestroy.id });
}
@@ -148,7 +148,7 @@ describe('AlertsSettingsWrapper', () => {
expect(findIntegrations()).toHaveLength(mockIntegrations.length);
});
- it('renders `Add new integration` button when multiple integrations are supported ', () => {
+ it('renders `Add new integration` button when multiple integrations are supported', () => {
createComponent({
data: {
integrations: mockIntegrations,
@@ -189,7 +189,7 @@ describe('AlertsSettingsWrapper', () => {
data: { integrations: [] },
loading: true,
});
- expect(wrapper.find(IntegrationsList).exists()).toBe(true);
+ expect(wrapper.findComponent(IntegrationsList).exists()).toBe(true);
expect(findLoader().exists()).toBe(true);
});
});
@@ -321,7 +321,7 @@ describe('AlertsSettingsWrapper', () => {
});
});
- it('shows an error alert when integration creation fails ', async () => {
+ it('shows an error alert when integration creation fails', async () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue(ADD_INTEGRATION_ERROR);
findAlertsSettingsForm().vm.$emit('create-new-integration', {});
@@ -330,7 +330,7 @@ describe('AlertsSettingsWrapper', () => {
expect(createFlash).toHaveBeenCalledWith({ message: ADD_INTEGRATION_ERROR });
});
- it('shows an error alert when integration token reset fails ', async () => {
+ it('shows an error alert when integration token reset fails', async () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue(RESET_INTEGRATION_TOKEN_ERROR);
findAlertsSettingsForm().vm.$emit('reset-token', {});
@@ -339,7 +339,7 @@ describe('AlertsSettingsWrapper', () => {
expect(createFlash).toHaveBeenCalledWith({ message: RESET_INTEGRATION_TOKEN_ERROR });
});
- it('shows an error alert when integration update fails ', async () => {
+ it('shows an error alert when integration update fails', async () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue(errorMsg);
findAlertsSettingsForm().vm.$emit('update-integration', {});
@@ -357,14 +357,14 @@ describe('AlertsSettingsWrapper', () => {
mock.restore();
});
- it('shows an error alert when integration test payload is invalid ', async () => {
+ it('shows an error alert when integration test payload is invalid', async () => {
mock.onPost(/(.*)/).replyOnce(httpStatusCodes.UNPROCESSABLE_ENTITY);
await wrapper.vm.testAlertPayload({ endpoint: '', data: '', token: '' });
expect(createFlash).toHaveBeenCalledWith({ message: INTEGRATION_PAYLOAD_TEST_ERROR });
expect(createFlash).toHaveBeenCalledTimes(1);
});
- it('shows an error alert when integration is not activated ', async () => {
+ it('shows an error alert when integration is not activated', async () => {
mock.onPost(/(.*)/).replyOnce(httpStatusCodes.FORBIDDEN);
await wrapper.vm.testAlertPayload({ endpoint: '', data: '', token: '' });
expect(createFlash).toHaveBeenCalledWith({
diff --git a/spec/frontend/analytics/components/activity_chart_spec.js b/spec/frontend/analytics/components/activity_chart_spec.js
index a6b45ffe20f..c26407f5c1d 100644
--- a/spec/frontend/analytics/components/activity_chart_spec.js
+++ b/spec/frontend/analytics/components/activity_chart_spec.js
@@ -18,7 +18,7 @@ describe('Activity Chart Bundle', () => {
wrapper = null;
});
- const findChart = () => wrapper.find(GlColumnChart);
+ const findChart = () => wrapper.findComponent(GlColumnChart);
const findNoData = () => wrapper.find('[data-testid="noActivityChartData"]');
describe('Activity Chart', () => {
diff --git a/spec/frontend/analytics/shared/components/daterange_spec.js b/spec/frontend/analytics/shared/components/daterange_spec.js
index a38df274243..7a09fe3319d 100644
--- a/spec/frontend/analytics/shared/components/daterange_spec.js
+++ b/spec/frontend/analytics/shared/components/daterange_spec.js
@@ -1,5 +1,5 @@
-import { GlDaterangePicker, GlSprintf } from '@gitlab/ui';
-import { shallowMount, mount } from '@vue/test-utils';
+import { GlDaterangePicker } from '@gitlab/ui';
+import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
import { useFakeDate } from 'helpers/fake_date';
import Daterange from '~/analytics/shared/components/daterange.vue';
@@ -13,13 +13,12 @@ describe('Daterange component', () => {
let wrapper;
- const factory = (props = defaultProps, mountFn = shallowMount) => {
+ const factory = (props = defaultProps, mountFn = shallowMountExtended) => {
wrapper = mountFn(Daterange, {
propsData: {
...defaultProps,
...props,
},
- stubs: { GlSprintf },
});
};
@@ -28,7 +27,7 @@ describe('Daterange component', () => {
});
const findDaterangePicker = () => wrapper.findComponent(GlDaterangePicker);
- const findDateRangeIndicator = () => wrapper.findComponent(GlSprintf);
+ const findDateRangeIndicator = () => wrapper.findByTestId('daterange-picker-indicator');
describe('template', () => {
describe('when show is false', () => {
@@ -52,7 +51,7 @@ describe('Daterange component', () => {
const endDate = new Date('2019-09-30');
const minDate = new Date('2019-06-01');
- factory({ show: true, startDate, endDate, minDate }, mount);
+ factory({ show: true, startDate, endDate, minDate }, mountExtended);
const input = findDaterangePicker().find('input');
input.setValue('2019-01-01');
@@ -64,7 +63,7 @@ describe('Daterange component', () => {
describe('with a maxDateRange being set', () => {
beforeEach(() => {
- factory({ maxDateRange: 30 });
+ factory({ maxDateRange: 30 }, mountExtended);
});
it('displays the max date range indicator', () => {
@@ -72,7 +71,7 @@ describe('Daterange component', () => {
});
it('displays the correct number of selected days in the indicator', () => {
- expect(findDateRangeIndicator().text()).toMatchInterpolatedText('10 days selected');
+ expect(findDateRangeIndicator().text()).toBe('10 days selected');
});
it('sets the tooltip', () => {
diff --git a/spec/frontend/analytics/shared/components/metric_popover_spec.js b/spec/frontend/analytics/shared/components/metric_popover_spec.js
index ffec77c2708..6a58f8c6d29 100644
--- a/spec/frontend/analytics/shared/components/metric_popover_spec.js
+++ b/spec/frontend/analytics/shared/components/metric_popover_spec.js
@@ -30,7 +30,7 @@ describe('MetricPopover', () => {
const findAllMetricLinks = () => wrapper.findAll('[data-testid="metric-link"]');
const findMetricDescription = () => wrapper.findByTestId('metric-description');
const findMetricDocsLink = () => wrapper.findByTestId('metric-docs-link');
- const findMetricDocsLinkIcon = () => findMetricDocsLink().find(GlIcon);
+ const findMetricDocsLinkIcon = () => findMetricDocsLink().findComponent(GlIcon);
afterEach(() => {
wrapper.destroy();
@@ -83,7 +83,9 @@ describe('MetricPopover', () => {
const allLinkContainers = findAllMetricLinks();
expect(allLinkContainers.at(idx).text()).toContain(link.name);
- expect(allLinkContainers.at(idx).find(GlLink).attributes('href')).toBe(link.url);
+ expect(allLinkContainers.at(idx).findComponent(GlLink).attributes('href')).toBe(
+ link.url,
+ );
});
});
diff --git a/spec/frontend/analytics/shared/components/projects_dropdown_filter_spec.js b/spec/frontend/analytics/shared/components/projects_dropdown_filter_spec.js
index 69918c1db65..3871fd530d8 100644
--- a/spec/frontend/analytics/shared/components/projects_dropdown_filter_spec.js
+++ b/spec/frontend/analytics/shared/components/projects_dropdown_filter_spec.js
@@ -79,11 +79,11 @@ describe('ProjectsDropdownFilter component', () => {
const findClearAllButton = () => wrapper.findByText('Clear all');
const findSelectedProjectsLabel = () => wrapper.findComponent(GlTruncate);
- const findDropdown = () => wrapper.find(GlDropdown);
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
const findDropdownItems = () =>
findDropdown()
- .findAll(GlDropdownItem)
+ .findAllComponents(GlDropdownItem)
.filter((w) => w.text() !== 'No matching results');
const findDropdownAtIndex = (index) => findDropdownItems().at(index);
@@ -106,7 +106,7 @@ describe('ProjectsDropdownFilter component', () => {
};
// NOTE: Selected items are now visually separated from unselected items
- const findSelectedDropdownItems = () => findHighlightedItems().findAll(GlDropdownItem);
+ const findSelectedDropdownItems = () => findHighlightedItems().findAllComponents(GlDropdownItem);
const findSelectedDropdownAtIndex = (index) => findSelectedDropdownItems().at(index);
const findSelectedButtonIdentIconAtIndex = (index) =>
diff --git a/spec/frontend/analytics/usage_trends/components/app_spec.js b/spec/frontend/analytics/usage_trends/components/app_spec.js
index 156be26f895..c732dc22322 100644
--- a/spec/frontend/analytics/usage_trends/components/app_spec.js
+++ b/spec/frontend/analytics/usage_trends/components/app_spec.js
@@ -21,13 +21,13 @@ describe('UsageTrendsApp', () => {
});
it('displays the usage counts component', () => {
- expect(wrapper.find(UsageCounts).exists()).toBe(true);
+ expect(wrapper.findComponent(UsageCounts).exists()).toBe(true);
});
['Total projects & groups', 'Pipelines', 'Issues & merge requests'].forEach((usage) => {
it(`displays the ${usage} chart`, () => {
const chartTitles = wrapper
- .findAll(UsageTrendsCountChart)
+ .findAllComponents(UsageTrendsCountChart)
.wrappers.map((chartComponent) => chartComponent.props('chartTitle'));
expect(chartTitles).toContain(usage);
@@ -35,6 +35,6 @@ describe('UsageTrendsApp', () => {
});
it('displays the users chart component', () => {
- expect(wrapper.find(UsersChart).exists()).toBe(true);
+ expect(wrapper.findComponent(UsersChart).exists()).toBe(true);
});
});
diff --git a/spec/frontend/analytics/usage_trends/components/usage_trends_count_chart_spec.js b/spec/frontend/analytics/usage_trends/components/usage_trends_count_chart_spec.js
index 02cf7f42a0b..ad6089f74b5 100644
--- a/spec/frontend/analytics/usage_trends/components/usage_trends_count_chart_spec.js
+++ b/spec/frontend/analytics/usage_trends/components/usage_trends_count_chart_spec.js
@@ -50,9 +50,9 @@ describe('UsageTrendsCountChart', () => {
wrapper = null;
});
- const findLoader = () => wrapper.find(ChartSkeletonLoader);
- const findChart = () => wrapper.find(GlLineChart);
- const findAlert = () => wrapper.find(GlAlert);
+ const findLoader = () => wrapper.findComponent(ChartSkeletonLoader);
+ const findChart = () => wrapper.findComponent(GlLineChart);
+ const findAlert = () => wrapper.findComponent(GlAlert);
describe('while loading', () => {
beforeEach(() => {
@@ -61,7 +61,7 @@ describe('UsageTrendsCountChart', () => {
});
it('requests data', () => {
- expect(queryHandler).toBeCalledTimes(1);
+ expect(queryHandler).toHaveBeenCalledTimes(1);
});
it('displays the skeleton loader', () => {
@@ -105,7 +105,7 @@ describe('UsageTrendsCountChart', () => {
});
it('requests data', () => {
- expect(queryHandler).toBeCalledTimes(1);
+ expect(queryHandler).toHaveBeenCalledTimes(1);
});
it('hides the skeleton loader', () => {
@@ -141,7 +141,7 @@ describe('UsageTrendsCountChart', () => {
});
it('requests data twice', () => {
- expect(queryHandler).toBeCalledTimes(2);
+ expect(queryHandler).toHaveBeenCalledTimes(2);
});
it('passes the data to the line chart', () => {
diff --git a/spec/frontend/analytics/usage_trends/components/users_chart_spec.js b/spec/frontend/analytics/usage_trends/components/users_chart_spec.js
index 32a664a5026..e7abd4d4323 100644
--- a/spec/frontend/analytics/usage_trends/components/users_chart_spec.js
+++ b/spec/frontend/analytics/usage_trends/components/users_chart_spec.js
@@ -47,9 +47,9 @@ describe('UsersChart', () => {
wrapper = null;
});
- const findLoader = () => wrapper.find(ChartSkeletonLoader);
- const findAlert = () => wrapper.find(GlAlert);
- const findChart = () => wrapper.find(GlAreaChart);
+ const findLoader = () => wrapper.findComponent(ChartSkeletonLoader);
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findChart = () => wrapper.findComponent(GlAreaChart);
describe('while loading', () => {
beforeEach(() => {
@@ -139,7 +139,7 @@ describe('UsersChart', () => {
});
it('requests data twice', () => {
- expect(queryHandler).toBeCalledTimes(2);
+ expect(queryHandler).toHaveBeenCalledTimes(2);
});
it('calls fetchMore', () => {
diff --git a/spec/frontend/analytics/usage_trends/utils_spec.js b/spec/frontend/analytics/usage_trends/utils_spec.js
index 656f310dda7..9982e96735e 100644
--- a/spec/frontend/analytics/usage_trends/utils_spec.js
+++ b/spec/frontend/analytics/usage_trends/utils_spec.js
@@ -16,17 +16,17 @@ describe('getAverageByMonth', () => {
expect(getAverageByMonth(mockCountsData2)).toStrictEqual(countsMonthlyChartData2);
});
- it('it transforms a data point to the first of the month', () => {
+ it('transforms a data point to the first of the month', () => {
const item = mockCountsData1[0];
const firstOfTheMonth = item.recordedAt.replace(/-[0-9]{2}$/, '-01');
expect(getAverageByMonth([item])).toStrictEqual([[firstOfTheMonth, item.count]]);
});
- it('it uses sane defaults', () => {
+ it('uses sane defaults', () => {
expect(getAverageByMonth()).toStrictEqual([]);
});
- it('it errors when passing null', () => {
+ it('errors when passing null', () => {
expect(() => {
getAverageByMonth(null);
}).toThrow();
diff --git a/spec/frontend/api/harbor_registry_spec.js b/spec/frontend/api/harbor_registry_spec.js
new file mode 100644
index 00000000000..8a4c377ebd1
--- /dev/null
+++ b/spec/frontend/api/harbor_registry_spec.js
@@ -0,0 +1,107 @@
+import MockAdapter from 'axios-mock-adapter';
+import * as harborRegistryApi from '~/api/harbor_registry';
+import axios from '~/lib/utils/axios_utils';
+import httpStatus from '~/lib/utils/http_status';
+
+describe('~/api/harbor_registry', () => {
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ jest.spyOn(axios, 'get');
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('getHarborRepositoriesList', () => {
+ it('fetches the harbor repositories of the configured harbor project', () => {
+ const requestPath = '/flightjs/Flight/-/harbor/repositories';
+ const expectedUrl = `${requestPath}.json`;
+ const expectedParams = {
+ limit: 10,
+ page: 1,
+ sort: 'update_time desc',
+ requestPath,
+ };
+ const expectResponse = [
+ {
+ harbor_id: 1,
+ name: 'test-project/image-1',
+ artifact_count: 1,
+ creation_time: '2022-07-16T08:20:34.851Z',
+ update_time: '2022-07-16T08:20:34.851Z',
+ harbor_project_id: 2,
+ pull_count: 0,
+ location: 'http://demo.harbor.com/harbor/projects/2/repositories/image-1',
+ },
+ ];
+ mock.onGet(expectedUrl).reply(httpStatus.OK, expectResponse);
+
+ return harborRegistryApi.getHarborRepositoriesList(expectedParams).then(({ data }) => {
+ expect(data).toEqual(expectResponse);
+ });
+ });
+ });
+
+ describe('getHarborArtifacts', () => {
+ it('fetches the artifacts of a particular harbor repository', () => {
+ const requestPath = '/flightjs/Flight/-/harbor/repositories';
+ const repoName = 'image-1';
+ const expectedUrl = `${requestPath}/${repoName}/artifacts.json`;
+ const expectedParams = {
+ limit: 10,
+ page: 1,
+ sort: 'name asc',
+ repoName,
+ requestPath,
+ };
+ const expectResponse = [
+ {
+ harbor_id: 1,
+ digest: 'sha256:dcdf379c574e1773d703f0c0d56d67594e7a91d6b84d11ff46799f60fb081c52',
+ size: 775241,
+ push_time: '2022-07-16T08:20:34.867Z',
+ tags: ['v2', 'v1', 'latest'],
+ },
+ ];
+ mock.onGet(expectedUrl).reply(httpStatus.OK, expectResponse);
+
+ return harborRegistryApi.getHarborArtifacts(expectedParams).then(({ data }) => {
+ expect(data).toEqual(expectResponse);
+ });
+ });
+ });
+
+ describe('getHarborTags', () => {
+ it('fetches the tags of a particular artifact', () => {
+ const requestPath = '/flightjs/Flight/-/harbor/repositories';
+ const repoName = 'image-1';
+ const digest = 'sha256:5d98daa36cdc8d6c7ed6579ce17230f0f9fd893a9012fc069cb7d714c0e3df35';
+ const expectedUrl = `${requestPath}/${repoName}/artifacts/${digest}/tags.json`;
+ const expectedParams = {
+ requestPath,
+ digest,
+ repoName,
+ };
+ const expectResponse = [
+ {
+ repositoryId: 4,
+ artifactId: 5,
+ id: 4,
+ name: 'latest',
+ pullTime: '0001-01-01T00:00:00.000Z',
+ pushTime: '2022-05-27T18:21:27.903Z',
+ signed: false,
+ immutable: false,
+ },
+ ];
+ mock.onGet(expectedUrl).reply(httpStatus.OK, expectResponse);
+
+ return harborRegistryApi.getHarborTags(expectedParams).then(({ data }) => {
+ expect(data).toEqual(expectResponse);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/artifacts_settings/components/keep_latest_artifact_checkbox_spec.js b/spec/frontend/artifacts_settings/components/keep_latest_artifact_checkbox_spec.js
index 2f3ff2b22f2..ca94acfa444 100644
--- a/spec/frontend/artifacts_settings/components/keep_latest_artifact_checkbox_spec.js
+++ b/spec/frontend/artifacts_settings/components/keep_latest_artifact_checkbox_spec.js
@@ -39,8 +39,8 @@ describe('Keep latest artifact checkbox', () => {
const fullPath = 'gitlab-org/gitlab';
const helpPagePath = '/help/ci/pipelines/job_artifacts';
- const findCheckbox = () => wrapper.find(GlFormCheckbox);
- const findHelpLink = () => wrapper.find(GlLink);
+ const findCheckbox = () => wrapper.findComponent(GlFormCheckbox);
+ const findHelpLink = () => wrapper.findComponent(GlLink);
const createComponent = (handlers) => {
requestHandlers = {
diff --git a/spec/frontend/authentication/two_factor_auth/components/recovery_codes_spec.js b/spec/frontend/authentication/two_factor_auth/components/recovery_codes_spec.js
index 2dcc537809f..0d9196b88ed 100644
--- a/spec/frontend/authentication/two_factor_auth/components/recovery_codes_spec.js
+++ b/spec/frontend/authentication/two_factor_auth/components/recovery_codes_spec.js
@@ -31,11 +31,13 @@ describe('RecoveryCodes', () => {
};
const queryByText = (text, options) => within(wrapper.element).queryByText(text, options);
- const findAlert = () => wrapper.find(GlAlert);
+ const findAlert = () => wrapper.findComponent(GlAlert);
const findRecoveryCodes = () => wrapper.findByTestId('recovery-codes');
- const findCopyButton = () => wrapper.find(ClipboardButton);
+ const findCopyButton = () => wrapper.findComponent(ClipboardButton);
const findButtonByText = (text) =>
- wrapper.findAll(GlButton).wrappers.find((buttonWrapper) => buttonWrapper.text() === text);
+ wrapper
+ .findAllComponents(GlButton)
+ .wrappers.find((buttonWrapper) => buttonWrapper.text() === text);
const findDownloadButton = () => findButtonByText('Download codes');
const findPrintButton = () => findButtonByText('Print codes');
const findProceedButton = () => findButtonByText('Proceed');
diff --git a/spec/frontend/authentication/two_factor_auth/index_spec.js b/spec/frontend/authentication/two_factor_auth/index_spec.js
index f9a6b2df662..427159bacd9 100644
--- a/spec/frontend/authentication/two_factor_auth/index_spec.js
+++ b/spec/frontend/authentication/two_factor_auth/index_spec.js
@@ -10,7 +10,7 @@ describe('initRecoveryCodes', () => {
let el;
let wrapper;
- const findRecoveryCodesComponent = () => wrapper.find(RecoveryCodes);
+ const findRecoveryCodesComponent = () => wrapper.findComponent(RecoveryCodes);
beforeEach(() => {
el = document.createElement('div');
diff --git a/spec/frontend/autosave_spec.js b/spec/frontend/autosave_spec.js
index c881e0f9794..7a9262cd004 100644
--- a/spec/frontend/autosave_spec.js
+++ b/spec/frontend/autosave_spec.js
@@ -8,6 +8,7 @@ describe('Autosave', () => {
let autosave;
const field = $('<textarea></textarea>');
+ const checkbox = $('<input type="checkbox">');
const key = 'key';
const fallbackKey = 'fallbackKey';
const lockVersionKey = 'lockVersionKey';
@@ -90,6 +91,24 @@ describe('Autosave', () => {
expect(eventHandler).toHaveBeenCalledTimes(1);
fieldElement.removeEventListener('change', eventHandler);
});
+
+ describe('if field type is checkbox', () => {
+ beforeEach(() => {
+ autosave = {
+ field: checkbox,
+ key,
+ isLocalStorageAvailable: true,
+ type: 'checkbox',
+ };
+ });
+
+ it('should restore', () => {
+ window.localStorage.setItem(key, true);
+ expect(checkbox.is(':checked')).toBe(false);
+ Autosave.prototype.restore.call(autosave);
+ expect(checkbox.is(':checked')).toBe(true);
+ });
+ });
});
describe('if field gets deleted from DOM', () => {
@@ -169,6 +188,31 @@ describe('Autosave', () => {
expect(window.localStorage.setItem).toHaveBeenCalled();
});
});
+
+ describe('if field type is checkbox', () => {
+ beforeEach(() => {
+ autosave = {
+ field: checkbox,
+ key,
+ isLocalStorageAvailable: true,
+ type: 'checkbox',
+ };
+ });
+
+ it('should save true when checkbox on', () => {
+ checkbox.prop('checked', true);
+ Autosave.prototype.save.call(autosave);
+ expect(window.localStorage.setItem).toHaveBeenCalledWith(key, true);
+ });
+
+ it('should call reset when checkbox off', () => {
+ autosave.reset = jest.fn();
+ checkbox.prop('checked', false);
+ Autosave.prototype.save.call(autosave);
+ expect(autosave.reset).toHaveBeenCalled();
+ expect(window.localStorage.setItem).not.toHaveBeenCalled();
+ });
+ });
});
describe('save with lockVersion', () => {
diff --git a/spec/frontend/badges/components/badge_settings_spec.js b/spec/frontend/badges/components/badge_settings_spec.js
index 79cf5f3e4ff..bddb6d3801c 100644
--- a/spec/frontend/badges/components/badge_settings_spec.js
+++ b/spec/frontend/badges/components/badge_settings_spec.js
@@ -42,7 +42,7 @@ describe('BadgeSettings component', () => {
button.vm.$emit('click');
await nextTick();
- const modal = wrapper.find(GlModal);
+ const modal = wrapper.findComponent(GlModal);
expect(modal.isVisible()).toBe(true);
});
@@ -51,7 +51,7 @@ describe('BadgeSettings component', () => {
});
it('displays badge list', () => {
- expect(wrapper.find(BadgeList).isVisible()).toBe(true);
+ expect(wrapper.findComponent(BadgeList).isVisible()).toBe(true);
});
describe('when editing', () => {
@@ -64,7 +64,7 @@ describe('BadgeSettings component', () => {
});
it('displays no badge list', () => {
- expect(wrapper.find(BadgeList).isVisible()).toBe(false);
+ expect(wrapper.findComponent(BadgeList).isVisible()).toBe(false);
});
});
});
diff --git a/spec/frontend/batch_comments/components/diff_file_drafts_spec.js b/spec/frontend/batch_comments/components/diff_file_drafts_spec.js
index 6a5ff1af7c9..c922d6a9809 100644
--- a/spec/frontend/batch_comments/components/diff_file_drafts_spec.js
+++ b/spec/frontend/batch_comments/components/diff_file_drafts_spec.js
@@ -35,13 +35,13 @@ describe('Batch comments diff file drafts component', () => {
it('renders list of draft notes', () => {
factory();
- expect(vm.findAll(DraftNote).length).toEqual(2);
+ expect(vm.findAllComponents(DraftNote).length).toEqual(2);
});
it('renders index of draft note', () => {
factory();
- const elements = vm.findAll(DesignNotePin);
+ const elements = vm.findAllComponents(DesignNotePin);
expect(elements.length).toEqual(2);
diff --git a/spec/frontend/batch_comments/components/draft_note_spec.js b/spec/frontend/batch_comments/components/draft_note_spec.js
index ccca4a2c3e9..03ecbc01a56 100644
--- a/spec/frontend/batch_comments/components/draft_note_spec.js
+++ b/spec/frontend/batch_comments/components/draft_note_spec.js
@@ -61,7 +61,7 @@ describe('Batch comments draft note component', () => {
createComponent();
expect(wrapper.findComponent(GlBadge).exists()).toBe(true);
- const note = wrapper.find(NoteableNote);
+ const note = wrapper.findComponent(NoteableNote);
expect(note.exists()).toBe(true);
expect(note.props().note).toEqual(draft);
@@ -115,14 +115,14 @@ describe('Batch comments draft note component', () => {
await nextTick();
const publishNowButton = findSubmitReviewButton();
- expect(publishNowButton.attributes().disabled).toBeTruthy();
+ expect(publishNowButton.attributes().disabled).toBe('true');
});
});
describe('update', () => {
it('dispatches updateDraft', async () => {
createComponent();
- const note = wrapper.find(NoteableNote);
+ const note = wrapper.findComponent(NoteableNote);
note.vm.$emit('handleEdit');
@@ -147,7 +147,7 @@ describe('Batch comments draft note component', () => {
createComponent();
jest.spyOn(window, 'confirm').mockImplementation(() => true);
- const note = wrapper.find(NoteableNote);
+ const note = wrapper.findComponent(NoteableNote);
note.vm.$emit('handleDeleteNote', draft);
diff --git a/spec/frontend/batch_comments/components/preview_dropdown_spec.js b/spec/frontend/batch_comments/components/preview_dropdown_spec.js
index 079b64225e4..283632cb560 100644
--- a/spec/frontend/batch_comments/components/preview_dropdown_spec.js
+++ b/spec/frontend/batch_comments/components/preview_dropdown_spec.js
@@ -53,7 +53,7 @@ describe('Batch comments preview dropdown', () => {
});
describe('clicking draft', () => {
- it('it toggles active file when viewDiffsFileByFile is true', async () => {
+ it('toggles active file when viewDiffsFileByFile is true', async () => {
factory({
viewDiffsFileByFile: true,
sortedDrafts: [{ id: 1, file_hash: 'hash' }],
diff --git a/spec/frontend/batch_comments/components/preview_item_spec.js b/spec/frontend/batch_comments/components/preview_item_spec.js
index cb71edd1238..91e6b84a216 100644
--- a/spec/frontend/batch_comments/components/preview_item_spec.js
+++ b/spec/frontend/batch_comments/components/preview_item_spec.js
@@ -118,7 +118,7 @@ describe('Batch comments draft preview item component', () => {
);
});
- it('it renders thread resolved text', () => {
+ it('renders thread resolved text', () => {
expect(vm.$el.querySelector('.draft-note-resolution').textContent).toContain(
'Thread will be resolved',
);
diff --git a/spec/frontend/batch_comments/components/publish_dropdown_spec.js b/spec/frontend/batch_comments/components/publish_dropdown_spec.js
index a3168931f1f..d1b7160d231 100644
--- a/spec/frontend/batch_comments/components/publish_dropdown_spec.js
+++ b/spec/frontend/batch_comments/components/publish_dropdown_spec.js
@@ -28,12 +28,12 @@ describe('Batch comments publish dropdown component', () => {
it('renders list of drafts', () => {
createComponent();
- expect(wrapper.findAll(GlDropdownItem).length).toBe(2);
+ expect(wrapper.findAllComponents(GlDropdownItem).length).toBe(2);
});
it('renders draft count in dropdown title', () => {
createComponent();
- expect(wrapper.find(GlDropdown).props('headerText')).toEqual('2 pending comments');
+ expect(wrapper.findComponent(GlDropdown).props('headerText')).toEqual('2 pending comments');
});
});
diff --git a/spec/frontend/batch_comments/components/review_bar_spec.js b/spec/frontend/batch_comments/components/review_bar_spec.js
index f50db6ab210..0a4c9ff62e4 100644
--- a/spec/frontend/batch_comments/components/review_bar_spec.js
+++ b/spec/frontend/batch_comments/components/review_bar_spec.js
@@ -24,7 +24,7 @@ describe('Batch comments review bar component', () => {
wrapper.destroy();
});
- it('it adds review-bar-visible class to body when review bar is mounted', async () => {
+ it('adds review-bar-visible class to body when review bar is mounted', async () => {
expect(document.body.classList.contains(REVIEW_BAR_VISIBLE_CLASS_NAME)).toBe(false);
createComponent();
@@ -32,7 +32,7 @@ describe('Batch comments review bar component', () => {
expect(document.body.classList.contains(REVIEW_BAR_VISIBLE_CLASS_NAME)).toBe(true);
});
- it('it removes review-bar-visible class to body when review bar is destroyed', async () => {
+ it('removes review-bar-visible class to body when review bar is destroyed', async () => {
createComponent();
wrapper.destroy();
diff --git a/spec/frontend/batch_comments/components/submit_dropdown_spec.js b/spec/frontend/batch_comments/components/submit_dropdown_spec.js
index 4f5ff797230..462ef7e7280 100644
--- a/spec/frontend/batch_comments/components/submit_dropdown_spec.js
+++ b/spec/frontend/batch_comments/components/submit_dropdown_spec.js
@@ -8,7 +8,7 @@ Vue.use(Vuex);
let wrapper;
let publishReview;
-function factory() {
+function factory({ canApprove = true } = {}) {
publishReview = jest.fn();
const store = new Vuex.Store({
@@ -17,8 +17,13 @@ function factory() {
markdownDocsPath: '/markdown/docs',
quickActionsDocsPath: '/quickactions/docs',
}),
- getNoteableData: () => ({ id: 1, preview_note_path: '/preview' }),
+ getNoteableData: () => ({
+ id: 1,
+ preview_note_path: '/preview',
+ current_user: { can_approve: canApprove },
+ }),
noteableType: () => 'merge_request',
+ getCurrentUserLastNote: () => ({ id: 1 }),
},
modules: {
batchComments: {
@@ -41,6 +46,7 @@ const findForm = () => wrapper.findByTestId('submit-gl-form');
describe('Batch comments submit dropdown', () => {
afterEach(() => {
wrapper.destroy();
+ window.mrTabs = null;
});
it('calls publishReview with note data', async () => {
@@ -54,9 +60,24 @@ describe('Batch comments submit dropdown', () => {
noteable_type: 'merge_request',
noteable_id: 1,
note: 'Hello world',
+ approve: false,
+ approval_password: '',
});
});
+ it('switches to the overview tab after submit', async () => {
+ window.mrTabs = { tabShown: jest.fn() };
+
+ factory();
+
+ findCommentTextarea().setValue('Hello world');
+
+ await findForm().vm.$emit('submit', { preventDefault: jest.fn() });
+ await Vue.nextTick();
+
+ expect(window.mrTabs.tabShown).toHaveBeenCalledWith('show');
+ });
+
it('sets submit dropdown to loading', async () => {
factory();
@@ -66,4 +87,14 @@ describe('Batch comments submit dropdown', () => {
expect(findSubmitButton().props('loading')).toBe(true);
});
+
+ it.each`
+ canApprove | exists | existsText
+ ${true} | ${true} | ${'shows'}
+ ${false} | ${false} | ${'hides'}
+ `('$existsText approve checkbox if can_approve is $canApprove', ({ canApprove, exists }) => {
+ factory({ canApprove });
+
+ expect(wrapper.findByTestId('approve_merge_request').exists()).toBe(exists);
+ });
});
diff --git a/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js b/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js
index 9f50b12bac2..6369ea9aa15 100644
--- a/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js
+++ b/spec/frontend/batch_comments/stores/modules/batch_comments/actions_spec.js
@@ -180,6 +180,7 @@ describe('Batch comments store actions', () => {
});
it('calls service with notes data', () => {
+ mock.onAny().reply(200);
jest.spyOn(axios, 'post');
return actions
@@ -192,7 +193,7 @@ describe('Batch comments store actions', () => {
it('dispatches error commits', () => {
mock.onAny().reply(500);
- return actions.publishReview({ dispatch, commit, getters, rootGetters }).then(() => {
+ return actions.publishReview({ dispatch, commit, getters, rootGetters }).catch(() => {
expect(commit.mock.calls[0]).toEqual(['REQUEST_PUBLISH_REVIEW']);
expect(commit.mock.calls[1]).toEqual(['RECEIVE_PUBLISH_REVIEW_ERROR']);
});
diff --git a/spec/frontend/behaviors/bind_in_out_spec.js b/spec/frontend/behaviors/bind_in_out_spec.js
index 49425a9377e..4d958e30b4d 100644
--- a/spec/frontend/behaviors/bind_in_out_spec.js
+++ b/spec/frontend/behaviors/bind_in_out_spec.js
@@ -33,7 +33,7 @@ describe('BindInOut', () => {
testContext.bindInOut = new BindInOut({ tagName: 'INPUT' });
});
- it('should set .eventType to keyup ', () => {
+ it('should set .eventType to keyup', () => {
expect(testContext.bindInOut.eventType).toEqual('keyup');
});
});
@@ -43,7 +43,7 @@ describe('BindInOut', () => {
testContext.bindInOut = new BindInOut({ tagName: 'TEXTAREA' });
});
- it('should set .eventType to keyup ', () => {
+ it('should set .eventType to keyup', () => {
expect(testContext.bindInOut.eventType).toEqual('keyup');
});
});
@@ -53,7 +53,7 @@ describe('BindInOut', () => {
testContext.bindInOut = new BindInOut({ tagName: 'SELECT' });
});
- it('should set .eventType to change ', () => {
+ it('should set .eventType to change', () => {
expect(testContext.bindInOut.eventType).toEqual('change');
});
});
diff --git a/spec/frontend/blob/sketch/index_spec.js b/spec/frontend/blob/sketch/index_spec.js
index e8d1f724c4b..4b6cb79791c 100644
--- a/spec/frontend/blob/sketch/index_spec.js
+++ b/spec/frontend/blob/sketch/index_spec.js
@@ -2,18 +2,6 @@ import SketchLoader from '~/blob/sketch';
import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import waitForPromises from 'helpers/wait_for_promises';
-jest.mock('jszip', () => {
- return {
- loadAsync: jest.fn().mockResolvedValue({
- files: {
- 'previews/preview.png': {
- async: jest.fn().mockResolvedValue('foo'),
- },
- },
- }),
- };
-});
-
describe('Sketch viewer', () => {
beforeEach(() => {
loadHTMLFixture('static/sketch_viewer.html');
@@ -25,7 +13,7 @@ describe('Sketch viewer', () => {
describe('with error message', () => {
beforeEach(() => {
- jest.spyOn(SketchLoader.prototype, 'getZipFile').mockImplementation(
+ jest.spyOn(SketchLoader.prototype, 'getZipContents').mockImplementation(
() =>
new Promise((resolve, reject) => {
reject();
@@ -50,7 +38,13 @@ describe('Sketch viewer', () => {
describe('success', () => {
beforeEach(() => {
- jest.spyOn(SketchLoader.prototype, 'getZipFile').mockResolvedValue();
+ jest.spyOn(SketchLoader.prototype, 'getZipContents').mockResolvedValue({
+ files: {
+ 'previews/preview.png': {
+ async: jest.fn().mockResolvedValue('foo'),
+ },
+ },
+ });
// eslint-disable-next-line no-new
new SketchLoader(document.getElementById('js-sketch-viewer'));
diff --git a/spec/frontend/boards/board_card_inner_spec.js b/spec/frontend/boards/board_card_inner_spec.js
index 985902b4a3b..2c3ec69f9ae 100644
--- a/spec/frontend/boards/board_card_inner_spec.js
+++ b/spec/frontend/boards/board_card_inner_spec.js
@@ -7,6 +7,8 @@ import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import BoardBlockedIcon from '~/boards/components/board_blocked_icon.vue';
import BoardCardInner from '~/boards/components/board_card_inner.vue';
+import BoardCardMoveToPosition from '~/boards/components/board_card_move_to_position.vue';
+import WorkItemTypeIcon from '~/work_items/components/work_item_type_icon.vue';
import { issuableTypes } from '~/boards/constants';
import eventHub from '~/boards/eventhub';
import defaultStore from '~/boards/stores';
@@ -47,6 +49,8 @@ describe('Board card component', () => {
const findEpicCountablesTotalWeight = () => wrapper.findByTestId('epic-countables-total-weight');
const findEpicProgressTooltip = () => wrapper.findByTestId('epic-progress-tooltip-content');
const findHiddenIssueIcon = () => wrapper.findByTestId('hidden-icon');
+ const findMoveToPositionComponent = () => wrapper.findComponent(BoardCardMoveToPosition);
+ const findWorkItemIcon = () => wrapper.findComponent(WorkItemTypeIcon);
const performSearchMock = jest.fn();
@@ -75,10 +79,12 @@ describe('Board card component', () => {
propsData: {
list,
item: issue,
+ index: 0,
...props,
},
stubs: {
GlLoadingIcon: true,
+ BoardCardMoveToPosition: true,
},
directives: {
GlTooltip: createMockDirective(),
@@ -137,6 +143,20 @@ describe('Board card component', () => {
expect(findHiddenIssueIcon().exists()).toBe(false);
});
+ it('renders the move to position icon', () => {
+ expect(findMoveToPositionComponent().exists()).toBe(true);
+ });
+
+ it('does not render the work type icon by default', () => {
+ expect(findWorkItemIcon().exists()).toBe(false);
+ });
+
+ it('renders the work type icon when props is passed', () => {
+ createWrapper({ item: issue, list, showWorkItemTypeIcon: true });
+ expect(findWorkItemIcon().exists()).toBe(true);
+ expect(findWorkItemIcon().props('workItemType')).toBe(issue.type);
+ });
+
it('renders issue ID with #', () => {
expect(wrapper.find('.board-card-number').text()).toContain(`#${issue.iid}`);
});
diff --git a/spec/frontend/boards/board_list_helper.js b/spec/frontend/boards/board_list_helper.js
index 04192489817..65a41c49e7f 100644
--- a/spec/frontend/boards/board_list_helper.js
+++ b/spec/frontend/boards/board_list_helper.js
@@ -75,6 +75,7 @@ export default function createComponent({
id: 1,
iid: 1,
confidential: false,
+ referencePath: 'gitlab-org/test-subgroup/gitlab-test#1',
labels: [],
assignees: [],
...listIssueProps,
diff --git a/spec/frontend/boards/components/__snapshots__/board_blocked_icon_spec.js.snap b/spec/frontend/boards/components/__snapshots__/board_blocked_icon_spec.js.snap
index 3fb0706fd10..34e4f996ff0 100644
--- a/spec/frontend/boards/components/__snapshots__/board_blocked_icon_spec.js.snap
+++ b/spec/frontend/boards/components/__snapshots__/board_blocked_icon_spec.js.snap
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BoardBlockedIcon on mouseenter on blocked icon with more than three blocking issues matches the snapshot 1`] = `
-"<div class=\\"gl-display-inline\\"><svg data-testid=\\"issue-blocked-icon\\" role=\\"img\\" aria-hidden=\\"true\\" class=\\"issue-blocked-icon gl-mr-2 gl-cursor-pointer gl-icon s16\\" id=\\"blocked-icon-uniqueId\\">
+"<div class=\\"gl-display-inline\\"><svg data-testid=\\"issue-blocked-icon\\" role=\\"img\\" aria-hidden=\\"true\\" class=\\"issue-blocked-icon gl-mr-2 gl-cursor-pointer gl-text-red-500 gl-icon s16\\" id=\\"blocked-icon-uniqueId\\">
<use href=\\"#issue-block\\"></use>
</svg>
<div class=\\"gl-popover\\">
diff --git a/spec/frontend/boards/components/board_blocked_icon_spec.js b/spec/frontend/boards/components/board_blocked_icon_spec.js
index cf4ba07da16..ffdc0a7cecc 100644
--- a/spec/frontend/boards/components/board_blocked_icon_spec.js
+++ b/spec/frontend/boards/components/board_blocked_icon_spec.js
@@ -10,13 +10,17 @@ import { blockingIssuablesQueries, issuableTypes } from '~/boards/constants';
import { truncate } from '~/lib/utils/text_utility';
import {
mockIssue,
+ mockEpic,
mockBlockingIssue1,
mockBlockingIssue2,
+ mockBlockingEpic1,
mockBlockingIssuablesResponse1,
mockBlockingIssuablesResponse2,
mockBlockingIssuablesResponse3,
mockBlockedIssue1,
mockBlockedIssue2,
+ mockBlockedEpic1,
+ mockBlockingEpicIssuablesResponse1,
} from '../mock_data';
describe('BoardBlockedIcon', () => {
@@ -51,9 +55,11 @@ describe('BoardBlockedIcon', () => {
const createWrapperWithApollo = ({
item = mockBlockedIssue1,
blockingIssuablesSpy = jest.fn().mockResolvedValue(mockBlockingIssuablesResponse1),
+ issuableItem = mockIssue,
+ issuableType = issuableTypes.issue,
} = {}) => {
mockApollo = createMockApollo([
- [blockingIssuablesQueries[issuableTypes.issue].query, blockingIssuablesSpy],
+ [blockingIssuablesQueries[issuableType].query, blockingIssuablesSpy],
]);
Vue.use(VueApollo);
@@ -62,27 +68,34 @@ describe('BoardBlockedIcon', () => {
apolloProvider: mockApollo,
propsData: {
item: {
- ...mockIssue,
+ ...issuableItem,
...item,
},
uniqueId: 'uniqueId',
- issuableType: issuableTypes.issue,
+ issuableType,
},
attachTo: document.body,
}),
);
};
- const createWrapper = ({ item = {}, queries = {}, data = {}, loading = false } = {}) => {
+ const createWrapper = ({
+ item = {},
+ queries = {},
+ data = {},
+ loading = false,
+ mockIssuable = mockIssue,
+ issuableType = issuableTypes.issue,
+ } = {}) => {
wrapper = extendedWrapper(
shallowMount(BoardBlockedIcon, {
propsData: {
item: {
- ...mockIssue,
+ ...mockIssuable,
...item,
},
uniqueId: 'uniqueid',
- issuableType: issuableTypes.issue,
+ issuableType,
},
data() {
return {
@@ -105,11 +118,24 @@ describe('BoardBlockedIcon', () => {
);
};
- it('should render blocked icon', () => {
- createWrapper();
+ it.each`
+ mockIssuable | issuableType | expectedIcon
+ ${mockIssue} | ${issuableTypes.issue} | ${'issue-block'}
+ ${mockEpic} | ${issuableTypes.epic} | ${'entity-blocked'}
+ `(
+ 'should render blocked icon for $issuableType',
+ ({ mockIssuable, issuableType, expectedIcon }) => {
+ createWrapper({
+ mockIssuable,
+ issuableType,
+ });
- expect(findGlIcon().exists()).toBe(true);
- });
+ expect(findGlIcon().exists()).toBe(true);
+ const icon = findGlIcon();
+ expect(icon.exists()).toBe(true);
+ expect(icon.props('name')).toBe(expectedIcon);
+ },
+ );
it('should display a loading spinner while loading', () => {
createWrapper({ loading: true });
@@ -124,17 +150,29 @@ describe('BoardBlockedIcon', () => {
});
describe('on mouseenter on blocked icon', () => {
- it('should query for blocking issuables and render the result', async () => {
- createWrapperWithApollo();
+ it.each`
+ item | issuableType | mockBlockingIssuable | issuableItem | blockingIssuablesSpy
+ ${mockBlockedIssue1} | ${issuableTypes.issue} | ${mockBlockingIssue1} | ${mockIssue} | ${jest.fn().mockResolvedValue(mockBlockingIssuablesResponse1)}
+ ${mockBlockedEpic1} | ${issuableTypes.epic} | ${mockBlockingEpic1} | ${mockEpic} | ${jest.fn().mockResolvedValue(mockBlockingEpicIssuablesResponse1)}
+ `(
+ 'should query for blocking issuables and render the result for $issuableType',
+ async ({ item, issuableType, issuableItem, mockBlockingIssuable, blockingIssuablesSpy }) => {
+ createWrapperWithApollo({
+ item,
+ issuableType,
+ issuableItem,
+ blockingIssuablesSpy,
+ });
- expect(findGlPopover().text()).not.toContain(mockBlockingIssue1.title);
+ expect(findGlPopover().text()).not.toContain(mockBlockingIssuable.title);
- await mouseenter();
+ await mouseenter();
- expect(findGlPopover().exists()).toBe(true);
- expect(findIssuableTitle().text()).toContain(mockBlockingIssue1.title);
- expect(wrapper.vm.skip).toBe(true);
- });
+ expect(findGlPopover().exists()).toBe(true);
+ expect(findIssuableTitle().text()).toContain(mockBlockingIssuable.title);
+ expect(wrapper.vm.skip).toBe(true);
+ },
+ );
it('should emit "blocking-issuables-error" event on query error', async () => {
const mockError = new Error('mayday');
diff --git a/spec/frontend/boards/components/board_card_move_to_position_spec.js b/spec/frontend/boards/components/board_card_move_to_position_spec.js
new file mode 100644
index 00000000000..7254b9486ef
--- /dev/null
+++ b/spec/frontend/boards/components/board_card_move_to_position_spec.js
@@ -0,0 +1,133 @@
+import { shallowMount } from '@vue/test-utils';
+import Vue, { nextTick } from 'vue';
+import Vuex from 'vuex';
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+
+import BoardCardMoveToPosition from '~/boards/components/board_card_move_to_position.vue';
+import { mockList, mockIssue2, mockIssue, mockIssue3, mockIssue4 } from 'jest/boards/mock_data';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
+
+Vue.use(Vuex);
+
+const dropdownOptions = [
+ BoardCardMoveToPosition.i18n.moveToStartText,
+ BoardCardMoveToPosition.i18n.moveToEndText,
+];
+
+describe('Board Card Move to position', () => {
+ let wrapper;
+ let trackingSpy;
+ let store;
+ let dispatch;
+ const itemIndex = 1;
+
+ const createStoreOptions = () => {
+ const state = {
+ pageInfoByListId: {
+ 'gid://gitlab/List/1': {},
+ 'gid://gitlab/List/2': { hasNextPage: true },
+ },
+ };
+ const getters = {
+ getBoardItemsByList: () => () => [mockIssue, mockIssue2, mockIssue3, mockIssue4],
+ };
+ const actions = {
+ moveItem: jest.fn(),
+ };
+
+ return {
+ state,
+ getters,
+ actions,
+ };
+ };
+
+ const createComponent = (propsData) => {
+ wrapper = shallowMount(BoardCardMoveToPosition, {
+ store,
+ propsData: {
+ item: mockIssue2,
+ list: mockList,
+ index: 0,
+ ...propsData,
+ },
+ stubs: {
+ GlDropdown,
+ GlDropdownItem,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ store = new Vuex.Store(createStoreOptions());
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findMoveToPositionDropdown = () => wrapper.findComponent(GlDropdown);
+ const findDropdownItems = () => findMoveToPositionDropdown().findAllComponents(GlDropdownItem);
+ const findDropdownItemAtIndex = (index) => findDropdownItems().at(index);
+
+ describe('Dropdown', () => {
+ describe('Dropdown button', () => {
+ it('has an icon with vertical ellipsis', () => {
+ expect(findMoveToPositionDropdown().exists()).toBe(true);
+ expect(findMoveToPositionDropdown().props('icon')).toBe('ellipsis_v');
+ });
+
+ it('is opened on the click of vertical ellipsis and has 2 dropdown items when number of list items < 10', () => {
+ findMoveToPositionDropdown().vm.$emit('click');
+ expect(findDropdownItems()).toHaveLength(dropdownOptions.length);
+ });
+ });
+
+ describe('Dropdown options', () => {
+ beforeEach(() => {
+ createComponent({ index: itemIndex });
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ dispatch = jest.spyOn(store, 'dispatch').mockImplementation(() => {});
+ });
+
+ afterEach(() => {
+ unmockTracking();
+ });
+
+ it.each`
+ dropdownIndex | dropdownLabel | trackLabel | positionInList
+ ${0} | ${BoardCardMoveToPosition.i18n.moveToStartText} | ${'move_to_start'} | ${0}
+ ${1} | ${BoardCardMoveToPosition.i18n.moveToEndText} | ${'move_to_end'} | ${-1}
+ `(
+ 'on click of dropdown index $dropdownIndex with label $dropdownLabel should call moveItem action with tracking label $trackLabel',
+ async ({ dropdownIndex, dropdownLabel, trackLabel, positionInList }) => {
+ await findMoveToPositionDropdown().vm.$emit('click');
+
+ expect(findDropdownItemAtIndex(dropdownIndex).text()).toBe(dropdownLabel);
+ await findDropdownItemAtIndex(dropdownIndex).vm.$emit('click', {
+ stopPropagation: () => {},
+ });
+
+ await nextTick();
+
+ expect(trackingSpy).toHaveBeenCalledWith('boards:list', 'click_toggle_button', {
+ category: 'boards:list',
+ label: trackLabel,
+ property: 'type_card',
+ });
+ expect(dispatch).toHaveBeenCalledWith('moveItem', {
+ fromListId: mockList.id,
+ itemId: mockIssue2.id,
+ itemIid: mockIssue2.iid,
+ itemPath: mockIssue2.referencePath,
+ positionInList,
+ toListId: mockList.id,
+ allItemsLoadedInList: true,
+ atIndex: itemIndex,
+ });
+ },
+ );
+ });
+ });
+});
diff --git a/spec/frontend/boards/components/board_card_spec.js b/spec/frontend/boards/components/board_card_spec.js
index bb1e63a581e..2feaa5dff8c 100644
--- a/spec/frontend/boards/components/board_card_spec.js
+++ b/spec/frontend/boards/components/board_card_spec.js
@@ -1,5 +1,5 @@
import { GlLabel } from '@gitlab/ui';
-import { shallowMount, mount } from '@vue/test-utils';
+import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
@@ -45,7 +45,10 @@ describe('Board card', () => {
item = mockIssue,
} = {}) => {
wrapper = mountFn(BoardCard, {
- stubs,
+ stubs: {
+ ...stubs,
+ BoardCardInner,
+ },
store,
propsData: {
list: mockLabelList,
@@ -86,7 +89,7 @@ describe('Board card', () => {
describe('when GlLabel is clicked in BoardCardInner', () => {
it('doesnt call toggleBoardItem', () => {
createStore({ initialState: { isShowingLabels: true } });
- mountComponent({ mountFn: mount, stubs: {} });
+ mountComponent();
wrapper.findComponent(GlLabel).trigger('mouseup');
diff --git a/spec/frontend/boards/components/board_new_issue_spec.js b/spec/frontend/boards/components/board_new_issue_spec.js
index 8b0100d069a..f097f42476a 100644
--- a/spec/frontend/boards/components/board_new_issue_spec.js
+++ b/spec/frontend/boards/components/board_new_issue_spec.js
@@ -90,7 +90,7 @@ describe('Issue boards new issue form', () => {
});
});
- it('it uses the first issue ID as moveAfterId', async () => {
+ it('uses the first issue ID as moveAfterId', async () => {
findBoardNewItem().vm.$emit('form-submit', { title: 'Foo' });
await nextTick();
diff --git a/spec/frontend/boards/components/issue_due_date_spec.js b/spec/frontend/boards/components/issue_due_date_spec.js
index 73340c1b96b..45fa10bf03a 100644
--- a/spec/frontend/boards/components/issue_due_date_spec.js
+++ b/spec/frontend/boards/components/issue_due_date_spec.js
@@ -1,6 +1,6 @@
import { shallowMount } from '@vue/test-utils';
-import dateFormat from 'dateformat';
import IssueDueDate from '~/boards/components/issue_due_date.vue';
+import dateFormat from '~/lib/dateformat';
const createComponent = (dueDate = new Date(), closed = false) =>
shallowMount(IssueDueDate, {
diff --git a/spec/frontend/boards/components/item_count_spec.js b/spec/frontend/boards/components/item_count_spec.js
index 06cd3910fc0..0c0c7f66933 100644
--- a/spec/frontend/boards/components/item_count_spec.js
+++ b/spec/frontend/boards/components/item_count_spec.js
@@ -50,7 +50,7 @@ describe('IssueCount', () => {
});
it('contains maxIssueCount in the template', () => {
- expect(vm.find('.max-issue-size').text()).toEqual(String(maxIssueCount));
+ expect(vm.find('.max-issue-size').text()).toContain(String(maxIssueCount));
});
it('does not have text-danger class when issueSize is less than maxIssueCount', () => {
@@ -75,7 +75,7 @@ describe('IssueCount', () => {
});
it('contains maxIssueCount in the template', () => {
- expect(vm.find('.max-issue-size').text()).toEqual(String(maxIssueCount));
+ expect(vm.find('.max-issue-size').text()).toContain(String(maxIssueCount));
});
it('has text-danger class', () => {
diff --git a/spec/frontend/boards/mock_data.js b/spec/frontend/boards/mock_data.js
index 1ee05d81f37..dc1f3246be0 100644
--- a/spec/frontend/boards/mock_data.js
+++ b/spec/frontend/boards/mock_data.js
@@ -262,9 +262,11 @@ export const rawIssue = {
epic: {
id: 'gid://gitlab/Epic/41',
},
+ type: 'ISSUE',
};
export const mockIssueFullPath = 'gitlab-org/test-subgroup/gitlab-test';
+export const mockEpicFullPath = 'gitlab-org/test-subgroup';
export const mockIssue = {
id: 'gid://gitlab/Issue/436',
@@ -287,6 +289,48 @@ export const mockIssue = {
epic: {
id: 'gid://gitlab/Epic/41',
},
+ type: 'ISSUE',
+};
+
+export const mockEpic = {
+ id: 'gid://gitlab/Epic/26',
+ iid: '1',
+ group: {
+ id: 'gid://gitlab/Group/33',
+ fullPath: 'twitter',
+ __typename: 'Group',
+ },
+ title: 'Eum animi debitis occaecati ad non odio repellat voluptatem similique.',
+ state: 'opened',
+ reference: '&1',
+ referencePath: `${mockEpicFullPath}&1`,
+ webPath: `/groups/${mockEpicFullPath}/-/epics/1`,
+ webUrl: `${mockEpicFullPath}/-/epics/1`,
+ createdAt: '2022-01-18T05:15:15Z',
+ closedAt: null,
+ __typename: 'Epic',
+ relativePosition: null,
+ confidential: false,
+ subscribed: true,
+ blocked: true,
+ blockedByCount: 1,
+ labels: {
+ nodes: [],
+ __typename: 'LabelConnection',
+ },
+ hasIssues: true,
+ descendantCounts: {
+ closedEpics: 0,
+ closedIssues: 0,
+ openedEpics: 0,
+ openedIssues: 2,
+ __typename: 'EpicDescendantCount',
+ },
+ descendantWeightSum: {
+ closedIssues: 0,
+ openedIssues: 0,
+ __typename: 'EpicDescendantWeights',
+ },
};
export const mockActiveIssue = {
@@ -521,6 +565,15 @@ export const mockBlockingIssue1 = {
__typename: 'Issue',
};
+export const mockBlockingEpic1 = {
+ id: 'gid://gitlab/Epic/29',
+ iid: '4',
+ title: 'Sint nihil exercitationem aspernatur unde molestiae rem accusantium.',
+ reference: 'twitter&4',
+ webUrl: 'http://gdk.test:3000/groups/gitlab-org/test-subgroup/-/epics/4',
+ __typename: 'Epic',
+};
+
export const mockBlockingIssue2 = {
id: 'gid://gitlab/Issue/524',
iid: '5',
@@ -562,6 +615,23 @@ export const mockBlockingIssuablesResponse1 = {
},
};
+export const mockBlockingEpicIssuablesResponse1 = {
+ data: {
+ group: {
+ __typename: 'Group',
+ id: 'gid://gitlab/Group/33',
+ issuable: {
+ __typename: 'Epic',
+ id: 'gid://gitlab/Epic/26',
+ blockingIssuables: {
+ __typename: 'EpicConnection',
+ nodes: [mockBlockingEpic1],
+ },
+ },
+ },
+ },
+};
+
export const mockBlockingIssuablesResponse2 = {
data: {
issuable: {
@@ -599,6 +669,12 @@ export const mockBlockedIssue2 = {
webUrl: 'http://gdk.test:3000/gitlab-org/my-project-1/-/issues/0',
};
+export const mockBlockedEpic1 = {
+ id: '26',
+ blockedByCount: 1,
+ webUrl: 'http://gdk.test:3000/gitlab-org/test-subgroup/-/epics/1',
+};
+
export const mockMoveIssueParams = {
itemId: 1,
fromListId: 'gid://gitlab/List/1',
diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js
index e48b946ff1b..e919300228a 100644
--- a/spec/frontend/boards/stores/actions_spec.js
+++ b/spec/frontend/boards/stores/actions_spec.js
@@ -1056,6 +1056,8 @@ describe('moveIssueCard and undoMoveIssueCard', () => {
originalIndex = 0,
moveBeforeId = undefined,
moveAfterId = undefined,
+ allItemsLoadedInList = true,
+ listPosition = undefined,
} = {}) => {
state = {
boardLists: {
@@ -1065,12 +1067,28 @@ describe('moveIssueCard and undoMoveIssueCard', () => {
boardItems: { [itemId]: originalIssue },
boardItemsByListId: { [fromListId]: [123] },
};
- params = { itemId, fromListId, toListId, moveBeforeId, moveAfterId };
+ params = {
+ itemId,
+ fromListId,
+ toListId,
+ moveBeforeId,
+ moveAfterId,
+ listPosition,
+ allItemsLoadedInList,
+ };
moveMutations = [
{ type: types.REMOVE_BOARD_ITEM_FROM_LIST, payload: { itemId, listId: fromListId } },
{
type: types.ADD_BOARD_ITEM_TO_LIST,
- payload: { itemId, listId: toListId, moveBeforeId, moveAfterId },
+ payload: {
+ itemId,
+ listId: toListId,
+ moveBeforeId,
+ moveAfterId,
+ listPosition,
+ allItemsLoadedInList,
+ atIndex: originalIndex,
+ },
},
];
undoMutations = [
@@ -1366,9 +1384,17 @@ describe('updateIssueOrder', () => {
state,
[
{
+ type: types.MUTATE_ISSUE_IN_PROGRESS,
+ payload: true,
+ },
+ {
type: types.MUTATE_ISSUE_SUCCESS,
payload: { issue: rawIssue },
},
+ {
+ type: types.MUTATE_ISSUE_IN_PROGRESS,
+ payload: false,
+ },
],
[],
);
@@ -1390,6 +1416,14 @@ describe('updateIssueOrder', () => {
state,
[
{
+ type: types.MUTATE_ISSUE_IN_PROGRESS,
+ payload: true,
+ },
+ {
+ type: types.MUTATE_ISSUE_IN_PROGRESS,
+ payload: false,
+ },
+ {
type: types.SET_ERROR,
payload: 'An error occurred while moving the issue. Please try again.',
},
diff --git a/spec/frontend/boards/stores/mutations_spec.js b/spec/frontend/boards/stores/mutations_spec.js
index 1606ca09d8f..87a183c0441 100644
--- a/spec/frontend/boards/stores/mutations_spec.js
+++ b/spec/frontend/boards/stores/mutations_spec.js
@@ -513,6 +513,31 @@ describe('Board Store Mutations', () => {
listState: [mockIssue2.id, mockIssue.id],
},
],
+ [
+ 'to the top of the list',
+ {
+ payload: {
+ itemId: mockIssue2.id,
+ listId: mockList.id,
+ positionInList: 0,
+ atIndex: 1,
+ },
+ listState: [mockIssue2.id, mockIssue.id],
+ },
+ ],
+ [
+ 'to the bottom of the list when the list is fully loaded',
+ {
+ payload: {
+ itemId: mockIssue2.id,
+ listId: mockList.id,
+ positionInList: -1,
+ atIndex: 0,
+ allItemsLoadedInList: true,
+ },
+ listState: [mockIssue.id, mockIssue2.id],
+ },
+ ],
])(`inserts an item into a list %s`, (_, { payload, listState }) => {
mutations.ADD_BOARD_ITEM_TO_LIST(state, payload);
diff --git a/spec/frontend/branches/components/divergence_graph_spec.js b/spec/frontend/branches/components/divergence_graph_spec.js
index 3b565539f87..9429a6e982c 100644
--- a/spec/frontend/branches/components/divergence_graph_spec.js
+++ b/spec/frontend/branches/components/divergence_graph_spec.js
@@ -21,7 +21,7 @@ describe('Branch divergence graph component', () => {
maxCommits: 100,
});
- expect(vm.findAll(GraphBar).length).toBe(2);
+ expect(vm.findAllComponents(GraphBar).length).toBe(2);
expect(vm.element).toMatchSnapshot();
});
@@ -45,7 +45,7 @@ describe('Branch divergence graph component', () => {
maxCommits: 100,
});
- expect(vm.findAll(GraphBar).length).toBe(1);
+ expect(vm.findAllComponents(GraphBar).length).toBe(1);
expect(vm.element).toMatchSnapshot();
});
diff --git a/spec/frontend/captcha/captcha_modal_spec.js b/spec/frontend/captcha/captcha_modal_spec.js
index b8448f9ff0a..20e69b5a834 100644
--- a/spec/frontend/captcha/captcha_modal_spec.js
+++ b/spec/frontend/captcha/captcha_modal_spec.js
@@ -40,7 +40,7 @@ describe('Captcha Modal', () => {
});
const findGlModal = () => {
- const glModal = wrapper.find(GlModal);
+ const glModal = wrapper.findComponent(GlModal);
jest.spyOn(glModal.vm, 'show').mockImplementation(() => glModal.vm.$emit('shown'));
jest
diff --git a/spec/frontend/cascading_settings/components/lock_popovers_spec.js b/spec/frontend/cascading_settings/components/lock_popovers_spec.js
index 182e3c1c8ff..9d3275a1ff2 100644
--- a/spec/frontend/cascading_settings/components/lock_popovers_spec.js
+++ b/spec/frontend/cascading_settings/components/lock_popovers_spec.js
@@ -39,7 +39,7 @@ describe('LockPopovers', () => {
wrapper = mountExtended(LockPopovers);
};
- const findPopover = () => extendedWrapper(wrapper.find(GlPopover));
+ const findPopover = () => extendedWrapper(wrapper.findComponent(GlPopover));
const findByTextInPopover = (text, options) =>
findPopover().findByText((_, element) => element.textContent === text, options);
@@ -143,7 +143,7 @@ describe('LockPopovers', () => {
});
it('mounts multiple popovers', () => {
- const popovers = wrapper.findAll(GlPopover).wrappers;
+ const popovers = wrapper.findAllComponents(GlPopover).wrappers;
expectCorrectPopoverTarget(popoverMountEl1, popovers[0]);
expectCorrectPopoverTarget(popoverMountEl2, popovers[1]);
diff --git a/spec/frontend/chronic_duration_spec.js b/spec/frontend/chronic_duration_spec.js
index 32652e13dfc..b063110782a 100644
--- a/spec/frontend/chronic_duration_spec.js
+++ b/spec/frontend/chronic_duration_spec.js
@@ -86,7 +86,7 @@ describe('parseChronicDuration', () => {
describe('when .raiseExceptions set to true', () => {
it('raises with DurationParseError', () => {
- expect(() => parseChronicDuration('23 gobblygoos', { raiseExceptions: true })).toThrowError(
+ expect(() => parseChronicDuration('23 gobblygoos', { raiseExceptions: true })).toThrow(
DurationParseError,
);
});
diff --git a/spec/frontend/ci_lint/components/ci_lint_spec.js b/spec/frontend/ci_lint/components/ci_lint_spec.js
index 0ad6ed56b0e..ea69a80274e 100644
--- a/spec/frontend/ci_lint/components/ci_lint_spec.js
+++ b/spec/frontend/ci_lint/components/ci_lint_spec.js
@@ -36,9 +36,9 @@ describe('CI Lint', () => {
});
};
- const findEditor = () => wrapper.find(SourceEditor);
- const findAlert = () => wrapper.find(GlAlert);
- const findCiLintResults = () => wrapper.find(CiLintResults);
+ 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"]');
diff --git a/spec/frontend/ci_secure_files/components/secure_files_list_spec.js b/spec/frontend/ci_secure_files/components/secure_files_list_spec.js
index 04d38a3281a..5273aafbb04 100644
--- a/spec/frontend/ci_secure_files/components/secure_files_list_spec.js
+++ b/spec/frontend/ci_secure_files/components/secure_files_list_spec.js
@@ -83,7 +83,9 @@ describe('SecureFilesList', () => {
const [secureFile] = secureFiles;
expect(findCell(0, 0).text()).toBe(secureFile.name);
- expect(findCell(0, 1).find(TimeAgoTooltip).props('time')).toBe(secureFile.created_at);
+ expect(findCell(0, 1).findComponent(TimeAgoTooltip).props('time')).toBe(
+ secureFile.created_at,
+ );
});
describe('event tracking', () => {
diff --git a/spec/frontend/ci_settings_pipeline_triggers/components/triggers_list_spec.js b/spec/frontend/ci_settings_pipeline_triggers/components/triggers_list_spec.js
index 6bf28a67300..01eb08f4ece 100644
--- a/spec/frontend/ci_settings_pipeline_triggers/components/triggers_list_spec.js
+++ b/spec/frontend/ci_settings_pipeline_triggers/components/triggers_list_spec.js
@@ -16,13 +16,13 @@ describe('TriggersList', () => {
});
};
- const findTable = () => wrapper.find(GlTable);
+ const findTable = () => wrapper.findComponent(GlTable);
const findHeaderAt = (i) => wrapper.findAll('thead th').at(i);
const findRows = () => wrapper.findAll('tbody tr');
const findRowAt = (i) => findRows().at(i);
const findCell = (i, col) => findRowAt(i).findAll('td').at(col);
- const findClipboardBtn = (i) => findCell(i, 0).find(ClipboardButton);
- const findInvalidBadge = (i) => findCell(i, 0).find(GlBadge);
+ const findClipboardBtn = (i) => findCell(i, 0).findComponent(ClipboardButton);
+ const findInvalidBadge = (i) => findCell(i, 0).findComponent(GlBadge);
const findEditBtn = (i) => findRowAt(i).find('[data-testid="edit-btn"]');
const findRevokeBtn = (i) => findRowAt(i).find('[data-testid="trigger_revoke_button"]');
@@ -65,17 +65,19 @@ describe('TriggersList', () => {
it('displays a time ago label when last used', () => {
expect(findCell(0, 3).text()).toBe('Never');
- expect(findCell(1, 3).find(TimeAgoTooltip).props('time')).toBe(triggers[1].lastUsed);
+ expect(findCell(1, 3).findComponent(TimeAgoTooltip).props('time')).toBe(triggers[1].lastUsed);
});
it('displays actions in a rows', () => {
const [data] = triggers;
+ const confirmWarning =
+ 'By revoking a trigger you will break any processes making use of it. Are you sure?';
expect(findEditBtn(0).attributes('href')).toBe(data.editProjectTriggerPath);
expect(findRevokeBtn(0).attributes('href')).toBe(data.projectTriggerPath);
expect(findRevokeBtn(0).attributes('data-method')).toBe('delete');
- expect(findRevokeBtn(0).attributes('data-confirm')).toBeTruthy();
+ expect(findRevokeBtn(0).attributes('data-confirm')).toBe(confirmWarning);
});
describe('when there are no triggers set', () => {
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
new file mode 100644
index 00000000000..867f8e0cf8f
--- /dev/null
+++ b/spec/frontend/ci_variable_list/components/ci_project_variables_spec.js
@@ -0,0 +1,215 @@
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
+import { GlLoadingIcon, GlTable } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import createFlash from '~/flash';
+import { resolvers } from '~/ci_variable_list/graphql/resolvers';
+import { convertToGraphQLId } from '~/graphql_shared/utils';
+
+import ciProjectVariables from '~/ci_variable_list/components/ci_project_variables.vue';
+import ciVariableSettings from '~/ci_variable_list/components/ci_variable_settings.vue';
+import ciVariableTable from '~/ci_variable_list/components/ci_variable_table.vue';
+import getProjectEnvironments from '~/ci_variable_list/graphql/queries/project_environments.query.graphql';
+import getProjectVariables from '~/ci_variable_list/graphql/queries/project_variables.query.graphql';
+
+import addProjectVariable from '~/ci_variable_list/graphql/mutations/project_add_variable.mutation.graphql';
+import deleteProjectVariable from '~/ci_variable_list/graphql/mutations/project_delete_variable.mutation.graphql';
+import updateProjectVariable from '~/ci_variable_list/graphql/mutations/project_update_variable.mutation.graphql';
+
+import {
+ environmentFetchErrorText,
+ genericMutationErrorText,
+ variableFetchErrorText,
+} from '~/ci_variable_list/constants';
+
+import {
+ devName,
+ mockProjectEnvironments,
+ mockProjectVariables,
+ newVariable,
+ prodName,
+} from '../mocks';
+
+jest.mock('~/flash');
+
+Vue.use(VueApollo);
+
+const mockProvide = {
+ endpoint: '/variables',
+ projectFullPath: '/namespace/project',
+ projectId: 1,
+};
+
+describe('Ci Project Variable list', () => {
+ let wrapper;
+
+ let mockApollo;
+ let mockEnvironments;
+ let mockVariables;
+
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findCiTable = () => wrapper.findComponent(GlTable);
+ const findCiSettings = () => wrapper.findComponent(ciVariableSettings);
+
+ // eslint-disable-next-line consistent-return
+ const createComponentWithApollo = async ({ isLoading = false } = {}) => {
+ const handlers = [
+ [getProjectEnvironments, mockEnvironments],
+ [getProjectVariables, mockVariables],
+ ];
+
+ mockApollo = createMockApollo(handlers, resolvers);
+
+ wrapper = shallowMount(ciProjectVariables, {
+ provide: mockProvide,
+ apolloProvider: mockApollo,
+ stubs: { ciVariableSettings, ciVariableTable },
+ });
+
+ if (!isLoading) {
+ return waitForPromises();
+ }
+ };
+
+ beforeEach(() => {
+ mockEnvironments = jest.fn();
+ mockVariables = jest.fn();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('while queries are being fetch', () => {
+ beforeEach(() => {
+ createComponentWithApollo({ isLoading: true });
+ });
+
+ it('shows a loading icon', () => {
+ expect(findLoadingIcon().exists()).toBe(true);
+ expect(findCiTable().exists()).toBe(false);
+ });
+ });
+
+ describe('when queries are resolved', () => {
+ describe('successfuly', () => {
+ beforeEach(async () => {
+ mockEnvironments.mockResolvedValue(mockProjectEnvironments);
+ mockVariables.mockResolvedValue(mockProjectVariables);
+
+ await createComponentWithApollo();
+ });
+
+ it('passes down the expected environments as props', () => {
+ expect(findCiSettings().props('environments')).toEqual([prodName, devName]);
+ });
+
+ it('passes down the expected variables as props', () => {
+ expect(findCiSettings().props('variables')).toEqual(
+ mockProjectVariables.data.project.ciVariables.nodes,
+ );
+ });
+
+ it('createFlash was not called', () => {
+ expect(createFlash).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('with an error for variables', () => {
+ beforeEach(async () => {
+ mockEnvironments.mockResolvedValue(mockProjectEnvironments);
+ mockVariables.mockRejectedValue();
+
+ await createComponentWithApollo();
+ });
+
+ it('calls createFlash with the expected error message', () => {
+ expect(createFlash).toHaveBeenCalledWith({ message: variableFetchErrorText });
+ });
+ });
+
+ describe('with an error for environments', () => {
+ beforeEach(async () => {
+ mockEnvironments.mockRejectedValue();
+ mockVariables.mockResolvedValue(mockProjectVariables);
+
+ await createComponentWithApollo();
+ });
+
+ it('calls createFlash with the expected error message', () => {
+ expect(createFlash).toHaveBeenCalledWith({ message: environmentFetchErrorText });
+ });
+ });
+ });
+
+ describe('mutations', () => {
+ beforeEach(async () => {
+ mockEnvironments.mockResolvedValue(mockProjectEnvironments);
+ mockVariables.mockResolvedValue(mockProjectVariables);
+
+ await createComponentWithApollo();
+ });
+ it.each`
+ actionName | mutation | event
+ ${'add'} | ${addProjectVariable} | ${'add-variable'}
+ ${'update'} | ${updateProjectVariable} | ${'update-variable'}
+ ${'delete'} | ${deleteProjectVariable} | ${'delete-variable'}
+ `(
+ 'calls the right mutation when user performs $actionName variable',
+ async ({ event, mutation }) => {
+ jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue();
+ await findCiSettings().vm.$emit(event, newVariable);
+
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
+ mutation,
+ variables: {
+ endpoint: mockProvide.endpoint,
+ fullPath: mockProvide.projectFullPath,
+ projectId: convertToGraphQLId('Project', mockProvide.projectId),
+ variable: newVariable,
+ },
+ });
+ },
+ );
+
+ it.each`
+ actionName | event | mutationName
+ ${'add'} | ${'add-variable'} | ${'addProjectVariable'}
+ ${'update'} | ${'update-variable'} | ${'updateProjectVariable'}
+ ${'delete'} | ${'delete-variable'} | ${'deleteProjectVariable'}
+ `(
+ 'throws with the specific graphql error if present when user performs $actionName variable',
+ async ({ event, mutationName }) => {
+ const graphQLErrorMessage = 'There is a problem with this graphQL action';
+ jest
+ .spyOn(wrapper.vm.$apollo, 'mutate')
+ .mockResolvedValue({ data: { [mutationName]: { errors: [graphQLErrorMessage] } } });
+ await findCiSettings().vm.$emit(event, newVariable);
+ await nextTick();
+
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalled();
+ expect(createFlash).toHaveBeenCalledWith({ message: graphQLErrorMessage });
+ },
+ );
+
+ it.each`
+ actionName | event
+ ${'add'} | ${'add-variable'}
+ ${'update'} | ${'update-variable'}
+ ${'delete'} | ${'delete-variable'}
+ `(
+ 'throws generic error when the mutation fails with no graphql errors and user performs $actionName variable',
+ async ({ event }) => {
+ jest.spyOn(wrapper.vm.$apollo, 'mutate').mockImplementationOnce(() => {
+ throw new Error();
+ });
+ await findCiSettings().vm.$emit(event, newVariable);
+
+ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalled();
+ expect(createFlash).toHaveBeenCalledWith({ message: genericMutationErrorText });
+ },
+ );
+ });
+});
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 e5019e3261e..1ea4e4f833b 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
@@ -11,6 +11,7 @@ import {
EVENT_ACTION,
ENVIRONMENT_SCOPE_LINK_TITLE,
instanceString,
+ variableOptions,
} from '~/ci_variable_list/constants';
import { mockVariablesWithScopes } from '../mocks';
import ModalStub from '../stubs';
@@ -57,21 +58,23 @@ describe('Ci variable modal', () => {
});
};
- const findCiEnvironmentsDropdown = () => wrapper.find(CiEnvironmentsDropdown);
+ const findCiEnvironmentsDropdown = () => wrapper.findComponent(CiEnvironmentsDropdown);
const findReferenceWarning = () => wrapper.findByTestId('contains-variable-reference');
- const findModal = () => wrapper.find(ModalStub);
+ const findModal = () => wrapper.findComponent(ModalStub);
const findAWSTip = () => wrapper.findByTestId('aws-guidance-tip');
const findAddorUpdateButton = () => wrapper.findByTestId('ciUpdateOrAddVariableBtn');
const deleteVariableButton = () =>
findModal()
- .findAll(GlButton)
+ .findAllComponents(GlButton)
.wrappers.find((button) => button.props('variant') === 'danger');
const findProtectedVariableCheckbox = () =>
wrapper.findByTestId('ci-variable-protected-checkbox');
const findMaskedVariableCheckbox = () => wrapper.findByTestId('ci-variable-masked-checkbox');
const findValueField = () => wrapper.find('#ci-variable-value');
const findEnvScopeLink = () => wrapper.findByTestId('environment-scope-link');
- const findEnvScopeInput = () => wrapper.findByTestId('environment-scope').find(GlFormInput);
+ const findEnvScopeInput = () =>
+ wrapper.findByTestId('environment-scope').findComponent(GlFormInput);
+ const findVariableTypeDropdown = () => wrapper.find('#ci-variable-type');
afterEach(() => {
wrapper.destroy();
@@ -83,7 +86,7 @@ describe('Ci variable modal', () => {
createComponent();
});
- it('shows the submit button as disabled ', () => {
+ it('shows the submit button as disabled', () => {
expect(findAddorUpdateButton().attributes('disabled')).toBe('true');
});
});
@@ -93,7 +96,7 @@ describe('Ci variable modal', () => {
createComponent({ props: { selectedVariable: mockVariables[0] } });
});
- it('shows the submit button as enabled ', () => {
+ it('shows the submit button as enabled', () => {
expect(findAddorUpdateButton().attributes('disabled')).toBeUndefined();
});
});
@@ -284,6 +287,21 @@ describe('Ci variable modal', () => {
});
});
+ describe('variable type dropdown', () => {
+ describe('default behaviour', () => {
+ beforeEach(() => {
+ createComponent({ mountFn: mountExtended });
+ });
+
+ it('adds each option as a dropdown item', () => {
+ expect(findVariableTypeDropdown().findAll('option')).toHaveLength(variableOptions.length);
+ variableOptions.forEach((v) => {
+ expect(findVariableTypeDropdown().text()).toContain(v.text);
+ });
+ });
+ });
+ });
+
describe('Validations', () => {
const maskError = 'This variable can not be masked.';
diff --git a/spec/frontend/ci_variable_list/components/ci_variable_popover_spec.js b/spec/frontend/ci_variable_list/components/ci_variable_popover_spec.js
index b43153d3d7c..4d0c378d10e 100644
--- a/spec/frontend/ci_variable_list/components/ci_variable_popover_spec.js
+++ b/spec/frontend/ci_variable_list/components/ci_variable_popover_spec.js
@@ -18,7 +18,7 @@ describe('Ci Variable Popover', () => {
});
};
- const findButton = () => wrapper.find(GlButton);
+ const findButton = () => wrapper.findComponent(GlButton);
beforeEach(() => {
createComponent();
diff --git a/spec/frontend/ci_variable_list/components/legacy_ci_variable_modal_spec.js b/spec/frontend/ci_variable_list/components/legacy_ci_variable_modal_spec.js
index 6681ab91a4a..b607232907b 100644
--- a/spec/frontend/ci_variable_list/components/legacy_ci_variable_modal_spec.js
+++ b/spec/frontend/ci_variable_list/components/legacy_ci_variable_modal_spec.js
@@ -40,12 +40,12 @@ describe('Ci variable modal', () => {
});
};
- const findCiEnvironmentsDropdown = () => wrapper.find(CiEnvironmentsDropdown);
- const findModal = () => wrapper.find(ModalStub);
+ const findCiEnvironmentsDropdown = () => wrapper.findComponent(CiEnvironmentsDropdown);
+ const findModal = () => wrapper.findComponent(ModalStub);
const findAddorUpdateButton = () => findModal().find('[data-testid="ciUpdateOrAddVariableBtn"]');
const deleteVariableButton = () =>
findModal()
- .findAll(GlButton)
+ .findAllComponents(GlButton)
.wrappers.find((button) => button.props('variant') === 'danger');
afterEach(() => {
@@ -213,7 +213,7 @@ describe('Ci variable modal', () => {
const environmentScopeInput = wrapper
.find('[data-testid="environment-scope"]')
- .find(GlFormInput);
+ .findComponent(GlFormInput);
expect(findCiEnvironmentsDropdown().exists()).toBe(false);
expect(environmentScopeInput.attributes('readonly')).toBe('readonly');
});
diff --git a/spec/frontend/ci_variable_list/mocks.js b/spec/frontend/ci_variable_list/mocks.js
index 89ba77858dc..6d633c8b740 100644
--- a/spec/frontend/ci_variable_list/mocks.js
+++ b/spec/frontend/ci_variable_list/mocks.js
@@ -1,4 +1,9 @@
-import { variableTypes, groupString, instanceString } from '~/ci_variable_list/constants';
+import {
+ variableTypes,
+ groupString,
+ instanceString,
+ projectString,
+} from '~/ci_variable_list/constants';
export const devName = 'dev';
export const prodName = 'prod';
@@ -11,8 +16,8 @@ export const mockVariables = (kind) => {
key: 'my-var',
masked: false,
protected: true,
- value: 'env_val',
- variableType: variableTypes.variableType,
+ value: 'variable_value',
+ variableType: variableTypes.envType,
},
{
__typename: `Ci${kind}Variable`,
@@ -20,7 +25,7 @@ export const mockVariables = (kind) => {
key: 'secret',
masked: true,
protected: false,
- value: 'the_secret_value',
+ value: 'another_value',
variableType: variableTypes.fileType,
},
];
@@ -77,7 +82,7 @@ export const mockProjectVariables = {
project: {
__typename: 'Project',
id: 1,
- ciVariables: createDefaultVars(),
+ ciVariables: createDefaultVars({ kind: projectString }),
},
},
};
diff --git a/spec/frontend/ci_variable_list/store/mutations_spec.js b/spec/frontend/ci_variable_list/store/mutations_spec.js
index ae750ff426d..c7d07ead09b 100644
--- a/spec/frontend/ci_variable_list/store/mutations_spec.js
+++ b/spec/frontend/ci_variable_list/store/mutations_spec.js
@@ -36,7 +36,7 @@ describe('CI variable list mutations', () => {
});
describe('CLEAR_MODAL', () => {
- it('should clear modal state ', () => {
+ it('should clear modal state', () => {
const modalState = {
variable_type: 'Variable',
key: '',
diff --git a/spec/frontend/clusters/agents/components/agent_integration_status_row_spec.js b/spec/frontend/clusters/agents/components/agent_integration_status_row_spec.js
new file mode 100644
index 00000000000..2af64191a88
--- /dev/null
+++ b/spec/frontend/clusters/agents/components/agent_integration_status_row_spec.js
@@ -0,0 +1,96 @@
+import { GlLink, GlIcon, GlBadge } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import AgentIntegrationStatusRow from '~/clusters/agents/components/agent_integration_status_row.vue';
+
+const defaultProps = {
+ text: 'Default integration status',
+};
+
+describe('IntegrationStatus', () => {
+ let wrapper;
+
+ const createWrapper = ({ props = {}, glFeatures = {} } = {}) => {
+ wrapper = shallowMount(AgentIntegrationStatusRow, {
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ provide: {
+ glFeatures,
+ },
+ });
+ };
+
+ const findLink = () => wrapper.findComponent(GlLink);
+ const findIcon = () => wrapper.findComponent(GlIcon);
+ const findBadge = () => wrapper.findComponent(GlBadge);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('icon', () => {
+ const icon = 'status-success';
+ const iconClass = 'text-success-500';
+ it.each`
+ props | iconName | iconClassName
+ ${{ icon, iconClass }} | ${icon} | ${iconClass}
+ ${{ icon }} | ${icon} | ${'text-info'}
+ ${{ iconClass }} | ${'information'} | ${iconClass}
+ ${null} | ${'information'} | ${'text-info'}
+ `('displays correct icon when props are $props', ({ props, iconName, iconClassName }) => {
+ createWrapper({ props });
+
+ expect(findIcon().props('name')).toBe(iconName);
+ expect(findIcon().attributes('class')).toContain(iconClassName);
+ });
+ });
+
+ describe('helpUrl', () => {
+ it('displays a link with the correct help url when provided in props', () => {
+ const props = {
+ helpUrl: 'help-page-path',
+ };
+ createWrapper({ props });
+
+ expect(findLink().attributes('href')).toBe(props.helpUrl);
+ expect(findLink().text()).toBe(defaultProps.text);
+ });
+
+ it("displays the text without a link when it's not provided", () => {
+ createWrapper();
+
+ expect(findLink().exists()).toBe(false);
+ expect(wrapper.text()).toBe(defaultProps.text);
+ });
+ });
+
+ describe('badge', () => {
+ it('does not display premium feature badge when featureName is not provided', () => {
+ createWrapper();
+
+ expect(findBadge().exists()).toBe(false);
+ });
+
+ it('does not display premium feature badge when featureName is provided and is available for the project', () => {
+ const props = { featureName: 'feature' };
+ const glFeatures = { feature: true };
+ createWrapper({ props, glFeatures });
+
+ expect(findBadge().exists()).toBe(false);
+ });
+
+ it('displays premium feature badge when featureName is provided and is not available for the project', () => {
+ const props = { featureName: 'feature' };
+ const glFeatures = { feature: false };
+ createWrapper({ props, glFeatures });
+
+ expect(findBadge().props()).toMatchObject({
+ icon: 'license',
+ variant: 'tier',
+ size: 'md',
+ });
+ expect(findBadge().text()).toBe(wrapper.vm.$options.i18n.premiumTitle);
+ });
+ });
+});
diff --git a/spec/frontend/clusters/agents/components/integration_status_spec.js b/spec/frontend/clusters/agents/components/integration_status_spec.js
new file mode 100644
index 00000000000..36f0e622452
--- /dev/null
+++ b/spec/frontend/clusters/agents/components/integration_status_spec.js
@@ -0,0 +1,111 @@
+import { GlCollapse, GlButton, GlIcon } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import IntegrationStatus from '~/clusters/agents/components/integration_status.vue';
+import AgentIntegrationStatusRow from '~/clusters/agents/components/agent_integration_status_row.vue';
+import { ACTIVE_CONNECTION_TIME } from '~/clusters_list/constants';
+import {
+ INTEGRATION_STATUS_VALID_TOKEN,
+ INTEGRATION_STATUS_NO_TOKEN,
+ INTEGRATION_STATUS_RESTRICTED_CI_CD,
+} from '~/clusters/agents/constants';
+
+const connectedTimeNow = new Date();
+const connectedTimeInactive = new Date(connectedTimeNow.getTime() - ACTIVE_CONNECTION_TIME);
+
+describe('IntegrationStatus', () => {
+ let wrapper;
+
+ const createWrapper = (tokens = []) => {
+ wrapper = shallowMountExtended(IntegrationStatus, {
+ propsData: { tokens },
+ });
+ };
+
+ const findCollapseButton = () => wrapper.findComponent(GlButton);
+ const findCollapse = () => wrapper.findComponent(GlCollapse);
+ const findStatusIcon = () => wrapper.findComponent(GlIcon);
+ const findAgentStatus = () => wrapper.findByTestId('agent-status');
+ const findAgentIntegrationStatusRows = () => wrapper.findAllComponents(AgentIntegrationStatusRow);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it.each`
+ lastUsedAt | status | iconName
+ ${null} | ${'Never connected'} | ${'status-neutral'}
+ ${connectedTimeNow} | ${'Connected'} | ${'status-success'}
+ ${connectedTimeInactive} | ${'Not connected'} | ${'status-alert'}
+ `(
+ 'displays correct text and icon when agent connection status is "$status"',
+ ({ lastUsedAt, status, iconName }) => {
+ const tokens = [{ lastUsedAt }];
+ createWrapper(tokens);
+
+ expect(findStatusIcon().props('name')).toBe(iconName);
+ expect(findAgentStatus().text()).toBe(status);
+ },
+ );
+
+ describe('default', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it('shows the collapse toggle button', () => {
+ expect(findCollapseButton().text()).toBe(wrapper.vm.$options.i18n.title);
+ expect(findCollapseButton().attributes()).toMatchObject({
+ variant: 'link',
+ icon: 'chevron-right',
+ size: 'small',
+ });
+ });
+
+ it('sets collapse component as invisible by default', () => {
+ expect(findCollapse().props('visible')).toBeUndefined();
+ });
+ });
+
+ describe('when user clicks collapse toggle', () => {
+ beforeEach(() => {
+ createWrapper();
+ findCollapseButton().vm.$emit('click');
+ });
+
+ it('changes the collapse button icon', () => {
+ expect(findCollapseButton().props('icon')).toBe('chevron-down');
+ });
+
+ it('sets collapse component as visible', () => {
+ expect(findCollapse().attributes('visible')).toBe('true');
+ });
+ });
+
+ describe('integration status details', () => {
+ it.each`
+ agentStatus | tokens | integrationStatuses
+ ${'active'} | ${[{ lastUsedAt: connectedTimeNow }]} | ${[INTEGRATION_STATUS_VALID_TOKEN, INTEGRATION_STATUS_RESTRICTED_CI_CD]}
+ ${'inactive'} | ${[{ lastUsedAt: connectedTimeInactive }]} | ${[INTEGRATION_STATUS_RESTRICTED_CI_CD]}
+ ${'inactive'} | ${[]} | ${[INTEGRATION_STATUS_NO_TOKEN, INTEGRATION_STATUS_RESTRICTED_CI_CD]}
+ ${'unused'} | ${[{ lastUsedAt: null }]} | ${[INTEGRATION_STATUS_RESTRICTED_CI_CD]}
+ ${'unused'} | ${[]} | ${[INTEGRATION_STATUS_NO_TOKEN, INTEGRATION_STATUS_RESTRICTED_CI_CD]}
+ `(
+ 'displays AgentIntegrationStatusRow component with correct properties when agent status is $agentStatus and agent has $tokens.length tokens',
+ ({ tokens, integrationStatuses }) => {
+ createWrapper(tokens);
+
+ expect(findAgentIntegrationStatusRows().length).toBe(integrationStatuses.length);
+
+ integrationStatuses.forEach((integrationStatus, index) => {
+ expect(findAgentIntegrationStatusRows().at(index).props()).toMatchObject({
+ icon: integrationStatus.icon,
+ iconClass: integrationStatus.iconClass,
+ text: integrationStatus.text,
+ helpUrl: integrationStatus.helpUrl || null,
+ featureName: integrationStatus.featureName || null,
+ });
+ });
+ },
+ );
+ });
+});
diff --git a/spec/frontend/clusters/agents/components/show_spec.js b/spec/frontend/clusters/agents/components/show_spec.js
index f2f073544e3..efa85136b17 100644
--- a/spec/frontend/clusters/agents/components/show_spec.js
+++ b/spec/frontend/clusters/agents/components/show_spec.js
@@ -6,6 +6,7 @@ import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import ClusterAgentShow from '~/clusters/agents/components/show.vue';
import TokenTable from '~/clusters/agents/components/token_table.vue';
import ActivityEvents from '~/clusters/agents/components/activity_events_list.vue';
+import IntegrationStatus from '~/clusters/agents/components/integration_status.vue';
import getAgentQuery from '~/clusters/agents/graphql/queries/get_cluster_agent.query.graphql';
import { useFakeDate } from 'helpers/fake_date';
import createMockApollo from 'helpers/mock_apollo_helper';
@@ -76,6 +77,7 @@ describe('ClusterAgentShow', () => {
const findTokenCount = () => wrapper.findByTestId('cluster-agent-token-count').text();
const findEESecurityTabSlot = () => wrapper.findByTestId('ee-security-tab');
const findActivity = () => wrapper.findComponent(ActivityEvents);
+ const findIntegrationStatus = () => wrapper.findComponent(IntegrationStatus);
afterEach(() => {
wrapper.destroy();
@@ -107,6 +109,10 @@ describe('ClusterAgentShow', () => {
expect(findCreatedText()).toMatchInterpolatedText('Created by user-1 2 days ago');
});
+ it('displays agent integration status section', () => {
+ expect(findIntegrationStatus().exists()).toBe(true);
+ });
+
it('displays token count', () => {
expect(findTokenCount()).toMatchInterpolatedText(
`${ClusterAgentShow.i18n.tokens} ${defaultClusterAgent.tokens.count}`,
diff --git a/spec/frontend/clusters_list/components/agent_table_spec.js b/spec/frontend/clusters_list/components/agent_table_spec.js
index b78f0a3686c..9cbb83eedd2 100644
--- a/spec/frontend/clusters_list/components/agent_table_spec.js
+++ b/spec/frontend/clusters_list/components/agent_table_spec.js
@@ -36,7 +36,7 @@ describe('AgentTable', () => {
const findAgentLink = (at) => wrapper.findAllByTestId('cluster-agent-name-link').at(at);
const findStatusText = (at) => wrapper.findAllByTestId('cluster-agent-connection-status').at(at);
- const findStatusIcon = (at) => findStatusText(at).find(GlIcon);
+ const findStatusIcon = (at) => findStatusText(at).findComponent(GlIcon);
const findLastContactText = (at) => wrapper.findAllByTestId('cluster-agent-last-contact').at(at);
const findVersionText = (at) => wrapper.findAllByTestId('cluster-agent-version').at(at);
const findConfiguration = (at) =>
@@ -113,7 +113,7 @@ describe('AgentTable', () => {
texts,
lineNumber,
}) => {
- const findIcon = () => findVersionText(lineNumber).find(GlIcon);
+ const findIcon = () => findVersionText(lineNumber).findComponent(GlIcon);
const findPopover = () => wrapper.findByTestId(`popover-${agent}`);
const versionWarning = versionMismatch || versionOutdated;
@@ -151,7 +151,7 @@ describe('AgentTable', () => {
`(
'displays config file path as "$agentPath" at line $lineNumber',
({ agentConfig, link, lineNumber }) => {
- const findLink = findConfiguration(lineNumber).find(GlLink);
+ const findLink = findConfiguration(lineNumber).findComponent(GlLink);
expect(findLink.attributes('href')).toBe(link);
expect(findConfiguration(lineNumber).text()).toBe(agentConfig);
diff --git a/spec/frontend/clusters_list/components/agents_spec.js b/spec/frontend/clusters_list/components/agents_spec.js
index 92cfff7d490..bff1e573dbd 100644
--- a/spec/frontend/clusters_list/components/agents_spec.js
+++ b/spec/frontend/clusters_list/components/agents_spec.js
@@ -334,7 +334,7 @@ describe('Agents', () => {
});
it('displays a loading icon', () => {
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
});
});
diff --git a/spec/frontend/clusters_list/components/ancestor_notice_spec.js b/spec/frontend/clusters_list/components/ancestor_notice_spec.js
index a9f11e6fdf8..758f6586e1a 100644
--- a/spec/frontend/clusters_list/components/ancestor_notice_spec.js
+++ b/spec/frontend/clusters_list/components/ancestor_notice_spec.js
@@ -46,7 +46,7 @@ describe('ClustersAncestorNotice', () => {
});
it('displays link', () => {
- expect(wrapper.find(GlLink).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLink).exists()).toBe(true);
});
});
});
diff --git a/spec/frontend/clusters_list/components/clusters_main_view_spec.js b/spec/frontend/clusters_list/components/clusters_main_view_spec.js
index 218463b9adf..6f23ed47d2a 100644
--- a/spec/frontend/clusters_list/components/clusters_main_view_spec.js
+++ b/spec/frontend/clusters_list/components/clusters_main_view_spec.js
@@ -142,7 +142,7 @@ describe('ClustersMainViewComponent', () => {
createWrapper({ certificateBasedClustersEnabled: false });
});
- it('it displays only the Agent tab', () => {
+ it('displays only the Agent tab', () => {
expect(findAllTabs()).toHaveLength(1);
const agentTab = findGlTabAtIndex(0);
diff --git a/spec/frontend/clusters_list/components/clusters_spec.js b/spec/frontend/clusters_list/components/clusters_spec.js
index 5c7635c1617..a3f42c1f161 100644
--- a/spec/frontend/clusters_list/components/clusters_spec.js
+++ b/spec/frontend/clusters_list/components/clusters_spec.js
@@ -142,7 +142,7 @@ describe('Clusters', () => {
({ lineNumber, result }) => {
const statuses = findStatuses();
const status = statuses.at(lineNumber);
- expect(status.find(GlLoadingIcon).exists()).toBe(result);
+ expect(status.findComponent(GlLoadingIcon).exists()).toBe(result);
},
);
});
diff --git a/spec/frontend/clusters_list/components/install_agent_modal_spec.js b/spec/frontend/clusters_list/components/install_agent_modal_spec.js
index 964dd005a27..10264d6a011 100644
--- a/spec/frontend/clusters_list/components/install_agent_modal_spec.js
+++ b/spec/frontend/clusters_list/components/install_agent_modal_spec.js
@@ -65,7 +65,7 @@ describe('InstallAgentModal', () => {
const findAgentInstructions = () => findModal().findComponent(AgentToken);
const findButtonByVariant = (variant) =>
findModal()
- .findAll(GlButton)
+ .findAllComponents(GlButton)
.wrappers.find((button) => button.props('variant') === variant);
const findActionButton = () => findButtonByVariant('confirm');
const findCancelButton = () => findButtonByVariant('default');
diff --git a/spec/frontend/clusters_list/components/node_error_help_text_spec.js b/spec/frontend/clusters_list/components/node_error_help_text_spec.js
index 8187ab75c58..3211ba44eff 100644
--- a/spec/frontend/clusters_list/components/node_error_help_text_spec.js
+++ b/spec/frontend/clusters_list/components/node_error_help_text_spec.js
@@ -11,7 +11,7 @@ describe('NodeErrorHelpText', () => {
await nextTick();
};
- const findPopover = () => wrapper.find(GlPopover);
+ const findPopover = () => wrapper.findComponent(GlPopover);
afterEach(() => {
wrapper.destroy();
diff --git a/spec/frontend/code_navigation/components/app_spec.js b/spec/frontend/code_navigation/components/app_spec.js
index b85047dc816..b9be262efd0 100644
--- a/spec/frontend/code_navigation/components/app_spec.js
+++ b/spec/frontend/code_navigation/components/app_spec.js
@@ -63,7 +63,7 @@ describe('Code navigation app component', () => {
it('hides popover when no definition set', () => {
factory();
- expect(wrapper.find(Popover).exists()).toBe(false);
+ expect(wrapper.findComponent(Popover).exists()).toBe(false);
});
it('renders popover when definition set', () => {
@@ -73,7 +73,7 @@ describe('Code navigation app component', () => {
currentBlobPath: 'index.js',
});
- expect(wrapper.find(Popover).exists()).toBe(true);
+ expect(wrapper.findComponent(Popover).exists()).toBe(true);
});
it('calls showDefinition when clicking blob viewer', () => {
diff --git a/spec/frontend/code_navigation/components/popover_spec.js b/spec/frontend/code_navigation/components/popover_spec.js
index c038c04a0f8..874263e046a 100644
--- a/spec/frontend/code_navigation/components/popover_spec.js
+++ b/spec/frontend/code_navigation/components/popover_spec.js
@@ -115,8 +115,8 @@ describe('Code navigation popover component', () => {
definitionPathPrefix: DEFINITION_PATH_PREFIX,
});
- expect(wrapper.find({ ref: 'code-output' }).exists()).toBe(true);
- expect(wrapper.find({ ref: 'doc-output' }).exists()).toBe(false);
+ expect(wrapper.findComponent({ ref: 'code-output' }).exists()).toBe(true);
+ expect(wrapper.findComponent({ ref: 'doc-output' }).exists()).toBe(false);
});
});
@@ -128,8 +128,8 @@ describe('Code navigation popover component', () => {
definitionPathPrefix: DEFINITION_PATH_PREFIX,
});
- expect(wrapper.find({ ref: 'code-output' }).exists()).toBe(false);
- expect(wrapper.find({ ref: 'doc-output' }).exists()).toBe(true);
+ expect(wrapper.findComponent({ ref: 'code-output' }).exists()).toBe(false);
+ expect(wrapper.findComponent({ ref: 'doc-output' }).exists()).toBe(true);
});
});
});
diff --git a/spec/frontend/code_navigation/utils/index_spec.js b/spec/frontend/code_navigation/utils/index_spec.js
index b8448709f0b..700c912029c 100644
--- a/spec/frontend/code_navigation/utils/index_spec.js
+++ b/spec/frontend/code_navigation/utils/index_spec.js
@@ -17,7 +17,7 @@ describe('getCurrentHoverElement', () => {
value
${'test'}
${undefined}
- `('it returns cached current key', ({ value }) => {
+ `('returns cached current key', ({ value }) => {
if (value) {
cachedData.set('current', value);
}
@@ -52,7 +52,7 @@ describe('addInteractionClass', () => {
${1} | ${0} | ${0}
${1} | ${0} | ${0}
`(
- 'it sets code navigation attributes for line $line and character $char',
+ 'sets code navigation attributes for line $line and character $char',
({ line, char, index }) => {
addInteractionClass({ path: 'index.js', d: { start_line: line, start_char: char } });
diff --git a/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js b/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js
index b1c8ba48475..fddc767953a 100644
--- a/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js
+++ b/spec/frontend/commit/commit_box_pipeline_mini_graph_spec.js
@@ -1,14 +1,24 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
+import { GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import createMockApollo from 'helpers/mock_apollo_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
import CommitBoxPipelineMiniGraph from '~/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue';
+import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
+import { COMMIT_BOX_POLL_INTERVAL } from '~/projects/commit_box/info/constants';
import getLinkedPipelinesQuery from '~/projects/commit_box/info/graphql/queries/get_linked_pipelines.query.graphql';
import getPipelineStagesQuery from '~/projects/commit_box/info/graphql/queries/get_pipeline_stages.query.graphql';
-import { mockPipelineStagesQueryResponse, mockStages } from './mock_data';
+import * as graphQlUtils from '~/pipelines/components/graph/utils';
+import {
+ mockDownstreamQueryResponse,
+ mockPipelineStagesQueryResponse,
+ mockStages,
+ mockUpstreamDownstreamQueryResponse,
+ mockUpstreamQueryResponse,
+} from './mock_data';
jest.mock('~/flash');
@@ -17,61 +27,219 @@ Vue.use(VueApollo);
describe('Commit box pipeline mini graph', () => {
let wrapper;
- const findMiniGraph = () => wrapper.findByTestId('commit-box-mini-graph');
- const findUpstream = () => wrapper.findByTestId('commit-box-mini-graph-upstream');
- const findDownstream = () => wrapper.findByTestId('commit-box-mini-graph-downstream');
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph);
+ const downstreamHandler = jest.fn().mockResolvedValue(mockDownstreamQueryResponse);
+ const failedHandler = jest.fn().mockRejectedValue(new Error('GraphQL error'));
const stagesHandler = jest.fn().mockResolvedValue(mockPipelineStagesQueryResponse);
+ const upstreamDownstreamHandler = jest
+ .fn()
+ .mockResolvedValue(mockUpstreamDownstreamQueryResponse);
+ const upstreamHandler = jest.fn().mockResolvedValue(mockUpstreamQueryResponse);
+ const advanceToNextFetch = () => {
+ jest.advanceTimersByTime(COMMIT_BOX_POLL_INTERVAL);
+ };
- const createComponent = ({ props = {} } = {}) => {
- const handlers = [
- [getLinkedPipelinesQuery, {}],
+ const fullPath = 'gitlab-org/gitlab';
+ const iid = '315';
+ const createMockApolloProvider = (handler = downstreamHandler) => {
+ const requestHandlers = [
+ [getLinkedPipelinesQuery, handler],
[getPipelineStagesQuery, stagesHandler],
];
+ return createMockApollo(requestHandlers);
+ };
+
+ const createComponent = (handler) => {
wrapper = extendedWrapper(
shallowMount(CommitBoxPipelineMiniGraph, {
propsData: {
stages: mockStages,
- ...props,
},
- apolloProvider: createMockApollo(handlers),
+ provide: {
+ fullPath,
+ iid,
+ dataMethod: 'graphql',
+ graphqlResourceEtag: '/api/graphql:pipelines/id/320',
+ },
+ apolloProvider: createMockApolloProvider(handler),
}),
);
-
- return waitForPromises();
};
afterEach(() => {
wrapper.destroy();
});
- describe('linked pipelines', () => {
+ describe('loading state', () => {
+ it('should display loading state when loading', () => {
+ createComponent();
+
+ expect(findLoadingIcon().exists()).toBe(true);
+ expect(findPipelineMiniGraph().exists()).toBe(false);
+ });
+ });
+
+ describe('loaded state', () => {
beforeEach(async () => {
await createComponent();
});
- it('should display the mini pipeine graph', () => {
- expect(findMiniGraph().exists()).toBe(true);
+ it('should not display loading state after the query is resolved', async () => {
+ expect(findLoadingIcon().exists()).toBe(false);
+ expect(findPipelineMiniGraph().exists()).toBe(true);
});
- it('should not display linked pipelines', () => {
- expect(findUpstream().exists()).toBe(false);
- expect(findDownstream().exists()).toBe(false);
+ it('should display the pipeline mini graph', () => {
+ expect(findPipelineMiniGraph().exists()).toBe(true);
});
});
- describe('when data is mismatched', () => {
- beforeEach(async () => {
- await createComponent({ props: { stages: [] } });
+ describe('load upstream/downstream', () => {
+ const samplePipeline = {
+ __typename: expect.any(String),
+ id: expect.any(String),
+ path: expect.any(String),
+ project: expect.any(Object),
+ detailedStatus: expect.any(Object),
+ };
+
+ it('formatted stages should be passed to the pipeline mini graph', async () => {
+ const stage = mockStages[0];
+ const expectedStages = [
+ {
+ name: stage.name,
+ status: {
+ __typename: 'DetailedStatus',
+ id: stage.status.id,
+ icon: stage.status.icon,
+ group: stage.status.group,
+ },
+ dropdown_path: stage.dropdown_path,
+ title: stage.title,
+ },
+ ];
+
+ createComponent();
+
+ await waitForPromises();
+
+ expect(findPipelineMiniGraph().props('stages')).toEqual(expectedStages);
+ });
+
+ it('should render a downstream pipeline only', async () => {
+ createComponent(downstreamHandler);
+
+ await waitForPromises();
+
+ const downstreamPipelines = findPipelineMiniGraph().props('downstreamPipelines');
+ const upstreamPipeline = findPipelineMiniGraph().props('upstreamPipeline');
+
+ expect(downstreamPipelines).toEqual(expect.any(Array));
+ expect(upstreamPipeline).toEqual(null);
+ });
+
+ it('should pass the pipeline path prop for the counter badge', async () => {
+ createComponent(downstreamHandler);
+
+ await waitForPromises();
+
+ const expectedPath = mockDownstreamQueryResponse.data.project.pipeline.path;
+ const pipelinePath = findPipelineMiniGraph().props('pipelinePath');
+
+ expect(pipelinePath).toBe(expectedPath);
+ });
+
+ it('should render an upstream pipeline only', async () => {
+ createComponent(upstreamHandler);
+
+ await waitForPromises();
+
+ const downstreamPipelines = findPipelineMiniGraph().props('downstreamPipelines');
+ const upstreamPipeline = findPipelineMiniGraph().props('upstreamPipeline');
+
+ expect(upstreamPipeline).toEqual(samplePipeline);
+ expect(downstreamPipelines).toHaveLength(0);
});
- it('calls create flash with expected arguments', () => {
+ it('should render downstream and upstream pipelines', async () => {
+ createComponent(upstreamDownstreamHandler);
+
+ await waitForPromises();
+
+ const downstreamPipelines = findPipelineMiniGraph().props('downstreamPipelines');
+ const upstreamPipeline = findPipelineMiniGraph().props('upstreamPipeline');
+
+ expect(upstreamPipeline).toEqual(samplePipeline);
+ expect(downstreamPipelines).toEqual(expect.arrayContaining([samplePipeline]));
+ });
+ });
+
+ describe('error state', () => {
+ it('createFlash should show if there is an error fetching the data', async () => {
+ createComponent({ handler: failedHandler });
+
+ await waitForPromises();
+
expect(createFlash).toHaveBeenCalledWith({
- message: 'There was a problem handling the pipeline data.',
- captureError: true,
- error: new Error('Rest stages and graphQl stages must be the same length'),
+ message: 'There was a problem fetching linked pipelines.',
});
});
});
+
+ describe('polling', () => {
+ it('polling interval is set for linked pipelines', () => {
+ createComponent();
+
+ const expectedInterval = wrapper.vm.$apollo.queries.pipeline.options.pollInterval;
+
+ expect(expectedInterval).toBe(COMMIT_BOX_POLL_INTERVAL);
+ });
+
+ it('polling interval is set for pipeline stages', () => {
+ createComponent();
+
+ const expectedInterval = wrapper.vm.$apollo.queries.pipelineStages.options.pollInterval;
+
+ expect(expectedInterval).toBe(COMMIT_BOX_POLL_INTERVAL);
+ });
+
+ it('polls for stages and linked pipelines', async () => {
+ createComponent();
+
+ await waitForPromises();
+
+ expect(stagesHandler).toHaveBeenCalledTimes(1);
+ expect(downstreamHandler).toHaveBeenCalledTimes(1);
+
+ advanceToNextFetch();
+ await waitForPromises();
+
+ expect(stagesHandler).toHaveBeenCalledTimes(2);
+ expect(downstreamHandler).toHaveBeenCalledTimes(2);
+
+ advanceToNextFetch();
+ await waitForPromises();
+
+ expect(stagesHandler).toHaveBeenCalledTimes(3);
+ expect(downstreamHandler).toHaveBeenCalledTimes(3);
+ });
+
+ it('toggles query polling with visibility check', async () => {
+ jest.spyOn(graphQlUtils, 'toggleQueryPollingByVisibility');
+
+ createComponent();
+
+ await waitForPromises();
+
+ expect(graphQlUtils.toggleQueryPollingByVisibility).toHaveBeenCalledWith(
+ wrapper.vm.$apollo.queries.pipelineStages,
+ );
+ expect(graphQlUtils.toggleQueryPollingByVisibility).toHaveBeenCalledWith(
+ wrapper.vm.$apollo.queries.pipeline,
+ );
+ });
+ });
});
diff --git a/spec/frontend/commit/commit_pipeline_status_component_spec.js b/spec/frontend/commit/commit_pipeline_status_component_spec.js
index 43db6db00c1..73720c1cc88 100644
--- a/spec/frontend/commit/commit_pipeline_status_component_spec.js
+++ b/spec/frontend/commit/commit_pipeline_status_component_spec.js
@@ -37,9 +37,9 @@ describe('Commit pipeline status component', () => {
});
};
- const findLoader = () => wrapper.find(GlLoadingIcon);
+ const findLoader = () => wrapper.findComponent(GlLoadingIcon);
const findLink = () => wrapper.find('a');
- const findCiIcon = () => findLink().find(CiIcon);
+ const findCiIcon = () => findLink().findComponent(CiIcon);
afterEach(() => {
wrapper.destroy();
diff --git a/spec/frontend/commit/mock_data.js b/spec/frontend/commit/mock_data.js
index 8db162c07c2..aef137e6fa5 100644
--- a/spec/frontend/commit/mock_data.js
+++ b/spec/frontend/commit/mock_data.js
@@ -3,116 +3,21 @@ export const mockStages = [
name: 'build',
title: 'build: passed',
status: {
+ __typename: 'DetailedStatus',
+ id: 'success-409-409',
icon: 'status_success',
text: 'passed',
label: 'passed',
group: 'success',
tooltip: 'passed',
has_details: true,
- details_path: '/root/ci-project/-/pipelines/611#build',
+ details_path: '/root/ci-project/-/pipelines/318#build',
illustration: null,
favicon:
'/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
},
- path: '/root/ci-project/-/pipelines/611#build',
- dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=build',
- },
- {
- name: 'test',
- title: 'test: passed',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- tooltip: 'passed',
- has_details: true,
- details_path: '/root/ci-project/-/pipelines/611#test',
- illustration: null,
- favicon:
- '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
- },
- path: '/root/ci-project/-/pipelines/611#test',
- dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=test',
- },
- {
- name: 'test_two',
- title: 'test_two: passed',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- tooltip: 'passed',
- has_details: true,
- details_path: '/root/ci-project/-/pipelines/611#test_two',
- illustration: null,
- favicon:
- '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
- },
- path: '/root/ci-project/-/pipelines/611#test_two',
- dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=test_two',
- },
- {
- name: 'manual',
- title: 'manual: skipped',
- status: {
- icon: 'status_skipped',
- text: 'skipped',
- label: 'skipped',
- group: 'skipped',
- tooltip: 'skipped',
- has_details: true,
- details_path: '/root/ci-project/-/pipelines/611#manual',
- illustration: null,
- favicon:
- '/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png',
- action: {
- icon: 'play',
- title: 'Play all manual',
- path: '/root/ci-project/-/pipelines/611/stages/manual/play_manual',
- method: 'post',
- button_title: 'Play all manual',
- },
- },
- path: '/root/ci-project/-/pipelines/611#manual',
- dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=manual',
- },
- {
- name: 'deploy',
- title: 'deploy: passed',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- tooltip: 'passed',
- has_details: true,
- details_path: '/root/ci-project/-/pipelines/611#deploy',
- illustration: null,
- favicon:
- '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
- },
- path: '/root/ci-project/-/pipelines/611#deploy',
- dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=deploy',
- },
- {
- name: 'qa',
- title: 'qa: passed',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- tooltip: 'passed',
- has_details: true,
- details_path: '/root/ci-project/-/pipelines/611#qa',
- illustration: null,
- favicon:
- '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
- },
- path: '/root/ci-project/-/pipelines/611#qa',
- dropdown_path: '/root/ci-project/-/pipelines/611/stage.json?stage=qa',
+ path: '/root/ci-project/-/pipelines/318#build',
+ dropdown_path: '/root/ci-project/-/pipelines/318/stage.json?stage=build',
},
];
@@ -161,3 +66,109 @@ export const mockPipelineStatusResponse = {
},
},
};
+
+export const mockDownstreamQueryResponse = {
+ data: {
+ project: {
+ id: '1',
+ pipeline: {
+ path: '/root/ci-project/-/pipelines/790',
+ id: 'pipeline-1',
+ downstream: {
+ nodes: [
+ {
+ id: 'gid://gitlab/Ci::Pipeline/612',
+ path: '/root/job-log-sections/-/pipelines/612',
+ project: { id: '1', name: 'job-log-sections', __typename: 'Project' },
+ detailedStatus: {
+ id: 'status-1',
+ group: 'success',
+ icon: 'status_success',
+ label: 'passed',
+ __typename: 'DetailedStatus',
+ },
+ __typename: 'Pipeline',
+ },
+ ],
+ __typename: 'PipelineConnection',
+ },
+ upstream: null,
+ },
+ __typename: 'Project',
+ },
+ },
+};
+
+export const mockUpstreamDownstreamQueryResponse = {
+ data: {
+ project: {
+ id: '1',
+ pipeline: {
+ id: 'pipeline-1',
+ path: '/root/ci-project/-/pipelines/790',
+ downstream: {
+ nodes: [
+ {
+ id: 'gid://gitlab/Ci::Pipeline/612',
+ path: '/root/job-log-sections/-/pipelines/612',
+ project: { id: '1', name: 'job-log-sections', __typename: 'Project' },
+ detailedStatus: {
+ id: 'status-1',
+ group: 'success',
+ icon: 'status_success',
+ label: 'passed',
+ __typename: 'DetailedStatus',
+ },
+ __typename: 'Pipeline',
+ },
+ ],
+ __typename: 'PipelineConnection',
+ },
+ upstream: {
+ id: 'gid://gitlab/Ci::Pipeline/610',
+ path: '/root/trigger-downstream/-/pipelines/610',
+ project: { id: '1', name: 'trigger-downstream', __typename: 'Project' },
+ detailedStatus: {
+ id: 'status-1',
+ group: 'success',
+ icon: 'status_success',
+ label: 'passed',
+ __typename: 'DetailedStatus',
+ },
+ __typename: 'Pipeline',
+ },
+ },
+ __typename: 'Project',
+ },
+ },
+};
+
+export const mockUpstreamQueryResponse = {
+ data: {
+ project: {
+ id: '1',
+ pipeline: {
+ id: 'pipeline-1',
+ path: '/root/ci-project/-/pipelines/790',
+ downstream: {
+ nodes: [],
+ __typename: 'PipelineConnection',
+ },
+ upstream: {
+ id: 'gid://gitlab/Ci::Pipeline/610',
+ path: '/root/trigger-downstream/-/pipelines/610',
+ project: { id: '1', name: 'trigger-downstream', __typename: 'Project' },
+ detailedStatus: {
+ id: 'status-1',
+ group: 'success',
+ icon: 'status_success',
+ label: 'passed',
+ __typename: 'DetailedStatus',
+ },
+ __typename: 'Pipeline',
+ },
+ },
+ __typename: 'Project',
+ },
+ },
+};
diff --git a/spec/frontend/commit/pipelines/pipelines_table_spec.js b/spec/frontend/commit/pipelines/pipelines_table_spec.js
index 71ee12cf02d..d89a238105b 100644
--- a/spec/frontend/commit/pipelines/pipelines_table_spec.js
+++ b/spec/frontend/commit/pipelines/pipelines_table_spec.js
@@ -302,6 +302,33 @@ describe('Pipelines table in Commits and Merge requests', () => {
expect(findModal()).not.toBeNull();
});
});
+
+ describe('when no pipelines were created on a forked merge request', () => {
+ beforeEach(async () => {
+ mock.onGet('endpoint.json').reply(200, []);
+
+ createComponent({
+ projectId: '5',
+ mergeRequestId: 3,
+ canCreatePipelineInTargetProject: true,
+ sourceProjectFullPath: 'test/parent-project',
+ targetProjectFullPath: 'test/fork-project',
+ });
+
+ jest.spyOn(findModal().vm, 'show').mockReturnValue();
+
+ await waitForPromises();
+ });
+
+ it('should show security modal from empty state run pipeline button', () => {
+ expect(findEmptyState().exists()).toBe(true);
+ expect(findModal().exists()).toBe(true);
+
+ findRunPipelineBtn().trigger('click');
+
+ expect(findModal().vm.show).toHaveBeenCalled();
+ });
+ });
});
describe('unsuccessfull request', () => {
diff --git a/spec/frontend/confidential_merge_request/components/dropdown_spec.js b/spec/frontend/confidential_merge_request/components/dropdown_spec.js
index 14a0b98a0d5..770f2636648 100644
--- a/spec/frontend/confidential_merge_request/components/dropdown_spec.js
+++ b/spec/frontend/confidential_merge_request/components/dropdown_spec.js
@@ -30,18 +30,18 @@ describe('Confidential merge request project dropdown component', () => {
},
]);
- expect(vm.findAll(GlDropdownItem).length).toBe(2);
+ expect(vm.findAllComponents(GlDropdownItem).length).toBe(2);
});
it('shows lock icon', () => {
factory();
- expect(vm.find(GlDropdown).props('icon')).toBe('lock');
+ expect(vm.findComponent(GlDropdown).props('icon')).toBe('lock');
});
it('has dropdown text', () => {
factory();
- expect(vm.find(GlDropdown).props('text')).toBe('Select private project');
+ expect(vm.findComponent(GlDropdown).props('text')).toBe('Select private project');
});
});
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 b54f7cf17c8..6ad8a9de8d3 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
@@ -1,49 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`content_editor/components/toolbar_link_button renders dropdown component 1`] = `
-"<div class=\\"dropdown b-dropdown gl-new-dropdown btn-group\\" aria-label=\\"Insert link\\" title=\\"Insert link\\">
- <!----><button aria-haspopup=\\"true\\" aria-expanded=\\"false\\" type=\\"button\\" class=\\"btn dropdown-toggle btn-default btn-sm gl-button gl-dropdown-toggle btn-default-tertiary dropdown-icon-only\\">
- <!----> <svg data-testid=\\"link-icon\\" role=\\"img\\" aria-hidden=\\"true\\" class=\\"dropdown-icon gl-icon s16\\">
- <use href=\\"#link\\"></use>
- </svg> <span class=\\"gl-new-dropdown-button-text\\"></span> <svg data-testid=\\"chevron-down-icon\\" role=\\"img\\" aria-hidden=\\"true\\" class=\\"gl-button-icon dropdown-chevron gl-icon s16\\">
- <use href=\\"#chevron-down\\"></use>
- </svg></button>
- <ul role=\\"menu\\" tabindex=\\"-1\\" class=\\"dropdown-menu\\">
- <div class=\\"gl-new-dropdown-inner\\">
+"<div title=\\"Insert link\\" lazy=\\"\\">
+ <li role=\\"presentation\\" class=\\"gl-px-3!\\">
+ <form tabindex=\\"-1\\" class=\\"b-dropdown-form gl-p-0\\">
+ <div role=\\"group\\" class=\\"input-group\\" placeholder=\\"Link URL\\">
+ <!---->
+ <!----> <input type=\\"text\\" placeholder=\\"Link URL\\" class=\\"form-control gl-form-input\\">
+ <div class=\\"input-group-append\\"><button type=\\"button\\" class=\\"btn btn-confirm btn-md gl-button\\">
+ <!---->
+ <!----> <span class=\\"gl-button-text\\">Apply</span></button></div>
+ <!---->
+ </div>
+ </form>
+ </li>
+ <li role=\\"presentation\\" class=\\"gl-new-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\\">
+ <!---->
<!---->
<!---->
- <div class=\\"gl-new-dropdown-contents\\">
+ <div class=\\"gl-new-dropdown-item-text-wrapper\\">
+ <p class=\\"gl-new-dropdown-item-text-primary\\">
+ Upload file
+ </p>
<!---->
- <li role=\\"presentation\\" class=\\"gl-px-3!\\">
- <form tabindex=\\"-1\\" class=\\"b-dropdown-form gl-p-0\\">
- <div role=\\"group\\" class=\\"input-group\\" placeholder=\\"Link URL\\">
- <!---->
- <!----> <input type=\\"text\\" placeholder=\\"Link URL\\" class=\\"form-control gl-form-input\\">
- <div class=\\"input-group-append\\"><button type=\\"button\\" class=\\"btn btn-confirm btn-md gl-button\\">
- <!---->
- <!----> <span class=\\"gl-button-text\\">Apply</span></button></div>
- <!---->
- </div>
- </form>
- </li>
- <li role=\\"presentation\\" class=\\"gl-new-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\\">
- <!---->
- <!---->
- <!---->
- <div class=\\"gl-new-dropdown-item-text-wrapper\\">
- <p class=\\"gl-new-dropdown-item-text-primary\\">
- Upload file
- </p>
- <!---->
- </div>
- <!---->
- </button></li> <input type=\\"file\\" name=\\"content_editor_attachment\\" class=\\"gl-display-none\\">
</div>
<!---->
- </div>
- </ul>
+ </button></li>
</div>"
`;
diff --git a/spec/frontend/content_editor/components/bubble_menus/bubble_menu_spec.js b/spec/frontend/content_editor/components/bubble_menus/bubble_menu_spec.js
new file mode 100644
index 00000000000..0700cf5d529
--- /dev/null
+++ b/spec/frontend/content_editor/components/bubble_menus/bubble_menu_spec.js
@@ -0,0 +1,126 @@
+import { BubbleMenuPlugin } from '@tiptap/extension-bubble-menu';
+import { nextTick } from 'vue';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import BubbleMenu from '~/content_editor/components/bubble_menus/bubble_menu.vue';
+import { createTestEditor } from '../../test_utils';
+
+jest.mock('@tiptap/extension-bubble-menu');
+
+describe('content_editor/components/bubble_menus/bubble_menu', () => {
+ let wrapper;
+ let tiptapEditor;
+ const pluginKey = 'key';
+ const shouldShow = jest.fn();
+ const tippyOptions = { placement: 'bottom' };
+ const pluginInitializationResult = {};
+
+ const buildEditor = () => {
+ tiptapEditor = createTestEditor();
+ };
+
+ const createWrapper = (propsData = {}) => {
+ wrapper = shallowMountExtended(BubbleMenu, {
+ provide: {
+ tiptapEditor,
+ },
+ propsData: {
+ pluginKey,
+ shouldShow,
+ tippyOptions,
+ ...propsData,
+ },
+ slots: {
+ default: '<div>menu content</div>',
+ },
+ });
+ };
+
+ const setupMocks = () => {
+ BubbleMenuPlugin.mockReturnValueOnce(pluginInitializationResult);
+ jest.spyOn(tiptapEditor, 'registerPlugin').mockImplementationOnce(() => true);
+ };
+
+ const invokeTippyEvent = (eventName, eventArgs) => {
+ const pluginConfig = BubbleMenuPlugin.mock.calls[0][0];
+
+ pluginConfig.tippyOptions[eventName](eventArgs);
+ };
+
+ beforeEach(() => {
+ buildEditor();
+ setupMocks();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('initializes BubbleMenuPlugin', async () => {
+ createWrapper({});
+
+ await nextTick();
+
+ expect(BubbleMenuPlugin).toHaveBeenCalledWith({
+ pluginKey,
+ editor: tiptapEditor,
+ shouldShow,
+ element: wrapper.vm.$el,
+ tippyOptions: expect.objectContaining({
+ onHidden: expect.any(Function),
+ onShow: expect.any(Function),
+ ...tippyOptions,
+ }),
+ });
+
+ expect(tiptapEditor.registerPlugin).toHaveBeenCalledWith(pluginInitializationResult);
+ });
+
+ it('does not render default slot by default', async () => {
+ createWrapper({});
+
+ await nextTick();
+
+ expect(wrapper.text()).not.toContain('menu content');
+ });
+
+ describe('when onShow event handler is invoked', () => {
+ const onShowArgs = {};
+
+ beforeEach(async () => {
+ createWrapper({});
+
+ await nextTick();
+
+ invokeTippyEvent('onShow', onShowArgs);
+ });
+
+ it('displays the menu content', () => {
+ expect(wrapper.text()).toContain('menu content');
+ });
+
+ it('emits show event', () => {
+ expect(wrapper.emitted('show')).toEqual([[onShowArgs]]);
+ });
+ });
+
+ describe('when onHidden event handler is invoked', () => {
+ const onHiddenArgs = {};
+
+ beforeEach(async () => {
+ createWrapper({});
+
+ await nextTick();
+
+ invokeTippyEvent('onShow', onHiddenArgs);
+ invokeTippyEvent('onHidden', onHiddenArgs);
+ });
+
+ it('displays the menu content', () => {
+ expect(wrapper.text()).not.toContain('menu content');
+ });
+
+ it('emits show event', () => {
+ expect(wrapper.emitted('hidden')).toEqual([[onHiddenArgs]]);
+ });
+ });
+});
diff --git a/spec/frontend/content_editor/components/bubble_menus/code_block_bubble_menu_spec.js b/spec/frontend/content_editor/components/bubble_menus/code_block_bubble_menu_spec.js
new file mode 100644
index 00000000000..378b11f4ae9
--- /dev/null
+++ b/spec/frontend/content_editor/components/bubble_menus/code_block_bubble_menu_spec.js
@@ -0,0 +1,296 @@
+import {
+ GlDropdown,
+ GlDropdownForm,
+ GlDropdownItem,
+ GlSearchBoxByType,
+ GlFormInput,
+} from '@gitlab/ui';
+import { nextTick } from 'vue';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import { stubComponent } from 'helpers/stub_component';
+import CodeBlockBubbleMenu from '~/content_editor/components/bubble_menus/code_block_bubble_menu.vue';
+import eventHubFactory from '~/helpers/event_hub_factory';
+import BubbleMenu from '~/content_editor/components/bubble_menus/bubble_menu.vue';
+import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight';
+import Diagram from '~/content_editor/extensions/diagram';
+import codeBlockLanguageLoader from '~/content_editor/services/code_block_language_loader';
+import { createTestEditor, emitEditorEvent } from '../../test_utils';
+
+const createFakeEvent = () => ({ preventDefault: jest.fn(), stopPropagation: jest.fn() });
+
+describe('content_editor/components/bubble_menus/code_block_bubble_menu', () => {
+ let wrapper;
+ let tiptapEditor;
+ let contentEditor;
+ let bubbleMenu;
+ let eventHub;
+
+ const buildEditor = () => {
+ tiptapEditor = createTestEditor({ extensions: [CodeBlockHighlight, Diagram] });
+ contentEditor = { renderDiagram: jest.fn() };
+ eventHub = eventHubFactory();
+ };
+
+ const buildWrapper = () => {
+ wrapper = mountExtended(CodeBlockBubbleMenu, {
+ provide: {
+ tiptapEditor,
+ contentEditor,
+ eventHub,
+ },
+ stubs: {
+ GlDropdownItem: stubComponent(GlDropdownItem),
+ BubbleMenu: stubComponent(BubbleMenu),
+ },
+ });
+ };
+
+ const preTag = ({ language, content = 'test' } = {}) => {
+ const languageAttr = language ? ` lang="${language}"` : '';
+
+ return `<pre class="js-syntax-highlight"${languageAttr}>${content}</pre>`;
+ };
+
+ const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
+ const findDropdownItemsData = () =>
+ findDropdownItems().wrappers.map((x) => ({
+ text: x.text(),
+ visible: x.isVisible(),
+ checked: x.props('isChecked'),
+ }));
+
+ beforeEach(async () => {
+ buildEditor();
+ buildWrapper();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders bubble menu component', async () => {
+ tiptapEditor.commands.insertContent(preTag());
+ bubbleMenu = wrapper.findComponent(BubbleMenu);
+
+ await emitEditorEvent({ event: 'transaction', tiptapEditor });
+
+ expect(bubbleMenu.classes()).toEqual(['gl-shadow', 'gl-rounded-base', 'gl-bg-white']);
+ });
+
+ it('selects plaintext language by default', async () => {
+ tiptapEditor.commands.insertContent(preTag());
+ bubbleMenu = wrapper.findComponent(BubbleMenu);
+
+ await emitEditorEvent({ event: 'transaction', tiptapEditor });
+
+ expect(wrapper.findComponent(GlDropdown).props('text')).toBe('Plain text');
+ });
+
+ it('selects appropriate language based on the code block', async () => {
+ tiptapEditor.commands.insertContent(preTag({ language: 'javascript' }));
+ bubbleMenu = wrapper.findComponent(BubbleMenu);
+
+ await emitEditorEvent({ event: 'transaction', tiptapEditor });
+
+ expect(wrapper.findComponent(GlDropdown).props('text')).toBe('Javascript');
+ });
+
+ it('selects diagram sytnax for mermaid', async () => {
+ tiptapEditor.commands.insertContent(preTag({ language: 'mermaid' }));
+ bubbleMenu = wrapper.findComponent(BubbleMenu);
+
+ await emitEditorEvent({ event: 'transaction', tiptapEditor });
+
+ expect(wrapper.findComponent(GlDropdown).props('text')).toBe('Diagram (mermaid)');
+ });
+
+ it("selects Custom (syntax) if the language doesn't exist in the list", async () => {
+ tiptapEditor.commands.insertContent(preTag({ language: 'nomnoml' }));
+ bubbleMenu = wrapper.findComponent(BubbleMenu);
+
+ await emitEditorEvent({ event: 'transaction', tiptapEditor });
+
+ expect(wrapper.findComponent(GlDropdown).props('text')).toBe('Custom (nomnoml)');
+ });
+
+ describe('copy button', () => {
+ it('copies the text of the code block', async () => {
+ const content = 'var a = Math.PI / 2;';
+ jest.spyOn(navigator.clipboard, 'writeText');
+
+ tiptapEditor.commands.insertContent(preTag({ language: 'javascript', content }));
+
+ await wrapper.findByTestId('copy-code-block').vm.$emit('click');
+
+ expect(navigator.clipboard.writeText).toHaveBeenCalledWith(content);
+ });
+ });
+
+ describe('delete button', () => {
+ it('deletes the code block', async () => {
+ tiptapEditor.commands.insertContent(preTag({ language: 'javascript' }));
+
+ await wrapper.findByTestId('delete-code-block').vm.$emit('click');
+
+ expect(tiptapEditor.getText()).toBe('');
+ });
+ });
+
+ describe('preview button', () => {
+ it('does not appear for a regular code block', async () => {
+ tiptapEditor.commands.insertContent('<pre lang="javascript">var a = 2;</pre>');
+
+ expect(wrapper.findByTestId('preview-diagram').exists()).toBe(false);
+ });
+
+ it.each`
+ diagramType | diagramCode
+ ${'mermaid'} | ${'<pre lang="mermaid">graph TD;\n A-->B;</pre>'}
+ ${'nomnoml'} | ${'<img data-diagram="nomnoml" data-diagram-src="data:text/plain;base64,WzxmcmFtZT5EZWNvcmF0b3IgcGF0dGVybl0=">'}
+ `('toggles preview for a $diagramType diagram', async ({ diagramType, diagramCode }) => {
+ tiptapEditor.commands.insertContent(diagramCode);
+
+ await nextTick();
+ await wrapper.findByTestId('preview-diagram').vm.$emit('click');
+
+ expect(tiptapEditor.getAttributes(Diagram.name)).toEqual({
+ isDiagram: true,
+ language: diagramType,
+ showPreview: false,
+ });
+
+ await wrapper.findByTestId('preview-diagram').vm.$emit('click');
+
+ expect(tiptapEditor.getAttributes(Diagram.name)).toEqual({
+ isDiagram: true,
+ language: diagramType,
+ showPreview: true,
+ });
+ });
+ });
+
+ describe('when opened and search is changed', () => {
+ beforeEach(async () => {
+ tiptapEditor.commands.insertContent(preTag({ language: 'javascript' }));
+
+ wrapper.findComponent(GlSearchBoxByType).vm.$emit('input', 'js');
+
+ await nextTick();
+ });
+
+ it('shows dropdown items', () => {
+ expect(findDropdownItemsData()).toEqual(
+ expect.arrayContaining([
+ { text: 'Javascript', visible: true, checked: true },
+ { text: 'Java', visible: true, checked: false },
+ { text: 'Javascript', visible: false, checked: false },
+ { text: 'JSON', visible: true, checked: false },
+ ]),
+ );
+ });
+
+ describe('when dropdown item is clicked', () => {
+ beforeEach(async () => {
+ jest.spyOn(codeBlockLanguageLoader, 'loadLanguage').mockResolvedValue();
+
+ findDropdownItems().at(1).vm.$emit('click');
+
+ await nextTick();
+ });
+
+ it('loads language', () => {
+ expect(codeBlockLanguageLoader.loadLanguage).toHaveBeenCalledWith('java');
+ });
+
+ it('sets code block', () => {
+ expect(tiptapEditor.getJSON()).toMatchObject({
+ content: [
+ {
+ type: 'codeBlock',
+ attrs: {
+ language: 'java',
+ },
+ },
+ ],
+ });
+ });
+
+ it('updates selected dropdown', () => {
+ expect(wrapper.findComponent(GlDropdown).props('text')).toBe('Java');
+ });
+ });
+
+ describe('Create custom type', () => {
+ beforeEach(async () => {
+ tiptapEditor.commands.insertContent('<pre lang="javascript">var a = 2;</pre>');
+
+ await wrapper.findComponent(GlDropdown).vm.show();
+ await wrapper.findByTestId('create-custom-type').trigger('click');
+ });
+
+ it('shows custom language input form and hides dropdown items', () => {
+ expect(wrapper.findComponent(GlDropdownItem).exists()).toBe(false);
+ expect(wrapper.findComponent(GlSearchBoxByType).exists()).toBe(false);
+ expect(wrapper.findComponent(GlDropdownForm).exists()).toBe(true);
+ });
+
+ describe('on clicking back', () => {
+ it('hides the custom language input form and shows dropdown items', async () => {
+ await wrapper.findByRole('button', { name: 'Go back' }).trigger('click');
+
+ expect(wrapper.findComponent(GlDropdownItem).exists()).toBe(true);
+ expect(wrapper.findComponent(GlSearchBoxByType).exists()).toBe(true);
+ expect(wrapper.findComponent(GlDropdownForm).exists()).toBe(false);
+ });
+ });
+
+ describe('on clicking cancel', () => {
+ it('hides the custom language input form and shows dropdown items', async () => {
+ await wrapper.findByRole('button', { name: 'Cancel' }).trigger('click');
+
+ expect(wrapper.findComponent(GlDropdownItem).exists()).toBe(true);
+ expect(wrapper.findComponent(GlSearchBoxByType).exists()).toBe(true);
+ expect(wrapper.findComponent(GlDropdownForm).exists()).toBe(false);
+ });
+ });
+
+ describe('on dropdown hide', () => {
+ it('hides the form', async () => {
+ wrapper.findComponent(GlFormInput).setValue('foobar');
+ await wrapper.findComponent(GlDropdown).vm.$emit('hide');
+
+ expect(wrapper.findComponent(GlDropdownItem).exists()).toBe(true);
+ expect(wrapper.findComponent(GlSearchBoxByType).exists()).toBe(true);
+ expect(wrapper.findComponent(GlDropdownForm).exists()).toBe(false);
+ });
+ });
+
+ describe('on clicking apply', () => {
+ beforeEach(async () => {
+ wrapper.findComponent(GlFormInput).setValue('foobar');
+ await wrapper.findComponent(GlDropdownForm).vm.$emit('submit', createFakeEvent());
+
+ await emitEditorEvent({ event: 'transaction', tiptapEditor });
+ });
+
+ it('hides the custom language input form and shows dropdown items', async () => {
+ expect(wrapper.findComponent(GlDropdownItem).exists()).toBe(true);
+ expect(wrapper.findComponent(GlSearchBoxByType).exists()).toBe(true);
+ expect(wrapper.findComponent(GlDropdownForm).exists()).toBe(false);
+ });
+
+ it('updates dropdown value to the custom language type', () => {
+ expect(wrapper.findComponent(GlDropdown).props('text')).toBe('Custom (foobar)');
+ });
+
+ it('updates tiptap editor to the custom language type', () => {
+ expect(tiptapEditor.getAttributes(CodeBlockHighlight.name)).toEqual(
+ expect.objectContaining({
+ language: 'foobar',
+ }),
+ );
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/content_editor/components/bubble_menus/code_block_spec.js b/spec/frontend/content_editor/components/bubble_menus/code_block_spec.js
deleted file mode 100644
index 154035a46ed..00000000000
--- a/spec/frontend/content_editor/components/bubble_menus/code_block_spec.js
+++ /dev/null
@@ -1,296 +0,0 @@
-import { BubbleMenu } from '@tiptap/vue-2';
-import {
- GlDropdown,
- GlDropdownForm,
- GlDropdownItem,
- GlSearchBoxByType,
- GlFormInput,
-} from '@gitlab/ui';
-import { nextTick } from 'vue';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import { stubComponent } from 'helpers/stub_component';
-import CodeBlockBubbleMenu from '~/content_editor/components/bubble_menus/code_block.vue';
-import eventHubFactory from '~/helpers/event_hub_factory';
-import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight';
-import Diagram from '~/content_editor/extensions/diagram';
-import codeBlockLanguageLoader from '~/content_editor/services/code_block_language_loader';
-import { createTestEditor, emitEditorEvent } from '../../test_utils';
-
-const createFakeEvent = () => ({ preventDefault: jest.fn(), stopPropagation: jest.fn() });
-
-describe('content_editor/components/bubble_menus/code_block', () => {
- let wrapper;
- let tiptapEditor;
- let contentEditor;
- let bubbleMenu;
- let eventHub;
-
- const buildEditor = () => {
- tiptapEditor = createTestEditor({ extensions: [CodeBlockHighlight, Diagram] });
- contentEditor = { renderDiagram: jest.fn() };
- eventHub = eventHubFactory();
- };
-
- const buildWrapper = () => {
- wrapper = mountExtended(CodeBlockBubbleMenu, {
- provide: {
- tiptapEditor,
- contentEditor,
- eventHub,
- },
- stubs: {
- GlDropdownItem: stubComponent(GlDropdownItem),
- },
- });
- };
-
- const preTag = ({ language, content = 'test' } = {}) => {
- const languageAttr = language ? ` lang="${language}"` : '';
-
- return `<pre class="js-syntax-highlight"${languageAttr}>${content}</pre>`;
- };
-
- const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
- const findDropdownItemsData = () =>
- findDropdownItems().wrappers.map((x) => ({
- text: x.text(),
- visible: x.isVisible(),
- checked: x.props('isChecked'),
- }));
-
- beforeEach(async () => {
- buildEditor();
- buildWrapper();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders bubble menu component', async () => {
- tiptapEditor.commands.insertContent(preTag());
- bubbleMenu = wrapper.findComponent(BubbleMenu);
-
- await emitEditorEvent({ event: 'transaction', tiptapEditor });
-
- expect(bubbleMenu.props('editor')).toBe(tiptapEditor);
- expect(bubbleMenu.classes()).toEqual(['gl-shadow', 'gl-rounded-base', 'gl-bg-white']);
- });
-
- it('selects plaintext language by default', async () => {
- tiptapEditor.commands.insertContent(preTag());
- bubbleMenu = wrapper.findComponent(BubbleMenu);
-
- await emitEditorEvent({ event: 'transaction', tiptapEditor });
-
- expect(wrapper.findComponent(GlDropdown).props('text')).toBe('Plain text');
- });
-
- it('selects appropriate language based on the code block', async () => {
- tiptapEditor.commands.insertContent(preTag({ language: 'javascript' }));
- bubbleMenu = wrapper.findComponent(BubbleMenu);
-
- await emitEditorEvent({ event: 'transaction', tiptapEditor });
-
- expect(wrapper.findComponent(GlDropdown).props('text')).toBe('Javascript');
- });
-
- it('selects diagram sytnax for mermaid', async () => {
- tiptapEditor.commands.insertContent(preTag({ language: 'mermaid' }));
- bubbleMenu = wrapper.findComponent(BubbleMenu);
-
- await emitEditorEvent({ event: 'transaction', tiptapEditor });
-
- expect(wrapper.findComponent(GlDropdown).props('text')).toBe('Diagram (mermaid)');
- });
-
- it("selects Custom (syntax) if the language doesn't exist in the list", async () => {
- tiptapEditor.commands.insertContent(preTag({ language: 'nomnoml' }));
- bubbleMenu = wrapper.findComponent(BubbleMenu);
-
- await emitEditorEvent({ event: 'transaction', tiptapEditor });
-
- expect(wrapper.findComponent(GlDropdown).props('text')).toBe('Custom (nomnoml)');
- });
-
- describe('copy button', () => {
- it('copies the text of the code block', async () => {
- const content = 'var a = Math.PI / 2;';
- jest.spyOn(navigator.clipboard, 'writeText');
-
- tiptapEditor.commands.insertContent(preTag({ language: 'javascript', content }));
-
- await wrapper.findByTestId('copy-code-block').vm.$emit('click');
-
- expect(navigator.clipboard.writeText).toHaveBeenCalledWith(content);
- });
- });
-
- describe('delete button', () => {
- it('deletes the code block', async () => {
- tiptapEditor.commands.insertContent(preTag({ language: 'javascript' }));
-
- await wrapper.findByTestId('delete-code-block').vm.$emit('click');
-
- expect(tiptapEditor.getText()).toBe('');
- });
- });
-
- describe('preview button', () => {
- it('does not appear for a regular code block', async () => {
- tiptapEditor.commands.insertContent('<pre lang="javascript">var a = 2;</pre>');
-
- expect(wrapper.findByTestId('preview-diagram').exists()).toBe(false);
- });
-
- it.each`
- diagramType | diagramCode
- ${'mermaid'} | ${'<pre lang="mermaid">graph TD;\n A-->B;</pre>'}
- ${'nomnoml'} | ${'<img data-diagram="nomnoml" data-diagram-src="data:text/plain;base64,WzxmcmFtZT5EZWNvcmF0b3IgcGF0dGVybl0=">'}
- `('toggles preview for a $diagramType diagram', async ({ diagramType, diagramCode }) => {
- tiptapEditor.commands.insertContent(diagramCode);
-
- await nextTick();
- await wrapper.findByTestId('preview-diagram').vm.$emit('click');
-
- expect(tiptapEditor.getAttributes(Diagram.name)).toEqual({
- isDiagram: true,
- language: diagramType,
- showPreview: false,
- });
-
- await wrapper.findByTestId('preview-diagram').vm.$emit('click');
-
- expect(tiptapEditor.getAttributes(Diagram.name)).toEqual({
- isDiagram: true,
- language: diagramType,
- showPreview: true,
- });
- });
- });
-
- describe('when opened and search is changed', () => {
- beforeEach(async () => {
- tiptapEditor.commands.insertContent(preTag({ language: 'javascript' }));
-
- wrapper.findComponent(GlSearchBoxByType).vm.$emit('input', 'js');
-
- await nextTick();
- });
-
- it('shows dropdown items', () => {
- expect(findDropdownItemsData()).toEqual(
- expect.arrayContaining([
- { text: 'Javascript', visible: true, checked: true },
- { text: 'Java', visible: true, checked: false },
- { text: 'Javascript', visible: false, checked: false },
- { text: 'JSON', visible: true, checked: false },
- ]),
- );
- });
-
- describe('when dropdown item is clicked', () => {
- beforeEach(async () => {
- jest.spyOn(codeBlockLanguageLoader, 'loadLanguage').mockResolvedValue();
-
- findDropdownItems().at(1).vm.$emit('click');
-
- await nextTick();
- });
-
- it('loads language', () => {
- expect(codeBlockLanguageLoader.loadLanguage).toHaveBeenCalledWith('java');
- });
-
- it('sets code block', () => {
- expect(tiptapEditor.getJSON()).toMatchObject({
- content: [
- {
- type: 'codeBlock',
- attrs: {
- language: 'java',
- },
- },
- ],
- });
- });
-
- it('updates selected dropdown', () => {
- expect(wrapper.findComponent(GlDropdown).props('text')).toBe('Java');
- });
- });
-
- describe('Create custom type', () => {
- beforeEach(async () => {
- tiptapEditor.commands.insertContent('<pre lang="javascript">var a = 2;</pre>');
-
- await wrapper.findComponent(GlDropdown).vm.show();
- await wrapper.findByTestId('create-custom-type').trigger('click');
- });
-
- it('shows custom language input form and hides dropdown items', () => {
- expect(wrapper.findComponent(GlDropdownItem).exists()).toBe(false);
- expect(wrapper.findComponent(GlSearchBoxByType).exists()).toBe(false);
- expect(wrapper.findComponent(GlDropdownForm).exists()).toBe(true);
- });
-
- describe('on clicking back', () => {
- it('hides the custom language input form and shows dropdown items', async () => {
- await wrapper.findByRole('button', { name: 'Go back' }).trigger('click');
-
- expect(wrapper.findComponent(GlDropdownItem).exists()).toBe(true);
- expect(wrapper.findComponent(GlSearchBoxByType).exists()).toBe(true);
- expect(wrapper.findComponent(GlDropdownForm).exists()).toBe(false);
- });
- });
-
- describe('on clicking cancel', () => {
- it('hides the custom language input form and shows dropdown items', async () => {
- await wrapper.findByRole('button', { name: 'Cancel' }).trigger('click');
-
- expect(wrapper.findComponent(GlDropdownItem).exists()).toBe(true);
- expect(wrapper.findComponent(GlSearchBoxByType).exists()).toBe(true);
- expect(wrapper.findComponent(GlDropdownForm).exists()).toBe(false);
- });
- });
-
- describe('on dropdown hide', () => {
- it('hides the form', async () => {
- wrapper.findComponent(GlFormInput).setValue('foobar');
- await wrapper.findComponent(GlDropdown).vm.$emit('hide');
-
- expect(wrapper.findComponent(GlDropdownItem).exists()).toBe(true);
- expect(wrapper.findComponent(GlSearchBoxByType).exists()).toBe(true);
- expect(wrapper.findComponent(GlDropdownForm).exists()).toBe(false);
- });
- });
-
- describe('on clicking apply', () => {
- beforeEach(async () => {
- wrapper.findComponent(GlFormInput).setValue('foobar');
- await wrapper.findComponent(GlDropdownForm).vm.$emit('submit', createFakeEvent());
-
- await emitEditorEvent({ event: 'transaction', tiptapEditor });
- });
-
- it('hides the custom language input form and shows dropdown items', async () => {
- expect(wrapper.findComponent(GlDropdownItem).exists()).toBe(true);
- expect(wrapper.findComponent(GlSearchBoxByType).exists()).toBe(true);
- expect(wrapper.findComponent(GlDropdownForm).exists()).toBe(false);
- });
-
- it('updates dropdown value to the custom language type', () => {
- expect(wrapper.findComponent(GlDropdown).props('text')).toBe('Custom (foobar)');
- });
-
- it('updates tiptap editor to the custom language type', () => {
- expect(tiptapEditor.getAttributes(CodeBlockHighlight.name)).toEqual(
- expect.objectContaining({
- language: 'foobar',
- }),
- );
- });
- });
- });
- });
-});
diff --git a/spec/frontend/content_editor/components/bubble_menus/formatting_bubble_menu_spec.js b/spec/frontend/content_editor/components/bubble_menus/formatting_bubble_menu_spec.js
new file mode 100644
index 00000000000..cce17176129
--- /dev/null
+++ b/spec/frontend/content_editor/components/bubble_menus/formatting_bubble_menu_spec.js
@@ -0,0 +1,90 @@
+import { mockTracking } from 'helpers/tracking_helper';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import FormattingBubbleMenu from '~/content_editor/components/bubble_menus/formatting_bubble_menu.vue';
+import BubbleMenu from '~/content_editor/components/bubble_menus/bubble_menu.vue';
+import { stubComponent } from 'helpers/stub_component';
+
+import {
+ BUBBLE_MENU_TRACKING_ACTION,
+ CONTENT_EDITOR_TRACKING_LABEL,
+} from '~/content_editor/constants';
+import { createTestEditor } from '../../test_utils';
+
+describe('content_editor/components/bubble_menus/formatting_bubble_menu', () => {
+ let wrapper;
+ let trackingSpy;
+ let tiptapEditor;
+
+ const buildEditor = () => {
+ tiptapEditor = createTestEditor();
+
+ jest.spyOn(tiptapEditor, 'isActive');
+ };
+
+ const buildWrapper = () => {
+ wrapper = shallowMountExtended(FormattingBubbleMenu, {
+ provide: {
+ tiptapEditor,
+ },
+ stubs: {
+ BubbleMenu: stubComponent(BubbleMenu),
+ },
+ });
+ };
+
+ beforeEach(() => {
+ trackingSpy = mockTracking(undefined, null, jest.spyOn);
+ buildEditor();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders bubble menu component', () => {
+ buildWrapper();
+ const bubbleMenu = wrapper.findComponent(BubbleMenu);
+
+ expect(bubbleMenu.classes()).toEqual(['gl-shadow', 'gl-rounded-base', 'gl-bg-white']);
+ });
+
+ describe.each`
+ testId | controlProps
+ ${'bold'} | ${{ contentType: 'bold', iconName: 'bold', label: 'Bold text', editorCommand: 'toggleBold' }}
+ ${'italic'} | ${{ contentType: 'italic', iconName: 'italic', label: 'Italic text', editorCommand: 'toggleItalic' }}
+ ${'strike'} | ${{ contentType: 'strike', iconName: 'strikethrough', label: 'Strikethrough', editorCommand: 'toggleStrike' }}
+ ${'code'} | ${{ contentType: 'code', iconName: 'code', label: 'Code', editorCommand: 'toggleCode' }}
+ ${'superscript'} | ${{ contentType: 'superscript', iconName: 'superscript', label: 'Superscript', editorCommand: 'toggleSuperscript' }}
+ ${'subscript'} | ${{ contentType: 'subscript', iconName: 'subscript', label: 'Subscript', editorCommand: 'toggleSubscript' }}
+ ${'link'} | ${{ contentType: 'link', iconName: 'link', label: 'Insert link', editorCommand: 'toggleLink', editorCommandParams: { href: '' } }}
+ `('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);
+
+ expect(wrapper.findByTestId(testId).props()).toEqual(
+ expect.objectContaining({
+ ...controlProps,
+ size: 'medium',
+ category: 'tertiary',
+ }),
+ );
+ });
+
+ it('tracks the execution of toolbar controls', () => {
+ const eventData = { contentType: 'italic', value: 1 };
+ const { contentType, value } = eventData;
+
+ wrapper.findByTestId(testId).vm.$emit('execute', eventData);
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, BUBBLE_MENU_TRACKING_ACTION, {
+ label: CONTENT_EDITOR_TRACKING_LABEL,
+ property: contentType,
+ value,
+ });
+ });
+ });
+});
diff --git a/spec/frontend/content_editor/components/bubble_menus/formatting_spec.js b/spec/frontend/content_editor/components/bubble_menus/formatting_spec.js
deleted file mode 100644
index 1e2f58d9e40..00000000000
--- a/spec/frontend/content_editor/components/bubble_menus/formatting_spec.js
+++ /dev/null
@@ -1,87 +0,0 @@
-import { BubbleMenu } from '@tiptap/vue-2';
-import { mockTracking } from 'helpers/tracking_helper';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import FormattingBubbleMenu from '~/content_editor/components/bubble_menus/formatting.vue';
-
-import {
- BUBBLE_MENU_TRACKING_ACTION,
- CONTENT_EDITOR_TRACKING_LABEL,
-} from '~/content_editor/constants';
-import { createTestEditor } from '../../test_utils';
-
-describe('content_editor/components/bubble_menus/formatting', () => {
- let wrapper;
- let trackingSpy;
- let tiptapEditor;
-
- const buildEditor = () => {
- tiptapEditor = createTestEditor();
-
- jest.spyOn(tiptapEditor, 'isActive');
- };
-
- const buildWrapper = () => {
- wrapper = shallowMountExtended(FormattingBubbleMenu, {
- provide: {
- tiptapEditor,
- },
- });
- };
-
- beforeEach(() => {
- trackingSpy = mockTracking(undefined, null, jest.spyOn);
- buildEditor();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders bubble menu component', () => {
- buildWrapper();
- const bubbleMenu = wrapper.findComponent(BubbleMenu);
-
- expect(bubbleMenu.props().editor).toBe(tiptapEditor);
- expect(bubbleMenu.classes()).toEqual(['gl-shadow', 'gl-rounded-base', 'gl-bg-white']);
- });
-
- describe.each`
- testId | controlProps
- ${'bold'} | ${{ contentType: 'bold', iconName: 'bold', label: 'Bold text', editorCommand: 'toggleBold' }}
- ${'italic'} | ${{ contentType: 'italic', iconName: 'italic', label: 'Italic text', editorCommand: 'toggleItalic' }}
- ${'strike'} | ${{ contentType: 'strike', iconName: 'strikethrough', label: 'Strikethrough', editorCommand: 'toggleStrike' }}
- ${'code'} | ${{ contentType: 'code', iconName: 'code', label: 'Code', editorCommand: 'toggleCode' }}
- ${'superscript'} | ${{ contentType: 'superscript', iconName: 'superscript', label: 'Superscript', editorCommand: 'toggleSuperscript' }}
- ${'subscript'} | ${{ contentType: 'subscript', iconName: 'subscript', label: 'Subscript', editorCommand: 'toggleSubscript' }}
- ${'link'} | ${{ contentType: 'link', iconName: 'link', label: 'Insert link', editorCommand: 'toggleLink', editorCommandParams: { href: '' } }}
- `('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);
-
- expect(wrapper.findByTestId(testId).props()).toEqual(
- expect.objectContaining({
- ...controlProps,
- size: 'medium',
- category: 'tertiary',
- }),
- );
- });
-
- it('tracks the execution of toolbar controls', () => {
- const eventData = { contentType: 'italic', value: 1 };
- const { contentType, value } = eventData;
-
- wrapper.findByTestId(testId).vm.$emit('execute', eventData);
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, BUBBLE_MENU_TRACKING_ACTION, {
- label: CONTENT_EDITOR_TRACKING_LABEL,
- property: contentType,
- value,
- });
- });
- });
-});
diff --git a/spec/frontend/content_editor/components/bubble_menus/link_bubble_menu_spec.js b/spec/frontend/content_editor/components/bubble_menus/link_bubble_menu_spec.js
new file mode 100644
index 00000000000..9aa9c6483f4
--- /dev/null
+++ b/spec/frontend/content_editor/components/bubble_menus/link_bubble_menu_spec.js
@@ -0,0 +1,305 @@
+import { GlLink, GlForm } from '@gitlab/ui';
+import { nextTick } from 'vue';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import LinkBubbleMenu from '~/content_editor/components/bubble_menus/link_bubble_menu.vue';
+import EditorStateObserver from '~/content_editor/components/editor_state_observer.vue';
+import eventHubFactory from '~/helpers/event_hub_factory';
+import BubbleMenu from '~/content_editor/components/bubble_menus/bubble_menu.vue';
+import { stubComponent } from 'helpers/stub_component';
+import Link from '~/content_editor/extensions/link';
+import { createTestEditor } from '../../test_utils';
+
+const createFakeEvent = () => ({ preventDefault: jest.fn(), stopPropagation: jest.fn() });
+
+describe('content_editor/components/bubble_menus/link_bubble_menu', () => {
+ let wrapper;
+ let tiptapEditor;
+ let contentEditor;
+ let eventHub;
+
+ const buildEditor = () => {
+ tiptapEditor = createTestEditor({ extensions: [Link] });
+ contentEditor = { resolveUrl: jest.fn() };
+ eventHub = eventHubFactory();
+ };
+
+ const buildWrapper = () => {
+ wrapper = mountExtended(LinkBubbleMenu, {
+ provide: {
+ tiptapEditor,
+ contentEditor,
+ eventHub,
+ },
+ stubs: {
+ BubbleMenu: stubComponent(BubbleMenu),
+ },
+ });
+ };
+
+ const showMenu = () => {
+ wrapper.findComponent(BubbleMenu).vm.$emit('show');
+ return nextTick();
+ };
+
+ const buildWrapperAndDisplayMenu = () => {
+ buildWrapper();
+
+ return showMenu();
+ };
+
+ const findBubbleMenu = () => wrapper.findComponent(BubbleMenu);
+ const findLink = () => wrapper.findComponent(GlLink);
+ const findEditorStateObserver = () => wrapper.findComponent(EditorStateObserver);
+ const findEditLinkButton = () => wrapper.findByTestId('edit-link');
+
+ const expectLinkButtonsToExist = (exist = true) => {
+ expect(wrapper.findComponent(GlLink).exists()).toBe(exist);
+ expect(wrapper.findByTestId('copy-link-url').exists()).toBe(exist);
+ expect(wrapper.findByTestId('edit-link').exists()).toBe(exist);
+ expect(wrapper.findByTestId('remove-link').exists()).toBe(exist);
+ };
+
+ beforeEach(async () => {
+ buildEditor();
+
+ tiptapEditor
+ .chain()
+ .insertContent(
+ 'Download <a href="/path/to/project/-/wikis/uploads/my_file.pdf" data-canonical-src="uploads/my_file.pdf" title="Click here to download">PDF File</a>',
+ )
+ .setTextSelection(14) // put cursor in the middle of the link
+ .run();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders bubble menu component', async () => {
+ await buildWrapperAndDisplayMenu();
+
+ expect(findBubbleMenu().classes()).toEqual(['gl-shadow', 'gl-rounded-base', 'gl-bg-white']);
+ });
+
+ it('shows a clickable link to the URL in the link node', async () => {
+ await buildWrapperAndDisplayMenu();
+
+ expect(findLink().attributes()).toEqual(
+ expect.objectContaining({
+ href: '/path/to/project/-/wikis/uploads/my_file.pdf',
+ 'aria-label': 'uploads/my_file.pdf',
+ title: 'uploads/my_file.pdf',
+ target: '_blank',
+ }),
+ );
+ expect(findLink().text()).toBe('uploads/my_file.pdf');
+ });
+
+ it('updates the bubble menu state when @selectionUpdate event is triggered', async () => {
+ const linkUrl = 'https://gitlab.com';
+
+ await buildWrapperAndDisplayMenu();
+
+ expect(findLink().attributes()).toEqual(
+ expect.objectContaining({
+ href: '/path/to/project/-/wikis/uploads/my_file.pdf',
+ }),
+ );
+
+ tiptapEditor
+ .chain()
+ .setContent(
+ `Link to <a href="${linkUrl}" data-canonical-src="${linkUrl}" title="Click here to download">GitLab</a>`,
+ )
+ .setTextSelection(11)
+ .run();
+
+ findEditorStateObserver().vm.$emit('selectionUpdate');
+
+ await nextTick();
+
+ expect(findLink().attributes()).toEqual(
+ expect.objectContaining({
+ href: linkUrl,
+ }),
+ );
+ });
+
+ describe('when the selection changes within the same link', () => {
+ it('does not update the bubble menu state', async () => {
+ await buildWrapperAndDisplayMenu();
+
+ await findEditLinkButton().trigger('click');
+
+ expect(wrapper.findComponent(GlForm).exists()).toBe(true);
+
+ tiptapEditor.commands.setTextSelection(13);
+
+ findEditorStateObserver().vm.$emit('selectionUpdate');
+
+ await nextTick();
+
+ expect(wrapper.findComponent(GlForm).exists()).toBe(true);
+ });
+ });
+
+ it('cleans bubble menu state when hidden event is triggered', async () => {
+ await buildWrapperAndDisplayMenu();
+
+ expect(findLink().attributes()).toEqual(
+ expect.objectContaining({
+ href: '/path/to/project/-/wikis/uploads/my_file.pdf',
+ }),
+ );
+
+ findBubbleMenu().vm.$emit('hidden');
+
+ await nextTick();
+
+ expect(findLink().attributes()).toEqual(
+ expect.objectContaining({
+ href: '#',
+ }),
+ );
+ expect(findLink().text()).toEqual('');
+ });
+
+ describe('copy button', () => {
+ it('copies the canonical link to clipboard', async () => {
+ await buildWrapperAndDisplayMenu();
+
+ jest.spyOn(navigator.clipboard, 'writeText');
+
+ await wrapper.findByTestId('copy-link-url').vm.$emit('click');
+
+ expect(navigator.clipboard.writeText).toHaveBeenCalledWith('uploads/my_file.pdf');
+ });
+ });
+
+ describe('remove link button', () => {
+ it('removes the link', async () => {
+ await buildWrapperAndDisplayMenu();
+ await wrapper.findByTestId('remove-link').vm.$emit('click');
+
+ expect(tiptapEditor.getHTML()).toBe('<p>Download PDF File</p>');
+ });
+ });
+
+ describe('for a placeholder link', () => {
+ beforeEach(async () => {
+ tiptapEditor
+ .chain()
+ .clearContent()
+ .insertContent('Dummy link')
+ .selectAll()
+ .setLink({ href: '' })
+ .setTextSelection(4)
+ .run();
+
+ await buildWrapperAndDisplayMenu();
+ });
+
+ it('directly opens the edit form for a placeholder link', async () => {
+ expectLinkButtonsToExist(false);
+
+ expect(wrapper.findComponent(GlForm).exists()).toBe(true);
+ });
+
+ it('removes the link on clicking apply (if no change)', async () => {
+ await wrapper.findComponent(GlForm).vm.$emit('submit', createFakeEvent());
+
+ expect(tiptapEditor.getHTML()).toBe('<p>Dummy link</p>');
+ });
+
+ it('removes the link on clicking cancel', async () => {
+ await wrapper.findByTestId('cancel-link').vm.$emit('click');
+
+ expect(tiptapEditor.getHTML()).toBe('<p>Dummy link</p>');
+ });
+ });
+
+ describe('edit button', () => {
+ let linkHrefInput;
+ let linkTitleInput;
+
+ beforeEach(async () => {
+ await buildWrapperAndDisplayMenu();
+ await wrapper.findByTestId('edit-link').vm.$emit('click');
+
+ linkHrefInput = wrapper.findByTestId('link-href');
+ linkTitleInput = wrapper.findByTestId('link-title');
+ });
+
+ it('hides the link and copy/edit/remove link buttons', async () => {
+ expectLinkButtonsToExist(false);
+ });
+
+ it('shows a form to edit the link', () => {
+ expect(wrapper.findComponent(GlForm).exists()).toBe(true);
+
+ expect(linkHrefInput.element.value).toBe('uploads/my_file.pdf');
+ expect(linkTitleInput.element.value).toBe('Click here to download');
+ });
+
+ it('extends selection to select the entire link', () => {
+ const { from, to } = tiptapEditor.state.selection;
+
+ expect(from).toBe(10);
+ expect(to).toBe(18);
+ });
+
+ describe('after making changes in the form and clicking apply', () => {
+ beforeEach(async () => {
+ linkHrefInput.setValue('https://google.com');
+ linkTitleInput.setValue('Search Google');
+
+ contentEditor.resolveUrl.mockResolvedValue('https://google.com');
+
+ await wrapper.findComponent(GlForm).vm.$emit('submit', createFakeEvent());
+ });
+
+ it('updates prosemirror doc with new link', async () => {
+ expect(tiptapEditor.getHTML()).toBe(
+ '<p>Download <a target="_blank" rel="noopener noreferrer nofollow" href="https://google.com" title="Search Google">PDF File</a></p>',
+ );
+ });
+
+ it('updates the link in the bubble menu', () => {
+ const link = wrapper.findComponent(GlLink);
+ expect(link.attributes()).toEqual(
+ expect.objectContaining({
+ href: 'https://google.com',
+ 'aria-label': 'https://google.com',
+ title: 'https://google.com',
+ target: '_blank',
+ }),
+ );
+ expect(link.text()).toBe('https://google.com');
+ });
+ });
+
+ describe('after making changes in the form and clicking cancel', () => {
+ beforeEach(async () => {
+ linkHrefInput.setValue('https://google.com');
+ linkTitleInput.setValue('Search Google');
+
+ await wrapper.findByTestId('cancel-link').vm.$emit('click');
+ });
+
+ it('hides the form and shows the copy/edit/remove link buttons', () => {
+ expectLinkButtonsToExist();
+ });
+
+ it('resets the form with old values of the link from prosemirror', async () => {
+ // click edit once again to show the form back
+ await wrapper.findByTestId('edit-link').vm.$emit('click');
+
+ linkHrefInput = wrapper.findByTestId('link-href');
+ linkTitleInput = wrapper.findByTestId('link-title');
+
+ expect(linkHrefInput.element.value).toBe('uploads/my_file.pdf');
+ expect(linkTitleInput.element.value).toBe('Click here to download');
+ });
+ });
+ });
+});
diff --git a/spec/frontend/content_editor/components/bubble_menus/link_spec.js b/spec/frontend/content_editor/components/bubble_menus/link_spec.js
deleted file mode 100644
index 93204deb68c..00000000000
--- a/spec/frontend/content_editor/components/bubble_menus/link_spec.js
+++ /dev/null
@@ -1,227 +0,0 @@
-import { GlLink, GlForm } from '@gitlab/ui';
-import { BubbleMenu } from '@tiptap/vue-2';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import LinkBubbleMenu from '~/content_editor/components/bubble_menus/link.vue';
-import eventHubFactory from '~/helpers/event_hub_factory';
-import Link from '~/content_editor/extensions/link';
-import { createTestEditor, emitEditorEvent } from '../../test_utils';
-
-const createFakeEvent = () => ({ preventDefault: jest.fn(), stopPropagation: jest.fn() });
-
-describe('content_editor/components/bubble_menus/link', () => {
- let wrapper;
- let tiptapEditor;
- let contentEditor;
- let bubbleMenu;
- let eventHub;
-
- const buildEditor = () => {
- tiptapEditor = createTestEditor({ extensions: [Link] });
- contentEditor = { resolveUrl: jest.fn() };
- eventHub = eventHubFactory();
- };
-
- const buildWrapper = () => {
- wrapper = mountExtended(LinkBubbleMenu, {
- provide: {
- tiptapEditor,
- contentEditor,
- eventHub,
- },
- });
- };
-
- const expectLinkButtonsToExist = (exist = true) => {
- expect(wrapper.findComponent(GlLink).exists()).toBe(exist);
- expect(wrapper.findByTestId('copy-link-url').exists()).toBe(exist);
- expect(wrapper.findByTestId('edit-link').exists()).toBe(exist);
- expect(wrapper.findByTestId('remove-link').exists()).toBe(exist);
- };
-
- beforeEach(async () => {
- buildEditor();
- buildWrapper();
-
- tiptapEditor
- .chain()
- .insertContent(
- 'Download <a href="/path/to/project/-/wikis/uploads/my_file.pdf" data-canonical-src="uploads/my_file.pdf" title="Click here to download">PDF File</a>',
- )
- .setTextSelection(14) // put cursor in the middle of the link
- .run();
-
- await emitEditorEvent({ event: 'transaction', tiptapEditor });
-
- bubbleMenu = wrapper.findComponent(BubbleMenu);
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders bubble menu component', async () => {
- expect(bubbleMenu.props('editor')).toBe(tiptapEditor);
- expect(bubbleMenu.classes()).toEqual(['gl-shadow', 'gl-rounded-base', 'gl-bg-white']);
- });
-
- it('shows a clickable link to the URL in the link node', async () => {
- const link = wrapper.findComponent(GlLink);
- expect(link.attributes()).toEqual(
- expect.objectContaining({
- href: '/path/to/project/-/wikis/uploads/my_file.pdf',
- 'aria-label': 'uploads/my_file.pdf',
- title: 'uploads/my_file.pdf',
- target: '_blank',
- }),
- );
- expect(link.text()).toBe('uploads/my_file.pdf');
- });
-
- describe('copy button', () => {
- it('copies the canonical link to clipboard', async () => {
- jest.spyOn(navigator.clipboard, 'writeText');
-
- await wrapper.findByTestId('copy-link-url').vm.$emit('click');
-
- expect(navigator.clipboard.writeText).toHaveBeenCalledWith('uploads/my_file.pdf');
- });
- });
-
- describe('remove link button', () => {
- it('removes the link', async () => {
- await wrapper.findByTestId('remove-link').vm.$emit('click');
-
- expect(tiptapEditor.getHTML()).toBe('<p>Download PDF File</p>');
- });
- });
-
- describe('for a placeholder link', () => {
- beforeEach(async () => {
- tiptapEditor
- .chain()
- .clearContent()
- .insertContent('Dummy link')
- .selectAll()
- .setLink({ href: '' })
- .setTextSelection(4)
- .run();
-
- await emitEditorEvent({ event: 'transaction', tiptapEditor });
- });
-
- it('directly opens the edit form for a placeholder link', async () => {
- expectLinkButtonsToExist(false);
-
- expect(wrapper.findComponent(GlForm).exists()).toBe(true);
- });
-
- it('removes the link on clicking apply (if no change)', async () => {
- await wrapper.findComponent(GlForm).vm.$emit('submit', createFakeEvent());
-
- expect(tiptapEditor.getHTML()).toBe('<p>Dummy link</p>');
- });
-
- it('removes the link on clicking cancel', async () => {
- await wrapper.findByTestId('cancel-link').vm.$emit('click');
-
- expect(tiptapEditor.getHTML()).toBe('<p>Dummy link</p>');
- });
- });
-
- describe('edit button', () => {
- let linkHrefInput;
- let linkTitleInput;
-
- beforeEach(async () => {
- await wrapper.findByTestId('edit-link').vm.$emit('click');
-
- linkHrefInput = wrapper.findByTestId('link-href');
- linkTitleInput = wrapper.findByTestId('link-title');
- });
-
- it('hides the link and copy/edit/remove link buttons', async () => {
- expectLinkButtonsToExist(false);
- });
-
- it('shows a form to edit the link', () => {
- expect(wrapper.findComponent(GlForm).exists()).toBe(true);
-
- expect(linkHrefInput.element.value).toBe('uploads/my_file.pdf');
- expect(linkTitleInput.element.value).toBe('Click here to download');
- });
-
- it('extends selection to select the entire link', () => {
- const { from, to } = tiptapEditor.state.selection;
-
- expect(from).toBe(10);
- expect(to).toBe(18);
- });
-
- it('shows the copy/edit/remove link buttons again if selection changes to another non-link and then back again to a link', async () => {
- expectLinkButtonsToExist(false);
-
- tiptapEditor.commands.setTextSelection(3);
- await emitEditorEvent({ event: 'transaction', tiptapEditor });
-
- tiptapEditor.commands.setTextSelection(14);
- await emitEditorEvent({ event: 'transaction', tiptapEditor });
-
- expectLinkButtonsToExist(true);
- expect(wrapper.findComponent(GlForm).exists()).toBe(false);
- });
-
- describe('after making changes in the form and clicking apply', () => {
- beforeEach(async () => {
- linkHrefInput.setValue('https://google.com');
- linkTitleInput.setValue('Search Google');
-
- contentEditor.resolveUrl.mockResolvedValue('https://google.com');
-
- await wrapper.findComponent(GlForm).vm.$emit('submit', createFakeEvent());
- });
-
- it('updates prosemirror doc with new link', async () => {
- expect(tiptapEditor.getHTML()).toBe(
- '<p>Download <a target="_blank" rel="noopener noreferrer nofollow" href="https://google.com" title="Search Google">PDF File</a></p>',
- );
- });
-
- it('updates the link in the bubble menu', () => {
- const link = wrapper.findComponent(GlLink);
- expect(link.attributes()).toEqual(
- expect.objectContaining({
- href: 'https://google.com',
- 'aria-label': 'https://google.com',
- title: 'https://google.com',
- target: '_blank',
- }),
- );
- expect(link.text()).toBe('https://google.com');
- });
- });
-
- describe('after making changes in the form and clicking cancel', () => {
- beforeEach(async () => {
- linkHrefInput.setValue('https://google.com');
- linkTitleInput.setValue('Search Google');
-
- await wrapper.findByTestId('cancel-link').vm.$emit('click');
- });
-
- it('hides the form and shows the copy/edit/remove link buttons', () => {
- expectLinkButtonsToExist();
- });
-
- it('resets the form with old values of the link from prosemirror', async () => {
- // click edit once again to show the form back
- await wrapper.findByTestId('edit-link').vm.$emit('click');
-
- linkHrefInput = wrapper.findByTestId('link-href');
- linkTitleInput = wrapper.findByTestId('link-title');
-
- expect(linkHrefInput.element.value).toBe('uploads/my_file.pdf');
- expect(linkTitleInput.element.value).toBe('Click here to download');
- });
- });
- });
-});
diff --git a/spec/frontend/content_editor/components/bubble_menus/media_bubble_menu_spec.js b/spec/frontend/content_editor/components/bubble_menus/media_bubble_menu_spec.js
new file mode 100644
index 00000000000..13c6495ac41
--- /dev/null
+++ b/spec/frontend/content_editor/components/bubble_menus/media_bubble_menu_spec.js
@@ -0,0 +1,237 @@
+import { GlLink, GlForm } from '@gitlab/ui';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import BubbleMenu from '~/content_editor/components/bubble_menus/bubble_menu.vue';
+import MediaBubbleMenu from '~/content_editor/components/bubble_menus/media_bubble_menu.vue';
+import { stubComponent } from 'helpers/stub_component';
+import eventHubFactory from '~/helpers/event_hub_factory';
+import Image from '~/content_editor/extensions/image';
+import Audio from '~/content_editor/extensions/audio';
+import Video from '~/content_editor/extensions/video';
+import { createTestEditor, emitEditorEvent, mockChainedCommands } from '../../test_utils';
+import {
+ PROJECT_WIKI_ATTACHMENT_IMAGE_HTML,
+ PROJECT_WIKI_ATTACHMENT_AUDIO_HTML,
+ PROJECT_WIKI_ATTACHMENT_VIDEO_HTML,
+} from '../../test_constants';
+
+const TIPTAP_IMAGE_HTML = `<p>
+ <img src="https://gitlab.com/favicon.png" alt="gitlab favicon" title="gitlab favicon">
+</p>`;
+
+const TIPTAP_AUDIO_HTML = `<p>
+ <span class="media-container audio-container"><audio src="https://gitlab.com/favicon.png" controls="true" data-setup="{}" data-title="gitlab favicon"></audio><a href="https://gitlab.com/favicon.png">gitlab favicon</a></span>
+</p>`;
+
+const TIPTAP_VIDEO_HTML = `<p>
+ <span class="media-container video-container"><video src="https://gitlab.com/favicon.png" controls="true" data-setup="{}" data-title="gitlab favicon"></video><a href="https://gitlab.com/favicon.png">gitlab favicon</a></span>
+</p>`;
+
+const createFakeEvent = () => ({ preventDefault: jest.fn(), stopPropagation: jest.fn() });
+
+describe.each`
+ mediaType | mediaHTML | filePath | mediaOutputHTML
+ ${'image'} | ${PROJECT_WIKI_ATTACHMENT_IMAGE_HTML} | ${'test-file.png'} | ${TIPTAP_IMAGE_HTML}
+ ${'audio'} | ${PROJECT_WIKI_ATTACHMENT_AUDIO_HTML} | ${'test-file.mp3'} | ${TIPTAP_AUDIO_HTML}
+ ${'video'} | ${PROJECT_WIKI_ATTACHMENT_VIDEO_HTML} | ${'test-file.mp4'} | ${TIPTAP_VIDEO_HTML}
+`(
+ 'content_editor/components/bubble_menus/media_bubble_menu ($mediaType)',
+ ({ mediaType, mediaHTML, filePath, mediaOutputHTML }) => {
+ let wrapper;
+ let tiptapEditor;
+ let contentEditor;
+ let bubbleMenu;
+ let eventHub;
+
+ const buildEditor = () => {
+ tiptapEditor = createTestEditor({ extensions: [Image, Audio, Video] });
+ contentEditor = { resolveUrl: jest.fn() };
+ eventHub = eventHubFactory();
+ };
+
+ const buildWrapper = () => {
+ wrapper = mountExtended(MediaBubbleMenu, {
+ provide: {
+ tiptapEditor,
+ contentEditor,
+ eventHub,
+ },
+ stubs: {
+ BubbleMenu: stubComponent(BubbleMenu),
+ },
+ });
+ };
+
+ const selectFile = async (file) => {
+ const input = wrapper.findComponent({ ref: 'fileSelector' });
+
+ // override the property definition because `input.files` isn't directly modifyable
+ Object.defineProperty(input.element, 'files', { value: [file], writable: true });
+ await input.trigger('change');
+ };
+
+ const expectLinkButtonsToExist = (exist = true) => {
+ expect(wrapper.findComponent(GlLink).exists()).toBe(exist);
+ expect(wrapper.findByTestId('copy-media-src').exists()).toBe(exist);
+ expect(wrapper.findByTestId('edit-media').exists()).toBe(exist);
+ expect(wrapper.findByTestId('delete-media').exists()).toBe(exist);
+ };
+
+ beforeEach(async () => {
+ buildEditor();
+ buildWrapper();
+
+ tiptapEditor
+ .chain()
+ .insertContent(mediaHTML)
+ .setNodeSelection(4) // select the media
+ .run();
+
+ contentEditor.resolveUrl.mockResolvedValue(`/group1/project1/-/wikis/${filePath}`);
+
+ await emitEditorEvent({ event: 'transaction', tiptapEditor });
+
+ bubbleMenu = wrapper.findComponent(BubbleMenu);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders bubble menu component', async () => {
+ expect(bubbleMenu.classes()).toEqual(['gl-shadow', 'gl-rounded-base', 'gl-bg-white']);
+ });
+
+ it('shows a clickable link to the image', async () => {
+ const link = wrapper.findComponent(GlLink);
+ expect(link.attributes()).toEqual(
+ expect.objectContaining({
+ href: `/group1/project1/-/wikis/${filePath}`,
+ 'aria-label': filePath,
+ title: filePath,
+ target: '_blank',
+ }),
+ );
+ expect(link.text()).toBe(filePath);
+ });
+
+ describe('copy button', () => {
+ it(`copies the canonical link to the ${mediaType} to clipboard`, async () => {
+ jest.spyOn(navigator.clipboard, 'writeText');
+
+ await wrapper.findByTestId('copy-media-src').vm.$emit('click');
+
+ expect(navigator.clipboard.writeText).toHaveBeenCalledWith(filePath);
+ });
+ });
+
+ describe(`remove ${mediaType} button`, () => {
+ it(`removes the ${mediaType}`, async () => {
+ await wrapper.findByTestId('delete-media').vm.$emit('click');
+
+ expect(tiptapEditor.getHTML()).toBe('<p>\n \n</p>');
+ });
+ });
+
+ describe(`replace ${mediaType} button`, () => {
+ it('uploads and replaces the selected image when file input changes', async () => {
+ const commands = mockChainedCommands(tiptapEditor, [
+ 'focus',
+ 'deleteSelection',
+ 'uploadAttachment',
+ 'run',
+ ]);
+ const file = new File(['foo'], 'foo.png', { type: 'image/png' });
+
+ await wrapper.findByTestId('replace-media').vm.$emit('click');
+ await selectFile(file);
+
+ expect(commands.focus).toHaveBeenCalled();
+ expect(commands.deleteSelection).toHaveBeenCalled();
+ expect(commands.uploadAttachment).toHaveBeenCalledWith({ file });
+ expect(commands.run).toHaveBeenCalled();
+ });
+ });
+
+ describe('edit button', () => {
+ let mediaSrcInput;
+ let mediaTitleInput;
+ let mediaAltInput;
+
+ beforeEach(async () => {
+ await wrapper.findByTestId('edit-media').vm.$emit('click');
+
+ mediaSrcInput = wrapper.findByTestId('media-src');
+ mediaTitleInput = wrapper.findByTestId('media-title');
+ mediaAltInput = wrapper.findByTestId('media-alt');
+ });
+
+ it('hides the link and copy/edit/remove link buttons', async () => {
+ expectLinkButtonsToExist(false);
+ });
+
+ it(`shows a form to edit the ${mediaType} src/title/alt`, () => {
+ expect(wrapper.findComponent(GlForm).exists()).toBe(true);
+
+ expect(mediaSrcInput.element.value).toBe(filePath);
+ expect(mediaTitleInput.element.value).toBe('');
+ expect(mediaAltInput.element.value).toBe('test-file');
+ });
+
+ describe('after making changes in the form and clicking apply', () => {
+ beforeEach(async () => {
+ mediaSrcInput.setValue('https://gitlab.com/favicon.png');
+ mediaAltInput.setValue('gitlab favicon');
+ mediaTitleInput.setValue('gitlab favicon');
+
+ contentEditor.resolveUrl.mockResolvedValue('https://gitlab.com/favicon.png');
+
+ await wrapper.findComponent(GlForm).vm.$emit('submit', createFakeEvent());
+ });
+
+ it(`updates prosemirror doc with new src to the ${mediaType}`, async () => {
+ expect(tiptapEditor.getHTML()).toBe(mediaOutputHTML);
+ });
+
+ it(`updates the link to the ${mediaType} in the bubble menu`, () => {
+ const link = wrapper.findComponent(GlLink);
+ expect(link.attributes()).toEqual(
+ expect.objectContaining({
+ href: 'https://gitlab.com/favicon.png',
+ 'aria-label': 'https://gitlab.com/favicon.png',
+ title: 'https://gitlab.com/favicon.png',
+ target: '_blank',
+ }),
+ );
+ expect(link.text()).toBe('https://gitlab.com/favicon.png');
+ });
+ });
+
+ describe('after making changes in the form and clicking cancel', () => {
+ beforeEach(async () => {
+ mediaSrcInput.setValue('https://gitlab.com/favicon.png');
+ mediaAltInput.setValue('gitlab favicon');
+ mediaTitleInput.setValue('gitlab favicon');
+
+ await wrapper.findByTestId('cancel-editing-media').vm.$emit('click');
+ });
+
+ it('hides the form and shows the copy/edit/remove link buttons', () => {
+ expectLinkButtonsToExist();
+ });
+
+ it(`resets the form with old values of the ${mediaType} from prosemirror`, async () => {
+ // click edit once again to show the form back
+ await wrapper.findByTestId('edit-media').vm.$emit('click');
+
+ mediaSrcInput = wrapper.findByTestId('media-src');
+ mediaTitleInput = wrapper.findByTestId('media-title');
+ mediaAltInput = wrapper.findByTestId('media-alt');
+
+ expect(mediaSrcInput.element.value).toBe(filePath);
+ expect(mediaAltInput.element.value).toBe('test-file');
+ expect(mediaTitleInput.element.value).toBe('');
+ });
+ });
+ });
+ },
+);
diff --git a/spec/frontend/content_editor/components/bubble_menus/media_spec.js b/spec/frontend/content_editor/components/bubble_menus/media_spec.js
deleted file mode 100644
index fada4f06743..00000000000
--- a/spec/frontend/content_editor/components/bubble_menus/media_spec.js
+++ /dev/null
@@ -1,234 +0,0 @@
-import { GlLink, GlForm } from '@gitlab/ui';
-import { BubbleMenu } from '@tiptap/vue-2';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import MediaBubbleMenu from '~/content_editor/components/bubble_menus/media.vue';
-import eventHubFactory from '~/helpers/event_hub_factory';
-import Image from '~/content_editor/extensions/image';
-import Audio from '~/content_editor/extensions/audio';
-import Video from '~/content_editor/extensions/video';
-import { createTestEditor, emitEditorEvent, mockChainedCommands } from '../../test_utils';
-import {
- PROJECT_WIKI_ATTACHMENT_IMAGE_HTML,
- PROJECT_WIKI_ATTACHMENT_AUDIO_HTML,
- PROJECT_WIKI_ATTACHMENT_VIDEO_HTML,
-} from '../../test_constants';
-
-const TIPTAP_IMAGE_HTML = `<p>
- <img src="https://gitlab.com/favicon.png" alt="gitlab favicon" title="gitlab favicon">
-</p>`;
-
-const TIPTAP_AUDIO_HTML = `<p>
- <span class="media-container audio-container"><audio src="https://gitlab.com/favicon.png" controls="true" data-setup="{}" data-title="gitlab favicon"></audio><a href="https://gitlab.com/favicon.png">gitlab favicon</a></span>
-</p>`;
-
-const TIPTAP_VIDEO_HTML = `<p>
- <span class="media-container video-container"><video src="https://gitlab.com/favicon.png" controls="true" data-setup="{}" data-title="gitlab favicon"></video><a href="https://gitlab.com/favicon.png">gitlab favicon</a></span>
-</p>`;
-
-const createFakeEvent = () => ({ preventDefault: jest.fn(), stopPropagation: jest.fn() });
-
-describe.each`
- mediaType | mediaHTML | filePath | mediaOutputHTML
- ${'image'} | ${PROJECT_WIKI_ATTACHMENT_IMAGE_HTML} | ${'test-file.png'} | ${TIPTAP_IMAGE_HTML}
- ${'audio'} | ${PROJECT_WIKI_ATTACHMENT_AUDIO_HTML} | ${'test-file.mp3'} | ${TIPTAP_AUDIO_HTML}
- ${'video'} | ${PROJECT_WIKI_ATTACHMENT_VIDEO_HTML} | ${'test-file.mp4'} | ${TIPTAP_VIDEO_HTML}
-`(
- 'content_editor/components/bubble_menus/media ($mediaType)',
- ({ mediaType, mediaHTML, filePath, mediaOutputHTML }) => {
- let wrapper;
- let tiptapEditor;
- let contentEditor;
- let bubbleMenu;
- let eventHub;
-
- const buildEditor = () => {
- tiptapEditor = createTestEditor({ extensions: [Image, Audio, Video] });
- contentEditor = { resolveUrl: jest.fn() };
- eventHub = eventHubFactory();
- };
-
- const buildWrapper = () => {
- wrapper = mountExtended(MediaBubbleMenu, {
- provide: {
- tiptapEditor,
- contentEditor,
- eventHub,
- },
- });
- };
-
- const selectFile = async (file) => {
- const input = wrapper.find({ ref: 'fileSelector' });
-
- // override the property definition because `input.files` isn't directly modifyable
- Object.defineProperty(input.element, 'files', { value: [file], writable: true });
- await input.trigger('change');
- };
-
- const expectLinkButtonsToExist = (exist = true) => {
- expect(wrapper.findComponent(GlLink).exists()).toBe(exist);
- expect(wrapper.findByTestId('copy-media-src').exists()).toBe(exist);
- expect(wrapper.findByTestId('edit-media').exists()).toBe(exist);
- expect(wrapper.findByTestId('delete-media').exists()).toBe(exist);
- };
-
- beforeEach(async () => {
- buildEditor();
- buildWrapper();
-
- tiptapEditor
- .chain()
- .insertContent(mediaHTML)
- .setNodeSelection(4) // select the media
- .run();
-
- contentEditor.resolveUrl.mockResolvedValue(`/group1/project1/-/wikis/${filePath}`);
-
- await emitEditorEvent({ event: 'transaction', tiptapEditor });
-
- bubbleMenu = wrapper.findComponent(BubbleMenu);
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders bubble menu component', async () => {
- expect(bubbleMenu.props('editor')).toBe(tiptapEditor);
- expect(bubbleMenu.classes()).toEqual(['gl-shadow', 'gl-rounded-base', 'gl-bg-white']);
- });
-
- it('shows a clickable link to the image', async () => {
- const link = wrapper.findComponent(GlLink);
- expect(link.attributes()).toEqual(
- expect.objectContaining({
- href: `/group1/project1/-/wikis/${filePath}`,
- 'aria-label': filePath,
- title: filePath,
- target: '_blank',
- }),
- );
- expect(link.text()).toBe(filePath);
- });
-
- describe('copy button', () => {
- it(`copies the canonical link to the ${mediaType} to clipboard`, async () => {
- jest.spyOn(navigator.clipboard, 'writeText');
-
- await wrapper.findByTestId('copy-media-src').vm.$emit('click');
-
- expect(navigator.clipboard.writeText).toHaveBeenCalledWith(filePath);
- });
- });
-
- describe(`remove ${mediaType} button`, () => {
- it(`removes the ${mediaType}`, async () => {
- await wrapper.findByTestId('delete-media').vm.$emit('click');
-
- expect(tiptapEditor.getHTML()).toBe('<p>\n \n</p>');
- });
- });
-
- describe(`replace ${mediaType} button`, () => {
- it('uploads and replaces the selected image when file input changes', async () => {
- const commands = mockChainedCommands(tiptapEditor, [
- 'focus',
- 'deleteSelection',
- 'uploadAttachment',
- 'run',
- ]);
- const file = new File(['foo'], 'foo.png', { type: 'image/png' });
-
- await wrapper.findByTestId('replace-media').vm.$emit('click');
- await selectFile(file);
-
- expect(commands.focus).toHaveBeenCalled();
- expect(commands.deleteSelection).toHaveBeenCalled();
- expect(commands.uploadAttachment).toHaveBeenCalledWith({ file });
- expect(commands.run).toHaveBeenCalled();
- });
- });
-
- describe('edit button', () => {
- let mediaSrcInput;
- let mediaTitleInput;
- let mediaAltInput;
-
- beforeEach(async () => {
- await wrapper.findByTestId('edit-media').vm.$emit('click');
-
- mediaSrcInput = wrapper.findByTestId('media-src');
- mediaTitleInput = wrapper.findByTestId('media-title');
- mediaAltInput = wrapper.findByTestId('media-alt');
- });
-
- it('hides the link and copy/edit/remove link buttons', async () => {
- expectLinkButtonsToExist(false);
- });
-
- it(`shows a form to edit the ${mediaType} src/title/alt`, () => {
- expect(wrapper.findComponent(GlForm).exists()).toBe(true);
-
- expect(mediaSrcInput.element.value).toBe(filePath);
- expect(mediaTitleInput.element.value).toBe('');
- expect(mediaAltInput.element.value).toBe('test-file');
- });
-
- describe('after making changes in the form and clicking apply', () => {
- beforeEach(async () => {
- mediaSrcInput.setValue('https://gitlab.com/favicon.png');
- mediaAltInput.setValue('gitlab favicon');
- mediaTitleInput.setValue('gitlab favicon');
-
- contentEditor.resolveUrl.mockResolvedValue('https://gitlab.com/favicon.png');
-
- await wrapper.findComponent(GlForm).vm.$emit('submit', createFakeEvent());
- });
-
- it(`updates prosemirror doc with new src to the ${mediaType}`, async () => {
- expect(tiptapEditor.getHTML()).toBe(mediaOutputHTML);
- });
-
- it(`updates the link to the ${mediaType} in the bubble menu`, () => {
- const link = wrapper.findComponent(GlLink);
- expect(link.attributes()).toEqual(
- expect.objectContaining({
- href: 'https://gitlab.com/favicon.png',
- 'aria-label': 'https://gitlab.com/favicon.png',
- title: 'https://gitlab.com/favicon.png',
- target: '_blank',
- }),
- );
- expect(link.text()).toBe('https://gitlab.com/favicon.png');
- });
- });
-
- describe('after making changes in the form and clicking cancel', () => {
- beforeEach(async () => {
- mediaSrcInput.setValue('https://gitlab.com/favicon.png');
- mediaAltInput.setValue('gitlab favicon');
- mediaTitleInput.setValue('gitlab favicon');
-
- await wrapper.findByTestId('cancel-editing-media').vm.$emit('click');
- });
-
- it('hides the form and shows the copy/edit/remove link buttons', () => {
- expectLinkButtonsToExist();
- });
-
- it(`resets the form with old values of the ${mediaType} from prosemirror`, async () => {
- // click edit once again to show the form back
- await wrapper.findByTestId('edit-media').vm.$emit('click');
-
- mediaSrcInput = wrapper.findByTestId('media-src');
- mediaTitleInput = wrapper.findByTestId('media-title');
- mediaAltInput = wrapper.findByTestId('media-alt');
-
- expect(mediaSrcInput.element.value).toBe(filePath);
- expect(mediaAltInput.element.value).toBe('test-file');
- expect(mediaTitleInput.element.value).toBe('');
- });
- });
- });
- },
-);
diff --git a/spec/frontend/content_editor/components/content_editor_alert_spec.js b/spec/frontend/content_editor/components/content_editor_alert_spec.js
index 12484cb13c6..ee9ead8f8a7 100644
--- a/spec/frontend/content_editor/components/content_editor_alert_spec.js
+++ b/spec/frontend/content_editor/components/content_editor_alert_spec.js
@@ -51,6 +51,16 @@ describe('content_editor/components/content_editor_alert', () => {
},
);
+ it('does not show primary action by default', async () => {
+ const message = 'error message';
+
+ createWrapper();
+ eventHub.$emit(ALERT_EVENT, { message });
+ await nextTick();
+
+ expect(findErrorAlert().attributes().primaryButtonText).toBeUndefined();
+ });
+
it('allows dismissing the error', async () => {
const message = 'error message';
@@ -62,4 +72,19 @@ describe('content_editor/components/content_editor_alert', () => {
expect(findErrorAlert().exists()).toBe(false);
});
+
+ it('allows dismissing the error with a primary action button', async () => {
+ const message = 'error message';
+ const actionLabel = 'Retry';
+ const action = jest.fn();
+
+ createWrapper();
+ eventHub.$emit(ALERT_EVENT, { message, action, actionLabel });
+ await nextTick();
+ findErrorAlert().vm.$emit('primaryAction');
+ await nextTick();
+
+ expect(action).toHaveBeenCalled();
+ expect(findErrorAlert().exists()).toBe(false);
+ });
});
diff --git a/spec/frontend/content_editor/components/content_editor_spec.js b/spec/frontend/content_editor/components/content_editor_spec.js
index 0ba2672100b..ae52cb05eaf 100644
--- a/spec/frontend/content_editor/components/content_editor_spec.js
+++ b/spec/frontend/content_editor/components/content_editor_spec.js
@@ -1,136 +1,227 @@
-import { EditorContent } from '@tiptap/vue-2';
+import { GlAlert } from '@gitlab/ui';
+import { EditorContent, Editor } from '@tiptap/vue-2';
+import { nextTick } from 'vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ContentEditor from '~/content_editor/components/content_editor.vue';
import ContentEditorAlert from '~/content_editor/components/content_editor_alert.vue';
import ContentEditorProvider from '~/content_editor/components/content_editor_provider.vue';
import EditorStateObserver from '~/content_editor/components/editor_state_observer.vue';
-import FormattingBubbleMenu from '~/content_editor/components/bubble_menus/formatting.vue';
+import FormattingBubbleMenu from '~/content_editor/components/bubble_menus/formatting_bubble_menu.vue';
+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 LoadingIndicator from '~/content_editor/components/loading_indicator.vue';
-import { emitEditorEvent } from '../test_utils';
+import waitForPromises from 'helpers/wait_for_promises';
jest.mock('~/emoji');
describe('ContentEditor', () => {
let wrapper;
- let contentEditor;
let renderMarkdown;
const uploadsPath = '/uploads';
const findEditorElement = () => wrapper.findByTestId('content-editor');
const findEditorContent = () => wrapper.findComponent(EditorContent);
const findEditorStateObserver = () => wrapper.findComponent(EditorStateObserver);
- const createWrapper = (propsData = {}) => {
- renderMarkdown = jest.fn();
-
+ const findLoadingIndicator = () => wrapper.findComponent(LoadingIndicator);
+ const findContentEditorAlert = () => wrapper.findComponent(ContentEditorAlert);
+ const createWrapper = ({ markdown } = {}) => {
wrapper = shallowMountExtended(ContentEditor, {
propsData: {
renderMarkdown,
uploadsPath,
- ...propsData,
+ markdown,
},
stubs: {
EditorStateObserver,
ContentEditorProvider,
- },
- listeners: {
- initialized(editor) {
- contentEditor = editor;
- },
+ ContentEditorAlert,
},
});
};
+ beforeEach(() => {
+ renderMarkdown = jest.fn();
+ });
+
afterEach(() => {
wrapper.destroy();
});
- it('triggers initialized event and provides contentEditor instance as event data', () => {
+ it('triggers initialized event', () => {
createWrapper();
- expect(contentEditor).not.toBeFalsy();
+ expect(wrapper.emitted('initialized')).toHaveLength(1);
});
- it('renders EditorContent component and provides tiptapEditor instance', () => {
- createWrapper();
+ it('renders EditorContent component and provides tiptapEditor instance', async () => {
+ const markdown = 'hello world';
+
+ createWrapper({ markdown });
+
+ renderMarkdown.mockResolvedValueOnce(markdown);
+
+ await nextTick();
const editorContent = findEditorContent();
- expect(editorContent.props().editor).toBe(contentEditor.tiptapEditor);
+ expect(editorContent.props().editor).toBeInstanceOf(Editor);
expect(editorContent.classes()).toContain('md');
});
- it('renders ContentEditorProvider component', () => {
- createWrapper();
+ it('renders ContentEditorProvider component', async () => {
+ await createWrapper();
expect(wrapper.findComponent(ContentEditorProvider).exists()).toBe(true);
});
- it('renders top toolbar component', () => {
- createWrapper();
+ it('renders top toolbar component', async () => {
+ await createWrapper();
expect(wrapper.findComponent(TopToolbar).exists()).toBe(true);
});
- it('adds is-focused class when focus event is emitted', async () => {
- createWrapper();
+ describe('when setting initial content', () => {
+ it('displays loading indicator', async () => {
+ createWrapper();
- await emitEditorEvent({ tiptapEditor: contentEditor.tiptapEditor, event: 'focus' });
+ await nextTick();
- expect(findEditorElement().classes()).toContain('is-focused');
- });
+ expect(findLoadingIndicator().exists()).toBe(true);
+ });
- it('removes is-focused class when blur event is emitted', async () => {
- createWrapper();
+ it('emits loading event', async () => {
+ createWrapper();
- await emitEditorEvent({ tiptapEditor: contentEditor.tiptapEditor, event: 'focus' });
- await emitEditorEvent({ tiptapEditor: contentEditor.tiptapEditor, event: 'blur' });
+ await nextTick();
- expect(findEditorElement().classes()).not.toContain('is-focused');
- });
+ expect(wrapper.emitted('loading')).toHaveLength(1);
+ });
- it('emits change event when document is updated', async () => {
- createWrapper();
+ describe('succeeds', () => {
+ beforeEach(async () => {
+ renderMarkdown.mockResolvedValueOnce('hello world');
- await emitEditorEvent({ tiptapEditor: contentEditor.tiptapEditor, event: 'update' });
+ createWrapper({ markddown: 'hello world' });
+ await nextTick();
+ });
- expect(wrapper.emitted('change')).toEqual([
- [
- {
- empty: contentEditor.empty,
- },
- ],
- ]);
- });
+ it('hides loading indicator', async () => {
+ await nextTick();
+ expect(findLoadingIndicator().exists()).toBe(false);
+ });
- it('renders content_editor_alert component', () => {
- createWrapper();
+ it('emits loadingSuccess event', () => {
+ expect(wrapper.emitted('loadingSuccess')).toHaveLength(1);
+ });
+ });
+
+ describe('fails', () => {
+ beforeEach(async () => {
+ renderMarkdown.mockRejectedValueOnce(new Error());
+
+ createWrapper({ markddown: 'hello world' });
+ await nextTick();
+ });
+
+ it('sets the content editor as read only when loading content fails', async () => {
+ await nextTick();
- expect(wrapper.findComponent(ContentEditorAlert).exists()).toBe(true);
+ expect(findEditorContent().props().editor.isEditable).toBe(false);
+ });
+
+ it('hides loading indicator', async () => {
+ await nextTick();
+
+ expect(findLoadingIndicator().exists()).toBe(false);
+ });
+
+ it('emits loadingError event', () => {
+ expect(wrapper.emitted('loadingError')).toHaveLength(1);
+ });
+
+ it('displays error alert indicating that the content editor failed to load', () => {
+ expect(findContentEditorAlert().text()).toContain(
+ 'An error occurred while trying to render the content editor. Please try again.',
+ );
+ });
+
+ describe('when clicking the retry button in the loading error alert and loading succeeds', () => {
+ beforeEach(async () => {
+ renderMarkdown.mockResolvedValueOnce('hello markdown');
+ await wrapper.findComponent(GlAlert).vm.$emit('primaryAction');
+ });
+
+ it('hides the loading error alert', () => {
+ expect(findContentEditorAlert().text()).toBe('');
+ });
+
+ it('sets the content editor as writable', async () => {
+ await nextTick();
+
+ expect(findEditorContent().props().editor.isEditable).toBe(true);
+ });
+ });
+ });
});
- it('renders loading indicator component', () => {
- createWrapper();
+ describe('when focused event is emitted', () => {
+ beforeEach(async () => {
+ createWrapper();
+
+ findEditorStateObserver().vm.$emit('focus');
+
+ await nextTick();
+ });
- expect(wrapper.findComponent(LoadingIndicator).exists()).toBe(true);
+ it('adds is-focused class when focus event is emitted', () => {
+ expect(findEditorElement().classes()).toContain('is-focused');
+ });
+
+ it('removes is-focused class when blur event is emitted', async () => {
+ findEditorStateObserver().vm.$emit('blur');
+
+ await nextTick();
+
+ expect(findEditorElement().classes()).not.toContain('is-focused');
+ });
});
- it('renders formatting bubble menu', () => {
- createWrapper();
+ describe('when editorStateObserver emits docUpdate event', () => {
+ it('emits change event with the latest markdown', async () => {
+ const markdown = 'Loaded content';
- expect(wrapper.findComponent(FormattingBubbleMenu).exists()).toBe(true);
+ renderMarkdown.mockResolvedValueOnce(markdown);
+
+ createWrapper({ markdown: 'initial content' });
+
+ await nextTick();
+ await waitForPromises();
+
+ findEditorStateObserver().vm.$emit('docUpdate');
+
+ expect(wrapper.emitted('change')).toEqual([
+ [
+ {
+ markdown,
+ changed: false,
+ empty: false,
+ },
+ ],
+ ]);
+ });
});
it.each`
- event
- ${'loading'}
- ${'loadingSuccess'}
- ${'loadingError'}
- `('broadcasts $event event triggered by editor-state-observer component', ({ event }) => {
+ name | component
+ ${'formatting'} | ${FormattingBubbleMenu}
+ ${'link'} | ${LinkBubbleMenu}
+ ${'media'} | ${MediaBubbleMenu}
+ ${'codeBlock'} | ${CodeBlockBubbleMenu}
+ `('renders formatting bubble menu', ({ component }) => {
createWrapper();
- findEditorStateObserver().vm.$emit(event);
-
- expect(wrapper.emitted(event)).toHaveLength(1);
+ expect(wrapper.findComponent(component).exists()).toBe(true);
});
});
diff --git a/spec/frontend/content_editor/components/editor_state_observer_spec.js b/spec/frontend/content_editor/components/editor_state_observer_spec.js
index 51a594a606b..e8c2d8c8793 100644
--- a/spec/frontend/content_editor/components/editor_state_observer_spec.js
+++ b/spec/frontend/content_editor/components/editor_state_observer_spec.js
@@ -4,12 +4,7 @@ import EditorStateObserver, {
tiptapToComponentMap,
} from '~/content_editor/components/editor_state_observer.vue';
import eventHubFactory from '~/helpers/event_hub_factory';
-import {
- LOADING_CONTENT_EVENT,
- LOADING_SUCCESS_EVENT,
- LOADING_ERROR_EVENT,
- ALERT_EVENT,
-} from '~/content_editor/constants';
+import { ALERT_EVENT } from '~/content_editor/constants';
import { createTestEditor } from '../test_utils';
describe('content_editor/components/editor_state_observer', () => {
@@ -18,9 +13,6 @@ describe('content_editor/components/editor_state_observer', () => {
let onDocUpdateListener;
let onSelectionUpdateListener;
let onTransactionListener;
- let onLoadingContentListener;
- let onLoadingSuccessListener;
- let onLoadingErrorListener;
let onAlertListener;
let eventHub;
@@ -38,9 +30,6 @@ describe('content_editor/components/editor_state_observer', () => {
selectionUpdate: onSelectionUpdateListener,
transaction: onTransactionListener,
[ALERT_EVENT]: onAlertListener,
- [LOADING_CONTENT_EVENT]: onLoadingContentListener,
- [LOADING_SUCCESS_EVENT]: onLoadingSuccessListener,
- [LOADING_ERROR_EVENT]: onLoadingErrorListener,
},
});
};
@@ -50,9 +39,6 @@ describe('content_editor/components/editor_state_observer', () => {
onSelectionUpdateListener = jest.fn();
onTransactionListener = jest.fn();
onAlertListener = jest.fn();
- onLoadingSuccessListener = jest.fn();
- onLoadingContentListener = jest.fn();
- onLoadingErrorListener = jest.fn();
buildEditor();
});
@@ -81,11 +67,8 @@ describe('content_editor/components/editor_state_observer', () => {
});
it.each`
- event | listener
- ${ALERT_EVENT} | ${() => onAlertListener}
- ${LOADING_CONTENT_EVENT} | ${() => onLoadingContentListener}
- ${LOADING_SUCCESS_EVENT} | ${() => onLoadingSuccessListener}
- ${LOADING_ERROR_EVENT} | ${() => onLoadingErrorListener}
+ event | listener
+ ${ALERT_EVENT} | ${() => onAlertListener}
`('listens to $event event in the eventBus object', ({ event, listener }) => {
const args = {};
@@ -114,9 +97,6 @@ describe('content_editor/components/editor_state_observer', () => {
it.each`
event
${ALERT_EVENT}
- ${LOADING_CONTENT_EVENT}
- ${LOADING_SUCCESS_EVENT}
- ${LOADING_ERROR_EVENT}
`('removes $event event hook from eventHub', ({ event }) => {
jest.spyOn(eventHub, '$off');
jest.spyOn(eventHub, '$on');
diff --git a/spec/frontend/content_editor/components/loading_indicator_spec.js b/spec/frontend/content_editor/components/loading_indicator_spec.js
index e4fb09b70a4..0065103d01b 100644
--- a/spec/frontend/content_editor/components/loading_indicator_spec.js
+++ b/spec/frontend/content_editor/components/loading_indicator_spec.js
@@ -1,18 +1,10 @@
import { GlLoadingIcon } from '@gitlab/ui';
-import { nextTick } from 'vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import LoadingIndicator from '~/content_editor/components/loading_indicator.vue';
-import EditorStateObserver from '~/content_editor/components/editor_state_observer.vue';
-import {
- LOADING_CONTENT_EVENT,
- LOADING_SUCCESS_EVENT,
- LOADING_ERROR_EVENT,
-} from '~/content_editor/constants';
describe('content_editor/components/loading_indicator', () => {
let wrapper;
- const findEditorStateObserver = () => wrapper.findComponent(EditorStateObserver);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const createWrapper = () => {
@@ -24,48 +16,12 @@ describe('content_editor/components/loading_indicator', () => {
});
describe('when loading content', () => {
- beforeEach(async () => {
+ beforeEach(() => {
createWrapper();
-
- findEditorStateObserver().vm.$emit(LOADING_CONTENT_EVENT);
-
- await nextTick();
});
it('displays loading indicator', () => {
expect(findLoadingIcon().exists()).toBe(true);
});
});
-
- describe('when loading content succeeds', () => {
- beforeEach(async () => {
- createWrapper();
-
- findEditorStateObserver().vm.$emit(LOADING_CONTENT_EVENT);
- await nextTick();
- findEditorStateObserver().vm.$emit(LOADING_SUCCESS_EVENT);
- await nextTick();
- });
-
- it('hides loading indicator', () => {
- expect(findLoadingIcon().exists()).toBe(false);
- });
- });
-
- describe('when loading content fails', () => {
- const error = 'error';
-
- beforeEach(async () => {
- createWrapper();
-
- findEditorStateObserver().vm.$emit(LOADING_CONTENT_EVENT);
- await nextTick();
- findEditorStateObserver().vm.$emit(LOADING_ERROR_EVENT, error);
- await nextTick();
- });
-
- it('hides loading indicator', () => {
- expect(findLoadingIcon().exists()).toBe(false);
- });
- });
});
diff --git a/spec/frontend/content_editor/components/toolbar_image_button_spec.js b/spec/frontend/content_editor/components/toolbar_image_button_spec.js
index dab7e67d7c5..5473d43f5a1 100644
--- a/spec/frontend/content_editor/components/toolbar_image_button_spec.js
+++ b/spec/frontend/content_editor/components/toolbar_image_button_spec.js
@@ -1,8 +1,9 @@
-import { GlButton, GlFormInputGroup } from '@gitlab/ui';
+import { GlButton, GlFormInputGroup, GlDropdown } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import ToolbarImageButton from '~/content_editor/components/toolbar_image_button.vue';
import Attachment from '~/content_editor/extensions/attachment';
import Image from '~/content_editor/extensions/image';
+import { stubComponent } from 'helpers/stub_component';
import { createTestEditor, mockChainedCommands } from '../test_utils';
describe('content_editor/components/toolbar_image_button', () => {
@@ -14,15 +15,19 @@ describe('content_editor/components/toolbar_image_button', () => {
provide: {
tiptapEditor: editor,
},
+ stubs: {
+ GlDropdown: stubComponent(GlDropdown),
+ },
});
};
const findImageURLInput = () =>
wrapper.findComponent(GlFormInputGroup).find('input[type="text"]');
const findApplyImageButton = () => wrapper.findComponent(GlButton);
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
const selectFile = async (file) => {
- const input = wrapper.find({ ref: 'fileSelector' });
+ const input = wrapper.findComponent({ ref: 'fileSelector' });
// override the property definition because `input.files` isn't directly modifyable
Object.defineProperty(input.element, 'files', { value: [file], writable: true });
@@ -77,4 +82,16 @@ describe('content_editor/components/toolbar_image_button', () => {
expect(wrapper.emitted().execute[0]).toEqual([{ contentType: 'image', value: 'upload' }]);
});
+
+ describe('a11y tests', () => {
+ it('sets text, title, and text-sr-only properties to the table button dropdown', () => {
+ buildWrapper();
+
+ expect(findDropdown().props()).toMatchObject({
+ text: 'Insert image',
+ textSrOnly: true,
+ });
+ expect(findDropdown().attributes('title')).toBe('Insert image');
+ });
+ });
});
diff --git a/spec/frontend/content_editor/components/toolbar_link_button_spec.js b/spec/frontend/content_editor/components/toolbar_link_button_spec.js
index fc26a9da471..40e859e96af 100644
--- a/spec/frontend/content_editor/components/toolbar_link_button_spec.js
+++ b/spec/frontend/content_editor/components/toolbar_link_button_spec.js
@@ -4,6 +4,7 @@ import ToolbarLinkButton from '~/content_editor/components/toolbar_link_button.v
import eventHubFactory from '~/helpers/event_hub_factory';
import Link from '~/content_editor/extensions/link';
import { hasSelection } from '~/content_editor/services/utils';
+import { stubComponent } from 'helpers/stub_component';
import { createTestEditor, mockChainedCommands, emitEditorEvent } from '../test_utils';
jest.mock('~/content_editor/services/utils');
@@ -18,6 +19,9 @@ describe('content_editor/components/toolbar_link_button', () => {
tiptapEditor: editor,
eventHub: eventHubFactory(),
},
+ stubs: {
+ GlDropdown: stubComponent(GlDropdown),
+ },
});
};
const findDropdown = () => wrapper.findComponent(GlDropdown);
@@ -26,7 +30,7 @@ describe('content_editor/components/toolbar_link_button', () => {
const findRemoveLinkButton = () => wrapper.findByText('Remove link');
const selectFile = async (file) => {
- const input = wrapper.find({ ref: 'fileSelector' });
+ const input = wrapper.findComponent({ ref: 'fileSelector' });
// override the property definition because `input.files` isn't directly modifyable
Object.defineProperty(input.element, 'files', { value: [file], writable: true });
@@ -205,4 +209,16 @@ describe('content_editor/components/toolbar_link_button', () => {
});
});
});
+
+ describe('a11y tests', () => {
+ it('sets text, title, and text-sr-only properties to the table button dropdown', () => {
+ buildWrapper();
+
+ expect(findDropdown().props()).toMatchObject({
+ text: 'Insert link',
+ textSrOnly: true,
+ });
+ expect(findDropdown().attributes('title')).toBe('Insert link');
+ });
+ });
});
diff --git a/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js b/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js
index 62fec8d4e72..a23f8370adf 100644
--- a/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js
+++ b/spec/frontend/content_editor/components/toolbar_more_dropdown_spec.js
@@ -1,8 +1,10 @@
+import { GlDropdown } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import ToolbarMoreDropdown from '~/content_editor/components/toolbar_more_dropdown.vue';
import Diagram from '~/content_editor/extensions/diagram';
import HorizontalRule from '~/content_editor/extensions/horizontal_rule';
import eventHubFactory from '~/helpers/event_hub_factory';
+import { stubComponent } from 'helpers/stub_component';
import { createTestEditor, mockChainedCommands, emitEditorEvent } from '../test_utils';
describe('content_editor/components/toolbar_more_dropdown', () => {
@@ -23,10 +25,15 @@ describe('content_editor/components/toolbar_more_dropdown', () => {
tiptapEditor,
eventHub,
},
+ stubs: {
+ GlDropdown: stubComponent(GlDropdown),
+ },
propsData,
});
};
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
+
beforeEach(() => {
buildEditor();
buildWrapper();
@@ -67,4 +74,14 @@ describe('content_editor/components/toolbar_more_dropdown', () => {
expect(wrapper.emitted('execute')).toEqual([[{ contentType }]]);
});
});
+
+ describe('a11y tests', () => {
+ it('sets text, title, and text-sr-only properties to the table button dropdown', () => {
+ expect(findDropdown().props()).toMatchObject({
+ text: 'More',
+ textSrOnly: true,
+ });
+ expect(findDropdown().attributes('title')).toBe('More');
+ });
+ });
});
diff --git a/spec/frontend/content_editor/components/toolbar_table_button_spec.js b/spec/frontend/content_editor/components/toolbar_table_button_spec.js
index 056e5e04e1f..aa4604661e5 100644
--- a/spec/frontend/content_editor/components/toolbar_table_button_spec.js
+++ b/spec/frontend/content_editor/components/toolbar_table_button_spec.js
@@ -1,6 +1,7 @@
import { GlDropdown, GlButton } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import ToolbarTableButton from '~/content_editor/components/toolbar_table_button.vue';
+import { stubComponent } from 'helpers/stub_component';
import { createTestEditor, mockChainedCommands } from '../test_utils';
describe('content_editor/components/toolbar_table_button', () => {
@@ -12,6 +13,9 @@ describe('content_editor/components/toolbar_table_button', () => {
provide: {
tiptapEditor: editor,
},
+ stubs: {
+ GlDropdown: stubComponent(GlDropdown),
+ },
});
};
@@ -98,4 +102,14 @@ describe('content_editor/components/toolbar_table_button', () => {
expect(getNumButtons()).toBe(100); // 10x10 (and not 11x11)
});
+
+ describe('a11y tests', () => {
+ it('sets text, title, and text-sr-only properties to the table button dropdown', () => {
+ expect(findDropdown().props()).toMatchObject({
+ text: 'Insert table',
+ textSrOnly: true,
+ });
+ expect(findDropdown().attributes('title')).toBe('Insert table');
+ });
+ });
});
diff --git a/spec/frontend/content_editor/components/toolbar_text_style_dropdown_spec.js b/spec/frontend/content_editor/components/toolbar_text_style_dropdown_spec.js
index 608be1bd693..3ebb305afbf 100644
--- a/spec/frontend/content_editor/components/toolbar_text_style_dropdown_spec.js
+++ b/spec/frontend/content_editor/components/toolbar_text_style_dropdown_spec.js
@@ -53,7 +53,7 @@ describe('content_editor/components/toolbar_text_style_dropdown', () => {
});
});
- describe('when there is an active item ', () => {
+ describe('when there is an active item', () => {
let activeTextStyle;
beforeEach(async () => {
@@ -68,7 +68,7 @@ describe('content_editor/components/toolbar_text_style_dropdown', () => {
await emitEditorEvent({ event: 'transaction', tiptapEditor });
});
- it('displays the active text style label as the dropdown toggle text ', () => {
+ it('displays the active text style label as the dropdown toggle text', () => {
expect(findDropdown().props().text).toBe(activeTextStyle.label);
});
diff --git a/spec/frontend/content_editor/components/wrappers/code_block_spec.js b/spec/frontend/content_editor/components/wrappers/code_block_spec.js
index 17a365e12bb..a5ef19fb8e8 100644
--- a/spec/frontend/content_editor/components/wrappers/code_block_spec.js
+++ b/spec/frontend/content_editor/components/wrappers/code_block_spec.js
@@ -104,7 +104,7 @@ describe('content/components/wrappers/code_block', () => {
it('does not render a preview if showPreview: false', async () => {
createWrapper({ language: 'plantuml', isDiagram: true, showPreview: false });
- expect(wrapper.find({ ref: 'diagramContainer' }).exists()).toBe(false);
+ expect(wrapper.findComponent({ ref: 'diagramContainer' }).exists()).toBe(false);
});
it('does not update preview when diagram is not active', async () => {
@@ -134,7 +134,7 @@ describe('content/components/wrappers/code_block', () => {
await nextTick();
expect(wrapper.find('img').attributes('src')).toBe('url/to/some/diagram');
- expect(wrapper.find(SandboxedMermaid).exists()).toBe(false);
+ expect(wrapper.findComponent(SandboxedMermaid).exists()).toBe(false);
});
it('renders an iframe with preview for a mermaid diagram', async () => {
@@ -143,7 +143,7 @@ describe('content/components/wrappers/code_block', () => {
await emitEditorEvent({ event: 'transaction', tiptapEditor });
await nextTick();
- expect(wrapper.find(SandboxedMermaid).props('source')).toBe('');
+ expect(wrapper.findComponent(SandboxedMermaid).props('source')).toBe('');
expect(wrapper.find('img').exists()).toBe(false);
});
});
diff --git a/spec/frontend/content_editor/extensions/paste_markdown_spec.js b/spec/frontend/content_editor/extensions/paste_markdown_spec.js
index 53efda6aee2..30e798e8817 100644
--- a/spec/frontend/content_editor/extensions/paste_markdown_spec.js
+++ b/spec/frontend/content_editor/extensions/paste_markdown_spec.js
@@ -5,12 +5,7 @@ import Frontmatter from '~/content_editor/extensions/frontmatter';
import Bold from '~/content_editor/extensions/bold';
import { VARIANT_DANGER } from '~/flash';
import eventHubFactory from '~/helpers/event_hub_factory';
-import {
- ALERT_EVENT,
- LOADING_CONTENT_EVENT,
- LOADING_SUCCESS_EVENT,
- LOADING_ERROR_EVENT,
-} from '~/content_editor/constants';
+import { ALERT_EVENT } from '~/content_editor/constants';
import waitForPromises from 'helpers/wait_for_promises';
import { createTestEditor, createDocBuilder, waitUntilNextDocTransaction } from '../test_utils';
@@ -115,13 +110,6 @@ describe('content_editor/extensions/paste_markdown', () => {
expect(tiptapEditor.state.doc.toJSON()).toEqual(expectedDoc.toJSON());
});
-
- it(`triggers ${LOADING_SUCCESS_EVENT}`, async () => {
- await triggerPasteEventHandlerAndWaitForTransaction(buildClipboardEvent());
-
- expect(eventHub.$emit).toHaveBeenCalledWith(LOADING_CONTENT_EVENT);
- expect(eventHub.$emit).toHaveBeenCalledWith(LOADING_SUCCESS_EVENT);
- });
});
describe('when rendering markdown fails', () => {
@@ -129,13 +117,6 @@ describe('content_editor/extensions/paste_markdown', () => {
renderMarkdown.mockRejectedValueOnce();
});
- it(`triggers ${LOADING_ERROR_EVENT} event`, async () => {
- await triggerPasteEventHandler(buildClipboardEvent());
- await waitForPromises();
-
- expect(eventHub.$emit).toHaveBeenCalledWith(LOADING_ERROR_EVENT);
- });
-
it(`triggers ${ALERT_EVENT} event`, async () => {
await triggerPasteEventHandler(buildClipboardEvent());
await waitForPromises();
diff --git a/spec/frontend/content_editor/remark_markdown_processing_spec.js b/spec/frontend/content_editor/remark_markdown_processing_spec.js
index 7ae0a7c13c1..bc43af9bd8b 100644
--- a/spec/frontend/content_editor/remark_markdown_processing_spec.js
+++ b/spec/frontend/content_editor/remark_markdown_processing_spec.js
@@ -1,8 +1,10 @@
+import Audio from '~/content_editor/extensions/audio';
import Bold from '~/content_editor/extensions/bold';
import Blockquote from '~/content_editor/extensions/blockquote';
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 Diagram from '~/content_editor/extensions/diagram';
import FootnoteDefinition from '~/content_editor/extensions/footnote_definition';
import FootnoteReference from '~/content_editor/extensions/footnote_reference';
import Frontmatter from '~/content_editor/extensions/frontmatter';
@@ -21,22 +23,27 @@ import Sourcemap from '~/content_editor/extensions/sourcemap';
import Strike from '~/content_editor/extensions/strike';
import Table from '~/content_editor/extensions/table';
import TableHeader from '~/content_editor/extensions/table_header';
+import TableOfContents from '~/content_editor/extensions/table_of_contents';
import TableRow from '~/content_editor/extensions/table_row';
import TableCell from '~/content_editor/extensions/table_cell';
import TaskList from '~/content_editor/extensions/task_list';
import TaskItem from '~/content_editor/extensions/task_item';
+import Video from '~/content_editor/extensions/video';
import remarkMarkdownDeserializer from '~/content_editor/services/remark_markdown_deserializer';
import markdownSerializer from '~/content_editor/services/markdown_serializer';
+import { SAFE_VIDEO_EXT, SAFE_AUDIO_EXT, DIAGRAM_LANGUAGES } from '~/content_editor/constants';
import { createTestEditor, createDocBuilder } from './test_utils';
const tiptapEditor = createTestEditor({
extensions: [
+ Audio,
Blockquote,
Bold,
BulletList,
Code,
CodeBlockHighlight,
+ Diagram,
FootnoteDefinition,
FootnoteReference,
Frontmatter,
@@ -55,8 +62,10 @@ const tiptapEditor = createTestEditor({
TableRow,
TableHeader,
TableCell,
+ TableOfContents,
TaskList,
TaskItem,
+ Video,
...HTMLNodes,
],
});
@@ -65,12 +74,14 @@ const {
builders: {
doc,
paragraph,
+ audio,
bold,
blockquote,
bulletList,
code,
codeBlock,
div,
+ diagram,
footnoteDefinition,
footnoteReference,
frontmatter,
@@ -89,17 +100,21 @@ const {
tableRow,
tableHeader,
tableCell,
+ tableOfContents,
taskItem,
taskList,
+ video,
},
} = createDocBuilder({
tiptapEditor,
names: {
+ audio: { nodeType: Audio.name },
blockquote: { nodeType: Blockquote.name },
bold: { markType: Bold.name },
bulletList: { nodeType: BulletList.name },
code: { markType: Code.name },
codeBlock: { nodeType: CodeBlockHighlight.name },
+ diagram: { nodeType: Diagram.name },
footnoteDefinition: { nodeType: FootnoteDefinition.name },
footnoteReference: { nodeType: FootnoteReference.name },
frontmatter: { nodeType: Frontmatter.name },
@@ -118,8 +133,10 @@ const {
tableCell: { nodeType: TableCell.name },
tableHeader: { nodeType: TableHeader.name },
tableRow: { nodeType: TableRow.name },
+ tableOfContents: { nodeType: TableOfContents.name },
taskItem: { nodeType: TaskItem.name },
taskList: { nodeType: TaskList.name },
+ video: { nodeType: Video.name },
...HTMLNodes.reduce(
(builders, htmlNode) => ({
...builders,
@@ -1233,6 +1250,62 @@ title: 'layout'
),
),
},
+ ...SAFE_AUDIO_EXT.map((extension) => {
+ const src = `http://test.host/video.${extension}`;
+ const markdown = `![audio](${src})`;
+
+ return {
+ markdown,
+ expectedDoc: doc(
+ paragraph(
+ source(markdown),
+ audio({
+ ...source(markdown),
+ canonicalSrc: src,
+ src,
+ alt: 'audio',
+ }),
+ ),
+ ),
+ };
+ }),
+ ...SAFE_VIDEO_EXT.map((extension) => {
+ const src = `http://test.host/video.${extension}`;
+ const markdown = `![video](${src})`;
+
+ return {
+ markdown,
+ expectedDoc: doc(
+ paragraph(
+ source(markdown),
+ video({
+ ...source(markdown),
+ canonicalSrc: src,
+ src,
+ alt: 'video',
+ }),
+ ),
+ ),
+ };
+ }),
+ ...DIAGRAM_LANGUAGES.map((language) => {
+ const markdown = `\`\`\`${language}
+content
+\`\`\``;
+
+ return {
+ markdown,
+ expectedDoc: doc(diagram({ ...source(markdown), language }, 'content')),
+ };
+ }),
+ {
+ markdown: '[[_TOC_]]',
+ expectedDoc: doc(tableOfContents(source('[[_TOC_]]'))),
+ },
+ {
+ markdown: '[TOC]',
+ expectedDoc: doc(tableOfContents(source('[TOC]'))),
+ },
];
const runOnly = examples.find((example) => example.only === true);
diff --git a/spec/frontend/content_editor/render_html_and_json_for_all_examples.js b/spec/frontend/content_editor/render_html_and_json_for_all_examples.js
index 4a57c7b1942..bd48b7fdd23 100644
--- a/spec/frontend/content_editor/render_html_and_json_for_all_examples.js
+++ b/spec/frontend/content_editor/render_html_and_json_for_all_examples.js
@@ -1,6 +1,7 @@
import { DOMSerializer } from 'prosemirror-model';
// TODO: DRY up duplication with spec/frontend/content_editor/services/markdown_serializer_spec.js
// See https://gitlab.com/groups/gitlab-org/-/epics/7719#plan
+import Audio from '~/content_editor/extensions/audio';
import Blockquote from '~/content_editor/extensions/blockquote';
import Bold from '~/content_editor/extensions/bold';
import BulletList from '~/content_editor/extensions/bullet_list';
@@ -33,13 +34,16 @@ import Table from '~/content_editor/extensions/table';
import TableCell from '~/content_editor/extensions/table_cell';
import TableHeader from '~/content_editor/extensions/table_header';
import TableRow from '~/content_editor/extensions/table_row';
+import TableOfContents from '~/content_editor/extensions/table_of_contents';
import TaskItem from '~/content_editor/extensions/task_item';
import TaskList from '~/content_editor/extensions/task_list';
+import Video from '~/content_editor/extensions/video';
import createMarkdownDeserializer from '~/content_editor/services/remark_markdown_deserializer';
import { createTestEditor } from 'jest/content_editor/test_utils';
const tiptapEditor = createTestEditor({
extensions: [
+ Audio,
Blockquote,
Bold,
BulletList,
@@ -72,8 +76,10 @@ const tiptapEditor = createTestEditor({
TableCell,
TableHeader,
TableRow,
+ TableOfContents,
TaskItem,
TaskList,
+ Video,
],
});
diff --git a/spec/frontend/content_editor/services/content_editor_spec.js b/spec/frontend/content_editor/services/content_editor_spec.js
index a3553e612ca..6175cbdd3d4 100644
--- a/spec/frontend/content_editor/services/content_editor_spec.js
+++ b/spec/frontend/content_editor/services/content_editor_spec.js
@@ -1,8 +1,3 @@
-import {
- LOADING_CONTENT_EVENT,
- LOADING_SUCCESS_EVENT,
- LOADING_ERROR_EVENT,
-} from '~/content_editor/constants';
import { ContentEditor } from '~/content_editor/services/content_editor';
import eventHubFactory from '~/helpers/event_hub_factory';
import { createTestEditor, createDocBuilder } from '../test_utils';
@@ -14,6 +9,7 @@ describe('content_editor/services/content_editor', () => {
let eventHub;
let doc;
let p;
+ const testMarkdown = '**bold text**';
beforeEach(() => {
const tiptapEditor = createTestEditor();
@@ -36,6 +32,9 @@ describe('content_editor/services/content_editor', () => {
});
});
+ const testDoc = () => doc(p('document'));
+ const testEmptyDoc = () => doc();
+
describe('.dispose', () => {
it('destroys the tiptapEditor', () => {
expect(contentEditor.tiptapEditor.destroy).not.toHaveBeenCalled();
@@ -46,51 +45,77 @@ describe('content_editor/services/content_editor', () => {
});
});
- describe('when setSerializedContent succeeds', () => {
- let document;
- const languages = ['javascript'];
- const testMarkdown = '**bold text**';
+ describe('empty', () => {
+ it('returns true when tiptapEditor is empty', async () => {
+ deserializer.deserialize.mockResolvedValueOnce({ document: testEmptyDoc() });
+
+ await contentEditor.setSerializedContent(testMarkdown);
- beforeEach(() => {
- document = doc(p('document'));
- deserializer.deserialize.mockResolvedValueOnce({ document, languages });
+ expect(contentEditor.empty).toBe(true);
});
- it('emits loadingContent and loadingSuccess event in the eventHub', () => {
- let loadingContentEmitted = false;
+ it('returns false when tiptapEditor is not empty', async () => {
+ deserializer.deserialize.mockResolvedValueOnce({ document: testDoc() });
- eventHub.$on(LOADING_CONTENT_EVENT, () => {
- loadingContentEmitted = true;
- });
- eventHub.$on(LOADING_SUCCESS_EVENT, () => {
- expect(loadingContentEmitted).toBe(true);
- });
+ await contentEditor.setSerializedContent(testMarkdown);
- contentEditor.setSerializedContent(testMarkdown);
+ expect(contentEditor.empty).toBe(false);
});
+ });
- it('sets the deserialized document in the tiptap editor object', async () => {
- await contentEditor.setSerializedContent(testMarkdown);
+ describe('editable', () => {
+ it('returns true when tiptapEditor is editable', async () => {
+ contentEditor.setEditable(true);
- expect(contentEditor.tiptapEditor.state.doc.toJSON()).toEqual(document.toJSON());
+ expect(contentEditor.editable).toBe(true);
+ });
+
+ it('returns false when tiptapEditor is readonly', async () => {
+ contentEditor.setEditable(false);
+
+ expect(contentEditor.editable).toBe(false);
});
});
- describe('when setSerializedContent fails', () => {
- const error = 'error';
+ describe('changed', () => {
+ it('returns true when the initial document changes', async () => {
+ deserializer.deserialize.mockResolvedValueOnce({ document: testDoc() });
+
+ await contentEditor.setSerializedContent(testMarkdown);
+
+ contentEditor.tiptapEditor.commands.insertContent(' new content');
+
+ expect(contentEditor.changed).toBe(true);
+ });
+
+ it('returns false when the initial document hasn’t changed', async () => {
+ deserializer.deserialize.mockResolvedValueOnce({ document: testDoc() });
+
+ await contentEditor.setSerializedContent(testMarkdown);
+
+ expect(contentEditor.changed).toBe(false);
+ });
+
+ it('returns false when an initial document is not set and the document is empty', () => {
+ expect(contentEditor.changed).toBe(false);
+ });
- beforeEach(() => {
- deserializer.deserialize.mockRejectedValueOnce(error);
+ it('returns true when an initial document is not set and the document is not empty', () => {
+ contentEditor.tiptapEditor.commands.insertContent('new content');
+
+ expect(contentEditor.changed).toBe(true);
});
+ });
+
+ describe('when setSerializedContent succeeds', () => {
+ it('sets the deserialized document in the tiptap editor object', async () => {
+ const document = testDoc();
+
+ deserializer.deserialize.mockResolvedValueOnce({ document });
- it('emits loadingError event', async () => {
- eventHub.$on(LOADING_ERROR_EVENT, (e) => {
- expect(e).toBe('error');
- });
+ await contentEditor.setSerializedContent(testMarkdown);
- await expect(() => contentEditor.setSerializedContent('**bold text**')).rejects.toEqual(
- error,
- );
+ expect(contentEditor.tiptapEditor.state.doc.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 0e5281be9bf..56394c85e8b 100644
--- a/spec/frontend/content_editor/services/markdown_serializer_spec.js
+++ b/spec/frontend/content_editor/services/markdown_serializer_spec.js
@@ -1,3 +1,4 @@
+import Audio from '~/content_editor/extensions/audio';
import Blockquote from '~/content_editor/extensions/blockquote';
import Bold from '~/content_editor/extensions/bold';
import BulletList from '~/content_editor/extensions/bullet_list';
@@ -33,6 +34,7 @@ import TableHeader from '~/content_editor/extensions/table_header';
import TableRow from '~/content_editor/extensions/table_row';
import TaskItem from '~/content_editor/extensions/task_item';
import TaskList from '~/content_editor/extensions/task_list';
+import Video from '~/content_editor/extensions/video';
import markdownSerializer from '~/content_editor/services/markdown_serializer';
import remarkMarkdownDeserializer from '~/content_editor/services/remark_markdown_deserializer';
import { createTestEditor, createDocBuilder } from '../test_utils';
@@ -41,6 +43,7 @@ jest.mock('~/emoji');
const tiptapEditor = createTestEditor({
extensions: [
+ Audio,
Blockquote,
Bold,
BulletList,
@@ -73,6 +76,7 @@ const tiptapEditor = createTestEditor({
TableRow,
TaskItem,
TaskList,
+ Video,
...HTMLMarks,
...HTMLNodes,
],
@@ -80,6 +84,7 @@ const tiptapEditor = createTestEditor({
const {
builders: {
+ audio,
doc,
blockquote,
bold,
@@ -114,6 +119,7 @@ const {
tableRow,
taskItem,
taskList,
+ video,
},
} = createDocBuilder({
tiptapEditor,
@@ -1230,6 +1236,21 @@ paragraph
);
});
+ it('serializes audio and video elements', () => {
+ expect(
+ serialize(
+ paragraph(
+ audio({ alt: 'audio', canonicalSrc: 'audio.mp3' }),
+ ' and ',
+ video({ alt: 'video', canonicalSrc: 'video.mov' }),
+ ),
+ ),
+ ).toBe(
+ `
+![audio](audio.mp3) and ![video](video.mov)`.trimLeft(),
+ );
+ });
+
const defaultEditAction = (initialContent) => {
tiptapEditor.chain().setContent(initialContent.toJSON()).insertContent(' modified').run();
};
diff --git a/spec/frontend/crm/form_spec.js b/spec/frontend/crm/form_spec.js
index f0e9150cada..57e28b396cf 100644
--- a/spec/frontend/crm/form_spec.js
+++ b/spec/frontend/crm/form_spec.js
@@ -298,7 +298,7 @@ describe('Reusable form component', () => {
`(
'should render the correct component for #$id with the value "$value"',
({ index, id, component, value }) => {
- const findFormElement = () => findFormGroup(index).find(component);
+ const findFormElement = () => findFormGroup(index).findComponent(component);
expect(findFormElement().attributes('id')).toBe(id);
expect(findFormElement().attributes('value')).toBe(value);
@@ -307,7 +307,8 @@ describe('Reusable form component', () => {
it('should render a checked GlFormCheckbox for #active', () => {
const activeCheckboxIndex = 6;
- const findFormElement = () => findFormGroup(activeCheckboxIndex).find(GlFormCheckbox);
+ const findFormElement = () =>
+ findFormGroup(activeCheckboxIndex).findComponent(GlFormCheckbox);
expect(findFormElement().attributes('id')).toBe('active');
expect(findFormElement().attributes('checked')).toBe('true');
diff --git a/spec/frontend/crm/mock_data.js b/spec/frontend/crm/mock_data.js
index a2e2e88ac60..a19ee01c2a5 100644
--- a/spec/frontend/crm/mock_data.js
+++ b/spec/frontend/crm/mock_data.js
@@ -102,6 +102,13 @@ export const getGroupOrganizationsQueryResponse = {
active: true,
},
],
+ pageInfo: {
+ __typename: 'PageInfo',
+ hasNextPage: false,
+ endCursor: 'eyJsYXN0X25hbWUiOiJMZWRuZXIiLCJpZCI6IjE3OSJ9',
+ hasPreviousPage: false,
+ startCursor: 'eyJsYXN0X25hbWUiOiJCYXJ0b24iLCJpZCI6IjE5MyJ9',
+ },
},
},
},
@@ -155,6 +162,21 @@ export const updateContactMutationResponse = {
},
};
+export const getGroupOrganizationsCountQueryResponse = {
+ data: {
+ group: {
+ __typename: 'Group',
+ id: 'gid://gitlab/Group/26',
+ organizationStateCounts: {
+ all: 24,
+ active: 21,
+ inactive: 3,
+ __typename: 'OrganizationStateCountsType',
+ },
+ },
+ },
+};
+
export const updateContactMutationErrorResponse = {
data: {
customerRelationsContactUpdate: {
diff --git a/spec/frontend/crm/organizations_root_spec.js b/spec/frontend/crm/organizations_root_spec.js
index 1780a5945a6..a0b56596177 100644
--- a/spec/frontend/crm/organizations_root_spec.js
+++ b/spec/frontend/crm/organizations_root_spec.js
@@ -1,14 +1,19 @@
-import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
+import { GlLoadingIcon } from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import VueRouter from 'vue-router';
-import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import OrganizationsRoot from '~/crm/organizations/components/organizations_root.vue';
import routes from '~/crm/organizations/routes';
import getGroupOrganizationsQuery from '~/crm/organizations/components/graphql/get_group_organizations.query.graphql';
-import { getGroupOrganizationsQueryResponse } from './mock_data';
+import getGroupOrganizationsCountByStateQuery from '~/crm/organizations/components/graphql/get_group_organizations_count_by_state.query.graphql';
+import PaginatedTableWithSearchAndTabs from '~/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue';
+import {
+ getGroupOrganizationsQueryResponse,
+ getGroupOrganizationsCountQueryResponse,
+} from './mock_data';
describe('Customer relations organizations root app', () => {
Vue.use(VueApollo);
@@ -21,23 +26,31 @@ describe('Customer relations organizations root app', () => {
const findRowByName = (rowName) => wrapper.findAllByRole('row', { name: rowName });
const findIssuesLinks = () => wrapper.findAllByTestId('issues-link');
const findNewOrganizationButton = () => wrapper.findByTestId('new-organization-button');
- const findError = () => wrapper.findComponent(GlAlert);
+ const findTable = () => wrapper.findComponent(PaginatedTableWithSearchAndTabs);
const successQueryHandler = jest.fn().mockResolvedValue(getGroupOrganizationsQueryResponse);
+ const successCountQueryHandler = jest
+ .fn()
+ .mockResolvedValue(getGroupOrganizationsCountQueryResponse);
const basePath = '/groups/flightjs/-/crm/organizations';
const mountComponent = ({
queryHandler = successQueryHandler,
- mountFunction = shallowMountExtended,
+ countQueryHandler = successCountQueryHandler,
canAdminCrmOrganization = true,
+ textQuery = null,
} = {}) => {
- fakeApollo = createMockApollo([[getGroupOrganizationsQuery, queryHandler]]);
- wrapper = mountFunction(OrganizationsRoot, {
+ fakeApollo = createMockApollo([
+ [getGroupOrganizationsQuery, queryHandler],
+ [getGroupOrganizationsCountByStateQuery, countQueryHandler],
+ ]);
+ wrapper = mountExtended(OrganizationsRoot, {
router,
provide: {
canAdminCrmOrganization,
groupFullPath: 'flightjs',
groupIssuesPath: '/issues',
+ textQuery,
},
apolloProvider: fakeApollo,
});
@@ -57,9 +70,33 @@ describe('Customer relations organizations root app', () => {
router = null;
});
- it('should render loading spinner', () => {
+ it('should render table with default props and loading spinner', () => {
mountComponent();
+ expect(findTable().props()).toMatchObject({
+ items: [],
+ itemsCount: {},
+ pageInfo: {},
+ statusTabs: [
+ { title: 'Active', status: 'ACTIVE', filters: 'active' },
+ { title: 'Inactive', status: 'INACTIVE', filters: 'inactive' },
+ { title: 'All', status: 'ALL', filters: 'all' },
+ ],
+ showItems: true,
+ showErrorMsg: false,
+ trackViewsOptions: { category: 'Customer Relations', action: 'view_organizations_list' },
+ i18n: {
+ emptyText: 'No organizations found',
+ issuesButtonLabel: 'View issues',
+ editButtonLabel: 'Edit',
+ title: 'Customer relations organizations',
+ newOrganization: 'New organization',
+ errorText: 'Something went wrong. Please try again.',
+ },
+ serverErrorMessage: '',
+ filterSearchKey: 'organizations',
+ filterSearchTokens: [],
+ });
expect(findLoadingIcon().exists()).toBe(true);
});
@@ -77,11 +114,25 @@ describe('Customer relations organizations root app', () => {
});
});
- it('should render error message on reject', async () => {
- mountComponent({ queryHandler: jest.fn().mockRejectedValue('ERROR') });
- await waitForPromises();
+ describe('error', () => {
+ it('should render on reject', async () => {
+ mountComponent({ queryHandler: jest.fn().mockRejectedValue('ERROR') });
+ await waitForPromises();
+
+ expect(wrapper.text()).toContain('Something went wrong. Please try again.');
+ });
+
+ it('should be removed on error-alert-dismissed event', async () => {
+ mountComponent({ queryHandler: jest.fn().mockRejectedValue('ERROR') });
+ await waitForPromises();
- expect(findError().exists()).toBe(true);
+ expect(wrapper.text()).toContain('Something went wrong. Please try again.');
+
+ findTable().vm.$emit('error-alert-dismissed');
+ await waitForPromises();
+
+ expect(wrapper.text()).not.toContain('Something went wrong. Please try again.');
+ });
});
describe('on successful load', () => {
@@ -89,20 +140,27 @@ describe('Customer relations organizations root app', () => {
mountComponent();
await waitForPromises();
- expect(findError().exists()).toBe(false);
+ expect(wrapper.text()).not.toContain('Something went wrong. Please try again.');
});
it('renders correct results', async () => {
- mountComponent({ mountFunction: mountExtended });
+ mountComponent();
await waitForPromises();
expect(findRowByName(/Test Inc/i)).toHaveLength(1);
expect(findRowByName(/VIP/i)).toHaveLength(1);
expect(findRowByName(/120/i)).toHaveLength(1);
- const issueLink = findIssuesLinks().at(0);
- expect(issueLink.exists()).toBe(true);
- expect(issueLink.attributes('href')).toBe('/issues?crm_organization_id=2');
+ expect(findIssuesLinks()).toHaveLength(3);
+
+ const links = findIssuesLinks().wrappers.map((w) => w.attributes('href'));
+ expect(links).toEqual(
+ expect.arrayContaining([
+ '/issues?crm_organization_id=1',
+ '/issues?crm_organization_id=2',
+ '/issues?crm_organization_id=3',
+ ]),
+ );
});
});
});
diff --git a/spec/frontend/cycle_analytics/__snapshots__/total_time_spec.js.snap b/spec/frontend/cycle_analytics/__snapshots__/total_time_spec.js.snap
index 7f211c1028e..92927ef16ec 100644
--- a/spec/frontend/cycle_analytics/__snapshots__/total_time_spec.js.snap
+++ b/spec/frontend/cycle_analytics/__snapshots__/total_time_spec.js.snap
@@ -1,28 +1,28 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`TotalTime with a blank object should render -- 1`] = `"<span class=\\"total-time\\"> -- </span>"`;
+exports[`TotalTime with a blank object should render -- 1`] = `"<span> -- </span>"`;
exports[`TotalTime with a valid time object with {"days": 3, "mins": 47, "seconds": 3} 1`] = `
-"<span class=\\"total-time\\">
+"<span>
3 <span>days</span></span>"
`;
exports[`TotalTime with a valid time object with {"hours": 7, "mins": 20, "seconds": 10} 1`] = `
-"<span class=\\"total-time\\">
+"<span>
7 <span>hrs</span></span>"
`;
exports[`TotalTime with a valid time object with {"hours": 23, "mins": 10} 1`] = `
-"<span class=\\"total-time\\">
+"<span>
23 <span>hrs</span></span>"
`;
exports[`TotalTime with a valid time object with {"mins": 47, "seconds": 3} 1`] = `
-"<span class=\\"total-time\\">
+"<span>
47 <span>mins</span></span>"
`;
exports[`TotalTime with a valid time object with {"seconds": 35} 1`] = `
-"<span class=\\"total-time\\">
+"<span>
35 <span>s</span></span>"
`;
diff --git a/spec/frontend/cycle_analytics/base_spec.js b/spec/frontend/cycle_analytics/base_spec.js
index ea3da86c7b2..013bea671a8 100644
--- a/spec/frontend/cycle_analytics/base_spec.js
+++ b/spec/frontend/cycle_analytics/base_spec.js
@@ -201,7 +201,7 @@ describe('Value stream analytics component', () => {
it('renders the stage table with a loading icon', () => {
const tableWrapper = findStageTable();
expect(tableWrapper.exists()).toBe(true);
- expect(tableWrapper.find(GlLoadingIcon).exists()).toBe(true);
+ expect(tableWrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
it('renders the path navigation loading state', () => {
diff --git a/spec/frontend/cycle_analytics/path_navigation_spec.js b/spec/frontend/cycle_analytics/path_navigation_spec.js
index fa9eadbd071..fec1526359c 100644
--- a/spec/frontend/cycle_analytics/path_navigation_spec.js
+++ b/spec/frontend/cycle_analytics/path_navigation_spec.js
@@ -56,7 +56,9 @@ describe('Project PathNavigation', () => {
describe('displays correctly', () => {
it('has the correct props', () => {
- expect(wrapper.find(GlPath).props('items')).toMatchObject(transformedProjectStagePathData);
+ expect(wrapper.findComponent(GlPath).props('items')).toMatchObject(
+ transformedProjectStagePathData,
+ );
});
it('contains all the expected stages', () => {
@@ -69,11 +71,11 @@ describe('Project PathNavigation', () => {
describe('loading', () => {
describe('is false', () => {
it('displays the gl-path component', () => {
- expect(wrapper.find(GlPath).exists()).toBe(true);
+ expect(wrapper.findComponent(GlPath).exists()).toBe(true);
});
it('hides the gl-skeleton-loading component', () => {
- expect(wrapper.find(GlSkeletonLoader).exists()).toBe(false);
+ expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(false);
});
it('renders each stage', () => {
@@ -112,11 +114,11 @@ describe('Project PathNavigation', () => {
});
it('hides the gl-path component', () => {
- expect(wrapper.find(GlPath).exists()).toBe(false);
+ expect(wrapper.findComponent(GlPath).exists()).toBe(false);
});
it('displays the gl-skeleton-loading component', () => {
- expect(wrapper.find(GlSkeletonLoader).exists()).toBe(true);
+ expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(true);
});
});
});
diff --git a/spec/frontend/cycle_analytics/value_stream_metrics_spec.js b/spec/frontend/cycle_analytics/value_stream_metrics_spec.js
index 23e41f35b00..9c8cd6a3dbc 100644
--- a/spec/frontend/cycle_analytics/value_stream_metrics_spec.js
+++ b/spec/frontend/cycle_analytics/value_stream_metrics_spec.js
@@ -176,7 +176,7 @@ describe('ValueStreamMetrics', () => {
await waitForPromises();
});
- it('it should render an error message', () => {
+ it('should render an error message', () => {
expect(createFlash).toHaveBeenCalledWith({
message: `There was an error while fetching value stream analytics ${fakeReqName} data.`,
});
diff --git a/spec/frontend/deploy_freeze/components/deploy_freeze_modal_spec.js b/spec/frontend/deploy_freeze/components/deploy_freeze_modal_spec.js
index 7c46c280d46..bbafdc000db 100644
--- a/spec/frontend/deploy_freeze/components/deploy_freeze_modal_spec.js
+++ b/spec/frontend/deploy_freeze/components/deploy_freeze_modal_spec.js
@@ -42,7 +42,7 @@ describe('Deploy freeze modal', () => {
wrapper.find('#deploy-freeze-start').trigger('input');
wrapper.find('#deploy-freeze-end').trigger('input');
- wrapper.find(TimezoneDropdown).trigger('input');
+ wrapper.findComponent(TimezoneDropdown).trigger('input');
};
afterEach(() => {
diff --git a/spec/frontend/deploy_freeze/components/deploy_freeze_settings_spec.js b/spec/frontend/deploy_freeze/components/deploy_freeze_settings_spec.js
index cc044800e5e..637efe30022 100644
--- a/spec/frontend/deploy_freeze/components/deploy_freeze_settings_spec.js
+++ b/spec/frontend/deploy_freeze/components/deploy_freeze_settings_spec.js
@@ -31,11 +31,11 @@ describe('Deploy freeze settings', () => {
describe('Deploy freeze table contains components', () => {
it('contains deploy freeze table', () => {
- expect(wrapper.find(DeployFreezeTable).exists()).toBe(true);
+ expect(wrapper.findComponent(DeployFreezeTable).exists()).toBe(true);
});
it('contains deploy freeze modal', () => {
- expect(wrapper.find(DeployFreezeModal).exists()).toBe(true);
+ expect(wrapper.findComponent(DeployFreezeModal).exists()).toBe(true);
});
});
});
diff --git a/spec/frontend/deploy_freeze/components/timezone_dropdown_spec.js b/spec/frontend/deploy_freeze/components/timezone_dropdown_spec.js
index aea81daecef..567d18f8b92 100644
--- a/spec/frontend/deploy_freeze/components/timezone_dropdown_spec.js
+++ b/spec/frontend/deploy_freeze/components/timezone_dropdown_spec.js
@@ -30,8 +30,8 @@ describe('Deploy freeze timezone dropdown', () => {
wrapper.setData({ searchTerm });
};
- const findAllDropdownItems = () => wrapper.findAll(GlDropdownItem);
- const findDropdownItemByIndex = (index) => wrapper.findAll(GlDropdownItem).at(index);
+ const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
+ const findDropdownItemByIndex = (index) => wrapper.findAllComponents(GlDropdownItem).at(index);
afterEach(() => {
wrapper.destroy();
@@ -96,7 +96,7 @@ describe('Deploy freeze timezone dropdown', () => {
});
it('renders selected time zone as dropdown label', () => {
- expect(wrapper.find(GlDropdown).vm.text).toBe('Alaska');
+ expect(wrapper.findComponent(GlDropdown).vm.text).toBe('Alaska');
});
});
});
diff --git a/spec/frontend/deprecated_jquery_dropdown_spec.js b/spec/frontend/deprecated_jquery_dropdown_spec.js
index b18d53b317d..4a070395eaf 100644
--- a/spec/frontend/deprecated_jquery_dropdown_spec.js
+++ b/spec/frontend/deprecated_jquery_dropdown_spec.js
@@ -314,7 +314,7 @@ describe('deprecatedJQueryDropdown', () => {
});
describe('with a trackSuggestionsClickedLabel', () => {
- it('it includes data-track attributes', () => {
+ it('includes data-track attributes', () => {
const dropdown = dropdownWithOptions({
trackSuggestionClickedLabel: 'some_value_for_label',
});
@@ -333,7 +333,7 @@ describe('deprecatedJQueryDropdown', () => {
expect(link).toHaveAttr('data-track-property', 'suggestion-category');
});
- it('it defaults property to no_category when category not provided', () => {
+ it('defaults property to no_category when category not provided', () => {
const dropdown = dropdownWithOptions({
trackSuggestionClickedLabel: 'some_value_for_label',
});
diff --git a/spec/frontend/design_management/components/design_notes/design_note_spec.js b/spec/frontend/design_management/components/design_notes/design_note_spec.js
index 28833b4af5c..df511586c10 100644
--- a/spec/frontend/design_management/components/design_notes/design_note_spec.js
+++ b/spec/frontend/design_management/components/design_notes/design_note_spec.js
@@ -43,6 +43,7 @@ describe('Design note component', () => {
wrapper = shallowMountExtended(DesignNote, {
propsData: {
note: {},
+ noteableId: 'gid://gitlab/DesignManagement::Design/6',
...props,
},
data() {
diff --git a/spec/frontend/design_management/components/design_notes/design_reply_form_spec.js b/spec/frontend/design_management/components/design_notes/design_reply_form_spec.js
index f7ce742b933..e36f5c79e3e 100644
--- a/spec/frontend/design_management/components/design_notes/design_reply_form_spec.js
+++ b/spec/frontend/design_management/components/design_notes/design_reply_form_spec.js
@@ -1,5 +1,6 @@
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
+import Autosave from '~/autosave';
import DesignReplyForm from '~/design_management/components/design_notes/design_reply_form.vue';
const showModal = jest.fn();
@@ -13,6 +14,7 @@ const GlModal = {
describe('Design reply form component', () => {
let wrapper;
+ let originalGon;
const findTextarea = () => wrapper.find('textarea');
const findSubmitButton = () => wrapper.findComponent({ ref: 'submitButton' });
@@ -24,6 +26,7 @@ describe('Design reply form component', () => {
propsData: {
value: '',
isSaving: false,
+ noteableId: 'gid://gitlab/DesignManagement::Design/6',
...props,
},
stubs: { GlModal },
@@ -31,8 +34,14 @@ describe('Design reply form component', () => {
});
}
+ beforeEach(() => {
+ originalGon = window.gon;
+ window.gon.current_user_id = 1;
+ });
+
afterEach(() => {
wrapper.destroy();
+ window.gon = originalGon;
});
it('textarea has focus after component mount', () => {
@@ -66,6 +75,25 @@ describe('Design reply form component', () => {
expect(findSubmitButton().html()).toMatchSnapshot();
});
+ it.each`
+ discussionId | shortDiscussionId
+ ${undefined} | ${'new'}
+ ${'gid://gitlab/DiffDiscussion/123'} | ${123}
+ `(
+ 'initializes autosave support on discussion with proper key',
+ async ({ discussionId, shortDiscussionId }) => {
+ createComponent({ discussionId });
+ await nextTick();
+
+ // We discourage testing `wrapper.vm` properties but
+ // since `autosave` library instantiates on component
+ // there's no other way to test whether instantiation
+ // happened correctly or not.
+ expect(wrapper.vm.autosaveDiscussion).toBeInstanceOf(Autosave);
+ expect(wrapper.vm.autosaveDiscussion.key).toBe(`autosave/Discussion/6/${shortDiscussionId}`);
+ },
+ );
+
describe('when form has no text', () => {
beforeEach(() => {
createComponent({
@@ -120,28 +148,37 @@ describe('Design reply form component', () => {
});
it('emits submitForm event on Comment button click', async () => {
+ const autosaveResetSpy = jest.spyOn(wrapper.vm.autosaveDiscussion, 'reset');
+
findSubmitButton().vm.$emit('click');
await nextTick();
expect(wrapper.emitted('submit-form')).toBeTruthy();
+ expect(autosaveResetSpy).toHaveBeenCalled();
});
it('emits submitForm event on textarea ctrl+enter keydown', async () => {
+ const autosaveResetSpy = jest.spyOn(wrapper.vm.autosaveDiscussion, 'reset');
+
findTextarea().trigger('keydown.enter', {
ctrlKey: true,
});
await nextTick();
expect(wrapper.emitted('submit-form')).toBeTruthy();
+ expect(autosaveResetSpy).toHaveBeenCalled();
});
it('emits submitForm event on textarea meta+enter keydown', async () => {
+ const autosaveResetSpy = jest.spyOn(wrapper.vm.autosaveDiscussion, 'reset');
+
findTextarea().trigger('keydown.enter', {
metaKey: true,
});
await nextTick();
expect(wrapper.emitted('submit-form')).toBeTruthy();
+ expect(autosaveResetSpy).toHaveBeenCalled();
});
it('emits input event on changing textarea content', async () => {
@@ -180,10 +217,13 @@ describe('Design reply form component', () => {
});
it('emits cancelForm event on modal Ok button click', () => {
+ const autosaveResetSpy = jest.spyOn(wrapper.vm.autosaveDiscussion, 'reset');
+
findTextarea().trigger('keyup.esc');
findModal().vm.$emit('ok');
expect(wrapper.emitted('cancel-form')).toBeTruthy();
+ expect(autosaveResetSpy).toHaveBeenCalled();
});
});
});
diff --git a/spec/frontend/design_management/components/design_presentation_spec.js b/spec/frontend/design_management/components/design_presentation_spec.js
index 30eddcee86a..4a339899473 100644
--- a/spec/frontend/design_management/components/design_presentation_spec.js
+++ b/spec/frontend/design_management/components/design_presentation_spec.js
@@ -525,7 +525,7 @@ describe('Design management design presentation component', () => {
{ clientX: 10, clientY: 10 },
{ mouseup: true },
).then(() => {
- expect(wrapper.emitted('openCommentForm')).toBeFalsy();
+ expect(wrapper.emitted('openCommentForm')).toBeUndefined();
});
});
diff --git a/spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap b/spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap
index 8fe3e92360a..096d776a7d2 100644
--- a/spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap
+++ b/spec/frontend/design_management/components/list/__snapshots__/item_spec.js.snap
@@ -11,7 +11,7 @@ exports[`Design management list item component when item appears in view after i
exports[`Design management list item component with notes renders item with multiple comments 1`] = `
<router-link-stub
ariacurrentvalue="page"
- class="card gl-cursor-pointer text-plain js-design-list-item design-list-item design-list-item-new"
+ class="card gl-cursor-pointer text-plain js-design-list-item design-list-item design-list-item-new gl-mb-0"
event="click"
tag="a"
to="[object Object]"
@@ -88,7 +88,7 @@ exports[`Design management list item component with notes renders item with mult
exports[`Design management list item component with notes renders item with single comment 1`] = `
<router-link-stub
ariacurrentvalue="page"
- class="card gl-cursor-pointer text-plain js-design-list-item design-list-item design-list-item-new"
+ class="card gl-cursor-pointer text-plain js-design-list-item design-list-item design-list-item-new gl-mb-0"
event="click"
tag="a"
to="[object Object]"
diff --git a/spec/frontend/design_management/components/toolbar/index_spec.js b/spec/frontend/design_management/components/toolbar/index_spec.js
index b6137ba2eee..1776405ece9 100644
--- a/spec/frontend/design_management/components/toolbar/index_spec.js
+++ b/spec/frontend/design_management/components/toolbar/index_spec.js
@@ -107,7 +107,7 @@ describe('Design management toolbar component', () => {
await nextTick();
wrapper.findComponent(DeleteButton).vm.$emit('delete-selected-designs');
- expect(wrapper.emitted().delete).toBeTruthy();
+ expect(wrapper.emitted().delete).toHaveLength(1);
});
it('renders download button with correct link', () => {
diff --git a/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap b/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap
index 9997f02cd01..8cfe11c9040 100644
--- a/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap
+++ b/spec/frontend/design_management/pages/__snapshots__/index_spec.js.snap
@@ -9,9 +9,7 @@ exports[`Design management index page designs renders error 1`] = `
<!---->
- <div
- class="gl-mt-6"
- >
+ <div>
<gl-alert-stub
dismisslabel="Dismiss"
primarybuttonlink=""
@@ -43,9 +41,7 @@ exports[`Design management index page designs renders loading icon 1`] = `
<!---->
- <div
- class="gl-mt-6"
- >
+ <div>
<gl-loading-icon-stub
color="dark"
label="Loading"
diff --git a/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap b/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap
index 3177a5e016c..d86fbf81d20 100644
--- a/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap
+++ b/spec/frontend/design_management/pages/design/__snapshots__/index_spec.js.snap
@@ -64,7 +64,7 @@ exports[`Design management design index page renders design index 1`] = `
<participants-stub
class="gl-mb-4"
lazy="true"
- numberoflessparticipants="7"
+ numberoflessparticipants="8"
participants="[object Object]"
/>
@@ -195,7 +195,7 @@ exports[`Design management design index page with error GlAlert is rendered in c
<participants-stub
class="gl-mb-4"
lazy="true"
- numberoflessparticipants="7"
+ numberoflessparticipants="8"
participants="[object Object]"
/>
diff --git a/spec/frontend/design_management/pages/index_spec.js b/spec/frontend/design_management/pages/index_spec.js
index f90feaadfb0..1033b509419 100644
--- a/spec/frontend/design_management/pages/index_spec.js
+++ b/spec/frontend/design_management/pages/index_spec.js
@@ -254,7 +254,7 @@ describe('Design management index page', () => {
'gl-flex-direction-column',
'col-md-6',
'col-lg-3',
- 'gl-mb-3',
+ 'gl-mt-5',
]);
});
});
diff --git a/spec/frontend/diffs/components/app_spec.js b/spec/frontend/diffs/components/app_spec.js
index 96f2ac1692c..b88206c3b9a 100644
--- a/spec/frontend/diffs/components/app_spec.js
+++ b/spec/frontend/diffs/components/app_spec.js
@@ -30,7 +30,7 @@ const UPDATED_COMMIT_URL = `${TEST_HOST}/COMMIT/NEW`;
Vue.use(Vuex);
function getCollapsedFilesWarning(wrapper) {
- return wrapper.find(CollapsedFilesWarning);
+ return wrapper.findComponent(CollapsedFilesWarning);
}
describe('diffs/components/app', () => {
@@ -167,7 +167,7 @@ describe('diffs/components/app', () => {
state.diffs.isLoading = true;
});
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
it('displays loading icon on batch loading', () => {
@@ -175,13 +175,13 @@ describe('diffs/components/app', () => {
state.diffs.batchLoadingState = 'loading';
});
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
it('displays diffs container when not loading', () => {
createComponent();
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.find('#diffs').exists()).toBe(true);
});
@@ -263,7 +263,7 @@ describe('diffs/components/app', () => {
it('renders empty state when no diff files exist', () => {
createComponent();
- expect(wrapper.find(NoChanges).exists()).toBe(true);
+ expect(wrapper.findComponent(NoChanges).exists()).toBe(true);
});
it('does not render empty state when diff files exist', () => {
@@ -273,8 +273,8 @@ describe('diffs/components/app', () => {
});
});
- expect(wrapper.find(NoChanges).exists()).toBe(false);
- expect(wrapper.findAll(DiffFile).length).toBe(1);
+ expect(wrapper.findComponent(NoChanges).exists()).toBe(false);
+ expect(wrapper.findAllComponents(DiffFile).length).toBe(1);
});
});
@@ -487,8 +487,8 @@ describe('diffs/components/app', () => {
state.diffs.mergeRequestDiff = mergeRequestDiff;
});
- expect(wrapper.find(CompareVersions).exists()).toBe(true);
- expect(wrapper.find(CompareVersions).props()).toEqual(
+ expect(wrapper.findComponent(CompareVersions).exists()).toBe(true);
+ expect(wrapper.findComponent(CompareVersions).props()).toEqual(
expect.objectContaining({
diffFilesCountText: null,
}),
@@ -506,8 +506,8 @@ describe('diffs/components/app', () => {
state.diffs.size = 1;
});
- expect(wrapper.find(HiddenFilesWarning).exists()).toBe(true);
- expect(wrapper.find(HiddenFilesWarning).props()).toEqual(
+ expect(wrapper.findComponent(HiddenFilesWarning).exists()).toBe(true);
+ expect(wrapper.findComponent(HiddenFilesWarning).props()).toEqual(
expect.objectContaining({
total: '5',
plainDiffPath: 'plain diff path',
@@ -547,7 +547,7 @@ describe('diffs/components/app', () => {
};
});
- expect(wrapper.find(CommitWidget).exists()).toBe(true);
+ expect(wrapper.findComponent(CommitWidget).exists()).toBe(true);
});
it('should display diff file if there are diff files', () => {
@@ -555,13 +555,13 @@ describe('diffs/components/app', () => {
state.diffs.diffFiles.push({ sha: '123' });
});
- expect(wrapper.find(DiffFile).exists()).toBe(true);
+ expect(wrapper.findComponent(DiffFile).exists()).toBe(true);
});
it("doesn't render tree list when no changes exist", () => {
createComponent();
- expect(wrapper.find(TreeList).exists()).toBe(false);
+ expect(wrapper.findComponent(TreeList).exists()).toBe(false);
});
it('should render tree list', () => {
@@ -569,7 +569,7 @@ describe('diffs/components/app', () => {
state.diffs.diffFiles = [{ file_hash: '111', file_path: '111.js' }];
});
- expect(wrapper.find(TreeList).exists()).toBe(true);
+ expect(wrapper.findComponent(TreeList).exists()).toBe(true);
});
});
@@ -636,12 +636,12 @@ describe('diffs/components/app', () => {
await nextTick();
- expect(wrapper.findAll(DiffFile).length).toBe(1);
+ expect(wrapper.findAllComponents(DiffFile).length).toBe(1);
});
describe('pagination', () => {
const fileByFileNav = () => wrapper.find('[data-testid="file-by-file-navigation"]');
- const paginator = () => fileByFileNav().find(GlPagination);
+ const paginator = () => fileByFileNav().findComponent(GlPagination);
it('sets previous button as disabled', async () => {
createComponent({ fileByFileUserPreference: true }, ({ state }) => {
@@ -682,7 +682,7 @@ describe('diffs/components/app', () => {
${'123'} | ${2}
${'312'} | ${1}
`(
- 'it calls navigateToDiffFileIndex with $index when $link is clicked',
+ 'calls navigateToDiffFileIndex with $index when $link is clicked',
async ({ currentDiffFileId, targetFile }) => {
createComponent({ fileByFileUserPreference: true }, ({ state }) => {
state.diffs.diffFiles.push({ file_hash: '123' }, { file_hash: '312' });
diff --git a/spec/frontend/diffs/components/collapsed_files_warning_spec.js b/spec/frontend/diffs/components/collapsed_files_warning_spec.js
index cc4f13ab0cf..eca5b536a35 100644
--- a/spec/frontend/diffs/components/collapsed_files_warning_spec.js
+++ b/spec/frontend/diffs/components/collapsed_files_warning_spec.js
@@ -28,8 +28,8 @@ describe('CollapsedFilesWarning', () => {
Vue.use(Vuex);
const getAlertActionButton = () =>
- wrapper.find(CollapsedFilesWarning).find('button.gl-alert-action:first-child');
- const getAlertCloseButton = () => wrapper.find(CollapsedFilesWarning).find('button');
+ wrapper.findComponent(CollapsedFilesWarning).find('button.gl-alert-action:first-child');
+ const getAlertCloseButton = () => wrapper.findComponent(CollapsedFilesWarning).find('button');
const createComponent = (props = {}, { full } = { full: false }) => {
const mounter = full ? mount : shallowMount;
diff --git a/spec/frontend/diffs/components/commit_item_spec.js b/spec/frontend/diffs/components/commit_item_spec.js
index e52c5abbc7b..440f169be86 100644
--- a/spec/frontend/diffs/components/commit_item_spec.js
+++ b/spec/frontend/diffs/components/commit_item_spec.js
@@ -27,7 +27,7 @@ describe('diffs/components/commit_item', () => {
const getAvatarElement = () => wrapper.find('.user-avatar-link');
const getCommitterElement = () => wrapper.find('.committer');
const getCommitActionsElement = () => wrapper.find('.commit-actions');
- const getCommitPipelineStatus = () => wrapper.find(CommitPipelineStatus);
+ const getCommitPipelineStatus = () => wrapper.findComponent(CommitPipelineStatus);
const mountComponent = (propsData) => {
wrapper = mount(Component, {
@@ -111,8 +111,8 @@ describe('diffs/components/commit_item', () => {
const descElement = getDescElement();
const descExpandElement = getDescExpandElement();
- expect(descElement.exists()).toBeFalsy();
- expect(descExpandElement.exists()).toBeFalsy();
+ expect(descElement.exists()).toBe(false);
+ expect(descExpandElement.exists()).toBe(false);
});
});
diff --git a/spec/frontend/diffs/components/commit_widget_spec.js b/spec/frontend/diffs/components/commit_widget_spec.js
index fbff473e4df..f650ead6f83 100644
--- a/spec/frontend/diffs/components/commit_widget_spec.js
+++ b/spec/frontend/diffs/components/commit_widget_spec.js
@@ -12,7 +12,7 @@ describe('diffs/components/commit_widget', () => {
});
it('renders commit item', () => {
- const commitElement = wrapper.find(CommitItem);
+ const commitElement = wrapper.findComponent(CommitItem);
expect(commitElement.exists()).toBe(true);
});
diff --git a/spec/frontend/diffs/components/compare_dropdown_layout_spec.js b/spec/frontend/diffs/components/compare_dropdown_layout_spec.js
index 98f88226742..09128b04caa 100644
--- a/spec/frontend/diffs/components/compare_dropdown_layout_spec.js
+++ b/spec/frontend/diffs/components/compare_dropdown_layout_spec.js
@@ -34,7 +34,7 @@ describe('CompareDropdownLayout', () => {
findListItems().wrappers.map((listItem) => ({
href: listItem.find('a').attributes('href'),
text: trimText(listItem.text()),
- createdAt: listItem.findAll(TimeAgo).wrappers[0]?.props('time'),
+ createdAt: listItem.findAllComponents(TimeAgo).wrappers[0]?.props('time'),
isActive: listItem.classes().includes('is-active'),
}));
diff --git a/spec/frontend/diffs/components/diff_code_quality_spec.js b/spec/frontend/diffs/components/diff_code_quality_spec.js
index 81a817c47dc..b5dce4fc924 100644
--- a/spec/frontend/diffs/components/diff_code_quality_spec.js
+++ b/spec/frontend/diffs/components/diff_code_quality_spec.js
@@ -17,7 +17,6 @@ describe('DiffCodeQuality', () => {
return mountFunction(DiffCodeQuality, {
propsData: {
expandedLines: [],
- line: 1,
codeQuality,
},
});
@@ -28,9 +27,7 @@ describe('DiffCodeQuality', () => {
expect(wrapper.findByTestId('diff-codequality').exists()).toBe(true);
await wrapper.findByTestId('diff-codequality-close').trigger('click');
-
expect(wrapper.emitted('hideCodeQualityFindings').length).toBe(1);
- expect(wrapper.emitted().hideCodeQualityFindings[0][0]).toBe(wrapper.props('line'));
});
it('renders correct amount of list items for codequality array and their description', async () => {
diff --git a/spec/frontend/diffs/components/diff_comment_cell_spec.js b/spec/frontend/diffs/components/diff_comment_cell_spec.js
index b636a178593..2acfc2c6d7e 100644
--- a/spec/frontend/diffs/components/diff_comment_cell_spec.js
+++ b/spec/frontend/diffs/components/diff_comment_cell_spec.js
@@ -20,24 +20,24 @@ describe('DiffCommentCell', () => {
it('renders discussions if line has discussions', () => {
const wrapper = createWrapper({ renderDiscussion: true });
- expect(wrapper.find(DiffDiscussions).exists()).toBe(true);
+ expect(wrapper.findComponent(DiffDiscussions).exists()).toBe(true);
});
it('does not render discussions if line has no discussions', () => {
const wrapper = createWrapper();
- expect(wrapper.find(DiffDiscussions).exists()).toBe(false);
+ expect(wrapper.findComponent(DiffDiscussions).exists()).toBe(false);
});
it('renders discussion reply if line has no draft', () => {
const wrapper = createWrapper();
- expect(wrapper.find(DiffDiscussionReply).exists()).toBe(true);
+ expect(wrapper.findComponent(DiffDiscussionReply).exists()).toBe(true);
});
it('does not render discussion reply if line has draft', () => {
const wrapper = createWrapper({ hasDraft: true });
- expect(wrapper.find(DiffDiscussionReply).exists()).toBe(false);
+ expect(wrapper.findComponent(DiffDiscussionReply).exists()).toBe(false);
});
});
diff --git a/spec/frontend/diffs/components/diff_content_spec.js b/spec/frontend/diffs/components/diff_content_spec.js
index 6844e6e497a..9f593ee0d49 100644
--- a/spec/frontend/diffs/components/diff_content_spec.js
+++ b/spec/frontend/diffs/components/diff_content_spec.js
@@ -110,13 +110,13 @@ describe('DiffContent', () => {
props: { diffFile: textDiffFile },
});
- expect(wrapper.find(DiffView).exists()).toBe(true);
+ expect(wrapper.findComponent(DiffView).exists()).toBe(true);
});
it('renders rendering more lines loading icon', () => {
createComponent({ props: { diffFile: { ...textDiffFile, renderingLines: true } } });
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
});
@@ -133,7 +133,7 @@ describe('DiffContent', () => {
props: { diffFile: { ...emptyDiffFile, viewer: { name: diffViewerModes.no_preview } } },
});
- expect(wrapper.find(NoPreviewViewer).exists()).toBe(true);
+ expect(wrapper.findComponent(NoPreviewViewer).exists()).toBe(true);
});
it('should render not diffable view if viewer set to non_diffable', () => {
@@ -141,7 +141,7 @@ describe('DiffContent', () => {
props: { diffFile: { ...emptyDiffFile, viewer: { name: diffViewerModes.not_diffable } } },
});
- expect(wrapper.find(NotDiffableViewer).exists()).toBe(true);
+ expect(wrapper.findComponent(NotDiffableViewer).exists()).toBe(true);
});
});
@@ -156,7 +156,7 @@ describe('DiffContent', () => {
},
});
- expect(wrapper.find(DiffDiscussions).exists()).toBe(true);
+ expect(wrapper.findComponent(DiffDiscussions).exists()).toBe(true);
});
it('emits saveDiffDiscussion when note-form emits `handleFormUpdate`', () => {
@@ -169,7 +169,7 @@ describe('DiffContent', () => {
},
});
- wrapper.find(NoteForm).vm.$emit('handleFormUpdate', noteStub);
+ wrapper.findComponent(NoteForm).vm.$emit('handleFormUpdate', noteStub);
expect(saveDiffDiscussionMock).toHaveBeenCalledWith(expect.any(Object), {
note: noteStub,
formData: {
diff --git a/spec/frontend/diffs/components/diff_discussion_reply_spec.js b/spec/frontend/diffs/components/diff_discussion_reply_spec.js
index f03c0357a0e..5ccd2002462 100644
--- a/spec/frontend/diffs/components/diff_discussion_reply_spec.js
+++ b/spec/frontend/diffs/components/diff_discussion_reply_spec.js
@@ -64,7 +64,7 @@ describe('DiffDiscussionReply', () => {
hasForm: false,
});
- expect(wrapper.find(ReplyPlaceholder).exists()).toBe(true);
+ expect(wrapper.findComponent(ReplyPlaceholder).exists()).toBe(true);
});
});
@@ -83,6 +83,6 @@ describe('DiffDiscussionReply', () => {
hasForm: false,
});
- expect(wrapper.find(NoteSignedOutWidget).exists()).toBe(true);
+ expect(wrapper.findComponent(NoteSignedOutWidget).exists()).toBe(true);
});
});
diff --git a/spec/frontend/diffs/components/diff_discussions_spec.js b/spec/frontend/diffs/components/diff_discussions_spec.js
index 2da68adddf6..e9a0e0745fd 100644
--- a/spec/frontend/diffs/components/diff_discussions_spec.js
+++ b/spec/frontend/diffs/components/diff_discussions_spec.js
@@ -32,11 +32,11 @@ describe('DiffDiscussions', () => {
it('should have notes list', () => {
createComponent();
- expect(wrapper.find(NoteableDiscussion).exists()).toBe(true);
- expect(wrapper.find(DiscussionNotes).exists()).toBe(true);
- expect(wrapper.find(DiscussionNotes).findAll(TimelineEntryItem).length).toBe(
- discussionsMockData.notes.length,
- );
+ expect(wrapper.findComponent(NoteableDiscussion).exists()).toBe(true);
+ expect(wrapper.findComponent(DiscussionNotes).exists()).toBe(true);
+ expect(
+ wrapper.findComponent(DiscussionNotes).findAllComponents(TimelineEntryItem).length,
+ ).toBe(discussionsMockData.notes.length);
});
});
@@ -48,7 +48,7 @@ describe('DiffDiscussions', () => {
const diffNotesToggle = findDiffNotesToggle();
expect(diffNotesToggle.exists()).toBe(true);
- expect(diffNotesToggle.find(GlIcon).exists()).toBe(true);
+ expect(diffNotesToggle.findComponent(GlIcon).exists()).toBe(true);
expect(diffNotesToggle.classes('diff-notes-collapse')).toBe(true);
});
@@ -80,12 +80,12 @@ describe('DiffDiscussions', () => {
discussions[0].expanded = false;
createComponent({ discussions, shouldCollapseDiscussions: true });
- expect(wrapper.find(NoteableDiscussion).isVisible()).toBe(false);
+ expect(wrapper.findComponent(NoteableDiscussion).isVisible()).toBe(false);
});
it('renders badge on avatar', () => {
createComponent({ renderAvatarBadge: true });
- const noteableDiscussion = wrapper.find(NoteableDiscussion);
+ const noteableDiscussion = wrapper.findComponent(NoteableDiscussion);
expect(noteableDiscussion.find('.design-note-pin').exists()).toBe(true);
expect(noteableDiscussion.find('.design-note-pin').text().trim()).toBe('1');
diff --git a/spec/frontend/diffs/components/diff_file_header_spec.js b/spec/frontend/diffs/components/diff_file_header_spec.js
index 92b8b2d4aa3..c23eb2f3d24 100644
--- a/spec/frontend/diffs/components/diff_file_header_spec.js
+++ b/spec/frontend/diffs/components/diff_file_header_spec.js
@@ -76,18 +76,19 @@ describe('DiffFileHeader component', () => {
wrapper.destroy();
});
- const findHeader = () => wrapper.find({ ref: 'header' });
- const findTitleLink = () => wrapper.find({ ref: 'titleWrapper' });
- const findExpandButton = () => wrapper.find({ ref: 'expandDiffToFullFileButton' });
+ const findHeader = () => wrapper.findComponent({ ref: 'header' });
+ const findTitleLink = () => wrapper.findComponent({ ref: 'titleWrapper' });
+ const findExpandButton = () => wrapper.findComponent({ ref: 'expandDiffToFullFileButton' });
const findFileActions = () => wrapper.find('.file-actions');
- const findModeChangedLine = () => wrapper.find({ ref: 'fileMode' });
+ const findModeChangedLine = () => wrapper.findComponent({ ref: 'fileMode' });
const findLfsLabel = () => wrapper.find('[data-testid="label-lfs"]');
- const findToggleDiscussionsButton = () => wrapper.find({ ref: 'toggleDiscussionsButton' });
- const findExternalLink = () => wrapper.find({ ref: 'externalLink' });
- const findReplacedFileButton = () => wrapper.find({ ref: 'replacedFileButton' });
- const findViewFileButton = () => wrapper.find({ ref: 'viewButton' });
- const findCollapseIcon = () => wrapper.find({ ref: 'collapseIcon' });
- const findEditButton = () => wrapper.find({ ref: 'editButton' });
+ const findToggleDiscussionsButton = () =>
+ wrapper.findComponent({ ref: 'toggleDiscussionsButton' });
+ const findExternalLink = () => wrapper.findComponent({ ref: 'externalLink' });
+ const findReplacedFileButton = () => wrapper.findComponent({ ref: 'replacedFileButton' });
+ const findViewFileButton = () => wrapper.findComponent({ ref: 'viewButton' });
+ const findCollapseIcon = () => wrapper.findComponent({ ref: 'collapseIcon' });
+ const findEditButton = () => wrapper.findComponent({ ref: 'editButton' });
const findReviewFileCheckbox = () => wrapper.find("[data-testid='fileReviewCheckbox']");
const createComponent = ({ props, options = {} } = {}) => {
@@ -153,7 +154,7 @@ describe('DiffFileHeader component', () => {
});
it('displays a copy to clipboard button', () => {
- expect(wrapper.find(ClipboardButton).exists()).toBe(true);
+ expect(wrapper.findComponent(ClipboardButton).exists()).toBe(true);
});
it('triggers the copy to clipboard tracking event', () => {
diff --git a/spec/frontend/diffs/components/diff_file_row_spec.js b/spec/frontend/diffs/components/diff_file_row_spec.js
index 1d1c5fec293..c5b76551fcc 100644
--- a/spec/frontend/diffs/components/diff_file_row_spec.js
+++ b/spec/frontend/diffs/components/diff_file_row_spec.js
@@ -32,7 +32,7 @@ describe('Diff File Row component', () => {
...diffFileRowProps,
});
- expect(wrapper.find(FileRow).props()).toEqual(
+ expect(wrapper.findComponent(FileRow).props()).toEqual(
expect.objectContaining({
...sharedProps,
}),
@@ -47,7 +47,7 @@ describe('Diff File Row component', () => {
showTooltip: true,
});
- expect(wrapper.find(ChangedFileIcon).props()).toEqual(
+ expect(wrapper.findComponent(ChangedFileIcon).props()).toEqual(
expect.objectContaining({
file: {},
size: 16,
@@ -74,7 +74,7 @@ describe('Diff File Row component', () => {
hideFileStats: false,
viewedFiles: isViewed ? { '#123456789': true } : {},
});
- expect(wrapper.find(FileRow).props('fileClasses')).toBe(expected);
+ expect(wrapper.findComponent(FileRow).props('fileClasses')).toBe(expected);
},
);
@@ -92,7 +92,7 @@ describe('Diff File Row component', () => {
},
hideFileStats,
});
- expect(wrapper.find(FileRowStats).exists()).toEqual(value);
+ expect(wrapper.findComponent(FileRowStats).exists()).toEqual(value);
});
});
diff --git a/spec/frontend/diffs/components/diff_file_spec.js b/spec/frontend/diffs/components/diff_file_spec.js
index 9e8d9e1ca29..944cec77efb 100644
--- a/spec/frontend/diffs/components/diff_file_spec.js
+++ b/spec/frontend/diffs/components/diff_file_spec.js
@@ -100,7 +100,7 @@ function createComponent({ file, first = false, last = false, options = {}, prop
};
}
-const findDiffHeader = (wrapper) => wrapper.find(DiffFileHeaderComponent);
+const findDiffHeader = (wrapper) => wrapper.findComponent(DiffFileHeaderComponent);
const findDiffContentArea = (wrapper) => wrapper.find('[data-testid="content-area"]');
const findLoader = (wrapper) => wrapper.find('[data-testid="loader-icon"]');
const findToggleButton = (wrapper) => wrapper.find('[data-testid="expand-button"]');
@@ -209,14 +209,14 @@ describe('DiffFile', () => {
expect(el.querySelectorAll('.diff-content.hidden').length).toEqual(0);
expect(el.querySelector('.js-file-title')).toBeDefined();
- expect(wrapper.find(DiffFileHeaderComponent).exists()).toBe(true);
+ expect(wrapper.findComponent(DiffFileHeaderComponent).exists()).toBe(true);
expect(el.querySelector('.js-syntax-highlight')).toBeDefined();
markFileToBeRendered(store);
await nextTick();
- expect(wrapper.find(DiffContentComponent).exists()).toBe(true);
+ expect(wrapper.findComponent(DiffContentComponent).exists()).toBe(true);
});
});
@@ -320,7 +320,7 @@ describe('DiffFile', () => {
});
it('should have the file content', async () => {
- expect(wrapper.find(DiffContentComponent).exists()).toBe(true);
+ expect(wrapper.findComponent(DiffContentComponent).exists()).toBe(true);
});
it('should style the component so that it `.has-body` for layout purposes', () => {
@@ -473,8 +473,8 @@ describe('DiffFile', () => {
await nextTick();
expect(wrapper.classes('has-body')).toBe(true);
- expect(wrapper.find(DiffContentComponent).exists()).toBe(true);
- expect(wrapper.find(DiffContentComponent).isVisible()).toBe(true);
+ expect(wrapper.findComponent(DiffContentComponent).exists()).toBe(true);
+ expect(wrapper.findComponent(DiffContentComponent).isVisible()).toBe(true);
},
);
});
diff --git a/spec/frontend/diffs/components/diff_gutter_avatars_spec.js b/spec/frontend/diffs/components/diff_gutter_avatars_spec.js
index c18f0b721da..f13988fc11f 100644
--- a/spec/frontend/diffs/components/diff_gutter_avatars_spec.js
+++ b/spec/frontend/diffs/components/diff_gutter_avatars_spec.js
@@ -1,6 +1,7 @@
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import DiffGutterAvatars from '~/diffs/components/diff_gutter_avatars.vue';
+import { HIDE_COMMENTS } from '~/diffs/i18n';
import discussionsMockData from '../mock_data/diff_discussions';
const getDiscussionsMockData = () => [{ ...discussionsMockData }];
@@ -40,7 +41,12 @@ describe('DiffGutterAvatars', () => {
findCollapseButton().trigger('click');
await nextTick();
- expect(wrapper.emitted().toggleLineDiscussions).toBeTruthy();
+ expect(wrapper.emitted().toggleLineDiscussions).toBeDefined();
+ });
+
+ it('renders the proper title and aria-label', () => {
+ expect(findCollapseButton().attributes('title')).toBe(HIDE_COMMENTS);
+ expect(findCollapseButton().attributes('aria-label')).toBe(HIDE_COMMENTS);
});
});
@@ -69,14 +75,14 @@ describe('DiffGutterAvatars', () => {
findUserAvatars().at(0).trigger('click');
await nextTick();
- expect(wrapper.emitted().toggleLineDiscussions).toBeTruthy();
+ expect(wrapper.emitted().toggleLineDiscussions).toBeDefined();
});
it('should emit toggleDiscussions event on more count text click', async () => {
findMoreCount().trigger('click');
await nextTick();
- expect(wrapper.emitted().toggleLineDiscussions).toBeTruthy();
+ expect(wrapper.emitted().toggleLineDiscussions).toBeDefined();
});
});
diff --git a/spec/frontend/diffs/components/diff_line_note_form_spec.js b/spec/frontend/diffs/components/diff_line_note_form_spec.js
index 542d61c4680..9493dc8855e 100644
--- a/spec/frontend/diffs/components/diff_line_note_form_spec.js
+++ b/spec/frontend/diffs/components/diff_line_note_form_spec.js
@@ -82,7 +82,7 @@ describe('DiffLineNoteForm', () => {
});
it('shows note form', () => {
- expect(wrapper.find(NoteForm).exists()).toBe(true);
+ expect(wrapper.findComponent(NoteForm).exists()).toBe(true);
});
it('passes the provided range of lines to comment form', () => {
diff --git a/spec/frontend/diffs/components/diff_line_spec.js b/spec/frontend/diffs/components/diff_line_spec.js
new file mode 100644
index 00000000000..37368eb1461
--- /dev/null
+++ b/spec/frontend/diffs/components/diff_line_spec.js
@@ -0,0 +1,65 @@
+import { shallowMount } from '@vue/test-utils';
+import DiffLine from '~/diffs/components/diff_line.vue';
+import DiffCodeQuality from '~/diffs/components/diff_code_quality.vue';
+
+const EXAMPLE_LINE_NUMBER = 3;
+const EXAMPLE_DESCRIPTION = 'example description';
+const EXAMPLE_SEVERITY = 'example severity';
+
+const left = {
+ line: {
+ left: {
+ codequality: [
+ {
+ line: EXAMPLE_LINE_NUMBER,
+ description: EXAMPLE_DESCRIPTION,
+ severity: EXAMPLE_SEVERITY,
+ },
+ ],
+ },
+ },
+};
+
+const right = {
+ line: {
+ right: {
+ codequality: [
+ {
+ line: EXAMPLE_LINE_NUMBER,
+ description: EXAMPLE_DESCRIPTION,
+ severity: EXAMPLE_SEVERITY,
+ },
+ ],
+ },
+ },
+};
+
+const mockData = [right, left];
+
+describe('DiffLine', () => {
+ const createWrapper = (propsData) => {
+ return shallowMount(DiffLine, { propsData });
+ };
+
+ it('should emit event when hideCodeQualityFindings is called', () => {
+ const wrapper = createWrapper(right);
+
+ wrapper.findComponent(DiffCodeQuality).vm.$emit('hideCodeQualityFindings');
+ expect(wrapper.emitted()).toEqual({
+ hideCodeQualityFindings: [[EXAMPLE_LINE_NUMBER]],
+ });
+ });
+
+ mockData.forEach((element) => {
+ it('should set correct props for DiffCodeQuality', () => {
+ const wrapper = createWrapper(element);
+ expect(wrapper.findComponent(DiffCodeQuality).props('codeQuality')).toEqual([
+ {
+ line: EXAMPLE_LINE_NUMBER,
+ description: EXAMPLE_DESCRIPTION,
+ severity: EXAMPLE_SEVERITY,
+ },
+ ]);
+ });
+ });
+});
diff --git a/spec/frontend/diffs/components/diff_stats_spec.js b/spec/frontend/diffs/components/diff_stats_spec.js
index 09fe69e97de..3a04547fa69 100644
--- a/spec/frontend/diffs/components/diff_stats_spec.js
+++ b/spec/frontend/diffs/components/diff_stats_spec.js
@@ -87,7 +87,7 @@ describe('diff_stats', () => {
describe('files changes', () => {
const findIcon = (name) =>
wrapper
- .findAll(GlIcon)
+ .findAllComponents(GlIcon)
.filter((c) => c.attributes('name') === name)
.at(0).element.parentNode;
diff --git a/spec/frontend/diffs/components/diff_view_spec.js b/spec/frontend/diffs/components/diff_view_spec.js
index 15923a1c6de..1dd4a2f6c23 100644
--- a/spec/frontend/diffs/components/diff_view_spec.js
+++ b/spec/frontend/diffs/components/diff_view_spec.js
@@ -2,7 +2,7 @@ import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import DiffView from '~/diffs/components/diff_view.vue';
-import DiffCodeQuality from '~/diffs/components/diff_code_quality.vue';
+import DiffLine from '~/diffs/components/diff_line.vue';
import { diffCodeQuality } from '../mock_data/diff_code_quality';
describe('DiffView', () => {
@@ -51,28 +51,27 @@ describe('DiffView', () => {
return shallowMount(DiffView, { propsData, store, stubs, provide });
};
- it('does not render a codeQuality diff view when there is no finding', () => {
+ it('does not render a diff-line component when there is no finding', () => {
const wrapper = createWrapper();
- expect(wrapper.findComponent(DiffCodeQuality).exists()).toBe(false);
+ expect(wrapper.findComponent(DiffLine).exists()).toBe(false);
});
- it('does render a codeQuality diff view with the correct props when there is a finding & refactorCodeQualityInlineFindings flag is true ', async () => {
+ it('does render a diff-line component with the correct props when there is a finding & refactorCodeQualityInlineFindings flag is true', async () => {
const wrapper = createWrapper(diffCodeQuality, {
glFeatures: { refactorCodeQualityInlineFindings: true },
});
wrapper.findComponent(DiffRow).vm.$emit('toggleCodeQualityFindings', 2);
await nextTick();
- expect(wrapper.findComponent(DiffCodeQuality).exists()).toBe(true);
- expect(wrapper.findComponent(DiffCodeQuality).props().codeQuality.length).not.toBe(0);
+ expect(wrapper.findComponent(DiffLine).props('line')).toBe(diffCodeQuality.diffLines[2]);
});
- it('does not render a codeQuality diff view when there is a finding & refactorCodeQualityInlineFindings flag is false ', async () => {
+ it('does not render a diff-line component when there is a finding & refactorCodeQualityInlineFindings flag is false', async () => {
const wrapper = createWrapper(diffCodeQuality, {
glFeatures: { refactorCodeQualityInlineFindings: false },
});
wrapper.findComponent(DiffRow).vm.$emit('toggleCodeQualityFindings', 2);
await nextTick();
- expect(wrapper.findComponent(DiffCodeQuality).exists()).toBe(false);
+ expect(wrapper.findComponent(DiffLine).exists()).toBe(false);
});
it.each`
@@ -89,8 +88,8 @@ describe('DiffView', () => {
diffLines: [{ renderCommentRow: true, ...sides }],
inline: type === 'inline',
});
- expect(wrapper.findAll(DiffCommentCell).length).toBe(total);
- expect(wrapper.find(container).find(DiffCommentCell).exists()).toBe(true);
+ expect(wrapper.findAllComponents(DiffCommentCell).length).toBe(total);
+ expect(wrapper.find(container).findComponent(DiffCommentCell).exists()).toBe(true);
},
);
@@ -98,7 +97,7 @@ describe('DiffView', () => {
const wrapper = createWrapper({
diffLines: [{ renderCommentRow: true, left: { lineDraft: { isDraft: true } } }],
});
- expect(wrapper.find(DraftNote).exists()).toBe(true);
+ expect(wrapper.findComponent(DraftNote).exists()).toBe(true);
});
describe('drag operations', () => {
diff --git a/spec/frontend/diffs/components/image_diff_overlay_spec.js b/spec/frontend/diffs/components/image_diff_overlay_spec.js
index 70191620eb6..ccf942bdcef 100644
--- a/spec/frontend/diffs/components/image_diff_overlay_spec.js
+++ b/spec/frontend/diffs/components/image_diff_overlay_spec.js
@@ -57,7 +57,7 @@ describe('Diffs image diff overlay component', () => {
it('renders icon when showCommentIcon is true', () => {
createComponent({ showCommentIcon: true });
- expect(wrapper.find(GlIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlIcon).exists()).toBe(true);
});
it('sets badge comment positions', () => {
diff --git a/spec/frontend/diffs/components/no_changes_spec.js b/spec/frontend/diffs/components/no_changes_spec.js
index 6903b844e5e..dbfe9770e07 100644
--- a/spec/frontend/diffs/components/no_changes_spec.js
+++ b/spec/frontend/diffs/components/no_changes_spec.js
@@ -56,7 +56,7 @@ describe('Diff no changes empty state', () => {
it('Show create commit button', () => {
createComponent();
- expect(wrapper.find(GlButton).exists()).toBe(true);
+ expect(wrapper.findComponent(GlButton).exists()).toBe(true);
});
it.each`
diff --git a/spec/frontend/diffs/components/tree_list_spec.js b/spec/frontend/diffs/components/tree_list_spec.js
index 931a9562d36..ca7de8fd751 100644
--- a/spec/frontend/diffs/components/tree_list_spec.js
+++ b/spec/frontend/diffs/components/tree_list_spec.js
@@ -106,7 +106,7 @@ describe('Diffs tree list component', () => {
${'index.js'} | ${1}
${'app/*.js'} | ${1}
${'*.js, *.rb'} | ${2}
- `('it returns $itemSize item for $extension', async ({ extension, itemSize }) => {
+ `('returns $itemSize item for $extension', async ({ extension, itemSize }) => {
wrapper.find('[data-testid="diff-tree-search"]').setValue(extension);
await nextTick();
@@ -175,7 +175,7 @@ describe('Diffs tree list component', () => {
await nextTick();
// Have to use $attrs['viewed-files'] because we are passing down an object
// and attributes('') stringifies values (e.g. [object])...
- expect(wrapper.find(FileTree).vm.$attrs['viewed-files']).toBe(viewedDiffFileIds);
+ expect(wrapper.findComponent(FileTree).vm.$attrs['viewed-files']).toBe(viewedDiffFileIds);
});
});
});
diff --git a/spec/frontend/editor/components/source_editor_toolbar_spec.js b/spec/frontend/editor/components/source_editor_toolbar_spec.js
index 6e99eadbd97..bead39ca744 100644
--- a/spec/frontend/editor/components/source_editor_toolbar_spec.js
+++ b/spec/frontend/editor/components/source_editor_toolbar_spec.js
@@ -68,7 +68,7 @@ describe('Source Editor Toolbar', () => {
});
describe('buttons update', () => {
- it('it properly updates buttons on Apollo cache update', async () => {
+ it('properly updates buttons on Apollo cache update', async () => {
const item = buildButton('first', {
group: EDITOR_TOOLBAR_RIGHT_GROUP,
});
diff --git a/spec/frontend/editor/source_editor_extension_spec.js b/spec/frontend/editor/source_editor_extension_spec.js
index 78453aaa491..3424e71d326 100644
--- a/spec/frontend/editor/source_editor_extension_spec.js
+++ b/spec/frontend/editor/source_editor_extension_spec.js
@@ -16,7 +16,7 @@ describe('Editor Extension', () => {
'throws when definition = $definition and setupOptions = $setupOptions',
({ definition, setupOptions }) => {
const constructExtension = () => new EditorExtension({ definition, setupOptions });
- expect(constructExtension).toThrowError(EDITOR_EXTENSION_DEFINITION_ERROR);
+ expect(constructExtension).toThrow(EDITOR_EXTENSION_DEFINITION_ERROR);
},
);
diff --git a/spec/frontend/editor/source_editor_instance_spec.js b/spec/frontend/editor/source_editor_instance_spec.js
index 1223fee320e..20ba23d56ff 100644
--- a/spec/frontend/editor/source_editor_instance_spec.js
+++ b/spec/frontend/editor/source_editor_instance_spec.js
@@ -248,7 +248,7 @@ describe('Source Editor Instance', () => {
const useExtension = () => {
seInstance.use(extensions);
};
- expect(useExtension).toThrowError(thrownError);
+ expect(useExtension).toThrow(thrownError);
},
);
@@ -336,7 +336,7 @@ describe('Source Editor Instance', () => {
const unuse = () => {
seInstance.unuse(unuseExtension);
};
- expect(unuse).toThrowError(thrownError);
+ expect(unuse).toThrow(thrownError);
},
);
@@ -382,7 +382,7 @@ describe('Source Editor Instance', () => {
},
);
- it('it does not remove entry from the global registry to keep for potential future re-use', () => {
+ it('does not remove entry from the global registry to keep for potential future re-use', () => {
const extensionStore = new Map();
seInstance = new SourceEditorInstance({}, extensionStore);
const extensions = seInstance.use(fullExtensionsArray);
diff --git a/spec/frontend/editor/source_editor_webide_ext_spec.js b/spec/frontend/editor/source_editor_webide_ext_spec.js
index 096b6b1646f..f418eab668a 100644
--- a/spec/frontend/editor/source_editor_webide_ext_spec.js
+++ b/spec/frontend/editor/source_editor_webide_ext_spec.js
@@ -30,7 +30,7 @@ describe('Source Editor Web IDE Extension', () => {
const sideBySideSpy = jest.spyOn(instance, 'updateOptions');
instance.use({ definition: EditorWebIdeExtension });
- expect(sideBySideSpy).toBeCalledWith({ renderSideBySide });
+ expect(sideBySideSpy).toHaveBeenCalledWith({ renderSideBySide });
},
);
@@ -45,11 +45,11 @@ describe('Source Editor Web IDE Extension', () => {
const sideBySideSpy = jest.spyOn(instance, 'updateOptions');
await emitter.fire();
- expect(sideBySideSpy).toBeCalledWith({ renderSideBySide: true });
+ expect(sideBySideSpy).toHaveBeenCalledWith({ renderSideBySide: true });
editorEl.style.width = '0px';
await emitter.fire();
- expect(sideBySideSpy).toBeCalledWith({ renderSideBySide: false });
+ expect(sideBySideSpy).toHaveBeenCalledWith({ renderSideBySide: false });
});
});
});
diff --git a/spec/frontend/emoji/components/category_spec.js b/spec/frontend/emoji/components/category_spec.js
index 82dc0cdc250..90816f28d5b 100644
--- a/spec/frontend/emoji/components/category_spec.js
+++ b/spec/frontend/emoji/components/category_spec.js
@@ -22,7 +22,7 @@ describe('Emoji category component', () => {
});
it('renders emoji groups', () => {
- expect(wrapper.findAll(EmojiGroup).length).toBe(2);
+ expect(wrapper.findAllComponents(EmojiGroup).length).toBe(2);
});
it('renders group', async () => {
@@ -30,19 +30,19 @@ describe('Emoji category component', () => {
// eslint-disable-next-line no-restricted-syntax
await wrapper.setData({ renderGroup: true });
- expect(wrapper.find(EmojiGroup).attributes('rendergroup')).toBe('true');
+ expect(wrapper.findComponent(EmojiGroup).attributes('rendergroup')).toBe('true');
});
it('renders group on appear', async () => {
- wrapper.find(GlIntersectionObserver).vm.$emit('appear');
+ wrapper.findComponent(GlIntersectionObserver).vm.$emit('appear');
await nextTick();
- expect(wrapper.find(EmojiGroup).attributes('rendergroup')).toBe('true');
+ expect(wrapper.findComponent(EmojiGroup).attributes('rendergroup')).toBe('true');
});
it('emits appear event on appear', async () => {
- wrapper.find(GlIntersectionObserver).vm.$emit('appear');
+ wrapper.findComponent(GlIntersectionObserver).vm.$emit('appear');
await nextTick();
diff --git a/spec/frontend/emoji/components/utils_spec.js b/spec/frontend/emoji/components/utils_spec.js
index 56f514ee9a8..a17ddb3bb9a 100644
--- a/spec/frontend/emoji/components/utils_spec.js
+++ b/spec/frontend/emoji/components/utils_spec.js
@@ -4,13 +4,13 @@ import { getFrequentlyUsedEmojis, addToFrequentlyUsed } from '~/emoji/components
jest.mock('~/lib/utils/cookies');
describe('getFrequentlyUsedEmojis', () => {
- it('it returns null when no saved emojis set', () => {
+ it('returns null when no saved emojis set', () => {
jest.spyOn(Cookies, 'get').mockReturnValue(null);
expect(getFrequentlyUsedEmojis()).toBe(null);
});
- it('it returns frequently used emojis object', () => {
+ it('returns frequently used emojis object', () => {
jest.spyOn(Cookies, 'get').mockReturnValue('thumbsup,thumbsdown');
expect(getFrequentlyUsedEmojis()).toEqual({
diff --git a/spec/frontend/emoji/index_spec.js b/spec/frontend/emoji/index_spec.js
index dc8f50e0e4b..36c3eeb5a52 100644
--- a/spec/frontend/emoji/index_spec.js
+++ b/spec/frontend/emoji/index_spec.js
@@ -120,177 +120,177 @@ describe('emoji', () => {
describe('isFlagEmoji', () => {
it('should gracefully handle empty string', () => {
- expect(isFlagEmoji('')).toBeFalsy();
+ expect(isFlagEmoji('')).toBe(false);
});
it('should detect flag_ac', () => {
- expect(isFlagEmoji('🇦🇨')).toBeTruthy();
+ expect(isFlagEmoji('🇦🇨')).toBe(true);
});
it('should detect flag_us', () => {
- expect(isFlagEmoji('🇺🇸')).toBeTruthy();
+ expect(isFlagEmoji('🇺🇸')).toBe(true);
});
it('should detect flag_zw', () => {
- expect(isFlagEmoji('🇿🇼')).toBeTruthy();
+ expect(isFlagEmoji('🇿🇼')).toBe(true);
});
it('should not detect flags', () => {
- expect(isFlagEmoji('ðŸŽ')).toBeFalsy();
+ expect(isFlagEmoji('ðŸŽ')).toBe(false);
});
it('should not detect triangular_flag_on_post', () => {
- expect(isFlagEmoji('🚩')).toBeFalsy();
+ expect(isFlagEmoji('🚩')).toBe(false);
});
it('should not detect single letter', () => {
- expect(isFlagEmoji('🇦')).toBeFalsy();
+ expect(isFlagEmoji('🇦')).toBe(false);
});
it('should not detect >2 letters', () => {
- expect(isFlagEmoji('🇦🇧🇨')).toBeFalsy();
+ expect(isFlagEmoji('🇦🇧🇨')).toBe(false);
});
});
describe('isRainbowFlagEmoji', () => {
it('should gracefully handle empty string', () => {
- expect(isRainbowFlagEmoji('')).toBeFalsy();
+ expect(isRainbowFlagEmoji('')).toBe(false);
});
it('should detect rainbow_flag', () => {
- expect(isRainbowFlagEmoji('ðŸ³ðŸŒˆ')).toBeTruthy();
+ expect(isRainbowFlagEmoji('ðŸ³ðŸŒˆ')).toBe(true);
});
it("should not detect flag_white on its' own", () => {
- expect(isRainbowFlagEmoji('ðŸ³')).toBeFalsy();
+ expect(isRainbowFlagEmoji('ðŸ³')).toBe(false);
});
it("should not detect rainbow on its' own", () => {
- expect(isRainbowFlagEmoji('🌈')).toBeFalsy();
+ expect(isRainbowFlagEmoji('🌈')).toBe(false);
});
it('should not detect flag_white with something else', () => {
- expect(isRainbowFlagEmoji('ðŸ³ðŸ”µ')).toBeFalsy();
+ expect(isRainbowFlagEmoji('ðŸ³ðŸ”µ')).toBe(false);
});
});
describe('isKeycapEmoji', () => {
it('should gracefully handle empty string', () => {
- expect(isKeycapEmoji('')).toBeFalsy();
+ expect(isKeycapEmoji('')).toBe(false);
});
it('should detect one(keycap)', () => {
- expect(isKeycapEmoji('1ï¸âƒ£')).toBeTruthy();
+ expect(isKeycapEmoji('1ï¸âƒ£')).toBe(true);
});
it('should detect nine(keycap)', () => {
- expect(isKeycapEmoji('9ï¸âƒ£')).toBeTruthy();
+ expect(isKeycapEmoji('9ï¸âƒ£')).toBe(true);
});
it('should not detect ten(keycap)', () => {
- expect(isKeycapEmoji('🔟')).toBeFalsy();
+ expect(isKeycapEmoji('🔟')).toBe(false);
});
it('should not detect hash(keycap)', () => {
- expect(isKeycapEmoji('#⃣')).toBeFalsy();
+ expect(isKeycapEmoji('#⃣')).toBe(false);
});
});
describe('isSkinToneComboEmoji', () => {
it('should gracefully handle empty string', () => {
- expect(isSkinToneComboEmoji('')).toBeFalsy();
+ expect(isSkinToneComboEmoji('')).toBe(false);
});
it('should detect hand_splayed_tone5', () => {
- expect(isSkinToneComboEmoji('ðŸ–ðŸ¿')).toBeTruthy();
+ expect(isSkinToneComboEmoji('ðŸ–ðŸ¿')).toBe(true);
});
it('should not detect hand_splayed', () => {
- expect(isSkinToneComboEmoji('ðŸ–')).toBeFalsy();
+ expect(isSkinToneComboEmoji('ðŸ–')).toBe(false);
});
it('should detect lifter_tone1', () => {
- expect(isSkinToneComboEmoji('ðŸ‹ðŸ»')).toBeTruthy();
+ expect(isSkinToneComboEmoji('ðŸ‹ðŸ»')).toBe(true);
});
it('should not detect lifter', () => {
- expect(isSkinToneComboEmoji('ðŸ‹')).toBeFalsy();
+ expect(isSkinToneComboEmoji('ðŸ‹')).toBe(false);
});
it('should detect rowboat_tone4', () => {
- expect(isSkinToneComboEmoji('🚣ðŸ¾')).toBeTruthy();
+ expect(isSkinToneComboEmoji('🚣ðŸ¾')).toBe(true);
});
it('should not detect rowboat', () => {
- expect(isSkinToneComboEmoji('🚣')).toBeFalsy();
+ expect(isSkinToneComboEmoji('🚣')).toBe(false);
});
it('should not detect individual tone emoji', () => {
- expect(isSkinToneComboEmoji('ðŸ»')).toBeFalsy();
+ expect(isSkinToneComboEmoji('ðŸ»')).toBe(false);
});
});
describe('isHorceRacingSkinToneComboEmoji', () => {
it('should gracefully handle empty string', () => {
- expect(isHorceRacingSkinToneComboEmoji('')).toBeFalsy();
+ expect(isHorceRacingSkinToneComboEmoji('')).toBeUndefined();
});
it('should detect horse_racing_tone2', () => {
- expect(isHorceRacingSkinToneComboEmoji('ðŸ‡ðŸ¼')).toBeTruthy();
+ expect(isHorceRacingSkinToneComboEmoji('ðŸ‡ðŸ¼')).toBe(true);
});
it('should not detect horse_racing', () => {
- expect(isHorceRacingSkinToneComboEmoji('ðŸ‡')).toBeFalsy();
+ expect(isHorceRacingSkinToneComboEmoji('ðŸ‡')).toBe(false);
});
});
describe('isPersonZwjEmoji', () => {
it('should gracefully handle empty string', () => {
- expect(isPersonZwjEmoji('')).toBeFalsy();
+ expect(isPersonZwjEmoji('')).toBe(false);
});
it('should detect couple_mm', () => {
- expect(isPersonZwjEmoji('👨â€â¤ï¸â€ðŸ‘¨')).toBeTruthy();
+ expect(isPersonZwjEmoji('👨â€â¤ï¸â€ðŸ‘¨')).toBe(true);
});
it('should not detect couple_with_heart', () => {
- expect(isPersonZwjEmoji('💑')).toBeFalsy();
+ expect(isPersonZwjEmoji('💑')).toBe(false);
});
it('should not detect couplekiss', () => {
- expect(isPersonZwjEmoji('ðŸ’')).toBeFalsy();
+ expect(isPersonZwjEmoji('ðŸ’')).toBe(false);
});
it('should detect family_mmb', () => {
- expect(isPersonZwjEmoji('👨â€ðŸ‘¨â€ðŸ‘¦')).toBeTruthy();
+ expect(isPersonZwjEmoji('👨â€ðŸ‘¨â€ðŸ‘¦')).toBe(true);
});
it('should detect family_mwgb', () => {
- expect(isPersonZwjEmoji('👨â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦')).toBeTruthy();
+ expect(isPersonZwjEmoji('👨â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦')).toBe(true);
});
it('should not detect family', () => {
- expect(isPersonZwjEmoji('👪')).toBeFalsy();
+ expect(isPersonZwjEmoji('👪')).toBe(false);
});
it('should detect kiss_ww', () => {
- expect(isPersonZwjEmoji('👩â€â¤ï¸â€ðŸ’‹â€ðŸ‘©')).toBeTruthy();
+ expect(isPersonZwjEmoji('👩â€â¤ï¸â€ðŸ’‹â€ðŸ‘©')).toBe(true);
});
it('should not detect girl', () => {
- expect(isPersonZwjEmoji('👧')).toBeFalsy();
+ expect(isPersonZwjEmoji('👧')).toBe(false);
});
it('should not detect girl_tone5', () => {
- expect(isPersonZwjEmoji('👧ðŸ¿')).toBeFalsy();
+ expect(isPersonZwjEmoji('👧ðŸ¿')).toBe(false);
});
it('should not detect man', () => {
- expect(isPersonZwjEmoji('👨')).toBeFalsy();
+ expect(isPersonZwjEmoji('👨')).toBe(false);
});
it('should not detect woman', () => {
- expect(isPersonZwjEmoji('👩')).toBeFalsy();
+ expect(isPersonZwjEmoji('👩')).toBe(false);
});
});
@@ -298,13 +298,13 @@ describe('emoji', () => {
it('should gracefully handle empty string with unicode support', () => {
const isSupported = isEmojiUnicodeSupported({ '1.0': true }, '', '1.0');
- expect(isSupported).toBeTruthy();
+ expect(isSupported).toBe(true);
});
it('should gracefully handle empty string without unicode support', () => {
const isSupported = isEmojiUnicodeSupported({}, '', '1.0');
- expect(isSupported).toBeFalsy();
+ expect(isSupported).toBeUndefined();
});
it('bomb(6.0) with 6.0 support', () => {
@@ -316,7 +316,7 @@ describe('emoji', () => {
emojiFixtureMap[emojiKey].unicodeVersion,
);
- expect(isSupported).toBeTruthy();
+ expect(isSupported).toBe(true);
});
it('bomb(6.0) without 6.0 support', () => {
@@ -328,7 +328,7 @@ describe('emoji', () => {
emojiFixtureMap[emojiKey].unicodeVersion,
);
- expect(isSupported).toBeFalsy();
+ expect(isSupported).toBe(false);
});
it('bomb(6.0) without 6.0 but with 9.0 support', () => {
@@ -340,7 +340,7 @@ describe('emoji', () => {
emojiFixtureMap[emojiKey].unicodeVersion,
);
- expect(isSupported).toBeFalsy();
+ expect(isSupported).toBe(false);
});
it('construction_worker_tone5(8.0) without skin tone modifier support', () => {
@@ -367,7 +367,7 @@ describe('emoji', () => {
emojiFixtureMap[emojiKey].unicodeVersion,
);
- expect(isSupported).toBeFalsy();
+ expect(isSupported).toBe(false);
});
it('use native keycap on >=57 chrome', () => {
@@ -386,7 +386,7 @@ describe('emoji', () => {
emojiFixtureMap[emojiKey].unicodeVersion,
);
- expect(isSupported).toBeTruthy();
+ expect(isSupported).toBe(true);
});
it('fallback keycap on <57 chrome', () => {
@@ -405,7 +405,7 @@ describe('emoji', () => {
emojiFixtureMap[emojiKey].unicodeVersion,
);
- expect(isSupported).toBeFalsy();
+ expect(isSupported).toBe(false);
});
});
diff --git a/spec/frontend/environments/deployment_spec.js b/spec/frontend/environments/deployment_spec.js
index 6cc363e000b..4cbbb60b74c 100644
--- a/spec/frontend/environments/deployment_spec.js
+++ b/spec/frontend/environments/deployment_spec.js
@@ -1,4 +1,6 @@
-import { GlCollapse } from '@gitlab/ui';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import { GlLoadingIcon } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { useFakeDate } from 'helpers/fake_date';
import { stubTransition } from 'helpers/stub_transition';
@@ -8,9 +10,13 @@ import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import Deployment from '~/environments/components/deployment.vue';
import Commit from '~/environments/components/commit.vue';
import DeploymentStatusBadge from '~/environments/components/deployment_status_badge.vue';
-import { resolvedEnvironment } from './graphql/mock_data';
+import createMockApollo from '../__helpers__/mock_apollo_helper';
+import waitForPromises from '../__helpers__/wait_for_promises';
+import getDeploymentDetails from '../../../app/assets/javascripts/environments/graphql/queries/deployment_details.query.graphql';
+import { resolvedEnvironment, resolvedDeploymentDetails } from './graphql/mock_data';
describe('~/environments/components/deployment.vue', () => {
+ Vue.use(VueApollo);
useFakeDate(2022, 0, 8, 16);
let deployment;
@@ -20,14 +26,23 @@ describe('~/environments/components/deployment.vue', () => {
deployment = resolvedEnvironment.lastDeployment;
});
- const createWrapper = ({ propsData = {} } = {}) =>
- mountExtended(Deployment, {
+ const createWrapper = ({ propsData = {}, options = {} } = {}) => {
+ const mockApollo = createMockApollo([
+ [getDeploymentDetails, jest.fn().mockResolvedValue(resolvedDeploymentDetails)],
+ ]);
+
+ return mountExtended(Deployment, {
+ stubs: { transition: stubTransition() },
propsData: {
deployment,
+ visible: true,
...propsData,
},
- stubs: { transition: stubTransition() },
+ apolloProvider: mockApollo,
+ provide: { projectPath: '/1' },
+ ...options,
});
+ };
afterEach(() => {
wrapper?.destroy();
@@ -102,10 +117,11 @@ describe('~/environments/components/deployment.vue', () => {
});
it('shows the short SHA for the commit of the deployment', () => {
- const sha = wrapper.findByTitle(__('Commit SHA'));
+ const sha = wrapper.findByRole('link', { name: __('Commit SHA') });
expect(sha.exists()).toBe(true);
expect(sha.text()).toBe(deployment.commit.shortId);
+ expect(sha.attributes('href')).toBe(deployment.commit.commitPath);
});
it('shows the commit icon', () => {
@@ -183,29 +199,12 @@ describe('~/environments/components/deployment.vue', () => {
});
});
- describe('collapse', () => {
- let collapse;
- let button;
-
+ describe('details', () => {
beforeEach(() => {
wrapper = createWrapper();
- collapse = wrapper.findComponent(GlCollapse);
- button = wrapper.findComponent({ ref: 'details-toggle' });
});
- it('is collapsed by default', () => {
- expect(collapse.attributes('visible')).toBeUndefined();
- expect(button.props('icon')).toBe('expand-down');
- expect(button.text()).toBe(__('Show details'));
- });
-
- it('opens on click', async () => {
- await button.trigger('click');
-
- expect(button.text()).toBe(__('Hide details'));
- expect(button.props('icon')).toBe('expand-up');
- expect(collapse.attributes('visible')).toBe('visible');
-
+ it('shows information about the deployment', () => {
const username = wrapper.findByRole('link', { name: `@${deployment.user.username}` });
expect(username.attributes('href')).toBe(deployment.user.path);
@@ -221,24 +220,43 @@ describe('~/environments/components/deployment.vue', () => {
const ref = wrapper.findByRole('link', { name: deployment.ref.name });
expect(ref.attributes('href')).toBe(deployment.ref.refPath);
});
+
+ it('shows information about tags related to the deployment', async () => {
+ expect(wrapper.findByText(__('Tags')).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
+
+ await waitForPromises();
+
+ for (let i = 1; i < 6; i += 1) {
+ const tagName = __(`testTag${i}`);
+ const testTag = wrapper.findByText(tagName);
+ expect(testTag.exists()).toBe(true);
+ expect(testTag.attributes('href')).toBe(`tags/${tagName}`);
+ }
+ expect(wrapper.findByText(__('testTag6')).exists()).toBe(false);
+ expect(wrapper.findByText(__('Tag')).exists()).toBe(false);
+ // with more than 5 tags, show overflow marker
+ expect(wrapper.findByText('...').exists()).toBe(true);
+ });
});
describe('with tagged deployment', () => {
- beforeEach(async () => {
+ beforeEach(() => {
wrapper = createWrapper({ propsData: { deployment: { ...deployment, tag: true } } });
- await wrapper.findComponent({ ref: 'details-toggle' }).trigger('click');
});
- it('shows tag instead of branch', () => {
- const refLabel = wrapper.findByText(__('Tag'));
+ it('shows tags instead of branch', () => {
+ const refLabel = wrapper.findByText(__('Tags'));
expect(refLabel.exists()).toBe(true);
+
+ const branchLabel = wrapper.findByText(__('Branch'));
+ expect(branchLabel.exists()).toBe(false);
});
});
describe('with API deployment', () => {
- beforeEach(async () => {
+ beforeEach(() => {
wrapper = createWrapper({ propsData: { deployment: { ...deployment, deployable: null } } });
- await wrapper.findComponent({ ref: 'details-toggle' }).trigger('click');
});
it('shows API instead of a job name', () => {
@@ -247,13 +265,12 @@ describe('~/environments/components/deployment.vue', () => {
});
});
describe('without a job path', () => {
- beforeEach(async () => {
+ beforeEach(() => {
wrapper = createWrapper({
propsData: {
deployment: { ...deployment, deployable: { name: deployment.deployable.name } },
},
});
- await wrapper.findComponent({ ref: 'details-toggle' }).trigger('click');
});
it('shows a span instead of a link', () => {
diff --git a/spec/frontend/environments/environment_table_spec.js b/spec/frontend/environments/environment_table_spec.js
index 49a643aaac8..a86cfdd56ba 100644
--- a/spec/frontend/environments/environment_table_spec.js
+++ b/spec/frontend/environments/environment_table_spec.js
@@ -363,7 +363,7 @@ describe('Environment table', () => {
});
describe('sortedEnvironments', () => {
- it('it should sort children as well', () => {
+ it('should sort children as well', () => {
const mockItems = [
{
name: 'production',
diff --git a/spec/frontend/environments/environments_app_spec.js b/spec/frontend/environments/environments_app_spec.js
index 57f98c81124..aff54107d6b 100644
--- a/spec/frontend/environments/environments_app_spec.js
+++ b/spec/frontend/environments/environments_app_spec.js
@@ -50,6 +50,7 @@ describe('~/environments/components/environments_app.vue', () => {
defaultBranchName: 'main',
helpPagePath: '/help',
projectId: '1',
+ projectPath: '/1',
...provide,
},
apolloProvider,
diff --git a/spec/frontend/environments/graphql/mock_data.js b/spec/frontend/environments/graphql/mock_data.js
index 7e436476a8f..d246641b94b 100644
--- a/spec/frontend/environments/graphql/mock_data.js
+++ b/spec/frontend/environments/graphql/mock_data.js
@@ -757,3 +757,41 @@ export const resolvedFolder = {
stoppedCount: 0,
__typename: 'LocalEnvironmentFolder',
};
+
+export const resolvedDeploymentDetails = {
+ data: {
+ project: {
+ id: 'gid://gitlab/Project/20',
+ deployment: {
+ id: 'gid://gitlab/Deployment/99',
+ iid: '55',
+ tags: [
+ {
+ name: 'testTag1',
+ path: 'tags/testTag1',
+ },
+ {
+ name: 'testTag2',
+ path: 'tags/testTag2',
+ },
+ {
+ name: 'testTag3',
+ path: 'tags/testTag3',
+ },
+ {
+ name: 'testTag4',
+ path: 'tags/testTag4',
+ },
+ {
+ name: 'testTag5',
+ path: 'tags/testTag5',
+ },
+ {
+ name: 'testTag6',
+ path: 'tags/testTag6',
+ },
+ ],
+ },
+ },
+ },
+};
diff --git a/spec/frontend/environments/new_environment_item_spec.js b/spec/frontend/environments/new_environment_item_spec.js
index a151595bf64..76cd09cfb4e 100644
--- a/spec/frontend/environments/new_environment_item_spec.js
+++ b/spec/frontend/environments/new_environment_item_spec.js
@@ -24,7 +24,7 @@ describe('~/environments/components/new_environment_item.vue', () => {
mountExtended(EnvironmentItem, {
apolloProvider,
propsData: { environment: resolvedEnvironment, ...propsData },
- provide: { helpPagePath: '/help', projectId: '1' },
+ provide: { helpPagePath: '/help', projectId: '1', projectPath: '/1' },
stubs: { transition: stubTransition() },
});
diff --git a/spec/frontend/environments/new_environment_spec.js b/spec/frontend/environments/new_environment_spec.js
index 5a1c1c7714c..2405cb82eac 100644
--- a/spec/frontend/environments/new_environment_spec.js
+++ b/spec/frontend/environments/new_environment_spec.js
@@ -65,7 +65,7 @@ describe('~/environments/components/new.vue', () => {
input | value
${() => name} | ${'test'}
${() => url} | ${'https://example.org'}
- `('it changes the value of the input to $value', async ({ input, value }) => {
+ `('changes the value of the input to $value', async ({ input, value }) => {
await input().setValue(value);
expect(input().element.value).toBe(value);
diff --git a/spec/frontend/error_tracking/components/error_tracking_list_spec.js b/spec/frontend/error_tracking/components/error_tracking_list_spec.js
index b7dffbbec04..805ada54509 100644
--- a/spec/frontend/error_tracking/components/error_tracking_list_spec.js
+++ b/spec/frontend/error_tracking/components/error_tracking_list_spec.js
@@ -164,19 +164,19 @@ describe('ErrorTrackingList', () => {
expect(findSortDropdown().exists()).toBe(true);
});
- it('it searches by query', () => {
+ it('searches by query', () => {
findSearchBox().vm.$emit('input', 'search');
findSearchBox().trigger('keyup.enter');
expect(actions.searchByQuery.mock.calls[0][1]).toBe('search');
});
- it('it sorts by fields', () => {
+ it('sorts by fields', () => {
const findSortItem = () => findSortDropdown().find('.dropdown-item');
findSortItem().trigger('click');
expect(actions.sortByField).toHaveBeenCalled();
});
- it('it filters by status', () => {
+ it('filters by status', () => {
const findStatusFilter = () => findStatusFilterDropdown().find('.dropdown-item');
findStatusFilter().trigger('click');
expect(actions.filterByStatus).toHaveBeenCalled();
diff --git a/spec/frontend/error_tracking/components/stacktrace_entry_spec.js b/spec/frontend/error_tracking/components/stacktrace_entry_spec.js
index 693fcff50ca..0de4277b08a 100644
--- a/spec/frontend/error_tracking/components/stacktrace_entry_spec.js
+++ b/spec/frontend/error_tracking/components/stacktrace_entry_spec.js
@@ -62,7 +62,7 @@ describe('Stacktrace Entry', () => {
);
});
- it('should render only lineNo:columnNO when there is no errorFn ', () => {
+ it('should render only lineNo:columnNO when there is no errorFn', () => {
const extraInfo = { errorLine: 34, errorFn: null, errorColumn: 77 };
mountComponent({ expanded: false, lines: [], ...extraInfo });
const fileHeaderContent = trimText(findFileHeaderContent());
@@ -70,7 +70,7 @@ describe('Stacktrace Entry', () => {
expect(fileHeaderContent).toContain(`${extraInfo.errorLine}:${extraInfo.errorColumn}`);
});
- it('should render only lineNo when there is no errorColumn ', () => {
+ it('should render only lineNo when there is no errorColumn', () => {
const extraInfo = { errorLine: 34, errorFn: 'errorFn', errorColumn: null };
mountComponent({ expanded: false, lines: [], ...extraInfo });
const fileHeaderContent = trimText(findFileHeaderContent());
diff --git a/spec/frontend/error_tracking_settings/components/app_spec.js b/spec/frontend/error_tracking_settings/components/app_spec.js
index c660c9c4a99..7a714cc1ebc 100644
--- a/spec/frontend/error_tracking_settings/components/app_spec.js
+++ b/spec/frontend/error_tracking_settings/components/app_spec.js
@@ -76,23 +76,23 @@ describe('error tracking settings app', () => {
describe('section', () => {
it('renders the form and dropdown', () => {
- expect(wrapper.find(ErrorTrackingForm).exists()).toBeTruthy();
- expect(wrapper.find(ProjectDropdown).exists()).toBeTruthy();
+ expect(wrapper.findComponent(ErrorTrackingForm).exists()).toBe(true);
+ expect(wrapper.findComponent(ProjectDropdown).exists()).toBe(true);
});
it('renders the Save Changes button', () => {
- expect(wrapper.find('.js-error-tracking-button').exists()).toBeTruthy();
+ expect(wrapper.find('.js-error-tracking-button').exists()).toBe(true);
});
it('enables the button by default', () => {
- expect(wrapper.find('.js-error-tracking-button').attributes('disabled')).toBeFalsy();
+ expect(wrapper.find('.js-error-tracking-button').attributes('disabled')).toBeUndefined();
});
it('disables the button when saving', async () => {
store.state.settingsLoading = true;
await nextTick();
- expect(wrapper.find('.js-error-tracking-button').attributes('disabled')).toBeTruthy();
+ expect(wrapper.find('.js-error-tracking-button').attributes('disabled')).toBe('true');
});
});
diff --git a/spec/frontend/error_tracking_settings/components/project_dropdown_spec.js b/spec/frontend/error_tracking_settings/components/project_dropdown_spec.js
index b44af547658..c9095441d41 100644
--- a/spec/frontend/error_tracking_settings/components/project_dropdown_spec.js
+++ b/spec/frontend/error_tracking_settings/components/project_dropdown_spec.js
@@ -42,7 +42,7 @@ describe('error tracking settings project dropdown', () => {
describe('empty project list', () => {
it('renders the dropdown', () => {
expect(wrapper.find('#project-dropdown').exists()).toBe(true);
- expect(wrapper.find(GlDropdown).exists()).toBe(true);
+ expect(wrapper.findComponent(GlDropdown).exists()).toBe(true);
});
it('shows helper text', () => {
@@ -57,8 +57,8 @@ describe('error tracking settings project dropdown', () => {
});
it('does not contain any dropdown items', () => {
- expect(wrapper.find(GlDropdownItem).exists()).toBe(false);
- expect(wrapper.find(GlDropdown).props('text')).toBe('No projects available');
+ expect(wrapper.findComponent(GlDropdownItem).exists()).toBe(false);
+ expect(wrapper.findComponent(GlDropdown).props('text')).toBe('No projects available');
});
});
@@ -71,12 +71,12 @@ describe('error tracking settings project dropdown', () => {
it('renders the dropdown', () => {
expect(wrapper.find('#project-dropdown').exists()).toBe(true);
- expect(wrapper.find(GlDropdown).exists()).toBe(true);
+ expect(wrapper.findComponent(GlDropdown).exists()).toBe(true);
});
it('contains a number of dropdown items', () => {
- expect(wrapper.find(GlDropdownItem).exists()).toBe(true);
- expect(wrapper.findAll(GlDropdownItem).length).toBe(2);
+ expect(wrapper.findComponent(GlDropdownItem).exists()).toBe(true);
+ expect(wrapper.findAllComponents(GlDropdownItem).length).toBe(2);
});
});
diff --git a/spec/frontend/feature_flags/components/environments_dropdown_spec.js b/spec/frontend/feature_flags/components/environments_dropdown_spec.js
index e8103df78bc..2b9710c9085 100644
--- a/spec/frontend/feature_flags/components/environments_dropdown_spec.js
+++ b/spec/frontend/feature_flags/components/environments_dropdown_spec.js
@@ -8,7 +8,7 @@ import EnvironmentsDropdown from '~/feature_flags/components/environments_dropdo
import axios from '~/lib/utils/axios_utils';
import httpStatusCodes from '~/lib/utils/http_status';
-describe('Feature flags > Environments dropdown ', () => {
+describe('Feature flags > Environments dropdown', () => {
let wrapper;
let mock;
const results = ['production', 'staging'];
diff --git a/spec/frontend/feature_flags/store/edit/actions_spec.js b/spec/frontend/feature_flags/store/edit/actions_spec.js
index b6114cb0c9f..7132e83a940 100644
--- a/spec/frontend/feature_flags/store/edit/actions_spec.js
+++ b/spec/frontend/feature_flags/store/edit/actions_spec.js
@@ -40,7 +40,7 @@ describe('Feature flags Edit Module actions', () => {
});
describe('success', () => {
- it('dispatches requestUpdateFeatureFlag and receiveUpdateFeatureFlagSuccess ', () => {
+ it('dispatches requestUpdateFeatureFlag and receiveUpdateFeatureFlagSuccess', () => {
const featureFlag = {
name: 'name',
description: 'description',
@@ -75,7 +75,7 @@ describe('Feature flags Edit Module actions', () => {
});
describe('error', () => {
- it('dispatches requestUpdateFeatureFlag and receiveUpdateFeatureFlagError ', () => {
+ it('dispatches requestUpdateFeatureFlag and receiveUpdateFeatureFlagError', () => {
mock.onPut(`${TEST_HOST}/endpoint.json`).replyOnce(500, { message: [] });
return testAction(
@@ -154,7 +154,7 @@ describe('Feature flags Edit Module actions', () => {
});
describe('success', () => {
- it('dispatches requestFeatureFlag and receiveFeatureFlagSuccess ', () => {
+ it('dispatches requestFeatureFlag and receiveFeatureFlagSuccess', () => {
mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, { id: 1 });
return testAction(
@@ -176,7 +176,7 @@ describe('Feature flags Edit Module actions', () => {
});
describe('error', () => {
- it('dispatches requestFeatureFlag and receiveUpdateFeatureFlagError ', () => {
+ it('dispatches requestFeatureFlag and receiveUpdateFeatureFlagError', () => {
mock.onGet(`${TEST_HOST}/endpoint.json`, {}).replyOnce(500, {});
return testAction(
diff --git a/spec/frontend/feature_flags/store/index/actions_spec.js b/spec/frontend/feature_flags/store/index/actions_spec.js
index ce62c3b0473..96a7d868316 100644
--- a/spec/frontend/feature_flags/store/index/actions_spec.js
+++ b/spec/frontend/feature_flags/store/index/actions_spec.js
@@ -56,7 +56,7 @@ describe('Feature flags actions', () => {
});
describe('success', () => {
- it('dispatches requestFeatureFlags and receiveFeatureFlagsSuccess ', () => {
+ it('dispatches requestFeatureFlags and receiveFeatureFlagsSuccess', () => {
mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, getRequestData, {});
return testAction(
@@ -78,7 +78,7 @@ describe('Feature flags actions', () => {
});
describe('error', () => {
- it('dispatches requestFeatureFlags and receiveFeatureFlagsError ', () => {
+ it('dispatches requestFeatureFlags and receiveFeatureFlagsError', () => {
mock.onGet(`${TEST_HOST}/endpoint.json`, {}).replyOnce(500, {});
return testAction(
@@ -153,7 +153,7 @@ describe('Feature flags actions', () => {
});
describe('success', () => {
- it('dispatches requestRotateInstanceId and receiveRotateInstanceIdSuccess ', () => {
+ it('dispatches requestRotateInstanceId and receiveRotateInstanceIdSuccess', () => {
mock.onPost(`${TEST_HOST}/endpoint.json`).replyOnce(200, rotateData, {});
return testAction(
@@ -175,7 +175,7 @@ describe('Feature flags actions', () => {
});
describe('error', () => {
- it('dispatches requestRotateInstanceId and receiveRotateInstanceIdError ', () => {
+ it('dispatches requestRotateInstanceId and receiveRotateInstanceIdError', () => {
mock.onGet(`${TEST_HOST}/endpoint.json`, {}).replyOnce(500, {});
return testAction(
diff --git a/spec/frontend/feature_flags/store/new/actions_spec.js b/spec/frontend/feature_flags/store/new/actions_spec.js
index 1dcd2da1d93..dbe6669c868 100644
--- a/spec/frontend/feature_flags/store/new/actions_spec.js
+++ b/spec/frontend/feature_flags/store/new/actions_spec.js
@@ -33,7 +33,7 @@ describe('Feature flags New Module Actions', () => {
});
describe('success', () => {
- it('dispatches requestCreateFeatureFlag and receiveCreateFeatureFlagSuccess ', () => {
+ it('dispatches requestCreateFeatureFlag and receiveCreateFeatureFlagSuccess', () => {
const actionParams = {
name: 'name',
description: 'description',
@@ -68,7 +68,7 @@ describe('Feature flags New Module Actions', () => {
});
describe('error', () => {
- it('dispatches requestCreateFeatureFlag and receiveCreateFeatureFlagError ', () => {
+ it('dispatches requestCreateFeatureFlag and receiveCreateFeatureFlagError', () => {
const actionParams = {
name: 'name',
description: 'description',
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 897ad5ee2bf..91457f10bf8 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
@@ -1,4 +1,4 @@
-import { shallowMount } from '@vue/test-utils';
+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';
@@ -6,12 +6,12 @@ import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered
describe('Recent Searches Dropdown Content', () => {
let wrapper;
- const findLocalStorageNote = () => wrapper.find({ ref: 'localStorageNote' });
- const findDropdownItems = () => wrapper.findAll({ ref: 'dropdownItem' });
- const findDropdownNote = () => wrapper.find({ ref: 'dropdownNote' });
+ const findLocalStorageNote = () => wrapper.findByTestId('local-storage-note');
+ const findDropdownItems = () => wrapper.findAllByTestId('dropdown-item');
+ const findDropdownNote = () => wrapper.findByTestId('dropdown-note');
const createComponent = (props) => {
- wrapper = shallowMount(RecentSearchesDropdownContent, {
+ wrapper = shallowMountExtended(RecentSearchesDropdownContent, {
propsData: {
allowedKeys: IssuableFilteredSearchTokenKeys.getKeys(),
items: [],
@@ -94,7 +94,7 @@ describe('Recent Searches Dropdown Content', () => {
});
it('emits requestClearRecentSearches on Clear resent searches button', () => {
- wrapper.find({ ref: 'clearButton' }).trigger('click');
+ wrapper.findByTestId('clear-button').trigger('click');
expect(onRequestClearRecentSearchesSpy).toHaveBeenCalled();
});
diff --git a/spec/frontend/filtered_search/droplab/drop_down_spec.js b/spec/frontend/filtered_search/droplab/drop_down_spec.js
index f49dbfcf79c..6fbb4394944 100644
--- a/spec/frontend/filtered_search/droplab/drop_down_spec.js
+++ b/spec/frontend/filtered_search/droplab/drop_down_spec.js
@@ -557,11 +557,11 @@ describe('DropLab DropDown', () => {
DropDown.prototype.show.call(testContext.dropdown);
});
- it('it should set .list display to block', () => {
+ it('should set .list display to block', () => {
expect(testContext.list.style.display).toBe('block');
});
- it('it should set .hidden to false', () => {
+ it('should set .hidden to false', () => {
expect(testContext.dropdown.hidden).toBe(false);
});
@@ -591,11 +591,11 @@ describe('DropLab DropDown', () => {
DropDown.prototype.hide.call(testContext.dropdown);
});
- it('it should set .list display to none', () => {
+ it('should set .list display to none', () => {
expect(testContext.list.style.display).toBe('none');
});
- it('it should set .hidden to true', () => {
+ it('should set .hidden to true', () => {
expect(testContext.dropdown.hidden).toBe(true);
});
});
@@ -648,11 +648,11 @@ describe('DropLab DropDown', () => {
DropDown.prototype.destroy.call(testContext.dropdown);
});
- it('it should call .hide', () => {
+ it('should call .hide', () => {
expect(testContext.dropdown.hide).toHaveBeenCalled();
});
- it('it should call .removeEventListener', () => {
+ it('should call .removeEventListener', () => {
expect(testContext.list.removeEventListener).toHaveBeenCalledWith(
'click',
testContext.eventWrapper.clickEvent,
diff --git a/spec/frontend/fixtures/api_merge_requests.rb b/spec/frontend/fixtures/api_merge_requests.rb
index 75bc8c8df25..7d95c506e6c 100644
--- a/spec/frontend/fixtures/api_merge_requests.rb
+++ b/spec/frontend/fixtures/api_merge_requests.rb
@@ -7,7 +7,7 @@ RSpec.describe API::MergeRequests, '(JavaScript fixtures)', type: :request do
include JavaScriptFixturesHelpers
let_it_be(:admin) { create(:admin, name: 'root') }
- let_it_be(:namespace) { create(:namespace, name: 'gitlab-test' )}
+ 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
4.times { |i| create(:merge_request, source_project: project, source_branch: "branch-#{i}") }
diff --git a/spec/frontend/fixtures/api_projects.rb b/spec/frontend/fixtures/api_projects.rb
index eada2f8e0f7..5acc1095d5c 100644
--- a/spec/frontend/fixtures/api_projects.rb
+++ b/spec/frontend/fixtures/api_projects.rb
@@ -7,7 +7,7 @@ RSpec.describe API::Projects, '(JavaScript fixtures)', type: :request do
include JavaScriptFixturesHelpers
let(:admin) { create(:admin, name: 'root') }
- let(:namespace) { create(:namespace, name: 'gitlab-test' )}
+ 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') }
diff --git a/spec/frontend/fixtures/application_settings.rb b/spec/frontend/fixtures/application_settings.rb
index a7a989f31ec..b3ce23c8cd7 100644
--- a/spec/frontend/fixtures/application_settings.rb
+++ b/spec/frontend/fixtures/application_settings.rb
@@ -8,7 +8,7 @@ RSpec.describe Admin::ApplicationSettingsController, '(JavaScript fixtures)', ty
include AdminModeHelper
let(:admin) { create(:admin) }
- let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) }
let(:project) { create(:project_empty_repo, namespace: namespace, path: 'application-settings') }
before do
diff --git a/spec/frontend/fixtures/blob.rb b/spec/frontend/fixtures/blob.rb
index b2bbdd2749e..54c5b83da3e 100644
--- a/spec/frontend/fixtures/blob.rb
+++ b/spec/frontend/fixtures/blob.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Projects::BlobController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) }
let(:project) { create(:project, :repository, namespace: namespace, path: 'branches-project') }
let(:user) { project.first_owner }
diff --git a/spec/frontend/fixtures/branches.rb b/spec/frontend/fixtures/branches.rb
index b3bb4b8873a..6cda2f0f665 100644
--- a/spec/frontend/fixtures/branches.rb
+++ b/spec/frontend/fixtures/branches.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe 'Branches (JavaScript fixtures)' do
include JavaScriptFixturesHelpers
- let_it_be(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let_it_be(:namespace) { create(:namespace, name: 'frontend-fixtures' ) }
let_it_be(:project) { create(:project, :repository, namespace: namespace, path: 'branches-project') }
let_it_be(:user) { project.first_owner }
diff --git a/spec/frontend/fixtures/clusters.rb b/spec/frontend/fixtures/clusters.rb
index 49596d98774..426a76f29e0 100644
--- a/spec/frontend/fixtures/clusters.rb
+++ b/spec/frontend/fixtures/clusters.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Projects::ClustersController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) }
let(:project) { create(:project, :repository, namespace: namespace) }
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
let(:user) { project.first_owner }
diff --git a/spec/frontend/fixtures/deploy_keys.rb b/spec/frontend/fixtures/deploy_keys.rb
index 154084e0181..24d602216d8 100644
--- a/spec/frontend/fixtures/deploy_keys.rb
+++ b/spec/frontend/fixtures/deploy_keys.rb
@@ -7,11 +7,11 @@ RSpec.describe Projects::DeployKeysController, '(JavaScript fixtures)', type: :c
include AdminModeHelper
let(:admin) { create(:admin) }
- let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) }
let(:project) { create(:project_empty_repo, namespace: namespace, path: 'todos-project') }
- let(:project2) { create(:project, :internal)}
- let(:project3) { create(:project, :internal)}
- let(:project4) { create(:project, :internal)}
+ let(:project2) { create(:project, :internal) }
+ let(:project3) { create(:project, :internal) }
+ let(:project4) { create(:project, :internal) }
before do
# Using an admin for these fixtures because they are used for verifying a frontend
diff --git a/spec/frontend/fixtures/groups.rb b/spec/frontend/fixtures/groups.rb
index ddd436b98c6..9c22ff176ff 100644
--- a/spec/frontend/fixtures/groups.rb
+++ b/spec/frontend/fixtures/groups.rb
@@ -6,7 +6,7 @@ RSpec.describe 'Groups (JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
let(:user) { create(:user) }
- let(:group) { create(:group, name: 'frontend-fixtures-group', runners_token: 'runnerstoken:intabulasreferre')}
+ let(:group) { create(:group, name: 'frontend-fixtures-group', runners_token: 'runnerstoken:intabulasreferre') }
before do
group.add_owner(user)
diff --git a/spec/frontend/fixtures/issues.rb b/spec/frontend/fixtures/issues.rb
index cde796497d4..e3d88098841 100644
--- a/spec/frontend/fixtures/issues.rb
+++ b/spec/frontend/fixtures/issues.rb
@@ -6,7 +6,7 @@ RSpec.describe Projects::IssuesController, '(JavaScript fixtures)', type: :contr
include JavaScriptFixturesHelpers
let(:user) { create(:user, feed_token: 'feedtoken:coldfeed') }
- let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) }
let(:project) { create(:project_empty_repo, namespace: namespace, path: 'issues-project') }
render_views
diff --git a/spec/frontend/fixtures/jobs.rb b/spec/frontend/fixtures/jobs.rb
index 2e15eefdce6..3657a5405a4 100644
--- a/spec/frontend/fixtures/jobs.rb
+++ b/spec/frontend/fixtures/jobs.rb
@@ -7,7 +7,7 @@ RSpec.describe 'Jobs (JavaScript fixtures)' do
include JavaScriptFixturesHelpers
include GraphqlHelpers
- let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) }
let(:project) { create(:project, :repository, namespace: namespace, path: 'builds-project') }
let(:user) { project.first_owner }
let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id) }
diff --git a/spec/frontend/fixtures/labels.rb b/spec/frontend/fixtures/labels.rb
index 6736baed199..2445c9376e2 100644
--- a/spec/frontend/fixtures/labels.rb
+++ b/spec/frontend/fixtures/labels.rb
@@ -6,7 +6,7 @@ RSpec.describe 'Labels (JavaScript fixtures)' do
include JavaScriptFixturesHelpers
let(:user) { create(:user) }
- let(:group) { create(:group, name: 'frontend-fixtures-group' )}
+ let(:group) { create(:group, name: 'frontend-fixtures-group' ) }
let(:project) { create(:project_empty_repo, namespace: group, path: 'labels-project') }
let!(:project_label_bug) { create(:label, project: project, title: 'bug', color: '#FF0000') }
diff --git a/spec/frontend/fixtures/merge_requests.rb b/spec/frontend/fixtures/merge_requests.rb
index cb4eb43b88d..cbf26a70e5f 100644
--- a/spec/frontend/fixtures/merge_requests.rb
+++ b/spec/frontend/fixtures/merge_requests.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) }
let(:project) { create(:project, :repository, namespace: namespace, path: 'merge-requests-project') }
let(:user) { project.first_owner }
diff --git a/spec/frontend/fixtures/merge_requests_diffs.rb b/spec/frontend/fixtures/merge_requests_diffs.rb
index 7f0d650b710..ff4b27844a6 100644
--- a/spec/frontend/fixtures/merge_requests_diffs.rb
+++ b/spec/frontend/fixtures/merge_requests_diffs.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Projects::MergeRequests::DiffsController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) }
let(:project) { create(:project, :repository, namespace: namespace, path: 'merge-requests-project') }
let(:user) { project.first_owner }
let(:merge_request) { create(:merge_request, source_project: project, target_project: project, description: '- [ ] Task List Item') }
diff --git a/spec/frontend/fixtures/metrics_dashboard.rb b/spec/frontend/fixtures/metrics_dashboard.rb
index d59b01b04af..7f8b3d378d3 100644
--- a/spec/frontend/fixtures/metrics_dashboard.rb
+++ b/spec/frontend/fixtures/metrics_dashboard.rb
@@ -7,7 +7,7 @@ RSpec.describe MetricsDashboard, '(JavaScript fixtures)', type: :controller do
include MetricsDashboardHelpers
let_it_be(:user) { create(:user) }
- let_it_be(:namespace) { create(:namespace, name: 'monitoring' )}
+ let_it_be(:namespace) { create(:namespace, name: 'monitoring' ) }
let_it_be(:project) { project_with_dashboard_namespace('.gitlab/dashboards/test.yml', nil, namespace: namespace) }
let_it_be(:environment) { create(:environment, id: 1, project: project) }
let_it_be(:params) { { environment: environment } }
diff --git a/spec/frontend/fixtures/pipeline_schedules.rb b/spec/frontend/fixtures/pipeline_schedules.rb
index e155d27920d..5b7a445557e 100644
--- a/spec/frontend/fixtures/pipeline_schedules.rb
+++ b/spec/frontend/fixtures/pipeline_schedules.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Projects::PipelineSchedulesController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) }
let(:project) { create(:project, :public, :repository) }
let(:user) { project.first_owner }
let!(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: user) }
diff --git a/spec/frontend/fixtures/pipelines.rb b/spec/frontend/fixtures/pipelines.rb
index 709e14183df..114db26d6a9 100644
--- a/spec/frontend/fixtures/pipelines.rb
+++ b/spec/frontend/fixtures/pipelines.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe Projects::PipelinesController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let_it_be(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let_it_be(:namespace) { create(:namespace, name: 'frontend-fixtures' ) }
let_it_be(:project) { create(:project, :repository, namespace: namespace, path: 'pipelines-project') }
let_it_be(:commit_without_author) { RepoHelpers.another_sample_commit }
diff --git a/spec/frontend/fixtures/projects.rb b/spec/frontend/fixtures/projects.rb
index fa7d61df3e8..b9c427c7505 100644
--- a/spec/frontend/fixtures/projects.rb
+++ b/spec/frontend/fixtures/projects.rb
@@ -8,7 +8,7 @@ RSpec.describe 'Projects (JavaScript fixtures)', type: :controller do
runners_token = 'runnerstoken:intabulasreferre'
- let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) }
let(:project) { create(:project, namespace: namespace, path: 'builds-project', runners_token: runners_token, avatar: fixture_file_upload('spec/fixtures/dk.png', 'image/png')) }
let(:project_with_repo) { create(:project, :repository, description: 'Code and stuff', avatar: fixture_file_upload('spec/fixtures/dk.png', 'image/png')) }
let(:project_variable_populated) { create(:project, namespace: namespace, path: 'builds-project2', runners_token: runners_token) }
diff --git a/spec/frontend/fixtures/raw.rb b/spec/frontend/fixtures/raw.rb
index b117cfea5fa..7bd5b8c5f6c 100644
--- a/spec/frontend/fixtures/raw.rb
+++ b/spec/frontend/fixtures/raw.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe 'Raw files', '(JavaScript fixtures)' do
include JavaScriptFixturesHelpers
- let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) }
let(:project) { create(:project, :repository, namespace: namespace, path: 'raw-project') }
let(:response) { @response }
diff --git a/spec/frontend/fixtures/search.rb b/spec/frontend/fixtures/search.rb
index db1ef67998f..b2da383d657 100644
--- a/spec/frontend/fixtures/search.rb
+++ b/spec/frontend/fixtures/search.rb
@@ -23,40 +23,41 @@ RSpec.describe SearchController, '(JavaScript fixtures)', type: :controller do
let(:namespace) { create(:namespace, name: 'frontend-fixtures') }
let(:project) { create(:project, :public, :repository, namespace: namespace, path: 'search-project') }
let(:blobs) do
- Kaminari.paginate_array([
- Gitlab::Search::FoundBlob.new(
- path: 'CHANGELOG',
- basename: 'CHANGELOG',
- ref: 'master',
- data: "hello\nworld\nfoo\nSend # this is the highligh\nbaz\nboo\nbat",
- project: project,
- project_id: project.id,
- startline: 2),
- Gitlab::Search::FoundBlob.new(
- path: 'CONTRIBUTING',
- basename: 'CONTRIBUTING',
- ref: 'master',
- data: "hello\nworld\nfoo\nSend # this is the highligh\nbaz\nboo\nbat",
- project: project,
- project_id: project.id,
- startline: 2),
- Gitlab::Search::FoundBlob.new(
- path: 'README',
- basename: 'README',
- ref: 'master',
- data: "foo\nSend # this is the highlight\nbaz\nboo\nbat",
- project: project,
- project_id: project.id,
- startline: 2),
- Gitlab::Search::FoundBlob.new(
- path: 'test',
- basename: 'test',
- ref: 'master',
- data: "foo\nSend # this is the highlight\nbaz\nboo\nbat",
- project: project,
- project_id: project.id,
- startline: 2)
- ],
+ Kaminari.paginate_array(
+ [
+ Gitlab::Search::FoundBlob.new(
+ path: 'CHANGELOG',
+ basename: 'CHANGELOG',
+ ref: 'master',
+ data: "hello\nworld\nfoo\nSend # this is the highligh\nbaz\nboo\nbat",
+ project: project,
+ project_id: project.id,
+ startline: 2),
+ Gitlab::Search::FoundBlob.new(
+ path: 'CONTRIBUTING',
+ basename: 'CONTRIBUTING',
+ ref: 'master',
+ data: "hello\nworld\nfoo\nSend # this is the highligh\nbaz\nboo\nbat",
+ project: project,
+ project_id: project.id,
+ startline: 2),
+ Gitlab::Search::FoundBlob.new(
+ path: 'README',
+ basename: 'README',
+ ref: 'master',
+ data: "foo\nSend # this is the highlight\nbaz\nboo\nbat",
+ project: project,
+ project_id: project.id,
+ startline: 2),
+ Gitlab::Search::FoundBlob.new(
+ path: 'test',
+ basename: 'test',
+ ref: 'master',
+ data: "foo\nSend # this is the highlight\nbaz\nboo\nbat",
+ project: project,
+ project_id: project.id,
+ startline: 2)
+ ],
total_count: 4,
limit: 4,
offset: 0)
diff --git a/spec/frontend/fixtures/snippet.rb b/spec/frontend/fixtures/snippet.rb
index f05ff3ee269..58d4bc5c1f3 100644
--- a/spec/frontend/fixtures/snippet.rb
+++ b/spec/frontend/fixtures/snippet.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe SnippetsController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) }
let(:project) { create(:project, :repository, namespace: namespace, path: 'branches-project') }
let(:user) { project.first_owner }
let(:snippet) { create(:personal_snippet, :public, title: 'snippet.md', content: '# snippet', file_name: 'snippet.md', author: user) }
diff --git a/spec/frontend/fixtures/startup_css.rb b/spec/frontend/fixtures/startup_css.rb
index cf7383fa6ca..bd2d63a1827 100644
--- a/spec/frontend/fixtures/startup_css.rb
+++ b/spec/frontend/fixtures/startup_css.rb
@@ -69,11 +69,25 @@ RSpec.describe 'Startup CSS fixtures', type: :controller do
it_behaves_like 'startup css project fixtures', 'dark'
end
- describe RegistrationsController, '(Startup CSS fixtures)', type: :controller do
+ describe SessionsController, '(Startup CSS fixtures)', type: :controller do
+ include DeviseHelpers
+
+ before do
+ set_devise_mapping(context: request)
+ end
+
it 'startup_css/sign-in.html' do
get :new
expect(response).to be_successful
end
+
+ it 'startup_css/sign-in-old.html' do
+ stub_feature_flags(restyle_login_page: false)
+
+ get :new
+
+ expect(response).to be_successful
+ end
end
end
diff --git a/spec/frontend/fixtures/todos.rb b/spec/frontend/fixtures/todos.rb
index 7dce09e8f49..d934396f803 100644
--- a/spec/frontend/fixtures/todos.rb
+++ b/spec/frontend/fixtures/todos.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe 'Todos (JavaScript fixtures)' do
include JavaScriptFixturesHelpers
- let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
+ let(:namespace) { create(:namespace, name: 'frontend-fixtures' ) }
let(:project) { create(:project_empty_repo, namespace: namespace, path: 'todos-project') }
let(:user) { project.first_owner }
let(:issue_1) { create(:issue, title: 'issue_1', project: project) }
diff --git a/spec/frontend/flash_spec.js b/spec/frontend/flash_spec.js
index 6cd32ff6b40..e26c52f0bf7 100644
--- a/spec/frontend/flash_spec.js
+++ b/spec/frontend/flash_spec.js
@@ -36,7 +36,7 @@ describe('Flash', () => {
hideFlash(el, false);
expect(el.style.opacity).toBe('');
- expect(el.style.transition).toBeFalsy();
+ expect(el.style.transition).toHaveLength(0);
});
it('removes element after transitionend', () => {
diff --git a/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js b/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js
index eef5dc86c1a..e6673fa78ec 100644
--- a/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js
+++ b/spec/frontend/frequent_items/components/frequent_items_list_item_spec.js
@@ -1,7 +1,7 @@
import { GlButton } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { trimText } from 'helpers/text_helper';
import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import frequentItemsListItemComponent from '~/frequent_items/components/frequent_items_list_item.vue';
@@ -16,18 +16,18 @@ describe('FrequentItemsListItemComponent', () => {
let trackingSpy;
let store;
- const findTitle = () => wrapper.find({ ref: 'frequentItemsItemTitle' });
+ const findTitle = () => wrapper.findByTestId('frequent-items-item-title');
const findAvatar = () => wrapper.findComponent(ProjectAvatar);
- const findAllTitles = () => wrapper.findAll({ ref: 'frequentItemsItemTitle' });
- const findNamespace = () => wrapper.find({ ref: 'frequentItemsItemNamespace' });
+ const findAllTitles = () => wrapper.findAllByTestId('frequent-items-item-title');
+ const findNamespace = () => wrapper.findByTestId('frequent-items-item-namespace');
const findAllButtons = () => wrapper.findAllComponents(GlButton);
- const findAllNamespace = () => wrapper.findAll({ ref: 'frequentItemsItemNamespace' });
+ const findAllNamespace = () => wrapper.findAllByTestId('frequent-items-item-namespace');
const findAllAvatars = () => wrapper.findAllComponents(ProjectAvatar);
const findAllMetadataContainers = () =>
- wrapper.findAll({ ref: 'frequentItemsItemMetadataContainer' });
+ wrapper.findAllByTestId('frequent-items-item-metadata-container');
const createComponent = (props = {}) => {
- wrapper = shallowMount(frequentItemsListItemComponent, {
+ wrapper = shallowMountExtended(frequentItemsListItemComponent, {
store,
propsData: {
itemId: mockProject.id,
diff --git a/spec/frontend/frequent_items/components/frequent_items_list_spec.js b/spec/frontend/frequent_items/components/frequent_items_list_spec.js
index beaab1913d0..9f08a432a3d 100644
--- a/spec/frontend/frequent_items/components/frequent_items_list_spec.js
+++ b/spec/frontend/frequent_items/components/frequent_items_list_spec.js
@@ -1,6 +1,6 @@
-import { mount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
import frequentItemsListComponent from '~/frequent_items/components/frequent_items_list.vue';
import frequentItemsListItemComponent from '~/frequent_items/components/frequent_items_list_item.vue';
import { createStore } from '~/frequent_items/store';
@@ -12,7 +12,7 @@ describe('FrequentItemsListComponent', () => {
let wrapper;
const createComponent = (props = {}) => {
- wrapper = mount(frequentItemsListComponent, {
+ wrapper = mountExtended(frequentItemsListComponent, {
store: createStore(),
propsData: {
namespace: 'projects',
@@ -94,8 +94,8 @@ describe('FrequentItemsListComponent', () => {
await nextTick();
expect(wrapper.classes('frequent-items-list-container')).toBe(true);
- expect(wrapper.findAll({ ref: 'frequentItemsList' })).toHaveLength(1);
- expect(wrapper.findAll(frequentItemsListItemComponent)).toHaveLength(5);
+ expect(wrapper.findAllByTestId('frequent-items-list')).toHaveLength(1);
+ expect(wrapper.findAllComponents(frequentItemsListItemComponent)).toHaveLength(5);
});
it('should render component element with empty message', async () => {
@@ -105,7 +105,7 @@ describe('FrequentItemsListComponent', () => {
await nextTick();
expect(wrapper.vm.$el.querySelectorAll('li.section-empty')).toHaveLength(1);
- expect(wrapper.findAll(frequentItemsListItemComponent)).toHaveLength(0);
+ expect(wrapper.findAllComponents(frequentItemsListItemComponent)).toHaveLength(0);
});
});
});
diff --git a/spec/frontend/frequent_items/components/frequent_items_search_input_spec.js b/spec/frontend/frequent_items/components/frequent_items_search_input_spec.js
index d0a4cf70f5f..94fc97b82c2 100644
--- a/spec/frontend/frequent_items/components/frequent_items_search_input_spec.js
+++ b/spec/frontend/frequent_items/components/frequent_items_search_input_spec.js
@@ -23,7 +23,7 @@ describe('FrequentItemsSearchInputComponent', () => {
},
});
- const findSearchBoxByType = () => wrapper.find(GlSearchBoxByType);
+ const findSearchBoxByType = () => wrapper.findComponent(GlSearchBoxByType);
beforeEach(() => {
store = createStore();
diff --git a/spec/frontend/frequent_items/utils_spec.js b/spec/frontend/frequent_items/utils_spec.js
index 33c655a6ffd..8d4c89bd48f 100644
--- a/spec/frontend/frequent_items/utils_spec.js
+++ b/spec/frontend/frequent_items/utils_spec.js
@@ -10,25 +10,25 @@ import { mockProject, unsortedFrequentItems, sortedFrequentItems } from './mock_
describe('Frequent Items utils spec', () => {
describe('isMobile', () => {
- it('returns true when the screen is medium ', () => {
+ it('returns true when the screen is medium', () => {
jest.spyOn(bp, 'getBreakpointSize').mockReturnValue('md');
expect(isMobile()).toBe(true);
});
- it('returns true when the screen is small ', () => {
+ it('returns true when the screen is small', () => {
jest.spyOn(bp, 'getBreakpointSize').mockReturnValue('sm');
expect(isMobile()).toBe(true);
});
- it('returns true when the screen is extra-small ', () => {
+ it('returns true when the screen is extra-small', () => {
jest.spyOn(bp, 'getBreakpointSize').mockReturnValue('xs');
expect(isMobile()).toBe(true);
});
- it('returns false when the screen is larger than medium ', () => {
+ it('returns false when the screen is larger than medium', () => {
jest.spyOn(bp, 'getBreakpointSize').mockReturnValue('lg');
expect(isMobile()).toBe(false);
diff --git a/spec/frontend/google_cloud/databases/panel_spec.js b/spec/frontend/google_cloud/databases/panel_spec.js
index 490c0136651..e6a0d74f348 100644
--- a/spec/frontend/google_cloud/databases/panel_spec.js
+++ b/spec/frontend/google_cloud/databases/panel_spec.js
@@ -2,6 +2,8 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import Panel from '~/google_cloud/databases/panel.vue';
import IncubationBanner from '~/google_cloud/components/incubation_banner.vue';
import GoogleCloudMenu from '~/google_cloud/components/google_cloud_menu.vue';
+import ServiceTable from '~/google_cloud/databases/service_table.vue';
+import InstanceTable from '~/google_cloud/databases/cloudsql/instance_table.vue';
describe('google_cloud/databases/panel', () => {
let wrapper;
@@ -10,6 +12,11 @@ describe('google_cloud/databases/panel', () => {
configurationUrl: 'configuration-url',
deploymentsUrl: 'deployments-url',
databasesUrl: 'databases-url',
+ cloudsqlPostgresUrl: 'cloudsql-postgres-url',
+ cloudsqlMysqlUrl: 'cloudsql-mysql-url',
+ cloudsqlSqlserverUrl: 'cloudsql-sqlserver-url',
+ cloudsqlInstances: [],
+ emptyIllustrationUrl: 'empty-illustration-url',
};
beforeEach(() => {
@@ -33,4 +40,14 @@ describe('google_cloud/databases/panel', () => {
expect(target.props('deploymentsUrl')).toBe(props.deploymentsUrl);
expect(target.props('databasesUrl')).toBe(props.databasesUrl);
});
+
+ it('contains Databases service table', () => {
+ const target = wrapper.findComponent(ServiceTable);
+ expect(target.exists()).toBe(true);
+ });
+
+ it('contains CloudSQL instance table', () => {
+ const target = wrapper.findComponent(InstanceTable);
+ expect(target.exists()).toBe(true);
+ });
});
diff --git a/spec/frontend/google_tag_manager/index_spec.js b/spec/frontend/google_tag_manager/index_spec.js
index 6a7eb1fd9f1..ec9e1ef8e5f 100644
--- a/spec/frontend/google_tag_manager/index_spec.js
+++ b/spec/frontend/google_tag_manager/index_spec.js
@@ -8,13 +8,13 @@ import {
trackSaasTrialSubmit,
trackSaasTrialSkip,
trackSaasTrialGroup,
- trackSaasTrialProject,
trackSaasTrialGetStarted,
trackTrialAcceptTerms,
trackCheckout,
trackTransaction,
trackAddToCartUsageTab,
getNamespaceId,
+ trackCompanyForm,
} from '~/google_tag_manager';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import { logError } from '~/lib/logger';
@@ -149,9 +149,6 @@ describe('~/google_tag_manager/index', () => {
createTestCase(trackSaasTrialGroup, {
forms: [{ cls: 'js-saas-trial-group', expectation: { event: 'saasTrialGroup' } }],
}),
- createTestCase(trackSaasTrialProject, {
- forms: [{ id: 'new_project', expectation: { event: 'saasTrialProject' } }],
- }),
createTestCase(trackProjectImport, {
links: [
{
@@ -440,6 +437,34 @@ describe('~/google_tag_manager/index', () => {
});
});
});
+
+ describe('when trackCompanyForm is invoked', () => {
+ it('with an ultimate trial', () => {
+ expect(spy).not.toHaveBeenCalled();
+
+ trackCompanyForm('ultimate_trial');
+
+ expect(spy).toHaveBeenCalledTimes(1);
+ expect(spy).toHaveBeenCalledWith({
+ event: 'aboutYourCompanyFormSubmit',
+ aboutYourCompanyType: 'ultimate_trial',
+ });
+ expect(logError).not.toHaveBeenCalled();
+ });
+
+ it('with a free account', () => {
+ expect(spy).not.toHaveBeenCalled();
+
+ trackCompanyForm('free_account');
+
+ expect(spy).toHaveBeenCalledTimes(1);
+ expect(spy).toHaveBeenCalledWith({
+ event: 'aboutYourCompanyFormSubmit',
+ aboutYourCompanyType: 'free_account',
+ });
+ expect(logError).not.toHaveBeenCalled();
+ });
+ });
});
describe.each([
@@ -452,11 +477,11 @@ describe('~/google_tag_manager/index', () => {
});
it('no ops', () => {
- setHTMLFixture(createHTML({ forms: [{ id: 'new_project' }] }));
+ setHTMLFixture(createHTML({ forms: [{ cls: 'js-saas-trial-group' }] }));
- trackSaasTrialProject();
+ trackSaasTrialGroup();
- triggerEvent('#new_project', 'submit');
+ triggerEvent('.js-saas-trial-group', 'submit');
expect(spy).not.toHaveBeenCalled();
expect(logError).not.toHaveBeenCalled();
@@ -477,11 +502,11 @@ describe('~/google_tag_manager/index', () => {
});
it('logs error', () => {
- setHTMLFixture(createHTML({ forms: [{ id: 'new_project' }] }));
+ setHTMLFixture(createHTML({ forms: [{ cls: 'js-saas-trial-group' }] }));
- trackSaasTrialProject();
+ trackSaasTrialGroup();
- triggerEvent('#new_project', 'submit');
+ triggerEvent('.js-saas-trial-group', 'submit');
expect(logError).toHaveBeenCalledWith(
'Unexpected error while pushing to dataLayer',
diff --git a/spec/frontend/groups/components/app_spec.js b/spec/frontend/groups/components/app_spec.js
index a6bbea648d2..a4a7530184d 100644
--- a/spec/frontend/groups/components/app_spec.js
+++ b/spec/frontend/groups/components/app_spec.js
@@ -40,7 +40,7 @@ describe('AppComponent', () => {
const store = new GroupsStore({ hideProjects: false });
const service = new GroupsService(mockEndpoint);
- const createShallowComponent = ({ propsData = {}, provide = {} } = {}) => {
+ const createShallowComponent = ({ propsData = {} } = {}) => {
store.state.pageInfo = mockPageInfo;
wrapper = shallowMount(appComponent, {
propsData: {
@@ -53,10 +53,6 @@ describe('AppComponent', () => {
mocks: {
$toast,
},
- provide: {
- renderEmptyState: false,
- ...provide,
- },
});
vm = wrapper.vm;
};
@@ -402,8 +398,7 @@ describe('AppComponent', () => {
({ action, groups, fromSearch, renderEmptyState, expected }) => {
it(expected ? 'renders empty state' : 'does not render empty state', async () => {
createShallowComponent({
- propsData: { action },
- provide: { renderEmptyState },
+ propsData: { action, renderEmptyState },
});
vm.updateGroups(groups, fromSearch);
@@ -420,7 +415,6 @@ describe('AppComponent', () => {
it('renders legacy empty state', async () => {
createShallowComponent({
propsData: { action: 'subgroups_and_projects' },
- provide: { renderEmptyState: false },
});
vm.updateGroups([], false);
@@ -481,7 +475,7 @@ describe('AppComponent', () => {
it('should render loading icon', async () => {
vm.isLoading = true;
await nextTick();
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
it('should render groups tree', async () => {
@@ -494,7 +488,7 @@ describe('AppComponent', () => {
it('renders modal confirmation dialog', () => {
createShallowComponent();
- const findGlModal = wrapper.find(GlModal);
+ const findGlModal = wrapper.findComponent(GlModal);
expect(findGlModal.exists()).toBe(true);
expect(findGlModal.attributes('title')).toBe('Are you sure?');
diff --git a/spec/frontend/groups/components/empty_state_spec.js b/spec/frontend/groups/components/empty_state_spec.js
index c0e71e814d0..fbeaa32b1ec 100644
--- a/spec/frontend/groups/components/empty_state_spec.js
+++ b/spec/frontend/groups/components/empty_state_spec.js
@@ -68,7 +68,7 @@ describe('EmptyState', () => {
it('renders empty state', () => {
createComponent({ provide: { canCreateSubgroups: false, canCreateProjects: false } });
- expect(wrapper.find(GlEmptyState).props()).toMatchObject({
+ 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/group_item_spec.js b/spec/frontend/groups/components/group_item_spec.js
index 9906f62878f..3aa66644c19 100644
--- a/spec/frontend/groups/components/group_item_spec.js
+++ b/spec/frontend/groups/components/group_item_spec.js
@@ -8,9 +8,9 @@ import { getGroupItemMicrodata } from '~/groups/store/utils';
import * as urlUtilities from '~/lib/utils/url_utility';
import { ITEM_TYPE } from '~/groups/constants';
import {
- VISIBILITY_LEVEL_PRIVATE,
- VISIBILITY_LEVEL_INTERNAL,
- VISIBILITY_LEVEL_PUBLIC,
+ VISIBILITY_LEVEL_PRIVATE_STRING,
+ VISIBILITY_LEVEL_INTERNAL_STRING,
+ VISIBILITY_LEVEL_PUBLIC_STRING,
} from '~/visibility_level/constants';
import { helpPagePath } from '~/helpers/help_page_helper';
import { mountExtended, extendedWrapper } from 'helpers/vue_test_utils_helper';
@@ -19,7 +19,7 @@ import { mockParentGroupItem, mockChildren } from '../mock_data';
const createComponent = (
propsData = { group: mockParentGroupItem, parentGroup: mockChildren[0] },
provide = {
- currentGroupVisibility: VISIBILITY_LEVEL_PRIVATE,
+ currentGroupVisibility: VISIBILITY_LEVEL_PRIVATE_STRING,
},
) => {
return mountExtended(GroupItem, {
@@ -274,7 +274,7 @@ describe('GroupItemComponent', () => {
${'itemscope'} | ${'itemscope'}
${'itemtype'} | ${'https://schema.org/Organization'}
${'itemprop'} | ${'subOrganization'}
- `('it does set correct $attr', ({ attr, value } = {}) => {
+ `('does set correct $attr', ({ attr, value } = {}) => {
expect(wrapper.attributes(attr)).toBe(value);
});
@@ -283,7 +283,7 @@ describe('GroupItemComponent', () => {
${'img'} | ${'logo'}
${'[data-testid="group-name"]'} | ${'name'}
${'[data-testid="group-description"]'} | ${'description'}
- `('it does set correct $selector', ({ selector, propValue } = {}) => {
+ `('does set correct $selector', ({ selector, propValue } = {}) => {
expect(wrapper.find(selector).attributes('itemprop')).toBe(propValue);
});
});
@@ -320,16 +320,16 @@ describe('GroupItemComponent', () => {
describe('when showing projects', () => {
describe.each`
- itemVisibility | currentGroupVisibility | isPopoverShown
- ${VISIBILITY_LEVEL_PRIVATE} | ${VISIBILITY_LEVEL_PUBLIC} | ${false}
- ${VISIBILITY_LEVEL_INTERNAL} | ${VISIBILITY_LEVEL_PUBLIC} | ${false}
- ${VISIBILITY_LEVEL_PUBLIC} | ${VISIBILITY_LEVEL_PUBLIC} | ${false}
- ${VISIBILITY_LEVEL_PRIVATE} | ${VISIBILITY_LEVEL_PRIVATE} | ${false}
- ${VISIBILITY_LEVEL_INTERNAL} | ${VISIBILITY_LEVEL_PRIVATE} | ${true}
- ${VISIBILITY_LEVEL_PUBLIC} | ${VISIBILITY_LEVEL_PRIVATE} | ${true}
- ${VISIBILITY_LEVEL_PRIVATE} | ${VISIBILITY_LEVEL_INTERNAL} | ${false}
- ${VISIBILITY_LEVEL_INTERNAL} | ${VISIBILITY_LEVEL_INTERNAL} | ${false}
- ${VISIBILITY_LEVEL_PUBLIC} | ${VISIBILITY_LEVEL_INTERNAL} | ${true}
+ itemVisibility | currentGroupVisibility | isPopoverShown
+ ${VISIBILITY_LEVEL_PRIVATE_STRING} | ${VISIBILITY_LEVEL_PUBLIC_STRING} | ${false}
+ ${VISIBILITY_LEVEL_INTERNAL_STRING} | ${VISIBILITY_LEVEL_PUBLIC_STRING} | ${false}
+ ${VISIBILITY_LEVEL_PUBLIC_STRING} | ${VISIBILITY_LEVEL_PUBLIC_STRING} | ${false}
+ ${VISIBILITY_LEVEL_PRIVATE_STRING} | ${VISIBILITY_LEVEL_PRIVATE_STRING} | ${false}
+ ${VISIBILITY_LEVEL_INTERNAL_STRING} | ${VISIBILITY_LEVEL_PRIVATE_STRING} | ${true}
+ ${VISIBILITY_LEVEL_PUBLIC_STRING} | ${VISIBILITY_LEVEL_PRIVATE_STRING} | ${true}
+ ${VISIBILITY_LEVEL_PRIVATE_STRING} | ${VISIBILITY_LEVEL_INTERNAL_STRING} | ${false}
+ ${VISIBILITY_LEVEL_INTERNAL_STRING} | ${VISIBILITY_LEVEL_INTERNAL_STRING} | ${false}
+ ${VISIBILITY_LEVEL_PUBLIC_STRING} | ${VISIBILITY_LEVEL_INTERNAL_STRING} | ${true}
`(
'when item visibility is $itemVisibility and parent group visibility is $currentGroupVisibility',
({ itemVisibility, currentGroupVisibility, isPopoverShown }) => {
@@ -374,7 +374,7 @@ describe('GroupItemComponent', () => {
wrapper = createComponent({
group: {
...mockParentGroupItem,
- visibility: VISIBILITY_LEVEL_PUBLIC,
+ visibility: VISIBILITY_LEVEL_PUBLIC_STRING,
type: ITEM_TYPE.PROJECT,
},
parentGroup: mockChildren[0],
diff --git a/spec/frontend/groups/components/groups_spec.js b/spec/frontend/groups/components/groups_spec.js
index 6c1eb373b7e..866868eff36 100644
--- a/spec/frontend/groups/components/groups_spec.js
+++ b/spec/frontend/groups/components/groups_spec.js
@@ -6,7 +6,7 @@ import GroupItemComponent from '~/groups/components/group_item.vue';
import PaginationLinks from '~/vue_shared/components/pagination_links.vue';
import GroupsComponent from '~/groups/components/groups.vue';
import eventHub from '~/groups/event_hub';
-import { VISIBILITY_LEVEL_PRIVATE } from '~/visibility_level/constants';
+import { VISIBILITY_LEVEL_PRIVATE_STRING } from '~/visibility_level/constants';
import { mockGroups, mockPageInfo } from '../mock_data';
describe('GroupsComponent', () => {
@@ -26,7 +26,7 @@ describe('GroupsComponent', () => {
...propsData,
},
provide: {
- currentGroupVisibility: VISIBILITY_LEVEL_PRIVATE,
+ currentGroupVisibility: VISIBILITY_LEVEL_PRIVATE_STRING,
},
});
};
diff --git a/spec/frontend/groups/components/invite_members_banner_spec.js b/spec/frontend/groups/components/invite_members_banner_spec.js
index 1924f400861..d25b45bd662 100644
--- a/spec/frontend/groups/components/invite_members_banner_spec.js
+++ b/spec/frontend/groups/components/invite_members_banner_spec.js
@@ -71,7 +71,7 @@ describe('InviteMembersBanner', () => {
describe('when the button is clicked', () => {
beforeEach(() => {
jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- wrapper.find(GlBanner).vm.$emit('primary');
+ wrapper.findComponent(GlBanner).vm.$emit('primary');
});
it('calls openModal through the eventHub', () => {
@@ -92,7 +92,7 @@ describe('InviteMembersBanner', () => {
mockAxios.onPost(provide.calloutsPath).replyOnce(200);
const dismissEvent = 'invite_members_banner_dismissed';
- wrapper.find(GlBanner).vm.$emit('close');
+ wrapper.findComponent(GlBanner).vm.$emit('close');
expect(trackingSpy).toHaveBeenCalledWith(trackCategory, dismissEvent, {
label: provide.trackLabel,
@@ -102,7 +102,7 @@ describe('InviteMembersBanner', () => {
describe('rendering', () => {
const findBanner = () => {
- return wrapper.find(GlBanner);
+ return wrapper.findComponent(GlBanner);
};
beforeEach(() => {
@@ -132,16 +132,16 @@ describe('InviteMembersBanner', () => {
});
it('should render the banner when not dismissed', () => {
- expect(wrapper.find(GlBanner).exists()).toBe(true);
+ expect(wrapper.findComponent(GlBanner).exists()).toBe(true);
});
it('should close the banner when dismiss is clicked', async () => {
mockAxios.onPost(provide.calloutsPath).replyOnce(200);
- expect(wrapper.find(GlBanner).exists()).toBe(true);
- wrapper.find(GlBanner).vm.$emit('close');
+ expect(wrapper.findComponent(GlBanner).exists()).toBe(true);
+ wrapper.findComponent(GlBanner).vm.$emit('close');
await nextTick();
- expect(wrapper.find(GlBanner).exists()).toBe(false);
+ expect(wrapper.findComponent(GlBanner).exists()).toBe(false);
});
});
});
diff --git a/spec/frontend/groups/components/item_caret_spec.js b/spec/frontend/groups/components/item_caret_spec.js
index 4bf92bb5642..2333f04bb2e 100644
--- a/spec/frontend/groups/components/item_caret_spec.js
+++ b/spec/frontend/groups/components/item_caret_spec.js
@@ -22,8 +22,8 @@ describe('ItemCaret', () => {
}
});
- const findAllGlIcons = () => wrapper.findAll(GlIcon);
- const findGlIcon = () => wrapper.find(GlIcon);
+ const findAllGlIcons = () => wrapper.findAllComponents(GlIcon);
+ const findGlIcon = () => wrapper.findComponent(GlIcon);
describe('template', () => {
it('renders component template correctly', () => {
diff --git a/spec/frontend/groups/components/item_stats_spec.js b/spec/frontend/groups/components/item_stats_spec.js
index fdc267bc14a..0c2912adc66 100644
--- a/spec/frontend/groups/components/item_stats_spec.js
+++ b/spec/frontend/groups/components/item_stats_spec.js
@@ -24,7 +24,7 @@ describe('ItemStats', () => {
}
});
- const findItemStatsValue = () => wrapper.find(ItemStatsValue);
+ const findItemStatsValue = () => wrapper.findComponent(ItemStatsValue);
describe('template', () => {
it('renders component container element correctly', () => {
diff --git a/spec/frontend/groups/components/item_stats_value_spec.js b/spec/frontend/groups/components/item_stats_value_spec.js
index 98186120a81..b9db83c7dd7 100644
--- a/spec/frontend/groups/components/item_stats_value_spec.js
+++ b/spec/frontend/groups/components/item_stats_value_spec.js
@@ -25,7 +25,7 @@ describe('ItemStatsValue', () => {
}
});
- const findGlIcon = () => wrapper.find(GlIcon);
+ const findGlIcon = () => wrapper.findComponent(GlIcon);
const findStatValue = () => wrapper.find('[data-testid="itemStatValue"]');
describe('template', () => {
diff --git a/spec/frontend/groups/components/item_type_icon_spec.js b/spec/frontend/groups/components/item_type_icon_spec.js
index f3652f1a410..aa00e82150b 100644
--- a/spec/frontend/groups/components/item_type_icon_spec.js
+++ b/spec/frontend/groups/components/item_type_icon_spec.js
@@ -23,7 +23,7 @@ describe('ItemTypeIcon', () => {
}
});
- const findGlIcon = () => wrapper.find(GlIcon);
+ const findGlIcon = () => wrapper.findComponent(GlIcon);
describe('template', () => {
it('renders component template correctly', () => {
diff --git a/spec/frontend/groups/components/overview_tabs_spec.js b/spec/frontend/groups/components/overview_tabs_spec.js
new file mode 100644
index 00000000000..352bf25b84f
--- /dev/null
+++ b/spec/frontend/groups/components/overview_tabs_spec.js
@@ -0,0 +1,187 @@
+import { GlTab } from '@gitlab/ui';
+import { 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 GroupsStore from '~/groups/store/groups_store';
+import GroupsService from '~/groups/service/groups_service';
+import { createRouter } from '~/groups/init_overview_tabs';
+import {
+ ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
+ ACTIVE_TAB_SHARED,
+ ACTIVE_TAB_ARCHIVED,
+} from '~/groups/constants';
+import axios from '~/lib/utils/axios_utils';
+
+const router = createRouter();
+
+describe('OverviewTabs', () => {
+ let wrapper;
+
+ const endpoints = {
+ subgroups_and_projects: '/groups/foobar/-/children.json',
+ shared: '/groups/foobar/-/shared_projects.json',
+ archived: '/groups/foobar/-/children.json?archived=only',
+ };
+
+ const routerMock = {
+ push: jest.fn(),
+ };
+
+ const createComponent = async ({
+ route = { name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, params: { group: 'foo/bar/baz' } },
+ } = {}) => {
+ wrapper = mountExtended(OverviewTabs, {
+ router,
+ provide: {
+ endpoints,
+ },
+ mocks: { $route: route, $router: routerMock },
+ });
+
+ await nextTick();
+ };
+
+ const findTabPanels = () => wrapper.findAllComponents(GlTab);
+ const findTab = (name) => wrapper.findByRole('tab', { name });
+ const findSelectedTab = () => wrapper.findByRole('tab', { selected: true });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ beforeEach(async () => {
+ // eslint-disable-next-line no-new
+ new AxiosMockAdapter(axios);
+ });
+
+ it('renders `Subgroups and projects` tab with `GroupsApp` component', async () => {
+ await createComponent();
+
+ const tabPanel = findTabPanels().at(0);
+
+ expect(tabPanel.vm.$attrs).toMatchObject({
+ title: OverviewTabs.i18n[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS],
+ lazy: false,
+ });
+ expect(tabPanel.findComponent(GroupsApp).props()).toMatchObject({
+ action: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
+ store: new GroupsStore({ showSchemaMarkup: true }),
+ service: new GroupsService(endpoints[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS]),
+ hideProjects: false,
+ renderEmptyState: true,
+ });
+ });
+
+ it('renders `Shared projects` tab and renders `GroupsApp` component after clicking tab', async () => {
+ await createComponent();
+
+ const tabPanel = findTabPanels().at(1);
+
+ expect(tabPanel.vm.$attrs).toMatchObject({
+ title: OverviewTabs.i18n[ACTIVE_TAB_SHARED],
+ lazy: true,
+ });
+
+ await findTab(OverviewTabs.i18n[ACTIVE_TAB_SHARED]).trigger('click');
+
+ expect(tabPanel.findComponent(GroupsApp).props()).toMatchObject({
+ action: ACTIVE_TAB_SHARED,
+ store: new GroupsStore(),
+ service: new GroupsService(endpoints[ACTIVE_TAB_SHARED]),
+ hideProjects: false,
+ renderEmptyState: false,
+ });
+
+ expect(tabPanel.vm.$attrs.lazy).toBe(false);
+ });
+
+ it('renders `Archived projects` tab and renders `GroupsApp` component after clicking tab', async () => {
+ await createComponent();
+
+ const tabPanel = findTabPanels().at(2);
+
+ expect(tabPanel.vm.$attrs).toMatchObject({
+ title: OverviewTabs.i18n[ACTIVE_TAB_ARCHIVED],
+ lazy: true,
+ });
+
+ await findTab(OverviewTabs.i18n[ACTIVE_TAB_ARCHIVED]).trigger('click');
+
+ expect(tabPanel.findComponent(GroupsApp).props()).toMatchObject({
+ action: ACTIVE_TAB_ARCHIVED,
+ store: new GroupsStore(),
+ service: new GroupsService(endpoints[ACTIVE_TAB_ARCHIVED]),
+ hideProjects: false,
+ renderEmptyState: false,
+ });
+
+ expect(tabPanel.vm.$attrs.lazy).toBe(false);
+ });
+
+ describe.each([
+ [
+ { name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, params: { group: 'foo/bar/baz' } },
+ OverviewTabs.i18n[ACTIVE_TAB_SHARED],
+ {
+ name: ACTIVE_TAB_SHARED,
+ params: { group: ['foo', 'bar', 'baz'] },
+ },
+ ],
+ [
+ { name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, params: { group: ['foo', 'bar', 'baz'] } },
+ OverviewTabs.i18n[ACTIVE_TAB_SHARED],
+ {
+ name: ACTIVE_TAB_SHARED,
+ params: { group: ['foo', 'bar', 'baz'] },
+ },
+ ],
+ [
+ { name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, params: { group: 'foo' } },
+ OverviewTabs.i18n[ACTIVE_TAB_SHARED],
+ {
+ name: ACTIVE_TAB_SHARED,
+ params: { group: ['foo'] },
+ },
+ ],
+ [
+ { name: ACTIVE_TAB_SHARED, params: { group: 'foo/bar' } },
+ OverviewTabs.i18n[ACTIVE_TAB_ARCHIVED],
+ {
+ name: ACTIVE_TAB_ARCHIVED,
+ params: { group: ['foo', 'bar'] },
+ },
+ ],
+ [
+ { name: ACTIVE_TAB_SHARED, params: { group: 'foo/bar' } },
+ OverviewTabs.i18n[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS],
+ {
+ name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS,
+ params: { group: ['foo', 'bar'] },
+ },
+ ],
+ [
+ { name: ACTIVE_TAB_ARCHIVED, params: { group: ['foo'] } },
+ OverviewTabs.i18n[ACTIVE_TAB_SHARED],
+ {
+ name: ACTIVE_TAB_SHARED,
+ params: { group: ['foo'] },
+ },
+ ],
+ ])('when current route is %j', (currentRoute, tabToClick, expectedRoute) => {
+ beforeEach(async () => {
+ await createComponent({ route: currentRoute });
+ });
+
+ it(`sets ${OverviewTabs.i18n[currentRoute.name]} as active tab`, () => {
+ expect(findSelectedTab().text()).toBe(OverviewTabs.i18n[currentRoute.name]);
+ });
+
+ it(`pushes expected route when ${tabToClick} tab is clicked`, async () => {
+ await findTab(tabToClick).trigger('click');
+
+ expect(routerMock.push).toHaveBeenCalledWith(expectedRoute);
+ });
+ });
+});
diff --git a/spec/frontend/groups/components/visibility_level_dropdown_spec.js b/spec/frontend/groups/components/visibility_level_dropdown_spec.js
deleted file mode 100644
index 61b7bbb0833..00000000000
--- a/spec/frontend/groups/components/visibility_level_dropdown_spec.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Component from '~/groups/components/visibility_level_dropdown.vue';
-
-describe('Visibility Level Dropdown', () => {
- let wrapper;
-
- const options = [
- { level: 0, label: 'Private', description: 'Private description' },
- { level: 20, label: 'Public', description: 'Public description' },
- ];
- const defaultLevel = 0;
-
- const createComponent = (propsData) => {
- wrapper = shallowMount(Component, {
- propsData,
- });
- };
-
- beforeEach(() => {
- createComponent({
- visibilityLevelOptions: options,
- defaultLevel,
- });
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- const hiddenInputValue = () =>
- wrapper.find("input[name='group[visibility_level]']").attributes('value');
- const dropdownText = () => wrapper.find(GlDropdown).props('text');
- const findDropdownItems = () =>
- wrapper.findAll(GlDropdownItem).wrappers.map((option) => ({
- text: option.text(),
- secondaryText: option.props('secondaryText'),
- }));
-
- describe('Default values', () => {
- it('sets the value of the hidden input to the default value', () => {
- expect(hiddenInputValue()).toBe(options[0].level.toString());
- });
-
- it('sets the text of the dropdown to the default value', () => {
- expect(dropdownText()).toBe(options[0].label);
- });
-
- it('shows all dropdown options', () => {
- expect(findDropdownItems()).toEqual(
- options.map(({ label, description }) => ({ text: label, secondaryText: description })),
- );
- });
- });
-
- describe('Selecting an option', () => {
- beforeEach(() => {
- wrapper.findAll(GlDropdownItem).at(1).vm.$emit('click');
- });
-
- it('sets the value of the hidden input to the selected value', () => {
- expect(hiddenInputValue()).toBe(options[1].level.toString());
- });
-
- it('sets the text of the dropdown to the selected value', () => {
- expect(dropdownText()).toBe(options[1].label);
- });
- });
-});
diff --git a/spec/frontend/header_search/init_spec.js b/spec/frontend/header_search/init_spec.js
index 9515ca8c812..40c1843d461 100644
--- a/spec/frontend/header_search/init_spec.js
+++ b/spec/frontend/header_search/init_spec.js
@@ -24,10 +24,10 @@ describe('Header Search EventListener', () => {
const addEventListenerSpy = jest.spyOn(searchInputBox, 'addEventListener');
initHeaderSearch();
- expect(addEventListenerSpy).toBeCalledTimes(2);
+ expect(addEventListenerSpy).toHaveBeenCalledTimes(2);
});
- it('removes event listener ', async () => {
+ it('removes event listener', async () => {
const searchInputBox = document?.querySelector('#search');
const removeEventListenerSpy = jest.spyOn(searchInputBox, 'removeEventListener');
jest.mock('~/header_search', () => ({ initHeaderSearchApp: jest.fn() }));
@@ -39,7 +39,7 @@ describe('Header Search EventListener', () => {
[cleanEventListeners],
);
- expect(removeEventListenerSpy).toBeCalledTimes(2);
+ expect(removeEventListenerSpy).toHaveBeenCalledTimes(2);
});
it('attaches new vue dropdown when feature flag is enabled', async () => {
@@ -53,7 +53,7 @@ describe('Header Search EventListener', () => {
() => {},
);
- expect(mockVueApp).toBeCalled();
+ expect(mockVueApp).toHaveBeenCalled();
});
it('attaches old vue dropdown when feature flag is disabled', async () => {
@@ -69,6 +69,6 @@ describe('Header Search EventListener', () => {
() => {},
);
- expect(mockLegacyApp).toBeCalled();
+ expect(mockLegacyApp).toHaveBeenCalled();
});
});
diff --git a/spec/frontend/header_search/mock_data.js b/spec/frontend/header_search/mock_data.js
index 8ccd7fb17e3..3a8624ad9dd 100644
--- a/spec/frontend/header_search/mock_data.js
+++ b/spec/frontend/header_search/mock_data.js
@@ -223,6 +223,20 @@ export const MOCK_AUTOCOMPLETE_OPTIONS = [
export const MOCK_GROUPED_AUTOCOMPLETE_OPTIONS = [
{
+ category: 'Groups',
+ data: [
+ {
+ category: 'Groups',
+ html_id: 'autocomplete-Groups-1',
+
+ id: 1,
+ label: 'Gitlab Org / MockGroup1',
+ value: 'MockGroup1',
+ url: 'group/1',
+ },
+ ],
+ },
+ {
category: 'Projects',
data: [
{
@@ -246,20 +260,6 @@ export const MOCK_GROUPED_AUTOCOMPLETE_OPTIONS = [
],
},
{
- category: 'Groups',
- data: [
- {
- category: 'Groups',
- html_id: 'autocomplete-Groups-1',
-
- id: 1,
- label: 'Gitlab Org / MockGroup1',
- value: 'MockGroup1',
- url: 'group/1',
- },
- ],
- },
- {
category: 'Help',
data: [
{
@@ -275,6 +275,14 @@ export const MOCK_GROUPED_AUTOCOMPLETE_OPTIONS = [
export const MOCK_SORTED_AUTOCOMPLETE_OPTIONS = [
{
+ category: 'Groups',
+ html_id: 'autocomplete-Groups-1',
+ id: 1,
+ label: 'Gitlab Org / MockGroup1',
+ value: 'MockGroup1',
+ url: 'group/1',
+ },
+ {
category: 'Projects',
html_id: 'autocomplete-Projects-0',
id: 1,
@@ -291,14 +299,6 @@ export const MOCK_SORTED_AUTOCOMPLETE_OPTIONS = [
url: 'project/2',
},
{
- category: 'Groups',
- html_id: 'autocomplete-Groups-1',
- id: 1,
- label: 'Gitlab Org / MockGroup1',
- value: 'MockGroup1',
- url: 'group/1',
- },
- {
category: 'Help',
html_id: 'autocomplete-Help-3',
label: 'GitLab Help',
diff --git a/spec/frontend/header_search/store/actions_spec.js b/spec/frontend/header_search/store/actions_spec.js
index 1748d89a6d3..1ae149128ca 100644
--- a/spec/frontend/header_search/store/actions_spec.js
+++ b/spec/frontend/header_search/store/actions_spec.js
@@ -2,9 +2,18 @@ import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import * as actions from '~/header_search/store/actions';
import * as types from '~/header_search/store/mutation_types';
-import createState from '~/header_search/store/state';
+import initState from '~/header_search/store/state';
import axios from '~/lib/utils/axios_utils';
-import { MOCK_SEARCH, MOCK_AUTOCOMPLETE_OPTIONS_RES } from '../mock_data';
+import {
+ MOCK_SEARCH,
+ MOCK_AUTOCOMPLETE_OPTIONS_RES,
+ MOCK_AUTOCOMPLETE_PATH,
+ MOCK_PROJECT,
+ MOCK_SEARCH_CONTEXT,
+ MOCK_SEARCH_PATH,
+ MOCK_MR_PATH,
+ MOCK_ISSUE_PATH,
+} from '../mock_data';
jest.mock('~/flash');
@@ -12,10 +21,15 @@ describe('Header Search Store Actions', () => {
let state;
let mock;
- beforeEach(() => {
- state = createState({});
- mock = new MockAdapter(axios);
- });
+ const createState = (initialState) =>
+ initState({
+ searchPath: MOCK_SEARCH_PATH,
+ issuesPath: MOCK_ISSUE_PATH,
+ mrPath: MOCK_MR_PATH,
+ autocompletePath: MOCK_AUTOCOMPLETE_PATH,
+ searchContext: MOCK_SEARCH_CONTEXT,
+ ...initialState,
+ });
afterEach(() => {
state = null;
@@ -24,12 +38,14 @@ describe('Header Search Store Actions', () => {
describe.each`
axiosMock | type | expectedMutations
- ${{ method: 'onGet', code: 200, res: MOCK_AUTOCOMPLETE_OPTIONS_RES }} | ${'success'} | ${[{ type: types.REQUEST_AUTOCOMPLETE }, { type: types.RECEIVE_AUTOCOMPLETE_SUCCESS, payload: MOCK_AUTOCOMPLETE_OPTIONS_RES }]}
- ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_AUTOCOMPLETE }, { type: types.RECEIVE_AUTOCOMPLETE_ERROR }]}
+ ${{ method: 'onGet', code: 200, res: MOCK_AUTOCOMPLETE_OPTIONS_RES }} | ${'success'} | ${[{ type: types.REQUEST_AUTOCOMPLETE }, { type: types.RECEIVE_AUTOCOMPLETE_SUCCESS, payload: MOCK_AUTOCOMPLETE_OPTIONS_RES }, { type: types.RECEIVE_AUTOCOMPLETE_SUCCESS, payload: MOCK_AUTOCOMPLETE_OPTIONS_RES }]}
+ ${{ method: 'onGet', code: 500, res: null }} | ${'error'} | ${[{ type: types.REQUEST_AUTOCOMPLETE }, { type: types.RECEIVE_AUTOCOMPLETE_ERROR }, { type: types.RECEIVE_AUTOCOMPLETE_ERROR }]}
`('fetchAutocompleteOptions', ({ axiosMock, type, expectedMutations }) => {
describe(`on ${type}`, () => {
beforeEach(() => {
- mock[axiosMock.method]().replyOnce(axiosMock.code, axiosMock.res);
+ state = createState({});
+ mock = new MockAdapter(axios);
+ mock[axiosMock.method]().reply(axiosMock.code, axiosMock.res);
});
it(`should dispatch the correct mutations`, () => {
return testAction({
@@ -41,7 +57,35 @@ describe('Header Search Store Actions', () => {
});
});
+ describe.each`
+ project | ref | fetchType | expectedPath
+ ${null} | ${null} | ${null} | ${`${MOCK_AUTOCOMPLETE_PATH}?term=${MOCK_SEARCH}`}
+ ${MOCK_PROJECT} | ${null} | ${'generic'} | ${`${MOCK_AUTOCOMPLETE_PATH}?term=${MOCK_SEARCH}&project_id=${MOCK_PROJECT.id}&filter=generic`}
+ ${null} | ${MOCK_PROJECT.id} | ${'generic'} | ${`${MOCK_AUTOCOMPLETE_PATH}?term=${MOCK_SEARCH}&project_ref=${MOCK_PROJECT.id}&filter=generic`}
+ ${MOCK_PROJECT} | ${MOCK_PROJECT.id} | ${'search'} | ${`${MOCK_AUTOCOMPLETE_PATH}?term=${MOCK_SEARCH}&project_id=${MOCK_PROJECT.id}&project_ref=${MOCK_PROJECT.id}&filter=search`}
+ `('autocompleteQuery', ({ project, ref, fetchType, expectedPath }) => {
+ describe(`when project is ${project?.name} and project ref is ${ref}`, () => {
+ beforeEach(() => {
+ state = createState({
+ search: MOCK_SEARCH,
+ searchContext: {
+ project,
+ ref,
+ },
+ });
+ });
+
+ it(`should return ${expectedPath}`, () => {
+ expect(actions.autocompleteQuery({ state, fetchType })).toBe(expectedPath);
+ });
+ });
+ });
+
describe('clearAutocomplete', () => {
+ beforeEach(() => {
+ state = createState({});
+ });
+
it('calls the CLEAR_AUTOCOMPLETE mutation', () => {
return testAction({
action: actions.clearAutocomplete,
@@ -52,6 +96,10 @@ describe('Header Search Store Actions', () => {
});
describe('setSearch', () => {
+ beforeEach(() => {
+ state = createState({});
+ });
+
it('calls the SET_SEARCH mutation', () => {
return testAction({
action: actions.setSearch,
diff --git a/spec/frontend/header_search/store/getters_spec.js b/spec/frontend/header_search/store/getters_spec.js
index c76be3c0360..a1d9481b5cc 100644
--- a/spec/frontend/header_search/store/getters_spec.js
+++ b/spec/frontend/header_search/store/getters_spec.js
@@ -73,30 +73,6 @@ describe('Header Search Store Getters', () => {
});
describe.each`
- project | ref | expectedPath
- ${null} | ${null} | ${`${MOCK_AUTOCOMPLETE_PATH}?term=${MOCK_SEARCH}`}
- ${MOCK_PROJECT} | ${null} | ${`${MOCK_AUTOCOMPLETE_PATH}?term=${MOCK_SEARCH}&project_id=${MOCK_PROJECT.id}`}
- ${null} | ${MOCK_PROJECT.id} | ${`${MOCK_AUTOCOMPLETE_PATH}?term=${MOCK_SEARCH}&project_ref=${MOCK_PROJECT.id}`}
- ${MOCK_PROJECT} | ${MOCK_PROJECT.id} | ${`${MOCK_AUTOCOMPLETE_PATH}?term=${MOCK_SEARCH}&project_id=${MOCK_PROJECT.id}&project_ref=${MOCK_PROJECT.id}`}
- `('autocompleteQuery', ({ project, ref, expectedPath }) => {
- describe(`when project is ${project?.name} and project ref is ${ref}`, () => {
- beforeEach(() => {
- createState({
- searchContext: {
- project,
- ref,
- },
- });
- state.search = MOCK_SEARCH;
- });
-
- it(`should return ${expectedPath}`, () => {
- expect(getters.autocompleteQuery(state)).toBe(expectedPath);
- });
- });
- });
-
- describe.each`
group | group_metadata | project | project_metadata | expectedPath
${null} | ${null} | ${null} | ${null} | ${MOCK_ISSUE_PATH}
${{ name: 'Test Group' }} | ${{ issues_path: 'group/path' }} | ${null} | ${null} | ${'group/path'}
diff --git a/spec/frontend/ide/components/commit_sidebar/list_spec.js b/spec/frontend/ide/components/commit_sidebar/list_spec.js
index 81c81fc0a9f..4406d14d990 100644
--- a/spec/frontend/ide/components/commit_sidebar/list_spec.js
+++ b/spec/frontend/ide/components/commit_sidebar/list_spec.js
@@ -40,7 +40,7 @@ describe('Multi-file editor commit sidebar list', () => {
wrapper = mountComponent({ fileList: [] });
});
- it('renders no changes text ', () => {
+ it('renders no changes text', () => {
expect(wrapper.text()).toContain('No changes');
});
});
diff --git a/spec/frontend/ide/components/commit_sidebar/radio_group_spec.js b/spec/frontend/ide/components/commit_sidebar/radio_group_spec.js
index d899bc4f7d8..ee6ed694285 100644
--- a/spec/frontend/ide/components/commit_sidebar/radio_group_spec.js
+++ b/spec/frontend/ide/components/commit_sidebar/radio_group_spec.js
@@ -1,6 +1,6 @@
import Vue, { nextTick } from 'vue';
import { createComponentWithStore } from 'helpers/vue_mount_component_helper';
-import radioGroup from '~/ide/components/commit_sidebar/radio_group.vue';
+import RadioGroup from '~/ide/components/commit_sidebar/radio_group.vue';
import { createStore } from '~/ide/stores';
describe('IDE commit sidebar radio group', () => {
@@ -10,7 +10,7 @@ describe('IDE commit sidebar radio group', () => {
beforeEach(async () => {
store = createStore();
- const Component = Vue.extend(radioGroup);
+ const Component = Vue.extend(RadioGroup);
store.state.commit.commitAction = '2';
@@ -38,7 +38,7 @@ describe('IDE commit sidebar radio group', () => {
vm = new Vue({
components: {
- radioGroup,
+ RadioGroup,
},
store,
render: (createElement) =>
@@ -62,7 +62,7 @@ describe('IDE commit sidebar radio group', () => {
beforeEach(async () => {
vm.$destroy();
- const Component = Vue.extend(radioGroup);
+ const Component = Vue.extend(RadioGroup);
store.state.commit.commitAction = '1';
store.state.commit.newBranchName = 'test-123';
diff --git a/spec/frontend/ide/components/preview/navigator_spec.js b/spec/frontend/ide/components/preview/navigator_spec.js
index 532cb6e795c..043dcade858 100644
--- a/spec/frontend/ide/components/preview/navigator_spec.js
+++ b/spec/frontend/ide/components/preview/navigator_spec.js
@@ -76,7 +76,7 @@ describe('IDE clientside preview navigator', () => {
listenHandler({ type: 'urlchange', url: `${TEST_HOST}/url1` });
await nextTick();
findBackButton().trigger('click');
- expect(findBackButton().attributes('disabled')).toBeFalsy();
+ expect(findBackButton().attributes()).not.toHaveProperty('disabled');
});
it('is disabled when there is no previous entry', async () => {
@@ -117,7 +117,7 @@ describe('IDE clientside preview navigator', () => {
findBackButton().trigger('click');
await nextTick();
- expect(findForwardButton().attributes('disabled')).toBeFalsy();
+ expect(findForwardButton().attributes()).not.toHaveProperty('disabled');
});
it('is disabled when there is no next entry', async () => {
diff --git a/spec/frontend/ide/components/shared/tokened_input_spec.js b/spec/frontend/ide/components/shared/tokened_input_spec.js
index a37c08af0a1..2efef9918b1 100644
--- a/spec/frontend/ide/components/shared/tokened_input_spec.js
+++ b/spec/frontend/ide/components/shared/tokened_input_spec.js
@@ -50,7 +50,7 @@ describe('IDE shared/TokenedInput', () => {
});
it('renders input', () => {
- expect(vm.$refs.input).toBeTruthy();
+ expect(vm.$refs.input).toBeInstanceOf(HTMLInputElement);
expect(vm.$refs.input).toHaveValue(TEST_VALUE);
});
diff --git a/spec/frontend/ide/init_gitlab_web_ide_spec.js b/spec/frontend/ide/init_gitlab_web_ide_spec.js
new file mode 100644
index 00000000000..ec8559f1b56
--- /dev/null
+++ b/spec/frontend/ide/init_gitlab_web_ide_spec.js
@@ -0,0 +1,62 @@
+import { start } from '@gitlab/web-ide';
+import { initGitlabWebIDE } from '~/ide/init_gitlab_web_ide';
+import { TEST_HOST } from 'helpers/test_constants';
+
+jest.mock('@gitlab/web-ide');
+
+const ROOT_ELEMENT_ID = 'ide';
+const TEST_NONCE = 'test123nonce';
+const TEST_PROJECT = { path_with_namespace: 'group1/project1' };
+const TEST_BRANCH_NAME = '12345-foo-patch';
+const TEST_GITLAB_URL = 'https://test-gitlab/';
+const TEST_GITLAB_WEB_IDE_PUBLIC_PATH = 'test/webpack/assets/gitlab-web-ide/public/path';
+
+describe('ide/init_gitlab_web_ide', () => {
+ 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.dataset.project = JSON.stringify(TEST_PROJECT);
+ el.dataset.cspNonce = TEST_NONCE;
+ el.dataset.branchName = TEST_BRANCH_NAME;
+
+ document.body.append(el);
+ };
+ const findRootElement = () => document.getElementById(ROOT_ELEMENT_ID);
+ const act = () => initGitlabWebIDE(findRootElement());
+
+ beforeEach(() => {
+ process.env.GITLAB_WEB_IDE_PUBLIC_PATH = TEST_GITLAB_WEB_IDE_PUBLIC_PATH;
+ window.gon.gitlab_url = TEST_GITLAB_URL;
+
+ createRootElement();
+
+ act();
+ });
+
+ 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_with_namespace,
+ ref: TEST_BRANCH_NAME,
+ gitlabUrl: TEST_GITLAB_URL,
+ nonce: TEST_NONCE,
+ });
+ });
+
+ it('clears classes and data from root element', () => {
+ const rootEl = findRootElement();
+
+ // 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>',
+ );
+ });
+});
diff --git a/spec/frontend/ide/stores/actions/file_spec.js b/spec/frontend/ide/stores/actions/file_spec.js
index d1c31cd412b..38a54e569a9 100644
--- a/spec/frontend/ide/stores/actions/file_spec.js
+++ b/spec/frontend/ide/stores/actions/file_spec.js
@@ -78,7 +78,7 @@ describe('IDE store file actions', () => {
});
});
- it('switches to the next available file before closing the current one ', () => {
+ it('switches to the next available file before closing the current one', () => {
const f = file('newOpenFile');
store.state.openFiles.push(f);
diff --git a/spec/frontend/ide/stores/modules/commit/actions_spec.js b/spec/frontend/ide/stores/modules/commit/actions_spec.js
index d65039e89cc..4e8467de759 100644
--- a/spec/frontend/ide/stores/modules/commit/actions_spec.js
+++ b/spec/frontend/ide/stores/modules/commit/actions_spec.js
@@ -210,7 +210,7 @@ describe('IDE commit module actions', () => {
branch,
});
store.state.openFiles.forEach((entry) => {
- expect(entry.changed).toBeFalsy();
+ expect(entry.changed).toBe(false);
});
});
diff --git a/spec/frontend/import_entities/components/group_dropdown_spec.js b/spec/frontend/import_entities/components/group_dropdown_spec.js
index 1c1e1e7ebd4..b896437ecb2 100644
--- a/spec/frontend/import_entities/components/group_dropdown_spec.js
+++ b/spec/frontend/import_entities/components/group_dropdown_spec.js
@@ -42,7 +42,7 @@ describe('Import entities group dropdown component', () => {
createComponent({ namespaces });
namespacesTracker.mockReset();
- wrapper.find(GlSearchBoxByType).vm.$emit('input', 'match');
+ wrapper.findComponent(GlSearchBoxByType).vm.$emit('input', 'match');
await nextTick();
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 cdc508a0033..f97ea046cbe 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
@@ -99,7 +99,7 @@ describe('import table', () => {
});
await waitForPromises();
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
it('does not renders loading icon when request is completed', async () => {
@@ -108,7 +108,7 @@ describe('import table', () => {
});
await waitForPromises();
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
});
});
@@ -123,7 +123,7 @@ describe('import table', () => {
});
await waitForPromises();
- expect(wrapper.find(GlEmptyState).props().title).toBe(i18n.NO_GROUPS_FOUND);
+ expect(wrapper.findComponent(GlEmptyState).props().title).toBe(i18n.NO_GROUPS_FOUND);
});
});
@@ -268,7 +268,7 @@ describe('import table', () => {
});
it('correctly passes pagination info from query', () => {
- expect(wrapper.find(PaginationLinks).props().pageInfo).toStrictEqual(FAKE_PAGE_INFO);
+ expect(wrapper.findComponent(PaginationLinks).props().pageInfo).toStrictEqual(FAKE_PAGE_INFO);
});
it('renders pagination dropdown', () => {
@@ -293,7 +293,7 @@ describe('import table', () => {
it('updates page when page change is requested', async () => {
const REQUESTED_PAGE = 2;
- wrapper.find(PaginationLinks).props().change(REQUESTED_PAGE);
+ wrapper.findComponent(PaginationLinks).props().change(REQUESTED_PAGE);
await waitForPromises();
expect(bulkImportSourceGroupsQueryMock).toHaveBeenCalledWith(
@@ -316,7 +316,7 @@ describe('import table', () => {
},
versionValidation: FAKE_VERSION_VALIDATION,
});
- wrapper.find(PaginationLinks).props().change(REQUESTED_PAGE);
+ wrapper.findComponent(PaginationLinks).props().change(REQUESTED_PAGE);
await waitForPromises();
expect(wrapper.text()).toContain('Showing 21-21 of 38 groups that you own from');
@@ -539,8 +539,8 @@ describe('import table', () => {
});
await waitForPromises();
- expect(wrapper.find(GlAlert).exists()).toBe(true);
- expect(wrapper.find(GlAlert).text()).toContain('projects (require v14.8.0)');
+ expect(wrapper.findComponent(GlAlert).exists()).toBe(true);
+ expect(wrapper.findComponent(GlAlert).text()).toContain('projects (require v14.8.0)');
});
it('does not renders alert when there are no unavailable features', async () => {
@@ -558,7 +558,7 @@ describe('import table', () => {
});
await waitForPromises();
- expect(wrapper.find(GlAlert).exists()).toBe(false);
+ expect(wrapper.findComponent(GlAlert).exists()).toBe(false);
});
});
});
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 d3f86672f33..18dc1217fec 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
@@ -22,8 +22,8 @@ describe('import target cell', () => {
let wrapper;
let group;
- const findNameInput = () => wrapper.find(GlFormInput);
- const findNamespaceDropdown = () => wrapper.find(ImportGroupDropdown);
+ const findNameInput = () => wrapper.findComponent(GlFormInput);
+ const findNamespaceDropdown = () => wrapper.findComponent(ImportGroupDropdown);
const createComponent = (props) => {
wrapper = shallowMount(ImportTargetCell, {
diff --git a/spec/frontend/import_entities/import_projects/components/bitbucket_status_table_spec.js b/spec/frontend/import_entities/import_projects/components/bitbucket_status_table_spec.js
index ea88c361f7b..9eae4ed974e 100644
--- a/spec/frontend/import_entities/import_projects/components/bitbucket_status_table_spec.js
+++ b/spec/frontend/import_entities/import_projects/components/bitbucket_status_table_spec.js
@@ -33,12 +33,12 @@ describe('BitbucketStatusTable', () => {
it('renders import table component', () => {
createComponent({ providerTitle: 'Test' });
- expect(wrapper.find(ImportProjectsTable).exists()).toBe(true);
+ expect(wrapper.findComponent(ImportProjectsTable).exists()).toBe(true);
});
it('passes alert in incompatible-repos-warning slot', () => {
createComponent({ providerTitle: 'Test' }, ImportProjectsTableStub);
- expect(wrapper.find(GlAlert).exists()).toBe(true);
+ expect(wrapper.findComponent(GlAlert).exists()).toBe(true);
});
it('passes actions slot to import project table component', () => {
@@ -46,14 +46,14 @@ describe('BitbucketStatusTable', () => {
createComponent({ providerTitle: 'Test' }, ImportProjectsTableStub, {
actions: actionsSlotContent,
});
- expect(wrapper.find(ImportProjectsTable).text()).toBe(actionsSlotContent);
+ expect(wrapper.findComponent(ImportProjectsTable).text()).toBe(actionsSlotContent);
});
it('dismisses alert when requested', async () => {
createComponent({ providerTitle: 'Test' }, ImportProjectsTableStub);
- wrapper.find(GlAlert).vm.$emit('dismiss');
+ wrapper.findComponent(GlAlert).vm.$emit('dismiss');
await nextTick();
- expect(wrapper.find(GlAlert).exists()).toBe(false);
+ expect(wrapper.findComponent(GlAlert).exists()).toBe(false);
});
});
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 140fec3863b..c0ae4294e3d 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
@@ -30,10 +30,10 @@ describe('ImportProjectsTable', () => {
const findImportAllButton = () =>
wrapper
- .findAll(GlButton)
+ .findAllComponents(GlButton)
.filter((w) => w.props().variant === 'confirm')
.at(0);
- const findImportAllModal = () => wrapper.find({ ref: 'importAllModal' });
+ const findImportAllModal = () => wrapper.findComponent({ ref: 'importAllModal' });
const importAllFn = jest.fn();
const importAllModalShowFn = jest.fn();
@@ -89,13 +89,13 @@ describe('ImportProjectsTable', () => {
it('renders a loading icon while repos are loading', () => {
createComponent({ state: { isLoadingRepos: true } });
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
it('renders a loading icon while namespaces are loading', () => {
createComponent({ state: { isLoadingNamespaces: true } });
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
it('renders a table with provider repos', () => {
@@ -109,7 +109,7 @@ describe('ImportProjectsTable', () => {
state: { namespaces: [{ fullPath: 'path' }], repositories },
});
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.find('table').exists()).toBe(true);
expect(
wrapper
@@ -118,7 +118,7 @@ describe('ImportProjectsTable', () => {
.exists(),
).toBe(true);
- expect(wrapper.findAll(ProviderRepoTableRow)).toHaveLength(repositories.length);
+ expect(wrapper.findAllComponents(ProviderRepoTableRow)).toHaveLength(repositories.length);
});
it.each`
@@ -170,7 +170,7 @@ describe('ImportProjectsTable', () => {
it('renders an empty state if there are no repositories available', () => {
createComponent({ state: { repositories: [] } });
- expect(wrapper.find(ProviderRepoTableRow).exists()).toBe(false);
+ expect(wrapper.findComponent(ProviderRepoTableRow).exists()).toBe(false);
expect(wrapper.text()).toContain(`No ${providerTitle} repositories found`);
});
@@ -231,11 +231,11 @@ describe('ImportProjectsTable', () => {
});
it('renders intersection observer component', () => {
- expect(wrapper.find(GlIntersectionObserver).exists()).toBe(true);
+ expect(wrapper.findComponent(GlIntersectionObserver).exists()).toBe(true);
});
it('calls fetchRepos when intersection observer appears', async () => {
- wrapper.find(GlIntersectionObserver).vm.$emit('appear');
+ wrapper.findComponent(GlIntersectionObserver).vm.$emit('appear');
await nextTick();
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 41a005199e1..17a07b1e9f9 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
@@ -74,11 +74,13 @@ describe('ProviderRepoTableRow', () => {
});
it('renders empty import status', () => {
- expect(wrapper.find(ImportStatus).props().status).toBe(STATUSES.NONE);
+ expect(wrapper.findComponent(ImportStatus).props().status).toBe(STATUSES.NONE);
});
it('renders a group namespace select', () => {
- expect(wrapper.find(ImportGroupDropdown).props().namespaces).toBe(availableNamespaces);
+ expect(wrapper.findComponent(ImportGroupDropdown).props().namespaces).toBe(
+ availableNamespaces,
+ );
});
it('renders import button', () => {
@@ -127,11 +129,13 @@ describe('ProviderRepoTableRow', () => {
});
it('renders proper import status', () => {
- expect(wrapper.find(ImportStatus).props().status).toBe(repo.importedProject.importStatus);
+ expect(wrapper.findComponent(ImportStatus).props().status).toBe(
+ repo.importedProject.importStatus,
+ );
});
it('does not renders a namespace select', () => {
- expect(wrapper.find(GlDropdown).exists()).toBe(false);
+ expect(wrapper.findComponent(GlDropdown).exists()).toBe(false);
});
it('does not render import button', () => {
@@ -139,7 +143,7 @@ describe('ProviderRepoTableRow', () => {
});
it('passes stats to import status component', () => {
- expect(wrapper.find(ImportStatus).props().stats).toBe(FAKE_STATS);
+ expect(wrapper.findComponent(ImportStatus).props().stats).toBe(FAKE_STATS);
});
});
@@ -165,7 +169,7 @@ describe('ProviderRepoTableRow', () => {
});
it('renders badge with error', () => {
- expect(wrapper.find(GlBadge).text()).toBe('Incompatible project');
+ expect(wrapper.findComponent(GlBadge).text()).toBe('Incompatible project');
});
});
});
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 55826b20ca3..110b692b222 100644
--- a/spec/frontend/import_entities/import_projects/store/getters_spec.js
+++ b/spec/frontend/import_entities/import_projects/store/getters_spec.js
@@ -85,7 +85,7 @@ describe('import_projects store getters', () => {
});
describe('hasImportableRepos', () => {
- it('returns true if there are any importable projects ', () => {
+ it('returns true if there are any importable projects', () => {
localState.repositories = [IMPORTABLE_REPO, IMPORTED_REPO, INCOMPATIBLE_REPO];
expect(hasImportableRepos(localState)).toBe(true);
@@ -99,7 +99,7 @@ describe('import_projects store getters', () => {
});
describe('importAllCount', () => {
- it('returns count of available importable projects ', () => {
+ it('returns count of available importable projects', () => {
localState.repositories = [
IMPORTABLE_REPO,
IMPORTABLE_REPO,
diff --git a/spec/frontend/incidents/components/incidents_list_spec.js b/spec/frontend/incidents/components/incidents_list_spec.js
index 356480f931e..e8d222dc2e9 100644
--- a/spec/frontend/incidents/components/incidents_list_spec.js
+++ b/spec/frontend/incidents/components/incidents_list_spec.js
@@ -40,16 +40,16 @@ describe('Incidents List', () => {
all: 26,
};
- const findTable = () => wrapper.find(GlTable);
+ const findTable = () => wrapper.findComponent(GlTable);
const findTableRows = () => wrapper.findAll('table tbody tr');
- const findAlert = () => wrapper.find(GlAlert);
- const findLoader = () => wrapper.find(GlLoadingIcon);
- const findTimeAgo = () => wrapper.findAll(TimeAgoTooltip);
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findLoader = () => wrapper.findComponent(GlLoadingIcon);
+ const findTimeAgo = () => wrapper.findAllComponents(TimeAgoTooltip);
const findAssignees = () => wrapper.findAll('[data-testid="incident-assignees"]');
const findCreateIncidentBtn = () => wrapper.find('[data-testid="createIncidentBtn"]');
const findClosedIcon = () => wrapper.findAll("[data-testid='incident-closed']");
- const findEmptyState = () => wrapper.find(GlEmptyState);
- const findSeverity = () => wrapper.findAll(SeverityToken);
+ const findEmptyState = () => wrapper.findComponent(GlEmptyState);
+ const findSeverity = () => wrapper.findAllComponents(SeverityToken);
const findEscalationStatus = () => wrapper.findAll('[data-testid="incident-escalation-status"]');
const findIncidentLink = () => wrapper.findByTestId('incident-link');
@@ -179,7 +179,7 @@ describe('Incidents List', () => {
});
it('renders an avatar component when there is an assignee', () => {
- const avatar = findAssignees().at(1).find(GlAvatar);
+ const avatar = findAssignees().at(1).findComponent(GlAvatar);
const { src, label } = avatar.attributes();
const { name, avatarUrl } = mockIncidents[1].assignees.nodes[0];
diff --git a/spec/frontend/incidents_settings/components/incidents_settings_tabs_spec.js b/spec/frontend/incidents_settings/components/incidents_settings_tabs_spec.js
index ff40f1fa008..394d1f12bcb 100644
--- a/spec/frontend/incidents_settings/components/incidents_settings_tabs_spec.js
+++ b/spec/frontend/incidents_settings/components/incidents_settings_tabs_spec.js
@@ -20,10 +20,10 @@ describe('IncidentsSettingTabs', () => {
}
});
- const findToggleButton = () => wrapper.find({ ref: 'toggleBtn' });
- const findSectionHeader = () => wrapper.find({ ref: 'sectionHeader' });
+ const findToggleButton = () => wrapper.findComponent({ ref: 'toggleBtn' });
+ const findSectionHeader = () => wrapper.findComponent({ ref: 'sectionHeader' });
- const findIntegrationTabs = () => wrapper.findAll(GlTab);
+ const findIntegrationTabs = () => wrapper.findAllComponents(GlTab);
it('renders header text', () => {
expect(findSectionHeader().text()).toBe('Incidents');
});
diff --git a/spec/frontend/integrations/edit/components/trigger_fields_spec.js b/spec/frontend/integrations/edit/components/trigger_fields_spec.js
index 8ee55928926..c329ca8522f 100644
--- a/spec/frontend/integrations/edit/components/trigger_fields_spec.js
+++ b/spec/frontend/integrations/edit/components/trigger_fields_spec.js
@@ -24,7 +24,7 @@ describe('TriggerFields', () => {
});
const findTriggerLabel = () => wrapper.findByTestId('trigger-fields-group').find('label');
- const findAllGlFormGroups = () => wrapper.find('#trigger-fields').findAll(GlFormGroup);
+ const findAllGlFormGroups = () => wrapper.find('#trigger-fields').findAllComponents(GlFormGroup);
const findAllGlFormCheckboxes = () => wrapper.findAllComponents(GlFormCheckbox);
const findAllGlFormInputs = () => wrapper.findAllComponents(GlFormInput);
@@ -86,7 +86,7 @@ describe('TriggerFields', () => {
expect(checkboxes).toHaveLength(2);
checkboxes.wrappers.forEach((checkbox, index) => {
- const checkBox = checkbox.find(GlFormCheckbox);
+ const checkBox = checkbox.findComponent(GlFormCheckbox);
expect(checkbox.find('label').text()).toBe(expectedResults[index].labelText);
expect(checkbox.find('[type=hidden]').attributes('name')).toBe(
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 b4d42d90d99..8b2d13be309 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
@@ -53,7 +53,7 @@ afterEach(() => {
describe('ImportProjectMembersModal', () => {
const findGlModal = () => wrapper.findComponent(GlModal);
- const findIntroText = () => wrapper.find({ ref: 'modalIntro' }).text();
+ const findIntroText = () => wrapper.findComponent({ ref: 'modalIntro' }).text();
const clickImportButton = () => findGlModal().vm.$emit('primary', { preventDefault: jest.fn() });
const closeModal = () => findGlModal().vm.$emit('hidden', { preventDefault: jest.fn() });
const findFormGroup = () => wrapper.findByTestId('form-group');
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 2058784b033..e9e1fbad07b 100644
--- a/spec/frontend/invite_members/components/invite_members_modal_spec.js
+++ b/spec/frontend/invite_members/components/invite_members_modal_spec.js
@@ -19,6 +19,7 @@ import {
MEMBERS_TO_PROJECT_CELEBRATE_INTRO_TEXT,
LEARN_GITLAB,
EXPANDED_ERRORS,
+ EMPTY_INVITES_ERROR_TEXT,
} from '~/invite_members/constants';
import eventHub from '~/invite_members/event_hub';
import ContentTransition from '~/vue_shared/components/content_transition.vue';
@@ -255,6 +256,8 @@ describe('InviteMembersModal', () => {
it('tracks the submit for invite_members_for_task', async () => {
await setupComponentWithTasks();
+ await triggerMembersTokenSelect([user1]);
+
clickInviteButton();
expect(ExperimentTracking).toHaveBeenCalledWith(INVITE_MEMBERS_FOR_TASK.name, {
@@ -265,6 +268,16 @@ describe('InviteMembersModal', () => {
INVITE_MEMBERS_FOR_TASK.submit,
);
});
+
+ it('does not track the submit for invite_members_for_task when invites have not been entered', async () => {
+ await setupComponentWithTasks();
+ clickInviteButton();
+
+ expect(ExperimentTracking).not.toHaveBeenCalledWith(
+ INVITE_MEMBERS_FOR_TASK.name,
+ expect.any,
+ );
+ });
});
});
@@ -380,6 +393,25 @@ describe('InviteMembersModal', () => {
"The member's email address is not allowed for this project. Go to the Admin area > Sign-up restrictions, and check Allowed domains for sign-ups.";
const expectedSyntaxError = 'email contains an invalid email address';
+ describe('when no invites have been entered in the form and then some are entered', () => {
+ beforeEach(async () => {
+ createInviteMembersToGroupWrapper();
+ });
+
+ it('displays an error', async () => {
+ clickInviteButton();
+
+ await waitForPromises();
+
+ expect(membersFormGroupInvalidFeedback()).toBe(EMPTY_INVITES_ERROR_TEXT);
+ expect(findMembersSelect().props('exceptionState')).toBe(false);
+
+ await triggerMembersTokenSelect([user1]);
+
+ expect(membersFormGroupInvalidFeedback()).toBe('');
+ });
+ });
+
describe('when inviting an existing user to group by user ID', () => {
const postData = {
user_id: '1,2',
diff --git a/spec/frontend/invite_members/components/user_limit_notification_spec.js b/spec/frontend/invite_members/components/user_limit_notification_spec.js
index 543fc28a342..1ff2e86412f 100644
--- a/spec/frontend/invite_members/components/user_limit_notification_spec.js
+++ b/spec/frontend/invite_members/components/user_limit_notification_spec.js
@@ -1,12 +1,7 @@
import { GlAlert, GlSprintf } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import UserLimitNotification from '~/invite_members/components/user_limit_notification.vue';
-
-import {
- REACHED_LIMIT_MESSAGE,
- REACHED_LIMIT_UPGRADE_SUGGESTION_MESSAGE,
-} from '~/invite_members/constants';
-
+import { REACHED_LIMIT_UPGRADE_SUGGESTION_MESSAGE } from '~/invite_members/constants';
import { freeUsersLimit, membersCount } from '../mock_data/member_modal';
const WARNING_ALERT_TITLE = 'You only have space for 2 more members in name';
@@ -52,22 +47,6 @@ describe('UserLimitNotification', () => {
});
});
- describe('when close to limit within a personal namepace', () => {
- beforeEach(() => {
- createComponent(true, false, { membersCount: 3, userNamespace: true });
- });
-
- it('renders the limit for a personal namespace', () => {
- const alert = findAlert();
-
- expect(alert.attributes('title')).toEqual(WARNING_ALERT_TITLE);
-
- expect(alert.text()).toEqual(
- 'To make more space, you can remove members who no longer need access.',
- );
- });
- });
-
describe('when close to limit within a group', () => {
it("renders user's limit notification", () => {
createComponent(true, false, { membersCount: 3 });
@@ -91,19 +70,5 @@ describe('UserLimitNotification', () => {
expect(alert.attributes('title')).toEqual("You've reached your 5 members limit for name");
expect(alert.text()).toEqual(REACHED_LIMIT_UPGRADE_SUGGESTION_MESSAGE);
});
-
- describe('when free user namespace', () => {
- it("renders user's limit notification", () => {
- createComponent(true, true, { userNamespace: true });
-
- const alert = findAlert();
-
- expect(alert.attributes('title')).toEqual(
- "You've reached your 5 members limit for your personal projects",
- );
-
- expect(alert.text()).toEqual(REACHED_LIMIT_MESSAGE);
- });
- });
});
});
diff --git a/spec/frontend/issuable/components/issue_assignees_spec.js b/spec/frontend/issuable/components/issue_assignees_spec.js
index 713c8b1dfdd..9a33bfae240 100644
--- a/spec/frontend/issuable/components/issue_assignees_spec.js
+++ b/spec/frontend/issuable/components/issue_assignees_spec.js
@@ -27,7 +27,7 @@ describe('IssueAssigneesComponent', () => {
});
const findTooltipText = () => wrapper.find('.js-assignee-tooltip').text();
- const findAvatars = () => wrapper.findAll(UserAvatarLink);
+ const findAvatars = () => wrapper.findAllComponents(UserAvatarLink);
const findOverflowCounter = () => wrapper.find('.avatar-counter');
it('returns default data props', () => {
diff --git a/spec/frontend/issuable/components/issue_milestone_spec.js b/spec/frontend/issuable/components/issue_milestone_spec.js
index 9d67f602136..eac53c5f761 100644
--- a/spec/frontend/issuable/components/issue_milestone_spec.js
+++ b/spec/frontend/issuable/components/issue_milestone_spec.js
@@ -144,7 +144,7 @@ describe('IssueMilestoneComponent', () => {
});
it('renders milestone icon', () => {
- expect(wrapper.find(GlIcon).props('name')).toBe('clock');
+ expect(wrapper.findComponent(GlIcon).props('name')).toBe('clock');
});
it('renders milestone title', () => {
diff --git a/spec/frontend/issuable/issuable_form_spec.js b/spec/frontend/issuable/issuable_form_spec.js
index d844f3394d5..5e67ea42b87 100644
--- a/spec/frontend/issuable/issuable_form_spec.js
+++ b/spec/frontend/issuable/issuable_form_spec.js
@@ -1,111 +1,200 @@
import $ from 'jquery';
+import Autosave from '~/autosave';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import IssuableForm from '~/issuable/issuable_form';
import setWindowLocation from 'helpers/set_window_location_helper';
+jest.mock('~/autosave');
+
+const createIssuable = (form) => {
+ return new IssuableForm(form);
+};
+
describe('IssuableForm', () => {
+ let $form;
let instance;
- const createIssuable = (form) => {
- instance = new IssuableForm(form);
- };
-
beforeEach(() => {
setHTMLFixture(`
<form>
<input name="[title]" />
+ <textarea name="[description]"></textarea>
</form>
`);
- createIssuable($('form'));
+ $form = $('form');
});
afterEach(() => {
resetHTMLFixture();
+ $form = null;
+ instance = null;
});
- describe('initAutosave', () => {
- it('creates autosave with the searchTerm included', () => {
- setWindowLocation('https://gitlab.test/foo?bar=true');
- const autosave = instance.initAutosave();
+ describe('autosave', () => {
+ let $title;
+ let $description;
+
+ beforeEach(() => {
+ $title = $form.find('input[name*="[title]"]');
+ $description = $form.find('textarea[name*="[description]"]');
+ });
- expect(autosave.key.includes('bar=true')).toBe(true);
+ afterEach(() => {
+ $title = null;
+ $description = null;
});
- it("creates autosave fields without the searchTerm if it's an issue new form", () => {
- setHTMLFixture(`
- <form data-new-issue-path="/issues/new">
- <input name="[title]" />
- </form>
- `);
- createIssuable($('form'));
+ describe('initAutosave', () => {
+ it('calls initAutosave', () => {
+ const initAutosave = jest.spyOn(IssuableForm.prototype, 'initAutosave');
+ createIssuable($form);
+
+ expect(initAutosave).toHaveBeenCalledTimes(1);
+ });
+
+ it('creates autosave with the searchTerm included', () => {
+ setWindowLocation('https://gitlab.test/foo?bar=true');
+ createIssuable($form);
+
+ expect(Autosave).toHaveBeenCalledWith(
+ $title,
+ ['/foo', 'bar=true', 'title'],
+ 'autosave//foo/bar=true=title',
+ );
+ expect(Autosave).toHaveBeenCalledWith(
+ $description,
+ ['/foo', 'bar=true', 'description'],
+ 'autosave//foo/bar=true=description',
+ );
+ });
+
+ it("creates autosave fields without the searchTerm if it's an issue new form", () => {
+ setWindowLocation('https://gitlab.test/issues/new?bar=true');
+ $form.attr('data-new-issue-path', '/issues/new');
+ createIssuable($form);
+
+ expect(Autosave).toHaveBeenCalledWith(
+ $title,
+ ['/issues/new', '', 'title'],
+ 'autosave//issues/new/bar=true=title',
+ );
+ expect(Autosave).toHaveBeenCalledWith(
+ $description,
+ ['/issues/new', '', 'description'],
+ 'autosave//issues/new/bar=true=description',
+ );
+ });
+
+ it.each([
+ {
+ id: 'confidential',
+ input: '<input type="checkbox" name="issue[confidential]"/>',
+ selector: 'input[name*="[confidential]"]',
+ },
+ {
+ id: 'due_date',
+ input: '<input type="text" name="issue[due_date]"/>',
+ selector: 'input[name*="[due_date]"]',
+ },
+ ])('creates $id autosave when $id input exist', ({ id, input, selector }) => {
+ $form.append(input);
+ const $input = $form.find(selector);
+ const totalAutosaveFormFields = $form.children().length;
+ createIssuable($form);
+
+ expect(Autosave).toHaveBeenCalledTimes(totalAutosaveFormFields);
+ expect(Autosave).toHaveBeenLastCalledWith($input, ['/', '', id], `autosave///=${id}`);
+ });
+ });
+
+ describe('resetAutosave', () => {
+ it('calls reset on title and description', () => {
+ instance = createIssuable($form);
+
+ instance.resetAutosave();
+
+ expect(instance.autosaves.get('title').reset).toHaveBeenCalledTimes(1);
+ expect(instance.autosaves.get('description').reset).toHaveBeenCalledTimes(1);
+ });
- setWindowLocation('https://gitlab.test/issues/new?bar=true');
+ it('resets autosave when submit', () => {
+ const resetAutosave = jest.spyOn(IssuableForm.prototype, 'resetAutosave');
+ createIssuable($form);
- const autosave = instance.initAutosave();
+ $form.submit();
- expect(autosave.key.includes('bar=true')).toBe(false);
+ expect(resetAutosave).toHaveBeenCalledTimes(1);
+ });
+
+ it('resets autosave on elements with the .js-reset-autosave class', () => {
+ const resetAutosave = jest.spyOn(IssuableForm.prototype, 'resetAutosave');
+ $form.append('<a class="js-reset-autosave">Cancel</a>');
+ createIssuable($form);
+
+ $form.find('.js-reset-autosave').trigger('click');
+
+ expect(resetAutosave).toHaveBeenCalledTimes(1);
+ });
+
+ it.each([
+ { id: 'confidential', input: '<input type="checkbox" name="issue[confidential]"/>' },
+ { id: 'due_date', input: '<input type="text" name="issue[due_date]"/>' },
+ ])('calls reset on autosave $id when $id input exist', ({ id, input }) => {
+ $form.append(input);
+ instance = createIssuable($form);
+ instance.resetAutosave();
+
+ expect(instance.autosaves.get(id).reset).toHaveBeenCalledTimes(1);
+ });
});
});
- describe('resetAutosave', () => {
- it('resets autosave on elements with the .js-reset-autosave class', () => {
- setHTMLFixture(`
- <form>
- <input name="[title]" />
- <textarea name="[description]"></textarea>
- <a class="js-reset-autosave">Cancel</a>
- </form>
- `);
- const $form = $('form');
- const resetAutosave = jest.spyOn(IssuableForm.prototype, 'resetAutosave');
- createIssuable($form);
-
- $form.find('.js-reset-autosave').trigger('click');
-
- expect(resetAutosave).toHaveBeenCalled();
+ describe('wip', () => {
+ beforeEach(() => {
+ instance = createIssuable($form);
});
- });
- describe('removeWip', () => {
- it.each`
- prefix
- ${'draFT: '}
- ${' [DRaft] '}
- ${'drAft:'}
- ${'[draFT]'}
- ${'(draft) '}
- ${' (DrafT)'}
- ${'draft: [draft] (draft)'}
- `('removes "$prefix" from the beginning of the title', ({ prefix }) => {
- instance.titleField.val(`${prefix}The Issuable's Title Value`);
-
- instance.removeWip();
-
- expect(instance.titleField.val()).toBe("The Issuable's Title Value");
+ describe('removeWip', () => {
+ it.each`
+ prefix
+ ${'draFT: '}
+ ${' [DRaft] '}
+ ${'drAft:'}
+ ${'[draFT]'}
+ ${'(draft) '}
+ ${' (DrafT)'}
+ ${'draft: [draft] (draft)'}
+ `('removes "$prefix" from the beginning of the title', ({ prefix }) => {
+ instance.titleField.val(`${prefix}The Issuable's Title Value`);
+
+ instance.removeWip();
+
+ expect(instance.titleField.val()).toBe("The Issuable's Title Value");
+ });
});
- });
- describe('addWip', () => {
- it("properly adds the work in progress prefix to the Issuable's title", () => {
- instance.titleField.val("The Issuable's Title Value");
+ describe('addWip', () => {
+ it("properly adds the work in progress prefix to the Issuable's title", () => {
+ instance.titleField.val("The Issuable's Title Value");
- instance.addWip();
+ instance.addWip();
- expect(instance.titleField.val()).toBe("Draft: The Issuable's Title Value");
+ expect(instance.titleField.val()).toBe("Draft: The Issuable's Title Value");
+ });
});
- });
- describe('workInProgress', () => {
- it.each`
- title | expected
- ${'draFT: something is happening'} | ${true}
- ${'draft something is happening'} | ${false}
- ${'something is happening to drafts'} | ${false}
- ${'something is happening'} | ${false}
- `('returns $expected with "$title"', ({ title, expected }) => {
- instance.titleField.val(title);
-
- expect(instance.workInProgress()).toBe(expected);
+ describe('workInProgress', () => {
+ it.each`
+ title | expected
+ ${'draFT: something is happening'} | ${true}
+ ${'draft something is happening'} | ${false}
+ ${'something is happening to drafts'} | ${false}
+ ${'something is happening'} | ${false}
+ `('returns $expected with "$title"', ({ title, expected }) => {
+ instance.titleField.val(title);
+
+ expect(instance.workInProgress()).toBe(expected);
+ });
});
});
});
diff --git a/spec/frontend/issuable/related_issues/components/issue_token_spec.js b/spec/frontend/issuable/related_issues/components/issue_token_spec.js
index d6aeacfe07a..bacebbade7f 100644
--- a/spec/frontend/issuable/related_issues/components/issue_token_spec.js
+++ b/spec/frontend/issuable/related_issues/components/issue_token_spec.js
@@ -31,11 +31,11 @@ describe('IssueToken', () => {
}
});
- const findLink = () => wrapper.find({ ref: 'link' });
- const findReference = () => wrapper.find({ ref: 'reference' });
+ const findLink = () => wrapper.findComponent({ ref: 'link' });
+ const findReference = () => wrapper.findComponent({ ref: 'reference' });
const findReferenceIcon = () => wrapper.find('[data-testid="referenceIcon"]');
const findRemoveBtn = () => wrapper.find('[data-testid="removeBtn"]');
- const findTitle = () => wrapper.find({ ref: 'title' });
+ const findTitle = () => wrapper.findComponent({ ref: 'title' });
describe('with reference supplied', () => {
beforeEach(() => {
diff --git a/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js b/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js
index 772cc75a205..1b2935ce5d1 100644
--- a/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js
+++ b/spec/frontend/issuable/related_issues/components/related_issues_block_spec.js
@@ -153,7 +153,7 @@ describe('RelatedIssuesBlock', () => {
});
it('sets `autoCompleteEpics` to false for add-issuable-form', () => {
- expect(wrapper.find(AddIssuableForm).props('autoCompleteEpics')).toBe(false);
+ expect(wrapper.findComponent(AddIssuableForm).props('autoCompleteEpics')).toBe(false);
});
});
@@ -227,7 +227,7 @@ describe('RelatedIssuesBlock', () => {
},
});
- const iconComponent = wrapper.find(GlIcon);
+ const iconComponent = wrapper.findComponent(GlIcon);
expect(iconComponent.exists()).toBe(true);
expect(iconComponent.props('name')).toBe(icon);
});
diff --git a/spec/frontend/issuable/related_issues/components/related_issues_list_spec.js b/spec/frontend/issuable/related_issues/components/related_issues_list_spec.js
index fd623ad9a5f..9bb71ec3dcb 100644
--- a/spec/frontend/issuable/related_issues/components/related_issues_list_spec.js
+++ b/spec/frontend/issuable/related_issues/components/related_issues_list_spec.js
@@ -187,7 +187,9 @@ describe('RelatedIssuesList', () => {
});
it('shows due date', () => {
- expect(wrapper.find(IssueDueDate).find('.board-card-info-text').text()).toBe('Nov 22, 2010');
+ expect(wrapper.findComponent(IssueDueDate).find('.board-card-info-text').text()).toBe(
+ 'Nov 22, 2010',
+ );
});
});
});
diff --git a/spec/frontend/issues/create_merge_request_dropdown_spec.js b/spec/frontend/issues/create_merge_request_dropdown_spec.js
index cb7173c56a8..cc2ee84348a 100644
--- a/spec/frontend/issues/create_merge_request_dropdown_spec.js
+++ b/spec/frontend/issues/create_merge_request_dropdown_spec.js
@@ -106,7 +106,7 @@ describe('CreateMergeRequestDropdown', () => {
loading | hasClass
${true} | ${false}
${false} | ${true}
- `('it toggle loading spinner when loading is $loading', ({ loading, hasClass }) => {
+ `('toggle loading spinner when loading is $loading', ({ loading, hasClass }) => {
dropdown.setLoading(loading);
expect(document.querySelector('.js-spinner').classList.contains('gl-display-none')).toEqual(
diff --git a/spec/frontend/issues/list/components/issue_card_time_info_spec.js b/spec/frontend/issues/list/components/issue_card_time_info_spec.js
index c3f13ca6f9a..b0d3a63a8cf 100644
--- a/spec/frontend/issues/list/components/issue_card_time_info_spec.js
+++ b/spec/frontend/issues/list/components/issue_card_time_info_spec.js
@@ -21,7 +21,7 @@ describe('CE IssueCardTimeInfo component', () => {
};
const findMilestone = () => wrapper.find('[data-testid="issuable-milestone"]');
- const findMilestoneTitle = () => findMilestone().find(GlLink).attributes('title');
+ const findMilestoneTitle = () => findMilestone().findComponent(GlLink).attributes('title');
const findDueDate = () => wrapper.find('[data-testid="issuable-due-date"]');
const mountComponent = ({
@@ -56,8 +56,8 @@ describe('CE IssueCardTimeInfo component', () => {
const milestone = findMilestone();
expect(milestone.text()).toBe(issue.milestone.title);
- expect(milestone.find(GlIcon).props('name')).toBe('clock');
- expect(milestone.find(GlLink).attributes('href')).toBe(issue.milestone.webPath);
+ expect(milestone.findComponent(GlIcon).props('name')).toBe('clock');
+ expect(milestone.findComponent(GlLink).attributes('href')).toBe(issue.milestone.webPath);
});
describe.each`
@@ -84,7 +84,7 @@ describe('CE IssueCardTimeInfo component', () => {
expect(dueDate.text()).toBe('Dec 12, 2020');
expect(dueDate.attributes('title')).toBe('Due date');
- expect(dueDate.find(GlIcon).props('name')).toBe('calendar');
+ expect(dueDate.findComponent(GlIcon).props('name')).toBe('calendar');
expect(dueDate.classes()).not.toContain('gl-text-red-500');
});
});
@@ -118,6 +118,6 @@ describe('CE IssueCardTimeInfo component', () => {
expect(timeEstimate.text()).toBe(issue.humanTimeEstimate);
expect(timeEstimate.attributes('title')).toBe('Estimate');
- expect(timeEstimate.find(GlIcon).props('name')).toBe('timer');
+ expect(timeEstimate.findComponent(GlIcon).props('name')).toBe('timer');
});
});
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 a39853fd29c..5133c02b190 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, GlLink } from '@gitlab/ui';
+import { GlButton, GlEmptyState } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { mount, shallowMount } from '@vue/test-utils';
import AxiosMockAdapter from 'axios-mock-adapter';
@@ -29,7 +29,6 @@ import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_ro
import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/constants';
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,
@@ -58,6 +57,10 @@ import {
WORK_ITEM_TYPE_ENUM_TASK,
WORK_ITEM_TYPE_ENUM_TEST_CASE,
} from '~/work_items/constants';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
+
+import('~/issuable/bulk_update_sidebar');
+import('~/users_select');
jest.mock('@sentry/browser');
jest.mock('~/flash');
@@ -122,7 +125,6 @@ describe('CE IssuesListApp component', () => {
const findGlButtons = () => wrapper.findAllComponents(GlButton);
const findGlButtonAt = (index) => findGlButtons().at(index);
const findGlEmptyState = () => wrapper.findComponent(GlEmptyState);
- const findGlLink = () => wrapper.findComponent(GlLink);
const findIssuableList = () => wrapper.findComponent(IssuableList);
const findNewIssueDropdown = () => wrapper.findComponent(NewIssueDropdown);
@@ -430,8 +432,9 @@ describe('CE IssuesListApp component', () => {
});
});
- it('is not set from url params', () => {
- expect(findIssuableList().props('initialFilterValue')).toEqual([]);
+ it('is set from url params and removes search terms', () => {
+ const expected = filteredTokens.filter((token) => token.type !== FILTERED_SEARCH_TERM);
+ expect(findIssuableList().props('initialFilterValue')).toEqual(expected);
});
it('shows an alert to tell the user they must be signed in to search', () => {
@@ -562,15 +565,16 @@ describe('CE IssuesListApp component', () => {
it('shows Jira integration information', () => {
const paragraphs = wrapper.findAll('p');
- expect(paragraphs.at(2).text()).toContain(IssuesListApp.i18n.jiraIntegrationTitle);
- expect(paragraphs.at(3).text()).toContain(
+ 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(4).text()).toContain(
+ expect(paragraphs.at(3).text()).toContain(
IssuesListApp.i18n.jiraIntegrationSecondaryMessage,
);
- expect(findGlLink().text()).toBe('Enable the Jira integration');
- expect(findGlLink().attributes('href')).toBe(defaultProvide.jiraIntegrationPath);
+ expect(links.at(1).text()).toBe('Enable the Jira integration');
+ expect(links.at(1).attributes('href')).toBe(defaultProvide.jiraIntegrationPath);
});
});
@@ -1006,8 +1010,9 @@ describe('CE IssuesListApp component', () => {
findIssuableList().vm.$emit('filter', filteredTokens);
});
- it('does not update url params', () => {
- expect(router.push).not.toHaveBeenCalled();
+ it('removes search terms', () => {
+ const expected = filteredTokens.filter((token) => token.type !== FILTERED_SEARCH_TERM);
+ expect(findIssuableList().props('initialFilterValue')).toEqual(expected);
});
it('shows an alert to tell the user they must be signed in to search', () => {
diff --git a/spec/frontend/issues/list/components/jira_issues_import_status_app_spec.js b/spec/frontend/issues/list/components/jira_issues_import_status_app_spec.js
index 2d773e8bf56..406b1fbc1af 100644
--- a/spec/frontend/issues/list/components/jira_issues_import_status_app_spec.js
+++ b/spec/frontend/issues/list/components/jira_issues_import_status_app_spec.js
@@ -11,9 +11,9 @@ describe('JiraIssuesImportStatus', () => {
};
let wrapper;
- const findAlert = () => wrapper.find(GlAlert);
+ const findAlert = () => wrapper.findComponent(GlAlert);
- const findAlertLabel = () => wrapper.find(GlAlert).find(GlLabel);
+ const findAlertLabel = () => wrapper.findComponent(GlAlert).findComponent(GlLabel);
const mountComponent = ({
shouldShowFinishedAlert = false,
@@ -49,7 +49,7 @@ describe('JiraIssuesImportStatus', () => {
});
it('does not show an alert', () => {
- expect(wrapper.find(GlAlert).exists()).toBe(false);
+ expect(wrapper.findComponent(GlAlert).exists()).toBe(false);
});
});
@@ -105,12 +105,12 @@ describe('JiraIssuesImportStatus', () => {
shouldShowInProgressAlert: true,
});
- expect(wrapper.find(GlAlert).exists()).toBe(true);
+ expect(wrapper.findComponent(GlAlert).exists()).toBe(true);
findAlert().vm.$emit('dismiss');
await nextTick();
- expect(wrapper.find(GlAlert).exists()).toBe(false);
+ expect(wrapper.findComponent(GlAlert).exists()).toBe(false);
});
});
});
diff --git a/spec/frontend/issues/new/components/title_suggestions_item_spec.js b/spec/frontend/issues/new/components/title_suggestions_item_spec.js
index 5eb30b52de5..c54a762440f 100644
--- a/spec/frontend/issues/new/components/title_suggestions_item_spec.js
+++ b/spec/frontend/issues/new/components/title_suggestions_item_spec.js
@@ -20,7 +20,7 @@ describe('Issue title suggestions item component', () => {
}
const findLink = () => wrapper.findComponent(GlLink);
- const findAuthorLink = () => wrapper.findAll(GlLink).at(1);
+ const findAuthorLink = () => wrapper.findAllComponents(GlLink).at(1);
const findIcon = () => wrapper.findComponent(GlIcon);
const findTooltip = () => wrapper.findComponent(GlTooltip);
const findUserAvatar = () => wrapper.findComponent(UserAvatarImage);
@@ -105,7 +105,7 @@ describe('Issue title suggestions item component', () => {
const count = wrapper.findAll('.suggestion-counts span').at(0);
expect(count.text()).toContain('1');
- expect(count.find(GlIcon).props('name')).toBe('thumb-up');
+ expect(count.findComponent(GlIcon).props('name')).toBe('thumb-up');
});
it('renders notes count', () => {
@@ -114,7 +114,7 @@ describe('Issue title suggestions item component', () => {
const count = wrapper.findAll('.suggestion-counts span').at(1);
expect(count.text()).toContain('2');
- expect(count.find(GlIcon).props('name')).toBe('comment');
+ expect(count.findComponent(GlIcon).props('name')).toBe('comment');
});
});
diff --git a/spec/frontend/issues/new/components/title_suggestions_spec.js b/spec/frontend/issues/new/components/title_suggestions_spec.js
index 0a64890e4ca..1cd6576967a 100644
--- a/spec/frontend/issues/new/components/title_suggestions_spec.js
+++ b/spec/frontend/issues/new/components/title_suggestions_spec.js
@@ -83,7 +83,7 @@ describe('Issue title suggestions component', () => {
wrapper.setData(data);
await nextTick();
- expect(wrapper.findAll(TitleSuggestionsItem).length).toBe(2);
+ expect(wrapper.findAllComponents(TitleSuggestionsItem).length).toBe(2);
});
it('adds margin class to first item', async () => {
diff --git a/spec/frontend/issues/related_merge_requests/components/related_merge_requests_spec.js b/spec/frontend/issues/related_merge_requests/components/related_merge_requests_spec.js
index 4df04cd5257..d30a8c081cc 100644
--- a/spec/frontend/issues/related_merge_requests/components/related_merge_requests_spec.js
+++ b/spec/frontend/issues/related_merge_requests/components/related_merge_requests_spec.js
@@ -65,9 +65,9 @@ describe('RelatedMergeRequests', () => {
describe('template', () => {
it('should render related merge request items', () => {
expect(wrapper.find('[data-testid="count"]').text()).toBe('2');
- expect(wrapper.findAll(RelatedIssuableItem)).toHaveLength(2);
+ expect(wrapper.findAllComponents(RelatedIssuableItem)).toHaveLength(2);
- const props = wrapper.findAll(RelatedIssuableItem).at(1).props();
+ const props = wrapper.findAllComponents(RelatedIssuableItem).at(1).props();
const data = mockData[1];
expect(props.idKey).toEqual(data.id);
diff --git a/spec/frontend/issues/show/components/app_spec.js b/spec/frontend/issues/show/components/app_spec.js
index 12f9707da04..3d027e2084c 100644
--- a/spec/frontend/issues/show/components/app_spec.js
+++ b/spec/frontend/issues/show/components/app_spec.js
@@ -461,7 +461,7 @@ describe('Issuable output', () => {
describe('when title is not in view', () => {
beforeEach(() => {
wrapper.vm.state.titleText = 'Sticky header title';
- wrapper.find(GlIntersectionObserver).vm.$emit('disappear');
+ wrapper.findComponent(GlIntersectionObserver).vm.$emit('disappear');
});
it('shows with title', () => {
diff --git a/spec/frontend/issues/show/components/description_spec.js b/spec/frontend/issues/show/components/description_spec.js
index bdb1448148e..9d9abce887b 100644
--- a/spec/frontend/issues/show/components/description_spec.js
+++ b/spec/frontend/issues/show/components/description_spec.js
@@ -12,6 +12,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 Description from '~/issues/show/components/description.vue';
import { updateHistory } from '~/lib/utils/url_utility';
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
@@ -71,7 +72,11 @@ describe('Description component', () => {
const findModal = () => wrapper.findComponent(GlModal);
const findWorkItemDetailModal = () => wrapper.findComponent(WorkItemDetailModal);
- function createComponent({ props = {}, provide } = {}) {
+ function createComponent({
+ props = {},
+ provide,
+ createWorkItemFromTaskHandler = createWorkItemFromTaskSuccessHandler,
+ } = {}) {
wrapper = shallowMountExtended(Description, {
propsData: {
issueId: 1,
@@ -85,7 +90,7 @@ describe('Description component', () => {
apolloProvider: createMockApollo([
[workItemQuery, queryHandler],
[workItemTypesQuery, workItemTypesQueryHandler],
- [createWorkItemFromTaskMutation, createWorkItemFromTaskSuccessHandler],
+ [createWorkItemFromTaskMutation, createWorkItemFromTaskHandler],
]),
mocks: {
$toast,
@@ -317,7 +322,28 @@ describe('Description component', () => {
expect(findModal().exists()).toBe(false);
});
+ it('shows toast after delete success', async () => {
+ const newDesc = 'description';
+ findWorkItemDetailModal().vm.$emit('workItemDeleted', newDesc);
+
+ expect(wrapper.emitted('updateDescription')).toEqual([[newDesc]]);
+ expect($toast.show).toHaveBeenCalledWith('Task deleted');
+ });
+ });
+
+ describe('creating work item from checklist item', () => {
it('emits `updateDescription` after creating new work item', async () => {
+ createComponent({
+ props: {
+ descriptionHtml: descriptionHtmlWithCheckboxes,
+ },
+ provide: {
+ glFeatures: {
+ workItemsCreateFromMarkdown: true,
+ },
+ },
+ });
+
const newDescription = `<p>New description</p>`;
await findConvertToTaskButton().trigger('click');
@@ -327,12 +353,28 @@ describe('Description component', () => {
expect(wrapper.emitted('updateDescription')).toEqual([[newDescription]]);
});
- it('shows toast after delete success', async () => {
- const newDesc = 'description';
- findWorkItemDetailModal().vm.$emit('workItemDeleted', newDesc);
+ it('shows flash message when creating task fails', async () => {
+ createComponent({
+ props: {
+ descriptionHtml: descriptionHtmlWithCheckboxes,
+ },
+ provide: {
+ glFeatures: {
+ workItemsCreateFromMarkdown: true,
+ },
+ },
+ createWorkItemFromTaskHandler: jest.fn().mockRejectedValue({}),
+ });
- expect(wrapper.emitted('updateDescription')).toEqual([[newDesc]]);
- expect($toast.show).toHaveBeenCalledWith('Task deleted');
+ await findConvertToTaskButton().trigger('click');
+
+ await waitForPromises();
+
+ expect(createFlash).toHaveBeenCalledWith(
+ expect.objectContaining({
+ message: 'Something went wrong when creating task. Please try again.',
+ }),
+ );
});
});
diff --git a/spec/frontend/issues/show/components/edit_actions_spec.js b/spec/frontend/issues/show/components/edit_actions_spec.js
index d58bf1be812..11c43ea4388 100644
--- a/spec/frontend/issues/show/components/edit_actions_spec.js
+++ b/spec/frontend/issues/show/components/edit_actions_spec.js
@@ -2,16 +2,9 @@ import { GlButton } from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
-import { mockTracking } from 'helpers/tracking_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import waitForPromises from 'helpers/wait_for_promises';
import IssuableEditActions from '~/issues/show/components/edit_actions.vue';
-import DeleteIssueModal from '~/issues/show/components/delete_issue_modal.vue';
import eventHub from '~/issues/show/event_hub';
-import {
- getIssueStateQueryResponse,
- updateIssueStateQueryResponse,
-} from '../mock_data/apollo_mock';
describe('Edit Actions component', () => {
let wrapper;
@@ -31,8 +24,6 @@ describe('Edit Actions component', () => {
},
};
- const modalId = 'delete-issuable-modal-1';
-
const createComponent = ({ props, data } = {}) => {
fakeApollo = createMockApollo([], mockResolvers);
@@ -50,16 +41,13 @@ describe('Edit Actions component', () => {
data() {
return {
issueState: {},
- modalId,
...data,
};
},
});
};
- const findModal = () => wrapper.findComponent(DeleteIssueModal);
const findEditButtons = () => wrapper.findAllComponents(GlButton);
- const findDeleteButton = () => wrapper.findByTestId('issuable-delete-button');
const findSaveButton = () => wrapper.findByTestId('issuable-save-button');
const findCancelButton = () => wrapper.findByTestId('issuable-cancel-button');
@@ -79,23 +67,12 @@ describe('Edit Actions component', () => {
});
});
- it('does not render the delete button if canDestroy is false', () => {
- createComponent({ props: { canDestroy: false } });
- expect(findDeleteButton().exists()).toBe(false);
- });
-
it('disables save button when title is blank', () => {
createComponent({ props: { formState: { title: '', issue_type: '' } } });
expect(findSaveButton().attributes('disabled')).toBe('true');
});
- it('does not render the delete button if showDeleteButton is false', () => {
- createComponent({ props: { showDeleteButton: false } });
-
- expect(findDeleteButton().exists()).toBe(false);
- });
-
describe('updateIssuable', () => {
beforeEach(() => {
jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
@@ -119,63 +96,4 @@ describe('Edit Actions component', () => {
expect(eventHub.$emit).toHaveBeenCalledWith('close.form');
});
});
-
- describe('delete issue button', () => {
- let trackingSpy;
-
- beforeEach(() => {
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- });
-
- it('tracks clicking on button', () => {
- findDeleteButton().vm.$emit('click');
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_button', {
- label: 'delete_issue',
- });
- });
- });
-
- describe('delete issue modal', () => {
- it('renders', () => {
- expect(findModal().props()).toEqual({
- issuePath: 'gitlab-org/gitlab-test/-/issues/1',
- issueType: 'Issue',
- modalId,
- title: 'Delete issue',
- });
- });
- });
-
- describe('deleteIssuable', () => {
- beforeEach(() => {
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- });
-
- it('does not send the `delete.issuable` event when clicking delete button', () => {
- findDeleteButton().vm.$emit('click');
- expect(eventHub.$emit).not.toHaveBeenCalled();
- });
-
- it('sends the `delete.issuable` event when clicking the delete confirm button', async () => {
- expect(eventHub.$emit).toHaveBeenCalledTimes(0);
- findModal().vm.$emit('delete');
- expect(eventHub.$emit).toHaveBeenCalledWith('delete.issuable');
- expect(eventHub.$emit).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('with Apollo cache mock', () => {
- it('renders the right delete button text per apollo cache type', async () => {
- mockIssueStateData.mockResolvedValue(getIssueStateQueryResponse);
- await waitForPromises();
- expect(findDeleteButton().text()).toBe('Delete issue');
- });
-
- it('should not change the delete button text per apollo cache mutation', async () => {
- mockIssueStateData.mockResolvedValue(updateIssueStateQueryResponse);
- await waitForPromises();
- expect(findDeleteButton().text()).toBe('Delete issue');
- });
- });
});
diff --git a/spec/frontend/issues/show/components/fields/description_spec.js b/spec/frontend/issues/show/components/fields/description_spec.js
index d0e33f0b980..61433607a2b 100644
--- a/spec/frontend/issues/show/components/fields/description_spec.js
+++ b/spec/frontend/issues/show/components/fields/description_spec.js
@@ -6,7 +6,7 @@ import MarkdownField from '~/vue_shared/components/markdown/field.vue';
describe('Description field component', () => {
let wrapper;
- const findTextarea = () => wrapper.find({ ref: 'textarea' });
+ const findTextarea = () => wrapper.findComponent({ ref: 'textarea' });
const mountComponent = (description = 'test') =>
shallowMount(DescriptionField, {
diff --git a/spec/frontend/issues/show/components/fields/title_spec.js b/spec/frontend/issues/show/components/fields/title_spec.js
index de04405d89b..a5fa96d8d64 100644
--- a/spec/frontend/issues/show/components/fields/title_spec.js
+++ b/spec/frontend/issues/show/components/fields/title_spec.js
@@ -5,7 +5,7 @@ import eventHub from '~/issues/show/event_hub';
describe('Title field component', () => {
let wrapper;
- const findInput = () => wrapper.find({ ref: 'input' });
+ const findInput = () => wrapper.findComponent({ ref: 'input' });
beforeEach(() => {
jest.spyOn(eventHub, '$emit');
diff --git a/spec/frontend/issues/show/components/header_actions_spec.js b/spec/frontend/issues/show/components/header_actions_spec.js
index 329c4234f30..dc2b3c6fc48 100644
--- a/spec/frontend/issues/show/components/header_actions_spec.js
+++ b/spec/frontend/issues/show/components/header_actions_spec.js
@@ -65,17 +65,17 @@ describe('HeaderActions component', () => {
},
};
- const findToggleIssueStateButton = () => wrapper.find(GlButton);
+ const findToggleIssueStateButton = () => wrapper.findComponent(GlButton);
const findDropdownBy = (dataTestId) => wrapper.find(`[data-testid="${dataTestId}"]`);
const findMobileDropdown = () => findDropdownBy('mobile-dropdown');
const findDesktopDropdown = () => findDropdownBy('desktop-dropdown');
- const findMobileDropdownItems = () => findMobileDropdown().findAll(GlDropdownItem);
- const findDesktopDropdownItems = () => findDesktopDropdown().findAll(GlDropdownItem);
+ const findMobileDropdownItems = () => findMobileDropdown().findAllComponents(GlDropdownItem);
+ const findDesktopDropdownItems = () => findDesktopDropdown().findAllComponents(GlDropdownItem);
- const findModal = () => wrapper.find(GlModal);
+ const findModal = () => wrapper.findComponent(GlModal);
- const findModalLinkAt = (index) => findModal().findAll(GlLink).at(index);
+ const findModalLinkAt = (index) => findModal().findAllComponents(GlLink).at(index);
const mountComponent = ({
props = {},
diff --git a/spec/frontend/issues/show/components/incidents/create_timeline_events_form_spec.js b/spec/frontend/issues/show/components/incidents/create_timeline_events_form_spec.js
index 3ab2bb3460b..1286617d64a 100644
--- a/spec/frontend/issues/show/components/incidents/create_timeline_events_form_spec.js
+++ b/spec/frontend/issues/show/components/incidents/create_timeline_events_form_spec.js
@@ -1,13 +1,13 @@
import VueApollo from 'vue-apollo';
import Vue from 'vue';
import { GlDatepicker } from '@gitlab/ui';
-import { __, s__ } from '~/locale';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import CreateTimelineEvent from '~/issues/show/components/incidents/create_timeline_event.vue';
import TimelineEventsForm from '~/issues/show/components/incidents/timeline_events_form.vue';
import createTimelineEventMutation from '~/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql';
import getTimelineEvents from '~/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql';
+import { timelineFormI18n } from '~/issues/show/components/incidents/constants';
import createMockApollo from 'helpers/mock_apollo_helper';
import { createAlert } from '~/flash';
import { useFakeDate } from 'helpers/fake_date';
@@ -35,24 +35,21 @@ describe('Create Timeline events', () => {
let responseSpy;
let apolloProvider;
- const findSubmitButton = () => wrapper.findByText(__('Save'));
- const findSubmitAndAddButton = () =>
- wrapper.findByText(s__('Incident|Save and add another event'));
- const findCancelButton = () => wrapper.findByText(__('Cancel'));
+ const findSubmitButton = () => wrapper.findByText(timelineFormI18n.save);
+ const findSubmitAndAddButton = () => wrapper.findByText(timelineFormI18n.saveAndAdd);
+ const findCancelButton = () => wrapper.findByText(timelineFormI18n.cancel);
const findDatePicker = () => wrapper.findComponent(GlDatepicker);
const findNoteInput = () => wrapper.findByTestId('input-note');
const setNoteInput = () => {
- const textarea = findNoteInput().element;
- textarea.value = mockInputData.note;
- textarea.dispatchEvent(new Event('input'));
+ findNoteInput().setValue(mockInputData.note);
};
const findHourInput = () => wrapper.findByTestId('input-hours');
const findMinuteInput = () => wrapper.findByTestId('input-minutes');
const setDatetime = () => {
const inputDate = new Date(mockInputData.occurredAt);
findDatePicker().vm.$emit('input', inputDate);
- findHourInput().vm.$emit('input', inputDate.getHours());
- findMinuteInput().vm.$emit('input', inputDate.getMinutes());
+ findHourInput().setValue(inputDate.getHours());
+ findMinuteInput().setValue(inputDate.getMinutes());
};
const fillForm = () => {
setDatetime();
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
new file mode 100644
index 00000000000..4c1638a9147
--- /dev/null
+++ b/spec/frontend/issues/show/components/incidents/edit_timeline_event_spec.js
@@ -0,0 +1,44 @@
+import EditTimelineEvent from '~/issues/show/components/incidents/edit_timeline_event.vue';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import TimelineEventsForm from '~/issues/show/components/incidents/timeline_events_form.vue';
+
+import { mockEvents, fakeEventData, mockInputData } from './mock_data';
+
+describe('Edit Timeline events', () => {
+ let wrapper;
+
+ const mountComponent = () => {
+ wrapper = mountExtended(EditTimelineEvent, {
+ propsData: {
+ event: mockEvents[0],
+ editTimelineEventActive: false,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ mountComponent();
+ });
+
+ const findTimelineEventsForm = () => wrapper.findComponent(TimelineEventsForm);
+
+ const mockSaveData = { ...fakeEventData, ...mockInputData };
+
+ describe('editTimelineEvent', () => {
+ const saveEventEvent = { 'handle-save-edit': [[mockSaveData, false]] };
+
+ it('should call the mutation with the right variables', async () => {
+ await findTimelineEventsForm().vm.$emit('save-event', mockSaveData, false);
+
+ expect(wrapper.emitted()).toEqual(saveEventEvent);
+ });
+
+ it('should close the form on cancel', async () => {
+ const cancelEvent = { 'hide-edit': [[]] };
+
+ await findTimelineEventsForm().vm.$emit('cancel');
+
+ expect(wrapper.emitted()).toEqual(cancelEvent);
+ });
+ });
+});
diff --git a/spec/frontend/issues/show/components/incidents/highlight_bar_spec.js b/spec/frontend/issues/show/components/incidents/highlight_bar_spec.js
index 155ae703e48..1cfb7d12a91 100644
--- a/spec/frontend/issues/show/components/incidents/highlight_bar_spec.js
+++ b/spec/frontend/issues/show/components/incidents/highlight_bar_spec.js
@@ -41,7 +41,7 @@ describe('Highlight Bar', () => {
}
});
- const findLink = () => wrapper.find(GlLink);
+ const findLink = () => wrapper.findComponent(GlLink);
describe('empty state', () => {
beforeEach(() => {
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 8e090645be2..d92aeabba0f 100644
--- a/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js
+++ b/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js
@@ -61,12 +61,12 @@ describe('Incident Tabs component', () => {
);
};
- const findTabs = () => wrapper.findAll(GlTab);
+ const findTabs = () => wrapper.findAllComponents(GlTab);
const findSummaryTab = () => findTabs().at(0);
const findAlertDetailsTab = () => wrapper.find('[data-testid="alert-details-tab"]');
- const findAlertDetailsComponent = () => wrapper.find(AlertDetailsTable);
- const findDescriptionComponent = () => wrapper.find(DescriptionComponent);
- const findHighlightBarComponent = () => wrapper.find(HighlightBar);
+ const findAlertDetailsComponent = () => wrapper.findComponent(AlertDetailsTable);
+ const findDescriptionComponent = () => wrapper.findComponent(DescriptionComponent);
+ const findHighlightBarComponent = () => wrapper.findComponent(HighlightBar);
const findTimelineTab = () => wrapper.findComponent(TimelineTab);
describe('empty state', () => {
diff --git a/spec/frontend/issues/show/components/incidents/mock_data.js b/spec/frontend/issues/show/components/incidents/mock_data.js
index 75c0a7350ae..adea2b6df59 100644
--- a/spec/frontend/issues/show/components/incidents/mock_data.js
+++ b/spec/frontend/issues/show/components/incidents/mock_data.js
@@ -49,6 +49,15 @@ export const mockEvents = [
},
];
+const mockUpdatedEvent = {
+ id: 'gid://gitlab/IncidentManagement::TimelineEvent/8',
+ note: 'another one23',
+ noteHtml: '<p>another one23</p>',
+ action: 'comment',
+ occurredAt: '2022-07-01T12:47:00Z',
+ createdAt: '2022-07-20T12:47:40Z',
+};
+
export const timelineEventsQueryListResponse = {
data: {
project: {
@@ -93,6 +102,29 @@ export const timelineEventsCreateEventError = {
},
};
+export const timelineEventsEditEventResponse = {
+ data: {
+ timelineEventUpdate: {
+ timelineEvent: {
+ ...mockUpdatedEvent,
+ },
+ errors: [],
+ __typename: 'TimelineEventUpdatePayload',
+ },
+ },
+};
+
+export const timelineEventsEditEventError = {
+ data: {
+ timelineEventUpdate: {
+ timelineEvent: {
+ ...mockUpdatedEvent,
+ },
+ errors: ['Create error'],
+ },
+ },
+};
+
const timelineEventDeleteData = (errors = []) => {
return {
data: {
@@ -125,3 +157,13 @@ export const mockGetTimelineData = {
},
},
};
+
+export const fakeDate = '2020-07-08T00:00:00.000Z';
+
+export const mockInputData = {
+ note: 'test',
+ occurredAt: '2020-08-10T02:30:00.000Z',
+};
+
+const { id, note, occurredAt } = mockEvents[0];
+export const fakeEventData = { id, note, occurredAt };
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 cd2cbb63246..7f086a276f7 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
@@ -4,6 +4,8 @@ import { GlDatepicker } from '@gitlab/ui';
import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import TimelineEventsForm from '~/issues/show/components/incidents/timeline_events_form.vue';
+import MarkdownField from '~/vue_shared/components/markdown/field.vue';
+import { timelineFormI18n } from '~/issues/show/components/incidents/constants';
import { createAlert } from '~/flash';
import { useFakeDate } from 'helpers/fake_date';
@@ -13,6 +15,8 @@ jest.mock('~/flash');
const fakeDate = '2020-07-08T00:00:00.000Z';
+const mockInputDate = new Date('2021-08-12');
+
describe('Timeline events form', () => {
// July 8 2020
useFakeDate(fakeDate);
@@ -21,7 +25,7 @@ describe('Timeline events form', () => {
const mountComponent = ({ mountMethod = shallowMountExtended }) => {
wrapper = mountMethod(TimelineEventsForm, {
propsData: {
- hasTimelineEvents: true,
+ showSaveAndAdd: true,
isEventProcessed: false,
},
});
@@ -32,17 +36,17 @@ describe('Timeline events form', () => {
wrapper.destroy();
});
- const findSubmitButton = () => wrapper.findByText('Save');
- const findSubmitAndAddButton = () => wrapper.findByText('Save and add another event');
- const findCancelButton = () => wrapper.findByText('Cancel');
+ const findMarkdownField = () => wrapper.findComponent(MarkdownField);
+ const findSubmitButton = () => wrapper.findByText(timelineFormI18n.save);
+ const findSubmitAndAddButton = () => wrapper.findByText(timelineFormI18n.saveAndAdd);
+ const findCancelButton = () => wrapper.findByText(timelineFormI18n.cancel);
const findDatePicker = () => wrapper.findComponent(GlDatepicker);
- const findDatePickerInput = () => wrapper.findByTestId('input-datepicker');
const findHourInput = () => wrapper.findByTestId('input-hours');
const findMinuteInput = () => wrapper.findByTestId('input-minutes');
const setDatetime = () => {
- findDatePicker().vm.$emit('input', new Date('2021-08-12'));
- findHourInput().vm.$emit('input', 5);
- findMinuteInput().vm.$emit('input', 45);
+ findDatePicker().vm.$emit('input', mockInputDate);
+ findHourInput().setValue(5);
+ findMinuteInput().setValue(45);
};
const submitForm = async () => {
@@ -58,6 +62,22 @@ describe('Timeline events form', () => {
await waitForPromises();
};
+ it('renders markdown-field component with correct list of toolbar items', () => {
+ mountComponent({ mountMethod: mountExtended });
+
+ expect(findMarkdownField().props('restrictedToolBarItems')).toEqual([
+ 'quote',
+ 'strikethrough',
+ 'bullet-list',
+ 'numbered-list',
+ 'task-list',
+ 'collapsible-section',
+ 'table',
+ 'attach-file',
+ 'full-screen',
+ ]);
+ });
+
describe('form button behaviour', () => {
beforeEach(() => {
mountComponent({ mountMethod: mountExtended });
@@ -87,14 +107,14 @@ describe('Timeline events form', () => {
setDatetime();
await nextTick();
- expect(findDatePickerInput().element.value).toBe('2021-08-12');
+ expect(findDatePicker().props('value')).toBe(mockInputDate);
expect(findHourInput().element.value).toBe('5');
expect(findMinuteInput().element.value).toBe('45');
wrapper.vm.clear();
await nextTick();
- expect(findDatePickerInput().element.value).toBe('2020-07-08');
+ expect(findDatePicker().props('value')).toStrictEqual(new Date(fakeDate));
expect(findHourInput().element.value).toBe('0');
expect(findMinuteInput().element.value).toBe('0');
});
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 90e55003ab3..1bf8d68efd4 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,6 +1,7 @@
import timezoneMock from 'timezone-mock';
import { GlIcon, GlDropdown } from '@gitlab/ui';
import { nextTick } from 'vue';
+import { timelineItemI18n } from '~/issues/show/components/incidents/constants';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import IncidentTimelineEventItem from '~/issues/show/components/incidents/timeline_events_item.vue';
import { mockEvents } from './mock_data';
@@ -15,21 +16,19 @@ describe('IncidentTimelineEventList', () => {
action,
noteHtml,
occurredAt,
- isLastItem: false,
...propsData,
},
provide: {
- canUpdate: false,
+ canUpdateTimelineEvent: false,
...provide,
},
});
};
const findCommentIcon = () => wrapper.findComponent(GlIcon);
- const findTextContainer = () => wrapper.findByTestId('event-text-container');
const findEventTime = () => wrapper.findByTestId('event-time');
const findDropdown = () => wrapper.findComponent(GlDropdown);
- const findDeleteButton = () => wrapper.findByText('Delete');
+ const findDeleteButton = () => wrapper.findByText(timelineItemI18n.delete);
describe('template', () => {
it('shows comment icon', () => {
@@ -50,20 +49,6 @@ describe('IncidentTimelineEventList', () => {
expect(findEventTime().text()).toBe('15:59 UTC');
});
- describe('last item in list', () => {
- it('shows a bottom border when not the last item', () => {
- mountComponent();
-
- expect(findTextContainer().classes()).toContain('gl-border-1');
- });
-
- it('does not show a bottom border when the last item', () => {
- mountComponent({ propsData: { isLastItem: true } });
-
- expect(wrapper.classes()).not.toContain('gl-border-1');
- });
- });
-
describe.each`
timezone
${'Europe/London'}
@@ -96,20 +81,20 @@ describe('IncidentTimelineEventList', () => {
});
it('shows dropdown and delete item when user has update permission', () => {
- mountComponent({ provide: { canUpdate: true } });
+ mountComponent({ provide: { canUpdateTimelineEvent: true } });
expect(findDropdown().exists()).toBe(true);
expect(findDeleteButton().exists()).toBe(true);
});
it('triggers a delete when the delete button is clicked', async () => {
- mountComponent({ provide: { canUpdate: true } });
+ mountComponent({ provide: { canUpdateTimelineEvent: true } });
findDeleteButton().trigger('click');
await nextTick();
- expect(wrapper.emitted().delete).toBeTruthy();
+ expect(wrapper.emitted().delete).toHaveLength(1);
});
});
});
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 4d2d53c990e..dff1c429d07 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
@@ -3,16 +3,24 @@ import VueApollo from 'vue-apollo';
import Vue from 'vue';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
import IncidentTimelineEventList from '~/issues/show/components/incidents/timeline_events_list.vue';
-import IncidentTimelineEventListItem from '~/issues/show/components/incidents/timeline_events_item.vue';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import IncidentTimelineEventItem from '~/issues/show/components/incidents/timeline_events_item.vue';
+import EditTimelineEvent from '~/issues/show/components/incidents/edit_timeline_event.vue';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import deleteTimelineEventMutation from '~/issues/show/components/incidents/graphql/queries/delete_timeline_event.mutation.graphql';
+import editTimelineEventMutation from '~/issues/show/components/incidents/graphql/queries/edit_timeline_event.mutation.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
+import { useFakeDate } from 'helpers/fake_date';
import { createAlert } from '~/flash';
import {
mockEvents,
timelineEventsDeleteEventResponse,
timelineEventsDeleteEventError,
+ timelineEventsEditEventResponse,
+ timelineEventsEditEventError,
+ fakeDate,
+ fakeEventData,
+ mockInputData,
} from './mock_data';
Vue.use(VueApollo);
@@ -20,83 +28,73 @@ Vue.use(VueApollo);
jest.mock('~/flash');
jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal');
-const deleteEventResponse = jest.fn();
-
-function createMockApolloProvider() {
- deleteEventResponse.mockResolvedValue(timelineEventsDeleteEventResponse);
- const requestHandlers = [[deleteTimelineEventMutation, deleteEventResponse]];
- return createMockApollo(requestHandlers);
-}
-
const mockConfirmAction = ({ confirmed }) => {
confirmAction.mockResolvedValueOnce(confirmed);
};
describe('IncidentTimelineEventList', () => {
+ useFakeDate(fakeDate);
let wrapper;
+ const deleteResponseSpy = jest.fn().mockResolvedValue(timelineEventsDeleteEventResponse);
+ const editResponseSpy = jest.fn().mockResolvedValue(timelineEventsEditEventResponse);
- const mountComponent = (mockApollo) => {
- const apollo = mockApollo ? { apolloProvider: mockApollo } : {};
+ const requestHandlers = [
+ [deleteTimelineEventMutation, deleteResponseSpy],
+ [editTimelineEventMutation, editResponseSpy],
+ ];
+ const apolloProvider = createMockApollo(requestHandlers);
- wrapper = shallowMountExtended(IncidentTimelineEventList, {
+ const mountComponent = () => {
+ wrapper = mountExtended(IncidentTimelineEventList, {
+ propsData: {
+ timelineEvents: mockEvents,
+ },
provide: {
fullPath: 'group/project',
issuableId: '1',
+ canUpdateTimelineEvent: true,
},
- propsData: {
- timelineEvents: mockEvents,
- },
- ...apollo,
+ apolloProvider,
});
};
const findTimelineEventGroups = () => wrapper.findAllByTestId('timeline-group');
- const findItems = (base = wrapper) => base.findAll(IncidentTimelineEventListItem);
+ const findItems = (base = wrapper) => base.findAllComponents(IncidentTimelineEventItem);
const findFirstTimelineEventGroup = () => findTimelineEventGroups().at(0);
const findSecondTimelineEventGroup = () => findTimelineEventGroups().at(1);
const findDates = () => wrapper.findAllByTestId('event-date');
const clickFirstDeleteButton = async () => {
- findItems()
- .at(0)
- .vm.$emit('delete', { ...mockEvents[0] });
+ findItems().at(0).vm.$emit('delete', { fakeEventData });
await waitForPromises();
};
+ const clickFirstEditButton = async () => {
+ findItems().at(0).vm.$emit('edit');
+ await waitForPromises();
+ };
+ beforeEach(() => {
+ mountComponent();
+ });
+
afterEach(() => {
- confirmAction.mockReset();
- deleteEventResponse.mockReset();
wrapper.destroy();
});
describe('template', () => {
it('groups items correctly', () => {
- mountComponent();
-
expect(findTimelineEventGroups()).toHaveLength(2);
expect(findItems(findFirstTimelineEventGroup())).toHaveLength(1);
expect(findItems(findSecondTimelineEventGroup())).toHaveLength(2);
});
- it('sets the isLastItem prop correctly', () => {
- mountComponent();
-
- expect(findItems().at(0).props('isLastItem')).toBe(false);
- expect(findItems().at(1).props('isLastItem')).toBe(false);
- expect(findItems().at(2).props('isLastItem')).toBe(true);
- });
-
it('sets the event props correctly', () => {
- mountComponent();
-
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);
});
it('formats dates correctly', () => {
- mountComponent();
-
expect(findDates().at(0).text()).toBe('2022-03-22');
expect(findDates().at(1).text()).toBe('2022-03-23');
});
@@ -110,8 +108,6 @@ describe('IncidentTimelineEventList', () => {
describe(timezone, () => {
beforeEach(() => {
timezoneMock.register(timezone);
-
- mountComponent();
});
afterEach(() => {
@@ -131,12 +127,9 @@ describe('IncidentTimelineEventList', () => {
it('should delete when button is clicked', async () => {
const expectedVars = { input: { id: mockEvents[0].id } };
-
- mountComponent(createMockApolloProvider());
-
await clickFirstDeleteButton();
- expect(deleteEventResponse).toHaveBeenCalledWith(expectedVars);
+ expect(deleteResponseSpy).toHaveBeenCalledWith(expectedVars);
});
it('should show an error when delete returns an error', async () => {
@@ -144,8 +137,7 @@ describe('IncidentTimelineEventList', () => {
message: 'Error deleting incident timeline event: Item does not exist',
};
- mountComponent(createMockApolloProvider());
- deleteEventResponse.mockResolvedValue(timelineEventsDeleteEventError);
+ deleteResponseSpy.mockResolvedValue(timelineEventsDeleteEventError);
await clickFirstDeleteButton();
@@ -158,8 +150,7 @@ describe('IncidentTimelineEventList', () => {
error: new Error(),
message: 'Something went wrong while deleting the incident timeline event.',
};
- mountComponent(createMockApolloProvider());
- deleteEventResponse.mockRejectedValueOnce();
+ deleteResponseSpy.mockRejectedValueOnce();
await clickFirstDeleteButton();
@@ -167,4 +158,76 @@ describe('IncidentTimelineEventList', () => {
});
});
});
+
+ describe('Edit Functionality', () => {
+ beforeEach(() => {
+ mountComponent();
+ clickFirstEditButton();
+ });
+
+ const findEditEvent = () => wrapper.findComponent(EditTimelineEvent);
+ const mockSaveData = { ...fakeEventData, ...mockInputData };
+
+ describe('editTimelineEvent', () => {
+ it('should call the mutation with the right variables', async () => {
+ await findEditEvent().vm.$emit('handle-save-edit', mockSaveData);
+ await waitForPromises();
+
+ expect(editResponseSpy).toHaveBeenCalledWith({
+ input: mockSaveData,
+ });
+ });
+
+ it('should close the form on successful addition', async () => {
+ await findEditEvent().vm.$emit('handle-save-edit', mockSaveData);
+ await waitForPromises();
+
+ expect(findEditEvent().exists()).toBe(false);
+ });
+
+ it('should close the form on cancel', async () => {
+ await findEditEvent().vm.$emit('hide-edit');
+ await waitForPromises();
+
+ expect(findEditEvent().exists()).toBe(false);
+ });
+ });
+
+ describe('error handling', () => {
+ it('should show an error when submission returns an error', async () => {
+ const expectedAlertArgs = {
+ message: `Error updating incident timeline event: ${timelineEventsEditEventError.data.timelineEventUpdate.errors[0]}`,
+ };
+ editResponseSpy.mockResolvedValueOnce(timelineEventsEditEventError);
+
+ await findEditEvent().vm.$emit('handle-save-edit', mockSaveData);
+ await waitForPromises();
+
+ expect(createAlert).toHaveBeenCalledWith(expectedAlertArgs);
+ });
+
+ it('should show an error when submission fails', async () => {
+ const expectedAlertArgs = {
+ captureError: true,
+ error: new Error(),
+ message: 'Something went wrong while updating the incident timeline event.',
+ };
+ editResponseSpy.mockRejectedValueOnce();
+
+ await findEditEvent().vm.$emit('handle-save-edit', mockSaveData);
+ await waitForPromises();
+
+ expect(createAlert).toHaveBeenCalledWith(expectedAlertArgs);
+ });
+
+ it('should keep the form open on failed addition', async () => {
+ editResponseSpy.mockResolvedValueOnce(timelineEventsEditEventError);
+
+ await findEditEvent().vm.$emit('handle-save-edit', mockSaveData);
+ await waitForPromises();
+
+ expect(findEditEvent().exists()).toBe(true);
+ });
+ });
+ });
});
diff --git a/spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js b/spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js
index 2cdb971395d..5bac1d6e7ad 100644
--- a/spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js
+++ b/spec/frontend/issues/show/components/incidents/timeline_events_tab_spec.js
@@ -36,7 +36,7 @@ describe('TimelineEventsTab', () => {
provide: {
fullPath: 'group/project',
issuableId: '1',
- canUpdate: true,
+ canUpdateTimelineEvent: true,
...provide,
},
apolloProvider: mockApollo,
@@ -136,29 +136,20 @@ describe('TimelineEventsTab', () => {
it('should not show a button when user cannot update', () => {
mountComponent({
mockApollo: createMockApolloProvider(emptyResponse),
- provide: { canUpdate: false },
+ provide: { canUpdateTimelineEvent: false },
});
expect(findAddEventButton().exists()).toBe(false);
});
it('should not show a form by default', () => {
- expect(findCreateTimelineEvent().isVisible()).toBe(false);
+ expect(findCreateTimelineEvent().exists()).toBe(false);
});
it('should show a form when button is clicked', async () => {
await findAddEventButton().trigger('click');
- expect(findCreateTimelineEvent().isVisible()).toBe(true);
- });
-
- it('should clear the form when button is clicked', async () => {
- const mockClear = jest.fn();
- wrapper.vm.$refs.createEventForm.clearForm = mockClear;
-
- await findAddEventButton().trigger('click');
-
- expect(mockClear).toHaveBeenCalled();
+ expect(findCreateTimelineEvent().exists()).toBe(true);
});
it('should hide the form when the hide event is emitted', async () => {
@@ -167,7 +158,7 @@ describe('TimelineEventsTab', () => {
await findCreateTimelineEvent().vm.$emit('hide-new-timeline-events-form');
- expect(findCreateTimelineEvent().isVisible()).toBe(false);
+ expect(findCreateTimelineEvent().exists()).toBe(false);
});
});
});
diff --git a/spec/frontend/issues/show/components/incidents/utils_spec.js b/spec/frontend/issues/show/components/incidents/utils_spec.js
index d3a86680f14..f0494591e95 100644
--- a/spec/frontend/issues/show/components/incidents/utils_spec.js
+++ b/spec/frontend/issues/show/components/incidents/utils_spec.js
@@ -2,7 +2,7 @@ import timezoneMock from 'timezone-mock';
import {
displayAndLogError,
getEventIcon,
- getUtcShiftedDateNow,
+ getUtcShiftedDate,
} from '~/issues/show/components/incidents/utils';
import { createAlert } from '~/flash';
@@ -34,7 +34,7 @@ describe('incident utils', () => {
});
});
- describe('getUtcShiftedDateNow', () => {
+ describe('getUtcShiftedDate', () => {
beforeEach(() => {
timezoneMock.register('US/Pacific');
});
@@ -46,7 +46,7 @@ describe('incident utils', () => {
it('should shift the date by the timezone offset', () => {
const date = new Date();
- const shiftedDate = getUtcShiftedDateNow();
+ const shiftedDate = getUtcShiftedDate();
expect(shiftedDate > date).toBe(true);
});
diff --git a/spec/frontend/issues/show/components/pinned_links_spec.js b/spec/frontend/issues/show/components/pinned_links_spec.js
index aac720df6e9..208baac7124 100644
--- a/spec/frontend/issues/show/components/pinned_links_spec.js
+++ b/spec/frontend/issues/show/components/pinned_links_spec.js
@@ -9,7 +9,7 @@ const plainStatusUrl = 'https://status.com';
describe('PinnedLinks', () => {
let wrapper;
- const findButtons = () => wrapper.findAll(GlButton);
+ const findButtons = () => wrapper.findAllComponents(GlButton);
const createComponent = (props) => {
wrapper = shallowMount(PinnedLinks, {
diff --git a/spec/frontend/issues/show/components/sentry_error_stack_trace_spec.js b/spec/frontend/issues/show/components/sentry_error_stack_trace_spec.js
index b38d2b60057..d4202f4a6ab 100644
--- a/spec/frontend/issues/show/components/sentry_error_stack_trace_spec.js
+++ b/spec/frontend/issues/show/components/sentry_error_stack_trace_spec.js
@@ -62,8 +62,8 @@ describe('Sentry Error Stack Trace', () => {
describe('loading', () => {
it('should show spinner while loading', () => {
mountComponent();
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
- expect(wrapper.find(Stacktrace).exists()).toBe(false);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(Stacktrace).exists()).toBe(false);
});
});
@@ -74,8 +74,8 @@ describe('Sentry Error Stack Trace', () => {
it('should show stacktrace', () => {
mountComponent({ stubs: {} });
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
- expect(wrapper.find(Stacktrace).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
+ expect(wrapper.findComponent(Stacktrace).exists()).toBe(true);
});
});
});
diff --git a/spec/frontend/jira_connect/branches/components/new_branch_form_spec.js b/spec/frontend/jira_connect/branches/components/new_branch_form_spec.js
index b9fed5f34f1..cc8346253ee 100644
--- a/spec/frontend/jira_connect/branches/components/new_branch_form_spec.js
+++ b/spec/frontend/jira_connect/branches/components/new_branch_form_spec.js
@@ -217,7 +217,7 @@ describe('NewBranchForm', () => {
});
it('emits `success` event', () => {
- expect(wrapper.emitted('success')).toBeTruthy();
+ expect(wrapper.emitted('success')).toHaveLength(1);
});
it('called `createBranch` mutation correctly', () => {
diff --git a/spec/frontend/jira_connect/subscriptions/api_spec.js b/spec/frontend/jira_connect/subscriptions/api_spec.js
index 57b11bdbc27..cf496d5836a 100644
--- a/spec/frontend/jira_connect/subscriptions/api_spec.js
+++ b/spec/frontend/jira_connect/subscriptions/api_spec.js
@@ -1,7 +1,14 @@
import MockAdapter from 'axios-mock-adapter';
-import { addSubscription, removeSubscription, fetchGroups } from '~/jira_connect/subscriptions/api';
+import {
+ axiosInstance,
+ addSubscription,
+ removeSubscription,
+ fetchGroups,
+ getCurrentUser,
+ addJiraConnectSubscription,
+ updateInstallation,
+} from '~/jira_connect/subscriptions/api';
import { getJwt } from '~/jira_connect/subscriptions/utils';
-import axios from '~/lib/utils/axios_utils';
import httpStatus from '~/lib/utils/http_status';
jest.mock('~/jira_connect/subscriptions/utils', () => ({
@@ -9,21 +16,26 @@ jest.mock('~/jira_connect/subscriptions/utils', () => ({
}));
describe('JiraConnect API', () => {
- let mock;
+ let axiosMock;
+ let originalGon;
let response;
const mockAddPath = 'addPath';
const mockRemovePath = 'removePath';
const mockNamespace = 'namespace';
const mockJwt = 'jwt';
+ const mockAccessToken = 'accessToken';
const mockResponse = { success: true };
beforeEach(() => {
- mock = new MockAdapter(axios);
+ axiosMock = new MockAdapter(axiosInstance);
+ originalGon = window.gon;
+ window.gon = { api_version: 'v4' };
});
afterEach(() => {
- mock.restore();
+ axiosMock.restore();
+ window.gon = originalGon;
response = null;
});
@@ -31,8 +43,8 @@ describe('JiraConnect API', () => {
const makeRequest = () => addSubscription(mockAddPath, mockNamespace);
it('returns success response', async () => {
- jest.spyOn(axios, 'post');
- mock
+ jest.spyOn(axiosInstance, 'post');
+ axiosMock
.onPost(mockAddPath, {
jwt: mockJwt,
namespace_path: mockNamespace,
@@ -42,7 +54,7 @@ describe('JiraConnect API', () => {
response = await makeRequest();
expect(getJwt).toHaveBeenCalled();
- expect(axios.post).toHaveBeenCalledWith(mockAddPath, {
+ expect(axiosInstance.post).toHaveBeenCalledWith(mockAddPath, {
jwt: mockJwt,
namespace_path: mockNamespace,
});
@@ -54,13 +66,13 @@ describe('JiraConnect API', () => {
const makeRequest = () => removeSubscription(mockRemovePath);
it('returns success response', async () => {
- jest.spyOn(axios, 'delete');
- mock.onDelete(mockRemovePath).replyOnce(httpStatus.OK, mockResponse);
+ jest.spyOn(axiosInstance, 'delete');
+ axiosMock.onDelete(mockRemovePath).replyOnce(httpStatus.OK, mockResponse);
response = await makeRequest();
expect(getJwt).toHaveBeenCalled();
- expect(axios.delete).toHaveBeenCalledWith(mockRemovePath, {
+ expect(axiosInstance.delete).toHaveBeenCalledWith(mockRemovePath, {
params: {
jwt: mockJwt,
},
@@ -81,8 +93,8 @@ describe('JiraConnect API', () => {
});
it('returns success response', async () => {
- jest.spyOn(axios, 'get');
- mock
+ jest.spyOn(axiosInstance, 'get');
+ axiosMock
.onGet(mockGroupsPath, {
page: mockPage,
per_page: mockPerPage,
@@ -91,7 +103,7 @@ describe('JiraConnect API', () => {
response = await makeRequest();
- expect(axios.get).toHaveBeenCalledWith(mockGroupsPath, {
+ expect(axiosInstance.get).toHaveBeenCalledWith(mockGroupsPath, {
params: {
page: mockPage,
per_page: mockPerPage,
@@ -100,4 +112,82 @@ describe('JiraConnect API', () => {
expect(response.data).toEqual(mockResponse);
});
});
+
+ describe('getCurrentUser', () => {
+ const makeRequest = () => getCurrentUser();
+
+ it('returns success response', async () => {
+ const expectedUrl = '/api/v4/user';
+
+ jest.spyOn(axiosInstance, 'get');
+
+ axiosMock.onGet(expectedUrl).replyOnce(httpStatus.OK, mockResponse);
+
+ response = await makeRequest();
+
+ expect(axiosInstance.get).toHaveBeenCalledWith(expectedUrl, {});
+ expect(response.data).toEqual(mockResponse);
+ });
+ });
+
+ describe('addJiraConnectSubscription', () => {
+ const makeRequest = () =>
+ addJiraConnectSubscription(mockNamespace, { jwt: mockJwt, accessToken: mockAccessToken });
+
+ it('returns success response', async () => {
+ const expectedUrl = '/api/v4/integrations/jira_connect/subscriptions';
+
+ jest.spyOn(axiosInstance, 'post');
+
+ axiosMock.onPost(expectedUrl).replyOnce(httpStatus.OK, mockResponse);
+
+ response = await makeRequest();
+
+ expect(axiosInstance.post).toHaveBeenCalledWith(
+ expectedUrl,
+ {
+ jwt: mockJwt,
+ namespace_path: mockNamespace,
+ },
+ { headers: { Authorization: `Bearer ${mockAccessToken}` } },
+ );
+ expect(response.data).toEqual(mockResponse);
+ });
+ });
+
+ describe('updateInstallation', () => {
+ const expectedUrl = '/-/jira_connect/installations';
+
+ it.each`
+ instanceUrl | expectedInstanceUrl
+ ${'https://gitlab.com'} | ${null}
+ ${'https://gitlab.mycompany.com'} | ${'https://gitlab.mycompany.com'}
+ `(
+ 'when instanceUrl is $instanceUrl, it passes `instance_url` as $expectedInstanceUrl',
+ async ({ instanceUrl, expectedInstanceUrl }) => {
+ const makeRequest = () => updateInstallation(instanceUrl);
+
+ jest.spyOn(axiosInstance, 'put');
+ axiosMock
+ .onPut(expectedUrl, {
+ jwt: mockJwt,
+ installation: {
+ instance_url: expectedInstanceUrl,
+ },
+ })
+ .replyOnce(httpStatus.OK, mockResponse);
+
+ response = await makeRequest();
+
+ expect(getJwt).toHaveBeenCalled();
+ expect(axiosInstance.put).toHaveBeenCalledWith(expectedUrl, {
+ jwt: mockJwt,
+ installation: {
+ instance_url: expectedInstanceUrl,
+ },
+ });
+ expect(response.data).toEqual(mockResponse);
+ },
+ );
+ });
});
diff --git a/spec/frontend/jira_connect/subscriptions/components/add_namespace_modal/groups_list_spec.js b/spec/frontend/jira_connect/subscriptions/components/add_namespace_modal/groups_list_spec.js
index d871b1e1dcc..f1fc5e4d90b 100644
--- a/spec/frontend/jira_connect/subscriptions/components/add_namespace_modal/groups_list_spec.js
+++ b/spec/frontend/jira_connect/subscriptions/components/add_namespace_modal/groups_list_spec.js
@@ -50,7 +50,7 @@ describe('GroupsList', () => {
const findGlAlert = () => wrapper.findComponent(GlAlert);
const findGlLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findAllItems = () => wrapper.findAll(GroupsListItem);
+ const findAllItems = () => wrapper.findAllComponents(GroupsListItem);
const findFirstItem = () => findAllItems().at(0);
const findSecondItem = () => findAllItems().at(1);
const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
diff --git a/spec/frontend/jira_connect/subscriptions/components/app_spec.js b/spec/frontend/jira_connect/subscriptions/components/app_spec.js
index 9894141be5a..369ddda8dbe 100644
--- a/spec/frontend/jira_connect/subscriptions/components/app_spec.js
+++ b/spec/frontend/jira_connect/subscriptions/components/app_spec.js
@@ -31,8 +31,8 @@ describe('JiraConnectApp', () => {
const findUserLink = () => wrapper.findComponent(UserLink);
const findBrowserSupportAlert = () => wrapper.findComponent(BrowserSupportAlert);
- const createComponent = ({ provide, mountFn = shallowMountExtended } = {}) => {
- store = createStore({ subscriptions: [mockSubscription] });
+ const createComponent = ({ provide, mountFn = shallowMountExtended, initialState = {} } = {}) => {
+ store = createStore({ ...initialState, subscriptions: [mockSubscription] });
jest.spyOn(store, 'dispatch').mockImplementation();
wrapper = mountFn(JiraConnectApp, {
@@ -60,7 +60,7 @@ describe('JiraConnectApp', () => {
});
it(`${shouldRenderSignInPage ? 'renders' : 'does not render'} sign in page`, () => {
- expect(findSignInPage().exists()).toBe(shouldRenderSignInPage);
+ expect(findSignInPage().isVisible()).toBe(shouldRenderSignInPage);
if (shouldRenderSignInPage) {
expect(findSignInPage().props('hasSubscriptions')).toBe(true);
}
@@ -133,7 +133,7 @@ describe('JiraConnectApp', () => {
});
it('renders link when `linkUrl` is set', async () => {
- createComponent({ mountFn: mountExtended });
+ createComponent({ provide: { usersPath: '' }, mountFn: mountExtended });
store.commit(SET_ALERT, {
message: __('test message %{linkStart}test link%{linkEnd}'),
@@ -211,21 +211,22 @@ describe('JiraConnectApp', () => {
describe('when `jiraConnectOauth` feature flag is enabled', () => {
const mockSubscriptionsPath = '/mockSubscriptionsPath';
- beforeEach(() => {
+ beforeEach(async () => {
jest.spyOn(api, 'fetchSubscriptions').mockResolvedValue({ data: { subscriptions: [] } });
+ jest.spyOn(AccessorUtilities, 'canUseCrypto').mockReturnValue(true);
createComponent({
+ initialState: {
+ currentUser: { name: 'root' },
+ },
provide: {
glFeatures: { jiraConnectOauth: true },
subscriptionsPath: mockSubscriptionsPath,
},
});
- });
- describe('when component mounts', () => {
- it('dispatches `fetchSubscriptions` action', async () => {
- expect(store.dispatch).toHaveBeenCalledWith('fetchSubscriptions', mockSubscriptionsPath);
- });
+ findSignInPage().vm.$emit('sign-in-oauth');
+ await nextTick();
});
describe('when oauth button emits `sign-in-oauth` event', () => {
diff --git a/spec/frontend/jira_connect/subscriptions/components/sign_in_oauth_button_spec.js b/spec/frontend/jira_connect/subscriptions/components/sign_in_oauth_button_spec.js
index ed0abaaf576..01317eb5dba 100644
--- a/spec/frontend/jira_connect/subscriptions/components/sign_in_oauth_button_spec.js
+++ b/spec/frontend/jira_connect/subscriptions/components/sign_in_oauth_button_spec.js
@@ -1,39 +1,41 @@
import { GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue';
+
import SignInOauthButton from '~/jira_connect/subscriptions/components/sign_in_oauth_button.vue';
import {
I18N_DEFAULT_SIGN_IN_BUTTON_TEXT,
OAUTH_WINDOW_OPTIONS,
} from '~/jira_connect/subscriptions/constants';
-import axios from '~/lib/utils/axios_utils';
import waitForPromises from 'helpers/wait_for_promises';
-import httpStatus from '~/lib/utils/http_status';
import AccessorUtilities from '~/lib/utils/accessor';
-import { getCurrentUser } from '~/rest_api';
+import {
+ getCurrentUser,
+ fetchOAuthApplicationId,
+ fetchOAuthToken,
+} from '~/jira_connect/subscriptions/api';
import createStore from '~/jira_connect/subscriptions/store';
import { SET_ACCESS_TOKEN } from '~/jira_connect/subscriptions/store/mutation_types';
jest.mock('~/lib/utils/accessor');
jest.mock('~/jira_connect/subscriptions/utils');
jest.mock('~/jira_connect/subscriptions/api');
-jest.mock('~/rest_api');
jest.mock('~/jira_connect/subscriptions/pkce', () => ({
createCodeVerifier: jest.fn().mockReturnValue('mock-verifier'),
createCodeChallenge: jest.fn().mockResolvedValue('mock-challenge'),
}));
-const mockOauthMetadata = {
- oauth_authorize_url: 'https://gitlab.com/mockOauth',
- oauth_token_url: 'https://gitlab.com/mockOauthToken',
- state: 'good-state',
-};
-
describe('SignInOauthButton', () => {
let wrapper;
- let mockAxios;
let store;
+ const mockOauthMetadata = {
+ oauth_authorize_url: 'https://gitlab.com/mockOauth',
+ oauth_token_path: 'https://gitlab.com/mockOauthToken',
+ oauth_token_payload: {
+ client_id: '543678901',
+ },
+ state: 'good-state',
+ };
const createComponent = ({ slots, props } = {}) => {
store = createStore();
@@ -50,13 +52,8 @@ describe('SignInOauthButton', () => {
});
};
- beforeEach(() => {
- mockAxios = new MockAdapter(axios);
- });
-
afterEach(() => {
wrapper.destroy();
- mockAxios.restore();
});
const findButton = () => wrapper.findComponent(GlButton);
@@ -69,6 +66,46 @@ describe('SignInOauthButton', () => {
expect(findButton().props('category')).toBe('primary');
});
+ describe('when `gitlabBasePath` is passed', () => {
+ const mockBasePath = 'https://gitlab.mycompany.com';
+
+ it('uses custom text for button', () => {
+ createComponent({
+ props: {
+ gitlabBasePath: mockBasePath,
+ },
+ });
+
+ expect(findButton().text()).toBe(`Sign in to ${mockBasePath}`);
+ });
+
+ describe('on click', () => {
+ const mockClientId = '798412381';
+
+ beforeEach(async () => {
+ fetchOAuthApplicationId.mockReturnValue({ data: { application_id: mockClientId } });
+ jest.spyOn(window, 'open').mockReturnValue();
+ createComponent({
+ props: {
+ gitlabBasePath: mockBasePath,
+ },
+ });
+
+ findButton().vm.$emit('click');
+
+ await nextTick();
+ });
+
+ it('calls `window.open` with correct arguments', () => {
+ expect(window.open).toHaveBeenCalledWith(
+ `${mockBasePath}/mockOauth?code_challenge=mock-challenge&code_challenge_method=S256&client_id=${mockClientId}`,
+ I18N_DEFAULT_SIGN_IN_BUTTON_TEXT,
+ OAUTH_WINDOW_OPTIONS,
+ );
+ });
+ });
+ });
+
it.each`
scenario | cryptoAvailable
${'when crypto API is available'} | ${true}
@@ -96,7 +133,7 @@ describe('SignInOauthButton', () => {
it('calls `window.open` with correct arguments', () => {
expect(window.open).toHaveBeenCalledWith(
- `${mockOauthMetadata.oauth_authorize_url}?code_challenge=mock-challenge&code_challenge_method=S256`,
+ `${mockOauthMetadata.oauth_authorize_url}?code_challenge=mock-challenge&code_challenge_method=S256&client_id=${mockOauthMetadata.oauth_token_payload.client_id}`,
I18N_DEFAULT_SIGN_IN_BUTTON_TEXT,
OAUTH_WINDOW_OPTIONS,
);
@@ -151,11 +188,7 @@ describe('SignInOauthButton', () => {
describe('when API requests succeed', () => {
beforeEach(async () => {
- jest.spyOn(axios, 'post');
- jest.spyOn(axios, 'get');
- mockAxios
- .onPost(mockOauthMetadata.oauth_token_url)
- .replyOnce(httpStatus.OK, { access_token: mockAccessToken });
+ fetchOAuthToken.mockResolvedValue({ data: { access_token: mockAccessToken } });
getCurrentUser.mockResolvedValue({ data: mockUser });
window.dispatchEvent(new MessageEvent('message', mockEvent));
@@ -164,9 +197,10 @@ describe('SignInOauthButton', () => {
});
it('executes POST request to Oauth token endpoint', () => {
- expect(axios.post).toHaveBeenCalledWith(mockOauthMetadata.oauth_token_url, {
+ expect(fetchOAuthToken).toHaveBeenCalledWith(mockOauthMetadata.oauth_token_path, {
code: '1234',
code_verifier: 'mock-verifier',
+ client_id: mockOauthMetadata.oauth_token_payload.client_id,
});
});
@@ -185,10 +219,7 @@ describe('SignInOauthButton', () => {
describe('when API requests fail', () => {
beforeEach(async () => {
- jest.spyOn(axios, 'post');
- mockAxios
- .onPost(mockOauthMetadata.oauth_token_url)
- .replyOnce(httpStatus.INTERNAL_SERVER_ERROR);
+ fetchOAuthToken.mockRejectedValue();
window.dispatchEvent(new MessageEvent('message', mockEvent));
diff --git a/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_com_spec.js b/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_com_spec.js
index 1649920b48b..b9a8451f3b3 100644
--- a/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_com_spec.js
+++ b/spec/frontend/jira_connect/subscriptions/pages/sign_in/sign_in_gitlab_com_spec.js
@@ -101,7 +101,7 @@ describe('SignInGitlabCom', () => {
const button = findSignInOauthButton();
button.vm.$emit('error');
- expect(wrapper.emitted('error')).toBeTruthy();
+ expect(wrapper.emitted('error')).toHaveLength(1);
});
});
});
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 f4be8bf121d..10696d25f17 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
@@ -5,9 +5,22 @@ import SignInGitlabMultiversion from '~/jira_connect/subscriptions/pages/sign_in
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 { updateInstallation } from '~/jira_connect/subscriptions/api';
+import { reloadPage, persistBaseUrl, retrieveBaseUrl } from '~/jira_connect/subscriptions/utils';
+
+jest.mock('~/jira_connect/subscriptions/api', () => {
+ return {
+ updateInstallation: jest.fn(),
+ setApiBaseURL: jest.fn(),
+ };
+});
+jest.mock('~/jira_connect/subscriptions/utils');
+
describe('SignInGitlabMultiversion', () => {
let wrapper;
+ const mockBasePath = 'gitlab.mycompany.com';
+
const findVersionSelectForm = () => wrapper.findComponent(VersionSelectForm);
const findSignInOauthButton = () => wrapper.findComponent(SignInOauthButton);
const findSubtitle = () => wrapper.findByTestId('subtitle');
@@ -29,30 +42,32 @@ describe('SignInGitlabMultiversion', () => {
});
describe('when form emits "submit" event', () => {
- it('hides the version select form and shows the sign in button', async () => {
+ it('updates the backend, then saves the baseUrl and reloads', async () => {
+ updateInstallation.mockResolvedValue({});
+
createComponent();
- findVersionSelectForm().vm.$emit('submit', 'gitlab.mycompany.com');
+ findVersionSelectForm().vm.$emit('submit', mockBasePath);
await nextTick();
- expect(findVersionSelectForm().exists()).toBe(false);
- expect(findSignInOauthButton().exists()).toBe(true);
+ expect(updateInstallation).toHaveBeenCalled();
+ expect(persistBaseUrl).toHaveBeenCalledWith(mockBasePath);
+ expect(reloadPage).toHaveBeenCalled();
});
});
});
});
describe('when version is selected', () => {
- beforeEach(async () => {
+ beforeEach(() => {
+ retrieveBaseUrl.mockReturnValue(mockBasePath);
createComponent();
-
- findVersionSelectForm().vm.$emit('submit', 'gitlab.mycompany.com');
- await nextTick();
});
describe('sign in button', () => {
it('renders sign in button', () => {
expect(findSignInOauthButton().exists()).toBe(true);
+ expect(findSignInOauthButton().props('gitlabBasePath')).toBe(mockBasePath);
});
describe('when button emits `sign-in` event', () => {
@@ -71,7 +86,7 @@ describe('SignInGitlabMultiversion', () => {
const button = findSignInOauthButton();
button.vm.$emit('error');
- expect(wrapper.emitted('error')).toBeTruthy();
+ expect(wrapper.emitted('error')).toHaveLength(1);
});
});
});
diff --git a/spec/frontend/jira_connect/subscriptions/store/actions_spec.js b/spec/frontend/jira_connect/subscriptions/store/actions_spec.js
index 53b5d8e70af..5e3c30269b5 100644
--- a/spec/frontend/jira_connect/subscriptions/store/actions_spec.js
+++ b/spec/frontend/jira_connect/subscriptions/store/actions_spec.js
@@ -8,8 +8,6 @@ import {
} from '~/jira_connect/subscriptions/store/actions';
import state from '~/jira_connect/subscriptions/store/state';
import * as api from '~/jira_connect/subscriptions/api';
-import * as userApi from '~/api/user_api';
-import * as integrationsApi from '~/api/integrations_api';
import {
I18N_DEFAULT_SUBSCRIPTIONS_ERROR_MESSAGE,
I18N_ADD_SUBSCRIPTION_SUCCESS_ALERT_TITLE,
@@ -79,7 +77,7 @@ describe('JiraConnect actions', () => {
describe('when API request succeeds', () => {
it('commits the SET_ACCESS_TOKEN and SET_CURRENT_USER mutations', async () => {
const mockUser = { name: 'root' };
- jest.spyOn(userApi, 'getCurrentUser').mockResolvedValue({ data: mockUser });
+ jest.spyOn(api, 'getCurrentUser').mockResolvedValue({ data: mockUser });
await testAction(
loadCurrentUser,
@@ -89,7 +87,7 @@ describe('JiraConnect actions', () => {
[],
);
- expect(userApi.getCurrentUser).toHaveBeenCalledWith({
+ expect(api.getCurrentUser).toHaveBeenCalledWith({
headers: { Authorization: `Bearer ${mockAccessToken}` },
});
});
@@ -97,7 +95,7 @@ describe('JiraConnect actions', () => {
describe('when API request fails', () => {
it('commits the SET_CURRENT_USER_ERROR mutation', async () => {
- jest.spyOn(userApi, 'getCurrentUser').mockRejectedValue();
+ jest.spyOn(api, 'getCurrentUser').mockRejectedValue();
await testAction(
loadCurrentUser,
@@ -120,9 +118,7 @@ describe('JiraConnect actions', () => {
describe('when API request succeeds', () => {
it('commits the SET_ACCESS_TOKEN and SET_CURRENT_USER mutations', async () => {
- jest
- .spyOn(integrationsApi, 'addJiraConnectSubscription')
- .mockResolvedValue({ success: true });
+ jest.spyOn(api, 'addJiraConnectSubscription').mockResolvedValue({ success: true });
await testAction(
addSubscription,
@@ -144,7 +140,7 @@ describe('JiraConnect actions', () => {
[{ type: 'fetchSubscriptions', payload: mockSubscriptionsPath }],
);
- expect(integrationsApi.addJiraConnectSubscription).toHaveBeenCalledWith(mockNamespace, {
+ expect(api.addJiraConnectSubscription).toHaveBeenCalledWith(mockNamespace, {
accessToken: null,
jwt: '1234',
});
@@ -153,7 +149,7 @@ describe('JiraConnect actions', () => {
describe('when API request fails', () => {
it('commits the SET_CURRENT_USER_ERROR mutation', async () => {
- jest.spyOn(integrationsApi, 'addJiraConnectSubscription').mockRejectedValue();
+ jest.spyOn(api, 'addJiraConnectSubscription').mockRejectedValue();
await testAction(
addSubscription,
diff --git a/spec/frontend/jira_import/components/jira_import_app_spec.js b/spec/frontend/jira_import/components/jira_import_app_spec.js
index cd8024d4962..022a0f81aaa 100644
--- a/spec/frontend/jira_import/components/jira_import_app_spec.js
+++ b/spec/frontend/jira_import/components/jira_import_app_spec.js
@@ -21,15 +21,15 @@ describe('JiraImportApp', () => {
const setupIllustration = 'setup-illustration.svg';
- const getFormComponent = () => wrapper.find(JiraImportForm);
+ const getFormComponent = () => wrapper.findComponent(JiraImportForm);
- const getProgressComponent = () => wrapper.find(JiraImportProgress);
+ const getProgressComponent = () => wrapper.findComponent(JiraImportProgress);
- const getSetupComponent = () => wrapper.find(JiraImportSetup);
+ const getSetupComponent = () => wrapper.findComponent(JiraImportSetup);
- const getAlert = () => wrapper.find(GlAlert);
+ const getAlert = () => wrapper.findComponent(GlAlert);
- const getLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const getLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const mountComponent = ({
isJiraConfigured = true,
diff --git a/spec/frontend/jira_import/components/jira_import_form_spec.js b/spec/frontend/jira_import/components/jira_import_form_spec.js
index 41d3cd46d01..d43a9f8a145 100644
--- a/spec/frontend/jira_import/components/jira_import_form_spec.js
+++ b/spec/frontend/jira_import/components/jira_import_form_spec.js
@@ -164,8 +164,9 @@ describe('JiraImportForm', () => {
it('shows a heading for the user mapping section', () => {
expect(
- getByRole(wrapper.element, 'heading', { name: 'Jira-GitLab user mapping template' }),
- ).toBeTruthy();
+ getByRole(wrapper.element, 'heading', { name: 'Jira-GitLab user mapping template' })
+ .innerText,
+ ).toBe('Jira-GitLab user mapping template');
});
it('shows information to the user', () => {
@@ -182,15 +183,15 @@ describe('JiraImportForm', () => {
});
it('has a "Jira display name" column', () => {
- expect(getHeader('Jira display name')).toBeTruthy();
+ expect(getHeader('Jira display name').innerText).toBe('Jira display name');
});
it('has an "arrow" column', () => {
- expect(getHeader('Arrow')).toBeTruthy();
+ expect(getHeader('Arrow').getAttribute('aria-label')).toBe('Arrow');
});
it('has a "GitLab username" column', () => {
- expect(getHeader('GitLab username')).toBeTruthy();
+ expect(getHeader('GitLab username').innerText).toBe('GitLab username');
});
});
@@ -288,8 +289,8 @@ describe('JiraImportForm', () => {
});
it('updates the user list', () => {
- expect(getUserDropdown().findAll(GlDropdownItem)).toHaveLength(1);
- expect(getUserDropdown().find(GlDropdownItem).text()).toContain(
+ expect(getUserDropdown().findAllComponents(GlDropdownItem)).toHaveLength(1);
+ expect(getUserDropdown().findComponent(GlDropdownItem).text()).toContain(
'fchopin (Frederic Chopin)',
);
});
diff --git a/spec/frontend/jira_import/components/jira_import_progress_spec.js b/spec/frontend/jira_import/components/jira_import_progress_spec.js
index 04b2a2da622..42356763492 100644
--- a/spec/frontend/jira_import/components/jira_import_progress_spec.js
+++ b/spec/frontend/jira_import/components/jira_import_progress_spec.js
@@ -8,7 +8,7 @@ describe('JiraImportProgress', () => {
const importProject = 'JIRAPROJECT';
- const getGlEmptyStateProp = (attribute) => wrapper.find(GlEmptyState).props(attribute);
+ const getGlEmptyStateProp = (attribute) => wrapper.findComponent(GlEmptyState).props(attribute);
const getParagraphText = () => wrapper.find('p').text();
diff --git a/spec/frontend/jira_import/components/jira_import_setup_spec.js b/spec/frontend/jira_import/components/jira_import_setup_spec.js
index 320e270b493..0085a2b5572 100644
--- a/spec/frontend/jira_import/components/jira_import_setup_spec.js
+++ b/spec/frontend/jira_import/components/jira_import_setup_spec.js
@@ -6,7 +6,7 @@ import { illustration, jiraIntegrationPath } from '../mock_data';
describe('JiraImportSetup', () => {
let wrapper;
- const getGlEmptyStateProp = (attribute) => wrapper.find(GlEmptyState).props(attribute);
+ const getGlEmptyStateProp = (attribute) => wrapper.findComponent(GlEmptyState).props(attribute);
beforeEach(() => {
wrapper = shallowMount(JiraImportSetup, {
diff --git a/spec/frontend/jobs/components/artifacts_block_spec.js b/spec/frontend/jobs/components/artifacts_block_spec.js
deleted file mode 100644
index 0c7c0a6c311..00000000000
--- a/spec/frontend/jobs/components/artifacts_block_spec.js
+++ /dev/null
@@ -1,176 +0,0 @@
-import { mount } from '@vue/test-utils';
-import { trimText } from 'helpers/text_helper';
-import ArtifactsBlock from '~/jobs/components/artifacts_block.vue';
-import { getTimeago } from '~/lib/utils/datetime_utility';
-
-describe('Artifacts block', () => {
- let wrapper;
-
- const createWrapper = (propsData) =>
- mount(ArtifactsBlock, {
- propsData: {
- helpUrl: 'help-url',
- ...propsData,
- },
- });
-
- const findArtifactRemoveElt = () => wrapper.find('[data-testid="artifacts-remove-timeline"]');
- const findJobLockedElt = () => wrapper.find('[data-testid="job-locked-message"]');
- const findKeepBtn = () => wrapper.find('[data-testid="keep-artifacts"]');
- const findDownloadBtn = () => wrapper.find('[data-testid="download-artifacts"]');
- const findBrowseBtn = () => wrapper.find('[data-testid="browse-artifacts"]');
-
- const expireAt = '2018-08-14T09:38:49.157Z';
- const timeago = getTimeago();
- const formattedDate = timeago.format(expireAt);
- const lockedText =
- 'These artifacts are the latest. They will not be deleted (even if expired) until newer artifacts are available.';
-
- const expiredArtifact = {
- expire_at: expireAt,
- expired: true,
- locked: false,
- };
-
- const nonExpiredArtifact = {
- download_path: '/gitlab-org/gitlab-foss/-/jobs/98314558/artifacts/download',
- browse_path: '/gitlab-org/gitlab-foss/-/jobs/98314558/artifacts/browse',
- keep_path: '/gitlab-org/gitlab-foss/-/jobs/98314558/artifacts/keep',
- expire_at: expireAt,
- expired: false,
- locked: false,
- };
-
- const lockedExpiredArtifact = {
- ...expiredArtifact,
- download_path: '/gitlab-org/gitlab-foss/-/jobs/98314558/artifacts/download',
- browse_path: '/gitlab-org/gitlab-foss/-/jobs/98314558/artifacts/browse',
- expired: true,
- locked: true,
- };
-
- const lockedNonExpiredArtifact = {
- ...nonExpiredArtifact,
- keep_path: undefined,
- locked: true,
- };
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- describe('with expired artifacts that are not locked', () => {
- beforeEach(() => {
- wrapper = createWrapper({
- artifact: expiredArtifact,
- });
- });
-
- it('renders expired artifact date and info', () => {
- expect(trimText(findArtifactRemoveElt().text())).toBe(
- `The artifacts were removed ${formattedDate}`,
- );
-
- expect(
- findArtifactRemoveElt()
- .find('[data-testid="artifact-expired-help-link"]')
- .attributes('href'),
- ).toBe('help-url');
- });
-
- it('does not show the keep button', () => {
- expect(findKeepBtn().exists()).toBe(false);
- });
-
- it('does not show the download button', () => {
- expect(findDownloadBtn().exists()).toBe(false);
- });
-
- it('does not show the browse button', () => {
- expect(findBrowseBtn().exists()).toBe(false);
- });
- });
-
- describe('with artifacts that will expire', () => {
- beforeEach(() => {
- wrapper = createWrapper({
- artifact: nonExpiredArtifact,
- });
- });
-
- it('renders will expire artifact date and info', () => {
- expect(trimText(findArtifactRemoveElt().text())).toBe(
- `The artifacts will be removed ${formattedDate}`,
- );
-
- expect(
- findArtifactRemoveElt()
- .find('[data-testid="artifact-expired-help-link"]')
- .attributes('href'),
- ).toBe('help-url');
- });
-
- it('renders the keep button', () => {
- expect(findKeepBtn().exists()).toBe(true);
- });
-
- it('renders the download button', () => {
- expect(findDownloadBtn().exists()).toBe(true);
- });
-
- it('renders the browse button', () => {
- expect(findBrowseBtn().exists()).toBe(true);
- });
- });
-
- describe('with expired locked artifacts', () => {
- beforeEach(() => {
- wrapper = createWrapper({
- artifact: lockedExpiredArtifact,
- });
- });
-
- it('renders the information that the artefacts are locked', () => {
- expect(findArtifactRemoveElt().exists()).toBe(false);
- expect(trimText(findJobLockedElt().text())).toBe(lockedText);
- });
-
- it('does not render the keep button', () => {
- expect(findKeepBtn().exists()).toBe(false);
- });
-
- it('renders the download button', () => {
- expect(findDownloadBtn().exists()).toBe(true);
- });
-
- it('renders the browse button', () => {
- expect(findBrowseBtn().exists()).toBe(true);
- });
- });
-
- describe('with non expired locked artifacts', () => {
- beforeEach(() => {
- wrapper = createWrapper({
- artifact: lockedNonExpiredArtifact,
- });
- });
-
- it('renders the information that the artefacts are locked', () => {
- expect(findArtifactRemoveElt().exists()).toBe(false);
- expect(trimText(findJobLockedElt().text())).toBe(lockedText);
- });
-
- it('does not render the keep button', () => {
- expect(findKeepBtn().exists()).toBe(false);
- });
-
- it('renders the download button', () => {
- expect(findDownloadBtn().exists()).toBe(true);
- });
-
- it('renders the browse button', () => {
- expect(findBrowseBtn().exists()).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/jobs/components/commit_block_spec.js b/spec/frontend/jobs/components/commit_block_spec.js
deleted file mode 100644
index 8a6d48cecb8..00000000000
--- a/spec/frontend/jobs/components/commit_block_spec.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import CommitBlock from '~/jobs/components/commit_block.vue';
-import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
-
-describe('Commit block', () => {
- let wrapper;
-
- const commit = {
- short_id: '1f0fb84f',
- id: '1f0fb84fb6770d74d97eee58118fd3909cd4f48c',
- commit_path: 'commit/1f0fb84fb6770d74d97eee58118fd3909cd4f48c',
- title: 'Update README.md',
- };
-
- const mergeRequest = {
- iid: '!21244',
- path: 'merge_requests/21244',
- };
-
- const findCommitSha = () => wrapper.findByTestId('commit-sha');
- const findLinkSha = () => wrapper.findByTestId('link-commit');
-
- const mountComponent = (props) => {
- wrapper = extendedWrapper(
- shallowMount(CommitBlock, {
- propsData: {
- commit,
- ...props,
- },
- }),
- );
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('without merge request', () => {
- beforeEach(() => {
- mountComponent();
- });
-
- it('renders pipeline short sha link', () => {
- expect(findCommitSha().attributes('href')).toBe(commit.commit_path);
- expect(findCommitSha().text()).toBe(commit.short_id);
- });
-
- it('renders clipboard button', () => {
- expect(wrapper.findComponent(ClipboardButton).attributes('text')).toBe(commit.id);
- });
-
- it('renders git commit title', () => {
- expect(wrapper.text()).toContain(commit.title);
- });
-
- it('does not render merge request', () => {
- expect(findLinkSha().exists()).toBe(false);
- });
- });
-
- describe('with merge request', () => {
- it('renders merge request link and reference', () => {
- mountComponent({ mergeRequest });
-
- expect(findLinkSha().attributes('href')).toBe(mergeRequest.path);
- expect(findLinkSha().text()).toBe(`!${mergeRequest.iid}`);
- });
- });
-});
diff --git a/spec/frontend/jobs/components/empty_state_spec.js b/spec/frontend/jobs/components/empty_state_spec.js
deleted file mode 100644
index 9738fd14275..00000000000
--- a/spec/frontend/jobs/components/empty_state_spec.js
+++ /dev/null
@@ -1,140 +0,0 @@
-import { mount } from '@vue/test-utils';
-import EmptyState from '~/jobs/components/empty_state.vue';
-
-describe('Empty State', () => {
- let wrapper;
-
- const defaultProps = {
- illustrationPath: 'illustrations/pending_job_empty.svg',
- illustrationSizeClass: 'svg-430',
- title: 'This job has not started yet',
- playable: false,
- };
-
- const createWrapper = (props) => {
- wrapper = mount(EmptyState, {
- propsData: {
- ...defaultProps,
- ...props,
- },
- });
- };
-
- 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"]');
-
- afterEach(() => {
- if (wrapper?.destroy) {
- wrapper.destroy();
- wrapper = null;
- }
- });
-
- describe('renders image and title', () => {
- beforeEach(() => {
- createWrapper();
- });
-
- it('renders empty state image', () => {
- expect(findEmptyStateImage().exists()).toBe(true);
- });
-
- it('renders provided title', () => {
- expect(findTitle().text().trim()).toBe(defaultProps.title);
- });
- });
-
- describe('with content', () => {
- beforeEach(() => {
- createWrapper({ content });
- });
-
- it('renders content', () => {
- expect(findContent().text().trim()).toBe(content);
- });
- });
-
- describe('without content', () => {
- beforeEach(() => {
- createWrapper();
- });
-
- it('does not render content', () => {
- expect(findContent().exists()).toBe(false);
- });
- });
-
- describe('with action', () => {
- beforeEach(() => {
- createWrapper({
- action: {
- path: 'runner',
- button_title: 'Check runner',
- method: 'post',
- },
- });
- });
-
- it('renders action', () => {
- expect(findAction().attributes('href')).toBe('runner');
- });
- });
-
- describe('without action', () => {
- beforeEach(() => {
- createWrapper({
- action: null,
- });
- });
-
- it('does not render action', () => {
- expect(findAction().exists()).toBe(false);
- });
-
- it('does not render manual variables form', () => {
- expect(findManualVarsForm().exists()).toBe(false);
- });
- });
-
- describe('with playable action and not scheduled job', () => {
- beforeEach(() => {
- createWrapper({
- content,
- playable: true,
- scheduled: false,
- action: {
- path: 'runner',
- button_title: 'Check runner',
- method: 'post',
- },
- });
- });
-
- it('renders manual variables form', () => {
- expect(findManualVarsForm().exists()).toBe(true);
- });
-
- it('does not render the empty state action', () => {
- expect(findAction().exists()).toBe(false);
- });
- });
-
- describe('with playable action and scheduled job', () => {
- beforeEach(() => {
- createWrapper({
- playable: true,
- scheduled: true,
- content,
- });
- });
-
- it('does not render manual variables form', () => {
- expect(findManualVarsForm().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/jobs/components/environments_block_spec.js b/spec/frontend/jobs/components/environments_block_spec.js
deleted file mode 100644
index d90c9137a8f..00000000000
--- a/spec/frontend/jobs/components/environments_block_spec.js
+++ /dev/null
@@ -1,265 +0,0 @@
-import { mount } from '@vue/test-utils';
-import EnvironmentsBlock from '~/jobs/components/environments_block.vue';
-
-const TEST_CLUSTER_NAME = 'test_cluster';
-const TEST_CLUSTER_PATH = 'path/to/test_cluster';
-const TEST_KUBERNETES_NAMESPACE = 'this-is-a-kubernetes-namespace';
-
-describe('Environments block', () => {
- let wrapper;
-
- const status = {
- group: 'success',
- icon: 'status_success',
- label: 'passed',
- text: 'passed',
- tooltip: 'passed',
- };
-
- const environment = {
- environment_path: '/environment',
- name: 'environment',
- };
-
- const lastDeployment = { iid: 'deployment', deployable: { build_path: 'bar' } };
-
- const createEnvironmentWithLastDeployment = () => ({
- ...environment,
- last_deployment: { ...lastDeployment },
- });
-
- const createDeploymentWithCluster = () => ({ name: TEST_CLUSTER_NAME, path: TEST_CLUSTER_PATH });
-
- const createDeploymentWithClusterAndKubernetesNamespace = () => ({
- name: TEST_CLUSTER_NAME,
- path: TEST_CLUSTER_PATH,
- kubernetes_namespace: TEST_KUBERNETES_NAMESPACE,
- });
-
- const createComponent = (deploymentStatus = {}, deploymentCluster = {}) => {
- wrapper = mount(EnvironmentsBlock, {
- propsData: {
- deploymentStatus,
- deploymentCluster,
- iconStatus: status,
- },
- });
- };
-
- const findText = () => wrapper.find(EnvironmentsBlock).text();
- const findJobDeploymentLink = () => wrapper.find('[data-testid="job-deployment-link"]');
- const findEnvironmentLink = () => wrapper.find('[data-testid="job-environment-link"]');
- const findClusterLink = () => wrapper.find('[data-testid="job-cluster-link"]');
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- describe('with last deployment', () => {
- it('renders info for most recent deployment', () => {
- createComponent({
- status: 'last',
- environment,
- });
-
- expect(findText()).toBe('This job is deployed to environment.');
- });
-
- describe('when there is a cluster', () => {
- it('renders info with cluster', () => {
- createComponent(
- {
- status: 'last',
- environment: createEnvironmentWithLastDeployment(),
- },
- createDeploymentWithCluster(),
- );
-
- expect(findText()).toBe(
- `This job is deployed to environment using cluster ${TEST_CLUSTER_NAME}.`,
- );
- });
-
- describe('when there is a kubernetes namespace', () => {
- it('renders info with cluster', () => {
- createComponent(
- {
- status: 'last',
- environment: createEnvironmentWithLastDeployment(),
- },
- createDeploymentWithClusterAndKubernetesNamespace(),
- );
-
- expect(findText()).toBe(
- `This job is deployed to environment using cluster ${TEST_CLUSTER_NAME} and namespace ${TEST_KUBERNETES_NAMESPACE}.`,
- );
- });
- });
- });
- });
-
- describe('with out of date deployment', () => {
- describe('with last deployment', () => {
- it('renders info for out date and most recent', () => {
- createComponent({
- status: 'out_of_date',
- environment: createEnvironmentWithLastDeployment(),
- });
-
- expect(findText()).toBe(
- 'This job is an out-of-date deployment to environment. View the most recent deployment.',
- );
-
- expect(findJobDeploymentLink().attributes('href')).toBe('bar');
- });
-
- describe('when there is a cluster', () => {
- it('renders info with cluster', () => {
- createComponent(
- {
- status: 'out_of_date',
- environment: createEnvironmentWithLastDeployment(),
- },
- createDeploymentWithCluster(),
- );
-
- expect(findText()).toBe(
- `This job is an out-of-date deployment to environment using cluster ${TEST_CLUSTER_NAME}. View the most recent deployment.`,
- );
- });
-
- describe('when there is a kubernetes namespace', () => {
- it('renders info with cluster', () => {
- createComponent(
- {
- status: 'out_of_date',
- environment: createEnvironmentWithLastDeployment(),
- },
- createDeploymentWithClusterAndKubernetesNamespace(),
- );
-
- expect(findText()).toBe(
- `This job is an out-of-date deployment to environment using cluster ${TEST_CLUSTER_NAME} and namespace ${TEST_KUBERNETES_NAMESPACE}. View the most recent deployment.`,
- );
- });
- });
- });
- });
-
- describe('without last deployment', () => {
- it('renders info about out of date deployment', () => {
- createComponent({
- status: 'out_of_date',
- environment,
- });
-
- expect(findText()).toBe('This job is an out-of-date deployment to environment.');
- });
- });
- });
-
- describe('with failed deployment', () => {
- it('renders info about failed deployment', () => {
- createComponent({
- status: 'failed',
- environment,
- });
-
- expect(findText()).toBe('The deployment of this job to environment did not succeed.');
- });
- });
-
- describe('creating deployment', () => {
- describe('with last deployment', () => {
- it('renders info about creating deployment and overriding latest deployment', () => {
- createComponent({
- status: 'creating',
- environment: createEnvironmentWithLastDeployment(),
- });
-
- expect(findText()).toBe(
- 'This job is creating a deployment to environment. This will overwrite the latest deployment.',
- );
-
- expect(findEnvironmentLink().attributes('href')).toBe(environment.environment_path);
-
- expect(findJobDeploymentLink().attributes('href')).toBe('bar');
-
- expect(findClusterLink().exists()).toBe(false);
- });
- });
-
- describe('without last deployment', () => {
- it('renders info about deployment being created', () => {
- createComponent({
- status: 'creating',
- environment,
- });
-
- expect(findText()).toBe('This job is creating a deployment to environment.');
- });
-
- describe('when there is a cluster', () => {
- it('inclues information about the cluster', () => {
- createComponent(
- {
- status: 'creating',
- environment,
- },
- createDeploymentWithCluster(),
- );
-
- expect(findText()).toBe(
- `This job is creating a deployment to environment using cluster ${TEST_CLUSTER_NAME}.`,
- );
- });
- });
- });
-
- describe('without environment', () => {
- it('does not render environment link', () => {
- createComponent({
- status: 'creating',
- environment: null,
- });
-
- expect(findEnvironmentLink().exists()).toBe(false);
- });
- });
- });
-
- describe('with a cluster', () => {
- it('renders the cluster link', () => {
- createComponent(
- {
- status: 'last',
- environment: createEnvironmentWithLastDeployment(),
- },
- createDeploymentWithCluster(),
- );
-
- expect(findText()).toBe(
- `This job is deployed to environment using cluster ${TEST_CLUSTER_NAME}.`,
- );
-
- expect(findClusterLink().attributes('href')).toBe(TEST_CLUSTER_PATH);
- });
-
- describe('when the cluster is missing the path', () => {
- it('renders the name without a link', () => {
- createComponent(
- {
- status: 'last',
- environment: createEnvironmentWithLastDeployment(),
- },
- { name: 'the-cluster' },
- );
-
- expect(findText()).toContain('using cluster the-cluster.');
-
- expect(findClusterLink().exists()).toBe(false);
- });
- });
- });
-});
diff --git a/spec/frontend/jobs/components/erased_block_spec.js b/spec/frontend/jobs/components/erased_block_spec.js
deleted file mode 100644
index 057df20ccc2..00000000000
--- a/spec/frontend/jobs/components/erased_block_spec.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import { GlLink } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import ErasedBlock from '~/jobs/components/erased_block.vue';
-import { getTimeago } from '~/lib/utils/datetime_utility';
-
-describe('Erased block', () => {
- let wrapper;
-
- const erasedAt = '2016-11-07T11:11:16.525Z';
- const timeago = getTimeago();
- const formattedDate = timeago.format(erasedAt);
-
- const findLink = () => wrapper.find(GlLink);
-
- const createComponent = (props) => {
- wrapper = mount(ErasedBlock, {
- propsData: props,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('with job erased by user', () => {
- beforeEach(() => {
- createComponent({
- user: {
- username: 'root',
- web_url: 'gitlab.com/root',
- },
- erasedAt,
- });
- });
-
- it('renders username and link', () => {
- expect(findLink().attributes('href')).toEqual('gitlab.com/root');
-
- expect(wrapper.text().trim()).toContain('Job has been erased by');
- expect(wrapper.text().trim()).toContain('root');
- });
-
- it('renders erasedAt', () => {
- expect(wrapper.text().trim()).toContain(formattedDate);
- });
- });
-
- describe('with erased job', () => {
- beforeEach(() => {
- createComponent({
- erasedAt,
- });
- });
-
- it('renders username and link', () => {
- expect(wrapper.text().trim()).toContain('Job has been erased');
- });
-
- it('renders erasedAt', () => {
- expect(wrapper.text().trim()).toContain(formattedDate);
- });
- });
-});
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 322cfa3ba1f..98bdfc3fcbc 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
@@ -15,23 +15,27 @@ describe('Jobs filtered search', () => {
const findStatusToken = () => getSearchToken('status');
- const createComponent = () => {
- wrapper = shallowMount(JobsFilteredSearch);
+ const createComponent = (props) => {
+ wrapper = shallowMount(JobsFilteredSearch, {
+ propsData: {
+ ...props,
+ },
+ });
};
- beforeEach(() => {
- createComponent();
- });
-
afterEach(() => {
wrapper.destroy();
});
it('displays filtered search', () => {
+ createComponent();
+
expect(findFilteredSearch().exists()).toBe(true);
});
it('displays status token', () => {
+ createComponent();
+
expect(findStatusToken()).toMatchObject({
type: 'status',
icon: 'status',
@@ -42,8 +46,26 @@ describe('Jobs filtered search', () => {
});
it('emits filter token to parent component', () => {
+ createComponent();
+
findFilteredSearch().vm.$emit('submit', mockFailedSearchToken);
expect(wrapper.emitted('filterJobsBySearch')).toEqual([[mockFailedSearchToken]]);
});
+
+ it('filtered search value is empty array when no query string is passed', () => {
+ createComponent();
+
+ expect(findFilteredSearch().props('value')).toEqual([]);
+ });
+
+ it('filtered search returns correct data shape when passed query string', () => {
+ const value = 'SUCCESS';
+
+ createComponent({ queryString: { statuses: value } });
+
+ expect(findFilteredSearch().props('value')).toEqual([
+ { type: 'status', value: { data: value, operator: '=' } },
+ ]);
+ });
});
diff --git a/spec/frontend/jobs/components/filtered_search/utils_spec.js b/spec/frontend/jobs/components/filtered_search/utils_spec.js
new file mode 100644
index 00000000000..8440ab42b86
--- /dev/null
+++ b/spec/frontend/jobs/components/filtered_search/utils_spec.js
@@ -0,0 +1,19 @@
+import { validateQueryString } from '~/jobs/components/filtered_search/utils';
+
+describe('Filtered search utils', () => {
+ describe('validateQueryString', () => {
+ it.each`
+ queryStringObject | expected
+ ${{ statuses: 'SUCCESS' }} | ${{ statuses: 'SUCCESS' }}
+ ${{ statuses: 'failed' }} | ${{ statuses: 'FAILED' }}
+ ${{ wrong: 'SUCCESS' }} | ${null}
+ ${{ statuses: 'wrong' }} | ${null}
+ ${{ wrong: 'wrong' }} | ${null}
+ `(
+ 'when provided $queryStringObject, the expected result is $expected',
+ ({ queryStringObject, expected }) => {
+ expect(validateQueryString(queryStringObject)).toEqual(expected);
+ },
+ );
+ });
+});
diff --git a/spec/frontend/jobs/components/job/artifacts_block_spec.js b/spec/frontend/jobs/components/job/artifacts_block_spec.js
new file mode 100644
index 00000000000..c75deb64d84
--- /dev/null
+++ b/spec/frontend/jobs/components/job/artifacts_block_spec.js
@@ -0,0 +1,176 @@
+import { mount } from '@vue/test-utils';
+import { trimText } from 'helpers/text_helper';
+import ArtifactsBlock from '~/jobs/components/job/sidebar/artifacts_block.vue';
+import { getTimeago } from '~/lib/utils/datetime_utility';
+
+describe('Artifacts block', () => {
+ let wrapper;
+
+ const createWrapper = (propsData) =>
+ mount(ArtifactsBlock, {
+ propsData: {
+ helpUrl: 'help-url',
+ ...propsData,
+ },
+ });
+
+ const findArtifactRemoveElt = () => wrapper.find('[data-testid="artifacts-remove-timeline"]');
+ const findJobLockedElt = () => wrapper.find('[data-testid="job-locked-message"]');
+ const findKeepBtn = () => wrapper.find('[data-testid="keep-artifacts"]');
+ const findDownloadBtn = () => wrapper.find('[data-testid="download-artifacts"]');
+ const findBrowseBtn = () => wrapper.find('[data-testid="browse-artifacts"]');
+
+ const expireAt = '2018-08-14T09:38:49.157Z';
+ const timeago = getTimeago();
+ const formattedDate = timeago.format(expireAt);
+ const lockedText =
+ 'These artifacts are the latest. They will not be deleted (even if expired) until newer artifacts are available.';
+
+ const expiredArtifact = {
+ expire_at: expireAt,
+ expired: true,
+ locked: false,
+ };
+
+ const nonExpiredArtifact = {
+ download_path: '/gitlab-org/gitlab-foss/-/jobs/98314558/artifacts/download',
+ browse_path: '/gitlab-org/gitlab-foss/-/jobs/98314558/artifacts/browse',
+ keep_path: '/gitlab-org/gitlab-foss/-/jobs/98314558/artifacts/keep',
+ expire_at: expireAt,
+ expired: false,
+ locked: false,
+ };
+
+ const lockedExpiredArtifact = {
+ ...expiredArtifact,
+ download_path: '/gitlab-org/gitlab-foss/-/jobs/98314558/artifacts/download',
+ browse_path: '/gitlab-org/gitlab-foss/-/jobs/98314558/artifacts/browse',
+ expired: true,
+ locked: true,
+ };
+
+ const lockedNonExpiredArtifact = {
+ ...nonExpiredArtifact,
+ keep_path: undefined,
+ locked: true,
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('with expired artifacts that are not locked', () => {
+ beforeEach(() => {
+ wrapper = createWrapper({
+ artifact: expiredArtifact,
+ });
+ });
+
+ it('renders expired artifact date and info', () => {
+ expect(trimText(findArtifactRemoveElt().text())).toBe(
+ `The artifacts were removed ${formattedDate}`,
+ );
+
+ expect(
+ findArtifactRemoveElt()
+ .find('[data-testid="artifact-expired-help-link"]')
+ .attributes('href'),
+ ).toBe('help-url');
+ });
+
+ it('does not show the keep button', () => {
+ expect(findKeepBtn().exists()).toBe(false);
+ });
+
+ it('does not show the download button', () => {
+ expect(findDownloadBtn().exists()).toBe(false);
+ });
+
+ it('does not show the browse button', () => {
+ expect(findBrowseBtn().exists()).toBe(false);
+ });
+ });
+
+ describe('with artifacts that will expire', () => {
+ beforeEach(() => {
+ wrapper = createWrapper({
+ artifact: nonExpiredArtifact,
+ });
+ });
+
+ it('renders will expire artifact date and info', () => {
+ expect(trimText(findArtifactRemoveElt().text())).toBe(
+ `The artifacts will be removed ${formattedDate}`,
+ );
+
+ expect(
+ findArtifactRemoveElt()
+ .find('[data-testid="artifact-expired-help-link"]')
+ .attributes('href'),
+ ).toBe('help-url');
+ });
+
+ it('renders the keep button', () => {
+ expect(findKeepBtn().exists()).toBe(true);
+ });
+
+ it('renders the download button', () => {
+ expect(findDownloadBtn().exists()).toBe(true);
+ });
+
+ it('renders the browse button', () => {
+ expect(findBrowseBtn().exists()).toBe(true);
+ });
+ });
+
+ describe('with expired locked artifacts', () => {
+ beforeEach(() => {
+ wrapper = createWrapper({
+ artifact: lockedExpiredArtifact,
+ });
+ });
+
+ it('renders the information that the artefacts are locked', () => {
+ expect(findArtifactRemoveElt().exists()).toBe(false);
+ expect(trimText(findJobLockedElt().text())).toBe(lockedText);
+ });
+
+ it('does not render the keep button', () => {
+ expect(findKeepBtn().exists()).toBe(false);
+ });
+
+ it('renders the download button', () => {
+ expect(findDownloadBtn().exists()).toBe(true);
+ });
+
+ it('renders the browse button', () => {
+ expect(findBrowseBtn().exists()).toBe(true);
+ });
+ });
+
+ describe('with non expired locked artifacts', () => {
+ beforeEach(() => {
+ wrapper = createWrapper({
+ artifact: lockedNonExpiredArtifact,
+ });
+ });
+
+ it('renders the information that the artefacts are locked', () => {
+ expect(findArtifactRemoveElt().exists()).toBe(false);
+ expect(trimText(findJobLockedElt().text())).toBe(lockedText);
+ });
+
+ it('does not render the keep button', () => {
+ expect(findKeepBtn().exists()).toBe(false);
+ });
+
+ it('renders the download button', () => {
+ expect(findDownloadBtn().exists()).toBe(true);
+ });
+
+ it('renders the browse button', () => {
+ expect(findBrowseBtn().exists()).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/jobs/components/job/commit_block_spec.js b/spec/frontend/jobs/components/job/commit_block_spec.js
new file mode 100644
index 00000000000..4fcc754c82c
--- /dev/null
+++ b/spec/frontend/jobs/components/job/commit_block_spec.js
@@ -0,0 +1,70 @@
+import { shallowMount } from '@vue/test-utils';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import CommitBlock from '~/jobs/components/job/sidebar/commit_block.vue';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+
+describe('Commit block', () => {
+ let wrapper;
+
+ const commit = {
+ short_id: '1f0fb84f',
+ id: '1f0fb84fb6770d74d97eee58118fd3909cd4f48c',
+ commit_path: 'commit/1f0fb84fb6770d74d97eee58118fd3909cd4f48c',
+ title: 'Update README.md',
+ };
+
+ const mergeRequest = {
+ iid: '!21244',
+ path: 'merge_requests/21244',
+ };
+
+ const findCommitSha = () => wrapper.findByTestId('commit-sha');
+ const findLinkSha = () => wrapper.findByTestId('link-commit');
+
+ const mountComponent = (props) => {
+ wrapper = extendedWrapper(
+ shallowMount(CommitBlock, {
+ propsData: {
+ commit,
+ ...props,
+ },
+ }),
+ );
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('without merge request', () => {
+ beforeEach(() => {
+ mountComponent();
+ });
+
+ it('renders pipeline short sha link', () => {
+ expect(findCommitSha().attributes('href')).toBe(commit.commit_path);
+ expect(findCommitSha().text()).toBe(commit.short_id);
+ });
+
+ it('renders clipboard button', () => {
+ expect(wrapper.findComponent(ClipboardButton).attributes('text')).toBe(commit.id);
+ });
+
+ it('renders git commit title', () => {
+ expect(wrapper.text()).toContain(commit.title);
+ });
+
+ it('does not render merge request', () => {
+ expect(findLinkSha().exists()).toBe(false);
+ });
+ });
+
+ describe('with merge request', () => {
+ it('renders merge request link and reference', () => {
+ mountComponent({ mergeRequest });
+
+ expect(findLinkSha().attributes('href')).toBe(mergeRequest.path);
+ expect(findLinkSha().text()).toBe(`!${mergeRequest.iid}`);
+ });
+ });
+});
diff --git a/spec/frontend/jobs/components/job/empty_state_spec.js b/spec/frontend/jobs/components/job/empty_state_spec.js
new file mode 100644
index 00000000000..299b607ad78
--- /dev/null
+++ b/spec/frontend/jobs/components/job/empty_state_spec.js
@@ -0,0 +1,140 @@
+import { mount } from '@vue/test-utils';
+import EmptyState from '~/jobs/components/job/empty_state.vue';
+
+describe('Empty State', () => {
+ let wrapper;
+
+ const defaultProps = {
+ illustrationPath: 'illustrations/pending_job_empty.svg',
+ illustrationSizeClass: 'svg-430',
+ title: 'This job has not started yet',
+ playable: false,
+ };
+
+ const createWrapper = (props) => {
+ wrapper = mount(EmptyState, {
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ });
+ };
+
+ 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"]');
+
+ afterEach(() => {
+ if (wrapper?.destroy) {
+ wrapper.destroy();
+ wrapper = null;
+ }
+ });
+
+ describe('renders image and title', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it('renders empty state image', () => {
+ expect(findEmptyStateImage().exists()).toBe(true);
+ });
+
+ it('renders provided title', () => {
+ expect(findTitle().text().trim()).toBe(defaultProps.title);
+ });
+ });
+
+ describe('with content', () => {
+ beforeEach(() => {
+ createWrapper({ content });
+ });
+
+ it('renders content', () => {
+ expect(findContent().text().trim()).toBe(content);
+ });
+ });
+
+ describe('without content', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it('does not render content', () => {
+ expect(findContent().exists()).toBe(false);
+ });
+ });
+
+ describe('with action', () => {
+ beforeEach(() => {
+ createWrapper({
+ action: {
+ path: 'runner',
+ button_title: 'Check runner',
+ method: 'post',
+ },
+ });
+ });
+
+ it('renders action', () => {
+ expect(findAction().attributes('href')).toBe('runner');
+ });
+ });
+
+ describe('without action', () => {
+ beforeEach(() => {
+ createWrapper({
+ action: null,
+ });
+ });
+
+ it('does not render action', () => {
+ expect(findAction().exists()).toBe(false);
+ });
+
+ it('does not render manual variables form', () => {
+ expect(findManualVarsForm().exists()).toBe(false);
+ });
+ });
+
+ describe('with playable action and not scheduled job', () => {
+ beforeEach(() => {
+ createWrapper({
+ content,
+ playable: true,
+ scheduled: false,
+ action: {
+ path: 'runner',
+ button_title: 'Check runner',
+ method: 'post',
+ },
+ });
+ });
+
+ it('renders manual variables form', () => {
+ expect(findManualVarsForm().exists()).toBe(true);
+ });
+
+ it('does not render the empty state action', () => {
+ expect(findAction().exists()).toBe(false);
+ });
+ });
+
+ describe('with playable action and scheduled job', () => {
+ beforeEach(() => {
+ createWrapper({
+ playable: true,
+ scheduled: true,
+ content,
+ });
+ });
+
+ it('does not render manual variables form', () => {
+ expect(findManualVarsForm().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/jobs/components/job/environments_block_spec.js b/spec/frontend/jobs/components/job/environments_block_spec.js
new file mode 100644
index 00000000000..134533e2af8
--- /dev/null
+++ b/spec/frontend/jobs/components/job/environments_block_spec.js
@@ -0,0 +1,265 @@
+import { mount } from '@vue/test-utils';
+import EnvironmentsBlock from '~/jobs/components/job/environments_block.vue';
+
+const TEST_CLUSTER_NAME = 'test_cluster';
+const TEST_CLUSTER_PATH = 'path/to/test_cluster';
+const TEST_KUBERNETES_NAMESPACE = 'this-is-a-kubernetes-namespace';
+
+describe('Environments block', () => {
+ let wrapper;
+
+ const status = {
+ group: 'success',
+ icon: 'status_success',
+ label: 'passed',
+ text: 'passed',
+ tooltip: 'passed',
+ };
+
+ const environment = {
+ environment_path: '/environment',
+ name: 'environment',
+ };
+
+ const lastDeployment = { iid: 'deployment', deployable: { build_path: 'bar' } };
+
+ const createEnvironmentWithLastDeployment = () => ({
+ ...environment,
+ last_deployment: { ...lastDeployment },
+ });
+
+ const createDeploymentWithCluster = () => ({ name: TEST_CLUSTER_NAME, path: TEST_CLUSTER_PATH });
+
+ const createDeploymentWithClusterAndKubernetesNamespace = () => ({
+ name: TEST_CLUSTER_NAME,
+ path: TEST_CLUSTER_PATH,
+ kubernetes_namespace: TEST_KUBERNETES_NAMESPACE,
+ });
+
+ const createComponent = (deploymentStatus = {}, deploymentCluster = {}) => {
+ wrapper = mount(EnvironmentsBlock, {
+ propsData: {
+ deploymentStatus,
+ deploymentCluster,
+ iconStatus: status,
+ },
+ });
+ };
+
+ const findText = () => wrapper.findComponent(EnvironmentsBlock).text();
+ const findJobDeploymentLink = () => wrapper.find('[data-testid="job-deployment-link"]');
+ const findEnvironmentLink = () => wrapper.find('[data-testid="job-environment-link"]');
+ const findClusterLink = () => wrapper.find('[data-testid="job-cluster-link"]');
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('with last deployment', () => {
+ it('renders info for most recent deployment', () => {
+ createComponent({
+ status: 'last',
+ environment,
+ });
+
+ expect(findText()).toBe('This job is deployed to environment.');
+ });
+
+ describe('when there is a cluster', () => {
+ it('renders info with cluster', () => {
+ createComponent(
+ {
+ status: 'last',
+ environment: createEnvironmentWithLastDeployment(),
+ },
+ createDeploymentWithCluster(),
+ );
+
+ expect(findText()).toBe(
+ `This job is deployed to environment using cluster ${TEST_CLUSTER_NAME}.`,
+ );
+ });
+
+ describe('when there is a kubernetes namespace', () => {
+ it('renders info with cluster', () => {
+ createComponent(
+ {
+ status: 'last',
+ environment: createEnvironmentWithLastDeployment(),
+ },
+ createDeploymentWithClusterAndKubernetesNamespace(),
+ );
+
+ expect(findText()).toBe(
+ `This job is deployed to environment using cluster ${TEST_CLUSTER_NAME} and namespace ${TEST_KUBERNETES_NAMESPACE}.`,
+ );
+ });
+ });
+ });
+ });
+
+ describe('with out of date deployment', () => {
+ describe('with last deployment', () => {
+ it('renders info for out date and most recent', () => {
+ createComponent({
+ status: 'out_of_date',
+ environment: createEnvironmentWithLastDeployment(),
+ });
+
+ expect(findText()).toBe(
+ 'This job is an out-of-date deployment to environment. View the most recent deployment.',
+ );
+
+ expect(findJobDeploymentLink().attributes('href')).toBe('bar');
+ });
+
+ describe('when there is a cluster', () => {
+ it('renders info with cluster', () => {
+ createComponent(
+ {
+ status: 'out_of_date',
+ environment: createEnvironmentWithLastDeployment(),
+ },
+ createDeploymentWithCluster(),
+ );
+
+ expect(findText()).toBe(
+ `This job is an out-of-date deployment to environment using cluster ${TEST_CLUSTER_NAME}. View the most recent deployment.`,
+ );
+ });
+
+ describe('when there is a kubernetes namespace', () => {
+ it('renders info with cluster', () => {
+ createComponent(
+ {
+ status: 'out_of_date',
+ environment: createEnvironmentWithLastDeployment(),
+ },
+ createDeploymentWithClusterAndKubernetesNamespace(),
+ );
+
+ expect(findText()).toBe(
+ `This job is an out-of-date deployment to environment using cluster ${TEST_CLUSTER_NAME} and namespace ${TEST_KUBERNETES_NAMESPACE}. View the most recent deployment.`,
+ );
+ });
+ });
+ });
+ });
+
+ describe('without last deployment', () => {
+ it('renders info about out of date deployment', () => {
+ createComponent({
+ status: 'out_of_date',
+ environment,
+ });
+
+ expect(findText()).toBe('This job is an out-of-date deployment to environment.');
+ });
+ });
+ });
+
+ describe('with failed deployment', () => {
+ it('renders info about failed deployment', () => {
+ createComponent({
+ status: 'failed',
+ environment,
+ });
+
+ expect(findText()).toBe('The deployment of this job to environment did not succeed.');
+ });
+ });
+
+ describe('creating deployment', () => {
+ describe('with last deployment', () => {
+ it('renders info about creating deployment and overriding latest deployment', () => {
+ createComponent({
+ status: 'creating',
+ environment: createEnvironmentWithLastDeployment(),
+ });
+
+ expect(findText()).toBe(
+ 'This job is creating a deployment to environment. This will overwrite the latest deployment.',
+ );
+
+ expect(findEnvironmentLink().attributes('href')).toBe(environment.environment_path);
+
+ expect(findJobDeploymentLink().attributes('href')).toBe('bar');
+
+ expect(findClusterLink().exists()).toBe(false);
+ });
+ });
+
+ describe('without last deployment', () => {
+ it('renders info about deployment being created', () => {
+ createComponent({
+ status: 'creating',
+ environment,
+ });
+
+ expect(findText()).toBe('This job is creating a deployment to environment.');
+ });
+
+ describe('when there is a cluster', () => {
+ it('inclues information about the cluster', () => {
+ createComponent(
+ {
+ status: 'creating',
+ environment,
+ },
+ createDeploymentWithCluster(),
+ );
+
+ expect(findText()).toBe(
+ `This job is creating a deployment to environment using cluster ${TEST_CLUSTER_NAME}.`,
+ );
+ });
+ });
+ });
+
+ describe('without environment', () => {
+ it('does not render environment link', () => {
+ createComponent({
+ status: 'creating',
+ environment: null,
+ });
+
+ expect(findEnvironmentLink().exists()).toBe(false);
+ });
+ });
+ });
+
+ describe('with a cluster', () => {
+ it('renders the cluster link', () => {
+ createComponent(
+ {
+ status: 'last',
+ environment: createEnvironmentWithLastDeployment(),
+ },
+ createDeploymentWithCluster(),
+ );
+
+ expect(findText()).toBe(
+ `This job is deployed to environment using cluster ${TEST_CLUSTER_NAME}.`,
+ );
+
+ expect(findClusterLink().attributes('href')).toBe(TEST_CLUSTER_PATH);
+ });
+
+ describe('when the cluster is missing the path', () => {
+ it('renders the name without a link', () => {
+ createComponent(
+ {
+ status: 'last',
+ environment: createEnvironmentWithLastDeployment(),
+ },
+ { name: 'the-cluster' },
+ );
+
+ expect(findText()).toContain('using cluster the-cluster.');
+
+ expect(findClusterLink().exists()).toBe(false);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/jobs/components/job/erased_block_spec.js b/spec/frontend/jobs/components/job/erased_block_spec.js
new file mode 100644
index 00000000000..c6aba01fa53
--- /dev/null
+++ b/spec/frontend/jobs/components/job/erased_block_spec.js
@@ -0,0 +1,63 @@
+import { GlLink } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import ErasedBlock from '~/jobs/components/job/erased_block.vue';
+import { getTimeago } from '~/lib/utils/datetime_utility';
+
+describe('Erased block', () => {
+ let wrapper;
+
+ const erasedAt = '2016-11-07T11:11:16.525Z';
+ const timeago = getTimeago();
+ const formattedDate = timeago.format(erasedAt);
+
+ const findLink = () => wrapper.findComponent(GlLink);
+
+ const createComponent = (props) => {
+ wrapper = mount(ErasedBlock, {
+ propsData: props,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('with job erased by user', () => {
+ beforeEach(() => {
+ createComponent({
+ user: {
+ username: 'root',
+ web_url: 'gitlab.com/root',
+ },
+ erasedAt,
+ });
+ });
+
+ it('renders username and link', () => {
+ expect(findLink().attributes('href')).toEqual('gitlab.com/root');
+
+ expect(wrapper.text().trim()).toContain('Job has been erased by');
+ expect(wrapper.text().trim()).toContain('root');
+ });
+
+ it('renders erasedAt', () => {
+ expect(wrapper.text().trim()).toContain(formattedDate);
+ });
+ });
+
+ describe('with erased job', () => {
+ beforeEach(() => {
+ createComponent({
+ erasedAt,
+ });
+ });
+
+ it('renders username and link', () => {
+ expect(wrapper.text().trim()).toContain('Job has been erased');
+ });
+
+ it('renders erasedAt', () => {
+ expect(wrapper.text().trim()).toContain(formattedDate);
+ });
+ });
+});
diff --git a/spec/frontend/jobs/components/job/job_app_spec.js b/spec/frontend/jobs/components/job/job_app_spec.js
new file mode 100644
index 00000000000..822528403cf
--- /dev/null
+++ b/spec/frontend/jobs/components/job/job_app_spec.js
@@ -0,0 +1,440 @@
+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 { 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 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';
+import createStore from '~/jobs/store';
+import axios from '~/lib/utils/axios_utils';
+import job from '../../mock_data';
+
+describe('Job App', () => {
+ Vue.use(Vuex);
+
+ let store;
+ let wrapper;
+ let mock;
+
+ const initSettings = {
+ endpoint: `${TEST_HOST}jobs/123.json`,
+ pagePath: `${TEST_HOST}jobs/123`,
+ logState:
+ 'eyJvZmZzZXQiOjE3NDUxLCJuX29wZW5fdGFncyI6MCwiZmdfY29sb3IiOm51bGwsImJnX2NvbG9yIjpudWxsLCJzdHlsZV9tYXNrIjowfQ%3D%3D',
+ };
+
+ const props = {
+ artifactHelpUrl: 'help/artifact',
+ deploymentHelpUrl: 'help/deployment',
+ runnerSettingsUrl: 'settings/ci-cd/runners',
+ terminalPath: 'jobs/123/terminal',
+ projectPath: 'user-name/project-name',
+ subscriptionsMoreMinutesUrl: 'https://customers.gitlab.com/buy_pipeline_minutes',
+ };
+
+ const createComponent = () => {
+ wrapper = mount(JobApp, { propsData: { ...props }, store });
+ };
+
+ const setupAndMount = async ({ jobData = {}, jobLogData = {} } = {}) => {
+ mock.onGet(initSettings.endpoint).replyOnce(200, { ...job, ...jobData });
+ mock.onGet(`${initSettings.pagePath}/trace.json`).reply(200, jobLogData);
+
+ const asyncInit = store.dispatch('init', initSettings);
+
+ createComponent();
+
+ await asyncInit;
+ jest.runOnlyPendingTimers();
+ await axios.waitForAll();
+ await nextTick();
+ };
+
+ 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"]');
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ store = createStore();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ mock.restore();
+ });
+
+ describe('while loading', () => {
+ beforeEach(() => {
+ store.state.isLoading = true;
+ createComponent();
+ });
+
+ it('renders loading icon', () => {
+ expect(findLoadingComponent().exists()).toBe(true);
+ expect(findSidebar().exists()).toBe(false);
+ expect(findJobContent().exists()).toBe(false);
+ });
+ });
+
+ describe('with successful request', () => {
+ describe('Header section', () => {
+ describe('job callout message', () => {
+ it('should not render the reason when reason is absent', () =>
+ setupAndMount().then(() => {
+ expect(wrapper.vm.shouldRenderCalloutMessage).toBe(false);
+ }));
+
+ it('should render the reason when reason is present', () =>
+ setupAndMount({
+ jobData: {
+ callout_message: 'There is an unkown failure, please try again',
+ },
+ }).then(() => {
+ 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', () => {
+ describe('without active runners available', () => {
+ it('renders 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,
+ },
+ tags: [],
+ },
+ }).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', () =>
+ setupAndMount({
+ jobData: {
+ runners: { available: true },
+ },
+ }).then(() => {
+ expect(findStuckBlockComponent().exists()).toBe(false);
+ }));
+ });
+
+ describe('unmet prerequisites block', () => {
+ it('renders unmet prerequisites block when there is an unmet prerequisites failure', () =>
+ setupAndMount({
+ jobData: {
+ status: {
+ group: 'failed',
+ icon: 'status_failed',
+ label: 'failed',
+ text: 'failed',
+ details_path: 'path',
+ illustration: {
+ content: 'Retry this job in order to create the necessary resources.',
+ image: 'path',
+ size: 'svg-430',
+ title: 'Failed to create resources',
+ },
+ },
+ failure_reason: 'unmet_prerequisites',
+ has_trace: false,
+ runners: {
+ available: true,
+ },
+ tags: [],
+ },
+ }).then(() => {
+ expect(findFailedJobComponent().exists()).toBe(true);
+ }));
+ });
+
+ describe('environments block', () => {
+ it('renders environment block when job has environment', () =>
+ setupAndMount({
+ jobData: {
+ deployment_status: {
+ environment: {
+ environment_path: '/path',
+ name: 'foo',
+ },
+ },
+ },
+ }).then(() => {
+ expect(findEnvironmentsBlockComponent().exists()).toBe(true);
+ }));
+
+ it('does not render environment block when job has environment', () =>
+ setupAndMount().then(() => {
+ expect(findEnvironmentsBlockComponent().exists()).toBe(false);
+ }));
+ });
+
+ describe('erased block', () => {
+ it('renders erased block when `erased` is true', () =>
+ setupAndMount({
+ jobData: {
+ erased_by: {
+ username: 'root',
+ web_url: 'gitlab.com/root',
+ },
+ erased_at: '2016-11-07T11:11:16.525Z',
+ },
+ }).then(() => {
+ expect(findErasedBlock().exists()).toBe(true);
+ }));
+
+ it('does not render erased block when `erased` is false', () =>
+ setupAndMount({
+ jobData: {
+ erased_at: null,
+ },
+ }).then(() => {
+ expect(findErasedBlock().exists()).toBe(false);
+ }));
+ });
+
+ describe('empty states block', () => {
+ it('renders empty state when job does not have log and is not running', () =>
+ setupAndMount({
+ jobData: {
+ has_trace: false,
+ status: {
+ group: 'pending',
+ icon: 'status_pending',
+ label: 'pending',
+ text: 'pending',
+ details_path: 'path',
+ illustration: {
+ image: 'path',
+ size: '340',
+ title: 'Empty State',
+ content: 'This is an empty state',
+ },
+ action: {
+ button_title: 'Retry job',
+ method: 'post',
+ path: '/path',
+ },
+ },
+ },
+ }).then(() => {
+ expect(findEmptyState().exists()).toBe(true);
+ }));
+
+ it('does not render empty state when job does not have log but it is running', () =>
+ setupAndMount({
+ jobData: {
+ has_trace: false,
+ status: {
+ group: 'running',
+ icon: 'status_running',
+ label: 'running',
+ text: 'running',
+ details_path: 'path',
+ },
+ },
+ }).then(() => {
+ expect(findEmptyState().exists()).toBe(false);
+ }));
+
+ it('does not render empty state when job has log but it is not running', () =>
+ 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,
+ },
+ });
+
+ const blocks = wrapper.findAll('.blocks-container > *').wrappers;
+ expect(blocks.length).toBeGreaterThan(0);
+
+ blocks.forEach((block) => {
+ expect(block.text().trim()).not.toBe('');
+ });
+ });
+ });
+ });
+
+ describe('archived job', () => {
+ beforeEach(() => setupAndMount({ jobData: { archived: true } }));
+
+ it('renders warning about job being archived', () => {
+ expect(findArchivedJob().exists()).toBe(true);
+ });
+ });
+
+ describe('non-archived job', () => {
+ beforeEach(() => setupAndMount());
+
+ it('does not warning about job being archived', () => {
+ expect(findArchivedJob().exists()).toBe(false);
+ });
+ });
+
+ 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);
+ });
+
+ it('should render link to raw ouput', () => {
+ expect(findJobLogController().exists()).toBe(true);
+ });
+
+ it('should render link to erase job', () => {
+ expect(findJobLogEraseLink().exists()).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/jobs/components/job/job_container_item_spec.js b/spec/frontend/jobs/components/job/job_container_item_spec.js
new file mode 100644
index 00000000000..05c38dd74b7
--- /dev/null
+++ b/spec/frontend/jobs/components/job/job_container_item_spec.js
@@ -0,0 +1,98 @@
+import { GlIcon, GlLink } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import delayedJobFixture from 'test_fixtures/jobs/delayed.json';
+import JobContainerItem from '~/jobs/components/job/sidebar/job_container_item.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import job from '../../mock_data';
+
+describe('JobContainerItem', () => {
+ let wrapper;
+
+ const findCiIconComponent = () => wrapper.findComponent(CiIcon);
+ const findGlIconComponent = () => wrapper.findComponent(GlIcon);
+
+ function createComponent(jobData = {}, props = { isActive: false, retried: false }) {
+ wrapper = shallowMount(JobContainerItem, {
+ propsData: {
+ job: {
+ ...jobData,
+ retried: props.retried,
+ },
+ isActive: props.isActive,
+ },
+ });
+ }
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('when a job is not active and not retried', () => {
+ beforeEach(() => {
+ createComponent(job);
+ });
+
+ it('displays a status icon', () => {
+ const ciIcon = findCiIconComponent();
+
+ expect(ciIcon.props('status')).toBe(job.status);
+ });
+
+ it('displays the job name', () => {
+ expect(wrapper.text()).toContain(job.name);
+ });
+
+ it('displays a link to the job', () => {
+ const link = wrapper.findComponent(GlLink);
+
+ expect(link.attributes('href')).toBe(job.status.details_path);
+ });
+ });
+
+ describe('when a job is active', () => {
+ beforeEach(() => {
+ createComponent(job, { isActive: true });
+ });
+
+ it('displays an arrow sprite icon', () => {
+ const icon = findGlIconComponent();
+
+ expect(icon.props('name')).toBe('arrow-right');
+ });
+ });
+
+ describe('when a job is retried', () => {
+ beforeEach(() => {
+ createComponent(job, { isActive: false, retried: true });
+ });
+
+ it('displays a retry icon', () => {
+ const icon = findGlIconComponent();
+
+ expect(icon.props('name')).toBe('retry');
+ });
+ });
+
+ describe('for a delayed job', () => {
+ beforeEach(() => {
+ const remainingMilliseconds = 1337000;
+ jest
+ .spyOn(Date, 'now')
+ .mockImplementation(
+ () => new Date(delayedJobFixture.scheduled_at).getTime() - remainingMilliseconds,
+ );
+
+ createComponent(delayedJobFixture);
+ });
+
+ it('displays remaining time in tooltip', async () => {
+ await nextTick();
+
+ const link = wrapper.findComponent(GlLink);
+
+ expect(link.attributes('title')).toMatch('delayed job - delayed manual action (00:22:17)');
+ });
+ });
+});
diff --git a/spec/frontend/jobs/components/job/job_log_controllers_spec.js b/spec/frontend/jobs/components/job/job_log_controllers_spec.js
new file mode 100644
index 00000000000..5e9a73b4387
--- /dev/null
+++ b/spec/frontend/jobs/components/job/job_log_controllers_spec.js
@@ -0,0 +1,315 @@
+import { GlSearchBoxByClick } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import JobLogControllers from '~/jobs/components/job/job_log_controllers.vue';
+import HelpPopover from '~/vue_shared/components/help_popover.vue';
+import { backoffMockImplementation } from 'helpers/backoff_helper';
+import * as commonUtils from '~/lib/utils/common_utils';
+import { mockJobLog } from '../../mock_data';
+
+const mockToastShow = jest.fn();
+
+describe('Job log controllers', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ jest.spyOn(commonUtils, 'backOff').mockImplementation(backoffMockImplementation);
+ });
+
+ afterEach(() => {
+ if (wrapper?.destroy) {
+ wrapper.destroy();
+ }
+ commonUtils.backOff.mockReset();
+ });
+
+ const defaultProps = {
+ rawPath: '/raw',
+ erasePath: '/erase',
+ size: 511952,
+ isScrollTopDisabled: false,
+ isScrollBottomDisabled: false,
+ isScrollingDown: true,
+ isJobLogSizeVisible: true,
+ isComplete: true,
+ jobLog: mockJobLog,
+ };
+
+ const createWrapper = (props, { jobLogJumpToFailures = false } = {}) => {
+ wrapper = mount(JobLogControllers, {
+ propsData: {
+ ...defaultProps,
+ ...props,
+ },
+ provide: {
+ glFeatures: {
+ jobLogJumpToFailures,
+ },
+ },
+ data() {
+ return {
+ searchTerm: '82',
+ };
+ },
+ mocks: {
+ $toast: {
+ show: mockToastShow,
+ },
+ },
+ });
+ };
+
+ const findTruncatedInfo = () => wrapper.find('[data-testid="log-truncated-info"]');
+ const findRawLink = () => wrapper.find('[data-testid="raw-link"]');
+ const findRawLinkController = () => wrapper.find('[data-testid="job-raw-link-controller"]');
+ const findScrollTop = () => wrapper.find('[data-testid="job-controller-scroll-top"]');
+ const findScrollBottom = () => wrapper.find('[data-testid="job-controller-scroll-bottom"]');
+ const findJobLogSearch = () => wrapper.findComponent(GlSearchBoxByClick);
+ const findSearchHelp = () => wrapper.findComponent(HelpPopover);
+ const findScrollFailure = () => wrapper.find('[data-testid="job-controller-scroll-to-failure"]');
+
+ describe('Truncate information', () => {
+ describe('with isJobLogSizeVisible', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it('renders size information', () => {
+ expect(findTruncatedInfo().text()).toMatch('499.95 KiB');
+ });
+
+ it('renders link to raw job log', () => {
+ expect(findRawLink().attributes('href')).toBe(defaultProps.rawPath);
+ });
+ });
+ });
+
+ describe('links section', () => {
+ describe('with raw job log path', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it('renders raw job log link', () => {
+ expect(findRawLinkController().attributes('href')).toBe(defaultProps.rawPath);
+ });
+ });
+
+ describe('without raw job log path', () => {
+ beforeEach(() => {
+ createWrapper({
+ rawPath: null,
+ });
+ });
+
+ it('does not render raw job log link', () => {
+ expect(findRawLinkController().exists()).toBe(false);
+ });
+ });
+ });
+
+ describe('scroll buttons', () => {
+ describe('scroll top button', () => {
+ describe('when user can scroll top', () => {
+ beforeEach(() => {
+ createWrapper({
+ isScrollTopDisabled: false,
+ });
+ });
+
+ it('emits scrollJobLogTop event on click', async () => {
+ await findScrollTop().trigger('click');
+
+ expect(wrapper.emitted().scrollJobLogTop).toHaveLength(1);
+ });
+ });
+
+ describe('when user can not scroll top', () => {
+ beforeEach(() => {
+ createWrapper({
+ isScrollTopDisabled: true,
+ isScrollBottomDisabled: false,
+ isScrollingDown: false,
+ });
+ });
+
+ it('renders disabled scroll top button', () => {
+ expect(findScrollTop().attributes('disabled')).toBe('disabled');
+ });
+
+ it('does not emit scrollJobLogTop event on click', async () => {
+ await findScrollTop().trigger('click');
+
+ expect(wrapper.emitted().scrollJobLogTop).toBeUndefined();
+ });
+ });
+ });
+
+ describe('scroll bottom button', () => {
+ describe('when user can scroll bottom', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it('emits scrollJobLogBottom event on click', async () => {
+ await findScrollBottom().trigger('click');
+
+ expect(wrapper.emitted().scrollJobLogBottom).toHaveLength(1);
+ });
+ });
+
+ describe('when user can not scroll bottom', () => {
+ beforeEach(() => {
+ createWrapper({
+ isScrollTopDisabled: false,
+ isScrollBottomDisabled: true,
+ isScrollingDown: false,
+ });
+ });
+
+ it('renders disabled scroll bottom button', () => {
+ expect(findScrollBottom().attributes('disabled')).toEqual('disabled');
+ });
+
+ it('does not emit scrollJobLogBottom event on click', async () => {
+ await findScrollBottom().trigger('click');
+
+ expect(wrapper.emitted().scrollJobLogBottom).toBeUndefined();
+ });
+ });
+
+ describe('while isScrollingDown is true', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it('renders animate class for the scroll down button', () => {
+ expect(findScrollBottom().classes()).toContain('animate');
+ });
+ });
+
+ describe('while isScrollingDown is false', () => {
+ beforeEach(() => {
+ createWrapper({
+ isScrollTopDisabled: true,
+ isScrollBottomDisabled: false,
+ isScrollingDown: false,
+ });
+ });
+
+ it('does not render animate class for the scroll down button', () => {
+ expect(findScrollBottom().classes()).not.toContain('animate');
+ });
+ });
+ });
+
+ describe('scroll to failure button', () => {
+ describe('with feature flag disabled', () => {
+ it('does not display button', () => {
+ createWrapper();
+
+ expect(findScrollFailure().exists()).toBe(false);
+ });
+ });
+
+ describe('with red text failures on the page', () => {
+ let firstFailure;
+ let secondFailure;
+
+ beforeEach(() => {
+ jest.spyOn(document, 'querySelectorAll').mockReturnValueOnce(['mock-element']);
+
+ createWrapper({}, { jobLogJumpToFailures: true });
+
+ firstFailure = document.createElement('div');
+ firstFailure.className = 'term-fg-l-red';
+ document.body.appendChild(firstFailure);
+
+ secondFailure = document.createElement('div');
+ secondFailure.className = 'term-fg-l-red';
+ document.body.appendChild(secondFailure);
+ });
+
+ afterEach(() => {
+ if (firstFailure) {
+ firstFailure.remove();
+ firstFailure = null;
+ }
+
+ if (secondFailure) {
+ secondFailure.remove();
+ secondFailure = null;
+ }
+ });
+
+ it('is enabled', () => {
+ expect(findScrollFailure().props('disabled')).toBe(false);
+ });
+
+ it('scrolls to each failure', async () => {
+ jest.spyOn(firstFailure, 'scrollIntoView');
+
+ await findScrollFailure().trigger('click');
+
+ expect(firstFailure.scrollIntoView).toHaveBeenCalled();
+
+ await findScrollFailure().trigger('click');
+
+ expect(secondFailure.scrollIntoView).toHaveBeenCalled();
+
+ await findScrollFailure().trigger('click');
+
+ expect(firstFailure.scrollIntoView).toHaveBeenCalled();
+ });
+ });
+
+ describe('with no red text failures on the page', () => {
+ beforeEach(() => {
+ jest.spyOn(document, 'querySelectorAll').mockReturnValueOnce([]);
+
+ createWrapper({}, { jobLogJumpToFailures: true });
+ });
+
+ it('is disabled', () => {
+ expect(findScrollFailure().props('disabled')).toBe(true);
+ });
+ });
+
+ describe('when the job log is not complete', () => {
+ beforeEach(() => {
+ jest.spyOn(document, 'querySelectorAll').mockReturnValueOnce(['mock-element']);
+
+ createWrapper({ isComplete: false }, { jobLogJumpToFailures: true });
+ });
+
+ it('is enabled', () => {
+ expect(findScrollFailure().props('disabled')).toBe(false);
+ });
+ });
+ });
+ });
+
+ describe('Job log search', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it('displays job log search', () => {
+ expect(findJobLogSearch().exists()).toBe(true);
+ expect(findSearchHelp().exists()).toBe(true);
+ });
+
+ it('emits search results', () => {
+ const expectedSearchResults = [[[mockJobLog[6].lines[1], mockJobLog[6].lines[2]]]];
+
+ findJobLogSearch().vm.$emit('submit');
+
+ expect(wrapper.emitted('searchResults')).toEqual(expectedSearchResults);
+ });
+
+ it('clears search results', () => {
+ findJobLogSearch().vm.$emit('clear');
+
+ expect(wrapper.emitted('searchResults')).toEqual([[[]]]);
+ });
+ });
+});
diff --git a/spec/frontend/jobs/components/job/job_retry_forward_deployment_modal_spec.js b/spec/frontend/jobs/components/job/job_retry_forward_deployment_modal_spec.js
new file mode 100644
index 00000000000..d60043f33f7
--- /dev/null
+++ b/spec/frontend/jobs/components/job/job_retry_forward_deployment_modal_spec.js
@@ -0,0 +1,76 @@
+import { GlLink, GlModal } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import JobRetryForwardDeploymentModal from '~/jobs/components/job/sidebar/job_retry_forward_deployment_modal.vue';
+import { JOB_RETRY_FORWARD_DEPLOYMENT_MODAL } from '~/jobs/constants';
+import createStore from '~/jobs/store';
+import job from '../../mock_data';
+
+describe('Job Retry Forward Deployment Modal', () => {
+ let store;
+ let wrapper;
+
+ const retryOutdatedJobDocsUrl = 'url-to-docs';
+ const findLink = () => wrapper.findComponent(GlLink);
+ const findModal = () => wrapper.findComponent(GlModal);
+
+ const createWrapper = ({ props = {}, provide = {}, stubs = {} } = {}) => {
+ store = createStore();
+ wrapper = shallowMount(JobRetryForwardDeploymentModal, {
+ propsData: {
+ modalId: 'modal-id',
+ href: job.retry_path,
+ ...props,
+ },
+ provide,
+ store,
+ stubs,
+ });
+ };
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ wrapper = null;
+ }
+ });
+
+ beforeEach(createWrapper);
+
+ describe('Modal configuration', () => {
+ it('should display the correct messages', () => {
+ const modal = findModal();
+ expect(modal.attributes('title')).toMatch(JOB_RETRY_FORWARD_DEPLOYMENT_MODAL.title);
+ expect(modal.text()).toMatch(JOB_RETRY_FORWARD_DEPLOYMENT_MODAL.info);
+ expect(modal.text()).toMatch(JOB_RETRY_FORWARD_DEPLOYMENT_MODAL.areYouSure);
+ });
+ });
+
+ describe('Modal docs help link', () => {
+ it('should not display an info link when none is provided', () => {
+ createWrapper();
+
+ expect(findLink().exists()).toBe(false);
+ });
+
+ it('should display an info link when one is provided', () => {
+ createWrapper({ provide: { retryOutdatedJobDocsUrl } });
+
+ expect(findLink().attributes('href')).toBe(retryOutdatedJobDocsUrl);
+ expect(findLink().text()).toMatch(JOB_RETRY_FORWARD_DEPLOYMENT_MODAL.moreInfo);
+ });
+ });
+
+ describe('Modal actions', () => {
+ beforeEach(createWrapper);
+
+ it('should correctly configure the primary action', () => {
+ expect(findModal().props('actionPrimary').attributes).toMatchObject([
+ {
+ 'data-method': 'post',
+ href: job.retry_path,
+ variant: 'danger',
+ },
+ ]);
+ });
+ });
+});
diff --git a/spec/frontend/jobs/components/job/job_sidebar_details_container_spec.js b/spec/frontend/jobs/components/job/job_sidebar_details_container_spec.js
new file mode 100644
index 00000000000..4da17ed8366
--- /dev/null
+++ b/spec/frontend/jobs/components/job/job_sidebar_details_container_spec.js
@@ -0,0 +1,140 @@
+import { shallowMount } from '@vue/test-utils';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import DetailRow from '~/jobs/components/job/sidebar/sidebar_detail_row.vue';
+import SidebarJobDetailsContainer from '~/jobs/components/job/sidebar/sidebar_job_details_container.vue';
+import createStore from '~/jobs/store';
+import job from '../../mock_data';
+
+describe('Job Sidebar Details Container', () => {
+ let store;
+ let wrapper;
+
+ const findJobTimeout = () => wrapper.findByTestId('job-timeout');
+ const findJobTags = () => wrapper.findByTestId('job-tags');
+ const findAllDetailsRow = () => wrapper.findAllComponents(DetailRow);
+
+ const createWrapper = ({ props = {} } = {}) => {
+ store = createStore();
+ wrapper = extendedWrapper(
+ shallowMount(SidebarJobDetailsContainer, {
+ propsData: props,
+ store,
+ stubs: {
+ DetailRow,
+ },
+ }),
+ );
+ };
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ wrapper = null;
+ }
+ });
+
+ describe('when no details are available', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it('should render an empty container', () => {
+ expect(wrapper.html()).toBe('');
+ });
+
+ it.each(['duration', 'erased_at', 'finished_at', 'queued_at', 'runner', 'coverage'])(
+ 'should not render %s details when missing',
+ async (detail) => {
+ await store.dispatch('receiveJobSuccess', { [detail]: undefined });
+
+ expect(findAllDetailsRow()).toHaveLength(0);
+ },
+ );
+ });
+
+ describe('when some of the details are available', () => {
+ beforeEach(createWrapper);
+
+ it.each([
+ ['duration', 'Elapsed time: 6 seconds'],
+ ['erased_at', 'Erased: 3 weeks ago'],
+ ['finished_at', 'Finished: 3 weeks ago'],
+ ['queued_duration', 'Queued: 9 seconds'],
+ ['runner', 'Runner: #1 (ABCDEFGH) local ci runner'],
+ ['coverage', 'Coverage: 20%'],
+ ])('uses %s to render job-%s', async (detail, value) => {
+ await store.dispatch('receiveJobSuccess', { [detail]: job[detail] });
+ const detailsRow = findAllDetailsRow();
+
+ expect(detailsRow).toHaveLength(1);
+ expect(detailsRow.at(0).text()).toBe(value);
+ });
+
+ it('only renders tags', async () => {
+ const { tags } = job;
+ await store.dispatch('receiveJobSuccess', { tags });
+ const tagsComponent = findJobTags();
+
+ expect(tagsComponent.text()).toBe('Tags: tag');
+ });
+ });
+
+ describe('when all the info are available', () => {
+ it('renders all the details components', async () => {
+ createWrapper();
+ await store.dispatch('receiveJobSuccess', job);
+
+ expect(findAllDetailsRow()).toHaveLength(7);
+ });
+
+ describe('duration row', () => {
+ it('renders all the details components', async () => {
+ createWrapper();
+ await store.dispatch('receiveJobSuccess', job);
+
+ expect(findAllDetailsRow().at(0).text()).toBe('Duration: 6 seconds');
+ });
+ });
+ });
+
+ describe('timeout', () => {
+ const {
+ metadata: { timeout_human_readable, timeout_source },
+ } = job;
+
+ beforeEach(createWrapper);
+
+ it('does not render if metadata is empty', async () => {
+ const metadata = {};
+ await store.dispatch('receiveJobSuccess', { metadata });
+ const detailsRow = findAllDetailsRow();
+
+ expect(wrapper.html()).toBe('');
+ expect(detailsRow.exists()).toBe(false);
+ });
+
+ it('uses metadata to render timeout', async () => {
+ const metadata = { timeout_human_readable };
+ await store.dispatch('receiveJobSuccess', { metadata });
+ const detailsRow = findAllDetailsRow();
+
+ expect(detailsRow).toHaveLength(1);
+ expect(detailsRow.at(0).text()).toBe('Timeout: 1m 40s');
+ });
+
+ it('uses metadata to render timeout and the source', async () => {
+ const metadata = { timeout_human_readable, timeout_source };
+ await store.dispatch('receiveJobSuccess', { metadata });
+ const detailsRow = findAllDetailsRow();
+
+ expect(detailsRow.at(0).text()).toBe('Timeout: 1m 40s (from runner)');
+ });
+
+ it('should not render when no time is provided', async () => {
+ const metadata = { timeout_source };
+ await store.dispatch('receiveJobSuccess', { metadata });
+
+ expect(findJobTimeout().exists()).toBe(false);
+ });
+ });
+});
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
new file mode 100644
index 00000000000..18d5f35bde4
--- /dev/null
+++ b/spec/frontend/jobs/components/job/job_sidebar_retry_button_spec.js
@@ -0,0 +1,69 @@
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import JobsSidebarRetryButton from '~/jobs/components/job/sidebar/job_sidebar_retry_button.vue';
+import createStore from '~/jobs/store';
+import job from '../../mock_data';
+
+describe('Job Sidebar Retry Button', () => {
+ let store;
+ let wrapper;
+
+ const forwardDeploymentFailure = 'forward_deployment_failure';
+ const findRetryButton = () => wrapper.findByTestId('retry-job-button');
+ const findRetryLink = () => wrapper.findByTestId('retry-job-link');
+
+ const createWrapper = ({ props = {} } = {}) => {
+ store = createStore();
+ wrapper = shallowMountExtended(JobsSidebarRetryButton, {
+ propsData: {
+ href: job.retry_path,
+ modalId: 'modal-id',
+ ...props,
+ },
+ store,
+ });
+ };
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ }
+ });
+
+ beforeEach(createWrapper);
+
+ it.each([
+ [null, false, true],
+ ['unmet_prerequisites', false, true],
+ [forwardDeploymentFailure, true, false],
+ ])(
+ 'when error is: %s, should render button: %s | should render link: %s',
+ async (failureReason, buttonExists, linkExists) => {
+ await store.dispatch('receiveJobSuccess', { ...job, failure_reason: failureReason });
+
+ expect(findRetryButton().exists()).toBe(buttonExists);
+ expect(findRetryLink().exists()).toBe(linkExists);
+ },
+ );
+
+ describe('Button', () => {
+ it('should have the correct configuration', async () => {
+ await store.dispatch('receiveJobSuccess', { failure_reason: forwardDeploymentFailure });
+
+ expect(findRetryButton().attributes()).toMatchObject({
+ category: 'primary',
+ variant: 'confirm',
+ icon: 'retry',
+ });
+ });
+ });
+
+ describe('Link', () => {
+ it('should have the correct configuration', () => {
+ expect(findRetryLink().attributes()).toMatchObject({
+ 'data-method': 'post',
+ href: job.retry_path,
+ icon: 'retry',
+ });
+ });
+ });
+});
diff --git a/spec/frontend/jobs/components/job/jobs_container_spec.js b/spec/frontend/jobs/components/job/jobs_container_spec.js
new file mode 100644
index 00000000000..2fde4d3020b
--- /dev/null
+++ b/spec/frontend/jobs/components/job/jobs_container_spec.js
@@ -0,0 +1,147 @@
+import { GlLink } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import JobsContainer from '~/jobs/components/job/sidebar/jobs_container.vue';
+
+describe('Jobs List block', () => {
+ let wrapper;
+
+ const retried = {
+ status: {
+ details_path: '/gitlab-org/gitlab-foss/pipelines/28029444',
+ group: 'success',
+ has_details: true,
+ icon: 'status_success',
+ label: 'passed',
+ text: 'passed',
+ tooltip: 'passed',
+ },
+ id: 233432756,
+ tooltip: 'build - passed',
+ retried: true,
+ };
+
+ const active = {
+ name: 'test',
+ status: {
+ details_path: '/gitlab-org/gitlab-foss/pipelines/28029444',
+ group: 'success',
+ has_details: true,
+ icon: 'status_success',
+ label: 'passed',
+ text: 'passed',
+ tooltip: 'passed',
+ },
+ id: 2322756,
+ tooltip: 'build - passed',
+ active: true,
+ };
+
+ const job = {
+ name: 'build',
+ status: {
+ details_path: '/gitlab-org/gitlab-foss/pipelines/28029444',
+ group: 'success',
+ has_details: true,
+ icon: 'status_success',
+ label: 'passed',
+ text: 'passed',
+ tooltip: 'passed',
+ },
+ id: 232153,
+ tooltip: 'build - passed',
+ };
+
+ const findAllJobs = () => wrapper.findAllComponents(GlLink);
+ const findJob = () => findAllJobs().at(0);
+
+ const findArrowIcon = () => wrapper.findByTestId('arrow-right-icon');
+ const findRetryIcon = () => wrapper.findByTestId('retry-icon');
+
+ const createComponent = (props) => {
+ wrapper = extendedWrapper(
+ mount(JobsContainer, {
+ propsData: {
+ ...props,
+ },
+ }),
+ );
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders a list of jobs', () => {
+ createComponent({
+ jobs: [job, retried, active],
+ jobId: 12313,
+ });
+
+ expect(findAllJobs()).toHaveLength(3);
+ });
+
+ it('renders the arrow right icon when job id matches `jobId`', () => {
+ createComponent({
+ jobs: [active],
+ jobId: active.id,
+ });
+
+ expect(findArrowIcon().exists()).toBe(true);
+ });
+
+ it('does not render the arrow right icon when the job is not active', () => {
+ createComponent({
+ jobs: [job],
+ jobId: active.id,
+ });
+
+ expect(findArrowIcon().exists()).toBe(false);
+ });
+
+ it('renders the job name when present', () => {
+ createComponent({
+ jobs: [job],
+ jobId: active.id,
+ });
+
+ expect(findJob().text()).toBe(job.name);
+ expect(findJob().text()).not.toContain(job.id.toString());
+ });
+
+ it('renders job id when job name is not available', () => {
+ createComponent({
+ jobs: [retried],
+ jobId: active.id,
+ });
+
+ expect(findJob().text()).toBe(retried.id.toString());
+ });
+
+ it('links to the job page', () => {
+ createComponent({
+ jobs: [job],
+ jobId: active.id,
+ });
+
+ expect(findJob().attributes('href')).toBe(job.status.details_path);
+ });
+
+ it('renders retry icon when job was retried', () => {
+ createComponent({
+ jobs: [retried],
+ jobId: active.id,
+ });
+
+ expect(findRetryIcon().exists()).toBe(true);
+ });
+
+ it('does not render retry icon when job was not retried', () => {
+ createComponent({
+ jobs: [job],
+ jobId: active.id,
+ });
+
+ expect(findRetryIcon().exists()).toBe(false);
+ });
+});
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
new file mode 100644
index 00000000000..184562b2968
--- /dev/null
+++ b/spec/frontend/jobs/components/job/legacy_manual_variables_form_spec.js
@@ -0,0 +1,156 @@
+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
new file mode 100644
index 00000000000..cb32ca9d3dc
--- /dev/null
+++ b/spec/frontend/jobs/components/job/legacy_sidebar_header_spec.js
@@ -0,0 +1,91 @@
+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 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);
+ });
+ });
+
+ 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);
+ });
+ });
+});
diff --git a/spec/frontend/jobs/components/job/manual_variables_form_spec.js b/spec/frontend/jobs/components/job/manual_variables_form_spec.js
new file mode 100644
index 00000000000..5806f9f75f9
--- /dev/null
+++ b/spec/frontend/jobs/components/job/manual_variables_form_spec.js
@@ -0,0 +1,156 @@
+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 ManualVariablesForm from '~/jobs/components/job/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(ManualVariablesForm, {
+ 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/sidebar_detail_row_spec.js b/spec/frontend/jobs/components/job/sidebar_detail_row_spec.js
new file mode 100644
index 00000000000..5c9c011b4ab
--- /dev/null
+++ b/spec/frontend/jobs/components/job/sidebar_detail_row_spec.js
@@ -0,0 +1,55 @@
+import { GlLink } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import SidebarDetailRow from '~/jobs/components/job/sidebar/sidebar_detail_row.vue';
+
+describe('Sidebar detail row', () => {
+ let wrapper;
+
+ const title = 'this is the title';
+ const value = 'this is the value';
+ const helpUrl = 'https://docs.gitlab.com/runner/register/index.html';
+
+ const findHelpLink = () => wrapper.findComponent(GlLink);
+
+ const createComponent = (props) => {
+ wrapper = shallowMount(SidebarDetailRow, {
+ propsData: {
+ ...props,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ describe('with title/value and without helpUrl', () => {
+ beforeEach(() => {
+ createComponent({ title, value });
+ });
+
+ it('should render the provided title and value', () => {
+ expect(wrapper.text()).toBe(`${title}: ${value}`);
+ });
+
+ it('should not render the help link', () => {
+ expect(findHelpLink().exists()).toBe(false);
+ });
+ });
+
+ describe('when helpUrl provided', () => {
+ beforeEach(() => {
+ createComponent({
+ helpUrl,
+ title,
+ value,
+ });
+ });
+
+ it('should render the help link', () => {
+ expect(findHelpLink().exists()).toBe(true);
+ expect(findHelpLink().attributes('href')).toBe(helpUrl);
+ });
+ });
+});
diff --git a/spec/frontend/jobs/components/job/sidebar_header_spec.js b/spec/frontend/jobs/components/job/sidebar_header_spec.js
new file mode 100644
index 00000000000..cb32ca9d3dc
--- /dev/null
+++ b/spec/frontend/jobs/components/job/sidebar_header_spec.js
@@ -0,0 +1,91 @@
+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 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);
+ });
+ });
+
+ 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);
+ });
+ });
+});
diff --git a/spec/frontend/jobs/components/job/sidebar_spec.js b/spec/frontend/jobs/components/job/sidebar_spec.js
new file mode 100644
index 00000000000..dc1aa67489d
--- /dev/null
+++ b/spec/frontend/jobs/components/job/sidebar_spec.js
@@ -0,0 +1,166 @@
+import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import ArtifactsBlock from '~/jobs/components/job/sidebar/artifacts_block.vue';
+import JobRetryForwardDeploymentModal from '~/jobs/components/job/sidebar/job_retry_forward_deployment_modal.vue';
+import JobsContainer from '~/jobs/components/job/sidebar/jobs_container.vue';
+import Sidebar from '~/jobs/components/job/sidebar/sidebar.vue';
+import StagesDropdown from '~/jobs/components/job/sidebar/stages_dropdown.vue';
+import createStore from '~/jobs/store';
+import job, { jobsInStage } from '../../mock_data';
+
+describe('Sidebar details block', () => {
+ let store;
+ let wrapper;
+
+ const forwardDeploymentFailure = 'forward_deployment_failure';
+ const findModal = () => wrapper.findComponent(JobRetryForwardDeploymentModal);
+ const findArtifactsBlock = () => wrapper.findComponent(ArtifactsBlock);
+ const findNewIssueButton = () => wrapper.findByTestId('job-new-issue');
+ const findTerminalLink = () => wrapper.findByTestId('terminal-link');
+
+ const createWrapper = (props) => {
+ store = createStore();
+
+ store.state.job = job;
+
+ wrapper = extendedWrapper(
+ shallowMount(Sidebar, {
+ propsData: {
+ ...props,
+ },
+
+ store,
+ }),
+ );
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('without terminal path', () => {
+ it('does not render terminal link', async () => {
+ createWrapper();
+ await store.dispatch('receiveJobSuccess', job);
+
+ expect(findTerminalLink().exists()).toBe(false);
+ });
+ });
+
+ describe('with terminal path', () => {
+ it('renders terminal link', async () => {
+ createWrapper();
+ await store.dispatch('receiveJobSuccess', { ...job, terminal_path: 'job/43123/terminal' });
+
+ expect(findTerminalLink().exists()).toBe(true);
+ });
+ });
+
+ describe('actions', () => {
+ beforeEach(() => {
+ createWrapper();
+ return store.dispatch('receiveJobSuccess', job);
+ });
+
+ it('should render link to new issue', () => {
+ expect(findNewIssueButton().attributes('href')).toBe(job.new_issue_path);
+ expect(findNewIssueButton().text()).toBe('New issue');
+ });
+ });
+
+ describe('forward deployment failure', () => {
+ describe('when the relevant data is missing', () => {
+ it.each`
+ retryPath | failureReason
+ ${null} | ${null}
+ ${''} | ${''}
+ ${job.retry_path} | ${''}
+ ${''} | ${forwardDeploymentFailure}
+ ${job.retry_path} | ${'unmet_prerequisites'}
+ `(
+ 'should not render the modal when path and failure are $retryPath, $failureReason',
+ async ({ retryPath, failureReason }) => {
+ createWrapper();
+ await store.dispatch('receiveJobSuccess', {
+ ...job,
+ failure_reason: failureReason,
+ retry_path: retryPath,
+ });
+ expect(findModal().exists()).toBe(false);
+ },
+ );
+ });
+
+ describe('when there is the relevant error', () => {
+ beforeEach(() => {
+ createWrapper();
+ return store.dispatch('receiveJobSuccess', {
+ ...job,
+ failure_reason: forwardDeploymentFailure,
+ });
+ });
+
+ it('should render the modal', () => {
+ expect(findModal().exists()).toBe(true);
+ });
+ });
+ });
+
+ describe('stages dropdown', () => {
+ beforeEach(() => {
+ createWrapper();
+ return store.dispatch('receiveJobSuccess', { ...job, stage: 'aStage' });
+ });
+
+ describe('with stages', () => {
+ it('renders value provided as selectedStage as selected', () => {
+ expect(wrapper.findComponent(StagesDropdown).props('selectedStage')).toBe('aStage');
+ });
+ });
+
+ describe('without jobs for stages', () => {
+ beforeEach(() => store.dispatch('receiveJobSuccess', job));
+
+ it('does not render jobs container', () => {
+ expect(wrapper.findComponent(JobsContainer).exists()).toBe(false);
+ });
+ });
+
+ describe('with jobs for stages', () => {
+ beforeEach(async () => {
+ await store.dispatch('receiveJobSuccess', job);
+ await store.dispatch('receiveJobsForStageSuccess', jobsInStage.latest_statuses);
+ });
+
+ it('renders list of jobs', () => {
+ expect(wrapper.findComponent(JobsContainer).exists()).toBe(true);
+ });
+ });
+ });
+
+ describe('artifacts', () => {
+ beforeEach(() => {
+ createWrapper();
+ });
+
+ it('artifacts are not shown if there are no properties other than locked', () => {
+ expect(findArtifactsBlock().exists()).toBe(false);
+ });
+
+ it('artifacts are shown if present', async () => {
+ store.state.job.artifact = {
+ download_path: '/root/ci-project/-/jobs/1960/artifacts/download',
+ browse_path: '/root/ci-project/-/jobs/1960/artifacts/browse',
+ keep_path: '/root/ci-project/-/jobs/1960/artifacts/keep',
+ expire_at: '2021-03-23T17:57:11.211Z',
+ expired: false,
+ locked: false,
+ };
+
+ await nextTick();
+
+ expect(findArtifactsBlock().exists()).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/jobs/components/job/stages_dropdown_spec.js b/spec/frontend/jobs/components/job/stages_dropdown_spec.js
new file mode 100644
index 00000000000..61dec585e82
--- /dev/null
+++ b/spec/frontend/jobs/components/job/stages_dropdown_spec.js
@@ -0,0 +1,192 @@
+import { GlDropdown, GlDropdownItem, GlLink, GlSprintf } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Mousetrap from 'mousetrap';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import StagesDropdown from '~/jobs/components/job/sidebar/stages_dropdown.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import * as copyToClipboard from '~/behaviors/copy_to_clipboard';
+import {
+ mockPipelineWithoutRef,
+ mockPipelineWithoutMR,
+ mockPipelineWithAttachedMR,
+ mockPipelineDetached,
+} from '../../mock_data';
+
+describe('Stages Dropdown', () => {
+ let wrapper;
+
+ const findStatus = () => wrapper.findComponent(CiIcon);
+ const findSelectedStageText = () => wrapper.findComponent(GlDropdown).props('text');
+ const findStageItem = (index) => wrapper.findAllComponents(GlDropdownItem).at(index);
+
+ const findPipelineInfoText = () => wrapper.findByTestId('pipeline-info').text();
+
+ const createComponent = (props) => {
+ wrapper = extendedWrapper(
+ shallowMount(StagesDropdown, {
+ propsData: {
+ stages: [],
+ selectedStage: 'deploy',
+ ...props,
+ },
+ stubs: {
+ GlSprintf,
+ GlLink,
+ },
+ }),
+ );
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('without a merge request pipeline', () => {
+ beforeEach(() => {
+ createComponent({
+ pipeline: mockPipelineWithoutMR,
+ stages: [{ name: 'build' }, { name: 'test' }],
+ });
+ });
+
+ it('renders pipeline status', () => {
+ expect(findStatus().exists()).toBe(true);
+ });
+
+ it('renders dropdown with stages', () => {
+ expect(findStageItem(0).text()).toBe('build');
+ });
+
+ it('rendes selected stage', () => {
+ expect(findSelectedStageText()).toBe('deploy');
+ });
+ });
+
+ describe('pipelineInfo', () => {
+ const allElements = [
+ 'pipeline-path',
+ 'mr-link',
+ 'source-ref-link',
+ 'copy-source-ref-link',
+ 'source-branch-link',
+ 'copy-source-branch-link',
+ 'target-branch-link',
+ 'copy-target-branch-link',
+ ];
+ describe.each([
+ [
+ 'does not have a ref',
+ {
+ pipeline: mockPipelineWithoutRef,
+ text: `Pipeline #${mockPipelineWithoutRef.id}`,
+ foundElements: [
+ { testId: 'pipeline-path', props: [{ href: mockPipelineWithoutRef.path }] },
+ ],
+ },
+ ],
+ [
+ 'hasRef but not triggered by MR',
+ {
+ pipeline: mockPipelineWithoutMR,
+ text: `Pipeline #${mockPipelineWithoutMR.id} for ${mockPipelineWithoutMR.ref.name}`,
+ foundElements: [
+ { testId: 'pipeline-path', props: [{ href: mockPipelineWithoutMR.path }] },
+ { testId: 'source-ref-link', props: [{ href: mockPipelineWithoutMR.ref.path }] },
+ { testId: 'copy-source-ref-link', props: [{ text: mockPipelineWithoutMR.ref.name }] },
+ ],
+ },
+ ],
+ [
+ 'hasRef and MR but not MR pipeline',
+ {
+ pipeline: mockPipelineDetached,
+ text: `Pipeline #${mockPipelineDetached.id} for !${mockPipelineDetached.merge_request.iid} with ${mockPipelineDetached.merge_request.source_branch}`,
+ foundElements: [
+ { testId: 'pipeline-path', props: [{ href: mockPipelineDetached.path }] },
+ { testId: 'mr-link', props: [{ href: mockPipelineDetached.merge_request.path }] },
+ {
+ testId: 'source-branch-link',
+ props: [{ href: mockPipelineDetached.merge_request.source_branch_path }],
+ },
+ {
+ testId: 'copy-source-branch-link',
+ props: [{ text: mockPipelineDetached.merge_request.source_branch }],
+ },
+ ],
+ },
+ ],
+ [
+ 'hasRef and MR and MR pipeline',
+ {
+ pipeline: mockPipelineWithAttachedMR,
+ text: `Pipeline #${mockPipelineWithAttachedMR.id} for !${mockPipelineWithAttachedMR.merge_request.iid} with ${mockPipelineWithAttachedMR.merge_request.source_branch} into ${mockPipelineWithAttachedMR.merge_request.target_branch}`,
+ foundElements: [
+ { testId: 'pipeline-path', props: [{ href: mockPipelineWithAttachedMR.path }] },
+ { testId: 'mr-link', props: [{ href: mockPipelineWithAttachedMR.merge_request.path }] },
+ {
+ testId: 'source-branch-link',
+ props: [{ href: mockPipelineWithAttachedMR.merge_request.source_branch_path }],
+ },
+ {
+ testId: 'copy-source-branch-link',
+ props: [{ text: mockPipelineWithAttachedMR.merge_request.source_branch }],
+ },
+ {
+ testId: 'target-branch-link',
+ props: [{ href: mockPipelineWithAttachedMR.merge_request.target_branch_path }],
+ },
+ {
+ testId: 'copy-target-branch-link',
+ props: [{ text: mockPipelineWithAttachedMR.merge_request.target_branch }],
+ },
+ ],
+ },
+ ],
+ ])('%s', (_, { pipeline, text, foundElements }) => {
+ beforeEach(() => {
+ createComponent({
+ pipeline,
+ });
+ });
+
+ it('should render the text', () => {
+ expect(findPipelineInfoText()).toMatchInterpolatedText(text);
+ });
+
+ it('should find components with props', () => {
+ foundElements.forEach((element) => {
+ element.props.forEach((prop) => {
+ const key = Object.keys(prop)[0];
+ expect(wrapper.findByTestId(element.testId).attributes(key)).toBe(prop[key]);
+ });
+ });
+ });
+
+ it('should not find components', () => {
+ const foundTestIds = foundElements.map((element) => element.testId);
+ allElements
+ .filter((testId) => !foundTestIds.includes(testId))
+ .forEach((testId) => {
+ expect(wrapper.findByTestId(testId).exists()).toBe(false);
+ });
+ });
+ });
+ });
+
+ describe('mousetrap', () => {
+ it.each([
+ ['copy-source-ref-link', mockPipelineWithoutMR],
+ ['copy-source-branch-link', mockPipelineWithAttachedMR],
+ ])(
+ 'calls clickCopyToClipboardButton with `%s` button when `b` is pressed',
+ (button, pipeline) => {
+ const copyToClipboardMock = jest.spyOn(copyToClipboard, 'clickCopyToClipboardButton');
+ createComponent({ pipeline });
+
+ Mousetrap.trigger('b');
+
+ expect(copyToClipboardMock).toHaveBeenCalledWith(wrapper.findByTestId(button).element);
+ },
+ );
+ });
+});
diff --git a/spec/frontend/jobs/components/job/stuck_block_spec.js b/spec/frontend/jobs/components/job/stuck_block_spec.js
new file mode 100644
index 00000000000..8dc570cce27
--- /dev/null
+++ b/spec/frontend/jobs/components/job/stuck_block_spec.js
@@ -0,0 +1,101 @@
+import { GlBadge, GlLink } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import StuckBlock from '~/jobs/components/job/stuck_block.vue';
+
+describe('Stuck Block Job component', () => {
+ let wrapper;
+
+ afterEach(() => {
+ if (wrapper?.destroy) {
+ wrapper.destroy();
+ wrapper = null;
+ }
+ });
+
+ const createWrapper = (props) => {
+ wrapper = shallowMount(StuckBlock, {
+ propsData: {
+ ...props,
+ },
+ });
+ };
+
+ const tags = ['docker', 'gitlab-org'];
+
+ const findStuckNoActiveRunners = () =>
+ wrapper.find('[data-testid="job-stuck-no-active-runners"]');
+ const findStuckNoRunners = () => wrapper.find('[data-testid="job-stuck-no-runners"]');
+ const findStuckWithTags = () => wrapper.find('[data-testid="job-stuck-with-tags"]');
+ const findRunnerPathLink = () => wrapper.findComponent(GlLink);
+ const findAllBadges = () => wrapper.findAllComponents(GlBadge);
+
+ describe('with no runners for project', () => {
+ beforeEach(() => {
+ createWrapper({
+ hasOfflineRunnersForProject: true,
+ runnersPath: '/root/project/runners#js-runners-settings',
+ });
+ });
+
+ it('renders only information about project not having runners', () => {
+ expect(findStuckNoRunners().exists()).toBe(true);
+ expect(findStuckWithTags().exists()).toBe(false);
+ expect(findStuckNoActiveRunners().exists()).toBe(false);
+ });
+
+ it('renders link to runners page', () => {
+ expect(findRunnerPathLink().attributes('href')).toBe(
+ '/root/project/runners#js-runners-settings',
+ );
+ });
+ });
+
+ describe('with tags', () => {
+ beforeEach(() => {
+ createWrapper({
+ hasOfflineRunnersForProject: false,
+ tags,
+ runnersPath: '/root/project/runners#js-runners-settings',
+ });
+ });
+
+ it('renders information about the tags not being set', () => {
+ expect(findStuckWithTags().exists()).toBe(true);
+ expect(findStuckNoActiveRunners().exists()).toBe(false);
+ expect(findStuckNoRunners().exists()).toBe(false);
+ });
+
+ it('renders tags', () => {
+ findAllBadges().wrappers.forEach((badgeElt, index) => {
+ return expect(badgeElt.text()).toBe(tags[index]);
+ });
+ });
+
+ it('renders link to runners page', () => {
+ expect(findRunnerPathLink().attributes('href')).toBe(
+ '/root/project/runners#js-runners-settings',
+ );
+ });
+ });
+
+ describe('without active runners', () => {
+ beforeEach(() => {
+ createWrapper({
+ hasOfflineRunnersForProject: false,
+ runnersPath: '/root/project/runners#js-runners-settings',
+ });
+ });
+
+ it('renders information about project not having runners', () => {
+ expect(findStuckNoActiveRunners().exists()).toBe(true);
+ expect(findStuckNoRunners().exists()).toBe(false);
+ expect(findStuckWithTags().exists()).toBe(false);
+ });
+
+ it('renders link to runners page', () => {
+ expect(findRunnerPathLink().attributes('href')).toBe(
+ '/root/project/runners#js-runners-settings',
+ );
+ });
+ });
+});
diff --git a/spec/frontend/jobs/components/job/trigger_block_spec.js b/spec/frontend/jobs/components/job/trigger_block_spec.js
new file mode 100644
index 00000000000..a1de8fd143f
--- /dev/null
+++ b/spec/frontend/jobs/components/job/trigger_block_spec.js
@@ -0,0 +1,85 @@
+import { GlButton, GlTableLite } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import TriggerBlock from '~/jobs/components/job/sidebar/trigger_block.vue';
+
+describe('Trigger block', () => {
+ let wrapper;
+
+ const findRevealButton = () => wrapper.findComponent(GlButton);
+ const findVariableTable = () => wrapper.findComponent(GlTableLite);
+ const findShortToken = () => wrapper.find('[data-testid="trigger-short-token"]');
+ const findVariableValue = (index) =>
+ wrapper.findAll('[data-testid="trigger-build-value"]').at(index);
+ const findVariableKey = (index) => wrapper.findAll('[data-testid="trigger-build-key"]').at(index);
+
+ const createComponent = (props) => {
+ wrapper = mount(TriggerBlock, {
+ propsData: {
+ ...props,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('with short token and no variables', () => {
+ it('renders short token', () => {
+ createComponent({
+ trigger: {
+ short_token: '0a666b2',
+ variables: [],
+ },
+ });
+
+ expect(findShortToken().text()).toContain('0a666b2');
+ });
+ });
+
+ describe('without variables or short token', () => {
+ beforeEach(() => {
+ createComponent({ trigger: { variables: [] } });
+ });
+
+ it('does not render short token', () => {
+ expect(findShortToken().exists()).toBe(false);
+ });
+
+ it('does not render variables', () => {
+ expect(findRevealButton().exists()).toBe(false);
+ expect(findVariableTable().exists()).toBe(false);
+ });
+ });
+
+ describe('with variables', () => {
+ describe('hide/reveal variables', () => {
+ it('should toggle variables on click', async () => {
+ const hiddenValue = '••••••';
+ const gcsVar = { key: 'UPLOAD_TO_GCS', value: 'false', public: false };
+ const s3Var = { key: 'UPLOAD_TO_S3', value: 'true', public: false };
+
+ createComponent({
+ trigger: {
+ variables: [gcsVar, s3Var],
+ },
+ });
+
+ expect(findRevealButton().text()).toBe('Reveal values');
+
+ expect(findVariableValue(0).text()).toBe(hiddenValue);
+ expect(findVariableValue(1).text()).toBe(hiddenValue);
+
+ expect(findVariableKey(0).text()).toBe(gcsVar.key);
+ expect(findVariableKey(1).text()).toBe(s3Var.key);
+
+ await findRevealButton().trigger('click');
+
+ expect(findRevealButton().text()).toBe('Hide values');
+
+ expect(findVariableValue(0).text()).toBe(gcsVar.value);
+ expect(findVariableValue(1).text()).toBe(s3Var.value);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/jobs/components/job/unmet_prerequisites_block_spec.js b/spec/frontend/jobs/components/job/unmet_prerequisites_block_spec.js
new file mode 100644
index 00000000000..fb7d389c4d6
--- /dev/null
+++ b/spec/frontend/jobs/components/job/unmet_prerequisites_block_spec.js
@@ -0,0 +1,41 @@
+import { GlAlert, GlLink } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import UnmetPrerequisitesBlock from '~/jobs/components/job/unmet_prerequisites_block.vue';
+
+describe('Unmet Prerequisites Block Job component', () => {
+ let wrapper;
+ const helpPath = '/user/project/clusters/index.html#troubleshooting-failed-deployment-jobs';
+
+ const createComponent = () => {
+ wrapper = shallowMount(UnmetPrerequisitesBlock, {
+ propsData: {
+ helpPath,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders an alert with the correct message', () => {
+ const container = wrapper.findComponent(GlAlert);
+ const alertMessage =
+ 'This job failed because the necessary resources were not successfully created.';
+
+ expect(container).not.toBeNull();
+ expect(container.text()).toContain(alertMessage);
+ });
+
+ it('renders link to help page', () => {
+ const helpLink = wrapper.findComponent(GlLink);
+
+ expect(helpLink).not.toBeNull();
+ expect(helpLink.text()).toContain('More information');
+ expect(helpLink.attributes().href).toEqual(helpPath);
+ });
+});
diff --git a/spec/frontend/jobs/components/job_app_spec.js b/spec/frontend/jobs/components/job_app_spec.js
deleted file mode 100644
index b4b5bc4669d..00000000000
--- a/spec/frontend/jobs/components/job_app_spec.js
+++ /dev/null
@@ -1,440 +0,0 @@
-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 { TEST_HOST } from 'helpers/test_constants';
-import EmptyState from '~/jobs/components/empty_state.vue';
-import EnvironmentsBlock from '~/jobs/components/environments_block.vue';
-import ErasedBlock from '~/jobs/components/erased_block.vue';
-import JobApp from '~/jobs/components/job_app.vue';
-import Sidebar from '~/jobs/components/sidebar.vue';
-import StuckBlock from '~/jobs/components/stuck_block.vue';
-import UnmetPrerequisitesBlock from '~/jobs/components/unmet_prerequisites_block.vue';
-import createStore from '~/jobs/store';
-import axios from '~/lib/utils/axios_utils';
-import job from '../mock_data';
-
-describe('Job App', () => {
- Vue.use(Vuex);
-
- let store;
- let wrapper;
- let mock;
-
- const initSettings = {
- endpoint: `${TEST_HOST}jobs/123.json`,
- pagePath: `${TEST_HOST}jobs/123`,
- logState:
- 'eyJvZmZzZXQiOjE3NDUxLCJuX29wZW5fdGFncyI6MCwiZmdfY29sb3IiOm51bGwsImJnX2NvbG9yIjpudWxsLCJzdHlsZV9tYXNrIjowfQ%3D%3D',
- };
-
- const props = {
- artifactHelpUrl: 'help/artifact',
- deploymentHelpUrl: 'help/deployment',
- runnerSettingsUrl: 'settings/ci-cd/runners',
- terminalPath: 'jobs/123/terminal',
- projectPath: 'user-name/project-name',
- subscriptionsMoreMinutesUrl: 'https://customers.gitlab.com/buy_pipeline_minutes',
- };
-
- const createComponent = () => {
- wrapper = mount(JobApp, { propsData: { ...props }, store });
- };
-
- const setupAndMount = async ({ jobData = {}, jobLogData = {} } = {}) => {
- mock.onGet(initSettings.endpoint).replyOnce(200, { ...job, ...jobData });
- mock.onGet(`${initSettings.pagePath}/trace.json`).reply(200, jobLogData);
-
- const asyncInit = store.dispatch('init', initSettings);
-
- createComponent();
-
- await asyncInit;
- jest.runOnlyPendingTimers();
- await axios.waitForAll();
- await nextTick();
- };
-
- const findLoadingComponent = () => wrapper.find(GlLoadingIcon);
- const findSidebar = () => wrapper.find(Sidebar);
- const findJobContent = () => wrapper.find('[data-testid="job-content"');
- const findStuckBlockComponent = () => wrapper.find(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.find(UnmetPrerequisitesBlock);
- const findEnvironmentsBlockComponent = () => wrapper.find(EnvironmentsBlock);
- const findErasedBlock = () => wrapper.find(ErasedBlock);
- const findArchivedJob = () => wrapper.find('[data-testid="archived-job"]');
- const findEmptyState = () => wrapper.find(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"]');
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- store = createStore();
- });
-
- afterEach(() => {
- wrapper.destroy();
- mock.restore();
- });
-
- describe('while loading', () => {
- beforeEach(() => {
- store.state.isLoading = true;
- createComponent();
- });
-
- it('renders loading icon', () => {
- expect(findLoadingComponent().exists()).toBe(true);
- expect(findSidebar().exists()).toBe(false);
- expect(findJobContent().exists()).toBe(false);
- });
- });
-
- describe('with successful request', () => {
- describe('Header section', () => {
- describe('job callout message', () => {
- it('should not render the reason when reason is absent', () =>
- setupAndMount().then(() => {
- expect(wrapper.vm.shouldRenderCalloutMessage).toBe(false);
- }));
-
- it('should render the reason when reason is present', () =>
- setupAndMount({
- jobData: {
- callout_message: 'There is an unkown failure, please try again',
- },
- }).then(() => {
- 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', () => {
- describe('without active runners available', () => {
- it('renders 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,
- },
- tags: [],
- },
- }).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', () =>
- setupAndMount({
- jobData: {
- runners: { available: true },
- },
- }).then(() => {
- expect(findStuckBlockComponent().exists()).toBe(false);
- }));
- });
-
- describe('unmet prerequisites block', () => {
- it('renders unmet prerequisites block when there is an unmet prerequisites failure', () =>
- setupAndMount({
- jobData: {
- status: {
- group: 'failed',
- icon: 'status_failed',
- label: 'failed',
- text: 'failed',
- details_path: 'path',
- illustration: {
- content: 'Retry this job in order to create the necessary resources.',
- image: 'path',
- size: 'svg-430',
- title: 'Failed to create resources',
- },
- },
- failure_reason: 'unmet_prerequisites',
- has_trace: false,
- runners: {
- available: true,
- },
- tags: [],
- },
- }).then(() => {
- expect(findFailedJobComponent().exists()).toBe(true);
- }));
- });
-
- describe('environments block', () => {
- it('renders environment block when job has environment', () =>
- setupAndMount({
- jobData: {
- deployment_status: {
- environment: {
- environment_path: '/path',
- name: 'foo',
- },
- },
- },
- }).then(() => {
- expect(findEnvironmentsBlockComponent().exists()).toBe(true);
- }));
-
- it('does not render environment block when job has environment', () =>
- setupAndMount().then(() => {
- expect(findEnvironmentsBlockComponent().exists()).toBe(false);
- }));
- });
-
- describe('erased block', () => {
- it('renders erased block when `erased` is true', () =>
- setupAndMount({
- jobData: {
- erased_by: {
- username: 'root',
- web_url: 'gitlab.com/root',
- },
- erased_at: '2016-11-07T11:11:16.525Z',
- },
- }).then(() => {
- expect(findErasedBlock().exists()).toBe(true);
- }));
-
- it('does not render erased block when `erased` is false', () =>
- setupAndMount({
- jobData: {
- erased_at: null,
- },
- }).then(() => {
- expect(findErasedBlock().exists()).toBe(false);
- }));
- });
-
- describe('empty states block', () => {
- it('renders empty state when job does not have log and is not running', () =>
- setupAndMount({
- jobData: {
- has_trace: false,
- status: {
- group: 'pending',
- icon: 'status_pending',
- label: 'pending',
- text: 'pending',
- details_path: 'path',
- illustration: {
- image: 'path',
- size: '340',
- title: 'Empty State',
- content: 'This is an empty state',
- },
- action: {
- button_title: 'Retry job',
- method: 'post',
- path: '/path',
- },
- },
- },
- }).then(() => {
- expect(findEmptyState().exists()).toBe(true);
- }));
-
- it('does not render empty state when job does not have log but it is running', () =>
- setupAndMount({
- jobData: {
- has_trace: false,
- status: {
- group: 'running',
- icon: 'status_running',
- label: 'running',
- text: 'running',
- details_path: 'path',
- },
- },
- }).then(() => {
- expect(findEmptyState().exists()).toBe(false);
- }));
-
- it('does not render empty state when job has log but it is not running', () =>
- 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,
- },
- });
-
- const blocks = wrapper.findAll('.blocks-container > *').wrappers;
- expect(blocks.length).toBeGreaterThan(0);
-
- blocks.forEach((block) => {
- expect(block.text().trim()).not.toBe('');
- });
- });
- });
- });
-
- describe('archived job', () => {
- beforeEach(() => setupAndMount({ jobData: { archived: true } }));
-
- it('renders warning about job being archived', () => {
- expect(findArchivedJob().exists()).toBe(true);
- });
- });
-
- describe('non-archived job', () => {
- beforeEach(() => setupAndMount());
-
- it('does not warning about job being archived', () => {
- expect(findArchivedJob().exists()).toBe(false);
- });
- });
-
- 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);
- });
-
- it('should render link to raw ouput', () => {
- expect(findJobLogController().exists()).toBe(true);
- });
-
- it('should render link to erase job', () => {
- expect(findJobLogEraseLink().exists()).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/jobs/components/job_container_item_spec.js b/spec/frontend/jobs/components/job_container_item_spec.js
deleted file mode 100644
index eb2b0184e5f..00000000000
--- a/spec/frontend/jobs/components/job_container_item_spec.js
+++ /dev/null
@@ -1,98 +0,0 @@
-import { GlIcon, GlLink } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import delayedJobFixture from 'test_fixtures/jobs/delayed.json';
-import JobContainerItem from '~/jobs/components/job_container_item.vue';
-import CiIcon from '~/vue_shared/components/ci_icon.vue';
-import job from '../mock_data';
-
-describe('JobContainerItem', () => {
- let wrapper;
-
- const findCiIconComponent = () => wrapper.findComponent(CiIcon);
- const findGlIconComponent = () => wrapper.findComponent(GlIcon);
-
- function createComponent(jobData = {}, props = { isActive: false, retried: false }) {
- wrapper = shallowMount(JobContainerItem, {
- propsData: {
- job: {
- ...jobData,
- retried: props.retried,
- },
- isActive: props.isActive,
- },
- });
- }
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- describe('when a job is not active and not retried', () => {
- beforeEach(() => {
- createComponent(job);
- });
-
- it('displays a status icon', () => {
- const ciIcon = findCiIconComponent();
-
- expect(ciIcon.props('status')).toBe(job.status);
- });
-
- it('displays the job name', () => {
- expect(wrapper.text()).toContain(job.name);
- });
-
- it('displays a link to the job', () => {
- const link = wrapper.findComponent(GlLink);
-
- expect(link.attributes('href')).toBe(job.status.details_path);
- });
- });
-
- describe('when a job is active', () => {
- beforeEach(() => {
- createComponent(job, { isActive: true });
- });
-
- it('displays an arrow sprite icon', () => {
- const icon = findGlIconComponent();
-
- expect(icon.props('name')).toBe('arrow-right');
- });
- });
-
- describe('when a job is retried', () => {
- beforeEach(() => {
- createComponent(job, { isActive: false, retried: true });
- });
-
- it('displays a retry icon', () => {
- const icon = findGlIconComponent();
-
- expect(icon.props('name')).toBe('retry');
- });
- });
-
- describe('for a delayed job', () => {
- beforeEach(() => {
- const remainingMilliseconds = 1337000;
- jest
- .spyOn(Date, 'now')
- .mockImplementation(
- () => new Date(delayedJobFixture.scheduled_at).getTime() - remainingMilliseconds,
- );
-
- createComponent(delayedJobFixture);
- });
-
- it('displays remaining time in tooltip', async () => {
- await nextTick();
-
- const link = wrapper.findComponent(GlLink);
-
- expect(link.attributes('title')).toMatch('delayed job - delayed manual action (00:22:17)');
- });
- });
-});
diff --git a/spec/frontend/jobs/components/job_log_controllers_spec.js b/spec/frontend/jobs/components/job_log_controllers_spec.js
deleted file mode 100644
index aa85253a177..00000000000
--- a/spec/frontend/jobs/components/job_log_controllers_spec.js
+++ /dev/null
@@ -1,315 +0,0 @@
-import { GlSearchBoxByClick } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import JobLogControllers from '~/jobs/components/job_log_controllers.vue';
-import HelpPopover from '~/vue_shared/components/help_popover.vue';
-import { backoffMockImplementation } from 'helpers/backoff_helper';
-import * as commonUtils from '~/lib/utils/common_utils';
-import { mockJobLog } from '../mock_data';
-
-const mockToastShow = jest.fn();
-
-describe('Job log controllers', () => {
- let wrapper;
-
- beforeEach(() => {
- jest.spyOn(commonUtils, 'backOff').mockImplementation(backoffMockImplementation);
- });
-
- afterEach(() => {
- if (wrapper?.destroy) {
- wrapper.destroy();
- }
- commonUtils.backOff.mockReset();
- });
-
- const defaultProps = {
- rawPath: '/raw',
- erasePath: '/erase',
- size: 511952,
- isScrollTopDisabled: false,
- isScrollBottomDisabled: false,
- isScrollingDown: true,
- isJobLogSizeVisible: true,
- isComplete: true,
- jobLog: mockJobLog,
- };
-
- const createWrapper = (props, { jobLogJumpToFailures = false } = {}) => {
- wrapper = mount(JobLogControllers, {
- propsData: {
- ...defaultProps,
- ...props,
- },
- provide: {
- glFeatures: {
- jobLogJumpToFailures,
- },
- },
- data() {
- return {
- searchTerm: '82',
- };
- },
- mocks: {
- $toast: {
- show: mockToastShow,
- },
- },
- });
- };
-
- const findTruncatedInfo = () => wrapper.find('[data-testid="log-truncated-info"]');
- const findRawLink = () => wrapper.find('[data-testid="raw-link"]');
- const findRawLinkController = () => wrapper.find('[data-testid="job-raw-link-controller"]');
- const findScrollTop = () => wrapper.find('[data-testid="job-controller-scroll-top"]');
- const findScrollBottom = () => wrapper.find('[data-testid="job-controller-scroll-bottom"]');
- const findJobLogSearch = () => wrapper.findComponent(GlSearchBoxByClick);
- const findSearchHelp = () => wrapper.findComponent(HelpPopover);
- const findScrollFailure = () => wrapper.find('[data-testid="job-controller-scroll-to-failure"]');
-
- describe('Truncate information', () => {
- describe('with isJobLogSizeVisible', () => {
- beforeEach(() => {
- createWrapper();
- });
-
- it('renders size information', () => {
- expect(findTruncatedInfo().text()).toMatch('499.95 KiB');
- });
-
- it('renders link to raw job log', () => {
- expect(findRawLink().attributes('href')).toBe(defaultProps.rawPath);
- });
- });
- });
-
- describe('links section', () => {
- describe('with raw job log path', () => {
- beforeEach(() => {
- createWrapper();
- });
-
- it('renders raw job log link', () => {
- expect(findRawLinkController().attributes('href')).toBe(defaultProps.rawPath);
- });
- });
-
- describe('without raw job log path', () => {
- beforeEach(() => {
- createWrapper({
- rawPath: null,
- });
- });
-
- it('does not render raw job log link', () => {
- expect(findRawLinkController().exists()).toBe(false);
- });
- });
- });
-
- describe('scroll buttons', () => {
- describe('scroll top button', () => {
- describe('when user can scroll top', () => {
- beforeEach(() => {
- createWrapper({
- isScrollTopDisabled: false,
- });
- });
-
- it('emits scrollJobLogTop event on click', async () => {
- await findScrollTop().trigger('click');
-
- expect(wrapper.emitted().scrollJobLogTop).toHaveLength(1);
- });
- });
-
- describe('when user can not scroll top', () => {
- beforeEach(() => {
- createWrapper({
- isScrollTopDisabled: true,
- isScrollBottomDisabled: false,
- isScrollingDown: false,
- });
- });
-
- it('renders disabled scroll top button', () => {
- expect(findScrollTop().attributes('disabled')).toBe('disabled');
- });
-
- it('does not emit scrollJobLogTop event on click', async () => {
- await findScrollTop().trigger('click');
-
- expect(wrapper.emitted().scrollJobLogTop).toBeUndefined();
- });
- });
- });
-
- describe('scroll bottom button', () => {
- describe('when user can scroll bottom', () => {
- beforeEach(() => {
- createWrapper();
- });
-
- it('emits scrollJobLogBottom event on click', async () => {
- await findScrollBottom().trigger('click');
-
- expect(wrapper.emitted().scrollJobLogBottom).toHaveLength(1);
- });
- });
-
- describe('when user can not scroll bottom', () => {
- beforeEach(() => {
- createWrapper({
- isScrollTopDisabled: false,
- isScrollBottomDisabled: true,
- isScrollingDown: false,
- });
- });
-
- it('renders disabled scroll bottom button', () => {
- expect(findScrollBottom().attributes('disabled')).toEqual('disabled');
- });
-
- it('does not emit scrollJobLogBottom event on click', async () => {
- await findScrollBottom().trigger('click');
-
- expect(wrapper.emitted().scrollJobLogBottom).toBeUndefined();
- });
- });
-
- describe('while isScrollingDown is true', () => {
- beforeEach(() => {
- createWrapper();
- });
-
- it('renders animate class for the scroll down button', () => {
- expect(findScrollBottom().classes()).toContain('animate');
- });
- });
-
- describe('while isScrollingDown is false', () => {
- beforeEach(() => {
- createWrapper({
- isScrollTopDisabled: true,
- isScrollBottomDisabled: false,
- isScrollingDown: false,
- });
- });
-
- it('does not render animate class for the scroll down button', () => {
- expect(findScrollBottom().classes()).not.toContain('animate');
- });
- });
- });
-
- describe('scroll to failure button', () => {
- describe('with feature flag disabled', () => {
- it('does not display button', () => {
- createWrapper();
-
- expect(findScrollFailure().exists()).toBe(false);
- });
- });
-
- describe('with red text failures on the page', () => {
- let firstFailure;
- let secondFailure;
-
- beforeEach(() => {
- jest.spyOn(document, 'querySelectorAll').mockReturnValueOnce(['mock-element']);
-
- createWrapper({}, { jobLogJumpToFailures: true });
-
- firstFailure = document.createElement('div');
- firstFailure.className = 'term-fg-l-red';
- document.body.appendChild(firstFailure);
-
- secondFailure = document.createElement('div');
- secondFailure.className = 'term-fg-l-red';
- document.body.appendChild(secondFailure);
- });
-
- afterEach(() => {
- if (firstFailure) {
- firstFailure.remove();
- firstFailure = null;
- }
-
- if (secondFailure) {
- secondFailure.remove();
- secondFailure = null;
- }
- });
-
- it('is enabled', () => {
- expect(findScrollFailure().props('disabled')).toBe(false);
- });
-
- it('scrolls to each failure', async () => {
- jest.spyOn(firstFailure, 'scrollIntoView');
-
- await findScrollFailure().trigger('click');
-
- expect(firstFailure.scrollIntoView).toHaveBeenCalled();
-
- await findScrollFailure().trigger('click');
-
- expect(secondFailure.scrollIntoView).toHaveBeenCalled();
-
- await findScrollFailure().trigger('click');
-
- expect(firstFailure.scrollIntoView).toHaveBeenCalled();
- });
- });
-
- describe('with no red text failures on the page', () => {
- beforeEach(() => {
- jest.spyOn(document, 'querySelectorAll').mockReturnValueOnce([]);
-
- createWrapper({}, { jobLogJumpToFailures: true });
- });
-
- it('is disabled', () => {
- expect(findScrollFailure().props('disabled')).toBe(true);
- });
- });
-
- describe('when the job log is not complete', () => {
- beforeEach(() => {
- jest.spyOn(document, 'querySelectorAll').mockReturnValueOnce(['mock-element']);
-
- createWrapper({ isComplete: false }, { jobLogJumpToFailures: true });
- });
-
- it('is enabled', () => {
- expect(findScrollFailure().props('disabled')).toBe(false);
- });
- });
- });
- });
-
- describe('Job log search', () => {
- beforeEach(() => {
- createWrapper();
- });
-
- it('displays job log search', () => {
- expect(findJobLogSearch().exists()).toBe(true);
- expect(findSearchHelp().exists()).toBe(true);
- });
-
- it('emits search results', () => {
- const expectedSearchResults = [[[mockJobLog[6].lines[1], mockJobLog[6].lines[2]]]];
-
- findJobLogSearch().vm.$emit('submit');
-
- expect(wrapper.emitted('searchResults')).toEqual(expectedSearchResults);
- });
-
- it('clears search results', () => {
- findJobLogSearch().vm.$emit('clear');
-
- expect(wrapper.emitted('searchResults')).toEqual([[[]]]);
- });
- });
-});
diff --git a/spec/frontend/jobs/components/job_retry_forward_deployment_modal_spec.js b/spec/frontend/jobs/components/job_retry_forward_deployment_modal_spec.js
deleted file mode 100644
index 08973223c08..00000000000
--- a/spec/frontend/jobs/components/job_retry_forward_deployment_modal_spec.js
+++ /dev/null
@@ -1,76 +0,0 @@
-import { GlLink, GlModal } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import JobRetryForwardDeploymentModal from '~/jobs/components/job_retry_forward_deployment_modal.vue';
-import { JOB_RETRY_FORWARD_DEPLOYMENT_MODAL } from '~/jobs/constants';
-import createStore from '~/jobs/store';
-import job from '../mock_data';
-
-describe('Job Retry Forward Deployment Modal', () => {
- let store;
- let wrapper;
-
- const retryOutdatedJobDocsUrl = 'url-to-docs';
- const findLink = () => wrapper.find(GlLink);
- const findModal = () => wrapper.find(GlModal);
-
- const createWrapper = ({ props = {}, provide = {}, stubs = {} } = {}) => {
- store = createStore();
- wrapper = shallowMount(JobRetryForwardDeploymentModal, {
- propsData: {
- modalId: 'modal-id',
- href: job.retry_path,
- ...props,
- },
- provide,
- store,
- stubs,
- });
- };
-
- afterEach(() => {
- if (wrapper) {
- wrapper.destroy();
- wrapper = null;
- }
- });
-
- beforeEach(createWrapper);
-
- describe('Modal configuration', () => {
- it('should display the correct messages', () => {
- const modal = findModal();
- expect(modal.attributes('title')).toMatch(JOB_RETRY_FORWARD_DEPLOYMENT_MODAL.title);
- expect(modal.text()).toMatch(JOB_RETRY_FORWARD_DEPLOYMENT_MODAL.info);
- expect(modal.text()).toMatch(JOB_RETRY_FORWARD_DEPLOYMENT_MODAL.areYouSure);
- });
- });
-
- describe('Modal docs help link', () => {
- it('should not display an info link when none is provided', () => {
- createWrapper();
-
- expect(findLink().exists()).toBe(false);
- });
-
- it('should display an info link when one is provided', () => {
- createWrapper({ provide: { retryOutdatedJobDocsUrl } });
-
- expect(findLink().attributes('href')).toBe(retryOutdatedJobDocsUrl);
- expect(findLink().text()).toMatch(JOB_RETRY_FORWARD_DEPLOYMENT_MODAL.moreInfo);
- });
- });
-
- describe('Modal actions', () => {
- beforeEach(createWrapper);
-
- it('should correctly configure the primary action', () => {
- expect(findModal().props('actionPrimary').attributes).toMatchObject([
- {
- 'data-method': 'post',
- href: job.retry_path,
- variant: 'danger',
- },
- ]);
- });
- });
-});
diff --git a/spec/frontend/jobs/components/job_sidebar_details_container_spec.js b/spec/frontend/jobs/components/job_sidebar_details_container_spec.js
deleted file mode 100644
index 4046f0269dd..00000000000
--- a/spec/frontend/jobs/components/job_sidebar_details_container_spec.js
+++ /dev/null
@@ -1,140 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import DetailRow from '~/jobs/components/sidebar_detail_row.vue';
-import SidebarJobDetailsContainer from '~/jobs/components/sidebar_job_details_container.vue';
-import createStore from '~/jobs/store';
-import job from '../mock_data';
-
-describe('Job Sidebar Details Container', () => {
- let store;
- let wrapper;
-
- const findJobTimeout = () => wrapper.findByTestId('job-timeout');
- const findJobTags = () => wrapper.findByTestId('job-tags');
- const findAllDetailsRow = () => wrapper.findAll(DetailRow);
-
- const createWrapper = ({ props = {} } = {}) => {
- store = createStore();
- wrapper = extendedWrapper(
- shallowMount(SidebarJobDetailsContainer, {
- propsData: props,
- store,
- stubs: {
- DetailRow,
- },
- }),
- );
- };
-
- afterEach(() => {
- if (wrapper) {
- wrapper.destroy();
- wrapper = null;
- }
- });
-
- describe('when no details are available', () => {
- beforeEach(() => {
- createWrapper();
- });
-
- it('should render an empty container', () => {
- expect(wrapper.html()).toBe('');
- });
-
- it.each(['duration', 'erased_at', 'finished_at', 'queued_at', 'runner', 'coverage'])(
- 'should not render %s details when missing',
- async (detail) => {
- await store.dispatch('receiveJobSuccess', { [detail]: undefined });
-
- expect(findAllDetailsRow()).toHaveLength(0);
- },
- );
- });
-
- describe('when some of the details are available', () => {
- beforeEach(createWrapper);
-
- it.each([
- ['duration', 'Elapsed time: 6 seconds'],
- ['erased_at', 'Erased: 3 weeks ago'],
- ['finished_at', 'Finished: 3 weeks ago'],
- ['queued_duration', 'Queued: 9 seconds'],
- ['runner', 'Runner: #1 (ABCDEFGH) local ci runner'],
- ['coverage', 'Coverage: 20%'],
- ])('uses %s to render job-%s', async (detail, value) => {
- await store.dispatch('receiveJobSuccess', { [detail]: job[detail] });
- const detailsRow = findAllDetailsRow();
-
- expect(detailsRow).toHaveLength(1);
- expect(detailsRow.at(0).text()).toBe(value);
- });
-
- it('only renders tags', async () => {
- const { tags } = job;
- await store.dispatch('receiveJobSuccess', { tags });
- const tagsComponent = findJobTags();
-
- expect(tagsComponent.text()).toBe('Tags: tag');
- });
- });
-
- describe('when all the info are available', () => {
- it('renders all the details components', async () => {
- createWrapper();
- await store.dispatch('receiveJobSuccess', job);
-
- expect(findAllDetailsRow()).toHaveLength(7);
- });
-
- describe('duration row', () => {
- it('renders all the details components', async () => {
- createWrapper();
- await store.dispatch('receiveJobSuccess', job);
-
- expect(findAllDetailsRow().at(0).text()).toBe('Duration: 6 seconds');
- });
- });
- });
-
- describe('timeout', () => {
- const {
- metadata: { timeout_human_readable, timeout_source },
- } = job;
-
- beforeEach(createWrapper);
-
- it('does not render if metadata is empty', async () => {
- const metadata = {};
- await store.dispatch('receiveJobSuccess', { metadata });
- const detailsRow = findAllDetailsRow();
-
- expect(wrapper.html()).toBe('');
- expect(detailsRow.exists()).toBe(false);
- });
-
- it('uses metadata to render timeout', async () => {
- const metadata = { timeout_human_readable };
- await store.dispatch('receiveJobSuccess', { metadata });
- const detailsRow = findAllDetailsRow();
-
- expect(detailsRow).toHaveLength(1);
- expect(detailsRow.at(0).text()).toBe('Timeout: 1m 40s');
- });
-
- it('uses metadata to render timeout and the source', async () => {
- const metadata = { timeout_human_readable, timeout_source };
- await store.dispatch('receiveJobSuccess', { metadata });
- const detailsRow = findAllDetailsRow();
-
- expect(detailsRow.at(0).text()).toBe('Timeout: 1m 40s (from runner)');
- });
-
- it('should not render when no time is provided', async () => {
- const metadata = { timeout_source };
- await store.dispatch('receiveJobSuccess', { metadata });
-
- expect(findJobTimeout().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/jobs/components/job_sidebar_retry_button_spec.js b/spec/frontend/jobs/components/job_sidebar_retry_button_spec.js
deleted file mode 100644
index ad72b9be261..00000000000
--- a/spec/frontend/jobs/components/job_sidebar_retry_button_spec.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import JobsSidebarRetryButton from '~/jobs/components/job_sidebar_retry_button.vue';
-import createStore from '~/jobs/store';
-import job from '../mock_data';
-
-describe('Job Sidebar Retry Button', () => {
- let store;
- let wrapper;
-
- const forwardDeploymentFailure = 'forward_deployment_failure';
- const findRetryButton = () => wrapper.findByTestId('retry-job-button');
- const findRetryLink = () => wrapper.findByTestId('retry-job-link');
-
- const createWrapper = ({ props = {} } = {}) => {
- store = createStore();
- wrapper = shallowMountExtended(JobsSidebarRetryButton, {
- propsData: {
- href: job.retry_path,
- modalId: 'modal-id',
- ...props,
- },
- store,
- });
- };
-
- afterEach(() => {
- if (wrapper) {
- wrapper.destroy();
- }
- });
-
- beforeEach(createWrapper);
-
- it.each([
- [null, false, true],
- ['unmet_prerequisites', false, true],
- [forwardDeploymentFailure, true, false],
- ])(
- 'when error is: %s, should render button: %s | should render link: %s',
- async (failureReason, buttonExists, linkExists) => {
- await store.dispatch('receiveJobSuccess', { ...job, failure_reason: failureReason });
-
- expect(findRetryButton().exists()).toBe(buttonExists);
- expect(findRetryLink().exists()).toBe(linkExists);
- },
- );
-
- describe('Button', () => {
- it('should have the correct configuration', async () => {
- await store.dispatch('receiveJobSuccess', { failure_reason: forwardDeploymentFailure });
-
- expect(findRetryButton().attributes()).toMatchObject({
- category: 'primary',
- variant: 'confirm',
- icon: 'retry',
- });
- });
- });
-
- describe('Link', () => {
- it('should have the correct configuration', () => {
- expect(findRetryLink().attributes()).toMatchObject({
- 'data-method': 'post',
- href: job.retry_path,
- icon: 'retry',
- });
- });
- });
-});
diff --git a/spec/frontend/jobs/components/jobs_container_spec.js b/spec/frontend/jobs/components/jobs_container_spec.js
deleted file mode 100644
index 127570b8184..00000000000
--- a/spec/frontend/jobs/components/jobs_container_spec.js
+++ /dev/null
@@ -1,147 +0,0 @@
-import { GlLink } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import JobsContainer from '~/jobs/components/jobs_container.vue';
-
-describe('Jobs List block', () => {
- let wrapper;
-
- const retried = {
- status: {
- details_path: '/gitlab-org/gitlab-foss/pipelines/28029444',
- group: 'success',
- has_details: true,
- icon: 'status_success',
- label: 'passed',
- text: 'passed',
- tooltip: 'passed',
- },
- id: 233432756,
- tooltip: 'build - passed',
- retried: true,
- };
-
- const active = {
- name: 'test',
- status: {
- details_path: '/gitlab-org/gitlab-foss/pipelines/28029444',
- group: 'success',
- has_details: true,
- icon: 'status_success',
- label: 'passed',
- text: 'passed',
- tooltip: 'passed',
- },
- id: 2322756,
- tooltip: 'build - passed',
- active: true,
- };
-
- const job = {
- name: 'build',
- status: {
- details_path: '/gitlab-org/gitlab-foss/pipelines/28029444',
- group: 'success',
- has_details: true,
- icon: 'status_success',
- label: 'passed',
- text: 'passed',
- tooltip: 'passed',
- },
- id: 232153,
- tooltip: 'build - passed',
- };
-
- const findAllJobs = () => wrapper.findAllComponents(GlLink);
- const findJob = () => findAllJobs().at(0);
-
- const findArrowIcon = () => wrapper.findByTestId('arrow-right-icon');
- const findRetryIcon = () => wrapper.findByTestId('retry-icon');
-
- const createComponent = (props) => {
- wrapper = extendedWrapper(
- mount(JobsContainer, {
- propsData: {
- ...props,
- },
- }),
- );
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders a list of jobs', () => {
- createComponent({
- jobs: [job, retried, active],
- jobId: 12313,
- });
-
- expect(findAllJobs()).toHaveLength(3);
- });
-
- it('renders the arrow right icon when job id matches `jobId`', () => {
- createComponent({
- jobs: [active],
- jobId: active.id,
- });
-
- expect(findArrowIcon().exists()).toBe(true);
- });
-
- it('does not render the arrow right icon when the job is not active', () => {
- createComponent({
- jobs: [job],
- jobId: active.id,
- });
-
- expect(findArrowIcon().exists()).toBe(false);
- });
-
- it('renders the job name when present', () => {
- createComponent({
- jobs: [job],
- jobId: active.id,
- });
-
- expect(findJob().text()).toBe(job.name);
- expect(findJob().text()).not.toContain(job.id.toString());
- });
-
- it('renders job id when job name is not available', () => {
- createComponent({
- jobs: [retried],
- jobId: active.id,
- });
-
- expect(findJob().text()).toBe(retried.id.toString());
- });
-
- it('links to the job page', () => {
- createComponent({
- jobs: [job],
- jobId: active.id,
- });
-
- expect(findJob().attributes('href')).toBe(job.status.details_path);
- });
-
- it('renders retry icon when job was retried', () => {
- createComponent({
- jobs: [retried],
- jobId: active.id,
- });
-
- expect(findRetryIcon().exists()).toBe(true);
- });
-
- it('does not render retry icon when job was not retried', () => {
- createComponent({
- jobs: [job],
- jobId: active.id,
- });
-
- expect(findRetryIcon().exists()).toBe(false);
- });
-});
diff --git a/spec/frontend/jobs/components/log/line_header_spec.js b/spec/frontend/jobs/components/log/line_header_spec.js
index bdc8ae0eef0..ec8e79bba13 100644
--- a/spec/frontend/jobs/components/log/line_header_spec.js
+++ b/spec/frontend/jobs/components/log/line_header_spec.js
@@ -39,7 +39,7 @@ describe('Job Log Header Line', () => {
});
it('renders the line number component', () => {
- expect(wrapper.find(LineNumber).exists()).toBe(true);
+ expect(wrapper.findComponent(LineNumber).exists()).toBe(true);
});
it('renders a span the provided text', () => {
@@ -90,7 +90,7 @@ describe('Job Log Header Line', () => {
});
it('renders the duration badge', () => {
- expect(wrapper.find(DurationBadge).exists()).toBe(true);
+ expect(wrapper.findComponent(DurationBadge).exists()).toBe(true);
});
});
});
diff --git a/spec/frontend/jobs/components/log/line_spec.js b/spec/frontend/jobs/components/log/line_spec.js
index bf80d90e299..50ebd1610d2 100644
--- a/spec/frontend/jobs/components/log/line_spec.js
+++ b/spec/frontend/jobs/components/log/line_spec.js
@@ -42,7 +42,7 @@ describe('Job Log Line', () => {
});
it('renders the line number component', () => {
- expect(wrapper.find(LineNumber).exists()).toBe(true);
+ expect(wrapper.findComponent(LineNumber).exists()).toBe(true);
});
it('renders a span the provided text', () => {
diff --git a/spec/frontend/jobs/components/manual_variables_form_spec.js b/spec/frontend/jobs/components/manual_variables_form_spec.js
deleted file mode 100644
index 6faab3ddf31..00000000000
--- a/spec/frontend/jobs/components/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 ManualVariablesForm from '~/jobs/components/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(ManualVariablesForm, {
- 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/sidebar_detail_row_spec.js b/spec/frontend/jobs/components/sidebar_detail_row_spec.js
deleted file mode 100644
index 8d2680608ab..00000000000
--- a/spec/frontend/jobs/components/sidebar_detail_row_spec.js
+++ /dev/null
@@ -1,55 +0,0 @@
-import { GlLink } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import SidebarDetailRow from '~/jobs/components/sidebar_detail_row.vue';
-
-describe('Sidebar detail row', () => {
- let wrapper;
-
- const title = 'this is the title';
- const value = 'this is the value';
- const helpUrl = 'https://docs.gitlab.com/runner/register/index.html';
-
- const findHelpLink = () => wrapper.findComponent(GlLink);
-
- const createComponent = (props) => {
- wrapper = shallowMount(SidebarDetailRow, {
- propsData: {
- ...props,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- describe('with title/value and without helpUrl', () => {
- beforeEach(() => {
- createComponent({ title, value });
- });
-
- it('should render the provided title and value', () => {
- expect(wrapper.text()).toBe(`${title}: ${value}`);
- });
-
- it('should not render the help link', () => {
- expect(findHelpLink().exists()).toBe(false);
- });
- });
-
- describe('when helpUrl provided', () => {
- beforeEach(() => {
- createComponent({
- helpUrl,
- title,
- value,
- });
- });
-
- it('should render the help link', () => {
- expect(findHelpLink().exists()).toBe(true);
- expect(findHelpLink().attributes('href')).toBe(helpUrl);
- });
- });
-});
diff --git a/spec/frontend/jobs/components/sidebar_spec.js b/spec/frontend/jobs/components/sidebar_spec.js
deleted file mode 100644
index 39c71986ce4..00000000000
--- a/spec/frontend/jobs/components/sidebar_spec.js
+++ /dev/null
@@ -1,227 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import ArtifactsBlock from '~/jobs/components/artifacts_block.vue';
-import JobRetryForwardDeploymentModal from '~/jobs/components/job_retry_forward_deployment_modal.vue';
-import JobRetryButton from '~/jobs/components/job_sidebar_retry_button.vue';
-import JobsContainer from '~/jobs/components/jobs_container.vue';
-import Sidebar, { forwardDeploymentFailureModalId } from '~/jobs/components/sidebar.vue';
-import StagesDropdown from '~/jobs/components/stages_dropdown.vue';
-import createStore from '~/jobs/store';
-import job, { jobsInStage } from '../mock_data';
-
-describe('Sidebar details block', () => {
- let store;
- let wrapper;
-
- const forwardDeploymentFailure = 'forward_deployment_failure';
- const findModal = () => wrapper.find(JobRetryForwardDeploymentModal);
- const findArtifactsBlock = () => wrapper.findComponent(ArtifactsBlock);
- const findCancelButton = () => wrapper.findByTestId('cancel-button');
- const findNewIssueButton = () => wrapper.findByTestId('job-new-issue');
- const findRetryButton = () => wrapper.find(JobRetryButton);
- const findTerminalLink = () => wrapper.findByTestId('terminal-link');
- const findEraseLink = () => wrapper.findByTestId('job-log-erase-link');
-
- const createWrapper = (props) => {
- store = createStore();
-
- store.state.job = job;
-
- wrapper = extendedWrapper(
- shallowMount(Sidebar, {
- propsData: {
- ...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 there is no retry path retry', () => {
- it('should not render a retry button', async () => {
- createWrapper();
- const copy = { ...job, retry_path: null };
- await store.dispatch('receiveJobSuccess', copy);
-
- expect(findRetryButton().exists()).toBe(false);
- });
- });
-
- describe('without terminal path', () => {
- it('does not render terminal link', async () => {
- createWrapper();
- await store.dispatch('receiveJobSuccess', job);
-
- expect(findTerminalLink().exists()).toBe(false);
- });
- });
-
- describe('with terminal path', () => {
- it('renders terminal link', async () => {
- createWrapper();
- await store.dispatch('receiveJobSuccess', { ...job, terminal_path: 'job/43123/terminal' });
-
- expect(findTerminalLink().exists()).toBe(true);
- });
- });
-
- describe('actions', () => {
- beforeEach(() => {
- createWrapper();
- return store.dispatch('receiveJobSuccess', job);
- });
-
- it('should render link to new issue', () => {
- expect(findNewIssueButton().attributes('href')).toBe(job.new_issue_path);
- expect(findNewIssueButton().text()).toBe('New issue');
- });
-
- it('should render the retry button', () => {
- expect(findRetryButton().props('href')).toBe(job.retry_path);
- });
-
- it('should render link to cancel job', () => {
- expect(findCancelButton().props('icon')).toBe('cancel');
- expect(findCancelButton().attributes('href')).toBe(job.cancel_path);
- });
- });
-
- describe('forward deployment failure', () => {
- describe('when the relevant data is missing', () => {
- it.each`
- retryPath | failureReason
- ${null} | ${null}
- ${''} | ${''}
- ${job.retry_path} | ${''}
- ${''} | ${forwardDeploymentFailure}
- ${job.retry_path} | ${'unmet_prerequisites'}
- `(
- 'should not render the modal when path and failure are $retryPath, $failureReason',
- async ({ retryPath, failureReason }) => {
- createWrapper();
- await store.dispatch('receiveJobSuccess', {
- ...job,
- failure_reason: failureReason,
- retry_path: retryPath,
- });
- expect(findModal().exists()).toBe(false);
- },
- );
- });
-
- describe('when there is the relevant error', () => {
- beforeEach(() => {
- createWrapper();
- return store.dispatch('receiveJobSuccess', {
- ...job,
- failure_reason: forwardDeploymentFailure,
- });
- });
-
- it('should render the modal', () => {
- expect(findModal().exists()).toBe(true);
- });
-
- it('should provide the modal id to the button and modal', () => {
- expect(findRetryButton().props('modalId')).toBe(forwardDeploymentFailureModalId);
- expect(findModal().props('modalId')).toBe(forwardDeploymentFailureModalId);
- });
-
- it('should provide the retry path to the button and modal', () => {
- expect(findRetryButton().props('href')).toBe(job.retry_path);
- expect(findModal().props('href')).toBe(job.retry_path);
- });
- });
- });
-
- describe('stages dropdown', () => {
- beforeEach(() => {
- createWrapper();
- return store.dispatch('receiveJobSuccess', { ...job, stage: 'aStage' });
- });
-
- describe('with stages', () => {
- it('renders value provided as selectedStage as selected', () => {
- expect(wrapper.find(StagesDropdown).props('selectedStage')).toBe('aStage');
- });
- });
-
- describe('without jobs for stages', () => {
- beforeEach(() => store.dispatch('receiveJobSuccess', job));
-
- it('does not render jobs container', () => {
- expect(wrapper.find(JobsContainer).exists()).toBe(false);
- });
- });
-
- describe('with jobs for stages', () => {
- beforeEach(async () => {
- await store.dispatch('receiveJobSuccess', job);
- await store.dispatch('receiveJobsForStageSuccess', jobsInStage.latest_statuses);
- });
-
- it('renders list of jobs', () => {
- expect(wrapper.find(JobsContainer).exists()).toBe(true);
- });
- });
- });
-
- describe('artifacts', () => {
- beforeEach(() => {
- createWrapper();
- });
-
- it('artifacts are not shown if there are no properties other than locked', () => {
- expect(findArtifactsBlock().exists()).toBe(false);
- });
-
- it('artifacts are shown if present', async () => {
- store.state.job.artifact = {
- download_path: '/root/ci-project/-/jobs/1960/artifacts/download',
- browse_path: '/root/ci-project/-/jobs/1960/artifacts/browse',
- keep_path: '/root/ci-project/-/jobs/1960/artifacts/keep',
- expire_at: '2021-03-23T17:57:11.211Z',
- expired: false,
- locked: false,
- };
-
- await nextTick();
-
- expect(findArtifactsBlock().exists()).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/jobs/components/stages_dropdown_spec.js b/spec/frontend/jobs/components/stages_dropdown_spec.js
deleted file mode 100644
index f638213ef0c..00000000000
--- a/spec/frontend/jobs/components/stages_dropdown_spec.js
+++ /dev/null
@@ -1,192 +0,0 @@
-import { GlDropdown, GlDropdownItem, GlLink, GlSprintf } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Mousetrap from 'mousetrap';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import StagesDropdown from '~/jobs/components/stages_dropdown.vue';
-import CiIcon from '~/vue_shared/components/ci_icon.vue';
-import * as copyToClipboard from '~/behaviors/copy_to_clipboard';
-import {
- mockPipelineWithoutRef,
- mockPipelineWithoutMR,
- mockPipelineWithAttachedMR,
- mockPipelineDetached,
-} from '../mock_data';
-
-describe('Stages Dropdown', () => {
- let wrapper;
-
- const findStatus = () => wrapper.findComponent(CiIcon);
- const findSelectedStageText = () => wrapper.findComponent(GlDropdown).props('text');
- const findStageItem = (index) => wrapper.findAllComponents(GlDropdownItem).at(index);
-
- const findPipelineInfoText = () => wrapper.findByTestId('pipeline-info').text();
-
- const createComponent = (props) => {
- wrapper = extendedWrapper(
- shallowMount(StagesDropdown, {
- propsData: {
- stages: [],
- selectedStage: 'deploy',
- ...props,
- },
- stubs: {
- GlSprintf,
- GlLink,
- },
- }),
- );
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('without a merge request pipeline', () => {
- beforeEach(() => {
- createComponent({
- pipeline: mockPipelineWithoutMR,
- stages: [{ name: 'build' }, { name: 'test' }],
- });
- });
-
- it('renders pipeline status', () => {
- expect(findStatus().exists()).toBe(true);
- });
-
- it('renders dropdown with stages', () => {
- expect(findStageItem(0).text()).toBe('build');
- });
-
- it('rendes selected stage', () => {
- expect(findSelectedStageText()).toBe('deploy');
- });
- });
-
- describe('pipelineInfo', () => {
- const allElements = [
- 'pipeline-path',
- 'mr-link',
- 'source-ref-link',
- 'copy-source-ref-link',
- 'source-branch-link',
- 'copy-source-branch-link',
- 'target-branch-link',
- 'copy-target-branch-link',
- ];
- describe.each([
- [
- 'does not have a ref',
- {
- pipeline: mockPipelineWithoutRef,
- text: `Pipeline #${mockPipelineWithoutRef.id}`,
- foundElements: [
- { testId: 'pipeline-path', props: [{ href: mockPipelineWithoutRef.path }] },
- ],
- },
- ],
- [
- 'hasRef but not triggered by MR',
- {
- pipeline: mockPipelineWithoutMR,
- text: `Pipeline #${mockPipelineWithoutMR.id} for ${mockPipelineWithoutMR.ref.name}`,
- foundElements: [
- { testId: 'pipeline-path', props: [{ href: mockPipelineWithoutMR.path }] },
- { testId: 'source-ref-link', props: [{ href: mockPipelineWithoutMR.ref.path }] },
- { testId: 'copy-source-ref-link', props: [{ text: mockPipelineWithoutMR.ref.name }] },
- ],
- },
- ],
- [
- 'hasRef and MR but not MR pipeline',
- {
- pipeline: mockPipelineDetached,
- text: `Pipeline #${mockPipelineDetached.id} for !${mockPipelineDetached.merge_request.iid} with ${mockPipelineDetached.merge_request.source_branch}`,
- foundElements: [
- { testId: 'pipeline-path', props: [{ href: mockPipelineDetached.path }] },
- { testId: 'mr-link', props: [{ href: mockPipelineDetached.merge_request.path }] },
- {
- testId: 'source-branch-link',
- props: [{ href: mockPipelineDetached.merge_request.source_branch_path }],
- },
- {
- testId: 'copy-source-branch-link',
- props: [{ text: mockPipelineDetached.merge_request.source_branch }],
- },
- ],
- },
- ],
- [
- 'hasRef and MR and MR pipeline',
- {
- pipeline: mockPipelineWithAttachedMR,
- text: `Pipeline #${mockPipelineWithAttachedMR.id} for !${mockPipelineWithAttachedMR.merge_request.iid} with ${mockPipelineWithAttachedMR.merge_request.source_branch} into ${mockPipelineWithAttachedMR.merge_request.target_branch}`,
- foundElements: [
- { testId: 'pipeline-path', props: [{ href: mockPipelineWithAttachedMR.path }] },
- { testId: 'mr-link', props: [{ href: mockPipelineWithAttachedMR.merge_request.path }] },
- {
- testId: 'source-branch-link',
- props: [{ href: mockPipelineWithAttachedMR.merge_request.source_branch_path }],
- },
- {
- testId: 'copy-source-branch-link',
- props: [{ text: mockPipelineWithAttachedMR.merge_request.source_branch }],
- },
- {
- testId: 'target-branch-link',
- props: [{ href: mockPipelineWithAttachedMR.merge_request.target_branch_path }],
- },
- {
- testId: 'copy-target-branch-link',
- props: [{ text: mockPipelineWithAttachedMR.merge_request.target_branch }],
- },
- ],
- },
- ],
- ])('%s', (_, { pipeline, text, foundElements }) => {
- beforeEach(() => {
- createComponent({
- pipeline,
- });
- });
-
- it('should render the text', () => {
- expect(findPipelineInfoText()).toMatchInterpolatedText(text);
- });
-
- it('should find components with props', () => {
- foundElements.forEach((element) => {
- element.props.forEach((prop) => {
- const key = Object.keys(prop)[0];
- expect(wrapper.findByTestId(element.testId).attributes(key)).toBe(prop[key]);
- });
- });
- });
-
- it('should not find components', () => {
- const foundTestIds = foundElements.map((element) => element.testId);
- allElements
- .filter((testId) => !foundTestIds.includes(testId))
- .forEach((testId) => {
- expect(wrapper.findByTestId(testId).exists()).toBe(false);
- });
- });
- });
- });
-
- describe('mousetrap', () => {
- it.each([
- ['copy-source-ref-link', mockPipelineWithoutMR],
- ['copy-source-branch-link', mockPipelineWithAttachedMR],
- ])(
- 'calls clickCopyToClipboardButton with `%s` button when `b` is pressed',
- (button, pipeline) => {
- const copyToClipboardMock = jest.spyOn(copyToClipboard, 'clickCopyToClipboardButton');
- createComponent({ pipeline });
-
- Mousetrap.trigger('b');
-
- expect(copyToClipboardMock).toHaveBeenCalledWith(wrapper.findByTestId(button).element);
- },
- );
- });
-});
diff --git a/spec/frontend/jobs/components/stuck_block_spec.js b/spec/frontend/jobs/components/stuck_block_spec.js
deleted file mode 100644
index 1580ed45e46..00000000000
--- a/spec/frontend/jobs/components/stuck_block_spec.js
+++ /dev/null
@@ -1,101 +0,0 @@
-import { GlBadge, GlLink } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import StuckBlock from '~/jobs/components/stuck_block.vue';
-
-describe('Stuck Block Job component', () => {
- let wrapper;
-
- afterEach(() => {
- if (wrapper?.destroy) {
- wrapper.destroy();
- wrapper = null;
- }
- });
-
- const createWrapper = (props) => {
- wrapper = shallowMount(StuckBlock, {
- propsData: {
- ...props,
- },
- });
- };
-
- const tags = ['docker', 'gitlab-org'];
-
- const findStuckNoActiveRunners = () =>
- wrapper.find('[data-testid="job-stuck-no-active-runners"]');
- const findStuckNoRunners = () => wrapper.find('[data-testid="job-stuck-no-runners"]');
- const findStuckWithTags = () => wrapper.find('[data-testid="job-stuck-with-tags"]');
- const findRunnerPathLink = () => wrapper.find(GlLink);
- const findAllBadges = () => wrapper.findAll(GlBadge);
-
- describe('with no runners for project', () => {
- beforeEach(() => {
- createWrapper({
- hasOfflineRunnersForProject: true,
- runnersPath: '/root/project/runners#js-runners-settings',
- });
- });
-
- it('renders only information about project not having runners', () => {
- expect(findStuckNoRunners().exists()).toBe(true);
- expect(findStuckWithTags().exists()).toBe(false);
- expect(findStuckNoActiveRunners().exists()).toBe(false);
- });
-
- it('renders link to runners page', () => {
- expect(findRunnerPathLink().attributes('href')).toBe(
- '/root/project/runners#js-runners-settings',
- );
- });
- });
-
- describe('with tags', () => {
- beforeEach(() => {
- createWrapper({
- hasOfflineRunnersForProject: false,
- tags,
- runnersPath: '/root/project/runners#js-runners-settings',
- });
- });
-
- it('renders information about the tags not being set', () => {
- expect(findStuckWithTags().exists()).toBe(true);
- expect(findStuckNoActiveRunners().exists()).toBe(false);
- expect(findStuckNoRunners().exists()).toBe(false);
- });
-
- it('renders tags', () => {
- findAllBadges().wrappers.forEach((badgeElt, index) => {
- return expect(badgeElt.text()).toBe(tags[index]);
- });
- });
-
- it('renders link to runners page', () => {
- expect(findRunnerPathLink().attributes('href')).toBe(
- '/root/project/runners#js-runners-settings',
- );
- });
- });
-
- describe('without active runners', () => {
- beforeEach(() => {
- createWrapper({
- hasOfflineRunnersForProject: false,
- runnersPath: '/root/project/runners#js-runners-settings',
- });
- });
-
- it('renders information about project not having runners', () => {
- expect(findStuckNoActiveRunners().exists()).toBe(true);
- expect(findStuckNoRunners().exists()).toBe(false);
- expect(findStuckWithTags().exists()).toBe(false);
- });
-
- it('renders link to runners page', () => {
- expect(findRunnerPathLink().attributes('href')).toBe(
- '/root/project/runners#js-runners-settings',
- );
- });
- });
-});
diff --git a/spec/frontend/jobs/components/table/job_table_app_spec.js b/spec/frontend/jobs/components/table/job_table_app_spec.js
index 374768c3ee4..8c724a8030b 100644
--- a/spec/frontend/jobs/components/table/job_table_app_spec.js
+++ b/spec/frontend/jobs/components/table/job_table_app_spec.js
@@ -11,12 +11,14 @@ import VueApollo from 'vue-apollo';
import { s__ } from '~/locale';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
+import { TEST_HOST } from 'spec/test_constants';
import createFlash from '~/flash';
import getJobsQuery from '~/jobs/components/table/graphql/queries/get_jobs.query.graphql';
import JobsTable from '~/jobs/components/table/jobs_table.vue';
import JobsTableApp from '~/jobs/components/table/jobs_table_app.vue';
import JobsTableTabs from '~/jobs/components/table/jobs_table_tabs.vue';
import JobsFilteredSearch from '~/jobs/components/filtered_search/jobs_filtered_search.vue';
+import * as urlUtils from '~/lib/utils/url_utility';
import {
mockJobsResponsePaginated,
mockJobsResponseEmpty,
@@ -230,5 +232,17 @@ describe('Job table app', () => {
expect(createFlash).toHaveBeenCalledWith(expectedWarning);
expect(wrapper.vm.$apollo.queries.jobs.refetch).toHaveBeenCalledTimes(0);
});
+
+ it('updates URL query string when filtering jobs by status', async () => {
+ createComponent();
+
+ jest.spyOn(urlUtils, 'updateHistory');
+
+ await findFilteredSearch().vm.$emit('filterJobsBySearch', [mockFailedSearchToken]);
+
+ expect(urlUtils.updateHistory).toHaveBeenCalledWith({
+ url: `${TEST_HOST}/?statuses=FAILED`,
+ });
+ });
});
});
diff --git a/spec/frontend/jobs/components/trigger_block_spec.js b/spec/frontend/jobs/components/trigger_block_spec.js
deleted file mode 100644
index 78596612d23..00000000000
--- a/spec/frontend/jobs/components/trigger_block_spec.js
+++ /dev/null
@@ -1,85 +0,0 @@
-import { GlButton, GlTableLite } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import TriggerBlock from '~/jobs/components/trigger_block.vue';
-
-describe('Trigger block', () => {
- let wrapper;
-
- const findRevealButton = () => wrapper.findComponent(GlButton);
- const findVariableTable = () => wrapper.findComponent(GlTableLite);
- const findShortToken = () => wrapper.find('[data-testid="trigger-short-token"]');
- const findVariableValue = (index) =>
- wrapper.findAll('[data-testid="trigger-build-value"]').at(index);
- const findVariableKey = (index) => wrapper.findAll('[data-testid="trigger-build-key"]').at(index);
-
- const createComponent = (props) => {
- wrapper = mount(TriggerBlock, {
- propsData: {
- ...props,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('with short token and no variables', () => {
- it('renders short token', () => {
- createComponent({
- trigger: {
- short_token: '0a666b2',
- variables: [],
- },
- });
-
- expect(findShortToken().text()).toContain('0a666b2');
- });
- });
-
- describe('without variables or short token', () => {
- beforeEach(() => {
- createComponent({ trigger: { variables: [] } });
- });
-
- it('does not render short token', () => {
- expect(findShortToken().exists()).toBe(false);
- });
-
- it('does not render variables', () => {
- expect(findRevealButton().exists()).toBe(false);
- expect(findVariableTable().exists()).toBe(false);
- });
- });
-
- describe('with variables', () => {
- describe('hide/reveal variables', () => {
- it('should toggle variables on click', async () => {
- const hiddenValue = '••••••';
- const gcsVar = { key: 'UPLOAD_TO_GCS', value: 'false', public: false };
- const s3Var = { key: 'UPLOAD_TO_S3', value: 'true', public: false };
-
- createComponent({
- trigger: {
- variables: [gcsVar, s3Var],
- },
- });
-
- expect(findRevealButton().text()).toBe('Reveal values');
-
- expect(findVariableValue(0).text()).toBe(hiddenValue);
- expect(findVariableValue(1).text()).toBe(hiddenValue);
-
- expect(findVariableKey(0).text()).toBe(gcsVar.key);
- expect(findVariableKey(1).text()).toBe(s3Var.key);
-
- await findRevealButton().trigger('click');
-
- expect(findRevealButton().text()).toBe('Hide values');
-
- expect(findVariableValue(0).text()).toBe(gcsVar.value);
- expect(findVariableValue(1).text()).toBe(s3Var.value);
- });
- });
- });
-});
diff --git a/spec/frontend/jobs/components/unmet_prerequisites_block_spec.js b/spec/frontend/jobs/components/unmet_prerequisites_block_spec.js
deleted file mode 100644
index aeb85694e60..00000000000
--- a/spec/frontend/jobs/components/unmet_prerequisites_block_spec.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import { GlAlert, GlLink } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import UnmetPrerequisitesBlock from '~/jobs/components/unmet_prerequisites_block.vue';
-
-describe('Unmet Prerequisites Block Job component', () => {
- let wrapper;
- const helpPath = '/user/project/clusters/index.html#troubleshooting-failed-deployment-jobs';
-
- const createComponent = () => {
- wrapper = shallowMount(UnmetPrerequisitesBlock, {
- propsData: {
- helpPath,
- },
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders an alert with the correct message', () => {
- const container = wrapper.find(GlAlert);
- const alertMessage =
- 'This job failed because the necessary resources were not successfully created.';
-
- expect(container).not.toBeNull();
- expect(container.text()).toContain(alertMessage);
- });
-
- it('renders link to help page', () => {
- const helpLink = wrapper.find(GlLink);
-
- expect(helpLink).not.toBeNull();
- expect(helpLink.text()).toContain('More information');
- expect(helpLink.attributes().href).toEqual(helpPath);
- });
-});
diff --git a/spec/frontend/jobs/store/actions_spec.js b/spec/frontend/jobs/store/actions_spec.js
index b9f97a3c3ae..0d11c4d56bf 100644
--- a/spec/frontend/jobs/store/actions_spec.js
+++ b/spec/frontend/jobs/store/actions_spec.js
@@ -111,7 +111,7 @@ describe('Job State actions', () => {
});
describe('success', () => {
- it('dispatches requestJob and receiveJobSuccess ', () => {
+ it('dispatches requestJob and receiveJobSuccess', () => {
mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, { id: 121212, name: 'karma' });
return testAction(
@@ -137,7 +137,7 @@ describe('Job State actions', () => {
mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500);
});
- it('dispatches requestJob and receiveJobError ', () => {
+ it('dispatches requestJob and receiveJobError', () => {
return testAction(
fetchJob,
null,
@@ -291,7 +291,7 @@ describe('Job State actions', () => {
mock.onGet(`${TEST_HOST}/endpoint/trace.json`).reply(500);
});
- it('dispatches requestJobLog and receiveJobLogError ', () => {
+ it('dispatches requestJobLog and receiveJobLogError', () => {
return testAction(
fetchJobLog,
null,
@@ -355,7 +355,7 @@ describe('Job State actions', () => {
window.clearTimeout = origTimeout;
});
- it('should commit STOP_POLLING_JOB_LOG mutation ', async () => {
+ it('should commit STOP_POLLING_JOB_LOG mutation', async () => {
const jobLogTimeout = 7;
await testAction(
@@ -370,7 +370,7 @@ describe('Job State actions', () => {
});
describe('receiveJobLogSuccess', () => {
- it('should commit RECEIVE_JOB_LOG_SUCCESS mutation ', () => {
+ it('should commit RECEIVE_JOB_LOG_SUCCESS mutation', () => {
return testAction(
receiveJobLogSuccess,
'hello world',
@@ -388,7 +388,7 @@ describe('Job State actions', () => {
});
describe('toggleCollapsibleLine', () => {
- it('should commit TOGGLE_COLLAPSIBLE_LINE mutation ', () => {
+ it('should commit TOGGLE_COLLAPSIBLE_LINE mutation', () => {
return testAction(
toggleCollapsibleLine,
{ isClosed: true },
@@ -400,7 +400,7 @@ describe('Job State actions', () => {
});
describe('requestJobsForStage', () => {
- it('should commit REQUEST_JOBS_FOR_STAGE mutation ', () => {
+ it('should commit REQUEST_JOBS_FOR_STAGE mutation', () => {
return testAction(
requestJobsForStage,
{ name: 'deploy' },
@@ -423,7 +423,7 @@ describe('Job State actions', () => {
});
describe('success', () => {
- it('dispatches requestJobsForStage and receiveJobsForStageSuccess ', () => {
+ it('dispatches requestJobsForStage and receiveJobsForStageSuccess', () => {
mock
.onGet(`${TEST_HOST}/jobs.json`)
.replyOnce(200, { latest_statuses: [{ id: 121212, name: 'build' }], retried: [] });
@@ -473,7 +473,7 @@ describe('Job State actions', () => {
});
describe('receiveJobsForStageSuccess', () => {
- it('should commit RECEIVE_JOBS_FOR_STAGE_SUCCESS mutation ', () => {
+ it('should commit RECEIVE_JOBS_FOR_STAGE_SUCCESS mutation', () => {
return testAction(
receiveJobsForStageSuccess,
[{ id: 121212, name: 'karma' }],
@@ -485,7 +485,7 @@ describe('Job State actions', () => {
});
describe('receiveJobsForStageError', () => {
- it('should commit RECEIVE_JOBS_FOR_STAGE_ERROR mutation ', () => {
+ it('should commit RECEIVE_JOBS_FOR_STAGE_ERROR mutation', () => {
return testAction(
receiveJobsForStageError,
null,
diff --git a/spec/frontend/jobs/store/mutations_spec.js b/spec/frontend/jobs/store/mutations_spec.js
index ea1ec383d6e..89cda3b0544 100644
--- a/spec/frontend/jobs/store/mutations_spec.js
+++ b/spec/frontend/jobs/store/mutations_spec.js
@@ -83,7 +83,7 @@ describe('Jobs Store Mutations', () => {
describe('with new job log', () => {
describe('log.lines', () => {
describe('when append is true', () => {
- it('sets the parsed log ', () => {
+ it('sets the parsed log', () => {
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, {
append: true,
size: 511846,
@@ -107,7 +107,7 @@ describe('Jobs Store Mutations', () => {
});
describe('when it is defined', () => {
- it('sets the parsed log ', () => {
+ it('sets the parsed log', () => {
mutations[types.RECEIVE_JOB_LOG_SUCCESS](stateCopy, {
append: false,
size: 511846,
diff --git a/spec/frontend/labels/components/delete_label_modal_spec.js b/spec/frontend/labels/components/delete_label_modal_spec.js
index 6204138f885..24a803d3f16 100644
--- a/spec/frontend/labels/components/delete_label_modal_spec.js
+++ b/spec/frontend/labels/components/delete_label_modal_spec.js
@@ -34,7 +34,7 @@ describe('~/labels/components/delete_label_modal', () => {
wrapper.destroy();
});
- const findModal = () => wrapper.find(GlModal);
+ const findModal = () => wrapper.findComponent(GlModal);
const findPrimaryModalButton = () => wrapper.findByTestId('delete-button');
describe('template', () => {
diff --git a/spec/frontend/lib/dompurify_spec.js b/spec/frontend/lib/dompurify_spec.js
index 29b927ef628..5523cc0606e 100644
--- a/spec/frontend/lib/dompurify_spec.js
+++ b/spec/frontend/lib/dompurify_spec.js
@@ -203,7 +203,7 @@ describe('~/lib/dompurify', () => {
expect(el.getAttribute('rel')).toBe('noreferrer noopener');
});
- it('does not update `rel` values when target is not `_blank` ', () => {
+ it('does not update `rel` values when target is not `_blank`', () => {
const html = `<a href="https://example.com" target="_self" rel="help">internal</a>`;
const el = getSanitizedNode(html);
diff --git a/spec/frontend/lib/gfm/index_spec.js b/spec/frontend/lib/gfm/index_spec.js
index f53f809b799..7c383ae68a4 100644
--- a/spec/frontend/lib/gfm/index_spec.js
+++ b/spec/frontend/lib/gfm/index_spec.js
@@ -24,12 +24,6 @@ describe('gfm', () => {
};
describe('render', () => {
- it('processes Commonmark and provides an ast to the renderer function', async () => {
- const result = await markdownToAST('This is text');
-
- expect(result.type).toBe('root');
- });
-
it('transforms raw HTML into individual nodes in the AST', async () => {
const result = await markdownToAST('<strong>This is bold text</strong>');
@@ -46,216 +40,270 @@ describe('gfm', () => {
);
});
- it('returns the result of executing the renderer function', async () => {
- const rendered = { value: 'rendered tree' };
+ describe('with custom renderer', () => {
+ it('processes Commonmark and provides an ast to the renderer function', async () => {
+ const result = await markdownToAST('This is text');
- const result = await render({
- markdown: '<strong>This is bold text</strong>',
- renderer: () => {
- return rendered;
- },
+ expect(result.type).toBe('root');
});
- expect(result).toEqual(rendered);
+ it('returns the result of executing the renderer function', async () => {
+ const rendered = { value: 'rendered tree' };
+
+ const result = await render({
+ markdown: '<strong>This is bold text</strong>',
+ renderer: () => {
+ return rendered;
+ },
+ });
+
+ expect(result).toEqual(rendered);
+ });
});
- describe('when skipping the rendering of footnote reference and definition nodes', () => {
- it('transforms footnotes into footnotedefinition and footnotereference tags', async () => {
- const result = await markdownToAST(
- `footnote reference [^footnote]
+ describe('footnote references and footnote definitions', () => {
+ describe('when skipping the rendering of footnote reference and definition nodes', () => {
+ it('transforms footnotes into footnotedefinition and footnotereference tags', async () => {
+ const result = await markdownToAST(
+ `footnote reference [^footnote]
[^footnote]: Footnote definition`,
- ['footnoteReference', 'footnoteDefinition'],
- );
+ ['footnoteReference', 'footnoteDefinition'],
+ );
- expectInRoot(
- result,
- expect.objectContaining({
- children: expect.arrayContaining([
- expect.objectContaining({
- type: 'element',
- tagName: 'footnotereference',
- properties: {
- identifier: 'footnote',
- label: 'footnote',
- },
- }),
- ]),
- }),
- );
+ expectInRoot(
+ result,
+ expect.objectContaining({
+ children: expect.arrayContaining([
+ expect.objectContaining({
+ type: 'element',
+ tagName: 'footnotereference',
+ properties: {
+ identifier: 'footnote',
+ label: 'footnote',
+ },
+ }),
+ ]),
+ }),
+ );
- expectInRoot(
- result,
- expect.objectContaining({
- tagName: 'footnotedefinition',
- properties: {
- identifier: 'footnote',
- label: 'footnote',
- },
- }),
- );
+ expectInRoot(
+ result,
+ expect.objectContaining({
+ tagName: 'footnotedefinition',
+ properties: {
+ identifier: 'footnote',
+ label: 'footnote',
+ },
+ }),
+ );
+ });
});
});
- describe('when skipping the rendering of code blocks', () => {
- it('transforms code nodes into codeblock html tags', async () => {
- const result = await markdownToAST(
- `
+ describe('code blocks', () => {
+ describe('when skipping the rendering of code blocks', () => {
+ it('transforms code nodes into codeblock html tags', async () => {
+ const result = await markdownToAST(
+ `
\`\`\`javascript
console.log('Hola');
\`\`\`\
`,
- ['code'],
- );
+ ['code'],
+ );
- expectInRoot(
- result,
- expect.objectContaining({
- tagName: 'codeblock',
- properties: {
- language: 'javascript',
- },
- }),
- );
+ expectInRoot(
+ result,
+ expect.objectContaining({
+ tagName: 'codeblock',
+ properties: {
+ language: 'javascript',
+ },
+ }),
+ );
+ });
});
});
- describe('when skipping the rendering of reference definitions', () => {
- it('transforms code nodes into codeblock html tags', async () => {
- const result = await markdownToAST(
- `
+ describe('reference definitions', () => {
+ describe('when skipping the rendering of reference definitions', () => {
+ it('transforms code nodes into codeblock html tags', async () => {
+ const result = await markdownToAST(
+ `
[gitlab][gitlab]
[gitlab]: https://gitlab.com "GitLab"
`,
- ['definition'],
- );
+ ['definition'],
+ );
- expectInRoot(
- result,
- expect.objectContaining({
- type: 'element',
- tagName: 'referencedefinition',
- properties: {
- identifier: 'gitlab',
- title: 'GitLab',
- url: 'https://gitlab.com',
- },
- children: [
- {
- type: 'text',
- value: '[gitlab]: https://gitlab.com "GitLab"',
+ expectInRoot(
+ result,
+ expect.objectContaining({
+ type: 'element',
+ tagName: 'referencedefinition',
+ properties: {
+ identifier: 'gitlab',
+ title: 'GitLab',
+ url: 'https://gitlab.com',
},
- ],
- }),
- );
+ children: [
+ {
+ type: 'text',
+ value: '[gitlab]: https://gitlab.com "GitLab"',
+ },
+ ],
+ }),
+ );
+ });
});
});
- describe('when skipping the rendering of link and image references', () => {
- it('transforms linkReference and imageReference nodes into html tags', async () => {
- const result = await markdownToAST(
- `
+ describe('link and image references', () => {
+ describe('when skipping the rendering of link and image references', () => {
+ it('transforms linkReference and imageReference nodes into html tags', async () => {
+ const result = await markdownToAST(
+ `
[gitlab][gitlab] and ![GitLab Logo][gitlab-logo]
[gitlab]: https://gitlab.com "GitLab"
[gitlab-logo]: https://gitlab.com/gitlab-logo.png "GitLab Logo"
`,
- ['linkReference', 'imageReference'],
- );
+ ['linkReference', 'imageReference'],
+ );
- expectInRoot(
- result,
- expect.objectContaining({
- tagName: 'p',
- children: expect.arrayContaining([
- expect.objectContaining({
- type: 'element',
- tagName: 'a',
- properties: expect.objectContaining({
- href: 'https://gitlab.com',
- isReference: 'true',
- identifier: 'gitlab',
- title: 'GitLab',
+ expectInRoot(
+ result,
+ expect.objectContaining({
+ tagName: 'p',
+ children: expect.arrayContaining([
+ expect.objectContaining({
+ type: 'element',
+ tagName: 'a',
+ properties: expect.objectContaining({
+ href: 'https://gitlab.com',
+ isReference: 'true',
+ identifier: 'gitlab',
+ title: 'GitLab',
+ }),
}),
- }),
- expect.objectContaining({
- type: 'element',
- tagName: 'img',
- properties: expect.objectContaining({
- src: 'https://gitlab.com/gitlab-logo.png',
- isReference: 'true',
- identifier: 'gitlab-logo',
- title: 'GitLab Logo',
- alt: 'GitLab Logo',
+ expect.objectContaining({
+ type: 'element',
+ tagName: 'img',
+ properties: expect.objectContaining({
+ src: 'https://gitlab.com/gitlab-logo.png',
+ isReference: 'true',
+ identifier: 'gitlab-logo',
+ title: 'GitLab Logo',
+ alt: 'GitLab Logo',
+ }),
}),
- }),
- ]),
- }),
- );
- });
+ ]),
+ }),
+ );
+ });
- it('normalizes the urls extracted from the reference definitions', async () => {
- const result = await markdownToAST(
- `
+ it('normalizes the urls extracted from the reference definitions', async () => {
+ const result = await markdownToAST(
+ `
[gitlab][gitlab] and ![GitLab Logo][gitlab]
[gitlab]: /url\\bar*baz
`,
- ['linkReference', 'imageReference'],
- );
+ ['linkReference', 'imageReference'],
+ );
+
+ expectInRoot(
+ result,
+ expect.objectContaining({
+ tagName: 'p',
+ children: expect.arrayContaining([
+ expect.objectContaining({
+ type: 'element',
+ tagName: 'a',
+ properties: expect.objectContaining({
+ href: '/url%5Cbar*baz',
+ }),
+ }),
+ expect.objectContaining({
+ type: 'element',
+ tagName: 'img',
+ properties: expect.objectContaining({
+ src: '/url%5Cbar*baz',
+ }),
+ }),
+ ]),
+ }),
+ );
+ });
+ });
+ });
+
+ describe('frontmatter', () => {
+ describe('when skipping the rendering of frontmatter types', () => {
+ it.each`
+ type | input
+ ${'yaml'} | ${'---\ntitle: page\n---'}
+ ${'toml'} | ${'+++\ntitle: page\n+++'}
+ ${'json'} | ${';;;\ntitle: page\n;;;'}
+ `('transforms $type nodes into frontmatter html tags', async ({ input, type }) => {
+ const result = await markdownToAST(input, [type]);
+
+ expectInRoot(
+ result,
+ expect.objectContaining({
+ type: 'element',
+ tagName: 'frontmatter',
+ properties: {
+ language: type,
+ },
+ children: [
+ {
+ type: 'text',
+ value: 'title: page',
+ },
+ ],
+ }),
+ );
+ });
+ });
+ });
+
+ describe('table of contents', () => {
+ it.each`
+ markdown
+ ${'[[_TOC_]]'}
+ ${' [[_TOC_]]'}
+ ${'[[_TOC_]] '}
+ ${'[TOC]'}
+ ${' [TOC]'}
+ ${'[TOC] '}
+ `('parses $markdown and produces a table of contents section', async ({ markdown }) => {
+ const result = await markdownToAST(markdown);
expectInRoot(
result,
expect.objectContaining({
- tagName: 'p',
- children: expect.arrayContaining([
- expect.objectContaining({
- type: 'element',
- tagName: 'a',
- properties: expect.objectContaining({
- href: '/url%5Cbar*baz',
- }),
- }),
- expect.objectContaining({
- type: 'element',
- tagName: 'img',
- properties: expect.objectContaining({
- src: '/url%5Cbar*baz',
- }),
- }),
- ]),
+ type: 'element',
+ tagName: 'nav',
}),
);
});
});
- });
- describe('when skipping the rendering of frontmatter types', () => {
- it.each`
- type | input
- ${'yaml'} | ${'---\ntitle: page\n---'}
- ${'toml'} | ${'+++\ntitle: page\n+++'}
- ${'json'} | ${';;;\ntitle: page\n;;;'}
- `('transforms $type nodes into frontmatter html tags', async ({ input, type }) => {
- const result = await markdownToAST(input, [type]);
+ describe('when skipping the rendering of table of contents', () => {
+ it('transforms table of contents nodes into html tableofcontents tags', async () => {
+ const result = await markdownToAST('[[_TOC_]]', ['tableOfContents']);
- expectInRoot(
- result,
- expect.objectContaining({
- type: 'element',
- tagName: 'frontmatter',
- properties: {
- language: type,
- },
- children: [
- {
- type: 'text',
- value: 'title: page',
- },
- ],
- }),
- );
+ expectInRoot(
+ result,
+ expect.objectContaining({
+ type: 'element',
+ tagName: 'tableofcontents',
+ }),
+ );
+ });
});
});
});
diff --git a/spec/frontend/lib/utils/apollo_startup_js_link_spec.js b/spec/frontend/lib/utils/apollo_startup_js_link_spec.js
index 06573f346e0..b972f669ac4 100644
--- a/spec/frontend/lib/utils/apollo_startup_js_link_spec.js
+++ b/spec/frontend/lib/utils/apollo_startup_js_link_spec.js
@@ -84,7 +84,7 @@ describe('StartupJSLink', () => {
});
});
- describe('variable match errors: ', () => {
+ describe('variable match errors:', () => {
it('forwards requests if the variables are not matching', () => {
window.gl = {
startup_graphql_calls: [
diff --git a/spec/frontend/lib/utils/common_utils_spec.js b/spec/frontend/lib/utils/common_utils_spec.js
index a2ace8857ed..a0140d1d8a8 100644
--- a/spec/frontend/lib/utils/common_utils_spec.js
+++ b/spec/frontend/lib/utils/common_utils_spec.js
@@ -476,7 +476,7 @@ describe('common_utils', () => {
});
});
- it('catches the rejected promise from the callback ', () => {
+ it('catches the rejected promise from the callback', () => {
const errorMessage = 'Mistakes were made!';
return commonUtils
.backOff((next, stop) => {
diff --git a/spec/frontend/lib/utils/datetime/date_calculation_utility_spec.js b/spec/frontend/lib/utils/datetime/date_calculation_utility_spec.js
index 47bb512cbb5..59b3b4c02df 100644
--- a/spec/frontend/lib/utils/datetime/date_calculation_utility_spec.js
+++ b/spec/frontend/lib/utils/datetime/date_calculation_utility_spec.js
@@ -1,4 +1,4 @@
-import { newDateAsLocaleTime } from '~/lib/utils/datetime/date_calculation_utility';
+import { getDateWithUTC, newDateAsLocaleTime } from '~/lib/utils/datetime/date_calculation_utility';
describe('newDateAsLocaleTime', () => {
it.each`
@@ -15,3 +15,19 @@ describe('newDateAsLocaleTime', () => {
expect(newDateAsLocaleTime(string)).toEqual(expected);
});
});
+
+describe('getDateWithUTC', () => {
+ it.each`
+ date | expected
+ ${new Date('2022-03-22T01:23:45.678Z')} | ${new Date('2022-03-22T00:00:00.000Z')}
+ ${new Date('1999-12-31T23:59:59.999Z')} | ${new Date('1999-12-31T00:00:00.000Z')}
+ ${2022} | ${null}
+ ${[]} | ${null}
+ ${{}} | ${null}
+ ${true} | ${null}
+ ${null} | ${null}
+ ${undefined} | ${null}
+ `('returns $expected given $string', ({ date, expected }) => {
+ expect(getDateWithUTC(date)).toEqual(expected);
+ });
+});
diff --git a/spec/frontend/lib/utils/finite_state_machine_spec.js b/spec/frontend/lib/utils/finite_state_machine_spec.js
index 441dd24c758..cfde3b8596e 100644
--- a/spec/frontend/lib/utils/finite_state_machine_spec.js
+++ b/spec/frontend/lib/utils/finite_state_machine_spec.js
@@ -50,13 +50,13 @@ describe('Finite State Machine', () => {
});
it('throws an error if the machine definition is invalid', () => {
- expect(() => machine(badDefinition)).toThrowError(
+ expect(() => machine(badDefinition)).toThrow(
'A state machine must have an initial state (`.initial`) and a dictionary of possible states (`.states`)',
);
});
it('throws an error if the initial state is invalid', () => {
- expect(() => machine(unstartableDefinition)).toThrowError(
+ expect(() => machine(unstartableDefinition)).toThrow(
`Cannot initialize the state machine to state '${STATE_IMPOSSIBLE}'. Is that one of the machine's defined states?`,
);
});
diff --git a/spec/frontend/lib/utils/is_navigating_away_spec.js b/spec/frontend/lib/utils/is_navigating_away_spec.js
index e1230fe96bf..b8a01a1706c 100644
--- a/spec/frontend/lib/utils/is_navigating_away_spec.js
+++ b/spec/frontend/lib/utils/is_navigating_away_spec.js
@@ -6,7 +6,7 @@ describe('isNavigatingAway', () => {
setNavigatingForTestsOnly(false);
});
- it.each([false, true])('it returns the navigation flag with value %s', (flag) => {
+ it.each([false, true])('returns the navigation flag with value %s', (flag) => {
setNavigatingForTestsOnly(flag);
expect(isNavigatingAway()).toEqual(flag);
});
diff --git a/spec/frontend/lib/utils/navigation_utility_spec.js b/spec/frontend/lib/utils/navigation_utility_spec.js
index 6d3a871eb33..4dbd50223d5 100644
--- a/spec/frontend/lib/utils/navigation_utility_spec.js
+++ b/spec/frontend/lib/utils/navigation_utility_spec.js
@@ -63,7 +63,7 @@ describe('initPrefetchLinks', () => {
expect(newLink.addEventListener).toHaveBeenCalled();
});
- it('it is not fired when less then 100ms over link', () => {
+ it('is not fired when less then 100ms over link', () => {
const mouseOverEvent = new Event('mouseover');
const mouseOutEvent = new Event('mouseout');
diff --git a/spec/frontend/lib/utils/poll_spec.js b/spec/frontend/lib/utils/poll_spec.js
index 1f150599983..94a5f5385b7 100644
--- a/spec/frontend/lib/utils/poll_spec.js
+++ b/spec/frontend/lib/utils/poll_spec.js
@@ -128,9 +128,11 @@ describe('Poll', () => {
errorCallback: callbacks.error,
});
+ expect(Polling.timeoutID).toBeNull();
+
Polling.makeDelayedRequest(1);
- expect(Polling.timeoutID).toBeTruthy();
+ expect(Polling.timeoutID).not.toBeNull();
return waitForAllCallsToFinish(2, () => {
Polling.stop();
diff --git a/spec/frontend/lib/utils/text_markdown_spec.js b/spec/frontend/lib/utils/text_markdown_spec.js
index 733d89fe08c..8d179baa505 100644
--- a/spec/frontend/lib/utils/text_markdown_spec.js
+++ b/spec/frontend/lib/utils/text_markdown_spec.js
@@ -586,6 +586,33 @@ describe('init markdown', () => {
);
});
+ it('only converts valid URLs', () => {
+ const notValidUrl = 'group::label';
+ const expectedUrlValue = 'url';
+ const expectedText = `other [${notValidUrl}](${expectedUrlValue}) text`;
+ const initialValue = `other ${notValidUrl} text`;
+
+ textArea.value = initialValue;
+ selectedIndex = initialValue.indexOf(notValidUrl);
+ textArea.setSelectionRange(selectedIndex, selectedIndex + notValidUrl.length);
+
+ insertMarkdownText({
+ textArea,
+ text: textArea.value,
+ tag,
+ blockTag: null,
+ selected: notValidUrl,
+ wrap: false,
+ select,
+ });
+
+ expect(textArea.value).toEqual(expectedText);
+ expect(textArea.selectionStart).toEqual(expectedText.indexOf(expectedUrlValue, 1));
+ expect(textArea.selectionEnd).toEqual(
+ expectedText.indexOf(expectedUrlValue, 1) + expectedUrlValue.length,
+ );
+ });
+
it('adds block tags on line above and below selection', () => {
selected = 'this text\nis multiple\nlines';
text = `before \n${selected}\nafter `;
diff --git a/spec/frontend/lib/utils/text_utility_spec.js b/spec/frontend/lib/utils/text_utility_spec.js
index 8e31fc792c5..49a160c9f23 100644
--- a/spec/frontend/lib/utils/text_utility_spec.js
+++ b/spec/frontend/lib/utils/text_utility_spec.js
@@ -45,29 +45,18 @@ describe('text_utility', () => {
});
describe('slugify', () => {
- it('should remove accents and convert to lower case', () => {
- expect(textUtils.slugify('João')).toEqual('jo-o');
- });
- it('should replaces whitespaces with hyphens and convert to lower case', () => {
- expect(textUtils.slugify('My Input String')).toEqual('my-input-string');
- });
- it('should remove trailing whitespace and replace whitespaces within string with a hyphen', () => {
- expect(textUtils.slugify(' a new project ')).toEqual('a-new-project');
- });
- it('should only remove non-allowed special characters', () => {
- expect(textUtils.slugify('test!_pro-ject~')).toEqual('test-_pro-ject');
- });
- it('should squash multiple hypens', () => {
- expect(textUtils.slugify('test!!!!_pro-ject~')).toEqual('test-_pro-ject');
- });
- it('should return empty string if only non-allowed characters', () => {
- expect(textUtils.slugify('здраÑти')).toEqual('');
- });
- it('should squash multiple separators', () => {
- expect(textUtils.slugify('Test:-)')).toEqual('test');
- });
- it('should trim any separators from the beginning and end of the slug', () => {
- expect(textUtils.slugify('-Test:-)-')).toEqual('test');
+ it.each`
+ title | input | output
+ ${'should remove accents and convert to lower case'} | ${'João'} | ${'jo-o'}
+ ${'should replaces whitespaces with hyphens and convert to lower case'} | ${'My Input String'} | ${'my-input-string'}
+ ${'should remove trailing whitespace and replace whitespaces within string with a hyphen'} | ${' a new project '} | ${'a-new-project'}
+ ${'should only remove non-allowed special characters'} | ${'test!_pro-ject~'} | ${'test-_pro-ject'}
+ ${'should squash to multiple non-allowed special characters'} | ${'test!!!!_pro-ject~'} | ${'test-_pro-ject'}
+ ${'should return empty string if only non-allowed characters'} | ${'дружба'} | ${''}
+ ${'should squash multiple separators'} | ${'Test:-)'} | ${'test'}
+ ${'should trim any separators from the beginning and end of the slug'} | ${'-Test:-)-'} | ${'test'}
+ `('$title', ({ input, output }) => {
+ expect(textUtils.slugify(input)).toBe(output);
});
});
diff --git a/spec/frontend/lib/utils/vuex_module_mappers_spec.js b/spec/frontend/lib/utils/vuex_module_mappers_spec.js
index 1821a15f677..d25a692dfea 100644
--- a/spec/frontend/lib/utils/vuex_module_mappers_spec.js
+++ b/spec/frontend/lib/utils/vuex_module_mappers_spec.js
@@ -128,7 +128,7 @@ describe('~/lib/utils/vuex_module_mappers', () => {
describe('with non-string object value', () => {
it('throws helpful error', () => {
- expect(() => mapVuexModuleActions((vm) => vm.bogus, { foo: () => {} })).toThrowError(
+ expect(() => mapVuexModuleActions((vm) => vm.bogus, { foo: () => {} })).toThrow(
REQUIRE_STRING_ERROR_MESSAGE,
);
});
diff --git a/spec/frontend/locale/sprintf_spec.js b/spec/frontend/locale/sprintf_spec.js
index 52e903b819f..e0d0e117ea4 100644
--- a/spec/frontend/locale/sprintf_spec.js
+++ b/spec/frontend/locale/sprintf_spec.js
@@ -63,12 +63,26 @@ describe('locale', () => {
it('does not escape parameters for escapeParameters = false', () => {
const input = 'contains %{safeContent}';
const parameters = {
- safeContent: '<strong>bold attempt</strong>',
+ safeContent: '15',
};
const output = sprintf(input, parameters, false);
- expect(output).toBe('contains <strong>bold attempt</strong>');
+ expect(output).toBe('contains 15');
+ });
+
+ describe('replaces duplicated % in input', () => {
+ it('removes duplicated percentage signs', () => {
+ const input = 'contains duplicated %{safeContent}%%';
+
+ const parameters = {
+ safeContent: '15',
+ };
+
+ const output = sprintf(input, parameters, false);
+
+ expect(output).toBe('contains duplicated 15%');
+ });
});
});
});
diff --git a/spec/frontend/members/components/avatars/user_avatar_spec.js b/spec/frontend/members/components/avatars/user_avatar_spec.js
index 9b908e5b6f0..9172876e76f 100644
--- a/spec/frontend/members/components/avatars/user_avatar_spec.js
+++ b/spec/frontend/members/components/avatars/user_avatar_spec.js
@@ -1,7 +1,7 @@
import { GlAvatarLink, GlBadge } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import UserAvatar from '~/members/components/avatars/user_avatar.vue';
-import { AVAILABILITY_STATUS } from '~/set_status_modal/utils';
+import { AVAILABILITY_STATUS } from '~/set_status_modal/constants';
import { member as memberMock, member2faEnabled, orphanedMember } from '../../mock_data';
diff --git a/spec/frontend/members/mock_data.js b/spec/frontend/members/mock_data.js
index 06ccd107ce3..49c4c46c3ac 100644
--- a/spec/frontend/members/mock_data.js
+++ b/spec/frontend/members/mock_data.js
@@ -23,6 +23,7 @@ export const member = {
webUrl: 'https://gitlab.com/root',
avatarUrl: 'https://www.gravatar.com/avatar/4816142ef496f956a277bedf1a40607b?s=80&d=identicon',
blocked: false,
+ isBot: false,
twoFactorEnabled: false,
oncallSchedules: [{ name: 'schedule 1' }],
escalationPolicies: [{ name: 'policy 1' }],
diff --git a/spec/frontend/members/store/actions_spec.js b/spec/frontend/members/store/actions_spec.js
index d37e6871387..20dce639177 100644
--- a/spec/frontend/members/store/actions_spec.js
+++ b/spec/frontend/members/store/actions_spec.js
@@ -69,7 +69,7 @@ describe('Vuex members actions', () => {
payload: { error },
},
]),
- ).rejects.toThrowError(error);
+ ).rejects.toThrow(error);
});
});
});
@@ -122,7 +122,7 @@ describe('Vuex members actions', () => {
payload: { error },
},
]),
- ).rejects.toThrowError(error);
+ ).rejects.toThrow(error);
});
});
});
diff --git a/spec/frontend/members/utils_spec.js b/spec/frontend/members/utils_spec.js
index b0c9459ff4f..0271483801c 100644
--- a/spec/frontend/members/utils_spec.js
+++ b/spec/frontend/members/utils_spec.js
@@ -1,5 +1,12 @@
import setWindowLocation from 'helpers/set_window_location_helper';
-import { DEFAULT_SORT, MEMBER_TYPES } from '~/members/constants';
+import {
+ DEFAULT_SORT,
+ MEMBER_TYPES,
+ I18N_USER_YOU,
+ I18N_USER_BLOCKED,
+ I18N_USER_BOT,
+ I188N_USER_2FA,
+} from '~/members/constants';
import {
generateBadges,
isGroup,
@@ -52,9 +59,10 @@ describe('Members Utils', () => {
it.each`
member | expected
- ${memberMock} | ${{ show: true, text: "It's you", variant: 'success' }}
- ${{ ...memberMock, user: { ...memberMock.user, blocked: true } }} | ${{ show: true, text: 'Blocked', variant: 'danger' }}
- ${member2faEnabled} | ${{ show: true, text: '2FA', variant: 'info' }}
+ ${memberMock} | ${{ show: true, text: I18N_USER_YOU, variant: 'success' }}
+ ${{ ...memberMock, user: { ...memberMock.user, blocked: true } }} | ${{ show: true, text: I18N_USER_BLOCKED, variant: 'danger' }}
+ ${{ ...memberMock, user: { ...memberMock.user, isBot: true } }} | ${{ show: true, text: I18N_USER_BOT, variant: 'muted' }}
+ ${member2faEnabled} | ${{ show: true, text: I188N_USER_2FA, variant: 'info' }}
`('returns expected output for "$expected.text" badge', ({ member, expected }) => {
expect(
generateBadges({ member, isCurrentUser: true, canManageMembers: true }),
diff --git a/spec/frontend/merge_conflicts/components/merge_conflict_resolver_app_spec.js b/spec/frontend/merge_conflicts/components/merge_conflict_resolver_app_spec.js
index 4fdc4024e10..9b5641ef7b3 100644
--- a/spec/frontend/merge_conflicts/components/merge_conflict_resolver_app_spec.js
+++ b/spec/frontend/merge_conflicts/components/merge_conflict_resolver_app_spec.js
@@ -49,8 +49,8 @@ describe('Merge Conflict Resolver App', () => {
extendedWrapper(w).findByTestId('interactive-button');
const findFileInlineButton = (w = wrapper) => extendedWrapper(w).findByTestId('inline-button');
const findSideBySideButton = () => wrapper.findByTestId('side-by-side');
- const findInlineConflictLines = (w = wrapper) => w.find(InlineConflictLines);
- const findParallelConflictLines = (w = wrapper) => w.find(ParallelConflictLines);
+ const findInlineConflictLines = (w = wrapper) => w.findComponent(InlineConflictLines);
+ const findParallelConflictLines = (w = wrapper) => w.findComponent(ParallelConflictLines);
const findCommitMessageTextarea = () => wrapper.findByTestId('commit-message');
it('shows the amount of conflicts', () => {
diff --git a/spec/frontend/merge_conflicts/store/actions_spec.js b/spec/frontend/merge_conflicts/store/actions_spec.js
index 7cee6576b53..e73769cba51 100644
--- a/spec/frontend/merge_conflicts/store/actions_spec.js
+++ b/spec/frontend/merge_conflicts/store/actions_spec.js
@@ -48,7 +48,7 @@ describe('merge conflicts actions', () => {
);
});
- it('when data has type equal to error ', () => {
+ it('when data has type equal to error', () => {
mock.onGet(conflictsPath).reply(200, { type: 'error', message: 'error message' });
return testAction(
actions.fetchConflictsData,
@@ -63,7 +63,7 @@ describe('merge conflicts actions', () => {
);
});
- it('when request fails ', () => {
+ it('when request fails', () => {
mock.onGet(conflictsPath).reply(400);
return testAction(
actions.fetchConflictsData,
@@ -80,7 +80,7 @@ describe('merge conflicts actions', () => {
});
describe('setConflictsData', () => {
- it('INTERACTIVE_RESOLVE_MODE updates the correct file ', () => {
+ it('INTERACTIVE_RESOLVE_MODE updates the correct file', () => {
decorateFiles.mockReturnValue([{ bar: 'baz' }]);
return testAction(
actions.setConflictsData,
@@ -239,7 +239,7 @@ describe('merge conflicts actions', () => {
});
describe('setFileResolveMode', () => {
- it('INTERACTIVE_RESOLVE_MODE updates the correct file ', () => {
+ it('INTERACTIVE_RESOLVE_MODE updates the correct file', () => {
return testAction(
actions.setFileResolveMode,
{ file: files[0], mode: INTERACTIVE_RESOLVE_MODE },
@@ -257,7 +257,7 @@ describe('merge conflicts actions', () => {
);
});
- it('EDIT_RESOLVE_MODE updates the correct file ', async () => {
+ it('EDIT_RESOLVE_MODE updates the correct file', async () => {
restoreFileLinesState.mockReturnValue([]);
const file = {
...files[0],
@@ -286,7 +286,7 @@ describe('merge conflicts actions', () => {
});
describe('setPromptConfirmationState', () => {
- it('updates the correct file ', () => {
+ it('updates the correct file', () => {
return testAction(
actions.setPromptConfirmationState,
{ file: files[0], promptDiscardConfirmation: true },
@@ -315,7 +315,7 @@ describe('merge conflicts actions', () => {
],
};
- it('updates the correct file ', async () => {
+ it('updates the correct file', async () => {
const marLikeMockReturn = { foo: 'bar' };
markLine.mockReturnValue(marLikeMockReturn);
diff --git a/spec/frontend/merge_request_tabs_spec.js b/spec/frontend/merge_request_tabs_spec.js
index 2001bb5f95e..c6e90a4b20d 100644
--- a/spec/frontend/merge_request_tabs_spec.js
+++ b/spec/frontend/merge_request_tabs_spec.js
@@ -333,7 +333,7 @@ describe('MergeRequestTabs', () => {
${'show'} | ${false} | ${'shows'}
${'diffs'} | ${true} | ${'hides'}
${'commits'} | ${true} | ${'hides'}
- `('it $hidesText expand button on $tab tab', ({ tab, hides }) => {
+ `('$hidesText expand button on $tab tab', ({ tab, hides }) => {
window.gon = { features: { movedMrSidebar: true } };
const expandButton = document.createElement('div');
diff --git a/spec/frontend/milestones/components/milestone_combobox_spec.js b/spec/frontend/milestones/components/milestone_combobox_spec.js
index a8e3d13dca0..ce5b2a1000b 100644
--- a/spec/frontend/milestones/components/milestone_combobox_spec.js
+++ b/spec/frontend/milestones/components/milestone_combobox_spec.js
@@ -96,19 +96,19 @@ describe('Milestone combobox component', () => {
const findNoResults = () => wrapper.find('[data-testid="milestone-combobox-no-results"]');
- const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findSearchBox = () => wrapper.find(GlSearchBoxByType);
+ const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
const findProjectMilestonesSection = () =>
wrapper.find('[data-testid="project-milestones-section"]');
const findProjectMilestonesDropdownItems = () =>
- findProjectMilestonesSection().findAll(GlDropdownItem);
+ findProjectMilestonesSection().findAllComponents(GlDropdownItem);
const findFirstProjectMilestonesDropdownItem = () => findProjectMilestonesDropdownItems().at(0);
const findGroupMilestonesSection = () => wrapper.find('[data-testid="group-milestones-section"]');
const findGroupMilestonesDropdownItems = () =>
- findGroupMilestonesSection().findAll(GlDropdownItem);
+ findGroupMilestonesSection().findAllComponents(GlDropdownItem);
const findFirstGroupMilestonesDropdownItem = () => findGroupMilestonesDropdownItems().at(0);
//
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 14f04d9b767..263d6225a9f 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,8 @@
exports[`Dashboard template matches the default snapshot 1`] = `
<div
class="prometheus-graphs"
- data-qa-selector="prometheus_graphs"
+ data-qa-selector="prometheus_graphs_content"
+ data-testid="prometheus-graphs"
environmentstate="available"
metricsdashboardbasepath="/monitoring/monitor-project/-/metrics?environment=1"
metricsendpoint="/monitoring/monitor-project/-/environments/1/additional_metrics.json"
@@ -60,6 +61,7 @@ exports[`Dashboard template matches the default snapshot 1`] = `
clearalltext="Clear all"
clearalltextclass="gl-px-5"
data-qa-selector="environments_dropdown"
+ data-testid="environments-dropdown"
headertext=""
hideheaderborder="true"
highlighteditemstitle="Selected"
diff --git a/spec/frontend/monitoring/components/charts/stacked_column_spec.js b/spec/frontend/monitoring/components/charts/stacked_column_spec.js
index 91fe36bc6e4..779ded090c2 100644
--- a/spec/frontend/monitoring/components/charts/stacked_column_spec.js
+++ b/spec/frontend/monitoring/components/charts/stacked_column_spec.js
@@ -72,7 +72,7 @@ describe('Stacked column chart component', () => {
]);
});
- it('chart options should configure data zoom and axis label ', () => {
+ it('chart options should configure data zoom and axis label', () => {
const chartOptions = findChart().props('option');
const xAxisType = findChart().props('xAxisType');
diff --git a/spec/frontend/monitoring/components/dashboard_panel_spec.js b/spec/frontend/monitoring/components/dashboard_panel_spec.js
index d797d9e2ad0..339c1710a9e 100644
--- a/spec/frontend/monitoring/components/dashboard_panel_spec.js
+++ b/spec/frontend/monitoring/components/dashboard_panel_spec.js
@@ -430,7 +430,7 @@ describe('Dashboard Panel', () => {
expect(findTimeChart().props().projectPath).toBe(mockProjectPath);
});
- it('it renders a time series chart with no errors', () => {
+ it('renders a time series chart with no errors', () => {
expect(wrapper.findComponent(MonitorTimeSeriesChart).exists()).toBe(true);
});
});
diff --git a/spec/frontend/monitoring/components/dashboard_spec.js b/spec/frontend/monitoring/components/dashboard_spec.js
index 608404e5c5b..1de6b6e3e98 100644
--- a/spec/frontend/monitoring/components/dashboard_spec.js
+++ b/spec/frontend/monitoring/components/dashboard_spec.js
@@ -407,7 +407,7 @@ describe('Dashboard', () => {
await nextTick();
});
- it('it does not show loading icons in any group', async () => {
+ it('does not show loading icons in any group', async () => {
setupStoreWithData(store);
await nextTick();
@@ -614,11 +614,11 @@ describe('Dashboard', () => {
const findFirstDraggableRemoveButton = () =>
findDraggablePanels().at(0).find('.js-draggable-remove');
- it('it enables draggables', async () => {
+ it('enables draggables', async () => {
findRearrangeButton().vm.$emit('click');
await nextTick();
- expect(findRearrangeButton().attributes('pressed')).toBeTruthy();
+ expect(findRearrangeButton().attributes('pressed')).toBe('true');
expect(findEnabledDraggables().wrappers).toEqual(findDraggables().wrappers);
});
@@ -656,13 +656,13 @@ describe('Dashboard', () => {
expect(findDraggablePanels().length).toEqual(metricsDashboardPanelCount - 1);
});
- it('it disables draggables when clicked again', async () => {
+ it('disables draggables when clicked again', async () => {
findRearrangeButton().vm.$emit('click');
await nextTick();
findRearrangeButton().vm.$emit('click');
await nextTick();
- expect(findRearrangeButton().attributes('pressed')).toBeFalsy();
+ expect(findRearrangeButton().attributes('pressed')).toBeUndefined();
expect(findEnabledDraggables().length).toBe(0);
});
});
diff --git a/spec/frontend/monitoring/components/dashboards_dropdown_spec.js b/spec/frontend/monitoring/components/dashboards_dropdown_spec.js
index 721992e710a..3ccaa2d28ac 100644
--- a/spec/frontend/monitoring/components/dashboards_dropdown_spec.js
+++ b/spec/frontend/monitoring/components/dashboards_dropdown_spec.js
@@ -163,9 +163,6 @@ describe('DashboardsDropdown', () => {
findItemAt(1).vm.$emit('click');
});
- it('emits a "selectDashboard" event', () => {
- expect(wrapper.emitted().selectDashboard).toBeTruthy();
- });
it('emits a "selectDashboard" event with dashboard information', () => {
expect(wrapper.emitted().selectDashboard[0]).toEqual([dashboardGitResponse[0]]);
});
diff --git a/spec/frontend/monitoring/components/duplicate_dashboard_form_spec.js b/spec/frontend/monitoring/components/duplicate_dashboard_form_spec.js
index 755204dc721..b54ca926dae 100644
--- a/spec/frontend/monitoring/components/duplicate_dashboard_form_spec.js
+++ b/spec/frontend/monitoring/components/duplicate_dashboard_form_spec.js
@@ -68,7 +68,7 @@ describe('DuplicateDashboardForm', () => {
await nextTick();
expect(findByRef('fileNameFormGroup').classes()).toContain('is-invalid');
- expect(findInvalidFeedback().text()).toBeTruthy();
+ expect(findInvalidFeedback().text()).toBe('The file name should have a .yml extension');
});
});
diff --git a/spec/frontend/monitoring/components/duplicate_dashboard_modal_spec.js b/spec/frontend/monitoring/components/duplicate_dashboard_modal_spec.js
index 3032c236741..d83a9192876 100644
--- a/spec/frontend/monitoring/components/duplicate_dashboard_modal_spec.js
+++ b/spec/frontend/monitoring/components/duplicate_dashboard_modal_spec.js
@@ -72,7 +72,7 @@ describe('duplicate dashboard modal', () => {
await waitForPromises();
expect(okEvent.preventDefault).toHaveBeenCalled();
- expect(wrapper.emitted().dashboardDuplicated).toBeTruthy();
+ expect(wrapper.emitted('dashboardDuplicated')).toHaveLength(1);
expect(wrapper.emitted().dashboardDuplicated[0]).toEqual([dashboardGitResponse[0]]);
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.vm.$refs.duplicateDashboardModal.hide).toHaveBeenCalled();
diff --git a/spec/frontend/monitoring/store/actions_spec.js b/spec/frontend/monitoring/store/actions_spec.js
index d1a13fbf9cd..a872a7780eb 100644
--- a/spec/frontend/monitoring/store/actions_spec.js
+++ b/spec/frontend/monitoring/store/actions_spec.js
@@ -855,7 +855,7 @@ describe('Monitoring store actions', () => {
);
});
- it('dispatches receiveDashboardValidationWarningsSuccess with false payload when the response is empty ', () => {
+ it('dispatches receiveDashboardValidationWarningsSuccess with false payload when the response is empty', () => {
mockMutate.mockResolvedValue({
data: {
project: null,
diff --git a/spec/frontend/nav/components/top_nav_app_spec.js b/spec/frontend/nav/components/top_nav_app_spec.js
index 1d6ea99155b..745707c1d28 100644
--- a/spec/frontend/nav/components/top_nav_app_spec.js
+++ b/spec/frontend/nav/components/top_nav_app_spec.js
@@ -30,9 +30,10 @@ describe('~/nav/components/top_nav_app.vue', () => {
it('renders nav item dropdown', () => {
expect(findNavItemDropdown().attributes('href')).toBeUndefined();
expect(findNavItemDropdown().attributes()).toMatchObject({
- icon: 'hamburger',
- text: TEST_NAV_DATA.activeTitle,
+ icon: '',
+ text: '',
'no-flip': '',
+ 'no-caret': '',
});
});
diff --git a/spec/frontend/nav/components/top_nav_dropdown_menu_spec.js b/spec/frontend/nav/components/top_nav_dropdown_menu_spec.js
index 6cfbdb16111..048fca846ad 100644
--- a/spec/frontend/nav/components/top_nav_dropdown_menu_spec.js
+++ b/spec/frontend/nav/components/top_nav_dropdown_menu_spec.js
@@ -25,7 +25,7 @@ describe('~/nav/components/top_nav_dropdown_menu.vue', () => {
};
const findMenuItems = () => wrapper.findAllComponents(TopNavMenuItem);
- const findMenuSections = () => wrapper.find(TopNavMenuSections);
+ const findMenuSections = () => wrapper.findComponent(TopNavMenuSections);
const findMenuSidebar = () => wrapper.find('[data-testid="menu-sidebar"]');
const findMenuSubview = () => wrapper.findComponent(KeepAliveSlots);
const hasFullWidthMenuSidebar = () => findMenuSidebar().classes('gl-w-full');
diff --git a/spec/frontend/nav/components/top_nav_menu_item_spec.js b/spec/frontend/nav/components/top_nav_menu_item_spec.js
index a7430d8c73f..b9cf39b8c1d 100644
--- a/spec/frontend/nav/components/top_nav_menu_item_spec.js
+++ b/spec/frontend/nav/components/top_nav_menu_item_spec.js
@@ -26,7 +26,7 @@ describe('~/nav/components/top_nav_menu_item.vue', () => {
});
};
- const findButton = () => wrapper.find(GlButton);
+ const findButton = () => wrapper.findComponent(GlButton);
const findButtonIcons = () =>
findButton()
.findAllComponents(GlIcon)
diff --git a/spec/frontend/nav/components/top_nav_menu_sections_spec.js b/spec/frontend/nav/components/top_nav_menu_sections_spec.js
index d56542fe572..0ed5cffd93f 100644
--- a/spec/frontend/nav/components/top_nav_menu_sections_spec.js
+++ b/spec/frontend/nav/components/top_nav_menu_sections_spec.js
@@ -4,11 +4,20 @@ import TopNavMenuSections from '~/nav/components/top_nav_menu_sections.vue';
const TEST_SECTIONS = [
{
id: 'primary',
- menuItems: [{ id: 'test', href: '/test/href' }, { id: 'foo' }, { id: 'bar' }],
+ menuItems: [
+ { type: 'header', title: 'Heading' },
+ { type: 'item', id: 'test', href: '/test/href' },
+ { type: 'header', title: 'Another Heading' },
+ { type: 'item', id: 'foo' },
+ { type: 'item', id: 'bar' },
+ ],
},
{
id: 'secondary',
- menuItems: [{ id: 'lorem' }, { id: 'ipsum' }],
+ menuItems: [
+ { type: 'item', id: 'lorem' },
+ { type: 'item', id: 'ipsum' },
+ ],
},
];
@@ -25,10 +34,20 @@ describe('~/nav/components/top_nav_menu_sections.vue', () => {
};
const findMenuItemModels = (parent) =>
- parent.findAll('[data-testid="menu-item"]').wrappers.map((x) => ({
- menuItem: x.props('menuItem'),
- classes: x.classes(),
- }));
+ parent.findAll('[data-testid="menu-header"],[data-testid="menu-item"]').wrappers.map((x) => {
+ return {
+ menuItem: x.vm
+ ? {
+ type: 'item',
+ ...x.props('menuItem'),
+ }
+ : {
+ type: 'header',
+ title: x.text(),
+ },
+ classes: x.classes(),
+ };
+ });
const findSectionModels = () =>
wrapper.findAll('[data-testid="menu-section"]').wrappers.map((x) => ({
classes: x.classes(),
@@ -45,32 +64,31 @@ describe('~/nav/components/top_nav_menu_sections.vue', () => {
});
it('renders sections with menu items', () => {
+ const headerClasses = ['gl-px-4', 'gl-py-2', 'gl-text-gray-900', 'gl-display-block'];
+ const itemClasses = ['gl-w-full'];
+
expect(findSectionModels()).toEqual([
{
classes: [],
- menuItems: [
- {
- menuItem: TEST_SECTIONS[0].menuItems[0],
- classes: ['gl-w-full'],
- },
- ...TEST_SECTIONS[0].menuItems.slice(1).map((menuItem) => ({
+ menuItems: TEST_SECTIONS[0].menuItems.map((menuItem, index) => {
+ const classes = menuItem.type === 'header' ? [...headerClasses] : [...itemClasses];
+ if (index > 0) classes.push(menuItem.type === 'header' ? 'gl-pt-3!' : 'gl-mt-1');
+ return {
menuItem,
- classes: ['gl-w-full', 'gl-mt-1'],
- })),
- ],
+ classes,
+ };
+ }),
},
{
classes: [...TopNavMenuSections.BORDER_CLASSES.split(' '), 'gl-mt-3'],
- menuItems: [
- {
- menuItem: TEST_SECTIONS[1].menuItems[0],
- classes: ['gl-w-full'],
- },
- ...TEST_SECTIONS[1].menuItems.slice(1).map((menuItem) => ({
+ menuItems: TEST_SECTIONS[1].menuItems.map((menuItem, index) => {
+ const classes = menuItem.type === 'header' ? [...headerClasses] : [...itemClasses];
+ if (index > 0) classes.push(menuItem.type === 'header' ? 'gl-pt-3!' : 'gl-mt-1');
+ return {
menuItem,
- classes: ['gl-w-full', 'gl-mt-1'],
- })),
- ],
+ classes,
+ };
+ }),
},
]);
});
@@ -88,7 +106,7 @@ describe('~/nav/components/top_nav_menu_sections.vue', () => {
menuItem.vm.$emit('click');
- expect(wrapper.emitted('menu-item-click')).toEqual([[TEST_SECTIONS[0].menuItems[1]]]);
+ expect(wrapper.emitted('menu-item-click')).toEqual([[TEST_SECTIONS[0].menuItems[3]]]);
});
});
diff --git a/spec/frontend/nav/mock_data.js b/spec/frontend/nav/mock_data.js
index c2ad86a4605..2052acfe001 100644
--- a/spec/frontend/nav/mock_data.js
+++ b/spec/frontend/nav/mock_data.js
@@ -1,7 +1,7 @@
import { range } from 'lodash';
export const TEST_NAV_DATA = {
- activeTitle: 'Test Active Title',
+ menuTitle: 'Test Menu Title',
primary: [
...['projects', 'groups'].map((view) => ({
id: view,
diff --git a/spec/frontend/notebook/cells/output/latex_spec.js b/spec/frontend/notebook/cells/output/latex_spec.js
index 848d2069421..ed3b63be50a 100644
--- a/spec/frontend/notebook/cells/output/latex_spec.js
+++ b/spec/frontend/notebook/cells/output/latex_spec.js
@@ -27,7 +27,7 @@ describe('LaTeX output cell', () => {
${1} | ${false}
`('sets `Prompt.show-output` to $expectation when index is $index', ({ index, expectation }) => {
const wrapper = createComponent(inlineLatex, index);
- const prompt = wrapper.find(Prompt);
+ const prompt = wrapper.findComponent(Prompt);
expect(prompt.props().count).toEqual(count);
expect(prompt.props().showOutput).toEqual(expectation);
diff --git a/spec/frontend/notebook/index_spec.js b/spec/frontend/notebook/index_spec.js
index 475c41a72f6..b79000a3505 100644
--- a/spec/frontend/notebook/index_spec.js
+++ b/spec/frontend/notebook/index_spec.js
@@ -11,7 +11,7 @@ describe('Notebook component', () => {
function buildComponent(notebook) {
return mount(Component, {
- propsData: { notebook, codeCssClass: 'js-code-class' },
+ propsData: { notebook },
provide: { relativeRawPath: '' },
}).vm;
}
@@ -46,10 +46,6 @@ describe('Notebook component', () => {
it('renders code cell', () => {
expect(vm.$el.querySelector('pre')).not.toBeNull();
});
-
- it('add code class to code blocks', () => {
- expect(vm.$el.querySelector('.js-code-class')).not.toBeNull();
- });
});
describe('with worksheets', () => {
@@ -72,9 +68,5 @@ describe('Notebook component', () => {
it('renders code cell', () => {
expect(vm.$el.querySelector('pre')).not.toBeNull();
});
-
- it('add code class to code blocks', () => {
- expect(vm.$el.querySelector('.js-code-class')).not.toBeNull();
- });
});
});
diff --git a/spec/frontend/notebook/lib/highlight_spec.js b/spec/frontend/notebook/lib/highlight_spec.js
deleted file mode 100644
index 944ccd6aa9f..00000000000
--- a/spec/frontend/notebook/lib/highlight_spec.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import Prism from '~/notebook/lib/highlight';
-
-describe('Highlight library', () => {
- it('imports python language', () => {
- expect(Prism.languages.python).toBeDefined();
- });
-
- it('uses custom CSS classes', () => {
- const el = document.createElement('div');
- el.innerHTML = Prism.highlight('console.log("a");', Prism.languages.javascript);
-
- expect(el.querySelector('.string')).not.toBeNull();
- expect(el.querySelector('.function')).not.toBeNull();
- });
-});
diff --git a/spec/frontend/notes/components/comment_form_spec.js b/spec/frontend/notes/components/comment_form_spec.js
index 463787c148b..55e4ef42e37 100644
--- a/spec/frontend/notes/components/comment_form_spec.js
+++ b/spec/frontend/notes/components/comment_form_spec.js
@@ -586,10 +586,10 @@ describe('issue_comment_form component', () => {
${true}
${false}
`('when checkbox value is `$shouldCheckboxBeChecked`', ({ shouldCheckboxBeChecked }) => {
- it(`sets \`confidential\` to \`${shouldCheckboxBeChecked}\``, async () => {
+ it(`sets \`internal\` to \`${shouldCheckboxBeChecked}\``, async () => {
mountComponent({
mountFunction: mount,
- initialData: { note: 'confidential note' },
+ initialData: { note: 'internal note' },
noteableData: { ...notableDataMockCanUpdateIssuable },
});
@@ -606,7 +606,7 @@ describe('issue_comment_form component', () => {
findCommentButton().trigger('click');
const [providedData] = wrapper.vm.saveNote.mock.calls[0];
- expect(providedData.data.note.confidential).toBe(shouldCheckboxBeChecked);
+ expect(providedData.data.note.internal).toBe(shouldCheckboxBeChecked);
});
});
@@ -679,7 +679,7 @@ describe('issue_comment_form component', () => {
);
});
- it('clicking `add comment now`, should call note endpoint, set `isDraft` false ', () => {
+ it('clicking `add comment now`, should call note endpoint, set `isDraft` false', () => {
mountComponent({ mountFunction: mount, initialData: { note: 'a comment' } });
jest.spyOn(store, 'dispatch').mockResolvedValue();
diff --git a/spec/frontend/notes/components/discussion_counter_spec.js b/spec/frontend/notes/components/discussion_counter_spec.js
index a7e2f1efa09..f4ec7f835bb 100644
--- a/spec/frontend/notes/components/discussion_counter_spec.js
+++ b/spec/frontend/notes/components/discussion_counter_spec.js
@@ -1,5 +1,5 @@
-import { GlButton } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
+import { GlDropdownItem } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import DiscussionCounter from '~/notes/components/discussion_counter.vue';
@@ -45,7 +45,7 @@ describe('DiscussionCounter component', () => {
describe('has no discussions', () => {
it('does not render', () => {
- wrapper = shallowMount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
+ wrapper = mount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
expect(wrapper.findComponent({ ref: 'discussionCounter' }).exists()).toBe(false);
});
@@ -55,7 +55,7 @@ describe('DiscussionCounter component', () => {
it('does not render', () => {
store.commit(types.ADD_OR_UPDATE_DISCUSSIONS, [{ ...discussionMock, resolvable: false }]);
store.dispatch('updateResolvableDiscussionsCounts');
- wrapper = shallowMount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
+ wrapper = mount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
expect(wrapper.findComponent({ ref: 'discussionCounter' }).exists()).toBe(false);
});
@@ -75,7 +75,7 @@ describe('DiscussionCounter component', () => {
it('renders', () => {
updateStore();
- wrapper = shallowMount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
+ wrapper = mount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
expect(wrapper.findComponent({ ref: 'discussionCounter' }).exists()).toBe(true);
});
@@ -89,7 +89,7 @@ describe('DiscussionCounter component', () => {
({ blocksMerge, color }) => {
updateStore();
store.state.unresolvedDiscussionsCount = 1;
- wrapper = shallowMount(DiscussionCounter, { store, propsData: { blocksMerge } });
+ wrapper = mount(DiscussionCounter, { store, propsData: { blocksMerge } });
expect(wrapper.find('[data-testid="discussions-counter-text"]').classes()).toContain(color);
},
@@ -97,60 +97,58 @@ describe('DiscussionCounter component', () => {
it.each`
title | resolved | groupLength
- ${'not allResolved'} | ${false} | ${4}
+ ${'not allResolved'} | ${false} | ${2}
${'allResolved'} | ${true} | ${1}
- `('renders correctly if $title', ({ resolved, groupLength }) => {
+ `('renders correctly if $title', async ({ resolved, groupLength }) => {
updateStore({ resolvable: true, resolved });
- wrapper = shallowMount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
+ wrapper = mount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
+ await wrapper.find('.dropdown-toggle').trigger('click');
- expect(wrapper.findAllComponents(GlButton)).toHaveLength(groupLength);
+ expect(wrapper.findAllComponents(GlDropdownItem)).toHaveLength(groupLength);
});
});
describe('toggle all threads button', () => {
let toggleAllButton;
- const updateStoreWithExpanded = (expanded) => {
+ const updateStoreWithExpanded = async (expanded) => {
const discussion = { ...discussionMock, expanded };
store.commit(types.ADD_OR_UPDATE_DISCUSSIONS, [discussion]);
store.dispatch('updateResolvableDiscussionsCounts');
- wrapper = shallowMount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
- toggleAllButton = wrapper.find('.toggle-all-discussions-btn');
+ wrapper = mount(DiscussionCounter, { store, propsData: { blocksMerge: true } });
+ await wrapper.find('.dropdown-toggle').trigger('click');
+ toggleAllButton = wrapper.find('[data-testid="toggle-all-discussions-btn"]');
};
afterEach(() => wrapper.destroy());
- it('calls button handler when clicked', () => {
- updateStoreWithExpanded(true);
+ it('calls button handler when clicked', async () => {
+ await updateStoreWithExpanded(true);
- toggleAllButton.vm.$emit('click');
+ toggleAllButton.trigger('click');
expect(setExpandDiscussionsFn).toHaveBeenCalledTimes(1);
});
it('collapses all discussions if expanded', async () => {
- updateStoreWithExpanded(true);
+ await updateStoreWithExpanded(true);
expect(wrapper.vm.allExpanded).toBe(true);
- expect(toggleAllButton.props('icon')).toBe('collapse');
- toggleAllButton.vm.$emit('click');
+ toggleAllButton.trigger('click');
await nextTick();
expect(wrapper.vm.allExpanded).toBe(false);
- expect(toggleAllButton.props('icon')).toBe('expand');
});
it('expands all discussions if collapsed', async () => {
- updateStoreWithExpanded(false);
+ await updateStoreWithExpanded(false);
expect(wrapper.vm.allExpanded).toBe(false);
- expect(toggleAllButton.props('icon')).toBe('expand');
- toggleAllButton.vm.$emit('click');
+ toggleAllButton.trigger('click');
await nextTick();
expect(wrapper.vm.allExpanded).toBe(true);
- expect(toggleAllButton.props('icon')).toBe('collapse');
});
});
});
diff --git a/spec/frontend/notes/components/discussion_filter_spec.js b/spec/frontend/notes/components/discussion_filter_spec.js
index 27206bddbfc..ed9fc47540d 100644
--- a/spec/frontend/notes/components/discussion_filter_spec.js
+++ b/spec/frontend/notes/components/discussion_filter_spec.js
@@ -8,7 +8,14 @@ import createEventHub from '~/helpers/event_hub_factory';
import axios from '~/lib/utils/axios_utils';
import DiscussionFilter from '~/notes/components/discussion_filter.vue';
-import { DISCUSSION_FILTERS_DEFAULT_VALUE, DISCUSSION_FILTER_TYPES } from '~/notes/constants';
+import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
+import Tracking from '~/tracking';
+import {
+ DISCUSSION_FILTERS_DEFAULT_VALUE,
+ DISCUSSION_FILTER_TYPES,
+ ASC,
+ DESC,
+} from '~/notes/constants';
import notesModule from '~/notes/stores/modules';
import { discussionFiltersMock, discussionMock } from '../mock_data';
@@ -28,6 +35,8 @@ describe('DiscussionFilter component', () => {
const findFilter = (filterType) =>
wrapper.find(`.dropdown-item[data-filter-type="${filterType}"]`);
+ const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
+
const mountComponent = () => {
const discussions = [
{
@@ -68,6 +77,7 @@ describe('DiscussionFilter component', () => {
mock.onGet(DISCUSSION_PATH).reply(200, '');
window.mrTabs = undefined;
wrapper = mountComponent();
+ jest.spyOn(Tracking, 'event');
});
afterEach(() => {
@@ -75,6 +85,65 @@ describe('DiscussionFilter component', () => {
mock.restore();
});
+ describe('default', () => {
+ beforeEach(() => {
+ jest.spyOn(store, 'dispatch').mockImplementation();
+ });
+
+ it('has local storage sync with the correct props', () => {
+ expect(findLocalStorageSync().props('asString')).toBe(true);
+ });
+
+ it('calls setDiscussionSortDirection when update is emitted', () => {
+ findLocalStorageSync().vm.$emit('input', ASC);
+
+ expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', { direction: ASC });
+ });
+ });
+
+ describe('when asc', () => {
+ beforeEach(() => {
+ jest.spyOn(store, 'dispatch').mockImplementation();
+ });
+
+ describe('when the dropdown is clicked', () => {
+ it('calls the right actions', () => {
+ wrapper.find('.js-newest-first').vm.$emit('click');
+
+ expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', {
+ direction: DESC,
+ });
+ expect(Tracking.event).toHaveBeenCalledWith(undefined, 'change_discussion_sort_direction', {
+ property: DESC,
+ });
+ });
+ });
+ });
+
+ describe('when desc', () => {
+ beforeEach(() => {
+ store.state.discussionSortOrder = DESC;
+ jest.spyOn(store, 'dispatch').mockImplementation();
+ });
+
+ describe('when the dropdown item is clicked', () => {
+ it('calls the right actions', () => {
+ wrapper.find('.js-oldest-first').vm.$emit('click');
+
+ expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', {
+ direction: ASC,
+ });
+ expect(Tracking.event).toHaveBeenCalledWith(undefined, 'change_discussion_sort_direction', {
+ property: ASC,
+ });
+ });
+
+ it('sets is-checked to true on the active button in the dropdown', () => {
+ expect(wrapper.find('.js-newest-first').props('isChecked')).toBe(true);
+ });
+ });
+ });
+
it('renders the all filters', () => {
expect(wrapper.findAll('.discussion-filter-container .dropdown-item').length).toBe(
discussionFiltersMock.length,
@@ -82,7 +151,7 @@ describe('DiscussionFilter component', () => {
});
it('renders the default selected item', () => {
- expect(wrapper.find('#discussion-filter-dropdown .dropdown-item').text().trim()).toBe(
+ expect(wrapper.find('.discussion-filter-container .dropdown-item').text().trim()).toBe(
discussionFiltersMock[0].title,
);
});
@@ -127,14 +196,6 @@ describe('DiscussionFilter component', () => {
expect(wrapper.vm.$store.state.commentsDisabled).toBe(false);
});
- it('renders a dropdown divider for the default filter', () => {
- const defaultFilter = wrapper.findAll(
- `.discussion-filter-container .dropdown-item-wrapper > *`,
- );
-
- expect(defaultFilter.at(1).classes('gl-new-dropdown-divider')).toBe(true);
- });
-
describe('Merge request tabs', () => {
eventHub = createEventHub();
diff --git a/spec/frontend/notes/components/discussion_notes_spec.js b/spec/frontend/notes/components/discussion_notes_spec.js
index 1b8b6bec490..a74d709ed3a 100644
--- a/spec/frontend/notes/components/discussion_notes_spec.js
+++ b/spec/frontend/notes/components/discussion_notes_spec.js
@@ -140,21 +140,21 @@ describe('DiscussionNotes', () => {
findNoteAtIndex(0).vm.$emit('handleDeleteNote');
await nextTick();
- expect(wrapper.emitted().deleteNote).toBeTruthy();
+ expect(wrapper.emitted().deleteNote).toHaveLength(1);
});
it('emits startReplying when first note emits startReplying', async () => {
findNoteAtIndex(0).vm.$emit('startReplying');
await nextTick();
- expect(wrapper.emitted().startReplying).toBeTruthy();
+ expect(wrapper.emitted().startReplying).toHaveLength(1);
});
it('emits deleteNote when second note emits handleDeleteNote', async () => {
findNoteAtIndex(1).vm.$emit('handleDeleteNote');
await nextTick();
- expect(wrapper.emitted().deleteNote).toBeTruthy();
+ expect(wrapper.emitted().deleteNote).toHaveLength(1);
});
});
@@ -169,7 +169,7 @@ describe('DiscussionNotes', () => {
note.vm.$emit('handleDeleteNote');
await nextTick();
- expect(wrapper.emitted().deleteNote).toBeTruthy();
+ expect(wrapper.emitted().deleteNote).toHaveLength(1);
});
});
});
diff --git a/spec/frontend/notes/components/discussion_resolve_with_issue_button_spec.js b/spec/frontend/notes/components/discussion_resolve_with_issue_button_spec.js
index 71406eeb7b4..a185f11ffaa 100644
--- a/spec/frontend/notes/components/discussion_resolve_with_issue_button_spec.js
+++ b/spec/frontend/notes/components/discussion_resolve_with_issue_button_spec.js
@@ -19,7 +19,7 @@ describe('ResolveWithIssueButton', () => {
wrapper.destroy();
});
- it('it should have a link with the provided link property as href', () => {
+ it('should have a link with the provided link property as href', () => {
const button = wrapper.findComponent(GlButton);
expect(button.attributes().href).toBe(url);
diff --git a/spec/frontend/notes/components/multiline_comment_form_spec.js b/spec/frontend/notes/components/multiline_comment_form_spec.js
index b027a261c15..8446bba340f 100644
--- a/spec/frontend/notes/components/multiline_comment_form_spec.js
+++ b/spec/frontend/notes/components/multiline_comment_form_spec.js
@@ -70,7 +70,7 @@ describe('MultilineCommentForm', () => {
glSelect.vm.$emit('change', { ...testLine });
expect(wrapper.vm.commentLineStart).toEqual(line);
- expect(wrapper.emitted('input')).toBeTruthy();
+ expect(wrapper.emitted('input')).toHaveLength(1);
// Once during created, once during updateCommentLineStart
expect(setSelectedCommentPosition).toHaveBeenCalledTimes(2);
});
diff --git a/spec/frontend/notes/components/note_actions/timeline_event_button_spec.js b/spec/frontend/notes/components/note_actions/timeline_event_button_spec.js
new file mode 100644
index 00000000000..658e844a9b1
--- /dev/null
+++ b/spec/frontend/notes/components/note_actions/timeline_event_button_spec.js
@@ -0,0 +1,35 @@
+import { GlButton } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import TimelineEventButton from '~/notes/components/note_actions/timeline_event_button.vue';
+
+const emitData = {
+ noteId: '1',
+ addError: 'Error promoting the note to timeline event: %{error}',
+ addGenericError: 'Something went wrong while promoting the note to timeline event.',
+};
+
+describe('NoteTimelineEventButton', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = shallowMount(TimelineEventButton, {
+ propsData: {
+ noteId: '1',
+ isPromotionInProgress: true,
+ },
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findTimelineButton = () => wrapper.findComponent(GlButton);
+
+ it('emits click-promote-comment-to-event', async () => {
+ findTimelineButton().vm.$emit('click');
+
+ expect(wrapper.emitted('click-promote-comment-to-event')).toEqual([[emitData]]);
+ expect(findTimelineButton().props('disabled')).toEqual(true);
+ });
+});
diff --git a/spec/frontend/notes/components/note_body_spec.js b/spec/frontend/notes/components/note_body_spec.js
index c2e56d3e7a7..3b5313744ff 100644
--- a/spec/frontend/notes/components/note_body_spec.js
+++ b/spec/frontend/notes/components/note_body_spec.js
@@ -74,11 +74,11 @@ describe('issue_note_body component', () => {
});
it.each`
- confidential | buttonText
- ${false} | ${'Save comment'}
- ${true} | ${'Save internal note'}
- `('renders save button with text "$buttonText"', ({ confidential, buttonText }) => {
- wrapper = createComponent({ props: { note: { ...note, confidential }, isEditing: true } });
+ internal | buttonText
+ ${false} | ${'Save comment'}
+ ${true} | ${'Save internal note'}
+ `('renders save button with text "$buttonText"', ({ internal, buttonText }) => {
+ wrapper = createComponent({ props: { note: { ...note, internal }, isEditing: true } });
expect(wrapper.findComponent(NoteForm).props('saveButtonTitle')).toBe(buttonText);
});
diff --git a/spec/frontend/notes/components/note_form_spec.js b/spec/frontend/notes/components/note_form_spec.js
index fad04e9063d..90473e7ccba 100644
--- a/spec/frontend/notes/components/note_form_spec.js
+++ b/spec/frontend/notes/components/note_form_spec.js
@@ -116,15 +116,15 @@ describe('issue_note_form component', () => {
});
it.each`
- confidential | placeholder
- ${false} | ${'Write a comment or drag your files here…'}
- ${true} | ${'Write an internal note or drag your files here…'}
+ internal | placeholder
+ ${false} | ${'Write a comment or drag your files here…'}
+ ${true} | ${'Write an internal note or drag your files here…'}
`(
- 'should set correct textarea placeholder text when discussion confidentiality is $confidential',
- ({ confidential, placeholder }) => {
+ 'should set correct textarea placeholder text when discussion confidentiality is $internal',
+ ({ internal, placeholder }) => {
props.note = {
...note,
- confidential,
+ internal,
};
wrapper = createComponentWrapper();
diff --git a/spec/frontend/notes/components/note_header_spec.js b/spec/frontend/notes/components/note_header_spec.js
index 43fbc5e26dc..76177229cff 100644
--- a/spec/frontend/notes/components/note_header_spec.js
+++ b/spec/frontend/notes/components/note_header_spec.js
@@ -3,7 +3,7 @@ import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import NoteHeader from '~/notes/components/note_header.vue';
-import { AVAILABILITY_STATUS } from '~/set_status_modal/utils';
+import { AVAILABILITY_STATUS } from '~/set_status_modal/constants';
import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue';
Vue.use(Vuex);
@@ -40,13 +40,19 @@ describe('NoteHeader component', () => {
availability: '',
};
- const createComponent = (props) => {
+ const createComponent = (props, userAttributes = false) => {
wrapper = shallowMountExtended(NoteHeader, {
store: new Vuex.Store({
actions,
}),
propsData: { ...props },
stubs: { GlSprintf, UserNameWithStatus },
+ provide: {
+ glFeatures: {
+ removeUserAttributesProjects: userAttributes,
+ removeUserAttributesGroups: userAttributes,
+ },
+ },
});
};
@@ -55,6 +61,26 @@ describe('NoteHeader component', () => {
wrapper = null;
});
+ describe('when removeUserAttributesProjects feature flag is enabled', () => {
+ it('does not render busy status', () => {
+ createComponent({ author: { ...author, availability: AVAILABILITY_STATUS.BUSY } }, true);
+
+ expect(wrapper.find('.note-header-info').text()).not.toContain('(Busy)');
+ });
+
+ it('does not render author status', () => {
+ createComponent({ author }, true);
+
+ expect(findAuthorStatus().exists()).toBe(false);
+ });
+
+ it('does not render username', () => {
+ createComponent({ author }, true);
+
+ expect(wrapper.find('.note-header-info').text()).not.toContain('@');
+ });
+ });
+
it('does not render discussion actions when includeToggle is false', () => {
createComponent({
includeToggle: false,
diff --git a/spec/frontend/notes/components/noteable_discussion_spec.js b/spec/frontend/notes/components/noteable_discussion_spec.js
index b34305688d9..2175849aeb9 100644
--- a/spec/frontend/notes/components/noteable_discussion_spec.js
+++ b/spec/frontend/notes/components/noteable_discussion_spec.js
@@ -97,7 +97,7 @@ describe('noteable_discussion component', () => {
`(
'reply button on form should have title "$saveButtonTitle" when note is $noteType',
async ({ isNoteInternal, saveButtonTitle }) => {
- wrapper.setProps({ discussion: { ...discussionMock, confidential: isNoteInternal } });
+ wrapper.setProps({ discussion: { ...discussionMock, internal: isNoteInternal } });
await nextTick();
const replyPlaceholder = wrapper.findComponent(ReplyPlaceholder);
diff --git a/spec/frontend/notes/components/noteable_note_spec.js b/spec/frontend/notes/components/noteable_note_spec.js
index e049c5bc0c8..b044d40cbe4 100644
--- a/spec/frontend/notes/components/noteable_note_spec.js
+++ b/spec/frontend/notes/components/noteable_note_spec.js
@@ -292,7 +292,7 @@ describe('issue_note', () => {
describe('internal note', () => {
it('has internal note class for internal notes', () => {
- createWrapper({ note: { ...note, confidential: true } });
+ createWrapper({ note: { ...note, internal: true } });
expect(wrapper.classes()).toContain('internal-note');
});
diff --git a/spec/frontend/notes/components/sort_discussion_spec.js b/spec/frontend/notes/components/sort_discussion_spec.js
deleted file mode 100644
index 8b6e05da3c0..00000000000
--- a/spec/frontend/notes/components/sort_discussion_spec.js
+++ /dev/null
@@ -1,102 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-import Vuex from 'vuex';
-import SortDiscussion from '~/notes/components/sort_discussion.vue';
-import { ASC, DESC } from '~/notes/constants';
-import createStore from '~/notes/stores';
-import Tracking from '~/tracking';
-import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
-
-Vue.use(Vuex);
-
-describe('Sort Discussion component', () => {
- let wrapper;
- let store;
-
- const createComponent = () => {
- jest.spyOn(store, 'dispatch').mockImplementation();
-
- wrapper = shallowMount(SortDiscussion, {
- store,
- });
- };
-
- const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
-
- beforeEach(() => {
- store = createStore();
- jest.spyOn(Tracking, 'event');
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- describe('default', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('has local storage sync with the correct props', () => {
- expect(findLocalStorageSync().props('asString')).toBe(true);
- });
-
- it('calls setDiscussionSortDirection when update is emitted', () => {
- findLocalStorageSync().vm.$emit('input', ASC);
-
- expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', { direction: ASC });
- });
- });
-
- describe('when asc', () => {
- describe('when the dropdown is clicked', () => {
- it('calls the right actions', () => {
- createComponent();
-
- wrapper.find('.js-newest-first').vm.$emit('click');
-
- expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', {
- direction: DESC,
- });
- expect(Tracking.event).toHaveBeenCalledWith(undefined, 'change_discussion_sort_direction', {
- property: DESC,
- });
- });
- });
-
- it('shows the "Oldest First" as the dropdown', () => {
- createComponent();
-
- expect(wrapper.find('.js-dropdown-text').props('text')).toBe('Oldest first');
- });
- });
-
- describe('when desc', () => {
- beforeEach(() => {
- store.state.discussionSortOrder = DESC;
- createComponent();
- });
-
- describe('when the dropdown item is clicked', () => {
- it('calls the right actions', () => {
- wrapper.find('.js-oldest-first').vm.$emit('click');
-
- expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', {
- direction: ASC,
- });
- expect(Tracking.event).toHaveBeenCalledWith(undefined, 'change_discussion_sort_direction', {
- property: ASC,
- });
- });
-
- it('sets is-checked to true on the active button in the dropdown', () => {
- expect(wrapper.find('.js-newest-first').props('isChecked')).toBe(true);
- });
- });
-
- it('shows the "Newest First" as the dropdown', () => {
- expect(wrapper.find('.js-dropdown-text').props('text')).toBe('Newest first');
- });
- });
-});
diff --git a/spec/frontend/notes/mixins/discussion_navigation_spec.js b/spec/frontend/notes/mixins/discussion_navigation_spec.js
index 35b3dec6298..1b4e8026d84 100644
--- a/spec/frontend/notes/mixins/discussion_navigation_spec.js
+++ b/spec/frontend/notes/mixins/discussion_navigation_spec.js
@@ -110,16 +110,13 @@ describe('Discussion navigation mixin', () => {
});
describe.each`
- fn | args | currentId | expected
- ${'jumpToNextDiscussion'} | ${[]} | ${null} | ${'a'}
- ${'jumpToNextDiscussion'} | ${[]} | ${'a'} | ${'c'}
- ${'jumpToNextDiscussion'} | ${[]} | ${'e'} | ${'a'}
- ${'jumpToPreviousDiscussion'} | ${[]} | ${null} | ${'e'}
- ${'jumpToPreviousDiscussion'} | ${[]} | ${'e'} | ${'c'}
- ${'jumpToPreviousDiscussion'} | ${[]} | ${'c'} | ${'a'}
- ${'jumpToNextRelativeDiscussion'} | ${[null]} | ${null} | ${'a'}
- ${'jumpToNextRelativeDiscussion'} | ${['a']} | ${null} | ${'c'}
- ${'jumpToNextRelativeDiscussion'} | ${['e']} | ${'c'} | ${'a'}
+ fn | args | currentId | expected
+ ${'jumpToNextDiscussion'} | ${[]} | ${null} | ${'a'}
+ ${'jumpToNextDiscussion'} | ${[]} | ${'a'} | ${'c'}
+ ${'jumpToNextDiscussion'} | ${[]} | ${'e'} | ${'a'}
+ ${'jumpToPreviousDiscussion'} | ${[]} | ${null} | ${'e'}
+ ${'jumpToPreviousDiscussion'} | ${[]} | ${'e'} | ${'c'}
+ ${'jumpToPreviousDiscussion'} | ${[]} | ${'c'} | ${'a'}
`('$fn (args = $args, currentId = $currentId)', ({ fn, args, currentId, expected }) => {
beforeEach(() => {
store.state.notes.currentDiscussionId = currentId;
@@ -133,19 +130,12 @@ describe('Discussion navigation mixin', () => {
await nextTick();
});
- it('sets current discussion', () => {
- expect(store.state.notes.currentDiscussionId).toEqual(expected);
- });
-
it('expands discussion', () => {
expect(expandDiscussion).toHaveBeenCalled();
});
it('scrolls to element', () => {
- expect(utils.scrollToElement).toHaveBeenCalledWith(
- findDiscussion('div.discussion', expected),
- { behavior: 'auto' },
- );
+ expect(utils.scrollToElement).toHaveBeenCalled();
});
});
@@ -172,7 +162,7 @@ describe('Discussion navigation mixin', () => {
expect(utils.scrollToElementWithContext).toHaveBeenCalledWith(
findDiscussion('ul.notes', expected),
- { behavior: 'auto' },
+ { behavior: 'auto', offset: 0 },
);
});
});
@@ -213,7 +203,7 @@ describe('Discussion navigation mixin', () => {
it('scrolls to discussion', () => {
expect(utils.scrollToElement).toHaveBeenCalledWith(
findDiscussion('div.discussion', expected),
- { behavior: 'auto' },
+ { behavior: 'auto', offset: 0 },
);
});
});
@@ -244,7 +234,6 @@ describe('Discussion navigation mixin', () => {
it.each`
tabValue
${'diffs'}
- ${'show'}
${'other'}
`(
'calls scrollToFile with setHash as $hashValue when the tab is $tabValue',
diff --git a/spec/frontend/notes/stores/actions_spec.js b/spec/frontend/notes/stores/actions_spec.js
index 02b27eca196..989dd74b6d0 100644
--- a/spec/frontend/notes/stores/actions_spec.js
+++ b/spec/frontend/notes/stores/actions_spec.js
@@ -4,6 +4,7 @@ import testAction from 'helpers/vuex_action_helper';
import { TEST_HOST } from 'spec/test_constants';
import Api from '~/api';
import createFlash 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';
import * as notesConstants from '~/notes/constants';
@@ -14,7 +15,9 @@ 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 promoteTimelineEvent from '~/notes/graphql/promote_timeline_event.mutation.graphql';
import mrWidgetEventHub from '~/vue_merge_request_widget/event_hub';
+import notesEventHub from '~/notes/event_hub';
import waitForPromises from 'helpers/wait_for_promises';
import { resetStore } from '../helpers';
import {
@@ -38,6 +41,8 @@ jest.mock('~/flash', () => {
return flash;
});
+jest.mock('~/vue_shared/plugins/global_toast');
+
describe('Actions Notes Store', () => {
let commit;
let dispatch;
@@ -1324,6 +1329,102 @@ describe('Actions Notes Store', () => {
});
});
+ describe('promoteCommentToTimelineEvent', () => {
+ const actionArgs = {
+ noteId: '1',
+ addError: 'addError: Create error',
+ addGenericError: 'addGenericError',
+ };
+ const commitSpy = jest.fn();
+
+ describe('for successful request', () => {
+ const timelineEventSuccessResponse = {
+ data: {
+ timelineEventPromoteFromNote: {
+ timelineEvent: {
+ id: 'gid://gitlab/IncidentManagement::TimelineEvent/19',
+ },
+ errors: [],
+ },
+ },
+ };
+
+ beforeEach(() => {
+ jest.spyOn(utils.gqClient, 'mutate').mockResolvedValue(timelineEventSuccessResponse);
+ });
+
+ it('calls gqClient mutation with the correct values', () => {
+ actions.promoteCommentToTimelineEvent({ commit: () => {} }, actionArgs);
+
+ expect(utils.gqClient.mutate).toHaveBeenCalledTimes(1);
+ expect(utils.gqClient.mutate).toHaveBeenCalledWith({
+ mutation: promoteTimelineEvent,
+ variables: {
+ input: {
+ noteId: 'gid://gitlab/Note/1',
+ },
+ },
+ });
+ });
+
+ it('returns success response', () => {
+ jest.spyOn(notesEventHub, '$emit').mockImplementation(() => {});
+
+ return actions.promoteCommentToTimelineEvent({ commit: commitSpy }, actionArgs).then(() => {
+ expect(notesEventHub.$emit).toHaveBeenLastCalledWith(
+ 'comment-promoted-to-timeline-event',
+ );
+ expect(toast).toHaveBeenCalledWith('Comment added to the timeline.');
+ expect(commitSpy).toHaveBeenCalledWith(
+ mutationTypes.SET_PROMOTE_COMMENT_TO_TIMELINE_PROGRESS,
+ false,
+ );
+ });
+ });
+ });
+
+ describe('for failing request', () => {
+ const errorResponse = {
+ data: {
+ timelineEventPromoteFromNote: {
+ timelineEvent: null,
+ errors: ['Create error'],
+ },
+ },
+ };
+
+ it.each`
+ mockReject | message | captureError | error
+ ${true} | ${'addGenericError'} | ${true} | ${new Error()}
+ ${false} | ${'addError: Create error'} | ${false} | ${null}
+ `(
+ 'should show an error when submission fails',
+ ({ mockReject, message, captureError, error }) => {
+ const expectedAlertArgs = {
+ captureError,
+ error,
+ message,
+ };
+ if (mockReject) {
+ jest.spyOn(utils.gqClient, 'mutate').mockRejectedValueOnce(new Error());
+ } else {
+ jest.spyOn(utils.gqClient, 'mutate').mockResolvedValue(errorResponse);
+ }
+
+ return actions
+ .promoteCommentToTimelineEvent({ commit: commitSpy }, actionArgs)
+ .then(() => {
+ expect(createFlash).toHaveBeenCalledWith(expectedAlertArgs);
+ expect(commitSpy).toHaveBeenCalledWith(
+ mutationTypes.SET_PROMOTE_COMMENT_TO_TIMELINE_PROGRESS,
+ false,
+ );
+ });
+ },
+ );
+ });
+ });
+
describe('setFetchingState', () => {
it('commits SET_NOTES_FETCHING_STATE', () => {
return testAction(
diff --git a/spec/frontend/notes/stores/getters_spec.js b/spec/frontend/notes/stores/getters_spec.js
index 6d078dcefcf..e03fa854e54 100644
--- a/spec/frontend/notes/stores/getters_spec.js
+++ b/spec/frontend/notes/stores/getters_spec.js
@@ -211,7 +211,7 @@ describe('Getters Notes Store', () => {
describe('isNotesFetched', () => {
it('should return the state for the fetching notes', () => {
- expect(getters.isNotesFetched(state)).toBeFalsy();
+ expect(getters.isNotesFetched(state)).toBe(false);
});
});
@@ -512,8 +512,8 @@ describe('Getters Notes Store', () => {
unresolvedDiscussionsIdsByDate: [],
};
- expect(getters.firstUnresolvedDiscussionId(state, localGettersFalsy)(true)).toBeFalsy();
- expect(getters.firstUnresolvedDiscussionId(state, localGettersFalsy)(false)).toBeFalsy();
+ expect(getters.firstUnresolvedDiscussionId(state, localGettersFalsy)(true)).toBeUndefined();
+ expect(getters.firstUnresolvedDiscussionId(state, localGettersFalsy)(false)).toBeUndefined();
});
});
diff --git a/spec/frontend/notes/stores/mutation_spec.js b/spec/frontend/notes/stores/mutation_spec.js
index e0a0fc43ffe..8809a496c52 100644
--- a/spec/frontend/notes/stores/mutation_spec.js
+++ b/spec/frontend/notes/stores/mutation_spec.js
@@ -74,7 +74,7 @@ describe('Notes Store mutations', () => {
});
describe('DELETE_NOTE', () => {
- it('should delete a note ', () => {
+ it('should delete a note', () => {
const state = { discussions: [discussionMock] };
const toDelete = discussionMock.notes[0];
const lengthBefore = discussionMock.notes.length;
diff --git a/spec/frontend/notifications/components/custom_notifications_modal_spec.js b/spec/frontend/notifications/components/custom_notifications_modal_spec.js
index c5d201c3aec..cd04adac72d 100644
--- a/spec/frontend/notifications/components/custom_notifications_modal_spec.js
+++ b/spec/frontend/notifications/components/custom_notifications_modal_spec.js
@@ -56,8 +56,8 @@ describe('CustomNotificationsModal', () => {
);
}
- const findModalBodyDescription = () => wrapper.find(GlSprintf);
- const findAllCheckboxes = () => wrapper.findAll(GlFormCheckbox);
+ const findModalBodyDescription = () => wrapper.findComponent(GlSprintf);
+ const findAllCheckboxes = () => wrapper.findAllComponents(GlFormCheckbox);
const findCheckboxAt = (index) => findAllCheckboxes().at(index);
beforeEach(() => {
@@ -111,7 +111,7 @@ describe('CustomNotificationsModal', () => {
const checkbox = findCheckboxAt(index);
expect(checkbox.text()).toContain(eventName);
expect(checkbox.vm.$attrs.checked).toBe(enabled);
- expect(checkbox.find(GlLoadingIcon).exists()).toBe(loading);
+ expect(checkbox.findComponent(GlLoadingIcon).exists()).toBe(loading);
},
);
});
@@ -142,7 +142,7 @@ describe('CustomNotificationsModal', () => {
wrapper = createComponent({ injectedProperties });
- wrapper.find(GlModal).vm.$emit('show');
+ wrapper.findComponent(GlModal).vm.$emit('show');
await waitForPromises();
@@ -159,7 +159,7 @@ describe('CustomNotificationsModal', () => {
wrapper = createComponent();
- wrapper.find(GlModal).vm.$emit('show');
+ wrapper.findComponent(GlModal).vm.$emit('show');
expect(wrapper.vm.isLoading).toBe(true);
await waitForPromises();
@@ -176,7 +176,7 @@ describe('CustomNotificationsModal', () => {
mockAxios.onGet('/api/v4/notification_settings').reply(httpStatus.NOT_FOUND, {});
wrapper = createComponent();
- wrapper.find(GlModal).vm.$emit('show');
+ wrapper.findComponent(GlModal).vm.$emit('show');
await waitForPromises();
@@ -197,7 +197,7 @@ describe('CustomNotificationsModal', () => {
${null} | ${1} | ${'/api/v4/groups/1/notification_settings'} | ${'group'} | ${'a groupId is given'}
${null} | ${null} | ${'/api/v4/notification_settings'} | ${'global'} | ${'neither projectId nor groupId are given'}
`(
- 'updates the $notificationType notification settings when $condition and the user clicks the checkbox ',
+ 'updates the $notificationType notification settings when $condition and the user clicks the checkbox',
async ({ projectId, groupId, endpointUrl }) => {
mockAxios
.onGet(endpointUrl)
diff --git a/spec/frontend/notifications/components/notifications_dropdown_spec.js b/spec/frontend/notifications/components/notifications_dropdown_spec.js
index 7ca6c2052ae..7a98b374095 100644
--- a/spec/frontend/notifications/components/notifications_dropdown_spec.js
+++ b/spec/frontend/notifications/components/notifications_dropdown_spec.js
@@ -40,12 +40,13 @@ describe('NotificationsDropdown', () => {
});
}
- const findDropdown = () => wrapper.find(GlDropdown);
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
const findByTestId = (testId) => wrapper.find(`[data-testid="${testId}"]`);
- const findAllNotificationsDropdownItems = () => wrapper.findAll(NotificationsDropdownItem);
+ const findAllNotificationsDropdownItems = () =>
+ wrapper.findAllComponents(NotificationsDropdownItem);
const findDropdownItemAt = (index) =>
- findAllNotificationsDropdownItems().at(index).find(GlDropdownItem);
- const findNotificationsModal = () => wrapper.find(CustomNotificationsModal);
+ findAllNotificationsDropdownItems().at(index).findComponent(GlDropdownItem);
+ const findNotificationsModal = () => wrapper.findComponent(CustomNotificationsModal);
const clickDropdownItemAt = async (index) => {
const dropdownItem = findDropdownItemAt(index);
@@ -243,7 +244,7 @@ describe('NotificationsDropdown', () => {
expect(dropdownItem.props('isChecked')).toBe(true);
});
- it("won't update the selectedNotificationLevel and shows a toast message when the request fails and ", async () => {
+ it("won't update the selectedNotificationLevel and shows a toast message when the request fails and", async () => {
mockAxios.onPut('/api/v4/notification_settings').reply(httpStatus.NOT_FOUND, {});
wrapper = createComponent();
diff --git a/spec/frontend/operation_settings/components/metrics_settings_spec.js b/spec/frontend/operation_settings/components/metrics_settings_spec.js
index 21145466016..810049220ae 100644
--- a/spec/frontend/operation_settings/components/metrics_settings_spec.js
+++ b/spec/frontend/operation_settings/components/metrics_settings_spec.js
@@ -63,7 +63,7 @@ describe('operation settings external dashboard component', () => {
describe('expand/collapse button', () => {
it('renders as an expand button by default', () => {
mountComponent();
- const button = wrapper.find(GlButton);
+ const button = wrapper.findComponent(GlButton);
expect(button.text()).toBe('Expand');
});
@@ -82,7 +82,7 @@ describe('operation settings external dashboard component', () => {
});
it('renders help page link', () => {
- const link = subHeader.find(GlLink);
+ const link = subHeader.findComponent(GlLink);
expect(link.text()).toBe('Learn more.');
expect(link.attributes().href).toBe(helpPage);
@@ -96,7 +96,7 @@ describe('operation settings external dashboard component', () => {
beforeEach(() => {
mountComponent(false);
- formGroup = wrapper.find(DashboardTimezone).find(GlFormGroup);
+ formGroup = wrapper.findComponent(DashboardTimezone).findComponent(GlFormGroup);
});
it('uses label text', () => {
@@ -117,7 +117,7 @@ describe('operation settings external dashboard component', () => {
beforeEach(() => {
mountComponent();
- select = wrapper.find(DashboardTimezone).find(GlFormSelect);
+ select = wrapper.findComponent(DashboardTimezone).findComponent(GlFormSelect);
});
it('defaults to externalDashboardUrl', () => {
@@ -132,7 +132,7 @@ describe('operation settings external dashboard component', () => {
beforeEach(() => {
mountComponent(false);
- formGroup = wrapper.find(ExternalDashboard).find(GlFormGroup);
+ formGroup = wrapper.findComponent(ExternalDashboard).findComponent(GlFormGroup);
});
it('uses label text', () => {
@@ -153,7 +153,7 @@ describe('operation settings external dashboard component', () => {
beforeEach(() => {
mountComponent();
- input = wrapper.find(ExternalDashboard).find(GlFormInput);
+ input = wrapper.findComponent(ExternalDashboard).findComponent(GlFormInput);
});
it('defaults to externalDashboardUrl', () => {
@@ -167,7 +167,7 @@ describe('operation settings external dashboard component', () => {
});
describe('submit button', () => {
- const findSubmitButton = () => wrapper.find('.settings-content form').find(GlButton);
+ const findSubmitButton = () => wrapper.find('.settings-content form').findComponent(GlButton);
const endpointRequest = [
operationsSettingsEndpoint,
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/delete_button_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/delete_button_spec.js
index ad67128502a..ff11c8843bb 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/delete_button_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/delete_button_spec.js
@@ -11,8 +11,8 @@ describe('delete_button', () => {
tooltipTitle: 'Bar tooltipTitle',
};
- const findButton = () => wrapper.find(GlButton);
- const findTooltip = () => wrapper.find(GlTooltip);
+ const findButton = () => wrapper.findComponent(GlButton);
+ const findTooltip = () => wrapper.findComponent(GlTooltip);
const mountComponent = (props) => {
wrapper = shallowMount(component, {
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 9680e273add..4a026f35822 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
@@ -13,8 +13,8 @@ import {
describe('Delete alert', () => {
let wrapper;
- const findAlert = () => wrapper.find(GlAlert);
- const findLink = () => wrapper.find(GlLink);
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findLink = () => wrapper.findComponent(GlLink);
const mountComponent = (propsData) => {
wrapper = shallowMount(component, { stubs: { GlSprintf }, propsData });
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/details_header_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/details_header_spec.js
index 9982286c625..b37edac83f7 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/details_header_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/details_header_spec.js
@@ -120,7 +120,7 @@ describe('Details Header', () => {
return waitForPromises();
});
- it('shows image.name ', () => {
+ it('shows image.name', () => {
expect(findTitle().text()).toContain('foo');
});
@@ -289,7 +289,7 @@ describe('Details Header', () => {
);
});
- describe('visibility and updated at ', () => {
+ describe('visibility and updated at', () => {
it('has last updated text', async () => {
mountComponent();
await waitForMetadataItems();
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/partial_cleanup_alert_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/partial_cleanup_alert_spec.js
index 1a27481a828..ce5ecfe4608 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/partial_cleanup_alert_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/partial_cleanup_alert_spec.js
@@ -9,7 +9,7 @@ import {
describe('Partial Cleanup alert', () => {
let wrapper;
- const findAlert = () => wrapper.find(GlAlert);
+ const findAlert = () => wrapper.findComponent(GlAlert);
const findRunLink = () => wrapper.find('[data-testid="run-link"');
const findHelpLink = () => wrapper.find('[data-testid="help-link"');
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/status_alert_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/status_alert_spec.js
index a11b102d9a6..d83a5099bcd 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/status_alert_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/status_alert_spec.js
@@ -14,8 +14,8 @@ import {
describe('Status Alert', () => {
let wrapper;
- const findLink = () => wrapper.find(GlLink);
- const findAlert = () => wrapper.find(GlAlert);
+ const findLink = () => wrapper.findComponent(GlLink);
+ const findAlert = () => wrapper.findComponent(GlAlert);
const findMessage = () => wrapper.find('[data-testid="message"]');
const mountComponent = (propsData) => {
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row_spec.js
index 84f01f10f21..96c670eaad2 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_list_row_spec.js
@@ -32,7 +32,7 @@ describe('tags list row', () => {
const findShortRevision = () => wrapper.find('[data-testid="digest"]');
const findClipboardButton = () => wrapper.findComponent(ClipboardButton);
const findTimeAgoTooltip = () => wrapper.findComponent(TimeAgoTooltip);
- const findDetailsRows = () => wrapper.findAll(DetailsRow);
+ const findDetailsRows = () => wrapper.findAllComponents(DetailsRow);
const findPublishedDateDetail = () => wrapper.find('[data-testid="published-date-detail"]');
const findManifestDetail = () => wrapper.find('[data-testid="manifest-detail"]');
const findConfigurationDetail = () => wrapper.find('[data-testid="configuration-detail"]');
@@ -359,7 +359,7 @@ describe('tags list row', () => {
mountComponent();
await nextTick();
- expect(finderFunction().find(ClipboardButton).exists()).toBe(clipboard);
+ expect(finderFunction().findComponent(ClipboardButton).exists()).toBe(clipboard);
});
it('is disabled when the component is disabled', async () => {
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_loader_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_loader_spec.js
index e5df260a260..88e79c513bc 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_loader_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/details_page/tags_loader_spec.js
@@ -5,7 +5,7 @@ import { GlSkeletonLoader } from '../../stubs';
describe('TagsLoader component', () => {
let wrapper;
- const findGlSkeletonLoaders = () => wrapper.findAll(GlSkeletonLoader);
+ const findGlSkeletonLoaders = () => wrapper.findAllComponents(GlSkeletonLoader);
const mountComponent = () => {
wrapper = shallowMount(component, {
@@ -25,7 +25,7 @@ describe('TagsLoader component', () => {
wrapper = null;
});
- it('produces the correct amount of loaders ', () => {
+ it('produces the correct amount of loaders', () => {
mountComponent();
expect(findGlSkeletonLoaders().length).toBe(1);
});
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/cleanup_status_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/cleanup_status_spec.js
index 61503d0f3bf..535faebdd4e 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/cleanup_status_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/cleanup_status_spec.js
@@ -16,7 +16,7 @@ describe('cleanup_status', () => {
let wrapper;
const findMainIcon = () => wrapper.findByTestId('main-icon');
- const findMainIconName = () => wrapper.findByTestId('main-icon').find(GlIcon);
+ const findMainIconName = () => wrapper.findByTestId('main-icon').findComponent(GlIcon);
const findExtraInfoIcon = () => wrapper.findByTestId('extra-info');
const findPopover = () => wrapper.findComponent(GlPopover);
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/cli_commands_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/cli_commands_spec.js
deleted file mode 100644
index 7727bf167fe..00000000000
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/cli_commands_spec.js
+++ /dev/null
@@ -1,85 +0,0 @@
-import { GlDropdown } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import Vue from 'vue';
-import Vuex from 'vuex';
-import QuickstartDropdown from '~/packages_and_registries/shared/components/cli_commands.vue';
-import {
- QUICK_START,
- LOGIN_COMMAND_LABEL,
- COPY_LOGIN_TITLE,
- BUILD_COMMAND_LABEL,
- COPY_BUILD_TITLE,
- PUSH_COMMAND_LABEL,
- COPY_PUSH_TITLE,
-} from '~/packages_and_registries/container_registry/explorer/constants';
-import Tracking from '~/tracking';
-import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
-
-import { dockerCommands } from '../../mock_data';
-
-Vue.use(Vuex);
-
-describe('cli_commands', () => {
- let wrapper;
-
- const findDropdownButton = () => wrapper.find(GlDropdown);
- const findCodeInstruction = () => wrapper.findAll(CodeInstruction);
-
- const mountComponent = () => {
- wrapper = mount(QuickstartDropdown, {
- propsData: {
- ...dockerCommands,
- },
- });
- };
-
- beforeEach(() => {
- jest.spyOn(Tracking, 'event');
- mountComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-
- it('shows the correct text on the button', () => {
- expect(findDropdownButton().text()).toContain(QUICK_START);
- });
-
- it('clicking on the dropdown emit a tracking event', () => {
- findDropdownButton().vm.$emit('shown');
- expect(Tracking.event).toHaveBeenCalledWith(
- undefined,
- 'click_dropdown',
- expect.objectContaining({ label: 'quickstart_dropdown' }),
- );
- });
-
- describe.each`
- index | labelText | titleText | command | trackedEvent
- ${0} | ${LOGIN_COMMAND_LABEL} | ${COPY_LOGIN_TITLE} | ${dockerCommands.dockerLoginCommand} | ${'click_copy_login'}
- ${1} | ${BUILD_COMMAND_LABEL} | ${COPY_BUILD_TITLE} | ${dockerCommands.dockerBuildCommand} | ${'click_copy_build'}
- ${2} | ${PUSH_COMMAND_LABEL} | ${COPY_PUSH_TITLE} | ${dockerCommands.dockerPushCommand} | ${'click_copy_push'}
- `('code instructions at $index', ({ index, labelText, titleText, command, trackedEvent }) => {
- let codeInstruction;
-
- beforeEach(() => {
- codeInstruction = findCodeInstruction().at(index);
- });
-
- it('exists', () => {
- expect(codeInstruction.exists()).toBe(true);
- });
-
- it(`has the correct props`, () => {
- expect(codeInstruction.props()).toMatchObject({
- label: labelText,
- instruction: command,
- copyText: titleText,
- trackingAction: trackedEvent,
- trackingLabel: 'quickstart_dropdown',
- });
- });
- });
-});
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_row_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_row_spec.js
index d12933526bc..0b59fe2d8ce 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_row_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_row_spec.js
@@ -233,7 +233,7 @@ describe('Image List Row', () => {
it('contains a tag icon', () => {
mountComponent();
- const icon = findTagsCount().find(GlIcon);
+ const icon = findTagsCount().findComponent(GlIcon);
expect(icon.exists()).toBe(true);
expect(icon.props('name')).toBe('tag');
});
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_spec.js
index e0119954ed4..042b8383571 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/image_list_spec.js
@@ -8,8 +8,8 @@ import { imagesListResponse, pageInfo as defaultPageInfo } from '../../mock_data
describe('Image List', () => {
let wrapper;
- const findRow = () => wrapper.findAll(ImageListRow);
- const findPagination = () => wrapper.find(GlKeysetPagination);
+ const findRow = () => wrapper.findAllComponents(ImageListRow);
+ const findPagination = () => wrapper.findComponent(GlKeysetPagination);
const mountComponent = (props) => {
wrapper = shallowMount(Component, {
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/registry_header_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/registry_header_spec.js
index a006de9f00c..e6d81d4a28f 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/registry_header_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/components/list_page/registry_header_spec.js
@@ -17,7 +17,7 @@ jest.mock('~/lib/utils/datetime_utility', () => ({
describe('registry_header', () => {
let wrapper;
- const findTitleArea = () => wrapper.find(TitleArea);
+ const findTitleArea = () => wrapper.findComponent(TitleArea);
const findCommandsSlot = () => wrapper.find('[data-testid="commands-slot"]');
const findImagesCountSubHeader = () => wrapper.find('[data-testid="images-count"]');
const findExpirationPolicySubHeader = () => wrapper.find('[data-testid="expiration-policy"]');
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 1d161888a4d..ee6470a9df8 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
@@ -45,16 +45,16 @@ describe('Details Page', () => {
let wrapper;
let apolloProvider;
- const findDeleteModal = () => wrapper.find(DeleteModal);
- const findPagination = () => wrapper.find(GlKeysetPagination);
- const findTagsLoader = () => wrapper.find(TagsLoader);
- const findTagsList = () => wrapper.find(TagsList);
- const findDeleteAlert = () => wrapper.find(DeleteAlert);
- const findDetailsHeader = () => wrapper.find(DetailsHeader);
- const findEmptyState = () => wrapper.find(GlEmptyState);
- const findPartialCleanupAlert = () => wrapper.find(PartialCleanupAlert);
- const findStatusAlert = () => wrapper.find(StatusAlert);
- const findDeleteImage = () => wrapper.find(DeleteImage);
+ const findDeleteModal = () => wrapper.findComponent(DeleteModal);
+ const findPagination = () => wrapper.findComponent(GlKeysetPagination);
+ const findTagsLoader = () => wrapper.findComponent(TagsLoader);
+ const findTagsList = () => wrapper.findComponent(TagsList);
+ const findDeleteAlert = () => wrapper.findComponent(DeleteAlert);
+ const findDetailsHeader = () => wrapper.findComponent(DetailsHeader);
+ const findEmptyState = () => wrapper.findComponent(GlEmptyState);
+ const findPartialCleanupAlert = () => wrapper.findComponent(PartialCleanupAlert);
+ const findStatusAlert = () => wrapper.findComponent(StatusAlert);
+ const findDeleteImage = () => wrapper.findComponent(DeleteImage);
const routeId = 1;
diff --git a/spec/frontend/packages_and_registries/container_registry/explorer/pages/index_spec.js b/spec/frontend/packages_and_registries/container_registry/explorer/pages/index_spec.js
index 5f4cb8969bc..add772d27ef 100644
--- a/spec/frontend/packages_and_registries/container_registry/explorer/pages/index_spec.js
+++ b/spec/frontend/packages_and_registries/container_registry/explorer/pages/index_spec.js
@@ -4,7 +4,7 @@ import component from '~/packages_and_registries/container_registry/explorer/pag
describe('List Page', () => {
let wrapper;
- const findRouterView = () => wrapper.find({ ref: 'router-view' });
+ const findRouterView = () => wrapper.findComponent({ ref: 'router-view' });
const mountComponent = () => {
wrapper = shallowMount(component, {
diff --git a/spec/frontend/packages_and_registries/harbor_registry/components/details/artifacts_list_row_spec.js b/spec/frontend/packages_and_registries/harbor_registry/components/details/artifacts_list_row_spec.js
new file mode 100644
index 00000000000..a2e5cbdce8b
--- /dev/null
+++ b/spec/frontend/packages_and_registries/harbor_registry/components/details/artifacts_list_row_spec.js
@@ -0,0 +1,143 @@
+import { GlSprintf } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { n__ } from '~/locale';
+import ArtifactsListRow from '~/packages_and_registries/harbor_registry/components/details/artifacts_list_row.vue';
+import RealListItem from '~/vue_shared/components/registry/list_item.vue';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import { numberToHumanSize } from '~/lib/utils/number_utils';
+import { harborArtifactsList, defaultConfig } from '../../mock_data';
+
+describe('Harbor artifact list row', () => {
+ let wrapper;
+
+ const ListItem = {
+ ...RealListItem,
+ data() {
+ return {
+ detailsSlots: [],
+ isDetailsShown: true,
+ };
+ },
+ };
+
+ const RouterLinkStub = {
+ props: {
+ to: {
+ type: Object,
+ },
+ },
+ render(createElement) {
+ return createElement('a', {}, this.$slots.default);
+ },
+ };
+
+ const findListItem = () => wrapper.findComponent(ListItem);
+ const findClipboardButton = () => wrapper.findAllComponents(ClipboardButton);
+ const findTimeAgoTooltip = () => wrapper.findComponent(TimeAgoTooltip);
+ const findByTestId = (testId) => wrapper.findByTestId(testId);
+
+ const $route = {
+ params: {
+ project: defaultConfig.harborIntegrationProjectName,
+ image: 'test-repository',
+ },
+ };
+
+ const mountComponent = ({ propsData, config = defaultConfig }) => {
+ wrapper = shallowMountExtended(ArtifactsListRow, {
+ stubs: {
+ GlSprintf,
+ ListItem,
+ 'router-link': RouterLinkStub,
+ },
+ mocks: {
+ $route,
+ },
+ propsData,
+ provide() {
+ return {
+ ...config,
+ };
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('list item', () => {
+ beforeEach(() => {
+ mountComponent({
+ propsData: {
+ artifact: harborArtifactsList[0],
+ },
+ });
+ });
+
+ it('exists', () => {
+ expect(findListItem().exists()).toBe(true);
+ });
+
+ it('has the correct artifact name', () => {
+ expect(findByTestId('name').text()).toBe(harborArtifactsList[0].digest);
+ });
+
+ it('has the correct tags count', () => {
+ const tagsCount = harborArtifactsList[0].tags.length;
+ expect(findByTestId('tags-count').text()).toBe(n__('%d tag', '%d tags', tagsCount));
+ });
+
+ it('has correct digest', () => {
+ expect(findByTestId('digest').text()).toBe('Digest: mock_sh');
+ });
+ describe('time', () => {
+ it('has the correct push time', () => {
+ expect(findByTestId('time').text()).toBe('Published');
+ expect(findTimeAgoTooltip().attributes()).toMatchObject({
+ time: harborArtifactsList[0].pushTime,
+ });
+ });
+ });
+
+ describe('clipboard button', () => {
+ it('exists', () => {
+ expect(findClipboardButton()).toHaveLength(2);
+ });
+
+ it('has the correct props', () => {
+ expect(findClipboardButton().at(0).attributes()).toMatchObject({
+ text: `docker pull demo.harbor.com/test-project/test-repository@${harborArtifactsList[0].digest}`,
+ title: `docker pull demo.harbor.com/test-project/test-repository@${harborArtifactsList[0].digest}`,
+ });
+
+ expect(findClipboardButton().at(1).attributes()).toMatchObject({
+ text: harborArtifactsList[0].digest,
+ title: harborArtifactsList[0].digest,
+ });
+ });
+ });
+
+ describe('size', () => {
+ it('calculated correctly', () => {
+ expect(findByTestId('size').text()).toBe(
+ numberToHumanSize(Number(harborArtifactsList[0].size)),
+ );
+ });
+
+ it('when size is missing', () => {
+ const artifactInfo = harborArtifactsList[0];
+ artifactInfo.size = null;
+
+ mountComponent({
+ propsData: {
+ artifact: artifactInfo,
+ },
+ });
+
+ expect(findByTestId('size').text()).toBe('0 bytes');
+ });
+ });
+ });
+});
diff --git a/spec/frontend/packages_and_registries/harbor_registry/components/details/artifacts_list_spec.js b/spec/frontend/packages_and_registries/harbor_registry/components/details/artifacts_list_spec.js
new file mode 100644
index 00000000000..b9d6dc2679e
--- /dev/null
+++ b/spec/frontend/packages_and_registries/harbor_registry/components/details/artifacts_list_spec.js
@@ -0,0 +1,75 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlEmptyState } from '@gitlab/ui';
+import TagsLoader from '~/packages_and_registries/shared/components/tags_loader.vue';
+import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue';
+import ArtifactsList from '~/packages_and_registries/harbor_registry/components/details/artifacts_list.vue';
+import ArtifactsListRow from '~/packages_and_registries/harbor_registry/components/details/artifacts_list_row.vue';
+import { defaultConfig, harborArtifactsList } from '../../mock_data';
+
+describe('Harbor artifacts list', () => {
+ let wrapper;
+
+ const findTagsLoader = () => wrapper.findComponent(TagsLoader);
+ const findGlEmptyState = () => wrapper.findComponent(GlEmptyState);
+ const findRegistryList = () => wrapper.findComponent(RegistryList);
+ const findArtifactsListRow = () => wrapper.findAllComponents(ArtifactsListRow);
+
+ const mountComponent = ({ propsData, config = defaultConfig }) => {
+ wrapper = shallowMount(ArtifactsList, {
+ propsData,
+ stubs: { RegistryList },
+ provide() {
+ return {
+ ...config,
+ };
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when isLoading is true', () => {
+ beforeEach(() => {
+ mountComponent({
+ propsData: {
+ isLoading: true,
+ pageInfo: {},
+ filter: '',
+ artifacts: [],
+ },
+ });
+ });
+
+ it('show the loader', () => {
+ expect(findTagsLoader().exists()).toBe(true);
+ });
+
+ it('does not show the list', () => {
+ expect(findGlEmptyState().exists()).toBe(false);
+ expect(findRegistryList().exists()).toBe(false);
+ });
+ });
+
+ describe('registry list', () => {
+ beforeEach(() => {
+ mountComponent({
+ propsData: {
+ isLoading: false,
+ pageInfo: {},
+ filter: '',
+ artifacts: harborArtifactsList,
+ },
+ });
+ });
+
+ it('exists', () => {
+ expect(findRegistryList().exists()).toBe(true);
+ });
+
+ it('one artifact row exist', () => {
+ expect(findArtifactsListRow()).toHaveLength(harborArtifactsList.length);
+ });
+ });
+});
diff --git a/spec/frontend/packages_and_registries/harbor_registry/components/details/details_header_spec.js b/spec/frontend/packages_and_registries/harbor_registry/components/details/details_header_spec.js
new file mode 100644
index 00000000000..e8cc2b2e22d
--- /dev/null
+++ b/spec/frontend/packages_and_registries/harbor_registry/components/details/details_header_spec.js
@@ -0,0 +1,85 @@
+import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import DetailsHeader from '~/packages_and_registries/harbor_registry/components/details/details_header.vue';
+import TitleArea from '~/vue_shared/components/registry/title_area.vue';
+import { ROOT_IMAGE_TEXT } from '~/packages_and_registries/harbor_registry/constants/index';
+
+describe('Harbor Details Header', () => {
+ let wrapper;
+
+ const findByTestId = (testId) => wrapper.find(`[data-testid="${testId}"]`);
+ const findTitle = () => findByTestId('title');
+ const findArtifactsCount = () => findByTestId('artifacts-count');
+
+ const mountComponent = ({ propsData }) => {
+ wrapper = shallowMount(DetailsHeader, {
+ propsData,
+ stubs: {
+ TitleArea,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('artifact name', () => {
+ describe('missing image name', () => {
+ beforeEach(() => {
+ mountComponent({ propsData: { imagesDetail: { name: '', artifactCount: 1 } } });
+ });
+
+ it('root image', () => {
+ expect(findTitle().text()).toBe(ROOT_IMAGE_TEXT);
+ });
+ });
+
+ describe('with artifact name present', () => {
+ beforeEach(() => {
+ mountComponent({ propsData: { imagesDetail: { name: 'shao/flinkx', artifactCount: 1 } } });
+ });
+
+ it('shows artifact.name', () => {
+ expect(findTitle().text()).toContain('shao/flinkx');
+ });
+ });
+ });
+
+ describe('metadata items', () => {
+ describe('artifacts count', () => {
+ it('displays "-- artifacts" while loading', async () => {
+ mountComponent({ propsData: { imagesDetail: {} } });
+ await nextTick();
+
+ expect(findArtifactsCount().props('text')).toBe('-- artifacts');
+ });
+
+ it('when there is more than one artifact has the correct text', async () => {
+ mountComponent({ propsData: { imagesDetail: { name: 'shao/flinkx', artifactCount: 10 } } });
+
+ await nextTick();
+
+ expect(findArtifactsCount().props('text')).toBe('10 artifacts');
+ });
+
+ it('when there is one artifact has the correct text', async () => {
+ mountComponent({
+ propsData: { imagesDetail: { name: 'shao/flinkx', artifactCount: 1 } },
+ });
+ await nextTick();
+
+ expect(findArtifactsCount().props('text')).toBe('1 artifact');
+ });
+
+ it('has the correct icon', async () => {
+ mountComponent({
+ propsData: { imagesDetail: { name: 'shao/flinkx', artifactCount: 1 } },
+ });
+ await nextTick();
+
+ expect(findArtifactsCount().props('icon')).toBe('package');
+ });
+ });
+ });
+});
diff --git a/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_header_spec.js b/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_header_spec.js
index 636f3eeb04a..7a6169d300c 100644
--- a/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_header_spec.js
+++ b/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_header_spec.js
@@ -7,14 +7,15 @@ import MetadataItem from '~/vue_shared/components/registry/metadata_item.vue';
import {
HARBOR_REGISTRY_TITLE,
LIST_INTRO_TEXT,
+ HARBOR_REGISTRY_HELP_PAGE_PATH,
} from '~/packages_and_registries/harbor_registry/constants/index';
describe('harbor_list_header', () => {
let wrapper;
- const findTitleArea = () => wrapper.find(TitleArea);
+ const findTitleArea = () => wrapper.findComponent(TitleArea);
const findCommandsSlot = () => wrapper.find('[data-testid="commands-slot"]');
- const findImagesMetaDataItem = () => wrapper.find(MetadataItem);
+ const findImagesMetaDataItem = () => wrapper.findComponent(MetadataItem);
const mountComponent = async (propsData, slots) => {
wrapper = shallowMount(HarborListHeader, {
@@ -77,10 +78,10 @@ describe('harbor_list_header', () => {
describe('info messages', () => {
describe('default message', () => {
it('is correctly bound to title_area props', () => {
- mountComponent({ helpPagePath: 'foo' });
+ mountComponent();
expect(findTitleArea().props('infoMessages')).toEqual([
- { text: LIST_INTRO_TEXT, link: 'foo' },
+ { text: LIST_INTRO_TEXT, link: HARBOR_REGISTRY_HELP_PAGE_PATH },
]);
});
});
diff --git a/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_row_spec.js b/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_row_spec.js
index 8560c4f78f7..b62d4e8836b 100644
--- a/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_row_spec.js
+++ b/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_row_spec.js
@@ -1,25 +1,24 @@
import { shallowMount, RouterLinkStub as RouterLink } from '@vue/test-utils';
-import { GlIcon, GlSprintf, GlSkeletonLoader } from '@gitlab/ui';
+import { GlIcon, GlSkeletonLoader } from '@gitlab/ui';
import HarborListRow from '~/packages_and_registries/harbor_registry/components/list/harbor_list_row.vue';
import ListItem from '~/vue_shared/components/registry/list_item.vue';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
-import { harborListResponse } from '../../mock_data';
+import { harborImagesList } from '../../mock_data';
describe('Harbor List Row', () => {
let wrapper;
- const [item] = harborListResponse.repositories;
+ const item = harborImagesList[0];
- const findDetailsLink = () => wrapper.find(RouterLink);
+ const findDetailsLink = () => wrapper.findComponent(RouterLink);
const findClipboardButton = () => wrapper.findComponent(ClipboardButton);
- const findTagsCount = () => wrapper.find('[data-testid="tags-count"]');
+ const findArtifactsCount = () => wrapper.find('[data-testid="artifacts-count"]');
const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
const mountComponent = (props) => {
wrapper = shallowMount(HarborListRow, {
stubs: {
RouterLink,
- GlSprintf,
ListItem,
},
propsData: {
@@ -42,7 +41,8 @@ describe('Harbor List Row', () => {
expect(findDetailsLink().props('to')).toMatchObject({
name: 'details',
params: {
- id: item.id,
+ image: 'nginx',
+ project: 'nginx',
},
});
});
@@ -56,17 +56,17 @@ describe('Harbor List Row', () => {
});
});
- describe('tags count', () => {
+ describe('artifacts count', () => {
it('exists', () => {
mountComponent();
- expect(findTagsCount().exists()).toBe(true);
+ expect(findArtifactsCount().exists()).toBe(true);
});
- it('contains a tag icon', () => {
+ it('contains a package icon', () => {
mountComponent();
- const icon = findTagsCount().find(GlIcon);
+ const icon = findArtifactsCount().findComponent(GlIcon);
expect(icon.exists()).toBe(true);
- expect(icon.props('name')).toBe('tag');
+ expect(icon.props('name')).toBe('package');
});
describe('loading state', () => {
@@ -76,23 +76,23 @@ describe('Harbor List Row', () => {
expect(findSkeletonLoader().exists()).toBe(true);
});
- it('hides the tags count while loading', () => {
+ it('hides the artifacts count while loading', () => {
mountComponent({ metadataLoading: true });
- expect(findTagsCount().exists()).toBe(false);
+ expect(findArtifactsCount().exists()).toBe(false);
});
});
- describe('tags count text', () => {
- it('with one tag in the image', () => {
+ describe('artifacts count text', () => {
+ it('with one artifact in the image', () => {
mountComponent({ item: { ...item, artifactCount: 1 } });
- expect(findTagsCount().text()).toMatchInterpolatedText('1 Tag');
+ expect(findArtifactsCount().text()).toMatchInterpolatedText('1 artifact');
});
- it('with more than one tag in the image', () => {
+ it('with more than one artifact in the image', () => {
mountComponent({ item: { ...item, artifactCount: 3 } });
- expect(findTagsCount().text()).toMatchInterpolatedText('3 Tags');
+ expect(findArtifactsCount().text()).toMatchInterpolatedText('3 artifacts');
});
});
});
diff --git a/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_spec.js b/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_spec.js
index f018eff58c9..e7e74a0da58 100644
--- a/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_spec.js
+++ b/spec/frontend/packages_and_registries/harbor_registry/components/list/harbor_list_spec.js
@@ -2,19 +2,19 @@ import { shallowMount } from '@vue/test-utils';
import HarborList from '~/packages_and_registries/harbor_registry/components/list/harbor_list.vue';
import HarborListRow from '~/packages_and_registries/harbor_registry/components/list/harbor_list_row.vue';
import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue';
-import { harborListResponse } from '../../mock_data';
+import { harborImagesList } from '../../mock_data';
describe('Harbor List', () => {
let wrapper;
- const findHarborListRow = () => wrapper.findAll(HarborListRow);
+ const findHarborListRow = () => wrapper.findAllComponents(HarborListRow);
const mountComponent = (props) => {
wrapper = shallowMount(HarborList, {
stubs: { RegistryList },
propsData: {
- images: harborListResponse.repositories,
- pageInfo: harborListResponse.pageInfo,
+ images: harborImagesList,
+ pageInfo: {},
...props,
},
});
@@ -28,7 +28,7 @@ describe('Harbor List', () => {
it('contains one list element for each image', () => {
mountComponent();
- expect(findHarborListRow().length).toBe(harborListResponse.repositories.length);
+ expect(findHarborListRow().length).toBe(harborImagesList.length);
});
it('passes down the metadataLoading prop', () => {
diff --git a/spec/frontend/packages_and_registries/harbor_registry/components/tags/tags_header_spec.js b/spec/frontend/packages_and_registries/harbor_registry/components/tags/tags_header_spec.js
new file mode 100644
index 00000000000..5e299a269e3
--- /dev/null
+++ b/spec/frontend/packages_and_registries/harbor_registry/components/tags/tags_header_spec.js
@@ -0,0 +1,52 @@
+import { nextTick } from 'vue';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import TagsHeader from '~/packages_and_registries/harbor_registry/components/tags/tags_header.vue';
+import TitleArea from '~/vue_shared/components/registry/title_area.vue';
+import { mockArtifactDetail, MOCK_SHA_DIGEST } from '../../mock_data';
+
+describe('Harbor Tags Header', () => {
+ let wrapper;
+
+ const findTitle = () => wrapper.findByTestId('title');
+ const findTagsCount = () => wrapper.findByTestId('tags-count');
+
+ const mountComponent = ({ propsData }) => {
+ wrapper = shallowMountExtended(TagsHeader, {
+ propsData,
+ stubs: {
+ TitleArea,
+ },
+ });
+ };
+
+ const mockPageInfo = {
+ page: 1,
+ perPage: 20,
+ total: 1,
+ totalPages: 1,
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ beforeEach(() => {
+ mountComponent({
+ propsData: { artifactDetail: mockArtifactDetail, pageInfo: mockPageInfo, tagsLoading: false },
+ });
+ });
+
+ describe('tags title', () => {
+ it('should be artifact digest', () => {
+ expect(findTitle().text()).toBe(`sha256:${MOCK_SHA_DIGEST}`);
+ });
+ });
+
+ describe('tags count', () => {
+ it('would has the correct text', async () => {
+ await nextTick();
+
+ expect(findTagsCount().props('text')).toBe('1 tag');
+ });
+ });
+});
diff --git a/spec/frontend/packages_and_registries/harbor_registry/components/tags/tags_list_row_spec.js b/spec/frontend/packages_and_registries/harbor_registry/components/tags/tags_list_row_spec.js
new file mode 100644
index 00000000000..6fe3dabc603
--- /dev/null
+++ b/spec/frontend/packages_and_registries/harbor_registry/components/tags/tags_list_row_spec.js
@@ -0,0 +1,75 @@
+import { GlSprintf } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import ListItem from '~/vue_shared/components/registry/list_item.vue';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import TagsListRow from '~/packages_and_registries/harbor_registry/components/tags/tags_list_row.vue';
+import { defaultConfig, harborTagsList } from '../../mock_data';
+
+describe('Harbor tag list row', () => {
+ let wrapper;
+
+ const findListItem = () => wrapper.find(ListItem);
+ const findClipboardButton = () => wrapper.find(ClipboardButton);
+ const findByTestId = (testId) => wrapper.findByTestId(testId);
+
+ const $route = {
+ params: {
+ project: defaultConfig.harborIntegrationProjectName,
+ image: 'test-repository',
+ },
+ };
+
+ const mountComponent = ({ propsData, config = defaultConfig }) => {
+ wrapper = shallowMountExtended(TagsListRow, {
+ stubs: {
+ ListItem,
+ GlSprintf,
+ },
+ propsData,
+ mocks: {
+ $route,
+ },
+ provide() {
+ return {
+ ...config,
+ };
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('list item', () => {
+ beforeEach(() => {
+ mountComponent({
+ propsData: {
+ tag: harborTagsList[0],
+ },
+ });
+ });
+
+ it('exists', () => {
+ expect(findListItem().exists()).toBe(true);
+ });
+
+ it('has the correct tag name', () => {
+ expect(findByTestId('name').text()).toBe(harborTagsList[0].name);
+ });
+
+ describe(' clipboard button', () => {
+ it('exists', () => {
+ expect(findClipboardButton().exists()).toBe(true);
+ });
+
+ it('has the correct props', () => {
+ const pullCommand = `docker pull demo.harbor.com/test-project/test-repository:${harborTagsList[0].name}`;
+ expect(findClipboardButton().attributes()).toMatchObject({
+ text: pullCommand,
+ title: pullCommand,
+ });
+ });
+ });
+ });
+});
diff --git a/spec/frontend/packages_and_registries/harbor_registry/components/tags/tags_list_spec.js b/spec/frontend/packages_and_registries/harbor_registry/components/tags/tags_list_spec.js
new file mode 100644
index 00000000000..6bcf6611d07
--- /dev/null
+++ b/spec/frontend/packages_and_registries/harbor_registry/components/tags/tags_list_spec.js
@@ -0,0 +1,66 @@
+import { shallowMount } from '@vue/test-utils';
+import TagsList from '~/packages_and_registries/harbor_registry/components/tags/tags_list.vue';
+import TagsLoader from '~/packages_and_registries/shared/components/tags_loader.vue';
+import TagsListRow from '~/packages_and_registries/harbor_registry/components/tags/tags_list_row.vue';
+import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue';
+import { defaultConfig, harborTagsResponse } from '../../mock_data';
+
+describe('Harbor Tags List', () => {
+ let wrapper;
+
+ const findTagsLoader = () => wrapper.find(TagsLoader);
+ const findTagsListRows = () => wrapper.findAllComponents(TagsListRow);
+ const findRegistryList = () => wrapper.find(RegistryList);
+
+ const mountComponent = ({ propsData, config = defaultConfig }) => {
+ wrapper = shallowMount(TagsList, {
+ propsData,
+ stubs: { RegistryList },
+ provide() {
+ return {
+ ...config,
+ };
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when isLoading is true', () => {
+ beforeEach(() => {
+ mountComponent({
+ propsData: {
+ isLoading: true,
+ pageInfo: {},
+ tags: [],
+ },
+ });
+ });
+
+ it('show the loader', () => {
+ expect(findTagsLoader().exists()).toBe(true);
+ });
+ });
+
+ describe('tags list', () => {
+ beforeEach(() => {
+ mountComponent({
+ propsData: {
+ isLoading: false,
+ pageInfo: {},
+ tags: harborTagsResponse,
+ },
+ });
+ });
+
+ it('should render correctly', () => {
+ expect(findRegistryList().exists()).toBe(true);
+ });
+
+ it('one tag row exists', () => {
+ expect(findTagsListRows()).toHaveLength(harborTagsResponse.length);
+ });
+ });
+});
diff --git a/spec/frontend/packages_and_registries/harbor_registry/mock_data.js b/spec/frontend/packages_and_registries/harbor_registry/mock_data.js
index 85399c22e79..b8989b6092e 100644
--- a/spec/frontend/packages_and_registries/harbor_registry/mock_data.js
+++ b/spec/frontend/packages_and_registries/harbor_registry/mock_data.js
@@ -1,175 +1,114 @@
-export const harborListResponse = {
- repositories: [
- {
- artifactCount: 1,
- creationTime: '2022-03-02T06:35:53.205Z',
- id: 25,
- name: 'shao/flinkx',
- projectId: 21,
- pullCount: 0,
- updateTime: '2022-03-02T06:35:53.205Z',
- location: 'demo.harbor.com/gitlab-cn/build/cng-images/gitlab-kas',
- },
- {
- artifactCount: 1,
- creationTime: '2022-03-02T06:35:53.205Z',
- id: 26,
- name: 'shao/flinkx1',
- projectId: 21,
- pullCount: 0,
- updateTime: '2022-03-02T06:35:53.205Z',
- location: 'demo.harbor.com/gitlab-cn/build/cng-images/gitlab-kas',
- },
- {
- artifactCount: 1,
- creationTime: '2022-03-02T06:35:53.205Z',
- id: 27,
- name: 'shao/flinkx2',
- projectId: 21,
- pullCount: 0,
- updateTime: '2022-03-02T06:35:53.205Z',
- location: 'demo.harbor.com/gitlab-cn/build/cng-images/gitlab-kas',
- },
- ],
- totalCount: 3,
- pageInfo: {
- hasNextPage: false,
- hasPreviousPage: false,
- },
+export const harborImageDetailEmptyResponse = {
+ data: null,
};
-export const harborTagsResponse = {
- tags: [
- {
- digest: 'sha256:7f386a1844faf341353e1c20f2f39f11f397604fedc475435d13f756eeb235d1',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:02310e655103823920157bc4410ea361dc638bc2cda59667d2cb1f2a988e264c',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:02310e655103823920157bc4410ea361dc638bc2cda59667d2cb1f2a988e264c',
- name: '02310e655103823920157bc4410ea361dc638bc2cda59667d2cb1f2a988e264c',
- revision: 'f53bde3d44699e04e11cf15fb415046a0913e2623d878d89bc21adb2cbda5255',
- shortRevision: 'f53bde3d4',
- createdAt: '2022-03-02T23:59:05+00:00',
- totalSize: '6623124',
- },
- {
- digest: 'sha256:4554416b84c4568fe93086620b637064ed029737aabe7308b96d50e3d9d92ed7',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:02deb4dddf177212b50e883d5e4f6c03731fad1a18cd27261736cd9dbba79160',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:02deb4dddf177212b50e883d5e4f6c03731fad1a18cd27261736cd9dbba79160',
- name: '02deb4dddf177212b50e883d5e4f6c03731fad1a18cd27261736cd9dbba79160',
- revision: 'e1fe52d8bab66d71bd54a6b8784d3b9edbc68adbd6ea87f5fa44d9974144ef9e',
- shortRevision: 'e1fe52d8b',
- createdAt: '2022-02-10T01:09:56+00:00',
- totalSize: '920760',
- },
- {
- digest: 'sha256:14f37b60e52b9ce0e9f8f7094b311d265384798592f783487c30aaa3d58e6345',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:03bc5971bab1e849ba52a20a31e7273053f22b2ddb1d04bd6b77d53a2635727a',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:03bc5971bab1e849ba52a20a31e7273053f22b2ddb1d04bd6b77d53a2635727a',
- name: '03bc5971bab1e849ba52a20a31e7273053f22b2ddb1d04bd6b77d53a2635727a',
- revision: 'c72770c6eb93c421bc496964b4bffc742b1ec2e642cdab876be7afda1856029f',
- shortRevision: 'c72770c6e',
- createdAt: '2021-12-22T04:48:48+00:00',
- totalSize: '48609053',
- },
- {
- digest: 'sha256:e925e3b8277ea23f387ed5fba5e78280cfac7cfb261a78cf046becf7b6a3faae',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:03f495bc5714bff78bb14293320d336afdf47fd47ddff0c3c5f09f8da86d5d19',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:03f495bc5714bff78bb14293320d336afdf47fd47ddff0c3c5f09f8da86d5d19',
- name: '03f495bc5714bff78bb14293320d336afdf47fd47ddff0c3c5f09f8da86d5d19',
- revision: '1ac2a43194f4e15166abdf3f26e6ec92215240490b9cac834d63de1a3d87494a',
- shortRevision: '1ac2a4319',
- createdAt: '2022-03-09T11:02:27+00:00',
- totalSize: '35141894',
- },
- {
- digest: 'sha256:7d8303fd5c077787a8c879f8f66b69e2b5605f48ccd3f286e236fb0749fcc1ca',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:05a4e58231e54b70aab2d6f22ba4fbe10e48aa4ddcbfef11c5662241c2ae4fda',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:05a4e58231e54b70aab2d6f22ba4fbe10e48aa4ddcbfef11c5662241c2ae4fda',
- name: '05a4e58231e54b70aab2d6f22ba4fbe10e48aa4ddcbfef11c5662241c2ae4fda',
- revision: 'cf8fee086701016e1a84e6824f0c896951fef4cce9d4745459558b87eec3232c',
- shortRevision: 'cf8fee086',
- createdAt: '2022-01-21T11:31:43+00:00',
- totalSize: '48716070',
- },
- {
- digest: 'sha256:b33611cefe20e4a41a6e0dce356a5d7ef3c177ea7536a58652f5b3a4f2f83549',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:093d2746876997723541aec8b88687a4cdb3b5bbb0279c5089b7891317741a9a',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:093d2746876997723541aec8b88687a4cdb3b5bbb0279c5089b7891317741a9a',
- name: '093d2746876997723541aec8b88687a4cdb3b5bbb0279c5089b7891317741a9a',
- revision: '1a4b48198b13d55242c5164e64d41c4e9f75b5d9506bc6e0efc1534dd0dd1f15',
- shortRevision: '1a4b48198',
- createdAt: '2022-01-21T11:31:51+00:00',
- totalSize: '6623127',
- },
- {
- digest: 'sha256:d25c3c020e2dbd4711a67b9fe308f4cbb7b0bb21815e722a02f91c570dc5d519',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:09698b3fae81dfd6e02554dbc82930f304a6356c8f541c80e8598a42aed985f7',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:09698b3fae81dfd6e02554dbc82930f304a6356c8f541c80e8598a42aed985f7',
- name: '09698b3fae81dfd6e02554dbc82930f304a6356c8f541c80e8598a42aed985f7',
- revision: '03e2e2777dde01c30469ee8c710973dd08a7a4f70494d7dc1583c24b525d7f61',
- shortRevision: '03e2e2777',
- createdAt: '2022-03-02T23:58:20+00:00',
- totalSize: '911377',
- },
- {
- digest: 'sha256:fb760e4d2184e9e8e39d6917534d4610fe01009734698a5653b2de1391ba28f4',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:09b830c3eaf80d547f3b523d8e242a2c411085c349dab86c520f36c7b7644f95',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:09b830c3eaf80d547f3b523d8e242a2c411085c349dab86c520f36c7b7644f95',
- name: '09b830c3eaf80d547f3b523d8e242a2c411085c349dab86c520f36c7b7644f95',
- revision: '350e78d60646bf6967244448c6aaa14d21ecb9a0c6cf87e9ff0361cbe59b9012',
- shortRevision: '350e78d60',
- createdAt: '2022-01-19T13:49:14+00:00',
- totalSize: '48710241',
- },
- {
- digest: 'sha256:407250f380cea92729cbc038c420e74900f53b852e11edc6404fe75a0fd2c402',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:0d03504a17b467eafc8c96bde70af26c74bd459a32b7eb2dd189dd6b3c121557',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:0d03504a17b467eafc8c96bde70af26c74bd459a32b7eb2dd189dd6b3c121557',
- name: '0d03504a17b467eafc8c96bde70af26c74bd459a32b7eb2dd189dd6b3c121557',
- revision: '76038370b7f3904364891457c4a6a234897255e6b9f45d0a852bf3a7e5257e18',
- shortRevision: '76038370b',
- createdAt: '2022-01-24T12:56:22+00:00',
- totalSize: '280065',
- },
- {
- digest: 'sha256:ada87f25218542951ce6720c27f3d0758e90c2540bd129f5cfb9e15b31e07b07',
- location:
- 'registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa/cache:0eb20a4a7cac2ebea821d420b3279654fe550fd8502f1785c1927aa84e5949eb',
- path:
- 'gitlab-org/gitlab/gitlab-ee-qa/cache:0eb20a4a7cac2ebea821d420b3279654fe550fd8502f1785c1927aa84e5949eb',
- name: '0eb20a4a7cac2ebea821d420b3279654fe550fd8502f1785c1927aa84e5949eb',
- revision: '3d4b49a7bbb36c48bb721f4d0e76e7950bec3878ee29cdfdd6da39f575d6d37f',
- shortRevision: '3d4b49a7b',
- createdAt: '2022-02-17T17:37:52+00:00',
- totalSize: '48655767',
- },
- ],
- totalCount: 100,
- pageInfo: {
- hasNextPage: false,
- hasPreviousPage: false,
+export const MOCK_SHA_DIGEST = 'mock_sha_digest_value';
+
+export const harborImageDetailResponse = {
+ artifactCount: 10,
+ creationTime: '2022-03-02T06:35:53.205Z',
+ id: 25,
+ name: 'shao/flinkx',
+ projectId: 21,
+ pullCount: 0,
+ updateTime: '2022-03-02T06:35:53.205Z',
+ location: 'demo.harbor.com/gitlab-cn/build/cng-images/gitlab-kas',
+};
+
+export const harborArtifactsResponse = [
+ {
+ id: 1,
+ digest: `sha256:${MOCK_SHA_DIGEST}`,
+ size: 773928,
+ push_time: '2022-05-19T15:54:47.821Z',
+ tags: ['latest'],
+ },
+];
+
+export const harborArtifactsList = [
+ {
+ id: 1,
+ digest: `sha256:${MOCK_SHA_DIGEST}`,
+ size: 773928,
+ pushTime: '2022-05-19T15:54:47.821Z',
+ tags: ['latest'],
+ },
+];
+
+export const harborTagsResponse = [
+ {
+ repository_id: 4,
+ artifact_id: 5,
+ id: 4,
+ name: 'latest',
+ pull_time: '0001-01-01T00:00:00.000Z',
+ push_time: '2022-05-27T18:21:27.903Z',
+ signed: false,
+ immutable: false,
+ },
+];
+
+export const harborTagsList = [
+ {
+ repositoryId: 4,
+ artifactId: 5,
+ id: 4,
+ name: 'latest',
+ pullTime: '0001-01-01T00:00:00.000Z',
+ pushTime: '2022-05-27T18:21:27.903Z',
+ signed: false,
+ immutable: false,
},
+];
+
+export const defaultConfig = {
+ noContainersImage: 'noContainersImage',
+ repositoryUrl: 'demo.harbor.com',
+ harborIntegrationProjectName: 'test-project',
+ projectName: 'Flight',
+ endpoint: '/flightjs/Flight/-/harbor/repositories',
+ connectionError: false,
+ invalidPathError: false,
+ isGroupPage: false,
+ containersErrorImage: 'containersErrorImage',
};
+export const defaultFullPath = 'flightjs/Flight';
+
+export const harborImagesResponse = [
+ {
+ id: 1,
+ name: 'nginx/nginx',
+ artifact_count: 1,
+ creation_time: '2022-05-29T10:07:16.812Z',
+ update_time: '2022-05-29T10:07:16.812Z',
+ project_id: 4,
+ pull_count: 0,
+ location: 'https://demo.goharbor.io/harbor/projects/4/repositories/nginx',
+ },
+];
+
+export const harborImagesList = [
+ {
+ id: 1,
+ name: 'nginx/nginx',
+ artifactCount: 1,
+ creationTime: '2022-05-29T10:07:16.812Z',
+ updateTime: '2022-05-29T10:07:16.812Z',
+ projectId: 4,
+ pullCount: 0,
+ location: 'https://demo.goharbor.io/harbor/projects/4/repositories/nginx',
+ },
+];
+
export const dockerCommands = {
dockerBuildCommand: 'foofoo',
dockerPushCommand: 'barbar',
dockerLoginCommand: 'bazbaz',
};
+
+export const mockArtifactDetail = {
+ project: 'test-project',
+ image: 'test-repository',
+ digest: `sha256:${MOCK_SHA_DIGEST}`,
+};
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
new file mode 100644
index 00000000000..8fd50bea280
--- /dev/null
+++ b/spec/frontend/packages_and_registries/harbor_registry/pages/details_spec.js
@@ -0,0 +1,162 @@
+import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import { GlFilteredSearchToken } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import HarborDetailsPage from '~/packages_and_registries/harbor_registry/pages/details.vue';
+import TagsLoader from '~/packages_and_registries/shared/components/tags_loader.vue';
+import ArtifactsList from '~/packages_and_registries/harbor_registry/components/details/artifacts_list.vue';
+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 {
+ NAME_SORT_FIELD,
+ TOKEN_TYPE_TAG_NAME,
+} from '~/packages_and_registries/harbor_registry/constants/index';
+import { harborArtifactsResponse, harborArtifactsList, defaultConfig } from '../mock_data';
+
+let mockHarborArtifactsResponse;
+
+jest.mock('~/rest_api', () => ({
+ getHarborArtifacts: () => mockHarborArtifactsResponse,
+}));
+
+describe('Harbor Details Page', () => {
+ let wrapper;
+
+ const findTagsLoader = () => wrapper.findComponent(TagsLoader);
+ const findArtifactsList = () => wrapper.findComponent(ArtifactsList);
+ const findDetailsHeader = () => wrapper.findComponent(DetailsHeader);
+ const findPersistedSearch = () => wrapper.findComponent(PersistedSearch);
+
+ const waitForHarborDetailRequest = async () => {
+ await waitForPromises();
+ await nextTick();
+ };
+
+ const $route = {
+ params: {
+ project: 'test-project',
+ image: 'test-repository',
+ },
+ };
+
+ const breadCrumbState = {
+ updateName: jest.fn(),
+ updateHref: jest.fn(),
+ };
+
+ const defaultHeaders = {
+ 'x-page': '1',
+ 'X-Per-Page': '20',
+ 'X-TOTAL': '1',
+ 'X-Total-Pages': '1',
+ };
+
+ const mountComponent = ({ config = defaultConfig } = {}) => {
+ wrapper = shallowMount(HarborDetailsPage, {
+ mocks: {
+ $route,
+ },
+ provide() {
+ return {
+ breadCrumbState,
+ ...config,
+ };
+ },
+ });
+ };
+
+ beforeEach(() => {
+ mockHarborArtifactsResponse = Promise.resolve({
+ data: harborArtifactsResponse,
+ headers: defaultHeaders,
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when isLoading is true', () => {
+ it('shows the loader', () => {
+ mountComponent();
+
+ expect(findTagsLoader().exists()).toBe(true);
+ });
+
+ it('does not show the list', () => {
+ mountComponent();
+
+ expect(findArtifactsList().exists()).toBe(false);
+ });
+ });
+
+ describe('artifacts list', () => {
+ it('exists', async () => {
+ mountComponent();
+
+ findPersistedSearch().vm.$emit('update', { sort: 'NAME_ASC', filters: [] });
+ await waitForHarborDetailRequest();
+
+ expect(findArtifactsList().exists()).toBe(true);
+ });
+
+ it('has the correct props bound', async () => {
+ mountComponent();
+
+ findPersistedSearch().vm.$emit('update', { sort: 'NAME_ASC', filters: [] });
+ await waitForHarborDetailRequest();
+
+ expect(findArtifactsList().props()).toMatchObject({
+ isLoading: false,
+ filter: '',
+ artifacts: harborArtifactsList,
+ pageInfo: {
+ page: 1,
+ perPage: 20,
+ total: 1,
+ totalPages: 1,
+ },
+ });
+ });
+ });
+
+ describe('persisted search', () => {
+ it('has the correct props', () => {
+ mountComponent();
+
+ expect(findPersistedSearch().props()).toMatchObject({
+ sortableFields: [NAME_SORT_FIELD],
+ defaultOrder: NAME_SORT_FIELD.orderBy,
+ defaultSort: 'asc',
+ tokens: [
+ {
+ type: TOKEN_TYPE_TAG_NAME,
+ icon: 'tag',
+ title: s__('HarborRegistry|Tag'),
+ unique: true,
+ token: GlFilteredSearchToken,
+ operators: OPERATOR_IS_ONLY,
+ },
+ ],
+ });
+ });
+ });
+
+ describe('header', () => {
+ it('has the correct props', async () => {
+ mountComponent();
+
+ findPersistedSearch().vm.$emit('update', { sort: 'NAME_ASC', filters: [] });
+ await waitForHarborDetailRequest();
+
+ expect(findDetailsHeader().props()).toMatchObject({
+ imagesDetail: {
+ name: 'test-project/test-repository',
+ artifactCount: 1,
+ },
+ });
+ });
+ });
+});
diff --git a/spec/frontend/packages_and_registries/harbor_registry/pages/index_spec.js b/spec/frontend/packages_and_registries/harbor_registry/pages/index_spec.js
index 55fc8066f65..942cf9bad2c 100644
--- a/spec/frontend/packages_and_registries/harbor_registry/pages/index_spec.js
+++ b/spec/frontend/packages_and_registries/harbor_registry/pages/index_spec.js
@@ -4,7 +4,7 @@ import component from '~/packages_and_registries/harbor_registry/pages/index.vue
describe('List Page', () => {
let wrapper;
- const findRouterView = () => wrapper.find({ ref: 'router-view' });
+ const findRouterView = () => wrapper.findComponent({ ref: 'router-view' });
const mountComponent = () => {
wrapper = shallowMount(component, {
diff --git a/spec/frontend/packages_and_registries/harbor_registry/pages/list_spec.js b/spec/frontend/packages_and_registries/harbor_registry/pages/list_spec.js
index 61ee36a2794..97d30e6fe99 100644
--- a/spec/frontend/packages_and_registries/harbor_registry/pages/list_spec.js
+++ b/spec/frontend/packages_and_registries/harbor_registry/pages/list_spec.js
@@ -5,15 +5,14 @@ import HarborListHeader from '~/packages_and_registries/harbor_registry/componen
import HarborRegistryList from '~/packages_and_registries/harbor_registry/pages/list.vue';
import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue';
import waitForPromises from 'helpers/wait_for_promises';
-// import { harborListResponse } from '~/packages_and_registries/harbor_registry/mock_api.js';
import HarborList from '~/packages_and_registries/harbor_registry/components/list/harbor_list.vue';
import CliCommands from '~/packages_and_registries/shared/components/cli_commands.vue';
import { SORT_FIELDS } from '~/packages_and_registries/harbor_registry/constants/index';
-import { harborListResponse, dockerCommands } from '../mock_data';
+import { harborImagesResponse, defaultConfig, harborImagesList } from '../mock_data';
let mockHarborListResponse;
-jest.mock('~/packages_and_registries/harbor_registry/mock_api.js', () => ({
- harborListResponse: () => mockHarborListResponse,
+jest.mock('~/rest_api', () => ({
+ getHarborRepositoriesList: () => mockHarborListResponse,
}));
describe('Harbor List Page', () => {
@@ -24,34 +23,43 @@ describe('Harbor List Page', () => {
await nextTick();
};
- beforeEach(() => {
- mockHarborListResponse = Promise.resolve(harborListResponse);
- });
-
const findHarborListHeader = () => wrapper.findComponent(HarborListHeader);
const findPersistedSearch = () => wrapper.findComponent(PersistedSearch);
const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
const findHarborList = () => wrapper.findComponent(HarborList);
const findCliCommands = () => wrapper.findComponent(CliCommands);
+ const defaultHeaders = {
+ 'x-page': '1',
+ 'X-Per-Page': '20',
+ 'X-TOTAL': '1',
+ 'X-Total-Pages': '1',
+ };
+
const fireFirstSortUpdate = () => {
findPersistedSearch().vm.$emit('update', { sort: 'UPDATED_DESC', filters: [] });
};
- const mountComponent = ({ config = { isGroupPage: false } } = {}) => {
+ const mountComponent = ({ config = defaultConfig } = {}) => {
wrapper = shallowMount(HarborRegistryList, {
stubs: {
HarborListHeader,
},
provide() {
return {
- config,
- ...dockerCommands,
+ ...config,
};
},
});
};
+ beforeEach(() => {
+ mockHarborListResponse = Promise.resolve({
+ data: harborImagesResponse,
+ headers: defaultHeaders,
+ });
+ });
+
afterEach(() => {
wrapper.destroy();
});
@@ -64,7 +72,7 @@ describe('Harbor List Page', () => {
expect(findHarborListHeader().exists()).toBe(true);
expect(findHarborListHeader().props()).toMatchObject({
- imagesCount: 3,
+ imagesCount: 1,
metadataLoading: false,
});
});
@@ -117,6 +125,16 @@ describe('Harbor List Page', () => {
await nextTick();
expect(findHarborList().exists()).toBe(true);
+ expect(findHarborList().props()).toMatchObject({
+ images: harborImagesList,
+ metadataLoading: false,
+ pageInfo: {
+ page: 1,
+ perPage: 20,
+ total: 1,
+ totalPages: 1,
+ },
+ });
});
});
diff --git a/spec/frontend/packages_and_registries/harbor_registry/pages/tags_spec.js b/spec/frontend/packages_and_registries/harbor_registry/pages/tags_spec.js
new file mode 100644
index 00000000000..7e0f05e736b
--- /dev/null
+++ b/spec/frontend/packages_and_registries/harbor_registry/pages/tags_spec.js
@@ -0,0 +1,125 @@
+import { nextTick } from 'vue';
+import { shallowMount } from '@vue/test-utils';
+import HarborTagsPage from '~/packages_and_registries/harbor_registry/pages/harbor_tags.vue';
+import TagsHeader from '~/packages_and_registries/harbor_registry/components/tags/tags_header.vue';
+import TagsList from '~/packages_and_registries/harbor_registry/components/tags/tags_list.vue';
+import waitForPromises from 'helpers/wait_for_promises';
+import { defaultConfig, harborTagsResponse, mockArtifactDetail } from '../mock_data';
+
+let mockHarborTagsResponse;
+
+jest.mock('~/rest_api', () => ({
+ getHarborTags: () => mockHarborTagsResponse,
+}));
+
+describe('Harbor Tags page', () => {
+ let wrapper;
+
+ const findTagsHeader = () => wrapper.find(TagsHeader);
+ const findTagsList = () => wrapper.find(TagsList);
+
+ const waitForHarborTagsRequest = async () => {
+ await waitForPromises();
+ await nextTick();
+ };
+
+ const breadCrumbState = {
+ updateName: jest.fn(),
+ updateHref: jest.fn(),
+ };
+
+ const $route = {
+ params: mockArtifactDetail,
+ };
+
+ const defaultHeaders = {
+ 'x-page': '1',
+ 'X-Per-Page': '20',
+ 'X-TOTAL': '1',
+ 'X-Total-Pages': '1',
+ };
+
+ const mountComponent = ({ endpoint = defaultConfig.endpoint } = {}) => {
+ wrapper = shallowMount(HarborTagsPage, {
+ mocks: {
+ $route,
+ },
+ provide() {
+ return {
+ breadCrumbState,
+ endpoint,
+ };
+ },
+ });
+ };
+
+ beforeEach(() => {
+ mockHarborTagsResponse = Promise.resolve({
+ data: harborTagsResponse,
+ headers: defaultHeaders,
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('contains tags header', () => {
+ mountComponent();
+
+ expect(findTagsHeader().exists()).toBe(true);
+ });
+
+ it('contains tags list', () => {
+ mountComponent();
+
+ expect(findTagsList().exists()).toBe(true);
+ });
+
+ describe('header', () => {
+ it('has the correct props', async () => {
+ mountComponent();
+
+ await waitForHarborTagsRequest();
+ expect(findTagsHeader().props()).toMatchObject({
+ artifactDetail: mockArtifactDetail,
+ pageInfo: {
+ page: 1,
+ perPage: 20,
+ total: 1,
+ totalPages: 1,
+ },
+ tagsLoading: false,
+ });
+ });
+ });
+
+ describe('list', () => {
+ it('has the correct props', async () => {
+ mountComponent();
+
+ await waitForHarborTagsRequest();
+ expect(findTagsList().props()).toMatchObject({
+ tags: [
+ {
+ repositoryId: 4,
+ artifactId: 5,
+ id: 4,
+ name: 'latest',
+ pullTime: '0001-01-01T00:00:00.000Z',
+ pushTime: '2022-05-27T18:21:27.903Z',
+ signed: false,
+ immutable: false,
+ },
+ ],
+ isLoading: false,
+ pageInfo: {
+ page: 1,
+ perPage: 20,
+ total: 1,
+ totalPages: 1,
+ },
+ });
+ });
+ });
+});
diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/app_spec.js b/spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/app_spec.js
index 69c78e64e22..e74375b7705 100644
--- a/spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/app_spec.js
+++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/app_spec.js
@@ -76,8 +76,8 @@ describe('PackagesApp', () => {
const packageTitle = () => wrapper.findComponent(TerraformTitle);
const emptyState = () => wrapper.findComponent(GlEmptyState);
const deleteButton = () => wrapper.find('.js-delete-button');
- const findDeleteModal = () => wrapper.find({ ref: 'deleteModal' });
- const findDeleteFileModal = () => wrapper.find({ ref: 'deleteFileModal' });
+ const findDeleteModal = () => wrapper.findComponent({ ref: 'deleteModal' });
+ const findDeleteFileModal = () => wrapper.findComponent({ ref: 'deleteFileModal' });
const versionsTab = () => wrapper.find('.js-versions-tab > a');
const packagesLoader = () => wrapper.findComponent(PackagesListLoader);
const packagesVersionRows = () => wrapper.findAllComponents(PackageListRow);
diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/package_files_spec.js b/spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/package_files_spec.js
index 95de2f0bb0b..b76d7c2b57b 100644
--- a/spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/package_files_spec.js
+++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/package_files_spec.js
@@ -17,8 +17,8 @@ describe('Package Files', () => {
const findFirstRowDownloadLink = () => findFirstRow().find('[data-testid="download-link"]');
const findFirstRowCommitLink = () => findFirstRow().find('[data-testid="commit-link"]');
const findSecondRowCommitLink = () => findSecondRow().find('[data-testid="commit-link"]');
- const findFirstRowFileIcon = () => findFirstRow().find(FileIcon);
- const findFirstRowCreatedAt = () => findFirstRow().find(TimeAgoTooltip);
+ const findFirstRowFileIcon = () => findFirstRow().findComponent(FileIcon);
+ const findFirstRowCreatedAt = () => findFirstRow().findComponent(TimeAgoTooltip);
const findFirstActionMenu = () => findFirstRow().findComponent(GlDropdown);
const findActionMenuDelete = () => findFirstActionMenu().find('[data-testid="delete-file"]');
const findFirstToggleDetailsButton = () => findFirstRow().findComponent(GlButton);
diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/package_history_spec.js b/spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/package_history_spec.js
index f10f05f4a0d..c6b5138639e 100644
--- a/spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/package_history_spec.js
+++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/details/components/package_history_spec.js
@@ -36,8 +36,8 @@ describe('Package History', () => {
});
const findHistoryElement = (testId) => wrapper.find(`[data-testid="${testId}"]`);
- const findElementLink = (container) => container.find(GlLink);
- const findElementTimeAgo = (container) => container.find(TimeAgoTooltip);
+ const findElementLink = (container) => container.findComponent(GlLink);
+ const findElementTimeAgo = (container) => container.findComponent(TimeAgoTooltip);
const findTitle = () => wrapper.find('[data-testid="title"]');
const findTimeline = () => wrapper.find('[data-testid="timeline"]');
diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/infrastructure_title_spec.js b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/infrastructure_title_spec.js
index 72d08d5683b..93d013bb458 100644
--- a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/infrastructure_title_spec.js
+++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/infrastructure_title_spec.js
@@ -7,8 +7,8 @@ describe('Infrastructure Title', () => {
let wrapper;
let store;
- const findTitleArea = () => wrapper.find(TitleArea);
- const findMetadataItem = () => wrapper.find(MetadataItem);
+ const findTitleArea = () => wrapper.findComponent(TitleArea);
+ const findMetadataItem = () => wrapper.findComponent(MetadataItem);
const exampleProps = { helpUrl: 'http://example.gitlab.com/help' };
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 31616e0b2f5..db1d3f3f633 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
@@ -31,9 +31,9 @@ describe('packages_list_app', () => {
const GlLoadingIcon = { name: 'gl-loading-icon', template: '<div>loading</div>' };
const emptyListHelpUrl = 'helpUrl';
- const findEmptyState = () => wrapper.find(GlEmptyState);
- const findListComponent = () => wrapper.find(PackageList);
- const findInfrastructureSearch = () => wrapper.find(InfrastructureSearch);
+ const findEmptyState = () => wrapper.findComponent(GlEmptyState);
+ const findListComponent = () => wrapper.findComponent(PackageList);
+ const findInfrastructureSearch = () => wrapper.findComponent(InfrastructureSearch);
const createStore = ({ filter = [], packageCount = 0 } = {}) => {
store = new Vuex.Store({
@@ -151,7 +151,7 @@ describe('packages_list_app', () => {
describe('empty state', () => {
it('generate the correct empty list link', () => {
- const link = findListComponent().find(GlLink);
+ const link = findListComponent().findComponent(GlLink);
expect(link.attributes('href')).toBe(emptyListHelpUrl);
expect(link.text()).toBe('publish and share your packages');
diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/packages_list_spec.js b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/packages_list_spec.js
index fed82653016..fb5ee4e6884 100644
--- a/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/packages_list_spec.js
+++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/list/components/packages_list_spec.js
@@ -20,11 +20,11 @@ describe('packages_list', () => {
const EmptySlotStub = { name: 'empty-slot-stub', template: '<div>bar</div>' };
- const findPackagesListLoader = () => wrapper.find(PackagesListLoader);
- const findPackageListPagination = () => wrapper.find(GlPagination);
- const findPackageListDeleteModal = () => wrapper.find(GlModal);
- const findEmptySlot = () => wrapper.find(EmptySlotStub);
- const findPackagesListRow = () => wrapper.find(PackagesListRow);
+ const findPackagesListLoader = () => wrapper.findComponent(PackagesListLoader);
+ const findPackageListPagination = () => wrapper.findComponent(GlPagination);
+ const findPackageListDeleteModal = () => wrapper.findComponent(GlModal);
+ const findEmptySlot = () => wrapper.findComponent(EmptySlotStub);
+ const findPackagesListRow = () => wrapper.findComponent(PackagesListRow);
const createStore = (isGroupPage, packages, isLoading) => {
const state = {
diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/shared/__snapshots__/package_list_row_spec.js.snap b/spec/frontend/packages_and_registries/infrastructure_registry/components/shared/__snapshots__/package_list_row_spec.js.snap
index 67c3b8b795a..91824dee5b0 100644
--- a/spec/frontend/packages_and_registries/infrastructure_registry/components/shared/__snapshots__/package_list_row_spec.js.snap
+++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/shared/__snapshots__/package_list_row_spec.js.snap
@@ -3,7 +3,7 @@
exports[`packages_list_row renders 1`] = `
<div
class="gl-display-flex gl-flex-direction-column gl-border-b-solid gl-border-t-solid gl-border-t-1 gl-border-b-1 gl-border-t-transparent gl-border-b-gray-100"
- data-qa-selector="package_row"
+ data-testid="package-row"
>
<div
class="gl-display-flex gl-align-items-center gl-py-3"
diff --git a/spec/frontend/packages_and_registries/infrastructure_registry/components/shared/infrastructure_icon_and_name_spec.js b/spec/frontend/packages_and_registries/infrastructure_registry/components/shared/infrastructure_icon_and_name_spec.js
index abb0d23b6e4..db90bb4c25f 100644
--- a/spec/frontend/packages_and_registries/infrastructure_registry/components/shared/infrastructure_icon_and_name_spec.js
+++ b/spec/frontend/packages_and_registries/infrastructure_registry/components/shared/infrastructure_icon_and_name_spec.js
@@ -5,7 +5,7 @@ import InfrastructureIconAndName from '~/packages_and_registries/infrastructure_
describe('InfrastructureIconAndName', () => {
let wrapper;
- const findIcon = () => wrapper.find(GlIcon);
+ const findIcon = () => wrapper.findComponent(GlIcon);
const mountComponent = () => {
wrapper = shallowMount(InfrastructureIconAndName, {});
diff --git a/spec/frontend/packages_and_registries/package_registry/components/details/nuget_installation_spec.js b/spec/frontend/packages_and_registries/package_registry/components/details/nuget_installation_spec.js
index d324d43258c..9449c40c7c6 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/details/nuget_installation_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/details/nuget_installation_spec.js
@@ -71,7 +71,7 @@ describe('NugetInstallation', () => {
});
});
- it('it has docs link', () => {
+ it('has docs link', () => {
expect(findSetupDocsLink().attributes()).toMatchObject({
href: NUGET_HELP_PATH,
target: '_blank',
diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap b/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap
index 031afa62890..5be05ddf629 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap
+++ b/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap
@@ -3,7 +3,7 @@
exports[`packages_list_row renders 1`] = `
<div
class="gl-display-flex gl-flex-direction-column gl-border-b-solid gl-border-t-solid gl-border-t-1 gl-border-b-1 gl-border-t-transparent gl-border-b-gray-100"
- data-qa-selector="package_row"
+ data-testid="package-row"
>
<div
class="gl-display-flex gl-align-items-center gl-py-3"
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 eb1e76377ff..b5a512b8806 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
@@ -30,10 +30,10 @@ describe('packages_list_row', () => {
const packageWithTags = { ...packageWithoutTags, tags: { nodes: packageTags() } };
const packageCannotDestroy = { ...packageData(), canDestroy: false };
- const findPackageTags = () => wrapper.find(PackageTags);
- const findPackagePath = () => wrapper.find(PackagePath);
+ const findPackageTags = () => wrapper.findComponent(PackageTags);
+ const findPackagePath = () => wrapper.findComponent(PackagePath);
const findDeleteDropdown = () => wrapper.findByTestId('action-delete');
- const findPackageIconAndName = () => wrapper.find(PackageIconAndName);
+ const findPackageIconAndName = () => wrapper.findComponent(PackageIconAndName);
const findPackageLink = () => wrapper.findByTestId('details-link');
const findWarningIcon = () => wrapper.findByTestId('warning-icon');
const findLeftSecondaryInfos = () => wrapper.findByTestId('left-secondary-infos');
diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js b/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js
index 660f00a2b31..3e3607a361c 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js
@@ -190,7 +190,7 @@ describe('packages_list', () => {
});
});
- describe('pagination ', () => {
+ describe('pagination', () => {
beforeEach(() => {
mountComponent({ pageInfo: { hasPreviousPage: true } });
});
diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/packages_title_spec.js b/spec/frontend/packages_and_registries/package_registry/components/list/packages_title_spec.js
index 23e5c7330d5..b47515e15c3 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/list/packages_title_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/list/packages_title_spec.js
@@ -7,8 +7,8 @@ describe('PackageTitle', () => {
let wrapper;
let store;
- const findTitleArea = () => wrapper.find(TitleArea);
- const findMetadataItem = () => wrapper.find(MetadataItem);
+ const findTitleArea = () => wrapper.findComponent(TitleArea);
+ const findMetadataItem = () => wrapper.findComponent(MetadataItem);
const mountComponent = (propsData = { helpUrl: 'foo' }) => {
wrapper = shallowMount(PackageTitle, {
diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/tokens/package_type_token_spec.js b/spec/frontend/packages_and_registries/package_registry/components/list/tokens/package_type_token_spec.js
index d0c111bae2d..8f3c8667c47 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/list/tokens/package_type_token_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/list/tokens/package_type_token_spec.js
@@ -6,8 +6,8 @@ import { PACKAGE_TYPES } from '~/packages_and_registries/package_registry/consta
describe('packages_filter', () => {
let wrapper;
- const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
- const findFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
+ const findFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken);
+ const findFilteredSearchSuggestions = () => wrapper.findAllComponents(GlFilteredSearchSuggestion);
const mountComponent = ({ attrs, listeners } = {}) => {
wrapper = shallowMount(component, {
@@ -24,13 +24,13 @@ describe('packages_filter', () => {
wrapper = null;
});
- it('it binds all of his attrs to filtered search token', () => {
+ it('binds all of his attrs to filtered search token', () => {
mountComponent({ attrs: { foo: 'bar' } });
expect(findFilteredSearchToken().attributes('foo')).toBe('bar');
});
- it('it binds all of his events to filtered search token', () => {
+ it('binds all of his events to filtered search token', () => {
const clickListener = jest.fn();
mountComponent({ listeners: { click: clickListener } });
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 de78e6bb87b..83158d1cc5e 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
@@ -178,7 +178,7 @@ describe('PackagesApp', () => {
${PACKAGE_TYPE_PYPI} | ${true}
${PACKAGE_TYPE_NPM} | ${false}
`(
- `It is $visible that the component is visible when the package is $packageType`,
+ `is $visible that the component is visible when the package is $packageType`,
async ({ packageType, visible }) => {
createComponent({
resolver: jest.fn().mockResolvedValue(
@@ -328,8 +328,8 @@ describe('PackagesApp', () => {
findPackageFiles().vm.$emit('delete-files', [fileToDelete]);
- expect(showDeletePackageSpy).not.toBeCalled();
- expect(showDeleteFileSpy).toBeCalled();
+ expect(showDeletePackageSpy).not.toHaveBeenCalled();
+ expect(showDeleteFileSpy).toHaveBeenCalled();
});
it('when its the only file opens delete package confirmation modal', async () => {
@@ -357,8 +357,8 @@ describe('PackagesApp', () => {
findPackageFiles().vm.$emit('delete-files', [fileToDelete]);
- expect(showDeletePackageSpy).toBeCalled();
- expect(showDeleteFileSpy).not.toBeCalled();
+ expect(showDeletePackageSpy).toHaveBeenCalled();
+ expect(showDeleteFileSpy).not.toHaveBeenCalled();
});
it('confirming on the modal sets the loading state', async () => {
@@ -443,7 +443,7 @@ describe('PackagesApp', () => {
findPackageFiles().vm.$emit('delete-files', packageFiles());
- expect(showDeleteFilesSpy).toBeCalled();
+ expect(showDeleteFilesSpy).toHaveBeenCalled();
});
it('confirming on the modal sets the loading state', async () => {
@@ -532,7 +532,7 @@ describe('PackagesApp', () => {
findPackageFiles().vm.$emit('delete-files', packageFiles());
- expect(showDeletePackageSpy).toBeCalled();
+ expect(showDeletePackageSpy).toHaveBeenCalled();
});
});
});
diff --git a/spec/frontend/packages_and_registries/settings/group/components/__snapshots__/settings_titles_spec.js.snap b/spec/frontend/packages_and_registries/settings/group/components/__snapshots__/settings_titles_spec.js.snap
deleted file mode 100644
index 5b56cb7f74e..00000000000
--- a/spec/frontend/packages_and_registries/settings/group/components/__snapshots__/settings_titles_spec.js.snap
+++ /dev/null
@@ -1,18 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`settings_titles renders properly 1`] = `
-<div>
- <h5
- class="gl-border-b-solid gl-border-b-1 gl-border-gray-200 gl-pb-3"
- >
-
- foo
-
- </h5>
-
- <p>
- bar
- </p>
-
-</div>
-`;
diff --git a/spec/frontend/packages_and_registries/settings/group/components/dependency_proxy_settings_spec.js b/spec/frontend/packages_and_registries/settings/group/components/dependency_proxy_settings_spec.js
index 9d4c7f4737b..796d89231f4 100644
--- a/spec/frontend/packages_and_registries/settings/group/components/dependency_proxy_settings_spec.js
+++ b/spec/frontend/packages_and_registries/settings/group/components/dependency_proxy_settings_spec.js
@@ -169,7 +169,7 @@ describe('DependencyProxySettings', () => {
toggleName | toggleFinder | localErrorMock | optimisticResponse
${'enable proxy'} | ${findEnableProxyToggle} | ${dependencyProxySettingMutationMock} | ${updateGroupDependencyProxySettingsOptimisticResponse}
${'enable ttl policies'} | ${findEnableTtlPoliciesToggle} | ${dependencyProxyUpdateTllPolicyMutationMock} | ${updateDependencyProxyImageTtlGroupPolicyOptimisticResponse}
- `('$toggleName settings update ', ({ optimisticResponse, toggleFinder, localErrorMock }) => {
+ `('$toggleName settings update', ({ optimisticResponse, toggleFinder, localErrorMock }) => {
describe('success state', () => {
it('emits a success event', async () => {
mountComponent();
diff --git a/spec/frontend/packages_and_registries/settings/group/components/duplicates_settings_spec.js b/spec/frontend/packages_and_registries/settings/group/components/duplicates_settings_spec.js
deleted file mode 100644
index 3eecdeb5b1f..00000000000
--- a/spec/frontend/packages_and_registries/settings/group/components/duplicates_settings_spec.js
+++ /dev/null
@@ -1,143 +0,0 @@
-import { GlSprintf, GlToggle, GlFormGroup, GlFormInput } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import component from '~/packages_and_registries/settings/group/components/duplicates_settings.vue';
-
-import {
- DUPLICATES_TOGGLE_LABEL,
- DUPLICATES_SETTING_EXCEPTION_TITLE,
- DUPLICATES_SETTINGS_EXCEPTION_LEGEND,
-} from '~/packages_and_registries/settings/group/constants';
-
-describe('Duplicates Settings', () => {
- let wrapper;
-
- const defaultProps = {
- duplicatesAllowed: false,
- duplicateExceptionRegex: 'foo',
- modelNames: {
- allowed: 'allowedModel',
- exception: 'exceptionModel',
- },
- };
-
- const mountComponent = (propsData = defaultProps) => {
- wrapper = shallowMount(component, {
- propsData,
- stubs: {
- GlSprintf,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findToggle = () => wrapper.findComponent(GlToggle);
-
- const findInputGroup = () => wrapper.findComponent(GlFormGroup);
- const findInput = () => wrapper.findComponent(GlFormInput);
-
- it('has a toggle', () => {
- mountComponent();
-
- expect(findToggle().exists()).toBe(true);
- expect(findToggle().props()).toMatchObject({
- label: DUPLICATES_TOGGLE_LABEL,
- value: !defaultProps.duplicatesAllowed,
- });
- });
-
- it('toggle emits an update event', () => {
- mountComponent();
-
- findToggle().vm.$emit('change', false);
-
- expect(wrapper.emitted('update')).toStrictEqual([
- [{ [defaultProps.modelNames.allowed]: true }],
- ]);
- });
-
- describe('when the duplicates are disabled', () => {
- it('shows a form group with an input field', () => {
- mountComponent();
-
- expect(findInputGroup().exists()).toBe(true);
-
- expect(findInputGroup().attributes()).toMatchObject({
- 'label-for': 'maven-duplicated-settings-regex-input',
- label: DUPLICATES_SETTING_EXCEPTION_TITLE,
- description: DUPLICATES_SETTINGS_EXCEPTION_LEGEND,
- });
- });
-
- it('shows an input field', () => {
- mountComponent();
-
- expect(findInput().exists()).toBe(true);
-
- expect(findInput().attributes()).toMatchObject({
- id: 'maven-duplicated-settings-regex-input',
- value: defaultProps.duplicateExceptionRegex,
- });
- });
-
- it('input change event emits an update event', () => {
- mountComponent();
-
- findInput().vm.$emit('change', 'bar');
-
- expect(wrapper.emitted('update')).toStrictEqual([
- [{ [defaultProps.modelNames.exception]: 'bar' }],
- ]);
- });
-
- describe('valid state', () => {
- it('form group has correct props', () => {
- mountComponent();
-
- expect(findInputGroup().attributes()).toMatchObject({
- state: 'true',
- 'invalid-feedback': '',
- });
- });
- });
-
- describe('invalid state', () => {
- it('form group has correct props', () => {
- const propsWithError = {
- ...defaultProps,
- duplicateExceptionRegexError: 'some error string',
- };
-
- mountComponent(propsWithError);
-
- expect(findInputGroup().attributes()).toMatchObject({
- 'invalid-feedback': propsWithError.duplicateExceptionRegexError,
- });
- });
- });
- });
-
- describe('when the duplicates are enabled', () => {
- it('hides the form input group', () => {
- mountComponent({ ...defaultProps, duplicatesAllowed: true });
-
- expect(findInputGroup().exists()).toBe(false);
- });
- });
-
- describe('loading', () => {
- beforeEach(() => {
- mountComponent({ ...defaultProps, loading: true });
- });
-
- it('disables the enable toggle', () => {
- expect(findToggle().props('disabled')).toBe(true);
- });
-
- it('disables the form input', () => {
- expect(findInput().attributes('disabled')).toBe('true');
- });
- });
-});
diff --git a/spec/frontend/packages_and_registries/settings/group/components/exceptions_input_spec.js b/spec/frontend/packages_and_registries/settings/group/components/exceptions_input_spec.js
new file mode 100644
index 00000000000..86f14961690
--- /dev/null
+++ b/spec/frontend/packages_and_registries/settings/group/components/exceptions_input_spec.js
@@ -0,0 +1,108 @@
+import { GlSprintf, GlFormGroup, GlFormInput } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import component from '~/packages_and_registries/settings/group/components/exceptions_input.vue';
+
+import { DUPLICATES_SETTING_EXCEPTION_TITLE } from '~/packages_and_registries/settings/group/constants';
+
+describe('Exceptions Input', () => {
+ let wrapper;
+
+ const defaultProps = {
+ duplicatesAllowed: false,
+ duplicateExceptionRegex: 'foo',
+ id: 'maven-duplicated-settings-regex-input',
+ name: 'exceptionModel',
+ };
+
+ const mountComponent = (propsData = defaultProps) => {
+ wrapper = shallowMount(component, {
+ propsData,
+ stubs: {
+ GlSprintf,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findInputGroup = () => wrapper.findComponent(GlFormGroup);
+ const findInput = () => wrapper.findComponent(GlFormInput);
+
+ it('shows a form group with an input field', () => {
+ mountComponent();
+
+ expect(findInputGroup().exists()).toBe(true);
+
+ expect(findInputGroup().attributes()).toMatchObject({
+ 'label-for': defaultProps.id,
+ label: DUPLICATES_SETTING_EXCEPTION_TITLE,
+ 'label-sr-only': '',
+ });
+ });
+
+ it('shows an input field', () => {
+ mountComponent();
+
+ expect(findInput().exists()).toBe(true);
+
+ expect(findInput().attributes()).toMatchObject({
+ id: 'maven-duplicated-settings-regex-input',
+ value: defaultProps.duplicateExceptionRegex,
+ });
+ });
+
+ it('input change event emits an update event', () => {
+ mountComponent();
+
+ findInput().vm.$emit('change', 'bar');
+
+ expect(wrapper.emitted('update')).toStrictEqual([[{ [defaultProps.name]: 'bar' }]]);
+ });
+
+ describe('valid state', () => {
+ beforeEach(() => {
+ mountComponent();
+ });
+
+ it('form group has correct props', () => {
+ expect(findInputGroup().attributes('input-feedback')).toBeUndefined();
+ });
+
+ it('form input has correct props', () => {
+ expect(findInput().attributes('state')).toBe('true');
+ });
+ });
+
+ describe('invalid state', () => {
+ const propsWithError = {
+ ...defaultProps,
+ duplicateExceptionRegexError: 'some error string',
+ };
+
+ beforeEach(() => {
+ mountComponent(propsWithError);
+ });
+
+ it('form group has correct props', () => {
+ expect(findInputGroup().attributes('invalid-feedback')).toBe(
+ propsWithError.duplicateExceptionRegexError,
+ );
+ });
+
+ it('form input has correct props', () => {
+ expect(findInput().attributes('state')).toBeUndefined();
+ });
+ });
+
+ describe('loading', () => {
+ beforeEach(() => {
+ mountComponent({ ...defaultProps, loading: true });
+ });
+
+ it('disables the form input', () => {
+ expect(findInput().attributes('disabled')).toBe('true');
+ });
+ });
+});
diff --git a/spec/frontend/packages_and_registries/settings/group/components/generic_settings_spec.js b/spec/frontend/packages_and_registries/settings/group/components/generic_settings_spec.js
deleted file mode 100644
index 4eafeedd55e..00000000000
--- a/spec/frontend/packages_and_registries/settings/group/components/generic_settings_spec.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import GenericSettings from '~/packages_and_registries/settings/group/components/generic_settings.vue';
-import SettingsTitles from '~/packages_and_registries/settings/group/components/settings_titles.vue';
-
-describe('generic_settings', () => {
- let wrapper;
-
- const mountComponent = () => {
- wrapper = shallowMount(GenericSettings, {
- scopedSlots: {
- default: '<div data-testid="default-slot">{{props.modelNames}}</div>',
- },
- });
- };
-
- const findSettingsTitle = () => wrapper.findComponent(SettingsTitles);
- const findDefaultSlot = () => wrapper.find('[data-testid="default-slot"]');
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('title component', () => {
- it('has a title component', () => {
- mountComponent();
-
- expect(findSettingsTitle().exists()).toBe(true);
- });
-
- it('passes the correct props', () => {
- mountComponent();
-
- expect(findSettingsTitle().props()).toMatchObject({
- title: 'Generic',
- subTitle: 'Settings for Generic packages',
- });
- });
- });
-
- describe('default slot', () => {
- it('accept a default slots', () => {
- mountComponent();
-
- expect(findDefaultSlot().exists()).toBe(true);
- });
-
- it('binds model names', () => {
- mountComponent();
-
- expect(findDefaultSlot().text()).toContain('genericDuplicatesAllowed');
- expect(findDefaultSlot().text()).toContain('genericDuplicateExceptionRegex');
- });
- });
-});
diff --git a/spec/frontend/packages_and_registries/settings/group/components/maven_settings_spec.js b/spec/frontend/packages_and_registries/settings/group/components/maven_settings_spec.js
deleted file mode 100644
index 22644b97b43..00000000000
--- a/spec/frontend/packages_and_registries/settings/group/components/maven_settings_spec.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import MavenSettings from '~/packages_and_registries/settings/group/components/maven_settings.vue';
-import SettingsTitles from '~/packages_and_registries/settings/group/components/settings_titles.vue';
-
-describe('maven_settings', () => {
- let wrapper;
-
- const mountComponent = () => {
- wrapper = shallowMount(MavenSettings, {
- scopedSlots: {
- default: '<div data-testid="default-slot">{{props.modelNames}}</div>',
- },
- });
- };
-
- const findSettingsTitle = () => wrapper.findComponent(SettingsTitles);
- const findDefaultSlot = () => wrapper.find('[data-testid="default-slot"]');
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('title component', () => {
- it('has a title component', () => {
- mountComponent();
-
- expect(findSettingsTitle().exists()).toBe(true);
- });
-
- it('passes the correct props', () => {
- mountComponent();
-
- expect(findSettingsTitle().props()).toMatchObject({
- title: 'Maven',
- subTitle: 'Settings for Maven packages',
- });
- });
- });
-
- describe('default slot', () => {
- it('accept a default slots', () => {
- mountComponent();
-
- expect(findDefaultSlot().exists()).toBe(true);
- });
-
- it('binds model names', () => {
- mountComponent();
-
- expect(findDefaultSlot().text()).toContain('mavenDuplicatesAllowed');
- expect(findDefaultSlot().text()).toContain('mavenDuplicateExceptionRegex');
- });
- });
-});
diff --git a/spec/frontend/packages_and_registries/settings/group/components/package_settings_spec.js b/spec/frontend/packages_and_registries/settings/group/components/package_settings_spec.js
index 274930ce668..13eba39ec8c 100644
--- a/spec/frontend/packages_and_registries/settings/group/components/package_settings_spec.js
+++ b/spec/frontend/packages_and_registries/settings/group/components/package_settings_spec.js
@@ -1,13 +1,13 @@
import Vue, { nextTick } from 'vue';
+import { GlToggle } from '@gitlab/ui';
import VueApollo from 'vue-apollo';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import DuplicatesSettings from '~/packages_and_registries/settings/group/components/duplicates_settings.vue';
-import GenericSettings from '~/packages_and_registries/settings/group/components/generic_settings.vue';
+import ExceptionsInput from '~/packages_and_registries/settings/group/components/exceptions_input.vue';
import component from '~/packages_and_registries/settings/group/components/packages_settings.vue';
-import MavenSettings from '~/packages_and_registries/settings/group/components/maven_settings.vue';
import {
+ DUPLICATES_TOGGLE_LABEL,
PACKAGE_SETTINGS_HEADER,
PACKAGE_SETTINGS_DESCRIPTION,
} from '~/packages_and_registries/settings/group/constants';
@@ -35,6 +35,7 @@ describe('Packages Settings', () => {
};
const mountComponent = ({
+ mountFn = shallowMountExtended,
mutationResolver = jest.fn().mockResolvedValue(groupPackageSettingsMutationMock()),
} = {}) => {
Vue.use(VueApollo);
@@ -43,7 +44,7 @@ describe('Packages Settings', () => {
apolloProvider = createMockApollo(requestHandlers);
- wrapper = shallowMountExtended(component, {
+ wrapper = mountFn(component, {
apolloProvider,
provide: defaultProvide,
propsData: {
@@ -51,8 +52,6 @@ describe('Packages Settings', () => {
},
stubs: {
SettingsBlock,
- MavenSettings,
- GenericSettings,
},
});
};
@@ -63,11 +62,15 @@ describe('Packages Settings', () => {
const findSettingsBlock = () => wrapper.findComponent(SettingsBlock);
const findDescription = () => wrapper.findByTestId('description');
- const findMavenSettings = () => wrapper.findComponent(MavenSettings);
- const findMavenDuplicatedSettings = () => findMavenSettings().findComponent(DuplicatesSettings);
- const findGenericSettings = () => wrapper.findComponent(GenericSettings);
- const findGenericDuplicatedSettings = () =>
- findGenericSettings().findComponent(DuplicatesSettings);
+ const findMavenSettings = () => wrapper.findByTestId('maven-settings');
+ const findGenericSettings = () => wrapper.findByTestId('generic-settings');
+
+ const findMavenDuplicatedSettingsToggle = () => findMavenSettings().findComponent(GlToggle);
+ const findGenericDuplicatedSettingsToggle = () => findGenericSettings().findComponent(GlToggle);
+ const findMavenDuplicatedSettingsExceptionsInput = () =>
+ findMavenSettings().findComponent(ExceptionsInput);
+ const findGenericDuplicatedSettingsExceptionsInput = () =>
+ findGenericSettings().findComponent(ExceptionsInput);
const fillApolloCache = () => {
apolloProvider.defaultClient.cache.writeQuery({
@@ -80,7 +83,7 @@ describe('Packages Settings', () => {
};
const emitMavenSettingsUpdate = (override) => {
- findMavenDuplicatedSettings().vm.$emit('update', {
+ findGenericDuplicatedSettingsExceptionsInput().vm.$emit('update', {
mavenDuplicateExceptionRegex: ')',
...override,
});
@@ -106,27 +109,46 @@ describe('Packages Settings', () => {
describe('maven settings', () => {
it('exists', () => {
- mountComponent();
+ mountComponent({ mountFn: mountExtended });
+
+ expect(findMavenSettings().find('td').text()).toBe('Maven');
+ });
+
+ it('renders toggle', () => {
+ mountComponent({ mountFn: mountExtended });
- expect(findMavenSettings().exists()).toBe(true);
+ const { mavenDuplicatesAllowed } = packageSettings();
+
+ expect(findMavenDuplicatedSettingsToggle().exists()).toBe(true);
+
+ expect(findMavenDuplicatedSettingsToggle().props()).toMatchObject({
+ label: DUPLICATES_TOGGLE_LABEL,
+ value: mavenDuplicatesAllowed,
+ disabled: false,
+ labelPosition: 'hidden',
+ });
});
- it('assigns duplication allowness and exception props', async () => {
- mountComponent();
+ it('renders ExceptionsInput and assigns duplication allowness and exception props', () => {
+ mountComponent({ mountFn: mountExtended });
const { mavenDuplicatesAllowed, mavenDuplicateExceptionRegex } = packageSettings();
- expect(findMavenDuplicatedSettings().props()).toMatchObject({
+ expect(findMavenDuplicatedSettingsExceptionsInput().exists()).toBe(true);
+
+ expect(findMavenDuplicatedSettingsExceptionsInput().props()).toMatchObject({
duplicatesAllowed: mavenDuplicatesAllowed,
duplicateExceptionRegex: mavenDuplicateExceptionRegex,
duplicateExceptionRegexError: '',
loading: false,
+ name: 'mavenDuplicateExceptionRegex',
+ id: 'maven-duplicated-settings-regex-input',
});
});
it('on update event calls the mutation', () => {
const mutationResolver = jest.fn().mockResolvedValue(groupPackageSettingsMutationMock());
- mountComponent({ mutationResolver });
+ mountComponent({ mountFn: mountExtended, mutationResolver });
fillApolloCache();
@@ -140,31 +162,47 @@ describe('Packages Settings', () => {
describe('generic settings', () => {
it('exists', () => {
- mountComponent();
+ mountComponent({ mountFn: mountExtended });
- expect(findGenericSettings().exists()).toBe(true);
+ expect(findGenericSettings().find('td').text()).toBe('Generic');
});
- it('assigns duplication allowness and exception props', async () => {
- mountComponent();
+ it('renders toggle', () => {
+ mountComponent({ mountFn: mountExtended });
+
+ const { genericDuplicatesAllowed } = packageSettings();
+
+ expect(findGenericDuplicatedSettingsToggle().exists()).toBe(true);
+ expect(findGenericDuplicatedSettingsToggle().props()).toMatchObject({
+ label: DUPLICATES_TOGGLE_LABEL,
+ value: genericDuplicatesAllowed,
+ disabled: false,
+ labelPosition: 'hidden',
+ });
+ });
+
+ it('renders ExceptionsInput and assigns duplication allowness and exception props', async () => {
+ mountComponent({ mountFn: mountExtended });
const { genericDuplicatesAllowed, genericDuplicateExceptionRegex } = packageSettings();
- expect(findGenericDuplicatedSettings().props()).toMatchObject({
+ expect(findGenericDuplicatedSettingsExceptionsInput().props()).toMatchObject({
duplicatesAllowed: genericDuplicatesAllowed,
duplicateExceptionRegex: genericDuplicateExceptionRegex,
duplicateExceptionRegexError: '',
loading: false,
+ name: 'genericDuplicateExceptionRegex',
+ id: 'generic-duplicated-settings-regex-input',
});
});
it('on update event calls the mutation', async () => {
const mutationResolver = jest.fn().mockResolvedValue(groupPackageSettingsMutationMock());
- mountComponent({ mutationResolver });
+ mountComponent({ mountFn: mountExtended, mutationResolver });
fillApolloCache();
- findMavenDuplicatedSettings().vm.$emit('update', {
+ findGenericDuplicatedSettingsExceptionsInput().vm.$emit('update', {
genericDuplicateExceptionRegex: ')',
});
@@ -176,9 +214,11 @@ describe('Packages Settings', () => {
describe('settings update', () => {
describe('success state', () => {
- it('emits a success event', async () => {
- mountComponent();
+ beforeEach(() => {
+ mountComponent({ mountFn: mountExtended });
+ });
+ it('emits a success event', async () => {
fillApolloCache();
emitMavenSettingsUpdate();
@@ -189,11 +229,12 @@ describe('Packages Settings', () => {
it('has an optimistic response', () => {
const mavenDuplicateExceptionRegex = 'latest[main]something';
- mountComponent();
fillApolloCache();
- expect(findMavenDuplicatedSettings().props('duplicateExceptionRegex')).toBe('');
+ expect(
+ findGenericDuplicatedSettingsExceptionsInput().props('duplicateExceptionRegex'),
+ ).toBe('');
emitMavenSettingsUpdate({ mavenDuplicateExceptionRegex });
@@ -209,7 +250,7 @@ describe('Packages Settings', () => {
// note this is a complex test that covers all the path around errors that are shown in the form
// it's one single it case, due to the expensive preparation and execution
const mutationResolver = jest.fn().mockResolvedValue(groupPackageSettingsMutationErrorMock);
- mountComponent({ mutationResolver });
+ mountComponent({ mountFn: mountExtended, mutationResolver });
fillApolloCache();
@@ -218,9 +259,9 @@ describe('Packages Settings', () => {
await waitForPromises();
// errors are bound to the component
- expect(findMavenDuplicatedSettings().props('duplicateExceptionRegexError')).toBe(
- groupPackageSettingsMutationErrorMock.errors[0].extensions.problems[0].message,
- );
+ expect(
+ findMavenDuplicatedSettingsExceptionsInput().props('duplicateExceptionRegexError'),
+ ).toBe(groupPackageSettingsMutationErrorMock.errors[0].extensions.problems[0].message);
// general error message is shown
@@ -231,7 +272,9 @@ describe('Packages Settings', () => {
await nextTick();
// errors are reset on mutation call
- expect(findMavenDuplicatedSettings().props('duplicateExceptionRegexError')).toBe('');
+ expect(
+ findMavenDuplicatedSettingsExceptionsInput().props('duplicateExceptionRegexError'),
+ ).toBe('');
});
it.each`
@@ -239,7 +282,7 @@ describe('Packages Settings', () => {
${'local'} | ${jest.fn().mockResolvedValue(groupPackageSettingsMutationMock({ errors: ['foo'] }))}
${'network'} | ${jest.fn().mockRejectedValue()}
`('mutation payload with $type error', async ({ mutationResolver }) => {
- mountComponent({ mutationResolver });
+ mountComponent({ mountFn: mountExtended, mutationResolver });
fillApolloCache();
emitMavenSettingsUpdate();
diff --git a/spec/frontend/packages_and_registries/settings/group/components/settings_titles_spec.js b/spec/frontend/packages_and_registries/settings/group/components/settings_titles_spec.js
deleted file mode 100644
index fcfad4b42b8..00000000000
--- a/spec/frontend/packages_and_registries/settings/group/components/settings_titles_spec.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import SettingsTitles from '~/packages_and_registries/settings/group/components/settings_titles.vue';
-
-describe('settings_titles', () => {
- let wrapper;
-
- const defaultProps = {
- title: 'foo',
- subTitle: 'bar',
- };
-
- const mountComponent = (propsData = defaultProps) => {
- wrapper = shallowMount(SettingsTitles, {
- propsData,
- });
- };
-
- const findSubTitle = () => wrapper.find('p');
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders properly', () => {
- mountComponent();
-
- expect(wrapper.element).toMatchSnapshot();
- });
-
- it('does not render the subtitle paragraph when no subtitle is passed', () => {
- mountComponent({ title: defaultProps.title });
-
- expect(findSubTitle().exists()).toBe(false);
- });
-});
diff --git a/spec/frontend/packages_and_registries/settings/project/settings/components/cleanup_image_tags_spec.js b/spec/frontend/packages_and_registries/settings/project/settings/components/cleanup_image_tags_spec.js
new file mode 100644
index 00000000000..8b60f31512b
--- /dev/null
+++ b/spec/frontend/packages_and_registries/settings/project/settings/components/cleanup_image_tags_spec.js
@@ -0,0 +1,164 @@
+import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import component from '~/packages_and_registries/settings/project/components/cleanup_image_tags.vue';
+import ContainerExpirationPolicyForm from '~/packages_and_registries/settings/project/components/container_expiration_policy_form.vue';
+import {
+ CONTAINER_CLEANUP_POLICY_TITLE,
+ CONTAINER_CLEANUP_POLICY_DESCRIPTION,
+ FETCH_SETTINGS_ERROR_MESSAGE,
+ UNAVAILABLE_FEATURE_INTRO_TEXT,
+ UNAVAILABLE_USER_FEATURE_TEXT,
+} from '~/packages_and_registries/settings/project/constants';
+import expirationPolicyQuery from '~/packages_and_registries/settings/project/graphql/queries/get_expiration_policy.query.graphql';
+
+import {
+ expirationPolicyPayload,
+ emptyExpirationPolicyPayload,
+ containerExpirationPolicyData,
+} from '../mock_data';
+
+describe('Cleanup image tags project settings', () => {
+ let wrapper;
+ let fakeApollo;
+
+ const defaultProvidedValues = {
+ projectPath: 'path',
+ isAdmin: false,
+ adminSettingsPath: 'settingsPath',
+ enableHistoricEntries: false,
+ helpPagePath: 'helpPagePath',
+ showCleanupPolicyLink: false,
+ };
+
+ const findFormComponent = () => wrapper.findComponent(ContainerExpirationPolicyForm);
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findTitle = () => wrapper.findByTestId('title');
+ const findDescription = () => wrapper.findByTestId('description');
+
+ const mountComponent = (provide = defaultProvidedValues, config) => {
+ wrapper = shallowMountExtended(component, {
+ stubs: {
+ GlSprintf,
+ },
+ provide,
+ ...config,
+ });
+ };
+
+ const mountComponentWithApollo = ({ provide = defaultProvidedValues, resolver } = {}) => {
+ Vue.use(VueApollo);
+
+ const requestHandlers = [[expirationPolicyQuery, resolver]];
+
+ fakeApollo = createMockApollo(requestHandlers);
+ mountComponent(provide, {
+ apolloProvider: fakeApollo,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('isEdited status', () => {
+ it.each`
+ description | apiResponse | workingCopy | result
+ ${'empty response and no changes from user'} | ${emptyExpirationPolicyPayload()} | ${{}} | ${false}
+ ${'empty response and changes from user'} | ${emptyExpirationPolicyPayload()} | ${{ enabled: true }} | ${true}
+ ${'response and no changes'} | ${expirationPolicyPayload()} | ${containerExpirationPolicyData()} | ${false}
+ ${'response and changes'} | ${expirationPolicyPayload()} | ${{ ...containerExpirationPolicyData(), nameRegex: '12345' }} | ${true}
+ ${'response and empty'} | ${expirationPolicyPayload()} | ${{}} | ${true}
+ `('$description', async ({ apiResponse, workingCopy, result }) => {
+ mountComponentWithApollo({
+ provide: { ...defaultProvidedValues, enableHistoricEntries: true },
+ resolver: jest.fn().mockResolvedValue(apiResponse),
+ });
+ await waitForPromises();
+
+ findFormComponent().vm.$emit('input', workingCopy);
+
+ await waitForPromises();
+
+ expect(findFormComponent().props('isEdited')).toBe(result);
+ });
+ });
+
+ it('renders the setting form', async () => {
+ mountComponentWithApollo({
+ resolver: jest.fn().mockResolvedValue(expirationPolicyPayload()),
+ });
+ await waitForPromises();
+
+ expect(findFormComponent().exists()).toBe(true);
+ expect(findTitle().text()).toMatchInterpolatedText(CONTAINER_CLEANUP_POLICY_TITLE);
+ expect(findDescription().text()).toMatchInterpolatedText(CONTAINER_CLEANUP_POLICY_DESCRIPTION);
+ });
+
+ describe('the form is disabled', () => {
+ it('hides the form', () => {
+ mountComponent();
+
+ expect(findFormComponent().exists()).toBe(false);
+ });
+
+ it('shows an alert', () => {
+ mountComponent();
+
+ const text = findAlert().text();
+ expect(text).toContain(UNAVAILABLE_FEATURE_INTRO_TEXT);
+ expect(text).toContain(UNAVAILABLE_USER_FEATURE_TEXT);
+ });
+
+ describe('an admin is visiting the page', () => {
+ it('shows the admin part of the alert message', () => {
+ mountComponent({ ...defaultProvidedValues, isAdmin: true });
+
+ const sprintf = findAlert().findComponent(GlSprintf);
+ expect(sprintf.text()).toBe('administration settings');
+ expect(sprintf.findComponent(GlLink).attributes('href')).toBe(
+ defaultProvidedValues.adminSettingsPath,
+ );
+ });
+ });
+ });
+
+ describe('fetchSettingsError', () => {
+ beforeEach(async () => {
+ mountComponentWithApollo({
+ resolver: jest.fn().mockRejectedValue(new Error('GraphQL error')),
+ });
+ await waitForPromises();
+ });
+
+ it('hides the form', () => {
+ expect(findFormComponent().exists()).toBe(false);
+ });
+
+ it('shows an alert', () => {
+ expect(findAlert().html()).toContain(FETCH_SETTINGS_ERROR_MESSAGE);
+ });
+ });
+
+ describe('empty API response', () => {
+ it.each`
+ enableHistoricEntries | isShown
+ ${true} | ${true}
+ ${false} | ${false}
+ `('is $isShown that the form is shown', async ({ enableHistoricEntries, isShown }) => {
+ mountComponentWithApollo({
+ provide: {
+ ...defaultProvidedValues,
+ enableHistoricEntries,
+ },
+ resolver: jest.fn().mockResolvedValue(emptyExpirationPolicyPayload()),
+ });
+ await waitForPromises();
+
+ expect(findFormComponent().exists()).toBe(isShown);
+ });
+ });
+});
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 ca44e77e694..8e08864bdb8 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
@@ -2,13 +2,11 @@ import { shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import Vue, { nextTick } from 'vue';
import createMockApollo from 'helpers/mock_apollo_helper';
+import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { GlCard, GlLoadingIcon } from 'jest/packages_and_registries/shared/stubs';
import component from '~/packages_and_registries/settings/project/components/container_expiration_policy_form.vue';
-import {
- UPDATE_SETTINGS_ERROR_MESSAGE,
- UPDATE_SETTINGS_SUCCESS_MESSAGE,
-} from '~/packages_and_registries/settings/project/constants';
+import { UPDATE_SETTINGS_ERROR_MESSAGE } from '~/packages_and_registries/settings/project/constants';
import updateContainerExpirationPolicyMutation from '~/packages_and_registries/settings/project/graphql/mutations/update_container_expiration_policy.mutation.graphql';
import expirationPolicyQuery from '~/packages_and_registries/settings/project/graphql/queries/get_expiration_policy.query.graphql';
import Tracking from '~/tracking';
@@ -20,6 +18,7 @@ describe('Container Expiration Policy Settings Form', () => {
const defaultProvidedValues = {
projectPath: 'path',
+ projectSettingsPath: 'settings-path',
};
const {
@@ -36,7 +35,7 @@ describe('Container Expiration Policy Settings Form', () => {
label: 'docker_container_retention_and_expiration_policies',
};
- const findForm = () => wrapper.find({ ref: 'form-element' });
+ const findForm = () => wrapper.find('form');
const findCancelButton = () => wrapper.find('[data-testid="cancel-button"');
const findSaveButton = () => wrapper.find('[data-testid="save-button"');
@@ -208,7 +207,9 @@ describe('Container Expiration Policy Settings Form', () => {
});
it('validation event updates buttons disabled state', async () => {
- mountComponent();
+ mountComponent({
+ props: { ...defaultProps, isEdited: true },
+ });
expect(findSaveButton().props('disabled')).toBe(false);
@@ -229,52 +230,22 @@ describe('Container Expiration Policy Settings Form', () => {
});
describe('form', () => {
- describe('form reset event', () => {
- it('calls the appropriate function', () => {
- mountComponent();
-
- findForm().trigger('reset');
-
- expect(wrapper.emitted('reset')).toEqual([[]]);
- });
-
- it('tracks the reset event', () => {
- mountComponent();
-
- findForm().trigger('reset');
-
- expect(Tracking.event).toHaveBeenCalledWith(undefined, 'reset_form', trackingPayload);
- });
-
- it('resets the errors objects', async () => {
- mountComponent({
- data: { apiErrors: { nameRegex: 'bar' }, localErrors: { nameRegexKeep: false } },
- });
-
- findForm().trigger('reset');
-
- await nextTick();
+ describe('form submit event', () => {
+ useMockLocationHelper();
- expect(findKeepRegexInput().props('error')).toBe('');
- expect(findRemoveRegexInput().props('error')).toBe('');
- expect(findSaveButton().props('disabled')).toBe(false);
- });
- });
-
- describe('form submit event ', () => {
it('save has type submit', () => {
mountComponent();
expect(findSaveButton().attributes('type')).toBe('submit');
});
- it('dispatches the correct apollo mutation', () => {
+ it('dispatches the correct apollo mutation', async () => {
const mutationResolver = jest.fn().mockResolvedValue(expirationPolicyMutationPayload());
mountComponentWithApollo({
mutationResolver,
});
- findForm().trigger('submit');
+ await submitForm();
expect(mutationResolver).toHaveBeenCalled();
});
@@ -286,9 +257,7 @@ describe('Container Expiration Policy Settings Form', () => {
queryPayload: expirationPolicyPayload({ keepN: null, cadence: null, olderThan: null }),
});
- await waitForPromises();
-
- findForm().trigger('submit');
+ await submitForm();
expect(mutationResolver).toHaveBeenCalledWith({
input: {
@@ -303,24 +272,26 @@ describe('Container Expiration Policy Settings Form', () => {
});
});
- it('tracks the submit event', () => {
+ it('tracks the submit event', async () => {
mountComponentWithApollo({
mutationResolver: jest.fn().mockResolvedValue(expirationPolicyMutationPayload()),
});
- findForm().trigger('submit');
+ await submitForm();
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'submit_form', trackingPayload);
});
- it('show a success toast when submit succeed', async () => {
+ it('redirects to package and registry project settings page when submitted successfully', async () => {
mountComponentWithApollo({
mutationResolver: jest.fn().mockResolvedValue(expirationPolicyMutationPayload()),
});
await submitForm();
- expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(UPDATE_SETTINGS_SUCCESS_MESSAGE);
+ expect(window.location.href.endsWith('settings-path?showSetupSuccessAlert=true')).toBe(
+ true,
+ );
});
describe('when submit fails', () => {
@@ -348,6 +319,7 @@ describe('Container Expiration Policy Settings Form', () => {
await submitForm();
expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(UPDATE_SETTINGS_ERROR_MESSAGE);
+ expect(window.location.href).toBeUndefined();
});
it('parses the error messages', async () => {
@@ -375,24 +347,24 @@ describe('Container Expiration Policy Settings Form', () => {
describe('form actions', () => {
describe('cancel button', () => {
- it('has type reset', () => {
+ it('links to project package and registry settings path', () => {
mountComponent();
- expect(findCancelButton().attributes('type')).toBe('reset');
+ expect(findCancelButton().attributes('href')).toBe(
+ defaultProvidedValues.projectSettingsPath,
+ );
});
it.each`
- isLoading | isEdited | mutationLoading
- ${true} | ${true} | ${true}
- ${false} | ${true} | ${true}
- ${false} | ${false} | ${true}
- ${true} | ${false} | ${false}
- ${false} | ${false} | ${false}
+ isLoading | mutationLoading
+ ${true} | ${true}
+ ${false} | ${true}
+ ${true} | ${false}
`(
- 'when isLoading is $isLoading, isEdited is $isEdited and mutationLoading is $mutationLoading is disabled',
- ({ isEdited, isLoading, mutationLoading }) => {
+ 'is disabled when isLoading is $isLoading and mutationLoading is $mutationLoading',
+ ({ isLoading, mutationLoading }) => {
mountComponent({
- props: { ...defaultProps, isEdited, isLoading },
+ props: { ...defaultProps, isLoading },
data: { mutationLoading },
});
@@ -409,18 +381,19 @@ describe('Container Expiration Policy Settings Form', () => {
});
it.each`
- isLoading | localErrors | mutationLoading
- ${true} | ${{}} | ${true}
- ${true} | ${{}} | ${false}
- ${false} | ${{}} | ${true}
- ${false} | ${{ foo: false }} | ${true}
- ${true} | ${{ foo: false }} | ${false}
- ${false} | ${{ foo: false }} | ${false}
+ isLoading | isEdited | localErrors | mutationLoading
+ ${true} | ${false} | ${{}} | ${true}
+ ${true} | ${false} | ${{}} | ${false}
+ ${false} | ${false} | ${{}} | ${true}
+ ${false} | ${false} | ${{}} | ${false}
+ ${false} | ${false} | ${{ foo: false }} | ${true}
+ ${true} | ${false} | ${{ foo: false }} | ${false}
+ ${false} | ${false} | ${{ foo: false }} | ${false}
`(
- 'when isLoading is $isLoading, localErrors is $localErrors and mutationLoading is $mutationLoading is disabled',
- ({ localErrors, isLoading, mutationLoading }) => {
+ 'is disabled when isLoading is $isLoading, isEdited is $isEdited, localErrors is $localErrors and mutationLoading is $mutationLoading',
+ ({ localErrors, isEdited, isLoading, mutationLoading }) => {
mountComponent({
- props: { ...defaultProps, isLoading },
+ props: { ...defaultProps, isEdited, isLoading },
data: { mutationLoading, localErrors },
});
diff --git a/spec/frontend/packages_and_registries/settings/project/settings/components/container_expiration_policy_spec.js b/spec/frontend/packages_and_registries/settings/project/settings/components/container_expiration_policy_spec.js
index d83c717da6a..35baeaeac61 100644
--- a/spec/frontend/packages_and_registries/settings/project/settings/components/container_expiration_policy_spec.js
+++ b/spec/frontend/packages_and_registries/settings/project/settings/components/container_expiration_policy_spec.js
@@ -1,12 +1,14 @@
-import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
+import { GlAlert, GlSprintf, GlLink, GlCard } 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 component from '~/packages_and_registries/settings/project/components/container_expiration_policy.vue';
-import ContainerExpirationPolicyForm from '~/packages_and_registries/settings/project/components/container_expiration_policy_form.vue';
import {
+ CONTAINER_CLEANUP_POLICY_EDIT_RULES,
+ CONTAINER_CLEANUP_POLICY_SET_RULES,
+ CONTAINER_CLEANUP_POLICY_RULES_DESCRIPTION,
FETCH_SETTINGS_ERROR_MESSAGE,
UNAVAILABLE_FEATURE_INTRO_TEXT,
UNAVAILABLE_USER_FEATURE_TEXT,
@@ -14,11 +16,7 @@ import {
import expirationPolicyQuery from '~/packages_and_registries/settings/project/graphql/queries/get_expiration_policy.query.graphql';
import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
-import {
- expirationPolicyPayload,
- emptyExpirationPolicyPayload,
- containerExpirationPolicyData,
-} from '../mock_data';
+import { expirationPolicyPayload, emptyExpirationPolicyPayload } from '../mock_data';
describe('Container expiration policy project settings', () => {
let wrapper;
@@ -28,17 +26,19 @@ describe('Container expiration policy project settings', () => {
projectPath: 'path',
isAdmin: false,
adminSettingsPath: 'settingsPath',
+ cleanupSettingsPath: 'cleanupSettingsPath',
enableHistoricEntries: false,
helpPagePath: 'helpPagePath',
- showCleanupPolicyLink: false,
};
- const findFormComponent = () => wrapper.find(ContainerExpirationPolicyForm);
- const findAlert = () => wrapper.find(GlAlert);
- const findSettingsBlock = () => wrapper.find(SettingsBlock);
+ const findFormComponent = () => wrapper.findComponent(GlCard);
+ const findDescription = () => wrapper.findByTestId('description');
+ const findButton = () => wrapper.findByTestId('rules-button');
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findSettingsBlock = () => wrapper.findComponent(SettingsBlock);
const mountComponent = (provide = defaultProvidedValues, config) => {
- wrapper = shallowMount(component, {
+ wrapper = shallowMountExtended(component, {
stubs: {
GlSprintf,
SettingsBlock,
@@ -63,37 +63,19 @@ describe('Container expiration policy project settings', () => {
wrapper.destroy();
});
- describe('isEdited status', () => {
- it.each`
- description | apiResponse | workingCopy | result
- ${'empty response and no changes from user'} | ${emptyExpirationPolicyPayload()} | ${{}} | ${false}
- ${'empty response and changes from user'} | ${emptyExpirationPolicyPayload()} | ${{ enabled: true }} | ${true}
- ${'response and no changes'} | ${expirationPolicyPayload()} | ${containerExpirationPolicyData()} | ${false}
- ${'response and changes'} | ${expirationPolicyPayload()} | ${{ ...containerExpirationPolicyData(), nameRegex: '12345' }} | ${true}
- ${'response and empty'} | ${expirationPolicyPayload()} | ${{}} | ${true}
- `('$description', async ({ apiResponse, workingCopy, result }) => {
- mountComponentWithApollo({
- provide: { ...defaultProvidedValues, enableHistoricEntries: true },
- resolver: jest.fn().mockResolvedValue(apiResponse),
- });
- await waitForPromises();
-
- findFormComponent().vm.$emit('input', workingCopy);
-
- await waitForPromises();
-
- expect(findFormComponent().props('isEdited')).toBe(result);
- });
- });
-
it('renders the setting form', async () => {
mountComponentWithApollo({
resolver: jest.fn().mockResolvedValue(expirationPolicyPayload()),
});
await waitForPromises();
- expect(findFormComponent().exists()).toBe(true);
expect(findSettingsBlock().exists()).toBe(true);
+ expect(findFormComponent().exists()).toBe(true);
+ expect(findDescription().text()).toMatchInterpolatedText(
+ CONTAINER_CLEANUP_POLICY_RULES_DESCRIPTION,
+ );
+ expect(findButton().text()).toMatchInterpolatedText(CONTAINER_CLEANUP_POLICY_EDIT_RULES);
+ expect(findButton().attributes('href')).toBe(defaultProvidedValues.cleanupSettingsPath);
});
describe('the form is disabled', () => {
@@ -115,9 +97,9 @@ describe('Container expiration policy project settings', () => {
it('shows the admin part of the alert message', () => {
mountComponent({ ...defaultProvidedValues, isAdmin: true });
- const sprintf = findAlert().find(GlSprintf);
+ const sprintf = findAlert().findComponent(GlSprintf);
expect(sprintf.text()).toBe('administration settings');
- expect(sprintf.find(GlLink).attributes('href')).toBe(
+ expect(sprintf.findComponent(GlLink).attributes('href')).toBe(
defaultProvidedValues.adminSettingsPath,
);
});
@@ -157,6 +139,10 @@ describe('Container expiration policy project settings', () => {
await waitForPromises();
expect(findFormComponent().exists()).toBe(isShown);
+ if (isShown) {
+ expect(findButton().text()).toMatchInterpolatedText(CONTAINER_CLEANUP_POLICY_SET_RULES);
+ expect(findButton().attributes('href')).toBe(defaultProvidedValues.cleanupSettingsPath);
+ }
});
});
});
diff --git a/spec/frontend/packages_and_registries/settings/project/settings/components/expiration_dropdown_spec.js b/spec/frontend/packages_and_registries/settings/project/settings/components/expiration_dropdown_spec.js
index 8b99ac6b06c..ae41fdf65e0 100644
--- a/spec/frontend/packages_and_registries/settings/project/settings/components/expiration_dropdown_spec.js
+++ b/spec/frontend/packages_and_registries/settings/project/settings/components/expiration_dropdown_spec.js
@@ -14,8 +14,8 @@ describe('ExpirationDropdown', () => {
],
};
- const findFormSelect = () => wrapper.find(GlFormSelect);
- const findFormGroup = () => wrapper.find(GlFormGroup);
+ const findFormSelect = () => wrapper.findComponent(GlFormSelect);
+ const findFormGroup = () => wrapper.findComponent(GlFormGroup);
const findDescription = () => wrapper.find('[data-testid="description"]');
const findOptions = () => wrapper.findAll('[data-testid="option"]');
diff --git a/spec/frontend/packages_and_registries/settings/project/settings/components/expiration_input_spec.js b/spec/frontend/packages_and_registries/settings/project/settings/components/expiration_input_spec.js
index 6b681924fcf..1cea0704154 100644
--- a/spec/frontend/packages_and_registries/settings/project/settings/components/expiration_input_spec.js
+++ b/spec/frontend/packages_and_registries/settings/project/settings/components/expiration_input_spec.js
@@ -16,11 +16,11 @@ describe('ExpirationInput', () => {
const tagsRegexHelpPagePath = 'fooPath';
- const findInput = () => wrapper.find(GlFormInput);
- const findFormGroup = () => wrapper.find(GlFormGroup);
+ const findInput = () => wrapper.findComponent(GlFormInput);
+ const findFormGroup = () => wrapper.findComponent(GlFormGroup);
const findLabel = () => wrapper.find('[data-testid="label"]');
const findDescription = () => wrapper.find('[data-testid="description"]');
- const findDescriptionLink = () => wrapper.find(GlLink);
+ const findDescriptionLink = () => wrapper.findComponent(GlLink);
const mountComponent = (props) => {
wrapper = shallowMount(component, {
diff --git a/spec/frontend/packages_and_registries/settings/project/settings/components/expiration_run_text_spec.js b/spec/frontend/packages_and_registries/settings/project/settings/components/expiration_run_text_spec.js
index 94f7783afe7..653f2a8b40e 100644
--- a/spec/frontend/packages_and_registries/settings/project/settings/components/expiration_run_text_spec.js
+++ b/spec/frontend/packages_and_registries/settings/project/settings/components/expiration_run_text_spec.js
@@ -11,8 +11,8 @@ describe('ExpirationToggle', () => {
let wrapper;
const value = 'foo';
- const findInput = () => wrapper.find(GlFormInput);
- const findFormGroup = () => wrapper.find(GlFormGroup);
+ const findInput = () => wrapper.findComponent(GlFormInput);
+ const findFormGroup = () => wrapper.findComponent(GlFormGroup);
const mountComponent = (propsData) => {
wrapper = shallowMount(component, {
diff --git a/spec/frontend/packages_and_registries/settings/project/settings/components/expiration_toggle_spec.js b/spec/frontend/packages_and_registries/settings/project/settings/components/expiration_toggle_spec.js
index 45039614e49..55a66cebd83 100644
--- a/spec/frontend/packages_and_registries/settings/project/settings/components/expiration_toggle_spec.js
+++ b/spec/frontend/packages_and_registries/settings/project/settings/components/expiration_toggle_spec.js
@@ -10,7 +10,7 @@ import {
describe('ExpirationToggle', () => {
let wrapper;
- const findToggle = () => wrapper.find(GlToggle);
+ const findToggle = () => wrapper.findComponent(GlToggle);
const findDescription = () => wrapper.find('[data-testid="description"]');
const mountComponent = (propsData) => {
diff --git a/spec/frontend/packages_and_registries/settings/project/settings/components/packages_cleanup_policy_form_spec.js b/spec/frontend/packages_and_registries/settings/project/settings/components/packages_cleanup_policy_form_spec.js
index 86f45d78bae..daf0ee85fdf 100644
--- a/spec/frontend/packages_and_registries/settings/project/settings/components/packages_cleanup_policy_form_spec.js
+++ b/spec/frontend/packages_and_registries/settings/project/settings/components/packages_cleanup_policy_form_spec.js
@@ -39,7 +39,7 @@ describe('Packages Cleanup Policy Settings Form', () => {
label: 'packages_cleanup_policies',
};
- const findForm = () => wrapper.find({ ref: 'form-element' });
+ const findForm = () => wrapper.findComponent({ ref: 'form-element' });
const findSaveButton = () => wrapper.findByTestId('save-button');
const findKeepNDuplicatedPackageFilesDropdown = () =>
wrapper.findByTestId('keep-n-duplicated-package-files-dropdown');
diff --git a/spec/frontend/packages_and_registries/settings/project/settings/components/registry_settings_app_spec.js b/spec/frontend/packages_and_registries/settings/project/settings/components/registry_settings_app_spec.js
index f576bc79eae..07d13839c61 100644
--- a/spec/frontend/packages_and_registries/settings/project/settings/components/registry_settings_app_spec.js
+++ b/spec/frontend/packages_and_registries/settings/project/settings/components/registry_settings_app_spec.js
@@ -1,41 +1,99 @@
+import { GlAlert } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
+import waitForPromises from 'helpers/wait_for_promises';
+import setWindowLocation from 'helpers/set_window_location_helper';
+import * as commonUtils from '~/lib/utils/common_utils';
import component from '~/packages_and_registries/settings/project/components/registry_settings_app.vue';
import ContainerExpirationPolicy from '~/packages_and_registries/settings/project/components/container_expiration_policy.vue';
import PackagesCleanupPolicy from '~/packages_and_registries/settings/project/components/packages_cleanup_policy.vue';
+import {
+ SHOW_SETUP_SUCCESS_ALERT,
+ UPDATE_SETTINGS_SUCCESS_MESSAGE,
+} from '~/packages_and_registries/settings/project/constants';
+
+jest.mock('~/lib/utils/common_utils');
describe('Registry Settings app', () => {
let wrapper;
- const findContainerExpirationPolicy = () => wrapper.find(ContainerExpirationPolicy);
- const findPackagesCleanupPolicy = () => wrapper.find(PackagesCleanupPolicy);
+ const findContainerExpirationPolicy = () => wrapper.findComponent(ContainerExpirationPolicy);
+ const findPackagesCleanupPolicy = () => wrapper.findComponent(PackagesCleanupPolicy);
+ const findAlert = () => wrapper.findComponent(GlAlert);
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
- const mountComponent = (provide) => {
+ const defaultProvide = {
+ showContainerRegistrySettings: true,
+ showPackageRegistrySettings: true,
+ };
+
+ const mountComponent = (provide = defaultProvide) => {
wrapper = shallowMount(component, {
provide,
});
};
- it.each`
- showContainerRegistrySettings | showPackageRegistrySettings
- ${true} | ${false}
- ${true} | ${true}
- ${false} | ${true}
- ${false} | ${false}
- `(
- 'container expiration policy $showContainerRegistrySettings and package cleanup policy is $showPackageRegistrySettings',
- ({ showContainerRegistrySettings, showPackageRegistrySettings }) => {
- mountComponent({
- showContainerRegistrySettings,
- showPackageRegistrySettings,
+ describe('container policy success alert handling', () => {
+ const originalLocation = window.location.href;
+ const search = `?${SHOW_SETUP_SUCCESS_ALERT}=true`;
+
+ beforeEach(() => {
+ setWindowLocation(search);
+ });
+
+ afterEach(() => {
+ setWindowLocation(originalLocation);
+ });
+
+ it(`renders alert if the query string contains ${SHOW_SETUP_SUCCESS_ALERT}`, async () => {
+ mountComponent();
+
+ await waitForPromises();
+
+ expect(findAlert().exists()).toBe(true);
+ expect(findAlert().props()).toMatchObject({
+ dismissible: true,
+ variant: 'success',
});
+ expect(findAlert().text()).toMatchInterpolatedText(UPDATE_SETTINGS_SUCCESS_MESSAGE);
+ });
+
+ it('calls historyReplaceState with a clean url', () => {
+ mountComponent();
+
+ expect(commonUtils.historyReplaceState).toHaveBeenCalledWith(originalLocation);
+ });
+
+ it(`does nothing if the query string does not contain ${SHOW_SETUP_SUCCESS_ALERT}`, () => {
+ setWindowLocation('?');
+ mountComponent();
- expect(findContainerExpirationPolicy().exists()).toBe(showContainerRegistrySettings);
- expect(findPackagesCleanupPolicy().exists()).toBe(showPackageRegistrySettings);
- },
- );
+ expect(findAlert().exists()).toBe(false);
+ expect(commonUtils.historyReplaceState).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('settings', () => {
+ it.each`
+ showContainerRegistrySettings | showPackageRegistrySettings
+ ${true} | ${false}
+ ${true} | ${true}
+ ${false} | ${true}
+ ${false} | ${false}
+ `(
+ 'container expiration policy $showContainerRegistrySettings and package cleanup policy is $showPackageRegistrySettings',
+ ({ showContainerRegistrySettings, showPackageRegistrySettings }) => {
+ mountComponent({
+ showContainerRegistrySettings,
+ showPackageRegistrySettings,
+ });
+
+ expect(findContainerExpirationPolicy().exists()).toBe(showContainerRegistrySettings);
+ expect(findPackagesCleanupPolicy().exists()).toBe(showPackageRegistrySettings);
+ },
+ );
+ });
});
diff --git a/spec/frontend/packages_and_registries/shared/components/cli_commands_spec.js b/spec/frontend/packages_and_registries/shared/components/cli_commands_spec.js
new file mode 100644
index 00000000000..18084766db9
--- /dev/null
+++ b/spec/frontend/packages_and_registries/shared/components/cli_commands_spec.js
@@ -0,0 +1,85 @@
+import { GlDropdown } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import Vue from 'vue';
+import Vuex from 'vuex';
+import QuickstartDropdown from '~/packages_and_registries/shared/components/cli_commands.vue';
+import {
+ QUICK_START,
+ LOGIN_COMMAND_LABEL,
+ COPY_LOGIN_TITLE,
+ BUILD_COMMAND_LABEL,
+ COPY_BUILD_TITLE,
+ PUSH_COMMAND_LABEL,
+ COPY_PUSH_TITLE,
+} from '~/packages_and_registries/container_registry/explorer/constants';
+import Tracking from '~/tracking';
+import CodeInstruction from '~/vue_shared/components/registry/code_instruction.vue';
+
+import { dockerCommands } from 'jest/packages_and_registries/container_registry/explorer/mock_data';
+
+Vue.use(Vuex);
+
+describe('cli_commands', () => {
+ let wrapper;
+
+ const findDropdownButton = () => wrapper.findComponent(GlDropdown);
+ const findCodeInstruction = () => wrapper.findAllComponents(CodeInstruction);
+
+ const mountComponent = () => {
+ wrapper = mount(QuickstartDropdown, {
+ propsData: {
+ ...dockerCommands,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ jest.spyOn(Tracking, 'event');
+ mountComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('shows the correct text on the button', () => {
+ expect(findDropdownButton().text()).toContain(QUICK_START);
+ });
+
+ it('clicking on the dropdown emit a tracking event', () => {
+ findDropdownButton().vm.$emit('shown');
+ expect(Tracking.event).toHaveBeenCalledWith(
+ undefined,
+ 'click_dropdown',
+ expect.objectContaining({ label: 'quickstart_dropdown' }),
+ );
+ });
+
+ describe.each`
+ index | labelText | titleText | command | trackedEvent
+ ${0} | ${LOGIN_COMMAND_LABEL} | ${COPY_LOGIN_TITLE} | ${dockerCommands.dockerLoginCommand} | ${'click_copy_login'}
+ ${1} | ${BUILD_COMMAND_LABEL} | ${COPY_BUILD_TITLE} | ${dockerCommands.dockerBuildCommand} | ${'click_copy_build'}
+ ${2} | ${PUSH_COMMAND_LABEL} | ${COPY_PUSH_TITLE} | ${dockerCommands.dockerPushCommand} | ${'click_copy_push'}
+ `('code instructions at $index', ({ index, labelText, titleText, command, trackedEvent }) => {
+ let codeInstruction;
+
+ beforeEach(() => {
+ codeInstruction = findCodeInstruction().at(index);
+ });
+
+ it('exists', () => {
+ expect(codeInstruction.exists()).toBe(true);
+ });
+
+ it(`has the correct props`, () => {
+ expect(codeInstruction.props()).toMatchObject({
+ label: labelText,
+ instruction: command,
+ copyText: titleText,
+ trackingAction: trackedEvent,
+ trackingLabel: 'quickstart_dropdown',
+ });
+ });
+ });
+});
diff --git a/spec/frontend/packages_and_registries/shared/components/package_icon_and_name_spec.js b/spec/frontend/packages_and_registries/shared/components/package_icon_and_name_spec.js
index d6d1970cb12..a0ff6ca01b5 100644
--- a/spec/frontend/packages_and_registries/shared/components/package_icon_and_name_spec.js
+++ b/spec/frontend/packages_and_registries/shared/components/package_icon_and_name_spec.js
@@ -5,7 +5,7 @@ import PackageIconAndName from '~/packages_and_registries/shared/components/pack
describe('PackageIconAndName', () => {
let wrapper;
- const findIcon = () => wrapper.find(GlIcon);
+ const findIcon = () => wrapper.findComponent(GlIcon);
const mountComponent = () => {
wrapper = shallowMount(PackageIconAndName, {
diff --git a/spec/frontend/pages/admin/application_settings/metrics_and_profiling/usage_statistics_spec.js b/spec/frontend/pages/admin/application_settings/metrics_and_profiling/usage_statistics_spec.js
index 3a52c243867..3c512cfd6ae 100644
--- a/spec/frontend/pages/admin/application_settings/metrics_and_profiling/usage_statistics_spec.js
+++ b/spec/frontend/pages/admin/application_settings/metrics_and_profiling/usage_statistics_spec.js
@@ -48,7 +48,7 @@ describe('UsageStatistics', () => {
expectEnabledservicePingFeaturesCheckBox();
});
- it('is switched to disabled when Service Ping checkbox is unchecked ', () => {
+ it('is switched to disabled when Service Ping checkbox is unchecked', () => {
servicePingCheckBox.click();
servicePingFeaturesCheckBox.click();
expectEnabledservicePingFeaturesCheckBox();
diff --git a/spec/frontend/pages/import/bitbucket_server/components/bitbucket_server_status_table_spec.js b/spec/frontend/pages/import/bitbucket_server/components/bitbucket_server_status_table_spec.js
index 7a8a249cb2a..b020caa3010 100644
--- a/spec/frontend/pages/import/bitbucket_server/components/bitbucket_server_status_table_spec.js
+++ b/spec/frontend/pages/import/bitbucket_server/components/bitbucket_server_status_table_spec.js
@@ -14,7 +14,7 @@ describe('BitbucketServerStatusTable', () => {
const findReconfigureButton = () =>
wrapper
- .findAll(GlButton)
+ .findAllComponents(GlButton)
.filter((w) => w.props().variant === 'info')
.at(0);
@@ -36,7 +36,7 @@ describe('BitbucketServerStatusTable', () => {
it('renders bitbucket status table component', () => {
createComponent();
- expect(wrapper.find(BitbucketStatusTable).exists()).toBe(true);
+ expect(wrapper.findComponent(BitbucketStatusTable).exists()).toBe(true);
});
it('renders Reconfigure button', async () => {
diff --git a/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js b/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js
index a850b1655f7..1790a9c9bf5 100644
--- a/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js
+++ b/spec/frontend/pages/import/bulk_imports/history/components/bulk_imports_history_app_spec.js
@@ -84,7 +84,7 @@ describe('BulkImportsHistoryApp', () => {
describe('general behavior', () => {
it('renders loading state when loading', () => {
createComponent();
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
it('renders empty state when no data is available', async () => {
@@ -92,8 +92,8 @@ describe('BulkImportsHistoryApp', () => {
createComponent();
await axios.waitForAll();
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
- expect(wrapper.find(GlEmptyState).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
+ expect(wrapper.findComponent(GlEmptyState).exists()).toBe(true);
});
it('renders table with data when history is available', async () => {
@@ -101,7 +101,7 @@ describe('BulkImportsHistoryApp', () => {
createComponent();
await axios.waitForAll();
- const table = wrapper.find(GlTable);
+ const table = wrapper.findComponent(GlTable);
expect(table.exists()).toBe(true);
// can't use .props() or .attributes() here
expect(table.vm.$attrs.items).toHaveLength(DUMMY_RESPONSE.length);
diff --git a/spec/frontend/pages/import/history/components/import_error_details_spec.js b/spec/frontend/pages/import/history/components/import_error_details_spec.js
index 4ff3f0361cf..82a3e11186e 100644
--- a/spec/frontend/pages/import/history/components/import_error_details_spec.js
+++ b/spec/frontend/pages/import/history/components/import_error_details_spec.js
@@ -41,7 +41,7 @@ describe('ImportErrorDetails', () => {
describe('general behavior', () => {
it('renders loading state when loading', () => {
createComponent();
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
it('renders import_error if it is available', async () => {
@@ -50,7 +50,7 @@ describe('ImportErrorDetails', () => {
createComponent();
await axios.waitForAll();
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.find('pre').text()).toBe(FAKE_IMPORT_ERROR);
});
@@ -59,7 +59,7 @@ describe('ImportErrorDetails', () => {
createComponent();
await axios.waitForAll();
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
expect(wrapper.find('pre').text()).toBe('No additional information provided.');
});
});
diff --git a/spec/frontend/pages/import/history/components/import_history_app_spec.js b/spec/frontend/pages/import/history/components/import_history_app_spec.js
index 0d821b114cf..5030adae2fa 100644
--- a/spec/frontend/pages/import/history/components/import_history_app_spec.js
+++ b/spec/frontend/pages/import/history/components/import_history_app_spec.js
@@ -79,7 +79,7 @@ describe('ImportHistoryApp', () => {
describe('general behavior', () => {
it('renders loading state when loading', () => {
createComponent();
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
it('renders empty state when no data is available', async () => {
@@ -87,8 +87,8 @@ describe('ImportHistoryApp', () => {
createComponent();
await axios.waitForAll();
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
- expect(wrapper.find(GlEmptyState).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
+ expect(wrapper.findComponent(GlEmptyState).exists()).toBe(true);
});
it('renders table with data when history is available', async () => {
@@ -96,7 +96,7 @@ describe('ImportHistoryApp', () => {
createComponent();
await axios.waitForAll();
- const table = wrapper.find(GlTable);
+ const table = wrapper.findComponent(GlTable);
expect(table.exists()).toBe(true);
expect(table.props().items).toStrictEqual(DUMMY_RESPONSE);
});
@@ -127,7 +127,7 @@ describe('ImportHistoryApp', () => {
expect(mock.history.get.length).toBe(1);
expect(mock.history.get[0].params).toStrictEqual(expect.objectContaining({ page: NEW_PAGE }));
- expect(wrapper.find(GlTable).props().items).toStrictEqual(FAKE_NEXT_PAGE_REPLY);
+ expect(wrapper.findComponent(GlTable).props().items).toStrictEqual(FAKE_NEXT_PAGE_REPLY);
});
});
diff --git a/spec/frontend/pages/profiles/show/emoji_menu_spec.js b/spec/frontend/pages/profiles/show/emoji_menu_spec.js
deleted file mode 100644
index fa6e7e51a60..00000000000
--- a/spec/frontend/pages/profiles/show/emoji_menu_spec.js
+++ /dev/null
@@ -1,115 +0,0 @@
-import $ from 'jquery';
-import { TEST_HOST } from 'helpers/test_constants';
-import axios from '~/lib/utils/axios_utils';
-import EmojiMenu from '~/pages/profiles/show/emoji_menu';
-
-describe('EmojiMenu', () => {
- const dummyEmojiTag = '<dummy></tag>';
- const dummyToggleButtonSelector = '.toggle-button-selector';
- const dummyMenuClass = 'dummy-menu-class';
-
- let emojiMenu;
- let dummySelectEmojiCallback;
- let dummyEmojiList;
-
- beforeEach(() => {
- dummySelectEmojiCallback = jest.fn().mockName('dummySelectEmojiCallback');
- dummyEmojiList = {
- glEmojiTag() {
- return dummyEmojiTag;
- },
- normalizeEmojiName(emoji) {
- return emoji;
- },
- isEmojiNameValid() {
- return true;
- },
- getEmojiCategoryMap() {
- return { dummyCategory: [] };
- },
- };
-
- emojiMenu = new EmojiMenu(
- dummyEmojiList,
- dummyToggleButtonSelector,
- dummyMenuClass,
- dummySelectEmojiCallback,
- );
- });
-
- afterEach(() => {
- emojiMenu.destroy();
- });
-
- describe('addAward', () => {
- const dummyAwardUrl = `${TEST_HOST}/award/url`;
- const dummyEmoji = 'tropical_fish';
- const dummyVotesBlock = () => $('<div />');
-
- it('calls selectEmojiCallback', async () => {
- expect(dummySelectEmojiCallback).not.toHaveBeenCalled();
-
- await emojiMenu.addAward(dummyVotesBlock(), dummyAwardUrl, dummyEmoji, false);
- expect(dummySelectEmojiCallback).toHaveBeenCalledWith(dummyEmoji, dummyEmojiTag);
- });
-
- it('does not make an axios request', async () => {
- jest.spyOn(axios, 'request').mockReturnValue();
-
- await emojiMenu.addAward(dummyVotesBlock(), dummyAwardUrl, dummyEmoji, false);
- expect(axios.request).not.toHaveBeenCalled();
- });
- });
-
- describe('bindEvents', () => {
- beforeEach(() => {
- jest.spyOn(emojiMenu, 'registerEventListener').mockReturnValue();
- });
-
- it('binds event listeners to custom toggle button', () => {
- emojiMenu.bindEvents();
-
- expect(emojiMenu.registerEventListener).toHaveBeenCalledWith(
- 'one',
- expect.anything(),
- 'mouseenter focus',
- dummyToggleButtonSelector,
- 'mouseenter focus',
- expect.anything(),
- );
-
- expect(emojiMenu.registerEventListener).toHaveBeenCalledWith(
- 'on',
- expect.anything(),
- 'click',
- dummyToggleButtonSelector,
- expect.anything(),
- );
- });
-
- it('binds event listeners to custom menu class', () => {
- emojiMenu.bindEvents();
-
- expect(emojiMenu.registerEventListener).toHaveBeenCalledWith(
- 'on',
- expect.anything(),
- 'click',
- `.js-awards-block .js-emoji-btn, .${dummyMenuClass} .js-emoji-btn`,
- expect.anything(),
- );
- });
- });
-
- describe('createEmojiMenu', () => {
- it('renders the menu with custom menu class', () => {
- const menuElement = () =>
- document.body.querySelector(`.emoji-menu.${dummyMenuClass} .emoji-menu-content`);
-
- expect(menuElement()).toBe(null);
-
- emojiMenu.createEmojiMenu();
-
- expect(menuElement()).not.toBe(null);
- });
- });
-});
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 2a0fde45384..f221a90da61 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
@@ -4,11 +4,14 @@ import { mount, shallowMount } from '@vue/test-utils';
import axios from 'axios';
import AxiosMockAdapter from 'axios-mock-adapter';
import { kebabCase } from 'lodash';
-import { nextTick } from 'vue';
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
import createFlash from '~/flash';
-import httpStatus from '~/lib/utils/http_status';
import * as urlUtility from '~/lib/utils/url_utility';
import ForkForm from '~/pages/projects/forks/new/components/fork_form.vue';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import searchQuery from '~/pages/projects/forks/new/queries/search_forkable_namespaces.query.graphql';
+import ProjectNamespace from '~/pages/projects/forks/new/components/project_namespace.vue';
jest.mock('~/flash');
jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
@@ -16,6 +19,7 @@ jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
describe('ForkForm component', () => {
let wrapper;
let axiosMock;
+ let mockQueryResponse;
const PROJECT_VISIBILITY_TYPE = {
private:
@@ -24,26 +28,11 @@ describe('ForkForm component', () => {
public: 'Public The project can be accessed without any authentication.',
};
- const GON_GITLAB_URL = 'https://gitlab.com';
const GON_API_VERSION = 'v7';
- const MOCK_NAMESPACES_RESPONSE = [
- {
- name: 'one',
- full_name: 'one-group/one',
- id: 1,
- },
- {
- name: 'two',
- full_name: 'two-group/two',
- id: 2,
- },
- ];
-
const DEFAULT_PROVIDE = {
newGroupPath: 'some/groups/path',
visibilityHelpPath: 'some/visibility/help/path',
- endpoint: '/some/project-full-path/-/forks/new.json',
projectFullPath: '/some/project-full-path',
projectId: '10',
projectName: 'Project Name',
@@ -53,12 +42,44 @@ describe('ForkForm component', () => {
restrictedVisibilityLevels: [],
};
- const mockGetRequest = (data = {}, statusCode = httpStatus.OK) => {
- axiosMock.onGet(DEFAULT_PROVIDE.endpoint).replyOnce(statusCode, data);
- };
+ Vue.use(VueApollo);
const createComponentFactory = (mountFn) => (provide = {}, data = {}) => {
+ const queryResponse = {
+ project: {
+ id: 'gid://gitlab/Project/1',
+ forkTargets: {
+ nodes: [
+ {
+ id: 'gid://gitlab/Group/21',
+ fullPath: 'flightjs',
+ name: 'Flight JS',
+ visibility: 'public',
+ },
+ {
+ id: 'gid://gitlab/Namespace/4',
+ fullPath: 'root',
+ name: 'Administrator',
+ visibility: 'public',
+ },
+ ],
+ },
+ },
+ };
+
+ mockQueryResponse = jest.fn().mockResolvedValue({ data: queryResponse });
+ const requestHandlers = [[searchQuery, mockQueryResponse]];
+ const apolloProvider = createMockApollo(requestHandlers);
+
+ apolloProvider.clients.defaultClient.cache.writeQuery({
+ query: searchQuery,
+ data: {
+ ...queryResponse,
+ },
+ });
+
wrapper = mountFn(ForkForm, {
+ apolloProvider,
provide: {
...DEFAULT_PROVIDE,
...provide,
@@ -83,7 +104,6 @@ describe('ForkForm component', () => {
beforeEach(() => {
axiosMock = new AxiosMockAdapter(axios);
window.gon = {
- gitlab_url: GON_GITLAB_URL,
api_version: GON_API_VERSION,
};
});
@@ -93,12 +113,11 @@ describe('ForkForm component', () => {
axiosMock.restore();
});
- const findFormSelectOptions = () => wrapper.find('select[name="namespace"]').findAll('option');
const findPrivateRadio = () => wrapper.find('[data-testid="radio-private"]');
const findInternalRadio = () => wrapper.find('[data-testid="radio-internal"]');
const findPublicRadio = () => wrapper.find('[data-testid="radio-public"]');
const findForkNameInput = () => wrapper.find('[data-testid="fork-name-input"]');
- const findForkUrlInput = () => wrapper.find('[data-testid="fork-url-input"]');
+ const findForkUrlInput = () => wrapper.findComponent(ProjectNamespace);
const findForkSlugInput = () => wrapper.find('[data-testid="fork-slug-input"]');
const findForkDescriptionTextarea = () =>
wrapper.find('[data-testid="fork-description-textarea"]');
@@ -106,7 +125,6 @@ describe('ForkForm component', () => {
wrapper.find('[data-testid="fork-visibility-radio-group"]');
it('will go to projectFullPath when click cancel button', () => {
- mockGetRequest();
createComponent();
const { projectFullPath } = DEFAULT_PROVIDE;
@@ -115,8 +133,13 @@ describe('ForkForm component', () => {
expect(cancelButton.attributes('href')).toBe(projectFullPath);
});
+ const selectedMockNamespace = { name: 'two', full_name: 'two-group/two', id: 2 };
+
+ const fillForm = () => {
+ findForkUrlInput().vm.$emit('select', selectedMockNamespace);
+ };
+
it('has input with csrf token', () => {
- mockGetRequest();
createComponent();
expect(wrapper.find('input[name="authenticity_token"]').attributes('value')).toBe(
@@ -125,7 +148,6 @@ describe('ForkForm component', () => {
});
it('pre-populate form from project props', () => {
- mockGetRequest();
createComponent();
expect(findForkNameInput().attributes('value')).toBe(DEFAULT_PROVIDE.projectName);
@@ -135,75 +157,19 @@ describe('ForkForm component', () => {
);
});
- it('sets project URL prepend text with gon.gitlab_url', () => {
- mockGetRequest();
- createComponent();
-
- expect(wrapper.find(GlFormInputGroup).text()).toContain(`${GON_GITLAB_URL}/`);
- });
-
it('will have required attribute for required fields', () => {
- mockGetRequest();
createComponent();
expect(findForkNameInput().attributes('required')).not.toBeUndefined();
- expect(findForkUrlInput().attributes('required')).not.toBeUndefined();
expect(findForkSlugInput().attributes('required')).not.toBeUndefined();
expect(findVisibilityRadioGroup().attributes('required')).not.toBeUndefined();
expect(findForkDescriptionTextarea().attributes('required')).toBeUndefined();
});
- describe('forks namespaces', () => {
- beforeEach(() => {
- mockGetRequest({ namespaces: MOCK_NAMESPACES_RESPONSE });
- createFullComponent();
- });
-
- it('make GET request from endpoint', async () => {
- await axios.waitForAll();
-
- expect(axiosMock.history.get[0].url).toBe(DEFAULT_PROVIDE.endpoint);
- });
-
- it('generate default option', async () => {
- await axios.waitForAll();
-
- const optionsArray = findForkUrlInput().findAll('option');
-
- expect(optionsArray.at(0).text()).toBe('Select a namespace');
- });
-
- it('populate project url namespace options', async () => {
- await axios.waitForAll();
-
- const optionsArray = findForkUrlInput().findAll('option');
-
- expect(optionsArray).toHaveLength(MOCK_NAMESPACES_RESPONSE.length + 1);
- expect(optionsArray.at(1).text()).toBe(MOCK_NAMESPACES_RESPONSE[0].full_name);
- expect(optionsArray.at(2).text()).toBe(MOCK_NAMESPACES_RESPONSE[1].full_name);
- });
-
- it('set namespaces in alphabetical order', async () => {
- const namespace = {
- name: 'three',
- full_name: 'aaa/three',
- id: 3,
- };
- mockGetRequest({
- namespaces: [...MOCK_NAMESPACES_RESPONSE, namespace],
- });
- createComponent();
- await axios.waitForAll();
-
- expect(wrapper.vm.namespaces).toEqual([namespace, ...MOCK_NAMESPACES_RESPONSE]);
- });
- });
-
describe('project slug', () => {
const projectPath = 'some other project slug';
beforeEach(() => {
- mockGetRequest();
createComponent({
projectPath,
});
@@ -232,10 +198,9 @@ describe('ForkForm component', () => {
describe('visibility level', () => {
it('displays the correct description', () => {
- mockGetRequest();
createComponent();
- const formRadios = wrapper.findAll(GlFormRadio);
+ const formRadios = wrapper.findAllComponents(GlFormRadio);
Object.keys(PROJECT_VISIBILITY_TYPE).forEach((visibilityType, index) => {
expect(formRadios.at(index).text()).toBe(PROJECT_VISIBILITY_TYPE[visibilityType]);
@@ -243,10 +208,9 @@ describe('ForkForm component', () => {
});
it('displays all 3 visibility levels', () => {
- mockGetRequest();
createComponent();
- expect(wrapper.findAll(GlFormRadio)).toHaveLength(3);
+ expect(wrapper.findAllComponents(GlFormRadio)).toHaveLength(3);
});
describe('when the namespace is changed', () => {
@@ -262,16 +226,12 @@ describe('ForkForm component', () => {
},
];
- beforeEach(() => {
- mockGetRequest();
- });
-
it('resets the visibility to default "private"', async () => {
createFullComponent({ projectVisibility: 'public' }, { namespaces });
expect(wrapper.vm.form.fields.visibility.value).toBe('public');
- await findFormSelectOptions().at(1).setSelected();
+ fillForm();
await nextTick();
expect(getByRole(wrapper.element, 'radio', { name: /private/i }).checked).toBe(true);
@@ -280,8 +240,7 @@ describe('ForkForm component', () => {
it('sets the visibility to be null when restrictedVisibilityLevels is set', async () => {
createFullComponent({ restrictedVisibilityLevels: [10] }, { namespaces });
- await findFormSelectOptions().at(1).setSelected();
-
+ fillForm();
await nextTick();
const container = getByRole(wrapper.element, 'radiogroup', { name: /visibility/i });
@@ -315,8 +274,7 @@ describe('ForkForm component', () => {
${'public'} | ${[0, 20]}
${'public'} | ${[10, 20]}
${'public'} | ${[0, 10, 20]}
- `('checks the correct radio button', async ({ project, restrictedVisibilityLevels }) => {
- mockGetRequest();
+ `('checks the correct radio button', ({ project, restrictedVisibilityLevels }) => {
createFullComponent({
projectVisibility: project,
restrictedVisibilityLevels,
@@ -357,7 +315,7 @@ describe('ForkForm component', () => {
${'public'} | ${'public'} | ${undefined} | ${'true'} | ${'true'} | ${[0, 10, 20]}
`(
'sets appropriate radio button disabled state',
- async ({
+ ({
project,
namespace,
privateIsDisabled,
@@ -365,7 +323,6 @@ describe('ForkForm component', () => {
publicIsDisabled,
restrictedVisibilityLevels,
}) => {
- mockGetRequest();
createComponent(
{
projectVisibility: project,
@@ -387,11 +344,9 @@ describe('ForkForm component', () => {
const setupComponent = (fields = {}) => {
jest.spyOn(urlUtility, 'redirectTo').mockImplementation();
- mockGetRequest();
createFullComponent(
{},
{
- namespaces: MOCK_NAMESPACES_RESPONSE,
form: {
state: true,
...fields,
@@ -400,25 +355,21 @@ describe('ForkForm component', () => {
);
};
- const selectedMockNamespaceIndex = 1;
- const namespaceId = MOCK_NAMESPACES_RESPONSE[selectedMockNamespaceIndex].id;
-
- const fillForm = async () => {
- const namespaceOptions = findForkUrlInput().findAll('option');
-
- await namespaceOptions.at(selectedMockNamespaceIndex + 1).setSelected();
- };
+ beforeEach(() => {
+ setupComponent();
+ });
const submitForm = async () => {
- await fillForm();
- const form = wrapper.find(GlForm);
+ fillForm();
+ await nextTick();
+ const form = wrapper.findComponent(GlForm);
await form.trigger('submit');
await nextTick();
};
describe('with invalid form', () => {
- it('does not make POST request', async () => {
+ it('does not make POST request', () => {
jest.spyOn(axios, 'post');
setupComponent();
@@ -471,7 +422,7 @@ describe('ForkForm component', () => {
description: projectDescription,
id: projectId,
name: projectName,
- namespace_id: namespaceId,
+ namespace_id: selectedMockNamespace.id,
path: projectPath,
visibility: projectVisibility,
};
diff --git a/spec/frontend/pages/projects/forks/new/components/project_namespace_spec.js b/spec/frontend/pages/projects/forks/new/components/project_namespace_spec.js
new file mode 100644
index 00000000000..1a88aebae32
--- /dev/null
+++ b/spec/frontend/pages/projects/forks/new/components/project_namespace_spec.js
@@ -0,0 +1,177 @@
+import {
+ GlButton,
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownSectionHeader,
+ GlSearchBoxByType,
+ GlTruncate,
+} from '@gitlab/ui';
+import { mount, shallowMount } from '@vue/test-utils';
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import createFlash from '~/flash';
+import searchQuery from '~/pages/projects/forks/new/queries/search_forkable_namespaces.query.graphql';
+import ProjectNamespace from '~/pages/projects/forks/new/components/project_namespace.vue';
+
+jest.mock('~/flash');
+
+describe('ProjectNamespace component', () => {
+ let wrapper;
+ let originalGon;
+
+ const data = {
+ project: {
+ __typename: 'Project',
+ id: 'gid://gitlab/Project/1',
+ forkTargets: {
+ nodes: [
+ {
+ id: 'gid://gitlab/Group/21',
+ fullPath: 'flightjs',
+ name: 'Flight JS',
+ visibility: 'public',
+ },
+ {
+ id: 'gid://gitlab/Namespace/4',
+ fullPath: 'root',
+ name: 'Administrator',
+ visibility: 'public',
+ },
+ ],
+ },
+ },
+ };
+
+ const mockQueryResponse = jest.fn().mockResolvedValue({ data });
+
+ const emptyQueryResponse = {
+ project: {
+ __typename: 'Project',
+ id: 'gid://gitlab/Project/1',
+ forkTargets: {
+ nodes: [],
+ },
+ },
+ };
+
+ const mockQueryError = jest.fn().mockRejectedValue(new Error('Network error'));
+
+ Vue.use(VueApollo);
+
+ const gitlabUrl = 'https://gitlab.com';
+
+ const defaultProvide = {
+ projectFullPath: 'gitlab-org/project',
+ };
+
+ const mountComponent = ({
+ provide = defaultProvide,
+ queryHandler = mockQueryResponse,
+ mountFn = shallowMount,
+ } = {}) => {
+ const requestHandlers = [[searchQuery, queryHandler]];
+ const apolloProvider = createMockApollo(requestHandlers);
+
+ wrapper = mountFn(ProjectNamespace, {
+ apolloProvider,
+ provide,
+ });
+ };
+
+ const findButtonLabel = () => wrapper.findComponent(GlButton);
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
+ const findDropdownText = () => wrapper.findComponent(GlTruncate);
+ const findInput = () => wrapper.findComponent(GlSearchBoxByType);
+
+ const clickDropdownItem = async () => {
+ wrapper.findComponent(GlDropdownItem).vm.$emit('click');
+ await nextTick();
+ };
+
+ const showDropdown = () => {
+ findDropdown().vm.$emit('shown');
+ };
+
+ beforeAll(() => {
+ originalGon = window.gon;
+ window.gon = { gitlab_url: gitlabUrl };
+ });
+
+ afterAll(() => {
+ window.gon = originalGon;
+ wrapper.destroy();
+ });
+
+ describe('Initial state', () => {
+ beforeEach(() => {
+ mountComponent({ mountFn: mount });
+ jest.runOnlyPendingTimers();
+ });
+
+ it('renders the root url as a label', () => {
+ expect(findButtonLabel().text()).toBe(`${gitlabUrl}/`);
+ expect(findButtonLabel().props('label')).toBe(true);
+ });
+
+ it('renders placeholder text', () => {
+ expect(findDropdownText().props('text')).toBe('Select a namespace');
+ });
+ });
+
+ describe('After user interactions', () => {
+ beforeEach(async () => {
+ mountComponent({ mountFn: mount });
+ jest.runOnlyPendingTimers();
+ await nextTick();
+ showDropdown();
+ });
+
+ it('focuses on the input when the dropdown is opened', () => {
+ const spy = jest.spyOn(findInput().vm, 'focusInput');
+ showDropdown();
+ expect(spy).toHaveBeenCalledTimes(1);
+ });
+
+ it('displays fetched namespaces', () => {
+ const listItems = wrapper.findAll('li');
+ expect(listItems).toHaveLength(3);
+ expect(listItems.at(0).findComponent(GlDropdownSectionHeader).text()).toBe('Namespaces');
+ expect(listItems.at(1).text()).toBe(data.project.forkTargets.nodes[0].fullPath);
+ expect(listItems.at(2).text()).toBe(data.project.forkTargets.nodes[1].fullPath);
+ });
+
+ it('sets the selected namespace', async () => {
+ const { fullPath } = data.project.forkTargets.nodes[0];
+ await clickDropdownItem();
+ expect(findDropdownText().props('text')).toBe(fullPath);
+ });
+ });
+
+ describe('With empty query response', () => {
+ beforeEach(() => {
+ mountComponent({ queryHandler: emptyQueryResponse, mountFn: mount });
+ jest.runOnlyPendingTimers();
+ });
+
+ it('renders `No matches found`', () => {
+ expect(wrapper.find('li').text()).toBe('No matches found');
+ });
+ });
+
+ describe('With error while fetching data', () => {
+ beforeEach(async () => {
+ mountComponent({ queryHandler: mockQueryError });
+ jest.runOnlyPendingTimers();
+ await nextTick();
+ });
+
+ it('creates a flash message and captures the error', () => {
+ expect(createFlash).toHaveBeenCalledWith({
+ message: 'Something went wrong while loading data. Please refresh the page to try again.',
+ captureError: true,
+ error: expect.any(Error),
+ });
+ });
+ });
+});
diff --git a/spec/frontend/pages/projects/graphs/code_coverage_spec.js b/spec/frontend/pages/projects/graphs/code_coverage_spec.js
index f272891919d..2f2edd6b025 100644
--- a/spec/frontend/pages/projects/graphs/code_coverage_spec.js
+++ b/spec/frontend/pages/projects/graphs/code_coverage_spec.js
@@ -20,9 +20,9 @@ describe('Code Coverage', () => {
const graphRef = 'master';
const graphCsvPath = 'url/';
- const findAlert = () => wrapper.find(GlAlert);
- const findAreaChart = () => wrapper.find(GlAreaChart);
- const findAllDropdownItems = () => wrapper.findAll(GlDropdownItem);
+ 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 findDownloadButton = () => wrapper.find('[data-testid="download-button"]');
@@ -142,7 +142,7 @@ describe('Code Coverage', () => {
});
it('renders the dropdown with all custom names as options', () => {
- expect(wrapper.find(GlDropdown).exists()).toBeDefined();
+ expect(wrapper.findComponent(GlDropdown).exists()).toBeDefined();
expect(findAllDropdownItems()).toHaveLength(codeCoverageMockData.length);
expect(findFirstDropdownItem().text()).toBe(codeCoverageMockData[0].group_name);
});
diff --git a/spec/frontend/pages/projects/merge_requests/edit/update_form_spec.js b/spec/frontend/pages/projects/merge_requests/edit/update_form_spec.js
new file mode 100644
index 00000000000..72077038dff
--- /dev/null
+++ b/spec/frontend/pages/projects/merge_requests/edit/update_form_spec.js
@@ -0,0 +1,59 @@
+import { setHTMLFixture, resetHTMLFixture } from 'jest/__helpers__/fixtures';
+import initFormUpdate from '~/pages/projects/merge_requests/edit/update_form';
+
+describe('Update form state', () => {
+ const submitEvent = new Event('submit', {
+ bubbles: true,
+ cancelable: true,
+ });
+
+ const submitForm = () => document.querySelector('.merge-request-form').dispatchEvent(submitEvent);
+ const hiddenInputs = () => document.querySelectorAll('input[type="hidden"]');
+ const checkboxes = () => document.querySelectorAll('.js-form-update');
+
+ beforeEach(() => {
+ setHTMLFixture(`
+ <form class="merge-request-form">
+ <div class="form-check">
+ <input type="hidden" name="merge_request[force_remove_source_branch]" value="0" autocomplete="off">
+ <input type="checkbox" name="merge_request[force_remove_source_branch]" id="merge_request_force_remove_source_branch" value="1" class="form-check-input js-form-update">
+ </div>
+ <div class="form-check">
+ <input type="hidden" name="merge_request[squash]" value="0" autocomplete="off">
+ <input type="checkbox" name="merge_request[squash]" id="merge_request_squash" value="1" class="form-check-input js-form-update">
+ </div>
+ </form>`);
+ initFormUpdate();
+ });
+
+ afterEach(() => {
+ resetHTMLFixture();
+ });
+
+ it('at initial state', () => {
+ submitForm();
+ expect(hiddenInputs()).toHaveLength(2);
+ });
+
+ it('when one element is checked', () => {
+ checkboxes()[0].setAttribute('checked', true);
+ submitForm();
+ expect(hiddenInputs()).toHaveLength(1);
+ });
+
+ it('when all elements are checked', () => {
+ checkboxes()[0].setAttribute('checked', true);
+ checkboxes()[1].setAttribute('checked', true);
+ submitForm();
+ expect(hiddenInputs()).toHaveLength(0);
+ });
+
+ it('when checked and then unchecked', () => {
+ checkboxes()[0].setAttribute('checked', true);
+ checkboxes()[0].removeAttribute('checked');
+ checkboxes()[1].setAttribute('checked', true);
+ checkboxes()[1].removeAttribute('checked');
+ submitForm();
+ expect(hiddenInputs()).toHaveLength(2);
+ });
+});
diff --git a/spec/frontend/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js b/spec/frontend/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js
index ca7f70f4434..a633332ab65 100644
--- a/spec/frontend/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js
+++ b/spec/frontend/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js
@@ -21,7 +21,7 @@ describe('Pipeline Schedule Callout', () => {
};
const findInnerContentOfCallout = () => wrapper.find('[data-testid="innerContent"]');
- const findDismissCalloutBtn = () => wrapper.find(GlButton);
+ const findDismissCalloutBtn = () => wrapper.findComponent(GlButton);
describe(`when ${cookieKey} cookie is set`, () => {
beforeEach(async () => {
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 f908508c4b5..ed7d4ad269e 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
@@ -5,8 +5,12 @@ import settingsPanel from '~/pages/projects/shared/permissions/components/settin
import {
featureAccessLevel,
visibilityLevelDescriptions,
- visibilityOptions,
} from '~/pages/projects/shared/permissions/constants';
+import {
+ VISIBILITY_LEVEL_PRIVATE_INTEGER,
+ VISIBILITY_LEVEL_INTERNAL_INTEGER,
+ VISIBILITY_LEVEL_PUBLIC_INTEGER,
+} from '~/visibility_level/constants';
import ConfirmDanger from '~/vue_shared/components/confirm_danger/confirm_danger.vue';
const defaultProps = {
@@ -81,15 +85,17 @@ describe('Settings Panel', () => {
});
};
- const findLFSSettingsRow = () => wrapper.find({ ref: 'git-lfs-settings' });
+ const findLFSSettingsRow = () => wrapper.findComponent({ ref: 'git-lfs-settings' });
const findLFSSettingsMessage = () => findLFSSettingsRow().find('p');
- const findLFSFeatureToggle = () => findLFSSettingsRow().find(GlToggle);
- const findRepositoryFeatureProjectRow = () => wrapper.find({ ref: 'repository-settings' });
+ const findLFSFeatureToggle = () => findLFSSettingsRow().findComponent(GlToggle);
+ const findRepositoryFeatureProjectRow = () =>
+ wrapper.findComponent({ ref: 'repository-settings' });
const findRepositoryFeatureSetting = () =>
- findRepositoryFeatureProjectRow().find(ProjectFeatureSetting);
- const findProjectVisibilitySettings = () => wrapper.find({ ref: 'project-visibility-settings' });
- const findIssuesSettingsRow = () => wrapper.find({ ref: 'issues-settings' });
- const findAnalyticsRow = () => wrapper.find({ ref: 'analytics-settings' });
+ findRepositoryFeatureProjectRow().findComponent(ProjectFeatureSetting);
+ const findProjectVisibilitySettings = () =>
+ wrapper.findComponent({ ref: 'project-visibility-settings' });
+ const findIssuesSettingsRow = () => wrapper.findComponent({ ref: 'issues-settings' });
+ const findAnalyticsRow = () => wrapper.findComponent({ ref: 'analytics-settings' });
const findProjectVisibilityLevelInput = () => wrapper.find('[name="project[visibility_level]"]');
const findRequestAccessEnabledInput = () =>
wrapper.find('[name="project[request_access_enabled]"]');
@@ -99,35 +105,40 @@ describe('Settings Panel', () => {
wrapper.find('[name="project[project_feature_attributes][forking_access_level]"]');
const findBuildsAccessLevelInput = () =>
wrapper.find('[name="project[project_feature_attributes][builds_access_level]"]');
- const findContainerRegistrySettings = () => wrapper.find({ ref: 'container-registry-settings' });
+ const findContainerRegistrySettings = () =>
+ wrapper.findComponent({ ref: 'container-registry-settings' });
const findContainerRegistryPublicNoteGlSprintfComponent = () =>
findContainerRegistrySettings().findComponent(GlSprintf);
const findContainerRegistryAccessLevelInput = () =>
wrapper.find('[name="project[project_feature_attributes][container_registry_access_level]"]');
- const findPackageSettings = () => wrapper.find({ ref: 'package-settings' });
+ 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 findPagesSettings = () => wrapper.find({ ref: 'pages-settings' });
+ const findPagesSettings = () => wrapper.findComponent({ ref: 'pages-settings' });
const findPagesAccessLevels = () =>
wrapper.find('[name="project[project_feature_attributes][pages_access_level]"]');
- const findEmailSettings = () => wrapper.find({ ref: 'email-settings' });
+ const findEmailSettings = () => wrapper.findComponent({ ref: 'email-settings' });
const findShowDefaultAwardEmojis = () =>
wrapper.find('input[name="project[project_setting_attributes][show_default_award_emojis]"]');
const findWarnAboutPuc = () =>
wrapper.find(
'input[name="project[project_setting_attributes][warn_about_potentially_unwanted_characters]"]',
);
- const findMetricsVisibilitySettings = () => wrapper.find({ ref: 'metrics-visibility-settings' });
+ const findMetricsVisibilitySettings = () =>
+ wrapper.findComponent({ ref: 'metrics-visibility-settings' });
const findMetricsVisibilityInput = () =>
findMetricsVisibilitySettings().findComponent(ProjectFeatureSetting);
- const findOperationsSettings = () => wrapper.find({ ref: 'operations-settings' });
+ 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' });
+ const findReleasesSettings = () => wrapper.findComponent({ ref: 'environments-settings' });
+ const findMonitorSettings = () => wrapper.findComponent({ ref: 'monitor-settings' });
afterEach(() => {
wrapper.destroy();
@@ -156,13 +167,13 @@ describe('Settings Panel', () => {
});
it.each`
- option | allowedOptions | disabled
- ${visibilityOptions.PRIVATE} | ${[visibilityOptions.PRIVATE, visibilityOptions.INTERNAL, visibilityOptions.PUBLIC]} | ${false}
- ${visibilityOptions.PRIVATE} | ${[visibilityOptions.INTERNAL, visibilityOptions.PUBLIC]} | ${true}
- ${visibilityOptions.INTERNAL} | ${[visibilityOptions.PRIVATE, visibilityOptions.INTERNAL, visibilityOptions.PUBLIC]} | ${false}
- ${visibilityOptions.INTERNAL} | ${[visibilityOptions.PRIVATE, visibilityOptions.PUBLIC]} | ${true}
- ${visibilityOptions.PUBLIC} | ${[visibilityOptions.PRIVATE, visibilityOptions.INTERNAL, visibilityOptions.PUBLIC]} | ${false}
- ${visibilityOptions.PUBLIC} | ${[visibilityOptions.PRIVATE, visibilityOptions.INTERNAL]} | ${true}
+ option | allowedOptions | disabled
+ ${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${[VISIBILITY_LEVEL_PRIVATE_INTEGER, VISIBILITY_LEVEL_INTERNAL_INTEGER, VISIBILITY_LEVEL_PUBLIC_INTEGER]} | ${false}
+ ${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${[VISIBILITY_LEVEL_INTERNAL_INTEGER, VISIBILITY_LEVEL_PUBLIC_INTEGER]} | ${true}
+ ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${[VISIBILITY_LEVEL_PRIVATE_INTEGER, VISIBILITY_LEVEL_INTERNAL_INTEGER, VISIBILITY_LEVEL_PUBLIC_INTEGER]} | ${false}
+ ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${[VISIBILITY_LEVEL_PRIVATE_INTEGER, VISIBILITY_LEVEL_PUBLIC_INTEGER]} | ${true}
+ ${VISIBILITY_LEVEL_PUBLIC_INTEGER} | ${[VISIBILITY_LEVEL_PRIVATE_INTEGER, VISIBILITY_LEVEL_INTERNAL_INTEGER, VISIBILITY_LEVEL_PUBLIC_INTEGER]} | ${false}
+ ${VISIBILITY_LEVEL_PUBLIC_INTEGER} | ${[VISIBILITY_LEVEL_PRIVATE_INTEGER, VISIBILITY_LEVEL_INTERNAL_INTEGER]} | ${true}
`(
'sets disabled to $disabled for the visibility option $option when given $allowedOptions',
({ option, allowedOptions, disabled }) => {
@@ -181,35 +192,37 @@ describe('Settings Panel', () => {
it('should set the visibility level description based upon the selected visibility level', () => {
wrapper = mountComponent({ stubs: { GlSprintf } });
- findProjectVisibilityLevelInput().setValue(visibilityOptions.INTERNAL);
+ findProjectVisibilityLevelInput().setValue(VISIBILITY_LEVEL_INTERNAL_INTEGER);
expect(findProjectVisibilitySettings().text()).toContain(
- visibilityLevelDescriptions[visibilityOptions.INTERNAL],
+ visibilityLevelDescriptions[VISIBILITY_LEVEL_INTERNAL_INTEGER],
);
});
it('should show the request access checkbox if the visibility level is not private', () => {
wrapper = mountComponent({
- currentSettings: { visibilityLevel: visibilityOptions.INTERNAL },
+ currentSettings: { visibilityLevel: VISIBILITY_LEVEL_INTERNAL_INTEGER },
});
expect(findRequestAccessEnabledInput().exists()).toBe(true);
});
it('should not show the request access checkbox if the visibility level is private', () => {
- wrapper = mountComponent({ currentSettings: { visibilityLevel: visibilityOptions.PRIVATE } });
+ wrapper = mountComponent({
+ currentSettings: { visibilityLevel: VISIBILITY_LEVEL_PRIVATE_INTEGER },
+ });
expect(findRequestAccessEnabledInput().exists()).toBe(false);
});
it('does not require confirmation if the visibility is reduced', async () => {
wrapper = mountComponent({
- currentSettings: { visibilityLevel: visibilityOptions.INTERNAL },
+ currentSettings: { visibilityLevel: VISIBILITY_LEVEL_INTERNAL_INTEGER },
});
expect(findConfirmDangerButton().exists()).toBe(false);
- await findProjectVisibilityLevelInput().setValue(visibilityOptions.PRIVATE);
+ await findProjectVisibilityLevelInput().setValue(VISIBILITY_LEVEL_PRIVATE_INTEGER);
expect(findConfirmDangerButton().exists()).toBe(false);
});
@@ -217,7 +230,7 @@ describe('Settings Panel', () => {
describe('showVisibilityConfirmModal=true', () => {
beforeEach(() => {
wrapper = mountComponent({
- currentSettings: { visibilityLevel: visibilityOptions.INTERNAL },
+ currentSettings: { visibilityLevel: VISIBILITY_LEVEL_INTERNAL_INTEGER },
showVisibilityConfirmModal: true,
});
});
@@ -225,7 +238,7 @@ describe('Settings Panel', () => {
it('will render the confirmation dialog if the visibility is reduced', async () => {
expect(findConfirmDangerButton().exists()).toBe(false);
- await findProjectVisibilityLevelInput().setValue(visibilityOptions.PRIVATE);
+ await findProjectVisibilityLevelInput().setValue(VISIBILITY_LEVEL_PRIVATE_INTEGER);
expect(findConfirmDangerButton().exists()).toBe(true);
});
@@ -233,7 +246,7 @@ describe('Settings Panel', () => {
it('emits the `confirm` event when the reduce visibility warning is confirmed', async () => {
expect(wrapper.emitted('confirm')).toBeUndefined();
- await findProjectVisibilityLevelInput().setValue(visibilityOptions.PRIVATE);
+ await findProjectVisibilityLevelInput().setValue(VISIBILITY_LEVEL_PRIVATE_INTEGER);
await findConfirmDangerButton().vm.$emit('confirm');
expect(wrapper.emitted('confirm')).toHaveLength(1);
@@ -253,7 +266,9 @@ describe('Settings Panel', () => {
describe('Repository', () => {
it('should set the repository help text when the visibility level is set to private', () => {
- wrapper = mountComponent({ currentSettings: { visibilityLevel: visibilityOptions.PRIVATE } });
+ wrapper = mountComponent({
+ currentSettings: { visibilityLevel: VISIBILITY_LEVEL_PRIVATE_INTEGER },
+ });
expect(findRepositoryFeatureProjectRow().props('helpText')).toBe(
'View and edit files in this project.',
@@ -261,7 +276,9 @@ describe('Settings Panel', () => {
});
it('should set the repository help text with a read access warning when the visibility level is set to non-private', () => {
- wrapper = mountComponent({ currentSettings: { visibilityLevel: visibilityOptions.PUBLIC } });
+ wrapper = mountComponent({
+ currentSettings: { visibilityLevel: VISIBILITY_LEVEL_PUBLIC_INTEGER },
+ });
expect(findRepositoryFeatureProjectRow().props('helpText')).toBe(
'View and edit files in this project. Non-project members have only read access.',
@@ -345,7 +362,7 @@ describe('Settings Panel', () => {
it('should show the container registry public note if the visibility level is public and the registry is available', () => {
wrapper = mountComponent({
currentSettings: {
- visibilityLevel: visibilityOptions.PUBLIC,
+ visibilityLevel: VISIBILITY_LEVEL_PUBLIC_INTEGER,
containerRegistryAccessLevel: featureAccessLevel.EVERYONE,
},
registryAvailable: true,
@@ -360,7 +377,7 @@ describe('Settings Panel', () => {
it('should hide the container registry public note if the visibility level is public but the registry is private', () => {
wrapper = mountComponent({
currentSettings: {
- visibilityLevel: visibilityOptions.PUBLIC,
+ visibilityLevel: VISIBILITY_LEVEL_PUBLIC_INTEGER,
containerRegistryAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
},
registryAvailable: true,
@@ -371,7 +388,7 @@ describe('Settings Panel', () => {
it('should hide the container registry public note if the visibility level is private and the registry is available', () => {
wrapper = mountComponent({
- currentSettings: { visibilityLevel: visibilityOptions.PRIVATE },
+ currentSettings: { visibilityLevel: VISIBILITY_LEVEL_PRIVATE_INTEGER },
registryAvailable: true,
});
@@ -380,7 +397,7 @@ describe('Settings Panel', () => {
it('has label for the toggle', () => {
wrapper = mountComponent({
- currentSettings: { visibilityLevel: visibilityOptions.PUBLIC },
+ currentSettings: { visibilityLevel: VISIBILITY_LEVEL_PUBLIC_INTEGER },
registryAvailable: true,
});
@@ -569,10 +586,10 @@ describe('Settings Panel', () => {
});
it.each`
- visibilityLevel | output
- ${visibilityOptions.PRIVATE} | ${[[featureAccessLevel.PROJECT_MEMBERS, 'Only Project Members'], [30, 'Everyone']]}
- ${visibilityOptions.INTERNAL} | ${[[featureAccessLevel.EVERYONE, 'Everyone With Access'], [30, 'Everyone']]}
- ${visibilityOptions.PUBLIC} | ${[[30, 'Everyone']]}
+ 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']]}
`(
'renders correct options when visibilityLevel is $visibilityLevel',
async ({ visibilityLevel, output }) => {
@@ -589,23 +606,23 @@ describe('Settings Panel', () => {
);
it.each`
- initialProjectVisibilityLevel | newProjectVisibilityLevel | initialPackageRegistryOption | expectedPackageRegistryOption
- ${visibilityOptions.PRIVATE} | ${visibilityOptions.INTERNAL} | ${featureAccessLevel.NOT_ENABLED} | ${featureAccessLevel.NOT_ENABLED}
- ${visibilityOptions.PRIVATE} | ${visibilityOptions.INTERNAL} | ${featureAccessLevel.PROJECT_MEMBERS} | ${featureAccessLevel.EVERYONE}
- ${visibilityOptions.PRIVATE} | ${visibilityOptions.INTERNAL} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS}
- ${visibilityOptions.PRIVATE} | ${visibilityOptions.PUBLIC} | ${featureAccessLevel.NOT_ENABLED} | ${featureAccessLevel.NOT_ENABLED}
- ${visibilityOptions.PRIVATE} | ${visibilityOptions.PUBLIC} | ${featureAccessLevel.PROJECT_MEMBERS} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS}
- ${visibilityOptions.PRIVATE} | ${visibilityOptions.PUBLIC} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS}
- ${visibilityOptions.INTERNAL} | ${visibilityOptions.PRIVATE} | ${featureAccessLevel.NOT_ENABLED} | ${featureAccessLevel.NOT_ENABLED}
- ${visibilityOptions.INTERNAL} | ${visibilityOptions.PRIVATE} | ${featureAccessLevel.EVERYONE} | ${featureAccessLevel.PROJECT_MEMBERS}
- ${visibilityOptions.INTERNAL} | ${visibilityOptions.PRIVATE} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS}
- ${visibilityOptions.INTERNAL} | ${visibilityOptions.PUBLIC} | ${featureAccessLevel.NOT_ENABLED} | ${featureAccessLevel.NOT_ENABLED}
- ${visibilityOptions.INTERNAL} | ${visibilityOptions.PUBLIC} | ${featureAccessLevel.EVERYONE} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS}
- ${visibilityOptions.INTERNAL} | ${visibilityOptions.PUBLIC} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS}
- ${visibilityOptions.PUBLIC} | ${visibilityOptions.PRIVATE} | ${featureAccessLevel.NOT_ENABLED} | ${featureAccessLevel.NOT_ENABLED}
- ${visibilityOptions.PUBLIC} | ${visibilityOptions.PRIVATE} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS} | ${featureAccessLevel.PROJECT_MEMBERS}
- ${visibilityOptions.PUBLIC} | ${visibilityOptions.INTERNAL} | ${featureAccessLevel.NOT_ENABLED} | ${featureAccessLevel.NOT_ENABLED}
- ${visibilityOptions.PUBLIC} | ${visibilityOptions.INTERNAL} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS} | ${featureAccessLevel.EVERYONE}
+ initialProjectVisibilityLevel | newProjectVisibilityLevel | initialPackageRegistryOption | expectedPackageRegistryOption
+ ${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}
+ ${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${VISIBILITY_LEVEL_PUBLIC_INTEGER} | ${featureAccessLevel.NOT_ENABLED} | ${featureAccessLevel.NOT_ENABLED}
+ ${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${VISIBILITY_LEVEL_PUBLIC_INTEGER} | ${featureAccessLevel.PROJECT_MEMBERS} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS}
+ ${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${VISIBILITY_LEVEL_PUBLIC_INTEGER} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS}
+ ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${featureAccessLevel.NOT_ENABLED} | ${featureAccessLevel.NOT_ENABLED}
+ ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${featureAccessLevel.EVERYONE} | ${featureAccessLevel.PROJECT_MEMBERS}
+ ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS}
+ ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${VISIBILITY_LEVEL_PUBLIC_INTEGER} | ${featureAccessLevel.NOT_ENABLED} | ${featureAccessLevel.NOT_ENABLED}
+ ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${VISIBILITY_LEVEL_PUBLIC_INTEGER} | ${featureAccessLevel.EVERYONE} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS}
+ ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${VISIBILITY_LEVEL_PUBLIC_INTEGER} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS}
+ ${VISIBILITY_LEVEL_PUBLIC_INTEGER} | ${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${featureAccessLevel.NOT_ENABLED} | ${featureAccessLevel.NOT_ENABLED}
+ ${VISIBILITY_LEVEL_PUBLIC_INTEGER} | ${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${FEATURE_ACCESS_LEVEL_ANONYMOUS} | ${featureAccessLevel.PROJECT_MEMBERS}
+ ${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',
async ({
@@ -635,13 +652,13 @@ describe('Settings Panel', () => {
describe('Pages', () => {
it.each`
- visibilityLevel | pagesAccessControlForced | output
- ${visibilityOptions.PRIVATE} | ${true} | ${[[visibilityOptions.INTERNAL, 'Only Project Members'], [visibilityOptions.PUBLIC, 'Everyone With Access']]}
- ${visibilityOptions.PRIVATE} | ${false} | ${[[visibilityOptions.INTERNAL, 'Only Project Members'], [visibilityOptions.PUBLIC, 'Everyone With Access'], [30, 'Everyone']]}
- ${visibilityOptions.INTERNAL} | ${true} | ${[[visibilityOptions.INTERNAL, 'Only Project Members'], [visibilityOptions.PUBLIC, 'Everyone With Access']]}
- ${visibilityOptions.INTERNAL} | ${false} | ${[[visibilityOptions.INTERNAL, 'Only Project Members'], [visibilityOptions.PUBLIC, 'Everyone With Access'], [30, 'Everyone']]}
- ${visibilityOptions.PUBLIC} | ${true} | ${[[visibilityOptions.INTERNAL, 'Only Project Members'], [visibilityOptions.PUBLIC, 'Everyone With Access']]}
- ${visibilityOptions.PUBLIC} | ${false} | ${[[visibilityOptions.INTERNAL, 'Only Project Members'], [visibilityOptions.PUBLIC, 'Everyone With Access'], [30, 'Everyone']]}
+ visibilityLevel | pagesAccessControlForced | output
+ ${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${true} | ${[[VISIBILITY_LEVEL_INTERNAL_INTEGER, 'Only Project Members'], [VISIBILITY_LEVEL_PUBLIC_INTEGER, 'Everyone With Access']]}
+ ${VISIBILITY_LEVEL_PRIVATE_INTEGER} | ${false} | ${[[VISIBILITY_LEVEL_INTERNAL_INTEGER, 'Only Project Members'], [VISIBILITY_LEVEL_PUBLIC_INTEGER, 'Everyone With Access'], [30, 'Everyone']]}
+ ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${true} | ${[[VISIBILITY_LEVEL_INTERNAL_INTEGER, 'Only Project Members'], [VISIBILITY_LEVEL_PUBLIC_INTEGER, 'Everyone With Access']]}
+ ${VISIBILITY_LEVEL_INTERNAL_INTEGER} | ${false} | ${[[VISIBILITY_LEVEL_INTERNAL_INTEGER, 'Only Project Members'], [VISIBILITY_LEVEL_PUBLIC_INTEGER, 'Everyone With Access'], [30, 'Everyone']]}
+ ${VISIBILITY_LEVEL_PUBLIC_INTEGER} | ${true} | ${[[VISIBILITY_LEVEL_INTERNAL_INTEGER, 'Only Project Members'], [VISIBILITY_LEVEL_PUBLIC_INTEGER, 'Everyone With Access']]}
+ ${VISIBILITY_LEVEL_PUBLIC_INTEGER} | ${false} | ${[[VISIBILITY_LEVEL_INTERNAL_INTEGER, 'Only Project Members'], [VISIBILITY_LEVEL_PUBLIC_INTEGER, 'Everyone With Access'], [30, 'Everyone']]}
`(
'renders correct options when pagesAccessControlForced is $pagesAccessControlForced and visibilityLevel is $visibilityLevel',
async ({ visibilityLevel, pagesAccessControlForced, output }) => {
@@ -760,13 +777,13 @@ describe('Settings Panel', () => {
it('should reduce Metrics visibility level when visibility is set to private', async () => {
wrapper = mountComponent({
currentSettings: {
- visibilityLevel: visibilityOptions.PUBLIC,
+ visibilityLevel: VISIBILITY_LEVEL_PUBLIC_INTEGER,
operationsAccessLevel: featureAccessLevel.EVERYONE,
metricsDashboardAccessLevel: featureAccessLevel.EVERYONE,
},
});
- await findProjectVisibilityLevelInput().setValue(visibilityOptions.PRIVATE);
+ await findProjectVisibilityLevelInput().setValue(VISIBILITY_LEVEL_PRIVATE_INTEGER);
expect(findMetricsVisibilityInput().props('value')).toBe(featureAccessLevel.PROJECT_MEMBERS);
});
@@ -806,4 +823,78 @@ describe('Settings Panel', () => {
});
});
});
+ 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({});
+
+ expect(findFeatureFlagsSettings().exists()).toBe(false);
+ });
+ });
+ });
+ describe('Releases', () => {
+ describe('with feature flag', () => {
+ it('should show the releases toggle', () => {
+ wrapper = mountComponent({
+ glFeatures: { splitOperationsVisibilityPermissions: true },
+ });
+
+ expect(findReleasesSettings().exists()).toBe(true);
+ });
+ });
+ describe('without feature flag', () => {
+ it('should not show the releases toggle', () => {
+ wrapper = mountComponent({});
+
+ expect(findReleasesSettings().exists()).toBe(false);
+ });
+ });
+ });
+ describe('Monitor', () => {
+ const expectedAccessLevel = [
+ [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 },
+ });
+
+ expect(findMetricsVisibilityInput().props('value')).toBe(featureAccessLevel.EVERYONE);
+ });
+ });
+ 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);
+ });
+ });
+ });
});
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 108f816fe01..982c81b9272 100644
--- a/spec/frontend/pages/shared/wikis/components/wiki_content_spec.js
+++ b/spec/frontend/pages/shared/wikis/components/wiki_content_spec.js
@@ -38,7 +38,7 @@ describe('pages/shared/wikis/components/wiki_content', () => {
const findGlAlert = () => wrapper.findComponent(GlAlert);
const findGlSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
- const findContent = () => wrapper.find('[data-testid="wiki_page_content"]');
+ const findContent = () => wrapper.find('[data-testid="wiki-page-content"]');
describe('when loading content', () => {
beforeEach(() => {
diff --git a/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js b/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js
index 204c48f8de1..b37d2f06191 100644
--- a/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js
+++ b/spec/frontend/pages/shared/wikis/components/wiki_form_spec.js
@@ -39,7 +39,7 @@ describe('WikiForm', () => {
const findMarkdownHelpLink = () => wrapper.findByTestId('wiki-markdown-help-link');
const findContentEditor = () => wrapper.findComponent(ContentEditor);
const findClassicEditor = () => wrapper.findComponent(MarkdownField);
- const findLocalStorageSync = () => wrapper.find(LocalStorageSync);
+ const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
const setFormat = (value) => {
const format = findFormat();
@@ -302,19 +302,15 @@ describe('WikiForm', () => {
});
it.each`
- format | enabled | action
+ format | exists | action
${'markdown'} | ${true} | ${'displays'}
${'rdoc'} | ${false} | ${'hides'}
${'asciidoc'} | ${false} | ${'hides'}
${'org'} | ${false} | ${'hides'}
- `('$action toggle editing mode button when format is $format', async ({ format, enabled }) => {
+ `('$action toggle editing mode button when format is $format', async ({ format, exists }) => {
await setFormat(format);
- expect(findToggleEditingModeButton().exists()).toBe(enabled);
- });
-
- it('displays toggle editing mode button', () => {
- expect(findToggleEditingModeButton().exists()).toBe(true);
+ expect(findToggleEditingModeButton().exists()).toBe(exists);
});
describe('when content editor is not active', () => {
@@ -351,15 +347,8 @@ describe('WikiForm', () => {
});
describe('when content editor is active', () => {
- let mockContentEditor;
-
beforeEach(() => {
createWrapper();
- mockContentEditor = {
- getSerializedContent: jest.fn(),
- setSerializedContent: jest.fn(),
- };
-
findToggleEditingModeButton().vm.$emit('input', 'richText');
});
@@ -368,14 +357,7 @@ describe('WikiForm', () => {
});
describe('when clicking the toggle editing mode button', () => {
- const contentEditorFakeSerializedContent = 'fake content';
-
beforeEach(async () => {
- mockContentEditor.getSerializedContent.mockReturnValueOnce(
- contentEditorFakeSerializedContent,
- );
-
- findContentEditor().vm.$emit('initialized', mockContentEditor);
await findToggleEditingModeButton().vm.$emit('input', 'source');
await nextTick();
});
@@ -387,10 +369,6 @@ describe('WikiForm', () => {
it('displays the classic editor', () => {
expect(findClassicEditor().exists()).toBe(true);
});
-
- it('updates the classic editor content field', () => {
- expect(findContent().element.value).toBe(contentEditorFakeSerializedContent);
- });
});
describe('when content editor is loading', () => {
@@ -480,8 +458,14 @@ describe('WikiForm', () => {
});
describe('when wiki content is updated', () => {
+ const updatedMarkdown = 'hello **world**';
+
beforeEach(() => {
- findContentEditor().vm.$emit('change', { empty: false });
+ findContentEditor().vm.$emit('change', {
+ empty: false,
+ changed: true,
+ markdown: updatedMarkdown,
+ });
});
it('sets before unload warning', () => {
@@ -512,16 +496,8 @@ describe('WikiForm', () => {
});
});
- it('updates content from content editor on form submit', async () => {
- // old value
- expect(findContent().element.value).toBe(' My page content ');
-
- // wait for content editor to load
- await waitForPromises();
-
- await triggerFormSubmit();
-
- expect(findContent().element.value).toBe('hello **world**');
+ it('sets content field to the content editor updated markdown', async () => {
+ expect(findContent().element.value).toBe(updatedMarkdown);
});
});
});
diff --git a/spec/frontend/performance_bar/components/add_request_spec.js b/spec/frontend/performance_bar/components/add_request_spec.js
index 627e004ce3e..5460feb66fe 100644
--- a/spec/frontend/performance_bar/components/add_request_spec.js
+++ b/spec/frontend/performance_bar/components/add_request_spec.js
@@ -51,7 +51,7 @@ describe('add request form', () => {
});
it('emits an event to add the request', () => {
- expect(wrapper.emitted()['add-request']).toBeTruthy();
+ expect(wrapper.emitted()['add-request']).toHaveLength(1);
expect(wrapper.emitted()['add-request'][0]).toEqual([
'http://gitlab.example.com/users/root/calendar.json',
]);
diff --git a/spec/frontend/performance_bar/components/detailed_metric_spec.js b/spec/frontend/performance_bar/components/detailed_metric_spec.js
index 2ae36740dfb..437d51e02ba 100644
--- a/spec/frontend/performance_bar/components/detailed_metric_spec.js
+++ b/spec/frontend/performance_bar/components/detailed_metric_spec.js
@@ -257,7 +257,7 @@ describe('detailedMetric', () => {
});
it('displays request warnings', () => {
- expect(wrapper.find(RequestWarning).exists()).toBe(true);
+ expect(wrapper.findComponent(RequestWarning).exists()).toBe(true);
});
it('can open and close traces', async () => {
diff --git a/spec/frontend/persistent_user_callout_spec.js b/spec/frontend/persistent_user_callout_spec.js
index bff8fcda9b9..9cd5bb9e9a1 100644
--- a/spec/frontend/persistent_user_callout_spec.js
+++ b/spec/frontend/persistent_user_callout_spec.js
@@ -201,7 +201,7 @@ describe('PersistentUserCallout', () => {
await waitForPromises();
- expect(window.location.assign).toBeCalledWith(href);
+ expect(window.location.assign).toHaveBeenCalledWith(href);
expect(persistentUserCallout.container.remove).not.toHaveBeenCalled();
expect(mockAxios.history.post[0].data).toBe(JSON.stringify({ feature_name: featureName }));
});
diff --git a/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js b/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js
index bec6c2a8d0c..0ee6da9d329 100644
--- a/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js
+++ b/spec/frontend/pipeline_editor/components/commit/commit_form_spec.js
@@ -152,7 +152,7 @@ describe('Pipeline Editor | Commit Form', () => {
});
it('emits "scrolled-to-commit-form"', () => {
- expect(wrapper.emitted()['scrolled-to-commit-form']).toBeTruthy();
+ 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
index 33c76309951..744b0378a75 100644
--- a/spec/frontend/pipeline_editor/components/commit/commit_section_spec.js
+++ b/spec/frontend/pipeline_editor/components/commit/commit_section_spec.js
@@ -224,7 +224,7 @@ describe('Pipeline Editor | Commit section', () => {
});
it('emits a commit event with the right type, sourceBranch and targetBranch', () => {
- expect(wrapper.emitted('commit')).toBeTruthy();
+ expect(wrapper.emitted('commit')).toHaveLength(1);
expect(wrapper.emitted('commit')[0]).toMatchObject([
{
type: COMMIT_SUCCESS_WITH_REDIRECT,
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
index 7dbacad34bf..8f6f4d8cff9 100644
--- a/spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js
+++ b/spec/frontend/pipeline_editor/components/file-nav/branch_switcher_spec.js
@@ -119,7 +119,7 @@ describe('Pipeline editor branch switcher', () => {
};
const findDropdown = () => wrapper.findComponent(GlDropdown);
- const findDropdownItems = () => wrapper.findAll(GlDropdownItem);
+ const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
const findInfiniteScroll = () => wrapper.findComponent(GlInfiniteScroll);
diff --git a/spec/frontend/pipeline_editor/components/file-tree/container_spec.js b/spec/frontend/pipeline_editor/components/file-tree/container_spec.js
index 04a93e8db25..f79074f1e0f 100644
--- a/spec/frontend/pipeline_editor/components/file-tree/container_spec.js
+++ b/spec/frontend/pipeline_editor/components/file-tree/container_spec.js
@@ -31,7 +31,7 @@ describe('Pipeline editor file nav', () => {
const findTip = () => wrapper.findComponent(GlAlert);
const findCurrentConfigFilename = () => wrapper.findByTestId('current-config-filename');
- const fileTreeItems = () => wrapper.findAll(PipelineEditorFileTreeItem);
+ const fileTreeItems = () => wrapper.findAllComponents(PipelineEditorFileTreeItem);
afterEach(() => {
localStorage.clear();
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
new file mode 100644
index 00000000000..d40a9cc8100
--- /dev/null
+++ b/spec/frontend/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 '~/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/pipline_editor_mini_graph_spec.js b/spec/frontend/pipeline_editor/components/header/pipline_editor_mini_graph_spec.js
index 93eb18c90cf..d40a9cc8100 100644
--- 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
@@ -4,7 +4,7 @@ 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/pipelines_list/pipeline_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';
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
index 82ac390971d..7f89eda4dff 100644
--- a/spec/frontend/pipeline_editor/components/lint/ci_lint_results_spec.js
+++ b/spec/frontend/pipeline_editor/components/lint/ci_lint_results_spec.js
@@ -24,11 +24,11 @@ describe('CI Lint Results', () => {
});
};
- const findTable = () => wrapper.find(GlTableLite);
+ 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.find(GlLink);
+ const findLinkToDoc = () => wrapper.findComponent(GlLink);
const findErrors = findByTestId('errors');
const findWarnings = findByTestId('warnings');
const findStatus = findByTestId('status');
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
index 4b576508ee9..36052a2e16a 100644
--- a/spec/frontend/pipeline_editor/components/lint/ci_lint_warnings_spec.js
+++ b/spec/frontend/pipeline_editor/components/lint/ci_lint_warnings_spec.js
@@ -17,9 +17,9 @@ describe('CI lint warnings', () => {
});
};
- const findWarningAlert = () => wrapper.find(GlAlert);
+ const findWarningAlert = () => wrapper.findComponent(GlAlert);
const findWarnings = () => wrapper.findAll('[data-testid="ci-lint-warning"]');
- const findWarningMessage = () => trimText(wrapper.find(GlSprintf).text());
+ const findWarningMessage = () => trimText(wrapper.findComponent(GlSprintf).text());
afterEach(() => {
wrapper.destroy();
diff --git a/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js b/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js
index 2f3e1b49b37..3b79739630d 100644
--- a/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js
+++ b/spec/frontend/pipeline_editor/components/pipeline_editor_tabs_spec.js
@@ -256,7 +256,7 @@ describe('Pipeline editor tabs component', () => {
${EDITOR_APP_STATUS_INVALID} | ${true} | ${false} | ${true} | ${false}
${EDITOR_APP_STATUS_VALID} | ${true} | ${true} | ${true} | ${true}
`(
- 'when status is $appStatus, we show - editor:$editor | viz:$viz | validate:$validate | merged:$merged ',
+ 'when status is $appStatus, we show - editor:$editor | viz:$viz | validate:$validate | merged:$merged',
({ appStatus, editor, viz, validate, merged }) => {
createComponent({ appStatus });
diff --git a/spec/frontend/pipeline_editor/components/popovers/walkthrough_popover_spec.js b/spec/frontend/pipeline_editor/components/popovers/walkthrough_popover_spec.js
index 8d172a8462a..b86c82850c5 100644
--- a/spec/frontend/pipeline_editor/components/popovers/walkthrough_popover_spec.js
+++ b/spec/frontend/pipeline_editor/components/popovers/walkthrough_popover_spec.js
@@ -23,7 +23,7 @@ describe('WalkthroughPopover component', () => {
});
it('emits "walkthrough-popover-cta-clicked" event', async () => {
- expect(wrapper.emitted()['walkthrough-popover-cta-clicked']).toBeTruthy();
+ expect(wrapper.emitted()['walkthrough-popover-cta-clicked']).toHaveLength(1);
});
});
});
diff --git a/spec/frontend/pipeline_editor/components/ui/editor_tab_spec.js b/spec/frontend/pipeline_editor/components/ui/editor_tab_spec.js
index 3a40ce32a24..24f27e8c5fb 100644
--- a/spec/frontend/pipeline_editor/components/ui/editor_tab_spec.js
+++ b/spec/frontend/pipeline_editor/components/ui/editor_tab_spec.js
@@ -58,7 +58,7 @@ describe('~/pipeline_editor/components/ui/editor_tab.vue', () => {
const findSlotComponent = () => wrapper.findComponent(MockSourceEditor);
const findAlert = () => wrapper.findComponent(GlAlert);
- const findBadges = () => wrapper.findAll(GlBadge);
+ const findBadges = () => wrapper.findAllComponents(GlBadge);
beforeEach(() => {
mockChildMounted = jest.fn();
diff --git a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
index 0ce6cc3f2d4..1989f23a415 100644
--- a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
+++ b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js
@@ -149,8 +149,7 @@ describe('Pipeline editor app component', () => {
const findAlert = () => wrapper.findComponent(GlAlert);
const findEditorHome = () => wrapper.findComponent(PipelineEditorHome);
const findEmptyState = () => wrapper.findComponent(PipelineEditorEmptyState);
- const findEmptyStateButton = () =>
- wrapper.findComponent(PipelineEditorEmptyState).findComponent(GlButton);
+ const findEmptyStateButton = () => findEmptyState().findComponent(GlButton);
const findValidationSegment = () => wrapper.findComponent(ValidationSegment);
beforeEach(() => {
diff --git a/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js
index 0cb7155c8c0..e317d1ddcc2 100644
--- a/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js
+++ b/spec/frontend/pipeline_editor/pipeline_editor_home_spec.js
@@ -254,7 +254,7 @@ describe('Pipeline editor home wrapper', () => {
expect(findPipelineEditorDrawer().props('isVisible')).toBe(true);
- findPipelineEditorDrawer().find(GlDrawer).vm.$emit('close');
+ findPipelineEditorDrawer().findComponent(GlDrawer).vm.$emit('close');
await nextTick();
expect(findPipelineEditorDrawer().props('isVisible')).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
new file mode 100644
index 00000000000..512b152f106
--- /dev/null
+++ b/spec/frontend/pipeline_new/components/legacy_pipeline_new_form_spec.js
@@ -0,0 +1,456 @@
+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 18dbd1ce9d6..5ce29bd6c5d 100644
--- a/spec/frontend/pipeline_new/components/pipeline_new_form_spec.js
+++ b/spec/frontend/pipeline_new/components/pipeline_new_form_spec.js
@@ -34,7 +34,7 @@ describe('Pipeline New Form', () => {
let mock;
let dummySubmitEvent;
- const findForm = () => wrapper.find(GlForm);
+ 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"]');
@@ -44,9 +44,9 @@ describe('Pipeline New Form', () => {
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().find(GlSprintf);
+ const findWarningAlertSummary = () => findWarningAlert().findComponent(GlSprintf);
const findWarnings = () => wrapper.findAll('[data-testid="run-pipeline-warning"]');
- const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findCCAlert = () => wrapper.findComponent(CreditCardValidationRequiredAlert);
const getFormPostParams = () => JSON.parse(mock.history.post[0].data);
@@ -329,6 +329,12 @@ describe('Pipeline New Form', () => {
value: mockYmlValue,
description: null,
},
+ yml_var2: {
+ value: 'yml_var2_val',
+ },
+ yml_var3: {
+ description: '',
+ },
});
await waitForPromises();
diff --git a/spec/frontend/pipeline_new/components/refs_dropdown_spec.js b/spec/frontend/pipeline_new/components/refs_dropdown_spec.js
index 826f2826d3c..8cba876c688 100644
--- a/spec/frontend/pipeline_new/components/refs_dropdown_spec.js
+++ b/spec/frontend/pipeline_new/components/refs_dropdown_spec.js
@@ -19,8 +19,8 @@ describe('Pipeline New Form', () => {
let wrapper;
let mock;
- const findDropdown = () => wrapper.find(GlDropdown);
- const findRefsDropdownItems = () => wrapper.findAll(GlDropdownItem);
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
+ const findRefsDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
const createComponent = (props = {}, mountFn = shallowMount) => {
diff --git a/spec/frontend/pipeline_wizard/components/commit_spec.js b/spec/frontend/pipeline_wizard/components/commit_spec.js
index c987accbb0d..d7e019c642e 100644
--- a/spec/frontend/pipeline_wizard/components/commit_spec.js
+++ b/spec/frontend/pipeline_wizard/components/commit_spec.js
@@ -174,7 +174,7 @@ describe('Pipeline Wizard - Commit Page', () => {
});
it('will not emit a done event', () => {
- expect(wrapper.emitted().done?.length).toBeFalsy();
+ expect(wrapper.emitted().done?.length).toBeUndefined();
});
afterEach(() => {
diff --git a/spec/frontend/pipeline_wizard/components/editor_spec.js b/spec/frontend/pipeline_wizard/components/editor_spec.js
index 540a08d2c7f..26e4b8eb0ea 100644
--- a/spec/frontend/pipeline_wizard/components/editor_spec.js
+++ b/spec/frontend/pipeline_wizard/components/editor_spec.js
@@ -11,7 +11,7 @@ describe('Pages Yaml Editor wrapper', () => {
const wrapper = mount(YamlEditor, defaultOptions);
it('editor is mounted', () => {
- expect(wrapper.vm.editor).not.toBeFalsy();
+ expect(wrapper.vm.editor).not.toBeUndefined();
expect(wrapper.find('.gl-source-editor').exists()).toBe(true);
});
});
@@ -57,13 +57,4 @@ describe('Pages Yaml Editor wrapper', () => {
});
});
});
-
- describe('events', () => {
- const wrapper = mount(YamlEditor, defaultOptions);
-
- it('emits touch if content is changed in editor', async () => {
- await wrapper.vm.editor.setValue('foo: boo');
- expect(wrapper.emitted('touch')).toEqual([expect.any(Array)]);
- });
- });
});
diff --git a/spec/frontend/pipeline_wizard/components/input_wrapper_spec.js b/spec/frontend/pipeline_wizard/components/input_wrapper_spec.js
index ea2448b1362..f288264a11e 100644
--- a/spec/frontend/pipeline_wizard/components/input_wrapper_spec.js
+++ b/spec/frontend/pipeline_wizard/components/input_wrapper_spec.js
@@ -30,7 +30,7 @@ describe('Pipeline Wizard -- Input Wrapper', () => {
beforeEach(() => {
createComponent({});
- inputChild = wrapper.find(TextWidget);
+ inputChild = wrapper.findComponent(TextWidget);
});
afterEach(() => {
diff --git a/spec/frontend/pipeline_wizard/components/wrapper_spec.js b/spec/frontend/pipeline_wizard/components/wrapper_spec.js
index 357a9d21723..f064bf01c86 100644
--- a/spec/frontend/pipeline_wizard/components/wrapper_spec.js
+++ b/spec/frontend/pipeline_wizard/components/wrapper_spec.js
@@ -2,6 +2,7 @@ import { Document, parseDocument } from 'yaml';
import { GlProgressBar } from '@gitlab/ui';
import { nextTick } from 'vue';
import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
+import { mockTracking } from 'helpers/tracking_helper';
import PipelineWizardWrapper, { i18n } from '~/pipeline_wizard/components/wrapper.vue';
import WizardStep from '~/pipeline_wizard/components/step.vue';
import CommitStep from '~/pipeline_wizard/components/commit.vue';
@@ -19,9 +20,11 @@ describe('Pipeline Wizard - wrapper.vue', () => {
const steps = parseDocument(stepsYaml).toJS();
const getAsYamlNode = (value) => new Document(value).contents;
+ const templateId = 'my-namespace/my-template';
const createComponent = (props = {}, mountFn = shallowMountExtended) => {
wrapper = mountFn(PipelineWizardWrapper, {
propsData: {
+ templateId,
projectPath: '/user/repo',
defaultBranch: 'main',
filename: '.gitlab-ci.yml',
@@ -311,4 +314,126 @@ describe('Pipeline Wizard - wrapper.vue', () => {
});
});
});
+
+ describe('when commit step done', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('emits done', () => {
+ expect(wrapper.emitted('done')).toBeUndefined();
+
+ wrapper.findComponent(CommitStep).vm.$emit('done');
+
+ expect(wrapper.emitted('done')).toHaveLength(1);
+ });
+ });
+
+ describe('tracking', () => {
+ let trackingSpy;
+ const trackingCategory = `pipeline_wizard:${templateId}`;
+
+ const setUpTrackingSpy = () => {
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ };
+
+ it('tracks next button click event', () => {
+ createComponent();
+ setUpTrackingSpy();
+ findFirstVisibleStep().vm.$emit('next');
+
+ expect(trackingSpy).toHaveBeenCalledWith(trackingCategory, 'click_button', {
+ category: trackingCategory,
+ property: 'next',
+ label: 'pipeline_wizard_navigation',
+ extra: {
+ fromStep: 0,
+ toStep: 1,
+ },
+ });
+ });
+
+ it('tracks back button click event', () => {
+ createComponent();
+
+ // Navigate to step 1 without the spy set up
+ findFirstVisibleStep().vm.$emit('next');
+
+ // Now enable the tracking spy
+ setUpTrackingSpy();
+
+ findFirstVisibleStep().vm.$emit('back');
+
+ expect(trackingSpy).toHaveBeenCalledWith(trackingCategory, 'click_button', {
+ category: trackingCategory,
+ property: 'back',
+ label: 'pipeline_wizard_navigation',
+ extra: {
+ fromStep: 1,
+ toStep: 0,
+ },
+ });
+ });
+
+ it('tracks back button click event on the commit step', () => {
+ createComponent();
+
+ // Navigate to step 2 without the spy set up
+ findFirstVisibleStep().vm.$emit('next');
+ findFirstVisibleStep().vm.$emit('next');
+
+ // Now enable the tracking spy
+ setUpTrackingSpy();
+
+ wrapper.findComponent(CommitStep).vm.$emit('back');
+
+ expect(trackingSpy).toHaveBeenCalledWith(trackingCategory, 'click_button', {
+ category: trackingCategory,
+ property: 'back',
+ label: 'pipeline_wizard_navigation',
+ extra: {
+ fromStep: 2,
+ toStep: 1,
+ },
+ });
+ });
+
+ it('tracks done event on the commit step', () => {
+ createComponent();
+
+ // Navigate to step 2 without the spy set up
+ findFirstVisibleStep().vm.$emit('next');
+ findFirstVisibleStep().vm.$emit('next');
+
+ // Now enable the tracking spy
+ setUpTrackingSpy();
+
+ wrapper.findComponent(CommitStep).vm.$emit('done');
+
+ expect(trackingSpy).toHaveBeenCalledWith(trackingCategory, 'click_button', {
+ category: trackingCategory,
+ label: 'pipeline_wizard_commit',
+ property: 'commit',
+ });
+ });
+
+ it('tracks when editor emits touch events', () => {
+ createComponent();
+ setUpTrackingSpy();
+
+ wrapper.findComponent(YamlEditor).vm.$emit('touch');
+
+ expect(trackingSpy).toHaveBeenCalledWith(trackingCategory, 'edit', {
+ category: trackingCategory,
+ label: 'pipeline_wizard_editor_interaction',
+ extra: {
+ currentStep: 0,
+ },
+ });
+ });
+ });
});
diff --git a/spec/frontend/pipeline_wizard/mock/yaml.js b/spec/frontend/pipeline_wizard/mock/yaml.js
index e7087b59ce7..12b6f1052b2 100644
--- a/spec/frontend/pipeline_wizard/mock/yaml.js
+++ b/spec/frontend/pipeline_wizard/mock/yaml.js
@@ -71,6 +71,7 @@ bar: barVal
`;
export const fullTemplate = `
+id: test/full-template
title: some title
description: some description
filename: foo.yml
@@ -84,6 +85,7 @@ steps:
`;
export const fullTemplateWithoutFilename = `
+id: test/full-template-no-filename
title: some title
description: some description
steps:
diff --git a/spec/frontend/pipeline_wizard/pipeline_wizard_spec.js b/spec/frontend/pipeline_wizard/pipeline_wizard_spec.js
index 3f689ffdbc8..13234525159 100644
--- a/spec/frontend/pipeline_wizard/pipeline_wizard_spec.js
+++ b/spec/frontend/pipeline_wizard/pipeline_wizard_spec.js
@@ -59,6 +59,7 @@ describe('PipelineWizard', () => {
defaultBranch,
projectPath,
filename: parseDocument(template).get('filename'),
+ templateId: parseDocument(template).get('id'),
}),
);
});
diff --git a/spec/frontend/pipelines/components/dag/dag_annotations_spec.js b/spec/frontend/pipelines/components/dag/dag_annotations_spec.js
index 212f8e19a6d..28a08b6da0f 100644
--- a/spec/frontend/pipelines/components/dag/dag_annotations_spec.js
+++ b/spec/frontend/pipelines/components/dag/dag_annotations_spec.js
@@ -11,7 +11,7 @@ describe('The DAG annotations', () => {
const getAllColorBlocks = () => wrapper.findAll('[data-testid="dag-color-block"]');
const getTextBlock = () => wrapper.find('[data-testid="dag-note-text"]');
const getAllTextBlocks = () => wrapper.findAll('[data-testid="dag-note-text"]');
- const getToggleButton = () => wrapper.find(GlButton);
+ const getToggleButton = () => wrapper.findComponent(GlButton);
const createComponent = (propsData = {}, method = shallowMount) => {
if (wrapper?.destroy) {
diff --git a/spec/frontend/pipelines/components/dag/dag_spec.js b/spec/frontend/pipelines/components/dag/dag_spec.js
index d78df3eb35e..b0c26976c85 100644
--- a/spec/frontend/pipelines/components/dag/dag_spec.js
+++ b/spec/frontend/pipelines/components/dag/dag_spec.js
@@ -18,12 +18,12 @@ import {
describe('Pipeline DAG graph wrapper', () => {
let wrapper;
- const getAlert = () => wrapper.find(GlAlert);
- const getAllAlerts = () => wrapper.findAll(GlAlert);
- const getGraph = () => wrapper.find(DagGraph);
- const getNotes = () => wrapper.find(DagAnnotations);
+ const getAlert = () => wrapper.findComponent(GlAlert);
+ const getAllAlerts = () => wrapper.findAllComponents(GlAlert);
+ const getGraph = () => wrapper.findComponent(DagGraph);
+ const getNotes = () => wrapper.findComponent(DagAnnotations);
const getErrorText = (type) => wrapper.vm.$options.errorTexts[type];
- const getEmptyState = () => wrapper.find(GlEmptyState);
+ const getEmptyState = () => wrapper.findComponent(GlEmptyState);
const createComponent = ({
graphData = mockParsedGraphQLNodes,
diff --git a/spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mini_list_spec.js b/spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mini_list_spec.js
new file mode 100644
index 00000000000..5ea57c51e70
--- /dev/null
+++ b/spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mini_list_spec.js
@@ -0,0 +1,176 @@
+import { mount } from '@vue/test-utils';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
+import LinkedPipelinesMiniList from '~/pipelines/components/pipeline_mini_graph/linked_pipelines_mini_list.vue';
+import mockData from './linked_pipelines_mock_data';
+
+describe('Linked pipeline mini list', () => {
+ let wrapper;
+
+ const findCiIcon = () => wrapper.findComponent(CiIcon);
+ const findCiIcons = () => wrapper.findAllComponents(CiIcon);
+ const findLinkedPipelineCounter = () => wrapper.find('[data-testid="linked-pipeline-counter"]');
+ const findLinkedPipelineMiniItem = () =>
+ wrapper.find('[data-testid="linked-pipeline-mini-item"]');
+ const findLinkedPipelineMiniItems = () =>
+ wrapper.findAll('[data-testid="linked-pipeline-mini-item"]');
+ const findLinkedPipelineMiniList = () => wrapper.findComponent(LinkedPipelinesMiniList);
+
+ const createComponent = (props = {}) => {
+ wrapper = mount(LinkedPipelinesMiniList, {
+ directives: {
+ GlTooltip: createMockDirective(),
+ },
+ propsData: {
+ ...props,
+ },
+ });
+ };
+
+ describe('when passed an upstream pipeline as prop', () => {
+ beforeEach(() => {
+ createComponent({
+ triggeredBy: [mockData.triggered_by],
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('should render one linked pipeline item', () => {
+ expect(findLinkedPipelineMiniItem().exists()).toBe(true);
+ });
+
+ it('should render a linked pipeline with the correct href', () => {
+ expect(findLinkedPipelineMiniItem().exists()).toBe(true);
+
+ expect(findLinkedPipelineMiniItem().attributes('href')).toBe(
+ '/gitlab-org/gitlab-foss/-/pipelines/129',
+ );
+ });
+
+ it('should render one ci status icon', () => {
+ expect(findCiIcon().exists()).toBe(true);
+ });
+
+ it('should render a borderless ci-icon', () => {
+ expect(findCiIcon().exists()).toBe(true);
+
+ expect(findCiIcon().props('isBorderless')).toBe(true);
+ expect(findCiIcon().classes('borderless')).toBe(true);
+ });
+
+ it('should render a ci-icon with a custom border class', () => {
+ expect(findCiIcon().exists()).toBe(true);
+
+ expect(findCiIcon().classes('gl-border')).toBe(true);
+ });
+
+ it('should render the correct ci status icon', () => {
+ expect(findCiIcon().classes('ci-status-icon-running')).toBe(true);
+ });
+
+ it('should have an activated tooltip', () => {
+ expect(findLinkedPipelineMiniItem().exists()).toBe(true);
+ const tooltip = getBinding(findLinkedPipelineMiniItem().element, 'gl-tooltip');
+
+ expect(tooltip.value.title).toBe('GitLabCE - running');
+ });
+
+ it('should correctly set is-upstream', () => {
+ expect(findLinkedPipelineMiniList().exists()).toBe(true);
+
+ expect(findLinkedPipelineMiniList().classes('is-upstream')).toBe(true);
+ });
+
+ it('should correctly compute shouldRenderCounter', () => {
+ expect(findLinkedPipelineMiniList().vm.shouldRenderCounter).toBe(false);
+ });
+
+ it('should not render the pipeline counter', () => {
+ expect(findLinkedPipelineCounter().exists()).toBe(false);
+ });
+ });
+
+ describe('when passed downstream pipelines as props', () => {
+ beforeEach(() => {
+ createComponent({
+ triggered: mockData.triggered,
+ pipelinePath: 'my/pipeline/path',
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('should render three linked pipeline items', () => {
+ expect(findLinkedPipelineMiniItems().exists()).toBe(true);
+ expect(findLinkedPipelineMiniItems().length).toBe(3);
+ });
+
+ it('should render three ci status icons', () => {
+ expect(findCiIcons().exists()).toBe(true);
+ expect(findCiIcons().length).toBe(3);
+ });
+
+ it('should render the correct ci status icon', () => {
+ expect(findCiIcon().classes('ci-status-icon-running')).toBe(true);
+ });
+
+ it('should have an activated tooltip', () => {
+ expect(findLinkedPipelineMiniItem().exists()).toBe(true);
+ const tooltip = getBinding(findLinkedPipelineMiniItem().element, 'gl-tooltip');
+
+ expect(tooltip.value.title).toBe('GitLabCE - running');
+ });
+
+ it('should correctly set is-downstream', () => {
+ expect(findLinkedPipelineMiniList().exists()).toBe(true);
+
+ expect(findLinkedPipelineMiniList().classes('is-downstream')).toBe(true);
+ });
+
+ it('should render a borderless ci-icon', () => {
+ expect(findCiIcon().exists()).toBe(true);
+
+ expect(findCiIcon().props('isBorderless')).toBe(true);
+ expect(findCiIcon().classes('borderless')).toBe(true);
+ });
+
+ it('should render a ci-icon with a custom border class', () => {
+ expect(findCiIcon().exists()).toBe(true);
+
+ expect(findCiIcon().classes('gl-border')).toBe(true);
+ });
+
+ it('should render the pipeline counter', () => {
+ expect(findLinkedPipelineCounter().exists()).toBe(true);
+ });
+
+ it('should correctly compute shouldRenderCounter', () => {
+ expect(findLinkedPipelineMiniList().vm.shouldRenderCounter).toBe(true);
+ });
+
+ it('should correctly trim linkedPipelines', () => {
+ expect(findLinkedPipelineMiniList().props('triggered').length).toBe(6);
+ expect(findLinkedPipelineMiniList().vm.linkedPipelinesTrimmed.length).toBe(3);
+ });
+
+ it('should set the correct pipeline path', () => {
+ expect(findLinkedPipelineCounter().exists()).toBe(true);
+
+ expect(findLinkedPipelineCounter().attributes('href')).toBe('my/pipeline/path');
+ });
+
+ it('should render the correct counterTooltipText', () => {
+ expect(findLinkedPipelineCounter().exists()).toBe(true);
+ const tooltip = getBinding(findLinkedPipelineCounter().element, 'gl-tooltip');
+
+ expect(tooltip.value.title).toBe(findLinkedPipelineMiniList().vm.counterTooltipText);
+ });
+ });
+});
diff --git a/spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mock_data.js b/spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mock_data.js
new file mode 100644
index 00000000000..117c7f2ae52
--- /dev/null
+++ b/spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mock_data.js
@@ -0,0 +1,407 @@
+export default {
+ triggered_by: {
+ id: 129,
+ active: true,
+ path: '/gitlab-org/gitlab-foss/-/pipelines/129',
+ project: {
+ name: 'GitLabCE',
+ },
+ details: {
+ status: {
+ icon: 'status_running',
+ text: 'running',
+ label: 'running',
+ group: 'running',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-foss/-/pipelines/129',
+ favicon:
+ '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
+ },
+ },
+ flags: {
+ latest: false,
+ triggered: false,
+ stuck: false,
+ yaml_errors: false,
+ retryable: true,
+ cancelable: true,
+ },
+ ref: {
+ name: '7-5-stable',
+ path: '/gitlab-org/gitlab-foss/commits/7-5-stable',
+ tag: false,
+ branch: true,
+ },
+ commit: {
+ id: '23433d4d8b20d7e45c103d0b6048faad38a130ab',
+ short_id: '23433d4d',
+ title: 'Version 7.5.0.rc1',
+ created_at: '2014-11-17T15:44:14.000+01:00',
+ parent_ids: ['30ac909f30f58d319b42ed1537664483894b18cd'],
+ message: 'Version 7.5.0.rc1\n',
+ author_name: 'Jacob Vosmaer',
+ author_email: 'contact@jacobvosmaer.nl',
+ authored_date: '2014-11-17T15:44:14.000+01:00',
+ committer_name: 'Jacob Vosmaer',
+ committer_email: 'contact@jacobvosmaer.nl',
+ committed_date: '2014-11-17T15:44:14.000+01:00',
+ author_gravatar_url:
+ 'http://www.gravatar.com/avatar/e66d11c0eedf8c07b3b18fca46599807?s=80&d=identicon',
+ commit_url:
+ 'http://localhost:3000/gitlab-org/gitlab-foss/commit/23433d4d8b20d7e45c103d0b6048faad38a130ab',
+ commit_path: '/gitlab-org/gitlab-foss/commit/23433d4d8b20d7e45c103d0b6048faad38a130ab',
+ },
+ retry_path: '/gitlab-org/gitlab-foss/-/pipelines/129/retry',
+ cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/129/cancel',
+ created_at: '2017-05-24T14:46:20.090Z',
+ updated_at: '2017-05-24T14:46:29.906Z',
+ },
+ triggered: [
+ {
+ id: 132,
+ active: true,
+ path: '/gitlab-org/gitlab-foss/-/pipelines/132',
+ project: {
+ name: 'GitLabCE',
+ },
+ details: {
+ status: {
+ icon: 'status_running',
+ text: 'running',
+ label: 'running',
+ group: 'running',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-foss/-/pipelines/132',
+ favicon:
+ '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
+ },
+ },
+ flags: {
+ latest: false,
+ triggered: false,
+ stuck: false,
+ yaml_errors: false,
+ retryable: true,
+ cancelable: true,
+ },
+ ref: {
+ name: 'crowd',
+ path: '/gitlab-org/gitlab-foss/commits/crowd',
+ tag: false,
+ branch: true,
+ },
+ commit: {
+ id: 'b9d58c4cecd06be74c3cc32ccfb522b31544ab2e',
+ short_id: 'b9d58c4c',
+ title: 'getting user keys publically through http without any authentication, the github…',
+ created_at: '2013-10-03T12:50:33.000+05:30',
+ parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'],
+ message:
+ 'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n\nchangelog updated to include ssh key retrieval feature update\n',
+ author_name: 'devaroop',
+ author_email: 'devaroop123@yahoo.co.in',
+ authored_date: '2013-10-02T20:39:29.000+05:30',
+ committer_name: 'devaroop',
+ committer_email: 'devaroop123@yahoo.co.in',
+ committed_date: '2013-10-03T12:50:33.000+05:30',
+ author_gravatar_url:
+ 'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon',
+ commit_url:
+ 'http://localhost:3000/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e',
+ commit_path: '/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e',
+ },
+ retry_path: '/gitlab-org/gitlab-foss/-/pipelines/132/retry',
+ cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/132/cancel',
+ created_at: '2017-05-24T14:46:24.644Z',
+ updated_at: '2017-05-24T14:48:55.226Z',
+ },
+ {
+ id: 133,
+ active: true,
+ path: '/gitlab-org/gitlab-foss/-/pipelines/133',
+ project: {
+ name: 'GitLabCE',
+ },
+ details: {
+ status: {
+ icon: 'status_running',
+ text: 'running',
+ label: 'running',
+ group: 'running',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-foss/-/pipelines/133',
+ favicon:
+ '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
+ },
+ },
+ flags: {
+ latest: false,
+ triggered: false,
+ stuck: false,
+ yaml_errors: false,
+ retryable: true,
+ cancelable: true,
+ },
+ ref: {
+ name: 'crowd',
+ path: '/gitlab-org/gitlab-foss/commits/crowd',
+ tag: false,
+ branch: true,
+ },
+ commit: {
+ id: 'b6bd4856a33df3d144be66c4ed1f1396009bb08b',
+ short_id: 'b6bd4856',
+ title: 'getting user keys publically through http without any authentication, the github…',
+ created_at: '2013-10-02T20:39:29.000+05:30',
+ parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'],
+ message:
+ 'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n',
+ author_name: 'devaroop',
+ author_email: 'devaroop123@yahoo.co.in',
+ authored_date: '2013-10-02T20:39:29.000+05:30',
+ committer_name: 'devaroop',
+ committer_email: 'devaroop123@yahoo.co.in',
+ committed_date: '2013-10-02T20:39:29.000+05:30',
+ author_gravatar_url:
+ 'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon',
+ commit_url:
+ 'http://localhost:3000/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b',
+ commit_path: '/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b',
+ },
+ retry_path: '/gitlab-org/gitlab-foss/-/pipelines/133/retry',
+ cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/133/cancel',
+ created_at: '2017-05-24T14:46:24.648Z',
+ updated_at: '2017-05-24T14:48:59.673Z',
+ },
+ {
+ id: 130,
+ active: true,
+ path: '/gitlab-org/gitlab-foss/-/pipelines/130',
+ project: {
+ name: 'GitLabCE',
+ },
+ details: {
+ status: {
+ icon: 'status_running',
+ text: 'running',
+ label: 'running',
+ group: 'running',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-foss/-/pipelines/130',
+ favicon:
+ '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
+ },
+ },
+ flags: {
+ latest: false,
+ triggered: false,
+ stuck: false,
+ yaml_errors: false,
+ retryable: true,
+ cancelable: true,
+ },
+ ref: {
+ name: 'crowd',
+ path: '/gitlab-org/gitlab-foss/commits/crowd',
+ tag: false,
+ branch: true,
+ },
+ commit: {
+ id: '6d7ced4a2311eeff037c5575cca1868a6d3f586f',
+ short_id: '6d7ced4a',
+ title: 'Whitespace fixes to patch',
+ created_at: '2013-10-08T13:53:22.000-05:00',
+ parent_ids: ['1875141a963a4238bda29011d8f7105839485253'],
+ message: 'Whitespace fixes to patch\n',
+ author_name: 'Dale Hamel',
+ author_email: 'dale.hamel@srvthe.net',
+ authored_date: '2013-10-08T13:53:22.000-05:00',
+ committer_name: 'Dale Hamel',
+ committer_email: 'dale.hamel@invenia.ca',
+ committed_date: '2013-10-08T13:53:22.000-05:00',
+ author_gravatar_url:
+ 'http://www.gravatar.com/avatar/cd08930e69fa5ad1a669206e7bafe476?s=80&d=identicon',
+ commit_url:
+ 'http://localhost:3000/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f',
+ commit_path: '/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f',
+ },
+ retry_path: '/gitlab-org/gitlab-foss/-/pipelines/130/retry',
+ cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/130/cancel',
+ created_at: '2017-05-24T14:46:24.630Z',
+ updated_at: '2017-05-24T14:49:45.091Z',
+ },
+ {
+ id: 131,
+ active: true,
+ path: '/gitlab-org/gitlab-foss/-/pipelines/132',
+ project: {
+ name: 'GitLabCE',
+ },
+ details: {
+ status: {
+ icon: 'status_running',
+ text: 'running',
+ label: 'running',
+ group: 'running',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-foss/-/pipelines/132',
+ favicon:
+ '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
+ },
+ },
+ flags: {
+ latest: false,
+ triggered: false,
+ stuck: false,
+ yaml_errors: false,
+ retryable: true,
+ cancelable: true,
+ },
+ ref: {
+ name: 'crowd',
+ path: '/gitlab-org/gitlab-foss/commits/crowd',
+ tag: false,
+ branch: true,
+ },
+ commit: {
+ id: 'b9d58c4cecd06be74c3cc32ccfb522b31544ab2e',
+ short_id: 'b9d58c4c',
+ title: 'getting user keys publically through http without any authentication, the github…',
+ created_at: '2013-10-03T12:50:33.000+05:30',
+ parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'],
+ message:
+ 'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n\nchangelog updated to include ssh key retrieval feature update\n',
+ author_name: 'devaroop',
+ author_email: 'devaroop123@yahoo.co.in',
+ authored_date: '2013-10-02T20:39:29.000+05:30',
+ committer_name: 'devaroop',
+ committer_email: 'devaroop123@yahoo.co.in',
+ committed_date: '2013-10-03T12:50:33.000+05:30',
+ author_gravatar_url:
+ 'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon',
+ commit_url:
+ 'http://localhost:3000/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e',
+ commit_path: '/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e',
+ },
+ retry_path: '/gitlab-org/gitlab-foss/-/pipelines/132/retry',
+ cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/132/cancel',
+ created_at: '2017-05-24T14:46:24.644Z',
+ updated_at: '2017-05-24T14:48:55.226Z',
+ },
+ {
+ id: 134,
+ active: true,
+ path: '/gitlab-org/gitlab-foss/-/pipelines/133',
+ project: {
+ name: 'GitLabCE',
+ },
+ details: {
+ status: {
+ icon: 'status_running',
+ text: 'running',
+ label: 'running',
+ group: 'running',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-foss/-/pipelines/133',
+ favicon:
+ '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
+ },
+ },
+ flags: {
+ latest: false,
+ triggered: false,
+ stuck: false,
+ yaml_errors: false,
+ retryable: true,
+ cancelable: true,
+ },
+ ref: {
+ name: 'crowd',
+ path: '/gitlab-org/gitlab-foss/commits/crowd',
+ tag: false,
+ branch: true,
+ },
+ commit: {
+ id: 'b6bd4856a33df3d144be66c4ed1f1396009bb08b',
+ short_id: 'b6bd4856',
+ title: 'getting user keys publically through http without any authentication, the github…',
+ created_at: '2013-10-02T20:39:29.000+05:30',
+ parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'],
+ message:
+ 'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n',
+ author_name: 'devaroop',
+ author_email: 'devaroop123@yahoo.co.in',
+ authored_date: '2013-10-02T20:39:29.000+05:30',
+ committer_name: 'devaroop',
+ committer_email: 'devaroop123@yahoo.co.in',
+ committed_date: '2013-10-02T20:39:29.000+05:30',
+ author_gravatar_url:
+ 'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon',
+ commit_url:
+ 'http://localhost:3000/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b',
+ commit_path: '/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b',
+ },
+ retry_path: '/gitlab-org/gitlab-foss/-/pipelines/133/retry',
+ cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/133/cancel',
+ created_at: '2017-05-24T14:46:24.648Z',
+ updated_at: '2017-05-24T14:48:59.673Z',
+ },
+ {
+ id: 135,
+ active: true,
+ path: '/gitlab-org/gitlab-foss/-/pipelines/130',
+ project: {
+ name: 'GitLabCE',
+ },
+ details: {
+ status: {
+ icon: 'status_running',
+ text: 'running',
+ label: 'running',
+ group: 'running',
+ has_details: true,
+ details_path: '/gitlab-org/gitlab-foss/-/pipelines/130',
+ favicon:
+ '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
+ },
+ },
+ flags: {
+ latest: false,
+ triggered: false,
+ stuck: false,
+ yaml_errors: false,
+ retryable: true,
+ cancelable: true,
+ },
+ ref: {
+ name: 'crowd',
+ path: '/gitlab-org/gitlab-foss/commits/crowd',
+ tag: false,
+ branch: true,
+ },
+ commit: {
+ id: '6d7ced4a2311eeff037c5575cca1868a6d3f586f',
+ short_id: '6d7ced4a',
+ title: 'Whitespace fixes to patch',
+ created_at: '2013-10-08T13:53:22.000-05:00',
+ parent_ids: ['1875141a963a4238bda29011d8f7105839485253'],
+ message: 'Whitespace fixes to patch\n',
+ author_name: 'Dale Hamel',
+ author_email: 'dale.hamel@srvthe.net',
+ authored_date: '2013-10-08T13:53:22.000-05:00',
+ committer_name: 'Dale Hamel',
+ committer_email: 'dale.hamel@invenia.ca',
+ committed_date: '2013-10-08T13:53:22.000-05:00',
+ author_gravatar_url:
+ 'http://www.gravatar.com/avatar/cd08930e69fa5ad1a669206e7bafe476?s=80&d=identicon',
+ commit_url:
+ 'http://localhost:3000/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f',
+ commit_path: '/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f',
+ },
+ retry_path: '/gitlab-org/gitlab-foss/-/pipelines/130/retry',
+ cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/130/cancel',
+ created_at: '2017-05-24T14:46:24.630Z',
+ updated_at: '2017-05-24T14:49:45.091Z',
+ },
+ ],
+};
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
new file mode 100644
index 00000000000..7fa8a18ea1f
--- /dev/null
+++ b/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_mini_graph_spec.js
@@ -0,0 +1,149 @@
+import { mount } from '@vue/test-utils';
+import { pipelines } from 'test_fixtures/pipelines/pipelines.json';
+import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
+import PipelineStages from '~/pipelines/components/pipeline_mini_graph/pipeline_stages.vue';
+import mockLinkedPipelines from './linked_pipelines_mock_data';
+
+const mockStages = pipelines[0].details.stages;
+
+describe('Pipeline Mini Graph', () => {
+ let wrapper;
+
+ const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph);
+ const findPipelineStages = () => wrapper.findComponent(PipelineStages);
+
+ const findLinkedPipelineUpstream = () =>
+ wrapper.findComponent('[data-testid="pipeline-mini-graph-upstream"]');
+ const findLinkedPipelineDownstream = () =>
+ wrapper.findComponent('[data-testid="pipeline-mini-graph-downstream"]');
+ const findDownstreamArrowIcon = () => wrapper.find('[data-testid="downstream-arrow-icon"]');
+ const findUpstreamArrowIcon = () => wrapper.find('[data-testid="upstream-arrow-icon"]');
+
+ const createComponent = (props = {}) => {
+ wrapper = mount(PipelineMiniGraph, {
+ propsData: {
+ stages: mockStages,
+ ...props,
+ },
+ });
+ };
+
+ describe('rendered state without upstream or downstream pipelines', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('should render the pipeline stages', () => {
+ expect(findPipelineStages().exists()).toBe(true);
+ });
+
+ it('should have the correct props', () => {
+ expect(findPipelineMiniGraph().props()).toMatchObject({
+ downstreamPipelines: [],
+ isMergeTrain: false,
+ pipelinePath: '',
+ stages: expect.any(Array),
+ stagesClass: '',
+ updateDropdown: false,
+ upstreamPipeline: undefined,
+ });
+ });
+
+ it('should have no linked pipelines', () => {
+ expect(findLinkedPipelineDownstream().exists()).toBe(false);
+ expect(findLinkedPipelineUpstream().exists()).toBe(false);
+ });
+
+ it('should not render arrow icons', () => {
+ 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', () => {
+ beforeEach(() => {
+ createComponent({
+ upstreamPipeline: mockLinkedPipelines.triggered_by,
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('should have the correct props', () => {
+ expect(findPipelineMiniGraph().props()).toMatchObject({
+ downstreamPipelines: [],
+ isMergeTrain: false,
+ pipelinePath: '',
+ stages: expect.any(Array),
+ stagesClass: '',
+ updateDropdown: false,
+ upstreamPipeline: expect.any(Object),
+ });
+ });
+
+ it('should render the upstream linked pipelines mini list only', () => {
+ expect(findLinkedPipelineUpstream().exists()).toBe(true);
+ expect(findLinkedPipelineDownstream().exists()).toBe(false);
+ });
+
+ it('should render an upstream arrow icon only', () => {
+ expect(findDownstreamArrowIcon().exists()).toBe(false);
+ expect(findUpstreamArrowIcon().exists()).toBe(true);
+ expect(findUpstreamArrowIcon().props('name')).toBe('long-arrow');
+ });
+ });
+
+ describe('rendered state with downstream pipelines', () => {
+ beforeEach(() => {
+ createComponent({
+ downstreamPipelines: mockLinkedPipelines.triggered,
+ pipelinePath: 'my/pipeline/path',
+ });
+ });
+
+ it('should have the correct props', () => {
+ expect(findPipelineMiniGraph().props()).toMatchObject({
+ downstreamPipelines: expect.any(Array),
+ isMergeTrain: false,
+ pipelinePath: 'my/pipeline/path',
+ stages: expect.any(Array),
+ stagesClass: '',
+ updateDropdown: false,
+ upstreamPipeline: undefined,
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('should render the downstream linked pipelines mini list only', () => {
+ expect(findLinkedPipelineDownstream().exists()).toBe(true);
+ expect(findLinkedPipelineUpstream().exists()).toBe(false);
+ });
+
+ it('should render a downstream arrow icon only', () => {
+ expect(findUpstreamArrowIcon().exists()).toBe(false);
+ expect(findDownstreamArrowIcon().exists()).toBe(true);
+ expect(findDownstreamArrowIcon().props('name')).toBe('long-arrow');
+ });
+ });
+});
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
new file mode 100644
index 00000000000..52b440f18bb
--- /dev/null
+++ b/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stage_spec.js
@@ -0,0 +1,260 @@
+import { GlDropdown } from '@gitlab/ui';
+import { nextTick } from 'vue';
+import { mount } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import axios from '~/lib/utils/axios_utils';
+import PipelineStage from '~/pipelines/components/pipeline_mini_graph/pipeline_stage.vue';
+import eventHub from '~/pipelines/event_hub';
+import waitForPromises from 'helpers/wait_for_promises';
+import { stageReply } from '../../mock_data';
+
+const dropdownPath = 'path.json';
+
+describe('Pipelines stage component', () => {
+ let wrapper;
+ let mock;
+ let glTooltipDirectiveMock;
+
+ const createComponent = (props = {}) => {
+ glTooltipDirectiveMock = jest.fn();
+ wrapper = mount(PipelineStage, {
+ attachTo: document.body,
+ directives: {
+ GlTooltip: glTooltipDirectiveMock,
+ },
+ propsData: {
+ stage: {
+ status: {
+ group: 'success',
+ icon: 'status_success',
+ title: 'success',
+ },
+ dropdown_path: dropdownPath,
+ },
+ updateDropdown: false,
+ ...props,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ jest.spyOn(eventHub, '$emit');
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+
+ eventHub.$emit.mockRestore();
+ mock.restore();
+ });
+
+ const findCiActionBtn = () => wrapper.find('.js-ci-action');
+ const findCiIcon = () => wrapper.findComponent(CiIcon);
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
+ const findDropdownToggle = () => wrapper.find('button.dropdown-toggle');
+ const findDropdownMenu = () =>
+ wrapper.find('[data-testid="mini-pipeline-graph-dropdown-menu-list"]');
+ const findDropdownMenuTitle = () =>
+ wrapper.find('[data-testid="pipeline-stage-dropdown-menu-title"]');
+ const findMergeTrainWarning = () => wrapper.find('[data-testid="warning-message-merge-trains"]');
+ const findLoadingState = () => wrapper.find('[data-testid="pipeline-stage-loading-state"]');
+
+ const openStageDropdown = async () => {
+ await findDropdownToggle().trigger('click');
+ await waitForPromises();
+ await nextTick();
+ };
+
+ describe('loading state', () => {
+ beforeEach(async () => {
+ createComponent({ updateDropdown: true });
+
+ mock.onGet(dropdownPath).reply(200, stageReply);
+
+ await openStageDropdown();
+ });
+
+ it('displays loading state while jobs are being fetched', async () => {
+ jest.runOnlyPendingTimers();
+ await nextTick();
+
+ expect(findLoadingState().exists()).toBe(true);
+ expect(findLoadingState().text()).toBe(PipelineStage.i18n.loadingText);
+ });
+
+ it('does not display loading state after jobs have been fetched', async () => {
+ await waitForPromises();
+
+ expect(findLoadingState().exists()).toBe(false);
+ });
+ });
+
+ describe('default appearance', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('sets up the tooltip to not have a show delay animation', () => {
+ expect(glTooltipDirectiveMock.mock.calls[0][1].modifiers.ds0).toBe(true);
+ });
+
+ it('renders a dropdown with the status icon', () => {
+ expect(findDropdown().exists()).toBe(true);
+ expect(findDropdownToggle().exists()).toBe(true);
+ expect(findCiIcon().exists()).toBe(true);
+ });
+
+ it('renders a borderless ci-icon', () => {
+ expect(findCiIcon().exists()).toBe(true);
+ expect(findCiIcon().props('isBorderless')).toBe(true);
+ expect(findCiIcon().classes('borderless')).toBe(true);
+ });
+
+ it('renders a ci-icon with a custom border class', () => {
+ expect(findCiIcon().exists()).toBe(true);
+ expect(findCiIcon().classes('gl-border')).toBe(true);
+ });
+ });
+
+ describe('when user opens dropdown and stage request is successful', () => {
+ beforeEach(async () => {
+ mock.onGet(dropdownPath).reply(200, stageReply);
+ createComponent();
+
+ await openStageDropdown();
+ await jest.runAllTimers();
+ await axios.waitForAll();
+ });
+
+ it('renders the received data and emits the correct events', async () => {
+ expect(findDropdownMenu().text()).toContain(stageReply.latest_statuses[0].name);
+ expect(findDropdownMenuTitle().text()).toContain(stageReply.name);
+ expect(eventHub.$emit).toHaveBeenCalledWith('clickedDropdown');
+ expect(wrapper.emitted('miniGraphStageClick')).toEqual([[]]);
+ });
+
+ it('refreshes when updateDropdown is set to true', async () => {
+ expect(mock.history.get).toHaveLength(1);
+
+ wrapper.setProps({ updateDropdown: true });
+ await axios.waitForAll();
+
+ expect(mock.history.get).toHaveLength(2);
+ });
+ });
+
+ describe('when user opens dropdown and stage request fails', () => {
+ it('should close the dropdown', async () => {
+ mock.onGet(dropdownPath).reply(500);
+ createComponent();
+
+ await openStageDropdown();
+ await axios.waitForAll();
+ await waitForPromises();
+
+ expect(findDropdown().classes('show')).toBe(false);
+ });
+ });
+
+ describe('update endpoint correctly', () => {
+ beforeEach(async () => {
+ const copyStage = { ...stageReply };
+ copyStage.latest_statuses[0].name = 'this is the updated content';
+ mock.onGet('bar.json').reply(200, copyStage);
+ createComponent({
+ stage: {
+ status: {
+ group: 'running',
+ icon: 'status_running',
+ title: 'running',
+ },
+ dropdown_path: 'bar.json',
+ },
+ });
+ await axios.waitForAll();
+ });
+
+ it('should update the stage to request the new endpoint provided', async () => {
+ await openStageDropdown();
+ jest.runOnlyPendingTimers();
+ await waitForPromises();
+
+ expect(findDropdownMenu().text()).toContain('this is the updated content');
+ });
+ });
+
+ describe('pipelineActionRequestComplete', () => {
+ beforeEach(async () => {
+ mock.onGet(dropdownPath).reply(200, stageReply);
+ mock.onPost(`${stageReply.latest_statuses[0].status.action.path}.json`).reply(200);
+
+ createComponent();
+ await waitForPromises();
+ await nextTick();
+ });
+
+ const clickCiAction = async () => {
+ await openStageDropdown();
+ jest.runOnlyPendingTimers();
+ await waitForPromises();
+
+ 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 () => {
+ await clickCiAction();
+ await waitForPromises();
+
+ expect(wrapper.emitted('pipelineActionRequestComplete')).toHaveLength(1);
+ });
+ });
+
+ describe('With merge trains enabled', () => {
+ it('shows a warning on the dropdown', async () => {
+ mock.onGet(dropdownPath).reply(200, stageReply);
+ createComponent({
+ isMergeTrain: true,
+ });
+
+ await openStageDropdown();
+ jest.runOnlyPendingTimers();
+ await waitForPromises();
+
+ const warning = findMergeTrainWarning();
+
+ expect(warning.text()).toBe('Merge train pipeline jobs can not be retried');
+ });
+ });
+
+ describe('With merge trains disabled', () => {
+ beforeEach(async () => {
+ mock.onGet(dropdownPath).reply(200, stageReply);
+ createComponent();
+
+ await openStageDropdown();
+ await axios.waitForAll();
+ });
+
+ it('does not show a warning on the dropdown', () => {
+ const warning = findMergeTrainWarning();
+
+ expect(warning.exists()).toBe(false);
+ });
+ });
+});
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
new file mode 100644
index 00000000000..bfb780d5d39
--- /dev/null
+++ b/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stages_spec.js
@@ -0,0 +1,83 @@
+import { shallowMount } from '@vue/test-utils';
+import { pipelines } from 'test_fixtures/pipelines/pipelines.json';
+import PipelineStage from '~/pipelines/components/pipeline_mini_graph/pipeline_stage.vue';
+import PipelineStages from '~/pipelines/components/pipeline_mini_graph/pipeline_stages.vue';
+
+const mockStages = pipelines[0].details.stages;
+
+describe('Pipeline Stages', () => {
+ let wrapper;
+
+ const findPipelineStages = () => wrapper.findAllComponents(PipelineStage);
+ const findPipelineStagesAt = (i) => findPipelineStages().at(i);
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(PipelineStages, {
+ propsData: {
+ stages: mockStages,
+ ...props,
+ },
+ });
+ };
+
+ it('renders stages', () => {
+ createComponent();
+
+ 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: [] });
+
+ expect(wrapper.exists()).toBe(true);
+ 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();
+
+ expect(findPipelineStagesAt(0).props('updateDropdown')).toBe(false);
+ expect(findPipelineStagesAt(1).props('updateDropdown')).toBe(false);
+ });
+
+ it('update dropdown is set to true', () => {
+ createComponent({ updateDropdown: true });
+
+ expect(findPipelineStagesAt(0).props('updateDropdown')).toBe(true);
+ expect(findPipelineStagesAt(1).props('updateDropdown')).toBe(true);
+ });
+
+ it('is merge train is false by default', () => {
+ createComponent();
+
+ expect(findPipelineStagesAt(0).props('isMergeTrain')).toBe(false);
+ expect(findPipelineStagesAt(1).props('isMergeTrain')).toBe(false);
+ });
+
+ it('is merge train is set to true', () => {
+ createComponent({ isMergeTrain: true });
+
+ expect(findPipelineStagesAt(0).props('isMergeTrain')).toBe(true);
+ expect(findPipelineStagesAt(1).props('isMergeTrain')).toBe(true);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+});
diff --git a/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js b/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js
index f958f12acd4..ee3eaaf5ef3 100644
--- a/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js
+++ b/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js
@@ -2,17 +2,19 @@ import { GlFilteredSearch } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue';
+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 { TRACKING_CATEGORIES } from '~/pipelines/constants';
import { users, mockSearch, branches, tags } from '../mock_data';
describe('Pipelines filtered search', () => {
let wrapper;
let mock;
- const findFilteredSearch = () => wrapper.find(GlFilteredSearch);
+ const findFilteredSearch = () => wrapper.findComponent(GlFilteredSearch);
const getSearchToken = (type) =>
findFilteredSearch()
.props('availableTokens')
@@ -177,4 +179,20 @@ describe('Pipelines filtered search', () => {
expect(findFilteredSearch().props('value')).toHaveLength(expectedValueProp.length);
});
});
+
+ describe('tracking', () => {
+ afterEach(() => {
+ unmockTracking();
+ });
+
+ it('tracks filtered search click', () => {
+ const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+
+ findFilteredSearch().vm.$emit('submit', mockSearch);
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_filtered_search', {
+ label: TRACKING_CATEGORIES.search,
+ });
+ });
+ });
});
diff --git a/spec/frontend/pipelines/components/pipelines_list/pipeline_mini_graph_spec.js b/spec/frontend/pipelines/components/pipelines_list/pipeline_mini_graph_spec.js
deleted file mode 100644
index 1cb43c199aa..00000000000
--- a/spec/frontend/pipelines/components/pipelines_list/pipeline_mini_graph_spec.js
+++ /dev/null
@@ -1,83 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { pipelines } from 'test_fixtures/pipelines/pipelines.json';
-import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue';
-import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue';
-
-const mockStages = pipelines[0].details.stages;
-
-describe('Pipeline Mini Graph', () => {
- let wrapper;
-
- const findPipelineStages = () => wrapper.findAll(PipelineStage);
- const findPipelineStagesAt = (i) => findPipelineStages().at(i);
-
- const createComponent = (props = {}) => {
- wrapper = shallowMount(PipelineMiniGraph, {
- propsData: {
- stages: mockStages,
- ...props,
- },
- });
- };
-
- it('renders stages', () => {
- createComponent();
-
- 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: [] });
-
- expect(wrapper.exists()).toBe(true);
- 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();
-
- expect(findPipelineStagesAt(0).props('updateDropdown')).toBe(false);
- expect(findPipelineStagesAt(1).props('updateDropdown')).toBe(false);
- });
-
- it('update dropdown is set to true', () => {
- createComponent({ updateDropdown: true });
-
- expect(findPipelineStagesAt(0).props('updateDropdown')).toBe(true);
- expect(findPipelineStagesAt(1).props('updateDropdown')).toBe(true);
- });
-
- it('is merge train is false by default', () => {
- createComponent();
-
- expect(findPipelineStagesAt(0).props('isMergeTrain')).toBe(false);
- expect(findPipelineStagesAt(1).props('isMergeTrain')).toBe(false);
- });
-
- it('is merge train is set to true', () => {
- createComponent({ isMergeTrain: true });
-
- expect(findPipelineStagesAt(0).props('isMergeTrain')).toBe(true);
- expect(findPipelineStagesAt(1).props('isMergeTrain')).toBe(true);
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
- });
-});
diff --git a/spec/frontend/pipelines/components/pipelines_list/pipeline_stage_spec.js b/spec/frontend/pipelines/components/pipelines_list/pipeline_stage_spec.js
deleted file mode 100644
index e712cdeaea2..00000000000
--- a/spec/frontend/pipelines/components/pipelines_list/pipeline_stage_spec.js
+++ /dev/null
@@ -1,259 +0,0 @@
-import { GlDropdown } from '@gitlab/ui';
-import { nextTick } from 'vue';
-import { mount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-import CiIcon from '~/vue_shared/components/ci_icon.vue';
-import axios from '~/lib/utils/axios_utils';
-import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue';
-import eventHub from '~/pipelines/event_hub';
-import waitForPromises from 'helpers/wait_for_promises';
-import { stageReply } from '../../mock_data';
-
-const dropdownPath = 'path.json';
-
-describe('Pipelines stage component', () => {
- let wrapper;
- let mock;
- let glTooltipDirectiveMock;
-
- const createComponent = (props = {}) => {
- glTooltipDirectiveMock = jest.fn();
- wrapper = mount(PipelineStage, {
- attachTo: document.body,
- directives: {
- GlTooltip: glTooltipDirectiveMock,
- },
- propsData: {
- stage: {
- status: {
- group: 'success',
- icon: 'status_success',
- title: 'success',
- },
- dropdown_path: dropdownPath,
- },
- updateDropdown: false,
- ...props,
- },
- });
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- jest.spyOn(eventHub, '$emit');
- });
-
- afterEach(() => {
- wrapper.destroy();
- wrapper = null;
-
- eventHub.$emit.mockRestore();
- mock.restore();
- });
-
- const findCiActionBtn = () => wrapper.find('.js-ci-action');
- const findCiIcon = () => wrapper.findComponent(CiIcon);
- const findDropdown = () => wrapper.findComponent(GlDropdown);
- const findDropdownToggle = () => wrapper.find('button.dropdown-toggle');
- const findDropdownMenu = () =>
- wrapper.find('[data-testid="mini-pipeline-graph-dropdown-menu-list"]');
- const findDropdownMenuTitle = () =>
- wrapper.find('[data-testid="pipeline-stage-dropdown-menu-title"]');
- const findMergeTrainWarning = () => wrapper.find('[data-testid="warning-message-merge-trains"]');
- const findLoadingState = () => wrapper.find('[data-testid="pipeline-stage-loading-state"]');
-
- const openStageDropdown = async () => {
- await findDropdownToggle().trigger('click');
- await waitForPromises();
- await nextTick();
- };
-
- describe('loading state', () => {
- beforeEach(async () => {
- createComponent({ updateDropdown: true });
-
- mock.onGet(dropdownPath).reply(200, stageReply);
-
- await openStageDropdown();
- });
-
- it('displays loading state while jobs are being fetched', async () => {
- jest.runOnlyPendingTimers();
- await nextTick();
-
- expect(findLoadingState().exists()).toBe(true);
- expect(findLoadingState().text()).toBe(PipelineStage.i18n.loadingText);
- });
-
- it('does not display loading state after jobs have been fetched', async () => {
- await waitForPromises();
-
- expect(findLoadingState().exists()).toBe(false);
- });
- });
-
- describe('default appearance', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('sets up the tooltip to not have a show delay animation', () => {
- expect(glTooltipDirectiveMock.mock.calls[0][1].modifiers.ds0).toBe(true);
- });
-
- it('renders a dropdown with the status icon', () => {
- expect(findDropdown().exists()).toBe(true);
- expect(findDropdownToggle().exists()).toBe(true);
- expect(findCiIcon().exists()).toBe(true);
- });
-
- it('renders a borderless ci-icon', () => {
- expect(findCiIcon().exists()).toBe(true);
- expect(findCiIcon().props('isBorderless')).toBe(true);
- expect(findCiIcon().classes('borderless')).toBe(true);
- });
-
- it('renders a ci-icon with a custom border class', () => {
- expect(findCiIcon().exists()).toBe(true);
- expect(findCiIcon().classes('gl-border')).toBe(true);
- });
- });
-
- describe('when user opens dropdown and stage request is successful', () => {
- beforeEach(async () => {
- mock.onGet(dropdownPath).reply(200, stageReply);
- createComponent();
-
- await openStageDropdown();
- await jest.runAllTimers();
- await axios.waitForAll();
- });
-
- it('renders the received data and emit `clickedDropdown` event', async () => {
- expect(findDropdownMenu().text()).toContain(stageReply.latest_statuses[0].name);
- expect(findDropdownMenuTitle().text()).toContain(stageReply.name);
- expect(eventHub.$emit).toHaveBeenCalledWith('clickedDropdown');
- });
-
- it('refreshes when updateDropdown is set to true', async () => {
- expect(mock.history.get).toHaveLength(1);
-
- wrapper.setProps({ updateDropdown: true });
- await axios.waitForAll();
-
- expect(mock.history.get).toHaveLength(2);
- });
- });
-
- describe('when user opens dropdown and stage request fails', () => {
- it('should close the dropdown', async () => {
- mock.onGet(dropdownPath).reply(500);
- createComponent();
-
- await openStageDropdown();
- await axios.waitForAll();
- await waitForPromises();
-
- expect(findDropdown().classes('show')).toBe(false);
- });
- });
-
- describe('update endpoint correctly', () => {
- beforeEach(async () => {
- const copyStage = { ...stageReply };
- copyStage.latest_statuses[0].name = 'this is the updated content';
- mock.onGet('bar.json').reply(200, copyStage);
- createComponent({
- stage: {
- status: {
- group: 'running',
- icon: 'status_running',
- title: 'running',
- },
- dropdown_path: 'bar.json',
- },
- });
- await axios.waitForAll();
- });
-
- it('should update the stage to request the new endpoint provided', async () => {
- await openStageDropdown();
- jest.runOnlyPendingTimers();
- await waitForPromises();
-
- expect(findDropdownMenu().text()).toContain('this is the updated content');
- });
- });
-
- describe('pipelineActionRequestComplete', () => {
- beforeEach(async () => {
- mock.onGet(dropdownPath).reply(200, stageReply);
- mock.onPost(`${stageReply.latest_statuses[0].status.action.path}.json`).reply(200);
-
- createComponent();
- await waitForPromises();
- await nextTick();
- });
-
- const clickCiAction = async () => {
- await openStageDropdown();
- jest.runOnlyPendingTimers();
- await waitForPromises();
-
- 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 () => {
- await clickCiAction();
- await waitForPromises();
-
- expect(wrapper.emitted('pipelineActionRequestComplete')).toHaveLength(1);
- });
- });
-
- describe('With merge trains enabled', () => {
- it('shows a warning on the dropdown', async () => {
- mock.onGet(dropdownPath).reply(200, stageReply);
- createComponent({
- isMergeTrain: true,
- });
-
- await openStageDropdown();
- jest.runOnlyPendingTimers();
- await waitForPromises();
-
- const warning = findMergeTrainWarning();
-
- expect(warning.text()).toBe('Merge train pipeline jobs can not be retried');
- });
- });
-
- describe('With merge trains disabled', () => {
- beforeEach(async () => {
- mock.onGet(dropdownPath).reply(200, stageReply);
- createComponent();
-
- await openStageDropdown();
- await axios.waitForAll();
- });
-
- it('does not show a warning on the dropdown', () => {
- const warning = findMergeTrainWarning();
-
- expect(warning.exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/pipelines/graph/action_component_spec.js b/spec/frontend/pipelines/graph/action_component_spec.js
index 6e5aa572ec0..a823e029281 100644
--- a/spec/frontend/pipelines/graph/action_component_spec.js
+++ b/spec/frontend/pipelines/graph/action_component_spec.js
@@ -9,7 +9,7 @@ import ActionComponent from '~/pipelines/components/jobs_shared/action_component
describe('pipeline graph action component', () => {
let wrapper;
let mock;
- const findButton = () => wrapper.find(GlButton);
+ const findButton = () => wrapper.findComponent(GlButton);
const findTooltipWrapper = () => wrapper.find('[data-testid="ci-action-icon-tooltip-wrapper"]');
beforeEach(() => {
diff --git a/spec/frontend/pipelines/graph/graph_component_spec.js b/spec/frontend/pipelines/graph/graph_component_spec.js
index 4b2b61c8edd..2abb5f7dc58 100644
--- a/spec/frontend/pipelines/graph/graph_component_spec.js
+++ b/spec/frontend/pipelines/graph/graph_component_spec.js
@@ -15,9 +15,9 @@ import {
describe('graph component', () => {
let wrapper;
- const findLinkedColumns = () => wrapper.findAll(LinkedPipelinesColumn);
- const findLinksLayer = () => wrapper.find(LinksLayer);
- const findStageColumns = () => wrapper.findAll(StageColumnComponent);
+ const findLinkedColumns = () => wrapper.findAllComponents(LinkedPipelinesColumn);
+ const findLinksLayer = () => wrapper.findComponent(LinksLayer);
+ const findStageColumns = () => wrapper.findAllComponents(StageColumnComponent);
const findStageNameInJob = () => wrapper.find('[data-testid="stage-name-in-job"]');
const defaultProps = {
@@ -107,7 +107,7 @@ describe('graph component', () => {
});
it('dims unrelated jobs', () => {
- const unrelatedJob = wrapper.find(JobItem);
+ const unrelatedJob = wrapper.findComponent(JobItem);
expect(findLinksLayer().emitted().highlightedJobsChange).toHaveLength(1);
expect(unrelatedJob.classes('gl-opacity-3')).toBe(true);
});
diff --git a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
index 3eaf06e0656..587a3c67168 100644
--- a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
+++ b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
@@ -30,16 +30,10 @@ import * as Api from '~/pipelines/components/graph_shared/api';
import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue';
import * as parsingUtils from '~/pipelines/components/parsing_utils';
import getPipelineHeaderData from '~/pipelines/graphql/queries/get_pipeline_header_data.query.graphql';
-import getPerformanceInsights from '~/pipelines/graphql/queries/get_performance_insights.query.graphql';
import * as sentryUtils from '~/pipelines/utils';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { mockRunningPipelineHeaderData } from '../mock_data';
-import {
- mapCallouts,
- mockCalloutsResponse,
- mockPipelineResponse,
- mockPerformanceInsightsResponse,
-} from './mock_data';
+import { mapCallouts, mockCalloutsResponse, mockPipelineResponse } from './mock_data';
const defaultProvide = {
graphqlResourceEtag: 'frog/amphibirama/etag/',
@@ -57,11 +51,11 @@ describe('Pipeline graph wrapper', () => {
const getDependenciesToggle = () => wrapper.find('[data-testid="show-links-toggle"]');
const getLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const getLinksLayer = () => wrapper.findComponent(LinksLayer);
- const getGraph = () => wrapper.find(PipelineGraph);
+ const getGraph = () => wrapper.findComponent(PipelineGraph);
const getStageColumnTitle = () => wrapper.find('[data-testid="stage-column-title"]');
const getAllStageColumnGroupsInColumn = () =>
- wrapper.find(StageColumnComponent).findAll('[data-testid="stage-column-group"]');
- const getViewSelector = () => wrapper.find(GraphViewSelector);
+ wrapper.findComponent(StageColumnComponent).findAll('[data-testid="stage-column-group"]');
+ const getViewSelector = () => wrapper.findComponent(GraphViewSelector);
const getViewSelectorTrip = () => getViewSelector().findComponent(GlAlert);
const getLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
@@ -95,15 +89,11 @@ describe('Pipeline graph wrapper', () => {
const callouts = mapCallouts(calloutsList);
const getUserCalloutsHandler = jest.fn().mockResolvedValue(mockCalloutsResponse(callouts));
const getPipelineHeaderDataHandler = jest.fn().mockResolvedValue(mockRunningPipelineHeaderData);
- const getPerformanceInsightsHandler = jest
- .fn()
- .mockResolvedValue(mockPerformanceInsightsResponse);
const requestHandlers = [
[getPipelineHeaderData, getPipelineHeaderDataHandler],
[getPipelineDetails, getPipelineDetailsHandler],
[getUserCallouts, getUserCalloutsHandler],
- [getPerformanceInsights, getPerformanceInsightsHandler],
];
const apolloProvider = createMockApollo(requestHandlers);
@@ -309,7 +299,7 @@ describe('Pipeline graph wrapper', () => {
const groupsInFirstColumn =
mockPipelineResponse.data.project.pipeline.stages.nodes[0].groups.nodes.length;
expect(getAllStageColumnGroupsInColumn()).toHaveLength(groupsInFirstColumn);
- expect(getStageColumnTitle().text()).toBe('Build');
+ expect(getStageColumnTitle().text()).toBe('build');
await getViewSelector().vm.$emit('updateViewType', LAYER_VIEW);
expect(getAllStageColumnGroupsInColumn()).toHaveLength(groupsInFirstColumn + 1);
expect(getStageColumnTitle().text()).toBe('');
@@ -418,7 +408,7 @@ describe('Pipeline graph wrapper', () => {
it('reads the view type from localStorage when available', () => {
const viewSelectorNeedsSegment = wrapper
- .find(GlButtonGroup)
+ .findComponent(GlButtonGroup)
.findAllComponents(GlButton)
.at(1);
expect(viewSelectorNeedsSegment.classes()).toContain('selected');
@@ -564,7 +554,7 @@ describe('Pipeline graph wrapper', () => {
mock.restore();
});
- it('it calls reportPerformance with expected arguments', () => {
+ it('calls reportPerformance with expected arguments', () => {
expect(markAndMeasure).toHaveBeenCalled();
expect(reportPerformance).toHaveBeenCalled();
expect(reportPerformance).toHaveBeenCalledWith(metricsPath, metricsData);
diff --git a/spec/frontend/pipelines/graph/graph_view_selector_spec.js b/spec/frontend/pipelines/graph/graph_view_selector_spec.js
index 1397500bdc7..43587bebedf 100644
--- a/spec/frontend/pipelines/graph/graph_view_selector_spec.js
+++ b/spec/frontend/pipelines/graph/graph_view_selector_spec.js
@@ -1,34 +1,23 @@
import { GlAlert, GlButton, GlButtonGroup, GlLoadingIcon } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { LAYER_VIEW, STAGE_VIEW } from '~/pipelines/components/graph/constants';
import GraphViewSelector from '~/pipelines/components/graph/graph_view_selector.vue';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import getPerformanceInsights from '~/pipelines/graphql/queries/get_performance_insights.query.graphql';
-import { mockPerformanceInsightsResponse } from './mock_data';
-
-Vue.use(VueApollo);
describe('the graph view selector component', () => {
let wrapper;
- let trackingSpy;
const findDependenciesToggle = () => wrapper.find('[data-testid="show-links-toggle"]');
const findViewTypeSelector = () => wrapper.findComponent(GlButtonGroup);
const findStageViewButton = () => findViewTypeSelector().findAllComponents(GlButton).at(0);
const findLayerViewButton = () => findViewTypeSelector().findAllComponents(GlButton).at(1);
const findSwitcherLoader = () => wrapper.find('[data-testid="switcher-loading-state"]');
- const findToggleLoader = () => findDependenciesToggle().find(GlLoadingIcon);
+ const findToggleLoader = () => findDependenciesToggle().findComponent(GlLoadingIcon);
const findHoverTip = () => wrapper.findComponent(GlAlert);
- const findPipelineInsightsBtn = () => wrapper.find('[data-testid="pipeline-insights-btn"]');
const defaultProps = {
showLinks: false,
tipPreviouslyDismissed: false,
type: STAGE_VIEW,
- isPipelineComplete: true,
};
const defaultData = {
@@ -38,14 +27,6 @@ describe('the graph view selector component', () => {
showLinksActive: false,
};
- const getPerformanceInsightsHandler = jest
- .fn()
- .mockResolvedValue(mockPerformanceInsightsResponse);
-
- const requestHandlers = [[getPerformanceInsights, getPerformanceInsightsHandler]];
-
- const apolloProvider = createMockApollo(requestHandlers);
-
const createComponent = ({ data = {}, mountFn = shallowMount, props = {} } = {}) => {
wrapper = mountFn(GraphViewSelector, {
propsData: {
@@ -58,7 +39,6 @@ describe('the graph view selector component', () => {
...data,
};
},
- apolloProvider,
});
};
@@ -222,44 +202,5 @@ describe('the graph view selector component', () => {
expect(findHoverTip().exists()).toBe(false);
});
});
-
- describe('pipeline insights', () => {
- it.each`
- isPipelineComplete | shouldShow
- ${true} | ${true}
- ${false} | ${false}
- `(
- 'button should display $shouldShow if isPipelineComplete is $isPipelineComplete ',
- ({ isPipelineComplete, shouldShow }) => {
- createComponent({
- props: {
- isPipelineComplete,
- },
- });
-
- expect(findPipelineInsightsBtn().exists()).toBe(shouldShow);
- },
- );
- });
-
- describe('tracking', () => {
- beforeEach(() => {
- createComponent();
-
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- });
-
- afterEach(() => {
- unmockTracking();
- });
-
- it('tracks performance insights button click', () => {
- findPipelineInsightsBtn().vm.$emit('click');
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_insights_button', {
- label: 'performance_insights',
- });
- });
- });
});
});
diff --git a/spec/frontend/pipelines/graph/job_item_spec.js b/spec/frontend/pipelines/graph/job_item_spec.js
index 4f0da09fec6..05776ec0706 100644
--- a/spec/frontend/pipelines/graph/job_item_spec.js
+++ b/spec/frontend/pipelines/graph/job_item_spec.js
@@ -59,7 +59,7 @@ describe('pipeline graph job item', () => {
});
});
- it('it should render status and name', () => {
+ it('should render status and name', () => {
expect(wrapper.find('.ci-status-icon-success').exists()).toBe(true);
expect(wrapper.find('a').exists()).toBe(false);
@@ -72,7 +72,7 @@ describe('pipeline graph job item', () => {
});
describe('action icon', () => {
- it('it should render the action icon', () => {
+ it('should render the action icon', () => {
createWrapper({ job: mockJob });
const actionComponent = findActionComponent();
@@ -82,7 +82,7 @@ describe('pipeline graph job item', () => {
expect(actionComponent.attributes('disabled')).not.toBe('disabled');
});
- it('it should render disabled action icon when user cannot run the action', () => {
+ it('should render disabled action icon when user cannot run the action', () => {
createWrapper({ job: mockJobWithUnauthorizedAction });
const actionComponent = findActionComponent();
diff --git a/spec/frontend/pipelines/graph/job_name_component_spec.js b/spec/frontend/pipelines/graph/job_name_component_spec.js
index d3008c046e8..ec432e98fdf 100644
--- a/spec/frontend/pipelines/graph/job_name_component_spec.js
+++ b/spec/frontend/pipelines/graph/job_name_component_spec.js
@@ -24,7 +24,7 @@ describe('job name component', () => {
});
it('should render an icon with the provided status', () => {
- expect(wrapper.find(ciIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(ciIcon).exists()).toBe(true);
expect(wrapper.find('.ci-status-icon-success').exists()).toBe(true);
});
});
diff --git a/spec/frontend/pipelines/graph/linked_pipeline_spec.js b/spec/frontend/pipelines/graph/linked_pipeline_spec.js
index 7d1e4774a24..399d52c3dff 100644
--- a/spec/frontend/pipelines/graph/linked_pipeline_spec.js
+++ b/spec/frontend/pipelines/graph/linked_pipeline_spec.js
@@ -36,13 +36,13 @@ describe('Linked pipeline', () => {
type: UPSTREAM,
};
- const findButton = () => wrapper.find(GlButton);
+ const findButton = () => wrapper.findComponent(GlButton);
const findCancelButton = () => wrapper.findByLabelText('Cancel downstream pipeline');
const findCardTooltip = () => wrapper.findComponent(GlTooltip);
const findDownstreamPipelineTitle = () => wrapper.findByTestId('downstream-title');
const findExpandButton = () => wrapper.findByTestId('expand-pipeline-button');
- const findLinkedPipeline = () => wrapper.find({ ref: 'linkedPipeline' });
- const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const findLinkedPipeline = () => wrapper.findComponent({ ref: 'linkedPipeline' });
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findPipelineLabel = () => wrapper.findByTestId('downstream-pipeline-label');
const findPipelineLink = () => wrapper.findByTestId('pipelineLink');
const findRetryButton = () => wrapper.findByLabelText('Retry downstream pipeline');
@@ -80,7 +80,7 @@ describe('Linked pipeline', () => {
});
it('should render an svg within the status container', () => {
- const pipelineStatusElement = wrapper.find(CiStatus);
+ const pipelineStatusElement = wrapper.findComponent(CiStatus);
expect(pipelineStatusElement.find('svg').exists()).toBe(true);
});
@@ -90,7 +90,7 @@ describe('Linked pipeline', () => {
});
it('should have a ci-status child component', () => {
- expect(wrapper.find(CiStatus).exists()).toBe(true);
+ expect(wrapper.findComponent(CiStatus).exists()).toBe(true);
});
it('should render the pipeline id', () => {
@@ -214,7 +214,7 @@ describe('Linked pipeline', () => {
await findRetryButton().trigger('click');
});
- it('calls the retry mutation ', () => {
+ it('calls the retry mutation', () => {
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: RetryPipelineMutation,
@@ -255,7 +255,7 @@ describe('Linked pipeline', () => {
createWrapper({ propsData: cancelablePipeline });
});
- it('shows only the cancel button ', () => {
+ it('shows only the cancel button', () => {
expect(findCancelButton().exists()).toBe(true);
expect(findRetryButton().exists()).toBe(false);
});
@@ -375,7 +375,7 @@ describe('Linked pipeline', () => {
${'mouseover'} | ${'mouseout'}
${'focus'} | ${'blur'}
`(
- 'applies the class on $activateEventName and removes it on $deactivateEventName ',
+ 'applies the class on $activateEventName and removes it on $deactivateEventName',
async ({ activateEventName, deactivateEventName }) => {
const shadowClass = 'gl-shadow-none!';
diff --git a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
index 46000711110..63e2d8707ea 100644
--- a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
+++ b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
@@ -38,8 +38,8 @@ describe('Linked Pipelines Column', () => {
let wrapper;
const findLinkedColumnTitle = () => wrapper.find('[data-testid="linked-column-title"]');
- const findLinkedPipelineElements = () => wrapper.findAll(LinkedPipeline);
- const findPipelineGraph = () => wrapper.find(PipelineGraph);
+ const findLinkedPipelineElements = () => wrapper.findAllComponents(LinkedPipeline);
+ const findPipelineGraph = () => wrapper.findComponent(PipelineGraph);
const findExpandButton = () => wrapper.find('[data-testid="expand-pipeline-button"]');
Vue.use(VueApollo);
diff --git a/spec/frontend/pipelines/graph/mock_data.js b/spec/frontend/pipelines/graph/mock_data.js
index 959bbcefc98..6124d67af09 100644
--- a/spec/frontend/pipelines/graph/mock_data.js
+++ b/spec/frontend/pipelines/graph/mock_data.js
@@ -1038,245 +1038,3 @@ export const triggerJob = {
action: null,
},
};
-
-export const mockPerformanceInsightsResponse = {
- data: {
- project: {
- __typename: 'Project',
- id: 'gid://gitlab/Project/20',
- pipeline: {
- __typename: 'Pipeline',
- id: 'gid://gitlab/Ci::Pipeline/97',
- jobs: {
- __typename: 'CiJobConnection',
- pageInfo: {
- __typename: 'PageInfo',
- hasNextPage: false,
- },
- nodes: [
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Bridge/2502',
- duration: null,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-2502-2502',
- detailsPath: '/root/lots-of-jobs-project/-/pipelines/98',
- },
- name: 'trigger_job',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/303',
- name: 'deploy',
- },
- startedAt: null,
- queuedDuration: 424850.376278,
- },
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Build/2501',
- duration: 10,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-2501-2501',
- detailsPath: '/root/ci-project/-/jobs/2501',
- },
- name: 'artifact_job',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/303',
- name: 'deploy',
- },
- startedAt: '2022-07-01T16:31:41Z',
- queuedDuration: 2.621553,
- },
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Build/2500',
- duration: 4,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-2500-2500',
- detailsPath: '/root/ci-project/-/jobs/2500',
- },
- name: 'coverage_job',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/302',
- name: 'test',
- },
- startedAt: '2022-07-01T16:31:33Z',
- queuedDuration: 14.388869,
- },
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Build/2499',
- duration: 4,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-2499-2499',
- detailsPath: '/root/ci-project/-/jobs/2499',
- },
- name: 'test_job_two',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/302',
- name: 'test',
- },
- startedAt: '2022-07-01T16:31:28Z',
- queuedDuration: 15.792664,
- },
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Build/2498',
- duration: 4,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-2498-2498',
- detailsPath: '/root/ci-project/-/jobs/2498',
- },
- name: 'test_job_one',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/302',
- name: 'test',
- },
- startedAt: '2022-07-01T16:31:17Z',
- queuedDuration: 8.317072,
- },
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Build/2497',
- duration: 5,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'failed-2497-2497',
- detailsPath: '/root/ci-project/-/jobs/2497',
- },
- name: 'allow_failure_test_job',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/302',
- name: 'test',
- },
- startedAt: '2022-07-01T16:31:22Z',
- queuedDuration: 3.547553,
- },
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Build/2496',
- duration: null,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'manual-2496-2496',
- detailsPath: '/root/ci-project/-/jobs/2496',
- },
- name: 'test_manual_job',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/302',
- name: 'test',
- },
- startedAt: null,
- queuedDuration: null,
- },
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Build/2495',
- duration: 5,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-2495-2495',
- detailsPath: '/root/ci-project/-/jobs/2495',
- },
- name: 'large_log_output',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/301',
- name: 'build',
- },
- startedAt: '2022-07-01T16:31:11Z',
- queuedDuration: 79.128625,
- },
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Build/2494',
- duration: 5,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-2494-2494',
- detailsPath: '/root/ci-project/-/jobs/2494',
- },
- name: 'build_job',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/301',
- name: 'build',
- },
- startedAt: '2022-07-01T16:31:05Z',
- queuedDuration: 73.286895,
- },
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Build/2493',
- duration: 16,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-2493-2493',
- detailsPath: '/root/ci-project/-/jobs/2493',
- },
- name: 'wait_job',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/301',
- name: 'build',
- },
- startedAt: '2022-07-01T16:30:48Z',
- queuedDuration: 56.258856,
- },
- ],
- },
- },
- },
- },
-};
-
-export const mockPerformanceInsightsNextPageResponse = {
- data: {
- project: {
- __typename: 'Project',
- id: 'gid://gitlab/Project/20',
- pipeline: {
- __typename: 'Pipeline',
- id: 'gid://gitlab/Ci::Pipeline/97',
- jobs: {
- __typename: 'CiJobConnection',
- pageInfo: {
- __typename: 'PageInfo',
- hasNextPage: true,
- },
- nodes: [
- {
- __typename: 'CiJob',
- id: 'gid://gitlab/Ci::Bridge/2502',
- duration: null,
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-2502-2502',
- detailsPath: '/root/lots-of-jobs-project/-/pipelines/98',
- },
- name: 'trigger_job',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/303',
- name: 'deploy',
- },
- startedAt: null,
- queuedDuration: 424850.376278,
- },
- ],
- },
- },
- },
- },
-};
diff --git a/spec/frontend/pipelines/graph/stage_column_component_spec.js b/spec/frontend/pipelines/graph/stage_column_component_spec.js
index 99e8ea9d0a4..19f597a7267 100644
--- a/spec/frontend/pipelines/graph/stage_column_component_spec.js
+++ b/spec/frontend/pipelines/graph/stage_column_component_spec.js
@@ -42,8 +42,8 @@ describe('stage column component', () => {
const findStageColumnTitle = () => wrapper.find('[data-testid="stage-column-title"]');
const findStageColumnGroup = () => wrapper.find('[data-testid="stage-column-group"]');
const findAllStageColumnGroups = () => wrapper.findAll('[data-testid="stage-column-group"]');
- const findJobItem = () => wrapper.find(JobItem);
- const findActionComponent = () => wrapper.find(ActionComponent);
+ const findJobItem = () => wrapper.findComponent(JobItem);
+ const findActionComponent = () => wrapper.findComponent(ActionComponent);
const createComponent = ({ method = shallowMount, props = {} } = {}) => {
wrapper = method(StageColumnComponent, {
@@ -126,9 +126,9 @@ describe('stage column component', () => {
});
});
- it('capitalizes and escapes name', () => {
- expect(findStageColumnTitle().text()).toBe(
- 'Test &lt;img src=x onerror=alert(document.domain)&gt;',
+ it('escapes name', () => {
+ expect(findStageColumnTitle().html()).toContain(
+ 'test &lt;img src=x onerror=alert(document.domain)&gt;',
);
});
diff --git a/spec/frontend/pipelines/graph_shared/links_layer_spec.js b/spec/frontend/pipelines/graph_shared/links_layer_spec.js
index 44ab60cbee7..e2699d6ff2e 100644
--- a/spec/frontend/pipelines/graph_shared/links_layer_spec.js
+++ b/spec/frontend/pipelines/graph_shared/links_layer_spec.js
@@ -6,7 +6,7 @@ import { generateResponse, mockPipelineResponse } from '../graph/mock_data';
describe('links layer component', () => {
let wrapper;
- const findLinksInner = () => wrapper.find(LinksInner);
+ const findLinksInner = () => wrapper.findComponent(LinksInner);
const pipeline = generateResponse(mockPipelineResponse, 'root/fungi-xoxo');
const containerId = `pipeline-links-container-${pipeline.id}`;
diff --git a/spec/frontend/pipelines/header_component_spec.js b/spec/frontend/pipelines/header_component_spec.js
index 859be8d342c..e583c0798f5 100644
--- a/spec/frontend/pipelines/header_component_spec.js
+++ b/spec/frontend/pipelines/header_component_spec.js
@@ -21,12 +21,12 @@ describe('Pipeline details header', () => {
let glModalDirective;
let mutate = jest.fn();
- const findAlert = () => wrapper.find(GlAlert);
- const findDeleteModal = () => wrapper.find(GlModal);
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findDeleteModal = () => wrapper.findComponent(GlModal);
const findRetryButton = () => wrapper.find('[data-testid="retryPipeline"]');
const findCancelButton = () => wrapper.find('[data-testid="cancelPipeline"]');
const findDeleteButton = () => wrapper.find('[data-testid="deletePipeline"]');
- const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const defaultProvideOptions = {
pipelineId: '14',
diff --git a/spec/frontend/pipelines/performance_insights_modal_spec.js b/spec/frontend/pipelines/performance_insights_modal_spec.js
deleted file mode 100644
index 8c802be7718..00000000000
--- a/spec/frontend/pipelines/performance_insights_modal_spec.js
+++ /dev/null
@@ -1,131 +0,0 @@
-import { GlAlert, GlLink, GlModal } from '@gitlab/ui';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import waitForPromises from 'helpers/wait_for_promises';
-import PerformanceInsightsModal from '~/pipelines/components/performance_insights_modal.vue';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { trimText } from 'helpers/text_helper';
-import getPerformanceInsights from '~/pipelines/graphql/queries/get_performance_insights.query.graphql';
-import {
- mockPerformanceInsightsResponse,
- mockPerformanceInsightsNextPageResponse,
-} from './graph/mock_data';
-
-Vue.use(VueApollo);
-
-describe('Performance insights modal', () => {
- let wrapper;
-
- const findModal = () => wrapper.findComponent(GlModal);
- const findAlert = () => wrapper.findComponent(GlAlert);
- const findLink = () => wrapper.findComponent(GlLink);
- const findLimitText = () => wrapper.findByTestId('limit-alert-text');
- const findQueuedCardData = () => wrapper.findByTestId('insights-queued-card-data');
- const findQueuedCardLink = () => wrapper.findByTestId('insights-queued-card-link');
- const findExecutedCardData = () => wrapper.findByTestId('insights-executed-card-data');
- const findExecutedCardLink = () => wrapper.findByTestId('insights-executed-card-link');
- const findSlowJobsStage = (index) => wrapper.findAllByTestId('insights-slow-job-stage').at(index);
- const findSlowJobsLink = (index) => wrapper.findAllByTestId('insights-slow-job-link').at(index);
-
- const getPerformanceInsightsHandler = jest
- .fn()
- .mockResolvedValue(mockPerformanceInsightsResponse);
-
- const getPerformanceInsightsNextPageHandler = jest
- .fn()
- .mockResolvedValue(mockPerformanceInsightsNextPageResponse);
-
- const requestHandlers = [[getPerformanceInsights, getPerformanceInsightsHandler]];
-
- const createComponent = (handlers = requestHandlers) => {
- wrapper = shallowMountExtended(PerformanceInsightsModal, {
- provide: {
- pipelineIid: '1',
- pipelineProjectPath: 'root/ci-project',
- },
- apolloProvider: createMockApollo(handlers),
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('without next page', () => {
- beforeEach(async () => {
- createComponent();
-
- await waitForPromises();
- });
-
- it('displays modal', () => {
- expect(findModal().exists()).toBe(true);
- });
-
- it('displays alert', () => {
- expect(findAlert().exists()).toBe(true);
- });
-
- it('displays feedback issue link', () => {
- expect(findLink().text()).toBe('Feedback issue');
- expect(findLink().attributes('href')).toBe(
- 'https://gitlab.com/gitlab-org/gitlab/-/issues/365902',
- );
- });
-
- it('does not display limit text', () => {
- expect(findLimitText().exists()).toBe(false);
- });
-
- describe('queued duration card', () => {
- it('displays card data', () => {
- expect(trimText(findQueuedCardData().text())).toBe('4.9 days');
- });
- it('displays card link', () => {
- expect(findQueuedCardLink().attributes('href')).toBe(
- '/root/lots-of-jobs-project/-/pipelines/98',
- );
- });
- });
-
- describe('executed duration card', () => {
- it('displays card data', () => {
- expect(trimText(findExecutedCardData().text())).toBe('trigger_job');
- });
- it('displays card link', () => {
- expect(findExecutedCardLink().attributes('href')).toBe(
- '/root/lots-of-jobs-project/-/pipelines/98',
- );
- });
- });
-
- describe('slow jobs', () => {
- it.each`
- index | expectedStage | expectedName | expectedLink
- ${0} | ${'build'} | ${'wait_job'} | ${'/root/ci-project/-/jobs/2493'}
- ${1} | ${'deploy'} | ${'artifact_job'} | ${'/root/ci-project/-/jobs/2501'}
- ${2} | ${'test'} | ${'allow_failure_test_job'} | ${'/root/ci-project/-/jobs/2497'}
- ${3} | ${'build'} | ${'large_log_output'} | ${'/root/ci-project/-/jobs/2495'}
- ${4} | ${'build'} | ${'build_job'} | ${'/root/ci-project/-/jobs/2494'}
- `(
- 'should display slow job correctly',
- ({ index, expectedStage, expectedName, expectedLink }) => {
- expect(findSlowJobsStage(index).text()).toBe(expectedStage);
- expect(findSlowJobsLink(index).text()).toBe(expectedName);
- expect(findSlowJobsLink(index).attributes('href')).toBe(expectedLink);
- },
- );
- });
- });
-
- describe('with next page', () => {
- it('displays limit text when there is a next page', async () => {
- createComponent([[getPerformanceInsights, getPerformanceInsightsNextPageHandler]]);
-
- await waitForPromises();
-
- expect(findLimitText().exists()).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js b/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js
index 1b89e322d31..d9199f3b0f7 100644
--- a/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js
+++ b/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js
@@ -34,7 +34,7 @@ describe('pipeline graph component', () => {
};
const findAlert = () => wrapper.findComponent(GlAlert);
- const findAllJobPills = () => wrapper.findAll(JobPill);
+ const findAllJobPills = () => wrapper.findAllComponents(JobPill);
const findAllStageNames = () => wrapper.findAllComponents(StageName);
const findLinksLayer = () => wrapper.findComponent(LinksLayer);
const findPipelineGraph = () => wrapper.find('[data-testid="graph-container"]');
diff --git a/spec/frontend/pipelines/pipeline_multi_actions_spec.js b/spec/frontend/pipelines/pipeline_multi_actions_spec.js
index f554166da33..149b40330e2 100644
--- a/spec/frontend/pipelines/pipeline_multi_actions_spec.js
+++ b/spec/frontend/pipelines/pipeline_multi_actions_spec.js
@@ -1,12 +1,14 @@
import { GlAlert, GlDropdown, GlSprintf, GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
import PipelineMultiActions, {
i18n,
} from '~/pipelines/components/pipelines_list/pipeline_multi_actions.vue';
+import { TRACKING_CATEGORIES } from '~/pipelines/constants';
describe('Pipeline Multi Actions Dropdown', () => {
let wrapper;
@@ -136,4 +138,22 @@ describe('Pipeline Multi Actions Dropdown', () => {
});
});
});
+
+ describe('tracking', () => {
+ afterEach(() => {
+ unmockTracking();
+ });
+
+ it('tracks artifacts dropdown click', () => {
+ const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+
+ createComponent();
+
+ findDropdown().vm.$emit('show');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_artifacts_dropdown', {
+ label: TRACKING_CATEGORIES.table,
+ });
+ });
+ });
});
diff --git a/spec/frontend/pipelines/pipeline_url_spec.js b/spec/frontend/pipelines/pipeline_url_spec.js
index 25a97ecf49d..1d66607e72b 100644
--- a/spec/frontend/pipelines/pipeline_url_spec.js
+++ b/spec/frontend/pipelines/pipeline_url_spec.js
@@ -1,12 +1,15 @@
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import PipelineUrlComponent from '~/pipelines/components/pipelines_list/pipeline_url.vue';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
+import { TRACKING_CATEGORIES } from '~/pipelines/constants';
import { mockPipeline, mockPipelineBranch, mockPipelineTag } from './mock_data';
const projectPath = 'test/test';
describe('Pipeline Url Component', () => {
let wrapper;
+ let trackingSpy;
const findTableCell = () => wrapper.findByTestId('pipeline-url-table-cell');
const findPipelineUrlLink = () => wrapper.findByTestId('pipeline-url-link');
@@ -14,6 +17,7 @@ describe('Pipeline Url Component', () => {
const findCommitShortSha = () => wrapper.findByTestId('commit-short-sha');
const findCommitIcon = () => wrapper.findByTestId('commit-icon');
const findCommitIconType = () => wrapper.findByTestId('commit-icon-type');
+ const findCommitRefName = () => wrapper.findByTestId('commit-ref-name');
const findCommitTitleContainer = () => wrapper.findByTestId('commit-title-container');
const findCommitTitle = (commitWrapper) => commitWrapper.find('[data-testid="commit-title"]');
@@ -31,7 +35,6 @@ describe('Pipeline Url Component', () => {
afterEach(() => {
wrapper.destroy();
- wrapper = null;
});
it('should render pipeline url table cell', () => {
@@ -49,7 +52,7 @@ describe('Pipeline Url Component', () => {
});
it('should render the commit title, commit reference and commit-short-sha', () => {
- createComponent({}, true);
+ createComponent();
const commitWrapper = findCommitTitleContainer();
@@ -83,7 +86,7 @@ describe('Pipeline Url Component', () => {
});
it('should render commit icon tooltip', () => {
- createComponent({}, true);
+ createComponent();
expect(findCommitIcon().attributes('title')).toBe('Commit');
});
@@ -94,8 +97,68 @@ describe('Pipeline Url Component', () => {
${mockPipelineBranch()} | ${'Branch'}
${mockPipeline()} | ${'Merge Request'}
`('should render tooltip $expectedTitle for commit icon type', ({ pipeline, expectedTitle }) => {
- createComponent(pipeline, true);
+ createComponent(pipeline);
expect(findCommitIconType().attributes('title')).toBe(expectedTitle);
});
+
+ describe('tracking', () => {
+ beforeEach(() => {
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ });
+
+ afterEach(() => {
+ unmockTracking();
+ });
+
+ it('tracks pipeline id click', () => {
+ createComponent();
+
+ findPipelineUrlLink().vm.$emit('click');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_pipeline_id', {
+ label: TRACKING_CATEGORIES.table,
+ });
+ });
+
+ it('tracks merge request ref click', () => {
+ createComponent();
+
+ findRefName().vm.$emit('click');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_mr_ref', {
+ label: TRACKING_CATEGORIES.table,
+ });
+ });
+
+ it('tracks commit ref name click', () => {
+ createComponent(mockPipelineBranch());
+
+ findCommitRefName().vm.$emit('click');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_commit_name', {
+ label: TRACKING_CATEGORIES.table,
+ });
+ });
+
+ it('tracks commit title click', () => {
+ createComponent(mockPipelineBranch());
+
+ findCommitTitle(findCommitTitleContainer()).vm.$emit('click');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_commit_title', {
+ label: TRACKING_CATEGORIES.table,
+ });
+ });
+
+ it('tracks commit short sha click', () => {
+ createComponent(mockPipelineBranch());
+
+ findCommitShortSha().vm.$emit('click');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_commit_sha', {
+ label: TRACKING_CATEGORIES.table,
+ });
+ });
+ });
});
diff --git a/spec/frontend/pipelines/pipelines_actions_spec.js b/spec/frontend/pipelines/pipelines_actions_spec.js
index 9b2ee6b8278..fdfced38dca 100644
--- a/spec/frontend/pipelines/pipelines_actions_spec.js
+++ b/spec/frontend/pipelines/pipelines_actions_spec.js
@@ -2,6 +2,7 @@ import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { TEST_HOST } from 'spec/test_constants';
import createFlash from '~/flash';
@@ -9,6 +10,7 @@ import axios from '~/lib/utils/axios_utils';
import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
import PipelinesManualActions from '~/pipelines/components/pipelines_list/pipelines_manual_actions.vue';
import GlCountdown from '~/vue_shared/components/gl_countdown.vue';
+import { TRACKING_CATEGORIES } from '~/pipelines/constants';
jest.mock('~/flash');
jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal', () => {
@@ -29,9 +31,9 @@ describe('Pipelines Actions dropdown', () => {
});
};
- const findDropdown = () => wrapper.find(GlDropdown);
- const findAllDropdownItems = () => wrapper.findAll(GlDropdownItem);
- const findAllCountdowns = () => wrapper.findAll(GlCountdown);
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
+ const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
+ const findAllCountdowns = () => wrapper.findAllComponents(GlCountdown);
beforeEach(() => {
mock = new MockAdapter(axios);
@@ -96,6 +98,22 @@ describe('Pipelines Actions dropdown', () => {
expect(createFlash).toHaveBeenCalledTimes(1);
});
});
+
+ describe('tracking', () => {
+ afterEach(() => {
+ unmockTracking();
+ });
+
+ it('tracks manual actions click', () => {
+ const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+
+ findDropdown().vm.$emit('shown');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_manual_actions', {
+ label: TRACKING_CATEGORIES.table,
+ });
+ });
+ });
});
describe('scheduled jobs', () => {
diff --git a/spec/frontend/pipelines/pipelines_artifacts_spec.js b/spec/frontend/pipelines/pipelines_artifacts_spec.js
index 2d876841e06..e3e54716a7b 100644
--- a/spec/frontend/pipelines/pipelines_artifacts_spec.js
+++ b/spec/frontend/pipelines/pipelines_artifacts_spec.js
@@ -30,8 +30,9 @@ describe('Pipelines Artifacts dropdown', () => {
};
const findDropdown = () => wrapper.findComponent(GlDropdown);
- const findFirstGlDropdownItem = () => wrapper.find(GlDropdownItem);
- const findAllGlDropdownItems = () => wrapper.find(GlDropdown).findAll(GlDropdownItem);
+ const findFirstGlDropdownItem = () => wrapper.findComponent(GlDropdownItem);
+ const findAllGlDropdownItems = () =>
+ wrapper.findComponent(GlDropdown).findAllComponents(GlDropdownItem);
afterEach(() => {
wrapper.destroy();
diff --git a/spec/frontend/pipelines/pipelines_spec.js b/spec/frontend/pipelines/pipelines_spec.js
index 0bed24e588e..cc2ff90de57 100644
--- a/spec/frontend/pipelines/pipelines_spec.js
+++ b/spec/frontend/pipelines/pipelines_spec.js
@@ -7,6 +7,7 @@ import { nextTick } from 'vue';
import mockPipelinesResponse from 'test_fixtures/pipelines/pipelines.json';
import setWindowLocation from 'helpers/set_window_location_helper';
import { TEST_HOST } from 'helpers/test_constants';
+import { mockTracking } from 'helpers/tracking_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import Api from '~/api';
@@ -16,7 +17,7 @@ import NavigationControls from '~/pipelines/components/pipelines_list/nav_contro
import PipelinesComponent from '~/pipelines/components/pipelines_list/pipelines.vue';
import PipelinesCiTemplates from '~/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates.vue';
import PipelinesTableComponent from '~/pipelines/components/pipelines_list/pipelines_table.vue';
-import { RAW_TEXT_WARNING } from '~/pipelines/constants';
+import { RAW_TEXT_WARNING, TRACKING_CATEGORIES } from '~/pipelines/constants';
import Store from '~/pipelines/stores/pipelines_store';
import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue';
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
@@ -37,6 +38,7 @@ const mockPipelineWithStages = mockPipelinesResponse.pipelines.find(
describe('Pipelines', () => {
let wrapper;
let mock;
+ let trackingSpy;
const paths = {
emptyStateSvgPath: '/assets/illustrations/pipelines_empty.svg',
@@ -123,7 +125,7 @@ describe('Pipelines', () => {
});
it('shows loading state when the app is loading', () => {
- expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
it('does not display tabs when the first request has not yet been made', () => {
@@ -236,6 +238,8 @@ describe('Pipelines', () => {
count: mockPipelinesResponse.count,
});
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+
goToTab('finished');
await waitForPromises();
@@ -256,6 +260,12 @@ describe('Pipelines', () => {
`${window.location.pathname}?scope=finished&page=1`,
);
});
+
+ it('tracks tab change click', () => {
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_filter_tabs', {
+ label: TRACKING_CATEGORIES.tabs,
+ });
+ });
});
describe('when the scope in the tab is empty', () => {
@@ -375,7 +385,7 @@ describe('Pipelines', () => {
const [firstPage, secondPage] = chunk(mockPipelinesResponse.pipelines, mockPageSize);
const goToPage = (page) => {
- findTablePagination().find(GlPagination).vm.$emit('input', page);
+ findTablePagination().findComponent(GlPagination).vm.$emit('input', page);
};
beforeEach(async () => {
@@ -583,7 +593,7 @@ describe('Pipelines', () => {
'This project is not currently set up to run pipelines.',
);
- expect(findEmptyState().find(GlButton).exists()).toBe(false);
+ expect(findEmptyState().findComponent(GlButton).exists()).toBe(false);
});
it('does not render tabs or buttons', () => {
diff --git a/spec/frontend/pipelines/pipelines_table_spec.js b/spec/frontend/pipelines/pipelines_table_spec.js
index 7b49baa5a20..044683ce533 100644
--- a/spec/frontend/pipelines/pipelines_table_spec.js
+++ b/spec/frontend/pipelines/pipelines_table_spec.js
@@ -2,8 +2,9 @@ import '~/commons';
import { GlTableLite } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import fixture from 'test_fixtures/pipelines/pipelines.json';
+import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue';
+import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
import PipelineOperations from '~/pipelines/components/pipelines_list/pipeline_operations.vue';
import PipelineTriggerer from '~/pipelines/components/pipelines_list/pipeline_triggerer.vue';
import PipelineUrl from '~/pipelines/components/pipelines_list/pipeline_url.vue';
@@ -13,6 +14,7 @@ import {
PipelineKeyOptions,
BUTTON_TOOLTIP_RETRY,
BUTTON_TOOLTIP_CANCEL,
+ TRACKING_CATEGORIES,
} from '~/pipelines/constants';
import eventHub from '~/pipelines/event_hub';
@@ -23,6 +25,7 @@ jest.mock('~/pipelines/event_hub');
describe('Pipelines Table', () => {
let pipeline;
let wrapper;
+ let trackingSpy;
const defaultProps = {
pipelines: [],
@@ -69,6 +72,7 @@ describe('Pipelines Table', () => {
afterEach(() => {
wrapper.destroy();
+
wrapper = null;
});
@@ -96,10 +100,6 @@ describe('Pipelines Table', () => {
it('should render a status badge', () => {
expect(findStatusBadge().exists()).toBe(true);
});
-
- it('should render status badge with correct path', () => {
- expect(findStatusBadge().attributes('href')).toBe(pipeline.path);
- });
});
describe('pipeline cell', () => {
@@ -113,40 +113,28 @@ describe('Pipelines Table', () => {
});
describe('stages cell', () => {
- it('should render a pipeline mini graph', () => {
+ it('should render pipeline mini graph', () => {
expect(findPipelineMiniGraph().exists()).toBe(true);
});
it('should render the right number of stages', () => {
const stagesLength = pipeline.details.stages.length;
- expect(
- findPipelineMiniGraph().findAll('[data-testid="mini-pipeline-graph-dropdown"]'),
- ).toHaveLength(stagesLength);
+ expect(findPipelineMiniGraph().props('stages').length).toBe(stagesLength);
});
describe('when pipeline does not have stages', () => {
beforeEach(() => {
pipeline = createMockPipeline();
- pipeline.details.stages = null;
+ pipeline.details.stages = [];
createComponent({ pipelines: [pipeline] });
});
it('stages are not rendered', () => {
- expect(findPipelineMiniGraph().exists()).toBe(false);
+ expect(findPipelineMiniGraph().props('stages')).toHaveLength(0);
});
});
- it('should not update dropdown', () => {
- expect(findPipelineMiniGraph().props('updateDropdown')).toBe(false);
- });
-
- it('when update graph dropdown is set, should update graph dropdown', () => {
- createComponent({ pipelines: [pipeline], updateGraphDropdown: true });
-
- expect(findPipelineMiniGraph().props('updateDropdown')).toBe(true);
- });
-
it('when action request is complete, should refresh table', () => {
findPipelineMiniGraph().vm.$emit('pipelineActionRequestComplete');
@@ -179,5 +167,47 @@ describe('Pipelines Table', () => {
expect(findTriggerer().exists()).toBe(true);
});
});
+
+ describe('tracking', () => {
+ beforeEach(() => {
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ });
+
+ afterEach(() => {
+ unmockTracking();
+ });
+
+ it('tracks status badge click', () => {
+ findStatusBadge().vm.$emit('ciStatusBadgeClick');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_ci_status_badge', {
+ label: TRACKING_CATEGORIES.table,
+ });
+ });
+
+ it('tracks retry pipeline button click', () => {
+ findRetryBtn().vm.$emit('click');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_retry_button', {
+ label: TRACKING_CATEGORIES.table,
+ });
+ });
+
+ it('tracks cancel pipeline button click', () => {
+ findCancelBtn().vm.$emit('click');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_cancel_button', {
+ label: TRACKING_CATEGORIES.table,
+ });
+ });
+
+ it('tracks pipeline mini graph stage click', () => {
+ findPipelineMiniGraph().vm.$emit('miniGraphStageClick');
+
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_minigraph', {
+ label: TRACKING_CATEGORIES.table,
+ });
+ });
+ });
});
});
diff --git a/spec/frontend/pipelines/test_reports/test_suite_table_spec.js b/spec/frontend/pipelines/test_reports/test_suite_table_spec.js
index c372ac06c35..da13df833e7 100644
--- a/spec/frontend/pipelines/test_reports/test_suite_table_spec.js
+++ b/spec/frontend/pipelines/test_reports/test_suite_table_spec.js
@@ -26,10 +26,10 @@ describe('Test reports suite table', () => {
const noCasesMessage = () => wrapper.findByTestId('no-test-cases');
const artifactsExpiredMessage = () => wrapper.findByTestId('artifacts-expired');
- const artifactsExpiredEmptyState = () => wrapper.find(GlEmptyState);
+ const artifactsExpiredEmptyState = () => wrapper.findComponent(GlEmptyState);
const allCaseRows = () => wrapper.findAllByTestId('test-case-row');
const findCaseRowAtIndex = (index) => wrapper.findAllByTestId('test-case-row').at(index);
- const findLinkForRow = (row) => row.find(GlLink);
+ const findLinkForRow = (row) => row.findComponent(GlLink);
const findIconForRow = (row, status) => row.find(`.ci-status-icon-${status}`);
const createComponent = ({ suite = testSuite, perPage = 20, errorMessage } = {}) => {
@@ -113,7 +113,7 @@ describe('Test reports suite table', () => {
const filePath = `${blobPath}/${relativeFile}`;
const row = findCaseRowAtIndex(0);
const fileLink = findLinkForRow(row);
- const button = row.find(GlButton);
+ const button = row.findComponent(GlButton);
expect(fileLink.attributes('href')).toBe(filePath);
expect(row.text()).toContain(file);
@@ -134,7 +134,7 @@ describe('Test reports suite table', () => {
});
it('renders a pagination component', () => {
- expect(wrapper.find(GlPagination).exists()).toBe(true);
+ expect(wrapper.findComponent(GlPagination).exists()).toBe(true);
});
});
diff --git a/spec/frontend/pipelines/test_reports/test_summary_table_spec.js b/spec/frontend/pipelines/test_reports/test_summary_table_spec.js
index 0e1229f7067..cfe9ff564dc 100644
--- a/spec/frontend/pipelines/test_reports/test_summary_table_spec.js
+++ b/spec/frontend/pipelines/test_reports/test_summary_table_spec.js
@@ -44,7 +44,7 @@ describe('Test reports summary table', () => {
describe('when test reports are supplied', () => {
beforeEach(() => createComponent());
- const findErrorIcon = () => wrapper.find({ ref: 'suiteErrorIcon' });
+ const findErrorIcon = () => wrapper.findComponent({ ref: 'suiteErrorIcon' });
it('renders the correct number of rows', () => {
expect(noSuitesToShow().exists()).toBe(false);
diff --git a/spec/frontend/pipelines/time_ago_spec.js b/spec/frontend/pipelines/time_ago_spec.js
index 3de7995b476..f0da0df2ba6 100644
--- a/spec/frontend/pipelines/time_ago_spec.js
+++ b/spec/frontend/pipelines/time_ago_spec.js
@@ -48,7 +48,7 @@ describe('Timeago component', () => {
});
it('should render duration and timer svg', () => {
- const icon = duration().find(GlIcon);
+ const icon = duration().findComponent(GlIcon);
expect(duration().exists()).toBe(true);
expect(icon.props('name')).toBe('timer');
@@ -71,7 +71,7 @@ describe('Timeago component', () => {
});
it('should render time and calendar icon', () => {
- const icon = finishedAt().find(GlIcon);
+ const icon = finishedAt().findComponent(GlIcon);
const time = finishedAt().find('time');
expect(finishedAt().exists()).toBe(true);
diff --git a/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js
index ba478363d04..caa66502e11 100644
--- a/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js
+++ b/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js
@@ -9,9 +9,10 @@ import { branches, mockBranchesAfterMap } from '../mock_data';
describe('Pipeline Branch Name Token', () => {
let wrapper;
- const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
- const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
- const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const findFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken);
+ const findAllFilteredSearchSuggestions = () =>
+ wrapper.findAllComponents(GlFilteredSearchSuggestion);
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const getBranchSuggestions = () =>
findAllFilteredSearchSuggestions().wrappers.map((w) => w.text());
diff --git a/spec/frontend/pipelines/tokens/pipeline_source_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_source_token_spec.js
index b8abf2c1727..60abb63a7e0 100644
--- a/spec/frontend/pipelines/tokens/pipeline_source_token_spec.js
+++ b/spec/frontend/pipelines/tokens/pipeline_source_token_spec.js
@@ -7,8 +7,9 @@ import PipelineSourceToken from '~/pipelines/components/pipelines_list/tokens/pi
describe('Pipeline Source Token', () => {
let wrapper;
- const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
- const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
+ const findFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken);
+ const findAllFilteredSearchSuggestions = () =>
+ wrapper.findAllComponents(GlFilteredSearchSuggestion);
const defaultProps = {
config: {
diff --git a/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js
index 2c5fa8b00e2..94f9a37f707 100644
--- a/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js
+++ b/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js
@@ -6,9 +6,10 @@ import PipelineStatusToken from '~/pipelines/components/pipelines_list/tokens/pi
describe('Pipeline Status Token', () => {
let wrapper;
- const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
- const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
- const findAllGlIcons = () => wrapper.findAll(GlIcon);
+ const findFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken);
+ const findAllFilteredSearchSuggestions = () =>
+ wrapper.findAllComponents(GlFilteredSearchSuggestion);
+ const findAllGlIcons = () => wrapper.findAllComponents(GlIcon);
const defaultProps = {
config: {
diff --git a/spec/frontend/pipelines/tokens/pipeline_tag_name_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_tag_name_token_spec.js
index 596a9218c39..7311a5d2f5a 100644
--- a/spec/frontend/pipelines/tokens/pipeline_tag_name_token_spec.js
+++ b/spec/frontend/pipelines/tokens/pipeline_tag_name_token_spec.js
@@ -7,9 +7,10 @@ import { tags, mockTagsAfterMap } from '../mock_data';
describe('Pipeline Branch Name Token', () => {
let wrapper;
- const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
- const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
- const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const findFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken);
+ const findAllFilteredSearchSuggestions = () =>
+ wrapper.findAllComponents(GlFilteredSearchSuggestion);
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const stubs = {
GlFilteredSearchToken: {
diff --git a/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js
index 397dbdf95a9..c763bfe1b27 100644
--- a/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js
+++ b/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js
@@ -8,9 +8,10 @@ import { users } from '../mock_data';
describe('Pipeline Trigger Author Token', () => {
let wrapper;
- const findFilteredSearchToken = () => wrapper.find(GlFilteredSearchToken);
- const findAllFilteredSearchSuggestions = () => wrapper.findAll(GlFilteredSearchSuggestion);
- const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const findFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken);
+ const findAllFilteredSearchSuggestions = () =>
+ wrapper.findAllComponents(GlFilteredSearchSuggestion);
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const defaultProps = {
config: {
diff --git a/spec/frontend/pipelines/utils_spec.js b/spec/frontend/pipelines/utils_spec.js
index a82390fae22..1c23a7e4fcf 100644
--- a/spec/frontend/pipelines/utils_spec.js
+++ b/spec/frontend/pipelines/utils_spec.js
@@ -8,14 +8,10 @@ import {
removeOrphanNodes,
getMaxNodes,
} from '~/pipelines/components/parsing_utils';
-import { createNodeDict, calculateJobStats, calculateSlowestFiveJobs } from '~/pipelines/utils';
+import { createNodeDict } from '~/pipelines/utils';
import { mockParsedGraphQLNodes, missingJob } from './components/dag/mock_data';
-import {
- generateResponse,
- mockPipelineResponse,
- mockPerformanceInsightsResponse,
-} from './graph/mock_data';
+import { generateResponse, mockPipelineResponse } from './graph/mock_data';
describe('DAG visualization parsing utilities', () => {
const nodeDict = createNodeDict(mockParsedGraphQLNodes);
@@ -162,40 +158,4 @@ describe('DAG visualization parsing utilities', () => {
expect(columns).toMatchSnapshot();
});
});
-
- describe('performance insights', () => {
- const {
- data: {
- project: {
- pipeline: { jobs },
- },
- },
- } = mockPerformanceInsightsResponse;
-
- describe('calculateJobStats', () => {
- const expectedJob = jobs.nodes[0];
-
- it('returns the job that spent this longest time queued', () => {
- expect(calculateJobStats(jobs, 'queuedDuration')).toEqual(expectedJob);
- });
-
- it('returns the job that was executed last', () => {
- expect(calculateJobStats(jobs, 'startedAt')).toEqual(expectedJob);
- });
- });
-
- describe('calculateSlowestFiveJobs', () => {
- it('returns the slowest five jobs of the pipeline', () => {
- const expectedJobs = [
- jobs.nodes[9],
- jobs.nodes[1],
- jobs.nodes[5],
- jobs.nodes[7],
- jobs.nodes[8],
- ];
-
- expect(calculateSlowestFiveJobs(jobs)).toEqual(expectedJobs);
- });
- });
- });
});
diff --git a/spec/frontend/popovers/components/popovers_spec.js b/spec/frontend/popovers/components/popovers_spec.js
index 6fdcd34ae83..eba6b95214d 100644
--- a/spec/frontend/popovers/components/popovers_spec.js
+++ b/spec/frontend/popovers/components/popovers_spec.js
@@ -31,7 +31,7 @@ describe('popovers/components/popovers.vue', () => {
return target;
};
- const allPopovers = () => wrapper.findAll(GlPopover);
+ const allPopovers = () => wrapper.findAllComponents(GlPopover);
afterEach(() => {
wrapper.destroy();
@@ -42,7 +42,7 @@ describe('popovers/components/popovers.vue', () => {
it('attaches popovers to the targets specified', async () => {
const target = createPopoverTarget();
await buildWrapper(target);
- expect(wrapper.find(GlPopover).props('target')).toBe(target);
+ expect(wrapper.findComponent(GlPopover).props('target')).toBe(target);
});
it('does not attach a popover twice to the same element', async () => {
@@ -52,7 +52,7 @@ describe('popovers/components/popovers.vue', () => {
await nextTick();
- expect(wrapper.findAll(GlPopover)).toHaveLength(1);
+ expect(wrapper.findAllComponents(GlPopover)).toHaveLength(1);
});
describe('supports HTML content', () => {
@@ -66,7 +66,7 @@ describe('popovers/components/popovers.vue', () => {
`('$description', async ({ content, render }) => {
await buildWrapper(createPopoverTarget({ content, html: true }));
- const html = wrapper.find(GlPopover).html();
+ const html = wrapper.findComponent(GlPopover).html();
expect(html).toContain(render);
});
});
@@ -78,7 +78,7 @@ describe('popovers/components/popovers.vue', () => {
`('sets $option to $value when data-$option is set in target', async ({ option, value }) => {
await buildWrapper(createPopoverTarget({ [option]: value }));
- expect(wrapper.find(GlPopover).props(option)).toBe(value);
+ expect(wrapper.findComponent(GlPopover).props(option)).toBe(value);
});
});
diff --git a/spec/frontend/profile/account/components/delete_account_modal_spec.js b/spec/frontend/profile/account/components/delete_account_modal_spec.js
index ad62d84c43c..e4a316e1ee7 100644
--- a/spec/frontend/profile/account/components/delete_account_modal_spec.js
+++ b/spec/frontend/profile/account/components/delete_account_modal_spec.js
@@ -53,7 +53,7 @@ describe('DeleteAccountModal component', () => {
input: vm.$el.querySelector(`[name="${confirmation}"]`),
};
};
- const findModal = () => wrapper.find(GlModalStub);
+ const findModal = () => wrapper.findComponent(GlModalStub);
describe('with password confirmation', () => {
beforeEach(async () => {
diff --git a/spec/frontend/profile/account/components/update_username_spec.js b/spec/frontend/profile/account/components/update_username_spec.js
index 0e56bccf27e..e331eed1863 100644
--- a/spec/frontend/profile/account/components/update_username_spec.js
+++ b/spec/frontend/profile/account/components/update_username_spec.js
@@ -44,7 +44,7 @@ describe('UpdateUsername component', () => {
});
const findElements = () => {
- const modal = wrapper.find(GlModal);
+ const modal = wrapper.findComponent(GlModal);
return {
modal,
@@ -149,7 +149,7 @@ describe('UpdateUsername component', () => {
await expect(wrapper.vm.onConfirm()).rejects.toThrow();
- expect(createFlash).toBeCalledWith({
+ expect(createFlash).toHaveBeenCalledWith({
message: 'Invalid username',
});
});
@@ -161,7 +161,7 @@ describe('UpdateUsername component', () => {
await expect(wrapper.vm.onConfirm()).rejects.toThrow();
- expect(createFlash).toBeCalledWith({
+ expect(createFlash).toHaveBeenCalledWith({
message: 'An error occurred while updating your username, please try again.',
});
});
diff --git a/spec/frontend/profile/preferences/components/integration_view_spec.js b/spec/frontend/profile/preferences/components/integration_view_spec.js
index 92c53b8c91b..f650bee7fda 100644
--- a/spec/frontend/profile/preferences/components/integration_view_spec.js
+++ b/spec/frontend/profile/preferences/components/integration_view_spec.js
@@ -98,6 +98,6 @@ describe('IntegrationView component', () => {
it('should render the help text', () => {
wrapper = createComponent();
- expect(wrapper.find(IntegrationHelpText).exists()).toBe(true);
+ expect(wrapper.findComponent(IntegrationHelpText).exists()).toBe(true);
});
});
diff --git a/spec/frontend/profile/preferences/components/profile_preferences_spec.js b/spec/frontend/profile/preferences/components/profile_preferences_spec.js
index 4d2dcf83d3b..89ce838a383 100644
--- a/spec/frontend/profile/preferences/components/profile_preferences_spec.js
+++ b/spec/frontend/profile/preferences/components/profile_preferences_spec.js
@@ -90,7 +90,7 @@ describe('ProfilePreferences component', () => {
it('should not render Integrations section', () => {
wrapper = createComponent();
- const views = wrapper.findAll(IntegrationView);
+ const views = wrapper.findAllComponents(IntegrationView);
const divider = findIntegrationsDivider();
const heading = findIntegrationsHeading();
@@ -103,7 +103,7 @@ describe('ProfilePreferences component', () => {
wrapper = createComponent({ provide: { integrationViews } });
const divider = findIntegrationsDivider();
const heading = findIntegrationsHeading();
- const views = wrapper.findAll(IntegrationView);
+ const views = wrapper.findAllComponents(IntegrationView);
expect(divider.exists()).toBe(true);
expect(heading.exists()).toBe(true);
diff --git a/spec/frontend/projects/commit/components/form_modal_spec.js b/spec/frontend/projects/commit/components/form_modal_spec.js
index 79e9dab935d..20c312ec771 100644
--- a/spec/frontend/projects/commit/components/form_modal_spec.js
+++ b/spec/frontend/projects/commit/components/form_modal_spec.js
@@ -99,7 +99,9 @@ describe('CommitFormModal', () => {
createComponent(shallowMount, {}, { prependedText: '_prepended_text_' });
expect(findPrependedText().exists()).toBe(true);
- expect(findPrependedText().find(GlSprintf).attributes('message')).toBe('_prepended_text_');
+ expect(findPrependedText().findComponent(GlSprintf).attributes('message')).toBe(
+ '_prepended_text_',
+ );
});
it('Does not show prepended text', () => {
@@ -124,7 +126,7 @@ describe('CommitFormModal', () => {
createComponent(shallowMount, { pushCode: false });
expect(findAppendedText().exists()).toBe(true);
- expect(findAppendedText().find(GlSprintf).attributes('message')).toContain(
+ expect(findAppendedText().findComponent(GlSprintf).attributes('message')).toContain(
mockData.modalPropsData.i18n.branchInFork,
);
});
@@ -133,7 +135,7 @@ describe('CommitFormModal', () => {
createComponent(shallowMount, { pushCode: false, branchCollaboration: true });
expect(findAppendedText().exists()).toBe(true);
- expect(findAppendedText().find(GlSprintf).attributes('message')).toContain(
+ expect(findAppendedText().findComponent(GlSprintf).attributes('message')).toContain(
mockData.modalPropsData.i18n.existingBranch,
);
});
diff --git a/spec/frontend/projects/commit/store/mutations_spec.js b/spec/frontend/projects/commit/store/mutations_spec.js
index 60abf0fddad..40174b3057a 100644
--- a/spec/frontend/projects/commit/store/mutations_spec.js
+++ b/spec/frontend/projects/commit/store/mutations_spec.js
@@ -26,7 +26,7 @@ describe('Commit form modal mutations', () => {
});
describe('CLEAR_MODAL', () => {
- it('should clear modal state ', () => {
+ it('should clear modal state', () => {
stateCopy = { branch: '_main_', defaultBranch: '_default_branch_' };
mutations[types.CLEAR_MODAL](stateCopy);
diff --git a/spec/frontend/projects/commits/components/author_select_spec.js b/spec/frontend/projects/commits/components/author_select_spec.js
index 57e5ef0ed1d..907e0e226b6 100644
--- a/spec/frontend/projects/commits/components/author_select_spec.js
+++ b/spec/frontend/projects/commits/components/author_select_spec.js
@@ -58,11 +58,11 @@ describe('Author Select', () => {
resetHTMLFixture();
});
- const findDropdownContainer = () => wrapper.find({ ref: 'dropdownContainer' });
- const findDropdown = () => wrapper.find(GlDropdown);
- const findDropdownHeader = () => wrapper.find(GlDropdownSectionHeader);
- const findSearchBox = () => wrapper.find(GlSearchBoxByType);
- const findDropdownItems = () => wrapper.findAll(GlDropdownItem);
+ const findDropdownContainer = () => wrapper.findComponent({ ref: 'dropdownContainer' });
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
+ const findDropdownHeader = () => wrapper.findComponent(GlDropdownSectionHeader);
+ const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
+ const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
describe('user is searching via "filter by commit message"', () => {
it('disables dropdown container', async () => {
diff --git a/spec/frontend/projects/compare/components/app_spec.js b/spec/frontend/projects/compare/components/app_spec.js
index c9ffdf20c32..2dbecf7cc61 100644
--- a/spec/frontend/projects/compare/components/app_spec.js
+++ b/spec/frontend/projects/compare/components/app_spec.js
@@ -58,7 +58,7 @@ describe('CompareApp component', () => {
});
it('render Source and Target BranchDropdown components', () => {
- const revisionCards = wrapper.findAll(RevisionCard);
+ const revisionCards = wrapper.findAllComponents(RevisionCard);
expect(revisionCards.length).toBe(2);
expect(revisionCards.at(0).props('revisionText')).toBe('Source');
@@ -66,7 +66,7 @@ describe('CompareApp component', () => {
});
describe('compare button', () => {
- const findCompareButton = () => wrapper.find(GlButton);
+ const findCompareButton = () => wrapper.findComponent(GlButton);
it('renders button', () => {
expect(findCompareButton().exists()).toBe(true);
diff --git a/spec/frontend/projects/compare/components/repo_dropdown_spec.js b/spec/frontend/projects/compare/components/repo_dropdown_spec.js
index 98aec347e4b..21cca857c6a 100644
--- a/spec/frontend/projects/compare/components/repo_dropdown_spec.js
+++ b/spec/frontend/projects/compare/components/repo_dropdown_spec.js
@@ -21,7 +21,7 @@ describe('RepoDropdown component', () => {
wrapper = null;
});
- const findGlDropdown = () => wrapper.find(GlDropdown);
+ const findGlDropdown = () => wrapper.findComponent(GlDropdown);
const findHiddenInput = () => wrapper.find('input[type="hidden"]');
describe('Source Revision', () => {
@@ -73,7 +73,7 @@ describe('RepoDropdown component', () => {
});
it('emits `selectProject` event when another target project is selected', async () => {
- findGlDropdown().findAll(GlDropdownItem).at(0).vm.$emit('click');
+ findGlDropdown().findAllComponents(GlDropdownItem).at(0).vm.$emit('click');
await nextTick();
expect(wrapper.emitted('selectProject')[0][0]).toEqual({
diff --git a/spec/frontend/projects/compare/components/revision_card_spec.js b/spec/frontend/projects/compare/components/revision_card_spec.js
index a741393fcf3..b23bd91ceda 100644
--- a/spec/frontend/projects/compare/components/revision_card_spec.js
+++ b/spec/frontend/projects/compare/components/revision_card_spec.js
@@ -32,10 +32,10 @@ describe('RepoDropdown component', () => {
});
it('renders RepoDropdown component', () => {
- expect(wrapper.findAll(RepoDropdown).exists()).toBe(true);
+ expect(wrapper.findAllComponents(RepoDropdown).exists()).toBe(true);
});
it('renders RevisionDropdown component', () => {
- expect(wrapper.findAll(RevisionDropdown).exists()).toBe(true);
+ expect(wrapper.findAllComponents(RevisionDropdown).exists()).toBe(true);
});
});
diff --git a/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js b/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js
index 102f95f65da..f64af1aa994 100644
--- a/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js
+++ b/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js
@@ -38,7 +38,7 @@ describe('RevisionDropdown component', () => {
axiosMock.restore();
});
- const findGlDropdown = () => wrapper.find(GlDropdown);
+ const findGlDropdown = () => wrapper.findComponent(GlDropdown);
it('sets hidden input', () => {
expect(wrapper.find('input[type="hidden"]').attributes('value')).toBe(
@@ -99,7 +99,7 @@ describe('RevisionDropdown component', () => {
});
it('emits a "selectRevision" event when a revision is selected', async () => {
- const findGlDropdownItems = () => wrapper.findAll(GlDropdownItem);
+ const findGlDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
const findFirstGlDropdownItem = () => findGlDropdownItems().at(0);
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
diff --git a/spec/frontend/projects/compare/components/revision_dropdown_spec.js b/spec/frontend/projects/compare/components/revision_dropdown_spec.js
index c8a90848492..35e32fd3da0 100644
--- a/spec/frontend/projects/compare/components/revision_dropdown_spec.js
+++ b/spec/frontend/projects/compare/components/revision_dropdown_spec.js
@@ -35,8 +35,8 @@ describe('RevisionDropdown component', () => {
axiosMock.restore();
});
- const findGlDropdown = () => wrapper.find(GlDropdown);
- const findSearchBox = () => wrapper.find(GlSearchBoxByType);
+ const findGlDropdown = () => wrapper.findComponent(GlDropdown);
+ const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
it('sets hidden input', () => {
createComponent();
@@ -144,7 +144,7 @@ describe('RevisionDropdown component', () => {
wrapper.vm.branches = ['some-branch'];
await nextTick();
- findGlDropdown().findAll(GlDropdownItem).at(0).vm.$emit('click');
+ findGlDropdown().findAllComponents(GlDropdownItem).at(0).vm.$emit('click');
expect(wrapper.emitted('selectRevision')[0][0]).toEqual({
direction: 'to',
diff --git a/spec/frontend/projects/components/project_delete_button_spec.js b/spec/frontend/projects/components/project_delete_button_spec.js
index a3bc4931eb3..49e3218e5bc 100644
--- a/spec/frontend/projects/components/project_delete_button_spec.js
+++ b/spec/frontend/projects/components/project_delete_button_spec.js
@@ -8,7 +8,7 @@ jest.mock('lodash/uniqueId', () => () => 'fakeUniqueId');
describe('Project remove modal', () => {
let wrapper;
- const findSharedDeleteButton = () => wrapper.find(SharedDeleteButton);
+ const findSharedDeleteButton = () => wrapper.findComponent(SharedDeleteButton);
const defaultProps = {
confirmPhrase: 'foo',
diff --git a/spec/frontend/projects/components/shared/delete_button_spec.js b/spec/frontend/projects/components/shared/delete_button_spec.js
index 45c39ee91d8..097b18025a3 100644
--- a/spec/frontend/projects/components/shared/delete_button_spec.js
+++ b/spec/frontend/projects/components/shared/delete_button_spec.js
@@ -11,7 +11,7 @@ describe('Project remove modal', () => {
const findFormElement = () => wrapper.find('form');
const findConfirmButton = () => wrapper.find('.js-modal-action-primary');
const findAuthenticityTokenInput = () => findFormElement().find('input[name=authenticity_token]');
- const findModal = () => wrapper.find(GlModal);
+ const findModal = () => wrapper.findComponent(GlModal);
const findTitle = () => wrapper.find('[data-testid="delete-alert-title"]');
const findAlertBody = () => wrapper.find('[data-testid="delete-alert-body"]');
diff --git a/spec/frontend/projects/details/upload_button_spec.js b/spec/frontend/projects/details/upload_button_spec.js
index d7308963088..50638755260 100644
--- a/spec/frontend/projects/details/upload_button_spec.js
+++ b/spec/frontend/projects/details/upload_button_spec.js
@@ -32,11 +32,11 @@ describe('UploadButton', () => {
});
it('displays an upload button', () => {
- expect(wrapper.find(GlButton).exists()).toBe(true);
+ expect(wrapper.findComponent(GlButton).exists()).toBe(true);
});
it('contains a modal', () => {
- const modal = wrapper.find(UploadBlobModal);
+ const modal = wrapper.findComponent(UploadBlobModal);
expect(modal.exists()).toBe(true);
expect(modal.props('modalId')).toBe(MODAL_ID);
@@ -44,7 +44,7 @@ describe('UploadButton', () => {
describe('when clickinig the upload file button', () => {
beforeEach(() => {
- wrapper.find(GlButton).vm.$emit('click');
+ wrapper.findComponent(GlButton).vm.$emit('click');
});
it('opens the modal', () => {
diff --git a/spec/frontend/projects/pipelines/charts/components/app_spec.js b/spec/frontend/projects/pipelines/charts/components/app_spec.js
index 7b9011fa3d9..e3aaf760d1e 100644
--- a/spec/frontend/projects/pipelines/charts/components/app_spec.js
+++ b/spec/frontend/projects/pipelines/charts/components/app_spec.js
@@ -47,15 +47,16 @@ describe('ProjectsPipelinesChartsApp', () => {
wrapper.destroy();
});
- const findGlTabs = () => wrapper.find(GlTabs);
- const findAllGlTabs = () => wrapper.findAll(GlTab);
+ const findGlTabs = () => wrapper.findComponent(GlTabs);
+ const findAllGlTabs = () => wrapper.findAllComponents(GlTab);
const findGlTabAtIndex = (index) => findAllGlTabs().at(index);
- const findLeadTimeCharts = () => wrapper.find(LeadTimeChartsStub);
- const findTimeToRestoreServiceCharts = () => wrapper.find(TimeToRestoreServiceChartsStub);
- const findChangeFailureRateCharts = () => wrapper.find(ChangeFailureRateChartsStub);
- const findDeploymentFrequencyCharts = () => wrapper.find(DeploymentFrequencyChartsStub);
- const findPipelineCharts = () => wrapper.find(PipelineCharts);
- const findProjectQualitySummary = () => wrapper.find(ProjectQualitySummaryStub);
+ const findLeadTimeCharts = () => wrapper.findComponent(LeadTimeChartsStub);
+ const findTimeToRestoreServiceCharts = () =>
+ wrapper.findComponent(TimeToRestoreServiceChartsStub);
+ const findChangeFailureRateCharts = () => wrapper.findComponent(ChangeFailureRateChartsStub);
+ const findDeploymentFrequencyCharts = () => wrapper.findComponent(DeploymentFrequencyChartsStub);
+ const findPipelineCharts = () => wrapper.findComponent(PipelineCharts);
+ const findProjectQualitySummary = () => wrapper.findComponent(ProjectQualitySummaryStub);
describe('when all charts are available', () => {
beforeEach(() => {
diff --git a/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js b/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js
index 7bb289408b8..8c18d2992ea 100644
--- a/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js
+++ b/spec/frontend/projects/pipelines/charts/components/ci_cd_analytics_charts_spec.js
@@ -81,7 +81,7 @@ describe('~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue', (
it('should select a different chart on change', async () => {
findSegmentedControl().vm.$emit('input', 1);
- const chart = wrapper.find(CiCdAnalyticsAreaChart);
+ const chart = wrapper.findComponent(CiCdAnalyticsAreaChart);
await nextTick();
@@ -92,7 +92,7 @@ describe('~/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue', (
it('should not display charts if there are no charts', () => {
wrapper = createWrapper({ charts: [] });
- expect(wrapper.find(CiCdAnalyticsAreaChart).exists()).toBe(false);
+ expect(wrapper.findComponent(CiCdAnalyticsAreaChart).exists()).toBe(false);
});
describe('slots', () => {
diff --git a/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js b/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js
index 3c91b913e67..8fb59f38ee1 100644
--- a/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js
+++ b/spec/frontend/projects/pipelines/charts/components/pipeline_charts_spec.js
@@ -44,7 +44,7 @@ describe('~/projects/pipelines/charts/components/pipeline_charts.vue', () => {
describe('overall statistics', () => {
it('displays the statistics list', () => {
- const list = wrapper.find(StatisticsList);
+ const list = wrapper.findComponent(StatisticsList);
expect(list.exists()).toBe(true);
expect(list.props('counts')).toEqual({
@@ -56,9 +56,9 @@ describe('~/projects/pipelines/charts/components/pipeline_charts.vue', () => {
});
it('displays the commit duration chart', () => {
- const chart = wrapper.find(GlColumnChart);
+ const chart = wrapper.findComponent(GlColumnChart);
- expect(chart.exists()).toBeTruthy();
+ expect(chart.exists()).toBe(true);
expect(chart.props('yAxisTitle')).toBe('Minutes');
expect(chart.props('xAxisTitle')).toBe('Commit');
expect(chart.props('bars')).toBe(wrapper.vm.timesChartTransformedData);
@@ -68,12 +68,12 @@ describe('~/projects/pipelines/charts/components/pipeline_charts.vue', () => {
describe('pipelines charts', () => {
it('displays the charts components', () => {
- expect(wrapper.find(CiCdAnalyticsCharts).exists()).toBe(true);
+ expect(wrapper.findComponent(CiCdAnalyticsCharts).exists()).toBe(true);
});
describe('displays individual correctly', () => {
it('renders with the correct data', () => {
- const charts = wrapper.find(CiCdAnalyticsCharts);
+ const charts = wrapper.findComponent(CiCdAnalyticsCharts);
expect(charts.props()).toEqual({
charts: wrapper.vm.areaCharts,
chartOptions: wrapper.vm.$options.areaChartOptions,
diff --git a/spec/frontend/projects/settings/components/new_access_dropdown_spec.js b/spec/frontend/projects/settings/components/new_access_dropdown_spec.js
index 1db48ce05d7..1b06f7874a3 100644
--- a/spec/frontend/projects/settings/components/new_access_dropdown_spec.js
+++ b/spec/frontend/projects/settings/components/new_access_dropdown_spec.js
@@ -134,7 +134,7 @@ describe('Access Level Dropdown', () => {
await waitForPromises();
});
- it('renders headers for each section ', () => {
+ it('renders headers for each section', () => {
expect(findAllDropdownHeaders()).toHaveLength(4);
});
@@ -164,7 +164,7 @@ describe('Access Level Dropdown', () => {
expect(findDropdown().props('toggleClass')).toBe('gl-text-gray-500!');
});
- it('when no items selected, displays a default fallback label and has default CSS class ', () => {
+ it('when no items selected, displays a default fallback label and has default CSS class', () => {
expect(findDropdownToggleLabel()).toBe(i18n.selectUsers);
expect(findDropdown().props('toggleClass')).toBe('gl-text-gray-500!');
});
@@ -217,7 +217,7 @@ describe('Access Level Dropdown', () => {
});
describe('selecting an item', () => {
- it('selects the item on click and deselects on the next click ', async () => {
+ it('selects the item on click and deselects on the next click', async () => {
createComponent();
await waitForPromises();
@@ -230,7 +230,7 @@ describe('Access Level Dropdown', () => {
expect(item.props('isChecked')).toBe(false);
});
- it('emits a formatted update on selection ', async () => {
+ it('emits a formatted update on selection', async () => {
// ids: the items appear in that order in the dropdown
// 1 2 3 - roles
// 4 5 6 - groups
diff --git a/spec/frontend/projects/settings/components/shared_runners_toggle_spec.js b/spec/frontend/projects/settings/components/shared_runners_toggle_spec.js
index 0a05832ceb6..329060b9d10 100644
--- a/spec/frontend/projects/settings/components/shared_runners_toggle_spec.js
+++ b/spec/frontend/projects/settings/components/shared_runners_toggle_spec.js
@@ -27,9 +27,9 @@ describe('projects/settings/components/shared_runners', () => {
});
};
- const findErrorAlert = () => wrapper.find(GlAlert);
- const findSharedRunnersToggle = () => wrapper.find(GlToggle);
- const findToggleTooltip = () => wrapper.find(GlTooltip);
+ const findErrorAlert = () => wrapper.findComponent(GlAlert);
+ const findSharedRunnersToggle = () => wrapper.findComponent(GlToggle);
+ const findToggleTooltip = () => wrapper.findComponent(GlTooltip);
const getToggleValue = () => findSharedRunnersToggle().props('value');
const isToggleLoading = () => findSharedRunnersToggle().props('isLoading');
const isToggleDisabled = () => findSharedRunnersToggle().props('disabled');
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 e12c3aeedd6..e920cd48163 100644
--- a/spec/frontend/projects/settings/repository/branch_rules/app_spec.js
+++ b/spec/frontend/projects/settings/repository/branch_rules/app_spec.js
@@ -1,18 +1,55 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
import { mountExtended } from 'helpers/vue_test_utils_helper';
-import BranchRules from '~/projects/settings/repository/branch_rules/app.vue';
+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 createFlash from '~/flash';
+import { branchRulesMockResponse, propsDataMock } from './mock_data';
+
+jest.mock('~/flash');
+
+Vue.use(VueApollo);
describe('Branch rules app', () => {
let wrapper;
+ let fakeApollo;
+
+ const branchRulesQuerySuccessHandler = jest.fn().mockResolvedValue(branchRulesMockResponse);
+
+ const createComponent = async ({ queryHandler = branchRulesQuerySuccessHandler } = {}) => {
+ fakeApollo = createMockApollo([[branchRulesQuery, queryHandler]]);
+
+ wrapper = mountExtended(BranchRules, {
+ apolloProvider: fakeApollo,
+ propsData: {
+ ...propsDataMock,
+ },
+ });
- const createComponent = () => {
- wrapper = mountExtended(BranchRules);
+ await waitForPromises();
};
- const findTitle = () => wrapper.find('strong');
+ const findAllBranchRules = () => wrapper.findAllComponents(BranchRule);
+ const findEmptyState = () => wrapper.findByTestId('empty');
beforeEach(() => createComponent());
- it('renders a title', () => {
- expect(findTitle().text()).toBe('Branch');
+ it('displays an error if branch rules query fails', async () => {
+ await createComponent({ queryHandler: jest.fn().mockRejectedValue() });
+ expect(createFlash).toHaveBeenCalledWith({ message: i18n.queryError });
+ });
+
+ it('displays an empty state if no branch rules are present', async () => {
+ await createComponent({ queryHandler: jest.fn().mockRejectedValue() });
+ expect(findEmptyState().text()).toBe(i18n.emptyState);
+ });
+
+ it('renders branch rules', () => {
+ const { nodes } = branchRulesMockResponse.data.project.branchRules;
+ expect(findAllBranchRules().at(0).text()).toBe(nodes[0].name);
+ expect(findAllBranchRules().at(1).text()).toBe(nodes[1].name);
});
});
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
new file mode 100644
index 00000000000..924dab60704
--- /dev/null
+++ b/spec/frontend/projects/settings/repository/branch_rules/components/branch_rule_spec.js
@@ -0,0 +1,58 @@
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import BranchRule, {
+ i18n,
+} from '~/projects/settings/repository/branch_rules/components/branch_rule.vue';
+
+const defaultProps = {
+ name: 'main',
+ isDefault: true,
+ isProtected: true,
+ approvalDetails: ['requires approval from TEST', '2 status checks'],
+};
+
+describe('Branch rule', () => {
+ let wrapper;
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMountExtended(BranchRule, { propsData: { ...defaultProps, ...props } });
+ };
+
+ const findDefaultBadge = () => wrapper.findByText(i18n.defaultLabel);
+ const findProtectedBadge = () => wrapper.findByText(i18n.protectedLabel);
+ const findBranchName = () => wrapper.findByText(defaultProps.name);
+ const findProtectionDetailsList = () => wrapper.findByRole('list');
+ const findProtectionDetailsListItems = () => wrapper.findAllByRole('listitem');
+
+ beforeEach(() => createComponent());
+
+ it('renders the branch name', () => {
+ expect(findBranchName().exists()).toBe(true);
+ });
+
+ describe('badges', () => {
+ it('renders both default and protected badges', () => {
+ expect(findDefaultBadge().exists()).toBe(true);
+ expect(findProtectedBadge().exists()).toBe(true);
+ });
+
+ it('does not render default badge if isDefault is set to false', () => {
+ createComponent({ isDefault: false });
+ expect(findDefaultBadge().exists()).toBe(false);
+ });
+
+ it('does not render protected badge if isProtected is set to false', () => {
+ createComponent({ isProtected: false });
+ expect(findProtectedBadge().exists()).toBe(false);
+ });
+ });
+
+ it('does not render the protection details list of no details are present', () => {
+ createComponent({ approvalDetails: null });
+ expect(findProtectionDetailsList().exists()).toBe(false);
+ });
+
+ it('renders the protection details list items', () => {
+ expect(findProtectionDetailsListItems().at(0).text()).toBe(defaultProps.approvalDetails[0]);
+ expect(findProtectionDetailsListItems().at(1).text()).toBe(defaultProps.approvalDetails[1]);
+ });
+});
diff --git a/spec/frontend/projects/settings/repository/branch_rules/mock_data.js b/spec/frontend/projects/settings/repository/branch_rules/mock_data.js
new file mode 100644
index 00000000000..14ed35f047d
--- /dev/null
+++ b/spec/frontend/projects/settings/repository/branch_rules/mock_data.js
@@ -0,0 +1,25 @@
+export const branchRulesMockResponse = {
+ data: {
+ project: {
+ id: '123',
+ __typename: 'Project',
+ branchRules: {
+ __typename: 'BranchRuleConnection',
+ nodes: [
+ {
+ name: 'main',
+ __typename: 'BranchRule',
+ },
+ {
+ name: 'test-*',
+ __typename: 'BranchRule',
+ },
+ ],
+ },
+ },
+ },
+};
+
+export const propsDataMock = {
+ projectPath: 'some/project/path',
+};
diff --git a/spec/frontend/projects/settings_service_desk/components/service_desk_root_spec.js b/spec/frontend/projects/settings_service_desk/components/service_desk_root_spec.js
index 62224612387..13f3eea277a 100644
--- a/spec/frontend/projects/settings_service_desk/components/service_desk_root_spec.js
+++ b/spec/frontend/projects/settings_service_desk/components/service_desk_root_spec.js
@@ -1,4 +1,4 @@
-import { GlAlert } from '@gitlab/ui';
+import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import AxiosMockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises';
@@ -23,11 +23,16 @@ describe('ServiceDeskRoot', () => {
selectedTemplate: 'Bug',
selectedFileTemplateProjectId: 42,
templates: ['Bug', 'Documentation'],
+ publicProject: false,
};
- const getAlertText = () => wrapper.find(GlAlert).text();
+ const getAlertText = () => wrapper.findComponent(GlAlert).text();
- const createComponent = () => shallowMount(ServiceDeskRoot, { provide: provideData });
+ const createComponent = (customInject = {}) =>
+ shallowMount(ServiceDeskRoot, {
+ provide: { ...provideData, ...customInject },
+ stubs: { GlSprintf },
+ });
beforeEach(() => {
axiosMock = new AxiosMockAdapter(axios);
@@ -46,7 +51,7 @@ describe('ServiceDeskRoot', () => {
it('is rendered', () => {
wrapper = createComponent();
- expect(wrapper.find(ServiceDeskSetting).props()).toEqual({
+ expect(wrapper.findComponent(ServiceDeskSetting).props()).toEqual({
customEmail: provideData.customEmail,
customEmailEnabled: provideData.customEmailEnabled,
incomingEmail: provideData.initialIncomingEmail,
@@ -60,12 +65,31 @@ describe('ServiceDeskRoot', () => {
});
});
+ it('shows alert about email inference when current project is public', () => {
+ wrapper = createComponent({
+ publicProject: true,
+ });
+
+ const alertEl = wrapper.find('[data-testid="public-project-alert"]');
+ expect(alertEl.exists()).toBe(true);
+ expect(alertEl.text()).toContain(
+ 'This project is public. Non-members can guess the Service Desk email address, because it contains the group and project name.',
+ );
+
+ const alertBodyLink = alertEl.findComponent(GlLink);
+ expect(alertBodyLink.exists()).toBe(true);
+ expect(alertBodyLink.attributes('href')).toBe(
+ '/help/user/project/service_desk.html#using-a-custom-email-address',
+ );
+ expect(alertBodyLink.text()).toBe('How do I create a custom email address?');
+ });
+
describe('toggle event', () => {
describe('when toggling service desk on', () => {
beforeEach(async () => {
wrapper = createComponent();
- wrapper.find(ServiceDeskSetting).vm.$emit('toggle', true);
+ wrapper.findComponent(ServiceDeskSetting).vm.$emit('toggle', true);
await waitForPromises();
});
@@ -87,7 +111,7 @@ describe('ServiceDeskRoot', () => {
beforeEach(async () => {
wrapper = createComponent();
- wrapper.find(ServiceDeskSetting).vm.$emit('toggle', false);
+ wrapper.findComponent(ServiceDeskSetting).vm.$emit('toggle', false);
await waitForPromises();
});
@@ -119,7 +143,7 @@ describe('ServiceDeskRoot', () => {
projectKey: 'key',
};
- wrapper.find(ServiceDeskSetting).vm.$emit('save', payload);
+ wrapper.findComponent(ServiceDeskSetting).vm.$emit('save', payload);
await waitForPromises();
});
@@ -150,7 +174,7 @@ describe('ServiceDeskRoot', () => {
projectKey: 'key',
};
- wrapper.find(ServiceDeskSetting).vm.$emit('save', payload);
+ wrapper.findComponent(ServiceDeskSetting).vm.$emit('save', payload);
await waitForPromises();
});
diff --git a/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js b/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js
index aac1a418142..7c3f4e76ae5 100644
--- a/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js
+++ b/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js
@@ -8,13 +8,14 @@ import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
describe('ServiceDeskSetting', () => {
let wrapper;
- const findButton = () => wrapper.find(GlButton);
- const findClipboardButton = () => wrapper.find(ClipboardButton);
+ const findButton = () => wrapper.findComponent(GlButton);
+ const findClipboardButton = () => wrapper.findComponent(ClipboardButton);
const findIncomingEmail = () => wrapper.findByTestId('incoming-email');
const findIncomingEmailLabel = () => wrapper.findByTestId('incoming-email-label');
- const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
- const findTemplateDropdown = () => wrapper.find(GlDropdown);
- const findToggle = () => wrapper.find(GlToggle);
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findTemplateDropdown = () => wrapper.findComponent(GlDropdown);
+ const findToggle = () => wrapper.findComponent(GlToggle);
+ const findSuffixFormGroup = () => wrapper.findByTestId('suffix-form-group');
const createComponent = ({ props = {} } = {}) =>
extendedWrapper(
@@ -51,6 +52,32 @@ describe('ServiceDeskSetting', () => {
expect(findLoadingIcon().exists()).toBe(true);
expect(findIncomingEmail().exists()).toBe(false);
});
+
+ it('should display help text', () => {
+ expect(findSuffixFormGroup().text()).toContain(
+ 'To add a custom suffix, set up a Service Desk email address',
+ );
+ expect(findSuffixFormGroup().text()).not.toContain(
+ 'Add a suffix to Service Desk email address',
+ );
+ });
+ });
+ });
+
+ describe('when customEmailEnabled', () => {
+ beforeEach(() => {
+ wrapper = createComponent({
+ props: { customEmailEnabled: true },
+ });
+ });
+
+ it('should not display help text', () => {
+ expect(findSuffixFormGroup().text()).not.toContain(
+ 'To add a custom suffix, set up a Service Desk email address',
+ );
+ expect(findSuffixFormGroup().text()).toContain(
+ 'Add a suffix to Service Desk email address',
+ );
});
});
diff --git a/spec/frontend/projects/settings_service_desk/components/service_desk_template_dropdown_spec.js b/spec/frontend/projects/settings_service_desk/components/service_desk_template_dropdown_spec.js
index cdb355f5a9b..6adcfbe8157 100644
--- a/spec/frontend/projects/settings_service_desk/components/service_desk_template_dropdown_spec.js
+++ b/spec/frontend/projects/settings_service_desk/components/service_desk_template_dropdown_spec.js
@@ -7,7 +7,7 @@ import { TEMPLATES } from './mock_data';
describe('ServiceDeskTemplateDropdown', () => {
let wrapper;
- const findTemplateDropdown = () => wrapper.find(GlDropdown);
+ const findTemplateDropdown = () => wrapper.findComponent(GlDropdown);
const createComponent = ({ props = {} } = {}) =>
extendedWrapper(
@@ -53,7 +53,7 @@ describe('ServiceDeskTemplateDropdown', () => {
props: { templates: TEMPLATES },
});
- const headerItems = wrapper.findAll(GlDropdownSectionHeader);
+ const headerItems = wrapper.findAllComponents(GlDropdownSectionHeader);
expect(headerItems).toHaveLength(1);
expect(headerItems.at(0).text()).toBe(TEMPLATES[0]);
@@ -68,7 +68,7 @@ describe('ServiceDeskTemplateDropdown', () => {
const expectedTemplates = templates[1];
- const items = wrapper.findAll(GlDropdownItem);
+ const items = wrapper.findAllComponents(GlDropdownItem);
const dropdownList = expectedTemplates.map((_, index) => items.at(index).text());
expect(items).toHaveLength(expectedTemplates.length);
diff --git a/spec/frontend/ref/components/ref_selector_spec.js b/spec/frontend/ref/components/ref_selector_spec.js
index 882cb2c1199..6c5af5a2625 100644
--- a/spec/frontend/ref/components/ref_selector_spec.js
+++ b/spec/frontend/ref/components/ref_selector_spec.js
@@ -93,20 +93,20 @@ describe('Ref selector component', () => {
const findNoResults = () => wrapper.find('[data-testid="no-results"]');
- const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findSearchBox = () => wrapper.find(GlSearchBoxByType);
+ const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
const findBranchesSection = () => wrapper.find('[data-testid="branches-section"]');
- const findBranchDropdownItems = () => findBranchesSection().findAll(GlDropdownItem);
+ const findBranchDropdownItems = () => findBranchesSection().findAllComponents(GlDropdownItem);
const findFirstBranchDropdownItem = () => findBranchDropdownItems().at(0);
const findTagsSection = () => wrapper.find('[data-testid="tags-section"]');
- const findTagDropdownItems = () => findTagsSection().findAll(GlDropdownItem);
+ const findTagDropdownItems = () => findTagsSection().findAllComponents(GlDropdownItem);
const findFirstTagDropdownItem = () => findTagDropdownItems().at(0);
const findCommitsSection = () => wrapper.find('[data-testid="commits-section"]');
- const findCommitDropdownItems = () => findCommitsSection().findAll(GlDropdownItem);
+ const findCommitDropdownItems = () => findCommitsSection().findAllComponents(GlDropdownItem);
const findFirstCommitDropdownItem = () => findCommitDropdownItems().at(0);
//
@@ -530,13 +530,13 @@ describe('Ref selector component', () => {
});
it('renders a checkmark by the selected item', async () => {
- expect(findFirstBranchDropdownItem().find(GlIcon).element).toHaveClass(
+ expect(findFirstBranchDropdownItem().findComponent(GlIcon).element).toHaveClass(
'gl-visibility-hidden',
);
await selectFirstBranch();
- expect(findFirstBranchDropdownItem().find(GlIcon).element).not.toHaveClass(
+ expect(findFirstBranchDropdownItem().findComponent(GlIcon).element).not.toHaveClass(
'gl-visibility-hidden',
);
});
@@ -684,7 +684,8 @@ describe('Ref selector component', () => {
describe('validation state', () => {
const invalidClass = 'gl-inset-border-1-red-500!';
- const isInvalidClassApplied = () => wrapper.find(GlDropdown).props('toggleClass')[invalidClass];
+ const isInvalidClassApplied = () =>
+ wrapper.findComponent(GlDropdown).props('toggleClass')[invalidClass];
describe('valid state', () => {
describe('when the state prop is not provided', () => {
diff --git a/spec/frontend/related_issues/components/related_issuable_input_spec.js b/spec/frontend/related_issues/components/related_issuable_input_spec.js
index 7d11e3cffb0..f6a13856042 100644
--- a/spec/frontend/related_issues/components/related_issuable_input_spec.js
+++ b/spec/frontend/related_issues/components/related_issuable_input_spec.js
@@ -33,7 +33,7 @@ describe('RelatedIssuableInput', () => {
it('shows placeholder text', () => {
const wrapper = shallowMount(RelatedIssuableInput, { propsData });
- expect(wrapper.find({ ref: 'input' }).element.placeholder).toBe(
+ expect(wrapper.findComponent({ ref: 'input' }).element.placeholder).toBe(
'Paste issue link or <#issue id>',
);
});
@@ -54,7 +54,7 @@ describe('RelatedIssuableInput', () => {
},
});
- expect(wrapper.find({ ref: 'input' }).element.value).toBe('');
+ expect(wrapper.findComponent({ ref: 'input' }).element.value).toBe('');
});
it('does not have GfmAutoComplete', () => {
@@ -85,7 +85,7 @@ describe('RelatedIssuableInput', () => {
await nextTick();
- expect(document.activeElement).toBe(wrapper.find({ ref: 'input' }).element);
+ expect(document.activeElement).toBe(wrapper.findComponent({ ref: 'input' }).element);
});
});
@@ -100,7 +100,7 @@ describe('RelatedIssuableInput', () => {
const newInputValue = 'filling in things';
const untouchedRawReferences = newInputValue.trim().split(/\s/);
const touchedReference = untouchedRawReferences.pop();
- const input = wrapper.find({ ref: 'input' });
+ const input = wrapper.findComponent({ ref: 'input' });
input.element.value = newInputValue;
input.element.selectionStart = newInputValue.length;
diff --git a/spec/frontend/releases/components/app_edit_new_spec.js b/spec/frontend/releases/components/app_edit_new_spec.js
index cb044b9e891..649d8eef6ec 100644
--- a/spec/frontend/releases/components/app_edit_new_spec.js
+++ b/spec/frontend/releases/components/app_edit_new_spec.js
@@ -220,7 +220,7 @@ describe('Release edit/new component', () => {
});
it('renders a checkbox to include release notes', () => {
- expect(wrapper.find(GlFormCheckbox).exists()).toBe(true);
+ expect(wrapper.findComponent(GlFormCheckbox).exists()).toBe(true);
});
});
@@ -238,7 +238,7 @@ describe('Release edit/new component', () => {
beforeEach(factory);
it('renders the asset links portion of the form', () => {
- expect(wrapper.find(AssetLinksForm).exists()).toBe(true);
+ expect(wrapper.findComponent(AssetLinksForm).exists()).toBe(true);
});
});
diff --git a/spec/frontend/releases/components/app_show_spec.js b/spec/frontend/releases/components/app_show_spec.js
index c2ea6900d6e..9ca25b3b69a 100644
--- a/spec/frontend/releases/components/app_show_spec.js
+++ b/spec/frontend/releases/components/app_show_spec.js
@@ -36,8 +36,8 @@ describe('Release show component', () => {
wrapper = null;
});
- const findLoadingSkeleton = () => wrapper.find(ReleaseSkeletonLoader);
- const findReleaseBlock = () => wrapper.find(ReleaseBlock);
+ const findLoadingSkeleton = () => wrapper.findComponent(ReleaseSkeletonLoader);
+ const findReleaseBlock = () => wrapper.findComponent(ReleaseBlock);
const expectLoadingIndicator = () => {
it('renders a loading indicator', () => {
diff --git a/spec/frontend/releases/components/asset_links_form_spec.js b/spec/frontend/releases/components/asset_links_form_spec.js
index 17f079ba5a6..1ff5766b074 100644
--- a/spec/frontend/releases/components/asset_links_form_spec.js
+++ b/spec/frontend/releases/components/asset_links_form_spec.js
@@ -73,7 +73,7 @@ describe('Release edit component', () => {
it('calls the "addEmptyAssetLink" store method when the "Add another link" button is clicked', () => {
expect(actions.addEmptyAssetLink).not.toHaveBeenCalled();
- wrapper.find({ ref: 'addAnotherLinkButton' }).vm.$emit('click');
+ wrapper.findComponent({ ref: 'addAnotherLinkButton' }).vm.$emit('click');
expect(actions.addEmptyAssetLink).toHaveBeenCalledTimes(1);
});
@@ -92,7 +92,7 @@ describe('Release edit component', () => {
let newUrl;
beforeEach(() => {
- input = wrapper.find({ ref: 'urlInput' }).element;
+ input = wrapper.findComponent({ ref: 'urlInput' }).element;
linkIdToUpdate = release.assets.links[0].id;
newUrl = 'updated url';
});
@@ -118,7 +118,7 @@ describe('Release edit component', () => {
it('calls the "updateAssetLinkUrl" store method when text is entered into the "URL" input field', () => {
expectStoreMethodNotToBeCalled();
- wrapper.find({ ref: 'urlInput' }).vm.$emit('change', newUrl);
+ wrapper.findComponent({ ref: 'urlInput' }).vm.$emit('change', newUrl);
expectStoreMethodToBeCalled();
});
@@ -150,7 +150,7 @@ describe('Release edit component', () => {
let newName;
beforeEach(() => {
- input = wrapper.find({ ref: 'nameInput' }).element;
+ input = wrapper.findComponent({ ref: 'nameInput' }).element;
linkIdToUpdate = release.assets.links[0].id;
newName = 'updated name';
});
@@ -176,7 +176,7 @@ describe('Release edit component', () => {
it('calls the "updateAssetLinkName" store method when text is entered into the "Link title" input field', () => {
expectStoreMethodNotToBeCalled();
- wrapper.find({ ref: 'nameInput' }).vm.$emit('change', newName);
+ wrapper.findComponent({ ref: 'nameInput' }).vm.$emit('change', newName);
expectStoreMethodToBeCalled();
});
@@ -208,7 +208,7 @@ describe('Release edit component', () => {
expect(actions.updateAssetLinkType).not.toHaveBeenCalled();
- wrapper.find({ ref: 'typeSelect' }).vm.$emit('change', newType);
+ wrapper.findComponent({ ref: 'typeSelect' }).vm.$emit('change', newType);
expect(actions.updateAssetLinkType).toHaveBeenCalledTimes(1);
expect(actions.updateAssetLinkType).toHaveBeenCalledWith(expect.anything(), {
@@ -225,7 +225,7 @@ describe('Release edit component', () => {
});
it('selects the default asset type', () => {
- const selected = wrapper.find({ ref: 'typeSelect' }).element.value;
+ const selected = wrapper.findComponent({ ref: 'typeSelect' }).element.value;
expect(selected).toBe(DEFAULT_ASSET_LINK_TYPE);
});
diff --git a/spec/frontend/releases/components/evidence_block_spec.js b/spec/frontend/releases/components/evidence_block_spec.js
index f0d02884305..2db1e9e38a2 100644
--- a/spec/frontend/releases/components/evidence_block_spec.js
+++ b/spec/frontend/releases/components/evidence_block_spec.js
@@ -32,19 +32,19 @@ describe('Evidence Block', () => {
});
it('renders the evidence icon', () => {
- expect(wrapper.find(GlIcon).props('name')).toBe('review-list');
+ expect(wrapper.findComponent(GlIcon).props('name')).toBe('review-list');
});
it('renders the title for the dowload link', () => {
- expect(wrapper.find(GlLink).text()).toBe(`v1.1-evidences-1.json`);
+ expect(wrapper.findComponent(GlLink).text()).toBe(`v1.1-evidences-1.json`);
});
it('renders the correct hover text for the download', () => {
- expect(wrapper.find(GlLink).attributes('title')).toBe('Download evidence JSON');
+ expect(wrapper.findComponent(GlLink).attributes('title')).toBe('Download evidence JSON');
});
it('renders the correct file link for download', () => {
- expect(wrapper.find(GlLink).attributes().download).toBe(`v1.1-evidences-1.json`);
+ expect(wrapper.findComponent(GlLink).attributes().download).toBe(`v1.1-evidences-1.json`);
});
describe('sha text', () => {
@@ -62,15 +62,15 @@ describe('Evidence Block', () => {
describe('copy to clipboard button', () => {
it('renders button', () => {
- expect(wrapper.find(ClipboardButton).exists()).toBe(true);
+ expect(wrapper.findComponent(ClipboardButton).exists()).toBe(true);
});
it('renders the correct hover text', () => {
- expect(wrapper.find(ClipboardButton).attributes('title')).toBe('Copy evidence SHA');
+ expect(wrapper.findComponent(ClipboardButton).attributes('title')).toBe('Copy evidence SHA');
});
it('copies the sha', () => {
- expect(wrapper.find(ClipboardButton).attributes('data-clipboard-text')).toBe(
+ expect(wrapper.findComponent(ClipboardButton).attributes('data-clipboard-text')).toBe(
release.evidences[0].sha,
);
});
diff --git a/spec/frontend/releases/components/issuable_stats_spec.js b/spec/frontend/releases/components/issuable_stats_spec.js
index 8fc0779da14..3ac75e138ee 100644
--- a/spec/frontend/releases/components/issuable_stats_spec.js
+++ b/spec/frontend/releases/components/issuable_stats_spec.js
@@ -16,9 +16,11 @@ describe('~/releases/components/issuable_stats.vue', () => {
});
};
- const findOpenStatLink = () => wrapper.find('[data-testid="open-stat"]').find(GlLink);
- const findMergedStatLink = () => wrapper.find('[data-testid="merged-stat"]').find(GlLink);
- const findClosedStatLink = () => wrapper.find('[data-testid="closed-stat"]').find(GlLink);
+ const findOpenStatLink = () => wrapper.find('[data-testid="open-stat"]').findComponent(GlLink);
+ const findMergedStatLink = () =>
+ wrapper.find('[data-testid="merged-stat"]').findComponent(GlLink);
+ const findClosedStatLink = () =>
+ wrapper.find('[data-testid="closed-stat"]').findComponent(GlLink);
beforeEach(() => {
defaultProps = {
diff --git a/spec/frontend/releases/components/release_block_assets_spec.js b/spec/frontend/releases/components/release_block_assets_spec.js
index c63689e11ac..4f94e4dfd55 100644
--- a/spec/frontend/releases/components/release_block_assets_spec.js
+++ b/spec/frontend/releases/components/release_block_assets_spec.js
@@ -44,7 +44,7 @@ describe('Release block assets', () => {
});
it('renders the accordion as expanded by default', () => {
- const accordion = wrapper.find(GlCollapse);
+ const accordion = wrapper.findComponent(GlCollapse);
expect(accordion.exists()).toBe(true);
expect(accordion.isVisible()).toBe(true);
diff --git a/spec/frontend/releases/components/release_block_footer_spec.js b/spec/frontend/releases/components/release_block_footer_spec.js
index 848e802df4b..8f4efad197f 100644
--- a/spec/frontend/releases/components/release_block_footer_spec.js
+++ b/spec/frontend/releases/components/release_block_footer_spec.js
@@ -38,16 +38,16 @@ describe('Release block footer', () => {
});
const commitInfoSection = () => wrapper.find('.js-commit-info');
- const commitInfoSectionLink = () => commitInfoSection().find(GlLink);
+ const commitInfoSectionLink = () => commitInfoSection().findComponent(GlLink);
const tagInfoSection = () => wrapper.find('.js-tag-info');
- const tagInfoSectionLink = () => tagInfoSection().find(GlLink);
+ 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().find(GlIcon);
+ const commitIcon = commitInfoSection().findComponent(GlIcon);
expect(commitIcon.exists()).toBe(true);
expect(commitIcon.props('name')).toBe('commit');
@@ -62,14 +62,14 @@ describe('Release block footer', () => {
});
it('renders the tag icon', () => {
- const commitIcon = tagInfoSection().find(GlIcon);
+ 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().find(GlLink);
+ const commitLink = tagInfoSection().findComponent(GlLink);
expect(commitLink.exists()).toBe(true);
expect(commitLink.text()).toBe(release.tagName);
@@ -120,7 +120,7 @@ describe('Release block footer', () => {
});
it("renders a link to the author's profile", () => {
- const authorLink = authorDateInfoSection().find(GlLink);
+ const authorLink = authorDateInfoSection().findComponent(GlLink);
expect(authorLink.exists()).toBe(true);
expect(authorLink.attributes('href')).toBe(release.author.webUrl);
diff --git a/spec/frontend/releases/components/release_block_header_spec.js b/spec/frontend/releases/components/release_block_header_spec.js
index c9921185bad..fc421776d60 100644
--- a/spec/frontend/releases/components/release_block_header_spec.js
+++ b/spec/frontend/releases/components/release_block_header_spec.js
@@ -30,7 +30,7 @@ describe('Release block header', () => {
});
const findHeader = () => wrapper.find('h2');
- const findHeaderLink = () => findHeader().find(GlLink);
+ const findHeaderLink = () => findHeader().findComponent(GlLink);
const findEditButton = () => wrapper.find('.js-edit-button');
const findBadge = () => wrapper.findComponent(GlBadge);
diff --git a/spec/frontend/releases/components/release_block_milestone_info_spec.js b/spec/frontend/releases/components/release_block_milestone_info_spec.js
index 84a0080965b..541d487091c 100644
--- a/spec/frontend/releases/components/release_block_milestone_info_spec.js
+++ b/spec/frontend/releases/components/release_block_milestone_info_spec.js
@@ -43,7 +43,7 @@ describe('Release block milestone info', () => {
});
it('renders a progress bar that displays the correct percentage', () => {
- const progressBar = milestoneProgressBarContainer().find(GlProgressBar);
+ const progressBar = milestoneProgressBarContainer().findComponent(GlProgressBar);
expect(progressBar.exists()).toBe(true);
expect(progressBar.attributes()).toEqual(
@@ -58,7 +58,7 @@ describe('Release block milestone info', () => {
expect(milestoneListContainer().text()).toMatchInterpolatedText('Milestones 12.3 • 12.4');
milestones.forEach((m, i) => {
- const milestoneLink = milestoneListContainer().findAll(GlLink).at(i);
+ const milestoneLink = milestoneListContainer().findAllComponents(GlLink).at(i);
expect(milestoneLink.text()).toBe(m.title);
expect(milestoneLink.attributes('href')).toBe(m.webUrl);
@@ -72,7 +72,7 @@ describe('Release block milestone info', () => {
expect(issuesContainerText).toContain(`Issues ${totalIssueCount}`);
- const badge = issuesContainer().find(GlBadge);
+ const badge = issuesContainer().findComponent(GlBadge);
expect(badge.text()).toBe(totalIssueCount.toString());
expect(issuesContainerText).toContain('Open: 5 • Closed: 4');
@@ -107,7 +107,7 @@ describe('Release block milestone info', () => {
});
const clickShowMoreFewerButton = async () => {
- milestoneListContainer().find(GlButton).trigger('click');
+ milestoneListContainer().findComponent(GlButton).trigger('click');
await nextTick();
};
diff --git a/spec/frontend/releases/components/release_block_spec.js b/spec/frontend/releases/components/release_block_spec.js
index 17e2af687a6..096c3db8902 100644
--- a/spec/frontend/releases/components/release_block_spec.js
+++ b/spec/frontend/releases/components/release_block_spec.js
@@ -74,7 +74,7 @@ describe('Release block', () => {
});
it('renders the footer', () => {
- expect(wrapper.find(ReleaseBlockFooter).exists()).toBe(true);
+ expect(wrapper.findComponent(ReleaseBlockFooter).exists()).toBe(true);
});
});
@@ -133,7 +133,7 @@ describe('Release block', () => {
describe('evidence block', () => {
it('renders the evidence block when the evidence is available', () => {
return factory(release).then(() => {
- expect(wrapper.find(EvidenceBlock).exists()).toBe(true);
+ expect(wrapper.findComponent(EvidenceBlock).exists()).toBe(true);
});
});
@@ -141,7 +141,7 @@ describe('Release block', () => {
release.evidences = [];
return factory(release).then(() => {
- expect(wrapper.find(EvidenceBlock).exists()).toBe(false);
+ expect(wrapper.findComponent(EvidenceBlock).exists()).toBe(false);
});
});
});
diff --git a/spec/frontend/releases/components/release_skeleton_loader_spec.js b/spec/frontend/releases/components/release_skeleton_loader_spec.js
index 7f81081ff6c..76dfe0d9777 100644
--- a/spec/frontend/releases/components/release_skeleton_loader_spec.js
+++ b/spec/frontend/releases/components/release_skeleton_loader_spec.js
@@ -10,6 +10,6 @@ describe('release_skeleton_loader.vue', () => {
});
it('renders a GlSkeletonLoader', () => {
- expect(wrapper.find(GlSkeletonLoader).exists()).toBe(true);
+ expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(true);
});
});
diff --git a/spec/frontend/releases/components/tag_field_exsting_spec.js b/spec/frontend/releases/components/tag_field_exsting_spec.js
index f45a28392b7..8105aa4f6f2 100644
--- a/spec/frontend/releases/components/tag_field_exsting_spec.js
+++ b/spec/frontend/releases/components/tag_field_exsting_spec.js
@@ -20,7 +20,7 @@ describe('releases/components/tag_field_existing', () => {
});
};
- const findInput = () => wrapper.find(GlFormInput);
+ const findInput = () => wrapper.findComponent(GlFormInput);
const findHelp = () => wrapper.find('[data-testid="tag-name-help"]');
beforeEach(() => {
diff --git a/spec/frontend/releases/components/tag_field_new_spec.js b/spec/frontend/releases/components/tag_field_new_spec.js
index 9f500c318ea..b8047cae8c2 100644
--- a/spec/frontend/releases/components/tag_field_new_spec.js
+++ b/spec/frontend/releases/components/tag_field_new_spec.js
@@ -79,12 +79,12 @@ describe('releases/components/tag_field_new', () => {
});
const findTagNameFormGroup = () => wrapper.find('[data-testid="tag-name-field"]');
- const findTagNameDropdown = () => findTagNameFormGroup().find(RefSelectorStub);
+ const findTagNameDropdown = () => findTagNameFormGroup().findComponent(RefSelectorStub);
const findCreateFromFormGroup = () => wrapper.find('[data-testid="create-from-field"]');
- const findCreateFromDropdown = () => findCreateFromFormGroup().find(RefSelectorStub);
+ const findCreateFromDropdown = () => findCreateFromFormGroup().findComponent(RefSelectorStub);
- const findCreateNewTagOption = () => wrapper.find(GlDropdownItem);
+ const findCreateNewTagOption = () => wrapper.findComponent(GlDropdownItem);
describe('"Tag name" field', () => {
describe('rendering and behavior', () => {
diff --git a/spec/frontend/releases/components/tag_field_spec.js b/spec/frontend/releases/components/tag_field_spec.js
index e7b9aa4abbb..85a40f02c53 100644
--- a/spec/frontend/releases/components/tag_field_spec.js
+++ b/spec/frontend/releases/components/tag_field_spec.js
@@ -21,8 +21,8 @@ describe('releases/components/tag_field', () => {
wrapper = shallowMount(TagField, { store });
};
- const findTagFieldNew = () => wrapper.find(TagFieldNew);
- const findTagFieldExisting = () => wrapper.find(TagFieldExisting);
+ const findTagFieldNew = () => wrapper.findComponent(TagFieldNew);
+ const findTagFieldExisting = () => wrapper.findComponent(TagFieldExisting);
afterEach(() => {
wrapper.destroy();
diff --git a/spec/frontend/releases/stores/modules/detail/actions_spec.js b/spec/frontend/releases/stores/modules/detail/actions_spec.js
index ce3b690213c..48fba3adb24 100644
--- a/spec/frontend/releases/stores/modules/detail/actions_spec.js
+++ b/spec/frontend/releases/stores/modules/detail/actions_spec.js
@@ -352,6 +352,32 @@ describe('Release edit/new actions', () => {
});
});
+ describe('when the GraphQL returns errors as data', () => {
+ beforeEach(() => {
+ gqClient.mutate.mockResolvedValue({ data: { releaseCreate: { errors: ['Yikes!'] } } });
+ });
+
+ it(`commits ${types.RECEIVE_SAVE_RELEASE_ERROR} with an error object`, () => {
+ return testAction(actions.createRelease, undefined, state, [
+ {
+ type: types.RECEIVE_SAVE_RELEASE_ERROR,
+ payload: expect.any(Error),
+ },
+ ]);
+ });
+
+ it(`shows a flash message`, () => {
+ return actions
+ .createRelease({ commit: jest.fn(), dispatch: jest.fn(), state, getters: {} })
+ .then(() => {
+ expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createFlash).toHaveBeenCalledWith({
+ message: 'Yikes!',
+ });
+ });
+ });
+ });
+
describe('when the GraphQL network request fails', () => {
beforeEach(() => {
gqClient.mutate.mockRejectedValue(error);
diff --git a/spec/frontend/releases/stores/modules/detail/getters_spec.js b/spec/frontend/releases/stores/modules/detail/getters_spec.js
index 4ac6eaebaa2..2982dc5c46c 100644
--- a/spec/frontend/releases/stores/modules/detail/getters_spec.js
+++ b/spec/frontend/releases/stores/modules/detail/getters_spec.js
@@ -320,7 +320,9 @@ describe('Release edit/new getters', () => {
it(description, () => {
const expectedVariablesObject = { input: expect.objectContaining(expectedVariables) };
- const actualVariables = getters.releaseUpdateMutatationVariables(state);
+ const actualVariables = getters.releaseUpdateMutatationVariables(state, {
+ releasedAtChanged: Object.hasOwn(state.release, 'releasedAt'),
+ });
expect(actualVariables).toEqual(expectedVariablesObject);
});
@@ -409,4 +411,19 @@ describe('Release edit/new getters', () => {
},
);
});
+
+ describe('releasedAtChange', () => {
+ it('is false if the released at date has not changed', () => {
+ const date = new Date();
+ expect(
+ getters.releasedAtChanged({ originalReleasedAt: date, release: { releasedAt: date } }),
+ ).toBe(false);
+ });
+
+ it('is true if the date changed', () => {
+ const originalReleasedAt = new Date();
+ const releasedAt = new Date(2022, 5, 30);
+ expect(getters.releasedAtChanged({ originalReleasedAt, release: { releasedAt } })).toBe(true);
+ });
+ });
});
diff --git a/spec/frontend/releases/stores/modules/detail/mutations_spec.js b/spec/frontend/releases/stores/modules/detail/mutations_spec.js
index 60b57c7a7ff..8bbf550b77d 100644
--- a/spec/frontend/releases/stores/modules/detail/mutations_spec.js
+++ b/spec/frontend/releases/stores/modules/detail/mutations_spec.js
@@ -36,6 +36,12 @@ describe('Release edit/new mutations', () => {
},
});
});
+
+ it('saves the original released at date as well', () => {
+ mutations[types.INITIALIZE_EMPTY_RELEASE](state);
+
+ expect(state.originalReleasedAt).toEqual(new Date());
+ });
});
describe(`${types.REQUEST_RELEASE}`, () => {
@@ -57,6 +63,7 @@ describe('Release edit/new mutations', () => {
expect(state.release).toEqual(release);
expect(state.originalRelease).toEqual(release);
+ expect(state.originalReleasedAt).toEqual(release.releasedAt);
});
});
diff --git a/spec/frontend/reports/accessibility_report/components/accessibility_issue_body_spec.js b/spec/frontend/reports/accessibility_report/components/accessibility_issue_body_spec.js
index ddabb7194cb..d835ca4c733 100644
--- a/spec/frontend/reports/accessibility_report/components/accessibility_issue_body_spec.js
+++ b/spec/frontend/reports/accessibility_report/components/accessibility_issue_body_spec.js
@@ -41,7 +41,7 @@ describe('CustomMetricsForm', () => {
});
it('Displays the issue message', () => {
- const description = wrapper.find({ ref: 'accessibility-issue-description' }).text();
+ const description = wrapper.findComponent({ ref: 'accessibility-issue-description' }).text();
expect(description).toContain(`Message: ${issue.message}`);
});
@@ -49,7 +49,7 @@ describe('CustomMetricsForm', () => {
describe('When an issue code is present', () => {
it('Creates the correct URL for learning more about the issue code', () => {
const learnMoreUrl = wrapper
- .find({ ref: 'accessibility-issue-learn-more' })
+ .findComponent({ ref: 'accessibility-issue-learn-more' })
.attributes('href');
expect(learnMoreUrl).toBe(issue.learnMoreUrl);
@@ -66,7 +66,7 @@ describe('CustomMetricsForm', () => {
it('Creates a URL leading to the overview documentation page', () => {
const learnMoreUrl = wrapper
- .find({ ref: 'accessibility-issue-learn-more' })
+ .findComponent({ ref: 'accessibility-issue-learn-more' })
.attributes('href');
expect(learnMoreUrl).toBe('https://www.w3.org/TR/WCAG20-TECHS/Overview.html');
@@ -83,7 +83,7 @@ describe('CustomMetricsForm', () => {
it('Creates a URL leading to the overview documentation page', () => {
const learnMoreUrl = wrapper
- .find({ ref: 'accessibility-issue-learn-more' })
+ .findComponent({ ref: 'accessibility-issue-learn-more' })
.attributes('href');
expect(learnMoreUrl).toBe('https://www.w3.org/TR/WCAG20-TECHS/Overview.html');
diff --git a/spec/frontend/reports/accessibility_report/grouped_accessibility_reports_app_spec.js b/spec/frontend/reports/accessibility_report/grouped_accessibility_reports_app_spec.js
index 34b1cdd92bc..9d3535291eb 100644
--- a/spec/frontend/reports/accessibility_report/grouped_accessibility_reports_app_spec.js
+++ b/spec/frontend/reports/accessibility_report/grouped_accessibility_reports_app_spec.js
@@ -114,7 +114,7 @@ describe('Grouped accessibility reports app', () => {
});
it('renders custom accessibility issue body', () => {
- const issueBody = wrapper.find(AccessibilityIssueBody);
+ const issueBody = wrapper.findComponent(AccessibilityIssueBody);
expect(issueBody.props('issue').code).toBe(mockReport.new_errors[0].code);
expect(issueBody.props('issue').message).toBe(mockReport.new_errors[0].message);
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
index 17f07ac2b8f..c32b52d9e77 100644
--- a/spec/frontend/reports/codequality_report/components/codequality_issue_body_spec.js
+++ b/spec/frontend/reports/codequality_report/components/codequality_issue_body_spec.js
@@ -8,7 +8,7 @@ describe('code quality issue body issue body', () => {
let wrapper;
const findSeverityIcon = () => wrapper.findByTestId('codequality-severity-icon');
- const findGlIcon = () => wrapper.find(GlIcon);
+ const findGlIcon = () => wrapper.findComponent(GlIcon);
const codequalityIssue = {
name:
diff --git a/spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js b/spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js
index b61b65c2713..962ff068b92 100644
--- a/spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js
+++ b/spec/frontend/reports/codequality_report/grouped_codequality_reports_app_spec.js
@@ -30,7 +30,7 @@ describe('Grouped code quality reports app', () => {
};
const findWidget = () => wrapper.find('.js-codequality-widget');
- const findIssueBody = () => wrapper.find(CodequalityIssueBody);
+ const findIssueBody = () => wrapper.findComponent(CodequalityIssueBody);
beforeEach(() => {
const { state, ...storeConfig } = getStoreConfig();
diff --git a/spec/frontend/reports/components/grouped_issues_list_spec.js b/spec/frontend/reports/components/grouped_issues_list_spec.js
index 95ef0bcbcc7..6c0275dc47d 100644
--- a/spec/frontend/reports/components/grouped_issues_list_spec.js
+++ b/spec/frontend/reports/components/grouped_issues_list_spec.js
@@ -30,7 +30,7 @@ describe('Grouped Issues List', () => {
},
});
- expect(wrapper.find(SmartVirtualList).props()).toMatchSnapshot();
+ expect(wrapper.findComponent(SmartVirtualList).props()).toMatchSnapshot();
});
describe('without data', () => {
@@ -43,7 +43,7 @@ describe('Grouped Issues List', () => {
});
it.each(['resolved', 'unresolved'])('does not render report items for %s issues', () => {
- expect(wrapper.find(ReportItem).exists()).toBe(false);
+ expect(wrapper.findComponent(ReportItem).exists()).toBe(false);
});
});
@@ -67,7 +67,7 @@ describe('Grouped Issues List', () => {
propsData: { [`${issueName}Issues`]: issues },
});
- expect(wrapper.findAll(ReportItem)).toHaveLength(issues.length);
+ expect(wrapper.findAllComponents(ReportItem)).toHaveLength(issues.length);
});
it('renders a report item with the correct props', () => {
@@ -81,7 +81,7 @@ describe('Grouped Issues List', () => {
},
});
- expect(wrapper.find(ReportItem).props()).toMatchSnapshot();
+ expect(wrapper.findComponent(ReportItem).props()).toMatchSnapshot();
});
});
});
diff --git a/spec/frontend/reports/components/report_item_spec.js b/spec/frontend/reports/components/report_item_spec.js
index a7243c5377b..b52c163eb26 100644
--- a/spec/frontend/reports/components/report_item_spec.js
+++ b/spec/frontend/reports/components/report_item_spec.js
@@ -16,7 +16,7 @@ describe('ReportItem', () => {
},
});
- expect(wrapper.find(IssueStatusIcon).exists()).toBe(false);
+ expect(wrapper.findComponent(IssueStatusIcon).exists()).toBe(false);
});
it('shows status icon when unspecified', () => {
@@ -28,7 +28,7 @@ describe('ReportItem', () => {
},
});
- expect(wrapper.find(IssueStatusIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(IssueStatusIcon).exists()).toBe(true);
});
});
});
diff --git a/spec/frontend/reports/grouped_test_report/components/modal_spec.js b/spec/frontend/reports/grouped_test_report/components/modal_spec.js
index 3de81f754fd..e8564d2428d 100644
--- a/spec/frontend/reports/grouped_test_report/components/modal_spec.js
+++ b/spec/frontend/reports/grouped_test_report/components/modal_spec.js
@@ -40,7 +40,9 @@ describe('Grouped Test Reports Modal', () => {
});
it('renders code block', () => {
- expect(wrapper.find(CodeBlock).props().code).toEqual(modalDataStructure.system_output.value);
+ expect(wrapper.findComponent(CodeBlock).props().code).toEqual(
+ modalDataStructure.system_output.value,
+ );
});
it('renders link', () => {
diff --git a/spec/frontend/reports/grouped_test_report/store/actions_spec.js b/spec/frontend/reports/grouped_test_report/store/actions_spec.js
index 5876827c548..7469c31cf84 100644
--- a/spec/frontend/reports/grouped_test_report/store/actions_spec.js
+++ b/spec/frontend/reports/grouped_test_report/store/actions_spec.js
@@ -61,7 +61,7 @@ describe('Reports Store Actions', () => {
});
describe('success', () => {
- it('dispatches requestReports and receiveReportsSuccess ', () => {
+ it('dispatches requestReports and receiveReportsSuccess', () => {
mock
.onGet(`${TEST_HOST}/endpoint.json`)
.replyOnce(200, { summary: {}, suites: [{ name: 'rspec' }] });
@@ -89,7 +89,7 @@ describe('Reports Store Actions', () => {
mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500);
});
- it('dispatches requestReports and receiveReportsError ', () => {
+ it('dispatches requestReports and receiveReportsError', () => {
return testAction(
fetchReports,
null,
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
new file mode 100644
index 00000000000..28ee7d194b9
--- /dev/null
+++ b/spec/frontend/reports/mock_data/new_failures_with_null_files_report.json
@@ -0,0 +1,40 @@
+{
+ "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/repository/components/__snapshots__/last_commit_spec.js.snap b/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap
index cb56f392ec9..01494cb6a24 100644
--- a/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap
+++ b/spec/frontend/repository/components/__snapshots__/last_commit_spec.js.snap
@@ -20,7 +20,8 @@ exports[`Repository last commit component renders commit widget 1`] = `
class="commit-detail flex-list gl-display-flex gl-justify-content-space-between gl-align-items-flex-start gl-flex-grow-1 gl-min-w-0"
>
<div
- class="commit-content qa-commit-content"
+ class="commit-content"
+ data-qa-selector="commit_content"
>
<gl-link-stub
class="commit-row-message item-title"
diff --git a/spec/frontend/repository/components/blob_button_group_spec.js b/spec/frontend/repository/components/blob_button_group_spec.js
index d5b882bd715..33a85c04fcf 100644
--- a/spec/frontend/repository/components/blob_button_group_spec.js
+++ b/spec/frontend/repository/components/blob_button_group_spec.js
@@ -66,12 +66,12 @@ describe('BlobButtonGroup component', () => {
});
it('renders both the replace and delete button', () => {
- expect(wrapper.findAll(GlButton)).toHaveLength(2);
+ expect(wrapper.findAllComponents(GlButton)).toHaveLength(2);
});
it('renders the buttons in the correct order', () => {
- expect(wrapper.findAll(GlButton).at(0).text()).toBe('Replace');
- expect(wrapper.findAll(GlButton).at(1).text()).toBe('Delete');
+ expect(wrapper.findAllComponents(GlButton).at(0).text()).toBe('Replace');
+ expect(wrapper.findAllComponents(GlButton).at(1).text()).toBe('Delete');
});
it('triggers the UploadBlobModal from the replace button', () => {
@@ -97,14 +97,14 @@ describe('BlobButtonGroup component', () => {
findReplaceButton().trigger('click');
expect(findUploadBlobModal().vm.show).not.toHaveBeenCalled();
- expect(wrapper.emitted().fork).toBeTruthy();
+ expect(wrapper.emitted().fork).toHaveLength(1);
});
it('does not trigger the DeleteBlobModal from the delete button', () => {
findDeleteButton().trigger('click');
expect(findDeleteBlobModal().vm.show).not.toHaveBeenCalled();
- expect(wrapper.emitted().fork).toBeTruthy();
+ expect(wrapper.emitted().fork).toHaveLength(1);
});
});
});
diff --git a/spec/frontend/repository/components/blob_content_viewer_spec.js b/spec/frontend/repository/components/blob_content_viewer_spec.js
index 0f7cf4e61b2..6ece72c41bb 100644
--- a/spec/frontend/repository/components/blob_content_viewer_spec.js
+++ b/spec/frontend/repository/components/blob_content_viewer_spec.js
@@ -17,7 +17,8 @@ import { loadViewer } from '~/repository/components/blob_viewers';
import DownloadViewer from '~/repository/components/blob_viewers/download_viewer.vue';
import EmptyViewer from '~/repository/components/blob_viewers/empty_viewer.vue';
import SourceViewer from '~/vue_shared/components/source_viewer/source_viewer.vue';
-import blobInfoQuery from '~/repository/queries/blob_info.query.graphql';
+import blobInfoQuery from 'shared_queries/repository/blob_info.query.graphql';
+import projectInfoQuery from '~/repository/queries/project_info.query.graphql';
import userInfoQuery from '~/repository/queries/user_info.query.graphql';
import applicationInfoQuery from '~/repository/queries/application_info.query.graphql';
import CodeIntelligence from '~/code_navigation/components/app.vue';
@@ -45,8 +46,9 @@ jest.mock('~/lib/utils/common_utils');
jest.mock('~/blob/line_highlighter');
let wrapper;
-let mockResolver;
+let blobInfoMockResolver;
let userInfoMockResolver;
+let projectInfoMockResolver;
let applicationInfoMockResolver;
const mockAxios = new MockAdapter(axios);
@@ -74,22 +76,40 @@ const createComponent = async (mockData = {}, mountFn = shallowMount, mockRoute
highlightJs = true,
} = mockData;
- const project = {
+ const blobInfo = {
...projectMock,
+ repository: {
+ empty,
+ blobs: { nodes: [blob] },
+ },
+ };
+
+ const projectInfo = {
+ __typename: 'Project',
+ id: '123',
userPermissions: {
pushCode,
forkProject,
downloadCode,
createMergeRequestIn,
},
- repository: {
- empty,
- blobs: { nodes: [blob] },
+ pathLocks: {
+ nodes: [
+ {
+ id: 'test',
+ path: 'locked_file.js',
+ user: { id: '123', username: 'root' },
+ },
+ ],
},
};
- mockResolver = jest.fn().mockResolvedValue({
- data: { isBinary, project },
+ projectInfoMockResolver = jest.fn().mockResolvedValue({
+ data: { project: projectInfo },
+ });
+
+ blobInfoMockResolver = jest.fn().mockResolvedValue({
+ data: { isBinary, project: blobInfo },
});
userInfoMockResolver = jest.fn().mockResolvedValue({
@@ -101,8 +121,9 @@ const createComponent = async (mockData = {}, mountFn = shallowMount, mockRoute
});
const fakeApollo = createMockApollo([
- [blobInfoQuery, mockResolver],
+ [blobInfoQuery, blobInfoMockResolver],
[userInfoQuery, userInfoMockResolver],
+ [projectInfoQuery, projectInfoMockResolver],
[applicationInfoQuery, applicationInfoMockResolver],
]);
@@ -129,7 +150,7 @@ const createComponent = async (mockData = {}, mountFn = shallowMount, mockRoute
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax
- wrapper.setData({ project, isBinary });
+ wrapper.setData({ project: blobInfo, isBinary });
await waitForPromises();
};
@@ -504,14 +525,16 @@ describe('Blob content viewer component', () => {
async ({ highlightJs, shouldFetchRawText }) => {
await createComponent({ highlightJs });
- expect(mockResolver).toHaveBeenCalledWith(expect.objectContaining({ shouldFetchRawText }));
+ expect(blobInfoMockResolver).toHaveBeenCalledWith(
+ expect.objectContaining({ shouldFetchRawText }),
+ );
},
);
it('is called with originalBranch value if the prop has a value', async () => {
await createComponent({ inject: { originalBranch: 'some-branch' } });
- expect(mockResolver).toHaveBeenCalledWith(
+ expect(blobInfoMockResolver).toHaveBeenCalledWith(
expect.objectContaining({
ref: 'some-branch',
}),
@@ -521,7 +544,7 @@ describe('Blob content viewer component', () => {
it('is called with ref value if the originalBranch prop has no value', async () => {
await createComponent();
- expect(mockResolver).toHaveBeenCalledWith(
+ expect(blobInfoMockResolver).toHaveBeenCalledWith(
expect.objectContaining({
ref: 'default-ref',
}),
diff --git a/spec/frontend/repository/components/blob_viewers/csv_viewer_spec.js b/spec/frontend/repository/components/blob_viewers/csv_viewer_spec.js
index 7d43e4e660b..c6b9737dde2 100644
--- a/spec/frontend/repository/components/blob_viewers/csv_viewer_spec.js
+++ b/spec/frontend/repository/components/blob_viewers/csv_viewer_spec.js
@@ -21,7 +21,7 @@ describe('CSV Viewer', () => {
it('renders a Source Editor component', () => {
createComponent();
expect(findCsvViewerComp().exists()).toBe(true);
- expect(findCsvViewerComp().props('remoteFile')).toBeTruthy();
+ expect(findCsvViewerComp().props('remoteFile')).toBe(true);
expect(findCsvViewerComp().props('csv')).toBe(DEFAULT_BLOB_DATA.rawPath);
});
});
diff --git a/spec/frontend/repository/components/breadcrumbs_spec.js b/spec/frontend/repository/components/breadcrumbs_spec.js
index 40b32904589..c2f34f79f89 100644
--- a/spec/frontend/repository/components/breadcrumbs_spec.js
+++ b/spec/frontend/repository/components/breadcrumbs_spec.js
@@ -39,8 +39,8 @@ describe('Repository breadcrumbs component', () => {
});
};
- const findUploadBlobModal = () => wrapper.find(UploadBlobModal);
- const findNewDirectoryModal = () => wrapper.find(NewDirectoryModal);
+ const findUploadBlobModal = () => wrapper.findComponent(UploadBlobModal);
+ const findNewDirectoryModal = () => wrapper.findComponent(NewDirectoryModal);
afterEach(() => {
wrapper.destroy();
@@ -55,7 +55,7 @@ describe('Repository breadcrumbs component', () => {
`('renders $linkCount links for path $path', ({ path, linkCount }) => {
factory(path);
- expect(wrapper.findAll(RouterLinkStub).length).toEqual(linkCount);
+ expect(wrapper.findAllComponents(RouterLinkStub).length).toEqual(linkCount);
});
it.each`
@@ -68,14 +68,14 @@ describe('Repository breadcrumbs component', () => {
'links to the correct router path when routeName is $routeName',
({ routeName, path, linkTo }) => {
factory(path, {}, { name: routeName });
- expect(wrapper.findAll(RouterLinkStub).at(3).props('to')).toEqual(linkTo);
+ expect(wrapper.findAllComponents(RouterLinkStub).at(3).props('to')).toEqual(linkTo);
},
);
it('escapes hash in directory path', () => {
factory('app/assets/javascripts#');
- expect(wrapper.findAll(RouterLinkStub).at(3).props('to')).toEqual(
+ expect(wrapper.findAllComponents(RouterLinkStub).at(3).props('to')).toEqual(
'/-/tree/app/assets/javascripts%23',
);
});
@@ -83,7 +83,9 @@ describe('Repository breadcrumbs component', () => {
it('renders last link as active', () => {
factory('app/assets');
- expect(wrapper.findAll(RouterLinkStub).at(2).attributes('aria-current')).toEqual('page');
+ expect(wrapper.findAllComponents(RouterLinkStub).at(2).attributes('aria-current')).toEqual(
+ 'page',
+ );
});
it('does not render add to tree dropdown when permissions are false', async () => {
@@ -95,7 +97,7 @@ describe('Repository breadcrumbs component', () => {
await nextTick();
- expect(wrapper.find(GlDropdown).exists()).toBe(false);
+ expect(wrapper.findComponent(GlDropdown).exists()).toBe(false);
});
it.each`
@@ -109,7 +111,7 @@ describe('Repository breadcrumbs component', () => {
'does render add to tree dropdown $isRendered when route is $routeName',
({ routeName, isRendered }) => {
factory('app/assets/javascripts.js', { canCollaborate: true }, { name: routeName });
- expect(wrapper.find(GlDropdown).exists()).toBe(isRendered);
+ expect(wrapper.findComponent(GlDropdown).exists()).toBe(isRendered);
},
);
@@ -122,7 +124,7 @@ describe('Repository breadcrumbs component', () => {
await nextTick();
- expect(wrapper.find(GlDropdown).exists()).toBe(true);
+ expect(wrapper.findComponent(GlDropdown).exists()).toBe(true);
});
describe('renders the upload blob modal', () => {
diff --git a/spec/frontend/repository/components/delete_blob_modal_spec.js b/spec/frontend/repository/components/delete_blob_modal_spec.js
index 785783b2e75..b5996816ad8 100644
--- a/spec/frontend/repository/components/delete_blob_modal_spec.js
+++ b/spec/frontend/repository/components/delete_blob_modal_spec.js
@@ -84,7 +84,7 @@ describe('DeleteBlobModal', () => {
${GlToggle} | ${'true'} | ${true} | ${initialProps.targetBranch} | ${initialProps.originalBranch} | ${true}
${GlToggle} | ${undefined} | ${true} | ${'same-branch'} | ${'same-branch'} | ${false}
`(
- 'has the correct form fields ',
+ 'has the correct form fields',
({ component, defaultValue, canPushCode, targetBranch, originalBranch, exist }) => {
createComponent({
canPushCode,
diff --git a/spec/frontend/repository/components/last_commit_spec.js b/spec/frontend/repository/components/last_commit_spec.js
index 3783b34e33a..bf9528953b6 100644
--- a/spec/frontend/repository/components/last_commit_spec.js
+++ b/spec/frontend/repository/components/last_commit_spec.js
@@ -190,11 +190,16 @@ describe('Repository last commit component', () => {
});
it('expands commit description when clicking expander', async () => {
+ expect(findCommitRowDescription().classes('d-block')).toBe(false);
+ expect(findTextExpander().classes('open')).toBe(false);
+ expect(findTextExpander().props('selected')).toBe(false);
+
findTextExpander().vm.$emit('click');
await nextTick();
- expect(findCommitRowDescription().isVisible()).toBe(true);
- expect(findTextExpander().classes()).toContain('open');
+ expect(findCommitRowDescription().classes('d-block')).toBe(true);
+ expect(findTextExpander().classes('open')).toBe(true);
+ expect(findTextExpander().props('selected')).toBe(true);
});
});
diff --git a/spec/frontend/repository/components/new_directory_modal_spec.js b/spec/frontend/repository/components/new_directory_modal_spec.js
index e1c50d63851..aaf751a9a8d 100644
--- a/spec/frontend/repository/components/new_directory_modal_spec.js
+++ b/spec/frontend/repository/components/new_directory_modal_spec.js
@@ -107,7 +107,7 @@ describe('NewDirectoryModal', () => {
${findMrToggle} | ${'true'} | ${true} | ${'new-target-branch'} | ${'master'} | ${true}
${findMrToggle} | ${'true'} | ${true} | ${'master'} | ${'master'} | ${true}
`(
- 'has the correct form fields ',
+ 'has the correct form fields',
({ component, defaultValue, canPushCode, targetBranch, originalBranch, exist }) => {
createComponent({
canPushCode,
diff --git a/spec/frontend/repository/components/preview/index_spec.js b/spec/frontend/repository/components/preview/index_spec.js
index 0d9bfc62ed5..e4eba65795e 100644
--- a/spec/frontend/repository/components/preview/index_spec.js
+++ b/spec/frontend/repository/components/preview/index_spec.js
@@ -68,6 +68,6 @@ describe('Repository file preview component', () => {
vm.setData({ loading: 1 });
await nextTick();
- expect(vm.find(GlLoadingIcon).exists()).toBe(true);
+ expect(vm.findComponent(GlLoadingIcon).exists()).toBe(true);
});
});
diff --git a/spec/frontend/repository/components/table/index_spec.js b/spec/frontend/repository/components/table/index_spec.js
index ff0371b5c07..697d2dcc7f5 100644
--- a/spec/frontend/repository/components/table/index_spec.js
+++ b/spec/frontend/repository/components/table/index_spec.js
@@ -38,6 +38,7 @@ const MOCK_BLOBS = [
const MOCK_COMMITS = [
{
fileName: 'blob.md',
+ filePath: 'test_dir/blob.md',
type: 'blob',
commit: {
message: 'Updated blob.md',
@@ -45,6 +46,7 @@ const MOCK_COMMITS = [
},
{
fileName: 'blob2.md',
+ filePath: 'test_dir/blob2.md',
type: 'blob',
commit: {
message: 'Updated blob2.md',
@@ -52,11 +54,20 @@ const MOCK_COMMITS = [
},
{
fileName: 'blob3.md',
+ filePath: 'test_dir/blob3.md',
type: 'blob',
commit: {
message: 'Updated blob3.md',
},
},
+ {
+ fileName: 'root_blob.md',
+ filePath: '/root_blob.md',
+ type: 'blob',
+ commit: {
+ message: 'Updated root_blob.md',
+ },
+ },
];
function factory({ path, isLoading = false, hasMore = true, entries = {}, commits = [] }) {
@@ -77,6 +88,8 @@ function factory({ path, isLoading = false, hasMore = true, entries = {}, commit
});
}
+const findTableRows = () => vm.findAllComponents(TableRow);
+
describe('Repository table component', () => {
afterEach(() => {
vm.destroy();
@@ -108,14 +121,14 @@ describe('Repository table component', () => {
it('renders table rows', () => {
factory({
- path: '/',
+ path: 'test_dir',
entries: {
blobs: MOCK_BLOBS,
},
commits: MOCK_COMMITS,
});
- const rows = vm.findAll(TableRow);
+ const rows = findTableRows();
expect(rows.length).toEqual(3);
expect(rows.at(2).attributes().mode).toEqual('120000');
@@ -123,6 +136,28 @@ describe('Repository table component', () => {
expect(rows.at(2).props().commitInfo).toEqual(MOCK_COMMITS[2]);
});
+ it('renders correct commit info for blobs in the root', () => {
+ factory({
+ path: '/',
+ entries: {
+ blobs: [
+ {
+ id: '126abc',
+ sha: '126abc',
+ flatPath: 'root_blob.md',
+ name: 'root_blob.md',
+ type: 'blob',
+ webUrl: 'http://test.com',
+ mode: '120000',
+ },
+ ],
+ },
+ commits: MOCK_COMMITS,
+ });
+
+ expect(findTableRows().at(0).props().commitInfo).toEqual(MOCK_COMMITS[3]);
+ });
+
describe('Show more button', () => {
const showMoreButton = () => vm.find(GlButton);
diff --git a/spec/frontend/repository/components/upload_blob_modal_spec.js b/spec/frontend/repository/components/upload_blob_modal_spec.js
index bf024baa627..505ff7f3dd6 100644
--- a/spec/frontend/repository/components/upload_blob_modal_spec.js
+++ b/spec/frontend/repository/components/upload_blob_modal_spec.js
@@ -217,7 +217,7 @@ describe('UploadBlobModal', () => {
createComponent();
});
- it('displays the default "Upload new file" modal title ', () => {
+ it('displays the default "Upload new file" modal title', () => {
expect(findModal().props('title')).toBe('Upload new file');
});
diff --git a/spec/frontend/repository/log_tree_spec.js b/spec/frontend/repository/log_tree_spec.js
index e3b4dcb8acc..c1309539b6d 100644
--- a/spec/frontend/repository/log_tree_spec.js
+++ b/spec/frontend/repository/log_tree_spec.js
@@ -30,7 +30,7 @@ describe('resolveCommit', () => {
{ fileName: 'index.js', filePath: '/app/assets/index.js' },
];
- resolveCommit(commits, '', resolver);
+ resolveCommit(commits, '/', resolver);
expect(resolver.resolve).toHaveBeenCalledWith({
fileName: 'index.js',
@@ -107,14 +107,14 @@ describe('fetchLogsTree', () => {
}));
it('calls entry resolver', () =>
- fetchLogsTree(client, '', '0', resolver).then(() => {
+ fetchLogsTree(client, 'test', '0', resolver).then(() => {
expect(resolver.resolve).toHaveBeenCalledWith(
expect.objectContaining({
__typename: 'LogTreeCommit',
commitPath: 'https://test.com',
committedDate: '2019-01-01',
fileName: 'index.js',
- filePath: '/index.js',
+ filePath: 'test/index.js',
message: 'testing message',
sha: '123',
}),
@@ -122,7 +122,7 @@ describe('fetchLogsTree', () => {
}));
it('writes query to client', async () => {
- await fetchLogsTree(client, '', '0', resolver);
+ await fetchLogsTree(client, '/', '0', resolver);
expect(client.readQuery({ query: commitsQuery })).toEqual({
commits: [
expect.objectContaining({
diff --git a/spec/frontend/repository/mock_data.js b/spec/frontend/repository/mock_data.js
index 4db295fe0b7..cda47a5b0a5 100644
--- a/spec/frontend/repository/mock_data.js
+++ b/spec/frontend/repository/mock_data.js
@@ -1,4 +1,5 @@
export const simpleViewerMock = {
+ __typename: 'RepositoryBlob',
id: '1',
name: 'some_file.js',
size: 123,
diff --git a/spec/frontend/repository/utils/commit_spec.js b/spec/frontend/repository/utils/commit_spec.js
index b3dd5118308..65728e9cb24 100644
--- a/spec/frontend/repository/utils/commit_spec.js
+++ b/spec/frontend/repository/utils/commit_spec.js
@@ -15,7 +15,7 @@ const mockData = [
describe('normalizeData', () => {
it('normalizes data into LogTreeCommit object', () => {
- expect(normalizeData(mockData, '')).toEqual([
+ expect(normalizeData(mockData, '/')).toEqual([
{
sha: '123',
message: 'testing message',
diff --git a/spec/frontend/runner/admin_runner_edit/admin_runner_edit_app_spec.js b/spec/frontend/runner/admin_runner_edit/admin_runner_edit_app_spec.js
deleted file mode 100644
index ffe3599ac64..00000000000
--- a/spec/frontend/runner/admin_runner_edit/admin_runner_edit_app_spec.js
+++ /dev/null
@@ -1,113 +0,0 @@
-import { mount, 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 { createAlert } from '~/flash';
-
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import RunnerHeader from '~/runner/components/runner_header.vue';
-import RunnerUpdateForm from '~/runner/components/runner_update_form.vue';
-import runnerFormQuery from '~/runner/graphql/edit/runner_form.query.graphql';
-import AdminRunnerEditApp from '~//runner/admin_runner_edit/admin_runner_edit_app.vue';
-import { captureException } from '~/runner/sentry_utils';
-
-import { runnerFormData } from '../mock_data';
-
-jest.mock('~/flash');
-jest.mock('~/runner/sentry_utils');
-
-const mockRunner = runnerFormData.data.runner;
-const mockRunnerGraphqlId = mockRunner.id;
-const mockRunnerId = `${getIdFromGraphQLId(mockRunnerGraphqlId)}`;
-const mockRunnerPath = `/admin/runners/${mockRunnerId}`;
-
-Vue.use(VueApollo);
-
-describe('AdminRunnerEditApp', () => {
- let wrapper;
- let mockRunnerQuery;
-
- const findRunnerHeader = () => wrapper.findComponent(RunnerHeader);
- const findRunnerUpdateForm = () => wrapper.findComponent(RunnerUpdateForm);
-
- const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => {
- wrapper = mountFn(AdminRunnerEditApp, {
- apolloProvider: createMockApollo([[runnerFormQuery, mockRunnerQuery]]),
- propsData: {
- runnerId: mockRunnerId,
- runnerPath: mockRunnerPath,
- ...props,
- },
- });
-
- return waitForPromises();
- };
-
- beforeEach(() => {
- mockRunnerQuery = jest.fn().mockResolvedValue(runnerFormData);
- });
-
- afterEach(() => {
- mockRunnerQuery.mockReset();
- wrapper.destroy();
- });
-
- it('expect GraphQL ID to be requested', async () => {
- await createComponentWithApollo();
-
- expect(mockRunnerQuery).toHaveBeenCalledWith({ id: mockRunnerGraphqlId });
- });
-
- it('displays the runner id and creation date', async () => {
- await createComponentWithApollo({ mountFn: mount });
-
- expect(findRunnerHeader().text()).toContain(`Runner #${mockRunnerId}`);
- expect(findRunnerHeader().text()).toContain('created');
- });
-
- it('displays the runner type and status', async () => {
- await createComponentWithApollo({ mountFn: mount });
-
- expect(findRunnerHeader().text()).toContain(`never contacted`);
- expect(findRunnerHeader().text()).toContain(`shared`);
- });
-
- it('displays a loading runner form', () => {
- createComponentWithApollo();
-
- expect(findRunnerUpdateForm().props()).toMatchObject({
- runner: null,
- loading: true,
- runnerPath: mockRunnerPath,
- });
- });
-
- it('displays the runner form', async () => {
- await createComponentWithApollo();
-
- expect(findRunnerUpdateForm().props()).toMatchObject({
- loading: false,
- runnerPath: mockRunnerPath,
- });
- expect(findRunnerUpdateForm().props('runner')).toEqual(mockRunner);
- });
-
- describe('When there is an error', () => {
- beforeEach(async () => {
- mockRunnerQuery = jest.fn().mockRejectedValueOnce(new Error('Error!'));
- await createComponentWithApollo();
- });
-
- it('error is reported to sentry', () => {
- expect(captureException).toHaveBeenCalledWith({
- error: new Error('Error!'),
- component: 'AdminRunnerEditApp',
- });
- });
-
- it('error is shown to the user', () => {
- expect(createAlert).toHaveBeenCalled();
- });
- });
-});
diff --git a/spec/frontend/runner/admin_runner_show/admin_runner_show_app_spec.js b/spec/frontend/runner/admin_runner_show/admin_runner_show_app_spec.js
index 509681c5a77..7ab4aeee9bc 100644
--- a/spec/frontend/runner/admin_runner_show/admin_runner_show_app_spec.js
+++ b/spec/frontend/runner/admin_runner_show/admin_runner_show_app_spec.js
@@ -164,7 +164,7 @@ describe('AdminRunnerShowApp', () => {
});
});
- describe('when runner does not have an edit url ', () => {
+ describe('when runner does not have an edit url', () => {
beforeEach(async () => {
mockRunnerQueryResult({
editAdminUrl: null,
diff --git a/spec/frontend/runner/admin_runners/admin_runners_app_spec.js b/spec/frontend/runner/admin_runners/admin_runners_app_spec.js
index 97341be7d5d..55a298e1695 100644
--- a/spec/frontend/runner/admin_runners/admin_runners_app_spec.js
+++ b/spec/frontend/runner/admin_runners/admin_runners_app_spec.js
@@ -17,6 +17,7 @@ import { updateHistory } from '~/lib/utils/url_utility';
import { upgradeStatusTokenConfig } from 'ee_else_ce/runner/components/search_tokens/upgrade_status_token_config';
import { createLocalState } from '~/runner/graphql/list/local_state';
import AdminRunnersApp from '~/runner/admin_runners/admin_runners_app.vue';
+import RunnerStackedLayoutBanner from '~/runner/components/runner_stacked_layout_banner.vue';
import RunnerTypeTabs from '~/runner/components/runner_type_tabs.vue';
import RunnerFilteredSearchBar from '~/runner/components/runner_filtered_search_bar.vue';
import RunnerBulkDelete from '~/runner/components/runner_bulk_delete.vue';
@@ -33,6 +34,12 @@ import {
CREATED_ASC,
CREATED_DESC,
DEFAULT_SORT,
+ I18N_STATUS_ONLINE,
+ I18N_STATUS_OFFLINE,
+ I18N_STATUS_STALE,
+ I18N_INSTANCE_TYPE,
+ I18N_GROUP_TYPE,
+ I18N_PROJECT_TYPE,
INSTANCE_TYPE,
PARAM_KEY_PAUSED,
PARAM_KEY_STATUS,
@@ -80,6 +87,7 @@ describe('AdminRunnersApp', () => {
let localMutations;
let showToast;
+ const findRunnerStackedLayoutBanner = () => wrapper.findComponent(RunnerStackedLayoutBanner);
const findRunnerStats = () => wrapper.findComponent(RunnerStats);
const findRunnerActionsCell = () => wrapper.findComponent(RunnerActionsCell);
const findRegistrationDropdown = () => wrapper.findComponent(RegistrationDropdown);
@@ -139,6 +147,11 @@ describe('AdminRunnersApp', () => {
wrapper.destroy();
});
+ it('shows the feedback banner', () => {
+ createComponent();
+ expect(findRunnerStackedLayoutBanner().exists()).toBe(true);
+ });
+
it('shows the runner setup instructions', () => {
createComponent();
@@ -156,21 +169,16 @@ describe('AdminRunnersApp', () => {
});
it('shows the runner tabs', () => {
- expect(findRunnerTypeTabs().text()).toMatchInterpolatedText(
- `All ${mockRunnersCount} Instance ${mockRunnersCount} Group ${mockRunnersCount} Project ${mockRunnersCount}`,
+ const tabs = findRunnerTypeTabs().text();
+ expect(tabs).toMatchInterpolatedText(
+ `All ${mockRunnersCount} ${I18N_INSTANCE_TYPE} ${mockRunnersCount} ${I18N_GROUP_TYPE} ${mockRunnersCount} ${I18N_PROJECT_TYPE} ${mockRunnersCount}`,
);
});
it('shows the total', () => {
- expect(findRunnerStats().text()).toContain(
- `${s__('Runners|Online runners')} ${mockRunnersCount}`,
- );
- expect(findRunnerStats().text()).toContain(
- `${s__('Runners|Offline runners')} ${mockRunnersCount}`,
- );
- expect(findRunnerStats().text()).toContain(
- `${s__('Runners|Stale runners')} ${mockRunnersCount}`,
- );
+ expect(findRunnerStats().text()).toContain(`${I18N_STATUS_ONLINE} ${mockRunnersCount}`);
+ expect(findRunnerStats().text()).toContain(`${I18N_STATUS_OFFLINE} ${mockRunnersCount}`);
+ expect(findRunnerStats().text()).toContain(`${I18N_STATUS_STALE} ${mockRunnersCount}`);
});
});
diff --git a/spec/frontend/runner/components/cells/runner_stacked_summary_cell_spec.js b/spec/frontend/runner/components/cells/runner_stacked_summary_cell_spec.js
new file mode 100644
index 00000000000..21ec9f61f37
--- /dev/null
+++ b/spec/frontend/runner/components/cells/runner_stacked_summary_cell_spec.js
@@ -0,0 +1,164 @@
+import { __ } from '~/locale';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import RunnerStackedSummaryCell from '~/runner/components/cells/runner_stacked_summary_cell.vue';
+import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
+import RunnerTags from '~/runner/components/runner_tags.vue';
+import RunnerSummaryField from '~/runner/components/cells/runner_summary_field.vue';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+
+import { INSTANCE_TYPE, I18N_INSTANCE_TYPE, PROJECT_TYPE } from '~/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').find(TimeAgo).props('time')).toBe('2022-01-02');
+ });
+
+ it('Displays empty last contact', () => {
+ createComponent({
+ contactedAt: null,
+ });
+
+ expect(findRunnerSummaryField('clock').find(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').find(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/runner/components/cells/runner_status_cell_spec.js b/spec/frontend/runner/components/cells/runner_status_cell_spec.js
index 0f5133d0ae2..1d4e3762c91 100644
--- a/spec/frontend/runner/components/cells/runner_status_cell_spec.js
+++ b/spec/frontend/runner/components/cells/runner_status_cell_spec.js
@@ -3,7 +3,14 @@ import RunnerStatusCell from '~/runner/components/cells/runner_status_cell.vue';
import RunnerStatusBadge from '~/runner/components/runner_status_badge.vue';
import RunnerPausedBadge from '~/runner/components/runner_paused_badge.vue';
-import { INSTANCE_TYPE, STATUS_ONLINE, STATUS_OFFLINE } from '~/runner/constants';
+import {
+ I18N_PAUSED,
+ I18N_STATUS_ONLINE,
+ I18N_STATUS_OFFLINE,
+ INSTANCE_TYPE,
+ STATUS_ONLINE,
+ STATUS_OFFLINE,
+} from '~/runner/constants';
describe('RunnerStatusCell', () => {
let wrapper;
@@ -31,8 +38,8 @@ describe('RunnerStatusCell', () => {
it('Displays online status', () => {
createComponent();
- expect(wrapper.text()).toMatchInterpolatedText('online');
- expect(findStatusBadge().text()).toBe('online');
+ expect(wrapper.text()).toContain(I18N_STATUS_ONLINE);
+ expect(findStatusBadge().text()).toBe(I18N_STATUS_ONLINE);
});
it('Displays offline status', () => {
@@ -42,8 +49,8 @@ describe('RunnerStatusCell', () => {
},
});
- expect(wrapper.text()).toMatchInterpolatedText('offline');
- expect(findStatusBadge().text()).toBe('offline');
+ expect(wrapper.text()).toMatchInterpolatedText(I18N_STATUS_OFFLINE);
+ expect(findStatusBadge().text()).toBe(I18N_STATUS_OFFLINE);
});
it('Displays paused status', () => {
@@ -54,8 +61,8 @@ describe('RunnerStatusCell', () => {
},
});
- expect(wrapper.text()).toMatchInterpolatedText('online paused');
- expect(findPausedBadge().text()).toBe('paused');
+ expect(wrapper.text()).toMatchInterpolatedText(`${I18N_STATUS_ONLINE} ${I18N_PAUSED}`);
+ expect(findPausedBadge().text()).toBe(I18N_PAUSED);
});
it('Is empty when data is missing', () => {
diff --git a/spec/frontend/runner/components/cells/runner_summary_cell_spec.js b/spec/frontend/runner/components/cells/runner_summary_cell_spec.js
deleted file mode 100644
index b06ab652212..00000000000
--- a/spec/frontend/runner/components/cells/runner_summary_cell_spec.js
+++ /dev/null
@@ -1,91 +0,0 @@
-import { __ } from '~/locale';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import RunnerSummaryCell from '~/runner/components/cells/runner_summary_cell.vue';
-import { INSTANCE_TYPE, PROJECT_TYPE } from '~/runner/constants';
-
-const mockId = '1';
-const mockShortSha = '2P6oDVDm';
-const mockDescription = 'runner-1';
-const mockIpAddress = '0.0.0.0';
-
-describe('RunnerTypeCell', () => {
- let wrapper;
-
- const findLockIcon = () => wrapper.findByTestId('lock-icon');
-
- const createComponent = (runner, options) => {
- wrapper = mountExtended(RunnerSummaryCell, {
- propsData: {
- runner: {
- id: `gid://gitlab/Ci::Runner/${mockId}`,
- shortSha: mockShortSha,
- description: mockDescription,
- ipAddress: mockIpAddress,
- runnerType: INSTANCE_TYPE,
- ...runner,
- },
- },
- ...options,
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Displays the runner name as id and short token', () => {
- expect(wrapper.text()).toContain(`#${mockId} (${mockShortSha})`);
- });
-
- it('Displays the runner type', () => {
- expect(wrapper.text()).toContain('shared');
- });
-
- 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 description', () => {
- expect(wrapper.text()).toContain(mockDescription);
- });
-
- it('Displays ip address', () => {
- expect(wrapper.text()).toContain(`${__('IP Address')} ${mockIpAddress}`);
- });
-
- it('Displays no ip address', () => {
- createComponent({
- ipAddress: null,
- });
-
- expect(wrapper.text()).not.toContain(__('IP Address'));
- });
-
- it('Displays a custom slot', () => {
- const slotContent = 'My custom runner summary';
-
- createComponent(
- {},
- {
- slots: {
- 'runner-name': slotContent,
- },
- },
- );
-
- expect(wrapper.text()).toContain(slotContent);
- });
-});
diff --git a/spec/frontend/runner/components/cells/runner_summary_field_spec.js b/spec/frontend/runner/components/cells/runner_summary_field_spec.js
new file mode 100644
index 00000000000..b49addf112f
--- /dev/null
+++ b/spec/frontend/runner/components/cells/runner_summary_field_spec.js
@@ -0,0 +1,49 @@
+import { GlIcon } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import RunnerSummaryField from '~/runner/components/cells/runner_summary_field.vue';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
+
+describe('RunnerSummaryField', () => {
+ let wrapper;
+
+ const findIcon = () => wrapper.findComponent(GlIcon);
+ const getTooltipValue = () => getBinding(wrapper.element, 'gl-tooltip').value;
+
+ const createComponent = ({ props, ...options } = {}) => {
+ wrapper = shallowMount(RunnerSummaryField, {
+ propsData: {
+ icon: '',
+ tooltip: '',
+ ...props,
+ },
+ directives: {
+ GlTooltip: createMockDirective(),
+ },
+ ...options,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('shows content in slot', () => {
+ createComponent({
+ slots: { default: 'content' },
+ });
+
+ expect(wrapper.text()).toBe('content');
+ });
+
+ it('shows icon', () => {
+ createComponent({ props: { icon: 'git' } });
+
+ expect(findIcon().props('name')).toBe('git');
+ });
+
+ it('shows tooltip', () => {
+ createComponent({ props: { tooltip: 'tooltip' } });
+
+ expect(getTooltipValue()).toBe('tooltip');
+ });
+});
diff --git a/spec/frontend/runner/components/runner_details_spec.js b/spec/frontend/runner/components/runner_details_spec.js
index 552ee29b6f9..f2281223a25 100644
--- a/spec/frontend/runner/components/runner_details_spec.js
+++ b/spec/frontend/runner/components/runner_details_spec.js
@@ -25,7 +25,12 @@ describe('RunnerDetails', () => {
const findDetailGroups = () => wrapper.findComponent(RunnerGroups);
- const createComponent = ({ props = {}, stubs, mountFn = shallowMountExtended } = {}) => {
+ const createComponent = ({
+ props = {},
+ stubs,
+ mountFn = shallowMountExtended,
+ enforceRunnerTokenExpiresAt = false,
+ } = {}) => {
wrapper = mountFn(RunnerDetails, {
propsData: {
...props,
@@ -34,6 +39,9 @@ describe('RunnerDetails', () => {
RunnerDetail,
...stubs,
},
+ provide: {
+ glFeatures: { enforceRunnerTokenExpiresAt },
+ },
});
};
@@ -63,6 +71,8 @@ describe('RunnerDetails', () => {
${'Maximum job timeout'} | ${{ maximumTimeout: 0 }} | ${'0 seconds'}
${'Maximum job timeout'} | ${{ maximumTimeout: 59 }} | ${'59 seconds'}
${'Maximum job timeout'} | ${{ maximumTimeout: 10 * 60 + 5 }} | ${'10 minutes 5 seconds'}
+ ${'Token expiry'} | ${{ tokenExpiresAt: mockOneHourAgo }} | ${'1 hour ago'}
+ ${'Token expiry'} | ${{ tokenExpiresAt: null }} | ${'Never expires'}
`('"$field" field', ({ field, runner, expectedValue }) => {
beforeEach(() => {
createComponent({
@@ -72,6 +82,7 @@ describe('RunnerDetails', () => {
...runner,
},
},
+ enforceRunnerTokenExpiresAt: true,
stubs: {
GlIntersperse,
GlSprintf,
@@ -124,5 +135,22 @@ describe('RunnerDetails', () => {
expect(findDetailGroups().props('runner')).toEqual(mockGroupRunner);
});
});
+
+ describe('Token expiration field', () => {
+ it.each`
+ case | flag | shown
+ ${'is shown when feature flag is enabled'} | ${true} | ${true}
+ ${'is not shown when feature flag is disabled'} | ${false} | ${false}
+ `('$case', ({ flag, shown }) => {
+ createComponent({
+ props: {
+ runner: mockGroupRunner,
+ },
+ enforceRunnerTokenExpiresAt: flag,
+ });
+
+ expect(findDd('Token expiry', wrapper).exists()).toBe(shown);
+ });
+ });
});
});
diff --git a/spec/frontend/runner/components/runner_header_spec.js b/spec/frontend/runner/components/runner_header_spec.js
index 8799c218b06..701d39108cb 100644
--- a/spec/frontend/runner/components/runner_header_spec.js
+++ b/spec/frontend/runner/components/runner_header_spec.js
@@ -1,6 +1,6 @@
import { GlSprintf } from '@gitlab/ui';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { GROUP_TYPE, STATUS_ONLINE } from '~/runner/constants';
+import { I18N_STATUS_ONLINE, I18N_GROUP_TYPE, GROUP_TYPE, STATUS_ONLINE } from '~/runner/constants';
import { TYPE_CI_RUNNER } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
@@ -49,7 +49,7 @@ describe('RunnerHeader', () => {
},
});
- expect(findRunnerStatusBadge().text()).toContain('online');
+ expect(findRunnerStatusBadge().text()).toContain(I18N_STATUS_ONLINE);
});
it('displays the runner type', () => {
@@ -60,7 +60,7 @@ describe('RunnerHeader', () => {
},
});
- expect(findRunnerTypeBadge().text()).toContain('group');
+ expect(findRunnerTypeBadge().text()).toContain(I18N_GROUP_TYPE);
});
it('displays the runner id', () => {
diff --git a/spec/frontend/runner/components/runner_list_spec.js b/spec/frontend/runner/components/runner_list_spec.js
index 7b58a81bb0d..54a9e713721 100644
--- a/spec/frontend/runner/components/runner_list_spec.js
+++ b/spec/frontend/runner/components/runner_list_spec.js
@@ -7,6 +7,7 @@ import {
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import RunnerList from '~/runner/components/runner_list.vue';
import RunnerStatusPopover from '~/runner/components/runner_status_popover.vue';
+import { I18N_PROJECT_TYPE, I18N_STATUS_NEVER_CONTACTED } from '~/runner/constants';
import { allRunnersData, onlineContactTimeoutSecs, staleTimeoutSecs } from '../mock_data';
const mockRunners = allRunnersData.data.runners.nodes;
@@ -22,7 +23,10 @@ describe('RunnerList', () => {
const findCell = ({ row = 0, fieldKey }) =>
extendedWrapper(findRows().at(row).find(`[data-testid="td-${fieldKey}"]`));
- const createComponent = ({ props = {}, ...options } = {}, mountFn = shallowMountExtended) => {
+ const createComponent = (
+ { props = {}, provide = {}, ...options } = {},
+ mountFn = shallowMountExtended,
+ ) => {
wrapper = mountFn(RunnerList, {
propsData: {
runners: mockRunners,
@@ -32,6 +36,7 @@ describe('RunnerList', () => {
provide: {
onlineContactTimeoutSecs,
staleTimeoutSecs,
+ ...provide,
},
...options,
});
@@ -60,10 +65,6 @@ describe('RunnerList', () => {
expect(headerLabels).toEqual([
'Status',
'Runner',
- 'Version',
- 'Jobs',
- 'Tags',
- 'Last contact',
'', // actions has no label
]);
});
@@ -83,24 +84,28 @@ describe('RunnerList', () => {
});
it('Displays details of a runner', () => {
- const { id, description, version, shortSha } = mockRunners[0];
-
createComponent({}, mountExtended);
+ const { id, description, version, shortSha } = mockRunners[0];
+ const numericId = getIdFromGraphQLId(id);
+
// Badges
- expect(findCell({ fieldKey: 'status' }).text()).toMatchInterpolatedText('never contacted');
+ expect(findCell({ fieldKey: 'status' }).text()).toMatchInterpolatedText(
+ I18N_STATUS_NEVER_CONTACTED,
+ );
// Runner summary
- expect(findCell({ fieldKey: 'summary' }).text()).toContain(
- `#${getIdFromGraphQLId(id)} (${shortSha})`,
- );
- expect(findCell({ fieldKey: 'summary' }).text()).toContain(description);
+ const summary = findCell({ fieldKey: 'summary' }).text();
- // Other fields
- expect(findCell({ fieldKey: 'version' }).text()).toBe(version);
- expect(findCell({ fieldKey: 'jobCount' }).text()).toBe('0');
- expect(findCell({ fieldKey: 'tagList' }).text()).toBe('');
- expect(findCell({ fieldKey: 'contactedAt' }).text()).toEqual(expect.any(String));
+ expect(summary).toContain(`#${numericId} (${shortSha})`);
+ expect(summary).toContain(I18N_PROJECT_TYPE);
+
+ expect(summary).toContain(version);
+ expect(summary).toContain(description);
+
+ expect(summary).toContain('Last contact');
+ expect(summary).toContain('0'); // job count
+ expect(summary).toContain('Created');
// Actions
expect(findCell({ fieldKey: 'actions' }).exists()).toBe(true);
@@ -159,42 +164,6 @@ describe('RunnerList', () => {
});
});
- describe('Table data formatting', () => {
- let mockRunnersCopy;
-
- beforeEach(() => {
- mockRunnersCopy = [
- {
- ...mockRunners[0],
- },
- ];
- });
-
- it('Formats job counts', () => {
- mockRunnersCopy[0].jobCount = 1;
-
- createComponent({ props: { runners: mockRunnersCopy } }, mountExtended);
-
- expect(findCell({ fieldKey: 'jobCount' }).text()).toBe('1');
- });
-
- it('Formats large job counts', () => {
- mockRunnersCopy[0].jobCount = 1000;
-
- createComponent({ props: { runners: mockRunnersCopy } }, mountExtended);
-
- expect(findCell({ fieldKey: 'jobCount' }).text()).toBe('1,000');
- });
-
- it('Formats large job counts with a plus symbol', () => {
- mockRunnersCopy[0].jobCount = 1001;
-
- createComponent({ props: { runners: mockRunnersCopy } }, mountExtended);
-
- expect(findCell({ fieldKey: 'jobCount' }).text()).toBe('1,000+');
- });
- });
-
it('Shows runner identifier', () => {
const { id, shortSha } = mockRunners[0];
const numericId = getIdFromGraphQLId(id);
diff --git a/spec/frontend/runner/components/runner_paused_badge_spec.js b/spec/frontend/runner/components/runner_paused_badge_spec.js
index 18cfcfae864..c1c7351aab2 100644
--- a/spec/frontend/runner/components/runner_paused_badge_spec.js
+++ b/spec/frontend/runner/components/runner_paused_badge_spec.js
@@ -2,6 +2,7 @@ import { GlBadge } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import RunnerStatePausedBadge from '~/runner/components/runner_paused_badge.vue';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
+import { I18N_PAUSED } from '~/runner/constants';
describe('RunnerTypeBadge', () => {
let wrapper;
@@ -29,8 +30,8 @@ describe('RunnerTypeBadge', () => {
});
it('renders paused state', () => {
- expect(wrapper.text()).toBe('paused');
- expect(findBadge().props('variant')).toBe('danger');
+ expect(wrapper.text()).toBe(I18N_PAUSED);
+ expect(findBadge().props('variant')).toBe('warning');
});
it('renders tooltip', () => {
diff --git a/spec/frontend/runner/components/runner_projects_spec.js b/spec/frontend/runner/components/runner_projects_spec.js
index c988fb8477d..eca042cae86 100644
--- a/spec/frontend/runner/components/runner_projects_spec.js
+++ b/spec/frontend/runner/components/runner_projects_spec.js
@@ -1,4 +1,4 @@
-import { GlSkeletonLoader } from '@gitlab/ui';
+import { GlSearchBoxByType, GlSkeletonLoader } from '@gitlab/ui';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
@@ -8,7 +8,9 @@ import { createAlert } from '~/flash';
import { sprintf } from '~/locale';
import {
I18N_ASSIGNED_PROJECTS,
- I18N_NONE,
+ I18N_CLEAR_FILTER_PROJECTS,
+ I18N_FILTER_PROJECTS,
+ I18N_NO_PROJECTS_FOUND,
RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
} from '~/runner/constants';
import RunnerProjects from '~/runner/components/runner_projects.vue';
@@ -35,6 +37,7 @@ describe('RunnerProjects', () => {
const findHeading = () => wrapper.find('h3');
const findGlSkeletonLoading = () => wrapper.findComponent(GlSkeletonLoader);
+ const findGlSearchBoxByType = () => wrapper.findComponent(GlSearchBoxByType);
const findRunnerAssignedItems = () => wrapper.findAllComponents(RunnerAssignedItem);
const findRunnerPagination = () => wrapper.findComponent(RunnerPagination);
@@ -64,10 +67,21 @@ describe('RunnerProjects', () => {
expect(mockRunnerProjectsQuery).toHaveBeenCalledTimes(1);
expect(mockRunnerProjectsQuery).toHaveBeenCalledWith({
id: mockRunner.id,
+ search: '',
first: RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
});
});
+ it('Shows a filter box', () => {
+ createComponent();
+
+ expect(findGlSearchBoxByType().attributes()).toMatchObject({
+ clearbuttontitle: I18N_CLEAR_FILTER_PROJECTS,
+ debounce: '500',
+ placeholder: I18N_FILTER_PROJECTS,
+ });
+ });
+
describe('When there are projects assigned', () => {
beforeEach(async () => {
mockRunnerProjectsQuery.mockResolvedValueOnce(runnerProjectsData);
@@ -110,6 +124,7 @@ describe('RunnerProjects', () => {
expect(mockRunnerProjectsQuery).toHaveBeenCalledTimes(2);
expect(mockRunnerProjectsQuery).toHaveBeenLastCalledWith({
id: mockRunner.id,
+ search: '',
first: RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
after: 'AFTER_CURSOR',
});
@@ -123,10 +138,51 @@ describe('RunnerProjects', () => {
expect(mockRunnerProjectsQuery).toHaveBeenCalledTimes(3);
expect(mockRunnerProjectsQuery).toHaveBeenLastCalledWith({
id: mockRunner.id,
+ search: '',
last: RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
before: 'BEFORE_CURSOR',
});
});
+
+ it('When user filters after paginating, the first page is requested', async () => {
+ findGlSearchBoxByType().vm.$emit('input', 'my search');
+ await waitForPromises();
+
+ expect(mockRunnerProjectsQuery).toHaveBeenCalledTimes(3);
+ expect(mockRunnerProjectsQuery).toHaveBeenLastCalledWith({
+ id: mockRunner.id,
+ search: 'my search',
+ first: RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
+ });
+ });
+ });
+
+ describe('When user filters', () => {
+ it('Filtered results are requested', async () => {
+ expect(mockRunnerProjectsQuery).toHaveBeenCalledTimes(1);
+
+ findGlSearchBoxByType().vm.$emit('input', 'my search');
+ await waitForPromises();
+
+ expect(mockRunnerProjectsQuery).toHaveBeenCalledTimes(2);
+ expect(mockRunnerProjectsQuery).toHaveBeenLastCalledWith({
+ id: mockRunner.id,
+ search: 'my search',
+ first: RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
+ });
+ });
+
+ it('Filtered results are not requested for short searches', async () => {
+ expect(mockRunnerProjectsQuery).toHaveBeenCalledTimes(1);
+
+ findGlSearchBoxByType().vm.$emit('input', 'm');
+ await waitForPromises();
+
+ findGlSearchBoxByType().vm.$emit('input', 'my');
+ await waitForPromises();
+
+ expect(mockRunnerProjectsQuery).toHaveBeenCalledTimes(1);
+ });
});
});
@@ -136,10 +192,11 @@ describe('RunnerProjects', () => {
expect(findGlSkeletonLoading().exists()).toBe(true);
- expect(wrapper.findByText(I18N_NONE).exists()).toBe(false);
+ expect(wrapper.findByText(I18N_NO_PROJECTS_FOUND).exists()).toBe(false);
expect(findRunnerAssignedItems().length).toBe(0);
expect(findRunnerPagination().attributes('disabled')).toBe('true');
+ expect(findGlSearchBoxByType().props('isLoading')).toBe(true);
});
});
@@ -168,7 +225,7 @@ describe('RunnerProjects', () => {
});
it('Shows a "None" label', () => {
- expect(wrapper.findByText(I18N_NONE).exists()).toBe(true);
+ expect(wrapper.findByText(I18N_NO_PROJECTS_FOUND).exists()).toBe(true);
});
});
diff --git a/spec/frontend/runner/components/runner_stacked_layout_banner_spec.js b/spec/frontend/runner/components/runner_stacked_layout_banner_spec.js
new file mode 100644
index 00000000000..1a8aced9292
--- /dev/null
+++ b/spec/frontend/runner/components/runner_stacked_layout_banner_spec.js
@@ -0,0 +1,39 @@
+import { nextTick } from 'vue';
+import { GlBanner } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import RunnerStackedLayoutBanner from '~/runner/components/runner_stacked_layout_banner.vue';
+import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
+
+describe('RunnerStackedLayoutBanner', () => {
+ let wrapper;
+
+ const findBanner = () => wrapper.findComponent(GlBanner);
+ const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
+
+ const createComponent = ({ ...options } = {}, mountFn = shallowMount) => {
+ wrapper = mountFn(RunnerStackedLayoutBanner, {
+ ...options,
+ });
+ };
+
+ it('Displays a banner', () => {
+ createComponent();
+
+ expect(findBanner().props()).toMatchObject({
+ svgPath: expect.stringContaining('data:image/svg+xml;utf8,'),
+ title: expect.any(String),
+ buttonText: expect.any(String),
+ buttonLink: expect.stringContaining('https://gitlab.com/gitlab-org/gitlab/-/issues/'),
+ });
+ expect(findLocalStorageSync().exists()).toBe(true);
+ });
+
+ it('Does not display a banner when dismissed', async () => {
+ findLocalStorageSync().vm.$emit('input', true);
+
+ await nextTick();
+
+ expect(findBanner().exists()).toBe(false);
+ expect(findLocalStorageSync().exists()).toBe(true); // continues syncing after removal
+ });
+});
diff --git a/spec/frontend/runner/components/runner_status_badge_spec.js b/spec/frontend/runner/components/runner_status_badge_spec.js
index bb833bd7d5a..9ab6378304f 100644
--- a/spec/frontend/runner/components/runner_status_badge_spec.js
+++ b/spec/frontend/runner/components/runner_status_badge_spec.js
@@ -3,12 +3,16 @@ import { shallowMount } from '@vue/test-utils';
import RunnerStatusBadge from '~/runner/components/runner_status_badge.vue';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import {
+ I18N_STATUS_ONLINE,
+ I18N_STATUS_NEVER_CONTACTED,
+ I18N_STATUS_OFFLINE,
+ I18N_STATUS_STALE,
+ I18N_NEVER_CONTACTED_TOOLTIP,
+ I18N_STALE_NEVER_CONTACTED_TOOLTIP,
STATUS_ONLINE,
STATUS_OFFLINE,
STATUS_STALE,
STATUS_NEVER_CONTACTED,
- I18N_NEVER_CONTACTED_TOOLTIP,
- I18N_STALE_NEVER_CONTACTED_TOOLTIP,
} from '~/runner/constants';
describe('RunnerTypeBadge', () => {
@@ -46,7 +50,7 @@ describe('RunnerTypeBadge', () => {
it('renders online state', () => {
createComponent();
- expect(wrapper.text()).toBe('online');
+ expect(wrapper.text()).toBe(I18N_STATUS_ONLINE);
expect(findBadge().props('variant')).toBe('success');
expect(getTooltip().value).toBe('Runner is online; last contact was 1 minute ago');
});
@@ -59,7 +63,7 @@ describe('RunnerTypeBadge', () => {
},
});
- expect(wrapper.text()).toBe('never contacted');
+ expect(wrapper.text()).toBe(I18N_STATUS_NEVER_CONTACTED);
expect(findBadge().props('variant')).toBe('muted');
expect(getTooltip().value).toBe(I18N_NEVER_CONTACTED_TOOLTIP);
});
@@ -72,7 +76,7 @@ describe('RunnerTypeBadge', () => {
},
});
- expect(wrapper.text()).toBe('offline');
+ expect(wrapper.text()).toBe(I18N_STATUS_OFFLINE);
expect(findBadge().props('variant')).toBe('muted');
expect(getTooltip().value).toBe('Runner is offline; last contact was 1 day ago');
});
@@ -85,7 +89,7 @@ describe('RunnerTypeBadge', () => {
},
});
- expect(wrapper.text()).toBe('stale');
+ expect(wrapper.text()).toBe(I18N_STATUS_STALE);
expect(findBadge().props('variant')).toBe('warning');
expect(getTooltip().value).toBe('Runner is stale; last contact was 1 year ago');
});
@@ -98,7 +102,7 @@ describe('RunnerTypeBadge', () => {
},
});
- expect(wrapper.text()).toBe('stale');
+ expect(wrapper.text()).toBe(I18N_STATUS_STALE);
expect(findBadge().props('variant')).toBe('warning');
expect(getTooltip().value).toBe(I18N_STALE_NEVER_CONTACTED_TOOLTIP);
});
@@ -112,7 +116,7 @@ describe('RunnerTypeBadge', () => {
},
});
- expect(wrapper.text()).toBe('online');
+ expect(wrapper.text()).toBe(I18N_STATUS_ONLINE);
expect(getTooltip().value).toBe('Runner is online; last contact was never');
});
diff --git a/spec/frontend/runner/components/runner_tag_spec.js b/spec/frontend/runner/components/runner_tag_spec.js
index bd05d4b2cfe..391c17f81cb 100644
--- a/spec/frontend/runner/components/runner_tag_spec.js
+++ b/spec/frontend/runner/components/runner_tag_spec.js
@@ -1,6 +1,8 @@
import { GlBadge } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
+
+import { RUNNER_TAG_BADGE_VARIANT } from '~/runner/constants';
import RunnerTag from '~/runner/components/runner_tag.vue';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
@@ -48,7 +50,7 @@ describe('RunnerTag', () => {
it('Displays tags with correct style', () => {
expect(findBadge().props()).toMatchObject({
size: 'sm',
- variant: 'neutral',
+ variant: RUNNER_TAG_BADGE_VARIANT,
});
});
diff --git a/spec/frontend/runner/components/runner_tags_spec.js b/spec/frontend/runner/components/runner_tags_spec.js
index da89a659432..c6bfabdb18a 100644
--- a/spec/frontend/runner/components/runner_tags_spec.js
+++ b/spec/frontend/runner/components/runner_tags_spec.js
@@ -34,7 +34,6 @@ describe('RunnerTags', () => {
it('Displays tags with correct style', () => {
expect(findBadge().props('size')).toBe('sm');
- expect(findBadge().props('variant')).toBe('neutral');
});
it('Displays tags with md size', () => {
@@ -50,7 +49,6 @@ describe('RunnerTags', () => {
props: { tagList: null },
});
- expect(wrapper.text()).toBe('');
- expect(findBadge().exists()).toBe(false);
+ expect(wrapper.html()).toEqual('');
});
});
diff --git a/spec/frontend/runner/components/runner_type_badge_spec.js b/spec/frontend/runner/components/runner_type_badge_spec.js
index 7bb0a2e6e2f..fe922fb9d18 100644
--- a/spec/frontend/runner/components/runner_type_badge_spec.js
+++ b/spec/frontend/runner/components/runner_type_badge_spec.js
@@ -2,7 +2,14 @@ import { GlBadge } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import RunnerTypeBadge from '~/runner/components/runner_type_badge.vue';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '~/runner/constants';
+import {
+ INSTANCE_TYPE,
+ GROUP_TYPE,
+ PROJECT_TYPE,
+ I18N_INSTANCE_TYPE,
+ I18N_GROUP_TYPE,
+ I18N_PROJECT_TYPE,
+} from '~/runner/constants';
describe('RunnerTypeBadge', () => {
let wrapper;
@@ -27,9 +34,9 @@ describe('RunnerTypeBadge', () => {
describe.each`
type | text
- ${INSTANCE_TYPE} | ${'shared'}
- ${GROUP_TYPE} | ${'group'}
- ${PROJECT_TYPE} | ${'specific'}
+ ${INSTANCE_TYPE} | ${I18N_INSTANCE_TYPE}
+ ${GROUP_TYPE} | ${I18N_GROUP_TYPE}
+ ${PROJECT_TYPE} | ${I18N_PROJECT_TYPE}
`('displays $type runner', ({ type, text }) => {
beforeEach(() => {
createComponent({ props: { type } });
@@ -37,7 +44,7 @@ describe('RunnerTypeBadge', () => {
it(`as "${text}" with an "info" variant`, () => {
expect(findBadge().text()).toBe(text);
- expect(findBadge().props('variant')).toBe('info');
+ expect(findBadge().props('variant')).toBe('muted');
});
it('with a tooltip', () => {
diff --git a/spec/frontend/runner/components/runner_type_tabs_spec.js b/spec/frontend/runner/components/runner_type_tabs_spec.js
index 22d2a9e60f7..45ab8684332 100644
--- a/spec/frontend/runner/components/runner_type_tabs_spec.js
+++ b/spec/frontend/runner/components/runner_type_tabs_spec.js
@@ -28,7 +28,7 @@ const mockCount = (type, multiplier = 1) => {
describe('RunnerTypeTabs', () => {
let wrapper;
- const findTabs = () => wrapper.findAll(GlTab);
+ const findTabs = () => wrapper.findAllComponents(GlTab);
const findActiveTab = () =>
findTabs()
.filter((tab) => tab.attributes('active') === 'true')
diff --git a/spec/frontend/runner/components/runner_update_form_spec.js b/spec/frontend/runner/components/runner_update_form_spec.js
index 3037364d941..7b67a89f989 100644
--- a/spec/frontend/runner/components/runner_update_form_spec.js
+++ b/spec/frontend/runner/components/runner_update_form_spec.js
@@ -1,6 +1,7 @@
import Vue, { nextTick } from 'vue';
import { GlForm, GlSkeletonLoader } from '@gitlab/ui';
import VueApollo from 'vue-apollo';
+import { __ } from '~/locale';
import createMockApollo from 'helpers/mock_apollo_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
@@ -47,6 +48,7 @@ describe('RunnerUpdateForm', () => {
const findSubmit = () => wrapper.find('[type="submit"]');
const findSubmitDisabledAttr = () => findSubmit().attributes('disabled');
+ const findCancelBtn = () => wrapper.findByRole('link', { name: __('Cancel') });
const submitForm = () => findForm().trigger('submit');
const submitFormAndWait = () => submitForm().then(waitForPromises);
@@ -117,6 +119,11 @@ describe('RunnerUpdateForm', () => {
expect(mockRunner).toMatchObject(getFieldsModel());
});
+ it('Form shows a cancel button', () => {
+ expect(runnerUpdateHandler).not.toHaveBeenCalled();
+ expect(findCancelBtn().attributes('href')).toBe(mockRunnerPath);
+ });
+
it('Form prevent multiple submissions', async () => {
await submitForm();
diff --git a/spec/frontend/runner/components/stat/runner_stats_spec.js b/spec/frontend/runner/components/stat/runner_stats_spec.js
index 7f1f22be94f..4afbe453903 100644
--- a/spec/frontend/runner/components/stat/runner_stats_spec.js
+++ b/spec/frontend/runner/components/stat/runner_stats_spec.js
@@ -1,13 +1,20 @@
import { shallowMount, mount } from '@vue/test-utils';
-import { s__ } from '~/locale';
import RunnerStats from '~/runner/components/stat/runner_stats.vue';
import RunnerSingleStat from '~/runner/components/stat/runner_single_stat.vue';
-import { INSTANCE_TYPE, STATUS_ONLINE, STATUS_OFFLINE, STATUS_STALE } from '~/runner/constants';
+import {
+ I18N_STATUS_ONLINE,
+ I18N_STATUS_OFFLINE,
+ I18N_STATUS_STALE,
+ INSTANCE_TYPE,
+ STATUS_ONLINE,
+ STATUS_OFFLINE,
+ STATUS_STALE,
+} from '~/runner/constants';
describe('RunnerStats', () => {
let wrapper;
- const findSingleStats = () => wrapper.findAllComponents(RunnerSingleStat).wrappers;
+ const findSingleStats = () => wrapper.findAllComponents(RunnerSingleStat);
const createComponent = ({ props = {}, mountFn = shallowMount, ...options } = {}) => {
wrapper = mountFn(RunnerStats, {
@@ -46,16 +53,28 @@ describe('RunnerStats', () => {
});
const text = wrapper.text();
- expect(text).toMatch(`${s__('Runners|Online runners')} 3`);
- expect(text).toMatch(`${s__('Runners|Offline runners')} 2`);
- expect(text).toMatch(`${s__('Runners|Stale runners')} 1`);
+ expect(text).toContain(`${I18N_STATUS_ONLINE} 3`);
+ expect(text).toContain(`${I18N_STATUS_OFFLINE} 2`);
+ expect(text).toContain(`${I18N_STATUS_STALE} 1`);
+ });
+
+ it('Skips query for other stats', () => {
+ createComponent({
+ props: {
+ variables: { status: STATUS_ONLINE },
+ },
+ });
+
+ expect(findSingleStats().at(0).props('skip')).toBe(false);
+ expect(findSingleStats().at(1).props('skip')).toBe(true);
+ expect(findSingleStats().at(2).props('skip')).toBe(true);
});
it('Displays all counts for filtered searches', () => {
const mockVariables = { paused: true };
createComponent({ props: { variables: mockVariables } });
- findSingleStats().forEach((stat) => {
+ findSingleStats().wrappers.forEach((stat) => {
expect(stat.props('variables')).toMatchObject(mockVariables);
});
});
diff --git a/spec/frontend/runner/group_runners/group_runners_app_spec.js b/spec/frontend/runner/group_runners/group_runners_app_spec.js
index 57d64202219..a17502c7eec 100644
--- a/spec/frontend/runner/group_runners/group_runners_app_spec.js
+++ b/spec/frontend/runner/group_runners/group_runners_app_spec.js
@@ -15,6 +15,7 @@ import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { updateHistory } from '~/lib/utils/url_utility';
import { upgradeStatusTokenConfig } from 'ee_else_ce/runner/components/search_tokens/upgrade_status_token_config';
+import RunnerStackedLayoutBanner from '~/runner/components/runner_stacked_layout_banner.vue';
import RunnerTypeTabs from '~/runner/components/runner_type_tabs.vue';
import RunnerFilteredSearchBar from '~/runner/components/runner_filtered_search_bar.vue';
import RunnerList from '~/runner/components/runner_list.vue';
@@ -28,6 +29,9 @@ import {
CREATED_ASC,
CREATED_DESC,
DEFAULT_SORT,
+ I18N_STATUS_ONLINE,
+ I18N_STATUS_OFFLINE,
+ I18N_STATUS_STALE,
INSTANCE_TYPE,
GROUP_TYPE,
PARAM_KEY_PAUSED,
@@ -74,6 +78,7 @@ jest.mock('~/lib/utils/url_utility', () => ({
describe('GroupRunnersApp', () => {
let wrapper;
+ const findRunnerStackedLayoutBanner = () => wrapper.findComponent(RunnerStackedLayoutBanner);
const findRunnerStats = () => wrapper.findComponent(RunnerStats);
const findRunnerActionsCell = () => wrapper.findComponent(RunnerActionsCell);
const findRegistrationDropdown = () => wrapper.findComponent(RegistrationDropdown);
@@ -122,6 +127,11 @@ describe('GroupRunnersApp', () => {
wrapper.destroy();
});
+ it('shows the feedback banner', () => {
+ createComponent();
+ expect(findRunnerStackedLayoutBanner().exists()).toBe(true);
+ });
+
it('shows the runner tabs with a runner count for each type', async () => {
await createComponent({ mountFn: mountExtended });
@@ -153,15 +163,10 @@ describe('GroupRunnersApp', () => {
groupFullPath: mockGroupFullPath,
});
- expect(findRunnerStats().text()).toContain(
- `${s__('Runners|Online runners')} ${mockGroupRunnersCount}`,
- );
- expect(findRunnerStats().text()).toContain(
- `${s__('Runners|Offline runners')} ${mockGroupRunnersCount}`,
- );
- expect(findRunnerStats().text()).toContain(
- `${s__('Runners|Stale runners')} ${mockGroupRunnersCount}`,
- );
+ const text = findRunnerStats().text();
+ expect(text).toContain(`${I18N_STATUS_ONLINE} ${mockGroupRunnersCount}`);
+ expect(text).toContain(`${I18N_STATUS_OFFLINE} ${mockGroupRunnersCount}`);
+ expect(text).toContain(`${I18N_STATUS_STALE} ${mockGroupRunnersCount}`);
});
it('shows the runners list', async () => {
@@ -396,4 +401,36 @@ describe('GroupRunnersApp', () => {
});
});
});
+
+ describe('when user has permission to register group runner', () => {
+ beforeEach(() => {
+ createComponent({
+ propsData: {
+ registrationToken: mockRegistrationToken,
+ groupFullPath: mockGroupFullPath,
+ groupRunnersLimitedCount: mockGroupRunnersCount,
+ },
+ });
+ });
+
+ it('shows the register group runner button', () => {
+ expect(findRegistrationDropdown().exists()).toBe(true);
+ });
+ });
+
+ describe('when user has no permission to register group runner', () => {
+ beforeEach(() => {
+ createComponent({
+ propsData: {
+ registrationToken: null,
+ groupFullPath: mockGroupFullPath,
+ groupRunnersLimitedCount: mockGroupRunnersCount,
+ },
+ });
+ });
+
+ it('does not show the register group runner button', () => {
+ expect(findRegistrationDropdown().exists()).toBe(false);
+ });
+ });
});
diff --git a/spec/frontend/runner/runner_edit/runner_edit_app_spec.js b/spec/frontend/runner/runner_edit/runner_edit_app_spec.js
new file mode 100644
index 00000000000..fb118817d51
--- /dev/null
+++ b/spec/frontend/runner/runner_edit/runner_edit_app_spec.js
@@ -0,0 +1,114 @@
+import { mount, 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 { createAlert } from '~/flash';
+
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import RunnerHeader from '~/runner/components/runner_header.vue';
+import RunnerUpdateForm from '~/runner/components/runner_update_form.vue';
+import runnerFormQuery from '~/runner/graphql/edit/runner_form.query.graphql';
+import RunnerEditApp from '~//runner/runner_edit/runner_edit_app.vue';
+import { captureException } from '~/runner/sentry_utils';
+import { I18N_STATUS_NEVER_CONTACTED, I18N_INSTANCE_TYPE } from '~/runner/constants';
+
+import { runnerFormData } from '../mock_data';
+
+jest.mock('~/flash');
+jest.mock('~/runner/sentry_utils');
+
+const mockRunner = runnerFormData.data.runner;
+const mockRunnerGraphqlId = mockRunner.id;
+const mockRunnerId = `${getIdFromGraphQLId(mockRunnerGraphqlId)}`;
+const mockRunnerPath = `/admin/runners/${mockRunnerId}`;
+
+Vue.use(VueApollo);
+
+describe('RunnerEditApp', () => {
+ let wrapper;
+ let mockRunnerQuery;
+
+ const findRunnerHeader = () => wrapper.findComponent(RunnerHeader);
+ const findRunnerUpdateForm = () => wrapper.findComponent(RunnerUpdateForm);
+
+ const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => {
+ wrapper = mountFn(RunnerEditApp, {
+ apolloProvider: createMockApollo([[runnerFormQuery, mockRunnerQuery]]),
+ propsData: {
+ runnerId: mockRunnerId,
+ runnerPath: mockRunnerPath,
+ ...props,
+ },
+ });
+
+ return waitForPromises();
+ };
+
+ beforeEach(() => {
+ mockRunnerQuery = jest.fn().mockResolvedValue(runnerFormData);
+ });
+
+ afterEach(() => {
+ mockRunnerQuery.mockReset();
+ wrapper.destroy();
+ });
+
+ it('expect GraphQL ID to be requested', async () => {
+ await createComponentWithApollo();
+
+ expect(mockRunnerQuery).toHaveBeenCalledWith({ id: mockRunnerGraphqlId });
+ });
+
+ it('displays the runner id and creation date', async () => {
+ await createComponentWithApollo({ mountFn: mount });
+
+ expect(findRunnerHeader().text()).toContain(`Runner #${mockRunnerId}`);
+ expect(findRunnerHeader().text()).toContain('created');
+ });
+
+ it('displays the runner type and status', async () => {
+ await createComponentWithApollo({ mountFn: mount });
+
+ expect(findRunnerHeader().text()).toContain(I18N_STATUS_NEVER_CONTACTED);
+ expect(findRunnerHeader().text()).toContain(I18N_INSTANCE_TYPE);
+ });
+
+ it('displays a loading runner form', () => {
+ createComponentWithApollo();
+
+ expect(findRunnerUpdateForm().props()).toMatchObject({
+ runner: null,
+ loading: true,
+ runnerPath: mockRunnerPath,
+ });
+ });
+
+ it('displays the runner form', async () => {
+ await createComponentWithApollo();
+
+ expect(findRunnerUpdateForm().props()).toMatchObject({
+ loading: false,
+ runnerPath: mockRunnerPath,
+ });
+ expect(findRunnerUpdateForm().props('runner')).toEqual(mockRunner);
+ });
+
+ describe('When there is an error', () => {
+ beforeEach(async () => {
+ mockRunnerQuery = jest.fn().mockRejectedValueOnce(new Error('Error!'));
+ await createComponentWithApollo();
+ });
+
+ it('error is reported to sentry', () => {
+ expect(captureException).toHaveBeenCalledWith({
+ error: new Error('Error!'),
+ component: 'RunnerEditApp',
+ });
+ });
+
+ it('error is shown to the user', () => {
+ expect(createAlert).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/frontend/runner/utils_spec.js b/spec/frontend/runner/utils_spec.js
index 1db9815dfd8..33de1345f85 100644
--- a/spec/frontend/runner/utils_spec.js
+++ b/spec/frontend/runner/utils_spec.js
@@ -1,4 +1,4 @@
-import { formatJobCount, tableField, getPaginationVariables } from '~/runner/utils';
+import { formatJobCount, tableField, getPaginationVariables, parseInterval } from '~/runner/utils';
describe('~/runner/utils', () => {
describe('formatJobCount', () => {
@@ -66,4 +66,15 @@ describe('~/runner/utils', () => {
expect(getPaginationVariables(pagination, pageSize)).toEqual(variables);
});
});
+
+ describe('parseInterval', () => {
+ it.each`
+ case | argument | returnValue
+ ${'parses integer'} | ${'86400'} | ${86400}
+ ${'returns null for undefined'} | ${undefined} | ${null}
+ ${'returns null for null'} | ${null} | ${null}
+ `('$case', ({ argument, returnValue }) => {
+ expect(parseInterval(argument)).toStrictEqual(returnValue);
+ });
+ });
});
diff --git a/spec/frontend/search/sidebar/components/radio_filter_spec.js b/spec/frontend/search/sidebar/components/radio_filter_spec.js
index 39d5ee581ec..c0a8259b4fe 100644
--- a/spec/frontend/search/sidebar/components/radio_filter_spec.js
+++ b/spec/frontend/search/sidebar/components/radio_filter_spec.js
@@ -44,7 +44,7 @@ describe('RadioFilter', () => {
});
const findGlRadioButtonGroup = () => wrapper.find(GlFormRadioGroup);
- const findGlRadioButtons = () => findGlRadioButtonGroup().findAll(GlFormRadio);
+ const findGlRadioButtons = () => findGlRadioButtonGroup().findAllComponents(GlFormRadio);
const findGlRadioButtonsText = () => findGlRadioButtons().wrappers.map((w) => w.text());
describe('template', () => {
diff --git a/spec/frontend/search/sort/components/app_spec.js b/spec/frontend/search/sort/components/app_spec.js
index 04520a3e704..0e8eebba3cb 100644
--- a/spec/frontend/search/sort/components/app_spec.js
+++ b/spec/frontend/search/sort/components/app_spec.js
@@ -46,7 +46,7 @@ describe('GlobalSearchSort', () => {
const findSortButtonGroup = () => wrapper.find(GlButtonGroup);
const findSortDropdown = () => wrapper.find(GlDropdown);
const findSortDirectionButton = () => wrapper.find(GlButton);
- const findDropdownItems = () => findSortDropdown().findAll(GlDropdownItem);
+ const findDropdownItems = () => findSortDropdown().findAllComponents(GlDropdownItem);
const findDropdownItemsText = () => findDropdownItems().wrappers.map((w) => w.text());
describe('template', () => {
diff --git a/spec/frontend/set_status_modal/set_status_form_spec.js b/spec/frontend/set_status_modal/set_status_form_spec.js
new file mode 100644
index 00000000000..8e1623eedf5
--- /dev/null
+++ b/spec/frontend/set_status_modal/set_status_form_spec.js
@@ -0,0 +1,167 @@
+import $ from 'jquery';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import SetStatusForm from '~/set_status_modal/set_status_form.vue';
+import EmojiPicker from '~/emoji/components/picker.vue';
+import { timeRanges } from '~/vue_shared/constants';
+import { sprintf } from '~/locale';
+import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
+
+describe('SetStatusForm', () => {
+ let wrapper;
+
+ const defaultPropsData = {
+ defaultEmoji: 'speech_balloon',
+ emoji: 'thumbsup',
+ message: 'Foo bar',
+ availability: false,
+ };
+
+ const createComponent = async ({ propsData = {} } = {}) => {
+ wrapper = mountExtended(SetStatusForm, {
+ propsData: {
+ ...defaultPropsData,
+ ...propsData,
+ },
+ });
+
+ await waitForPromises();
+ };
+
+ const findMessageInput = () =>
+ wrapper.findByPlaceholderText(SetStatusForm.i18n.statusMessagePlaceholder);
+ const findSelectedEmoji = (emoji) =>
+ wrapper.findByTestId('selected-emoji').find(`gl-emoji[data-name="${emoji}"]`);
+
+ it('sets up emoji autocomplete for the message input', async () => {
+ const gfmAutoCompleteSetupSpy = jest.spyOn(GfmAutoComplete.prototype, 'setup');
+
+ await createComponent();
+
+ expect(gfmAutoCompleteSetupSpy).toHaveBeenCalledWith($(findMessageInput().element), {
+ emojis: true,
+ });
+ });
+
+ describe('when emoji is set', () => {
+ it('displays emoji', async () => {
+ await createComponent();
+
+ expect(findSelectedEmoji(defaultPropsData.emoji).exists()).toBe(true);
+ });
+ });
+
+ describe('when emoji is not set and message is changed', () => {
+ it('displays default emoji', async () => {
+ await createComponent({
+ propsData: {
+ emoji: '',
+ },
+ });
+
+ await findMessageInput().trigger('keyup');
+
+ expect(findSelectedEmoji(defaultPropsData.defaultEmoji).exists()).toBe(true);
+ });
+ });
+
+ describe('when message is set', () => {
+ it('displays filled in message input', async () => {
+ await createComponent();
+
+ expect(findMessageInput().element.value).toBe(defaultPropsData.message);
+ });
+ });
+
+ describe('when clear status after is set', () => {
+ it('displays value in dropdown toggle button', async () => {
+ const clearStatusAfter = timeRanges[0];
+
+ await createComponent({
+ propsData: {
+ clearStatusAfter,
+ },
+ });
+
+ expect(wrapper.findByRole('button', { name: clearStatusAfter.label }).exists()).toBe(true);
+ });
+ });
+
+ describe('when emoji is changed', () => {
+ beforeEach(async () => {
+ await createComponent();
+
+ wrapper.findComponent(EmojiPicker).vm.$emit('click', defaultPropsData.emoji);
+ });
+
+ it('emits `emoji-click` event', () => {
+ expect(wrapper.emitted('emoji-click')).toEqual([[defaultPropsData.emoji]]);
+ });
+ });
+
+ describe('when message is changed', () => {
+ it('emits `message-input` event', async () => {
+ await createComponent();
+
+ const newMessage = 'Foo bar baz';
+
+ await findMessageInput().setValue(newMessage);
+
+ expect(wrapper.emitted('message-input')).toEqual([[newMessage]]);
+ });
+ });
+
+ describe('when availability checkbox is changed', () => {
+ it('emits `availability-input` event', async () => {
+ await createComponent();
+
+ await wrapper
+ .findByLabelText(
+ `${SetStatusForm.i18n.availabilityCheckboxLabel} ${SetStatusForm.i18n.availabilityCheckboxHelpText}`,
+ )
+ .setChecked();
+
+ expect(wrapper.emitted('availability-input')).toEqual([[true]]);
+ });
+ });
+
+ describe('when `Clear status after` dropdown is changed', () => {
+ it('emits `clear-status-after-click`', async () => {
+ await wrapper.findByTestId('thirtyMinutes').trigger('click');
+
+ expect(wrapper.emitted('clear-status-after-click')).toEqual([[timeRanges[0]]]);
+ });
+ });
+
+ describe('when clear status button is clicked', () => {
+ beforeEach(async () => {
+ await createComponent();
+
+ await wrapper
+ .findByRole('button', { name: SetStatusForm.i18n.clearStatusButtonLabel })
+ .trigger('click');
+ });
+
+ it('clears emoji and message', () => {
+ expect(wrapper.emitted('emoji-click')).toEqual([['']]);
+ expect(wrapper.emitted('message-input')).toEqual([['']]);
+ expect(wrapper.findByTestId('no-emoji-placeholder').exists()).toBe(true);
+ });
+ });
+
+ describe('when `currentClearStatusAfter` prop is set', () => {
+ it('displays clear status message', async () => {
+ const date = '2022-08-25 21:14:48 UTC';
+
+ await createComponent({
+ propsData: {
+ currentClearStatusAfter: date,
+ },
+ });
+
+ expect(
+ wrapper.findByText(sprintf(SetStatusForm.i18n.clearStatusAfterMessage, { date })).exists(),
+ ).toBe(true);
+ });
+ });
+});
diff --git a/spec/frontend/set_status_modal/set_status_modal_wrapper_spec.js b/spec/frontend/set_status_modal/set_status_modal_wrapper_spec.js
index e3b5478290a..c5fb590646d 100644
--- a/spec/frontend/set_status_modal/set_status_modal_wrapper_spec.js
+++ b/spec/frontend/set_status_modal/set_status_modal_wrapper_spec.js
@@ -1,14 +1,14 @@
import { GlModal, GlFormCheckbox } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
import { initEmojiMock, clearEmojiMock } from 'helpers/emoji';
import * as UserApi from '~/api/user_api';
import EmojiPicker from '~/emoji/components/picker.vue';
import createFlash from '~/flash';
import stubChildren from 'helpers/stub_children';
-import SetStatusModalWrapper, {
- AVAILABILITY_STATUS,
-} from '~/set_status_modal/set_status_modal_wrapper.vue';
+import SetStatusModalWrapper from '~/set_status_modal/set_status_modal_wrapper.vue';
+import { AVAILABILITY_STATUS } from '~/set_status_modal/constants';
+import SetStatusForm from '~/set_status_modal/set_status_form.vue';
jest.mock('~/flash');
@@ -33,7 +33,7 @@ describe('SetStatusModalWrapper', () => {
};
const createComponent = (props = {}) => {
- return mount(SetStatusModalWrapper, {
+ return mountExtended(SetStatusModalWrapper, {
propsData: {
...defaultProps,
...props,
@@ -42,6 +42,7 @@ describe('SetStatusModalWrapper', () => {
...stubChildren(SetStatusModalWrapper),
GlFormInput: false,
GlFormInputGroup: false,
+ SetStatusForm: false,
EmojiPicker: EmojiPickerStub,
},
mocks: {
@@ -51,7 +52,8 @@ describe('SetStatusModalWrapper', () => {
};
const findModal = () => wrapper.find(GlModal);
- const findFormField = (field) => wrapper.find(`[name="user[status][${field}]"]`);
+ const findMessageField = () =>
+ wrapper.findByPlaceholderText(SetStatusForm.i18n.statusMessagePlaceholder);
const findClearStatusButton = () => wrapper.find('.js-clear-user-status-button');
const findAvailabilityCheckbox = () => wrapper.find(GlFormCheckbox);
const findClearStatusAtMessage = () => wrapper.find('[data-testid="clear-status-at-message"]');
@@ -81,14 +83,8 @@ describe('SetStatusModalWrapper', () => {
return initModal();
});
- it('sets the hidden status emoji field', () => {
- const field = findFormField('emoji');
- expect(field.exists()).toBe(true);
- expect(field.element.value).toBe(defaultEmoji);
- });
-
it('sets the message field', () => {
- const field = findFormField('message');
+ const field = findMessageField();
expect(field.exists()).toBe(true);
expect(field.element.value).toBe(defaultMessage);
});
@@ -118,10 +114,10 @@ describe('SetStatusModalWrapper', () => {
});
});
- it('sets emojiTag when clicking in emoji picker', async () => {
+ it('passes emoji to `SetStatusForm`', async () => {
await getEmojiPicker().vm.$emit('click', 'thumbsup');
- expect(wrapper.vm.emojiTag).toContain('data-name="thumbsup"');
+ expect(wrapper.findComponent(SetStatusForm).props('emoji')).toBe('thumbsup');
});
});
@@ -133,7 +129,7 @@ describe('SetStatusModalWrapper', () => {
});
it('does not set the message field', () => {
- expect(findFormField('message').element.value).toBe('');
+ expect(findMessageField().element.value).toBe('');
});
it('hides the clear status button', () => {
@@ -141,18 +137,6 @@ describe('SetStatusModalWrapper', () => {
});
});
- describe('with no currentEmoji set', () => {
- beforeEach(async () => {
- await initEmojiMock();
- wrapper = createComponent({ currentEmoji: '' });
- return initModal();
- });
-
- it('does not set the hidden status emoji field', () => {
- expect(findFormField('emoji').element.value).toBe('');
- });
- });
-
describe('with currentClearStatusAfter set', () => {
beforeEach(async () => {
await initEmojiMock();
@@ -182,8 +166,7 @@ describe('SetStatusModalWrapper', () => {
findModal().vm.$emit('secondary');
await nextTick();
- expect(findFormField('message').element.value).toBe('');
- expect(findFormField('emoji').element.value).toBe('');
+ expect(findMessageField().element.value).toBe('');
});
it('clicking "setStatus" submits the user status', async () => {
@@ -194,7 +177,7 @@ describe('SetStatusModalWrapper', () => {
findAvailabilityCheckbox().vm.$emit('input', true);
// set the currentClearStatusAfter to 30 minutes
- wrapper.find('[data-testid="thirtyMinutes"]').vm.$emit('click');
+ wrapper.find('[data-testid="thirtyMinutes"]').trigger('click');
findModal().vm.$emit('primary');
await nextTick();
diff --git a/spec/frontend/set_status_modal/user_profile_set_status_wrapper_spec.js b/spec/frontend/set_status_modal/user_profile_set_status_wrapper_spec.js
new file mode 100644
index 00000000000..eaee0e77311
--- /dev/null
+++ b/spec/frontend/set_status_modal/user_profile_set_status_wrapper_spec.js
@@ -0,0 +1,156 @@
+import { nextTick } from 'vue';
+import { cloneDeep } from 'lodash';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import { resetHTMLFixture } from 'helpers/fixtures';
+import { useFakeDate } from 'helpers/fake_date';
+import UserProfileSetStatusWrapper from '~/set_status_modal/user_profile_set_status_wrapper.vue';
+import SetStatusForm from '~/set_status_modal/set_status_form.vue';
+import { TIME_RANGES_WITH_NEVER, NEVER_TIME_RANGE } from '~/set_status_modal/constants';
+
+describe('UserProfileSetStatusWrapper', () => {
+ let wrapper;
+
+ const defaultProvide = {
+ fields: {
+ emoji: { name: 'user[status][emoji]', id: 'user_status_emoji', value: '8ball' },
+ message: { name: 'user[status][message]', id: 'user_status_message', value: 'foo bar' },
+ availability: {
+ name: 'user[status][availability]',
+ id: 'user_status_availability',
+ value: 'busy',
+ },
+ clearStatusAfter: {
+ name: 'user[status][clear_status_after]',
+ id: 'user_status_clear_status_after',
+ value: '2022-09-03 03:06:26 UTC',
+ },
+ },
+ };
+
+ const createComponent = ({ provide = {} } = {}) => {
+ wrapper = mountExtended(UserProfileSetStatusWrapper, {
+ provide: {
+ ...defaultProvide,
+ ...provide,
+ },
+ });
+ };
+
+ const findInput = (name) => wrapper.find(`[name="${name}"]`);
+ const findSetStatusForm = () => wrapper.findComponent(SetStatusForm);
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders `SetStatusForm` component and passes expected props', () => {
+ createComponent();
+
+ expect(cloneDeep(findSetStatusForm().props())).toMatchObject({
+ defaultEmoji: 'speech_balloon',
+ emoji: defaultProvide.fields.emoji.value,
+ message: defaultProvide.fields.message.value,
+ availability: true,
+ clearStatusAfter: NEVER_TIME_RANGE,
+ currentClearStatusAfter: defaultProvide.fields.clearStatusAfter.value,
+ });
+ });
+
+ it.each`
+ input
+ ${'emoji'}
+ ${'message'}
+ ${'availability'}
+ `('renders hidden $input input with value set', ({ input }) => {
+ createComponent();
+
+ expect(findInput(defaultProvide.fields[input].name).attributes('value')).toBe(
+ defaultProvide.fields[input].value,
+ );
+ });
+
+ describe('when clear status after dropdown is set to `Never`', () => {
+ it('renders hidden clear status after input with value unset', () => {
+ createComponent();
+
+ expect(
+ findInput(defaultProvide.fields.clearStatusAfter.name).attributes('value'),
+ ).toBeUndefined();
+ });
+ });
+
+ describe('when clear status after dropdown has a value selected', () => {
+ it('renders hidden clear status after input with value set', async () => {
+ createComponent();
+
+ findSetStatusForm().vm.$emit('clear-status-after-click', TIME_RANGES_WITH_NEVER[1]);
+
+ await nextTick();
+
+ expect(findInput(defaultProvide.fields.clearStatusAfter.name).attributes('value')).toBe(
+ TIME_RANGES_WITH_NEVER[1].shortcut,
+ );
+ });
+ });
+
+ describe('when emoji is changed', () => {
+ it('updates hidden emoji input value', async () => {
+ createComponent();
+
+ const newEmoji = 'basketball';
+
+ findSetStatusForm().vm.$emit('emoji-click', newEmoji);
+
+ await nextTick();
+
+ expect(findInput(defaultProvide.fields.emoji.name).attributes('value')).toBe(newEmoji);
+ });
+ });
+
+ describe('when message is changed', () => {
+ it('updates hidden message input value', async () => {
+ createComponent();
+
+ const newMessage = 'foo bar baz';
+
+ findSetStatusForm().vm.$emit('message-input', newMessage);
+
+ await nextTick();
+
+ expect(findInput(defaultProvide.fields.message.name).attributes('value')).toBe(newMessage);
+ });
+ });
+
+ describe('when form is successfully submitted', () => {
+ // 2022-09-02 00:00:00 UTC
+ useFakeDate(2022, 8, 2);
+
+ const form = document.createElement('form');
+ form.classList.add('js-edit-user');
+
+ beforeEach(async () => {
+ document.body.appendChild(form);
+ createComponent();
+
+ const oneDay = TIME_RANGES_WITH_NEVER[4];
+
+ findSetStatusForm().vm.$emit('clear-status-after-click', oneDay);
+
+ await nextTick();
+
+ form.dispatchEvent(new Event('ajax:success'));
+ });
+
+ afterEach(() => {
+ resetHTMLFixture();
+ });
+
+ it('updates clear status after dropdown to `Never`', () => {
+ expect(findSetStatusForm().props('clearStatusAfter')).toBe(NEVER_TIME_RANGE);
+ });
+
+ it('updates `currentClearStatusAfter` prop', () => {
+ expect(findSetStatusForm().props('currentClearStatusAfter')).toBe('2022-09-03 00:00:00 UTC');
+ });
+ });
+});
diff --git a/spec/frontend/set_status_modal/utils_spec.js b/spec/frontend/set_status_modal/utils_spec.js
index 273f30f8311..1e918b75a98 100644
--- a/spec/frontend/set_status_modal/utils_spec.js
+++ b/spec/frontend/set_status_modal/utils_spec.js
@@ -1,4 +1,5 @@
-import { AVAILABILITY_STATUS, isUserBusy } from '~/set_status_modal/utils';
+import { isUserBusy } from '~/set_status_modal/utils';
+import { AVAILABILITY_STATUS } from '~/set_status_modal/constants';
describe('Set status modal utils', () => {
describe('isUserBusy', () => {
diff --git a/spec/frontend/sidebar/assignee_title_spec.js b/spec/frontend/sidebar/assignee_title_spec.js
index 3079cb28406..e29e3d489a5 100644
--- a/spec/frontend/sidebar/assignee_title_spec.js
+++ b/spec/frontend/sidebar/assignee_title_spec.js
@@ -85,7 +85,7 @@ describe('AssigneeTitle component', () => {
editable: false,
});
- expect(wrapper.find(GlLoadingIcon).exists()).toBeFalsy();
+ expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
});
it('renders spinner when loading', () => {
@@ -95,7 +95,7 @@ describe('AssigneeTitle component', () => {
editable: false,
});
- expect(wrapper.find(GlLoadingIcon).exists()).toBeTruthy();
+ expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
});
it('does not render edit link when not editable', () => {
diff --git a/spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js b/spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js
index 517b4f12559..8cde70ff8da 100644
--- a/spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js
+++ b/spec/frontend/sidebar/components/assignees/assignee_avatar_link_spec.js
@@ -143,7 +143,7 @@ describe('AssigneeAvatarLink component', () => {
issuableType | userId
${'merge_request'} | ${undefined}
${'issue'} | ${'1'}
- `('it sets data-user-id as $userId for $issuableType', ({ issuableType, userId }) => {
+ `('sets data-user-id as $userId for $issuableType', ({ issuableType, userId }) => {
createComponent({
issuableType,
});
diff --git a/spec/frontend/sidebar/components/assignees/collapsed_assignee_list_spec.js b/spec/frontend/sidebar/components/assignees/collapsed_assignee_list_spec.js
index 5aa8264b98c..81ff51133bf 100644
--- a/spec/frontend/sidebar/components/assignees/collapsed_assignee_list_spec.js
+++ b/spec/frontend/sidebar/components/assignees/collapsed_assignee_list_spec.js
@@ -23,7 +23,7 @@ describe('CollapsedAssigneeList component', () => {
const findNoUsersIcon = () => wrapper.find(GlIcon);
const findAvatarCounter = () => wrapper.find('.avatar-counter');
- const findAssignees = () => wrapper.findAll(CollapsedAssignee);
+ const findAssignees = () => wrapper.findAllComponents(CollapsedAssignee);
const getTooltipTitle = () => wrapper.attributes('title');
afterEach(() => {
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 88015ed42a3..3644a51c7fd 100644
--- a/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
+++ b/spec/frontend/sidebar/components/assignees/sidebar_assignees_widget_spec.js
@@ -65,6 +65,7 @@ describe('Sidebar assignees widget', () => {
issuableId: 0,
fullPath: '/mygroup/myProject',
allowMultipleAssignees: true,
+ editable: true,
...props,
},
provide: {
@@ -350,6 +351,17 @@ describe('Sidebar assignees widget', () => {
});
});
+ describe('when issuable is not editable by the user', () => {
+ beforeEach(async () => {
+ createComponent({ props: { editable: false } });
+ await waitForPromises();
+ });
+
+ it('passes editable prop as false to IssuableAssignees', () => {
+ expect(findAssignees().props('editable')).toBe(false);
+ });
+ });
+
it('includes the real-time assignees component', async () => {
createComponent();
await waitForPromises();
diff --git a/spec/frontend/sidebar/components/assignees/uncollapsed_assignee_list_spec.js b/spec/frontend/sidebar/components/assignees/uncollapsed_assignee_list_spec.js
index f7437386814..b902d7313fd 100644
--- a/spec/frontend/sidebar/components/assignees/uncollapsed_assignee_list_spec.js
+++ b/spec/frontend/sidebar/components/assignees/uncollapsed_assignee_list_spec.js
@@ -42,7 +42,7 @@ describe('UncollapsedAssigneeList component', () => {
});
it('only has one user', () => {
- expect(wrapper.findAll(AssigneeAvatarLink).length).toBe(1);
+ expect(wrapper.findAllComponents(AssigneeAvatarLink).length).toBe(1);
});
it('calls the AssigneeAvatarLink with the proper props', () => {
@@ -79,7 +79,7 @@ describe('UncollapsedAssigneeList component', () => {
});
it('shows truncated users', () => {
- expect(wrapper.findAll(AssigneeAvatarLink).length).toBe(DEFAULT_RENDER_COUNT);
+ expect(wrapper.findAllComponents(AssigneeAvatarLink).length).toBe(DEFAULT_RENDER_COUNT);
});
describe('when more button is clicked', () => {
@@ -94,7 +94,9 @@ describe('UncollapsedAssigneeList component', () => {
});
it('shows all users', () => {
- expect(wrapper.findAll(AssigneeAvatarLink).length).toBe(DEFAULT_RENDER_COUNT + 1);
+ expect(wrapper.findAllComponents(AssigneeAvatarLink).length).toBe(
+ DEFAULT_RENDER_COUNT + 1,
+ );
});
});
});
diff --git a/spec/frontend/sidebar/components/assignees/user_name_with_status_spec.js b/spec/frontend/sidebar/components/assignees/user_name_with_status_spec.js
index 4dbf3d426bb..37c16bc9235 100644
--- a/spec/frontend/sidebar/components/assignees/user_name_with_status_spec.js
+++ b/spec/frontend/sidebar/components/assignees/user_name_with_status_spec.js
@@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils';
-import { AVAILABILITY_STATUS } from '~/set_status_modal/utils';
+import { AVAILABILITY_STATUS } from '~/set_status_modal/constants';
import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue';
const name = 'Administrator';
diff --git a/spec/frontend/sidebar/components/confidential/sidebar_confidentiality_form_spec.js b/spec/frontend/sidebar/components/confidential/sidebar_confidentiality_form_spec.js
index 7775ed6aa37..1ea035c7184 100644
--- a/spec/frontend/sidebar/components/confidential/sidebar_confidentiality_form_spec.js
+++ b/spec/frontend/sidebar/components/confidential/sidebar_confidentiality_form_spec.js
@@ -71,12 +71,7 @@ describe('Sidebar Confidentiality Form', () => {
it('creates a flash if mutation contains errors', async () => {
createComponent({
mutate: jest.fn().mockResolvedValue({
- data: {
- issuableSetConfidential: {
- issuable: { confidential: false },
- errors: ['Houston, we have a problem!'],
- },
- },
+ data: { issuableSetConfidential: { errors: ['Houston, we have a problem!'] } },
}),
});
findConfidentialToggle().vm.$emit('click', new MouseEvent('click'));
@@ -87,24 +82,6 @@ describe('Sidebar Confidentiality Form', () => {
});
});
- it('emits `closeForm` event with confidentiality value when mutation is successful', async () => {
- createComponent({
- mutate: jest.fn().mockResolvedValue({
- data: {
- issuableSetConfidential: {
- issuable: { confidential: true },
- errors: [],
- },
- },
- }),
- });
-
- findConfidentialToggle().vm.$emit('click', new MouseEvent('click'));
- await waitForPromises();
-
- expect(wrapper.emitted('closeForm')).toEqual([[{ confidential: true }]]);
- });
-
describe('when issue is not confidential', () => {
beforeEach(() => {
createComponent();
diff --git a/spec/frontend/sidebar/components/confidential/sidebar_confidentiality_widget_spec.js b/spec/frontend/sidebar/components/confidential/sidebar_confidentiality_widget_spec.js
index 18ee423d12e..3a3f0b1d9fa 100644
--- a/spec/frontend/sidebar/components/confidential/sidebar_confidentiality_widget_spec.js
+++ b/spec/frontend/sidebar/components/confidential/sidebar_confidentiality_widget_spec.js
@@ -132,7 +132,6 @@ describe('Sidebar Confidentiality Widget', () => {
it('closes the form and dispatches an event when `closeForm` is emitted', async () => {
createComponent();
const el = wrapper.vm.$el;
- const closeFormPayload = { confidential: true };
jest.spyOn(el, 'dispatchEvent');
await waitForPromises();
@@ -141,12 +140,12 @@ describe('Sidebar Confidentiality Widget', () => {
expect(findConfidentialityForm().isVisible()).toBe(true);
- findConfidentialityForm().vm.$emit('closeForm', closeFormPayload);
+ findConfidentialityForm().vm.$emit('closeForm');
await nextTick();
expect(findConfidentialityForm().isVisible()).toBe(false);
expect(el.dispatchEvent).toHaveBeenCalled();
- expect(wrapper.emitted('closeForm')).toEqual([[closeFormPayload]]);
+ expect(wrapper.emitted('closeForm')).toEqual([[]]);
});
it('emits `expandSidebar` event when it is emitted from child component', async () => {
diff --git a/spec/frontend/sidebar/components/date/sidebar_inherit_date_spec.js b/spec/frontend/sidebar/components/date/sidebar_inherit_date_spec.js
index fda21e06987..a7556b9110c 100644
--- a/spec/frontend/sidebar/components/date/sidebar_inherit_date_spec.js
+++ b/spec/frontend/sidebar/components/date/sidebar_inherit_date_spec.js
@@ -5,10 +5,10 @@ import SidebarInheritDate from '~/sidebar/components/date/sidebar_inherit_date.v
describe('SidebarInheritDate', () => {
let wrapper;
- const findFixedFormattedDate = () => wrapper.findAll(SidebarFormattedDate).at(0);
- const findInheritFormattedDate = () => wrapper.findAll(SidebarFormattedDate).at(1);
- const findFixedRadio = () => wrapper.findAll(GlFormRadio).at(0);
- const findInheritRadio = () => wrapper.findAll(GlFormRadio).at(1);
+ const findFixedFormattedDate = () => wrapper.findAllComponents(SidebarFormattedDate).at(0);
+ const findInheritFormattedDate = () => wrapper.findAllComponents(SidebarFormattedDate).at(1);
+ const findFixedRadio = () => wrapper.findAllComponents(GlFormRadio).at(0);
+ const findInheritRadio = () => wrapper.findAllComponents(GlFormRadio).at(1);
const createComponent = ({ dueDateIsFixed = false } = {}) => {
wrapper = shallowMount(SidebarInheritDate, {
@@ -36,8 +36,8 @@ describe('SidebarInheritDate', () => {
});
it('displays formatted fixed and inherited dates with radio buttons', () => {
- expect(wrapper.findAll(SidebarFormattedDate)).toHaveLength(2);
- expect(wrapper.findAll(GlFormRadio)).toHaveLength(2);
+ expect(wrapper.findAllComponents(SidebarFormattedDate)).toHaveLength(2);
+ expect(wrapper.findAllComponents(GlFormRadio)).toHaveLength(2);
expect(findFixedFormattedDate().props('formattedDate')).toBe('Apr 15, 2021');
expect(findInheritFormattedDate().props('formattedDate')).toBe('May 15, 2021');
expect(findFixedRadio().text()).toBe('Fixed:');
diff --git a/spec/frontend/sidebar/components/reviewers/uncollapsed_reviewer_list_spec.js b/spec/frontend/sidebar/components/reviewers/uncollapsed_reviewer_list_spec.js
index 2c24df2436a..d00c8dcb653 100644
--- a/spec/frontend/sidebar/components/reviewers/uncollapsed_reviewer_list_spec.js
+++ b/spec/frontend/sidebar/components/reviewers/uncollapsed_reviewer_list_spec.js
@@ -52,7 +52,7 @@ describe('UncollapsedReviewerList component', () => {
});
it('only has one user', () => {
- expect(wrapper.findAll(ReviewerAvatarLink).length).toBe(1);
+ expect(wrapper.findAllComponents(ReviewerAvatarLink).length).toBe(1);
});
it('shows one user with avatar, and author name', () => {
@@ -96,7 +96,7 @@ describe('UncollapsedReviewerList component', () => {
});
it('has both users', () => {
- expect(wrapper.findAll(ReviewerAvatarLink).length).toBe(2);
+ expect(wrapper.findAllComponents(ReviewerAvatarLink).length).toBe(2);
});
it('shows both users with avatar, and author name', () => {
diff --git a/spec/frontend/sidebar/components/severity/sidebar_severity_spec.js b/spec/frontend/sidebar/components/severity/sidebar_severity_spec.js
index 5d80a221d8e..83eb9a18597 100644
--- a/spec/frontend/sidebar/components/severity/sidebar_severity_spec.js
+++ b/spec/frontend/sidebar/components/severity/sidebar_severity_spec.js
@@ -97,7 +97,7 @@ describe('SidebarSeverity', () => {
});
});
- it('shows error alert when severity update fails ', async () => {
+ it('shows error alert when severity update fails', async () => {
const errorMsg = 'Something went wrong';
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValueOnce(errorMsg);
findCriticalSeverityDropdownItem().vm.$emit('click');
diff --git a/spec/frontend/sidebar/components/sidebar_dropdown_widget_spec.js b/spec/frontend/sidebar/components/sidebar_dropdown_widget_spec.js
index 8ebd2dabfc2..6761731c093 100644
--- a/spec/frontend/sidebar/components/sidebar_dropdown_widget_spec.js
+++ b/spec/frontend/sidebar/components/sidebar_dropdown_widget_spec.js
@@ -238,6 +238,24 @@ describe('SidebarDropdownWidget', () => {
expect(findSelectedAttribute().text()).toBe('None');
});
});
+
+ describe("when user doesn't have permission to view current attribute", () => {
+ it('renders no permission text', () => {
+ createComponent({
+ data: {
+ hasCurrentAttribute: true,
+ currentAttribute: null,
+ },
+ queries: {
+ currentAttribute: { loading: false },
+ },
+ });
+
+ expect(findSelectedAttribute().text()).toBe(
+ `You don't have permission to view this ${wrapper.props('issuableAttribute')}.`,
+ );
+ });
+ });
});
describe('when a user can edit', () => {
diff --git a/spec/frontend/sidebar/components/subscriptions/sidebar_subscriptions_widget_spec.js b/spec/frontend/sidebar/components/subscriptions/sidebar_subscriptions_widget_spec.js
index 9a68940590d..430acf9f9e7 100644
--- a/spec/frontend/sidebar/components/subscriptions/sidebar_subscriptions_widget_spec.js
+++ b/spec/frontend/sidebar/components/subscriptions/sidebar_subscriptions_widget_spec.js
@@ -156,7 +156,7 @@ describe('Sidebar Subscriptions Widget', () => {
});
await waitForPromises();
- await wrapper.find('.dropdown-item').trigger('click');
+ await wrapper.find('[data-testid="notifications-toggle"]').vm.$emit('change');
await waitForPromises();
diff --git a/spec/frontend/sidebar/components/time_tracking/report_spec.js b/spec/frontend/sidebar/components/time_tracking/report_spec.js
index 5ed8810e95e..4e619a4e609 100644
--- a/spec/frontend/sidebar/components/time_tracking/report_spec.js
+++ b/spec/frontend/sidebar/components/time_tracking/report_spec.js
@@ -161,7 +161,6 @@ describe('Issuable Time Tracking Report', () => {
id: timelogToRemoveId,
},
},
- update: expect.anything(),
});
});
@@ -179,7 +178,6 @@ describe('Issuable Time Tracking Report', () => {
id: timelogToRemoveId,
},
},
- update: expect.anything(),
});
expect(createFlash).toHaveBeenCalledWith({
diff --git a/spec/frontend/sidebar/issuable_assignees_spec.js b/spec/frontend/sidebar/issuable_assignees_spec.js
index 3563d478f3f..dc59b68bbd4 100644
--- a/spec/frontend/sidebar/issuable_assignees_spec.js
+++ b/spec/frontend/sidebar/issuable_assignees_spec.js
@@ -12,6 +12,7 @@ describe('IssuableAssignees', () => {
},
propsData: {
users: [],
+ editable: true,
...props,
},
});
@@ -25,15 +26,19 @@ describe('IssuableAssignees', () => {
});
describe('when no assignees are present', () => {
- it('renders "None - assign yourself" when user is logged in', () => {
- createComponent({ signedIn: true });
- expect(findEmptyAssignee().text()).toBe('None - assign yourself');
- });
-
- it('renders "None" when user is not logged in', () => {
- createComponent();
- expect(findEmptyAssignee().text()).toBe('None');
- });
+ it.each`
+ signedIn | editable | message
+ ${true} | ${true} | ${'None - assign yourself'}
+ ${true} | ${false} | ${'None'}
+ ${false} | ${true} | ${'None'}
+ ${false} | ${false} | ${'None'}
+ `(
+ 'renders "$message" when signedIn is $signedIn and editable is $editable',
+ ({ signedIn, editable, message }) => {
+ createComponent({ signedIn, editable });
+ expect(findEmptyAssignee().text()).toBe(message);
+ },
+ );
});
describe('when assignees are present', () => {
diff --git a/spec/frontend/sidebar/lock/issuable_lock_form_spec.js b/spec/frontend/sidebar/lock/issuable_lock_form_spec.js
index bb757fdf63b..986ccaea4b6 100644
--- a/spec/frontend/sidebar/lock/issuable_lock_form_spec.js
+++ b/spec/frontend/sidebar/lock/issuable_lock_form_spec.js
@@ -130,7 +130,7 @@ describe('IssuableLockForm', () => {
expect(findEditForm().exists()).toBe(true);
});
- it('tracks the event ', () => {
+ it('tracks the event', () => {
const spy = mockTracking('_category_', wrapper.element, jest.spyOn);
triggerEvent(findEditLink().element);
diff --git a/spec/frontend/sidebar/mock_data.js b/spec/frontend/sidebar/mock_data.js
index 9c6e23e928c..2afe9647cbe 100644
--- a/spec/frontend/sidebar/mock_data.js
+++ b/spec/frontend/sidebar/mock_data.js
@@ -497,6 +497,11 @@ export const searchResponse = {
user: mockUser2,
},
],
+ pageInfo: {
+ hasNextPage: false,
+ endCursor: null,
+ startCursor: null,
+ },
},
},
},
@@ -559,6 +564,11 @@ export const projectMembersResponse = {
},
},
],
+ pageInfo: {
+ hasNextPage: false,
+ startCursor: null,
+ endCursor: null,
+ },
},
},
},
diff --git a/spec/frontend/sidebar/sidebar_mediator_spec.js b/spec/frontend/sidebar/sidebar_mediator_spec.js
index e32694abcce..355f0c45bbe 100644
--- a/spec/frontend/sidebar/sidebar_mediator_spec.js
+++ b/spec/frontend/sidebar/sidebar_mediator_spec.js
@@ -27,7 +27,7 @@ describe('Sidebar mediator', () => {
mock.restore();
});
- it('assigns yourself ', () => {
+ it('assigns yourself', () => {
mediator.assignYourself();
expect(mediator.store.currentUser).toEqual(mediatorMockData.currentUser);
diff --git a/spec/frontend/sidebar/sidebar_move_issue_spec.js b/spec/frontend/sidebar/sidebar_move_issue_spec.js
index 7bb7b18adf8..2e6807ed9d8 100644
--- a/spec/frontend/sidebar/sidebar_move_issue_spec.js
+++ b/spec/frontend/sidebar/sidebar_move_issue_spec.js
@@ -7,6 +7,7 @@ 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');
@@ -75,7 +76,9 @@ describe('SidebarMoveIssue', () => {
it('should initialize the deprecatedJQueryDropdown', () => {
test.sidebarMoveIssue.initDropdown();
- expect(test.sidebarMoveIssue.$dropdownToggle.data('deprecatedJQueryDropdown')).toBeTruthy();
+ expect(test.sidebarMoveIssue.$dropdownToggle.data('deprecatedJQueryDropdown')).toBeInstanceOf(
+ GitLabDropdown,
+ );
});
it('escapes html from project name', async () => {
@@ -97,7 +100,7 @@ describe('SidebarMoveIssue', () => {
test.sidebarMoveIssue.onConfirmClicked();
expect(test.mediator.moveIssue).toHaveBeenCalled();
- expect(test.$confirmButton.prop('disabled')).toBeTruthy();
+ expect(test.$confirmButton.prop('disabled')).toBe(true);
expect(test.$confirmButton.hasClass('is-loading')).toBe(true);
});
@@ -113,7 +116,7 @@ describe('SidebarMoveIssue', () => {
await waitForPromises();
expect(createFlash).toHaveBeenCalled();
- expect(test.$confirmButton.prop('disabled')).toBeFalsy();
+ expect(test.$confirmButton.prop('disabled')).toBe(false);
expect(test.$confirmButton.hasClass('is-loading')).toBe(false);
});
@@ -139,7 +142,7 @@ describe('SidebarMoveIssue', () => {
test.$content.find('.js-move-issue-dropdown-item').eq(0).trigger('click');
expect(test.mediator.setMoveToProjectId).toHaveBeenCalledWith(0);
- expect(test.$confirmButton.prop('disabled')).toBeTruthy();
+ expect(test.$confirmButton.prop('disabled')).toBe(true);
});
it('should set moveToProjectId on dropdown item click', async () => {
diff --git a/spec/frontend/sidebar/todo_spec.js b/spec/frontend/sidebar/todo_spec.js
index 9316268d2ad..5f696b237e0 100644
--- a/spec/frontend/sidebar/todo_spec.js
+++ b/spec/frontend/sidebar/todo_spec.js
@@ -55,7 +55,7 @@ describe('SidebarTodo', () => {
wrapper.find('button').trigger('click');
await nextTick();
- expect(wrapper.emitted().toggleTodo).toBeTruthy();
+ expect(wrapper.emitted().toggleTodo).toHaveLength(1);
});
it('renders component container element with proper data attributes', () => {
diff --git a/spec/frontend/snippets/components/edit_spec.js b/spec/frontend/snippets/components/edit_spec.js
index f49ceb2fede..cf897414ccb 100644
--- a/spec/frontend/snippets/components/edit_spec.js
+++ b/spec/frontend/snippets/components/edit_spec.js
@@ -16,10 +16,10 @@ import SnippetBlobActionsEdit from '~/snippets/components/snippet_blob_actions_e
import SnippetDescriptionEdit from '~/snippets/components/snippet_description_edit.vue';
import SnippetVisibilityEdit from '~/snippets/components/snippet_visibility_edit.vue';
import {
- SNIPPET_VISIBILITY_PRIVATE,
- SNIPPET_VISIBILITY_INTERNAL,
- SNIPPET_VISIBILITY_PUBLIC,
-} from '~/snippets/constants';
+ VISIBILITY_LEVEL_PRIVATE_STRING,
+ VISIBILITY_LEVEL_INTERNAL_STRING,
+ VISIBILITY_LEVEL_PUBLIC_STRING,
+} from '~/visibility_level/constants';
import CreateSnippetMutation from '~/snippets/mutations/create_snippet.mutation.graphql';
import UpdateSnippetMutation from '~/snippets/mutations/update_snippet.mutation.graphql';
import FormFooterActions from '~/vue_shared/components/form/form_footer_actions.vue';
@@ -41,7 +41,7 @@ const TEST_SNIPPET_GID = 'gid://gitlab/PersonalSnippet/42';
const createSnippet = () =>
merge(createGQLSnippet(), {
webUrl: TEST_WEB_URL,
- visibilityLevel: SNIPPET_VISIBILITY_PRIVATE,
+ visibilityLevel: VISIBILITY_LEVEL_PRIVATE_STRING,
});
const createQueryResponse = (obj = {}) =>
@@ -70,7 +70,7 @@ const getApiData = ({
id,
title = '',
description = '',
- visibilityLevel = SNIPPET_VISIBILITY_PRIVATE,
+ visibilityLevel = VISIBILITY_LEVEL_PRIVATE_STRING,
} = {}) => ({
id,
title,
@@ -128,7 +128,10 @@ describe('Snippet Edit app', () => {
const setDescription = (val) =>
wrapper.findComponent(SnippetDescriptionEdit).vm.$emit('input', val);
- const createComponent = ({ props = {}, selectedLevel = SNIPPET_VISIBILITY_PRIVATE } = {}) => {
+ const createComponent = ({
+ props = {},
+ selectedLevel = VISIBILITY_LEVEL_PRIVATE_STRING,
+ } = {}) => {
if (wrapper) {
throw new Error('wrapper already created');
}
@@ -260,17 +263,18 @@ describe('Snippet Edit app', () => {
},
);
- it.each([SNIPPET_VISIBILITY_PRIVATE, SNIPPET_VISIBILITY_INTERNAL, SNIPPET_VISIBILITY_PUBLIC])(
- 'marks %s visibility by default',
- async (visibility) => {
- createComponent({
- props: { snippetGid: '' },
- selectedLevel: visibility,
- });
+ it.each([
+ VISIBILITY_LEVEL_PRIVATE_STRING,
+ VISIBILITY_LEVEL_INTERNAL_STRING,
+ VISIBILITY_LEVEL_PUBLIC_STRING,
+ ])('marks %s visibility by default', async (visibility) => {
+ createComponent({
+ props: { snippetGid: '' },
+ selectedLevel: visibility,
+ });
- expect(wrapper.find(SnippetVisibilityEdit).props('value')).toBe(visibility);
- },
- );
+ expect(wrapper.find(SnippetVisibilityEdit).props('value')).toBe(visibility);
+ });
describe('form submission handling', () => {
describe('when creating a new snippet', () => {
diff --git a/spec/frontend/snippets/components/show_spec.js b/spec/frontend/snippets/components/show_spec.js
index b29ed97099f..032dcf8e5f5 100644
--- a/spec/frontend/snippets/components/show_spec.js
+++ b/spec/frontend/snippets/components/show_spec.js
@@ -7,10 +7,10 @@ import SnippetBlob from '~/snippets/components/snippet_blob_view.vue';
import SnippetHeader from '~/snippets/components/snippet_header.vue';
import SnippetTitle from '~/snippets/components/snippet_title.vue';
import {
- SNIPPET_VISIBILITY_INTERNAL,
- SNIPPET_VISIBILITY_PRIVATE,
- SNIPPET_VISIBILITY_PUBLIC,
-} from '~/snippets/constants';
+ VISIBILITY_LEVEL_INTERNAL_STRING,
+ VISIBILITY_LEVEL_PRIVATE_STRING,
+ VISIBILITY_LEVEL_PUBLIC_STRING,
+} from '~/visibility_level/constants';
import CloneDropdownButton from '~/vue_shared/components/clone_dropdown.vue';
import { stubPerformanceWebAPI } from 'helpers/performance';
@@ -69,7 +69,7 @@ describe('Snippet view app', () => {
createComponent({
data: {
snippet: {
- visibilityLevel: SNIPPET_VISIBILITY_PUBLIC,
+ visibilityLevel: VISIBILITY_LEVEL_PUBLIC_STRING,
webUrl: 'http://foo.bar',
},
},
@@ -85,7 +85,7 @@ describe('Snippet view app', () => {
},
},
});
- const blobs = wrapper.findAll(SnippetBlob);
+ const blobs = wrapper.findAllComponents(SnippetBlob);
expect(blobs.length).toBe(2);
expect(blobs.at(0).props('blob')).toEqual(Blob);
expect(blobs.at(1).props('blob')).toEqual(BinaryBlob);
@@ -93,11 +93,11 @@ describe('Snippet view app', () => {
describe('Embed dropdown rendering', () => {
it.each`
- visibilityLevel | condition | isRendered
- ${SNIPPET_VISIBILITY_INTERNAL} | ${'not render'} | ${false}
- ${SNIPPET_VISIBILITY_PRIVATE} | ${'not render'} | ${false}
- ${'foo'} | ${'not render'} | ${false}
- ${SNIPPET_VISIBILITY_PUBLIC} | ${'render'} | ${true}
+ visibilityLevel | condition | isRendered
+ ${VISIBILITY_LEVEL_INTERNAL_STRING} | ${'not render'} | ${false}
+ ${VISIBILITY_LEVEL_PRIVATE_STRING} | ${'not render'} | ${false}
+ ${'foo'} | ${'not render'} | ${false}
+ ${VISIBILITY_LEVEL_PUBLIC_STRING} | ${'render'} | ${true}
`('does $condition embed-dropdown by default', ({ visibilityLevel, isRendered }) => {
createComponent({
data: {
diff --git a/spec/frontend/snippets/components/snippet_blob_actions_edit_spec.js b/spec/frontend/snippets/components/snippet_blob_actions_edit_spec.js
index df98312b498..a650353093d 100644
--- a/spec/frontend/snippets/components/snippet_blob_actions_edit_spec.js
+++ b/spec/frontend/snippets/components/snippet_blob_actions_edit_spec.js
@@ -32,7 +32,7 @@ describe('snippets/components/snippet_blob_actions_edit', () => {
};
const findLabel = () => wrapper.findComponent(GlFormGroup);
- const findBlobEdits = () => wrapper.findAll(SnippetBlobEdit);
+ const findBlobEdits = () => wrapper.findAllComponents(SnippetBlobEdit);
const findBlobsData = () =>
findBlobEdits().wrappers.map((x) => ({
blob: x.props('blob'),
diff --git a/spec/frontend/snippets/components/snippet_blob_view_spec.js b/spec/frontend/snippets/components/snippet_blob_view_spec.js
index c395112e313..aa31377f390 100644
--- a/spec/frontend/snippets/components/snippet_blob_view_spec.js
+++ b/spec/frontend/snippets/components/snippet_blob_view_spec.js
@@ -15,7 +15,7 @@ import {
BLOB_RENDER_ERRORS,
} from '~/blob/components/constants';
import SnippetBlobView from '~/snippets/components/snippet_blob_view.vue';
-import { SNIPPET_VISIBILITY_PUBLIC } from '~/snippets/constants';
+import { VISIBILITY_LEVEL_PUBLIC_STRING } from '~/visibility_level/constants';
import { RichViewer, SimpleViewer } from '~/vue_shared/components/blob_viewers';
describe('Blob Embeddable', () => {
@@ -23,7 +23,7 @@ describe('Blob Embeddable', () => {
const snippet = {
id: 'gid://foo.bar/snippet',
webUrl: 'https://foo.bar',
- visibilityLevel: SNIPPET_VISIBILITY_PUBLIC,
+ visibilityLevel: VISIBILITY_LEVEL_PUBLIC_STRING,
};
const dataMock = {
activeViewerType: SimpleViewerMock.type,
diff --git a/spec/frontend/snippets/components/snippet_visibility_edit_spec.js b/spec/frontend/snippets/components/snippet_visibility_edit_spec.js
index 62d1ac9b476..2d043a5caba 100644
--- a/spec/frontend/snippets/components/snippet_visibility_edit_spec.js
+++ b/spec/frontend/snippets/components/snippet_visibility_edit_spec.js
@@ -2,10 +2,12 @@ import { GlFormRadio, GlIcon, GlFormRadioGroup, GlLink } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import SnippetVisibilityEdit from '~/snippets/components/snippet_visibility_edit.vue';
import {
+ VISIBILITY_LEVEL_PRIVATE_STRING,
+ VISIBILITY_LEVEL_INTERNAL_STRING,
+ VISIBILITY_LEVEL_PUBLIC_STRING,
+} from '~/visibility_level/constants';
+import {
SNIPPET_VISIBILITY,
- SNIPPET_VISIBILITY_PRIVATE,
- SNIPPET_VISIBILITY_INTERNAL,
- SNIPPET_VISIBILITY_PUBLIC,
SNIPPET_LEVELS_RESTRICTED,
SNIPPET_LEVELS_DISABLED,
} from '~/snippets/constants';
@@ -38,7 +40,7 @@ describe('Snippet Visibility Edit component', () => {
}
const findLink = () => wrapper.find('label').find(GlLink);
- const findRadios = () => wrapper.find(GlFormRadioGroup).findAll(GlFormRadio);
+ const findRadios = () => wrapper.find(GlFormRadioGroup).findAllComponents(GlFormRadio);
const findRadiosData = () =>
findRadios().wrappers.map((x) => {
return {
@@ -75,19 +77,19 @@ describe('Snippet Visibility Edit component', () => {
const findRestrictedInfo = () => wrapper.find('[data-testid="restricted-levels-info"]');
const RESULTING_OPTIONS = {
0: {
- value: SNIPPET_VISIBILITY_PRIVATE,
+ value: VISIBILITY_LEVEL_PRIVATE_STRING,
icon: SNIPPET_VISIBILITY.private.icon,
text: SNIPPET_VISIBILITY.private.label,
description: SNIPPET_VISIBILITY.private.description,
},
10: {
- value: SNIPPET_VISIBILITY_INTERNAL,
+ value: VISIBILITY_LEVEL_INTERNAL_STRING,
icon: SNIPPET_VISIBILITY.internal.icon,
text: SNIPPET_VISIBILITY.internal.label,
description: SNIPPET_VISIBILITY.internal.description,
},
20: {
- value: SNIPPET_VISIBILITY_PUBLIC,
+ value: VISIBILITY_LEVEL_PUBLIC_STRING,
icon: SNIPPET_VISIBILITY.public.icon,
text: SNIPPET_VISIBILITY.public.label,
description: SNIPPET_VISIBILITY.public.description,
@@ -130,7 +132,7 @@ describe('Snippet Visibility Edit component', () => {
createComponent({ propsData: { isProjectSnippet: true }, deep: true });
expect(findRadiosData()[0]).toEqual({
- value: SNIPPET_VISIBILITY_PRIVATE,
+ value: VISIBILITY_LEVEL_PRIVATE_STRING,
icon: SNIPPET_VISIBILITY.private.icon,
text: SNIPPET_VISIBILITY.private.label,
description: SNIPPET_VISIBILITY.private.description_project,
@@ -141,7 +143,7 @@ describe('Snippet Visibility Edit component', () => {
describe('functionality', () => {
it('pre-selects correct option in the list', () => {
- const value = SNIPPET_VISIBILITY_INTERNAL;
+ const value = VISIBILITY_LEVEL_INTERNAL_STRING;
createComponent({ propsData: { value } });
diff --git a/spec/frontend/surveys/merge_request_performance/app_spec.js b/spec/frontend/surveys/merge_request_performance/app_spec.js
index cd549155914..af91d8aeb6b 100644
--- a/spec/frontend/surveys/merge_request_performance/app_spec.js
+++ b/spec/frontend/surveys/merge_request_performance/app_spec.js
@@ -6,6 +6,17 @@ import { makeMockUserCalloutDismisser } from 'helpers/mock_user_callout_dismisse
import MergeRequestExperienceSurveyApp from '~/surveys/merge_request_experience/app.vue';
import SatisfactionRate from '~/surveys/components/satisfaction_rate.vue';
+const createRenderTrackedArguments = () => [
+ undefined,
+ 'survey:mr_experience',
+ {
+ label: 'render',
+ extra: {
+ accountAge: 0,
+ },
+ },
+];
+
describe('MergeRequestExperienceSurveyApp', () => {
let trackingSpy;
let wrapper;
@@ -24,6 +35,7 @@ describe('MergeRequestExperienceSurveyApp', () => {
dismiss,
shouldShowCallout,
});
+ trackingSpy = mockTracking(undefined, undefined, jest.spyOn);
wrapper = shallowMountExtended(MergeRequestExperienceSurveyApp, {
propsData: {
accountAge: 0,
@@ -35,10 +47,13 @@ describe('MergeRequestExperienceSurveyApp', () => {
});
};
+ beforeEach(() => {
+ localStorage.clear();
+ });
+
describe('when user callout is visible', () => {
beforeEach(() => {
createWrapper();
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
});
it('shows survey', async () => {
@@ -47,14 +62,46 @@ describe('MergeRequestExperienceSurveyApp', () => {
expect(wrapper.emitted().close).toBe(undefined);
});
- it('triggers user callout on close', async () => {
- findCloseButton().vm.$emit('click');
- expect(dismiss).toHaveBeenCalledTimes(1);
+ it('tracks render once', async () => {
+ expect(trackingSpy).toHaveBeenCalledWith(...createRenderTrackedArguments());
});
- it('emits close event on close button click', async () => {
- findCloseButton().vm.$emit('click');
- expect(wrapper.emitted()).toMatchObject({ close: [[]] });
+ it("doesn't track subsequent renders", async () => {
+ createWrapper();
+ expect(trackingSpy).toHaveBeenCalledWith(...createRenderTrackedArguments());
+ expect(trackingSpy).toHaveBeenCalledTimes(1);
+ });
+
+ describe('when close button clicked', () => {
+ beforeEach(() => {
+ findCloseButton().vm.$emit('click');
+ });
+
+ it('triggers user callout on close', async () => {
+ expect(dismiss).toHaveBeenCalledTimes(1);
+ });
+
+ it('emits close event on close button click', async () => {
+ expect(wrapper.emitted()).toMatchObject({ close: [[]] });
+ });
+
+ it('tracks dismissal', async () => {
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'survey:mr_experience', {
+ label: 'dismiss',
+ extra: {
+ accountAge: 0,
+ },
+ });
+ });
+
+ it('tracks subsequent renders', async () => {
+ createWrapper();
+ expect(trackingSpy.mock.calls).toEqual([
+ createRenderTrackedArguments(),
+ expect.anything(),
+ createRenderTrackedArguments(),
+ ]);
+ });
});
it('applies correct feature name for user callout', () => {
@@ -135,6 +182,10 @@ describe('MergeRequestExperienceSurveyApp', () => {
it('emits close event', async () => {
expect(wrapper.emitted()).toMatchObject({ close: [[]] });
});
+
+ it("doesn't track anything", async () => {
+ expect(trackingSpy).toHaveBeenCalledTimes(0);
+ });
});
describe('when Escape key is pressed', () => {
@@ -148,5 +199,14 @@ describe('MergeRequestExperienceSurveyApp', () => {
expect(wrapper.emitted()).toMatchObject({ close: [[]] });
expect(dismiss).toHaveBeenCalledTimes(1);
});
+
+ it('tracks dismissal', async () => {
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, 'survey:mr_experience', {
+ label: 'dismiss',
+ extra: {
+ accountAge: 0,
+ },
+ });
+ });
});
});
diff --git a/spec/frontend/terraform/components/states_table_spec.js b/spec/frontend/terraform/components/states_table_spec.js
index 16ffd2b7013..12a44452717 100644
--- a/spec/frontend/terraform/components/states_table_spec.js
+++ b/spec/frontend/terraform/components/states_table_spec.js
@@ -134,7 +134,7 @@ describe('StatesTable', () => {
await nextTick();
};
- const findActions = () => wrapper.findAll(StateActions);
+ const findActions = () => wrapper.findAllComponents(StateActions);
beforeEach(() => {
return createComponent();
diff --git a/spec/frontend/token_access/mock_data.js b/spec/frontend/token_access/mock_data.js
index 0f121fd1beb..6e2908e659f 100644
--- a/spec/frontend/token_access/mock_data.js
+++ b/spec/frontend/token_access/mock_data.js
@@ -24,19 +24,6 @@ export const disabledJobTokenScope = {
},
};
-export const updateJobTokenScope = {
- data: {
- ciCdSettingsUpdate: {
- ciCdSettings: {
- jobTokenScopeEnabled: true,
- __typename: 'ProjectCiCdSetting',
- },
- errors: [],
- __typename: 'CiCdSettingsUpdatePayload',
- },
- },
-};
-
export const projectsWithScope = {
data: {
project: {
diff --git a/spec/frontend/token_access/token_access_spec.js b/spec/frontend/token_access/token_access_spec.js
index 5aaeebd5af4..024e7dfff8c 100644
--- a/spec/frontend/token_access/token_access_spec.js
+++ b/spec/frontend/token_access/token_access_spec.js
@@ -8,13 +8,11 @@ import createFlash 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 {
enabledJobTokenScope,
disabledJobTokenScope,
- updateJobTokenScope,
projectsWithScope,
addProjectSuccess,
removeProjectSuccess,
@@ -32,7 +30,6 @@ describe('TokenAccess component', () => {
const enabledJobTokenScopeHandler = jest.fn().mockResolvedValue(enabledJobTokenScope);
const disabledJobTokenScopeHandler = jest.fn().mockResolvedValue(disabledJobTokenScope);
- const updateJobTokenScopeHandler = jest.fn().mockResolvedValue(updateJobTokenScope);
const getProjectsWithScope = jest.fn().mockResolvedValue(projectsWithScope);
const addProjectSuccessHandler = jest.fn().mockResolvedValue(addProjectSuccess);
const addProjectFailureHandler = jest.fn().mockRejectedValue(error);
@@ -95,7 +92,7 @@ describe('TokenAccess component', () => {
expect(findTokenSection().exists()).toBe(true);
});
- it('the toggle should be disabled and the token section should not show', async () => {
+ it('the toggle should be disabled and the token section should show', async () => {
createComponent([
[getCIJobTokenScopeQuery, disabledJobTokenScopeHandler],
[getProjectsWithCIJobTokenScopeQuery, getProjectsWithScope],
@@ -104,28 +101,7 @@ describe('TokenAccess component', () => {
await waitForPromises();
expect(findToggle().props('value')).toBe(false);
- expect(findTokenSection().exists()).toBe(false);
- });
-
- it('switching the toggle calls the mutation and fetches the projects again', async () => {
- createComponent([
- [getCIJobTokenScopeQuery, disabledJobTokenScopeHandler],
- [updateCIJobTokenScopeMutation, updateJobTokenScopeHandler],
- [getProjectsWithCIJobTokenScopeQuery, getProjectsWithScope],
- ]);
-
- await waitForPromises();
-
- expect(getProjectsWithScope).toHaveBeenCalledTimes(1);
-
- findToggle().vm.$emit('change', true);
-
- await waitForPromises();
-
- expect(updateJobTokenScopeHandler).toHaveBeenCalledWith({
- input: { fullPath: projectPath, jobTokenScopeEnabled: true },
- });
- expect(getProjectsWithScope).toHaveBeenCalledTimes(2);
+ expect(findTokenSection().exists()).toBe(true);
});
});
diff --git a/spec/frontend/tooltips/components/tooltips_spec.js b/spec/frontend/tooltips/components/tooltips_spec.js
index eef352a72ff..998bb2a9ea2 100644
--- a/spec/frontend/tooltips/components/tooltips_spec.js
+++ b/spec/frontend/tooltips/components/tooltips_spec.js
@@ -28,7 +28,7 @@ describe('tooltips/components/tooltips.vue', () => {
return target;
};
- const allTooltips = () => wrapper.findAll(GlTooltip);
+ const allTooltips = () => wrapper.findAllComponents(GlTooltip);
afterEach(() => {
wrapper.destroy();
@@ -68,7 +68,7 @@ describe('tooltips/components/tooltips.vue', () => {
await nextTick();
- expect(wrapper.findAll(GlTooltip)).toHaveLength(1);
+ expect(wrapper.findAllComponents(GlTooltip)).toHaveLength(1);
});
it('sets tooltip content from title attribute', async () => {
diff --git a/spec/frontend/user_lists/store/index/actions_spec.js b/spec/frontend/user_lists/store/index/actions_spec.js
index 4a8d0afb963..7b2e29ae412 100644
--- a/spec/frontend/user_lists/store/index/actions_spec.js
+++ b/spec/frontend/user_lists/store/index/actions_spec.js
@@ -41,7 +41,7 @@ describe('~/user_lists/store/index/actions', () => {
});
describe('success', () => {
- it('dispatches requestUserLists and receiveUserListsSuccess ', () => {
+ it('dispatches requestUserLists and receiveUserListsSuccess', () => {
return testAction(
fetchUserLists,
null,
@@ -61,7 +61,7 @@ describe('~/user_lists/store/index/actions', () => {
});
describe('error', () => {
- it('dispatches requestUserLists and receiveUserListsError ', () => {
+ it('dispatches requestUserLists and receiveUserListsError', () => {
Api.fetchFeatureFlagUserLists.mockRejectedValue();
return testAction(
diff --git a/spec/frontend/vue_merge_request_widget/components/added_commit_message_spec.js b/spec/frontend/vue_merge_request_widget/components/added_commit_message_spec.js
index cb53dc1fb61..063425454d7 100644
--- a/spec/frontend/vue_merge_request_widget/components/added_commit_message_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/added_commit_message_spec.js
@@ -1,10 +1,10 @@
-import { shallowMount } from '@vue/test-utils';
+import { mount } from '@vue/test-utils';
import AddedCommentMessage from '~/vue_merge_request_widget/components/added_commit_message.vue';
let wrapper;
function factory(propsData) {
- wrapper = shallowMount(AddedCommentMessage, {
+ wrapper = mount(AddedCommentMessage, {
propsData: {
isFastForwardEnabled: false,
targetBranch: 'main',
@@ -23,4 +23,13 @@ describe('Widget added commit message', () => {
expect(wrapper.element.outerHTML).toContain('The changes were not merged');
});
+
+ it('renders merge commit as a link', () => {
+ factory({ state: 'merged', mergeCommitPath: 'https://test.host/merge-commit-link' });
+
+ expect(wrapper.find('[data-testid="merge-commit-sha"]').exists()).toBe(true);
+ expect(wrapper.find('[data-testid="merge-commit-sha"]').attributes('href')).toBe(
+ 'https://test.host/merge-commit-link',
+ );
+ });
});
diff --git a/spec/frontend/vue_merge_request_widget/components/artifacts_list_spec.js b/spec/frontend/vue_merge_request_widget/components/artifacts_list_spec.js
index 712abfe228a..d519ad2cdb0 100644
--- a/spec/frontend/vue_merge_request_widget/components/artifacts_list_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/artifacts_list_spec.js
@@ -39,10 +39,12 @@ describe('Artifacts List', () => {
});
it('renders job url', () => {
- expect(wrapper.findAll(GlLink).at(1).attributes('href')).toEqual(data.artifacts[0].job_path);
+ expect(wrapper.findAllComponents(GlLink).at(1).attributes('href')).toEqual(
+ data.artifacts[0].job_path,
+ );
});
it('renders job name', () => {
- expect(wrapper.findAll(GlLink).at(1).text()).toEqual(data.artifacts[0].job_name);
+ expect(wrapper.findAllComponents(GlLink).at(1).text()).toEqual(data.artifacts[0].job_name);
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_spec.js
index 6347e3c3be3..7f0173b7445 100644
--- a/spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_pipeline_spec.js
@@ -4,9 +4,8 @@ import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { trimText } from 'helpers/text_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import PipelineMiniGraph from '~/pipelines/components/pipelines_list/pipeline_mini_graph.vue';
-import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue';
-import PipelineComponent from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue';
+import MRWidgetPipelineComponent from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue';
+import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
import { SUCCESS } from '~/vue_merge_request_widget/constants';
import mockData from '../mock_data';
@@ -30,14 +29,13 @@ describe('MRWidgetPipeline', () => {
const findPipelineInfoContainer = () => wrapper.findByTestId('pipeline-info-container');
const findCommitLink = () => wrapper.findByTestId('commit-link');
const findPipelineFinishedAt = () => wrapper.findByTestId('finished-at');
- const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph);
- const findAllPipelineStages = () => wrapper.findAllComponents(PipelineStage);
const findPipelineCoverage = () => wrapper.findByTestId('pipeline-coverage');
const findPipelineCoverageDelta = () => wrapper.findByTestId('pipeline-coverage-delta');
const findPipelineCoverageTooltipText = () =>
wrapper.findByTestId('pipeline-coverage-tooltip').text();
const findPipelineCoverageDeltaTooltipText = () =>
wrapper.findByTestId('pipeline-coverage-delta-tooltip').text();
+ const findPipelineMiniGraph = () => wrapper.findComponent(PipelineMiniGraph);
const findMonitoringPipelineMessage = () => wrapper.findByTestId('monitoring-pipeline-message');
const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
@@ -45,7 +43,7 @@ describe('MRWidgetPipeline', () => {
const createWrapper = (props = {}, mountFn = shallowMount) => {
wrapper = extendedWrapper(
- mountFn(PipelineComponent, {
+ mountFn(MRWidgetPipelineComponent, {
propsData: {
...defaultProps,
...props,
@@ -106,8 +104,10 @@ describe('MRWidgetPipeline', () => {
});
it('should render pipeline graph', () => {
+ const stagesCount = mockData.pipeline.details.stages.length;
+
expect(findPipelineMiniGraph().exists()).toBe(true);
- expect(findAllPipelineStages()).toHaveLength(mockData.pipeline.details.stages.length);
+ expect(findPipelineMiniGraph().props('stages')).toHaveLength(stagesCount);
});
describe('should render pipeline coverage information', () => {
@@ -176,15 +176,11 @@ describe('MRWidgetPipeline', () => {
expect(findPipelineInfoContainer().text()).toMatch(mockData.pipeline.details.status.label);
});
- it('should render pipeline graph with correct styles', () => {
+ it('should render pipeline graph', () => {
const stagesCount = mockData.pipeline.details.stages.length;
expect(findPipelineMiniGraph().exists()).toBe(true);
- expect(findPipelineMiniGraph().findAll('.mr-widget-pipeline-stages')).toHaveLength(
- stagesCount,
- );
-
- expect(findAllPipelineStages()).toHaveLength(stagesCount);
+ expect(findPipelineMiniGraph().props('stages')).toHaveLength(stagesCount);
});
it('should render coverage information', () => {
@@ -266,13 +262,13 @@ describe('MRWidgetPipeline', () => {
});
describe('for a detached merge request pipeline', () => {
- it('renders a pipeline widget that reads "Detached merge request pipeline <ID> <status> for <SHA>"', () => {
- pipeline.details.name = 'Detached merge request pipeline';
+ it('renders a pipeline widget that reads "Merge request pipeline <ID> <status> for <SHA>"', () => {
+ pipeline.details.name = 'Merge request pipeline';
pipeline.merge_request_event_type = 'detached';
factory();
- const expected = `Detached merge request pipeline #${pipeline.id} ${pipeline.details.status.label} for ${pipeline.commit.short_id}`;
+ const expected = `Merge request pipeline #${pipeline.id} ${pipeline.details.status.label} for ${pipeline.commit.short_id}`;
const actual = trimText(findPipelineInfoContainer().text());
expect(actual).toBe(expected);
diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js
index 534c0baf35d..05c259de370 100644
--- a/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_rebase_spec.js
@@ -110,7 +110,7 @@ describe('Merge request widget rebase component', () => {
expect(findRebaseMessageText()).toContain('Something went wrong!');
});
- describe('Rebase buttons with', () => {
+ describe('Rebase buttons', () => {
beforeEach(() => {
createWrapper(
{
@@ -148,6 +148,79 @@ describe('Merge request widget rebase component', () => {
expect(rebaseMock).toHaveBeenCalledWith({ skipCi: true });
});
});
+
+ describe('Rebase when pipelines must succeed is enabled', () => {
+ beforeEach(() => {
+ createWrapper(
+ {
+ mr: {
+ rebaseInProgress: false,
+ canPushToSourceBranch: true,
+ onlyAllowMergeIfPipelineSucceeds: true,
+ },
+ service: {
+ rebase: rebaseMock,
+ poll: pollMock,
+ },
+ },
+ mergeRequestWidgetGraphql,
+ );
+ });
+
+ it('renders only the rebase button', () => {
+ expect(findRebaseWithoutCiButton().exists()).toBe(false);
+ expect(findStandardRebaseButton().exists()).toBe(true);
+ });
+
+ it('starts the rebase when clicking', async () => {
+ findStandardRebaseButton().vm.$emit('click');
+
+ await nextTick();
+
+ expect(rebaseMock).toHaveBeenCalledWith({ skipCi: false });
+ });
+ });
+
+ describe('Rebase when pipelines must succeed and skipped pipelines are considered successful are enabled', () => {
+ beforeEach(() => {
+ createWrapper(
+ {
+ mr: {
+ rebaseInProgress: false,
+ canPushToSourceBranch: true,
+ onlyAllowMergeIfPipelineSucceeds: true,
+ allowMergeOnSkippedPipeline: true,
+ },
+ service: {
+ rebase: rebaseMock,
+ poll: pollMock,
+ },
+ },
+ mergeRequestWidgetGraphql,
+ );
+ });
+
+ it('renders both rebase buttons', () => {
+ expect(findRebaseWithoutCiButton().exists()).toBe(true);
+ expect(findStandardRebaseButton().exists()).toBe(true);
+ });
+
+ it('starts the rebase when clicking', async () => {
+ findStandardRebaseButton().vm.$emit('click');
+
+ await nextTick();
+
+ expect(rebaseMock).toHaveBeenCalledWith({ skipCi: false });
+ });
+
+ it('starts the CI-skipping rebase when clicking on "Rebase without CI"', async () => {
+ findRebaseWithoutCiButton().vm.$emit('click');
+
+ await nextTick();
+
+ expect(rebaseMock).toHaveBeenCalledWith({ skipCi: true });
+ });
+ });
});
describe('without permissions', () => {
diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_status_icon_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_status_icon_spec.js
index 11373be578a..530549b7b9c 100644
--- a/spec/frontend/vue_merge_request_widget/components/mr_widget_status_icon_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_status_icon_spec.js
@@ -1,14 +1,16 @@
-import { GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount, mount } from '@vue/test-utils';
+import { GlIcon } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
import mrStatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
+import StatusIcon from '~/vue_merge_request_widget/components/extensions/status_icon.vue';
describe('MR widget status icon component', () => {
let wrapper;
- const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
+ const findStatusIcon = () => wrapper.findComponent(StatusIcon);
+ const findIcon = () => wrapper.findComponent(GlIcon);
- const createWrapper = (props, mountFn = shallowMount) => {
- wrapper = mountFn(mrStatusIcon, {
+ const createWrapper = (props) => {
+ wrapper = shallowMount(mrStatusIcon, {
propsData: {
...props,
},
@@ -17,27 +19,45 @@ describe('MR widget status icon component', () => {
afterEach(() => {
wrapper.destroy();
+ wrapper = null;
});
describe('while loading', () => {
it('renders loading icon', () => {
createWrapper({ status: 'loading' });
- expect(findLoadingIcon().exists()).toBe(true);
+ expect(findStatusIcon().exists()).toBe(true);
+ expect(findStatusIcon().props().isLoading).toBe(true);
});
});
describe('with status icon', () => {
it('renders success status icon', () => {
- createWrapper({ status: 'success' }, mount);
+ createWrapper({ status: 'success' });
- expect(wrapper.find('[data-testid="status_success-icon"]').exists()).toBe(true);
+ expect(findStatusIcon().exists()).toBe(true);
+ expect(findStatusIcon().props().iconName).toBe('success');
});
it('renders failed status icon', () => {
- createWrapper({ status: 'failed' }, mount);
+ createWrapper({ status: 'failed' });
- expect(wrapper.find('[data-testid="status_failed-icon"]').exists()).toBe(true);
+ expect(findStatusIcon().exists()).toBe(true);
+ expect(findStatusIcon().props().iconName).toBe('failed');
+ });
+
+ it('renders merged status icon', () => {
+ createWrapper({ status: 'merged' });
+
+ expect(findIcon().exists()).toBe(true);
+ expect(findIcon().props().name).toBe('merge');
+ });
+
+ it('renders closed status icon', () => {
+ createWrapper({ status: 'closed' });
+
+ expect(findIcon().exists()).toBe(true);
+ expect(findIcon().props().name).toBe('merge-request-close');
});
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/mr_widget_suggest_pipeline_spec.js b/spec/frontend/vue_merge_request_widget/components/mr_widget_suggest_pipeline_spec.js
index 352bc1a08ea..d6c67dab381 100644
--- a/spec/frontend/vue_merge_request_widget/components/mr_widget_suggest_pipeline_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/mr_widget_suggest_pipeline_spec.js
@@ -128,7 +128,7 @@ describe('MRWidgetSuggestPipeline', () => {
it('emits dismiss upon dismissal button click', () => {
findDismissContainer().vm.$emit('dismiss');
- expect(wrapper.emitted().dismiss).toBeTruthy();
+ expect(wrapper.emitted().dismiss).toHaveLength(1);
});
});
});
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 de25e2a0450..635ef0f6b0d 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
@@ -4,117 +4,171 @@ exports[`MRWidgetAutoMergeEnabled when graphql is disabled template should have
<div
class="mr-widget-body media"
>
- <svg
- aria-hidden="true"
- class="gl-text-blue-500 gl-mr-3 gl-mt-1 gl-icon s24"
- data-testid="status_scheduled-icon"
- role="img"
- >
- <use
- href="#status_scheduled"
- />
- </svg>
-
<div
- class="media-body gl-display-flex"
+ class="gl-w-6 gl-h-6 gl-display-flex gl-align-self-start gl-mr-3"
>
-
- <h4
- class="gl-mr-3"
- data-testid="statusText"
+ <div
+ class="gl-display-flex gl-m-auto"
>
- Set by
- <a
- class="author-link inline"
+ <div
+ class="gl-mr-3 gl-p-2 gl-m-0! gl-text-blue-500 gl-w-6 gl-p-2"
>
- <img
- class="avatar avatar-inline s16"
- src="no_avatar.png"
- />
-
- <span
- class="author"
+ <div
+ class="gl-rounded-full gl-relative gl-display-flex mr-widget-extension-icon"
>
-
- </span>
- </a>
- to be merged automatically when the pipeline succeeds
- </h4>
-
+ <div
+ class="gl-absolute gl-top-half gl-left-50p gl-translate-x-n50 gl-display-flex gl-m-auto"
+ >
+ <div
+ class="gl-display-flex gl-m-auto gl-translate-y-n50"
+ >
+ <svg
+ aria-label="Scheduled "
+ class="gl-display-block gl-icon s12"
+ data-qa-selector="status_scheduled_icon"
+ data-testid="status-scheduled-icon"
+ role="img"
+ >
+ <use
+ href="#status-scheduled"
+ />
+ </svg>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div
+ class="gl-display-flex gl-w-full"
+ >
<div
- class="gl-display-flex gl-md-display-block gl-font-size-0 gl-ml-auto gl-mt-1"
+ class="media-body gl-display-flex"
>
- <div>
- <div
- class="dropdown b-dropdown gl-new-dropdown gl-display-block gl-md-display-none! btn-group"
- lazy=""
- no-caret=""
+
+ <h4
+ class="gl-mr-3"
+ data-testid="statusText"
+ >
+ Set by
+ <a
+ class="author-link inline"
>
- <!---->
+ <img
+ class="avatar avatar-inline s16"
+ src="no_avatar.png"
+ />
+
+ <span
+ class="author"
+ >
+
+ </span>
+ </a>
+ to be merged automatically when the pipeline succeeds
+ </h4>
+
+ <div
+ class="gl-display-flex gl-md-display-block gl-font-size-0 gl-ml-auto"
+ >
+ <div>
+ <div
+ class="dropdown b-dropdown gl-new-dropdown gl-display-block gl-md-display-none! btn-group"
+ lazy=""
+ no-caret=""
+ >
+ <!---->
+ <button
+ aria-expanded="false"
+ aria-haspopup="true"
+ class="btn dropdown-toggle btn-default btn-sm gl-p-2! gl-button gl-dropdown-toggle btn-default-tertiary dropdown-icon-only dropdown-toggle-no-caret"
+ type="button"
+ >
+ <!---->
+
+ <svg
+ aria-hidden="true"
+ class="dropdown-icon gl-icon s16"
+ data-testid="ellipsis_v-icon"
+ role="img"
+ >
+ <use
+ href="#ellipsis_v"
+ />
+ </svg>
+
+ <span
+ class="gl-new-dropdown-button-text gl-sr-only"
+ >
+
+ </span>
+
+ <svg
+ aria-hidden="true"
+ class="gl-button-icon dropdown-chevron gl-icon s16"
+ data-testid="chevron-down-icon"
+ role="img"
+ >
+ <use
+ href="#chevron-down"
+ />
+ </svg>
+ </button>
+ <ul
+ class="dropdown-menu dropdown-menu-right"
+ role="menu"
+ tabindex="-1"
+ >
+ <!---->
+ </ul>
+ </div>
+
<button
- aria-expanded="false"
- aria-haspopup="true"
- class="btn dropdown-toggle btn-default btn-sm gl-p-2! gl-button gl-dropdown-toggle btn-default-tertiary dropdown-icon-only dropdown-toggle-no-caret"
+ class="btn gl-display-none gl-md-display-block gl-float-left btn-confirm btn-sm gl-button btn-confirm-tertiary js-cancel-auto-merge"
+ data-qa-selector="cancel_auto_merge_button"
+ data-testid="cancelAutomaticMergeButton"
type="button"
>
<!---->
- <svg
- aria-hidden="true"
- class="dropdown-icon gl-icon s16"
- data-testid="ellipsis_v-icon"
- role="img"
- >
- <use
- href="#ellipsis_v"
- />
- </svg>
-
+ <!---->
+
<span
- class="gl-new-dropdown-button-text gl-sr-only"
+ class="gl-button-text"
>
+ Cancel auto-merge
+
</span>
-
- <svg
- aria-hidden="true"
- class="gl-button-icon dropdown-chevron gl-icon s16"
- data-testid="chevron-down-icon"
- role="img"
- >
- <use
- href="#chevron-down"
- />
- </svg>
</button>
- <ul
- class="dropdown-menu dropdown-menu-right"
- role="menu"
- tabindex="-1"
- >
- <!---->
- </ul>
</div>
+ </div>
+ </div>
+
+ <div
+ class="gl-md-display-none gl-border-l-1 gl-border-l-solid gl-border-gray-100 gl-ml-3 gl-pl-3 gl-h-6 gl-mt-1"
+ >
+ <button
+ class="btn gl-vertical-align-top btn-default btn-sm gl-button btn-default-tertiary btn-icon"
+ title="Collapse merge details"
+ type="button"
+ >
+ <!---->
- <button
- class="btn gl-display-none gl-md-display-block gl-float-left btn-confirm btn-sm gl-button btn-confirm-tertiary js-cancel-auto-merge"
- data-qa-selector="cancel_auto_merge_button"
- data-testid="cancelAutomaticMergeButton"
- type="button"
+ <svg
+ aria-hidden="true"
+ class="gl-button-icon gl-icon s16"
+ data-testid="chevron-lg-up-icon"
+ role="img"
>
- <!---->
-
- <!---->
-
- <span
- class="gl-button-text"
- >
-
- Cancel auto-merge
-
- </span>
- </button>
- </div>
+ <use
+ href="#chevron-lg-up"
+ />
+ </svg>
+
+ <!---->
+ </button>
</div>
</div>
</div>
@@ -124,117 +178,171 @@ exports[`MRWidgetAutoMergeEnabled when graphql is enabled template should have c
<div
class="mr-widget-body media"
>
- <svg
- aria-hidden="true"
- class="gl-text-blue-500 gl-mr-3 gl-mt-1 gl-icon s24"
- data-testid="status_scheduled-icon"
- role="img"
- >
- <use
- href="#status_scheduled"
- />
- </svg>
-
<div
- class="media-body gl-display-flex"
+ class="gl-w-6 gl-h-6 gl-display-flex gl-align-self-start gl-mr-3"
>
-
- <h4
- class="gl-mr-3"
- data-testid="statusText"
+ <div
+ class="gl-display-flex gl-m-auto"
>
- Set by
- <a
- class="author-link inline"
+ <div
+ class="gl-mr-3 gl-p-2 gl-m-0! gl-text-blue-500 gl-w-6 gl-p-2"
>
- <img
- class="avatar avatar-inline s16"
- src="no_avatar.png"
- />
-
- <span
- class="author"
+ <div
+ class="gl-rounded-full gl-relative gl-display-flex mr-widget-extension-icon"
>
-
- </span>
- </a>
- to be merged automatically when the pipeline succeeds
- </h4>
-
+ <div
+ class="gl-absolute gl-top-half gl-left-50p gl-translate-x-n50 gl-display-flex gl-m-auto"
+ >
+ <div
+ class="gl-display-flex gl-m-auto gl-translate-y-n50"
+ >
+ <svg
+ aria-label="Scheduled "
+ class="gl-display-block gl-icon s12"
+ data-qa-selector="status_scheduled_icon"
+ data-testid="status-scheduled-icon"
+ role="img"
+ >
+ <use
+ href="#status-scheduled"
+ />
+ </svg>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div
+ class="gl-display-flex gl-w-full"
+ >
<div
- class="gl-display-flex gl-md-display-block gl-font-size-0 gl-ml-auto gl-mt-1"
+ class="media-body gl-display-flex"
>
- <div>
- <div
- class="dropdown b-dropdown gl-new-dropdown gl-display-block gl-md-display-none! btn-group"
- lazy=""
- no-caret=""
+
+ <h4
+ class="gl-mr-3"
+ data-testid="statusText"
+ >
+ Set by
+ <a
+ class="author-link inline"
>
- <!---->
+ <img
+ class="avatar avatar-inline s16"
+ src="no_avatar.png"
+ />
+
+ <span
+ class="author"
+ >
+
+ </span>
+ </a>
+ to be merged automatically when the pipeline succeeds
+ </h4>
+
+ <div
+ class="gl-display-flex gl-md-display-block gl-font-size-0 gl-ml-auto"
+ >
+ <div>
+ <div
+ class="dropdown b-dropdown gl-new-dropdown gl-display-block gl-md-display-none! btn-group"
+ lazy=""
+ no-caret=""
+ >
+ <!---->
+ <button
+ aria-expanded="false"
+ aria-haspopup="true"
+ class="btn dropdown-toggle btn-default btn-sm gl-p-2! gl-button gl-dropdown-toggle btn-default-tertiary dropdown-icon-only dropdown-toggle-no-caret"
+ type="button"
+ >
+ <!---->
+
+ <svg
+ aria-hidden="true"
+ class="dropdown-icon gl-icon s16"
+ data-testid="ellipsis_v-icon"
+ role="img"
+ >
+ <use
+ href="#ellipsis_v"
+ />
+ </svg>
+
+ <span
+ class="gl-new-dropdown-button-text gl-sr-only"
+ >
+
+ </span>
+
+ <svg
+ aria-hidden="true"
+ class="gl-button-icon dropdown-chevron gl-icon s16"
+ data-testid="chevron-down-icon"
+ role="img"
+ >
+ <use
+ href="#chevron-down"
+ />
+ </svg>
+ </button>
+ <ul
+ class="dropdown-menu dropdown-menu-right"
+ role="menu"
+ tabindex="-1"
+ >
+ <!---->
+ </ul>
+ </div>
+
<button
- aria-expanded="false"
- aria-haspopup="true"
- class="btn dropdown-toggle btn-default btn-sm gl-p-2! gl-button gl-dropdown-toggle btn-default-tertiary dropdown-icon-only dropdown-toggle-no-caret"
+ class="btn gl-display-none gl-md-display-block gl-float-left btn-confirm btn-sm gl-button btn-confirm-tertiary js-cancel-auto-merge"
+ data-qa-selector="cancel_auto_merge_button"
+ data-testid="cancelAutomaticMergeButton"
type="button"
>
<!---->
- <svg
- aria-hidden="true"
- class="dropdown-icon gl-icon s16"
- data-testid="ellipsis_v-icon"
- role="img"
- >
- <use
- href="#ellipsis_v"
- />
- </svg>
-
+ <!---->
+
<span
- class="gl-new-dropdown-button-text gl-sr-only"
+ class="gl-button-text"
>
+ Cancel auto-merge
+
</span>
-
- <svg
- aria-hidden="true"
- class="gl-button-icon dropdown-chevron gl-icon s16"
- data-testid="chevron-down-icon"
- role="img"
- >
- <use
- href="#chevron-down"
- />
- </svg>
</button>
- <ul
- class="dropdown-menu dropdown-menu-right"
- role="menu"
- tabindex="-1"
- >
- <!---->
- </ul>
</div>
+ </div>
+ </div>
+
+ <div
+ class="gl-md-display-none gl-border-l-1 gl-border-l-solid gl-border-gray-100 gl-ml-3 gl-pl-3 gl-h-6 gl-mt-1"
+ >
+ <button
+ class="btn gl-vertical-align-top btn-default btn-sm gl-button btn-default-tertiary btn-icon"
+ title="Collapse merge details"
+ type="button"
+ >
+ <!---->
- <button
- class="btn gl-display-none gl-md-display-block gl-float-left btn-confirm btn-sm gl-button btn-confirm-tertiary js-cancel-auto-merge"
- data-qa-selector="cancel_auto_merge_button"
- data-testid="cancelAutomaticMergeButton"
- type="button"
+ <svg
+ aria-hidden="true"
+ class="gl-button-icon gl-icon s16"
+ data-testid="chevron-lg-up-icon"
+ role="img"
>
- <!---->
-
- <!---->
-
- <span
- class="gl-button-text"
- >
-
- Cancel auto-merge
-
- </span>
- </button>
- </div>
+ <use
+ href="#chevron-lg-up"
+ />
+ </svg>
+
+ <!---->
+ </button>
</div>
</div>
</div>
diff --git a/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_pipeline_failed_spec.js.snap b/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_pipeline_failed_spec.js.snap
deleted file mode 100644
index 7e741bf4660..00000000000
--- a/spec/frontend/vue_merge_request_widget/components/states/__snapshots__/mr_widget_pipeline_failed_spec.js.snap
+++ /dev/null
@@ -1,24 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`PipelineFailed should render error message with a disabled merge button 1`] = `
-<div
- class="mr-widget-body media"
->
- <status-icon-stub
- show-disabled-button="true"
- status="warning"
- />
-
- <div
- class="media-body space-children"
- >
- <span
- class="gl-ml-0! gl-text-body! bold"
- >
- <gl-sprintf-stub
- message="Merge blocked: pipeline must succeed. Push a commit that fixes the failure, or %{linkStart}learn about other solutions.%{linkEnd}"
- />
- </span>
- </div>
-</div>
-`;
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_archived_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_archived_spec.js
index 9332b7e334a..5c07f4ce143 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_archived_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_archived_spec.js
@@ -1,25 +1,26 @@
-import Vue from 'vue';
-import mountComponent from 'helpers/vue_mount_component_helper';
+import { shallowMount } from '@vue/test-utils';
import archivedComponent from '~/vue_merge_request_widget/components/states/mr_widget_archived.vue';
+import StateContainer from '~/vue_merge_request_widget/components/state_container.vue';
describe('MRWidgetArchived', () => {
- let vm;
+ let wrapper;
beforeEach(() => {
- const Component = Vue.extend(archivedComponent);
- vm = mountComponent(Component);
+ wrapper = shallowMount(archivedComponent, { propsData: { mr: {} } });
});
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
+ wrapper = null;
});
- it('renders a ci status failed icon', () => {
- expect(vm.$el.querySelector('.ci-status-icon')).not.toBeNull();
+ it('renders error icon', () => {
+ expect(wrapper.findComponent(StateContainer).exists()).toBe(true);
+ expect(wrapper.findComponent(StateContainer).props().status).toBe('failed');
});
- it('renders information', () => {
- expect(vm.$el.querySelector('.bold').textContent.trim()).toEqual(
+ it('renders information about merging', () => {
+ expect(wrapper.text()).toContain(
'Merge unavailable: merge requests are read-only on archived projects.',
);
});
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_checking_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_checking_spec.js
index 02de426204b..ac18ccf9e26 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_checking_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_checking_spec.js
@@ -1,27 +1,25 @@
-import Vue from 'vue';
-import mountComponent from 'helpers/vue_mount_component_helper';
-import checkingComponent from '~/vue_merge_request_widget/components/states/mr_widget_checking.vue';
+import { shallowMount } from '@vue/test-utils';
+import CheckingComponent from '~/vue_merge_request_widget/components/states/mr_widget_checking.vue';
+import StateContainer from '~/vue_merge_request_widget/components/state_container.vue';
describe('MRWidgetChecking', () => {
- let Component;
- let vm;
+ let wrapper;
beforeEach(() => {
- Component = Vue.extend(checkingComponent);
- vm = mountComponent(Component);
+ wrapper = shallowMount(CheckingComponent, { propsData: { mr: {} } });
});
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
+ wrapper = null;
});
it('renders loading icon', () => {
- expect(vm.$el.querySelector('.mr-widget-icon span').classList).toContain('gl-spinner');
+ expect(wrapper.findComponent(StateContainer).exists()).toBe(true);
+ expect(wrapper.findComponent(StateContainer).props().status).toBe('loading');
});
it('renders information about merging', () => {
- expect(vm.$el.querySelector('.media-body').textContent.trim()).toEqual(
- 'Checking if merge request can be merged…',
- );
+ expect(wrapper.text()).toContain('Checking if merge request can be merged…');
});
});
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 f7d046eb8f9..06ee017dee7 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,39 +1,54 @@
-import Vue from 'vue';
-import mountComponent from 'helpers/vue_mount_component_helper';
+import { shallowMount } from '@vue/test-utils';
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';
+
+const MOCK_DATA = {
+ metrics: {
+ mergedBy: {},
+ closedBy: {
+ name: 'Administrator',
+ username: 'root',
+ webUrl: 'http://localhost:3000/root',
+ avatarUrl: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ },
+ mergedAt: 'Jan 24, 2018 1:02pm UTC',
+ closedAt: 'Jan 24, 2018 1:02pm UTC',
+ readableMergedAt: '',
+ readableClosedAt: 'less than a minute ago',
+ },
+ targetBranchPath: '/twitter/flight/commits/so_long_jquery',
+ targetBranch: 'so_long_jquery',
+};
describe('MRWidgetClosed', () => {
- let vm;
+ let wrapper;
beforeEach(() => {
- const Component = Vue.extend(closedComponent);
- vm = mountComponent(Component, {
- mr: {
- metrics: {
- mergedBy: {},
- closedBy: {
- name: 'Administrator',
- username: 'root',
- webUrl: 'http://localhost:3000/root',
- avatarUrl:
- 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- },
- mergedAt: 'Jan 24, 2018 1:02pm UTC',
- closedAt: 'Jan 24, 2018 1:02pm UTC',
- readableMergedAt: '',
- readableClosedAt: 'less than a minute ago',
- },
- targetBranchPath: '/twitter/flight/commits/so_long_jquery',
- targetBranch: 'so_long_jquery',
+ wrapper = shallowMount(closedComponent, {
+ propsData: {
+ mr: MOCK_DATA,
},
});
});
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('renders closed icon', () => {
+ expect(wrapper.findComponent(StateContainer).exists()).toBe(true);
+ expect(wrapper.findComponent(StateContainer).props().status).toBe('closed');
});
- it('renders warning icon', () => {
- expect(vm.$el.querySelector('.js-ci-status-icon-warning')).not.toBeNull();
+ it('renders mr widget author time', () => {
+ expect(wrapper.findComponent(MrWidgetAuthorTime).exists()).toBe(true);
+ expect(wrapper.findComponent(MrWidgetAuthorTime).props()).toEqual({
+ actionText: 'Closed by',
+ author: MOCK_DATA.metrics.closedBy,
+ dateTitle: MOCK_DATA.metrics.closedAt,
+ dateReadable: MOCK_DATA.metrics.readableClosedAt,
+ });
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_commit_message_dropdown_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_commit_message_dropdown_spec.js
index 663fabb761c..5d2d1fdd6f1 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_commit_message_dropdown_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_commit_message_dropdown_spec.js
@@ -40,7 +40,7 @@ describe('Commits message dropdown component', () => {
wrapper.destroy();
});
- const findDropdownElements = () => wrapper.findAll(GlDropdownItem);
+ const findDropdownElements = () => wrapper.findAllComponents(GlDropdownItem);
const findFirstDropdownElement = () => findDropdownElements().at(0);
it('should have 3 elements in dropdown list', () => {
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_failed_to_merge_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_failed_to_merge_spec.js
index 989aa76f09b..833fa27d453 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_failed_to_merge_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_failed_to_merge_spec.js
@@ -1,6 +1,7 @@
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import MrWidgetFailedToMerge from '~/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue';
+import StateContainer from '~/vue_merge_request_widget/components/state_container.vue';
import eventHub from '~/vue_merge_request_widget/event_hub';
describe('MRWidgetFailedToMerge', () => {
@@ -39,7 +40,7 @@ describe('MRWidgetFailedToMerge', () => {
expect(wrapper.vm.intervalId).toBe(dummyIntervalId);
});
- it('clears interval when destroying ', () => {
+ it('clears interval when destroying', () => {
createComponent();
wrapper.destroy();
@@ -128,7 +129,11 @@ describe('MRWidgetFailedToMerge', () => {
await nextTick();
- expect(wrapper.find('.js-refresh-label').text().trim()).toBe('Refreshing now');
+ const stateContainerWrapper = wrapper.findComponent(StateContainer);
+
+ expect(stateContainerWrapper.exists()).toBe(true);
+ expect(stateContainerWrapper.props('status')).toBe('loading');
+ expect(stateContainerWrapper.text().trim()).toBe('Refreshing now');
});
});
@@ -146,9 +151,9 @@ describe('MRWidgetFailedToMerge', () => {
});
it('renders refresh button', () => {
- expect(
- wrapper.find('[data-testid="merge-request-failed-refresh-button"]').text().trim(),
- ).toBe('Refresh now');
+ expect(wrapper.findComponent(StateContainer).props('actions')).toMatchObject([
+ { text: 'Refresh now', onClick: expect.any(Function) },
+ ]);
});
it('renders remaining time', () => {
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_not_allowed_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_not_allowed_spec.js
index 63e93074857..c6e7198c678 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_not_allowed_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_not_allowed_spec.js
@@ -1,25 +1,27 @@
-import Vue from 'vue';
-import mountComponent from 'helpers/vue_mount_component_helper';
+import { shallowMount } from '@vue/test-utils';
import notAllowedComponent from '~/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue';
+import StatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
describe('MRWidgetNotAllowed', () => {
- let vm;
+ let wrapper;
+
beforeEach(() => {
- const Component = Vue.extend(notAllowedComponent);
- vm = mountComponent(Component);
+ wrapper = shallowMount(notAllowedComponent);
});
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
+ wrapper = null;
});
it('renders success icon', () => {
- expect(vm.$el.querySelector('.ci-status-icon-success')).not.toBe(null);
+ expect(wrapper.findComponent(StatusIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(StatusIcon).props().status).toBe('success');
});
it('renders informative text', () => {
- expect(vm.$el.innerText).toContain('Ready to be merged automatically.');
- expect(vm.$el.innerText).toContain(
+ expect(wrapper.text()).toContain('Ready to be merged automatically.');
+ expect(wrapper.text()).toContain(
'Ask someone with write access to this repository to merge this request',
);
});
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked_spec.js
index 9b10b078e89..4219ad70b4c 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked_spec.js
@@ -1,26 +1,25 @@
-import { shallowMount, mount } from '@vue/test-utils';
+import { shallowMount } from '@vue/test-utils';
import PipelineBlockedComponent from '~/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue';
+import StatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
describe('MRWidgetPipelineBlocked', () => {
let wrapper;
- const createWrapper = (mountFn = shallowMount) => {
- wrapper = mountFn(PipelineBlockedComponent);
- };
+ beforeEach(() => {
+ wrapper = shallowMount(PipelineBlockedComponent);
+ });
afterEach(() => {
wrapper.destroy();
+ wrapper = null;
});
- it('renders warning icon', () => {
- createWrapper(mount);
-
- expect(wrapper.find('.ci-status-icon-warning').exists()).toBe(true);
+ it('renders error icon', () => {
+ expect(wrapper.findComponent(StatusIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(StatusIcon).props().status).toBe('failed');
});
it('renders information text', () => {
- createWrapper();
-
expect(wrapper.text()).toBe(
"Merge blocked: pipeline must succeed. It's waiting for a manual action to continue.",
);
diff --git a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_failed_spec.js b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_failed_spec.js
index 4e44ac539f2..d5619d4996d 100644
--- a/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_failed_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/states/mr_widget_pipeline_failed_spec.js
@@ -1,11 +1,17 @@
+import { GlSprintf, GlLink } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import PipelineFailed from '~/vue_merge_request_widget/components/states/pipeline_failed.vue';
+import StatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
describe('PipelineFailed', () => {
let wrapper;
const createComponent = () => {
- wrapper = shallowMount(PipelineFailed);
+ wrapper = shallowMount(PipelineFailed, {
+ stubs: {
+ GlSprintf,
+ },
+ });
};
beforeEach(() => {
@@ -17,7 +23,14 @@ describe('PipelineFailed', () => {
wrapper = null;
});
+ it('should render error status icon', () => {
+ expect(wrapper.findComponent(StatusIcon).exists()).toBe(true);
+ expect(wrapper.findComponent(StatusIcon).props().status).toBe('failed');
+ });
+
it('should render error message with a disabled merge button', () => {
- expect(wrapper.element).toMatchSnapshot();
+ expect(wrapper.text()).toContain('Merge blocked: pipeline must succeed.');
+ expect(wrapper.text()).toContain('Push a commit that fixes the failure');
+ expect(wrapper.findComponent(GlLink).text()).toContain('learn about other solutions');
});
});
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 6e89cd41559..9a6bf66909e 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
@@ -111,7 +111,7 @@ const createComponent = (
};
const findCheckboxElement = () => wrapper.find(SquashBeforeMerge);
-const findCommitEditElements = () => wrapper.findAll(CommitEdit);
+const findCommitEditElements = () => wrapper.findAllComponents(CommitEdit);
const findCommitDropdownElement = () => wrapper.find(CommitMessageDropdown);
const findFirstCommitEditLabel = () => findCommitEditElements().at(0).props('label');
const findTipLink = () => wrapper.find(GlSprintf);
@@ -549,7 +549,7 @@ describe('ReadyToMerge', () => {
${'squashIsSelected'} | ${'selected'} | ${'value'} | ${false}
${'squashIsSelected'} | ${'unselected'} | ${'value'} | ${false}
`(
- 'is $state when squashIsReadonly returns $expectation ',
+ 'is $state when squashIsReadonly returns $expectation',
({ squashState, prop, expectation }) => {
createComponent({
mr: { commitsCount: 2, enableSquashBeforeMerge: true, [squashState]: expectation },
diff --git a/spec/frontend/vue_merge_request_widget/components/terraform/mr_widget_terraform_container_spec.js b/spec/frontend/vue_merge_request_widget/components/terraform/mr_widget_terraform_container_spec.js
index 8f20d6a8fc9..7a868eb8cc9 100644
--- a/spec/frontend/vue_merge_request_widget/components/terraform/mr_widget_terraform_container_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/terraform/mr_widget_terraform_container_spec.js
@@ -16,7 +16,8 @@ describe('MrWidgetTerraformConainer', () => {
const propsData = { endpoint: '/path/to/terraform/report.json' };
const findHeader = () => wrapper.find('[data-testid="terraform-header-text"]');
- const findPlans = () => wrapper.findAll(TerraformPlan).wrappers.map((x) => x.props('plan'));
+ const findPlans = () =>
+ wrapper.findAllComponents(TerraformPlan).wrappers.map((x) => x.props('plan'));
const mockPollingApi = (response, body, header) => {
mock.onGet(propsData.endpoint).reply(response, body, header);
diff --git a/spec/frontend/vue_merge_request_widget/components/widget/app_spec.js b/spec/frontend/vue_merge_request_widget/components/widget/app_spec.js
index 6bb718082a4..8dbee9b370c 100644
--- a/spec/frontend/vue_merge_request_widget/components/widget/app_spec.js
+++ b/spec/frontend/vue_merge_request_widget/components/widget/app_spec.js
@@ -12,8 +12,8 @@ describe('MR Widget App', () => {
});
};
- it('mounts the component', () => {
+ it('does not mount if widgets array is empty', () => {
createComponent();
- expect(wrapper.findByTestId('mr-widget-app').exists()).toBe(true);
+ expect(wrapper.findByTestId('mr-widget-app').exists()).toBe(false);
});
});
diff --git a/spec/frontend/vue_merge_request_widget/components/widget/widget_content_section_spec.js b/spec/frontend/vue_merge_request_widget/components/widget/widget_content_section_spec.js
new file mode 100644
index 00000000000..c2128d3ff33
--- /dev/null
+++ b/spec/frontend/vue_merge_request_widget/components/widget/widget_content_section_spec.js
@@ -0,0 +1,39 @@
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import WidgetContentSection from '~/vue_merge_request_widget/components/widget/widget_content_section.vue';
+import StatusIcon from '~/vue_merge_request_widget/components/extensions/status_icon.vue';
+
+describe('~/vue_merge_request_widget/components/widget/widget_content_section.vue', () => {
+ let wrapper;
+
+ const findStatusIcon = () => wrapper.findComponent(StatusIcon);
+
+ const createComponent = ({ propsData, slots } = {}) => {
+ wrapper = shallowMountExtended(WidgetContentSection, {
+ propsData: {
+ widgetName: 'MyWidget',
+ ...propsData,
+ },
+ slots,
+ });
+ };
+
+ it('does not render the status icon when it is not provided', () => {
+ createComponent();
+ expect(findStatusIcon().exists()).toBe(false);
+ });
+
+ it('renders the status icon when provided', () => {
+ createComponent({ propsData: { statusIconName: 'failed' } });
+ expect(findStatusIcon().exists()).toBe(true);
+ });
+
+ it('renders the default slot', () => {
+ createComponent({
+ slots: {
+ default: 'Hello world',
+ },
+ });
+
+ expect(wrapper.findByText('Hello world').exists()).toBe(true);
+ });
+});
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 3c08ffdef18..b67b5703ad5 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
@@ -3,16 +3,21 @@ import * as Sentry from '@sentry/browser';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
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 Widget from '~/vue_merge_request_widget/components/widget/widget.vue';
describe('MR Widget', () => {
let wrapper;
const findStatusIcon = () => wrapper.findComponent(StatusIcon);
+ const findExpandedSection = () => wrapper.findByTestId('widget-extension-collapsed-section');
+ const findActionButtons = () => wrapper.findComponent(ActionButtons);
+ const findToggleButton = () => wrapper.findByTestId('toggle-button');
const createComponent = ({ propsData, slots } = {}) => {
wrapper = shallowMountExtended(Widget, {
propsData: {
+ isCollapsible: false,
loadingText: 'Loading widget',
widgetName: 'MyWidget',
value: {
@@ -38,14 +43,15 @@ describe('MR Widget', () => {
createComponent({ propsData: { fetchCollapsedData } });
await waitForPromises();
expect(fetchCollapsedData).toHaveBeenCalled();
- expect(wrapper.vm.error).toBe(null);
+ expect(wrapper.vm.summaryError).toBe(null);
});
it('sets the error text when fetch method fails', async () => {
const fetchCollapsedData = jest.fn().mockReturnValue(() => Promise.reject());
createComponent({ propsData: { fetchCollapsedData } });
await waitForPromises();
- expect(wrapper.vm.error).toBe('Failed to load');
+ expect(wrapper.findByText('Failed to load').exists()).toBe(true);
+ expect(findStatusIcon().props()).toMatchObject({ iconName: 'failed', isLoading: false });
});
it('displays loading icon until request is made and then displays status icon when the request is complete', async () => {
@@ -111,7 +117,7 @@ describe('MR Widget', () => {
jest.spyOn(Sentry, 'captureException').mockImplementation();
createComponent({
propsData: {
- fetchCollapsedData: async () => Promise.reject(error),
+ fetchCollapsedData: () => Promise.reject(error),
},
});
await waitForPromises();
@@ -125,7 +131,7 @@ describe('MR Widget', () => {
createComponent({
propsData: {
summary: 'Hello world',
- fetchCollapsedData: async () => Promise.resolve(),
+ fetchCollapsedData: () => Promise.resolve(),
},
});
@@ -137,7 +143,7 @@ describe('MR Widget', () => {
it('displays the summary slot when provided', () => {
createComponent({
propsData: {
- fetchCollapsedData: async () => Promise.resolve(),
+ fetchCollapsedData: () => Promise.resolve(),
},
slots: {
summary: '<b>More complex summary</b>',
@@ -149,19 +155,167 @@ describe('MR Widget', () => {
);
});
- it('displays the content slot when provided', () => {
+ it('does not display action buttons if actionButtons is not provided', () => {
createComponent({
propsData: {
- fetchCollapsedData: async () => Promise.resolve(),
+ fetchCollapsedData: () => Promise.resolve(),
+ },
+ });
+
+ expect(findActionButtons().exists()).toBe(false);
+ });
+
+ it('does display action buttons if actionButtons is provided', () => {
+ const actionButtons = [{ text: 'click-me', href: '#' }];
+
+ createComponent({
+ propsData: {
+ fetchCollapsedData: () => Promise.resolve(),
+ actionButtons,
+ },
+ });
+
+ expect(findActionButtons().props('tertiaryButtons')).toEqual(actionButtons);
+ });
+ });
+
+ describe('handle collapse toggle', () => {
+ it('displays the toggle button correctly', () => {
+ createComponent({
+ propsData: {
+ isCollapsible: true,
+ fetchCollapsedData: () => Promise.resolve(),
},
slots: {
content: '<b>More complex content</b>',
},
});
- expect(wrapper.findByTestId('widget-extension-collapsed-section').text()).toBe(
- 'More complex content',
- );
+ expect(findToggleButton().attributes('title')).toBe('Show details');
+ expect(findToggleButton().attributes('aria-label')).toBe('Show details');
+ });
+
+ it('does not display the content slot until toggle is clicked', async () => {
+ createComponent({
+ propsData: {
+ isCollapsible: true,
+ fetchCollapsedData: () => Promise.resolve(),
+ },
+ slots: {
+ content: '<b>More complex content</b>',
+ },
+ });
+
+ expect(findExpandedSection().exists()).toBe(false);
+ findToggleButton().vm.$emit('click');
+ await nextTick();
+ expect(findExpandedSection().text()).toBe('More complex content');
+ });
+
+ it('does not display the toggle button if isCollapsible is false', () => {
+ createComponent({
+ propsData: {
+ isCollapsible: false,
+ fetchCollapsedData: () => Promise.resolve(),
+ },
+ });
+
+ expect(findToggleButton().exists()).toBe(false);
+ });
+
+ it('fetches expanded data when clicked for the first time', async () => {
+ const mockDataCollapsed = {
+ headers: {},
+ status: 200,
+ data: { vulnerabilities: [{ vuln: 1 }] },
+ };
+
+ const mockDataExpanded = {
+ headers: {},
+ status: 200,
+ data: { vulnerabilities: [{ vuln: 2 }] },
+ };
+
+ const fetchExpandedData = jest.fn().mockResolvedValue(mockDataExpanded);
+
+ createComponent({
+ propsData: {
+ isCollapsible: true,
+ fetchCollapsedData: () => Promise.resolve(mockDataCollapsed),
+ fetchExpandedData,
+ },
+ });
+
+ findToggleButton().vm.$emit('click');
+ await waitForPromises();
+
+ // First fetches the collapsed data
+ expect(wrapper.emitted('input')[0][0]).toEqual({
+ collapsed: mockDataCollapsed.data,
+ expanded: null,
+ });
+
+ // Then fetches the expanded data
+ expect(wrapper.emitted('input')[1][0]).toEqual({
+ collapsed: null,
+ expanded: mockDataExpanded.data,
+ });
+
+ // Triggering a click does not call the expanded data again
+ findToggleButton().vm.$emit('click');
+ await waitForPromises();
+ expect(fetchExpandedData).toHaveBeenCalledTimes(1);
+ });
+
+ it('allows refetching when fetch expanded data returns an error', async () => {
+ const fetchExpandedData = jest.fn().mockRejectedValue({ error: true });
+
+ createComponent({
+ propsData: {
+ isCollapsible: true,
+ fetchCollapsedData: () => Promise.resolve([]),
+ fetchExpandedData,
+ },
+ });
+
+ findToggleButton().vm.$emit('click');
+ await waitForPromises();
+
+ // First fetches the collapsed data
+ expect(wrapper.emitted('input')[0][0]).toEqual({
+ collapsed: undefined,
+ expanded: null,
+ });
+
+ expect(fetchExpandedData).toHaveBeenCalledTimes(1);
+ expect(wrapper.emitted('input')).toHaveLength(1); // Should not an emit an input call because request failed
+
+ findToggleButton().vm.$emit('click');
+ await waitForPromises();
+ expect(fetchExpandedData).toHaveBeenCalledTimes(2);
+ });
+
+ it('resets the error message when another request is fetched', async () => {
+ const fetchExpandedData = jest.fn().mockRejectedValue({ error: true });
+
+ createComponent({
+ propsData: {
+ isCollapsible: true,
+ fetchCollapsedData: () => Promise.resolve([]),
+ fetchExpandedData,
+ },
+ });
+
+ findToggleButton().vm.$emit('click');
+ await waitForPromises();
+
+ expect(wrapper.findByText('Failed to load').exists()).toBe(true);
+ fetchExpandedData.mockImplementation(() => new Promise(() => {}));
+
+ findToggleButton().vm.$emit('click');
+ await nextTick();
+
+ expect(wrapper.findByText('Failed to load').exists()).toBe(false);
});
});
});
diff --git a/spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js b/spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js
index a285d26f404..a8912405fa8 100644
--- a/spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js
+++ b/spec/frontend/vue_merge_request_widget/deployment/deployment_actions_spec.js
@@ -189,7 +189,7 @@ describe('DeploymentAction component', () => {
});
});
- describe('it should call the executeAction method ', () => {
+ describe('it should call the executeAction method', () => {
beforeEach(async () => {
jest.spyOn(wrapper.vm, 'executeAction').mockImplementation();
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 5c1d3c8e8e8..82743275739 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
@@ -15,6 +15,7 @@ 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';
@@ -157,6 +158,15 @@ describe('Test report extension', () => {
);
});
+ it('hides copy failed tests button when endpoint returns null files', async () => {
+ mockApi(httpStatusCodes.OK, newFailedTestWithNullFilesReport);
+ createComponent();
+
+ await waitForPromises();
+
+ expect(findCopyFailedSpecsBtn().exists()).toBe(false);
+ });
+
it('copy failed tests button updates tooltip text when clicked', async () => {
mockApi(httpStatusCodes.OK, newFailedTestReports);
createComponent();
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 819841317f9..cc894f94f80 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
@@ -845,7 +845,7 @@ describe('MrWidgetOptions', () => {
${'closed'} | ${false} | ${'hides'}
${'merged'} | ${true} | ${'shows'}
${'open'} | ${true} | ${'shows'}
- `('it $showText merge error when state is $state', ({ state, show }) => {
+ `('$showText merge error when state is $state', ({ state, show }) => {
createComponent({ ...mockData, state, merge_error: 'Error!' });
expect(wrapper.find('[data-testid="merge_error"]').exists()).toBe(show);
@@ -1133,7 +1133,7 @@ describe('MrWidgetOptions', () => {
widgetName | nonStandardEvent
${'WidgetCodeQuality'} | ${'i_testing_code_quality_widget_total'}
${'WidgetTerraform'} | ${'i_testing_terraform_widget_total'}
- ${'WidgetIssues'} | ${'i_testing_load_performance_widget_total'}
+ ${'WidgetIssues'} | ${'i_testing_issues_widget_total'}
${'WidgetTestReport'} | ${'i_testing_summary_widget_total'}
`(
"sends non-standard events for the '$widgetName' widget",
diff --git a/spec/frontend/vue_merge_request_widget/stores/artifacts_list/actions_spec.js b/spec/frontend/vue_merge_request_widget/stores/artifacts_list/actions_spec.js
index 22562bb4ddb..1a109aad911 100644
--- a/spec/frontend/vue_merge_request_widget/stores/artifacts_list/actions_spec.js
+++ b/spec/frontend/vue_merge_request_widget/stores/artifacts_list/actions_spec.js
@@ -60,7 +60,7 @@ describe('Artifacts App Store Actions', () => {
});
describe('success', () => {
- it('dispatches requestArtifacts and receiveArtifactsSuccess ', () => {
+ it('dispatches requestArtifacts and receiveArtifactsSuccess', () => {
mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, [
{
text: 'result.txt',
@@ -103,7 +103,7 @@ describe('Artifacts App Store Actions', () => {
mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500);
});
- it('dispatches requestArtifacts and receiveArtifactsError ', () => {
+ it('dispatches requestArtifacts and receiveArtifactsError', () => {
return testAction(
fetchArtifacts,
null,
diff --git a/spec/frontend/vue_shared/alert_details/alert_details_spec.js b/spec/frontend/vue_shared/alert_details/alert_details_spec.js
index 59e21b2ff40..d309432bc63 100644
--- a/spec/frontend/vue_shared/alert_details/alert_details_spec.js
+++ b/spec/frontend/vue_shared/alert_details/alert_details_spec.js
@@ -248,7 +248,7 @@ describe('AlertDetails', () => {
});
});
- it('shows error alert when incident creation fails ', async () => {
+ it('shows error alert when incident creation fails', async () => {
const errorMsg = 'Something went wrong';
mountComponent({
mountMethod: mount,
diff --git a/spec/frontend/vue_shared/alert_details/alert_metrics_spec.js b/spec/frontend/vue_shared/alert_details/alert_metrics_spec.js
index cf04c1eb24a..9d84a535d67 100644
--- a/spec/frontend/vue_shared/alert_details/alert_metrics_spec.js
+++ b/spec/frontend/vue_shared/alert_details/alert_metrics_spec.js
@@ -42,7 +42,7 @@ describe('Alert Metrics', () => {
});
describe('Empty state', () => {
- it('should display a message when metrics dashboard url is not provided ', () => {
+ it('should display a message when metrics dashboard url is not provided', () => {
mountComponent();
expect(findChart().exists()).toBe(false);
expect(findEmptyState().text()).toBe("Metrics weren't available in the alerts payload.");
diff --git a/spec/frontend/vue_shared/components/__snapshots__/code_block_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/code_block_spec.js.snap
deleted file mode 100644
index 7f655d67ae8..00000000000
--- a/spec/frontend/vue_shared/components/__snapshots__/code_block_spec.js.snap
+++ /dev/null
@@ -1,26 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Code Block with default props renders correctly 1`] = `
-<pre
- class="code-block rounded code"
->
- <code
- class="d-block"
- >
- test-code
- </code>
-</pre>
-`;
-
-exports[`Code Block with maxHeight set to "200px" renders correctly 1`] = `
-<pre
- class="code-block rounded code"
- style="max-height: 200px; overflow-y: auto;"
->
- <code
- class="d-block"
- >
- test-code
- </code>
-</pre>
-`;
diff --git a/spec/frontend/vue_shared/components/ci_badge_link_spec.js b/spec/frontend/vue_shared/components/ci_badge_link_spec.js
index a943d931f67..27b6718fb8e 100644
--- a/spec/frontend/vue_shared/components/ci_badge_link_spec.js
+++ b/spec/frontend/vue_shared/components/ci_badge_link_spec.js
@@ -1,6 +1,11 @@
import { shallowMount } from '@vue/test-utils';
import CiBadge from '~/vue_shared/components/ci_badge_link.vue';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import { visitUrl } from '~/lib/utils/url_utility';
+
+jest.mock('~/lib/utils/url_utility', () => ({
+ visitUrl: jest.fn(),
+}));
describe('CI Badge Link Component', () => {
let wrapper;
@@ -79,17 +84,20 @@ describe('CI Badge Link Component', () => {
afterEach(() => {
wrapper.destroy();
- wrapper = null;
});
- it.each(Object.keys(statuses))('should render badge for status: %s', (status) => {
+ it.each(Object.keys(statuses))('should render badge for status: %s', async (status) => {
createComponent({ status: statuses[status] });
- expect(wrapper.attributes('href')).toBe(statuses[status].details_path);
+ expect(wrapper.attributes('href')).toBe();
expect(wrapper.text()).toBe(statuses[status].text);
expect(wrapper.classes()).toContain('ci-status');
expect(wrapper.classes()).toContain(`ci-${statuses[status].group}`);
expect(findIcon().exists()).toBe(true);
+
+ await wrapper.trigger('click');
+
+ expect(visitUrl).toHaveBeenCalledWith(statuses[status].details_path);
});
it('should not render label', () => {
@@ -97,4 +105,12 @@ describe('CI Badge Link Component', () => {
expect(wrapper.text()).toBe('');
});
+
+ it('should emit ciStatusBadgeClick event', async () => {
+ createComponent({ status: statuses.success });
+
+ await wrapper.trigger('click');
+
+ expect(wrapper.emitted('ciStatusBadgeClick')).toEqual([[]]);
+ });
});
diff --git a/spec/frontend/vue_shared/components/code_block_highlighted_spec.js b/spec/frontend/vue_shared/components/code_block_highlighted_spec.js
new file mode 100644
index 00000000000..181692e61b5
--- /dev/null
+++ b/spec/frontend/vue_shared/components/code_block_highlighted_spec.js
@@ -0,0 +1,65 @@
+import { shallowMount } from '@vue/test-utils';
+import CodeBlock from '~/vue_shared/components/code_block_highlighted.vue';
+import waitForPromises from 'helpers/wait_for_promises';
+
+describe('Code Block Highlighted', () => {
+ let wrapper;
+
+ const code = 'const foo = 1;';
+
+ const createComponent = (propsData = {}) => {
+ wrapper = shallowMount(CodeBlock, { propsData });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders highlighted code if language is supported', async () => {
+ createComponent({ code, language: 'javascript' });
+
+ await waitForPromises();
+
+ expect(wrapper.element).toMatchInlineSnapshot(`
+ <code-block-stub
+ class="highlight"
+ code=""
+ maxheight="initial"
+ >
+ <span>
+ <span
+ class="hljs-keyword"
+ >
+ const
+ </span>
+ foo =
+ <span
+ class="hljs-number"
+ >
+ 1
+ </span>
+ ;
+ </span>
+ </code-block-stub>
+ `);
+ });
+
+ it("renders plain text if language isn't supported", async () => {
+ createComponent({ code, language: 'foobar' });
+ await waitForPromises();
+
+ expect(wrapper.emitted('error')).toEqual([[expect.any(TypeError)]]);
+
+ expect(wrapper.element).toMatchInlineSnapshot(`
+ <code-block-stub
+ class="highlight"
+ code=""
+ maxheight="initial"
+ >
+ <span>
+ const foo = 1;
+ </span>
+ </code-block-stub>
+ `);
+ });
+});
diff --git a/spec/frontend/vue_shared/components/code_block_spec.js b/spec/frontend/vue_shared/components/code_block_spec.js
index 60b0b0b566b..9a4dbcc47ff 100644
--- a/spec/frontend/vue_shared/components/code_block_spec.js
+++ b/spec/frontend/vue_shared/components/code_block_spec.js
@@ -4,41 +4,77 @@ import CodeBlock from '~/vue_shared/components/code_block.vue';
describe('Code Block', () => {
let wrapper;
- const defaultProps = {
- code: 'test-code',
- };
+ const code = 'test-code';
- const createComponent = (props = {}) => {
+ const createComponent = (propsData, slots = {}) => {
wrapper = shallowMount(CodeBlock, {
- propsData: {
- ...defaultProps,
- ...props,
- },
+ slots,
+ propsData,
});
};
afterEach(() => {
wrapper.destroy();
- wrapper = null;
});
- describe('with default props', () => {
- beforeEach(() => {
- createComponent();
- });
+ it('overwrites the default slot', () => {
+ createComponent({}, { default: 'DEFAULT SLOT' });
- it('renders correctly', () => {
- expect(wrapper.element).toMatchSnapshot();
- });
+ expect(wrapper.element).toMatchInlineSnapshot(`
+ <pre
+ class="code-block rounded code"
+ >
+ DEFAULT SLOT
+ </pre>
+ `);
});
- describe('with maxHeight set to "200px"', () => {
- beforeEach(() => {
- createComponent({ maxHeight: '200px' });
- });
+ it('renders with empty code prop', () => {
+ createComponent({});
- it('renders correctly', () => {
- expect(wrapper.element).toMatchSnapshot();
- });
+ expect(wrapper.element).toMatchInlineSnapshot(`
+ <pre
+ class="code-block rounded code"
+ >
+ <code
+ class="d-block"
+ >
+
+ </code>
+ </pre>
+ `);
+ });
+
+ it('renders code prop when provided', () => {
+ createComponent({ code });
+
+ expect(wrapper.element).toMatchInlineSnapshot(`
+ <pre
+ class="code-block rounded code"
+ >
+ <code
+ class="d-block"
+ >
+ test-code
+ </code>
+ </pre>
+ `);
+ });
+
+ it('sets maxHeight properly when provided', () => {
+ createComponent({ code, maxHeight: '200px' });
+
+ expect(wrapper.element).toMatchInlineSnapshot(`
+ <pre
+ class="code-block rounded code"
+ style="max-height: 200px; overflow-y: auto;"
+ >
+ <code
+ class="d-block"
+ >
+ test-code
+ </code>
+ </pre>
+ `);
});
});
diff --git a/spec/frontend/vue_shared/components/diff_stats_dropdown_spec.js b/spec/frontend/vue_shared/components/diff_stats_dropdown_spec.js
index 04f63b4bd45..68684004b82 100644
--- a/spec/frontend/vue_shared/components/diff_stats_dropdown_spec.js
+++ b/spec/frontend/vue_shared/components/diff_stats_dropdown_spec.js
@@ -66,7 +66,7 @@ describe('Diff Stats Dropdown', () => {
createComponent({ files: mockFiles });
});
- it('when no file name provided ', () => {
+ it('when no file name provided', () => {
expect(findChangedFiles().at(0).text()).toContain(i18n.noFileNameAvailable);
});
@@ -153,7 +153,7 @@ describe('Diff Stats Dropdown', () => {
createComponent({ files: mockFiles });
});
- it('updates the URL ', () => {
+ it('updates the URL', () => {
findChangedFiles().at(0).vm.$emit('click');
expect(window.location.hash).toBe(mockFiles[0].href);
findChangedFiles().at(1).vm.$emit('click');
diff --git a/spec/frontend/vue_shared/components/gl_modal_vuex_spec.js b/spec/frontend/vue_shared/components/gl_modal_vuex_spec.js
index 2dcd91f737f..6dc018797a6 100644
--- a/spec/frontend/vue_shared/components/gl_modal_vuex_spec.js
+++ b/spec/frontend/vue_shared/components/gl_modal_vuex_spec.js
@@ -157,13 +157,13 @@ describe('GlModalVuex', () => {
const handler = modalFooterSlotContent.mock.calls[0][0][handlerName];
- expect(wrapper.emitted(handlerName)).toBeFalsy();
+ expect(wrapper.emitted(handlerName)).toBeUndefined();
expect(actions.hide).not.toHaveBeenCalled();
handler();
expect(actions.hide).toHaveBeenCalledTimes(1);
- expect(wrapper.emitted(handlerName)).toBeTruthy();
+ expect(wrapper.emitted(handlerName)).toHaveLength(1);
},
);
});
diff --git a/spec/frontend/vue_shared/components/paginated_list_spec.js b/spec/frontend/vue_shared/components/paginated_list_spec.js
index 9f819cc4e94..ae9c920ebd2 100644
--- a/spec/frontend/vue_shared/components/paginated_list_spec.js
+++ b/spec/frontend/vue_shared/components/paginated_list_spec.js
@@ -49,7 +49,7 @@ describe('Pagination links component', () => {
});
describe('rendering', () => {
- it('it renders the gl-paginated-list', () => {
+ it('renders the gl-paginated-list', () => {
expect(wrapper.find('ul.list-group').exists()).toBe(true);
expect(wrapper.findAll('li.list-group-item').length).toBe(2);
});
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 70f4693ae81..fa7fabfaef6 100644
--- a/spec/frontend/vue_shared/components/registry/registry_search_spec.js
+++ b/spec/frontend/vue_shared/components/registry/registry_search_spec.js
@@ -108,7 +108,7 @@ describe('Registry Search', () => {
]);
});
- it('on sort item click emits sorting:changed event ', () => {
+ it('on sort item click emits sorting:changed event', () => {
mountComponent();
findSortingItems().at(1).vm.$emit('click');
diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/mock_data.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/mock_data.js
index afad9314ace..48530a0261f 100644
--- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/mock_data.js
+++ b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/mock_data.js
@@ -56,7 +56,7 @@ export const mockSuggestedColors = {
'#013220': 'Dark green',
'#6699cc': 'Blue-gray',
'#0000ff': 'Blue',
- '#e6e6fa': 'Lavendar',
+ '#e6e6fa': 'Lavender',
'#9400d3': 'Dark violet',
'#330066': 'Deep violet',
'#808080': 'Gray',
diff --git a/spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js b/spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js
index 4fbc907a813..e020d9a557e 100644
--- a/spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js
+++ b/spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js
@@ -110,6 +110,13 @@ describe('Source Viewer component', () => {
expect(hljs.registerLanguage).toHaveBeenCalledWith('json', languageDefinition.default);
});
+ it('correctly maps languages starting with uppercase', async () => {
+ await createComponent({ language: 'Python3' });
+ const languageDefinition = await import(`highlight.js/lib/languages/python`);
+
+ expect(hljs.registerLanguage).toHaveBeenCalledWith('python', languageDefinition.default);
+ });
+
it('highlights the first chunk', () => {
expect(hljs.highlight).toHaveBeenCalledWith(chunk1.trim(), { language: mappedLanguage });
});
@@ -149,7 +156,7 @@ describe('Source Viewer component', () => {
it('emits showBlobInteractionZones on the eventHub when chunk appears', () => {
findChunks().at(0).vm.$emit('appear');
- expect(eventHub.$emit).toBeCalledWith('showBlobInteractionZones', path);
+ expect(eventHub.$emit).toHaveBeenCalledWith('showBlobInteractionZones', path);
});
describe('LineHighlighter', () => {
diff --git a/spec/frontend/vue_shared/components/upload_dropzone/__snapshots__/upload_dropzone_spec.js.snap b/spec/frontend/vue_shared/components/upload_dropzone/__snapshots__/upload_dropzone_spec.js.snap
index 1798ca5ccde..f9d615d4f68 100644
--- a/spec/frontend/vue_shared/components/upload_dropzone/__snapshots__/upload_dropzone_spec.js.snap
+++ b/spec/frontend/vue_shared/components/upload_dropzone/__snapshots__/upload_dropzone_spec.js.snap
@@ -5,7 +5,7 @@ exports[`Upload dropzone component correctly overrides description and drop mess
class="gl-w-full gl-relative"
>
<button
- class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4"
+ class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4 gl-mb-0"
type="button"
>
<div
@@ -86,7 +86,7 @@ exports[`Upload dropzone component when dragging renders correct template when d
class="gl-w-full gl-relative"
>
<button
- class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4"
+ class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4 gl-mb-0"
type="button"
>
<div
@@ -171,7 +171,7 @@ exports[`Upload dropzone component when dragging renders correct template when d
class="gl-w-full gl-relative"
>
<button
- class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4"
+ class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4 gl-mb-0"
type="button"
>
<div
@@ -256,7 +256,7 @@ exports[`Upload dropzone component when dragging renders correct template when d
class="gl-w-full gl-relative"
>
<button
- class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4"
+ class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4 gl-mb-0"
type="button"
>
<div
@@ -342,7 +342,7 @@ exports[`Upload dropzone component when dragging renders correct template when d
class="gl-w-full gl-relative"
>
<button
- class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4"
+ class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4 gl-mb-0"
type="button"
>
<div
@@ -428,7 +428,7 @@ exports[`Upload dropzone component when dragging renders correct template when d
class="gl-w-full gl-relative"
>
<button
- class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4"
+ class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4 gl-mb-0"
type="button"
>
<div
@@ -514,7 +514,7 @@ exports[`Upload dropzone component when no slot provided renders default dropzon
class="gl-w-full gl-relative"
>
<button
- class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4"
+ class="card upload-dropzone-card upload-dropzone-border gl-w-full gl-h-full gl-align-items-center gl-justify-content-center gl-p-4 gl-mb-0"
type="button"
>
<div
diff --git a/spec/frontend/vue_shared/components/user_callout_dismisser_spec.js b/spec/frontend/vue_shared/components/user_callout_dismisser_spec.js
index 70dec42ab32..521744154ba 100644
--- a/spec/frontend/vue_shared/components/user_callout_dismisser_spec.js
+++ b/spec/frontend/vue_shared/components/user_callout_dismisser_spec.js
@@ -84,7 +84,7 @@ describe('UserCalloutDismisser', () => {
});
it('passes expected slot props to child', () => {
- expect(defaultScopedSlotSpy).lastCalledWith(initialSlotProps());
+ expect(defaultScopedSlotSpy).toHaveBeenLastCalledWith(initialSlotProps());
});
});
@@ -98,7 +98,7 @@ describe('UserCalloutDismisser', () => {
});
it('passes expected slot props to child', () => {
- expect(defaultScopedSlotSpy).lastCalledWith(
+ expect(defaultScopedSlotSpy).toHaveBeenLastCalledWith(
initialSlotProps({
isDismissed: true,
isLoadingQuery: false,
@@ -117,7 +117,7 @@ describe('UserCalloutDismisser', () => {
});
it('passes expected slot props to child', () => {
- expect(defaultScopedSlotSpy).lastCalledWith(
+ expect(defaultScopedSlotSpy).toHaveBeenLastCalledWith(
initialSlotProps({
isLoadingQuery: false,
shouldShowCallout: true,
@@ -136,7 +136,7 @@ describe('UserCalloutDismisser', () => {
});
it('passes expected slot props to child', () => {
- expect(defaultScopedSlotSpy).lastCalledWith(
+ expect(defaultScopedSlotSpy).toHaveBeenLastCalledWith(
initialSlotProps({
isLoadingQuery: false,
queryError: expect.any(Error),
@@ -155,7 +155,7 @@ describe('UserCalloutDismisser', () => {
});
it('passes expected slot props to child', () => {
- expect(defaultScopedSlotSpy).lastCalledWith(
+ expect(defaultScopedSlotSpy).toHaveBeenLastCalledWith(
initialSlotProps({
isAnonUser: true,
isLoadingQuery: false,
@@ -186,7 +186,7 @@ describe('UserCalloutDismisser', () => {
});
it('passes expected slot props to child', () => {
- expect(defaultScopedSlotSpy).lastCalledWith(
+ expect(defaultScopedSlotSpy).toHaveBeenLastCalledWith(
initialSlotProps({
isLoadingQuery: false,
shouldShowCallout: true,
@@ -217,7 +217,7 @@ describe('UserCalloutDismisser', () => {
});
it('passes expected slot props to child', async () => {
- expect(defaultScopedSlotSpy).lastCalledWith(
+ expect(defaultScopedSlotSpy).toHaveBeenLastCalledWith(
initialSlotProps({
isLoadingQuery: false,
shouldShowCallout: true,
@@ -229,7 +229,7 @@ describe('UserCalloutDismisser', () => {
// Wait for Vue re-render due to prop change
await nextTick();
- expect(defaultScopedSlotSpy).lastCalledWith(
+ expect(defaultScopedSlotSpy).toHaveBeenLastCalledWith(
initialSlotProps({
isDismissed: true,
isLoadingMutation: true,
@@ -240,7 +240,7 @@ describe('UserCalloutDismisser', () => {
// Wait for mutation to resolve
await waitForPromises();
- expect(defaultScopedSlotSpy).lastCalledWith(
+ expect(defaultScopedSlotSpy).toHaveBeenLastCalledWith(
initialSlotProps({
isDismissed: true,
isLoadingQuery: false,
@@ -270,7 +270,7 @@ describe('UserCalloutDismisser', () => {
});
it('passes expected slot props to child', async () => {
- expect(defaultScopedSlotSpy).lastCalledWith(
+ expect(defaultScopedSlotSpy).toHaveBeenLastCalledWith(
initialSlotProps({
isLoadingQuery: false,
shouldShowCallout: true,
@@ -282,7 +282,7 @@ describe('UserCalloutDismisser', () => {
// Wait for Vue re-render due to prop change
await nextTick();
- expect(defaultScopedSlotSpy).lastCalledWith(
+ expect(defaultScopedSlotSpy).toHaveBeenLastCalledWith(
initialSlotProps({
isDismissed: true,
isLoadingMutation: true,
@@ -293,7 +293,7 @@ describe('UserCalloutDismisser', () => {
// Wait for mutation to resolve
await waitForPromises();
- expect(defaultScopedSlotSpy).lastCalledWith(
+ expect(defaultScopedSlotSpy).toHaveBeenLastCalledWith(
initialSlotProps({
isDismissed: true,
isLoadingQuery: false,
diff --git a/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js b/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js
index b7ce3e47cef..6d48000beb0 100644
--- a/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js
+++ b/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js
@@ -1,8 +1,15 @@
import { GlSkeletonLoader, GlIcon } from '@gitlab/ui';
import { loadHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
+import { sprintf } from '~/locale';
import { mountExtended } from 'helpers/vue_test_utils_helper';
-import { AVAILABILITY_STATUS } from '~/set_status_modal/utils';
+import { AVAILABILITY_STATUS } from '~/set_status_modal/constants';
import UserPopover from '~/vue_shared/components/user_popover/user_popover.vue';
+import {
+ I18N_USER_BLOCKED,
+ I18N_USER_LEARN,
+ I18N_USER_FOLLOW,
+ I18N_USER_UNFOLLOW,
+} from '~/vue_shared/components/user_popover/constants';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
import { followUser, unfollowUser } from '~/api/user_api';
@@ -310,7 +317,9 @@ describe('User Popover Component', () => {
const securityBotDocsLink = findSecurityBotDocsLink();
expect(securityBotDocsLink.exists()).toBe(true);
expect(securityBotDocsLink.attributes('href')).toBe(SECURITY_BOT_USER.websiteUrl);
- expect(securityBotDocsLink.text()).toBe('Learn more about GitLab Security Bot');
+ expect(securityBotDocsLink.text()).toBe(
+ sprintf(I18N_USER_LEARN, { name: SECURITY_BOT_USER.name }),
+ );
});
it("does not show a link to the bot's documentation if there is no website_url", () => {
@@ -320,9 +329,10 @@ describe('User Popover Component', () => {
});
it("doesn't escape user's name", () => {
- createWrapper({ user: { ...SECURITY_BOT_USER, name: '%<>\';"' } });
+ const name = '%<>\';"';
+ createWrapper({ user: { ...SECURITY_BOT_USER, name } });
const securityBotDocsLink = findSecurityBotDocsLink();
- expect(securityBotDocsLink.text()).toBe('Learn more about %<>\';"');
+ expect(securityBotDocsLink.text()).toBe(sprintf(I18N_USER_LEARN, { name }, false));
});
it('does not display local time', () => {
@@ -336,7 +346,7 @@ describe('User Popover Component', () => {
beforeEach(() => createWrapper());
it('renders the Follow button with the correct variant', () => {
- expect(findToggleFollowButton().text()).toBe('Follow');
+ expect(findToggleFollowButton().text()).toBe(I18N_USER_FOLLOW);
expect(findToggleFollowButton().props('variant')).toBe('confirm');
});
@@ -387,7 +397,7 @@ describe('User Popover Component', () => {
beforeEach(() => createWrapper({ user: { ...DEFAULT_PROPS.user, isFollowed: true } }));
it('renders the Unfollow button with the correct variant', () => {
- expect(findToggleFollowButton().text()).toBe('Unfollow');
+ expect(findToggleFollowButton().text()).toBe(I18N_USER_UNFOLLOW);
expect(findToggleFollowButton().props('variant')).toBe('default');
});
@@ -441,6 +451,25 @@ describe('User Popover Component', () => {
});
});
+ describe('when the user is blocked', () => {
+ const bio = 'My super interesting bio';
+ const status = 'My status';
+ beforeEach(() =>
+ createWrapper({
+ user: { ...DEFAULT_PROPS.user, state: 'blocked', bio, status: { message_html: status } },
+ }),
+ );
+
+ it('renders warning', () => {
+ expect(wrapper.text()).toContain(I18N_USER_BLOCKED);
+ });
+
+ it("doesn't show other information", () => {
+ expect(wrapper.text()).not.toContain(bio);
+ expect(wrapper.text()).not.toContain(status);
+ });
+ });
+
describe('when API does not support `isFollowed`', () => {
beforeEach(() => {
const user = {
diff --git a/spec/frontend/vue_shared/issuable/show/components/issuable_edit_form_spec.js b/spec/frontend/vue_shared/issuable/show/components/issuable_edit_form_spec.js
index d843da4da5b..e5594b6d37e 100644
--- a/spec/frontend/vue_shared/issuable/show/components/issuable_edit_form_spec.js
+++ b/spec/frontend/vue_shared/issuable/show/components/issuable_edit_form_spec.js
@@ -164,8 +164,7 @@ describe('IssuableEditForm', () => {
const titleInputEl = wrapper.findComponent(GlFormInput);
titleInputEl.vm.$emit('keydown', eventObj, 'title');
-
- expect(wrapper.emitted('keydown-title')).toBeTruthy();
+ expect(wrapper.emitted('keydown-title')).toHaveLength(1);
expect(wrapper.emitted('keydown-title')[0]).toMatchObject([
eventObj,
{
@@ -179,8 +178,7 @@ describe('IssuableEditForm', () => {
const descriptionInputEl = wrapper.find('[data-testid="description"] textarea');
descriptionInputEl.trigger('keydown', eventObj, 'description');
-
- expect(wrapper.emitted('keydown-description')).toBeTruthy();
+ expect(wrapper.emitted('keydown-description')).toHaveLength(1);
expect(wrapper.emitted('keydown-description')[0]).toMatchObject([
eventObj,
{
diff --git a/spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js b/spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js
index 39909e26ef0..0a5e46d9263 100644
--- a/spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js
+++ b/spec/frontend/vue_shared/security_reports/components/manage_via_mr_spec.js
@@ -93,7 +93,7 @@ describe('ManageViaMr component', () => {
createComponent({ apolloProvider, featureName, featureType, isFeatureConfigured: true });
});
- it('it does not render a button', () => {
+ it('does not render a button', () => {
expect(findButton().exists()).toBe(false);
});
});
@@ -104,7 +104,7 @@ describe('ManageViaMr component', () => {
createComponent({ apolloProvider, featureName, featureType, isFeatureConfigured: false });
});
- it('it does render a button', () => {
+ it('does render a button', () => {
expect(findButton().exists()).toBe(true);
});
diff --git a/spec/frontend/work_items/components/item_title_spec.js b/spec/frontend/work_items/components/item_title_spec.js
index de20369eb1b..13e04ef6671 100644
--- a/spec/frontend/work_items/components/item_title_spec.js
+++ b/spec/frontend/work_items/components/item_title_spec.js
@@ -49,6 +49,6 @@ describe('ItemTitle', () => {
findInputEl().element.innerText = mockUpdatedTitle;
await findInputEl().trigger(sourceEvent);
- expect(wrapper.emitted(eventName)).toBeTruthy();
+ expect(wrapper.emitted(eventName)).toBeDefined();
});
});
diff --git a/spec/frontend/work_items/components/work_item_actions_spec.js b/spec/frontend/work_items/components/work_item_actions_spec.js
index a1f1d47ab90..3c312fb4552 100644
--- a/spec/frontend/work_items/components/work_item_actions_spec.js
+++ b/spec/frontend/work_items/components/work_item_actions_spec.js
@@ -1,15 +1,30 @@
-import { GlModal } from '@gitlab/ui';
+import { GlDropdownDivider, GlModal } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import WorkItemActions from '~/work_items/components/work_item_actions.vue';
+const TEST_ID_CONFIDENTIALITY_TOGGLE_ACTION = 'confidentiality-toggle-action';
+const TEST_ID_DELETE_ACTION = 'delete-action';
+
describe('WorkItemActions component', () => {
let wrapper;
let glModalDirective;
const findModal = () => wrapper.findComponent(GlModal);
const findConfidentialityToggleButton = () =>
- wrapper.findByTestId('confidentiality-toggle-action');
- const findDeleteButton = () => wrapper.findByTestId('delete-action');
+ wrapper.findByTestId(TEST_ID_CONFIDENTIALITY_TOGGLE_ACTION);
+ const findDeleteButton = () => wrapper.findByTestId(TEST_ID_DELETE_ACTION);
+ const findDropdownItems = () => wrapper.findAll('[data-testid="work-item-actions-dropdown"] > *');
+ const findDropdownItemsActual = () =>
+ findDropdownItems().wrappers.map((x) => {
+ if (x.is(GlDropdownDivider)) {
+ return { divider: true };
+ }
+
+ return {
+ testId: x.attributes('data-testid'),
+ text: x.text(),
+ };
+ });
const createComponent = ({
canUpdate = true,
@@ -19,7 +34,14 @@ describe('WorkItemActions component', () => {
} = {}) => {
glModalDirective = jest.fn();
wrapper = shallowMountExtended(WorkItemActions, {
- propsData: { workItemId: '123', canUpdate, canDelete, isConfidential, isParentConfidential },
+ propsData: {
+ workItemId: '123',
+ canUpdate,
+ canDelete,
+ isConfidential,
+ isParentConfidential,
+ workItemType: 'Task',
+ },
directives: {
glModal: {
bind(_, { value }) {
@@ -44,8 +66,19 @@ describe('WorkItemActions component', () => {
it('renders dropdown actions', () => {
createComponent();
- expect(findConfidentialityToggleButton().exists()).toBe(true);
- expect(findDeleteButton().exists()).toBe(true);
+ expect(findDropdownItemsActual()).toEqual([
+ {
+ testId: TEST_ID_CONFIDENTIALITY_TOGGLE_ACTION,
+ text: 'Turn on confidentiality',
+ },
+ {
+ divider: true,
+ },
+ {
+ testId: TEST_ID_DELETE_ACTION,
+ text: 'Delete task',
+ },
+ ]);
});
describe('toggle confidentiality action', () => {
@@ -103,7 +136,8 @@ describe('WorkItemActions component', () => {
canDelete: false,
});
- expect(wrapper.findByTestId('delete-action').exists()).toBe(false);
+ expect(findDeleteButton().exists()).toBe(false);
+ expect(wrapper.findComponent(GlDropdownDivider).exists()).toBe(false);
});
});
});
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 f0ef8aee7a9..28231fad108 100644
--- a/spec/frontend/work_items/components/work_item_assignees_spec.js
+++ b/spec/frontend/work_items/components/work_item_assignees_spec.js
@@ -1,4 +1,4 @@
-import { GlLink, GlTokenSelector, GlSkeletonLoader } from '@gitlab/ui';
+import { GlLink, GlTokenSelector, GlSkeletonLoader, GlIntersectionObserver } from '@gitlab/ui';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
@@ -8,12 +8,17 @@ import { mockTracking } from 'helpers/tracking_helper';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import userSearchQuery from '~/graphql_shared/queries/users_search.query.graphql';
import currentUserQuery from '~/graphql_shared/queries/current_user.query.graphql';
+import { temporaryConfig } from '~/graphql_shared/issuable_client';
import InviteMembersTrigger from '~/invite_members/components/invite_members_trigger.vue';
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
import WorkItemAssignees from '~/work_items/components/work_item_assignees.vue';
-import { i18n, TASK_TYPE_NAME, TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
-import { temporaryConfig } from '~/work_items/graphql/provider';
+import {
+ i18n,
+ TASK_TYPE_NAME,
+ TRACKING_CATEGORY_SHOW,
+ DEFAULT_PAGE_SIZE_ASSIGNEES,
+} from '~/work_items/constants';
import {
projectMembersResponseWithCurrentUser,
mockAssignees,
@@ -22,6 +27,8 @@ import {
currentUserNullResponse,
projectMembersResponseWithoutCurrentUser,
updateWorkItemMutationResponse,
+ projectMembersResponseWithCurrentUserWithNextPage,
+ projectMembersResponseWithNoMatchingUsers,
} from '../mock_data';
Vue.use(VueApollo);
@@ -40,15 +47,25 @@ describe('WorkItemAssignees component', () => {
const findEmptyState = () => wrapper.findByTestId('empty-state');
const findAssignSelfButton = () => wrapper.findByTestId('assign-self');
const findAssigneesTitle = () => wrapper.findByTestId('assignees-title');
+ const findIntersectionObserver = () => wrapper.findComponent(GlIntersectionObserver);
+
+ const triggerInfiniteScroll = () =>
+ wrapper.findComponent(GlIntersectionObserver).vm.$emit('appear');
const successSearchQueryHandler = jest
.fn()
.mockResolvedValue(projectMembersResponseWithCurrentUser);
+ const successSearchQueryHandlerWithMoreAssignees = jest
+ .fn()
+ .mockResolvedValue(projectMembersResponseWithCurrentUserWithNextPage);
const successCurrentUserQueryHandler = jest.fn().mockResolvedValue(currentUserResponse);
const noCurrentUserQueryHandler = jest.fn().mockResolvedValue(currentUserNullResponse);
const successUpdateWorkItemMutationHandler = jest
.fn()
.mockResolvedValue(updateWorkItemMutationResponse);
+ const successSearchWithNoMatchingUsers = jest
+ .fn()
+ .mockResolvedValue(projectMembersResponseWithNoMatchingUsers);
const errorHandler = jest.fn().mockRejectedValue('Houston, we have a problem');
@@ -82,9 +99,6 @@ describe('WorkItemAssignees component', () => {
});
wrapper = mountExtended(WorkItemAssignees, {
- provide: {
- fullPath: 'test-project-path',
- },
propsData: {
assignees,
workItemId,
@@ -92,6 +106,7 @@ describe('WorkItemAssignees component', () => {
workItemType: TASK_TYPE_NAME,
canUpdate,
canInviteMembers,
+ fullPath: 'test-project-path',
},
attachTo: document.body,
apolloProvider,
@@ -459,4 +474,56 @@ describe('WorkItemAssignees component', () => {
expect(findInviteMembersTrigger().exists()).toBe(true);
});
});
+
+ describe('load more assignees', () => {
+ it('does not have intersection observer when no matching users', async () => {
+ createComponent({ searchQueryHandler: successSearchWithNoMatchingUsers });
+ findTokenSelector().vm.$emit('focus');
+ await nextTick();
+
+ expect(findSkeletonLoader().exists()).toBe(true);
+
+ await waitForPromises();
+
+ expect(findSkeletonLoader().exists()).toBe(false);
+ expect(findIntersectionObserver().exists()).toBe(false);
+ });
+
+ it('does not trigger load more when does not have next page', async () => {
+ createComponent();
+ findTokenSelector().vm.$emit('focus');
+ await nextTick();
+
+ expect(findSkeletonLoader().exists()).toBe(true);
+
+ await waitForPromises();
+
+ expect(findSkeletonLoader().exists()).toBe(false);
+
+ expect(findIntersectionObserver().exists()).toBe(false);
+ });
+
+ it('triggers load more when there are more users', async () => {
+ createComponent({ searchQueryHandler: successSearchQueryHandlerWithMoreAssignees });
+ findTokenSelector().vm.$emit('focus');
+ await nextTick();
+
+ expect(findSkeletonLoader().exists()).toBe(true);
+
+ await waitForPromises();
+
+ expect(findSkeletonLoader().exists()).toBe(false);
+ expect(findIntersectionObserver().exists()).toBe(true);
+
+ triggerInfiniteScroll();
+
+ expect(successSearchQueryHandlerWithMoreAssignees).toHaveBeenCalledWith({
+ first: DEFAULT_PAGE_SIZE_ASSIGNEES,
+ after:
+ projectMembersResponseWithCurrentUserWithNextPage.data.workspace.users.pageInfo.endCursor,
+ search: '',
+ fullPath: 'test-project-path',
+ });
+ });
+ });
});
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 8017c46dea8..d3165d8dc26 100644
--- a/spec/frontend/work_items/components/work_item_description_spec.js
+++ b/spec/frontend/work_items/components/work_item_description_spec.js
@@ -10,9 +10,9 @@ import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import WorkItemDescription from '~/work_items/components/work_item_description.vue';
import { TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
-import updateWorkItemWidgetsMutation from '~/work_items/graphql/update_work_item_widgets.mutation.graphql';
+import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
import {
- updateWorkItemWidgetsResponse,
+ updateWorkItemMutationResponse,
workItemResponseFactory,
workItemQueryResponse,
} from '../mock_data';
@@ -31,7 +31,7 @@ describe('WorkItemDescription', () => {
Vue.use(VueApollo);
- const mutationSuccessHandler = jest.fn().mockResolvedValue(updateWorkItemWidgetsResponse);
+ const mutationSuccessHandler = jest.fn().mockResolvedValue(updateWorkItemMutationResponse);
const findEditButton = () => wrapper.find('[data-testid="edit-description"]');
const findMarkdownField = () => wrapper.findComponent(MarkdownField);
@@ -53,13 +53,11 @@ describe('WorkItemDescription', () => {
wrapper = shallowMount(WorkItemDescription, {
apolloProvider: createMockApollo([
[workItemQuery, workItemResponseHandler],
- [updateWorkItemWidgetsMutation, mutationHandler],
+ [updateWorkItemMutation, mutationHandler],
]),
propsData: {
workItemId: id,
- },
- provide: {
- fullPath: '/group/project',
+ fullPath: 'test-project-path',
},
stubs: {
MarkdownField,
@@ -175,7 +173,7 @@ describe('WorkItemDescription', () => {
isEditing: true,
mutationHandler: jest.fn().mockResolvedValue({
data: {
- workItemUpdateWidgets: {
+ workItemUpdate: {
workItem: {},
errors: [error],
},
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 01891012f99..6b1ef8971d3 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
@@ -113,7 +113,7 @@ describe('WorkItemDetailModal component', () => {
createComponent();
findModal().vm.$emit('hide');
- expect(wrapper.emitted('close')).toBeTruthy();
+ expect(wrapper.emitted('close')).toHaveLength(1);
});
it('hides the modal when WorkItemDetail emits `close` event', () => {
diff --git a/spec/frontend/work_items/components/work_item_detail_spec.js b/spec/frontend/work_items/components/work_item_detail_spec.js
new file mode 100644
index 00000000000..b047e0dc8d7
--- /dev/null
+++ b/spec/frontend/work_items/components/work_item_detail_spec.js
@@ -0,0 +1,522 @@
+import { GlAlert, GlBadge, GlLoadingIcon, GlSkeletonLoader, GlButton } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue, { nextTick } 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 waitForPromises from 'helpers/wait_for_promises';
+import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
+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';
+import WorkItemDueDate from '~/work_items/components/work_item_due_date.vue';
+import WorkItemState from '~/work_items/components/work_item_state.vue';
+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 WorkItemInformation from '~/work_items/components/work_item_information.vue';
+import { i18n } from '~/work_items/constants';
+import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
+import workItemDatesSubscription from '~/work_items/graphql/work_item_dates.subscription.graphql';
+import workItemTitleSubscription from '~/work_items/graphql/work_item_title.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 { temporaryConfig } from '~/graphql_shared/issuable_client';
+import { useLocalStorageSpy } from 'helpers/local_storage_helper';
+import {
+ mockParent,
+ workItemDatesSubscriptionResponse,
+ workItemResponseFactory,
+ workItemTitleSubscriptionResponse,
+ workItemWeightSubscriptionResponse,
+} from '../mock_data';
+
+describe('WorkItemDetail component', () => {
+ let wrapper;
+ useLocalStorageSpy();
+
+ Vue.use(VueApollo);
+
+ const workItemQueryResponse = workItemResponseFactory({ canUpdate: true, canDelete: true });
+ const workItemQueryResponseWithoutParent = workItemResponseFactory({
+ parent: null,
+ canUpdate: true,
+ canDelete: true,
+ });
+ const successHandler = jest.fn().mockResolvedValue(workItemQueryResponse);
+ const datesSubscriptionHandler = jest.fn().mockResolvedValue(workItemDatesSubscriptionResponse);
+ const titleSubscriptionHandler = jest.fn().mockResolvedValue(workItemTitleSubscriptionResponse);
+ const weightSubscriptionHandler = jest.fn().mockResolvedValue(workItemWeightSubscriptionResponse);
+
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findSkeleton = () => wrapper.findComponent(GlSkeletonLoader);
+ const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findWorkItemActions = () => wrapper.findComponent(WorkItemActions);
+ const findWorkItemTitle = () => wrapper.findComponent(WorkItemTitle);
+ const findWorkItemState = () => wrapper.findComponent(WorkItemState);
+ const findWorkItemDescription = () => wrapper.findComponent(WorkItemDescription);
+ const findWorkItemDueDate = () => wrapper.findComponent(WorkItemDueDate);
+ const findWorkItemAssignees = () => wrapper.findComponent(WorkItemAssignees);
+ const findWorkItemLabels = () => wrapper.findComponent(WorkItemLabels);
+ const findParent = () => wrapper.find('[data-testid="work-item-parent"]');
+ 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 createComponent = ({
+ isModal = false,
+ updateInProgress = false,
+ workItemId = workItemQueryResponse.data.workItem.id,
+ handler = successHandler,
+ subscriptionHandler = titleSubscriptionHandler,
+ confidentialityMock = [updateWorkItemMutation, jest.fn()],
+ workItemsMvc2Enabled = false,
+ includeWidgets = false,
+ error = undefined,
+ } = {}) => {
+ const handlers = [
+ [workItemQuery, handler],
+ [workItemTitleSubscription, subscriptionHandler],
+ [workItemDatesSubscription, datesSubscriptionHandler],
+ confidentialityMock,
+ ];
+
+ if (IS_EE) {
+ handlers.push([workItemWeightSubscription, weightSubscriptionHandler]);
+ }
+
+ wrapper = shallowMount(WorkItemDetail, {
+ apolloProvider: createMockApollo(
+ handlers,
+ {},
+ {
+ typePolicies: includeWidgets ? temporaryConfig.cacheConfig.typePolicies : {},
+ },
+ ),
+ propsData: { isModal, workItemId },
+ data() {
+ return {
+ updateInProgress,
+ error,
+ };
+ },
+ provide: {
+ glFeatures: {
+ workItemsMvc2: workItemsMvc2Enabled,
+ },
+ hasIssueWeightsFeature: true,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when there is no `workItemId` prop', () => {
+ beforeEach(() => {
+ createComponent({ workItemId: null });
+ });
+
+ it('skips the work item query', () => {
+ expect(successHandler).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('when loading', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders skeleton loader', () => {
+ expect(findSkeleton().exists()).toBe(true);
+ expect(findWorkItemState().exists()).toBe(false);
+ expect(findWorkItemTitle().exists()).toBe(false);
+ });
+ });
+
+ describe('when loaded', () => {
+ beforeEach(() => {
+ createComponent();
+ return waitForPromises();
+ });
+
+ it('does not render skeleton', () => {
+ expect(findSkeleton().exists()).toBe(false);
+ expect(findWorkItemState().exists()).toBe(true);
+ expect(findWorkItemTitle().exists()).toBe(true);
+ });
+
+ it('updates the document title', () => {
+ expect(document.title).toEqual('Updated title · Task · test-project-path');
+ });
+ });
+
+ describe('close button', () => {
+ describe('when isModal prop is false', () => {
+ it('does not render', async () => {
+ createComponent({ isModal: false });
+ await waitForPromises();
+
+ expect(findCloseButton().exists()).toBe(false);
+ });
+ });
+
+ describe('when isModal prop is true', () => {
+ it('renders', async () => {
+ createComponent({ isModal: true });
+ await waitForPromises();
+
+ expect(findCloseButton().props('icon')).toBe('close');
+ expect(findCloseButton().attributes('aria-label')).toBe('Close');
+ });
+
+ it('emits `close` event when clicked', async () => {
+ createComponent({ isModal: true });
+ await waitForPromises();
+
+ findCloseButton().vm.$emit('click');
+
+ expect(wrapper.emitted('close')).toEqual([[]]);
+ });
+ });
+ });
+
+ describe('confidentiality', () => {
+ const errorMessage = 'Mutation failed';
+ const confidentialWorkItem = workItemResponseFactory({
+ confidential: true,
+ });
+
+ // Mocks for work item without parent
+ const withoutParentExpectedInputVars = {
+ id: workItemQueryResponse.data.workItem.id,
+ confidential: true,
+ };
+ const toggleConfidentialityWithoutParentHandler = jest.fn().mockResolvedValue({
+ data: {
+ workItemUpdate: {
+ workItem: confidentialWorkItem.data.workItem,
+ errors: [],
+ },
+ },
+ });
+ const withoutParentHandlerMock = jest
+ .fn()
+ .mockResolvedValue(workItemQueryResponseWithoutParent);
+ const confidentialityWithoutParentMock = [
+ updateWorkItemMutation,
+ toggleConfidentialityWithoutParentHandler,
+ ];
+ const confidentialityWithoutParentFailureMock = [
+ updateWorkItemMutation,
+ jest.fn().mockRejectedValue(new Error(errorMessage)),
+ ];
+
+ // Mocks for work item with parent
+ const withParentExpectedInputVars = {
+ id: mockParent.parent.id,
+ taskData: { id: workItemQueryResponse.data.workItem.id, confidential: true },
+ };
+ const toggleConfidentialityWithParentHandler = jest.fn().mockResolvedValue({
+ data: {
+ workItemUpdate: {
+ workItem: {
+ id: confidentialWorkItem.data.workItem.id,
+ descriptionHtml: confidentialWorkItem.data.workItem.description,
+ },
+ task: {
+ workItem: confidentialWorkItem.data.workItem,
+ confidential: true,
+ },
+ errors: [],
+ },
+ },
+ });
+ const confidentialityWithParentMock = [
+ updateWorkItemTaskMutation,
+ toggleConfidentialityWithParentHandler,
+ ];
+ const confidentialityWithParentFailureMock = [
+ updateWorkItemTaskMutation,
+ jest.fn().mockRejectedValue(new Error(errorMessage)),
+ ];
+
+ describe.each`
+ context | handlerMock | confidentialityMock | confidentialityFailureMock | inputVariables
+ ${'no parent'} | ${withoutParentHandlerMock} | ${confidentialityWithoutParentMock} | ${confidentialityWithoutParentFailureMock} | ${withoutParentExpectedInputVars}
+ ${'parent'} | ${successHandler} | ${confidentialityWithParentMock} | ${confidentialityWithParentFailureMock} | ${withParentExpectedInputVars}
+ `(
+ 'when work item has $context',
+ ({ handlerMock, confidentialityMock, confidentialityFailureMock, inputVariables }) => {
+ it('renders confidential badge when work item is confidential', async () => {
+ createComponent({
+ handler: jest.fn().mockResolvedValue(confidentialWorkItem),
+ confidentialityMock,
+ });
+
+ await waitForPromises();
+
+ const confidentialBadge = wrapper.findComponent(GlBadge);
+ expect(confidentialBadge.exists()).toBe(true);
+ expect(confidentialBadge.props()).toMatchObject({
+ variant: 'warning',
+ icon: 'eye-slash',
+ });
+ expect(confidentialBadge.attributes('title')).toBe(
+ 'Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task.',
+ );
+ expect(confidentialBadge.text()).toBe('Confidential');
+ });
+
+ it('renders gl-loading-icon while update mutation is in progress', async () => {
+ createComponent({
+ handler: handlerMock,
+ confidentialityMock,
+ });
+
+ await waitForPromises();
+
+ findWorkItemActions().vm.$emit('toggleWorkItemConfidentiality', true);
+
+ await nextTick();
+
+ expect(findLoadingIcon().exists()).toBe(true);
+ });
+
+ it('emits workItemUpdated and shows confidentiality badge when mutation is successful', async () => {
+ createComponent({
+ handler: handlerMock,
+ confidentialityMock,
+ });
+
+ await waitForPromises();
+
+ findWorkItemActions().vm.$emit('toggleWorkItemConfidentiality', true);
+ await waitForPromises();
+
+ expect(wrapper.emitted('workItemUpdated')).toEqual([[{ confidential: true }]]);
+ expect(confidentialityMock[1]).toHaveBeenCalledWith({
+ input: inputVariables,
+ });
+ expect(findLoadingIcon().exists()).toBe(false);
+ });
+
+ it('shows alert message when mutation fails', async () => {
+ createComponent({
+ handler: handlerMock,
+ confidentialityMock: confidentialityFailureMock,
+ });
+
+ await waitForPromises();
+ findWorkItemActions().vm.$emit('toggleWorkItemConfidentiality', true);
+ await waitForPromises();
+ expect(wrapper.emitted('workItemUpdated')).toBeUndefined();
+
+ await nextTick();
+
+ expect(findAlert().exists()).toBe(true);
+ expect(findAlert().text()).toBe(errorMessage);
+ expect(findLoadingIcon().exists()).toBe(false);
+ });
+ },
+ );
+ });
+
+ describe('description', () => {
+ it('does not show description widget if loading description fails', () => {
+ createComponent();
+
+ expect(findWorkItemDescription().exists()).toBe(false);
+ });
+
+ it('shows description widget if description loads', async () => {
+ createComponent();
+ await waitForPromises();
+
+ expect(findWorkItemDescription().exists()).toBe(true);
+ });
+ });
+
+ describe('secondary breadcrumbs', () => {
+ it('does not show secondary breadcrumbs by default', () => {
+ createComponent();
+
+ expect(findParent().exists()).toBe(false);
+ });
+
+ it('does not show secondary breadcrumbs if there is not a parent', async () => {
+ createComponent({ handler: jest.fn().mockResolvedValue(workItemQueryResponseWithoutParent) });
+
+ await waitForPromises();
+
+ expect(findParent().exists()).toBe(false);
+ });
+
+ it('shows work item type if there is not a parent', async () => {
+ createComponent({ handler: jest.fn().mockResolvedValue(workItemQueryResponseWithoutParent) });
+
+ await waitForPromises();
+ expect(findWorkItemType().exists()).toBe(true);
+ });
+
+ describe('with parent', () => {
+ beforeEach(() => {
+ const parentResponse = workItemResponseFactory(mockParent);
+ createComponent({ handler: jest.fn().mockResolvedValue(parentResponse) });
+
+ return waitForPromises();
+ });
+
+ it('shows secondary breadcrumbs if there is a parent', () => {
+ expect(findParent().exists()).toBe(true);
+ });
+
+ it('does not show work item type', async () => {
+ expect(findWorkItemType().exists()).toBe(false);
+ });
+
+ it('sets the parent breadcrumb URL', () => {
+ expect(findParentButton().attributes().href).toBe('../../issues/5');
+ });
+ });
+ });
+
+ it('shows an error message when the work item query was unsuccessful', async () => {
+ const errorHandler = jest.fn().mockRejectedValue('Oops');
+ createComponent({ handler: errorHandler });
+ await waitForPromises();
+
+ expect(errorHandler).toHaveBeenCalled();
+ expect(findAlert().text()).toBe(i18n.fetchError);
+ });
+
+ it('shows an error message when WorkItemTitle emits an `error` event', async () => {
+ createComponent();
+ await waitForPromises();
+ const updateError = 'Failed to update';
+
+ findWorkItemTitle().vm.$emit('error', updateError);
+ await waitForPromises();
+
+ expect(findAlert().text()).toBe(updateError);
+ });
+
+ describe('subscriptions', () => {
+ it('calls the title subscription', () => {
+ createComponent();
+
+ expect(titleSubscriptionHandler).toHaveBeenCalledWith({
+ issuableId: workItemQueryResponse.data.workItem.id,
+ });
+ });
+
+ describe('dates subscription', () => {
+ describe('when the due date widget exists', () => {
+ it('calls the dates subscription', async () => {
+ createComponent();
+ await waitForPromises();
+
+ expect(datesSubscriptionHandler).toHaveBeenCalledWith({
+ issuableId: workItemQueryResponse.data.workItem.id,
+ });
+ });
+ });
+
+ describe('when the due date widget does not exist', () => {
+ it('does not call the dates subscription', async () => {
+ const response = workItemResponseFactory({ datesWidgetPresent: false });
+ const handler = jest.fn().mockResolvedValue(response);
+ createComponent({ handler, workItemsMvc2Enabled: true });
+ await waitForPromises();
+
+ expect(datesSubscriptionHandler).not.toHaveBeenCalled();
+ });
+ });
+ });
+ });
+
+ describe('assignees widget', () => {
+ it('renders assignees component when widget is returned from the API', async () => {
+ createComponent({
+ workItemsMvc2Enabled: true,
+ });
+ await waitForPromises();
+
+ expect(findWorkItemAssignees().exists()).toBe(true);
+ });
+
+ it('does not render assignees component when widget is not returned from the API', async () => {
+ createComponent({
+ workItemsMvc2Enabled: true,
+ handler: jest
+ .fn()
+ .mockResolvedValue(workItemResponseFactory({ assigneesWidgetPresent: false })),
+ });
+ await waitForPromises();
+
+ expect(findWorkItemAssignees().exists()).toBe(false);
+ });
+ });
+
+ describe('labels widget', () => {
+ it.each`
+ description | includeWidgets | exists
+ ${'renders when widget is returned from API'} | ${true} | ${true}
+ ${'does not render when widget is not returned from API'} | ${false} | ${false}
+ `('$description', async ({ includeWidgets, exists }) => {
+ createComponent({ includeWidgets, workItemsMvc2Enabled: true });
+ await waitForPromises();
+
+ expect(findWorkItemLabels().exists()).toBe(exists);
+ });
+ });
+
+ describe('dates widget', () => {
+ describe.each`
+ description | datesWidgetPresent | exists
+ ${'when widget is returned from API'} | ${true} | ${true}
+ ${'when widget is not returned from API'} | ${false} | ${false}
+ `('$description', ({ datesWidgetPresent, exists }) => {
+ it(`${datesWidgetPresent ? 'renders' : 'does not render'} due date component`, async () => {
+ const response = workItemResponseFactory({ datesWidgetPresent });
+ const handler = jest.fn().mockResolvedValue(response);
+ createComponent({ handler, workItemsMvc2Enabled: true });
+ await waitForPromises();
+
+ expect(findWorkItemDueDate().exists()).toBe(exists);
+ });
+ });
+
+ it('shows an error message when it emits an `error` event', async () => {
+ createComponent({ workItemsMvc2Enabled: true });
+ await waitForPromises();
+ const updateError = 'Failed to update';
+
+ findWorkItemDueDate().vm.$emit('error', updateError);
+ await waitForPromises();
+
+ expect(findAlert().text()).toBe(updateError);
+ });
+ });
+
+ 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);
+ });
+ });
+});
diff --git a/spec/frontend/work_items/components/work_item_due_date_spec.js b/spec/frontend/work_items/components/work_item_due_date_spec.js
new file mode 100644
index 00000000000..1d76154a1f0
--- /dev/null
+++ b/spec/frontend/work_items/components/work_item_due_date_spec.js
@@ -0,0 +1,346 @@
+import { GlFormGroup, GlDatepicker } from '@gitlab/ui';
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { mockTracking } from 'helpers/tracking_helper';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import WorkItemDueDate from '~/work_items/components/work_item_due_date.vue';
+import { TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
+import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
+import { updateWorkItemMutationResponse, updateWorkItemMutationErrorResponse } from '../mock_data';
+
+describe('WorkItemDueDate component', () => {
+ let wrapper;
+
+ Vue.use(VueApollo);
+
+ const workItemId = 'gid://gitlab/WorkItem/1';
+ const updateWorkItemMutationHandler = jest.fn().mockResolvedValue(updateWorkItemMutationResponse);
+
+ const findStartDateButton = () =>
+ wrapper.findByRole('button', { name: WorkItemDueDate.i18n.addStartDate });
+ const findStartDateInput = () => wrapper.findByLabelText(WorkItemDueDate.i18n.startDate);
+ const findStartDatePicker = () => wrapper.findComponent(GlDatepicker);
+ const findDueDateButton = () =>
+ wrapper.findByRole('button', { name: WorkItemDueDate.i18n.addDueDate });
+ const findDueDateInput = () => wrapper.findByLabelText(WorkItemDueDate.i18n.dueDate);
+ const findDueDatePicker = () => wrapper.findAllComponents(GlDatepicker).at(1);
+ const findGlFormGroup = () => wrapper.findComponent(GlFormGroup);
+
+ const createComponent = ({
+ canUpdate = false,
+ dueDate = null,
+ startDate = null,
+ mutationHandler = updateWorkItemMutationHandler,
+ } = {}) => {
+ wrapper = mountExtended(WorkItemDueDate, {
+ apolloProvider: createMockApollo([[updateWorkItemMutation, mutationHandler]]),
+ propsData: {
+ canUpdate,
+ dueDate,
+ startDate,
+ workItemId,
+ workItemType: 'Task',
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when can update', () => {
+ describe('start date', () => {
+ describe('`Add start date` button', () => {
+ describe.each`
+ description | startDate | exists
+ ${'when there is no start date'} | ${null} | ${true}
+ ${'when there is a start date'} | ${'2022-01-01'} | ${false}
+ `('$description', ({ startDate, exists }) => {
+ beforeEach(() => {
+ createComponent({ canUpdate: true, startDate });
+ });
+
+ it(exists ? 'renders' : 'does not render', () => {
+ expect(findStartDateButton().exists()).toBe(exists);
+ });
+ });
+
+ describe('when it emits `click` event', () => {
+ beforeEach(() => {
+ createComponent({ canUpdate: true, startDate: null });
+ findStartDateButton().vm.$emit('click');
+ });
+
+ it('renders start date picker', () => {
+ expect(findStartDateInput().exists()).toBe(true);
+ });
+
+ it('hides itself', () => {
+ expect(findStartDateButton().exists()).toBe(false);
+ });
+ });
+ });
+
+ describe('date picker', () => {
+ describe('when it emits a `clear` event', () => {
+ beforeEach(() => {
+ createComponent({ canUpdate: true, dueDate: '2022-01-01', startDate: '2022-01-01' });
+ findStartDatePicker().vm.$emit('clear');
+ });
+
+ it('hides the date picker', () => {
+ expect(findStartDateInput().exists()).toBe(false);
+ });
+
+ it('shows the `Add start date` button', () => {
+ expect(findStartDateButton().exists()).toBe(true);
+ });
+
+ it('calls a mutation to update the dates', () => {
+ expect(updateWorkItemMutationHandler).toHaveBeenCalledWith({
+ input: {
+ id: workItemId,
+ startAndDueDateWidget: {
+ dueDate: new Date('2022-01-01T00:00:00.000Z'),
+ startDate: null,
+ },
+ },
+ });
+ });
+ });
+
+ describe('when it emits a `close` event', () => {
+ describe('when the start date is earlier than the due date', () => {
+ const startDate = new Date('2022-01-01T00:00:00.000Z');
+
+ beforeEach(() => {
+ createComponent({ canUpdate: true, dueDate: '2022-12-31', startDate: '2022-12-31' });
+ findStartDatePicker().vm.$emit('input', startDate);
+ findStartDatePicker().vm.$emit('close');
+ });
+
+ it('calls a mutation to update the dates', () => {
+ expect(updateWorkItemMutationHandler).toHaveBeenCalledWith({
+ input: {
+ id: workItemId,
+ startAndDueDateWidget: {
+ dueDate: new Date('2022-12-31T00:00:00.000Z'),
+ startDate,
+ },
+ },
+ });
+ });
+ });
+
+ describe('when the start date is later than the due date', () => {
+ const startDate = new Date('2030-01-01T00:00:00.000Z');
+ let datePickerOpenSpy;
+
+ beforeEach(() => {
+ createComponent({ canUpdate: true, dueDate: '2022-12-31', startDate: '2022-12-31' });
+ datePickerOpenSpy = jest.spyOn(wrapper.vm.$refs.dueDatePicker.calendar, 'show');
+ findStartDatePicker().vm.$emit('input', startDate);
+ findStartDatePicker().vm.$emit('close');
+ });
+
+ it('does not call a mutation to update the dates', () => {
+ expect(updateWorkItemMutationHandler).not.toHaveBeenCalled();
+ });
+
+ it('updates the due date picker to the same date', () => {
+ expect(findDueDatePicker().props('value')).toEqual(startDate);
+ });
+
+ it('opens the due date picker', () => {
+ expect(datePickerOpenSpy).toHaveBeenCalled();
+ });
+ });
+ });
+ });
+ });
+
+ describe('due date', () => {
+ describe('`Add due date` button', () => {
+ describe.each`
+ description | dueDate | exists
+ ${'when there is no due date'} | ${null} | ${true}
+ ${'when there is a due date'} | ${'2022-01-01'} | ${false}
+ `('$description', ({ dueDate, exists }) => {
+ beforeEach(() => {
+ createComponent({ canUpdate: true, dueDate });
+ });
+
+ it(exists ? 'renders' : 'does not render', () => {
+ expect(findDueDateButton().exists()).toBe(exists);
+ });
+ });
+
+ describe('when it emits `click` event', () => {
+ beforeEach(() => {
+ createComponent({ canUpdate: true, dueDate: null });
+ findDueDateButton().vm.$emit('click');
+ });
+
+ it('renders due date picker', () => {
+ expect(findDueDateInput().exists()).toBe(true);
+ });
+
+ it('hides itself', () => {
+ expect(findDueDateButton().exists()).toBe(false);
+ });
+ });
+ });
+
+ describe('date picker', () => {
+ describe('when it emits a `clear` event', () => {
+ beforeEach(() => {
+ createComponent({ canUpdate: true, dueDate: '2022-01-01', startDate: '2022-01-01' });
+ findDueDatePicker().vm.$emit('clear');
+ });
+
+ it('hides the date picker', () => {
+ expect(findDueDateInput().exists()).toBe(false);
+ });
+
+ it('shows the `Add due date` button', () => {
+ expect(findDueDateButton().exists()).toBe(true);
+ });
+
+ it('calls a mutation to update the dates', () => {
+ expect(updateWorkItemMutationHandler).toHaveBeenCalledWith({
+ input: {
+ id: workItemId,
+ startAndDueDateWidget: {
+ dueDate: null,
+ startDate: new Date('2022-01-01T00:00:00.000Z'),
+ },
+ },
+ });
+ });
+ });
+
+ describe('when it emits a `close` event', () => {
+ const dueDate = new Date('2022-12-31T00:00:00.000Z');
+
+ beforeEach(() => {
+ createComponent({ canUpdate: true, dueDate: '2022-01-01', startDate: '2022-01-01' });
+ findDueDatePicker().vm.$emit('input', dueDate);
+ findDueDatePicker().vm.$emit('close');
+ });
+
+ it('calls a mutation to update the dates', () => {
+ expect(updateWorkItemMutationHandler).toHaveBeenCalledWith({
+ input: {
+ id: workItemId,
+ startAndDueDateWidget: {
+ dueDate,
+ startDate: new Date('2022-01-01T00:00:00.000Z'),
+ },
+ },
+ });
+ });
+ });
+ });
+ });
+
+ describe('when updating date', () => {
+ describe('when dates are changed', () => {
+ let trackingSpy;
+
+ beforeEach(() => {
+ createComponent({ canUpdate: true, dueDate: '2022-12-31', startDate: '2022-12-31' });
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+
+ findStartDatePicker().vm.$emit('input', new Date('2022-01-01T00:00:00.000Z'));
+ findStartDatePicker().vm.$emit('close');
+ });
+
+ it('mutation is called to update dates', () => {
+ expect(updateWorkItemMutationHandler).toHaveBeenCalledWith({
+ input: {
+ id: workItemId,
+ startAndDueDateWidget: {
+ dueDate: new Date('2022-12-31T00:00:00.000Z'),
+ startDate: new Date('2022-01-01T00:00:00.000Z'),
+ },
+ },
+ });
+ });
+
+ it('start date input is disabled', () => {
+ expect(findStartDatePicker().props('disabled')).toBe(true);
+ });
+
+ it('due date input is disabled', () => {
+ expect(findDueDatePicker().props('disabled')).toBe(true);
+ });
+
+ it('tracks updating the dates', () => {
+ expect(trackingSpy).toHaveBeenCalledWith(TRACKING_CATEGORY_SHOW, 'updated_dates', {
+ category: TRACKING_CATEGORY_SHOW,
+ label: 'item_dates',
+ property: 'type_Task',
+ });
+ });
+ });
+
+ describe('when dates are unchanged', () => {
+ beforeEach(() => {
+ createComponent({ canUpdate: true, dueDate: '2022-12-31', startDate: '2022-12-31' });
+
+ findStartDatePicker().vm.$emit('input', new Date('2022-12-31T00:00:00.000Z'));
+ findStartDatePicker().vm.$emit('close');
+ });
+
+ it('mutation is not called to update dates', () => {
+ expect(updateWorkItemMutationHandler).not.toHaveBeenCalled();
+ });
+ });
+
+ describe.each`
+ description | mutationHandler
+ ${'when there is a GraphQL error'} | ${jest.fn().mockResolvedValue(updateWorkItemMutationErrorResponse)}
+ ${'when there is a network error'} | ${jest.fn().mockRejectedValue(new Error())}
+ `('$description', ({ mutationHandler }) => {
+ beforeEach(() => {
+ createComponent({
+ canUpdate: true,
+ dueDate: '2022-12-31',
+ startDate: '2022-12-31',
+ mutationHandler,
+ });
+
+ findStartDatePicker().vm.$emit('input', new Date('2022-01-01T00:00:00.000Z'));
+ findStartDatePicker().vm.$emit('close');
+ return waitForPromises();
+ });
+
+ it('emits an error', () => {
+ expect(wrapper.emitted('error')).toEqual([
+ ['Something went wrong while updating the task. Please try again.'],
+ ]);
+ });
+ });
+ });
+ });
+
+ describe('when cannot update', () => {
+ it('start and due date inputs are disabled', async () => {
+ createComponent({ canUpdate: false, dueDate: '2022-01-01', startDate: '2022-01-01' });
+ await nextTick();
+
+ expect(findStartDateInput().props('disabled')).toBe(true);
+ expect(findDueDateInput().props('disabled')).toBe(true);
+ });
+
+ describe('when there is no start and due date', () => {
+ it('shows None', () => {
+ createComponent({ canUpdate: false, dueDate: null, startDate: null });
+
+ expect(findGlFormGroup().text()).toContain(WorkItemDueDate.i18n.none);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/work_items/components/work_item_information_spec.js b/spec/frontend/work_items/components/work_item_information_spec.js
index d5f6921c2bc..887c5f615e9 100644
--- a/spec/frontend/work_items/components/work_item_information_spec.js
+++ b/spec/frontend/work_items/components/work_item_information_spec.js
@@ -8,7 +8,6 @@ const createComponent = () => mount(WorkItemInformation);
describe('Work item information alert', () => {
let wrapper;
const tasksHelpPath = helpPagePath('user/tasks');
- const workItemsHelpPath = helpPagePath('development/work_items');
const findAlert = () => wrapper.findComponent(GlAlert);
const findHelpLink = () => wrapper.findComponent(GlLink);
@@ -33,16 +32,12 @@ describe('Work item information alert', () => {
expect(findAlert().props('variant')).toBe('tip');
});
- it('should have the correct text for primary button and link', () => {
+ it('should have the correct text for title', () => {
expect(findAlert().props('title')).toBe(WorkItemInformation.i18n.tasksInformationTitle);
- expect(findAlert().props('primaryButtonText')).toBe(
- WorkItemInformation.i18n.learnTasksButtonText,
- );
- expect(findAlert().props('primaryButtonLink')).toBe(tasksHelpPath);
});
it('should have the correct link to work item link', () => {
expect(findHelpLink().exists()).toBe(true);
- expect(findHelpLink().attributes('href')).toBe(workItemsHelpPath);
+ 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 1734b901d1a..1d976897c15 100644
--- a/spec/frontend/work_items/components/work_item_labels_spec.js
+++ b/spec/frontend/work_items/components/work_item_labels_spec.js
@@ -9,7 +9,7 @@ import labelSearchQuery from '~/vue_shared/components/sidebar/labels_select_widg
import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
import WorkItemLabels from '~/work_items/components/work_item_labels.vue';
import { i18n } from '~/work_items/constants';
-import { temporaryConfig, resolvers } from '~/work_items/graphql/provider';
+import { temporaryConfig, resolvers } from '~/graphql_shared/issuable_client';
import { projectLabelsResponse, mockLabels, workItemQueryResponse } from '../mock_data';
Vue.use(VueApollo);
@@ -45,13 +45,11 @@ describe('WorkItemLabels component', () => {
});
wrapper = mountExtended(WorkItemLabels, {
- provide: {
- fullPath: 'test-project-path',
- },
propsData: {
labels,
workItemId,
canUpdate,
+ fullPath: 'test-project-path',
},
attachTo: document.body,
apolloProvider,
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
new file mode 100644
index 00000000000..1d5472a0473
--- /dev/null
+++ b/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js
@@ -0,0 +1,122 @@
+import { GlButton, GlIcon } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+
+import RichTimestampTooltip from '~/vue_shared/components/rich_timestamp_tooltip.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 { workItemTask, confidentialWorkItemTask, closedWorkItemTask } from '../../mock_data';
+
+describe('WorkItemLinkChild', () => {
+ const WORK_ITEM_ID = 'gid://gitlab/WorkItem/2';
+ let wrapper;
+
+ const createComponent = ({
+ projectPath = 'gitlab-org/gitlab-test',
+ canUpdate = true,
+ issuableGid = WORK_ITEM_ID,
+ childItem = workItemTask,
+ } = {}) => {
+ wrapper = shallowMountExtended(WorkItemLinkChild, {
+ propsData: {
+ projectPath,
+ canUpdate,
+ issuableGid,
+ childItem,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it.each`
+ status | childItem | statusIconName | statusIconColorClass | rawTimestamp | tooltipContents
+ ${'open'} | ${workItemTask} | ${'issue-open-m'} | ${'gl-text-green-500'} | ${workItemTask.createdAt} | ${'Created'}
+ ${'closed'} | ${closedWorkItemTask} | ${'issue-close'} | ${'gl-text-blue-500'} | ${closedWorkItemTask.closedAt} | ${'Closed'}
+ `(
+ 'renders item status icon and tooltip when item status is `$status`',
+ ({ childItem, statusIconName, statusIconColorClass, rawTimestamp, tooltipContents }) => {
+ createComponent({ childItem });
+
+ const statusIcon = wrapper.findByTestId('item-status-icon').findComponent(GlIcon);
+ const statusTooltip = wrapper.findComponent(RichTimestampTooltip);
+
+ expect(statusIcon.props('name')).toBe(statusIconName);
+ expect(statusIcon.classes()).toContain(statusIconColorClass);
+ expect(statusTooltip.props('rawTimestamp')).toBe(rawTimestamp);
+ expect(statusTooltip.props('timestampTypeText')).toContain(tooltipContents);
+ },
+ );
+
+ it('renders confidential icon when item is confidential', () => {
+ createComponent({ childItem: confidentialWorkItemTask });
+
+ const confidentialIcon = wrapper.findByTestId('confidential-icon');
+
+ expect(confidentialIcon.props('name')).toBe('eye-slash');
+ expect(confidentialIcon.attributes('title')).toBe('Confidential');
+ });
+
+ describe('item title', () => {
+ let titleEl;
+
+ beforeEach(() => {
+ createComponent();
+
+ titleEl = wrapper.findComponent(GlButton);
+ });
+
+ it('renders item title', () => {
+ expect(titleEl.attributes('href')).toBe('/gitlab-org/gitlab-test/-/work_items/4');
+ expect(titleEl.text()).toBe(workItemTask.title);
+ });
+
+ 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 }) => {
+ const eventObj = {
+ preventDefault: jest.fn(),
+ };
+ titleEl.vm.$emit(event, eventObj);
+
+ expect(wrapper.emitted(emittedEvent)).toEqual([[workItemTask.id, eventObj]]);
+ });
+ });
+
+ describe('item menu', () => {
+ let itemMenuEl;
+
+ beforeEach(() => {
+ createComponent();
+
+ itemMenuEl = wrapper.findComponent(WorkItemLinksMenu);
+ });
+
+ it('renders work-item-links-menu', () => {
+ expect(itemMenuEl.exists()).toBe(true);
+
+ expect(itemMenuEl.attributes()).toMatchObject({
+ 'work-item-id': workItemTask.id,
+ 'parent-work-item-id': WORK_ITEM_ID,
+ });
+ });
+
+ it('does not render work-item-links-menu when canUpdate is false', () => {
+ createComponent({ canUpdate: false });
+
+ expect(wrapper.findComponent(WorkItemLinksMenu).exists()).toBe(false);
+ });
+
+ it('removeChild event on menu triggers `click-remove-child` event', () => {
+ itemMenuEl.vm.$emit('removeChild');
+
+ expect(wrapper.emitted('remove')).toEqual([[workItemTask.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 00f508f1548..876aedff08b 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
@@ -1,12 +1,13 @@
import Vue, { nextTick } from 'vue';
-import { GlButton, GlIcon, GlAlert } from '@gitlab/ui';
+import { GlAlert } from '@gitlab/ui';
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 SidebarEventHub from '~/sidebar/event_hub';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
+import issueConfidentialQuery from '~/sidebar/queries/issue_confidential.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 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';
@@ -20,6 +21,20 @@ import {
Vue.use(VueApollo);
+const issueConfidentialityResponse = (confidential = false) => ({
+ data: {
+ workspace: {
+ id: '1',
+ __typename: 'Project',
+ issuable: {
+ __typename: 'Issue',
+ id: 'gid://gitlab/Issue/4',
+ confidential,
+ },
+ },
+ },
+});
+
describe('WorkItemLinks', () => {
let wrapper;
let mockApollo;
@@ -36,18 +51,18 @@ describe('WorkItemLinks', () => {
const childWorkItemQueryHandler = jest.fn().mockResolvedValue(workItemQueryResponse);
- const findChildren = () => wrapper.findAll('[data-testid="links-child"]');
-
const createComponent = async ({
data = {},
fetchHandler = jest.fn().mockResolvedValue(workItemHierarchyResponse),
mutationHandler = mutationChangeParentHandler,
+ confidentialQueryHandler = jest.fn().mockResolvedValue(issueConfidentialityResponse()),
} = {}) => {
mockApollo = createMockApollo(
[
[getWorkItemLinksQuery, fetchHandler],
[changeWorkItemParentMutation, mutationHandler],
[workItemQuery, childWorkItemQueryHandler],
+ [issueConfidentialQuery, confidentialQueryHandler],
],
{},
{ addTypename: true },
@@ -61,6 +76,7 @@ describe('WorkItemLinks', () => {
},
provide: {
projectPath: 'project/path',
+ iid: '1',
},
propsData: { issuableId: 1 },
apolloProvider: mockApollo,
@@ -77,8 +93,9 @@ describe('WorkItemLinks', () => {
const findLinksBody = () => wrapper.findByTestId('links-body');
const findEmptyState = () => wrapper.findByTestId('links-empty');
const findToggleAddFormButton = () => wrapper.findByTestId('toggle-add-form');
+ const findWorkItemLinkChildItems = () => wrapper.findAllComponents(WorkItemLinkChild);
+ const findFirstWorkItemLinkChild = () => findWorkItemLinkChildItems().at(0);
const findAddLinksForm = () => wrapper.findByTestId('add-links-form');
- const findFirstLinksMenu = () => wrapper.findByTestId('links-menu');
const findChildrenCount = () => wrapper.findByTestId('children-count');
beforeEach(async () => {
@@ -132,8 +149,7 @@ describe('WorkItemLinks', () => {
it('renders all hierarchy widget children', () => {
expect(findLinksBody().exists()).toBe(true);
- expect(findChildren()).toHaveLength(4);
- expect(findFirstLinksMenu().exists()).toBe(true);
+ expect(findWorkItemLinkChildItems()).toHaveLength(4);
});
it('shows alert when list loading fails', async () => {
@@ -148,40 +164,12 @@ describe('WorkItemLinks', () => {
expect(findAlert().text()).toBe(errorMessage);
});
- it('renders widget child icon and tooltip', () => {
- expect(findChildren().at(0).findComponent(GlIcon).props('name')).toBe('issue-open-m');
- expect(findChildren().at(1).findComponent(GlIcon).props('name')).toBe('issue-close');
- });
-
- it('renders confidentiality icon when child item is confidential', () => {
- const children = wrapper.findAll('[data-testid="links-child"]');
- const confidentialIcon = children.at(0).find('[data-testid="confidential-icon"]');
-
- expect(confidentialIcon.exists()).toBe(true);
- expect(confidentialIcon.props('name')).toBe('eye-slash');
- });
-
it('displays number if children', () => {
expect(findChildrenCount().exists()).toBe(true);
expect(findChildrenCount().text()).toContain('4');
});
- it('refetches child items when `confidentialityUpdated` event is emitted on SidebarEventhub', async () => {
- const fetchHandler = jest.fn().mockResolvedValue(workItemHierarchyResponse);
- await createComponent({
- fetchHandler,
- });
- await waitForPromises();
-
- SidebarEventHub.$emit('confidentialityUpdated');
- await nextTick();
-
- // First call is done on component mount.
- // Second call is done on confidentialityUpdated event.
- expect(fetchHandler).toHaveBeenCalledTimes(2);
- });
-
describe('when no permission to update', () => {
beforeEach(async () => {
await createComponent({
@@ -194,17 +182,21 @@ describe('WorkItemLinks', () => {
});
it('does not display link menu on children', () => {
- expect(findFirstLinksMenu().exists()).toBe(false);
+ expect(findWorkItemLinkChildItems().at(0).props('canUpdate')).toBe(false);
});
});
describe('remove child', () => {
+ let firstChild;
+
beforeEach(async () => {
await createComponent({ mutationHandler: mutationChangeParentHandler });
+
+ firstChild = findFirstWorkItemLinkChild();
});
it('calls correct mutation with correct variables', async () => {
- findFirstLinksMenu().vm.$emit('removeChild');
+ firstChild.vm.$emit('remove', firstChild.vm.childItem.id);
await waitForPromises();
@@ -219,7 +211,7 @@ describe('WorkItemLinks', () => {
});
it('shows toast when mutation succeeds', async () => {
- findFirstLinksMenu().vm.$emit('removeChild');
+ firstChild.vm.$emit('remove', firstChild.vm.childItem.id);
await waitForPromises();
@@ -229,28 +221,30 @@ describe('WorkItemLinks', () => {
});
it('renders correct number of children after removal', async () => {
- expect(findChildren()).toHaveLength(4);
+ expect(findWorkItemLinkChildItems()).toHaveLength(4);
- findFirstLinksMenu().vm.$emit('removeChild');
+ firstChild.vm.$emit('remove', firstChild.vm.childItem.id);
await waitForPromises();
- expect(findChildren()).toHaveLength(3);
+ expect(findWorkItemLinkChildItems()).toHaveLength(3);
});
});
describe('prefetching child items', () => {
+ let firstChild;
+
beforeEach(async () => {
await createComponent();
- });
- const findChildLink = () => findChildren().at(0).findComponent(GlButton);
+ firstChild = findFirstWorkItemLinkChild();
+ });
it('does not fetch the child work item before hovering work item links', () => {
expect(childWorkItemQueryHandler).not.toHaveBeenCalled();
});
it('fetches the child work item if link is hovered for 250+ ms', async () => {
- findChildLink().vm.$emit('mouseover');
+ firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id);
jest.advanceTimersByTime(DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
await waitForPromises();
@@ -260,12 +254,24 @@ describe('WorkItemLinks', () => {
});
it('does not fetch the child work item if link is hovered for less than 250 ms', async () => {
- findChildLink().vm.$emit('mouseover');
+ firstChild.vm.$emit('mouseover', firstChild.vm.childItem.id);
jest.advanceTimersByTime(200);
- findChildLink().vm.$emit('mouseout');
+ firstChild.vm.$emit('mouseout', firstChild.vm.childItem.id);
await waitForPromises();
expect(childWorkItemQueryHandler).not.toHaveBeenCalled();
});
});
+
+ describe('when parent item is confidential', () => {
+ it('passes correct confidentiality status to form', async () => {
+ await createComponent({
+ confidentialQueryHandler: jest.fn().mockResolvedValue(issueConfidentialityResponse(true)),
+ });
+ findToggleAddFormButton().vm.$emit('click');
+ await nextTick();
+
+ expect(findAddLinksForm().props('parentConfidential')).toBe(true);
+ });
+ });
});
diff --git a/spec/frontend/work_items/components/work_item_state_spec.js b/spec/frontend/work_items/components/work_item_state_spec.js
index 6b23a6e4795..b24d940d56a 100644
--- a/spec/frontend/work_items/components/work_item_state_spec.js
+++ b/spec/frontend/work_items/components/work_item_state_spec.js
@@ -7,7 +7,6 @@ import waitForPromises from 'helpers/wait_for_promises';
import ItemState from '~/work_items/components/item_state.vue';
import WorkItemState from '~/work_items/components/work_item_state.vue';
import {
- i18n,
STATE_OPEN,
STATE_CLOSED,
STATE_EVENT_CLOSE,
@@ -104,7 +103,9 @@ describe('WorkItemState component', () => {
findItemState().vm.$emit('changed', STATE_CLOSED);
await waitForPromises();
- expect(wrapper.emitted('error')).toEqual([[i18n.updateError]]);
+ expect(wrapper.emitted('error')).toEqual([
+ ['Something went wrong while updating the task. Please try again.'],
+ ]);
});
it('tracks editing the state', async () => {
diff --git a/spec/frontend/work_items/components/work_item_title_spec.js b/spec/frontend/work_items/components/work_item_title_spec.js
index c0d966abab8..a549aad5cd8 100644
--- a/spec/frontend/work_items/components/work_item_title_spec.js
+++ b/spec/frontend/work_items/components/work_item_title_spec.js
@@ -6,7 +6,7 @@ import { mockTracking } from 'helpers/tracking_helper';
import waitForPromises from 'helpers/wait_for_promises';
import ItemTitle from '~/work_items/components/item_title.vue';
import WorkItemTitle from '~/work_items/components/work_item_title.vue';
-import { i18n, TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
+import { TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
import updateWorkItemTaskMutation from '~/work_items/graphql/update_work_item_task.mutation.graphql';
import { updateWorkItemMutationResponse, workItemQueryResponse } from '../mock_data';
@@ -116,7 +116,9 @@ describe('WorkItemTitle component', () => {
findItemTitle().vm.$emit('title-changed', 'new title');
await waitForPromises();
- expect(wrapper.emitted('error')).toEqual([[i18n.updateError]]);
+ expect(wrapper.emitted('error')).toEqual([
+ ['Something went wrong while updating the task. Please try again.'],
+ ]);
});
it('tracks editing the title', async () => {
diff --git a/spec/frontend/work_items/components/work_item_type_icon_spec.js b/spec/frontend/work_items/components/work_item_type_icon_spec.js
index 85466578e18..95ddfc3980e 100644
--- a/spec/frontend/work_items/components/work_item_type_icon_spec.js
+++ b/spec/frontend/work_items/components/work_item_type_icon_spec.js
@@ -1,11 +1,17 @@
import { GlIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import WorkItemTypeIcon from '~/work_items/components/work_item_type_icon.vue';
+import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
let wrapper;
function createComponent(propsData) {
- wrapper = shallowMount(WorkItemTypeIcon, { propsData });
+ wrapper = shallowMount(WorkItemTypeIcon, {
+ propsData,
+ directives: {
+ GlTooltip: createMockDirective(),
+ },
+ });
}
describe('Work Item type component', () => {
@@ -16,22 +22,23 @@ describe('Work Item type component', () => {
});
describe.each`
- workItemType | workItemIconName | iconName | text
- ${'TASK'} | ${''} | ${'issue-type-task'} | ${'Task'}
- ${''} | ${'issue-type-task'} | ${'issue-type-task'} | ${''}
- ${'ISSUE'} | ${''} | ${'issue-type-issue'} | ${'Issue'}
- ${''} | ${'issue-type-issue'} | ${'issue-type-issue'} | ${''}
- ${'REQUIREMENTS'} | ${''} | ${'issue-type-requirements'} | ${'Requirements'}
- ${'INCIDENT'} | ${''} | ${'issue-type-incident'} | ${'Incident'}
- ${'TEST_CASE'} | ${''} | ${'issue-type-test-case'} | ${'Test case'}
- ${'random-issue-type'} | ${''} | ${'issue-type-issue'} | ${''}
+ workItemType | workItemIconName | iconName | text | showTooltipOnHover
+ ${'TASK'} | ${''} | ${'issue-type-task'} | ${'Task'} | ${false}
+ ${''} | ${'issue-type-task'} | ${'issue-type-task'} | ${''} | ${true}
+ ${'ISSUE'} | ${''} | ${'issue-type-issue'} | ${'Issue'} | ${true}
+ ${''} | ${'issue-type-issue'} | ${'issue-type-issue'} | ${''} | ${true}
+ ${'REQUIREMENTS'} | ${''} | ${'issue-type-requirements'} | ${'Requirements'} | ${true}
+ ${'INCIDENT'} | ${''} | ${'issue-type-incident'} | ${'Incident'} | ${false}
+ ${'TEST_CASE'} | ${''} | ${'issue-type-test-case'} | ${'Test case'} | ${true}
+ ${'random-issue-type'} | ${''} | ${'issue-type-issue'} | ${''} | ${true}
`(
'with workItemType set to "$workItemType" and workItemIconName set to "$workItemIconName"',
- ({ workItemType, workItemIconName, iconName, text }) => {
+ ({ workItemType, workItemIconName, iconName, text, showTooltipOnHover }) => {
beforeEach(() => {
createComponent({
workItemType,
workItemIconName,
+ showTooltipOnHover,
});
});
@@ -42,6 +49,16 @@ describe('Work Item type component', () => {
it(`renders correct text`, () => {
expect(wrapper.text()).toBe(text);
});
+
+ it('renders the icon in gray color', () => {
+ expect(findIcon().classes()).toContain('gl-text-gray-500');
+ });
+
+ it('shows tooltip on hover when props passed', () => {
+ const tooltip = getBinding(findIcon().element, 'gl-tooltip');
+
+ expect(tooltip.value).toBe(showTooltipOnHover);
+ });
},
);
});
diff --git a/spec/frontend/work_items/components/work_item_weight_spec.js b/spec/frontend/work_items/components/work_item_weight_spec.js
deleted file mode 100644
index 94bdb336deb..00000000000
--- a/spec/frontend/work_items/components/work_item_weight_spec.js
+++ /dev/null
@@ -1,214 +0,0 @@
-import { GlForm, GlFormInput } from '@gitlab/ui';
-import Vue, { nextTick } from 'vue';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import { mockTracking } from 'helpers/tracking_helper';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { __ } from '~/locale';
-import WorkItemWeight from '~/work_items/components/work_item_weight.vue';
-import { i18n, TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
-import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
-import { updateWorkItemMutationResponse } from 'jest/work_items/mock_data';
-
-describe('WorkItemWeight component', () => {
- Vue.use(VueApollo);
-
- let wrapper;
-
- const workItemId = 'gid://gitlab/WorkItem/1';
- const workItemType = 'Task';
-
- const findForm = () => wrapper.findComponent(GlForm);
- const findInput = () => wrapper.findComponent(GlFormInput);
-
- const createComponent = ({
- canUpdate = false,
- hasIssueWeightsFeature = true,
- isEditing = false,
- weight,
- mutationHandler = jest.fn().mockResolvedValue(updateWorkItemMutationResponse),
- } = {}) => {
- wrapper = mountExtended(WorkItemWeight, {
- apolloProvider: createMockApollo([[updateWorkItemMutation, mutationHandler]]),
- propsData: {
- canUpdate,
- weight,
- workItemId,
- workItemType,
- },
- provide: {
- hasIssueWeightsFeature,
- },
- });
-
- if (isEditing) {
- findInput().vm.$emit('focus');
- }
- };
-
- describe('`issue_weights` licensed feature', () => {
- describe.each`
- description | hasIssueWeightsFeature | exists
- ${'when available'} | ${true} | ${true}
- ${'when not available'} | ${false} | ${false}
- `('$description', ({ hasIssueWeightsFeature, exists }) => {
- it(hasIssueWeightsFeature ? 'renders component' : 'does not render component', () => {
- createComponent({ hasIssueWeightsFeature });
-
- expect(findForm().exists()).toBe(exists);
- });
- });
- });
-
- describe('weight input', () => {
- it('has "Weight" label', () => {
- createComponent();
-
- expect(wrapper.findByLabelText(__('Weight')).exists()).toBe(true);
- });
-
- describe('placeholder attribute', () => {
- describe.each`
- description | isEditing | canUpdate | value
- ${'when not editing and cannot update'} | ${false} | ${false} | ${__('None')}
- ${'when editing and cannot update'} | ${true} | ${false} | ${__('None')}
- ${'when not editing and can update'} | ${false} | ${true} | ${__('None')}
- ${'when editing and can update'} | ${true} | ${true} | ${__('Enter a number')}
- `('$description', ({ isEditing, canUpdate, value }) => {
- it(`has a value of "${value}"`, async () => {
- createComponent({ canUpdate, isEditing });
- await nextTick();
-
- expect(findInput().attributes('placeholder')).toBe(value);
- });
- });
- });
-
- describe('readonly attribute', () => {
- describe.each`
- description | canUpdate | value
- ${'when cannot update'} | ${false} | ${'readonly'}
- ${'when can update'} | ${true} | ${undefined}
- `('$description', ({ canUpdate, value }) => {
- it(`renders readonly=${value}`, () => {
- createComponent({ canUpdate });
-
- expect(findInput().attributes('readonly')).toBe(value);
- });
- });
- });
-
- describe('type attribute', () => {
- describe.each`
- description | isEditing | canUpdate | type
- ${'when not editing and cannot update'} | ${false} | ${false} | ${'text'}
- ${'when editing and cannot update'} | ${true} | ${false} | ${'text'}
- ${'when not editing and can update'} | ${false} | ${true} | ${'text'}
- ${'when editing and can update'} | ${true} | ${true} | ${'number'}
- `('$description', ({ isEditing, canUpdate, type }) => {
- it(`has a value of "${type}"`, async () => {
- createComponent({ canUpdate, isEditing });
- await nextTick();
-
- expect(findInput().attributes('type')).toBe(type);
- });
- });
- });
-
- describe('value attribute', () => {
- describe.each`
- weight | value
- ${1} | ${'1'}
- ${0} | ${'0'}
- ${null} | ${''}
- ${undefined} | ${''}
- `('when `weight` prop is "$weight"', ({ weight, value }) => {
- it(`value is "${value}"`, () => {
- createComponent({ weight });
-
- expect(findInput().element.value).toBe(value);
- });
- });
- });
-
- describe('when blurred', () => {
- it('calls a mutation to update the weight when the input value is different', () => {
- const mutationSpy = jest.fn().mockResolvedValue(updateWorkItemMutationResponse);
- createComponent({
- isEditing: true,
- weight: 0,
- mutationHandler: mutationSpy,
- canUpdate: true,
- });
-
- findInput().vm.$emit('blur', { target: { value: 1 } });
-
- expect(mutationSpy).toHaveBeenCalledWith({
- input: {
- id: workItemId,
- weightWidget: {
- weight: 1,
- },
- },
- });
- });
-
- it('does not call a mutation to update the weight when the input value is the same', () => {
- const mutationSpy = jest.fn().mockResolvedValue(updateWorkItemMutationResponse);
- createComponent({ isEditing: true, mutationHandler: mutationSpy, canUpdate: true });
-
- findInput().trigger('blur');
-
- expect(mutationSpy).not.toHaveBeenCalledWith();
- });
-
- it('emits an error when there is a GraphQL error', async () => {
- const response = {
- data: {
- workItemUpdate: {
- errors: ['Error!'],
- workItem: {},
- },
- },
- };
- createComponent({
- isEditing: true,
- mutationHandler: jest.fn().mockResolvedValue(response),
- canUpdate: true,
- });
-
- findInput().trigger('blur');
- await waitForPromises();
-
- expect(wrapper.emitted('error')).toEqual([[i18n.updateError]]);
- });
-
- it('emits an error when there is a network error', async () => {
- createComponent({
- isEditing: true,
- mutationHandler: jest.fn().mockRejectedValue(new Error()),
- canUpdate: true,
- });
-
- findInput().trigger('blur');
- await waitForPromises();
-
- expect(wrapper.emitted('error')).toEqual([[i18n.updateError]]);
- });
-
- it('tracks updating the weight', () => {
- const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- createComponent({ canUpdate: true });
-
- findInput().trigger('blur');
-
- expect(trackingSpy).toHaveBeenCalledWith(TRACKING_CATEGORY_SHOW, 'updated_weight', {
- category: TRACKING_CATEGORY_SHOW,
- label: 'item_weight',
- property: 'type_Task',
- });
- });
- });
- });
-});
diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js
index d24ac2a9f93..e1bc8d2f6b7 100644
--- a/spec/frontend/work_items/mock_data.js
+++ b/spec/frontend/work_items/mock_data.js
@@ -28,6 +28,11 @@ export const workItemQueryResponse = {
confidential: false,
createdAt: '2022-08-03T12:41:54Z',
closedAt: null,
+ project: {
+ __typename: 'Project',
+ id: '1',
+ fullPath: 'test-project-path',
+ },
workItemType: {
__typename: 'WorkItemType',
id: 'gid://gitlab/WorkItems::Type/5',
@@ -93,6 +98,11 @@ export const updateWorkItemMutationResponse = {
confidential: false,
createdAt: '2022-08-03T12:41:54Z',
closedAt: null,
+ project: {
+ __typename: 'Project',
+ id: '1',
+ fullPath: 'test-project-path',
+ },
workItemType: {
__typename: 'WorkItemType',
id: 'gid://gitlab/WorkItems::Type/5',
@@ -128,6 +138,16 @@ export const updateWorkItemMutationResponse = {
},
};
+export const updateWorkItemMutationErrorResponse = {
+ data: {
+ workItemUpdate: {
+ __typename: 'WorkItemUpdatePayload',
+ errors: ['Error!'],
+ workItem: {},
+ },
+ },
+};
+
export const mockParent = {
parent: {
id: 'gid://gitlab/Issue/1',
@@ -142,6 +162,7 @@ export const workItemResponseFactory = ({
canDelete = false,
allowsMultipleAssignees = true,
assigneesWidgetPresent = true,
+ datesWidgetPresent = true,
weightWidgetPresent = true,
confidential = false,
canInviteMembers = false,
@@ -157,6 +178,11 @@ export const workItemResponseFactory = ({
confidential,
createdAt: '2022-08-03T12:41:54Z',
closedAt: null,
+ project: {
+ __typename: 'Project',
+ id: '1',
+ fullPath: 'test-project-path',
+ },
workItemType: {
__typename: 'WorkItemType',
id: 'gid://gitlab/WorkItems::Type/5',
@@ -186,6 +212,14 @@ export const workItemResponseFactory = ({
},
}
: { type: 'MOCK TYPE' },
+ datesWidgetPresent
+ ? {
+ __typename: 'WorkItemWidgetStartAndDueDate',
+ type: 'START_AND_DUE_DATE',
+ dueDate: '2022-12-31',
+ startDate: '2022-01-01',
+ }
+ : { type: 'MOCK TYPE' },
weightWidgetPresent
? {
__typename: 'WorkItemWidgetWeight',
@@ -212,17 +246,6 @@ export const workItemResponseFactory = ({
},
});
-export const updateWorkItemWidgetsResponse = {
- data: {
- workItemUpdateWidgets: {
- workItem: {
- id: 1234,
- },
- errors: [],
- },
- },
-};
-
export const projectWorkItemTypesQueryResponse = {
data: {
workspace: {
@@ -251,6 +274,11 @@ export const createWorkItemMutationResponse = {
confidential: false,
createdAt: '2022-08-03T12:41:54Z',
closedAt: null,
+ project: {
+ __typename: 'Project',
+ id: '1',
+ fullPath: 'test-project-path',
+ },
workItemType: {
__typename: 'WorkItemType',
id: 'gid://gitlab/WorkItems::Type/5',
@@ -282,6 +310,11 @@ export const createWorkItemFromTaskMutationResponse = {
confidential: false,
createdAt: '2022-08-03T12:41:54Z',
closedAt: null,
+ project: {
+ __typename: 'Project',
+ id: '1',
+ fullPath: 'test-project-path',
+ },
workItemType: {
__typename: 'WorkItemType',
id: 'gid://gitlab/WorkItems::Type/5',
@@ -310,6 +343,11 @@ export const createWorkItemFromTaskMutationResponse = {
closedAt: null,
description: '',
confidential: false,
+ project: {
+ __typename: 'Project',
+ id: '1',
+ fullPath: 'test-project-path',
+ },
workItemType: {
__typename: 'WorkItemType',
id: 'gid://gitlab/WorkItems::Type/5',
@@ -368,6 +406,21 @@ export const deleteWorkItemFromTaskMutationErrorResponse = {
},
};
+export const workItemDatesSubscriptionResponse = {
+ data: {
+ issuableDatesUpdated: {
+ id: 'gid://gitlab/WorkItem/1',
+ widgets: [
+ {
+ __typename: 'WorkItemWidgetStartAndDueDate',
+ dueDate: '2022-12-31',
+ startDate: '2022-01-01',
+ },
+ ],
+ },
+ },
+};
+
export const workItemTitleSubscriptionResponse = {
data: {
issuableTitleUpdated: {
@@ -377,6 +430,20 @@ export const workItemTitleSubscriptionResponse = {
},
};
+export const workItemWeightSubscriptionResponse = {
+ data: {
+ issuableWeightUpdated: {
+ id: 'gid://gitlab/WorkItem/1',
+ widgets: [
+ {
+ __typename: 'WorkItemWidgetWeight',
+ weight: 1,
+ },
+ ],
+ },
+ },
+};
+
export const workItemHierarchyEmptyResponse = {
data: {
workItem: {
@@ -388,6 +455,11 @@ export const workItemHierarchyEmptyResponse = {
title: 'New title',
createdAt: '2022-08-03T12:41:54Z',
closedAt: null,
+ project: {
+ __typename: 'Project',
+ id: '1',
+ fullPath: 'test-project-path',
+ },
userPermissions: {
deleteWorkItem: false,
updateWorkItem: false,
@@ -426,6 +498,11 @@ export const workItemHierarchyNoUpdatePermissionResponse = {
deleteWorkItem: false,
updateWorkItem: false,
},
+ project: {
+ __typename: 'Project',
+ id: '1',
+ fullPath: 'test-project-path',
+ },
confidential: false,
widgets: [
{
@@ -461,6 +538,48 @@ export const workItemHierarchyNoUpdatePermissionResponse = {
},
};
+export const workItemTask = {
+ id: 'gid://gitlab/WorkItem/4',
+ workItemType: {
+ id: 'gid://gitlab/WorkItems::Type/5',
+ __typename: 'WorkItemType',
+ },
+ title: 'bar',
+ state: 'OPEN',
+ confidential: false,
+ createdAt: '2022-08-03T12:41:54Z',
+ closedAt: null,
+ __typename: 'WorkItem',
+};
+
+export const confidentialWorkItemTask = {
+ id: 'gid://gitlab/WorkItem/2',
+ workItemType: {
+ id: 'gid://gitlab/WorkItems::Type/5',
+ __typename: 'WorkItemType',
+ },
+ title: 'xyz',
+ state: 'OPEN',
+ confidential: true,
+ createdAt: '2022-08-03T12:41:54Z',
+ closedAt: null,
+ __typename: 'WorkItem',
+};
+
+export const closedWorkItemTask = {
+ id: 'gid://gitlab/WorkItem/3',
+ workItemType: {
+ id: 'gid://gitlab/WorkItems::Type/5',
+ __typename: 'WorkItemType',
+ },
+ title: 'abc',
+ state: 'CLOSED',
+ confidential: false,
+ createdAt: '2022-08-03T12:41:54Z',
+ closedAt: '2022-08-12T13:07:52Z',
+ __typename: 'WorkItem',
+};
+
export const workItemHierarchyResponse = {
data: {
workItem: {
@@ -475,6 +594,11 @@ export const workItemHierarchyResponse = {
updateWorkItem: true,
},
confidential: false,
+ project: {
+ __typename: 'Project',
+ id: '1',
+ fullPath: 'test-project-path',
+ },
widgets: [
{
type: 'DESCRIPTION',
@@ -485,45 +609,9 @@ export const workItemHierarchyResponse = {
parent: null,
children: {
nodes: [
- {
- id: 'gid://gitlab/WorkItem/2',
- workItemType: {
- id: 'gid://gitlab/WorkItems::Type/5',
- __typename: 'WorkItemType',
- },
- title: 'xyz',
- state: 'OPEN',
- confidential: true,
- createdAt: '2022-08-03T12:41:54Z',
- closedAt: null,
- __typename: 'WorkItem',
- },
- {
- id: 'gid://gitlab/WorkItem/3',
- workItemType: {
- id: 'gid://gitlab/WorkItems::Type/5',
- __typename: 'WorkItemType',
- },
- title: 'abc',
- state: 'CLOSED',
- confidential: false,
- createdAt: '2022-08-03T12:41:54Z',
- closedAt: '2022-08-12T13:07:52Z',
- __typename: 'WorkItem',
- },
- {
- id: 'gid://gitlab/WorkItem/4',
- workItemType: {
- id: 'gid://gitlab/WorkItems::Type/5',
- __typename: 'WorkItemType',
- },
- title: 'bar',
- state: 'OPEN',
- confidential: false,
- createdAt: '2022-08-03T12:41:54Z',
- closedAt: null,
- __typename: 'WorkItem',
- },
+ confidentialWorkItemTask,
+ closedWorkItemTask,
+ workItemTask,
{
id: 'gid://gitlab/WorkItem/5',
workItemType: {
@@ -570,6 +658,11 @@ export const changeWorkItemParentMutationResponse = {
confidential: false,
createdAt: '2022-08-03T12:41:54Z',
closedAt: null,
+ project: {
+ __typename: 'Project',
+ id: '1',
+ fullPath: 'test-project-path',
+ },
widgets: [
{
__typename: 'WorkItemWidgetHierarchy',
@@ -649,6 +742,71 @@ export const projectMembersResponseWithCurrentUser = {
},
},
],
+ pageInfo: {
+ hasNextPage: false,
+ endCursor: null,
+ startCursor: null,
+ },
+ },
+ },
+ },
+};
+
+export const projectMembersResponseWithCurrentUserWithNextPage = {
+ data: {
+ workspace: {
+ id: '1',
+ __typename: 'Project',
+ users: {
+ nodes: [
+ {
+ id: 'user-2',
+ user: {
+ __typename: 'UserCore',
+ id: 'gid://gitlab/User/5',
+ avatarUrl: '/avatar2',
+ name: 'rookie',
+ username: 'rookie',
+ webUrl: 'rookie',
+ status: null,
+ },
+ },
+ {
+ id: 'user-1',
+ user: {
+ __typename: 'UserCore',
+ id: 'gid://gitlab/User/1',
+ avatarUrl:
+ 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ name: 'Administrator',
+ username: 'root',
+ webUrl: '/root',
+ status: null,
+ },
+ },
+ ],
+ pageInfo: {
+ hasNextPage: true,
+ endCursor: 'endCursor',
+ startCursor: 'startCursor',
+ },
+ },
+ },
+ },
+};
+
+export const projectMembersResponseWithNoMatchingUsers = {
+ data: {
+ workspace: {
+ id: '1',
+ __typename: 'Project',
+ users: {
+ nodes: [],
+ pageInfo: {
+ endCursor: null,
+ hasNextPage: false,
+ startCursor: null,
+ },
},
},
},
diff --git a/spec/frontend/work_items/pages/create_work_item_spec.js b/spec/frontend/work_items/pages/create_work_item_spec.js
index fed8be3783a..15dac25b7d9 100644
--- a/spec/frontend/work_items/pages/create_work_item_spec.js
+++ b/spec/frontend/work_items/pages/create_work_item_spec.js
@@ -193,6 +193,8 @@ describe('Create work item component', () => {
wrapper.find('form').trigger('submit');
await waitForPromises();
- expect(findAlert().text()).toBe(CreateWorkItem.createErrorText);
+ expect(findAlert().text()).toBe(
+ 'Something went wrong when creating work item. Please try again.',
+ );
});
});
diff --git a/spec/frontend/work_items/pages/work_item_detail_spec.js b/spec/frontend/work_items/pages/work_item_detail_spec.js
deleted file mode 100644
index 823981df880..00000000000
--- a/spec/frontend/work_items/pages/work_item_detail_spec.js
+++ /dev/null
@@ -1,484 +0,0 @@
-import { GlAlert, GlBadge, GlLoadingIcon, GlSkeletonLoader, GlButton } 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 LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
-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';
-import WorkItemState from '~/work_items/components/work_item_state.vue';
-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 WorkItemWeight from '~/work_items/components/work_item_weight.vue';
-import WorkItemInformation from '~/work_items/components/work_item_information.vue';
-import { i18n } from '~/work_items/constants';
-import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
-import workItemTitleSubscription from '~/work_items/graphql/work_item_title.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 { temporaryConfig } from '~/work_items/graphql/provider';
-import { useLocalStorageSpy } from 'helpers/local_storage_helper';
-import {
- workItemTitleSubscriptionResponse,
- workItemResponseFactory,
- mockParent,
-} from '../mock_data';
-
-describe('WorkItemDetail component', () => {
- let wrapper;
- useLocalStorageSpy();
-
- Vue.use(VueApollo);
-
- const workItemQueryResponse = workItemResponseFactory({ canUpdate: true, canDelete: true });
- const workItemQueryResponseWithoutParent = workItemResponseFactory({
- parent: null,
- canUpdate: true,
- canDelete: true,
- });
- const successHandler = jest.fn().mockResolvedValue(workItemQueryResponse);
- const initialSubscriptionHandler = jest.fn().mockResolvedValue(workItemTitleSubscriptionResponse);
-
- const findAlert = () => wrapper.findComponent(GlAlert);
- const findSkeleton = () => wrapper.findComponent(GlSkeletonLoader);
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findWorkItemActions = () => wrapper.findComponent(WorkItemActions);
- const findWorkItemTitle = () => wrapper.findComponent(WorkItemTitle);
- const findWorkItemState = () => wrapper.findComponent(WorkItemState);
- const findWorkItemDescription = () => wrapper.findComponent(WorkItemDescription);
- const findWorkItemAssignees = () => wrapper.findComponent(WorkItemAssignees);
- const findWorkItemLabels = () => wrapper.findComponent(WorkItemLabels);
- const findWorkItemWeight = () => wrapper.findComponent(WorkItemWeight);
- const findParent = () => wrapper.find('[data-testid="work-item-parent"]');
- 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 createComponent = ({
- isModal = false,
- updateInProgress = false,
- workItemId = workItemQueryResponse.data.workItem.id,
- handler = successHandler,
- subscriptionHandler = initialSubscriptionHandler,
- confidentialityMock = [updateWorkItemMutation, jest.fn()],
- workItemsMvc2Enabled = false,
- includeWidgets = false,
- error = undefined,
- } = {}) => {
- wrapper = shallowMount(WorkItemDetail, {
- apolloProvider: createMockApollo(
- [
- [workItemQuery, handler],
- [workItemTitleSubscription, subscriptionHandler],
- confidentialityMock,
- ],
- {},
- {
- typePolicies: includeWidgets ? temporaryConfig.cacheConfig.typePolicies : {},
- },
- ),
- propsData: { isModal, workItemId },
- data() {
- return {
- updateInProgress,
- error,
- };
- },
- provide: {
- glFeatures: {
- workItemsMvc2: workItemsMvc2Enabled,
- },
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('when there is no `workItemId` prop', () => {
- beforeEach(() => {
- createComponent({ workItemId: null });
- });
-
- it('skips the work item query', () => {
- expect(successHandler).not.toHaveBeenCalled();
- });
- });
-
- describe('when loading', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders skeleton loader', () => {
- expect(findSkeleton().exists()).toBe(true);
- expect(findWorkItemState().exists()).toBe(false);
- expect(findWorkItemTitle().exists()).toBe(false);
- });
- });
-
- describe('when loaded', () => {
- beforeEach(() => {
- createComponent();
- return waitForPromises();
- });
-
- it('does not render skeleton', () => {
- expect(findSkeleton().exists()).toBe(false);
- expect(findWorkItemState().exists()).toBe(true);
- expect(findWorkItemTitle().exists()).toBe(true);
- });
- });
-
- describe('close button', () => {
- describe('when isModal prop is false', () => {
- it('does not render', async () => {
- createComponent({ isModal: false });
- await waitForPromises();
-
- expect(findCloseButton().exists()).toBe(false);
- });
- });
-
- describe('when isModal prop is true', () => {
- it('renders', async () => {
- createComponent({ isModal: true });
- await waitForPromises();
-
- expect(findCloseButton().props('icon')).toBe('close');
- expect(findCloseButton().attributes('aria-label')).toBe('Close');
- });
-
- it('emits `close` event when clicked', async () => {
- createComponent({ isModal: true });
- await waitForPromises();
-
- findCloseButton().vm.$emit('click');
-
- expect(wrapper.emitted('close')).toEqual([[]]);
- });
- });
- });
-
- describe('confidentiality', () => {
- const errorMessage = 'Mutation failed';
- const confidentialWorkItem = workItemResponseFactory({
- confidential: true,
- });
-
- // Mocks for work item without parent
- const withoutParentExpectedInputVars = {
- id: workItemQueryResponse.data.workItem.id,
- confidential: true,
- };
- const toggleConfidentialityWithoutParentHandler = jest.fn().mockResolvedValue({
- data: {
- workItemUpdate: {
- workItem: confidentialWorkItem.data.workItem,
- errors: [],
- },
- },
- });
- const withoutParentHandlerMock = jest
- .fn()
- .mockResolvedValue(workItemQueryResponseWithoutParent);
- const confidentialityWithoutParentMock = [
- updateWorkItemMutation,
- toggleConfidentialityWithoutParentHandler,
- ];
- const confidentialityWithoutParentFailureMock = [
- updateWorkItemMutation,
- jest.fn().mockRejectedValue(new Error(errorMessage)),
- ];
-
- // Mocks for work item with parent
- const withParentExpectedInputVars = {
- id: mockParent.parent.id,
- taskData: { id: workItemQueryResponse.data.workItem.id, confidential: true },
- };
- const toggleConfidentialityWithParentHandler = jest.fn().mockResolvedValue({
- data: {
- workItemUpdate: {
- workItem: {
- id: confidentialWorkItem.data.workItem.id,
- descriptionHtml: confidentialWorkItem.data.workItem.description,
- },
- task: {
- workItem: confidentialWorkItem.data.workItem,
- confidential: true,
- },
- errors: [],
- },
- },
- });
- const confidentialityWithParentMock = [
- updateWorkItemTaskMutation,
- toggleConfidentialityWithParentHandler,
- ];
- const confidentialityWithParentFailureMock = [
- updateWorkItemTaskMutation,
- jest.fn().mockRejectedValue(new Error(errorMessage)),
- ];
-
- describe.each`
- context | handlerMock | confidentialityMock | confidentialityFailureMock | inputVariables
- ${'no parent'} | ${withoutParentHandlerMock} | ${confidentialityWithoutParentMock} | ${confidentialityWithoutParentFailureMock} | ${withoutParentExpectedInputVars}
- ${'parent'} | ${successHandler} | ${confidentialityWithParentMock} | ${confidentialityWithParentFailureMock} | ${withParentExpectedInputVars}
- `(
- 'when work item has $context',
- ({ handlerMock, confidentialityMock, confidentialityFailureMock, inputVariables }) => {
- it('renders confidential badge when work item is confidential', async () => {
- createComponent({
- handler: jest.fn().mockResolvedValue(confidentialWorkItem),
- confidentialityMock,
- });
-
- await waitForPromises();
-
- const confidentialBadge = wrapper.findComponent(GlBadge);
- expect(confidentialBadge.exists()).toBe(true);
- expect(confidentialBadge.props()).toMatchObject({
- variant: 'warning',
- icon: 'eye-slash',
- });
- expect(confidentialBadge.attributes('title')).toBe(
- 'Only project members with at least the Reporter role, the author, and assignees can view or be notified about this task.',
- );
- expect(confidentialBadge.text()).toBe('Confidential');
- });
-
- it('renders gl-loading-icon while update mutation is in progress', async () => {
- createComponent({
- handler: handlerMock,
- confidentialityMock,
- });
-
- await waitForPromises();
-
- findWorkItemActions().vm.$emit('toggleWorkItemConfidentiality', true);
-
- await nextTick();
-
- expect(findLoadingIcon().exists()).toBe(true);
- });
-
- it('emits workItemUpdated and shows confidentiality badge when mutation is successful', async () => {
- createComponent({
- handler: handlerMock,
- confidentialityMock,
- });
-
- await waitForPromises();
-
- findWorkItemActions().vm.$emit('toggleWorkItemConfidentiality', true);
- await waitForPromises();
-
- expect(wrapper.emitted('workItemUpdated')).toEqual([[{ confidential: true }]]);
- expect(confidentialityMock[1]).toHaveBeenCalledWith({
- input: inputVariables,
- });
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- it('shows alert message when mutation fails', async () => {
- createComponent({
- handler: handlerMock,
- confidentialityMock: confidentialityFailureMock,
- });
-
- await waitForPromises();
- findWorkItemActions().vm.$emit('toggleWorkItemConfidentiality', true);
- await waitForPromises();
-
- expect(wrapper.emitted('workItemUpdated')).toBeFalsy();
-
- await nextTick();
-
- expect(findAlert().exists()).toBe(true);
- expect(findAlert().text()).toBe(errorMessage);
- expect(findLoadingIcon().exists()).toBe(false);
- });
- },
- );
- });
-
- describe('description', () => {
- it('does not show description widget if loading description fails', () => {
- createComponent();
-
- expect(findWorkItemDescription().exists()).toBe(false);
- });
-
- it('shows description widget if description loads', async () => {
- createComponent();
- await waitForPromises();
-
- expect(findWorkItemDescription().exists()).toBe(true);
- });
- });
-
- describe('secondary breadcrumbs', () => {
- it('does not show secondary breadcrumbs by default', () => {
- createComponent();
-
- expect(findParent().exists()).toBe(false);
- });
-
- it('does not show secondary breadcrumbs if there is not a parent', async () => {
- createComponent({ handler: jest.fn().mockResolvedValue(workItemQueryResponseWithoutParent) });
-
- await waitForPromises();
-
- expect(findParent().exists()).toBe(false);
- });
-
- it('shows work item type if there is not a parent', async () => {
- createComponent({ handler: jest.fn().mockResolvedValue(workItemQueryResponseWithoutParent) });
-
- await waitForPromises();
- expect(findWorkItemType().exists()).toBe(true);
- });
-
- describe('with parent', () => {
- beforeEach(() => {
- const parentResponse = workItemResponseFactory(mockParent);
- createComponent({ handler: jest.fn().mockResolvedValue(parentResponse) });
-
- return waitForPromises();
- });
-
- it('shows secondary breadcrumbs if there is a parent', () => {
- expect(findParent().exists()).toBe(true);
- });
-
- it('does not show work item type', async () => {
- expect(findWorkItemType().exists()).toBe(false);
- });
-
- it('sets the parent breadcrumb URL', () => {
- expect(findParentButton().attributes().href).toBe('../../issues/5');
- });
- });
- });
-
- it('shows an error message when the work item query was unsuccessful', async () => {
- const errorHandler = jest.fn().mockRejectedValue('Oops');
- createComponent({ handler: errorHandler });
- await waitForPromises();
-
- expect(errorHandler).toHaveBeenCalled();
- expect(findAlert().text()).toBe(i18n.fetchError);
- });
-
- it('shows an error message when WorkItemTitle emits an `error` event', async () => {
- createComponent();
- await waitForPromises();
-
- findWorkItemTitle().vm.$emit('error', i18n.updateError);
- await waitForPromises();
-
- expect(findAlert().text()).toBe(i18n.updateError);
- });
-
- it('calls the subscription', () => {
- createComponent();
-
- expect(initialSubscriptionHandler).toHaveBeenCalledWith({
- issuableId: workItemQueryResponse.data.workItem.id,
- });
- });
-
- describe('when work_items_mvc_2 feature flag is enabled', () => {
- it('renders assignees component when assignees widget is returned from the API', async () => {
- createComponent({
- workItemsMvc2Enabled: true,
- });
- await waitForPromises();
-
- expect(findWorkItemAssignees().exists()).toBe(true);
- });
-
- it('does not render assignees component when assignees widget is not returned from the API', async () => {
- createComponent({
- workItemsMvc2Enabled: true,
- handler: jest
- .fn()
- .mockResolvedValue(workItemResponseFactory({ assigneesWidgetPresent: false })),
- });
- await waitForPromises();
-
- expect(findWorkItemAssignees().exists()).toBe(false);
- });
- });
-
- it('does not render assignees component when assignees feature flag is disabled', async () => {
- createComponent();
- await waitForPromises();
-
- expect(findWorkItemAssignees().exists()).toBe(false);
- });
-
- describe('labels widget', () => {
- it.each`
- description | includeWidgets | exists
- ${'renders when widget is returned from API'} | ${true} | ${true}
- ${'does not render when widget is not returned from API'} | ${false} | ${false}
- `('$description', async ({ includeWidgets, exists }) => {
- createComponent({ includeWidgets, workItemsMvc2Enabled: true });
- await waitForPromises();
-
- expect(findWorkItemLabels().exists()).toBe(exists);
- });
- });
-
- describe('weight widget', () => {
- describe.each`
- description | weightWidgetPresent | exists
- ${'when widget is returned from API'} | ${true} | ${true}
- ${'when widget is not returned from API'} | ${false} | ${false}
- `('$description', ({ weightWidgetPresent, exists }) => {
- it(`${weightWidgetPresent ? 'renders' : 'does not render'} weight component`, async () => {
- const response = workItemResponseFactory({ weightWidgetPresent });
- const handler = jest.fn().mockResolvedValue(response);
- createComponent({ handler });
- await waitForPromises();
-
- expect(findWorkItemWeight().exists()).toBe(exists);
- });
- });
-
- it('shows an error message when it emits an `error` event', async () => {
- createComponent({ workItemsMvc2Enabled: true });
- await waitForPromises();
-
- findWorkItemWeight().vm.$emit('error', i18n.updateError);
- await waitForPromises();
-
- expect(findAlert().text()).toBe(i18n.updateError);
- });
- });
-
- 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);
- });
- });
-});
diff --git a/spec/frontend/work_items/router_spec.js b/spec/frontend/work_items/router_spec.js
index 99dcd886f7b..ab370e2ca8b 100644
--- a/spec/frontend/work_items/router_spec.js
+++ b/spec/frontend/work_items/router_spec.js
@@ -1,5 +1,18 @@
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 {
+ workItemDatesSubscriptionResponse,
+ workItemResponseFactory,
+ workItemTitleSubscriptionResponse,
+ workItemWeightSubscriptionResponse,
+} from 'jest/work_items/mock_data';
import App from '~/work_items/components/app.vue';
+import workItemQuery from '~/work_items/graphql/work_item.query.graphql';
+import workItemDatesSubscription from '~/work_items/graphql/work_item_dates.subscription.graphql';
+import workItemTitleSubscription from '~/work_items/graphql/work_item_title.subscription.graphql';
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';
@@ -7,26 +20,36 @@ import { createRouter } from '~/work_items/router';
describe('Work items router', () => {
let wrapper;
+ Vue.use(VueApollo);
+
+ 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 createComponent = async (routeArg) => {
const router = createRouter('/work_item');
if (routeArg !== undefined) {
await router.push(routeArg);
}
+ const handlers = [
+ [workItemQuery, workItemQueryHandler],
+ [workItemDatesSubscription, datesSubscriptionHandler],
+ [workItemTitleSubscription, titleSubscriptionHandler],
+ ];
+
+ if (IS_EE) {
+ handlers.push([workItemWeightSubscription, weightSubscriptionHandler]);
+ }
+
wrapper = mount(App, {
+ apolloProvider: createMockApollo(handlers),
router,
provide: {
fullPath: 'full-path',
issuesListPath: 'full-path/-/issues',
},
- mocks: {
- $apollo: {
- queries: {
- workItem: {},
- workItemTypes: {},
- },
- },
- },
});
};
diff --git a/spec/frontend/work_items_hierarchy/components/hierarchy_spec.js b/spec/frontend/work_items_hierarchy/components/hierarchy_spec.js
index 67420e7fc2a..dca016dc317 100644
--- a/spec/frontend/work_items_hierarchy/components/hierarchy_spec.js
+++ b/spec/frontend/work_items_hierarchy/components/hierarchy_spec.js
@@ -74,7 +74,7 @@ describe('WorkItemsHierarchy Hierarchy', () => {
});
it('renders license badges for all work items', () => {
- expect(wrapper.findAll(GlBadge)).toHaveLength(items.length);
+ expect(wrapper.findAllComponents(GlBadge)).toHaveLength(items.length);
});
it('does not render svg icon for linking', () => {
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 12cd6dcad83..c0c6b5e5dc8 100644
--- a/spec/frontend_integration/content_editor/content_editor_integration_spec.js
+++ b/spec/frontend_integration/content_editor/content_editor_integration_spec.js
@@ -1,6 +1,7 @@
import { nextTick } from 'vue';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { ContentEditor } from '~/content_editor';
+import waitForPromises from 'helpers/wait_for_promises';
/**
* This spec exercises some workflows in the Content Editor without mocking
@@ -10,32 +11,38 @@ import { ContentEditor } from '~/content_editor';
describe('content_editor', () => {
let wrapper;
let renderMarkdown;
- let contentEditorService;
- const buildWrapper = () => {
- renderMarkdown = jest.fn();
+ const buildWrapper = ({ markdown = '' } = {}) => {
wrapper = mountExtended(ContentEditor, {
propsData: {
renderMarkdown,
uploadsPath: '/',
- },
- listeners: {
- initialized(contentEditor) {
- contentEditorService = contentEditor;
- },
+ markdown,
},
});
};
+ const waitUntilContentIsLoaded = async () => {
+ await waitForPromises();
+ await nextTick();
+ };
+
+ const mockRenderMarkdownResponse = (response) => {
+ renderMarkdown.mockImplementation((markdown) => (markdown ? response : null));
+ };
+
+ beforeEach(() => {
+ renderMarkdown = jest.fn();
+ });
+
describe('when loading initial content', () => {
describe('when the initial content is empty', () => {
it('still hides the loading indicator', async () => {
- buildWrapper();
+ mockRenderMarkdownResponse('');
- renderMarkdown.mockResolvedValue('');
+ buildWrapper();
- await contentEditorService.setSerializedContent('');
- await nextTick();
+ await waitUntilContentIsLoaded();
expect(wrapper.findByTestId('content-editor-loading-indicator').exists()).toBe(false);
});
@@ -44,14 +51,15 @@ describe('content_editor', () => {
describe('when the initial content is not empty', () => {
const initialContent = '<p><strong>bold text</strong></p>';
beforeEach(async () => {
- buildWrapper();
+ mockRenderMarkdownResponse(initialContent);
- renderMarkdown.mockResolvedValue(initialContent);
+ buildWrapper({
+ markdown: '**bold text**',
+ });
- await contentEditorService.setSerializedContent('**bold text**');
- await nextTick();
+ await waitUntilContentIsLoaded();
});
- it('hides the loading indicator', async () => {
+ it('hides the loading indicator', () => {
expect(wrapper.findByTestId('content-editor-loading-indicator').exists()).toBe(false);
});
@@ -70,27 +78,29 @@ describe('content_editor', () => {
});
it('processes and renders footnote ids alongside the footnote definition', async () => {
- buildWrapper();
-
- await contentEditorService.setSerializedContent(`
+ buildWrapper({
+ markdown: `
This reference tag is a mix of letters and numbers [^footnote].
[^footnote]: This is another footnote.
- `);
- await nextTick();
+ `,
+ });
+
+ await waitUntilContentIsLoaded();
expect(wrapper.text()).toContain('footnote: This is another footnote');
});
it('processes and displays reference definitions', async () => {
- buildWrapper();
-
- await contentEditorService.setSerializedContent(`
+ buildWrapper({
+ markdown: `
[GitLab][gitlab]
[gitlab]: https://gitlab.com
- `);
- await nextTick();
+ `,
+ });
+
+ await waitUntilContentIsLoaded();
expect(wrapper.find('pre').text()).toContain('[gitlab]: https://gitlab.com');
});
@@ -99,9 +109,7 @@ This reference tag is a mix of letters and numbers [^footnote].
it('renders table of contents', async () => {
jest.useFakeTimers();
- buildWrapper();
-
- renderMarkdown.mockResolvedValue(`
+ renderMarkdown.mockResolvedValueOnce(`
<ul class="section-nav">
</ul>
<h1 dir="auto" data-sourcepos="3:1-3:11">
@@ -112,18 +120,53 @@ This reference tag is a mix of letters and numbers [^footnote].
</h2>
`);
- await contentEditorService.setSerializedContent(`
+ buildWrapper({
+ markdown: `
[TOC]
# Heading 1
## Heading 2
- `);
+ `,
+ });
- await nextTick();
- jest.runAllTimers();
+ await waitUntilContentIsLoaded();
expect(wrapper.findByTestId('table-of-contents').text()).toContain('Heading 1');
expect(wrapper.findByTestId('table-of-contents').text()).toContain('Heading 2');
});
+
+ describe('when pasting content', () => {
+ const buildClipboardData = (data = {}) => ({
+ clipboardData: {
+ getData(mimeType) {
+ return data[mimeType];
+ },
+ types: Object.keys(data),
+ },
+ });
+
+ describe('when the clipboard does not contain text/html data', () => {
+ it('processes the clipboard content as markdown', async () => {
+ const processedMarkdown = '<strong>bold text</strong>';
+
+ buildWrapper();
+
+ await waitUntilContentIsLoaded();
+
+ mockRenderMarkdownResponse(processedMarkdown);
+
+ wrapper.find('[contenteditable]').trigger(
+ 'paste',
+ buildClipboardData({
+ 'text/plain': '**bold text**',
+ }),
+ );
+
+ await waitUntilContentIsLoaded();
+
+ expect(wrapper.find('[contenteditable]').html()).toContain(processedMarkdown);
+ });
+ });
+ });
});
diff --git a/spec/frontend_integration/ide/helpers/start.js b/spec/frontend_integration/ide/helpers/start.js
index 925db12f36e..40e6a725358 100644
--- a/spec/frontend_integration/ide/helpers/start.js
+++ b/spec/frontend_integration/ide/helpers/start.js
@@ -1,7 +1,7 @@
import { editor as monacoEditor } from 'monaco-editor';
import setWindowLocation from 'helpers/set_window_location_helper';
import { TEST_HOST } from 'helpers/test_constants';
-import { initIde } from '~/ide';
+import { initLegacyWebIDE } from '~/ide';
import extendStore from '~/ide/stores/extend';
import { getProject, getEmptyProject } from 'jest/../frontend_integration/test_helpers/fixtures';
import { IDE_DATASET } from './mock_data';
@@ -16,7 +16,7 @@ export default (container, { isRepoEmpty = false, path = '', mrId = '' } = {}) =
const el = document.createElement('div');
Object.assign(el.dataset, IDE_DATASET, { project: JSON.stringify(project) });
container.appendChild(el);
- const vm = initIde(el, { extendStore });
+ const vm = initLegacyWebIDE(el, { extendStore });
// We need to dispose of editor Singleton things or tests will bump into eachother
vm.$on('destroy', () => monacoEditor.getModels().forEach((model) => model.dispose()));
diff --git a/spec/graphql/features/feature_flag_spec.rb b/spec/graphql/features/feature_flag_spec.rb
deleted file mode 100644
index b06718eb16a..00000000000
--- a/spec/graphql/features/feature_flag_spec.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe 'Graphql Field feature flags' do
- include GraphqlHelpers
- include Graphql::ResolverFactories
-
- let_it_be(:user) { create(:user) }
-
- let(:feature_flag) { 'test_feature' }
- let(:test_object) { double(name: 'My name') }
- let(:query_string) { '{ item { name } }' }
- let(:result) { execute_query(query_type)['data'] }
-
- before do
- skip_feature_flags_yaml_validation
- end
-
- subject { result }
-
- describe 'Feature flagged field' do
- let(:type) { type_factory }
-
- let(:query_type) do
- query_factory do |query|
- query.field :item, type, null: true, _deprecated_feature_flag: feature_flag, resolver: new_resolver(test_object)
- end
- end
-
- it 'checks YAML definition for default_enabled' do
- # Exception is indicative of a check for YAML definition
- expect { subject }.to raise_error(Feature::InvalidFeatureFlagError, /The feature flag YAML definition for '#{feature_flag}' does not exist/)
- end
-
- context 'skipping YAML check' do
- before do
- skip_default_enabled_yaml_check
- end
-
- it 'returns the value when feature is enabled' do
- expect(subject['item']).to eq('name' => test_object.name)
- end
-
- it 'returns nil when the feature is disabled' do
- stub_feature_flags(feature_flag => false)
-
- expect(subject).to be_nil
- end
- end
- end
-end
diff --git a/spec/graphql/mutations/boards/issues/issue_move_list_spec.rb b/spec/graphql/mutations/boards/issues/issue_move_list_spec.rb
index 10aed8a1f00..8e9a567f614 100644
--- a/spec/graphql/mutations/boards/issues/issue_move_list_spec.rb
+++ b/spec/graphql/mutations/boards/issues/issue_move_list_spec.rb
@@ -55,7 +55,7 @@ RSpec.describe Mutations::Boards::Issues::IssueMoveList do
let(:move_params) { {} }
it 'generates an error' do
- expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, 'At least one of the arguments fromListId, toListId, afterId or beforeId is required') do
+ expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, 'At least one of the arguments fromListId, toListId, positionInList, moveAfterId, or moveBeforeId is required') do
subject
end
end
@@ -71,6 +71,50 @@ RSpec.describe Mutations::Boards::Issues::IssueMoveList do
end
end
+ context 'when positionInList is given' do
+ let(:move_params) { { from_list_id: list1.id, to_list_id: list2.id, position_in_list: 0 } }
+
+ context 'when fromListId and toListId are missing' do
+ let(:move_params) { { position_in_list: 0 } }
+
+ it 'generates an error' do
+ expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, 'Both fromListId and toListId are required when positionInList is given') do
+ subject
+ end
+ end
+ end
+
+ context 'when move_before_id is also given' do
+ let(:move_params) { { from_list_id: list1.id, to_list_id: list2.id, position_in_list: 0, move_before_id: 1 } }
+
+ it 'generates an error' do
+ expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, 'positionInList is mutually exclusive with any of moveBeforeId or moveAfterId') do
+ subject
+ end
+ end
+ end
+
+ context 'when move_after_id is also given' do
+ let(:move_params) { { from_list_id: list1.id, to_list_id: list2.id, position_in_list: 0, move_after_id: 1 } }
+
+ it 'generates an error' do
+ expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, 'positionInList is mutually exclusive with any of moveBeforeId or moveAfterId') do
+ subject
+ end
+ end
+ end
+
+ context 'when position_in_list is invalid' do
+ let(:move_params) { { from_list_id: list1.id, to_list_id: list2.id, position_in_list: -5 } }
+
+ it 'generates an error' do
+ expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, "positionInList must be >= 0 or #{Boards::Issues::MoveService::LIST_END_POSITION}") do
+ subject
+ end
+ end
+ end
+ end
+
context 'when user have access to resources' do
it 'moves and repositions issue' do
subject
diff --git a/spec/graphql/mutations/ci/runner/update_spec.rb b/spec/graphql/mutations/ci/runner/update_spec.rb
index b8efd4213fa..39fe2a53a68 100644
--- a/spec/graphql/mutations/ci/runner/update_spec.rb
+++ b/spec/graphql/mutations/ci/runner/update_spec.rb
@@ -6,10 +6,13 @@ RSpec.describe Mutations::Ci::Runner::Update do
include GraphqlHelpers
let_it_be(:user) { create(:user) }
- let_it_be(:runner) { create(:ci_runner, active: true, locked: false, run_untagged: true) }
+ let_it_be(:project1) { create(:project) }
+ let_it_be(:runner) do
+ create(:ci_runner, :project, projects: [project1], active: true, locked: false, run_untagged: true)
+ end
let(:current_ctx) { { current_user: user } }
- let(:mutated_runner) { subject[:runner] }
+ let(:mutated_runner) { response[:runner] }
let(:mutation_params) do
{
@@ -21,14 +24,14 @@ RSpec.describe Mutations::Ci::Runner::Update do
specify { expect(described_class).to require_graphql_authorizations(:update_runner) }
describe '#resolve' do
- subject do
+ subject(:response) do
sync(resolve(described_class, args: mutation_params, ctx: current_ctx))
end
context 'when the user cannot admin the runner' do
it 'generates an error' do
expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ResourceNotAvailable) do
- subject
+ response
end
end
end
@@ -37,7 +40,7 @@ RSpec.describe Mutations::Ci::Runner::Update do
let(:mutation_params) { {} }
it 'raises an error' do
- expect { subject }.to raise_error(ArgumentError, "Arguments must be provided: id")
+ expect { response }.to raise_error(ArgumentError, "Arguments must be provided: id")
end
end
@@ -45,41 +48,150 @@ RSpec.describe Mutations::Ci::Runner::Update do
let(:admin_user) { create(:user, :admin) }
let(:current_ctx) { { current_user: admin_user } }
- let(:mutation_params) do
- {
- id: runner.to_global_id,
- description: 'updated description',
- maintenance_note: 'updated maintenance note',
- maximum_timeout: 900,
- access_level: 'ref_protected',
- active: false,
- locked: true,
- run_untagged: false,
- tag_list: %w(tag1 tag2)
- }
- end
-
context 'with valid arguments' do
+ let(:mutation_params) do
+ {
+ id: runner.to_global_id,
+ description: 'updated description',
+ maintenance_note: 'updated maintenance note',
+ maximum_timeout: 900,
+ access_level: 'ref_protected',
+ active: false,
+ locked: true,
+ run_untagged: false,
+ tag_list: %w(tag1 tag2)
+ }
+ end
+
it 'updates runner with correct values' do
expected_attributes = mutation_params.except(:id, :tag_list)
- subject
+ response
- expect(subject[:errors]).to be_empty
- expect(subject[:runner]).to be_an_instance_of(Ci::Runner)
- expect(subject[:runner]).to have_attributes(expected_attributes)
- expect(subject[:runner].tag_list).to contain_exactly(*mutation_params[:tag_list])
+ expect(response[:errors]).to be_empty
+ expect(response[:runner]).to be_an_instance_of(Ci::Runner)
+ expect(response[:runner]).to have_attributes(expected_attributes)
+ expect(response[:runner].tag_list).to contain_exactly(*mutation_params[:tag_list])
expect(runner.reload).to have_attributes(expected_attributes)
expect(runner.tag_list).to contain_exactly(*mutation_params[:tag_list])
end
end
+ context 'with associatedProjects argument' do
+ let_it_be(:project2) { create(:project) }
+
+ context 'with id set to project runner' do
+ let(:mutation_params) do
+ {
+ id: runner.to_global_id,
+ description: 'updated description',
+ associated_projects: [project2.to_global_id.to_s]
+ }
+ end
+
+ it 'updates runner attributes and project relationships', :aggregate_failures do
+ expect_next_instance_of(
+ ::Ci::Runners::SetRunnerAssociatedProjectsService,
+ {
+ runner: runner,
+ current_user: admin_user,
+ project_ids: [project2.id]
+ }
+ ) do |service|
+ expect(service).to receive(:execute).and_call_original
+ end
+
+ expected_attributes = mutation_params.except(:id, :associated_projects)
+
+ response
+
+ expect(response[:errors]).to be_empty
+ expect(response[:runner]).to be_an_instance_of(Ci::Runner)
+ expect(response[:runner]).to have_attributes(expected_attributes)
+ expect(runner.reload).to have_attributes(expected_attributes)
+ expect(runner.projects).to match_array([project1, project2])
+ end
+
+ context 'with user not allowed to assign runner' do
+ before do
+ allow(admin_user).to receive(:can?).with(:assign_runner, runner).and_return(false)
+ end
+
+ it 'does not update runner', :aggregate_failures do
+ expect_next_instance_of(
+ ::Ci::Runners::SetRunnerAssociatedProjectsService,
+ {
+ runner: runner,
+ current_user: admin_user,
+ project_ids: [project2.id]
+ }
+ ) do |service|
+ expect(service).to receive(:execute).and_call_original
+ end
+
+ expected_attributes = mutation_params.except(:id, :associated_projects)
+
+ response
+
+ expect(response[:errors]).to match_array(['user not allowed to assign runner'])
+ expect(response[:runner]).to be_an_instance_of(Ci::Runner)
+ expect(response[:runner]).not_to have_attributes(expected_attributes)
+ expect(runner.reload).not_to have_attributes(expected_attributes)
+ expect(runner.projects).to match_array([project1])
+ end
+ end
+ end
+
+ context 'with id set to instance runner' do
+ let(:instance_runner) { create(:ci_runner, :instance) }
+ let(:mutation_params) do
+ {
+ id: instance_runner.to_global_id,
+ description: 'updated description',
+ associated_projects: [project2.to_global_id.to_s]
+ }
+ end
+
+ it 'raises error', :aggregate_failures do
+ expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError) do
+ response
+ end
+ end
+ end
+ end
+
+ context 'with non-existing project ID in associatedProjects argument' do
+ let(:mutation_params) do
+ {
+ id: runner.to_global_id,
+ associated_projects: ['gid://gitlab/Project/-1']
+ }
+ end
+
+ it 'does not change associated projects' do
+ expected_attributes = mutation_params.except(:id, :associated_projects)
+
+ response
+
+ expect(response[:errors]).to be_empty
+ expect(response[:runner]).to be_an_instance_of(Ci::Runner)
+ expect(response[:runner]).to have_attributes(expected_attributes)
+ expect(runner.reload).to have_attributes(expected_attributes)
+ expect(runner.projects).to match_array([project1])
+ end
+ end
+
context 'with out-of-range maximum_timeout and missing tag_list' do
- it 'returns a descriptive error' do
- mutation_params[:maximum_timeout] = 100
- mutation_params.delete(:tag_list)
+ let(:mutation_params) do
+ {
+ id: runner.to_global_id,
+ maximum_timeout: 100,
+ run_untagged: false
+ }
+ end
- expect(subject[:errors]).to contain_exactly(
+ it 'returns a descriptive error' do
+ expect(response[:errors]).to contain_exactly(
'Maximum timeout needs to be at least 10 minutes',
'Tags list can not be empty when runner is not allowed to pick untagged jobs'
)
@@ -90,7 +202,7 @@ RSpec.describe Mutations::Ci::Runner::Update do
it 'returns a descriptive error' do
mutation_params[:maintenance_note] = '1' * 1025
- expect(subject[:errors]).to contain_exactly(
+ expect(response[:errors]).to contain_exactly(
'Maintenance note is too long (maximum is 1024 characters)'
)
end
diff --git a/spec/graphql/mutations/commits/create_spec.rb b/spec/graphql/mutations/commits/create_spec.rb
index 9fc9c731b96..fd0c2c46b2e 100644
--- a/spec/graphql/mutations/commits/create_spec.rb
+++ b/spec/graphql/mutations/commits/create_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe Mutations::Commits::Create do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:group) { create(:group, :public) }
let(:context) do
GraphQL::Query::Context.new(
@@ -39,174 +40,208 @@ RSpec.describe Mutations::Commits::Create do
let(:mutated_commit) { subject[:commit] }
- it 'raises an error if the resource is not accessible to the user' do
- expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
- end
-
- context 'when user does not have enough permissions' do
- before do
- project.add_guest(user)
- end
-
+ context 'when user is not a project member' do
it 'raises an error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
- context 'when user is a maintainer of a different project' do
- before do
- create(:project_empty_repo).add_maintainer(user)
- end
+ context 'when user is a direct project member' do
+ context 'and user is a guest' do
+ before do
+ project.add_guest(user)
+ end
- it 'raises an error' do
- expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ it 'raises an error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
end
- end
- context 'when the user can create a commit' do
- let(:deltas) { mutated_commit.raw_deltas }
+ context 'and user is a developer' do
+ let(:deltas) { mutated_commit.raw_deltas }
- before_all do
- project.add_developer(user)
- end
-
- context 'when service successfully creates a new commit' do
- it "returns the ETag path for the commit's pipeline" do
- commit_pipeline_path = subject[:commit_pipeline_path]
- expect(commit_pipeline_path).to match(%r(pipelines/sha/\w+))
+ before_all do
+ project.add_developer(user)
end
- it 'returns the content of the commit' do
- expect(subject[:content]).to eq(actions.pluck(:content))
- end
+ context 'when service successfully creates a new commit' do
+ it "returns the ETag path for the commit's pipeline" do
+ commit_pipeline_path = subject[:commit_pipeline_path]
+ expect(commit_pipeline_path).to match(%r(pipelines/sha/\w+))
+ end
- it 'returns a new commit' do
- expect(mutated_commit).to have_attributes(message: message, project: project)
- expect(subject[:errors]).to be_empty
+ it 'returns the content of the commit' do
+ expect(subject[:content]).to eq(actions.pluck(:content))
+ end
- expect_to_contain_deltas([
- a_hash_including(a_mode: '0', b_mode: '100644', new_file: true, new_path: file_path)
- ])
+ it 'returns a new commit' do
+ expect(mutated_commit).to have_attributes(message: message, project: project)
+ expect(subject[:errors]).to be_empty
+
+ expect_to_contain_deltas([
+ a_hash_including(a_mode: '0', b_mode: '100644', new_file: true, new_path: file_path)
+ ])
+ end
end
- end
- context 'when request has multiple actions' do
- let(:actions) do
- [
- {
- action: 'create',
- file_path: 'foo/foobar',
- content: 'some content'
- },
- {
- action: 'delete',
- file_path: 'README.md'
- },
- {
- action: 'move',
- file_path: "LICENSE.md",
- previous_path: "LICENSE",
- content: "some content"
- },
- {
- action: 'update',
- file_path: 'VERSION',
- content: 'new content'
- },
- {
- action: 'chmod',
- file_path: 'CHANGELOG',
- execute_filemode: true
- }
- ]
+ context 'when request has multiple actions' do
+ let(:actions) do
+ [
+ {
+ action: 'create',
+ file_path: 'foo/foobar',
+ content: 'some content'
+ },
+ {
+ action: 'delete',
+ file_path: 'README.md'
+ },
+ {
+ action: 'move',
+ file_path: "LICENSE.md",
+ previous_path: "LICENSE",
+ content: "some content"
+ },
+ {
+ action: 'update',
+ file_path: 'VERSION',
+ content: 'new content'
+ },
+ {
+ action: 'chmod',
+ file_path: 'CHANGELOG',
+ execute_filemode: true
+ }
+ ]
+ end
+
+ it 'returns a new commit' do
+ expect(mutated_commit).to have_attributes(message: message, project: project)
+ expect(subject[:errors]).to be_empty
+
+ expect_to_contain_deltas([
+ a_hash_including(a_mode: '0', b_mode: '100644', new_path: 'foo/foobar'),
+ a_hash_including(deleted_file: true, new_path: 'README.md'),
+ a_hash_including(deleted_file: true, new_path: 'LICENSE'),
+ a_hash_including(new_file: true, new_path: 'LICENSE.md'),
+ a_hash_including(new_file: false, new_path: 'VERSION'),
+ a_hash_including(a_mode: '100644', b_mode: '100755', new_path: 'CHANGELOG')
+ ])
+ end
end
- it 'returns a new commit' do
- expect(mutated_commit).to have_attributes(message: message, project: project)
- expect(subject[:errors]).to be_empty
-
- expect_to_contain_deltas([
- a_hash_including(a_mode: '0', b_mode: '100644', new_path: 'foo/foobar'),
- a_hash_including(deleted_file: true, new_path: 'README.md'),
- a_hash_including(deleted_file: true, new_path: 'LICENSE'),
- a_hash_including(new_file: true, new_path: 'LICENSE.md'),
- a_hash_including(new_file: false, new_path: 'VERSION'),
- a_hash_including(a_mode: '100644', b_mode: '100755', new_path: 'CHANGELOG')
- ])
+ context 'when actions are not defined' do
+ let(:actions) { [] }
+
+ it 'returns a new commit' do
+ expect(mutated_commit).to have_attributes(message: message, project: project)
+ expect(subject[:errors]).to be_empty
+
+ expect_to_contain_deltas([])
+ end
end
- end
- context 'when actions are not defined' do
- let(:actions) { [] }
+ context 'when branch does not exist' do
+ let(:branch) { 'unknown' }
- it 'returns a new commit' do
- expect(mutated_commit).to have_attributes(message: message, project: project)
- expect(subject[:errors]).to be_empty
+ it 'returns errors' do
+ expect(mutated_commit).to be_nil
+ expect(subject[:errors]).to match_array(['You can only create or edit files when you are on a branch'])
+ end
+ end
- expect_to_contain_deltas([])
+ context 'when branch does not exist and a start branch is provided' do
+ let(:branch) { 'my-branch' }
+ let(:start_branch) { 'master' }
+ let(:actions) do
+ [
+ {
+ action: 'create',
+ file_path: 'ANOTHER_FILE.md',
+ content: 'Bye'
+ }
+ ]
+ end
+
+ it 'returns a new commit' do
+ expect(mutated_commit).to have_attributes(message: message, project: project)
+ expect(subject[:errors]).to be_empty
+ expect(subject[:content]).to eq(actions.pluck(:content))
+
+ expect_to_contain_deltas([
+ a_hash_including(a_mode: '0', b_mode: '100644', new_file: true, new_path: 'ANOTHER_FILE.md')
+ ])
+ end
end
- end
- context 'when branch does not exist' do
- let(:branch) { 'unknown' }
+ context 'when message is not set' do
+ let(:message) { nil }
- it 'returns errors' do
- expect(mutated_commit).to be_nil
- expect(subject[:errors]).to eq(['You can only create or edit files when you are on a branch'])
+ it 'returns errors' do
+ expect(mutated_commit).to be_nil
+ expect(subject[:errors].to_s).to match(/3:UserCommitFiles: empty CommitMessage/)
+ end
end
- end
- context 'when branch does not exist and a start branch is provided' do
- let(:branch) { 'my-branch' }
- let(:start_branch) { 'master' }
- let(:actions) do
- [
- {
- action: 'create',
- file_path: 'ANOTHER_FILE.md',
- content: 'Bye'
- }
- ]
+ context 'when actions are incorrect' do
+ let(:actions) { [{ action: 'unknown', file_path: 'test.md', content: '' }] }
+
+ it 'returns errors' do
+ expect(mutated_commit).to be_nil
+ expect(subject[:errors]).to match_array(['Unknown action \'unknown\''])
+ end
end
- it 'returns a new commit' do
- expect(mutated_commit).to have_attributes(message: message, project: project)
- expect(subject[:errors]).to be_empty
- expect(subject[:content]).to eq(actions.pluck(:content))
+ context 'when branch is protected' do
+ before do
+ create(:protected_branch, project: project, name: branch)
+ end
- expect_to_contain_deltas([
- a_hash_including(a_mode: '0', b_mode: '100644', new_file: true, new_path: 'ANOTHER_FILE.md')
- ])
+ it 'returns errors' do
+ expect(mutated_commit).to be_nil
+ expect(subject[:errors]).to match_array(['You are not allowed to push into this branch'])
+ end
end
end
+ end
- context 'when message is not set' do
- let(:message) { nil }
+ context 'when user is an inherited member from the group' do
+ context 'when project is public with private repository' do
+ let(:project) { create(:project, :public, :repository, :repository_private, group: group) }
- it 'returns errors' do
- expect(mutated_commit).to be_nil
- expect(subject[:errors].to_s).to match(/3:UserCommitFiles: empty CommitMessage/)
+ context 'and user is a guest' do
+ before do
+ group.add_guest(user)
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
end
end
- context 'when actions are incorrect' do
- let(:actions) { [{ action: 'unknown', file_path: 'test.md', content: '' }] }
+ context 'when project is private' do
+ let(:project) { create(:project, :private, :repository, group: group) }
+
+ context 'and user is a guest' do
+ before do
+ group.add_guest(user)
+ end
- it 'returns errors' do
- expect(mutated_commit).to be_nil
- expect(subject[:errors]).to eq(['Unknown action \'unknown\''])
+ it 'raises an error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
end
end
+ end
- context 'when branch is protected' do
- before do
- create(:protected_branch, project: project, name: branch)
- end
+ context 'when user is a maintainer of a different project' do
+ before do
+ create(:project_empty_repo).add_maintainer(user)
+ end
- it 'returns errors' do
- expect(mutated_commit).to be_nil
- expect(subject[:errors]).to eq(['You are not allowed to push into this branch'])
- end
+ it 'raises an error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
end
diff --git a/spec/graphql/mutations/environments/canary_ingress/update_spec.rb b/spec/graphql/mutations/environments/canary_ingress/update_spec.rb
index b93fb36a8ff..2476431816e 100644
--- a/spec/graphql/mutations/environments/canary_ingress/update_spec.rb
+++ b/spec/graphql/mutations/environments/canary_ingress/update_spec.rb
@@ -43,11 +43,12 @@ RSpec.describe Mutations::Environments::CanaryIngress::Update do
end
it 'returns notice about feature removal' do
- expect(subject[:errors]).to match_array([
- 'This endpoint was deactivated as part of the certificate-based' \
- 'kubernetes integration removal. See Epic:' \
- 'https://gitlab.com/groups/gitlab-org/configure/-/epics/8'
- ])
+ expect(subject[:errors]).to match_array(
+ [
+ 'This endpoint was deactivated as part of the certificate-based' \
+ 'kubernetes integration removal. See Epic:' \
+ 'https://gitlab.com/groups/gitlab-org/configure/-/epics/8'
+ ])
end
end
end
diff --git a/spec/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb b/spec/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb
index 4541f8af7d3..56514c985ff 100644
--- a/spec/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb
+++ b/spec/graphql/mutations/incident_management/timeline_event/promote_from_note_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe Mutations::IncidentManagement::TimelineEvent::PromoteFromNote do
+ include NotesHelper
+
let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:incident) { create(:incident, project: project) }
@@ -23,7 +25,7 @@ RSpec.describe Mutations::IncidentManagement::TimelineEvent::PromoteFromNote do
let(:expected_timeline_event) do
instance_double(
'IncidentManagement::TimelineEvent',
- note: comment.note,
+ note: "@#{comment.author.username} [commented](#{noteable_note_url(comment)}): '#{comment.note}'",
occurred_at: comment.created_at.to_s,
incident: incident,
author: current_user,
diff --git a/spec/graphql/mutations/merge_requests/create_spec.rb b/spec/graphql/mutations/merge_requests/create_spec.rb
index e1edb60e4ff..6e593a5f4be 100644
--- a/spec/graphql/mutations/merge_requests/create_spec.rb
+++ b/spec/graphql/mutations/merge_requests/create_spec.rb
@@ -7,8 +7,7 @@ RSpec.describe Mutations::MergeRequests::Create do
subject(:mutation) { described_class.new(object: nil, context: context, field: nil) }
- let_it_be(:project) { create(:project, :public, :repository) }
- let_it_be(:user) { create(:user) }
+ let(:user) { create(:user) }
let(:context) do
GraphQL::Query::Context.new(
@@ -38,62 +37,106 @@ RSpec.describe Mutations::MergeRequests::Create do
let(:mutated_merge_request) { subject[:merge_request] }
- it 'raises an error if the resource is not accessible to the user' do
- expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ shared_examples 'resource not available' do
+ it 'raises an error' do
+ expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
+ end
end
- context 'when user does not have enough permissions to create a merge request' do
- before do
- project.add_guest(user)
- end
+ context 'when user is not a project member' do
+ let_it_be(:project) { create(:project, :public, :repository) }
- it 'raises an error if the resource is not accessible to the user' do
- expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
- end
+ it_behaves_like 'resource not available'
end
- context 'when the user can create a merge request' do
- before_all do
- project.add_developer(user)
- end
+ context 'when user is a direct project member' do
+ let_it_be(:project) { create(:project, :public, :repository) }
- it 'creates a new merge request' do
- expect { mutated_merge_request }.to change(MergeRequest, :count).by(1)
- end
+ context 'and user is a guest' do
+ before do
+ project.add_guest(user)
+ end
- it 'returns a new merge request' do
- expect(mutated_merge_request.title).to eq(title)
- expect(subject[:errors]).to be_empty
+ it_behaves_like 'resource not available'
end
- context 'when optional description field is set' do
- let(:description) { 'content' }
+ context 'and user is a developer' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'creates a new merge request' do
+ expect { mutated_merge_request }.to change(MergeRequest, :count).by(1)
+ end
- it 'returns a new merge request with a description' do
- expect(mutated_merge_request.description).to eq(description)
+ it 'returns a new merge request' do
+ expect(mutated_merge_request.title).to eq(title)
expect(subject[:errors]).to be_empty
end
- end
- context 'when optional labels field is set' do
- let(:labels) { %w[label-1 label-2] }
+ context 'when optional description field is set' do
+ let(:description) { 'content' }
- it 'returns a new merge request with labels' do
- expect(mutated_merge_request.labels.map(&:title)).to eq(labels)
- expect(subject[:errors]).to be_empty
+ it 'returns a new merge request with a description' do
+ expect(mutated_merge_request.description).to eq(description)
+ expect(subject[:errors]).to be_empty
+ end
+ end
+
+ context 'when optional labels field is set' do
+ let(:labels) { %w[label-1 label-2] }
+
+ it 'returns a new merge request with labels' do
+ expect(mutated_merge_request.labels.map(&:title)).to eq(labels)
+ expect(subject[:errors]).to be_empty
+ end
+ end
+
+ context 'when service cannot create a merge request' do
+ let(:title) { nil }
+
+ it 'does not create a new merge request' do
+ expect { mutated_merge_request }.not_to change(MergeRequest, :count)
+ end
+
+ it 'returns errors' do
+ expect(mutated_merge_request).to be_nil
+ expect(subject[:errors]).to match_array(['Title can\'t be blank'])
+ end
end
end
+ end
- context 'when service cannot create a merge request' do
- let(:title) { nil }
+ context 'when user is an inherited member from the group' do
+ let_it_be(:group) { create(:group, :public) }
- it 'does not create a new merge request' do
- expect { mutated_merge_request }.not_to change(MergeRequest, :count)
+ context 'when project is public with private merge requests' do
+ let_it_be(:project) do
+ create(:project,
+ :public,
+ :repository,
+ group: group,
+ merge_requests_access_level: ProjectFeature::DISABLED)
end
- it 'returns errors' do
- expect(mutated_merge_request).to be_nil
- expect(subject[:errors]).to eq(['Title can\'t be blank'])
+ context 'and user is a guest' do
+ before do
+ group.add_guest(user)
+ end
+
+ it_behaves_like 'resource not available'
+ end
+ end
+
+ context 'when project is private' do
+ let_it_be(:project) { create(:project, :private, :repository, group: group) }
+
+ context 'and user is a guest' do
+ before do
+ group.add_guest(user)
+ end
+
+ it_behaves_like 'resource not available'
end
end
end
diff --git a/spec/graphql/mutations/releases/create_spec.rb b/spec/graphql/mutations/releases/create_spec.rb
index b6b9449aa39..e7c25a20bad 100644
--- a/spec/graphql/mutations/releases/create_spec.rb
+++ b/spec/graphql/mutations/releases/create_spec.rb
@@ -135,7 +135,7 @@ RSpec.describe Mutations::Releases::Create do
it 'has an access error' do
subject
- expect(resolve).to include(errors: ['Access Denied'])
+ expect(resolve).to include(errors: ['You are not allowed to create this tag as it is protected.'])
end
end
end
diff --git a/spec/graphql/mutations/releases/update_spec.rb b/spec/graphql/mutations/releases/update_spec.rb
index 15b10ea0648..0cf10e03fb1 100644
--- a/spec/graphql/mutations/releases/update_spec.rb
+++ b/spec/graphql/mutations/releases/update_spec.rb
@@ -18,8 +18,8 @@ RSpec.describe Mutations::Releases::Update do
let_it_be(:release) do
create(:release, project: project, tag: tag, name: name,
- description: description, released_at: released_at,
- created_at: created_at, milestones: [milestone_12_3, milestone_12_4])
+ description: description, released_at: released_at,
+ created_at: created_at, milestones: [milestone_12_3, milestone_12_4])
end
let(:mutation) { described_class.new(object: nil, context: { current_user: current_user }, field: nil) }
diff --git a/spec/graphql/resolvers/board_lists_resolver_spec.rb b/spec/graphql/resolvers/board_lists_resolver_spec.rb
index c882ad7c818..2fb7c5c4717 100644
--- a/spec/graphql/resolvers/board_lists_resolver_spec.rb
+++ b/spec/graphql/resolvers/board_lists_resolver_spec.rb
@@ -101,7 +101,7 @@ RSpec.describe Resolvers::BoardListsResolver do
def resolve_board_lists(args: {}, current_user: user)
resolve(described_class, obj: board, args: args, ctx: { current_user: current_user },
- arg_style: :internal
+ arg_style: :internal
)
end
end
diff --git a/spec/graphql/resolvers/ci/config_resolver_spec.rb b/spec/graphql/resolvers/ci/config_resolver_spec.rb
index dc030b1313b..692bdf58784 100644
--- a/spec/graphql/resolvers/ci/config_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/config_resolver_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe Resolvers::Ci::ConfigResolver do
subject(:response) do
resolve(described_class,
args: { project_path: project.full_path, content: content, sha: sha },
- ctx: { current_user: user })
+ ctx: { current_user: user })
end
shared_examples 'a valid config file' do
diff --git a/spec/graphql/resolvers/ci/group_runners_resolver_spec.rb b/spec/graphql/resolvers/ci/group_runners_resolver_spec.rb
index f99f48f5b07..57b2fcbea63 100644
--- a/spec/graphql/resolvers/ci/group_runners_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/group_runners_resolver_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe Resolvers::Ci::GroupRunnersResolver do
describe '#resolve' do
subject do
resolve(described_class, obj: obj, ctx: { current_user: user }, args: args,
- arg_style: :internal)
+ arg_style: :internal)
end
include_context 'runners resolver setup'
diff --git a/spec/graphql/resolvers/ci/job_token_scope_resolver_spec.rb b/spec/graphql/resolvers/ci/job_token_scope_resolver_spec.rb
index ac7cef20df4..1bfd6fbf6b9 100644
--- a/spec/graphql/resolvers/ci/job_token_scope_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/job_token_scope_resolver_spec.rb
@@ -20,10 +20,10 @@ RSpec.describe Resolvers::Ci::JobTokenScopeResolver do
project.add_member(current_user, :maintainer)
end
- it 'returns nil when scope is not enabled' do
+ it 'returns the same project in the allow list of projects for the Ci Job Token when scope is not enabled' do
allow(project).to receive(:ci_job_token_scope_enabled?).and_return(false)
- expect(resolve_scope).to eq(nil)
+ expect(resolve_scope.all_projects).to contain_exactly(project)
end
it 'returns the same project in the allow list of projects for the Ci Job Token' do
@@ -43,8 +43,8 @@ RSpec.describe Resolvers::Ci::JobTokenScopeResolver do
project.update!(ci_job_token_scope_enabled: false)
end
- it 'returns nil' do
- expect(resolve_scope).to be_nil
+ it 'resolves projects' do
+ expect(resolve_scope.all_projects).to contain_exactly(project)
end
end
end
diff --git a/spec/graphql/resolvers/ci/jobs_resolver_spec.rb b/spec/graphql/resolvers/ci/jobs_resolver_spec.rb
index 6c228861ddf..80a70938dc4 100644
--- a/spec/graphql/resolvers/ci/jobs_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/jobs_resolver_spec.rb
@@ -38,7 +38,7 @@ RSpec.describe Resolvers::Ci::JobsResolver do
::Types::Security::ReportTypeEnum.values['DAST'].value
]
jobs = resolve(described_class, obj: pipeline, args: { security_report_types: report_types },
- arg_style: :internal)
+ arg_style: :internal)
expect(jobs).to contain_exactly(
have_attributes(name: 'DAST job'),
diff --git a/spec/graphql/resolvers/ci/runner_projects_resolver_spec.rb b/spec/graphql/resolvers/ci/runner_projects_resolver_spec.rb
new file mode 100644
index 00000000000..952c7337d65
--- /dev/null
+++ b/spec/graphql/resolvers/ci/runner_projects_resolver_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::Ci::RunnerProjectsResolver do
+ include GraphqlHelpers
+
+ let_it_be(:project1) { create(:project, description: 'Project1.1') }
+ let_it_be(:project2) { create(:project, description: 'Project1.2') }
+ let_it_be(:project3) { create(:project, description: 'Project2.1') }
+ let_it_be(:runner) { create(:ci_runner, :project, projects: [project1, project2, project3]) }
+
+ let(:args) { {} }
+
+ subject { resolve_projects(args) }
+
+ describe '#resolve' do
+ context 'with authorized user', :enable_admin_mode do
+ let(:current_user) { create(:user, :admin) }
+
+ context 'with search argument' do
+ let(:args) { { search: 'Project1.' } }
+
+ it 'returns a lazy value with projects containing the specified prefix' do
+ expect(subject).to be_a(GraphQL::Execution::Lazy)
+ expect(subject.value).to contain_exactly(project1, project2)
+ end
+ end
+
+ context 'with supported arguments' do
+ let(:args) { { membership: true, search_namespaces: true, topics: %w[xyz] } }
+
+ it 'creates ProjectsFinder with expected arguments' do
+ expect(ProjectsFinder).to receive(:new).with(
+ a_hash_including(
+ params: a_hash_including(
+ non_public: true,
+ search_namespaces: true,
+ topic: %w[xyz]
+ )
+ )
+ ).and_call_original
+
+ expect(subject).to be_a(GraphQL::Execution::Lazy)
+ subject.value
+ end
+ end
+
+ context 'without arguments' do
+ it 'returns a lazy value with all projects' do
+ expect(subject).to be_a(GraphQL::Execution::Lazy)
+ expect(subject.value).to contain_exactly(project1, project2, project3)
+ end
+ end
+ end
+
+ context 'with unauthorized user' do
+ let(:current_user) { create(:user) }
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ private
+
+ def resolve_projects(args = {}, context = { current_user: current_user })
+ resolve(described_class, obj: runner, args: args, ctx: context)
+ end
+end
diff --git a/spec/graphql/resolvers/ci/runners_resolver_spec.rb b/spec/graphql/resolvers/ci/runners_resolver_spec.rb
index 8586d359336..4038192a68a 100644
--- a/spec/graphql/resolvers/ci/runners_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/runners_resolver_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe Resolvers::Ci::RunnersResolver do
subject do
resolve(described_class, obj: obj, ctx: { current_user: user }, args: args,
- arg_style: :internal)
+ arg_style: :internal)
end
include_context 'runners resolver setup'
diff --git a/spec/graphql/resolvers/ci/test_suite_resolver_spec.rb b/spec/graphql/resolvers/ci/test_suite_resolver_spec.rb
index 606c6eb03a3..4083e77a38f 100644
--- a/spec/graphql/resolvers/ci/test_suite_resolver_spec.rb
+++ b/spec/graphql/resolvers/ci/test_suite_resolver_spec.rb
@@ -32,11 +32,12 @@ RSpec.describe Resolvers::Ci::TestSuiteResolver do
# Each test failure in this pipeline has a matching failure in the default branch
recent_failures = test_suite[:test_cases].map { |tc| tc[:recent_failures] }
- expect(recent_failures).to eq([
- { count: 1, base_branch: 'master' },
- { count: 1, base_branch: 'master' },
- { count: 1, base_branch: 'master' }
- ])
+ expect(recent_failures).to eq(
+ [
+ { count: 1, base_branch: 'master' },
+ { count: 1, base_branch: 'master' },
+ { count: 1, base_branch: 'master' }
+ ])
end
end
diff --git a/spec/graphql/resolvers/container_repositories_resolver_spec.rb b/spec/graphql/resolvers/container_repositories_resolver_spec.rb
index ed922259903..8cbb366f873 100644
--- a/spec/graphql/resolvers/container_repositories_resolver_spec.rb
+++ b/spec/graphql/resolvers/container_repositories_resolver_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe Resolvers::ContainerRepositoriesResolver do
subject do
resolve(described_class, ctx: { current_user: user }, args: args, obj: object,
- arg_style: :internal)
+ arg_style: :internal)
end
shared_examples 'returning container repositories' do
diff --git a/spec/graphql/resolvers/container_repository_tags_resolver_spec.rb b/spec/graphql/resolvers/container_repository_tags_resolver_spec.rb
index 9747f663759..3ed3fe76267 100644
--- a/spec/graphql/resolvers/container_repository_tags_resolver_spec.rb
+++ b/spec/graphql/resolvers/container_repository_tags_resolver_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe Resolvers::ContainerRepositoryTagsResolver do
describe '#resolve' do
let(:resolver) do
resolve(described_class, ctx: { current_user: user }, obj: repository, args: args,
- arg_style: :internal)
+ arg_style: :internal)
end
before do
diff --git a/spec/graphql/resolvers/crm/organization_state_counts_resolver_spec.rb b/spec/graphql/resolvers/crm/organization_state_counts_resolver_spec.rb
new file mode 100644
index 00000000000..c6ad4beeee0
--- /dev/null
+++ b/spec/graphql/resolvers/crm/organization_state_counts_resolver_spec.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::Crm::OrganizationStateCountsResolver do
+ include GraphqlHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group, :crm_enabled) }
+
+ before_all do
+ create(:organization, group: group, name: "ABC Corp")
+ create(:organization, group: group, name: "123 Corp", state: 'inactive')
+ create_list(:organization, 3, group: group)
+ create_list(:organization, 2, group: group, state: 'inactive')
+ end
+
+ describe '#resolve' do
+ context 'with unauthorized user' do
+ it 'does not raise an error and returns nil' do
+ expect { resolve_counts(group) }.not_to raise_error
+ expect(resolve_counts(group)).to be_nil
+ end
+ end
+
+ context 'with authorized user' do
+ before do
+ group.add_reporter(user)
+ end
+
+ context 'without parent' do
+ it 'returns nil' do
+ expect(resolve_counts(nil)).to be_nil
+ end
+ end
+
+ context 'with a group' do
+ context 'when no filter is provided' do
+ it 'returns the count of all organizations' do
+ counts = resolve_counts(group)
+ expect(counts['active']).to eq(4)
+ expect(counts['inactive']).to eq(3)
+ end
+ end
+
+ context 'when search term is provided' do
+ it 'returns the correct counts' do
+ counts = resolve_counts(group, { search: "Corp" })
+
+ expect(counts['active']).to eq(1)
+ expect(counts['inactive']).to eq(1)
+ end
+ end
+ end
+ end
+ end
+
+ def resolve_counts(parent, args = {}, context = { current_user: user })
+ resolve(described_class, obj: parent, args: args, ctx: context)
+ end
+end
diff --git a/spec/graphql/resolvers/crm/organizations_resolver_spec.rb b/spec/graphql/resolvers/crm/organizations_resolver_spec.rb
index 323f134ffc3..d5980bf3c41 100644
--- a/spec/graphql/resolvers/crm/organizations_resolver_spec.rb
+++ b/spec/graphql/resolvers/crm/organizations_resolver_spec.rb
@@ -55,11 +55,23 @@ RSpec.describe Resolvers::Crm::OrganizationsResolver do
end
context 'when no filter is provided' do
- it 'returns all the organizations in the correct order' do
+ it 'returns all the organizations in the default order' do
expect(resolve_organizations(group)).to eq([organization_a, organization_b])
end
end
+ context 'when a sort is provided' do
+ it 'returns all the organizations in the correct order' do
+ expect(resolve_organizations(group, { sort: 'NAME_DESC' })).to eq([organization_b, organization_a])
+ end
+ end
+
+ context 'when filtering for all states' do
+ it 'returns all the organizations' do
+ expect(resolve_organizations(group, { state: 'all' })).to contain_exactly(organization_a, organization_b)
+ end
+ end
+
context 'when search term is provided' do
it 'returns the correct organizations' do
expect(resolve_organizations(group, { search: "def" })).to contain_exactly(organization_b)
diff --git a/spec/graphql/resolvers/deployment_resolver_spec.rb b/spec/graphql/resolvers/deployment_resolver_spec.rb
new file mode 100644
index 00000000000..9231edefddc
--- /dev/null
+++ b/spec/graphql/resolvers/deployment_resolver_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::DeploymentResolver do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project, :repository, :private) }
+ let_it_be(:environment) { create(:environment, project: project) }
+ let_it_be(:deployment) { create(:deployment, :created, environment: environment, project: project) }
+ let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
+
+ let(:current_user) { developer }
+
+ describe '#resolve' do
+ it 'finds the deployment' do
+ expect(resolve_deployments(iid: deployment.iid)).to contain_exactly(deployment)
+ end
+
+ it 'does not find the deployment if the IID does not match' do
+ expect(resolve_deployments(iid: non_existing_record_id)).to be_empty
+ end
+ end
+
+ def resolve_deployments(args = {}, context = { current_user: current_user })
+ resolve(described_class, obj: project, args: args, ctx: context)
+ end
+end
diff --git a/spec/graphql/resolvers/deployments_resolver_spec.rb b/spec/graphql/resolvers/deployments_resolver_spec.rb
new file mode 100644
index 00000000000..4e5564aad0b
--- /dev/null
+++ b/spec/graphql/resolvers/deployments_resolver_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::DeploymentsResolver do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project, :repository, :private) }
+ let_it_be(:environment) { create(:environment, project: project) }
+ let_it_be(:deployment) { create(:deployment, :created, environment: environment, project: project) }
+ let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
+
+ let(:current_user) { developer }
+
+ describe '#resolve' do
+ it 'finds the deployment' do
+ expect(resolve_deployments).to contain_exactly(deployment)
+ end
+
+ it 'finds the deployment when status matches' do
+ expect(resolve_deployments(statuses: [:created])).to contain_exactly(deployment)
+ end
+
+ it 'does not find the deployment when status does not match' do
+ expect(resolve_deployments(statuses: [:success])).to be_empty
+ end
+
+ it 'transforms order_by for finder' do
+ expect(DeploymentsFinder)
+ .to receive(:new)
+ .with(environment: environment.id, status: ['success'], order_by: 'finished_at', sort: 'asc')
+ .and_call_original
+
+ resolve_deployments(statuses: [:success], order_by: { finished_at: :asc })
+ end
+ end
+
+ def resolve_deployments(args = {}, context = { current_user: current_user })
+ resolve(described_class, obj: environment, args: args, ctx: context)
+ end
+end
diff --git a/spec/graphql/resolvers/design_management/versions_resolver_spec.rb b/spec/graphql/resolvers/design_management/versions_resolver_spec.rb
index 3a2ed445484..eb39e5bafc5 100644
--- a/spec/graphql/resolvers/design_management/versions_resolver_spec.rb
+++ b/spec/graphql/resolvers/design_management/versions_resolver_spec.rb
@@ -82,7 +82,7 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do
let(:params) do
{
earlier_or_equal_to_sha: first_version.sha,
- earlier_or_equal_to_id: global_id_of(first_version)
+ earlier_or_equal_to_id: global_id_of(first_version)
}
end
@@ -95,7 +95,7 @@ RSpec.describe Resolvers::DesignManagement::VersionsResolver do
let(:params) do
{
earlier_or_equal_to_sha: first_version.sha,
- earlier_or_equal_to_id: global_id_of(other_version)
+ earlier_or_equal_to_id: global_id_of(other_version)
}
end
diff --git a/spec/graphql/resolvers/environments/last_deployment_resolver_spec.rb b/spec/graphql/resolvers/environments/last_deployment_resolver_spec.rb
new file mode 100644
index 00000000000..95a1a06730d
--- /dev/null
+++ b/spec/graphql/resolvers/environments/last_deployment_resolver_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Resolvers::Environments::LastDeploymentResolver do
+ include GraphqlHelpers
+ include Gitlab::Graphql::Laziness
+
+ let_it_be(:project) { create(:project, :repository, :private) }
+ let_it_be(:environment) { create(:environment, project: project) }
+ let_it_be(:deployment) { create(:deployment, :created, environment: environment, project: project) }
+ let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
+
+ let(:current_user) { developer }
+
+ describe '#resolve' do
+ it 'finds the deployment when status matches' do
+ expect(resolve_last_deployment(status: :created)).to eq(deployment)
+ end
+
+ it 'does not find the deployment when status does not match' do
+ expect(resolve_last_deployment(status: :success)).to be_nil
+ end
+
+ it 'raises an error when status is not specified' do
+ expect { resolve_last_deployment }.to raise_error(ArgumentError)
+ end
+
+ it 'raises an error when status is not supported' do
+ expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError,
+ '"skipped" status is not supported.') do
+ resolve_last_deployment(status: :skipped)
+ end
+ end
+ end
+
+ def resolve_last_deployment(args = {}, context = { current_user: current_user })
+ force(resolve(described_class, obj: environment, ctx: context, args: args))
+ end
+end
diff --git a/spec/graphql/resolvers/group_members_resolver_spec.rb b/spec/graphql/resolvers/group_members_resolver_spec.rb
index bd0b4870062..d860b87875e 100644
--- a/spec/graphql/resolvers/group_members_resolver_spec.rb
+++ b/spec/graphql/resolvers/group_members_resolver_spec.rb
@@ -2,9 +2,11 @@
require 'spec_helper'
-RSpec.describe Resolvers::GroupMembersResolver do
+RSpec.describe 'Resolvers::GroupMembersResolver' do
include GraphqlHelpers
+ let(:described_class) { Resolvers::GroupMembersResolver }
+
specify do
expect(described_class).to have_nullable_graphql_type(Types::GroupMemberType.connection_type)
end
diff --git a/spec/graphql/resolvers/issues_resolver_spec.rb b/spec/graphql/resolvers/issues_resolver_spec.rb
index 89e45810033..a74b2a3f18c 100644
--- a/spec/graphql/resolvers/issues_resolver_spec.rb
+++ b/spec/graphql/resolvers/issues_resolver_spec.rb
@@ -311,49 +311,15 @@ RSpec.describe Resolvers::IssuesResolver do
end
context 'when searching issues' do
- it 'returns correct issues' do
- expect(resolve_issues(search: 'foo')).to contain_exactly(issue2)
- end
-
- it 'uses project search optimization' do
- expected_arguments = a_hash_including(
- search: 'foo',
- attempt_project_search_optimizations: true
- )
- expect(IssuesFinder).to receive(:new).with(anything, expected_arguments).and_call_original
-
- resolve_issues(search: 'foo')
- end
-
- context 'with anonymous user' do
- let_it_be(:public_project) { create(:project, :public) }
- let_it_be(:public_issue) { create(:issue, project: public_project, title: 'Test issue') }
-
- context 'with disable_anonymous_search enabled' do
- before do
- stub_feature_flags(disable_anonymous_search: true)
- end
-
- it 'generates an error' do
- error_message = "User must be authenticated to include the `search` argument."
-
- expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, error_message) do
- resolve(described_class, obj: public_project, args: { search: 'test' }, ctx: { current_user: nil })
- end
- end
- end
-
- context 'with disable_anonymous_search disabled' do
- before do
- stub_feature_flags(disable_anonymous_search: false)
- end
-
- it 'returns correct issues' do
- expect(
- resolve(described_class, obj: public_project, args: { search: 'test' }, ctx: { current_user: nil })
- ).to contain_exactly(public_issue)
- end
- end
+ it_behaves_like 'graphql query for searching issuables' do
+ let_it_be(:parent) { project }
+ let_it_be(:issuable1) { create(:issue, project: project, title: 'first created') }
+ let_it_be(:issuable2) { create(:issue, project: project, title: 'second created', description: 'text 1') }
+ let_it_be(:issuable3) { create(:issue, project: project, title: 'third', description: 'text 2') }
+ let_it_be(:issuable4) { create(:issue, project: project) }
+
+ let_it_be(:finder_class) { IssuesFinder }
+ let_it_be(:optimization_param) { :attempt_project_search_optimizations }
end
end
diff --git a/spec/graphql/resolvers/project_members_resolver_spec.rb b/spec/graphql/resolvers/project_members_resolver_spec.rb
index 2f4145b3215..c38cb3d157b 100644
--- a/spec/graphql/resolvers/project_members_resolver_spec.rb
+++ b/spec/graphql/resolvers/project_members_resolver_spec.rb
@@ -2,9 +2,11 @@
require 'spec_helper'
-RSpec.describe Resolvers::ProjectMembersResolver do
+RSpec.describe 'Resolvers::ProjectMembersResolver' do
include GraphqlHelpers
+ let(:described_class) { Resolvers::ProjectMembersResolver }
+
it_behaves_like 'querying members with a group' do
let_it_be(:project) { create(:project, group: group_1) }
let_it_be(:resource_member) { create(:project_member, user: user_1, project: project) }
diff --git a/spec/graphql/resolvers/work_items_resolver_spec.rb b/spec/graphql/resolvers/work_items_resolver_spec.rb
index 29eac0ab46e..d89ccc7f806 100644
--- a/spec/graphql/resolvers/work_items_resolver_spec.rb
+++ b/spec/graphql/resolvers/work_items_resolver_spec.rb
@@ -19,13 +19,13 @@ RSpec.describe Resolvers::WorkItemsResolver do
let_it_be(:item2) do
create(:work_item, project: project, state: :closed, title: 'foo',
- created_at: 1.hour.ago, updated_at: 1.hour.ago, closed_at:
+ created_at: 1.hour.ago, updated_at: 1.hour.ago, closed_at:
1.hour.ago)
end
let_it_be(:item3) do
create(:work_item, project: other_project, state: :closed, title: 'foo',
- created_at: 1.hour.ago, updated_at: 1.hour.ago, closed_at:
+ created_at: 1.hour.ago, updated_at: 1.hour.ago, closed_at:
1.hour.ago)
end
@@ -52,49 +52,15 @@ RSpec.describe Resolvers::WorkItemsResolver do
end
context 'when searching items' do
- it 'returns correct items' do
- expect(resolve_items(search: 'foo')).to contain_exactly(item2)
- end
-
- it 'uses project search optimization' do
- expected_arguments = a_hash_including(
- search: 'foo',
- attempt_project_search_optimizations: true
- )
- expect(::WorkItems::WorkItemsFinder).to receive(:new).with(anything, expected_arguments).and_call_original
-
- resolve_items(search: 'foo')
- end
-
- context 'with anonymous user' do
- let_it_be(:public_project) { create(:project, :public) }
- let_it_be(:public_item) { create(:work_item, project: public_project, title: 'Test item') }
-
- context 'with disable_anonymous_search enabled' do
- before do
- stub_feature_flags(disable_anonymous_search: true)
- end
-
- it 'generates an error' do
- error_message = "User must be authenticated to include the `search` argument."
-
- expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, error_message) do
- resolve(described_class, obj: public_project, args: { search: 'test' }, ctx: { current_user: nil })
- end
- end
- end
-
- context 'with disable_anonymous_search disabled' do
- before do
- stub_feature_flags(disable_anonymous_search: false)
- end
-
- it 'returns correct items' do
- expect(
- resolve(described_class, obj: public_project, args: { search: 'test' }, ctx: { current_user: nil })
- ).to contain_exactly(public_item)
- end
- end
+ it_behaves_like 'graphql query for searching issuables' do
+ let_it_be(:parent) { project }
+ let_it_be(:issuable1) { create(:work_item, project: project, title: 'first created') }
+ let_it_be(:issuable2) { create(:work_item, project: project, title: 'second created', description: 'text 1') }
+ let_it_be(:issuable3) { create(:work_item, project: project, title: 'third', description: 'text 2') }
+ let_it_be(:issuable4) { create(:work_item, project: project) }
+
+ let_it_be(:finder_class) { ::WorkItems::WorkItemsFinder }
+ let_it_be(:optimization_param) { :attempt_project_search_optimizations }
end
end
diff --git a/spec/graphql/types/base_field_spec.rb b/spec/graphql/types/base_field_spec.rb
index b85716e4d21..9f8a8717efb 100644
--- a/spec/graphql/types/base_field_spec.rb
+++ b/spec/graphql/types/base_field_spec.rb
@@ -205,39 +205,6 @@ RSpec.describe Types::BaseField do
end
end
end
-
- describe '#visible?' do
- context 'and has a feature_flag' do
- let(:flag) { :test_feature }
- let(:field) { described_class.new(name: 'test', type: GraphQL::Types::String, _deprecated_feature_flag: flag, null: false) }
- let(:context) { {} }
-
- before do
- skip_feature_flags_yaml_validation
- end
-
- it 'checks YAML definition for default_enabled' do
- # Exception is indicative of a check for YAML definition
- expect { field.visible?(context) }.to raise_error(Feature::InvalidFeatureFlagError, /The feature flag YAML definition for '#{flag}' does not exist/)
- end
-
- context 'skipping YAML check' do
- before do
- skip_default_enabled_yaml_check
- end
-
- it 'returns false if the feature is not enabled' do
- stub_feature_flags(flag => false)
-
- expect(field.visible?(context)).to eq(false)
- end
-
- it 'returns true if the feature is enabled' do
- expect(field.visible?(context)).to eq(true)
- end
- end
- end
- end
end
describe '#resolve' do
@@ -251,77 +218,11 @@ RSpec.describe Types::BaseField do
end
end
- describe '#description' do
- context 'feature flag given' do
- let(:field) { described_class.new(name: 'test', type: GraphQL::Types::String, _deprecated_feature_flag: flag, null: false, description: 'Test description.') }
- let(:flag) { :test_flag }
-
- it 'prepends the description' do
- expect(field.description).to start_with 'Test description. Available only when feature flag `test_flag` is enabled.'
- end
-
- context 'falsey feature_flag values' do
- using RSpec::Parameterized::TableSyntax
-
- where(:flag, :feature_value, :default_enabled) do
- '' | false | false
- '' | true | false
- nil | false | true
- nil | true | false
- end
-
- with_them do
- it 'returns the correct description' do
- expect(field.description).to eq('Test description.')
- end
- end
- end
-
- context 'with different default_enabled values' do
- using RSpec::Parameterized::TableSyntax
-
- where(:feature_value, :default_enabled, :expected_description) do
- disabled_ff_description = "Test description. Available only when feature flag `test_flag` is enabled. This flag is disabled by default, because the feature is experimental and is subject to change without notice."
- enabled_ff_description = "Test description. Available only when feature flag `test_flag` is enabled. This flag is enabled by default."
-
- false | false | disabled_ff_description
- true | false | disabled_ff_description
- false | true | enabled_ff_description
- true | true | enabled_ff_description
- end
-
- with_them do
- before do
- stub_feature_flags("#{flag}": feature_value)
-
- allow(Feature::Definition).to receive(:has_definition?).with(flag).and_return(true)
- allow(Feature::Definition).to receive(:default_enabled?).and_return(default_enabled)
- end
-
- it 'returns the correct availability in the description' do
- expect(field.description).to eq expected_description
- end
- end
- end
- end
- end
-
include_examples 'Gitlab-style deprecations' do
def subject(args = {})
base_args = { name: 'test', type: GraphQL::Types::String, null: true }
described_class.new(**base_args.merge(args))
end
-
- it 'interacts well with the `_deprecated_feature_flag` property' do
- field = subject(
- deprecated: { milestone: '1.10', reason: 'Deprecation reason' },
- description: 'Field description.',
- _deprecated_feature_flag: 'foo_flag'
- )
-
- expect(field.description).to start_with('Field description. Available only when feature flag `foo_flag` is enabled.')
- expect(field.description).to end_with('Deprecated in 1.10: Deprecation reason.')
- end
end
end
diff --git a/spec/graphql/types/branch_protections/merge_access_level_type_spec.rb b/spec/graphql/types/branch_protections/merge_access_level_type_spec.rb
new file mode 100644
index 00000000000..8cc1005d97e
--- /dev/null
+++ b/spec/graphql/types/branch_protections/merge_access_level_type_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['MergeAccessLevel'] do
+ subject { described_class }
+
+ let(:fields) { %i[access_level access_level_description] }
+
+ specify { is_expected.to require_graphql_authorizations(:read_protected_branch) }
+
+ specify { is_expected.to have_graphql_fields(fields).at_least }
+end
diff --git a/spec/graphql/types/branch_protections/push_access_level_type_spec.rb b/spec/graphql/types/branch_protections/push_access_level_type_spec.rb
new file mode 100644
index 00000000000..c78c0bda74c
--- /dev/null
+++ b/spec/graphql/types/branch_protections/push_access_level_type_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['PushAccessLevel'] do
+ subject { described_class }
+
+ let(:fields) { %i[access_level access_level_description] }
+
+ specify { is_expected.to require_graphql_authorizations(:read_protected_branch) }
+
+ specify { is_expected.to have_graphql_fields(fields).at_least }
+end
diff --git a/spec/graphql/types/branch_rule_type_spec.rb b/spec/graphql/types/branch_rule_type_spec.rb
new file mode 100644
index 00000000000..277901f00bf
--- /dev/null
+++ b/spec/graphql/types/branch_rule_type_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['BranchRule'] do
+ include GraphqlHelpers
+
+ subject { described_class }
+
+ let(:fields) do
+ %i[
+ name
+ branch_protection
+ created_at
+ updated_at
+ ]
+ end
+
+ specify { is_expected.to require_graphql_authorizations(:read_protected_branch) }
+
+ specify { is_expected.to have_graphql_fields(fields) }
+end
diff --git a/spec/graphql/types/branch_rules/branch_protection_type_spec.rb b/spec/graphql/types/branch_rules/branch_protection_type_spec.rb
new file mode 100644
index 00000000000..bbc92fd8fef
--- /dev/null
+++ b/spec/graphql/types/branch_rules/branch_protection_type_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['BranchProtection'] do
+ subject { described_class }
+
+ let(:fields) { %i[merge_access_levels push_access_levels allow_force_push] }
+
+ specify { is_expected.to require_graphql_authorizations(:read_protected_branch) }
+
+ specify { is_expected.to have_graphql_fields(fields).at_least }
+end
diff --git a/spec/graphql/types/ci/config_variable_type_spec.rb b/spec/graphql/types/ci/config_variable_type_spec.rb
new file mode 100644
index 00000000000..2b0937a7858
--- /dev/null
+++ b/spec/graphql/types/ci/config_variable_type_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['CiConfigVariable'] do
+ specify { expect(described_class).to have_graphql_fields(:key, :description, :value).at_least }
+end
diff --git a/spec/graphql/types/ci/group_variable_connection_type_spec.rb b/spec/graphql/types/ci/group_variable_connection_type_spec.rb
new file mode 100644
index 00000000000..4a1fd490506
--- /dev/null
+++ b/spec/graphql/types/ci/group_variable_connection_type_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['CiGroupVariableConnection'] do
+ it 'has the expected fields' do
+ expected_fields = %i[limit page_info edges nodes]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/ci/instance_variable_type_spec.rb b/spec/graphql/types/ci/instance_variable_type_spec.rb
index cf4aaed31f1..c77a4ac1dc4 100644
--- a/spec/graphql/types/ci/instance_variable_type_spec.rb
+++ b/spec/graphql/types/ci/instance_variable_type_spec.rb
@@ -5,5 +5,5 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['CiInstanceVariable'] do
specify { expect(described_class.interfaces).to contain_exactly(Types::Ci::VariableInterface) }
- specify { expect(described_class).to have_graphql_fields(:masked, :protected).at_least }
+ specify { expect(described_class).to have_graphql_fields(:environment_scope, :masked, :protected).at_least }
end
diff --git a/spec/graphql/types/ci/job_artifact_type_spec.rb b/spec/graphql/types/ci/job_artifact_type_spec.rb
index 58b5f9cfcb7..3e054faf0c9 100644
--- a/spec/graphql/types/ci/job_artifact_type_spec.rb
+++ b/spec/graphql/types/ci/job_artifact_type_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['CiJobArtifact'] do
it 'has the correct fields' do
- expected_fields = [:download_path, :file_type, :name]
+ expected_fields = [:id, :download_path, :file_type, :name, :size, :expire_at]
expect(described_class).to have_graphql_fields(*expected_fields)
end
diff --git a/spec/graphql/types/ci/job_token_scope_type_spec.rb b/spec/graphql/types/ci/job_token_scope_type_spec.rb
index 457d46b6896..18f4d762d1e 100644
--- a/spec/graphql/types/ci/job_token_scope_type_spec.rb
+++ b/spec/graphql/types/ci/job_token_scope_type_spec.rb
@@ -69,8 +69,8 @@ RSpec.describe GitlabSchema.types['CiJobTokenScopeType'] do
expect(subject['errors']).to be_nil
end
- it 'returns nil' do
- expect(subject['data']['project']['ciJobTokenScope']).to be_nil
+ it 'returns readable projects in scope' do
+ expect(returned_project_paths).to contain_exactly(project.path)
end
end
end
diff --git a/spec/graphql/types/ci/job_type_spec.rb b/spec/graphql/types/ci/job_type_spec.rb
index bc9e64282bc..b3dee082d1f 100644
--- a/spec/graphql/types/ci/job_type_spec.rb
+++ b/spec/graphql/types/ci/job_type_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe Types::Ci::JobType do
+ include GraphqlHelpers
+
specify { expect(described_class.graphql_name).to eq('CiJob') }
specify { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Ci::Job) }
@@ -45,8 +47,21 @@ RSpec.describe Types::Ci::JobType do
tags
triggered
userPermissions
+ webPath
]
expect(described_class).to have_graphql_fields(*expected_fields)
end
+
+ describe '#web_path' do
+ subject { resolve_field(:web_path, build, current_user: user, object_type: described_class) }
+
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:build) { create(:ci_build, project: project, user: user) }
+
+ it 'returns the web path of the job' do
+ is_expected.to eq("/#{project.full_path}/-/jobs/#{build.id}")
+ end
+ end
end
diff --git a/spec/graphql/types/ci/manual_variable_type_spec.rb b/spec/graphql/types/ci/manual_variable_type_spec.rb
index 2884c818a52..21d36b7dfc0 100644
--- a/spec/graphql/types/ci/manual_variable_type_spec.rb
+++ b/spec/graphql/types/ci/manual_variable_type_spec.rb
@@ -4,4 +4,6 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['CiManualVariable'] do
specify { expect(described_class.interfaces).to contain_exactly(Types::Ci::VariableInterface) }
+
+ specify { expect(described_class).to have_graphql_fields(:environment_scope).at_least }
end
diff --git a/spec/graphql/types/ci/project_variable_connection_type_spec.rb b/spec/graphql/types/ci/project_variable_connection_type_spec.rb
new file mode 100644
index 00000000000..97c3a207f7f
--- /dev/null
+++ b/spec/graphql/types/ci/project_variable_connection_type_spec.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['CiProjectVariableConnection'] do
+ it 'has the expected fields' do
+ expected_fields = %i[limit page_info edges nodes]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/ci/runner_architecture_type_spec.rb b/spec/graphql/types/ci/runner_architecture_type_spec.rb
index 527adef8cf9..60709acfd53 100644
--- a/spec/graphql/types/ci/runner_architecture_type_spec.rb
+++ b/spec/graphql/types/ci/runner_architecture_type_spec.rb
@@ -7,8 +7,8 @@ RSpec.describe Types::Ci::RunnerArchitectureType do
it 'exposes the expected fields' do
expected_fields = %i[
- name
- download_location
+ name
+ download_location
]
expect(described_class).to have_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/ci/runner_platform_type_spec.rb b/spec/graphql/types/ci/runner_platform_type_spec.rb
index 66b83f607d0..29b8e885183 100644
--- a/spec/graphql/types/ci/runner_platform_type_spec.rb
+++ b/spec/graphql/types/ci/runner_platform_type_spec.rb
@@ -7,9 +7,9 @@ RSpec.describe Types::Ci::RunnerPlatformType do
it 'exposes the expected fields' do
expected_fields = %i[
- name
- human_readable_name
- architectures
+ name
+ human_readable_name
+ architectures
]
expect(described_class).to have_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/ci/variable_interface_spec.rb b/spec/graphql/types/ci/variable_interface_spec.rb
index 8cef0ac2a14..328c5305a44 100644
--- a/spec/graphql/types/ci/variable_interface_spec.rb
+++ b/spec/graphql/types/ci/variable_interface_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['CiVariable'] do
specify do
expect(described_class).to have_graphql_fields(
- :id, :key, :value, :variable_type, :raw
+ :id, :key, :raw, :value, :variable_type
).at_least
end
end
diff --git a/spec/graphql/types/clusters/agent_type_spec.rb b/spec/graphql/types/clusters/agent_type_spec.rb
index 3f4faccf15d..bb1006c55c0 100644
--- a/spec/graphql/types/clusters/agent_type_spec.rb
+++ b/spec/graphql/types/clusters/agent_type_spec.rb
@@ -9,5 +9,5 @@ RSpec.describe GitlabSchema.types['ClusterAgent'] do
it { expect(described_class).to require_graphql_authorizations(:read_cluster) }
- it { expect(described_class).to have_graphql_fields(fields) }
+ it { expect(described_class).to include_graphql_fields(*fields) }
end
diff --git a/spec/graphql/types/customer_relations/organization_sort_enum_spec.rb b/spec/graphql/types/customer_relations/organization_sort_enum_spec.rb
new file mode 100644
index 00000000000..7ff498f0097
--- /dev/null
+++ b/spec/graphql/types/customer_relations/organization_sort_enum_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['OrganizationSort'] do
+ specify { expect(described_class.graphql_name).to eq('OrganizationSort') }
+
+ it_behaves_like 'common sort values'
+
+ it 'exposes all the contact sort values' do
+ expect(described_class.values.keys).to include(
+ *%w[
+ NAME_ASC
+ NAME_DESC
+ DESCRIPTION_ASC
+ DESCRIPTION_DESC
+ DEFAULT_RATE_ASC
+ DEFAULT_RATE_DESC
+ ]
+ )
+ end
+end
diff --git a/spec/graphql/types/customer_relations/organization_state_counts_type_spec.rb b/spec/graphql/types/customer_relations/organization_state_counts_type_spec.rb
new file mode 100644
index 00000000000..a2c8dacb1a5
--- /dev/null
+++ b/spec/graphql/types/customer_relations/organization_state_counts_type_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['OrganizationStateCounts'] do
+ include GraphqlHelpers
+
+ let(:fields) do
+ %w[
+ all
+ active
+ inactive
+ ]
+ end
+
+ let(:object) do
+ {
+ 'inactive' => 3,
+ 'active' => 4
+ }
+ end
+
+ it { expect(described_class.graphql_name).to eq('OrganizationStateCounts') }
+ it { expect(described_class).to have_graphql_fields(fields) }
+
+ describe '#all' do
+ it 'returns the sum of all counts' do
+ expect(resolve_field(:all, object)).to eq(7)
+ end
+ end
+end
diff --git a/spec/graphql/types/deployment_details_type_spec.rb b/spec/graphql/types/deployment_details_type_spec.rb
new file mode 100644
index 00000000000..70fdc38019e
--- /dev/null
+++ b/spec/graphql/types/deployment_details_type_spec.rb
@@ -0,0 +1,17 @@
+# 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 have_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
new file mode 100644
index 00000000000..bf4be0523c6
--- /dev/null
+++ b/spec/graphql/types/deployment_type_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['Deployment'] do
+ specify { expect(described_class.graphql_name).to eq('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
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+
+ specify { expect(described_class).to require_graphql_authorizations(:read_deployment) }
+end
diff --git a/spec/graphql/types/detployment_tag_type_spec.rb b/spec/graphql/types/detployment_tag_type_spec.rb
new file mode 100644
index 00000000000..9a7a8db0970
--- /dev/null
+++ b/spec/graphql/types/detployment_tag_type_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['DeploymentTag'] do
+ specify { expect(described_class.graphql_name).to eq('DeploymentTag') }
+
+ it 'has the expected fields' do
+ expected_fields = %w[
+ name path
+ ]
+
+ expect(described_class).to have_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/graphql/types/environment_type_spec.rb b/spec/graphql/types/environment_type_spec.rb
index 3671d35e8a5..ae58fe00af7 100644
--- a/spec/graphql/types/environment_type_spec.rb
+++ b/spec/graphql/types/environment_type_spec.rb
@@ -7,7 +7,8 @@ RSpec.describe GitlabSchema.types['Environment'] do
it 'has the expected fields' do
expected_fields = %w[
- name id state metrics_dashboard latest_opened_most_severe_alert path
+ name id state metrics_dashboard latest_opened_most_severe_alert path external_url deployments
+ slug createdAt updatedAt autoStopAt autoDeleteAt tier environmentType lastDeployment
]
expect(described_class).to have_graphql_fields(*expected_fields)
@@ -17,7 +18,7 @@ RSpec.describe GitlabSchema.types['Environment'] do
context 'when there is an environment' do
let_it_be(:project) { create(:project) }
- let_it_be(:environment) { create(:environment, project: project) }
+ let_it_be(:environment) { create(:environment, project: project, external_url: 'https://gitlab.com') }
let_it_be(:user) { create(:user) }
subject { GitlabSchema.execute(query, context: { current_user: user }).as_json }
@@ -29,6 +30,7 @@ RSpec.describe GitlabSchema.types['Environment'] do
environment(name: "#{environment.name}") {
name
path
+ externalUrl
state
}
}
@@ -50,6 +52,10 @@ RSpec.describe GitlabSchema.types['Environment'] do
)
end
+ it 'returns the external url of the environment' do
+ expect(subject['data']['project']['environment']['externalUrl']).to eq(environment.external_url)
+ end
+
context 'when query alert data for the environment' do
let_it_be(:query) do
%(
diff --git a/spec/graphql/types/group_type_spec.rb b/spec/graphql/types/group_type_spec.rb
index 72b3bb90194..0b65778ce90 100644
--- a/spec/graphql/types/group_type_spec.rb
+++ b/spec/graphql/types/group_type_spec.rb
@@ -24,8 +24,9 @@ RSpec.describe GitlabSchema.types['Group'] do
dependency_proxy_blobs dependency_proxy_image_count
dependency_proxy_blob_count dependency_proxy_total_size
dependency_proxy_image_prefix dependency_proxy_image_ttl_policy
- shared_runners_setting timelogs organizations contacts contact_state_counts
- work_item_types recent_issue_boards ci_variables
+ shared_runners_setting timelogs organization_state_counts organizations
+ contact_state_counts contacts work_item_types
+ recent_issue_boards ci_variables
]
expect(described_class).to include_graphql_fields(*expected_fields)
@@ -62,6 +63,13 @@ RSpec.describe GitlabSchema.types['Group'] do
it { is_expected.to have_graphql_resolver(Resolvers::Crm::ContactStateCountsResolver) }
end
+ describe 'organization_state_counts field' do
+ subject { described_class.fields['organizationStateCounts'] }
+
+ it { is_expected.to have_graphql_type(Types::CustomerRelations::OrganizationStateCountsType) }
+ it { is_expected.to have_graphql_resolver(Resolvers::Crm::OrganizationStateCountsResolver) }
+ end
+
it_behaves_like 'a GraphQL type with labels' do
let(:labels_resolver_arguments) { [:search_term, :includeAncestorGroups, :includeDescendantGroups, :onlyGroupLabels] }
end
diff --git a/spec/graphql/types/merge_request_review_state_enum_spec.rb b/spec/graphql/types/merge_request_review_state_enum_spec.rb
index 407a1ae3c1f..486e1c4f502 100644
--- a/spec/graphql/types/merge_request_review_state_enum_spec.rb
+++ b/spec/graphql/types/merge_request_review_state_enum_spec.rb
@@ -12,10 +12,6 @@ RSpec.describe GitlabSchema.types['MergeRequestReviewState'] do
'UNREVIEWED' => have_attributes(
description: 'The merge request is unreviewed.',
value: 'unreviewed'
- ),
- 'ATTENTION_REQUESTED' => have_attributes(
- description: 'The merge request is attention_requested.',
- value: 'attention_requested'
)
)
end
diff --git a/spec/graphql/types/metrics/dashboard_type_spec.rb b/spec/graphql/types/metrics/dashboard_type_spec.rb
index 30dccc7c0be..114db87d5f1 100644
--- a/spec/graphql/types/metrics/dashboard_type_spec.rb
+++ b/spec/graphql/types/metrics/dashboard_type_spec.rb
@@ -7,8 +7,8 @@ RSpec.describe GitlabSchema.types['MetricsDashboard'] do
it 'has the expected fields' do
expected_fields = %w[
- path annotations schema_validation_warnings
- ]
+ path annotations schema_validation_warnings
+ ]
expect(described_class).to have_graphql_fields(*expected_fields)
end
diff --git a/spec/graphql/types/packages/composer/metadatum_type_spec.rb b/spec/graphql/types/packages/composer/metadatum_type_spec.rb
index a950c10a41d..272d518cdd5 100644
--- a/spec/graphql/types/packages/composer/metadatum_type_spec.rb
+++ b/spec/graphql/types/packages/composer/metadatum_type_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['ComposerMetadata'] do
it 'includes composer metadatum fields' do
expected_fields = %w[
- target_sha composer_json
+ target_sha composer_json
]
expect(described_class).to include_graphql_fields(*expected_fields)
diff --git a/spec/graphql/types/packages/package_type_enum_spec.rb b/spec/graphql/types/packages/package_type_enum_spec.rb
index 9d5a7716a61..fb93b1c8c8a 100644
--- a/spec/graphql/types/packages/package_type_enum_spec.rb
+++ b/spec/graphql/types/packages/package_type_enum_spec.rb
@@ -4,6 +4,6 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['PackageTypeEnum'] do
it 'exposes all package types' do
- expect(described_class.values.keys).to contain_exactly(*%w[MAVEN NPM CONAN NUGET PYPI COMPOSER GENERIC GOLANG DEBIAN RUBYGEMS HELM TERRAFORM_MODULE])
+ expect(described_class.values.keys).to contain_exactly(*%w[MAVEN NPM CONAN NUGET PYPI COMPOSER GENERIC GOLANG DEBIAN RUBYGEMS HELM TERRAFORM_MODULE RPM])
end
end
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index 5ff7653ce39..617cbdb07fe 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -4,7 +4,6 @@ require 'spec_helper'
RSpec.describe GitlabSchema.types['Project'] do
include GraphqlHelpers
- include Ci::TemplateHelpers
specify { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Project) }
@@ -37,7 +36,7 @@ RSpec.describe GitlabSchema.types['Project'] do
cluster_agent cluster_agents agent_configurations
ci_template timelogs merge_commit_template squash_commit_template work_item_types
recent_issue_boards ci_config_path_or_default packages_cleanup_policy ci_variables
- timelog_categories fork_targets
+ timelog_categories fork_targets branch_rules ci_config_variables
]
expect(described_class).to include_graphql_fields(*expected_fields)
@@ -509,6 +508,20 @@ RSpec.describe GitlabSchema.types['Project'] do
it { is_expected.to have_graphql_resolver(Resolvers::Ci::JobTokenScopeResolver) }
end
+ describe 'branch_rules field' do
+ subject { described_class.fields['branchRules'] }
+
+ let(:br_resolver) { Resolvers::Projects::BranchRulesResolver }
+
+ specify do
+ is_expected.to have_graphql_type(
+ Types::Projects::BranchRuleType.connection_type
+ )
+ end
+
+ specify { is_expected.to have_graphql_resolver(br_resolver) }
+ end
+
describe 'agent_configurations' do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
@@ -682,4 +695,54 @@ RSpec.describe GitlabSchema.types['Project'] do
end
end
end
+
+ describe 'branch_rules' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project, reload: true) { create(:project, :public) }
+ let_it_be(:name) { 'feat/*' }
+ let_it_be(:protected_branch) do
+ create(:protected_branch, project: project, name: name)
+ end
+
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ branchRules {
+ nodes {
+ name
+ }
+ }
+ }
+ }
+ )
+ end
+
+ let(:branch_rules_data) do
+ subject.dig('data', 'project', 'branchRules', 'nodes')
+ end
+
+ subject { GitlabSchema.execute(query, context: { current_user: user }).as_json }
+
+ context 'when a user can read protected branches' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ it 'is present and correct' do
+ expect(branch_rules_data.count).to eq(1)
+ expect(branch_rules_data.first['name']).to eq(name)
+ end
+ end
+
+ context 'when a user cannot read protected branches' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'is empty' do
+ expect(branch_rules_data.count).to eq(0)
+ end
+ end
+ end
end
diff --git a/spec/graphql/types/subscription_type_spec.rb b/spec/graphql/types/subscription_type_spec.rb
index 9b043fa52cf..860cbbf0c15 100644
--- a/spec/graphql/types/subscription_type_spec.rb
+++ b/spec/graphql/types/subscription_type_spec.rb
@@ -10,8 +10,9 @@ RSpec.describe GitlabSchema.types['Subscription'] do
issuable_title_updated
issuable_labels_updated
issuable_dates_updated
+ merge_request_reviewers_updated
]
- expect(described_class).to have_graphql_fields(*expected_fields).only
+ expect(described_class).to include_graphql_fields(*expected_fields)
end
end
diff --git a/spec/graphql/types/timelog_type_spec.rb b/spec/graphql/types/timelog_type_spec.rb
index c897a25d10d..3a26ba89e04 100644
--- a/spec/graphql/types/timelog_type_spec.rb
+++ b/spec/graphql/types/timelog_type_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe GitlabSchema.types['Timelog'] do
it { expect(described_class.graphql_name).to eq('Timelog') }
it { expect(described_class).to have_graphql_fields(fields) }
- it { expect(described_class).to require_graphql_authorizations(:read_issue) }
+ it { expect(described_class).to require_graphql_authorizations(:read_issuable) }
it { expect(described_class).to expose_permissions_using(Types::PermissionTypes::Timelog) }
describe 'user field' do
diff --git a/spec/graphql/types/user_merge_request_interaction_type_spec.rb b/spec/graphql/types/user_merge_request_interaction_type_spec.rb
index 4782a1faf8d..3cd9750debb 100644
--- a/spec/graphql/types/user_merge_request_interaction_type_spec.rb
+++ b/spec/graphql/types/user_merge_request_interaction_type_spec.rb
@@ -76,11 +76,8 @@ RSpec.describe GitlabSchema.types['UserMergeRequestInteraction'] do
context 'when the user has been asked to review the MR' do
before do
merge_request.reviewers << user
- merge_request.find_reviewer(user).update!(state: :attention_requested)
end
- it { is_expected.to eq(Types::MergeRequestReviewStateEnum.values['ATTENTION_REQUESTED'].value) }
-
it 'implies not reviewed' do
expect(resolve(:reviewed)).to be false
end
diff --git a/spec/graphql/types/work_item_type_spec.rb b/spec/graphql/types/work_item_type_spec.rb
index c556424b0b4..11b02a88dbd 100644
--- a/spec/graphql/types/work_item_type_spec.rb
+++ b/spec/graphql/types/work_item_type_spec.rb
@@ -28,8 +28,6 @@ RSpec.describe GitlabSchema.types['WorkItem'] do
closed_at
]
- fields.each do |field_name|
- expect(described_class).to have_graphql_fields(*fields)
- end
+ expect(described_class).to have_graphql_fields(*fields)
end
end
diff --git a/spec/graphql/types/work_items/widgets/description_type_spec.rb b/spec/graphql/types/work_items/widgets/description_type_spec.rb
index 5ade1fe4aa2..aee388ce82a 100644
--- a/spec/graphql/types/work_items/widgets/description_type_spec.rb
+++ b/spec/graphql/types/work_items/widgets/description_type_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Types::WorkItems::Widgets::DescriptionType do
it 'exposes the expected fields' do
- expected_fields = %i[description description_html type]
+ expected_fields = %i[description description_html edited last_edited_at last_edited_by type]
expect(described_class).to have_graphql_fields(*expected_fields)
end
diff --git a/spec/helpers/application_settings_helper_spec.rb b/spec/helpers/application_settings_helper_spec.rb
index 0304aac18ae..1703727db21 100644
--- a/spec/helpers/application_settings_helper_spec.rb
+++ b/spec/helpers/application_settings_helper_spec.rb
@@ -46,6 +46,10 @@ RSpec.describe ApplicationSettingsHelper do
expect(helper.visible_attributes).to include(:deactivate_dormant_users)
end
+ it 'contains :deactivate_dormant_users_period' do
+ expect(helper.visible_attributes).to include(:deactivate_dormant_users_period)
+ end
+
it 'contains rate limit parameters' do
expect(helper.visible_attributes).to include(*%i(
issues_create_limit notes_create_limit project_export_limit
@@ -63,6 +67,10 @@ RSpec.describe ApplicationSettingsHelper do
it 'does not contain :deactivate_dormant_users' do
expect(helper.visible_attributes).not_to include(:deactivate_dormant_users)
end
+
+ it 'does not contain :deactivate_dormant_users_period' do
+ expect(helper.visible_attributes).not_to include(:deactivate_dormant_users_period)
+ end
end
end
@@ -289,6 +297,66 @@ RSpec.describe ApplicationSettingsHelper do
end
end
+ describe '.spam_check_endpoint_enabled?' do
+ subject { helper.spam_check_endpoint_enabled? }
+
+ context 'when spam check endpoint is enabled' do
+ before do
+ stub_application_setting(spam_check_endpoint_enabled: true)
+ end
+
+ it { is_expected.to be true }
+ end
+
+ context 'when spam check endpoint is disabled' do
+ before do
+ stub_application_setting(spam_check_endpoint_enabled: false)
+ end
+
+ it { is_expected.to be false }
+ end
+ end
+
+ describe '.anti_spam_service_enabled?' do
+ subject { helper.anti_spam_service_enabled? }
+
+ context 'when akismet is enabled and spam check endpoint is disabled' do
+ before do
+ stub_application_setting(spam_check_endpoint_enabled: false)
+ stub_application_setting(akismet_enabled: true)
+ end
+
+ it { is_expected.to be true }
+ end
+
+ context 'when akismet is disabled and spam check endpoint is enabled' do
+ before do
+ stub_application_setting(spam_check_endpoint_enabled: true)
+ stub_application_setting(akismet_enabled: false)
+ end
+
+ it { is_expected.to be true }
+ end
+
+ context 'when akismet and spam check endpoint are both enabled' do
+ before do
+ stub_application_setting(spam_check_endpoint_enabled: true)
+ stub_application_setting(akismet_enabled: true)
+ end
+
+ it { is_expected.to be true }
+ end
+
+ context 'when akismet and spam check endpoint are both disabled' do
+ before do
+ stub_application_setting(spam_check_endpoint_enabled: false)
+ stub_application_setting(akismet_enabled: false)
+ end
+
+ it { is_expected.to be false }
+ end
+ end
+
describe '#sidekiq_job_limiter_modes_for_select' do
subject { helper.sidekiq_job_limiter_modes_for_select }
@@ -305,7 +373,7 @@ RSpec.describe ApplicationSettingsHelper do
allow(helper).to receive(:can?).with(user, :read_cluster, instance_of(Clusters::Instance)).and_return(true)
end
- it { is_expected.to be_truthy}
+ it { is_expected.to be_truthy }
context ':certificate_based_clusters feature flag is disabled' do
before do
diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb
index 65e46b61882..fe652e905cc 100644
--- a/spec/helpers/blob_helper_spec.rb
+++ b/spec/helpers/blob_helper_spec.rb
@@ -357,7 +357,7 @@ RSpec.describe BlobHelper do
describe '#ide_merge_request_path' do
let_it_be(:project) { create(:project, :repository) }
- let_it_be(:merge_request) { create(:merge_request, source_project: project)}
+ let_it_be(:merge_request) { create(:merge_request, source_project: project) }
it 'returns IDE path for the given MR if MR is not merged' do
expect(helper.ide_merge_request_path(merge_request)).to eq("/-/ide/project/#{project.full_path}/merge_requests/#{merge_request.iid}")
diff --git a/spec/helpers/ci/builds_helper_spec.rb b/spec/helpers/ci/builds_helper_spec.rb
index ea3b5aac4ea..c215d7b4a78 100644
--- a/spec/helpers/ci/builds_helper_spec.rb
+++ b/spec/helpers/ci/builds_helper_spec.rb
@@ -80,7 +80,7 @@ RSpec.describe Ci::BuildsHelper do
expect(subject).to eq({
page_path: project_job_path(project, ci_build),
build_status: ci_build.status,
- build_stage: ci_build.stage,
+ build_stage: ci_build.stage_name,
log_state: ''
})
end
@@ -106,7 +106,7 @@ RSpec.describe Ci::BuildsHelper do
expect(subject).to eq([{
id: failed_build.id,
failure: failed_build.present.callout_failure_message,
- failure_summary: helper.build_summary(failed_build)
+ failure_summary: helper.build_summary(failed_build)
}].to_json)
end
end
diff --git a/spec/helpers/ci/runners_helper_spec.rb b/spec/helpers/ci/runners_helper_spec.rb
index 3b18572ad64..1b1edde8faf 100644
--- a/spec/helpers/ci/runners_helper_spec.rb
+++ b/spec/helpers/ci/runners_helper_spec.rb
@@ -131,17 +131,32 @@ RSpec.describe Ci::RunnersHelper do
describe '#group_runners_data_attributes' do
let(:group) { create(:group) }
- it 'returns group data to render a runner list' do
- expect(helper.group_runners_data_attributes(group)).to include(
- registration_token: group.runners_token,
- group_id: group.id,
- group_full_path: group.full_path,
- runner_install_help_page: 'https://docs.gitlab.com/runner/install/',
- online_contact_timeout_secs: 7200,
- stale_timeout_secs: 7889238,
- empty_state_svg_path: start_with('/assets/illustrations/pipelines_empty'),
- empty_state_filtered_svg_path: start_with('/assets/illustrations/magnifying-glass')
- )
+ context 'when user can register group runners' do
+ before do
+ allow(helper).to receive(:can?).with(user, :register_group_runners, group).and_return(true)
+ end
+
+ it 'returns group data to render a runner list' do
+ expect(helper.group_runners_data_attributes(group)).to include(
+ group_id: group.id,
+ group_full_path: group.full_path,
+ runner_install_help_page: 'https://docs.gitlab.com/runner/install/',
+ online_contact_timeout_secs: 7200,
+ stale_timeout_secs: 7889238,
+ empty_state_svg_path: start_with('/assets/illustrations/pipelines_empty'),
+ empty_state_filtered_svg_path: start_with('/assets/illustrations/magnifying-glass')
+ )
+ end
+ end
+
+ context 'when user cannot register group runners' do
+ before do
+ allow(helper).to receive(:can?).with(user, :register_group_runners, group).and_return(false)
+ end
+
+ it 'returns empty registration token' do
+ expect(helper.group_runners_data_attributes(group)).not_to include(registration_token: group.runners_token)
+ end
end
end
diff --git a/spec/helpers/commits_helper_spec.rb b/spec/helpers/commits_helper_spec.rb
index 010100769d4..0cc53da98b2 100644
--- a/spec/helpers/commits_helper_spec.rb
+++ b/spec/helpers/commits_helper_spec.rb
@@ -329,7 +329,7 @@ RSpec.describe CommitsHelper do
it { is_expected.to include(commit.author) }
it { is_expected.to include(ref) }
- it do
+ specify do
is_expected.to include(
{
merge_request: merge_request.cache_key,
diff --git a/spec/helpers/gitlab_script_tag_helper_spec.rb b/spec/helpers/gitlab_script_tag_helper_spec.rb
index 9d71e25286e..cfe7b349cec 100644
--- a/spec/helpers/gitlab_script_tag_helper_spec.rb
+++ b/spec/helpers/gitlab_script_tag_helper_spec.rb
@@ -27,8 +27,8 @@ RSpec.describe GitlabScriptTagHelper do
end
describe 'inline script tag' do
- let(:tag_with_nonce) {"<script nonce=\"noncevalue\">\n//<![CDATA[\nalert(1)\n//]]>\n</script>"}
- let(:tag_with_nonce_and_type) {"<script type=\"application/javascript\" nonce=\"noncevalue\">\n//<![CDATA[\nalert(1)\n//]]>\n</script>"}
+ let(:tag_with_nonce) { "<script nonce=\"noncevalue\">\n//<![CDATA[\nalert(1)\n//]]>\n</script>" }
+ let(:tag_with_nonce_and_type) { "<script type=\"application/javascript\" nonce=\"noncevalue\">\n//<![CDATA[\nalert(1)\n//]]>\n</script>" }
it 'returns a script tag with a nonce using block syntax' do
expect(helper.javascript_tag { 'alert(1)' }.to_s).to eq tag_with_nonce
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index 2c1061d2f1b..00e620832b3 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -520,6 +520,29 @@ RSpec.describe GroupsHelper do
end
end
+ describe '#group_overview_tabs_app_data' do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:user) { create(:user) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+
+ allow(helper).to receive(:can?).with(user, :create_subgroup, group) { true }
+ allow(helper).to receive(:can?).with(user, :create_projects, group) { true }
+ end
+
+ it 'returns expected hash' do
+ expect(helper.group_overview_tabs_app_data(group)).to match(
+ {
+ subgroups_and_projects_endpoint: including("/groups/#{group.path}/-/children.json"),
+ shared_projects_endpoint: including("/groups/#{group.path}/-/shared_projects.json"),
+ archived_projects_endpoint: including("/groups/#{group.path}/-/children.json?archived=only"),
+ current_group_visibility: group.visibility
+ }.merge(helper.group_overview_tabs_app_data(group))
+ )
+ end
+ end
+
describe "#enabled_git_access_protocol_options_for_group" do
subject { helper.enabled_git_access_protocol_options_for_group }
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index 069465c2fec..18a21b59409 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -448,7 +448,7 @@ RSpec.describe IssuablesHelper do
allow(merge_request).to receive(:can_be_merged_by?).and_return(can_merge)
end
- it { is_expected.to include({ can_merge: can_merge })}
+ it { is_expected.to include({ can_merge: can_merge }) }
end
end
end
@@ -480,7 +480,7 @@ RSpec.describe IssuablesHelper do
allow(merge_request).to receive(:can_be_merged_by?).and_return(can_merge)
end
- it { is_expected.to include({ can_merge: can_merge })}
+ it { is_expected.to include({ can_merge: can_merge }) }
end
end
end
diff --git a/spec/helpers/jira_connect_helper_spec.rb b/spec/helpers/jira_connect_helper_spec.rb
index 4d2fc3d9ee6..97e37023c2d 100644
--- a/spec/helpers/jira_connect_helper_spec.rb
+++ b/spec/helpers/jira_connect_helper_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe JiraConnectHelper do
describe '#jira_connect_app_data' do
+ let_it_be(:installation) { create(:jira_connect_installation) }
let_it_be(:subscription) { create(:jira_connect_subscription) }
let(:user) { create(:user) }
@@ -13,11 +14,12 @@ RSpec.describe JiraConnectHelper do
stub_application_setting(jira_connect_application_key: client_id)
end
- subject { helper.jira_connect_app_data([subscription]) }
+ subject { helper.jira_connect_app_data([subscription], installation) }
context 'user is not logged in' do
before do
allow(view).to receive(:current_user).and_return(nil)
+ allow(Gitlab).to receive_message_chain('config.gitlab.url') { 'http://test.host' }
end
it 'includes Jira Connect app attributes' do
@@ -36,14 +38,14 @@ RSpec.describe JiraConnectHelper do
end
context 'with oauth_metadata' do
- let(:oauth_metadata) { helper.jira_connect_app_data([subscription])[:oauth_metadata] }
+ let(:oauth_metadata) { helper.jira_connect_app_data([subscription], installation)[:oauth_metadata] }
subject(:parsed_oauth_metadata) { Gitlab::Json.parse(oauth_metadata).deep_symbolize_keys }
it 'assigns oauth_metadata' do
expect(parsed_oauth_metadata).to include(
oauth_authorize_url: start_with('http://test.host/oauth/authorize?'),
- oauth_token_url: 'http://test.host/oauth/token',
+ oauth_token_path: '/oauth/token',
state: %r/[a-z0-9.]{32}/,
oauth_token_payload: hash_including(
grant_type: 'authorization_code',
@@ -74,6 +76,30 @@ RSpec.describe JiraConnectHelper do
expect(oauth_metadata).to be_nil
end
end
+
+ context 'with self-managed instance' do
+ let_it_be(:installation) { create(:jira_connect_installation, instance_url: 'https://gitlab.example.com') }
+
+ it 'points urls to the self-managed instance' do
+ expect(parsed_oauth_metadata).to include(
+ oauth_authorize_url: start_with('https://gitlab.example.com/oauth/authorize?'),
+ oauth_token_path: '/oauth/token'
+ )
+ end
+
+ context 'and jira_connect_oauth_self_managed feature is disabled' do
+ before do
+ stub_feature_flags(jira_connect_oauth_self_managed: false)
+ end
+
+ it 'does not point urls to the self-managed instance' do
+ expect(parsed_oauth_metadata).not_to include(
+ oauth_authorize_url: start_with('https://gitlab.example.com/oauth/authorize?'),
+ oauth_token_path: 'https://gitlab.example.com/oauth/token'
+ )
+ end
+ end
+ end
end
it 'passes group as "skip_groups" param' do
diff --git a/spec/helpers/learn_gitlab_helper_spec.rb b/spec/helpers/learn_gitlab_helper_spec.rb
index 7c9dfd6b5be..0d4f1965d92 100644
--- a/spec/helpers/learn_gitlab_helper_spec.rb
+++ b/spec/helpers/learn_gitlab_helper_spec.rb
@@ -7,16 +7,16 @@ RSpec.describe LearnGitlabHelper do
include Devise::Test::ControllerHelpers
let_it_be(:user) { create(:user) }
- let_it_be(:project) { create(:project, name: LearnGitlab::Project::PROJECT_NAME, namespace: user.namespace) }
+ let_it_be(:project) { create(:project, name: Onboarding::LearnGitlab::PROJECT_NAME, namespace: user.namespace) }
let_it_be(:namespace) { project.namespace }
before do
- allow_next_instance_of(LearnGitlab::Project) do |learn_gitlab|
+ allow_next_instance_of(Onboarding::LearnGitlab) do |learn_gitlab|
allow(learn_gitlab).to receive(:project).and_return(project)
end
- OnboardingProgress.onboard(namespace)
- OnboardingProgress.register(namespace, :git_write)
+ Onboarding::Progress.onboard(namespace)
+ Onboarding::Progress.register(namespace, :git_write)
end
describe '#learn_gitlab_enabled?' do
@@ -37,8 +37,8 @@ RSpec.describe LearnGitlabHelper do
with_them do
before do
- allow(OnboardingProgress).to receive(:onboarding?).with(project.namespace).and_return(onboarding)
- allow_next(LearnGitlab::Project, user).to receive(:available?).and_return(learn_gitlab_available)
+ allow(Onboarding::Progress).to receive(:onboarding?).with(project.namespace).and_return(onboarding)
+ allow_next(Onboarding::LearnGitlab, user).to receive(:available?).and_return(learn_gitlab_available)
end
context 'when signed in' do
@@ -81,7 +81,7 @@ RSpec.describe LearnGitlabHelper do
it 'has all section data', :aggregate_failures do
expect(onboarding_sections_data.keys).to contain_exactly(:deploy, :plan, :workspace)
- expect(onboarding_sections_data.values.map { |section| section.keys }).to match_array([[:svg]] * 3)
+ expect(onboarding_sections_data.values.map(&:keys)).to match_array([[:svg]] * 3)
end
it 'has all project data', :aggregate_failures do
diff --git a/spec/helpers/members_helper_spec.rb b/spec/helpers/members_helper_spec.rb
index 4a3a623ce77..005fce1730f 100644
--- a/spec/helpers/members_helper_spec.rb
+++ b/spec/helpers/members_helper_spec.rb
@@ -6,12 +6,12 @@ RSpec.describe MembersHelper do
describe '#remove_member_message' do
let(:requester) { create(:user) }
let(:project) { create(:project, :public) }
- let(:project_member) { build(:project_member, project: project) }
- let(:project_member_invite) { build(:project_member, project: project).tap { |m| m.generate_invite_token! } }
+ let(:project_member) { create(:project_member, project: project) }
+ let(:project_member_invite) { create(:project_member, project: project).tap { |m| m.generate_invite_token! } }
let(:project_member_request) { project.request_access(requester) }
let(:group) { create(:group) }
- let(:group_member) { build(:group_member, group: group) }
- let(:group_member_invite) { build(:group_member, group: group).tap { |m| m.generate_invite_token! } }
+ let(:group_member) { create(:group_member, group: group) }
+ let(:group_member_invite) { create(:group_member, group: group).tap { |m| m.generate_invite_token! } }
let(:group_member_request) { group.request_access(requester) }
it { expect(remove_member_message(project_member)).to eq "Are you sure you want to remove #{project_member.user.name} from the #{project.full_name} project?" }
diff --git a/spec/helpers/nav/new_dropdown_helper_spec.rb b/spec/helpers/nav/new_dropdown_helper_spec.rb
index 45664a7e0bd..3a65131aab0 100644
--- a/spec/helpers/nav/new_dropdown_helper_spec.rb
+++ b/spec/helpers/nav/new_dropdown_helper_spec.rb
@@ -100,7 +100,7 @@ RSpec.describe Nav::NewDropdownHelper do
id: 'general_new_group',
title: 'New group',
href: '/groups/new',
- data: { track_action: 'click_link_new_group', track_label: 'plus_menu_dropdown' }
+ data: { qa_selector: 'global_new_group_link', track_action: 'click_link_new_group', track_label: 'plus_menu_dropdown' }
)
)
)
diff --git a/spec/helpers/nav/top_nav_helper_spec.rb b/spec/helpers/nav/top_nav_helper_spec.rb
index e4fa503b5ee..9c396d6bf25 100644
--- a/spec/helpers/nav/top_nav_helper_spec.rb
+++ b/spec/helpers/nav/top_nav_helper_spec.rb
@@ -27,9 +27,11 @@ RSpec.describe Nav::TopNavHelper do
let(:subject) { helper.top_nav_view_model(project: current_project, group: current_group) }
- let(:active_title) { 'Menu' }
+ let(:menu_title) { 'Menu' }
before do
+ stub_feature_flags(new_navbar_layout: false)
+
allow(Gitlab::CurrentSettings).to receive(:admin_mode) { with_current_settings_admin_mode }
allow(helper).to receive(:header_link?).with(:admin_mode) { with_header_link_admin_mode }
@@ -44,12 +46,15 @@ RSpec.describe Nav::TopNavHelper do
allow(helper).to receive(:dashboard_nav_link?).with(:activity) { with_activity }
end
- it 'has :activeTitle' do
- expect(subject[:activeTitle]).to eq(active_title)
+ it 'has :menuTitle' do
+ expect(subject[:menuTitle]).to eq(menu_title)
end
context 'when current_user is nil (anonymous)' do
it 'has expected :primary' do
+ expected_header = ::Gitlab::Nav::TopNavMenuHeader.build(
+ title: 'Explore'
+ )
expected_primary = [
{ href: '/explore', icon: 'project', id: 'project', title: 'Projects' },
{ href: '/explore/groups', icon: 'group', id: 'groups', title: 'Groups' },
@@ -58,7 +63,7 @@ RSpec.describe Nav::TopNavHelper do
::Gitlab::Nav::TopNavMenuItem.build(**item)
end
- expect(subject[:primary]).to eq(expected_primary)
+ expect(subject[:primary]).to eq([expected_header, *expected_primary])
end
it 'has expected :shortcuts' do
@@ -103,7 +108,7 @@ RSpec.describe Nav::TopNavHelper do
let(:current_user) { user }
it 'has no menu items or views by default' do
- expect(subject).to eq({ activeTitle: active_title,
+ expect(subject).to eq({ menuTitle: menu_title,
primary: [],
secondary: [],
shortcuts: [],
@@ -115,6 +120,9 @@ RSpec.describe Nav::TopNavHelper do
let(:projects_view) { subject[:views][:projects] }
it 'has expected :primary' do
+ expected_header = ::Gitlab::Nav::TopNavMenuHeader.build(
+ title: 'Switch to'
+ )
expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
css_class: 'qa-projects-dropdown',
data: {
@@ -126,7 +134,7 @@ RSpec.describe Nav::TopNavHelper do
title: 'Projects',
view: 'projects'
)
- expect(subject[:primary]).to eq([expected_primary])
+ expect(subject[:primary]).to eq([expected_header, expected_primary])
end
it 'has expected :shortcuts' do
@@ -153,61 +161,87 @@ RSpec.describe Nav::TopNavHelper do
::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
- qa_title: 'Your projects',
- **menu_data_tracking_attrs('your_projects')
+ qa_title: 'View all projects',
+ **menu_data_tracking_attrs('view_all_projects')
},
href: '/dashboard/projects',
id: 'your',
- title: 'Your projects'
- ),
- ::Gitlab::Nav::TopNavMenuItem.build(
- data: {
- qa_selector: 'menu_item_link',
- qa_title: 'Starred projects',
- **menu_data_tracking_attrs('starred_projects')
- },
- href: '/dashboard/projects/starred',
- id: 'starred',
- title: 'Starred projects'
- ),
- ::Gitlab::Nav::TopNavMenuItem.build(
- data: {
- qa_selector: 'menu_item_link',
- qa_title: 'Explore projects',
- **menu_data_tracking_attrs('explore_projects')
- },
- href: '/explore',
- id: 'explore',
- title: 'Explore projects'
- ),
- ::Gitlab::Nav::TopNavMenuItem.build(
- data: {
- qa_selector: 'menu_item_link',
- qa_title: 'Explore topics',
- **menu_data_tracking_attrs('explore_topics')
- },
- href: '/explore/projects/topics',
- id: 'topics',
- title: 'Explore topics'
+ title: 'View all projects'
)
]
expect(projects_view[:linksPrimary]).to eq(expected_links_primary)
end
- it 'has expected :linksSecondary' do
- expected_links_secondary = [
- ::Gitlab::Nav::TopNavMenuItem.build(
- data: {
- qa_selector: 'menu_item_link',
- qa_title: 'Create new project',
- **menu_data_tracking_attrs('create_new_project')
- },
- href: '/projects/new',
- id: 'create',
- title: 'Create new project'
- )
- ]
- expect(projects_view[:linksSecondary]).to eq(expected_links_secondary)
+ it 'does not have any :linksSecondary' do
+ expect(projects_view[:linksSecondary]).to eq([])
+ end
+
+ context 'when extra submenu options are not hidden' do
+ before do
+ stub_feature_flags(remove_extra_primary_submenu_options: false)
+ end
+
+ it 'has expected :linksPrimary' do
+ expected_links_primary = [
+ ::Gitlab::Nav::TopNavMenuItem.build(
+ data: {
+ qa_selector: 'menu_item_link',
+ qa_title: 'Your projects',
+ **menu_data_tracking_attrs('your_projects')
+ },
+ href: '/dashboard/projects',
+ id: 'your',
+ title: 'Your projects'
+ ),
+ ::Gitlab::Nav::TopNavMenuItem.build(
+ data: {
+ qa_selector: 'menu_item_link',
+ qa_title: 'Starred projects',
+ **menu_data_tracking_attrs('starred_projects')
+ },
+ href: '/dashboard/projects/starred',
+ id: 'starred',
+ title: 'Starred projects'
+ ),
+ ::Gitlab::Nav::TopNavMenuItem.build(
+ data: {
+ qa_selector: 'menu_item_link',
+ qa_title: 'Explore projects',
+ **menu_data_tracking_attrs('explore_projects')
+ },
+ href: '/explore',
+ id: 'explore',
+ title: 'Explore projects'
+ ),
+ ::Gitlab::Nav::TopNavMenuItem.build(
+ data: {
+ qa_selector: 'menu_item_link',
+ qa_title: 'Explore topics',
+ **menu_data_tracking_attrs('explore_topics')
+ },
+ href: '/explore/projects/topics',
+ id: 'topics',
+ title: 'Explore topics'
+ )
+ ]
+ expect(projects_view[:linksPrimary]).to eq(expected_links_primary)
+ end
+
+ it 'has expected :linksSecondary' do
+ expected_links_secondary = [
+ ::Gitlab::Nav::TopNavMenuItem.build(
+ data: {
+ qa_selector: 'menu_item_link',
+ qa_title: 'Create new project',
+ **menu_data_tracking_attrs('create_new_project')
+ },
+ href: '/projects/new',
+ id: 'create',
+ title: 'Create new project'
+ )
+ ]
+ expect(projects_view[:linksSecondary]).to eq(expected_links_secondary)
+ end
end
context 'with current nav as project' do
@@ -251,6 +285,9 @@ RSpec.describe Nav::TopNavHelper do
let(:groups_view) { subject[:views][:groups] }
it 'has expected :primary' do
+ expected_header = ::Gitlab::Nav::TopNavMenuHeader.build(
+ title: 'Switch to'
+ )
expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
css_class: 'qa-groups-dropdown',
data: {
@@ -262,7 +299,7 @@ RSpec.describe Nav::TopNavHelper do
title: 'Groups',
view: 'groups'
)
- expect(subject[:primary]).to eq([expected_primary])
+ expect(subject[:primary]).to eq([expected_header, expected_primary])
end
it 'has expected :shortcuts' do
@@ -289,41 +326,67 @@ RSpec.describe Nav::TopNavHelper do
::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'menu_item_link',
- qa_title: 'Your groups',
- **menu_data_tracking_attrs('your_groups')
+ qa_title: 'View all groups',
+ **menu_data_tracking_attrs('view_all_groups')
},
href: '/dashboard/groups',
id: 'your',
- title: 'Your groups'
- ),
- ::Gitlab::Nav::TopNavMenuItem.build(
- data: {
- qa_selector: 'menu_item_link',
- qa_title: 'Explore groups',
- **menu_data_tracking_attrs('explore_groups')
- },
- href: '/explore/groups',
- id: 'explore',
- title: 'Explore groups'
+ title: 'View all groups'
)
]
expect(groups_view[:linksPrimary]).to eq(expected_links_primary)
end
- it 'has expected :linksSecondary' do
- expected_links_secondary = [
- ::Gitlab::Nav::TopNavMenuItem.build(
- data: {
- qa_selector: 'menu_item_link',
- qa_title: 'Create group',
- **menu_data_tracking_attrs('create_group')
- },
- href: '/groups/new',
- id: 'create',
- title: 'Create group'
- )
- ]
- expect(groups_view[:linksSecondary]).to eq(expected_links_secondary)
+ it 'does not have any :linksSecondary' do
+ expect(groups_view[:linksSecondary]).to eq([])
+ end
+
+ context 'when extra submenu options are not hidden' do
+ before do
+ stub_feature_flags(remove_extra_primary_submenu_options: false)
+ end
+
+ it 'has expected :linksPrimary' do
+ expected_links_primary = [
+ ::Gitlab::Nav::TopNavMenuItem.build(
+ data: {
+ qa_selector: 'menu_item_link',
+ qa_title: 'Your groups',
+ **menu_data_tracking_attrs('your_groups')
+ },
+ href: '/dashboard/groups',
+ id: 'your',
+ title: 'Your groups'
+ ),
+ ::Gitlab::Nav::TopNavMenuItem.build(
+ data: {
+ qa_selector: 'menu_item_link',
+ qa_title: 'Explore groups',
+ **menu_data_tracking_attrs('explore_groups')
+ },
+ href: '/explore/groups',
+ id: 'explore',
+ title: 'Explore groups'
+ )
+ ]
+ expect(groups_view[:linksPrimary]).to eq(expected_links_primary)
+ end
+
+ it 'has expected :linksSecondary' do
+ expected_links_secondary = [
+ ::Gitlab::Nav::TopNavMenuItem.build(
+ data: {
+ qa_selector: 'menu_item_link',
+ qa_title: 'Create group',
+ **menu_data_tracking_attrs('create_group')
+ },
+ href: '/groups/new',
+ id: 'create',
+ title: 'Create group'
+ )
+ ]
+ expect(groups_view[:linksSecondary]).to eq(expected_links_secondary)
+ end
end
context 'with external user' do
@@ -374,6 +437,9 @@ RSpec.describe Nav::TopNavHelper do
let(:with_milestones) { true }
it 'has expected :primary' do
+ expected_header = ::Gitlab::Nav::TopNavMenuHeader.build(
+ title: 'Explore'
+ )
expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'milestones_link',
@@ -384,7 +450,7 @@ RSpec.describe Nav::TopNavHelper do
id: 'milestones',
title: 'Milestones'
)
- expect(subject[:primary]).to eq([expected_primary])
+ expect(subject[:primary]).to eq([expected_header, expected_primary])
end
it 'has expected :shortcuts' do
@@ -402,6 +468,9 @@ RSpec.describe Nav::TopNavHelper do
let(:with_snippets) { true }
it 'has expected :primary' do
+ expected_header = ::Gitlab::Nav::TopNavMenuHeader.build(
+ title: 'Explore'
+ )
expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'snippets_link',
@@ -412,7 +481,7 @@ RSpec.describe Nav::TopNavHelper do
id: 'snippets',
title: 'Snippets'
)
- expect(subject[:primary]).to eq([expected_primary])
+ expect(subject[:primary]).to eq([expected_header, expected_primary])
end
it 'has expected :shortcuts' do
@@ -430,6 +499,9 @@ RSpec.describe Nav::TopNavHelper do
let(:with_activity) { true }
it 'has expected :primary' do
+ expected_header = ::Gitlab::Nav::TopNavMenuHeader.build(
+ title: 'Explore'
+ )
expected_primary = ::Gitlab::Nav::TopNavMenuItem.build(
data: {
qa_selector: 'activity_link',
@@ -440,7 +512,7 @@ RSpec.describe Nav::TopNavHelper do
id: 'activity',
title: 'Activity'
)
- expect(subject[:primary]).to eq([expected_primary])
+ expect(subject[:primary]).to eq([expected_header, expected_primary])
end
it 'has expected :shortcuts' do
diff --git a/spec/helpers/notify_helper_spec.rb b/spec/helpers/notify_helper_spec.rb
index 654fb9bb3f8..09da2b89dff 100644
--- a/spec/helpers/notify_helper_spec.rb
+++ b/spec/helpers/notify_helper_spec.rb
@@ -51,6 +51,33 @@ RSpec.describe NotifyHelper do
end
end
+ describe '#merge_request_hash_param' do
+ let(:merge_request) { create(:merge_request) }
+ let(:reviewer) { create(:user) }
+ let(:avatar_icon_for_user) { 'avatar_icon_for_user' }
+
+ before do
+ allow(helper).to receive(:avatar_icon_for_user).and_return(avatar_icon_for_user)
+ end
+
+ it 'returns MR approved description' do
+ mr_link_style = "font-weight: 600;color:#3777b0;text-decoration:none"
+ reviewer_avatar_style = "border-radius:12px;margin:-7px 0 -7px 3px;"
+ mr_link = link_to(merge_request.to_reference, merge_request_url(merge_request), style: mr_link_style).html_safe
+ reviewer_avatar = content_tag(:img, nil, height: "24", src: avatar_icon_for_user, style: reviewer_avatar_style, \
+ width: "24", alt: "Avatar", class: "avatar").html_safe
+ reviewer_link = link_to(reviewer.name, user_url(reviewer), style: "color:#333333;text-decoration:none;", \
+ class: "muted").html_safe
+ result = helper.merge_request_hash_param(merge_request, reviewer)
+ expect(result[:mr_highlight]).to eq '<span style="font-weight: 600;color:#333333;">'.html_safe
+ expect(result[:highlight_end]).to eq '</span>'.html_safe
+ expect(result[:mr_link]).to eq mr_link
+ expect(result[:reviewer_highlight]).to eq '<span>'.html_safe
+ expect(result[:reviewer_avatar]).to eq reviewer_avatar
+ expect(result[:reviewer_link]).to eq reviewer_link
+ end
+ end
+
def reference_link(entity, url)
"<a href=\"#{url}\">#{entity.to_reference}</a>"
end
diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb
index d0d399ad10f..1e16d969744 100644
--- a/spec/helpers/page_layout_helper_spec.rb
+++ b/spec/helpers/page_layout_helper_spec.rb
@@ -222,6 +222,22 @@ RSpec.describe PageLayoutHelper do
end
end
+ describe '#full_content_class' do
+ before do
+ allow(helper).to receive(:current_user).and_return(build(:user))
+ end
+
+ it 'has a content_class set' do
+ assign(:content_class, '_content_class_')
+
+ expect(helper.full_content_class).to eq 'container-fluid container-limited _content_class_'
+ end
+
+ it 'has no content_class set' do
+ expect(helper.full_content_class).to eq 'container-fluid container-limited '
+ end
+ end
+
describe '#user_status_properties' do
let(:user) { build(:user) }
diff --git a/spec/helpers/profiles_helper_spec.rb b/spec/helpers/profiles_helper_spec.rb
index 63641e65942..7de8ca89d3d 100644
--- a/spec/helpers/profiles_helper_spec.rb
+++ b/spec/helpers/profiles_helper_spec.rb
@@ -99,7 +99,7 @@ RSpec.describe ProfilesHelper do
describe "#ssh_key_expires_field_description" do
subject { helper.ssh_key_expires_field_description }
- it { is_expected.to eq('Key becomes invalid on this date.') }
+ it { is_expected.to eq(s_('Profiles|Optional but recommended. If set, key becomes invalid on the specified date.')) }
end
describe '#middle_dot_divider_classes' do
diff --git a/spec/helpers/projects/google_cloud/cloudsql_helper_spec.rb b/spec/helpers/projects/google_cloud/cloudsql_helper_spec.rb
new file mode 100644
index 00000000000..6b82518592f
--- /dev/null
+++ b/spec/helpers/projects/google_cloud/cloudsql_helper_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::GoogleCloud::CloudsqlHelper do
+ describe '#TIERS' do
+ it 'is an array' do
+ expect(described_class::TIERS).to be_an_instance_of(Array)
+ end
+ end
+
+ describe '#VERSIONS' do
+ it 'returns versions for :postgres' do
+ expect(described_class::VERSIONS[:postgres]).to be_an_instance_of(Array)
+ end
+
+ it 'returns versions for :mysql' do
+ expect(described_class::VERSIONS[:mysql]).to be_an_instance_of(Array)
+ end
+
+ it 'returns versions for :sqlserver' do
+ expect(described_class::VERSIONS[:sqlserver]).to be_an_instance_of(Array)
+ end
+ end
+end
diff --git a/spec/helpers/projects/pages_helper_spec.rb b/spec/helpers/projects/pages_helper_spec.rb
new file mode 100644
index 00000000000..4a4cebc9d70
--- /dev/null
+++ b/spec/helpers/projects/pages_helper_spec.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::PagesHelper do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+
+ before do
+ stub_config(pages: {
+ access_control: true,
+ external_http: true,
+ external_https: true,
+ host: "new.domain.com"
+ })
+ end
+
+ context 'when the user have permission' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ context 'on custom domain' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:external_http, :external_https, :can_create) do
+ false | false | false
+ false | true | true
+ true | false | true
+ true | true | true
+ end
+
+ with_them do
+ it do
+ stub_config(pages: { external_http: external_http, external_https: external_https })
+
+ expect(can_create_pages_custom_domains?(user, project)).to be can_create
+ end
+ end
+ end
+
+ context 'on domain limit' do
+ it 'can create new domains when the limit is 0' do
+ Gitlab::CurrentSettings.update!(max_pages_custom_domains_per_project: 0)
+
+ expect(can_create_pages_custom_domains?(user, project)).to be true
+ end
+
+ it 'validates custom domain creation is only allowed upto max value' do
+ Gitlab::CurrentSettings.update!(max_pages_custom_domains_per_project: 1)
+
+ expect(can_create_pages_custom_domains?(user, project)).to be true
+ create(:pages_domain, project: project)
+ expect(can_create_pages_custom_domains?(user, project)).to be false
+ end
+ end
+ end
+
+ context 'when the user does not have permission' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'validates user cannot create domain' do
+ expect(can_create_pages_custom_domains?(user, project)).to be false
+ end
+ end
+end
diff --git a/spec/helpers/projects/pipeline_helper_spec.rb b/spec/helpers/projects/pipeline_helper_spec.rb
index 8ce4e9f5293..a70544ace1a 100644
--- a/spec/helpers/projects/pipeline_helper_spec.rb
+++ b/spec/helpers/projects/pipeline_helper_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe Projects::PipelineHelper do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
let_it_be(:raw_pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) }
- let_it_be(:pipeline) { Ci::PipelinePresenter.new(raw_pipeline, current_user: user)}
+ let_it_be(:pipeline) { Ci::PipelinePresenter.new(raw_pipeline, current_user: user) }
describe '#js_pipeline_tabs_data' do
before do
@@ -19,7 +19,6 @@ RSpec.describe Projects::PipelineHelper do
it 'returns pipeline tabs data' do
expect(pipeline_tabs_data).to include({
- can_generate_codequality_reports: pipeline.can_generate_codequality_reports?.to_json,
failed_jobs_count: pipeline.failed_builds.count,
failed_jobs_summary: prepare_failed_jobs_summary_data(pipeline.failed_builds),
full_path: project.full_path,
@@ -31,9 +30,10 @@ RSpec.describe Projects::PipelineHelper do
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.has_reports?(Ci::JobArtifact.test_reports),
+ has_test_report: pipeline.complete_and_has_reports?(Ci::JobArtifact.of_report_type(:test)),
empty_state_image_path: match_asset_path('illustrations/empty-state/empty-test-cases-lg.svg'),
- artifacts_expired_image_path: match_asset_path('illustrations/pipeline.svg')
+ artifacts_expired_image_path: match_asset_path('illustrations/pipeline.svg'),
+ tests_count: pipeline.test_report_summary.total[:count]
})
end
end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 04c066986b7..a9db2a1c008 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -1147,37 +1147,23 @@ RSpec.describe ProjectsHelper do
context 'with the setting enabled' do
before do
stub_application_setting(delete_inactive_projects: true)
+ stub_application_setting(inactive_projects_min_size_mb: 0)
+ stub_application_setting(inactive_projects_send_warning_email_after_months: 1)
end
- context 'with the feature flag disabled' do
- before do
- stub_feature_flags(inactive_projects_deletion: false)
- end
-
+ context 'with an active project' do
it_behaves_like 'does not show the banner'
end
- context 'with the feature flag enabled' do
+ context 'with an inactive project' do
before do
- stub_feature_flags(inactive_projects_deletion: true)
- stub_application_setting(inactive_projects_min_size_mb: 0)
- stub_application_setting(inactive_projects_send_warning_email_after_months: 1)
+ project.statistics.storage_size = 1.megabyte
+ project.last_activity_at = 1.year.ago
+ project.save!
end
- context 'with an active project' do
- it_behaves_like 'does not show the banner'
- end
-
- context 'with an inactive project' do
- before do
- project.statistics.storage_size = 1.megabyte
- project.last_activity_at = 1.year.ago
- project.save!
- end
-
- it 'shows the banner' do
- expect(helper.show_inactive_project_deletion_banner?(project)).to be(true)
- end
+ it 'shows the banner' do
+ expect(helper.show_inactive_project_deletion_banner?(project)).to be(true)
end
end
end
@@ -1304,7 +1290,7 @@ RSpec.describe ProjectsHelper do
let_it_be(:has_active_license) { true }
it 'displays the correct messagee' do
- expect(subject).to eq(s_('Clusters|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support.'))
+ expect(subject).to eq(s_('Clusters|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd} or reach out to GitLab support.'))
end
end
@@ -1312,7 +1298,7 @@ RSpec.describe ProjectsHelper do
let_it_be(:has_active_license) { false }
it 'displays the correct message' do
- expect(subject).to eq(s_('Clusters|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of November 2022. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}.'))
+ expect(subject).to eq(s_('Clusters|The certificate-based Kubernetes integration has been deprecated and will be turned off at the end of February 2023. Please %{linkStart}migrate to the GitLab agent for Kubernetes%{linkEnd}.'))
end
end
end
diff --git a/spec/helpers/routing/pseudonymization_helper_spec.rb b/spec/helpers/routing/pseudonymization_helper_spec.rb
index dd4cc55ed2b..eb2cb548f35 100644
--- a/spec/helpers/routing/pseudonymization_helper_spec.rb
+++ b/spec/helpers/routing/pseudonymization_helper_spec.rb
@@ -70,7 +70,7 @@ RSpec.describe ::Routing::PseudonymizationHelper do
end
context 'with controller for groups with subgroups and project' do
- let(:masked_url) { "http://localhost/namespace#{subgroup.id}/project#{subproject.id}"}
+ let(:masked_url) { "http://localhost/namespace#{subgroup.id}/project#{subproject.id}" }
let(:group) { subgroup }
let(:project) { subproject }
let(:request) do
@@ -94,7 +94,7 @@ RSpec.describe ::Routing::PseudonymizationHelper do
end
context 'with controller for groups and subgroups' do
- let(:masked_url) { "http://localhost/groups/namespace#{subgroup.id}/-/shared"}
+ let(:masked_url) { "http://localhost/groups/namespace#{subgroup.id}/-/shared" }
let(:group) { subgroup }
let(:request) do
double(:Request,
diff --git a/spec/helpers/search_helper_spec.rb b/spec/helpers/search_helper_spec.rb
index 513e2865ee3..ad0705e4fbf 100644
--- a/spec/helpers/search_helper_spec.rb
+++ b/spec/helpers/search_helper_spec.rb
@@ -625,7 +625,7 @@ RSpec.describe SearchHelper do
false | false
end
- let(:params) {{ confidential: confidential }}
+ let(:params) { { confidential: confidential } }
with_them do
it 'transforms confidentiality param' do
diff --git a/spec/helpers/sorting_helper_spec.rb b/spec/helpers/sorting_helper_spec.rb
index 1ee920d1c95..3e555301325 100644
--- a/spec/helpers/sorting_helper_spec.rb
+++ b/spec/helpers/sorting_helper_spec.rb
@@ -74,11 +74,11 @@ RSpec.describe SortingHelper do
def project_common_options
{
- sort_value_latest_activity => sort_title_latest_activity,
+ 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_name => sort_title_name,
+ sort_value_name_desc => sort_title_name_desc,
+ sort_value_stars_desc => sort_title_stars
}
end
@@ -90,11 +90,11 @@ RSpec.describe SortingHelper do
describe '#projects_sort_options_hash' do
it 'returns a hash of available sorting options' do
admin_options = project_common_options.merge({
- sort_value_oldest_activity => sort_title_oldest_activity,
- sort_value_oldest_created => sort_title_oldest_created,
+ 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,
- sort_value_largest_repo => sort_title_largest_repo
+ sort_value_stars_desc => sort_title_most_stars,
+ sort_value_largest_repo => sort_title_largest_repo
})
expect(projects_sort_options_hash).to eq(admin_options)
@@ -180,10 +180,10 @@ RSpec.describe SortingHelper do
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_latest_activity,
+ sort_value_oldest_created => sort_title_created_date,
+ sort_value_name_desc => sort_title_name,
+ sort_value_stars_asc => sort_title_stars
})
expect(projects_sort_option_titles).to eq(options)
@@ -198,10 +198,10 @@ RSpec.describe SortingHelper do
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_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
+ sort_value_stars_desc => sort_title_most_stars
})
expect(projects_sort_options_hash).to eq(options)
@@ -210,6 +210,18 @@ RSpec.describe SortingHelper do
end
end
+ describe '#tags_sort_options_hash' do
+ it 'returns a hash of available sorting options' do
+ expect(tags_sort_options_hash).to include({
+ sort_value_name => sort_title_name,
+ sort_value_oldest_updated => sort_title_oldest_updated,
+ sort_value_recently_updated => sort_title_recently_updated,
+ sort_value_version_desc => sort_title_version_desc,
+ sort_value_version_asc => sort_title_version_asc
+ })
+ end
+ end
+
describe 'with `forks` controller' do
before do
stub_controller_path 'forks'
@@ -219,9 +231,9 @@ RSpec.describe SortingHelper do
it 'returns a hash of available sorting options' do
expect(forks_sort_options_hash).to include({
sort_value_recently_created => sort_title_created_date,
- sort_value_oldest_created => sort_title_created_date,
- sort_value_latest_activity => sort_title_latest_activity,
- sort_value_oldest_activity => sort_title_latest_activity
+ sort_value_oldest_created => sort_title_created_date,
+ sort_value_latest_activity => sort_title_latest_activity,
+ sort_value_oldest_activity => sort_title_latest_activity
})
end
end
diff --git a/spec/helpers/storage_helper_spec.rb b/spec/helpers/storage_helper_spec.rb
index 6c3556c874b..6c0f1034d65 100644
--- a/spec/helpers/storage_helper_spec.rb
+++ b/spec/helpers/storage_helper_spec.rb
@@ -27,15 +27,15 @@ RSpec.describe StorageHelper do
create(:project,
namespace: namespace,
statistics: build(:project_statistics,
- namespace: namespace,
- repository_size: 10.kilobytes,
- wiki_size: 10.bytes,
- lfs_objects_size: 20.gigabytes,
- build_artifacts_size: 30.megabytes,
+ namespace: namespace,
+ repository_size: 10.kilobytes,
+ wiki_size: 10.bytes,
+ lfs_objects_size: 20.gigabytes,
+ build_artifacts_size: 30.megabytes,
pipeline_artifacts_size: 11.megabytes,
- snippets_size: 40.megabytes,
- packages_size: 12.megabytes,
- uploads_size: 15.megabytes))
+ snippets_size: 40.megabytes,
+ packages_size: 12.megabytes,
+ uploads_size: 15.megabytes))
end
let(:message) { 'Repository: 10 KB / Wikis: 10 Bytes / Build Artifacts: 30 MB / Pipeline Artifacts: 11 MB / LFS: 20 GB / Snippets: 40 MB / Packages: 12 MB / Uploads: 15 MB' }
@@ -50,147 +50,4 @@ RSpec.describe StorageHelper do
expect(helper.storage_counters_details(namespace_stats)).to eq(message)
end
end
-
- describe "storage_enforcement_banner" do
- let_it_be_with_refind(:current_user) { create(:user) }
- let_it_be(:free_group) { create(:group) }
- let_it_be(:paid_group) { create(:group) }
-
- before do
- allow(helper).to receive(:can?).with(current_user, :maintainer_access, free_group).and_return(true)
- allow(helper).to receive(:can?).with(current_user, :maintainer_access, paid_group).and_return(true)
- allow(helper).to receive(:current_user) { current_user }
- allow(paid_group).to receive(:paid?).and_return(true)
-
- stub_feature_flags(namespace_storage_limit_bypass_date_check: false)
- end
-
- describe "#storage_enforcement_banner_info" do
- it 'returns nil when namespace is not free' do
- expect(helper.storage_enforcement_banner_info(paid_group)).to be(nil)
- end
-
- it 'returns nil when storage_enforcement_date is not set' do
- allow(free_group).to receive(:storage_enforcement_date).and_return(nil)
-
- expect(helper.storage_enforcement_banner_info(free_group)).to be(nil)
- end
-
- describe 'when storage_enforcement_date is set' do
- let_it_be(:storage_enforcement_date) { Date.today + 30 }
-
- before do
- allow(free_group).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
- end
-
- it 'returns nil when current_user do not have access usage quotas page' do
- allow(helper).to receive(:can?).with(current_user, :maintainer_access, free_group).and_return(false)
-
- expect(helper.storage_enforcement_banner_info(free_group)).to be(nil)
- end
-
- it 'returns nil when namespace_storage_limit_show_preenforcement_banner FF is disabled' do
- stub_feature_flags(namespace_storage_limit_show_preenforcement_banner: false)
-
- expect(helper.storage_enforcement_banner_info(free_group)).to be(nil)
- end
-
- context 'when current_user can access the usage quotas page' do
- it 'returns a hash' do
- used_storage = helper.storage_counter(free_group.root_storage_statistics&.storage_size || 0)
-
- expect(helper.storage_enforcement_banner_info(free_group)).to eql({
- text_paragraph_1: "Effective #{storage_enforcement_date}, namespace storage limits will apply to the <strong>#{free_group.name}</strong> namespace. View the <a href=\"/help/user/usage_quotas#namespace-storage-limit-enforcement-schedule\" >rollout schedule for this change</a>.",
- text_paragraph_2: "The namespace is currently using <strong>#{used_storage}</strong> of namespace storage. Group owners can view namespace storage usage and purchase more from <strong>Group settings &gt; Usage quotas</strong>. <a href=\"/help/user/usage_quotas#manage-your-storage-usage\" >Learn more.</a>",
- text_paragraph_3: "See our <a href=\"https://about.gitlab.com/pricing/faq-efficient-free-tier/#storage-limits-on-gitlab-saas-free-tier\" >FAQ</a> for more information.",
- variant: 'warning',
- namespace_id: free_group.id,
- callouts_feature_name: 'storage_enforcement_banner_second_enforcement_threshold',
- callouts_path: '/-/users/group_callouts'
- })
- end
-
- context 'when namespace has used storage' do
- before do
- create(:namespace_root_storage_statistics, namespace: free_group, storage_size: 102400)
- end
-
- it 'returns a hash with the correct storage size text' do
- expect(helper.storage_enforcement_banner_info(free_group)[:text_paragraph_2]).to eql("The namespace is currently using <strong>100 KB</strong> of namespace storage. Group owners can view namespace storage usage and purchase more from <strong>Group settings &gt; Usage quotas</strong>. <a href=\"/help/user/usage_quotas#manage-your-storage-usage\" >Learn more.</a>")
- end
- end
-
- context 'when the given group is a sub-group' do
- let_it_be(:sub_group) { build(:group) }
-
- before do
- allow(helper).to receive(:can?).with(current_user, :maintainer_access, sub_group).and_return(true)
- allow(sub_group).to receive(:root_ancestor).and_return(free_group)
- end
-
- it 'returns the banner hash' do
- expect(helper.storage_enforcement_banner_info(sub_group).keys).to match_array(%i(text_paragraph_1 text_paragraph_2 text_paragraph_3 variant namespace_id callouts_feature_name callouts_path))
- end
- end
- end
- end
-
- context 'when the :storage_banner_bypass_date_check is enabled', :freeze_time do
- before do
- stub_feature_flags(namespace_storage_limit_bypass_date_check: true)
- end
-
- it 'returns the enforcement info' do
- puts helper.storage_enforcement_banner_info(free_group)[:text_paragraph_1]
- expect(helper.storage_enforcement_banner_info(free_group)[:text_paragraph_1]).to include("Effective #{Date.current}, namespace storage limits will apply")
- end
- end
-
- context 'when storage_enforcement_date is set and dismissed callout exists' do
- before do
- create(:group_callout,
- user: current_user,
- group_id: free_group.id,
- feature_name: 'storage_enforcement_banner_second_enforcement_threshold')
- storage_enforcement_date = Date.today + 30
- allow(free_group).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
- end
-
- it { expect(helper.storage_enforcement_banner_info(free_group)).to be(nil) }
- end
-
- context 'callouts_feature_name' do
- let(:days_from_now) { 45 }
-
- subject do
- storage_enforcement_date = Date.today + days_from_now
- allow(free_group).to receive(:storage_enforcement_date).and_return(storage_enforcement_date)
-
- helper.storage_enforcement_banner_info(free_group)[:callouts_feature_name]
- end
-
- it 'returns first callouts_feature_name' do
- is_expected.to eq('storage_enforcement_banner_first_enforcement_threshold')
- end
-
- context 'returns second callouts_feature_name' do
- let(:days_from_now) { 20 }
-
- it { is_expected.to eq('storage_enforcement_banner_second_enforcement_threshold') }
- end
-
- context 'returns third callouts_feature_name' do
- let(:days_from_now) { 13 }
-
- it { is_expected.to eq('storage_enforcement_banner_third_enforcement_threshold') }
- end
-
- context 'returns fourth callouts_feature_name' do
- let(:days_from_now) { 3 }
-
- it { is_expected.to eq('storage_enforcement_banner_fourth_enforcement_threshold') }
- end
- end
- end
- end
end
diff --git a/spec/helpers/tab_helper_spec.rb b/spec/helpers/tab_helper_spec.rb
index dd5707e2aff..80a1224abbb 100644
--- a/spec/helpers/tab_helper_spec.rb
+++ b/spec/helpers/tab_helper_spec.rb
@@ -182,7 +182,7 @@ RSpec.describe TabHelper do
context 'with data attributes' do
it 'creates a tab counter badge with the data attributes' do
expect(helper.gl_tab_counter_badge(1, { data: { some_attribute: 'foo' } })).to eq(
- '<span data-some-attribute="foo" class="gl-badge badge badge-pill badge-muted sm gl-tab-counter-badge">1</span>'
+ '<span class="gl-badge badge badge-pill badge-muted sm gl-tab-counter-badge" data-some-attribute="foo">1</span>'
)
end
end
diff --git a/spec/helpers/todos_helper_spec.rb b/spec/helpers/todos_helper_spec.rb
index bbabfedc3ee..a8945424877 100644
--- a/spec/helpers/todos_helper_spec.rb
+++ b/spec/helpers/todos_helper_spec.rb
@@ -258,6 +258,21 @@ RSpec.describe TodosHelper do
end
end
+ describe '#no_todos_messages' do
+ context 'when getting todos messsages' do
+ it 'return these sentences' do
+ expected_sentences = [
+ s_('Todos|Good job! Looks like you don\'t have anything left on your To-Do List'),
+ s_('Todos|Isn\'t an empty To-Do List beautiful?'),
+ s_('Todos|Give yourself a pat on the back!'),
+ s_('Todos|Nothing left to do. High five!'),
+ s_('Todos|Henceforth, you shall be known as "To-Do Destroyer"')
+ ]
+ expect(helper.no_todos_messages).to eq(expected_sentences)
+ end
+ end
+ end
+
describe '#todo_author_display?' do
using RSpec::Parameterized::TableSyntax
diff --git a/spec/helpers/users/callouts_helper_spec.rb b/spec/helpers/users/callouts_helper_spec.rb
index 2c148aabead..170ae098a2f 100644
--- a/spec/helpers/users/callouts_helper_spec.rb
+++ b/spec/helpers/users/callouts_helper_spec.rb
@@ -241,10 +241,10 @@ RSpec.describe Users::CalloutsHelper do
context 'the web-hook failure callout has been dismissed', :freeze_time do
before do
- create(:namespace_callout,
+ create(:project_callout,
feature_name: described_class::WEB_HOOK_DISABLED,
user: user,
- namespace: project.namespace,
+ project: project,
dismissed_at: 1.week.ago)
end
diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb
index 78a15f52be5..617a796781e 100644
--- a/spec/helpers/users_helper_spec.rb
+++ b/spec/helpers/users_helper_spec.rb
@@ -136,7 +136,7 @@ RSpec.describe UsersHelper do
badges = helper.user_badges_in_admin_section(blocked_user)
- expect(filter_ee_badges(badges)).to match_array([text: "Blocked", variant: "danger"])
+ expect(filter_ee_badges(badges)).to match_array([text: s_("AdminUsers|Blocked"), variant: "danger"])
end
end
@@ -146,7 +146,7 @@ RSpec.describe UsersHelper do
badges = helper.user_badges_in_admin_section(blocked_pending_approval_user)
- expect(filter_ee_badges(badges)).to match_array([text: 'Pending approval', variant: 'info'])
+ expect(filter_ee_badges(badges)).to match_array([text: s_('AdminUsers|Pending approval'), variant: 'info'])
end
end
@@ -156,7 +156,7 @@ RSpec.describe UsersHelper do
badges = helper.user_badges_in_admin_section(banned_user)
- expect(filter_ee_badges(badges)).to match_array([text: 'Banned', variant: 'danger'])
+ expect(filter_ee_badges(badges)).to match_array([text: s_('AdminUsers|Banned'), variant: 'danger'])
end
end
@@ -166,7 +166,17 @@ RSpec.describe UsersHelper do
badges = helper.user_badges_in_admin_section(admin_user)
- expect(filter_ee_badges(badges)).to match_array([text: "Admin", variant: "success"])
+ expect(filter_ee_badges(badges)).to match_array([text: s_("AdminUsers|Admin"), variant: "success"])
+ end
+ end
+
+ context 'with a bot' do
+ it "returns the bot badge" do
+ bot = create(:user, :bot)
+
+ badges = helper.user_badges_in_admin_section(bot)
+
+ expect(filter_ee_badges(badges)).to match_array([text: s_('AdminUsers|Bot'), variant: "muted"])
end
end
@@ -176,7 +186,7 @@ RSpec.describe UsersHelper do
badges = helper.user_badges_in_admin_section(external_user)
- expect(filter_ee_badges(badges)).to match_array([text: "External", variant: "secondary"])
+ expect(filter_ee_badges(badges)).to match_array([text: s_("AdminUsers|External"), variant: "secondary"])
end
end
@@ -184,7 +194,7 @@ RSpec.describe UsersHelper do
it 'returns the "It\'s You" badge' do
badges = helper.user_badges_in_admin_section(user)
- expect(filter_ee_badges(badges)).to match_array([text: "It's you!", variant: "muted"])
+ expect(filter_ee_badges(badges)).to match_array([text: s_("AdminUsers|It's you!"), variant: "muted"])
end
end
@@ -195,9 +205,9 @@ RSpec.describe UsersHelper do
badges = helper.user_badges_in_admin_section(user)
expect(badges).to match_array([
- { text: "Blocked", variant: "danger" },
- { text: "Admin", variant: "success" },
- { text: "External", variant: "secondary" }
+ { text: s_("AdminUsers|Blocked"), variant: "danger" },
+ { text: s_("AdminUsers|Admin"), variant: "success" },
+ { text: s_("AdminUsers|External"), variant: "secondary" }
])
end
end
@@ -208,7 +218,7 @@ RSpec.describe UsersHelper do
badges = helper.user_badges_in_admin_section(locked_user)
- expect(filter_ee_badges(badges)).to match_array([text: "Locked", variant: "warning"])
+ expect(filter_ee_badges(badges)).to match_array([text: s_("AdminUsers|Locked"), variant: "warning"])
end
end
@@ -435,7 +445,7 @@ RSpec.describe UsersHelper do
it 'contains resend confirmation e-mail text' do
expect(user_email_help_text).to include _('Resend confirmation e-mail')
- expect(user_email_help_text).to include _('Please click the link in the confirmation email before continuing. It was sent to ')
+ expect(user_email_help_text).to match /Please click the link in the confirmation email before continuing. It was sent to.*#{user.unconfirmed_email}/
end
end
end
diff --git a/spec/helpers/wiki_helper_spec.rb b/spec/helpers/wiki_helper_spec.rb
index 5adcbe3334d..75128d758f9 100644
--- a/spec/helpers/wiki_helper_spec.rb
+++ b/spec/helpers/wiki_helper_spec.rb
@@ -132,11 +132,11 @@ RSpec.describe WikiHelper do
it 'returns the tracking context' do
expect(subject).to eq(
- 'wiki-format' => :markdown,
- 'wiki-title-size' => 9,
- 'wiki-content-size' => 4,
+ 'wiki-format' => :markdown,
+ 'wiki-title-size' => 9,
+ 'wiki-content-size' => 4,
'wiki-directory-nest-level' => 2,
- 'wiki-container-type' => 'Project'
+ 'wiki-container-type' => 'Project'
)
end
diff --git a/spec/helpers/wiki_page_version_helper_spec.rb b/spec/helpers/wiki_page_version_helper_spec.rb
index bc500c28c5a..a792e5df035 100644
--- a/spec/helpers/wiki_page_version_helper_spec.rb
+++ b/spec/helpers/wiki_page_version_helper_spec.rb
@@ -6,8 +6,8 @@ RSpec.describe WikiPageVersionHelper do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:user) { create(:user, username: 'foo') }
- let(:commit_with_user) { create(:commit, project: project, author: user)}
- let(:commit_without_user) { create(:commit, project: project, author_name: 'Foo', author_email: 'foo@example.com')}
+ let(:commit_with_user) { create(:commit, project: project, author: user) }
+ let(:commit_without_user) { create(:commit, project: project, author_name: 'Foo', author_email: 'foo@example.com') }
let(:wiki_page_version) { Gitlab::Git::WikiPageVersion.new(commit, nil) }
describe '#wiki_page_version_author_url' do
diff --git a/spec/initializers/00_rails_disable_joins_spec.rb b/spec/initializers/00_rails_disable_joins_spec.rb
index 78e78b6810b..3b390f1ef17 100644
--- a/spec/initializers/00_rails_disable_joins_spec.rb
+++ b/spec/initializers/00_rails_disable_joins_spec.rb
@@ -98,8 +98,8 @@ RSpec.describe 'DisableJoins' do
primary_model.has_one :test_bridge, anonymous_class: bridge_model, foreign_key: :primary_record_id
bridge_model.belongs_to :test_secondary, anonymous_class: secondary_model, foreign_key: :secondary_record_id
- primary_model.has_one :test_secondary, through: :test_bridge, anonymous_class: secondary_model,
- disable_joins: -> { joins_disabled_flag }
+ primary_model.has_one :test_secondary,
+ through: :test_bridge, anonymous_class: secondary_model, disable_joins: -> { joins_disabled_flag }
primary_record = primary_model.create!
secondary_record = secondary_model.create!
@@ -149,7 +149,7 @@ RSpec.describe 'DisableJoins' do
primary_model.has_many :test_bridges, anonymous_class: bridge_model, foreign_key: :primary_record_id
bridge_model.has_many :test_secondaries, anonymous_class: secondary_model, foreign_key: :bridge_record_id
primary_model.has_many :test_secondaries, through: :test_bridges, anonymous_class: secondary_model,
- disable_joins: -> { disabled_join_flag }
+ disable_joins: -> { disabled_join_flag }
primary_record = primary_model.create!
bridge_record = bridge_model.create!(primary_record_id: primary_record.id)
diff --git a/spec/initializers/action_cable_subscription_adapter_identifier_spec.rb b/spec/initializers/action_cable_subscription_adapter_identifier_spec.rb
index 074df9adc21..94134ce44fd 100644
--- a/spec/initializers/action_cable_subscription_adapter_identifier_spec.rb
+++ b/spec/initializers/action_cable_subscription_adapter_identifier_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe 'ActionCableSubscriptionAdapterIdentifier override' do
sub = ActionCable.server.pubsub.send(:redis_connection)
- expect(sub.connection[:id]).to eq('redis:///home/localuser/redis/redis.socket/0')
+ expect(sub.connection[:id]).to eq('unix:///home/localuser/redis/redis.socket/0')
expect(ActionCable.server.config.cable[:id]).to be_nil
end
end
diff --git a/spec/initializers/carrierwave_patch_spec.rb b/spec/initializers/carrierwave_patch_spec.rb
index b0f337935ef..0910342f10f 100644
--- a/spec/initializers/carrierwave_patch_spec.rb
+++ b/spec/initializers/carrierwave_patch_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'CarrierWave::Storage::Fog::File' do
let(:storage) { CarrierWave::Storage::Fog.new(uploader) }
let(:bucket_name) { 'some-bucket' }
let(:connection) { ::Fog::Storage.new(connection_options) }
- let(:bucket) { connection.directories.new(key: bucket_name )}
+ let(:bucket) { connection.directories.new(key: bucket_name ) }
let(:test_filename) { 'test' }
let(:test_data) { File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) }
@@ -33,7 +33,7 @@ RSpec.describe 'CarrierWave::Storage::Fog::File' do
end
describe '#copy_to' do
- let(:dest_filename) { 'copied.txt'}
+ let(:dest_filename) { 'copied.txt' }
it 'copies the file' do
fog_file = subject.send(:file)
@@ -67,7 +67,7 @@ RSpec.describe 'CarrierWave::Storage::Fog::File' do
end
describe '#copy_to' do
- let(:dest_filename) { 'copied.txt'}
+ let(:dest_filename) { 'copied.txt' }
it 'copies the file' do
result = subject.copy_to(dest_filename)
diff --git a/spec/initializers/load_balancing_spec.rb b/spec/initializers/load_balancing_spec.rb
new file mode 100644
index 00000000000..d9162acd2cd
--- /dev/null
+++ b/spec/initializers/load_balancing_spec.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'load_balancing', :delete, :reestablished_active_record_base do
+ subject(:initialize_load_balancer) do
+ load Rails.root.join('config/initializers/load_balancing.rb')
+ end
+
+ before do
+ # Stub out middleware call, as not idempotent
+ allow(Gitlab::Application.instance.middleware).to receive(:use)
+ end
+
+ context 'with replica hosts configured' do
+ before do
+ # Setup host-based load balancing
+ # Patch in our load balancer config, simply pointing at the test database twice
+ allow(Gitlab::Database::LoadBalancing::Configuration).to receive(:for_model) do |base_model|
+ db_host = base_model.connection_pool.db_config.host
+
+ Gitlab::Database::LoadBalancing::Configuration.new(base_model, [db_host, db_host])
+ end
+ end
+
+ after do
+ # reset load balancing to original state
+ allow(Gitlab::Database::LoadBalancing::Configuration).to receive(:for_model).and_call_original
+ allow(Gitlab::Cluster::LifecycleEvents).to receive(:in_clustered_puma?).and_call_original
+
+ load Rails.root.join('config/initializers/load_balancing.rb')
+ end
+
+ it 'configures load balancer with two replica hosts' do
+ expect(ApplicationRecord.connection.load_balancer.configuration.hosts.size).to eq(0)
+ expect(Ci::ApplicationRecord.connection.load_balancer.configuration.hosts.size).to eq(0)
+
+ initialize_load_balancer
+
+ expect(ApplicationRecord.connection.load_balancer.configuration.hosts.size).to eq(2)
+ expect(Ci::ApplicationRecord.connection.load_balancer.configuration.hosts.size).to eq(2)
+ end
+
+ context 'for a clustered puma worker' do
+ let!(:group) { create(:group, name: 'my group') }
+
+ before do
+ # Pretend we are in clustered environment
+ allow(Gitlab::Cluster::LifecycleEvents).to receive(:in_clustered_puma?).and_return(true)
+ end
+
+ it 'configures load balancer to have two replica hosts' do
+ initialize_load_balancer
+
+ simulate_puma_worker do
+ expect(ApplicationRecord.connection.load_balancer.configuration.hosts.size).to eq(2)
+ expect(Ci::ApplicationRecord.connection.load_balancer.configuration.hosts.size).to eq(2)
+ end
+ end
+
+ # We tried using Process.fork for a more realistic simulation
+ # but run into bugs where GPRC cannot be used before forking processes.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/333184#note_1081658113
+ def simulate_puma_worker
+ # Called in https://github.com/rails/rails/blob/6-1-stable/activerecord/lib/active_record/connection_adapters/pool_config.rb#L73
+ ActiveRecord::ConnectionAdapters::PoolConfig.discard_pools!
+
+ # Called in config/puma.rb
+ Gitlab::Cluster::LifecycleEvents.do_worker_start
+
+ yield
+ end
+
+ it 'makes a read query successfully' do
+ # Clear any previous sticky writes
+ ::Gitlab::Database::LoadBalancing::Session.clear_session
+
+ initialize_load_balancer
+
+ group_name = simulate_puma_worker do
+ Group.find_by_name('my group').name
+ end
+
+ expect(group_name).to eq(group.name)
+ end
+
+ it 'makes a write query successfully' do
+ initialize_load_balancer
+
+ expect do
+ simulate_puma_worker do
+ Group.touch_all
+ end
+
+ group.reload
+ end.to change(group, :updated_at)
+ end
+ end
+ end
+end
diff --git a/spec/initializers/microsoft_graph_mailer_spec.rb b/spec/initializers/microsoft_graph_mailer_spec.rb
new file mode 100644
index 00000000000..fbe667e34fe
--- /dev/null
+++ b/spec/initializers/microsoft_graph_mailer_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'microsoft_graph_mailer initializer for GitLab' do
+ let(:microsoft_graph_setting) do
+ {
+ user_id: SecureRandom.hex,
+ tenant: SecureRandom.hex,
+ client_id: SecureRandom.hex,
+ client_secret: SecureRandom.hex,
+ azure_ad_endpoint: 'https://test-azure_ad_endpoint',
+ graph_endpoint: 'https://test-graph_endpoint'
+ }
+ end
+
+ def load_microsoft_graph_mailer_initializer
+ load Rails.root.join('config/initializers/microsoft_graph_mailer.rb')
+ end
+
+ context 'when microsoft_graph_mailer is enabled' do
+ before do
+ stub_microsoft_graph_mailer_setting(microsoft_graph_setting.merge(enabled: true))
+ end
+
+ it 'configures ActionMailer' do
+ previous_delivery_method = ActionMailer::Base.delivery_method
+ previous_microsoft_graph_settings = ActionMailer::Base.microsoft_graph_settings
+
+ load_microsoft_graph_mailer_initializer
+
+ expect(ActionMailer::Base.delivery_method).to eq(:microsoft_graph)
+ expect(ActionMailer::Base.microsoft_graph_settings).to eq(microsoft_graph_setting)
+ ensure
+ ActionMailer::Base.delivery_method = previous_delivery_method
+ ActionMailer::Base.microsoft_graph_settings = previous_microsoft_graph_settings
+ end
+ end
+
+ context 'when microsoft_graph_mailer is disabled' do
+ before do
+ stub_microsoft_graph_mailer_setting(microsoft_graph_setting.merge(enabled: false))
+ end
+
+ it 'does not configure ActionMailer' do
+ previous_delivery_method = ActionMailer::Base.delivery_method
+ previous_microsoft_graph_settings = ActionMailer::Base.microsoft_graph_settings
+
+ load_microsoft_graph_mailer_initializer
+
+ expect(previous_microsoft_graph_settings).not_to eq(:microsoft_graph)
+ expect(ActionMailer::Base.delivery_method).to eq(previous_delivery_method)
+ expect(ActionMailer::Base.microsoft_graph_settings).to eq(previous_microsoft_graph_settings)
+ end
+ end
+end
diff --git a/spec/initializers/net_http_patch_spec.rb b/spec/initializers/net_http_patch_spec.rb
index d6b003d84fa..d56730917f1 100644
--- a/spec/initializers/net_http_patch_spec.rb
+++ b/spec/initializers/net_http_patch_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require 'net/http'
require_relative '../../config/initializers/net_http_patch'
diff --git a/spec/initializers/settings_spec.rb b/spec/initializers/settings_spec.rb
index 71ea12a41aa..c3200d2fab1 100644
--- a/spec/initializers/settings_spec.rb
+++ b/spec/initializers/settings_spec.rb
@@ -58,4 +58,40 @@ RSpec.describe Settings do
end
end
end
+
+ describe "#weak_passwords_digest_set" do
+ subject { described_class.gitlab.weak_passwords_digest_set }
+
+ it 'is a Set' do
+ expect(subject).to be_kind_of(Set)
+ end
+
+ it 'contains 4500 password digests' do
+ expect(subject.length).to eq(4500)
+ end
+
+ it 'includes 8 char weak password digest' do
+ expect(subject).to include(digest("password"))
+ end
+
+ it 'includes 16 char weak password digest' do
+ expect(subject).to include(digest("progressivehouse"))
+ end
+
+ it 'includes long char weak password digest' do
+ expect(subject).to include(digest("01234567890123456789"))
+ end
+
+ it 'does not include 7 char weak password digest' do
+ expect(subject).not_to include(digest("1234567"))
+ end
+
+ it 'does not include plaintext' do
+ expect(subject).not_to include("password")
+ end
+
+ def digest(plaintext)
+ Digest::SHA256.base64digest(plaintext)
+ end
+ end
end
diff --git a/spec/initializers/trusted_proxies_spec.rb b/spec/initializers/trusted_proxies_spec.rb
index 2786f034969..63c96ce17d1 100644
--- a/spec/initializers/trusted_proxies_spec.rb
+++ b/spec/initializers/trusted_proxies_spec.rb
@@ -58,7 +58,7 @@ RSpec.describe 'trusted_proxies' do
end
def stub_request(headers = {})
- ActionDispatch::RemoteIp.new(proc { }, false, Rails.application.config.action_dispatch.trusted_proxies).call(headers)
+ ActionDispatch::RemoteIp.new(proc {}, false, Rails.application.config.action_dispatch.trusted_proxies).call(headers)
ActionDispatch::Request.new(headers)
end
diff --git a/spec/lib/api/entities/ci/job_request/image_spec.rb b/spec/lib/api/entities/ci/job_request/image_spec.rb
index fca3b5d3fa9..14d4a074fce 100644
--- a/spec/lib/api/entities/ci/job_request/image_spec.rb
+++ b/spec/lib/api/entities/ci/job_request/image_spec.rb
@@ -32,14 +32,4 @@ RSpec.describe API::Entities::Ci::JobRequest::Image do
it 'returns the pull policy' do
expect(subject[:pull_policy]).to eq(['if-not-present'])
end
-
- context 'when the FF ci_docker_image_pull_policy is disabled' do
- before do
- stub_feature_flags(ci_docker_image_pull_policy: false)
- end
-
- it 'does not return the pull policy' do
- expect(subject).not_to have_key(:pull_policy)
- end
- end
end
diff --git a/spec/lib/api/entities/ci/job_request/service_spec.rb b/spec/lib/api/entities/ci/job_request/service_spec.rb
index 86f2120c321..11350f7c41b 100644
--- a/spec/lib/api/entities/ci/job_request/service_spec.rb
+++ b/spec/lib/api/entities/ci/job_request/service_spec.rb
@@ -40,12 +40,4 @@ RSpec.describe API::Entities::Ci::JobRequest::Service do
expect(subject[:ports]).to be_nil
end
end
-
- context 'when the FF ci_docker_image_pull_policy is disabled' do
- before do
- stub_feature_flags(ci_docker_image_pull_policy: false)
- end
-
- it { is_expected.not_to have_key(:pull_policy) }
- end
end
diff --git a/spec/lib/api/entities/ml/mlflow/run_info_spec.rb b/spec/lib/api/entities/ml/mlflow/run_info_spec.rb
new file mode 100644
index 00000000000..2a6d0825e5c
--- /dev/null
+++ b/spec/lib/api/entities/ml/mlflow/run_info_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe API::Entities::Ml::Mlflow::RunInfo do
+ let_it_be(:candidate) { create(:ml_candidates) }
+
+ subject { described_class.new(candidate).as_json }
+
+ context 'when start_time is nil' do
+ it { expect(subject[:start_time]).to eq(0) }
+ end
+
+ context 'when start_time is not nil' do
+ before do
+ allow(candidate).to receive(:start_time).and_return(1234)
+ end
+
+ it { expect(subject[:start_time]).to eq(1234) }
+ end
+
+ describe 'end_time' do
+ context 'when nil' do
+ it { is_expected.not_to have_key(:end_time) }
+ end
+
+ context 'when not nil' do
+ before do
+ allow(candidate).to receive(:end_time).and_return(1234)
+ end
+
+ it { expect(subject[:end_time]).to eq(1234) }
+ end
+ end
+
+ describe 'experiment_id' do
+ it 'is the experiment iid as string' do
+ expect(subject[:experiment_id]).to eq(candidate.experiment.iid.to_s)
+ end
+ end
+
+ describe 'run_id' do
+ it 'is the iid as string' do
+ expect(subject[:run_id]).to eq(candidate.iid.to_s)
+ end
+ end
+
+ describe 'run_uuid' do
+ it 'is the iid as string' do
+ expect(subject[:run_uuid]).to eq(candidate.iid.to_s)
+ end
+ end
+
+ describe 'artifact_uri' do
+ it 'is not implemented' do
+ expect(subject[:artifact_uri]).to eq('not_implemented')
+ end
+ end
+
+ describe 'lifecycle_stage' do
+ it 'is active' do
+ expect(subject[:lifecycle_stage]).to eq('active')
+ end
+ end
+end
diff --git a/spec/lib/api/entities/ml/mlflow/run_spec.rb b/spec/lib/api/entities/ml/mlflow/run_spec.rb
new file mode 100644
index 00000000000..84234f474f5
--- /dev/null
+++ b/spec/lib/api/entities/ml/mlflow/run_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe API::Entities::Ml::Mlflow::Run do
+ let_it_be(:candidate) { create(:ml_candidates) }
+
+ subject { described_class.new(candidate).as_json }
+
+ it 'has run key' do
+ expect(subject).to have_key(:run)
+ end
+
+ it 'has the id' do
+ expect(subject[:run][:info][:run_id]).to eq(candidate.iid.to_s)
+ end
+
+ it 'data is empty' do
+ expect(subject[:run][:data]).to be_empty
+ end
+end
diff --git a/spec/lib/api/entities/personal_access_token_with_details_spec.rb b/spec/lib/api/entities/personal_access_token_with_details_spec.rb
deleted file mode 100644
index a53d6febba1..00000000000
--- a/spec/lib/api/entities/personal_access_token_with_details_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe API::Entities::PersonalAccessTokenWithDetails do
- describe '#as_json' do
- let_it_be(:user) { create(:user) }
- let_it_be(:token) { create(:personal_access_token, user: user, expires_at: nil) }
-
- let(:entity) { described_class.new(token) }
-
- it 'returns token data' do
- expect(entity.as_json).to eq({
- id: token.id,
- name: token.name,
- revoked: false,
- created_at: token.created_at,
- scopes: ['api'],
- user_id: user.id,
- last_used_at: nil,
- active: true,
- expires_at: nil,
- expired: false,
- expires_soon: false,
- revoke_path: Gitlab::Routing.url_helpers.revoke_profile_personal_access_token_path(token)
- })
- end
- end
-end
diff --git a/spec/lib/api/helpers/caching_spec.rb b/spec/lib/api/helpers/caching_spec.rb
index 38b7b386d5c..828af7b5f91 100644
--- a/spec/lib/api/helpers/caching_spec.rb
+++ b/spec/lib/api/helpers/caching_spec.rb
@@ -33,10 +33,7 @@ RSpec.describe API::Helpers::Caching, :use_clean_rails_redis_caching do
end
describe "#present_cached" do
- subject do
- instance.present_cached(presentable, **kwargs)
- end
-
+ let(:method) { :present_cached }
let(:kwargs) do
{
with: presenter,
@@ -44,6 +41,10 @@ RSpec.describe API::Helpers::Caching, :use_clean_rails_redis_caching do
}
end
+ subject do
+ instance.public_send(method, presentable, **kwargs)
+ end
+
context 'single object' do
let_it_be(:presentable) { create(:todo, project: project) }
diff --git a/spec/lib/api/helpers/packages/dependency_proxy_helpers_spec.rb b/spec/lib/api/helpers/packages/dependency_proxy_helpers_spec.rb
index ae0c0f53acd..aa4b0a137cd 100644
--- a/spec/lib/api/helpers/packages/dependency_proxy_helpers_spec.rb
+++ b/spec/lib/api/helpers/packages/dependency_proxy_helpers_spec.rb
@@ -8,6 +8,8 @@ RSpec.describe API::Helpers::Packages::DependencyProxyHelpers do
describe '#redirect_registry_request' do
using RSpec::Parameterized::TableSyntax
+ let_it_be(:project) { create(:project) }
+
let(:options) { {} }
subject { helper.redirect_registry_request(forward_to_registry, package_type, options) { helper.fallback } }
@@ -18,8 +20,8 @@ RSpec.describe API::Helpers::Packages::DependencyProxyHelpers do
shared_examples 'executing fallback' do
it 'redirects to package registry' do
- expect(helper).to receive(:registry_url).never
- expect(helper).to receive(:redirect).never
+ expect(helper).not_to receive(:registry_url)
+ expect(helper).not_to receive(:redirect)
expect(helper).to receive(:fallback).once
subject
@@ -30,7 +32,7 @@ RSpec.describe API::Helpers::Packages::DependencyProxyHelpers do
it 'redirects to package registry', :snowplow do
expect(helper).to receive(:registry_url).once
expect(helper).to receive(:redirect).once
- expect(helper).to receive(:fallback).never
+ expect(helper).not_to receive(:fallback)
subject
@@ -38,11 +40,12 @@ RSpec.describe API::Helpers::Packages::DependencyProxyHelpers do
end
end
- %i[npm pypi].each do |forwardable_package_type|
+ %i[maven npm pypi].each do |forwardable_package_type|
context "with #{forwardable_package_type} packages" do
include_context 'dependency proxy helpers context'
let(:package_type) { forwardable_package_type }
+ let(:options) { { project: project } }
where(:application_setting, :forward_to_registry, :example_name) do
true | true | 'executing redirect'
@@ -59,17 +62,41 @@ RSpec.describe API::Helpers::Packages::DependencyProxyHelpers do
it_behaves_like params[:example_name]
end
end
+
+ context 'when maven_central_request_forwarding is disabled' do
+ let(:package_type) { :maven }
+ let(:options) { { project: project } }
+
+ include_context 'dependency proxy helpers context'
+
+ where(:application_setting, :forward_to_registry) do
+ true | true
+ true | false
+ false | true
+ false | false
+ end
+
+ with_them do
+ before do
+ stub_feature_flags(maven_central_request_forwarding: false)
+ allow_fetch_application_setting(attribute: "maven_package_requests_forwarding", return_value: application_setting)
+ end
+
+ it_behaves_like 'executing fallback'
+ end
+ end
end
context 'with non-forwardable package type' do
let(:forward_to_registry) { true }
before do
+ stub_application_setting(maven_package_requests_forwarding: true)
stub_application_setting(npm_package_requests_forwarding: true)
stub_application_setting(pypi_package_requests_forwarding: true)
end
- Packages::Package.package_types.keys.without('npm', 'pypi').each do |pkg_type|
+ Packages::Package.package_types.keys.without('maven', 'npm', 'pypi').each do |pkg_type|
context "#{pkg_type}" do
let(:package_type) { pkg_type.to_sym }
@@ -81,18 +108,21 @@ RSpec.describe API::Helpers::Packages::DependencyProxyHelpers do
end
describe '#registry_url' do
- subject { helper.registry_url(package_type, package_name: 'test') }
+ subject { helper.registry_url(package_type, options) }
- where(:package_type, :expected_result) do
- :npm | 'https://registry.npmjs.org/test'
- :pypi | 'https://pypi.org/simple/test/'
+ where(:package_type, :expected_result, :params) do
+ :maven | 'https://repo.maven.apache.org/maven2/test/123' | { path: 'test', file_name: '123', project: project }
+ :npm | 'https://registry.npmjs.org/test' | { package_name: 'test' }
+ :pypi | 'https://pypi.org/simple/test/' | { package_name: 'test' }
end
with_them do
+ let(:options) { params }
+
it { is_expected.to eq(expected_result) }
end
- Packages::Package.package_types.keys.without('npm', 'pypi').each do |pkg_type|
+ Packages::Package.package_types.keys.without('maven', 'npm', 'pypi').each do |pkg_type|
context "with non-forwardable package type #{pkg_type}" do
let(:package_type) { pkg_type }
diff --git a/spec/lib/api/helpers/packages_helpers_spec.rb b/spec/lib/api/helpers/packages_helpers_spec.rb
index 0c51e25bad9..cd6e718ce98 100644
--- a/spec/lib/api/helpers/packages_helpers_spec.rb
+++ b/spec/lib/api/helpers/packages_helpers_spec.rb
@@ -5,6 +5,8 @@ require 'spec_helper'
RSpec.describe API::Helpers::PackagesHelpers do
let_it_be(:helper) { Class.new.include(described_class).new }
let_it_be(:project) { create(:project) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:package) { create(:package) }
describe 'authorize_packages_access!' do
subject { helper.authorize_packages_access!(project) }
@@ -17,7 +19,45 @@ RSpec.describe API::Helpers::PackagesHelpers do
end
end
- %i[read_package create_package destroy_package].each do |action|
+ describe 'authorize_read_package!' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:subject, :expected_class) do
+ ref(:project) | ::Packages::Policies::Project
+ ref(:group) | ::Packages::Policies::Group
+ ref(:package) | ::Packages::Package
+ end
+
+ with_them do
+ it 'calls authorize! with correct subject' do
+ expect(helper).to receive(:authorize!).with(:read_package, have_attributes(id: subject.id, class: expected_class))
+
+ expect(helper.send('authorize_read_package!', subject)).to eq nil
+ end
+ end
+
+ context 'with feature flag disabled' do
+ before do
+ stub_feature_flags(read_package_policy_rule: false)
+ end
+
+ where(:subject, :expected_class) do
+ ref(:project) | ::Project
+ ref(:group) | ::Group
+ ref(:package) | ::Packages::Package
+ end
+
+ with_them do
+ it 'calls authorize! with correct subject' do
+ expect(helper).to receive(:authorize!).with(:read_package, have_attributes(id: subject.id, class: expected_class))
+
+ expect(helper.send('authorize_read_package!', subject)).to eq nil
+ end
+ end
+ end
+ end
+
+ %i[create_package destroy_package].each do |action|
describe "authorize_#{action}!" do
subject { helper.send("authorize_#{action}!", project) }
@@ -40,7 +80,7 @@ RSpec.describe API::Helpers::PackagesHelpers do
context 'with packages enabled' do
it "doesn't call not_found!" do
- expect(helper).to receive(:not_found!).never
+ expect(helper).not_to receive(:not_found!)
expect(subject).to eq nil
end
diff --git a/spec/lib/api/helpers/pagination_spec.rb b/spec/lib/api/helpers/pagination_spec.rb
index a008c1adeac..ae6af5b540e 100644
--- a/spec/lib/api/helpers/pagination_spec.rb
+++ b/spec/lib/api/helpers/pagination_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe API::Helpers::Pagination do
subject { Class.new.include(described_class).new }
diff --git a/spec/lib/api/helpers_spec.rb b/spec/lib/api/helpers_spec.rb
index cd41d362d03..f25c75ef93c 100644
--- a/spec/lib/api/helpers_spec.rb
+++ b/spec/lib/api/helpers_spec.rb
@@ -865,4 +865,93 @@ RSpec.describe API::Helpers do
helper.bad_request!('custom reason')
end
end
+
+ describe '#authenticate_by_gitlab_shell_token!' do
+ include GitlabShellHelpers
+
+ let(:valid_secret_token) { 'valid' }
+ let(:invalid_secret_token) { 'invalid' }
+ let(:headers) { {} }
+ let(:params) { {} }
+
+ shared_examples 'authorized' do
+ it 'authorized' do
+ expect(helper).not_to receive(:unauthorized!)
+
+ helper.authenticate_by_gitlab_shell_token!
+ end
+ end
+
+ shared_examples 'unauthorized' do
+ it 'unauthorized' do
+ expect(helper).to receive(:unauthorized!)
+
+ helper.authenticate_by_gitlab_shell_token!
+ end
+ end
+
+ before do
+ allow(Gitlab::Shell).to receive(:secret_token).and_return(valid_secret_token)
+ allow(helper).to receive_messages(params: params, headers: headers, secret_token: valid_secret_token)
+ end
+
+ context 'when jwt token is not provided' do
+ it_behaves_like 'unauthorized'
+ end
+
+ context 'when jwt token is invalid' do
+ let(:headers) { gitlab_shell_internal_api_request_header(secret_token: invalid_secret_token) }
+
+ it_behaves_like 'unauthorized'
+ end
+
+ context 'when jwt token issuer is invalid' do
+ let(:headers) { gitlab_shell_internal_api_request_header(issuer: 'gitlab-workhorse') }
+
+ it_behaves_like 'unauthorized'
+ end
+
+ context 'when jwt token is valid' do
+ let(:headers) { gitlab_shell_internal_api_request_header }
+
+ it_behaves_like 'authorized'
+ end
+
+ context 'when gitlab_shell_jwt_token is disabled' do
+ let(:valid_secret_token) { +'valid' } # mutable string to use chomp!
+ let(:invalid_secret_token) { +'invalid' } # mutable string to use chomp!
+
+ before do
+ stub_feature_flags(gitlab_shell_jwt_token: false)
+ end
+
+ context 'when shared secret is not provided' do
+ it_behaves_like 'unauthorized'
+ end
+
+ context 'when shared secret provided via params' do
+ let(:params) { { 'secret_token' => valid_secret_token } }
+
+ it_behaves_like 'authorized'
+
+ context 'but it is invalid' do
+ let(:params) { { 'secret_token' => invalid_secret_token } }
+
+ it_behaves_like 'unauthorized'
+ end
+ end
+
+ context 'when shared secret provided via headers' do
+ let(:headers) { { described_class::GITLAB_SHARED_SECRET_HEADER => Base64.encode64(valid_secret_token) } }
+
+ it_behaves_like 'authorized'
+
+ context 'but it is invalid' do
+ let(:headers) { { described_class::GITLAB_SHARED_SECRET_HEADER => Base64.encode64(invalid_secret_token) } }
+
+ it_behaves_like 'unauthorized'
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/api/integrations/slack/events/url_verification_spec.rb b/spec/lib/api/integrations/slack/events/url_verification_spec.rb
deleted file mode 100644
index 2778f0d708d..00000000000
--- a/spec/lib/api/integrations/slack/events/url_verification_spec.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe API::Integrations::Slack::Events::UrlVerification do
- describe '.call' do
- it 'returns the challenge' do
- expect(described_class.call({ challenge: 'foo' })).to eq({ challenge: 'foo' })
- end
- end
-end
diff --git a/spec/lib/backup/database_backup_error_spec.rb b/spec/lib/backup/database_backup_error_spec.rb
index ef627900050..e001f65465c 100644
--- a/spec/lib/backup/database_backup_error_spec.rb
+++ b/spec/lib/backup/database_backup_error_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Backup::DatabaseBackupError do
let(:config) do
diff --git a/spec/lib/backup/gitaly_backup_spec.rb b/spec/lib/backup/gitaly_backup_spec.rb
index d427e41026e..6b0747735ed 100644
--- a/spec/lib/backup/gitaly_backup_spec.rb
+++ b/spec/lib/backup/gitaly_backup_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe Backup::GitalyBackup do
let(:expected_env) do
{
'SSL_CERT_FILE' => Gitlab::X509::Certificate.default_cert_file,
- 'SSL_CERT_DIR' => Gitlab::X509::Certificate.default_cert_dir
+ 'SSL_CERT_DIR' => Gitlab::X509::Certificate.default_cert_dir
}.merge(ENV)
end
@@ -121,7 +121,7 @@ RSpec.describe Backup::GitalyBackup do
let(:ssl_env) do
{
'SSL_CERT_FILE' => '/some/cert/file',
- 'SSL_CERT_DIR' => '/some/cert'
+ 'SSL_CERT_DIR' => '/some/cert'
}
end
diff --git a/spec/lib/backup/task_spec.rb b/spec/lib/backup/task_spec.rb
index 80f1fe01b78..1de99729512 100644
--- a/spec/lib/backup/task_spec.rb
+++ b/spec/lib/backup/task_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Backup::Task do
let(:progress) { StringIO.new }
diff --git a/spec/lib/banzai/color_parser_spec.rb b/spec/lib/banzai/color_parser_spec.rb
index 95b3955d8fe..3914aee2d4c 100644
--- a/spec/lib/banzai/color_parser_spec.rb
+++ b/spec/lib/banzai/color_parser_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Banzai::ColorParser do
describe '.parse' do
diff --git a/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb b/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb
index 2d326bd77a6..5712ed7da1f 100644
--- a/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb
+++ b/spec/lib/banzai/filter/blockquote_fence_filter_spec.rb
@@ -14,6 +14,10 @@ RSpec.describe Banzai::Filter::BlockquoteFenceFilter do
expect(output).to eq(expected)
end
+ it 'does not require newlines at start or end of string' do
+ expect(filter(">>>\ntest\n>>>")).to eq("\n> test\n")
+ end
+
it 'allows trailing whitespace on blockquote fence lines' do
expect(filter(">>> \ntest\n>>> ")).to eq("\n> test\n")
end
diff --git a/spec/lib/banzai/filter/kroki_filter_spec.rb b/spec/lib/banzai/filter/kroki_filter_spec.rb
index 1fb61ad1991..3f4f3aafdd6 100644
--- a/spec/lib/banzai/filter/kroki_filter_spec.rb
+++ b/spec/lib/banzai/filter/kroki_filter_spec.rb
@@ -46,4 +46,12 @@ RSpec.describe Banzai::Filter::KrokiFilter do
expect(doc.to_s).to start_with '<img src="http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KyJyVNQiE5KTSxKidXVjS5ILCrKL4lFFrSyi07LL81RyM0vLckAysRGjxo8avCowaMGjxo8avCowaMGU8lgAE7mIdc=" hidden="" class="js-render-kroki" data-diagram="nomnoml" data-diagram-src="data:text/plain;base64,W1BpcmF0ZXxleWVDb3VudDog'
end
+
+ it 'allows the lang attribute on the code tag to support RST files processed by gitlab-markup gem' do
+ stub_application_setting(kroki_enabled: true, kroki_url: "http://localhost:8000")
+ text = '[Pirate|eyeCount: Int|raid();pillage()|\n [beard]--[parrot]\n [beard]-:>[foul mouth]\n]' * 25
+ doc = filter("<pre><code lang='nomnoml'>#{text}</code></pre>")
+
+ expect(doc.to_s).to start_with '<img src="http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KyJyVNQiE5KTSxKidXVjS5ILCrKL4lFFrSyi07LL81RyM0vLckAysRGjxo8avCowaMGjxo8avCowaMGU8lgAE7mIdc=" hidden="" class="js-render-kroki" data-diagram="nomnoml" data-diagram-src="data:text/plain;base64,W1BpcmF0ZXxleWVDb3VudDog'
+ end
end
diff --git a/spec/lib/banzai/filter/math_filter_spec.rb b/spec/lib/banzai/filter/math_filter_spec.rb
index 128f8532d39..dd116eb1109 100644
--- a/spec/lib/banzai/filter/math_filter_spec.rb
+++ b/spec/lib/banzai/filter/math_filter_spec.rb
@@ -3,128 +3,179 @@
require 'spec_helper'
RSpec.describe Banzai::Filter::MathFilter do
+ using RSpec::Parameterized::TableSyntax
include FilterSpecHelper
- it 'leaves regular inline code unchanged' do
- input = "<code>2+2</code>"
- doc = filter(input)
-
- expect(doc.to_s).to eq input
- end
-
- it 'removes surrounding dollar signs and adds class code, math and js-render-math' do
- doc = filter("$<code>2+2</code>$")
-
- expect(doc.to_s).to eq '<code class="code math js-render-math" data-math-style="inline">2+2</code>'
- end
-
- it 'only removes surrounding dollar signs' do
- doc = filter("test $<code>2+2</code>$ test")
- before = doc.xpath('descendant-or-self::text()[1]').first
- after = doc.xpath('descendant-or-self::text()[3]').first
-
- expect(before.to_s).to eq 'test '
- expect(after.to_s).to eq ' test'
- end
-
- it 'only removes surrounding single dollar sign' do
- doc = filter("test $$<code>2+2</code>$$ test")
- before = doc.xpath('descendant-or-self::text()[1]').first
- after = doc.xpath('descendant-or-self::text()[3]').first
-
- expect(before.to_s).to eq 'test $'
- expect(after.to_s).to eq '$ test'
- end
-
- it 'adds data-math-style inline attribute to inline math' do
- doc = filter('$<code>2+2</code>$')
- code = doc.xpath('descendant-or-self::code').first
-
- expect(code['data-math-style']).to eq 'inline'
- end
-
- it 'adds class code and math to inline math' do
- doc = filter('$<code>2+2</code>$')
- code = doc.xpath('descendant-or-self::code').first
-
- expect(code[:class]).to include("code")
- expect(code[:class]).to include("math")
- end
-
- it 'adds js-render-math class to inline math' do
- doc = filter('$<code>2+2</code>$')
- code = doc.xpath('descendant-or-self::code').first
-
- expect(code[:class]).to include("js-render-math")
- end
-
- # Cases with faulty syntax. Should be a no-op
-
- it 'ignores cases with missing dolar sign at the end' do
- input = "test $<code>2+2</code> test"
- doc = filter(input)
-
- expect(doc.to_s).to eq input
- end
-
- it 'ignores cases with missing dolar sign at the beginning' do
- input = "test <code>2+2</code>$ test"
- doc = filter(input)
-
- expect(doc.to_s).to eq input
- end
-
- it 'ignores dollar signs if it is not adjacent' do
- input = '<p>We check strictly $<code>2+2</code> and <code>2+2</code>$ </p>'
- doc = filter(input)
-
- expect(doc.to_s).to eq input
- end
-
- it 'ignores dollar signs if they are inside another element' do
- input = '<p>We check strictly <em>$</em><code>2+2</code><em>$</em></p>'
- doc = filter(input)
-
- expect(doc.to_s).to eq input
- end
-
- # Display math
-
- it 'adds data-math-style display attribute to display math' do
- doc = filter('<pre class="code highlight js-syntax-highlight language-math" v-pre="true"><code>2+2</code></pre>')
- pre = doc.xpath('descendant-or-self::pre').first
-
- expect(pre['data-math-style']).to eq 'display'
- end
-
- it 'adds js-render-math class to display math' do
- doc = filter('<pre class="code highlight js-syntax-highlight language-math" v-pre="true"><code>2+2</code></pre>')
- pre = doc.xpath('descendant-or-self::pre').first
-
- expect(pre[:class]).to include("js-render-math")
- end
-
- it 'ignores code blocks that are not math' do
- input = '<pre class="code highlight js-syntax-highlight language-plaintext" v-pre="true"><code>2+2</code></pre>'
- doc = filter(input)
-
- expect(doc.to_s).to eq input
- end
-
- it 'requires the pre to contain both code and math' do
- input = '<pre class="highlight js-syntax-highlight language-plaintext language-math" v-pre="true"><code>2+2</code></pre>'
- doc = filter(input)
-
- expect(doc.to_s).to eq input
- end
-
- it 'dollar signs around to display math' do
- doc = filter('$<pre class="code highlight js-syntax-highlight language-math" v-pre="true"><code>2+2</code></pre>$')
- before = doc.xpath('descendant-or-self::text()[1]').first
- after = doc.xpath('descendant-or-self::text()[3]').first
-
- expect(before.to_s).to eq '$'
- expect(after.to_s).to eq '$'
+ shared_examples 'inline math' do
+ it 'removes surrounding dollar signs and adds class code, math and js-render-math' do
+ doc = filter(text)
+ expected = result_template.gsub('<math>', '<code class="code math js-render-math" data-math-style="inline">')
+ expected.gsub!('</math>', '</code>')
+
+ expect(doc.to_s).to eq expected
+ end
+ end
+
+ shared_examples 'display math' do
+ let_it_be(:template_prefix_with_pre) { '<pre class="code math js-render-math" data-math-style="display"><code>' }
+ let_it_be(:template_prefix_with_code) { '<code class="code math js-render-math" data-math-style="display">' }
+ let(:use_pre_tags) { false }
+
+ it 'removes surrounding dollar signs and adds class code, math and js-render-math' do
+ doc = filter(text)
+
+ template_prefix = use_pre_tags ? template_prefix_with_pre : template_prefix_with_code
+ template_suffix = "</code>#{'</pre>' if use_pre_tags}"
+ expected = result_template.gsub('<math>', template_prefix)
+ expected.gsub!('</math>', template_suffix)
+
+ expect(doc.to_s).to eq expected
+ end
+ end
+
+ describe 'inline math using $...$ syntax' do
+ context 'with valid syntax' do
+ where(:text, :result_template) do
+ '$2+2$' | '<math>2+2</math>'
+ '$22+1$ and $22 + a^2$' | '<math>22+1</math> and <math>22 + a^2</math>'
+ '$22 and $2+2$' | '$22 and <math>2+2</math>'
+ '$2+2$ $22 and flightjs/Flight$22 $2+2$' | '<math>2+2</math> $22 and flightjs/Flight$22 <math>2+2</math>'
+ '$1/2$ &lt;b&gt;test&lt;/b&gt;' | '<math>1/2</math> &lt;b&gt;test&lt;/b&gt;'
+ '$a!$' | '<math>a!</math>'
+ '$x$' | '<math>x</math>'
+ end
+
+ with_them do
+ it_behaves_like 'inline math'
+ end
+ end
+
+ it 'does not handle dollar literals properly' do
+ doc = filter('$20+30\$$')
+ expected = '<code class="code math js-render-math" data-math-style="inline">20+30\\</code>$'
+
+ expect(doc.to_s).to eq expected
+ end
+ end
+
+ describe 'inline math using $`...`$ syntax' do
+ context 'with valid syntax' do
+ where(:text, :result_template) do
+ '$<code>2+2</code>$' | '<math>2+2</math>'
+ '$<code>22+1</code>$ and $<code>22 + a^2</code>$' | '<math>22+1</math> and <math>22 + a^2</math>'
+ '$22 and $<code>2+2</code>$' | '$22 and <math>2+2</math>'
+ '$<code>2+2</code>$ $22 and flightjs/Flight$22 $<code>2+2</code>$' | '<math>2+2</math> $22 and flightjs/Flight$22 <math>2+2</math>'
+ 'test $$<code>2+2</code>$$ test' | 'test $<math>2+2</math>$ test'
+ end
+
+ with_them do
+ it_behaves_like 'inline math'
+ end
+ end
+ end
+
+ describe 'inline display math using $$...$$ syntax' do
+ context 'with valid syntax' do
+ where(:text, :result_template) do
+ '$$2+2$$' | '<math>2+2</math>'
+ '$$ 2+2 $$' | '<math>2+2</math>'
+ '$$22+1$$ and $$22 + a^2$$' | '<math>22+1</math> and <math>22 + a^2</math>'
+ '$22 and $$2+2$$' | '$22 and <math>2+2</math>'
+ '$$2+2$$ $22 and flightjs/Flight$22 $$2+2$$' | '<math>2+2</math> $22 and flightjs/Flight$22 <math>2+2</math>'
+ 'flightjs/Flight$22 and $$a^2 + b^2 = c^2$$' | 'flightjs/Flight$22 and <math>a^2 + b^2 = c^2</math>'
+ '$$a!$$' | '<math>a!</math>'
+ '$$x$$' | '<math>x</math>'
+ '$$20,000 and $$30,000' | '<math>20,000 and</math>30,000'
+ end
+
+ with_them do
+ it_behaves_like 'display math'
+ end
+ end
+ end
+
+ describe 'block display math using $$\n...\n$$ syntax' do
+ context 'with valid syntax' do
+ where(:text, :result_template) do
+ "$$\n2+2\n$$" | "<math>2+2</math>"
+ end
+
+ with_them do
+ it_behaves_like 'display math' do
+ let(:use_pre_tags) { true }
+ end
+ end
+ end
+ end
+
+ describe 'display math using ```math...``` syntax' do
+ it 'adds data-math-style display attribute to display math' do
+ doc = filter('<pre class="code highlight js-syntax-highlight language-math" v-pre="true"><code>2+2</code></pre>')
+ pre = doc.xpath('descendant-or-self::pre').first
+
+ expect(pre['data-math-style']).to eq 'display'
+ end
+
+ it 'adds js-render-math class to display math' do
+ doc = filter('<pre class="code highlight js-syntax-highlight language-math" v-pre="true"><code>2+2</code></pre>')
+ pre = doc.xpath('descendant-or-self::pre').first
+
+ expect(pre[:class]).to include("js-render-math")
+ end
+
+ it 'ignores code blocks that are not math' do
+ input = '<pre class="code highlight js-syntax-highlight language-plaintext" v-pre="true"><code>2+2</code></pre>'
+ doc = filter(input)
+
+ expect(doc.to_s).to eq input
+ end
+
+ it 'requires the pre to contain both code and math' do
+ input = '<pre class="highlight js-syntax-highlight language-plaintext language-math" v-pre="true"><code>2+2</code></pre>'
+ doc = filter(input)
+
+ expect(doc.to_s).to eq input
+ end
+
+ it 'dollar signs around to display math' do
+ doc = filter('$<pre class="code highlight js-syntax-highlight language-math" v-pre="true"><code>2+2</code></pre>$')
+ before = doc.xpath('descendant-or-self::text()[1]').first
+ after = doc.xpath('descendant-or-self::text()[3]').first
+
+ expect(before.to_s).to eq '$'
+ expect(after.to_s).to eq '$'
+ end
+ end
+
+ describe 'unrecognized syntax' do
+ where(:text) do
+ [
+ '<code>2+2</code>',
+ 'test $<code>2+2</code> test',
+ 'test <code>2+2</code>$ test',
+ '<em>$</em><code>2+2</code><em>$</em>',
+ '$20,000 and $30,000',
+ '$20,000 in $USD',
+ '$ a^2 $',
+ "test $$\n2+2\n$$",
+ "$\n$",
+ '$$$'
+ ]
+ end
+
+ with_them do
+ it 'is ignored' do
+ expect(filter(text).to_s).to eq text
+ end
+ end
+ end
+
+ it 'handles multiple styles in one text block' do
+ doc = filter('$<code>2+2</code>$ + $3+3$ + $$4+4$$')
+
+ expect(doc.search('.js-render-math').count).to eq(3)
+ expect(doc.search('[data-math-style="inline"]').count).to eq(2)
+ expect(doc.search('[data-math-style="display"]').count).to eq(1)
end
it 'limits how many elements can be marked as math' do
@@ -134,4 +185,11 @@ RSpec.describe Banzai::Filter::MathFilter do
expect(doc.search('.js-render-math').count).to eq(2)
end
+
+ it 'does not recognize new syntax when feature flag is off' do
+ stub_feature_flags(markdown_dollar_math: false)
+ doc = filter('$1+2$')
+
+ expect(doc.to_s).to eq '$1+2$'
+ end
end
diff --git a/spec/lib/banzai/filter/output_safety_spec.rb b/spec/lib/banzai/filter/output_safety_spec.rb
index 5b7b7298411..8186935f4b2 100644
--- a/spec/lib/banzai/filter/output_safety_spec.rb
+++ b/spec/lib/banzai/filter/output_safety_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Banzai::Filter::OutputSafety do
subject do
diff --git a/spec/lib/banzai/filter/plantuml_filter_spec.rb b/spec/lib/banzai/filter/plantuml_filter_spec.rb
index dcfeb2ce3ba..4373af90cde 100644
--- a/spec/lib/banzai/filter/plantuml_filter_spec.rb
+++ b/spec/lib/banzai/filter/plantuml_filter_spec.rb
@@ -15,6 +15,16 @@ RSpec.describe Banzai::Filter::PlantumlFilter do
expect(doc.to_s).to eq output
end
+ it 'allows the lang attribute on the code tag to support RST files processed by gitlab-markup gem' do
+ stub_application_setting(plantuml_enabled: true, plantuml_url: "http://localhost:8080")
+
+ input = '<pre><code lang="plantuml">Bob -> Sara : Hello</code></pre>'
+ output = '<img class="plantuml" src="http://localhost:8080/png/U9npoazIqBLJ24uiIbImKl18pSd91m0rkGMq" data-diagram="plantuml" data-diagram-src="data:text/plain;base64,Qm9iIC0+IFNhcmEgOiBIZWxsbw==">'
+ doc = filter(input)
+
+ expect(doc.to_s).to eq output
+ end
+
it 'does not replace plantuml pre tag with img tag if disabled' do
stub_application_setting(plantuml_enabled: false)
diff --git a/spec/lib/banzai/filter/repository_link_filter_spec.rb b/spec/lib/banzai/filter/repository_link_filter_spec.rb
index 815053aac2f..c220263b238 100644
--- a/spec/lib/banzai/filter/repository_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/repository_link_filter_spec.rb
@@ -8,14 +8,14 @@ RSpec.describe Banzai::Filter::RepositoryLinkFilter do
def filter(doc, contexts = {})
contexts.reverse_merge!({
- commit: commit,
- project: project,
- current_user: user,
- group: group,
- wiki: wiki,
- ref: ref,
+ commit: commit,
+ project: project,
+ current_user: user,
+ group: group,
+ wiki: wiki,
+ ref: ref,
requested_path: requested_path,
- only_path: only_path
+ only_path: only_path
})
described_class.call(doc, contexts)
diff --git a/spec/lib/banzai/filter_array_spec.rb b/spec/lib/banzai/filter_array_spec.rb
index 47bc5633300..f341d5d51a0 100644
--- a/spec/lib/banzai/filter_array_spec.rb
+++ b/spec/lib/banzai/filter_array_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Banzai::FilterArray do
describe '#insert_after' do
diff --git a/spec/lib/banzai/pipeline/pre_process_pipeline_spec.rb b/spec/lib/banzai/pipeline/pre_process_pipeline_spec.rb
index 5021ef3a79a..303d0fcb6c2 100644
--- a/spec/lib/banzai/pipeline/pre_process_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/pre_process_pipeline_spec.rb
@@ -32,4 +32,21 @@ RSpec.describe Banzai::Pipeline::PreProcessPipeline do
expect(result[:output]).to eq('foo foo f...')
end
+
+ context 'when multiline blockquote' do
+ it 'data-sourcepos references correct line in source markdown' do
+ markdown = <<~MD
+ >>>
+ foo
+ >>>
+ MD
+
+ pipeline_output = described_class.call(markdown, {})[:output]
+ pipeline_output = Banzai::Pipeline::PlainMarkdownPipeline.call(pipeline_output, {})[:output]
+ sourcepos = pipeline_output.at('blockquote')['data-sourcepos']
+ source_line = sourcepos.split(':').first.to_i
+
+ expect(markdown.lines[source_line - 1]).to eq "foo\n"
+ end
+ end
end
diff --git a/spec/lib/banzai/pipeline_spec.rb b/spec/lib/banzai/pipeline_spec.rb
index 7d4df2ca5ce..b2c970e4394 100644
--- a/spec/lib/banzai/pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Banzai::Pipeline do
describe '.[]' do
diff --git a/spec/lib/banzai/querying_spec.rb b/spec/lib/banzai/querying_spec.rb
index b76f6ec533c..fc7aaa94954 100644
--- a/spec/lib/banzai/querying_spec.rb
+++ b/spec/lib/banzai/querying_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Banzai::Querying do
describe '.css' do
diff --git a/spec/lib/banzai/renderer_spec.rb b/spec/lib/banzai/renderer_spec.rb
index ae9cf4c5068..705f44baf16 100644
--- a/spec/lib/banzai/renderer_spec.rb
+++ b/spec/lib/banzai/renderer_spec.rb
@@ -76,7 +76,7 @@ RSpec.describe Banzai::Renderer do
let(:object) { fake_object(fresh: true) }
it 'uses the cache' do
- expect(object).to receive(:refresh_markdown_cache!).never
+ expect(object).not_to receive(:refresh_markdown_cache!)
is_expected.to eq('field_html')
end
diff --git a/spec/lib/bitbucket/collection_spec.rb b/spec/lib/bitbucket/collection_spec.rb
index 349274585c4..715b78c95eb 100644
--- a/spec/lib/bitbucket/collection_spec.rb
+++ b/spec/lib/bitbucket/collection_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
# Emulates paginator. It returns 2 pages with results
class TestPaginator
diff --git a/spec/lib/bitbucket/page_spec.rb b/spec/lib/bitbucket/page_spec.rb
index 1d599007d9e..46ab5a45551 100644
--- a/spec/lib/bitbucket/page_spec.rb
+++ b/spec/lib/bitbucket/page_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Bitbucket::Page do
let(:response) { { 'values' => [{ 'username' => 'Ben' }], 'pagelen' => 2, 'next' => '' } }
diff --git a/spec/lib/bitbucket/paginator_spec.rb b/spec/lib/bitbucket/paginator_spec.rb
index e74af8a264b..3285fae5b82 100644
--- a/spec/lib/bitbucket/paginator_spec.rb
+++ b/spec/lib/bitbucket/paginator_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Bitbucket::Paginator do
let(:last_page) { double(:page, next?: false, items: ['item_2']) }
diff --git a/spec/lib/bitbucket/representation/comment_spec.rb b/spec/lib/bitbucket/representation/comment_spec.rb
index f6766ab685b..d108bcfe767 100644
--- a/spec/lib/bitbucket/representation/comment_spec.rb
+++ b/spec/lib/bitbucket/representation/comment_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Bitbucket::Representation::Comment do
describe '#author' do
diff --git a/spec/lib/bitbucket/representation/issue_spec.rb b/spec/lib/bitbucket/representation/issue_spec.rb
index 8c27086546f..a40bbcb7bf8 100644
--- a/spec/lib/bitbucket/representation/issue_spec.rb
+++ b/spec/lib/bitbucket/representation/issue_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Bitbucket::Representation::Issue do
describe '#iid' do
diff --git a/spec/lib/bitbucket/representation/pull_request_comment_spec.rb b/spec/lib/bitbucket/representation/pull_request_comment_spec.rb
index cdab683492f..e748cd7b955 100644
--- a/spec/lib/bitbucket/representation/pull_request_comment_spec.rb
+++ b/spec/lib/bitbucket/representation/pull_request_comment_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Bitbucket::Representation::PullRequestComment do
describe '#iid' do
diff --git a/spec/lib/bitbucket/representation/pull_request_spec.rb b/spec/lib/bitbucket/representation/pull_request_spec.rb
index 6f05d03aa0a..87a9a0fa76d 100644
--- a/spec/lib/bitbucket/representation/pull_request_spec.rb
+++ b/spec/lib/bitbucket/representation/pull_request_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Bitbucket::Representation::PullRequest do
describe '#iid' do
diff --git a/spec/lib/bitbucket/representation/repo_spec.rb b/spec/lib/bitbucket/representation/repo_spec.rb
index a779a153f25..b5b9f45f3d4 100644
--- a/spec/lib/bitbucket/representation/repo_spec.rb
+++ b/spec/lib/bitbucket/representation/repo_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Bitbucket::Representation::Repo do
describe '#has_wiki?' do
diff --git a/spec/lib/bitbucket/representation/user_spec.rb b/spec/lib/bitbucket/representation/user_spec.rb
index e1f6c724da8..62431a5ad8b 100644
--- a/spec/lib/bitbucket/representation/user_spec.rb
+++ b/spec/lib/bitbucket/representation/user_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Bitbucket::Representation::User do
describe '#username' do
diff --git a/spec/lib/bitbucket_server/page_spec.rb b/spec/lib/bitbucket_server/page_spec.rb
index 2d4e946e590..2837f94ba3e 100644
--- a/spec/lib/bitbucket_server/page_spec.rb
+++ b/spec/lib/bitbucket_server/page_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe BitbucketServer::Page do
let(:response) { { 'values' => [{ 'description' => 'Test' }], 'isLastPage' => false, 'nextPageStart' => 2 } }
diff --git a/spec/lib/bulk_imports/file_downloads/filename_fetch_spec.rb b/spec/lib/bulk_imports/file_downloads/filename_fetch_spec.rb
new file mode 100644
index 00000000000..a77eba06027
--- /dev/null
+++ b/spec/lib/bulk_imports/file_downloads/filename_fetch_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BulkImports::FileDownloads::FilenameFetch do
+ let(:dummy_instance) { dummy_class.new }
+ let(:dummy_class) do
+ Class.new do
+ include BulkImports::FileDownloads::FilenameFetch
+ end
+ end
+
+ describe '#raise_error' do
+ it { expect { dummy_instance.raise_error('text') }.to raise_exception(NotImplementedError) }
+ end
+end
diff --git a/spec/lib/bulk_imports/file_downloads/validations_spec.rb b/spec/lib/bulk_imports/file_downloads/validations_spec.rb
new file mode 100644
index 00000000000..85f45c2a8f0
--- /dev/null
+++ b/spec/lib/bulk_imports/file_downloads/validations_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe BulkImports::FileDownloads::Validations do
+ let(:dummy_instance) { dummy_class.new }
+ let(:dummy_class) do
+ Class.new do
+ include BulkImports::FileDownloads::Validations
+ end
+ end
+
+ describe '#raise_error' do
+ it { expect { dummy_instance.raise_error('text') }.to raise_exception(NotImplementedError) }
+ end
+
+ describe '#filepath' do
+ it { expect { dummy_instance.filepath }.to raise_exception(NotImplementedError) }
+ end
+
+ describe '#response_headers' do
+ it { expect { dummy_instance.response_headers }.to raise_exception(NotImplementedError) }
+ end
+
+ describe '#file_size_limit' do
+ it { expect { dummy_instance.file_size_limit }.to raise_exception(NotImplementedError) }
+ end
+end
diff --git a/spec/lib/bulk_imports/pipeline/extracted_data_spec.rb b/spec/lib/bulk_imports/pipeline/extracted_data_spec.rb
index 9c79b3f4c9e..045908de5c4 100644
--- a/spec/lib/bulk_imports/pipeline/extracted_data_spec.rb
+++ b/spec/lib/bulk_imports/pipeline/extracted_data_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe BulkImports::Pipeline::ExtractedData do
let(:data) { 'data' }
diff --git a/spec/lib/bulk_imports/pipeline_spec.rb b/spec/lib/bulk_imports/pipeline_spec.rb
index e4ecf99dab0..dc169bb8d88 100644
--- a/spec/lib/bulk_imports/pipeline_spec.rb
+++ b/spec/lib/bulk_imports/pipeline_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe BulkImports::Pipeline do
let(:context) { instance_double(BulkImports::Pipeline::Context, tracker: nil) }
diff --git a/spec/lib/bulk_imports/projects/pipelines/merge_requests_pipeline_spec.rb b/spec/lib/bulk_imports/projects/pipelines/merge_requests_pipeline_spec.rb
index 3f02356b41e..e780cde4ae2 100644
--- a/spec/lib/bulk_imports/projects/pipelines/merge_requests_pipeline_spec.rb
+++ b/spec/lib/bulk_imports/projects/pipelines/merge_requests_pipeline_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe BulkImports::Projects::Pipelines::MergeRequestsPipeline do
let_it_be(:user) { create(:user) }
+ let_it_be(:another_user) { create(:user) }
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, group: group) }
let_it_be(:bulk_import) { create(:bulk_import, user: user) }
@@ -85,6 +86,9 @@ RSpec.describe BulkImports::Projects::Pipelines::MergeRequestsPipeline do
describe '#run' do
before do
group.add_owner(user)
+ group.add_maintainer(another_user)
+
+ ::BulkImports::UsersMapper.new(context: context).cache_source_user_id(42, another_user.id)
allow_next_instance_of(BulkImports::Common::Extractors::NdjsonExtractor) do |extractor|
allow(extractor).to receive(:remove_tmp_dir)
@@ -293,5 +297,52 @@ RSpec.describe BulkImports::Projects::Pipelines::MergeRequestsPipeline do
expect(imported_mr.milestone.title).to eq(attributes.dig('milestone', 'title'))
end
end
+
+ context 'user assignments' do
+ let(:attributes) do
+ {
+ key => [
+ {
+ 'user_id' => 22,
+ 'created_at' => '2020-01-07T11:21:21.235Z'
+ },
+ {
+ 'user_id' => 42,
+ 'created_at' => '2020-01-08T12:21:21.235Z'
+ }
+ ]
+ }
+ end
+
+ context 'assignees' do
+ let(:key) { 'merge_request_assignees' }
+
+ it 'imports mr assignees' do
+ assignees = imported_mr.merge_request_assignees
+
+ expect(assignees.pluck(:user_id)).to contain_exactly(user.id, another_user.id)
+ end
+ end
+
+ context 'approvals' do
+ let(:key) { 'approvals' }
+
+ it 'imports mr approvals' do
+ approvals = imported_mr.approvals
+
+ expect(approvals.pluck(:user_id)).to contain_exactly(user.id, another_user.id)
+ end
+ end
+
+ context 'reviewers' do
+ let(:key) { 'merge_request_reviewers' }
+
+ it 'imports mr reviewers' do
+ reviewers = imported_mr.merge_request_reviewers
+
+ expect(reviewers.pluck(:user_id)).to contain_exactly(user.id, another_user.id)
+ end
+ end
+ end
end
end
diff --git a/spec/lib/bulk_imports/retry_pipeline_error_spec.rb b/spec/lib/bulk_imports/retry_pipeline_error_spec.rb
index 9d96407b03a..2ff6a7d2b5c 100644
--- a/spec/lib/bulk_imports/retry_pipeline_error_spec.rb
+++ b/spec/lib/bulk_imports/retry_pipeline_error_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe BulkImports::RetryPipelineError do
describe '#retry_delay' do
diff --git a/spec/lib/constraints/jira_encoded_url_constrainer_spec.rb b/spec/lib/constraints/jira_encoded_url_constrainer_spec.rb
index 70e649d35da..f01703033cc 100644
--- a/spec/lib/constraints/jira_encoded_url_constrainer_spec.rb
+++ b/spec/lib/constraints/jira_encoded_url_constrainer_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Constraints::JiraEncodedUrlConstrainer do
let(:namespace_id) { 'group' }
diff --git a/spec/lib/container_registry/gitlab_api_client_spec.rb b/spec/lib/container_registry/gitlab_api_client_spec.rb
index 7836d8706f6..f19bedbda0e 100644
--- a/spec/lib/container_registry/gitlab_api_client_spec.rb
+++ b/spec/lib/container_registry/gitlab_api_client_spec.rb
@@ -421,7 +421,9 @@ RSpec.describe ContainerRegistry::GitlabApiClient do
before do
expect(Auth::ContainerRegistryAuthenticationService).to receive(:pull_nested_repositories_access_token).with(path.downcase).and_return(token)
- stub_repository_details(path, sizing: :self_with_descendants, status_code: 200, respond_with: response)
+ expect_next_instance_of(described_class) do |client|
+ expect(client).to receive(:repository_details).with(path.downcase, sizing: :self_with_descendants).and_return(response.with_indifferent_access).once
+ end
end
it { is_expected.to eq(555) }
diff --git a/spec/lib/container_registry/tag_spec.rb b/spec/lib/container_registry/tag_spec.rb
index 190ddef0cd5..cb5c6a60e1d 100644
--- a/spec/lib/container_registry/tag_spec.rb
+++ b/spec/lib/container_registry/tag_spec.rb
@@ -240,6 +240,31 @@ RSpec.describe ContainerRegistry::Tag do
it_behaves_like 'setting and caching the created_at value'
end
end
+
+ describe 'updated_at=' do
+ subject do
+ tag.updated_at = input
+ tag.updated_at
+ end
+
+ context 'with a valid input' do
+ let(:input) { 2.days.ago.iso8601 }
+
+ it { is_expected.to eq(DateTime.iso8601(input)) }
+ end
+
+ context 'with a nil input' do
+ let(:input) { nil }
+
+ it { is_expected.to eq(nil) }
+ end
+
+ context 'with an invalid input' do
+ let(:input) { 'not a timestamp' }
+
+ it { is_expected.to eq(nil) }
+ end
+ end
end
end
end
diff --git a/spec/lib/declarative_enum_spec.rb b/spec/lib/declarative_enum_spec.rb
index 66cda9fc3a8..06e74b639cf 100644
--- a/spec/lib/declarative_enum_spec.rb
+++ b/spec/lib/declarative_enum_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe DeclarativeEnum do
let(:enum_module) do
diff --git a/spec/lib/error_tracking/sentry_client/event_spec.rb b/spec/lib/error_tracking/sentry_client/event_spec.rb
index 64e674f1e9b..d65bfa31018 100644
--- a/spec/lib/error_tracking/sentry_client/event_spec.rb
+++ b/spec/lib/error_tracking/sentry_client/event_spec.rb
@@ -32,6 +32,7 @@ RSpec.describe ErrorTracking::SentryClient do
subject { client.issue_latest_event(issue_id: issue_id) }
it_behaves_like 'calls sentry api'
+ it_behaves_like 'Sentry API response size limit'
it 'has correct return type' do
expect(subject).to be_a(Gitlab::ErrorTracking::ErrorEvent)
@@ -50,7 +51,7 @@ RSpec.describe ErrorTracking::SentryClient do
end
end
- context 'error object created from sentry response' do
+ context 'with error object created from sentry response' do
it_behaves_like 'assigns error tracking event correctly'
it 'parses the stack trace' do
@@ -58,7 +59,7 @@ RSpec.describe ErrorTracking::SentryClient do
expect(subject.stack_trace_entries).not_to be_empty
end
- context 'error without stack trace' do
+ context 'with error without stack trace' do
before do
sample_response['entries'] = []
stub_sentry_request(sentry_request_url, body: sample_response)
diff --git a/spec/lib/error_tracking/sentry_client/issue_link_spec.rb b/spec/lib/error_tracking/sentry_client/issue_link_spec.rb
index f86d328ef89..75e7ac8304e 100644
--- a/spec/lib/error_tracking/sentry_client/issue_link_spec.rb
+++ b/spec/lib/error_tracking/sentry_client/issue_link_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe ErrorTracking::SentryClient::IssueLink do
let_it_be(:error_tracking_setting) { create(:project_error_tracking_setting, api_url: sentry_url) }
let_it_be(:issue) { create(:issue, project: error_tracking_setting.project) }
+ let(:token) { 'test-token' }
let(:client) { error_tracking_setting.sentry_client }
let(:sentry_issue_id) { 11111111 }
@@ -22,11 +23,12 @@ RSpec.describe ErrorTracking::SentryClient::IssueLink do
subject { client.create_issue_link(integration_id, sentry_issue_id, issue) }
+ it_behaves_like 'Sentry API response size limit'
it_behaves_like 'calls sentry api'
it { is_expected.to be_present }
- context 'redirects' do
+ context 'with redirects' do
let(:sentry_api_url) { sentry_issue_link_url }
it_behaves_like 'no Sentry redirects', :put
@@ -45,11 +47,12 @@ RSpec.describe ErrorTracking::SentryClient::IssueLink do
let(:issue_link_sample_response) { Gitlab::Json.parse(fixture_file('sentry/plugin_link_sample_response.json')) }
let!(:sentry_api_request) { stub_sentry_request(sentry_issue_link_url, :post, body: sentry_api_response) }
+ it_behaves_like 'Sentry API response size limit'
it_behaves_like 'calls sentry api'
it { is_expected.to be_present }
- context 'redirects' do
+ context 'with redirects' do
let(:sentry_api_url) { sentry_issue_link_url }
it_behaves_like 'no Sentry redirects', :post
diff --git a/spec/lib/error_tracking/sentry_client/issue_spec.rb b/spec/lib/error_tracking/sentry_client/issue_spec.rb
index d7bb0ca5c9a..1468a1ff7eb 100644
--- a/spec/lib/error_tracking/sentry_client/issue_spec.rb
+++ b/spec/lib/error_tracking/sentry_client/issue_spec.rb
@@ -58,6 +58,8 @@ RSpec.describe ErrorTracking::SentryClient::Issue do
it_behaves_like 'issues have correct return type', Gitlab::ErrorTracking::Error
it_behaves_like 'issues have correct length', 3
+ it_behaves_like 'maps Sentry exceptions'
+ it_behaves_like 'Sentry API response size limit', enabled_by_default: true
shared_examples 'has correct external_url' do
describe '#external_url' do
@@ -151,7 +153,7 @@ RSpec.describe ErrorTracking::SentryClient::Issue do
context 'with older sentry versions where keys are not present' do
let(:sentry_api_response) do
- issues_sample_response[0...1].map do |issue|
+ issues_sample_response.first(1).map do |issue|
issue[:project].delete(:id)
issue
end
@@ -167,7 +169,7 @@ RSpec.describe ErrorTracking::SentryClient::Issue do
context 'when essential keys are missing in API response' do
let(:sentry_api_response) do
- issues_sample_response[0...1].map do |issue|
+ issues_sample_response.first(1).map do |issue|
issue.except(:id)
end
end
@@ -178,18 +180,6 @@ RSpec.describe ErrorTracking::SentryClient::Issue do
end
end
- context 'when sentry api response is too large' do
- it 'raises exception' do
- deep_size = instance_double(Gitlab::Utils::DeepSize, valid?: false)
- allow(Gitlab::Utils::DeepSize).to receive(:new).with(sentry_api_response).and_return(deep_size)
-
- expect { subject }.to raise_error(ErrorTracking::SentryClient::ResponseInvalidSizeError,
- 'Sentry API response is too big. Limit is 1 MB.')
- end
- end
-
- it_behaves_like 'maps Sentry exceptions'
-
context 'when search term is present' do
let(:search_term) { 'NoMethodError' }
let(:sentry_request_url) { "#{sentry_url}/issues/?limit=20&query=is:unresolved NoMethodError" }
@@ -219,10 +209,14 @@ RSpec.describe ErrorTracking::SentryClient::Issue do
end
let(:sentry_request_url) { "#{sentry_url}/issues/#{issue_id}/" }
- let!(:sentry_api_request) { stub_sentry_request(sentry_request_url, body: issue_sample_response) }
+ let(:sentry_api_response) { issue_sample_response }
+ let!(:sentry_api_request) { stub_sentry_request(sentry_request_url, body: sentry_api_response) }
subject { client.issue_details(issue_id: issue_id) }
+ it_behaves_like 'maps Sentry exceptions'
+ it_behaves_like 'Sentry API response size limit'
+
context 'with error object created from sentry response' do
using RSpec::Parameterized::TableSyntax
@@ -321,6 +315,10 @@ RSpec.describe ErrorTracking::SentryClient::Issue do
subject { client.update_issue(issue_id: issue_id, params: params) }
+ it_behaves_like 'Sentry API response size limit' do
+ let(:sentry_api_response) { {} }
+ end
+
it_behaves_like 'calls sentry api' do
let(:sentry_api_request) { stub_sentry_request(sentry_request_url, :put) }
end
diff --git a/spec/lib/error_tracking/sentry_client/projects_spec.rb b/spec/lib/error_tracking/sentry_client/projects_spec.rb
index 247f9c1c085..52f8cdc915e 100644
--- a/spec/lib/error_tracking/sentry_client/projects_spec.rb
+++ b/spec/lib/error_tracking/sentry_client/projects_spec.rb
@@ -35,10 +35,11 @@ RSpec.describe ErrorTracking::SentryClient::Projects do
it_behaves_like 'has correct return type', Gitlab::ErrorTracking::Project
it_behaves_like 'has correct length', 2
+ it_behaves_like 'Sentry API response size limit'
context 'essential keys missing in API response' do
let(:sentry_api_response) do
- projects_sample_response[0...1].map do |project|
+ projects_sample_response.first(1).map do |project|
project.except(:slug)
end
end
@@ -50,7 +51,7 @@ RSpec.describe ErrorTracking::SentryClient::Projects do
context 'optional keys missing in sentry response' do
let(:sentry_api_response) do
- projects_sample_response[0...1].map do |project|
+ projects_sample_response.first(1).map do |project|
project[:organization].delete(:id)
project.delete(:id)
project.except(:status)
diff --git a/spec/lib/error_tracking/sentry_client/repo_spec.rb b/spec/lib/error_tracking/sentry_client/repo_spec.rb
index 9a1c7a69c3d..445a8e35f8e 100644
--- a/spec/lib/error_tracking/sentry_client/repo_spec.rb
+++ b/spec/lib/error_tracking/sentry_client/repo_spec.rb
@@ -19,12 +19,13 @@ RSpec.describe ErrorTracking::SentryClient::Repo do
subject { client.repos(organization_slug) }
it_behaves_like 'calls sentry api'
+ it_behaves_like 'Sentry API response size limit'
it { is_expected.to all( be_a(Gitlab::ErrorTracking::Repo)) }
it { expect(subject.length).to eq(1) }
- context 'redirects' do
+ context 'with redirects' do
let(:sentry_api_url) { sentry_repos_url }
it_behaves_like 'no Sentry redirects'
diff --git a/spec/lib/error_tracking/sentry_client_spec.rb b/spec/lib/error_tracking/sentry_client_spec.rb
index 9ffd756f057..633b7ae9a91 100644
--- a/spec/lib/error_tracking/sentry_client_spec.rb
+++ b/spec/lib/error_tracking/sentry_client_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe ErrorTracking::SentryClient do
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project' }
diff --git a/spec/lib/gitlab/alert_management/payload/base_spec.rb b/spec/lib/gitlab/alert_management/payload/base_spec.rb
index ad2a3c7b462..3e8d71ac673 100644
--- a/spec/lib/gitlab/alert_management/payload/base_spec.rb
+++ b/spec/lib/gitlab/alert_management/payload/base_spec.rb
@@ -347,4 +347,26 @@ RSpec.describe Gitlab::AlertManagement::Payload::Base do
it { is_expected.to be(true) }
end
+
+ describe '#source' do
+ subject { parsed_payload.source }
+
+ it { is_expected.to be_nil }
+
+ context 'with alerting integration provided' do
+ before do
+ parsed_payload.integration = instance_double('::AlertManagement::HttpIntegration', name: 'INTEGRATION')
+ end
+
+ it { is_expected.to eq('INTEGRATION') }
+ end
+
+ context 'with monitoring tool defined in the raw payload' do
+ before do
+ allow(parsed_payload).to receive(:monitoring_tool).and_return('TOOL')
+ end
+
+ it { is_expected.to eq('TOOL') }
+ end
+ end
end
diff --git a/spec/lib/gitlab/alert_management/payload/generic_spec.rb b/spec/lib/gitlab/alert_management/payload/generic_spec.rb
index 59933f7459d..bc3b6edc638 100644
--- a/spec/lib/gitlab/alert_management/payload/generic_spec.rb
+++ b/spec/lib/gitlab/alert_management/payload/generic_spec.rb
@@ -144,4 +144,40 @@ RSpec.describe Gitlab::AlertManagement::Payload::Generic do
it { is_expected.to eq(value) }
end
end
+
+ describe '#resolved?' do
+ subject { parsed_payload.resolved? }
+
+ context 'without end time' do
+ it { is_expected.to eq(false) }
+ end
+
+ context 'with end time' do
+ let(:raw_payload) { { 'end_time' => Time.current.to_s } }
+
+ it { is_expected.to eq(true) }
+ end
+ end
+
+ describe '#source' do
+ subject { parsed_payload.source }
+
+ it { is_expected.to eq('Generic Alert Endpoint') }
+
+ context 'with alerting integration provided' do
+ before do
+ parsed_payload.integration = instance_double('::AlertManagement::HttpIntegration', name: 'INTEGRATION')
+ end
+
+ it { is_expected.to eq('INTEGRATION') }
+ end
+
+ context 'with monitoring tool defined in the raw payload' do
+ before do
+ allow(parsed_payload).to receive(:monitoring_tool).and_return('TOOL')
+ end
+
+ it { is_expected.to eq('TOOL') }
+ end
+ end
end
diff --git a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb
index 6fc658ecade..1e0034e386e 100644
--- a/spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb
+++ b/spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Analytics::CycleAnalytics::StageEvents::StageEvent do
let(:instance) { described_class.new({}) }
diff --git a/spec/lib/gitlab/analytics/date_filler_spec.rb b/spec/lib/gitlab/analytics/date_filler_spec.rb
new file mode 100644
index 00000000000..3f547f667f2
--- /dev/null
+++ b/spec/lib/gitlab/analytics/date_filler_spec.rb
@@ -0,0 +1,136 @@
+# frozen_string_literal: true
+require 'fast_spec_helper'
+
+RSpec.describe Gitlab::Analytics::DateFiller do
+ let(:default_value) { 0 }
+ let(:formatter) { Gitlab::Analytics::DateFiller::DEFAULT_DATE_FORMATTER }
+
+ subject(:filler_result) do
+ described_class.new(data,
+ from: from,
+ to: to,
+ period: period,
+ default_value: default_value,
+ date_formatter: formatter).fill.to_a
+ end
+
+ context 'when unknown period is given' do
+ it 'raises error' do
+ input = { 3.days.ago.to_date => 10, Date.today => 5 }
+
+ expect do
+ described_class.new(input, from: 4.days.ago, to: Date.today, period: :unknown).fill
+ end.to raise_error(/Unknown period given/)
+ end
+ end
+
+ context 'when period=:day' do
+ let(:from) { Date.new(2021, 5, 25) }
+ let(:to) { Date.new(2021, 6, 5) }
+ let(:period) { :day }
+
+ let(:expected_result) do
+ {
+ Date.new(2021, 5, 25) => 1,
+ Date.new(2021, 5, 26) => default_value,
+ Date.new(2021, 5, 27) => default_value,
+ Date.new(2021, 5, 28) => default_value,
+ Date.new(2021, 5, 29) => default_value,
+ Date.new(2021, 5, 30) => default_value,
+ Date.new(2021, 5, 31) => default_value,
+ Date.new(2021, 6, 1) => default_value,
+ Date.new(2021, 6, 2) => default_value,
+ Date.new(2021, 6, 3) => 10,
+ Date.new(2021, 6, 4) => default_value,
+ Date.new(2021, 6, 5) => default_value
+ }
+ end
+
+ let(:data) do
+ {
+ Date.new(2021, 6, 3) => 10, # deliberatly not sorted
+ Date.new(2021, 5, 27) => nil,
+ Date.new(2021, 5, 25) => 1
+ }
+ end
+
+ it { is_expected.to eq(expected_result.to_a) }
+
+ context 'when a custom default value is given' do
+ let(:default_value) { 'MISSING' }
+
+ it do
+ is_expected.to eq(expected_result.to_a)
+ end
+ end
+
+ context 'when a custom date formatter is given' do
+ let(:formatter) { -> (date) { date.to_s } }
+
+ it do
+ expected_result.transform_keys!(&:to_s)
+
+ is_expected.to eq(expected_result.to_a)
+ end
+ end
+
+ context 'when the data contains dates outside of the requested period' do
+ before do
+ data[Date.new(2022, 6, 1)] = 5
+ end
+
+ it 'raises error' do
+ expect { filler_result }.to raise_error(/Input contains values which doesn't/)
+ end
+ end
+ end
+
+ context 'when period=:week' do
+ let(:from) { Date.new(2021, 5, 16) }
+ let(:to) { Date.new(2021, 6, 7) }
+ let(:period) { :week }
+ let(:data) do
+ {
+ Date.new(2021, 5, 24) => nil,
+ Date.new(2021, 6, 7) => 10
+ }
+ end
+
+ let(:expected_result) do
+ {
+ Date.new(2021, 5, 10) => 0,
+ Date.new(2021, 5, 17) => 0,
+ Date.new(2021, 5, 24) => 0,
+ Date.new(2021, 5, 31) => 0,
+ Date.new(2021, 6, 7) => 10
+ }
+ end
+
+ it do
+ is_expected.to eq(expected_result.to_a)
+ end
+ end
+
+ context 'when period=:month' do
+ let(:from) { Date.new(2021, 5, 1) }
+ let(:to) { Date.new(2021, 7, 1) }
+ let(:period) { :month }
+ let(:data) do
+ {
+ Date.new(2021, 5, 1) => 100
+ }
+ end
+
+ let(:expected_result) do
+ {
+ Date.new(2021, 5, 1) => 100,
+ Date.new(2021, 6, 1) => 0,
+ Date.new(2021, 7, 1) => 0
+ }
+ end
+
+ it do
+ is_expected.to eq(expected_result.to_a)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/application_rate_limiter/base_strategy_spec.rb b/spec/lib/gitlab/application_rate_limiter/base_strategy_spec.rb
index b34ac538b24..12679b51ce9 100644
--- a/spec/lib/gitlab/application_rate_limiter/base_strategy_spec.rb
+++ b/spec/lib/gitlab/application_rate_limiter/base_strategy_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::ApplicationRateLimiter::BaseStrategy do
describe '#increment' do
diff --git a/spec/lib/gitlab/asciidoc/html5_converter_spec.rb b/spec/lib/gitlab/asciidoc/html5_converter_spec.rb
index 84c2cda496e..de1b3e2af71 100644
--- a/spec/lib/gitlab/asciidoc/html5_converter_spec.rb
+++ b/spec/lib/gitlab/asciidoc/html5_converter_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Asciidoc::Html5Converter do
describe 'convert AsciiDoc to HTML5' do
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index b2bce2076b0..8fec8bce23e 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -638,9 +638,9 @@ module Gitlab
context 'with project' do
let(:context) do
{
- commit: commit,
- project: project,
- ref: ref,
+ commit: commit,
+ project: project,
+ ref: ref,
requested_path: requested_path
}
end
diff --git a/spec/lib/gitlab/audit/auditor_spec.rb b/spec/lib/gitlab/audit/auditor_spec.rb
index fc5917ca583..f743515e616 100644
--- a/spec/lib/gitlab/audit/auditor_spec.rb
+++ b/spec/lib/gitlab/audit/auditor_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Audit::Auditor do
let(:name) { 'audit_operation' }
- let(:author) { create(:user) }
+ let(:author) { create(:user, :with_sign_ins) }
let(:group) { create(:group) }
let(:provider) { 'standard' }
let(:context) do
@@ -37,6 +37,13 @@ RSpec.describe Gitlab::Audit::Auditor do
).and_call_original
audit!
+
+ authentication_event = AuthenticationEvent.last
+
+ expect(authentication_event.user).to eq(author)
+ expect(authentication_event.user_name).to eq(author.name)
+ expect(authentication_event.ip_address).to eq(author.current_sign_in_ip)
+ expect(authentication_event.provider).to eq(provider)
end
it 'logs audit events to database', :aggregate_failures do
diff --git a/spec/lib/gitlab/audit/null_target_spec.rb b/spec/lib/gitlab/audit/null_target_spec.rb
index f192e0cd8db..9197b72afd0 100644
--- a/spec/lib/gitlab/audit/null_target_spec.rb
+++ b/spec/lib/gitlab/audit/null_target_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Audit::NullTarget do
subject { described_class.new }
diff --git a/spec/lib/gitlab/audit/type/definition_spec.rb b/spec/lib/gitlab/audit/type/definition_spec.rb
new file mode 100644
index 00000000000..9f4282a4ec0
--- /dev/null
+++ b/spec/lib/gitlab/audit/type/definition_spec.rb
@@ -0,0 +1,219 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Audit::Type::Definition do
+ let(:attributes) do
+ { name: 'group_deploy_token_destroyed',
+ 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',
+ milestone: '15.4',
+ saved_to_database: true,
+ streamed: true }
+ end
+
+ let(:path) { File.join('types', 'group_deploy_token_destroyed.yml') }
+ let(:definition) { described_class.new(path, attributes) }
+ let(:yaml_content) { attributes.deep_stringify_keys.to_yaml }
+
+ describe '#key' do
+ subject { definition.key }
+
+ it 'returns a symbol from name' do
+ is_expected.to eq(:group_deploy_token_destroyed)
+ end
+ end
+
+ describe '#validate!', :aggregate_failures do
+ using RSpec::Parameterized::TableSyntax
+
+ # rubocop:disable Layout/LineLength
+ where(:param, :value, :result) do
+ :path | 'audit_event/types/invalid.yml' | /Audit event type 'group_deploy_token_destroyed' has an invalid path/
+ :name | nil | %r{property '/name' is not of type: string}
+ :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}
+ :milestone | nil | %r{property '/milestone' is not of type: string}
+ end
+ # rubocop:enable Layout/LineLength
+
+ with_them do
+ let(:params) { attributes.merge(path: path) }
+
+ before do
+ params[param] = value
+ end
+
+ it do
+ expect do
+ described_class.new(
+ params[:path], params.except(:path)
+ ).validate!
+ end.to raise_error(result)
+ end
+ end
+
+ context 'when both saved_to_database and streamed are false' do
+ let(:params) { attributes.merge({ path: path, saved_to_database: false, streamed: false }) }
+
+ it 'raises an exception' do
+ expect do
+ described_class.new(
+ params[:path], params.except(:path)
+ ).validate!
+ end.to raise_error(/root is invalid: error_type=not/)
+ end
+ end
+ end
+
+ describe '.paths' do
+ it 'returns at least one path' do
+ expect(described_class.paths).not_to be_empty
+ end
+ end
+
+ describe '.get' do
+ before do
+ allow(described_class).to receive(:definitions) do
+ { definition.key => definition }
+ end
+ end
+
+ context 'when audit event type is not defined' do
+ let(:undefined_audit_event_type) { 'undefined_audit_event_type' }
+
+ it 'returns nil' do
+ expect(described_class.get(undefined_audit_event_type)).to be nil
+ end
+ end
+
+ context 'when audit event type is defined' do
+ let(:audit_event_type) { 'group_deploy_token_destroyed' }
+
+ it 'returns an instance of Gitlab::Audit::Type::Definition' do
+ expect(described_class.get(audit_event_type)).to be_an_instance_of(described_class)
+ end
+
+ it 'returns the properties as defined for that audit event type', :aggregate_failures do
+ audit_event_type_definition = described_class.get(audit_event_type)
+
+ 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.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
+ end
+ end
+ end
+
+ describe '.load_from_file' do
+ it 'properly loads a definition from file' do
+ expect_file_read(path, content: yaml_content)
+
+ expect(described_class.send(:load_from_file, path).attributes)
+ .to eq(definition.attributes)
+ end
+
+ context 'for missing file' do
+ let(:path) { 'missing/audit_events/type/file.yml' }
+
+ it 'raises exception' do
+ expect do
+ described_class.send(:load_from_file, path)
+ end.to raise_error(/Invalid definition for/)
+ end
+ end
+
+ context 'for invalid definition' do
+ it 'raises exception' do
+ expect_file_read(path, content: '{}')
+
+ expect do
+ described_class.send(:load_from_file, path)
+ end.to raise_error(%r{property '/name' is not of type: string})
+ end
+ end
+ end
+
+ describe '.load_all!' do
+ let(:store1) { Dir.mktmpdir('path1') }
+ let(:store2) { Dir.mktmpdir('path2') }
+ let(:definitions) { {} }
+
+ before do
+ allow(described_class).to receive(:paths).and_return(
+ [
+ File.join(store1, '**', '*.yml'),
+ File.join(store2, '**', '*.yml')
+ ]
+ )
+ end
+
+ subject { described_class.send(:load_all!) }
+
+ after do
+ FileUtils.rm_rf(store1)
+ FileUtils.rm_rf(store2)
+ end
+
+ it "when there are no audit event types a list of definitions is empty" do
+ is_expected.to be_empty
+ end
+
+ it "when there's a single audit event type it properly loads them" do
+ write_audit_event_type(store1, path, yaml_content)
+
+ is_expected.to be_one
+ end
+
+ it "when the same audit event type is stored multiple times raises exception" do
+ write_audit_event_type(store1, path, yaml_content)
+ write_audit_event_type(store2, path, yaml_content)
+
+ expect { subject }
+ .to raise_error(/Audit event type 'group_deploy_token_destroyed' is already defined/)
+ end
+
+ it "when one of the YAMLs is invalid it does raise exception" do
+ write_audit_event_type(store1, path, '{}')
+
+ expect { subject }.to raise_error(/Invalid definition for .* '' must match the filename/)
+ end
+ end
+
+ describe '.definitions' do
+ let(:store1) { Dir.mktmpdir('path1') }
+
+ before do
+ allow(described_class).to receive(:paths).and_return(
+ [
+ File.join(store1, '**', '*.yml')
+ ]
+ )
+ end
+
+ subject { described_class.definitions }
+
+ after do
+ FileUtils.rm_rf(store1)
+ end
+
+ it "loads the definitions for all the audit event types" do
+ write_audit_event_type(store1, path, yaml_content)
+
+ is_expected.to be_one
+ end
+ end
+
+ def write_audit_event_type(store, path, content)
+ path = File.join(store, path)
+ dir = File.dirname(path)
+ FileUtils.mkdir_p(dir)
+ File.write(path, content)
+ end
+end
diff --git a/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb b/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb
index 9dff7f7b3dc..e8008aeaf57 100644
--- a/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/auth_hash_spec.rb
@@ -20,17 +20,17 @@ RSpec.describe Gitlab::Auth::Ldap::AuthHash do
let(:info) do
{
- name: 'Smith, J.',
- email: 'johnsmith@example.com',
+ name: 'Smith, J.',
+ email: 'johnsmith@example.com',
nickname: '123456'
}
end
let(:raw_info) do
{
- uid: ['123456'],
- email: ['johnsmith@example.com'],
- cn: ['Smith, J.'],
+ uid: ['123456'],
+ email: ['johnsmith@example.com'],
+ cn: ['Smith, J.'],
fullName: ['John Smith']
}
end
@@ -52,8 +52,8 @@ RSpec.describe Gitlab::Auth::Ldap::AuthHash do
let(:attributes) do
{
- 'username' => %w(mail email),
- 'name' => 'fullName'
+ 'username' => %w(mail email),
+ 'name' => 'fullName'
}
end
diff --git a/spec/lib/gitlab/auth/ldap/config_spec.rb b/spec/lib/gitlab/auth/ldap/config_spec.rb
index 3039fce6141..3be983857bc 100644
--- a/spec/lib/gitlab/auth/ldap/config_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/config_spec.rb
@@ -112,8 +112,8 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'constructs basic options' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 386,
+ 'host' => 'ldap.example.com',
+ 'port' => 386,
'encryption' => 'plain'
}
)
@@ -129,16 +129,16 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'includes failover hosts when set' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'hosts' => [
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'hosts' => [
['ldap1.example.com', 636],
['ldap2.example.com', 636]
],
- 'encryption' => 'simple_tls',
+ 'encryption' => 'simple_tls',
'verify_certificates' => true,
- 'bind_dn' => 'uid=admin,dc=example,dc=com',
- 'password' => 'super_secret'
+ 'bind_dn' => 'uid=admin,dc=example,dc=com',
+ 'password' => 'super_secret'
}
)
@@ -158,12 +158,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'includes authentication options when auth is configured' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
'verify_certificates' => true,
- 'bind_dn' => 'uid=admin,dc=example,dc=com',
- 'password' => 'super_secret'
+ 'bind_dn' => 'uid=admin,dc=example,dc=com',
+ 'password' => 'super_secret'
}
)
@@ -179,9 +179,9 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'sets encryption method to simple_tls when configured as simple_tls' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls'
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls'
}
)
@@ -191,9 +191,9 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'sets encryption method to start_tls when configured as start_tls' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'start_tls'
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'start_tls'
}
)
@@ -203,12 +203,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'transforms SSL cert and key to OpenSSL objects' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'start_tls',
- 'tls_options' => {
- 'cert' => raw_cert,
- 'key' => raw_key
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'start_tls',
+ 'tls_options' => {
+ 'cert' => raw_cert,
+ 'key' => raw_key
}
}
)
@@ -221,12 +221,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
allow(Gitlab::AppLogger).to receive(:error)
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'start_tls',
- 'tls_options' => {
- 'cert' => 'invalid cert',
- 'key' => 'invalid_key'
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'start_tls',
+ 'tls_options' => {
+ 'cert' => 'invalid cert',
+ 'key' => 'invalid_key'
}
}
)
@@ -240,9 +240,9 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'sets tls_options to OpenSSL defaults' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
'verify_certificates' => true
}
)
@@ -255,9 +255,9 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'sets verify_mode to OpenSSL VERIFY_NONE' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
'verify_certificates' => false
}
)
@@ -274,11 +274,11 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'passes it through in tls_options' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
- 'tls_options' => {
- 'ca_file' => '/etc/ca.pem'
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
+ 'tls_options' => {
+ 'ca_file' => '/etc/ca.pem'
}
}
)
@@ -291,11 +291,11 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'does not add the ca_file key to tls_options' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
- 'tls_options' => {
- 'ca_file' => ' '
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
+ 'tls_options' => {
+ 'ca_file' => ' '
}
}
)
@@ -308,11 +308,11 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'passes it through in tls_options' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
- 'tls_options' => {
- 'ssl_version' => 'TLSv1_2'
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
+ 'tls_options' => {
+ 'ssl_version' => 'TLSv1_2'
}
}
)
@@ -325,11 +325,11 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'does not add the ssl_version key to tls_options' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
- 'tls_options' => {
- 'ssl_version' => ' '
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
+ 'tls_options' => {
+ 'ssl_version' => ' '
}
}
)
@@ -343,11 +343,11 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'constructs basic options' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 386,
- 'base' => 'ou=users,dc=example,dc=com',
+ 'host' => 'ldap.example.com',
+ 'port' => 386,
+ 'base' => 'ou=users,dc=example,dc=com',
'encryption' => 'plain',
- 'uid' => 'uid'
+ 'uid' => 'uid'
}
)
@@ -364,10 +364,10 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'includes authentication options when auth is configured' do
stub_ldap_config(
options: {
- 'uid' => 'sAMAccountName',
+ 'uid' => 'sAMAccountName',
'user_filter' => '(memberOf=cn=group1,ou=groups,dc=example,dc=com)',
- 'bind_dn' => 'uid=admin,dc=example,dc=com',
- 'password' => 'super_secret'
+ 'bind_dn' => 'uid=admin,dc=example,dc=com',
+ 'password' => 'super_secret'
}
)
@@ -381,12 +381,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'transforms SSL cert and key to OpenSSL objects' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'start_tls',
- 'tls_options' => {
- 'cert' => raw_cert,
- 'key' => raw_key
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'start_tls',
+ 'tls_options' => {
+ 'cert' => raw_cert,
+ 'key' => raw_key
}
}
)
@@ -399,9 +399,9 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'specifies disable_verify_certificates as false' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
'verify_certificates' => true
}
)
@@ -414,9 +414,9 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'specifies disable_verify_certificates as true' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
'verify_certificates' => false
}
)
@@ -429,12 +429,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'passes it through' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
'verify_certificates' => true,
- 'tls_options' => {
- 'ca_file' => '/etc/ca.pem'
+ 'tls_options' => {
+ 'ca_file' => '/etc/ca.pem'
}
}
)
@@ -447,12 +447,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'does not include the ca_file option' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
'verify_certificates' => true,
- 'tls_options' => {
- 'ca_file' => ' '
+ 'tls_options' => {
+ 'ca_file' => ' '
}
}
)
@@ -465,12 +465,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'passes it through' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
'verify_certificates' => true,
- 'tls_options' => {
- 'ssl_version' => 'TLSv1_2'
+ 'tls_options' => {
+ 'ssl_version' => 'TLSv1_2'
}
}
)
@@ -483,12 +483,12 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'does not include the ssl_version option' do
stub_ldap_config(
options: {
- 'host' => 'ldap.example.com',
- 'port' => 686,
- 'encryption' => 'simple_tls',
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'simple_tls',
'verify_certificates' => true,
- 'tls_options' => {
- 'ssl_version' => ' '
+ 'tls_options' => {
+ 'ssl_version' => ' '
}
}
)
@@ -503,7 +503,7 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'is true when password is set' do
stub_ldap_config(
options: {
- 'bind_dn' => 'uid=admin,dc=example,dc=com',
+ 'bind_dn' => 'uid=admin,dc=example,dc=com',
'password' => 'super_secret'
}
)
@@ -514,7 +514,7 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
it 'is true when bind_dn is set and password is empty' do
stub_ldap_config(
options: {
- 'bind_dn' => 'uid=admin,dc=example,dc=com',
+ 'bind_dn' => 'uid=admin,dc=example,dc=com',
'password' => ''
}
)
@@ -539,15 +539,15 @@ AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
options: {
'attributes' => {
'username' => %w(sAMAccountName),
- 'email' => %w(userPrincipalName)
+ 'email' => %w(userPrincipalName)
}
}
)
expect(config.attributes).to include({
'username' => %w(sAMAccountName),
- 'email' => %w(userPrincipalName),
- 'name' => 'cn'
+ 'email' => %w(userPrincipalName),
+ 'name' => 'cn'
})
end
end
diff --git a/spec/lib/gitlab/auth/ldap/person_spec.rb b/spec/lib/gitlab/auth/ldap/person_spec.rb
index 6857b561370..f8268bb1666 100644
--- a/spec/lib/gitlab/auth/ldap/person_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/person_spec.rb
@@ -10,10 +10,10 @@ RSpec.describe Gitlab::Auth::Ldap::Person do
before do
stub_ldap_config(
options: {
- 'uid' => 'uid',
+ 'uid' => 'uid',
'attributes' => {
- 'name' => 'cn',
- 'email' => %w(mail email userPrincipalName),
+ 'name' => 'cn',
+ 'email' => %w(mail email userPrincipalName),
'username' => username_attribute
}
}
@@ -53,10 +53,10 @@ RSpec.describe Gitlab::Auth::Ldap::Person do
it 'returns a compact and unique array' do
stub_ldap_config(
options: {
- 'uid' => nil,
+ 'uid' => nil,
'attributes' => {
- 'name' => 'cn',
- 'email' => 'mail',
+ 'name' => 'cn',
+ 'email' => 'mail',
'username' => %w(uid mail),
'first_name' => ''
}
diff --git a/spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb b/spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb
index a044094179c..c94f962ee93 100644
--- a/spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb
+++ b/spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb
@@ -40,12 +40,12 @@ RSpec.describe Gitlab::Auth::OAuth::AuthHash do
let(:info_hash) do
{
- email: email_ascii,
+ email: email_ascii,
first_name: first_name_ascii,
- last_name: last_name_ascii,
- name: name_ascii,
- nickname: nickname_ascii,
- uid: uid_ascii,
+ last_name: last_name_ascii,
+ name: name_ascii,
+ nickname: nickname_ascii,
+ uid: uid_ascii,
address: {
locality: 'some locality',
country: 'some country'
diff --git a/spec/lib/gitlab/auth/o_auth/provider_spec.rb b/spec/lib/gitlab/auth/o_auth/provider_spec.rb
index c1b96819176..96a31c50989 100644
--- a/spec/lib/gitlab/auth/o_auth/provider_spec.rb
+++ b/spec/lib/gitlab/auth/o_auth/provider_spec.rb
@@ -90,6 +90,24 @@ RSpec.describe Gitlab::Auth::OAuth::Provider do
end
end
end
+
+ context 'for an OpenID Connect provider' do
+ before do
+ provider = ActiveSupport::InheritableOptions.new(
+ name: 'openid_connect',
+ args: ActiveSupport::InheritableOptions.new(name: 'custom_oidc')
+ )
+ allow(Gitlab.config.omniauth).to receive(:providers).and_return([provider])
+ end
+
+ context 'when the provider exists' do
+ subject { described_class.config_for('custom_oidc') }
+
+ it 'returns the config' do
+ expect(subject).to be_a(ActiveSupport::InheritableOptions)
+ end
+ end
+ end
end
describe '.label_for' do
diff --git a/spec/lib/gitlab/auth/otp/strategies/forti_token_cloud_spec.rb b/spec/lib/gitlab/auth/otp/strategies/forti_token_cloud_spec.rb
index 57ee53a452e..61e17ad2424 100644
--- a/spec/lib/gitlab/auth/otp/strategies/forti_token_cloud_spec.rb
+++ b/spec/lib/gitlab/auth/otp/strategies/forti_token_cloud_spec.rb
@@ -49,10 +49,10 @@ RSpec.describe Gitlab::Auth::Otp::Strategies::FortiTokenCloud do
stub_request(:post, otp_verification_url)
.with(body: JSON(otp_verification_request_body),
- headers: {
- 'Content-Type' => 'application/json',
- 'Authorization' => "Bearer #{access_token}"
- })
+ headers: {
+ 'Content-Type' => 'application/json',
+ 'Authorization' => "Bearer #{access_token}"
+ })
.to_return(status: otp_verification_response_status, body: '', headers: {})
end
diff --git a/spec/lib/gitlab/background_migration/backfill_cluster_agents_has_vulnerabilities_spec.rb b/spec/lib/gitlab/background_migration/backfill_cluster_agents_has_vulnerabilities_spec.rb
new file mode 100644
index 00000000000..3aab0cdf54b
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/backfill_cluster_agents_has_vulnerabilities_spec.rb
@@ -0,0 +1,107 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::BackfillClusterAgentsHasVulnerabilities, :migration do # rubocop:disable Layout/LineLength
+ let(:migration) do
+ described_class.new(start_id: 1, end_id: 10,
+ batch_table: table_name, batch_column: batch_column,
+ sub_batch_size: sub_batch_size, pause_ms: pause_ms,
+ connection: ApplicationRecord.connection)
+ end
+
+ let(:users_table) { table(:users) }
+ let(:vulnerability_reads_table) { table(:vulnerability_reads) }
+ let(:vulnerability_scanners_table) { table(:vulnerability_scanners) }
+ let(:vulnerabilities_table) { table(:vulnerabilities) }
+ let(:namespaces_table) { table(:namespaces) }
+ let(:projects_table) { table(:projects) }
+ let(:cluster_agents_table) { table(:cluster_agents) }
+
+ let(:table_name) { 'cluster_agents' }
+ let(:batch_column) { :id }
+ let(:sub_batch_size) { 100 }
+ let(:pause_ms) { 0 }
+
+ subject(:perform_migration) { migration.perform }
+
+ before do
+ users_table.create!(id: 1, name: 'John Doe', email: 'test@example.com', projects_limit: 5)
+
+ namespaces_table.create!(id: 1, name: 'Namespace 1', path: 'namespace-1')
+ namespaces_table.create!(id: 2, name: 'Namespace 2', path: 'namespace-2')
+ namespaces_table.create!(id: 3, name: 'Namespace 3', path: 'namespace-3')
+
+ projects_table.create!(id: 1, namespace_id: 1, name: 'Project 1', path: 'project-1', project_namespace_id: 1)
+ projects_table.create!(id: 2, namespace_id: 2, name: 'Project 2', path: 'project-2', project_namespace_id: 2)
+ projects_table.create!(id: 3, namespace_id: 2, name: 'Project 3', path: 'project-3', project_namespace_id: 3)
+
+ cluster_agents_table.create!(id: 1, name: 'Agent 1', project_id: 1)
+ cluster_agents_table.create!(id: 2, name: 'Agent 2', project_id: 2)
+ cluster_agents_table.create!(id: 3, name: 'Agent 3', project_id: 1)
+ cluster_agents_table.create!(id: 4, name: 'Agent 4', project_id: 1)
+ cluster_agents_table.create!(id: 5, name: 'Agent 5', project_id: 1)
+ cluster_agents_table.create!(id: 6, name: 'Agent 6', project_id: 1)
+ cluster_agents_table.create!(id: 7, name: 'Agent 7', project_id: 3)
+ cluster_agents_table.create!(id: 8, name: 'Agent 8', project_id: 1)
+ cluster_agents_table.create!(id: 9, name: 'Agent 9', project_id: 1)
+ cluster_agents_table.create!(id: 10, name: 'Agent 10', project_id: 3)
+ cluster_agents_table.create!(id: 11, name: 'Agent 11', project_id: 1)
+
+ vulnerability_scanners_table.create!(id: 1, project_id: 1, external_id: 'starboard', name: 'Starboard')
+ vulnerability_scanners_table.create!(id: 2, project_id: 2, external_id: 'starboard', name: 'Starboard')
+ vulnerability_scanners_table.create!(id: 3, project_id: 3, external_id: 'starboard', name: 'Starboard')
+
+ add_vulnerability_read!(1, project_id: 1, cluster_agent_id: 1, report_type: 7)
+ add_vulnerability_read!(2, project_id: 1, cluster_agent_id: nil, report_type: 7)
+ add_vulnerability_read!(3, project_id: 1, cluster_agent_id: 3, report_type: 7)
+ add_vulnerability_read!(4, project_id: 1, cluster_agent_id: nil, report_type: 7)
+ add_vulnerability_read!(5, project_id: 2, cluster_agent_id: 5, report_type: 5)
+ add_vulnerability_read!(7, project_id: 2, cluster_agent_id: 7, report_type: 7)
+ add_vulnerability_read!(9, project_id: 3, cluster_agent_id: 9, report_type: 7)
+ add_vulnerability_read!(10, project_id: 1, cluster_agent_id: 10, report_type: 7)
+ add_vulnerability_read!(11, project_id: 2, cluster_agent_id: 11, report_type: 7)
+ end
+
+ it 'backfills `has_vulnerabilities` for the selected records', :aggregate_failures do
+ queries = ActiveRecord::QueryRecorder.new do
+ perform_migration
+ end
+
+ expect(queries.count).to eq(3)
+ expect(cluster_agents_table.where(has_vulnerabilities: true).count).to eq 2
+ expect(cluster_agents_table.where(has_vulnerabilities: true).pluck(:id)).to match_array([1, 3])
+ end
+
+ it 'tracks timings of queries' do
+ expect(migration.batch_metrics.timings).to be_empty
+
+ expect { perform_migration }.to change { migration.batch_metrics.timings }
+ end
+
+ private
+
+ def add_vulnerability_read!(id, project_id:, cluster_agent_id:, report_type:)
+ vulnerabilities_table.create!(
+ id: id,
+ project_id: project_id,
+ author_id: 1,
+ title: "Vulnerability #{id}",
+ severity: 5,
+ confidence: 5,
+ report_type: report_type
+ )
+
+ vulnerability_reads_table.create!(
+ id: id,
+ uuid: SecureRandom.uuid,
+ severity: 5,
+ state: 1,
+ vulnerability_id: id,
+ scanner_id: project_id,
+ casted_cluster_agent_id: cluster_agent_id,
+ project_id: project_id,
+ report_type: report_type
+ )
+ 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 e363a5a6b20..8947262ae9e 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
@@ -14,10 +14,10 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillImportedIssueSearchData,
table(:projects)
.create!(
namespace_id: namespace.id,
- creator_id: user.id,
- name: 'projecty',
- path: 'path',
- project_namespace_id: namespace.id)
+ creator_id: user.id,
+ name: 'projecty',
+ path: 'path',
+ project_namespace_id: namespace.id)
end
let!(:issue) do
diff --git a/spec/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification_spec.rb b/spec/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification_spec.rb
index b3825a7c4ea..3c0b7766871 100644
--- a/spec/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_integrations_enable_ssl_verification_spec.rb
@@ -9,25 +9,35 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillIntegrationsEnableSslVerific
before do
integrations.create!(id: 1, type_new: 'Integrations::Bamboo') # unaffected integration
integrations.create!(id: 2, type_new: 'Integrations::DroneCi') # no properties
- integrations.create!(id: 3, type_new: 'Integrations::DroneCi',
+ integrations.create!(
+ id: 3, type_new: 'Integrations::DroneCi',
properties: {}) # no URL
- integrations.create!(id: 4, type_new: 'Integrations::DroneCi',
+ integrations.create!(
+ id: 4, type_new: 'Integrations::DroneCi',
properties: { 'drone_url' => '' }) # blank URL
- integrations.create!(id: 5, type_new: 'Integrations::DroneCi',
+ integrations.create!(
+ id: 5, type_new: 'Integrations::DroneCi',
properties: { 'drone_url' => 'https://example.com:foo' }) # invalid URL
- integrations.create!(id: 6, type_new: 'Integrations::DroneCi',
+ integrations.create!(
+ id: 6, type_new: 'Integrations::DroneCi',
properties: { 'drone_url' => 'https://example.com' }) # unknown URL
- integrations.create!(id: 7, type_new: 'Integrations::DroneCi',
+ integrations.create!(
+ id: 7, type_new: 'Integrations::DroneCi',
properties: { 'drone_url' => 'http://cloud.drone.io' }) # no HTTPS
- integrations.create!(id: 8, type_new: 'Integrations::DroneCi',
+ integrations.create!(
+ id: 8, type_new: 'Integrations::DroneCi',
properties: { 'drone_url' => 'https://cloud.drone.io' }) # known URL
- integrations.create!(id: 9, type_new: 'Integrations::Teamcity',
+ integrations.create!(
+ id: 9, type_new: 'Integrations::Teamcity',
properties: { 'teamcity_url' => 'https://example.com' }) # unknown URL
- integrations.create!(id: 10, type_new: 'Integrations::Teamcity',
+ integrations.create!(
+ id: 10, type_new: 'Integrations::Teamcity',
properties: { 'teamcity_url' => 'https://foo.bar.teamcity.com' }) # unknown URL
- integrations.create!(id: 11, type_new: 'Integrations::Teamcity',
+ integrations.create!(
+ id: 11, type_new: 'Integrations::Teamcity',
properties: { 'teamcity_url' => 'https://teamcity.com' }) # unknown URL
- integrations.create!(id: 12, type_new: 'Integrations::Teamcity',
+ integrations.create!(
+ id: 12, type_new: 'Integrations::Teamcity',
properties: { 'teamcity_url' => 'https://customer.teamcity.com' }) # known URL
end
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
new file mode 100644
index 00000000000..29833074109
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/backfill_project_namespace_on_issues_spec.rb
@@ -0,0 +1,57 @@
+# 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
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:issues) { table(:issues) }
+
+ let(:namespace1) { namespaces.create!(name: 'batchtest1', type: 'Group', path: 'space1') }
+ let(:namespace2) { namespaces.create!(name: 'batchtest2', type: 'Group', parent_id: namespace1.id, path: 'space2') }
+
+ let(:proj_namespace1) { namespaces.create!(name: 'proj1', path: 'proj1', type: 'Project', parent_id: namespace1.id) }
+ let(:proj_namespace2) { namespaces.create!(name: 'proj2', path: 'proj2', type: 'Project', parent_id: namespace2.id) }
+
+ # rubocop:disable Layout/LineLength
+ 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) }
+ # rubocop:enable Layout/LineLength
+
+ let(:migration) do
+ described_class.new(
+ start_id: proj1_issue_with_namespace.id,
+ end_id: proj2_issue_without_namespace2.id,
+ batch_table: :issues,
+ batch_column: :id,
+ sub_batch_size: 2,
+ pause_ms: 2,
+ connection: ApplicationRecord.connection
+ )
+ end
+
+ subject(:perform_migration) { migration.perform }
+
+ it 'backfills namespace_id for the selected records', :aggregate_failures do
+ perform_migration
+
+ expected_namespaces = [proj_namespace1.id, proj_namespace2.id]
+
+ expect(issues.where.not(namespace_id: nil).count).to eq(6)
+ expect(issues.where.not(namespace_id: nil).pluck(:namespace_id).uniq).to match_array(expected_namespaces)
+ end
+
+ it 'tracks timings of queries' do
+ expect(migration.batch_metrics.timings).to be_empty
+
+ expect { perform_migration }.to change { migration.batch_metrics.timings }
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb b/spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb
index 6f75d3faef3..1c2e0e991d9 100644
--- a/spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb
@@ -14,23 +14,23 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillSnippetRepositories, :migrat
let!(:user) do
users.create!(id: 1,
- email: 'user@example.com',
- projects_limit: 10,
- username: 'test',
- name: user_name,
- state: user_state,
- last_activity_on: 1.minute.ago,
- user_type: user_type,
- confirmed_at: 1.day.ago)
+ email: 'user@example.com',
+ projects_limit: 10,
+ username: 'test',
+ name: user_name,
+ state: user_state,
+ last_activity_on: 1.minute.ago,
+ user_type: user_type,
+ confirmed_at: 1.day.ago)
end
let!(:migration_bot) do
users.create!(id: 100,
- email: "noreply+gitlab-migration-bot%s@#{Settings.gitlab.host}",
- user_type: HasUserType::USER_TYPES[:migration_bot],
- name: 'GitLab Migration Bot',
- projects_limit: 10,
- username: 'bot')
+ email: "noreply+gitlab-migration-bot%s@#{Settings.gitlab.host}",
+ user_type: HasUserType::USER_TYPES[:migration_bot],
+ name: 'GitLab Migration Bot',
+ projects_limit: 10,
+ username: 'bot')
end
let!(:snippet_with_repo) { snippets.create!(id: 1, type: 'PersonalSnippet', author_id: user.id, file_name: file_name, content: content) }
@@ -260,14 +260,14 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillSnippetRepositories, :migrat
let(:user_name) { '.' }
let!(:other_user) do
users.create!(id: 2,
- email: 'user2@example.com',
- projects_limit: 10,
- username: 'test2',
- name: 'Test2',
- state: user_state,
- last_activity_on: 1.minute.ago,
- user_type: user_type,
- confirmed_at: 1.day.ago)
+ email: 'user2@example.com',
+ projects_limit: 10,
+ username: 'test2',
+ name: 'Test2',
+ state: user_state,
+ last_activity_on: 1.minute.ago,
+ user_type: user_type,
+ confirmed_at: 1.day.ago)
end
let!(:invalid_snippet) { snippets.create!(id: 4, type: 'PersonalSnippet', author_id: user.id, file_name: '.', content: content) }
diff --git a/spec/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent_spec.rb b/spec/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent_spec.rb
index 79699375a8d..f642ec8c20d 100644
--- a/spec/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent_spec.rb
+++ b/spec/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent_spec.rb
@@ -23,8 +23,6 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillVulnerabilityReadsClusterAge
let(:sub_batch_size) { 1_000 }
let(:pause_ms) { 0 }
- subject(:perform_migration) { migration.perform }
-
before do
users_table.create!(id: 1, name: 'John Doe', email: 'test@example.com', projects_limit: 5)
@@ -49,20 +47,30 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillVulnerabilityReadsClusterAge
add_vulnerability_read!(11, project_id: 1, cluster_agent_id: 1, report_type: 7)
end
- it 'backfills `casted_cluster_agent_id` for the selected records', :aggregate_failures do
- queries = ActiveRecord::QueryRecorder.new do
- perform_migration
+ describe '#filter_batch' do
+ it 'pick only vulnerability reads where report_type = 7' do
+ expect(migration.filter_batch(vulnerability_reads_table).pluck(:id)).to contain_exactly(1, 3, 7, 9, 10, 11)
end
-
- expect(queries.count).to eq(3)
- expect(vulnerability_reads_table.where.not(casted_cluster_agent_id: nil).count).to eq 3
- expect(vulnerability_reads_table.where.not(casted_cluster_agent_id: nil).pluck(:id)).to match_array([1, 9, 10])
end
- it 'tracks timings of queries' do
- expect(migration.batch_metrics.timings).to be_empty
+ describe '#perform' do
+ subject(:perform_migration) { migration.perform }
- expect { perform_migration }.to change { migration.batch_metrics.timings }
+ it 'backfills `casted_cluster_agent_id` for the selected records', :aggregate_failures do
+ queries = ActiveRecord::QueryRecorder.new do
+ perform_migration
+ end
+
+ expect(queries.count).to eq(3)
+ expect(vulnerability_reads_table.where.not(casted_cluster_agent_id: nil).count).to eq 3
+ expect(vulnerability_reads_table.where.not(casted_cluster_agent_id: nil).pluck(:id)).to match_array([1, 9, 10])
+ end
+
+ it 'tracks timings of queries' do
+ expect(migration.batch_metrics.timings).to be_empty
+
+ expect { perform_migration }.to change { migration.batch_metrics.timings }
+ end
end
private
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 8d82c533d20..6ef474ad7f9 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
@@ -2,12 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::BackgroundMigration::BackfillWorkItemTypeIdForIssues, :migration, schema: 20220326161803 do
- subject(:migrate) { migration.perform(start_id, end_id, batch_table, batch_column, sub_batch_size, pause_ms, issue_type_enum[:issue], issue_type.id) }
-
- let(:migration) { described_class.new }
-
- let(:batch_table) { 'issues' }
+RSpec.describe Gitlab::BackgroundMigration::BackfillWorkItemTypeIdForIssues, :migration, schema: 20220825142324 do
let(:batch_column) { 'id' }
let(:sub_batch_size) { 2 }
let(:pause_ms) { 0 }
@@ -15,7 +10,7 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillWorkItemTypeIdForIssues, :mi
# let_it_be can't be used in migration specs because all tables but `work_item_types` are deleted after each spec
let(:issue_type_enum) { { issue: 0, incident: 1, test_case: 2, requirement: 3, task: 4 } }
let(:namespace) { table(:namespaces).create!(name: 'namespace', path: 'namespace') }
- let(:project) { table(:projects).create!(namespace_id: namespace.id) }
+ let(:project) { table(:projects).create!(namespace_id: namespace.id, project_namespace_id: namespace.id) }
let(:issues_table) { table(:issues) }
let(:issue_type) { table(:work_item_types).find_by!(namespace_id: nil, base_type: issue_type_enum[:issue]) }
@@ -32,6 +27,21 @@ RSpec.describe Gitlab::BackgroundMigration::BackfillWorkItemTypeIdForIssues, :mi
let(:all_issues) { [issue1, issue2, issue3, incident1, test_case1, requirement1] }
+ let(:migration) do
+ described_class.new(
+ start_id: start_id,
+ end_id: end_id,
+ batch_table: :issues,
+ batch_column: :id,
+ sub_batch_size: sub_batch_size,
+ pause_ms: pause_ms,
+ job_arguments: [issue_type_enum[:issue], issue_type.id],
+ connection: ApplicationRecord.connection
+ )
+ end
+
+ subject(:migrate) { migration.perform }
+
it 'sets work_item_type_id only for the given type' do
expect(all_issues).to all(have_attributes(work_item_type_id: nil))
diff --git a/spec/lib/gitlab/background_migration/batching_strategies/backfill_issue_work_item_type_batching_strategy_spec.rb b/spec/lib/gitlab/background_migration/batching_strategies/backfill_issue_work_item_type_batching_strategy_spec.rb
deleted file mode 100644
index 3cba99bfe51..00000000000
--- a/spec/lib/gitlab/background_migration/batching_strategies/backfill_issue_work_item_type_batching_strategy_spec.rb
+++ /dev/null
@@ -1,135 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::BackfillIssueWorkItemTypeBatchingStrategy, '#next_batch', schema: 20220326161803 do # rubocop:disable Layout/LineLength
- # let! can't be used in migration specs because all tables but `work_item_types` are deleted after each spec
- let!(:issue_type_enum) { { issue: 0, incident: 1, test_case: 2, requirement: 3, task: 4 } }
- let!(:namespace) { table(:namespaces).create!(name: 'namespace', path: 'namespace') }
- let!(:project) { table(:projects).create!(namespace_id: namespace.id) }
- let!(:issues_table) { table(:issues) }
- let!(:task_type) { table(:work_item_types).find_by!(namespace_id: nil, base_type: issue_type_enum[:task]) }
-
- let!(:issue1) { issues_table.create!(project_id: project.id, issue_type: issue_type_enum[:issue]) }
- let!(:task1) { issues_table.create!(project_id: project.id, issue_type: issue_type_enum[:task]) }
- let!(:issue2) { issues_table.create!(project_id: project.id, issue_type: issue_type_enum[:issue]) }
- let!(:issue3) { issues_table.create!(project_id: project.id, issue_type: issue_type_enum[:issue]) }
- let!(:task2) { issues_table.create!(project_id: project.id, issue_type: issue_type_enum[:task]) }
- let!(:incident1) { issues_table.create!(project_id: project.id, issue_type: issue_type_enum[:incident]) }
- # test_case is EE only, but enum values exist on the FOSS model
- let!(:test_case1) { issues_table.create!(project_id: project.id, issue_type: issue_type_enum[:test_case]) }
-
- let!(:task3) do
- issues_table.create!(project_id: project.id, issue_type: issue_type_enum[:task], work_item_type_id: task_type.id)
- end
-
- let!(:task4) { issues_table.create!(project_id: project.id, issue_type: issue_type_enum[:task]) }
-
- let!(:batching_strategy) { described_class.new(connection: ActiveRecord::Base.connection) }
-
- context 'when issue_type is issue' do
- let(:job_arguments) { [issue_type_enum[:issue], 'irrelevant_work_item_id'] }
-
- context 'when starting on the first batch' do
- it 'returns the bounds of the next batch' do
- batch_bounds = next_batch(issue1.id, 2)
-
- expect(batch_bounds).to match_array([issue1.id, issue2.id])
- end
- end
-
- context 'when additional batches remain' do
- it 'returns the bounds of the next batch' do
- batch_bounds = next_batch(issue2.id, 2)
-
- expect(batch_bounds).to match_array([issue2.id, issue3.id])
- end
- end
-
- context 'when on the final batch' do
- it 'returns the bounds of the next batch' do
- batch_bounds = next_batch(issue3.id, 2)
-
- expect(batch_bounds).to match_array([issue3.id, issue3.id])
- end
- end
-
- context 'when no additional batches remain' do
- it 'returns nil' do
- batch_bounds = next_batch(issue3.id + 1, 1)
-
- expect(batch_bounds).to be_nil
- end
- end
- end
-
- context 'when issue_type is incident' do
- let(:job_arguments) { [issue_type_enum[:incident], 'irrelevant_work_item_id'] }
-
- context 'when starting on the first batch' do
- it 'returns the bounds of the next batch with only one element' do
- batch_bounds = next_batch(incident1.id, 2)
-
- expect(batch_bounds).to match_array([incident1.id, incident1.id])
- end
- end
- end
-
- context 'when issue_type is requirement and there are no matching records' do
- let(:job_arguments) { [issue_type_enum[:requirement], 'irrelevant_work_item_id'] }
-
- context 'when starting on the first batch' do
- it 'returns nil' do
- batch_bounds = next_batch(1, 2)
-
- expect(batch_bounds).to be_nil
- end
- end
- end
-
- context 'when issue_type is task' do
- let(:job_arguments) { [issue_type_enum[:task], 'irrelevant_work_item_id'] }
-
- context 'when starting on the first batch' do
- it 'returns the bounds of the next batch' do
- batch_bounds = next_batch(task1.id, 2)
-
- expect(batch_bounds).to match_array([task1.id, task2.id])
- end
- end
-
- context 'when additional batches remain' do
- it 'returns the bounds of the next batch, does not skip records where FK is already set' do
- batch_bounds = next_batch(task2.id, 2)
-
- expect(batch_bounds).to match_array([task2.id, task3.id])
- end
- end
-
- context 'when on the final batch' do
- it 'returns the bounds of the next batch' do
- batch_bounds = next_batch(task4.id, 2)
-
- expect(batch_bounds).to match_array([task4.id, task4.id])
- end
- end
-
- context 'when no additional batches remain' do
- it 'returns nil' do
- batch_bounds = next_batch(task4.id + 1, 1)
-
- expect(batch_bounds).to be_nil
- end
- end
- end
-
- def next_batch(min_value, batch_size)
- batching_strategy.next_batch(
- :issues,
- :id,
- batch_min_value: min_value,
- batch_size: batch_size,
- job_arguments: job_arguments
- )
- end
-end
diff --git a/spec/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy_spec.rb b/spec/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy_spec.rb
index 94e9bcf9207..7076e82ea34 100644
--- a/spec/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy_spec.rb
+++ b/spec/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy_spec.rb
@@ -2,137 +2,6 @@
require 'spec_helper'
-RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::BackfillProjectStatisticsWithContainerRegistrySizeBatchingStrategy, '#next_batch' do # rubocop:disable Layout/LineLength
- let(:batching_strategy) { described_class.new(connection: ActiveRecord::Base.connection) }
- let(:namespace) { table(:namespaces) }
- let(:project) { table(:projects) }
- let(:container_repositories) { table(:container_repositories) }
-
- let!(:group) do
- namespace.create!(
- name: 'namespace1', type: 'Group', path: 'space1'
- )
- end
-
- let!(:proj_namespace1) do
- namespace.create!(
- name: 'proj1', path: 'proj1', type: 'Project', parent_id: group.id
- )
- end
-
- let!(:proj_namespace2) do
- namespace.create!(
- name: 'proj2', path: 'proj2', type: 'Project', parent_id: group.id
- )
- end
-
- let!(:proj_namespace3) do
- namespace.create!(
- name: 'proj3', path: 'proj3', type: 'Project', parent_id: group.id
- )
- end
-
- let!(:proj1) do
- project.create!(
- name: 'proj1', path: 'proj1', namespace_id: group.id, project_namespace_id: proj_namespace1.id
- )
- end
-
- let!(:proj2) do
- project.create!(
- name: 'proj2', path: 'proj2', namespace_id: group.id, project_namespace_id: proj_namespace2.id
- )
- end
-
- let!(:proj3) do
- project.create!(
- name: 'proj3', path: 'proj3', namespace_id: group.id, project_namespace_id: proj_namespace3.id
- )
- end
-
- let!(:con1) do
- container_repositories.create!(
- project_id: proj1.id,
- name: "ContReg_#{proj1.id}:1",
- migration_state: 'import_done',
- created_at: Date.new(2022, 01, 20)
- )
- end
-
- let!(:con2) do
- container_repositories.create!(
- project_id: proj1.id,
- name: "ContReg_#{proj1.id}:2",
- migration_state: 'import_done',
- created_at: Date.new(2022, 01, 20)
- )
- end
-
- let!(:con3) do
- container_repositories.create!(
- project_id: proj2.id,
- name: "ContReg_#{proj2.id}:1",
- migration_state: 'import_done',
- created_at: Date.new(2022, 01, 20)
- )
- end
-
- let!(:con4) do
- container_repositories.create!(
- project_id: proj3.id,
- name: "ContReg_#{proj3.id}:1",
- migration_state: 'default',
- created_at: Date.new(2022, 02, 20)
- )
- end
-
- let!(:con5) do
- container_repositories.create!(
- project_id: proj3.id,
- name: "ContReg_#{proj3.id}:2",
- migration_state: 'default',
- created_at: Date.new(2022, 02, 20)
- )
- end
-
+RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::BackfillProjectStatisticsWithContainerRegistrySizeBatchingStrategy do # rubocop:disable Layout/LineLength
it { expect(described_class).to be < Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy }
-
- context 'when starting on the first batch' do
- it 'returns the bounds of the next batch' do
- batch_bounds = batching_strategy.next_batch(
- :container_repositories,
- :project_id,
- batch_min_value: con1.project_id,
- batch_size: 3,
- job_arguments: []
- )
- expect(batch_bounds).to eq([con1.project_id, con4.project_id])
- end
- end
-
- context 'when additional batches remain' do
- it 'returns the bounds of the next batch' do
- batch_bounds = batching_strategy.next_batch(
- :container_repositories,
- :project_id,
- batch_min_value: con3.project_id,
- batch_size: 3,
- job_arguments: []
- )
-
- expect(batch_bounds).to eq([con3.project_id, con5.project_id])
- end
- end
-
- context 'when no additional batches remain' do
- it 'returns nil' do
- batch_bounds = batching_strategy.next_batch(:container_repositories,
- :project_id,
- batch_min_value: con5.project_id + 1,
- batch_size: 1, job_arguments: []
- )
-
- expect(batch_bounds).to be_nil
- end
- end
end
diff --git a/spec/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy_spec.rb b/spec/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy_spec.rb
index f96c7de50f2..e4bef81e0bd 100644
--- a/spec/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy_spec.rb
+++ b/spec/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy_spec.rb
@@ -3,117 +3,5 @@
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::DismissedVulnerabilitiesStrategy, '#next_batch' do
- let(:batching_strategy) { described_class.new(connection: ActiveRecord::Base.connection) }
- let(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
- let(:users) { table(:users) }
- let(:user) { create_user! }
- let(:project) do
- table(:projects).create!(
- namespace_id: namespace.id,
- project_namespace_id: namespace.id,
- packages_enabled: false)
- end
-
- let(:vulnerabilities) { table(:vulnerabilities) }
-
- let!(:vulnerability1) do
- create_vulnerability!(
- project_id: project.id,
- author_id: user.id,
- dismissed_at: Time.current
- )
- end
-
- let!(:vulnerability2) do
- create_vulnerability!(
- project_id: project.id,
- author_id: user.id,
- dismissed_at: Time.current
- )
- end
-
- let!(:vulnerability3) do
- create_vulnerability!(
- project_id: project.id,
- author_id: user.id,
- dismissed_at: Time.current
- )
- end
-
- let!(:vulnerability4) do
- create_vulnerability!(
- project_id: project.id,
- author_id: user.id,
- dismissed_at: nil
- )
- end
-
it { expect(described_class).to be < Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy }
-
- context 'when starting on the first batch' do
- it 'returns the bounds of the next batch' do
- batch_bounds = batching_strategy.next_batch(
- :vulnerabilities,
- :id,
- batch_min_value: vulnerability1.id,
- batch_size: 2,
- job_arguments: []
- )
- expect(batch_bounds).to eq([vulnerability1.id, vulnerability2.id])
- end
- end
-
- context 'when additional batches remain' do
- it 'returns the bounds of the next batch and skips the records that do not have `dismissed_at` set' do
- batch_bounds = batching_strategy.next_batch(
- :vulnerabilities,
- :id,
- batch_min_value: vulnerability3.id,
- batch_size: 2,
- job_arguments: []
- )
-
- expect(batch_bounds).to eq([vulnerability3.id, vulnerability3.id])
- end
- end
-
- context 'when no additional batches remain' do
- it 'returns nil' do
- batch_bounds = batching_strategy.next_batch(
- :vulnerabilities,
- :id,
- batch_min_value: vulnerability4.id + 1,
- batch_size: 1,
- job_arguments: []
- )
-
- expect(batch_bounds).to be_nil
- end
- end
-
- private
-
- def create_vulnerability!(
- project_id:, author_id:, title: 'test', severity: 7, confidence: 7, report_type: 0, state: 1, dismissed_at: nil
- )
- vulnerabilities.create!(
- project_id: project_id,
- author_id: author_id,
- title: title,
- severity: severity,
- confidence: confidence,
- report_type: report_type,
- state: state,
- dismissed_at: dismissed_at
- )
- end
-
- def create_user!(name: "Example User", email: "user@example.com", user_type: nil)
- users.create!(
- name: name,
- email: email,
- username: name,
- projects_limit: 10
- )
- end
end
diff --git a/spec/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy_spec.rb b/spec/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy_spec.rb
index 9fdd7bf8adc..37fdd209622 100644
--- a/spec/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy_spec.rb
+++ b/spec/lib/gitlab/background_migration/batching_strategies/primary_key_batching_strategy_spec.rb
@@ -60,26 +60,21 @@ RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchi
expect(batch_bounds).to eq([namespace4.id, namespace4.id])
end
- end
-
- context 'additional filters' do
- let(:strategy_with_filters) do
- Class.new(described_class) do
- def apply_additional_filters(relation, job_arguments:, job_class: nil)
- min_id = job_arguments.first
- relation.where.not(type: 'Project').where('id >= ?', min_id)
+ context 'when scope has a join which makes the column name ambiguous' do
+ let(:job_class) do
+ Class.new(Gitlab::BackgroundMigration::BatchedMigrationJob) do
+ scope_to ->(r) { r.joins('LEFT JOIN users ON users.id = namespaces.owner_id') }
end
end
- end
- let(:batching_strategy) { strategy_with_filters.new(connection: ActiveRecord::Base.connection) }
- let!(:namespace5) { namespaces.create!(name: 'batchtest5', path: 'batch-test5', type: 'Project') }
+ it 'executes the correct query' do
+ expect(job_class).to receive(:generic_instance).and_call_original
- it 'applies additional filters' do
- batch_bounds = batching_strategy.next_batch(:namespaces, :id, batch_min_value: namespace4.id, batch_size: 3, job_arguments: [1])
+ batch_bounds = batching_strategy.next_batch(:namespaces, :id, batch_min_value: namespace4.id, batch_size: 3, job_arguments: [], job_class: job_class)
- expect(batch_bounds).to eq([namespace4.id, namespace4.id])
+ expect(batch_bounds).to eq([namespace4.id, namespace4.id])
+ end
end
end
end
diff --git a/spec/lib/gitlab/background_migration/batching_strategies/remove_backfilled_job_artifacts_expire_at_batching_strategy_spec.rb b/spec/lib/gitlab/background_migration/batching_strategies/remove_backfilled_job_artifacts_expire_at_batching_strategy_spec.rb
new file mode 100644
index 00000000000..e296a46ea2f
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/batching_strategies/remove_backfilled_job_artifacts_expire_at_batching_strategy_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::BatchingStrategies::RemoveBackfilledJobArtifactsExpireAtBatchingStrategy do # rubocop:disable Layout/LineLength
+ it { expect(described_class).to be < Gitlab::BackgroundMigration::BatchingStrategies::PrimaryKeyBatchingStrategy }
+end
diff --git a/spec/lib/gitlab/background_migration/destroy_invalid_group_members_spec.rb b/spec/lib/gitlab/background_migration/destroy_invalid_group_members_spec.rb
new file mode 100644
index 00000000000..76a9ea82c76
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/destroy_invalid_group_members_spec.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::DestroyInvalidGroupMembers, :migration, schema: 20220809002011 do
+ # rubocop: disable Layout/LineLength
+ # rubocop: disable RSpec/ScatteredLet
+ let!(:migration_attrs) do
+ {
+ start_id: 1,
+ end_id: 1000,
+ batch_table: :members,
+ batch_column: :id,
+ sub_batch_size: 100,
+ pause_ms: 0,
+ connection: ApplicationRecord.connection
+ }
+ end
+
+ let!(:migration) { described_class.new(**migration_attrs) }
+
+ subject(:perform_migration) { migration.perform }
+
+ let(:users_table) { table(:users) }
+ let(:namespaces_table) { table(:namespaces) }
+ let(:members_table) { table(:members) }
+ let(:projects_table) { table(:projects) }
+
+ let(:user1) { users_table.create!(name: 'user1', email: 'user1@example.com', projects_limit: 5) }
+ let(:user2) { users_table.create!(name: 'user2', email: 'user2@example.com', projects_limit: 5) }
+ let(:user3) { users_table.create!(name: 'user3', email: 'user3@example.com', projects_limit: 5) }
+ let(:user4) { users_table.create!(name: 'user4', email: 'user4@example.com', projects_limit: 5) }
+ let(:user5) { users_table.create!(name: 'user5', email: 'user5@example.com', projects_limit: 5) }
+ let(:user6) { users_table.create!(name: 'user6', email: 'user6@example.com', projects_limit: 5) }
+
+ let!(:group1) { namespaces_table.create!(name: 'marvellous group 1', path: 'group-path-1', type: 'Group') }
+
+ let!(:group2) { namespaces_table.create!(name: 'outstanding group 2', path: 'group-path-2', type: 'Group') }
+
+ # create group member records, a mix of both valid and invalid
+ # project members will have already been filtered out.
+ let!(:group_member1) { create_invalid_group_member(id: 1, user_id: user1.id) }
+
+ let!(:group_member4) { create_valid_group_member(id: 4, user_id: user2.id, group_id: group1.id) }
+
+ let!(:group_member5) { create_valid_group_member(id: 5, user_id: user3.id, group_id: group2.id) }
+
+ let!(:group_member6) { create_invalid_group_member(id: 6, user_id: user4.id) }
+
+ let!(:group_member7) { create_valid_group_member(id: 7, user_id: user5.id, group_id: group1.id) }
+
+ let!(:group_member8) { create_invalid_group_member(id: 8, user_id: user6.id) }
+
+ it 'removes invalid memberships but keeps valid ones', :aggregate_failures do
+ expect(members_table.where(type: 'GroupMember').count).to eq 6
+
+ queries = ActiveRecord::QueryRecorder.new do
+ perform_migration
+ end
+
+ expect(queries.count).to eq(4)
+ expect(members_table.where(type: 'GroupMember').pluck(:id)).to match_array([group_member4, group_member5, group_member7].map(&:id))
+ end
+
+ it 'tracks timings of queries' do
+ expect(migration.batch_metrics.timings).to be_empty
+
+ expect { perform_migration }.to change { migration.batch_metrics.timings }
+ end
+
+ it 'logs IDs of deleted records' do
+ expect(Gitlab::AppLogger).to receive(:info).with({ message: 'Removing invalid group member records',
+ deleted_count: 3, ids: [group_member1, group_member6, group_member8].map(&:id) })
+
+ perform_migration
+ end
+
+ def create_invalid_group_member(id:, user_id:)
+ members_table.create!(id: id, user_id: user_id, source_id: non_existing_record_id, access_level: Gitlab::Access::MAINTAINER,
+ type: "GroupMember", source_type: "Namespace", notification_level: 3, member_namespace_id: nil)
+ end
+
+ def create_valid_group_member(id:, user_id:, group_id:)
+ members_table.create!(id: id, user_id: user_id, source_id: group_id, access_level: Gitlab::Access::MAINTAINER,
+ type: "GroupMember", source_type: "Namespace", member_namespace_id: group_id, notification_level: 3)
+ end
+ # rubocop: enable Layout/LineLength
+ # rubocop: enable RSpec/ScatteredLet
+end
diff --git a/spec/lib/gitlab/background_migration/destroy_invalid_project_members_spec.rb b/spec/lib/gitlab/background_migration/destroy_invalid_project_members_spec.rb
new file mode 100644
index 00000000000..029a6adf831
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/destroy_invalid_project_members_spec.rb
@@ -0,0 +1,102 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::DestroyInvalidProjectMembers, :migration, schema: 20220901035725 do
+ # rubocop: disable Layout/LineLength
+ # rubocop: disable RSpec/ScatteredLet
+ let!(:migration_attrs) do
+ {
+ start_id: 1,
+ end_id: 1000,
+ batch_table: :members,
+ batch_column: :id,
+ sub_batch_size: 100,
+ pause_ms: 0,
+ connection: ApplicationRecord.connection
+ }
+ end
+
+ let!(:migration) { described_class.new(**migration_attrs) }
+
+ subject(:perform_migration) { migration.perform }
+
+ let(:users_table) { table(:users) }
+ let(:namespaces_table) { table(:namespaces) }
+ let(:members_table) { table(:members) }
+ let(:projects_table) { table(:projects) }
+
+ let(:user1) { users_table.create!(name: 'user1', email: 'user1@example.com', projects_limit: 5) }
+ let(:user2) { users_table.create!(name: 'user2', email: 'user2@example.com', projects_limit: 5) }
+ let(:user3) { users_table.create!(name: 'user3', email: 'user3@example.com', projects_limit: 5) }
+ let(:user4) { users_table.create!(name: 'user4', email: 'user4@example.com', projects_limit: 5) }
+ let(:user5) { users_table.create!(name: 'user5', email: 'user5@example.com', projects_limit: 5) }
+ let(:user6) { users_table.create!(name: 'user6', email: 'user6@example.com', projects_limit: 5) }
+
+ let!(:group1) { namespaces_table.create!(name: 'marvellous group 1', path: 'group-path-1', type: 'Group') }
+
+ let!(:project_namespace1) do
+ namespaces_table.create!(name: 'fabulous project', path: 'project-path-1', type: 'ProjectNamespace',
+ parent_id: group1.id)
+ end
+
+ let!(:project1) do
+ projects_table.create!(name: 'fabulous project', path: 'project-path-1', project_namespace_id: project_namespace1.id,
+ namespace_id: group1.id)
+ end
+
+ let!(:project_namespace2) do
+ namespaces_table.create!(name: 'splendiferous project', path: 'project-path-2', type: 'ProjectNamespace',
+ parent_id: group1.id)
+ end
+
+ let!(:project2) do
+ projects_table.create!(name: 'splendiferous project', path: 'project-path-2', project_namespace_id: project_namespace2.id,
+ namespace_id: group1.id)
+ end
+
+ # create project member records, a mix of both valid and invalid
+ # group members will have already been filtered out.
+ let!(:project_member1) { create_invalid_project_member(id: 1, user_id: user1.id) }
+ let!(:project_member2) { create_valid_project_member(id: 4, user_id: user2.id, project: project1) }
+ let!(:project_member3) { create_valid_project_member(id: 5, user_id: user3.id, project: project2) }
+ let!(:project_member4) { create_invalid_project_member(id: 6, user_id: user4.id) }
+ let!(:project_member5) { create_valid_project_member(id: 7, user_id: user5.id, project: project2) }
+ let!(:project_member6) { create_invalid_project_member(id: 8, user_id: user6.id) }
+
+ it 'removes invalid memberships but keeps valid ones', :aggregate_failures do
+ expect(members_table.where(type: 'ProjectMember').count).to eq 6
+
+ queries = ActiveRecord::QueryRecorder.new do
+ perform_migration
+ end
+
+ expect(queries.count).to eq(4)
+ expect(members_table.where(type: 'ProjectMember')).to match_array([project_member2, project_member3, project_member5])
+ end
+
+ it 'tracks timings of queries' do
+ expect(migration.batch_metrics.timings).to be_empty
+
+ expect { perform_migration }.to change { migration.batch_metrics.timings }
+ end
+
+ it 'logs IDs of deleted records' do
+ expect(Gitlab::AppLogger).to receive(:info).with({ message: 'Removing invalid project member records',
+ deleted_count: 3, ids: [project_member1, project_member4, project_member6].map(&:id) })
+
+ perform_migration
+ end
+
+ def create_invalid_project_member(id:, user_id:)
+ members_table.create!(id: id, user_id: user_id, source_id: non_existing_record_id, access_level: Gitlab::Access::MAINTAINER,
+ type: "ProjectMember", source_type: "Project", notification_level: 3, member_namespace_id: nil)
+ end
+
+ def create_valid_project_member(id:, user_id:, project:)
+ members_table.create!(id: id, user_id: user_id, source_id: project.id, access_level: Gitlab::Access::MAINTAINER,
+ type: "ProjectMember", source_type: "Project", member_namespace_id: project.project_namespace_id, notification_level: 3)
+ end
+ # rubocop: enable Layout/LineLength
+ # rubocop: enable RSpec/ScatteredLet
+end
diff --git a/spec/lib/gitlab/background_migration/disable_legacy_open_source_licence_for_recent_public_projects_spec.rb b/spec/lib/gitlab/background_migration/disable_legacy_open_source_licence_for_recent_public_projects_spec.rb
new file mode 100644
index 00000000000..7edba8cf524
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/disable_legacy_open_source_licence_for_recent_public_projects_spec.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::DisableLegacyOpenSourceLicenceForRecentPublicProjects, :migration do
+ let(:namespaces_table) { table(:namespaces) }
+ let(:namespace_1) { namespaces_table.create!(name: 'namespace', path: 'namespace-path-1') }
+ let(:project_namespace_2) { namespaces_table.create!(name: 'namespace', path: 'namespace-path-2', type: 'Project') }
+ let(:project_namespace_3) { namespaces_table.create!(name: 'namespace', path: 'namespace-path-3', type: 'Project') }
+ let(:project_namespace_4) { namespaces_table.create!(name: 'namespace', path: 'namespace-path-4', type: 'Project') }
+ let(:project_namespace_5) { namespaces_table.create!(name: 'namespace', path: 'namespace-path-5', type: 'Project') }
+ let(:project_namespace_6) { namespaces_table.create!(name: 'namespace', path: 'namespace-path-6', type: 'Project') }
+ let(:project_namespace_7) { namespaces_table.create!(name: 'namespace', path: 'namespace-path-7', type: 'Project') }
+ let(:project_namespace_8) { namespaces_table.create!(name: 'namespace', path: 'namespace-path-8', type: 'Project') }
+
+ let(:project_1) do
+ projects_table
+ .create!(
+ name: 'proj-1', path: 'path-1', namespace_id: namespace_1.id,
+ project_namespace_id: project_namespace_2.id, visibility_level: 0
+ )
+ end
+
+ let(:project_2) do
+ projects_table
+ .create!(
+ name: 'proj-2', path: 'path-2', namespace_id: namespace_1.id,
+ project_namespace_id: project_namespace_3.id, visibility_level: 10, created_at: '2022-02-22'
+ )
+ end
+
+ let(:project_3) do
+ projects_table
+ .create!(
+ name: 'proj-3', path: 'path-3', namespace_id: namespace_1.id,
+ project_namespace_id: project_namespace_4.id, visibility_level: 20, created_at: '2022-02-17 09:00:01'
+ )
+ end
+
+ let(:project_4) do
+ projects_table
+ .create!(
+ name: 'proj-4', path: 'path-4', namespace_id: namespace_1.id,
+ project_namespace_id: project_namespace_5.id, visibility_level: 20, created_at: '2022-02-01'
+ )
+ end
+
+ let(:project_5) do
+ projects_table
+ .create!(
+ name: 'proj-5', path: 'path-5', namespace_id: namespace_1.id,
+ project_namespace_id: project_namespace_6.id, visibility_level: 20, created_at: '2022-01-04'
+ )
+ end
+
+ let(:project_6) do
+ projects_table
+ .create!(
+ name: 'proj-6', path: 'path-6', namespace_id: namespace_1.id,
+ project_namespace_id: project_namespace_7.id, visibility_level: 20, created_at: '2022-02-17 08:59:59'
+ )
+ end
+
+ let(:project_7) do
+ projects_table
+ .create!(
+ name: 'proj-7', path: 'path-7', namespace_id: namespace_1.id,
+ project_namespace_id: project_namespace_8.id, visibility_level: 20, created_at: '2022-02-17 09:00:00'
+ )
+ end
+
+ let(:projects_table) { table(:projects) }
+ let(:project_settings_table) { table(:project_settings) }
+
+ subject(:perform_migration) do
+ described_class.new(start_id: projects_table.minimum(:id),
+ end_id: projects_table.maximum(:id),
+ batch_table: :projects,
+ batch_column: :id,
+ sub_batch_size: 2,
+ pause_ms: 0,
+ connection: ActiveRecord::Base.connection)
+ .perform
+ end
+
+ before do
+ project_settings_table.create!(project_id: project_1.id, legacy_open_source_license_available: true)
+ project_settings_table.create!(project_id: project_2.id, legacy_open_source_license_available: true)
+ project_settings_table.create!(project_id: project_3.id, legacy_open_source_license_available: true)
+ project_settings_table.create!(project_id: project_4.id, legacy_open_source_license_available: true)
+ project_settings_table.create!(project_id: project_5.id, legacy_open_source_license_available: false)
+ project_settings_table.create!(project_id: project_6.id, legacy_open_source_license_available: true)
+ project_settings_table.create!(project_id: project_7.id, legacy_open_source_license_available: true)
+ end
+
+ it 'sets `legacy_open_source_license_available` attribute to false for public projects created after threshold time',
+ :aggregate_failures do
+ record = ActiveRecord::QueryRecorder.new do
+ expect { perform_migration }
+ .to not_change { migrated_attribute(project_1.id) }.from(true)
+ .and not_change { migrated_attribute(project_2.id) }.from(true)
+ .and change { migrated_attribute(project_3.id) }.from(true).to(false)
+ .and not_change { migrated_attribute(project_4.id) }.from(true)
+ .and not_change { migrated_attribute(project_5.id) }.from(false)
+ .and not_change { migrated_attribute(project_6.id) }.from(true)
+ .and change { migrated_attribute(project_7.id) }.from(true).to(false)
+ end
+ expect(record.count).to eq(19)
+ end
+
+ def migrated_attribute(project_id)
+ project_settings_table.find(project_id).legacy_open_source_license_available
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_one_mb_spec.rb b/spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_one_mb_spec.rb
new file mode 100644
index 00000000000..205350f9df4
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_projects_less_than_one_mb_spec.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::DisableLegacyOpenSourceLicenseForProjectsLessThanOneMb,
+ :migration,
+ schema: 20220906074449 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 1 MB',
+ :aggregate_failures do
+ project_setting_1_mb = create_legacy_license_project_setting(repo_size: 1)
+ project_setting_2_mb = create_legacy_license_project_setting(repo_size: 2)
+ project_setting_quarter_mb = create_legacy_license_project_setting(repo_size: 0.25)
+ project_setting_half_mb = create_legacy_license_project_setting(repo_size: 0.5)
+
+ queries = ActiveRecord::QueryRecorder.new { perform_migration }
+
+ expect(queries.count).to eq(7)
+ expect(migrated_attribute(project_setting_1_mb)).to be_truthy
+ expect(migrated_attribute(project_setting_2_mb)).to be_truthy
+ expect(migrated_attribute(project_setting_quarter_mb)).to be_falsey
+ expect(migrated_attribute(project_setting_half_mb)).to be_falsey
+ 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/encrypt_integration_properties_spec.rb b/spec/lib/gitlab/background_migration/encrypt_integration_properties_spec.rb
index 38e8b159e63..c788b701d79 100644
--- a/spec/lib/gitlab/background_migration/encrypt_integration_properties_spec.rb
+++ b/spec/lib/gitlab/background_migration/encrypt_integration_properties_spec.rb
@@ -40,10 +40,10 @@ RSpec.describe Gitlab::BackgroundMigration::EncryptIntegrationProperties, schema
expect(integrations.count).to eq(4)
expect(props).to match(
- no_properties.id => both(be_nil),
+ no_properties.id => both(be_nil),
with_plaintext_1.id => both(eq some_props(1)),
with_plaintext_2.id => both(eq some_props(2)),
- with_encrypted.id => match([be_nil, eq(some_props(3))])
+ with_encrypted.id => match([be_nil, eq(some_props(3))])
)
end
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
new file mode 100644
index 00000000000..41266cb24da
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/remove_backfilled_job_artifacts_expire_at_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::RemoveBackfilledJobArtifactsExpireAt do
+ it { expect(described_class).to be < Gitlab::BackgroundMigration::BatchedMigrationJob }
+
+ describe '#perform' do
+ let(:job_artifact) { table(:ci_job_artifacts, database: :ci) }
+
+ let(:test_worker) do
+ described_class.new(
+ start_id: 1,
+ end_id: 100,
+ batch_table: :ci_job_artifacts,
+ batch_column: :id,
+ sub_batch_size: 10,
+ pause_ms: 0,
+ connection: Ci::ApplicationRecord.connection
+ )
+ end
+
+ let_it_be(:namespace) { table(:namespaces).create!(id: 1, name: 'user', path: 'user') }
+ let_it_be(:project) do
+ table(:projects).create!(
+ id: 1,
+ name: 'gitlab1',
+ path: 'gitlab1',
+ project_namespace_id: 1,
+ namespace_id: namespace.id
+ )
+ end
+
+ subject { test_worker.perform }
+
+ context 'with artifacts that has backfilled expire_at' do
+ let!(:created_on_00_30_45_minutes_on_21_22_23) do
+ create_job_artifact(id: 1, file_type: 1, expire_at: Time.zone.parse('2022-01-21 00:00:00.000'))
+ create_job_artifact(id: 2, file_type: 1, expire_at: Time.zone.parse('2022-01-21 01:30:00.000'))
+ create_job_artifact(id: 3, file_type: 1, expire_at: Time.zone.parse('2022-01-22 12:00:00.000'))
+ create_job_artifact(id: 4, file_type: 1, expire_at: Time.zone.parse('2022-01-22 12:30:00.000'))
+ create_job_artifact(id: 5, file_type: 1, expire_at: Time.zone.parse('2022-01-23 23:00:00.000'))
+ create_job_artifact(id: 6, file_type: 1, expire_at: Time.zone.parse('2022-01-23 23:30:00.000'))
+ create_job_artifact(id: 7, file_type: 1, expire_at: Time.zone.parse('2022-01-23 06:45:00.000'))
+ end
+
+ let!(:created_close_to_00_or_30_minutes) do
+ create_job_artifact(id: 8, file_type: 1, expire_at: Time.zone.parse('2022-01-21 00:00:00.001'))
+ create_job_artifact(id: 9, file_type: 1, expire_at: Time.zone.parse('2022-01-21 00:30:00.999'))
+ end
+
+ let!(:created_on_00_or_30_minutes_on_other_dates) do
+ create_job_artifact(id: 10, file_type: 1, expire_at: Time.zone.parse('2022-01-01 00:00:00.000'))
+ create_job_artifact(id: 11, file_type: 1, expire_at: Time.zone.parse('2022-01-19 12:00:00.000'))
+ create_job_artifact(id: 12, file_type: 1, expire_at: Time.zone.parse('2022-01-24 23:30:00.000'))
+ end
+
+ let!(:created_at_other_times) do
+ create_job_artifact(id: 13, file_type: 1, expire_at: Time.zone.parse('2022-01-19 00:00:00.000'))
+ create_job_artifact(id: 14, file_type: 1, expire_at: Time.zone.parse('2022-01-19 00:30:00.000'))
+ create_job_artifact(id: 15, file_type: 1, expire_at: Time.zone.parse('2022-01-24 00:00:00.000'))
+ create_job_artifact(id: 16, file_type: 1, expire_at: Time.zone.parse('2022-01-24 00:30:00.000'))
+ end
+
+ it 'removes expire_at on job artifacts that have expire_at on 00, 30 or 45 minute of 21, 22, 23 of the month' do
+ expect { subject }.to change { job_artifact.where(expire_at: nil).count }.from(0).to(7)
+ end
+
+ it 'keeps expire_at on other job artifacts' do
+ expect { subject }.to change { job_artifact.where.not(expire_at: nil).count }.from(16).to(9)
+ end
+ end
+
+ context 'with trace artifacts that has backfilled expire_at' do
+ let!(:trace_artifacts) do
+ create_job_artifact(id: 1, file_type: 3, expire_at: Time.zone.parse('2022-01-01 00:00:00.000'))
+ create_job_artifact(id: 2, file_type: 3, expire_at: Time.zone.parse('2022-01-21 00:00:00.000'))
+ end
+
+ it 'removes expire_at on trace job artifacts' do
+ expect { subject }.to change { job_artifact.where(expire_at: nil).count }.from(0).to(2)
+ end
+ end
+
+ private
+
+ def create_job_artifact(id:, file_type:, expire_at:)
+ job = table(:ci_builds, database: :ci).create!(id: id)
+ job_artifact.create!(id: id, job_id: job.id, expire_at: expire_at, project_id: project.id, file_type: file_type)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/remove_self_managed_wiki_notes_spec.rb b/spec/lib/gitlab/background_migration/remove_self_managed_wiki_notes_spec.rb
new file mode 100644
index 00000000000..81927100562
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/remove_self_managed_wiki_notes_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::RemoveSelfManagedWikiNotes, :migration, schema: 20220601110011 do
+ let(:notes) { table(:notes) }
+
+ subject(:perform_migration) do
+ described_class.new(start_id: 1,
+ end_id: 30,
+ batch_table: :notes,
+ batch_column: :id,
+ sub_batch_size: 2,
+ pause_ms: 0,
+ connection: ActiveRecord::Base.connection)
+ .perform
+ end
+
+ it 'removes all wiki notes' do
+ notes.create!(id: 2, note: 'Commit note', noteable_type: 'Commit')
+ notes.create!(id: 10, note: 'Issue note', noteable_type: 'Issue')
+ notes.create!(id: 20, note: 'Wiki note', noteable_type: 'Wiki')
+ notes.create!(id: 30, note: 'MergeRequest note', noteable_type: 'MergeRequest')
+
+ expect(notes.where(noteable_type: 'Wiki').size).to eq(1)
+
+ expect { perform_migration }.to change(notes, :count).by(-1)
+
+ expect(notes.where(noteable_type: 'Wiki').size).to eq(0)
+ 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
new file mode 100644
index 00000000000..45932defaf9
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/rename_task_system_note_to_checklist_item_spec.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+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!(:note1) do
+ notes.create!(
+ note: 'marked the task **Task 1** as complete', noteable_type: 'Issue', noteable_id: issue.id, system: true
+ )
+ end
+
+ let!(:note2) do
+ notes.create!(
+ note: 'NO_MATCH marked the task **Task 2** as complete',
+ noteable_type: 'Issue',
+ noteable_id: issue.id,
+ system: true
+ )
+ end
+
+ let!(:note3) do
+ notes.create!(
+ note: 'marked the task **Task 3** as incomplete',
+ noteable_type: 'Issue',
+ noteable_id: issue.id,
+ system: true
+ )
+ end
+
+ let!(:note4) do
+ notes.create!(
+ note: 'marked the task **Task 4** as incomplete',
+ noteable_type: 'Issue',
+ noteable_id: issue.id,
+ system: true
+ )
+ end
+
+ let!(:metadata1) { table(:system_note_metadata).create!(note_id: note1.id, action: :task) }
+ let!(:metadata2) { table(:system_note_metadata).create!(note_id: note2.id, action: :task) }
+ let!(:metadata3) { table(:system_note_metadata).create!(note_id: note3.id, action: :task) }
+ let!(:metadata4) { table(:system_note_metadata).create!(note_id: note4.id, action: :not_task) }
+
+ let(:migration) do
+ described_class.new(
+ start_id: metadata1.id,
+ end_id: metadata4.id,
+ batch_table: :system_note_metadata,
+ batch_column: :id,
+ sub_batch_size: 2,
+ pause_ms: 2,
+ connection: ApplicationRecord.connection
+ )
+ end
+
+ subject(:perform_migration) { migration.perform }
+
+ it 'renames task to checklist item in task system notes that match', :aggregate_failures do
+ expect do
+ perform_migration
+
+ note1.reload
+ note2.reload
+ note3.reload
+ note4.reload
+ end.to change(note1, :note).to('marked the checklist item **Task 1** as complete').and(
+ not_change(note2, :note).from('NO_MATCH marked the task **Task 2** as complete')
+ ).and(
+ change(note3, :note).to('marked the checklist item **Task 3** as incomplete')
+ ).and(
+ not_change(note4, :note).from('marked the task **Task 4** as incomplete')
+ )
+ end
+
+ it 'updates in batches' do
+ expect { perform_migration }.to make_queries_matching(/UPDATE notes/, 2)
+ end
+
+ it 'tracks timings of queries' do
+ expect(migration.batch_metrics.timings).to be_empty
+
+ expect { perform_migration }.to change { migration.batch_metrics.timings }
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/set_correct_vulnerability_state_spec.rb b/spec/lib/gitlab/background_migration/set_correct_vulnerability_state_spec.rb
index d5b98e49a31..2372ce21c4c 100644
--- a/spec/lib/gitlab/background_migration/set_correct_vulnerability_state_spec.rb
+++ b/spec/lib/gitlab/background_migration/set_correct_vulnerability_state_spec.rb
@@ -34,7 +34,7 @@ RSpec.describe Gitlab::BackgroundMigration::SetCorrectVulnerabilityState do
let(:detected_state) { 1 }
let(:dismissed_state) { 2 }
- subject(:perform_migration) do
+ let(:migration_job) do
described_class.new(start_id: vulnerability_with_dismissed_at.id,
end_id: vulnerability_without_dismissed_at.id,
batch_table: :vulnerabilities,
@@ -42,15 +42,24 @@ RSpec.describe Gitlab::BackgroundMigration::SetCorrectVulnerabilityState do
sub_batch_size: 1,
pause_ms: 0,
connection: ActiveRecord::Base.connection)
- .perform
end
- it 'changes vulnerability state to `dismissed` when dismissed_at is not nil' do
- expect { perform_migration }.to change { vulnerability_with_dismissed_at.reload.state }.to(dismissed_state)
+ describe '#filter_batch' do
+ it 'filters out vulnerabilities where dismissed_at is null' do
+ expect(migration_job.filter_batch(vulnerabilities)).to contain_exactly(vulnerability_with_dismissed_at)
+ end
end
- it 'does not change the state when dismissed_at is nil' do
- expect { perform_migration }.not_to change { vulnerability_without_dismissed_at.reload.state }
+ describe '#perform' do
+ subject(:perform_migration) { migration_job.perform }
+
+ it 'changes vulnerability state to `dismissed` when dismissed_at is not nil' do
+ expect { perform_migration }.to change { vulnerability_with_dismissed_at.reload.state }.to(dismissed_state)
+ end
+
+ it 'does not change the state when dismissed_at is nil' do
+ expect { perform_migration }.not_to change { vulnerability_without_dismissed_at.reload.state }
+ end
end
private
diff --git a/spec/lib/gitlab/bullet/exclusions_spec.rb b/spec/lib/gitlab/bullet/exclusions_spec.rb
index ba42156b0c4..325b0167f58 100644
--- a/spec/lib/gitlab/bullet/exclusions_spec.rb
+++ b/spec/lib/gitlab/bullet/exclusions_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require 'tempfile'
RSpec.describe Gitlab::Bullet::Exclusions do
let(:config_file) do
diff --git a/spec/lib/gitlab/cache/helpers_spec.rb b/spec/lib/gitlab/cache/helpers_spec.rb
index 08e0d7729bd..39d37e979b4 100644
--- a/spec/lib/gitlab/cache/helpers_spec.rb
+++ b/spec/lib/gitlab/cache/helpers_spec.rb
@@ -18,10 +18,7 @@ RSpec.describe Gitlab::Cache::Helpers, :use_clean_rails_redis_caching do
end
describe "#render_cached" do
- subject do
- instance.render_cached(presentable, **kwargs)
- end
-
+ let(:method) { :render_cached }
let(:kwargs) do
{
with: presenter,
@@ -29,6 +26,10 @@ RSpec.describe Gitlab::Cache::Helpers, :use_clean_rails_redis_caching do
}
end
+ subject do
+ instance.public_send(method, presentable, **kwargs)
+ end
+
context 'single object' do
let_it_be(:presentable) { create(:merge_request, source_project: project, source_branch: 'wip') }
diff --git a/spec/lib/gitlab/changes_list_spec.rb b/spec/lib/gitlab/changes_list_spec.rb
index 8292764f561..762a121340e 100644
--- a/spec/lib/gitlab/changes_list_spec.rb
+++ b/spec/lib/gitlab/changes_list_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require "spec_helper"
+require 'fast_spec_helper'
RSpec.describe Gitlab::ChangesList do
let(:valid_changes_string) { "\n000000 570e7b2 refs/heads/my_branch\nd14d6c 6fd24d refs/heads/master" }
diff --git a/spec/lib/gitlab/chat/responder/base_spec.rb b/spec/lib/gitlab/chat/responder/base_spec.rb
index 667228cbab4..2a253449678 100644
--- a/spec/lib/gitlab/chat/responder/base_spec.rb
+++ b/spec/lib/gitlab/chat/responder/base_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Chat::Responder::Base do
let(:project) { double(:project) }
diff --git a/spec/lib/gitlab/ci/ansi2json/parser_spec.rb b/spec/lib/gitlab/ci/ansi2json/parser_spec.rb
index cf93ebe0721..b11002e8e93 100644
--- a/spec/lib/gitlab/ci/ansi2json/parser_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2json/parser_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
# The rest of the specs for this class are covered in style_spec.rb
RSpec.describe Gitlab::Ci::Ansi2json::Parser do
diff --git a/spec/lib/gitlab/ci/ansi2json/result_spec.rb b/spec/lib/gitlab/ci/ansi2json/result_spec.rb
index b7b4d6de8b9..14e2a9625fe 100644
--- a/spec/lib/gitlab/ci/ansi2json/result_spec.rb
+++ b/spec/lib/gitlab/ci/ansi2json/result_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Ansi2json::Result do
let(:stream) { StringIO.new('hello') }
diff --git a/spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb b/spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb
deleted file mode 100644
index 2c236ba3726..00000000000
--- a/spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Ci::Build::Artifacts::Adapters::ZipStream do
- let(:file_name) { 'single_file.zip' }
- let(:fixture_path) { "lib/gitlab/ci/build/artifacts/adapters/zip_stream/#{file_name}" }
- let(:stream) { File.open(expand_fixture_path(fixture_path), 'rb') }
-
- describe '#initialize' do
- it 'initializes when stream is passed' do
- expect { described_class.new(stream) }.not_to raise_error
- end
-
- context 'when stream is not passed' do
- let(:stream) { nil }
-
- it 'raises an error' do
- expect { described_class.new(stream) }.to raise_error(described_class::InvalidStreamError)
- end
- end
- end
-
- describe '#each_blob' do
- let(:adapter) { described_class.new(stream) }
-
- context 'when stream is a zip file' do
- it 'iterates file content when zip file contains one file' do
- expect { |b| adapter.each_blob(&b) }
- .to yield_with_args("file 1 content\n")
- end
-
- context 'when zip file contains multiple files' do
- let(:file_name) { 'multiple_files.zip' }
-
- it 'iterates content of all files' do
- expect { |b| adapter.each_blob(&b) }
- .to yield_successive_args("file 1 content\n", "file 2 content\n")
- end
- end
-
- context 'when zip file includes files in a directory' do
- let(:file_name) { 'with_directory.zip' }
-
- it 'iterates contents from files only' do
- expect { |b| adapter.each_blob(&b) }
- .to yield_successive_args("file 1 content\n", "file 2 content\n")
- end
- end
-
- context 'when zip contains a file which decompresses beyond the size limit' do
- let(:file_name) { '200_mb_decompressed.zip' }
-
- it 'does not read the file' do
- expect { |b| adapter.each_blob(&b) }.not_to yield_control
- end
- end
-
- context 'when the zip contains too many files' do
- let(:file_name) { '100_files.zip' }
-
- it 'stops processing when the limit is reached' do
- expect { |b| adapter.each_blob(&b) }
- .to yield_control.exactly(described_class::MAX_FILES_PROCESSED).times
- end
- end
-
- context 'when stream is a zipbomb' do
- let(:file_name) { 'zipbomb.zip' }
-
- it 'does not read the file' do
- expect { |b| adapter.each_blob(&b) }.not_to yield_control
- end
- end
- end
-
- context 'when stream is not a zip file' do
- let(:stream) { File.open(expand_fixture_path('junit/junit.xml.gz'), 'rb') }
-
- it 'does not yield any data' do
- expect { |b| adapter.each_blob(&b) }.not_to yield_control
- expect { adapter.each_blob { |b| b } }.not_to raise_error
- end
- end
- end
-end
diff --git a/spec/lib/gitlab/ci/build/artifacts/path_spec.rb b/spec/lib/gitlab/ci/build/artifacts/path_spec.rb
index 27b7dac2ae4..773eaf4b3fc 100644
--- a/spec/lib/gitlab/ci/build/artifacts/path_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/path_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Build::Artifacts::Path do
describe '#valid?' do
diff --git a/spec/lib/gitlab/ci/build/policy/variables_spec.rb b/spec/lib/gitlab/ci/build/policy/variables_spec.rb
index 436ad59bdf7..e560f1c2b5a 100644
--- a/spec/lib/gitlab/ci/build/policy/variables_spec.rb
+++ b/spec/lib/gitlab/ci/build/policy/variables_spec.rb
@@ -108,7 +108,8 @@ RSpec.describe Gitlab::Ci::Build::Policy::Variables do
project: project,
ref: 'master',
stage: 'review',
- environment: 'test/$CI_JOB_STAGE/1')
+ environment: 'test/$CI_JOB_STAGE/1',
+ ci_stage: build(:ci_stage, name: 'review', project: project, pipeline: pipeline))
end
before do
diff --git a/spec/lib/gitlab/ci/build/policy_spec.rb b/spec/lib/gitlab/ci/build/policy_spec.rb
index b85b093fd03..4baf4a1b4c4 100644
--- a/spec/lib/gitlab/ci/build/policy_spec.rb
+++ b/spec/lib/gitlab/ci/build/policy_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Build::Policy do
let(:policy) { spy('policy specification') }
diff --git a/spec/lib/gitlab/ci/build/port_spec.rb b/spec/lib/gitlab/ci/build/port_spec.rb
index 480418e0851..51820c1ab2c 100644
--- a/spec/lib/gitlab/ci/build/port_spec.rb
+++ b/spec/lib/gitlab/ci/build/port_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Build::Port do
subject { described_class.new(port) }
diff --git a/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb b/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb
index f192862c1c4..f9ebab149a5 100644
--- a/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb
+++ b/spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb
@@ -3,44 +3,54 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Build::Rules::Rule::Clause::Exists do
- shared_examples 'an exists rule with a context' do
- subject { described_class.new(globs).satisfied_by?(pipeline, context) }
+ describe '#satisfied_by?' do
+ shared_examples 'an exists rule with a context' do
+ it_behaves_like 'a glob matching rule' do
+ let(:project) { create(:project, :custom_repo, files: files) }
+ end
- it_behaves_like 'a glob matching rule' do
- let(:project) { create(:project, :custom_repo, files: files) }
- end
+ context 'after pattern comparision limit is reached' do
+ let(:globs) { ['*definitely_not_a_matching_glob*'] }
+ let(:project) { create(:project, :repository) }
- context 'after pattern comparision limit is reached' do
- let(:globs) { ['*definitely_not_a_matching_glob*'] }
- let(:project) { create(:project, :repository) }
+ before do
+ stub_const('Gitlab::Ci::Build::Rules::Rule::Clause::Exists::MAX_PATTERN_COMPARISONS', 2)
+ expect(File).to receive(:fnmatch?).twice.and_call_original
+ end
- before do
- stub_const('Gitlab::Ci::Build::Rules::Rule::Clause::Exists::MAX_PATTERN_COMPARISONS', 2)
- expect(File).to receive(:fnmatch?).twice.and_call_original
+ it { is_expected.to be_truthy }
end
-
- it { is_expected.to be_truthy }
end
- end
- describe '#satisfied_by?' do
- let(:pipeline) { build(:ci_pipeline, project: project, sha: project.repository.head_commit.sha) }
+ subject(:satisfied_by?) { described_class.new(globs).satisfied_by?(nil, context) }
context 'when context is Build::Context::Build' do
it_behaves_like 'an exists rule with a context' do
- let(:context) { Gitlab::Ci::Build::Context::Build.new(pipeline, sha: 'abc1234') }
+ let(:pipeline) { build(:ci_pipeline, project: project, sha: project.repository.commit.sha) }
+ let(:context) { Gitlab::Ci::Build::Context::Build.new(pipeline, sha: project.repository.commit.sha) }
end
end
context 'when context is Build::Context::Global' do
it_behaves_like 'an exists rule with a context' do
+ let(:pipeline) { build(:ci_pipeline, project: project, sha: project.repository.commit.sha) }
let(:context) { Gitlab::Ci::Build::Context::Global.new(pipeline, yaml_variables: {}) }
end
end
context 'when context is Config::External::Context' do
+ let(:context) { Gitlab::Ci::Config::External::Context.new(project: project, sha: sha) }
+
it_behaves_like 'an exists rule with a context' do
- let(:context) { Gitlab::Ci::Config::External::Context.new(project: project, sha: project.repository.tree.sha) }
+ let(:sha) { project.repository.commit.sha }
+ end
+
+ context 'when context has no project' do
+ let(:globs) { ['Dockerfile'] }
+ let(:project) {}
+ let(:sha) { 'abc1234' }
+
+ it { is_expected.to eq(false) }
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/environment_spec.rb b/spec/lib/gitlab/ci/config/entry/environment_spec.rb
index 36c26c8ee4f..3562706ff33 100644
--- a/spec/lib/gitlab/ci/config/entry/environment_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/environment_spec.rb
@@ -230,12 +230,12 @@ RSpec.describe Gitlab::Ci::Config::Entry::Environment do
end
end
- context 'when auto_stop_in is invalid format' do
- let(:auto_stop_in) { 'invalid' }
+ context 'when variables are used for auto_stop_in' do
+ let(:auto_stop_in) { '$TTL' }
- it 'becomes invalid' do
- expect(entry).not_to be_valid
- expect(entry.errors).to include 'environment auto stop in should be a duration'
+ it 'becomes valid' do
+ expect(entry).to be_valid
+ expect(entry.auto_stop_in).to eq(auto_stop_in)
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/image_spec.rb b/spec/lib/gitlab/ci/config/entry/image_spec.rb
index 6121c28070f..b37498ba10a 100644
--- a/spec/lib/gitlab/ci/config/entry/image_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/image_spec.rb
@@ -4,8 +4,6 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::Entry::Image do
before do
- stub_feature_flags(ci_docker_image_pull_policy: true)
-
entry.compose!
end
@@ -129,18 +127,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Image do
it 'is valid' do
expect(entry).to be_valid
end
-
- context 'when the feature flag ci_docker_image_pull_policy is disabled' do
- before do
- stub_feature_flags(ci_docker_image_pull_policy: false)
- entry.compose!
- end
-
- it 'is not valid' do
- expect(entry).not_to be_valid
- expect(entry.errors).to include('image config contains unknown keys: pull_policy')
- end
- end
end
describe '#value' do
@@ -150,19 +136,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Image do
pull_policy: ['if-not-present']
)
end
-
- context 'when the feature flag ci_docker_image_pull_policy is disabled' do
- before do
- stub_feature_flags(ci_docker_image_pull_policy: false)
- entry.compose!
- end
-
- it 'is not valid' do
- expect(entry.value).to eq(
- name: 'image:1.0'
- )
- end
- end
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 ca336c3ecaa..75ac2ca87ab 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -605,8 +605,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Job do
let(:deps) do
double('deps',
'default_entry' => default,
- 'workflow_entry' => workflow,
- 'variables_value' => nil)
+ 'workflow_entry' => workflow)
end
context 'when job config overrides default config' do
diff --git a/spec/lib/gitlab/ci/config/entry/legacy_variables_spec.rb b/spec/lib/gitlab/ci/config/entry/legacy_variables_spec.rb
new file mode 100644
index 00000000000..e9edec9a0a4
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/legacy_variables_spec.rb
@@ -0,0 +1,173 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Config::Entry::LegacyVariables do
+ let(:config) { {} }
+ let(:metadata) { {} }
+
+ subject(:entry) { described_class.new(config, **metadata) }
+
+ before do
+ entry.compose!
+ end
+
+ shared_examples 'valid config' do
+ describe '#value' do
+ it 'returns hash with key value strings' do
+ expect(entry.value).to eq result
+ end
+ end
+
+ describe '#errors' do
+ it 'does not append errors' do
+ expect(entry.errors).to be_empty
+ end
+ end
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+ end
+
+ shared_examples 'invalid config' do |error_message|
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(entry).not_to be_valid
+ end
+ end
+
+ describe '#errors' do
+ it 'saves errors' do
+ expect(entry.errors)
+ .to include(error_message)
+ end
+ end
+ end
+
+ context 'when entry config value has key-value pairs' do
+ let(:config) do
+ { 'VARIABLE_1' => 'value 1', 'VARIABLE_2' => 'value 2' }
+ end
+
+ let(:result) do
+ { 'VARIABLE_1' => 'value 1', 'VARIABLE_2' => 'value 2' }
+ end
+
+ it_behaves_like 'valid config'
+
+ describe '#value_with_data' do
+ it 'returns variable with data' do
+ expect(entry.value_with_data).to eq(
+ 'VARIABLE_1' => { value: 'value 1' },
+ 'VARIABLE_2' => { value: 'value 2' }
+ )
+ end
+ end
+ end
+
+ context 'with numeric keys and values in the config' do
+ let(:config) { { 10 => 20 } }
+ let(:result) do
+ { '10' => '20' }
+ end
+
+ it_behaves_like 'valid config'
+ end
+
+ context 'when key is an array' do
+ let(:config) { { ['VAR1'] => 'val1' } }
+ let(:result) do
+ { 'VAR1' => 'val1' }
+ end
+
+ it_behaves_like 'invalid config', /should be a hash of key value pairs/
+ end
+
+ context 'when value is a symbol' do
+ let(:config) { { 'VAR1' => :val1 } }
+ let(:result) do
+ { 'VAR1' => 'val1' }
+ end
+
+ it_behaves_like 'valid config'
+ end
+
+ context 'when value is a boolean' do
+ let(:config) { { 'VAR1' => true } }
+ let(:result) do
+ { 'VAR1' => 'val1' }
+ end
+
+ it_behaves_like 'invalid config', /should be a hash of key value pairs/
+ end
+
+ context 'when entry config value has key-value pair and hash' do
+ let(:config) do
+ { 'VARIABLE_1' => { value: 'value 1', description: 'variable 1' },
+ 'VARIABLE_2' => 'value 2' }
+ end
+
+ it_behaves_like 'invalid config', /should be a hash of key value pairs/
+
+ context 'when metadata has use_value_data: true' do
+ let(:metadata) { { use_value_data: true } }
+
+ let(:result) do
+ { 'VARIABLE_1' => 'value 1', 'VARIABLE_2' => 'value 2' }
+ end
+
+ it_behaves_like 'valid config'
+
+ describe '#value_with_data' do
+ it 'returns variable with data' do
+ expect(entry.value_with_data).to eq(
+ 'VARIABLE_1' => { value: 'value 1', description: 'variable 1' },
+ 'VARIABLE_2' => { value: 'value 2' }
+ )
+ end
+ end
+ end
+ end
+
+ context 'when entry value is an array' do
+ let(:config) { [:VAR, 'test'] }
+
+ it_behaves_like 'invalid config', /should be a hash of key value pairs/
+ end
+
+ context 'when metadata has use_value_data: true' do
+ let(:metadata) { { use_value_data: true } }
+
+ context 'when entry value has hash with other key-pairs' do
+ let(:config) do
+ { 'VARIABLE_1' => { value: 'value 1', hello: 'variable 1' },
+ 'VARIABLE_2' => 'value 2' }
+ end
+
+ it_behaves_like 'invalid config', /should be a hash of key value pairs, value can be a hash/
+ 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', /should be a hash of key value pairs, value can be a hash/
+ end
+
+ context 'when entry config value has hash without description' do
+ let(:config) do
+ { 'VARIABLE_1' => { value: 'value 1' } }
+ end
+
+ let(:result) do
+ { 'VARIABLE_1' => 'value 1' }
+ end
+
+ it_behaves_like 'valid config'
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/port_spec.rb b/spec/lib/gitlab/ci/config/entry/port_spec.rb
index e2840c07f6b..77f846f95f0 100644
--- a/spec/lib/gitlab/ci/config/entry/port_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/port_spec.rb
@@ -48,7 +48,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Port do
let(:config) do
{ number: 80,
protocol: 'http',
- name: 'foobar' }
+ name: 'foobar' }
end
describe '#valid?' do
diff --git a/spec/lib/gitlab/ci/config/entry/processable_spec.rb b/spec/lib/gitlab/ci/config/entry/processable_spec.rb
index 714b0a3b6aa..5f42a8c49a7 100644
--- a/spec/lib/gitlab/ci/config/entry/processable_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/processable_spec.rb
@@ -197,6 +197,34 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable do
end
end
end
+
+ context 'when a variable has an invalid data attribute' do
+ let(:config) do
+ {
+ script: 'echo',
+ variables: { 'VAR1' => 'val 1', 'VAR2' => { value: 'val 2', description: 'hello var 2' } }
+ }
+ end
+
+ it 'reports error about variable' do
+ expect(entry.errors)
+ .to include 'variables:var2 config must be a string'
+ end
+
+ context 'when the FF ci_variables_refactoring_to_variable is disabled' do
+ let(:entry_without_ff) { node_class.new(config, name: :rspec) }
+
+ before do
+ stub_feature_flags(ci_variables_refactoring_to_variable: false)
+ entry_without_ff.compose!
+ end
+
+ it 'reports error about variable' do
+ expect(entry_without_ff.errors)
+ .to include /config should be a hash of key value pairs/
+ end
+ end
+ end
end
end
@@ -212,13 +240,11 @@ RSpec.describe Gitlab::Ci::Config::Entry::Processable do
let(:unspecified) { double('unspecified', 'specified?' => false) }
let(:default) { double('default', '[]' => unspecified) }
let(:workflow) { double('workflow', 'has_rules?' => false) }
- let(:variables) {}
let(:deps) do
double('deps',
default_entry: default,
- workflow_entry: workflow,
- variables_value: variables)
+ workflow_entry: workflow)
end
context 'with workflow rules' do
diff --git a/spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb b/spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb
index ff44a235ea5..394d91466bf 100644
--- a/spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
require_dependency 'active_model'
RSpec.describe ::Gitlab::Ci::Config::Entry::Product::Matrix do
diff --git a/spec/lib/gitlab/ci/config/entry/root_spec.rb b/spec/lib/gitlab/ci/config/entry/root_spec.rb
index 55ad119ea21..3d19987a0be 100644
--- a/spec/lib/gitlab/ci/config/entry/root_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/root_spec.rb
@@ -117,49 +117,49 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
expect(root.jobs_value.keys).to eq([:rspec, :spinach, :release])
expect(root.jobs_value[:rspec]).to eq(
{ name: :rspec,
- script: %w[rspec ls],
- before_script: %w(ls pwd),
- image: { name: 'image:1.0' },
- services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
- stage: 'test',
- cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
- job_variables: {},
- root_variables_inheritance: true,
- ignore: false,
- after_script: ['make clean'],
- only: { refs: %w[branches tags] },
- scheduling_type: :stage }
+ script: %w[rspec ls],
+ before_script: %w(ls pwd),
+ image: { name: 'image:1.0' },
+ services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
+ stage: 'test',
+ cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
+ job_variables: {},
+ root_variables_inheritance: true,
+ ignore: false,
+ after_script: ['make clean'],
+ only: { refs: %w[branches tags] },
+ scheduling_type: :stage }
)
expect(root.jobs_value[:spinach]).to eq(
{ name: :spinach,
- before_script: [],
- script: %w[spinach],
- image: { name: 'image:1.0' },
- services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
- stage: 'test',
- cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
- job_variables: {},
- root_variables_inheritance: true,
- ignore: false,
- after_script: ['make clean'],
- only: { refs: %w[branches tags] },
- scheduling_type: :stage }
+ before_script: [],
+ script: %w[spinach],
+ image: { name: 'image:1.0' },
+ services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
+ stage: 'test',
+ cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
+ job_variables: {},
+ root_variables_inheritance: true,
+ ignore: false,
+ after_script: ['make clean'],
+ only: { refs: %w[branches tags] },
+ scheduling_type: :stage }
)
expect(root.jobs_value[:release]).to eq(
{ name: :release,
- stage: 'release',
- before_script: [],
- script: ["make changelog | tee release_changelog.txt"],
- release: { name: "Release $CI_TAG_NAME", tag_name: 'v0.06', description: "./release_changelog.txt" },
- image: { name: "image:1.0" },
- services: [{ name: "postgres:9.1" }, { name: "mysql:5.5" }],
- cache: [{ key: "k", untracked: true, paths: ["public/"], policy: "pull-push", when: 'on_success' }],
- only: { refs: %w(branches tags) },
- job_variables: { 'VAR' => 'job' },
- root_variables_inheritance: true,
- after_script: [],
- ignore: false,
- scheduling_type: :stage }
+ stage: 'release',
+ before_script: [],
+ script: ["make changelog | tee release_changelog.txt"],
+ release: { name: "Release $CI_TAG_NAME", tag_name: 'v0.06', description: "./release_changelog.txt" },
+ image: { name: "image:1.0" },
+ services: [{ name: "postgres:9.1" }, { name: "mysql:5.5" }],
+ cache: [{ key: "k", untracked: true, paths: ["public/"], policy: "pull-push", when: 'on_success' }],
+ only: { refs: %w(branches tags) },
+ job_variables: { 'VAR' => { value: 'job' } },
+ root_variables_inheritance: true,
+ after_script: [],
+ ignore: false,
+ scheduling_type: :stage }
)
end
end
@@ -196,31 +196,31 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
it 'returns jobs configuration' do
expect(root.jobs_value).to eq(
rspec: { name: :rspec,
- script: %w[rspec ls],
- before_script: %w(ls pwd),
- image: { name: 'image:1.0' },
- services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
- stage: 'test',
- cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
- job_variables: {},
- root_variables_inheritance: true,
- ignore: false,
- after_script: ['make clean'],
- only: { refs: %w[branches tags] },
- scheduling_type: :stage },
+ script: %w[rspec ls],
+ before_script: %w(ls pwd),
+ image: { name: 'image:1.0' },
+ services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
+ stage: 'test',
+ cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
+ job_variables: {},
+ root_variables_inheritance: true,
+ ignore: false,
+ after_script: ['make clean'],
+ only: { refs: %w[branches tags] },
+ scheduling_type: :stage },
spinach: { name: :spinach,
- before_script: [],
- script: %w[spinach],
- image: { name: 'image:1.0' },
- services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
- stage: 'test',
- cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
- job_variables: { 'VAR' => 'job' },
- root_variables_inheritance: true,
- ignore: false,
- after_script: ['make clean'],
- only: { refs: %w[branches tags] },
- scheduling_type: :stage }
+ before_script: [],
+ script: %w[spinach],
+ image: { name: 'image:1.0' },
+ services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
+ stage: 'test',
+ cache: [{ key: 'k', untracked: true, paths: ['public/'], policy: 'pull-push', when: 'on_success' }],
+ job_variables: { 'VAR' => { value: 'job' } },
+ root_variables_inheritance: true,
+ ignore: false,
+ after_script: ['make clean'],
+ only: { refs: %w[branches tags] },
+ scheduling_type: :stage }
)
end
end
@@ -350,6 +350,33 @@ RSpec.describe Gitlab::Ci::Config::Entry::Root do
end
end
end
+
+ context 'when a variable has an invalid data key' do
+ let(:hash) do
+ { variables: { VAR1: { invalid: 'hello' } }, rspec: { script: 'hello' } }
+ end
+
+ describe '#errors' do
+ it 'reports errors about the invalid variable' do
+ expect(root.errors)
+ .to include /var1 config uses invalid data keys: invalid/
+ end
+
+ context 'when the FF ci_variables_refactoring_to_variable is disabled' do
+ let(:root_without_ff) { described_class.new(hash, user: user, project: project) }
+
+ before do
+ stub_feature_flags(ci_variables_refactoring_to_variable: false)
+ root_without_ff.compose!
+ end
+
+ it 'reports errors about the invalid variable' do
+ expect(root_without_ff.errors)
+ .to include /variables config should be a hash of key value pairs, value can be a hash/
+ end
+ end
+ end
+ end
end
context 'when value is not a hash' do
diff --git a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
index c85fe366da6..303d825c591 100644
--- a/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb
@@ -1,8 +1,7 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
require 'gitlab_chronic_duration'
-require_dependency 'active_model'
RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule do
let(:factory) do
@@ -363,7 +362,20 @@ RSpec.describe Gitlab::Ci::Config::Entry::Rules::Rule do
it { is_expected.not_to be_valid }
it 'returns an error about invalid variables:' do
- expect(subject.errors).to include(/variables config should be a hash of key value pairs/)
+ expect(subject.errors).to include(/variables config should be a hash/)
+ end
+
+ context 'when the FF ci_variables_refactoring_to_variable is disabled' do
+ let(:entry_without_ff) { factory.create! }
+
+ before do
+ stub_feature_flags(ci_variables_refactoring_to_variable: false)
+ entry_without_ff.compose!
+ end
+
+ it 'returns an error about invalid variables:' do
+ expect(subject.errors).to include(/variables config should be a hash/)
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/service_spec.rb b/spec/lib/gitlab/ci/config/entry/service_spec.rb
index 821ab442d61..e36484bb0ae 100644
--- a/spec/lib/gitlab/ci/config/entry/service_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/service_spec.rb
@@ -4,7 +4,6 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::Entry::Service do
before do
- stub_feature_flags(ci_docker_image_pull_policy: true)
entry.compose!
end
@@ -149,18 +148,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Service do
it 'is valid' do
expect(entry).to be_valid
end
-
- context 'when the feature flag ci_docker_image_pull_policy is disabled' do
- before do
- stub_feature_flags(ci_docker_image_pull_policy: false)
- entry.compose!
- end
-
- it 'is not valid' do
- expect(entry).not_to be_valid
- expect(entry.errors).to include('service config contains unknown keys: pull_policy')
- end
- end
end
describe '#value' do
@@ -170,18 +157,6 @@ RSpec.describe Gitlab::Ci::Config::Entry::Service do
pull_policy: ['if-not-present']
)
end
-
- context 'when the feature flag ci_docker_image_pull_policy is disabled' do
- before do
- stub_feature_flags(ci_docker_image_pull_policy: false)
- end
-
- it 'is not valid' do
- expect(entry.value).to eq(
- name: 'postgresql:9.5'
- )
- end
- end
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/variable_spec.rb b/spec/lib/gitlab/ci/config/entry/variable_spec.rb
new file mode 100644
index 00000000000..744a89d4509
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/variable_spec.rb
@@ -0,0 +1,212 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Config::Entry::Variable do
+ let(:config) { {} }
+ let(:metadata) { {} }
+
+ subject(:entry) do
+ described_class.new(config, **metadata).tap do |entry|
+ entry.key = 'VAR1' # composable_hash requires key to be set
+ end
+ end
+
+ before do
+ entry.compose!
+ end
+
+ describe 'SimpleVariable' do
+ context 'when config is a string' do
+ let(:config) { 'value' }
+
+ describe '#valid?' do
+ it { is_expected.to be_valid }
+ end
+
+ describe '#value' do
+ subject(:value) { entry.value }
+
+ it { is_expected.to eq('value') }
+ end
+ end
+
+ context 'when config is an integer' do
+ let(:config) { 1 }
+
+ describe '#valid?' do
+ it { is_expected.to be_valid }
+ end
+
+ describe '#value' do
+ subject(:value) { entry.value }
+
+ it { is_expected.to eq('1') }
+ end
+ end
+
+ context 'when config is an array' do
+ let(:config) { [] }
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ subject(:errors) { entry.errors }
+
+ it { is_expected.to include 'variable definition must be either a string or a hash' }
+ end
+ end
+ end
+
+ describe 'ComplexVariable' do
+ context 'when config is a hash with description' do
+ let(:config) { { value: 'value', description: 'description' } }
+
+ context 'when metadata allowed_value_data is not provided' do
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ subject(:errors) { entry.errors }
+
+ it { is_expected.to include 'var1 config must be a string' }
+ end
+ end
+
+ context 'when metadata allowed_value_data is (value, description)' do
+ let(:metadata) { { allowed_value_data: %i[value description] } }
+
+ describe '#valid?' do
+ it { is_expected.to be_valid }
+ end
+
+ describe '#value' do
+ subject(:value) { entry.value }
+
+ it { is_expected.to eq('value') }
+ end
+
+ describe '#value_with_data' do
+ subject(:value_with_data) { entry.value_with_data }
+
+ it { is_expected.to eq(value: 'value', description: 'description') }
+ end
+
+ context 'when config value is a symbol' do
+ let(:config) { { value: :value, description: 'description' } }
+
+ describe '#value' do
+ subject(:value) { entry.value }
+
+ it { is_expected.to eq('value') }
+ end
+
+ describe '#value_with_data' do
+ subject(:value_with_data) { entry.value_with_data }
+
+ it { is_expected.to eq(value: 'value', description: 'description') }
+ end
+ end
+
+ context 'when config value is an integer' do
+ let(:config) { { value: 123, description: 'description' } }
+
+ describe '#value' do
+ subject(:value) { entry.value }
+
+ it { is_expected.to eq('123') }
+ end
+
+ describe '#value_with_data' do
+ subject(:value_with_data) { entry.value_with_data }
+
+ it { is_expected.to eq(value: '123', description: 'description') }
+ end
+ end
+
+ context 'when config value is an array' do
+ let(:config) { { value: ['value'], description: 'description' } }
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ subject(:errors) { entry.errors }
+
+ it { is_expected.to include 'var1 config value must be an alphanumeric string' }
+ end
+ end
+
+ context 'when config description is a symbol' do
+ let(:config) { { value: 'value', description: :description } }
+
+ describe '#value' do
+ subject(:value) { entry.value }
+
+ it { is_expected.to eq('value') }
+ end
+
+ describe '#value_with_data' do
+ subject(:value_with_data) { entry.value_with_data }
+
+ it { is_expected.to eq(value: 'value', description: :description) }
+ end
+ end
+ end
+
+ context 'when metadata allowed_value_data is (value, xyz)' do
+ let(:metadata) { { allowed_value_data: %i[value xyz] } }
+
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ subject(:errors) { entry.errors }
+
+ it { is_expected.to include 'var1 config uses invalid data keys: description' }
+ end
+ end
+ end
+
+ context 'when config is a hash without description' do
+ let(:config) { { value: 'value' } }
+
+ context 'when metadata allowed_value_data is not provided' do
+ describe '#valid?' do
+ it { is_expected.not_to be_valid }
+ end
+
+ describe '#errors' do
+ subject(:errors) { entry.errors }
+
+ it { is_expected.to include 'var1 config must be a string' }
+ end
+ end
+
+ context 'when metadata allowed_value_data is (value, description)' do
+ let(:metadata) { { allowed_value_data: %i[value description] } }
+
+ describe '#valid?' do
+ it { is_expected.to be_valid }
+ end
+
+ describe '#value' do
+ subject(:value) { entry.value }
+
+ it { is_expected.to eq('value') }
+ end
+
+ describe '#value_with_data' do
+ subject(:value_with_data) { entry.value_with_data }
+
+ it { is_expected.to eq(value: 'value') }
+ end
+ 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 78d37e228df..ad7290d0589 100644
--- a/spec/lib/gitlab/ci/config/entry/variables_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/variables_spec.rb
@@ -3,41 +3,46 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Config::Entry::Variables do
+ let(:config) { {} }
let(:metadata) { {} }
- subject { described_class.new(config, **metadata) }
+ subject(:entry) { described_class.new(config, **metadata) }
+
+ before do
+ entry.compose!
+ end
shared_examples 'valid config' do
describe '#value' do
it 'returns hash with key value strings' do
- expect(subject.value).to eq result
+ expect(entry.value).to eq result
end
end
describe '#errors' do
it 'does not append errors' do
- expect(subject.errors).to be_empty
+ expect(entry.errors).to be_empty
end
end
describe '#valid?' do
it 'is valid' do
- expect(subject).to be_valid
+ expect(entry).to be_valid
end
end
end
- shared_examples 'invalid config' do
+ shared_examples 'invalid config' do |error_message|
describe '#valid?' do
it 'is not valid' do
- expect(subject).not_to be_valid
+ expect(entry).not_to be_valid
end
end
describe '#errors' do
it 'saves errors' do
- expect(subject.errors)
- .to include /should be a hash of key value pairs/
+ expect(entry.errors)
+ .to include(error_message)
end
end
end
@@ -52,6 +57,15 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do
end
it_behaves_like 'valid config'
+
+ describe '#value_with_data' do
+ it 'returns variable with data' do
+ expect(entry.value_with_data).to eq(
+ 'VARIABLE_1' => { value: 'value 1' },
+ 'VARIABLE_2' => { value: 'value 2' }
+ )
+ end
+ end
end
context 'with numeric keys and values in the config' do
@@ -63,33 +77,63 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do
it_behaves_like 'valid config'
end
+ context 'when key is an array' do
+ let(:config) { { ['VAR1'] => 'val1' } }
+
+ it_behaves_like 'invalid config', /must be an alphanumeric string/
+ end
+
+ context 'when value is a symbol' do
+ let(:config) { { 'VAR1' => :val1 } }
+ let(:result) do
+ { 'VAR1' => 'val1' }
+ end
+
+ it_behaves_like 'valid config'
+ end
+
+ context 'when value is a boolean' do
+ let(:config) { { 'VAR1' => true } }
+
+ it_behaves_like 'invalid config', /must be either a string or a hash/
+ end
+
context 'when entry config value has key-value pair and hash' do
let(:config) do
{ 'VARIABLE_1' => { value: 'value 1', description: 'variable 1' },
'VARIABLE_2' => 'value 2' }
end
- let(:result) do
- { 'VARIABLE_1' => 'value 1', 'VARIABLE_2' => 'value 2' }
- end
+ it_behaves_like 'invalid config', /variable_1 config must be a string/
- it_behaves_like 'invalid config'
+ context 'when metadata has allowed_value_data' do
+ let(:metadata) { { allowed_value_data: %i[value description] } }
- context 'when metadata has use_value_data' do
- let(:metadata) { { use_value_data: true } }
+ let(:result) do
+ { 'VARIABLE_1' => 'value 1', 'VARIABLE_2' => 'value 2' }
+ end
it_behaves_like 'valid config'
+
+ describe '#value_with_data' do
+ it 'returns variable with data' do
+ expect(entry.value_with_data).to eq(
+ 'VARIABLE_1' => { value: 'value 1', description: 'variable 1' },
+ 'VARIABLE_2' => { value: 'value 2' }
+ )
+ end
+ end
end
end
context 'when entry value is an array' do
let(:config) { [:VAR, 'test'] }
- it_behaves_like 'invalid config'
+ it_behaves_like 'invalid config', /variables config should be a hash/
end
- context 'when metadata has use_value_data' do
- let(:metadata) { { use_value_data: true } }
+ context 'when metadata has allowed_value_data' do
+ let(:metadata) { { allowed_value_data: %i[value description] } }
context 'when entry value has hash with other key-pairs' do
let(:config) do
@@ -97,7 +141,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do
'VARIABLE_2' => 'value 2' }
end
- it_behaves_like 'invalid config'
+ 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
@@ -105,7 +149,7 @@ RSpec.describe Gitlab::Ci::Config::Entry::Variables do
{ 'VARIABLE_1' => { value: 'value 1', description: nil } }
end
- it_behaves_like 'invalid config'
+ 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
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 45dfea636f3..c22afb32756 100644
--- a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
@@ -219,4 +219,43 @@ RSpec.describe Gitlab::Ci::Config::External::File::Remote do
)
}
end
+
+ describe '#to_hash' do
+ subject(:to_hash) { remote_file.to_hash }
+
+ before do
+ stub_full_request(location).to_return(body: remote_file_content)
+ end
+
+ context 'with a valid remote file' do
+ it 'returns the content as a hash' do
+ expect(to_hash).to eql(
+ before_script: ["apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs",
+ "ruby -v",
+ "which ruby",
+ "bundle install --jobs $(nproc) \"${FLAGS[@]}\""]
+ )
+ end
+ end
+
+ context 'when it has `include` with rules:exists' do
+ let(:remote_file_content) do
+ <<~HEREDOC
+ include:
+ - local: another-file.yml
+ rules:
+ - exists: [Dockerfile]
+ HEREDOC
+ end
+
+ it 'returns the content as a hash' do
+ expect(to_hash).to eql(
+ include: [
+ { local: 'another-file.yml',
+ rules: [{ exists: ['Dockerfile'] }] }
+ ]
+ )
+ 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 e74fdc2071b..9eaba12f388 100644
--- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
@@ -210,7 +210,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
{ 'local' => local_file },
{ 'local' => local_file }
],
- image: 'image:1.0' }
+ image: 'image:1.0' }
end
it 'does not raise an exception' do
@@ -427,7 +427,7 @@ RSpec.describe Gitlab::Ci::Config::External::Mapper do
{ 'local' => 'hello/secret-file1.yml' },
{ 'local' => 'hello/secret-file2.yml' }
],
- image: 'ruby:2.7' }
+ image: 'ruby:2.7' }
end
it 'has expanset with two' do
diff --git a/spec/lib/gitlab/ci/config/external/processor_spec.rb b/spec/lib/gitlab/ci/config/external/processor_spec.rb
index 841a46e197d..b1dff6f9723 100644
--- a/spec/lib/gitlab/ci/config/external/processor_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/processor_spec.rb
@@ -94,6 +94,36 @@ RSpec.describe Gitlab::Ci::Config::External::Processor do
end
end
+ context 'when the remote file has `include` with rules:exists' 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
+ include:
+ - local: another-file.yml
+ rules:
+ - exists: [Dockerfile]
+
+ rspec:
+ script:
+ - bundle exec rspec
+ HEREDOC
+ end
+
+ before do
+ stub_full_request(remote_file).to_return(body: external_file_content)
+ end
+
+ it 'evaluates the rule as false' do
+ output = processor.perform
+ expect(output.keys).to match_array([:image, :rspec])
+ end
+
+ it "removes the 'include' keyword" do
+ expect(processor.perform[:include]).to be_nil
+ end
+ end
+
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
diff --git a/spec/lib/gitlab/ci/lint_spec.rb b/spec/lib/gitlab/ci/lint_spec.rb
index 7e0b2b5aa8e..3d46d266c13 100644
--- a/spec/lib/gitlab/ci/lint_spec.rb
+++ b/spec/lib/gitlab/ci/lint_spec.rb
@@ -341,9 +341,9 @@ RSpec.describe Gitlab::Ci::Lint do
let(:counters) do
{
'count' => a_kind_of(Numeric),
- 'avg' => a_kind_of(Numeric),
- 'max' => a_kind_of(Numeric),
- 'min' => a_kind_of(Numeric)
+ 'avg' => a_kind_of(Numeric),
+ 'max' => a_kind_of(Numeric),
+ 'min' => a_kind_of(Numeric)
}
end
diff --git a/spec/lib/gitlab/ci/mask_secret_spec.rb b/spec/lib/gitlab/ci/mask_secret_spec.rb
index 7d950c86700..ffe36e69a8f 100644
--- a/spec/lib/gitlab/ci/mask_secret_spec.rb
+++ b/spec/lib/gitlab/ci/mask_secret_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::MaskSecret do
subject { described_class }
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 c99cfa94aa6..38b229e0dd8 100644
--- a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Parsers::Sbom::CyclonedxProperties do
subject(:parse_source) { described_class.parse_source(properties) }
diff --git a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb
index 431fe9f3591..f3636106b98 100644
--- a/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb
@@ -102,11 +102,11 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::Cyclonedx do
it 'adds each component, ignoring unused attributes' do
expect(report).to receive(:add_component)
- .with({ "name" => "activesupport", "version" => "5.1.4", "type" => "library" })
+ .with(an_object_having_attributes(name: "activesupport", version: "5.1.4", component_type: "library"))
expect(report).to receive(:add_component)
- .with({ "name" => "byebug", "version" => "10.0.0", "type" => "library" })
+ .with(an_object_having_attributes(name: "byebug", version: "10.0.0", component_type: "library"))
expect(report).to receive(:add_component)
- .with({ "name" => "minimal-component", "type" => "library" })
+ .with(an_object_having_attributes(name: "minimal-component", version: nil, component_type: "library"))
parse!
end
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 30114b17cac..7222ebc3cb8 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
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning do
subject { described_class.source(property_data) }
@@ -17,11 +17,11 @@ RSpec.describe Gitlab::Ci::Parsers::Sbom::Source::DependencyScanning do
end
it 'returns expected source data' do
- is_expected.to eq({
- 'type' => :dependency_scanning,
- 'data' => property_data,
- 'fingerprint' => '4dbcb747e6f0fb3ed4f48d96b777f1d64acdf43e459fdfefad404e55c004a188'
- })
+ is_expected.to have_attributes(
+ source_type: :dependency_scanning,
+ data: property_data,
+ fingerprint: '4dbcb747e6f0fb3ed4f48d96b777f1d64acdf43e459fdfefad404e55c004a188'
+ )
end
end
diff --git a/spec/lib/gitlab/ci/parsers/security/common_spec.rb b/spec/lib/gitlab/ci/parsers/security/common_spec.rb
index 6495d1f654b..297ef1f5bb9 100644
--- a/spec/lib/gitlab/ci/parsers/security/common_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/security/common_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
}
end
- where(vulnerability_finding_signatures_enabled: [true, false])
+ where(signatures_enabled: [true, false])
with_them do
let_it_be(:pipeline) { create(:ci_pipeline) }
@@ -44,7 +44,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
let(:validator_class) { Gitlab::Ci::Parsers::Security::Validators::SchemaValidator }
let(:data) { {}.merge(scanner_data) }
let(:json_data) { data.to_json }
- let(:parser) { described_class.new(json_data, report, vulnerability_finding_signatures_enabled, validate: validate) }
+ let(:parser) { described_class.new(json_data, report, signatures_enabled: signatures_enabled, validate: validate) }
subject(:parse_report) { parser.parse! }
@@ -191,7 +191,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
context 'report parsing' do
before do
- artifact.each_blob { |blob| described_class.parse!(blob, report, vulnerability_finding_signatures_enabled) }
+ artifact.each_blob { |blob| described_class.parse!(blob, report, signatures_enabled: signatures_enabled) }
end
describe 'parsing finding.name' do
@@ -262,7 +262,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
describe 'top-level scanner' do
it 'is the primary scanner' do
expect(report.primary_scanner.external_id).to eq('gemnasium')
- expect(report.primary_scanner.name).to eq('Gemnasium')
+ expect(report.primary_scanner.name).to eq('Gemnasium top-level')
expect(report.primary_scanner.vendor).to eq('GitLab')
expect(report.primary_scanner.version).to eq('2.18.0')
end
@@ -278,9 +278,17 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
describe 'parsing scanners' do
subject(:scanner) { report.findings.first.scanner }
- context 'when vendor is not missing in scanner' do
- it 'returns scanner with parsed vendor value' do
- expect(scanner.vendor).to eq('GitLab')
+ context 'when the report contains top-level scanner' do
+ it 'sets the scanner of finding as top-level scanner' do
+ expect(scanner.name).to eq('Gemnasium top-level')
+ end
+ end
+
+ context 'when the report does not contain top-level scanner' do
+ let(:artifact) { build(:ci_job_artifact, :common_security_report_without_top_level_scanner) }
+
+ it 'sets the scanner of finding as `vulnerabilities[].scanner`' do
+ expect(scanner.name).to eq('Gemnasium')
end
end
end
@@ -465,7 +473,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
finding = report.findings.first
highest_signature = finding.signatures.max_by(&:priority)
- identifiers = if vulnerability_finding_signatures_enabled
+ identifiers = if signatures_enabled
"#{finding.report_type}-#{finding.primary_identifier.fingerprint}-#{highest_signature.signature_hex}-#{report.project_id}"
else
"#{finding.report_type}-#{finding.primary_identifier.fingerprint}-#{finding.location.fingerprint}-#{report.project_id}"
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 7828aa99f6a..e730afc72b5 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
@@ -19,8 +19,72 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
+ let(:report_data) do
+ {
+ 'scan' => {
+ 'analyzer' => {
+ 'id' => 'my-dast-analyzer',
+ 'name' => 'My DAST analyzer',
+ 'version' => '0.1.0',
+ 'vendor' => { 'name' => 'A DAST analyzer' }
+ },
+ 'end_time' => '2020-01-28T03:26:02',
+ 'scanned_resources' => [],
+ 'scanner' => {
+ 'id' => 'my-dast-scanner',
+ 'name' => 'My DAST scanner',
+ 'version' => '0.2.0',
+ 'vendor' => { 'name' => 'A DAST scanner' }
+ },
+ 'start_time' => '2020-01-28T03:26:01',
+ 'status' => 'success',
+ 'type' => 'dast'
+ },
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
let(:validator) { described_class.new(report_type, report_data, report_version, project: project, scanner: scanner) }
+ shared_examples 'report is valid' do
+ context 'and the report is valid' do
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ shared_examples 'logs related information' do
+ it 'logs related information' do
+ expect(Gitlab::AppLogger).to receive(:info).with(
+ message: "security report schema validation problem",
+ security_report_type: report_type,
+ security_report_version: report_version,
+ project_id: project.id,
+ security_report_failure: security_report_failure,
+ security_report_scanner_id: 'gemnasium',
+ security_report_scanner_version: '2.1.0'
+ )
+
+ subject
+ end
+ end
+
+ shared_examples 'report is invalid' do
+ context 'and the report is invalid' do
+ let(:report_data) do
+ {
+ 'version' => report_version
+ }
+ end
+
+ let(:security_report_failure) { 'schema_validation_fails' }
+
+ it { is_expected.to be_falsey }
+
+ it_behaves_like 'logs related information'
+ end
+ end
+
describe 'SUPPORTED_VERSIONS' do
schema_path = Rails.root.join("lib", "gitlab", "ci", "parsers", "security", "validators", "schemas")
@@ -75,80 +139,16 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
(latest_vendored_version[0...2] << "34").join(".")
end
- context 'and the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- it { is_expected.to be_truthy }
- end
-
- context 'and the report is invalid' do
- let(:report_data) do
- {
- 'version' => report_version
- }
- end
-
- it { is_expected.to be_falsey }
-
- it 'logs related information' do
- expect(Gitlab::AppLogger).to receive(:info).with(
- message: "security report schema validation problem",
- security_report_type: report_type,
- security_report_version: report_version,
- project_id: project.id,
- security_report_failure: 'schema_validation_fails',
- security_report_scanner_id: 'gemnasium',
- security_report_scanner_version: '2.1.0'
- )
-
- subject
- end
- end
+ it_behaves_like 'report is valid'
+ it_behaves_like 'report is invalid'
end
context 'when given a supported schema version' do
let(:report_type) { :dast }
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- context 'and the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- it { is_expected.to be_truthy }
- end
-
- context 'and the report is invalid' do
- let(:report_data) do
- {
- 'version' => report_version
- }
- end
-
- it { is_expected.to be_falsey }
-
- it 'logs related information' do
- expect(Gitlab::AppLogger).to receive(:info).with(
- message: "security report schema validation problem",
- security_report_type: report_type,
- security_report_version: report_version,
- project_id: project.id,
- security_report_failure: 'schema_validation_fails',
- security_report_scanner_id: 'gemnasium',
- security_report_scanner_version: '2.1.0'
- )
-
- subject
- end
- end
+ it_behaves_like 'report is valid'
+ it_behaves_like 'report is invalid'
end
context 'when given a deprecated schema version' do
@@ -173,21 +173,11 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
+ let(:security_report_failure) { 'using_deprecated_schema_version' }
+
it { is_expected.to be_truthy }
- it 'logs related information' do
- expect(Gitlab::AppLogger).to receive(:info).with(
- message: "security report schema validation problem",
- security_report_type: report_type,
- security_report_version: report_version,
- project_id: project.id,
- security_report_failure: 'using_deprecated_schema_version',
- security_report_scanner_id: 'gemnasium',
- security_report_scanner_version: '2.1.0'
- )
-
- subject
- end
+ it_behaves_like 'logs related information'
end
context 'and the report does not pass schema validation' do
@@ -213,21 +203,11 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
+ let(:security_report_failure) { 'using_unsupported_schema_version' }
+
it { is_expected.to be_falsey }
- it 'logs related information' do
- expect(Gitlab::AppLogger).to receive(:info).with(
- message: "security report schema validation problem",
- security_report_type: report_type,
- security_report_version: report_version,
- project_id: project.id,
- security_report_failure: 'using_unsupported_schema_version',
- security_report_scanner_id: 'gemnasium',
- security_report_scanner_version: '2.1.0'
- )
-
- subject
- end
+ it_behaves_like 'logs related information'
end
context 'and the report is invalid' do
@@ -282,6 +262,16 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
end
end
+ shared_examples 'report is valid with no error' do
+ context 'and the report is valid' do
+ it { is_expected.to be_empty }
+ end
+ end
+
+ shared_examples 'report with expected errors' do
+ it { is_expected.to match_array(expected_errors) }
+ end
+
describe '#errors' do
subject { validator.errors }
@@ -289,16 +279,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- context 'and the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- it { is_expected.to be_empty }
- end
+ it_behaves_like 'report is valid with no error'
context 'and the report is invalid' do
let(:report_data) do
@@ -309,11 +290,11 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:expected_errors) do
[
- 'root is missing required keys: vulnerabilities'
+ 'root is missing required keys: scan, vulnerabilities'
]
end
- it { is_expected.to match_array(expected_errors) }
+ it_behaves_like 'report with expected errors'
end
end
@@ -331,16 +312,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
stub_const("#{described_class}::DEPRECATED_VERSIONS", deprecations_hash)
end
- context 'and the report passes schema validation' do
- let(:report_data) do
- {
- 'version' => '10.0.0',
- 'vulnerabilities' => []
- }
- end
-
- it { is_expected.to be_empty }
- end
+ it_behaves_like 'report is valid with no error'
context 'and the report does not pass schema validation' do
let(:report_data) do
@@ -356,7 +328,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
]
end
- it { is_expected.to match_array(expected_errors) }
+ it_behaves_like 'report with expected errors'
end
end
@@ -383,7 +355,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
]
end
- it { is_expected.to match_array(expected_errors) }
+ it_behaves_like 'report with expected errors'
end
context 'and the report is invalid' do
@@ -400,7 +372,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
]
end
- it { is_expected.to match_array(expected_errors) }
+ it_behaves_like 'report with expected errors'
end
end
@@ -426,10 +398,27 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
]
end
- it { is_expected.to match_array(expected_errors) }
+ it_behaves_like 'report with expected errors'
end
end
+ shared_examples 'report is valid with no warning' do
+ context 'and the report is valid' do
+ let(:report_data) do
+ {
+ 'version' => report_version,
+ 'vulnerabilities' => []
+ }
+ end
+
+ it { is_expected.to be_empty }
+ end
+ end
+
+ shared_examples 'report with expected warnings' do
+ it { is_expected.to match_array(expected_deprecation_warnings) }
+ end
+
describe '#deprecation_warnings' do
subject { validator.deprecation_warnings }
@@ -491,7 +480,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
- it { is_expected.to match_array(expected_deprecation_warnings) }
+ it_behaves_like 'report with expected warnings'
end
context 'and the report does not pass schema validation' do
@@ -501,7 +490,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
- it { is_expected.to match_array(expected_deprecation_warnings) }
+ it_behaves_like 'report with expected warnings'
end
end
@@ -516,7 +505,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
- it { is_expected.to match_array(expected_deprecation_warnings) }
+ it_behaves_like 'report with expected warnings'
end
end
@@ -561,21 +550,11 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
}
end
+ let(:security_report_failure) { 'schema_validation_fails' }
+
it { is_expected.to match_array([message]) }
- it 'logs related information' do
- expect(Gitlab::AppLogger).to receive(:info).with(
- message: "security report schema validation problem",
- security_report_type: report_type,
- security_report_version: report_version,
- project_id: project.id,
- security_report_failure: 'schema_validation_fails',
- security_report_scanner_id: 'gemnasium',
- security_report_scanner_version: '2.1.0'
- )
-
- subject
- end
+ it_behaves_like 'logs related information'
end
end
@@ -583,16 +562,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { described_class::SUPPORTED_VERSIONS[report_type].last }
- context 'and the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- it { is_expected.to be_empty }
- end
+ it_behaves_like 'report is valid with no warning'
context 'and the report is invalid' do
let(:report_data) do
@@ -644,16 +614,7 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Validators::SchemaValidator do
let(:report_type) { :dast }
let(:report_version) { "12.37.0" }
- context 'and the report is valid' do
- let(:report_data) do
- {
- 'version' => report_version,
- 'vulnerabilities' => []
- }
- end
-
- it { is_expected.to be_empty }
- end
+ it_behaves_like 'report is valid with no warning'
context 'and the report is invalid' do
let(:report_data) do
diff --git a/spec/lib/gitlab/ci/parsers/test/junit_spec.rb b/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
index 82fa11d5f98..821a5057d2e 100644
--- a/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
+++ b/spec/lib/gitlab/ci/parsers/test/junit_spec.rb
@@ -4,11 +4,13 @@ require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Parsers::Test::Junit do
describe '#parse!' do
- subject { described_class.new.parse!(junit, test_suite, job: job) }
+ subject { described_class.new.parse!(junit, test_report, job: job) }
- let(:test_suite) { Gitlab::Ci::Reports::TestSuite.new('rspec') }
+ let(:job) { double(test_suite_name: 'rspec', max_test_cases_per_report: max_test_cases) }
+
+ let(:test_report) { Gitlab::Ci::Reports::TestReport.new }
+ let(:test_suite) { test_report.get_suite(job.test_suite_name) }
let(:test_cases) { flattened_test_cases(test_suite) }
- let(:job) { double(max_test_cases_per_report: max_test_cases) }
let(:max_test_cases) { 0 }
context 'when data is JUnit style XML' do
diff --git a/spec/lib/gitlab/ci/parsers_spec.rb b/spec/lib/gitlab/ci/parsers_spec.rb
index c9891c06507..a9adff4fce3 100644
--- a/spec/lib/gitlab/ci/parsers_spec.rb
+++ b/spec/lib/gitlab/ci/parsers_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Parsers do
describe '.fabricate!' do
diff --git a/spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb
new file mode 100644
index 00000000000..15df5b2f68c
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Pipeline::Chain::AssignPartition do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user)
+ end
+
+ let(:pipeline) { build(:ci_pipeline, project: project) }
+ let(:step) { described_class.new(pipeline, command) }
+ let(:current_partition_id) { 123 }
+
+ describe '#perform!' do
+ before do
+ allow(Ci::Pipeline).to receive(:current_partition_value) { current_partition_id }
+ end
+
+ subject { step.perform! }
+
+ it 'assigns partition_id to pipeline' do
+ expect { subject }.to change(pipeline, :partition_id).to(current_partition_id)
+ end
+
+ context 'with parent-child pipelines' do
+ let(:bridge) do
+ instance_double(Ci::Bridge,
+ triggers_child_pipeline?: true,
+ parent_pipeline: instance_double(Ci::Pipeline, partition_id: 125))
+ end
+
+ let(:command) do
+ Gitlab::Ci::Pipeline::Chain::Command.new(
+ project: project,
+ current_user: user,
+ bridge: bridge)
+ end
+
+ it 'assigns partition_id to pipeline' do
+ expect { subject }.to change(pipeline, :partition_id).to(125)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
index de43e759193..6e8b6e40928 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
@@ -302,13 +302,13 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do
context 'when bridge is present' do
context 'when bridge triggers a child pipeline' do
- let(:bridge) { double(:bridge, triggers_child_pipeline?: true) }
+ let(:bridge) { instance_double(Ci::Bridge, triggers_child_pipeline?: true) }
it { is_expected.to be_truthy }
end
context 'when bridge triggers a multi-project pipeline' do
- let(:bridge) { double(:bridge, triggers_child_pipeline?: false) }
+ let(:bridge) { instance_double(Ci::Bridge, triggers_child_pipeline?: false) }
it { is_expected.to be_falsey }
end
@@ -321,6 +321,38 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do
end
end
+ describe '#parent_pipeline_partition_id' do
+ let(:command) { described_class.new(bridge: bridge) }
+
+ subject { command.parent_pipeline_partition_id }
+
+ context 'when bridge is present' do
+ context 'when bridge triggers a child pipeline' do
+ let(:pipeline) { instance_double(Ci::Pipeline, partition_id: 123) }
+
+ let(:bridge) do
+ instance_double(Ci::Bridge,
+ triggers_child_pipeline?: true,
+ parent_pipeline: pipeline)
+ end
+
+ it { is_expected.to eq(123) }
+ end
+
+ context 'when bridge triggers a multi-project pipeline' do
+ let(:bridge) { instance_double(Ci::Bridge, triggers_child_pipeline?: false) }
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ context 'when bridge is not present' do
+ let(:bridge) { nil }
+
+ it { is_expected.to be_nil }
+ end
+ end
+
describe '#increment_pipeline_failure_reason_counter' do
let(:command) { described_class.new }
let(:reason) { :size_limit_exceeded }
@@ -345,7 +377,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Command do
describe '#observe_step_duration' do
context 'when ci_pipeline_creation_step_duration_tracking is enabled' do
it 'adds the duration to the step duration histogram' do
- histogram = double(:histogram)
+ histogram = instance_double(Prometheus::Client::Histogram)
duration = 1.hour
expect(::Gitlab::Ci::Pipeline::Metrics).to receive(:pipeline_creation_step_duration_histogram)
diff --git a/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb
index e0d656f456e..f451bd6bfef 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb
@@ -11,7 +11,9 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Config::Content do
subject { described_class.new(pipeline, command) }
- describe '#perform!' do
+ # TODO: change this to `describe` and remove rubocop-disable
+ # when removing the FF ci_project_pipeline_config_refactoring
+ shared_context '#perform!' do # rubocop:disable RSpec/ContextWording
context 'when bridge job is passed in as parameter' do
let(:ci_config_path) { nil }
let(:bridge) { create(:ci_bridge) }
@@ -201,4 +203,14 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Config::Content do
end
end
end
+
+ it_behaves_like '#perform!'
+
+ context 'when the FF ci_project_pipeline_config_refactoring is disabled' do
+ before do
+ stub_feature_flags(ci_project_pipeline_config_refactoring: false)
+ end
+
+ it_behaves_like '#perform!'
+ 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 e07a3ca9033..7fb5b0b4200 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/ensure_environments_spec.rb
@@ -2,11 +2,13 @@
require 'spec_helper'
-RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
+RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments, :aggregate_failures do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:stage) { build(:ci_stage, project: project, statuses: [job]) }
let(:pipeline) { build(:ci_pipeline, project: project, stages: [stage]) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:environment) { project.environments.find_by_name('review/master') }
let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user)
@@ -24,12 +26,26 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
context 'when a pipeline contains a deployment job' do
let!(:job) { build(:ci_build, :start_review_app, project: project) }
- it 'ensures environment existence for the job' do
- expect { subject }.to change { Environment.count }.by(1)
+ context 'and the environment does not exist' do
+ it 'creates the environment specified by the job' do
+ expect { subject }.to change { Environment.count }.by(1)
- expect(project.environments.find_by_name('review/master')).to be_present
- expect(job.persisted_environment.name).to eq('review/master')
- expect(job.metadata.expanded_environment_name).to eq('review/master')
+ expect(environment).to be_present
+ expect(job.persisted_environment.name).to eq('review/master')
+ expect(job.metadata.expanded_environment_name).to eq('review/master')
+ 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
+
+ it 'associates the environment with the merge request' do
+ expect { subject }.to change { Environment.count }.by(1)
+
+ expect(environment.merge_request).to eq(merge_request)
+ end
+ end
end
context 'when an environment has already been existed' do
@@ -40,10 +56,22 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
it 'ensures environment existence for the job' do
expect { subject }.not_to change { Environment.count }
- expect(project.environments.find_by_name('review/master')).to be_present
+ expect(environment).to be_present
expect(job.persisted_environment.name).to eq('review/master')
expect(job.metadata.expanded_environment_name).to eq('review/master')
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
+
+ it 'does not associate the environment with the merge request' do
+ expect { subject }.not_to change { Environment.count }
+
+ expect(environment.merge_request).to be_nil
+ end
+ end
end
context 'when an environment name contains an invalid character' do
@@ -65,7 +93,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::EnsureEnvironments do
it 'ensures environment existence for the job' do
expect { subject }.to change { Environment.count }.by(1)
- expect(project.environments.find_by_name('review/master')).to be_present
+ expect(environment).to be_present
expect(job.persisted_environment.name).to eq('review/master')
expect(job.metadata.expanded_environment_name).to eq('review/master')
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
index f7774e199fb..8c4f7af0ef4 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb
@@ -277,7 +277,6 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do
non_handled_sql_queries = 2
# 1. Ci::InstanceVariable Load => `Ci::InstanceVariable#cached_data` => already cached with `fetch_memory_cache`
- # 2. Ci::Variable Load => `Project#ci_variables_for` => already cached with `Gitlab::SafeRequestStore`
extra_jobs * non_handled_sql_queries
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
index e8eb3333b88..ee32661f267 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/sequence_spec.rb
@@ -83,19 +83,36 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Sequence do
.with({ source: 'push' }, 0)
end
- it 'records active jobs by pipeline plan in a histogram' do
- allow(command.metrics)
- .to receive(:active_jobs_histogram)
- .and_return(histogram)
+ describe 'active jobs by pipeline plan histogram' do
+ before do
+ allow(command.metrics)
+ .to receive(:active_jobs_histogram)
+ .and_return(histogram)
+
+ pipeline = create(:ci_pipeline, :running, project: project)
+ create_list(:ci_build, 3, pipeline: pipeline)
+ create(:ci_bridge, pipeline: pipeline)
+ end
- pipeline = create(:ci_pipeline, project: project, status: :running)
- create(:ci_build, :finished, project: project, pipeline: pipeline)
- create(:ci_build, :failed, project: project, pipeline: pipeline)
- create(:ci_build, :running, project: project, pipeline: pipeline)
- subject.build!
+ it 'counts all the active jobs' do
+ subject.build!
- expect(histogram).to have_received(:observe)
- .with(hash_including(plan: project.actual_plan_name), 3)
+ expect(histogram).to have_received(:observe)
+ .with(hash_including(plan: project.actual_plan_name), 4)
+ end
+
+ context 'when feature flag ci_limit_active_jobs_early is disabled' do
+ before do
+ stub_feature_flags(ci_limit_active_jobs_early: false)
+ end
+
+ it 'counts all the active builds' do
+ subject.build!
+
+ expect(histogram).to have_received(:observe)
+ .with(hash_including(plan: project.actual_plan_name), 3)
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb
index fb1a360a4b7..52a00e0d501 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb
@@ -179,6 +179,70 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Validate::External do
perform!
end
end
+
+ describe 'credit_card' do
+ context 'with no registered credit_card' do
+ it 'returns the expected credit card counts' do
+ expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
+ payload = Gitlab::Json.parse(params[:body])
+
+ expect(payload['credit_card']['similar_cards_count']).to eq(0)
+ expect(payload['credit_card']['similar_holder_names_count']).to eq(0)
+ end
+
+ perform!
+ end
+ end
+
+ context 'with a registered credit card' do
+ let!(:credit_card) { create(:credit_card_validation, last_digits: 10, holder_name: 'Alice', user: user) }
+
+ it 'returns the expected credit card counts' do
+ expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
+ payload = Gitlab::Json.parse(params[:body])
+
+ expect(payload['credit_card']['similar_cards_count']).to eq(1)
+ expect(payload['credit_card']['similar_holder_names_count']).to eq(1)
+ end
+
+ perform!
+ end
+
+ context 'with similar credit cards registered by other users' do
+ before do
+ create(:credit_card_validation, last_digits: 10, holder_name: 'Bob')
+ end
+
+ it 'returns the expected credit card counts' do
+ expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
+ payload = Gitlab::Json.parse(params[:body])
+
+ expect(payload['credit_card']['similar_cards_count']).to eq(2)
+ expect(payload['credit_card']['similar_holder_names_count']).to eq(1)
+ end
+
+ perform!
+ end
+ end
+
+ context 'with similar holder names registered by other users' do
+ before do
+ create(:credit_card_validation, last_digits: 11, holder_name: 'Alice')
+ end
+
+ it 'returns the expected credit card counts' do
+ expect(::Gitlab::HTTP).to receive(:post) do |_url, params|
+ payload = Gitlab::Json.parse(params[:body])
+
+ expect(payload['credit_card']['similar_cards_count']).to eq(1)
+ expect(payload['credit_card']['similar_holder_names_count']).to eq(2)
+ end
+
+ perform!
+ end
+ end
+ end
+ end
end
context 'when EXTERNAL_VALIDATION_SERVICE_TOKEN is set' do
diff --git a/spec/lib/gitlab/ci/pipeline/duration_spec.rb b/spec/lib/gitlab/ci/pipeline/duration_spec.rb
index e0b4928d7f7..46c7072ad8e 100644
--- a/spec/lib/gitlab/ci/pipeline/duration_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/duration_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Duration do
let(:calculated_duration) { calculate(data) }
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/null_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/null_spec.rb
index 49686d1a9bd..3ca6fd9143f 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/null_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/null_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Null do
describe '.build' do
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/string_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/string_spec.rb
index c6d0d2534a5..b224fca6011 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/string_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/string_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::String do
describe '.build' do
diff --git a/spec/lib/gitlab/ci/pipeline/expression/lexeme/variable_spec.rb b/spec/lib/gitlab/ci/pipeline/expression/lexeme/variable_spec.rb
index 3e10ca686ba..b030bd22aa1 100644
--- a/spec/lib/gitlab/ci/pipeline/expression/lexeme/variable_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/expression/lexeme/variable_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Expression::Lexeme::Variable do
describe '.build' do
diff --git a/spec/lib/gitlab/ci/pipeline/metrics_spec.rb b/spec/lib/gitlab/ci/pipeline/metrics_spec.rb
index 83b969ff3c4..8df3c67beaa 100644
--- a/spec/lib/gitlab/ci/pipeline/metrics_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/metrics_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe ::Gitlab::Ci::Pipeline::Metrics do
describe '.pipeline_creation_step_duration_histogram' do
diff --git a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
index 890ba51157a..75f6a773c2d 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/build_spec.rb
@@ -97,15 +97,15 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
let(:attributes) do
{ name: 'rspec',
ref: 'master',
- job_variables: [{ key: 'VAR1', value: 'var 1', public: true },
- { key: 'VAR2', value: 'var 2', public: true }],
+ 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 do
- is_expected.to include(yaml_variables: [{ key: 'VAR1', value: 'new var 1', public: true },
- { key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }])
+ is_expected.to include(yaml_variables: [{ key: 'VAR1', value: 'new var 1' },
+ { key: 'VAR3', value: 'var 3' },
+ { key: 'VAR2', value: 'var 2' }])
end
end
@@ -114,13 +114,13 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
{
name: 'rspec',
ref: 'master',
- job_variables: [{ key: 'VARIABLE', value: 'value', public: true }],
+ job_variables: [{ key: 'VARIABLE', value: 'value' }],
tag_list: ['static-tag', '$VARIABLE', '$NO_VARIABLE']
}
end
it { is_expected.to include(tag_list: ['static-tag', 'value', '$NO_VARIABLE']) }
- it { is_expected.to include(yaml_variables: [{ key: 'VARIABLE', value: 'value', public: true }]) }
+ it { is_expected.to include(yaml_variables: [{ key: 'VARIABLE', value: 'value' }]) }
end
context 'with cache:key' do
@@ -257,19 +257,19 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
let(:attributes) do
{ name: 'rspec',
ref: 'master',
- yaml_variables: [{ key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }],
- job_variables: [{ key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }],
+ 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', public: true },
- { key: 'VAR2', value: 'var pipeline 2', public: true },
- { key: 'VAR3', value: 'var pipeline 3', public: true },
- { key: 'VAR4', value: 'new var pipeline 4', public: true }]
+ [{ 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
context 'when root_variables_inheritance is true' do
@@ -277,10 +277,10 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
it 'returns calculated yaml variables' do
expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR1', value: 'var overridden pipeline 1', public: true },
- { key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true },
- { key: 'VAR4', value: 'new var pipeline 4', public: true }]
+ [{ 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
@@ -290,8 +290,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
it 'returns job variables' do
expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }]
+ [{ key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }]
)
end
end
@@ -301,9 +301,9 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
it 'returns calculated yaml variables' do
expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR1', value: 'var overridden pipeline 1', public: true },
- { key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }]
+ [{ key: 'VAR1', value: 'var overridden pipeline 1' },
+ { key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }]
)
end
end
@@ -314,8 +314,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
it 'returns seed yaml variables' do
expect(subject[:yaml_variables]).to match_array(
- [{ key: 'VAR2', value: 'var 2', public: true },
- { key: 'VAR3', value: 'var 3', public: true }])
+ [{ key: 'VAR2', value: 'var 2' },
+ { key: 'VAR3', value: 'var 3' }])
end
end
end
@@ -324,8 +324,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
let(:attributes) do
{ name: 'rspec',
ref: 'master',
- yaml_variables: [{ key: 'VAR1', value: 'var 1', public: true }],
- job_variables: [{ key: 'VAR1', value: 'var 1', public: true }],
+ yaml_variables: [{ key: 'VAR1', value: 'var 1' }],
+ job_variables: [{ key: 'VAR1', value: 'var 1' }],
root_variables_inheritance: root_variables_inheritance,
rules: rules }
end
@@ -338,14 +338,14 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
end
it 'recalculates the variables' do
- expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1', public: true },
- { key: 'VAR2', value: 'new var 2', public: true })
+ expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1' },
+ { key: 'VAR2', value: 'new var 2' })
end
end
context 'when the rules use root variables' do
let(:root_variables) do
- [{ key: 'VAR2', value: 'var pipeline 2', public: true }]
+ [{ key: 'VAR2', value: 'var pipeline 2' }]
end
let(:rules) do
@@ -353,15 +353,15 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Build do
end
it 'recalculates the variables' do
- expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1', public: true },
- { key: 'VAR2', value: 'overridden var 2', public: true })
+ expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'overridden var 1' },
+ { key: 'VAR2', value: 'overridden var 2' })
end
context 'when the root_variables_inheritance is false' do
let(:root_variables_inheritance) { false }
it 'does not recalculate the variables' do
- expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'var 1', public: true })
+ expect(subject[:yaml_variables]).to contain_exactly({ key: 'VAR1', value: 'var 1' })
end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb
index 51185be3e74..6569ce937ac 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb
@@ -6,8 +6,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Deployment do
let_it_be(:project, refind: true) { create(:project, :repository) }
let(:pipeline) do
- create(:ci_pipeline, project: project,
- sha: 'b83d6e391c22777fca1ed3012fce84f633d7fed0')
+ create(:ci_pipeline, project: project, sha: 'b83d6e391c22777fca1ed3012fce84f633d7fed0')
end
let(:job) { build(:ci_build, project: project, pipeline: pipeline) }
diff --git a/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb
index ad89f1f5cda..2b9d8127886 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb
@@ -5,7 +5,9 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do
let_it_be(:project) { create(:project) }
- let(:job) { build(:ci_build, project: project) }
+ let!(:pipeline) { create(:ci_pipeline, project: project) }
+
+ let(:job) { build(:ci_build, project: project, pipeline: pipeline) }
let(:seed) { described_class.new(job) }
let(:attributes) { {} }
@@ -87,6 +89,28 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do
it_behaves_like 'returning a correct environment'
end
+
+ context 'and job environment has an auto_stop_in variable attribute' do
+ let(:environment_auto_stop_in) { '10 minutes' }
+ let(:expected_auto_stop_in) { '10 minutes' }
+
+ let(:attributes) do
+ {
+ environment: environment_name,
+ options: {
+ environment: {
+ name: environment_name,
+ auto_stop_in: '$TTL'
+ }
+ },
+ yaml_variables: [
+ { key: "TTL", value: environment_auto_stop_in, public: true }
+ ]
+ }
+ end
+
+ it_behaves_like 'returning a correct environment'
+ end
end
context 'when job has deployment tier attribute' do
@@ -167,5 +191,34 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do
it_behaves_like 'returning a correct environment'
end
+
+ context 'when merge_request is provided' do
+ 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) }
+
+ context 'and environment does not exist' do
+ let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' }
+
+ it 'creates an environment associated with the merge request' do
+ expect { subject }.to change { Environment.count }.by(1)
+
+ expect(subject.merge_request).to eq(merge_request)
+ end
+ end
+
+ context 'and environment already exists' do
+ before do
+ create(:environment, project: project, name: environment_name)
+ end
+
+ it 'does not change the merge request associated with the environment' do
+ expect { subject }.not_to change { Environment.count }
+
+ expect(subject.merge_request).to be_nil
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/processable_object_hierarchy_spec.rb b/spec/lib/gitlab/ci/processable_object_hierarchy_spec.rb
new file mode 100644
index 00000000000..a844ce6486b
--- /dev/null
+++ b/spec/lib/gitlab/ci/processable_object_hierarchy_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::ProcessableObjectHierarchy do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { project.owner }
+
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, project: project, ref: 'master') }
+
+ let_it_be(:job1) { create(:ci_build, :created, pipeline: pipeline, name: 'job1') }
+ let_it_be(:job2) { create(:ci_build, :created, :dependent, pipeline: pipeline, name: 'job2', needed: job1) }
+ let_it_be(:job3) { create(:ci_build, :created, :dependent, pipeline: pipeline, name: 'job3', needed: job1) }
+ let_it_be(:job4) { create(:ci_build, :created, :dependent, pipeline: pipeline, name: 'job4', needed: job2) }
+ let_it_be(:job5) { create(:ci_build, :created, :dependent, pipeline: pipeline, name: 'job5', needed: job3) }
+ let_it_be(:job6) { create(:ci_build, :created, :dependent, pipeline: pipeline, name: 'job6', needed: job4) }
+
+ describe '#base_and_ancestors' do
+ it 'includes the base and its ancestors' do
+ relation = described_class.new(::Ci::Processable.where(id: job2.id)).base_and_ancestors
+
+ expect(relation).to eq([job2, job1])
+ end
+
+ it 'can find ancestors upto a certain level' do
+ relation = described_class.new(::Ci::Processable.where(id: job4.id)).base_and_ancestors(upto: job1.name)
+
+ expect(relation).to eq([job4, job2])
+ end
+
+ describe 'hierarchy_order option' do
+ let(:relation) do
+ described_class.new(::Ci::Processable.where(id: job4.id)).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([job4, job2, job1])
+ end
+ end
+
+ context 'for :desc' do
+ let(:hierarchy_order) { :desc }
+
+ it 'orders by ancestor to child' do
+ expect(relation).to eq([job1, job2, job4])
+ end
+ end
+ end
+ end
+
+ describe '#base_and_descendants' do
+ it 'includes the base and its descendants' do
+ relation = described_class.new(::Ci::Processable.where(id: job2.id)).base_and_descendants
+
+ expect(relation).to contain_exactly(job2, job4, job6)
+ end
+
+ context 'when with_depth is true' do
+ let(:relation) do
+ described_class.new(::Ci::Processable.where(id: job1.id)).base_and_descendants(with_depth: true)
+ end
+
+ it 'includes depth in the results' do
+ object_depths = {
+ job1.id => 1,
+ job2.id => 2,
+ job3.id => 2,
+ job4.id => 3,
+ job5.id => 3,
+ job6.id => 4
+ }
+
+ relation.each do |object|
+ expect(object.depth).to eq(object_depths[object.id])
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/project_config/repository_spec.rb b/spec/lib/gitlab/ci/project_config/repository_spec.rb
new file mode 100644
index 00000000000..2105b691d9e
--- /dev/null
+++ b/spec/lib/gitlab/ci/project_config/repository_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::ProjectConfig::Repository do
+ let(:project) { create(:project, :custom_repo, files: files) }
+ let(:sha) { project.repository.head_commit.sha }
+ let(:files) { { 'README.md' => 'hello' } }
+
+ subject(:config) { described_class.new(project, sha, nil, nil, nil) }
+
+ describe '#content' do
+ subject(:content) { config.content }
+
+ context 'when file is in repository' do
+ let(:config_content_result) do
+ <<~CICONFIG
+ ---
+ include:
+ - local: ".gitlab-ci.yml"
+ CICONFIG
+ end
+
+ let(:files) { { '.gitlab-ci.yml' => 'content' } }
+
+ it { is_expected.to eq(config_content_result) }
+ end
+
+ context 'when file is not in repository' do
+ it { is_expected.to be_nil }
+ end
+
+ context 'when Gitaly raises error' do
+ before do
+ allow(project.repository).to receive(:gitlab_ci_yml_for).and_raise(GRPC::Internal)
+ end
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ describe '#source' do
+ subject { config.source }
+
+ it { is_expected.to eq(:repository_source) }
+ end
+end
diff --git a/spec/lib/gitlab/ci/project_config/source_spec.rb b/spec/lib/gitlab/ci/project_config/source_spec.rb
new file mode 100644
index 00000000000..dda5c7cdce8
--- /dev/null
+++ b/spec/lib/gitlab/ci/project_config/source_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::ProjectConfig::Source do
+ let_it_be(:custom_config_class) { Class.new(described_class) }
+ let_it_be(:project) { build_stubbed(:project) }
+ let_it_be(:sha) { '123456' }
+
+ subject(:custom_config) { custom_config_class.new(project, sha, nil, nil, nil) }
+
+ describe '#content' do
+ subject(:content) { custom_config.content }
+
+ it { expect { content }.to raise_error(NotImplementedError) }
+ end
+
+ describe '#source' do
+ subject(:source) { custom_config.source }
+
+ it { expect { source }.to raise_error(NotImplementedError) }
+ end
+end
diff --git a/spec/lib/gitlab/ci/project_config_spec.rb b/spec/lib/gitlab/ci/project_config_spec.rb
new file mode 100644
index 00000000000..c4b179c9ef5
--- /dev/null
+++ b/spec/lib/gitlab/ci/project_config_spec.rb
@@ -0,0 +1,177 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::ProjectConfig do
+ let(:project) { create(:project, :empty_repo, ci_config_path: ci_config_path) }
+ let(:sha) { '123456' }
+ let(:content) { nil }
+ let(:source) { :push }
+ let(:bridge) { nil }
+
+ subject(:config) do
+ described_class.new(project: project, sha: sha,
+ custom_content: content, pipeline_source: source, pipeline_source_bridge: bridge)
+ end
+
+ context 'when bridge job is passed in as parameter' do
+ let(:ci_config_path) { nil }
+ let(:bridge) { create(:ci_bridge) }
+
+ before do
+ allow(bridge).to receive(:yaml_for_downstream).and_return('the-yaml')
+ end
+
+ it 'returns the content already available in command' do
+ expect(config.source).to eq(:bridge_source)
+ expect(config.content).to eq('the-yaml')
+ end
+ end
+
+ context 'when config is defined in a custom path in the repository' do
+ let(:ci_config_path) { 'path/to/config.yml' }
+ let(:config_content_result) do
+ <<~CICONFIG
+ ---
+ include:
+ - local: #{ci_config_path}
+ CICONFIG
+ end
+
+ before do
+ allow(project.repository)
+ .to receive(:gitlab_ci_yml_for)
+ .with(sha, ci_config_path)
+ .and_return('the-content')
+ end
+
+ it 'returns root config including the local custom file' do
+ expect(config.source).to eq(:repository_source)
+ expect(config.content).to eq(config_content_result)
+ end
+ end
+
+ context 'when config is defined remotely' do
+ let(:ci_config_path) { 'http://example.com/path/to/ci/config.yml' }
+ let(:config_content_result) do
+ <<~CICONFIG
+ ---
+ include:
+ - remote: #{ci_config_path}
+ CICONFIG
+ end
+
+ it 'returns root config including the remote config' do
+ expect(config.source).to eq(:remote_source)
+ expect(config.content).to eq(config_content_result)
+ end
+ end
+
+ context 'when config is defined in a separate repository' do
+ let(:ci_config_path) { 'path/to/.gitlab-ci.yml@another-group/another-repo' }
+ let(:config_content_result) do
+ <<~CICONFIG
+ ---
+ include:
+ - project: another-group/another-repo
+ file: path/to/.gitlab-ci.yml
+ CICONFIG
+ end
+
+ it 'returns root config including the path to another repository' do
+ expect(config.source).to eq(:external_project_source)
+ expect(config.content).to eq(config_content_result)
+ end
+
+ context 'when path specifies a refname' do
+ let(:ci_config_path) { 'path/to/.gitlab-ci.yml@another-group/another-repo:refname' }
+ let(:config_content_result) do
+ <<~CICONFIG
+ ---
+ include:
+ - project: another-group/another-repo
+ file: path/to/.gitlab-ci.yml
+ ref: refname
+ CICONFIG
+ end
+
+ it 'returns root config including the path and refname to another repository' do
+ expect(config.source).to eq(:external_project_source)
+ expect(config.content).to eq(config_content_result)
+ end
+ end
+ end
+
+ context 'when config is defined in the default .gitlab-ci.yml' do
+ let(:ci_config_path) { nil }
+ let(:config_content_result) do
+ <<~CICONFIG
+ ---
+ include:
+ - local: ".gitlab-ci.yml"
+ CICONFIG
+ end
+
+ before do
+ allow(project.repository)
+ .to receive(:gitlab_ci_yml_for)
+ .with(sha, '.gitlab-ci.yml')
+ .and_return('the-content')
+ end
+
+ it 'returns root config including the canonical CI config file' do
+ expect(config.source).to eq(:repository_source)
+ expect(config.content).to eq(config_content_result)
+ end
+ end
+
+ context 'when config is the Auto-Devops template' do
+ let(:ci_config_path) { nil }
+ let(:config_content_result) do
+ <<~CICONFIG
+ ---
+ include:
+ - template: Auto-DevOps.gitlab-ci.yml
+ CICONFIG
+ end
+
+ before do
+ allow(project).to receive(:auto_devops_enabled?).and_return(true)
+ end
+
+ it 'returns root config including the auto-devops template' do
+ expect(config.source).to eq(:auto_devops_source)
+ expect(config.content).to eq(config_content_result)
+ end
+ end
+
+ context 'when config is passed as a parameter' do
+ let(:source) { :ondemand_dast_scan }
+ let(:ci_config_path) { nil }
+ let(:content) do
+ <<~CICONFIG
+ ---
+ stages:
+ - dast
+ CICONFIG
+ end
+
+ it 'returns the parameter content' do
+ expect(config.source).to eq(:parameter_source)
+ expect(config.content).to eq(content)
+ end
+ end
+
+ context 'when config is not defined anywhere' do
+ let(:ci_config_path) { nil }
+
+ before do
+ allow(project).to receive(:auto_devops_enabled?).and_return(false)
+ end
+
+ it 'returns nil' do
+ expect(config.source).to be_nil
+ expect(config.content).to be_nil
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb b/spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb
index ade0e36cf1e..ad8f1dc11f8 100644
--- a/spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb
+++ b/spec/lib/gitlab/ci/reports/accessibility_reports_comparer_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::AccessibilityReportsComparer do
let(:comparer) { described_class.new(base_report, head_report) }
diff --git a/spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb b/spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb
index 8c35b2a34cf..af6844491ca 100644
--- a/spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb
+++ b/spec/lib/gitlab/ci/reports/accessibility_reports_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::AccessibilityReports do
let(:accessibility_report) { described_class.new }
diff --git a/spec/lib/gitlab/ci/reports/coverage_report_spec.rb b/spec/lib/gitlab/ci/reports/coverage_report_spec.rb
index 53646f7dfc0..23361a0c768 100644
--- a/spec/lib/gitlab/ci/reports/coverage_report_spec.rb
+++ b/spec/lib/gitlab/ci/reports/coverage_report_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::CoverageReport do
let(:coverage_report) { described_class.new }
diff --git a/spec/lib/gitlab/ci/reports/sbom/component_spec.rb b/spec/lib/gitlab/ci/reports/sbom/component_spec.rb
index 672117c311f..06ea3433ef0 100644
--- a/spec/lib/gitlab/ci/reports/sbom/component_spec.rb
+++ b/spec/lib/gitlab/ci/reports/sbom/component_spec.rb
@@ -1,23 +1,23 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::Sbom::Component do
let(:attributes) do
{
- 'type' => 'library',
- 'name' => 'component-name',
- 'version' => 'v0.0.1'
+ type: 'library',
+ name: 'component-name',
+ version: 'v0.0.1'
}
end
- subject { described_class.new(attributes) }
+ subject { described_class.new(**attributes) }
it 'has correct attributes' do
expect(subject).to have_attributes(
- component_type: 'library',
- name: 'component-name',
- version: 'v0.0.1'
+ component_type: attributes[:type],
+ name: attributes[:name],
+ version: attributes[:version]
)
end
end
diff --git a/spec/lib/gitlab/ci/reports/sbom/report_spec.rb b/spec/lib/gitlab/ci/reports/sbom/report_spec.rb
index d7a285ab13c..6ffa93e5fc8 100644
--- a/spec/lib/gitlab/ci/reports/sbom/report_spec.rb
+++ b/spec/lib/gitlab/ci/reports/sbom/report_spec.rb
@@ -15,40 +15,22 @@ RSpec.describe Gitlab::Ci::Reports::Sbom::Report do
end
describe '#set_source' do
- let_it_be(:source) do
- {
- 'type' => :dependency_scanning,
- 'data' => {
- 'input_file' => { 'path' => 'package-lock.json' },
- 'source_file' => { 'path' => 'package.json' },
- 'package_manager' => { 'name' => 'npm' },
- 'language' => { 'name' => 'JavaScript' }
- },
- 'fingerprint' => 'c01df1dc736c1148717e053edbde56cb3a55d3e31f87cea955945b6f67c17d42'
- }
- end
+ let_it_be(:source) { create(:ci_reports_sbom_source) }
it 'stores the source' do
report.set_source(source)
- expect(report.source).to be_a(Gitlab::Ci::Reports::Sbom::Source)
+ expect(report.source).to eq(source)
end
end
describe '#add_component' do
- let_it_be(:components) do
- [
- { 'type' => 'library', 'name' => 'component1', 'version' => 'v0.0.1' },
- { 'type' => 'library', 'name' => 'component2', 'version' => 'v0.0.2' },
- { 'type' => 'library', 'name' => 'component2' }
- ]
- end
+ let_it_be(:components) { create_list(:ci_reports_sbom_component, 3) }
it 'appends components to a list' do
components.each { |component| report.add_component(component) }
- expect(report.components.size).to eq(3)
- expect(report.components).to all(be_a(Gitlab::Ci::Reports::Sbom::Component))
+ expect(report.components).to match_array(components)
end
end
end
diff --git a/spec/lib/gitlab/ci/reports/sbom/reports_spec.rb b/spec/lib/gitlab/ci/reports/sbom/reports_spec.rb
index 97d8d7abb33..75ea91251eb 100644
--- a/spec/lib/gitlab/ci/reports/sbom/reports_spec.rb
+++ b/spec/lib/gitlab/ci/reports/sbom/reports_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::Sbom::Reports do
subject(:reports_list) { described_class.new }
diff --git a/spec/lib/gitlab/ci/reports/sbom/source_spec.rb b/spec/lib/gitlab/ci/reports/sbom/source_spec.rb
index 2d6434534a0..cb30bd721dd 100644
--- a/spec/lib/gitlab/ci/reports/sbom/source_spec.rb
+++ b/spec/lib/gitlab/ci/reports/sbom/source_spec.rb
@@ -1,29 +1,29 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::Sbom::Source do
let(:attributes) do
{
- 'type' => :dependency_scanning,
- 'data' => {
+ type: :dependency_scanning,
+ data: {
'category' => 'development',
'input_file' => { 'path' => 'package-lock.json' },
'source_file' => { 'path' => 'package.json' },
'package_manager' => { 'name' => 'npm' },
'language' => { 'name' => 'JavaScript' }
},
- 'fingerprint' => '4dbcb747e6f0fb3ed4f48d96b777f1d64acdf43e459fdfefad404e55c004a188'
+ fingerprint: '4dbcb747e6f0fb3ed4f48d96b777f1d64acdf43e459fdfefad404e55c004a188'
}
end
- subject { described_class.new(attributes) }
+ subject { described_class.new(**attributes) }
it 'has correct attributes' do
expect(subject).to have_attributes(
- source_type: attributes['type'],
- data: attributes['data'],
- fingerprint: attributes['fingerprint']
+ source_type: attributes[:type],
+ data: attributes[:data],
+ fingerprint: attributes[:fingerprint]
)
end
end
diff --git a/spec/lib/gitlab/ci/reports/security/flag_spec.rb b/spec/lib/gitlab/ci/reports/security/flag_spec.rb
index d677425a8da..6ee074f7aeb 100644
--- a/spec/lib/gitlab/ci/reports/security/flag_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/flag_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::Security::Flag do
subject(:security_flag) { described_class.new(type: 'flagged-as-likely-false-positive', origin: 'post analyzer X', description: 'static string to sink') }
diff --git a/spec/lib/gitlab/ci/reports/security/link_spec.rb b/spec/lib/gitlab/ci/reports/security/link_spec.rb
index 7b55af27f4d..0e1cdc93f6c 100644
--- a/spec/lib/gitlab/ci/reports/security/link_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/link_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::Security::Link do
subject(:security_link) { described_class.new(name: 'CVE-2020-0202', url: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-0202') }
diff --git a/spec/lib/gitlab/ci/reports/security/scan_spec.rb b/spec/lib/gitlab/ci/reports/security/scan_spec.rb
index b4968ff3a6e..23427e8608c 100644
--- a/spec/lib/gitlab/ci/reports/security/scan_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/scan_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::Security::Scan do
describe '#initialize' do
diff --git a/spec/lib/gitlab/ci/reports/security/scanned_resource_spec.rb b/spec/lib/gitlab/ci/reports/security/scanned_resource_spec.rb
index e9daa05e8b9..74a5344f79e 100644
--- a/spec/lib/gitlab/ci/reports/security/scanned_resource_spec.rb
+++ b/spec/lib/gitlab/ci/reports/security/scanned_resource_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::Security::ScannedResource do
let(:url) { 'http://example.com:3001/1?foo=bar' }
diff --git a/spec/lib/gitlab/ci/reports/terraform_reports_spec.rb b/spec/lib/gitlab/ci/reports/terraform_reports_spec.rb
index 5e94fe2bb3d..f754786d071 100644
--- a/spec/lib/gitlab/ci/reports/terraform_reports_spec.rb
+++ b/spec/lib/gitlab/ci/reports/terraform_reports_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Reports::TerraformReports do
it 'initializes plans with and empty hash' do
diff --git a/spec/lib/gitlab/ci/status/extended_spec.rb b/spec/lib/gitlab/ci/status/extended_spec.rb
index 3e1004754ba..e81c7b0f6be 100644
--- a/spec/lib/gitlab/ci/status/extended_spec.rb
+++ b/spec/lib/gitlab/ci/status/extended_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Status::Extended do
it 'requires subclass to implement matcher' do
diff --git a/spec/lib/gitlab/ci/status/group/factory_spec.rb b/spec/lib/gitlab/ci/status/group/factory_spec.rb
index c67c7ff8271..38aa1ba4ebb 100644
--- a/spec/lib/gitlab/ci/status/group/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/group/factory_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Ci::Status::Group::Factory do
it 'inherits from the core factory' do
diff --git a/spec/lib/gitlab/ci/templates/katalon_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/katalon_gitlab_ci_yaml_spec.rb
new file mode 100644
index 00000000000..5a62324da74
--- /dev/null
+++ b/spec/lib/gitlab/ci/templates/katalon_gitlab_ci_yaml_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Katalon.gitlab-ci.yml' do
+ subject(:template) do
+ <<~YAML
+ include:
+ - template: 'Katalon.gitlab-ci.yml'
+
+ katalon_tests_placeholder:
+ extends: .katalon_tests
+ stage: test
+ script:
+ - echo "katalon tests"
+
+ katalon_tests_with_artifacts_placeholder:
+ extends: .katalon_tests_with_artifacts
+ stage: test
+ script:
+ - echo "katalon tests with artifacts"
+ YAML
+ end
+
+ describe 'the created pipeline' do
+ let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) }
+ let(:user) { project.first_owner }
+
+ let(:service) { Ci::CreatePipelineService.new(project, user, ref: 'master' ) }
+ let(:pipeline) { service.execute!(:push).payload }
+ let(:build_names) { pipeline.builds.pluck(:name) }
+
+ before do
+ stub_ci_pipeline_yaml_file(template)
+ end
+
+ it 'create katalon tests jobs' do
+ expect(build_names).to match_array(%w[katalon_tests_placeholder katalon_tests_with_artifacts_placeholder])
+
+ expect(pipeline.builds.find_by(name: 'katalon_tests_placeholder').options).to include(
+ image: { name: 'katalonstudio/katalon' },
+ services: [{ name: 'docker:dind' }]
+ )
+
+ expect(pipeline.builds.find_by(name: 'katalon_tests_with_artifacts_placeholder').options).to include(
+ image: { name: 'katalonstudio/katalon' },
+ services: [{ name: 'docker:dind' }],
+ artifacts: { when: 'always', paths: ['Reports/'], reports: { junit: ['Reports/*/*/*/*.xml'] } }
+ )
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/trace/archive_spec.rb b/spec/lib/gitlab/ci/trace/archive_spec.rb
index 3ae0e5d1f0e..f91cb03883a 100644
--- a/spec/lib/gitlab/ci/trace/archive_spec.rb
+++ b/spec/lib/gitlab/ci/trace/archive_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Trace::Archive do
context 'with transactional fixtures' do
- let_it_be(:job) { create(:ci_build, :success, :trace_live) }
+ let_it_be_with_reload(:job) { create(:ci_build, :success, :trace_live) }
let_it_be_with_reload(:trace_metadata) { create(:ci_build_trace_metadata, build: job) }
let_it_be(:src_checksum) do
job.trace.read { |stream| Digest::MD5.hexdigest(stream.raw) }
diff --git a/spec/lib/gitlab/ci/trace_spec.rb b/spec/lib/gitlab/ci/trace_spec.rb
index 888ceb7ff9a..3043c8c5467 100644
--- a/spec/lib/gitlab/ci/trace_spec.rb
+++ b/spec/lib/gitlab/ci/trace_spec.rb
@@ -10,7 +10,6 @@ RSpec.describe Gitlab::Ci::Trace, :clean_gitlab_redis_shared_state, factory_defa
describe "associations" do
it { expect(trace).to respond_to(:job) }
- it { expect(trace).to delegate_method(:old_trace).to(:job) }
end
context 'when trace is migrated to object storage' do
diff --git a/spec/lib/gitlab/ci/variables/builder_spec.rb b/spec/lib/gitlab/ci/variables/builder_spec.rb
index 6ab2089cce8..4833ccf9093 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 do
+RSpec.describe Gitlab::Ci::Variables::Builder, :clean_gitlab_redis_cache do
include Ci::TemplateHelpers
let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, :repository, namespace: group) }
@@ -26,13 +26,13 @@ RSpec.describe Gitlab::Ci::Variables::Builder do
{ key: 'CI_JOB_NAME',
value: job.name },
{ key: 'CI_JOB_STAGE',
- value: job.stage },
+ value: job.stage_name },
{ key: 'CI_NODE_TOTAL',
value: '1' },
{ key: 'CI_BUILD_NAME',
value: job.name },
{ key: 'CI_BUILD_STAGE',
- value: job.stage },
+ value: job.stage_name },
{ key: 'CI',
value: 'true' },
{ key: 'GITLAB_CI',
@@ -138,11 +138,11 @@ RSpec.describe Gitlab::Ci::Variables::Builder do
{ key: 'GITLAB_USER_ID',
value: user.id.to_s },
{ key: 'GITLAB_USER_EMAIL',
- value: user.email },
+ value: user.email },
{ key: 'GITLAB_USER_LOGIN',
- value: user.username },
+ value: user.username },
{ key: 'GITLAB_USER_NAME',
- value: user.name }
+ value: user.name }
].map { |var| var.merge(public: true, masked: false) }
end
diff --git a/spec/lib/gitlab/ci/variables/collection/sort_spec.rb b/spec/lib/gitlab/ci/variables/collection/sort_spec.rb
index 7e4e9602a92..57171e5be69 100644
--- a/spec/lib/gitlab/ci/variables/collection/sort_spec.rb
+++ b/spec/lib/gitlab/ci/variables/collection/sort_spec.rb
@@ -2,6 +2,7 @@
require 'fast_spec_helper'
require 'rspec-parameterized'
+require 'tsort'
RSpec.describe Gitlab::Ci::Variables::Collection::Sort do
describe '#initialize with non-Collection value' do
diff --git a/spec/lib/gitlab/ci/variables/helpers_spec.rb b/spec/lib/gitlab/ci/variables/helpers_spec.rb
index fc1055751bd..fb1e66bd605 100644
--- a/spec/lib/gitlab/ci/variables/helpers_spec.rb
+++ b/spec/lib/gitlab/ci/variables/helpers_spec.rb
@@ -15,21 +15,21 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do
end
let(:result) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value22', public: true },
- { key: 'key3', value: 'value3', public: true }]
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
end
subject { described_class.merge_variables(current_variables, new_variables) }
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
context 'when new variables is a hash' do
let(:new_variables) do
{ 'key2' => 'value22', 'key3' => 'value3' }
end
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
end
context 'when new variables is a hash with symbol keys' do
@@ -37,79 +37,68 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do
{ key2: 'value22', key3: 'value3' }
end
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
end
context 'when new variables is nil' do
let(:new_variables) {}
let(:result) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value2', public: true }]
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value2' }]
end
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
end
end
- describe '.transform_to_yaml_variables' do
- let(:variables) do
- { 'key1' => 'value1', 'key2' => 'value2' }
- end
-
- let(:result) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value2', public: true }]
- end
-
- subject { described_class.transform_to_yaml_variables(variables) }
-
- it { is_expected.to eq(result) }
+ describe '.transform_to_array' do
+ subject { described_class.transform_to_array(variables) }
- context 'when variables is nil' do
- let(:variables) {}
-
- it { is_expected.to eq([]) }
- end
- end
+ context 'when values are strings' do
+ let(:variables) do
+ { 'key1' => 'value1', 'key2' => 'value2' }
+ end
- describe '.transform_from_yaml_variables' do
- let(:variables) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value2', public: true }]
- end
+ let(:result) do
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value2' }]
+ end
- let(:result) do
- { 'key1' => 'value1', 'key2' => 'value2' }
+ it { is_expected.to match_array(result) }
end
- subject { described_class.transform_from_yaml_variables(variables) }
-
- it { is_expected.to eq(result) }
-
context 'when variables is nil' do
let(:variables) {}
- it { is_expected.to eq({}) }
+ it { is_expected.to match_array([]) }
end
- context 'when variables is a hash' do
+ context 'when values are hashes' do
let(:variables) do
- { key1: 'value1', 'key2' => 'value2' }
+ { 'key1' => { value: 'value1', description: 'var 1' }, 'key2' => { value: 'value2' } }
end
- it { is_expected.to eq(result) }
- end
-
- context 'when variables contain integers and symbols' do
- let(:variables) do
- { key1: 1, key2: :value2 }
+ let(:result) do
+ [{ key: 'key1', value: 'value1', description: 'var 1' },
+ { key: 'key2', value: 'value2' }]
end
- let(:result1) do
- { 'key1' => '1', 'key2' => 'value2' }
- end
+ it { is_expected.to match_array(result) }
+
+ context 'when a value data has `key` as a key' do
+ let(:variables) do
+ { 'key1' => { value: 'value1', key: 'new_key1' }, 'key2' => { value: 'value2' } }
+ end
+
+ let(:result) do
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value2' }]
+ end
- it { is_expected.to eq(result1) }
+ it 'ignores the key set with "key"' do
+ is_expected.to match_array(result)
+ end
+ end
end
end
@@ -127,35 +116,35 @@ RSpec.describe Gitlab::Ci::Variables::Helpers do
let(:inheritance) { true }
let(:result) do
- [{ key: 'key1', value: 'value1', public: true },
- { key: 'key2', value: 'value22', public: true },
- { key: 'key3', value: 'value3', public: true }]
+ [{ key: 'key1', value: 'value1' },
+ { key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
end
subject { described_class.inherit_yaml_variables(from: from, to: to, inheritance: inheritance) }
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
context 'when inheritance is false' do
let(:inheritance) { false }
let(:result) do
- [{ key: 'key2', value: 'value22', public: true },
- { key: 'key3', value: 'value3', public: true }]
+ [{ key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
end
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
end
context 'when inheritance is array' do
let(:inheritance) { ['key2'] }
let(:result) do
- [{ key: 'key2', value: 'value22', public: true },
- { key: 'key3', value: 'value3', public: true }]
+ [{ key: 'key2', value: 'value22' },
+ { key: 'key3', value: 'value3' }]
end
- it { is_expected.to eq(result) }
+ it { is_expected.to match_array(result) }
end
end
end
diff --git a/spec/lib/gitlab/ci/yaml_processor/dag_spec.rb b/spec/lib/gitlab/ci/yaml_processor/dag_spec.rb
index f815f56543c..082febacbd7 100644
--- a/spec/lib/gitlab/ci/yaml_processor/dag_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor/dag_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require 'tsort'
RSpec.describe Gitlab::Ci::YamlProcessor::Dag do
let(:nodes) { {} }
diff --git a/spec/lib/gitlab/ci/yaml_processor/feature_flags_spec.rb b/spec/lib/gitlab/ci/yaml_processor/feature_flags_spec.rb
index 0bd9563d191..77346f328ca 100644
--- a/spec/lib/gitlab/ci/yaml_processor/feature_flags_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor/feature_flags_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'spec_helper'
RSpec.describe Gitlab::Ci::YamlProcessor::FeatureFlags do
let(:feature_flag) { :my_feature_flag }
@@ -48,20 +48,32 @@ RSpec.describe Gitlab::Ci::YamlProcessor::FeatureFlags do
end
context 'when feature flag is checked outside the "with_actor" block' do
- it 'raises an error on dev/test environment' do
- expect { described_class.enabled?(feature_flag) }.to raise_error(described_class::NoActorError)
- end
+ context 'when yaml_processor_feature_flag_corectness is used', :yaml_processor_feature_flag_corectness do
+ it 'raises an error on dev/test environment' do
+ expect { described_class.enabled?(feature_flag) }.to raise_error(described_class::NoActorError)
+ end
+
+ context 'when on production' do
+ before do
+ allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(false)
+ end
- context 'when on production' do
- before do
- allow(Gitlab::ErrorTracking).to receive(:should_raise_for_dev?).and_return(false)
+ it 'checks the feature flag without actor' do
+ expect(Feature).to receive(:enabled?).with(feature_flag, nil)
+ expect(Gitlab::ErrorTracking)
+ .to receive(:track_and_raise_for_dev_exception)
+ .and_call_original
+
+ described_class.enabled?(feature_flag)
+ end
end
+ end
+ context 'when yaml_processor_feature_flag_corectness is not used' do
it 'checks the feature flag without actor' do
expect(Feature).to receive(:enabled?).with(feature_flag, nil)
expect(Gitlab::ErrorTracking)
- .to receive(:track_and_raise_for_dev_exception)
- .and_call_original
+ .to receive(:track_exception)
described_class.enabled?(feature_flag)
end
diff --git a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
index 8416501e949..f7a0905d9da 100644
--- a/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor/result_spec.rb
@@ -72,8 +72,8 @@ module Gitlab
it 'returns calculated variables with root and job variables' do
is_expected.to match_array([
- { key: 'VAR1', value: 'value 11', public: true },
- { key: 'VAR2', value: 'value 2', public: true }
+ { key: 'VAR1', value: 'value 11' },
+ { key: 'VAR2', value: 'value 2' }
])
end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 35af9ae6201..cc327f5b5f1 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -298,8 +298,8 @@ module Gitlab
context 'when delayed is defined' do
let(:config) do
YAML.dump(rspec: {
- script: 'rollout 10%',
- when: 'delayed',
+ script: 'rollout 10%',
+ when: 'delayed',
start_in: '1 day'
})
end
@@ -315,7 +315,7 @@ module Gitlab
context 'when resource group is defined' do
let(:config) do
YAML.dump(rspec: {
- script: 'test',
+ script: 'test',
resource_group: 'iOS'
})
end
@@ -448,7 +448,7 @@ module Gitlab
it 'parses the root:variables as #root_variables' do
expect(subject.root_variables)
- .to contain_exactly({ key: 'SUPPORTED', value: 'parsed', public: true })
+ .to contain_exactly({ key: 'SUPPORTED', value: 'parsed' })
end
end
@@ -490,7 +490,7 @@ module Gitlab
it 'parses the root:variables as #root_variables' do
expect(subject.root_variables)
- .to contain_exactly({ key: 'SUPPORTED', value: 'parsed', public: true })
+ .to contain_exactly({ key: 'SUPPORTED', value: 'parsed' })
end
end
@@ -997,18 +997,6 @@ module Gitlab
scheduling_type: :stage
})
end
-
- context 'when the feature flag ci_docker_image_pull_policy is disabled' do
- before do
- stub_feature_flags(ci_docker_image_pull_policy: false)
- end
-
- it { is_expected.not_to be_valid }
-
- it "returns no job" do
- expect(processor.jobs).to eq({})
- end
- end
end
context 'when a service has pull_policy' do
@@ -1042,39 +1030,29 @@ module Gitlab
scheduling_type: :stage
})
end
-
- context 'when the feature flag ci_docker_image_pull_policy is disabled' do
- before do
- stub_feature_flags(ci_docker_image_pull_policy: false)
- end
-
- it { is_expected.not_to be_valid }
-
- it "returns no job" do
- expect(processor.jobs).to eq({})
- end
- end
end
end
- describe 'Variables' do
- subject { Gitlab::Ci::YamlProcessor.new(YAML.dump(config)).execute }
+ # Change this to a `describe` block when removing the FF ci_variables_refactoring_to_variable
+ shared_examples 'Variables' do
+ subject(:execute) { described_class.new(config).execute }
- let(:build) { subject.builds.first }
+ let(:build) { execute.builds.first }
let(:job_variables) { build[:job_variables] }
let(:root_variables_inheritance) { build[:root_variables_inheritance] }
context 'when global variables are defined' do
- let(:variables) do
- { 'VAR1' => 'value1', 'VAR2' => 'value2' }
- end
-
let(:config) do
- {
- variables: variables,
- before_script: ['pwd'],
- rspec: { script: 'rspec' }
- }
+ <<~YAML
+ variables:
+ VAR1: value1
+ VAR2: value2
+
+ before_script: [pwd]
+
+ rspec:
+ script: rspec
+ YAML
end
it 'returns global variables' do
@@ -1084,22 +1062,23 @@ module Gitlab
end
context 'when job variables are defined' do
- let(:config) do
- {
- before_script: ['pwd'],
- rspec: { script: 'rspec', variables: variables }
- }
- end
-
context 'when syntax is correct' do
- let(:variables) do
- { 'VAR1' => 'value1', 'VAR2' => 'value2' }
+ let(:config) do
+ <<~YAML
+ before_script: [pwd]
+
+ rspec:
+ script: rspec
+ variables:
+ VAR1: value1
+ VAR2: value2
+ YAML
end
it 'returns job variables' do
expect(job_variables).to contain_exactly(
- { key: 'VAR1', value: 'value1', public: true },
- { key: 'VAR2', value: 'value2', public: true }
+ { key: 'VAR1', value: 'value1' },
+ { key: 'VAR2', value: 'value2' }
)
expect(root_variables_inheritance).to eq(true)
end
@@ -1107,16 +1086,28 @@ module Gitlab
context 'when syntax is incorrect' do
context 'when variables defined but invalid' do
- let(:variables) do
- %w(VAR1 value1 VAR2 value2)
+ let(:config) do
+ <<~YAML
+ before_script: [pwd]
+
+ rspec:
+ script: rspec
+ variables: [VAR1 value1 VAR2 value2]
+ YAML
end
- it_behaves_like 'returns errors', /jobs:rspec:variables config should be a hash of key value pairs/
+ it_behaves_like 'returns errors', /jobs:rspec:variables config should be a hash/
end
context 'when variables key defined but value not specified' do
- let(:variables) do
- nil
+ let(:config) do
+ <<~YAML
+ before_script: [pwd]
+
+ rspec:
+ script: rspec
+ variables: null
+ YAML
end
it 'returns empty array' do
@@ -1133,10 +1124,12 @@ module Gitlab
context 'when job variables are not defined' do
let(:config) do
- {
- before_script: ['pwd'],
- rspec: { script: 'rspec' }
- }
+ <<~YAML
+ before_script: ['pwd']
+
+ rspec:
+ script: rspec
+ YAML
end
it 'returns empty array' do
@@ -1144,6 +1137,42 @@ module Gitlab
expect(root_variables_inheritance).to eq(true)
end
end
+
+ context 'when variables have different type of values' do
+ let(:config) do
+ <<~YAML
+ before_script: [pwd]
+
+ rspec:
+ variables:
+ VAR1: value1
+ VAR2: :value2
+ VAR3: 123
+ script: rspec
+ YAML
+ end
+
+ it 'returns job variables' do
+ expect(job_variables).to contain_exactly(
+ { key: 'VAR1', value: 'value1' },
+ { key: 'VAR2', value: 'value2' },
+ { key: 'VAR3', value: '123' }
+ )
+ expect(root_variables_inheritance).to eq(true)
+ end
+ end
+ end
+
+ context 'when ci_variables_refactoring_to_variable is enabled' do
+ it_behaves_like 'Variables'
+ end
+
+ context 'when ci_variables_refactoring_to_variable is disabled' do
+ before do
+ stub_feature_flags(ci_variables_refactoring_to_variable: false)
+ end
+
+ it_behaves_like 'Variables'
end
context 'when using `extends`' do
@@ -1203,21 +1232,21 @@ module Gitlab
expect(config_processor.builds[0]).to include(
name: 'test1',
options: { script: ['test'] },
- job_variables: [{ key: 'VAR1', value: 'test1 var 1', public: true },
- { key: 'VAR2', value: 'test2 var 2', public: true }]
+ job_variables: [{ key: 'VAR1', value: 'test1 var 1' },
+ { key: 'VAR2', value: 'test2 var 2' }]
)
expect(config_processor.builds[1]).to include(
name: 'test2',
options: { script: ['test'] },
- job_variables: [{ key: 'VAR1', value: 'base var 1', public: true },
- { key: 'VAR2', value: 'test2 var 2', public: true }]
+ job_variables: [{ key: 'VAR1', value: 'base var 1' },
+ { key: 'VAR2', value: 'test2 var 2' }]
)
expect(config_processor.builds[2]).to include(
name: 'test3',
options: { script: ['test'] },
- job_variables: [{ key: 'VAR1', value: 'base var 1', public: true }]
+ job_variables: [{ key: 'VAR1', value: 'base var 1' }]
)
expect(config_processor.builds[3]).to include(
@@ -1647,10 +1676,10 @@ module Gitlab
describe "Artifacts" do
it "returns artifacts when defined" do
config = YAML.dump({
- image: "image:1.0",
- services: ["mysql"],
+ image: "image:1.0",
+ services: ["mysql"],
before_script: ["pwd"],
- rspec: {
+ rspec: {
artifacts: {
paths: ["logs/", "binaries/"],
expose_as: "Exposed artifacts",
@@ -1906,7 +1935,7 @@ module Gitlab
let(:config) do
{
deploy_to_production: {
- stage: 'deploy',
+ stage: 'deploy',
script: 'test'
}
}
@@ -2275,15 +2304,15 @@ module Gitlab
let(:config) do
{
- var_default: { stage: 'build', script: 'test', rules: [{ if: '$VAR == null' }] },
- var_when: { stage: 'build', script: 'test', rules: [{ if: '$VAR == null', when: 'always' }] },
+ var_default: { stage: 'build', script: 'test', rules: [{ if: '$VAR == null' }] },
+ var_when: { stage: 'build', script: 'test', rules: [{ if: '$VAR == null', when: 'always' }] },
var_and_changes: { stage: 'build', script: 'test', rules: [{ if: '$VAR == null', changes: %w[README], when: 'always' }] },
changes_not_var: { stage: 'test', script: 'test', rules: [{ if: '$VAR != null', changes: %w[README] }] },
var_not_changes: { stage: 'test', script: 'test', rules: [{ if: '$VAR == null', changes: %w[other/file.rb], when: 'always' }] },
- nothing: { stage: 'test', script: 'test', rules: [{ when: 'manual' }] },
- var_never: { stage: 'deploy', script: 'test', rules: [{ if: '$VAR == null', when: 'never' }] },
- var_delayed: { stage: 'deploy', script: 'test', rules: [{ if: '$VAR == null', when: 'delayed', start_in: '3 hours' }] },
- two_rules: { stage: 'deploy', script: 'test', rules: [{ if: '$VAR == null', when: 'on_success' }, { changes: %w[README], when: 'manual' }] }
+ nothing: { stage: 'test', script: 'test', rules: [{ when: 'manual' }] },
+ var_never: { stage: 'deploy', script: 'test', rules: [{ if: '$VAR == null', when: 'never' }] },
+ var_delayed: { stage: 'deploy', script: 'test', rules: [{ if: '$VAR == null', when: 'delayed', start_in: '3 hours' }] },
+ two_rules: { stage: 'deploy', script: 'test', rules: [{ if: '$VAR == null', when: 'on_success' }, { changes: %w[README], when: 'manual' }] }
}
end
@@ -2729,13 +2758,13 @@ module Gitlab
context 'returns errors if variables is not a map' do
let(:config) { YAML.dump({ variables: "test", rspec: { script: "test" } }) }
- it_behaves_like 'returns errors', 'variables config should be a hash of key value pairs, value can be a hash'
+ it_behaves_like 'returns errors', 'variables config should be a hash'
end
context 'returns errors if variables is not a map of key-value strings' do
let(:config) { YAML.dump({ variables: { test: false }, rspec: { script: "test" } }) }
- it_behaves_like 'returns errors', 'variables config should be a hash of key value pairs, value can be a hash'
+ it_behaves_like 'returns errors', 'variable definition must be either a string or a hash'
end
context 'returns errors if job when is not on_success, on_failure or always' do
diff --git a/spec/lib/gitlab/ci_access_spec.rb b/spec/lib/gitlab/ci_access_spec.rb
index 9b573c6eb7a..e41b666abda 100644
--- a/spec/lib/gitlab/ci_access_spec.rb
+++ b/spec/lib/gitlab/ci_access_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::CiAccess do
let(:access) { described_class.new }
diff --git a/spec/lib/gitlab/cleanup/personal_access_tokens_spec.rb b/spec/lib/gitlab/cleanup/personal_access_tokens_spec.rb
new file mode 100644
index 00000000000..36c5d0e9b0c
--- /dev/null
+++ b/spec/lib/gitlab/cleanup/personal_access_tokens_spec.rb
@@ -0,0 +1,168 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Cleanup::PersonalAccessTokens do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:subgroup) { create(:group, parent: group) }
+ let_it_be(:project_bot) { create(:user, :project_bot) }
+
+ let(:group_full_path) { group.full_path }
+ let(:logger) { instance_double(Gitlab::AppJsonLogger, info: nil, warn: nil) }
+ let(:last_used_at) { 1.month.ago.beginning_of_hour }
+ let!(:unused_token) { create(:personal_access_token) }
+
+ let!(:old_unused_token) do
+ create(:personal_access_token, created_at: last_used_at - 1.minute)
+ end
+
+ let!(:old_actively_used_token) do
+ create(:personal_access_token, created_at: last_used_at - 1.minute, last_used_at: 1.day.ago)
+ end
+
+ let!(:old_unused_token_for_non_group_member) do
+ create(:personal_access_token, created_at: last_used_at - 1.minute)
+ end
+
+ let!(:old_unused_token_for_subgroup_member) do
+ create(:personal_access_token, created_at: last_used_at - 1.minute)
+ end
+
+ let!(:old_unused_project_access_token) do
+ create(:personal_access_token, user: project_bot, created_at: last_used_at - 1.minute)
+ end
+
+ let!(:old_formerly_used_token) do
+ create(:personal_access_token,
+ created_at: last_used_at - 1.minute,
+ last_used_at: last_used_at - 1.minute
+ )
+ end
+
+ before do
+ group.add_member(old_formerly_used_token.user, Gitlab::Access::DEVELOPER)
+ group.add_member(old_actively_used_token.user, Gitlab::Access::DEVELOPER)
+ group.add_member(unused_token.user, Gitlab::Access::DEVELOPER)
+ group.add_member(old_unused_token.user, Gitlab::Access::DEVELOPER)
+ group.add_member(project_bot, Gitlab::Access::MAINTAINER)
+
+ subgroup.add_member(old_unused_token_for_subgroup_member.user, Gitlab::Access::DEVELOPER)
+ end
+
+ subject do
+ described_class.new(
+ logger: logger,
+ cut_off_date: last_used_at,
+ group_full_path: group_full_path
+ )
+ end
+
+ context 'when initialized with an invalid logger' do
+ let(:logger) { "not a logger" }
+
+ it 'raises error' do
+ expect do
+ subject.run!
+ end.to raise_error('Invalid logger: not a logger')
+ end
+ end
+
+ describe '#run!' do
+ context 'when invalid group path passed' do
+ let(:group_full_path) { 'notagroup' }
+
+ it 'raises error' do
+ expect do
+ subject.run!
+ end.to raise_error("Group with full_path notagroup not found")
+ end
+ end
+
+ context 'in a real run' do
+ let(:args) { { dry_run: false } }
+
+ context 'when revoking unused tokens' do
+ it 'revokes human-owned tokens created and last used over 1 year ago' do
+ subject.run!(**args)
+
+ expect(PersonalAccessToken.active).to contain_exactly(
+ unused_token,
+ old_actively_used_token,
+ old_unused_project_access_token,
+ old_unused_token_for_non_group_member,
+ old_unused_token_for_subgroup_member
+ )
+ expect(PersonalAccessToken.revoked).to contain_exactly(
+ old_unused_token,
+ old_formerly_used_token
+ )
+ end
+ end
+
+ context 'when revoking used and unused tokens' do
+ let(:args) { { dry_run: false, revoke_active_tokens: true } }
+
+ it 'revokes human-owned tokens created over 1 year ago' do
+ subject.run!(**args)
+
+ expect(PersonalAccessToken.active).to contain_exactly(
+ unused_token,
+ old_unused_project_access_token,
+ old_unused_token_for_non_group_member,
+ old_unused_token_for_subgroup_member
+ )
+ expect(PersonalAccessToken.revoked).to contain_exactly(
+ old_unused_token,
+ old_actively_used_token,
+ old_formerly_used_token
+ )
+ end
+ end
+
+ it 'updates updated_at' do
+ expect do
+ subject.run!(**args)
+ end.to change {
+ old_unused_token.reload.updated_at
+ }
+ end
+
+ it 'logs action as done' do
+ message = {
+ dry_run: false,
+ token_count: 2,
+ updated_count: 2,
+ tokens: instance_of(Array),
+ group_full_path: group_full_path
+ }
+ expect(logger).to receive(:info).with(include(message))
+ subject.run!(**args)
+ end
+ end
+
+ context 'in a dry run' do
+ # Dry run is the default
+ let(:args) { {} }
+
+ it 'does not revoke any tokens' do
+ expect do
+ subject.run!(**args)
+ end.to not_change {
+ PersonalAccessToken.active.count
+ }
+ end
+
+ it 'logs what could be revoked' do
+ message = {
+ dry_run: true,
+ token_count: 2,
+ updated_count: 0,
+ tokens: instance_of(Array),
+ group_full_path: group_full_path
+ }
+ expect(logger).to receive(:info).with(include(message))
+ subject.run!(**args)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index 279486aa2a1..1422f83c629 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::ClosingIssueExtractor do
let_it_be_with_reload(:project) { create(:project) }
- let_it_be(:project2) { create(:project) }
+ let_it_be_with_reload(:project2) { create(:project) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:issue2) { create(:issue, project: project2) }
@@ -335,6 +335,17 @@ RSpec.describe Gitlab::ClosingIssueExtractor do
end
end
+ context 'when target project has autoclose issues disabled' do
+ before do
+ project2.update!(autoclose_referenced_issues: false)
+ end
+
+ it 'omits the issue reference' do
+ message = "Closes #{cross_reference}"
+ expect(subject.closed_by_message(message)).to be_empty
+ end
+ end
+
context "with an invalid URL" do
it do
message = "Closes https://google.com#{urls.project_issue_path(issue2.project, issue2)}"
@@ -443,14 +454,19 @@ RSpec.describe Gitlab::ClosingIssueExtractor do
end
context "with autoclose referenced issues disabled" do
- before do
+ before_all do
project.update!(autoclose_referenced_issues: false)
end
- it do
+ it 'excludes same project references' do
message = "Awesome commit (Closes #{reference})"
expect(subject.closed_by_message(message)).to eq([])
end
+
+ it 'includes issues from other projects with autoclose enabled' do
+ message = "Closes #{cross_reference}"
+ expect(subject.closed_by_message(message)).to eq([issue2])
+ end
end
end
diff --git a/spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb b/spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb
index 948de161235..cf532cf7be6 100644
--- a/spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb
+++ b/spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Cluster::PumaWorkerKillerObserver do
let(:counter) { Gitlab::Metrics::NullMetric.instance }
diff --git a/spec/lib/gitlab/config/entry/attributable_spec.rb b/spec/lib/gitlab/config/entry/attributable_spec.rb
index 1e7880ed898..8a207bddaae 100644
--- a/spec/lib/gitlab/config/entry/attributable_spec.rb
+++ b/spec/lib/gitlab/config/entry/attributable_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Config::Entry::Attributable do
let(:node) do
diff --git a/spec/lib/gitlab/config/entry/composable_hash_spec.rb b/spec/lib/gitlab/config/entry/composable_hash_spec.rb
index f64b39231a3..331c9efc741 100644
--- a/spec/lib/gitlab/config/entry/composable_hash_spec.rb
+++ b/spec/lib/gitlab/config/entry/composable_hash_spec.rb
@@ -6,7 +6,8 @@ RSpec.describe Gitlab::Config::Entry::ComposableHash, :aggregate_failures do
let(:valid_config) do
{
DATABASE_SECRET: 'passw0rd',
- API_TOKEN: 'passw0rd2'
+ API_TOKEN: 'passw0rd2',
+ ACCEPT_PASSWORD: false
}
end
@@ -55,6 +56,12 @@ RSpec.describe Gitlab::Config::Entry::ComposableHash, :aggregate_failures do
expect(entry[:API_TOKEN].metadata).to eq(name: :API_TOKEN)
expect(entry[:API_TOKEN].parent.class).to eq(Gitlab::Config::Entry::ComposableHash)
expect(entry[:API_TOKEN].value).to eq('passw0rd2')
+ expect(entry[:ACCEPT_PASSWORD]).to be_a(Gitlab::Config::Entry::Node)
+ expect(entry[:ACCEPT_PASSWORD].description).to eq('ACCEPT_PASSWORD node definition')
+ expect(entry[:ACCEPT_PASSWORD].key).to eq(:ACCEPT_PASSWORD)
+ expect(entry[:ACCEPT_PASSWORD].metadata).to eq(name: :ACCEPT_PASSWORD)
+ expect(entry[:ACCEPT_PASSWORD].parent.class).to eq(Gitlab::Config::Entry::ComposableHash)
+ expect(entry[:ACCEPT_PASSWORD].value).to eq(false)
end
describe '#descendants' do
diff --git a/spec/lib/gitlab/config/entry/simplifiable_spec.rb b/spec/lib/gitlab/config/entry/simplifiable_spec.rb
index f9088130037..fbbc9571eb0 100644
--- a/spec/lib/gitlab/config/entry/simplifiable_spec.rb
+++ b/spec/lib/gitlab/config/entry/simplifiable_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Config::Entry::Simplifiable do
describe '.strategy' do
diff --git a/spec/lib/gitlab/config/entry/undefined_spec.rb b/spec/lib/gitlab/config/entry/undefined_spec.rb
index 31e0f9487aa..faa9b9b8a7c 100644
--- a/spec/lib/gitlab/config/entry/undefined_spec.rb
+++ b/spec/lib/gitlab/config/entry/undefined_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Config::Entry::Undefined do
let(:entry) { described_class.new }
diff --git a/spec/lib/gitlab/config/entry/unspecified_spec.rb b/spec/lib/gitlab/config/entry/unspecified_spec.rb
index 35ba992f62a..8fc0889367f 100644
--- a/spec/lib/gitlab/config/entry/unspecified_spec.rb
+++ b/spec/lib/gitlab/config/entry/unspecified_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Config::Entry::Unspecified do
let(:unspecified) { described_class.new(entry) }
diff --git a/spec/lib/gitlab/container_repository/tags/cache_spec.rb b/spec/lib/gitlab/container_repository/tags/cache_spec.rb
index f84c1ce173f..fcfc8e7a348 100644
--- a/spec/lib/gitlab/container_repository/tags/cache_spec.rb
+++ b/spec/lib/gitlab/container_repository/tags/cache_spec.rb
@@ -79,10 +79,14 @@ RSpec.describe ::Gitlab::ContainerRepository::Tags::Cache, :clean_gitlab_redis_c
it 'inserts values in redis' do
::Gitlab::Redis::Cache.with do |redis|
- expect(redis)
- .to receive(:set)
- .with(cache_key(tag), rfc3339(tag.created_at), ex: ttl.to_i)
- .and_call_original
+ expect(redis).to receive(:pipelined).and_call_original
+
+ expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
+ expect(pipeline)
+ .to receive(:set)
+ .with(cache_key(tag), rfc3339(tag.created_at), ex: ttl.to_i)
+ .and_call_original
+ end
end
subject
diff --git a/spec/lib/gitlab/cross_project_access/check_collection_spec.rb b/spec/lib/gitlab/cross_project_access/check_collection_spec.rb
index 178188f5555..a75c943aaf6 100644
--- a/spec/lib/gitlab/cross_project_access/check_collection_spec.rb
+++ b/spec/lib/gitlab/cross_project_access/check_collection_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::CrossProjectAccess::CheckCollection do
subject(:collection) { described_class.new }
diff --git a/spec/lib/gitlab/cross_project_access/check_info_spec.rb b/spec/lib/gitlab/cross_project_access/check_info_spec.rb
index 5327030daf0..7cf2309a1f8 100644
--- a/spec/lib/gitlab/cross_project_access/check_info_spec.rb
+++ b/spec/lib/gitlab/cross_project_access/check_info_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::CrossProjectAccess::CheckInfo do
let(:dummy_controller) { double }
diff --git a/spec/lib/gitlab/cross_project_access/class_methods_spec.rb b/spec/lib/gitlab/cross_project_access/class_methods_spec.rb
index afc45c86362..3a6e528c9b0 100644
--- a/spec/lib/gitlab/cross_project_access/class_methods_spec.rb
+++ b/spec/lib/gitlab/cross_project_access/class_methods_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::CrossProjectAccess::ClassMethods do
let(:dummy_class) do
diff --git a/spec/lib/gitlab/cross_project_access_spec.rb b/spec/lib/gitlab/cross_project_access_spec.rb
index fb72b85f161..e45c734a003 100644
--- a/spec/lib/gitlab/cross_project_access_spec.rb
+++ b/spec/lib/gitlab/cross_project_access_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::CrossProjectAccess do
let(:super_class) { Class.new }
diff --git a/spec/lib/gitlab/cycle_analytics/summary/value_spec.rb b/spec/lib/gitlab/cycle_analytics/summary/value_spec.rb
index c955b288500..41b0604bee0 100644
--- a/spec/lib/gitlab/cycle_analytics/summary/value_spec.rb
+++ b/spec/lib/gitlab/cycle_analytics/summary/value_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::CycleAnalytics::Summary::Value do
describe Gitlab::CycleAnalytics::Summary::Value::None do
diff --git a/spec/lib/gitlab/data_builder/issuable_spec.rb b/spec/lib/gitlab/data_builder/issuable_spec.rb
index f0802f335f4..455800a3f7d 100644
--- a/spec/lib/gitlab/data_builder/issuable_spec.rb
+++ b/spec/lib/gitlab/data_builder/issuable_spec.rb
@@ -73,7 +73,7 @@ RSpec.describe Gitlab::DataBuilder::Issuable do
},
assignees: {
previous: [],
- current: [{
+ current: [{
name: "Foo Bar",
username: "foobar",
avatar_url: "http://www.example.com/my-avatar.jpg"
diff --git a/spec/lib/gitlab/data_builder/note_spec.rb b/spec/lib/gitlab/data_builder/note_spec.rb
index 3fa535dd800..8e8b8ce6681 100644
--- a/spec/lib/gitlab/data_builder/note_spec.rb
+++ b/spec/lib/gitlab/data_builder/note_spec.rb
@@ -49,8 +49,7 @@ RSpec.describe Gitlab::DataBuilder::Note do
let(:label) { create(:label, project: project) }
let(:issue) do
- create(:labeled_issue, created_at: fixed_time, updated_at: fixed_time,
- project: project, labels: [label])
+ create(:labeled_issue, created_at: fixed_time, updated_at: fixed_time, project: project, labels: [label])
end
let(:note) do
@@ -84,15 +83,15 @@ RSpec.describe Gitlab::DataBuilder::Note do
describe 'When asking for a note on merge request' do
let(:label) { create(:label, project: project) }
let(:merge_request) do
- create(:labeled_merge_request, created_at: fixed_time,
- updated_at: fixed_time,
- source_project: project,
- labels: [label])
+ create(:labeled_merge_request,
+ created_at: fixed_time,
+ updated_at: fixed_time,
+ source_project: project,
+ labels: [label])
end
let(:note) do
- create(:note_on_merge_request, noteable: merge_request,
- project: project)
+ create(:note_on_merge_request, noteable: merge_request, project: project)
end
it_behaves_like 'includes general data'
@@ -112,14 +111,15 @@ RSpec.describe Gitlab::DataBuilder::Note do
describe 'When asking for a note on merge request diff' do
let(:label) { create(:label, project: project) }
let(:merge_request) do
- create(:labeled_merge_request, created_at: fixed_time, updated_at: fixed_time,
- source_project: project,
- labels: [label])
+ create(:labeled_merge_request,
+ created_at: fixed_time,
+ updated_at: fixed_time,
+ source_project:
+ project, labels: [label])
end
let(:note) do
- create(:diff_note_on_merge_request, noteable: merge_request,
- project: project)
+ create(:diff_note_on_merge_request, noteable: merge_request, project: project)
end
it_behaves_like 'includes general data'
@@ -138,13 +138,11 @@ RSpec.describe Gitlab::DataBuilder::Note do
describe 'When asking for a note on project snippet' do
let!(:snippet) do
- create(:project_snippet, created_at: fixed_time, updated_at: fixed_time,
- project: project)
+ create(:project_snippet, created_at: fixed_time, updated_at: fixed_time, project: project)
end
let!(:note) do
- create(:note_on_project_snippet, noteable: snippet,
- project: project)
+ create(:note_on_project_snippet, noteable: snippet, project: project)
end
it_behaves_like 'includes general data'
diff --git a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
index 06c2bc32db3..3daed2508a2 100644
--- a/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
+++ b/spec/lib/gitlab/database/background_migration/batched_migration_spec.rb
@@ -59,6 +59,50 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
end
end
+ describe '#pause!' do
+ context 'when an invalid transition is applied' do
+ %i[finished failed finalizing].each do |state|
+ it 'raises an exception' do
+ batched_migration = create(:batched_background_migration, state)
+
+ expect { batched_migration.pause! }.to raise_error(StateMachines::InvalidTransition, /Cannot transition status/)
+ end
+ end
+ end
+
+ context 'when a valid transition is applied' do
+ %i[active paused].each do |state|
+ it 'moves to pause' do
+ batched_migration = create(:batched_background_migration, state)
+
+ expect(batched_migration.pause!).to be_truthy
+ end
+ end
+ end
+ end
+
+ describe '#execute!' do
+ context 'when an invalid transition is applied' do
+ %i[finished finalizing].each do |state|
+ it 'raises an exception' do
+ batched_migration = create(:batched_background_migration, state)
+
+ expect { batched_migration.execute! }.to raise_error(StateMachines::InvalidTransition, /Cannot transition status/)
+ end
+ end
+ end
+
+ context 'when a valid transition is applied' do
+ %i[active paused failed].each do |state|
+ it 'moves to active' do
+ batched_migration = create(:batched_background_migration, state)
+
+ expect(batched_migration.execute!).to be_truthy
+ end
+ end
+ end
+ end
+
describe '.valid_status' do
valid_status = [:paused, :active, :finished, :failed, :finalizing]
@@ -77,6 +121,16 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
end
end
+ describe '.ordered_by_created_at_desc' do
+ let!(:migration_1) { create(:batched_background_migration, created_at: Time.zone.now - 2) }
+ let!(:migration_2) { create(:batched_background_migration, created_at: Time.zone.now - 1) }
+ let!(:migration_3) { create(:batched_background_migration, created_at: Time.zone.now - 3) }
+
+ it 'returns batched migrations ordered by created_at (DESC)' do
+ expect(described_class.ordered_by_created_at_desc).to eq([migration_2, migration_1, migration_3])
+ end
+ end
+
describe '.active_migration' do
let(:connection) { Gitlab::Database.database_base_models[:main].connection }
let!(:migration1) { create(:batched_background_migration, :finished) }
@@ -620,7 +674,7 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
describe '#progress' do
subject { migration.progress }
- context 'when the migration is finished' do
+ context 'when the migration is completed' do
let(:migration) do
create(:batched_background_migration, :finished, total_tuple_count: 1).tap do |record|
create(:batched_background_migration_job, :succeeded, batched_migration: record, batch_size: 1)
@@ -632,6 +686,18 @@ RSpec.describe Gitlab::Database::BackgroundMigration::BatchedMigration, type: :m
end
end
+ context 'when the status is finished' do
+ let(:migration) do
+ create(:batched_background_migration, :finished, total_tuple_count: 100).tap do |record|
+ create(:batched_background_migration_job, :succeeded, batched_migration: record, batch_size: 5)
+ end
+ end
+
+ it 'returns 100' do
+ expect(subject).to be 100
+ end
+ end
+
context 'when the migration does not have jobs' do
let(:migration) { create(:batched_background_migration, :active) }
diff --git a/spec/lib/gitlab/database/batch_average_counter_spec.rb b/spec/lib/gitlab/database/batch_average_counter_spec.rb
new file mode 100644
index 00000000000..43c7a1554f7
--- /dev/null
+++ b/spec/lib/gitlab/database/batch_average_counter_spec.rb
@@ -0,0 +1,107 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::BatchAverageCounter do
+ let(:model) { Issue }
+ let(:column) { :weight }
+
+ let(:in_transaction) { false }
+
+ before do
+ allow(model.connection).to receive(:transaction_open?).and_return(in_transaction)
+ end
+
+ describe '#count' do
+ before do
+ create_list(:issue, 2, weight: 4)
+ create_list(:issue, 2, weight: 2)
+ create_list(:issue, 2, weight: 3)
+ end
+
+ subject(:batch_average_counter) { described_class.new(model, column) }
+
+ it 'returns correct average of weights' do
+ expect(subject.count).to eq(3.0)
+ end
+
+ it 'does no raise an exception if transaction is not open' do
+ expect { subject.count }.not_to raise_error
+ end
+
+ context 'when transaction is open' do
+ let(:in_transaction) { true }
+
+ it 'raises an error' do
+ expect { subject.count }.to raise_error('BatchAverageCounter can not be run inside a transaction')
+ end
+ end
+
+ context 'when batch size is small' do
+ let(:batch_size) { 2 }
+
+ it 'returns correct average of weights' do
+ expect(subject.count(batch_size: batch_size)).to eq(3.0)
+ end
+ end
+
+ context 'when column passed is an Arel attribute' do
+ let(:column) { model.arel_table[:weight] }
+
+ it 'returns correct average of weights' do
+ expect(subject.count).to eq(3.0)
+ end
+ end
+
+ context 'when column has total count of zero' do
+ before do
+ Issue.update_all(weight: nil)
+ end
+
+ it 'returns the fallback value' do
+ expect(subject.count).to eq(-1)
+ end
+ end
+
+ context 'when one batch has nil weights (no average)' do
+ before do
+ issues = Issue.where(weight: 4)
+ issues.update_all(weight: nil)
+ end
+
+ let(:batch_size) { 2 }
+
+ it 'calculates average of weights with no errors' do
+ expect(subject.count(batch_size: batch_size)).to eq(2.5)
+ end
+ end
+
+ context 'when batch fetch query is cancelled' do
+ let(:batch_size) { 22_000 }
+ let(:relation) { instance_double(ActiveRecord::Relation, to_sql: batch_average_query) }
+
+ context 'when all retries fail' do
+ let(:batch_average_query) { 'SELECT AVG(weight) FROM issues WHERE weight BETWEEN 2 and 5' }
+ let(:query_timed_out_exception) { ActiveRecord::QueryCanceled.new('query timed out') }
+
+ before do
+ allow(model).to receive(:where).and_return(relation)
+ allow(relation).to receive(:pick).and_raise(query_timed_out_exception)
+ end
+
+ it 'logs failing query' do
+ expect(Gitlab::AppJsonLogger).to receive(:error).with(
+ event: 'batch_count',
+ relation: model.table_name,
+ operation: 'average',
+ start: 2,
+ query: batch_average_query,
+ message: 'Query has been canceled with message: query timed out'
+ )
+
+ expect(subject.count(batch_size: batch_size)).to eq(-1)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/batch_count_spec.rb b/spec/lib/gitlab/database/batch_count_spec.rb
index 811d4fad95c..a87b0c1a3a8 100644
--- a/spec/lib/gitlab/database/batch_count_spec.rb
+++ b/spec/lib/gitlab/database/batch_count_spec.rb
@@ -86,48 +86,48 @@ RSpec.describe Gitlab::Database::BatchCount do
query: batch_count_query,
message: 'Query has been canceled with message: query timed out'
)
- expect(subject.call(model, column, batch_size: batch_size, start: 0)).to eq(-1)
+ expect(subject.call(model, column, batch_size: batch_size, start: 0)).to eq(fallback)
end
end
end
describe '#batch_count' do
it 'counts table' do
- expect(described_class.batch_count(model)).to eq(5)
+ expect(described_class.batch_count(model)).to eq(model.count)
end
it 'counts with :id field' do
- expect(described_class.batch_count(model, :id)).to eq(5)
+ expect(described_class.batch_count(model, :id)).to eq(model.count)
end
it 'counts with "id" field' do
- expect(described_class.batch_count(model, 'id')).to eq(5)
+ expect(described_class.batch_count(model, 'id')).to eq(model.count)
end
it 'counts with table.id field' do
- expect(described_class.batch_count(model, "#{model.table_name}.id")).to eq(5)
+ expect(described_class.batch_count(model, "#{model.table_name}.id")).to eq(model.count)
end
it 'counts with Arel column' do
- expect(described_class.batch_count(model, model.arel_table[:id])).to eq(5)
+ expect(described_class.batch_count(model, model.arel_table[:id])).to eq(model.count)
end
it 'counts table with batch_size 50K' do
- expect(described_class.batch_count(model, batch_size: 50_000)).to eq(5)
+ expect(described_class.batch_count(model, batch_size: 50_000)).to eq(model.count)
end
it 'will not count table with a batch size less than allowed' do
expect(described_class.batch_count(model, batch_size: small_batch_size)).to eq(fallback)
end
- it 'counts with a small edge case batch_sizes than result' do
+ it 'produces the same result with different batch sizes' do
stub_const('Gitlab::Database::BatchCounter::MIN_REQUIRED_BATCH_SIZE', 0)
- [1, 2, 4, 5, 6].each { |i| expect(described_class.batch_count(model, batch_size: i)).to eq(5) }
+ [1, 2, 4, 5, 6].each { |i| expect(described_class.batch_count(model, batch_size: i)).to eq(model.count) }
end
it 'counts with a start and finish' do
- expect(described_class.batch_count(model, start: model.minimum(:id), finish: model.maximum(:id))).to eq(5)
+ expect(described_class.batch_count(model, start: model.minimum(:id), finish: model.maximum(:id))).to eq(model.count)
end
it 'stops counting when finish value is reached' do
@@ -217,6 +217,113 @@ RSpec.describe Gitlab::Database::BatchCount do
end
end
+ describe '#batch_count_with_timeout' do
+ it 'counts table' do
+ expect(described_class.batch_count_with_timeout(model)).to eq({ status: :completed, count: model.count })
+ end
+
+ it 'counts with :id field' do
+ expect(described_class.batch_count_with_timeout(model, :id)).to eq({ status: :completed, count: model.count })
+ end
+
+ it 'counts with "id" field' do
+ expect(described_class.batch_count_with_timeout(model, 'id')).to eq({ status: :completed, count: model.count })
+ end
+
+ it 'counts with table.id field' do
+ expect(described_class.batch_count_with_timeout(model, "#{model.table_name}.id")).to eq({ status: :completed, count: model.count })
+ end
+
+ it 'counts with Arel column' do
+ expect(described_class.batch_count_with_timeout(model, model.arel_table[:id])).to eq({ status: :completed, count: model.count })
+ end
+
+ it 'counts table with batch_size 50K' do
+ expect(described_class.batch_count_with_timeout(model, batch_size: 50_000)).to eq({ status: :completed, count: model.count })
+ end
+
+ it 'will not count table with a batch size less than allowed' do
+ expect(described_class.batch_count_with_timeout(model, batch_size: small_batch_size)).to eq({ status: :bad_config })
+ end
+
+ it 'produces the same result with different batch sizes' do
+ stub_const('Gitlab::Database::BatchCounter::MIN_REQUIRED_BATCH_SIZE', 0)
+
+ [1, 2, 4, 5, 6].each { |i| expect(described_class.batch_count_with_timeout(model, batch_size: i)).to eq({ status: :completed, count: model.count }) }
+ end
+
+ it 'counts with a start and finish' do
+ expect(described_class.batch_count_with_timeout(model, start: model.minimum(:id), finish: model.maximum(:id))).to eq({ status: :completed, count: model.count })
+ end
+
+ it 'stops counting when finish value is reached' do
+ stub_const('Gitlab::Database::BatchCounter::MIN_REQUIRED_BATCH_SIZE', 0)
+
+ expect(described_class.batch_count_with_timeout(model,
+ start: model.minimum(:id),
+ finish: model.maximum(:id) - 1, # Do not count the last record
+ batch_size: model.count - 2 # Ensure there are multiple batches
+ )).to eq({ status: :completed, count: model.count - 1 })
+ end
+
+ it 'returns a partial count when timeout elapses' do
+ stub_const('Gitlab::Database::BatchCounter::MIN_REQUIRED_BATCH_SIZE', 0)
+
+ expect(::Gitlab::Metrics::System).to receive(:monotonic_time).and_return(1, 10, 300)
+
+ expect(
+ described_class.batch_count_with_timeout(model, batch_size: 1, timeout: 250.seconds)
+ ).to eq({ status: :timeout, partial_results: 1, continue_from: model.minimum(:id) + 1 })
+ end
+
+ it 'starts counting from a given partial result' do
+ expect(described_class.batch_count_with_timeout(model, partial_results: 3)).to eq({ status: :completed, count: 3 + model.count })
+ end
+
+ it_behaves_like 'when a transaction is open' do
+ subject { described_class.batch_count_with_timeout(model) }
+ end
+
+ it_behaves_like 'when batch fetch query is canceled' do
+ let(:mode) { :itself }
+ let(:operation) { :count }
+ let(:operation_args) { nil }
+ let(:column) { nil }
+ let(:fallback) { { status: :cancelled } }
+
+ subject { described_class.method(:batch_count_with_timeout) }
+ end
+
+ context 'disallowed_configurations' do
+ include_examples 'disallowed configurations', :batch_count do
+ let(:args) { [Issue] }
+ let(:default_batch_size) { Gitlab::Database::BatchCounter::DEFAULT_BATCH_SIZE }
+ end
+
+ it 'raises an error if distinct count is requested' do
+ expect { described_class.batch_count_with_timeout(model.distinct(column)) }.to raise_error 'Use distinct count for optimized distinct counting'
+ end
+ end
+
+ context 'when a relation is grouped' do
+ let!(:one_more_issue) { create(:issue, author: user, project: model.first.project) }
+
+ before do
+ stub_const('Gitlab::Database::BatchCounter::MIN_REQUIRED_BATCH_SIZE', 1)
+ end
+
+ context 'count by default column' do
+ let(:count) do
+ described_class.batch_count_with_timeout(model.group(column), batch_size: 2)
+ end
+
+ it 'counts grouped records' do
+ expect(count).to eq({ status: :completed, count: { user.id => 4, another_user.id => 2 } })
+ end
+ end
+ end
+ end
+
describe '#batch_distinct_count' do
it 'counts with column field' do
expect(described_class.batch_distinct_count(model, column)).to eq(2)
@@ -242,7 +349,7 @@ RSpec.describe Gitlab::Database::BatchCount do
expect(described_class.batch_distinct_count(model, column, batch_size: small_batch_size)).to eq(fallback)
end
- it 'counts with a small edge case batch_sizes than result' do
+ it 'produces the same result with different batch sizes' do
stub_const('Gitlab::Database::BatchCounter::MIN_REQUIRED_BATCH_SIZE', 0)
[1, 2, 4, 5, 6].each { |i| expect(described_class.batch_distinct_count(model, column, batch_size: i)).to eq(2) }
@@ -386,56 +493,18 @@ RSpec.describe Gitlab::Database::BatchCount do
end
describe '#batch_average' do
- let(:model) { Issue }
let(:column) { :weight }
before do
- Issue.update_all(weight: 2)
- end
-
- it 'returns the average of values in the given column' do
- expect(described_class.batch_average(model, column)).to eq(2)
- end
-
- it 'works when given an Arel column' do
- expect(described_class.batch_average(model, model.arel_table[column])).to eq(2)
- end
-
- it 'works with a batch size of 50K' do
- expect(described_class.batch_average(model, column, batch_size: 50_000)).to eq(2)
- end
-
- it 'works with start and finish provided' do
- expect(described_class.batch_average(model, column, start: model.minimum(:id), finish: model.maximum(:id))).to eq(2)
+ allow_next_instance_of(Gitlab::Database::BatchAverageCounter) do |instance|
+ allow(instance).to receive(:count).and_return
+ end
end
- it "defaults the batch size to #{Gitlab::Database::BatchCounter::DEFAULT_AVERAGE_BATCH_SIZE}" do
- min_id = model.minimum(:id)
- relation = instance_double(ActiveRecord::Relation)
- allow(model).to receive_message_chain(:select, public_send: relation)
- batch_end_id = min_id + calculate_batch_size(Gitlab::Database::BatchCounter::DEFAULT_AVERAGE_BATCH_SIZE)
-
- expect(relation).to receive(:where).with("id" => min_id..batch_end_id).and_return(double(send: 1))
+ it 'calls BatchAverageCounter' do
+ expect(Gitlab::Database::BatchAverageCounter).to receive(:new).with(model, column).and_call_original
described_class.batch_average(model, column)
end
-
- it_behaves_like 'when a transaction is open' do
- subject { described_class.batch_average(model, column) }
- end
-
- it_behaves_like 'disallowed configurations', :batch_average do
- let(:args) { [model, column] }
- let(:default_batch_size) { Gitlab::Database::BatchCounter::DEFAULT_AVERAGE_BATCH_SIZE }
- let(:small_batch_size) { Gitlab::Database::BatchCounter::DEFAULT_AVERAGE_BATCH_SIZE - 1 }
- end
-
- it_behaves_like 'when batch fetch query is canceled' do
- let(:mode) { :itself }
- let(:operation) { :average }
- let(:operation_args) { [column] }
-
- subject { described_class.method(:batch_average) }
- 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 eb527d492cf..b1cc8add55a 100644
--- a/spec/lib/gitlab/database/lock_writes_manager_spec.rb
+++ b/spec/lib/gitlab/database/lock_writes_manager_spec.rb
@@ -6,13 +6,15 @@ RSpec.describe Gitlab::Database::LockWritesManager do
let(:connection) { ApplicationRecord.connection }
let(:test_table) { '_test_table' }
let(:logger) { instance_double(Logger) }
+ let(:dry_run) { false }
subject(:lock_writes_manager) do
described_class.new(
table_name: test_table,
connection: connection,
database_name: 'main',
- logger: logger
+ logger: logger,
+ dry_run: dry_run
)
end
@@ -27,6 +29,16 @@ RSpec.describe Gitlab::Database::LockWritesManager do
SQL
end
+ describe "#table_locked_for_writes?" do
+ it 'returns false for a table that is not locked for writes' do
+ expect(subject.table_locked_for_writes?(test_table)).to eq(false)
+ end
+
+ 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
+
describe '#lock_writes' do
it 'prevents any writes on the table' do
subject.lock_writes
@@ -84,11 +96,57 @@ RSpec.describe Gitlab::Database::LockWritesManager do
subject.lock_writes
end.to raise_error(ActiveRecord::QueryCanceled)
end
+
+ it 'skips the operation if the table is already locked for writes' do
+ subject.lock_writes
+
+ expect(logger).to receive(:info).with("Skipping lock_writes, because #{test_table} is already locked for writes")
+ expect(connection).not_to receive(:execute).with(/CREATE TRIGGER/)
+
+ expect do
+ subject.lock_writes
+ end.not_to change {
+ number_of_triggers_on(connection, test_table)
+ }
+ end
+
+ context 'when running in dry_run mode' do
+ let(:dry_run) { true }
+
+ it 'prints the sql statement to the logger' do
+ expect(logger).to receive(:info).with("Database: 'main', Table: '#{test_table}': Lock Writes")
+ expected_sql_statement = <<~SQL
+ CREATE TRIGGER gitlab_schema_write_trigger_for_#{test_table}
+ BEFORE INSERT OR UPDATE OR DELETE OR TRUNCATE
+ ON #{test_table}
+ FOR EACH STATEMENT EXECUTE FUNCTION gitlab_schema_prevent_write();
+ SQL
+ expect(logger).to receive(:info).with(expected_sql_statement)
+
+ subject.lock_writes
+ end
+
+ it 'does not lock the tables for writes' do
+ subject.lock_writes
+
+ expect do
+ connection.execute("delete from #{test_table}")
+ connection.execute("truncate #{test_table}")
+ end.not_to raise_error
+ end
+ end
end
describe '#unlock_writes' do
before do
- subject.lock_writes
+ # Locking the table without the considering the value of dry_run
+ described_class.new(
+ table_name: test_table,
+ connection: connection,
+ database_name: 'main',
+ logger: logger,
+ dry_run: false
+ ).lock_writes
end
it 'allows writing on the table again' do
@@ -114,6 +172,28 @@ RSpec.describe Gitlab::Database::LockWritesManager do
subject.unlock_writes
end
+
+ context 'when running in dry_run mode' do
+ let(:dry_run) { true }
+
+ it 'prints the sql statement to the logger' do
+ expect(logger).to receive(:info).with("Database: 'main', Table: '#{test_table}': Allow Writes")
+ expected_sql_statement = <<~SQL
+ DROP TRIGGER IF EXISTS gitlab_schema_write_trigger_for_#{test_table} ON #{test_table};
+ SQL
+ expect(logger).to receive(:info).with(expected_sql_statement)
+
+ subject.unlock_writes
+ end
+
+ it 'does not unlock the tables for writes' do
+ subject.unlock_writes
+
+ expect do
+ connection.execute("delete from #{test_table}")
+ end.to raise_error(ActiveRecord::StatementInvalid, /Table: "#{test_table}" is write protected/)
+ end
+ end
end
def number_of_triggers_on(connection, table_name)
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index dd5ad40d8ef..d73b478ee7c 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -667,7 +667,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
column: :user_id,
on_delete: :cascade,
name: name,
- primary_key: :id).and_return(true)
+ primary_key: :id).and_return(true)
expect(model).not_to receive(:execute).with(/ADD CONSTRAINT/)
expect(model).to receive(:execute).with(/VALIDATE CONSTRAINT/)
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 9451a6bd34a..3ac483c8ab7 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
@@ -24,7 +24,7 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez
connection.execute(<<~SQL)
CREATE TABLE #{table_name} (
id bigint primary key not null,
- data bigint
+ data bigint default 0
);
insert into #{table_name} (id) select i from generate_series(1, 1000) g(i);
@@ -40,10 +40,12 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez
:id, :data,
batch_size: 100,
job_interval: 5.minutes) # job_interval is skipped when testing
- described_class.new(result_dir: result_dir, connection: connection).run_jobs(for_duration: 1.minute)
- unmigrated_row_count = define_batchable_model(table_name).where('id != data').count
- expect(unmigrated_row_count).to eq(0)
+ # Expect that running sampling for this migration processes some of the rows. Sampling doesn't run
+ # over every row in the table, so this does not completely migrate the table.
+ expect { described_class.new(result_dir: result_dir, connection: connection).run_jobs(for_duration: 1.minute) }
+ .to change { define_batchable_model(table_name).where('id IS DISTINCT FROM data').count }
+ .by_at_most(-1)
end
end
@@ -62,7 +64,7 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez
described_class.new(result_dir: result_dir, connection: connection).run_jobs(for_duration: 3.minutes)
- expect(calls.count).to eq(10) # 1000 rows / batch size 100 = 10
+ expect(calls).not_to be_empty
end
context 'with multiple jobs to run' do
@@ -92,4 +94,19 @@ RSpec.describe Gitlab::Database::Migrations::TestBatchedBackgroundRunner, :freez
end
end
end
+
+ context 'choosing uniform batches to run' do
+ subject { described_class.new(result_dir: result_dir, connection: connection) }
+
+ describe '#uniform_fractions' do
+ it 'generates evenly distributed sequences of fractions' do
+ received = subject.uniform_fractions.take(9)
+ expected = [0, 1, 1.0 / 2, 1.0 / 4, 3.0 / 4, 1.0 / 8, 3.0 / 8, 5.0 / 8, 7.0 / 8]
+
+ # All the fraction numerators are small integers, and all denominators are powers of 2, so these
+ # fit perfectly into floating point numbers with zero loss of precision
+ expect(received).to eq(expected)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/database/partitioning/convert_table_to_first_list_partition_spec.rb b/spec/lib/gitlab/database/partitioning/convert_table_to_first_list_partition_spec.rb
new file mode 100644
index 00000000000..af7d751a404
--- /dev/null
+++ b/spec/lib/gitlab/database/partitioning/convert_table_to_first_list_partition_spec.rb
@@ -0,0 +1,246 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::Partitioning::ConvertTableToFirstListPartition do
+ include Gitlab::Database::DynamicModelHelpers
+ include Database::TableSchemaHelpers
+
+ let(:migration_context) { Gitlab::Database::Migration[2.0].new }
+
+ let(:connection) { migration_context.connection }
+ let(:table_name) { '_test_table_to_partition' }
+ let(:table_identifier) { "#{connection.current_schema}.#{table_name}" }
+ let(:partitioning_column) { :partition_number }
+ let(:partitioning_default) { 1 }
+ let(:referenced_table_name) { '_test_referenced_table' }
+ let(:other_referenced_table_name) { '_test_other_referenced_table' }
+ let(:parent_table_name) { "#{table_name}_parent" }
+
+ let(:model) { define_batchable_model(table_name, connection: connection) }
+
+ let(:parent_model) { define_batchable_model(parent_table_name, connection: connection) }
+
+ let(:converter) do
+ described_class.new(
+ migration_context: migration_context,
+ table_name: table_name,
+ partitioning_column: partitioning_column,
+ parent_table_name: parent_table_name,
+ zero_partition_value: partitioning_default
+ )
+ end
+
+ before do
+ # Suppress printing migration progress
+ allow(migration_context).to receive(:puts)
+ allow(migration_context.connection).to receive(:transaction_open?).and_return(false)
+
+ connection.execute(<<~SQL)
+ create table #{referenced_table_name} (
+ id bigserial primary key not null
+ )
+ SQL
+
+ connection.execute(<<~SQL)
+ create table #{other_referenced_table_name} (
+ id bigserial primary key not null
+ )
+ SQL
+
+ connection.execute(<<~SQL)
+ insert into #{referenced_table_name} default values;
+ insert into #{other_referenced_table_name} default values;
+ SQL
+
+ connection.execute(<<~SQL)
+ create table #{table_name} (
+ id bigserial not null,
+ #{partitioning_column} bigint not null default #{partitioning_default},
+ referenced_id bigint not null references #{referenced_table_name} (id) on delete cascade,
+ other_referenced_id bigint not null references #{other_referenced_table_name} (id) on delete set null,
+ primary key (id, #{partitioning_column})
+ )
+ SQL
+
+ connection.execute(<<~SQL)
+ insert into #{table_name} (referenced_id, other_referenced_id)
+ select #{referenced_table_name}.id, #{other_referenced_table_name}.id
+ from #{referenced_table_name}, #{other_referenced_table_name};
+ SQL
+ end
+
+ describe "#prepare_for_partitioning" do
+ subject(:prepare) { converter.prepare_for_partitioning }
+
+ it 'adds a check constraint' do
+ expect { prepare }.to change {
+ Gitlab::Database::PostgresConstraint
+ .check_constraints
+ .by_table_identifier(table_identifier)
+ .count
+ }.from(0).to(1)
+ end
+ end
+
+ describe '#revert_prepare_for_partitioning' do
+ before do
+ converter.prepare_for_partitioning
+ end
+
+ subject(:revert_prepare) { converter.revert_preparation_for_partitioning }
+
+ it 'removes a check constraint' do
+ expect { revert_prepare }.to change {
+ Gitlab::Database::PostgresConstraint
+ .check_constraints
+ .by_table_identifier("#{connection.current_schema}.#{table_name}")
+ .count
+ }.from(1).to(0)
+ end
+ end
+
+ describe "#convert_to_zero_partition" do
+ subject(:partition) { converter.partition }
+
+ before do
+ converter.prepare_for_partitioning
+ end
+
+ context 'when the primary key is incorrect' do
+ before do
+ connection.execute(<<~SQL)
+ alter table #{table_name} drop constraint #{table_name}_pkey;
+ alter table #{table_name} add constraint #{table_name}_pkey PRIMARY KEY (id);
+ SQL
+ end
+
+ it 'throws a reasonable error message' do
+ expect { partition }.to raise_error(described_class::UnableToPartition, /#{partitioning_column}/)
+ end
+ end
+
+ context 'when there is not a supporting check constraint' do
+ before do
+ connection.execute(<<~SQL)
+ alter table #{table_name} drop constraint partitioning_constraint;
+ SQL
+ end
+
+ it 'throws a reasonable error message' do
+ expect { partition }.to raise_error(described_class::UnableToPartition, /constraint /)
+ end
+ end
+
+ it 'migrates the table to a partitioned table' do
+ fks_before = migration_context.foreign_keys(table_name)
+
+ partition
+
+ expect(Gitlab::Database::PostgresPartition.for_parent_table(parent_table_name).count).to eq(1)
+ expect(migration_context.foreign_keys(parent_table_name).map(&:options)).to match_array(fks_before.map(&:options))
+
+ connection.execute(<<~SQL)
+ insert into #{table_name} (referenced_id, other_referenced_id) select #{referenced_table_name}.id, #{other_referenced_table_name}.id from #{referenced_table_name}, #{other_referenced_table_name};
+ SQL
+
+ # Create a second partition
+ connection.execute(<<~SQL)
+ create table #{table_name}2 partition of #{parent_table_name} FOR VALUES IN (2)
+ SQL
+
+ parent_model.create!(partitioning_column => 2, :referenced_id => 1, :other_referenced_id => 1)
+ expect(parent_model.pluck(:id)).to match_array([1, 2, 3])
+ end
+
+ context 'when an error occurs during the conversion' do
+ def fail_first_time
+ # We can't directly use a boolean here, as we need something that will be passed by-reference to the proc
+ fault_status = { faulted: false }
+ proc do |m, *args, **kwargs|
+ next m.call(*args, **kwargs) if fault_status[:faulted]
+
+ fault_status[:faulted] = true
+ raise 'fault!'
+ end
+ end
+
+ def fail_sql_matching(regex)
+ proc do
+ allow(migration_context.connection).to receive(:execute).and_call_original
+ allow(migration_context.connection).to receive(:execute).with(regex).and_wrap_original(&fail_first_time)
+ end
+ end
+
+ def fail_adding_fk(from_table, to_table)
+ proc do
+ allow(migration_context.connection).to receive(:add_foreign_key).and_call_original
+ expect(migration_context.connection).to receive(:add_foreign_key).with(from_table, to_table, any_args)
+ .and_wrap_original(&fail_first_time)
+ end
+ end
+
+ where(:case_name, :fault) do
+ [
+ ["creating parent table", lazy { fail_sql_matching(/CREATE/i) }],
+ ["adding the first foreign key", lazy { fail_adding_fk(parent_table_name, referenced_table_name) }],
+ ["adding the second foreign key", lazy { fail_adding_fk(parent_table_name, other_referenced_table_name) }],
+ ["attaching table", lazy { fail_sql_matching(/ATTACH/i) }]
+ ]
+ end
+
+ before do
+ # Set up the fault that we'd like to inject
+ fault.call
+ end
+
+ with_them do
+ it 'recovers from a fault', :aggregate_failures do
+ expect { converter.partition }.to raise_error(/fault/)
+ expect(Gitlab::Database::PostgresPartition.for_parent_table(parent_table_name).count).to eq(0)
+
+ expect { converter.partition }.not_to raise_error
+ expect(Gitlab::Database::PostgresPartition.for_parent_table(parent_table_name).count).to eq(1)
+ end
+ end
+ end
+ end
+
+ describe '#revert_conversion_to_zero_partition' do
+ before do
+ converter.prepare_for_partitioning
+ converter.partition
+ end
+
+ subject(:revert_conversion) { converter.revert_partitioning }
+
+ it 'detaches the partition' do
+ expect { revert_conversion }.to change {
+ Gitlab::Database::PostgresPartition
+ .for_parent_table(parent_table_name).count
+ }.from(1).to(0)
+ end
+
+ it 'does not drop the child partition' do
+ expect { revert_conversion }.not_to change { table_oid(table_name) }
+ end
+
+ it 'removes the parent table' do
+ expect { revert_conversion }.to change { table_oid(parent_table_name).present? }.from(true).to(false)
+ end
+
+ it 're-adds the check constraint' do
+ expect { revert_conversion }.to change {
+ Gitlab::Database::PostgresConstraint
+ .check_constraints
+ .by_table_identifier(table_identifier)
+ .count
+ }.by(1)
+ end
+
+ it 'moves sequences back to the original table' do
+ expect { revert_conversion }.to change { converter.send(:sequences_owned_by, table_name).count }.from(0)
+ .and change { converter.send(:sequences_owned_by, parent_table_name).count }.to(0)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb b/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb
index dca4548a0a3..8027990a546 100644
--- a/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb
+++ b/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb
@@ -21,20 +21,11 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager do
let(:model) { double(partitioning_strategy: partitioning_strategy, table_name: table, connection: connection) }
let(:connection) { ActiveRecord::Base.connection }
- let(:table) { "issues" }
+ let(:table) { "my_model_example_table" }
let(:partitioning_strategy) do
double(missing_partitions: partitions, extra_partitions: [], after_adding_partitions: nil)
end
- before do
- allow(connection).to receive(:table_exists?).and_call_original
- allow(connection).to receive(:table_exists?).with(table).and_return(true)
- allow(connection).to receive(:execute).and_call_original
- expect(partitioning_strategy).to receive(:validate_and_fix)
-
- stub_exclusive_lease(described_class::MANAGEMENT_LEASE_KEY % table, timeout: described_class::LEASE_TIMEOUT)
- end
-
let(:partitions) do
[
instance_double(Gitlab::Database::Partitioning::TimePartition, table: 'bar', partition_name: 'foo', to_sql: "SELECT 1"),
@@ -42,19 +33,63 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager do
]
end
- it 'creates the partition' do
- expect(connection).to receive(:execute).with("LOCK TABLE \"#{table}\" IN ACCESS EXCLUSIVE MODE")
- expect(connection).to receive(:execute).with(partitions.first.to_sql)
- expect(connection).to receive(:execute).with(partitions.second.to_sql)
+ context 'when the given table is partitioned' do
+ before do
+ create_partitioned_table(connection, table)
- sync_partitions
+ allow(connection).to receive(:table_exists?).and_call_original
+ allow(connection).to receive(:table_exists?).with(table).and_return(true)
+ allow(connection).to receive(:execute).and_call_original
+ expect(partitioning_strategy).to receive(:validate_and_fix)
+
+ stub_exclusive_lease(described_class::MANAGEMENT_LEASE_KEY % table, timeout: described_class::LEASE_TIMEOUT)
+ end
+
+ it 'creates the partition' do
+ expect(connection).to receive(:execute).with("LOCK TABLE \"#{table}\" IN ACCESS EXCLUSIVE MODE")
+ expect(connection).to receive(:execute).with(partitions.first.to_sql)
+ expect(connection).to receive(:execute).with(partitions.second.to_sql)
+
+ sync_partitions
+ end
+
+ context 'with eplicitly provided connection' do
+ let(:connection) { Ci::ApplicationRecord.connection }
+
+ it 'uses the explicitly provided connection when any' do
+ skip_if_multiple_databases_not_setup
+
+ expect(connection).to receive(:execute).with("LOCK TABLE \"#{table}\" IN ACCESS EXCLUSIVE MODE")
+ expect(connection).to receive(:execute).with(partitions.first.to_sql)
+ expect(connection).to receive(:execute).with(partitions.second.to_sql)
+
+ described_class.new(model, connection: connection).sync_partitions
+ end
+ end
+
+ context 'when an error occurs during partition management' do
+ it 'does not raise an error' do
+ expect(partitioning_strategy).to receive(:missing_partitions).and_raise('this should never happen (tm)')
+
+ expect { sync_partitions }.not_to raise_error
+ end
+ end
end
- context 'when an error occurs during partition management' do
- it 'does not raise an error' do
- expect(partitioning_strategy).to receive(:missing_partitions).and_raise('this should never happen (tm)')
+ context 'when the table is not partitioned' do
+ let(:table) { 'this_does_not_need_to_be_real_table' }
+
+ it 'does not try creating the partitions' do
+ expect(connection).not_to receive(:execute).with("LOCK TABLE \"#{table}\" IN ACCESS EXCLUSIVE MODE")
+ expect(Gitlab::AppLogger).to receive(:warn).with(
+ {
+ message: 'Skipping synching partitions',
+ table_name: table,
+ connection_name: 'main'
+ }
+ )
- expect { sync_partitions }.not_to raise_error
+ sync_partitions
end
end
end
@@ -74,11 +109,7 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager do
end
before do
- connection.execute(<<~SQL)
- CREATE TABLE my_model_example_table
- (id serial not null, created_at timestamptz not null, primary key (id, created_at))
- PARTITION BY RANGE (created_at);
- SQL
+ create_partitioned_table(connection, 'my_model_example_table')
end
it 'creates partitions' do
@@ -98,6 +129,8 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager do
end
before do
+ create_partitioned_table(connection, table)
+
allow(connection).to receive(:table_exists?).and_call_original
allow(connection).to receive(:table_exists?).with(table).and_return(true)
expect(partitioning_strategy).to receive(:validate_and_fix)
@@ -260,4 +293,12 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager do
expect { described_class.new(my_model).sync_partitions }.to change { has_partition(my_model, 2.months.ago.beginning_of_month) }.from(true).to(false).and(change { num_partitions(my_model) }.by(0))
end
end
+
+ def create_partitioned_table(connection, table)
+ connection.execute(<<~SQL)
+ CREATE TABLE #{table}
+ (id serial not null, created_at timestamptz not null, primary key (id, created_at))
+ PARTITION BY RANGE (created_at);
+ SQL
+ end
end
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 04b9fba5b2f..07c2c6606d8 100644
--- a/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb
+++ b/spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb
@@ -136,7 +136,7 @@ RSpec.describe Gitlab::Database::Partitioning::SlidingListStrategy do
end
context 'when some partitions are true for detach_partition_if' do
- let(:detach_partition_if) { ->(p) { p != 5 } }
+ let(:detach_partition_if) { ->(p) { p.value != 5 } }
it 'is the leading set of partitions before that value' do
# should not contain partition 2 since it's the default value for the partition column
@@ -181,9 +181,10 @@ RSpec.describe Gitlab::Database::Partitioning::SlidingListStrategy do
Class.new(ApplicationRecord) do
include PartitionedTable
- partitioned_by :partition, strategy: :sliding_list,
- next_partition_if: proc { false },
- detach_partition_if: proc { false }
+ partitioned_by :partition,
+ strategy: :sliding_list,
+ next_partition_if: proc { false },
+ detach_partition_if: proc { false }
end
end.to raise_error(/ignored_columns/)
end
@@ -195,7 +196,8 @@ RSpec.describe Gitlab::Database::Partitioning::SlidingListStrategy do
self.ignored_columns = [:partition]
- partitioned_by :partition, strategy: :sliding_list,
+ partitioned_by :partition,
+ strategy: :sliding_list,
next_partition_if: proc { false },
detach_partition_if: proc { false }
end
@@ -221,7 +223,8 @@ RSpec.describe Gitlab::Database::Partitioning::SlidingListStrategy do
def self.detach_partition_if_wrapper(...)
detach_partition?(...)
end
- partitioned_by :partition, strategy: :sliding_list,
+ partitioned_by :partition,
+ strategy: :sliding_list,
next_partition_if: method(:next_partition_if_wrapper),
detach_partition_if: method(:detach_partition_if_wrapper)
diff --git a/spec/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table_spec.rb b/spec/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table_spec.rb
index 3072c413246..1885e84ac4c 100644
--- a/spec/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table_spec.rb
+++ b/spec/lib/gitlab/database/partitioning_migration_helpers/backfill_partitioned_table_spec.rb
@@ -97,7 +97,8 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::BackfillPartition
end
it 'marks each job record as succeeded after processing' do
- create(:background_migration_job, class_name: "::#{described_class.name}",
+ create(:background_migration_job,
+ class_name: "::#{described_class.name}",
arguments: [source1.id, source3.id, source_table, destination_table, unique_key])
expect(::Gitlab::Database::BackgroundMigrationJob).to receive(:mark_all_as_succeeded).and_call_original
@@ -108,7 +109,8 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::BackfillPartition
end
it 'returns the number of job records marked as succeeded' do
- create(:background_migration_job, class_name: "::#{described_class.name}",
+ create(:background_migration_job,
+ class_name: "::#{described_class.name}",
arguments: [source1.id, source3.id, source_table, destination_table, unique_key])
jobs_updated = backfill_job.perform(source1.id, source3.id, source_table, destination_table, unique_key)
diff --git a/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb b/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb
index edb8ae36c45..7465f69b87c 100644
--- a/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb
+++ b/spec/lib/gitlab/database/partitioning_migration_helpers/index_helpers_spec.rb
@@ -26,6 +26,7 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
CREATE TABLE #{table_name} (
id serial NOT NULL,
created_at timestamptz NOT NULL,
+ updated_at timestamptz NOT NULL,
PRIMARY KEY (id, created_at)
) PARTITION BY RANGE (created_at);
@@ -204,4 +205,30 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::IndexHelpers do
end
end
end
+
+ describe '#find_duplicate_indexes' do
+ context 'when duplicate and non-duplicate indexes exist' do
+ let(:nonduplicate_column_name) { 'updated_at' }
+ let(:nonduplicate_index_name) { 'updated_at_idx' }
+ let(:duplicate_column_name) { 'created_at' }
+ let(:duplicate_index_name1) { 'created_at_idx' }
+ let(:duplicate_index_name2) { 'index_on_created_at' }
+
+ before do
+ connection.execute(<<~SQL)
+ CREATE INDEX #{nonduplicate_index_name} ON #{table_name} (#{nonduplicate_column_name});
+ CREATE INDEX #{duplicate_index_name1} ON #{table_name} (#{duplicate_column_name});
+ CREATE INDEX #{duplicate_index_name2} ON #{table_name} (#{duplicate_column_name});
+ SQL
+ end
+
+ subject do
+ migration.find_duplicate_indexes(table_name)
+ end
+
+ it 'finds the duplicate index' do
+ expect(subject).to match_array([match_array([duplicate_index_name1, duplicate_index_name2])])
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb b/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb
index 1026b4370a5..8bb9ad2737a 100644
--- a/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb
+++ b/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb
@@ -41,6 +41,76 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::TableManagementHe
allow(migration).to receive(:assert_table_is_allowed)
end
+ context 'list partitioning conversion helpers' do
+ shared_examples_for 'delegates to ConvertTableToFirstListPartition' do
+ it 'throws an error if in a transaction' do
+ allow(migration).to receive(:transaction_open?).and_return(true)
+ expect { migrate }.to raise_error(/cannot be run inside a transaction/)
+ end
+
+ it 'delegates to a method on ConvertTableToFirstListPartition' do
+ expect_next_instance_of(Gitlab::Database::Partitioning::ConvertTableToFirstListPartition,
+ migration_context: migration,
+ table_name: source_table,
+ parent_table_name: partitioned_table,
+ partitioning_column: partition_column,
+ zero_partition_value: min_date) do |converter|
+ expect(converter).to receive(expected_method)
+ end
+
+ migrate
+ end
+ end
+
+ describe '#convert_table_to_first_list_partition' do
+ it_behaves_like 'delegates to ConvertTableToFirstListPartition' do
+ let(:expected_method) { :partition }
+ let(:migrate) do
+ migration.convert_table_to_first_list_partition(table_name: source_table,
+ partitioning_column: partition_column,
+ parent_table_name: partitioned_table,
+ initial_partitioning_value: min_date)
+ end
+ end
+ end
+
+ describe '#revert_converting_table_to_first_list_partition' do
+ it_behaves_like 'delegates to ConvertTableToFirstListPartition' do
+ let(:expected_method) { :revert_partitioning }
+ let(:migrate) do
+ migration.revert_converting_table_to_first_list_partition(table_name: source_table,
+ partitioning_column: partition_column,
+ parent_table_name: partitioned_table,
+ initial_partitioning_value: min_date)
+ end
+ end
+ end
+
+ describe '#prepare_constraint_for_list_partitioning' do
+ it_behaves_like 'delegates to ConvertTableToFirstListPartition' do
+ let(:expected_method) { :prepare_for_partitioning }
+ let(:migrate) do
+ migration.prepare_constraint_for_list_partitioning(table_name: source_table,
+ partitioning_column: partition_column,
+ parent_table_name: partitioned_table,
+ initial_partitioning_value: min_date)
+ end
+ end
+ end
+
+ describe '#revert_preparing_constraint_for_list_partitioning' do
+ it_behaves_like 'delegates to ConvertTableToFirstListPartition' do
+ let(:expected_method) { :revert_preparation_for_partitioning }
+ let(:migrate) do
+ migration.revert_preparing_constraint_for_list_partitioning(table_name: source_table,
+ partitioning_column: partition_column,
+ parent_table_name: partitioned_table,
+ initial_partitioning_value: min_date)
+ end
+ end
+ end
+ end
+
describe '#partition_table_by_date' do
let(:partition_column) { 'created_at' }
let(:old_primary_key) { 'id' }
diff --git a/spec/lib/gitlab/database/partitioning_spec.rb b/spec/lib/gitlab/database/partitioning_spec.rb
index 36c8b0811fe..94cdbfb2328 100644
--- a/spec/lib/gitlab/database/partitioning_spec.rb
+++ b/spec/lib/gitlab/database/partitioning_spec.rb
@@ -56,7 +56,7 @@ RSpec.describe Gitlab::Database::Partitioning do
end
it 'does not call sync_partitions' do
- expect(described_class).to receive(:sync_partitions).never
+ expect(described_class).not_to receive(:sync_partitions)
described_class.sync_partitions_ignore_db_error
end
@@ -64,6 +64,7 @@ RSpec.describe Gitlab::Database::Partitioning do
end
describe '.sync_partitions' do
+ let(:ci_connection) { Ci::ApplicationRecord.connection }
let(:table_names) { %w[partitioning_test1 partitioning_test2] }
let(:models) do
table_names.map do |table_name|
@@ -94,6 +95,38 @@ RSpec.describe Gitlab::Database::Partitioning do
.and change { find_partitions(table_names.last).size }.from(0)
end
+ context 'with multiple databases' do
+ before do
+ table_names.each do |table_name|
+ ci_connection.execute("DROP TABLE IF EXISTS #{table_name}")
+
+ ci_connection.execute(<<~SQL)
+ CREATE TABLE #{table_name} (
+ id serial not null,
+ created_at timestamptz not null,
+ PRIMARY KEY (id, created_at))
+ PARTITION BY RANGE (created_at);
+ SQL
+ end
+ end
+
+ after do
+ table_names.each do |table_name|
+ ci_connection.execute("DROP TABLE IF EXISTS #{table_name}")
+ end
+ end
+
+ it 'creates partitions in each database' do
+ skip_if_multiple_databases_not_setup
+
+ expect { described_class.sync_partitions(models) }
+ .to change { find_partitions(table_names.first, conn: connection).size }.from(0)
+ .and change { find_partitions(table_names.last, conn: connection).size }.from(0)
+ .and change { find_partitions(table_names.first, conn: ci_connection).size }.from(0)
+ .and change { find_partitions(table_names.last, conn: ci_connection).size }.from(0)
+ end
+ end
+
context 'when no partitioned models are given' do
it 'manages partitions for each registered model' do
described_class.register_models([models.first])
@@ -111,16 +144,44 @@ RSpec.describe Gitlab::Database::Partitioning do
end
context 'when only a specific database is requested' do
+ let(:ci_model) do
+ Class.new(Ci::ApplicationRecord) do
+ include PartitionedTable
+
+ self.table_name = 'partitioning_test3'
+ partitioned_by :created_at, strategy: :monthly
+ end
+ end
+
before do
- allow(models.first).to receive_message_chain('connection_db_config.name').and_return('main')
- allow(models.last).to receive_message_chain('connection_db_config.name').and_return('ci')
+ (table_names + ['partitioning_test3']).each do |table_name|
+ ci_connection.execute("DROP TABLE IF EXISTS #{table_name}")
+
+ ci_connection.execute(<<~SQL)
+ CREATE TABLE #{table_name} (
+ id serial not null,
+ created_at timestamptz not null,
+ PRIMARY KEY (id, created_at))
+ PARTITION BY RANGE (created_at);
+ SQL
+ end
+ end
+
+ after do
+ (table_names + ['partitioning_test3']).each do |table_name|
+ ci_connection.execute("DROP TABLE IF EXISTS #{table_name}")
+ end
end
it 'manages partitions for models for the given database', :aggregate_failures do
- expect { described_class.sync_partitions(models, only_on: 'ci') }
- .to change { find_partitions(table_names.last).size }.from(0)
+ skip_if_multiple_databases_not_setup
+
+ expect { described_class.sync_partitions([models.first, ci_model], only_on: 'ci') }
+ .to change { find_partitions(ci_model.table_name, conn: ci_connection).size }.from(0)
- expect(find_partitions(table_names.first).size).to eq(0)
+ expect(find_partitions(models.first.table_name).size).to eq(0)
+ expect(find_partitions(models.first.table_name, conn: ci_connection).size).to eq(0)
+ expect(find_partitions(ci_model.table_name).size).to eq(0)
end
end
end
diff --git a/spec/lib/gitlab/database/postgres_constraint_spec.rb b/spec/lib/gitlab/database/postgres_constraint_spec.rb
new file mode 100644
index 00000000000..75084a69115
--- /dev/null
+++ b/spec/lib/gitlab/database/postgres_constraint_spec.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::PostgresConstraint, type: :model do
+ # PostgresConstraint does not `behaves_like 'a postgres model'` because it does not correspond 1-1 with a single entry
+ # in pg_class
+ let(:schema) { ActiveRecord::Base.connection.current_schema }
+ let(:table_name) { '_test_table' }
+ let(:table_identifier) { "#{schema}.#{table_name}" }
+ let(:referenced_name) { '_test_referenced' }
+ let(:check_constraint_a_positive) { 'check_constraint_a_positive' }
+ let(:check_constraint_a_gt_b) { 'check_constraint_a_gt_b' }
+ let(:invalid_constraint_a) { 'check_constraint_b_positive_invalid' }
+ let(:unique_constraint_a) { "#{table_name}_a_key" }
+
+ before do
+ ActiveRecord::Base.connection.execute(<<~SQL)
+ create table #{referenced_name} (
+ id bigserial primary key not null
+ );
+
+ create table #{table_name} (
+ id bigserial not null,
+ referenced_id bigint not null references #{referenced_name}(id),
+ a integer unique,
+ b integer,
+ primary key (id, referenced_id),
+ constraint #{check_constraint_a_positive} check (a > 0),
+ constraint #{check_constraint_a_gt_b} check (a > b)
+ );
+
+ alter table #{table_name} add constraint #{invalid_constraint_a} CHECK (a > 1) NOT VALID;
+ SQL
+ end
+
+ describe '#by_table_identifier' do
+ subject(:constraints_for_table) { described_class.by_table_identifier(table_identifier) }
+
+ it 'includes all constraints on the table' do
+ all_constraints_for_table = described_class.all.to_a.select { |c| c.table_identifier == table_identifier }
+ expect(all_constraints_for_table.map(&:oid)).to match_array(constraints_for_table.pluck(:oid))
+ end
+
+ it 'throws an error if the format is incorrect' do
+ expect { described_class.by_table_identifier('not-an-identifier') }.to raise_error(ArgumentError)
+ end
+ end
+
+ describe '#check_constraints' do
+ subject(:check_constraints) { described_class.check_constraints.by_table_identifier(table_identifier) }
+
+ it 'finds check constraints for the table' do
+ expect(check_constraints.map(&:name)).to contain_exactly(check_constraint_a_positive,
+ check_constraint_a_gt_b,
+ invalid_constraint_a)
+ end
+
+ it 'includes columns for the check constraints', :aggregate_failures do
+ expect(check_constraints.find_by(name: check_constraint_a_positive).column_names).to contain_exactly('a')
+ expect(check_constraints.find_by(name: check_constraint_a_gt_b).column_names).to contain_exactly('a', 'b')
+ end
+ end
+
+ describe "#valid" do
+ subject(:valid_constraint_names) { described_class.valid.by_table_identifier(table_identifier).pluck(:name) }
+
+ let(:all_constraint_names) { described_class.by_table_identifier(table_identifier).pluck(:name) }
+
+ it 'excludes invalid constraints' do
+ expect(valid_constraint_names).not_to include(invalid_constraint_a)
+ expect(valid_constraint_names).to match_array(all_constraint_names - [invalid_constraint_a])
+ end
+ end
+
+ describe '#primary_key_constraints' do
+ subject(:pk_constraints) { described_class.primary_key_constraints.by_table_identifier(table_identifier) }
+
+ it 'finds the primary key constraint for the table' do
+ expect(pk_constraints.count).to eq(1)
+ expect(pk_constraints.first.constraint_type).to eq('p')
+ end
+
+ it 'finds the columns in the primary key constraint' do
+ constraint = pk_constraints.first
+ expect(constraint.column_names).to contain_exactly('id', 'referenced_id')
+ end
+ end
+
+ describe '#unique_constraints' do
+ subject(:unique_constraints) { described_class.unique_constraints.by_table_identifier(table_identifier) }
+
+ it 'finds the unique constraints for the table' do
+ expect(unique_constraints.pluck(:name)).to contain_exactly(unique_constraint_a)
+ end
+ end
+
+ describe '#primary_or_unique_constraints' do
+ subject(:pk_or_unique_constraints) do
+ described_class.primary_or_unique_constraints.by_table_identifier(table_identifier)
+ end
+
+ it 'finds primary and unique constraints' do
+ expect(pk_or_unique_constraints.pluck(:name)).to contain_exactly("#{table_name}_pkey", unique_constraint_a)
+ end
+ end
+
+ describe '#including_column' do
+ it 'only matches constraints on the given column' do
+ constraints_on_a = described_class.by_table_identifier(table_identifier).including_column('a').map(&:name)
+ expect(constraints_on_a).to contain_exactly(check_constraint_a_positive, check_constraint_a_gt_b,
+ unique_constraint_a, invalid_constraint_a)
+ end
+ end
+
+ describe '#not_including_column' do
+ it 'only matches constraints not including the given column' do
+ constraints_not_on_a = described_class.by_table_identifier(table_identifier).not_including_column('a').map(&:name)
+
+ expect(constraints_not_on_a).to contain_exactly("#{table_name}_pkey", "#{table_name}_referenced_id_fkey")
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/query_analyzers/ci/partitioning_analyzer_spec.rb b/spec/lib/gitlab/database/query_analyzers/ci/partitioning_analyzer_spec.rb
new file mode 100644
index 00000000000..ef7c7965c09
--- /dev/null
+++ b/spec/lib/gitlab/database/query_analyzers/ci/partitioning_analyzer_spec.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::QueryAnalyzers::Ci::PartitioningAnalyzer, query_analyzers: false do
+ let(:analyzer) { described_class }
+
+ before do
+ allow(Gitlab::Database::QueryAnalyzer.instance).to receive(:all_analyzers).and_return([analyzer])
+ end
+
+ context 'when ci_partitioning_analyze_queries is disabled' do
+ before do
+ stub_feature_flags(ci_partitioning_analyze_queries: false)
+ end
+
+ it 'does not analyze the query' do
+ expect(analyzer).not_to receive(:analyze)
+
+ process_sql(Ci::BuildMetadata, "SELECT 1 FROM ci_builds_metadata")
+ end
+ end
+
+ context 'when ci_partitioning_analyze_queries is enabled' do
+ context 'when analyzing targeted tables' do
+ described_class::ENABLED_TABLES.each do |enabled_table|
+ context 'when querying a non routing table' do
+ it 'tracks exception' do
+ expect(::Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception)
+ process_sql(Ci::ApplicationRecord, "SELECT 1 FROM #{enabled_table}")
+ end
+
+ it 'raises RoutingTableNotUsedError' do
+ expect { process_sql(Ci::ApplicationRecord, "SELECT 1 FROM #{enabled_table}") }
+ .to raise_error(described_class::RoutingTableNotUsedError)
+ end
+ end
+ end
+
+ context 'when updating a record' do
+ it 'raises RoutingTableNotUsedError' do
+ expect { process_sql(Ci::BuildMetadata, "UPDATE ci_builds_metadata SET id = 1") }
+ .to raise_error(described_class::RoutingTableNotUsedError)
+ end
+ end
+
+ context 'when inserting a record' do
+ it 'raises RoutingTableNotUsedError' do
+ expect { process_sql(Ci::BuildMetadata, "INSERT INTO ci_builds_metadata (id) VALUES(1)") }
+ .to raise_error(described_class::RoutingTableNotUsedError)
+ end
+ end
+ end
+
+ context 'when analyzing non targeted table' do
+ it 'does not raise error' do
+ expect { process_sql(Ci::BuildMetadata, "SELECT 1 FROM projects") }
+ .not_to raise_error
+ end
+ end
+
+ context 'when querying a routing table' do
+ it 'does not raise error' do
+ expect { process_sql(Ci::BuildMetadata, "SELECT 1 FROM p_ci_builds_metadata") }
+ .not_to raise_error
+ end
+ end
+ end
+
+ private
+
+ def process_sql(model, sql)
+ Gitlab::Database::QueryAnalyzer.instance.within do
+ # Skip load balancer and retrieve connection assigned to model
+ Gitlab::Database::QueryAnalyzer.instance.send(:process_sql, sql, model.retrieve_connection)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/reindexing_spec.rb b/spec/lib/gitlab/database/reindexing_spec.rb
index 495e953f993..4c98185e780 100644
--- a/spec/lib/gitlab/database/reindexing_spec.rb
+++ b/spec/lib/gitlab/database/reindexing_spec.rb
@@ -46,25 +46,11 @@ RSpec.describe Gitlab::Database::Reindexing do
end
end
- context 'when async index destruction is enabled' do
- it 'executes async index destruction prior to any reindexing actions' do
- stub_feature_flags(database_async_index_destruction: true)
+ it 'executes async index destruction prior to any reindexing actions' do
+ expect(Gitlab::Database::AsyncIndexes).to receive(:drop_pending_indexes!).ordered.exactly(databases_count).times
+ expect(described_class).to receive(:automatic_reindexing).ordered.exactly(databases_count).times
- expect(Gitlab::Database::AsyncIndexes).to receive(:drop_pending_indexes!).ordered.exactly(databases_count).times
- expect(described_class).to receive(:automatic_reindexing).ordered.exactly(databases_count).times
-
- described_class.invoke
- end
- end
-
- context 'when async index destruction is disabled' do
- it 'does not execute async index destruction' do
- stub_feature_flags(database_async_index_destruction: false)
-
- expect(Gitlab::Database::AsyncIndexes).not_to receive(:drop_pending_indexes!)
-
- described_class.invoke
- end
+ described_class.invoke
end
context 'calls automatic reindexing' do
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
new file mode 100644
index 00000000000..97abd6d23bd
--- /dev/null
+++ b/spec/lib/gitlab/database/tables_sorted_by_foreign_keys_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::TablesSortedByForeignKeys do
+ let(:connection) { ApplicationRecord.connection }
+ let(:tables) { %w[_test_gitlab_main_items _test_gitlab_main_references] }
+
+ subject do
+ described_class.new(connection, tables).execute
+ end
+
+ before do
+ statement = <<~SQL
+ CREATE TABLE _test_gitlab_main_items (id serial NOT NULL PRIMARY KEY);
+
+ CREATE TABLE _test_gitlab_main_references (
+ id serial NOT NULL PRIMARY KEY,
+ item_id BIGINT NOT NULL,
+ CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_main_items(id)
+ );
+ 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']])
+ end
+
+ it 'returns both tables together if they are strongly connected' do
+ statement = <<~SQL
+ ALTER TABLE _test_gitlab_main_items ADD COLUMN reference_id BIGINT
+ REFERENCES _test_gitlab_main_references(id)
+ SQL
+ connection.execute(statement)
+
+ expect(subject).to eq([tables])
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/tables_truncate_spec.rb b/spec/lib/gitlab/database/tables_truncate_spec.rb
new file mode 100644
index 00000000000..01af9efd782
--- /dev/null
+++ b/spec/lib/gitlab/database/tables_truncate_spec.rb
@@ -0,0 +1,257 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Database::TablesTruncate, :reestablished_active_record_base,
+ :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") }
+ let(:main_db_main_reference_model) { table("_test_gitlab_main_references", database: "main") }
+ 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") }
+ # 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
+ end
+
+ shared_examples 'truncating legacy tables on a database' do
+ before do
+ skip_if_multiple_databases_not_setup
+
+ # Creating some test tables on the main database
+ main_tables_sql = <<~SQL
+ CREATE TABLE _test_gitlab_main_items (id serial NOT NULL PRIMARY KEY);
+
+ CREATE TABLE _test_gitlab_main_references (
+ id serial NOT NULL PRIMARY KEY,
+ item_id BIGINT NOT NULL,
+ CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_main_items(id)
+ );
+ SQL
+
+ main_connection.execute(main_tables_sql)
+ ci_connection.execute(main_tables_sql)
+
+ ci_tables_sql = <<~SQL
+ CREATE TABLE _test_gitlab_ci_items (id serial NOT NULL PRIMARY KEY);
+
+ CREATE TABLE _test_gitlab_ci_references (
+ id serial NOT NULL PRIMARY KEY,
+ item_id BIGINT NOT NULL,
+ CONSTRAINT fk_constrained_1 FOREIGN KEY(item_id) REFERENCES _test_gitlab_ci_items(id)
+ );
+ SQL
+
+ main_connection.execute(ci_tables_sql)
+ ci_connection.execute(ci_tables_sql)
+
+ internal_tables_sql = <<~SQL
+ CREATE TABLE _test_gitlab_shared_items (id serial NOT NULL PRIMARY KEY);
+ SQL
+
+ main_connection.execute(internal_tables_sql)
+ ci_connection.execute(internal_tables_sql)
+
+ # Filling the tables
+ 5.times do |i|
+ # Main Database
+ main_db_main_item_model.create!(id: i)
+ main_db_main_reference_model.create!(item_id: i)
+ 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)
+ # 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)
+ 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_ci_items" => :gitlab_ci,
+ "_test_gitlab_ci_references" => :gitlab_ci,
+ "_test_gitlab_shared_items" => :gitlab_shared,
+ "_test_gitlab_geo_items" => :gitlab_geo
+ }
+ )
+
+ allow(logger).to receive(:info).with(any_args)
+ end
+
+ context 'when the truncated tables are not locked for writes' do
+ it 'raises an error that the tables are not locked for writes' do
+ error_message = /is not locked for writes. Run the rake task gitlab:db:lock_writes first/
+ expect { truncate_legacy_tables }.to raise_error(error_message)
+ end
+ end
+
+ context 'when the truncated tables are locked for writes' do
+ before do
+ legacy_tables_models.map(&:table_name).each do |table|
+ Gitlab::Database::LockWritesManager.new(
+ table_name: table,
+ connection: connection,
+ database_name: database_name
+ ).lock_writes
+ end
+ end
+
+ it 'truncates the legacy tables' do
+ old_counts = legacy_tables_models.map(&:count)
+ expect do
+ truncate_legacy_tables
+ end.to change { legacy_tables_models.map(&:count) }.from(old_counts).to([0] * legacy_tables_models.length)
+ end
+
+ it 'does not affect the other tables' do
+ expect do
+ truncate_legacy_tables
+ end.not_to change { other_tables_models.map(&:count) }
+ end
+
+ it 'logs the sql statements to the logger' do
+ expect(logger).to receive(:info).with("SET LOCAL lock_timeout = 0")
+ expect(logger).to receive(:info).with("SET LOCAL statement_timeout = 0")
+ expect(logger).to receive(:info)
+ .with(/TRUNCATE TABLE #{legacy_tables_models.map(&:table_name).sort.join(', ')} RESTRICT/)
+ truncate_legacy_tables
+ end
+
+ context 'when running in dry_run mode' do
+ let(:dry_run) { true }
+
+ it 'does not truncate the legacy tables if running in dry run mode' do
+ legacy_tables_models = [main_db_ci_reference_model, main_db_ci_reference_model]
+ expect do
+ truncate_legacy_tables
+ end.not_to change { legacy_tables_models.map(&:count) }
+ end
+ end
+
+ context 'when passing until_table parameter' do
+ context 'with a table that exists' do
+ let(:until_table) { referencing_table_model.table_name }
+
+ it 'only truncates until the table specified' do
+ expect do
+ truncate_legacy_tables
+ end.to change(referencing_table_model, :count).by(-5)
+ .and change(referenced_table_model, :count).by(0)
+ end
+ end
+
+ context 'with a table that does not exist' do
+ let(:until_table) { 'foobar' }
+
+ it 'raises an error if the specified table does not exist' do
+ expect do
+ truncate_legacy_tables
+ end.to raise_error(/The table 'foobar' is not within the truncated tables/)
+ end
+ end
+ end
+
+ context 'with geo configured' do
+ let(:geo_connection) { Gitlab::Database.database_base_models[:geo].connection }
+
+ before do
+ skip unless geo_configured?
+ geo_connection.execute('CREATE TABLE _test_gitlab_geo_items (id serial NOT NULL PRIMARY KEY)')
+ geo_connection.execute('INSERT INTO _test_gitlab_geo_items VALUES(generate_series(1, 50))')
+ end
+
+ it 'does not truncate gitlab_geo tables' do
+ expect do
+ truncate_legacy_tables
+ end.not_to change { geo_connection.select_value("select count(*) from _test_gitlab_geo_items") }
+ end
+ end
+ end
+ end
+
+ 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 }
+ let(:other_tables_models) do
+ [
+ main_db_main_item_model, main_db_main_reference_model,
+ ci_db_ci_item_model, ci_db_ci_reference_model,
+ ci_db_main_item_model, ci_db_main_reference_model,
+ main_db_shared_item_model, ci_db_shared_item_model
+ ]
+ end
+
+ it_behaves_like 'truncating legacy tables on a database'
+ end
+
+ 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(:referencing_table_model) { ci_db_main_reference_model }
+ let(:referenced_table_model) { ci_db_main_item_model }
+ let(:other_tables_models) do
+ [
+ main_db_main_item_model, main_db_main_reference_model,
+ ci_db_ci_item_model, ci_db_ci_reference_model,
+ main_db_ci_item_model, main_db_ci_reference_model,
+ main_db_shared_item_model, ci_db_shared_item_model
+ ]
+ end
+
+ it_behaves_like 'truncating legacy tables on a database'
+ end
+
+ context 'when running in a single database mode' do
+ before do
+ skip_if_multiple_databases_are_setup
+ end
+
+ it 'raises an error when truncating the main database that it is a single database setup' do
+ expect do
+ described_class.new(database_name: 'main', min_batch_size: min_batch_size).execute
+ end.to raise_error(/Cannot truncate legacy tables in single-db setup/)
+ end
+
+ it 'raises an error when truncating the ci database that it is a single database setup' do
+ expect do
+ described_class.new(database_name: 'ci', min_batch_size: min_batch_size).execute
+ end.to raise_error(/Cannot truncate legacy tables in single-db setup/)
+ end
+ end
+
+ def geo_configured?
+ !!ActiveRecord::Base.configurations.configs_for(env_name: Rails.env, name: 'geo')
+ end
+end
diff --git a/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb b/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb
index 6601b6658d5..ad91320c6eb 100644
--- a/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb
+++ b/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb
@@ -181,8 +181,8 @@ RSpec.describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService
let(:existing_project) { create(:project, namespace: existing_group) }
before do
- application_setting.update!(instance_administrators_group_id: existing_group.id,
- self_monitoring_project_id: existing_project.id)
+ application_setting.update!(
+ instance_administrators_group_id: existing_group.id, self_monitoring_project_id: existing_project.id)
end
it 'returns success' do
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 452a662bdcb..c893bca9e62 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -237,6 +237,26 @@ RSpec.describe Gitlab::Database do
end
end
+ it 'does return a valid schema for a replica connection' do
+ with_replica_pool_for(ActiveRecord::Base) do |main_replica_pool|
+ expect(described_class.gitlab_schemas_for_connection(main_replica_pool.connection)).to include(:gitlab_main, :gitlab_shared)
+ end
+
+ with_replica_pool_for(Ci::ApplicationRecord) do |ci_replica_pool|
+ expect(described_class.gitlab_schemas_for_connection(ci_replica_pool.connection)).to include(:gitlab_ci, :gitlab_shared)
+ end
+ end
+
+ def with_replica_pool_for(base_model)
+ config = Gitlab::Database::LoadBalancing::Configuration.new(base_model, [base_model.connection_pool.db_config.host])
+ lb = Gitlab::Database::LoadBalancing::LoadBalancer.new(config)
+ pool = lb.create_replica_connection_pool(1)
+
+ yield pool
+ ensure
+ pool&.disconnect!
+ end
+
context "when there's CI connection", :request_store do
before do
skip_if_multiple_databases_not_setup
diff --git a/spec/lib/gitlab/dependency_linker/parser/gemfile_spec.rb b/spec/lib/gitlab/dependency_linker/parser/gemfile_spec.rb
index 15f580a3a60..47d09e7a165 100644
--- a/spec/lib/gitlab/dependency_linker/parser/gemfile_spec.rb
+++ b/spec/lib/gitlab/dependency_linker/parser/gemfile_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::DependencyLinker::Parser::Gemfile do
describe '#parse' do
diff --git a/spec/lib/gitlab/dependency_linker_spec.rb b/spec/lib/gitlab/dependency_linker_spec.rb
index 2daa8df815d..8feab0f8017 100644
--- a/spec/lib/gitlab/dependency_linker_spec.rb
+++ b/spec/lib/gitlab/dependency_linker_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::DependencyLinker do
describe '.link' do
diff --git a/spec/lib/gitlab/diff/file_collection_sorter_spec.rb b/spec/lib/gitlab/diff/file_collection_sorter_spec.rb
index 9ba9271cefc..ca9c156c1ad 100644
--- a/spec/lib/gitlab/diff/file_collection_sorter_spec.rb
+++ b/spec/lib/gitlab/diff/file_collection_sorter_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Diff::FileCollectionSorter do
let(:diffs) do
diff --git a/spec/lib/gitlab/diff/highlight_cache_spec.rb b/spec/lib/gitlab/diff/highlight_cache_spec.rb
index 1d1ffc8c275..53e74748234 100644
--- a/spec/lib/gitlab/diff/highlight_cache_spec.rb
+++ b/spec/lib/gitlab/diff/highlight_cache_spec.rb
@@ -9,33 +9,33 @@ RSpec.describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do
{ ".gitignore-false-false-false" =>
[{ line_code: nil, rich_text: nil, text: "@@ -17,3 +17,4 @@ rerun.txt", type: "match", index: 0, old_pos: 17, new_pos: 17 },
{ line_code: "a5cc2925ca8258af241be7e5b0381edf30266302_17_17",
- rich_text: " <span id=\"LC17\" class=\"line\" lang=\"plaintext\">pickle-email-*.html</span>\n",
- text: " pickle-email-*.html",
- type: nil,
- index: 1,
- old_pos: 17,
- new_pos: 17 },
+ rich_text: " <span id=\"LC17\" class=\"line\" lang=\"plaintext\">pickle-email-*.html</span>\n",
+ text: " pickle-email-*.html",
+ type: nil,
+ index: 1,
+ old_pos: 17,
+ new_pos: 17 },
{ line_code: "a5cc2925ca8258af241be7e5b0381edf30266302_18_18",
- rich_text: " <span id=\"LC18\" class=\"line\" lang=\"plaintext\">.project</span>\n",
- text: " .project",
- type: nil,
- index: 2,
- old_pos: 18,
- new_pos: 18 },
+ rich_text: " <span id=\"LC18\" class=\"line\" lang=\"plaintext\">.project</span>\n",
+ text: " .project",
+ type: nil,
+ index: 2,
+ old_pos: 18,
+ new_pos: 18 },
{ line_code: "a5cc2925ca8258af241be7e5b0381edf30266302_19_19",
- rich_text: " <span id=\"LC19\" class=\"line\" lang=\"plaintext\">config/initializers/secret_token.rb</span>\n",
- text: " config/initializers/secret_token.rb",
- type: nil,
- index: 3,
- old_pos: 19,
- new_pos: 19 },
+ rich_text: " <span id=\"LC19\" class=\"line\" lang=\"plaintext\">config/initializers/secret_token.rb</span>\n",
+ text: " config/initializers/secret_token.rb",
+ type: nil,
+ index: 3,
+ old_pos: 19,
+ new_pos: 19 },
{ line_code: "a5cc2925ca8258af241be7e5b0381edf30266302_20_20",
- rich_text: "+<span id=\"LC20\" class=\"line\" lang=\"plaintext\">.DS_Store</span>",
- text: "+.DS_Store",
- type: "new",
- index: 4,
- old_pos: 20,
- new_pos: 20 }] }
+ rich_text: "+<span id=\"LC20\" class=\"line\" lang=\"plaintext\">.DS_Store</span>",
+ text: "+.DS_Store",
+ type: "new",
+ index: 4,
+ old_pos: 20,
+ new_pos: 20 }] }
end
let(:cache_key) { cache.key }
@@ -109,23 +109,59 @@ RSpec.describe Gitlab::Diff::HighlightCache, :clean_gitlab_redis_cache do
end
shared_examples 'caches missing entries' do
- it 'filters the key/value list of entries to be caches for each invocation' do
- expect(cache).to receive(:write_to_redis_hash)
- .with(hash_including(*paths))
- .once
- .and_call_original
-
- Gitlab::Redis::Cache.with do |redis|
- expect(redis).to receive(:expire).with(cache.key, described_class::EXPIRATION)
+ where(:expiration_period, :renewable_expiration_ff, :short_renewable_expiration_ff) do
+ [
+ [1.day, false, true],
+ [1.day, false, false],
+ [1.hour, true, true],
+ [8.hours, true, false]
+ ]
+ end
+
+ with_them do
+ before do
+ stub_feature_flags(
+ highlight_diffs_renewable_expiration: renewable_expiration_ff,
+ highlight_diffs_short_renewable_expiration: short_renewable_expiration_ff
+ )
end
- 2.times { cache.write_if_empty }
- end
+ it 'filters the key/value list of entries to be caches for each invocation' do
+ expect(cache).to receive(:write_to_redis_hash)
+ .with(hash_including(*paths))
+ .once
+ .and_call_original
- it 'reads from cache once' do
- expect(cache).to receive(:read_cache).once.and_call_original
+ 2.times { cache.write_if_empty }
+ end
- cache.write_if_empty
+ it 'reads from cache once' do
+ expect(cache).to receive(:read_cache).once.and_call_original
+
+ cache.write_if_empty
+ end
+
+ it 'refreshes TTL of the key on read' do
+ cache.write_if_empty
+
+ time_until_expire = 30.minutes
+
+ Gitlab::Redis::Cache.with do |redis|
+ # Emulate that a key is going to expire soon
+ redis.expire(cache.key, time_until_expire)
+
+ expect(redis.ttl(cache.key)).to be <= time_until_expire
+
+ cache.send(:read_cache)
+
+ if renewable_expiration_ff
+ expect(redis.ttl(cache.key)).to be > time_until_expire
+ expect(redis.ttl(cache.key)).to be_within(1.minute).of(expiration_period)
+ else
+ expect(redis.ttl(cache.key)).to be <= time_until_expire
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb b/spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb
index 3670074cc21..87d47e36f6a 100644
--- a/spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb
+++ b/spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Diff::InlineDiffMarkdownMarker do
describe '#mark' do
diff --git a/spec/lib/gitlab/diff/inline_diff_marker_spec.rb b/spec/lib/gitlab/diff/inline_diff_marker_spec.rb
index 6820a7df95e..8ab2a7b64dd 100644
--- a/spec/lib/gitlab/diff/inline_diff_marker_spec.rb
+++ b/spec/lib/gitlab/diff/inline_diff_marker_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Diff::InlineDiffMarker do
describe '#mark' do
diff --git a/spec/lib/gitlab/diff/lines_unfolder_spec.rb b/spec/lib/gitlab/diff/lines_unfolder_spec.rb
index f0e710be2e4..98f0c4df204 100644
--- a/spec/lib/gitlab/diff/lines_unfolder_spec.rb
+++ b/spec/lib/gitlab/diff/lines_unfolder_spec.rb
@@ -189,14 +189,14 @@ RSpec.describe Gitlab::Diff::LinesUnfolder do
let(:diff) do
Gitlab::Git::Diff.new({ diff: raw_diff,
- new_path: "build-aux/flatpak/org.gnome.Nautilus.json",
- old_path: "build-aux/flatpak/org.gnome.Nautilus.json",
- a_mode: "100644",
- b_mode: "100644",
- new_file: false,
- renamed_file: false,
- deleted_file: false,
- too_large: false })
+ new_path: "build-aux/flatpak/org.gnome.Nautilus.json",
+ old_path: "build-aux/flatpak/org.gnome.Nautilus.json",
+ a_mode: "100644",
+ b_mode: "100644",
+ new_file: false,
+ renamed_file: false,
+ deleted_file: false,
+ too_large: false })
end
let(:diff_file) do
diff --git a/spec/lib/gitlab/diff/position_spec.rb b/spec/lib/gitlab/diff/position_spec.rb
index bb3522eb579..00a468bfef6 100644
--- a/spec/lib/gitlab/diff/position_spec.rb
+++ b/spec/lib/gitlab/diff/position_spec.rb
@@ -684,7 +684,7 @@ RSpec.describe Gitlab::Diff::Position do
"old_line" => 18,
"new_line" => 18
},
- "end" => {
+ "end" => {
"line_code" => end_line_code,
"type" => nil,
"old_line" => end_old_line,
diff --git a/spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb b/spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb
index b5137f9db6b..e1135f4d546 100644
--- a/spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb
+++ b/spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb
@@ -5,7 +5,8 @@ require 'spec_helper'
RSpec.describe Gitlab::Diff::Rendered::Notebook::DiffFile do
include RepoHelpers
- let(:project) { create(:project, :repository) }
+ let_it_be(:project) { create(:project, :repository) }
+
let(:commit) { project.commit("5d6ed1503801ca9dc28e95eeb85a7cf863527aee") }
let(:diffs) { commit.raw_diffs.to_a }
let(:diff) { diffs.first }
diff --git a/spec/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512_spec.rb b/spec/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512_spec.rb
deleted file mode 100644
index e953733c997..00000000000
--- a/spec/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512_spec.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::DoorkeeperSecretStoring::Pbkdf2Sha512 do
- describe '.transform_secret' do
- let(:plaintext_token) { 'CzOBzBfU9F-HvsqfTaTXF4ivuuxYZuv3BoAK4pnvmyw' }
-
- it 'generates a PBKDF2+SHA512 hashed value in the correct format' do
- expect(described_class.transform_secret(plaintext_token))
- .to eq("$pbkdf2-sha512$20000$$.c0G5XJVEew1TyeJk5TrkvB0VyOaTmDzPrsdNRED9vVeZlSyuG3G90F0ow23zUCiWKAVwmNnR/ceh.nJG3MdpQ") # rubocop:disable Layout/LineLength
- end
-
- context 'when hash_oauth_tokens is disabled' do
- before do
- stub_feature_flags(hash_oauth_tokens: false)
- end
-
- it 'returns a plaintext token' do
- expect(described_class.transform_secret(plaintext_token)).to eq(plaintext_token)
- end
- end
- end
-
- describe 'STRETCHES' do
- it 'is 20_000' do
- expect(described_class::STRETCHES).to eq(20_000)
- end
- end
-
- describe 'SALT' do
- it 'is empty' do
- expect(described_class::SALT).to be_empty
- end
- end
-end
diff --git a/spec/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512_spec.rb b/spec/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512_spec.rb
new file mode 100644
index 00000000000..df17d92bb0c
--- /dev/null
+++ b/spec/lib/gitlab/doorkeeper_secret_storing/secret/pbkdf2_sha512_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::DoorkeeperSecretStoring::Secret::Pbkdf2Sha512 do
+ describe '.transform_secret' do
+ let(:plaintext_secret) { 'CzOBzBfU9F-HvsqfTaTXF4ivuuxYZuv3BoAK4pnvmyw' }
+
+ it 'generates a PBKDF2+SHA512 hashed value in the correct format' do
+ expect(described_class.transform_secret(plaintext_secret))
+ .to eq("$pbkdf2-sha512$20000$$.c0G5XJVEew1TyeJk5TrkvB0VyOaTmDzPrsdNRED9vVeZlSyuG3G90F0ow23zUCiWKAVwmNnR/ceh.nJG3MdpQ") # rubocop:disable Layout/LineLength
+ end
+
+ context 'when hash_oauth_secrets is disabled' do
+ before do
+ stub_feature_flags(hash_oauth_secrets: false)
+ end
+
+ it 'returns a plaintext secret' do
+ expect(described_class.transform_secret(plaintext_secret)).to eq(plaintext_secret)
+ end
+ end
+ end
+
+ describe 'STRETCHES' do
+ it 'is 20_000' do
+ expect(described_class::STRETCHES).to eq(20_000)
+ end
+ end
+
+ describe 'SALT' do
+ it 'is empty' do
+ expect(described_class::SALT).to be_empty
+ end
+ end
+
+ describe '.secret_matches?' do
+ it "match by hashing the input if the stored value is hashed" do
+ stub_feature_flags(hash_oauth_secrets: false)
+ plain_secret = 'plain_secret'
+ stored_value = '$pbkdf2-sha512$20000$$/BwQRdwSpL16xkQhstavh7nvA5avCP7.4n9LLKe9AupgJDeA7M5xOAvG3N3E5XbRyGWWBbbr.BsojPVWzd1Sqg' # rubocop:disable Layout/LineLength
+ expect(described_class.secret_matches?(plain_secret, stored_value)).to be true
+ end
+ end
+end
diff --git a/spec/lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512_spec.rb b/spec/lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512_spec.rb
new file mode 100644
index 00000000000..c73744cd481
--- /dev/null
+++ b/spec/lib/gitlab/doorkeeper_secret_storing/token/pbkdf2_sha512_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::DoorkeeperSecretStoring::Token::Pbkdf2Sha512 do
+ describe '.transform_secret' do
+ let(:plaintext_token) { 'CzOBzBfU9F-HvsqfTaTXF4ivuuxYZuv3BoAK4pnvmyw' }
+
+ it 'generates a PBKDF2+SHA512 hashed value in the correct format' do
+ expect(described_class.transform_secret(plaintext_token))
+ .to eq("$pbkdf2-sha512$20000$$.c0G5XJVEew1TyeJk5TrkvB0VyOaTmDzPrsdNRED9vVeZlSyuG3G90F0ow23zUCiWKAVwmNnR/ceh.nJG3MdpQ") # rubocop:disable Layout/LineLength
+ end
+
+ context 'when hash_oauth_tokens is disabled' do
+ before do
+ stub_feature_flags(hash_oauth_tokens: false)
+ end
+
+ it 'returns a plaintext token' do
+ expect(described_class.transform_secret(plaintext_token)).to eq(plaintext_token)
+ end
+ end
+ end
+
+ describe 'STRETCHES' do
+ it 'is 20_000' do
+ expect(described_class::STRETCHES).to eq(20_000)
+ end
+ end
+
+ describe 'SALT' do
+ it 'is empty' do
+ expect(described_class::SALT).to be_empty
+ end
+ end
+end
diff --git a/spec/lib/gitlab/encoding_helper_spec.rb b/spec/lib/gitlab/encoding_helper_spec.rb
index b0c67cdafe1..690396d4dbc 100644
--- a/spec/lib/gitlab/encoding_helper_spec.rb
+++ b/spec/lib/gitlab/encoding_helper_spec.rb
@@ -98,6 +98,36 @@ RSpec.describe Gitlab::EncodingHelper do
end
end
+ describe '#encode_utf8_with_escaping!' do
+ where(:input, :expected) do
+ "abcd" | "abcd"
+ "DzDzDz" | "DzDzDz"
+ "\xC7\xB2\xC7DzDzDz" | "Dz%C7DzDzDz"
+ "ðŸ¤ðŸ¤ðŸ¤ðŸ¤\xF0\x9F\x90" | "ðŸ¤ðŸ¤ðŸ¤ðŸ¤%F0%9F%90"
+ "\xD0\x9F\xD1\x80 \x90" | "Пр %90"
+ "\x41" | "A"
+ end
+
+ with_them do
+ it 'escapes invalid UTF-8' do
+ expect(ext_class.encode_utf8_with_escaping!(input.dup.force_encoding(Encoding::ASCII_8BIT))).to eq(expected)
+ expect(ext_class.encode_utf8_with_escaping!(input)).to eq(expected)
+ end
+ end
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(escape_gitaly_refs: false)
+ end
+
+ it 'uses #encode! method' do
+ expect(ext_class).to receive(:encode!).with('String')
+
+ ext_class.encode_utf8_with_escaping!('String')
+ end
+ end
+ end
+
describe '#encode_utf8' do
[
["nil", nil, nil],
diff --git a/spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb b/spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb
index 210829056c8..c9b632b50e1 100644
--- a/spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb
+++ b/spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb
@@ -38,10 +38,10 @@ RSpec.describe Gitlab::ErrorTracking::Processor::ContextPayloadProcessor do
expect(result_hash[:tags])
.to include(priority: 'high',
- locale: 'en',
- program: 'test',
- feature_category: 'feature_a',
- correlation_id: 'cid')
+ locale: 'en',
+ program: 'test',
+ feature_category: 'feature_a',
+ correlation_id: 'cid')
expect(result_hash[:extra])
.to include(some_info: 'info',
diff --git a/spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb b/spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb
index 577d59798da..3d23249d00d 100644
--- a/spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb
+++ b/spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_spec.rb
@@ -52,7 +52,7 @@ RSpec.describe Gitlab::ErrorTracking::StackTraceHighlightDecorator do
'function' => 'print',
'lineNo' => 3,
'filename' => 'hello_world.php',
- 'context' => [
+ 'context' => [
[1, '<span id="LC1" class="line" lang="hack"><span class="c1">// PHP/Hack example</span></span>'],
[2, '<span id="LC1" class="line" lang="hack"><span class="cp">&lt;?php</span></span>'],
[3, '<span id="LC1" class="line" lang="hack"><span class="k">echo</span> <span class="s1">\'Hello, World!\'</span><span class="p">;</span></span>']
diff --git a/spec/lib/gitlab/etag_caching/middleware_spec.rb b/spec/lib/gitlab/etag_caching/middleware_spec.rb
index 8228f95dd5e..da5eaf2e4ab 100644
--- a/spec/lib/gitlab/etag_caching/middleware_spec.rb
+++ b/spec/lib/gitlab/etag_caching/middleware_spec.rb
@@ -119,11 +119,11 @@ RSpec.describe Gitlab::EtagCaching::Middleware, :clean_gitlab_redis_shared_state
let(:expected_items) do
{
etag_route: endpoint,
- params: {},
- format: :html,
- method: 'GET',
- path: enabled_path,
- status: status_code
+ params: {},
+ format: :html,
+ method: 'GET',
+ path: enabled_path,
+ status: status_code
}
end
diff --git a/spec/lib/gitlab/etag_caching/router/graphql_spec.rb b/spec/lib/gitlab/etag_caching/router/graphql_spec.rb
index 9a6787e3640..792f02f8cda 100644
--- a/spec/lib/gitlab/etag_caching/router/graphql_spec.rb
+++ b/spec/lib/gitlab/etag_caching/router/graphql_spec.rb
@@ -21,7 +21,7 @@ RSpec.describe Gitlab::EtagCaching::Router::Graphql do
def match_route(path, header)
described_class.match(
double(path_info: path,
- headers: { 'X-GITLAB-GRAPHQL-RESOURCE-ETAG' => header }))
+ headers: { 'X-GITLAB-GRAPHQL-RESOURCE-ETAG' => header }))
end
describe '.cache_key' do
diff --git a/spec/lib/gitlab/experimentation/group_types_spec.rb b/spec/lib/gitlab/experimentation/group_types_spec.rb
index 599ad08f706..2b118d76fa4 100644
--- a/spec/lib/gitlab/experimentation/group_types_spec.rb
+++ b/spec/lib/gitlab/experimentation/group_types_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Experimentation::GroupTypes do
it 'defines a GROUP_CONTROL constant' do
diff --git a/spec/lib/gitlab/file_detector_spec.rb b/spec/lib/gitlab/file_detector_spec.rb
index 8c0c56ea2c3..208acf28cc4 100644
--- a/spec/lib/gitlab/file_detector_spec.rb
+++ b/spec/lib/gitlab/file_detector_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::FileDetector do
describe '.types_in_paths' do
diff --git a/spec/lib/gitlab/file_markdown_link_builder_spec.rb b/spec/lib/gitlab/file_markdown_link_builder_spec.rb
index d684beaaaca..54dfde9fc45 100644
--- a/spec/lib/gitlab/file_markdown_link_builder_spec.rb
+++ b/spec/lib/gitlab/file_markdown_link_builder_spec.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::FileMarkdownLinkBuilder do
let(:custom_class) do
diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
index 763e6f1b5f4..a16f96a7d11 100644
--- a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe Gitlab::Gfm::UploadsRewriter do
end
let(:text) do
- "Text and #{image_uploader.markdown_link} and #{zip_uploader.markdown_link}"
+ "Text and #{image_uploader.markdown_link} and #{zip_uploader.markdown_link}".freeze # rubocop:disable Style/RedundantFreeze
end
def referenced_files(text, project)
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index 0da7aa7dad0..d35d288050a 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -2,11 +2,9 @@
require "spec_helper"
-RSpec.describe Gitlab::Git::Blob, :seed_helper do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
- let(:rugged) do
- Rugged::Repository.new(File.join(TestEnv.repos_path, TEST_REPO_PATH))
- end
+RSpec.describe Gitlab::Git::Blob do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:repository) { project.repository.raw }
describe 'initialize' do
let(:blob) { Gitlab::Git::Blob.new(name: 'test') }
@@ -44,7 +42,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
shared_examples '.find' do
context 'nil path' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, nil) }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], nil) }
it { expect(blob).to eq(nil) }
end
@@ -56,30 +54,30 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
end
context 'blank path' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, '') }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], '') }
it { expect(blob).to eq(nil) }
end
context 'file in subdir' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "files/ruby/popen.rb") }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "files/ruby/popen.rb") }
it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) }
it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) }
it { expect(blob.path).to eq("files/ruby/popen.rb") }
- it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
+ it { expect(blob.commit_id).to eq(TestEnv::BRANCH_SHA['master']) }
it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) }
it { expect(blob.size).to eq(669) }
it { expect(blob.mode).to eq("100644") }
end
context 'file in root' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, ".gitignore") }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], ".gitignore") }
it { expect(blob.id).to eq("dfaa3f97ca337e20154a98ac9d0be76ddd1fcc82") }
it { expect(blob.name).to eq(".gitignore") }
it { expect(blob.path).to eq(".gitignore") }
- it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
+ it { expect(blob.commit_id).to eq(TestEnv::BRANCH_SHA['master']) }
it { expect(blob.data[0..10]).to eq("*.rbc\n*.sas") }
it { expect(blob.size).to eq(241) }
it { expect(blob.mode).to eq("100644") }
@@ -87,25 +85,25 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
end
context 'file in root with leading slash' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "/.gitignore") }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "/.gitignore") }
it { expect(blob.id).to eq("dfaa3f97ca337e20154a98ac9d0be76ddd1fcc82") }
it { expect(blob.name).to eq(".gitignore") }
it { expect(blob.path).to eq(".gitignore") }
- it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
+ it { expect(blob.commit_id).to eq(TestEnv::BRANCH_SHA['master']) }
it { expect(blob.data[0..10]).to eq("*.rbc\n*.sas") }
it { expect(blob.size).to eq(241) }
it { expect(blob.mode).to eq("100644") }
end
context 'non-exist file' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "missing.rb") }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "missing.rb") }
it { expect(blob).to be_nil }
end
context 'six submodule' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, 'six') }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], 'six') }
it { expect(blob.id).to eq('409f37c4f05865e4fb208c771485f211a22c4c2d') }
it { expect(blob.data).to eq('') }
@@ -121,7 +119,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
end
context 'large file' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, 'files/images/6049019_460s.jpg') }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], 'files/images/6049019_460s.jpg') }
let(:blob_size) { 111803 }
let(:stub_limit) { 1000 }
@@ -159,10 +157,10 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
describe '.find with Rugged enabled', :enable_rugged do
it 'calls out to the Rugged implementation' do
allow_next_instance_of(Rugged) do |instance|
- allow(instance).to receive(:rev_parse).with(SeedRepo::Commit::ID).and_call_original
+ allow(instance).to receive(:rev_parse).with(TestEnv::BRANCH_SHA['master']).and_call_original
end
- described_class.find(repository, SeedRepo::Commit::ID, 'files/images/6049019_460s.jpg')
+ described_class.find(repository, TestEnv::BRANCH_SHA['master'], 'files/images/6049019_460s.jpg')
end
it_behaves_like '.find'
@@ -177,40 +175,13 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
it { expect(raw_blob.size).to eq(669) }
it { expect(raw_blob.truncated?).to be_falsey }
it { expect(bad_blob).to be_nil }
-
- context 'large file' do
- it 'limits the size of a large file' do
- blob_size = Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE + 1
- buffer = Array.new(blob_size, 0)
- rugged_blob = Rugged::Blob.from_buffer(rugged, buffer.join(''))
- blob = Gitlab::Git::Blob.raw(repository, rugged_blob)
-
- expect(blob.size).to eq(blob_size)
- expect(blob.loaded_size).to eq(Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
- expect(blob.data.length).to eq(Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE)
- expect(blob.truncated?).to be_truthy
-
- blob.load_all_data!(repository)
- expect(blob.loaded_size).to eq(blob_size)
- end
- end
-
- context 'when sha references a tree' do
- it 'returns nil' do
- tree = rugged.rev_parse('master^{tree}')
-
- blob = Gitlab::Git::Blob.raw(repository, tree.oid)
-
- expect(blob).to be_nil
- end
- end
end
describe '.batch' do
let(:blob_references) do
[
- [SeedRepo::Commit::ID, "files/ruby/popen.rb"],
- [SeedRepo::Commit::ID, 'six']
+ [TestEnv::BRANCH_SHA['master'], "files/ruby/popen.rb"],
+ [TestEnv::BRANCH_SHA['master'], 'six']
]
end
@@ -224,7 +195,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
it { expect(blob.id).to eq(SeedRepo::RubyBlob::ID) }
it { expect(blob.name).to eq(SeedRepo::RubyBlob::NAME) }
it { expect(blob.path).to eq("files/ruby/popen.rb") }
- it { expect(blob.commit_id).to eq(SeedRepo::Commit::ID) }
+ it { expect(blob.commit_id).to eq(TestEnv::BRANCH_SHA['master']) }
it { expect(blob.data[0..10]).to eq(SeedRepo::RubyBlob::CONTENT[0..10]) }
it { expect(blob.size).to eq(669) }
it { expect(blob.mode).to eq("100644") }
@@ -273,21 +244,21 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
context 'when large number of blobs requested' do
let(:first_batch) do
[
- [SeedRepo::Commit::ID, 'files/ruby/popen.rb'],
- [SeedRepo::Commit::ID, 'six']
+ [TestEnv::BRANCH_SHA['master'], 'files/ruby/popen.rb'],
+ [TestEnv::BRANCH_SHA['master'], 'six']
]
end
let(:second_batch) do
[
- [SeedRepo::Commit::ID, 'some'],
- [SeedRepo::Commit::ID, 'other']
+ [TestEnv::BRANCH_SHA['master'], 'some'],
+ [TestEnv::BRANCH_SHA['master'], 'other']
]
end
let(:third_batch) do
[
- [SeedRepo::Commit::ID, 'files']
+ [TestEnv::BRANCH_SHA['master'], 'files']
]
end
@@ -315,8 +286,8 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
describe '.batch_metadata' do
let(:blob_references) do
[
- [SeedRepo::Commit::ID, "files/ruby/popen.rb"],
- [SeedRepo::Commit::ID, 'six']
+ [TestEnv::BRANCH_SHA['master'], "files/ruby/popen.rb"],
+ [TestEnv::BRANCH_SHA['master'], 'six']
]
end
@@ -333,8 +304,6 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
end
describe '.batch_lfs_pointers' do
- let(:tree_object) { rugged.rev_parse('master^{tree}') }
-
let(:non_lfs_blob) do
Gitlab::Git::Blob.find(
repository,
@@ -346,8 +315,8 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
let(:lfs_blob) do
Gitlab::Git::Blob.find(
repository,
- '33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
- 'files/lfs/image.jpg'
+ TestEnv::BRANCH_SHA['master'],
+ 'files/lfs/lfs_object.iso'
)
end
@@ -374,12 +343,6 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
expect(blobs_2).to eq([])
end
- it 'silently ignores tree objects' do
- blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
-
- expect(blobs).to eq([])
- end
-
it 'silently ignores non lfs objects' do
blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
@@ -398,7 +361,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
describe 'encoding', :aggregate_failures do
context 'file with russian text' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "encoding/russian.rb") }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "encoding/russian.rb") }
it 'has the correct blob attributes' do
expect(blob.name).to eq("russian.rb")
@@ -412,7 +375,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
end
context 'file with Japanese text' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "encoding/テスト.txt") }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "encoding/テスト.txt") }
it 'has the correct blob attributes' do
expect(blob.name).to eq("テスト.txt")
@@ -424,12 +387,12 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
end
context 'file with ISO-8859 text' do
- let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::LastCommit::ID, "encoding/iso8859.txt") }
+ let(:blob) { Gitlab::Git::Blob.find(repository, TestEnv::BRANCH_SHA['master'], "encoding/iso8859.txt") }
it 'has the correct blob attributes' do
expect(blob.name).to eq("iso8859.txt")
- expect(blob.loaded_size).to eq(4)
- expect(blob.size).to eq(4)
+ expect(blob.loaded_size).to eq(3)
+ expect(blob.size).to eq(3)
expect(blob.mode).to eq("100644")
expect(blob.truncated?).to be_falsey
end
@@ -441,7 +404,7 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
let(:blob) do
Gitlab::Git::Blob.find(
repository,
- 'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
+ TestEnv::BRANCH_SHA['master'],
'files/ruby/regex.rb'
)
end
@@ -456,14 +419,14 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
let(:blob) do
Gitlab::Git::Blob.find(
repository,
- 'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
+ TestEnv::BRANCH_SHA['with-executables'],
'files/executables/ls'
)
end
it { expect(blob.name).to eq('ls') }
it { expect(blob.path).to eq('files/executables/ls') }
- it { expect(blob.size).to eq(110080) }
+ it { expect(blob.size).to eq(23) }
it { expect(blob.mode).to eq("100755") }
end
@@ -471,29 +434,14 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
let(:blob) do
Gitlab::Git::Blob.find(
repository,
- 'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
- 'files/links/ruby-style-guide.md'
+ '88ce9520c07b7067f589b7f83a30b6250883115c',
+ 'symlink'
)
end
- it { expect(blob.name).to eq('ruby-style-guide.md') }
- it { expect(blob.path).to eq('files/links/ruby-style-guide.md') }
- it { expect(blob.size).to eq(31) }
- it { expect(blob.mode).to eq("120000") }
- end
-
- context 'file symlink to binary' do
- let(:blob) do
- Gitlab::Git::Blob.find(
- repository,
- 'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6',
- 'files/links/touch'
- )
- end
-
- it { expect(blob.name).to eq('touch') }
- it { expect(blob.path).to eq('files/links/touch') }
- it { expect(blob.size).to eq(20) }
+ it { expect(blob.name).to eq('symlink') }
+ it { expect(blob.path).to eq('symlink') }
+ it { expect(blob.size).to eq(6) }
it { expect(blob.mode).to eq("120000") }
end
end
@@ -503,79 +451,20 @@ RSpec.describe Gitlab::Git::Blob, :seed_helper do
let(:blob) do
Gitlab::Git::Blob.find(
repository,
- '33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
- 'files/lfs/image.jpg'
+ TestEnv::BRANCH_SHA['png-lfs'],
+ 'files/images/emoji.png'
)
end
it { expect(blob.lfs_pointer?).to eq(true) }
- it { expect(blob.lfs_oid).to eq("4206f951d2691c78aac4c0ce9f2b23580b2c92cdcc4336e1028742c0274938e0") }
- it { expect(blob.lfs_size).to eq(19548) }
- it { expect(blob.id).to eq("f4d76af13003d1106be7ac8c5a2a3d37ddf32c2a") }
- it { expect(blob.name).to eq("image.jpg") }
- it { expect(blob.path).to eq("files/lfs/image.jpg") }
- it { expect(blob.size).to eq(130) }
+ it { expect(blob.lfs_oid).to eq("96f74c6fe7a2979eefb9ec74a5dfc6888fb25543cf99b77586b79afea1da6f97") }
+ it { expect(blob.lfs_size).to eq(1219696) }
+ it { expect(blob.id).to eq("ff0ab3afd1616ff78d0331865d922df103b64cf0") }
+ it { expect(blob.name).to eq("emoji.png") }
+ it { expect(blob.path).to eq("files/images/emoji.png") }
+ it { expect(blob.size).to eq(132) }
it { expect(blob.mode).to eq("100644") }
end
-
- describe 'file an invalid lfs pointer' do
- context 'with correct version header but incorrect size and oid' do
- let(:blob) do
- Gitlab::Git::Blob.find(
- repository,
- '33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
- 'files/lfs/archive-invalid.tar'
- )
- end
-
- it { expect(blob.lfs_pointer?).to eq(false) }
- it { expect(blob.lfs_oid).to eq(nil) }
- it { expect(blob.lfs_size).to eq(nil) }
- it { expect(blob.id).to eq("f8a898db217a5a85ed8b3d25b34c1df1d1094c46") }
- it { expect(blob.name).to eq("archive-invalid.tar") }
- it { expect(blob.path).to eq("files/lfs/archive-invalid.tar") }
- it { expect(blob.size).to eq(43) }
- it { expect(blob.mode).to eq("100644") }
- end
-
- context 'with correct version header and size but incorrect size and oid' do
- let(:blob) do
- Gitlab::Git::Blob.find(
- repository,
- '33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
- 'files/lfs/picture-invalid.png'
- )
- end
-
- it { expect(blob.lfs_pointer?).to eq(false) }
- it { expect(blob.lfs_oid).to eq(nil) }
- it { expect(blob.lfs_size).to eq(1575078) }
- it { expect(blob.id).to eq("5ae35296e1f95c1ef9feda1241477ed29a448572") }
- it { expect(blob.name).to eq("picture-invalid.png") }
- it { expect(blob.path).to eq("files/lfs/picture-invalid.png") }
- it { expect(blob.size).to eq(57) }
- it { expect(blob.mode).to eq("100644") }
- end
-
- context 'with correct version header and size but invalid size and oid' do
- let(:blob) do
- Gitlab::Git::Blob.find(
- repository,
- '33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
- 'files/lfs/file-invalid.zip'
- )
- end
-
- it { expect(blob.lfs_pointer?).to eq(false) }
- it { expect(blob.lfs_oid).to eq(nil) }
- it { expect(blob.lfs_size).to eq(nil) }
- it { expect(blob.id).to eq("d831981bd876732b85a1bcc6cc01210c9f36248f") }
- it { expect(blob.name).to eq("file-invalid.zip") }
- it { expect(blob.path).to eq("files/lfs/file-invalid.zip") }
- it { expect(blob.size).to eq(60) }
- it { expect(blob.mode).to eq("100644") }
- end
- end
end
describe '#load_all_data!' do
diff --git a/spec/lib/gitlab/git/branch_spec.rb b/spec/lib/gitlab/git/branch_spec.rb
index feaa1f6595c..95cc833390f 100644
--- a/spec/lib/gitlab/git/branch_spec.rb
+++ b/spec/lib/gitlab/git/branch_spec.rb
@@ -111,7 +111,7 @@ RSpec.describe Gitlab::Git::Branch do
end
def create_commit
- repository.multi_action(
+ repository.commit_files(
user,
branch_name: 'HEAD',
message: 'commit message',
diff --git a/spec/lib/gitlab/git/changed_path_spec.rb b/spec/lib/gitlab/git/changed_path_spec.rb
index 93db107ad5c..ef51021ba4c 100644
--- a/spec/lib/gitlab/git/changed_path_spec.rb
+++ b/spec/lib/gitlab/git/changed_path_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require "spec_helper"
+require 'fast_spec_helper'
RSpec.describe Gitlab::Git::ChangedPath do
subject(:changed_path) { described_class.new(path: path, status: status) }
diff --git a/spec/lib/gitlab/git/changes_spec.rb b/spec/lib/gitlab/git/changes_spec.rb
index 310be7a3731..7cded9740ee 100644
--- a/spec/lib/gitlab/git/changes_spec.rb
+++ b/spec/lib/gitlab/git/changes_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Git::Changes do
let(:changes) { described_class.new }
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index 95b49186d0f..d873151421d 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -2,8 +2,8 @@
require "spec_helper"
-RSpec.describe Gitlab::Git::Commit, :seed_helper do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
+RSpec.describe Gitlab::Git::Commit do
+ let(:repository) { create(:project, :repository).repository.raw }
let(:commit) { described_class.find(repository, SeedRepo::Commit::ID) }
describe "Commit info from gitaly commit" do
@@ -121,14 +121,6 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
it "returns nil for id containing NULL" do
expect(described_class.find(repository, "HE\x00AD")).to be_nil
end
-
- context 'with broken repo' do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_BROKEN_REPO_PATH, '', 'group/project') }
-
- it 'returns nil' do
- expect(described_class.find(repository, SeedRepo::Commit::ID)).to be_nil
- end
- end
end
describe '.find with Gitaly enabled' do
@@ -154,7 +146,7 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
describe '#id' do
subject { super().id }
- it { is_expected.to eq(SeedRepo::LastCommit::ID) }
+ it { is_expected.to eq(TestEnv::BRANCH_SHA['master']) }
end
end
@@ -223,7 +215,7 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
expect(subject.size).to eq(10)
end
- it { is_expected.to include(SeedRepo::EmptyCommit::ID) }
+ it { is_expected.to include(TestEnv::BRANCH_SHA['master']) }
end
context 'path is nil' do
@@ -242,28 +234,7 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
expect(subject.size).to eq(10)
end
- it { is_expected.to include(SeedRepo::EmptyCommit::ID) }
- end
-
- context 'ref is branch name' do
- subject do
- commits = described_class.where(
- repo: repository,
- ref: 'master',
- path: 'files',
- limit: 3,
- offset: 1
- )
-
- commits.map { |c| c.id }
- end
-
- it 'has 3 elements' do
- expect(subject.size).to eq(3)
- end
-
- it { is_expected.to include("d14d6c0abdd253381df51a723d58691b2ee1ab08") }
- it { is_expected.not_to include("eb49186cfa5c4338011f5f590fac11bd66c5c631") }
+ it { is_expected.to include(TestEnv::BRANCH_SHA['master']) }
end
context 'ref is commit id' do
@@ -323,13 +294,12 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
context 'requesting a commit range' do
let(:from) { 'v1.0.0' }
- let(:to) { 'v1.2.0' }
+ let(:to) { 'v1.1.0' }
let(:commits_in_range) do
%w[
570e7b2abdd848b95f2f578043fc23bd6f6fd24d
5937ac0a7beb003549fc5fd26fc247adbce4a52e
- eb49186cfa5c4338011f5f590fac11bd66c5c631
]
end
@@ -338,9 +308,9 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
end
context 'limited' do
- let(:limit) { 2 }
+ let(:limit) { 1 }
- it { expect(commit_ids).to eq(commits_in_range.last(2)) }
+ it { expect(commit_ids).to eq(commits_in_range.last(1)) }
end
end
end
@@ -383,16 +353,8 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
commits.map(&:id)
end
- it 'has 34 elements' do
- expect(subject.size).to eq(34)
- end
-
- it 'includes the expected commits' do
- expect(subject).to include(
- SeedRepo::Commit::ID,
- SeedRepo::Commit::PARENT_ID,
- SeedRepo::FirstCommit::ID
- )
+ it 'has maximum elements' do
+ expect(subject.size).to eq(50)
end
end
@@ -408,13 +370,13 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
commits.map(&:id)
end
- it 'has 24 elements' do
- expect(subject.size).to eq(24)
+ it 'has 36 elements' do
+ expect(subject.size).to eq(36)
end
it 'includes the expected commits' do
expect(subject).to include(SeedRepo::Commit::ID, SeedRepo::FirstCommit::ID)
- expect(subject).not_to include(SeedRepo::LastCommit::ID)
+ expect(subject).not_to include(TestEnv::BRANCH_SHA['master'])
end
end
end
@@ -650,8 +612,8 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
subject { commit.ref_names(repository) }
- it 'has 2 element' do
- expect(subject.size).to eq(2)
+ it 'has 3 elements' do
+ expect(subject.size).to eq(3)
end
it { is_expected.to include("master") }
@@ -681,6 +643,8 @@ RSpec.describe Gitlab::Git::Commit, :seed_helper do
end
it 'gets messages in one batch', :request_store do
+ repository # preload repository so that the project factory does not pollute request counts
+
expect { subject.map(&:itself) }.to change { Gitlab::GitalyClient.get_request_count }.by(1)
end
end
diff --git a/spec/lib/gitlab/git/commit_stats_spec.rb b/spec/lib/gitlab/git/commit_stats_spec.rb
index 29d3909efec..81d9dda4b8f 100644
--- a/spec/lib/gitlab/git/commit_stats_spec.rb
+++ b/spec/lib/gitlab/git/commit_stats_spec.rb
@@ -2,17 +2,19 @@
require "spec_helper"
-RSpec.describe Gitlab::Git::CommitStats, :seed_helper do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
- let(:commit) { Gitlab::Git::Commit.find(repository, SeedRepo::Commit::ID) }
+RSpec.describe Gitlab::Git::CommitStats do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:repository) { project.repository.raw }
+
+ let(:commit) { Gitlab::Git::Commit.find(repository, TestEnv::BRANCH_SHA['feature']) }
def verify_stats!
stats = described_class.new(repository, commit)
expect(stats).to have_attributes(
- additions: eq(11),
- deletions: eq(6),
- total: eq(17)
+ additions: eq(5),
+ deletions: eq(0),
+ total: eq(5)
)
end
@@ -21,7 +23,7 @@ RSpec.describe Gitlab::Git::CommitStats, :seed_helper do
verify_stats!
- expect(Rails.cache.fetch("commit_stats:group/project:#{commit.id}")).to eq([11, 6])
+ expect(Rails.cache.fetch("commit_stats:#{repository.gl_project_path}:#{commit.id}")).to eq([5, 0])
expect(repository.gitaly_commit_client).not_to receive(:commit_stats)
diff --git a/spec/lib/gitlab/git/compare_spec.rb b/spec/lib/gitlab/git/compare_spec.rb
index 51043355ede..e8c683cf8aa 100644
--- a/spec/lib/gitlab/git/compare_spec.rb
+++ b/spec/lib/gitlab/git/compare_spec.rb
@@ -2,8 +2,9 @@
require "spec_helper"
-RSpec.describe Gitlab::Git::Compare, :seed_helper do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
+RSpec.describe Gitlab::Git::Compare do
+ let_it_be(:repository) { create(:project, :repository).repository.raw }
+
let(:compare) { Gitlab::Git::Compare.new(repository, SeedRepo::BigCommit::ID, SeedRepo::Commit::ID, straight: false) }
let(:compare_straight) { Gitlab::Git::Compare.new(repository, SeedRepo::BigCommit::ID, SeedRepo::Commit::ID, straight: true) }
diff --git a/spec/lib/gitlab/git/conflict/file_spec.rb b/spec/lib/gitlab/git/conflict/file_spec.rb
index 6eb7a7e394e..fb1bec0a554 100644
--- a/spec/lib/gitlab/git/conflict/file_spec.rb
+++ b/spec/lib/gitlab/git/conflict/file_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Git::Conflict::File do
let(:conflict) { { ancestor: { path: 'ancestor' }, theirs: { path: 'foo', mode: 33188 }, ours: { path: 'foo', mode: 33188 } } }
diff --git a/spec/lib/gitlab/git/conflict/parser_spec.rb b/spec/lib/gitlab/git/conflict/parser_spec.rb
index 7d81af92412..67f288e0299 100644
--- a/spec/lib/gitlab/git/conflict/parser_spec.rb
+++ b/spec/lib/gitlab/git/conflict/parser_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Git::Conflict::Parser do
describe '.parse' do
diff --git a/spec/lib/gitlab/git/cross_repo_comparer_spec.rb b/spec/lib/gitlab/git/cross_repo_comparer_spec.rb
index 1c49486b7b1..7888e224d59 100644
--- a/spec/lib/gitlab/git/cross_repo_comparer_spec.rb
+++ b/spec/lib/gitlab/git/cross_repo_comparer_spec.rb
@@ -110,7 +110,7 @@ RSpec.describe Gitlab::Git::CrossRepoComparer do
def create_commit(user, repo, branch)
action = { action: :create, file_path: '/FILE', content: 'content' }
- result = repo.multi_action(user, branch_name: branch, message: 'Commit', actions: [action])
+ result = repo.commit_files(user, branch_name: branch, message: 'Commit', actions: [action])
result.newrev
end
diff --git a/spec/lib/gitlab/git/diff_collection_spec.rb b/spec/lib/gitlab/git/diff_collection_spec.rb
index 0e3e92e03cf..7fa5bd8a92b 100644
--- a/spec/lib/gitlab/git/diff_collection_spec.rb
+++ b/spec/lib/gitlab/git/diff_collection_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Git::DiffCollection, :seed_helper do
+RSpec.describe Gitlab::Git::DiffCollection do
before do
stub_const('MutatingConstantIterator', Class.new)
diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb
index 2c931a999f1..6745c700b92 100644
--- a/spec/lib/gitlab/git/diff_spec.rb
+++ b/spec/lib/gitlab/git/diff_spec.rb
@@ -2,8 +2,10 @@
require "spec_helper"
-RSpec.describe Gitlab::Git::Diff, :seed_helper do
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
+RSpec.describe Gitlab::Git::Diff do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:repository) { project.repository }
+
let(:gitaly_diff) do
Gitlab::GitalyClient::Diff.new(
from_path: '.gitmodules',
@@ -190,16 +192,6 @@ EOT
expect(binary_diff(project).diff).not_to be_empty
end
end
-
- context 'when convert_diff_to_utf8_with_replacement_symbol feature flag is disabled' do
- before do
- stub_feature_flags(convert_diff_to_utf8_with_replacement_symbol: false)
- end
-
- it 'will not try to convert invalid characters' do
- expect(Gitlab::EncodingHelper).not_to receive(:encode_utf8_with_replacement_character?)
- end
- end
end
context 'when replace_invalid_utf8_chars is false' do
@@ -218,7 +210,7 @@ EOT
let(:diffs) { described_class.between(repository, 'feature', 'master', options) }
it 'has the correct size' do
- expect(diffs.size).to eq(24)
+ expect(diffs.size).to eq(21)
end
context 'diff' do
diff --git a/spec/lib/gitlab/git/gitmodules_parser_spec.rb b/spec/lib/gitlab/git/gitmodules_parser_spec.rb
index 0e386c7f3d1..33268b4edcb 100644
--- a/spec/lib/gitlab/git/gitmodules_parser_spec.rb
+++ b/spec/lib/gitlab/git/gitmodules_parser_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Git::GitmodulesParser do
it 'parses a .gitmodules file correctly' do
diff --git a/spec/lib/gitlab/git/lfs_pointer_file_spec.rb b/spec/lib/gitlab/git/lfs_pointer_file_spec.rb
index f45c7cccca0..b210c86c3d1 100644
--- a/spec/lib/gitlab/git/lfs_pointer_file_spec.rb
+++ b/spec/lib/gitlab/git/lfs_pointer_file_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Git::LfsPointerFile do
let(:data) { "1234\n" }
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index a1fb8b70bd7..9a87911b6e8 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, :seed_helper do
+RSpec.describe Gitlab::Git::Repository do
include Gitlab::EncodingHelper
include RepoHelpers
using RSpec::Parameterized::TableSyntax
@@ -21,13 +21,11 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
end
- let(:mutable_repository) { Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '', 'group/project') }
- let(:mutable_repository_path) { File.join(TestEnv.repos_path, mutable_repository.relative_path) }
- let(:mutable_repository_rugged) { Rugged::Repository.new(mutable_repository_path) }
- let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
- let(:repository_path) { File.join(TestEnv.repos_path, repository.relative_path) }
- let(:repository_rugged) { Rugged::Repository.new(repository_path) }
- let(:storage_path) { TestEnv.repos_path }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:repository) { project.repository.raw }
+
+ let(:mutable_project) { create(:project, :repository) }
+ let(:mutable_repository) { mutable_project.repository.raw }
let(:user) { build(:user) }
describe "Respond to" do
@@ -61,8 +59,8 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#branch_names' do
subject { repository.branch_names }
- it 'has SeedRepo::Repo::BRANCHES.size elements' do
- expect(subject.size).to eq(SeedRepo::Repo::BRANCHES.size)
+ it 'has TestRepo::BRANCH_SHA.size elements' do
+ expect(subject.size).to eq(TestEnv::BRANCH_SHA.size)
end
it 'returns UTF-8' do
@@ -85,8 +83,8 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
it { is_expected.to be_kind_of Array }
- it 'has SeedRepo::Repo::TAGS.size elements' do
- expect(subject.size).to eq(SeedRepo::Repo::TAGS.size)
+ it 'has some elements' do
+ expect(subject.size).to be >= 1
end
it 'returns UTF-8' do
@@ -96,63 +94,24 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#last' do
subject { super().last }
- it { is_expected.to eq("v1.2.1") }
+ it { is_expected.to eq("v1.1.1") }
end
+
it { is_expected.to include("v1.0.0") }
it { is_expected.not_to include("v5.0.0") }
- it 'gets the tag names from GitalyClient' do
- expect_any_instance_of(Gitlab::GitalyClient::RefService).to receive(:tag_names)
- subject
- end
-
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :tag_names
end
describe '#tags' do
subject { repository.tags }
- it 'gets tags from GitalyClient' do
- expect_next_instance_of(Gitlab::GitalyClient::RefService) do |service|
- expect(service).to receive(:tags)
- end
-
- subject
- end
-
- context 'with sorting option' do
- subject { repository.tags(sort_by: 'name_asc') }
-
- it 'gets tags from GitalyClient' do
- expect_next_instance_of(Gitlab::GitalyClient::RefService) do |service|
- expect(service).to receive(:tags).with(sort_by: 'name_asc', pagination_params: nil)
- end
-
- subject
- end
- end
-
- context 'with pagination option' do
- subject { repository.tags(pagination_params: { limit: 5, page_token: 'refs/tags/v1.0.0' }) }
-
- it 'gets tags from GitalyClient' do
- expect_next_instance_of(Gitlab::GitalyClient::RefService) do |service|
- expect(service).to receive(:tags).with(
- sort_by: nil,
- pagination_params: { limit: 5, page_token: 'refs/tags/v1.0.0' }
- )
- end
-
- subject
- end
- end
-
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :tags
end
describe '#archive_metadata' do
let(:storage_path) { '/tmp' }
- let(:cache_key) { File.join(repository.gl_repository, SeedRepo::LastCommit::ID) }
+ let(:cache_key) { File.join(repository.gl_repository, TestEnv::BRANCH_SHA['master']) }
let(:append_sha) { true }
let(:ref) { 'master' }
@@ -162,12 +121,12 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
let(:expected_extension) { 'tar.gz' }
let(:expected_filename) { "#{expected_prefix}.#{expected_extension}" }
let(:expected_path) { File.join(storage_path, cache_key, "@v2", expected_filename) }
- let(:expected_prefix) { "gitlab-git-test-#{ref}-#{SeedRepo::LastCommit::ID}" }
+ let(:expected_prefix) { "gitlab-git-test-#{ref}-#{TestEnv::BRANCH_SHA['master']}" }
subject(:metadata) { repository.archive_metadata(ref, storage_path, 'gitlab-git-test', format, append_sha: append_sha, path: path) }
it 'sets CommitId to the commit SHA' do
- expect(metadata['CommitId']).to eq(SeedRepo::LastCommit::ID)
+ expect(metadata['CommitId']).to start_with(TestEnv::BRANCH_SHA['master'])
end
it 'sets ArchivePrefix to the expected prefix' do
@@ -175,7 +134,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
it 'sets ArchivePath to the expected globally-unique path' do
- expect(expected_path).to include(File.join(repository.gl_repository, SeedRepo::LastCommit::ID))
+ expect(expected_path).to include(File.join(repository.gl_repository, TestEnv::BRANCH_SHA['master']))
expect(metadata['ArchivePath']).to eq(expected_path)
end
@@ -190,7 +149,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
context 'append_sha varies archive path and filename' do
where(:append_sha, :ref, :expected_prefix) do
- sha = SeedRepo::LastCommit::ID
+ sha = TestEnv::BRANCH_SHA['master']
true | 'master' | "gitlab-git-test-master-#{sha}"
true | sha | "gitlab-git-test-#{sha}-#{sha}"
@@ -224,13 +183,13 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#size' do
subject { repository.size }
- it { is_expected.to be < 2 }
+ it { is_expected.to be > 0 }
end
describe '#to_s' do
subject { repository.to_s }
- it { is_expected.to eq("<Gitlab::Git::Repository: group/project>") }
+ it { is_expected.to eq("<Gitlab::Git::Repository: #{project.full_path}>") }
end
describe '#object_directory_size' do
@@ -259,26 +218,25 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#first' do
subject { super().first }
- it { is_expected.to eq('feature') }
+ it { is_expected.to eq(TestEnv::BRANCH_SHA.keys.min) }
end
describe '#last' do
subject { super().last }
- it { is_expected.to eq('v1.2.1') }
+ it { is_expected.to eq('v1.1.1') }
end
end
describe '#submodule_url_for' do
- let(:ref) { 'master' }
+ let(:ref) { 'submodule_inside_folder' }
def submodule_url(path)
repository.submodule_url_for(ref, path)
end
it { expect(submodule_url('six')).to eq('git://github.com/randx/six.git') }
- it { expect(submodule_url('nested/six')).to eq('git://github.com/randx/six.git') }
- it { expect(submodule_url('deeper/nested/six')).to eq('git://github.com/randx/six.git') }
+ it { expect(submodule_url('test_inside_folder/another_folder/six')).to eq('git://github.com/randx/six.git') }
it { expect(submodule_url('invalid/path')).to eq(nil) }
context 'uncommitted submodule dir' do
@@ -288,7 +246,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
context 'tags' do
- let(:ref) { 'v1.2.1' }
+ let(:ref) { 'v1.1.1' }
it { expect(submodule_url('six')).to eq('git://github.com/randx/six.git') }
end
@@ -313,17 +271,15 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
urls = repository.submodule_urls_for(ref)
expect(urls).to eq({
- "deeper/nested/six" => "git://github.com/randx/six.git",
- "gitlab-grack" => "https://gitlab.com/gitlab-org/gitlab-grack.git",
- "gitlab-shell" => "https://github.com/gitlabhq/gitlab-shell.git",
- "nested/six" => "git://github.com/randx/six.git",
+ "gitlab-grack" => "https://gitlab.com/gitlab-org/gitlab-grack.git",
+ "gitlab-shell" => "https://github.com/gitlabhq/gitlab-shell.git",
"six" => "git://github.com/randx/six.git"
})
end
end
describe '#commit_count' do
- it { expect(repository.commit_count("master")).to eq(25) }
+ it { expect(repository.commit_count("master")).to eq(37) }
it { expect(repository.commit_count("feature")).to eq(9) }
it { expect(repository.commit_count("does-not-exist")).to eq(0) }
@@ -353,7 +309,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
repository.create_branch('right-branch')
left.times do |i|
- repository.multi_action(
+ repository.commit_files(
user,
branch_name: 'left-branch',
message: 'some more content for a',
@@ -366,7 +322,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
right.times do |i|
- repository.multi_action(
+ repository.commit_files(
user,
branch_name: 'right-branch',
message: 'some more content for b',
@@ -411,7 +367,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
repository.create_branch('right-branch')
left.times do |i|
- repository.multi_action(
+ repository.commit_files(
user,
branch_name: 'left-branch',
message: 'some more content for a',
@@ -424,7 +380,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
right.times do |i|
- repository.multi_action(
+ repository.commit_files(
user,
branch_name: 'right-branch',
message: 'some more content for b',
@@ -461,47 +417,32 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#has_local_branches?' do
context 'check for local branches' do
it { expect(repository.has_local_branches?).to eq(true) }
+ end
+ end
- context 'mutable' do
- let(:repository) { mutable_repository }
+ describe '#delete_branch' do
+ let(:repository) { mutable_repository }
- after do
- ensure_seeds
- end
+ it 'deletes a branch' do
+ expect(repository.find_branch('feature')).not_to be_nil
- it 'returns false when there are no branches' do
- # Sanity check
- expect(repository.has_local_branches?).to eq(true)
+ repository.delete_branch('feature')
- FileUtils.rm_rf(File.join(repository_path, 'packed-refs'))
- heads_dir = File.join(repository_path, 'refs/heads')
- FileUtils.rm_rf(heads_dir)
- FileUtils.mkdir_p(heads_dir)
+ expect(repository.find_branch('feature')).to be_nil
+ end
- repository.expire_has_local_branches_cache
- expect(repository.has_local_branches?).to eq(false)
- end
- end
+ it 'deletes a fully qualified branch' do
+ expect(repository.find_branch('feature')).not_to be_nil
- context 'memoizes the value' do
- it 'returns true' do
- expect(repository).to receive(:uncached_has_local_branches?).once.and_call_original
+ repository.delete_branch('refs/heads/feature')
- 2.times do
- expect(repository.has_local_branches?).to eq(true)
- end
- end
- end
+ expect(repository.find_branch('feature')).to be_nil
end
end
describe '#delete_refs' do
let(:repository) { mutable_repository }
- after do
- ensure_seeds
- end
-
it 'deletes the ref' do
repository.delete_refs('refs/heads/feature')
@@ -548,9 +489,8 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
subject { repository.refs_hash }
it "has as many entries as branches and tags" do
- expected_refs = SeedRepo::Repo::BRANCHES + SeedRepo::Repo::TAGS
# We flatten in case a commit is pointed at by more than one branch and/or tag
- expect(subject.values.flatten.size).to eq(expected_refs.size)
+ expect(subject.values.flatten.size).to be > 0
end
it 'has valid commit ids as keys' do
@@ -598,7 +538,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
before do
repository.create_branch(ref)
- repository.multi_action(
+ repository.commit_files(
user,
branch_name: ref,
message: 'committing something',
@@ -608,7 +548,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
content: content
}]
)
- repository.multi_action(
+ repository.commit_files(
user,
branch_name: ref,
message: 'committing something',
@@ -620,10 +560,6 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
)
end
- after do
- ensure_seeds
- end
-
subject do
repository.search_files_by_content(content, ref)
end
@@ -647,8 +583,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
let(:filter) { 'files\/.*\/.*\.rb' }
it 'returns matched files' do
- expect(result).to contain_exactly('files/links/regex.rb',
- 'files/ruby/popen.rb',
+ expect(result).to contain_exactly('files/ruby/popen.rb',
'files/ruby/regex.rb',
'files/ruby/version_info.rb')
end
@@ -673,6 +608,61 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
end
+ describe '#search_files_by_name' do
+ let(:ref) { 'master' }
+
+ subject(:result) { mutable_repository.search_files_by_name(query, ref) }
+
+ context 'when sending a valid name' do
+ let(:query) { 'files/ruby/popen.rb' }
+
+ it 'returns matched files' do
+ expect(result).to contain_exactly('files/ruby/popen.rb')
+ end
+ end
+
+ context 'when sending a name with space' do
+ let(:query) { 'file with space.md' }
+
+ before do
+ mutable_repository.commit_files(
+ user,
+ actions: [{ action: :create, file_path: "file with space.md", content: "Test content" }],
+ branch_name: ref, message: "Test"
+ )
+ end
+
+ it 'returns matched files' do
+ expect(result).to contain_exactly('file with space.md')
+ end
+ end
+
+ context 'when sending a name with special ASCII characters' do
+ let(:file_name) { 'Hello !@#$%^&*()' }
+ let(:query) { file_name }
+
+ before do
+ mutable_repository.commit_files(
+ user,
+ actions: [{ action: :create, file_path: file_name, content: "Test content" }],
+ branch_name: ref, message: "Test"
+ )
+ end
+
+ it 'returns matched files' do
+ expect(result).to contain_exactly(file_name)
+ end
+ end
+
+ context 'when sending a non-existing name' do
+ let(:query) { 'please do not exist.md' }
+
+ it 'raises error' do
+ expect(result).to eql([])
+ end
+ end
+ end
+
describe '#find_remote_root_ref' do
it 'gets the remote root ref from GitalyClient' do
expect_any_instance_of(Gitlab::GitalyClient::RemoteService)
@@ -720,7 +710,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
before do
# Add new commits so that there's a renamed file in the commit history
- @commit_with_old_name_id = repository.multi_action(
+ @commit_with_old_name_id = repository.commit_files(
user,
branch_name: repository.root_ref,
message: 'Update CHANGELOG',
@@ -730,7 +720,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
content: 'CHANGELOG'
}]
).newrev
- @rename_commit_id = repository.multi_action(
+ @rename_commit_id = repository.commit_files(
user,
branch_name: repository.root_ref,
message: 'Move CHANGELOG to encoding/',
@@ -741,7 +731,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
content: 'CHANGELOG'
}]
).newrev
- @commit_with_new_name_id = repository.multi_action(
+ @commit_with_new_name_id = repository.commit_files(
user,
branch_name: repository.root_ref,
message: 'Edit encoding/CHANGELOG',
@@ -755,7 +745,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
after do
# Erase our commits so other tests get the original repo
- repository.write_ref(repository.root_ref, SeedRepo::LastCommit::ID)
+ repository.write_ref(repository.root_ref, TestEnv::BRANCH_SHA['master'])
end
context "where 'follow' == true" do
@@ -908,16 +898,6 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
expect(log_commits).not_to include(commit_with_new_name)
end
end
-
- context "and 'path' includes a directory that used to be a file" do
- let(:log_commits) do
- repository.log(options.merge(ref: "refs/heads/fix-blob-path", path: "files/testdir/file.txt"))
- end
-
- it "returns a list of commits" do
- expect(log_commits.size).to eq(1)
- end
- end
end
context "where provides 'after' timestamp" do
@@ -981,7 +961,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
it 'returns a list of commits' do
commits = repository.log({ all: true, limit: 50 })
- expect(commits.size).to eq(37)
+ expect(commits.size).to eq(50)
end
end
end
@@ -992,7 +972,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
describe '#blobs' do
- let_it_be(:commit_oid) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' }
+ let_it_be(:commit_oid) { TestEnv::BRANCH_SHA['master'] }
shared_examples 'a blob enumeration' do
it 'enumerates blobs' do
@@ -1008,7 +988,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
context 'single revision' do
let(:revisions) { [commit_oid] }
- let(:expected_blobs) { 53 }
+ let(:expected_blobs) { 52 }
it_behaves_like 'a blob enumeration'
end
@@ -1038,48 +1018,31 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
it_behaves_like 'a blob enumeration'
end
-
- context 'partially blank revisions' do
- let(:revisions) { [::Gitlab::Git::BLANK_SHA, commit_oid] }
- let(:expected_blobs) { 53 }
-
- before do
- expect_next_instance_of(Gitlab::GitalyClient::BlobService) do |service|
- expect(service)
- .to receive(:list_blobs)
- .with([commit_oid], kind_of(Hash))
- .and_call_original
- end
- end
-
- it_behaves_like 'a blob enumeration'
- end
end
describe '#new_blobs' do
let(:repository) { mutable_repository }
- let(:repository_rugged) { mutable_repository_rugged }
- let(:blob) { create_blob('This is a new blob') }
- let(:commit) { create_commit('nested/new-blob.txt' => blob) }
-
- def create_blob(content)
- repository_rugged.write(content, :blob)
- end
+ let(:commit) { create_commit('nested/new-blob.txt' => 'This is a new blob') }
def create_commit(blobs)
- author = { name: 'Test User', email: 'mail@example.com', time: Time.now }
+ commit_result = repository.commit_files(
+ user,
+ branch_name: 'a-new-branch',
+ message: 'Add a file',
+ actions: blobs.map do |path, content|
+ {
+ action: :create,
+ file_path: path,
+ content: content
+ }
+ end
+ )
- index = repository_rugged.index
- blobs.each do |path, oid|
- index.add(path: path, oid: oid, mode: 0100644)
- end
+ # new_blobs only returns unreferenced blobs because it is used for hooks.
+ # Gitaly does not allow us to create loose objects via the RPC.
+ repository.delete_branch('a-new-branch')
- Rugged::Commit.create(repository_rugged,
- author: author,
- committer: author,
- message: "Message",
- parents: [],
- tree: index.write_tree(repository_rugged))
+ commit_result.newrev
end
subject { repository.new_blobs(newrevs).to_a }
@@ -1112,7 +1075,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
let(:newrevs) { commit }
let(:expected_newrevs) { ['--not', '--all', '--not', newrevs] }
let(:expected_blobs) do
- [have_attributes(class: Gitlab::Git::Blob, id: blob, path: 'nested/new-blob.txt', size: 18)]
+ [have_attributes(class: Gitlab::Git::Blob, id: an_instance_of(String), path: 'nested/new-blob.txt', size: 18)]
end
it_behaves_like '#new_blobs with revisions'
@@ -1122,20 +1085,19 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
let(:newrevs) { [commit] }
let(:expected_newrevs) { ['--not', '--all', '--not'] + newrevs }
let(:expected_blobs) do
- [have_attributes(class: Gitlab::Git::Blob, id: blob, path: 'nested/new-blob.txt', size: 18)]
+ [have_attributes(class: Gitlab::Git::Blob, id: an_instance_of(String), path: 'nested/new-blob.txt', size: 18)]
end
it_behaves_like '#new_blobs with revisions'
end
context 'with multiple revisions' do
- let(:another_blob) { create_blob('Another blob') }
- let(:newrevs) { [commit, create_commit('another_path.txt' => another_blob)] }
+ let(:newrevs) { [commit, create_commit('another_path.txt' => 'Another blob')] }
let(:expected_newrevs) { ['--not', '--all', '--not'] + newrevs.sort }
let(:expected_blobs) do
[
- have_attributes(class: Gitlab::Git::Blob, id: blob, path: 'nested/new-blob.txt', size: 18),
- have_attributes(class: Gitlab::Git::Blob, id: another_blob, path: 'another_path.txt', size: 12)
+ have_attributes(class: Gitlab::Git::Blob, id: an_instance_of(String), path: 'nested/new-blob.txt', size: 18),
+ have_attributes(class: Gitlab::Git::Blob, id: an_instance_of(String), path: 'another_path.txt', size: 12)
]
end
@@ -1147,7 +1109,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
let(:expected_newrevs) { ['--not', '--all', '--not', commit] }
let(:expected_blobs) do
[
- have_attributes(class: Gitlab::Git::Blob, id: blob, path: 'nested/new-blob.txt', size: 18)
+ have_attributes(class: Gitlab::Git::Blob, id: an_instance_of(String), path: 'nested/new-blob.txt', size: 18)
]
end
@@ -1159,7 +1121,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
let(:expected_newrevs) { ['--not', '--all', '--not', commit] }
let(:expected_blobs) do
[
- have_attributes(class: Gitlab::Git::Blob, id: blob, path: 'nested/new-blob.txt', size: 18)
+ have_attributes(class: Gitlab::Git::Blob, id: an_instance_of(String), path: 'nested/new-blob.txt', size: 18)
]
end
@@ -1212,14 +1174,22 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#new_commits' do
let(:repository) { mutable_repository }
let(:new_commit) do
- author = { name: 'Test User', email: 'mail@example.com', time: Time.now }
+ commit_result = repository.commit_files(
+ user,
+ branch_name: 'a-new-branch',
+ message: 'Message',
+ actions: [{
+ action: :create,
+ file_path: 'some_file.txt',
+ content: 'This is a file'
+ }]
+ )
+
+ # new_commits only returns unreferenced commits because it is used for
+ # hooks. Gitaly does not allow us to create loose objects via the RPC.
+ repository.delete_branch('a-new-branch')
- Rugged::Commit.create(repository_rugged,
- author: author,
- committer: author,
- message: "Message",
- parents: [],
- tree: "4b825dc642cb6eb9a060e54bf8d69288fbee4904")
+ commit_result.newrev
end
let(:expected_commits) { 1 }
@@ -1248,7 +1218,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#count_commits_between' do
subject { repository.count_commits_between('feature', 'master') }
- it { is_expected.to eq(17) }
+ it { is_expected.to eq(29) }
end
describe '#raw_changes_between' do
@@ -1275,26 +1245,26 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
end
- context 'with valid revs' do
- let(:old_rev) { 'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6' }
- let(:new_rev) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' }
+ context 'with valid revs', :aggregate_failures do
+ let(:old_rev) { TestEnv::BRANCH_SHA['feature'] }
+ let(:new_rev) { TestEnv::BRANCH_SHA['master'] }
it 'returns the changes' do
- expect(changes.size).to eq(9)
- expect(changes.first.operation).to eq(:modified)
- expect(changes.first.new_path).to eq('.gitmodules')
+ expect(changes.size).to eq(21)
+ expect(changes.first.operation).to eq(:deleted)
+ expect(changes.first.old_path).to eq('.DS_Store')
expect(changes.last.operation).to eq(:added)
- expect(changes.last.new_path).to eq('files/lfs/picture-invalid.png')
+ expect(changes.last.new_path).to eq('with space/README.md')
end
end
end
describe '#merge_base' do
where(:from, :to, :result) do
- '570e7b2abdd848b95f2f578043fc23bd6f6fd24d' | '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | '570e7b2abdd848b95f2f578043fc23bd6f6fd24d'
- '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | '570e7b2abdd848b95f2f578043fc23bd6f6fd24d' | '570e7b2abdd848b95f2f578043fc23bd6f6fd24d'
- '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | 'foobar' | nil
- 'foobar' | '40f4a7a617393735a95a0bb67b08385bc1e7c66d' | nil
+ 'master' | 'feature' | 'ae73cb07c9eeaf35924a10f713b364d32b2dd34f'
+ 'feature' | 'master' | 'ae73cb07c9eeaf35924a10f713b364d32b2dd34f'
+ 'master' | 'foobar' | nil
+ 'foobar' | 'master' | nil
end
with_them do
@@ -1308,7 +1278,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
it 'returns the number of commits after timestamp' do
options = { ref: 'master', after: Time.iso8601('2013-03-03T20:15:01+00:00') }
- expect(repository.count_commits(options)).to eq(25)
+ expect(repository.count_commits(options)).to eq(37)
end
end
@@ -1337,28 +1307,28 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
context 'with option :from and option :to' do
- it 'returns the number of commits ahead for fix-mode..fix-blob-path' do
- options = { from: 'fix-mode', to: 'fix-blob-path' }
+ it 'returns the number of commits ahead for master..feature' do
+ options = { from: 'master', to: 'feature' }
- expect(repository.count_commits(options)).to eq(2)
+ expect(repository.count_commits(options)).to eq(1)
end
- it 'returns the number of commits ahead for fix-blob-path..fix-mode' do
- options = { from: 'fix-blob-path', to: 'fix-mode' }
+ it 'returns the number of commits ahead for feature..master' do
+ options = { from: 'feature', to: 'master' }
- expect(repository.count_commits(options)).to eq(1)
+ expect(repository.count_commits(options)).to eq(29)
end
context 'with option :left_right' do
- it 'returns the number of commits for fix-mode...fix-blob-path' do
- options = { from: 'fix-mode', to: 'fix-blob-path', left_right: true }
+ it 'returns the number of commits for master..feature' do
+ options = { from: 'master', to: 'feature', left_right: true }
- expect(repository.count_commits(options)).to eq([1, 2])
+ expect(repository.count_commits(options)).to eq([29, 1])
end
context 'with max_count' do
- it 'returns the number of commits with path' do
- options = { from: 'fix-mode', to: 'fix-blob-path', left_right: true, max_count: 1 }
+ it 'returns the number of commits' do
+ options = { from: 'feature', to: 'master', left_right: true, max_count: 1 }
expect(repository.count_commits(options)).to eq([1, 1])
end
@@ -1378,7 +1348,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
it "returns the number of commits in the whole repository" do
options = { all: true }
- expect(repository.count_commits(options)).to eq(34)
+ expect(repository.count_commits(options)).to eq(314)
end
end
@@ -1416,10 +1386,6 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
repository.create_branch('local_branch')
end
- after do
- ensure_seeds
- end
-
it 'returns the local and remote branches' do
expect(subject.any? { |b| b.name == 'joe/remote_branch' }).to eq(true)
expect(subject.any? { |b| b.name == 'local_branch' }).to eq(true)
@@ -1431,7 +1397,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#branch_count' do
it 'returns the number of branches' do
- expect(repository.branch_count).to eq(11)
+ expect(repository.branch_count).to eq(TestEnv::BRANCH_SHA.size)
end
context 'with local and remote branches' do
@@ -1442,10 +1408,6 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
repository.create_branch('local_branch')
end
- after do
- ensure_seeds
- end
-
it 'returns the count of local branches' do
expect(repository.branch_count).to eq(repository.local_branches.count)
end
@@ -1488,21 +1450,16 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
context 'when no branch names are specified' do
+ let(:repository) { mutable_repository }
+
before do
repository.create_branch('identical')
end
- after do
- ensure_seeds
- end
-
it 'returns all merged branch names except for identical one' do
names = repository.merged_branch_names
- expect(names).to include('merge-test')
- expect(names).to include('fix-mode')
- expect(names).not_to include('feature')
- expect(names).not_to include('identical')
+ expect(names).to match_array(["'test'", "branch-merged", "flatten-dir", "improve/awesome", "merge-test"])
end
end
end
@@ -1556,24 +1513,15 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
describe '#find_changed_paths' do
- let(:commit_1) { 'fa1b1e6c004a68b7d8763b86455da9e6b23e36d6' }
- let(:commit_2) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' }
+ let(:commit_1) { TestEnv::BRANCH_SHA['with-executables'] }
+ let(:commit_2) { TestEnv::BRANCH_SHA['master'] }
let(:commit_3) { '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9' }
let(:commit_1_files) do
- [
- Gitlab::Git::ChangedPath.new(status: :ADDED, path: "files/executables/ls"),
- Gitlab::Git::ChangedPath.new(status: :ADDED, path: "files/executables/touch"),
- Gitlab::Git::ChangedPath.new(status: :ADDED, path: "files/links/regex.rb"),
- Gitlab::Git::ChangedPath.new(status: :ADDED, path: "files/links/ruby-style-guide.md"),
- Gitlab::Git::ChangedPath.new(status: :ADDED, path: "files/links/touch"),
- Gitlab::Git::ChangedPath.new(status: :MODIFIED, path: ".gitmodules"),
- Gitlab::Git::ChangedPath.new(status: :ADDED, path: "deeper/nested/six"),
- Gitlab::Git::ChangedPath.new(status: :ADDED, path: "nested/six")
- ]
+ [Gitlab::Git::ChangedPath.new(status: :ADDED, path: "files/executables/ls")]
end
let(:commit_2_files) do
- [Gitlab::Git::ChangedPath.new(status: :ADDED, path: "bin/executable")]
+ [Gitlab::Git::ChangedPath.new(status: :ADDED, path: "bar/branch-test.txt")]
end
let(:commit_3_files) do
@@ -1621,7 +1569,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
let(:not_existed_branch) { repository.ls_files("not_existed_branch") }
it "read every file paths of master branch" do
- expect(master_file_paths.length).to equal(40)
+ expect(master_file_paths.length).to equal(38)
end
it "reads full file paths of master branch" do
@@ -1646,11 +1594,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
describe "#copy_gitattributes" do
- let(:attributes_path) { File.join(SEED_STORAGE_PATH, TEST_REPO_PATH, 'info/attributes') }
-
- after do
- FileUtils.rm_rf(attributes_path) if Dir.exist?(attributes_path)
- end
+ let(:repository) { mutable_repository }
it "raises an error with invalid ref" do
expect { repository.copy_gitattributes("invalid") }.to raise_error(Gitlab::Git::Repository::InvalidRef)
@@ -1673,63 +1617,10 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
repository
end
end
-
- context "with no .gitattrbutes" do
- before do
- repository.copy_gitattributes("master")
- end
-
- it "does not have an info/attributes" do
- expect(File.exist?(attributes_path)).to be_falsey
- end
- end
-
- context "with .gitattrbutes" do
- before do
- repository.copy_gitattributes("gitattributes")
- end
-
- it "has an info/attributes" do
- expect(File.exist?(attributes_path)).to be_truthy
- end
-
- it "has the same content in info/attributes as .gitattributes" do
- contents = File.open(attributes_path, "rb") { |f| f.read }
- expect(contents).to eq("*.md binary\n")
- end
- end
-
- context "with updated .gitattrbutes" do
- before do
- repository.copy_gitattributes("gitattributes")
- repository.copy_gitattributes("gitattributes-updated")
- end
-
- it "has an info/attributes" do
- expect(File.exist?(attributes_path)).to be_truthy
- end
-
- it "has the updated content in info/attributes" do
- contents = File.read(attributes_path)
- expect(contents).to eq("*.txt binary\n")
- end
- end
-
- context "with no .gitattrbutes in HEAD but with previous info/attributes" do
- before do
- repository.copy_gitattributes("gitattributes")
- repository.copy_gitattributes("master")
- end
-
- it "does not have an info/attributes" do
- expect(File.exist?(attributes_path)).to be_falsey
- end
- end
end
describe '#gitattribute' do
- let(:project) { create(:project, :repository) }
- let(:repository) { project.repository }
+ let(:repository) { mutable_repository }
context 'with gitattributes' do
before do
@@ -1808,10 +1699,6 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
repository.create_branch('local_branch')
end
- after do
- ensure_seeds
- end
-
it 'returns the local branches' do
expect(repository.local_branches.any? { |branch| branch.name == 'remote_branch' }).to eq(false)
expect(repository.local_branches.any? { |branch| branch.name == 'local_branch' }).to eq(true)
@@ -1880,7 +1767,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#languages' do
it 'returns exactly the expected results' do
- languages = repository.languages('4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6')
+ languages = repository.languages(TestEnv::BRANCH_SHA['master'])
expect(languages).to match_array([
{ value: a_value_within(0.1).of(66.7), label: "Ruby", color: "#701516", highlight: "#701516" },
@@ -1918,18 +1805,15 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#fetch_source_branch!' do
let(:local_ref) { 'refs/merge-requests/1/head' }
+ let(:repository) { create(:project, :repository).repository.raw }
let(:source_repository) { mutable_repository }
- after do
- ensure_seeds
- end
-
context 'when the branch exists' do
context 'when the commit does not exist locally' do
let(:source_branch) { 'new-branch-for-fetch-source-branch' }
let!(:new_oid) do
- source_repository.multi_action(
+ source_repository.commit_files(
user,
branch_name: source_branch,
message: 'Add a file',
@@ -1949,14 +1833,14 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
context 'when the commit exists locally' do
let(:source_branch) { 'master' }
- let(:expected_oid) { SeedRepo::LastCommit::ID }
+ let(:expected_oid) { TestEnv::BRANCH_SHA['master'] }
it 'writes the ref' do
# Sanity check: the commit should already exist
expect(repository.commit(expected_oid)).not_to be_nil
expect(repository.fetch_source_branch!(source_repository, source_branch, local_ref)).to eq(true)
- expect(repository.commit(local_ref).sha).to eq(expected_oid)
+ expect(repository.commit(local_ref).sha).to start_with(expected_oid)
end
end
end
@@ -2012,9 +1896,9 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
it 'writes other refs' do
- repository.write_ref('refs/heads/feature', SeedRepo::Commit::ID)
+ repository.write_ref('refs/heads/feature', TestEnv::BRANCH_SHA['master'])
- expect(repository.commit('feature').sha).to eq(SeedRepo::Commit::ID)
+ expect(repository.commit('feature').sha).to start_with(TestEnv::BRANCH_SHA['master'])
end
end
@@ -2052,28 +1936,28 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
it 'returns nil for an empty repo' do
project = create(:project)
- expect(project.repository.refs_by_oid(oid: SeedRepo::Commit::ID, limit: 0)).to be_nil
+ expect(project.repository.refs_by_oid(oid: TestEnv::BRANCH_SHA['master'], limit: 0)).to be_nil
end
end
describe '#set_full_path' do
+ let(:full_path) { 'some/path' }
+
before do
- repository.set_full_path(full_path: repository_path)
+ repository.set_full_path(full_path: full_path)
end
- context 'is given a path' do
- it 'writes it to disk' do
- repository.set_full_path(full_path: "not-the/real-path.git")
+ it 'writes full_path to gitaly' do
+ repository.set_full_path(full_path: "not-the/real-path.git")
- expect(repository.full_path).to eq('not-the/real-path.git')
- end
+ expect(repository.full_path).to eq('not-the/real-path.git')
end
context 'it is given an empty path' do
it 'does not write it to disk' do
repository.set_full_path(full_path: "")
- expect(repository.full_path).to eq(repository_path)
+ expect(repository.full_path).to eq(full_path)
end
end
@@ -2145,10 +2029,6 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
repository.create_branch(target_branch, '6d394385cf567f80a8fd85055db1ab4c5295806f')
end
- after do
- ensure_seeds
- end
-
it 'can perform a merge' do
merge_commit_id = nil
result = repository.merge(user, source_sha, target_branch, 'Test merge') do |commit_id|
@@ -2185,10 +2065,6 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
repository.create_branch(target_branch, branch_head)
end
- after do
- ensure_seeds
- end
-
subject { repository.ff_merge(user, source_sha, target_branch) }
shared_examples '#ff_merge' do
@@ -2242,14 +2118,10 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
let(:repository) { mutable_repository }
before do
- repository.write_ref("refs/delete/a", "0b4bc9a49b562e85de7cc9e834518ea6828729b9")
- repository.write_ref("refs/also-delete/b", "12d65c8dd2b2676fa3ac47d955accc085a37a9c1")
- repository.write_ref("refs/keep/c", "6473c90867124755509e100d0d35ebdc85a0b6ae")
- repository.write_ref("refs/also-keep/d", "0b4bc9a49b562e85de7cc9e834518ea6828729b9")
- end
-
- after do
- ensure_seeds
+ repository.write_ref("refs/delete/a", TestEnv::BRANCH_SHA['master'])
+ repository.write_ref("refs/also-delete/b", TestEnv::BRANCH_SHA['master'])
+ repository.write_ref("refs/keep/c", TestEnv::BRANCH_SHA['master'])
+ repository.write_ref("refs/also-keep/d", TestEnv::BRANCH_SHA['master'])
end
it 'deletes all refs except those with the specified prefixes' do
@@ -2272,11 +2144,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
it 'saves a bundle to disk' do
repository.bundle_to_disk(save_path)
- success = system(
- *%W(#{Gitlab.config.git.bin_path} -C #{repository_path} bundle verify #{save_path}),
- [:out, :err] => '/dev/null'
- )
- expect(success).to be true
+ expect(File).to exist(save_path)
end
end
@@ -2326,41 +2194,22 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#checksum' do
it 'calculates the checksum for non-empty repo' do
- expect(repository.checksum).to eq '51d0a9662681f93e1fee547a6b7ba2bcaf716059'
- end
-
- it 'returns 0000000000000000000000000000000000000000 for an empty repo' do
- FileUtils.rm_rf(File.join(storage_path, 'empty-repo.git'))
-
- system(git_env, *%W(#{Gitlab.config.git.bin_path} init --bare empty-repo.git),
- chdir: storage_path,
- out: '/dev/null',
- err: '/dev/null')
-
- empty_repo = described_class.new('default', 'empty-repo.git', '', 'group/empty-repo')
-
- expect(empty_repo.checksum).to eq '0000000000000000000000000000000000000000'
+ expect(repository.checksum.length).to be(40)
+ expect(Gitlab::Git.blank_ref?(repository.checksum)).to be false
end
- it 'raises Gitlab::Git::Repository::InvalidRepository error for non-valid git repo' do
- FileUtils.rm_rf(File.join(storage_path, 'non-valid.git'))
-
- system(git_env, *%W(#{Gitlab.config.git.bin_path} clone --bare #{TEST_REPO_PATH} non-valid.git),
- chdir: SEED_STORAGE_PATH,
- out: '/dev/null',
- err: '/dev/null')
-
- File.truncate(File.join(storage_path, 'non-valid.git/HEAD'), 0)
+ it 'returns a blank sha for an empty repo' do
+ repository = create(:project, :empty_repo).repository
- non_valid = described_class.new('default', 'non-valid.git', '', 'a/non-valid')
-
- expect { non_valid.checksum }.to raise_error(Gitlab::Git::Repository::InvalidRepository)
+ expect(Gitlab::Git.blank_ref?(repository.checksum)).to be true
end
- it 'raises Gitlab::Git::Repository::NoRepository error when there is no repo' do
- broken_repo = described_class.new('default', 'a/path.git', '', 'a/path')
+ it 'raises NoRepository for a non-existent repo' do
+ repository = create(:project).repository
- expect { broken_repo.checksum }.to raise_error(Gitlab::Git::Repository::NoRepository)
+ expect do
+ repository.checksum
+ end.to raise_error(described_class::NoRepository)
end
end
@@ -2375,7 +2224,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#squash' do
let(:branch_name) { 'fix' }
- let(:start_sha) { '4b4918a572fa86f9771e5ba40fbd48e1eb03e2c6' }
+ let(:start_sha) { TestEnv::BRANCH_SHA['master'] }
let(:end_sha) { '12d65c8dd2b2676fa3ac47d955accc085a37a9c1' }
subject do
@@ -2412,7 +2261,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
context 'when the diff contains a rename' do
let(:end_sha) do
- repository.multi_action(
+ repository.commit_files(
user,
branch_name: repository.root_ref,
message: 'Move CHANGELOG to encoding/',
@@ -2427,7 +2276,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
after do
# Erase our commits so other tests get the original repo
- repository.write_ref(repository.root_ref, SeedRepo::LastCommit::ID)
+ repository.write_ref(repository.root_ref, TestEnv::BRANCH_SHA['master'])
end
it 'does not include the renamed file in the sparse checkout' do
@@ -2480,9 +2329,9 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
describe '#disconnect_alternates' do
- let(:project) { create(:project, :repository) }
+ let(:project) { mutable_project }
+ let(:repository) { mutable_repository }
let(:pool_repository) { create(:pool_repository) }
- let(:repository) { project.repository }
let(:object_pool) { pool_repository.object_pool }
before do
@@ -2495,7 +2344,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
it 'can still access objects in the object pool' do
object_pool.link(repository)
- new_commit_id = object_pool.repository.multi_action(
+ new_commit_id = object_pool.repository.commit_files(
project.owner,
branch_name: object_pool.repository.root_ref,
message: 'Add a file',
@@ -2515,8 +2364,7 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
describe '#rename' do
- let(:project) { create(:project, :repository) }
- let(:repository) { project.repository }
+ let(:repository) { mutable_repository }
it 'moves the repository' do
checksum = repository.checksum
@@ -2531,15 +2379,14 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
describe '#remove' do
- let(:project) { create(:project, :repository) }
- let(:repository) { project.repository }
+ let(:repository) { mutable_repository }
it 'removes the repository' do
expect(repository.exists?).to be true
repository.remove
- expect(repository.raw_repository.exists?).to be false
+ expect(repository.exists?).to be false
end
context 'when the repository does not exist' do
@@ -2550,15 +2397,14 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
repository.remove
- expect(repository.raw_repository.exists?).to be false
+ expect(repository.exists?).to be false
end
end
end
describe '#import_repository' do
- let_it_be(:project) { create(:project) }
+ let_it_be(:repository) { create(:project).repository }
- let(:repository) { project.repository }
let(:url) { 'http://invalid.invalid' }
it 'raises an error if a relative path is provided' do
@@ -2584,11 +2430,9 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
describe '#replicate' do
let(:new_repository) do
- Gitlab::Git::Repository.new('test_second_storage', TEST_REPO_PATH, '', 'group/project')
+ Gitlab::Git::Repository.new('test_second_storage', repository.relative_path, '', 'group/project')
end
- let(:new_repository_path) { File.join(TestEnv::SECOND_STORAGE_PATH, new_repository.relative_path) }
-
subject { new_repository.replicate(repository) }
before do
@@ -2622,7 +2466,8 @@ RSpec.describe Gitlab::Git::Repository, :seed_helper do
end
context 'with keep-around refs' do
- let(:sha) { SeedRepo::Commit::ID }
+ let(:repository) { mutable_repository }
+ let(:sha) { TestEnv::BRANCH_SHA['master'] }
let(:keep_around_ref) { "refs/keep-around/#{sha}" }
let(:tmp_ref) { "refs/tmp/#{SecureRandom.hex}" }
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 03d1c125e36..747611a59e6 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, :seed_helper do
+RSpec.describe Gitlab::Git::RuggedImpl::UseRugged do
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let(:feature_flag_name) { wrapper.rugged_feature_keys.first }
diff --git a/spec/lib/gitlab/git/tree_spec.rb b/spec/lib/gitlab/git/tree_spec.rb
index 2e4520cd3a0..7c84c737c00 100644
--- a/spec/lib/gitlab/git/tree_spec.rb
+++ b/spec/lib/gitlab/git/tree_spec.rb
@@ -95,7 +95,7 @@ RSpec.describe Gitlab::Git::Tree do
let(:subdir_file) { entries.first }
# rubocop: enable Rails/FindBy
let!(:sha) do
- repository.multi_action(
+ repository.commit_files(
user,
branch_name: 'HEAD',
message: "Create #{filename}",
diff --git a/spec/lib/gitlab/git/util_spec.rb b/spec/lib/gitlab/git/util_spec.rb
index a0237c821b5..dd925a902ab 100644
--- a/spec/lib/gitlab/git/util_spec.rb
+++ b/spec/lib/gitlab/git/util_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Git::Util do
describe '#count_lines' do
diff --git a/spec/lib/gitlab/git/wiki_spec.rb b/spec/lib/gitlab/git/wiki_spec.rb
index dddcf8c40fc..05c7ac149e4 100644
--- a/spec/lib/gitlab/git/wiki_spec.rb
+++ b/spec/lib/gitlab/git/wiki_spec.rb
@@ -8,9 +8,15 @@ RSpec.describe Gitlab::Git::Wiki do
let(:project) { create(:project) }
let(:user) { project.first_owner }
let(:project_wiki) { ProjectWiki.new(project, user) }
+ let(:repository) { project_wiki.repository }
+ let(:default_branch) { described_class.default_ref(project) }
subject(:wiki) { project_wiki.wiki }
+ before do
+ repository.create_if_not_exists(project_wiki.default_branch)
+ end
+
describe '#pages' do
before do
create_page('page1', 'content')
@@ -44,7 +50,7 @@ RSpec.describe Gitlab::Git::Wiki do
after do
destroy_page('page1')
- destroy_page('page1', 'foo')
+ destroy_page('foo/page1')
end
it 'returns the right page' do
@@ -71,20 +77,20 @@ RSpec.describe Gitlab::Git::Wiki do
end
describe '#preview_slug' do
- where(:title, :format, :expected_slug) do
- 'The Best Thing' | :markdown | 'The-Best-Thing'
- 'The Best Thing' | :md | 'The-Best-Thing'
- 'The Best Thing' | :txt | 'The-Best-Thing'
- 'A Subject/Title Here' | :txt | 'A-Subject/Title-Here'
- 'A subject' | :txt | 'A-subject'
- 'A 1/B 2/C 3' | :txt | 'A-1/B-2/C-3'
- 'subject/title' | :txt | 'subject/title'
- 'subject/title.md' | :txt | 'subject/title.md'
- 'foo<bar>+baz' | :txt | 'foo-bar--baz'
- 'foo%2Fbar' | :txt | 'foo%2Fbar'
- '' | :markdown | '.md'
- '' | :md | '.md'
- '' | :txt | '.txt'
+ where(:title, :file_extension, :format, :expected_slug) do
+ 'The Best Thing' | :md | :markdown | 'The-Best-Thing'
+ 'The Best Thing' | :md | :md | 'The-Best-Thing'
+ 'The Best Thing' | :txt | :txt | 'The-Best-Thing'
+ 'A Subject/Title Here' | :txt | :txt | 'A-Subject/Title-Here'
+ 'A subject' | :txt | :txt | 'A-subject'
+ 'A 1/B 2/C 3' | :txt | :txt | 'A-1/B-2/C-3'
+ 'subject/title' | :txt | :txt | 'subject/title'
+ 'subject/title.md' | :txt | :txt | 'subject/title.md'
+ 'foo<bar>+baz' | :txt | :txt | 'foo-bar--baz'
+ 'foo%2Fbar' | :txt | :txt | 'foo%2Fbar'
+ '' | :md | :markdown | '.md'
+ '' | :md | :md | '.md'
+ '' | :txt | :txt | '.txt'
end
with_them do
@@ -97,7 +103,7 @@ RSpec.describe Gitlab::Git::Wiki do
it 'matches the slug generated by gitaly' do
skip('Gitaly cannot generate a slug for an empty title') unless title.present?
- create_page(title, 'content', format: format)
+ create_page(title, 'content', file_extension)
gitaly_slug = wiki.list_pages.first.url_path
@@ -106,16 +112,23 @@ RSpec.describe Gitlab::Git::Wiki do
end
end
- def create_page(name, content, format: :markdown)
- wiki.write_page(name, format, content, commit_details(name))
- end
-
- def commit_details(name)
- Gitlab::Git::Wiki::CommitDetails.new(user.id, user.username, user.name, user.email, "created page #{name}")
+ def create_page(name, content, extension = :md)
+ repository.create_file(
+ user, ::Wiki.sluggified_full_path(name, extension.to_s), content,
+ branch_name: default_branch,
+ message: "created page #{name}",
+ author_email: user.email,
+ author_name: user.name
+ )
end
- def destroy_page(title, dir = '')
- page = wiki.page(title: title, dir: dir)
- project_wiki.delete_page(page, "test commit")
+ def destroy_page(name, extension = :md)
+ repository.delete_file(
+ user, ::Wiki.sluggified_full_path(name, extension.to_s),
+ branch_name: described_class.default_ref(project),
+ message: "delete page #{name}",
+ author_email: user.email,
+ author_name: user.name
+ )
end
end
diff --git a/spec/lib/gitlab/git_spec.rb b/spec/lib/gitlab/git_spec.rb
index f359679a930..0f6ef55b4b1 100644
--- a/spec/lib/gitlab/git_spec.rb
+++ b/spec/lib/gitlab/git_spec.rb
@@ -7,10 +7,18 @@ RSpec.describe Gitlab::Git do
let(:committer_name) { 'John Doe' }
describe '.ref_name' do
+ let(:ref) { Gitlab::Git::BRANCH_REF_PREFIX + "an_invalid_ref_\xE5" }
+
it 'ensure ref is a valid UTF-8 string' do
- utf8_invalid_ref = Gitlab::Git::BRANCH_REF_PREFIX + "an_invalid_ref_\xE5"
+ expect(described_class.ref_name(ref)).to eq("an_invalid_ref_%E5")
+ end
- expect(described_class.ref_name(utf8_invalid_ref)).to eq("an_invalid_ref_Ã¥")
+ context 'when ref contains characters \x80 - \xFF' do
+ let(:ref) { Gitlab::Git::BRANCH_REF_PREFIX + "\x90" }
+
+ it 'correctly converts it' do
+ expect(described_class.ref_name(ref)).to eq("%90")
+ end
end
end
diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
index ed6a87cda6f..ff3cade07c0 100644
--- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
@@ -297,6 +297,11 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
describe '#list_commits' do
let(:revisions) { 'master' }
let(:reverse) { false }
+ let(:author) { nil }
+ let(:ignore_case) { nil }
+ let(:commit_message_patterns) { nil }
+ let(:before) { nil }
+ let(:after) { nil }
let(:pagination_params) { nil }
shared_examples 'a ListCommits request' do
@@ -309,13 +314,18 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
expected_request = gitaly_request_with_params(
Array.wrap(revisions),
reverse: reverse,
+ author: author,
+ ignore_case: ignore_case,
+ commit_message_patterns: commit_message_patterns,
+ before: before,
+ after: after,
pagination_params: pagination_params
)
expect(service).to receive(:list_commits).with(expected_request, kind_of(Hash)).and_return([])
end
- client.list_commits(revisions, reverse: reverse, pagination_params: pagination_params)
+ client.list_commits(revisions, { reverse: reverse, author: author, ignore_case: ignore_case, commit_message_patterns: commit_message_patterns, before: before, after: after, pagination_params: pagination_params })
end
end
@@ -333,7 +343,12 @@ RSpec.describe Gitlab::GitalyClient::CommitService do
it_behaves_like 'a ListCommits request'
end
- context 'with pagination params' do
+ context 'with commit message, author, before and after' do
+ let(:author) { "Dmitriy" }
+ let(:before) { 1474828200 }
+ let(:after) { 1474828200 }
+ let(:commit_message_patterns) { "Initial commit" }
+ let(:ignore_case) { true }
let(:pagination_params) { { limit: 1, page_token: 'foo' } }
it_behaves_like 'a ListCommits request'
diff --git a/spec/lib/gitlab/gitaly_client/diff_spec.rb b/spec/lib/gitlab/gitaly_client/diff_spec.rb
index 230322faecd..2c1f684c0c5 100644
--- a/spec/lib/gitlab/gitaly_client/diff_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/diff_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GitalyClient::Diff do
let(:diff_fields) do
diff --git a/spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb b/spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb
index 54c84ddc56f..39fd752ef7f 100644
--- a/spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GitalyClient::DiffStitcher do
describe 'enumeration' do
diff --git a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
index 5d854f0c9d1..7e8aaa3cdf4 100644
--- a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
@@ -56,6 +56,85 @@ RSpec.describe Gitlab::GitalyClient::OperationService do
Gitlab::Git::PreReceiveError, "something failed")
end
end
+
+ context 'with structured errors' do
+ context 'with CustomHookError' do
+ let(:stdout) { nil }
+ let(:stderr) { nil }
+ let(:error_message) { "error_message" }
+
+ let(:custom_hook_error) do
+ new_detailed_error(
+ GRPC::Core::StatusCodes::PERMISSION_DENIED,
+ error_message,
+ Gitaly::UserCreateBranchError.new(
+ custom_hook: Gitaly::CustomHookError.new(
+ stdout: stdout,
+ stderr: stderr,
+ hook_type: Gitaly::CustomHookError::HookType::HOOK_TYPE_PRERECEIVE
+ )))
+ end
+
+ shared_examples 'failed branch creation' do
+ it 'raised a PreRecieveError' do
+ expect_any_instance_of(Gitaly::OperationService::Stub)
+ .to receive(:user_create_branch)
+ .and_raise(custom_hook_error)
+
+ expect { subject }.to raise_error do |error|
+ expect(error).to be_a(Gitlab::Git::PreReceiveError)
+ expect(error.message).to eq(expected_message)
+ expect(error.raw_message).to eq(expected_raw_message)
+ end
+ end
+ end
+
+ context 'when details contain stderr without prefix' do
+ let(:stderr) { "something" }
+ let(:stdout) { "GL-HOOK-ERR: stdout is overridden by stderr" }
+ let(:expected_message) { error_message }
+ let(:expected_raw_message) { stderr }
+
+ it_behaves_like 'failed branch creation'
+ end
+
+ context 'when details contain stderr with prefix' do
+ let(:stderr) { "GL-HOOK-ERR: something" }
+ let(:stdout) { "GL-HOOK-ERR: stdout is overridden by stderr" }
+ let(:expected_message) { "something" }
+ let(:expected_raw_message) { stderr }
+
+ it_behaves_like 'failed branch creation'
+ end
+
+ context 'when details contain stdout without prefix' do
+ let(:stderr) { " \n" }
+ let(:stdout) { "something" }
+ let(:expected_message) { error_message }
+ let(:expected_raw_message) { stdout }
+
+ it_behaves_like 'failed branch creation'
+ end
+
+ context 'when details contain stdout with prefix' do
+ let(:stderr) { " \n" }
+ let(:stdout) { "GL-HOOK-ERR: something" }
+ let(:expected_message) { "something" }
+ let(:expected_raw_message) { stdout }
+
+ it_behaves_like 'failed branch creation'
+ end
+
+ context 'when details contain no stderr or stdout' do
+ let(:stderr) { " \n" }
+ let(:stdout) { "\n \n" }
+ let(:expected_message) { error_message }
+ let(:expected_raw_message) { "\n \n" }
+
+ it_behaves_like 'failed branch creation'
+ end
+ end
+ end
end
describe '#user_update_branch' do
diff --git a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
index 277276bb1d3..b7c21516c77 100644
--- a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
@@ -156,35 +156,84 @@ RSpec.describe Gitlab::GitalyClient::RefService do
end
describe '#local_branches' do
- it 'sends a find_local_branches message' do
- expect_any_instance_of(Gitaly::RefService::Stub)
- .to receive(:find_local_branches)
- .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
- .and_return([])
+ let(:remote_name) { 'my_remote' }
- client.local_branches
- end
+ shared_examples 'common examples' do
+ it 'sends a find_local_branches message' do
+ target_commits = create_list(:gitaly_commit, 4)
+ branches = target_commits.each_with_index.map do |gitaly_commit, i|
+ Gitaly::FindLocalBranchResponse.new(
+ name: "#{remote_name}/#{i}",
+ commit: gitaly_commit,
+ commit_author: Gitaly::FindLocalBranchCommitAuthor.new(
+ name: gitaly_commit.author.name,
+ email: gitaly_commit.author.email,
+ date: gitaly_commit.author.date,
+ timezone: gitaly_commit.author.timezone
+ ),
+ commit_committer: Gitaly::FindLocalBranchCommitAuthor.new(
+ name: gitaly_commit.committer.name,
+ email: gitaly_commit.committer.email,
+ date: gitaly_commit.committer.date,
+ timezone: gitaly_commit.committer.timezone
+ )
+ )
+ end
+ local_branches = target_commits.each_with_index.map do |gitaly_commit, i|
+ Gitaly::Branch.new(name: "#{remote_name}/#{i}", target_commit: gitaly_commit)
+ end
+ response = [
+ Gitaly::FindLocalBranchesResponse.new(branches: branches[0, 2], local_branches: local_branches[0, 2]),
+ Gitaly::FindLocalBranchesResponse.new(branches: branches[2, 2], local_branches: local_branches[2, 2])
+ ]
- it 'parses and sends the sort parameter' do
- expect_any_instance_of(Gitaly::RefService::Stub)
- .to receive(:find_local_branches)
- .with(gitaly_request_with_params(sort_by: :UPDATED_DESC), kind_of(Hash))
- .and_return([])
+ expect_any_instance_of(Gitaly::RefService::Stub)
+ .to receive(:find_local_branches)
+ .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ .and_return(response)
+
+ subject = client.local_branches
+
+ expect(subject.length).to be(target_commits.length)
+ end
+
+ it 'parses and sends the sort parameter' do
+ expect_any_instance_of(Gitaly::RefService::Stub)
+ .to receive(:find_local_branches)
+ .with(gitaly_request_with_params(sort_by: :UPDATED_DESC), kind_of(Hash))
+ .and_return([])
+
+ client.local_branches(sort_by: 'updated_desc')
+ end
+
+ it 'translates known mismatches on sort param values' do
+ expect_any_instance_of(Gitaly::RefService::Stub)
+ .to receive(:find_local_branches)
+ .with(gitaly_request_with_params(sort_by: :NAME), kind_of(Hash))
+ .and_return([])
+
+ client.local_branches(sort_by: 'name_asc')
+ end
- client.local_branches(sort_by: 'updated_desc')
+ it 'raises an argument error if an invalid sort_by parameter is passed' do
+ expect { client.local_branches(sort_by: 'invalid_sort') }.to raise_error(ArgumentError)
+ end
end
- it 'translates known mismatches on sort param values' do
- expect_any_instance_of(Gitaly::RefService::Stub)
- .to receive(:find_local_branches)
- .with(gitaly_request_with_params(sort_by: :NAME), kind_of(Hash))
- .and_return([])
+ context 'when feature flag :gitaly_simplify_find_local_branches_response is enabled' do
+ before do
+ stub_feature_flags(gitaly_simplify_find_local_branches_response: true)
+ end
- client.local_branches(sort_by: 'name_asc')
+ it_behaves_like 'common examples'
end
- it 'raises an argument error if an invalid sort_by parameter is passed' do
- expect { client.local_branches(sort_by: 'invalid_sort') }.to raise_error(ArgumentError)
+ context 'when feature flag :gitaly_simplify_find_local_branches_response is disabled' do
+ before do
+ stub_feature_flags(gitaly_simplify_find_local_branches_response: false)
+ end
+
+ it_behaves_like 'common examples'
end
end
@@ -211,6 +260,22 @@ RSpec.describe Gitlab::GitalyClient::RefService do
client.tags(sort_by: 'name_asc')
end
+
+ context 'with semantic version sorting' do
+ it 'sends a correct find_all_tags message' do
+ expected_sort_by = Gitaly::FindAllTagsRequest::SortBy.new(
+ key: :VERSION_REFNAME,
+ direction: :ASCENDING
+ )
+
+ expect_any_instance_of(Gitaly::RefService::Stub)
+ .to receive(:find_all_tags)
+ .with(gitaly_request_with_params(sort_by: expected_sort_by), kind_of(Hash))
+ .and_return([])
+
+ client.tags(sort_by: 'version_asc')
+ end
+ end
end
context 'with pagination option' do
diff --git a/spec/lib/gitlab/gitaly_client/server_service_spec.rb b/spec/lib/gitlab/gitaly_client/server_service_spec.rb
new file mode 100644
index 00000000000..615f2ce0c21
--- /dev/null
+++ b/spec/lib/gitlab/gitaly_client/server_service_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GitalyClient::ServerService do
+ let(:storage) { 'default' }
+
+ describe '#readiness_check' do
+ before do
+ ::Gitlab::GitalyClient.clear_stubs!
+ end
+
+ let(:request) do
+ Gitaly::ReadinessCheckRequest.new(timeout: 30)
+ end
+
+ subject(:readiness_check) { described_class.new(storage).readiness_check }
+
+ it 'returns a positive success if no failures happened' do
+ expect_next_instance_of(Gitaly::ServerService::Stub) do |service|
+ response = Gitaly::ReadinessCheckResponse.new(ok_response: Gitaly::ReadinessCheckResponse::Ok.new)
+ expect(service).to receive(:readiness_check).with(request, kind_of(Hash)).and_return(response)
+ end
+
+ expect(readiness_check[:success]).to eq(true)
+ end
+
+ it 'returns a negative success and a compiled message if at least one failure happened' do
+ failure1 = Gitaly::ReadinessCheckResponse::Failure::Response.new(name: '1', error_message: 'msg 1')
+ failure2 = Gitaly::ReadinessCheckResponse::Failure::Response.new(name: '2', error_message: 'msg 2')
+ failures = Gitaly::ReadinessCheckResponse::Failure.new(failed_checks: [failure1, failure2])
+ response = Gitaly::ReadinessCheckResponse.new(failure_response: failures)
+
+ expect_next_instance_of(Gitaly::ServerService::Stub) do |service|
+ expect(service).to receive(:readiness_check).with(request, kind_of(Hash)).and_return(response)
+ end
+
+ expect(readiness_check[:success]).to eq(false)
+ expect(readiness_check[:message]).to eq("1: msg 1\n2: msg 2")
+ end
+ end
+end
diff --git a/spec/lib/gitlab/gitaly_client/util_spec.rb b/spec/lib/gitlab/gitaly_client/util_spec.rb
index b6589a08f7d..ae7c3789051 100644
--- a/spec/lib/gitlab/gitaly_client/util_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/util_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GitalyClient::Util do
describe '.repository' do
diff --git a/spec/lib/gitlab/github_import/attachments_downloader_spec.rb b/spec/lib/gitlab/github_import/attachments_downloader_spec.rb
new file mode 100644
index 00000000000..57391e06192
--- /dev/null
+++ b/spec/lib/gitlab/github_import/attachments_downloader_spec.rb
@@ -0,0 +1,97 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::AttachmentsDownloader do
+ subject(:downloader) { described_class.new(file_url) }
+
+ let_it_be(:file_url) { 'https://example.com/avatar.png' }
+ let_it_be(:content_type) { 'application/octet-stream' }
+
+ let(:content_length) { 1000 }
+ let(:chunk_double) { instance_double(HTTParty::FragmentWithResponse, code: 200) }
+ let(:headers_double) do
+ instance_double(
+ HTTParty::Response,
+ code: 200,
+ success?: true,
+ parsed_response: {},
+ headers: {
+ 'content-length' => content_length,
+ 'content-type' => content_type
+ }
+ )
+ end
+
+ describe '#perform' do
+ before do
+ allow(Gitlab::HTTP).to receive(:perform_request)
+ .with(Net::HTTP::Get, file_url, stream_body: true).and_yield(chunk_double)
+ allow(Gitlab::HTTP).to receive(:perform_request)
+ .with(Net::HTTP::Head, file_url, {}).and_return(headers_double)
+ end
+
+ context 'when file valid' do
+ it 'downloads file' do
+ file = downloader.perform
+
+ expect(File.exist?(file.path)).to eq(true)
+ end
+ end
+
+ context 'when filename is malicious' do
+ let_it_be(:file_url) { 'https://example.com/ava%2F..%2Ftar.png' }
+
+ it 'raises expected exception' do
+ expect { downloader.perform }.to raise_exception(
+ Gitlab::Utils::PathTraversalAttackError,
+ 'Invalid path'
+ )
+ end
+ end
+
+ context 'when file size exceeds limit' do
+ let(:content_length) { 26.megabytes }
+
+ it 'raises expected exception' do
+ expect { downloader.perform }.to raise_exception(
+ Gitlab::GithubImport::AttachmentsDownloader::DownloadError,
+ 'File size 26 MB exceeds limit of 25 MB'
+ )
+ end
+ end
+
+ context 'when file name length exceeds limit' do
+ before do
+ stub_const('BulkImports::FileDownloads::FilenameFetch::FILENAME_SIZE_LIMIT', 2)
+ end
+
+ it 'chops filename' do
+ file = downloader.perform
+
+ expect(File.exist?(file.path)).to eq(true)
+ expect(File.basename(file)).to eq('av.png')
+ end
+ end
+ end
+
+ describe '#delete' do
+ let(:tmp_dir_path) { File.join(Dir.tmpdir, 'github_attachments_test') }
+ let(:file) do
+ downloader.mkdir_p(tmp_dir_path)
+ file = File.open("#{tmp_dir_path}/test.txt", 'wb')
+ file.write('foo')
+ file.close
+ file
+ end
+
+ before do
+ allow(downloader).to receive(:filepath).and_return(file.path)
+ end
+
+ it 'removes file with parent folder' do
+ downloader.delete
+ expect(Dir.exist?(tmp_dir_path)).to eq false
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/client_spec.rb b/spec/lib/gitlab/github_import/client_spec.rb
index 2bd3910ad87..c88bb6de859 100644
--- a/spec/lib/gitlab/github_import/client_spec.rb
+++ b/spec/lib/gitlab/github_import/client_spec.rb
@@ -40,6 +40,22 @@ RSpec.describe Gitlab::GithubImport::Client do
end
end
+ describe '#repos' do
+ it 'returns the user\'s repositories as a hash' do
+ client = described_class.new('foo')
+
+ stub_request(:get, 'https://api.github.com/rate_limit')
+ .to_return(status: 200, headers: { 'X-RateLimit-Limit' => 5000, 'X-RateLimit-Remaining' => 5000 })
+
+ stub_request(:get, 'https://api.github.com/user/repos?page=1&page_length=10&per_page=100')
+ .to_return(status: 200, body: [{ id: 1 }, { id: 2 }].to_json, headers: { 'Content-Type' => 'application/json' })
+
+ repos = client.repos({ page: 1, page_length: 10 })
+
+ expect(repos).to match_array([{ id: 1 }, { id: 2 }])
+ end
+ end
+
describe '#repository' do
it 'returns the details of a repository' do
client = described_class.new('foo')
@@ -49,6 +65,20 @@ RSpec.describe Gitlab::GithubImport::Client do
client.repository('foo/bar')
end
+
+ it 'returns repository data as a hash' do
+ client = described_class.new('foo')
+
+ stub_request(:get, 'https://api.github.com/rate_limit')
+ .to_return(status: 200, headers: { 'X-RateLimit-Limit' => 5000, 'X-RateLimit-Remaining' => 5000 })
+
+ stub_request(:get, 'https://api.github.com/repos/foo/bar')
+ .to_return(status: 200, body: { id: 1 }.to_json, headers: { 'Content-Type' => 'application/json' })
+
+ repository = client.repository('foo/bar')
+
+ expect(repository).to eq({ id: 1 })
+ end
end
describe '#pull_request' do
@@ -98,6 +128,30 @@ RSpec.describe Gitlab::GithubImport::Client do
end
end
+ describe '#branches' do
+ it 'returns the branches' do
+ client = described_class.new('foo')
+
+ expect(client)
+ .to receive(:each_object)
+ .with(:branches, 'foo/bar')
+
+ client.branches('foo/bar')
+ end
+ end
+
+ describe '#branch_protection' do
+ it 'returns the protection details for the given branch' do
+ client = described_class.new('foo')
+
+ expect(client.octokit)
+ .to receive(:branch_protection).with('org/repo', 'bar')
+ expect(client).to receive(:with_rate_limit).and_yield
+
+ client.branch_protection('org/repo', 'bar')
+ end
+ end
+
describe '#each_page' do
let(:client) { described_class.new('foo') }
let(:object1) { double(:object1) }
@@ -234,7 +288,7 @@ RSpec.describe Gitlab::GithubImport::Client do
expect(client).to receive(:requests_remaining?).twice.and_return(true)
expect(Gitlab::Import::Logger).to receive(:info).with(hash_including(info_params)).once
- expect(client.with_rate_limit(&block_to_rate_limit)).to be(true)
+ expect(client.with_rate_limit(&block_to_rate_limit)).to eq({})
end
it 'retries and does not succeed' do
@@ -255,7 +309,7 @@ RSpec.describe Gitlab::GithubImport::Client do
expect(Gitlab::Import::Logger).to receive(:info).with(hash_including(info_params)).once
- expect(client.with_rate_limit(&block_to_rate_limit)).to be(true)
+ expect(client.with_rate_limit(&block_to_rate_limit)).to eq({})
end
it 'retries and does not succeed' do
@@ -559,7 +613,7 @@ RSpec.describe Gitlab::GithubImport::Client do
expect(Gitlab::Import::Logger).to receive(:info).with(hash_including(info_params)).once
- expect(client.search_repos_by_name('test')).to be(true)
+ expect(client.search_repos_by_name('test')).to eq({})
end
it 'retries and does not succeed' do
@@ -599,7 +653,7 @@ RSpec.describe Gitlab::GithubImport::Client do
call_count = 0
allow(client.octokit).to receive(method) do
call_count += 1
- call_count > 1 ? true : raise(described_class::CLIENT_CONNECTION_ERROR, 'execution expired')
+ call_count > 1 ? {} : raise(described_class::CLIENT_CONNECTION_ERROR, 'execution expired')
end
end
end
diff --git a/spec/lib/gitlab/github_import/importer/events/changed_assignee_spec.rb b/spec/lib/gitlab/github_import/importer/events/changed_assignee_spec.rb
index 2f6f727dc38..dbc72574ec2 100644
--- a/spec/lib/gitlab/github_import/importer/events/changed_assignee_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/events/changed_assignee_spec.rb
@@ -6,31 +6,30 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedAssignee do
subject(:importer) { described_class.new(project, client) }
let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:author) { create(:user) }
let_it_be(:assignee) { create(:user) }
- let_it_be(:assigner) { create(:user) }
let(:client) { instance_double('Gitlab::GithubImport::Client') }
- let(:issue) { create(:issue, project: project) }
+ let(:issuable) { create(:issue, project: project) }
let(:issue_event) do
Gitlab::GithubImport::Representation::IssueEvent.from_json_hash(
'id' => 6501124486,
- 'actor' => { 'id' => 4, 'login' => 'alice' },
+ 'actor' => { 'id' => author.id, 'login' => author.username },
'event' => event_type,
'commit_id' => nil,
'created_at' => '2022-04-26 18:30:53 UTC',
- 'assigner' => { 'id' => assigner.id, 'login' => assigner.username },
'assignee' => { 'id' => assignee.id, 'login' => assignee.username },
- 'issue' => { 'number' => issue.iid }
+ 'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
)
end
let(:note_attrs) do
{
- noteable_id: issue.id,
- noteable_type: Issue.name,
+ noteable_id: issuable.id,
+ noteable_type: issuable.class.name,
project_id: project.id,
- author_id: assigner.id,
+ author_id: author.id,
system: true,
created_at: issue_event.created_at,
updated_at: issue_event.created_at
@@ -45,12 +44,12 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedAssignee do
}.stringify_keys
end
- shared_examples 'new note' do
+ shared_examples 'create expected notes' do
it 'creates expected note' do
- expect { importer.execute(issue_event) }.to change { issue.notes.count }
+ expect { importer.execute(issue_event) }.to change { issuable.notes.count }
.from(0).to(1)
- expect(issue.notes.last)
+ expect(issuable.notes.last)
.to have_attributes(expected_note_attrs)
end
@@ -67,29 +66,41 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedAssignee do
end
end
+ shared_examples 'process assigned & unassigned events' do
+ context 'when importing an assigned event' do
+ let(:event_type) { 'assigned' }
+ let(:expected_note_attrs) { note_attrs.merge(note: "assigned to @#{assignee.username}") }
+
+ it_behaves_like 'create expected notes'
+ end
+
+ context 'when importing an unassigned event' do
+ let(:event_type) { 'unassigned' }
+ let(:expected_note_attrs) { note_attrs.merge(note: "unassigned @#{assignee.username}") }
+
+ it_behaves_like 'create expected notes'
+ end
+ end
+
describe '#execute' do
before do
allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
- allow(finder).to receive(:database_id).and_return(issue.id)
+ allow(finder).to receive(:database_id).and_return(issuable.id)
end
allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
+ allow(finder).to receive(:find).with(author.id, author.username).and_return(author.id)
allow(finder).to receive(:find).with(assignee.id, assignee.username).and_return(assignee.id)
- allow(finder).to receive(:find).with(assigner.id, assigner.username).and_return(assigner.id)
end
end
- context 'when importing an assigned event' do
- let(:event_type) { 'assigned' }
- let(:expected_note_attrs) { note_attrs.merge(note: "assigned to @#{assignee.username}") }
-
- it_behaves_like 'new note'
+ context 'with Issue' do
+ it_behaves_like 'process assigned & unassigned events'
end
- context 'when importing an unassigned event' do
- let(:event_type) { 'unassigned' }
- let(:expected_note_attrs) { note_attrs.merge(note: "unassigned @#{assigner.username}") }
+ context 'with MergeRequest' do
+ let(:issuable) { create(:merge_request, source_project: project, target_project: project) }
- it_behaves_like 'new note'
+ it_behaves_like 'process assigned & unassigned events'
end
end
end
diff --git a/spec/lib/gitlab/github_import/importer/events/changed_label_spec.rb b/spec/lib/gitlab/github_import/importer/events/changed_label_spec.rb
index e21672aa430..4476b4123ee 100644
--- a/spec/lib/gitlab/github_import/importer/events/changed_label_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/events/changed_label_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedLabel do
let_it_be(:user) { create(:user) }
let(:client) { instance_double('Gitlab::GithubImport::Client') }
- let(:issue) { create(:issue, project: project) }
+ let(:issuable) { create(:issue, project: project) }
let!(:label) { create(:label, project: project) }
let(:issue_event) do
@@ -19,16 +19,14 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedLabel do
'event' => event_type,
'commit_id' => nil,
'label_title' => label.title,
- 'issue_db_id' => issue.id,
'created_at' => '2022-04-26 18:30:53 UTC',
- 'issue' => { 'number' => issue.iid }
+ 'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
)
end
let(:event_attrs) do
{
user_id: user.id,
- issue_id: issue.id,
label_id: label.id,
created_at: issue_event.created_at
}.stringify_keys
@@ -36,9 +34,9 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedLabel do
shared_examples 'new event' do
it 'creates a new label event' do
- expect { importer.execute(issue_event) }.to change { issue.resource_label_events.count }
+ expect { importer.execute(issue_event) }.to change { issuable.resource_label_events.count }
.from(0).to(1)
- expect(issue.resource_label_events.last)
+ expect(issuable.resource_label_events.last)
.to have_attributes(expected_event_attrs)
end
end
@@ -46,24 +44,44 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedLabel do
before do
allow(Gitlab::Cache::Import::Caching).to receive(:read_integer).and_return(label.id)
allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
- allow(finder).to receive(:database_id).and_return(issue.id)
+ allow(finder).to receive(:database_id).and_return(issuable.id)
end
allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
allow(finder).to receive(:find).with(user.id, user.username).and_return(user.id)
end
end
- context 'when importing a labeled event' do
- let(:event_type) { 'labeled' }
- let(:expected_event_attrs) { event_attrs.merge(action: 'add') }
+ context 'with Issue' do
+ context 'when importing a labeled event' do
+ let(:event_type) { 'labeled' }
+ let(:expected_event_attrs) { event_attrs.merge(issue_id: issuable.id, action: 'add') }
- it_behaves_like 'new event'
+ it_behaves_like 'new event'
+ end
+
+ context 'when importing an unlabeled event' do
+ let(:event_type) { 'unlabeled' }
+ let(:expected_event_attrs) { event_attrs.merge(issue_id: issuable.id, action: 'remove') }
+
+ it_behaves_like 'new event'
+ end
end
- context 'when importing an unlabeled event' do
- let(:event_type) { 'unlabeled' }
- let(:expected_event_attrs) { event_attrs.merge(action: 'remove') }
+ context 'with MergeRequest' do
+ let(:issuable) { create(:merge_request, source_project: project, target_project: project) }
+
+ context 'when importing a labeled event' do
+ let(:event_type) { 'labeled' }
+ let(:expected_event_attrs) { event_attrs.merge(merge_request_id: issuable.id, action: 'add') }
- it_behaves_like 'new event'
+ it_behaves_like 'new event'
+ end
+
+ context 'when importing an unlabeled event' do
+ let(:event_type) { 'unlabeled' }
+ let(:expected_event_attrs) { event_attrs.merge(merge_request_id: issuable.id, action: 'remove') }
+
+ it_behaves_like 'new event'
+ end
end
end
diff --git a/spec/lib/gitlab/github_import/importer/events/changed_milestone_spec.rb b/spec/lib/gitlab/github_import/importer/events/changed_milestone_spec.rb
index 2687627fc23..bc14b81bd91 100644
--- a/spec/lib/gitlab/github_import/importer/events/changed_milestone_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/events/changed_milestone_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedMilestone do
let_it_be(:user) { create(:user) }
let(:client) { instance_double('Gitlab::GithubImport::Client') }
- let(:issue) { create(:issue, project: project) }
+ let(:issuable) { create(:issue, project: project) }
let!(:milestone) { create(:milestone, project: project) }
let(:issue_event) do
@@ -19,16 +19,15 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedMilestone do
'event' => event_type,
'commit_id' => nil,
'milestone_title' => milestone.title,
- 'issue_db_id' => issue.id,
+ 'issue_db_id' => issuable.id,
'created_at' => '2022-04-26 18:30:53 UTC',
- 'issue' => { 'number' => issue.iid }
+ 'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
)
end
let(:event_attrs) do
{
user_id: user.id,
- issue_id: issue.id,
milestone_id: milestone.id,
state: 'opened',
created_at: issue_event.created_at
@@ -37,9 +36,9 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedMilestone do
shared_examples 'new event' do
it 'creates a new milestone event' do
- expect { importer.execute(issue_event) }.to change { issue.resource_milestone_events.count }
+ expect { importer.execute(issue_event) }.to change { issuable.resource_milestone_events.count }
.from(0).to(1)
- expect(issue.resource_milestone_events.last)
+ expect(issuable.resource_milestone_events.last)
.to have_attributes(expected_event_attrs)
end
end
@@ -48,25 +47,45 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedMilestone do
before do
allow(Gitlab::Cache::Import::Caching).to receive(:read_integer).and_return(milestone.id)
allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
- allow(finder).to receive(:database_id).and_return(issue.id)
+ allow(finder).to receive(:database_id).and_return(issuable.id)
end
allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
allow(finder).to receive(:find).with(user.id, user.username).and_return(user.id)
end
end
- context 'when importing a milestoned event' do
- let(:event_type) { 'milestoned' }
- let(:expected_event_attrs) { event_attrs.merge(action: 'add') }
+ context 'with Issue' do
+ context 'when importing a milestoned event' do
+ let(:event_type) { 'milestoned' }
+ let(:expected_event_attrs) { event_attrs.merge(issue_id: issuable.id, action: 'add') }
- it_behaves_like 'new event'
+ it_behaves_like 'new event'
+ end
+
+ context 'when importing demilestoned event' do
+ let(:event_type) { 'demilestoned' }
+ let(:expected_event_attrs) { event_attrs.merge(issue_id: issuable.id, action: 'remove') }
+
+ it_behaves_like 'new event'
+ end
end
- context 'when importing demilestoned event' do
- let(:event_type) { 'demilestoned' }
- let(:expected_event_attrs) { event_attrs.merge(action: 'remove') }
+ context 'with MergeRequest' do
+ let(:issuable) { create(:merge_request, source_project: project, target_project: project) }
+
+ context 'when importing a milestoned event' do
+ let(:event_type) { 'milestoned' }
+ let(:expected_event_attrs) { event_attrs.merge(merge_request_id: issuable.id, action: 'add') }
- it_behaves_like 'new event'
+ it_behaves_like 'new event'
+ end
+
+ context 'when importing demilestoned event' do
+ let(:event_type) { 'demilestoned' }
+ let(:expected_event_attrs) { event_attrs.merge(merge_request_id: issuable.id, action: 'remove') }
+
+ it_behaves_like 'new event'
+ end
end
end
end
diff --git a/spec/lib/gitlab/github_import/importer/events/changed_reviewer_spec.rb b/spec/lib/gitlab/github_import/importer/events/changed_reviewer_spec.rb
new file mode 100644
index 00000000000..ff813dd41d9
--- /dev/null
+++ b/spec/lib/gitlab/github_import/importer/events/changed_reviewer_spec.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedReviewer do
+ subject(:importer) { described_class.new(project, client) }
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:requested_reviewer) { create(:user) }
+ let_it_be(:review_requester) { create(:user) }
+
+ let(:client) { instance_double('Gitlab::GithubImport::Client') }
+ let(:issuable) { create(:merge_request, source_project: project, target_project: project) }
+
+ let(:issue_event) do
+ Gitlab::GithubImport::Representation::IssueEvent.from_json_hash(
+ 'id' => 6501124486,
+ 'actor' => { 'id' => 4, 'login' => 'alice' },
+ 'event' => event_type,
+ 'commit_id' => nil,
+ 'created_at' => '2022-04-26 18:30:53 UTC',
+ 'review_requester' => { 'id' => review_requester.id, 'login' => review_requester.username },
+ 'requested_reviewer' => { 'id' => requested_reviewer.id, 'login' => requested_reviewer.username },
+ 'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
+ )
+ end
+
+ let(:note_attrs) do
+ {
+ noteable_id: issuable.id,
+ noteable_type: issuable.class.name,
+ project_id: project.id,
+ author_id: review_requester.id,
+ system: true,
+ created_at: issue_event.created_at,
+ updated_at: issue_event.created_at
+ }.stringify_keys
+ end
+
+ let(:expected_system_note_metadata_attrs) do
+ {
+ action: 'reviewer',
+ created_at: issue_event.created_at,
+ updated_at: issue_event.created_at
+ }.stringify_keys
+ end
+
+ shared_examples 'create expected notes' do
+ it 'creates expected note' do
+ expect { importer.execute(issue_event) }.to change { issuable.notes.count }
+ .from(0).to(1)
+
+ expect(issuable.notes.last)
+ .to have_attributes(expected_note_attrs)
+ end
+
+ it 'creates expected system note metadata' do
+ expect { importer.execute(issue_event) }.to change(SystemNoteMetadata, :count)
+ .from(0).to(1)
+
+ expect(SystemNoteMetadata.last)
+ .to have_attributes(
+ expected_system_note_metadata_attrs.merge(
+ note_id: Note.last.id
+ )
+ )
+ end
+ end
+
+ shared_examples 'process review_requested & review_request_removed MR events' do
+ context 'when importing a review_requested event' do
+ let(:event_type) { 'review_requested' }
+ let(:expected_note_attrs) { note_attrs.merge(note: "requested review from @#{requested_reviewer.username}") }
+
+ it_behaves_like 'create expected notes'
+ end
+
+ context 'when importing a review_request_removed event' do
+ let(:event_type) { 'review_request_removed' }
+ let(:expected_note_attrs) { note_attrs.merge(note: "removed review request for @#{requested_reviewer.username}") }
+
+ it_behaves_like 'create expected notes'
+ end
+ end
+
+ describe '#execute' do
+ before do
+ allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
+ allow(finder).to receive(:database_id).and_return(issuable.id)
+ end
+ allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
+ allow(finder).to receive(:find).with(requested_reviewer.id, requested_reviewer.username)
+ .and_return(requested_reviewer.id)
+ allow(finder).to receive(:find).with(review_requester.id, review_requester.username)
+ .and_return(review_requester.id)
+ end
+ end
+
+ it_behaves_like 'process review_requested & review_request_removed MR events'
+ end
+end
diff --git a/spec/lib/gitlab/github_import/importer/events/closed_spec.rb b/spec/lib/gitlab/github_import/importer/events/closed_spec.rb
index 9a49d80a8bb..f7e38f373c0 100644
--- a/spec/lib/gitlab/github_import/importer/events/closed_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/events/closed_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Closed do
let_it_be(:user) { create(:user) }
let(:client) { instance_double('Gitlab::GithubImport::Client') }
- let(:issue) { create(:issue, project: project) }
+ let(:issuable) { create(:issue, project: project) }
let(:commit_id) { nil }
let(:issue_event) do
@@ -21,7 +21,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Closed do
'event' => 'closed',
'created_at' => '2022-04-26 18:30:53 UTC',
'commit_id' => commit_id,
- 'issue' => { 'number' => issue.iid }
+ 'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
)
end
@@ -29,54 +29,74 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Closed do
{
project_id: project.id,
author_id: user.id,
- target_id: issue.id,
- target_type: Issue.name,
+ target_id: issuable.id,
+ target_type: issuable.class.name,
action: 'closed',
created_at: issue_event.created_at,
updated_at: issue_event.created_at
}.stringify_keys
end
- let(:expected_state_event_attrs) do
- {
- user_id: user.id,
- issue_id: issue.id,
- state: 'closed',
- created_at: issue_event.created_at
- }.stringify_keys
- end
-
before do
allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
- allow(finder).to receive(:database_id).and_return(issue.id)
+ allow(finder).to receive(:database_id).and_return(issuable.id)
end
allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
allow(finder).to receive(:find).with(user.id, user.username).and_return(user.id)
end
end
- it 'creates expected event and state event' do
- importer.execute(issue_event)
+ shared_examples 'new event' do
+ it 'creates expected event and state event' do
+ importer.execute(issue_event)
+
+ expect(issuable.events.count).to eq 1
+ expect(issuable.events[0].attributes)
+ .to include expected_event_attrs
+
+ expect(issuable.resource_state_events.count).to eq 1
+ expect(issuable.resource_state_events[0].attributes)
+ .to include expected_state_event_attrs
+ end
+
+ context 'when closed by commit' do
+ let!(:closing_commit) { create(:commit, project: project) }
+ let(:commit_id) { closing_commit.id }
- expect(issue.events.count).to eq 1
- expect(issue.events[0].attributes)
- .to include expected_event_attrs
+ it 'creates expected event and state event' do
+ importer.execute(issue_event)
- expect(issue.resource_state_events.count).to eq 1
- expect(issue.resource_state_events[0].attributes)
- .to include expected_state_event_attrs
+ expect(issuable.events.count).to eq 1
+ state_event = issuable.resource_state_events.last
+ expect(state_event.source_commit).to eq commit_id[0..40]
+ end
+ end
end
- context 'when closed by commit' do
- let!(:closing_commit) { create(:commit, project: project) }
- let(:commit_id) { closing_commit.id }
+ context 'with Issue' do
+ let(:expected_state_event_attrs) do
+ {
+ user_id: user.id,
+ issue_id: issuable.id,
+ state: 'closed',
+ created_at: issue_event.created_at
+ }.stringify_keys
+ end
- it 'creates expected event and state event' do
- importer.execute(issue_event)
+ it_behaves_like 'new event'
+ end
- expect(issue.events.count).to eq 1
- state_event = issue.resource_state_events.last
- expect(state_event.source_commit).to eq commit_id[0..40]
+ context 'with MergeRequest' do
+ let(:issuable) { create(:merge_request, source_project: project, target_project: project) }
+ let(:expected_state_event_attrs) do
+ {
+ user_id: user.id,
+ merge_request_id: issuable.id,
+ state: 'closed',
+ created_at: issue_event.created_at
+ }.stringify_keys
end
+
+ it_behaves_like 'new event'
end
end
diff --git a/spec/lib/gitlab/github_import/importer/events/cross_referenced_spec.rb b/spec/lib/gitlab/github_import/importer/events/cross_referenced_spec.rb
index 68e001c7364..bf19147d4c8 100644
--- a/spec/lib/gitlab/github_import/importer/events/cross_referenced_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/events/cross_referenced_spec.rb
@@ -9,9 +9,8 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::CrossReferenced, :clean_g
let_it_be(:user) { create(:user) }
let(:client) { instance_double('Gitlab::GithubImport::Client') }
-
let(:issue_iid) { 999 }
- let(:issue) { create(:issue, project: project, iid: issue_iid) }
+ let(:issuable) { create(:issue, project: project, iid: issue_iid) }
let(:referenced_in) { build_stubbed(:issue, project: project, iid: issue_iid + 1) }
let(:commit_id) { nil }
@@ -30,7 +29,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::CrossReferenced, :clean_g
}
},
'created_at' => '2022-04-26 18:30:53 UTC',
- 'issue' => { 'number' => issue.iid }
+ 'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
)
end
@@ -38,8 +37,8 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::CrossReferenced, :clean_g
let(:expected_note_attrs) do
{
system: true,
- noteable_type: Issue.name,
- noteable_id: issue.id,
+ noteable_type: issuable.class.name,
+ noteable_id: issuable.id,
project_id: project.id,
author_id: user.id,
note: expected_note_body,
@@ -47,58 +46,70 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::CrossReferenced, :clean_g
}.stringify_keys
end
- context 'when referenced in other issue' do
- let(:expected_note_body) { "mentioned in issue ##{referenced_in.iid}" }
-
- before do
- allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
- allow(finder).to receive(:database_id).and_return(referenced_in.iid)
- allow(finder).to receive(:database_id).and_return(issue.id)
- end
- allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
- allow(finder).to receive(:find).with(user.id, user.username).and_return(user.id)
+ shared_examples 'import cross-referenced event' do
+ context 'when referenced in other issue' do
+ let(:expected_note_body) { "mentioned in issue ##{referenced_in.iid}" }
+
+ before do
+ allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
+ allow(finder).to receive(:database_id).and_return(referenced_in.iid)
+ allow(finder).to receive(:database_id).and_return(issuable.id)
+ end
+ allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
+ allow(finder).to receive(:find).with(user.id, user.username).and_return(user.id)
+ end
end
- end
- it 'creates expected note' do
- importer.execute(issue_event)
+ it 'creates expected note' do
+ importer.execute(issue_event)
- expect(issue.notes.count).to eq 1
- expect(issue.notes[0]).to have_attributes expected_note_attrs
- expect(issue.notes[0].system_note_metadata.action).to eq 'cross_reference'
+ expect(issuable.notes.count).to eq 1
+ expect(issuable.notes[0]).to have_attributes expected_note_attrs
+ expect(issuable.notes[0].system_note_metadata.action).to eq 'cross_reference'
+ end
end
- end
- context 'when referenced in pull request' do
- let(:referenced_in) { build_stubbed(:merge_request, project: project) }
- let(:pull_request_resource) { { 'id' => referenced_in.iid } }
+ context 'when referenced in pull request' do
+ let(:referenced_in) { build_stubbed(:merge_request, project: project) }
+ let(:pull_request_resource) { { 'id' => referenced_in.iid } }
- let(:expected_note_body) { "mentioned in merge request !#{referenced_in.iid}" }
+ let(:expected_note_body) { "mentioned in merge request !#{referenced_in.iid}" }
- before do
- allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
- allow(finder).to receive(:database_id).and_return(referenced_in.iid)
- allow(finder).to receive(:database_id).and_return(issue.id)
+ before do
+ allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
+ allow(finder).to receive(:database_id).and_return(referenced_in.iid)
+ allow(finder).to receive(:database_id).and_return(issuable.id)
+ end
+ allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
+ allow(finder).to receive(:find).with(user.id, user.username).and_return(user.id)
+ end
end
- allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
- allow(finder).to receive(:find).with(user.id, user.username).and_return(user.id)
+
+ it 'creates expected note' do
+ importer.execute(issue_event)
+
+ expect(issuable.notes.count).to eq 1
+ expect(issuable.notes[0]).to have_attributes expected_note_attrs
+ expect(issuable.notes[0].system_note_metadata.action).to eq 'cross_reference'
end
end
- it 'creates expected note' do
- importer.execute(issue_event)
+ context 'when referenced in out of project issue/pull_request' do
+ it 'does not create expected note' do
+ importer.execute(issue_event)
- expect(issue.notes.count).to eq 1
- expect(issue.notes[0]).to have_attributes expected_note_attrs
- expect(issue.notes[0].system_note_metadata.action).to eq 'cross_reference'
+ expect(issuable.notes.count).to eq 0
+ end
end
end
- context 'when referenced in out of project issue/pull_request' do
- it 'does not create expected note' do
- importer.execute(issue_event)
+ context 'with Issue' do
+ it_behaves_like 'import cross-referenced event'
+ end
- expect(issue.notes.count).to eq 0
- end
+ context 'with MergeRequest' do
+ let(:issuable) { create(:merge_request, source_project: project, target_project: project) }
+
+ it_behaves_like 'import cross-referenced event'
end
end
diff --git a/spec/lib/gitlab/github_import/importer/events/renamed_spec.rb b/spec/lib/gitlab/github_import/importer/events/renamed_spec.rb
index 316ea798965..29598cb4354 100644
--- a/spec/lib/gitlab/github_import/importer/events/renamed_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/events/renamed_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Renamed do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
- let(:issue) { create(:issue, project: project) }
+ let(:issuable) { create(:issue, project: project) }
let(:client) { instance_double('Gitlab::GithubImport::Client') }
let(:issue_event) do
@@ -20,14 +20,14 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Renamed do
'created_at' => '2022-04-26 18:30:53 UTC',
'old_title' => 'old title',
'new_title' => 'new title',
- 'issue' => { 'number' => issue.iid }
+ 'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
)
end
let(:expected_note_attrs) do
{
- noteable_id: issue.id,
- noteable_type: Issue.name,
+ noteable_id: issuable.id,
+ noteable_type: issuable.class.name,
project_id: project.id,
author_id: user.id,
note: "changed title from **{-old-} title** to **{+new+} title**",
@@ -48,31 +48,43 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Renamed do
describe '#execute' do
before do
allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
- allow(finder).to receive(:database_id).and_return(issue.id)
+ allow(finder).to receive(:database_id).and_return(issuable.id)
end
allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
allow(finder).to receive(:find).with(user.id, user.username).and_return(user.id)
end
end
- it 'creates expected note' do
- expect { importer.execute(issue_event) }.to change { issue.notes.count }
- .from(0).to(1)
+ shared_examples 'import renamed event' do
+ it 'creates expected note' do
+ expect { importer.execute(issue_event) }.to change { issuable.notes.count }
+ .from(0).to(1)
- expect(issue.notes.last)
- .to have_attributes(expected_note_attrs)
- end
+ expect(issuable.notes.last)
+ .to have_attributes(expected_note_attrs)
+ end
- it 'creates expected system note metadata' do
- expect { importer.execute(issue_event) }.to change { SystemNoteMetadata.count }
- .from(0).to(1)
+ it 'creates expected system note metadata' do
+ expect { importer.execute(issue_event) }.to change { SystemNoteMetadata.count }
+ .from(0).to(1)
- expect(SystemNoteMetadata.last)
- .to have_attributes(
- expected_system_note_metadata_attrs.merge(
- note_id: Note.last.id
+ expect(SystemNoteMetadata.last)
+ .to have_attributes(
+ expected_system_note_metadata_attrs.merge(
+ note_id: Note.last.id
+ )
)
- )
+ end
+ end
+
+ context 'with Issue' do
+ it_behaves_like 'import renamed event'
+ end
+
+ context 'with MergeRequest' do
+ let(:issuable) { create(:merge_request, source_project: project, target_project: project) }
+
+ it_behaves_like 'import renamed event'
end
end
end
diff --git a/spec/lib/gitlab/github_import/importer/events/reopened_spec.rb b/spec/lib/gitlab/github_import/importer/events/reopened_spec.rb
index 2461dbb9701..354003fc997 100644
--- a/spec/lib/gitlab/github_import/importer/events/reopened_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/events/reopened_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Reopened, :aggregate_fail
let_it_be(:user) { create(:user) }
let(:client) { instance_double('Gitlab::GithubImport::Client') }
- let(:issue) { create(:issue, project: project) }
+ let(:issuable) { create(:issue, project: project) }
let(:issue_event) do
Gitlab::GithubImport::Representation::IssueEvent.from_json_hash(
@@ -19,7 +19,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Reopened, :aggregate_fail
'actor' => { 'id' => user.id, 'login' => user.username },
'event' => 'reopened',
'created_at' => '2022-04-26 18:30:53 UTC',
- 'issue' => { 'number' => issue.iid }
+ 'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
)
end
@@ -27,40 +27,61 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::Reopened, :aggregate_fail
{
project_id: project.id,
author_id: user.id,
- target_id: issue.id,
- target_type: Issue.name,
+ target_id: issuable.id,
+ target_type: issuable.class.name,
action: 'reopened',
created_at: issue_event.created_at,
updated_at: issue_event.created_at
}.stringify_keys
end
- let(:expected_state_event_attrs) do
- {
- user_id: user.id,
- state: 'reopened',
- created_at: issue_event.created_at
- }.stringify_keys
- end
-
before do
allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
- allow(finder).to receive(:database_id).and_return(issue.id)
+ allow(finder).to receive(:database_id).and_return(issuable.id)
end
allow_next_instance_of(Gitlab::GithubImport::UserFinder) do |finder|
allow(finder).to receive(:find).with(user.id, user.username).and_return(user.id)
end
end
- it 'creates expected event and state event' do
- importer.execute(issue_event)
+ shared_examples 'new event' do
+ it 'creates expected event and state event' do
+ importer.execute(issue_event)
- expect(issue.events.count).to eq 1
- expect(issue.events[0].attributes)
- .to include expected_event_attrs
+ expect(issuable.events.count).to eq 1
+ expect(issuable.events[0].attributes)
+ .to include expected_event_attrs
+
+ expect(issuable.resource_state_events.count).to eq 1
+ expect(issuable.resource_state_events[0].attributes)
+ .to include expected_state_event_attrs
+ end
+ end
+
+ context 'with Issue' do
+ let(:expected_state_event_attrs) do
+ {
+ user_id: user.id,
+ issue_id: issuable.id,
+ state: 'reopened',
+ created_at: issue_event.created_at
+ }.stringify_keys
+ end
+
+ it_behaves_like 'new event'
+ end
+
+ context 'with MergeRequest' do
+ let(:issuable) { create(:merge_request, source_project: project, target_project: project) }
+ let(:expected_state_event_attrs) do
+ {
+ user_id: user.id,
+ merge_request_id: issuable.id,
+ state: 'reopened',
+ created_at: issue_event.created_at
+ }.stringify_keys
+ end
- expect(issue.resource_state_events.count).to eq 1
- expect(issue.resource_state_events[0].attributes)
- .to include expected_state_event_attrs
+ it_behaves_like 'new event'
end
end
diff --git a/spec/lib/gitlab/github_import/importer/issue_and_label_links_importer_spec.rb b/spec/lib/gitlab/github_import/importer/issue_and_label_links_importer_spec.rb
index 49a76fb5e6b..d28640a4f07 100644
--- a/spec/lib/gitlab/github_import/importer/issue_and_label_links_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/issue_and_label_links_importer_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Importer::IssueAndLabelLinksImporter do
describe '#execute' do
diff --git a/spec/lib/gitlab/github_import/importer/issue_event_importer_spec.rb b/spec/lib/gitlab/github_import/importer/issue_event_importer_spec.rb
index 33d5fbf13a0..91121f3c3fc 100644
--- a/spec/lib/gitlab/github_import/importer/issue_event_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/issue_event_importer_spec.rb
@@ -42,10 +42,6 @@ RSpec.describe Gitlab::GithubImport::Importer::IssueEventImporter, :clean_gitlab
end
describe '#execute' do
- before do
- issue_event.attributes[:issue_db_id] = issue.id
- end
-
context "when it's closed issue event" do
let(:event_name) { 'closed' }
@@ -116,6 +112,20 @@ RSpec.describe Gitlab::GithubImport::Importer::IssueEventImporter, :clean_gitlab
Gitlab::GithubImport::Importer::Events::ChangedAssignee
end
+ context "when it's review_requested issue event" do
+ let(:event_name) { 'review_requested' }
+
+ it_behaves_like 'triggers specific event importer',
+ Gitlab::GithubImport::Importer::Events::ChangedReviewer
+ end
+
+ context "when it's review_request_removed issue event" do
+ let(:event_name) { 'review_request_removed' }
+
+ it_behaves_like 'triggers specific event importer',
+ Gitlab::GithubImport::Importer::Events::ChangedReviewer
+ end
+
context "when it's unknown issue event" do
let(:event_name) { 'fake' }
diff --git a/spec/lib/gitlab/github_import/importer/issue_events_importer_spec.rb b/spec/lib/gitlab/github_import/importer/issue_events_importer_spec.rb
index 8d4c1b01e50..2c1af4f8948 100644
--- a/spec/lib/gitlab/github_import/importer/issue_events_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/issue_events_importer_spec.rb
@@ -11,8 +11,8 @@ RSpec.describe Gitlab::GithubImport::Importer::IssueEventsImporter do
let(:parallel) { true }
let(:issue_event) do
struct = Struct.new(
- :id, :node_id, :url, :actor, :event, :commit_id, :commit_url, :label, :rename, :milestone,
- :source, :assignee, :assigner, :issue, :created_at, :performed_via_github_app,
+ :id, :node_id, :url, :actor, :event, :commit_id, :commit_url, :label, :rename, :milestone, :source,
+ :assignee, :assigner, :review_requester, :requested_reviewer, :issue, :created_at, :performed_via_github_app,
keyword_init: true
)
struct.new(id: rand(10), event: 'closed', created_at: '2022-04-26 18:30:53 UTC')
diff --git a/spec/lib/gitlab/github_import/importer/protected_branch_importer_spec.rb b/spec/lib/gitlab/github_import/importer/protected_branch_importer_spec.rb
new file mode 100644
index 00000000000..6dc6db739f4
--- /dev/null
+++ b/spec/lib/gitlab/github_import/importer/protected_branch_importer_spec.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Importer::ProtectedBranchImporter do
+ subject(:importer) { described_class.new(github_protected_branch, project, client) }
+
+ let(:allow_force_pushes_on_github) { true }
+ let(:github_protected_branch) do
+ Gitlab::GithubImport::Representation::ProtectedBranch.new(
+ id: 'protection',
+ allow_force_pushes: allow_force_pushes_on_github
+ )
+ end
+
+ let(:project) { create(:project, :repository) }
+ let(:client) { instance_double('Gitlab::GithubImport::Client') }
+
+ describe '#execute' do
+ let(:create_service) { instance_double('ProtectedBranches::CreateService') }
+
+ shared_examples 'create branch protection by the strictest ruleset' do
+ let(:expected_ruleset) do
+ {
+ name: 'protection',
+ push_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }],
+ merge_access_levels_attributes: [{ access_level: Gitlab::Access::MAINTAINER }],
+ allow_force_push: expected_allow_force_push
+ }
+ end
+
+ it 'calls service with the correct arguments' do
+ expect(ProtectedBranches::CreateService).to receive(:new).with(
+ project,
+ project.creator,
+ expected_ruleset
+ ).and_return(create_service)
+
+ expect(create_service).to receive(:execute).with(skip_authorization: true)
+ importer.execute
+ end
+
+ it 'creates protected branch and access levels for given github rule' do
+ expect { importer.execute }.to change(ProtectedBranch, :count).by(1)
+ .and change(ProtectedBranch::PushAccessLevel, :count).by(1)
+ .and change(ProtectedBranch::MergeAccessLevel, :count).by(1)
+ end
+ end
+
+ context 'when branch is protected on GitLab' do
+ before do
+ create(
+ :protected_branch,
+ project: project,
+ name: 'protect*',
+ allow_force_push: allow_force_pushes_on_gitlab
+ )
+ end
+
+ context 'when branch protection rule on Gitlab is stricter than on Github' do
+ let(:allow_force_pushes_on_github) { true }
+ let(:allow_force_pushes_on_gitlab) { false }
+ let(:expected_allow_force_push) { false }
+
+ it_behaves_like 'create branch protection by the strictest ruleset'
+ end
+
+ context 'when branch protection rule on Github is stricter than on Gitlab' do
+ let(:allow_force_pushes_on_github) { false }
+ let(:allow_force_pushes_on_gitlab) { true }
+ let(:expected_allow_force_push) { false }
+
+ it_behaves_like 'create branch protection by the strictest ruleset'
+ end
+
+ context 'when branch protection rules on Github and Gitlab are the same' do
+ let(:allow_force_pushes_on_github) { true }
+ let(:allow_force_pushes_on_gitlab) { true }
+ let(:expected_allow_force_push) { true }
+
+ it_behaves_like 'create branch protection by the strictest ruleset'
+ end
+ end
+
+ context 'when branch is not protected on GitLab' do
+ let(:expected_allow_force_push) { true }
+
+ it_behaves_like 'create branch protection by the strictest ruleset'
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/importer/protected_branches_importer_spec.rb b/spec/lib/gitlab/github_import/importer/protected_branches_importer_spec.rb
new file mode 100644
index 00000000000..4e9208be985
--- /dev/null
+++ b/spec/lib/gitlab/github_import/importer/protected_branches_importer_spec.rb
@@ -0,0 +1,225 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Importer::ProtectedBranchesImporter do
+ subject(:importer) { described_class.new(project, client, parallel: parallel) }
+
+ let(:project) { instance_double('Project', id: 4, import_source: 'foo/bar') }
+ let(:client) { instance_double('Gitlab::GithubImport::Client') }
+ let(:parallel) { true }
+
+ let(:branches) do
+ branch = Struct.new(:name, :protection, keyword_init: true)
+ protection = Struct.new(:enabled, keyword_init: true)
+
+ [
+ branch.new(name: 'main', protection: protection.new(enabled: false)),
+ branch.new(name: 'staging', protection: protection.new(enabled: true)),
+ branch.new(name: 'development', protection: nil) # when user has no admin right for this repo
+ ]
+ end
+
+ let(:github_protection_rule) do
+ response = Struct.new(:name, :url, :required_signatures, :enforce_admins, :required_linear_history,
+ :allow_force_pushes, :allow_deletion, :block_creations, :required_conversation_resolution,
+ keyword_init: true
+ )
+ required_signatures = Struct.new(:url, :enabled, keyword_init: true)
+ enforce_admins = Struct.new(:url, :enabled, keyword_init: true)
+ allow_option = Struct.new(:enabled, keyword_init: true)
+ response.new(
+ name: 'main',
+ url: 'https://example.com/branches/main/protection',
+ required_signatures: required_signatures.new(
+ url: 'https://example.com/branches/main/protection/required_signatures',
+ enabled: false
+ ),
+ enforce_admins: enforce_admins.new(
+ url: 'https://example.com/branches/main/protection/enforce_admins',
+ enabled: false
+ ),
+ required_linear_history: allow_option.new(
+ enabled: false
+ ),
+ allow_force_pushes: allow_option.new(
+ enabled: false
+ ),
+ allow_deletion: allow_option.new(
+ enabled: false
+ ),
+ block_creations: allow_option.new(
+ enabled: true
+ ),
+ required_conversation_resolution: allow_option.new(
+ enabled: false
+ )
+ )
+ end
+
+ describe '#parallel?' do
+ context 'when running in parallel mode' do
+ it { expect(importer).to be_parallel }
+ end
+
+ context 'when running in sequential mode' do
+ let(:parallel) { false }
+
+ it { expect(importer).not_to be_parallel }
+ end
+ end
+
+ describe '#execute' do
+ context 'when running in parallel mode' do
+ it 'imports protected branches in parallel' do
+ expect(importer).to receive(:parallel_import)
+
+ importer.execute
+ end
+ end
+
+ context 'when running in sequential mode' do
+ let(:parallel) { false }
+
+ it 'imports protected branches in sequence' do
+ expect(importer).to receive(:sequential_import)
+
+ importer.execute
+ end
+ end
+ end
+
+ describe '#sequential_import', :clean_gitlab_redis_cache do
+ let(:parallel) { false }
+
+ before do
+ allow(client).to receive(:branches).and_return(branches)
+ allow(client)
+ .to receive(:branch_protection)
+ .with(project.import_source, 'staging')
+ .and_return(github_protection_rule)
+ .once
+ end
+
+ it 'imports each protected branch in sequence' do
+ protected_branch_importer = instance_double('Gitlab::GithubImport::Importer::ProtectedBranchImporter')
+
+ expect(Gitlab::GithubImport::Importer::ProtectedBranchImporter)
+ .to receive(:new)
+ .with(
+ an_instance_of(Gitlab::GithubImport::Representation::ProtectedBranch),
+ project,
+ client
+ )
+ .and_return(protected_branch_importer)
+
+ expect(protected_branch_importer).to receive(:execute)
+ expect(Gitlab::GithubImport::ObjectCounter)
+ .to receive(:increment).with(project, :protected_branch, :fetched)
+
+ importer.sequential_import
+ end
+ end
+
+ describe '#parallel_import', :clean_gitlab_redis_cache do
+ before do
+ allow(client).to receive(:branches).and_return(branches)
+ allow(client)
+ .to receive(:branch_protection)
+ .with(project.import_source, 'staging')
+ .and_return(github_protection_rule)
+ .once
+ end
+
+ it 'imports each protected branch in parallel' do
+ expect(Gitlab::GithubImport::ImportProtectedBranchWorker)
+ .to receive(:bulk_perform_in)
+ .with(
+ 1.second,
+ [[project.id, an_instance_of(Hash), an_instance_of(String)]],
+ batch_delay: 1.minute,
+ batch_size: 1000
+ )
+ expect(Gitlab::GithubImport::ObjectCounter)
+ .to receive(:increment).with(project, :protected_branch, :fetched)
+
+ waiter = importer.parallel_import
+
+ expect(waiter).to be_an_instance_of(Gitlab::JobWaiter)
+ expect(waiter.jobs_remaining).to eq(1)
+ end
+ end
+
+ describe '#each_object_to_import', :clean_gitlab_redis_cache do
+ let(:branch_struct) { Struct.new(:protection, :name, :url, keyword_init: true) }
+ let(:protection_struct) { Struct.new(:enabled, keyword_init: true) }
+ let(:protected_branch) { branch_struct.new(name: 'main', protection: protection_struct.new(enabled: true)) }
+ let(:unprotected_branch) { branch_struct.new(name: 'staging', protection: protection_struct.new(enabled: false)) }
+ # when user has no admin rights on repo
+ let(:unknown_protection_branch) { branch_struct.new(name: 'development', protection: nil) }
+
+ let(:page_counter) { instance_double(Gitlab::GithubImport::PageCounter) }
+
+ before do
+ allow(client).to receive(:branches).with(project.import_source)
+ .and_return([protected_branch, unprotected_branch, unknown_protection_branch])
+ allow(client).to receive(:branch_protection)
+ .with(project.import_source, protected_branch.name).once
+ .and_return(github_protection_rule)
+ allow(Gitlab::GithubImport::ObjectCounter).to receive(:increment)
+ .with(project, :protected_branch, :fetched)
+ end
+
+ it 'imports each protected branch page by page' do
+ subject.each_object_to_import do |object|
+ expect(object).to eq github_protection_rule
+ end
+ expect(Gitlab::GithubImport::ObjectCounter).to have_received(:increment).once
+ end
+
+ context 'when protected branch is already processed' do
+ it "doesn't process this branch" do
+ subject.mark_as_imported(protected_branch)
+
+ subject.each_object_to_import {}
+ expect(Gitlab::GithubImport::ObjectCounter).not_to have_received(:increment)
+ end
+ end
+ end
+
+ describe '#importer_class' do
+ it { expect(importer.importer_class).to eq Gitlab::GithubImport::Importer::ProtectedBranchImporter }
+ end
+
+ describe '#representation_class' do
+ it { expect(importer.representation_class).to eq Gitlab::GithubImport::Representation::ProtectedBranch }
+ end
+
+ describe '#sidekiq_worker_class' do
+ it { expect(importer.sidekiq_worker_class).to eq Gitlab::GithubImport::ImportProtectedBranchWorker }
+ end
+
+ describe '#object_type' do
+ it { expect(importer.object_type).to eq :protected_branch }
+ end
+
+ describe '#collection_method' do
+ it { expect(importer.collection_method).to eq :protected_branches }
+ end
+
+ describe '#id_for_already_imported_cache' do
+ it 'returns the ID of the given protected branch' do
+ expect(importer.id_for_already_imported_cache(github_protection_rule)).to eq('main')
+ end
+ end
+
+ describe '#collection_options' do
+ it 'returns an empty Hash' do
+ # For large projects (e.g. kubernetes/kubernetes) GitHub's API may produce
+ # HTTP 500 errors when using explicit sorting options, regardless of what
+ # order you sort in. Not using any sorting options at all allows us to
+ # work around this.
+ expect(importer.collection_options).to eq({})
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/importer/release_attachments_importer_spec.rb b/spec/lib/gitlab/github_import/importer/release_attachments_importer_spec.rb
new file mode 100644
index 00000000000..4779f9c8982
--- /dev/null
+++ b/spec/lib/gitlab/github_import/importer/release_attachments_importer_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Importer::ReleaseAttachmentsImporter do
+ subject(:importer) { described_class.new(release_attachments, project, client) }
+
+ let_it_be(:project) { create(:project) }
+
+ let(:client) { instance_double('Gitlab::GithubImport::Client') }
+ let(:release) { create(:release, project: project, description: description) }
+ let(:release_attachments) do
+ Gitlab::GithubImport::Representation::ReleaseAttachments
+ .from_json_hash(release_db_id: release.id, description: release.description)
+ end
+
+ let(:doc_url) { 'https://github.com/nickname/public-test-repo/files/9020437/git-cheat-sheet.txt' }
+ let(:image_url) { 'https://user-images.githubusercontent.com/6833842/0cf366b61ef2.jpeg' }
+ let(:description) do
+ <<-TEXT.strip
+ Some text...
+
+ [special-doc](#{doc_url})
+ ![image.jpeg](#{image_url})
+ TEXT
+ end
+
+ describe '#execute' do
+ let(:downloader_stub) { instance_double(Gitlab::GithubImport::AttachmentsDownloader) }
+ let(:tmp_stub_doc) { Tempfile.create('attachment_download_test.txt') }
+ let(:tmp_stub_image) { Tempfile.create('image.jpeg') }
+
+ context 'when importing doc attachment' do
+ before do
+ allow(Gitlab::GithubImport::AttachmentsDownloader).to receive(:new).with(doc_url)
+ .and_return(downloader_stub)
+ allow(Gitlab::GithubImport::AttachmentsDownloader).to receive(:new).with(image_url)
+ .and_return(downloader_stub)
+ allow(downloader_stub).to receive(:perform).and_return(tmp_stub_doc, tmp_stub_image)
+ allow(downloader_stub).to receive(:delete).twice
+
+ allow(UploadService).to receive(:new)
+ .with(project, tmp_stub_doc, FileUploader).and_call_original
+ allow(UploadService).to receive(:new)
+ .with(project, tmp_stub_image, FileUploader).and_call_original
+ end
+
+ it 'updates release description with new attachment url' do
+ importer.execute
+
+ release.reload
+ expect(release.description).to start_with("Some text...\n\n [special-doc](/uploads/")
+ expect(release.description).to include('![image.jpeg](/uploads/')
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/importer/releases_attachments_importer_spec.rb b/spec/lib/gitlab/github_import/importer/releases_attachments_importer_spec.rb
new file mode 100644
index 00000000000..1aeb3462cd5
--- /dev/null
+++ b/spec/lib/gitlab/github_import/importer/releases_attachments_importer_spec.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Importer::ReleasesAttachmentsImporter do
+ subject { described_class.new(project, client) }
+
+ let_it_be(:project) { create(:project) }
+
+ let(:client) { instance_double(Gitlab::GithubImport::Client) }
+
+ describe '#each_object_to_import', :clean_gitlab_redis_cache do
+ let!(:release_1) { create(:release, project: project) }
+ let!(:release_2) { create(:release, project: project) }
+
+ it 'iterates each project release' do
+ list = []
+ subject.each_object_to_import do |object|
+ list << object
+ end
+ expect(list).to contain_exactly(release_1, release_2)
+ end
+
+ context 'when release is already processed' do
+ it "doesn't process this release" do
+ subject.mark_as_imported(release_1)
+
+ list = []
+ subject.each_object_to_import do |object|
+ list << object
+ end
+ expect(list).to contain_exactly(release_2)
+ end
+ end
+ end
+
+ describe '#representation_class' do
+ it { expect(subject.representation_class).to eq(Gitlab::GithubImport::Representation::ReleaseAttachments) }
+ end
+
+ describe '#importer_class' do
+ it { expect(subject.importer_class).to eq(Gitlab::GithubImport::Importer::ReleaseAttachmentsImporter) }
+ end
+
+ describe '#sidekiq_worker_class' do
+ it { expect(subject.sidekiq_worker_class).to eq(Gitlab::GithubImport::ImportReleaseAttachmentsWorker) }
+ end
+
+ describe '#collection_method' do
+ it { expect(subject.collection_method).to eq(:release_attachments) }
+ end
+
+ describe '#object_type' do
+ it { expect(subject.object_type).to eq(:release_attachment) }
+ end
+
+ describe '#id_for_already_imported_cache' do
+ let(:release) { build_stubbed(:release) }
+
+ it { expect(subject.id_for_already_imported_cache(release)).to eq(release.id) }
+ end
+
+ describe '#object_representation' do
+ let(:release) { build_stubbed(:release) }
+
+ it 'returns release attachments representation' do
+ representation = subject.object_representation(release)
+
+ expect(representation.class).to eq subject.representation_class
+ expect(representation.release_db_id).to eq release.id
+ expect(representation.description).to eq release.description
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
index f2730ba74ec..0b8b1922d94 100644
--- a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
@@ -48,7 +48,7 @@ RSpec.describe Gitlab::GithubImport::Importer::RepositoryImporter do
describe '#import_wiki?' do
it 'returns true if the wiki should be imported' do
- repo = double(:repo, has_wiki: true)
+ repo = { has_wiki: true }
expect(client)
.to receive(:repository)
@@ -67,7 +67,7 @@ RSpec.describe Gitlab::GithubImport::Importer::RepositoryImporter do
end
it 'returns false if the GitHub wiki is disabled' do
- repo = double(:repo, has_wiki: false)
+ repo = { has_wiki: false }
expect(client)
.to receive(:repository)
@@ -78,7 +78,7 @@ RSpec.describe Gitlab::GithubImport::Importer::RepositoryImporter do
end
it 'returns false if the wiki has already been imported' do
- repo = double(:repo, has_wiki: true)
+ repo = { has_wiki: true }
expect(client)
.to receive(:repository)
@@ -186,7 +186,7 @@ RSpec.describe Gitlab::GithubImport::Importer::RepositoryImporter do
describe '#import_repository' do
it 'imports the repository' do
- repo = double(:repo, default_branch: 'develop')
+ repo = { default_branch: 'develop' }
expect(client)
.to receive(:repository)
diff --git a/spec/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer_spec.rb b/spec/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer_spec.rb
index bb1ee79ad93..4ed01fd7e0b 100644
--- a/spec/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/single_endpoint_issue_events_importer_spec.rb
@@ -6,7 +6,8 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter
let(:client) { double }
let_it_be(:project) { create(:project, :import_started, import_source: 'http://somegithub.com') }
- let_it_be(:issue) { create(:issue, project: project) }
+
+ let!(:issuable) { create(:issue, project: project) }
subject { described_class.new(project, client, parallel: parallel) }
@@ -35,7 +36,7 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter
end
describe '#page_counter_id' do
- it { expect(subject.page_counter_id(issue)).to eq("issues/#{issue.iid}/issue_timeline") }
+ it { expect(subject.page_counter_id(issuable)).to eq("issues/#{issuable.iid}/issue_timeline") }
end
describe '#id_for_already_imported_cache' do
@@ -51,6 +52,39 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter
end
end
+ describe '#compose_associated_id!' do
+ let(:issuable) { build_stubbed(:issue, iid: 99) }
+ let(:event_resource) { Struct.new(:id, :event, :source, keyword_init: true) }
+
+ context 'when event type is cross-referenced' do
+ let(:event) do
+ source_resource = Struct.new(:issue, keyword_init: true)
+ issue_resource = Struct.new(:id, keyword_init: true)
+ event_resource.new(
+ id: nil,
+ event: 'cross-referenced',
+ source: source_resource.new(issue: issue_resource.new(id: '100500'))
+ )
+ end
+
+ it 'assigns event id' do
+ subject.compose_associated_id!(issuable, event)
+
+ expect(event.id).to eq 'cross-reference#99-in-100500'
+ end
+ end
+
+ context "when event type isn't cross-referenced" do
+ let(:event) { event_resource.new(id: nil, event: 'labeled') }
+
+ it "doesn't assign event id" do
+ subject.compose_associated_id!(issuable, event)
+
+ expect(event.id).to eq nil
+ end
+ end
+ end
+
describe '#each_object_to_import', :clean_gitlab_redis_cache do
let(:issue_event) do
struct = Struct.new(:id, :event, :created_at, :issue, keyword_init: true)
@@ -72,19 +106,37 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter
.with(
:issue_timeline,
project.import_source,
- issue.iid,
+ issuable.iid,
{ state: 'all', sort: 'created', direction: 'asc', page: 1 }
).and_yield(page)
end
- it 'imports each issue event page by page' do
- counter = 0
- subject.each_object_to_import do |object|
- expect(object).to eq issue_event
- expect(issue_event.issue['number']).to eq issue.iid
- counter += 1
+ context 'with issues' do
+ it 'imports each issue event page by page' do
+ counter = 0
+ subject.each_object_to_import do |object|
+ expect(object).to eq issue_event
+ expect(issue_event.issue['number']).to eq issuable.iid
+ expect(issue_event.issue['pull_request']).to eq false
+ counter += 1
+ end
+ expect(counter).to eq 1
+ end
+ end
+
+ context 'with merge requests' do
+ let!(:issuable) { create(:merge_request, source_project: project, target_project: project) }
+
+ it 'imports each merge request event page by page' do
+ counter = 0
+ subject.each_object_to_import do |object|
+ expect(object).to eq issue_event
+ expect(issue_event.issue['number']).to eq issuable.iid
+ expect(issue_event.issue['pull_request']).to eq true
+ counter += 1
+ end
+ expect(counter).to eq 1
end
- expect(counter).to eq 1
end
it 'triggers page number increment' do
@@ -103,7 +155,7 @@ RSpec.describe Gitlab::GithubImport::Importer::SingleEndpointIssueEventsImporter
context 'when page is already processed' do
before do
page_counter = Gitlab::GithubImport::PageCounter.new(
- project, subject.page_counter_id(issue)
+ project, subject.page_counter_id(issuable)
)
page_counter.set(page.number)
end
diff --git a/spec/lib/gitlab/github_import/markdown_text_spec.rb b/spec/lib/gitlab/github_import/markdown_text_spec.rb
index ad45469a4c3..1da6bb06403 100644
--- a/spec/lib/gitlab/github_import/markdown_text_spec.rb
+++ b/spec/lib/gitlab/github_import/markdown_text_spec.rb
@@ -60,6 +60,34 @@ RSpec.describe Gitlab::GithubImport::MarkdownText do
end
end
+ describe '.fetch_attachment_urls' do
+ let(:image_extension) { described_class::MEDIA_TYPES.sample }
+ let(:image_attachment) do
+ "![special-image](https://user-images.githubusercontent.com/6833862/"\
+ "176685788-e7a93168-7ded-406a-82b5-eb1c56685a93.#{image_extension})"
+ end
+
+ let(:doc_extension) { described_class::DOC_TYPES.sample }
+ let(:doc_attachment) do
+ "[some-doc](https://github.com/nickname/public-test-repo/"\
+ "files/9020437/git-cheat-sheet.#{doc_extension})"
+ end
+
+ let(:text) do
+ <<-TEXT
+ Comment with an attachment
+ #{image_attachment}
+ #{FFaker::Lorem.sentence}
+ #{doc_attachment}
+ TEXT
+ end
+
+ it 'fetches attachment urls' do
+ expect(described_class.fetch_attachment_urls(text))
+ .to contain_exactly(image_attachment, doc_attachment)
+ end
+ end
+
describe '#to_s' do
it 'returns the text when the author was found' do
author = double(:author, login: 'Alice')
diff --git a/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb b/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
index 738e7c88d7d..860bb60f3ed 100644
--- a/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
+++ b/spec/lib/gitlab/github_import/parallel_scheduling_spec.rb
@@ -15,6 +15,10 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
Class
end
+ def sidekiq_worker_class
+ Class
+ end
+
def object_type
:dummy
end
diff --git a/spec/lib/gitlab/github_import/representation/diff_notes/suggestion_formatter_spec.rb b/spec/lib/gitlab/github_import/representation/diff_notes/suggestion_formatter_spec.rb
index bcb8575bdbf..5a24f929388 100644
--- a/spec/lib/gitlab/github_import/representation/diff_notes/suggestion_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/diff_notes/suggestion_formatter_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Representation::DiffNotes::SuggestionFormatter do
it 'does nothing when there is any text before the suggestion tag' do
diff --git a/spec/lib/gitlab/github_import/representation/expose_attribute_spec.rb b/spec/lib/gitlab/github_import/representation/expose_attribute_spec.rb
index d40be0e841c..43f0198704f 100644
--- a/spec/lib/gitlab/github_import/representation/expose_attribute_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/expose_attribute_spec.rb
@@ -1,21 +1,41 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Representation::ExposeAttribute do
- it 'defines a getter method that returns an attribute value' do
- klass = Class.new do
+ let(:klass) do
+ Class.new do
include Gitlab::GithubImport::Representation::ExposeAttribute
expose_attribute :number
attr_reader :attributes
- def initialize
- @attributes = { number: 42 }
+ def initialize(attributes)
+ @attributes = attributes
+ end
+ end
+ end
+
+ it 'defines a getter method that returns an attribute value' do
+ expect(klass.new({ number: 42 }).number).to eq(42)
+ end
+
+ describe '#[]' do
+ it 'returns exposed attributes value using array notation' do
+ expect(klass.new({ number: 42 })[:number]).to eq(42)
+ end
+
+ context 'when attribute does not exist' do
+ it 'returns nil' do
+ expect(klass.new({})[:number]).to eq(nil)
end
end
- expect(klass.new.number).to eq(42)
+ context 'when attribute is not exposed' do
+ it 'returns nil' do
+ expect(klass.new({ not_exposed_attribute: 42 })[:not_exposed_attribute]).to eq(nil)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/github_import/representation/issue_event_spec.rb b/spec/lib/gitlab/github_import/representation/issue_event_spec.rb
index d3a98035e73..0256858ecf1 100644
--- a/spec/lib/gitlab/github_import/representation/issue_event_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/issue_event_spec.rb
@@ -43,7 +43,7 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do
let(:with_actor) { false }
it 'does not return such info' do
- expect(issue_event.actor).to eq nil
+ expect(issue_event.actor).to be_nil
end
end
@@ -57,7 +57,7 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do
let(:with_label) { false }
it 'does not return such info' do
- expect(issue_event.label_title).to eq nil
+ expect(issue_event.label_title).to be_nil
end
end
@@ -72,8 +72,8 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do
let(:with_rename) { false }
it 'does not return such info' do
- expect(issue_event.old_title).to eq nil
- expect(issue_event.new_title).to eq nil
+ expect(issue_event.old_title).to be_nil
+ expect(issue_event.new_title).to be_nil
end
end
@@ -87,30 +87,47 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do
let(:with_milestone) { false }
it 'does not return such info' do
- expect(issue_event.milestone_title).to eq nil
+ expect(issue_event.milestone_title).to be_nil
end
end
- context 'when assignee and assigner data is present' do
- it 'includes assignee and assigner details' do
+ context 'when assignee data is present' do
+ it 'includes assignee details' do
expect(issue_event.assignee)
.to be_an_instance_of(Gitlab::GithubImport::Representation::User)
expect(issue_event.assignee.id).to eq(5)
expect(issue_event.assignee.login).to eq('tom')
+ end
+ end
+
+ context 'when assignee data is empty' do
+ let(:with_assignee) { false }
- expect(issue_event.assigner)
+ it 'does not return such info' do
+ expect(issue_event.assignee).to be_nil
+ end
+ end
+
+ context 'when requested_reviewer and review_requester data is present' do
+ it 'includes requested_reviewer and review_requester details' do
+ expect(issue_event.requested_reviewer)
.to be_an_instance_of(Gitlab::GithubImport::Representation::User)
- expect(issue_event.assigner.id).to eq(6)
- expect(issue_event.assigner.login).to eq('jerry')
+ expect(issue_event.requested_reviewer.id).to eq(6)
+ expect(issue_event.requested_reviewer.login).to eq('mickey')
+
+ expect(issue_event.review_requester)
+ .to be_an_instance_of(Gitlab::GithubImport::Representation::User)
+ expect(issue_event.review_requester.id).to eq(7)
+ expect(issue_event.review_requester.login).to eq('minnie')
end
end
- context 'when assignee and assigner data is empty' do
- let(:with_assignee) { false }
+ context 'when requested_reviewer and review_requester data is empty' do
+ let(:with_reviewer) { false }
it 'does not return such info' do
- expect(issue_event.assignee).to eq nil
- expect(issue_event.assigner).to eq nil
+ expect(issue_event.requested_reviewer).to be_nil
+ expect(issue_event.review_requester).to be_nil
end
end
@@ -148,7 +165,8 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do
let(:response) do
event_resource = Struct.new(
:id, :node_id, :url, :actor, :event, :commit_id, :commit_url, :label, :rename, :milestone,
- :source, :assignee, :assigner, :issue, :created_at, :performed_via_github_app,
+ :source, :assignee, :requested_reviewer, :review_requester, :issue, :created_at,
+ :performed_via_github_app,
keyword_init: true
)
user_resource = Struct.new(:id, :login, keyword_init: true)
@@ -166,7 +184,8 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do
milestone: with_milestone ? { title: 'milestone title' } : nil,
source: { type: 'issue', id: 123456 },
assignee: with_assignee ? user_resource.new(id: 5, login: 'tom') : nil,
- assigner: with_assignee ? user_resource.new(id: 6, login: 'jerry') : nil,
+ requested_reviewer: with_reviewer ? user_resource.new(id: 6, login: 'mickey') : nil,
+ review_requester: with_reviewer ? user_resource.new(id: 7, login: 'minnie') : nil,
issue: { 'number' => 2, 'pull_request' => pull_request },
created_at: '2022-04-26 18:30:53 UTC',
performed_via_github_app: nil
@@ -178,6 +197,7 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do
let(:with_rename) { true }
let(:with_milestone) { true }
let(:with_assignee) { true }
+ let(:with_reviewer) { true }
let(:pull_request) { nil }
it_behaves_like 'an IssueEvent' do
@@ -203,7 +223,8 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do
'milestone_title' => (with_milestone ? 'milestone title' : nil),
'source' => { 'type' => 'issue', 'id' => 123456 },
'assignee' => (with_assignee ? { 'id' => 5, 'login' => 'tom' } : nil),
- 'assigner' => (with_assignee ? { 'id' => 6, 'login' => 'jerry' } : nil),
+ 'requested_reviewer' => (with_reviewer ? { 'id' => 6, 'login' => 'mickey' } : nil),
+ 'review_requester' => (with_reviewer ? { 'id' => 7, 'login' => 'minnie' } : nil),
'issue' => { 'number' => 2, 'pull_request' => pull_request },
'created_at' => '2022-04-26 18:30:53 UTC',
'performed_via_github_app' => nil
@@ -215,6 +236,7 @@ RSpec.describe Gitlab::GithubImport::Representation::IssueEvent do
let(:with_rename) { true }
let(:with_milestone) { true }
let(:with_assignee) { true }
+ let(:with_reviewer) { true }
let(:pull_request) { nil }
let(:issue_event) { described_class.from_json_hash(hash) }
diff --git a/spec/lib/gitlab/github_import/representation/lfs_object_spec.rb b/spec/lib/gitlab/github_import/representation/lfs_object_spec.rb
index b59ea513436..6663a7366a5 100644
--- a/spec/lib/gitlab/github_import/representation/lfs_object_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/lfs_object_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Representation::LfsObject do
describe '#github_identifiers' do
diff --git a/spec/lib/gitlab/github_import/representation/note_spec.rb b/spec/lib/gitlab/github_import/representation/note_spec.rb
index 97addcc1c98..9f416eb3c02 100644
--- a/spec/lib/gitlab/github_import/representation/note_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/note_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Representation::Note do
let(:created_at) { Time.new(2017, 1, 1, 12, 00) }
diff --git a/spec/lib/gitlab/github_import/representation/protected_branch_spec.rb b/spec/lib/gitlab/github_import/representation/protected_branch_spec.rb
new file mode 100644
index 00000000000..e762dc469c1
--- /dev/null
+++ b/spec/lib/gitlab/github_import/representation/protected_branch_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Representation::ProtectedBranch do
+ shared_examples 'a ProtectedBranch rule' do
+ it 'returns an instance of ProtectedBranch' do
+ expect(protected_branch).to be_an_instance_of(described_class)
+ end
+
+ context 'with ProtectedBranch' do
+ it 'includes the protected branch ID (name)' do
+ expect(protected_branch.id).to eq 'main'
+ end
+
+ it 'includes the protected branch allow_force_pushes' do
+ expect(protected_branch.allow_force_pushes).to eq true
+ end
+ end
+ end
+
+ describe '.from_api_response' do
+ let(:response) do
+ response = Struct.new(:url, :allow_force_pushes, keyword_init: true)
+ allow_force_pushes = Struct.new(:enabled, keyword_init: true)
+ response.new(
+ url: 'https://example.com/branches/main/protection',
+ allow_force_pushes: allow_force_pushes.new(
+ enabled: true
+ )
+ )
+ end
+
+ it_behaves_like 'a ProtectedBranch rule' do
+ let(:protected_branch) { described_class.from_api_response(response) }
+ end
+ end
+
+ describe '.from_json_hash' do
+ it_behaves_like 'a ProtectedBranch rule' do
+ let(:hash) do
+ {
+ 'id' => 'main',
+ 'allow_force_pushes' => true
+ }
+ end
+
+ let(:protected_branch) { described_class.from_json_hash(hash) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/representation/pull_request_review_spec.rb b/spec/lib/gitlab/github_import/representation/pull_request_review_spec.rb
index f812fd85fbc..d6e7a8172f7 100644
--- a/spec/lib/gitlab/github_import/representation/pull_request_review_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/pull_request_review_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Representation::PullRequestReview do
let(:submitted_at) { Time.new(2017, 1, 1, 12, 00).utc }
diff --git a/spec/lib/gitlab/github_import/representation/pull_request_spec.rb b/spec/lib/gitlab/github_import/representation/pull_request_spec.rb
index 925dba5b5a7..deb9535a845 100644
--- a/spec/lib/gitlab/github_import/representation/pull_request_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/pull_request_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Representation::PullRequest do
let(:created_at) { Time.new(2017, 1, 1, 12, 00) }
diff --git a/spec/lib/gitlab/github_import/representation/release_attachments_spec.rb b/spec/lib/gitlab/github_import/representation/release_attachments_spec.rb
new file mode 100644
index 00000000000..0ef9dad6a13
--- /dev/null
+++ b/spec/lib/gitlab/github_import/representation/release_attachments_spec.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Representation::ReleaseAttachments do
+ shared_examples 'a Release attachments data' do
+ it 'returns an instance of ReleaseAttachments' do
+ expect(representation).to be_an_instance_of(described_class)
+ end
+
+ it 'includes release DB id' do
+ expect(representation.release_db_id).to eq 42
+ end
+
+ it 'includes release description' do
+ expect(representation.description).to eq 'Some text here..'
+ end
+ end
+
+ describe '.from_db_record' do
+ let(:release) { build_stubbed(:release, id: 42, description: 'Some text here..') }
+
+ it_behaves_like 'a Release attachments data' do
+ let(:representation) { described_class.from_db_record(release) }
+ end
+ end
+
+ describe '.from_json_hash' do
+ it_behaves_like 'a Release attachments data' do
+ let(:hash) do
+ {
+ 'release_db_id' => 42,
+ 'description' => 'Some text here..'
+ }
+ end
+
+ let(:representation) { described_class.from_json_hash(hash) }
+ end
+ end
+
+ describe '#github_identifiers' do
+ it 'returns a hash with needed identifiers' do
+ release_id = rand(100)
+ representation = described_class.new(release_db_id: release_id, description: 'text')
+
+ expect(representation.github_identifiers).to eq({ db_id: release_id })
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/representation/to_hash_spec.rb b/spec/lib/gitlab/github_import/representation/to_hash_spec.rb
index 2770e5c5397..739c832025c 100644
--- a/spec/lib/gitlab/github_import/representation/to_hash_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/to_hash_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Representation::ToHash do
describe '#to_hash' do
diff --git a/spec/lib/gitlab/github_import/representation/user_spec.rb b/spec/lib/gitlab/github_import/representation/user_spec.rb
index 14204886e9b..d7219556ada 100644
--- a/spec/lib/gitlab/github_import/representation/user_spec.rb
+++ b/spec/lib/gitlab/github_import/representation/user_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Representation::User do
shared_examples 'a User' do
diff --git a/spec/lib/gitlab/github_import/representation_spec.rb b/spec/lib/gitlab/github_import/representation_spec.rb
index 58c10c4a775..9a0ef45fc1d 100644
--- a/spec/lib/gitlab/github_import/representation_spec.rb
+++ b/spec/lib/gitlab/github_import/representation_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::GithubImport::Representation do
describe '.symbolize_hash' do
diff --git a/spec/lib/gitlab/github_import/user_finder_spec.rb b/spec/lib/gitlab/github_import/user_finder_spec.rb
index d85e298785c..8ebbff31f64 100644
--- a/spec/lib/gitlab/github_import/user_finder_spec.rb
+++ b/spec/lib/gitlab/github_import/user_finder_spec.rb
@@ -68,10 +68,16 @@ RSpec.describe Gitlab::GithubImport::UserFinder, :clean_gitlab_redis_cache do
it_behaves_like 'user ID finder', :assignee
end
- context 'when the author_key parameter is :assigner' do
- let(:issue_event) { double('Gitlab::GithubImport::Representation::IssueEvent', assigner: user) }
+ context 'when the author_key parameter is :requested_reviewer' do
+ let(:issue_event) { double('Gitlab::GithubImport::Representation::IssueEvent', requested_reviewer: user) }
- it_behaves_like 'user ID finder', :assigner
+ it_behaves_like 'user ID finder', :requested_reviewer
+ end
+
+ context 'when the author_key parameter is :review_requester' do
+ let(:issue_event) { double('Gitlab::GithubImport::Representation::IssueEvent', review_requester: user) }
+
+ it_behaves_like 'user ID finder', :review_requester
end
end
end
diff --git a/spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb b/spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb
index 487b19a98e0..5006d27c356 100644
--- a/spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb
+++ b/spec/lib/gitlab/grape_logging/formatters/lograge_with_timestamp_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Gitlab::GrapeLogging::Formatters::LogrageWithTimestamp do
let(:log_entry) do
{
status: 200,
- time: {
+ time: {
total: 758.58,
db: 77.06,
view: 681.52
diff --git a/spec/lib/gitlab/graphql/limit/field_call_count_spec.rb b/spec/lib/gitlab/graphql/limit/field_call_count_spec.rb
new file mode 100644
index 00000000000..5858986dfc8
--- /dev/null
+++ b/spec/lib/gitlab/graphql/limit/field_call_count_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Gitlab::Graphql::Limit::FieldCallCount do
+ include GraphqlHelpers
+
+ let(:field_args) { {} }
+ let(:owner) { fresh_object_type }
+ let(:field) do
+ ::Types::BaseField.new(name: 'value', type: GraphQL::Types::String, null: true, owner: owner) do
+ extension(::Gitlab::Graphql::Limit::FieldCallCount, limit: 1)
+ end
+ end
+
+ let(:query) do
+ GraphQL::Query.new(GitlabSchema)
+ end
+
+ def resolve_value
+ resolve_field(field, { value: 'foo' }, object_type: owner, query: query)
+ end
+
+ it 'allows the call' do
+ expect { resolve_value }.not_to raise_error
+ end
+
+ it 'executes the extension' do
+ expect(described_class).to receive(:new).and_call_original
+
+ resolve_value
+ end
+
+ it 'returns an error when the field is called multiple times' do
+ resolve_value
+
+ expect(resolve_value).to be_an_instance_of(Gitlab::Graphql::Errors::LimitError)
+ 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
+ extension(::Gitlab::Graphql::Limit::FieldCallCount)
+ end
+ end
+
+ it 'returns an error' do
+ expect(resolve_value).to be_an_instance_of(Gitlab::Graphql::Errors::ArgumentError)
+ end
+ end
+
+ context 'when the field is not extended' do
+ let(:field) do
+ ::Types::BaseField.new(name: 'value', type: GraphQL::Types::String, null: true, owner: owner)
+ end
+
+ it 'allows the call' do
+ expect { resolve_value }.not_to raise_error
+ end
+
+ it 'does not execute the extension' do
+ expect(described_class).not_to receive(:new)
+
+ resolve_value
+ end
+ end
+end
diff --git a/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb b/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb
index b54c618d8e0..bf09e98331f 100644
--- a/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb
+++ b/spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb
@@ -49,6 +49,31 @@ RSpec.describe Gitlab::Graphql::Pagination::Keyset::Connection do
Gitlab::Json.parse(Base64Bp.urlsafe_decode64(cursor))
end
+ before do
+ stub_feature_flags(graphql_keyset_pagination_without_next_page_query: false)
+ end
+
+ it 'invokes 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)
+ end
+
+ context 'when the relation is loaded' do
+ it 'invokes no extra query' do
+ allow(subject).to receive(:sliced_nodes).and_return(Project.all.to_a)
+ arguments[:first] = 1
+
+ subject.nodes
+
+ count = ActiveRecord::QueryRecorder.new { subject.has_next_page }.count
+ expect(count).to eq(0)
+ end
+ end
+
describe "with generic keyset order support" do
let(:nodes) { Project.all.order(Gitlab::Pagination::Keyset::Order.build([column_order_id])) }
@@ -412,4 +437,382 @@ 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/graphql/pagination/keyset/last_items_spec.rb b/spec/lib/gitlab/graphql/pagination/keyset/last_items_spec.rb
deleted file mode 100644
index 792cb03e8c7..00000000000
--- a/spec/lib/gitlab/graphql/pagination/keyset/last_items_spec.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::Graphql::Pagination::Keyset::LastItems do
- let_it_be(:merge_request) { create(:merge_request) }
-
- let(:scope) { MergeRequest.order_merged_at_asc }
-
- subject { described_class.take_items(*args) }
-
- context 'when the `count` parameter is nil' do
- let(:args) { [scope, nil] }
-
- it 'returns a single record' do
- expect(subject).to eq(merge_request)
- end
- end
-
- context 'when the `count` parameter is given' do
- let(:args) { [scope, 1] }
-
- it 'returns an array' do
- expect(subject).to eq([merge_request])
- end
- end
-end
diff --git a/spec/lib/gitlab/health_checks/gitaly_check_spec.rb b/spec/lib/gitlab/health_checks/gitaly_check_spec.rb
index 7c346e3eb69..000b8eff661 100644
--- a/spec/lib/gitlab/health_checks/gitaly_check_spec.rb
+++ b/spec/lib/gitlab/health_checks/gitaly_check_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::HealthChecks::GitalyCheck do
let(:result_class) { Gitlab::HealthChecks::Result }
@@ -14,20 +14,36 @@ RSpec.describe Gitlab::HealthChecks::GitalyCheck do
subject { described_class.readiness }
before do
- expect(Gitlab::GitalyClient::HealthCheckService).to receive(:new).and_return(gitaly_check)
+ expect(Gitlab::GitalyClient::HealthCheckService).to receive(:new).and_return(healthy_check)
end
context 'Gitaly server is up' do
- let(:gitaly_check) { double(check: { success: true }) }
+ before do
+ expect(Gitlab::GitalyClient::ServerService).to receive(:new).and_return(ready_check)
+ end
+
+ let(:healthy_check) { double(check: { success: true }) }
+ let(:ready_check) { double(readiness_check: { success: true }) }
it { is_expected.to eq([result_class.new('gitaly_check', true, nil, shard: 'default')]) }
end
context 'Gitaly server is down' do
- let(:gitaly_check) { double(check: { success: false, message: 'Connection refused' }) }
+ let(:healthy_check) { double(check: { success: false, message: 'Connection refused' }) }
it { is_expected.to eq([result_class.new('gitaly_check', false, 'Connection refused', shard: 'default')]) }
end
+
+ context 'Gitaly server is not ready' do
+ before do
+ expect(Gitlab::GitalyClient::ServerService).to receive(:new).and_return(ready_check)
+ end
+
+ let(:healthy_check) { double(check: { success: true }) }
+ let(:ready_check) { double(readiness_check: { success: false, message: 'Clock is out of sync' }) }
+
+ it { is_expected.to match_array([result_class.new('gitaly_check', false, 'Clock is out of sync', shard: 'default')]) }
+ end
end
describe '#metrics' do
diff --git a/spec/lib/gitlab/health_checks/master_check_spec.rb b/spec/lib/gitlab/health_checks/master_check_spec.rb
index 287ebcec207..8a87b01c560 100644
--- a/spec/lib/gitlab/health_checks/master_check_spec.rb
+++ b/spec/lib/gitlab/health_checks/master_check_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
require_relative './simple_check_shared'
RSpec.describe Gitlab::HealthChecks::MasterCheck do
diff --git a/spec/lib/gitlab/health_checks/probes/collection_spec.rb b/spec/lib/gitlab/health_checks/probes/collection_spec.rb
index 741c45d953c..f1791375cea 100644
--- a/spec/lib/gitlab/health_checks/probes/collection_spec.rb
+++ b/spec/lib/gitlab/health_checks/probes/collection_spec.rb
@@ -12,18 +12,16 @@ RSpec.describe Gitlab::HealthChecks::Probes::Collection do
let(:checks) do
[
Gitlab::HealthChecks::DbCheck,
- Gitlab::HealthChecks::Redis::RedisCheck,
- Gitlab::HealthChecks::Redis::CacheCheck,
- Gitlab::HealthChecks::Redis::QueuesCheck,
- Gitlab::HealthChecks::Redis::SharedStateCheck,
- Gitlab::HealthChecks::Redis::TraceChunksCheck,
- Gitlab::HealthChecks::Redis::RateLimitingCheck,
- Gitlab::HealthChecks::Redis::SessionsCheck,
+ *Gitlab::HealthChecks::Redis::ALL_INSTANCE_CHECKS,
Gitlab::HealthChecks::GitalyCheck
]
end
it 'responds with readiness checks data' do
+ expect_next_instance_of(Gitlab::GitalyClient::ServerService) do |service|
+ expect(service).to receive(:readiness_check).and_return({ success: true })
+ end
+
expect(subject.http_status).to eq(200)
expect(subject.json[:status]).to eq('ok')
@@ -37,8 +35,8 @@ RSpec.describe Gitlab::HealthChecks::Probes::Collection do
context 'when Redis fails' do
before do
- allow(Gitlab::HealthChecks::Redis::RedisCheck).to receive(:readiness).and_return(
- Gitlab::HealthChecks::Result.new('redis_check', false, "check error"))
+ allow(Gitlab::HealthChecks::Redis::SharedStateCheck).to receive(:readiness).and_return(
+ Gitlab::HealthChecks::Result.new('shared_state_check', false, "check error"))
end
it 'responds with failure' do
@@ -46,14 +44,14 @@ RSpec.describe Gitlab::HealthChecks::Probes::Collection do
expect(subject.json[:status]).to eq('failed')
expect(subject.json['cache_check']).to contain_exactly(status: 'ok')
- expect(subject.json['redis_check']).to contain_exactly(
+ expect(subject.json['shared_state_check']).to contain_exactly(
status: 'failed', message: 'check error')
end
end
context 'when check raises exception not handled inside the check' do
before do
- expect(Gitlab::HealthChecks::Redis::RedisCheck).to receive(:readiness).and_raise(
+ expect(Gitlab::HealthChecks::Redis::CacheCheck).to receive(:readiness).and_raise(
::Redis::CannotConnectError, 'Redis down')
end
diff --git a/spec/lib/gitlab/health_checks/redis/cache_check_spec.rb b/spec/lib/gitlab/health_checks/redis/cache_check_spec.rb
deleted file mode 100644
index c44bd2ed585..00000000000
--- a/spec/lib/gitlab/health_checks/redis/cache_check_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-require_relative '../simple_check_shared'
-
-RSpec.describe Gitlab::HealthChecks::Redis::CacheCheck do
- include_examples 'simple_check', 'redis_cache_ping', 'RedisCache', 'PONG'
-end
diff --git a/spec/lib/gitlab/health_checks/redis/queues_check_spec.rb b/spec/lib/gitlab/health_checks/redis/queues_check_spec.rb
deleted file mode 100644
index 3882e7db9d9..00000000000
--- a/spec/lib/gitlab/health_checks/redis/queues_check_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-require_relative '../simple_check_shared'
-
-RSpec.describe Gitlab::HealthChecks::Redis::QueuesCheck do
- include_examples 'simple_check', 'redis_queues_ping', 'RedisQueues', 'PONG'
-end
diff --git a/spec/lib/gitlab/health_checks/redis/rate_limiting_check_spec.rb b/spec/lib/gitlab/health_checks/redis/rate_limiting_check_spec.rb
deleted file mode 100644
index 1521fc99cde..00000000000
--- a/spec/lib/gitlab/health_checks/redis/rate_limiting_check_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-require_relative '../simple_check_shared'
-
-RSpec.describe Gitlab::HealthChecks::Redis::RateLimitingCheck do
- include_examples 'simple_check', 'redis_rate_limiting_ping', 'RedisRateLimiting', 'PONG'
-end
diff --git a/spec/lib/gitlab/health_checks/redis/redis_check_spec.rb b/spec/lib/gitlab/health_checks/redis/redis_check_spec.rb
deleted file mode 100644
index 145d573b6de..00000000000
--- a/spec/lib/gitlab/health_checks/redis/redis_check_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-require_relative '../simple_check_shared'
-
-RSpec.describe Gitlab::HealthChecks::Redis::RedisCheck do
- include_examples 'simple_check', 'redis_ping', 'Redis', true
-end
diff --git a/spec/lib/gitlab/health_checks/redis/sessions_check_spec.rb b/spec/lib/gitlab/health_checks/redis/sessions_check_spec.rb
deleted file mode 100644
index 82b3b33ec0a..00000000000
--- a/spec/lib/gitlab/health_checks/redis/sessions_check_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-require_relative '../simple_check_shared'
-
-RSpec.describe Gitlab::HealthChecks::Redis::SessionsCheck do
- include_examples 'simple_check', 'redis_sessions_ping', 'RedisSessions', 'PONG'
-end
diff --git a/spec/lib/gitlab/health_checks/redis/shared_state_check_spec.rb b/spec/lib/gitlab/health_checks/redis/shared_state_check_spec.rb
deleted file mode 100644
index 25917741a1c..00000000000
--- a/spec/lib/gitlab/health_checks/redis/shared_state_check_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-require_relative '../simple_check_shared'
-
-RSpec.describe Gitlab::HealthChecks::Redis::SharedStateCheck do
- include_examples 'simple_check', 'redis_shared_state_ping', 'RedisSharedState', 'PONG'
-end
diff --git a/spec/lib/gitlab/health_checks/redis/trace_chunks_check_spec.rb b/spec/lib/gitlab/health_checks/redis/trace_chunks_check_spec.rb
deleted file mode 100644
index 5fb5232a4dd..00000000000
--- a/spec/lib/gitlab/health_checks/redis/trace_chunks_check_spec.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-require_relative '../simple_check_shared'
-
-RSpec.describe Gitlab::HealthChecks::Redis::TraceChunksCheck do
- include_examples 'simple_check', 'redis_trace_chunks_ping', 'RedisTraceChunks', 'PONG'
-end
diff --git a/spec/lib/gitlab/health_checks/redis_spec.rb b/spec/lib/gitlab/health_checks/redis_spec.rb
new file mode 100644
index 00000000000..2460f57a9ec
--- /dev/null
+++ b/spec/lib/gitlab/health_checks/redis_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+require 'spec_helper'
+require_relative './simple_check_shared'
+
+RSpec.describe Gitlab::HealthChecks::Redis do
+ describe "ALL_INSTANCE_CHECKS" do
+ subject { described_class::ALL_INSTANCE_CHECKS }
+
+ it { is_expected.to include(described_class::CacheCheck, described_class::QueuesCheck) }
+
+ it "contains a check for each redis instance" do
+ expect(subject.map(&:redis_instance_class_name)).to contain_exactly(*Gitlab::Redis::ALL_CLASSES)
+ end
+ end
+
+ describe 'all checks' do
+ described_class::ALL_INSTANCE_CHECKS.each do |check|
+ describe check do
+ include_examples 'simple_check',
+ "redis_#{check.redis_instance_class_name.store_name.underscore}_ping",
+ check.redis_instance_class_name.store_name,
+ 'PONG'
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/i18n/metadata_entry_spec.rb b/spec/lib/gitlab/i18n/metadata_entry_spec.rb
index 2f8816e62cc..fcdf3358570 100644
--- a/spec/lib/gitlab/i18n/metadata_entry_spec.rb
+++ b/spec/lib/gitlab/i18n/metadata_entry_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::I18n::MetadataEntry do
describe '#expected_forms' do
diff --git a/spec/lib/gitlab/i18n/translation_entry_spec.rb b/spec/lib/gitlab/i18n/translation_entry_spec.rb
index f05346d07d3..df503e68cf1 100644
--- a/spec/lib/gitlab/i18n/translation_entry_spec.rb
+++ b/spec/lib/gitlab/i18n/translation_entry_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::I18n::TranslationEntry do
describe '#singular_translation' do
diff --git a/spec/lib/gitlab/import/merge_request_creator_spec.rb b/spec/lib/gitlab/import/merge_request_creator_spec.rb
index 9aedca40f1b..8f502216294 100644
--- a/spec/lib/gitlab/import/merge_request_creator_spec.rb
+++ b/spec/lib/gitlab/import/merge_request_creator_spec.rb
@@ -8,10 +8,13 @@ RSpec.describe Gitlab::Import::MergeRequestCreator do
subject { described_class.new(project) }
describe '#execute' do
+ let(:attributes) do
+ HashWithIndifferentAccess.new(merge_request.attributes.except('merge_params', 'suggested_reviewers'))
+ end
+
context 'merge request already exists' do
let(:merge_request) { create(:merge_request, target_project: project, source_project: project) }
let(:commits) { merge_request.merge_request_diffs.first.commits }
- let(:attributes) { HashWithIndifferentAccess.new(merge_request.attributes.except("merge_params")) }
it 'updates the data' do
commits_count = commits.count
@@ -31,7 +34,6 @@ RSpec.describe Gitlab::Import::MergeRequestCreator do
context 'new merge request' do
let(:merge_request) { build(:merge_request, target_project: project, source_project: project) }
- let(:attributes) { HashWithIndifferentAccess.new(merge_request.attributes.except("merge_params")) }
it 'creates a new merge request' do
attributes.delete(:id)
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 9aec3271913..e270ca9ec6a 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -211,6 +211,8 @@ merge_requests:
- user_note_authors
- cleanup_schedule
- compliance_violations
+- created_environments
+- predictions
external_pull_requests:
- project
merge_request_diff:
@@ -315,6 +317,7 @@ statuses:
- user
- auto_canceled_by
- needs
+- ci_stage
variables:
- project
triggers:
@@ -654,11 +657,9 @@ search_data:
merge_request_assignees:
- merge_request
- assignee
-- updated_state_by
merge_request_reviewers:
- merge_request
- reviewer
-- updated_state_by
lfs_file_locks:
- user
project_badges:
@@ -821,3 +822,28 @@ service_desk_setting:
approvals:
- user
- merge_request
+resource_milestone_events:
+ - user
+ - issue
+ - merge_request
+ - milestone
+resource_state_events:
+ - user
+ - issue
+ - merge_request
+ - source_merge_request
+ - epic
+iteration:
+ - group
+ - iterations_cadence
+ - issues
+ - labels
+ - merge_requests
+resource_iteration_events:
+ - user
+ - issue
+ - merge_request
+ - iteration
+iterations_cadence:
+ - group
+ - iterations
diff --git a/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb b/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb
index 733be7fc226..272c2629b08 100644
--- a/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb
+++ b/spec/lib/gitlab/import_export/attribute_cleaner_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::ImportExport::AttributeCleaner do
let(:relation_class) { double('relation_class').as_null_object }
diff --git a/spec/lib/gitlab/import_export/attributes_finder_spec.rb b/spec/lib/gitlab/import_export/attributes_finder_spec.rb
index 428d8d605ee..6536b895b2f 100644
--- a/spec/lib/gitlab/import_export/attributes_finder_spec.rb
+++ b/spec/lib/gitlab/import_export/attributes_finder_spec.rb
@@ -123,7 +123,7 @@ RSpec.describe Gitlab::ImportExport::AttributesFinder do
is_expected.to match(
include: [{ merge_requests: {
include: [{ notes: { include: [{ author: { include: [] } }],
- preload: { author: nil } } }],
+ preload: { author: nil } } }],
preload: { notes: { author: nil } }
} }],
preload: { merge_requests: { notes: { author: nil } } }
@@ -132,7 +132,7 @@ RSpec.describe Gitlab::ImportExport::AttributesFinder do
it 'generates the correct hash for a relation with included attributes' do
setup_yaml(tree: { project: [:issues] },
- included_attributes: { issues: [:name, :description] })
+ included_attributes: { issues: [:name, :description] })
is_expected.to match(
include: [{ issues: { include: [],
@@ -143,7 +143,7 @@ RSpec.describe Gitlab::ImportExport::AttributesFinder do
it 'generates the correct hash for a relation with excluded attributes' do
setup_yaml(tree: { project: [:issues] },
- excluded_attributes: { issues: [:name] })
+ excluded_attributes: { issues: [:name] })
is_expected.to match(
include: [{ issues: { except: [:name],
@@ -154,8 +154,8 @@ RSpec.describe Gitlab::ImportExport::AttributesFinder do
it 'generates the correct hash for a relation with both excluded and included attributes' do
setup_yaml(tree: { project: [:issues] },
- excluded_attributes: { issues: [:name] },
- included_attributes: { issues: [:description] })
+ excluded_attributes: { issues: [:name] },
+ included_attributes: { issues: [:description] })
is_expected.to match(
include: [{ issues: { except: [:name],
@@ -167,7 +167,7 @@ RSpec.describe Gitlab::ImportExport::AttributesFinder do
it 'generates the correct hash for a relation with custom methods' do
setup_yaml(tree: { project: [:issues] },
- methods: { issues: [:name] })
+ methods: { issues: [:name] })
is_expected.to match(
include: [{ issues: { include: [],
diff --git a/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb b/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb
index 9f1b15aa049..4ee825c71b6 100644
--- a/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb
@@ -79,14 +79,14 @@ RSpec.describe Gitlab::ImportExport::Base::RelationObjectSaver do
let(:relation_definition) { { 'notes' => {} } }
it 'saves valid subrelations and logs invalid subrelation' do
- expect(relation_object.notes).to receive(:<<).and_call_original
+ expect(relation_object.notes).to receive(:<<).twice.and_call_original
expect(Gitlab::Import::Logger)
.to receive(:info)
.with(
message: '[Project/Group Import] Invalid subrelation',
project_id: project.id,
relation_key: 'issues',
- error_messages: "Noteable can't be blank and Project does not match noteable project"
+ error_messages: "Project does not match noteable project"
)
saver.execute
@@ -94,9 +94,28 @@ RSpec.describe Gitlab::ImportExport::Base::RelationObjectSaver do
issue = project.issues.last
import_failure = project.import_failures.last
+ expect(invalid_note.persisted?).to eq(false)
expect(issue.notes.count).to eq(5)
expect(import_failure.source).to eq('RelationObjectSaver#save!')
- expect(import_failure.exception_message).to eq("Noteable can't be blank and Project does not match noteable project")
+ expect(import_failure.exception_message).to eq('Project does not match noteable project')
+ end
+
+ context 'when invalid subrelation can still be persisted' do
+ let(:relation_key) { 'merge_requests' }
+ let(:relation_definition) { { 'approvals' => {} } }
+ let(:approval_1) { build(:approval, merge_request_id: nil, user: create(:user)) }
+ let(:approval_2) { build(:approval, merge_request_id: nil, user: create(:user)) }
+ let(:relation_object) { build(:merge_request, source_project: project, target_project: project, approvals: [approval_1, approval_2]) }
+
+ it 'saves the subrelation' do
+ expect(approval_1.valid?).to eq(false)
+ expect(Gitlab::Import::Logger).not_to receive(:info)
+
+ saver.execute
+
+ expect(project.merge_requests.first.approvals.count).to eq(2)
+ expect(project.merge_requests.first.approvals.first.persisted?).to eq(true)
+ end
end
context 'when importable is group' do
diff --git a/spec/lib/gitlab/import_export/config_spec.rb b/spec/lib/gitlab/import_export/config_spec.rb
index fcb48678b88..8f848af8bd3 100644
--- a/spec/lib/gitlab/import_export/config_spec.rb
+++ b/spec/lib/gitlab/import_export/config_spec.rb
@@ -21,10 +21,12 @@ RSpec.describe Gitlab::ImportExport::Config do
end
it 'parses default config' do
+ expected_keys = [:tree, :excluded_attributes, :included_attributes, :methods, :preloads, :export_reorders]
+ expected_keys << :include_if_exportable if ee
+
expect { subject }.not_to raise_error
expect(subject).to be_a(Hash)
- expect(subject.keys).to contain_exactly(
- :tree, :excluded_attributes, :included_attributes, :methods, :preloads, :export_reorders)
+ expect(subject.keys).to match_array(expected_keys)
end
end
end
diff --git a/spec/lib/gitlab/import_export/file_importer_spec.rb b/spec/lib/gitlab/import_export/file_importer_spec.rb
index 7b27f7183b0..5a75631ec4d 100644
--- a/spec/lib/gitlab/import_export/file_importer_spec.rb
+++ b/spec/lib/gitlab/import_export/file_importer_spec.rb
@@ -169,7 +169,7 @@ RSpec.describe Gitlab::ImportExport::FileImporter do
end
it 'skips validation' do
- expect(subject).to receive(:validate_decompressed_archive_size).never
+ expect(subject).not_to receive(:validate_decompressed_archive_size)
subject.import
end
diff --git a/spec/lib/gitlab/import_export/group/object_builder_spec.rb b/spec/lib/gitlab/import_export/group/object_builder_spec.rb
index 09f40199b31..25d9858dd4c 100644
--- a/spec/lib/gitlab/import_export/group/object_builder_spec.rb
+++ b/spec/lib/gitlab/import_export/group/object_builder_spec.rb
@@ -6,9 +6,9 @@ RSpec.describe Gitlab::ImportExport::Group::ObjectBuilder do
let(:group) { create(:group) }
let(:base_attributes) do
{
- 'title' => 'title',
+ 'title' => 'title',
'description' => 'description',
- 'group' => group
+ 'group' => group
}
end
diff --git a/spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb
index 2f1e2dd2db4..5e84284a060 100644
--- a/spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb
@@ -33,15 +33,15 @@ RSpec.describe Gitlab::ImportExport::Group::RelationTreeRestorer do
let(:relation_tree_restorer) do
described_class.new(
- user: user,
- shared: shared,
- relation_reader: relation_reader,
- object_builder: Gitlab::ImportExport::Group::ObjectBuilder,
- members_mapper: members_mapper,
- relation_factory: Gitlab::ImportExport::Group::RelationFactory,
- reader: reader,
- importable: importable,
- importable_path: nil,
+ user: user,
+ shared: shared,
+ relation_reader: relation_reader,
+ object_builder: Gitlab::ImportExport::Group::ObjectBuilder,
+ members_mapper: members_mapper,
+ relation_factory: Gitlab::ImportExport::Group::RelationFactory,
+ reader: reader,
+ importable: importable,
+ importable_path: nil,
importable_attributes: attributes
)
end
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 ab2c4cc2059..ed4368ba802 100644
--- a/spec/lib/gitlab/import_export/json/legacy_writer_spec.rb
+++ b/spec/lib/gitlab/import_export/json/legacy_writer_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::ImportExport::Json::LegacyWriter do
let(:path) { "#{Dir.tmpdir}/legacy_writer_spec/test.json" }
diff --git a/spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb b/spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb
index 3088129a732..02ac8065c9f 100644
--- a/spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb
+++ b/spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb
@@ -32,18 +32,20 @@ RSpec.describe Gitlab::ImportExport::Json::StreamingSerializer do
let(:hash) { { name: exportable.name, description: exportable.description }.stringify_keys }
let(:include) { [] }
let(:custom_orderer) { nil }
+ let(:include_if_exportable) { {} }
let(:relations_schema) do
{
only: [:name, :description],
include: include,
preload: { issues: nil },
- export_reorder: custom_orderer
+ export_reorder: custom_orderer,
+ include_if_exportable: include_if_exportable
}
end
subject do
- described_class.new(exportable, relations_schema, json_writer, exportable_path: exportable_path, logger: logger)
+ described_class.new(exportable, relations_schema, json_writer, exportable_path: exportable_path, logger: logger, current_user: user)
end
describe '#execute' do
@@ -210,11 +212,62 @@ RSpec.describe Gitlab::ImportExport::Json::StreamingSerializer do
subject.execute
end
end
- end
- describe '.batch_size' do
- it 'returns default batch size' do
- expect(described_class.batch_size(exportable)).to eq(described_class::BATCH_SIZE)
+ describe 'conditional export of included associations' do
+ let(:include) do
+ [{ issues: { include: [{ label_links: { include: [:label] } }] } }]
+ end
+
+ let(:include_if_exportable) do
+ { issues: [:label_links] }
+ end
+
+ let_it_be(:label) { create(:label, project: exportable) }
+ let_it_be(:link) { create(:label_link, label: label, target: issue) }
+
+ context 'when association is exportable' do
+ before do
+ allow_next_found_instance_of(Issue) do |issue|
+ allow(issue).to receive(:exportable_association?).with(:label_links, current_user: user).and_return(true)
+ end
+ end
+
+ it 'includes exportable association' do
+ expected_issue = issue.to_json(include: [{ label_links: { include: [:label] } }])
+
+ expect(json_writer).to receive(:write_relation_array).with(exportable_path, :issues, array_including(expected_issue))
+
+ subject.execute
+ end
+ end
+
+ context 'when association is not exportable' do
+ before do
+ allow_next_found_instance_of(Issue) do |issue|
+ allow(issue).to receive(:exportable_association?).with(:label_links, current_user: user).and_return(false)
+ end
+ end
+
+ it 'filters out not exportable association' do
+ expect(json_writer).to receive(:write_relation_array).with(exportable_path, :issues, array_including(issue.to_json))
+
+ subject.execute
+ end
+ end
+
+ context 'when association does not respond to exportable_association?' do
+ before do
+ allow_next_found_instance_of(Issue) do |issue|
+ allow(issue).to receive(:respond_to?).with(:exportable_association?).and_return(false)
+ end
+ end
+
+ it 'filters out not exportable association' do
+ expect(json_writer).to receive(:write_relation_array).with(exportable_path, :issues, array_including(issue.to_json))
+
+ subject.execute
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/import_export/legacy_relation_tree_saver_spec.rb b/spec/lib/gitlab/import_export/legacy_relation_tree_saver_spec.rb
index 0d372def8b0..c2c50751c3f 100644
--- a/spec/lib/gitlab/import_export/legacy_relation_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/legacy_relation_tree_saver_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe Gitlab::ImportExport::LegacyRelationTreeSaver do
it 'uses FastHashSerializer' do
expect(Gitlab::ImportExport::FastHashSerializer)
.to receive(:new)
- .with(exportable, tree, batch_size: Gitlab::ImportExport::Json::StreamingSerializer::BATCH_SIZE)
+ .with(exportable, tree)
.and_return(serializer)
expect(serializer).to receive(:execute)
diff --git a/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb
index b7b652005e9..ac646087a95 100644
--- a/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb
@@ -21,15 +21,15 @@ RSpec.describe Gitlab::ImportExport::Project::RelationTreeRestorer do
let(:reader) { Gitlab::ImportExport::Reader.new(shared: shared) }
let(:relation_tree_restorer) do
described_class.new(
- user: user,
- shared: shared,
- relation_reader: relation_reader,
- object_builder: Gitlab::ImportExport::Project::ObjectBuilder,
- members_mapper: members_mapper,
- relation_factory: Gitlab::ImportExport::Project::RelationFactory,
- reader: reader,
- importable: importable,
- importable_path: 'project',
+ user: user,
+ shared: shared,
+ relation_reader: relation_reader,
+ object_builder: Gitlab::ImportExport::Project::ObjectBuilder,
+ members_mapper: members_mapper,
+ relation_factory: Gitlab::ImportExport::Project::RelationFactory,
+ reader: reader,
+ importable: importable,
+ importable_path: 'project',
importable_attributes: attributes
)
end
diff --git a/spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb
index 3dab84af744..d1fe9b80062 100644
--- a/spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_spec.rb
@@ -21,15 +21,15 @@ RSpec.describe Gitlab::ImportExport::Project::Sample::RelationTreeRestorer do
let(:relation_reader) { Gitlab::ImportExport::Json::NdjsonReader.new(path) }
let(:sample_data_relation_tree_restorer) do
described_class.new(
- user: user,
- shared: shared,
- relation_reader: relation_reader,
- object_builder: Gitlab::ImportExport::Project::ObjectBuilder,
- members_mapper: members_mapper,
- relation_factory: Gitlab::ImportExport::Project::Sample::RelationFactory,
- reader: reader,
- importable: importable,
- importable_path: 'project',
+ user: user,
+ shared: shared,
+ relation_reader: relation_reader,
+ object_builder: Gitlab::ImportExport::Project::ObjectBuilder,
+ members_mapper: members_mapper,
+ relation_factory: Gitlab::ImportExport::Project::Sample::RelationFactory,
+ reader: reader,
+ importable: importable,
+ importable_path: 'project',
importable_attributes: attributes
)
end
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 47d7555c8f4..299e107c881 100644
--- a/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project/tree_restorer_spec.rb
@@ -192,10 +192,26 @@ RSpec.describe Gitlab::ImportExport::Project::TreeRestorer do
expect(Issue.find_by(title: 'Voluptatem').resource_label_events).not_to be_empty
end
+ it 'restores issue resource milestone events' do
+ expect(Issue.find_by(title: 'Voluptatem').resource_milestone_events).not_to be_empty
+ end
+
+ it 'restores issue resource state events' do
+ expect(Issue.find_by(title: 'Voluptatem').resource_state_events).not_to be_empty
+ end
+
it 'restores merge requests resource label events' do
expect(MergeRequest.find_by(title: 'MR1').resource_label_events).not_to be_empty
end
+ it 'restores merge request resource milestone events' do
+ expect(MergeRequest.find_by(title: 'MR1').resource_milestone_events).not_to be_empty
+ end
+
+ it 'restores merge request resource state events' do
+ expect(MergeRequest.find_by(title: 'MR1').resource_state_events).not_to be_empty
+ end
+
it 'restores suggestion' do
note = Note.find_by("note LIKE 'Saepe asperiores exercitationem non dignissimos laborum reiciendis et ipsum%'")
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 6cfc24a8996..e591cbd05a0 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -586,6 +586,7 @@ ProjectFeature:
- environments_access_level
- feature_flags_access_level
- releases_access_level
+- monitor_access_level
- created_at
- updated_at
ProtectedBranch::MergeAccessLevel:
@@ -706,7 +707,7 @@ ProtectedEnvironment:
- name
- created_at
- updated_at
-ProtectedEnvironment::DeployAccessLevel:
+ProtectedEnvironments::DeployAccessLevel:
- id
- protected_environment_id
- access_level
@@ -917,3 +918,29 @@ Approval:
- user_id
- created_at
- updated_at
+ResourceMilestoneEvent:
+ - user_id
+ - action
+ - state
+ - created_at
+ResourceStateEvent:
+ - user_id
+ - created_at
+ - state
+ - source_commit
+ - close_after_error_tracking_resolve
+ - close_auto_resolve_prometheus_alert
+Iteration:
+ - created_at
+ - updated_at
+ - start_date
+ - due_date
+ - group_id
+ - iid
+ - description
+ResourceIterationEvent:
+ - user_id
+ - created_at
+ - action
+Iterations::Cadence:
+ - title
diff --git a/spec/lib/gitlab/import_formatter_spec.rb b/spec/lib/gitlab/import_formatter_spec.rb
index fbf00ab92d3..0feff61725b 100644
--- a/spec/lib/gitlab/import_formatter_spec.rb
+++ b/spec/lib/gitlab/import_formatter_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::ImportFormatter do
let(:formatter) { Gitlab::ImportFormatter.new }
diff --git a/spec/lib/gitlab/import_sources_spec.rb b/spec/lib/gitlab/import_sources_spec.rb
index f42a109aa3a..41ffcece221 100644
--- a/spec/lib/gitlab/import_sources_spec.rb
+++ b/spec/lib/gitlab/import_sources_spec.rb
@@ -7,17 +7,17 @@ RSpec.describe Gitlab::ImportSources do
it 'returns a hash' do
expected =
{
- 'GitHub' => 'github',
- 'Bitbucket Cloud' => 'bitbucket',
- 'Bitbucket Server' => 'bitbucket_server',
- 'GitLab.com' => 'gitlab',
- 'Google Code' => 'google_code',
- 'FogBugz' => 'fogbugz',
+ 'GitHub' => 'github',
+ 'Bitbucket Cloud' => 'bitbucket',
+ 'Bitbucket Server' => 'bitbucket_server',
+ 'GitLab.com' => 'gitlab',
+ 'Google Code' => 'google_code',
+ 'FogBugz' => 'fogbugz',
'Repository by URL' => 'git',
- 'GitLab export' => 'gitlab_project',
- 'Gitea' => 'gitea',
- 'Manifest file' => 'manifest',
- 'Phabricator' => 'phabricator'
+ 'GitLab export' => 'gitlab_project',
+ 'Gitea' => 'gitea',
+ 'Manifest file' => 'manifest',
+ 'Phabricator' => 'phabricator'
}
expect(described_class.options).to eq(expected)
diff --git a/spec/lib/gitlab/incoming_email_spec.rb b/spec/lib/gitlab/incoming_email_spec.rb
index 72d201eed77..1545de6d8fd 100644
--- a/spec/lib/gitlab/incoming_email_spec.rb
+++ b/spec/lib/gitlab/incoming_email_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require "spec_helper"
+require 'fast_spec_helper'
RSpec.describe Gitlab::IncomingEmail do
describe "self.enabled?" do
diff --git a/spec/lib/gitlab/insecure_key_fingerprint_spec.rb b/spec/lib/gitlab/insecure_key_fingerprint_spec.rb
index 3a281574563..f2bf06236b9 100644
--- a/spec/lib/gitlab/insecure_key_fingerprint_spec.rb
+++ b/spec/lib/gitlab/insecure_key_fingerprint_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::InsecureKeyFingerprint do
let(:key) do
diff --git a/spec/lib/gitlab/instrumentation/redis_base_spec.rb b/spec/lib/gitlab/instrumentation/redis_base_spec.rb
index a7e08b5a9bd..f9dd0c94c97 100644
--- a/spec/lib/gitlab/instrumentation/redis_base_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_base_spec.rb
@@ -65,6 +65,13 @@ RSpec.describe Gitlab::Instrumentation::RedisBase, :request_store do
expect(instrumentation_class_b.get_request_count).to eq(2)
end
end
+
+ it 'increments by the given amount' do
+ instrumentation_class_a.increment_request_count(2)
+ instrumentation_class_a.increment_request_count(3)
+
+ expect(instrumentation_class_a.get_request_count).to eq(5)
+ end
end
describe '.increment_write_bytes' do
@@ -103,21 +110,21 @@ RSpec.describe Gitlab::Instrumentation::RedisBase, :request_store do
context 'storage key overlapping' do
it 'keys do not overlap across storages' do
2.times do
- instrumentation_class_a.add_call_details(0.3, [:set])
- instrumentation_class_b.add_call_details(0.4, [:set])
+ instrumentation_class_a.add_call_details(0.3, [[:set]])
+ instrumentation_class_b.add_call_details(0.4, [[:set]])
end
expect(instrumentation_class_a.detail_store).to match(
[
- a_hash_including(cmd: :set, duration: 0.3, backtrace: an_instance_of(Array)),
- a_hash_including(cmd: :set, duration: 0.3, backtrace: an_instance_of(Array))
+ a_hash_including(commands: [[:set]], duration: 0.3, backtrace: an_instance_of(Array)),
+ a_hash_including(commands: [[:set]], duration: 0.3, backtrace: an_instance_of(Array))
]
)
expect(instrumentation_class_b.detail_store).to match(
[
- a_hash_including(cmd: :set, duration: 0.4, backtrace: an_instance_of(Array)),
- a_hash_including(cmd: :set, duration: 0.4, backtrace: an_instance_of(Array))
+ a_hash_including(commands: [[:set]], duration: 0.4, backtrace: an_instance_of(Array)),
+ a_hash_including(commands: [[:set]], duration: 0.4, backtrace: an_instance_of(Array))
]
)
end
diff --git a/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb b/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
index 09280402e2b..5b5516f100b 100644
--- a/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb
@@ -47,11 +47,22 @@ RSpec.describe Gitlab::Instrumentation::RedisInterceptor, :clean_gitlab_redis_sh
let(:instrumentation_class) { Gitlab::Redis::SharedState.instrumentation_class }
it 'counts successful requests' do
- expect(instrumentation_class).to receive(:instance_count_request).and_call_original
+ expect(instrumentation_class).to receive(:instance_count_request).with(1).and_call_original
Gitlab::Redis::SharedState.with { |redis| redis.call(:get, 'foobar') }
end
+ it 'counts successful pipelined requests' do
+ expect(instrumentation_class).to receive(:instance_count_request).with(2).and_call_original
+
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.pipelined do |pipeline|
+ pipeline.call(:get, 'foobar')
+ pipeline.call(:get, 'foobarbaz')
+ end
+ end
+ end
+
it 'counts exceptions' do
expect(instrumentation_class).to receive(:instance_count_exception)
.with(instance_of(Redis::CommandError)).and_call_original
@@ -84,6 +95,20 @@ RSpec.describe Gitlab::Instrumentation::RedisInterceptor, :clean_gitlab_redis_sh
Gitlab::Redis::SharedState.with { |redis| redis.call(*command) }
end
end
+
+ context 'with pipelined commands' do
+ it 'measures requests that do not have blocking commands' do
+ expect(instrumentation_class).to receive(:instance_observe_duration).twice.with(a_value > 0)
+ .and_call_original
+
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.pipelined do |pipeline|
+ pipeline.call(:get, 'foobar')
+ pipeline.call(:get, 'foobarbaz')
+ end
+ end
+ end
+ end
end
describe 'commands not in the apdex' do
@@ -109,6 +134,19 @@ RSpec.describe Gitlab::Instrumentation::RedisInterceptor, :clean_gitlab_redis_sh
end
end
end
+
+ context 'with pipelined commands' do
+ it 'skips requests that have blocking commands', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/373026' 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')
+ end
+ end
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/instrumentation/redis_spec.rb b/spec/lib/gitlab/instrumentation/redis_spec.rb
index 900a079cdd2..c01d06c97b0 100644
--- a/spec/lib/gitlab/instrumentation/redis_spec.rb
+++ b/spec/lib/gitlab/instrumentation/redis_spec.rb
@@ -71,14 +71,10 @@ RSpec.describe Gitlab::Instrumentation::Redis do
stub_storages(:detail_store, [details_row])
- expect(described_class.detail_store)
- .to contain_exactly(details_row.merge(storage: 'ActionCable'),
- details_row.merge(storage: 'Cache'),
- details_row.merge(storage: 'Queues'),
- details_row.merge(storage: 'SharedState'),
- details_row.merge(storage: 'TraceChunks'),
- details_row.merge(storage: 'RateLimiting'),
- details_row.merge(storage: 'Sessions'))
+ expected_detail_stores = Gitlab::Redis::ALL_CLASSES.map(&:store_name)
+ .map { |store_name| details_row.merge(storage: store_name) }
+ expected_detail_stores << details_row.merge(storage: 'ActionCable')
+ expect(described_class.detail_store).to contain_exactly(*expected_detail_stores)
end
end
end
diff --git a/spec/lib/gitlab/instrumentation_helper_spec.rb b/spec/lib/gitlab/instrumentation_helper_spec.rb
index 4fa9079144d..d5ff39767c4 100644
--- a/spec/lib/gitlab/instrumentation_helper_spec.rb
+++ b/spec/lib/gitlab/instrumentation_helper_spec.rb
@@ -140,13 +140,13 @@ RSpec.describe Gitlab::InstrumentationHelper do
subject
expect(payload).to include(db_replica_count: 0,
- db_replica_cached_count: 0,
- db_primary_count: 0,
- db_primary_cached_count: 0,
- db_primary_wal_count: 0,
- db_replica_wal_count: 0,
- db_primary_wal_cached_count: 0,
- db_replica_wal_cached_count: 0)
+ db_replica_cached_count: 0,
+ db_primary_count: 0,
+ db_primary_cached_count: 0,
+ db_primary_wal_count: 0,
+ db_replica_wal_count: 0,
+ db_primary_wal_cached_count: 0,
+ db_replica_wal_cached_count: 0)
end
context 'when replica caught up search was made' do
diff --git a/spec/lib/gitlab/internal_post_receive/response_spec.rb b/spec/lib/gitlab/internal_post_receive/response_spec.rb
index 135596c2de3..23ea5191486 100644
--- a/spec/lib/gitlab/internal_post_receive/response_spec.rb
+++ b/spec/lib/gitlab/internal_post_receive/response_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::InternalPostReceive::Response do
subject { described_class.new }
diff --git a/spec/lib/gitlab/jira/middleware_spec.rb b/spec/lib/gitlab/jira/middleware_spec.rb
index e7a79e40ac5..09cf67d0657 100644
--- a/spec/lib/gitlab/jira/middleware_spec.rb
+++ b/spec/lib/gitlab/jira/middleware_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Jira::Middleware do
let(:app) { double(:app) }
@@ -24,7 +24,7 @@ RSpec.describe Gitlab::Jira::Middleware do
describe '#call' do
it 'adjusts HTTP_AUTHORIZATION env when request from Jira DVCS user agent' do
expect(app).to receive(:call).with({ 'HTTP_USER_AGENT' => jira_user_agent,
- 'HTTP_AUTHORIZATION' => 'Bearer hash-123' })
+ 'HTTP_AUTHORIZATION' => 'Bearer hash-123' })
middleware.call('HTTP_USER_AGENT' => jira_user_agent, 'HTTP_AUTHORIZATION' => 'token hash-123')
end
diff --git a/spec/lib/gitlab/jira_import/metadata_collector_spec.rb b/spec/lib/gitlab/jira_import/metadata_collector_spec.rb
index 51751c7b75f..d8e31d0ae22 100644
--- a/spec/lib/gitlab/jira_import/metadata_collector_spec.rb
+++ b/spec/lib/gitlab/jira_import/metadata_collector_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::JiraImport::MetadataCollector do
describe '#execute' do
diff --git a/spec/lib/gitlab/jira_import_spec.rb b/spec/lib/gitlab/jira_import_spec.rb
index 972b0ab6ed1..c0c1a28b9ff 100644
--- a/spec/lib/gitlab/jira_import_spec.rb
+++ b/spec/lib/gitlab/jira_import_spec.rb
@@ -106,12 +106,6 @@ RSpec.describe Gitlab::JiraImport do
end
end
- describe '.jira_issue_cache_key' do
- it 'returns cache key for Jira issue imported to given project' do
- expect(described_class.jira_item_cache_key(project_id, 'DEMO-123', :issues)).to eq("jira-import/items-mapper/#{project_id}/issues/DEMO-123")
- end
- end
-
describe '.already_imported_cache_key' do
it 'returns cache key for already imported items' do
expect(described_class.already_imported_cache_key(:issues, project_id)).to eq("jira-importer/already-imported/#{project_id}/issues")
diff --git a/spec/lib/gitlab/kubernetes/helm/v2/certificate_spec.rb b/spec/lib/gitlab/kubernetes/helm/v2/certificate_spec.rb
index a3f0fd9eb9b..698b88c9fa1 100644
--- a/spec/lib/gitlab/kubernetes/helm/v2/certificate_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/v2/certificate_spec.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Kubernetes::Helm::V2::Certificate do
describe '.generate_root' do
diff --git a/spec/lib/gitlab/kubernetes/kubeconfig/entry/cluster_spec.rb b/spec/lib/gitlab/kubernetes/kubeconfig/entry/cluster_spec.rb
index 508808be1be..549fd862d2d 100644
--- a/spec/lib/gitlab/kubernetes/kubeconfig/entry/cluster_spec.rb
+++ b/spec/lib/gitlab/kubernetes/kubeconfig/entry/cluster_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Kubernetes::Kubeconfig::Entry::Cluster do
describe '#to_h' do
diff --git a/spec/lib/gitlab/kubernetes/kubeconfig/entry/context_spec.rb b/spec/lib/gitlab/kubernetes/kubeconfig/entry/context_spec.rb
index 43d4c46fda1..4734111a8ec 100644
--- a/spec/lib/gitlab/kubernetes/kubeconfig/entry/context_spec.rb
+++ b/spec/lib/gitlab/kubernetes/kubeconfig/entry/context_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Kubernetes::Kubeconfig::Entry::Context do
describe '#to_h' do
diff --git a/spec/lib/gitlab/kubernetes/kubeconfig/entry/user_spec.rb b/spec/lib/gitlab/kubernetes/kubeconfig/entry/user_spec.rb
index 3d6acc80823..9eb6ddcf30c 100644
--- a/spec/lib/gitlab/kubernetes/kubeconfig/entry/user_spec.rb
+++ b/spec/lib/gitlab/kubernetes/kubeconfig/entry/user_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Kubernetes::Kubeconfig::Entry::User do
describe '#to_h' do
diff --git a/spec/lib/gitlab/kubernetes/kubeconfig/template_spec.rb b/spec/lib/gitlab/kubernetes/kubeconfig/template_spec.rb
index 7d1f1aea291..869bba22a01 100644
--- a/spec/lib/gitlab/kubernetes/kubeconfig/template_spec.rb
+++ b/spec/lib/gitlab/kubernetes/kubeconfig/template_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Kubernetes::Kubeconfig::Template do
let(:template) { described_class.new }
diff --git a/spec/lib/gitlab/lazy_spec.rb b/spec/lib/gitlab/lazy_spec.rb
index 3e929cf200a..92907081867 100644
--- a/spec/lib/gitlab/lazy_spec.rb
+++ b/spec/lib/gitlab/lazy_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Lazy do
let(:dummy) { double(:dummy) }
diff --git a/spec/lib/gitlab/legacy_github_import/client_spec.rb b/spec/lib/gitlab/legacy_github_import/client_spec.rb
index 83ba5858d81..08679b7e9f1 100644
--- a/spec/lib/gitlab/legacy_github_import/client_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/client_spec.rb
@@ -98,6 +98,30 @@ RSpec.describe Gitlab::LegacyGithubImport::Client do
end
end
+ describe '#repository' do
+ it 'returns repository data as a hash' do
+ stub_request(:get, 'https://api.github.com/rate_limit')
+ .to_return(status: 200, headers: { 'X-RateLimit-Limit' => 5000, 'X-RateLimit-Remaining' => 5000 })
+
+ stub_request(:get, 'https://api.github.com/repositories/1')
+ .to_return(status: 200, body: { id: 1 }.to_json, headers: { 'Content-Type' => 'application/json' })
+
+ expect(client.repository(1)).to eq({ id: 1 })
+ end
+ end
+
+ describe '#repos' do
+ it 'returns the user\'s repositories as a hash' do
+ stub_request(:get, 'https://api.github.com/rate_limit')
+ .to_return(status: 200, headers: { 'X-RateLimit-Limit' => 5000, 'X-RateLimit-Remaining' => 5000 })
+
+ stub_request(:get, 'https://api.github.com/user/repos')
+ .to_return(status: 200, body: [{ id: 1 }, { id: 2 }].to_json, headers: { 'Content-Type' => 'application/json' })
+
+ expect(client.repos).to match_array([{ id: 1 }, { id: 2 }])
+ end
+ end
+
context 'github rate limit' do
it 'does not raise error when rate limit is disabled' do
stub_request(:get, /api.github.com/)
diff --git a/spec/lib/gitlab/legacy_github_import/issuable_formatter_spec.rb b/spec/lib/gitlab/legacy_github_import/issuable_formatter_spec.rb
index a5d2e00890b..a285a5820a2 100644
--- a/spec/lib/gitlab/legacy_github_import/issuable_formatter_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/issuable_formatter_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::LegacyGithubImport::IssuableFormatter do
let(:raw_data) do
diff --git a/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb b/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb
index 68f1c214cef..17ecd183ac9 100644
--- a/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb
+++ b/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb
@@ -7,15 +7,15 @@ RSpec.describe Gitlab::LegacyGithubImport::ProjectCreator do
let(:namespace) { create(:group) }
let(:repo) do
- ActiveSupport::InheritableOptions.new(
+ {
login: 'vim',
name: 'vim',
full_name: 'asd/vim',
clone_url: 'https://gitlab.com/asd/vim.git'
- )
+ }
end
- subject(:service) { described_class.new(repo, repo.name, namespace, user, github_access_token: 'asdffg') }
+ subject(:service) { described_class.new(repo, repo[:name], namespace, user, github_access_token: 'asdffg') }
before do
namespace.add_owner(user)
@@ -40,7 +40,7 @@ RSpec.describe Gitlab::LegacyGithubImport::ProjectCreator do
context 'when GitHub project is private' do
it 'sets project visibility to private' do
- repo.private = true
+ repo[:private] = true
project = service.execute
@@ -50,17 +50,19 @@ RSpec.describe Gitlab::LegacyGithubImport::ProjectCreator do
context 'when GitHub project is public' do
it 'sets project visibility to namespace visibility level' do
- repo.private = false
+ repo[:private] = false
+
project = service.execute
expect(project.visibility_level).to eq(namespace.visibility_level)
end
context 'when importing into a user namespace' do
- subject(:service) { described_class.new(repo, repo.name, user.namespace, user, github_access_token: 'asdffg') }
+ subject(:service) { described_class.new(repo, repo[:name], user.namespace, user, github_access_token: 'asdffg') }
it 'sets project visibility to user namespace visibility level' do
- repo.private = false
+ repo[:private] = false
+
project = service.execute
expect(project.visibility_level).to eq(user.namespace.visibility_level)
@@ -76,7 +78,7 @@ RSpec.describe Gitlab::LegacyGithubImport::ProjectCreator do
end
it 'sets project visibility to the default project visibility' do
- repo.private = true
+ repo[:private] = true
project = service.execute
@@ -91,7 +93,7 @@ RSpec.describe Gitlab::LegacyGithubImport::ProjectCreator do
end
it 'sets project visibility to the default project visibility' do
- repo.private = false
+ repo[:private] = false
project = service.execute
@@ -102,7 +104,7 @@ RSpec.describe Gitlab::LegacyGithubImport::ProjectCreator do
context 'when GitHub project has wiki' do
it 'does not create the wiki repository' do
- allow(repo).to receive(:has_wiki?).and_return(true)
+ repo[:has_wiki] = true
project = service.execute
@@ -112,7 +114,7 @@ RSpec.describe Gitlab::LegacyGithubImport::ProjectCreator do
context 'when GitHub project does not have wiki' do
it 'creates the wiki repository' do
- allow(repo).to receive(:has_wiki?).and_return(false)
+ repo[:has_wiki] = false
project = service.execute
diff --git a/spec/lib/gitlab/loop_helpers_spec.rb b/spec/lib/gitlab/loop_helpers_spec.rb
index 0535cb6068c..bb328e3dcce 100644
--- a/spec/lib/gitlab/loop_helpers_spec.rb
+++ b/spec/lib/gitlab/loop_helpers_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::LoopHelpers do
let(:class_instance) { (Class.new { include ::Gitlab::LoopHelpers }).new }
diff --git a/spec/lib/gitlab/mailgun/webhook_processors/failure_logger_spec.rb b/spec/lib/gitlab/mailgun/webhook_processors/failure_logger_spec.rb
index a2286415e96..4b9ea1c15a9 100644
--- a/spec/lib/gitlab/mailgun/webhook_processors/failure_logger_spec.rb
+++ b/spec/lib/gitlab/mailgun/webhook_processors/failure_logger_spec.rb
@@ -20,18 +20,43 @@ RSpec.describe Gitlab::Mailgun::WebhookProcessors::FailureLogger do
context 'on permanent failure' do
let(:processor) { described_class.new(base_payload.merge({ 'severity' => 'permanent' })) }
- it 'logs the failure immediately' do
- expect(Gitlab::ErrorTracking::Logger).to receive(:error).with(
- event: 'email_delivery_failure',
- mailgun_event_id: base_payload['id'],
- recipient: base_payload['recipient'],
- failure_type: 'permanent',
- failure_reason: base_payload['reason'],
- failure_code: base_payload['delivery-status']['code'],
- failure_message: base_payload['delivery-status']['message']
- )
+ before do
+ allow(Gitlab::ApplicationRateLimiter).to receive(:rate_limits)
+ .and_return(permanent_email_failure: { threshold: 1, interval: 1.minute })
+ end
- processor.execute
+ context 'when threshold is not exceeded' do
+ it 'increments counter but does not log the failure' do
+ expect(Gitlab::ApplicationRateLimiter).to receive(:throttled?).with(
+ :permanent_email_failure, scope: 'recipient@gitlab.com'
+ ).and_call_original
+ expect(Gitlab::ErrorTracking::Logger).not_to receive(:error)
+
+ processor.execute
+ end
+ end
+
+ context 'when threshold is exceeded' do
+ before do
+ processor.execute
+ end
+
+ it 'increments counter and logs the failure' do
+ expect(Gitlab::ApplicationRateLimiter).to receive(:throttled?).with(
+ :permanent_email_failure, scope: 'recipient@gitlab.com'
+ ).and_call_original
+ expect(Gitlab::ErrorTracking::Logger).to receive(:error).with(
+ event: 'email_delivery_failure',
+ mailgun_event_id: base_payload['id'],
+ recipient: base_payload['recipient'],
+ failure_type: 'permanent',
+ failure_reason: base_payload['reason'],
+ failure_code: base_payload['delivery-status']['code'],
+ failure_message: base_payload['delivery-status']['message']
+ )
+
+ processor.execute
+ end
end
end
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 81910773dfa..57f2b1cfd96 100644
--- a/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
+++ b/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
@@ -174,8 +174,8 @@ RSpec.describe Gitlab::MarkdownCache::ActiveRecord::Extension do
expect(thing).to receive(:update_columns)
.with({ "title_html" => updated_html,
- "description_html" => "",
- "cached_markdown_version" => cache_version })
+ "description_html" => "",
+ "cached_markdown_version" => cache_version })
thing.refresh_markdown_cache!
end
diff --git a/spec/lib/gitlab/markdown_cache/redis/extension_spec.rb b/spec/lib/gitlab/markdown_cache/redis/extension_spec.rb
index b5d458f15fc..8e75009099d 100644
--- a/spec/lib/gitlab/markdown_cache/redis/extension_spec.rb
+++ b/spec/lib/gitlab/markdown_cache/redis/extension_spec.rb
@@ -62,7 +62,13 @@ RSpec.describe Gitlab::MarkdownCache::Redis::Extension, :clean_gitlab_redis_cach
it 'does not preload the markdown twice' do
expect(Gitlab::MarkdownCache::Redis::Store).to receive(:bulk_read).and_call_original
- expect(Gitlab::Redis::Cache).to receive(:with).twice.and_call_original
+ Gitlab::Redis::Cache.with do |redis|
+ expect(redis).to receive(:pipelined).and_call_original
+
+ expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
+ expect(pipeline).to receive(:mapped_hmget).once.and_call_original
+ end
+ end
klass.preload_markdown_cache!([thing])
diff --git a/spec/lib/gitlab/markup_helper_spec.rb b/spec/lib/gitlab/markup_helper_spec.rb
index bf5415ba1d7..2bffd029568 100644
--- a/spec/lib/gitlab/markup_helper_spec.rb
+++ b/spec/lib/gitlab/markup_helper_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::MarkupHelper do
describe '#markup?' do
diff --git a/spec/lib/gitlab/memory/jemalloc_spec.rb b/spec/lib/gitlab/memory/jemalloc_spec.rb
index 482ac6e5802..414d6017534 100644
--- a/spec/lib/gitlab/memory/jemalloc_spec.rb
+++ b/spec/lib/gitlab/memory/jemalloc_spec.rb
@@ -1,12 +1,15 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require 'tmpdir'
RSpec.describe Gitlab::Memory::Jemalloc do
let(:outdir) { Dir.mktmpdir }
+ let(:tmp_outdir) { Dir.mktmpdir }
after do
FileUtils.rm_f(outdir)
+ FileUtils.rm_f(tmp_outdir)
end
context 'when jemalloc is loaded' do
@@ -28,7 +31,7 @@ RSpec.describe Gitlab::Memory::Jemalloc do
describe '.dump_stats' do
it 'writes stats JSON file' do
- file_path = described_class.dump_stats(path: outdir, format: format)
+ file_path = described_class.dump_stats(path: outdir, tmp_dir: tmp_outdir, format: format)
file = Dir.entries(outdir).find { |e| e.match(/jemalloc_stats\.#{$$}\.\d+\.json$/) }
expect(file).not_to be_nil
@@ -55,7 +58,8 @@ RSpec.describe Gitlab::Memory::Jemalloc do
describe '.dump_stats' do
shared_examples 'writes stats text file' do |filename_label, filename_pattern|
it do
- described_class.dump_stats(path: outdir, format: format, filename_label: filename_label)
+ 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
@@ -87,7 +91,7 @@ RSpec.describe Gitlab::Memory::Jemalloc do
describe '.dump_stats' do
it 'raises an error' do
expect do
- described_class.dump_stats(path: outdir, format: format)
+ described_class.dump_stats(path: outdir, tmp_dir: tmp_outdir, format: format)
end.to raise_error(/format must be one of/)
end
end
@@ -109,7 +113,7 @@ RSpec.describe Gitlab::Memory::Jemalloc do
it 'does nothing' do
stub_env('LD_PRELOAD', nil)
- described_class.dump_stats(path: outdir)
+ described_class.dump_stats(path: outdir, tmp_dir: tmp_outdir)
expect(Dir.empty?(outdir)).to be(true)
end
diff --git a/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb b/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb
index 53fae48776b..b327a40bc2c 100644
--- a/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb
+++ b/spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb
@@ -3,14 +3,19 @@
require 'spec_helper'
RSpec.describe Gitlab::Memory::Reports::JemallocStats do
- let(:reports_dir) { '/empty-dir' }
- let(:jemalloc_stats) { described_class.new(reports_path: reports_dir) }
+ let_it_be(:outdir) { Dir.mktmpdir }
+
+ let(:jemalloc_stats) { described_class.new(reports_path: outdir) }
+
+ after do
+ FileUtils.rm_f(outdir)
+ end
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(reports_dir, report_name) }
+ let(:report_path) { File.join(outdir, report_name) }
before do
allow(Prometheus::PidProvider).to receive(:worker_id).and_return(worker_id)
@@ -18,14 +23,16 @@ RSpec.describe Gitlab::Memory::Reports::JemallocStats do
it 'invokes Jemalloc.dump_stats and returns file path' do
expect(Gitlab::Memory::Jemalloc)
- .to receive(:dump_stats).with(path: reports_dir, filename_label: worker_id).and_return(report_path)
+ .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_it_be(:outdir) { Dir.mktmpdir }
-
let(:jemalloc_stats) { described_class.new(reports_path: outdir) }
before do
@@ -33,10 +40,6 @@ RSpec.describe Gitlab::Memory::Reports::JemallocStats do
allow(Gitlab::Memory::Jemalloc).to receive(:dump_stats)
end
- after do
- FileUtils.rm_f(outdir)
- end
-
context 'when number of reports exceeds `max_reports_stored`' do
let_it_be(:reports) do
now = Time.current
diff --git a/spec/lib/gitlab/memory/watchdog_spec.rb b/spec/lib/gitlab/memory/watchdog_spec.rb
index 010f6884df3..beb49660022 100644
--- a/spec/lib/gitlab/memory/watchdog_spec.rb
+++ b/spec/lib/gitlab/memory/watchdog_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
+require_relative '../../../../lib/gitlab/cluster/lifecycle_events'
RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
context 'watchdog' do
@@ -8,23 +9,31 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
let(:handler) { instance_double(described_class::NullHandler) }
let(:heap_frag_limit_gauge) { instance_double(::Prometheus::Client::Gauge) }
- let(:heap_frag_violations_counter) { instance_double(::Prometheus::Client::Counter) }
- let(:heap_frag_violations_handled_counter) { instance_double(::Prometheus::Client::Counter) }
+ let(:violations_counter) { instance_double(::Prometheus::Client::Counter) }
+ let(:violations_handled_counter) { instance_double(::Prometheus::Client::Counter) }
let(:sleep_time) { 0.1 }
let(:max_heap_fragmentation) { 0.2 }
+ let(:max_mem_growth) { 2 }
+
+ # Defaults that will not trigger any events.
+ let(:fragmentation) { 0 }
+ let(:worker_memory) { 0 }
+ let(:primary_memory) { 0 }
+ let(:max_strikes) { 0 }
# Tests should set this to control the number of loop iterations in `call`.
let(:watchdog_iterations) { 1 }
subject(:watchdog) do
described_class.new(handler: handler, logger: logger, sleep_time_seconds: sleep_time,
- max_strikes: max_strikes, max_heap_fragmentation: max_heap_fragmentation).tap do |instance|
+ max_strikes: max_strikes, max_mem_growth: max_mem_growth,
+ max_heap_fragmentation: max_heap_fragmentation).tap do |instance|
# We need to defuse `sleep` and stop the internal loop after N iterations.
iterations = 0
- expect(instance).to receive(:sleep) do
- instance.stop if (iterations += 1) >= watchdog_iterations
- end.at_most(watchdog_iterations)
+ allow(instance).to receive(:sleep) do
+ instance.stop if (iterations += 1) > watchdog_iterations
+ end
end
end
@@ -33,34 +42,35 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
.with(:gitlab_memwd_heap_frag_limit, anything)
.and_return(heap_frag_limit_gauge)
allow(Gitlab::Metrics).to receive(:counter)
- .with(:gitlab_memwd_heap_frag_violations_total, anything, anything)
- .and_return(heap_frag_violations_counter)
+ .with(:gitlab_memwd_violations_total, anything, anything)
+ .and_return(violations_counter)
allow(Gitlab::Metrics).to receive(:counter)
- .with(:gitlab_memwd_heap_frag_violations_handled_total, anything, anything)
- .and_return(heap_frag_violations_handled_counter)
+ .with(:gitlab_memwd_violations_handled_total, anything, anything)
+ .and_return(violations_handled_counter)
allow(heap_frag_limit_gauge).to receive(:set)
- allow(heap_frag_violations_counter).to receive(:increment)
- allow(heap_frag_violations_handled_counter).to receive(:increment)
+ allow(violations_counter).to receive(:increment)
+ allow(violations_handled_counter).to receive(:increment)
end
before do
stub_prometheus_metrics
- allow(handler).to receive(:on_high_heap_fragmentation).and_return(true)
+ allow(handler).to receive(:call).and_return(true)
allow(logger).to receive(:warn)
allow(logger).to receive(:info)
allow(Gitlab::Metrics::Memory).to receive(:gc_heap_fragmentation).and_return(fragmentation)
+ 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).with(
+ pid: Gitlab::Cluster::PRIMARY_PID
+ ).and_return({ uss: primary_memory })
allow(::Prometheus::PidProvider).to receive(:worker_id).and_return('worker_1')
end
context 'when created' do
- let(:fragmentation) { 0 }
- let(:max_strikes) { 0 }
-
it 'sets the heap fragmentation limit gauge' do
expect(heap_frag_limit_gauge).to receive(:set).with({}, max_heap_fragmentation)
@@ -71,7 +81,8 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
it 'initializes with defaults' do
watchdog = described_class.new(handler: handler, logger: logger)
- expect(watchdog.max_heap_fragmentation).to eq(described_class::DEFAULT_HEAP_FRAG_THRESHOLD)
+ expect(watchdog.max_heap_fragmentation).to eq(described_class::DEFAULT_MAX_HEAP_FRAG)
+ expect(watchdog.max_mem_growth).to eq(described_class::DEFAULT_MAX_MEM_GROWTH)
expect(watchdog.max_strikes).to eq(described_class::DEFAULT_MAX_STRIKES)
expect(watchdog.sleep_time_seconds).to eq(described_class::DEFAULT_SLEEP_TIME_SECONDS)
end
@@ -82,6 +93,7 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
stub_env('GITLAB_MEMWD_MAX_HEAP_FRAG', 1)
stub_env('GITLAB_MEMWD_MAX_STRIKES', 2)
stub_env('GITLAB_MEMWD_SLEEP_TIME_SEC', 3)
+ stub_env('GITLAB_MEMWD_MAX_MEM_GROWTH', 4)
end
it 'initializes with these settings' do
@@ -90,30 +102,17 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
expect(watchdog.max_heap_fragmentation).to eq(1)
expect(watchdog.max_strikes).to eq(2)
expect(watchdog.sleep_time_seconds).to eq(3)
+ expect(watchdog.max_mem_growth).to eq(4)
end
end
end
- context 'when process does not exceed heap fragmentation threshold' do
- let(:fragmentation) { max_heap_fragmentation - 0.1 }
- let(:max_strikes) { 0 } # To rule out that we were granting too many strikes.
-
- it 'does not signal the handler' do
- expect(handler).not_to receive(:on_high_heap_fragmentation)
-
- watchdog.call
- end
- end
-
- context 'when process exceeds heap fragmentation threshold permanently' do
- let(:fragmentation) { max_heap_fragmentation + 0.1 }
- let(:max_strikes) { 3 }
-
+ shared_examples 'has strikes left' do |stat|
context 'when process has not exceeded allowed number of strikes' do
let(:watchdog_iterations) { max_strikes }
it 'does not signal the handler' do
- expect(handler).not_to receive(:on_high_heap_fragmentation)
+ expect(handler).not_to receive(:call)
watchdog.call
end
@@ -125,119 +124,228 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
end
it 'increments the violations counter' do
- expect(heap_frag_violations_counter).to receive(:increment).exactly(watchdog_iterations)
+ expect(violations_counter).to receive(:increment).with(reason: stat).exactly(watchdog_iterations)
watchdog.call
end
it 'does not increment violations handled counter' do
- expect(heap_frag_violations_handled_counter).not_to receive(:increment)
+ expect(violations_handled_counter).not_to receive(:increment)
watchdog.call
end
end
+ end
+
+ shared_examples 'no strikes left' do |stat|
+ it 'signals the handler and resets strike counter' do
+ expect(handler).to receive(:call).and_return(true)
+
+ watchdog.call
+
+ expect(watchdog.strikes(stat.to_sym)).to eq(0)
+ end
+
+ it 'increments both the violations and violations handled counters' do
+ expect(violations_counter).to receive(:increment).with(reason: stat).exactly(watchdog_iterations)
+ expect(violations_handled_counter).to receive(:increment).with(reason: stat)
+
+ watchdog.call
+ end
- context 'when process exceeds the allowed number of strikes' do
- let(:watchdog_iterations) { max_strikes + 1 }
+ context 'when enforce_memory_watchdog ops toggle is off' do
+ before do
+ stub_feature_flags(enforce_memory_watchdog: false)
+ end
- it 'signals the handler and resets strike counter' do
- expect(handler).to receive(:on_high_heap_fragmentation).and_return(true)
+ 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
- expect(watchdog.strikes).to eq(0)
+ context 'when handler result is true' do
+ it 'considers the event handled and stops itself' do
+ expect(handler).to receive(:call).once.and_return(true)
+ expect(logger).to receive(:info).with(hash_including(message: 'stopped'))
+
+ watchdog.call
end
+ end
- it 'logs the event' do
- expect(Gitlab::Metrics::System).to receive(:memory_usage_rss).at_least(:once).and_return(1024)
- expect(logger).to receive(:warn).with({
- message: 'heap fragmentation limit exceeded',
- pid: Process.pid,
- worker_id: 'worker_1',
- memwd_handler_class: 'RSpec::Mocks::InstanceVerifyingDouble',
- memwd_sleep_time_s: sleep_time,
- memwd_max_heap_frag: max_heap_fragmentation,
- memwd_cur_heap_frag: fragmentation,
- memwd_max_strikes: max_strikes,
- memwd_cur_strikes: max_strikes + 1,
- memwd_rss_bytes: 1024
- })
+ context 'when handler result is false' do
+ let(:max_strikes) { 0 } # to make sure the handler fires each iteration
+ let(:watchdog_iterations) { 3 }
+
+ it 'keeps running' do
+ expect(violations_counter).to receive(:increment).exactly(watchdog_iterations)
+ expect(violations_handled_counter).to receive(:increment).exactly(watchdog_iterations)
+ # Return true the third time to terminate the daemon.
+ expect(handler).to receive(:call).and_return(false, false, true)
watchdog.call
end
+ end
+ end
+
+ context 'when monitoring memory growth' do
+ let(:primary_memory) { 2048 }
- it 'increments both the violations and violations handled counters' do
- expect(heap_frag_violations_counter).to receive(:increment).exactly(watchdog_iterations)
- expect(heap_frag_violations_handled_counter).to receive(:increment)
+ context 'when process does not exceed threshold' do
+ let(:worker_memory) { max_mem_growth * primary_memory - 1 }
+
+ it 'does not signal the handler' do
+ expect(handler).not_to receive(:call)
watchdog.call
end
+ end
- context 'when enforce_memory_watchdog ops toggle is off' do
- before do
- stub_feature_flags(enforce_memory_watchdog: false)
- end
+ context 'when process exceeds threshold permanently' do
+ let(:worker_memory) { max_mem_growth * primary_memory + 1 }
+ let(:max_strikes) { 3 }
+
+ it_behaves_like 'has strikes left', 'mem_growth'
+
+ context 'when process exceeds the allowed number of strikes' do
+ let(:watchdog_iterations) { max_strikes + 1 }
- it 'always uses the NullHandler' do
- expect(handler).not_to receive(:on_high_heap_fragmentation)
- expect(described_class::NullHandler.instance).to(
- receive(:on_high_heap_fragmentation).with(fragmentation).and_return(true)
- )
+ it_behaves_like 'no strikes left', 'mem_growth'
+
+ it 'only reads reference memory once' do
+ expect(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss)
+ .with(pid: Gitlab::Cluster::PRIMARY_PID)
+ .once
watchdog.call
end
- end
- context 'when handler result is true' do
- it 'considers the event handled and stops itself' do
- expect(handler).to receive(:on_high_heap_fragmentation).once.and_return(true)
- expect(logger).to receive(:info).with(hash_including(message: 'stopped'))
+ it 'logs the event' do
+ expect(Gitlab::Metrics::System).to receive(:memory_usage_rss).at_least(:once).and_return(1024)
+ expect(logger).to receive(:warn).with({
+ message: 'memory limit exceeded',
+ pid: Process.pid,
+ worker_id: 'worker_1',
+ memwd_handler_class: 'RSpec::Mocks::InstanceVerifyingDouble',
+ memwd_sleep_time_s: sleep_time,
+ memwd_max_uss_bytes: max_mem_growth * primary_memory,
+ memwd_ref_uss_bytes: primary_memory,
+ memwd_uss_bytes: worker_memory,
+ memwd_rss_bytes: 1024,
+ memwd_max_strikes: max_strikes,
+ memwd_cur_strikes: max_strikes + 1
+ })
watchdog.call
end
end
+ end
+
+ context 'when process exceeds threshold temporarily' do
+ let(:worker_memory) { max_mem_growth * primary_memory }
+ let(:max_strikes) { 1 }
+ let(:watchdog_iterations) { 4 }
+
+ before do
+ allow(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).and_return(
+ { uss: worker_memory - 0.1 },
+ { uss: worker_memory + 0.2 },
+ { uss: worker_memory - 0.1 },
+ { uss: worker_memory + 0.1 }
+ )
+ allow(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).with(
+ pid: Gitlab::Cluster::PRIMARY_PID
+ ).and_return({ uss: primary_memory })
+ end
+
+ it 'does not signal the handler' do
+ expect(handler).not_to receive(:call)
+
+ watchdog.call
+ end
+ end
+ end
+
+ context 'when monitoring heap fragmentation' do
+ context 'when process does not exceed threshold' do
+ let(:fragmentation) { max_heap_fragmentation - 0.1 }
+
+ it 'does not signal the handler' do
+ expect(handler).not_to receive(:call)
+
+ watchdog.call
+ end
+ end
+
+ context 'when process exceeds threshold permanently' do
+ let(:fragmentation) { max_heap_fragmentation + 0.1 }
+ let(:max_strikes) { 3 }
- context 'when handler result is false' do
- let(:max_strikes) { 0 } # to make sure the handler fires each iteration
- let(:watchdog_iterations) { 3 }
+ it_behaves_like 'has strikes left', 'heap_frag'
- it 'keeps running' do
- expect(heap_frag_violations_counter).to receive(:increment).exactly(watchdog_iterations)
- expect(heap_frag_violations_handled_counter).to receive(:increment).exactly(watchdog_iterations)
- # Return true the third time to terminate the daemon.
- expect(handler).to receive(:on_high_heap_fragmentation).and_return(false, false, true)
+ context 'when process exceeds the allowed number of strikes' do
+ let(:watchdog_iterations) { max_strikes + 1 }
+
+ it_behaves_like 'no strikes left', 'heap_frag'
+
+ it 'logs the event' do
+ expect(Gitlab::Metrics::System).to receive(:memory_usage_rss).at_least(:once).and_return(1024)
+ expect(logger).to receive(:warn).with({
+ message: 'heap fragmentation limit exceeded',
+ pid: Process.pid,
+ worker_id: 'worker_1',
+ memwd_handler_class: 'RSpec::Mocks::InstanceVerifyingDouble',
+ memwd_sleep_time_s: sleep_time,
+ memwd_max_heap_frag: max_heap_fragmentation,
+ memwd_cur_heap_frag: fragmentation,
+ memwd_max_strikes: max_strikes,
+ memwd_cur_strikes: max_strikes + 1,
+ memwd_rss_bytes: 1024
+ })
watchdog.call
end
end
end
- end
- context 'when process exceeds heap fragmentation threshold temporarily' do
- let(:fragmentation) { max_heap_fragmentation }
- let(:max_strikes) { 1 }
- let(:watchdog_iterations) { 4 }
+ context 'when process exceeds threshold temporarily' do
+ let(:fragmentation) { max_heap_fragmentation }
+ let(:max_strikes) { 1 }
+ let(:watchdog_iterations) { 4 }
- before do
- allow(Gitlab::Metrics::Memory).to receive(:gc_heap_fragmentation).and_return(
- fragmentation - 0.1,
- fragmentation + 0.2,
- fragmentation - 0.1,
- fragmentation + 0.1
- )
+ before do
+ allow(Gitlab::Metrics::Memory).to receive(:gc_heap_fragmentation).and_return(
+ fragmentation - 0.1,
+ fragmentation + 0.2,
+ fragmentation - 0.1,
+ fragmentation + 0.1
+ )
+ end
+
+ it 'does not signal the handler' do
+ expect(handler).not_to receive(:call)
+
+ watchdog.call
+ end
end
+ end
- it 'does not signal the handler' do
- expect(handler).not_to receive(:on_high_heap_fragmentation)
+ context 'when both memory fragmentation and growth exceed thresholds' do
+ let(:fragmentation) { max_heap_fragmentation + 0.1 }
+ let(:primary_memory) { 2048 }
+ let(:worker_memory) { max_mem_growth * primary_memory + 1 }
+ let(:watchdog_iterations) { max_strikes + 1 }
+
+ it 'only calls the handler once' do
+ expect(handler).to receive(:call).once.and_return(true)
watchdog.call
end
end
context 'when gitlab_memory_watchdog ops toggle is off' do
- let(:fragmentation) { 0 }
- let(:max_strikes) { 0 }
-
before do
stub_feature_flags(gitlab_memory_watchdog: false)
end
@@ -247,6 +355,12 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
watchdog.call
end
+
+ it 'does not monitor memory growth' do
+ expect(Gitlab::Metrics::System).not_to receive(:memory_usage_uss_pss)
+
+ watchdog.call
+ end
end
end
@@ -254,9 +368,9 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
context 'NullHandler' do
subject(:handler) { described_class::NullHandler.instance }
- describe '#on_high_heap_fragmentation' do
+ describe '#call' do
it 'does nothing' do
- expect(handler.on_high_heap_fragmentation(1.0)).to be(false)
+ expect(handler.call).to be(false)
end
end
end
@@ -264,11 +378,11 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
context 'TermProcessHandler' do
subject(:handler) { described_class::TermProcessHandler.new(42) }
- describe '#on_high_heap_fragmentation' do
+ describe '#call' do
it 'sends SIGTERM to the current process' do
expect(Process).to receive(:kill).with(:TERM, 42)
- expect(handler.on_high_heap_fragmentation(1.0)).to be(true)
+ expect(handler.call).to be(true)
end
end
end
@@ -286,12 +400,12 @@ RSpec.describe Gitlab::Memory::Watchdog, :aggregate_failures, :prometheus do
stub_const('::Puma::Cluster::WorkerHandle', puma_worker_handle_class)
end
- describe '#on_high_heap_fragmentation' do
+ 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.on_high_heap_fragmentation(1.0)).to be(true)
+ expect(handler.call).to be(true)
end
end
end
diff --git a/spec/lib/gitlab/merge_requests/mergeability/results_store_spec.rb b/spec/lib/gitlab/merge_requests/mergeability/results_store_spec.rb
index ed11f8ea6bb..0e8b598730c 100644
--- a/spec/lib/gitlab/merge_requests/mergeability/results_store_spec.rb
+++ b/spec/lib/gitlab/merge_requests/mergeability/results_store_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::MergeRequests::Mergeability::ResultsStore do
subject(:results_store) { described_class.new(merge_request: merge_request, interface: interface) }
diff --git a/spec/lib/gitlab/metrics/dashboard/defaults_spec.rb b/spec/lib/gitlab/metrics/dashboard/defaults_spec.rb
index 1f306753c39..b8556829a59 100644
--- a/spec/lib/gitlab/metrics/dashboard/defaults_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/defaults_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Metrics::Dashboard::Defaults do
it { is_expected.to be_const_defined(:DEFAULT_PANEL_TYPE) }
diff --git a/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb b/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb
index c15e717b126..bc6cd383758 100644
--- a/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb
@@ -24,13 +24,13 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
context 'with existing metrics' do
let(:existing_metric_attributes) do
{
- project: project,
- identifier: 'metric_b',
- title: 'overwrite',
- y_label: 'overwrite',
- query: 'overwrite',
- unit: 'overwrite',
- legend: 'overwrite',
+ project: project,
+ identifier: 'metric_b',
+ title: 'overwrite',
+ y_label: 'overwrite',
+ query: 'overwrite',
+ unit: 'overwrite',
+ legend: 'overwrite',
dashboard_path: dashboard_path
}
end
@@ -43,11 +43,11 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
subject.execute
expect(existing_metric.reload.attributes.with_indifferent_access).to include({
- title: 'Super Chart B',
+ title: 'Super Chart B',
y_label: 'y_label',
- query: 'query',
- unit: 'unit',
- legend: 'Legend Label'
+ query: 'query',
+ unit: 'unit',
+ legend: 'Legend Label'
})
end
@@ -69,11 +69,11 @@ RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do
subject.execute
expect(existing_metric.reload.attributes.with_indifferent_access).to include({
- title: 'Super Chart B',
+ title: 'Super Chart B',
y_label: 'y_label',
- query: 'query',
- unit: 'unit',
- legend: 'Legend Label'
+ query: 'query',
+ unit: 'unit',
+ legend: 'Legend Label'
})
end
diff --git a/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb b/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb
index fdbba6c31b5..a50c2a506cb 100644
--- a/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/validator/errors_spec.rb
@@ -17,11 +17,11 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator::Errors do
let(:error_hash) do
{
- 'data' => 'property_name',
+ 'data' => 'property_name',
'data_pointer' => pointer,
- 'type' => type,
- 'schema' => 'schema',
- 'details' => details
+ 'type' => type,
+ 'schema' => 'schema',
+ 'details' => details
}
end
@@ -72,10 +72,10 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator::Errors do
let(:type) { 'pattern' }
let(:error_hash) do
{
- 'data' => 'property_name',
+ 'data' => 'property_name',
'data_pointer' => pointer,
- 'type' => type,
- 'schema' => { 'pattern' => 'aa.*' }
+ 'type' => type,
+ 'schema' => { 'pattern' => 'aa.*' }
}
end
@@ -86,10 +86,10 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator::Errors do
let(:type) { 'format' }
let(:error_hash) do
{
- 'data' => 'property_name',
+ 'data' => 'property_name',
'data_pointer' => pointer,
- 'type' => type,
- 'schema' => { 'format' => 'date-time' }
+ 'type' => type,
+ 'schema' => { 'format' => 'date-time' }
}
end
@@ -100,10 +100,10 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator::Errors do
let(:type) { 'const' }
let(:error_hash) do
{
- 'data' => 'property_name',
+ 'data' => 'property_name',
'data_pointer' => pointer,
- 'type' => type,
- 'schema' => { 'const' => 'one' }
+ 'type' => type,
+ 'schema' => { 'const' => 'one' }
}
end
@@ -114,10 +114,10 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator::Errors do
let(:type) { 'enum' }
let(:error_hash) do
{
- 'data' => 'property_name',
+ 'data' => 'property_name',
'data_pointer' => pointer,
- 'type' => type,
- 'schema' => { 'enum' => %w(one two) }
+ 'type' => type,
+ 'schema' => { 'enum' => %w(one two) }
}
end
@@ -128,10 +128,10 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator::Errors do
let(:type) { 'unknown' }
let(:error_hash) do
{
- 'data' => 'property_name',
+ 'data' => 'property_name',
'data_pointer' => pointer,
- 'type' => type,
- 'schema' => 'schema'
+ 'type' => type,
+ 'schema' => 'schema'
}
end
diff --git a/spec/lib/gitlab/metrics/dashboard/validator_spec.rb b/spec/lib/gitlab/metrics/dashboard/validator_spec.rb
index eb67ea2b7da..aaa9daf8fee 100644
--- a/spec/lib/gitlab/metrics/dashboard/validator_spec.rb
+++ b/spec/lib/gitlab/metrics/dashboard/validator_spec.rb
@@ -33,9 +33,9 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator do
context 'with metric identifier present in current dashboard' do
before do
create(:prometheus_metric,
- identifier: 'metric_a1',
+ identifier: 'metric_a1',
dashboard_path: 'test/path.yml',
- project: project
+ project: project
)
end
@@ -45,9 +45,9 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator do
context 'with metric identifier present in another dashboard' do
before do
create(:prometheus_metric,
- identifier: 'metric_a1',
+ identifier: 'metric_a1',
dashboard_path: 'some/other/dashboard/path.yml',
- project: project
+ project: project
)
end
@@ -94,9 +94,9 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator do
context 'with metric identifier present in current dashboard' do
before do
create(:prometheus_metric,
- identifier: 'metric_a1',
+ identifier: 'metric_a1',
dashboard_path: 'test/path.yml',
- project: project
+ project: project
)
end
@@ -106,9 +106,9 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator do
context 'with metric identifier present in another dashboard' do
before do
create(:prometheus_metric,
- identifier: 'metric_a1',
+ identifier: 'metric_a1',
dashboard_path: 'some/other/dashboard/path.yml',
- project: project
+ project: project
)
end
@@ -166,9 +166,9 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator do
context 'with metric identifier present in current dashboard' do
before do
create(:prometheus_metric,
- identifier: 'metric_a1',
+ identifier: 'metric_a1',
dashboard_path: 'test/path.yml',
- project: project
+ project: project
)
end
@@ -178,9 +178,9 @@ RSpec.describe Gitlab::Metrics::Dashboard::Validator do
context 'with metric identifier present in another dashboard' do
before do
create(:prometheus_metric,
- identifier: 'metric_a1',
+ identifier: 'metric_a1',
dashboard_path: 'some/other/dashboard/path.yml',
- project: project
+ project: project
)
end
diff --git a/spec/lib/gitlab/metrics/delta_spec.rb b/spec/lib/gitlab/metrics/delta_spec.rb
index e768da875c2..fdbb5e4ce4d 100644
--- a/spec/lib/gitlab/metrics/delta_spec.rb
+++ b/spec/lib/gitlab/metrics/delta_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Metrics::Delta do
let(:delta) { described_class.new }
diff --git a/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb b/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb
index dc5c7eb2e55..fa50adb4e4f 100644
--- a/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb
+++ b/spec/lib/gitlab/metrics/exporter/base_exporter_spec.rb
@@ -10,11 +10,12 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do
describe 'when exporter is enabled' do
before do
allow(::WEBrick::HTTPServer).to receive(:new).with(
- Port: anything,
- BindAddress: anything,
- Logger: anything,
- AccessLog: anything
- ).and_call_original
+ {
+ Port: anything,
+ BindAddress: anything,
+ Logger: anything,
+ AccessLog: anything
+ }).and_call_original
allow(settings).to receive(:enabled).and_return(true)
allow(settings).to receive(:port).and_return(0)
@@ -45,11 +46,12 @@ RSpec.describe Gitlab::Metrics::Exporter::BaseExporter do
it 'starts server with port and address from settings' do
expect(::WEBrick::HTTPServer).to receive(:new).with(
- Port: port,
- BindAddress: address,
- Logger: anything,
- AccessLog: anything
- ).and_wrap_original do |m, *args|
+ {
+ Port: port,
+ BindAddress: address,
+ Logger: anything,
+ AccessLog: anything
+ }).and_wrap_original do |m, *args|
m.call(DoNotListen: true, Logger: args.first[:Logger])
end
diff --git a/spec/lib/gitlab/metrics/global_search_slis_spec.rb b/spec/lib/gitlab/metrics/global_search_slis_spec.rb
new file mode 100644
index 00000000000..28496eff2fc
--- /dev/null
+++ b/spec/lib/gitlab/metrics/global_search_slis_spec.rb
@@ -0,0 +1,173 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Metrics::GlobalSearchSlis do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:apdex_feature_flag_enabled) { true }
+ let(:error_rate_feature_flag_enabled) { true }
+
+ before do
+ stub_feature_flags(global_search_custom_slis: apdex_feature_flag_enabled)
+ stub_feature_flags(global_search_error_rate_sli: error_rate_feature_flag_enabled)
+ end
+
+ describe '#initialize_slis!' do
+ context 'when global_search_custom_slis feature flag is enabled' do
+ let(:apdex_feature_flag_enabled) { true }
+
+ it 'initializes Apdex SLIs for global_search' do
+ expect(Gitlab::Metrics::Sli::Apdex).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 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_custom_slis feature flag is disabled' do
+ let(:apdex_feature_flag_enabled) { false }
+
+ it 'does not initialize the Apdex SLIs for global_search' do
+ expect(Gitlab::Metrics::Sli::Apdex).not_to receive(:initialize_sli)
+
+ 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)
+
+ described_class.initialize_slis!
+ end
+ end
+ end
+
+ describe '#record_apdex' do
+ context 'when global_search_custom_slis feature flag is enabled' do
+ let(:apdex_feature_flag_enabled) { true }
+
+ where(:search_type, :code_search, :duration_target) do
+ 'basic' | false | 7.031
+ 'basic' | true | 21.903
+ 'advanced' | false | 4.865
+ 'advanced' | true | 13.546
+ end
+
+ with_them do
+ before do
+ allow(::Gitlab::ApplicationContext).to receive(:current_context_attribute).with(:caller_id).and_return('end')
+ end
+
+ let(:search_scope) { code_search ? 'blobs' : 'issues' }
+
+ it 'increments the global_search SLI as a success if the elapsed time is within the target' do
+ duration = duration_target - 0.1
+
+ expect(Gitlab::Metrics::Sli::Apdex[:global_search]).to receive(:increment).with(
+ labels: {
+ search_type: search_type,
+ search_level: 'global',
+ search_scope: search_scope,
+ endpoint_id: 'end'
+ },
+ success: true
+ )
+
+ described_class.record_apdex(
+ elapsed: duration,
+ search_type: search_type,
+ search_level: 'global',
+ search_scope: search_scope
+ )
+ end
+
+ it 'increments the global_search SLI as a failure if the elapsed time is not within the target' do
+ duration = duration_target + 0.1
+
+ expect(Gitlab::Metrics::Sli::Apdex[:global_search]).to receive(:increment).with(
+ labels: {
+ search_type: search_type,
+ search_level: 'global',
+ search_scope: search_scope,
+ endpoint_id: 'end'
+ },
+ success: false
+ )
+
+ described_class.record_apdex(
+ elapsed: duration,
+ search_type: search_type,
+ search_level: 'global',
+ search_scope: search_scope
+ )
+ end
+ end
+ end
+
+ context 'when global_search_custom_slis feature flag is disabled' do
+ let(:apdex_feature_flag_enabled) { false }
+
+ it 'does not call increment on the apdex SLI' do
+ expect(Gitlab::Metrics::Sli::Apdex[:global_search]).not_to receive(:increment)
+
+ described_class.record_apdex(
+ elapsed: 1,
+ search_type: 'basic',
+ search_level: 'global',
+ search_scope: 'issues'
+ )
+ end
+ end
+ 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
+ end
+ end
+end
diff --git a/spec/lib/gitlab/metrics/rack_middleware_spec.rb b/spec/lib/gitlab/metrics/rack_middleware_spec.rb
index ab56f38f0c1..21028d18648 100644
--- a/spec/lib/gitlab/metrics/rack_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/rack_middleware_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Metrics::RackMiddleware do
let(:app) { double(:app) }
diff --git a/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb b/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
index 3396de9b12c..ed78548ef62 100644
--- a/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb
@@ -194,9 +194,8 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
let(:endpoint) do
route = double(:route, request_method: 'GET', path: '/:version/projects/:id/archive(.:format)')
- double(:endpoint, route: route,
- options: { for: api_handler, path: [":id/archive"] },
- namespace: "/projects")
+ double(:endpoint,
+ route: route, options: { for: api_handler, path: [":id/archive"] }, namespace: "/projects")
end
let(:env) { { 'api.endpoint' => endpoint, 'REQUEST_METHOD' => 'GET' } }
@@ -256,9 +255,8 @@ RSpec.describe Gitlab::Metrics::RequestsRackMiddleware, :aggregate_failures do
context 'Grape API without expected duration' do
let(:endpoint) do
route = double(:route, request_method: 'GET', path: '/:version/projects/:id/archive(.:format)')
- double(:endpoint, route: route,
- options: { for: api_handler, path: [":id/archive"] },
- namespace: "/projects")
+ double(:endpoint,
+ route: route, options: { for: api_handler, path: [":id/archive"] }, namespace: "/projects")
end
let(:env) { { 'api.endpoint' => endpoint, 'REQUEST_METHOD' => 'GET' } }
diff --git a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
index adbc474343f..67cd8630758 100644
--- a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActionView do
root = Rails.root.to_s
double(:event, duration: 2.1,
- payload: { identifier: "#{root}/app/views/x.html.haml" })
+ payload: { identifier: "#{root}/app/views/x.html.haml" })
end
before do
diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
index 28c3ef229ab..005c1ae2d0a 100644
--- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
@@ -137,7 +137,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
:event,
name: 'transaction.active_record',
duration: 230,
- payload: { connection: connection }
+ payload: { connection: connection }
)
end
@@ -213,7 +213,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
:event,
name: 'sql.active_record',
duration: 2,
- payload: payload
+ payload: payload
)
end
@@ -278,7 +278,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
:event,
name: 'sql.active_record',
duration: 2,
- payload: payload
+ payload: payload
)
end
diff --git a/spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb b/spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb
index bc6effd0438..7f7efaffd9e 100644
--- a/spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/load_balancing_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::LoadBalancing, :request_store do
double(
:event,
name: 'load_balancing.caught_up_replica_pick',
- payload: payload
+ payload: payload
)
end
@@ -37,7 +37,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::LoadBalancing, :request_store do
double(
:event,
name: 'load_balancing.web_transaction_completed',
- payload: {}
+ payload: {}
)
end
diff --git a/spec/lib/gitlab/metrics/system_spec.rb b/spec/lib/gitlab/metrics/system_spec.rb
index ce3caf8cdfe..7739501dd95 100644
--- a/spec/lib/gitlab/metrics/system_spec.rb
+++ b/spec/lib/gitlab/metrics/system_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Metrics::System do
context 'when /proc files exist' do
@@ -72,10 +72,20 @@ RSpec.describe Gitlab::Metrics::System do
end
describe '.memory_usage_rss' do
- it "returns the process' resident set size (RSS) in bytes" do
- mock_existing_proc_file('/proc/self/status', proc_status)
+ context 'without PID' do
+ it "returns the current process' resident set size (RSS) in bytes" do
+ mock_existing_proc_file('/proc/self/status', proc_status)
+
+ expect(described_class.memory_usage_rss).to eq(2527232)
+ end
+ end
+
+ context 'with PID' do
+ it "returns the given process' resident set size (RSS) in bytes" do
+ mock_existing_proc_file('/proc/7/status', proc_status)
- expect(described_class.memory_usage_rss).to eq(2527232)
+ expect(described_class.memory_usage_rss(pid: 7)).to eq(2527232)
+ end
end
end
@@ -96,11 +106,22 @@ RSpec.describe Gitlab::Metrics::System do
end
describe '.memory_usage_uss_pss' do
- it "returns the process' unique and porportional set size (USS/PSS) in bytes" do
- mock_existing_proc_file('/proc/self/smaps_rollup', proc_smaps_rollup)
+ context 'without PID' do
+ it "returns the current process' unique and porportional set size (USS/PSS) in bytes" do
+ mock_existing_proc_file('/proc/self/smaps_rollup', proc_smaps_rollup)
+
+ # (Private_Clean (152 kB) + Private_Dirty (312 kB) + Private_Hugetlb (0 kB)) * 1024
+ expect(described_class.memory_usage_uss_pss).to eq(uss: 475136, pss: 515072)
+ end
+ end
+
+ context 'with PID' do
+ it "returns the given process' unique and porportional set size (USS/PSS) in bytes" do
+ mock_existing_proc_file('/proc/7/smaps_rollup', proc_smaps_rollup)
- # (Private_Clean (152 kB) + Private_Dirty (312 kB) + Private_Hugetlb (0 kB)) * 1024
- expect(described_class.memory_usage_uss_pss).to eq(uss: 475136, pss: 515072)
+ # (Private_Clean (152 kB) + Private_Dirty (312 kB) + Private_Hugetlb (0 kB)) * 1024
+ expect(described_class.memory_usage_uss_pss(pid: 7)).to eq(uss: 475136, pss: 515072)
+ end
end
end
diff --git a/spec/lib/gitlab/metrics/transaction_spec.rb b/spec/lib/gitlab/metrics/transaction_spec.rb
index b1c15db5193..1a8538b5d6a 100644
--- a/spec/lib/gitlab/metrics/transaction_spec.rb
+++ b/spec/lib/gitlab/metrics/transaction_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Metrics::Transaction do
describe '#run' do
diff --git a/spec/lib/gitlab/metrics/web_transaction_spec.rb b/spec/lib/gitlab/metrics/web_transaction_spec.rb
index d6590efcf4f..dc59fa804c4 100644
--- a/spec/lib/gitlab/metrics/web_transaction_spec.rb
+++ b/spec/lib/gitlab/metrics/web_transaction_spec.rb
@@ -66,8 +66,8 @@ RSpec.describe Gitlab::Metrics::WebTransaction do
before do
route = double(:route, request_method: 'GET', path: '/:version/projects/:id/archive(.:format)')
endpoint = double(:endpoint, route: route,
- options: { for: API::Projects, path: [":id/archive"] },
- namespace: "/projects")
+ options: { for: API::Projects, path: [":id/archive"] },
+ namespace: "/projects")
env['api.endpoint'] = endpoint
diff --git a/spec/lib/gitlab/middleware/rack_multipart_tempfile_factory_spec.rb b/spec/lib/gitlab/middleware/rack_multipart_tempfile_factory_spec.rb
index b868207e67c..02c4ea4df27 100644
--- a/spec/lib/gitlab/middleware/rack_multipart_tempfile_factory_spec.rb
+++ b/spec/lib/gitlab/middleware/rack_multipart_tempfile_factory_spec.rb
@@ -2,6 +2,7 @@
require 'fast_spec_helper'
require 'rack'
+require 'tempfile'
RSpec.describe Gitlab::Middleware::RackMultipartTempfileFactory do
let(:app) do
diff --git a/spec/lib/gitlab/middleware/release_env_spec.rb b/spec/lib/gitlab/middleware/release_env_spec.rb
index ca0ec0b9d83..a5bda23b38b 100644
--- a/spec/lib/gitlab/middleware/release_env_spec.rb
+++ b/spec/lib/gitlab/middleware/release_env_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Middleware::ReleaseEnv do
let(:inner_app) { double(:app, call: 'yay') }
diff --git a/spec/lib/gitlab/middleware/sidekiq_web_static_spec.rb b/spec/lib/gitlab/middleware/sidekiq_web_static_spec.rb
index 91c030a0f45..9fb56e45103 100644
--- a/spec/lib/gitlab/middleware/sidekiq_web_static_spec.rb
+++ b/spec/lib/gitlab/middleware/sidekiq_web_static_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Middleware::SidekiqWebStatic do
let(:app) { double(:app) }
diff --git a/spec/lib/gitlab/namespaced_session_store_spec.rb b/spec/lib/gitlab/namespaced_session_store_spec.rb
index a569c86960c..2c258ce3da6 100644
--- a/spec/lib/gitlab/namespaced_session_store_spec.rb
+++ b/spec/lib/gitlab/namespaced_session_store_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::NamespacedSessionStore do
let(:key) { :some_key }
diff --git a/spec/lib/gitlab/nav/top_nav_menu_header_spec.rb b/spec/lib/gitlab/nav/top_nav_menu_header_spec.rb
new file mode 100644
index 00000000000..d9da3ba1e46
--- /dev/null
+++ b/spec/lib/gitlab/nav/top_nav_menu_header_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+RSpec.describe ::Gitlab::Nav::TopNavMenuHeader do
+ describe '.build' do
+ it 'builds a hash from with the given header' do
+ title = 'Test Header'
+ expected = {
+ title: title,
+ type: :header
+ }
+ expect(described_class.build(title: title)).to eq(expected)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/nav/top_nav_menu_item_spec.rb b/spec/lib/gitlab/nav/top_nav_menu_item_spec.rb
index 966b23bf51a..d1d6ac80c40 100644
--- a/spec/lib/gitlab/nav/top_nav_menu_item_spec.rb
+++ b/spec/lib/gitlab/nav/top_nav_menu_item_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe ::Gitlab::Nav::TopNavMenuItem do
describe '.build' do
@@ -17,7 +17,7 @@ RSpec.describe ::Gitlab::Nav::TopNavMenuItem do
emoji: 'smile'
}
- expect(described_class.build(**item)).to eq(item)
+ expect(described_class.build(**item)).to eq(item.merge(type: :item))
end
end
end
diff --git a/spec/lib/gitlab/net_http_adapter_spec.rb b/spec/lib/gitlab/net_http_adapter_spec.rb
index 21c1a1ebe25..fdaf35be31e 100644
--- a/spec/lib/gitlab/net_http_adapter_spec.rb
+++ b/spec/lib/gitlab/net_http_adapter_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::NetHttpAdapter do
describe '#connect' do
diff --git a/spec/lib/gitlab/null_request_store_spec.rb b/spec/lib/gitlab/null_request_store_spec.rb
index 66700313c9a..f68f478c73e 100644
--- a/spec/lib/gitlab/null_request_store_spec.rb
+++ b/spec/lib/gitlab/null_request_store_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::NullRequestStore do
let(:null_store) { described_class.new }
diff --git a/spec/lib/gitlab/omniauth_initializer_spec.rb b/spec/lib/gitlab/omniauth_initializer_spec.rb
index c91b14a33ba..563c97fa2cb 100644
--- a/spec/lib/gitlab/omniauth_initializer_spec.rb
+++ b/spec/lib/gitlab/omniauth_initializer_spec.rb
@@ -31,7 +31,7 @@ RSpec.describe Gitlab::OmniauthInitializer do
context 'when there is an app_id and an app_secret, and an array of args' do
let(:provider) do
{
- 'name' => 'unknown',
+ 'name' => 'unknown',
'app_id' => 1,
'app_secret' => 2,
'args' => %w[one two three]
@@ -46,7 +46,7 @@ RSpec.describe Gitlab::OmniauthInitializer do
context 'when there is an app_id and an app_secret, and an array of args, and default values' do
let(:provider) do
{
- 'name' => 'unknown',
+ 'name' => 'unknown',
'app_id' => 1,
'app_secret' => 2,
'args' => %w[one two three]
@@ -68,7 +68,7 @@ RSpec.describe Gitlab::OmniauthInitializer do
context 'when there is an app_id and an app_secret, and a hash of args' do
let(:provider) do
{
- 'name' => 'unknown',
+ 'name' => 'unknown',
'app_id' => 1,
'app_secret' => 2,
'args' => { 'foo' => 100, 'bar' => 200, 'nested' => { 'value' => 300 } }
@@ -84,7 +84,7 @@ RSpec.describe Gitlab::OmniauthInitializer do
context 'when there is an app_id and an app_secret, and a hash of args, and default arguments' do
let(:provider) do
{
- 'name' => 'unknown',
+ 'name' => 'unknown',
'app_id' => 1,
'app_secret' => 2,
'args' => { 'foo' => 100, 'bar' => 200, 'nested' => { 'value' => 300 } }
@@ -106,7 +106,7 @@ RSpec.describe Gitlab::OmniauthInitializer do
context 'when there is an app_id and an app_secret, no args, and default values' do
let(:provider) do
{
- 'name' => 'unknown',
+ 'name' => 'unknown',
'app_id' => 1,
'app_secret' => 2
}
@@ -127,7 +127,7 @@ RSpec.describe Gitlab::OmniauthInitializer do
context 'when there are args, of an unsupported type' do
let(:provider) do
{
- 'name' => 'unknown',
+ 'name' => 'unknown',
'args' => 1
}
end
diff --git a/spec/lib/gitlab/pagination/keyset/column_order_definition_spec.rb b/spec/lib/gitlab/pagination/keyset/column_order_definition_spec.rb
index 778244677ef..100574cc75f 100644
--- a/spec/lib/gitlab/pagination/keyset/column_order_definition_spec.rb
+++ b/spec/lib/gitlab/pagination/keyset/column_order_definition_spec.rb
@@ -50,6 +50,20 @@ RSpec.describe Gitlab::Pagination::Keyset::ColumnOrderDefinition do
it { expect(project_calculated_column).to be_ascending_order }
it { expect(project_calculated_column).not_to be_descending_order }
+ context 'when order expression is an Arel node with nulls_last' do
+ it 'can automatically determine the reversed expression' do
+ column_order_definition = described_class.new(
+ attribute_name: :name,
+ column_expression: Project.arel_table[:name],
+ order_expression: Project.arel_table[:name].asc.nulls_last,
+ nullable: :nulls_last,
+ distinct: false
+ )
+
+ expect(column_order_definition).to be_ascending_order
+ end
+ end
+
it 'raises error when order direction cannot be infered' do
expect do
described_class.new(
@@ -132,6 +146,21 @@ RSpec.describe Gitlab::Pagination::Keyset::ColumnOrderDefinition do
expect(column_order_definition.reverse.order_expression).to eq('name desc')
end
end
+
+ context 'when order expression is an Arel node with nulls_last' do
+ it 'can automatically determine the reversed expression' do
+ column_order_definition = described_class.new(
+ attribute_name: :name,
+ column_expression: Project.arel_table[:name],
+ order_expression: Project.arel_table[:name].asc.nulls_last,
+ order_direction: :asc,
+ nullable: :nulls_last,
+ distinct: false
+ )
+
+ expect(column_order_definition.reverse.order_expression).to eq(Project.arel_table[:name].desc.nulls_first)
+ end
+ end
end
describe '#nullable' do
diff --git a/spec/lib/gitlab/phabricator_import/representation/task_spec.rb b/spec/lib/gitlab/phabricator_import/representation/task_spec.rb
index 25a52af3a7a..2b8570e4aff 100644
--- a/spec/lib/gitlab/phabricator_import/representation/task_spec.rb
+++ b/spec/lib/gitlab/phabricator_import/representation/task_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::PhabricatorImport::Representation::Task do
subject(:task) do
diff --git a/spec/lib/gitlab/phabricator_import/representation/user_spec.rb b/spec/lib/gitlab/phabricator_import/representation/user_spec.rb
index f51be0f7d8d..6df26b905cc 100644
--- a/spec/lib/gitlab/phabricator_import/representation/user_spec.rb
+++ b/spec/lib/gitlab/phabricator_import/representation/user_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::PhabricatorImport::Representation::User do
subject(:user) do
diff --git a/spec/lib/gitlab/popen/runner_spec.rb b/spec/lib/gitlab/popen/runner_spec.rb
index c7b64e8108b..eacb63c8f8a 100644
--- a/spec/lib/gitlab/popen/runner_spec.rb
+++ b/spec/lib/gitlab/popen/runner_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Popen::Runner do
subject { described_class.new }
diff --git a/spec/lib/gitlab/push_options_spec.rb b/spec/lib/gitlab/push_options_spec.rb
index 8f43943e2d1..3ff1c8e9012 100644
--- a/spec/lib/gitlab/push_options_spec.rb
+++ b/spec/lib/gitlab/push_options_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::PushOptions do
describe 'namespace and key validation' do
diff --git a/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb b/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb
index 8a4e9ab8bb7..08bb06150d4 100644
--- a/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb
+++ b/spec/lib/gitlab/quick_actions/substitution_definition_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::QuickActions::SubstitutionDefinition do
let(:content) do
diff --git a/spec/lib/gitlab/quick_actions/timeline_text_and_date_time_separator_spec.rb b/spec/lib/gitlab/quick_actions/timeline_text_and_date_time_separator_spec.rb
new file mode 100644
index 00000000000..89fe19b8f60
--- /dev/null
+++ b/spec/lib/gitlab/quick_actions/timeline_text_and_date_time_separator_spec.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::QuickActions::TimelineTextAndDateTimeSeparator do
+ subject(:timeline_text_and_datetime_separator) { described_class }
+
+ shared_examples 'arg line with invalid parameters' do
+ it 'returns nil' do
+ expect(timeline_text_and_datetime_separator.new(invalid_arg).execute).to eq(nil)
+ end
+ end
+
+ shared_examples 'arg line with valid parameters' do
+ it 'returns text and date time array' do
+ freeze_time do
+ expect(timeline_text_and_datetime_separator.new(valid_arg).execute).to eq(expected_response)
+ end
+ end
+ end
+
+ describe 'execute' do
+ context 'with invalid parameters in arg line' do
+ context 'with empty arg line' do
+ it_behaves_like 'arg line with invalid parameters' do
+ let(:invalid_arg) { '' }
+ end
+ end
+
+ context 'with invalid date' do
+ it_behaves_like 'arg line with invalid parameters' do
+ let(:invalid_arg) { 'timeline comment | 2022-13-13 09:30' }
+ end
+
+ it_behaves_like 'arg line with invalid parameters' do
+ let(:invalid_arg) { 'timeline comment | 2022-09/09 09:30' }
+ end
+
+ it_behaves_like 'arg line with invalid parameters' do
+ let(:invalid_arg) { 'timeline comment | 2022-09.09 09:30' }
+ end
+ end
+
+ context 'with invalid time' do
+ it_behaves_like 'arg line with invalid parameters' do
+ let(:invalid_arg) { 'timeline comment | 2022-11-13 29:30' }
+ end
+ end
+
+ context 'when date is invalid in arg line' do
+ let(:invalid_arg) { 'timeline comment | wrong data type' }
+
+ it 'return current date' do
+ timeline_args = timeline_text_and_datetime_separator.new(invalid_arg).execute
+
+ expect(timeline_args).to be_an_instance_of(Array)
+ expect(timeline_args.first).to eq('timeline comment')
+ expect(timeline_args.second).to match(Gitlab::QuickActions::TimelineTextAndDateTimeSeparator::DATETIME_REGEX)
+ end
+ end
+ end
+
+ context 'with valid parameters' do
+ context 'when only timeline text present in arg line' do
+ it_behaves_like 'arg line with valid parameters' do
+ let(:timeline_text) { 'timeline comment' }
+ let(:valid_arg) { timeline_text }
+ let(:date) { DateTime.current.strftime("%Y-%m-%d %H:%M:00 UTC") }
+ let(:expected_response) { [timeline_text, date] }
+ end
+ end
+
+ context 'when only timeline text and time present in arg line' do
+ it_behaves_like 'arg line with valid parameters' do
+ let(:timeline_text) { 'timeline comment' }
+ let(:date) { '09:30' }
+ let(:valid_arg) { "#{timeline_text} | #{date}" }
+ let(:parsed_date) { DateTime.parse(date) }
+ let(:expected_response) { [timeline_text, parsed_date] }
+ end
+ end
+
+ context 'when timeline text and date is present in arg line' do
+ it_behaves_like 'arg line with valid parameters' do
+ let(:timeline_text) { 'timeline comment' }
+ let(:date) { '2022-06-05 09:30' }
+ let(:valid_arg) { "#{timeline_text} | #{date}" }
+ let(:parsed_date) { DateTime.parse(date) }
+ let(:expected_response) { [timeline_text, parsed_date] }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/redis/boolean_spec.rb b/spec/lib/gitlab/redis/boolean_spec.rb
index 9c233ba089f..661261c79da 100644
--- a/spec/lib/gitlab/redis/boolean_spec.rb
+++ b/spec/lib/gitlab/redis/boolean_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require "spec_helper"
+require 'fast_spec_helper'
RSpec.describe Gitlab::Redis::Boolean do
subject(:redis_boolean) { described_class.new(bool) }
diff --git a/spec/lib/gitlab/redis/cache_spec.rb b/spec/lib/gitlab/redis/cache_spec.rb
index 1f0ebbe107f..82ff8a26199 100644
--- a/spec/lib/gitlab/redis/cache_spec.rb
+++ b/spec/lib/gitlab/redis/cache_spec.rb
@@ -17,8 +17,8 @@ RSpec.describe Gitlab::Redis::Cache do
end
describe '.active_support_config' do
- it 'has a default ttl of 2 weeks' do
- expect(described_class.active_support_config[:expires_in]).to eq(2.weeks)
+ it 'has a default ttl of 8 hours' do
+ expect(described_class.active_support_config[:expires_in]).to eq(8.hours)
end
it 'allows configuring the TTL through an env variable' do
diff --git a/spec/lib/gitlab/redis/duplicate_jobs_spec.rb b/spec/lib/gitlab/redis/duplicate_jobs_spec.rb
index 53e3d73d17e..be20e6dcdaf 100644
--- a/spec/lib/gitlab/redis/duplicate_jobs_spec.rb
+++ b/spec/lib/gitlab/redis/duplicate_jobs_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe Gitlab::Redis::DuplicateJobs do
expect(redis_instance.primary_store.connection[:id]).to eq("redis://test-host:6379/99")
expect(redis_instance.primary_store.connection[:namespace]).to be_nil
- expect(redis_instance.secondary_store.connection[:id]).to eq("redis:///path/to/redis.sock/0")
+ expect(redis_instance.secondary_store.connection[:id]).to eq("unix:///path/to/redis.sock/0")
expect(redis_instance.secondary_store.connection[:namespace]).to eq("resque:gitlab")
expect(redis_instance.instance_name).to eq('DuplicateJobs')
diff --git a/spec/lib/gitlab/redis/multi_store_spec.rb b/spec/lib/gitlab/redis/multi_store_spec.rb
index ef8549548d7..8b73b5e03c0 100644
--- a/spec/lib/gitlab/redis/multi_store_spec.rb
+++ b/spec/lib/gitlab/redis/multi_store_spec.rb
@@ -264,13 +264,20 @@ RSpec.describe Gitlab::Redis::MultiStore do
context 'when the command is executed within pipelined block' do
subject do
- multi_store.pipelined do
- multi_store.send(name, *args)
+ multi_store.pipelined do |pipeline|
+ pipeline.send(name, *args)
end
end
- it 'is executed only 1 time on primary instance' do
- expect(primary_store).to receive(name).with(*args).once
+ it 'is executed only 1 time on primary and secondary instance' do
+ expect(primary_store).to receive(:pipelined).and_call_original
+ expect(secondary_store).to receive(:pipelined).and_call_original
+
+ 2.times do
+ expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
+ expect(pipeline).to receive(name).with(*args).once.and_call_original
+ end
+ end
subject
end
@@ -438,14 +445,21 @@ RSpec.describe Gitlab::Redis::MultiStore do
context 'when the command is executed within pipelined block' do
subject do
- multi_store.pipelined do
- multi_store.send(name, *args)
+ multi_store.pipelined do |pipeline|
+ pipeline.send(name, *args)
end
end
it 'is executed only 1 time on each instance', :aggregate_errors do
- expect(primary_store).to receive(name).with(*expected_args).once
- expect(secondary_store).to receive(name).with(*expected_args).once
+ expect(primary_store).to receive(:pipelined).and_call_original
+ expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
+ expect(pipeline).to receive(name).with(*expected_args).once.and_call_original
+ end
+
+ expect(secondary_store).to receive(:pipelined).and_call_original
+ expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
+ expect(pipeline).to receive(name).with(*expected_args).once.and_call_original
+ end
subject
end
@@ -781,14 +795,20 @@ RSpec.describe Gitlab::Redis::MultiStore do
context 'when the command is executed within pipelined block' do
subject do
- multi_store.pipelined do
- multi_store.incr(key)
+ multi_store.pipelined do |pipeline|
+ pipeline.incr(key)
end
end
it 'is executed only 1 time on each instance', :aggregate_errors do
- expect(primary_store).to receive(:incr).with(key).once
- expect(secondary_store).to receive(:incr).with(key).once
+ expect(primary_store).to receive(:pipelined).once.and_call_original
+ expect(secondary_store).to receive(:pipelined).once.and_call_original
+
+ 2.times do
+ expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
+ expect(pipeline).to receive(:incr).with(key).once
+ end
+ end
subject
end
diff --git a/spec/lib/gitlab/redis/sidekiq_status_spec.rb b/spec/lib/gitlab/redis/sidekiq_status_spec.rb
index f641ea40efd..76d130d67f7 100644
--- a/spec/lib/gitlab/redis/sidekiq_status_spec.rb
+++ b/spec/lib/gitlab/redis/sidekiq_status_spec.rb
@@ -42,7 +42,7 @@ RSpec.describe Gitlab::Redis::SidekiqStatus do
expect(redis_instance).to be_instance_of(::Gitlab::Redis::MultiStore)
expect(redis_instance.primary_store.connection[:id]).to eq("redis://test-host:6379/99")
- expect(redis_instance.secondary_store.connection[:id]).to eq("redis:///path/to/redis.sock/0")
+ expect(redis_instance.secondary_store.connection[:id]).to eq("unix:///path/to/redis.sock/0")
expect(redis_instance.instance_name).to eq('SidekiqStatus')
end
diff --git a/spec/lib/gitlab/render_timeout_spec.rb b/spec/lib/gitlab/render_timeout_spec.rb
index f322d71867b..b1386855fd5 100644
--- a/spec/lib/gitlab/render_timeout_spec.rb
+++ b/spec/lib/gitlab/render_timeout_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::RenderTimeout do
def expect_timeout(period)
diff --git a/spec/lib/gitlab/seeder_spec.rb b/spec/lib/gitlab/seeder_spec.rb
index 0ad80323085..a94ae2bca7a 100644
--- a/spec/lib/gitlab/seeder_spec.rb
+++ b/spec/lib/gitlab/seeder_spec.rb
@@ -77,44 +77,4 @@ RSpec.describe Gitlab::Seeder do
end
end
end
-
- describe ::Gitlab::Seeder::Ci::DailyBuildGroupReportResult do
- let_it_be(:group) { create(:group) }
- let_it_be(:project) { create(:project, :repository, group: group) }
- let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
- let_it_be(:build) { create(:ci_build, :success, pipeline: pipeline) }
-
- subject(:build_report) do
- described_class.new(project)
- end
-
- describe '#seed' do
- it 'creates daily build results for the project' do
- expect { build_report.seed }.to change {
- Ci::DailyBuildGroupReportResult.count
- }.by(Gitlab::Seeder::Ci::DailyBuildGroupReportResult::COUNT_OF_DAYS)
- end
-
- it 'matches project data with last report' do
- build_report.seed
-
- report = project.daily_build_group_report_results.last
- reports_count = project.daily_build_group_report_results.count
-
- expect(build.group_name).to eq(report.group_name)
- expect(pipeline.source_ref_path).to eq(report.ref_path)
- expect(pipeline.default_branch?).to eq(report.default_branch)
- expect(reports_count).to eq(Gitlab::Seeder::Ci::DailyBuildGroupReportResult::COUNT_OF_DAYS)
- end
-
- it 'does not raise error on RecordNotUnique' do
- build_report.seed
- build_report.seed
-
- reports_count = project.daily_build_group_report_results.count
-
- expect(reports_count).to eq(Gitlab::Seeder::Ci::DailyBuildGroupReportResult::COUNT_OF_DAYS)
- end
- end
- end
end
diff --git a/spec/lib/gitlab/seeders/ci/daily_build_group_report_result_spec.rb b/spec/lib/gitlab/seeders/ci/daily_build_group_report_result_spec.rb
new file mode 100644
index 00000000000..4b41122d23c
--- /dev/null
+++ b/spec/lib/gitlab/seeders/ci/daily_build_group_report_result_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Gitlab::Seeders::Ci::DailyBuildGroupReportResult do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :repository, group: group) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:build) { create(:ci_build, :success, pipeline: pipeline) }
+
+ subject(:build_report) do
+ described_class.new(project)
+ end
+
+ describe '#seed' do
+ it 'creates daily build results for the project' do
+ expect { build_report.seed }.to change {
+ Ci::DailyBuildGroupReportResult.count
+ }.by(Gitlab::Seeders::Ci::DailyBuildGroupReportResult::COUNT_OF_DAYS)
+ end
+
+ it 'matches project data with last report' do
+ build_report.seed
+
+ report = project.daily_build_group_report_results.last
+ reports_count = project.daily_build_group_report_results.count
+
+ expect(build.group_name).to eq(report.group_name)
+ expect(pipeline.source_ref_path).to eq(report.ref_path)
+ expect(pipeline.default_branch?).to eq(report.default_branch)
+ expect(reports_count).to eq(Gitlab::Seeders::Ci::DailyBuildGroupReportResult::COUNT_OF_DAYS)
+ end
+
+ it 'does not raise error on RecordNotUnique' do
+ build_report.seed
+ build_report.seed
+
+ reports_count = project.daily_build_group_report_results.count
+
+ expect(reports_count).to eq(Gitlab::Seeders::Ci::DailyBuildGroupReportResult::COUNT_OF_DAYS)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/service_desk_email_spec.rb b/spec/lib/gitlab/service_desk_email_spec.rb
index 9847496e361..6667b61c02b 100644
--- a/spec/lib/gitlab/service_desk_email_spec.rb
+++ b/spec/lib/gitlab/service_desk_email_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::ServiceDeskEmail do
describe '.enabled?' do
diff --git a/spec/lib/gitlab/session_spec.rb b/spec/lib/gitlab/session_spec.rb
index 67ad59f956d..171288da1d5 100644
--- a/spec/lib/gitlab/session_spec.rb
+++ b/spec/lib/gitlab/session_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Session do
it 'uses the current thread as a data store' do
diff --git a/spec/lib/gitlab/setup_helper/workhorse_spec.rb b/spec/lib/gitlab/setup_helper/workhorse_spec.rb
index 18cb266bf4e..726b73a9dfe 100644
--- a/spec/lib/gitlab/setup_helper/workhorse_spec.rb
+++ b/spec/lib/gitlab/setup_helper/workhorse_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::SetupHelper::Workhorse do
describe '.make' do
diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb
index 891b3639709..785429aa3b0 100644
--- a/spec/lib/gitlab/shell_spec.rb
+++ b/spec/lib/gitlab/shell_spec.rb
@@ -9,9 +9,13 @@ RSpec.describe Gitlab::Shell do
let(:repository) { project.repository }
let(:gitlab_shell) { described_class.new }
+ before do
+ described_class.instance_variable_set(:@secret_token, nil)
+ end
+
it { is_expected.to respond_to :remove_repository }
- describe 'memoized secret_token' do
+ describe '.secret_token' do
let(:secret_file) { 'tmp/tests/.secret_shell_test' }
let(:link_file) { 'tmp/tests/shell-secret-test/.gitlab_shell_secret' }
@@ -19,7 +23,6 @@ RSpec.describe Gitlab::Shell do
allow(Gitlab.config.gitlab_shell).to receive(:secret_file).and_return(secret_file)
allow(Gitlab.config.gitlab_shell).to receive(:path).and_return('tmp/tests/shell-secret-test')
FileUtils.mkdir('tmp/tests/shell-secret-test')
- described_class.ensure_secret_token!
end
after do
@@ -27,13 +30,47 @@ RSpec.describe Gitlab::Shell do
FileUtils.rm_rf(secret_file)
end
- it 'creates and links the secret token file' do
- secret_token = described_class.secret_token
+ shared_examples 'creates and links the secret token file' do
+ it 'creates and links the secret token file' do
+ secret_token = described_class.secret_token
+
+ expect(File.exist?(secret_file)).to be(true)
+ expect(File.read(secret_file).chomp).to eq(secret_token)
+ expect(File.symlink?(link_file)).to be(true)
+ expect(File.readlink(link_file)).to eq(secret_file)
+ end
+ end
+
+ describe 'memoized secret_token' do
+ before do
+ described_class.ensure_secret_token!
+ end
+
+ it_behaves_like 'creates and links the secret token file'
+ end
+
+ context 'when link_file is a broken symbolic link' do
+ before do
+ File.symlink('tmp/tests/non_existing_file', link_file)
+ described_class.ensure_secret_token!
+ end
+
+ it_behaves_like 'creates and links the secret token file'
+ end
+
+ context 'when secret_file exists' do
+ let(:secret_token) { 'secret-token' }
- expect(File.exist?(secret_file)).to be(true)
- expect(File.read(secret_file).chomp).to eq(secret_token)
- expect(File.symlink?(link_file)).to be(true)
- expect(File.readlink(link_file)).to eq(secret_file)
+ before do
+ File.write(secret_file, 'secret-token')
+ described_class.ensure_secret_token!
+ end
+
+ it_behaves_like 'creates and links the secret token file'
+
+ it 'reads the token from the existing file' do
+ expect(described_class.secret_token).to eq(secret_token)
+ 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 635f572daef..dff04a2e509 100644
--- a/spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb
+++ b/spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb
@@ -326,7 +326,7 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
class: described_class.to_s,
signal: signal,
pid: pid,
- message: "sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})")
+ message: "sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})")
expect(Process).to receive(:kill).with(signal, pid).ordered
subject
@@ -340,7 +340,7 @@ RSpec.describe Gitlab::SidekiqDaemon::MemoryKiller do
class: described_class.to_s,
signal: signal,
pid: pid,
- message: "sending Sidekiq worker PGRP-#{pid} #{signal} (#{explanation})")
+ message: "sending Sidekiq worker PGRP-#{pid} #{signal} (#{explanation})")
expect(Process).to receive(:kill).with(signal, 0).ordered
subject
diff --git a/spec/lib/gitlab/sidekiq_death_handler_spec.rb b/spec/lib/gitlab/sidekiq_death_handler_spec.rb
index e3f9f8277a0..434642bf3ef 100644
--- a/spec/lib/gitlab/sidekiq_death_handler_spec.rb
+++ b/spec/lib/gitlab/sidekiq_death_handler_spec.rb
@@ -24,8 +24,8 @@ RSpec.describe Gitlab::SidekiqDeathHandler, :clean_gitlab_redis_queues do
expect(described_class.counter)
.to receive(:increment)
.with({ queue: 'test_queue', worker: 'TestWorker',
- urgency: 'low', external_dependencies: 'yes',
- feature_category: 'users', boundary: 'cpu' })
+ urgency: 'low', external_dependencies: 'yes',
+ feature_category: 'users', boundary: 'cpu' })
described_class.handler({ 'class' => 'TestWorker', 'queue' => 'test_queue' }, nil)
end
@@ -40,8 +40,8 @@ RSpec.describe Gitlab::SidekiqDeathHandler, :clean_gitlab_redis_queues do
expect(described_class.counter)
.to receive(:increment)
.with({ queue: 'test_queue', worker: 'TestWorker',
- urgency: '', external_dependencies: 'no',
- feature_category: '', boundary: '' })
+ urgency: '', external_dependencies: 'no',
+ feature_category: '', boundary: '' })
described_class.handler({ 'class' => 'TestWorker', 'queue' => 'test_queue' }, nil)
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..cc730e203f6 100644
--- a/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb
@@ -41,10 +41,10 @@ RSpec.describe Gitlab::SidekiqMiddleware::DuplicateJobs::Server, :clean_gitlab_r
describe '#call' do
it 'removes the stored job from redis before execution' do
bare_job = { 'class' => 'TestDeduplicationWorker', 'args' => ['hello'] }
- job_definition = Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob.new(bare_job.dup, 'test_deduplication')
+ job_definition = Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob.new(bare_job.dup, 'default')
expect(Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob)
- .to receive(:new).with(a_hash_including(bare_job), 'test_deduplication')
+ .to receive(:new).with(a_hash_including(bare_job), 'default')
.and_return(job_definition).twice # once in client middleware
expect(job_definition).to receive(:delete!).ordered.and_call_original
@@ -60,10 +60,10 @@ RSpec.describe Gitlab::SidekiqMiddleware::DuplicateJobs::Server, :clean_gitlab_r
it 'removes the stored job from redis after execution' do
bare_job = { 'class' => 'TestDeduplicationWorker', 'args' => ['hello'] }
- job_definition = Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob.new(bare_job.dup, 'test_deduplication')
+ job_definition = Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob.new(bare_job.dup, 'default')
expect(Gitlab::SidekiqMiddleware::DuplicateJobs::DuplicateJob)
- .to receive(:new).with(a_hash_including(bare_job), 'test_deduplication')
+ .to receive(:new).with(a_hash_including(bare_job), 'default')
.and_return(job_definition).twice # once in client middleware
expect(TestDeduplicationWorker).to receive(:work).ordered.and_call_original
diff --git a/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb b/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb
index d6d24ea3a24..52b50a143fc 100644
--- a/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb
@@ -22,39 +22,39 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics do
expect(completion_seconds_metric)
.to receive(:get).with({ queue: 'merge',
- worker: 'MergeWorker',
- urgency: 'high',
- external_dependencies: 'no',
- feature_category: 'source_code_management',
- boundary: '',
- job_status: 'done' })
+ worker: 'MergeWorker',
+ urgency: 'high',
+ external_dependencies: 'no',
+ feature_category: 'source_code_management',
+ boundary: '',
+ job_status: 'done' })
expect(completion_seconds_metric)
.to receive(:get).with({ queue: 'merge',
- worker: 'MergeWorker',
- urgency: 'high',
- external_dependencies: 'no',
- feature_category: 'source_code_management',
- boundary: '',
- job_status: 'fail' })
+ worker: 'MergeWorker',
+ urgency: 'high',
+ external_dependencies: 'no',
+ feature_category: 'source_code_management',
+ boundary: '',
+ job_status: 'fail' })
expect(completion_seconds_metric)
.to receive(:get).with({ queue: 'default',
- worker: 'Ci::BuildFinishedWorker',
- urgency: 'high',
- external_dependencies: 'no',
- feature_category: 'continuous_integration',
- boundary: 'cpu',
- job_status: 'done' })
+ worker: 'Ci::BuildFinishedWorker',
+ urgency: 'high',
+ external_dependencies: 'no',
+ feature_category: 'continuous_integration',
+ boundary: 'cpu',
+ job_status: 'done' })
expect(completion_seconds_metric)
.to receive(:get).with({ queue: 'default',
- worker: 'Ci::BuildFinishedWorker',
- urgency: 'high',
- external_dependencies: 'no',
- feature_category: 'continuous_integration',
- boundary: 'cpu',
- job_status: 'fail' })
+ worker: 'Ci::BuildFinishedWorker',
+ urgency: 'high',
+ external_dependencies: 'no',
+ feature_category: 'continuous_integration',
+ boundary: 'cpu',
+ job_status: 'fail' })
described_class.initialize_process_metrics
end
diff --git a/spec/lib/gitlab/sidekiq_middleware/size_limiter/server_spec.rb b/spec/lib/gitlab/sidekiq_middleware/size_limiter/server_spec.rb
index 91b8ef97ab4..12430313141 100644
--- a/spec/lib/gitlab/sidekiq_middleware/size_limiter/server_spec.rb
+++ b/spec/lib/gitlab/sidekiq_middleware/size_limiter/server_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
# rubocop: disable RSpec/MultipleMemoizedHelpers
RSpec.describe Gitlab::SidekiqMiddleware::SizeLimiter::Server, :clean_gitlab_redis_queues do
diff --git a/spec/lib/gitlab/sidekiq_migrate_jobs_spec.rb b/spec/lib/gitlab/sidekiq_migrate_jobs_spec.rb
index d4391d3023a..a576cf3e2ab 100644
--- a/spec/lib/gitlab/sidekiq_migrate_jobs_spec.rb
+++ b/spec/lib/gitlab/sidekiq_migrate_jobs_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do
expect(migrator.execute('PostReceive' => 'new_queue')).to eq(scanned: 3, migrated: 0)
expect(set_after.length).to eq(3)
- expect(set_after.map(&:first)).to all(include('queue' => 'authorized_projects',
+ expect(set_after.map(&:first)).to all(include('queue' => 'default',
'class' => 'AuthorizedProjectsWorker'))
end
end
@@ -62,7 +62,7 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do
if item['class'] == 'AuthorizedProjectsWorker'
expect(item).to include('queue' => 'new_queue', 'args' => [i])
else
- expect(item).to include('queue' => 'post_receive', 'args' => [i])
+ expect(item).to include('queue' => 'default', 'args' => [i])
end
expect(score).to be_within(schedule_jitter).of(i.succ.hours.from_now.to_i)
@@ -116,7 +116,7 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do
expect(migrator.execute('PostReceive' => 'new_queue')).to eq(scanned: 4, migrated: 0)
expect(set_after.length).to eq(3)
- expect(set_after.map(&:first)).to all(include('queue' => 'authorized_projects'))
+ expect(set_after.map(&:first)).to all(include('queue' => 'default'))
end
end
@@ -138,7 +138,7 @@ RSpec.describe Gitlab::SidekiqMigrateJobs, :clean_gitlab_redis_queues do
expect(migrator.execute('PostReceive' => 'new_queue')).to eq(scanned: 4, migrated: 1)
expect(set_after.group_by { |job| job.first['queue'] }.transform_values(&:count))
- .to eq('authorized_projects' => 6, 'new_queue' => 1)
+ .to eq('default' => 6, 'new_queue' => 1)
end
it 'iterates through the entire set of jobs' do
diff --git a/spec/lib/gitlab/sidekiq_queue_spec.rb b/spec/lib/gitlab/sidekiq_queue_spec.rb
index 5e91282612e..93632848788 100644
--- a/spec/lib/gitlab/sidekiq_queue_spec.rb
+++ b/spec/lib/gitlab/sidekiq_queue_spec.rb
@@ -4,15 +4,15 @@ require 'spec_helper'
RSpec.describe Gitlab::SidekiqQueue, :clean_gitlab_redis_queues do
around do |example|
- Sidekiq::Queue.new('default').clear
+ Sidekiq::Queue.new('foobar').clear
Sidekiq::Testing.disable!(&example)
- Sidekiq::Queue.new('default').clear
+ Sidekiq::Queue.new('foobar').clear
end
def add_job(args, user:, klass: 'AuthorizedProjectsWorker')
Sidekiq::Client.push(
'class' => klass,
- 'queue' => 'default',
+ 'queue' => 'foobar',
'args' => args,
'meta.user' => user.username
)
@@ -20,7 +20,7 @@ RSpec.describe Gitlab::SidekiqQueue, :clean_gitlab_redis_queues do
describe '#drop_jobs!' do
shared_examples 'queue processing' do
- let(:sidekiq_queue) { described_class.new('default') }
+ let(:sidekiq_queue) { described_class.new('foobar') }
let_it_be(:sidekiq_queue_user) { create(:user) }
before do
@@ -80,7 +80,7 @@ RSpec.describe Gitlab::SidekiqQueue, :clean_gitlab_redis_queues do
it 'raises NoMetadataError' do
add_job([1], user: create(:user))
- expect { described_class.new('default').drop_jobs!({ username: 'sidekiq_queue_user' }, timeout: 1) }
+ expect { described_class.new('foobar').drop_jobs!({ username: 'sidekiq_queue_user' }, timeout: 1) }
.to raise_error(described_class::NoMetadataError)
end
end
diff --git a/spec/lib/gitlab/sidekiq_signals_spec.rb b/spec/lib/gitlab/sidekiq_signals_spec.rb
index 2f751839f6a..734b9e79088 100644
--- a/spec/lib/gitlab/sidekiq_signals_spec.rb
+++ b/spec/lib/gitlab/sidekiq_signals_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::SidekiqSignals do
describe '.install' do
diff --git a/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb b/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb
index 5a0c4cbd1b5..c0fd88eab1b 100644
--- a/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb
+++ b/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::SidekiqStatus::ServerMiddleware do
describe '#call' do
diff --git a/spec/lib/gitlab/sidekiq_versioning_spec.rb b/spec/lib/gitlab/sidekiq_versioning_spec.rb
index afafd04d87d..bdbba04e0c0 100644
--- a/spec/lib/gitlab/sidekiq_versioning_spec.rb
+++ b/spec/lib/gitlab/sidekiq_versioning_spec.rb
@@ -2,30 +2,9 @@
require 'spec_helper'
-RSpec.describe Gitlab::SidekiqVersioning, :redis do
- let(:foo_worker) do
- Class.new do
- def self.name
- 'FooWorker'
- end
-
- include ApplicationWorker
- end
- end
-
- let(:bar_worker) do
- Class.new do
- def self.name
- 'BarWorker'
- end
-
- include ApplicationWorker
- end
- end
-
+RSpec.describe Gitlab::SidekiqVersioning, :clean_gitlab_redis_queues do
before do
- allow(Gitlab::SidekiqConfig).to receive(:workers).and_return([foo_worker, bar_worker])
- allow(Gitlab::SidekiqConfig).to receive(:worker_queues).and_return([foo_worker.queue, bar_worker.queue])
+ allow(Gitlab::SidekiqConfig).to receive(:worker_queues).and_return(%w[foo bar])
end
describe '.install!' do
diff --git a/spec/lib/gitlab/slug/environment_spec.rb b/spec/lib/gitlab/slug/environment_spec.rb
index f516322b937..e8f0fba27b2 100644
--- a/spec/lib/gitlab/slug/environment_spec.rb
+++ b/spec/lib/gitlab/slug/environment_spec.rb
@@ -1,27 +1,27 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Slug::Environment do
describe '#generate' do
{
"staging-12345678901234567" => "staging-123456789-q517sa",
"9-staging-123456789012345" => "env-9-staging-123-q517sa",
- "staging-1234567890123456" => "staging-1234567890123456",
+ "staging-1234567890123456" => "staging-1234567890123456",
"staging-1234567890123456-" => "staging-123456789-q517sa",
- "production" => "production",
- "PRODUCTION" => "production-q517sa",
- "review/1-foo" => "review-1-foo-q517sa",
- "1-foo" => "env-1-foo-q517sa",
- "1/foo" => "env-1-foo-q517sa",
- "foo-" => "foo",
- "foo--bar" => "foo-bar-q517sa",
- "foo**bar" => "foo-bar-q517sa",
- "*-foo" => "env-foo-q517sa",
- "staging-12345678-" => "staging-12345678",
+ "production" => "production",
+ "PRODUCTION" => "production-q517sa",
+ "review/1-foo" => "review-1-foo-q517sa",
+ "1-foo" => "env-1-foo-q517sa",
+ "1/foo" => "env-1-foo-q517sa",
+ "foo-" => "foo",
+ "foo--bar" => "foo-bar-q517sa",
+ "foo**bar" => "foo-bar-q517sa",
+ "*-foo" => "env-foo-q517sa",
+ "staging-12345678-" => "staging-12345678",
"staging-12345678-01234567" => "staging-12345678-q517sa",
- "" => "env-q517sa",
- nil => "env-q517sa"
+ "" => "env-q517sa",
+ nil => "env-q517sa"
}.each do |name, matcher|
before do
# ('a' * 64).to_i(16).to_s(36).last(6) gives 'q517sa'
diff --git a/spec/lib/gitlab/spamcheck/client_spec.rb b/spec/lib/gitlab/spamcheck/client_spec.rb
index 956ed2a976f..2fe978125c4 100644
--- a/spec/lib/gitlab/spamcheck/client_spec.rb
+++ b/spec/lib/gitlab/spamcheck/client_spec.rb
@@ -17,6 +17,7 @@ RSpec.describe Gitlab::Spamcheck::Client do
end
let_it_be(:issue) { create(:issue, description: 'Test issue description') }
+ let_it_be(:snippet) { create(:personal_snippet, :public, description: 'Test issue description') }
let(:response) do
verdict = ::Spamcheck::SpamVerdict.new
@@ -26,7 +27,7 @@ RSpec.describe Gitlab::Spamcheck::Client do
verdict
end
- subject { described_class.new.issue_spam?(spam_issue: issue, user: user) }
+ subject { described_class.new.spam?(spammable: issue, user: user) }
before do
stub_application_setting(spam_check_endpoint_url: endpoint)
@@ -56,10 +57,11 @@ RSpec.describe Gitlab::Spamcheck::Client do
end
end
- describe '#issue_spam?' do
+ shared_examples 'check for spam' do
before do
allow_next_instance_of(::Spamcheck::SpamcheckService::Stub) do |instance|
allow(instance).to receive(:check_for_spam_issue).and_return(response)
+ allow(instance).to receive(:check_for_spam_snippet).and_return(response)
end
end
@@ -89,12 +91,26 @@ RSpec.describe Gitlab::Spamcheck::Client do
end
end
- describe "#build_issue_protobuf", :aggregate_failures do
- it 'builds the expected protobuf object' do
+ describe "#spam?", :aggregate_failures do
+ describe 'issue' do
+ subject { described_class.new.spam?(spammable: issue, user: user) }
+
+ it_behaves_like "check for spam"
+ end
+
+ describe 'snippet' do
+ subject { described_class.new.spam?(spammable: snippet, user: user, extra_features: { files: [{ path: "file.rb" }] }) }
+
+ it_behaves_like "check for spam"
+ end
+ end
+
+ describe "#build_protobuf", :aggregate_failures do
+ it 'builds the expected issue protobuf object' do
cxt = { action: :create }
- issue_pb = described_class.new.send(:build_issue_protobuf,
- issue: issue, user: user,
- context: cxt)
+ issue_pb, _ = described_class.new.send(:build_protobuf,
+ spammable: issue, user: user,
+ context: cxt, extra_features: {})
expect(issue_pb.title).to eq issue.title
expect(issue_pb.description).to eq issue.description
expect(issue_pb.user_in_project).to be false
@@ -104,6 +120,22 @@ RSpec.describe Gitlab::Spamcheck::Client do
expect(issue_pb.action).to be ::Spamcheck::Action.lookup(::Spamcheck::Action::CREATE)
expect(issue_pb.user.username).to eq user.username
end
+
+ it 'builds the expected snippet protobuf object' do
+ cxt = { action: :create }
+ snippet_pb, _ = described_class.new.send(:build_protobuf,
+ spammable: snippet, user: user,
+ context: cxt, extra_features: { files: [{ path: 'first.rb' }, { path: 'second.rb' }] })
+ expect(snippet_pb.title).to eq snippet.title
+ expect(snippet_pb.description).to eq snippet.description
+ expect(snippet_pb.created_at).to eq timestamp_to_protobuf_timestamp(snippet.created_at)
+ expect(snippet_pb.updated_at).to eq timestamp_to_protobuf_timestamp(snippet.updated_at)
+ expect(snippet_pb.action).to be ::Spamcheck::Action.lookup(::Spamcheck::Action::CREATE)
+ expect(snippet_pb.user.username).to eq user.username
+ expect(snippet_pb.user.username).to eq user.username
+ expect(snippet_pb.files.first.path).to eq 'first.rb'
+ expect(snippet_pb.files.last.path).to eq 'second.rb'
+ end
end
describe '#build_user_protobuf', :aggregate_failures do
@@ -143,6 +175,19 @@ RSpec.describe Gitlab::Spamcheck::Client do
end
end
+ describe "#get_spammable_mappings", :aggregate_failures do
+ it 'is an expected spammable' do
+ protobuf_class, _ = described_class.new.send(:get_spammable_mappings, issue)
+ expect(protobuf_class).to eq ::Spamcheck::Issue
+ end
+
+ it 'is an unexpected spammable' do
+ expect { described_class.new.send(:get_spammable_mappings, 'spam') }.to raise_error(
+ ArgumentError, 'Not a spammable type: String'
+ )
+ end
+ end
+
private
def timestamp_to_protobuf_timestamp(timestamp)
diff --git a/spec/lib/gitlab/string_placeholder_replacer_spec.rb b/spec/lib/gitlab/string_placeholder_replacer_spec.rb
index 8f17bf64005..9f477998be2 100644
--- a/spec/lib/gitlab/string_placeholder_replacer_spec.rb
+++ b/spec/lib/gitlab/string_placeholder_replacer_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::StringPlaceholderReplacer do
describe '.render_url' do
diff --git a/spec/lib/gitlab/string_range_marker_spec.rb b/spec/lib/gitlab/string_range_marker_spec.rb
index 6f63c8e2df4..2ababd6a938 100644
--- a/spec/lib/gitlab/string_range_marker_spec.rb
+++ b/spec/lib/gitlab/string_range_marker_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::StringRangeMarker do
describe '#mark' do
diff --git a/spec/lib/gitlab/string_regex_marker_spec.rb b/spec/lib/gitlab/string_regex_marker_spec.rb
index 0cbe44eacf4..393bfea7c6b 100644
--- a/spec/lib/gitlab/string_regex_marker_spec.rb
+++ b/spec/lib/gitlab/string_regex_marker_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::StringRegexMarker do
describe '#mark' do
diff --git a/spec/lib/gitlab/subscription_portal_spec.rb b/spec/lib/gitlab/subscription_portal_spec.rb
index 098a58bff83..f93eb6f96cc 100644
--- a/spec/lib/gitlab/subscription_portal_spec.rb
+++ b/spec/lib/gitlab/subscription_portal_spec.rb
@@ -53,12 +53,13 @@ RSpec.describe ::Gitlab::SubscriptionPortal do
it { is_expected.to match(link_match) }
end
- context 'url methods' do
+ describe 'class methods' do
where(:method_name, :result) do
:default_subscriptions_url | staging_customers_url
:payment_form_url | "#{staging_customers_url}/payment_forms/cc_validation"
:payment_validation_form_id | 'payment_method_validation'
:registration_validation_form_url | "#{staging_customers_url}/payment_forms/cc_registration_validation"
+ :registration_validation_form_id | 'cc_registration_validation'
:subscriptions_graphql_url | "#{staging_customers_url}/graphql"
:subscriptions_more_minutes_url | "#{staging_customers_url}/buy_pipeline_minutes"
:subscriptions_more_storage_url | "#{staging_customers_url}/buy_storage"
@@ -108,4 +109,16 @@ RSpec.describe ::Gitlab::SubscriptionPortal do
is_expected.to eq(url)
end
end
+
+ describe 'constants' do
+ where(:constant_name, :result) do
+ 'REGISTRATION_VALIDATION_FORM_ID' | 'cc_registration_validation'
+ end
+
+ with_them do
+ subject { "#{described_class}::#{constant_name}".constantize }
+
+ it { is_expected.to eq(result) }
+ end
+ end
end
diff --git a/spec/lib/gitlab/tcp_checker_spec.rb b/spec/lib/gitlab/tcp_checker_spec.rb
index 12149576de0..5f9960265ec 100644
--- a/spec/lib/gitlab/tcp_checker_spec.rb
+++ b/spec/lib/gitlab/tcp_checker_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::TcpChecker, :permit_dns do
before do
diff --git a/spec/lib/gitlab/tracking/incident_management_spec.rb b/spec/lib/gitlab/tracking/incident_management_spec.rb
index ef7816aa0db..c27e2548526 100644
--- a/spec/lib/gitlab/tracking/incident_management_spec.rb
+++ b/spec/lib/gitlab/tracking/incident_management_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Tracking::IncidentManagement do
describe '.track_from_params' do
diff --git a/spec/lib/gitlab/tracking_spec.rb b/spec/lib/gitlab/tracking_spec.rb
index 028c985f3b3..e11175c776d 100644
--- a/spec/lib/gitlab/tracking_spec.rb
+++ b/spec/lib/gitlab/tracking_spec.rb
@@ -132,9 +132,36 @@ RSpec.describe Gitlab::Tracking do
expect(args[:context].last).to eq(other_context)
end
- described_class.event('category', 'action', label: 'label', property: 'property', value: 1.5,
- context: [other_context], project: project, user: user, namespace: namespace,
- extra_key_1: 'extra value 1', extra_key_2: 'extra value 2')
+ described_class.event('category', 'action',
+ label: 'label',
+ property: 'property',
+ value: 1.5,
+ context: [other_context],
+ project: project,
+ user: user,
+ namespace: namespace,
+ extra_key_1: 'extra value 1',
+ extra_key_2: 'extra value 2')
+ end
+ end
+
+ context 'when the action is not passed in as a string' do
+ it 'allows symbols' do
+ expect(Gitlab::ErrorTracking).not_to receive(:track_and_raise_for_dev_exception)
+
+ described_class.event('category', :some_action)
+ end
+
+ it 'allows nil' do
+ expect(Gitlab::ErrorTracking).not_to receive(:track_and_raise_for_dev_exception)
+
+ described_class.event('category', nil)
+ end
+
+ it 'allows integers' do
+ expect(Gitlab::ErrorTracking).not_to receive(:track_and_raise_for_dev_exception)
+
+ described_class.event('category', 1)
end
end
@@ -197,8 +224,15 @@ RSpec.describe Gitlab::Tracking do
expect(args[:extra_key_1]).to eq('extra value 1')
end
- described_class.definition('filename', category: nil, action: nil, label: 'label', property: '...',
- project: project, user: user, namespace: namespace, extra_key_1: 'extra value 1')
+ described_class.definition('filename',
+ category: nil,
+ action: nil,
+ label: 'label',
+ property: '...',
+ project: project,
+ user: user,
+ namespace: namespace,
+ extra_key_1: 'extra value 1')
end
end
diff --git a/spec/lib/gitlab/tree_summary_spec.rb b/spec/lib/gitlab/tree_summary_spec.rb
index f45005fcc9b..42cc15a9033 100644
--- a/spec/lib/gitlab/tree_summary_spec.rb
+++ b/spec/lib/gitlab/tree_summary_spec.rb
@@ -25,6 +25,14 @@ RSpec.describe Gitlab::TreeSummary do
it 'defaults limit to 25' do
expect(summary.limit).to eq(25)
end
+
+ context 'when offset is larger than the maximum' do
+ let(:offset) { described_class::MAX_OFFSET + 1 }
+
+ it 'sets offset to the maximum' do
+ expect(subject.offset).to eq(described_class::MAX_OFFSET)
+ end
+ end
end
describe '#summarize' do
@@ -45,6 +53,14 @@ RSpec.describe Gitlab::TreeSummary do
end
end
+ context 'when offset is negative' do
+ let(:offset) { -1 }
+
+ it 'returns an empty array' do
+ expect(entries).to eq([])
+ end
+ end
+
context 'with caching', :use_clean_rails_memory_store_caching do
subject { Rails.cache.fetch(key) }
diff --git a/spec/lib/gitlab/url_blockers/domain_allowlist_entry_spec.rb b/spec/lib/gitlab/url_blockers/domain_allowlist_entry_spec.rb
index ece0a018d53..2405b6769b7 100644
--- a/spec/lib/gitlab/url_blockers/domain_allowlist_entry_spec.rb
+++ b/spec/lib/gitlab/url_blockers/domain_allowlist_entry_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::UrlBlockers::DomainAllowlistEntry do
let(:domain) { 'www.example.com' }
diff --git a/spec/lib/gitlab/url_blockers/ip_allowlist_entry_spec.rb b/spec/lib/gitlab/url_blockers/ip_allowlist_entry_spec.rb
index 110a6c17adb..8dcb402dfb2 100644
--- a/spec/lib/gitlab/url_blockers/ip_allowlist_entry_spec.rb
+++ b/spec/lib/gitlab/url_blockers/ip_allowlist_entry_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::UrlBlockers::IpAllowlistEntry do
let(:ipv4) { IPAddr.new('192.168.1.1') }
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb
index b85d5a3ebf9..ce15d44b1e1 100644
--- a/spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/count_bulk_imports_entities_metric_spec.rb
@@ -5,15 +5,17 @@ require 'spec_helper'
RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountBulkImportsEntitiesMetric do
let_it_be(:user) { create(:user) }
let_it_be(:bulk_import_projects) do
- create_list(:bulk_import_entity, 3, source_type: 'project_entity', created_at: 3.weeks.ago)
+ create_list(:bulk_import_entity, 2, source_type: 'project_entity', created_at: 3.weeks.ago, status: 2)
+ create(:bulk_import_entity, source_type: 'project_entity', created_at: 3.weeks.ago, status: 0)
end
let_it_be(:bulk_import_groups) do
- create_list(:bulk_import_entity, 3, source_type: 'group_entity', created_at: 3.weeks.ago)
+ create_list(:bulk_import_entity, 2, source_type: 'group_entity', created_at: 3.weeks.ago, status: 2)
+ create(:bulk_import_entity, source_type: 'group_entity', created_at: 3.weeks.ago, status: 0)
end
let_it_be(:old_bulk_import_project) do
- create(:bulk_import_entity, source_type: 'project_entity', created_at: 2.months.ago)
+ create(:bulk_import_entity, source_type: 'project_entity', created_at: 2.months.ago, status: 2)
end
context 'with no source_type' do
@@ -103,4 +105,62 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountBulkImportsEntitie
options: { source_type: 'group_entity' }
end
end
+
+ context 'with entity status' do
+ context 'with all time frame' do
+ let(:expected_value) { 5 }
+ let(:expected_query) do
+ "SELECT COUNT(\"bulk_import_entities\".\"id\") FROM \"bulk_import_entities\""\
+ " WHERE \"bulk_import_entities\".\"status\" = 2"
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query',
+ time_frame: 'all',
+ options: { status: 2 }
+ end
+
+ context 'for 28d time frame' do
+ let(:expected_value) { 4 }
+ let(:start) { 30.days.ago.to_s(:db) }
+ let(:finish) { 2.days.ago.to_s(:db) }
+ let(:expected_query) do
+ "SELECT COUNT(\"bulk_import_entities\".\"id\") FROM \"bulk_import_entities\""\
+ " WHERE \"bulk_import_entities\".\"created_at\" BETWEEN '#{start}' AND '#{finish}'"\
+ " AND \"bulk_import_entities\".\"status\" = 2"
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query',
+ time_frame: '28d',
+ options: { status: 2 }
+ end
+ end
+
+ context 'with entity status and source_type' do
+ context 'with all time frame' do
+ let(:expected_value) { 3 }
+ let(:expected_query) do
+ "SELECT COUNT(\"bulk_import_entities\".\"id\") FROM \"bulk_import_entities\""\
+ " WHERE \"bulk_import_entities\".\"source_type\" = 1 AND \"bulk_import_entities\".\"status\" = 2"
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query',
+ time_frame: 'all',
+ options: { status: 2, source_type: 'project_entity' }
+ end
+
+ context 'for 28d time frame' do
+ let(:expected_value) { 2 }
+ let(:start) { 30.days.ago.to_s(:db) }
+ let(:finish) { 2.days.ago.to_s(:db) }
+ let(:expected_query) do
+ "SELECT COUNT(\"bulk_import_entities\".\"id\") FROM \"bulk_import_entities\""\
+ " WHERE \"bulk_import_entities\".\"created_at\" BETWEEN '#{start}' AND '#{finish}'"\
+ " AND \"bulk_import_entities\".\"source_type\" = 1 AND \"bulk_import_entities\".\"status\" = 2"
+ end
+
+ it_behaves_like 'a correct instrumented metric value and query',
+ time_frame: '28d',
+ options: { status: 2, source_type: 'project_entity' }
+ end
+ end
end
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/count_user_auth_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/count_user_auth_metric_spec.rb
new file mode 100644
index 00000000000..2f49c427bd0
--- /dev/null
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/count_user_auth_metric_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Usage::Metrics::Instrumentations::CountUserAuthMetric do
+ context 'with all time frame' do
+ let(:expected_value) { 2 }
+
+ before do
+ user = create(:user)
+ user2 = create(:user)
+ create(:authentication_event, user: user, provider: :ldapmain, result: :success)
+ create(:authentication_event, user: user2, provider: :ldapsecondary, result: :success)
+ create(:authentication_event, user: user2, provider: :group_saml, result: :success)
+ create(:authentication_event, user: user2, provider: :group_saml, result: :success)
+ create(:authentication_event, user: user, provider: :group_saml, result: :failed)
+ end
+
+ it_behaves_like 'a correct instrumented metric value', { time_frame: 'all', data_source: 'database' }
+ end
+
+ context 'with 28d time frame' do
+ let(:expected_value) { 1 }
+
+ before do
+ user = create(:user)
+ user2 = create(:user)
+
+ create(:authentication_event, created_at: 1.year.ago, user: user, provider: :ldapmain, result: :success)
+ create(:authentication_event, created_at: 1.week.ago, user: user2, provider: :ldapsecondary, result: :success)
+ end
+
+ it_behaves_like 'a correct instrumented metric value', { time_frame: '28d', data_source: 'database' }
+ end
+end
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb
index 831f775ec9a..80ae5c6fd21 100644
--- a/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb
@@ -11,18 +11,18 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::RedisMetric, :clean_git
let(:expected_value) { 4 }
- it_behaves_like 'a correct instrumented metric value', { options: { event: 'pushes', counter_class: 'SourceCodeCounter' } }
+ it_behaves_like 'a correct instrumented metric value', { options: { event: 'pushes', prefix: 'source_code' } }
it 'raises an exception if event option is not present' do
- expect { described_class.new(counter_class: 'SourceCodeCounter') }.to raise_error(ArgumentError)
+ expect { described_class.new(prefix: 'source_code') }.to raise_error(ArgumentError)
end
- it 'raises an exception if counter_class option is not present' do
+ it 'raises an exception if prefix option is not present' do
expect { described_class.new(event: 'pushes') }.to raise_error(ArgumentError)
end
describe 'children classes' do
- let(:options) { { event: 'pushes', counter_class: 'SourceCodeCounter' } }
+ let(:options) { { event: 'pushes', prefix: 'source_code' } }
context 'availability not defined' do
subject { Class.new(described_class).new(time_frame: nil, options: options) }
@@ -44,4 +44,18 @@ RSpec.describe Gitlab::Usage::Metrics::Instrumentations::RedisMetric, :clean_git
end
end
end
+
+ context "with usage prefix disabled" do
+ let(:expected_value) { 3 }
+
+ before do
+ 3.times do
+ Gitlab::UsageDataCounters::WebIdeCounter.increment_merge_requests_count
+ end
+ end
+
+ it_behaves_like 'a correct instrumented metric value', {
+ options: { event: 'merge_requests_count', prefix: 'web_ide', include_usage_prefix: false }
+ }
+ end
end
diff --git a/spec/lib/gitlab/usage_data/topology_spec.rb b/spec/lib/gitlab/usage_data/topology_spec.rb
index 737580e3493..dfdf8eaabe8 100644
--- a/spec/lib/gitlab/usage_data/topology_spec.rb
+++ b/spec/lib/gitlab/usage_data/topology_spec.rb
@@ -187,7 +187,7 @@ RSpec.describe Gitlab::UsageData::Topology do
[
{
'metric' => { 'instance' => 'localhost:9100' },
- 'value' => [1000, '512']
+ 'value' => [1000, '512']
}
]
end
@@ -196,7 +196,7 @@ RSpec.describe Gitlab::UsageData::Topology do
[
{
'metric' => { 'instance' => 'localhost:9100' },
- 'value' => [1000, '0.35']
+ 'value' => [1000, '0.35']
}
]
end
@@ -224,23 +224,23 @@ RSpec.describe Gitlab::UsageData::Topology do
[
{
'metric' => { 'instance' => 'localhost:8080', 'job' => 'gitlab-rails' },
- 'value' => [1000, '10']
+ 'value' => [1000, '10']
},
{
'metric' => { 'instance' => '127.0.0.1:8090', 'job' => 'gitlab-sidekiq' },
- 'value' => [1000, '11']
+ 'value' => [1000, '11']
},
{
'metric' => { 'instance' => '0.0.0.0:9090', 'job' => 'prometheus' },
- 'value' => [1000, '12']
+ 'value' => [1000, '12']
},
{
'metric' => { 'instance' => '[::1]:1234', 'job' => 'redis' },
- 'value' => [1000, '13']
+ 'value' => [1000, '13']
},
{
'metric' => { 'instance' => '[::]:1234', 'job' => 'postgres' },
- 'value' => [1000, '14']
+ 'value' => [1000, '14']
}
]
end
@@ -640,7 +640,7 @@ RSpec.describe Gitlab::UsageData::Topology do
.and_return(result || [
{
'metric' => { 'instance' => 'instance1:8080', 'job' => 'gitlab-rails' },
- 'value' => [1000, '300']
+ 'value' => [1000, '300']
},
{
'metric' => { 'instance' => 'instance1:8090', 'job' => 'gitlab-sidekiq' },
diff --git a/spec/lib/gitlab/usage_data_counters/base_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/base_counter_spec.rb
index 4a31191d75f..9cecaa01885 100644
--- a/spec/lib/gitlab/usage_data_counters/base_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/base_counter_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::UsageDataCounters::BaseCounter do
describe '.fetch_supported_event' do
diff --git a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
index e0b334cb5af..3fb2532521a 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
@@ -107,10 +107,8 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
'quickactions',
'pipeline_authoring',
'epics_usage',
- 'epic_boards_usage',
'secure',
'importer',
- 'network_policies',
'geo',
'growth',
'work_items',
diff --git a/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb
index 84a6f338282..032a5e78385 100644
--- a/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
let(:time) { Time.zone.now }
context 'for Issue title edit actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_TITLE_CHANGED }
def track_action(params)
@@ -25,7 +25,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue description edit actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_DESCRIPTION_CHANGED }
def track_action(params)
@@ -35,7 +35,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue assignee edit actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_ASSIGNEE_CHANGED }
def track_action(params)
@@ -45,7 +45,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue make confidential actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_MADE_CONFIDENTIAL }
def track_action(params)
@@ -55,7 +55,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue make visible actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_MADE_VISIBLE }
def track_action(params)
@@ -65,7 +65,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue created actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_CREATED }
def track_action(params)
@@ -75,7 +75,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue closed actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_CLOSED }
def track_action(params)
@@ -85,7 +85,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue reopened actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_REOPENED }
def track_action(params)
@@ -95,7 +95,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue label changed actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_LABEL_CHANGED }
def track_action(params)
@@ -104,8 +104,18 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
end
+ context 'for Issue label milestone actions' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
+ let(:action) { described_class::ISSUE_MILESTONE_CHANGED }
+
+ def track_action(params)
+ described_class.track_issue_milestone_changed_action(**params)
+ end
+ end
+ end
+
context 'for Issue cross-referenced actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_CROSS_REFERENCED }
def track_action(params)
@@ -115,7 +125,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue moved actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_MOVED }
def track_action(params)
@@ -135,7 +145,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue relate actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_RELATED }
def track_action(params)
@@ -145,7 +155,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue unrelate actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_UNRELATED }
def track_action(params)
@@ -155,7 +165,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue marked as duplicate actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_MARKED_AS_DUPLICATE }
def track_action(params)
@@ -165,7 +175,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue locked actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_LOCKED }
def track_action(params)
@@ -175,7 +185,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue unlocked actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_UNLOCKED }
def track_action(params)
@@ -185,7 +195,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue designs added actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_DESIGNS_ADDED }
def track_action(params)
@@ -195,7 +205,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue designs modified actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_DESIGNS_MODIFIED }
def track_action(params)
@@ -205,7 +215,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue designs removed actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_DESIGNS_REMOVED }
def track_action(params)
@@ -215,7 +225,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue due date changed actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_DUE_DATE_CHANGED }
def track_action(params)
@@ -225,7 +235,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue time estimate changed actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_TIME_ESTIMATE_CHANGED }
def track_action(params)
@@ -235,7 +245,7 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
context 'for Issue time spent changed actions' do
- it_behaves_like 'a daily tracked issuable event' do
+ it_behaves_like 'daily tracked issuable snowplow and service ping events with project' do
let(:action) { described_class::ISSUE_TIME_SPENT_CHANGED }
def track_action(params)
@@ -275,15 +285,15 @@ RSpec.describe Gitlab::UsageDataCounters::IssueActivityUniqueCounter, :clean_git
end
it 'can return the count of actions per user deduplicated', :aggregate_failures do
- described_class.track_issue_title_changed_action(author: user1)
- described_class.track_issue_description_changed_action(author: user1)
- described_class.track_issue_assignee_changed_action(author: user1)
+ described_class.track_issue_title_changed_action(author: user1, project: project)
+ described_class.track_issue_description_changed_action(author: user1, project: project)
+ described_class.track_issue_assignee_changed_action(author: user1, project: project)
travel_to(2.days.ago) do
- described_class.track_issue_title_changed_action(author: user2)
- described_class.track_issue_title_changed_action(author: user3)
- described_class.track_issue_description_changed_action(author: user3)
- described_class.track_issue_assignee_changed_action(author: user3)
+ described_class.track_issue_title_changed_action(author: user2, project: project)
+ described_class.track_issue_title_changed_action(author: user3, project: project)
+ described_class.track_issue_description_changed_action(author: user3, project: project)
+ described_class.track_issue_assignee_changed_action(author: user3, project: project)
end
events = Gitlab::UsageDataCounters::HLLRedisCounter.events_for_category(described_class::ISSUE_CATEGORY)
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 3f44cfdcf27..74e63d219bd 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
@@ -100,9 +100,9 @@ RSpec.describe Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter, :cl
subject
expect_snowplow_event(
- category: 'merge_requests',
+ category: 'merge_requests',
action: 'i_code_review_user_approve_mr',
- namespace: target_project.namespace,
+ namespace: target_project.namespace,
user: user,
project: target_project
)
diff --git a/spec/lib/gitlab/usage_data_counters/note_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/note_counter_spec.rb
index 7e8f0172e06..687f8c2cd41 100644
--- a/spec/lib/gitlab/usage_data_counters/note_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/note_counter_spec.rb
@@ -41,7 +41,7 @@ RSpec.describe Gitlab::UsageDataCounters::NoteCounter, :clean_gitlab_redis_share
let(:expected_totals) do
{ snippet_comment: 3,
merge_request_comment: 4,
- commit_comment: 5 }
+ commit_comment: 5 }
end
before do
diff --git a/spec/lib/gitlab/usage_data_counters_spec.rb b/spec/lib/gitlab/usage_data_counters_spec.rb
index 0696b375eb5..040b5deca54 100644
--- a/spec/lib/gitlab/usage_data_counters_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::UsageDataCounters do
describe '.usage_data_counters' do
diff --git a/spec/lib/gitlab/usage_data_metrics_spec.rb b/spec/lib/gitlab/usage_data_metrics_spec.rb
index 485f2131d87..ed0eabf1b4d 100644
--- a/spec/lib/gitlab/usage_data_metrics_spec.rb
+++ b/spec/lib/gitlab/usage_data_metrics_spec.rb
@@ -16,6 +16,10 @@ RSpec.describe Gitlab::UsageDataMetrics do
allow_next_instance_of(Gitlab::Database::BatchCounter) do |batch_counter|
allow(batch_counter).to receive(:transaction_open?).and_return(false)
end
+
+ allow_next_instance_of(Gitlab::Database::BatchAverageCounter) do |instance|
+ allow(instance).to receive(:transaction_open?).and_return(false)
+ end
end
context 'with instrumentation_class' do
@@ -33,6 +37,10 @@ RSpec.describe Gitlab::UsageDataMetrics do
expect(subject[:usage_activity_by_stage][:plan]).to include(:issues)
end
+ it 'includes usage_activity_by_stage metrics' do
+ expect(subject[:usage_activity_by_stage][:manage]).to include(:count_user_auth)
+ end
+
it 'includes usage_activity_by_stage_monthly keys' do
expect(subject[:usage_activity_by_stage_monthly][:plan]).to include(:issues)
end
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index 692b6483149..46ed4b57d3a 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -215,14 +215,28 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
groups: 2,
users_created: 10,
omniauth_providers: ['google_oauth2'],
- user_auth_by_provider: { 'group_saml' => 2, 'ldap' => 4, 'standard' => 0, 'two-factor' => 0, 'two-factor-via-u2f-device' => 0, "two-factor-via-webauthn-device" => 0 }
+ user_auth_by_provider: {
+ 'group_saml' => 2,
+ 'ldap' => 4,
+ 'standard' => 0,
+ 'two-factor' => 0,
+ 'two-factor-via-u2f-device' => 0,
+ "two-factor-via-webauthn-device" => 0
+ }
)
expect(described_class.usage_activity_by_stage_manage(described_class.monthly_time_range_db_params)).to include(
events: be_within(error_rate).percent_of(2),
groups: 1,
users_created: 6,
omniauth_providers: ['google_oauth2'],
- user_auth_by_provider: { 'group_saml' => 1, 'ldap' => 2, 'standard' => 0, 'two-factor' => 0, 'two-factor-via-u2f-device' => 0, "two-factor-via-webauthn-device" => 0 }
+ user_auth_by_provider: {
+ 'group_saml' => 1,
+ 'ldap' => 2,
+ 'standard' => 0,
+ 'two-factor' => 0,
+ 'two-factor-via-u2f-device' => 0,
+ "two-factor-via-webauthn-device" => 0
+ }
)
end
@@ -583,10 +597,10 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
it 'gathers object store usage correctly' do
expect(subject[:object_store]).to eq(
{ artifacts: { enabled: true, object_store: { enabled: true, direct_upload: true, background_upload: false, provider: "AWS" } },
- 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" } } }
+ 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" } } }
)
end
@@ -749,9 +763,6 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do
it { is_expected.to include(:kubernetes_agent_gitops_sync) }
it { is_expected.to include(:kubernetes_agent_k8s_api_proxy_request) }
- it { is_expected.to include(:package_events_i_package_pull_package) }
- it { is_expected.to include(:package_events_i_package_delete_package_by_user) }
- it { is_expected.to include(:package_events_i_package_conan_push_package) }
end
describe '.usage_data_counters' do
diff --git a/spec/lib/gitlab/utils/deep_size_spec.rb b/spec/lib/gitlab/utils/deep_size_spec.rb
index 7595fb2c1b0..6b0be4590f1 100644
--- a/spec/lib/gitlab/utils/deep_size_spec.rb
+++ b/spec/lib/gitlab/utils/deep_size_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Utils::DeepSize do
let(:data) do
@@ -58,10 +58,4 @@ RSpec.describe Gitlab::Utils::DeepSize do
it { is_expected.not_to be_valid }
end
end
-
- describe '.human_default_max_size' do
- it 'returns 1 MB' do
- expect(described_class.human_default_max_size).to eq('1 MB')
- end
- end
end
diff --git a/spec/lib/gitlab/utils/delegator_override_spec.rb b/spec/lib/gitlab/utils/delegator_override_spec.rb
index af4c7fa5d8e..2dafa75e344 100644
--- a/spec/lib/gitlab/utils/delegator_override_spec.rb
+++ b/spec/lib/gitlab/utils/delegator_override_spec.rb
@@ -31,6 +31,7 @@ RSpec.describe Gitlab::Utils::DelegatorOverride do
before do
stub_env('STATIC_VERIFICATION', 'true')
+ described_class.validators.clear
end
describe '.delegator_target' do
diff --git a/spec/lib/gitlab/utils/execution_tracker_spec.rb b/spec/lib/gitlab/utils/execution_tracker_spec.rb
new file mode 100644
index 00000000000..6c42863658c
--- /dev/null
+++ b/spec/lib/gitlab/utils/execution_tracker_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+RSpec.describe Gitlab::Utils::ExecutionTracker do
+ subject(:tracker) { described_class.new }
+
+ describe '#over_limit?' do
+ it 'is true when max runtime is exceeded' do
+ monotonic_time_before = 1 # this will be the start time
+ monotonic_time_after = described_class::MAX_RUNTIME.to_i + 1 # this will be returned when over_limit? is called
+
+ allow(Gitlab::Metrics::System).to receive(:monotonic_time).and_return(monotonic_time_before, monotonic_time_after)
+
+ tracker
+
+ expect(tracker).to be_over_limit
+ end
+
+ it 'is false when max runtime is not exceeded' do
+ expect(tracker).not_to be_over_limit
+ end
+ end
+end
diff --git a/spec/lib/gitlab/utils/json_size_estimator_spec.rb b/spec/lib/gitlab/utils/json_size_estimator_spec.rb
index 5fd66caa5e9..ba49cc3a847 100644
--- a/spec/lib/gitlab/utils/json_size_estimator_spec.rb
+++ b/spec/lib/gitlab/utils/json_size_estimator_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Utils::JsonSizeEstimator do
RSpec::Matchers.define :match_json_bytesize_of do |expected|
diff --git a/spec/lib/gitlab/utils/markdown_spec.rb b/spec/lib/gitlab/utils/markdown_spec.rb
index acc5bd47c8c..0a7d1160bbc 100644
--- a/spec/lib/gitlab/utils/markdown_spec.rb
+++ b/spec/lib/gitlab/utils/markdown_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Utils::Markdown do
let(:klass) do
diff --git a/spec/lib/gitlab/utils/merge_hash_spec.rb b/spec/lib/gitlab/utils/merge_hash_spec.rb
index 11daa05c9ee..4eec6e83be2 100644
--- a/spec/lib/gitlab/utils/merge_hash_spec.rb
+++ b/spec/lib/gitlab/utils/merge_hash_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::Utils::MergeHash do
describe '.crush' do
it 'can flatten a hash to each element' do
diff --git a/spec/lib/gitlab/utils/nokogiri_spec.rb b/spec/lib/gitlab/utils/nokogiri_spec.rb
index 90f137f53c8..7b4c63f9168 100644
--- a/spec/lib/gitlab/utils/nokogiri_spec.rb
+++ b/spec/lib/gitlab/utils/nokogiri_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
+require 'rspec-parameterized'
RSpec.describe Gitlab::Utils::Nokogiri do
describe '#css_to_xpath' do
diff --git a/spec/lib/gitlab/utils/sanitize_node_link_spec.rb b/spec/lib/gitlab/utils/sanitize_node_link_spec.rb
index 3ab592dfc62..1fc10bc3aa8 100644
--- a/spec/lib/gitlab/utils/sanitize_node_link_spec.rb
+++ b/spec/lib/gitlab/utils/sanitize_node_link_spec.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
+require 'html/pipeline'
+require 'addressable'
RSpec.describe Gitlab::Utils::SanitizeNodeLink do
let(:klass) do
diff --git a/spec/lib/gitlab/utils_spec.rb b/spec/lib/gitlab/utils_spec.rb
index ad1a65ffae8..61323f0646b 100644
--- a/spec/lib/gitlab/utils_spec.rb
+++ b/spec/lib/gitlab/utils_spec.rb
@@ -174,7 +174,7 @@ RSpec.describe Gitlab::Utils do
{
'TEST' => 'test',
'project_with_underscores' => 'project-with-underscores',
- 'namespace/project' => 'namespace-project',
+ 'namespace/project' => 'namespace-project',
'a' * 70 => 'a' * 63,
'test_trailing_' => 'test-trailing'
}.each do |original, expected|
diff --git a/spec/lib/gitlab/web_ide/config/entry/terminal_spec.rb b/spec/lib/gitlab/web_ide/config/entry/terminal_spec.rb
index 7d96adf95e8..8d4629bf48b 100644
--- a/spec/lib/gitlab/web_ide/config/entry/terminal_spec.rb
+++ b/spec/lib/gitlab/web_ide/config/entry/terminal_spec.rb
@@ -150,6 +150,29 @@ RSpec.describe Gitlab::WebIde::Config::Entry::Terminal do
}
)
end
+
+ context 'when the FF ci_variables_refactoring_to_variable is disabled' do
+ let(:entry_without_ff) { described_class.new(config, with_image_ports: true) }
+
+ before do
+ stub_feature_flags(ci_variables_refactoring_to_variable: false)
+ entry_without_ff.compose!
+ end
+
+ it 'returns correct value' do
+ expect(entry_without_ff.value)
+ .to eq(
+ tag_list: ['webide'],
+ job_variables: [{ key: 'KEY', value: 'value', public: true }],
+ options: {
+ image: { name: "image:1.0" },
+ services: [{ name: "mysql" }],
+ before_script: %w[ls pwd],
+ script: ['sleep 100']
+ }
+ )
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/word_diff/chunk_collection_spec.rb b/spec/lib/gitlab/word_diff/chunk_collection_spec.rb
index 73e9ff3974a..f76c4213c19 100644
--- a/spec/lib/gitlab/word_diff/chunk_collection_spec.rb
+++ b/spec/lib/gitlab/word_diff/chunk_collection_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::WordDiff::ChunkCollection do
subject(:collection) { described_class.new }
diff --git a/spec/lib/gitlab/word_diff/line_processor_spec.rb b/spec/lib/gitlab/word_diff/line_processor_spec.rb
index f448f5b5eb6..7246ed772f8 100644
--- a/spec/lib/gitlab/word_diff/line_processor_spec.rb
+++ b/spec/lib/gitlab/word_diff/line_processor_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::WordDiff::LineProcessor do
subject(:line_processor) { described_class.new(line) }
diff --git a/spec/lib/gitlab/word_diff/parser_spec.rb b/spec/lib/gitlab/word_diff/parser_spec.rb
index e793e44fd45..18109a8160b 100644
--- a/spec/lib/gitlab/word_diff/parser_spec.rb
+++ b/spec/lib/gitlab/word_diff/parser_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::WordDiff::Parser do
subject(:parser) { described_class.new }
@@ -42,18 +42,18 @@ RSpec.describe Gitlab::WordDiff::Parser do
{ index: 1, old_pos: 2, new_pos: 2, text: 'Unchanged line', type: nil, marker_ranges: [] },
{ index: 2, old_pos: 3, new_pos: 3, text: '', type: nil, marker_ranges: [] },
{ index: 3, old_pos: 4, new_pos: 4, text: 'Old changeNew addition unchanged content', type: nil,
- marker_ranges: [
- Gitlab::MarkerRange.new(0, 9, mode: :deletion),
- Gitlab::MarkerRange.new(10, 21, mode: :addition)
- ] },
+ marker_ranges: [
+ Gitlab::MarkerRange.new(0, 9, mode: :deletion),
+ Gitlab::MarkerRange.new(10, 21, mode: :addition)
+ ] },
{ index: 4, old_pos: 50, new_pos: 50, text: '@@ -50,14 +50,13 @@', type: 'match', marker_ranges: [] },
{ index: 5, old_pos: 50, new_pos: 50, text: 'First change same same same_removed_added_end of the line', type: nil,
- marker_ranges: [
- Gitlab::MarkerRange.new(0, 11, mode: :addition),
- Gitlab::MarkerRange.new(28, 35, mode: :deletion),
- Gitlab::MarkerRange.new(36, 41, mode: :addition)
- ] },
+ marker_ranges: [
+ Gitlab::MarkerRange.new(0, 11, mode: :addition),
+ Gitlab::MarkerRange.new(28, 35, mode: :deletion),
+ Gitlab::MarkerRange.new(36, 41, mode: :addition)
+ ] },
{ index: 6, old_pos: 51, new_pos: 51, text: '', type: nil, marker_ranges: [] }
]
diff --git a/spec/lib/gitlab/word_diff/positions_counter_spec.rb b/spec/lib/gitlab/word_diff/positions_counter_spec.rb
index e2c246f6801..32ce7c50591 100644
--- a/spec/lib/gitlab/word_diff/positions_counter_spec.rb
+++ b/spec/lib/gitlab/word_diff/positions_counter_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::WordDiff::PositionsCounter do
subject(:counter) { described_class.new }
diff --git a/spec/lib/gitlab/word_diff/segments/chunk_spec.rb b/spec/lib/gitlab/word_diff/segments/chunk_spec.rb
index 797cc42a03c..75c0e5b4a77 100644
--- a/spec/lib/gitlab/word_diff/segments/chunk_spec.rb
+++ b/spec/lib/gitlab/word_diff/segments/chunk_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::WordDiff::Segments::Chunk do
subject(:chunk) { described_class.new(line) }
diff --git a/spec/lib/gitlab/word_diff/segments/diff_hunk_spec.rb b/spec/lib/gitlab/word_diff/segments/diff_hunk_spec.rb
index 5250e6d73c2..a65f55c716f 100644
--- a/spec/lib/gitlab/word_diff/segments/diff_hunk_spec.rb
+++ b/spec/lib/gitlab/word_diff/segments/diff_hunk_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::WordDiff::Segments::DiffHunk do
subject(:diff_hunk) { described_class.new(line) }
diff --git a/spec/lib/gitlab/word_diff/segments/newline_spec.rb b/spec/lib/gitlab/word_diff/segments/newline_spec.rb
index ed5054844f1..4c0cf0c5ee4 100644
--- a/spec/lib/gitlab/word_diff/segments/newline_spec.rb
+++ b/spec/lib/gitlab/word_diff/segments/newline_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Gitlab::WordDiff::Segments::Newline do
subject(:newline) { described_class.new }
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index 703a4b5399e..5c9a3cc0a24 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -365,7 +365,7 @@ RSpec.describe Gitlab::Workhorse do
it 'set and notify' do
expect(Gitlab::Redis::SharedState).to receive(:with).and_call_original
expect_any_instance_of(::Redis).to receive(:publish)
- .with(described_class::NOTIFICATION_CHANNEL, "test-key=test-value")
+ .with(described_class::NOTIFICATION_PREFIX + 'test-key', "test-value")
subject
end
diff --git a/spec/lib/gitlab_edition_spec.rb b/spec/lib/gitlab_edition_spec.rb
index 6fc4312252d..46be1471896 100644
--- a/spec/lib/gitlab_edition_spec.rb
+++ b/spec/lib/gitlab_edition_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
+require 'rspec-parameterized'
RSpec.describe GitlabEdition do
def remove_instance_variable(ivar)
@@ -27,7 +28,57 @@ RSpec.describe GitlabEdition do
end
end
- describe 'extensions' do
+ describe '.path_glob' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:root) { described_class.root.to_s }
+
+ subject { described_class.path_glob(path) }
+
+ before do
+ allow(described_class).to receive(:jh?).and_return(jh)
+ allow(described_class).to receive(:ee?).and_return(ee)
+ end
+
+ where(:ee, :jh, :path, :expected) do
+ false | false | nil | ''
+ true | false | nil | '{,ee/}'
+ true | true | nil | '{,ee/,jh/}'
+ false | true | nil | '{,ee/,jh/}'
+ false | false | 'app/models' | 'app/models'
+ true | false | 'app/models' | '{,ee/}app/models'
+ true | true | 'app/models' | '{,ee/,jh/}app/models'
+ false | true | 'app/models' | '{,ee/,jh/}app/models'
+ end
+
+ with_them do
+ it { is_expected.to eq("#{root}/#{expected}") }
+ end
+ end
+
+ describe '.extension_path_prefixes' do
+ using RSpec::Parameterized::TableSyntax
+
+ subject { described_class.extension_path_prefixes }
+
+ before do
+ allow(described_class).to receive(:jh?).and_return(jh)
+ allow(described_class).to receive(:ee?).and_return(ee)
+ end
+
+ where(:ee, :jh, :expected) do
+ false | false | ''
+ true | false | '{,ee/}'
+ true | true | '{,ee/,jh/}'
+ false | true | '{,ee/,jh/}'
+ end
+
+ with_them do
+ it { is_expected.to eq(expected) }
+ end
+ end
+
+ describe '.extensions' do
context 'when .jh? is true' do
before do
allow(described_class).to receive(:jh?).and_return(true)
diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb
index aeca7b09a88..0f117f495d1 100644
--- a/spec/lib/google_api/cloud_platform/client_spec.rb
+++ b/spec/lib/google_api/cloud_platform/client_spec.rb
@@ -306,7 +306,7 @@ RSpec.describe GoogleApi::CloudPlatform::Client do
.with({ 'role': 'roles/storage.admin', 'members': ["serviceAccount:#{mock_email}"] })
expect(Google::Apis::CloudresourcemanagerV1::Binding).to receive(:new)
- .with({ 'role': 'roles/cloudsql.admin', 'members': ["serviceAccount:#{mock_email}"] })
+ .with({ 'role': 'roles/cloudsql.client', 'members': ["serviceAccount:#{mock_email}"] })
expect(Google::Apis::CloudresourcemanagerV1::Binding).to receive(:new)
.with({ 'role': 'roles/browser', 'members': ["serviceAccount:#{mock_email}"] })
diff --git a/spec/lib/learn_gitlab/onboarding_spec.rb b/spec/lib/learn_gitlab/onboarding_spec.rb
deleted file mode 100644
index 3e22ce59091..00000000000
--- a/spec/lib/learn_gitlab/onboarding_spec.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe LearnGitlab::Onboarding do
- describe '#completed_percentage' do
- let(:completed_actions) { {} }
- let(:onboarding_progress) { build(:onboarding_progress, namespace: namespace, **completed_actions) }
- let(:namespace) { create(:namespace) }
-
- let_it_be(:tracked_action_columns) do
- [
- *described_class::ACTION_ISSUE_IDS.keys,
- *described_class::ACTION_PATHS,
- :security_scan_enabled
- ].map { |key| OnboardingProgress.column_name(key) }
- end
-
- before do
- expect(OnboardingProgress).to receive(:find_by).with(namespace: namespace).and_return(onboarding_progress)
- end
-
- subject { described_class.new(namespace).completed_percentage }
-
- context 'when no onboarding_progress exists' do
- let(:onboarding_progress) { nil }
-
- it { is_expected.to eq(0) }
- end
-
- context 'when no action has been completed' do
- it { is_expected.to eq(0) }
- end
-
- context 'when all tracked actions have been completed' do
- let(:completed_actions) do
- tracked_action_columns.to_h { |action| [action, Time.current] }
- end
-
- it { is_expected.to eq(100) }
- end
-
- describe 'security_actions_continuous_onboarding experiment' do
- let(:completed_actions) { Hash[tracked_action_columns.first, Time.current] }
-
- context 'when control' do
- before do
- stub_experiments(security_actions_continuous_onboarding: :control)
- end
-
- it { is_expected.to eq(11) }
- end
-
- context 'when candidate' do
- before do
- stub_experiments(security_actions_continuous_onboarding: :candidate)
- end
-
- it { is_expected.to eq(9) }
- end
- end
- end
-end
diff --git a/spec/lib/learn_gitlab/project_spec.rb b/spec/lib/learn_gitlab/project_spec.rb
deleted file mode 100644
index 23784709817..00000000000
--- a/spec/lib/learn_gitlab/project_spec.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe LearnGitlab::Project do
- let_it_be(:current_user) { create(:user) }
- let_it_be(:learn_gitlab_project) { create(:project, name: LearnGitlab::Project::PROJECT_NAME) }
- let_it_be(:learn_gitlab_board) { create(:board, project: learn_gitlab_project, name: LearnGitlab::Project::BOARD_NAME) }
- let_it_be(:learn_gitlab_label) { create(:label, project: learn_gitlab_project, name: LearnGitlab::Project::LABEL_NAME) }
-
- before do
- learn_gitlab_project.add_developer(current_user)
- end
-
- describe '.available?' do
- using RSpec::Parameterized::TableSyntax
-
- where(:project, :board, :label, :expected_result) do
- nil | nil | nil | nil
- nil | nil | true | nil
- nil | true | nil | nil
- nil | true | true | nil
- true | nil | nil | nil
- true | nil | true | nil
- true | true | nil | nil
- true | true | true | true
- end
-
- with_them do
- before do
- allow_next_instance_of(described_class) do |learn_gitlab|
- allow(learn_gitlab).to receive(:project).and_return(project)
- allow(learn_gitlab).to receive(:board).and_return(board)
- allow(learn_gitlab).to receive(:label).and_return(label)
- end
- end
-
- subject { described_class.new(current_user).available? }
-
- it { is_expected.to be expected_result }
- end
- end
-
- describe '.project' do
- subject { described_class.new(current_user).project }
-
- it { is_expected.to eq learn_gitlab_project }
-
- context 'when it is created during trial signup' do
- let_it_be(:learn_gitlab_project) { create(:project, name: LearnGitlab::Project::PROJECT_NAME_ULTIMATE_TRIAL, path: 'learn-gitlab-ultimate-trial') }
-
- it { is_expected.to eq learn_gitlab_project }
- end
- end
-
- describe '.board' do
- subject { described_class.new(current_user).board }
-
- it { is_expected.to eq learn_gitlab_board }
- end
-
- describe '.label' do
- subject { described_class.new(current_user).label }
-
- it { is_expected.to eq learn_gitlab_label }
- end
-end
diff --git a/spec/lib/marginalia_spec.rb b/spec/lib/marginalia_spec.rb
index 59add4e8347..5f405e71d79 100644
--- a/spec/lib/marginalia_spec.rb
+++ b/spec/lib/marginalia_spec.rb
@@ -45,8 +45,8 @@ RSpec.describe 'Marginalia spec' do
let(:component_map) do
{
- "application" => "test",
- "endpoint_id" => "MarginaliaTestController#first_user",
+ "application" => "test",
+ "endpoint_id" => "MarginaliaTestController#first_user",
"correlation_id" => correlation_id,
"db_config_name" => "main"
}
@@ -62,8 +62,8 @@ RSpec.describe 'Marginalia spec' do
let(:recorded) { ActiveRecord::QueryRecorder.new { make_request(correlation_id, :first_ci_pipeline) } }
let(:component_map) do
{
- "application" => "test",
- "endpoint_id" => "MarginaliaTestController#first_ci_pipeline",
+ "application" => "test",
+ "endpoint_id" => "MarginaliaTestController#first_ci_pipeline",
"correlation_id" => correlation_id,
"db_config_name" => 'ci'
}
@@ -104,10 +104,10 @@ RSpec.describe 'Marginalia spec' do
let(:component_map) do
{
- "application" => "sidekiq",
- "endpoint_id" => "MarginaliaTestJob",
+ "application" => "sidekiq",
+ "endpoint_id" => "MarginaliaTestJob",
"correlation_id" => sidekiq_job['correlation_id'],
- "jid" => sidekiq_job['jid'],
+ "jid" => sidekiq_job['jid'],
"db_config_name" => "main"
}
end
@@ -129,9 +129,9 @@ RSpec.describe 'Marginalia spec' do
let(:component_map) do
{
- "application" => "sidekiq",
- "endpoint_id" => "ActionMailer::MailDeliveryJob",
- "jid" => delivery_job.job_id,
+ "application" => "sidekiq",
+ "endpoint_id" => "ActionMailer::MailDeliveryJob",
+ "jid" => delivery_job.job_id,
"db_config_name" => "main"
}
end
diff --git a/spec/lib/microsoft_teams/activity_spec.rb b/spec/lib/microsoft_teams/activity_spec.rb
index d1eac7204a6..08f71985a2a 100644
--- a/spec/lib/microsoft_teams/activity_spec.rb
+++ b/spec/lib/microsoft_teams/activity_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe MicrosoftTeams::Activity do
subject { described_class.new(title: 'title', subtitle: 'subtitle', text: 'text', image: 'image') }
diff --git a/spec/lib/object_storage/direct_upload_spec.rb b/spec/lib/object_storage/direct_upload_spec.rb
index 18a58522d12..1629aec89f5 100644
--- a/spec/lib/object_storage/direct_upload_spec.rb
+++ b/spec/lib/object_storage/direct_upload_spec.rb
@@ -342,84 +342,68 @@ RSpec.describe ObjectStorage::DirectUpload do
context 'when length is unknown' do
let(:has_length) { false }
- context 'when s3_omit_multipart_urls feature flag is enabled' do
- let(:consolidated_settings) { true }
-
- it 'omits multipart URLs' do
- expect(subject).not_to have_key(:MultipartUpload)
- end
-
- it_behaves_like 'a valid upload'
- end
-
- context 'when s3_omit_multipart_urls feature flag is disabled' do
+ it_behaves_like 'a valid S3 upload with multipart data' do
before do
- stub_feature_flags(s3_omit_multipart_urls: false)
+ stub_object_storage_multipart_init(storage_url, "myUpload")
end
- it_behaves_like 'a valid S3 upload with multipart data' do
- before do
- stub_object_storage_multipart_init(storage_url, "myUpload")
- end
-
- context 'when maximum upload size is 0' do
- let(:maximum_size) { 0 }
+ context 'when maximum upload size is 0' do
+ let(:maximum_size) { 0 }
- it 'returns maximum number of parts' do
- expect(subject[:MultipartUpload][:PartURLs].length).to eq(100)
- end
+ it 'returns maximum number of parts' do
+ expect(subject[:MultipartUpload][:PartURLs].length).to eq(100)
+ end
- it 'part size is minimum, 5MB' do
- expect(subject[:MultipartUpload][:PartSize]).to eq(5.megabyte)
- end
+ it 'part size is minimum, 5MB' do
+ expect(subject[:MultipartUpload][:PartSize]).to eq(5.megabyte)
end
+ end
- context 'when maximum upload size is < 5 MB' do
- let(:maximum_size) { 1024 }
+ context 'when maximum upload size is < 5 MB' do
+ let(:maximum_size) { 1024 }
- it 'returns only 1 part' do
- expect(subject[:MultipartUpload][:PartURLs].length).to eq(1)
- end
+ it 'returns only 1 part' do
+ expect(subject[:MultipartUpload][:PartURLs].length).to eq(1)
+ end
- it 'part size is minimum, 5MB' do
- expect(subject[:MultipartUpload][:PartSize]).to eq(5.megabyte)
- end
+ it 'part size is minimum, 5MB' do
+ expect(subject[:MultipartUpload][:PartSize]).to eq(5.megabyte)
end
+ end
- context 'when maximum upload size is 10MB' do
- let(:maximum_size) { 10.megabyte }
+ context 'when maximum upload size is 10MB' do
+ let(:maximum_size) { 10.megabyte }
- it 'returns only 2 parts' do
- expect(subject[:MultipartUpload][:PartURLs].length).to eq(2)
- end
+ it 'returns only 2 parts' do
+ expect(subject[:MultipartUpload][:PartURLs].length).to eq(2)
+ end
- it 'part size is minimum, 5MB' do
- expect(subject[:MultipartUpload][:PartSize]).to eq(5.megabyte)
- end
+ it 'part size is minimum, 5MB' do
+ expect(subject[:MultipartUpload][:PartSize]).to eq(5.megabyte)
end
+ end
- context 'when maximum upload size is 12MB' do
- let(:maximum_size) { 12.megabyte }
+ context 'when maximum upload size is 12MB' do
+ let(:maximum_size) { 12.megabyte }
- it 'returns only 3 parts' do
- expect(subject[:MultipartUpload][:PartURLs].length).to eq(3)
- end
+ it 'returns only 3 parts' do
+ expect(subject[:MultipartUpload][:PartURLs].length).to eq(3)
+ end
- it 'part size is rounded-up to 5MB' do
- expect(subject[:MultipartUpload][:PartSize]).to eq(5.megabyte)
- end
+ it 'part size is rounded-up to 5MB' do
+ expect(subject[:MultipartUpload][:PartSize]).to eq(5.megabyte)
end
+ end
- context 'when maximum upload size is 49GB' do
- let(:maximum_size) { 49.gigabyte }
+ context 'when maximum upload size is 49GB' do
+ let(:maximum_size) { 49.gigabyte }
- it 'returns maximum, 100 parts' do
- expect(subject[:MultipartUpload][:PartURLs].length).to eq(100)
- end
+ it 'returns maximum, 100 parts' do
+ expect(subject[:MultipartUpload][:PartURLs].length).to eq(100)
+ end
- it 'part size is rounded-up to 5MB' do
- expect(subject[:MultipartUpload][:PartSize]).to eq(505.megabyte)
- end
+ it 'part size is rounded-up to 5MB' do
+ expect(subject[:MultipartUpload][:PartSize]).to eq(505.megabyte)
end
end
end
diff --git a/spec/lib/omni_auth/strategies/bitbucket_spec.rb b/spec/lib/omni_auth/strategies/bitbucket_spec.rb
new file mode 100644
index 00000000000..d85ce71d60a
--- /dev/null
+++ b/spec/lib/omni_auth/strategies/bitbucket_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe OmniAuth::Strategies::Bitbucket do
+ subject { described_class.new({}) }
+
+ describe '#callback_url' do
+ let(:base_url) { 'https://example.com' }
+
+ context 'when script name is not present' do
+ it 'has the correct default callback path' do
+ allow(subject).to receive(:full_host) { base_url }
+ allow(subject).to receive(:script_name).and_return('')
+ allow(subject).to receive(:query_string).and_return('')
+ expect(subject.callback_url).to eq("#{base_url}/users/auth/bitbucket/callback")
+ end
+ end
+
+ context 'when script name is present' do
+ it 'sets the callback path with script_name' do
+ allow(subject).to receive(:full_host) { base_url }
+ allow(subject).to receive(:script_name).and_return('/v1')
+ allow(subject).to receive(:query_string).and_return('')
+ expect(subject.callback_url).to eq("#{base_url}/v1/users/auth/bitbucket/callback")
+ end
+ end
+ end
+end
diff --git a/spec/lib/peek/views/redis_detailed_spec.rb b/spec/lib/peek/views/redis_detailed_spec.rb
index a757af50dcb..5d75a6522e4 100644
--- a/spec/lib/peek/views/redis_detailed_spec.rb
+++ b/spec/lib/peek/views/redis_detailed_spec.rb
@@ -7,17 +7,19 @@ RSpec.describe Peek::Views::RedisDetailed, :request_store do
using RSpec::Parameterized::TableSyntax
- where(:cmd, :expected) do
- [:auth, 'test'] | 'auth <redacted>'
- [:set, 'key', 'value'] | 'set key <redacted>'
- [:set, 'bad'] | 'set bad'
- [:hmset, 'key1', 'value1', 'key2', 'value2'] | 'hmset key1 <redacted>'
- [:get, 'key'] | 'get key'
+ where(:commands, :expected) do
+ [[:auth, 'test']] | 'auth <redacted>'
+ [[:set, 'key', 'value']] | 'set key <redacted>'
+ [[:set, 'bad']] | 'set bad'
+ [[:hmset, 'key1', 'value1', 'key2', 'value2']] | 'hmset key1 <redacted>'
+ [[:get, 'key']] | 'get key'
+ [[:get, 'key1'], [:get, 'key2']] | 'get key1, get key2'
+ [[:set, 'key1', 'value'], [:set, 'key2', 'value']] | 'set key1 <redacted>, set key2 <redacted>'
end
with_them do
it 'scrubs Redis commands' do
- Gitlab::Instrumentation::Redis::SharedState.detail_store << { cmd: cmd, duration: 1.second }
+ Gitlab::Instrumentation::Redis::SharedState.detail_store << { commands: commands, duration: 1.second }
expect(subject.results[:details].count).to eq(1)
expect(subject.results[:details].first)
@@ -29,9 +31,9 @@ RSpec.describe Peek::Views::RedisDetailed, :request_store do
end
it 'returns aggregated results' do
- Gitlab::Instrumentation::Redis::Cache.detail_store << { cmd: [:get, 'test'], duration: 0.001 }
- Gitlab::Instrumentation::Redis::Cache.detail_store << { cmd: [:get, 'test'], duration: 1.second }
- Gitlab::Instrumentation::Redis::SharedState.detail_store << { cmd: [:get, 'test'], duration: 1.second }
+ Gitlab::Instrumentation::Redis::Cache.detail_store << { commands: [[:get, 'test']], duration: 0.001 }
+ Gitlab::Instrumentation::Redis::Cache.detail_store << { commands: [[:get, 'test']], duration: 1.second }
+ Gitlab::Instrumentation::Redis::SharedState.detail_store << { commands: [[:get, 'test']], duration: 1.second }
expect(subject.results[:calls]).to eq(3)
expect(subject.results[:duration]).to eq('2001.00ms')
diff --git a/spec/lib/prometheus/cleanup_multiproc_dir_service_spec.rb b/spec/lib/prometheus/cleanup_multiproc_dir_service_spec.rb
index bdf9673a53f..f93066e82be 100644
--- a/spec/lib/prometheus/cleanup_multiproc_dir_service_spec.rb
+++ b/spec/lib/prometheus/cleanup_multiproc_dir_service_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require 'tmpdir'
RSpec.describe Prometheus::CleanupMultiprocDirService do
describe '#execute' do
diff --git a/spec/lib/security/ci_configuration/sast_build_action_spec.rb b/spec/lib/security/ci_configuration/sast_build_action_spec.rb
index 611a886d252..381ea60e7f5 100644
--- a/spec/lib/security/ci_configuration/sast_build_action_spec.rb
+++ b/spec/lib/security/ci_configuration/sast_build_action_spec.rb
@@ -33,12 +33,12 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do
params.merge( { analyzers:
[
{
- name: "bandit",
- enabled: false
+ name: "bandit",
+ enabled: false
},
{
- name: "brakeman",
- enabled: true,
+ name: "brakeman",
+ enabled: true,
variables: [
{ field: "SAST_BRAKEMAN_LEVEL",
default_value: "1",
@@ -46,8 +46,8 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do
]
},
{
- name: "flawfinder",
- enabled: true,
+ name: "flawfinder",
+ enabled: true,
variables: [
{ field: "SAST_FLAWFINDER_LEVEL",
default_value: "1",
@@ -62,12 +62,12 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do
params.merge( { analyzers:
[
{
- name: "flawfinder",
- enabled: true
+ name: "flawfinder",
+ enabled: true
},
{
- name: "brakeman",
- enabled: true
+ name: "brakeman",
+ enabled: true
}
] }
)
@@ -219,49 +219,49 @@ RSpec.describe Security::CiConfiguration::SastBuildAction do
def existing_gitlab_ci_and_template_array_without_sast
{ "stages" => %w(test security),
- "variables" => { "RANDOM" => "make sure this persists", "SECURE_ANALYZERS_PREFIX" => "localhost:5000/analyzers" },
- "sast" => { "variables" => { "SEARCH_MAX_DEPTH" => 1 }, "stage" => "security" },
- "include" => [{ "template" => "existing.yml" }] }
+ "variables" => { "RANDOM" => "make sure this persists", "SECURE_ANALYZERS_PREFIX" => "localhost:5000/analyzers" },
+ "sast" => { "variables" => { "SEARCH_MAX_DEPTH" => 1 }, "stage" => "security" },
+ "include" => [{ "template" => "existing.yml" }] }
end
def existing_gitlab_ci_and_single_template_with_sast_and_default_stage
{ "stages" => %w(test),
- "variables" => { "SECURE_ANALYZERS_PREFIX" => "localhost:5000/analyzers" },
- "sast" => { "variables" => { "SEARCH_MAX_DEPTH" => 1 }, "stage" => "test" },
- "include" => { "template" => "Security/SAST.gitlab-ci.yml" } }
+ "variables" => { "SECURE_ANALYZERS_PREFIX" => "localhost:5000/analyzers" },
+ "sast" => { "variables" => { "SEARCH_MAX_DEPTH" => 1 }, "stage" => "test" },
+ "include" => { "template" => "Security/SAST.gitlab-ci.yml" } }
end
def existing_gitlab_ci_and_single_template_without_sast
{ "stages" => %w(test security),
- "variables" => { "RANDOM" => "make sure this persists", "SECURE_ANALYZERS_PREFIX" => "localhost:5000/analyzers" },
- "sast" => { "variables" => { "SEARCH_MAX_DEPTH" => 1 }, "stage" => "security" },
- "include" => { "template" => "existing.yml" } }
+ "variables" => { "RANDOM" => "make sure this persists", "SECURE_ANALYZERS_PREFIX" => "localhost:5000/analyzers" },
+ "sast" => { "variables" => { "SEARCH_MAX_DEPTH" => 1 }, "stage" => "security" },
+ "include" => { "template" => "existing.yml" } }
end
def existing_gitlab_ci_with_no_variables
{ "stages" => %w(test security),
- "sast" => { "variables" => { "SEARCH_MAX_DEPTH" => 1 }, "stage" => "security" },
- "include" => [{ "template" => "Security/SAST.gitlab-ci.yml" }] }
+ "sast" => { "variables" => { "SEARCH_MAX_DEPTH" => 1 }, "stage" => "security" },
+ "include" => [{ "template" => "Security/SAST.gitlab-ci.yml" }] }
end
def existing_gitlab_ci_with_no_sast_section
{ "stages" => %w(test security),
- "variables" => { "RANDOM" => "make sure this persists", "SECURE_ANALYZERS_PREFIX" => "localhost:5000/analyzers" },
- "include" => [{ "template" => "Security/SAST.gitlab-ci.yml" }] }
+ "variables" => { "RANDOM" => "make sure this persists", "SECURE_ANALYZERS_PREFIX" => "localhost:5000/analyzers" },
+ "include" => [{ "template" => "Security/SAST.gitlab-ci.yml" }] }
end
def existing_gitlab_ci_with_no_sast_variables
{ "stages" => %w(test security),
- "variables" => { "RANDOM" => "make sure this persists", "SECURE_ANALYZERS_PREFIX" => "localhost:5000/analyzers" },
- "sast" => { "stage" => "security" },
- "include" => [{ "template" => "Security/SAST.gitlab-ci.yml" }] }
+ "variables" => { "RANDOM" => "make sure this persists", "SECURE_ANALYZERS_PREFIX" => "localhost:5000/analyzers" },
+ "sast" => { "stage" => "security" },
+ "include" => [{ "template" => "Security/SAST.gitlab-ci.yml" }] }
end
def existing_gitlab_ci
{ "stages" => %w(test security),
- "variables" => { "RANDOM" => "make sure this persists", "SECURE_ANALYZERS_PREFIX" => "bad_prefix" },
- "sast" => { "variables" => { "SEARCH_MAX_DEPTH" => 1 }, "stage" => "security" },
- "include" => [{ "template" => "Security/SAST.gitlab-ci.yml" }] }
+ "variables" => { "RANDOM" => "make sure this persists", "SECURE_ANALYZERS_PREFIX" => "bad_prefix" },
+ "sast" => { "variables" => { "SEARCH_MAX_DEPTH" => 1 }, "stage" => "security" },
+ "include" => [{ "template" => "Security/SAST.gitlab-ci.yml" }] }
end
end
diff --git a/spec/lib/security/report_schema_version_matcher_spec.rb b/spec/lib/security/report_schema_version_matcher_spec.rb
index 9c40f0bc6fa..eaf49aa4744 100644
--- a/spec/lib/security/report_schema_version_matcher_spec.rb
+++ b/spec/lib/security/report_schema_version_matcher_spec.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Security::ReportSchemaVersionMatcher do
let(:vendored_versions) { %w[14.0.0 14.0.1 14.0.2 14.1.0] }
diff --git a/spec/lib/security/weak_passwords_spec.rb b/spec/lib/security/weak_passwords_spec.rb
new file mode 100644
index 00000000000..9d12c352abf
--- /dev/null
+++ b/spec/lib/security/weak_passwords_spec.rb
@@ -0,0 +1,112 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Security::WeakPasswords do
+ describe "#weak_for_user?" do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:user) do
+ build_stubbed(:user, username: "56d4ab689a_win",
+ name: "Weakést McWeaky-Pass Jr",
+ email: "predictÄble.ZZZ+seventeen@examplecorp.com",
+ public_email: "fortunate@acme.com"
+ )
+ end
+
+ where(:password, :too_weak) do
+ # A random password is not too weak
+ "d2262d56" | false
+
+ # The case-insensitive weak password list
+ "password" | true
+ "pAssWord" | true
+ "princeofdarkness" | true
+
+ # Forbidden substrings
+ "A1B2gitlabC3" | true
+ "gitlab123" | true
+ "theonedevopsplatform" | true
+ "A1gitlib" | false
+
+ # Predicatable name substrings
+ "Aweakést" | true
+ "!@mCwEaKy" | true
+ "A1B2pass" | true
+ "A1B2C3jr" | false # jr is too short
+
+ # Predictable username substrings
+ "56d4ab689a" | true
+ "56d4ab689a_win" | true
+ "56d4ab68" | false # it's part of the username, but not a full part
+ "A1B2Cwin" | false # win is too short
+
+ # Predictable user.email substrings
+ "predictÄble.ZZZ+seventeen@examplecorp.com" | true
+ "predictable.ZZZ+seventeen@examplecorp.com" | true
+ "predictÄble.ZZZ+seventeen" | true
+ "examplecorp.com" | true
+ "!@exAmplecorp" | true
+ "predictÄble123" | true
+ "seventeen" | true
+ "predictable" | false # the accent is different
+ "A1B2CZzZ" | false # ZZZ is too short
+ # Other emails are not considered
+ "fortunate@acme.com" | false
+ "A1B2acme" | false
+ "fortunate" | false
+
+ # A short password is not automatically too weak
+ # We rely on User's password length validation, not WeakPasswords.
+ "1" | false
+ "1234567" | false
+ # But a short password with forbidden words or user attributes
+ # is still weak
+ "gitlab" | true
+ "pass" | true
+ end
+
+ with_them do
+ it { expect(subject.weak_for_user?(password, user)).to eq(too_weak) }
+ end
+
+ context 'with a user who has short email parts' do
+ before do
+ user.email = 'sid@1.io'
+ end
+
+ where(:password, :too_weak) do
+ "11111111" | true # This is on the weak password list
+ "1.ioABCD" | true # 1.io is long enough to match
+ "sid@1.io" | true # matches the email in full
+ "sid@1.ioAB" | true
+ # sid, 1, and io on their own are too short
+ "sid1ioAB" | false
+ "sidsidsi" | false
+ "ioioioio" | false
+ end
+
+ with_them do
+ it { expect(subject.weak_for_user?(password, user)).to eq(too_weak) }
+ end
+ end
+
+ context 'with a user who is missing attributes' do
+ before do
+ user.name = nil
+ user.email = nil
+ user.username = nil
+ end
+
+ where(:password, :too_weak) do
+ "d2262d56" | false
+ "password" | true
+ "gitlab123" | true
+ end
+
+ with_them do
+ it { expect(subject.weak_for_user?(password, user)).to eq(too_weak) }
+ end
+ end
+ end
+end
diff --git a/spec/lib/sidebars/concerns/container_with_html_options_spec.rb b/spec/lib/sidebars/concerns/container_with_html_options_spec.rb
index 7f834419866..d95cdb9e0fe 100644
--- a/spec/lib/sidebars/concerns/container_with_html_options_spec.rb
+++ b/spec/lib/sidebars/concerns/container_with_html_options_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Sidebars::Concerns::ContainerWithHtmlOptions do
subject do
diff --git a/spec/lib/sidebars/concerns/link_with_html_options_spec.rb b/spec/lib/sidebars/concerns/link_with_html_options_spec.rb
index 1e890bffad1..f7e6701c37d 100644
--- a/spec/lib/sidebars/concerns/link_with_html_options_spec.rb
+++ b/spec/lib/sidebars/concerns/link_with_html_options_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Sidebars::Concerns::LinkWithHtmlOptions do
let(:options) { {} }
diff --git a/spec/lib/sidebars/groups/menus/observability_menu_spec.rb b/spec/lib/sidebars/groups/menus/observability_menu_spec.rb
new file mode 100644
index 00000000000..3a91b1aea2f
--- /dev/null
+++ b/spec/lib/sidebars/groups/menus/observability_menu_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Sidebars::Groups::Menus::ObservabilityMenu do
+ let_it_be(:owner) { create(:user) }
+ let_it_be(:root_group) do
+ build(:group, :private).tap do |g|
+ g.add_owner(owner)
+ end
+ end
+
+ let(:group) { root_group }
+ let(:user) { owner }
+ let(:context) { Sidebars::Groups::Context.new(current_user: user, container: group) }
+ let(:menu) { described_class.new(context) }
+
+ describe '#render?' do
+ before do
+ allow(menu).to receive(:can?).and_call_original
+ end
+
+ context 'when user can :read_observability' do
+ before do
+ allow(menu).to receive(:can?).with(user, :read_observability, group).and_return(true)
+ end
+
+ it 'returns true' do
+ expect(menu.render?).to eq true
+ end
+ end
+
+ context 'when user cannot :read_observability' do
+ before do
+ allow(menu).to receive(:can?).with(user, :read_observability, group).and_return(false)
+ end
+
+ it 'returns false' do
+ expect(menu.render?).to eq false
+ end
+ end
+ end
+end
diff --git a/spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb b/spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb
index d3cb18222b5..c5666724acf 100644
--- a/spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb
+++ b/spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb
@@ -10,6 +10,8 @@ RSpec.describe Sidebars::Groups::Menus::PackagesRegistriesMenu do
end
end
+ let_it_be(:harbor_integration) { create(:harbor_integration, group: group, project: nil) }
+
let(:user) { owner }
let(:context) { Sidebars::Groups::Context.new(current_user: user, container: group) }
let(:menu) { described_class.new(context) }
diff --git a/spec/lib/sidebars/groups/menus/settings_menu_spec.rb b/spec/lib/sidebars/groups/menus/settings_menu_spec.rb
index 252da8ea699..4e3c639672b 100644
--- a/spec/lib/sidebars/groups/menus/settings_menu_spec.rb
+++ b/spec/lib/sidebars/groups/menus/settings_menu_spec.rb
@@ -80,7 +80,7 @@ RSpec.describe Sidebars::Groups::Menus::SettingsMenu do
it_behaves_like 'access rights checks'
end
- describe 'Packages & Registries' do
+ describe 'Packages and registries' do
let(:item_id) { :packages_and_registries }
before do
diff --git a/spec/lib/sidebars/menu_item_spec.rb b/spec/lib/sidebars/menu_item_spec.rb
index 3adde64f550..15804f51934 100644
--- a/spec/lib/sidebars/menu_item_spec.rb
+++ b/spec/lib/sidebars/menu_item_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe Sidebars::MenuItem do
let(:title) { 'foo' }
diff --git a/spec/lib/sidebars/projects/menus/deployments_menu_spec.rb b/spec/lib/sidebars/projects/menus/deployments_menu_spec.rb
index 56eb082e101..90ff04a2064 100644
--- a/spec/lib/sidebars/projects/menus/deployments_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/deployments_menu_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::DeploymentsMenu do
- let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:project, reload: true) { create(:project, :repository) }
let(:user) { project.first_owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
@@ -37,6 +37,40 @@ RSpec.describe Sidebars::Projects::Menus::DeploymentsMenu do
specify { is_expected.to be_nil }
end
+
+ describe 'when the feature is disabled' do
+ before do
+ project.update_attribute("#{item_id}_access_level", 'disabled')
+ end
+
+ it { is_expected.to be_nil }
+ end
+
+ describe 'when split_operations_visibility_permissions FF is 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
end
describe 'Feature Flags' do
diff --git a/spec/lib/sidebars/projects/menus/learn_gitlab_menu_spec.rb b/spec/lib/sidebars/projects/menus/learn_gitlab_menu_spec.rb
index 36a76e70a48..4ae29f28f3a 100644
--- a/spec/lib/sidebars/projects/menus/learn_gitlab_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/learn_gitlab_menu_spec.rb
@@ -68,13 +68,11 @@ RSpec.describe Sidebars::Projects::Menus::LearnGitlabMenu do
end
describe '#pill_count' do
- before do
- expect_next_instance_of(LearnGitlab::Onboarding) do |onboarding|
- expect(onboarding).to receive(:completed_percentage).and_return(20)
+ it 'returns pill count' do
+ expect_next_instance_of(Onboarding::Completion) do |onboarding|
+ expect(onboarding).to receive(:percentage).and_return(20)
end
- end
- it 'returns pill count' do
expect(subject.pill_count).to eq '20%'
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 ba5137e2b92..bd0904b9db2 100644
--- a/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/monitor_menu_spec.rb
@@ -12,11 +12,28 @@ RSpec.describe Sidebars::Projects::Menus::MonitorMenu do
subject { described_class.new(context) }
describe '#render?' do
- context 'when operations feature is disabled' do
- it 'returns false' do
- project.project_feature.update!(operations_access_level: Featurable::DISABLED)
+ using RSpec::Parameterized::TableSyntax
+ 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
+ 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 false
+ expect(subject.render?).to be render
end
end
diff --git a/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb b/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb
index 9b78fc807bf..6491ef823e9 100644
--- a/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb
@@ -5,6 +5,8 @@ require 'spec_helper'
RSpec.describe Sidebars::Projects::Menus::PackagesRegistriesMenu do
let_it_be(:project) { create(:project) }
+ let_it_be(:harbor_integration) { create(:harbor_integration, project: project) }
+
let(:user) { project.first_owner }
let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) }
@@ -65,7 +67,7 @@ RSpec.describe Sidebars::Projects::Menus::PackagesRegistriesMenu do
describe 'Packages Registry' do
let(:item_id) { :packages_registry }
- context 'when user can read packages' do
+ shared_examples 'when user can read packages' do
context 'when config package setting is disabled' do
it 'the menu item is not added to list of menu items' do
stub_config(packages: { enabled: false })
@@ -83,13 +85,25 @@ RSpec.describe Sidebars::Projects::Menus::PackagesRegistriesMenu do
end
end
- context 'when user cannot read packages' do
+ shared_examples 'when user cannot read packages' do
let(:user) { nil }
it 'the menu item is not added to list of menu items' do
is_expected.to be_nil
end
end
+
+ it_behaves_like 'when user can read packages'
+ it_behaves_like 'when user cannot read packages'
+
+ context 'with feature flag disabled' do
+ before do
+ stub_feature_flags(read_package_policy_rule: false)
+ end
+
+ it_behaves_like 'when user can read packages'
+ it_behaves_like 'when user cannot read packages'
+ end
end
describe 'Container Registry' do
diff --git a/spec/lib/sidebars/projects/menus/settings_menu_spec.rb b/spec/lib/sidebars/projects/menus/settings_menu_spec.rb
index f41f7a01d88..0733e0c6521 100644
--- a/spec/lib/sidebars/projects/menus/settings_menu_spec.rb
+++ b/spec/lib/sidebars/projects/menus/settings_menu_spec.rb
@@ -133,7 +133,13 @@ RSpec.describe Sidebars::Projects::Menus::SettingsMenu do
end
end
- describe 'Packages & Registries' do
+ describe 'Merge requests' do
+ let(:item_id) { :merge_requests }
+
+ it_behaves_like 'access rights checks'
+ end
+
+ describe 'Packages and registries' do
let(:item_id) { :packages_and_registries }
let(:packages_enabled) { false }
diff --git a/spec/lib/system_check/base_check_spec.rb b/spec/lib/system_check/base_check_spec.rb
index 59b2fc519ae..241c3b33777 100644
--- a/spec/lib/system_check/base_check_spec.rb
+++ b/spec/lib/system_check/base_check_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'spec_helper'
+require 'fast_spec_helper'
RSpec.describe SystemCheck::BaseCheck do
context 'helpers on instance level' do
diff --git a/spec/mailers/emails/pipelines_spec.rb b/spec/mailers/emails/pipelines_spec.rb
index 3a2eb105964..1ac989cc46b 100644
--- a/spec/mailers/emails/pipelines_spec.rb
+++ b/spec/mailers/emails/pipelines_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe Emails::Pipelines do
let!(:merge_request) do
create(:merge_request, source_branch: 'master', target_branch: 'feature',
- source_project: project, target_project: project)
+ source_project: project, target_project: project)
end
it 'has correct information that there is no merge request link' do
@@ -56,7 +56,7 @@ RSpec.describe Emails::Pipelines do
context 'when branch pipeline is set to a merge request as a head pipeline' do
let(:pipeline) do
create(:ci_pipeline, project: project, ref: ref, sha: sha,
- merge_requests_as_head_pipeline: [merge_request])
+ merge_requests_as_head_pipeline: [merge_request])
end
let(:merge_request) do
diff --git a/spec/mailers/emails/service_desk_spec.rb b/spec/mailers/emails/service_desk_spec.rb
index 28011456a66..1523d9b986b 100644
--- a/spec/mailers/emails/service_desk_spec.rb
+++ b/spec/mailers/emails/service_desk_spec.rb
@@ -76,7 +76,7 @@ RSpec.describe Emails::ServiceDesk do
shared_examples 'read template from repository' do |template_key|
let(:template_content) { 'custom text' }
- let(:issue) { create(:issue, project: project)}
+ let(:issue) { create(:issue, project: project) }
before do
issue.issue_email_participants.create!(email: email)
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 8beb54bca4d..1f53c472c5c 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -167,6 +167,17 @@ RSpec.describe Notify do
is_expected.to have_header('X-GitLab-NotificationReason', NotificationReason::ASSIGNED)
end
end
+
+ context 'when sent with a non default locale' do
+ let(:email_obj) { create(:email, :confirmed, user_id: recipient.id, email: '123@abc') }
+ let(:recipient) { create(:user, preferred_language: :zh_CN) }
+
+ it 'is translated into zh_CN' do
+ recipient.notification_email = email_obj.email
+ recipient.save!
+ is_expected.to have_body_text '指派人从 <strong>Previous Assignee</strong> 更改为 <strong>John Doe</strong>'
+ end
+ end
end
describe 'that have been relabeled' do
diff --git a/spec/mailers/previews_spec.rb b/spec/mailers/previews_spec.rb
new file mode 100644
index 00000000000..14bd56e5d40
--- /dev/null
+++ b/spec/mailers/previews_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Mailer previews' do
+ # Setup needed for email previews
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, :repository, :import_failed, group: group, import_last_error: 'some error') }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+ let_it_be(:merge_request) { create(:merge_request, source_project: project) }
+ let_it_be(:milestone) { create(:milestone, project: project) }
+ let_it_be(:issue) { create(:issue, project: project, milestone: milestone) }
+ let_it_be(:remote_mirror) { create(:remote_mirror, project: project) }
+ let_it_be(:member) { create(:project_member, :maintainer, project: project, created_by: user) }
+
+ Gitlab.ee do
+ let_it_be(:epic) { create(:epic, group: group) }
+ end
+
+ let(:expected_kind) { [Mail::Message, ActionMailer::MessageDelivery] }
+
+ let(:pending_failures) do
+ {
+ 'NotifyPreview#note_merge_request_email_for_diff_discussion' =>
+ 'https://gitlab.com/gitlab-org/gitlab/-/issues/372885'
+ }
+ end
+
+ subject { preview.call(email) }
+
+ where(:preview, :email) do
+ ActionMailer::Preview.all.flat_map { |preview| preview.emails.map { |email| [preview, email] } }
+ end
+
+ with_them do
+ it do
+ issue_link = pending_failures["#{preview.name}##{email}"]
+ pending "See #{issue_link}" if issue_link
+
+ is_expected.to be_kind_of(Mail::Message).or(be_kind_of(ActionMailer::MessageDelivery))
+ end
+ end
+end
diff --git a/spec/mailers/repository_check_mailer_spec.rb b/spec/mailers/repository_check_mailer_spec.rb
index 8b1bc33d8be..5edd9c2d023 100644
--- a/spec/mailers/repository_check_mailer_spec.rb
+++ b/spec/mailers/repository_check_mailer_spec.rb
@@ -14,6 +14,15 @@ RSpec.describe RepositoryCheckMailer do
expect(mail).to deliver_to admins.map(&:email)
end
+ it 'email with I18n.default_locale' do
+ admins = [create(:admin, preferred_language: :zh_CN), create(:admin, preferred_language: :zh_CN)]
+
+ mail = described_class.notify(3)
+
+ expect(mail).to deliver_to admins.map(&:email)
+ expect(mail).to have_subject 'GitLab Admin | 3 projects failed their last repository check'
+ end
+
it 'omits blocked admins' do
blocked = create(:admin, :blocked)
admins = create_list(:admin, 3)
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 6df8e1b2ebf..ae510826fe1 100644
--- a/spec/migrations/20210804150320_create_base_work_item_types_spec.rb
+++ b/spec/migrations/20210804150320_create_base_work_item_types_spec.rb
@@ -10,9 +10,9 @@ RSpec.describe CreateBaseWorkItemTypes, :migration do
let(:base_types) do
{
- issue: 0,
- incident: 1,
- test_case: 2,
+ issue: 0,
+ incident: 1,
+ test_case: 2,
requirement: 3
}
end
diff --git a/spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb b/spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb
index f734456b0b6..c88f94c6426 100644
--- a/spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb
+++ b/spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb
@@ -48,7 +48,7 @@ RSpec.describe RemoveDuplicateProjectAuthorizations, :migration do
project_authorizations.create! project_id: project_1.id, user_id: user_1.id, access_level: Gitlab::Access::REPORTER
end
- it { expect { subject }.to change { ProjectAuthorization.count}.from(3).to(1) }
+ it { expect { subject }.to change { ProjectAuthorization.count }.from(3).to(1) }
it 'retains the highest access level' do
subject
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 1957a973ee1..552602983d9 100644
--- a/spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb
+++ b/spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb
@@ -10,9 +10,9 @@ RSpec.describe UpsertBaseWorkItemTypes, :migration do
let(:base_types) do
{
- issue: 0,
- incident: 1,
- test_case: 2,
+ issue: 0,
+ incident: 1,
+ test_case: 2,
requirement: 3
}
end
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 c90eabbe4eb..69ee10eb0d1 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
@@ -39,7 +39,7 @@ RSpec.describe UpdateReportTypeForExistingApprovalProjectRules, :migration do
end
context 'with the rule name set to another value (e.g., Test Rule)' do
- let(:rule_name) { 'Test Rule'}
+ let(:rule_name) { 'Test Rule' }
it 'does not update report_type' do
expect { migrate! }.not_to change { approval_project_rule.reload.report_type }
diff --git a/spec/migrations/20211117084814_migrate_remaining_u2f_registrations_spec.rb b/spec/migrations/20211117084814_migrate_remaining_u2f_registrations_spec.rb
index 6a82ed016af..ef6dd94d9e3 100644
--- a/spec/migrations/20211117084814_migrate_remaining_u2f_registrations_spec.rb
+++ b/spec/migrations/20211117084814_migrate_remaining_u2f_registrations_spec.rb
@@ -33,11 +33,11 @@ RSpec.describe MigrateRemainingU2fRegistrations, :migration do
device = U2F::FakeU2F.new(FFaker::BaconIpsum.characters(5), { key_handle: SecureRandom.random_bytes(255) })
public_key ||= Base64.strict_encode64(device.origin_public_key_raw)
u2f_registrations.create!({ id: id,
- certificate: Base64.strict_encode64(device.cert_raw),
- key_handle: U2F.urlsafe_encode64(device.key_handle_raw),
- public_key: public_key,
- counter: 5,
- name: name,
- user_id: user.id })
+ certificate: Base64.strict_encode64(device.cert_raw),
+ key_handle: U2F.urlsafe_encode64(device.key_handle_raw),
+ public_key: public_key,
+ counter: 5,
+ name: name,
+ user_id: user.id })
end
end
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 b80e4703f07..34a6e2fdd12 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
@@ -10,11 +10,11 @@ RSpec.describe AddTaskToWorkItemTypes, :migration do
let(:base_types) do
{
- issue: 0,
- incident: 1,
- test_case: 2,
+ issue: 0,
+ incident: 1,
+ test_case: 2,
requirement: 3,
- task: 4
+ task: 4
}
end
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
new file mode 100644
index 00000000000..44e80980b27
--- /dev/null
+++ b/spec/migrations/20220601110011_schedule_remove_self_managed_wiki_notes_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe ScheduleRemoveSelfManagedWikiNotes do
+ let_it_be(:batched_migration) { described_class::MIGRATION }
+
+ it 'schedules 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: :notes,
+ column_name: :id,
+ interval: described_class::INTERVAL
+ )
+ }
+ end
+ end
+
+ context 'with com? or staging?' do
+ before do
+ allow(::Gitlab).to receive(:com?).and_return(true)
+ allow(::Gitlab).to receive(:staging?).and_return(false)
+ end
+
+ it 'does not schedule 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/20220606080509_fix_incorrect_job_artifacts_expire_at_spec.rb b/spec/migrations/20220606080509_fix_incorrect_job_artifacts_expire_at_spec.rb
new file mode 100644
index 00000000000..5921dd64c0e
--- /dev/null
+++ b/spec/migrations/20220606080509_fix_incorrect_job_artifacts_expire_at_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe FixIncorrectJobArtifactsExpireAt, migration: :gitlab_ci do
+ let_it_be(: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)
+
+ 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 job on non Gitlab.com' do
+ allow(Gitlab).to receive(:com?).and_return(false)
+
+ 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(
+ gitlab_schema: :gitlab_ci,
+ table_name: :ci_job_artifacts,
+ column_name: :id,
+ interval: described_class::INTERVAL,
+ batch_size: described_class::BATCH_SIZE
+ )
+ }
+ end
+ end
+end
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
new file mode 100644
index 00000000000..fdd97f2d008
--- /dev/null
+++ b/spec/migrations/20220801155858_schedule_disable_legacy_open_source_licence_for_recent_public_projects_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe ScheduleDisableLegacyOpenSourceLicenceForRecentPublicProjects, schema: 20220801155858 do
+ context 'when on gitlab.com' do
+ let(:background_migration) { described_class::MIGRATION }
+ let(:migration) { described_class.new }
+
+ before do
+ allow(Gitlab).to receive(:com?).and_return(true)
+ migration.up
+ end
+
+ describe '#up' do
+ it 'schedules background jobs for each batch of projects' do
+ expect(background_migration).to(
+ have_scheduled_batched_migration(
+ table_name: :projects,
+ column_name: :id,
+ interval: described_class::INTERVAL,
+ batch_size: described_class::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(described_class::MIGRATION).not_to have_scheduled_batched_migration
+ end
+ end
+ end
+
+ context 'when on self-managed instances' do
+ let(:migration) { described_class.new }
+
+ 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/20220809002011_schedule_destroy_invalid_group_members_spec.rb b/spec/migrations/20220809002011_schedule_destroy_invalid_group_members_spec.rb
new file mode 100644
index 00000000000..31dd4344d9f
--- /dev/null
+++ b/spec/migrations/20220809002011_schedule_destroy_invalid_group_members_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe ScheduleDestroyInvalidGroupMembers, :migration do
+ let_it_be(:migration) { described_class::MIGRATION }
+
+ describe '#up' do
+ it 'schedules background jobs for each batch of members' do
+ migrate!
+
+ expect(migration).to have_scheduled_batched_migration(
+ table_name: :members,
+ 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/20220816163444_update_start_date_for_iterations_cadences_spec.rb b/spec/migrations/20220816163444_update_start_date_for_iterations_cadences_spec.rb
new file mode 100644
index 00000000000..5a5e2362a53
--- /dev/null
+++ b/spec/migrations/20220816163444_update_start_date_for_iterations_cadences_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+require_migration!
+
+RSpec.describe UpdateStartDateForIterationsCadences, :freeze_time do
+ let(:migration) { described_class.new }
+ let(:namespaces) { table(:namespaces) }
+ let(:sprints) { table(:sprints) }
+ let(:iterations_cadences) { table(:iterations_cadences) }
+
+ let!(:group1) { namespaces.create!(name: 'abc', path: 'abc') }
+ let!(:group2) { namespaces.create!(name: 'def', path: 'def') }
+
+ let(:first_upcoming_start_date) { Date.current + 2.weeks }
+ let(:original_cadence_start_date) { Date.current - 1.week }
+
+ # rubocop: disable Layout/LineLength
+ let!(:auto_cadence1) { iterations_cadences.create!(start_date: original_cadence_start_date, group_id: group1.id, title: "ic") }
+ let!(:auto_cadence2) { iterations_cadences.create!(start_date: original_cadence_start_date, group_id: group1.id, title: "ic") }
+ let!(:auto_cadence3) { iterations_cadences.create!(start_date: nil, group_id: group2.id, title: "ic") }
+ let!(:manual_cadence1) { iterations_cadences.create!(start_date: Date.current, group_id: group1.id, automatic: false, title: "ic") }
+ let!(:manual_cadence2) { iterations_cadences.create!(start_date: Date.current, group_id: group2.id, automatic: false, title: "ic") }
+ # rubocop: enable Layout/LineLength
+
+ def cadence_params(cadence)
+ { iterations_cadence_id: cadence.id, group_id: cadence.group_id }
+ end
+
+ before do
+ # Past iteratioin
+ sprints.create!(id: 1, iid: 1, **cadence_params(auto_cadence1),
+ start_date: Date.current - 1.week, due_date: Date.current - 1.day)
+ # Current iteraition
+ sprints.create!(id: 3, iid: 5, **cadence_params(auto_cadence1),
+ start_date: Date.current, due_date: Date.current + 1.week)
+ # First upcoming iteration
+ sprints.create!(id: 4, iid: 8, **cadence_params(auto_cadence1),
+ start_date: first_upcoming_start_date, due_date: first_upcoming_start_date + 1.week)
+ # Second upcoming iteration
+ sprints.create!(id: 5, iid: 9, **cadence_params(auto_cadence1),
+ start_date: first_upcoming_start_date + 2.weeks, due_date: first_upcoming_start_date + 3.weeks)
+
+ sprints.create!(id: 6, iid: 1, **cadence_params(manual_cadence2),
+ start_date: Date.current, due_date: Date.current + 1.week)
+ sprints.create!(id: 7, iid: 5, **cadence_params(manual_cadence2),
+ start_date: Date.current + 2.weeks, due_date: Date.current + 3.weeks)
+ end
+
+ describe '#up' do
+ it "updates the start date of an automatic cadence to the start date of its first upcoming sprint record." do
+ expect { migration.up }
+ .to change { auto_cadence1.reload.start_date }.to(first_upcoming_start_date)
+ .and not_change { auto_cadence2.reload.start_date } # the cadence doesn't have any upcoming iteration.
+ .and not_change { auto_cadence3.reload.start_date } # the cadence is empty; it has no iterations.
+ .and not_change { manual_cadence1.reload.start_date } # manual cadence don't need to be touched.
+ .and not_change { manual_cadence2.reload.start_date } # manual cadence don't need to be touched.
+ end
+ end
+
+ describe '#down' do
+ it "updates the start date of an automatic cadence to the start date of its earliest sprint record." do
+ migration.up
+
+ expect { migration.down }
+ .to change { auto_cadence1.reload.start_date }.to(original_cadence_start_date)
+ .and not_change { auto_cadence2.reload.start_date } # the cadence is empty; it has no iterations.
+ .and not_change { manual_cadence1.reload.start_date } # manual cadence don't need to be touched.
+ .and not_change { manual_cadence2.reload.start_date } # manual cadence don't need to be touched.
+ end
+ end
+end
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
new file mode 100644
index 00000000000..c53dd9de649
--- /dev/null
+++ b/spec/migrations/20220819153725_add_vulnerability_advisory_foreign_key_to_sbom_vulnerable_component_versions_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+require_migration!
+
+RSpec.describe AddVulnerabilityAdvisoryForeignKeyToSbomVulnerableComponentVersions do
+ let(:table) { described_class::SOURCE_TABLE }
+ let(:column) { described_class::COLUMN }
+ let(:foreign_key) { -> { described_class.new.foreign_keys_for(table, column).first } }
+
+ it "creates and drops the foreign key" do
+ reversible_migration do |migration|
+ migration.before -> do
+ expect(foreign_key.call).to be(nil)
+ end
+
+ migration.after -> do
+ expect(foreign_key.call).to have_attributes(column: column.to_s)
+ end
+ end
+ end
+end
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
new file mode 100644
index 00000000000..b9cb6891681
--- /dev/null
+++ b/spec/migrations/20220819162852_add_sbom_component_version_foreign_key_to_sbom_vulnerable_component_versions_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+require_migration!
+
+RSpec.describe AddSbomComponentVersionForeignKeyToSbomVulnerableComponentVersions do
+ let(:table) { described_class::SOURCE_TABLE }
+ let(:column) { described_class::COLUMN }
+ let(:foreign_key) { -> { described_class.new.foreign_keys_for(table, column).first } }
+
+ it "creates and drops the foreign key" do
+ reversible_migration do |migration|
+ migration.before -> do
+ expect(foreign_key.call).to be(nil)
+ end
+
+ migration.after -> do
+ expect(foreign_key.call).to have_attributes(column: column.to_s)
+ end
+ end
+ end
+end
diff --git a/spec/migrations/20220901035725_schedule_destroy_invalid_project_members_spec.rb b/spec/migrations/20220901035725_schedule_destroy_invalid_project_members_spec.rb
new file mode 100644
index 00000000000..ed9f7e3cd44
--- /dev/null
+++ b/spec/migrations/20220901035725_schedule_destroy_invalid_project_members_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe ScheduleDestroyInvalidProjectMembers, :migration do
+ let_it_be(:migration) { described_class::MIGRATION }
+
+ describe '#up' do
+ it 'schedules background jobs for each batch of members' do
+ migrate!
+
+ expect(migration).to have_scheduled_batched_migration(
+ table_name: :members,
+ 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/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
new file mode 100644
index 00000000000..e4ac094ab48
--- /dev/null
+++ b/spec/migrations/20220906074449_schedule_disable_legacy_open_source_license_for_projects_less_than_one_mb_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe ScheduleDisableLegacyOpenSourceLicenseForProjectsLessThanOneMb do
+ let_it_be(:migration) { described_class.new }
+ let_it_be(: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/20220913030624_cleanup_attention_request_related_system_notes_spec.rb b/spec/migrations/20220913030624_cleanup_attention_request_related_system_notes_spec.rb
new file mode 100644
index 00000000000..7338a6ab9ae
--- /dev/null
+++ b/spec/migrations/20220913030624_cleanup_attention_request_related_system_notes_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe CleanupAttentionRequestRelatedSystemNotes, :migration do
+ let(:notes) { table(:notes) }
+ let(:system_note_metadata) { table(:system_note_metadata) }
+
+ it 'removes all notes with attention request related system_note_metadata' do
+ notes.create!(id: 1, note: 'Attention request note', noteable_type: 'MergeRequest')
+ notes.create!(id: 2, note: 'Attention request remove note', noteable_type: 'MergeRequest')
+ notes.create!(id: 3, note: 'MergeRequest note', noteable_type: 'MergeRequest')
+ notes.create!(id: 4, note: 'Commit note', noteable_type: 'Commit')
+ system_note_metadata.create!(id: 11, action: 'attention_requested', note_id: 1)
+ system_note_metadata.create!(id: 22, action: 'attention_request_removed', note_id: 2)
+ system_note_metadata.create!(id: 33, action: 'merged', note_id: 3)
+
+ expect { migrate! }.to change(notes, :count).by(-2)
+
+ expect(system_note_metadata.where(action: %w[attention_requested attention_request_removed]).size).to eq(0)
+ expect(notes.where(noteable_type: 'MergeRequest').size).to eq(1)
+ expect(notes.where(noteable_type: 'Commit').size).to eq(1)
+ expect(system_note_metadata.where(action: 'merged').size).to eq(1)
+ end
+end
diff --git a/spec/migrations/backfill_namespace_id_on_issues_spec.rb b/spec/migrations/backfill_namespace_id_on_issues_spec.rb
new file mode 100644
index 00000000000..2721d7ce8f1
--- /dev/null
+++ b/spec/migrations/backfill_namespace_id_on_issues_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe BackfillNamespaceIdOnIssues, :migration do
+ let(:migration) { described_class::MIGRATION }
+
+ describe '#up' do
+ it 'schedules background jobs for each batch of issues' do
+ migrate!
+
+ expect(migration).to have_scheduled_batched_migration(
+ table_name: :issues,
+ column_name: :id,
+ interval: described_class::DELAY_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
+ migrate!
+ schema_migrate_down!
+
+ expect(migration).not_to have_scheduled_batched_migration
+ end
+ end
+end
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
new file mode 100644
index 00000000000..039ee92f8bd
--- /dev/null
+++ b/spec/migrations/change_task_system_note_wording_to_checklist_item_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe ChangeTaskSystemNoteWordingToChecklistItem, :migration do
+ let(:migration) { described_class::MIGRATION }
+
+ describe '#up' do
+ it 'schedules a batched background migration' do
+ migrate!
+
+ expect(migration).to have_scheduled_batched_migration(
+ table_name: :system_note_metadata,
+ column_name: :id,
+ interval: described_class::DELAY_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
+ migrate!
+ schema_migrate_down!
+
+ expect(migration).not_to have_scheduled_batched_migration
+ end
+ end
+end
diff --git a/spec/migrations/confirm_support_bot_user_spec.rb b/spec/migrations/confirm_support_bot_user_spec.rb
index f6bcab4aa7d..c60c7fe45f7 100644
--- a/spec/migrations/confirm_support_bot_user_spec.rb
+++ b/spec/migrations/confirm_support_bot_user_spec.rb
@@ -52,7 +52,7 @@ RSpec.describe ConfirmSupportBotUser, :migration do
end
it 'does not change the `created_at` attribute' do
- expect { migrate!}.not_to change { support_bot.reload.created_at }.from(nil)
+ expect { migrate! }.not_to change { support_bot.reload.created_at }.from(nil)
end
end
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
new file mode 100644
index 00000000000..b5bb86edce2
--- /dev/null
+++ b/spec/migrations/move_security_findings_table_to_gitlab_partitions_dynamic_schema_spec.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe MoveSecurityFindingsTableToGitlabPartitionsDynamicSchema do
+ let(:partitions_sql) do
+ <<~SQL
+ SELECT
+ partitions.relname AS partition_name
+ FROM pg_inherits
+ JOIN pg_class parent ON pg_inherits.inhparent = parent.oid
+ JOIN pg_class partitions ON pg_inherits.inhrelid = partitions.oid
+ WHERE
+ parent.relname = 'security_findings'
+ SQL
+ end
+
+ describe '#up' do
+ it 'changes the `security_findings` table to be partitioned' do
+ expect { migrate! }.to change { security_findings_partitioned? }.from(false).to(true)
+ .and change { execute(partitions_sql) }.from([]).to(['security_findings_1'])
+ end
+ end
+
+ describe '#down' do
+ context 'when there is a partition' do
+ let(:users) { table(:users) }
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:scanners) { table(:vulnerability_scanners) }
+ let(:security_scans) { table(:security_scans) }
+ let(:security_findings) { table(:security_findings) }
+
+ let(:user) { users.create!(email: 'test@gitlab.com', projects_limit: 5) }
+ let(:namespace) { namespaces.create!(name: 'gtlb', path: 'gitlab', type: Namespaces::UserNamespace.sti_name) }
+ let(:project) { projects.create!(namespace_id: namespace.id, project_namespace_id: namespace.id, name: 'foo') }
+ let(:scanner) { scanners.create!(project_id: project.id, external_id: 'bandit', name: 'Bandit') }
+ let(:security_scan) { security_scans.create!(build_id: 1, scan_type: 1) }
+
+ let(:security_findings_count_sql) { 'SELECT COUNT(*) FROM security_findings' }
+
+ before do
+ migrate!
+
+ security_findings.create!(
+ scan_id: security_scan.id,
+ scanner_id: scanner.id,
+ uuid: SecureRandom.uuid,
+ severity: 0,
+ confidence: 0
+ )
+ end
+
+ it 'creates the original table with the data from the existing partition' do
+ expect { schema_migrate_down! }.to change { security_findings_partitioned? }.from(true).to(false)
+ .and not_change { execute(security_findings_count_sql) }.from([1])
+ end
+
+ context 'when there are more than one partitions' do
+ before do
+ migrate!
+
+ execute(<<~SQL)
+ CREATE TABLE gitlab_partitions_dynamic.security_findings_11
+ PARTITION OF security_findings FOR VALUES IN (11)
+ SQL
+ end
+
+ it 'creates the original table from the latest existing partition' do
+ expect { schema_migrate_down! }.to change { security_findings_partitioned? }.from(true).to(false)
+ .and change { execute(security_findings_count_sql) }.from([1]).to([0])
+ end
+ end
+ end
+
+ context 'when there is no partition' do
+ before do
+ migrate!
+
+ execute(partitions_sql).each do |partition_name|
+ execute("DROP TABLE gitlab_partitions_dynamic.#{partition_name}")
+ end
+ end
+
+ it 'creates the original table' do
+ expect { schema_migrate_down! }.to change { security_findings_partitioned? }.from(true).to(false)
+ end
+ end
+ end
+
+ def security_findings_partitioned?
+ sql = <<~SQL
+ SELECT
+ COUNT(*)
+ FROM
+ pg_partitioned_table
+ INNER JOIN pg_class ON pg_class.oid = pg_partitioned_table.partrelid
+ WHERE pg_class.relname = 'security_findings'
+ SQL
+
+ execute(sql).first != 0
+ end
+
+ def execute(sql)
+ ActiveRecord::Base.connection.execute(sql).values.flatten
+ end
+end
diff --git a/spec/migrations/orphaned_invited_members_cleanup_spec.rb b/spec/migrations/orphaned_invited_members_cleanup_spec.rb
new file mode 100644
index 00000000000..4427e707f56
--- /dev/null
+++ b/spec/migrations/orphaned_invited_members_cleanup_spec.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe OrphanedInvitedMembersCleanup, :migration do
+ describe '#up', :aggregate_failures do
+ it 'removes accepted members with no associated user' do
+ user = create_user!('testuser1')
+
+ create_member(invite_token: nil, invite_accepted_at: 1.day.ago)
+ record2 = create_member(invite_token: nil, invite_accepted_at: 1.day.ago, user_id: user.id)
+ record3 = create_member(invite_token: 'foo2', invite_accepted_at: nil)
+ record4 = create_member(invite_token: 'foo3', invite_accepted_at: 1.day.ago)
+
+ migrate!
+
+ expect(table(:members).all.pluck(:id)).to match_array([record2.id, record3.id, record4.id])
+ end
+ end
+
+ private
+
+ def create_user!(name)
+ email = "#{name}@example.com"
+
+ table(:users).create!(
+ name: name,
+ email: email,
+ username: name,
+ projects_limit: 0
+ )
+ end
+
+ def create_member(**extra_attributes)
+ defaults = {
+ access_level: 10,
+ source_id: 1,
+ source_type: "Project",
+ notification_level: 0,
+ type: 'ProjectMember'
+ }
+
+ table(:members).create!(defaults.merge(extra_attributes))
+ end
+end
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
new file mode 100644
index 00000000000..126d49790a5
--- /dev/null
+++ b/spec/migrations/reschedule_issue_work_item_type_id_backfill_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+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
+ table(:work_item_types).where(namespace_id: nil).order(:base_type).each_with_object({}) do |type, hash|
+ hash[type.base_type] = type.id
+ end
+ end
+
+ describe '#up' do
+ it 'correctly schedules background migrations' do
+ Sidekiq::Testing.fake! do
+ freeze_time do
+ migrate!
+
+ scheduled_migrations = Gitlab::Database::BackgroundMigration::BatchedMigration.where(
+ job_class_name: migration
+ )
+ work_item_types = table(:work_item_types).where(namespace_id: nil)
+
+ expect(scheduled_migrations.count).to eq(work_item_types.count)
+
+ [:issue, :incident, :test_case, :requirement, :task].each do |issue_type|
+ expect(migration).to have_scheduled_batched_migration(
+ table_name: :issues,
+ column_name: :id,
+ job_arguments: [issue_type_enum[issue_type], base_work_item_type_ids[issue_type_enum[issue_type]]],
+ interval: interval,
+ batch_size: described_class::BATCH_SIZE,
+ max_batch_size: described_class::MAX_BATCH_SIZE,
+ sub_batch_size: described_class::SUB_BATCH_SIZE,
+ batch_class_name: described_class::BATCH_CLASS_NAME
+ )
+ end
+ end
+ end
+ 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/reset_job_token_scope_enabled_again_spec.rb b/spec/migrations/reset_job_token_scope_enabled_again_spec.rb
index da6817f6f21..8f9e12852e1 100644
--- a/spec/migrations/reset_job_token_scope_enabled_again_spec.rb
+++ b/spec/migrations/reset_job_token_scope_enabled_again_spec.rb
@@ -9,8 +9,8 @@ RSpec.describe ResetJobTokenScopeEnabledAgain do
let(:projects) { table(:projects) }
let(:namespaces) { table(:namespaces) }
let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
- let(:project_1) { projects.create!(name: 'proj-1', path: 'gitlab-org', namespace_id: namespace.id)}
- let(:project_2) { projects.create!(name: 'proj-2', path: 'gitlab-org', namespace_id: namespace.id)}
+ let(:project_1) { projects.create!(name: 'proj-1', path: 'gitlab-org', namespace_id: namespace.id) }
+ let(:project_2) { projects.create!(name: 'proj-2', path: 'gitlab-org', namespace_id: namespace.id) }
before do
settings.create!(id: 1, project_id: project_1.id, job_token_scope_enabled: true)
diff --git a/spec/migrations/reset_job_token_scope_enabled_spec.rb b/spec/migrations/reset_job_token_scope_enabled_spec.rb
index 40dfe4de34b..fb7bd78c11f 100644
--- a/spec/migrations/reset_job_token_scope_enabled_spec.rb
+++ b/spec/migrations/reset_job_token_scope_enabled_spec.rb
@@ -9,8 +9,8 @@ RSpec.describe ResetJobTokenScopeEnabled do
let(:projects) { table(:projects) }
let(:namespaces) { table(:namespaces) }
let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
- let(:project_1) { projects.create!(name: 'proj-1', path: 'gitlab-org', namespace_id: namespace.id)}
- let(:project_2) { projects.create!(name: 'proj-2', path: 'gitlab-org', namespace_id: namespace.id)}
+ let(:project_1) { projects.create!(name: 'proj-1', path: 'gitlab-org', namespace_id: namespace.id) }
+ let(:project_2) { projects.create!(name: 'proj-2', path: 'gitlab-org', namespace_id: namespace.id) }
before do
settings.create!(id: 1, project_id: project_1.id, job_token_scope_enabled: true)
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 18dc001db16..c352f1f3cee 100644
--- a/spec/migrations/reset_severity_levels_to_new_default_spec.rb
+++ b/spec/migrations/reset_severity_levels_to_new_default_spec.rb
@@ -6,10 +6,10 @@ require_migration!
RSpec.describe ResetSeverityLevelsToNewDefault do
let(:approval_project_rules) { table(:approval_project_rules) }
- let(:projects) { table(:projects)}
- let(:namespaces) { table(:namespaces)}
- let(:namespace) { namespaces.create!(name: 'namespace', path: 'namespace')}
- let(:project) { projects.create!(name: 'project', path: 'project', namespace_id: namespace.id)}
+ let(:projects) { table(:projects) }
+ let(:namespaces) { table(:namespaces) }
+ let(:namespace) { namespaces.create!(name: 'namespace', path: 'namespace') }
+ let(:project) { projects.create!(name: 'project', path: 'project', namespace_id: namespace.id) }
let(:approval_project_rule) { approval_project_rules.create!(name: 'rule', project_id: project.id, severity_levels: severity_levels) }
context 'without having all severity levels selected' do
@@ -27,7 +27,7 @@ RSpec.describe ResetSeverityLevelsToNewDefault do
it 'changes severity_levels to the default value' do
expect(approval_project_rule.severity_levels).to eq(severity_levels)
- expect { migrate! }.to change {approval_project_rule.reload.severity_levels}.from(severity_levels).to(default_levels)
+ expect { migrate! }.to change { approval_project_rule.reload.severity_levels }.from(severity_levels).to(default_levels)
end
end
end
diff --git a/spec/migrations/schedule_backfill_cluster_agents_has_vulnerabilities_spec.rb b/spec/migrations/schedule_backfill_cluster_agents_has_vulnerabilities_spec.rb
new file mode 100644
index 00000000000..675cc332e69
--- /dev/null
+++ b/spec/migrations/schedule_backfill_cluster_agents_has_vulnerabilities_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe ScheduleBackfillClusterAgentsHasVulnerabilities do
+ let_it_be(:batched_migration) { described_class::MIGRATION }
+
+ it 'schedules background jobs for each batch of cluster agents' 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: :cluster_agents,
+ column_name: :id,
+ interval: described_class::DELAY_INTERVAL
+ )
+ }
+ end
+ end
+end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 16e1d8fbc4d..b5f153e7add 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -116,12 +116,20 @@ RSpec.describe ApplicationSetting do
it { is_expected.to validate_presence_of(:max_yaml_depth) }
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 '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)
.is_less_than(::Gitlab::Pages::MAX_SIZE / 1.megabyte)
end
+ it 'ensures max_pages_custom_domains_per_project is an integer greater than 0 (or equal to 0 to indicate unlimited/maximum)' do
+ is_expected
+ .to validate_numericality_of(:max_pages_custom_domains_per_project)
+ .only_integer
+ .is_greater_than_or_equal_to(0)
+ end
+
it { is_expected.to validate_presence_of(:jobs_per_stage_page_size) }
it { is_expected.to validate_numericality_of(:jobs_per_stage_page_size).only_integer.is_greater_than_or_equal_to(0) }
diff --git a/spec/models/ci/build_dependencies_spec.rb b/spec/models/ci/build_dependencies_spec.rb
index 737348765d9..1dd0386060d 100644
--- a/spec/models/ci/build_dependencies_spec.rb
+++ b/spec/models/ci/build_dependencies_spec.rb
@@ -13,10 +13,15 @@ RSpec.describe Ci::BuildDependencies do
status: 'success')
end
- let!(:build) { create(:ci_build, pipeline: pipeline, name: 'build', stage_idx: 0, stage: 'build') }
- let!(:rspec_test) { create(:ci_build, :success, pipeline: pipeline, name: 'rspec', stage_idx: 1, stage: 'test') }
- let!(:rubocop_test) { create(:ci_build, pipeline: pipeline, name: 'rubocop', stage_idx: 1, stage: 'test') }
- let!(:staging) { create(:ci_build, pipeline: pipeline, name: 'staging', stage_idx: 2, stage: 'deploy') }
+ let(:build_stage) { create(:ci_stage, name: 'build', pipeline: pipeline) }
+ let(:test_stage) { create(:ci_stage, name: 'test', pipeline: pipeline) }
+ let(:deploy_stage) { create(:ci_stage, name: 'deploy', pipeline: pipeline) }
+ let!(:build) { create(:ci_build, pipeline: pipeline, name: 'build', stage_idx: 0, ci_stage: build_stage) }
+ let!(:rubocop_test) { create(:ci_build, pipeline: pipeline, name: 'rubocop', stage_idx: 1, ci_stage: test_stage) }
+ let!(:staging) { create(:ci_build, pipeline: pipeline, name: 'staging', stage_idx: 2, ci_stage: deploy_stage) }
+ let!(:rspec_test) do
+ create(:ci_build, :success, pipeline: pipeline, name: 'rspec', stage_idx: 1, ci_stage: test_stage)
+ end
context 'for local dependencies' do
subject { described_class.new(job).all }
@@ -63,7 +68,7 @@ RSpec.describe Ci::BuildDependencies do
name: 'dag_job',
scheduling_type: :dag,
stage_idx: 2,
- stage: 'deploy'
+ ci_stage: deploy_stage
)
end
@@ -87,7 +92,7 @@ RSpec.describe Ci::BuildDependencies do
name: 'final',
scheduling_type: scheduling_type,
stage_idx: 3,
- stage: 'deploy',
+ ci_stage: deploy_stage,
options: { dependencies: dependencies }
)
end
@@ -218,12 +223,12 @@ RSpec.describe Ci::BuildDependencies do
cross_pipeline_limit.times do |index|
create(:ci_build, :success,
pipeline: parent_pipeline, name: "dependency-#{index}",
- stage_idx: 1, stage: 'build', user: user
+ stage_idx: 1, ci_stage: build_stage, user: user
)
create(:ci_build, :success,
pipeline: sibling_pipeline, name: "dependency-#{index}",
- stage_idx: 1, stage: 'build', user: user
+ stage_idx: 1, ci_stage: build_stage, user: user
)
end
end
@@ -355,7 +360,7 @@ RSpec.describe Ci::BuildDependencies do
describe '#all' do
let!(:job) do
- create(:ci_build, pipeline: pipeline, name: 'deploy', stage_idx: 3, stage: 'deploy')
+ create(:ci_build, pipeline: pipeline, name: 'deploy', stage_idx: 3, ci_stage: deploy_stage)
end
let(:dependencies) { described_class.new(job) }
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index b865688d370..7ee381b29ea 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -67,31 +67,6 @@ RSpec.describe Ci::Build do
create(:ci_build)
end
-
- context 'when the execute_build_hooks_inline flag is disabled' do
- before do
- stub_feature_flags(execute_build_hooks_inline: false)
- end
-
- it 'uses the old job hooks worker' do
- expect(::BuildHooksWorker).to receive(:perform_async).with(Ci::Build)
-
- create(:ci_build)
- end
- end
-
- context 'when the execute_build_hooks_inline flag is enabled for a project' do
- before do
- stub_feature_flags(execute_build_hooks_inline: project)
- end
-
- it 'executes hooks inline' do
- expect(::BuildHooksWorker).not_to receive(:perform_async)
- expect_next(described_class).to receive(:execute_hooks)
-
- create(:ci_build, project: project)
- end
- end
end
end
@@ -594,6 +569,51 @@ RSpec.describe Ci::Build do
end
end
+ describe '#prevent_rollback_deployment?' do
+ subject { build.prevent_rollback_deployment? }
+
+ let(:build) { create(:ci_build, :created, :with_deployment, project: project, environment: 'production') }
+
+ context 'when build has no environment' do
+ let(:build) { create(:ci_build, :created, project: project, environment: nil) }
+
+ it { expect(subject).to be_falsey }
+ end
+
+ context 'when project has forward deployment disabled' do
+ before do
+ project.ci_cd_settings.update!(forward_deployment_enabled: false)
+ end
+
+ it { expect(subject).to be_falsey }
+ end
+
+ context 'when deployment cannot rollback' do
+ before do
+ expect(build.deployment).to receive(:older_than_last_successful_deployment?).and_return(false)
+ end
+
+ it { expect(subject).to be_falsey }
+ end
+
+ context 'when prevent_outdated_deployment_jobs FF is disabled' do
+ before do
+ stub_feature_flags(prevent_outdated_deployment_jobs: false)
+ expect(build.deployment).not_to receive(:rollback?)
+ end
+
+ it { expect(subject).to be_falsey }
+ end
+
+ context 'when build can prevent rollback deployment' do
+ before do
+ expect(build.deployment).to receive(:older_than_last_successful_deployment?).and_return(true)
+ end
+
+ it { expect(subject).to be_truthy }
+ end
+ end
+
describe '#schedulable?' do
subject { build.schedulable? }
@@ -1250,70 +1270,6 @@ RSpec.describe Ci::Build do
end
end
- describe '#has_old_trace?' do
- subject { build.has_old_trace? }
-
- context 'when old trace exists' do
- before do
- build.update_column(:trace, 'old trace')
- end
-
- it { is_expected.to be_truthy }
- end
-
- context 'when old trace does not exist' do
- it { is_expected.to be_falsy }
- end
- end
-
- describe '#trace=' do
- it "expect to fail trace=" do
- expect { build.trace = "new" }.to raise_error(NotImplementedError)
- end
- end
-
- describe '#old_trace' do
- subject { build.old_trace }
-
- before do
- build.update_column(:trace, 'old trace')
- end
-
- it "expect to receive data from database" do
- is_expected.to eq('old trace')
- end
- end
-
- describe '#erase_old_trace!' do
- subject { build.erase_old_trace! }
-
- context 'when old trace exists' do
- before do
- build.update_column(:trace, 'old trace')
- end
-
- it "erases old trace" do
- subject
-
- expect(build.old_trace).to be_nil
- end
-
- it "executes UPDATE query" do
- recorded = ActiveRecord::QueryRecorder.new { subject }
-
- expect(recorded.log.count { |l| l.match?(/UPDATE.*ci_builds/) }).to eq(1)
- end
- end
-
- context 'when old trace does not exist' do
- it 'does not execute UPDATE query' do
- recorded = ActiveRecord::QueryRecorder.new { subject }
-
- expect(recorded.log.count { |l| l.match?(/UPDATE.*ci_builds/) }).to eq(0)
- end
- end
- end
-
describe '#hide_secrets' do
let(:metrics) { spy('metrics') }
let(:subject) { build.hide_secrets(data) }
@@ -1370,13 +1326,12 @@ RSpec.describe Ci::Build do
subject { build.send(event) }
- where(:ff_enabled, :state, :report_count, :trait) do
- true | :success! | 1 | :sast
- true | :cancel! | 1 | :sast
- true | :drop! | 2 | :multiple_report_artifacts
- true | :success! | 0 | :allowed_to_fail
- true | :skip! | 0 | :pending
- false | :success! | 0 | :sast
+ where(:state, :report_count, :trait) do
+ :success! | 1 | :sast
+ :cancel! | 1 | :sast
+ :drop! | 2 | :multiple_report_artifacts
+ :success! | 0 | :allowed_to_fail
+ :skip! | 0 | :pending
end
with_them do
@@ -1386,7 +1341,6 @@ RSpec.describe Ci::Build do
context "when transitioning to #{params[:state]}" do
before do
allow(Gitlab).to receive(:com?).and_return(true)
- stub_feature_flags(report_artifact_build_completed_metrics_on_build_completion: ff_enabled)
end
it 'increments build_completed_report_type metric' do
@@ -1645,32 +1599,6 @@ RSpec.describe Ci::Build do
end
end
- describe '#count_user_verification?' do
- subject { build.count_user_verification? }
-
- context 'when build is the verify action for the environment' do
- let(:build) do
- create(:ci_build,
- ref: 'master',
- environment: 'staging',
- options: { environment: { action: 'verify' } })
- end
-
- it { is_expected.to be_truthy }
- end
-
- context 'when build is not the verify action for the environment' do
- let(:build) do
- create(:ci_build,
- ref: 'master',
- environment: 'staging',
- options: { environment: { action: 'start' } })
- end
-
- it { is_expected.to be_falsey }
- end
- end
-
describe '#expanded_environment_name' do
subject { build.expanded_environment_name }
@@ -1873,12 +1801,6 @@ RSpec.describe Ci::Build do
context 'build is not erasable' do
let!(:build) { create(:ci_build) }
- describe '#erase' do
- subject { build.erase }
-
- it { is_expected.to be false }
- end
-
describe '#erasable?' do
subject { build.erasable? }
@@ -1887,71 +1809,9 @@ RSpec.describe Ci::Build do
end
context 'build is erasable' do
- context 'logging erase' do
- let!(:build) { create(:ci_build, :test_reports, :trace_artifact, :success, :artifacts) }
-
- it 'logs erased artifacts' do
- expect(Gitlab::Ci::Artifacts::Logger)
- .to receive(:log_deleted)
- .with(
- match_array(build.job_artifacts.to_a),
- 'Ci::Build#erase'
- )
-
- build.erase
- end
- end
-
- context 'when project is undergoing stats refresh' do
- let!(:build) { create(:ci_build, :test_reports, :trace_artifact, :success, :artifacts) }
-
- describe '#erase' do
- before do
- allow(build.project).to receive(:refreshing_build_artifacts_size?).and_return(true)
- end
-
- it 'logs and continues with deleting the artifacts' do
- expect(Gitlab::ProjectStatsRefreshConflictsLogger).to receive(:warn_artifact_deletion_during_stats_refresh).with(
- method: 'Ci::Build#erase',
- project_id: build.project.id
- )
-
- build.erase
-
- expect(build.job_artifacts.count).to eq(0)
- end
- end
- end
-
context 'new artifacts' do
let!(:build) { create(:ci_build, :test_reports, :trace_artifact, :success, :artifacts) }
- describe '#erase' do
- before do
- build.erase(erased_by: erased_by)
- end
-
- context 'erased by user' do
- let!(:erased_by) { create(:user, username: 'eraser') }
-
- include_examples 'erasable'
-
- it 'records user who erased a build' do
- expect(build.erased_by).to eq erased_by
- end
- end
-
- context 'erased by system' do
- let(:erased_by) { nil }
-
- include_examples 'erasable'
-
- it 'does not set user who erased a build' do
- expect(build.erased_by).to be_nil
- end
- end
- end
-
describe '#erasable?' do
subject { build.erasable? }
@@ -1969,76 +1829,12 @@ RSpec.describe Ci::Build do
context 'job has been erased' do
before do
- build.erase
+ build.update!(erased_at: 1.minute.ago)
end
it { is_expected.to be_truthy }
end
end
-
- context 'metadata and build trace are not available' do
- let!(:build) { create(:ci_build, :success, :artifacts) }
-
- before do
- build.erase_erasable_artifacts!
- end
-
- describe '#erase' do
- it 'does not raise error' do
- expect { build.erase }.not_to raise_error
- end
- end
- end
- end
- end
- end
-
- describe '#erase_erasable_artifacts!' do
- let!(:build) { create(:ci_build, :success) }
-
- subject { build.erase_erasable_artifacts! }
-
- before do
- Ci::JobArtifact.file_types.keys.each do |file_type|
- create(:ci_job_artifact, job: build, file_type: file_type, file_format: Ci::JobArtifact::TYPE_AND_FORMAT_PAIRS[file_type.to_sym])
- end
- end
-
- it "erases erasable artifacts and logs them" do
- expect(Gitlab::Ci::Artifacts::Logger)
- .to receive(:log_deleted)
- .with(
- match_array(build.job_artifacts.erasable.to_a),
- 'Ci::Build#erase_erasable_artifacts!'
- )
-
- subject
-
- expect(build.job_artifacts.erasable).to be_empty
- end
-
- it "keeps non erasable artifacts" do
- subject
-
- Ci::JobArtifact::NON_ERASABLE_FILE_TYPES.each do |file_type|
- expect(build.send("job_artifacts_#{file_type}")).not_to be_nil
- end
- end
-
- context 'when the project is undergoing stats refresh' do
- before do
- allow(build.project).to receive(:refreshing_build_artifacts_size?).and_return(true)
- end
-
- it 'logs and continues with deleting the artifacts' do
- expect(Gitlab::ProjectStatsRefreshConflictsLogger).to receive(:warn_artifact_deletion_during_stats_refresh).with(
- method: 'Ci::Build#erase_erasable_artifacts!',
- project_id: build.project.id
- )
-
- subject
-
- expect(build.job_artifacts.erasable).to be_empty
end
end
end
@@ -2689,17 +2485,17 @@ RSpec.describe Ci::Build do
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
+ '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
build.ref = ref
@@ -3634,17 +3430,6 @@ RSpec.describe Ci::Build do
it 'includes deploy token variables' do
is_expected.to include(*deploy_token_variables)
end
-
- context 'when the FF ci_variable_for_group_gitlab_deploy_token is disabled' do
- before do
- stub_feature_flags(ci_variable_for_group_gitlab_deploy_token: false)
- end
-
- it 'does not include deploy token variables' do
- expect(subject.find { |v| v[:key] == 'CI_DEPLOY_USER' }).to be_nil
- expect(subject.find { |v| v[:key] == 'CI_DEPLOY_PASSWORD' }).to be_nil
- end
- end
end
end
end
@@ -3921,18 +3706,6 @@ RSpec.describe Ci::Build do
build.enqueue
end
- context 'when the execute_build_hooks_inline flag is disabled' do
- before do
- stub_feature_flags(execute_build_hooks_inline: false)
- end
-
- it 'queues BuildHooksWorker' do
- expect(BuildHooksWorker).to receive(:perform_async).with(build)
-
- build.enqueue
- end
- end
-
it 'executes hooks' do
expect(build).to receive(:execute_hooks)
@@ -4048,10 +3821,6 @@ RSpec.describe Ci::Build do
context 'when artifacts of depended job has been erased' do
let!(:pre_stage_job) { create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0, erased_at: 1.minute.ago) }
- before do
- pre_stage_job.erase
- end
-
it { expect(job).not_to have_valid_build_dependencies }
end
end
@@ -4072,10 +3841,6 @@ RSpec.describe Ci::Build do
context 'when artifacts of depended job has been erased' do
let!(:pre_stage_job) { create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0, erased_at: 1.minute.ago) }
- before do
- pre_stage_job.erase
- end
-
it { expect(job).to have_valid_build_dependencies }
end
end
@@ -4405,9 +4170,7 @@ RSpec.describe Ci::Build do
end
describe '#collect_test_reports!' do
- subject { build.collect_test_reports!(test_reports) }
-
- let(:test_reports) { Gitlab::Ci::Reports::TestReport.new }
+ subject(:test_reports) { build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new) }
it { expect(test_reports.get_suite(build.name).total_count).to eq(0) }
@@ -4455,56 +4218,6 @@ RSpec.describe Ci::Build do
end
end
end
-
- context 'when build is part of parallel build' do
- let(:build_1) { create(:ci_build, name: 'build 1/2') }
- let(:test_report) { Gitlab::Ci::Reports::TestReport.new }
-
- before do
- build_1.collect_test_reports!(test_report)
- end
-
- it 'uses the group name for test suite name' do
- expect(test_report.test_suites.keys).to contain_exactly('build')
- end
-
- context 'when there are more than one parallel builds' do
- let(:build_2) { create(:ci_build, name: 'build 2/2') }
-
- before do
- build_2.collect_test_reports!(test_report)
- end
-
- it 'merges the test suite from parallel builds' do
- expect(test_report.test_suites.keys).to contain_exactly('build')
- end
- end
- end
-
- context 'when build is part of matrix build' do
- let(:test_report) { Gitlab::Ci::Reports::TestReport.new }
- let(:matrix_build_1) { create(:ci_build, :matrix) }
-
- before do
- matrix_build_1.collect_test_reports!(test_report)
- end
-
- it 'uses the job name for the test suite' do
- expect(test_report.test_suites.keys).to contain_exactly(matrix_build_1.name)
- end
-
- context 'when there are more than one matrix builds' do
- let(:matrix_build_2) { create(:ci_build, :matrix) }
-
- before do
- matrix_build_2.collect_test_reports!(test_report)
- end
-
- it 'keeps separate test suites' do
- expect(test_report.test_suites.keys).to match_array([matrix_build_1.name, matrix_build_2.name])
- end
- end
- end
end
describe '#collect_accessibility_reports!' do
@@ -5620,7 +5333,7 @@ RSpec.describe Ci::Build do
end
end
- describe '#runner_features' do
+ describe '#runtime_runner_features' do
subject do
build.save!
build.cancel_gracefully?
@@ -5701,4 +5414,28 @@ RSpec.describe Ci::Build do
end
end
end
+
+ describe '#test_suite_name' do
+ let(:build) { create(:ci_build, name: 'test') }
+
+ it 'uses the group name for test suite name' do
+ expect(build.test_suite_name).to eq('test')
+ end
+
+ context 'when build is part of parallel build' do
+ let(:build) { create(:ci_build, name: 'build 1/2') }
+
+ it 'uses the group name for test suite name' do
+ expect(build.test_suite_name).to eq('build')
+ end
+ end
+
+ context 'when build is part of matrix build' do
+ let!(:matrix_build) { create(:ci_build, :matrix) }
+
+ it 'uses the job name for the test suite' do
+ expect(matrix_build.test_suite_name).to eq(matrix_build.name)
+ end
+ end
+ end
end
diff --git a/spec/models/ci/freeze_period_status_spec.rb b/spec/models/ci/freeze_period_status_spec.rb
index f51381f7a5f..ecbb7af64f7 100644
--- a/spec/models/ci/freeze_period_status_spec.rb
+++ b/spec/models/ci/freeze_period_status_spec.rb
@@ -59,4 +59,13 @@ RSpec.describe Ci::FreezePeriodStatus do
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 b996bf84529..098f8bd4514 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -8,6 +8,8 @@ RSpec.describe Ci::JobArtifact do
describe "Associations" do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:job) }
+ it { is_expected.to validate_presence_of(:job) }
+ it { is_expected.to validate_presence_of(:partition_id) }
end
it { is_expected.to respond_to(:file) }
@@ -48,82 +50,86 @@ RSpec.describe Ci::JobArtifact do
end
end
- describe '.test_reports' do
- subject { described_class.test_reports }
+ describe '.of_report_type' do
+ subject { described_class.of_report_type(report_type) }
- context 'when there is a test report' do
- let!(:artifact) { create(:ci_job_artifact, :junit) }
+ describe 'test_reports' do
+ let(:report_type) { :test }
- it { is_expected.to eq([artifact]) }
- end
+ context 'when there is a test report' do
+ let!(:artifact) { create(:ci_job_artifact, :junit) }
- context 'when there are no test reports' do
- let!(:artifact) { create(:ci_job_artifact, :archive) }
+ it { is_expected.to eq([artifact]) }
+ end
- it { is_expected.to be_empty }
+ context 'when there are no test reports' do
+ let!(:artifact) { create(:ci_job_artifact, :archive) }
+
+ it { is_expected.to be_empty }
+ end
end
- end
- describe '.accessibility_reports' do
- subject { described_class.accessibility_reports }
+ describe 'accessibility_reports' do
+ let(:report_type) { :accessibility }
- context 'when there is an accessibility report' do
- let(:artifact) { create(:ci_job_artifact, :accessibility) }
+ context 'when there is an accessibility report' do
+ let(:artifact) { create(:ci_job_artifact, :accessibility) }
- it { is_expected.to eq([artifact]) }
- end
+ it { is_expected.to eq([artifact]) }
+ end
- context 'when there are no accessibility report' do
- let(:artifact) { create(:ci_job_artifact, :archive) }
+ context 'when there are no accessibility report' do
+ let(:artifact) { create(:ci_job_artifact, :archive) }
- it { is_expected.to be_empty }
+ it { is_expected.to be_empty }
+ end
end
- end
- describe '.coverage_reports' do
- subject { described_class.coverage_reports }
+ describe 'coverage_reports' do
+ let(:report_type) { :coverage }
- context 'when there is a coverage report' do
- let!(:artifact) { create(:ci_job_artifact, :cobertura) }
+ context 'when there is a coverage report' do
+ let!(:artifact) { create(:ci_job_artifact, :cobertura) }
- it { is_expected.to eq([artifact]) }
- end
+ it { is_expected.to eq([artifact]) }
+ end
- context 'when there are no coverage reports' do
- let!(:artifact) { create(:ci_job_artifact, :archive) }
+ context 'when there are no coverage reports' do
+ let!(:artifact) { create(:ci_job_artifact, :archive) }
- it { is_expected.to be_empty }
+ it { is_expected.to be_empty }
+ end
end
- end
- describe '.codequality_reports' do
- subject { described_class.codequality_reports }
+ describe 'codequality_reports' do
+ let(:report_type) { :codequality }
- context 'when there is a codequality report' do
- let!(:artifact) { create(:ci_job_artifact, :codequality) }
+ context 'when there is a codequality report' do
+ let!(:artifact) { create(:ci_job_artifact, :codequality) }
- it { is_expected.to eq([artifact]) }
- end
+ it { is_expected.to eq([artifact]) }
+ end
- context 'when there are no codequality reports' do
- let!(:artifact) { create(:ci_job_artifact, :archive) }
+ context 'when there are no codequality reports' do
+ let!(:artifact) { create(:ci_job_artifact, :archive) }
- it { is_expected.to be_empty }
+ it { is_expected.to be_empty }
+ end
end
- end
- describe '.terraform_reports' do
- context 'when there is a terraform report' do
- it 'return the job artifact' do
- artifact = create(:ci_job_artifact, :terraform)
+ describe 'terraform_reports' do
+ let(:report_type) { :terraform }
+
+ context 'when there is a terraform report' do
+ let!(:artifact) { create(:ci_job_artifact, :terraform) }
- expect(described_class.terraform_reports).to eq([artifact])
+ it { is_expected.to eq([artifact]) }
end
- end
- context 'when there are no terraform reports' do
- it 'return the an empty array' do
- expect(described_class.terraform_reports).to eq([])
+ context 'when there are no terraform reports' do
+ let!(:artifact) { create(:ci_job_artifact, :archive) }
+
+ it { is_expected.to be_empty }
end
end
end
@@ -135,7 +141,7 @@ RSpec.describe Ci::JobArtifact do
context 'when given an unrecognized report type' do
it 'raises error' do
- expect { described_class.file_types_for_report(:blah) }.to raise_error(KeyError, /blah/)
+ expect { described_class.file_types_for_report(:blah) }.to raise_error(ArgumentError, "Unrecognized report type: blah")
end
end
end
@@ -146,8 +152,8 @@ RSpec.describe Ci::JobArtifact do
subject { Ci::JobArtifact.associated_file_types_for(file_type) }
where(:file_type, :result) do
- 'codequality' | %w(codequality)
- 'quality' | nil
+ 'codequality' | %w(codequality)
+ 'quality' | nil
end
with_them do
@@ -754,4 +760,26 @@ RSpec.describe Ci::JobArtifact do
let!(:model) { create(:ci_job_artifact, project: parent) }
end
end
+
+ describe 'partitioning' do
+ let(:job) { build(:ci_build, partition_id: 123) }
+ let(:artifact) { build(:ci_job_artifact, job: job, partition_id: nil) }
+
+ it 'copies the partition_id from job' do
+ expect { artifact.valid? }.to change(artifact, :partition_id).from(nil).to(123)
+ end
+
+ context 'when the job is missing' do
+ let(:artifact) do
+ build(:ci_job_artifact,
+ project: build_stubbed(:project),
+ job: nil,
+ partition_id: nil)
+ end
+
+ it 'does not change the partition_id value' do
+ expect { artifact.valid? }.not_to change(artifact, :partition_id)
+ end
+ end
+ end
end
diff --git a/spec/models/ci/namespace_mirror_spec.rb b/spec/models/ci/namespace_mirror_spec.rb
index 3e77c349ccb..29447cbc89d 100644
--- a/spec/models/ci/namespace_mirror_spec.rb
+++ b/spec/models/ci/namespace_mirror_spec.rb
@@ -16,7 +16,9 @@ RSpec.describe Ci::NamespaceMirror do
expect(group1.reload.ci_namespace_mirror).to have_attributes(traversal_ids: [group1.id])
expect(group2.reload.ci_namespace_mirror).to have_attributes(traversal_ids: [group1.id, group2.id])
expect(group3.reload.ci_namespace_mirror).to have_attributes(traversal_ids: [group1.id, group2.id, group3.id])
- expect(group4.reload.ci_namespace_mirror).to have_attributes(traversal_ids: [group1.id, group2.id, group3.id, group4.id])
+ expect(group4.reload.ci_namespace_mirror).to have_attributes(
+ traversal_ids: [group1.id, group2.id, group3.id, group4.id]
+ )
end
context 'scopes' do
@@ -103,6 +105,8 @@ RSpec.describe Ci::NamespaceMirror do
describe '.sync!' do
subject(:sync) { described_class.sync!(Namespaces::SyncEvent.last) }
+ let(:expected_traversal_ids) { [group1.id, group2.id, group3.id] }
+
context 'when namespace mirror does not exist in the first place' do
let(:namespace) { group3 }
@@ -114,7 +118,7 @@ RSpec.describe Ci::NamespaceMirror do
it 'creates the mirror' do
expect { sync }.to change { described_class.count }.from(3).to(4)
- expect(namespace.reload.ci_namespace_mirror).to have_attributes(traversal_ids: [group1.id, group2.id, group3.id])
+ expect(namespace.reload.ci_namespace_mirror).to have_attributes(traversal_ids: expected_traversal_ids)
end
end
@@ -128,36 +132,8 @@ RSpec.describe Ci::NamespaceMirror do
it 'updates the mirror' do
expect { sync }.not_to change { described_class.count }
- expect(namespace.reload.ci_namespace_mirror).to have_attributes(traversal_ids: [group1.id, group2.id, group3.id])
- end
- end
-
- shared_context 'changing the middle namespace' do
- let(:namespace) { group2 }
-
- before do
- group2.update!(parent: nil) # creates a sync event
- end
-
- it 'updates traversal_ids for the base and descendants' do
- expect { sync }.not_to change { described_class.count }
-
- expect(group1.reload.ci_namespace_mirror).to have_attributes(traversal_ids: [group1.id])
- expect(group2.reload.ci_namespace_mirror).to have_attributes(traversal_ids: [group2.id])
- expect(group3.reload.ci_namespace_mirror).to have_attributes(traversal_ids: [group2.id, group3.id])
- expect(group4.reload.ci_namespace_mirror).to have_attributes(traversal_ids: [group2.id, group3.id, group4.id])
- end
- end
-
- it_behaves_like 'changing the middle namespace'
-
- context 'when the FFs use_traversal_ids and use_traversal_ids_for_ancestors are disabled' do
- before do
- stub_feature_flags(use_traversal_ids: false,
- use_traversal_ids_for_ancestors: false)
+ expect(namespace.reload.ci_namespace_mirror).to have_attributes(traversal_ids: expected_traversal_ids)
end
-
- it_behaves_like 'changing the middle namespace'
end
end
end
diff --git a/spec/models/ci/pipeline_artifact_spec.rb b/spec/models/ci/pipeline_artifact_spec.rb
index b051f646bd4..3038cdc944b 100644
--- a/spec/models/ci/pipeline_artifact_spec.rb
+++ b/spec/models/ci/pipeline_artifact_spec.rb
@@ -227,6 +227,19 @@ RSpec.describe Ci::PipelineArtifact, type: :model do
expect(subject.size).to eq(size)
expect(subject.file_format).to eq(Ci::PipelineArtifact::REPORT_TYPES[file_type].to_s)
expect(subject.expire_at).to eq(Ci::PipelineArtifact::EXPIRATION_DATE.from_now)
+ expect(subject.locked).to eq('unknown')
+ end
+
+ it "creates a new pipeline artifact with pipeline's locked state" do
+ artifact = Ci::PipelineArtifact.create_or_replace_for_pipeline!(
+ pipeline: pipeline,
+ file_type: file_type,
+ file: file,
+ size: size,
+ locked: pipeline.locked
+ )
+
+ expect(artifact.locked).to eq(pipeline.locked)
end
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 0c28c99c113..ec03030a4b8 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -466,6 +466,48 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ describe '.jobs_count_in_alive_pipelines' do
+ before do
+ ::Ci::HasStatus::ALIVE_STATUSES.each do |status|
+ alive_pipeline = create(:ci_pipeline, status: status, project: project)
+ create(:ci_build, pipeline: alive_pipeline)
+ create(:ci_bridge, pipeline: alive_pipeline)
+ end
+
+ completed_pipeline = create(:ci_pipeline, :success, project: project)
+ create(:ci_build, pipeline: completed_pipeline)
+
+ old_pipeline = create(:ci_pipeline, :running, project: project, created_at: 2.days.ago)
+ create(:ci_build, pipeline: old_pipeline)
+ end
+
+ it 'includes all jobs in alive pipelines created in the last 24 hours' do
+ expect(described_class.jobs_count_in_alive_pipelines)
+ .to eq(::Ci::HasStatus::ALIVE_STATUSES.count * 2)
+ end
+ end
+
+ describe '.builds_count_in_alive_pipelines' do
+ before do
+ ::Ci::HasStatus::ALIVE_STATUSES.each do |status|
+ alive_pipeline = create(:ci_pipeline, status: status, project: project)
+ create(:ci_build, pipeline: alive_pipeline)
+ create(:ci_bridge, pipeline: alive_pipeline)
+ end
+
+ completed_pipeline = create(:ci_pipeline, :success, project: project)
+ create(:ci_build, pipeline: completed_pipeline)
+
+ old_pipeline = create(:ci_pipeline, :running, project: project, created_at: 2.days.ago)
+ create(:ci_build, pipeline: old_pipeline)
+ end
+
+ it 'includes all builds in alive pipelines created in the last 24 hours' do
+ expect(described_class.builds_count_in_alive_pipelines)
+ .to eq(::Ci::HasStatus::ALIVE_STATUSES.count)
+ end
+ end
+
describe '#merge_request?' do
let_it_be(:merge_request) { create(:merge_request) }
let_it_be_with_reload(:pipeline) do
@@ -686,7 +728,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
describe '.with_reports' do
context 'when pipeline has a test report' do
- subject { described_class.with_reports(Ci::JobArtifact.test_reports) }
+ subject { described_class.with_reports(Ci::JobArtifact.of_report_type(:test)) }
let!(:pipeline_with_report) { create(:ci_pipeline, :with_test_reports) }
@@ -696,7 +738,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when pipeline has a coverage report' do
- subject { described_class.with_reports(Ci::JobArtifact.coverage_reports) }
+ subject { described_class.with_reports(Ci::JobArtifact.of_report_type(:coverage)) }
let!(:pipeline_with_report) { create(:ci_pipeline, :with_coverage_reports) }
@@ -706,7 +748,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when pipeline has an accessibility report' do
- subject { described_class.with_reports(Ci::JobArtifact.accessibility_reports) }
+ subject { described_class.with_reports(Ci::JobArtifact.of_report_type(:accessibility)) }
let(:pipeline_with_report) { create(:ci_pipeline, :with_accessibility_reports) }
@@ -716,7 +758,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
context 'when pipeline has a codequality report' do
- subject { described_class.with_reports(Ci::JobArtifact.codequality_reports) }
+ subject { described_class.with_reports(Ci::JobArtifact.of_report_type(:codequality)) }
let(:pipeline_with_report) { create(:ci_pipeline, :with_codequality_reports) }
@@ -729,14 +771,14 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
it 'selects the pipeline' do
pipeline_with_report = create(:ci_pipeline, :with_terraform_reports)
- expect(described_class.with_reports(Ci::JobArtifact.terraform_reports)).to eq(
+ expect(described_class.with_reports(Ci::JobArtifact.of_report_type(:terraform))).to eq(
[pipeline_with_report]
)
end
end
context 'when pipeline does not have metrics reports' do
- subject { described_class.with_reports(Ci::JobArtifact.test_reports) }
+ subject { described_class.with_reports(Ci::JobArtifact.of_report_type(:test)) }
let!(:pipeline_without_report) { create(:ci_empty_pipeline) }
@@ -1375,32 +1417,11 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
let(:pipeline) { build(:ci_empty_pipeline, :created) }
before do
- create(:ci_stage, project: project,
- pipeline: pipeline,
- position: 4,
- name: 'deploy')
-
- create(:ci_build, project: project,
- pipeline: pipeline,
- stage: 'test',
- stage_idx: 3,
- name: 'test')
-
- create(:ci_build, project: project,
- pipeline: pipeline,
- stage: 'build',
- stage_idx: 2,
- name: 'build')
-
- create(:ci_stage, project: project,
- pipeline: pipeline,
- position: 1,
- name: 'sanity')
-
- create(:ci_stage, project: project,
- pipeline: pipeline,
- position: 5,
- name: 'cleanup')
+ create(:ci_stage, project: project, pipeline: pipeline, position: 4, name: 'deploy')
+ create(:ci_build, project: project, pipeline: pipeline, stage: 'test', stage_idx: 3, name: 'test')
+ create(:ci_build, project: project, pipeline: pipeline, stage: 'build', stage_idx: 2, name: 'build')
+ create(:ci_stage, project: project, pipeline: pipeline, position: 1, name: 'sanity')
+ create(:ci_stage, project: project, pipeline: pipeline, position: 5, name: 'cleanup')
end
subject { pipeline.stages }
@@ -1577,6 +1598,42 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ describe 'track artifact report' do
+ let(:pipeline) { create(:ci_pipeline, :running, :with_test_reports, status: :running, user: create(:user)) }
+
+ context 'when transitioning to completed status' do
+ %i[drop! skip! succeed! cancel!].each do |command|
+ it "performs worker on transition to #{command}" do
+ expect(Ci::JobArtifacts::TrackArtifactReportWorker).to receive(:perform_async).with(pipeline.id)
+ pipeline.send(command)
+ end
+ end
+ end
+
+ context 'when pipeline retried from failed to success', :clean_gitlab_redis_shared_state do
+ let(:test_event_name) { 'i_testing_test_report_uploaded' }
+ let(:start_time) { 1.week.ago }
+ let(:end_time) { 1.week.from_now }
+
+ it 'counts only one report' do
+ expect(Ci::JobArtifacts::TrackArtifactReportWorker).to receive(:perform_async).with(pipeline.id).twice.and_call_original
+
+ Sidekiq::Testing.inline! do
+ pipeline.drop!
+ pipeline.run!
+ pipeline.succeed!
+ end
+
+ unique_pipeline_pass = Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(
+ event_names: test_event_name,
+ start_date: start_time,
+ end_date: end_time
+ )
+ expect(unique_pipeline_pass).to eq(1)
+ end
+ end
+ end
+
describe 'merge request metrics' do
let(:pipeline) { create(:ci_empty_pipeline, status: from_status) }
@@ -1649,9 +1706,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'when auto merge is enabled' do
let_it_be_with_reload(:merge_request) { create(:merge_request, :merge_when_pipeline_succeeds) }
let_it_be_with_reload(:pipeline) do
- create(:ci_pipeline, :running, project: merge_request.source_project,
- ref: merge_request.source_branch,
- sha: merge_request.diff_head_sha)
+ create(:ci_pipeline, :running,
+ project: merge_request.source_project, ref: merge_request.source_branch, sha: merge_request.diff_head_sha)
end
before_all do
@@ -3615,8 +3671,8 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
- describe '#environments_in_self_and_descendants' do
- subject { pipeline.environments_in_self_and_descendants }
+ describe '#environments_in_self_and_project_descendants' do
+ subject { pipeline.environments_in_self_and_project_descendants }
context 'when pipeline is not child nor parent' do
let_it_be(:pipeline) { create(:ci_pipeline, :created) }
@@ -4022,13 +4078,13 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
- describe '#self_and_descendants_complete?' do
+ describe '#self_and_project_descendants_complete?' do
let_it_be(:pipeline) { create(:ci_pipeline, :success) }
let_it_be(:child_pipeline) { create(:ci_pipeline, :success, child_of: pipeline) }
let_it_be_with_reload(:grandchild_pipeline) { create(:ci_pipeline, :success, child_of: child_pipeline) }
context 'when all pipelines in the hierarchy is complete' do
- it { expect(pipeline.self_and_descendants_complete?).to be(true) }
+ it { expect(pipeline.self_and_project_descendants_complete?).to be(true) }
end
context 'when a pipeline in the hierarchy is not complete' do
@@ -4036,12 +4092,12 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
grandchild_pipeline.update!(status: :running)
end
- it { expect(pipeline.self_and_descendants_complete?).to be(false) }
+ it { expect(pipeline.self_and_project_descendants_complete?).to be(false) }
end
end
- describe '#builds_in_self_and_descendants' do
- subject(:builds) { pipeline.builds_in_self_and_descendants }
+ describe '#builds_in_self_and_project_descendants' do
+ subject(:builds) { pipeline.builds_in_self_and_project_descendants }
let(:pipeline) { create(:ci_pipeline) }
let!(:build) { create(:ci_build, pipeline: pipeline) }
@@ -4073,7 +4129,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
- describe '#build_with_artifacts_in_self_and_descendants' do
+ describe '#build_with_artifacts_in_self_and_project_descendants' do
let_it_be(:pipeline) { create(:ci_pipeline) }
let!(:build) { create(:ci_build, name: 'test', pipeline: pipeline) }
@@ -4081,14 +4137,14 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
let!(:child_build) { create(:ci_build, :artifacts, name: 'test', pipeline: child_pipeline) }
it 'returns the build with a given name, having artifacts' do
- expect(pipeline.build_with_artifacts_in_self_and_descendants('test')).to eq(child_build)
+ expect(pipeline.build_with_artifacts_in_self_and_project_descendants('test')).to eq(child_build)
end
context 'when same job name is present in both parent and child pipeline' do
let!(:build) { create(:ci_build, :artifacts, name: 'test', pipeline: pipeline) }
it 'returns the job in the parent pipeline' do
- expect(pipeline.build_with_artifacts_in_self_and_descendants('test')).to eq(build)
+ expect(pipeline.build_with_artifacts_in_self_and_project_descendants('test')).to eq(build)
end
end
end
@@ -4158,7 +4214,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
test_build = create(:ci_build, :test_reports, pipeline: pipeline)
create(:ci_build, :coverage_reports, pipeline: pipeline)
- expect(pipeline.latest_report_builds(Ci::JobArtifact.test_reports)).to contain_exactly(test_build)
+ expect(pipeline.latest_report_builds(Ci::JobArtifact.of_report_type(:test))).to contain_exactly(test_build)
end
it 'only returns not retried builds' do
@@ -4169,7 +4225,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
- describe '#latest_report_builds_in_self_and_descendants' do
+ describe '#latest_report_builds_in_self_and_project_descendants' do
let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
let_it_be(:child_pipeline) { create(:ci_pipeline, child_of: pipeline) }
let_it_be(:grandchild_pipeline) { create(:ci_pipeline, child_of: child_pipeline) }
@@ -4179,26 +4235,57 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
child_build = create(:ci_build, :coverage_reports, pipeline: child_pipeline)
grandchild_build = create(:ci_build, :codequality_reports, pipeline: grandchild_pipeline)
- expect(pipeline.latest_report_builds_in_self_and_descendants).to contain_exactly(parent_build, child_build, grandchild_build)
+ expect(pipeline.latest_report_builds_in_self_and_project_descendants).to contain_exactly(parent_build, child_build, grandchild_build)
end
it 'filters builds by scope' do
create(:ci_build, :test_reports, pipeline: pipeline)
grandchild_build = create(:ci_build, :codequality_reports, pipeline: grandchild_pipeline)
- expect(pipeline.latest_report_builds_in_self_and_descendants(Ci::JobArtifact.codequality_reports)).to contain_exactly(grandchild_build)
+ expect(pipeline.latest_report_builds_in_self_and_project_descendants(Ci::JobArtifact.of_report_type(:codequality))).to contain_exactly(grandchild_build)
end
it 'only returns builds that are not retried' do
create(:ci_build, :codequality_reports, :retried, pipeline: grandchild_pipeline)
grandchild_build = create(:ci_build, :codequality_reports, pipeline: grandchild_pipeline)
- expect(pipeline.latest_report_builds_in_self_and_descendants).to contain_exactly(grandchild_build)
+ expect(pipeline.latest_report_builds_in_self_and_project_descendants).to contain_exactly(grandchild_build)
end
end
describe '#has_reports?' do
- subject { pipeline.has_reports?(Ci::JobArtifact.test_reports) }
+ subject { pipeline.has_reports?(Ci::JobArtifact.of_report_type(:test)) }
+
+ let(:pipeline) { create(:ci_pipeline, :running) }
+
+ context 'when pipeline has builds with test reports' do
+ before do
+ create(:ci_build, :test_reports, pipeline: pipeline)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when pipeline does not have builds with test reports' do
+ before do
+ create(:ci_build, :artifacts, pipeline: pipeline)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when retried build has test reports but latest one has none' do
+ before do
+ create(:ci_build, :retried, :test_reports, pipeline: pipeline)
+ create(:ci_build, :artifacts, pipeline: pipeline)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#complete_and_has_reports?' do
+ subject { pipeline.complete_and_has_reports?(Ci::JobArtifact.of_report_type(:test)) }
context 'when pipeline has builds with test reports' do
before do
@@ -4370,6 +4457,10 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
create(:ci_job_artifact, :junit_with_ant, job: build_java)
end
+ it 'has a test suite for each job' do
+ expect(subject.test_suites.keys).to contain_exactly('rspec', 'java')
+ end
+
it 'returns test reports with collected data' do
expect(subject.total_count).to be(7)
expect(subject.success_count).to be(5)
@@ -4388,6 +4479,34 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
+ context 'when the pipeline has parallel builds with test reports' do
+ let!(:parallel_build_1) { create(:ci_build, name: 'build 1/2', pipeline: pipeline) }
+ let!(:parallel_build_2) { create(:ci_build, name: 'build 2/2', pipeline: pipeline) }
+
+ before do
+ create(:ci_job_artifact, :junit, job: parallel_build_1)
+ create(:ci_job_artifact, :junit, job: parallel_build_2)
+ end
+
+ it 'merges the test suite from parallel builds' do
+ expect(subject.test_suites.keys).to contain_exactly('build')
+ end
+ end
+
+ context 'the pipeline has matrix builds with test reports' do
+ let!(:matrix_build_1) { create(:ci_build, :matrix, pipeline: pipeline) }
+ let!(:matrix_build_2) { create(:ci_build, :matrix, pipeline: pipeline) }
+
+ before do
+ create(:ci_job_artifact, :junit, job: matrix_build_1)
+ create(:ci_job_artifact, :junit, job: matrix_build_2)
+ end
+
+ it 'keeps separate test suites for each matrix build' do
+ expect(subject.test_suites.keys).to contain_exactly(matrix_build_1.name, matrix_build_2.name)
+ end
+ end
+
context 'when pipeline does not have any builds with test reports' do
it 'returns empty test reports' do
expect(subject.total_count).to be(0)
@@ -4472,10 +4591,20 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
let_it_be(:pipeline) { create(:ci_pipeline) }
context 'when the scheduling type is `dag`' do
- it 'returns true' do
- create(:ci_build, pipeline: pipeline, scheduling_type: :dag)
+ context 'when the processable is a bridge' do
+ it 'returns true' do
+ create(:ci_bridge, pipeline: pipeline, scheduling_type: :dag)
+
+ expect(pipeline.uses_needs?).to eq(true)
+ end
+ end
- expect(pipeline.uses_needs?).to eq(true)
+ context 'when the processable is a build' do
+ it 'returns true' do
+ create(:ci_build, pipeline: pipeline, scheduling_type: :dag)
+
+ expect(pipeline.uses_needs?).to eq(true)
+ end
end
end
@@ -4911,8 +5040,58 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
- describe '#self_and_ancestors' do
- subject(:self_and_ancestors) { pipeline.self_and_ancestors }
+ describe '#self_and_downstreams' do
+ subject(:self_and_downstreams) { pipeline.self_and_downstreams }
+
+ let(:pipeline) { create(:ci_pipeline, :created) }
+
+ context 'when pipeline is not child nor parent' do
+ it 'returns just the pipeline itself' do
+ expect(self_and_downstreams).to contain_exactly(pipeline)
+ end
+ end
+
+ context 'when pipeline is child' do
+ let(:parent) { create(:ci_pipeline) }
+ let!(:pipeline) { create(:ci_pipeline, child_of: parent) }
+
+ it 'returns self and no ancestors' do
+ expect(self_and_downstreams).to contain_exactly(pipeline)
+ end
+ end
+
+ context 'when pipeline is parent' do
+ let(:child) { create(:ci_pipeline, child_of: pipeline) }
+
+ it 'returns self and child' do
+ expect(self_and_downstreams).to contain_exactly(pipeline, child)
+ end
+ end
+
+ context 'when pipeline is a grandparent pipeline' do
+ let(:child) { create(:ci_pipeline, child_of: pipeline) }
+ let(:grandchild) { create(:ci_pipeline, child_of: child) }
+
+ it 'returns self, child, and grandchild' do
+ expect(self_and_downstreams).to contain_exactly(pipeline, child, grandchild)
+ end
+ end
+
+ context 'when pipeline is a triggered pipeline from a different project' do
+ let(:downstream) { create(:ci_pipeline) }
+
+ before do
+ create_source_pipeline(pipeline, downstream)
+ end
+
+ it 'returns self and cross-project downstream' do
+ expect(self_and_downstreams).to contain_exactly(pipeline, downstream)
+ end
+ end
+ end
+
+ describe '#self_and_project_ancestors' do
+ subject(:self_and_project_ancestors) { pipeline.self_and_project_ancestors }
context 'when pipeline is child' do
let(:pipeline) { create(:ci_pipeline, :created) }
@@ -4925,7 +5104,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
it 'returns parent and self' do
- expect(self_and_ancestors).to contain_exactly(parent, pipeline)
+ expect(self_and_project_ancestors).to contain_exactly(parent, pipeline)
end
end
@@ -4939,7 +5118,20 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
it 'returns only self' do
- expect(self_and_ancestors).to contain_exactly(pipeline)
+ expect(self_and_project_ancestors).to contain_exactly(pipeline)
+ end
+ end
+ end
+
+ describe '#complete_hierarchy_count' do
+ context 'with a combination of ancestor, descendant and sibling pipelines' do
+ let!(:pipeline) { create(:ci_pipeline) }
+ let!(:child_pipeline) { create(:ci_pipeline, child_of: pipeline) }
+ let!(:sibling_pipeline) { create(:ci_pipeline, child_of: pipeline) }
+ let!(:grandchild_pipeline) { create(:ci_pipeline, child_of: child_pipeline) }
+
+ it 'counts the whole tree' do
+ expect(sibling_pipeline.complete_hierarchy_count).to eq(4)
end
end
end
@@ -5165,16 +5357,10 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
it { is_expected.to be_falsey }
end
- context 'when the pipeline is still running' do
- let(:pipeline) { create(:ci_pipeline, :running) }
+ context 'when the pipeline is still running and with test reports' do
+ let(:pipeline) { create(:ci_pipeline, :running, :with_test_reports) }
- it { is_expected.to be_falsey }
- end
-
- context 'when the pipeline is completed without test reports' do
- let(:pipeline) { create(:ci_pipeline, :success) }
-
- it { is_expected.to be_falsey }
+ it { is_expected.to be_truthy }
end
end
@@ -5298,4 +5484,36 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
end
+
+ describe 'partitioning' do
+ let(:pipeline) { build(:ci_pipeline) }
+
+ before do
+ allow(described_class).to receive(:current_partition_value) { 123 }
+ end
+
+ it 'sets partition_id to the current partition value' do
+ expect { pipeline.valid? }.to change(pipeline, :partition_id).to(123)
+ end
+
+ context 'when it is already set' do
+ let(:pipeline) { build(:ci_pipeline, partition_id: 125) }
+
+ it 'does not change the partition_id value' do
+ expect { pipeline.valid? }.not_to change(pipeline, :partition_id)
+ end
+ end
+
+ context 'without current partition value' do
+ before do
+ allow(described_class).to receive(:current_partition_value) {}
+ end
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { pipeline.valid? }.not_to change(pipeline, :partition_id)
+ end
+ end
+ end
end
diff --git a/spec/models/ci/pipeline_variable_spec.rb b/spec/models/ci/pipeline_variable_spec.rb
index 4e8d49585d0..fdcec0e96af 100644
--- a/spec/models/ci/pipeline_variable_spec.rb
+++ b/spec/models/ci/pipeline_variable_spec.rb
@@ -17,4 +17,25 @@ RSpec.describe Ci::PipelineVariable do
it { is_expected.to be_a(Hash) }
it { is_expected.to eq({ key: 'foo', value: 'bar' }) }
end
+
+ describe 'partitioning' do
+ context 'with pipeline' do
+ let(:pipeline) { build(:ci_pipeline, partition_id: 123) }
+ let(:variable) { build(:ci_pipeline_variable, pipeline: pipeline, partition_id: nil) }
+
+ it 'copies the partition_id from pipeline' do
+ expect { variable.valid? }.to change(variable, :partition_id).from(nil).to(123)
+ end
+ end
+
+ context 'without pipeline' do
+ subject(:variable) { build(:ci_pipeline_variable, pipeline: nil, partition_id: nil) }
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { variable.valid? }.not_to change(variable, :partition_id)
+ end
+ end
+ end
end
diff --git a/spec/models/ci/processable_spec.rb b/spec/models/ci/processable_spec.rb
index 127a1417d9e..61e2864a518 100644
--- a/spec/models/ci/processable_spec.rb
+++ b/spec/models/ci/processable_spec.rb
@@ -30,10 +30,8 @@ RSpec.describe Ci::Processable do
let_it_be(:downstream_project) { create(:project, :repository) }
let_it_be_with_refind(:processable) do
- create(
- :ci_bridge, :success, pipeline: pipeline, downstream: downstream_project,
- description: 'a trigger job', stage_id: stage.id
- )
+ create(:ci_bridge, :success,
+ pipeline: pipeline, downstream: downstream_project, description: 'a trigger job', stage_id: stage.id)
end
let(:clone_accessors) { ::Ci::Bridge.clone_accessors }
@@ -57,8 +55,7 @@ RSpec.describe Ci::Processable do
let(:clone_accessors) { ::Ci::Build.clone_accessors.without(::Ci::Build.extra_accessors) }
let(:reject_accessors) do
- %i[id status user token_encrypted coverage trace runner
- artifacts_expire_at
+ %i[id status user token_encrypted coverage runner artifacts_expire_at
created_at updated_at started_at finished_at queued_at erased_by
erased_at auto_canceled_by job_artifacts job_artifacts_archive
job_artifacts_metadata job_artifacts_trace job_artifacts_junit
@@ -86,7 +83,7 @@ RSpec.describe Ci::Processable do
resource resource_group_id processed security_scans author
pipeline_id report_results pending_state pages_deployments
queuing_entry runtime_metadata trace_metadata
- dast_site_profile dast_scanner_profile].freeze
+ dast_site_profile dast_scanner_profile stage_id].freeze
end
before_all do
@@ -208,10 +205,11 @@ RSpec.describe Ci::Processable do
let(:environment_name) { 'review/$CI_COMMIT_REF_SLUG-$GITLAB_USER_ID' }
let!(:processable) do
- create(:ci_build, :with_deployment, environment: environment_name,
- options: { environment: { name: environment_name } },
- pipeline: pipeline, stage_id: stage.id, project: project,
- user: other_developer)
+ create(:ci_build, :with_deployment,
+ environment: environment_name,
+ options: { environment: { name: environment_name } },
+ pipeline: pipeline, stage_id: stage.id, project: project,
+ user: other_developer)
end
it 're-uses the previous persisted environment' do
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index ae8748f8ae3..181351222c1 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -266,13 +266,13 @@ RSpec.describe Ci::Runner do
end
shared_examples '.belonging_to_parent_group_of_project' do
- let!(:group1) { create(:group) }
- let!(:project1) { create(:project, group: group1) }
- let!(:runner1) { create(:ci_runner, :group, groups: [group1]) }
+ let_it_be(:group1) { create(:group) }
+ let_it_be(:project1) { create(:project, group: group1) }
+ let_it_be(:runner1) { create(:ci_runner, :group, groups: [group1]) }
- let!(:group2) { create(:group) }
- let!(:project2) { create(:project, group: group2) }
- let!(:runner2) { create(:ci_runner, :group, groups: [group2]) }
+ let_it_be(:group2) { create(:group) }
+ let_it_be(:project2) { create(:project, group: group2) }
+ let_it_be(:runner2) { create(:ci_runner, :group, groups: [group2]) }
let(:project_id) { project1.id }
@@ -495,8 +495,8 @@ RSpec.describe Ci::Runner do
describe '.active' do
subject { described_class.active(active_value) }
- let!(:runner1) { create(:ci_runner, :instance, active: false) }
- let!(:runner2) { create(:ci_runner, :instance) }
+ let_it_be(:runner1) { create(:ci_runner, :instance, active: false) }
+ let_it_be(:runner2) { create(:ci_runner, :instance) }
context 'with active_value set to false' do
let(:active_value) { false }
@@ -544,7 +544,7 @@ RSpec.describe Ci::Runner do
end
describe '#stale?', :clean_gitlab_redis_cache do
- let(:runner) { create(:ci_runner, :instance) }
+ let(:runner) { build(:ci_runner, :instance) }
subject { runner.stale? }
@@ -619,7 +619,7 @@ RSpec.describe Ci::Runner do
end
describe '#online?', :clean_gitlab_redis_cache do
- let(:runner) { create(:ci_runner, :instance) }
+ let(:runner) { build(:ci_runner, :instance) }
subject { runner.online? }
@@ -1016,7 +1016,7 @@ RSpec.describe Ci::Runner do
let!(:last_update) { runner.ensure_runner_queue_value }
before do
- Ci::Runners::UpdateRunnerService.new(runner).update(description: 'new runner') # rubocop: disable Rails/SaveBang
+ Ci::Runners::UpdateRunnerService.new(runner).execute(description: 'new runner')
end
it 'sets a new last_update value' do
@@ -1162,13 +1162,13 @@ RSpec.describe Ci::Runner do
end
describe '.assignable_for' do
- let(:project) { create(:project) }
- let(:group) { create(:group) }
- let(:another_project) { create(:project) }
- let!(:unlocked_project_runner) { create(:ci_runner, :project, projects: [project]) }
- let!(:locked_project_runner) { create(:ci_runner, :project, locked: true, projects: [project]) }
- let!(:group_runner) { create(:ci_runner, :group, groups: [group]) }
- let!(:instance_runner) { create(:ci_runner, :instance) }
+ let_it_be(:project) { create(:project) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:another_project) { create(:project) }
+ let_it_be(:unlocked_project_runner) { create(:ci_runner, :project, projects: [project]) }
+ let_it_be(:locked_project_runner) { create(:ci_runner, :project, locked: true, projects: [project]) }
+ let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group]) }
+ let_it_be(:instance_runner) { create(:ci_runner, :instance) }
context 'with already assigned project' do
subject { described_class.assignable_for(project) }
@@ -1186,59 +1186,74 @@ RSpec.describe Ci::Runner do
end
end
- describe "belongs_to_one_project?" do
- it "returns false if there are two projects runner assigned to" do
- project1 = create(:project)
- project2 = create(:project)
- runner = create(:ci_runner, :project, projects: [project1, project2])
+ context 'Project-related queries' do
+ let_it_be(:project1) { create(:project) }
+ let_it_be(:project2) { create(:project) }
+
+ describe '#owner_project' do
+ subject(:owner_project) { project_runner.owner_project }
+
+ context 'with project1 as first project associated with runner' do
+ let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project1, project2]) }
+
+ it { is_expected.to eq project1 }
+ end
+
+ context 'with project2 as first project associated with runner' do
+ let_it_be(:project_runner) { create(:ci_runner, :project, projects: [project2, project1]) }
- expect(runner.belongs_to_one_project?).to be_falsey
+ it { is_expected.to eq project2 }
+ end
end
- it "returns true" do
- project = create(:project)
- runner = create(:ci_runner, :project, projects: [project])
+ describe "belongs_to_one_project?" do
+ it "returns false if there are two projects runner is assigned to" do
+ runner = create(:ci_runner, :project, projects: [project1, project2])
+
+ expect(runner.belongs_to_one_project?).to be_falsey
+ end
- expect(runner.belongs_to_one_project?).to be_truthy
+ it "returns true if there is only one project runner is assigned to" do
+ runner = create(:ci_runner, :project, projects: [project1])
+
+ expect(runner.belongs_to_one_project?).to be_truthy
+ end
end
- end
- describe '#belongs_to_more_than_one_project?' do
- context 'project runner' do
- let(:project1) { create(:project) }
- let(:project2) { create(:project) }
+ describe '#belongs_to_more_than_one_project?' do
+ context 'project runner' do
+ context 'two projects assigned to runner' do
+ let(:runner) { create(:ci_runner, :project, projects: [project1, project2]) }
+
+ it 'returns true' do
+ expect(runner.belongs_to_more_than_one_project?).to be_truthy
+ end
+ end
- context 'two projects assigned to runner' do
- let(:runner) { create(:ci_runner, :project, projects: [project1, project2]) }
+ context 'one project assigned to runner' do
+ let(:runner) { create(:ci_runner, :project, projects: [project1]) }
- it 'returns true' do
- expect(runner.belongs_to_more_than_one_project?).to be_truthy
+ it 'returns false' do
+ expect(runner.belongs_to_more_than_one_project?).to be_falsey
+ end
end
end
- context 'one project assigned to runner' do
- let(:runner) { create(:ci_runner, :project, projects: [project1]) }
+ context 'group runner' do
+ let(:group) { create(:group) }
+ let(:runner) { create(:ci_runner, :group, groups: [group]) }
it 'returns false' do
expect(runner.belongs_to_more_than_one_project?).to be_falsey
end
end
- end
-
- context 'group runner' do
- let(:group) { create(:group) }
- let(:runner) { create(:ci_runner, :group, groups: [group]) }
-
- it 'returns false' do
- expect(runner.belongs_to_more_than_one_project?).to be_falsey
- end
- end
- context 'shared runner' do
- let(:runner) { create(:ci_runner, :instance) }
+ context 'shared runner' do
+ let(:runner) { create(:ci_runner, :instance) }
- it 'returns false' do
- expect(runner.belongs_to_more_than_one_project?).to be_falsey
+ it 'returns false' do
+ expect(runner.belongs_to_more_than_one_project?).to be_falsey
+ end
end
end
end
@@ -1299,7 +1314,7 @@ RSpec.describe Ci::Runner do
end
describe '.search' do
- let(:runner) { create(:ci_runner, token: '123abc', description: 'test runner') }
+ let_it_be(:runner) { create(:ci_runner, token: '123abc', description: 'test runner') }
it 'returns runners with a matching token' do
expect(described_class.search(runner.token)).to eq([runner])
@@ -1326,57 +1341,10 @@ RSpec.describe Ci::Runner do
end
end
- describe '#assigned_to_group?' do
- subject { runner.assigned_to_group? }
-
- context 'when project runner' do
- let(:runner) { create(:ci_runner, :project, description: 'Project runner', projects: [project]) }
- let(:project) { create(:project) }
-
- it { is_expected.to be_falsey }
- end
-
- context 'when shared runner' do
- let(:runner) { create(:ci_runner, :instance, description: 'Shared runner') }
-
- it { is_expected.to be_falsey }
- end
-
- context 'when group runner' do
- let(:group) { create(:group) }
- let(:runner) { create(:ci_runner, :group, description: 'Group runner', groups: [group]) }
-
- it { is_expected.to be_truthy }
- end
- end
-
- describe '#assigned_to_project?' do
- subject { runner.assigned_to_project? }
-
- context 'when group runner' do
- let(:runner) { create(:ci_runner, :group, description: 'Group runner', groups: [group]) }
- let(:group) { create(:group) }
-
- it { is_expected.to be_falsey }
- end
-
- context 'when shared runner' do
- let(:runner) { create(:ci_runner, :instance, description: 'Shared runner') }
-
- it { is_expected.to be_falsey }
- end
-
- context 'when project runner' do
- let(:runner) { create(:ci_runner, :project, description: 'Project runner', projects: [project]) }
- let(:project) { create(:project) }
-
- it { is_expected.to be_truthy }
- end
- end
-
describe '#pick_build!' do
+ let_it_be(:runner) { create(:ci_runner) }
+
let(:build) { create(:ci_build) }
- let(:runner) { create(:ci_runner) }
context 'runner can pick the build' do
it 'calls #tick_runner_queue' do
@@ -1413,26 +1381,26 @@ RSpec.describe Ci::Runner do
end
describe '.order_by' do
+ let_it_be(:runner1) { create(:ci_runner, created_at: 1.year.ago, contacted_at: 1.year.ago) }
+ let_it_be(:runner2) { create(:ci_runner, created_at: 1.month.ago, contacted_at: 1.month.ago) }
+
+ before do
+ runner1.update!(token_expires_at: 1.year.from_now)
+ end
+
it 'supports ordering by the contact date' do
- runner1 = create(:ci_runner, contacted_at: 1.year.ago)
- runner2 = create(:ci_runner, contacted_at: 1.month.ago)
runners = described_class.order_by('contacted_asc')
expect(runners).to eq([runner1, runner2])
end
it 'supports ordering by the creation date' do
- runner1 = create(:ci_runner, created_at: 1.year.ago)
- runner2 = create(:ci_runner, created_at: 1.month.ago)
runners = described_class.order_by('created_asc')
expect(runners).to eq([runner2, runner1])
end
it 'supports ordering by the token expiration' do
- runner1 = create(:ci_runner)
- runner1.update!(token_expires_at: 1.year.from_now)
- runner2 = create(:ci_runner)
runner3 = create(:ci_runner)
runner3.update!(token_expires_at: 1.month.from_now)
diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb
index d55a8509a98..dd9af33a562 100644
--- a/spec/models/ci/stage_spec.rb
+++ b/spec/models/ci/stage_spec.rb
@@ -369,4 +369,33 @@ RSpec.describe Ci::Stage, :models do
let!(:model) { create(:ci_stage, project: parent) }
end
end
+
+ describe 'partitioning' do
+ context 'with pipeline' do
+ let(:pipeline) { build(:ci_pipeline, partition_id: 123) }
+ let(:stage) { build(:ci_stage, pipeline: pipeline) }
+
+ it 'copies the partition_id from pipeline' do
+ expect { stage.valid? }.to change(stage, :partition_id).to(123)
+ end
+
+ context 'when it is already set' do
+ let(:stage) { build(:ci_stage, pipeline: pipeline, partition_id: 125) }
+
+ it 'does not change the partition_id value' do
+ expect { stage.valid? }.not_to change(stage, :partition_id)
+ end
+ end
+ end
+
+ context 'without pipeline' do
+ subject(:stage) { build(:ci_stage, pipeline: nil) }
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { stage.valid? }.not_to change(stage, :partition_id)
+ end
+ end
+ end
end
diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb
index 4ac8720780c..8517e583ec7 100644
--- a/spec/models/ci/trigger_spec.rb
+++ b/spec/models/ci/trigger_spec.rb
@@ -20,6 +20,7 @@ RSpec.describe Ci::Trigger do
trigger = create(:ci_trigger_without_token, project: project)
expect(trigger.token).not_to be_nil
+ expect(trigger.token).to start_with(Ci::Trigger::TRIGGER_TOKEN_PREFIX)
end
it 'does not set a random token if one provided' do
@@ -30,12 +31,22 @@ RSpec.describe Ci::Trigger do
end
describe '#short_token' do
- let(:trigger) { create(:ci_trigger, token: '12345678') }
+ let(:trigger) { create(:ci_trigger) }
subject { trigger.short_token }
- it 'returns shortened token' do
- is_expected.to eq('1234')
+ it 'returns shortened token without prefix' do
+ is_expected.not_to start_with(Ci::Trigger::TRIGGER_TOKEN_PREFIX)
+ end
+
+ context 'token does not have a prefix' do
+ before do
+ trigger.token = '12345678'
+ end
+
+ it 'returns shortened token' do
+ is_expected.to eq('1234')
+ end
end
end
diff --git a/spec/models/clusters/platforms/kubernetes_spec.rb b/spec/models/clusters/platforms/kubernetes_spec.rb
index a0ede9fb0d9..7b9ff409edd 100644
--- a/spec/models/clusters/platforms/kubernetes_spec.rb
+++ b/spec/models/clusters/platforms/kubernetes_spec.rb
@@ -605,15 +605,15 @@ RSpec.describe Clusters::Platforms::Kubernetes do
{ 'app.gitlab.com/app' => project.full_path_slug, 'app.gitlab.com/env' => 'env-000000' }
])
expect(rollout_status.instances).to eq([{ pod_name: "kube-pod",
- stable: true,
- status: "pending",
- tooltip: "kube-pod (Pending)",
- track: "stable" },
+ stable: true,
+ status: "pending",
+ tooltip: "kube-pod (Pending)",
+ track: "stable" },
{ pod_name: "Not provided",
- stable: true,
- status: "pending",
- tooltip: "Not provided (Pending)",
- track: "stable" }])
+ stable: true,
+ status: "pending",
+ tooltip: "Not provided (Pending)",
+ track: "stable" }])
end
context 'with canary ingress' do
diff --git a/spec/models/commit_signatures/gpg_signature_spec.rb b/spec/models/commit_signatures/gpg_signature_spec.rb
index 6ae2a202b72..605ad725dd7 100644
--- a/spec/models/commit_signatures/gpg_signature_spec.rb
+++ b/spec/models/commit_signatures/gpg_signature_spec.rb
@@ -5,12 +5,14 @@ require 'spec_helper'
RSpec.describe CommitSignatures::GpgSignature do
# This commit is seeded from https://gitlab.com/gitlab-org/gitlab-test
# For instructions on how to add more seed data, see the project README
- let(:commit_sha) { '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' }
- let!(:project) { create(:project, :repository, path: 'sample-project') }
- let!(:commit) { create(:commit, project: project, sha: commit_sha) }
- let(:signature) { create(:gpg_signature, commit_sha: commit_sha) }
- let(:gpg_key) { create(:gpg_key) }
- let(:gpg_key_subkey) { create(:gpg_key_subkey) }
+ let_it_be(:commit_sha) { '0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33' }
+ let_it_be(:project) { create(:project, :repository, path: 'sample-project') }
+ let_it_be(:commit) { create(:commit, project: project, sha: commit_sha) }
+ let_it_be(:gpg_key) { create(:gpg_key) }
+ let_it_be(:gpg_key_subkey) { create(:gpg_key_subkey, gpg_key: gpg_key) }
+
+ let(:signature) { create(:gpg_signature, commit_sha: commit_sha, gpg_key: gpg_key) }
+
let(:attributes) do
{
commit_sha: commit_sha,
@@ -35,8 +37,7 @@ RSpec.describe CommitSignatures::GpgSignature do
end
describe '.by_commit_sha scope' do
- let(:gpg_key) { create(:gpg_key, key: GpgHelpers::User2.public_key) }
- let!(:another_gpg_signature) { create(:gpg_signature, gpg_key: gpg_key) }
+ let_it_be(:another_gpg_signature) { create(:gpg_signature, gpg_key: gpg_key) }
it 'returns all gpg signatures by sha' do
expect(described_class.by_commit_sha(commit_sha)).to match_array([signature])
diff --git a/spec/models/commit_signatures/ssh_signature_spec.rb b/spec/models/commit_signatures/ssh_signature_spec.rb
index 64d95fe3a71..08530bf6964 100644
--- a/spec/models/commit_signatures/ssh_signature_spec.rb
+++ b/spec/models/commit_signatures/ssh_signature_spec.rb
@@ -5,11 +5,11 @@ require 'spec_helper'
RSpec.describe CommitSignatures::SshSignature do
# This commit is seeded from https://gitlab.com/gitlab-org/gitlab-test
# For instructions on how to add more seed data, see the project README
- let(:commit_sha) { '7b5160f9bb23a3d58a0accdbe89da13b96b1ece9' }
- let!(:project) { create(:project, :repository, path: 'sample-project') }
- let!(:commit) { create(:commit, project: project, sha: commit_sha) }
- let(:signature) { create(:ssh_signature, commit_sha: commit_sha) }
- let(:ssh_key) { create(:ed25519_key_256) }
+ let_it_be(:commit_sha) { '7b5160f9bb23a3d58a0accdbe89da13b96b1ece9' }
+ let_it_be(:project) { create(:project, :repository, path: 'sample-project') }
+ let_it_be(:commit) { create(:commit, project: project, sha: commit_sha) }
+ let_it_be(:ssh_key) { create(:ed25519_key_256) }
+
let(:attributes) do
{
commit_sha: commit_sha,
@@ -18,6 +18,8 @@ RSpec.describe CommitSignatures::SshSignature do
}
end
+ let(:signature) { create(:ssh_signature, commit_sha: commit_sha, key: ssh_key) }
+
it_behaves_like 'having unique enum values'
it_behaves_like 'commit signature'
diff --git a/spec/models/commit_signatures/x509_commit_signature_spec.rb b/spec/models/commit_signatures/x509_commit_signature_spec.rb
index beb101cdd89..b971fd078e2 100644
--- a/spec/models/commit_signatures/x509_commit_signature_spec.rb
+++ b/spec/models/commit_signatures/x509_commit_signature_spec.rb
@@ -5,11 +5,10 @@ require 'spec_helper'
RSpec.describe CommitSignatures::X509CommitSignature do
# This commit is seeded from https://gitlab.com/gitlab-org/gitlab-test
# For instructions on how to add more seed data, see the project README
- let(:commit_sha) { '189a6c924013fc3fe40d6f1ec1dc20214183bc97' }
- let(:project) { create(:project, :public, :repository) }
- let!(:commit) { create(:commit, project: project, sha: commit_sha) }
- let(:x509_certificate) { create(:x509_certificate) }
- let(:signature) { create(:x509_commit_signature, commit_sha: commit_sha) }
+ let_it_be(:commit_sha) { '189a6c924013fc3fe40d6f1ec1dc20214183bc97' }
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:commit) { create(:commit, project: project, sha: commit_sha) }
+ let_it_be(:x509_certificate) { create(:x509_certificate) }
let(:attributes) do
{
@@ -20,6 +19,8 @@ RSpec.describe CommitSignatures::X509CommitSignature do
}
end
+ let(:signature) { create(:x509_commit_signature, commit_sha: commit_sha, x509_certificate: x509_certificate) }
+
it_behaves_like 'having unique enum values'
it_behaves_like 'commit signature'
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index 08d770a1beb..bab6247d4f9 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -84,6 +84,22 @@ RSpec.describe Commit do
end
end
+ describe '.build_from_sidekiq_hash' do
+ it 'returns a Commit' do
+ commit = described_class.build_from_sidekiq_hash(project, id: '123')
+
+ expect(commit).to be_an_instance_of(Commit)
+ end
+
+ it 'parses date strings into Time instances' do
+ commit = described_class.build_from_sidekiq_hash(project,
+ id: '123',
+ authored_date: Time.current.to_s)
+
+ expect(commit.authored_date).to be_a_kind_of(Time)
+ end
+ end
+
describe '#diff_refs' do
it 'is equal to itself' do
expect(commit.diff_refs).to eq(commit.diff_refs)
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index 78d4d9de84e..adbd20b6730 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -836,17 +836,11 @@ RSpec.describe CommitStatus do
context 'when commit status does not have stage but it exists' do
let!(:stage) do
- create(:ci_stage, project: project,
- pipeline: pipeline,
- name: 'test')
+ create(:ci_stage, project: project, pipeline: pipeline, name: 'test')
end
let(:commit_status) do
- create(:commit_status, project: project,
- pipeline: pipeline,
- name: 'rspec',
- stage: 'test',
- status: :success)
+ create(:commit_status, project: project, pipeline: pipeline, name: 'rspec', stage: 'test', status: :success)
end
it 'uses existing stage', :sidekiq_might_not_need_inline do
@@ -1008,4 +1002,53 @@ RSpec.describe CommitStatus do
let!(:model) { create(:ci_build, runner: parent) }
end
end
+
+ describe '.stage_name' do
+ subject(:stage_name) { commit_status.stage_name }
+
+ it 'returns the stage name' do
+ expect(stage_name).to eq('test')
+ end
+
+ context 'when ci stage is not present' do
+ before do
+ commit_status.ci_stage = nil
+ end
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ describe 'partitioning' do
+ context 'with pipeline' do
+ let(:pipeline) { build(:ci_pipeline, partition_id: 123) }
+ let(:status) { build(:commit_status, pipeline: pipeline) }
+
+ it 'copies the partition_id from pipeline' do
+ expect { status.valid? }.to change(status, :partition_id).to(123)
+ end
+
+ context 'when it is already set' do
+ let(:status) { build(:commit_status, pipeline: pipeline, partition_id: 125) }
+
+ it 'does not change the partition_id value' do
+ expect { status.valid? }.not_to change(status, :partition_id)
+ end
+ end
+ end
+
+ context 'without pipeline' do
+ subject(:status) do
+ build(:commit_status,
+ project: build_stubbed(:project),
+ pipeline: nil)
+ end
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { status.valid? }.not_to change(status, :partition_id)
+ end
+ end
+ end
end
diff --git a/spec/models/concerns/approvable_base_spec.rb b/spec/models/concerns/approvable_base_spec.rb
deleted file mode 100644
index 2bf6a98a64d..00000000000
--- a/spec/models/concerns/approvable_base_spec.rb
+++ /dev/null
@@ -1,111 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe ApprovableBase do
- let(:merge_request) { create(:merge_request) }
- let(:user) { create(:user) }
-
- describe '#approved_by?' do
- subject { merge_request.approved_by?(user) }
-
- context 'when a user has not approved' do
- it 'returns false' do
- is_expected.to be_falsy
- end
- end
-
- context 'when a user has approved' do
- let!(:approval) { create(:approval, merge_request: merge_request, user: user) }
-
- it 'returns false' do
- is_expected.to be_truthy
- end
- end
-
- context 'when a user is nil' do
- let(:user) { nil }
-
- it 'returns false' do
- is_expected.to be_falsy
- end
- end
- end
-
- describe '#can_be_approved_by?' do
- subject { merge_request.can_be_approved_by?(user) }
-
- before do
- merge_request.project.add_developer(user) if user
- end
-
- it 'returns true' do
- is_expected.to be_truthy
- end
-
- context 'when a user has approved' do
- let!(:approval) { create(:approval, merge_request: merge_request, user: user) }
-
- it 'returns false' do
- is_expected.to be_falsy
- end
- end
-
- context 'when a user is nil' do
- let(:user) { nil }
-
- it 'returns false' do
- is_expected.to be_falsy
- end
- end
- end
-
- describe '#can_be_unapproved_by?' do
- subject { merge_request.can_be_unapproved_by?(user) }
-
- before do
- merge_request.project.add_developer(user) if user
- end
-
- it 'returns false' do
- is_expected.to be_falsy
- end
-
- context 'when a user has approved' do
- let!(:approval) { create(:approval, merge_request: merge_request, user: user) }
-
- it 'returns true' do
- is_expected.to be_truthy
- end
- end
-
- context 'when a user is nil' do
- let(:user) { nil }
-
- it 'returns false' do
- is_expected.to be_falsy
- end
- end
- end
-
- describe '.not_approved_by_users_with_usernames' do
- subject { MergeRequest.not_approved_by_users_with_usernames([user.username, user2.username]) }
-
- let!(:merge_request2) { create(:merge_request) }
- let!(:merge_request3) { create(:merge_request) }
- let!(:merge_request4) { create(:merge_request) }
- let(:user2) { create(:user) }
- let(:user3) { create(:user) }
-
- before do
- create(:approval, merge_request: merge_request, user: user)
- create(:approval, merge_request: merge_request2, user: user2)
- create(:approval, merge_request: merge_request2, user: user3)
- create(:approval, merge_request: merge_request4, user: user3)
- end
-
- it 'has the merge request that is not approved at all and not approved by either user' do
- expect(subject).to contain_exactly(merge_request3, merge_request4)
- end
- end
-end
diff --git a/spec/models/concerns/approvable_spec.rb b/spec/models/concerns/approvable_spec.rb
new file mode 100644
index 00000000000..1ddd9b3edca
--- /dev/null
+++ b/spec/models/concerns/approvable_spec.rb
@@ -0,0 +1,111 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Approvable do
+ let(:merge_request) { create(:merge_request) }
+ let(:user) { create(:user) }
+
+ describe '#approved_by?' do
+ subject { merge_request.approved_by?(user) }
+
+ context 'when a user has not approved' do
+ it 'returns false' do
+ is_expected.to be_falsy
+ end
+ end
+
+ context 'when a user has approved' do
+ let!(:approval) { create(:approval, merge_request: merge_request, user: user) }
+
+ it 'returns false' do
+ is_expected.to be_truthy
+ end
+ end
+
+ context 'when a user is nil' do
+ let(:user) { nil }
+
+ it 'returns false' do
+ is_expected.to be_falsy
+ end
+ end
+ end
+
+ describe '#can_be_approved_by?' do
+ subject { merge_request.can_be_approved_by?(user) }
+
+ before do
+ merge_request.project.add_developer(user) if user
+ end
+
+ it 'returns true' do
+ is_expected.to be_truthy
+ end
+
+ context 'when a user has approved' do
+ let!(:approval) { create(:approval, merge_request: merge_request, user: user) }
+
+ it 'returns false' do
+ is_expected.to be_falsy
+ end
+ end
+
+ context 'when a user is nil' do
+ let(:user) { nil }
+
+ it 'returns false' do
+ is_expected.to be_falsy
+ end
+ end
+ end
+
+ describe '#can_be_unapproved_by?' do
+ subject { merge_request.can_be_unapproved_by?(user) }
+
+ before do
+ merge_request.project.add_developer(user) if user
+ end
+
+ it 'returns false' do
+ is_expected.to be_falsy
+ end
+
+ context 'when a user has approved' do
+ let!(:approval) { create(:approval, merge_request: merge_request, user: user) }
+
+ it 'returns true' do
+ is_expected.to be_truthy
+ end
+ end
+
+ context 'when a user is nil' do
+ let(:user) { nil }
+
+ it 'returns false' do
+ is_expected.to be_falsy
+ end
+ end
+ end
+
+ describe '.not_approved_by_users_with_usernames' do
+ subject { MergeRequest.not_approved_by_users_with_usernames([user.username, user2.username]) }
+
+ let!(:merge_request2) { create(:merge_request) }
+ let!(:merge_request3) { create(:merge_request) }
+ let!(:merge_request4) { create(:merge_request) }
+ let(:user2) { create(:user) }
+ let(:user3) { create(:user) }
+
+ before do
+ create(:approval, merge_request: merge_request, user: user)
+ create(:approval, merge_request: merge_request2, user: user2)
+ create(:approval, merge_request: merge_request2, user: user3)
+ create(:approval, merge_request: merge_request4, user: user3)
+ end
+
+ it 'has the merge request that is not approved at all and not approved by either user' do
+ expect(subject).to contain_exactly(merge_request3, merge_request4)
+ end
+ end
+end
diff --git a/spec/models/concerns/ci/artifactable_spec.rb b/spec/models/concerns/ci/artifactable_spec.rb
index 64691165e21..6af244a5a0f 100644
--- a/spec/models/concerns/ci/artifactable_spec.rb
+++ b/spec/models/concerns/ci/artifactable_spec.rb
@@ -46,30 +46,8 @@ RSpec.describe Ci::Artifactable do
end
end
- context 'when file format is zip' do
- context 'when artifact contains one file' do
- let(:artifact) { build(:ci_job_artifact, :zip_with_single_file) }
-
- it 'iterates blob once' do
- expect { |b| artifact.each_blob(&b) }.to yield_control.once
- end
- end
-
- context 'when artifact contains two files' do
- let(:artifact) { build(:ci_job_artifact, :zip_with_multiple_files) }
-
- it 'iterates blob two times' do
- expect { |b| artifact.each_blob(&b) }.to yield_control.exactly(2).times
- end
- end
- end
-
context 'when there are no adapters for the file format' do
- let(:artifact) { build(:ci_job_artifact, :junit) }
-
- before do
- allow(artifact).to receive(:file_format).and_return(:unknown)
- end
+ let(:artifact) { build(:ci_job_artifact, :junit, file_format: :zip) }
it 'raises an error' do
expect { |b| artifact.each_blob(&b) }.to raise_error(described_class::NotSupportedAdapterError)
diff --git a/spec/models/concerns/ci/has_deployment_name_spec.rb b/spec/models/concerns/ci/has_deployment_name_spec.rb
deleted file mode 100644
index 8c7338638b1..00000000000
--- a/spec/models/concerns/ci/has_deployment_name_spec.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Ci::HasDeploymentName do
- describe 'deployment_name?' do
- let(:build) { create(:ci_build) }
-
- subject { build.branch? }
-
- it 'does detect deployment names' do
- build.name = 'deployment'
-
- expect(build.deployment_name?).to be_truthy
- end
-
- it 'does detect partial deployment names' do
- build.name = 'do a really cool deploy'
-
- expect(build.deployment_name?).to be_truthy
- end
-
- it 'does not detect non-deployment names' do
- build.name = 'testing'
-
- expect(build.deployment_name?).to be_falsy
- end
-
- it 'is case insensitive' do
- build.name = 'DEPLOY'
- expect(build.deployment_name?).to be_truthy
- end
- end
-end
diff --git a/spec/models/concerns/ci/track_environment_usage_spec.rb b/spec/models/concerns/ci/track_environment_usage_spec.rb
new file mode 100644
index 00000000000..d75972c49b5
--- /dev/null
+++ b/spec/models/concerns/ci/track_environment_usage_spec.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::TrackEnvironmentUsage do
+ describe '#verifies_environment?' do
+ subject { build.verifies_environment? }
+
+ context 'when build is the verify action for the environment' do
+ let(:build) do
+ build_stubbed(:ci_build,
+ ref: 'master',
+ environment: 'staging',
+ options: { environment: { action: 'verify' } })
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when build is not the verify action for the environment' do
+ let(:build) do
+ build_stubbed(:ci_build,
+ ref: 'master',
+ environment: 'staging',
+ options: { environment: { action: 'start' } })
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe 'deployment_name?' do
+ let(:build) { create(:ci_build) }
+
+ subject { build.branch? }
+
+ it 'does detect deployment names' do
+ build.name = 'deployment'
+
+ expect(build).to be_deployment_name
+ end
+
+ it 'does detect partial deployment names' do
+ build.name = 'do a really cool deploy'
+
+ expect(build).to be_deployment_name
+ end
+
+ it 'does not detect non-deployment names' do
+ build.name = 'testing'
+
+ expect(build).not_to be_deployment_name
+ end
+
+ it 'is case insensitive' do
+ build.name = 'DEPLOY'
+
+ expect(build).to be_deployment_name
+ end
+ end
+end
diff --git a/spec/models/concerns/counter_attribute_spec.rb b/spec/models/concerns/counter_attribute_spec.rb
index 8d32ef14f47..2dd70188740 100644
--- a/spec/models/concerns/counter_attribute_spec.rb
+++ b/spec/models/concerns/counter_attribute_spec.rb
@@ -79,4 +79,14 @@ RSpec.describe CounterAttribute, :counter_attribute, :clean_gitlab_redis_shared_
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
+ end
+ end
end
diff --git a/spec/models/concerns/from_set_operator_spec.rb b/spec/models/concerns/from_set_operator_spec.rb
index 8ebbb5550c9..1ef0d1adb08 100644
--- a/spec/models/concerns/from_set_operator_spec.rb
+++ b/spec/models/concerns/from_set_operator_spec.rb
@@ -3,7 +3,22 @@
require 'spec_helper'
RSpec.describe FromSetOperator do
- describe 'when set operator method already exists' do
+ let_it_be(:from_set_operator) do
+ Class.new do
+ extend FromSetOperator
+ define_set_operator Gitlab::SQL::Union
+
+ def table_name
+ 'groups'
+ end
+
+ def from(*args)
+ ''
+ end
+ end
+ end
+
+ context 'when set operator method already exists' do
let(:redefine_method) do
Class.new do
def self.from_union
@@ -17,4 +32,38 @@ RSpec.describe FromSetOperator do
it { expect { redefine_method }.to raise_exception(RuntimeError) }
end
+
+ context 'with members' do
+ let_it_be(:group1) { create :group }
+ let_it_be(:group2) { create :group }
+ let_it_be(:groups) do
+ [
+ Group.where(id: group1),
+ Group.where(id: group2)
+ ]
+ end
+
+ shared_examples 'set operator called with correct members' do
+ it do
+ expect(Gitlab::SQL::Union).to receive(:new).with(groups, anything).and_call_original
+ subject
+ end
+ end
+
+ context 'as array' do
+ subject { from_set_operator.new.from_union(groups) }
+
+ it_behaves_like 'set operator called with correct members'
+
+ it { expect { subject }.not_to make_queries }
+ end
+
+ context 'as multiple parameters' do
+ subject { from_set_operator.new.from_union(*groups) }
+
+ it_behaves_like 'set operator called with correct members'
+
+ it { expect { subject }.not_to make_queries }
+ end
+ end
end
diff --git a/spec/models/concerns/pg_full_text_searchable_spec.rb b/spec/models/concerns/pg_full_text_searchable_spec.rb
index 55e3caf3c4c..3e42a3504ac 100644
--- a/spec/models/concerns/pg_full_text_searchable_spec.rb
+++ b/spec/models/concerns/pg_full_text_searchable_spec.rb
@@ -96,6 +96,7 @@ RSpec.describe PgFullTextSearchable do
it 'ignores accents' do
expect(model_class.pg_full_text_search('jurgen')).to contain_exactly(with_accent)
+ expect(model_class.pg_full_text_search('Jürgen')).to contain_exactly(with_accent)
end
it 'does not support searching by non-Latin characters' do
diff --git a/spec/models/concerns/project_features_compatibility_spec.rb b/spec/models/concerns/project_features_compatibility_spec.rb
index b49b9ce8a2a..89f34834aa4 100644
--- a/spec/models/concerns/project_features_compatibility_spec.rb
+++ b/spec/models/concerns/project_features_compatibility_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe ProjectFeaturesCompatibility do
let(:features) do
features_enabled + %w(
repository pages operations container_registry package_registry environments feature_flags releases
+ monitor
)
end
diff --git a/spec/models/concerns/reactive_caching_spec.rb b/spec/models/concerns/reactive_caching_spec.rb
index cb9bb676ede..039b9e574fe 100644
--- a/spec/models/concerns/reactive_caching_spec.rb
+++ b/spec/models/concerns/reactive_caching_spec.rb
@@ -281,7 +281,7 @@ RSpec.describe ReactiveCaching, :use_clean_rails_memory_store_caching do
end
it 'does not delete the value key' do
- expect(Rails.cache).to receive(:delete).with(cache_key).never
+ expect(Rails.cache).not_to receive(:delete).with(cache_key)
go!
end
@@ -338,7 +338,7 @@ RSpec.describe ReactiveCaching, :use_clean_rails_memory_store_caching do
context 'when lifetime is exceeded' do
it 'skips the calculation' do
- expect(instance).to receive(:calculate_reactive_cache).never
+ expect(instance).not_to receive(:calculate_reactive_cache)
go!
end
@@ -354,7 +354,7 @@ RSpec.describe ReactiveCaching, :use_clean_rails_memory_store_caching do
it 'skips the calculation' do
stub_exclusive_lease_taken(cache_key)
- expect(instance).to receive(:calculate_reactive_cache).never
+ expect(instance).not_to receive(:calculate_reactive_cache)
go!
end
diff --git a/spec/models/container_registry/event_spec.rb b/spec/models/container_registry/event_spec.rb
index 799d9d4fd1c..c2c494c49fb 100644
--- a/spec/models/container_registry/event_spec.rb
+++ b/spec/models/container_registry/event_spec.rb
@@ -144,7 +144,7 @@ RSpec.describe ContainerRegistry::Event do
let(:target) do
{
'mediaType' => ContainerRegistry::Client::DOCKER_DISTRIBUTION_MANIFEST_V2_TYPE,
- 'repository' => repository_path,
+ 'repository' => repository_path,
'tag' => 'latest'
}
end
diff --git a/spec/models/customer_relations/organization_spec.rb b/spec/models/customer_relations/organization_spec.rb
index 1833fcf5385..d19a0bdf6c7 100644
--- a/spec/models/customer_relations/organization_spec.rb
+++ b/spec/models/customer_relations/organization_spec.rb
@@ -146,15 +146,43 @@ RSpec.describe CustomerRelations::Organization, type: :model do
end
end
- describe '.sort_by_name' do
- let_it_be(:organization_a) { create(:organization, group: group, name: "c") }
+ describe '.counts_by_state' do
+ before do
+ create_list(:organization, 3, group: group)
+ create_list(:organization, 2, group: group, state: 'inactive')
+ end
+
+ it 'returns correct organization counts' do
+ counts = group.organizations.counts_by_state
+
+ expect(counts['active']).to be(3)
+ expect(counts['inactive']).to be(2)
+ end
+
+ it 'returns 0 with no results' do
+ counts = group.organizations.where(id: non_existing_record_id).counts_by_state
+
+ expect(counts['active']).to be(0)
+ expect(counts['inactive']).to be(0)
+ end
+ end
+
+ describe 'sorting' do
+ let_it_be(:organization_a) { create(:organization, group: group, name: "c", description: "1") }
let_it_be(:organization_b) { create(:organization, group: group, name: "a") }
- let_it_be(:organization_c) { create(:organization, group: group, name: "b") }
+ let_it_be(:organization_c) { create(:organization, group: group, name: "b", description: "2") }
- context 'when sorting the organizations' do
+ describe '.sort_by_name' do
it 'sorts them by name in ascendent order' do
expect(group.organizations.sort_by_name).to eq([organization_b, organization_c, organization_a])
end
end
+
+ describe '.sort_by_field' do
+ it 'sorts them by description in descending order' do
+ expect(group.organizations.sort_by_field('description', :desc))
+ .to eq([organization_c, organization_a, organization_b])
+ end
+ end
end
end
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index 0a4ee73f3d3..87fa5289795 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -74,6 +74,27 @@ RSpec.describe Deployment do
end
end
+ describe '.for_iid' do
+ subject { described_class.for_iid(project, iid) }
+
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:deployment) { create(:deployment, project: project) }
+
+ let(:iid) { deployment.iid }
+
+ it 'finds the deployment' do
+ is_expected.to contain_exactly(deployment)
+ end
+
+ context 'when iid does not match' do
+ let(:iid) { non_existing_record_id }
+
+ it 'does not find the deployment' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
describe '.for_environment_name' do
subject { described_class.for_environment_name(project, environment_name) }
@@ -142,8 +163,8 @@ RSpec.describe Deployment do
it 'executes Deployments::HooksWorker asynchronously' do
freeze_time do
expect(Deployments::HooksWorker)
- .to receive(:perform_async).with(deployment_id: deployment.id, status: 'running',
- status_changed_at: Time.current)
+ .to receive(:perform_async)
+ .with(deployment_id: deployment.id, status: 'running', status_changed_at: Time.current)
deployment.run!
end
@@ -179,8 +200,8 @@ RSpec.describe Deployment do
it 'executes Deployments::HooksWorker asynchronously' do
freeze_time do
expect(Deployments::HooksWorker)
- .to receive(:perform_async).with(deployment_id: deployment.id, status: 'success',
- status_changed_at: Time.current)
+ .to receive(:perform_async)
+ .with(deployment_id: deployment.id, status: 'success', status_changed_at: Time.current)
deployment.succeed!
end
@@ -209,8 +230,8 @@ RSpec.describe Deployment do
it 'executes Deployments::HooksWorker asynchronously' do
freeze_time do
expect(Deployments::HooksWorker)
- .to receive(:perform_async).with(deployment_id: deployment.id, status: 'failed',
- status_changed_at: Time.current)
+ .to receive(:perform_async)
+ .with(deployment_id: deployment.id, status: 'failed', status_changed_at: Time.current)
deployment.drop!
end
@@ -239,8 +260,8 @@ RSpec.describe Deployment do
it 'executes Deployments::HooksWorker asynchronously' do
freeze_time do
expect(Deployments::HooksWorker)
- .to receive(:perform_async).with(deployment_id: deployment.id, status: 'canceled',
- status_changed_at: Time.current)
+ .to receive(:perform_async)
+ .with(deployment_id: deployment.id, status: 'canceled', status_changed_at: Time.current)
deployment.cancel!
end
@@ -343,6 +364,31 @@ RSpec.describe Deployment do
end
end
+ describe '#older_than_last_successful_deployment?' do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:environment) { create(:environment, project: project) }
+
+ subject { deployment.older_than_last_successful_deployment? }
+
+ context 'when deployment is current deployment' do
+ let(:deployment) { create(:deployment, :success, project: project) }
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'when deployment is behind current deployment' do
+ let!(:deployment) do
+ create(:deployment, :success, project: project, environment: environment, finished_at: 1.year.ago)
+ end
+
+ let!(:last_deployment) do
+ create(:deployment, :success, project: project, environment: environment)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
describe '#success?' do
subject { deployment.success? }
@@ -517,6 +563,16 @@ RSpec.describe Deployment do
end
end
+ describe '.ordered_as_upcoming' do
+ let!(:deployment1) { create(:deployment, status: :running) }
+ let!(:deployment2) { create(:deployment, status: :blocked) }
+ let!(:deployment3) { create(:deployment, status: :created) }
+
+ it 'sorts by ID DESC' do
+ expect(described_class.ordered_as_upcoming).to eq([deployment3, deployment2, deployment1])
+ end
+ end
+
describe 'visible' do
subject { described_class.visible }
@@ -876,6 +932,22 @@ RSpec.describe Deployment do
end
end
+ describe '#build' do
+ let!(:deployment) { create(:deployment) }
+
+ subject { deployment.build }
+
+ it 'retrieves build for the deployment' do
+ is_expected.to eq(deployment.deployable)
+ end
+
+ it 'returns nil when the associated build is not found' do
+ deployment.update!(deployable_id: nil, deployable_type: nil)
+
+ is_expected.to be_nil
+ end
+ end
+
describe '#previous_deployment' do
using RSpec::Parameterized::TableSyntax
@@ -1233,6 +1305,19 @@ RSpec.describe Deployment do
end
end
+ describe '#tags' do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:deployment) { create(:deployment, project: project) }
+
+ subject { deployment.tags }
+
+ it 'will return tags related to this deployment' do
+ expect(project.repository).to receive(:tag_names_contains).with(deployment.sha, limit: 100).and_return(['test'])
+
+ is_expected.to match_array(['test'])
+ end
+ end
+
describe '#valid_sha' do
it 'does not add errors for a valid SHA' do
project = create(:project, :repository)
diff --git a/spec/models/design_management/version_spec.rb b/spec/models/design_management/version_spec.rb
index 519ba3c67b4..44ecae82174 100644
--- a/spec/models/design_management/version_spec.rb
+++ b/spec/models/design_management/version_spec.rb
@@ -256,7 +256,7 @@ RSpec.describe DesignManagement::Version do
it 'puts them in the right buckets' do
expect(version.designs_by_event).to match(
a_hash_including(
- 'creation' => have_attributes(size: 3),
+ 'creation' => have_attributes(size: 3),
'modification' => have_attributes(size: 4),
'deletion' => have_attributes(size: 5)
)
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index 3f4372dafd0..1e15b09a069 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -17,6 +17,8 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
it { is_expected.to nullify_if_blank(:external_url) }
it { is_expected.to belong_to(:project).required }
+ it { is_expected.to belong_to(:merge_request).optional }
+
it { is_expected.to have_many(:deployments) }
it { is_expected.to have_many(:metrics_dashboard_annotations) }
it { is_expected.to have_many(:alert_management_alerts) }
@@ -40,6 +42,26 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
expect(environment).to be_valid
end
+
+ context 'does not allow changes to merge_request' do
+ let(:merge_request) { create(:merge_request, source_project: project) }
+
+ it 'for an environment that has no merge request associated' do
+ environment = create(:environment)
+
+ environment.merge_request = merge_request
+
+ expect(environment).not_to be_valid
+ end
+
+ it 'for an environment that has a merge request associated' do
+ environment = create(:environment, merge_request: merge_request)
+
+ environment.merge_request = nil
+
+ expect(environment).not_to be_valid
+ end
+ end
end
describe 'validate and sanitize external url' do
@@ -318,6 +340,16 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
end
end
+ describe '.for_type' do
+ it 'filters by type' do
+ create(:environment)
+ create(:environment, name: 'type1/prod')
+ env = create(:environment, name: 'type2/prod')
+
+ expect(described_class.for_type('type2')).to contain_exactly(env)
+ end
+ end
+
describe '#guess_tier' do
using RSpec::Parameterized::TableSyntax
@@ -938,6 +970,26 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
end
end
+ describe 'Last deployment relations' do
+ Deployment::FINISHED_STATUSES.each do |status|
+ it "returns the last #{status} deployment" do
+ create(:deployment, status.to_sym, environment: environment, finished_at: 1.day.ago)
+ expected = create(:deployment, status.to_sym, environment: environment, finished_at: Time.current)
+
+ expect(environment.public_send(:"last_#{status}_deployment")).to eq(expected)
+ end
+ end
+
+ Deployment::UPCOMING_STATUSES.each do |status|
+ it "returns the last #{status} deployment" do
+ create(:deployment, status.to_sym, environment: environment)
+ expected = create(:deployment, status.to_sym, environment: environment)
+
+ expect(environment.public_send(:"last_#{status}_deployment")).to eq(expected)
+ end
+ end
+ end
+
describe '#last_deployable' do
subject { environment.last_deployable }
@@ -1573,11 +1625,38 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
expect(environment.auto_stop_in).to eq(expected_result)
else
+ expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
+ an_instance_of(expected_result),
+ project_id: environment.project_id,
+ environment_id: environment.id
+ )
+
expect { subject }.to raise_error(expected_result)
end
end
end
end
+
+ context 'resets earlier value' do
+ let(:environment) { create(:environment, auto_stop_at: 1.day.since.round) }
+
+ where(:value, :expected_result) do
+ '2 days' | 2.days.to_i
+ '1 week' | 1.week.to_i
+ '2h20min' | 2.hours.to_i + 20.minutes.to_i
+ '' | nil
+ 'never' | nil
+ end
+ with_them do
+ it 'assigns new value' do
+ freeze_time do
+ subject
+
+ expect(environment.auto_stop_in).to eq(expected_result)
+ end
+ end
+ end
+ end
end
describe '.for_id_and_slug' do
diff --git a/spec/models/error_tracking/project_error_tracking_setting_spec.rb b/spec/models/error_tracking/project_error_tracking_setting_spec.rb
index 0685144dea6..30e73d84cfb 100644
--- a/spec/models/error_tracking/project_error_tracking_setting_spec.rb
+++ b/spec/models/error_tracking/project_error_tracking_setting_spec.rb
@@ -187,9 +187,38 @@ RSpec.describe ErrorTracking::ProjectErrorTrackingSetting do
end
end
+ describe '#reactive_cache_limit_enabled?' do
+ subject { setting.reactive_cache_limit_enabled? }
+
+ it { is_expected.to eq(true) }
+
+ context 'when feature flag disabled' do
+ before do
+ stub_feature_flags(error_tracking_sentry_limit: false)
+ end
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
describe '#sentry_client' do
- it 'returns sentry client' do
- expect(subject.sentry_client).to be_a(ErrorTracking::SentryClient)
+ subject { setting.sentry_client }
+
+ it { is_expected.to be_a(ErrorTracking::SentryClient) }
+ it { is_expected.to have_attributes(url: setting.api_url, token: setting.token) }
+
+ describe '#validate_size_guarded_by_feature_flag?' do
+ subject { setting.sentry_client.validate_size_guarded_by_feature_flag? }
+
+ it { is_expected.to eq(true) }
+
+ context 'when feature flag disabled' do
+ before do
+ stub_feature_flags(error_tracking_sentry_limit: false)
+ end
+
+ it { is_expected.to eq(false) }
+ end
end
end
@@ -222,70 +251,39 @@ RSpec.describe ErrorTracking::ProjectErrorTrackingSetting do
end
end
- context 'when sentry client raises ErrorTracking::SentryClient::Error' do
- before do
- synchronous_reactive_cache(subject)
-
- allow(subject).to receive(:sentry_client).and_return(sentry_client)
- allow(sentry_client).to receive(:list_issues).with(opts)
- .and_raise(ErrorTracking::SentryClient::Error, 'error message')
- end
-
- it 'returns error' do
- expect(result).to eq(
- error: 'error message',
- error_type: ErrorTracking::ProjectErrorTrackingSetting::SENTRY_API_ERROR_TYPE_NON_20X_RESPONSE
- )
- end
- end
-
- context 'when sentry client raises ErrorTracking::SentryClient::MissingKeysError' do
- before do
- synchronous_reactive_cache(subject)
-
- allow(subject).to receive(:sentry_client).and_return(sentry_client)
- allow(sentry_client).to receive(:list_issues).with(opts)
- .and_raise(ErrorTracking::SentryClient::MissingKeysError,
- 'Sentry API response is missing keys. key not found: "id"')
- end
-
- it 'returns error' do
- expect(result).to eq(
- error: 'Sentry API response is missing keys. key not found: "id"',
- error_type: ErrorTracking::ProjectErrorTrackingSetting::SENTRY_API_ERROR_TYPE_MISSING_KEYS
- )
- end
- end
+ describe 'client errors' do
+ using RSpec::Parameterized::TableSyntax
- context 'when sentry client raises ErrorTracking::SentryClient::ResponseInvalidSizeError' do
- let(:error_msg) { "Sentry API response is too big. Limit is #{Gitlab::Utils::DeepSize.human_default_max_size}." }
+ sc = ErrorTracking::SentryClient
+ pets = described_class
+ msg = 'something'
before do
synchronous_reactive_cache(subject)
allow(subject).to receive(:sentry_client).and_return(sentry_client)
- allow(sentry_client).to receive(:list_issues).with(opts)
- .and_raise(ErrorTracking::SentryClient::ResponseInvalidSizeError, error_msg)
end
- it 'returns error' do
- expect(result).to eq(
- error: error_msg,
- error_type: ErrorTracking::ProjectErrorTrackingSetting::SENTRY_API_ERROR_INVALID_SIZE
- )
+ where(:exception, :error_type, :error_message) do
+ sc::Error | pets::SENTRY_API_ERROR_TYPE_NON_20X_RESPONSE | msg
+ sc::MissingKeysError | pets::SENTRY_API_ERROR_TYPE_MISSING_KEYS | msg
+ sc::ResponseInvalidSizeError | pets::SENTRY_API_ERROR_INVALID_SIZE | msg
+ sc::BadRequestError | pets::SENTRY_API_ERROR_TYPE_BAD_REQUEST | msg
+ StandardError | nil | 'Unexpected Error'
end
- end
- context 'when sentry client raises StandardError' do
- before do
- synchronous_reactive_cache(subject)
+ with_them do
+ it 'returns an error' do
+ allow(sentry_client).to receive(:list_issues).with(opts)
+ .and_raise(exception, msg)
- allow(subject).to receive(:sentry_client).and_return(sentry_client)
- allow(sentry_client).to receive(:list_issues).with(opts).and_raise(StandardError)
- end
+ expected_result = {
+ error: error_message,
+ error_type: error_type
+ }.compact
- it 'returns error' do
- expect(result).to eq(error: 'Unexpected Error')
+ expect(result).to eq(expected_result)
+ end
end
end
end
diff --git a/spec/models/group_group_link_spec.rb b/spec/models/group_group_link_spec.rb
index 969987c7e64..eec8fe0ef71 100644
--- a/spec/models/group_group_link_spec.rb
+++ b/spec/models/group_group_link_spec.rb
@@ -44,6 +44,17 @@ RSpec.describe GroupGroupLink do
end
end
+ describe '.with_owner_access' do
+ let_it_be(:group_group_link_maintainer) { create :group_group_link, :maintainer }
+ let_it_be(:group_group_link_owner) { create :group_group_link, :owner }
+ let_it_be(:group_group_link_reporter) { create :group_group_link, :reporter }
+ let_it_be(:group_group_link_guest) { create :group_group_link, :guest }
+
+ it 'returns all records which have OWNER access' do
+ expect(described_class.with_owner_access).to match_array([group_group_link_owner])
+ end
+ end
+
context 'for access via group shares' do
let_it_be(:shared_with_group_1) { create(:group) }
let_it_be(:shared_with_group_2) { create(:group) }
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 61662411ac8..2ce75fb1290 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -707,7 +707,8 @@ RSpec.describe Group do
end
describe '.public_or_visible_to_user' do
- let!(:private_group) { create(:group, :private) }
+ let!(:private_group) { create(:group, :private) }
+ let!(:private_subgroup) { create(:group, :private, parent: private_group) }
let!(:internal_group) { create(:group, :internal) }
subject { described_class.public_or_visible_to_user(user) }
@@ -731,6 +732,10 @@ RSpec.describe Group do
end
it { is_expected.to match_array([private_group, internal_group, group]) }
+
+ it 'does not have access to subgroups (see accessible_to_user scope)' do
+ is_expected.not_to include(private_subgroup)
+ end
end
context 'when user is a member of private subgroup' do
@@ -839,6 +844,36 @@ RSpec.describe Group do
expect(described_class.by_ids_or_paths([new_group.id], [group_path])).to match_array([group, new_group])
end
end
+
+ describe 'accessible_to_user' do
+ subject { described_class.accessible_to_user(user) }
+
+ let_it_be(:public_group) { create(:group, :public) }
+ let_it_be(:unaccessible_group) { create(:group, :private) }
+ let_it_be(:unaccessible_subgroup) { create(:group, :private, parent: unaccessible_group) }
+ let_it_be(:accessible_group) { create(:group, :private) }
+ let_it_be(:accessible_subgroup) { create(:group, :private, parent: accessible_group) }
+
+ context 'when user is nil' do
+ let(:user) { nil }
+
+ it { is_expected.to match_array([group, public_group]) }
+ end
+
+ context 'when user is present' do
+ let(:user) { create(:user) }
+
+ it { is_expected.to match_array([group, internal_group, public_group]) }
+
+ context 'when user has access to accessible group' do
+ before do
+ accessible_group.add_developer(user)
+ end
+
+ it { is_expected.to match_array([group, internal_group, public_group, accessible_group, accessible_subgroup]) }
+ end
+ end
+ end
end
describe '#to_reference' do
@@ -1857,56 +1892,31 @@ RSpec.describe Group do
end
end
- describe '#update_two_factor_requirement' do
- let(:user) { create(:user) }
+ describe '#update_two_factor_requirement_for_members' do
+ let_it_be_with_reload(:user) { create(:user) }
context 'group membership' do
- before do
+ it 'enables two_factor_requirement for group members' do
group.add_member(user, GroupMember::OWNER)
- end
-
- it 'is called when require_two_factor_authentication is changed' do
- expect_any_instance_of(User).to receive(:update_two_factor_requirement)
-
group.update!(require_two_factor_authentication: true)
- end
-
- it 'is called when two_factor_grace_period is changed' do
- expect_any_instance_of(User).to receive(:update_two_factor_requirement)
-
- group.update!(two_factor_grace_period: 23)
- end
- it 'is not called when other attributes are changed' do
- expect_any_instance_of(User).not_to receive(:update_two_factor_requirement)
+ group.update_two_factor_requirement_for_members
- group.update!(description: 'foobar')
+ expect(user.reload.require_two_factor_authentication_from_group).to be_truthy
end
- it 'calls #update_two_factor_requirement on each group member' do
- other_user = create(:user)
- group.add_member(other_user, GroupMember::OWNER)
-
- calls = 0
- allow_any_instance_of(User).to receive(:update_two_factor_requirement) do
- calls += 1
- end
+ it 'disables two_factor_requirement for group members' do
+ user.update!(require_two_factor_authentication_from_group: true)
+ group.add_member(user, GroupMember::OWNER)
+ group.update!(require_two_factor_authentication: false)
- group.update!(require_two_factor_authentication: true, two_factor_grace_period: 23)
+ group.update_two_factor_requirement_for_members
- expect(calls).to eq 2
+ expect(user.reload.require_two_factor_authentication_from_group).to be_falsey
end
end
context 'sub groups and projects' do
- it 'enables two_factor_requirement for group member' do
- group.add_member(user, GroupMember::OWNER)
-
- group.update!(require_two_factor_authentication: true)
-
- expect(user.reload.require_two_factor_authentication_from_group).to be_truthy
- end
-
context 'expanded group members' do
let(:indirect_user) { create(:user) }
@@ -1915,9 +1925,10 @@ RSpec.describe Group do
it 'enables two_factor_requirement for subgroup member' do
subgroup = create(:group, :nested, parent: group)
subgroup.add_member(indirect_user, GroupMember::OWNER)
-
group.update!(require_two_factor_authentication: true)
+ group.update_two_factor_requirement_for_members
+
expect(indirect_user.reload.require_two_factor_authentication_from_group).to be_truthy
end
end
@@ -1926,9 +1937,10 @@ RSpec.describe Group do
it 'enables two_factor_requirement for subgroup member' do
subgroup = create(:group, :nested, parent: group, require_two_factor_authentication: true)
subgroup.add_member(indirect_user, GroupMember::OWNER)
-
group.update!(require_two_factor_authentication: false)
+ group.update_two_factor_requirement_for_members
+
expect(indirect_user.reload.require_two_factor_authentication_from_group).to be_truthy
end
@@ -1936,9 +1948,10 @@ RSpec.describe Group do
ancestor_group = create(:group)
ancestor_group.add_member(indirect_user, GroupMember::OWNER)
group.update!(parent: ancestor_group)
-
group.update!(require_two_factor_authentication: true)
+ group.update_two_factor_requirement_for_members
+
expect(indirect_user.reload.require_two_factor_authentication_from_group).to be_truthy
end
end
@@ -1949,9 +1962,10 @@ RSpec.describe Group do
it 'enables two_factor_requirement for subgroup member' do
subgroup = create(:group, :nested, parent: group)
subgroup.add_member(indirect_user, GroupMember::OWNER)
-
group.update!(require_two_factor_authentication: true)
+ group.update_two_factor_requirement_for_members
+
expect(indirect_user.reload.require_two_factor_authentication_from_group).to be_truthy
end
end
@@ -1960,9 +1974,10 @@ RSpec.describe Group do
it 'disables two_factor_requirement for subgroup member' do
subgroup = create(:group, :nested, parent: group)
subgroup.add_member(indirect_user, GroupMember::OWNER)
-
group.update!(require_two_factor_authentication: false)
+ group.update_two_factor_requirement_for_members
+
expect(indirect_user.reload.require_two_factor_authentication_from_group).to be_falsey
end
@@ -1970,9 +1985,10 @@ RSpec.describe Group do
ancestor_group = create(:group, require_two_factor_authentication: false)
indirect_user.update!(require_two_factor_authentication_from_group: true)
ancestor_group.add_member(indirect_user, GroupMember::OWNER)
-
group.update!(require_two_factor_authentication: false)
+ group.update_two_factor_requirement_for_members
+
expect(indirect_user.reload.require_two_factor_authentication_from_group).to be_falsey
end
end
@@ -1983,9 +1999,10 @@ RSpec.describe Group do
it 'does not enable two_factor_requirement for child project member' do
project = create(:project, group: group)
project.add_maintainer(user)
-
group.update!(require_two_factor_authentication: true)
+ group.update_two_factor_requirement_for_members
+
expect(user.reload.require_two_factor_authentication_from_group).to be_falsey
end
@@ -1993,15 +2010,36 @@ RSpec.describe Group do
subgroup = create(:group, :nested, parent: group)
project = create(:project, group: subgroup)
project.add_maintainer(user)
-
group.update!(require_two_factor_authentication: true)
+ group.update_two_factor_requirement_for_members
+
expect(user.reload.require_two_factor_authentication_from_group).to be_falsey
end
end
end
end
+ describe '#update_two_factor_requirement' do
+ it 'enqueues a job when require_two_factor_authentication is changed' do
+ expect(Groups::UpdateTwoFactorRequirementForMembersWorker).to receive(:perform_async).with(group.id)
+
+ group.update!(require_two_factor_authentication: true)
+ end
+
+ it 'enqueues a job when two_factor_grace_period is changed' do
+ expect(Groups::UpdateTwoFactorRequirementForMembersWorker).to receive(:perform_async).with(group.id)
+
+ group.update!(two_factor_grace_period: 23)
+ end
+
+ it 'does not enqueue a job when other attributes are changed' do
+ expect(Groups::UpdateTwoFactorRequirementForMembersWorker).not_to receive(:perform_async).with(group.id)
+
+ group.update!(description: 'foobar')
+ end
+ end
+
describe '#path_changed_hook' do
let(:system_hook_service) { SystemHooksService.new }
@@ -2043,201 +2081,6 @@ RSpec.describe Group do
end
end
- describe '#ci_variables_for' do
- let(:project) { create(:project, group: group) }
- let(:environment_scope) { '*' }
-
- let!(:ci_variable) do
- create(:ci_group_variable, value: 'secret', group: group, environment_scope: environment_scope)
- end
-
- let!(:protected_variable) do
- create(:ci_group_variable, :protected, value: 'protected', group: group)
- end
-
- subject { group.ci_variables_for('ref', project) }
-
- it 'memoizes the result by ref and environment', :request_store do
- scoped_variable = create(:ci_group_variable, value: 'secret', group: group, environment_scope: 'scoped')
-
- expect(project).to receive(:protected_for?).with('ref').once.and_return(true)
- expect(project).to receive(:protected_for?).with('other').twice.and_return(false)
-
- 2.times do
- expect(group.ci_variables_for('ref', project, environment: 'production')).to contain_exactly(ci_variable, protected_variable)
- expect(group.ci_variables_for('other', project)).to contain_exactly(ci_variable)
- expect(group.ci_variables_for('other', project, environment: 'scoped')).to contain_exactly(ci_variable, scoped_variable)
- end
- end
-
- shared_examples 'ref is protected' do
- it 'contains all the variables' do
- is_expected.to contain_exactly(ci_variable, protected_variable)
- end
- end
-
- context 'when the ref is not protected' do
- before do
- stub_application_setting(
- default_branch_protection: Gitlab::Access::PROTECTION_NONE)
- end
-
- it 'contains only the CI variables' do
- is_expected.to contain_exactly(ci_variable)
- end
- end
-
- context 'when the ref is a protected branch' do
- before do
- allow(project).to receive(:protected_for?).with('ref').and_return(true)
- end
-
- it_behaves_like 'ref is protected'
- end
-
- context 'when the ref is a protected tag' do
- before do
- allow(project).to receive(:protected_for?).with('ref').and_return(true)
- end
-
- it_behaves_like 'ref is protected'
- end
-
- context 'when environment name is specified' do
- let(:environment) { 'review/name' }
-
- subject do
- group.ci_variables_for('ref', project, environment: environment)
- end
-
- context 'when environment scope is exactly matched' do
- let(:environment_scope) { 'review/name' }
-
- it { is_expected.to contain_exactly(ci_variable) }
- end
-
- context 'when environment scope is matched by wildcard' do
- let(:environment_scope) { 'review/*' }
-
- it { is_expected.to contain_exactly(ci_variable) }
- end
-
- context 'when environment scope does not match' do
- let(:environment_scope) { 'review/*/special' }
-
- it { is_expected.not_to contain_exactly(ci_variable) }
- end
-
- context 'when environment scope has _' do
- let(:environment_scope) { '*_*' }
-
- it 'does not treat it as wildcard' do
- is_expected.not_to contain_exactly(ci_variable)
- end
-
- context 'when environment name contains underscore' do
- let(:environment) { 'foo_bar/test' }
- let(:environment_scope) { 'foo_bar/*' }
-
- it 'matches literally for _' do
- is_expected.to contain_exactly(ci_variable)
- end
- end
- end
-
- # The environment name and scope cannot have % at the moment,
- # but we're considering relaxing it and we should also make sure
- # it doesn't break in case some data sneaked in somehow as we're
- # not checking this integrity in database level.
- context 'when environment scope has %' do
- it 'does not treat it as wildcard' do
- ci_variable.update_attribute(:environment_scope, '*%*')
-
- is_expected.not_to contain_exactly(ci_variable)
- end
-
- context 'when environment name contains a percent' do
- let(:environment) { 'foo%bar/test' }
-
- it 'matches literally for %' do
- ci_variable.update_attribute(:environment_scope, 'foo%bar/*')
-
- is_expected.to contain_exactly(ci_variable)
- end
- end
- end
-
- context 'when variables with the same name have different environment scopes' do
- let!(:partially_matched_variable) do
- create(:ci_group_variable,
- key: ci_variable.key,
- value: 'partial',
- environment_scope: 'review/*',
- group: group)
- end
-
- let!(:perfectly_matched_variable) do
- create(:ci_group_variable,
- key: ci_variable.key,
- value: 'prefect',
- environment_scope: 'review/name',
- group: group)
- end
-
- it 'puts variables matching environment scope more in the end' do
- is_expected.to eq(
- [ci_variable,
- partially_matched_variable,
- perfectly_matched_variable])
- end
- end
- end
-
- context 'when group has children' do
- let(:group_child) { create(:group, parent: group) }
- let(:group_child_2) { create(:group, parent: group_child) }
- let(:group_child_3) { create(:group, parent: group_child_2) }
- let(:variable_child) { create(:ci_group_variable, group: group_child) }
- let(:variable_child_2) { create(:ci_group_variable, group: group_child_2) }
- let(:variable_child_3) { create(:ci_group_variable, group: group_child_3) }
-
- before do
- allow(project).to receive(:protected_for?).with('ref').and_return(true)
- end
-
- context 'traversal queries' do
- shared_examples 'correct ancestor order' do
- it 'returns all variables belong to the group and parent groups' do
- expected_array1 = [protected_variable, ci_variable]
- expected_array2 = [variable_child, variable_child_2, variable_child_3]
- got_array = group_child_3.ci_variables_for('ref', project).to_a
-
- expect(got_array.shift(2)).to contain_exactly(*expected_array1)
- expect(got_array).to eq(expected_array2)
- end
- end
-
- context 'recursive' do
- before do
- stub_feature_flags(use_traversal_ids: false)
- end
-
- include_examples 'correct ancestor order'
- end
-
- context 'linear' do
- before do
- stub_feature_flags(use_traversal_ids: true)
-
- group_child_3.reload # make sure traversal_ids are reloaded
- end
-
- include_examples 'correct ancestor order'
- end
- end
- end
- end
-
describe '#highest_group_member' do
let(:nested_group) { create(:group, parent: group) }
let(:nested_group_2) { create(:group, parent: nested_group) }
@@ -2331,8 +2174,7 @@ RSpec.describe Group do
let(:another_shared_with_group) { create(:group, parent: group) }
before do
- create(:group_group_link, shared_group: nested_group,
- shared_with_group: another_shared_with_group)
+ create(:group_group_link, shared_group: nested_group, shared_with_group: another_shared_with_group)
end
it 'returns all shared with group ids' do
@@ -3465,6 +3307,23 @@ RSpec.describe Group do
end
end
+ describe '#packages_policy_subject' do
+ it 'returns wrapper' do
+ expect(group.packages_policy_subject).to be_a(Packages::Policies::Group)
+ expect(group.packages_policy_subject.group).to eq(group)
+ end
+
+ context 'with feature flag disabled' do
+ before do
+ stub_feature_flags(read_package_policy_rule: false)
+ end
+
+ it 'returns group' do
+ expect(group.packages_policy_subject).to eq(group)
+ end
+ end
+ end
+
describe '#gitlab_deploy_token' do
subject(:gitlab_deploy_token) { group.gitlab_deploy_token }
diff --git a/spec/models/incident_management/timeline_event_spec.rb b/spec/models/incident_management/timeline_event_spec.rb
index 9f4011fe6a7..fea391acda3 100644
--- a/spec/models/incident_management/timeline_event_spec.rb
+++ b/spec/models/incident_management/timeline_event_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe IncidentManagement::TimelineEvent do
it { is_expected.to validate_length_of(:action).is_at_most(128) }
end
- describe '.order_occurred_at_asc' do
+ describe '.order_occurred_at_asc_id_asc' do
let_it_be(:occurred_3mins_ago) do
create(:incident_management_timeline_event, project: project, occurred_at: 3.minutes.ago)
end
@@ -38,11 +38,23 @@ RSpec.describe IncidentManagement::TimelineEvent do
create(:incident_management_timeline_event, project: project, occurred_at: 2.minutes.ago)
end
- subject(:order) { described_class.order_occurred_at_asc }
+ subject(:order) { described_class.order_occurred_at_asc_id_asc }
it 'sorts timeline events by occurred_at' do
is_expected.to eq([occurred_3mins_ago, occurred_2mins_ago, timeline_event])
end
+
+ context 'when two events occured at the same time' do
+ let_it_be(:also_occurred_2mins_ago) do
+ create(:incident_management_timeline_event, project: project, occurred_at: occurred_2mins_ago.occurred_at)
+ end
+
+ it 'sorts timeline events by occurred_at then sorts by id' do
+ occurred_2mins_ago.touch # Interact with record of earlier id to switch default DB ordering
+
+ is_expected.to eq([occurred_3mins_ago, occurred_2mins_ago, also_occurred_2mins_ago, timeline_event])
+ end
+ end
end
describe '#cache_markdown_field' do
diff --git a/spec/models/integrations/chat_message/pipeline_message_spec.rb b/spec/models/integrations/chat_message/pipeline_message_spec.rb
index 68ef0ccb2e4..a63cc0b6d83 100644
--- a/spec/models/integrations/chat_message/pipeline_message_spec.rb
+++ b/spec/models/integrations/chat_message/pipeline_message_spec.rb
@@ -49,8 +49,8 @@ RSpec.describe Integrations::ChatMessage::PipelineMessage do
allow(test_project).to receive(:avatar_url).with(only_path: false).and_return(args[:project][:avatar_url])
allow(Project).to receive(:find) { test_project }
- test_pipeline = double("A test pipeline", has_yaml_errors?: has_yaml_errors,
- yaml_errors: "yaml error description here")
+ test_pipeline = double("A test pipeline",
+ has_yaml_errors?: has_yaml_errors, yaml_errors: "yaml error description here")
allow(Ci::Pipeline).to receive(:find) { test_pipeline }
allow(Gitlab::UrlBuilder).to receive(:build).with(test_commit).and_return("http://example.com/commit")
diff --git a/spec/models/integrations/datadog_spec.rb b/spec/models/integrations/datadog_spec.rb
index cfc44b22a84..4ac684e8ff0 100644
--- a/spec/models/integrations/datadog_spec.rb
+++ b/spec/models/integrations/datadog_spec.rb
@@ -203,13 +203,10 @@ RSpec.describe Integrations::Datadog do
end
before do
- stub_feature_flags(datadog_integration_logs_collection: enable_logs_collection)
stub_request(:post, expected_hook_url)
saved_instance.execute(data)
end
- let(:enable_logs_collection) { true }
-
context 'with pipeline data' do
let(:data) { pipeline_data }
let(:expected_headers) { { ::Gitlab::WebHooks::GITLAB_EVENT_HEADER => 'Pipeline Hook' } }
@@ -232,12 +229,6 @@ RSpec.describe Integrations::Datadog do
let(:expected_body) { data.to_json }
it { expect(a_request(:post, expected_hook_url).with(headers: expected_headers, body: expected_body)).to have_been_made }
-
- context 'but feature flag disabled' do
- let(:enable_logs_collection) { false }
-
- it { expect(a_request(:post, expected_hook_url)).not_to have_been_made }
- end
end
end
end
diff --git a/spec/models/integrations/discord_spec.rb b/spec/models/integrations/discord_spec.rb
index b85620782c1..eb90acc73be 100644
--- a/spec/models/integrations/discord_spec.rb
+++ b/spec/models/integrations/discord_spec.rb
@@ -23,10 +23,10 @@ RSpec.describe Integrations::Discord do
describe '#execute' do
include StubRequests
+ let_it_be(:project) { create(:project, :repository) }
+
let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
let(:webhook_url) { "https://example.gitlab.com/" }
-
let(:sample_data) do
Gitlab::DataBuilder::Push.build_sample(project, user)
end
diff --git a/spec/models/integrations/drone_ci_spec.rb b/spec/models/integrations/drone_ci_spec.rb
index 8a51f8a0705..905fee075ad 100644
--- a/spec/models/integrations/drone_ci_spec.rb
+++ b/spec/models/integrations/drone_ci_spec.rb
@@ -175,9 +175,9 @@ RSpec.describe Integrations::DroneCi, :use_clean_rails_memory_store_caching do
end
{
- "killed" => :canceled,
+ "killed" => :canceled,
"failure" => :failed,
- "error" => :failed,
+ "error" => :failed,
"success" => "success"
}.each do |drone_status, our_status|
it "sets commit status to #{our_status.inspect} when returned status is #{drone_status.inspect}" do
diff --git a/spec/models/integrations/hangouts_chat_spec.rb b/spec/models/integrations/hangouts_chat_spec.rb
index 17b40c484f5..828bcdf5d8f 100644
--- a/spec/models/integrations/hangouts_chat_spec.rb
+++ b/spec/models/integrations/hangouts_chat_spec.rb
@@ -12,4 +12,175 @@ RSpec.describe Integrations::HangoutsChat do
}
end
end
+
+ let(:chat_integration) { described_class.new }
+ let(:webhook_url) { 'https://example.gitlab.com/' }
+ let(:webhook_url_regex) { /\A#{webhook_url}.*/ }
+
+ describe "#execute" do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :repository, :wiki_repo) }
+
+ before do
+ allow(chat_integration).to receive_messages(
+ project: project,
+ project_id: project.id,
+ webhook: webhook_url
+ )
+
+ WebMock.stub_request(:post, webhook_url_regex)
+ end
+
+ context 'with push events' do
+ let(:push_sample_data) do
+ Gitlab::DataBuilder::Push.build_sample(project, user)
+ end
+
+ it "adds thread key for push events" do
+ expect(chat_integration.execute(push_sample_data)).to be(true)
+
+ expect(WebMock).to have_requested(:post, webhook_url)
+ .with(query: hash_including({ "threadKey" => /push .*?/ }))
+ .once
+ end
+ end
+
+ context 'with issue events' do
+ let(:issues_sample_data) { create(:issue).to_hook_data(user) }
+
+ it "adds thread key for issue events" do
+ expect(chat_integration.execute(issues_sample_data)).to be(true)
+
+ expect(WebMock).to have_requested(:post, webhook_url)
+ .with(query: hash_including({ "threadKey" => /issue .*?/ }))
+ .once
+ end
+ end
+
+ context 'with merge events' do
+ let(:merge_sample_data) { create(:merge_request).to_hook_data(user) }
+
+ it "adds thread key for merge events" do
+ expect(chat_integration.execute(merge_sample_data)).to be(true)
+
+ expect(WebMock).to have_requested(:post, webhook_url)
+ .with(query: hash_including({ "threadKey" => /merge request .*?/ }))
+ .once
+ end
+ end
+
+ context 'with wiki page events' do
+ let(:wiki_page_sample_data) do
+ Gitlab::DataBuilder::WikiPage.build(create(:wiki_page, message: 'foo'), user, 'create')
+ end
+
+ it "adds thread key for wiki page events" do
+ expect(chat_integration.execute(wiki_page_sample_data)).to be(true)
+
+ expect(WebMock).to have_requested(:post, webhook_url)
+ .with(query: hash_including({ "threadKey" => /wiki_page .*?/ }))
+ .once
+ end
+ end
+
+ context 'with pipeline events' do
+ let(:pipeline) do
+ create(:ci_pipeline, :failed, project: project, sha: project.commit.sha, ref: project.default_branch)
+ end
+
+ let(:pipeline_sample_data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
+
+ it "adds thread key for pipeline events" do
+ expect(chat_integration.execute(pipeline_sample_data)).to be(true)
+
+ expect(WebMock).to have_requested(:post, webhook_url)
+ .with(query: hash_including({ "threadKey" => /pipeline .*?/ }))
+ .once
+ end
+ end
+ end
+
+ describe "Note events" do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :repository, creator: user) }
+
+ before do
+ allow(chat_integration).to receive_messages(
+ project: project,
+ project_id: project.id,
+ webhook: webhook_url
+ )
+
+ WebMock.stub_request(:post, webhook_url_regex)
+ end
+
+ context 'when commit comment event executed' do
+ let(:commit_note) do
+ create(:note_on_commit, author: user,
+ project: project,
+ commit_id: project.repository.commit.id,
+ note: 'a comment on a commit')
+ end
+
+ it "adds thread key" do
+ data = Gitlab::DataBuilder::Note.build(commit_note, user)
+
+ expect(chat_integration.execute(data)).to be(true)
+
+ expect(WebMock).to have_requested(:post, webhook_url)
+ .with(query: hash_including({ "threadKey" => /commit .*?/ }))
+ .once
+ end
+ end
+
+ context 'when merge request comment event executed' do
+ let(:merge_request_note) do
+ create(:note_on_merge_request, project: project,
+ note: "merge request note")
+ end
+
+ it "adds thread key" do
+ data = Gitlab::DataBuilder::Note.build(merge_request_note, user)
+
+ expect(chat_integration.execute(data)).to be(true)
+
+ expect(WebMock).to have_requested(:post, webhook_url)
+ .with(query: hash_including({ "threadKey" => /merge request .*?/ }))
+ .once
+ end
+ end
+
+ context 'when issue comment event executed' do
+ let(:issue_note) do
+ create(:note_on_issue, project: project, note: "issue note")
+ end
+
+ it "adds thread key" do
+ data = Gitlab::DataBuilder::Note.build(issue_note, user)
+
+ expect(chat_integration.execute(data)).to be(true)
+
+ expect(WebMock).to have_requested(:post, webhook_url)
+ .with(query: hash_including({ "threadKey" => /issue .*?/ }))
+ .once
+ end
+ end
+
+ context 'when snippet comment event executed' do
+ let(:snippet_note) do
+ create(:note_on_project_snippet, project: project,
+ note: "snippet note")
+ end
+
+ it "adds thread key" do
+ data = Gitlab::DataBuilder::Note.build(snippet_note, user)
+
+ expect(chat_integration.execute(data)).to be(true)
+
+ expect(WebMock).to have_requested(:post, webhook_url)
+ .with(query: hash_including({ "threadKey" => /snippet .*?/ }))
+ .once
+ end
+ end
+ end
end
diff --git a/spec/models/integrations/harbor_spec.rb b/spec/models/integrations/harbor_spec.rb
index 3952495119a..26b43fa3313 100644
--- a/spec/models/integrations/harbor_spec.rb
+++ b/spec/models/integrations/harbor_spec.rb
@@ -27,6 +27,12 @@ RSpec.describe Integrations::Harbor do
it { is_expected.to allow_value('https://demo.goharbor.io').for(:url) }
end
+ describe 'hostname' do
+ it 'returns the host of the integration url' do
+ expect(harbor_integration.hostname).to eq('demo.goharbor.io')
+ end
+ end
+
describe '#fields' do
it 'returns custom fields' do
expect(harbor_integration.fields.pluck(:name)).to eq(%w[url project_name username password])
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index af4c48775ec..17c3cd17364 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -150,8 +150,8 @@ RSpec.describe Issue do
issue.confidential = false
expect(issue).not_to be_valid
- expect(issue.errors[:confidential])
- .to include('associated parent is confidential and can not have non-confidential children.')
+ expect(issue.errors[:base])
+ .to include(_('A non-confidential issue cannot have a confidential parent.'))
end
it 'allows to make parent not-confidential' do
@@ -172,8 +172,8 @@ RSpec.describe Issue do
issue.confidential = true
expect(issue).not_to be_valid
- expect(issue.errors[:confidential])
- .to include('confidential parent can not be used if there are non-confidential children.')
+ expect(issue.errors[:base])
+ .to include(_('A confidential issue cannot have a parent that already has non-confidential children.'))
end
it 'allows to make child confidential' do
@@ -283,6 +283,14 @@ RSpec.describe Issue do
create(:issue)
end
+
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:issue) { create(:issue) }
+ let(:project) { issue.project }
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_CREATED }
+ let(:user) { issue.author }
+ subject(:service_action) { issue }
+ end
end
context 'issue namespace' do
@@ -1782,20 +1790,4 @@ RSpec.describe Issue do
end
end
end
-
- describe '#full_search' do
- context 'when searching non-english terms' do
- [
- 'abc 中文語',
- '中文語cn',
- '中文語'
- ].each do |term|
- it 'adds extra where clause to match partial index' do
- expect(described_class.full_search(term).to_sql).to include(
- "AND (issues.title NOT SIMILAR TO '[\\u0000-\\u218F]*' OR issues.description NOT SIMILAR TO '[\\u0000-\\u218F]*')"
- )
- end
- end
- end
- end
end
diff --git a/spec/models/jira_connect_installation_spec.rb b/spec/models/jira_connect_installation_spec.rb
index 3d1095845aa..9c1f7c678a9 100644
--- a/spec/models/jira_connect_installation_spec.rb
+++ b/spec/models/jira_connect_installation_spec.rb
@@ -45,4 +45,30 @@ RSpec.describe JiraConnectInstallation do
expect(subject).to contain_exactly(subscription.installation)
end
end
+
+ describe '#oauth_authorization_url' do
+ let_it_be(:installation) { create(:jira_connect_installation) }
+
+ subject { installation.oauth_authorization_url }
+
+ before do
+ allow(Gitlab).to receive_message_chain('config.gitlab.url') { 'http://test.host' }
+ end
+
+ it { is_expected.to eq('http://test.host') }
+
+ context 'with instance_url' do
+ let_it_be(:installation) { create(:jira_connect_installation, instance_url: 'https://gitlab.example.com') }
+
+ it { is_expected.to eq('https://gitlab.example.com') }
+
+ context 'and jira_connect_oauth_self_managed feature is disabled' do
+ before do
+ stub_feature_flags(jira_connect_oauth_self_managed: false)
+ end
+
+ it { is_expected.to eq('http://test.host') }
+ end
+ end
+ end
end
diff --git a/spec/models/loose_foreign_keys/deleted_record_spec.rb b/spec/models/loose_foreign_keys/deleted_record_spec.rb
index 9ee5b7340f3..a909252a78c 100644
--- a/spec/models/loose_foreign_keys/deleted_record_spec.rb
+++ b/spec/models/loose_foreign_keys/deleted_record_spec.rb
@@ -66,7 +66,7 @@ RSpec.describe LooseForeignKeys::DeletedRecord, type: :model do
let(:partition_manager) { Gitlab::Database::Partitioning::PartitionManager.new(described_class) }
describe 'next_partition_if callback' do
- let(:active_partition) { described_class.partitioning_strategy.active_partition.value }
+ let(:active_partition) { described_class.partitioning_strategy.active_partition }
subject(:value) { described_class.partitioning_strategy.next_partition_if.call(active_partition) }
@@ -98,7 +98,7 @@ RSpec.describe LooseForeignKeys::DeletedRecord, type: :model do
end
describe 'detach_partition_if callback' do
- let(:active_partition) { described_class.partitioning_strategy.active_partition.value }
+ let(:active_partition) { described_class.partitioning_strategy.active_partition }
subject(:value) { described_class.partitioning_strategy.detach_partition_if.call(active_partition) }
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index 2716244b7f3..7b75a6ee1c2 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -195,6 +195,15 @@ RSpec.describe Member do
expect(member).not_to be_valid
end
end
+
+ context 'access_level cannot be 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")
+ end
+ end
end
end
end
@@ -880,7 +889,8 @@ RSpec.describe Member do
end
describe 'generate invite token on create' do
- let!(:member) { build(:project_member, invite_email: "user@example.com") }
+ let(:project) { create(:project) }
+ let!(:member) { build(:project_member, invite_email: "user@example.com", project: project) }
it 'sets the invite token' do
expect { member.save! }.to change { member.invite_token }.to(kind_of(String))
diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb
index c6266f15340..363830d21dd 100644
--- a/spec/models/members/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
@@ -74,7 +74,8 @@ RSpec.describe GroupMember do
describe '#update_two_factor_requirement' do
it 'is called after creation and deletion' do
user = build :user
- group_member = build :group_member, user: user
+ group = create :group
+ group_member = build :group_member, user: user, group: group
expect(user).to receive(:update_two_factor_requirement)
@@ -151,8 +152,8 @@ RSpec.describe GroupMember do
context 'when importing' do
it 'does not refresh' do
expect(UserProjectAccessChangedService).not_to receive(:new)
-
- member = build(:group_member)
+ group = create(:group)
+ member = build(:group_member, group: group)
member.importing = true
member.save!
end
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index 99fc5dc14df..ad6f3ca5428 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -201,7 +201,7 @@ RSpec.describe ProjectMember do
it 'does not refresh' do
expect(AuthorizedProjectUpdate::ProjectRecalculatePerUserWorker).not_to receive(:bulk_perform_and_wait)
- member = build(:project_member)
+ member = build(:project_member, project: project)
member.importing = true
member.save!
end
diff --git a/spec/models/merge_request_assignee_spec.rb b/spec/models/merge_request_assignee_spec.rb
index 387d17d7823..73bf7d02468 100644
--- a/spec/models/merge_request_assignee_spec.rb
+++ b/spec/models/merge_request_assignee_spec.rb
@@ -38,26 +38,4 @@ RSpec.describe MergeRequestAssignee do
end
end
end
-
- it_behaves_like 'having unique enum values'
-
- describe '#attention_requested_by' do
- let(:current_user) { create(:user) }
-
- before do
- subject.update!(updated_state_by: current_user, state: :attention_requested)
- end
-
- context 'attention requested' do
- it { expect(subject.attention_requested_by).to eq(current_user) }
- end
-
- context 'attention requested' do
- before do
- subject.update!(state: :reviewed)
- end
-
- it { expect(subject.attention_requested_by).to eq(nil) }
- end
- end
end
diff --git a/spec/models/merge_request_reviewer_spec.rb b/spec/models/merge_request_reviewer_spec.rb
index 4df2dba3a7d..5a29966e4b9 100644
--- a/spec/models/merge_request_reviewer_spec.rb
+++ b/spec/models/merge_request_reviewer_spec.rb
@@ -14,24 +14,4 @@ RSpec.describe MergeRequestReviewer do
it { is_expected.to belong_to(:merge_request).class_name('MergeRequest') }
it { is_expected.to belong_to(:reviewer).class_name('User').inverse_of(:merge_request_reviewers) }
end
-
- describe '#attention_requested_by' do
- let(:current_user) { create(:user) }
-
- before do
- subject.update!(updated_state_by: current_user, state: :attention_requested)
- end
-
- context 'attention requested' do
- it { expect(subject.attention_requested_by).to eq(current_user) }
- end
-
- context 'attention requested' do
- before do
- subject.update!(state: :reviewed)
- end
-
- it { expect(subject.attention_requested_by).to eq(nil) }
- end
- end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 19026a4772d..f27f3b749b1 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -31,6 +31,7 @@ RSpec.describe MergeRequest, factory_default: :keep do
it { is_expected.to have_many(:draft_notes) }
it { is_expected.to have_many(:reviews).inverse_of(:merge_request) }
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) }
context 'for forks' do
let!(:project) { create(:project) }
@@ -144,22 +145,6 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
end
- describe '.attention' do
- let_it_be(:merge_request5) { create(:merge_request, :unique_branches, assignees: [user2]) }
- let_it_be(:merge_request6) { create(:merge_request, :unique_branches, assignees: [user2]) }
-
- before do
- assignee = merge_request6.find_assignee(user2)
- assignee.update!(state: :reviewed)
- merge_request2.find_reviewer(user2).update!(state: :attention_requested)
- merge_request5.find_assignee(user2).update!(state: :attention_requested)
- end
-
- it 'returns MRs that have any attention requests' do
- expect(described_class.attention(user2)).to eq([merge_request2, merge_request5])
- end
- end
-
describe '.drafts' do
it 'returns MRs where draft == true' do
expect(described_class.drafts).to eq([merge_request4])
@@ -884,6 +869,16 @@ RSpec.describe MergeRequest, factory_default: :keep do
expect { subject.cache_merge_request_closes_issues!(subject.author) }
.not_to change(subject.merge_requests_closing_issues, :count)
end
+
+ it 'caches issues from another project with issues enabled' do
+ project = create(:project, :public, issues_enabled: true)
+ issue = create(:issue, project: project)
+ commit = double('commit1', safe_message: "Fixes #{issue.to_reference(full: true)}")
+ allow(subject).to receive(:commits).and_return([commit])
+
+ expect { subject.cache_merge_request_closes_issues!(subject.author) }
+ .to change(subject.merge_requests_closing_issues, :count).by(1)
+ end
end
end
@@ -3232,73 +3227,9 @@ RSpec.describe MergeRequest, factory_default: :keep do
end
end
- describe '#detailed_merge_status' do
- subject(:detailed_merge_status) { merge_request.detailed_merge_status }
-
- context 'when merge status is cannot_be_merged_rechecking' do
- let(:merge_request) { create(:merge_request, merge_status: :cannot_be_merged_rechecking) }
-
- it 'returns :checking' do
- expect(detailed_merge_status).to eq(:checking)
- end
- end
-
- context 'when merge status is preparing' do
- let(:merge_request) { create(:merge_request, merge_status: :preparing) }
-
- it 'returns :checking' do
- expect(detailed_merge_status).to eq(:checking)
- end
- end
-
- context 'when merge status is checking' do
- let(:merge_request) { create(:merge_request, merge_status: :checking) }
-
- it 'returns :checking' do
- expect(detailed_merge_status).to eq(:checking)
- end
- end
-
- context 'when merge status is unchecked' do
- let(:merge_request) { create(:merge_request, merge_status: :unchecked) }
-
- it 'returns :unchecked' do
- expect(detailed_merge_status).to eq(:unchecked)
- end
- end
-
- context 'when merge checks are a success' do
- let(:merge_request) { create(:merge_request) }
-
- it 'returns :mergeable' do
- expect(detailed_merge_status).to eq(:mergeable)
- end
- end
-
- context 'when merge status have a failure' do
- let(:merge_request) { create(:merge_request) }
-
- before do
- merge_request.close!
- end
-
- it 'returns the failure reason' do
- expect(detailed_merge_status).to eq(:not_open)
- end
- end
- end
-
describe '#mergeable_state?' do
it_behaves_like 'for mergeable_state'
- context 'when improved_mergeability_checks is off' do
- before do
- stub_feature_flags(improved_mergeability_checks: false)
- end
-
- it_behaves_like 'for mergeable_state'
- end
-
context 'when merge state caching is off' do
before do
stub_feature_flags(mergeability_caching: false)
@@ -3743,9 +3674,9 @@ RSpec.describe MergeRequest, factory_default: :keep do
let(:expected_diff_refs) do
Gitlab::Diff::DiffRefs.new(
- base_sha: subject.merge_request_diff.base_commit_sha,
+ base_sha: subject.merge_request_diff.base_commit_sha,
start_sha: subject.merge_request_diff.start_commit_sha,
- head_sha: subject.merge_request_diff.head_commit_sha
+ head_sha: subject.merge_request_diff.head_commit_sha
)
end
diff --git a/spec/models/ml/candidate_spec.rb b/spec/models/ml/candidate_spec.rb
index a48e291fa55..f58d30f81a0 100644
--- a/spec/models/ml/candidate_spec.rb
+++ b/spec/models/ml/candidate_spec.rb
@@ -9,4 +9,35 @@ RSpec.describe Ml::Candidate do
it { is_expected.to have_many(:params) }
it { is_expected.to have_many(:metrics) }
end
+
+ describe '#new' do
+ it 'iid is not null' do
+ expect(create(:ml_candidates).iid).not_to be_nil
+ end
+ end
+
+ describe 'by_project_id_and_iid' do
+ let_it_be(:candidate) { create(:ml_candidates) }
+
+ let(:project_id) { candidate.experiment.project_id }
+ let(:iid) { candidate.iid }
+
+ subject { described_class.with_project_id_and_iid(project_id, iid) }
+
+ context 'when iid exists', 'and belongs to project' do
+ it { is_expected.to eq(candidate) }
+ end
+
+ context 'when iid exists', 'and does not belong to project' do
+ let(:project_id) { non_existing_record_id }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when iid does not exist' do
+ let(:iid) { 'a' }
+
+ it { is_expected.to be_nil }
+ end
+ end
end
diff --git a/spec/models/ml/experiment_spec.rb b/spec/models/ml/experiment_spec.rb
index dca5280a8fe..e300f82d290 100644
--- a/spec/models/ml/experiment_spec.rb
+++ b/spec/models/ml/experiment_spec.rb
@@ -8,4 +8,55 @@ RSpec.describe Ml::Experiment do
it { is_expected.to belong_to(:user) }
it { is_expected.to have_many(:candidates) }
end
+
+ describe '#by_project_id_and_iid?' do
+ let(:exp) { create(:ml_experiments) }
+ let(:iid) { exp.iid }
+
+ subject { described_class.by_project_id_and_iid(exp.project_id, iid) }
+
+ context 'if exists' do
+ it { is_expected.to eq(exp) }
+ end
+
+ context 'if does not exist' do
+ let(:iid) { non_existing_record_id }
+
+ it { is_expected.to be(nil) }
+ end
+ end
+
+ describe '#by_project_id_and_name?' do
+ let(:exp) { create(:ml_experiments) }
+ let(:exp_name) { exp.name }
+
+ subject { described_class.by_project_id_and_name(exp.project_id, exp_name) }
+
+ context 'if exists' do
+ it { is_expected.to eq(exp) }
+ end
+
+ context 'if does not exist' do
+ let(:exp_name) { 'hello' }
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ describe '#has_record?' do
+ let(:exp) { create(:ml_experiments) }
+ let(:exp_name) { exp.name }
+
+ subject { described_class.has_record?(exp.project_id, exp_name) }
+
+ context 'if exists' do
+ it { is_expected.to be_truthy }
+ end
+
+ context 'if does not exist' do
+ let(:exp_name) { 'hello' }
+
+ it { is_expected.to be_falsey }
+ end
+ end
end
diff --git a/spec/models/namespace_setting_spec.rb b/spec/models/namespace_setting_spec.rb
index 25234db5734..4ac248802b8 100644
--- a/spec/models/namespace_setting_spec.rb
+++ b/spec/models/namespace_setting_spec.rb
@@ -127,4 +127,54 @@ RSpec.describe NamespaceSetting, type: :model do
end
end
end
+
+ describe '#show_diff_preview_in_email?' do
+ context 'when not a subgroup' do
+ context 'when :show_diff_preview_in_email is false' do
+ it 'returns false' do
+ settings = create(:namespace_settings, show_diff_preview_in_email: false)
+ group = create(:group, namespace_settings: settings )
+
+ expect(group.show_diff_preview_in_email?).to be_falsey
+ end
+ end
+
+ context 'when :show_diff_preview_in_email is true' do
+ it 'returns true' do
+ settings = create(:namespace_settings, show_diff_preview_in_email: true)
+ group = create(:group, namespace_settings: settings )
+
+ expect(group.show_diff_preview_in_email?).to be_truthy
+ end
+ end
+
+ it 'does not query the db when there is no parent group' do
+ group = create(:group)
+
+ expect { group.show_diff_preview_in_email? }.not_to exceed_query_limit(0)
+ end
+ end
+
+ context 'when a group has parent groups' do
+ let(:grandparent) { create(:group, namespace_settings: settings) }
+ let(:parent) { create(:group, parent: grandparent) }
+ let!(:group) { create(:group, parent: parent) }
+
+ context "when a parent group has disabled diff previews" do
+ let(:settings) { create(:namespace_settings, show_diff_preview_in_email: false) }
+
+ it 'returns false' do
+ expect(group.show_diff_preview_in_email?).to be_falsey
+ end
+ end
+
+ context 'when all parent groups have enabled diff previews' do
+ let(:settings) { create(:namespace_settings, show_diff_preview_in_email: true) }
+
+ it 'returns true' do
+ expect(group.show_diff_preview_in_email?).to be_truthy
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 71ce3afda44..2e8d22cb9db 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -336,6 +336,26 @@ RSpec.describe Namespace do
expect(described_class.without_project_namespaces).to match_array([namespace, namespace1, namespace2, namespace1sub, namespace2sub, user_namespace, project_namespace.parent])
end
end
+
+ describe '.with_shared_runners_enabled' do
+ subject { described_class.with_shared_runners_enabled }
+
+ context 'when shared runners are enabled for namespace' do
+ let!(:namespace_inheriting_shared_runners) { create(:namespace, shared_runners_enabled: true) }
+
+ it "returns a namespace inheriting shared runners" do
+ is_expected.to include(namespace_inheriting_shared_runners)
+ end
+ end
+
+ context 'when shared runners are disabled for namespace' do
+ let!(:namespace_not_inheriting_shared_runners) { create(:namespace, shared_runners_enabled: false) }
+
+ it "does not return a namespace not inheriting shared runners" do
+ is_expected.not_to include(namespace_not_inheriting_shared_runners)
+ end
+ end
+ end
end
describe 'delegate' do
@@ -439,19 +459,54 @@ RSpec.describe Namespace do
context 'traversal_ids on create' do
shared_examples 'default traversal_ids' do
- let(:namespace) { build(:namespace) }
-
- before do
- namespace.save!
- namespace.reload
- end
+ let!(:namespace) { create(:group) }
+ let!(:child_namespace) { create(:group, parent: namespace) }
- it { expect(namespace.traversal_ids).to eq [namespace.id] }
+ it { expect(namespace.reload.traversal_ids).to eq [namespace.id] }
+ it { expect(child_namespace.reload.traversal_ids).to eq [namespace.id, child_namespace.id] }
+ it { expect(namespace.sync_events.count).to eq 1 }
+ it { expect(child_namespace.sync_events.count).to eq 1 }
end
it_behaves_like 'default traversal_ids'
end
+ context 'traversal_ids on update' do
+ let!(:namespace1) { create(:group) }
+ let!(:namespace2) { create(:group) }
+
+ it 'updates the traversal_ids when the parent_id is changed' do
+ expect do
+ namespace1.update!(parent: namespace2)
+ end.to change { namespace1.reload.traversal_ids }.from([namespace1.id]).to([namespace2.id, namespace1.id])
+ end
+
+ it 'creates a Namespaces::SyncEvent using triggers' do
+ Namespaces::SyncEvent.delete_all
+ namespace1.update!(parent: namespace2)
+ expect(namespace1.reload.sync_events.count).to eq(1)
+ end
+
+ it 'creates sync_events using database trigger on the table' do
+ expect { Group.update_all(traversal_ids: [-1]) }.to change(Namespaces::SyncEvent, :count).by(2)
+ end
+
+ it 'does not create sync_events using database trigger on the table when only the parent_id has changed' do
+ expect { Group.update_all(parent_id: -1) }.not_to change(Namespaces::SyncEvent, :count)
+ end
+
+ it 'triggers the callback sync_traversal_ids on the namespace' do
+ allow(namespace1).to receive(:run_callbacks).and_call_original
+ expect(namespace1).to receive(:run_callbacks).with(:sync_traversal_ids)
+ namespace1.update!(parent: namespace2)
+ end
+
+ it 'calls schedule_sync_event_worker on the updated namespace' do
+ expect(namespace1).to receive(:schedule_sync_event_worker)
+ namespace1.update!(parent: namespace2)
+ end
+ end
+
describe "after_commit :expire_child_caches" do
let(:namespace) { create(:group) }
@@ -675,6 +730,24 @@ RSpec.describe Namespace do
end
end
+ describe '#any_project_with_shared_runners_enabled?' do
+ subject { namespace.any_project_with_shared_runners_enabled? }
+
+ let!(:project_not_inheriting_shared_runners) do
+ create(:project, namespace: namespace, shared_runners_enabled: false)
+ end
+
+ context 'when a child project has shared runners enabled' do
+ let!(:project_inheriting_shared_runners) { create(:project, namespace: namespace, shared_runners_enabled: true) }
+
+ it { is_expected.to eq true }
+ end
+
+ context 'when all child projects have shared runners disabled' do
+ it { is_expected.to eq false }
+ end
+ end
+
describe '.search' do
let_it_be(:first_group) { create(:group, name: 'my first namespace', path: 'old-path') }
let_it_be(:parent_group) { create(:group, name: 'my parent namespace', path: 'parent-path') }
@@ -744,30 +817,30 @@ RSpec.describe Namespace do
create(:project,
namespace: namespace,
statistics: build(:project_statistics,
- namespace: namespace,
- repository_size: 101,
- wiki_size: 505,
- lfs_objects_size: 202,
- build_artifacts_size: 303,
+ namespace: namespace,
+ repository_size: 101,
+ wiki_size: 505,
+ lfs_objects_size: 202,
+ build_artifacts_size: 303,
pipeline_artifacts_size: 707,
- packages_size: 404,
- snippets_size: 605,
- uploads_size: 808))
+ packages_size: 404,
+ snippets_size: 605,
+ uploads_size: 808))
end
let(:project2) do
create(:project,
namespace: namespace,
statistics: build(:project_statistics,
- namespace: namespace,
- repository_size: 10,
- wiki_size: 50,
- lfs_objects_size: 20,
- build_artifacts_size: 30,
+ namespace: namespace,
+ repository_size: 10,
+ wiki_size: 50,
+ lfs_objects_size: 20,
+ build_artifacts_size: 30,
pipeline_artifacts_size: 70,
- packages_size: 40,
- snippets_size: 60,
- uploads_size: 80))
+ packages_size: 40,
+ snippets_size: 60,
+ uploads_size: 80))
end
it "sums all project storage counters in the namespace" do
@@ -2216,6 +2289,20 @@ RSpec.describe Namespace do
expect(namespace.sync_events.count).to eq(2)
end
+ it 'creates a namespaces_sync_event for the parent and all the descendent namespaces' do
+ children_namespaces = create_list(:group, 2, parent_id: namespace.id)
+ grand_children_namespaces = create_list(:group, 2, parent_id: children_namespaces.first.id)
+ expect(Namespaces::ProcessSyncEventsWorker).to receive(:perform_async).exactly(:once)
+ Namespaces::SyncEvent.delete_all
+
+ expect do
+ namespace.update!(parent_id: new_namespace1.id)
+ end.to change(Namespaces::SyncEvent, :count).by(5)
+
+ expected_ids = [namespace.id] + children_namespaces.map(&:id) + grand_children_namespaces.map(&:id)
+ expect(Namespaces::SyncEvent.pluck(:namespace_id)).to match_array(expected_ids)
+ end
+
it 'enqueues ProcessSyncEventsWorker' do
expect(Namespaces::ProcessSyncEventsWorker).to receive(:perform_async)
diff --git a/spec/models/namespaces/sync_event_spec.rb b/spec/models/namespaces/sync_event_spec.rb
new file mode 100644
index 00000000000..a3a90ba9aff
--- /dev/null
+++ b/spec/models/namespaces/sync_event_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Namespaces::SyncEvent, type: :model do
+ describe '.enqueue_worker' do
+ it 'schedules Namespaces::ProcessSyncEventsWorker job' do
+ expect(::Namespaces::ProcessSyncEventsWorker).to receive(:perform_async)
+ described_class.enqueue_worker
+ end
+ end
+
+ describe '.upper_bound_count' do
+ it 'returns 0 when there are no records in the table' do
+ expect(described_class.upper_bound_count).to eq(0)
+ end
+
+ it 'returns an estimated number of the records in the database' do
+ create_list(:namespace, 3)
+ expect(described_class.upper_bound_count).to eq(3)
+ end
+ end
+end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index ca558848cb0..1fce1f97dcb 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -280,6 +280,32 @@ RSpec.describe Note do
expect { note.destroy! }.not_to raise_error
end
end
+
+ describe 'sets internal flag' do
+ subject(:internal) { note.reload.internal }
+
+ let(:note) { create(:note, confidential: confidential, project: issue.project, noteable: issue) }
+
+ let_it_be(:issue) { create(:issue) }
+
+ context 'when confidential is `true`' do
+ let(:confidential) { true }
+
+ it { is_expected.to be true }
+ end
+
+ context 'when confidential is `false`' do
+ let(:confidential) { false }
+
+ it { is_expected.to be false }
+ end
+
+ context 'when confidential is `nil`' do
+ let(:confidential) { nil }
+
+ it { is_expected.to be false }
+ end
+ end
end
describe "Commit notes" do
diff --git a/spec/models/notification_recipient_spec.rb b/spec/models/notification_recipient_spec.rb
index 4debda0621c..8105262aada 100644
--- a/spec/models/notification_recipient_spec.rb
+++ b/spec/models/notification_recipient_spec.rb
@@ -39,6 +39,56 @@ RSpec.describe NotificationRecipient do
expect(recipient.notifiable?).to eq true
end
end
+
+ context 'when recipient email is blocked', :clean_gitlab_redis_rate_limiting do
+ before do
+ allow(Gitlab::ApplicationRateLimiter).to receive(:rate_limits)
+ .and_return(
+ temporary_email_failure: { threshold: 1, interval: 1.minute },
+ permanent_email_failure: { threshold: 1, interval: 1.minute }
+ )
+ end
+
+ context 'with permanent failures' do
+ before do
+ 2.times { Gitlab::ApplicationRateLimiter.throttled?(:permanent_email_failure, scope: user.email) }
+ end
+
+ it 'returns false' do
+ expect(recipient.notifiable?).to eq(false)
+ end
+
+ context 'when block_emails_with_failures is disabled' do
+ before do
+ stub_feature_flags(block_emails_with_failures: false)
+ end
+
+ it 'returns true' do
+ expect(recipient.notifiable?).to eq(true)
+ end
+ end
+ end
+
+ context 'with temporary failures' do
+ before do
+ 2.times { Gitlab::ApplicationRateLimiter.throttled?(:temporary_email_failure, scope: user.email) }
+ end
+
+ it 'returns false' do
+ expect(recipient.notifiable?).to eq(false)
+ end
+
+ context 'when block_emails_with_failures is disabled' do
+ before do
+ stub_feature_flags(block_emails_with_failures: false)
+ end
+
+ it 'returns true' do
+ expect(recipient.notifiable?).to eq(true)
+ end
+ end
+ end
+ end
end
describe '#has_access?' do
diff --git a/spec/models/oauth_access_token_spec.rb b/spec/models/oauth_access_token_spec.rb
index 544f6643712..a4540ac95bc 100644
--- a/spec/models/oauth_access_token_spec.rb
+++ b/spec/models/oauth_access_token_spec.rb
@@ -3,7 +3,6 @@
require 'spec_helper'
RSpec.describe OauthAccessToken do
- let(:user) { create(:user) }
let(:app_one) { create(:oauth_application) }
let(:app_two) { create(:oauth_application) }
let(:app_three) { create(:oauth_application) }
@@ -69,4 +68,20 @@ RSpec.describe OauthAccessToken do
end
end
end
+
+ describe '.matching_token_for' do
+ it 'does not find existing tokens' do
+ expect(described_class.matching_token_for(app_one, token.resource_owner, token.scopes)).to be_nil
+ end
+
+ context 'when hash oauth tokens is disabled' do
+ before do
+ stub_feature_flags(hash_oauth_tokens: false)
+ end
+
+ it 'finds an existing token' do
+ expect(described_class.matching_token_for(app_one, token.resource_owner, token.scopes)).to be_present
+ end
+ end
+ end
end
diff --git a/spec/models/onboarding/completion_spec.rb b/spec/models/onboarding/completion_spec.rb
new file mode 100644
index 00000000000..e1fad4255bc
--- /dev/null
+++ b/spec/models/onboarding/completion_spec.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Onboarding::Completion do
+ describe '#percentage' do
+ let(:completed_actions) { {} }
+ let!(:onboarding_progress) { create(:onboarding_progress, namespace: namespace, **completed_actions) }
+ let(:tracked_action_columns) do
+ [
+ *described_class::ACTION_ISSUE_IDS.keys,
+ *described_class::ACTION_PATHS,
+ :security_scan_enabled
+ ].map { |key| ::Onboarding::Progress.column_name(key) }
+ end
+
+ let_it_be(:namespace) { create(:namespace) }
+
+ subject { described_class.new(namespace).percentage }
+
+ context 'when no onboarding_progress exists' do
+ subject { described_class.new(build(:namespace)).percentage }
+
+ it { is_expected.to eq(0) }
+ end
+
+ context 'when no action has been completed' do
+ it { is_expected.to eq(0) }
+ end
+
+ context 'when all tracked actions have been completed' do
+ let(:completed_actions) do
+ tracked_action_columns.index_with { Time.current }
+ end
+
+ it { is_expected.to eq(100) }
+ end
+
+ context 'with security_actions_continuous_onboarding experiment' do
+ let(:completed_actions) { Hash[tracked_action_columns.first, Time.current] }
+
+ context 'when control' do
+ before do
+ stub_experiments(security_actions_continuous_onboarding: :control)
+ end
+
+ it { is_expected.to eq(11) }
+ end
+
+ context 'when candidate' do
+ before do
+ stub_experiments(security_actions_continuous_onboarding: :candidate)
+ end
+
+ it { is_expected.to eq(9) }
+ end
+ end
+ end
+end
diff --git a/spec/models/onboarding/learn_gitlab_spec.rb b/spec/models/onboarding/learn_gitlab_spec.rb
new file mode 100644
index 00000000000..5e3e1f9c304
--- /dev/null
+++ b/spec/models/onboarding/learn_gitlab_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Onboarding::LearnGitlab do
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:learn_gitlab_project) { create(:project, name: described_class::PROJECT_NAME) }
+ let_it_be(:learn_gitlab_board) { create(:board, project: learn_gitlab_project, name: described_class::BOARD_NAME) }
+ let_it_be(:learn_gitlab_label) { create(:label, project: learn_gitlab_project, name: described_class::LABEL_NAME) }
+
+ before do
+ learn_gitlab_project.add_developer(current_user)
+ end
+
+ describe '#available?' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:project, :board, :label, :expected_result) do
+ nil | nil | nil | nil
+ nil | nil | true | nil
+ nil | true | nil | nil
+ nil | true | true | nil
+ true | nil | nil | nil
+ true | nil | true | nil
+ true | true | nil | nil
+ true | true | true | true
+ end
+
+ with_them do
+ before do
+ allow_next_instance_of(described_class) do |learn_gitlab|
+ allow(learn_gitlab).to receive(:project).and_return(project)
+ allow(learn_gitlab).to receive(:board).and_return(board)
+ allow(learn_gitlab).to receive(:label).and_return(label)
+ end
+ end
+
+ subject { described_class.new(current_user).available? }
+
+ it { is_expected.to be expected_result }
+ end
+ end
+
+ describe '#project' do
+ subject { described_class.new(current_user).project }
+
+ it { is_expected.to eq learn_gitlab_project }
+
+ context 'when it is created during trial signup' do
+ let_it_be(:learn_gitlab_project) do
+ create(:project, name: described_class::PROJECT_NAME_ULTIMATE_TRIAL, path: 'learn-gitlab-ultimate-trial')
+ end
+
+ it { is_expected.to eq learn_gitlab_project }
+ end
+ end
+
+ describe '#board' do
+ subject { described_class.new(current_user).board }
+
+ it { is_expected.to eq learn_gitlab_board }
+ end
+
+ describe '#label' do
+ subject { described_class.new(current_user).label }
+
+ it { is_expected.to eq learn_gitlab_label }
+ end
+end
diff --git a/spec/models/onboarding/progress_spec.rb b/spec/models/onboarding/progress_spec.rb
new file mode 100644
index 00000000000..9d91af2487a
--- /dev/null
+++ b/spec/models/onboarding/progress_spec.rb
@@ -0,0 +1,317 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Onboarding::Progress do
+ let(:namespace) { create(:namespace) }
+ let(:action) { :subscription_created }
+
+ describe 'associations' do
+ it { is_expected.to belong_to(:namespace).required }
+ end
+
+ describe 'validations' do
+ describe 'namespace_is_root_namespace' do
+ subject(:onboarding_progress) { build(:onboarding_progress, namespace: namespace) }
+
+ context 'when associated namespace is root' do
+ it { is_expected.to be_valid }
+ end
+
+ context 'when associated namespace is not root' do
+ let(:namespace) { build(:group, :nested) }
+
+ it 'is invalid' do
+ expect(onboarding_progress).to be_invalid
+ expect(onboarding_progress.errors[:namespace]).to include('must be a root namespace')
+ end
+ end
+ end
+ end
+
+ describe 'scopes' do
+ describe '.incomplete_actions' do
+ subject { described_class.incomplete_actions(actions) }
+
+ let!(:no_actions_completed) { create(:onboarding_progress) }
+ let!(:one_action_completed_one_action_incompleted) do
+ create(:onboarding_progress, "#{action}_at" => Time.current)
+ end
+
+ context 'when given one action' do
+ let(:actions) { action }
+
+ it { is_expected.to eq [no_actions_completed] }
+ end
+
+ context 'when given an array of actions' do
+ let(:actions) { [action, :git_write] }
+
+ it { is_expected.to eq [no_actions_completed] }
+ end
+ end
+
+ describe '.completed_actions' do
+ subject { described_class.completed_actions(actions) }
+
+ let!(:one_action_completed_one_action_incompleted) do
+ create(:onboarding_progress, "#{action}_at" => Time.current)
+ end
+
+ let!(:both_actions_completed) do
+ create(:onboarding_progress, "#{action}_at" => Time.current, git_write_at: Time.current)
+ end
+
+ context 'when given one action' do
+ let(:actions) { action }
+
+ it { is_expected.to eq [one_action_completed_one_action_incompleted, both_actions_completed] }
+ end
+
+ context 'when given an array of actions' do
+ let(:actions) { [action, :git_write] }
+
+ it { is_expected.to eq [both_actions_completed] }
+ end
+ end
+
+ describe '.completed_actions_with_latest_in_range' do
+ subject do
+ described_class.completed_actions_with_latest_in_range(actions,
+ 1.day.ago.beginning_of_day..1.day.ago.end_of_day)
+ end
+
+ let!(:one_action_completed_in_range_one_action_incompleted) do
+ create(:onboarding_progress, "#{action}_at" => 1.day.ago.middle_of_day)
+ end
+
+ let!(:git_write_action_completed_in_range) { create(:onboarding_progress, git_write_at: 1.day.ago.middle_of_day) }
+ let!(:both_actions_completed_latest_action_out_of_range) do
+ create(:onboarding_progress, "#{action}_at" => 1.day.ago.middle_of_day, git_write_at: Time.current)
+ end
+
+ let!(:both_actions_completed_latest_action_in_range) do
+ create(:onboarding_progress, "#{action}_at" => 1.day.ago.middle_of_day, git_write_at: 2.days.ago.middle_of_day)
+ end
+
+ context 'when given one action' do
+ let(:actions) { :git_write }
+
+ it { is_expected.to eq [git_write_action_completed_in_range] }
+ end
+
+ context 'when given an array of actions' do
+ let(:actions) { [action, :git_write] }
+
+ it { is_expected.to eq [both_actions_completed_latest_action_in_range] }
+ end
+ end
+ end
+
+ describe '.onboard' do
+ subject(:onboard) { described_class.onboard(namespace) }
+
+ it 'adds a record for the namespace' do
+ expect { onboard }.to change(described_class, :count).from(0).to(1)
+ end
+
+ context 'when not given a namespace' do
+ let(:namespace) { nil }
+
+ it 'does not add a record for the namespace' do
+ expect { onboard }.not_to change(described_class, :count).from(0)
+ end
+ end
+
+ context 'when not given a root namespace' do
+ let(:namespace) { create(:group, parent: build(:group)) }
+
+ it 'does not add a record for the namespace' do
+ expect { onboard }.not_to change(described_class, :count).from(0)
+ end
+ end
+ end
+
+ describe '.onboarding?' do
+ subject(:onboarding?) { described_class.onboarding?(namespace) }
+
+ context 'when onboarded' do
+ before do
+ described_class.onboard(namespace)
+ end
+
+ it { is_expected.to eq true }
+ end
+
+ context 'when not onboarding' do
+ it { is_expected.to eq false }
+ end
+ end
+
+ describe '.register' do
+ context 'for a single action' do
+ subject(:register_action) { described_class.register(namespace, action) }
+
+ context 'when the namespace was onboarded' do
+ before do
+ described_class.onboard(namespace)
+ end
+
+ it 'registers the action for the namespace' do
+ expect { register_action }.to change { described_class.completed?(namespace, action) }.from(false).to(true)
+ end
+
+ it 'does not override timestamp', :aggregate_failures do
+ expect(described_class.find_by_namespace_id(namespace.id).subscription_created_at).to be_nil
+ register_action
+ expect(described_class.find_by_namespace_id(namespace.id).subscription_created_at).not_to be_nil
+ expect do
+ described_class.register(namespace, action)
+ end.not_to change { described_class.find_by_namespace_id(namespace.id).subscription_created_at }
+ end
+
+ context 'when the action does not exist' do
+ let(:action) { :foo }
+
+ it 'does not register the action for the namespace' do
+ expect { register_action }.not_to change { described_class.completed?(namespace, action) }.from(nil)
+ end
+ end
+ end
+
+ context 'when the namespace was not onboarded' do
+ it 'does not register the action for the namespace' do
+ expect { register_action }.not_to change { described_class.completed?(namespace, action) }.from(false)
+ end
+ end
+ end
+
+ context 'for multiple actions' do
+ let(:action1) { :security_scan_enabled }
+ let(:action2) { :secure_dependency_scanning_run }
+ let(:actions) { [action1, action2] }
+
+ subject(:register_action) { described_class.register(namespace, actions) }
+
+ context 'when the namespace was onboarded' do
+ before do
+ described_class.onboard(namespace)
+ end
+
+ it 'registers the actions for the namespace' do
+ expect { register_action }.to change {
+ [described_class.completed?(namespace, action1), described_class.completed?(namespace, action2)]
+ }.from([false, false]).to([true, true])
+ end
+
+ it 'does not override timestamp', :aggregate_failures do
+ described_class.register(namespace, [action1])
+ expect(described_class.find_by_namespace_id(namespace.id).security_scan_enabled_at).not_to be_nil
+ expect(described_class.find_by_namespace_id(namespace.id).secure_dependency_scanning_run_at).to be_nil
+
+ expect { described_class.register(namespace, [action1, action2]) }.not_to change {
+ described_class.find_by_namespace_id(namespace.id).security_scan_enabled_at
+ }
+ expect(described_class.find_by_namespace_id(namespace.id).secure_dependency_scanning_run_at).not_to be_nil
+ end
+
+ context 'when one of the actions does not exist' do
+ let(:action2) { :foo }
+
+ it 'does not register any action for the namespace' do
+ expect { register_action }.not_to change {
+ [described_class.completed?(namespace, action1), described_class.completed?(namespace, action2)]
+ }.from([false, nil])
+ end
+ end
+ end
+
+ context 'when the namespace was not onboarded' do
+ it 'does not register the action for the namespace' do
+ expect { register_action }.not_to change { described_class.completed?(namespace, action1) }.from(false)
+ expect do
+ described_class.register(namespace, action)
+ end.not_to change { described_class.completed?(namespace, action2) }.from(false)
+ end
+ end
+ end
+ end
+
+ describe '.completed?' do
+ subject { described_class.completed?(namespace, action) }
+
+ context 'when the namespace has not yet been onboarded' do
+ it { is_expected.to eq(false) }
+ end
+
+ context 'when the namespace has been onboarded but not registered the action yet' do
+ before do
+ described_class.onboard(namespace)
+ end
+
+ it { is_expected.to eq(false) }
+
+ context 'when the action has been registered' do
+ before do
+ described_class.register(namespace, action)
+ end
+
+ it { is_expected.to eq(true) }
+ end
+ end
+ end
+
+ describe '.not_completed?' do
+ subject { described_class.not_completed?(namespace.id, action) }
+
+ context 'when the namespace has not yet been onboarded' do
+ it { is_expected.to be(false) }
+ end
+
+ context 'when the namespace has been onboarded but not registered the action yet' do
+ before do
+ described_class.onboard(namespace)
+ end
+
+ it { is_expected.to be(true) }
+
+ context 'when the action has been registered' do
+ before do
+ described_class.register(namespace, action)
+ end
+
+ it { is_expected.to be(false) }
+ end
+ end
+ end
+
+ describe '.column_name' do
+ subject { described_class.column_name(action) }
+
+ it { is_expected.to eq(:subscription_created_at) }
+ end
+
+ describe '#number_of_completed_actions' do
+ subject do
+ build(:onboarding_progress, actions.map { |x| { x => Time.current } }.inject(:merge)).number_of_completed_actions
+ end
+
+ context 'with 0 completed actions' do
+ let(:actions) { [:created_at, :updated_at] }
+
+ it { is_expected.to eq(0) }
+ end
+
+ context 'with 1 completed action' do
+ let(:actions) { [:created_at, :subscription_created_at] }
+
+ it { is_expected.to eq(1) }
+ end
+
+ context 'with 2 completed actions' do
+ let(:actions) { [:subscription_created_at, :git_write_at] }
+
+ it { is_expected.to eq(2) }
+ end
+ end
+end
diff --git a/spec/models/onboarding_progress_spec.rb b/spec/models/onboarding_progress_spec.rb
deleted file mode 100644
index 9688dd01c71..00000000000
--- a/spec/models/onboarding_progress_spec.rb
+++ /dev/null
@@ -1,293 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe OnboardingProgress do
- let(:namespace) { create(:namespace) }
- let(:action) { :subscription_created }
-
- describe 'associations' do
- it { is_expected.to belong_to(:namespace).required }
- end
-
- describe 'validations' do
- describe 'namespace_is_root_namespace' do
- subject(:onboarding_progress) { build(:onboarding_progress, namespace: namespace) }
-
- context 'when associated namespace is root' do
- it { is_expected.to be_valid }
- end
-
- context 'when associated namespace is not root' do
- let(:namespace) { build(:group, :nested) }
-
- it 'is invalid' do
- expect(onboarding_progress).to be_invalid
- expect(onboarding_progress.errors[:namespace]).to include('must be a root namespace')
- end
- end
- end
- end
-
- describe 'scopes' do
- describe '.incomplete_actions' do
- subject { described_class.incomplete_actions(actions) }
-
- let!(:no_actions_completed) { create(:onboarding_progress) }
- let!(:one_action_completed_one_action_incompleted) { create(:onboarding_progress, "#{action}_at" => Time.current) }
-
- context 'when given one action' do
- let(:actions) { action }
-
- it { is_expected.to eq [no_actions_completed] }
- end
-
- context 'when given an array of actions' do
- let(:actions) { [action, :git_write] }
-
- it { is_expected.to eq [no_actions_completed] }
- end
- end
-
- describe '.completed_actions' do
- subject { described_class.completed_actions(actions) }
-
- let!(:one_action_completed_one_action_incompleted) { create(:onboarding_progress, "#{action}_at" => Time.current) }
- let!(:both_actions_completed) { create(:onboarding_progress, "#{action}_at" => Time.current, git_write_at: Time.current) }
-
- context 'when given one action' do
- let(:actions) { action }
-
- it { is_expected.to eq [one_action_completed_one_action_incompleted, both_actions_completed] }
- end
-
- context 'when given an array of actions' do
- let(:actions) { [action, :git_write] }
-
- it { is_expected.to eq [both_actions_completed] }
- end
- end
-
- describe '.completed_actions_with_latest_in_range' do
- subject { described_class.completed_actions_with_latest_in_range(actions, 1.day.ago.beginning_of_day..1.day.ago.end_of_day) }
-
- let!(:one_action_completed_in_range_one_action_incompleted) { create(:onboarding_progress, "#{action}_at" => 1.day.ago.middle_of_day) }
- let!(:git_write_action_completed_in_range) { create(:onboarding_progress, git_write_at: 1.day.ago.middle_of_day) }
- let!(:both_actions_completed_latest_action_out_of_range) { create(:onboarding_progress, "#{action}_at" => 1.day.ago.middle_of_day, git_write_at: Time.current) }
- let!(:both_actions_completed_latest_action_in_range) { create(:onboarding_progress, "#{action}_at" => 1.day.ago.middle_of_day, git_write_at: 2.days.ago.middle_of_day) }
-
- context 'when given one action' do
- let(:actions) { :git_write }
-
- it { is_expected.to eq [git_write_action_completed_in_range] }
- end
-
- context 'when given an array of actions' do
- let(:actions) { [action, :git_write] }
-
- it { is_expected.to eq [both_actions_completed_latest_action_in_range] }
- end
- end
- end
-
- describe '.onboard' do
- subject(:onboard) { described_class.onboard(namespace) }
-
- it 'adds a record for the namespace' do
- expect { onboard }.to change(described_class, :count).from(0).to(1)
- end
-
- context 'when not given a namespace' do
- let(:namespace) { nil }
-
- it 'does not add a record for the namespace' do
- expect { onboard }.not_to change(described_class, :count).from(0)
- end
- end
-
- context 'when not given a root namespace' do
- let(:namespace) { create(:group, parent: build(:group)) }
-
- it 'does not add a record for the namespace' do
- expect { onboard }.not_to change(described_class, :count).from(0)
- end
- end
- end
-
- describe '.onboarding?' do
- subject(:onboarding?) { described_class.onboarding?(namespace) }
-
- context 'when onboarded' do
- before do
- described_class.onboard(namespace)
- end
-
- it { is_expected.to eq true }
- end
-
- context 'when not onboarding' do
- it { is_expected.to eq false }
- end
- end
-
- describe '.register' do
- context 'for a single action' do
- subject(:register_action) { described_class.register(namespace, action) }
-
- context 'when the namespace was onboarded' do
- before do
- described_class.onboard(namespace)
- end
-
- it 'registers the action for the namespace' do
- expect { register_action }.to change { described_class.completed?(namespace, action) }.from(false).to(true)
- end
-
- it 'does not override timestamp', :aggregate_failures do
- expect(described_class.find_by_namespace_id(namespace.id).subscription_created_at).to be_nil
- register_action
- expect(described_class.find_by_namespace_id(namespace.id).subscription_created_at).not_to be_nil
- expect { described_class.register(namespace, action) }.not_to change { described_class.find_by_namespace_id(namespace.id).subscription_created_at }
- end
-
- context 'when the action does not exist' do
- let(:action) { :foo }
-
- it 'does not register the action for the namespace' do
- expect { register_action }.not_to change { described_class.completed?(namespace, action) }.from(nil)
- end
- end
- end
-
- context 'when the namespace was not onboarded' do
- it 'does not register the action for the namespace' do
- expect { register_action }.not_to change { described_class.completed?(namespace, action) }.from(false)
- end
- end
- end
-
- context 'for multiple actions' do
- let(:action1) { :security_scan_enabled }
- let(:action2) { :secure_dependency_scanning_run }
- let(:actions) { [action1, action2] }
-
- subject(:register_action) { described_class.register(namespace, actions) }
-
- context 'when the namespace was onboarded' do
- before do
- described_class.onboard(namespace)
- end
-
- it 'registers the actions for the namespace' do
- expect { register_action }.to change {
- [described_class.completed?(namespace, action1), described_class.completed?(namespace, action2)]
- }.from([false, false]).to([true, true])
- end
-
- it 'does not override timestamp', :aggregate_failures do
- described_class.register(namespace, [action1])
- expect(described_class.find_by_namespace_id(namespace.id).security_scan_enabled_at).not_to be_nil
- expect(described_class.find_by_namespace_id(namespace.id).secure_dependency_scanning_run_at).to be_nil
-
- expect { described_class.register(namespace, [action1, action2]) }.not_to change {
- described_class.find_by_namespace_id(namespace.id).security_scan_enabled_at
- }
- expect(described_class.find_by_namespace_id(namespace.id).secure_dependency_scanning_run_at).not_to be_nil
- end
-
- context 'when one of the actions does not exist' do
- let(:action2) { :foo }
-
- it 'does not register any action for the namespace' do
- expect { register_action }.not_to change {
- [described_class.completed?(namespace, action1), described_class.completed?(namespace, action2)]
- }.from([false, nil])
- end
- end
- end
-
- context 'when the namespace was not onboarded' do
- it 'does not register the action for the namespace' do
- expect { register_action }.not_to change { described_class.completed?(namespace, action1) }.from(false)
- expect { described_class.register(namespace, action) }.not_to change { described_class.completed?(namespace, action2) }.from(false)
- end
- end
- end
- end
-
- describe '.completed?' do
- subject { described_class.completed?(namespace, action) }
-
- context 'when the namespace has not yet been onboarded' do
- it { is_expected.to eq(false) }
- end
-
- context 'when the namespace has been onboarded but not registered the action yet' do
- before do
- described_class.onboard(namespace)
- end
-
- it { is_expected.to eq(false) }
-
- context 'when the action has been registered' do
- before do
- described_class.register(namespace, action)
- end
-
- it { is_expected.to eq(true) }
- end
- end
- end
-
- describe '.not_completed?' do
- subject { described_class.not_completed?(namespace.id, action) }
-
- context 'when the namespace has not yet been onboarded' do
- it { is_expected.to be(false) }
- end
-
- context 'when the namespace has been onboarded but not registered the action yet' do
- before do
- described_class.onboard(namespace)
- end
-
- it { is_expected.to be(true) }
-
- context 'when the action has been registered' do
- before do
- described_class.register(namespace, action)
- end
-
- it { is_expected.to be(false) }
- end
- end
- end
-
- describe '.column_name' do
- subject { described_class.column_name(action) }
-
- it { is_expected.to eq(:subscription_created_at) }
- end
-
- describe '#number_of_completed_actions' do
- subject { build(:onboarding_progress, actions.map { |x| { x => Time.current } }.inject(:merge)).number_of_completed_actions }
-
- context '0 completed actions' do
- let(:actions) { [:created_at, :updated_at] }
-
- it { is_expected.to eq(0) }
- end
-
- context '1 completed action' do
- let(:actions) { [:created_at, :subscription_created_at] }
-
- it { is_expected.to eq(1) }
- end
-
- context '2 completed actions' do
- let(:actions) { [:subscription_created_at, :git_write_at] }
-
- it { is_expected.to eq(2) }
- end
- end
-end
diff --git a/spec/models/operations/feature_flag_spec.rb b/spec/models/operations/feature_flag_spec.rb
index e709470b312..85a475f5c53 100644
--- a/spec/models/operations/feature_flag_spec.rb
+++ b/spec/models/operations/feature_flag_spec.rb
@@ -55,7 +55,7 @@ RSpec.describe Operations::FeatureFlag do
it 'is valid if associated with Operations::FeatureFlags::Strategy models' do
project = create(:project)
feature_flag = described_class.create!({ name: 'test', project: project, version: 2,
- strategies_attributes: [{ name: 'default', parameters: {} }] })
+ strategies_attributes: [{ name: 'default', parameters: {} }] })
expect(feature_flag).to be_valid
end
@@ -114,13 +114,11 @@ RSpec.describe Operations::FeatureFlag do
let_it_be(:project) { create(:project) }
let!(:feature_flag) do
- create(:operations_feature_flag, project: project,
- name: 'feature1', active: true, version: 2)
+ create(:operations_feature_flag, project: project, name: 'feature1', active: true, version: 2)
end
let!(:strategy) do
- create(:operations_strategy, feature_flag: feature_flag,
- name: 'default', parameters: {})
+ create(:operations_strategy, feature_flag: feature_flag, name: 'default', parameters: {})
end
it 'matches wild cards in the scope' do
@@ -141,10 +139,8 @@ RSpec.describe Operations::FeatureFlag do
it 'returns feature flags ordered by id' do
create(:operations_scope, strategy: strategy, environment_scope: 'production')
- feature_flag_b = create(:operations_feature_flag, project: project,
- name: 'feature2', active: true, version: 2)
- strategy_b = create(:operations_strategy, feature_flag: feature_flag_b,
- name: 'default', parameters: {})
+ feature_flag_b = create(:operations_feature_flag, project: project, name: 'feature2', active: true, version: 2)
+ strategy_b = create(:operations_strategy, feature_flag: feature_flag_b, name: 'default', parameters: {})
create(:operations_scope, strategy: strategy_b, environment_scope: '*')
flags = described_class.for_unleash_client(project, 'production')
diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb
index 526c57d08b0..fb88dbb4212 100644
--- a/spec/models/packages/package_spec.rb
+++ b/spec/models/packages/package_spec.rb
@@ -21,6 +21,7 @@ RSpec.describe Packages::Package, type: :model do
it { is_expected.to have_one(:nuget_metadatum).inverse_of(:package) }
it { is_expected.to have_one(:rubygems_metadatum).inverse_of(:package) }
it { is_expected.to have_one(:npm_metadatum).inverse_of(:package) }
+ it { is_expected.to have_one(:rpm_metadatum).inverse_of(:package) }
end
describe '.with_debian_codename' do
@@ -1356,4 +1357,16 @@ RSpec.describe Packages::Package, type: :model do
it { is_expected.to eq(normalized_name) }
end
end
+
+ describe '#touch_last_downloaded_at' do
+ let_it_be(:package) { create(:package) }
+
+ subject { package.touch_last_downloaded_at }
+
+ it 'updates the downloaded_at' do
+ expect(::Gitlab::Database::LoadBalancing::Session).to receive(:without_sticky_writes).and_call_original
+ expect { subject }
+ .to change(package, :last_downloaded_at).from(nil).to(instance_of(ActiveSupport::TimeWithZone))
+ end
+ end
end
diff --git a/spec/models/packages/rpm/metadatum_spec.rb b/spec/models/packages/rpm/metadatum_spec.rb
new file mode 100644
index 00000000000..0e7817fdf86
--- /dev/null
+++ b/spec/models/packages/rpm/metadatum_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Packages::Rpm::Metadatum, type: :model do
+ describe 'relationships' do
+ it { is_expected.to belong_to(:package) }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:package) }
+ it { is_expected.to validate_presence_of(:epoch) }
+ it { is_expected.to validate_presence_of(:release) }
+ it { is_expected.to validate_presence_of(:summary) }
+ it { is_expected.to validate_presence_of(:description) }
+ it { is_expected.to validate_presence_of(:arch) }
+
+ it { is_expected.to validate_numericality_of(:epoch).only_integer.is_greater_than_or_equal_to(0) }
+
+ it { is_expected.to validate_length_of(:release).is_at_most(128) }
+ it { is_expected.to validate_length_of(:summary).is_at_most(1000) }
+ it { is_expected.to validate_length_of(:description).is_at_most(5000) }
+ it { is_expected.to validate_length_of(:arch).is_at_most(255) }
+ it { is_expected.to validate_length_of(:license).is_at_most(1000) }
+ it { is_expected.to validate_length_of(:url).is_at_most(1000) }
+
+ describe '#rpm_package_type' do
+ it 'will not allow a package with a different package_type' do
+ package = build('conan_package')
+ rpm_metadatum = build('rpm_metadatum', package: package)
+
+ expect(rpm_metadatum).not_to be_valid
+ expect(rpm_metadatum.errors.to_a).to include('Package type must be RPM')
+ end
+ end
+ end
+end
diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb
index 4e463b1194c..b50bfaed528 100644
--- a/spec/models/pages_domain_spec.rb
+++ b/spec/models/pages_domain_spec.rb
@@ -21,6 +21,15 @@ RSpec.describe PagesDomain do
end
end
+ describe '.verified' do
+ let!(:verified) { create(:pages_domain) }
+ let!(:unverified) { create(:pages_domain, :unverified) }
+
+ it 'finds verified' do
+ expect(described_class.verified).to match_array(verified)
+ end
+ end
+
describe 'validate domain' do
subject(:pages_domain) { build(:pages_domain, domain: domain) }
@@ -32,17 +41,17 @@ RSpec.describe PagesDomain do
describe "hostname" do
{
- 'my.domain.com' => true,
- '123.456.789' => true,
- '0x12345.com' => true,
- '0123123' => true,
- 'a-reserved.com' => true,
+ 'my.domain.com' => true,
+ '123.456.789' => true,
+ '0x12345.com' => true,
+ '0123123' => true,
+ 'a-reserved.com' => true,
'a.b-reserved.com' => true,
- 'reserved.com' => true,
- '_foo.com' => false,
- 'a.reserved.com' => false,
+ 'reserved.com' => true,
+ '_foo.com' => false,
+ 'a.reserved.com' => false,
'a.b.reserved.com' => false,
- nil => false
+ nil => false
}.each do |value, validity|
context "domain #{value.inspect} validity" do
before do
@@ -62,12 +71,11 @@ RSpec.describe PagesDomain do
let(:domain) { 'my.domain.com' }
let(:project) do
- instance_double(Project, pages_https_only?: pages_https_only)
+ instance_double(Project, pages_https_only?: pages_https_only, can_create_custom_domains?: true)
end
let(:pages_domain) do
- build(:pages_domain, certificate: certificate, key: key,
- auto_ssl_enabled: auto_ssl_enabled).tap do |pd|
+ build(:pages_domain, certificate: certificate, key: key, auto_ssl_enabled: auto_ssl_enabled).tap do |pd|
allow(pd).to receive(:project).and_return(project)
pd.valid?
end
@@ -572,6 +580,32 @@ RSpec.describe PagesDomain do
end
end
+ describe '#validate_custom_domain_count_per_project' do
+ let_it_be(:project) { create(:project) }
+
+ context 'when max custom domain setting is set to 0' do
+ it 'returns without an error' do
+ pages_domain = create(:pages_domain, project: project)
+
+ expect(pages_domain).to be_valid
+ end
+ end
+
+ context 'when max custom domain setting is not set to 0' do
+ it 'returns with an error for extra domains' do
+ Gitlab::CurrentSettings.update!(max_pages_custom_domains_per_project: 1)
+
+ pages_domain = create(:pages_domain, project: project)
+ expect(pages_domain).to be_valid
+
+ pages_domain = build(:pages_domain, project: project)
+ expect(pages_domain).not_to be_valid
+ expect(pages_domain.errors.full_messages)
+ .to contain_exactly('This project reached the limit of custom domains. (Max 1)')
+ end
+ end
+ end
+
describe '.find_by_domain_case_insensitive' do
it 'lookup is case-insensitive' do
pages_domain = create(:pages_domain, domain: "Pages.IO")
diff --git a/spec/models/personal_access_token_spec.rb b/spec/models/personal_access_token_spec.rb
index f3ef347121e..5bce6a2cc3f 100644
--- a/spec/models/personal_access_token_spec.rb
+++ b/spec/models/personal_access_token_spec.rb
@@ -64,6 +64,81 @@ RSpec.describe PersonalAccessToken do
expect(described_class.for_users([user_1, user_2])).to contain_exactly(token_of_user_1, token_of_user_2)
end
end
+
+ describe '.created_before' do
+ let(:last_used_at) { 1.month.ago.beginning_of_hour }
+ let!(:new_used_token) do
+ create(:personal_access_token,
+ created_at: last_used_at + 1.minute,
+ last_used_at: last_used_at + 1.minute
+ )
+ end
+
+ let!(:old_unused_token) do
+ create(:personal_access_token,
+ created_at: last_used_at - 1.minute
+ )
+ end
+
+ let!(:old_formerly_used_token) do
+ create(:personal_access_token,
+ created_at: last_used_at - 1.minute,
+ last_used_at: last_used_at - 1.minute
+ )
+ end
+
+ let!(:old_still_used_token) do
+ create(:personal_access_token,
+ created_at: last_used_at - 1.minute,
+ last_used_at: 1.minute.ago
+ )
+ end
+
+ subject { described_class.created_before(last_used_at) }
+
+ it do
+ is_expected.to contain_exactly(
+ old_unused_token,
+ old_formerly_used_token,
+ old_still_used_token
+ )
+ end
+ end
+
+ describe '.last_used_before_or_unused' do
+ let(:last_used_at) { 1.month.ago.beginning_of_hour }
+ let!(:unused_token) { create(:personal_access_token) }
+ let!(:used_token) do
+ create(:personal_access_token,
+ created_at: last_used_at + 1.minute,
+ last_used_at: last_used_at + 1.minute
+ )
+ end
+
+ let!(:old_unused_token) do
+ create(:personal_access_token,
+ created_at: last_used_at - 1.minute
+ )
+ end
+
+ let!(:old_formerly_used_token) do
+ create(:personal_access_token,
+ created_at: last_used_at - 1.minute,
+ last_used_at: last_used_at - 1.minute
+ )
+ end
+
+ let!(:old_still_used_token) do
+ create(:personal_access_token,
+ created_at: last_used_at - 1.minute,
+ last_used_at: 1.minute.ago
+ )
+ end
+
+ subject { described_class.last_used_before_or_unused(last_used_at) }
+
+ it { is_expected.to contain_exactly(old_unused_token, old_formerly_used_token) }
+ end
end
describe ".active?" do
diff --git a/spec/models/pool_repository_spec.rb b/spec/models/pool_repository_spec.rb
index 447b7b2e0a2..bf88e941540 100644
--- a/spec/models/pool_repository_spec.rb
+++ b/spec/models/pool_repository_spec.rb
@@ -43,6 +43,15 @@ RSpec.describe PoolRepository do
end
end
+ context 'when skipping disconnect' do
+ it 'does not change the alternates file' do
+ before = File.read(alternates_file)
+ pool.unlink_repository(pool.source_project.repository, disconnect: false)
+
+ expect(File.read(alternates_file)).to eq(before)
+ end
+ end
+
context 'when the second member leaves' do
it 'does not schedule pool removal' do
other_project = create(:project, :repository, pool_repository: pool)
diff --git a/spec/models/preloaders/project_policy_preloader_spec.rb b/spec/models/preloaders/project_policy_preloader_spec.rb
new file mode 100644
index 00000000000..79f232f5ce2
--- /dev/null
+++ b/spec/models/preloaders/project_policy_preloader_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Preloaders::ProjectPolicyPreloader do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:root_parent) { create(:group, :private, name: 'root-1', path: 'root-1') }
+ let_it_be(:guest_project) { create(:project, name: 'public guest', path: 'public-guest') }
+ let_it_be(:private_maintainer_project) do
+ create(:project, :private, name: 'b private maintainer', path: 'b-private-maintainer', namespace: root_parent)
+ end
+
+ let_it_be(:private_developer_project) do
+ create(:project, :private, name: 'c public developer', path: 'c-public-developer')
+ end
+
+ let_it_be(:public_maintainer_project) do
+ create(:project, :private, name: 'a public maintainer', path: 'a-public-maintainer')
+ end
+
+ let(:base_projects) do
+ Project.where(id: [guest_project, private_maintainer_project, private_developer_project, public_maintainer_project])
+ end
+
+ before_all do
+ guest_project.add_guest(user)
+ private_maintainer_project.add_maintainer(user)
+ private_developer_project.add_developer(user)
+ public_maintainer_project.add_maintainer(user)
+ end
+
+ it 'avoids N+1 queries when authorizing a list of projects', :request_store do
+ preload_projects_for_policy(user)
+ control = ActiveRecord::QueryRecorder.new { authorize_all_projects(user) }
+
+ new_project1 = create(:project, :private).tap { |project| project.add_maintainer(user) }
+ new_project2 = create(:project, :private, namespace: root_parent)
+
+ another_root = create(:group, :private, name: 'root-3', path: 'root-3')
+ new_project3 = create(:project, :private, namespace: another_root).tap { |project| project.add_maintainer(user) }
+
+ pristine_projects = Project.where(id: base_projects + [new_project1, new_project2, new_project3])
+
+ preload_projects_for_policy(user, pristine_projects)
+ expect { authorize_all_projects(user, pristine_projects) }.not_to exceed_query_limit(control)
+ end
+
+ def authorize_all_projects(current_user, project_list = base_projects)
+ project_list.each { |project| current_user.can?(:read_project, project) }
+ end
+
+ def preload_projects_for_policy(current_user, project_list = base_projects)
+ described_class.new(project_list, current_user).execute
+ end
+end
diff --git a/spec/models/preloaders/project_root_ancestor_preloader_spec.rb b/spec/models/preloaders/project_root_ancestor_preloader_spec.rb
new file mode 100644
index 00000000000..30036a6a033
--- /dev/null
+++ b/spec/models/preloaders/project_root_ancestor_preloader_spec.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Preloaders::ProjectRootAncestorPreloader do
+ let_it_be(:root_parent1) { create(:group, :private, name: 'root-1', path: 'root-1') }
+ let_it_be(:root_parent2) { create(:group, :private, name: 'root-2', path: 'root-2') }
+ let_it_be(:guest_project) { create(:project, name: 'public guest', path: 'public-guest') }
+ let_it_be(:private_maintainer_project) do
+ create(:project, :private, name: 'b private maintainer', path: 'b-private-maintainer', namespace: root_parent1)
+ end
+
+ let_it_be(:private_developer_project) do
+ create(:project, :private, name: 'c public developer', path: 'c-public-developer')
+ end
+
+ let_it_be(:public_maintainer_project) do
+ create(:project, :private, name: 'a public maintainer', path: 'a-public-maintainer', namespace: root_parent2)
+ end
+
+ let(:root_query_regex) { /\ASELECT.+FROM "namespaces" WHERE "namespaces"."id" = \d+/ }
+ let(:additional_preloads) { [] }
+ let(:projects) { [guest_project, private_maintainer_project, private_developer_project, public_maintainer_project] }
+ let(:pristine_projects) { Project.where(id: projects) }
+
+ shared_examples 'executes N matching DB queries' do |expected_query_count, query_method = nil|
+ it 'executes the specified root_ancestor queries' do
+ expect do
+ pristine_projects.each do |project|
+ root_ancestor = project.root_ancestor
+
+ root_ancestor.public_send(query_method) if query_method.present?
+ end
+ end.to make_queries_matching(root_query_regex, expected_query_count)
+ end
+
+ it 'strong_memoizes the correct root_ancestor' do
+ pristine_projects.each do |project|
+ expected_parent_id = project.root_ancestor&.id
+
+ expect(project.parent_id).to eq(expected_parent_id)
+ end
+ end
+ end
+
+ context 'when use_traversal_ids FF is enabled' do
+ context 'when the preloader is used' do
+ before do
+ preload_ancestors
+ end
+
+ context 'when no additional preloads are provided' do
+ it_behaves_like 'executes N matching DB queries', 0
+ end
+
+ context 'when additional preloads are provided' do
+ let(:additional_preloads) { [:route] }
+ let(:root_query_regex) { /\ASELECT.+FROM "routes" WHERE "routes"."source_id" = \d+/ }
+
+ it_behaves_like 'executes N matching DB queries', 0, :full_path
+ end
+ end
+
+ context 'when the preloader is not used' do
+ it_behaves_like 'executes N matching DB queries', 4
+ end
+ end
+
+ context 'when use_traversal_ids FF is disabled' do
+ before do
+ stub_feature_flags(use_traversal_ids: false)
+ end
+
+ context 'when the preloader is used' do
+ before do
+ preload_ancestors
+ end
+
+ context 'when no additional preloads are provided' do
+ it_behaves_like 'executes N matching DB queries', 4
+ end
+
+ context 'when additional preloads are provided' do
+ let(:additional_preloads) { [:route] }
+ let(:root_query_regex) { /\ASELECT.+FROM "routes" WHERE "routes"."source_id" = \d+/ }
+
+ it_behaves_like 'executes N matching DB queries', 4, :full_path
+ end
+ end
+
+ context 'when the preloader is not used' do
+ it_behaves_like 'executes N matching DB queries', 4
+ end
+ end
+
+ def preload_ancestors
+ described_class.new(pristine_projects, :namespace, additional_preloads).execute
+ end
+end
diff --git a/spec/models/project_setting_spec.rb b/spec/models/project_setting_spec.rb
index fb1601a5f9c..a09ae7ec7ae 100644
--- a/spec/models/project_setting_spec.rb
+++ b/spec/models/project_setting_spec.rb
@@ -63,4 +63,51 @@ RSpec.describe ProjectSetting, type: :model do
target_platforms.permutation(n).to_a
end
end
+
+ describe '#show_diff_preview_in_email?' do
+ context 'when a project is a top-level namespace' do
+ let(:project_settings ) { create(:project_setting, show_diff_preview_in_email: false) }
+ let(:project) { create(:project, project_setting: project_settings) }
+
+ context 'when show_diff_preview_in_email is disabled' do
+ it 'returns false' do
+ expect(project).not_to be_show_diff_preview_in_email
+ end
+ end
+
+ context 'when show_diff_preview_in_email is enabled' do
+ let(:project_settings ) { create(:project_setting, show_diff_preview_in_email: true) }
+
+ it 'returns true' do
+ settings = create(:project_setting, show_diff_preview_in_email: true)
+ project = create(:project, project_setting: settings)
+
+ expect(project).to be_show_diff_preview_in_email
+ end
+ end
+ end
+
+ context 'when a parent group has a parent group' do
+ let(:namespace_settings) { create(:namespace_settings, show_diff_preview_in_email: false) }
+ let(:project_settings) { create(:project_setting, show_diff_preview_in_email: true) }
+ let(:group) { create(:group, namespace_settings: namespace_settings) }
+ let!(:project) { create(:project, namespace_id: group.id, project_setting: project_settings) }
+
+ context 'when show_diff_preview_in_email is disabled for the parent group' do
+ it 'returns false' do
+ expect(project).not_to be_show_diff_preview_in_email
+ end
+ end
+
+ context 'when all ancestors have enabled diff previews' do
+ let(:namespace_settings) { create(:namespace_settings, show_diff_preview_in_email: true) }
+
+ it 'returns true' do
+ group.update_attribute(:show_diff_preview_in_email, true)
+
+ expect(project).to be_show_diff_preview_in_email
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 98b202299a8..99b984ff547 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -366,12 +366,35 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to include_module(Sortable) }
end
+ describe 'before_validation' do
+ context 'with removal of leading spaces' do
+ subject(:project) { build(:project, name: ' space first', path: 'some_path') }
+
+ it 'removes the leading space' do
+ expect(project.name).to eq ' space first'
+
+ expect(project).to be_valid # triggers before_validation and assures we automatically handle the bad format
+
+ expect(project.name).to eq 'space first'
+ end
+
+ context 'when name is nil' do
+ it 'falls through to the presence validation' do
+ project.name = nil
+
+ expect(project).not_to be_valid
+ end
+ end
+ end
+ end
+
describe 'validation' do
let!(:project) { create(:project) }
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:name).scoped_to(:namespace_id) }
it { is_expected.to validate_length_of(:name).is_at_most(255) }
+ it { is_expected.to allow_value('space last ').for(:name) }
it { is_expected.not_to allow_value('colon:in:path').for(:path) } # This is to validate that a specially crafted name cannot bypass a pattern match. See !72555
it { is_expected.to validate_presence_of(:path) }
it { is_expected.to validate_length_of(:path).is_at_most(255) }
@@ -1736,8 +1759,8 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '.with_shared_runners' do
- subject { described_class.with_shared_runners }
+ describe '.with_shared_runners_enabled' do
+ subject { described_class.with_shared_runners_enabled }
context 'when shared runners are enabled for project' do
let!(:project) { create(:project, shared_runners_enabled: true) }
@@ -3925,162 +3948,6 @@ RSpec.describe Project, factory_default: :keep do
end
end
- describe '#ci_variables_for' do
- let_it_be(:project) { create(:project) }
-
- let(:environment_scope) { '*' }
-
- let!(:ci_variable) do
- create(:ci_variable, value: 'secret', project: project, environment_scope: environment_scope)
- end
-
- let!(:protected_variable) do
- create(:ci_variable, :protected, value: 'protected', project: project)
- end
-
- subject { project.reload.ci_variables_for(ref: 'ref') }
-
- before do
- stub_application_setting(
- default_branch_protection: Gitlab::Access::PROTECTION_NONE)
- end
-
- shared_examples 'ref is protected' do
- it 'contains all the variables' do
- is_expected.to contain_exactly(ci_variable, protected_variable)
- end
- end
-
- it 'memoizes the result by ref and environment', :request_store do
- scoped_variable = create(:ci_variable, value: 'secret', project: project, environment_scope: 'scoped')
-
- expect(project).to receive(:protected_for?).with('ref').once.and_return(true)
- expect(project).to receive(:protected_for?).with('other').twice.and_return(false)
-
- 2.times do
- expect(project.reload.ci_variables_for(ref: 'ref', environment: 'production')).to contain_exactly(ci_variable, protected_variable)
- expect(project.reload.ci_variables_for(ref: 'other')).to contain_exactly(ci_variable)
- expect(project.reload.ci_variables_for(ref: 'other', environment: 'scoped')).to contain_exactly(ci_variable, scoped_variable)
- end
- end
-
- context 'when the ref is not protected' do
- before do
- allow(project).to receive(:protected_for?).with('ref').and_return(false)
- end
-
- it 'contains only the CI variables' do
- is_expected.to contain_exactly(ci_variable)
- end
- end
-
- context 'when the ref is a protected branch' do
- before do
- allow(project).to receive(:protected_for?).with('ref').and_return(true)
- end
-
- it_behaves_like 'ref is protected'
- end
-
- context 'when the ref is a protected tag' do
- before do
- allow(project).to receive(:protected_for?).with('ref').and_return(true)
- end
-
- it_behaves_like 'ref is protected'
- end
-
- context 'when environment name is specified' do
- let(:environment) { 'review/name' }
-
- subject do
- project.ci_variables_for(ref: 'ref', environment: environment)
- end
-
- context 'when environment scope is exactly matched' do
- let(:environment_scope) { 'review/name' }
-
- it { is_expected.to contain_exactly(ci_variable) }
- end
-
- context 'when environment scope is matched by wildcard' do
- let(:environment_scope) { 'review/*' }
-
- it { is_expected.to contain_exactly(ci_variable) }
- end
-
- context 'when environment scope does not match' do
- let(:environment_scope) { 'review/*/special' }
-
- it { is_expected.not_to contain_exactly(ci_variable) }
- end
-
- context 'when environment scope has _' do
- let(:environment_scope) { '*_*' }
-
- it 'does not treat it as wildcard' do
- is_expected.not_to contain_exactly(ci_variable)
- end
-
- context 'when environment name contains underscore' do
- let(:environment) { 'foo_bar/test' }
- let(:environment_scope) { 'foo_bar/*' }
-
- it 'matches literally for _' do
- is_expected.to contain_exactly(ci_variable)
- end
- end
- end
-
- # The environment name and scope cannot have % at the moment,
- # but we're considering relaxing it and we should also make sure
- # it doesn't break in case some data sneaked in somehow as we're
- # not checking this integrity in database level.
- context 'when environment scope has %' do
- it 'does not treat it as wildcard' do
- ci_variable.update_attribute(:environment_scope, '*%*')
-
- is_expected.not_to contain_exactly(ci_variable)
- end
-
- context 'when environment name contains a percent' do
- let(:environment) { 'foo%bar/test' }
-
- it 'matches literally for _' do
- ci_variable.environment_scope = 'foo%bar/*'
-
- is_expected.to contain_exactly(ci_variable)
- end
- end
- end
-
- context 'when variables with the same name have different environment scopes' do
- let!(:partially_matched_variable) do
- create(:ci_variable,
- key: ci_variable.key,
- value: 'partial',
- environment_scope: 'review/*',
- project: project)
- end
-
- let!(:perfectly_matched_variable) do
- create(:ci_variable,
- key: ci_variable.key,
- value: 'prefect',
- environment_scope: 'review/name',
- project: project)
- end
-
- it 'puts variables matching environment scope more in the end' do
- is_expected.to eq(
- [ci_variable,
- partially_matched_variable,
- perfectly_matched_variable])
- end
- end
- end
- end
-
describe '#any_lfs_file_locks?', :request_store do
let_it_be(:project) { create(:project) }
@@ -6290,14 +6157,6 @@ RSpec.describe Project, factory_default: :keep do
let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, :group, groups: [group]) }
it { is_expected.to eq(deploy_token) }
-
- context 'when the FF ci_variable_for_group_gitlab_deploy_token is disabled' do
- before do
- stub_feature_flags(ci_variable_for_group_gitlab_deploy_token: false)
- end
-
- it { is_expected.to be_nil }
- end
end
context 'when the project and its group has a gitlab deploy token associated' do
@@ -6307,14 +6166,6 @@ RSpec.describe Project, factory_default: :keep do
let!(:group_deploy_token) { create(:deploy_token, :gitlab_deploy_token, :group, groups: [group]) }
it { is_expected.to eq(project_deploy_token) }
-
- context 'when the FF ci_variable_for_group_gitlab_deploy_token is disabled' do
- before do
- stub_feature_flags(ci_variable_for_group_gitlab_deploy_token: false)
- end
-
- it { is_expected.to eq(project_deploy_token) }
- end
end
end
@@ -6624,11 +6475,27 @@ RSpec.describe Project, factory_default: :keep do
let(:pool) { create(:pool_repository) }
let(:project) { create(:project, :repository, pool_repository: pool) }
- it 'removes the membership' do
- project.leave_pool_repository
+ subject { project.leave_pool_repository }
+
+ it 'removes the membership and disconnects alternates' do
+ expect(pool).to receive(:unlink_repository).with(project.repository, disconnect: true).and_call_original
+
+ subject
expect(pool.member_projects.reload).not_to include(project)
end
+
+ context 'when the project is pending delete' do
+ it 'removes the membership and does not disconnect alternates' do
+ project.pending_delete = true
+
+ expect(pool).to receive(:unlink_repository).with(project.repository, disconnect: false).and_call_original
+
+ subject
+
+ expect(pool.member_projects.reload).not_to include(project)
+ end
+ end
end
describe '#check_personal_projects_limit' do
@@ -8434,6 +8301,25 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ describe '#packages_policy_subject' do
+ let_it_be(:project) { create(:project) }
+
+ it 'returns wrapper' do
+ expect(project.packages_policy_subject).to be_a(Packages::Policies::Project)
+ expect(project.packages_policy_subject.project).to eq(project)
+ end
+
+ context 'with feature flag disabled' do
+ before do
+ stub_feature_flags(read_package_policy_rule: false)
+ end
+
+ it 'returns project' do
+ expect(project.packages_policy_subject).to eq(project)
+ end
+ end
+ end
+
describe '#destroy_deployment_by_id' do
let(:project) { create(:project, :repository) }
@@ -8451,6 +8337,25 @@ RSpec.describe Project, factory_default: :keep do
end
end
+ describe '#can_create_custom_domains?' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:pages_domain) { create(:pages_domain, project: project) }
+
+ subject { project.can_create_custom_domains? }
+
+ context 'when max custom domain setting is set to 0' do
+ it { is_expected.to be true }
+ end
+
+ context 'when max custom domain setting is not set to 0' do
+ before do
+ Gitlab::CurrentSettings.update!(max_pages_custom_domains_per_project: 1)
+ end
+
+ it { is_expected.to be false }
+ 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 f4edc68457b..b2158baa670 100644
--- a/spec/models/project_statistics_spec.rb
+++ b/spec/models/project_statistics_spec.rb
@@ -407,6 +407,25 @@ RSpec.describe ProjectStatistics do
end
end
+ describe '#refresh_storage_size!' do
+ it 'recalculates storage size from its components and save it' do
+ statistics.update_columns(
+ repository_size: 2,
+ wiki_size: 4,
+ lfs_objects_size: 3,
+ snippets_size: 2,
+ pipeline_artifacts_size: 3,
+ build_artifacts_size: 3,
+ packages_size: 6,
+ uploads_size: 5,
+
+ storage_size: 0
+ )
+
+ expect { statistics.refresh_storage_size! }.to change { statistics.storage_size }.from(0).to(28)
+ end
+ end
+
describe '.increment_statistic' do
shared_examples 'a statistic that increases storage_size' do
it 'increases the statistic by that amount' do
@@ -432,16 +451,15 @@ RSpec.describe ProjectStatistics do
end
end
- it 'schedules a worker to update the statistic and storage_size async' 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)
+ .and_call_original
- expect(FlushCounterIncrementsWorker)
- .to receive(:perform_in)
- .with(CounterAttribute::WORKER_DELAY, described_class.name, statistics.id, :storage_size)
-
- described_class.increment_statistic(project, stat, 20)
+ expect { described_class.increment_statistic(project, stat, 20) }
+ .to change { statistics.reload.send(stat) }.by(20)
+ .and change { statistics.reload.send(:storage_size) }.by(20)
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 052e654af76..21cd8e0b9d4 100644
--- a/spec/models/projects/build_artifacts_size_refresh_spec.rb
+++ b/spec/models/projects/build_artifacts_size_refresh_spec.rb
@@ -265,4 +265,16 @@ RSpec.describe Projects::BuildArtifactsSizeRefresh, type: :model do
it { is_expected.to eq(result) }
end
end
+
+ describe 'callbacks' do
+ context 'when destroyed' do
+ it 'enqueues a Namespaces::ScheduleAggregationWorker' do
+ refresh = create(:project_build_artifacts_size_refresh)
+
+ expect(Namespaces::ScheduleAggregationWorker).to receive(:perform_async).with(refresh.project.namespace_id)
+
+ refresh.destroy!
+ end
+ end
+ end
end
diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb
index 3936e7127b8..54a90ca6049 100644
--- a/spec/models/protected_branch_spec.rb
+++ b/spec/models/protected_branch_spec.rb
@@ -171,8 +171,8 @@ RSpec.describe ProtectedBranch do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:protected_branch) { create(:protected_branch, project: project, name: "“jawnâ€") }
- let(:feature_flag) { true }
- let(:dry_run) { true }
+ let(:use_new_cache_implementation) { true }
+ let(:rely_on_new_cache) { true }
shared_examples_for 'hash based cache implementation' do
it 'calls only hash based cache implementation' do
@@ -182,19 +182,22 @@ RSpec.describe ProtectedBranch do
expect(Rails.cache).not_to receive(:fetch)
- described_class.protected?(project, 'missing-branch', dry_run: dry_run)
+ described_class.protected?(project, 'missing-branch')
end
end
before do
- stub_feature_flags(hash_based_cache_for_protected_branches: feature_flag)
+ stub_feature_flags(hash_based_cache_for_protected_branches: use_new_cache_implementation)
+ stub_feature_flags(rely_on_protected_branches_cache: rely_on_new_cache)
allow(described_class).to receive(:matching).and_call_original
# the original call works and warms the cache
- described_class.protected?(project, protected_branch.name, dry_run: dry_run)
+ described_class.protected?(project, protected_branch.name)
end
- context 'Dry-run: true' do
+ context 'Dry-run: true (rely_on_protected_branches_cache is off, new hash-based is used)' do
+ let(:rely_on_new_cache) { false }
+
it 'recalculates a fresh value every time in order to check the cache is not returning stale data' do
expect(described_class).to receive(:matching).with(protected_branch.name, protected_refs: anything).twice
@@ -204,21 +207,21 @@ RSpec.describe ProtectedBranch do
it_behaves_like 'hash based cache implementation'
end
- context 'Dry-run: false' do
- let(:dry_run) { false }
+ context 'Dry-run: false (rely_on_protected_branches_cache is enabled, new hash-based cache is used)' do
+ let(:rely_on_new_cache) { true }
it 'correctly invalidates a cache' do
expect(described_class).to receive(:matching).with(protected_branch.name, protected_refs: anything).exactly(3).times.and_call_original
create_params = { name: 'bar', merge_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }] }
branch = ProtectedBranches::CreateService.new(project, project.owner, create_params).execute
- expect(described_class.protected?(project, protected_branch.name, dry_run: dry_run)).to eq(true)
+ expect(described_class.protected?(project, protected_branch.name)).to eq(true)
ProtectedBranches::UpdateService.new(project, project.owner, name: 'ber').execute(branch)
- expect(described_class.protected?(project, protected_branch.name, dry_run: dry_run)).to eq(true)
+ expect(described_class.protected?(project, protected_branch.name)).to eq(true)
ProtectedBranches::DestroyService.new(project, project.owner).execute(branch)
- expect(described_class.protected?(project, protected_branch.name, dry_run: dry_run)).to eq(true)
+ expect(described_class.protected?(project, protected_branch.name)).to eq(true)
end
it_behaves_like 'hash based cache implementation'
@@ -229,7 +232,7 @@ RSpec.describe ProtectedBranch do
project.touch
- described_class.protected?(project, protected_branch.name, dry_run: dry_run)
+ described_class.protected?(project, protected_branch.name)
end
end
@@ -240,19 +243,19 @@ RSpec.describe ProtectedBranch do
another_project = create(:project)
ProtectedBranches::CreateService.new(another_project, another_project.owner, name: 'bar').execute
- described_class.protected?(project, protected_branch.name, dry_run: dry_run)
+ described_class.protected?(project, protected_branch.name)
end
end
it 'correctly uses the cached version' do
expect(described_class).not_to receive(:matching)
- expect(described_class.protected?(project, protected_branch.name, dry_run: dry_run)).to eq(true)
+ expect(described_class.protected?(project, protected_branch.name)).to eq(true)
end
end
context 'when feature flag hash_based_cache_for_protected_branches is off' do
- let(:feature_flag) { false }
+ let(:use_new_cache_implementation) { false }
it 'does not call hash based cache implementation' do
expect(ProtectedBranches::CacheService).not_to receive(:new)
diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb
index 429ad550626..adb4777ae90 100644
--- a/spec/models/remote_mirror_spec.rb
+++ b/spec/models/remote_mirror_spec.rb
@@ -359,10 +359,10 @@ RSpec.describe RemoteMirror, :mailer do
it 'resets all the columns when URL changes' do
remote_mirror.update!(last_error: Time.current,
- last_update_at: Time.current,
- last_successful_update_at: Time.current,
- update_status: 'started',
- error_notification_sent: true)
+ last_update_at: Time.current,
+ last_successful_update_at: Time.current,
+ update_status: 'started',
+ error_notification_sent: true)
expect { remote_mirror.update_attribute(:url, 'http://new.example.com') }
.to change { remote_mirror.last_error }.to(nil)
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 47532ed1216..4e386bf584f 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -161,6 +161,33 @@ RSpec.describe Repository do
end
end
+ context 'semantic versioning sort' do
+ let(:version_two) { 'v2.0.0' }
+ let(:version_ten) { 'v10.0.0' }
+
+ before do
+ repository.add_tag(user, version_two, repository.commit.id)
+ repository.add_tag(user, version_ten, repository.commit.id)
+ end
+
+ after do
+ repository.rm_tag(user, version_two)
+ repository.rm_tag(user, version_ten)
+ end
+
+ context 'desc' do
+ subject { repository.tags_sorted_by('version_desc').map(&:name) & (tags_to_compare + [version_two, version_ten]) }
+
+ it { is_expected.to eq([version_ten, version_two, 'v1.1.0', 'v1.0.0']) }
+ end
+
+ context 'asc' do
+ subject { repository.tags_sorted_by('version_asc').map(&:name) & (tags_to_compare + [version_two, version_ten]) }
+
+ it { is_expected.to eq(['v1.0.0', 'v1.1.0', version_two, version_ten]) }
+ end
+ end
+
context 'unknown option' do
subject { repository.tags_sorted_by('unknown_desc').map(&:name) & tags_to_compare }
@@ -518,6 +545,54 @@ RSpec.describe Repository do
end
end
+ describe '#list_commits_by' do
+ it 'returns commits with messages containing a given string' do
+ commit_ids = repository.list_commits_by('test text', 'master').map(&:id)
+
+ expect(commit_ids).to include(
+ 'b83d6e391c22777fca1ed3012fce84f633d7fed0',
+ '498214de67004b1da3d820901307bed2a68a8ef6'
+ )
+ expect(commit_ids).not_to include('c84ff944ff4529a70788a5e9003c2b7feae29047')
+ end
+
+ it 'is case insensitive' do
+ commit_ids = repository.list_commits_by('TEST TEXT', 'master').map(&:id)
+
+ expect(commit_ids).to include('b83d6e391c22777fca1ed3012fce84f633d7fed0')
+ end
+
+ it 'returns commits based in before filter' do
+ commit_ids = repository.list_commits_by('test text', 'master', before: 1474828200).map(&:id)
+ expect(commit_ids).to include(
+ '498214de67004b1da3d820901307bed2a68a8ef6'
+ )
+ expect(commit_ids).not_to include('b83d6e391c22777fca1ed3012fce84f633d7fed0')
+ end
+
+ it 'returns commits based in after filter' do
+ commit_ids = repository.list_commits_by('test text', 'master', after: 1474828200).map(&:id)
+ expect(commit_ids).to include(
+ 'b83d6e391c22777fca1ed3012fce84f633d7fed0'
+ )
+ expect(commit_ids).not_to include('498214de67004b1da3d820901307bed2a68a8ef6')
+ end
+
+ it 'returns commits based in author filter' do
+ commit_ids = repository.list_commits_by('test text', 'master', author: 'Job van der Voort').map(&:id)
+ expect(commit_ids).to include(
+ 'b83d6e391c22777fca1ed3012fce84f633d7fed0'
+ )
+ expect(commit_ids).not_to include('498214de67004b1da3d820901307bed2a68a8ef6')
+ end
+
+ describe 'when storage is broken', :broken_storage do
+ it 'raises a storage error' do
+ expect_to_raise_storage_error { broken_repository.list_commits_by('s') }
+ end
+ end
+ end
+
describe '#blob_at' do
context 'blank sha' do
subject { repository.blob_at(Gitlab::Git::BLANK_SHA, '.gitignore') }
@@ -3306,7 +3381,7 @@ RSpec.describe Repository do
before do
storages = {
'default' => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/repositories'),
- 'picked' => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/repositories')
+ 'picked' => Gitlab::GitalyClient::StorageSettings.new('path' => 'tmp/tests/repositories')
}
allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
diff --git a/spec/models/resource_state_event_spec.rb b/spec/models/resource_state_event_spec.rb
index 2b4898b750a..f84634bd220 100644
--- a/spec/models/resource_state_event_spec.rb
+++ b/spec/models/resource_state_event_spec.rb
@@ -42,16 +42,44 @@ RSpec.describe ResourceStateEvent, type: :model do
context 'callbacks' do
describe '#issue_usage_metrics' do
- it 'tracks closed issues' do
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_closed_action)
-
- create(described_class.name.underscore.to_sym, issue: issue, state: described_class.states[:closed])
+ describe 'when an issue is closed' do
+ subject(:close_issue) do
+ create(described_class.name.underscore.to_sym, issue: issue,
+ state: described_class.states[:closed])
+ end
+
+ it 'tracks closed issues' do
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_closed_action)
+
+ close_issue
+ end
+
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_CLOSED }
+ let(:project) { issue.project }
+ let(:user) { issue.author }
+ subject(:service_action) { close_issue }
+ end
end
- it 'tracks reopened issues' do
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_reopened_action)
+ describe 'when an issue is reopened' do
+ subject(:reopen_issue) do
+ create(described_class.name.underscore.to_sym, issue: issue,
+ state: described_class.states[:reopened])
+ end
+
+ it 'tracks reopened issues' do
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_reopened_action)
+
+ reopen_issue
+ end
- create(described_class.name.underscore.to_sym, issue: issue, state: described_class.states[:reopened])
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_REOPENED }
+ let(:project) { issue.project }
+ let(:user) { issue.author }
+ subject(:service_action) { reopen_issue }
+ end
end
it 'does not track merge requests' do
diff --git a/spec/models/snippet_repository_spec.rb b/spec/models/snippet_repository_spec.rb
index e8a933d2277..655cfad57c9 100644
--- a/spec/models/snippet_repository_spec.rb
+++ b/spec/models/snippet_repository_spec.rb
@@ -39,7 +39,7 @@ RSpec.describe SnippetRepository do
let(:data) { [new_file, move_file, update_file] }
it 'returns nil when files argument is empty' do
- expect(snippet.repository).not_to receive(:multi_action)
+ expect(snippet.repository).not_to receive(:commit_files)
operation = snippet_repository.multi_files_action(user, [], **commit_opts)
@@ -47,7 +47,7 @@ RSpec.describe SnippetRepository do
end
it 'returns nil when files argument is nil' do
- expect(snippet.repository).not_to receive(:multi_action)
+ expect(snippet.repository).not_to receive(:commit_files)
operation = snippet_repository.multi_files_action(user, nil, **commit_opts)
@@ -119,7 +119,7 @@ RSpec.describe SnippetRepository do
end
it 'infers the commit action based on the parameters if not present' do
- expect(repo).to receive(:multi_action).with(user, hash_including(actions: result))
+ expect(repo).to receive(:commit_files).with(user, hash_including(actions: result))
snippet_repository.multi_files_action(user, data, **commit_opts)
end
@@ -131,7 +131,7 @@ RSpec.describe SnippetRepository do
specify do
expect(repo).to(
- receive(:multi_action).with(
+ receive(:commit_files).with(
user,
hash_including(actions: array_including(hash_including(action: expected_action)))))
diff --git a/spec/models/spam_log_spec.rb b/spec/models/spam_log_spec.rb
index 97a0dc27f17..a40c7c5c892 100644
--- a/spec/models/spam_log_spec.rb
+++ b/spec/models/spam_log_spec.rb
@@ -21,15 +21,37 @@ RSpec.describe SpamLog do
end
context 'when admin mode is enabled', :enable_admin_mode do
- it 'removes the user', :sidekiq_might_not_need_inline do
- spam_log = build(:spam_log)
- user = spam_log.user
+ context 'when user_destroy_with_limited_execution_time_worker is enabled' do
+ it 'initiates user removal', :sidekiq_inline do
+ spam_log = build(:spam_log)
+ user = spam_log.user
+
+ perform_enqueued_jobs do
+ spam_log.remove_user(deleted_by: admin)
+ end
+
+ expect(
+ Users::GhostUserMigration.where(user: user,
+ initiator_user: admin)
+ ).to be_exists
+ end
+ end
- perform_enqueued_jobs do
- spam_log.remove_user(deleted_by: admin)
+ context 'when user_destroy_with_limited_execution_time_worker is disabled' do
+ before do
+ stub_feature_flags(user_destroy_with_limited_execution_time_worker: false)
end
- expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ it 'removes the user', :sidekiq_inline do
+ spam_log = build(:spam_log)
+ user = spam_log.user
+
+ perform_enqueued_jobs do
+ spam_log.remove_user(deleted_by: admin)
+ end
+
+ expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 69cd51137b5..04f2c7f9176 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -334,6 +334,58 @@ RSpec.describe User do
end
end
end
+
+ 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
+ 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 '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)
+
+ expect(Security::WeakPasswords).not_to receive(:weak_for_user?)
+
+ # Change an unrelated value
+ user.name = "Example McExampleFace"
+ expect(user).to be_valid
+ end
+ end
+ end
end
describe 'name' do
@@ -2071,11 +2123,12 @@ RSpec.describe User do
context 'user has existing U2F registration' do
it 'returns false' do
device = U2F::FakeU2F.new(FFaker::BaconIpsum.characters(5))
- create(:u2f_registration, name: 'my u2f device',
- user: user,
- certificate: Base64.strict_encode64(device.cert_raw),
- key_handle: U2F.urlsafe_encode64(device.key_handle_raw),
- public_key: Base64.strict_encode64(device.origin_public_key_raw))
+ create(:u2f_registration,
+ name: 'my u2f device',
+ user: user,
+ certificate: Base64.strict_encode64(device.cert_raw),
+ key_handle: U2F.urlsafe_encode64(device.key_handle_raw),
+ public_key: Base64.strict_encode64(device.origin_public_key_raw))
expect(user.two_factor_u2f_enabled?).to eq(false)
end
@@ -2094,11 +2147,12 @@ RSpec.describe User do
context 'user has existing U2F registration' do
it 'returns true' do
device = U2F::FakeU2F.new(FFaker::BaconIpsum.characters(5))
- create(:u2f_registration, name: 'my u2f device',
- user: user,
- certificate: Base64.strict_encode64(device.cert_raw),
- key_handle: U2F.urlsafe_encode64(device.key_handle_raw),
- public_key: Base64.strict_encode64(device.origin_public_key_raw))
+ create(:u2f_registration,
+ name: 'my u2f device',
+ user: user,
+ certificate: Base64.strict_encode64(device.cert_raw),
+ key_handle: U2F.urlsafe_encode64(device.key_handle_raw),
+ public_key: Base64.strict_encode64(device.origin_public_key_raw))
expect(user.two_factor_u2f_enabled?).to eq(true)
end
@@ -3601,15 +3655,15 @@ RSpec.describe User do
user = create :user
project = create(:project, :public)
- expect(user.starred?(project)).to be_falsey
-
- user.toggle_star(project)
-
- expect(user.starred?(project)).to be_truthy
-
- user.toggle_star(project)
+ # starring
+ expect { user.toggle_star(project) }
+ .to change { user.starred?(project) }.from(false).to(true)
+ .and not_change { project.reload.updated_at }
- expect(user.starred?(project)).to be_falsey
+ # unstarring
+ expect { user.toggle_star(project) }
+ .to change { user.starred?(project) }.from(true).to(false)
+ .and not_change { project.reload.updated_at }
end
end
@@ -3810,8 +3864,8 @@ RSpec.describe User do
describe '#can_be_deactivated?' do
let(:activity) { {} }
let(:user) { create(:user, name: 'John Smith', **activity) }
- let(:day_within_minium_inactive_days_threshold) { User::MINIMUM_INACTIVE_DAYS.pred.days.ago }
- let(:day_outside_minium_inactive_days_threshold) { User::MINIMUM_INACTIVE_DAYS.next.days.ago }
+ let(:day_within_minium_inactive_days_threshold) { Gitlab::CurrentSettings.deactivate_dormant_users_period.pred.days.ago }
+ let(:day_outside_minium_inactive_days_threshold) { Gitlab::CurrentSettings.deactivate_dormant_users_period.next.days.ago }
shared_examples 'not eligible for deactivation' do
it 'returns false' do
@@ -7193,8 +7247,8 @@ RSpec.describe User do
describe '.dormant' do
it 'returns dormant users' do
freeze_time do
- not_that_long_ago = (described_class::MINIMUM_INACTIVE_DAYS - 1).days.ago.to_date
- too_long_ago = described_class::MINIMUM_INACTIVE_DAYS.days.ago.to_date
+ not_that_long_ago = (Gitlab::CurrentSettings.deactivate_dormant_users_period - 1).days.ago.to_date
+ too_long_ago = Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_date
create(:user, :deactivated, last_activity_on: too_long_ago)
@@ -7214,8 +7268,8 @@ RSpec.describe User do
describe '.with_no_activity' do
it 'returns users with no activity' do
freeze_time do
- active_not_that_long_ago = (described_class::MINIMUM_INACTIVE_DAYS - 1).days.ago.to_date
- active_too_long_ago = described_class::MINIMUM_INACTIVE_DAYS.days.ago.to_date
+ active_not_that_long_ago = (Gitlab::CurrentSettings.deactivate_dormant_users_period - 1).days.ago.to_date
+ active_too_long_ago = Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_date
created_recently = (described_class::MINIMUM_DAYS_CREATED - 1).days.ago.to_date
created_not_recently = described_class::MINIMUM_DAYS_CREATED.days.ago.to_date
@@ -7396,25 +7450,6 @@ RSpec.describe User do
let(:factory_name) { :user }
end
- describe 'mr_attention_requests_enabled?' do
- let(:user) { create(:user) }
-
- before do
- stub_feature_flags(mr_attention_requests: false)
- end
-
- it { expect(user.mr_attention_requests_enabled?).to be(false) }
-
- it 'feature flag is enabled for user' do
- stub_feature_flags(mr_attention_requests: user)
-
- another_user = create(:user)
-
- expect(user.mr_attention_requests_enabled?).to be(true)
- expect(another_user.mr_attention_requests_enabled?).to be(false)
- end
- end
-
describe 'user age' do
let(:user) { create(:user, created_at: Date.yesterday) }
diff --git a/spec/models/user_status_spec.rb b/spec/models/user_status_spec.rb
index 663df9712ab..289e1ce1856 100644
--- a/spec/models/user_status_spec.rb
+++ b/spec/models/user_status_spec.rb
@@ -18,6 +18,14 @@ RSpec.describe UserStatus do
expect { status.user.destroy! }.to change { described_class.count }.from(1).to(0)
end
+ describe '#clear_status_after' do
+ it 'is an alias of #clear_status_at', :freeze_time do
+ status = build(:user_status, clear_status_at: 8.hours.from_now)
+
+ expect(status.clear_status_after).to be_like_time(8.hours.from_now)
+ end
+ end
+
describe '#clear_status_after=' do
it 'sets clear_status_at' do
status = build(:user_status)
diff --git a/spec/models/users/credit_card_validation_spec.rb b/spec/models/users/credit_card_validation_spec.rb
index 34cfd500c26..58b529ff18a 100644
--- a/spec/models/users/credit_card_validation_spec.rb
+++ b/spec/models/users/credit_card_validation_spec.rb
@@ -28,4 +28,27 @@ RSpec.describe Users::CreditCardValidation do
expect(subject.similar_records).to eq([match2, match1, subject])
end
end
+
+ describe '#similar_holder_names_count' do
+ subject!(:credit_card_validation) { create(:credit_card_validation, holder_name: holder_name) }
+
+ context 'when holder_name is present' do
+ let(:holder_name) { 'ALICE M SMITH' }
+
+ let!(:match) { create(:credit_card_validation, holder_name: 'Alice M Smith') }
+ let!(:non_match) { create(:credit_card_validation, holder_name: 'Bob B Brown') }
+
+ it 'returns the count of cards with similar case insensitive holder names' do
+ expect(subject.similar_holder_names_count).to eq(2)
+ end
+ end
+
+ context 'when holder_name is nil' do
+ let(:holder_name) { nil }
+
+ it 'returns 0' do
+ expect(subject.similar_holder_names_count).to eq(0)
+ end
+ end
+ end
end
diff --git a/spec/models/users/ghost_user_migration_spec.rb b/spec/models/users/ghost_user_migration_spec.rb
new file mode 100644
index 00000000000..d4a0657c3be
--- /dev/null
+++ b/spec/models/users/ghost_user_migration_spec.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Users::GhostUserMigration do
+ describe 'associations' do
+ it { is_expected.to belong_to(:user) }
+ it { is_expected.to belong_to(:initiator_user) }
+ end
+
+ describe 'validation' do
+ it { is_expected.to validate_presence_of(:user_id) }
+ end
+end
diff --git a/spec/models/users/merge_request_interaction_spec.rb b/spec/models/users/merge_request_interaction_spec.rb
index a499a7c68e8..0b1888bd9a6 100644
--- a/spec/models/users/merge_request_interaction_spec.rb
+++ b/spec/models/users/merge_request_interaction_spec.rb
@@ -59,11 +59,8 @@ RSpec.describe ::Users::MergeRequestInteraction do
context 'when the user has been asked to review the MR' do
before do
merge_request.reviewers << user
- merge_request.find_reviewer(user).update!(state: :attention_requested)
end
- it { is_expected.to eq(Types::MergeRequestReviewStateEnum.values['ATTENTION_REQUESTED'].value) }
-
it 'implies not reviewed' do
expect(interaction).not_to be_reviewed
end
diff --git a/spec/models/users_star_project_spec.rb b/spec/models/users_star_project_spec.rb
new file mode 100644
index 00000000000..e41519a2b69
--- /dev/null
+++ b/spec/models/users_star_project_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe UsersStarProject, type: :model do
+ it { is_expected.to belong_to(:project).touch(false) }
+end
diff --git a/spec/models/work_item_spec.rb b/spec/models/work_item_spec.rb
index e2240c225a9..341f9a9c60f 100644
--- a/spec/models/work_item_spec.rb
+++ b/spec/models/work_item_spec.rb
@@ -59,6 +59,14 @@ RSpec.describe WorkItem do
create(:work_item)
end
+
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:work_item) { create(:work_item) }
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_CREATED }
+ let(:project) { work_item.project }
+ let(:user) { work_item.author }
+ subject(:service_action) { work_item }
+ end
end
context 'work item namespace' do
@@ -123,8 +131,8 @@ RSpec.describe WorkItem do
child.confidential = false
expect(child).not_to be_valid
- expect(child.errors[:confidential])
- .to include('associated parent is confidential and can not have non-confidential children.')
+ expect(child.errors[:base])
+ .to include(_('A non-confidential work item cannot have a confidential parent.'))
end
it 'allows to make parent non-confidential' do
@@ -143,8 +151,9 @@ RSpec.describe WorkItem do
parent.confidential = true
expect(parent).not_to be_valid
- expect(parent.errors[:confidential])
- .to include('confidential parent can not be used if there are non-confidential children.')
+ expect(parent.errors[:base]).to include(
+ _('A confidential work item cannot have a parent that already has non-confidential children.')
+ )
end
it 'allows to make child confidential' do
@@ -161,8 +170,8 @@ RSpec.describe WorkItem do
child.work_item_parent = create(:work_item, confidential: true, project: project)
expect(child).not_to be_valid
- expect(child.errors[:confidential])
- .to include('associated parent is confidential and can not have non-confidential children.')
+ expect(child.errors[:base])
+ .to include('A non-confidential work item cannot have a confidential parent.')
end
end
end
diff --git a/spec/models/work_items/widgets/description_spec.rb b/spec/models/work_items/widgets/description_spec.rb
index 8359db31bff..c24dc9cfb9c 100644
--- a/spec/models/work_items/widgets/description_spec.rb
+++ b/spec/models/work_items/widgets/description_spec.rb
@@ -3,7 +3,10 @@
require 'spec_helper'
RSpec.describe WorkItems::Widgets::Description do
- let_it_be(:work_item) { create(:work_item, description: '# Title') }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:work_item, refind: true) do
+ create(:work_item, description: 'Title', last_edited_at: 10.days.ago, last_edited_by: user)
+ end
describe '.type' do
subject { described_class.type }
@@ -22,4 +25,42 @@ RSpec.describe WorkItems::Widgets::Description do
it { is_expected.to eq(work_item.description) }
end
+
+ describe '#edited?' do
+ subject { described_class.new(work_item).edited? }
+
+ it { is_expected.to be_truthy }
+ end
+
+ describe '#last_edited_at' do
+ subject { described_class.new(work_item).last_edited_at }
+
+ it { is_expected.to eq(work_item.last_edited_at) }
+ end
+
+ describe '#last_edited_by' do
+ subject { described_class.new(work_item).last_edited_by }
+
+ context 'when the work item is edited' do
+ context 'when last edited user still exists in the DB' do
+ it { is_expected.to eq(user) }
+ end
+
+ context 'when last edited user no longer exists' do
+ before do
+ work_item.update!(last_edited_by: nil)
+ end
+
+ it { is_expected.to eq(User.ghost) }
+ end
+ end
+
+ context 'when the work item is not edited yet' do
+ before do
+ work_item.update!(last_edited_at: nil)
+ end
+
+ it { is_expected.to be_nil }
+ end
+ end
end
diff --git a/spec/policies/ci/pipeline_schedule_policy_spec.rb b/spec/policies/ci/pipeline_schedule_policy_spec.rb
index f2c99e0de95..9aa50876b55 100644
--- a/spec/policies/ci/pipeline_schedule_policy_spec.rb
+++ b/spec/policies/ci/pipeline_schedule_policy_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::PipelineSchedulePolicy, :models do
+RSpec.describe Ci::PipelineSchedulePolicy, :models, :clean_gitlab_redis_cache do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
let_it_be(:pipeline_schedule, reload: true) { create(:ci_pipeline_schedule, :nightly, project: project) }
diff --git a/spec/policies/ci/runner_policy_spec.rb b/spec/policies/ci/runner_policy_spec.rb
new file mode 100644
index 00000000000..880ff0722fa
--- /dev/null
+++ b/spec/policies/ci/runner_policy_spec.rb
@@ -0,0 +1,160 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::RunnerPolicy do
+ describe 'ability :read_runner' do
+ let_it_be(:guest) { create(:user) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:owner) { create(:user) }
+
+ let_it_be(:group1) { create(:group, name: 'top-level', path: 'top-level') }
+ let_it_be(:subgroup1) { create(:group, name: 'subgroup1', path: 'subgroup1', parent: group1) }
+ let_it_be(:project1) { create(:project, group: subgroup1) }
+ let_it_be(:instance_runner) { create(:ci_runner, :instance) }
+ let_it_be(:group1_runner) { create(:ci_runner, :group, groups: [group1]) }
+ let_it_be(:project1_runner) { create(:ci_runner, :project, projects: [project1]) }
+
+ subject(:policy) { described_class.new(user, runner) }
+
+ before do
+ group1.add_guest(guest)
+ group1.add_developer(developer)
+ group1.add_owner(owner)
+ end
+
+ shared_context 'on hierarchy with shared runners disabled' do
+ around do |example|
+ group1.update!(shared_runners_enabled: false)
+ project1.update!(shared_runners_enabled: false)
+
+ example.run
+ ensure
+ project1.update!(shared_runners_enabled: true)
+ group1.update!(shared_runners_enabled: true)
+ end
+ end
+
+ shared_context 'on hierarchy with group runners disabled' do
+ around do |example|
+ project1.update!(group_runners_enabled: false)
+
+ example.run
+ ensure
+ project1.update!(group_runners_enabled: true)
+ end
+ end
+
+ shared_examples 'does not allow reading runners on any scope' do
+ context 'with instance runner' do
+ let(:runner) { instance_runner }
+
+ it { expect_disallowed :read_runner }
+
+ context 'with shared runners disabled' do
+ include_context 'on hierarchy with shared runners disabled' do
+ it { expect_disallowed :read_runner }
+ end
+ end
+ end
+
+ context 'with group runner' do
+ let(:runner) { group1_runner }
+
+ it { expect_disallowed :read_runner }
+
+ context 'with group runner disabled' do
+ include_context 'on hierarchy with group runners disabled' do
+ it { expect_disallowed :read_runner }
+ end
+ end
+ end
+
+ context 'with project runner' do
+ let(:runner) { project1_runner }
+
+ it { expect_disallowed :read_runner }
+ end
+ end
+
+ context 'without access' do
+ let_it_be(:user) { create(:user) }
+
+ it_behaves_like 'does not allow reading runners on any scope'
+ end
+
+ context 'with guest access' do
+ let(:user) { guest }
+
+ it_behaves_like 'does not allow reading runners on any scope'
+ end
+
+ context 'with developer access' do
+ let(:user) { developer }
+
+ context 'with instance runner' do
+ let(:runner) { instance_runner }
+
+ it { expect_allowed :read_runner }
+
+ context 'with shared runners disabled' do
+ include_context 'on hierarchy with shared runners disabled' do
+ it { expect_disallowed :read_runner }
+ end
+ end
+ end
+
+ context 'with group runner' do
+ let(:runner) { group1_runner }
+
+ it { expect_allowed :read_runner }
+
+ context 'with group runner disabled' do
+ include_context 'on hierarchy with group runners disabled' do
+ it { expect_disallowed :read_runner }
+ end
+ end
+ end
+
+ context 'with project runner' do
+ let(:runner) { project1_runner }
+
+ it { expect_disallowed :read_runner }
+ end
+ end
+
+ context 'with owner access' do
+ let(:user) { owner }
+
+ context 'with instance runner' do
+ let(:runner) { instance_runner }
+
+ context 'with shared runners disabled' do
+ include_context 'on hierarchy with shared runners disabled' do
+ it { expect_disallowed :read_runner }
+ end
+ end
+
+ it { expect_allowed :read_runner }
+ end
+
+ context 'with group runner' do
+ let(:runner) { group1_runner }
+
+ context 'with group runners disabled' do
+ include_context 'on hierarchy with group runners disabled' do
+ it { expect_allowed :read_runner }
+ end
+ end
+
+ it { expect_allowed :read_runner }
+ end
+
+ context 'with project runner' do
+ let(:runner) { project1_runner }
+
+ it { expect_allowed :read_runner }
+ end
+ end
+ end
+end
diff --git a/spec/policies/clusters/agent_policy_spec.rb b/spec/policies/clusters/agent_policy_spec.rb
index 307d751b78b..8f778d318ed 100644
--- a/spec/policies/clusters/agent_policy_spec.rb
+++ b/spec/policies/clusters/agent_policy_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Clusters::AgentPolicy do
- let(:cluster_agent) { create(:cluster_agent, name: 'agent' )}
+ let(:cluster_agent) { create(:cluster_agent, name: 'agent' ) }
let(:user) { create(:admin) }
let(:policy) { described_class.new(user, cluster_agent) }
let(:project) { cluster_agent.project }
diff --git a/spec/policies/commit_policy_spec.rb b/spec/policies/commit_policy_spec.rb
index 0d3dcc97565..cf2798b9ef3 100644
--- a/spec/policies/commit_policy_spec.rb
+++ b/spec/policies/commit_policy_spec.rb
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe CommitPolicy do
describe '#rules' do
+ let(:group) { create(:group, :public) }
let(:user) { create(:user) }
let(:commit) { project.repository.head_commit }
let(:policy) { described_class.new(user, commit) }
@@ -19,59 +20,119 @@ RSpec.describe CommitPolicy do
end
shared_examples 'cannot read commit nor create a note' do
- it 'can not read commit' do
+ it 'cannot read commit' do
expect(policy).to be_disallowed(:read_commit)
end
- it 'can not create a note' do
+ it 'cannot create a note' do
expect(policy).to be_disallowed(:create_note)
end
end
context 'when project is public' do
- let(:project) { create(:project, :public, :repository) }
+ let(:project) { create(:project, :public, :repository, group: group) }
- it_behaves_like 'can read commit and create a note'
+ context 'when the user is not a project member' do
+ it_behaves_like 'can read commit and create a note'
+ end
context 'when repository access level is private' do
- let(:project) { create(:project, :public, :repository, :repository_private) }
+ let(:project) { create(:project, :public, :repository, :repository_private, group: group) }
- it_behaves_like 'cannot read commit nor create a note'
+ context 'when the user is not a project member' do
+ it_behaves_like 'cannot read commit nor create a note'
+ end
- context 'when the user is a project member' do
- before do
- project.add_developer(user)
+ context 'when the user is a direct project member' do
+ context 'and the user is a developer' do
+ before do
+ project.add_developer(user)
+ end
+
+ it_behaves_like 'can read commit and create a note'
end
+ end
- it_behaves_like 'can read commit and create a note'
+ context 'when the user is an inherited member from the group' do
+ context 'and the user is a guest' do
+ before do
+ group.add_guest(user)
+ end
+
+ it_behaves_like 'can read commit and create a note'
+ end
+
+ context 'and the user is a reporter' do
+ before do
+ group.add_reporter(user)
+ end
+
+ it_behaves_like 'can read commit and create a note'
+ end
+
+ context 'and the user is a developer' do
+ before do
+ group.add_developer(user)
+ end
+
+ it_behaves_like 'can read commit and create a note'
+ end
end
end
end
context 'when project is private' do
- let(:project) { create(:project, :private, :repository) }
+ let(:project) { create(:project, :private, :repository, group: group) }
- it_behaves_like 'cannot read commit nor create a note'
+ context 'when the user is not a project member' do
+ it_behaves_like 'cannot read commit nor create a note'
+ end
- context 'when the user is a project member' do
- before do
- project.add_developer(user)
+ context 'when the user is a direct project member' do
+ context 'and the user is a developer' do
+ before do
+ project.add_developer(user)
+ end
+
+ it_behaves_like 'can read commit and create a note'
end
- it 'can read commit and create a note' do
- expect(policy).to be_allowed(:read_commit)
+ context 'and the user is a guest' do
+ before do
+ project.add_guest(user)
+ end
+
+ it_behaves_like 'cannot read commit nor create a note'
+
+ it 'cannot download code' do
+ expect(policy).to be_disallowed(:download_code)
+ end
end
end
- context 'when the user is a guest' do
- before do
- project.add_guest(user)
+ context 'when the user is an inherited member from the group' do
+ context 'and the user is a guest' do
+ before do
+ group.add_guest(user)
+ end
+
+ it_behaves_like 'cannot read commit nor create a note'
end
- it_behaves_like 'cannot read commit nor create a note'
+ context 'and the user is a reporter' do
+ before do
+ group.add_reporter(user)
+ end
+
+ it_behaves_like 'can read commit and create a note'
+ end
- it 'cannot download code' do
- expect(policy).to be_disallowed(:download_code)
+ context 'and the user is a developer' do
+ before do
+ group.add_developer(user)
+ end
+
+ it_behaves_like 'can read commit and create a note'
end
end
end
diff --git a/spec/policies/group_member_policy_spec.rb b/spec/policies/group_member_policy_spec.rb
index 50774313aae..27ce683861c 100644
--- a/spec/policies/group_member_policy_spec.rb
+++ b/spec/policies/group_member_policy_spec.rb
@@ -128,7 +128,7 @@ RSpec.describe GroupMemberPolicy do
context 'with the group parent' do
let(:current_user) { create :user }
- let(:subgroup) { create(:group, :private, parent: group)}
+ let(:subgroup) { create(:group, :private, parent: group) }
before do
group.add_owner(owner)
@@ -143,7 +143,7 @@ RSpec.describe GroupMemberPolicy do
context 'without group parent' do
let(:current_user) { create :user }
- let(:subgroup) { create(:group, :private)}
+ let(:subgroup) { create(:group, :private) }
before do
subgroup.add_owner(current_user)
@@ -158,7 +158,7 @@ RSpec.describe GroupMemberPolicy do
context 'without group parent with two owners' do
let(:current_user) { create :user }
let(:other_user) { create :user }
- let(:subgroup) { create(:group, :private)}
+ let(:subgroup) { create(:group, :private) }
before do
subgroup.add_owner(current_user)
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index 57923142648..da0270c15b9 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe GroupPolicy do
let(:group) { create(:group, :public, :crm_enabled) }
let(:current_user) { nil }
- it do
+ specify do
expect_allowed(*public_permissions)
expect_disallowed(:upload_file)
expect_disallowed(*reporter_permissions)
@@ -24,7 +24,7 @@ RSpec.describe GroupPolicy do
let(:group) { create(:group, :public, :crm_enabled) }
let(:current_user) { create(:user) }
- it do
+ specify do
expect_allowed(*public_permissions)
expect_disallowed(:upload_file)
expect_disallowed(*reporter_permissions)
@@ -43,7 +43,7 @@ RSpec.describe GroupPolicy do
create(:project_group_link, project: project, group: group)
end
- it do
+ specify do
expect_disallowed(*public_permissions)
expect_disallowed(*reporter_permissions)
expect_disallowed(*owner_permissions)
@@ -58,7 +58,7 @@ RSpec.describe GroupPolicy do
create(:project_group_link, project: project, group: group)
end
- it do
+ specify do
expect_disallowed(*public_permissions)
expect_disallowed(*reporter_permissions)
expect_disallowed(*owner_permissions)
@@ -91,7 +91,7 @@ RSpec.describe GroupPolicy do
let(:deploy_token) { create(:deploy_token) }
let(:current_user) { deploy_token }
- it do
+ specify do
expect_disallowed(*public_permissions)
expect_disallowed(*guest_permissions)
expect_disallowed(*reporter_permissions)
@@ -104,7 +104,7 @@ RSpec.describe GroupPolicy do
context 'guests' do
let(:current_user) { guest }
- it do
+ specify do
expect_allowed(*public_permissions)
expect_allowed(*guest_permissions)
expect_disallowed(*reporter_permissions)
@@ -121,7 +121,7 @@ RSpec.describe GroupPolicy do
context 'reporter' do
let(:current_user) { reporter }
- it do
+ specify do
expect_allowed(*public_permissions)
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
@@ -138,7 +138,7 @@ RSpec.describe GroupPolicy do
context 'developer' do
let(:current_user) { developer }
- it do
+ specify do
expect_allowed(*public_permissions)
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
@@ -195,7 +195,7 @@ RSpec.describe GroupPolicy do
context 'owner' do
let(:current_user) { owner }
- it do
+ specify do
expect_allowed(*public_permissions)
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
@@ -282,7 +282,7 @@ RSpec.describe GroupPolicy do
context 'with no user' do
let(:current_user) { nil }
- it do
+ specify do
expect_disallowed(*public_permissions)
expect_disallowed(*guest_permissions)
expect_disallowed(*reporter_permissions)
@@ -295,7 +295,7 @@ RSpec.describe GroupPolicy do
context 'guests' do
let(:current_user) { guest }
- it do
+ specify do
expect_allowed(*public_permissions)
expect_allowed(*guest_permissions)
expect_disallowed(*reporter_permissions)
@@ -308,7 +308,7 @@ RSpec.describe GroupPolicy do
context 'reporter' do
let(:current_user) { reporter }
- it do
+ specify do
expect_allowed(*public_permissions)
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
@@ -321,7 +321,7 @@ RSpec.describe GroupPolicy do
context 'developer' do
let(:current_user) { developer }
- it do
+ specify do
expect_allowed(*public_permissions)
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
@@ -334,7 +334,7 @@ RSpec.describe GroupPolicy do
context 'maintainer' do
let(:current_user) { maintainer }
- it do
+ specify do
expect_allowed(*public_permissions)
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
@@ -347,7 +347,7 @@ RSpec.describe GroupPolicy do
context 'owner' do
let(:current_user) { owner }
- it do
+ specify do
expect_allowed(*public_permissions)
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
@@ -916,6 +916,74 @@ RSpec.describe GroupPolicy do
end
end
+ describe 'observability' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:allowed) { be_allowed(:read_observability) }
+ let(:disallowed) { be_disallowed(:read_observability) }
+
+ # rubocop:disable Layout/LineLength
+ where(:feature_enabled, :admin_matcher, :owner_matcher, :maintainer_matcher, :developer_matcher, :reporter_matcher, :guest_matcher, :non_member_matcher, :anonymous_matcher) do
+ false | ref(:disallowed) | ref(:disallowed) | ref(:disallowed) | ref(:disallowed) | ref(:disallowed) | ref(:disallowed) | ref(:disallowed) | ref(:disallowed)
+ true | ref(:allowed) | ref(:allowed) | ref(:allowed) | ref(:allowed) | ref(:disallowed) | ref(:disallowed) | ref(:disallowed) | ref(:disallowed)
+ end
+ # rubocop:enable Layout/LineLength
+
+ with_them do
+ before do
+ stub_feature_flags(observability_group_tab: feature_enabled)
+ end
+
+ context 'admin', :enable_admin_mode do
+ let(:current_user) { admin }
+
+ it { is_expected.to admin_matcher }
+ end
+
+ context 'owner' do
+ let(:current_user) { owner }
+
+ it { is_expected.to owner_matcher }
+ end
+
+ context 'maintainer' do
+ let(:current_user) { maintainer }
+
+ it { is_expected.to maintainer_matcher }
+ end
+
+ context 'developer' do
+ let(:current_user) { developer }
+
+ it { is_expected.to developer_matcher }
+ end
+
+ context 'reporter' do
+ let(:current_user) { reporter }
+
+ it { is_expected.to reporter_matcher }
+ end
+
+ context 'with guest' do
+ let(:current_user) { guest }
+
+ it { is_expected.to guest_matcher }
+ end
+
+ context 'with non member' do
+ let(:current_user) { create(:user) }
+
+ it { is_expected.to non_member_matcher }
+ end
+
+ context 'with anonymous' do
+ let(:current_user) { nil }
+
+ it { is_expected.to anonymous_matcher }
+ end
+ end
+ end
+
describe 'dependency proxy' do
context 'feature disabled' do
let(:current_user) { owner }
diff --git a/spec/policies/issuable_policy_spec.rb b/spec/policies/issuable_policy_spec.rb
index 706570babd5..fd7ec5917d6 100644
--- a/spec/policies/issuable_policy_spec.rb
+++ b/spec/policies/issuable_policy_spec.rb
@@ -18,8 +18,8 @@ RSpec.describe IssuablePolicy, models: true do
project.add_reporter(reporter)
end
- def permissions(user, issue)
- described_class.new(user, issue)
+ def permissions(user, issuable)
+ described_class.new(user, issuable)
end
describe '#rules' do
@@ -153,5 +153,55 @@ RSpec.describe IssuablePolicy, models: true do
expect(permissions(reporter, issue)).to be_allowed(:create_timelog)
end
end
+
+ context 'when subject is a Merge Request' do
+ let(:issuable) { create(:merge_request) }
+ let(:policy) { permissions(user, issuable) }
+
+ before do
+ allow(policy).to receive(:can?).with(:read_merge_request).and_return(can_read_merge_request)
+ end
+
+ context 'when can_read_merge_request is false' do
+ let(:can_read_merge_request) { false }
+
+ it 'does not allow :read_issuable' do
+ expect(policy).not_to be_allowed(:read_issuable)
+ end
+ end
+
+ context 'when can_read_merge_request is true' do
+ let(:can_read_merge_request) { true }
+
+ it 'allows :read_issuable' do
+ expect(policy).to be_allowed(:read_issuable)
+ end
+ end
+ end
+
+ context 'when subject is an Issue' do
+ let(:issuable) { create(:issue) }
+ let(:policy) { permissions(user, issuable) }
+
+ before do
+ allow(policy).to receive(:can?).with(:read_issue).and_return(can_read_issue)
+ end
+
+ context 'when can_read_issue is false' do
+ let(:can_read_issue) { false }
+
+ it 'does not allow :read_issuable' do
+ expect(policy).not_to be_allowed(:read_issuable)
+ end
+ end
+
+ context 'when can_read_issue is true' do
+ let(:can_read_issue) { true }
+
+ it 'allows :read_issuable' do
+ expect(policy).to be_allowed(:read_issuable)
+ end
+ end
+ end
end
end
diff --git a/spec/policies/issue_policy_spec.rb b/spec/policies/issue_policy_spec.rb
index 7ca4baddb79..4d492deb54c 100644
--- a/spec/policies/issue_policy_spec.rb
+++ b/spec/policies/issue_policy_spec.rb
@@ -398,7 +398,7 @@ RSpec.describe IssuePolicy do
context 'with a hidden issue' do
let(:user) { create(:user) }
let(:banned_user) { create(:user, :banned) }
- let(:admin) { create(:user, :admin)}
+ 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
diff --git a/spec/policies/merge_request_policy_spec.rb b/spec/policies/merge_request_policy_spec.rb
index dd42e1b9313..7e1af132b1d 100644
--- a/spec/policies/merge_request_policy_spec.rb
+++ b/spec/policies/merge_request_policy_spec.rb
@@ -7,24 +7,18 @@ RSpec.describe MergeRequestPolicy do
let_it_be(:guest) { create(:user) }
let_it_be(:author) { create(:user) }
+ let_it_be(:reporter) { create(:user) }
let_it_be(:developer) { create(:user) }
let_it_be(:non_team_member) { create(:user) }
- let(:project) { create(:project, :public) }
-
def permissions(user, merge_request)
described_class.new(user, merge_request)
end
- before do
- project.add_guest(guest)
- project.add_guest(author)
- project.add_developer(developer)
- end
-
mr_perms = %i[create_merge_request_in
create_merge_request_from
read_merge_request
+ update_merge_request
create_todo
approve_merge_request
create_note
@@ -40,7 +34,28 @@ RSpec.describe MergeRequestPolicy do
end
end
- shared_examples_for 'a user with access' do
+ shared_examples_for 'a user with reporter access' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:policy, :is_allowed) do
+ :create_merge_request_in | true
+ :read_merge_request | true
+ :create_todo | true
+ :create_note | true
+ :update_subscription | true
+ :create_merge_request_from | false
+ :approve_merge_request | false
+ :update_merge_request | false
+ end
+
+ with_them do
+ specify do
+ is_allowed ? (is_expected.to be_allowed(policy)) : (is_expected.to be_disallowed(policy))
+ end
+ end
+ end
+
+ shared_examples_for 'a user with full access' do
let(:perms) { permissions(subject, merge_request) }
mr_perms.each do |thing|
@@ -50,199 +65,304 @@ RSpec.describe MergeRequestPolicy do
end
end
- context 'when merge request is public' do
- let(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) }
- let(:user) { author }
-
- context 'and user is anonymous' do
- subject { permissions(nil, merge_request) }
+ context 'when user is a direct project member' do
+ let(:project) { create(:project, :public) }
- it do
- is_expected.to be_disallowed(:create_todo, :update_subscription)
- end
+ before do
+ project.add_guest(guest)
+ project.add_guest(author)
+ project.add_developer(developer)
end
- context 'and user is author' do
- subject { permissions(user, merge_request) }
+ context 'when merge request is public' do
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) }
+ let(:user) { author }
- context 'and the user is a guest' do
- let(:user) { guest }
+ context 'and user is author' do
+ subject { permissions(user, merge_request) }
- it do
- is_expected.to be_allowed(:update_merge_request)
- end
+ context 'and the user is a guest' do
+ let(:user) { guest }
- it do
- is_expected.to be_allowed(:reopen_merge_request)
- end
+ it do
+ is_expected.to be_allowed(:update_merge_request)
+ end
- it do
- is_expected.to be_allowed(:approve_merge_request)
+ it do
+ is_expected.to be_allowed(:reopen_merge_request)
+ end
+
+ it do
+ is_expected.to be_allowed(:approve_merge_request)
+ end
end
end
+ end
- context 'and the user is a group member' do
- let(:project) { create(:project, :public, group: group) }
- let(:group) { create(:group) }
- let(:user) { non_team_member }
-
- before do
- group.add_guest(non_team_member)
- end
+ context 'when merge requests have been disabled' do
+ let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: author) }
- it do
- is_expected.to be_allowed(:approve_merge_request)
- end
+ before do
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::DISABLED)
end
- context 'and the user is a member of a shared group' do
- let(:user) { non_team_member }
+ describe 'the author' do
+ subject { author }
- before do
- group = create(:group)
- project.project_group_links.create!(
- group: group,
- group_access: Gitlab::Access::DEVELOPER)
+ it_behaves_like 'a denied user'
+ end
- group.add_guest(non_team_member)
- end
+ describe 'a guest' do
+ subject { guest }
- it do
- is_expected.to be_allowed(:approve_merge_request)
- end
+ it_behaves_like 'a denied user'
end
- context 'and the user is not a project member' do
- let(:user) { non_team_member }
+ describe 'a developer' do
+ subject { developer }
- it do
- is_expected.not_to be_allowed(:approve_merge_request)
- end
+ it_behaves_like 'a denied user'
end
end
- end
- context 'when merge requests have been disabled' do
- let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: author) }
+ context 'when merge requests are private' do
+ let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: author) }
- before do
- project.project_feature.update!(merge_requests_access_level: ProjectFeature::DISABLED)
- end
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
+ end
- describe 'the author' do
- subject { author }
+ describe 'the author' do
+ subject { author }
- it_behaves_like 'a denied user'
+ it_behaves_like 'a denied user'
+ end
+
+ describe 'a developer' do
+ subject { developer }
+
+ it_behaves_like 'a user with full access'
+ end
end
- describe 'a guest' do
- subject { guest }
+ context 'when merge request is unlocked' do
+ let(:merge_request) { create(:merge_request, :closed, source_project: project, target_project: project, author: author) }
- it_behaves_like 'a denied user'
+ it 'allows author to reopen merge request' do
+ expect(permissions(author, merge_request)).to be_allowed(:reopen_merge_request)
+ end
+
+ it 'allows developer to reopen merge request' do
+ expect(permissions(developer, merge_request)).to be_allowed(:reopen_merge_request)
+ end
+
+ it 'prevents guest from reopening merge request' do
+ expect(permissions(guest, merge_request)).to be_disallowed(:reopen_merge_request)
+ end
end
- describe 'a developer' do
- subject { developer }
+ context 'when merge request is locked' do
+ let(:merge_request_locked) { create(:merge_request, :closed, discussion_locked: true, source_project: project, target_project: project, author: author) }
- it_behaves_like 'a denied user'
+ it 'prevents author from reopening merge request' do
+ expect(permissions(author, merge_request_locked)).to be_disallowed(:reopen_merge_request)
+ end
+
+ it 'prevents developer from reopening merge request' do
+ expect(permissions(developer, merge_request_locked)).to be_disallowed(:reopen_merge_request)
+ end
+
+ it 'prevents guests from reopening merge request' do
+ expect(permissions(guest, merge_request_locked)).to be_disallowed(:reopen_merge_request)
+ end
+
+ context 'when the user is project member, with at least guest access' do
+ let(:user) { guest }
+
+ it 'can create a note' do
+ expect(permissions(user, merge_request_locked)).to be_allowed(:create_note)
+ end
+ end
end
- describe 'any other user' do
- subject { non_team_member }
+ context 'with external authorization enabled' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:policies) { described_class.new(user, merge_request) }
- it_behaves_like 'a denied user'
+ before do
+ enable_external_authorization_service_check
+ end
+
+ it 'can read the issue iid without accessing the external service' do
+ expect(::Gitlab::ExternalAuthorization).not_to receive(:access_allowed?)
+
+ expect(policies).to be_allowed(:read_merge_request_iid)
+ end
end
end
- context 'when merge requests are private' do
- let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: author) }
+ context 'when user is an inherited member from the parent group' do
+ let_it_be(:group) { create(:group, :public) }
- before do
- project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
- project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: author) }
+
+ before_all do
+ group.add_guest(guest)
+ group.add_guest(author)
+ group.add_reporter(reporter)
+ group.add_developer(developer)
end
- describe 'a non-team-member' do
- subject { non_team_member }
+ context 'when project is public' do
+ let(:project) { create(:project, :public, group: group) }
- it_behaves_like 'a denied user'
- end
+ describe 'the merge request author' do
+ subject { permissions(author, merge_request) }
- describe 'the author' do
- subject { author }
+ specify do
+ is_expected.to be_allowed(:approve_merge_request)
+ end
+ end
- it_behaves_like 'a denied user'
+ context 'and merge requests are private' do
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
+ end
+
+ describe 'a guest' do
+ subject { guest }
+
+ it_behaves_like 'a denied user'
+ end
+
+ describe 'a reporter' do
+ subject { permissions(reporter, merge_request) }
+
+ it_behaves_like 'a user with reporter access'
+ end
+
+ describe 'a developer' do
+ subject { developer }
+
+ it_behaves_like 'a user with full access'
+ end
+ end
end
- describe 'a developer' do
- subject { developer }
+ context 'when project is private' do
+ let(:project) { create(:project, :private, group: group) }
+
+ describe 'a guest' do
+ subject { guest }
- it_behaves_like 'a user with access'
+ it_behaves_like 'a denied user'
+ end
+
+ describe 'a reporter' do
+ subject { permissions(reporter, merge_request) }
+
+ it_behaves_like 'a user with reporter access'
+ end
+
+ describe 'a developer' do
+ subject { developer }
+
+ it_behaves_like 'a user with full access'
+ end
end
end
- context 'when merge request is unlocked' do
- let(:merge_request) { create(:merge_request, :closed, source_project: project, target_project: project, author: author) }
+ context 'when user is an inherited member from a shared group' do
+ let(:project) { create(:project, :public) }
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) }
+ let(:user) { author }
- it 'allows author to reopen merge request' do
- expect(permissions(author, merge_request)).to be_allowed(:reopen_merge_request)
+ before do
+ project.add_guest(author)
end
- it 'allows developer to reopen merge request' do
- expect(permissions(developer, merge_request)).to be_allowed(:reopen_merge_request)
- end
+ context 'and group is given developer access' do
+ let(:user) { non_team_member }
+
+ subject { permissions(user, merge_request) }
+
+ before do
+ group = create(:group)
+ project.project_group_links.create!(
+ group: group,
+ group_access: Gitlab::Access::DEVELOPER)
+
+ group.add_guest(non_team_member)
+ end
- it 'prevents guest from reopening merge request' do
- expect(permissions(guest, merge_request)).to be_disallowed(:reopen_merge_request)
+ specify do
+ is_expected.to be_allowed(:approve_merge_request)
+ end
end
end
- context 'when merge request is locked' do
- let(:merge_request_locked) { create(:merge_request, :closed, discussion_locked: true, source_project: project, target_project: project, author: author) }
+ context 'when user is not a project member' do
+ let(:project) { create(:project, :public) }
- it 'prevents author from reopening merge request' do
- expect(permissions(author, merge_request_locked)).to be_disallowed(:reopen_merge_request)
- end
+ context 'when merge request is public' do
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
- it 'prevents developer from reopening merge request' do
- expect(permissions(developer, merge_request_locked)).to be_disallowed(:reopen_merge_request)
+ subject { permissions(non_team_member, merge_request) }
+
+ specify do
+ is_expected.not_to be_allowed(:approve_merge_request)
+ end
end
- it 'prevents guests from reopening merge request' do
- expect(permissions(guest, merge_request_locked)).to be_disallowed(:reopen_merge_request)
+ context 'when merge requests are disabled' do
+ let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
+
+ before do
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::DISABLED)
+ end
+
+ subject { non_team_member }
+
+ it_behaves_like 'a denied user'
end
- context 'when the user is not a project member' do
- let(:user) { create(:user) }
+ context 'when merge requests are private' do
+ let!(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
- it 'cannot create a note' do
- expect(permissions(user, merge_request_locked)).to be_disallowed(:create_note)
+ before do
+ project.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
end
+
+ subject { non_team_member }
+
+ it_behaves_like 'a denied user'
end
- context 'when the user is project member, with at least guest access' do
- let(:user) { guest }
+ context 'when merge request is locked' do
+ let(:merge_request) { create(:merge_request, :closed, discussion_locked: true, source_project: project, target_project: project) }
- it 'can create a note' do
- expect(permissions(user, merge_request_locked)).to be_allowed(:create_note)
+ it 'cannot create a note' do
+ expect(permissions(non_team_member, merge_request)).to be_disallowed(:create_note)
end
end
end
- context 'with external authorization enabled' do
- let(:user) { create(:user) }
+ context 'when user is anonymous' do
let(:project) { create(:project, :public) }
- let(:merge_request) { create(:merge_request, source_project: project) }
- let(:policies) { described_class.new(user, merge_request) }
- before do
- enable_external_authorization_service_check
- end
+ context 'when merge request is public' do
+ let(:merge_request) { create(:merge_request, source_project: project, target_project: project) }
- it 'can read the issue iid without accessing the external service' do
- expect(::Gitlab::ExternalAuthorization).not_to receive(:access_allowed?)
+ subject { permissions(nil, merge_request) }
- expect(policies).to be_allowed(:read_merge_request_iid)
+ specify do
+ is_expected.to be_disallowed(:create_todo, :update_subscription)
+ end
end
end
end
diff --git a/spec/policies/packages/policies/group_policy_spec.rb b/spec/policies/packages/policies/group_policy_spec.rb
new file mode 100644
index 00000000000..d0d9a9a22f5
--- /dev/null
+++ b/spec/policies/packages/policies/group_policy_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Policies::GroupPolicy do
+ include_context 'GroupPolicy context'
+
+ subject { described_class.new(current_user, group.packages_policy_subject) }
+
+ describe 'read_package' do
+ context 'with admin' do
+ let(:current_user) { admin }
+
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it { is_expected.to be_allowed(:read_package) }
+ end
+
+ context 'when admin mode is disabled' do
+ it { is_expected.to be_disallowed(:read_package) }
+ end
+ end
+
+ context 'with owner' do
+ let(:current_user) { owner }
+
+ it { is_expected.to be_allowed(:read_package) }
+ end
+
+ context 'with maintainer' do
+ let(:current_user) { maintainer }
+
+ it { is_expected.to be_allowed(:read_package) }
+ end
+
+ context 'with reporter' do
+ let(:current_user) { reporter }
+
+ it { is_expected.to be_allowed(:read_package) }
+ end
+
+ context 'with guest' do
+ let(:current_user) { guest }
+
+ it { is_expected.to be_disallowed(:read_package) }
+ end
+
+ context 'with non member' do
+ let(:current_user) { create(:user) }
+
+ it { is_expected.to be_disallowed(:read_package) }
+ end
+
+ context 'with anonymous' do
+ let(:current_user) { nil }
+
+ it { is_expected.to be_disallowed(:read_package) }
+ end
+ end
+
+ describe 'deploy token access' do
+ let!(:group_deploy_token) do
+ create(:group_deploy_token, group: group, deploy_token: deploy_token)
+ end
+
+ subject { described_class.new(deploy_token, group.packages_policy_subject) }
+
+ context 'when a deploy token with read_package_registry scope' do
+ let(:deploy_token) { create(:deploy_token, :group, read_package_registry: true) }
+
+ it { is_expected.to be_allowed(:read_package) }
+ end
+
+ context 'when a deploy token with write_package_registry scope' do
+ let(:deploy_token) { create(:deploy_token, :group, write_package_registry: true) }
+
+ it { is_expected.to be_allowed(:read_package) }
+ end
+ end
+end
diff --git a/spec/policies/packages/policies/project_policy_spec.rb b/spec/policies/packages/policies/project_policy_spec.rb
new file mode 100644
index 00000000000..5d54ee54572
--- /dev/null
+++ b/spec/policies/packages/policies/project_policy_spec.rb
@@ -0,0 +1,164 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Packages::Policies::ProjectPolicy do
+ include_context 'ProjectPolicy context'
+
+ let(:project) { public_project }
+
+ subject { described_class.new(current_user, project.packages_policy_subject) }
+
+ describe 'deploy token access' do
+ let!(:project_deploy_token) do
+ create(:project_deploy_token, project: project, deploy_token: deploy_token)
+ end
+
+ subject { described_class.new(deploy_token, project.packages_policy_subject) }
+
+ context 'when a deploy token with read_package_registry scope' do
+ let(:deploy_token) { create(:deploy_token, read_package_registry: true) }
+
+ it { is_expected.to be_allowed(:read_package) }
+
+ it_behaves_like 'package access with repository disabled'
+ end
+
+ context 'when a deploy token with write_package_registry scope' do
+ let(:deploy_token) { create(:deploy_token, write_package_registry: true) }
+
+ it { is_expected.to be_allowed(:read_package) }
+
+ it_behaves_like 'package access with repository disabled'
+ end
+ end
+
+ describe 'read_package', :enable_admin_mode do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:project, :package_registry_access_level, :current_user, :expect_to_be_allowed) do
+ ref(:private_project) | ProjectFeature::DISABLED | ref(:anonymous) | false
+ ref(:private_project) | ProjectFeature::DISABLED | ref(:non_member) | false
+ ref(:private_project) | ProjectFeature::DISABLED | ref(:guest) | false
+ ref(:private_project) | ProjectFeature::DISABLED | ref(:reporter) | false
+ ref(:private_project) | ProjectFeature::DISABLED | ref(:developer) | false
+ ref(:private_project) | ProjectFeature::DISABLED | ref(:maintainer) | false
+ ref(:private_project) | ProjectFeature::DISABLED | ref(:owner) | false
+ ref(:private_project) | ProjectFeature::DISABLED | ref(:admin) | false
+
+ ref(:private_project) | ProjectFeature::PRIVATE | ref(:anonymous) | false
+ ref(:private_project) | ProjectFeature::PRIVATE | ref(:non_member) | false
+ ref(:private_project) | ProjectFeature::PRIVATE | ref(:guest) | false
+ ref(:private_project) | ProjectFeature::PRIVATE | ref(:reporter) | true
+ ref(:private_project) | ProjectFeature::PRIVATE | ref(:developer) | true
+ ref(:private_project) | ProjectFeature::PRIVATE | ref(:maintainer) | true
+ ref(:private_project) | ProjectFeature::PRIVATE | ref(:owner) | true
+ ref(:private_project) | ProjectFeature::PRIVATE | ref(:admin) | true
+
+ ref(:private_project) | ProjectFeature::PUBLIC | ref(:anonymous) | true
+ ref(:private_project) | ProjectFeature::PUBLIC | ref(:non_member) | true
+ ref(:private_project) | ProjectFeature::PUBLIC | ref(:guest) | true
+ ref(:private_project) | ProjectFeature::PUBLIC | ref(:reporter) | true
+ ref(:private_project) | ProjectFeature::PUBLIC | ref(:developer) | true
+ ref(:private_project) | ProjectFeature::PUBLIC | ref(:maintainer) | true
+ ref(:private_project) | ProjectFeature::PUBLIC | ref(:owner) | true
+ ref(:private_project) | ProjectFeature::PUBLIC | ref(:admin) | true
+
+ ref(:internal_project) | ProjectFeature::DISABLED | ref(:anonymous) | false
+ ref(:internal_project) | ProjectFeature::DISABLED | ref(:non_member) | false
+ ref(:internal_project) | ProjectFeature::DISABLED | ref(:guest) | false
+ ref(:internal_project) | ProjectFeature::DISABLED | ref(:reporter) | false
+ ref(:internal_project) | ProjectFeature::DISABLED | ref(:developer) | false
+ ref(:internal_project) | ProjectFeature::DISABLED | ref(:maintainer) | false
+ ref(:internal_project) | ProjectFeature::DISABLED | ref(:owner) | false
+ ref(:internal_project) | ProjectFeature::DISABLED | ref(:admin) | false
+
+ ref(:internal_project) | ProjectFeature::ENABLED | ref(:anonymous) | false
+ ref(:internal_project) | ProjectFeature::ENABLED | ref(:non_member) | true
+ ref(:internal_project) | ProjectFeature::ENABLED | ref(:guest) | true
+ ref(:internal_project) | ProjectFeature::ENABLED | ref(:reporter) | true
+ ref(:internal_project) | ProjectFeature::ENABLED | ref(:developer) | true
+ ref(:internal_project) | ProjectFeature::ENABLED | ref(:maintainer) | true
+ ref(:internal_project) | ProjectFeature::ENABLED | ref(:owner) | true
+ ref(:internal_project) | ProjectFeature::ENABLED | ref(:admin) | true
+
+ ref(:internal_project) | ProjectFeature::PUBLIC | ref(:anonymous) | true
+ ref(:internal_project) | ProjectFeature::PUBLIC | ref(:non_member) | true
+ ref(:internal_project) | ProjectFeature::PUBLIC | ref(:guest) | true
+ ref(:internal_project) | ProjectFeature::PUBLIC | ref(:reporter) | true
+ ref(:internal_project) | ProjectFeature::PUBLIC | ref(:developer) | true
+ ref(:internal_project) | ProjectFeature::PUBLIC | ref(:maintainer) | true
+ ref(:internal_project) | ProjectFeature::PUBLIC | ref(:owner) | true
+ ref(:internal_project) | ProjectFeature::PUBLIC | ref(:admin) | true
+
+ ref(:public_project) | ProjectFeature::DISABLED | ref(:anonymous) | false
+ ref(:public_project) | ProjectFeature::DISABLED | ref(:non_member) | false
+ ref(:public_project) | ProjectFeature::DISABLED | ref(:guest) | false
+ ref(:public_project) | ProjectFeature::DISABLED | ref(:reporter) | false
+ ref(:public_project) | ProjectFeature::DISABLED | ref(:developer) | false
+ ref(:public_project) | ProjectFeature::DISABLED | ref(:maintainer) | false
+ ref(:public_project) | ProjectFeature::DISABLED | ref(:owner) | false
+ ref(:public_project) | ProjectFeature::DISABLED | ref(:admin) | false
+
+ ref(:public_project) | ProjectFeature::PUBLIC | ref(:anonymous) | true
+ ref(:public_project) | ProjectFeature::PUBLIC | ref(:non_member) | true
+ ref(:public_project) | ProjectFeature::PUBLIC | ref(:guest) | true
+ ref(:public_project) | ProjectFeature::PUBLIC | ref(:reporter) | true
+ ref(:public_project) | ProjectFeature::PUBLIC | ref(:developer) | true
+ ref(:public_project) | ProjectFeature::PUBLIC | ref(:maintainer) | true
+ ref(:public_project) | ProjectFeature::PUBLIC | ref(:owner) | true
+ ref(:public_project) | ProjectFeature::PUBLIC | ref(:admin) | true
+ end
+
+ with_them do
+ it do
+ project.project_feature.update!(package_registry_access_level: package_registry_access_level)
+
+ if expect_to_be_allowed
+ is_expected.to be_allowed(:read_package)
+ else
+ is_expected.to be_disallowed(:read_package)
+ end
+ end
+ end
+
+ context 'with feature flag disabled' do
+ before do
+ stub_feature_flags(package_registry_access_level: false)
+ end
+
+ where(:project, :current_user, :expect_to_be_allowed) do
+ ref(:private_project) | ref(:anonymous) | false
+ ref(:private_project) | ref(:non_member) | false
+ ref(:private_project) | ref(:guest) | false
+ ref(:internal_project) | ref(:anonymous) | false
+ ref(:public_project) | ref(:admin) | true
+ ref(:public_project) | ref(:owner) | true
+ ref(:public_project) | ref(:maintainer) | true
+ ref(:public_project) | ref(:developer) | true
+ ref(:public_project) | ref(:reporter) | true
+ ref(:public_project) | ref(:guest) | true
+ ref(:public_project) | ref(:non_member) | true
+ ref(:public_project) | ref(:anonymous) | true
+ end
+
+ with_them do
+ it do
+ project.project_feature.update!(package_registry_access_level: ProjectFeature::PUBLIC)
+
+ if expect_to_be_allowed
+ is_expected.to be_allowed(:read_package)
+ else
+ is_expected.to be_disallowed(:read_package)
+ end
+ end
+ end
+ end
+
+ context 'with admin' do
+ let(:current_user) { admin }
+
+ it_behaves_like 'package access with repository disabled'
+ end
+ end
+end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index e8fdf9a8e25..fefd9f71408 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -777,13 +777,13 @@ RSpec.describe ProjectPolicy do
project.add_developer(user)
end
- it { is_expected.not_to be_allowed(:project_bot_access)}
+ it { is_expected.not_to be_allowed(:project_bot_access) }
end
context "when project bot and not part of the project" do
let(:current_user) { project_bot }
- it { is_expected.not_to be_allowed(:project_bot_access)}
+ it { is_expected.not_to be_allowed(:project_bot_access) }
end
context "when project bot and part of the project" do
@@ -793,7 +793,7 @@ RSpec.describe ProjectPolicy do
project.add_developer(project_bot)
end
- it { is_expected.to be_allowed(:project_bot_access)}
+ it { is_expected.to be_allowed(:project_bot_access) }
end
end
@@ -804,7 +804,7 @@ RSpec.describe ProjectPolicy do
project.add_maintainer(project_bot)
end
- it { is_expected.not_to be_allowed(:create_resource_access_tokens)}
+ it { is_expected.not_to be_allowed(:create_resource_access_tokens) }
end
end
@@ -946,7 +946,7 @@ RSpec.describe ProjectPolicy do
context 'with anonymous' do
let(:current_user) { anonymous }
- it { is_expected.to be_disallowed(:metrics_dashboard)}
+ it { is_expected.to be_disallowed(:metrics_dashboard) }
end
end
@@ -1930,14 +1930,10 @@ RSpec.describe ProjectPolicy do
describe 'operations feature' do
using RSpec::Parameterized::TableSyntax
- before do
- stub_feature_flags(split_operations_visibility_permissions: false)
- end
+ let(:guest_permissions) { [:read_environment, :read_deployment] }
- let(:guest_operations_permissions) { [:read_environment, :read_deployment] }
-
- let(:developer_operations_permissions) do
- guest_operations_permissions + [
+ 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,
@@ -1946,13 +1942,17 @@ RSpec.describe ProjectPolicy do
]
end
- let(:maintainer_operations_permissions) do
- developer_operations_permissions + [
+ 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
@@ -2005,33 +2005,22 @@ RSpec.describe ProjectPolicy do
expect_disallowed(*permissions_abilities(role))
end
end
-
- def permissions_abilities(role)
- case role
- when :maintainer
- maintainer_operations_permissions
- when :developer
- developer_operations_permissions
- else
- guest_operations_permissions
- end
- end
end
end
describe 'environments feature' do
using RSpec::Parameterized::TableSyntax
- let(:guest_environments_permissions) { [:read_environment, :read_deployment] }
+ let(:guest_permissions) { [:read_environment, :read_deployment] }
- let(:developer_environments_permissions) do
- guest_environments_permissions + [
+ let(:developer_permissions) do
+ guest_permissions + [
:create_environment, :create_deployment, :update_environment, :update_deployment, :destroy_environment
]
end
- let(:maintainer_environments_permissions) do
- developer_environments_permissions + [:admin_environment, :admin_deployment]
+ let(:maintainer_permissions) do
+ developer_permissions + [:admin_environment, :admin_deployment]
end
where(:project_visibility, :access_level, :role, :allowed) do
@@ -2086,15 +2075,73 @@ RSpec.describe ProjectPolicy do
expect_disallowed(*permissions_abilities(role))
end
end
+ end
+ end
- def permissions_abilities(role)
- case role
- when :maintainer
- maintainer_environments_permissions
- when :developer
- developer_environments_permissions
+ describe 'monitor feature' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:guest_permissions) { [] }
+
+ let(:developer_permissions) do
+ guest_permissions + [
+ :read_sentry_issue, :read_alert_management_alert, :metrics_dashboard,
+ :update_sentry_issue, :update_alert_management_alert
+ ]
+ end
+
+ let(:maintainer_permissions) { developer_permissions }
+
+ 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 monitor feature access level' do
+ project.project_feature.update!(monitor_access_level: access_level)
+
+ if allowed
+ expect_allowed(*permissions_abilities(role))
else
- guest_environments_permissions
+ expect_disallowed(*permissions_abilities(role))
end
end
end
@@ -2682,6 +2729,43 @@ RSpec.describe ProjectPolicy do
end
end
+ describe 'read_milestone' do
+ context 'when project is public' do
+ let(:project) { public_project_in_group }
+
+ context 'and issues and merge requests are private' do
+ before do
+ project.project_feature.update!(
+ issues_access_level: ProjectFeature::PRIVATE,
+ merge_requests_access_level: ProjectFeature::PRIVATE
+ )
+ end
+
+ context 'when user is an inherited member from the group' do
+ context 'and user is a guest' do
+ let(:current_user) { inherited_guest }
+
+ it { is_expected.to be_allowed(:read_milestone) }
+ end
+
+ context 'and user is a reporter' do
+ let(:current_user) { inherited_reporter }
+
+ it { is_expected.to be_allowed(:read_milestone) }
+ end
+
+ context 'and user is a developer' do
+ let(:current_user) { inherited_developer }
+
+ it { is_expected.to be_allowed(:read_milestone) }
+ end
+ end
+ end
+ end
+ end
+
+ private
+
def project_subject(project_type)
case project_type
when :public
diff --git a/spec/policies/protected_branch_access_policy_spec.rb b/spec/policies/protected_branch_access_policy_spec.rb
new file mode 100644
index 00000000000..68a130d666a
--- /dev/null
+++ b/spec/policies/protected_branch_access_policy_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ProtectedBranchAccessPolicy do
+ let(:user) { create(:user) }
+ let(:protected_branch_access) { create(:protected_branch_merge_access_level) }
+ let(:project) { protected_branch_access.protected_branch.project }
+
+ subject { described_class.new(user, protected_branch_access) }
+
+ context 'as maintainers' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ it 'can be read' do
+ is_expected.to be_allowed(:read_protected_branch)
+ end
+ end
+
+ context 'as guests' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'can not be read' do
+ is_expected.to be_disallowed(:read_protected_branch)
+ end
+ end
+end
diff --git a/spec/policies/protected_branch_policy_spec.rb b/spec/policies/protected_branch_policy_spec.rb
index bb6dbff18a0..d676de14735 100644
--- a/spec/policies/protected_branch_policy_spec.rb
+++ b/spec/policies/protected_branch_policy_spec.rb
@@ -10,15 +10,47 @@ RSpec.describe ProtectedBranchPolicy do
subject { described_class.new(user, protected_branch) }
- it 'branches can be updated via project maintainers' do
- project.add_maintainer(user)
+ context 'as maintainers' do
+ before do
+ project.add_maintainer(user)
+ end
- is_expected.to be_allowed(:update_protected_branch)
+ it 'can be read' do
+ is_expected.to be_allowed(:read_protected_branch)
+ end
+
+ it 'can be created' do
+ is_expected.to be_allowed(:create_protected_branch)
+ end
+
+ it 'can be updated' do
+ is_expected.to be_allowed(:update_protected_branch)
+ end
+
+ it 'can be destroyed' do
+ is_expected.to be_allowed(:destroy_protected_branch)
+ end
end
- it "branches can't be updated by guests" do
- project.add_guest(user)
+ context 'as guests' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'can be read' do
+ is_expected.to be_disallowed(:read_protected_branch)
+ end
+
+ it 'can be created' do
+ is_expected.to be_disallowed(:create_protected_branch)
+ end
+
+ it 'can be updated' do
+ is_expected.to be_disallowed(:update_protected_branch)
+ end
- is_expected.to be_disallowed(:update_protected_branch)
+ it 'cannot be destroyed' do
+ is_expected.to be_disallowed(:destroy_protected_branch)
+ end
end
end
diff --git a/spec/policies/terraform/state_policy_spec.rb b/spec/policies/terraform/state_policy_spec.rb
index 82152920997..d75e20a2c66 100644
--- a/spec/policies/terraform/state_policy_spec.rb
+++ b/spec/policies/terraform/state_policy_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Terraform::StatePolicy do
let_it_be(:project) { create(:project) }
- let_it_be(:terraform_state) { create(:terraform_state, project: project)}
+ let_it_be(:terraform_state) { create(:terraform_state, project: project) }
subject { described_class.new(user, terraform_state) }
diff --git a/spec/policies/terraform/state_version_policy_spec.rb b/spec/policies/terraform/state_version_policy_spec.rb
index 6614e073332..4d41dd44455 100644
--- a/spec/policies/terraform/state_version_policy_spec.rb
+++ b/spec/policies/terraform/state_version_policy_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Terraform::StateVersionPolicy do
let_it_be(:project) { create(:project) }
- let_it_be(:terraform_state) { create(:terraform_state, :with_version, project: project)}
+ let_it_be(:terraform_state) { create(:terraform_state, :with_version, project: project) }
subject { described_class.new(user, terraform_state.latest_version) }
diff --git a/spec/presenters/blobs/notebook_presenter_spec.rb b/spec/presenters/blobs/notebook_presenter_spec.rb
index 12f4ed67897..2f05dc98fb9 100644
--- a/spec/presenters/blobs/notebook_presenter_spec.rb
+++ b/spec/presenters/blobs/notebook_presenter_spec.rb
@@ -5,11 +5,11 @@ require 'spec_helper'
RSpec.describe Blobs::NotebookPresenter do
include RepoHelpers
- let(:project) { create(:project, :repository) }
- let(:repository) { project.repository }
- let(:blob) { repository.blob_at('HEAD', 'files/ruby/regex.rb') }
- let(:user) { project.first_owner }
- let(:git_blob) { blob.__getobj__ }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:repository) { project.repository }
+ let_it_be(:blob) { repository.blob_at('HEAD', 'files/ruby/regex.rb') }
+ let_it_be(:user) { project.first_owner }
+ let_it_be(:git_blob) { blob.__getobj__ }
subject(:presenter) { described_class.new(blob, current_user: user) }
diff --git a/spec/presenters/ci/pipeline_presenter_spec.rb b/spec/presenters/ci/pipeline_presenter_spec.rb
index a278d4dad83..4539c3d06f6 100644
--- a/spec/presenters/ci/pipeline_presenter_spec.rb
+++ b/spec/presenters/ci/pipeline_presenter_spec.rb
@@ -100,7 +100,7 @@ RSpec.describe Ci::PipelinePresenter do
context 'for a detached merge request pipeline' do
let(:event_type) { :detached }
- it { is_expected.to eq('Detached merge request pipeline') }
+ it { is_expected.to eq('Merge request pipeline') }
end
context 'for a merged result pipeline' do
diff --git a/spec/presenters/clusters/cluster_presenter_spec.rb b/spec/presenters/clusters/cluster_presenter_spec.rb
index 7349f444fac..755f1ea6078 100644
--- a/spec/presenters/clusters/cluster_presenter_spec.rb
+++ b/spec/presenters/clusters/cluster_presenter_spec.rb
@@ -119,18 +119,20 @@ RSpec.describe Clusters::ClusterPresenter do
subject { cluster_presenter.health_data(clusterable_presenter) }
it do
- is_expected.to include('clusters-path': clusterable_presenter.index_path,
- 'dashboard-endpoint': clusterable_presenter.metrics_dashboard_path(cluster),
- 'documentation-path': help_page_path('user/infrastructure/clusters/manage/clusters_health'),
- 'add-dashboard-documentation-path': help_page_path('operations/metrics/dashboards/index.md', anchor: 'add-a-new-dashboard-to-your-project'),
- 'empty-getting-started-svg-path': match_asset_path('/assets/illustrations/monitoring/getting_started.svg'),
- 'empty-loading-svg-path': match_asset_path('/assets/illustrations/monitoring/loading.svg'),
- 'empty-no-data-svg-path': match_asset_path('/assets/illustrations/monitoring/no_data.svg'),
- 'empty-no-data-small-svg-path': match_asset_path('illustrations/chart-empty-state-small.svg'),
- 'empty-unable-to-connect-svg-path': match_asset_path('/assets/illustrations/monitoring/unable_to_connect.svg'),
- 'settings-path': '',
- 'project-path': '',
- 'tags-path': '')
+ is_expected.to include(
+ 'clusters-path': clusterable_presenter.index_path,
+ 'dashboard-endpoint': clusterable_presenter.metrics_dashboard_path(cluster),
+ 'documentation-path': help_page_path('user/infrastructure/clusters/manage/clusters_health'),
+ 'add-dashboard-documentation-path': help_page_path('operations/metrics/dashboards/index.md', anchor: 'add-a-new-dashboard-to-your-project'),
+ 'empty-getting-started-svg-path': match_asset_path('/assets/illustrations/monitoring/getting_started.svg'),
+ 'empty-loading-svg-path': match_asset_path('/assets/illustrations/monitoring/loading.svg'),
+ 'empty-no-data-svg-path': match_asset_path('/assets/illustrations/monitoring/no_data.svg'),
+ 'empty-no-data-small-svg-path': match_asset_path('illustrations/chart-empty-state-small.svg'),
+ 'empty-unable-to-connect-svg-path': match_asset_path('/assets/illustrations/monitoring/unable_to_connect.svg'),
+ 'settings-path': '',
+ 'project-path': '',
+ 'tags-path': ''
+ )
end
end
diff --git a/spec/presenters/deployments/deployment_presenter_spec.rb b/spec/presenters/deployments/deployment_presenter_spec.rb
new file mode 100644
index 00000000000..689451677f4
--- /dev/null
+++ b/spec/presenters/deployments/deployment_presenter_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Deployments::DeploymentPresenter do
+ let(:deployment) { create(:deployment) }
+ let(:presenter) { described_class.new(deployment) }
+
+ describe '#tags' do
+ it do
+ expect(deployment).to receive(:tags).and_return(['test'])
+ expect(presenter.tags).to eq([{ name: 'test', path: 'tags/test' }])
+ end
+ end
+end
diff --git a/spec/presenters/packages/composer/packages_presenter_spec.rb b/spec/presenters/packages/composer/packages_presenter_spec.rb
index 1f638e5b935..ae88acea61d 100644
--- a/spec/presenters/packages/composer/packages_presenter_spec.rb
+++ b/spec/presenters/packages/composer/packages_presenter_spec.rb
@@ -50,7 +50,7 @@ RSpec.describe ::Packages::Composer::PackagesPresenter do
end
describe '#provider' do
- subject { presenter.provider}
+ subject { presenter.provider }
let(:expected_json) do
{
diff --git a/spec/presenters/packages/conan/package_presenter_spec.rb b/spec/presenters/packages/conan/package_presenter_spec.rb
index d35137cd820..9b74d2e637e 100644
--- a/spec/presenters/packages/conan/package_presenter_spec.rb
+++ b/spec/presenters/packages/conan/package_presenter_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe ::Packages::Conan::PackagePresenter do
let_it_be(:package) { create(:conan_package) }
let_it_be(:project) { package.project }
let_it_be(:package_file_pending_destruction) { create(:package_file, :pending_destruction, package: package) }
- let_it_be(:conan_package_reference) { '123456789'}
+ let_it_be(:conan_package_reference) { '123456789' }
let(:params) { { package_scope: :instance } }
let(:presenter) { described_class.new(package, user, project, params) }
diff --git a/spec/presenters/packages/nuget/packages_metadata_presenter_spec.rb b/spec/presenters/packages/nuget/packages_metadata_presenter_spec.rb
index b2bcdf8f03d..39682a3311c 100644
--- a/spec/presenters/packages/nuget/packages_metadata_presenter_spec.rb
+++ b/spec/presenters/packages/nuget/packages_metadata_presenter_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe Packages::Nuget::PackagesMetadataPresenter do
describe '#count' do
subject { presenter.count }
- it {is_expected.to eq 1}
+ it { is_expected.to eq 1 }
end
describe '#items' do
diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb
index df3e4b985ab..7ff19b1b770 100644
--- a/spec/presenters/project_presenter_spec.rb
+++ b/spec/presenters/project_presenter_spec.rb
@@ -102,7 +102,7 @@ RSpec.describe ProjectPresenter do
expect(release).to be_truthy
expect(presenter.releases_anchor_data).to have_attributes(
is_link: true,
- label: a_string_including("#{project.releases.count}"),
+ label: a_string_including("#{project.releases.count}"),
link: presenter.project_releases_path(project)
)
end
@@ -216,7 +216,7 @@ RSpec.describe ProjectPresenter do
it 'returns storage data' do
expect(presenter.storage_anchor_data).to have_attributes(
is_link: true,
- label: a_string_including('0 Bytes'),
+ label: a_string_including('0 Bytes'),
link: nil
)
end
@@ -270,7 +270,7 @@ RSpec.describe ProjectPresenter do
it 'returns storage data without usage quotas link for non-admin users' do
expect(presenter.storage_anchor_data).to have_attributes(
is_link: true,
- label: a_string_including('0 Bytes'),
+ label: a_string_including('0 Bytes'),
link: nil
)
end
@@ -280,7 +280,7 @@ RSpec.describe ProjectPresenter do
expect(presenter.storage_anchor_data).to have_attributes(
is_link: true,
- label: a_string_including('0 Bytes'),
+ label: a_string_including('0 Bytes'),
link: presenter.project_usage_quotas_path(project)
)
end
@@ -293,7 +293,7 @@ RSpec.describe ProjectPresenter do
expect(release).to be_truthy
expect(presenter.releases_anchor_data).to have_attributes(
is_link: true,
- label: a_string_including("#{project.releases.count}"),
+ label: a_string_including("#{project.releases.count}"),
link: presenter.project_releases_path(project)
)
end
@@ -484,6 +484,12 @@ RSpec.describe ProjectPresenter do
end
describe '#autodevops_anchor_data' do
+ it 'returns nil if builds feature is not available' do
+ allow(project).to receive(:feature_available?).with(:builds, user).and_return(false)
+
+ expect(presenter.autodevops_anchor_data).to be_nil
+ end
+
context 'when Auto Devops is enabled' do
it 'returns anchor data' do
allow(project).to receive(:auto_devops_enabled?).and_return(true)
@@ -566,7 +572,7 @@ RSpec.describe ProjectPresenter do
it 'returns upload_anchor_data' do
expect(presenter.upload_anchor_data).to have_attributes(
is_link: false,
- label: a_string_including('Upload file'),
+ label: a_string_including('Upload file'),
data: {
"can_push_code" => "true",
"original_branch" => "master",
@@ -613,7 +619,7 @@ RSpec.describe ProjectPresenter do
end
context 'empty repo' do
- let(:project) { create(:project, :stubbed_repository)}
+ let(:project) { create(:project, :stubbed_repository) }
context 'for a guest user' do
it 'orders the items correctly' do
diff --git a/spec/requests/admin/hook_logs_controller_spec.rb b/spec/requests/admin/hook_logs_controller_spec.rb
new file mode 100644
index 00000000000..f8d3381c052
--- /dev/null
+++ b/spec/requests/admin/hook_logs_controller_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Admin::HookLogsController, :enable_admin_mode 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) }
+
+ it_behaves_like WebHooks::HookLogActions do
+ let!(:show_path) { admin_hook_hook_log_path(web_hook, web_hook_log) }
+ let!(:retry_path) { retry_admin_hook_hook_log_path(web_hook, web_hook_log) }
+ let(:edit_hook_path) { edit_admin_hook_path(web_hook) }
+ end
+end
diff --git a/spec/requests/api/admin/batched_background_migrations_spec.rb b/spec/requests/api/admin/batched_background_migrations_spec.rb
new file mode 100644
index 00000000000..c99b21c0c27
--- /dev/null
+++ b/spec/requests/api/admin/batched_background_migrations_spec.rb
@@ -0,0 +1,230 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe API::Admin::BatchedBackgroundMigrations do
+ let(:admin) { create(:admin) }
+ let(:unauthorized_user) { create(:user) }
+
+ describe 'GET /admin/batched_background_migrations/:id' do
+ let!(:migration) { create(:batched_background_migration, :paused) }
+ let(:database) { :main }
+
+ subject(:show_migration) do
+ get api("/admin/batched_background_migrations/#{migration.id}", admin), params: { database: database }
+ end
+
+ it 'fetches the batched background migration' do
+ show_migration
+
+ aggregate_failures "testing response" do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['id']).to eq(migration.id)
+ expect(json_response['status']).to eq('paused')
+ expect(json_response['job_class_name']).to eq(migration.job_class_name)
+ expect(json_response['progress']).to be_zero
+ end
+ end
+
+ context 'when the batched background migration does not exist' do
+ let(:params) { { database: database } }
+
+ it 'returns 404' do
+ put api("/admin/batched_background_migrations/#{non_existing_record_id}", admin), params: params
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when multiple database is enabled' do
+ before do
+ skip_if_multiple_databases_not_setup
+ end
+
+ let(:ci_model) { Ci::ApplicationRecord }
+ let(:database) { :ci }
+
+ it 'uses the correct connection' do
+ expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(ci_model.connection).and_yield
+
+ show_migration
+ end
+ end
+
+ context 'when authenticated as a non-admin user' do
+ it 'returns 403' do
+ get api("/admin/batched_background_migrations/#{migration.id}", unauthorized_user)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+ end
+
+ describe 'GET /admin/batched_background_migrations' do
+ let!(:migration) { create(:batched_background_migration) }
+
+ context 'when is an admin user' do
+ it 'returns batched background migrations' do
+ get api('/admin/batched_background_migrations', admin)
+
+ aggregate_failures "testing response" do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.count).to eq(1)
+ expect(json_response.first['id']).to eq(migration.id)
+ expect(json_response.first['job_class_name']).to eq(migration.job_class_name)
+ expect(json_response.first['table_name']).to eq(migration.table_name)
+ expect(json_response.first['status']).to eq(migration.status_name.to_s)
+ expect(json_response.first['progress']).to be_zero
+ end
+ end
+
+ context 'when multiple database is enabled', :add_ci_connection do
+ let(:database) { :ci }
+ let(:schema) { :gitlab_ci }
+ let(:ci_model) { Ci::ApplicationRecord }
+
+ context 'when CI database is provided' do
+ let(:db_config) { instance_double(ActiveRecord::DatabaseConfigurations::HashConfig, name: 'fake_db') }
+ let(:default_model) { ActiveRecord::Base }
+ let(:base_models) { { 'fake_db' => default_model, 'ci' => ci_model }.with_indifferent_access }
+
+ it "uses CI database connection" do
+ allow(Gitlab::Database).to receive(:db_config_for_connection).and_return(db_config)
+ allow(Gitlab::Database).to receive(:database_base_models).and_return(base_models)
+
+ expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(ci_model.connection).and_yield
+
+ get api('/admin/batched_background_migrations', admin), params: { database: :ci }
+ end
+
+ it 'returns CI database records' do
+ # If we only have one DB we'll see both migrations
+ skip_if_multiple_databases_not_setup
+
+ ci_database_migration = Gitlab::Database::SharedModel.using_connection(ci_model.connection) do
+ create(:batched_background_migration, :active, gitlab_schema: schema)
+ end
+
+ get api('/admin/batched_background_migrations', admin), params: { database: :ci }
+
+ aggregate_failures "testing response" do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.count).to eq(1)
+ expect(json_response.first['id']).to eq(ci_database_migration.id)
+ expect(json_response.first['job_class_name']).to eq(ci_database_migration.job_class_name)
+ expect(json_response.first['table_name']).to eq(ci_database_migration.table_name)
+ expect(json_response.first['status']).to eq(ci_database_migration.status_name.to_s)
+ expect(json_response.first['progress']).to be_zero
+ end
+ end
+ end
+ end
+ end
+
+ context 'when authenticated as a non-admin user' do
+ it 'returns 403' do
+ get api('/admin/batched_background_migrations', unauthorized_user)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+ end
+
+ describe 'PUT /admin/batched_background_migrations/:id/resume' do
+ let!(:migration) { create(:batched_background_migration, :paused) }
+ let(:database) { :main }
+
+ subject(:resume) do
+ put api("/admin/batched_background_migrations/#{migration.id}/resume", admin), params: { database: database }
+ end
+
+ it 'pauses the batched background migration' do
+ resume
+
+ aggregate_failures "testing response" do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['id']).to eq(migration.id)
+ expect(json_response['status']).to eq('active')
+ end
+ end
+
+ context 'when the batched background migration does not exist' do
+ let(:params) { { database: database } }
+
+ it 'returns 404' do
+ put api("/admin/batched_background_migrations/#{non_existing_record_id}/resume", admin), params: params
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when multiple database is enabled' do
+ let(:ci_model) { Ci::ApplicationRecord }
+ let(:database) { :ci }
+
+ before do
+ skip_if_multiple_databases_not_setup
+ end
+
+ it 'uses the correct connection' do
+ expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(ci_model.connection).and_yield
+
+ resume
+ end
+ end
+
+ context 'when authenticated as a non-admin user' do
+ it 'returns 403' do
+ put api("/admin/batched_background_migrations/#{migration.id}/resume", unauthorized_user)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+ end
+
+ describe 'PUT /admin/batched_background_migrations/:id/pause' do
+ let!(:migration) { create(:batched_background_migration, :active) }
+
+ it 'pauses the batched background migration' do
+ put api("/admin/batched_background_migrations/#{migration.id}/pause", admin), params: { database: :main }
+
+ aggregate_failures "testing response" do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['id']).to eq(migration.id)
+ expect(json_response['status']).to eq('paused')
+ end
+ end
+
+ context 'when the batched background migration does not exist' do
+ let(:params) { { database: :main } }
+
+ it 'returns 404' do
+ put api("/admin/batched_background_migrations/#{non_existing_record_id}/pause", admin), params: params
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when multiple database is enabled' do
+ let(:ci_model) { Ci::ApplicationRecord }
+
+ before do
+ skip_if_multiple_databases_not_setup
+ end
+
+ it 'uses the correct connection' do
+ expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(ci_model.connection).and_yield
+
+ put api("/admin/batched_background_migrations/#{migration.id}/pause", admin), params: { database: :ci }
+ end
+ end
+
+ context 'when authenticated as a non-admin user' do
+ it 'returns 403' do
+ put api("/admin/batched_background_migrations/#{non_existing_record_id}/pause", unauthorized_user)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb
index cc696d76a02..f7539e13b80 100644
--- a/spec/requests/api/branches_spec.rb
+++ b/spec/requests/api/branches_spec.rb
@@ -211,6 +211,68 @@ RSpec.describe API::Branches do
end
it_behaves_like 'repository branches'
+
+ context 'caching' do
+ it 'caches the query' do
+ get api(route), params: { per_page: 1 }
+
+ expect(API::Entities::Branch).not_to receive(:represent)
+
+ get api(route), params: { per_page: 1 }
+ end
+
+ context 'when increase_branch_cache_expiry is enabled' do
+ it 'uses the cache up to 60 minutes' do
+ time_of_request = Time.current
+
+ get api(route), params: { per_page: 1 }
+
+ travel_to time_of_request + 59.minutes do
+ expect(API::Entities::Branch).not_to receive(:represent)
+
+ get api(route), params: { per_page: 1 }
+ end
+ end
+
+ it 'requests for new value after 60 minutes' do
+ get api(route), params: { per_page: 1 }
+
+ travel_to 61.minutes.from_now do
+ expect(API::Entities::Branch).to receive(:represent)
+
+ get api(route), params: { per_page: 1 }
+ end
+ end
+ end
+
+ context 'when increase_branch_cache_expiry is disabled' do
+ before do
+ stub_feature_flags(increase_branch_cache_expiry: false)
+ end
+
+ it 'uses the cache up to 10 minutes' do
+ time_of_request = Time.current
+
+ get api(route), params: { per_page: 1 }
+
+ travel_to time_of_request + 9.minutes do
+ expect(API::Entities::Branch).not_to receive(:represent)
+
+ get api(route), params: { per_page: 1 }
+ end
+ end
+
+ it 'requests for new value after 10 minutes' do
+ get api(route), params: { per_page: 1 }
+
+ travel_to 11.minutes.from_now do
+ expect(API::Entities::Branch).to receive(:represent)
+
+ get api(route), params: { per_page: 1 }
+ end
+ end
+ end
+ end
end
context 'when unauthenticated', 'and project is private' do
@@ -586,13 +648,36 @@ RSpec.describe API::Branches do
let(:route) { "/projects/#{project_id}/repository/branches/#{branch_name}/unprotect" }
shared_examples_for 'repository unprotected branch' do
- it 'unprotects a single branch' do
- put api(route, current_user)
+ context 'when branch is protected' do
+ let!(:protected_branch) { create(:protected_branch, project: project, name: protected_branch_name) }
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('public_api/v4/branch')
- expect(json_response['name']).to eq(CGI.unescape(branch_name))
- expect(json_response['protected']).to eq(false)
+ it 'unprotects a single branch' do
+ expect_next_instance_of(::ProtectedBranches::DestroyService, project, current_user) do |instance|
+ expect(instance).to receive(:execute).with(protected_branch).and_call_original
+ end
+
+ put api(route, current_user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('public_api/v4/branch')
+ expect(json_response['name']).to eq(CGI.unescape(branch_name))
+ expect(json_response['protected']).to eq(false)
+
+ expect { protected_branch.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
+ context 'when branch is not protected' do
+ it 'returns a single branch response' do
+ expect(::ProtectedBranches::DestroyService).not_to receive(:new)
+
+ put api(route, current_user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('public_api/v4/branch')
+ expect(json_response['name']).to eq(CGI.unescape(branch_name))
+ expect(json_response['protected']).to eq(false)
+ end
end
context 'when branch does not exist' do
@@ -637,40 +722,40 @@ RSpec.describe API::Branches do
context 'when authenticated', 'as a maintainer' do
let(:current_user) { user }
+ let(:protected_branch_name) { branch_name }
- context "when a protected branch doesn't already exist" do
- it_behaves_like 'repository unprotected branch'
+ it_behaves_like 'repository unprotected branch'
- context 'when branch contains a dot' do
- let(:branch_name) { branch_with_dot }
+ context 'when branch contains a dot' do
+ let(:branch_name) { branch_with_dot }
- it_behaves_like 'repository unprotected branch'
- end
+ it_behaves_like 'repository unprotected branch'
+ end
- context 'when branch contains a slash' do
- let(:branch_name) { branch_with_slash }
+ context 'when branch contains a slash' do
+ let(:branch_name) { branch_with_slash }
- it_behaves_like '404 response' do
- let(:request) { put api(route, current_user) }
- end
+ it_behaves_like '404 response' do
+ let(:request) { put api(route, current_user) }
end
+ end
- context 'when branch contains an escaped slash' do
- let(:branch_name) { CGI.escape(branch_with_slash) }
+ context 'when branch contains an escaped slash' do
+ let(:branch_name) { CGI.escape(branch_with_slash) }
+ let(:protected_branch_name) { branch_with_slash }
- it_behaves_like 'repository unprotected branch'
- end
+ it_behaves_like 'repository unprotected branch'
+ end
- context 'requesting with the escaped project full path' do
- let(:project_id) { CGI.escape(project.full_path) }
+ context 'requesting with the escaped project full path' do
+ let(:project_id) { CGI.escape(project.full_path) }
- it_behaves_like 'repository unprotected branch'
+ it_behaves_like 'repository unprotected branch'
- context 'when branch contains a dot' do
- let(:branch_name) { branch_with_dot }
+ context 'when branch contains a dot' do
+ let(:branch_name) { branch_with_dot }
- it_behaves_like 'repository unprotected branch'
- end
+ it_behaves_like 'repository unprotected branch'
end
end
end
diff --git a/spec/requests/api/ci/job_artifacts_spec.rb b/spec/requests/api/ci/job_artifacts_spec.rb
index 2fa1ffb4974..0fb11bf98d2 100644
--- a/spec/requests/api/ci/job_artifacts_spec.rb
+++ b/spec/requests/api/ci/job_artifacts_spec.rb
@@ -24,8 +24,7 @@ RSpec.describe API::Ci::JobArtifacts do
let(:guest) { create(:project_member, :guest, project: project).user }
let!(:job) do
- create(:ci_build, :success, :tags, pipeline: pipeline,
- artifacts_expire_at: 1.day.since)
+ create(:ci_build, :success, :tags, pipeline: pipeline, artifacts_expire_at: 1.day.since)
end
before do
@@ -535,8 +534,7 @@ RSpec.describe API::Ci::JobArtifacts do
context 'with regular branch' do
before do
pipeline.reload
- pipeline.update!(ref: 'master',
- sha: project.commit('master').sha)
+ pipeline.update!(ref: 'master', sha: project.commit('master').sha)
get_for_ref('master')
end
@@ -579,8 +577,7 @@ RSpec.describe API::Ci::JobArtifacts do
stub_artifacts_object_storage
job.success
- project.update!(visibility_level: visibility_level,
- public_builds: public_builds)
+ project.update!(visibility_level: visibility_level, public_builds: public_builds)
get_artifact_file(artifact)
end
@@ -676,8 +673,7 @@ RSpec.describe API::Ci::JobArtifacts do
context 'with branch name containing slash' do
before do
pipeline.reload
- pipeline.update!(ref: 'improve/awesome',
- sha: project.commit('improve/awesome').sha)
+ pipeline.update!(ref: 'improve/awesome', sha: project.commit('improve/awesome').sha)
end
it 'returns a specific artifact file for a valid path', :sidekiq_might_not_need_inline do
diff --git a/spec/requests/api/ci/jobs_spec.rb b/spec/requests/api/ci/jobs_spec.rb
index 57828e50320..b8983e9632e 100644
--- a/spec/requests/api/ci/jobs_spec.rb
+++ b/spec/requests/api/ci/jobs_spec.rb
@@ -32,8 +32,7 @@ RSpec.describe API::Ci::Jobs do
end
let!(:job) do
- create(:ci_build, :success, :tags, pipeline: pipeline,
- artifacts_expire_at: 1.day.since)
+ create(:ci_build, :success, :tags, pipeline: pipeline, artifacts_expire_at: 1.day.since)
end
before do
@@ -94,9 +93,13 @@ RSpec.describe API::Ci::Jobs do
let(:params_with_token) { {} }
end
+ def perform_request
+ get api('/job'), headers: headers_with_token, params: params_with_token
+ end
+
before do |example|
unless example.metadata[:skip_before_request]
- get api('/job'), headers: headers_with_token, params: params_with_token
+ perform_request
end
end
@@ -125,6 +128,15 @@ RSpec.describe API::Ci::Jobs do
expect(json_response['finished_at']).to be_nil
end
+ it 'avoids N+1 queries', :skip_before_request do
+ control_count = ActiveRecord::QueryRecorder.new { perform_request }.count
+
+ running_job = create(:ci_build, :running, project: project, user: user, pipeline: pipeline, artifacts_expire_at: 1.day.since)
+ running_job.save!
+
+ expect { perform_request }.not_to exceed_query_limit(control_count)
+ end
+
it_behaves_like 'returns common pipeline data' do
let(:jobx) { running_job }
end
@@ -237,6 +249,10 @@ RSpec.describe API::Ci::Jobs do
it 'includes environment slug' do
expect(json_response.dig('environment', 'slug')).to eq('production')
end
+
+ it 'includes environment tier' do
+ expect(json_response.dig('environment', 'tier')).to eq('production')
+ end
end
context 'when non-deployment environment action' do
@@ -248,6 +264,10 @@ RSpec.describe API::Ci::Jobs do
it 'includes environment slug' do
expect(json_response.dig('environment', 'slug')).to eq('review')
end
+
+ it 'includes environment tier' do
+ expect(json_response.dig('environment', 'tier')).to eq('development')
+ end
end
context 'when passing the token as params' do
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 cd58251cfcc..b33b97f90d7 100644
--- a/spec/requests/api/ci/runner/jobs_request_post_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_request_post_spec.rb
@@ -17,11 +17,12 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
describe '/api/v4/jobs' do
- let(:group) { create(:group, :nested) }
+ let_it_be(:group) { create(:group, :nested) }
+ let_it_be(:user) { create(:user) }
+
let(:project) { create(:project, namespace: group, shared_runners_enabled: false) }
- let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master') }
let(:runner) { create(:ci_runner, :project, projects: [project]) }
- let(:user) { create(:user) }
+ let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master') }
let(:job) do
create(:ci_build, :pending, :queued, :artifacts, :extended_options,
pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0)
@@ -145,7 +146,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
let(:expected_job_info) do
{ 'id' => job.id,
'name' => job.name,
- 'stage' => job.stage,
+ 'stage' => job.stage_name,
'project_id' => job.project.id,
'project_name' => job.project.name }
end
@@ -354,6 +355,9 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
context 'when GIT_DEPTH is not specified and there is no default git depth for the project' do
+ let(:project) { create(:project, namespace: group, shared_runners_enabled: false) }
+ let(:runner) { create(:ci_runner, :project, projects: [project]) }
+
before do
project.update!(ci_default_git_depth: nil)
end
@@ -411,7 +415,8 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
context 'when job is made for merge request' do
let(:pipeline) { create(:ci_pipeline, source: :merge_request_event, project: project, ref: 'feature', merge_request: merge_request) }
let!(:job) { create(:ci_build, :pending, :queued, pipeline: pipeline, name: 'spinach', ref: 'feature', stage: 'test', stage_idx: 0) }
- let(:merge_request) { create(:merge_request) }
+
+ let_it_be(:merge_request) { create(:merge_request) }
it 'sets branch as ref_type' do
request_job
@@ -546,9 +551,12 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
let!(:job) { create(:ci_build, :pending, :queued, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
let!(:job2) { create(:ci_build, :pending, :queued, :tag, pipeline: pipeline, name: 'rubocop', stage: 'test', stage_idx: 0) }
let!(:test_job) do
- create(:ci_build, :pending, :queued, pipeline: pipeline, name: 'deploy',
- stage: 'deploy', stage_idx: 1,
- options: { script: ['bash'], dependencies: [job2.name] })
+ create(:ci_build, :pending, :queued,
+ pipeline: pipeline,
+ name: 'deploy',
+ stage: 'deploy',
+ stage_idx: 1,
+ options: { script: ['bash'], dependencies: [job2.name] })
end
before do
@@ -570,9 +578,12 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
let!(:job) { create(:ci_build, :pending, :queued, :tag, pipeline: pipeline, name: 'spinach', stage: 'test', stage_idx: 0) }
let!(:job2) { create(:ci_build, :pending, :queued, :tag, pipeline: pipeline, name: 'rubocop', stage: 'test', stage_idx: 0) }
let!(:empty_dependencies_job) do
- create(:ci_build, :pending, :queued, pipeline: pipeline, name: 'empty_dependencies_job',
- stage: 'deploy', stage_idx: 1,
- options: { script: ['bash'], dependencies: [] })
+ create(:ci_build, :pending, :queued,
+ pipeline: pipeline,
+ name: 'empty_dependencies_job',
+ stage: 'deploy',
+ stage_idx: 1,
+ options: { script: ['bash'], dependencies: [] })
end
before do
@@ -722,7 +733,9 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
describe 'timeout support' do
context 'when project specifies job timeout' do
- let(:project) { create(:project, shared_runners_enabled: false, build_timeout: 1234) }
+ let_it_be(:project) { create(:project, shared_runners_enabled: false, build_timeout: 1234) }
+
+ let(:runner) { create(:ci_runner, :project, projects: [project]) }
it 'contains info about timeout taken from project' do
request_job
@@ -827,22 +840,6 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
'image' => { 'name' => 'ruby', 'pull_policy' => ['if-not-present'], 'entrypoint' => nil, 'ports' => [] }
)
end
-
- context 'when the FF ci_docker_image_pull_policy is disabled' do
- before do
- stub_feature_flags(ci_docker_image_pull_policy: false)
- end
-
- it 'returns the image without pull policy' do
- request_job
-
- expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to include(
- 'id' => job.id,
- 'image' => { 'name' => 'ruby', 'entrypoint' => nil, 'ports' => [] }
- )
- end
- end
end
context 'when service has pull_policy' do
@@ -867,31 +864,17 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
'ports' => [], 'pull_policy' => ['if-not-present'], 'variables' => [] }]
)
end
-
- context 'when the FF ci_docker_image_pull_policy is disabled' do
- before do
- stub_feature_flags(ci_docker_image_pull_policy: false)
- end
-
- it 'returns the service without pull policy' do
- request_job
-
- expect(response).to have_gitlab_http_status(:created)
- expect(json_response).to include(
- 'id' => job.id,
- 'services' => [{ 'alias' => nil, 'command' => nil, 'entrypoint' => nil, 'name' => 'postgres:11.9',
- 'ports' => [], 'variables' => [] }]
- )
- end
- end
end
describe 'a job with excluded artifacts' do
context 'when excluded paths are defined' do
let(:job) do
- create(:ci_build, :pending, :queued, pipeline: pipeline, name: 'test',
- stage: 'deploy', stage_idx: 1,
- options: { artifacts: { paths: ['abc'], exclude: ['cde'] } })
+ create(:ci_build, :pending, :queued,
+ pipeline: pipeline,
+ name: 'test',
+ stage: 'deploy',
+ stage_idx: 1,
+ options: { artifacts: { paths: ['abc'], exclude: ['cde'] } })
end
context 'when a runner supports this feature' do
@@ -950,8 +933,8 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
end
context 'when the runner is of group type' do
- let(:group) { create(:group) }
- let(:runner) { create(:ci_runner, :group, groups: [group]) }
+ let_it_be(:group) { create(:group) }
+ let_it_be(:runner) { create(:ci_runner, :group, groups: [group]) }
it_behaves_like 'storing arguments in the application context for the API' do
let(:expected_params) { { root_namespace: group.full_path_components.first, client_id: "runner/#{runner.id}" } }
diff --git a/spec/requests/api/ci/runners_spec.rb b/spec/requests/api/ci/runners_spec.rb
index 31b85a0b1d6..fa1f713e757 100644
--- a/spec/requests/api/ci/runners_spec.rb
+++ b/spec/requests/api/ci/runners_spec.rb
@@ -889,6 +889,44 @@ RSpec.describe API::Ci::Runners do
end
end
+ it 'avoids N+1 DB queries' do
+ get api("/runners/#{shared_runner.id}/jobs", admin)
+
+ control = ActiveRecord::QueryRecorder.new do
+ get api("/runners/#{shared_runner.id}/jobs", admin)
+ end
+
+ create(:ci_build, :failed, runner: shared_runner, project: project)
+
+ expect do
+ get api("/runners/#{shared_runner.id}/jobs", admin)
+ end.not_to exceed_query_limit(control.count)
+ end
+
+ it 'batches loading of commits' do
+ shared_runner = create(:ci_runner, :instance, description: 'Shared runner')
+
+ project_with_repo = create(:project, :repository)
+
+ pipeline = create(:ci_pipeline, project: project_with_repo, sha: 'ddd0f15ae83993f5cb66a927a28673882e99100b')
+ create(:ci_build, :running, runner: shared_runner, project: project_with_repo, pipeline: pipeline)
+
+ pipeline = create(:ci_pipeline, project: project_with_repo, sha: 'c1c67abbaf91f624347bb3ae96eabe3a1b742478')
+ create(:ci_build, :failed, runner: shared_runner, project: project_with_repo, pipeline: pipeline)
+
+ pipeline = create(:ci_pipeline, project: project_with_repo, sha: '1a0b36b3cdad1d2ee32457c102a8c0b7056fa863')
+ create(:ci_build, :failed, runner: shared_runner, project: project_with_repo, pipeline: pipeline)
+
+ expect_next_instance_of(Repository) do |repo|
+ expect(repo).to receive(:commits_by).with(oids: %w[
+ 1a0b36b3cdad1d2ee32457c102a8c0b7056fa863
+ c1c67abbaf91f624347bb3ae96eabe3a1b742478
+ ]).once.and_call_original
+ end
+
+ get api("/runners/#{shared_runner.id}/jobs", admin), params: { per_page: 2, order_by: 'id', sort: 'desc' }
+ end
+
context "when runner doesn't exist" do
it 'returns 404' do
get api('/runners/0/jobs', admin)
diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb
index 39be28d7427..dc5d9620dc4 100644
--- a/spec/requests/api/commit_statuses_spec.rb
+++ b/spec/requests/api/commit_statuses_spec.rb
@@ -478,6 +478,26 @@ RSpec.describe API::CommitStatuses do
.to include 'has already been taken'
end
end
+
+ context 'with partitions' do
+ let(:current_partition_id) { 123 }
+
+ before do
+ allow(Ci::Pipeline)
+ .to receive(:current_partition_value) { current_partition_id }
+ end
+
+ it 'creates records in the current partition' do
+ expect { post api(post_url, developer), params: { state: 'running' } }
+ .to change(CommitStatus, :count).by(1)
+ .and change(Ci::Pipeline, :count).by(1)
+
+ status = CommitStatus.find(json_response['id'])
+
+ expect(status.partition_id).to eq(current_partition_id)
+ expect(status.pipeline.partition_id).to eq(current_partition_id)
+ end
+ end
end
context 'reporter user' do
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 68fe45cd026..8a08d5203fd 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -7,14 +7,17 @@ RSpec.describe API::Commits do
include ProjectForksHelper
include SessionHelpers
- let(:user) { create(:user) }
- let(:guest) { create(:user).tap { |u| project.add_guest(u) } }
- let(:developer) { create(:user).tap { |u| project.add_developer(u) } }
- let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :repository, creator: user, path: 'my.project') }
+ let_it_be(:guest) { create(:user).tap { |u| project.add_guest(u) } }
+ let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
+
let(:branch_with_dot) { project.repository.find_branch('ends-with.json') }
let(:branch_with_slash) { project.repository.find_branch('improve/awesome') }
let(:project_id) { project.id }
let(:current_user) { nil }
+ let(:group) { create(:group, :public) }
+ let(:inherited_guest) { create(:user).tap { |u| group.add_guest(u) } }
before do
project.add_maintainer(user)
@@ -44,7 +47,7 @@ RSpec.describe API::Commits do
end
context 'when unauthenticated', 'and project is public' do
- let(:project) { create(:project, :public, :repository) }
+ let_it_be(:project) { create(:project, :public, :repository) }
it_behaves_like 'project commits'
end
@@ -56,311 +59,340 @@ RSpec.describe API::Commits do
end
end
- context 'when authenticated', 'as a maintainer' do
- let(:current_user) { user }
+ context 'when authenticated' do
+ context 'when user is a direct project member' do
+ context 'and user is a maintainer' do
+ let(:current_user) { user }
- it_behaves_like 'project commits'
+ it_behaves_like 'project commits'
- context "since optional parameter" do
- it "returns project commits since provided parameter" do
- commits = project.repository.commits("master", limit: 2)
- after = commits.second.created_at
+ context "since optional parameter" do
+ it "returns project commits since provided parameter" do
+ commits = project.repository.commits("master", limit: 2)
+ after = commits.second.created_at
- get api("/projects/#{project_id}/repository/commits?since=#{after.utc.iso8601}", user)
+ get api("/projects/#{project_id}/repository/commits?since=#{after.utc.iso8601}", user)
- expect(json_response.size).to eq 2
- expect(json_response.first["id"]).to eq(commits.first.id)
- expect(json_response.second["id"]).to eq(commits.second.id)
- end
+ expect(json_response.size).to eq 2
+ expect(json_response.first["id"]).to eq(commits.first.id)
+ expect(json_response.second["id"]).to eq(commits.second.id)
+ end
- it 'include correct pagination headers' do
- commits = project.repository.commits("master", limit: 2)
- after = commits.second.created_at
+ it 'include correct pagination headers' do
+ commits = project.repository.commits("master", limit: 2)
+ after = commits.second.created_at
- get api("/projects/#{project_id}/repository/commits?since=#{after.utc.iso8601}", user)
+ get api("/projects/#{project_id}/repository/commits?since=#{after.utc.iso8601}", user)
- expect(response).to include_limited_pagination_headers
- expect(response.headers['X-Page']).to eql('1')
- end
- end
+ expect(response).to include_limited_pagination_headers
+ expect(response.headers['X-Page']).to eql('1')
+ end
+ end
- context "until optional parameter" do
- it "returns project commits until provided parameter" do
- commits = project.repository.commits("master", limit: 20)
- before = commits.second.created_at
+ context "until optional parameter" do
+ it "returns project commits until provided parameter" do
+ commits = project.repository.commits("master", limit: 20)
+ before = commits.second.created_at
- get api("/projects/#{project_id}/repository/commits?until=#{before.utc.iso8601}", user)
+ get api("/projects/#{project_id}/repository/commits?until=#{before.utc.iso8601}", user)
- if commits.size == 20
- expect(json_response.size).to eq(20)
- else
- expect(json_response.size).to eq(commits.size - 1)
- end
+ if commits.size == 20
+ expect(json_response.size).to eq(20)
+ else
+ expect(json_response.size).to eq(commits.size - 1)
+ end
- expect(json_response.first["id"]).to eq(commits.second.id)
- expect(json_response.second["id"]).to eq(commits.third.id)
- end
+ expect(json_response.first["id"]).to eq(commits.second.id)
+ expect(json_response.second["id"]).to eq(commits.third.id)
+ end
- it 'include correct pagination headers' do
- commits = project.repository.commits("master", limit: 2)
- before = commits.second.created_at
+ it 'include correct pagination headers' do
+ commits = project.repository.commits("master", limit: 2)
+ before = commits.second.created_at
- get api("/projects/#{project_id}/repository/commits?until=#{before.utc.iso8601}", user)
+ get api("/projects/#{project_id}/repository/commits?until=#{before.utc.iso8601}", user)
- expect(response).to include_limited_pagination_headers
- expect(response.headers['X-Page']).to eql('1')
- end
- end
+ expect(response).to include_limited_pagination_headers
+ expect(response.headers['X-Page']).to eql('1')
+ end
+ end
- context "invalid xmlschema date parameters" do
- it "returns an invalid parameter error message" do
- get api("/projects/#{project_id}/repository/commits?since=invalid-date", user)
+ context "invalid xmlschema date parameters" do
+ it "returns an invalid parameter error message" do
+ get api("/projects/#{project_id}/repository/commits?since=invalid-date", user)
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['error']).to eq('since is invalid')
- end
- end
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq('since is invalid')
+ end
+ end
- context "with empty ref_name parameter" do
- let(:route) { "/projects/#{project_id}/repository/commits?ref_name=" }
+ context "with empty ref_name parameter" do
+ let(:route) { "/projects/#{project_id}/repository/commits?ref_name=" }
- it_behaves_like 'project commits'
- end
+ it_behaves_like 'project commits'
+ end
- context 'when repository does not exist' do
- let(:project) { create(:project, creator: user, path: 'my.project') }
+ context 'when repository does not exist' do
+ let(:project) { create(:project, creator: user, path: 'my.project') }
- it_behaves_like '404 response' do
- let(:request) { get api(route, current_user) }
- let(:message) { '404 Repository Not Found' }
- end
- end
+ it_behaves_like '404 response' do
+ let(:request) { get api(route, current_user) }
+ let(:message) { '404 Repository Not Found' }
+ end
+ end
- context "path optional parameter" do
- it "returns project commits matching provided path parameter" do
- path = 'files/ruby/popen.rb'
+ context "path optional parameter" do
+ it "returns project commits matching provided path parameter" do
+ path = 'files/ruby/popen.rb'
- get api("/projects/#{project_id}/repository/commits?path=#{path}", user)
+ get api("/projects/#{project_id}/repository/commits?path=#{path}", user)
- expect(json_response.size).to eq(3)
- expect(json_response.first["id"]).to eq("570e7b2abdd848b95f2f578043fc23bd6f6fd24d")
- expect(response).to include_limited_pagination_headers
- end
+ expect(json_response.size).to eq(3)
+ expect(json_response.first["id"]).to eq("570e7b2abdd848b95f2f578043fc23bd6f6fd24d")
+ expect(response).to include_limited_pagination_headers
+ end
- it 'include correct pagination headers' do
- path = 'files/ruby/popen.rb'
+ it 'include correct pagination headers' do
+ path = 'files/ruby/popen.rb'
- get api("/projects/#{project_id}/repository/commits?path=#{path}", user)
+ get api("/projects/#{project_id}/repository/commits?path=#{path}", user)
- expect(response).to include_limited_pagination_headers
- expect(response.headers['X-Page']).to eql('1')
- end
- end
+ expect(response).to include_limited_pagination_headers
+ expect(response.headers['X-Page']).to eql('1')
+ end
+ end
- context 'all optional parameter' do
- it 'returns all project commits' do
- expected_commit_ids = project.repository.commits(nil, all: true, limit: 50).map(&:id)
+ context 'all optional parameter' do
+ it 'returns all project commits' do
+ expected_commit_ids = project.repository.commits(nil, all: true, limit: 50).map(&:id)
- get api("/projects/#{project_id}/repository/commits?all=true&per_page=50", user)
+ get api("/projects/#{project_id}/repository/commits?all=true&per_page=50", user)
- commit_ids = json_response.map { |c| c['id'] }
+ commit_ids = json_response.map { |c| c['id'] }
- expect(response).to include_limited_pagination_headers
- expect(commit_ids).to eq(expected_commit_ids)
- expect(response.headers['X-Page']).to eql('1')
- end
- end
+ expect(response).to include_limited_pagination_headers
+ expect(commit_ids).to eq(expected_commit_ids)
+ expect(response.headers['X-Page']).to eql('1')
+ end
+ end
- context 'first_parent optional parameter' do
- it 'returns all first_parent commits' do
- expected_commit_ids = project.repository.commits(SeedRepo::Commit::ID, limit: 50, first_parent: true).map(&:id)
+ context 'first_parent optional parameter' do
+ it 'returns all first_parent commits' do
+ expected_commit_ids = project.repository.commits(SeedRepo::Commit::ID, limit: 50, first_parent: true).map(&:id)
- get api("/projects/#{project_id}/repository/commits?per_page=50", user), params: { ref_name: SeedRepo::Commit::ID, first_parent: 'true' }
+ get api("/projects/#{project_id}/repository/commits?per_page=50", user), params: { ref_name: SeedRepo::Commit::ID, first_parent: 'true' }
- commit_ids = json_response.map { |c| c['id'] }
+ commit_ids = json_response.map { |c| c['id'] }
- expect(response).to include_limited_pagination_headers
- expect(expected_commit_ids.size).to eq(12)
- expect(commit_ids).to eq(expected_commit_ids)
- end
- end
+ expect(response).to include_limited_pagination_headers
+ expect(expected_commit_ids.size).to eq(12)
+ expect(commit_ids).to eq(expected_commit_ids)
+ end
+ end
- context 'with_stats optional parameter' do
- let(:project) { create(:project, :public, :repository) }
+ context 'with_stats optional parameter' do
+ let(:project) { create(:project, :public, :repository) }
- it_behaves_like 'project commits', schema: 'public_api/v4/commits_with_stats' do
- let(:route) { "/projects/#{project_id}/repository/commits?with_stats=true" }
+ it_behaves_like 'project commits', schema: 'public_api/v4/commits_with_stats' do
+ let(:route) { "/projects/#{project_id}/repository/commits?with_stats=true" }
- it 'include commits details' do
- commit = project.repository.commit
- get api(route, current_user)
+ it 'include commits details' do
+ commit = project.repository.commit
+ get api(route, current_user)
- expect(json_response.first['stats']['additions']).to eq(commit.stats.additions)
- expect(json_response.first['stats']['deletions']).to eq(commit.stats.deletions)
- expect(json_response.first['stats']['total']).to eq(commit.stats.total)
+ expect(json_response.first['stats']['additions']).to eq(commit.stats.additions)
+ expect(json_response.first['stats']['deletions']).to eq(commit.stats.deletions)
+ expect(json_response.first['stats']['total']).to eq(commit.stats.total)
+ end
+ end
end
- end
- end
- context 'with pagination params' do
- let(:page) { 1 }
- let(:per_page) { 5 }
- let(:ref_name) { 'master' }
- let(:request) do
- get api("/projects/#{project_id}/repository/commits?page=#{page}&per_page=#{per_page}&ref_name=#{ref_name}", user)
- end
+ context 'with pagination params' do
+ let(:page) { 1 }
+ let(:per_page) { 5 }
+ let(:ref_name) { 'master' }
+ let(:request) do
+ get api("/projects/#{project_id}/repository/commits?page=#{page}&per_page=#{per_page}&ref_name=#{ref_name}", user)
+ end
- it 'returns correct headers' do
- request
+ it 'returns correct headers' do
+ request
- expect(response).to include_limited_pagination_headers
- expect(response.headers['Link']).to match(/page=1&per_page=5/)
- expect(response.headers['Link']).to match(/page=2&per_page=5/)
- end
+ expect(response).to include_limited_pagination_headers
+ expect(response.headers['Link']).to match(/page=1&per_page=5/)
+ expect(response.headers['Link']).to match(/page=2&per_page=5/)
+ end
- context 'viewing the first page' do
- it 'returns the first 5 commits' do
- request
+ context 'viewing the first page' do
+ it 'returns the first 5 commits' do
+ request
- commit = project.repository.commit
+ commit = project.repository.commit
- expect(json_response.size).to eq(per_page)
- expect(json_response.first['id']).to eq(commit.id)
- expect(response.headers['X-Page']).to eq('1')
- end
- end
+ expect(json_response.size).to eq(per_page)
+ expect(json_response.first['id']).to eq(commit.id)
+ expect(response.headers['X-Page']).to eq('1')
+ end
+ end
- context 'viewing the third page' do
- let(:page) { 3 }
+ context 'viewing the third page' do
+ let(:page) { 3 }
- it 'returns the third 5 commits' do
- request
+ it 'returns the third 5 commits' do
+ request
- commit = project.repository.commits('HEAD', limit: per_page, offset: (page - 1) * per_page).first
+ commit = project.repository.commits('HEAD', limit: per_page, offset: (page - 1) * per_page).first
- expect(json_response.size).to eq(per_page)
- expect(json_response.first['id']).to eq(commit.id)
- expect(response.headers['X-Page']).to eq('3')
- end
- end
+ expect(json_response.size).to eq(per_page)
+ expect(json_response.first['id']).to eq(commit.id)
+ expect(response.headers['X-Page']).to eq('3')
+ end
+ end
- context 'when pagination params are invalid' do
- let_it_be(:project) { create(:project, :repository) }
+ context 'when pagination params are invalid' do
+ let_it_be(:project) { create(:project, :repository) }
- using RSpec::Parameterized::TableSyntax
+ using RSpec::Parameterized::TableSyntax
- where(:page, :per_page, :error_message) do
- 0 | nil | 'page does not have a valid value'
- -1 | nil | 'page does not have a valid value'
- 'a' | nil | 'page is invalid'
- nil | 0 | 'per_page does not have a valid value'
- nil | -1 | 'per_page does not have a valid value'
- nil | 'a' | 'per_page is invalid'
- end
+ where(:page, :per_page, :error_message) do
+ 0 | nil | 'page does not have a valid value'
+ -1 | nil | 'page does not have a valid value'
+ 'a' | nil | 'page is invalid'
+ nil | 0 | 'per_page does not have a valid value'
+ nil | -1 | 'per_page does not have a valid value'
+ nil | 'a' | 'per_page is invalid'
+ end
- with_them do
- it 'returns 400 response' do
- request
+ with_them do
+ it 'returns 400 response' do
+ request
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['error']).to eq(error_message)
- end
- end
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq(error_message)
+ end
+ end
- context 'when FF is off' do
- before do
- stub_feature_flags(only_positive_pagination_values: false)
- end
+ context 'when FF is off' do
+ before do
+ stub_feature_flags(only_positive_pagination_values: false)
+ end
- where(:page, :per_page, :error_message, :status) do
- 0 | nil | nil | :success
- -10 | nil | nil | :internal_server_error
- 'a' | nil | 'page is invalid' | :bad_request
- nil | 0 | 'per_page has a value not allowed' | :bad_request
- nil | -1 | nil | :success
- nil | 'a' | 'per_page is invalid' | :bad_request
- end
+ where(:page, :per_page, :error_message, :status) do
+ 0 | nil | nil | :success
+ -10 | nil | nil | :internal_server_error
+ 'a' | nil | 'page is invalid' | :bad_request
+ nil | 0 | 'per_page has a value not allowed' | :bad_request
+ nil | -1 | nil | :success
+ nil | 'a' | 'per_page is invalid' | :bad_request
+ end
- with_them do
- it 'returns a response' do
- request
+ with_them do
+ it 'returns a response' do
+ request
- expect(response).to have_gitlab_http_status(status)
+ expect(response).to have_gitlab_http_status(status)
- if error_message
- expect(json_response['error']).to eq(error_message)
+ if error_message
+ expect(json_response['error']).to eq(error_message)
+ end
+ end
end
end
end
end
- end
- end
- context 'with order parameter' do
- let(:route) { "/projects/#{project_id}/repository/commits?ref_name=0031876&per_page=6&order=#{order}" }
+ context 'with order parameter' do
+ let(:route) { "/projects/#{project_id}/repository/commits?ref_name=0031876&per_page=6&order=#{order}" }
- context 'set to topo' do
- let(:order) { 'topo' }
+ context 'set to topo' do
+ let(:order) { 'topo' }
- # git log --graph -n 6 --pretty=format:"%h" --topo-order 0031876
- # * 0031876
- # |\
- # | * 48ca272
- # | * 335bc94
- # * | bf6e164
- # * | 9d526f8
- # |/
- # * 1039376
- it 'returns project commits ordered by topo order' do
- commits = project.repository.commits("0031876", limit: 6, order: 'topo')
+ # git log --graph -n 6 --pretty=format:"%h" --topo-order 0031876
+ # * 0031876
+ # |\
+ # | * 48ca272
+ # | * 335bc94
+ # * | bf6e164
+ # * | 9d526f8
+ # |/
+ # * 1039376
+ it 'returns project commits ordered by topo order' do
+ commits = project.repository.commits("0031876", limit: 6, order: 'topo')
- get api(route, current_user)
+ get api(route, current_user)
- expect(json_response.size).to eq(6)
- expect(json_response.map { |entry| entry["id"] }).to eq(commits.map(&:id))
- end
- end
+ expect(json_response.size).to eq(6)
+ expect(json_response.map { |entry| entry["id"] }).to eq(commits.map(&:id))
+ end
+ end
+
+ context 'set to default' do
+ let(:order) { 'default' }
+
+ # git log --graph -n 6 --pretty=format:"%h" --date-order 0031876
+ # * 0031876
+ # |\
+ # * | bf6e164
+ # | * 48ca272
+ # * | 9d526f8
+ # | * 335bc94
+ # |/
+ # * 1039376
+ it 'returns project commits ordered by default order' do
+ commits = project.repository.commits("0031876", limit: 6, order: 'default')
+
+ get api(route, current_user)
+
+ expect(json_response.size).to eq(6)
+ expect(json_response.map { |entry| entry["id"] }).to eq(commits.map(&:id))
+ end
+ end
- context 'set to default' do
- let(:order) { 'default' }
+ context 'set to an invalid parameter' do
+ let(:order) { 'invalid' }
- # git log --graph -n 6 --pretty=format:"%h" --date-order 0031876
- # * 0031876
- # |\
- # * | bf6e164
- # | * 48ca272
- # * | 9d526f8
- # | * 335bc94
- # |/
- # * 1039376
- it 'returns project commits ordered by default order' do
- commits = project.repository.commits("0031876", limit: 6, order: 'default')
+ it_behaves_like '400 response' do
+ let(:request) { get api(route, current_user) }
+ end
+ end
+ end
- get api(route, current_user)
+ context 'with the optional trailers parameter' do
+ it 'includes the Git trailers' do
+ get api("/projects/#{project_id}/repository/commits?ref_name=6d394385cf567f80a8fd85055db1ab4c5295806f&trailers=true", current_user)
- expect(json_response.size).to eq(6)
- expect(json_response.map { |entry| entry["id"] }).to eq(commits.map(&:id))
+ commit = json_response[0]
+
+ expect(commit['trailers']).to eq(
+ 'Signed-off-by' => 'Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>'
+ )
+ end
end
end
+ end
- context 'set to an invalid parameter' do
- let(:order) { 'invalid' }
+ context 'when user is an inherited member from the group' do
+ context 'when project is public with private repository' do
+ let(:project) { create(:project, :public, :repository, :repository_private, group: group) }
- it_behaves_like '400 response' do
- let(:request) { get api(route, current_user) }
+ context 'and user is a guest' do
+ let(:current_user) { inherited_guest }
+
+ it_behaves_like 'project commits'
end
end
- end
- context 'with the optional trailers parameter' do
- it 'includes the Git trailers' do
- get api("/projects/#{project_id}/repository/commits?ref_name=6d394385cf567f80a8fd85055db1ab4c5295806f&trailers=true", current_user)
+ context 'when project is private' do
+ let(:project) { create(:project, :private, :repository, group: group) }
- commit = json_response[0]
+ context 'and user is a guest' do
+ let(:current_user) { inherited_guest }
- expect(commit['trailers']).to eq(
- 'Signed-off-by' => 'Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>'
- )
+ it_behaves_like '404 response' do
+ let(:request) { get api(route) }
+ let(:message) { '404 Project Not Found' }
+ end
+ end
end
end
end
@@ -382,6 +414,9 @@ RSpec.describe API::Commits do
end
describe 'create' do
+ let_it_be(:sequencer) { FactoryBot::Sequence.new(:new_file_path) { |n| "files/test/#{n}.rb" } }
+
+ let(:new_file_path) { sequencer.next }
let(:message) { 'Created a new file with a very very looooooooooooooooooooooooooooooooooooooooooooooong commit message' }
let(:invalid_c_params) do
{
@@ -404,7 +439,7 @@ RSpec.describe API::Commits do
actions: [
{
action: 'create',
- file_path: 'foo/bar/baz.txt',
+ file_path: new_file_path,
content: 'puts 8'
}
]
@@ -418,7 +453,7 @@ RSpec.describe API::Commits do
actions: [
{
action: 'create',
- file_path: 'foo/bar/baz.txt',
+ file_path: new_file_path,
content: 'puts 🦊'
}
]
@@ -466,11 +501,57 @@ RSpec.describe API::Commits do
end
context 'a new file in project repo' do
- before do
- post api(url, user), params: valid_c_params
+ context 'when user is a direct project member' do
+ before do
+ post api(url, user), params: valid_c_params
+ end
+
+ it_behaves_like 'successfully creates the commit'
end
- it_behaves_like "successfully creates the commit"
+ context 'when user is an inherited member from the group' do
+ context 'when project is public with private repository' do
+ let(:project) { create(:project, :public, :repository, :repository_private, group: group) }
+
+ context 'and user is a guest' do
+ it_behaves_like '403 response' do
+ let(:request) { post api(url, inherited_guest), params: valid_c_params }
+ let(:message) { '403 Forbidden' }
+ end
+ end
+ end
+
+ context 'when project is private' do
+ let(:project) { create(:project, :private, :repository, group: group) }
+
+ context 'and user is a guest' do
+ it_behaves_like '403 response' do
+ let(:request) { post api(url, inherited_guest), params: valid_c_params }
+ let(:message) { '403 Forbidden' }
+ end
+ end
+ end
+ end
+ end
+
+ context 'when repository is empty' do
+ let!(:project) { create(:project, :empty_repo) }
+
+ context 'when params are valid' do
+ before do
+ post api(url, user), params: valid_c_params
+ end
+
+ it_behaves_like "successfully creates the commit"
+ end
+
+ context 'when branch name is invalid' do
+ before do
+ post api(url, user), params: valid_c_params.merge(branch: 'wrong:name')
+ end
+
+ it { expect(response).to have_gitlab_http_status(:bad_request) }
+ end
end
context 'a new file with utf8 chars in project repo' do
@@ -882,6 +963,7 @@ RSpec.describe API::Commits do
end
describe 'multiple operations' do
+ let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
let(:message) { 'Multiple actions' }
let(:invalid_mo_params) do
{
@@ -951,17 +1033,11 @@ RSpec.describe API::Commits do
}
end
- it 'are committed as one in project repo' do
+ it 'is committed as one in project repo and includes stats' do
post api(url, user), params: valid_mo_params
expect(response).to have_gitlab_http_status(:created)
expect(json_response['title']).to eq(message)
- end
-
- it 'includes the commit stats' do
- post api(url, user), params: valid_mo_params
-
- expect(response).to have_gitlab_http_status(:created)
expect(json_response).to include 'stats'
end
@@ -1047,7 +1123,8 @@ RSpec.describe API::Commits do
end
describe 'GET /projects/:id/repository/commits/:sha/refs' do
- let(:project) { create(:project, :public, :repository) }
+ let_it_be(:project) { create(:project, :public, :repository) }
+
let(:tag) { project.repository.find_tag('v1.1.0') }
let(:commit_id) { tag.dereferenced_target.id }
let(:route) { "/projects/#{project_id}/repository/commits/#{commit_id}/refs" }
@@ -1062,6 +1139,8 @@ RSpec.describe API::Commits do
end
context 'when repository is disabled' do
+ let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
+
include_context 'disabled repository'
it_behaves_like '404 response' do
@@ -1151,6 +1230,8 @@ RSpec.describe API::Commits do
end
context 'when repository is disabled' do
+ let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
+
include_context 'disabled repository'
it_behaves_like '404 response' do
@@ -1192,8 +1273,14 @@ RSpec.describe API::Commits do
end
shared_examples_for 'ref with unaccessible pipeline' do
- let!(:pipeline) do
- create(:ci_empty_pipeline, project: project, status: :created, source: :push, ref: 'master', sha: commit.sha, protected: false)
+ let(:pipeline) do
+ create(:ci_empty_pipeline,
+ project: project,
+ status: :created,
+ source: :push,
+ ref: 'master',
+ sha: commit.sha,
+ protected: false)
end
it 'does not include last_pipeline' do
@@ -1231,7 +1318,7 @@ RSpec.describe API::Commits do
end
context 'when unauthenticated', 'and project is public' do
- let(:project) { create(:project, :public, :repository) }
+ let_it_be_with_reload(:project) { create(:project, :public, :repository) }
it_behaves_like 'ref commit'
it_behaves_like 'ref with pipeline'
@@ -1261,6 +1348,7 @@ RSpec.describe API::Commits do
context 'when builds are disabled' do
before do
project
+ .reload
.project_feature
.update!(builds_access_level: ProjectFeature::DISABLED)
end
@@ -1312,7 +1400,7 @@ RSpec.describe API::Commits do
context 'with private builds' do
before do
- project.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
+ project.reload.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
end
it_behaves_like 'ref with pipeline'
@@ -1338,8 +1426,8 @@ RSpec.describe API::Commits do
end
context 'when authenticated', 'as non_member and project is public' do
- let(:current_user) { create(:user) }
- let(:project) { create(:project, :public, :repository) }
+ let_it_be(:current_user) { create(:user) }
+ let_it_be_with_reload(:project) { create(:project, :public, :repository) }
it_behaves_like 'ref with pipeline'
@@ -1392,6 +1480,8 @@ RSpec.describe API::Commits do
end
context 'when repository is disabled' do
+ let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
+
include_context 'disabled repository'
it_behaves_like '404 response' do
@@ -1401,7 +1491,7 @@ RSpec.describe API::Commits do
end
context 'when unauthenticated', 'and project is public' do
- let(:project) { create(:project, :public, :repository) }
+ let_it_be(:project) { create(:project, :public, :repository) }
it_behaves_like 'ref diff'
end
@@ -1491,6 +1581,8 @@ RSpec.describe API::Commits do
end
context 'when repository is disabled' do
+ let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
+
include_context 'disabled repository'
it_behaves_like '404 response' do
@@ -1500,7 +1592,7 @@ RSpec.describe API::Commits do
end
context 'when unauthenticated', 'and project is public' do
- let(:project) { create(:project, :public, :repository) }
+ let_it_be(:project) { create(:project, :public, :repository) }
it_behaves_like 'ref comments'
end
@@ -1589,6 +1681,7 @@ RSpec.describe API::Commits do
end
describe 'POST :id/repository/commits/:sha/cherry_pick' do
+ let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
let(:commit) { project.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') }
let(:commit_id) { commit.id }
let(:branch) { 'master' }
@@ -1626,6 +1719,8 @@ RSpec.describe API::Commits do
end
context 'when repository is disabled' do
+ let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
+
include_context 'disabled repository'
it_behaves_like '404 response' do
@@ -1635,7 +1730,7 @@ RSpec.describe API::Commits do
end
context 'when unauthenticated', 'and project is public' do
- let(:project) { create(:project, :public, :repository) }
+ let_it_be(:project) { create(:project, :public, :repository) }
it_behaves_like '403 response' do
let(:request) { post api(route), params: { branch: 'master' } }
@@ -1774,6 +1869,7 @@ RSpec.describe API::Commits do
end
describe 'POST :id/repository/commits/:sha/revert' do
+ let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
let(:commit_id) { 'b83d6e391c22777fca1ed3012fce84f633d7fed0' }
let(:commit) { project.commit(commit_id) }
let(:branch) { 'master' }
@@ -1814,7 +1910,7 @@ RSpec.describe API::Commits do
end
context 'when unauthenticated', 'and project is public' do
- let(:project) { create(:project, :public, :repository) }
+ let_it_be(:project) { create(:project, :public, :repository) }
it_behaves_like '403 response' do
let(:request) { post api(route), params: { branch: branch } }
@@ -1921,6 +2017,7 @@ RSpec.describe API::Commits do
end
describe 'POST /projects/:id/repository/commits/:sha/comments' do
+ let(:project) { create(:project, :repository, :private) }
let(:commit) { project.repository.commit }
let(:commit_id) { commit.id }
let(:note) { 'My comment' }
@@ -1941,6 +2038,8 @@ RSpec.describe API::Commits do
end
context 'when repository is disabled' do
+ let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
+
include_context 'disabled repository'
it_behaves_like '404 response' do
@@ -1950,7 +2049,7 @@ RSpec.describe API::Commits do
end
context 'when unauthenticated', 'and project is public' do
- let(:project) { create(:project, :public, :repository) }
+ let_it_be(:project) { create(:project, :public, :repository) }
it_behaves_like '400 response' do
let(:request) { post api(route), params: { note: 'My comment' } }
@@ -1970,12 +2069,13 @@ RSpec.describe API::Commits do
it_behaves_like 'ref new comment'
it 'returns the inline comment' do
- post api(route, current_user), params: { note: 'My comment', path: project.repository.commit.raw_diffs.first.new_path, line: 1, line_type: 'new' }
+ path = project.repository.commit.raw_diffs.first.new_path
+ post api(route, current_user), params: { note: 'My comment', path: path, line: 1, line_type: 'new' }
expect(response).to have_gitlab_http_status(:created)
expect(response).to match_response_schema('public_api/v4/commit_note')
expect(json_response['note']).to eq('My comment')
- expect(json_response['path']).to eq(project.repository.commit.raw_diffs.first.new_path)
+ expect(json_response['path']).to eq(path)
expect(json_response['line']).to eq(1)
expect(json_response['line_type']).to eq('new')
end
@@ -2050,7 +2150,8 @@ RSpec.describe API::Commits do
end
describe 'GET /projects/:id/repository/commits/:sha/merge_requests' do
- let(:project) { create(:project, :repository, :private) }
+ let_it_be(:project) { create(:project, :repository, :private) }
+
let(:merged_mr) { create(:merge_request, source_project: project, source_branch: 'master', target_branch: 'feature') }
let(:commit) { merged_mr.merge_request_diff.commits.last }
@@ -2082,7 +2183,8 @@ RSpec.describe API::Commits do
end
context 'public project' do
- let(:project) { create(:project, :repository, :public, :merge_requests_private) }
+ let_it_be(:project) { create(:project, :repository, :public, :merge_requests_private) }
+
let(:non_member) { create(:user) }
it 'responds 403 when only members are allowed to read merge requests' do
diff --git a/spec/requests/api/conan_instance_packages_spec.rb b/spec/requests/api/conan_instance_packages_spec.rb
index e4747e0eb99..b343e0cfc97 100644
--- a/spec/requests/api/conan_instance_packages_spec.rb
+++ b/spec/requests/api/conan_instance_packages_spec.rb
@@ -103,8 +103,7 @@ RSpec.describe API::ConanInstancePackages do
context 'file download endpoints' do
include_context 'conan file download endpoints'
- describe 'GET /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/
-:recipe_revision/export/:file_name' do
+ describe 'GET /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/export/:file_name' do
subject do
get api("/packages/conan/v1/files/#{recipe_path}/#{metadata.recipe_revision}/export/#{recipe_file.file_name}"),
headers: headers
@@ -114,8 +113,7 @@ RSpec.describe API::ConanInstancePackages do
it_behaves_like 'project not found by recipe'
end
- describe 'GET /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/
-:recipe_revision/package/:conan_package_reference/:package_revision/:file_name' do
+ describe 'GET /api/v4/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("/packages/conan/v1/files/#{recipe_path}/#{metadata.recipe_revision}/package/#{metadata.conan_package_reference}/#{metadata.package_revision}/#{package_file.file_name}"),
headers: headers
diff --git a/spec/requests/api/conan_project_packages_spec.rb b/spec/requests/api/conan_project_packages_spec.rb
index 48e36b55a68..4e6af9942ef 100644
--- a/spec/requests/api/conan_project_packages_spec.rb
+++ b/spec/requests/api/conan_project_packages_spec.rb
@@ -102,8 +102,7 @@ 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'
- describe 'GET /api/v4/projects/:id/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/
-:recipe_revision/export/:file_name' do
+ 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
@@ -113,8 +112,7 @@ RSpec.describe API::ConanProjectPackages do
it_behaves_like 'project not found by project id'
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
+ 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
diff --git a/spec/requests/api/debian_group_packages_spec.rb b/spec/requests/api/debian_group_packages_spec.rb
index d881d4350fb..9dbb75becf8 100644
--- a/spec/requests/api/debian_group_packages_spec.rb
+++ b/spec/requests/api/debian_group_packages_spec.rb
@@ -36,12 +36,42 @@ RSpec.describe API::DebianGroupPackages do
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete Packages file/
end
+ describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/binary-:architecture/by-hash/SHA256/:file_sha256' do
+ let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/binary-#{architecture.name}/by-hash/SHA256/#{component_file_older_sha256.file_sha256}" }
+
+ it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
+ end
+
+ describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/source/Sources' do
+ let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/source/Sources" }
+
+ it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete Sources file/
+ end
+
+ describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/source/by-hash/SHA256/:file_sha256' do
+ let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/source/by-hash/SHA256/#{component_file_sources_older_sha256.file_sha256}" }
+
+ it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
+ end
+
+ describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/Packages' do
+ let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/Packages" }
+
+ it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete D-I Packages file/
+ end
+
+ describe 'GET groups/:id/-/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/by-hash/SHA256/:file_sha256' do
+ let(:url) { "/groups/#{container.id}/-/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/by-hash/SHA256/#{component_file_di_older_sha256.file_sha256}" }
+
+ it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
+ end
+
describe 'GET groups/:id/-/packages/debian/pool/:codename/:project_id/:letter/:package_name/:package_version/:file_name' do
+ using RSpec::Parameterized::TableSyntax
+
let(:url) { "/groups/#{container.id}/-/packages/debian/pool/#{package.debian_distribution.codename}/#{project.id}/#{letter}/#{package.name}/#{package.version}/#{file_name}" }
let(:file_name) { params[:file_name] }
- using RSpec::Parameterized::TableSyntax
-
where(:file_name, :success_body) do
'sample_1.2.3~alpha2.tar.xz' | /^.7zXZ/
'sample_1.2.3~alpha2.dsc' | /^Format: 3.0 \(native\)/
@@ -53,6 +83,12 @@ RSpec.describe API::DebianGroupPackages do
with_them do
it_behaves_like 'Debian packages read endpoint', 'GET', :success, params[:success_body]
+
+ context 'for bumping last downloaded at' do
+ include_context 'Debian repository access', :public, :developer, :basic do
+ it_behaves_like 'bumping the package last downloaded at field'
+ end
+ end
end
end
end
diff --git a/spec/requests/api/debian_project_packages_spec.rb b/spec/requests/api/debian_project_packages_spec.rb
index bd68bf912e1..6bef669cb3a 100644
--- a/spec/requests/api/debian_project_packages_spec.rb
+++ b/spec/requests/api/debian_project_packages_spec.rb
@@ -36,12 +36,42 @@ RSpec.describe API::DebianProjectPackages do
it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete Packages file/
end
+ describe 'GET projects/:id/packages/debian/dists/*distribution/:component/binary-:architecture/by-hash/SHA256/:file_sha256' do
+ let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/binary-#{architecture.name}/by-hash/SHA256/#{component_file_older_sha256.file_sha256}" }
+
+ it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
+ end
+
+ describe 'GET projects/:id/packages/debian/dists/*distribution/source/Sources' do
+ let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/source/Sources" }
+
+ it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete Sources file/
+ end
+
+ describe 'GET projects/:id/packages/debian/dists/*distribution/source/by-hash/SHA256/:file_sha256' do
+ let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/source/by-hash/SHA256/#{component_file_sources_older_sha256.file_sha256}" }
+
+ it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
+ end
+
+ describe 'GET projects/:id/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/Packages' do
+ let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/Packages" }
+
+ it_behaves_like 'Debian packages read endpoint', 'GET', :success, /Description: This is an incomplete D-I Packages file/
+ end
+
+ describe 'GET projects/:id/packages/debian/dists/*distribution/:component/debian-installer/binary-:architecture/by-hash/SHA256/:file_sha256' do
+ let(:url) { "/projects/#{container.id}/packages/debian/dists/#{distribution.codename}/#{component.name}/debian-installer/binary-#{architecture.name}/by-hash/SHA256/#{component_file_di_older_sha256.file_sha256}" }
+
+ it_behaves_like 'Debian packages read endpoint', 'GET', :success, /^Other SHA256$/
+ end
+
describe 'GET projects/:id/packages/debian/pool/:codename/:letter/:package_name/:package_version/:file_name' do
+ using RSpec::Parameterized::TableSyntax
+
let(:url) { "/projects/#{container.id}/packages/debian/pool/#{package.debian_distribution.codename}/#{letter}/#{package.name}/#{package.version}/#{file_name}" }
let(:file_name) { params[:file_name] }
- using RSpec::Parameterized::TableSyntax
-
where(:file_name, :success_body) do
'sample_1.2.3~alpha2.tar.xz' | /^.7zXZ/
'sample_1.2.3~alpha2.dsc' | /^Format: 3.0 \(native\)/
@@ -53,6 +83,12 @@ RSpec.describe API::DebianProjectPackages do
with_them do
it_behaves_like 'Debian packages read endpoint', 'GET', :success, params[:success_body]
+
+ context 'for bumping last downloaded at' do
+ include_context 'Debian repository access', :public, :developer, :basic do
+ it_behaves_like 'bumping the package last downloaded at field'
+ end
+ end
end
end
diff --git a/spec/requests/api/deployments_spec.rb b/spec/requests/api/deployments_spec.rb
index 24c3ee59c18..24e0e5d3180 100644
--- a/spec/requests/api/deployments_spec.rb
+++ b/spec/requests/api/deployments_spec.rb
@@ -14,9 +14,10 @@ RSpec.describe API::Deployments do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:production) { create(:environment, :production, project: project) }
let_it_be(:staging) { create(:environment, :staging, project: project) }
- let_it_be(:deployment_1) { create(:deployment, :success, project: project, environment: production, ref: 'master', created_at: Time.now, updated_at: Time.now) }
- let_it_be(:deployment_2) { create(:deployment, :success, project: project, environment: staging, ref: 'master', created_at: 1.day.ago, updated_at: 2.hours.ago) }
- let_it_be(:deployment_3) { create(:deployment, :success, project: project, environment: staging, ref: 'master', created_at: 2.days.ago, updated_at: 1.hour.ago) }
+ 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) }
def perform_request(params = {})
get api("/projects/#{project.id}/deployments", user), params: params
@@ -104,7 +105,7 @@ RSpec.describe API::Deployments do
control_count = ActiveRecord::QueryRecorder.new { perform_request }.count
- create(:deployment, :success, project: project, iid: 21, ref: 'master')
+ create(:deployment, :success, project: project, deployable: build, iid: 21, ref: 'master')
expect { perform_request }.not_to exceed_query_limit(control_count)
end
diff --git a/spec/requests/api/feature_flags_spec.rb b/spec/requests/api/feature_flags_spec.rb
index a1aedc1d6b2..bf7eec167f5 100644
--- a/spec/requests/api/feature_flags_spec.rb
+++ b/spec/requests/api/feature_flags_spec.rb
@@ -365,8 +365,8 @@ RSpec.describe API::FeatureFlags do
describe 'PUT /projects/:id/feature_flags/:name' do
context 'with a version 2 feature flag' do
let!(:feature_flag) do
- create(:operations_feature_flag, :new_version_flag, project: project, active: true,
- name: 'feature1', description: 'old description')
+ create(:operations_feature_flag, :new_version_flag,
+ project: project, active: true, name: 'feature1', description: 'old description')
end
it 'returns a 404 if the feature flag does not exist' do
@@ -591,8 +591,8 @@ RSpec.describe API::FeatureFlags do
it 'deletes a feature flag strategy' do
strategy_a = create(:operations_strategy, feature_flag: feature_flag, name: 'default', parameters: {})
- strategy_b = create(:operations_strategy, feature_flag: feature_flag,
- name: 'userWithId', parameters: { userIds: 'userA,userB' })
+ strategy_b = create(:operations_strategy,
+ feature_flag: feature_flag, name: 'userWithId', parameters: { userIds: 'userA,userB' })
params = {
strategies: [{
id: strategy_a.id,
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index 06d22e7e218..e95a626b4aa 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -5,13 +5,21 @@ require 'spec_helper'
RSpec.describe API::Files do
include RepoHelpers
- let(:user) { create(:user) }
+ let_it_be(:group) { create(:group, :public) }
+ let_it_be_with_refind(:user) { create(:user) }
+ let_it_be(:inherited_guest) { create(:user) }
+ 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(:file_path) { "files%2Fruby%2Fpopen%2Erb" }
- let(:executable_file_path) { "files%2Fexecutables%2Fls" }
- let(:rouge_file_path) { "%2e%2e%2f" }
- let(:absolute_path) { "%2Fetc%2Fpasswd.rb" }
+ let(:file_path) { 'files%2Fruby%2Fpopen%2Erb' }
+ let(:file_name) { 'popen.rb' }
+ let(:last_commit_id) { '570e7b2abdd848b95f2f578043fc23bd6f6fd24d' }
+ let(:content_sha256) { 'c440cd09bae50c4632cc58638ad33c6aa375b6109d811e76a9cc3a613c1e8887' }
+ let(:executable_file_path) { 'files%2Fexecutables%2Fls' }
+ let(:invalid_file_path) { '%2e%2e%2f' }
+ let(:absolute_path) { '%2Fetc%2Fpasswd.rb' }
let(:invalid_file_message) { 'file_path should be a valid file path' }
let(:params) do
{
@@ -46,6 +54,12 @@ RSpec.describe API::Files do
fake_class.new
end
+ before_all do
+ group.add_guest(inherited_guest)
+ group.add_reporter(inherited_reporter)
+ group.add_developer(inherited_developer)
+ end
+
before do
project.add_developer(user)
end
@@ -70,8 +84,10 @@ RSpec.describe API::Files do
expect(helper.headers).to eq({ 'X-Gitlab-Test' => '1' })
end
- it 'raises exception if value is an Enumerable' do
- expect { helper.set_http_headers(test: [1]) }.to raise_error(ArgumentError)
+ context 'when value is an Enumerable' do
+ it 'raises an exception' do
+ expect { helper.set_http_headers(test: [1]) }.to raise_error(ArgumentError)
+ end
end
end
@@ -87,12 +103,12 @@ RSpec.describe API::Files do
end
end
- describe "HEAD /projects/:id/repository/files/:file_path" do
+ describe 'HEAD /projects/:id/repository/files/:file_path' do
shared_examples_for 'repository files' do
let(:options) { {} }
it 'returns 400 when file path is invalid' do
- head api(route(rouge_file_path), current_user, **options), params: params
+ head api(route(invalid_file_path), current_user, **options), params: params
expect(response).to have_gitlab_http_status(:bad_request)
end
@@ -106,16 +122,16 @@ RSpec.describe API::Files do
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers['X-Gitlab-File-Path']).to eq(CGI.unescape(file_path))
- expect(response.headers['X-Gitlab-File-Name']).to eq('popen.rb')
- expect(response.headers['X-Gitlab-Last-Commit-Id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d')
- expect(response.headers['X-Gitlab-Content-Sha256']).to eq('c440cd09bae50c4632cc58638ad33c6aa375b6109d811e76a9cc3a613c1e8887')
+ expect(response.headers['X-Gitlab-File-Name']).to eq(file_name)
+ expect(response.headers['X-Gitlab-Last-Commit-Id']).to eq(last_commit_id)
+ expect(response.headers['X-Gitlab-Content-Sha256']).to eq(content_sha256)
end
it 'caches sha256 of the content', :use_clean_rails_redis_caching do
head api(route(file_path), current_user, **options), params: params
expect(Rails.cache.fetch("blob_content_sha256:#{project.full_path}:#{response.headers['X-Gitlab-Blob-Id']}"))
- .to eq('c440cd09bae50c4632cc58638ad33c6aa375b6109d811e76a9cc3a613c1e8887')
+ .to eq(content_sha256)
expect_next_instance_of(Gitlab::Git::Blob) do |instance|
expect(instance).not_to receive(:load_all_data!)
@@ -126,8 +142,8 @@ RSpec.describe API::Files do
it 'returns file by commit sha' do
# This file is deleted on HEAD
- file_path = "files%2Fjs%2Fcommit%2Ejs%2Ecoffee"
- params[:ref] = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9"
+ file_path = 'files%2Fjs%2Fcommit%2Ejs%2Ecoffee'
+ params[:ref] = '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9'
head api(route(file_path), current_user, **options), params: params
@@ -137,15 +153,15 @@ RSpec.describe API::Files do
end
context 'when mandatory params are not given' do
- it "responds with a 400 status" do
- head api(route("any%2Ffile"), current_user, **options)
+ it 'responds with a 400 status' do
+ head api(route('any%2Ffile'), current_user, **options)
expect(response).to have_gitlab_http_status(:bad_request)
end
end
context 'when file_path does not exist' do
- it "responds with a 404 status" do
+ it 'responds with a 404 status' do
params[:ref] = 'master'
head api(route('app%2Fmodels%2Fapplication%2Erb'), current_user, **options), params: params
@@ -157,7 +173,7 @@ RSpec.describe API::Files do
context 'when file_path does not exist' do
include_context 'disabled repository'
- it "responds with a 403 status" do
+ it 'responds with a 403 status' do
head api(route(file_path), current_user, **options), params: params
expect(response).to have_gitlab_http_status(:forbidden)
@@ -165,20 +181,22 @@ RSpec.describe API::Files do
end
end
- context 'when unauthenticated', 'and project is public' do
- it_behaves_like 'repository files' do
- let(:project) { create(:project, :public, :repository) }
- let(:current_user) { nil }
+ context 'when unauthenticated' do
+ context 'and project is public' do
+ it_behaves_like 'repository files' do
+ let(:project) { create(:project, :public, :repository) }
+ let(:current_user) { nil }
+ end
end
- end
- context 'when unauthenticated', 'and project is private' do
- it "responds with a 404 status" do
- current_user = nil
+ context 'and project is private' do
+ it 'responds with a 404 status' do
+ current_user = nil
- head api(route(file_path), current_user), params: params
+ head api(route(file_path), current_user), params: params
- expect(response).to have_gitlab_http_status(:not_found)
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
end
end
@@ -190,25 +208,41 @@ RSpec.describe API::Files do
end
end
- context 'when authenticated', 'as a developer' do
- it_behaves_like 'repository files' do
- let(:current_user) { user }
+ context 'when authenticated' do
+ context 'and user is a developer' do
+ it_behaves_like 'repository files' do
+ let(:current_user) { user }
+ end
end
- end
- context 'when authenticated', 'as a guest' do
- it_behaves_like '403 response' do
- let(:request) { head api(route(file_path), guest), params: params }
+ context 'and user is a guest' do
+ it_behaves_like '403 response' do
+ let(:request) { head api(route(file_path), guest), params: params }
+ end
end
end
end
- describe "GET /projects/:id/repository/files/:file_path" do
- shared_examples_for 'repository files' do
- let(:options) { {} }
+ describe 'GET /projects/:id/repository/files/:file_path' do
+ let(:options) { {} }
+
+ shared_examples 'returns non-executable file attributes as json' do
+ specify do
+ get api(route(file_path), api_user, **options), params: params
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['file_path']).to eq(CGI.unescape(file_path))
+ expect(json_response['file_name']).to eq(file_name)
+ expect(json_response['last_commit_id']).to eq(last_commit_id)
+ expect(json_response['content_sha256']).to eq(content_sha256)
+ expect(json_response['execute_filemode']).to eq(false)
+ expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n")
+ end
+ end
+ shared_examples_for 'repository files' do
it 'returns 400 for invalid file path' do
- get api(route(rouge_file_path), api_user, **options), params: params
+ get api(route(invalid_file_path), api_user, **options), params: params
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq(invalid_file_message)
@@ -218,17 +252,7 @@ RSpec.describe API::Files do
subject { get api(route(absolute_path), api_user, **options), params: params }
end
- it 'returns file attributes as json' do
- get api(route(file_path), api_user, **options), params: params
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['file_path']).to eq(CGI.unescape(file_path))
- expect(json_response['file_name']).to eq('popen.rb')
- expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d')
- expect(json_response['content_sha256']).to eq('c440cd09bae50c4632cc58638ad33c6aa375b6109d811e76a9cc3a613c1e8887')
- expect(json_response['execute_filemode']).to eq(false)
- expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n")
- end
+ it_behaves_like 'returns non-executable file attributes as json'
context 'for executable file' do
it 'returns file attributes as json' do
@@ -247,7 +271,7 @@ RSpec.describe API::Files do
end
it 'returns json when file has txt extension' do
- file_path = "bar%2Fbranch-test.txt"
+ file_path = 'bar%2Fbranch-test.txt'
get api(route(file_path), api_user, **options), params: params
@@ -277,8 +301,8 @@ RSpec.describe API::Files do
it 'returns file by commit sha' do
# This file is deleted on HEAD
- file_path = "files%2Fjs%2Fcommit%2Ejs%2Ecoffee"
- params[:ref] = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9"
+ file_path = 'files%2Fjs%2Fcommit%2Ejs%2Ecoffee'
+ params[:ref] = '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9'
get api(route(file_path), api_user, **options), params: params
@@ -289,9 +313,9 @@ RSpec.describe API::Files do
end
it 'returns raw file info' do
- url = route(file_path) + "/raw"
+ url = route(file_path) + '/raw'
expect_to_send_git_blob(api(url, api_user, **options), params)
- expect(headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
+ expect(headers[Gitlab::Workhorse::DETECT_HEADER]).to eq 'true'
end
it 'returns blame file info' do
@@ -303,16 +327,16 @@ RSpec.describe API::Files do
end
it 'sets inline content disposition by default' do
- url = route(file_path) + "/raw"
+ url = route(file_path) + '/raw'
get api(url, api_user, **options), params: params
- expect(headers['Content-Disposition']).to eq(%q(inline; filename="popen.rb"; filename*=UTF-8''popen.rb))
+ expect(headers['Content-Disposition']).to eq(%(inline; filename="#{file_name}"; filename*=UTF-8''#{file_name}))
end
context 'when mandatory params are not given' do
it_behaves_like '400 response' do
- let(:request) { get api(route("any%2Ffile"), current_user, **options) }
+ let(:request) { get api(route('any%2Ffile'), current_user, **options) }
end
end
@@ -334,40 +358,96 @@ RSpec.describe API::Files do
end
end
- context 'when unauthenticated', 'and project is public' do
- it_behaves_like 'repository files' do
- let(:project) { create(:project, :public, :repository) }
- let(:current_user) { nil }
- let(:api_user) { nil }
+ context 'when unauthenticated' do
+ context 'and project is public' do
+ it_behaves_like 'repository files' do
+ let(:project) { create(:project, :public, :repository) }
+ let(:current_user) { nil }
+ let(:api_user) { nil }
+ end
end
- end
- context 'when PATs are used' do
- it_behaves_like 'repository files' do
- let(:token) { create(:personal_access_token, scopes: ['read_repository'], user: user) }
- let(:current_user) { user }
- let(:api_user) { nil }
- let(:options) { { personal_access_token: token } }
+ context 'and project is private' do
+ it_behaves_like '404 response' do
+ let(:request) { get api(route(file_path)), params: params }
+ let(:message) { '404 Project Not Found' }
+ end
end
end
- context 'when unauthenticated', 'and project is private' do
- it_behaves_like '404 response' do
- let(:request) { get api(route(file_path)), params: params }
- let(:message) { '404 Project Not Found' }
- end
- end
+ context 'when authenticated' do
+ context 'and user is a direct project member' do
+ context 'and project is private' do
+ context 'and user is a developer' do
+ it_behaves_like 'repository files' do
+ let(:current_user) { user }
+ let(:api_user) { user }
+ end
+
+ context 'and PATs are used' do
+ it_behaves_like 'repository files' do
+ let(:token) { create(:personal_access_token, scopes: ['read_repository'], user: user) }
+ let(:current_user) { user }
+ let(:api_user) { nil }
+ let(:options) { { personal_access_token: token } }
+ end
+ end
+ end
- context 'when authenticated', 'as a developer' do
- it_behaves_like 'repository files' do
- let(:current_user) { user }
- let(:api_user) { user }
+ context 'and user is a guest' do
+ it_behaves_like '403 response' do
+ let(:request) { get api(route(file_path), guest), params: params }
+ end
+ end
+ end
end
end
- context 'when authenticated', 'as a guest' do
- it_behaves_like '403 response' do
- let(:request) { get api(route(file_path), guest), params: params }
+ 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) }
+
+ context 'and user is a guest' do
+ it_behaves_like 'returns non-executable file attributes as json' do
+ let(:api_user) { inherited_guest }
+ end
+ end
+
+ context 'and user is a reporter' do
+ it_behaves_like 'returns non-executable file attributes as json' do
+ let(:api_user) { inherited_reporter }
+ end
+ end
+
+ context 'and user is a developer' do
+ it_behaves_like 'returns non-executable file attributes as json' do
+ let(:api_user) { inherited_developer }
+ end
+ end
+ end
+
+ context 'when project is private' do
+ let_it_be(:project) { create(:project, :private, :repository, group: group) }
+
+ context 'and user is a guest' do
+ it_behaves_like '403 response' do
+ let(:request) { get api(route(file_path), inherited_guest), params: params }
+ end
+ end
+
+ context 'and user is a reporter' do
+ it_behaves_like 'returns non-executable file attributes as json' do
+ let(:api_user) { inherited_reporter }
+ end
+ end
+
+ context 'and user is a developer' do
+ it_behaves_like 'returns non-executable file attributes as json' do
+ let(:api_user) { inherited_developer }
+ end
+ end
+ end
end
end
end
@@ -406,11 +486,10 @@ RSpec.describe API::Files do
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers['X-Gitlab-File-Path']).to eq(CGI.unescape(file_path))
- expect(response.headers['X-Gitlab-File-Name']).to eq('popen.rb')
- expect(response.headers['X-Gitlab-Last-Commit-Id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d')
- expect(response.headers['X-Gitlab-Content-Sha256'])
- .to eq('c440cd09bae50c4632cc58638ad33c6aa375b6109d811e76a9cc3a613c1e8887')
- expect(response.headers['X-Gitlab-Execute-Filemode']).to eq("false")
+ expect(response.headers['X-Gitlab-File-Name']).to eq(file_name)
+ expect(response.headers['X-Gitlab-Last-Commit-Id']).to eq(last_commit_id)
+ expect(response.headers['X-Gitlab-Content-Sha256']).to eq(content_sha256)
+ expect(response.headers['X-Gitlab-Execute-Filemode']).to eq('false')
end
context 'for executable file' do
@@ -424,13 +503,13 @@ RSpec.describe API::Files do
expect(response.headers['X-Gitlab-Last-Commit-Id']).to eq('6b8dc4a827797aa025ff6b8f425e583858a10d4f')
expect(response.headers['X-Gitlab-Content-Sha256'])
.to eq('2c74b1181ef780dfb692c030d3a0df6e0b624135c38a9344e56b9f80007b6191')
- expect(response.headers['X-Gitlab-Execute-Filemode']).to eq("true")
+ expect(response.headers['X-Gitlab-Execute-Filemode']).to eq('true')
end
end
end
it 'returns 400 when file path is invalid' do
- get api(route(rouge_file_path) + '/blame', current_user), params: params
+ get api(route(invalid_file_path) + '/blame', current_user), params: params
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq(invalid_file_message)
@@ -573,29 +652,33 @@ RSpec.describe API::Files do
end
end
- context 'when unauthenticated', 'and project is public' do
- it_behaves_like 'repository blame files' do
- let(:project) { create(:project, :public, :repository) }
- let(:current_user) { nil }
+ context 'when unauthenticated' do
+ context 'and project is public' do
+ it_behaves_like 'repository blame files' do
+ let(:project) { create(:project, :public, :repository) }
+ let(:current_user) { nil }
+ end
end
- end
- context 'when unauthenticated', 'and project is private' do
- it_behaves_like '404 response' do
- let(:request) { get api(route(file_path)), params: params }
- let(:message) { '404 Project Not Found' }
+ context 'and project is private' do
+ it_behaves_like '404 response' do
+ let(:request) { get api(route(file_path)), params: params }
+ let(:message) { '404 Project Not Found' }
+ end
end
end
- context 'when authenticated', 'as a developer' do
- it_behaves_like 'repository blame files' do
- let(:current_user) { user }
+ context 'when authenticated' do
+ context 'and user is a developer' do
+ it_behaves_like 'repository blame files' do
+ let(:current_user) { user }
+ end
end
- end
- context 'when authenticated', 'as a guest' do
- it_behaves_like '403 response' do
- let(:request) { get api(route(file_path) + '/blame', guest), params: params }
+ context 'and user is a guest' do
+ it_behaves_like '403 response' do
+ let(:request) { get api(route(file_path) + '/blame', guest), params: params }
+ end
end
end
@@ -614,10 +697,10 @@ RSpec.describe API::Files do
end
end
- describe "GET /projects/:id/repository/files/:file_path/raw" do
+ describe 'GET /projects/:id/repository/files/:file_path/raw' do
shared_examples_for 'repository raw files' do
it 'returns 400 when file path is invalid' do
- get api(route(rouge_file_path) + "/raw", current_user), params: params
+ get api(route(invalid_file_path) + '/raw', current_user), params: params
expect(response).to have_gitlab_http_status(:bad_request)
expect(json_response['error']).to eq(invalid_file_message)
@@ -628,7 +711,7 @@ RSpec.describe API::Files do
end
it 'returns raw file info' do
- url = route(file_path) + "/raw"
+ url = route(file_path) + '/raw'
expect_to_send_git_blob(api(url, current_user), params)
end
@@ -639,39 +722,39 @@ RSpec.describe API::Files do
end
it 'returns response :ok', :aggregate_failures do
- url = route(file_path) + "/raw"
+ url = route(file_path) + '/raw'
expect_to_send_git_blob(api(url, current_user), {})
end
end
it 'returns raw file info for files with dots' do
- url = route('.gitignore') + "/raw"
+ url = route('.gitignore') + '/raw'
expect_to_send_git_blob(api(url, current_user), params)
end
it 'returns file by commit sha' do
# This file is deleted on HEAD
- file_path = "files%2Fjs%2Fcommit%2Ejs%2Ecoffee"
- params[:ref] = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9"
+ file_path = 'files%2Fjs%2Fcommit%2Ejs%2Ecoffee'
+ params[:ref] = '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9'
- expect_to_send_git_blob(api(route(file_path) + "/raw", current_user), params)
+ expect_to_send_git_blob(api(route(file_path) + '/raw', current_user), params)
end
it 'sets no-cache headers' do
- url = route('.gitignore') + "/raw"
+ url = route('.gitignore') + '/raw'
expect_to_send_git_blob(api(url, current_user), params)
- expect(response.headers["Cache-Control"]).to eq("max-age=0, private, must-revalidate, no-store, no-cache")
- expect(response.headers["Pragma"]).to eq("no-cache")
- expect(response.headers["Expires"]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
+ expect(response.headers['Cache-Control']).to eq('max-age=0, private, must-revalidate, no-store, no-cache')
+ expect(response.headers['Pragma']).to eq('no-cache')
+ expect(response.headers['Expires']).to eq('Fri, 01 Jan 1990 00:00:00 GMT')
end
context 'when mandatory params are not given' do
it_behaves_like '400 response' do
- let(:request) { get api(route("any%2Ffile"), current_user) }
+ let(:request) { get api(route('any%2Ffile'), current_user) }
end
end
@@ -693,29 +776,33 @@ RSpec.describe API::Files do
end
end
- context 'when unauthenticated', 'and project is public' do
- it_behaves_like 'repository raw files' do
- let(:project) { create(:project, :public, :repository) }
- let(:current_user) { nil }
+ context 'when unauthenticated' do
+ context 'and project is public' do
+ it_behaves_like 'repository raw files' do
+ let(:project) { create(:project, :public, :repository) }
+ let(:current_user) { nil }
+ end
end
- end
- context 'when unauthenticated', 'and project is private' do
- it_behaves_like '404 response' do
- let(:request) { get api(route(file_path)), params: params }
- let(:message) { '404 Project Not Found' }
+ context 'and project is private' do
+ it_behaves_like '404 response' do
+ let(:request) { get api(route(file_path)), params: params }
+ let(:message) { '404 Project Not Found' }
+ end
end
end
- context 'when authenticated', 'as a developer' do
- it_behaves_like 'repository raw files' do
- let(:current_user) { user }
+ context 'when authenticated' do
+ context 'and user is a developer' do
+ it_behaves_like 'repository raw files' do
+ let(:current_user) { user }
+ end
end
- end
- context 'when authenticated', 'as a guest' do
- it_behaves_like '403 response' do
- let(:request) { get api(route(file_path), guest), params: params }
+ context 'and user is a guest' do
+ it_behaves_like '403 response' do
+ let(:request) { get api(route(file_path), guest), params: params }
+ end
end
end
@@ -724,139 +811,205 @@ RSpec.describe API::Files do
token = create(:personal_access_token, scopes: ['read_repository'], user: user)
# This file is deleted on HEAD
- file_path = "files%2Fjs%2Fcommit%2Ejs%2Ecoffee"
- params[:ref] = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9"
- url = api(route(file_path) + "/raw", personal_access_token: token)
+ file_path = 'files%2Fjs%2Fcommit%2Ejs%2Ecoffee'
+ params[:ref] = '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9'
+ url = api(route(file_path) + '/raw', personal_access_token: token)
expect_to_send_git_blob(url, params)
end
end
end
- describe "POST /projects/:id/repository/files/:file_path" do
- let!(:file_path) { "new_subfolder%2Fnewfile%2Erb" }
+ describe 'POST /projects/:id/repository/files/:file_path' do
+ let!(:file_path) { 'new_subfolder%2Fnewfile%2Erb' }
+
let(:params) do
{
- branch: "master",
- content: "puts 8",
- commit_message: "Added newfile"
+ branch: 'master',
+ content: 'puts 8',
+ commit_message: 'Added newfile'
}
end
let(:executable_params) do
{
- branch: "master",
- content: "puts 8",
- commit_message: "Added newfile",
+ branch: 'master',
+ content: 'puts 8',
+ commit_message: 'Added newfile',
execute_filemode: true
}
end
- it 'returns 400 when file path is invalid' do
- post api(route(rouge_file_path), user), params: params
+ shared_examples 'creates a new file in the project repo' do
+ specify do
+ post api(route(file_path), current_user), params: params
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['error']).to eq(invalid_file_message)
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['file_path']).to eq(CGI.unescape(file_path))
+ last_commit = project.repository.commit.raw
+ expect(last_commit.author_email).to eq(current_user.email)
+ expect(last_commit.author_name).to eq(current_user.name)
+ expect(project.repository.blob_at_branch(params[:branch], CGI.unescape(file_path)).executable?).to eq(false)
+ end
end
- it_behaves_like 'when path is absolute' do
- subject { post api(route(absolute_path), user), params: params }
- end
+ context 'when authenticated', 'as a direct project member' do
+ context 'when project is private' do
+ context 'and user is a developer' do
+ it 'returns 400 when file path is invalid' do
+ post api(route(invalid_file_path), user), params: params
- it "creates a new file in project repo" do
- post api(route(file_path), user), params: params
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq(invalid_file_message)
+ end
- expect(response).to have_gitlab_http_status(:created)
- expect(json_response["file_path"]).to eq(CGI.unescape(file_path))
- last_commit = project.repository.commit.raw
- expect(last_commit.author_email).to eq(user.email)
- expect(last_commit.author_name).to eq(user.name)
- expect(project.repository.blob_at_branch(params[:branch], CGI.unescape(file_path)).executable?).to eq(false)
- end
+ it_behaves_like 'when path is absolute' do
+ subject { post api(route(absolute_path), user), params: params }
+ end
- it "creates a new executable file in project repo" do
- post api(route(file_path), user), params: executable_params
+ it_behaves_like 'creates a new file in the project repo' do
+ let(:current_user) { user }
+ end
- expect(response).to have_gitlab_http_status(:created)
- expect(json_response["file_path"]).to eq(CGI.unescape(file_path))
- last_commit = project.repository.commit.raw
- expect(last_commit.author_email).to eq(user.email)
- expect(last_commit.author_name).to eq(user.name)
- expect(project.repository.blob_at_branch(params[:branch], CGI.unescape(file_path)).executable?).to eq(true)
- end
+ it 'creates a new executable file in project repo' do
+ post api(route(file_path), user), params: executable_params
- it "returns a 400 bad request if no mandatory params given" do
- post api(route("any%2Etxt"), user)
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response['file_path']).to eq(CGI.unescape(file_path))
+ last_commit = project.repository.commit.raw
+ expect(last_commit.author_email).to eq(user.email)
+ expect(last_commit.author_name).to eq(user.name)
+ expect(project.repository.blob_at_branch(params[:branch], CGI.unescape(file_path)).executable?).to eq(true)
+ end
- expect(response).to have_gitlab_http_status(:bad_request)
- end
+ context 'when no mandatory params given' do
+ it 'returns a 400 bad request' do
+ post api(route('any%2Etxt'), user)
- it 'returns a 400 bad request if the commit message is empty' do
- params[:commit_message] = ''
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
- post 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)
- end
+ it 'returns a 400 bad request' do
+ post api(route(file_path), user), params: params
- it "returns a 400 if editor fails to create file" do
- allow_next_instance_of(Repository) do |instance|
- allow(instance).to receive(:create_file).and_raise(Gitlab::Git::CommitError, 'Cannot create file')
- end
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
- post api(route("any%2Etxt"), user), params: params
+ context 'when editor fails to create file' do
+ before do
+ allow_next_instance_of(Repository) do |instance|
+ allow(instance).to receive(:create_file).and_raise(Gitlab::Git::CommitError, 'Cannot create file')
+ end
+ end
- expect(response).to have_gitlab_http_status(:bad_request)
- end
+ it 'returns a 400 bad request' do
+ post api(route('any%2Etxt'), user), params: params
- context 'with PATs' do
- it 'returns 403 with `read_repository` scope' do
- token = create(:personal_access_token, scopes: ['read_repository'], user: user)
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
+ end
- post api(route(file_path), personal_access_token: token), params: params
+ context 'and PATs are used' do
+ it 'returns 403 with `read_repository` scope' do
+ token = create(:personal_access_token, scopes: ['read_repository'], user: user)
- expect(response).to have_gitlab_http_status(:forbidden)
- end
+ post api(route(file_path), personal_access_token: token), params: params
- it 'returns 201 with `api` scope' do
- token = create(:personal_access_token, scopes: ['api'], user: user)
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
- post api(route(file_path), personal_access_token: token), params: params
+ it 'returns 201 with `api` scope' do
+ token = create(:personal_access_token, scopes: ['api'], user: user)
- expect(response).to have_gitlab_http_status(:created)
- end
- end
+ post api(route(file_path), personal_access_token: token), params: params
- 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)
+ expect(response).to have_gitlab_http_status(:created)
+ end
+ end
- post api(route("new_file_with_author%2Etxt"), user), params: params
+ context 'and the repo is empty' do
+ let!(:project) { create(:project_empty_repo, namespace: user.namespace ) }
- expect(response).to have_gitlab_http_status(:created)
- expect(response.media_type).to eq('application/json')
- last_commit = project.repository.commit.raw
- expect(last_commit.author_email).to eq(author_email)
- expect(last_commit.author_name).to eq(author_name)
+ it_behaves_like 'creates a new file in the project repo' do
+ let(:current_user) { user }
+ let(:file_path) { 'newfile%2Erb' }
+ 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)
+ expect(response.media_type).to eq('application/json')
+ last_commit = project.repository.commit.raw
+ expect(last_commit.author_email).to eq(author_email)
+ expect(last_commit.author_name).to eq(author_name)
+ end
+ end
+ end
end
end
- context 'when the repo is empty' do
- let!(:project) { create(:project_empty_repo, namespace: user.namespace ) }
+ 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) }
- it "creates a new file in project repo" do
- post api(route("newfile%2Erb"), user), params: params
+ context 'and user is a guest' do
+ it_behaves_like '403 response' do
+ let(:request) { post api(route(file_path), inherited_guest), params: params }
+ end
+ end
- expect(response).to have_gitlab_http_status(:created)
- expect(json_response['file_path']).to eq('newfile.rb')
- last_commit = project.repository.commit.raw
- expect(last_commit.author_email).to eq(user.email)
- expect(last_commit.author_name).to eq(user.name)
+ context 'and user is a reporter' do
+ it_behaves_like '403 response' do
+ let(:request) { post api(route(file_path), inherited_reporter), params: params }
+ end
+ end
+
+ context 'and user is a developer' do
+ it_behaves_like 'creates a new file in the project repo' do
+ let(:current_user) { inherited_developer }
+ end
+ end
+ end
+
+ context 'when project is private' do
+ let_it_be(:project) { create(:project, :private, :repository, group: group) }
+
+ context 'and user is a guest' do
+ it_behaves_like '403 response' do
+ let(:request) { post api(route(file_path), inherited_guest), params: params }
+ end
+ end
+
+ context 'and user is a reporter' do
+ it_behaves_like '403 response' do
+ let(:request) { post api(route(file_path), inherited_reporter), params: params }
+ end
+ end
+
+ context 'and user is a developer' do
+ it_behaves_like 'creates a new file in the project repo' do
+ let(:current_user) { inherited_developer }
+ end
+ end
+ end
end
end
end
- describe "PUT /projects/:id/repository/files" do
+ describe 'PUT /projects/:id/repository/files' do
let(:params) do
{
branch: 'master',
@@ -865,7 +1018,7 @@ RSpec.describe API::Files do
}
end
- it "updates existing file in project repo" do
+ it 'updates existing file in project repo' do
put api(route(file_path), user), params: params
expect(response).to have_gitlab_http_status(:ok)
@@ -875,42 +1028,58 @@ RSpec.describe API::Files do
expect(last_commit.author_name).to eq(user.name)
end
- it 'returns a 400 bad request if the commit message is empty' do
- params[:commit_message] = ''
+ context 'when the commit message is empty' do
+ before do
+ params[:commit_message] = ''
+ end
- put api(route(file_path), user), params: params
+ it 'returns a 400 bad request' do
+ put api(route(file_path), user), params: params
- expect(response).to have_gitlab_http_status(:bad_request)
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
end
- it "returns a 400 bad request if update existing file with stale last commit id" do
- params_with_stale_id = params.merge(last_commit_id: 'stale')
+ context 'when updating an existing file with stale last commit id' do
+ let(:params_with_stale_id) { params.merge(last_commit_id: 'stale') }
- put api(route(file_path), user), params: params_with_stale_id
+ it 'returns a 400 bad request' do
+ put api(route(file_path), user), params: params_with_stale_id
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['message']).to eq(_('You are attempting to update a file that has changed since you started editing it.'))
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to eq(_('You are attempting to update a file that has changed since you started editing it.'))
+ end
end
- it "updates existing file in project repo with accepts correct last commit id" do
- last_commit = Gitlab::Git::Commit
- .last_for_path(project.repository, 'master', Addressable::URI.unencode_component(file_path))
- params_with_correct_id = params.merge(last_commit_id: last_commit.id)
+ context 'with correct last commit id' do
+ let(:last_commit) do
+ Gitlab::Git::Commit
+ .last_for_path(project.repository, 'master', Addressable::URI.unencode_component(file_path))
+ end
- put api(route(file_path), user), params: params_with_correct_id
+ let(:params_with_correct_id) { params.merge(last_commit_id: last_commit.id) }
- expect(response).to have_gitlab_http_status(:ok)
+ it 'updates existing file in project repo' do
+ put api(route(file_path), user), params: params_with_correct_id
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
end
- it "returns 400 when file path is invalid" do
- last_commit = Gitlab::Git::Commit
- .last_for_path(project.repository, 'master', Addressable::URI.unencode_component(file_path))
- params_with_correct_id = params.merge(last_commit_id: last_commit.id)
+ context 'when file path is invalid' do
+ let(:last_commit) do
+ Gitlab::Git::Commit
+ .last_for_path(project.repository, 'master', Addressable::URI.unencode_component(file_path))
+ end
- put api(route(rouge_file_path), user), params: params_with_correct_id
+ let(:params_with_correct_id) { params.merge(last_commit_id: last_commit.id) }
- expect(response).to have_gitlab_http_status(:bad_request)
- expect(json_response['error']).to eq(invalid_file_message)
+ it 'returns a 400 bad request' do
+ put api(route(invalid_file_path), user), params: params_with_correct_id
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['error']).to eq(invalid_file_message)
+ end
end
it_behaves_like 'when path is absolute' do
@@ -924,15 +1093,17 @@ RSpec.describe API::Files do
subject { put api(route(absolute_path), user), params: params_with_correct_id }
end
- it "returns a 400 bad request if no params given" do
- put api(route(file_path), user)
+ context 'when no params given' do
+ it 'returns a 400 bad request' do
+ put api(route(file_path), user)
- expect(response).to have_gitlab_http_status(:bad_request)
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
end
- context "when specifying an author" do
- it "updates a file with the specified author" do
- params.merge!(author_email: author_email, author_name: author_name, content: "New content")
+ context 'when specifying an author' do
+ it 'updates a file with the specified author' do
+ params.merge!(author_email: author_email, author_name: author_name, content: 'New content')
put api(route(file_path), user), params: params
@@ -982,7 +1153,7 @@ RSpec.describe API::Files do
end
end
- describe "DELETE /projects/:id/repository/files" do
+ describe 'DELETE /projects/:id/repository/files' do
let(:params) do
{
branch: 'master',
@@ -991,7 +1162,7 @@ RSpec.describe API::Files do
end
it 'returns 400 when file path is invalid' do
- delete api(route(rouge_file_path), user), params: params
+ 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)
@@ -1001,38 +1172,48 @@ RSpec.describe API::Files do
subject { delete api(route(absolute_path), user), params: params }
end
- it "deletes existing file in project repo" do
+ 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
- it "returns a 400 bad request if no params given" do
- delete api(route(file_path), user)
+ 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)
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
end
- it 'returns a 400 bad request if the commit message is empty' do
- params[:commit_message] = ''
+ context 'when the commit message is empty' do
+ before do
+ params[:commit_message] = ''
+ 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(:bad_request)
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
end
- it "returns a 400 if fails to delete file" do
- allow_next_instance_of(Repository) do |instance|
- allow(instance).to receive(:delete_file).and_raise(Gitlab::Git::CommitError, 'Cannot delete file')
+ 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(:bad_request)
+ expect(response).to have_gitlab_http_status(:bad_request)
+ end
end
- context "when specifying an author" do
- it "removes a file with the specified author" do
+ 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)
delete api(route(file_path), user), params: params
@@ -1042,7 +1223,7 @@ RSpec.describe API::Files do
end
end
- describe "POST /projects/:id/repository/files with binary file" do
+ describe 'POST /projects/:id/repository/files with binary file' do
let(:file_path) { 'test%2Ebin' }
let(:put_params) do
{
@@ -1063,7 +1244,7 @@ RSpec.describe API::Files do
post api(route(file_path), user), params: put_params
end
- it "remains unchanged" do
+ it 'remains unchanged' do
get api(route(file_path), user), params: get_params
expect(response).to have_gitlab_http_status(:ok)
diff --git a/spec/requests/api/generic_packages_spec.rb b/spec/requests/api/generic_packages_spec.rb
index 3a5c6103781..823eafab734 100644
--- a/spec/requests/api/generic_packages_spec.rb
+++ b/spec/requests/api/generic_packages_spec.rb
@@ -572,6 +572,12 @@ RSpec.describe API::GenericPackages do
expect(response).to have_gitlab_http_status(expected_status)
end
+
+ if params[:expected_status] == :success
+ it_behaves_like 'bumping the package last downloaded at field' do
+ subject { download_file(auth_header) }
+ end
+ end
end
where(:authenticate_with, :expected_status) do
@@ -587,6 +593,12 @@ RSpec.describe API::GenericPackages do
expect(response).to have_gitlab_http_status(expected_status)
end
+
+ if params[:expected_status] == :success
+ it_behaves_like 'bumping the package last downloaded at field' do
+ subject { download_file(deploy_token_auth_header) }
+ end
+ end
end
end
@@ -608,6 +620,12 @@ RSpec.describe API::GenericPackages do
expect(response).to have_gitlab_http_status(expected_status)
end
+
+ if params[:expected_status] == :success
+ it_behaves_like 'bumping the package last downloaded at field' do
+ subject { download_file(personal_access_token_header) }
+ end
+ end
end
end
diff --git a/spec/requests/api/graphql/ci/config_spec.rb b/spec/requests/api/graphql/ci/config_spec.rb
index 5f8a895b16e..960fda80dd9 100644
--- a/spec/requests/api/graphql/ci/config_spec.rb
+++ b/spec/requests/api/graphql/ci/config_spec.rb
@@ -173,7 +173,7 @@ RSpec.describe 'Query.ciConfig' do
{
"name" => "docker",
"size" => 1,
- "jobs" =>
+ "jobs" =>
{
"nodes" => [
{
@@ -206,7 +206,7 @@ RSpec.describe 'Query.ciConfig' do
{
"name" => "deploy_job",
"size" => 1,
- "jobs" =>
+ "jobs" =>
{
"nodes" => [
{
@@ -332,7 +332,7 @@ RSpec.describe 'Query.ciConfig' do
"only" => { "refs" => %w[branches tags] },
"when" => "on_success",
"tags" => [],
- "needs" => { "nodes" => [] } }
+ "needs" => { "nodes" => [] } }
]
}
}
diff --git a/spec/requests/api/graphql/ci/config_variables_spec.rb b/spec/requests/api/graphql/ci/config_variables_spec.rb
new file mode 100644
index 00000000000..2b5a5d0dc93
--- /dev/null
+++ b/spec/requests/api/graphql/ci/config_variables_spec.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Query.project(fullPath).ciConfigVariables(sha)' do
+ include GraphqlHelpers
+ include ReactiveCachingHelpers
+
+ let_it_be(:content) do
+ File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+ end
+
+ let_it_be(:project) { create(:project, :custom_repo, :public, files: { '.gitlab-ci.yml' => content }) }
+ let_it_be(:user) { create(:user) }
+
+ let(:service) { Ci::ListConfigVariablesService.new(project, user) }
+ let(:sha) { project.repository.commit.sha }
+
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ ciConfigVariables(sha: "#{sha}") {
+ key
+ value
+ description
+ }
+ }
+ }
+ )
+ end
+
+ context 'when the user has the correct permissions' do
+ before do
+ project.add_maintainer(user)
+ allow(Ci::ListConfigVariablesService)
+ .to receive(:new)
+ .and_return(service)
+ end
+
+ context 'when the cache is not empty' do
+ before do
+ synchronous_reactive_cache(service)
+ end
+
+ it 'returns the CI variables for the config' do
+ expect(service)
+ .to receive(:execute)
+ .with(sha)
+ .and_call_original
+
+ post_graphql(query, current_user: user)
+
+ expect(graphql_data.dig('project', 'ciConfigVariables')).to contain_exactly(
+ {
+ 'key' => 'DB_NAME',
+ 'value' => 'postgres',
+ 'description' => nil
+ },
+ {
+ 'key' => 'ENVIRONMENT_VAR',
+ 'value' => 'env var value',
+ 'description' => 'env var description'
+ }
+ )
+ end
+ end
+
+ context 'when the cache is empty' do
+ it 'returns nothing' do
+ post_graphql(query, current_user: user)
+
+ expect(graphql_data.dig('project', 'ciConfigVariables')).to be_nil
+ end
+ end
+ end
+
+ context 'when the user is not authorized' do
+ before do
+ project.add_guest(user)
+ allow(Ci::ListConfigVariablesService)
+ .to receive(:new)
+ .and_return(service)
+ synchronous_reactive_cache(service)
+ end
+
+ it 'returns nothing' do
+ post_graphql(query, current_user: user)
+
+ expect(graphql_data.dig('project', 'ciConfigVariables')).to be_nil
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/ci/group_variables_spec.rb b/spec/requests/api/graphql/ci/group_variables_spec.rb
index 5ea6646ec2c..7baf26c7648 100644
--- a/spec/requests/api/graphql/ci/group_variables_spec.rb
+++ b/spec/requests/api/graphql/ci/group_variables_spec.rb
@@ -13,6 +13,7 @@ RSpec.describe 'Query.group(fullPath).ciVariables' do
query {
group(fullPath: "#{group.full_path}") {
ciVariables {
+ limit
nodes {
id
key
@@ -35,11 +36,18 @@ RSpec.describe 'Query.group(fullPath).ciVariables' do
end
it "returns the group's CI variables" do
- variable = create(:ci_group_variable, group: group, key: 'TEST_VAR', value: 'test',
- masked: false, protected: true, raw: true, environment_scope: 'staging')
+ variable = create(:ci_group_variable,
+ group: group,
+ key: 'TEST_VAR',
+ value: 'test',
+ masked: false,
+ protected: true,
+ raw: true,
+ environment_scope: 'staging')
post_graphql(query, current_user: user)
+ expect(graphql_data.dig('group', 'ciVariables', 'limit')).to be(200)
expect(graphql_data.dig('group', 'ciVariables', 'nodes')).to contain_exactly({
'id' => variable.to_global_id.to_s,
'key' => 'TEST_VAR',
diff --git a/spec/requests/api/graphql/ci/instance_variables_spec.rb b/spec/requests/api/graphql/ci/instance_variables_spec.rb
index c5c88697bf4..cd6b2de98a1 100644
--- a/spec/requests/api/graphql/ci/instance_variables_spec.rb
+++ b/spec/requests/api/graphql/ci/instance_variables_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe 'Query.ciVariables' do
it "returns the instance's CI variables" do
variable = create(:ci_instance_variable, key: 'TEST_VAR', value: 'test',
- masked: false, protected: true, raw: true)
+ masked: false, protected: true, raw: true)
post_graphql(query, current_user: user)
diff --git a/spec/requests/api/graphql/ci/jobs_spec.rb b/spec/requests/api/graphql/ci/jobs_spec.rb
index 8c4ab13fc35..fa8fb1d54aa 100644
--- a/spec/requests/api/graphql/ci/jobs_spec.rb
+++ b/spec/requests/api/graphql/ci/jobs_spec.rb
@@ -335,4 +335,35 @@ RSpec.describe 'Query.project.pipeline' do
end
end
end
+
+ context 'when querying jobs for multiple projects' do
+ let(:query) do
+ %(
+ query {
+ 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 'returns an error' do
+ post_graphql(query, current_user: user)
+
+ expect_graphql_errors_to_include [/"jobs" field can be requested only for 1 Project\(s\) at a time./]
+ end
+ end
end
diff --git a/spec/requests/api/graphql/ci/project_variables_spec.rb b/spec/requests/api/graphql/ci/project_variables_spec.rb
index e61f146b24c..d49a4a7e768 100644
--- a/spec/requests/api/graphql/ci/project_variables_spec.rb
+++ b/spec/requests/api/graphql/ci/project_variables_spec.rb
@@ -13,6 +13,7 @@ RSpec.describe 'Query.project(fullPath).ciVariables' do
query {
project(fullPath: "#{project.full_path}") {
ciVariables {
+ limit
nodes {
id
key
@@ -36,10 +37,11 @@ RSpec.describe 'Query.project(fullPath).ciVariables' do
it "returns the project's CI variables" do
variable = create(:ci_variable, project: project, key: 'TEST_VAR', value: 'test',
- masked: false, protected: true, raw: true, environment_scope: 'production')
+ masked: false, protected: true, raw: true, environment_scope: 'production')
post_graphql(query, current_user: user)
+ expect(graphql_data.dig('project', 'ciVariables', 'limit')).to be(200)
expect(graphql_data.dig('project', 'ciVariables', 'nodes')).to contain_exactly({
'id' => variable.to_global_id.to_s,
'key' => 'TEST_VAR',
diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb
index e17a83d8e47..bd90753f9ad 100644
--- a/spec/requests/api/graphql/ci/runner_spec.rb
+++ b/spec/requests/api/graphql/ci/runner_spec.rb
@@ -9,24 +9,53 @@ RSpec.describe 'Query.runner(id)' do
let_it_be(:group) { create(:group) }
let_it_be(:active_instance_runner) do
- create(:ci_runner, :instance, description: 'Runner 1', contacted_at: 2.hours.ago,
- active: true, version: 'adfe156', revision: 'a', locked: true, ip_address: '127.0.0.1', maximum_timeout: 600,
- access_level: 0, tag_list: %w[tag1 tag2], run_untagged: true, executor_type: :custom,
- maintenance_note: '**Test maintenance note**')
+ create(:ci_runner, :instance,
+ description: 'Runner 1',
+ contacted_at: 2.hours.ago,
+ active: true,
+ version: 'adfe156',
+ revision: 'a',
+ locked: true,
+ ip_address: '127.0.0.1',
+ maximum_timeout: 600,
+ access_level: 0,
+ tag_list: %w[tag1 tag2],
+ run_untagged: true,
+ executor_type: :custom,
+ maintenance_note: '**Test maintenance note**')
end
let_it_be(:inactive_instance_runner) do
- create(:ci_runner, :instance, description: 'Runner 2', contacted_at: 1.day.ago, active: false,
- version: 'adfe157', revision: 'b', ip_address: '10.10.10.10', access_level: 1, run_untagged: true)
+ create(:ci_runner, :instance,
+ description: 'Runner 2',
+ contacted_at: 1.day.ago,
+ active: false,
+ version: 'adfe157',
+ revision: 'b',
+ ip_address: '10.10.10.10',
+ access_level: 1,
+ run_untagged: true)
end
let_it_be(:active_group_runner) do
- create(:ci_runner, :group, groups: [group], description: 'Group runner 1', contacted_at: 2.hours.ago,
- active: true, version: 'adfe156', revision: 'a', locked: true, ip_address: '127.0.0.1', maximum_timeout: 600,
- access_level: 0, tag_list: %w[tag1 tag2], run_untagged: true, executor_type: :shell)
+ create(:ci_runner, :group,
+ groups: [group],
+ description: 'Group runner 1',
+ contacted_at: 2.hours.ago,
+ active: true,
+ version: 'adfe156',
+ revision: 'a',
+ locked: true,
+ ip_address: '127.0.0.1',
+ maximum_timeout: 600,
+ access_level: 0,
+ tag_list: %w[tag1 tag2],
+ run_untagged: true,
+ executor_type: :shell)
end
- let_it_be(:active_project_runner) { create(:ci_runner, :project) }
+ let_it_be(:project1) { create(:project) }
+ let_it_be(:active_project_runner) { create(:ci_runner, :project, projects: [project1]) }
shared_examples 'runner details fetch' do
let(:query) do
@@ -159,8 +188,16 @@ RSpec.describe 'Query.runner(id)' do
with_them do
let(:project_runner) do
- create(:ci_runner, :project, description: 'Runner 3', contacted_at: 1.day.ago, active: false, locked: is_locked,
- version: 'adfe157', revision: 'b', ip_address: '10.10.10.10', access_level: 1, run_untagged: true)
+ create(:ci_runner, :project,
+ description: 'Runner 3',
+ contacted_at: 1.day.ago,
+ active: false,
+ locked: is_locked,
+ version: 'adfe157',
+ revision: 'b',
+ ip_address: '10.10.10.10',
+ access_level: 1,
+ run_untagged: true)
end
let(:query) do
@@ -187,7 +224,6 @@ RSpec.describe 'Query.runner(id)' do
end
describe 'ownerProject' do
- let_it_be(:project1) { create(:project) }
let_it_be(:project2) { create(:project) }
let_it_be(:runner1) { create(:ci_runner, :project, projects: [project2, project1]) }
let_it_be(:runner2) { create(:ci_runner, :project, projects: [project1, project2]) }
@@ -301,7 +337,6 @@ RSpec.describe 'Query.runner(id)' do
end
describe 'for multiple runners' do
- let_it_be(:project1) { create(:project, :test_repo) }
let_it_be(:project2) { create(:project, :test_repo) }
let_it_be(:project_runner1) { create(:ci_runner, :project, projects: [project1, project2], description: 'Runner 1') }
let_it_be(:project_runner2) { create(:ci_runner, :project, projects: [], description: 'Runner 2') }
@@ -394,6 +429,8 @@ RSpec.describe 'Query.runner(id)' do
'jobs' => nil, # returning jobs not allowed for more than 1 runner (see RunnerJobsResolver)
'projectCount' => nil,
'projects' => nil)
+
+ expect_graphql_errors_to_include [/"jobs" field can be requested only for 1 CiRunner\(s\) at a time./]
end
end
end
@@ -472,8 +509,8 @@ RSpec.describe 'Query.runner(id)' do
<<~QUERY
{
instance_runner1: #{runner_query(active_instance_runner)}
- project_runner1: #{runner_query(active_project_runner)}
group_runner1: #{runner_query(active_group_runner)}
+ project_runner1: #{runner_query(active_project_runner)}
}
QUERY
end
@@ -493,12 +530,13 @@ RSpec.describe 'Query.runner(id)' do
it 'does not execute more queries per runner', :aggregate_failures do
# warm-up license cache and so on:
- post_graphql(double_query, current_user: user)
+ personal_access_token = create(:personal_access_token, user: user)
+ args = { current_user: user, token: { personal_access_token: personal_access_token } }
+ post_graphql(double_query, **args)
- control = ActiveRecord::QueryRecorder.new { post_graphql(single_query, current_user: user) }
+ control = ActiveRecord::QueryRecorder.new { post_graphql(single_query, **args) }
- expect { post_graphql(double_query, current_user: user) }
- .not_to exceed_query_limit(control)
+ expect { post_graphql(double_query, **args) }.not_to exceed_query_limit(control)
expect(graphql_data.count).to eq 6
expect(graphql_data).to match(
@@ -528,4 +566,91 @@ RSpec.describe 'Query.runner(id)' do
))
end
end
+
+ describe 'sorting and pagination' do
+ let(:query) do
+ <<~GQL
+ query($id: CiRunnerID!, $projectSearchTerm: String, $n: Int, $cursor: String) {
+ runner(id: $id) {
+ #{fields}
+ }
+ }
+ GQL
+ end
+
+ before do
+ post_graphql(query, current_user: user, variables: variables)
+ end
+
+ context 'with project search term' do
+ let_it_be(:project1) { create(:project, description: 'abc') }
+ let_it_be(:project2) { create(:project, description: 'def') }
+ let_it_be(:project_runner) do
+ create(:ci_runner, :project, projects: [project1, project2])
+ end
+
+ let(:variables) { { id: project_runner.to_global_id.to_s, n: n, project_search_term: search_term } }
+
+ let(:fields) do
+ <<~QUERY
+ projects(search: $projectSearchTerm, first: $n, after: $cursor) {
+ count
+ nodes {
+ id
+ }
+ pageInfo {
+ hasPreviousPage
+ startCursor
+ endCursor
+ hasNextPage
+ }
+ }
+ QUERY
+ end
+
+ let(:projects_data) { graphql_data_at('runner', 'projects') }
+
+ context 'set to empty string' do
+ let(:search_term) { '' }
+
+ context 'with n = 1' do
+ let(:n) { 1 }
+
+ it_behaves_like 'a working graphql query'
+
+ it 'returns paged result' do
+ expect(projects_data).not_to be_nil
+ expect(projects_data['count']).to eq 2
+ expect(projects_data['pageInfo']['hasNextPage']).to eq true
+ end
+ end
+
+ context 'with n = 2' do
+ let(:n) { 2 }
+
+ it 'returns non-paged result' do
+ expect(projects_data).not_to be_nil
+ expect(projects_data['count']).to eq 2
+ expect(projects_data['pageInfo']['hasNextPage']).to eq false
+ end
+ end
+ end
+
+ context 'set to partial match' do
+ let(:search_term) { 'def' }
+
+ context 'with n = 1' do
+ let(:n) { 1 }
+
+ it_behaves_like 'a working graphql query'
+
+ it 'returns paged result with no additional pages' do
+ expect(projects_data).not_to be_nil
+ expect(projects_data['count']).to eq 1
+ expect(projects_data['pageInfo']['hasNextPage']).to eq false
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/requests/api/graphql/ci/runners_spec.rb b/spec/requests/api/graphql/ci/runners_spec.rb
index 749f6839cb5..3054b866812 100644
--- a/spec/requests/api/graphql/ci/runners_spec.rb
+++ b/spec/requests/api/graphql/ci/runners_spec.rb
@@ -69,15 +69,6 @@ RSpec.describe 'Query.runners' do
it_behaves_like 'a working graphql query returning expected runner'
end
-
- context 'runner_type is PROJECT_TYPE and status is NEVER_CONTACTED' do
- let(:runner_type) { 'PROJECT_TYPE' }
- let(:status) { 'NEVER_CONTACTED' }
-
- let!(:expected_runner) { project_runner }
-
- it_behaves_like 'a working graphql query returning expected runner'
- end
end
describe 'pagination' do
@@ -141,8 +132,13 @@ RSpec.describe 'Group.runners' do
describe 'edges' do
let_it_be(:runner) do
- create(:ci_runner, :group, active: false, version: 'def', revision: '456',
- description: 'Project runner', groups: [group], ip_address: '127.0.0.1')
+ create(:ci_runner, :group,
+ active: false,
+ version: 'def',
+ revision: '456',
+ description: 'Project runner',
+ groups: [group],
+ ip_address: '127.0.0.1')
end
let(:query) do
diff --git a/spec/requests/api/graphql/custom_emoji_query_spec.rb b/spec/requests/api/graphql/custom_emoji_query_spec.rb
index 13b7a22e791..5dd5ad117b0 100644
--- a/spec/requests/api/graphql/custom_emoji_query_spec.rb
+++ b/spec/requests/api/graphql/custom_emoji_query_spec.rb
@@ -35,7 +35,17 @@ RSpec.describe 'getting custom emoji within namespace' do
expect(graphql_data['group']['customEmoji']['nodes'].first['name']).to eq(custom_emoji.name)
end
- it 'returns nil when unauthorised' do
+ it 'returns nil custom emoji when the custom_emoji feature flag is disabled' do
+ stub_feature_flags(custom_emoji: false)
+
+ post_graphql(custom_emoji_query(group), current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(graphql_data['group']).to be_present
+ expect(graphql_data['group']['customEmoji']).to be_nil
+ end
+
+ it 'returns nil group when unauthorised' do
user = create(:user)
post_graphql(custom_emoji_query(group), current_user: user)
diff --git a/spec/requests/api/graphql/environments/deployments_query_spec.rb b/spec/requests/api/graphql/environments/deployments_query_spec.rb
new file mode 100644
index 00000000000..6da00057449
--- /dev/null
+++ b/spec/requests/api/graphql/environments/deployments_query_spec.rb
@@ -0,0 +1,487 @@
+# 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/group/group_members_spec.rb b/spec/requests/api/graphql/group/group_members_spec.rb
index bab8d5b770c..5f8becc0726 100644
--- a/spec/requests/api/graphql/group/group_members_spec.rb
+++ b/spec/requests/api/graphql/group/group_members_spec.rb
@@ -156,13 +156,20 @@ RSpec.describe 'getting group members information' do
expect_array_response(child_user)
end
- it 'returns invited members plus inherited members' do
+ it 'returns invited members and inherited members of a shared group' do
fetch_members(group: child_group, args: { relations: [:DIRECT, :INHERITED, :SHARED_FROM_GROUPS] })
expect(graphql_errors).to be_nil
expect_array_response(invited_user, user_1, user_2, child_user)
end
+ it 'returns invited members and inherited members of an ancestor of a shared group' do
+ fetch_members(group: grandchild_group, args: { relations: [:DIRECT, :INHERITED, :SHARED_FROM_GROUPS] })
+
+ expect(graphql_errors).to be_nil
+ expect_array_response(grandchild_user, invited_user, user_1, user_2, child_user)
+ end
+
it 'returns direct and inherited members' do
fetch_members(group: child_group, args: { relations: [:DIRECT, :INHERITED] })
diff --git a/spec/requests/api/graphql/group/packages_spec.rb b/spec/requests/api/graphql/group/packages_spec.rb
index adee556db3a..cf8736db5af 100644
--- a/spec/requests/api/graphql/group/packages_spec.rb
+++ b/spec/requests/api/graphql/group/packages_spec.rb
@@ -39,7 +39,7 @@ RSpec.describe 'getting a package list for a group' do
it 'returns an error for the second group and data for the first' do
expect(a_packages_names).to contain_exactly(group_one_package.name)
- expect_graphql_errors_to_include [/Packages can be requested only for one group at a time/]
+ expect_graphql_errors_to_include [/"packages" field can be requested only for 1 Group\(s\) at a time./]
expect(graphql_data_at(:b, :packages)).to be(nil)
end
end
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 a33e3ae5427..d6b0673e4f8 100644
--- a/spec/requests/api/graphql/group/work_item_types_spec.rb
+++ b/spec/requests/api/graphql/group/work_item_types_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe 'getting a list of work item types for a group' do
end
end
- context "when user doesn't have acces to the group" do
+ context "when user doesn't have access to the group" do
let(:current_user) { create(:user) }
before do
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 46ec22e7ef8..06093e9f7c2 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
@@ -100,6 +100,20 @@ RSpec.describe 'Reposition and move issue within board lists' do
expect(response_issue['labels']['edges'][0]['node']['title']).to eq(testing.title)
end
end
+
+ context 'when moving an issue using position_in_list' do
+ let(:issue_move_params) { { from_list_id: list1.id, to_list_id: list2.id, position_in_list: 0 } }
+
+ it 'repositions an issue' do
+ post_graphql_mutation(mutation(params), current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ response_issue = json_response['data'][mutation_result_identifier]['issue']
+ expect(response_issue['iid']).to eq(issue1.iid.to_s)
+ expect(response_issue['labels']['edges'][0]['node']['title']).to eq(testing.title)
+ expect(response_issue['relativePosition']).to be < existing_issue1.relative_position
+ end
+ end
end
context 'when user has no access to resources' do
diff --git a/spec/requests/api/graphql/mutations/branches/create_spec.rb b/spec/requests/api/graphql/mutations/branches/create_spec.rb
index 6a098002963..9ee2f41e8fc 100644
--- a/spec/requests/api/graphql/mutations/branches/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/branches/create_spec.rb
@@ -5,26 +5,18 @@ require 'spec_helper'
RSpec.describe 'Creation of a new branch' do
include GraphqlHelpers
+ let_it_be(:group) { create(:group, :public) }
let_it_be(:current_user) { create(:user) }
- let_it_be(:project) { create(:project, :public, :empty_repo) }
let(:input) { { project_path: project.full_path, name: new_branch, ref: ref } }
- let(:new_branch) { 'new_branch' }
+ let(:new_branch) { "new_branch_#{SecureRandom.hex(4)}" }
let(:ref) { 'master' }
let(:mutation) { graphql_mutation(:create_branch, input) }
let(:mutation_response) { graphql_mutation_response(:create_branch) }
- context 'the user is not allowed to create a branch' do
- it_behaves_like 'a mutation that returns a top-level access error'
- end
-
- context 'when user has permissions to create a branch' do
- before do
- project.add_developer(current_user)
- end
-
- it 'creates a new branch' do
+ shared_examples 'creates a new branch' do
+ specify do
post_graphql_mutation(mutation, current_user: current_user)
expect(response).to have_gitlab_http_status(:success)
@@ -33,14 +25,75 @@ RSpec.describe 'Creation of a new branch' do
'commit' => a_hash_including('id')
)
end
+ end
+
+ context 'when project is public' do
+ let_it_be(:project) { create(:project, :public, :empty_repo) }
+
+ context 'when user is not allowed to create a branch' do
+ it_behaves_like 'a mutation that returns a top-level access error'
+ end
+
+ context 'when user is a direct project member' do
+ context 'and user is a developer' do
+ before do
+ project.add_developer(current_user)
+ end
+
+ it_behaves_like 'creates a new branch'
+
+ context 'when ref is not correct' do
+ err_msg = 'Failed to create branch \'another_branch\': invalid reference name \'unknown\''
+ let(:new_branch) { 'another_branch' }
+ let(:ref) { 'unknown' }
+
+ it_behaves_like 'a mutation that returns errors in the response', errors: [err_msg]
+ end
+ end
+ end
+
+ context 'when user is an inherited member from the group' do
+ context 'when project has a private repository' do
+ let_it_be(:project) { create(:project, :public, :empty_repo, :repository_private, group: group) }
+
+ context 'and user is a guest' do
+ before do
+ group.add_guest(current_user)
+ end
+
+ it_behaves_like 'a mutation that returns a top-level access error'
+ end
+
+ context 'and user is a developer' do
+ before do
+ group.add_developer(current_user)
+ end
+
+ it_behaves_like 'creates a new branch'
+ end
+ end
+ end
+ end
+
+ context 'when project is private' do
+ let_it_be(:project) { create(:project, :private, :empty_repo, group: group) }
+
+ context 'when user is an inherited member from the group' do
+ context 'and user is a guest' do
+ before do
+ group.add_guest(current_user)
+ end
+
+ it_behaves_like 'a mutation that returns a top-level access error'
+ end
- context 'when ref is not correct' do
- err_msg = 'Failed to create branch \'another_branch\': invalid reference name \'unknown\''
- let(:new_branch) { 'another_branch' }
- let(:ref) { 'unknown' }
+ context 'and user is a developer' do
+ before do
+ group.add_developer(current_user)
+ end
- it_behaves_like 'a mutation that returns errors in the response',
- errors: [err_msg]
+ it_behaves_like 'creates a new branch'
+ end
end
end
end
diff --git a/spec/requests/api/graphql/mutations/ci/job/destroy_spec.rb b/spec/requests/api/graphql/mutations/ci/job/destroy_spec.rb
new file mode 100644
index 00000000000..5855eb6bb51
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/ci/job/destroy_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'JobArtifactsDestroy' do
+ include GraphqlHelpers
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:job) { create(:ci_build) }
+
+ let(:mutation) do
+ variables = {
+ id: job.to_global_id.to_s
+ }
+ graphql_mutation(:job_artifacts_destroy, variables, <<~FIELDS)
+ job {
+ name
+ }
+ destroyedArtifactsCount
+ errors
+ FIELDS
+ end
+
+ before do
+ create(:ci_job_artifact, :archive, job: job)
+ create(:ci_job_artifact, :junit, job: job)
+ end
+
+ it 'returns an error if the user is not allowed to destroy the job artifacts' do
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(graphql_errors).not_to be_empty
+ expect(job.reload.job_artifacts.count).to be(2)
+ end
+
+ it 'destroys the job artifacts and returns the expected data' do
+ job.project.add_maintainer(user)
+ expected_data = {
+ 'jobArtifactsDestroy' => {
+ 'errors' => [],
+ 'destroyedArtifactsCount' => 2,
+ 'job' => {
+ 'name' => job.name
+ }
+ }
+ }
+
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(graphql_data).to eq(expected_data)
+ expect(job.reload.job_artifacts.count).to be(0)
+ end
+end
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
new file mode 100644
index 00000000000..a5ec9ea343d
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/ci/job_artifact/destroy_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'ArtifactDestroy' do
+ include GraphqlHelpers
+
+ let(:user) { create(:user) }
+ let(:artifact) { create(:ci_job_artifact) }
+
+ let(:mutation) do
+ variables = {
+ id: artifact.to_global_id.to_s
+ }
+ graphql_mutation(:artifact_destroy, variables, 'errors')
+ end
+
+ it 'returns an error if the user is not allowed to destroy the artifact' do
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(graphql_errors).not_to be_empty
+ end
+
+ context 'when the user is allowed to destroy the artifact' do
+ before do
+ artifact.job.project.add_maintainer(user)
+ end
+
+ it 'destroys the artifact' do
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect { artifact.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'returns error if destory fails' do
+ allow_next_found_instance_of(Ci::JobArtifact) do |instance|
+ allow(instance).to receive(:destroy).and_return(false)
+ allow(instance).to receive_message_chain(:errors, :full_messages).and_return(['cannot be removed'])
+ end
+
+ post_graphql_mutation(mutation, current_user: user)
+
+ expect(graphql_data_at(:artifact_destroy, :errors)).to contain_exactly('cannot be removed')
+ end
+ end
+end
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 c91437fa355..66facdebe78 100644
--- a/spec/requests/api/graphql/mutations/custom_emoji/create_spec.rb
+++ b/spec/requests/api/graphql/mutations/custom_emoji/create_spec.rb
@@ -39,5 +39,19 @@ RSpec.describe 'Creation of a new Custom Emoji' do
expect(gql_response['customEmoji']['name']).to eq(attributes[:name])
expect(gql_response['customEmoji']['url']).to eq(attributes[:url])
end
+
+ context 'when the custom_emoji feature flag is disabled' do
+ before do
+ stub_feature_flags(custom_emoji: false)
+ end
+
+ it 'does nothing and returns and error' do
+ expect do
+ post_graphql_mutation(mutation, current_user: current_user)
+ end.to not_change(CustomEmoji, :count)
+
+ expect_graphql_errors_to_include('Custom emoji feature is disabled')
+ end
+ end
end
end
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 07fd57a2cee..7d25206e617 100644
--- a/spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb
+++ b/spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb
@@ -68,6 +68,20 @@ RSpec.describe 'Deletion of custom emoji' do
end
it_behaves_like 'deletes custom emoji'
+
+ context 'when the custom_emoji feature flag is disabled' do
+ before do
+ stub_feature_flags(custom_emoji: false)
+ end
+
+ it_behaves_like 'does not delete custom emoji'
+
+ it 'returns an error' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect_graphql_errors_to_include('Custom emoji feature is disabled')
+ end
+ end
end
end
end
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 9272e218172..85eaec90f47 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
@@ -4,6 +4,7 @@ require 'spec_helper'
RSpec.describe 'Promote an incident timeline event from a comment' do
include GraphqlHelpers
+ include NotesHelper
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
@@ -53,7 +54,7 @@ RSpec.describe 'Promote an incident timeline event from a comment' do
'promotedFromNote' => {
'id' => comment.to_global_id.to_s
},
- 'note' => comment.note,
+ 'note' => "@#{comment.author.username} [commented](#{noteable_note_url(comment)}): '#{comment.note}'",
'action' => 'comment',
'editable' => true,
'occurredAt' => comment.created_at.iso8601
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 608b36e4f15..8cec5867aca 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
@@ -93,6 +93,16 @@ RSpec.describe 'Setting assignees of a merge request', :assume_throttled do
expect(response).to have_gitlab_http_status(:success)
expect(mutation_assignee_nodes).to match_array(expected_result)
end
+
+ it 'triggers webhooks', :sidekiq_inline do
+ hook = create(:project_hook, merge_requests_events: true, project: merge_request.project)
+
+ expect(WebHookWorker).to receive(:perform_async).with(hook.id, anything, 'merge_request_hooks', anything)
+
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ end
end
context 'when passing an empty list of assignees' do
diff --git a/spec/requests/api/graphql/mutations/releases/update_spec.rb b/spec/requests/api/graphql/mutations/releases/update_spec.rb
index 33d4e57904c..240db764f40 100644
--- a/spec/requests/api/graphql/mutations/releases/update_spec.rb
+++ b/spec/requests/api/graphql/mutations/releases/update_spec.rb
@@ -22,9 +22,14 @@ RSpec.describe 'Updating an existing release' do
let_it_be(:milestones) { [milestone_12_3, milestone_12_4] }
let_it_be(:release) do
- create(:release, project: project, tag: tag_name, name: name,
- description: description, released_at: Time.parse(released_at).utc,
- created_at: Time.parse(created_at).utc, milestones: milestones)
+ create(:release,
+ project: project,
+ tag: tag_name,
+ name: name,
+ description: description,
+ released_at: Time.parse(released_at).utc,
+ created_at: Time.parse(created_at).utc,
+ milestones: milestones)
end
let(:mutation_name) { :release_update }
diff --git a/spec/requests/api/graphql/packages/composer_spec.rb b/spec/requests/api/graphql/packages/composer_spec.rb
index 9830623ede8..89c01d44771 100644
--- a/spec/requests/api/graphql/packages/composer_spec.rb
+++ b/spec/requests/api/graphql/packages/composer_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe 'package details' do
include GraphqlHelpers
include_context 'package details setup'
- let_it_be(:package) { create(:composer_package, project: project) }
+ let_it_be(:package) { create(:composer_package, :last_downloaded_at, project: project) }
let_it_be(:composer_json) { { name: 'name', type: 'type', license: 'license', version: 1 } }
let_it_be(:composer_metadatum) do
# we are forced to manually create the metadatum, without using the factory to force the sha to be a string
diff --git a/spec/requests/api/graphql/packages/conan_spec.rb b/spec/requests/api/graphql/packages/conan_spec.rb
index 5bd5a71bbeb..7ad85edecef 100644
--- a/spec/requests/api/graphql/packages/conan_spec.rb
+++ b/spec/requests/api/graphql/packages/conan_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe 'conan package details' do
include GraphqlHelpers
include_context 'package details setup'
- let_it_be(:package) { create(:conan_package, project: project) }
+ let_it_be(:package) { create(:conan_package, :last_downloaded_at, project: project) }
let(:metadata) { query_graphql_fragment('ConanMetadata') }
let(:package_files_metadata) { query_graphql_fragment('ConanFileMetadata') }
diff --git a/spec/requests/api/graphql/packages/helm_spec.rb b/spec/requests/api/graphql/packages/helm_spec.rb
index 1675b8faa23..79a589e2dc2 100644
--- a/spec/requests/api/graphql/packages/helm_spec.rb
+++ b/spec/requests/api/graphql/packages/helm_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe 'helm package details' do
include GraphqlHelpers
include_context 'package details setup'
- let_it_be(:package) { create(:helm_package, project: project) }
+ let_it_be(:package) { create(:helm_package, :last_downloaded_at, project: project) }
let(:package_files_metadata) { query_graphql_fragment('HelmFileMetadata') }
diff --git a/spec/requests/api/graphql/packages/maven_spec.rb b/spec/requests/api/graphql/packages/maven_spec.rb
index 9d59a922660..b7f39efcf73 100644
--- a/spec/requests/api/graphql/packages/maven_spec.rb
+++ b/spec/requests/api/graphql/packages/maven_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe 'maven package details' do
include GraphqlHelpers
include_context 'package details setup'
- let_it_be(:package) { create(:maven_package, project: project) }
+ let_it_be(:package) { create(:maven_package, :last_downloaded_at, project: project) }
let(:metadata) { query_graphql_fragment('MavenMetadata') }
@@ -31,7 +31,9 @@ RSpec.describe 'maven package details' do
context 'a versionless maven package' do
let_it_be(:maven_metadatum) { create(:maven_metadatum, app_version: nil) }
- let_it_be(:package) { create(:maven_package, project: project, version: nil, maven_metadatum: maven_metadatum) }
+ let_it_be(:package) do
+ create(:maven_package, :last_downloaded_at, project: project, version: nil, maven_metadatum: maven_metadatum)
+ end
subject { post_graphql(query, current_user: user) }
diff --git a/spec/requests/api/graphql/packages/nuget_spec.rb b/spec/requests/api/graphql/packages/nuget_spec.rb
index 87cffc67ce5..7de132d1574 100644
--- a/spec/requests/api/graphql/packages/nuget_spec.rb
+++ b/spec/requests/api/graphql/packages/nuget_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe 'nuget package details' do
include GraphqlHelpers
include_context 'package details setup'
- let_it_be(:package) { create(:nuget_package, :with_metadatum, project: project) }
+ let_it_be(:package) { create(:nuget_package, :last_downloaded_at, :with_metadatum, project: project) }
let_it_be(:dependency_link) { create(:packages_dependency_link, :with_nuget_metadatum, package: package) }
let(:metadata) { query_graphql_fragment('NugetMetadata') }
diff --git a/spec/requests/api/graphql/packages/package_spec.rb b/spec/requests/api/graphql/packages/package_spec.rb
index c28b37db5af..e9f82d66775 100644
--- a/spec/requests/api/graphql/packages/package_spec.rb
+++ b/spec/requests/api/graphql/packages/package_spec.rb
@@ -6,8 +6,8 @@ RSpec.describe 'package details' do
let_it_be_with_reload(:group) { create(:group) }
let_it_be_with_reload(:project) { create(:project, group: group) }
+ let_it_be_with_reload(:composer_package) { create(:composer_package, :last_downloaded_at, project: project) }
let_it_be(:user) { create(:user) }
- let_it_be(:composer_package) { create(:composer_package, project: project) }
let_it_be(:composer_json) { { name: 'name', type: 'type', license: 'license', version: 1 } }
let_it_be(:composer_metadatum) do
# we are forced to manually create the metadatum, without using the factory to force the sha to be a string
@@ -65,6 +65,17 @@ RSpec.describe 'package details' do
end
end
+ context 'with package without last_downloaded_at' do
+ before do
+ composer_package.update!(last_downloaded_at: nil)
+ subject
+ end
+
+ it 'matches the JSON schema' do
+ expect(package_details).to match_schema('graphql/packages/package_details')
+ end
+ end
+
context 'with package files pending destruction' do
let_it_be(:package_file) { create(:package_file, package: composer_package) }
let_it_be(:package_file_pending_destruction) { create(:package_file, :pending_destruction, package: composer_package) }
@@ -97,7 +108,7 @@ RSpec.describe 'package details' do
expect(graphql_data_at(:a, :name)).to eq(composer_package.name)
- expect_graphql_errors_to_include [/Package details can be requested only for one package at a time/]
+ expect_graphql_errors_to_include [/"package" field can be requested only for 1 Query\(s\) at a time./]
expect(graphql_data_at(:b)).to be(nil)
end
end
diff --git a/spec/requests/api/graphql/packages/pypi_spec.rb b/spec/requests/api/graphql/packages/pypi_spec.rb
index 0cc5bd2e3b2..c0e589f3597 100644
--- a/spec/requests/api/graphql/packages/pypi_spec.rb
+++ b/spec/requests/api/graphql/packages/pypi_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe 'pypi package details' do
include GraphqlHelpers
include_context 'package details setup'
- let_it_be(:package) { create(:pypi_package, project: project) }
+ let_it_be(:package) { create(:pypi_package, :last_downloaded_at, project: project) }
let(:metadata) { query_graphql_fragment('PypiMetadata') }
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
new file mode 100644
index 00000000000..cb5006ec8e4
--- /dev/null
+++ b/spec/requests/api/graphql/project/branch_protections/merge_access_levels_spec.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'getting merge access levels for a branch protection' do
+ include GraphqlHelpers
+
+ let_it_be(:current_user) { create(:user) }
+
+ let(:merge_access_level_data) { merge_access_levels_data[0] }
+
+ let(:merge_access_levels_data) do
+ graphql_data_at('project',
+ 'branchRules',
+ 'nodes',
+ 0,
+ 'branchProtection',
+ 'mergeAccessLevels',
+ 'nodes')
+ end
+
+ let(:project) { protected_branch.project }
+
+ let(:merge_access_levels_count) { protected_branch.merge_access_levels.size }
+
+ let(:variables) { { path: project.full_path } }
+
+ let(:fields) { all_graphql_fields_for('MergeAccessLevel') }
+
+ let(:query) do
+ <<~GQL
+ query($path: ID!) {
+ project(fullPath: $path) {
+ branchRules(first: 1) {
+ nodes {
+ branchProtection {
+ mergeAccessLevels {
+ nodes {
+ #{fields}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ GQL
+ end
+
+ context 'when the user does not have read_protected_branch abilities' do
+ let_it_be(:protected_branch) { create(:protected_branch) }
+
+ before do
+ project.add_guest(current_user)
+ post_graphql(query, current_user: current_user, variables: variables)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ it { expect(merge_access_levels_data).not_to be_present }
+ end
+
+ shared_examples 'merge access request' do
+ let(:merge_access) { protected_branch.merge_access_levels.first }
+
+ before do
+ project.add_maintainer(current_user)
+ post_graphql(query, current_user: current_user, variables: variables)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ it 'returns all merge access levels' do
+ expect(merge_access_levels_data.size).to eq(merge_access_levels_count)
+ end
+
+ it 'includes access_level' do
+ expect(merge_access_level_data['accessLevel'])
+ .to eq(merge_access.access_level)
+ end
+
+ it 'includes access_level_description' do
+ expect(merge_access_level_data['accessLevelDescription'])
+ .to eq(merge_access.humanize)
+ end
+ end
+
+ context 'when the user does have read_protected_branch abilities' do
+ let(:merge_access) { protected_branch.merge_access_levels.first }
+
+ context 'when no one has access' do
+ let_it_be(:protected_branch) { create(:protected_branch, :no_one_can_merge) }
+
+ it_behaves_like 'merge access request'
+ end
+
+ context 'when developers have access' do
+ let_it_be(:protected_branch) { create(:protected_branch, :developers_can_merge) }
+
+ it_behaves_like 'merge access request'
+ end
+
+ context 'when maintainers have access' do
+ let_it_be(:protected_branch) { create(:protected_branch, :maintainers_can_merge) }
+
+ it_behaves_like 'merge access request'
+ end
+ end
+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
new file mode 100644
index 00000000000..59f9c7d61cb
--- /dev/null
+++ b/spec/requests/api/graphql/project/branch_protections/push_access_levels_spec.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'getting push access levels for a branch protection' do
+ include GraphqlHelpers
+
+ let_it_be(:current_user) { create(:user) }
+
+ let(:push_access_level_data) { push_access_levels_data[0] }
+
+ let(:push_access_levels_data) do
+ graphql_data_at('project',
+ 'branchRules',
+ 'nodes',
+ 0,
+ 'branchProtection',
+ 'pushAccessLevels',
+ 'nodes')
+ end
+
+ let(:project) { protected_branch.project }
+
+ let(:push_access_levels_count) { protected_branch.push_access_levels.size }
+
+ let(:variables) { { path: project.full_path } }
+
+ let(:fields) { all_graphql_fields_for('PushAccessLevel'.classify) }
+
+ let(:query) do
+ <<~GQL
+ query($path: ID!) {
+ project(fullPath: $path) {
+ branchRules(first: 1) {
+ nodes {
+ branchProtection {
+ pushAccessLevels {
+ nodes {
+ #{fields}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ GQL
+ end
+
+ context 'when the user does not have read_protected_branch abilities' do
+ let_it_be(:protected_branch) { create(:protected_branch) }
+
+ before do
+ project.add_guest(current_user)
+ post_graphql(query, current_user: current_user, variables: variables)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ it { expect(push_access_levels_data).not_to be_present }
+ end
+
+ shared_examples 'push access request' do
+ let(:push_access) { protected_branch.push_access_levels.first }
+
+ before do
+ project.add_maintainer(current_user)
+ post_graphql(query, current_user: current_user, variables: variables)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ it 'returns all push access levels' do
+ expect(push_access_levels_data.size).to eq(push_access_levels_count)
+ end
+
+ it 'includes access_level' do
+ expect(push_access_level_data['accessLevel'])
+ .to eq(push_access.access_level)
+ end
+
+ it 'includes access_level_description' do
+ expect(push_access_level_data['accessLevelDescription'])
+ .to eq(push_access.humanize)
+ end
+ end
+
+ context 'when the user does have read_protected_branch abilities' do
+ let(:push_access) { protected_branch.push_access_levels.first }
+
+ context 'when no one has access' do
+ let_it_be(:protected_branch) { create(:protected_branch, :no_one_can_push) }
+
+ it_behaves_like 'push access request'
+ end
+
+ context 'when developers have access' do
+ let_it_be(:protected_branch) { create(:protected_branch, :developers_can_push) }
+
+ it_behaves_like 'push access request'
+ end
+
+ context 'when maintainers have access' do
+ let_it_be(:protected_branch) { create(:protected_branch, :maintainers_can_push) }
+
+ it_behaves_like 'push access request'
+ end
+ end
+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
new file mode 100644
index 00000000000..8a3f546ef95
--- /dev/null
+++ b/spec/requests/api/graphql/project/branch_rules/branch_protection_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'getting branch protection for a branch rule' do
+ include GraphqlHelpers
+
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:branch_rule) { create(:protected_branch) }
+ let_it_be(:project) { branch_rule.project }
+
+ let(:branch_protection_data) do
+ graphql_data_at('project', 'branchRules', 'nodes', 0, 'branchProtection')
+ end
+
+ let(:variables) { { path: project.full_path } }
+
+ let(:fields) { all_graphql_fields_for('BranchProtection') }
+
+ let(:query) do
+ <<~GQL
+ query($path: ID!) {
+ project(fullPath: $path) {
+ branchRules(first: 1) {
+ nodes {
+ branchProtection {
+ #{fields}
+ }
+ }
+ }
+ }
+ }
+ GQL
+ end
+
+ context 'when the user does not have read_protected_branch abilities' do
+ before do
+ project.add_guest(current_user)
+ post_graphql(query, current_user: current_user, variables: variables)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ it { expect(branch_protection_data).not_to be_present }
+ end
+
+ context 'when the user does have read_protected_branch abilities' do
+ before do
+ project.add_maintainer(current_user)
+ post_graphql(query, current_user: current_user, variables: variables)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ it 'includes allow_force_push' do
+ expect(branch_protection_data['allowForcePush']).to be_in([true, false])
+ expect(branch_protection_data['allowForcePush']).to eq(branch_rule.allow_force_push)
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/project/branch_rules_spec.rb b/spec/requests/api/graphql/project/branch_rules_spec.rb
new file mode 100644
index 00000000000..70fb37941e2
--- /dev/null
+++ b/spec/requests/api/graphql/project/branch_rules_spec.rb
@@ -0,0 +1,122 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'getting list of branch rules for a project' do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project, :repository, :public) }
+ let_it_be(:current_user) { create(:user) }
+ let_it_be(:branch_name_a) { 'branch_name_a' }
+ let_it_be(:branch_name_b) { 'wildcard-*' }
+ 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 } }
+
+ let(:fields) do
+ <<~QUERY
+ pageInfo {
+ hasNextPage
+ hasPreviousPage
+ }
+ edges {
+ cursor
+ node {
+ #{all_graphql_fields_for('branch_rules'.classify)}
+ }
+ }
+ QUERY
+ end
+
+ let(:query) do
+ <<~GQL
+ query($path: ID!, $n: Int, $cursor: String) {
+ project(fullPath: $path) {
+ branchRules(first: $n, after: $cursor) { #{fields} }
+ }
+ }
+ GQL
+ end
+
+ context 'when the user does not have read_protected_branch abilities' do
+ before do
+ project.add_guest(current_user)
+ post_graphql(query, current_user: current_user, variables: variables)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ it { expect(branch_rules_data).to be_empty }
+ end
+
+ context 'when the user does have read_protected_branch abilities' do
+ before do
+ project.add_maintainer(current_user)
+ post_graphql(query, current_user: current_user, variables: variables)
+ end
+
+ it_behaves_like 'a working graphql query'
+
+ it 'includes a name' do
+ expect(branch_rules_data.dig(0, 'node', 'name')).to be_present
+ end
+
+ it 'includes created_at and updated_at' do
+ expect(branch_rules_data.dig(0, 'node', 'createdAt')).to be_present
+ expect(branch_rules_data.dig(1, 'node', 'updatedAt')).to be_present
+ end
+
+ context 'when limiting the number of results' do
+ let(:branch_rule_limit) { 1 }
+ let(:variables) { { path: project.full_path, n: branch_rule_limit } }
+ let(:next_variables) do
+ { path: project.full_path, n: branch_rule_limit, cursor: last_cursor }
+ end
+
+ it_behaves_like 'a working graphql query' do
+ it 'only returns N branch_rules' do
+ expect(branch_rules_data.size).to eq(branch_rule_limit)
+ expect(has_next_page).to be_truthy
+ expect(has_prev_page).to be_falsey
+ post_graphql(query, current_user: current_user, variables: next_variables)
+ expect(branch_rules_data.size).to eq(branch_rule_limit)
+ expect(has_next_page).to be_falsey
+ expect(has_prev_page).to be_truthy
+ end
+ end
+
+ context 'when no limit is provided' do
+ let(:branch_rule_limit) { nil }
+
+ it 'returns all branch_rules' do
+ expect(branch_rules_data.size).to eq(branch_rules.size)
+ end
+ end
+ end
+ end
+
+ def pagination_info
+ graphql_data_at('project', 'branchRules', 'pageInfo')
+ end
+
+ def has_next_page
+ pagination_info['hasNextPage']
+ end
+
+ def has_prev_page
+ pagination_info['hasPreviousPage']
+ end
+
+ def last_cursor
+ branch_rules_data.last['cursor']
+ end
+end
diff --git a/spec/requests/api/graphql/project/deployment_spec.rb b/spec/requests/api/graphql/project/deployment_spec.rb
new file mode 100644
index 00000000000..e5ef7bcafbf
--- /dev/null
+++ b/spec/requests/api/graphql/project/deployment_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Project Deployment query' 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) } }
+ let_it_be(:environment) { create(:environment, project: project) }
+ let_it_be(:deployment) { create(:deployment, environment: environment, project: project) }
+
+ subject { GitlabSchema.execute(query, context: { current_user: user }).as_json }
+
+ let(:user) { developer }
+
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ deployment(iid: #{deployment.iid}) {
+ id
+ iid
+ ref
+ tag
+ sha
+ createdAt
+ updatedAt
+ finishedAt
+ status
+ }
+ }
+ }
+ )
+ end
+
+ it 'returns the deployment of the project' do
+ deployment_data = subject.dig('data', 'project', 'deployment')
+
+ expect(deployment_data['iid']).to eq(deployment.iid.to_s)
+ end
+
+ context 'when user is guest' do
+ let(:user) { guest }
+
+ it 'returns nothing' do
+ deployment_data = subject.dig('data', 'project', 'deployment')
+
+ expect(deployment_data).to be_nil
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/project/environments_spec.rb b/spec/requests/api/graphql/project/environments_spec.rb
new file mode 100644
index 00000000000..e5b6aebbf2c
--- /dev/null
+++ b/spec/requests/api/graphql/project/environments_spec.rb
@@ -0,0 +1,133 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Project Environments query' do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project, :private, :repository) }
+ let_it_be_with_refind(:production) { create(:environment, :production, project: project) }
+ let_it_be_with_refind(:staging) { create(:environment, :staging, project: project) }
+ let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
+
+ subject { post_graphql(query, current_user: user) }
+
+ let(:user) { developer }
+
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{production.name}") {
+ slug
+ createdAt
+ updatedAt
+ autoStopAt
+ autoDeleteAt
+ tier
+ environmentType
+ }
+ }
+ }
+ )
+ end
+
+ it 'returns the specified fields of the environment', :aggregate_failures do
+ production.update!(auto_stop_at: 1.day.ago, auto_delete_at: 2.days.ago, environment_type: 'review')
+
+ subject
+
+ environment_data = graphql_data.dig('project', 'environment')
+ expect(environment_data['slug']).to eq(production.slug)
+ expect(environment_data['createdAt']).to eq(production.created_at.iso8601)
+ expect(environment_data['updatedAt']).to eq(production.updated_at.iso8601)
+ expect(environment_data['autoStopAt']).to eq(production.auto_stop_at.iso8601)
+ expect(environment_data['autoDeleteAt']).to eq(production.auto_delete_at.iso8601)
+ expect(environment_data['tier']).to eq(production.tier.upcase)
+ expect(environment_data['environmentType']).to eq(production.environment_type)
+ end
+
+ describe 'last deployments of environments' do
+ ::Deployment.statuses.each do |status, _|
+ let_it_be(:"production_#{status}_deployment") do
+ create(:deployment, status.to_sym, environment: production, project: project)
+ end
+
+ let_it_be(:"staging_#{status}_deployment") do
+ create(:deployment, status.to_sym, environment: staging, project: project)
+ end
+ end
+
+ let(:query) do
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environments {
+ nodes {
+ name
+ lastSuccessDeployment: lastDeployment(status: SUCCESS) {
+ iid
+ }
+ lastRunningDeployment: lastDeployment(status: RUNNING) {
+ iid
+ }
+ lastBlockedDeployment: lastDeployment(status: BLOCKED) {
+ iid
+ }
+ }
+ }
+ }
+ }
+ )
+ end
+
+ it 'returns all last deployments of the environment' do
+ subject
+
+ environments_data = graphql_data_at(:project, :environments, :nodes)
+
+ environments_data.each do |environment_data|
+ name = environment_data['name']
+ success_deployment = public_send(:"#{name}_success_deployment")
+ running_deployment = public_send(:"#{name}_running_deployment")
+ blocked_deployment = public_send(:"#{name}_blocked_deployment")
+
+ expect(environment_data['lastSuccessDeployment']['iid']).to eq(success_deployment.iid.to_s)
+ expect(environment_data['lastRunningDeployment']['iid']).to eq(running_deployment.iid.to_s)
+ expect(environment_data['lastBlockedDeployment']['iid']).to eq(blocked_deployment.iid.to_s)
+ end
+ end
+
+ it 'executes the same number of queries in single environment and multiple environments' do
+ single_environment_query =
+ %(
+ query {
+ project(fullPath: "#{project.full_path}") {
+ environment(name: "#{production.name}") {
+ name
+ lastSuccessDeployment: lastDeployment(status: SUCCESS) {
+ iid
+ }
+ lastRunningDeployment: lastDeployment(status: RUNNING) {
+ iid
+ }
+ lastBlockedDeployment: lastDeployment(status: BLOCKED) {
+ iid
+ }
+ }
+ }
+ }
+ )
+
+ baseline = ActiveRecord::QueryRecorder.new do
+ run_with_clean_state(single_environment_query, context: { current_user: user })
+ end
+
+ multi = ActiveRecord::QueryRecorder.new do
+ run_with_clean_state(query, context: { current_user: user })
+ end
+
+ expect(multi).not_to exceed_query_limit(baseline)
+ end
+ end
+end
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 8cda61f0628..0444ce43c22 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
@@ -11,14 +11,14 @@ RSpec.describe 'Query.project(fullPath).issue(iid).designCollection.version(sha)
let_it_be(:developer) { create(:user) }
let_it_be(:stranger) { create(:user) }
let_it_be(:old_version) do
- create(:design_version, issue: issue,
- created_designs: create_list(:design, 3, issue: issue))
+ create(:design_version, issue: issue, created_designs: create_list(:design, 3, issue: issue))
end
let_it_be(:version) do
- create(:design_version, issue: issue,
- modified_designs: old_version.designs,
- created_designs: create_list(:design, 2, issue: issue))
+ create(:design_version,
+ issue: issue,
+ modified_designs: old_version.designs,
+ created_designs: create_list(:design, 2, issue: issue))
end
let(:current_user) { developer }
diff --git a/spec/requests/api/graphql/project/issues_spec.rb b/spec/requests/api/graphql/project/issues_spec.rb
index 596e023a027..28282860416 100644
--- a/spec/requests/api/graphql/project/issues_spec.rb
+++ b/spec/requests/api/graphql/project/issues_spec.rb
@@ -27,14 +27,6 @@ RSpec.describe 'getting an issue list for a project' do
QUERY
end
- let(:query) do
- graphql_query_for(
- 'project',
- { 'fullPath' => project.full_path },
- query_graphql_field('issues', issue_filter_params, fields)
- )
- end
-
it_behaves_like 'a working graphql query' do
before do
post_graphql(query, current_user: current_user)
@@ -89,6 +81,14 @@ RSpec.describe 'getting an issue list for a project' do
end
end
+ context 'when filtering by search' do
+ it_behaves_like 'query with a search term' do
+ let(:issuable_data) { issues_data }
+ let(:user) { current_user }
+ let_it_be(:issuable) { create(:issue, project: project, description: 'bar') }
+ end
+ end
+
context 'when limiting the number of results' do
let(:query) do
<<~GQL
@@ -301,7 +301,7 @@ RSpec.describe 'getting an issue list for a project' do
let_it_be(:relative_issue5) { create(:issue, project: sort_project, relative_position: 500) }
context 'when ascending' do
- it_behaves_like 'sorted paginated query' 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
@@ -679,4 +679,12 @@ RSpec.describe 'getting an issue list for a project' do
def issues_ids
graphql_dig_at(issues_data, :node, :id)
end
+
+ def query(params = issue_filter_params)
+ graphql_query_for(
+ 'project',
+ { 'fullPath' => project.full_path },
+ query_graphql_field('issues', params, fields)
+ )
+ end
end
diff --git a/spec/requests/api/graphql/project/job_spec.rb b/spec/requests/api/graphql/project/job_spec.rb
new file mode 100644
index 00000000000..6edd4cf753f
--- /dev/null
+++ b/spec/requests/api/graphql/project/job_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Query.project.job' do
+ include GraphqlHelpers
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+
+ let(:job) { create(:ci_build, project: project, name: 'GQL test job') }
+
+ let(:query) do
+ <<~QUERY
+ {
+ project(fullPath: "#{project.full_path}") {
+ job(id: "#{job.to_global_id}") {
+ name
+ }
+ }
+ }
+ QUERY
+ end
+
+ context 'when the user can read jobs on the project' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'returns the job that matches the given ID' do
+ post_graphql(query, current_user: user)
+
+ expect(graphql_data.dig('project', 'job', 'name')).to eq('GQL test job')
+ end
+
+ context 'when no job matches the given ID' do
+ let(:job) { create(:ci_build, project: create(:project), name: 'Job from another project') }
+
+ it 'returns null' do
+ post_graphql(query, current_user: user)
+
+ expect(graphql_data.dig('project', 'job')).to be_nil
+ end
+ end
+ end
+
+ context 'when the user cannot read jobs on the project' do
+ it 'returns null' do
+ post_graphql(query, current_user: user)
+
+ expect(graphql_data.dig('project', 'job')).to be_nil
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/project/merge_request_spec.rb b/spec/requests/api/graphql/project/merge_request_spec.rb
index d2f34080be3..6a59df81405 100644
--- a/spec/requests/api/graphql/project/merge_request_spec.rb
+++ b/spec/requests/api/graphql/project/merge_request_spec.rb
@@ -365,7 +365,7 @@ RSpec.describe 'getting merge request information nested in a project' do
expect(interaction_data).to contain_exactly a_hash_including(
'canMerge' => false,
'canUpdate' => can_update,
- 'reviewState' => attention_requested,
+ 'reviewState' => unreviewed,
'reviewed' => false,
'approved' => false
)
@@ -398,8 +398,8 @@ RSpec.describe 'getting merge request information nested in a project' do
describe 'scalability' do
let_it_be(:other_users) { create_list(:user, 3) }
- let(:attention_requested) do
- { 'reviewState' => 'ATTENTION_REQUESTED' }
+ let(:unreviewed) do
+ { 'reviewState' => 'UNREVIEWED' }
end
let(:reviewed) do
@@ -425,15 +425,15 @@ RSpec.describe 'getting merge request information nested in a project' do
other_users.each do |user|
assign_user(user)
- merge_request.merge_request_reviewers.find_or_create_by!(reviewer: user, state: :attention_requested)
+ merge_request.merge_request_reviewers.find_or_create_by!(reviewer: user)
end
expect { post_graphql(query) }.not_to exceed_query_limit(baseline)
expect(interaction_data).to contain_exactly(
- include(attention_requested),
- include(attention_requested),
- include(attention_requested),
+ include(unreviewed),
+ include(unreviewed),
+ include(unreviewed),
include(reviewed)
)
end
@@ -462,17 +462,17 @@ RSpec.describe 'getting merge request information nested in a project' do
it_behaves_like 'when requesting information about MR interactions' do
let(:field) { :reviewers }
- let(:attention_requested) { 'ATTENTION_REQUESTED' }
+ let(:unreviewed) { 'UNREVIEWED' }
let(:can_update) { false }
def assign_user(user)
- merge_request.merge_request_reviewers.create!(reviewer: user, state: :attention_requested)
+ merge_request.merge_request_reviewers.create!(reviewer: user)
end
end
it_behaves_like 'when requesting information about MR interactions' do
let(:field) { :assignees }
- let(:attention_requested) { nil }
+ let(:unreviewed) { nil }
let(:can_update) { true } # assignees can update MRs
def assign_user(user)
diff --git a/spec/requests/api/graphql/project/pipeline_spec.rb b/spec/requests/api/graphql/project/pipeline_spec.rb
index 08c6a2d9927..41915d3cdee 100644
--- a/spec/requests/api/graphql/project/pipeline_spec.rb
+++ b/spec/requests/api/graphql/project/pipeline_spec.rb
@@ -111,7 +111,7 @@ RSpec.describe 'getting pipeline information nested in a project' do
name: build_job.name,
pipeline: pipeline,
stage_idx: 0,
- stage: build_job.stage)
+ stage: build_job.stage_name)
end
let(:fields) do
diff --git a/spec/requests/api/graphql/project/terraform/state_spec.rb b/spec/requests/api/graphql/project/terraform/state_spec.rb
index 8f2d2cffef2..5e207ec0963 100644
--- a/spec/requests/api/graphql/project/terraform/state_spec.rb
+++ b/spec/requests/api/graphql/project/terraform/state_spec.rb
@@ -60,17 +60,17 @@ RSpec.describe 'query a single terraform state' do
expect(data).to match a_graphql_entity_for(
terraform_state,
:name,
- 'lockedAt' => terraform_state.locked_at.iso8601,
- 'createdAt' => terraform_state.created_at.iso8601,
- 'updatedAt' => terraform_state.updated_at.iso8601,
- 'lockedByUser' => a_graphql_entity_for(terraform_state.locked_by_user),
+ 'lockedAt' => terraform_state.locked_at.iso8601,
+ 'createdAt' => terraform_state.created_at.iso8601,
+ 'updatedAt' => terraform_state.updated_at.iso8601,
+ 'lockedByUser' => a_graphql_entity_for(terraform_state.locked_by_user),
'latestVersion' => a_graphql_entity_for(
latest_version,
- 'serial' => eq(latest_version.version),
- 'createdAt' => eq(latest_version.created_at.iso8601),
- 'updatedAt' => eq(latest_version.updated_at.iso8601),
+ 'serial' => eq(latest_version.version),
+ 'createdAt' => eq(latest_version.created_at.iso8601),
+ 'updatedAt' => eq(latest_version.updated_at.iso8601),
'createdByUser' => a_graphql_entity_for(latest_version.created_by_user),
- 'job' => { 'name' => eq(latest_version.build.name) }
+ 'job' => { 'name' => eq(latest_version.build.name) }
)
)
end
diff --git a/spec/requests/api/graphql/project/terraform/states_spec.rb b/spec/requests/api/graphql/project/terraform/states_spec.rb
index a7ec6f69776..cc3660bcc6b 100644
--- a/spec/requests/api/graphql/project/terraform/states_spec.rb
+++ b/spec/requests/api/graphql/project/terraform/states_spec.rb
@@ -64,18 +64,18 @@ RSpec.describe 'query terraform states' do
expect(data['nodes']).to contain_exactly a_graphql_entity_for(
terraform_state, :name,
- 'lockedAt' => terraform_state.locked_at.iso8601,
- 'createdAt' => terraform_state.created_at.iso8601,
- 'updatedAt' => terraform_state.updated_at.iso8601,
- 'lockedByUser' => a_graphql_entity_for(terraform_state.locked_by_user),
+ 'lockedAt' => terraform_state.locked_at.iso8601,
+ 'createdAt' => terraform_state.created_at.iso8601,
+ 'updatedAt' => terraform_state.updated_at.iso8601,
+ 'lockedByUser' => a_graphql_entity_for(terraform_state.locked_by_user),
'latestVersion' => a_graphql_entity_for(
latest_version,
- 'serial' => eq(latest_version.version),
- 'downloadPath' => eq(download_path),
- 'createdAt' => eq(latest_version.created_at.iso8601),
- 'updatedAt' => eq(latest_version.updated_at.iso8601),
+ 'serial' => eq(latest_version.version),
+ 'downloadPath' => eq(download_path),
+ 'createdAt' => eq(latest_version.created_at.iso8601),
+ 'updatedAt' => eq(latest_version.updated_at.iso8601),
'createdByUser' => a_graphql_entity_for(latest_version.created_by_user),
- 'job' => { 'name' => eq(latest_version.build.name) }
+ 'job' => { 'name' => eq(latest_version.build.name) }
)
)
end
diff --git a/spec/requests/api/graphql/project/work_items_spec.rb b/spec/requests/api/graphql/project/work_items_spec.rb
index 6ef28392b8b..69f8d1cac74 100644
--- a/spec/requests/api/graphql/project/work_items_spec.rb
+++ b/spec/requests/api/graphql/project/work_items_spec.rb
@@ -10,7 +10,10 @@ RSpec.describe 'getting an work item list for a project' do
let_it_be(:current_user) { create(:user) }
let_it_be(:item1) { create(:work_item, project: project, discussion_locked: true, title: 'item1') }
- let_it_be(:item2) { create(:work_item, project: project, title: 'item2') }
+ let_it_be(:item2) do
+ create(:work_item, project: project, title: 'item2', last_edited_by: current_user, last_edited_at: 1.day.ago)
+ end
+
let_it_be(:confidential_item) { create(:work_item, confidential: true, project: project, title: 'item3') }
let_it_be(:other_item) { create(:work_item) }
@@ -27,14 +30,6 @@ RSpec.describe 'getting an work item list for a project' do
QUERY
end
- let(:query) do
- graphql_query_for(
- 'project',
- { 'fullPath' => project.full_path },
- query_graphql_field('workItems', item_filter_params, fields)
- )
- end
-
it_behaves_like 'a working graphql query' do
before do
post_graphql(query, current_user: current_user)
@@ -83,6 +78,48 @@ RSpec.describe 'getting an work item list for a project' do
end
end
+ context 'when fetching description edit information' do
+ let(:fields) do
+ <<~GRAPHQL
+ nodes {
+ widgets {
+ type
+ ... on WorkItemWidgetDescription {
+ edited
+ lastEditedAt
+ lastEditedBy {
+ webPath
+ username
+ }
+ }
+ }
+ }
+ GRAPHQL
+ end
+
+ it 'avoids 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
+ expect_graphql_errors_to_be_empty
+
+ create_list(:work_item, 3, :last_edited_by_user, last_edited_at: 1.week.ago, project: project)
+
+ expect_graphql_errors_to_be_empty
+ expect { post_graphql(query, current_user: current_user) }.not_to exceed_query_limit(control)
+ end
+ end
+
+ context 'when filtering by search' do
+ it_behaves_like 'query with a search term' do
+ let(:issuable_data) { items_data }
+ let(:user) { current_user }
+ let_it_be(:issuable) { create(:work_item, project: project, description: 'bar') }
+ end
+ end
+
describe 'sorting and pagination' do
let(:data_path) { [:project, :work_items] }
@@ -118,4 +155,12 @@ RSpec.describe 'getting an work item list for a project' do
def item_ids
graphql_dig_at(items_data, :node, :id)
end
+
+ def query(params = item_filter_params)
+ graphql_query_for(
+ 'project',
+ { 'fullPath' => project.full_path },
+ query_graphql_field('workItems', params, fields)
+ )
+ end
end
diff --git a/spec/requests/api/graphql/query_spec.rb b/spec/requests/api/graphql/query_spec.rb
index 4aa9c4b8254..359c599cd3a 100644
--- a/spec/requests/api/graphql/query_spec.rb
+++ b/spec/requests/api/graphql/query_spec.rb
@@ -108,8 +108,8 @@ RSpec.describe 'Query' do
design_at_version,
'filename' => design_at_version.design.filename,
'version' => a_graphql_entity_for(version, :sha),
- 'design' => a_graphql_entity_for(design),
- 'issue' => { 'title' => issue.title, 'iid' => issue.iid.to_s },
+ 'design' => a_graphql_entity_for(design),
+ 'issue' => { 'title' => issue.title, 'iid' => issue.iid.to_s },
'project' => a_graphql_entity_for(project, :full_path)
)
end
diff --git a/spec/requests/api/graphql/work_item_spec.rb b/spec/requests/api/graphql/work_item_spec.rb
index 34644e5893a..e4bb4109c76 100644
--- a/spec/requests/api/graphql/work_item_spec.rb
+++ b/spec/requests/api/graphql/work_item_spec.rb
@@ -14,7 +14,10 @@ RSpec.describe 'Query.work_item(id)' do
project: project,
description: '- List item',
start_date: Date.today,
- due_date: 1.week.from_now
+ due_date: 1.week.from_now,
+ created_at: 1.week.ago,
+ last_edited_at: 1.day.ago,
+ last_edited_by: guest
)
end
@@ -67,6 +70,12 @@ RSpec.describe 'Query.work_item(id)' do
... on WorkItemWidgetDescription {
description
descriptionHtml
+ edited
+ lastEditedBy {
+ webPath
+ username
+ }
+ lastEditedAt
}
}
GRAPHQL
@@ -79,7 +88,13 @@ RSpec.describe 'Query.work_item(id)' do
hash_including(
'type' => 'DESCRIPTION',
'description' => work_item.description,
- 'descriptionHtml' => ::MarkupHelper.markdown_field(work_item, :description, {})
+ 'descriptionHtml' => ::MarkupHelper.markdown_field(work_item, :description, {}),
+ 'edited' => true,
+ 'lastEditedAt' => work_item.last_edited_at.iso8601,
+ 'lastEditedBy' => {
+ 'webPath' => "/#{guest.full_path}",
+ 'username' => guest.username
+ }
)
)
)
diff --git a/spec/requests/api/group_export_spec.rb b/spec/requests/api/group_export_spec.rb
index bda46f85140..83c34204c78 100644
--- a/spec/requests/api/group_export_spec.rb
+++ b/spec/requests/api/group_export_spec.rb
@@ -34,6 +34,7 @@ RSpec.describe API::GroupExport do
before do
allow_next_instance_of(Gitlab::ApplicationRateLimiter::BaseStrategy) do |strategy|
allow(strategy).to receive(:increment).and_return(0)
+ allow(strategy).to receive(:read).and_return(0)
end
upload.export_file = fixture_file_upload('spec/fixtures/group_export.tar.gz', "`/tar.gz")
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index bc37f8e4655..6169bc9b2a2 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe API::Groups do
include GroupAPIHelpers
include UploadHelpers
+ include WorkhorseHelpers
let_it_be(:user1) { create(:user, can_create_group: false) }
let_it_be(:user2) { create(:user) }
@@ -540,9 +541,9 @@ RSpec.describe API::Groups do
# Returns a Hash of visibility_level => Project pairs
def add_projects_to_group(group, share_with: nil)
projects = {
- public: create(:project, :public, namespace: group),
+ public: create(:project, :public, namespace: group),
internal: create(:project, :internal, namespace: group),
- private: create(:project, :private, namespace: group)
+ private: create(:project, :private, namespace: group)
}
if share_with
@@ -872,21 +873,31 @@ RSpec.describe API::Groups do
group_param = {
avatar: fixture_file_upload(file_path)
}
- put api("/groups/#{group1.id}", user1), params: group_param
+ workhorse_form_with_file(
+ api("/groups/#{group1.id}", user1),
+ method: :put,
+ file_key: :avatar,
+ params: group_param
+ )
end
end
context 'when authenticated as the group owner' do
it 'updates the group' do
- put api("/groups/#{group1.id}", user1), params: {
- name: new_group_name,
- request_access_enabled: true,
- project_creation_level: "noone",
- subgroup_creation_level: "maintainer",
- default_branch_protection: ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS,
- prevent_sharing_groups_outside_hierarchy: true,
- avatar: fixture_file_upload(file_path)
- }
+ workhorse_form_with_file(
+ api("/groups/#{group1.id}", user1),
+ method: :put,
+ file_key: :avatar,
+ params: {
+ name: new_group_name,
+ request_access_enabled: true,
+ project_creation_level: "noone",
+ subgroup_creation_level: "maintainer",
+ default_branch_protection: ::Gitlab::Access::MAINTAINER_PROJECT_ACCESS,
+ prevent_sharing_groups_outside_hierarchy: true,
+ avatar: fixture_file_upload(file_path)
+ }
+ )
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['name']).to eq(new_group_name)
@@ -912,6 +923,16 @@ RSpec.describe API::Groups do
expect(json_response['prevent_sharing_groups_outside_hierarchy']).to eq(true)
end
+ it 'removes the group avatar' do
+ put api("/groups/#{group1.id}", user1), params: { avatar: '' }
+
+ aggregate_failures "testing response" do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['avatar_url']).to be_nil
+ expect(group1.reload.avatar_url).to be_nil
+ end
+ end
+
it 'does not update visibility_level if it is restricted' do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::INTERNAL])
@@ -1787,7 +1808,12 @@ RSpec.describe API::Groups do
attrs[:avatar] = fixture_file_upload(file_path)
end
- post api("/groups", user3), params: params
+ workhorse_form_with_file(
+ api('/groups', user3),
+ method: :post,
+ file_key: :avatar,
+ params: params
+ )
end
end
@@ -2029,6 +2055,90 @@ RSpec.describe API::Groups do
end
end
+ describe 'GET /groups/:id/transfer_locations' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:source_group) { create(:group, :private) }
+
+ let(:params) { {} }
+
+ subject(:request) do
+ get api("/groups/#{source_group.id}/transfer_locations", user), params: params
+ end
+
+ context 'when the user has rights to transfer the group' do
+ let_it_be(:guest_group) { create(:group) }
+ let_it_be(:maintainer_group) { create(:group, name: 'maintainer group', path: 'maintainer-group') }
+ let_it_be(:owner_group_1) { create(:group, name: 'owner group', path: 'owner-group') }
+ let_it_be(:owner_group_2) { create(:group, name: 'gitlab group', path: 'gitlab-group') }
+ let_it_be(:shared_with_group_where_direct_owner_as_owner) { create(:group) }
+
+ before do
+ source_group.add_owner(user)
+ guest_group.add_guest(user)
+ maintainer_group.add_maintainer(user)
+ owner_group_1.add_owner(user)
+ owner_group_2.add_owner(user)
+ create(:group_group_link, :owner,
+ shared_with_group: owner_group_1,
+ shared_group: shared_with_group_where_direct_owner_as_owner
+ )
+ end
+
+ it 'returns 200' do
+ request
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ end
+
+ it 'only includes groups where the user has permissions to transfer a group to' do
+ request
+
+ expect(group_ids_from_response).to contain_exactly(
+ owner_group_1.id,
+ owner_group_2.id,
+ shared_with_group_where_direct_owner_as_owner.id
+ )
+ end
+
+ context 'with search' do
+ let(:params) { { search: 'gitlab' } }
+
+ it 'includes groups where the user has permissions to transfer a group to, matching the search term' do
+ request
+
+ expect(group_ids_from_response).to contain_exactly(owner_group_2.id)
+ end
+ end
+
+ def group_ids_from_response
+ json_response.map { |group| group['id'] }
+ end
+ end
+
+ context 'when the user does not have permissions to transfer the group' do
+ before do
+ source_group.add_developer(user)
+ end
+
+ it 'returns 403' do
+ request
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'for an anonymous user' do
+ let_it_be(:user) { nil }
+
+ it 'returns 404' do
+ request
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+
describe 'POST /groups/:id/transfer' do
let_it_be(:user) { create(:user) }
let_it_be_with_reload(:new_parent_group) { create(:group, :private) }
diff --git a/spec/requests/api/import_github_spec.rb b/spec/requests/api/import_github_spec.rb
index 7de72de3940..d2fa3dabe69 100644
--- a/spec/requests/api/import_github_spec.rb
+++ b/spec/requests/api/import_github_spec.rb
@@ -13,15 +13,15 @@ RSpec.describe API::ImportGithub do
let(:provider_username) { user.username }
let(:provider_user) { double('provider', login: provider_username) }
let(:provider_repo) do
- double('provider',
+ {
name: 'vim',
full_name: "#{provider_username}/vim",
owner: double('provider', login: provider_username),
description: 'provider',
private: false,
clone_url: 'https://fake.url/vim.git',
- has_wiki?: true
- )
+ has_wiki: true
+ }
end
before do
@@ -48,7 +48,7 @@ RSpec.describe API::ImportGithub do
it 'returns 201 response when the project is imported successfully' do
allow(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, type: provider, **access_params)
+ .to receive(:new).with(provider_repo, provider_repo[:name], user.namespace, user, type: provider, **access_params)
.and_return(double(execute: project))
post api("/import/github", user), params: {
@@ -63,7 +63,7 @@ RSpec.describe API::ImportGithub do
it 'returns 201 response when the project is imported successfully from GHE' do
allow(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, type: provider, **access_params)
+ .to receive(:new).with(provider_repo, provider_repo[:name], user.namespace, user, type: provider, **access_params)
.and_return(double(execute: project))
post api("/import/github", user), params: {
diff --git a/spec/requests/api/integrations/slack/events_spec.rb b/spec/requests/api/integrations/slack/events_spec.rb
deleted file mode 100644
index 176e9eded31..00000000000
--- a/spec/requests/api/integrations/slack/events_spec.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe API::Integrations::Slack::Events do
- describe 'POST /integrations/slack/events' do
- let(:params) { {} }
- let(:headers) do
- {
- ::API::Integrations::Slack::Request::VERIFICATION_TIMESTAMP_HEADER => Time.current.to_i.to_s,
- ::API::Integrations::Slack::Request::VERIFICATION_SIGNATURE_HEADER => 'mock_verified_signature'
- }
- end
-
- before do
- allow(ActiveSupport::SecurityUtils).to receive(:secure_compare) do |signature|
- signature == 'mock_verified_signature'
- end
-
- stub_application_setting(slack_app_signing_secret: 'mock_key')
- end
-
- subject { post api('/integrations/slack/events'), params: params, headers: headers }
-
- shared_examples 'an unauthorized request' do
- specify do
- subject
-
- expect(response).to have_gitlab_http_status(:unauthorized)
- end
- end
-
- shared_examples 'a successful request that generates a tracked error' do
- specify do
- expect(Gitlab::ErrorTracking).to receive(:track_exception).once
-
- subject
-
- expect(response).to have_gitlab_http_status(:no_content)
- expect(response.body).to be_empty
- end
- end
-
- context 'when the slack_app_signing_secret setting is not set' do
- before do
- stub_application_setting(slack_app_signing_secret: nil)
- end
-
- it_behaves_like 'an unauthorized request'
- end
-
- context 'when the timestamp header has expired' do
- before do
- headers[::API::Integrations::Slack::Request::VERIFICATION_TIMESTAMP_HEADER] = 5.minutes.ago.to_i.to_s
- end
-
- it_behaves_like 'an unauthorized request'
- end
-
- context 'when the timestamp header is missing' do
- before do
- headers.delete(::API::Integrations::Slack::Request::VERIFICATION_TIMESTAMP_HEADER)
- end
-
- it_behaves_like 'an unauthorized request'
- end
-
- context 'when the signature header is missing' do
- before do
- headers.delete(::API::Integrations::Slack::Request::VERIFICATION_SIGNATURE_HEADER)
- end
-
- it_behaves_like 'an unauthorized request'
- end
-
- context 'when the signature is not verified' do
- before do
- headers[::API::Integrations::Slack::Request::VERIFICATION_SIGNATURE_HEADER] = 'unverified_signature'
- end
-
- it_behaves_like 'an unauthorized request'
- end
-
- context 'when type param is missing' do
- it_behaves_like 'a successful request that generates a tracked error'
- end
-
- context 'when type param is unknown' do
- let(:params) do
- { type: 'unknown_type' }
- end
-
- it_behaves_like 'a successful request that generates a tracked error'
- end
-
- context 'when type param is url_verification' do
- let(:params) do
- {
- type: 'url_verification',
- challenge: '3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P'
- }
- end
-
- it 'responds in-request with the challenge' do
- subject
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response).to eq({ 'challenge' => '3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P' })
- end
- end
- end
-end
diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index e100684018a..1f6c241b3f5 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -3,6 +3,7 @@
require 'spec_helper'
RSpec.describe API::Internal::Base do
+ include GitlabShellHelpers
include APIInternalBaseHelpers
let_it_be(:user, reload: true) { create(:user) }
@@ -17,10 +18,14 @@ RSpec.describe API::Internal::Base do
let(:snippet_changes) { "#{TestEnv::BRANCH_SHA['snippet/single-file']} #{TestEnv::BRANCH_SHA['snippet/edit-file']} refs/heads/snippet/edit-file" }
describe "GET /internal/check" do
+ def perform_request(headers: gitlab_shell_internal_api_request_header)
+ get api("/internal/check"), headers: headers
+ end
+
it do
expect_any_instance_of(Redis).to receive(:ping).and_return('PONG')
- get api("/internal/check"), params: { secret_token: secret_token }
+ perform_request
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['api_version']).to eq(API::API.version)
@@ -30,24 +35,57 @@ RSpec.describe API::Internal::Base do
it 'returns false for field `redis` when redis is unavailable' do
expect_any_instance_of(Redis).to receive(:ping).and_raise(Errno::ENOENT)
- get api("/internal/check"), params: { secret_token: secret_token }
+ perform_request
expect(json_response['redis']).to be(false)
end
context 'authenticating' do
- it 'authenticates using a header' do
- get api("/internal/check"),
- headers: { API::Helpers::GITLAB_SHARED_SECRET_HEADER => Base64.encode64(secret_token) }
+ it 'authenticates using a jwt token in a header' do
+ perform_request
expect(response).to have_gitlab_http_status(:ok)
end
- it 'returns 401 when no credentials provided' do
- get(api("/internal/check"))
+ it 'returns 401 when jwt token is expired' do
+ headers = gitlab_shell_internal_api_request_header
+
+ travel_to(2.minutes.since) do
+ perform_request(headers: headers)
+ end
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+
+ it 'returns 401 when jwt issuer is not Gitlab-Shell' do
+ perform_request(headers: gitlab_shell_internal_api_request_header(issuer: "gitlab-workhorse"))
expect(response).to have_gitlab_http_status(:unauthorized)
end
+
+ it 'returns 401 when jwt token is not provided, even if plain secret is provided' do
+ perform_request(headers: { API::Helpers::GITLAB_SHARED_SECRET_HEADER => Base64.encode64(secret_token) })
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+
+ context 'when gitlab_shell_jwt_token is disabled' do
+ before do
+ stub_feature_flags(gitlab_shell_jwt_token: false)
+ end
+
+ it 'authenticates using a header' do
+ perform_request(headers: { API::Helpers::GITLAB_SHARED_SECRET_HEADER => Base64.encode64(secret_token) })
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
+
+ it 'returns 401 when no credentials provided' do
+ get(api("/internal/check"))
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
end
end
@@ -56,10 +94,8 @@ RSpec.describe API::Internal::Base do
subject do
post api('/internal/two_factor_recovery_codes'),
- params: {
- secret_token: secret_token,
- key_id: key_id
- }
+ params: { key_id: key_id },
+ headers: gitlab_shell_internal_api_request_header
end
it_behaves_like 'actor key validations'
@@ -105,10 +141,8 @@ RSpec.describe API::Internal::Base do
subject do
post api('/internal/personal_access_token'),
- params: {
- secret_token: secret_token,
- key_id: key_id
- }
+ params: { key_id: key_id },
+ headers: gitlab_shell_internal_api_request_header
end
it_behaves_like 'actor key validations'
@@ -126,10 +160,8 @@ RSpec.describe API::Internal::Base do
it 'returns an error message when given an non existent user' do
post api('/internal/personal_access_token'),
- params: {
- secret_token: secret_token,
- user_id: 0
- }
+ params: { user_id: 0 },
+ headers: gitlab_shell_internal_api_request_header
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq("Could not find the given user")
@@ -137,10 +169,8 @@ RSpec.describe API::Internal::Base do
it 'returns an error message when no name parameter is received' do
post api('/internal/personal_access_token'),
- params: {
- secret_token: secret_token,
- key_id: key.id
- }
+ params: { key_id: key.id },
+ headers: gitlab_shell_internal_api_request_header
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq("No token name specified")
@@ -148,11 +178,8 @@ RSpec.describe API::Internal::Base do
it 'returns an error message when no scopes parameter is received' do
post api('/internal/personal_access_token'),
- params: {
- secret_token: secret_token,
- key_id: key.id,
- name: 'newtoken'
- }
+ params: { key_id: key.id, name: 'newtoken' },
+ headers: gitlab_shell_internal_api_request_header
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq("No token scopes specified")
@@ -161,12 +188,12 @@ RSpec.describe API::Internal::Base do
it 'returns an error message when expires_at contains an invalid date' do
post api('/internal/personal_access_token'),
params: {
- secret_token: secret_token,
- key_id: key.id,
+ key_id: key.id,
name: 'newtoken',
scopes: ['api'],
expires_at: 'invalid-date'
- }
+ },
+ headers: gitlab_shell_internal_api_request_header
expect(json_response['success']).to be_falsey
expect(json_response['message']).to eq("Invalid token expiry date: 'invalid-date'")
@@ -175,11 +202,11 @@ RSpec.describe API::Internal::Base do
it 'returns an error message when it receives an invalid scope' do
post api('/internal/personal_access_token'),
params: {
- secret_token: secret_token,
- key_id: key.id,
+ key_id: key.id,
name: 'newtoken',
scopes: %w(read_api badscope read_repository)
- }
+ },
+ headers: gitlab_shell_internal_api_request_header
expect(json_response['success']).to be_falsey
expect(json_response['message']).to match(/\AInvalid scope: 'badscope'. Valid scopes are: /)
@@ -190,11 +217,11 @@ RSpec.describe API::Internal::Base do
post api('/internal/personal_access_token'),
params: {
- secret_token: secret_token,
- key_id: key.id,
+ key_id: key.id,
name: 'newtoken',
scopes: %w(read_api read_repository)
- }
+ },
+ headers: gitlab_shell_internal_api_request_header
expect(json_response['success']).to be_truthy
expect(json_response['token']).to match(/\A\S{#{token_size}}\z/)
@@ -207,12 +234,12 @@ RSpec.describe API::Internal::Base do
post api('/internal/personal_access_token'),
params: {
- secret_token: secret_token,
- key_id: key.id,
+ key_id: key.id,
name: 'newtoken',
scopes: %w(read_api read_repository),
expires_at: '9001-11-17'
- }
+ },
+ headers: gitlab_shell_internal_api_request_header
expect(json_response['success']).to be_truthy
expect(json_response['token']).to match(/\A\S{#{token_size}}\z/)
@@ -309,7 +336,7 @@ RSpec.describe API::Internal::Base do
describe "GET /internal/discover" do
it "finds a user by key id" do
- get(api("/internal/discover"), params: { key_id: key.id, secret_token: secret_token })
+ get(api("/internal/discover"), params: { key_id: key.id }, headers: gitlab_shell_internal_api_request_header)
expect(response).to have_gitlab_http_status(:ok)
@@ -317,7 +344,7 @@ RSpec.describe API::Internal::Base do
end
it "finds a user by username" do
- get(api("/internal/discover"), params: { username: user.username, secret_token: secret_token })
+ get(api("/internal/discover"), params: { username: user.username }, headers: gitlab_shell_internal_api_request_header)
expect(response).to have_gitlab_http_status(:ok)
@@ -325,7 +352,7 @@ RSpec.describe API::Internal::Base do
end
it 'responds successfully when a user is not found' do
- get(api('/internal/discover'), params: { username: 'noone', secret_token: secret_token })
+ get(api('/internal/discover'), params: { username: 'noone' }, headers: gitlab_shell_internal_api_request_header)
expect(response).to have_gitlab_http_status(:ok)
@@ -333,7 +360,7 @@ RSpec.describe API::Internal::Base do
end
it 'response successfully when passing invalid params' do
- get(api('/internal/discover'), params: { nothing: 'to find a user', secret_token: secret_token })
+ get(api('/internal/discover'), params: { nothing: 'to find a user' }, headers: gitlab_shell_internal_api_request_header)
expect(response).to have_gitlab_http_status(:ok)
@@ -344,7 +371,7 @@ RSpec.describe API::Internal::Base do
describe "GET /internal/authorized_keys" do
context "using an existing key" do
it "finds the key" do
- get(api('/internal/authorized_keys'), params: { key: key.key.split[1], secret_token: secret_token })
+ 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)
@@ -352,7 +379,7 @@ RSpec.describe API::Internal::Base do
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], secret_token: secret_token })
+ 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['key']).to include("#{key.user_name} (#{Gitlab.config.gitlab.host})")
@@ -360,13 +387,13 @@ RSpec.describe API::Internal::Base do
end
it "returns 404 with a partial key" do
- get(api('/internal/authorized_keys'), params: { key: key.key.split[1][0...-3], secret_token: secret_token })
+ get(api('/internal/authorized_keys'), params: { key: key.key.split[1][0...-3] }, headers: gitlab_shell_internal_api_request_header)
expect(response).to have_gitlab_http_status(:not_found)
end
it "returns 404 with an not valid base64 string" do
- get(api('/internal/authorized_keys'), params: { key: "whatever!", secret_token: secret_token })
+ get(api('/internal/authorized_keys'), params: { key: "whatever!" }, headers: gitlab_shell_internal_api_request_header)
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -609,9 +636,9 @@ RSpec.describe API::Internal::Base do
project: full_path_for(project),
gl_repository: gl_repository_for(project),
action: 'git-upload-pack',
- secret_token: secret_token,
protocol: 'ssh'
- }
+ },
+ headers: gitlab_shell_internal_api_request_header
)
end
end
@@ -994,9 +1021,9 @@ RSpec.describe API::Internal::Base do
key_id: key.id,
project: 'project/does-not-exist.git',
action: 'git-upload-pack',
- secret_token: secret_token,
protocol: 'ssh'
- }
+ },
+ headers: gitlab_shell_internal_api_request_header
)
expect(response).to have_gitlab_http_status(:not_found)
@@ -1170,9 +1197,9 @@ RSpec.describe API::Internal::Base do
key_id: key.id,
project: project.full_path,
gl_repository: gl_repository,
- secret_token: secret_token,
protocol: 'ssh'
- })
+ }, headers: gitlab_shell_internal_api_request_header
+ )
expect(response).to have_gitlab_http_status(:unauthorized)
end
@@ -1285,7 +1312,6 @@ RSpec.describe API::Internal::Base do
let(:valid_params) do
{
gl_repository: gl_repository,
- secret_token: secret_token,
identifier: identifier,
changes: changes,
push_options: push_options
@@ -1296,7 +1322,7 @@ RSpec.describe API::Internal::Base do
"#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/#{branch_name}"
end
- subject { post api('/internal/post_receive'), params: valid_params }
+ subject { post api('/internal/post_receive'), params: valid_params, headers: gitlab_shell_internal_api_request_header }
before do
project.add_developer(user)
@@ -1397,7 +1423,7 @@ RSpec.describe API::Internal::Base do
describe 'POST /internal/pre_receive' do
let(:valid_params) do
- { gl_repository: gl_repository, secret_token: secret_token }
+ { gl_repository: gl_repository }
end
it 'decreases the reference counter and returns the result' do
@@ -1405,7 +1431,7 @@ RSpec.describe API::Internal::Base do
.and_return(reference_counter)
expect(reference_counter).to receive(:increase).and_return(true)
- post api("/internal/pre_receive"), params: valid_params
+ post api("/internal/pre_receive"), params: valid_params, headers: gitlab_shell_internal_api_request_header
expect(json_response['reference_counter_increased']).to be(true)
end
@@ -1420,10 +1446,8 @@ RSpec.describe API::Internal::Base do
subject do
post api('/internal/two_factor_config'),
- params: {
- secret_token: secret_token,
- key_id: key_id
- }
+ params: { key_id: key_id },
+ headers: gitlab_shell_internal_api_request_header
end
it_behaves_like 'actor key validations'
@@ -1478,27 +1502,6 @@ RSpec.describe API::Internal::Base do
end
end
- describe 'POST /internal/two_factor_otp_check' do
- let(:key_id) { key.id }
- let(:otp) { '123456' }
-
- subject do
- post api('/internal/two_factor_otp_check'),
- params: {
- secret_token: secret_token,
- key_id: key_id,
- otp_attempt: otp
- }
- end
-
- it 'is not available' do
- subject
-
- expect(json_response['success']).to be_falsey
- expect(json_response['message']).to eq 'Feature is not available'
- end
- end
-
describe 'POST /internal/two_factor_manual_otp_check' do
let(:key_id) { key.id }
let(:otp) { '123456' }
@@ -1509,7 +1512,8 @@ RSpec.describe API::Internal::Base do
secret_token: secret_token,
key_id: key_id,
otp_attempt: otp
- }
+ },
+ headers: gitlab_shell_internal_api_request_header
end
it 'is not available' do
@@ -1530,7 +1534,8 @@ RSpec.describe API::Internal::Base do
secret_token: secret_token,
key_id: key_id,
otp_attempt: otp
- }
+ },
+ headers: gitlab_shell_internal_api_request_header
end
it 'is not available' do
@@ -1551,7 +1556,8 @@ RSpec.describe API::Internal::Base do
secret_token: secret_token,
key_id: key_id,
otp_attempt: otp
- }
+ },
+ headers: gitlab_shell_internal_api_request_header
end
it 'is not available' do
@@ -1571,7 +1577,8 @@ RSpec.describe API::Internal::Base do
secret_token: secret_token,
key_id: key_id,
otp_attempt: otp
- }
+ },
+ headers: gitlab_shell_internal_api_request_header
end
it 'is not available' do
@@ -1584,32 +1591,24 @@ RSpec.describe API::Internal::Base do
def lfs_auth_project(project)
post(
api("/internal/lfs_authenticate"),
- params: {
- secret_token: secret_token,
- project: project.full_path
- }
+ params: { project: project.full_path },
+ headers: gitlab_shell_internal_api_request_header
)
end
def lfs_auth_key(key_id, project)
post(
api("/internal/lfs_authenticate"),
- params: {
- key_id: key_id,
- secret_token: secret_token,
- project: project.full_path
- }
+ params: { key_id: key_id, project: project.full_path },
+ headers: gitlab_shell_internal_api_request_header
)
end
def lfs_auth_user(user_id, project)
post(
api("/internal/lfs_authenticate"),
- params: {
- user_id: user_id,
- secret_token: secret_token,
- project: project.full_path
- }
+ params: { user_id: user_id, project: project.full_path },
+ headers: gitlab_shell_internal_api_request_header
)
end
end
diff --git a/spec/requests/api/internal/lfs_spec.rb b/spec/requests/api/internal/lfs_spec.rb
index 4739ec62992..9eb48db5bd5 100644
--- a/spec/requests/api/internal/lfs_spec.rb
+++ b/spec/requests/api/internal/lfs_spec.rb
@@ -3,6 +3,7 @@
require 'spec_helper'
RSpec.describe API::Internal::Lfs do
+ include GitlabShellHelpers
include APIInternalBaseHelpers
let_it_be(:project) { create(:project) }
@@ -11,25 +12,23 @@ RSpec.describe API::Internal::Lfs do
let_it_be(:gl_repository) { "project-#{project.id}" }
let_it_be(:filename) { lfs_object.file.path }
- let(:secret_token) { Gitlab::Shell.secret_token }
-
describe 'GET /internal/lfs' do
let(:valid_params) do
- { oid: lfs_object.oid, gl_repository: gl_repository, secret_token: secret_token }
+ { oid: lfs_object.oid, gl_repository: gl_repository }
end
context 'with invalid auth' do
- let(:invalid_params) { valid_params.merge!(secret_token: 'invalid_tokne') }
-
it 'returns 401' do
- get api("/internal/lfs"), params: invalid_params
+ get api("/internal/lfs"),
+ params: valid_params,
+ headers: gitlab_shell_internal_api_request_header(issuer: 'gitlab-workhorse')
end
end
context 'with valid auth' do
context 'LFS in local storage' do
it 'sends the file' do
- get api("/internal/lfs"), params: valid_params
+ get api("/internal/lfs"), params: valid_params, headers: gitlab_shell_internal_api_request_header
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers['Content-Type']).to eq('application/octet-stream')
@@ -39,7 +38,10 @@ RSpec.describe API::Internal::Lfs do
# https://www.rubydoc.info/github/rack/rack/master/Rack/Sendfile
it 'delegates sending to Web server' do
- get api("/internal/lfs"), params: valid_params, env: { 'HTTP_X_SENDFILE_TYPE' => 'X-Sendfile' }
+ get api("/internal/lfs"),
+ params: valid_params,
+ env: { 'HTTP_X_SENDFILE_TYPE' => 'X-Sendfile' },
+ headers: gitlab_shell_internal_api_request_header
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers['Content-Type']).to eq('application/octet-stream')
@@ -51,7 +53,7 @@ RSpec.describe API::Internal::Lfs do
it 'retuns 404 for unknown file' do
params = valid_params.merge(oid: SecureRandom.hex)
- get api("/internal/lfs"), params: params
+ get api("/internal/lfs"), params: params, headers: gitlab_shell_internal_api_request_header
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -60,7 +62,7 @@ RSpec.describe API::Internal::Lfs do
other_lfs = create(:lfs_object, :with_file)
params = valid_params.merge(oid: other_lfs.oid)
- get api("/internal/lfs"), params: params
+ get api("/internal/lfs"), params: params, headers: gitlab_shell_internal_api_request_header
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -70,7 +72,7 @@ RSpec.describe API::Internal::Lfs do
let!(:lfs_object2) { create(:lfs_object, :with_file) }
let!(:lfs_objects_project2) { create(:lfs_objects_project, project: project, lfs_object: lfs_object2) }
let(:valid_params) do
- { oid: lfs_object2.oid, gl_repository: gl_repository, secret_token: secret_token }
+ { oid: lfs_object2.oid, gl_repository: gl_repository }
end
before do
@@ -79,7 +81,7 @@ RSpec.describe API::Internal::Lfs do
end
it 'notifies Workhorse to send the file' do
- get api("/internal/lfs"), params: valid_params
+ get api("/internal/lfs"), params: valid_params, headers: gitlab_shell_internal_api_request_header
expect(response).to have_gitlab_http_status(:ok)
expect(response.headers[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("send-url:")
diff --git a/spec/requests/api/issues/get_group_issues_spec.rb b/spec/requests/api/issues/get_group_issues_spec.rb
index 3663a82891c..5c06214316b 100644
--- a/spec/requests/api/issues/get_group_issues_spec.rb
+++ b/spec/requests/api/issues/get_group_issues_spec.rb
@@ -465,10 +465,10 @@ RSpec.describe API::Issues do
context 'with archived projects' do
let_it_be(:archived_issue) do
- create(
- :issue, author: user, assignees: [user],
- project: create(:project, :public, :archived, creator_id: user.id, namespace: group)
- )
+ create(:issue,
+ author: user,
+ assignees: [user],
+ project: create(:project, :public, :archived, creator_id: user.id, namespace: group))
end
it 'returns only non archived projects issues' do
diff --git a/spec/requests/api/markdown_snapshot_spec.rb b/spec/requests/api/markdown_snapshot_spec.rb
index 1270efdfd6f..f2019172a54 100644
--- a/spec/requests/api/markdown_snapshot_spec.rb
+++ b/spec/requests/api/markdown_snapshot_spec.rb
@@ -5,7 +5,5 @@ 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
- # noinspection RubyMismatchedArgumentType (ignore RBS type warning: __dir__ can be nil, but 2nd argument can't be nil)
- glfm_specification_dir = File.expand_path('../../../glfm_specification', __dir__)
- include_context 'with API::Markdown Snapshot shared context', glfm_specification_dir
+ include_context 'with API::Markdown Snapshot shared context'
end
diff --git a/spec/requests/api/maven_packages_spec.rb b/spec/requests/api/maven_packages_spec.rb
index 1b378788b6a..d7cc6991ef4 100644
--- a/spec/requests/api/maven_packages_spec.rb
+++ b/spec/requests/api/maven_packages_spec.rb
@@ -2,6 +2,7 @@
require 'spec_helper'
RSpec.describe API::MavenPackages do
+ using RSpec::Parameterized::TableSyntax
include WorkhorseHelpers
include_context 'workhorse headers'
@@ -40,15 +41,15 @@ RSpec.describe API::MavenPackages do
project.add_developer(user)
end
- shared_examples 'handling groups and subgroups for' do |shared_example_name, visibilities: %i[public]|
+ shared_examples 'handling groups and subgroups for' do |shared_example_name, visibilities: { public: :redirect }|
context 'within a group' do
- visibilities.each do |visibility|
+ visibilities.each do |visibility, not_found_response|
context "that is #{visibility}" do
before do
group.update!(visibility_level: Gitlab::VisibilityLevel.level_value(visibility.to_s))
end
- it_behaves_like shared_example_name
+ it_behaves_like shared_example_name, not_found_response
end
end
end
@@ -60,20 +61,20 @@ RSpec.describe API::MavenPackages do
move_project_to_namespace(subgroup)
end
- visibilities.each do |visibility|
+ visibilities.each do |visibility, not_found_response|
context "that is #{visibility}" do
before do
subgroup.update!(visibility_level: Gitlab::VisibilityLevel.level_value(visibility.to_s))
group.update!(visibility_level: Gitlab::VisibilityLevel.level_value(visibility.to_s))
end
- it_behaves_like shared_example_name
+ it_behaves_like shared_example_name, not_found_response
end
end
end
end
- shared_examples 'handling groups, subgroups and user namespaces for' do |shared_example_name, visibilities: %i[public]|
+ shared_examples 'handling groups, subgroups and user namespaces for' do |shared_example_name, visibilities: { public: :redirect }|
it_behaves_like 'handling groups and subgroups for', shared_example_name, visibilities: visibilities
context 'within a user namespace' do
@@ -103,16 +104,6 @@ RSpec.describe API::MavenPackages do
end
end
- shared_examples 'rejecting the request for non existing maven path' do |expected_status: :not_found|
- it 'rejects the request' do
- expect(::Packages::Maven::PackageFinder).not_to receive(:new)
-
- subject
-
- expect(response).to have_gitlab_http_status(expected_status)
- end
- end
-
shared_examples 'processing HEAD requests' do |instance_level: false|
subject { head api(url) }
@@ -162,7 +153,7 @@ RSpec.describe API::MavenPackages do
context 'with a non existing maven path' do
let(:path) { 'foo/bar/1.2.3' }
- it_behaves_like 'rejecting the request for non existing maven path', expected_status: instance_level ? :forbidden : :not_found
+ it_behaves_like 'returning response status', instance_level ? :forbidden : :redirect
end
end
end
@@ -238,12 +229,66 @@ RSpec.describe API::MavenPackages do
end
end
+ shared_examples 'forwarding package requests' do
+ context 'request forwarding' do
+ include_context 'dependency proxy helpers context'
+
+ subject { download_file(file_name: package_name) }
+
+ shared_examples 'redirecting the request' do
+ it_behaves_like 'returning response status', :redirect
+ end
+
+ shared_examples 'package not found' do
+ it_behaves_like 'returning response status', :not_found
+ end
+
+ where(:forward, :package_in_project, :shared_examples_name) do
+ true | true | 'successfully returning the file'
+ true | false | 'redirecting the request'
+ false | true | 'successfully returning the file'
+ false | false | 'package not found'
+ end
+
+ with_them do
+ let(:package_name) { package_in_project ? package_file.file_name : 'foo' }
+
+ before do
+ allow_fetch_application_setting(attribute: 'maven_package_requests_forwarding', return_value: forward)
+ end
+
+ it_behaves_like params[:shared_examples_name]
+ end
+
+ context 'with maven_central_request_forwarding disabled' do
+ where(:forward, :package_in_project, :shared_examples_name) do
+ true | true | 'successfully returning the file'
+ true | false | 'package not found'
+ false | true | 'successfully returning the file'
+ false | false | 'package not found'
+ end
+
+ with_them do
+ let(:package_name) { package_in_project ? package_file.file_name : 'foo' }
+
+ before do
+ stub_feature_flags(maven_central_request_forwarding: false)
+ allow_fetch_application_setting(attribute: 'maven_package_requests_forwarding', return_value: forward)
+ end
+
+ it_behaves_like params[:shared_examples_name]
+ end
+ end
+ end
+ end
+
describe 'GET /api/v4/packages/maven/*path/:file_name' do
context 'a public project' do
subject { download_file(file_name: package_file.file_name) }
shared_examples 'getting a file' do
it_behaves_like 'tracking the file download event'
+ it_behaves_like 'bumping the package last downloaded at field'
it_behaves_like 'successfully returning the file'
it_behaves_like 'file download in FIPS mode'
@@ -258,7 +303,16 @@ RSpec.describe API::MavenPackages do
context 'with a non existing maven path' do
subject { download_file(file_name: package_file.file_name, path: 'foo/bar/1.2.3') }
- it_behaves_like 'rejecting the request for non existing maven path', expected_status: :forbidden
+ it_behaves_like 'returning response status', :forbidden
+ end
+
+ it 'returns not found when a package is not found' do
+ finder = double('finder', execute: nil)
+ expect(::Packages::Maven::PackageFinder).to receive(:new).and_return(finder)
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:not_found)
end
end
@@ -275,7 +329,7 @@ RSpec.describe API::MavenPackages do
shared_examples 'getting a file' do
it_behaves_like 'tracking the file download event'
-
+ it_behaves_like 'bumping the package last downloaded at field'
it_behaves_like 'successfully returning the file'
it 'denies download when no private token' do
@@ -285,17 +339,16 @@ RSpec.describe API::MavenPackages do
end
it_behaves_like 'downloads with a job token'
-
it_behaves_like 'downloads with a deploy token'
context 'with a non existing maven path' do
subject { download_file_with_token(file_name: package_file.file_name, path: 'foo/bar/1.2.3') }
- it_behaves_like 'rejecting the request for non existing maven path', expected_status: :forbidden
+ it_behaves_like 'returning response status', :forbidden
end
end
- it_behaves_like 'handling groups, subgroups and user namespaces for', 'getting a file', visibilities: %i[public internal]
+ it_behaves_like 'handling groups, subgroups and user namespaces for', 'getting a file', visibilities: { public: :redirect, internal: :not_found }
end
context 'private project' do
@@ -307,7 +360,7 @@ RSpec.describe API::MavenPackages do
shared_examples 'getting a file' do
it_behaves_like 'tracking the file download event'
-
+ it_behaves_like 'bumping the package last downloaded at field'
it_behaves_like 'successfully returning the file'
it 'denies download when not enough permissions' do
@@ -327,7 +380,6 @@ RSpec.describe API::MavenPackages do
end
it_behaves_like 'downloads with a job token'
-
it_behaves_like 'downloads with a deploy token'
it 'does not allow download by a unauthorized deploy token with same id as a user with access' do
@@ -350,11 +402,11 @@ RSpec.describe API::MavenPackages do
context 'with a non existing maven path' do
subject { download_file_with_token(file_name: package_file.file_name, path: 'foo/bar/1.2.3') }
- it_behaves_like 'rejecting the request for non existing maven path', expected_status: :forbidden
+ it_behaves_like 'returning response status', :forbidden
end
end
- it_behaves_like 'handling groups, subgroups and user namespaces for', 'getting a file', visibilities: %i[public internal private]
+ it_behaves_like 'handling groups, subgroups and user namespaces for', 'getting a file', visibilities: { public: :redirect, internal: :not_found, private: :not_found }
end
context 'project name is different from a package name' do
@@ -409,11 +461,14 @@ RSpec.describe API::MavenPackages do
group.add_developer(user)
end
+ it_behaves_like 'forwarding package requests'
+
context 'a public project' do
subject { download_file(file_name: package_file.file_name) }
shared_examples 'getting a file for a group' do
it_behaves_like 'tracking the file download event'
+ it_behaves_like 'bumping the package last downloaded at field'
it_behaves_like 'successfully returning the file'
it_behaves_like 'file download in FIPS mode'
@@ -428,7 +483,7 @@ RSpec.describe API::MavenPackages do
context 'with a non existing maven path' do
subject { download_file(file_name: package_file.file_name, path: 'foo/bar/1.2.3') }
- it_behaves_like 'rejecting the request for non existing maven path'
+ it_behaves_like 'returning response status', :redirect
end
end
@@ -443,29 +498,28 @@ RSpec.describe API::MavenPackages do
subject { download_file_with_token(file_name: package_file.file_name) }
- shared_examples 'getting a file for a group' do
+ shared_examples 'getting a file for a group' do |not_found_response|
it_behaves_like 'tracking the file download event'
-
+ it_behaves_like 'bumping the package last downloaded at field'
it_behaves_like 'successfully returning the file'
- it 'denies download when no private token' do
+ it 'forwards download when no private token' do
download_file(file_name: package_file.file_name)
- expect(response).to have_gitlab_http_status(:not_found)
+ expect(response).to have_gitlab_http_status(not_found_response)
end
it_behaves_like 'downloads with a job token'
-
it_behaves_like 'downloads with a deploy token'
context 'with a non existing maven path' do
subject { download_file_with_token(file_name: package_file.file_name, path: 'foo/bar/1.2.3') }
- it_behaves_like 'rejecting the request for non existing maven path'
+ it_behaves_like 'returning response status', :redirect
end
end
- it_behaves_like 'handling groups and subgroups for', 'getting a file for a group', visibilities: %i[internal public]
+ it_behaves_like 'handling groups and subgroups for', 'getting a file for a group', visibilities: { internal: :not_found, public: :redirect }
end
context 'private project' do
@@ -475,9 +529,9 @@ RSpec.describe API::MavenPackages do
subject { download_file_with_token(file_name: package_file.file_name) }
- shared_examples 'getting a file for a group' do
+ shared_examples 'getting a file for a group' do |not_found_response|
it_behaves_like 'tracking the file download event'
-
+ it_behaves_like 'bumping the package last downloaded at field'
it_behaves_like 'successfully returning the file'
it 'denies download when not enough permissions' do
@@ -485,23 +539,22 @@ RSpec.describe API::MavenPackages do
subject
- expect(response).to have_gitlab_http_status(:not_found)
+ expect(response).to have_gitlab_http_status(:redirect)
end
it 'denies download when no private token' do
download_file(file_name: package_file.file_name)
- expect(response).to have_gitlab_http_status(:not_found)
+ expect(response).to have_gitlab_http_status(not_found_response)
end
it_behaves_like 'downloads with a job token'
-
it_behaves_like 'downloads with a deploy token'
context 'with a non existing maven path' do
subject { download_file_with_token(file_name: package_file.file_name, path: 'foo/bar/1.2.3') }
- it_behaves_like 'rejecting the request for non existing maven path'
+ it_behaves_like 'returning response status', :redirect
end
context 'with group deploy token' do
@@ -521,12 +574,12 @@ RSpec.describe API::MavenPackages do
context 'with a non existing maven path' do
subject { download_file_with_token(file_name: package_file.file_name, path: 'foo/bar/1.2.3', request_headers: group_deploy_token_headers) }
- it_behaves_like 'rejecting the request for non existing maven path'
+ it_behaves_like 'returning response status', :redirect
end
end
end
- it_behaves_like 'handling groups and subgroups for', 'getting a file for a group', visibilities: %i[private internal public]
+ it_behaves_like 'handling groups and subgroups for', 'getting a file for a group', visibilities: { private: :not_found, internal: :not_found, public: :redirect }
context 'with a reporter from a subgroup accessing the root group' do
let_it_be(:root_group) { create(:group, :private) }
@@ -544,7 +597,7 @@ RSpec.describe API::MavenPackages do
context 'with a non existing maven path' do
subject { download_file_with_token(file_name: package_file.file_name, path: 'foo/bar/1.2.3', request_headers: headers_with_token, group_id: root_group.id) }
- it_behaves_like 'rejecting the request for non existing maven path'
+ it_behaves_like 'returning response status', :redirect
end
end
end
@@ -640,12 +693,14 @@ RSpec.describe API::MavenPackages do
it_behaves_like 'successfully returning the file'
it_behaves_like 'file download in FIPS mode'
- it 'returns sha1 of the file' do
- download_file(file_name: package_file.file_name + '.sha1')
+ %w[sha1 md5].each do |format|
+ it "returns #{format} of the file" do
+ download_file(file_name: package_file.file_name + ".#{format}")
- expect(response).to have_gitlab_http_status(:ok)
- expect(response.media_type).to eq('text/plain')
- expect(response.body).to eq(package_file.file_sha1)
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response.media_type).to eq('text/plain')
+ expect(response.body).to eq(package_file.send("file_#{format}".to_sym))
+ end
end
context 'when the repository is disabled' do
@@ -664,7 +719,7 @@ RSpec.describe API::MavenPackages do
context 'with a non existing maven path' do
subject { download_file(file_name: package_file.file_name, path: 'foo/bar/1.2.3') }
- it_behaves_like 'rejecting the request for non existing maven path'
+ it_behaves_like 'returning response status', :redirect
end
end
@@ -676,7 +731,7 @@ RSpec.describe API::MavenPackages do
subject { download_file_with_token(file_name: package_file.file_name) }
it_behaves_like 'tracking the file download event'
-
+ it_behaves_like 'bumping the package last downloaded at field'
it_behaves_like 'successfully returning the file'
it 'denies download when not enough permissions' do
@@ -694,16 +749,17 @@ RSpec.describe API::MavenPackages do
end
it_behaves_like 'downloads with a job token'
-
it_behaves_like 'downloads with a deploy token'
context 'with a non existing maven path' do
subject { download_file_with_token(file_name: package_file.file_name, path: 'foo/bar/1.2.3') }
- it_behaves_like 'rejecting the request for non existing maven path'
+ it_behaves_like 'returning response status', :redirect
end
end
+ it_behaves_like 'forwarding package requests'
+
def download_file(file_name:, params: {}, request_headers: headers, path: maven_metadatum.path)
get api("/projects/#{project.id}/packages/maven/" \
"#{path}/#{file_name}"), params: params, headers: request_headers
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 2a03ae89389..9d153286d14 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe API::MergeRequests do
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) }
@@ -1022,6 +1023,22 @@ RSpec.describe API::MergeRequests do
it_behaves_like 'a non-cached MergeRequest api request', 1
end
+ context 'when the assignees change' do
+ before do
+ merge_request.assignees << create(:user)
+ end
+
+ it_behaves_like 'a non-cached MergeRequest api request', 1
+ end
+
+ context 'when the reviewers change' do
+ before do
+ merge_request.reviewers << create(:user)
+ end
+
+ it_behaves_like 'a non-cached MergeRequest api request', 1
+ end
+
context 'when another user requests' do
before do
sign_in(user2)
@@ -1120,6 +1137,44 @@ RSpec.describe API::MergeRequests do
end.not_to exceed_query_limit(control)
end
end
+
+ context 'when user is an inherited member from the group' do
+ let_it_be(:group) { create(:group) }
+
+ shared_examples 'user cannot view merge requests' do
+ it 'returns 403 forbidden' do
+ get api("/projects/#{group_project.id}/merge_requests", inherited_user)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'and user is a guest' do
+ let_it_be(:inherited_user) { create(:user) }
+
+ before_all do
+ group.add_guest(inherited_user)
+ end
+
+ context 'when project is public with private merge requests' do
+ let(:group_project) do
+ create(:project,
+ :public,
+ :repository,
+ group: group,
+ merge_requests_access_level: ProjectFeature::DISABLED)
+ end
+
+ it_behaves_like 'user cannot view merge requests'
+ end
+
+ context 'when project is private' do
+ let(:group_project) { create(:project, :private, :repository, group: group) }
+
+ it_behaves_like 'user cannot view merge requests'
+ end
+ end
+ end
end
describe "GET /groups/:id/merge_requests" do
@@ -1528,7 +1583,6 @@ RSpec.describe API::MergeRequests do
expect(json_response.last['user']['name']).to eq(reviewer.name)
expect(json_response.last['user']['username']).to eq(reviewer.username)
expect(json_response.last['state']).to eq('unreviewed')
- expect(json_response.last['updated_state_by']).to be_nil
expect(json_response.last['created_at']).to be_present
end
@@ -2219,6 +2273,59 @@ RSpec.describe API::MergeRequests do
expect(response).to have_gitlab_http_status(:created)
end
end
+
+ context 'when user is an inherited member from the group' do
+ let_it_be(:group) { create(:group) }
+
+ shared_examples 'user cannot create merge requests' do
+ it 'returns 403 forbidden' do
+ post api("/projects/#{group_project.id}/merge_requests", inherited_user), params: params
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'and user is a guest' do
+ let_it_be(:inherited_user) { create(:user) }
+ let_it_be(:params) do
+ {
+ title: 'Test merge request',
+ source_branch: 'feature_conflict',
+ target_branch: 'master',
+ author_id: inherited_user.id
+ }
+ end
+
+ before_all do
+ group.add_guest(inherited_user)
+ end
+
+ context 'when project is public with private merge requests' do
+ let(:group_project) do
+ create(:project,
+ :public,
+ :repository,
+ group: group,
+ merge_requests_access_level: ProjectFeature::DISABLED,
+ only_allow_merge_if_pipeline_succeeds: false)
+ end
+
+ it_behaves_like 'user cannot create merge requests'
+ end
+
+ context 'when project is private' do
+ let(:group_project) do
+ create(:project,
+ :private,
+ :repository,
+ group: group,
+ only_allow_merge_if_pipeline_succeeds: false)
+ end
+
+ it_behaves_like 'user cannot create merge requests'
+ end
+ end
+ end
end
describe 'PUT /projects/:id/merge_requests/:merge_request_iid' do
@@ -2247,6 +2354,16 @@ RSpec.describe API::MergeRequests do
expect(merge_request.notes.system.last.note).to include("assigned to #{user2.to_reference}")
end
+
+ it 'triggers webhooks', :sidekiq_inline do
+ hook = create(:project_hook, merge_requests_events: true, project: merge_request.project)
+
+ expect(WebHookWorker).to receive(:perform_async).with(hook.id, anything, 'merge_request_hooks', anything)
+
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user), params: params
+
+ expect(response).to have_gitlab_http_status(:ok)
+ end
end
context 'when assignee_id=user2.id' do
@@ -3373,7 +3490,8 @@ RSpec.describe API::MergeRequests do
context 'when merge request branch does not allow force push' do
before do
- create(:protected_branch, project: project, name: merge_request.source_branch, allow_force_push: false)
+ create_params = { name: merge_request.source_branch, allow_force_push: false, merge_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }] }
+ ProtectedBranches::CreateService.new(project, project.first_owner, create_params).execute
end
it 'returns 403' do
@@ -3413,6 +3531,71 @@ 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/ml/mlflow_spec.rb b/spec/requests/api/ml/mlflow_spec.rb
new file mode 100644
index 00000000000..4e7091a5b0f
--- /dev/null
+++ b/spec/requests/api/ml/mlflow_spec.rb
@@ -0,0 +1,366 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'mime/types'
+
+RSpec.describe API::Ml::Mlflow do
+ include SessionHelpers
+ include ApiHelpers
+ include HttpBasicAuthHelpers
+
+ let_it_be(:project) { create(:project, :private) }
+ let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
+ let_it_be(:experiment) do
+ create(:ml_experiments, user: project.creator, project: project)
+ end
+
+ let_it_be(:candidate) do
+ create(:ml_candidates, user: experiment.user, start_time: 1234, experiment: experiment)
+ end
+
+ let_it_be(:another_candidate) do
+ create(:ml_candidates,
+ experiment: create(:ml_experiments, project: create(:project)))
+ end
+
+ let(:current_user) { developer }
+ let(:ff_value) { true }
+ let(:scopes) { %w[read_api api] }
+ let(:headers) do
+ { 'Authorization' => "Bearer #{create(:personal_access_token, scopes: scopes, user: current_user).token}" }
+ end
+
+ let(:params) { {} }
+ let(:request) { get api(route), params: params, headers: headers }
+
+ before do
+ stub_feature_flags(ml_experiment_tracking: ff_value)
+
+ request
+ end
+
+ shared_examples 'Not Found' do |message|
+ it "is Not Found" do
+ expect(response).to have_gitlab_http_status(:not_found)
+
+ expect(json_response['message']).to eq(message) if message.present?
+ end
+ end
+
+ shared_examples 'Not Found - Resource Does Not Exist' do
+ it "is Resource Does Not Exist" do
+ expect(response).to have_gitlab_http_status(:not_found)
+
+ expect(json_response).to include({ "error_code" => 'RESOURCE_DOES_NOT_EXIST' })
+ end
+ end
+
+ shared_examples 'Requires api scope' do
+ context 'when user has access but token has wrong scope' do
+ let(:scopes) { %w[read_api] }
+
+ it { expect(response).to have_gitlab_http_status(:forbidden) }
+ end
+ end
+
+ shared_examples 'Requires read_api scope' do
+ context 'when user has access but token has wrong scope' do
+ let(:scopes) { %w[read_user] }
+
+ it { expect(response).to have_gitlab_http_status(:forbidden) }
+ end
+ end
+
+ shared_examples 'Bad Request' do |error_code = nil|
+ it "is Bad Request" do
+ expect(response).to have_gitlab_http_status(:bad_request)
+
+ expect(json_response).to include({ 'error_code' => error_code }) if error_code.present?
+ end
+ end
+
+ shared_examples 'shared error cases' do
+ context 'when not authenticated' do
+ let(:headers) { {} }
+
+ it "is Unauthorized" do
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+
+ context 'when user does not have access' do
+ let(:current_user) { create(:user) }
+
+ it_behaves_like 'Not Found'
+ end
+
+ context 'when ff is disabled' do
+ let(:ff_value) { false }
+
+ it_behaves_like 'Not Found'
+ end
+ end
+
+ describe 'GET /projects/:id/ml/mflow/api/2.0/mlflow/get' do
+ let(:experiment_iid) { experiment.iid.to_s }
+ let(:route) { "/projects/#{project.id}/ml/mflow/api/2.0/mlflow/experiments/get?experiment_id=#{experiment_iid}" }
+
+ it 'returns the experiment' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('ml/get_experiment')
+ expect(json_response).to include({
+ 'experiment' => {
+ 'experiment_id' => experiment_iid,
+ 'name' => experiment.name,
+ 'lifecycle_stage' => 'active',
+ 'artifact_location' => 'not_implemented'
+ }
+ })
+ end
+
+ describe 'Error States' do
+ context 'when has access' do
+ context 'and experiment does not exist' do
+ let(:experiment_iid) { non_existing_record_iid.to_s }
+
+ it_behaves_like 'Not Found - Resource Does Not Exist'
+ end
+
+ context 'and experiment_id is not passed' do
+ let(:route) { "/projects/#{project.id}/ml/mflow/api/2.0/mlflow/experiments/get" }
+
+ it_behaves_like 'Not Found - Resource Does Not Exist'
+ end
+ end
+
+ it_behaves_like 'shared error cases'
+ it_behaves_like 'Requires read_api scope'
+ end
+ end
+
+ describe 'GET /projects/:id/ml/mflow/api/2.0/mlflow/experiments/get-by-name' do
+ let(:experiment_name) { experiment.name }
+ let(:route) do
+ "/projects/#{project.id}/ml/mflow/api/2.0/mlflow/experiments/get-by-name?experiment_name=#{experiment_name}"
+ end
+
+ it 'returns the experiment' do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('ml/get_experiment')
+ expect(json_response).to include({
+ 'experiment' => {
+ 'experiment_id' => experiment.iid.to_s,
+ 'name' => experiment_name,
+ 'lifecycle_stage' => 'active',
+ 'artifact_location' => 'not_implemented'
+ }
+ })
+ end
+
+ describe 'Error States' do
+ context 'when has access but experiment does not exist' do
+ let(:experiment_name) { "random_experiment" }
+
+ it_behaves_like 'Not Found - Resource Does Not Exist'
+ end
+
+ context 'when has access but experiment_name is not passed' do
+ let(:route) { "/projects/#{project.id}/ml/mflow/api/2.0/mlflow/experiments/get-by-name" }
+
+ it_behaves_like 'Not Found - Resource Does Not Exist'
+ end
+
+ it_behaves_like 'shared error cases'
+ it_behaves_like 'Requires read_api scope'
+ end
+ end
+
+ describe 'POST /projects/:id/ml/mflow/api/2.0/mlflow/experiments/create' do
+ let(:route) do
+ "/projects/#{project.id}/ml/mflow/api/2.0/mlflow/experiments/create"
+ end
+
+ let(:params) { { name: 'new_experiment' } }
+ let(:request) { post api(route), params: params, headers: headers }
+
+ it 'creates the experiment' do
+ expect(response).to have_gitlab_http_status(:created)
+ expect(json_response).to include('experiment_id' )
+ end
+
+ describe 'Error States' do
+ context 'when experiment name is not passed' do
+ let(:params) { {} }
+
+ it_behaves_like 'Bad Request'
+ end
+
+ context 'when experiment name already exists' do
+ let(:existing_experiment) do
+ create(:ml_experiments, user: current_user, project: project)
+ end
+
+ let(:params) { { name: existing_experiment.name } }
+
+ it_behaves_like 'Bad Request', 'RESOURCE_ALREADY_EXISTS'
+ end
+
+ context 'when project does not exist' do
+ let(:route) { "/projects/#{non_existing_record_id}/ml/mflow/api/2.0/mlflow/experiments/create" }
+
+ it_behaves_like 'Not Found', '404 Project Not Found'
+ end
+
+ it_behaves_like 'shared error cases'
+ it_behaves_like 'Requires api scope'
+ end
+ end
+
+ describe 'Runs' do
+ describe 'POST /projects/:id/ml/mflow/api/2.0/mlflow/runs/create' do
+ let(:route) do
+ "/projects/#{project.id}/ml/mflow/api/2.0/mlflow/runs/create"
+ end
+
+ let(:params) { { experiment_id: experiment.iid.to_s, start_time: Time.now.to_i } }
+ let(:request) { post api(route), params: params, headers: headers }
+
+ it 'creates the run' do
+ expected_properties = {
+ 'experiment_id' => params[:experiment_id],
+ 'user_id' => current_user.id.to_s,
+ 'start_time' => params[:start_time],
+ 'artifact_uri' => 'not_implemented',
+ 'status' => "RUNNING",
+ 'lifecycle_stage' => "active"
+ }
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect(response).to match_response_schema('ml/run')
+ expect(json_response['run']).to include('info' => hash_including(**expected_properties), 'data' => {})
+ end
+
+ describe 'Error States' do
+ context 'when experiment id is not passed' do
+ let(:params) { {} }
+
+ it_behaves_like 'Bad Request'
+ end
+
+ context 'when experiment id does not exist' do
+ let(:params) { { experiment_id: non_existing_record_iid.to_s } }
+
+ it_behaves_like 'Not Found - Resource Does Not Exist'
+ end
+
+ it_behaves_like 'shared error cases'
+ it_behaves_like 'Requires api scope'
+ end
+ end
+
+ describe 'GET /projects/:id/ml/mflow/api/2.0/mlflow/runs/get' do
+ let_it_be(:route) do
+ "/projects/#{project.id}/ml/mflow/api/2.0/mlflow/runs/get"
+ end
+
+ let_it_be(:candidate) { create(:ml_candidates, user: experiment.user, start_time: 1234, experiment: experiment) }
+
+ let(:params) { { 'run_id' => candidate.iid } }
+
+ it 'gets the run' do
+ expected_properties = {
+ 'experiment_id' => candidate.experiment.iid.to_s,
+ 'user_id' => candidate.user.id.to_s,
+ 'start_time' => candidate.start_time,
+ 'artifact_uri' => 'not_implemented',
+ 'status' => "RUNNING",
+ 'lifecycle_stage' => "active"
+ }
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(response).to match_response_schema('ml/run')
+ expect(json_response['run']).to include('info' => hash_including(**expected_properties), 'data' => {})
+ end
+
+ describe 'Error States' do
+ context 'when run id is not passed' do
+ let(:params) { {} }
+
+ it_behaves_like 'Not Found - Resource Does Not Exist'
+ end
+
+ context 'when run id does not exist' do
+ let(:params) { { run_id: non_existing_record_iid.to_s } }
+
+ it_behaves_like 'Not Found - Resource Does Not Exist'
+ end
+
+ context 'when run id exists but does not belong to project' do
+ let(:params) { { run_id: another_candidate.iid.to_s } }
+
+ it_behaves_like 'Not Found - Resource Does Not Exist'
+ end
+
+ it_behaves_like 'shared error cases'
+ it_behaves_like 'Requires read_api scope'
+ end
+ end
+ end
+
+ describe 'POST /projects/:id/ml/mflow/api/2.0/mlflow/runs/update' do
+ let(:route) { "/projects/#{project.id}/ml/mflow/api/2.0/mlflow/runs/update" }
+ let(:params) { { run_id: candidate.iid.to_s, status: 'FAILED', end_time: Time.now.to_i } }
+ let(:request) { post api(route), params: params, headers: headers }
+
+ it 'updates the run' do
+ expected_properties = {
+ 'experiment_id' => candidate.experiment.iid.to_s,
+ 'user_id' => candidate.user.id.to_s,
+ 'start_time' => candidate.start_time,
+ 'end_time' => params[:end_time],
+ 'artifact_uri' => 'not_implemented',
+ 'status' => 'FAILED',
+ 'lifecycle_stage' => 'active'
+ }
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(response).to match_response_schema('ml/update_run')
+ expect(json_response).to include('run_info' => hash_including(**expected_properties))
+ end
+
+ describe 'Error States' do
+ context 'when run id is not passed' do
+ let(:params) { {} }
+
+ it_behaves_like 'Not Found - Resource Does Not Exist'
+ end
+
+ context 'when run id does not exist' do
+ let(:params) { { run_id: non_existing_record_iid.to_s } }
+
+ it_behaves_like 'Not Found - Resource Does Not Exist'
+ end
+
+ context 'when run id exists but does not belong to project' do
+ let(:params) { { run_id: another_candidate.iid.to_s } }
+
+ it_behaves_like 'Not Found - Resource Does Not Exist'
+ end
+
+ context 'when run id exists but status in invalid' do
+ let(:params) { { run_id: candidate.iid.to_s, status: 'YOLO', end_time: Time.now.to_i } }
+
+ it_behaves_like 'Bad Request'
+ end
+
+ context 'when run id exists but end_time is invalid' do
+ let(:params) { { run_id: candidate.iid.to_s, status: 'FAILED', end_time: 's' } }
+
+ it_behaves_like 'Bad Request'
+ end
+
+ it_behaves_like 'shared error cases'
+ it_behaves_like 'Requires api scope'
+ end
+ end
+end
diff --git a/spec/requests/api/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb
index 09b87f41b82..ab39c29653f 100644
--- a/spec/requests/api/namespaces_spec.rb
+++ b/spec/requests/api/namespaces_spec.rb
@@ -285,6 +285,14 @@ RSpec.describe API::Namespaces do
end
context 'when authenticated' do
+ it_behaves_like 'rate limited endpoint', rate_limit_key: :namespace_exists do
+ let(:current_user) { user }
+
+ def request
+ get api("/namespaces/#{namespace1.path}/exists", current_user)
+ end
+ end
+
it 'returns JSON indicating the namespace exists and a suggestion' do
get api("/namespaces/#{namespace1.path}/exists", user)
diff --git a/spec/requests/api/npm_project_packages_spec.rb b/spec/requests/api/npm_project_packages_spec.rb
index 3bcffac2760..bdcd6e7278d 100644
--- a/spec/requests/api/npm_project_packages_spec.rb
+++ b/spec/requests/api/npm_project_packages_spec.rb
@@ -63,6 +63,7 @@ RSpec.describe API::NpmProjectPackages do
it_behaves_like 'successfully downloads the file'
it_behaves_like 'a package tracking event', 'API::NpmPackages', 'pull_package'
+ it_behaves_like 'bumping the package last downloaded at field'
end
context 'with job token' do
@@ -70,12 +71,14 @@ RSpec.describe API::NpmProjectPackages do
it_behaves_like 'successfully downloads the file'
it_behaves_like 'a package tracking event', 'API::NpmPackages', 'pull_package'
+ it_behaves_like 'bumping the package last downloaded at field'
end
end
context 'a public project' do
it_behaves_like 'successfully downloads the file'
it_behaves_like 'a package tracking event', 'API::NpmPackages', 'pull_package'
+ it_behaves_like 'bumping the package last downloaded at field'
context 'with a job token for a different user' do
let_it_be(:other_user) { create(:user) }
diff --git a/spec/requests/api/personal_access_tokens/self_revocation_spec.rb b/spec/requests/api/personal_access_tokens/self_revocation_spec.rb
new file mode 100644
index 00000000000..f829b39cc1e
--- /dev/null
+++ b/spec/requests/api/personal_access_tokens/self_revocation_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe API::PersonalAccessTokens::SelfRevocation do
+ let_it_be(:current_user) { create(:user) }
+
+ describe 'DELETE /personal_access_tokens/self' do
+ let(:path) { '/personal_access_tokens/self' }
+ let(:token) { create(:personal_access_token, user: current_user) }
+
+ subject(:delete_token) { delete api(path, personal_access_token: token) }
+
+ shared_examples 'revoking token succeeds' do
+ it 'revokes token' do
+ delete_token
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ expect(token.reload).to be_revoked
+ end
+ end
+
+ shared_examples 'revoking token denied' do |status|
+ it 'cannot revoke token' do
+ delete_token
+
+ expect(response).to have_gitlab_http_status(status)
+ end
+ end
+
+ context 'when current_user is an administrator', :enable_admin_mode do
+ let(:current_user) { create(:admin) }
+
+ it_behaves_like 'revoking token succeeds'
+
+ context 'with impersonated token' do
+ let(:token) { create(:personal_access_token, :impersonation, user: current_user) }
+
+ it_behaves_like 'revoking token succeeds'
+ end
+ end
+
+ context 'when current_user is not an administrator' do
+ let(:current_user) { create(:user) }
+
+ it_behaves_like 'revoking token succeeds'
+
+ context 'with impersonated token' do
+ let(:token) { create(:personal_access_token, :impersonation, user: current_user) }
+
+ it_behaves_like 'revoking token denied', :bad_request
+ end
+
+ context 'with already revoked token' do
+ let(:token) { create(:personal_access_token, :revoked, user: current_user) }
+
+ it_behaves_like 'revoking token denied', :unauthorized
+ end
+ end
+
+ Gitlab::Auth.all_available_scopes.each do |scope|
+ context "with a '#{scope}' scoped token" do
+ let(:token) { create(:personal_access_token, scopes: [scope], user: current_user) }
+
+ it_behaves_like 'revoking token succeeds'
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/personal_access_tokens_spec.rb b/spec/requests/api/personal_access_tokens_spec.rb
index 8d8998cfdd6..37b5a594f2a 100644
--- a/spec/requests/api/personal_access_tokens_spec.rb
+++ b/spec/requests/api/personal_access_tokens_spec.rb
@@ -75,6 +75,7 @@ RSpec.describe API::PersonalAccessTokens do
describe 'GET /personal_access_tokens/:id' do
let_it_be(:user_token) { create(:personal_access_token, user: current_user) }
+ let_it_be(:user_read_only_token) { create(:personal_access_token, scopes: ['read_repository'], user: current_user) }
let_it_be(:user_token_path) { "/personal_access_tokens/#{user_token.id}" }
let_it_be(:invalid_path) { "/personal_access_tokens/#{non_existing_record_id}" }
@@ -125,53 +126,11 @@ RSpec.describe API::PersonalAccessTokens do
expect(response).to have_gitlab_http_status(:unauthorized)
end
- end
- end
-
- describe 'DELETE /personal_access_tokens/self' do
- let(:path) { '/personal_access_tokens/self' }
- let(:token) { create(:personal_access_token, user: current_user) }
-
- subject { delete api(path, current_user, personal_access_token: token) }
-
- shared_examples 'revoking token succeeds' do
- it 'revokes token' do
- subject
-
- expect(response).to have_gitlab_http_status(:no_content)
- expect(token.reload).to be_revoked
- end
- end
- shared_examples 'revoking token denied' do |status|
- it 'cannot revoke token' do
- subject
+ it 'fails to return own PAT by id with read_repository token' do
+ get api(user_token_path, current_user, personal_access_token: user_read_only_token)
- expect(response).to have_gitlab_http_status(status)
- end
- end
-
- context 'when current_user is an administrator', :enable_admin_mode do
- let(:current_user) { create(:admin) }
-
- it_behaves_like 'revoking token succeeds'
- end
-
- context 'when current_user is not an administrator' do
- let(:current_user) { create(:user) }
-
- it_behaves_like 'revoking token succeeds'
-
- context 'with impersonated token' do
- let(:token) { create(:personal_access_token, :impersonation, user: current_user) }
-
- it_behaves_like 'revoking token denied', :bad_request
- end
-
- context 'with already revoked token' do
- let(:token) { create(:personal_access_token, :revoked, user: current_user) }
-
- it_behaves_like 'revoking token denied', :unauthorized
+ expect(response).to have_gitlab_http_status(:forbidden)
end
end
end
@@ -183,6 +142,9 @@ RSpec.describe API::PersonalAccessTokens do
let_it_be(:admin_user) { create(:admin) }
let_it_be(:admin_token) { create(:personal_access_token, user: admin_user) }
let_it_be(:admin_path) { "/personal_access_tokens/#{admin_token.id}" }
+ let_it_be(:admin_read_only_token) do
+ create(:personal_access_token, scopes: ['read_repository'], user: admin_user)
+ end
it 'revokes a different users token' do
delete api(path, admin_user)
@@ -196,6 +158,12 @@ RSpec.describe API::PersonalAccessTokens do
expect(response).to have_gitlab_http_status(:no_content)
end
+
+ it 'fails to revoke a different user token using a readonly scope' do
+ delete api(path, personal_access_token: admin_read_only_token)
+
+ expect(token1.reload.revoked?).to be false
+ end
end
context 'when current_user is not an administrator' do
diff --git a/spec/requests/api/project_attributes.yml b/spec/requests/api/project_attributes.yml
index 670035187cb..1335fa02aaf 100644
--- a/spec/requests/api/project_attributes.yml
+++ b/spec/requests/api/project_attributes.yml
@@ -154,11 +154,13 @@ project_setting:
- project_id
- push_rule_id
- show_default_award_emojis
+ - show_diff_preview_in_email
- updated_at
- cve_id_request_enabled
- mr_default_target_self
- target_platforms
- selective_code_owner_removals
+ - show_diff_preview_in_email
build_service_desk_setting: # service_desk_setting
unexposed_attributes:
diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb
index afe5a7d4a21..401db766589 100644
--- a/spec/requests/api/project_import_spec.rb
+++ b/spec/requests/api/project_import_spec.rb
@@ -47,7 +47,7 @@ RSpec.describe API::ProjectImport, :aggregate_failures do
it 'executes a limited number of queries' do
control_count = ActiveRecord::QueryRecorder.new { subject }.count
- expect(control_count).to be <= 109
+ expect(control_count).to be <= 110
end
it 'schedules an import using a namespace' do
diff --git a/spec/requests/api/project_packages_spec.rb b/spec/requests/api/project_packages_spec.rb
index 7a05da8e13f..00d295b3490 100644
--- a/spec/requests/api/project_packages_spec.rb
+++ b/spec/requests/api/project_packages_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe API::ProjectPackages do
let_it_be(:project) { create(:project, :public) }
let(:user) { create(:user) }
- let!(:package1) { create(:npm_package, project: project, version: '3.1.0', name: "@#{project.root_namespace.path}/foo1") }
+ let!(:package1) { create(:npm_package, :last_downloaded_at, project: project, version: '3.1.0', name: "@#{project.root_namespace.path}/foo1") }
let(:package_url) { "/projects/#{project.id}/packages/#{package1.id}" }
let!(:package2) { create(:nuget_package, project: project, version: '2.0.4') }
let!(:another_package) { create(:npm_package) }
@@ -272,6 +272,17 @@ RSpec.describe API::ProjectPackages do
it_behaves_like 'returns package', :project, :no_type
it_behaves_like 'returns package', :project, :guest
end
+
+ context 'with a package without last_downloaded_at' do
+ let(:package_url) { "/projects/#{project.id}/packages/#{package2.id}" }
+
+ it 'returns 200 and the package information' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema(single_package_schema)
+ end
+ end
end
context 'project is private' do
diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb
index 72519ed1683..6e2dd6e76a9 100644
--- a/spec/requests/api/project_snippets_spec.rb
+++ b/spec/requests/api/project_snippets_spec.rb
@@ -256,6 +256,7 @@ RSpec.describe API::ProjectSnippets do
allow_next_instance_of(Spam::AkismetService) do |instance|
allow(instance).to receive(:spam?).and_return(true)
end
+ stub_feature_flags(allow_possible_spam: false)
project.add_developer(user)
end
@@ -311,6 +312,8 @@ RSpec.describe API::ProjectSnippets do
allow_next_instance_of(Spam::AkismetService) do |instance|
allow(instance).to receive(:spam?).and_return(true)
end
+
+ stub_feature_flags(allow_possible_spam: false)
end
context 'when the snippet is private' do
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 94688833d88..7ad1ce0ede9 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -48,6 +48,7 @@ end
RSpec.describe API::Projects do
include ProjectForksHelper
+ include WorkhorseHelpers
include StubRequests
let_it_be(:user) { create(:user) }
@@ -1249,9 +1250,10 @@ RSpec.describe API::Projects do
stub_application_setting(import_sources: nil)
endpoint_url = "#{url}/info/refs?service=git-upload-pack"
- stub_full_request(endpoint_url, method: :get).to_return({ status: 200,
- body: '001e# service=git-upload-pack',
- headers: { 'Content-Type': 'application/x-git-upload-pack-advertisement' } })
+ stub_full_request(endpoint_url, method: :get).to_return(
+ { status: 200,
+ body: '001e# service=git-upload-pack',
+ headers: { 'Content-Type': 'application/x-git-upload-pack-advertisement' } })
project_params = { import_url: url, path: 'path-project-Foo', name: 'Foo Project' }
expect { post api('/projects', user), params: project_params }
@@ -1348,7 +1350,12 @@ RSpec.describe API::Projects do
it 'uploads avatar for project a project' do
project = attributes_for(:project, avatar: fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif'))
- post api('/projects', user), params: project
+ workhorse_form_with_file(
+ api('/projects', user),
+ method: :post,
+ file_key: :avatar,
+ params: project
+ )
project_id = json_response['id']
expect(json_response['avatar_url']).to eq("http://localhost/uploads/-/system/project/avatar/#{project_id}/banana_sample.gif")
@@ -1924,8 +1931,6 @@ RSpec.describe API::Projects do
end
describe "POST /projects/:id/uploads/authorize" do
- include WorkhorseHelpers
-
let(:headers) { workhorse_internal_api_request_header.merge({ 'HTTP_GITLAB_WORKHORSE' => 1 }) }
context 'with authorized user' do
@@ -3583,18 +3588,77 @@ RSpec.describe API::Projects do
end
end
- it 'updates avatar' do
- project_param = {
- avatar: fixture_file_upload('spec/fixtures/banana_sample.gif',
- 'image/gif')
- }
+ context 'with changes to the avatar' do
+ let_it_be(:avatar_file) { fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') }
+ let_it_be(:alternate_avatar_file) { fixture_file_upload('spec/fixtures/rails_sample.png', 'image/png') }
+ let_it_be(:project_with_avatar, reload: true) do
+ create(:project,
+ :private,
+ :repository,
+ name: 'project-with-avatar',
+ creator_id: user.id,
+ namespace: user.namespace,
+ avatar: avatar_file)
+ end
- put api("/projects/#{project3.id}", user), params: project_param
+ it 'uploads avatar to project without an avatar' do
+ workhorse_form_with_file(
+ api("/projects/#{project3.id}", user),
+ method: :put,
+ file_key: :avatar,
+ params: { avatar: avatar_file }
+ )
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['avatar_url']).to eq('http://localhost/uploads/'\
- '-/system/project/avatar/'\
- "#{project3.id}/banana_sample.gif")
+ aggregate_failures "testing response" do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['avatar_url']).to eq('http://localhost/uploads/'\
+ '-/system/project/avatar/'\
+ "#{project3.id}/banana_sample.gif")
+ end
+ end
+
+ it 'uploads and changes avatar to project with an avatar' do
+ workhorse_form_with_file(
+ api("/projects/#{project_with_avatar.id}", user),
+ method: :put,
+ file_key: :avatar,
+ params: { avatar: alternate_avatar_file }
+ )
+
+ aggregate_failures "testing response" do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['avatar_url']).to eq('http://localhost/uploads/'\
+ '-/system/project/avatar/'\
+ "#{project_with_avatar.id}/rails_sample.png")
+ end
+ end
+
+ it 'uploads and changes avatar to project among other changes' do
+ workhorse_form_with_file(
+ api("/projects/#{project_with_avatar.id}", user),
+ method: :put,
+ file_key: :avatar,
+ params: { description: 'changed description', avatar: avatar_file }
+ )
+
+ aggregate_failures "testing response" do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['description']).to eq('changed description')
+ expect(json_response['avatar_url']).to eq('http://localhost/uploads/'\
+ '-/system/project/avatar/'\
+ "#{project_with_avatar.id}/banana_sample.gif")
+ end
+ end
+
+ it 'removes avatar from project with an avatar' do
+ put api("/projects/#{project_with_avatar.id}", user), params: { avatar: '' }
+
+ aggregate_failures "testing response" do
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['avatar_url']).to be_nil
+ expect(project_with_avatar.reload.avatar_url).to be_nil
+ end
+ end
end
it 'updates auto_devops_deploy_strategy' do
@@ -4645,6 +4709,100 @@ RSpec.describe API::Projects do
end
end
+ describe 'GET /projects/:id/transfer_locations' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:source_group) { create(:group) }
+ let_it_be(:project) { create(:project, group: source_group) }
+
+ let(:params) { {} }
+
+ subject(:request) do
+ get api("/projects/#{project.id}/transfer_locations", user), params: params
+ end
+
+ context 'when the user has rights to transfer the project' do
+ let_it_be(:guest_group) { create(:group) }
+ let_it_be(:maintainer_group) { create(:group, name: 'maintainer group', path: 'maintainer-group') }
+ let_it_be(:owner_group) { create(:group, name: 'owner group', path: 'owner-group') }
+
+ before do
+ source_group.add_owner(user)
+ guest_group.add_guest(user)
+ maintainer_group.add_maintainer(user)
+ owner_group.add_owner(user)
+ end
+
+ it 'returns 200' do
+ request
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ end
+
+ it 'includes groups where the user has permissions to transfer a project to' do
+ request
+
+ expect(project_ids_from_response).to include(maintainer_group.id, owner_group.id)
+ end
+
+ it 'does not include groups where the user doesn not have permissions to transfer a project' do
+ request
+
+ expect(project_ids_from_response).not_to include(guest_group.id)
+ end
+
+ context 'with search' do
+ let(:params) { { search: 'maintainer' } }
+
+ it 'includes groups where the user has permissions to transfer a project to' do
+ request
+
+ expect(project_ids_from_response).to contain_exactly(maintainer_group.id)
+ end
+ end
+
+ context 'group shares' do
+ let_it_be(:shared_to_owner_group) { create(:group) }
+ let_it_be(:shared_to_guest_group) { create(:group) }
+
+ before do
+ create(:group_group_link, :owner,
+ shared_with_group: owner_group,
+ shared_group: shared_to_owner_group
+ )
+
+ create(:group_group_link, :guest,
+ shared_with_group: guest_group,
+ shared_group: shared_to_guest_group
+ )
+ end
+
+ it 'only includes groups arising from group shares where the user has permission to transfer a project to' do
+ request
+
+ expect(project_ids_from_response).to include(shared_to_owner_group.id)
+ expect(project_ids_from_response).not_to include(shared_to_guest_group.id)
+ end
+ end
+
+ def project_ids_from_response
+ json_response.map { |project| project['id'] }
+ end
+ end
+
+ context 'when the user does not have permissions to transfer the project' do
+ before do
+ source_group.add_developer(user)
+ end
+
+ it 'returns 403' do
+ request
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+ end
+
describe 'GET /projects/:id/storage' do
context 'when unauthenticated' do
it 'does not return project storage data' do
diff --git a/spec/requests/api/releases_spec.rb b/spec/requests/api/releases_spec.rb
index 1d9e3a6c887..754b77af60e 100644
--- a/spec/requests/api/releases_spec.rb
+++ b/spec/requests/api/releases_spec.rb
@@ -573,6 +573,224 @@ RSpec.describe API::Releases do
end
end
+ describe 'GET /projects/:id/releases/:tag_name/downloads/*file_path' do
+ let!(:release) { create(:release, project: project, tag: 'v0.1', author: maintainer) }
+ let!(:link) { create(:release_link, release: release, url: "#{url}#{filepath}", filepath: filepath) }
+ let(:filepath) { '/bin/bigfile.exe' }
+ let(:url) { 'https://google.com/-/jobs/140463678/artifacts/download' }
+
+ context 'with an invalid release tag' do
+ it 'returns 404 for maintater' do
+ get api("/projects/#{project.id}/releases/v0.2/downloads#{filepath}", maintainer)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response['message']).to eq('404 Not Found')
+ end
+
+ it 'returns project not found for no user' do
+ get api("/projects/#{project.id}/releases/v0.2/downloads#{filepath}", nil)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response['message']).to eq('404 Project Not Found')
+ end
+
+ it 'returns forbidden for guest' do
+ get api("/projects/#{project.id}/releases/v0.2/downloads#{filepath}", guest)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'with a valid release tag' do
+ context 'when filepath is provided' do
+ context 'when filepath exists' do
+ it 'redirects to the file download URL' do
+ get api("/projects/#{project.id}/releases/v0.1/downloads#{filepath}", maintainer)
+
+ expect(response).to redirect_to("#{url}#{filepath}")
+ end
+
+ it 'redirects to the file download URL when using JOB-TOKEN auth' do
+ job = create(:ci_build, :running, project: project, user: maintainer)
+
+ get api("/projects/#{project.id}/releases/v0.1/downloads#{filepath}"), params: { job_token: job.token }
+
+ expect(response).to redirect_to("#{url}#{filepath}")
+ end
+
+ context 'when user is a guest' do
+ it 'responds 403 Forbidden' do
+ get api("/projects/#{project.id}/releases/v0.1/downloads#{filepath}", guest)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ context 'when project is public' do
+ let(:project) { create(:project, :repository, :public) }
+
+ it 'responds 200 OK' do
+ get api("/projects/#{project.id}/releases/v0.1/downloads#{filepath}", guest)
+
+ expect(response).to redirect_to("#{url}#{filepath}")
+ end
+ end
+ end
+ end
+
+ context 'when filepath does not exists' do
+ it 'returns 404 for maintater' do
+ get api("/projects/#{project.id}/releases/v0.1/downloads/bin/not_existing.exe", maintainer)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response['message']).to eq('404 Not found')
+ end
+
+ it 'returns project not found for no user' do
+ get api("/projects/#{project.id}/releases/v0.1/downloads/bin/not_existing.exe", nil)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ expect(json_response['message']).to eq('404 Project Not Found')
+ end
+
+ it 'returns forbidden for guest' do
+ get api("/projects/#{project.id}/releases/v0.1/downloads/bin/not_existing.exe", guest)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+ end
+
+ context 'when filepath is not provided' do
+ it 'returns 404 for maintater' do
+ get api("/projects/#{project.id}/releases/v0.1/downloads", maintainer)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'returns project not found for no user' do
+ get api("/projects/#{project.id}/releases/v0.1/downloads", nil)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'returns forbidden for guest' do
+ get api("/projects/#{project.id}/releases/v0.1/downloads", guest)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+ end
+ end
+
+ describe 'GET /projects/:id/releases/permalink/latest' do
+ context 'when there is no release' do
+ it 'returns not found' do
+ get api("/projects/#{project.id}/releases/permalink/latest", maintainer)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'returns not found when using JOB-TOKEN auth' do
+ job = create(:ci_build, :running, project: project, user: maintainer)
+
+ get api("/projects/#{project.id}/releases/permalink/latest"), params: { job_token: job.token }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when there are more than one release' do
+ let!(:release_a) do
+ create(:release,
+ project: project,
+ tag: 'v0.1',
+ author: maintainer,
+ description: 'This is v0.1',
+ released_at: 3.days.ago)
+ end
+
+ let!(:release_b) do
+ create(:release,
+ project: project,
+ tag: 'v0.2',
+ author: maintainer,
+ description: 'This is v0.2',
+ released_at: 2.days.ago)
+ end
+
+ it 'redirects to the latest release tag' do
+ get api("/projects/#{project.id}/releases/permalink/latest", maintainer)
+
+ uri = URI(response.header["Location"])
+
+ expect(response).to have_gitlab_http_status(:redirect)
+ expect(uri.path).to eq("/api/v4/projects/#{project.id}/releases/#{release_b.tag}")
+ end
+
+ it 'redirects to the latest release tag when using JOB-TOKEN auth' do
+ job = create(:ci_build, :running, project: project, user: maintainer)
+
+ get api("/projects/#{project.id}/releases/permalink/latest"), params: { job_token: job.token }
+
+ uri = URI(response.header["Location"])
+
+ expect(response).to have_gitlab_http_status(:redirect)
+ expect(uri.path).to eq("/api/v4/projects/#{project.id}/releases/#{release_b.tag}")
+ end
+
+ context 'when there are query parameters present' do
+ it 'includes the query params on the redirection' do
+ get api("/projects/#{project.id}/releases/permalink/latest", maintainer), params: { include_html_description: true, other_param: "aaa" }
+
+ uri = URI(response.header["Location"])
+ query_params = Rack::Utils.parse_nested_query(uri.query)
+
+ expect(response).to have_gitlab_http_status(:redirect)
+ expect(uri.path).to eq("/api/v4/projects/#{project.id}/releases/#{release_b.tag}")
+ expect(query_params).to include({
+ "include_html_description" => "true",
+ "other_param" => "aaa"
+ })
+ end
+
+ it 'discards the `order_by` query param' do
+ get api("/projects/#{project.id}/releases/permalink/latest", maintainer), params: { order_by: 'something', other_param: "aaa" }
+
+ uri = URI(response.header["Location"])
+ query_params = Rack::Utils.parse_nested_query(uri.query)
+
+ expect(response).to have_gitlab_http_status(:redirect)
+ expect(uri.path).to eq("/api/v4/projects/#{project.id}/releases/#{release_b.tag}")
+ expect(query_params).to include({
+ "other_param" => "aaa"
+ })
+ expect(query_params).not_to include({
+ "order_by" => "something"
+ })
+ end
+ end
+
+ context 'when downloading a release asset' do
+ it 'redirects to the right endpoint keeping the suffix_path' do
+ get api("/projects/#{project.id}/releases/permalink/latest/downloads/bin/example.exe", maintainer)
+
+ uri = URI(response.header["Location"])
+
+ expect(response).to have_gitlab_http_status(:redirect)
+ expect(uri.path).to eq("/api/v4/projects/#{project.id}/releases/#{release_b.tag}/downloads/bin/example.exe")
+ end
+
+ it 'returns error when there is path traversal in suffix path' do
+ get api("/projects/#{project.id}/releases/permalink/latest/downloads/bin/../../../../../../../password.txt", maintainer)
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+
+ expect(json_response['error']).to eq('suffix_path should be a valid file path')
+ end
+ end
+ end
+ end
+
describe 'POST /projects/:id/releases' do
let(:params) do
{
diff --git a/spec/requests/api/resource_access_tokens_spec.rb b/spec/requests/api/resource_access_tokens_spec.rb
index 369a8c1b0ab..d9a12e7e148 100644
--- a/spec/requests/api/resource_access_tokens_spec.rb
+++ b/spec/requests/api/resource_access_tokens_spec.rb
@@ -243,27 +243,65 @@ RSpec.describe API::ResourceAccessTokens do
end
context "when the user has valid permissions" do
- it "deletes the #{source_type} access token from the #{source_type}" do
- delete_token
+ context 'when user_destroy_with_limited_execution_time_worker is enabled' do
+ it "deletes the #{source_type} access token from the #{source_type}" do
+ delete_token
- expect(response).to have_gitlab_http_status(:no_content)
- expect(User.exists?(project_bot.id)).to be_falsy
- end
+ expect(response).to have_gitlab_http_status(:no_content)
+ expect(
+ Users::GhostUserMigration.where(user: project_bot,
+ initiator_user: user)
+ ).to be_exists
+ end
- context "when using #{source_type} access token to DELETE other #{source_type} access token" do
- let_it_be(:other_project_bot) { create(:user, :project_bot) }
- let_it_be(:other_token) { create(:personal_access_token, user: other_project_bot) }
- let_it_be(:token_id) { other_token.id }
+ context "when using #{source_type} access token to DELETE other #{source_type} access token" do
+ let_it_be(:other_project_bot) { create(:user, :project_bot) }
+ let_it_be(:other_token) { create(:personal_access_token, user: other_project_bot) }
+ let_it_be(:token_id) { other_token.id }
+
+ before do
+ resource.add_maintainer(other_project_bot)
+ end
+
+ it "deletes the #{source_type} access token from the #{source_type}" do
+ delete_token
+ expect(response).to have_gitlab_http_status(:no_content)
+ expect(
+ Users::GhostUserMigration.where(user: other_project_bot,
+ initiator_user: user)
+ ).to be_exists
+ end
+ end
+ end
+
+ context 'when user_destroy_with_limited_execution_time_worker is disabled' do
before do
- resource.add_maintainer(other_project_bot)
+ stub_feature_flags(user_destroy_with_limited_execution_time_worker: false)
end
it "deletes the #{source_type} access token from the #{source_type}" do
delete_token
expect(response).to have_gitlab_http_status(:no_content)
- expect(User.exists?(other_project_bot.id)).to be_falsy
+ expect(User.exists?(project_bot.id)).to be_falsy
+ end
+
+ context "when using #{source_type} access token to DELETE other #{source_type} access token" do
+ let_it_be(:other_project_bot) { create(:user, :project_bot) }
+ let_it_be(:other_token) { create(:personal_access_token, user: other_project_bot) }
+ let_it_be(:token_id) { other_token.id }
+
+ before do
+ resource.add_maintainer(other_project_bot)
+ end
+
+ it "deletes the #{source_type} access token from the #{source_type}" do
+ delete_token
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ expect(User.exists?(other_project_bot.id)).to be_falsy
+ end
end
end
diff --git a/spec/requests/api/resource_state_events_spec.rb b/spec/requests/api/resource_state_events_spec.rb
index 46ca9874395..5f756bc6c63 100644
--- a/spec/requests/api/resource_state_events_spec.rb
+++ b/spec/requests/api/resource_state_events_spec.rb
@@ -6,87 +6,8 @@ RSpec.describe API::ResourceStateEvents do
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, :public, namespace: user.namespace) }
- before_all do
- project.add_developer(user)
- end
-
- shared_examples 'resource_state_events API' do |parent_type, eventable_type, id_name|
- describe "GET /#{parent_type}/:id/#{eventable_type}/:noteable_id/resource_state_events" do
- let!(:event) { create_event }
-
- it "returns an array of resource state events" do
- url = "/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_state_events"
- get api(url, user)
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to include_pagination_headers
- expect(json_response).to be_an Array
- expect(json_response.first['id']).to eq(event.id)
- expect(json_response.first['state']).to eq(event.state.to_s)
- end
-
- it "returns a 404 error when eventable id not found" do
- get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{non_existing_record_id}/resource_state_events", user)
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
-
- it "returns 404 when not authorized" do
- parent.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
- private_user = create(:user)
-
- get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_state_events", private_user)
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
- describe "GET /#{parent_type}/:id/#{eventable_type}/:noteable_id/resource_state_events/:event_id" do
- let!(:event) { create_event }
-
- it "returns a resource state event by id" do
- get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_state_events/#{event.id}", user)
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['id']).to eq(event.id)
- expect(json_response['state']).to eq(event.state.to_s)
- end
-
- it "returns 404 when not authorized" do
- parent.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
- private_user = create(:user)
-
- get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_state_events/#{event.id}", private_user)
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
-
- it "returns a 404 error if resource state event not found" do
- get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_state_events/#{non_existing_record_id}", user)
-
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
-
- describe 'pagination' do
- # https://gitlab.com/gitlab-org/gitlab/-/issues/220192
- it 'returns the second page' do
- create_event
- event2 = create_event
-
- get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}/resource_state_events?page=2&per_page=1", user)
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to include_pagination_headers
- expect(response.headers['X-Total']).to eq '2'
- expect(json_response.count).to eq(1)
- expect(json_response.first['id']).to eq(event2.id)
- end
- end
-
- def create_event(state: :opened)
- create(:resource_state_event, eventable.class.name.underscore => eventable, state: state)
- end
+ before do
+ parent.add_developer(user)
end
context 'when eventable is an Issue' do
diff --git a/spec/requests/api/rpm_project_packages_spec.rb b/spec/requests/api/rpm_project_packages_spec.rb
new file mode 100644
index 00000000000..6a646c26fd2
--- /dev/null
+++ b/spec/requests/api/rpm_project_packages_spec.rb
@@ -0,0 +1,250 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe API::RpmProjectPackages do
+ include HttpBasicAuthHelpers
+ include WorkhorseHelpers
+
+ include_context 'workhorse headers'
+
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be_with_reload(:project) { create(:project, :public) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:personal_access_token) { create(:personal_access_token, user: user) }
+ 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(:headers) { {} }
+ let(:package_name) { 'rpm-package.0-1.x86_64.rpm' }
+ let(:package_file_id) { 1 }
+
+ shared_examples 'rejects rpm packages access' do |status|
+ it_behaves_like 'returning response status', status
+
+ if status == :unauthorized
+ it 'has the correct response header' do
+ subject
+
+ expect(response.headers['WWW-Authenticate']).to eq 'Basic realm="GitLab Packages Registry"'
+ end
+ end
+ end
+
+ shared_examples 'process rpm packages upload/download' do |status|
+ it_behaves_like 'returning response status', status
+ end
+
+ shared_examples 'a deploy token for RPM requests' do
+ context 'with deploy token headers' do
+ before do
+ project.update_column(:visibility_level, Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ let(:headers) { basic_auth_header(deploy_token.username, deploy_token.token) }
+
+ context 'when token is valid' do
+ it_behaves_like 'returning response status', :not_found
+ end
+
+ context 'when token is invalid' do
+ let(:headers) { basic_auth_header(deploy_token.username, 'bar') }
+
+ it_behaves_like 'returning response status', :unauthorized
+ end
+ end
+ end
+
+ shared_examples 'a job token for RPM requests' do
+ context 'with job token headers' do
+ let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, job.token) }
+
+ before do
+ project.update_column(:visibility_level, Gitlab::VisibilityLevel::PRIVATE)
+ project.add_developer(user)
+ end
+
+ context 'with valid token' do
+ it_behaves_like 'returning response status', :not_found
+ end
+
+ context 'with invalid token' do
+ let(:headers) { basic_auth_header(::Gitlab::Auth::CI_JOB_USER, 'bar') }
+
+ it_behaves_like 'returning response status', :unauthorized
+ end
+
+ context 'with invalid user' do
+ let(:headers) { basic_auth_header('foo', job.token) }
+
+ it_behaves_like 'returning response status', :unauthorized
+ end
+ end
+ end
+
+ shared_examples 'a user token for RPM requests' do
+ context 'with valid project' do
+ where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
+ 'PUBLIC' | :developer | true | true | 'process rpm packages upload/download' | :not_found
+ 'PUBLIC' | :guest | true | true | 'process rpm packages upload/download' | :forbidden
+ 'PUBLIC' | :developer | true | false | 'rejects rpm packages access' | :unauthorized
+ 'PUBLIC' | :guest | true | false | 'rejects rpm packages access' | :unauthorized
+ 'PUBLIC' | :developer | false | true | 'process rpm packages upload/download' | :not_found
+ 'PUBLIC' | :guest | false | true | 'process rpm packages upload/download' | :not_found
+ 'PUBLIC' | :developer | false | false | 'rejects rpm packages access' | :unauthorized
+ 'PUBLIC' | :guest | false | false | 'rejects rpm packages access' | :unauthorized
+ 'PUBLIC' | :anonymous | false | true | 'process rpm packages upload/download' | :unauthorized
+ 'PRIVATE' | :developer | true | true | 'process rpm packages upload/download' | :not_found
+ 'PRIVATE' | :guest | true | true | 'rejects rpm packages access' | :forbidden
+ 'PRIVATE' | :developer | true | false | 'rejects rpm packages access' | :unauthorized
+ 'PRIVATE' | :guest | true | false | 'rejects rpm packages access' | :unauthorized
+ 'PRIVATE' | :developer | false | true | 'rejects rpm packages access' | :not_found
+ 'PRIVATE' | :guest | false | true | 'rejects rpm packages access' | :not_found
+ 'PRIVATE' | :developer | false | false | 'rejects rpm packages access' | :unauthorized
+ 'PRIVATE' | :guest | false | false | 'rejects rpm packages access' | :unauthorized
+ 'PRIVATE' | :anonymous | false | true | 'rejects rpm packages access' | :unauthorized
+ end
+
+ with_them do
+ let(:token) { user_token ? personal_access_token.token : 'wrong' }
+ let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
+
+ subject { get api(url), headers: headers }
+
+ before do
+ project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility_level))
+ project.send("add_#{user_role}", user) if member && user_role != :anonymous
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:expected_status]
+ end
+ end
+ end
+
+ describe 'GET /api/v4/projects/:project_id/packages/rpm/repodata/:filename' do
+ let(:url) { "/projects/#{project.id}/packages/rpm/repodata/#{package_name}" }
+
+ subject { get api(url), headers: headers }
+
+ it_behaves_like 'a job token for RPM requests'
+ it_behaves_like 'a deploy token for RPM requests'
+ it_behaves_like 'a user token for RPM requests'
+ end
+
+ describe 'GET /api/v4/projects/:id/packages/rpm/:package_file_id/:filename' do
+ let(:url) { "/projects/#{project.id}/packages/rpm/#{package_file_id}/#{package_name}" }
+
+ subject { get api(url), headers: headers }
+
+ it_behaves_like 'a job token for RPM requests'
+ it_behaves_like 'a deploy token for RPM requests'
+ it_behaves_like 'a user token for RPM requests'
+ end
+
+ describe 'POST /api/v4/projects/:project_id/packages/rpm' do
+ 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') }
+
+ subject { post api(url), params: { file: file_upload }, headers: headers }
+
+ context 'with user token' do
+ context 'with valid project' do
+ where(:visibility_level, :user_role, :member, :user_token, :shared_examples_name, :expected_status) do
+ 'PUBLIC' | :developer | true | true | 'process rpm packages upload/download' | :not_found
+ 'PUBLIC' | :guest | true | true | 'rejects rpm packages access' | :forbidden
+ 'PUBLIC' | :developer | true | false | 'rejects rpm packages access' | :unauthorized
+ 'PUBLIC' | :guest | true | false | 'rejects rpm packages access' | :unauthorized
+ 'PUBLIC' | :developer | false | true | 'rejects rpm packages access' | :not_found
+ 'PUBLIC' | :guest | false | true | 'rejects rpm packages access' | :not_found
+ 'PUBLIC' | :developer | false | false | 'rejects rpm packages access' | :unauthorized
+ 'PUBLIC' | :guest | false | false | 'rejects rpm packages access' | :unauthorized
+ 'PUBLIC' | :anonymous | false | true | 'rejects rpm packages access' | :unauthorized
+ 'PRIVATE' | :developer | true | true | 'process rpm packages upload/download' | :not_found
+ 'PRIVATE' | :guest | true | true | 'rejects rpm packages access' | :forbidden
+ 'PRIVATE' | :developer | true | false | 'rejects rpm packages access' | :unauthorized
+ 'PRIVATE' | :guest | true | false | 'rejects rpm packages access' | :unauthorized
+ 'PRIVATE' | :developer | false | true | 'rejects rpm packages access' | :not_found
+ 'PRIVATE' | :guest | false | true | 'rejects rpm packages access' | :not_found
+ 'PRIVATE' | :developer | false | false | 'rejects rpm packages access' | :unauthorized
+ 'PRIVATE' | :guest | false | false | 'rejects rpm packages access' | :unauthorized
+ 'PRIVATE' | :anonymous | false | true | 'rejects rpm packages access' | :unauthorized
+ end
+
+ with_them do
+ let(:token) { user_token ? personal_access_token.token : 'wrong' }
+ let(:headers) { user_role == :anonymous ? {} : basic_auth_header(user.username, token) }
+
+ before do
+ project.update_column(:visibility_level, Gitlab::VisibilityLevel.level_value(visibility_level))
+ project.send("add_#{user_role}", user) if member && user_role != :anonymous
+ end
+
+ it_behaves_like params[:shared_examples_name], params[:expected_status]
+ end
+ end
+
+ context 'when user can upload file' do
+ before do
+ project.add_developer(user)
+ end
+
+ let(:headers) { basic_auth_header(user.username, personal_access_token.token).merge(workhorse_headers) }
+
+ context 'when file size too large' do
+ before do
+ allow_next_instance_of(UploadedFile) do |uploaded_file|
+ allow(uploaded_file).to receive(:size).and_return(project.actual_limits.rpm_max_file_size + 1)
+ end
+ 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(/File is too large/)
+ end
+ end
+ end
+
+ def upload_file(params: {}, request_headers: headers)
+ url = "/projects/#{project.id}/packages/rpm"
+ workhorse_finalize(
+ api(url),
+ method: :post,
+ file_key: :file,
+ params: params,
+ headers: request_headers,
+ send_rewritten_field: true
+ )
+ end
+ end
+
+ it_behaves_like 'a deploy token for RPM requests'
+ it_behaves_like 'a job token for RPM requests'
+ end
+
+ describe 'POST /api/v4/projects/:project_id/packages/rpm/authorize' do
+ let(:url) { api("/projects/#{project.id}/packages/rpm/authorize") }
+
+ subject { post(url, headers: headers) }
+
+ it_behaves_like 'returning response status', :not_found
+
+ context 'when feature flag is disabled' do
+ before do
+ stub_feature_flags(rpm_packages: false)
+ end
+
+ it_behaves_like 'returning response status', :not_found
+ end
+
+ context 'when package feature is disabled' do
+ before do
+ stub_config(packages: { enabled: false })
+ end
+
+ it_behaves_like 'returning response status', :not_found
+ end
+ end
+end
diff --git a/spec/requests/api/search_spec.rb b/spec/requests/api/search_spec.rb
index 6034d26f1d2..05f38aff6ab 100644
--- a/spec/requests/api/search_spec.rb
+++ b/spec/requests/api/search_spec.rb
@@ -9,6 +9,7 @@ RSpec.describe API::Search do
let_it_be(:repo_project) { create(:project, :public, :repository, group: group) }
before do
+ allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).and_return(0)
allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit).and_return(1000)
allow(Gitlab::ApplicationRateLimiter).to receive(:threshold).with(:search_rate_limit_unauthenticated).and_return(1000)
end
@@ -351,6 +352,43 @@ RSpec.describe API::Search do
end
end
+ it 'increments the custom search sli apdex' do
+ expect(Gitlab::Metrics::GlobalSearchSlis).to receive(:record_apdex).with(
+ elapsed: a_kind_of(Numeric),
+ search_scope: 'issues',
+ search_type: 'basic',
+ search_level: 'global'
+ )
+
+ get api(endpoint, user), params: { scope: 'issues', search: 'john doe' }
+ end
+
+ it 'increments the custom search sli error rate with error false if no error occurred' do
+ expect(Gitlab::Metrics::GlobalSearchSlis).to receive(:record_error_rate).with(
+ error: false,
+ search_scope: 'issues',
+ search_type: 'basic',
+ search_level: 'global'
+ )
+
+ get api(endpoint, user), params: { scope: 'issues', search: 'john doe' }
+ end
+
+ it 'increments the custom search sli error rate with error true if an error occurred' do
+ allow_next_instance_of(SearchService) do |service|
+ allow(service).to receive(:search_results).and_raise(ActiveRecord::QueryCanceled)
+ end
+
+ expect(Gitlab::Metrics::GlobalSearchSlis).to receive(:record_error_rate).with(
+ error: true,
+ search_scope: 'issues',
+ search_type: 'basic',
+ search_level: 'global'
+ )
+
+ get api(endpoint, user), params: { scope: 'issues', search: 'john doe' }
+ end
+
it 'sets global search information for logging' do
expect(Gitlab::Instrumentation::GlobalSearchApi).to receive(:set_information).with(
type: 'basic',
@@ -618,7 +656,7 @@ RSpec.describe API::Search do
context 'when requesting basic search' do
it 'passes the parameter to search service' do
- expect(SearchService).to receive(:new).with(user, hash_including(basic_search: 'true'))
+ expect(SearchService).to receive(:new).with(user, hash_including(basic_search: 'true')).twice
get api(endpoint, user), params: { scope: 'issues', search: 'awesome', basic_search: 'true' }
end
diff --git a/spec/requests/api/settings_spec.rb b/spec/requests/api/settings_spec.rb
index 6f0d5827a80..315c76c8ac3 100644
--- a/spec/requests/api/settings_spec.rb
+++ b/spec/requests/api/settings_spec.rb
@@ -56,6 +56,10 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
expect(json_response['project_runner_token_expiration_interval']).to be_nil
expect(json_response['max_export_size']).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)
end
end
@@ -148,7 +152,11 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
user_deactivation_emails_enabled: false,
admin_mode: true,
suggest_pipeline_enabled: false,
- users_get_by_id_limit: 456
+ users_get_by_id_limit: 456,
+ delete_inactive_projects: true,
+ inactive_projects_delete_after_months: 24,
+ inactive_projects_min_size_mb: 10,
+ inactive_projects_send_warning_email_after_months: 12
}
expect(response).to have_gitlab_http_status(:ok)
@@ -205,6 +213,10 @@ RSpec.describe API::Settings, 'Settings', :do_not_mock_admin_mode_setting do
expect(json_response['user_deactivation_emails_enabled']).to be(false)
expect(json_response['suggest_pipeline_enabled']).to be(false)
expect(json_response['users_get_by_id_limit']).to eq(456)
+ expect(json_response['delete_inactive_projects']).to be(true)
+ expect(json_response['inactive_projects_delete_after_months']).to eq(24)
+ expect(json_response['inactive_projects_min_size_mb']).to eq(10)
+ expect(json_response['inactive_projects_send_warning_email_after_months']).to eq(12)
end
end
diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb
index 0dd6e484e8d..031bcb612f4 100644
--- a/spec/requests/api/snippets_spec.rb
+++ b/spec/requests/api/snippets_spec.rb
@@ -340,6 +340,7 @@ RSpec.describe API::Snippets, factory_default: :keep do
allow_next_instance_of(Spam::AkismetService) do |instance|
allow(instance).to receive(:spam?).and_return(true)
end
+ stub_feature_flags(allow_possible_spam: false)
end
context 'when the snippet is private' do
@@ -405,6 +406,7 @@ RSpec.describe API::Snippets, factory_default: :keep do
allow_next_instance_of(Spam::AkismetService) do |instance|
allow(instance).to receive(:spam?).and_return(true)
end
+ stub_feature_flags(allow_possible_spam: false)
end
context 'when the snippet is private' do
diff --git a/spec/requests/api/suggestions_spec.rb b/spec/requests/api/suggestions_spec.rb
index 7f53d379af5..2393a268693 100644
--- a/spec/requests/api/suggestions_spec.rb
+++ b/spec/requests/api/suggestions_spec.rb
@@ -34,15 +34,14 @@ RSpec.describe API::Suggestions do
end
let(:diff_note2) do
- create(:diff_note_on_merge_request, noteable: merge_request,
- position: position2,
- project: project)
+ create(:diff_note_on_merge_request, noteable: merge_request, position: position2, project: project)
end
let(:suggestion) do
- create(:suggestion, note: diff_note,
- from_content: " raise RuntimeError, \"System commands must be given as an array of strings\"\n",
- to_content: " raise RuntimeError, 'Explosion'\n # explosion?")
+ create(:suggestion,
+ note: diff_note,
+ from_content: " raise RuntimeError, \"System commands must be given as an array of strings\"\n",
+ to_content: " raise RuntimeError, 'Explosion'\n # explosion?")
end
let(:unappliable_suggestion) do
@@ -119,8 +118,8 @@ RSpec.describe API::Suggestions do
describe "PUT /suggestions/batch_apply" do
let(:suggestion2) do
create(:suggestion, note: diff_note2,
- from_content: " \"PWD\" => path\n",
- to_content: " *** FOO ***\n")
+ from_content: " \"PWD\" => path\n",
+ to_content: " *** FOO ***\n")
end
let(:url) { "/suggestions/batch_apply" }
diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb
index e81e9e0bf2f..b62fbaead6f 100644
--- a/spec/requests/api/tags_spec.rb
+++ b/spec/requests/api/tags_spec.rb
@@ -17,10 +17,6 @@ RSpec.describe API::Tags do
end
describe 'GET /projects/:id/repository/tags', :use_clean_rails_memory_store_caching do
- before do
- stub_feature_flags(tag_list_keyset_pagination: false)
- end
-
let(:route) { "/projects/#{project_id}/repository/tags" }
context 'sorting' do
@@ -59,6 +55,18 @@ RSpec.describe API::Tags do
expect(json_response.map { |tag| tag['name'] }).to eq(ordered_by_name)
end
+
+ it 'sorts by version in ascending order when requested' do
+ repository = project.repository
+ repository.add_tag(user, 'v1.2.0', repository.commit.id)
+ repository.add_tag(user, 'v1.10.0', repository.commit.id)
+
+ get api("#{route}?order_by=version&sort=asc", current_user)
+
+ ordered_by_version = VersionSorter.sort(project.repository.tags.map { |tag| tag.name })
+
+ expect(json_response.map { |tag| tag['name'] }).to eq(ordered_by_version)
+ end
end
context 'searching' do
@@ -154,50 +162,44 @@ RSpec.describe API::Tags do
end
end
- context 'with keyset pagination on', :aggregate_errors do
- before do
- stub_feature_flags(tag_list_keyset_pagination: true)
- end
-
- context 'with keyset pagination option' do
- let(:base_params) { { pagination: 'keyset' } }
+ context 'with keyset pagination option', :aggregate_errors do
+ let(:base_params) { { pagination: 'keyset' } }
- context 'with gitaly pagination params' do
- context 'with high limit' do
- let(:params) { base_params.merge(per_page: 100) }
+ context 'with gitaly pagination params' do
+ context 'with high limit' do
+ let(:params) { base_params.merge(per_page: 100) }
- it 'returns all repository tags' do
- get api(route, user), params: params
+ it 'returns all repository tags' do
+ get api(route, user), params: params
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('public_api/v4/tags')
- expect(response.headers).not_to include('Link')
- tag_names = json_response.map { |x| x['name'] }
- expect(tag_names).to match_array(project.repository.tag_names)
- end
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('public_api/v4/tags')
+ expect(response.headers).not_to include('Link')
+ tag_names = json_response.map { |x| x['name'] }
+ expect(tag_names).to match_array(project.repository.tag_names)
end
+ end
- context 'with low limit' do
- let(:params) { base_params.merge(per_page: 2) }
+ context 'with low limit' do
+ let(:params) { base_params.merge(per_page: 2) }
- it 'returns limited repository tags' do
- get api(route, user), params: params
+ it 'returns limited repository tags' do
+ get api(route, user), params: params
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to match_response_schema('public_api/v4/tags')
- expect(response.headers).to include('Link')
- tag_names = json_response.map { |x| x['name'] }
- expect(tag_names).to match_array(%w(v1.1.0 v1.1.1))
- end
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema('public_api/v4/tags')
+ expect(response.headers).to include('Link')
+ tag_names = json_response.map { |x| x['name'] }
+ expect(tag_names).to match_array(%w(v1.1.0 v1.1.1))
end
+ end
- context 'with missing page token' do
- let(:params) { base_params.merge(page_token: 'unknown') }
+ context 'with missing page token' do
+ let(:params) { base_params.merge(page_token: 'unknown') }
- it_behaves_like '422 response' do
- let(:request) { get api(route, user), params: params }
- let(:message) { 'Invalid page token: refs/tags/unknown' }
- end
+ it_behaves_like '422 response' do
+ let(:request) { get api(route, user), params: params }
+ let(:message) { 'Invalid page token: refs/tags/unknown' }
end
end
end
diff --git a/spec/requests/api/topics_spec.rb b/spec/requests/api/topics_spec.rb
index 72221e3fb6a..1ad6f876fab 100644
--- a/spec/requests/api/topics_spec.rb
+++ b/spec/requests/api/topics_spec.rb
@@ -317,4 +317,66 @@ RSpec.describe API::Topics do
end
end
end
+
+ describe 'POST /topics/merge', :aggregate_failures do
+ context 'as administrator' do
+ let_it_be(:api_url) { api('/topics/merge', admin) }
+
+ it 'merge topics' do
+ post api_url, params: { source_topic_id: topic_3.id, target_topic_id: topic_2.id }
+
+ expect(response).to have_gitlab_http_status(:created)
+ expect { topic_2.reload }.not_to raise_error
+ expect { topic_3.reload }.to raise_error(ActiveRecord::RecordNotFound)
+ expect(json_response['id']).to eq(topic_2.id)
+ expect(json_response['total_projects_count']).to eq(topic_2.total_projects_count)
+ end
+
+ it 'returns 404 for non existing source topic id' do
+ post api_url, params: { source_topic_id: non_existing_record_id, target_topic_id: topic_2.id }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'returns 404 for non existing target topic id' do
+ post api_url, params: { source_topic_id: topic_3.id, target_topic_id: non_existing_record_id }
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it 'returns 400 for identical topic ids' do
+ post api_url, params: { source_topic_id: topic_2.id, target_topic_id: topic_2.id }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to eql('The source topic and the target topic are identical.')
+ end
+
+ it 'returns 400 if merge failed' do
+ allow_next_found_instance_of(Projects::Topic) do |topic|
+ allow(topic).to receive(:destroy!).and_raise(ActiveRecord::RecordNotDestroyed)
+ end
+
+ post api_url, params: { source_topic_id: topic_3.id, target_topic_id: topic_2.id }
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).to eql('Topics could not be merged!')
+ end
+ end
+
+ context 'as normal user' do
+ it 'returns 403 Forbidden' do
+ post api('/topics/merge', user), params: { source_topic_id: topic_3.id, target_topic_id: topic_2.id }
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
+ context 'as anonymous' do
+ it 'returns 401 Unauthorized' do
+ post api('/topics/merge'), params: { source_topic_id: topic_3.id, target_topic_id: topic_2.id }
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+ end
end
diff --git a/spec/requests/api/unleash_spec.rb b/spec/requests/api/unleash_spec.rb
index 3ee895d9421..51c567309b7 100644
--- a/spec/requests/api/unleash_spec.rb
+++ b/spec/requests/api/unleash_spec.rb
@@ -218,8 +218,7 @@ RSpec.describe API::Unleash do
context 'with version 2 feature flags' do
it 'does not return a flag without any strategies' do
- create(:operations_feature_flag, project: project,
- name: 'feature1', active: true, version: 2)
+ create(:operations_feature_flag, project: project, name: 'feature1', active: true, version: 2)
get api(features_url), headers: { 'UNLEASH-INSTANCEID' => client.token, 'UNLEASH-APPNAME' => 'production' }
@@ -228,10 +227,8 @@ RSpec.describe API::Unleash do
end
it 'returns a flag with a default strategy' do
- feature_flag = create(:operations_feature_flag, project: project,
- name: 'feature1', active: true, version: 2)
- strategy = create(:operations_strategy, feature_flag: feature_flag,
- name: 'default', parameters: {})
+ feature_flag = create(:operations_feature_flag, project: project, name: 'feature1', active: true, version: 2)
+ strategy = create(:operations_strategy, feature_flag: feature_flag, name: 'default', parameters: {})
create(:operations_scope, strategy: strategy, environment_scope: 'production')
get api(features_url), headers: { 'UNLEASH-INSTANCEID' => client.token, 'UNLEASH-APPNAME' => 'production' }
@@ -248,10 +245,9 @@ RSpec.describe API::Unleash do
end
it 'returns a flag with a userWithId strategy' do
- feature_flag = create(:operations_feature_flag, project: project,
- name: 'feature1', active: true, version: 2)
- strategy = create(:operations_strategy, feature_flag: feature_flag,
- name: 'userWithId', parameters: { userIds: 'user123,user456' })
+ feature_flag = create(:operations_feature_flag, project: project, name: 'feature1', active: true, version: 2)
+ strategy = create(:operations_strategy,
+ feature_flag: feature_flag, name: 'userWithId', parameters: { userIds: 'user123,user456' })
create(:operations_scope, strategy: strategy, environment_scope: 'production')
get api(features_url), headers: { 'UNLEASH-INSTANCEID' => client.token, 'UNLEASH-APPNAME' => 'production' }
@@ -268,12 +264,13 @@ RSpec.describe API::Unleash do
end
it 'returns a flag with multiple strategies' do
- feature_flag = create(:operations_feature_flag, project: project,
- name: 'feature1', active: true, version: 2)
- strategy_a = create(:operations_strategy, feature_flag: feature_flag,
- name: 'userWithId', parameters: { userIds: 'user_a,user_b' })
- strategy_b = create(:operations_strategy, feature_flag: feature_flag,
- name: 'gradualRolloutUserId', parameters: { groupId: 'default', percentage: '45' })
+ feature_flag = create(:operations_feature_flag, project: project, name: 'feature1', active: true, version: 2)
+ strategy_a = create(:operations_strategy,
+ feature_flag: feature_flag, name: 'userWithId', parameters: { userIds: 'user_a,user_b' })
+ strategy_b = create(:operations_strategy,
+ feature_flag: feature_flag,
+ name: 'gradualRolloutUserId',
+ parameters: { groupId: 'default', percentage: '45' })
create(:operations_scope, strategy: strategy_a, environment_scope: 'production')
create(:operations_scope, strategy: strategy_b, environment_scope: 'production')
@@ -298,12 +295,12 @@ RSpec.describe API::Unleash do
end
it 'returns only flags matching the environment scope' do
- feature_flag_a = create(:operations_feature_flag, project: project,
- name: 'feature1', active: true, version: 2)
+ feature_flag_a = create(:operations_feature_flag,
+ project: project, name: 'feature1', active: true, version: 2)
strategy_a = create(:operations_strategy, feature_flag: feature_flag_a)
create(:operations_scope, strategy: strategy_a, environment_scope: 'production')
- feature_flag_b = create(:operations_feature_flag, project: project,
- name: 'feature2', active: true, version: 2)
+ feature_flag_b = create(:operations_feature_flag,
+ project: project, name: 'feature2', active: true, version: 2)
strategy_b = create(:operations_strategy, feature_flag: feature_flag_b)
create(:operations_scope, strategy: strategy_b, environment_scope: 'staging')
@@ -322,13 +319,11 @@ RSpec.describe API::Unleash do
end
it 'returns only strategies matching the environment scope' do
- feature_flag = create(:operations_feature_flag, project: project,
- name: 'feature1', active: true, version: 2)
- strategy_a = create(:operations_strategy, feature_flag: feature_flag,
- name: 'userWithId', parameters: { userIds: 'user2,user8,user4' })
+ feature_flag = create(:operations_feature_flag, project: project, name: 'feature1', active: true, version: 2)
+ strategy_a = create(:operations_strategy,
+ feature_flag: feature_flag, name: 'userWithId', parameters: { userIds: 'user2,user8,user4' })
create(:operations_scope, strategy: strategy_a, environment_scope: 'production')
- strategy_b = create(:operations_strategy, feature_flag: feature_flag,
- name: 'default', parameters: {})
+ strategy_b = create(:operations_strategy, feature_flag: feature_flag, name: 'default', parameters: {})
create(:operations_scope, strategy: strategy_b, environment_scope: 'staging')
get api(features_url), headers: { 'UNLEASH-INSTANCEID' => client.token, 'UNLEASH-APPNAME' => 'production' }
@@ -346,10 +341,12 @@ RSpec.describe API::Unleash do
it 'returns only flags for the given project' do
project_b = create(:project)
- feature_flag_a = create(:operations_feature_flag, project: project, name: 'feature_a', active: true, version: 2)
+ feature_flag_a = create(:operations_feature_flag,
+ project: project, name: 'feature_a', active: true, version: 2)
strategy_a = create(:operations_strategy, feature_flag: feature_flag_a)
create(:operations_scope, strategy: strategy_a, environment_scope: 'sandbox')
- feature_flag_b = create(:operations_feature_flag, project: project_b, name: 'feature_b', active: true, version: 2)
+ feature_flag_b = create(:operations_feature_flag,
+ project: project_b, name: 'feature_b', active: true, version: 2)
strategy_b = create(:operations_strategy, feature_flag: feature_flag_b)
create(:operations_scope, strategy: strategy_b, environment_scope: 'sandbox')
@@ -367,16 +364,16 @@ RSpec.describe API::Unleash do
end
it 'returns all strategies with a matching scope' do
- feature_flag = create(:operations_feature_flag, project: project,
- name: 'feature1', active: true, version: 2)
- strategy_a = create(:operations_strategy, feature_flag: feature_flag,
- name: 'userWithId', parameters: { userIds: 'user2,user8,user4' })
+ feature_flag = create(:operations_feature_flag, project: project, name: 'feature1', active: true, version: 2)
+ strategy_a = create(:operations_strategy,
+ feature_flag: feature_flag, name: 'userWithId', parameters: { userIds: 'user2,user8,user4' })
create(:operations_scope, strategy: strategy_a, environment_scope: '*')
- strategy_b = create(:operations_strategy, feature_flag: feature_flag,
- name: 'default', parameters: {})
+ strategy_b = create(:operations_strategy, feature_flag: feature_flag, name: 'default', parameters: {})
create(:operations_scope, strategy: strategy_b, environment_scope: 'review/*')
- strategy_c = create(:operations_strategy, feature_flag: feature_flag,
- name: 'gradualRolloutUserId', parameters: { groupId: 'default', percentage: '15' })
+ strategy_c = create(:operations_strategy,
+ feature_flag: feature_flag,
+ name: 'gradualRolloutUserId',
+ parameters: { groupId: 'default', percentage: '15' })
create(:operations_scope, strategy: strategy_c, environment_scope: 'review/patch-1')
get api(features_url), headers: { 'UNLEASH-INSTANCEID' => client.token, 'UNLEASH-APPNAME' => 'review/patch-1' }
@@ -395,10 +392,8 @@ RSpec.describe API::Unleash do
end
it 'returns a strategy with more than one matching scope' do
- feature_flag = create(:operations_feature_flag, project: project,
- name: 'feature1', active: true, version: 2)
- strategy = create(:operations_strategy, feature_flag: feature_flag,
- name: 'default', parameters: {})
+ feature_flag = create(:operations_feature_flag, project: project, name: 'feature1', active: true, version: 2)
+ strategy = create(:operations_strategy, feature_flag: feature_flag, name: 'default', parameters: {})
create(:operations_scope, strategy: strategy, environment_scope: 'production')
create(:operations_scope, strategy: strategy, environment_scope: '*')
@@ -416,10 +411,9 @@ RSpec.describe API::Unleash do
end
it 'returns a disabled flag with a matching scope' do
- feature_flag = create(:operations_feature_flag, project: project,
- name: 'myfeature', active: false, version: 2)
- strategy = create(:operations_strategy, feature_flag: feature_flag,
- name: 'default', parameters: {})
+ feature_flag = create(:operations_feature_flag,
+ project: project, name: 'myfeature', active: false, version: 2)
+ strategy = create(:operations_strategy, feature_flag: feature_flag, name: 'default', parameters: {})
create(:operations_scope, strategy: strategy, environment_scope: 'production')
get api(features_url), headers: { 'UNLEASH-INSTANCEID' => client.token, 'UNLEASH-APPNAME' => 'production' }
@@ -436,12 +430,12 @@ RSpec.describe API::Unleash do
end
it 'returns a userWithId strategy for a gitlabUserList strategy' do
- feature_flag = create(:operations_feature_flag, :new_version_flag, project: project,
- name: 'myfeature', active: true)
- user_list = create(:operations_feature_flag_user_list, project: project,
- name: 'My List', user_xids: 'user1,user2')
- strategy = create(:operations_strategy, feature_flag: feature_flag,
- name: 'gitlabUserList', parameters: {}, user_list: user_list)
+ feature_flag = create(:operations_feature_flag, :new_version_flag,
+ project: project, name: 'myfeature', active: true)
+ user_list = create(:operations_feature_flag_user_list,
+ project: project, name: 'My List', user_xids: 'user1,user2')
+ strategy = create(:operations_strategy,
+ feature_flag: feature_flag, name: 'gitlabUserList', parameters: {}, user_list: user_list)
create(:operations_scope, strategy: strategy, environment_scope: 'production')
get api(features_url), headers: { 'UNLEASH-INSTANCEID' => client.token, 'UNLEASH-APPNAME' => 'production' }
diff --git a/spec/requests/api/usage_data_queries_spec.rb b/spec/requests/api/usage_data_queries_spec.rb
index 69a8d865a59..6ce03954246 100644
--- a/spec/requests/api/usage_data_queries_spec.rb
+++ b/spec/requests/api/usage_data_queries_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
+require 'rake_helper'
RSpec.describe API::UsageDataQueries do
include UsageDataHelpers
@@ -64,5 +65,36 @@ RSpec.describe API::UsageDataQueries do
expect(response).to have_gitlab_http_status(:forbidden)
end
end
+
+ context 'when querying sql metrics' do
+ let(:file) { Rails.root.join('tmp', 'test', 'sql_metrics_queries.json') }
+
+ before do
+ Rake.application.rake_require 'tasks/gitlab/usage_data'
+
+ run_rake_task('gitlab:usage_data:generate_sql_metrics_queries')
+ end
+
+ after do
+ FileUtils.rm_rf(file)
+ end
+
+ it 'matches the generated query' do
+ Timecop.freeze(2021, 1, 1) do
+ get api(endpoint, admin)
+ end
+
+ data = Gitlab::Json.parse(File.read(file))
+
+ expect(
+ json_response['counts_monthly'].except('aggregated_metrics')
+ ).to eq(data['counts_monthly'].except('aggregated_metrics'))
+
+ expect(json_response['counts']).to eq(data['counts'])
+ expect(json_response['active_user_count']).to eq(data['active_user_count'])
+ expect(json_response['usage_activity_by_stage']).to eq(data['usage_activity_by_stage'])
+ expect(json_response['usage_activity_by_stage_monthly']).to eq(data['usage_activity_by_stage_monthly'])
+ end
+ end
end
end
diff --git a/spec/requests/api/usage_data_spec.rb b/spec/requests/api/usage_data_spec.rb
index ea50c404d92..d532fb6c168 100644
--- a/spec/requests/api/usage_data_spec.rb
+++ b/spec/requests/api/usage_data_spec.rb
@@ -138,7 +138,9 @@ RSpec.describe API::UsageData do
context 'with correct params' do
it 'returns status ok' do
- expect(Gitlab::Redis::HLL).to receive(:add)
+ expect(Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track).with(anything, known_event, anything)
+ # allow other events to also get triggered
+ allow(Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track)
post api(endpoint, user), params: { event: known_event }
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 26238a87209..96e23337411 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe API::Users do
+ include WorkhorseHelpers
+
let_it_be(:admin) { create(:admin) }
let_it_be(:user, reload: true) { create(:user, username: 'user.withdot') }
let_it_be(:key) { create(:key, user: user) }
@@ -116,7 +118,7 @@ RSpec.describe API::Users do
end
it "returns a 403 if the target user is an admin" do
- expect(TwoFactor::DestroyService).to receive(:new).never
+ expect(TwoFactor::DestroyService).not_to receive(:new)
expect do
patch api("/users/#{admin_with_2fa.id}/disable_two_factor", admin)
@@ -127,7 +129,7 @@ RSpec.describe API::Users do
end
it "returns a 404 if the target user cannot be found" do
- expect(TwoFactor::DestroyService).to receive(:new).never
+ expect(TwoFactor::DestroyService).not_to receive(:new)
patch api("/users/#{non_existing_record_id}/disable_two_factor", admin)
@@ -1180,6 +1182,22 @@ RSpec.describe API::Users do
expect(new_user.user_preference.view_diffs_file_by_file?).to eq(true)
end
+ it "creates user with avatar" do
+ workhorse_form_with_file(
+ api('/users', admin),
+ method: :post,
+ file_key: :avatar,
+ params: attributes_for(:user, avatar: fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif'))
+ )
+
+ expect(response).to have_gitlab_http_status(:created)
+
+ new_user = User.find_by(id: json_response['id'])
+
+ expect(new_user).not_to eq(nil)
+ expect(json_response['avatar_url']).to include(new_user.avatar_path)
+ end
+
it "does not create user with invalid email" do
post api('/users', admin),
params: {
@@ -1478,7 +1496,12 @@ RSpec.describe API::Users do
end
it 'updates user with avatar' do
- put api("/users/#{user.id}", admin), params: { avatar: fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') }
+ workhorse_form_with_file(
+ api("/users/#{user.id}", admin),
+ method: :put,
+ file_key: :avatar,
+ params: { avatar: fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') }
+ )
user.reload
@@ -2479,14 +2502,32 @@ RSpec.describe API::Users do
describe "DELETE /users/:id" do
let_it_be(:issue) { create(:issue, author: user) }
- it "deletes user", :sidekiq_inline do
- namespace_id = user.namespace.id
+ context 'user deletion' do
+ context 'when user_destroy_with_limited_execution_time_worker is enabled' do
+ it "deletes user", :sidekiq_inline do
+ perform_enqueued_jobs { delete api("/users/#{user.id}", admin) }
- perform_enqueued_jobs { delete api("/users/#{user.id}", admin) }
+ expect(response).to have_gitlab_http_status(:no_content)
+ expect(Users::GhostUserMigration.where(user: user,
+ initiator_user: admin)).to be_exists
+ end
+ end
- expect(response).to have_gitlab_http_status(:no_content)
- expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound
- expect { Namespace.find(namespace_id) }.to raise_error ActiveRecord::RecordNotFound
+ context 'when user_destroy_with_limited_execution_time_worker is disabled' do
+ before do
+ stub_feature_flags(user_destroy_with_limited_execution_time_worker: false)
+ end
+
+ it "deletes user", :sidekiq_inline do
+ namespace_id = user.namespace.id
+
+ perform_enqueued_jobs { delete api("/users/#{user.id}", admin) }
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ expect { User.find(user.id) }.to raise_error ActiveRecord::RecordNotFound
+ expect { Namespace.find(namespace_id) }.to raise_error ActiveRecord::RecordNotFound
+ end
+ end
end
context "sole owner of a group" do
@@ -2550,22 +2591,55 @@ RSpec.describe API::Users do
expect(response).to have_gitlab_http_status(:not_found)
end
- context "hard delete disabled" do
- it "moves contributions to the ghost user", :sidekiq_might_not_need_inline do
- perform_enqueued_jobs { delete api("/users/#{user.id}", admin) }
+ context 'hard delete' do
+ context 'when user_destroy_with_limited_execution_time_worker is enabled' do
+ context "hard delete disabled" do
+ it "moves contributions to the ghost user", :sidekiq_might_not_need_inline do
+ perform_enqueued_jobs { delete api("/users/#{user.id}", admin) }
- expect(response).to have_gitlab_http_status(:no_content)
- expect(issue.reload).to be_persisted
- expect(issue.author.ghost?).to be_truthy
+ expect(response).to have_gitlab_http_status(:no_content)
+ expect(issue.reload).to be_persisted
+ expect(Users::GhostUserMigration.where(user: user,
+ initiator_user: admin,
+ hard_delete: false)).to be_exists
+ end
+ end
+
+ context "hard delete enabled" do
+ it "removes contributions", :sidekiq_might_not_need_inline do
+ perform_enqueued_jobs { delete api("/users/#{user.id}?hard_delete=true", admin) }
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ expect(Users::GhostUserMigration.where(user: user,
+ initiator_user: admin,
+ hard_delete: true)).to be_exists
+ end
+ end
end
- end
- context "hard delete enabled" do
- it "removes contributions", :sidekiq_might_not_need_inline do
- perform_enqueued_jobs { delete api("/users/#{user.id}?hard_delete=true", admin) }
+ context 'when user_destroy_with_limited_execution_time_worker is disabled' do
+ before do
+ stub_feature_flags(user_destroy_with_limited_execution_time_worker: false)
+ end
- expect(response).to have_gitlab_http_status(:no_content)
- expect(Issue.exists?(issue.id)).to be_falsy
+ context "hard delete disabled" do
+ it "moves contributions to the ghost user", :sidekiq_might_not_need_inline do
+ perform_enqueued_jobs { delete api("/users/#{user.id}", admin) }
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ expect(issue.reload).to be_persisted
+ expect(issue.author.ghost?).to be_truthy
+ end
+ end
+
+ context "hard delete enabled" do
+ it "removes contributions", :sidekiq_might_not_need_inline do
+ perform_enqueued_jobs { delete api("/users/#{user.id}?hard_delete=true", admin) }
+
+ expect(response).to have_gitlab_http_status(:no_content)
+ expect(Issue.exists?(issue.id)).to be_falsy
+ end
+ end
end
end
end
@@ -3238,7 +3312,7 @@ RSpec.describe API::Users do
let(:user) { create(:user, **activity) }
context 'with no recent activity' do
- let(:activity) { { last_activity_on: ::User::MINIMUM_INACTIVE_DAYS.next.days.ago } }
+ let(:activity) { { last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.next.days.ago } }
it 'deactivates an active user' do
deactivate
@@ -3249,13 +3323,13 @@ RSpec.describe API::Users do
end
context 'with recent activity' do
- let(:activity) { { last_activity_on: ::User::MINIMUM_INACTIVE_DAYS.pred.days.ago } }
+ let(:activity) { { last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.pred.days.ago } }
it 'does not deactivate an active user' do
deactivate
expect(response).to have_gitlab_http_status(:forbidden)
- expect(json_response['message']).to eq("403 Forbidden - The user you are trying to deactivate has been active in the past #{::User::MINIMUM_INACTIVE_DAYS} days and cannot be deactivated")
+ expect(json_response['message']).to eq("403 Forbidden - The user you are trying to deactivate has been active in the past #{Gitlab::CurrentSettings.deactivate_dormant_users_period} days and cannot be deactivated")
expect(user.reload.state).to eq('active')
end
end
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 77107d0b43c..81e923983ab 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -452,7 +452,7 @@ RSpec.describe 'Git HTTP requests' do
canonical_project.add_maintainer(user)
create(:merge_request,
source_project: project,
- target_project: canonical_project,
+ target_project: canonical_project,
source_branch: 'fixes',
allow_collaboration: true)
end
@@ -1105,7 +1105,7 @@ RSpec.describe 'Git HTTP requests' do
canonical_project.add_maintainer(user)
create(:merge_request,
source_project: project,
- target_project: canonical_project,
+ target_project: canonical_project,
source_branch: 'fixes',
allow_collaboration: true)
end
diff --git a/spec/requests/groups/observability_controller_spec.rb b/spec/requests/groups/observability_controller_spec.rb
new file mode 100644
index 00000000000..9be013d4385
--- /dev/null
+++ b/spec/requests/groups/observability_controller_spec.rb
@@ -0,0 +1,190 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Groups::ObservabilityController do
+ include ContentSecurityPolicyHelpers
+
+ let_it_be(:group) { create(:group) }
+ let_it_be(:user) { create(:user) }
+
+ subject do
+ get group_observability_index_path(group)
+ response
+ end
+
+ describe 'GET #index' do
+ context 'when user is not authenticated' do
+ it 'returns 404' do
+ expect(subject).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when observability url is missing' do
+ before do
+ allow(described_class).to receive(:observability_url).and_return("")
+ end
+
+ it 'returns 404' do
+ expect(subject).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when user is not a developer' do
+ before do
+ sign_in(user)
+ end
+
+ it 'returns 404' do
+ expect(subject).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ context 'when user is authenticated and a developer' do
+ before do
+ sign_in(user)
+ group.add_developer(user)
+ end
+
+ it 'returns 200' do
+ expect(subject).to have_gitlab_http_status(:ok)
+ end
+
+ it 'renders the proper layout' do
+ expect(subject).to render_template("layouts/group")
+ expect(subject).to render_template("layouts/fullscreen")
+ expect(subject).not_to render_template('layouts/nav/breadcrumbs')
+ expect(subject).to render_template("nav/sidebar/_group")
+ end
+
+ describe 'iframe' do
+ subject do
+ get group_observability_index_path(group)
+ Nokogiri::HTML.parse(response.body).at_css('iframe#observability-ui-iframe')
+ end
+
+ it 'sets the iframe src to the proper URL' do
+ expect(subject.attributes['src'].value).to eq("https://observe.gitlab.com/-/#{group.id}")
+ end
+
+ it 'when the env is staging, sets the iframe src to the proper URL' do
+ stub_config_setting(url: Gitlab::Saas.staging_com_url)
+ expect(subject.attributes['src'].value).to eq("https://staging.observe.gitlab.com/-/#{group.id}")
+ end
+
+ it 'overrides the iframe src url if specified by OVERRIDE_OBSERVABILITY_URL env' do
+ stub_env('OVERRIDE_OBSERVABILITY_URL', 'http://foo.test')
+
+ expect(subject.attributes['src'].value).to eq("http://foo.test/-/#{group.id}")
+ end
+ end
+
+ describe 'CSP' do
+ before do
+ setup_existing_csp_for_controller(described_class, csp)
+ end
+
+ subject do
+ get group_observability_index_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 https://observe.gitlab.com 'self'")
+ end
+
+ it 'appends the proper url to frame-src CSP directives when Gilab.staging?' do
+ stub_config_setting(url: Gitlab::Saas.staging_com_url)
+
+ expect(subject).to include(
+ "frame-src https://something.test https://staging.observe.gitlab.com 'self'")
+ end
+
+ it 'appends the proper url to frame-src CSP directives when OVERRIDE_OBSERVABILITY_URL is specified' do
+ stub_env('OVERRIDE_OBSERVABILITY_URL', 'http://foo.test')
+
+ expect(subject).to include(
+ "frame-src https://something.test http://foo.test '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' https://observe.gitlab.com;")
+ 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 https://observe.gitlab.com 'self'")
+ end
+
+ it 'appends the proper url to frame-src CSP directives when Gilab.staging?' do
+ stub_config_setting(url: Gitlab::Saas.staging_com_url)
+
+ expect(subject).to include(
+ "frame-src https://something.test https://staging.observe.gitlab.com 'self'")
+ end
+
+ it 'appends the proper url to frame-src CSP directives when OVERRIDE_OBSERVABILITY_URL is specified' do
+ stub_env('OVERRIDE_OBSERVABILITY_URL', 'http://foo.test')
+
+ expect(subject).to include(
+ "frame-src https://something.test http://foo.test '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 https://observe.gitlab.com 'self'")
+ expect(subject).to include(
+ "default-src https://something_default.test")
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/health_controller_spec.rb b/spec/requests/health_controller_spec.rb
index f70faf5bb9c..ae15b63df19 100644
--- a/spec/requests/health_controller_spec.rb
+++ b/spec/requests/health_controller_spec.rb
@@ -127,6 +127,10 @@ RSpec.describe HealthController do
end
it 'responds with readiness checks data' do
+ expect_next_instance_of(Gitlab::GitalyClient::ServerService) do |service|
+ expect(service).to receive(:readiness_check).and_return({ success: true })
+ end
+
subject
expect(json_response['db_check']).to contain_exactly({ 'status' => 'ok' })
@@ -138,19 +142,29 @@ RSpec.describe HealthController do
end
it 'responds with readiness checks data when a failure happens' do
- allow(Gitlab::HealthChecks::Redis::RedisCheck).to receive(:readiness).and_return(
- Gitlab::HealthChecks::Result.new('redis_check', false, "check error"))
+ allow(Gitlab::HealthChecks::Redis::SharedStateCheck).to receive(:readiness).and_return(
+ Gitlab::HealthChecks::Result.new('shared_state_check', false, "check error"))
subject
expect(json_response['cache_check']).to contain_exactly({ 'status' => 'ok' })
- expect(json_response['redis_check']).to contain_exactly(
+ expect(json_response['shared_state_check']).to contain_exactly(
{ 'status' => 'failed', 'message' => 'check error' })
expect(response).to have_gitlab_http_status(:service_unavailable)
expect(response.headers['X-GitLab-Custom-Error']).to eq(1)
end
+ it 'checks all redis instances' do
+ expected_redis_checks = Gitlab::Redis::ALL_CLASSES.map do |redis|
+ { "#{redis.store_name.underscore}_check" => [{ 'status' => 'ok' }] }
+ end
+
+ subject
+
+ expect(json_response).to include(*expected_redis_checks)
+ end
+
context 'when DB is not accessible and connection raises an exception' do
before do
expect(Gitlab::HealthChecks::DbCheck)
@@ -170,7 +184,7 @@ RSpec.describe HealthController do
context 'when any exception happens during the probing' do
before do
- expect(Gitlab::HealthChecks::Redis::RedisCheck)
+ expect(Gitlab::HealthChecks::Redis::CacheCheck)
.to receive(:readiness)
.and_raise(::Redis::CannotConnectError, 'Redis down')
end
diff --git a/spec/requests/jira_connect/oauth_callbacks_controller_spec.rb b/spec/requests/jira_connect/oauth_callbacks_controller_spec.rb
index 1e4628e5d59..12b9429b648 100644
--- a/spec/requests/jira_connect/oauth_callbacks_controller_spec.rb
+++ b/spec/requests/jira_connect/oauth_callbacks_controller_spec.rb
@@ -5,12 +5,6 @@ require 'spec_helper'
RSpec.describe JiraConnect::OauthCallbacksController do
describe 'GET /-/jira_connect/oauth_callbacks' do
context 'when logged in' do
- let_it_be(:user) { create(:user) }
-
- before do
- sign_in(user)
- end
-
it 'renders a page prompting the user to close the window' do
get '/-/jira_connect/oauth_callbacks'
diff --git a/spec/requests/jira_connect/subscriptions_controller_spec.rb b/spec/requests/jira_connect/subscriptions_controller_spec.rb
index d8f329f13f5..f407ea09250 100644
--- a/spec/requests/jira_connect/subscriptions_controller_spec.rb
+++ b/spec/requests/jira_connect/subscriptions_controller_spec.rb
@@ -12,18 +12,29 @@ RSpec.describe JiraConnect::SubscriptionsController do
let(:jwt) { Atlassian::Jwt.encode({ iss: installation.client_key, qsh: qsh }, installation.shared_secret) }
- before do
+ subject(:content_security_policy) do
get '/-/jira_connect/subscriptions', params: { jwt: jwt }
- end
- subject(:content_security_policy) { response.headers['Content-Security-Policy'] }
+ response.headers['Content-Security-Policy']
+ end
- it { is_expected.to include('http://self-managed-gitlab.com/-/jira_connect/oauth_application_ids') }
+ it { is_expected.to include('http://self-managed-gitlab.com/-/jira_connect/') }
+ it { is_expected.to include('http://self-managed-gitlab.com/api/') }
context 'with no self-managed instance configured' do
let_it_be(:installation) { create(:jira_connect_installation, instance_url: '') }
- it { is_expected.not_to include('http://self-managed-gitlab.com') }
+ 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/') }
+ end
+
+ context 'with jira_connect_oauth_self_managed feature disabled' do
+ before do
+ stub_feature_flags(jira_connect_oauth_self_managed: false)
+ 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/') }
end
end
end
diff --git a/spec/requests/jwt_controller_spec.rb b/spec/requests/jwt_controller_spec.rb
index c9904ffa37b..e6916e02fde 100644
--- a/spec/requests/jwt_controller_spec.rb
+++ b/spec/requests/jwt_controller_spec.rb
@@ -27,6 +27,10 @@ RSpec.describe JwtController do
let(:headers) { { authorization: credentials('personal_access_token', pat.token) } }
it 'fails authentication' do
+ expect(::Gitlab::AuthLogger).to receive(:warn).with(
+ hash_including(message: 'JWT authentication failed',
+ http_user: 'personal_access_token')).and_call_original
+
get '/jwt/auth', params: parameters, headers: headers
expect(response).to have_gitlab_http_status(:unauthorized)
@@ -80,7 +84,7 @@ RSpec.describe JwtController do
context 'project with enabled CI' do
subject! { get '/jwt/auth', params: parameters, headers: headers }
- it { expect(service_class).to have_received(:new).with(project, user, ActionController::Parameters.new(parameters).permit!) }
+ it { expect(service_class).to have_received(:new).with(project, user, ActionController::Parameters.new(parameters.merge(auth_type: :build)).permit!) }
it_behaves_like 'user logging'
end
@@ -103,7 +107,12 @@ RSpec.describe JwtController do
it 'authenticates correctly' do
expect(response).to have_gitlab_http_status(:ok)
- expect(service_class).to have_received(:new).with(nil, nil, ActionController::Parameters.new(parameters.merge(deploy_token: deploy_token)).permit!)
+ expect(service_class).to have_received(:new)
+ .with(
+ nil,
+ nil,
+ ActionController::Parameters.new(parameters.merge(deploy_token: deploy_token, auth_type: :deploy_token)).permit!
+ )
end
it 'does not log a user' do
@@ -123,7 +132,12 @@ RSpec.describe JwtController do
it 'authenticates correctly' do
expect(response).to have_gitlab_http_status(:ok)
- expect(service_class).to have_received(:new).with(nil, user, ActionController::Parameters.new(parameters).permit!)
+ expect(service_class).to have_received(:new)
+ .with(
+ nil,
+ user,
+ ActionController::Parameters.new(parameters.merge(auth_type: :personal_access_token)).permit!
+ )
end
it_behaves_like 'rejecting a blocked user'
@@ -138,7 +152,7 @@ RSpec.describe JwtController do
subject! { get '/jwt/auth', params: parameters, headers: headers }
- it { expect(service_class).to have_received(:new).with(nil, user, ActionController::Parameters.new(parameters).permit!) }
+ it { expect(service_class).to have_received(:new).with(nil, user, ActionController::Parameters.new(parameters.merge(auth_type: :gitlab_or_ldap)).permit!) }
it_behaves_like 'rejecting a blocked user'
@@ -158,7 +172,7 @@ RSpec.describe JwtController do
ActionController::Parameters.new({ service: service_name, scopes: %w(scope1 scope2) }).permit!
end
- it { expect(service_class).to have_received(:new).with(nil, user, service_parameters) }
+ it { expect(service_class).to have_received(:new).with(nil, user, service_parameters.merge(auth_type: :gitlab_or_ldap)) }
it_behaves_like 'user logging'
end
diff --git a/spec/requests/oauth_tokens_spec.rb b/spec/requests/oauth_tokens_spec.rb
index 180341fc85d..f2fb380bde0 100644
--- a/spec/requests/oauth_tokens_spec.rb
+++ b/spec/requests/oauth_tokens_spec.rb
@@ -78,11 +78,12 @@ RSpec.describe 'OAuth Tokens requests' do
context 'revoked refresh token' do
let!(:existing_token) do
- create(:oauth_access_token, application: application,
- resource_owner_id: user.id,
- created_at: 2.hours.ago,
- revoked_at: 1.hour.ago,
- expires_in: 5)
+ create(:oauth_access_token,
+ application: application,
+ resource_owner_id: user.id,
+ created_at: 2.hours.ago,
+ revoked_at: 1.hour.ago,
+ expires_in: 5)
end
it 'does not issue a new token' do
diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb
index afaa6168bfd..3a40fec58e8 100644
--- a/spec/requests/openid_connect_spec.rb
+++ b/spec/requests/openid_connect_spec.rb
@@ -23,24 +23,24 @@ RSpec.describe 'OpenID Connect requests' do
let(:id_token_claims) do
{
- 'sub' => user.id.to_s,
+ 'sub' => user.id.to_s,
'sub_legacy' => hashed_subject
}
end
let(:user_info_claims) do
{
- 'name' => 'Alice',
- 'nickname' => 'alice',
- 'email' => 'public@example.com',
+ 'name' => 'Alice',
+ 'nickname' => 'alice',
+ 'email' => 'public@example.com',
'email_verified' => true,
- 'website' => 'https://example.com',
- 'profile' => 'http://localhost/alice',
- 'picture' => "http://localhost/uploads/-/system/user/avatar/#{user.id}/dk.png",
- 'groups' => kind_of(Array),
- 'https://gitlab.org/claims/groups/owner' => kind_of(Array),
+ 'website' => 'https://example.com',
+ 'profile' => 'http://localhost/alice',
+ 'picture' => "http://localhost/uploads/-/system/user/avatar/#{user.id}/dk.png",
+ 'groups' => kind_of(Array),
+ 'https://gitlab.org/claims/groups/owner' => kind_of(Array),
'https://gitlab.org/claims/groups/maintainer' => kind_of(Array),
- 'https://gitlab.org/claims/groups/developer' => kind_of(Array)
+ 'https://gitlab.org/claims/groups/developer' => kind_of(Array)
}
end
diff --git a/spec/requests/projects/environments_controller_spec.rb b/spec/requests/projects/environments_controller_spec.rb
index 0890b0c45da..66ab265fc0f 100644
--- a/spec/requests/projects/environments_controller_spec.rb
+++ b/spec/requests/projects/environments_controller_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe Projects::EnvironmentsController do
deployer = create(:user)
pipeline = create(:ci_pipeline, project: environment.project)
build = create(:ci_build, environment: environment.name, pipeline: pipeline, user: deployer)
- create(:deployment, :success, environment: environment, deployable: build, user: deployer,
- project: project, sha: commit.sha)
+ create(:deployment, :success,
+ environment: environment, deployable: build, user: deployer, project: project, sha: commit.sha)
end
end
diff --git a/spec/requests/projects/google_cloud/configuration_controller_spec.rb b/spec/requests/projects/google_cloud/configuration_controller_spec.rb
index 08d4ad2f9ba..41593b8d7a7 100644
--- a/spec/requests/projects/google_cloud/configuration_controller_spec.rb
+++ b/spec/requests/projects/google_cloud/configuration_controller_spec.rb
@@ -2,9 +2,6 @@
require 'spec_helper'
-# Mock Types
-MockGoogleOAuth2Credentials = Struct.new(:app_id, :app_secret)
-
RSpec.describe Projects::GoogleCloud::ConfigurationController do
let_it_be(:project) { create(:project, :public) }
let_it_be(:url) { project_google_cloud_configuration_path(project) }
@@ -29,10 +26,9 @@ RSpec.describe Projects::GoogleCloud::ConfigurationController do
get url
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'admin_project_google_cloud!',
- label: 'error_access_denied',
- property: 'invalid_user',
+ category: 'Projects::GoogleCloud::ConfigurationController',
+ action: 'error_invalid_user',
+ label: nil,
project: project,
user: unauthorized_member
)
@@ -56,7 +52,7 @@ RSpec.describe Projects::GoogleCloud::ConfigurationController do
context 'but gitlab instance is not configured for google oauth2' do
it 'returns forbidden' do
- unconfigured_google_oauth2 = MockGoogleOAuth2Credentials.new('', '')
+ unconfigured_google_oauth2 = Struct.new(:app_id, :app_secret).new('', '')
allow(Gitlab::Auth::OAuth::Provider).to receive(:config_for)
.with('google_oauth2')
.and_return(unconfigured_google_oauth2)
@@ -68,11 +64,9 @@ RSpec.describe Projects::GoogleCloud::ConfigurationController do
expect(response).to have_gitlab_http_status(:forbidden)
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'google_oauth2_enabled!',
- label: 'error_access_denied',
- extra: { reason: 'google_oauth2_not_configured',
- config: unconfigured_google_oauth2 },
+ category: 'Projects::GoogleCloud::ConfigurationController',
+ action: 'error_google_oauth2_not_enabled',
+ label: nil,
project: project,
user: authorized_member
)
@@ -93,10 +87,9 @@ RSpec.describe Projects::GoogleCloud::ConfigurationController do
expect(response).to have_gitlab_http_status(:not_found)
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'feature_flag_enabled!',
- label: 'error_access_denied',
- property: 'feature_flag_not_enabled',
+ category: 'Projects::GoogleCloud::ConfigurationController',
+ action: 'error_feature_flag_not_enabled',
+ label: nil,
project: project,
user: authorized_member
)
@@ -117,20 +110,9 @@ RSpec.describe Projects::GoogleCloud::ConfigurationController do
expect(response).to be_successful
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'configuration#index',
- label: 'success',
- extra: {
- configurationUrl: project_google_cloud_configuration_path(project),
- deploymentsUrl: project_google_cloud_deployments_path(project),
- databasesUrl: project_google_cloud_databases_path(project),
- serviceAccounts: [],
- createServiceAccountUrl: project_google_cloud_service_accounts_path(project),
- emptyIllustrationUrl: ActionController::Base.helpers.image_path('illustrations/pipelines_empty.svg'),
- configureGcpRegionsUrl: project_google_cloud_gcp_regions_path(project),
- gcpRegions: [],
- revokeOauthUrl: nil
- },
+ category: 'Projects::GoogleCloud::ConfigurationController',
+ action: 'render_page',
+ label: nil,
project: project,
user: authorized_member
)
diff --git a/spec/requests/projects/google_cloud/databases_controller_spec.rb b/spec/requests/projects/google_cloud/databases_controller_spec.rb
index c9335f8f317..4edef71f326 100644
--- a/spec/requests/projects/google_cloud/databases_controller_spec.rb
+++ b/spec/requests/projects/google_cloud/databases_controller_spec.rb
@@ -2,133 +2,166 @@
require 'spec_helper'
-# Mock Types
-MockGoogleOAuth2Credentials = Struct.new(:app_id, :app_secret)
+RSpec.describe Projects::GoogleCloud::DatabasesController, :snowplow do
+ shared_examples 'shared examples for database controller endpoints' do
+ include_examples 'requires `admin_project_google_cloud` role'
-RSpec.describe Projects::GoogleCloud::DatabasesController do
- let_it_be(:project) { create(:project, :public) }
- let_it_be(:url) { project_google_cloud_databases_path(project) }
+ include_examples 'requires feature flag `incubation_5mp_google_cloud` enabled'
- let_it_be(:user_guest) { create(:user) }
- let_it_be(:user_developer) { create(:user) }
- let_it_be(:user_maintainer) { create(:user) }
+ include_examples 'requires valid Google OAuth2 configuration'
- let_it_be(:unauthorized_members) { [user_guest, user_developer] }
- let_it_be(:authorized_members) { [user_maintainer] }
+ include_examples 'requires valid Google Oauth2 token' do
+ let_it_be(:mock_gcp_projects) { [{}, {}, {}] }
+ let_it_be(:mock_branches) { [] }
+ let_it_be(:mock_tags) { [] }
+ end
+ end
+
+ context '-/google_cloud/databases' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:renders_template) { 'projects/google_cloud/databases/index' }
+ let_it_be(:redirects_to) { nil }
- before do
- project.add_guest(user_guest)
- project.add_developer(user_developer)
- project.add_maintainer(user_maintainer)
+ subject { get project_google_cloud_databases_path(project) }
+
+ include_examples 'shared examples for database controller endpoints'
end
- context 'when accessed by unauthorized members' do
- it 'returns not found on GET request' do
- unauthorized_members.each do |unauthorized_member|
- sign_in(unauthorized_member)
+ context '-/google_cloud/databases/new/postgres' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:renders_template) { 'projects/google_cloud/databases/cloudsql_form' }
+ let_it_be(:redirects_to) { nil }
- get url
- expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'admin_project_google_cloud!',
- label: 'error_access_denied',
- property: 'invalid_user',
- project: project,
- user: unauthorized_member
- )
+ subject { get new_project_google_cloud_database_path(project, :postgres) }
- expect(response).to have_gitlab_http_status(:not_found)
- end
- end
+ include_examples 'shared examples for database controller endpoints'
end
- context 'when accessed by authorized members' do
- it 'returns successful' do
- authorized_members.each do |authorized_member|
- sign_in(authorized_member)
+ context '-/google_cloud/databases/new/mysql' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:renders_template) { 'projects/google_cloud/databases/cloudsql_form' }
+ let_it_be(:redirects_to) { nil }
- get url
+ subject { get new_project_google_cloud_database_path(project, :mysql) }
- expect(response).to be_successful
- expect(response).to render_template('projects/google_cloud/databases/index')
- end
- end
+ include_examples 'shared examples for database controller endpoints'
+ end
- context 'but gitlab instance is not configured for google oauth2' do
- it 'returns forbidden' do
- unconfigured_google_oauth2 = MockGoogleOAuth2Credentials.new('', '')
- allow(Gitlab::Auth::OAuth::Provider).to receive(:config_for)
- .with('google_oauth2')
- .and_return(unconfigured_google_oauth2)
+ context '-/google_cloud/databases/new/sqlserver' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:renders_template) { 'projects/google_cloud/databases/cloudsql_form' }
+ let_it_be(:redirects_to) { nil }
- authorized_members.each do |authorized_member|
- sign_in(authorized_member)
+ subject { get new_project_google_cloud_database_path(project, :sqlserver) }
- get url
+ include_examples 'shared examples for database controller endpoints'
+ end
- expect(response).to have_gitlab_http_status(:forbidden)
- expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'google_oauth2_enabled!',
- label: 'error_access_denied',
- extra: { reason: 'google_oauth2_not_configured',
- config: unconfigured_google_oauth2 },
- project: project,
- user: authorized_member
- )
+ context '-/google_cloud/databases/create' do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:renders_template) { nil }
+ let_it_be(:redirects_to) { project_google_cloud_databases_path(project) }
+
+ subject { post project_google_cloud_databases_path(project) }
+
+ include_examples 'shared examples for database controller endpoints'
+
+ context 'when the request is valid' do
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+
+ allow_next_instance_of(GoogleApi::CloudPlatform::Client) do |client|
+ allow(client).to receive(:validate_token).and_return(true)
+ allow(client).to receive(:list_projects).and_return(mock_gcp_projects)
+ end
+
+ allow_next_instance_of(BranchesFinder) do |finder|
+ allow(finder).to receive(:execute).and_return(mock_branches)
+ end
+
+ allow_next_instance_of(TagsFinder) do |finder|
+ allow(finder).to receive(:execute).and_return(mock_branches)
end
end
- end
- context 'but feature flag is disabled' do
- before do
- stub_feature_flags(incubation_5mp_google_cloud: false)
+ subject do
+ post project_google_cloud_databases_path(project)
end
- it 'returns not found' do
- authorized_members.each do |authorized_member|
- sign_in(authorized_member)
+ it 'calls EnableCloudsqlService and redirects on error' do
+ expect_next_instance_of(::GoogleCloud::EnableCloudsqlService) do |service|
+ expect(service).to receive(:execute)
+ .and_return({ status: :error, message: 'error' })
+ end
- get url
+ subject
- expect(response).to have_gitlab_http_status(:not_found)
- expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'feature_flag_enabled!',
- label: 'error_access_denied',
- property: 'feature_flag_not_enabled',
- project: project,
- user: authorized_member
- )
- end
+ expect(response).to redirect_to(project_google_cloud_databases_path(project))
+
+ expect_snowplow_event(
+ category: 'Projects::GoogleCloud::DatabasesController',
+ action: 'error_enable_cloudsql_services',
+ label: nil,
+ project: project,
+ user: user
+ )
end
- end
- context 'but google oauth2 token is not valid' do
- it 'does not return revoke oauth url' do
- allow_next_instance_of(GoogleApi::CloudPlatform::Client) do |client|
- allow(client).to receive(:validate_token).and_return(false)
+ context 'when EnableCloudsqlService is successful' do
+ before do
+ allow_next_instance_of(::GoogleCloud::EnableCloudsqlService) do |service|
+ allow(service).to receive(:execute)
+ .and_return({ status: :success, message: 'success' })
+ end
end
- authorized_members.each do |authorized_member|
- sign_in(authorized_member)
+ it 'calls CreateCloudsqlInstanceService and redirects on error' do
+ expect_next_instance_of(::GoogleCloud::CreateCloudsqlInstanceService) do |service|
+ expect(service).to receive(:execute)
+ .and_return({ status: :error, message: 'error' })
+ end
+
+ subject
- get url
+ expect(response).to redirect_to(project_google_cloud_databases_path(project))
- expect(response).to be_successful
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'databases#index',
- label: 'success',
- extra: {
- configurationUrl: project_google_cloud_configuration_path(project),
- deploymentsUrl: project_google_cloud_deployments_path(project),
- databasesUrl: project_google_cloud_databases_path(project)
- },
+ category: 'Projects::GoogleCloud::DatabasesController',
+ action: 'error_create_cloudsql_instance',
+ label: nil,
project: project,
- user: authorized_member
+ user: user
)
end
+
+ context 'when CreateCloudsqlInstanceService is successful' do
+ before do
+ allow_next_instance_of(::GoogleCloud::CreateCloudsqlInstanceService) do |service|
+ allow(service).to receive(:execute)
+ .and_return({ status: :success, message: 'success' })
+ end
+ end
+
+ it 'redirects as expected' do
+ subject
+
+ expect(response).to redirect_to(project_google_cloud_databases_path(project))
+
+ expect_snowplow_event(
+ category: 'Projects::GoogleCloud::DatabasesController',
+ action: 'create_cloudsql_instance',
+ label: "{}",
+ project: project,
+ user: user
+ )
+ end
+ end
end
end
end
diff --git a/spec/requests/projects/google_cloud/deployments_controller_spec.rb b/spec/requests/projects/google_cloud/deployments_controller_spec.rb
index 9e854e01516..ad6a3912e0b 100644
--- a/spec/requests/projects/google_cloud/deployments_controller_spec.rb
+++ b/spec/requests/projects/google_cloud/deployments_controller_spec.rb
@@ -29,10 +29,9 @@ RSpec.describe Projects::GoogleCloud::DeploymentsController do
expect(response).to have_gitlab_http_status(:not_found)
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'admin_project_google_cloud!',
- label: 'error_access_denied',
- property: 'invalid_user',
+ category: 'Projects::GoogleCloud::DeploymentsController',
+ action: 'error_invalid_user',
+ label: nil,
project: project,
user: nil
)
@@ -48,10 +47,9 @@ RSpec.describe Projects::GoogleCloud::DeploymentsController do
expect(response).to have_gitlab_http_status(:not_found)
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'admin_project_google_cloud!',
- label: 'error_access_denied',
- property: 'invalid_user',
+ category: 'Projects::GoogleCloud::DeploymentsController',
+ action: 'error_invalid_user',
+ label: nil,
project: project,
user: nil
)
@@ -75,6 +73,30 @@ RSpec.describe Projects::GoogleCloud::DeploymentsController do
end
end
+ describe 'Authorized GET project/-/google_cloud/deployments', :snowplow do
+ before do
+ sign_in(user_maintainer)
+
+ allow_next_instance_of(GoogleApi::CloudPlatform::Client) do |client|
+ allow(client).to receive(:validate_token).and_return(true)
+ end
+ end
+
+ it 'renders template' do
+ get "#{project_google_cloud_deployments_path(project)}"
+
+ expect(response).to render_template(:index)
+
+ expect_snowplow_event(
+ category: 'Projects::GoogleCloud::DeploymentsController',
+ action: 'render_page',
+ label: nil,
+ project: project,
+ user: user_maintainer
+ )
+ end
+ end
+
describe 'Authorized GET project/-/google_cloud/deployments/cloud_run', :snowplow do
let_it_be(:url) { "#{project_google_cloud_deployments_cloud_run_path(project)}" }
@@ -92,11 +114,9 @@ RSpec.describe Projects::GoogleCloud::DeploymentsController do
expect(response).to redirect_to(project_google_cloud_deployments_path(project))
# since GPC_PROJECT_ID is not set, enable cloud run service should return an error
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'deployments#cloud_run',
- label: 'error_enable_cloud_run',
- extra: { message: 'No GCP projects found. Configure a service account or GCP_PROJECT_ID ci variable.',
- status: :error },
+ category: 'Projects::GoogleCloud::DeploymentsController',
+ action: 'error_enable_services',
+ label: nil,
project: project,
user: user_maintainer
)
@@ -113,10 +133,9 @@ RSpec.describe Projects::GoogleCloud::DeploymentsController do
expect(response).to redirect_to(project_google_cloud_deployments_path(project))
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'deployments#cloud_run',
- label: 'error_gcp',
- extra: mock_gcp_error,
+ category: 'Projects::GoogleCloud::DeploymentsController',
+ action: 'error_google_api',
+ label: nil,
project: project,
user: user_maintainer
)
@@ -136,10 +155,9 @@ RSpec.describe Projects::GoogleCloud::DeploymentsController do
expect(response).to redirect_to(project_google_cloud_deployments_path(project))
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'deployments#cloud_run',
- label: 'error_generate_pipeline',
- extra: { status: :error },
+ category: 'Projects::GoogleCloud::DeploymentsController',
+ action: 'error_generate_cloudrun_pipeline',
+ label: nil,
project: project,
user: user_maintainer
)
@@ -159,15 +177,9 @@ RSpec.describe Projects::GoogleCloud::DeploymentsController do
expect(response).to have_gitlab_http_status(:found)
expect(response.location).to include(project_new_merge_request_path(project))
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'deployments#cloud_run',
- label: 'success',
- extra: { "title": "Enable deployments to Cloud Run",
- "description": "This merge request includes a Cloud Run deployment job in the pipeline definition (.gitlab-ci.yml).\n\nThe `deploy-to-cloud-run` job:\n* Requires the following environment variables\n * `GCP_PROJECT_ID`\n * `GCP_SERVICE_ACCOUNT_KEY`\n* Job definition can be found at: https://gitlab.com/gitlab-org/incubation-engineering/five-minute-production/library\n\nThis pipeline definition has been committed to the branch ``.\nYou may modify the pipeline definition further or accept the changes as-is if suitable.\n",
- "source_project_id": project.id,
- "target_project_id": project.id,
- "source_branch": nil,
- "target_branch": project.default_branch },
+ category: 'Projects::GoogleCloud::DeploymentsController',
+ action: 'generate_cloudrun_pipeline',
+ label: nil,
project: project,
user: user_maintainer
)
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 f88273080d5..e77bcdb40b8 100644
--- a/spec/requests/projects/google_cloud/gcp_regions_controller_spec.rb
+++ b/spec/requests/projects/google_cloud/gcp_regions_controller_spec.rb
@@ -13,10 +13,9 @@ RSpec.describe Projects::GoogleCloud::GcpRegionsController do
it "tracks event" do
is_expected.to be(404)
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'admin_project_google_cloud!',
- label: 'error_access_denied',
- property: 'invalid_user',
+ category: 'Projects::GoogleCloud::GcpRegionsController',
+ action: 'error_invalid_user',
+ label: nil,
project: project,
user: nil
)
@@ -27,10 +26,9 @@ RSpec.describe Projects::GoogleCloud::GcpRegionsController do
it "tracks event" do
is_expected.to be(404)
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'admin_project_google_cloud!',
- label: 'error_access_denied',
- property: 'invalid_user',
+ category: 'Projects::GoogleCloud::GcpRegionsController',
+ action: 'error_invalid_user',
+ label: nil,
project: project,
user: nil
)
@@ -41,10 +39,9 @@ RSpec.describe Projects::GoogleCloud::GcpRegionsController do
it "tracks event" do
is_expected.to be(404)
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'feature_flag_enabled!',
- label: 'error_access_denied',
- property: 'feature_flag_not_enabled',
+ category: 'Projects::GoogleCloud::GcpRegionsController',
+ action: 'error_feature_flag_not_enabled',
+ label: nil,
project: project,
user: user_maintainer
)
@@ -55,10 +52,9 @@ RSpec.describe Projects::GoogleCloud::GcpRegionsController do
it "tracks event" do
is_expected.to be(403)
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'google_oauth2_enabled!',
- label: 'error_access_denied',
- extra: { reason: 'google_oauth2_not_configured', config: config },
+ category: 'Projects::GoogleCloud::GcpRegionsController',
+ action: 'error_google_oauth2_not_enabled',
+ label: nil,
project: project,
user: user_maintainer
)
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 36441a184cb..9bd8468767d 100644
--- a/spec/requests/projects/google_cloud/revoke_oauth_controller_spec.rb
+++ b/spec/requests/projects/google_cloud/revoke_oauth_controller_spec.rb
@@ -50,10 +50,9 @@ RSpec.describe Projects::GoogleCloud::RevokeOauthController do
expect(response).to redirect_to(project_google_cloud_configuration_path(project))
expect(flash[:notice]).to eq('Google OAuth2 token revocation requested')
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'revoke_oauth#create',
- label: 'success',
- property: '{}',
+ category: 'Projects::GoogleCloud::RevokeOauthController',
+ action: 'revoke_oauth',
+ label: nil,
project: project,
user: user
)
@@ -73,10 +72,9 @@ RSpec.describe Projects::GoogleCloud::RevokeOauthController do
expect(response).to redirect_to(project_google_cloud_configuration_path(project))
expect(flash[:alert]).to eq('Google OAuth2 token revocation request failed')
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'revoke_oauth#create',
- label: 'error',
- property: '{}',
+ category: 'Projects::GoogleCloud::RevokeOauthController',
+ action: 'error',
+ label: nil,
project: project,
user: user
)
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 ae2519855db..133c6f9153d 100644
--- a/spec/requests/projects/google_cloud/service_accounts_controller_spec.rb
+++ b/spec/requests/projects/google_cloud/service_accounts_controller_spec.rb
@@ -30,10 +30,9 @@ RSpec.describe Projects::GoogleCloud::ServiceAccountsController do
expect(response).to have_gitlab_http_status(:not_found)
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'admin_project_google_cloud!',
- label: 'error_access_denied',
- property: 'invalid_user',
+ category: 'Projects::GoogleCloud::ServiceAccountsController',
+ action: 'error_invalid_user',
+ label: nil,
project: project,
user: nil
)
@@ -53,10 +52,9 @@ RSpec.describe Projects::GoogleCloud::ServiceAccountsController do
get url
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'admin_project_google_cloud!',
- label: 'error_access_denied',
- property: 'invalid_user',
+ category: 'Projects::GoogleCloud::ServiceAccountsController',
+ action: 'error_invalid_user',
+ label: nil,
project: project,
user: unauthorized_member
)
@@ -71,10 +69,9 @@ RSpec.describe Projects::GoogleCloud::ServiceAccountsController do
post url
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'admin_project_google_cloud!',
- label: 'error_access_denied',
- property: 'invalid_user',
+ category: 'Projects::GoogleCloud::ServiceAccountsController',
+ action: 'error_invalid_user',
+ label: nil,
project: project,
user: unauthorized_member
)
@@ -135,10 +132,9 @@ RSpec.describe Projects::GoogleCloud::ServiceAccountsController do
expect(response).to redirect_to(project_google_cloud_configuration_path(project))
expect(flash[:warning]).to eq('No Google Cloud projects - You need at least one Google Cloud project')
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'service_accounts#index',
- label: 'error_form',
- property: 'no_gcp_projects',
+ category: 'Projects::GoogleCloud::ServiceAccountsController',
+ action: 'error_no_gcp_projects',
+ label: nil,
project: project,
user: authorized_member
)
@@ -207,11 +203,10 @@ RSpec.describe Projects::GoogleCloud::ServiceAccountsController do
expect(response).to redirect_to(project_google_cloud_configuration_path(project))
expect(flash[:warning]).to eq('Google Cloud Error - client-error')
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'service_accounts#index',
- label: 'error_gcp',
- extra: google_client_error,
+ category: 'Projects::GoogleCloud::ServiceAccountsController',
+ action: 'error_google_api',
project: project,
+ label: nil,
user: authorized_member
)
end
@@ -226,10 +221,9 @@ RSpec.describe Projects::GoogleCloud::ServiceAccountsController do
expect(response).to redirect_to(project_google_cloud_configuration_path(project))
expect(flash[:warning]).to eq('Google Cloud Error - client-error')
expect_snowplow_event(
- category: 'Projects::GoogleCloud',
- action: 'service_accounts#create',
- label: 'error_gcp',
- extra: google_client_error,
+ category: 'Projects::GoogleCloud::ServiceAccountsController',
+ action: 'error_google_api',
+ label: nil,
project: project,
user: authorized_member
)
diff --git a/spec/requests/projects/hook_logs_controller_spec.rb b/spec/requests/projects/hook_logs_controller_spec.rb
new file mode 100644
index 00000000000..8b3ec307e53
--- /dev/null
+++ b/spec/requests/projects/hook_logs_controller_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::HookLogsController 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) }
+
+ let(:project) { web_hook.project }
+
+ it_behaves_like WebHooks::HookLogActions do
+ let(:edit_hook_path) { edit_project_hook_url(project, web_hook) }
+
+ before do
+ project.add_owner(user)
+ end
+ end
+end
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 7be863aae75..c859e91e21a 100644
--- a/spec/requests/projects/merge_requests/context_commit_diffs_spec.rb
+++ b/spec/requests/projects/merge_requests/context_commit_diffs_spec.rb
@@ -42,7 +42,7 @@ RSpec.describe 'Merge Requests Context Commit Diffs' do
}
end
- def go(extra_params = {})
+ def go(headers: {}, **extra_params)
params = {
namespace_id: project.namespace.to_param,
project_id: project,
@@ -56,10 +56,20 @@ RSpec.describe 'Merge Requests Context Commit Diffs' do
get diffs_batch_namespace_project_json_merge_request_path(params.merge(extra_params)), headers: headers
end
- context 'with caching', :use_clean_rails_memory_store_caching do
- subject { go(page: 0, per_page: 5) }
+ context 'without caching' do
+ subject { go(headers: headers, page: 0, per_page: 5) }
+
+ let(:headers) { {} }
+ let(:collection) { Gitlab::Diff::FileCollection::Compare }
+ let(:expected_options) { collection_arguments }
+ it_behaves_like 'serializes diffs with expected arguments'
+ end
+
+ context 'with caching', :use_clean_rails_memory_store_caching do
context 'when the request has not been cached' do
+ subject { go(headers: { 'If-None-Match' => '' }, page: 0, per_page: 5) }
+
it_behaves_like 'serializes diffs with expected arguments' do
let(:collection) { Gitlab::Diff::FileCollection::Compare }
let(:expected_options) { collection_arguments }
@@ -67,16 +77,18 @@ RSpec.describe 'Merge Requests Context Commit Diffs' do
end
context 'when the request has already been cached' do
+ subject { go(headers: { 'If-None-Match' => response.etag }, page: 0, per_page: 5) }
+
before do
go(page: 0, per_page: 5)
end
it 'does not serialize diffs' do
- expect_next_instance_of(PaginatedDiffSerializer) do |instance|
- expect(instance).not_to receive(:represent)
- end
+ expect(PaginatedDiffSerializer).not_to receive(:new)
+
+ go(headers: { 'If-None-Match' => response.etag }, page: 0, per_page: 5)
- subject
+ expect(response).to have_gitlab_http_status(:not_modified)
end
context 'with the different user' do
diff --git a/spec/requests/projects/merge_requests/diffs_spec.rb b/spec/requests/projects/merge_requests/diffs_spec.rb
index 937b0f1d713..9f0b9a9cb1b 100644
--- a/spec/requests/projects/merge_requests/diffs_spec.rb
+++ b/spec/requests/projects/merge_requests/diffs_spec.rb
@@ -53,247 +53,154 @@ RSpec.describe 'Merge Requests Diffs' do
get diffs_batch_namespace_project_json_merge_request_path(params.merge(extra_params)), headers: headers
end
- context 'with caching', :use_clean_rails_memory_store_caching do
+ context 'without caching' do
subject { go(headers: headers, page: 0, per_page: 5) }
let(:headers) { {} }
+ let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
+ let(:expected_options) { collection_arguments(total_pages: 20) }
- context 'when the request has not been cached' do
- let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
- let(:expected_options) { collection_arguments(total_pages: 20) }
-
- it_behaves_like 'serializes diffs with expected arguments'
- end
-
- context 'when the request has already been cached' do
- before do
- go(page: 0, per_page: 5)
- end
-
- it 'does not serialize diffs' do
- expect_next_instance_of(PaginatedDiffSerializer) do |instance|
- expect(instance).not_to receive(:represent)
- end
-
- subject
- end
-
- context 'when using ETags' do
- context 'when etag_merge_request_diff_batches is true' do
- let(:headers) { { 'If-None-Match' => response.etag } }
-
- 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 'when etag_merge_request_diff_batches is false' do
- let(:headers) { { 'If-None-Match' => response.etag } }
+ it_behaves_like 'serializes diffs with expected arguments'
+ end
- before do
- stub_feature_flags(etag_merge_request_diff_batches: false)
- end
+ context 'with caching', :use_clean_rails_memory_store_caching do
+ subject { go(headers: headers, page: 0, per_page: 5) }
- it 'does not serialize diffs' do
- expect_next_instance_of(PaginatedDiffSerializer) do |instance|
- expect(instance).not_to receive(:represent)
- end
+ let(:headers) { { 'If-None-Match' => response.etag } }
- subject
+ before do
+ go(page: 0, per_page: 5)
+ end
- expect(response).to have_gitlab_http_status(:success)
- end
- end
- end
+ it 'does not serialize diffs' do
+ expect(PaginatedDiffSerializer).not_to receive(:new)
- context 'with the different user' do
- let(:another_user) { create(:user) }
- let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
- let(:expected_options) { collection_arguments(total_pages: 20) }
+ go(headers: headers, page: 0, per_page: 5)
- before do
- project.add_maintainer(another_user)
- sign_in(another_user)
- end
+ expect(response).to have_gitlab_http_status(:not_modified)
+ end
- it_behaves_like 'serializes diffs with expected arguments'
+ context 'with the different user' do
+ let(:another_user) { create(:user) }
+ let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
+ let(:expected_options) { collection_arguments(total_pages: 20) }
- context 'when using ETag caching' do
- it_behaves_like 'serializes diffs with expected arguments' do
- let(:headers) { { 'If-None-Match' => response.etag } }
- end
- end
+ before do
+ project.add_maintainer(another_user)
+ sign_in(another_user)
end
- context 'with a new unfoldable diff position' do
- let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
- let(:expected_options) { collection_arguments(total_pages: 20) }
-
- let(:unfoldable_position) do
- create(:diff_position)
- end
-
- before do
- expect_next_instance_of(Gitlab::Diff::PositionCollection) do |instance|
- expect(instance)
- .to receive(:unfoldable)
- .and_return([unfoldable_position])
- end
- end
+ it_behaves_like 'serializes diffs with expected arguments'
+ end
- it_behaves_like 'serializes diffs with expected arguments'
+ context 'with a new unfoldable diff position' do
+ let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
+ let(:expected_options) { collection_arguments(total_pages: 20) }
- context 'when using ETag caching' do
- it_behaves_like 'serializes diffs with expected arguments' do
- let(:headers) { { 'If-None-Match' => response.etag } }
- end
- end
+ let(:unfoldable_position) do
+ create(:diff_position)
end
- context 'with disabled display_merge_conflicts_in_diff feature' do
- let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
- let(:expected_options) { collection_arguments(total_pages: 20).merge(allow_tree_conflicts: false) }
-
- before do
- stub_feature_flags(display_merge_conflicts_in_diff: false)
- end
-
- it_behaves_like 'serializes diffs with expected arguments'
-
- context 'when using ETag caching' do
- it_behaves_like 'serializes diffs with expected arguments' do
- let(:headers) { { 'If-None-Match' => response.etag } }
- end
+ before do
+ expect_next_instance_of(Gitlab::Diff::PositionCollection) do |instance|
+ expect(instance)
+ .to receive(:unfoldable)
+ .and_return([unfoldable_position])
end
end
- context 'with diff_head option' do
- subject { go(page: 0, per_page: 5, diff_head: true) }
-
- let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
- let(:expected_options) { collection_arguments(total_pages: 20).merge(merge_ref_head_diff: true) }
-
- before do
- merge_request.create_merge_head_diff!
- end
+ it_behaves_like 'serializes diffs with expected arguments'
+ end
- it_behaves_like 'serializes diffs with expected arguments'
+ context 'with disabled display_merge_conflicts_in_diff feature' do
+ let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
+ let(:expected_options) { collection_arguments(total_pages: 20).merge(allow_tree_conflicts: false) }
- context 'when using ETag caching' do
- it_behaves_like 'serializes diffs with expected arguments' do
- let(:headers) { { 'If-None-Match' => response.etag } }
- end
- end
+ before do
+ stub_feature_flags(display_merge_conflicts_in_diff: false)
end
- context 'with the different pagination option' do
- subject { go(page: 5, per_page: 5) }
+ it_behaves_like 'serializes diffs with expected arguments'
+ end
- let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
- let(:expected_options) { collection_arguments(total_pages: 20) }
+ context 'with diff_head option' do
+ subject { go(page: 0, per_page: 5, diff_head: true) }
- it_behaves_like 'serializes diffs with expected arguments'
+ let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
+ let(:expected_options) { collection_arguments(total_pages: 20).merge(merge_ref_head_diff: true) }
- context 'when using ETag caching' do
- it_behaves_like 'serializes diffs with expected arguments' do
- let(:headers) { { 'If-None-Match' => response.etag } }
- end
- end
+ before do
+ merge_request.create_merge_head_diff!
end
- context 'with the different diff_view' do
- subject { go(page: 0, per_page: 5, view: :parallel) }
-
- let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
- let(:expected_options) { collection_arguments(total_pages: 20).merge(diff_view: :parallel) }
-
- it_behaves_like 'serializes diffs with expected arguments'
+ it_behaves_like 'serializes diffs with expected arguments'
+ end
- context 'when using ETag caching' do
- it_behaves_like 'serializes diffs with expected arguments' do
- let(:headers) { { 'If-None-Match' => response.etag } }
- end
- end
- end
+ context 'with the different pagination option' do
+ subject { go(page: 5, per_page: 5) }
- context 'with the different expanded option' do
- subject { go(page: 0, per_page: 5, expanded: true ) }
+ let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
+ let(:expected_options) { collection_arguments(total_pages: 20) }
- let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
- let(:expected_options) { collection_arguments(total_pages: 20) }
+ it_behaves_like 'serializes diffs with expected arguments'
+ end
- it_behaves_like 'serializes diffs with expected arguments'
+ context 'with the different diff_view' do
+ subject { go(page: 0, per_page: 5, view: :parallel) }
- context 'when using ETag caching' do
- it_behaves_like 'serializes diffs with expected arguments' do
- let(:headers) { { 'If-None-Match' => response.etag } }
- end
- end
- end
+ let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
+ let(:expected_options) { collection_arguments(total_pages: 20).merge(diff_view: :parallel) }
- context 'with the different ignore_whitespace_change option' do
- subject { go(page: 0, per_page: 5, w: 1) }
+ it_behaves_like 'serializes diffs with expected arguments'
+ end
- let(:collection) { Gitlab::Diff::FileCollection::Compare }
- let(:expected_options) { collection_arguments(total_pages: 20) }
+ context 'with the different expanded option' do
+ subject { go(page: 0, per_page: 5, expanded: true ) }
- it_behaves_like 'serializes diffs with expected arguments'
+ let(:collection) { Gitlab::Diff::FileCollection::MergeRequestDiffBatch }
+ let(:expected_options) { collection_arguments(total_pages: 20) }
- context 'when using ETag caching' do
- it_behaves_like 'serializes diffs with expected arguments' do
- let(:headers) { { 'If-None-Match' => response.etag } }
- end
- end
- end
+ it_behaves_like 'serializes diffs with expected arguments'
end
- context 'when the paths is given' do
- subject { go(headers: headers, page: 0, per_page: 5, paths: %w[README CHANGELOG]) }
-
- before do
- go(page: 0, per_page: 5, paths: %w[README CHANGELOG])
- end
+ context 'with the different ignore_whitespace_change option' do
+ subject { go(page: 0, per_page: 5, w: 1) }
- context 'when using ETag caching' do
- let(:headers) { { 'If-None-Match' => response.etag } }
+ let(:collection) { Gitlab::Diff::FileCollection::Compare }
+ let(:expected_options) { collection_arguments(total_pages: 20) }
- context 'when etag_merge_request_diff_batches is true' do
- it 'does not serialize diffs' do
- expect(PaginatedDiffSerializer).not_to receive(:new)
+ it_behaves_like 'serializes diffs with expected arguments'
+ end
+ end
- subject
+ context 'when the paths is given' do
+ subject { go(headers: headers, page: 0, per_page: 5, paths: %w[README CHANGELOG]) }
- expect(response).to have_gitlab_http_status(:not_modified)
- end
- end
+ before do
+ go(page: 0, per_page: 5, paths: %w[README CHANGELOG])
+ end
- context 'when etag_merge_request_diff_batches is false' do
- before do
- stub_feature_flags(etag_merge_request_diff_batches: false)
- end
+ context 'when using ETag caching' do
+ let(:headers) { { 'If-None-Match' => response.etag } }
- it 'does not use cache' do
- expect(Rails.cache).not_to receive(:fetch).with(/cache:gitlab:PaginatedDiffSerializer/).and_call_original
+ it 'does not serialize diffs' do
+ expect(PaginatedDiffSerializer).not_to receive(:new)
- subject
+ subject
- expect(response).to have_gitlab_http_status(:success)
- end
- end
+ expect(response).to have_gitlab_http_status(:not_modified)
end
+ end
- context 'when not using ETag caching' do
- it 'does not use cache' do
- expect(Rails.cache).not_to receive(:fetch).with(/cache:gitlab:PaginatedDiffSerializer/).and_call_original
+ context 'when not using ETag caching' do
+ let(:headers) { {} }
- subject
+ it 'does not use cache' do
+ expect(Rails.cache).not_to receive(:fetch).with(/cache:gitlab:PaginatedDiffSerializer/).and_call_original
- expect(response).to have_gitlab_http_status(:success)
- end
+ subject
+
+ expect(response).to have_gitlab_http_status(:success)
end
end
end
diff --git a/spec/requests/projects/merge_requests_discussions_spec.rb b/spec/requests/projects/merge_requests_discussions_spec.rb
index 9503dafcf2a..305ca6147be 100644
--- a/spec/requests/projects/merge_requests_discussions_spec.rb
+++ b/spec/requests/projects/merge_requests_discussions_spec.rb
@@ -37,12 +37,10 @@ RSpec.describe 'merge requests discussions' do
it 'avoids N+1 DB queries', :request_store do
send_request # warm up
- create(:diff_note_on_merge_request, noteable: merge_request,
- project: merge_request.project)
+ create(:diff_note_on_merge_request, noteable: merge_request, project: merge_request.project)
control = ActiveRecord::QueryRecorder.new { send_request }
- create(:diff_note_on_merge_request, noteable: merge_request,
- project: merge_request.project)
+ create(:diff_note_on_merge_request, noteable: merge_request, project: merge_request.project)
expect do
send_request
@@ -51,8 +49,7 @@ RSpec.describe 'merge requests discussions' do
it 'limits Gitaly queries', :request_store do
Gitlab::GitalyClient.allow_n_plus_1_calls do
- create_list(:diff_note_on_merge_request, 7, noteable: merge_request,
- project: merge_request.project)
+ create_list(:diff_note_on_merge_request, 7, noteable: merge_request, project: merge_request.project)
end
# The creations above write into the Gitaly counts
diff --git a/spec/requests/projects/packages/package_files_controller_spec.rb b/spec/requests/projects/packages/package_files_controller_spec.rb
new file mode 100644
index 00000000000..a6daf57f0fa
--- /dev/null
+++ b/spec/requests/projects/packages/package_files_controller_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::Packages::PackageFilesController 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) }
+
+ let(:filename) { package_file.file_name }
+
+ describe 'GET download' do
+ subject do
+ get download_namespace_project_package_file_url(
+ id: package_file.id,
+ namespace_id: project.namespace,
+ project_id: project
+ )
+ end
+
+ it 'sends the package file' do
+ subject
+
+ expect(response.headers['Content-Disposition'])
+ .to eq(%Q(attachment; filename="#{filename}"; filename*=UTF-8''#{filename}))
+ end
+
+ it_behaves_like 'bumping the package last downloaded at field'
+ end
+end
diff --git a/spec/requests/projects/settings/integration_hook_logs_controller_spec.rb b/spec/requests/projects/settings/integration_hook_logs_controller_spec.rb
new file mode 100644
index 00000000000..77daff901a1
--- /dev/null
+++ b/spec/requests/projects/settings/integration_hook_logs_controller_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::Settings::IntegrationHookLogsController do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:integration) { create(:datadog_integration) }
+ let_it_be_with_refind(:web_hook) { integration.service_hook }
+ let_it_be_with_refind(:web_hook_log) { create(:web_hook_log, web_hook: web_hook) }
+
+ let(:project) { integration.project }
+
+ it_behaves_like WebHooks::HookLogActions do
+ let(:edit_hook_path) { edit_project_settings_integration_url(project, integration) }
+
+ before do
+ project.add_owner(user)
+ end
+ end
+end
diff --git a/spec/requests/verifies_with_email_spec.rb b/spec/requests/verifies_with_email_spec.rb
index 2f249952455..e8d3e94bd0e 100644
--- a/spec/requests/verifies_with_email_spec.rb
+++ b/spec/requests/verifies_with_email_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe 'VerifiesWithEmail', :clean_gitlab_redis_sessions, :clean_gitlab_
it 'sends an email' do
mail = find_email_for(user)
expect(mail.to).to match_array([user.email])
- expect(mail.subject).to eq('Verify your identity')
+ expect(mail.subject).to eq(s_('IdentityVerification|Verify your identity'))
end
end
@@ -50,7 +50,7 @@ RSpec.describe 'VerifiesWithEmail', :clean_gitlab_redis_sessions, :clean_gitlab_
it 'adds a verification error message' do
expect(response.body)
.to include("You&#39;ve reached the maximum amount of tries. "\
- 'Wait 10 minutes or resend a new code and try again.')
+ 'Wait 10 minutes or send a new code and try again.')
end
end
@@ -62,7 +62,8 @@ RSpec.describe 'VerifiesWithEmail', :clean_gitlab_redis_sessions, :clean_gitlab_
it_behaves_like 'prompt for email verification'
it 'adds a verification error message' do
- expect(response.body).to include(('The code is incorrect. Enter it again, or resend a new code.'))
+ expect(response.body)
+ .to include((s_('IdentityVerification|The code is incorrect. Enter it again, or send a new code.')))
end
end
@@ -75,7 +76,8 @@ RSpec.describe 'VerifiesWithEmail', :clean_gitlab_redis_sessions, :clean_gitlab_
it_behaves_like 'prompt for email verification'
it 'adds a verification error message' do
- expect(response.body).to include(('The code has expired. Resend a new code and try again.'))
+ expect(response.body)
+ .to include((s_('IdentityVerification|The code has expired. Send a new code and try again.')))
end
end
@@ -112,7 +114,8 @@ RSpec.describe 'VerifiesWithEmail', :clean_gitlab_redis_sessions, :clean_gitlab_
it 'redirects to the login form and shows an alert message' do
expect(response).to redirect_to(new_user_session_path)
- expect(flash[:alert]).to eq('Maximum login attempts exceeded. Wait 10 minutes and try again.')
+ expect(flash[:alert])
+ .to eq(s_('IdentityVerification|Maximum login attempts exceeded. Wait 10 minutes and try again.'))
end
end
@@ -217,6 +220,7 @@ RSpec.describe 'VerifiesWithEmail', :clean_gitlab_redis_sessions, :clean_gitlab_
describe 'successful_verification' do
before do
+ allow(user).to receive(:role_required?).and_return(true) # It skips the required signup info before_action
sign_in(user)
end
diff --git a/spec/routing/group_routing_spec.rb b/spec/routing/group_routing_spec.rb
index 9f5f821cc61..ae69b222280 100644
--- a/spec/routing/group_routing_spec.rb
+++ b/spec/routing/group_routing_spec.rb
@@ -71,6 +71,10 @@ RSpec.shared_examples 'groups routing' do
it 'routes to the harbor tags controller' do
expect(get("groups/#{group_path}/-/harbor/repositories/test/artifacts/test/tags")).to route_to('groups/harbor/tags#index', group_id: group_path, repository_id: 'test', artifact_id: 'test')
end
+
+ it 'routes to the observability controller' do
+ expect(get("groups/#{group_path}/-/observability")).to route_to('groups/observability#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 f701dd9c488..9317a661188 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -480,7 +480,7 @@ RSpec.describe 'project routing' do
newline_file = "new\n\nline.txt"
url_encoded_newline_file = ERB::Util.url_encode(newline_file)
assert_routing({ path: "/gitlab/gitlabhq/-/blame/master/#{url_encoded_newline_file}",
- method: :get },
+ method: :get },
{ controller: 'projects/blame', action: 'show',
namespace_id: 'gitlab', project_id: 'gitlabhq',
id: "master/#{newline_file}" })
@@ -499,7 +499,7 @@ RSpec.describe 'project routing' do
newline_file = "new\n\nline.txt"
url_encoded_newline_file = ERB::Util.url_encode(newline_file)
assert_routing({ path: "/gitlab/gitlabhq/-/blob/blob/master/blob/#{url_encoded_newline_file}",
- method: :get },
+ method: :get },
{ controller: 'projects/blob', action: 'show',
namespace_id: 'gitlab', project_id: 'gitlabhq',
id: "blob/master/blob/#{newline_file}" })
@@ -520,7 +520,7 @@ RSpec.describe 'project routing' do
newline_file = "new\n\nline.txt"
url_encoded_newline_file = ERB::Util.url_encode(newline_file)
assert_routing({ path: "/gitlab/gitlabhq/-/tree/master/#{url_encoded_newline_file}",
- method: :get },
+ method: :get },
{ controller: 'projects/tree', action: 'show',
namespace_id: 'gitlab', project_id: 'gitlabhq',
id: "master/#{newline_file}" })
@@ -540,7 +540,7 @@ RSpec.describe 'project routing' do
newline_file = "new\n\nline.txt"
url_encoded_newline_file = ERB::Util.url_encode(newline_file)
assert_routing({ path: "/gitlab/gitlabhq/-/find_file/#{url_encoded_newline_file}",
- method: :get },
+ method: :get },
{ controller: 'projects/find_file', action: 'show',
namespace_id: 'gitlab', project_id: 'gitlabhq',
id: "#{newline_file}" })
@@ -551,7 +551,7 @@ RSpec.describe 'project routing' do
newline_file = "new\n\nline.txt"
url_encoded_newline_file = ERB::Util.url_encode(newline_file)
assert_routing({ path: "/gitlab/gitlabhq/-/files/#{url_encoded_newline_file}",
- method: :get },
+ method: :get },
{ controller: 'projects/find_file', action: 'list',
namespace_id: 'gitlab', project_id: 'gitlabhq',
id: "#{newline_file}" })
@@ -570,7 +570,7 @@ RSpec.describe 'project routing' do
newline_file = "new\n\nline.txt"
url_encoded_newline_file = ERB::Util.url_encode(newline_file)
assert_routing({ path: "/gitlab/gitlabhq/-/edit/master/docs/#{url_encoded_newline_file}",
- method: :get },
+ method: :get },
{ controller: 'projects/blob', action: 'edit',
namespace_id: 'gitlab', project_id: 'gitlabhq',
id: "master/docs/#{newline_file}" })
@@ -584,7 +584,7 @@ RSpec.describe 'project routing' do
newline_file = "new\n\nline.txt"
url_encoded_newline_file = ERB::Util.url_encode(newline_file)
assert_routing({ path: "/gitlab/gitlabhq/-/edit/master/docs/#{url_encoded_newline_file}",
- method: :get },
+ method: :get },
{ controller: 'projects/blob', action: 'edit',
namespace_id: 'gitlab', project_id: 'gitlabhq',
id: "master/docs/#{newline_file}" })
@@ -600,7 +600,7 @@ RSpec.describe 'project routing' do
newline_file = "new\n\nline.txt"
url_encoded_newline_file = ERB::Util.url_encode(newline_file)
assert_routing({ path: "/gitlab/gitlabhq/-/raw/master/#{url_encoded_newline_file}",
- method: :get },
+ method: :get },
{ controller: 'projects/raw', action: 'show',
namespace_id: 'gitlab', project_id: 'gitlabhq',
id: "master/#{newline_file}" })
@@ -889,8 +889,8 @@ RSpec.describe 'project routing' do
describe Projects::Snippets::BlobsController, "routing" do
it "to #raw" do
expect(get('/gitlab/gitlabhq/-/snippets/1/raw/master/lib/version.rb'))
- .to route_to('projects/snippets/blobs#raw', namespace_id: 'gitlab',
- project_id: 'gitlabhq', snippet_id: '1', ref: 'master', path: 'lib/version.rb')
+ .to route_to('projects/snippets/blobs#raw',
+ namespace_id: 'gitlab', project_id: 'gitlabhq', snippet_id: '1', ref: 'master', path: 'lib/version.rb')
end
end
diff --git a/spec/routing/uploads_routing_spec.rb b/spec/routing/uploads_routing_spec.rb
index 41646d1b515..9eb421ec7d0 100644
--- a/spec/routing/uploads_routing_spec.rb
+++ b/spec/routing/uploads_routing_spec.rb
@@ -39,12 +39,4 @@ RSpec.describe 'Uploads', 'routing' do
expect(post("/uploads/#{model}?id=1")).not_to be_routable
end
end
-
- describe 'legacy paths' do
- include RSpec::Rails::RequestExampleGroup
-
- it 'redirects project uploads to canonical path under project namespace' do
- expect(get('/uploads/namespace/project/12345/test.png')).to redirect_to('/namespace/project/uploads/12345/test.png')
- end
- end
end
diff --git a/spec/rubocop/check_graceful_task_spec.rb b/spec/rubocop/check_graceful_task_spec.rb
new file mode 100644
index 00000000000..0364820a602
--- /dev/null
+++ b/spec/rubocop/check_graceful_task_spec.rb
@@ -0,0 +1,125 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'stringio'
+
+require_relative '../support/helpers/next_instance_of'
+require_relative '../../rubocop/check_graceful_task'
+
+RSpec.describe RuboCop::CheckGracefulTask do
+ include NextInstanceOf
+
+ let(:output) { StringIO.new }
+
+ subject(:task) { described_class.new(output) }
+
+ describe '#run' do
+ let(:status_success) { RuboCop::CLI::STATUS_SUCCESS }
+ let(:status_offenses) { RuboCop::CLI::STATUS_OFFENSES }
+ let(:rubocop_status) { status_success }
+ let(:adjusted_rubocop_status) { rubocop_status }
+
+ subject { task.run(args) }
+
+ before do
+ # Don't notify Slack accidentally.
+ allow(Gitlab::Popen).to receive(:popen).and_raise('Notifications forbidden.')
+ stub_const('ENV', ENV.to_hash.delete_if { |key, _| key.start_with?('CI_') })
+
+ allow_next_instance_of(RuboCop::CLI) do |cli|
+ allow(cli).to receive(:run).and_return(rubocop_status)
+ end
+
+ allow(RuboCop::Formatter::GracefulFormatter)
+ .to receive(:adjusted_exit_status).and_return(adjusted_rubocop_status)
+ end
+
+ shared_examples 'rubocop scan' do |rubocop_args:|
+ it 'invokes a RuboCop scan' do
+ rubocop_options = %w[--parallel --format RuboCop::Formatter::GracefulFormatter]
+ rubocop_options.concat(rubocop_args)
+
+ expect_next_instance_of(RuboCop::CLI) do |cli|
+ expect(cli).to receive(:run).with(rubocop_options).and_return(rubocop_status)
+ end
+
+ subject
+
+ expect(output.string)
+ .to include('Running RuboCop in graceful mode:')
+ .and include("rubocop #{rubocop_options.join(' ')}")
+ .and include('This might take a while...')
+ end
+ end
+
+ context 'without args' do
+ let(:args) { [] }
+
+ it_behaves_like 'rubocop scan', rubocop_args: []
+
+ context 'with adjusted rubocop status' do
+ let(:rubocop_status) { status_offenses }
+ let(:adjusted_rubocop_status) { status_success }
+
+ context 'with sufficient environment variables' do
+ let(:channel) { 'f_rubocop' }
+
+ before do
+ env = {
+ 'CI_SLACK_WEBHOOK_URL' => 'webhook_url',
+ 'CI_JOB_NAME' => 'job_name',
+ 'CI_JOB_URL' => 'job_url'
+ }
+
+ stub_const('ENV', ENV.to_hash.update(env))
+ end
+
+ it 'notifies slack' do
+ popen_args = ['scripts/slack', channel, kind_of(String), 'rubocop', kind_of(String)]
+ popen_result = ['', 0]
+ expect(Gitlab::Popen).to receive(:popen).with(popen_args).and_return(popen_result)
+
+ subject
+
+ expect(output.string).to include("Notifying Slack ##{channel}.")
+ end
+
+ context 'with when notification fails' do
+ it 'prints that notification failed' do
+ popen_result = ['', 1]
+ expect(Gitlab::Popen).to receive(:popen).and_return(popen_result)
+
+ subject
+
+ expect(output.string).to include("Failed to notify Slack channel ##{channel}.")
+ end
+ end
+ end
+
+ context 'with missing environment variables' do
+ it 'skips slack notification' do
+ expect(Gitlab::Popen).not_to receive(:popen)
+
+ subject
+
+ expect(output.string).to include('Skipping Slack notification.')
+ end
+ end
+ end
+ end
+
+ context 'with args' do
+ let(:args) { %w[a.rb Lint/EmptyFile b.rb Lint/Syntax] }
+
+ it_behaves_like 'rubocop scan', rubocop_args: %w[--only Lint/EmptyFile,Lint/Syntax a.rb b.rb]
+
+ it 'does not notify slack' do
+ expect(Gitlab::Popen).not_to receive(:popen)
+
+ subject
+
+ expect(output.string).not_to include('Skipping Slack notification.')
+ end
+ end
+ end
+end
diff --git a/spec/rubocop/code_reuse_helpers_spec.rb b/spec/rubocop/code_reuse_helpers_spec.rb
index 0d06d37d67a..a112c9754f3 100644
--- a/spec/rubocop/code_reuse_helpers_spec.rb
+++ b/spec/rubocop/code_reuse_helpers_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require 'parser/current'
require_relative '../../rubocop/code_reuse_helpers'
@@ -172,31 +172,6 @@ RSpec.describe RuboCop::CodeReuseHelpers do
end
end
- describe '#in_graphql_types?' do
- %w[
- app/graphql/types
- ee/app/graphql/ee/types
- ee/app/graphql/types
- ].each do |path|
- it "returns true for a node in #{path}" do
- node = build_and_parse_source('10', rails_root_join(path, 'foo.rb'))
-
- expect(cop.in_graphql_types?(node)).to eq(true)
- end
- end
-
- %w[
- app/graphql/resolvers
- app/foo
- ].each do |path|
- it "returns false for a node in #{path}" do
- node = build_and_parse_source('10', rails_root_join(path, 'foo.rb'))
-
- expect(cop.in_graphql_types?(node)).to eq(false)
- end
- end
- end
-
describe '#in_api?' do
it 'returns true for a node in the API directory' do
node = build_and_parse_source('10', rails_root_join('lib', 'api', 'foo.rb'))
@@ -367,7 +342,7 @@ RSpec.describe RuboCop::CodeReuseHelpers do
expect(cop)
.to receive(:add_offense)
- .with(send_node, location: :expression, message: 'oops')
+ .with(send_node, message: 'oops')
cop.disallow_send_to(def_node, 'Finder', 'oops')
end
diff --git a/spec/rubocop/cop/active_model_errors_direct_manipulation_spec.rb b/spec/rubocop/cop/active_model_errors_direct_manipulation_spec.rb
index 37fcdb38907..6be2f4945fd 100644
--- a/spec/rubocop/cop/active_model_errors_direct_manipulation_spec.rb
+++ b/spec/rubocop/cop/active_model_errors_direct_manipulation_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/active_model_errors_direct_manipulation'
RSpec.describe RuboCop::Cop::ActiveModelErrorsDirectManipulation do
- subject(:cop) { described_class.new }
-
context 'when modifying errors' do
it 'registers an offense' do
expect_offense(<<~PATTERN)
diff --git a/spec/rubocop/cop/active_record_association_reload_spec.rb b/spec/rubocop/cop/active_record_association_reload_spec.rb
index 1c0518815ee..9f101b80d9a 100644
--- a/spec/rubocop/cop/active_record_association_reload_spec.rb
+++ b/spec/rubocop/cop/active_record_association_reload_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/active_record_association_reload'
RSpec.describe RuboCop::Cop::ActiveRecordAssociationReload do
- subject(:cop) { described_class.new }
-
context 'when using ActiveRecord::Base' do
it 'registers an offense on reload usage' do
expect_offense(<<~PATTERN)
diff --git a/spec/rubocop/cop/api/base_spec.rb b/spec/rubocop/cop/api/base_spec.rb
index 547d3f53a08..66e99b75643 100644
--- a/spec/rubocop/cop/api/base_spec.rb
+++ b/spec/rubocop/cop/api/base_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/api/base'
RSpec.describe RuboCop::Cop::API::Base do
- subject(:cop) { described_class.new }
-
let(:corrected) do
<<~CORRECTED
class SomeAPI < ::API::Base
diff --git a/spec/rubocop/cop/api/grape_array_missing_coerce_spec.rb b/spec/rubocop/cop/api/grape_array_missing_coerce_spec.rb
index 01f1fc71f9a..1d1754df838 100644
--- a/spec/rubocop/cop/api/grape_array_missing_coerce_spec.rb
+++ b/spec/rubocop/cop/api/grape_array_missing_coerce_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/api/grape_array_missing_coerce'
RSpec.describe RuboCop::Cop::API::GrapeArrayMissingCoerce do
@@ -10,8 +10,6 @@ RSpec.describe RuboCop::Cop::API::GrapeArrayMissingCoerce do
"https://github.com/ruby-grape/grape/blob/master/UPGRADING.md#ensure-that-array-types-have-explicit-coercions"
end
- subject(:cop) { described_class.new }
-
it 'adds an offense with a required parameter' do
expect_offense(<<~TYPE)
class SomeAPI < Grape::API::Instance
diff --git a/spec/rubocop/cop/avoid_becomes_spec.rb b/spec/rubocop/cop/avoid_becomes_spec.rb
index 3ab1544b00d..d67b79329c9 100644
--- a/spec/rubocop/cop/avoid_becomes_spec.rb
+++ b/spec/rubocop/cop/avoid_becomes_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/avoid_becomes'
RSpec.describe RuboCop::Cop::AvoidBecomes do
- subject(:cop) { described_class.new }
-
it 'flags the use of becomes with a constant parameter' do
expect_offense(<<~CODE)
foo.becomes(Project)
diff --git a/spec/rubocop/cop/avoid_break_from_strong_memoize_spec.rb b/spec/rubocop/cop/avoid_break_from_strong_memoize_spec.rb
index cc851045c3c..9b7d988cb24 100644
--- a/spec/rubocop/cop/avoid_break_from_strong_memoize_spec.rb
+++ b/spec/rubocop/cop/avoid_break_from_strong_memoize_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/avoid_break_from_strong_memoize'
RSpec.describe RuboCop::Cop::AvoidBreakFromStrongMemoize do
- subject(:cop) { described_class.new }
-
it 'flags violation for break inside strong_memoize' do
expect_offense(<<~RUBY)
strong_memoize(:result) do
diff --git a/spec/rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers_spec.rb b/spec/rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers_spec.rb
index 90ee5772b66..eb2417a7eef 100644
--- a/spec/rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers_spec.rb
+++ b/spec/rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/avoid_keyword_arguments_in_sidekiq_workers'
RSpec.describe RuboCop::Cop::AvoidKeywordArgumentsInSidekiqWorkers do
- subject(:cop) { described_class.new }
-
it 'flags violation for keyword arguments usage in perform method signature' do
expect_offense(<<~RUBY)
def perform(id:)
diff --git a/spec/rubocop/cop/avoid_return_from_blocks_spec.rb b/spec/rubocop/cop/avoid_return_from_blocks_spec.rb
index 86098f1afcc..e35705ae791 100644
--- a/spec/rubocop/cop/avoid_return_from_blocks_spec.rb
+++ b/spec/rubocop/cop/avoid_return_from_blocks_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/avoid_return_from_blocks'
RSpec.describe RuboCop::Cop::AvoidReturnFromBlocks do
- subject(:cop) { described_class.new }
-
it 'flags violation for return inside a block' do
expect_offense(<<~RUBY)
call do
diff --git a/spec/rubocop/cop/avoid_route_redirect_leading_slash_spec.rb b/spec/rubocop/cop/avoid_route_redirect_leading_slash_spec.rb
index 61d6f45b5ba..377050eb301 100644
--- a/spec/rubocop/cop/avoid_route_redirect_leading_slash_spec.rb
+++ b/spec/rubocop/cop/avoid_route_redirect_leading_slash_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/avoid_route_redirect_leading_slash'
RSpec.describe RuboCop::Cop::AvoidRouteRedirectLeadingSlash do
- subject(:cop) { described_class.new }
-
before do
allow(cop).to receive(:in_routes?).and_return(true)
end
diff --git a/spec/rubocop/cop/ban_catch_throw_spec.rb b/spec/rubocop/cop/ban_catch_throw_spec.rb
index f255d27e7c7..a41868410eb 100644
--- a/spec/rubocop/cop/ban_catch_throw_spec.rb
+++ b/spec/rubocop/cop/ban_catch_throw_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/ban_catch_throw'
RSpec.describe RuboCop::Cop::BanCatchThrow do
- subject(:cop) { described_class.new }
-
it 'registers an offense when `catch` or `throw` are used' do
expect_offense(<<~CODE)
catch(:foo) {
diff --git a/spec/rubocop/cop/code_reuse/finder_spec.rb b/spec/rubocop/cop/code_reuse/finder_spec.rb
index 36f44ca79da..8e285e3d988 100644
--- a/spec/rubocop/cop/code_reuse/finder_spec.rb
+++ b/spec/rubocop/cop/code_reuse/finder_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/code_reuse/finder'
RSpec.describe RuboCop::Cop::CodeReuse::Finder do
- subject(:cop) { described_class.new }
-
it 'flags the use of a Finder inside another Finder' do
allow(cop)
.to receive(:in_finder?)
diff --git a/spec/rubocop/cop/code_reuse/presenter_spec.rb b/spec/rubocop/cop/code_reuse/presenter_spec.rb
index 070a7ed760c..fb7a95f930d 100644
--- a/spec/rubocop/cop/code_reuse/presenter_spec.rb
+++ b/spec/rubocop/cop/code_reuse/presenter_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/code_reuse/presenter'
RSpec.describe RuboCop::Cop::CodeReuse::Presenter do
- subject(:cop) { described_class.new }
-
it 'flags the use of a Presenter in a Service class' do
allow(cop)
.to receive(:in_service_class?)
diff --git a/spec/rubocop/cop/code_reuse/serializer_spec.rb b/spec/rubocop/cop/code_reuse/serializer_spec.rb
index d5577caa2b4..b1f22c7b969 100644
--- a/spec/rubocop/cop/code_reuse/serializer_spec.rb
+++ b/spec/rubocop/cop/code_reuse/serializer_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/code_reuse/serializer'
RSpec.describe RuboCop::Cop::CodeReuse::Serializer do
- subject(:cop) { described_class.new }
-
it 'flags the use of a Serializer in a Service class' do
allow(cop)
.to receive(:in_service_class?)
diff --git a/spec/rubocop/cop/code_reuse/service_class_spec.rb b/spec/rubocop/cop/code_reuse/service_class_spec.rb
index 353225b2c42..5792b86a535 100644
--- a/spec/rubocop/cop/code_reuse/service_class_spec.rb
+++ b/spec/rubocop/cop/code_reuse/service_class_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/code_reuse/service_class'
RSpec.describe RuboCop::Cop::CodeReuse::ServiceClass do
- subject(:cop) { described_class.new }
-
it 'flags the use of a Service class in a Finder' do
allow(cop)
.to receive(:in_finder?)
diff --git a/spec/rubocop/cop/code_reuse/worker_spec.rb b/spec/rubocop/cop/code_reuse/worker_spec.rb
index a548e90d8e1..2df5ebc56fa 100644
--- a/spec/rubocop/cop/code_reuse/worker_spec.rb
+++ b/spec/rubocop/cop/code_reuse/worker_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/code_reuse/worker'
RSpec.describe RuboCop::Cop::CodeReuse::Worker do
- subject(:cop) { described_class.new }
-
it 'flags the use of a worker in a controller' do
allow(cop)
.to receive(:in_controller?)
diff --git a/spec/rubocop/cop/database/disable_referential_integrity_spec.rb b/spec/rubocop/cop/database/disable_referential_integrity_spec.rb
index 9ac67363cb6..5d31e55e586 100644
--- a/spec/rubocop/cop/database/disable_referential_integrity_spec.rb
+++ b/spec/rubocop/cop/database/disable_referential_integrity_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/database/disable_referential_integrity'
RSpec.describe RuboCop::Cop::Database::DisableReferentialIntegrity do
- subject(:cop) { described_class.new }
-
it 'does not flag the use of disable_referential_integrity with a send receiver' do
expect_offense(<<~SOURCE)
foo.disable_referential_integrity
diff --git a/spec/rubocop/cop/database/establish_connection_spec.rb b/spec/rubocop/cop/database/establish_connection_spec.rb
index 3919872b5e7..987f68def75 100644
--- a/spec/rubocop/cop/database/establish_connection_spec.rb
+++ b/spec/rubocop/cop/database/establish_connection_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/database/establish_connection'
RSpec.describe RuboCop::Cop::Database::EstablishConnection do
- subject(:cop) { described_class.new }
-
it 'flags the use of ActiveRecord::Base.establish_connection' do
expect_offense(<<~CODE)
ActiveRecord::Base.establish_connection
diff --git a/spec/rubocop/cop/database/multiple_databases_spec.rb b/spec/rubocop/cop/database/multiple_databases_spec.rb
index 6ee1e7b13ca..124e8aaf39c 100644
--- a/spec/rubocop/cop/database/multiple_databases_spec.rb
+++ b/spec/rubocop/cop/database/multiple_databases_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/database/multiple_databases'
RSpec.describe RuboCop::Cop::Database::MultipleDatabases do
- subject(:cop) { described_class.new }
-
it 'flags the use of ActiveRecord::Base.connection' do
expect_offense(<<~SOURCE)
ActiveRecord::Base.connection.inspect
diff --git a/spec/rubocop/cop/database/rescue_query_canceled_spec.rb b/spec/rubocop/cop/database/rescue_query_canceled_spec.rb
index 56314a18bf5..2418b45e3bb 100644
--- a/spec/rubocop/cop/database/rescue_query_canceled_spec.rb
+++ b/spec/rubocop/cop/database/rescue_query_canceled_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/database/rescue_query_canceled'
RSpec.describe RuboCop::Cop::Database::RescueQueryCanceled do
- subject(:cop) { described_class.new }
-
it 'flags the use of ActiveRecord::QueryCanceled' do
expect_offense(<<~CODE)
begin
diff --git a/spec/rubocop/cop/database/rescue_statement_timeout_spec.rb b/spec/rubocop/cop/database/rescue_statement_timeout_spec.rb
index b9b2ce1c16b..bfeba2b4d00 100644
--- a/spec/rubocop/cop/database/rescue_statement_timeout_spec.rb
+++ b/spec/rubocop/cop/database/rescue_statement_timeout_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/database/rescue_statement_timeout'
RSpec.describe RuboCop::Cop::Database::RescueStatementTimeout do
- subject(:cop) { described_class.new }
-
it 'flags the use of ActiveRecord::StatementTimeout' do
expect_offense(<<~CODE)
begin
diff --git a/spec/rubocop/cop/default_scope_spec.rb b/spec/rubocop/cop/default_scope_spec.rb
index 4fac0d465e0..d1f26cdce46 100644
--- a/spec/rubocop/cop/default_scope_spec.rb
+++ b/spec/rubocop/cop/default_scope_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/default_scope'
RSpec.describe RuboCop::Cop::DefaultScope do
- subject(:cop) { described_class.new }
-
it 'does not flag the use of default_scope with a send receiver' do
expect_no_offenses('foo.default_scope')
end
diff --git a/spec/rubocop/cop/destroy_all_spec.rb b/spec/rubocop/cop/destroy_all_spec.rb
index 468b10c3816..f984822a4e8 100644
--- a/spec/rubocop/cop/destroy_all_spec.rb
+++ b/spec/rubocop/cop/destroy_all_spec.rb
@@ -1,21 +1,19 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/destroy_all'
RSpec.describe RuboCop::Cop::DestroyAll do
- subject(:cop) { described_class.new }
-
it 'flags the use of destroy_all with a send receiver' do
expect_offense(<<~CODE)
- foo.destroy_all # rubocop: disable Cop/DestroyAll
+ foo.destroy_all
^^^^^^^^^^^^^^^ Use `delete_all` instead of `destroy_all`. [...]
CODE
end
it 'flags the use of destroy_all with a constant receiver' do
expect_offense(<<~CODE)
- User.destroy_all # rubocop: disable Cop/DestroyAll
+ User.destroy_all
^^^^^^^^^^^^^^^^ Use `delete_all` instead of `destroy_all`. [...]
CODE
end
@@ -30,7 +28,7 @@ RSpec.describe RuboCop::Cop::DestroyAll do
it 'flags the use of destroy_all with a local variable receiver' do
expect_offense(<<~CODE)
users = User.all
- users.destroy_all # rubocop: disable Cop/DestroyAll
+ users.destroy_all
^^^^^^^^^^^^^^^^^ Use `delete_all` instead of `destroy_all`. [...]
CODE
end
diff --git a/spec/rubocop/cop/file_decompression_spec.rb b/spec/rubocop/cop/file_decompression_spec.rb
index 7be1a784001..19d71a2e85b 100644
--- a/spec/rubocop/cop/file_decompression_spec.rb
+++ b/spec/rubocop/cop/file_decompression_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/file_decompression'
RSpec.describe RuboCop::Cop::FileDecompression do
- subject(:cop) { described_class.new }
-
it 'does not flag when using a system command not related to file decompression' do
expect_no_offenses('system("ls")')
end
diff --git a/spec/rubocop/cop/filename_length_spec.rb b/spec/rubocop/cop/filename_length_spec.rb
index ee128cb2781..1ea368d282f 100644
--- a/spec/rubocop/cop/filename_length_spec.rb
+++ b/spec/rubocop/cop/filename_length_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require 'rubocop/rspec/support'
require_relative '../../../rubocop/cop/filename_length'
RSpec.describe RuboCop::Cop::FilenameLength do
- subject(:cop) { described_class.new }
-
it 'does not flag files with names 100 characters long' do
expect_no_offenses('puts "it does not matter"', 'a' * 100)
end
diff --git a/spec/rubocop/cop/gemspec/avoid_executing_git_spec.rb b/spec/rubocop/cop/gemspec/avoid_executing_git_spec.rb
index f94a990a2f7..6a1982d8101 100644
--- a/spec/rubocop/cop/gemspec/avoid_executing_git_spec.rb
+++ b/spec/rubocop/cop/gemspec/avoid_executing_git_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gemspec/avoid_executing_git'
RSpec.describe RuboCop::Cop::Gemspec::AvoidExecutingGit do
- subject(:cop) { described_class.new }
-
it 'flags violation for executing git' do
expect_offense(<<~RUBY)
Gem::Specification.new do |gem|
diff --git a/spec/rubocop/cop/gitlab/avoid_feature_category_not_owned_spec.rb b/spec/rubocop/cop/gitlab/avoid_feature_category_not_owned_spec.rb
index f6c6955f6bb..9cacee5f75d 100644
--- a/spec/rubocop/cop/gitlab/avoid_feature_category_not_owned_spec.rb
+++ b/spec/rubocop/cop/gitlab/avoid_feature_category_not_owned_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/avoid_feature_category_not_owned'
RSpec.describe RuboCop::Cop::Gitlab::AvoidFeatureCategoryNotOwned do
- subject(:cop) { described_class.new }
-
shared_examples 'defining feature category on a class' do
it 'flags a method call on a class' do
expect_offense(<<~SOURCE)
@@ -31,7 +29,7 @@ RSpec.describe RuboCop::Cop::Gitlab::AvoidFeatureCategoryNotOwned do
context 'in controllers' do
before do
- allow(subject).to receive(:in_controller?).and_return(true)
+ allow(cop).to receive(:in_controller?).and_return(true)
end
it_behaves_like 'defining feature category on a class'
@@ -39,7 +37,7 @@ RSpec.describe RuboCop::Cop::Gitlab::AvoidFeatureCategoryNotOwned do
context 'in workers' do
before do
- allow(subject).to receive(:in_worker?).and_return(true)
+ allow(cop).to receive(:in_worker?).and_return(true)
end
it_behaves_like 'defining feature category on a class'
@@ -47,7 +45,7 @@ RSpec.describe RuboCop::Cop::Gitlab::AvoidFeatureCategoryNotOwned do
context 'for grape endpoints' do
before do
- allow(subject).to receive(:in_api?).and_return(true)
+ allow(cop).to receive(:in_api?).and_return(true)
end
it_behaves_like 'defining feature category on a class'
diff --git a/spec/rubocop/cop/gitlab/avoid_feature_get_spec.rb b/spec/rubocop/cop/gitlab/avoid_feature_get_spec.rb
new file mode 100644
index 00000000000..b5017bebd28
--- /dev/null
+++ b/spec/rubocop/cop/gitlab/avoid_feature_get_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'rubocop_spec_helper'
+
+require_relative '../../../../rubocop/cop/gitlab/avoid_feature_get'
+
+RSpec.describe RuboCop::Cop::Gitlab::AvoidFeatureGet do
+ let(:msg) { described_class::MSG }
+
+ subject(:cop) { described_class.new }
+
+ it 'bans use of Feature.ban' do
+ expect_offense(<<~RUBY)
+ Feature.get
+ ^^^ #{msg}
+ Feature.get(x)
+ ^^^ #{msg}
+ ::Feature.get
+ ^^^ #{msg}
+ ::Feature.get(x)
+ ^^^ #{msg}
+ RUBY
+ end
+
+ it 'ignores unrelated code' do
+ expect_no_offenses(<<~RUBY)
+ Namespace::Feature.get
+ Namespace::Feature.get(x)
+ Feature.remove(:x)
+ RUBY
+ end
+end
diff --git a/spec/rubocop/cop/gitlab/avoid_uploaded_file_from_params_spec.rb b/spec/rubocop/cop/gitlab/avoid_uploaded_file_from_params_spec.rb
index 6d69eb5456f..09d5552d40c 100644
--- a/spec/rubocop/cop/gitlab/avoid_uploaded_file_from_params_spec.rb
+++ b/spec/rubocop/cop/gitlab/avoid_uploaded_file_from_params_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/avoid_uploaded_file_from_params'
RSpec.describe RuboCop::Cop::Gitlab::AvoidUploadedFileFromParams do
- subject(:cop) { described_class.new }
-
context 'when using UploadedFile.from_params' do
it 'flags its call' do
expect_offense(<<~SOURCE)
diff --git a/spec/rubocop/cop/gitlab/bulk_insert_spec.rb b/spec/rubocop/cop/gitlab/bulk_insert_spec.rb
index 7cd003d0a70..28fdd18b0f5 100644
--- a/spec/rubocop/cop/gitlab/bulk_insert_spec.rb
+++ b/spec/rubocop/cop/gitlab/bulk_insert_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/bulk_insert'
RSpec.describe RuboCop::Cop::Gitlab::BulkInsert do
- subject(:cop) { described_class.new }
-
it 'flags the use of ApplicationRecord.legacy_bulk_insert' do
expect_offense(<<~SOURCE)
ApplicationRecord.legacy_bulk_insert('merge_request_diff_files', rows)
diff --git a/spec/rubocop/cop/gitlab/change_timezone_spec.rb b/spec/rubocop/cop/gitlab/change_timezone_spec.rb
index ff6365aa0f7..d5100cb662a 100644
--- a/spec/rubocop/cop/gitlab/change_timezone_spec.rb
+++ b/spec/rubocop/cop/gitlab/change_timezone_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/change_timezone'
RSpec.describe RuboCop::Cop::Gitlab::ChangeTimezone do
- subject(:cop) { described_class.new }
-
context 'Time.zone=' do
it 'registers an offense with no 2nd argument' do
expect_offense(<<~PATTERN)
diff --git a/spec/rubocop/cop/gitlab/const_get_inherit_false_spec.rb b/spec/rubocop/cop/gitlab/const_get_inherit_false_spec.rb
index 1d99ec93e25..99cc9e0b469 100644
--- a/spec/rubocop/cop/gitlab/const_get_inherit_false_spec.rb
+++ b/spec/rubocop/cop/gitlab/const_get_inherit_false_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/const_get_inherit_false'
RSpec.describe RuboCop::Cop::Gitlab::ConstGetInheritFalse do
- subject(:cop) { described_class.new }
-
context 'Object.const_get' do
it 'registers an offense with no 2nd argument and corrects' do
expect_offense(<<~PATTERN)
diff --git a/spec/rubocop/cop/gitlab/delegate_predicate_methods_spec.rb b/spec/rubocop/cop/gitlab/delegate_predicate_methods_spec.rb
index 1ceff0dd681..1b497954aee 100644
--- a/spec/rubocop/cop/gitlab/delegate_predicate_methods_spec.rb
+++ b/spec/rubocop/cop/gitlab/delegate_predicate_methods_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/delegate_predicate_methods'
RSpec.describe RuboCop::Cop::Gitlab::DelegatePredicateMethods do
- subject(:cop) { described_class.new }
-
it 'registers offense for single predicate method with allow_nil:true' do
expect_offense(<<~SOURCE)
delegate :is_foo?, :do_foo, to: :bar, allow_nil: true
diff --git a/spec/rubocop/cop/gitlab/deprecate_track_redis_hll_event_spec.rb b/spec/rubocop/cop/gitlab/deprecate_track_redis_hll_event_spec.rb
index 453f0c36c14..eed30e11a98 100644
--- a/spec/rubocop/cop/gitlab/deprecate_track_redis_hll_event_spec.rb
+++ b/spec/rubocop/cop/gitlab/deprecate_track_redis_hll_event_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/deprecate_track_redis_hll_event'
RSpec.describe RuboCop::Cop::Gitlab::DeprecateTrackRedisHLLEvent do
- subject(:cop) { described_class.new }
-
it 'does not flag the use of track_event' do
expect_no_offenses('track_event :show, name: "p_analytics_insights"')
end
diff --git a/spec/rubocop/cop/gitlab/duplicate_spec_location_spec.rb b/spec/rubocop/cop/gitlab/duplicate_spec_location_spec.rb
index 3b3d5b01a30..9a1639806c8 100644
--- a/spec/rubocop/cop/gitlab/duplicate_spec_location_spec.rb
+++ b/spec/rubocop/cop/gitlab/duplicate_spec_location_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/duplicate_spec_location'
RSpec.describe RuboCop::Cop::Gitlab::DuplicateSpecLocation do
- subject(:cop) { described_class.new }
-
let(:rails_root) { '../../../../' }
def full_path(path)
diff --git a/spec/rubocop/cop/gitlab/event_store_subscriber_spec.rb b/spec/rubocop/cop/gitlab/event_store_subscriber_spec.rb
index e17fb71f9bc..7c692d5aad4 100644
--- a/spec/rubocop/cop/gitlab/event_store_subscriber_spec.rb
+++ b/spec/rubocop/cop/gitlab/event_store_subscriber_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/event_store_subscriber'
RSpec.describe RuboCop::Cop::Gitlab::EventStoreSubscriber do
- subject(:cop) { described_class.new }
-
context 'when an event store subscriber overrides #perform' do
it 'registers an offense' do
expect_offense(<<~WORKER)
diff --git a/spec/rubocop/cop/gitlab/except_spec.rb b/spec/rubocop/cop/gitlab/except_spec.rb
index 04cfe261cf2..47048b8f658 100644
--- a/spec/rubocop/cop/gitlab/except_spec.rb
+++ b/spec/rubocop/cop/gitlab/except_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/except'
RSpec.describe RuboCop::Cop::Gitlab::Except do
- subject(:cop) { described_class.new }
-
it 'flags the use of Gitlab::SQL::Except.new' do
expect_offense(<<~SOURCE)
Gitlab::SQL::Except.new([foo])
diff --git a/spec/rubocop/cop/gitlab/feature_available_usage_spec.rb b/spec/rubocop/cop/gitlab/feature_available_usage_spec.rb
index 514ef357785..30edd33a318 100644
--- a/spec/rubocop/cop/gitlab/feature_available_usage_spec.rb
+++ b/spec/rubocop/cop/gitlab/feature_available_usage_spec.rb
@@ -1,13 +1,11 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+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
- subject(:cop) { described_class.new }
-
context 'no arguments given' do
it 'does not flag the use of Gitlab::Sourcegraph.feature_available? with no arguments' do
expect_no_offenses('Gitlab::Sourcegraph.feature_available?')
diff --git a/spec/rubocop/cop/gitlab/finder_with_find_by_spec.rb b/spec/rubocop/cop/gitlab/finder_with_find_by_spec.rb
index d2cd06d77c5..6e01ef1bdec 100644
--- a/spec/rubocop/cop/gitlab/finder_with_find_by_spec.rb
+++ b/spec/rubocop/cop/gitlab/finder_with_find_by_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/finder_with_find_by'
RSpec.describe RuboCop::Cop::Gitlab::FinderWithFindBy do
- subject(:cop) { described_class.new }
-
context 'when calling execute.find' do
it 'registers an offense and corrects' do
expect_offense(<<~CODE)
diff --git a/spec/rubocop/cop/gitlab/httparty_spec.rb b/spec/rubocop/cop/gitlab/httparty_spec.rb
index 98b1aa36586..09204009d9b 100644
--- a/spec/rubocop/cop/gitlab/httparty_spec.rb
+++ b/spec/rubocop/cop/gitlab/httparty_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/httparty'
RSpec.describe RuboCop::Cop::Gitlab::HTTParty do # rubocop:disable RSpec/FilePath
- subject(:cop) { described_class.new }
-
shared_examples('registering include offense') do
it 'registers an offense when the class includes HTTParty' do
expect_offense(source)
diff --git a/spec/rubocop/cop/gitlab/intersect_spec.rb b/spec/rubocop/cop/gitlab/intersect_spec.rb
index f3cb1412f35..c81dae9af97 100644
--- a/spec/rubocop/cop/gitlab/intersect_spec.rb
+++ b/spec/rubocop/cop/gitlab/intersect_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/intersect'
RSpec.describe RuboCop::Cop::Gitlab::Intersect do
- subject(:cop) { described_class.new }
-
it 'flags the use of Gitlab::SQL::Intersect.new' do
expect_offense(<<~SOURCE)
Gitlab::SQL::Intersect.new([foo])
diff --git a/spec/rubocop/cop/gitlab/json_spec.rb b/spec/rubocop/cop/gitlab/json_spec.rb
index 7998f26da4e..e4ec107747d 100644
--- a/spec/rubocop/cop/gitlab/json_spec.rb
+++ b/spec/rubocop/cop/gitlab/json_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/json'
RSpec.describe RuboCop::Cop::Gitlab::Json do
- subject(:cop) { described_class.new }
-
context 'when ::JSON is called' do
it 'registers an offense' do
expect_offense(<<~RUBY)
diff --git a/spec/rubocop/cop/gitlab/keys_first_and_values_first_spec.rb b/spec/rubocop/cop/gitlab/keys_first_and_values_first_spec.rb
new file mode 100644
index 00000000000..073c78e78c0
--- /dev/null
+++ b/spec/rubocop/cop/gitlab/keys_first_and_values_first_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require 'rubocop_spec_helper'
+
+require_relative '../../../../rubocop/cop/gitlab/keys_first_and_values_first'
+
+RSpec.describe RuboCop::Cop::Gitlab::KeysFirstAndValuesFirst do
+ let(:msg) { described_class::MSG }
+
+ subject(:cop) { described_class.new }
+
+ shared_examples 'inspect use of keys or values first' do |method, autocorrect|
+ describe ".#{method}.first" do
+ it 'flags and autocorrects' do
+ expect_offense(<<~RUBY, method: method, autocorrect: autocorrect)
+ hash.%{method}.first
+ _{method} ^^^^^ Prefer `.%{autocorrect}.first` over `.%{method}.first`. [...]
+ var = {a: 1}; var.%{method}.first
+ _{method} ^^^^^ Prefer `.%{autocorrect}.first` over `.%{method}.first`. [...]
+ {a: 1}.%{method}.first
+ _{method} ^^^^^ Prefer `.%{autocorrect}.first` over `.%{method}.first`. [...]
+ CONST.%{method}.first
+ _{method} ^^^^^ Prefer `.%{autocorrect}.first` over `.%{method}.first`. [...]
+ ::CONST.%{method}.first
+ _{method} ^^^^^ Prefer `.%{autocorrect}.first` over `.%{method}.first`. [...]
+ RUBY
+
+ expect_correction(<<~RUBY)
+ hash.#{autocorrect}.first
+ var = {a: 1}; var.#{autocorrect}.first
+ {a: 1}.#{autocorrect}.first
+ CONST.#{autocorrect}.first
+ ::CONST.#{autocorrect}.first
+ RUBY
+ end
+
+ it 'does not flag unrelated code' do
+ expect_no_offenses(<<~RUBY)
+ array.first
+ hash.#{method}.last
+ hash.#{method}
+ #{method}.first
+ 1.#{method}.first
+ 'string'.#{method}.first
+ RUBY
+ end
+ end
+ end
+
+ it_behaves_like 'inspect use of keys or values first', :keys, :each_key
+ it_behaves_like 'inspect use of keys or values first', :values, :each_value
+end
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 9ab5cdc24a4..ac7e41dda44 100644
--- a/spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb
+++ b/spec/rubocop/cop/gitlab/mark_used_feature_flags_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require 'rubocop'
require 'rubocop/rspec/support'
require_relative '../../../../rubocop/cop/gitlab/mark_used_feature_flags'
@@ -10,8 +10,6 @@ RSpec.describe RuboCop::Cop::Gitlab::MarkUsedFeatureFlags do
%w[a_feature_flag foo_hello foo_world baz_experiment_percentage bar_baz]
end
- subject(:cop) { described_class.new }
-
before do
allow(cop).to receive(:defined_feature_flags).and_return(defined_feature_flags)
allow(cop).to receive(:usage_data_counters_known_event_feature_flags).and_return([])
@@ -48,6 +46,7 @@ RSpec.describe RuboCop::Cop::Gitlab::MarkUsedFeatureFlags do
Feature.enabled?
Feature.disabled?
push_frontend_feature_flag
+ YamlProcessor::FeatureFlags.enabled?
].each do |feature_flag_method|
context "#{feature_flag_method} method" do
context 'a string feature flag' do
@@ -212,19 +211,6 @@ RSpec.describe RuboCop::Cop::Gitlab::MarkUsedFeatureFlags do
include_examples 'does not set any flags as used', 'deduplicate :delayed'
end
- describe 'GraphQL `field` method' do
- before do
- allow(cop).to receive(:in_graphql_types?).and_return(true)
- end
-
- include_examples 'sets flag as used', 'field :runners, Types::Ci::RunnerType.connection_type, null: true, _deprecated_feature_flag: :foo', 'foo'
- include_examples 'sets flag as used', 'field :runners, null: true, _deprecated_feature_flag: :foo', 'foo'
- include_examples 'does not set any flags as used', 'field :solution'
- include_examples 'does not set any flags as used', 'field :runners, Types::Ci::RunnerType.connection_type'
- include_examples 'does not set any flags as used', 'field :runners, Types::Ci::RunnerType.connection_type, null: true, description: "hello world"'
- include_examples 'does not set any flags as used', 'field :solution, type: GraphQL::Types::String, null: true, description: "URL to the vulnerabilitys details page."'
- end
-
describe "tracking of usage data metrics known events happens at the beginning of inspection" do
let(:usage_data_counters_known_event_feature_flags) { ['an_event_feature_flag'] }
diff --git a/spec/rubocop/cop/gitlab/module_with_instance_variables_spec.rb b/spec/rubocop/cop/gitlab/module_with_instance_variables_spec.rb
index d46dec3b2e3..9f1691696eb 100644
--- a/spec/rubocop/cop/gitlab/module_with_instance_variables_spec.rb
+++ b/spec/rubocop/cop/gitlab/module_with_instance_variables_spec.rb
@@ -1,13 +1,11 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/module_with_instance_variables'
RSpec.describe RuboCop::Cop::Gitlab::ModuleWithInstanceVariables do
let(:msg) { "Do not use instance variables in a module. [...]" }
- subject(:cop) { described_class.new }
-
shared_examples('registering offense') do
it 'registers an offense when instance variable is used in a module' do
expect_offense(source)
diff --git a/spec/rubocop/cop/gitlab/namespaced_class_spec.rb b/spec/rubocop/cop/gitlab/namespaced_class_spec.rb
index 83d0eaf4884..b16c3aba5c7 100644
--- a/spec/rubocop/cop/gitlab/namespaced_class_spec.rb
+++ b/spec/rubocop/cop/gitlab/namespaced_class_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/namespaced_class'
RSpec.describe RuboCop::Cop::Gitlab::NamespacedClass do
- subject(:cop) { described_class.new }
-
shared_examples 'enforces namespaced classes' do
def namespaced(code)
return code unless namespace
diff --git a/spec/rubocop/cop/gitlab/policy_rule_boolean_spec.rb b/spec/rubocop/cop/gitlab/policy_rule_boolean_spec.rb
index f73fc71b601..d00a9861c77 100644
--- a/spec/rubocop/cop/gitlab/policy_rule_boolean_spec.rb
+++ b/spec/rubocop/cop/gitlab/policy_rule_boolean_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/policy_rule_boolean'
RSpec.describe RuboCop::Cop::Gitlab::PolicyRuleBoolean do
- subject(:cop) { described_class.new }
-
it 'registers offense for &&' do
expect_offense(<<~SOURCE)
rule { conducts_electricity && batteries }.enable :light_bulb
diff --git a/spec/rubocop/cop/gitlab/predicate_memoization_spec.rb b/spec/rubocop/cop/gitlab/predicate_memoization_spec.rb
index 903c02ba194..1ca34ad90da 100644
--- a/spec/rubocop/cop/gitlab/predicate_memoization_spec.rb
+++ b/spec/rubocop/cop/gitlab/predicate_memoization_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/predicate_memoization'
RSpec.describe RuboCop::Cop::Gitlab::PredicateMemoization do
- subject(:cop) { described_class.new }
-
shared_examples('not registering offense') do
it 'does not register offenses' do
expect_no_offenses(source)
diff --git a/spec/rubocop/cop/gitlab/rails_logger_spec.rb b/spec/rubocop/cop/gitlab/rails_logger_spec.rb
index 24f49bf3044..c9d361b49b8 100644
--- a/spec/rubocop/cop/gitlab/rails_logger_spec.rb
+++ b/spec/rubocop/cop/gitlab/rails_logger_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/rails_logger'
RSpec.describe RuboCop::Cop::Gitlab::RailsLogger do
- subject(:cop) { described_class.new }
-
described_class::LOG_METHODS.each do |method|
it "flags the use of Rails.logger.#{method} with a constant receiver" do
node = "Rails.logger.#{method}('some error')"
diff --git a/spec/rubocop/cop/gitlab/union_spec.rb b/spec/rubocop/cop/gitlab/union_spec.rb
index ce84c75338d..4042fe0263a 100644
--- a/spec/rubocop/cop/gitlab/union_spec.rb
+++ b/spec/rubocop/cop/gitlab/union_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/gitlab/union'
RSpec.describe RuboCop::Cop::Gitlab::Union do
- subject(:cop) { described_class.new }
-
it 'flags the use of Gitlab::SQL::Union.new' do
expect_offense(<<~SOURCE)
Gitlab::SQL::Union.new([foo])
diff --git a/spec/rubocop/cop/graphql/authorize_types_spec.rb b/spec/rubocop/cop/graphql/authorize_types_spec.rb
index 7aa36030526..a30cd5a1688 100644
--- a/spec/rubocop/cop/graphql/authorize_types_spec.rb
+++ b/spec/rubocop/cop/graphql/authorize_types_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/graphql/authorize_types'
RSpec.describe RuboCop::Cop::Graphql::AuthorizeTypes do
- subject(:cop) { described_class.new }
-
it 'adds an offense when there is no authorize call' do
expect_offense(<<~TYPE)
module Types
diff --git a/spec/rubocop/cop/graphql/descriptions_spec.rb b/spec/rubocop/cop/graphql/descriptions_spec.rb
index 84520a89b08..8826e700fdf 100644
--- a/spec/rubocop/cop/graphql/descriptions_spec.rb
+++ b/spec/rubocop/cop/graphql/descriptions_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/graphql/descriptions'
RSpec.describe RuboCop::Cop::Graphql::Descriptions do
- subject(:cop) { described_class.new }
-
context 'with fields' do
it 'adds an offense when there is no description' do
expect_offense(<<~TYPE)
diff --git a/spec/rubocop/cop/graphql/gid_expected_type_spec.rb b/spec/rubocop/cop/graphql/gid_expected_type_spec.rb
index 47a6ce24d53..563c16a99df 100644
--- a/spec/rubocop/cop/graphql/gid_expected_type_spec.rb
+++ b/spec/rubocop/cop/graphql/gid_expected_type_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/graphql/gid_expected_type'
RSpec.describe RuboCop::Cop::Graphql::GIDExpectedType do
- subject(:cop) { described_class.new }
-
it 'adds an offense when there is no expected_type parameter' do
expect_offense(<<~TYPE)
GitlabSchema.object_from_id(received_id)
diff --git a/spec/rubocop/cop/graphql/graphql_name_position_spec.rb b/spec/rubocop/cop/graphql/graphql_name_position_spec.rb
index 42cc398ed84..5db6fe6a801 100644
--- a/spec/rubocop/cop/graphql/graphql_name_position_spec.rb
+++ b/spec/rubocop/cop/graphql/graphql_name_position_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/graphql/graphql_name_position'
RSpec.describe RuboCop::Cop::Graphql::GraphqlNamePosition do
- subject(:cop) { described_class.new }
-
it 'adds an offense when graphql_name is not on the first line' do
expect_offense(<<~TYPE)
module Types
diff --git a/spec/rubocop/cop/graphql/id_type_spec.rb b/spec/rubocop/cop/graphql/id_type_spec.rb
index d71031c6e1a..3a56753d39e 100644
--- a/spec/rubocop/cop/graphql/id_type_spec.rb
+++ b/spec/rubocop/cop/graphql/id_type_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/graphql/id_type'
RSpec.describe RuboCop::Cop::Graphql::IDType do
- subject(:cop) { described_class.new }
-
it 'adds an offense when GraphQL::Types::ID is used as a param to #argument' do
expect_offense(<<~TYPE)
argument :some_arg, GraphQL::Types::ID, some: other, params: do_not_matter
diff --git a/spec/rubocop/cop/graphql/json_type_spec.rb b/spec/rubocop/cop/graphql/json_type_spec.rb
index 882e2b2ef88..c72e5b5b1c9 100644
--- a/spec/rubocop/cop/graphql/json_type_spec.rb
+++ b/spec/rubocop/cop/graphql/json_type_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/graphql/json_type'
RSpec.describe RuboCop::Cop::Graphql::JSONType do
@@ -8,8 +8,6 @@ RSpec.describe RuboCop::Cop::Graphql::JSONType do
'Avoid using GraphQL::Types::JSON. See: https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#json'
end
- subject(:cop) { described_class.new }
-
context 'fields' do
it 'adds an offense when GraphQL::Types::JSON is used' do
expect_offense(<<~RUBY)
diff --git a/spec/rubocop/cop/graphql/old_types_spec.rb b/spec/rubocop/cop/graphql/old_types_spec.rb
index 5cf3b11548f..45d47f3b516 100644
--- a/spec/rubocop/cop/graphql/old_types_spec.rb
+++ b/spec/rubocop/cop/graphql/old_types_spec.rb
@@ -1,14 +1,12 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require 'rspec-parameterized'
require_relative '../../../../rubocop/cop/graphql/old_types'
RSpec.describe RuboCop::Cop::Graphql::OldTypes do
using RSpec::Parameterized::TableSyntax
- subject(:cop) { described_class.new }
-
where(:old_type, :message) do
'GraphQL::ID_TYPE' | 'Avoid using GraphQL::ID_TYPE. Use GraphQL::Types::ID instead'
'GraphQL::INT_TYPE' | 'Avoid using GraphQL::INT_TYPE. Use GraphQL::Types::Int instead'
diff --git a/spec/rubocop/cop/graphql/resolver_type_spec.rb b/spec/rubocop/cop/graphql/resolver_type_spec.rb
index 06bf90a8a07..ade1bfc2cca 100644
--- a/spec/rubocop/cop/graphql/resolver_type_spec.rb
+++ b/spec/rubocop/cop/graphql/resolver_type_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/graphql/resolver_type'
RSpec.describe RuboCop::Cop::Graphql::ResolverType do
- subject(:cop) { described_class.new }
-
it 'adds an offense when there is no type annotation' do
expect_offense(<<~SRC)
module Resolvers
diff --git a/spec/rubocop/cop/group_public_or_visible_to_user_spec.rb b/spec/rubocop/cop/group_public_or_visible_to_user_spec.rb
index 2348552f9e4..c948ea606b8 100644
--- a/spec/rubocop/cop/group_public_or_visible_to_user_spec.rb
+++ b/spec/rubocop/cop/group_public_or_visible_to_user_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/group_public_or_visible_to_user'
RSpec.describe RuboCop::Cop::GroupPublicOrVisibleToUser do
@@ -9,8 +9,6 @@ RSpec.describe RuboCop::Cop::GroupPublicOrVisibleToUser do
"Please ensure that you are not using it on its own and that the amount of rows being filtered is reasonable."
end
- subject(:cop) { described_class.new }
-
it 'flags the use of Group.public_or_visible_to_user with a constant receiver' do
expect_offense(<<~CODE)
Group.public_or_visible_to_user
diff --git a/spec/rubocop/cop/ignored_columns_spec.rb b/spec/rubocop/cop/ignored_columns_spec.rb
index f87b1a1e520..c6c44399624 100644
--- a/spec/rubocop/cop/ignored_columns_spec.rb
+++ b/spec/rubocop/cop/ignored_columns_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/ignored_columns'
RSpec.describe RuboCop::Cop::IgnoredColumns do
- subject(:cop) { described_class.new }
-
it 'flags direct use of ignored_columns instead of the IgnoredColumns concern' do
expect_offense(<<~RUBY)
class Foo < ApplicationRecord
diff --git a/spec/rubocop/cop/include_sidekiq_worker_spec.rb b/spec/rubocop/cop/include_sidekiq_worker_spec.rb
index 8c706925ab9..f86bb1427db 100644
--- a/spec/rubocop/cop/include_sidekiq_worker_spec.rb
+++ b/spec/rubocop/cop/include_sidekiq_worker_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/include_sidekiq_worker'
RSpec.describe RuboCop::Cop::IncludeSidekiqWorker do
- subject(:cop) { described_class.new }
-
context 'when `Sidekiq::Worker` is included' do
it 'registers an offense and corrects', :aggregate_failures do
expect_offense(<<~CODE)
diff --git a/spec/rubocop/cop/inject_enterprise_edition_module_spec.rb b/spec/rubocop/cop/inject_enterprise_edition_module_spec.rb
index 3596badc599..3063a474bd7 100644
--- a/spec/rubocop/cop/inject_enterprise_edition_module_spec.rb
+++ b/spec/rubocop/cop/inject_enterprise_edition_module_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/inject_enterprise_edition_module'
RSpec.describe RuboCop::Cop::InjectEnterpriseEditionModule do
- subject(:cop) { described_class.new }
-
it 'flags the use of `prepend_mod_with` in the middle of a file' do
expect_offense(<<~SOURCE)
class Foo
diff --git a/spec/rubocop/cop/lint/last_keyword_argument_spec.rb b/spec/rubocop/cop/lint/last_keyword_argument_spec.rb
index b1b4c88e0f6..b0551a79c50 100644
--- a/spec/rubocop/cop/lint/last_keyword_argument_spec.rb
+++ b/spec/rubocop/cop/lint/last_keyword_argument_spec.rb
@@ -1,19 +1,18 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/lint/last_keyword_argument'
RSpec.describe RuboCop::Cop::Lint::LastKeywordArgument do
- subject(:cop) { described_class.new }
-
before do
described_class.instance_variable_set(:@keyword_warnings, nil)
+ allow(Dir).to receive(:glob).and_call_original
+ allow(File).to receive(:read).and_call_original
end
context 'deprecation files does not exist' do
before do
- allow(Dir).to receive(:glob).and_return([])
- allow(File).to receive(:exist?).and_return(false)
+ allow(Dir).to receive(:glob).with(described_class::DEPRECATIONS_GLOB).and_return([])
end
it 'does not register an offense' do
@@ -58,7 +57,8 @@ RSpec.describe RuboCop::Cop::Lint::LastKeywordArgument do
before do
allow(Dir).to receive(:glob).and_return(['deprecations/service/create_spec.yml', 'deprecations/api/projects_spec.yml'])
- allow(File).to receive(:read).and_return(create_spec_yaml, projects_spec_yaml)
+ allow(File).to receive(:read).with('deprecations/service/create_spec.yml').and_return(create_spec_yaml)
+ allow(File).to receive(:read).with('deprecations/api/projects_spec.yml').and_return(projects_spec_yaml)
end
it 'registers an offense for last keyword warning' do
diff --git a/spec/rubocop/cop/migration/add_column_with_default_spec.rb b/spec/rubocop/cop/migration/add_column_with_default_spec.rb
index 3f47613280f..865f567db44 100644
--- a/spec/rubocop/cop/migration/add_column_with_default_spec.rb
+++ b/spec/rubocop/cop/migration/add_column_with_default_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/add_column_with_default'
RSpec.describe RuboCop::Cop::Migration::AddColumnWithDefault do
diff --git a/spec/rubocop/cop/migration/add_columns_to_wide_tables_spec.rb b/spec/rubocop/cop/migration/add_columns_to_wide_tables_spec.rb
index b78ec971245..7cc88946cf1 100644
--- a/spec/rubocop/cop/migration/add_columns_to_wide_tables_spec.rb
+++ b/spec/rubocop/cop/migration/add_columns_to_wide_tables_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/add_columns_to_wide_tables'
RSpec.describe RuboCop::Cop::Migration::AddColumnsToWideTables do
diff --git a/spec/rubocop/cop/migration/add_concurrent_foreign_key_spec.rb b/spec/rubocop/cop/migration/add_concurrent_foreign_key_spec.rb
index 572c0d414b3..aa39f5f1603 100644
--- a/spec/rubocop/cop/migration/add_concurrent_foreign_key_spec.rb
+++ b/spec/rubocop/cop/migration/add_concurrent_foreign_key_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/add_concurrent_foreign_key'
RSpec.describe RuboCop::Cop::Migration::AddConcurrentForeignKey do
diff --git a/spec/rubocop/cop/migration/add_concurrent_index_spec.rb b/spec/rubocop/cop/migration/add_concurrent_index_spec.rb
index 52b3a5769ff..185b64b0334 100644
--- a/spec/rubocop/cop/migration/add_concurrent_index_spec.rb
+++ b/spec/rubocop/cop/migration/add_concurrent_index_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/add_concurrent_index'
RSpec.describe RuboCop::Cop::Migration::AddConcurrentIndex do
- subject(:cop) { described_class.new }
-
context 'when in migration' do
before do
allow(cop).to receive(:in_migration?).and_return(true)
diff --git a/spec/rubocop/cop/migration/add_index_spec.rb b/spec/rubocop/cop/migration/add_index_spec.rb
index 088bfe434f4..338dbf73a3a 100644
--- a/spec/rubocop/cop/migration/add_index_spec.rb
+++ b/spec/rubocop/cop/migration/add_index_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/add_index'
RSpec.describe RuboCop::Cop::Migration::AddIndex do
- subject(:cop) { described_class.new }
-
context 'in migration' do
before do
allow(cop).to receive(:in_migration?).and_return(true)
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 f6bed0d74fb..85a86a27c48 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
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/add_limit_to_text_columns'
RSpec.describe RuboCop::Cop::Migration::AddLimitToTextColumns do
- subject(:cop) { described_class.new }
-
context 'when in migration' do
let(:msg) { 'Text columns should always have a limit set (255 is suggested)[...]' }
diff --git a/spec/rubocop/cop/migration/add_reference_spec.rb b/spec/rubocop/cop/migration/add_reference_spec.rb
index 9445780e9ed..bb3fe7068b4 100644
--- a/spec/rubocop/cop/migration/add_reference_spec.rb
+++ b/spec/rubocop/cop/migration/add_reference_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/add_reference'
RSpec.describe RuboCop::Cop::Migration::AddReference do
diff --git a/spec/rubocop/cop/migration/add_timestamps_spec.rb b/spec/rubocop/cop/migration/add_timestamps_spec.rb
index 2a11d46be6e..fcc2f4aa363 100644
--- a/spec/rubocop/cop/migration/add_timestamps_spec.rb
+++ b/spec/rubocop/cop/migration/add_timestamps_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/add_timestamps'
RSpec.describe RuboCop::Cop::Migration::AddTimestamps do
- subject(:cop) { described_class.new }
-
let(:migration_with_add_timestamps) do
%q(
class Users < ActiveRecord::Migration[4.2]
diff --git a/spec/rubocop/cop/migration/background_migration_base_class_spec.rb b/spec/rubocop/cop/migration/background_migration_base_class_spec.rb
index 0a110418139..8cc85ac692c 100644
--- a/spec/rubocop/cop/migration/background_migration_base_class_spec.rb
+++ b/spec/rubocop/cop/migration/background_migration_base_class_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/background_migration_base_class'
RSpec.describe RuboCop::Cop::Migration::BackgroundMigrationBaseClass do
- subject(:cop) { described_class.new }
-
context 'when the migration class inherits from BatchedMigrationJob' do
it 'does not register any offenses' do
expect_no_offenses(<<~RUBY)
diff --git a/spec/rubocop/cop/migration/background_migration_record_spec.rb b/spec/rubocop/cop/migration/background_migration_record_spec.rb
index b5724ef1efd..d5a451e00c9 100644
--- a/spec/rubocop/cop/migration/background_migration_record_spec.rb
+++ b/spec/rubocop/cop/migration/background_migration_record_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/background_migration_record'
RSpec.describe RuboCop::Cop::Migration::BackgroundMigrationRecord do
- subject(:cop) { described_class.new }
-
context 'outside of a migration' do
it 'does not register any offenses' do
expect_no_offenses(<<~SOURCE)
diff --git a/spec/rubocop/cop/migration/background_migrations_spec.rb b/spec/rubocop/cop/migration/background_migrations_spec.rb
index 3242211ab47..681bbd84562 100644
--- a/spec/rubocop/cop/migration/background_migrations_spec.rb
+++ b/spec/rubocop/cop/migration/background_migrations_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/background_migrations'
RSpec.describe RuboCop::Cop::Migration::BackgroundMigrations do
diff --git a/spec/rubocop/cop/migration/complex_indexes_require_name_spec.rb b/spec/rubocop/cop/migration/complex_indexes_require_name_spec.rb
index ac814c10550..7329d399330 100644
--- a/spec/rubocop/cop/migration/complex_indexes_require_name_spec.rb
+++ b/spec/rubocop/cop/migration/complex_indexes_require_name_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
#
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/complex_indexes_require_name'
RSpec.describe RuboCop::Cop::Migration::ComplexIndexesRequireName do
- subject(:cop) { described_class.new }
-
context 'when in migration' do
let(:msg) { 'indexes added with custom options must be explicitly named' }
diff --git a/spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb b/spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb
index 6a8df2b507d..072edb5827b 100644
--- a/spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb
+++ b/spec/rubocop/cop/migration/create_table_with_foreign_keys_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/create_table_with_foreign_keys'
RSpec.describe RuboCop::Cop::Migration::CreateTableWithForeignKeys do
@@ -192,7 +192,7 @@ RSpec.describe RuboCop::Cop::Migration::CreateTableWithForeignKeys do
include_context 'when there is a target to a high traffic table', :foreign_key do
let(:explicit_target_opts) { ", to_table: :#{table_name}" }
- let(:implicit_target_opts) { }
+ let(:implicit_target_opts) {}
end
end
end
diff --git a/spec/rubocop/cop/migration/datetime_spec.rb b/spec/rubocop/cop/migration/datetime_spec.rb
index 95a875b3baa..400abe3be70 100644
--- a/spec/rubocop/cop/migration/datetime_spec.rb
+++ b/spec/rubocop/cop/migration/datetime_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/datetime'
RSpec.describe RuboCop::Cop::Migration::Datetime do
- subject(:cop) { described_class.new }
-
let(:create_table_migration_without_datetime) do
%q(
class Users < ActiveRecord::Migration[6.0]
diff --git a/spec/rubocop/cop/migration/drop_table_spec.rb b/spec/rubocop/cop/migration/drop_table_spec.rb
index f1bd710f5e6..dd5f93eaafc 100644
--- a/spec/rubocop/cop/migration/drop_table_spec.rb
+++ b/spec/rubocop/cop/migration/drop_table_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/drop_table'
RSpec.describe RuboCop::Cop::Migration::DropTable do
- subject(:cop) { described_class.new }
-
context 'when in deployment migration' do
let(:msg) do
'`drop_table` in deployment migrations requires downtime. Drop tables in post-deployment migrations instead.'
diff --git a/spec/rubocop/cop/migration/migration_record_spec.rb b/spec/rubocop/cop/migration/migration_record_spec.rb
index bfe6228c421..96a1d8fa107 100644
--- a/spec/rubocop/cop/migration/migration_record_spec.rb
+++ b/spec/rubocop/cop/migration/migration_record_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/migration_record'
RSpec.describe RuboCop::Cop::Migration::MigrationRecord do
- subject(:cop) { described_class.new }
-
shared_examples 'a disabled cop' do |klass|
it 'does not register any offenses' do
expect_no_offenses(<<~SOURCE)
diff --git a/spec/rubocop/cop/migration/prevent_global_enable_lock_retries_with_disable_ddl_transaction_spec.rb b/spec/rubocop/cop/migration/prevent_global_enable_lock_retries_with_disable_ddl_transaction_spec.rb
index aa63259288d..1035ed2fb4a 100644
--- a/spec/rubocop/cop/migration/prevent_global_enable_lock_retries_with_disable_ddl_transaction_spec.rb
+++ b/spec/rubocop/cop/migration/prevent_global_enable_lock_retries_with_disable_ddl_transaction_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/prevent_global_enable_lock_retries_with_disable_ddl_transaction'
RSpec.describe RuboCop::Cop::Migration::PreventGlobalEnableLockRetriesWithDisableDdlTransaction do
- subject(:cop) { described_class.new }
-
context 'when in migration' do
before do
allow(cop).to receive(:in_migration?).and_return(true)
diff --git a/spec/rubocop/cop/migration/prevent_index_creation_spec.rb b/spec/rubocop/cop/migration/prevent_index_creation_spec.rb
index ed7c8974d8d..9d886467a48 100644
--- a/spec/rubocop/cop/migration/prevent_index_creation_spec.rb
+++ b/spec/rubocop/cop/migration/prevent_index_creation_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/prevent_index_creation'
RSpec.describe RuboCop::Cop::Migration::PreventIndexCreation do
- subject(:cop) { described_class.new }
-
let(:forbidden_tables) { %w(ci_builds) }
let(:forbidden_tables_list) { forbidden_tables.join(', ') }
diff --git a/spec/rubocop/cop/migration/prevent_strings_spec.rb b/spec/rubocop/cop/migration/prevent_strings_spec.rb
index d1760c2db88..f1adeae6786 100644
--- a/spec/rubocop/cop/migration/prevent_strings_spec.rb
+++ b/spec/rubocop/cop/migration/prevent_strings_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/prevent_strings'
RSpec.describe RuboCop::Cop::Migration::PreventStrings do
- subject(:cop) { described_class.new }
-
context 'when in migration' do
before do
allow(cop).to receive(:in_migration?).and_return(true)
diff --git a/spec/rubocop/cop/migration/refer_to_index_by_name_spec.rb b/spec/rubocop/cop/migration/refer_to_index_by_name_spec.rb
index c65f86d1e13..acdc6843584 100644
--- a/spec/rubocop/cop/migration/refer_to_index_by_name_spec.rb
+++ b/spec/rubocop/cop/migration/refer_to_index_by_name_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
#
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/refer_to_index_by_name'
RSpec.describe RuboCop::Cop::Migration::ReferToIndexByName do
- subject(:cop) { described_class.new }
-
context 'when in migration' do
before do
allow(cop).to receive(:in_migration?).and_return(true)
diff --git a/spec/rubocop/cop/migration/remove_column_spec.rb b/spec/rubocop/cop/migration/remove_column_spec.rb
index f72a5b048d5..4aa842969fe 100644
--- a/spec/rubocop/cop/migration/remove_column_spec.rb
+++ b/spec/rubocop/cop/migration/remove_column_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/remove_column'
RSpec.describe RuboCop::Cop::Migration::RemoveColumn do
- subject(:cop) { described_class.new }
-
def source(meth = 'change')
"def #{meth}; remove_column :table, :column; end"
end
diff --git a/spec/rubocop/cop/migration/remove_concurrent_index_spec.rb b/spec/rubocop/cop/migration/remove_concurrent_index_spec.rb
index 10ca0353b0f..1d59390d659 100644
--- a/spec/rubocop/cop/migration/remove_concurrent_index_spec.rb
+++ b/spec/rubocop/cop/migration/remove_concurrent_index_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/remove_concurrent_index'
RSpec.describe RuboCop::Cop::Migration::RemoveConcurrentIndex do
- subject(:cop) { described_class.new }
-
context 'in migration' do
before do
allow(cop).to receive(:in_migration?).and_return(true)
diff --git a/spec/rubocop/cop/migration/remove_index_spec.rb b/spec/rubocop/cop/migration/remove_index_spec.rb
index 5d1ffef2589..24823b47d53 100644
--- a/spec/rubocop/cop/migration/remove_index_spec.rb
+++ b/spec/rubocop/cop/migration/remove_index_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/remove_index'
RSpec.describe RuboCop::Cop::Migration::RemoveIndex do
- subject(:cop) { described_class.new }
-
context 'when in migration' do
before do
allow(cop).to receive(:in_migration?).and_return(true)
diff --git a/spec/rubocop/cop/migration/safer_boolean_column_spec.rb b/spec/rubocop/cop/migration/safer_boolean_column_spec.rb
index cf9bdbeef91..2050051cac7 100644
--- a/spec/rubocop/cop/migration/safer_boolean_column_spec.rb
+++ b/spec/rubocop/cop/migration/safer_boolean_column_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/safer_boolean_column'
RSpec.describe RuboCop::Cop::Migration::SaferBooleanColumn do
- subject(:cop) { described_class.new }
-
context 'in migration' do
before do
allow(cop).to receive(:in_migration?).and_return(true)
diff --git a/spec/rubocop/cop/migration/schedule_async_spec.rb b/spec/rubocop/cop/migration/schedule_async_spec.rb
index 09d2c77369c..59e03db07c0 100644
--- a/spec/rubocop/cop/migration/schedule_async_spec.rb
+++ b/spec/rubocop/cop/migration/schedule_async_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/schedule_async'
diff --git a/spec/rubocop/cop/migration/sidekiq_queue_migrate_spec.rb b/spec/rubocop/cop/migration/sidekiq_queue_migrate_spec.rb
index 499351b3585..46c460b5d49 100644
--- a/spec/rubocop/cop/migration/sidekiq_queue_migrate_spec.rb
+++ b/spec/rubocop/cop/migration/sidekiq_queue_migrate_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/sidekiq_queue_migrate'
RSpec.describe RuboCop::Cop::Migration::SidekiqQueueMigrate do
- subject(:cop) { described_class.new }
-
def source(meth = 'change')
"def #{meth}; sidekiq_queue_migrate 'queue', to: 'new_queue'; end"
end
diff --git a/spec/rubocop/cop/migration/timestamps_spec.rb b/spec/rubocop/cop/migration/timestamps_spec.rb
index 2f99a3ff35b..706fd8a3d0f 100644
--- a/spec/rubocop/cop/migration/timestamps_spec.rb
+++ b/spec/rubocop/cop/migration/timestamps_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/timestamps'
RSpec.describe RuboCop::Cop::Migration::Timestamps do
- subject(:cop) { described_class.new }
-
let(:migration_with_timestamps) do
%q(
class Users < ActiveRecord::Migration[4.2]
diff --git a/spec/rubocop/cop/migration/update_column_in_batches_spec.rb b/spec/rubocop/cop/migration/update_column_in_batches_spec.rb
index a12ae94c22b..005d3fb6b2a 100644
--- a/spec/rubocop/cop/migration/update_column_in_batches_spec.rb
+++ b/spec/rubocop/cop/migration/update_column_in_batches_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/update_column_in_batches'
diff --git a/spec/rubocop/cop/migration/versioned_migration_class_spec.rb b/spec/rubocop/cop/migration/versioned_migration_class_spec.rb
index d9b0cd4546c..b44f5d64a62 100644
--- a/spec/rubocop/cop/migration/versioned_migration_class_spec.rb
+++ b/spec/rubocop/cop/migration/versioned_migration_class_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/versioned_migration_class'
RSpec.describe RuboCop::Cop::Migration::VersionedMigrationClass do
- subject(:cop) { described_class.new }
-
let(:migration) do
<<~SOURCE
class TestMigration < Gitlab::Database::Migration[1.0]
diff --git a/spec/rubocop/cop/migration/with_lock_retries_disallowed_method_spec.rb b/spec/rubocop/cop/migration/with_lock_retries_disallowed_method_spec.rb
index 298ca273256..5762f78820c 100644
--- a/spec/rubocop/cop/migration/with_lock_retries_disallowed_method_spec.rb
+++ b/spec/rubocop/cop/migration/with_lock_retries_disallowed_method_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/with_lock_retries_disallowed_method'
RSpec.describe RuboCop::Cop::Migration::WithLockRetriesDisallowedMethod do
- subject(:cop) { described_class.new }
-
context 'when in migration' do
before do
allow(cop).to receive(:in_migration?).and_return(true)
diff --git a/spec/rubocop/cop/migration/with_lock_retries_with_change_spec.rb b/spec/rubocop/cop/migration/with_lock_retries_with_change_spec.rb
index f2e84a8697c..fed9176ea97 100644
--- a/spec/rubocop/cop/migration/with_lock_retries_with_change_spec.rb
+++ b/spec/rubocop/cop/migration/with_lock_retries_with_change_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/migration/with_lock_retries_with_change'
RSpec.describe RuboCop::Cop::Migration::WithLockRetriesWithChange do
- subject(:cop) { described_class.new }
-
context 'when in migration' do
before do
allow(cop).to receive(:in_migration?).and_return(true)
diff --git a/spec/rubocop/cop/performance/active_record_subtransaction_methods_spec.rb b/spec/rubocop/cop/performance/active_record_subtransaction_methods_spec.rb
index df18121e2df..ac58ca1edf3 100644
--- a/spec/rubocop/cop/performance/active_record_subtransaction_methods_spec.rb
+++ b/spec/rubocop/cop/performance/active_record_subtransaction_methods_spec.rb
@@ -1,13 +1,11 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require 'rspec-parameterized'
require_relative '../../../../rubocop/cop/performance/active_record_subtransaction_methods'
RSpec.describe RuboCop::Cop::Performance::ActiveRecordSubtransactionMethods do
- subject(:cop) { described_class.new }
-
let(:message) { described_class::MSG }
shared_examples 'a method that uses a subtransaction' do |method_name|
diff --git a/spec/rubocop/cop/performance/active_record_subtransactions_spec.rb b/spec/rubocop/cop/performance/active_record_subtransactions_spec.rb
index 0da2e30062a..e839a3e9367 100644
--- a/spec/rubocop/cop/performance/active_record_subtransactions_spec.rb
+++ b/spec/rubocop/cop/performance/active_record_subtransactions_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/performance/active_record_subtransactions'
RSpec.describe RuboCop::Cop::Performance::ActiveRecordSubtransactions do
- subject(:cop) { described_class.new }
-
let(:message) { described_class::MSG }
context 'when calling #transaction with only requires_new: true' do
diff --git a/spec/rubocop/cop/performance/ar_count_each_spec.rb b/spec/rubocop/cop/performance/ar_count_each_spec.rb
index 4aeb9e13b18..a86b3f2b983 100644
--- a/spec/rubocop/cop/performance/ar_count_each_spec.rb
+++ b/spec/rubocop/cop/performance/ar_count_each_spec.rb
@@ -1,14 +1,12 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/performance/ar_count_each'
RSpec.describe RuboCop::Cop::Performance::ARCountEach do
- subject(:cop) { described_class.new }
-
context 'when it is not haml file' do
it 'does not flag it as an offense' do
- expect(subject).to receive(:in_haml_file?).with(anything).at_least(:once).and_return(false)
+ expect(cop).to receive(:in_haml_file?).with(anything).at_least(:once).and_return(false)
expect_no_offenses <<~SOURCE
show(@users.count)
@@ -19,7 +17,7 @@ RSpec.describe RuboCop::Cop::Performance::ARCountEach do
context 'when it is haml file' do
before do
- expect(subject).to receive(:in_haml_file?).with(anything).at_least(:once).and_return(true)
+ expect(cop).to receive(:in_haml_file?).with(anything).at_least(:once).and_return(true)
end
context 'when the same object uses count and each' do
diff --git a/spec/rubocop/cop/performance/ar_exists_and_present_blank_spec.rb b/spec/rubocop/cop/performance/ar_exists_and_present_blank_spec.rb
index e95220756ed..070e792eeec 100644
--- a/spec/rubocop/cop/performance/ar_exists_and_present_blank_spec.rb
+++ b/spec/rubocop/cop/performance/ar_exists_and_present_blank_spec.rb
@@ -1,14 +1,12 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/performance/ar_exists_and_present_blank'
RSpec.describe RuboCop::Cop::Performance::ARExistsAndPresentBlank do
- subject(:cop) { described_class.new }
-
context 'when it is not haml file' do
it 'does not flag it as an offense' do
- expect(subject).to receive(:in_haml_file?).with(anything).at_least(:once).and_return(false)
+ expect(cop).to receive(:in_haml_file?).with(anything).at_least(:once).and_return(false)
expect_no_offenses <<~SOURCE
return unless @users.exists?
@@ -19,7 +17,7 @@ RSpec.describe RuboCop::Cop::Performance::ARExistsAndPresentBlank do
context 'when it is haml file' do
before do
- expect(subject).to receive(:in_haml_file?).with(anything).at_least(:once).and_return(true)
+ expect(cop).to receive(:in_haml_file?).with(anything).at_least(:once).and_return(true)
end
context 'the same object uses exists? and present?' do
diff --git a/spec/rubocop/cop/performance/readlines_each_spec.rb b/spec/rubocop/cop/performance/readlines_each_spec.rb
index 0a8b168ce5d..d876cbf79a5 100644
--- a/spec/rubocop/cop/performance/readlines_each_spec.rb
+++ b/spec/rubocop/cop/performance/readlines_each_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/performance/readlines_each'
RSpec.describe RuboCop::Cop::Performance::ReadlinesEach do
- subject(:cop) { described_class.new }
-
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|
diff --git a/spec/rubocop/cop/prefer_class_methods_over_module_spec.rb b/spec/rubocop/cop/prefer_class_methods_over_module_spec.rb
index 1261ca7891c..a2a4270c48e 100644
--- a/spec/rubocop/cop/prefer_class_methods_over_module_spec.rb
+++ b/spec/rubocop/cop/prefer_class_methods_over_module_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/prefer_class_methods_over_module'
RSpec.describe RuboCop::Cop::PreferClassMethodsOverModule do
- subject(:cop) { described_class.new }
-
it 'flags violation when using module ClassMethods and corrects', :aggregate_failures do
expect_offense(<<~RUBY)
module Foo
diff --git a/spec/rubocop/cop/project_path_helper_spec.rb b/spec/rubocop/cop/project_path_helper_spec.rb
index b3c920f9d25..3153c928c77 100644
--- a/spec/rubocop/cop/project_path_helper_spec.rb
+++ b/spec/rubocop/cop/project_path_helper_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/project_path_helper'
RSpec.describe RuboCop::Cop::ProjectPathHelper do
- subject(:cop) { described_class.new }
-
context "when using namespace_project with the project's namespace" do
let(:source) { 'edit_namespace_project_issue_path(@issue.project.namespace, @issue.project, @issue)' }
let(:correct_source) { 'edit_project_issue_path(@issue.project, @issue)' }
diff --git a/spec/rubocop/cop/put_group_routes_under_scope_spec.rb b/spec/rubocop/cop/put_group_routes_under_scope_spec.rb
index 366fc4b5657..8697345cddc 100644
--- a/spec/rubocop/cop/put_group_routes_under_scope_spec.rb
+++ b/spec/rubocop/cop/put_group_routes_under_scope_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/put_group_routes_under_scope'
RSpec.describe RuboCop::Cop::PutGroupRoutesUnderScope do
- subject(:cop) { described_class.new }
-
%w[resource resources get post put patch delete].each do |route_method|
it "registers an offense when route is outside scope for `#{route_method}`" do
offense = "#{route_method} :notes"
diff --git a/spec/rubocop/cop/put_project_routes_under_scope_spec.rb b/spec/rubocop/cop/put_project_routes_under_scope_spec.rb
index 9d226db09ef..65d330b0f05 100644
--- a/spec/rubocop/cop/put_project_routes_under_scope_spec.rb
+++ b/spec/rubocop/cop/put_project_routes_under_scope_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/put_project_routes_under_scope'
RSpec.describe RuboCop::Cop::PutProjectRoutesUnderScope do
- subject(:cop) { described_class.new }
-
%w[resource resources get post put patch delete].each do |route_method|
it "registers an offense when route is outside scope for `#{route_method}`" do
offense = "#{route_method} :notes"
diff --git a/spec/rubocop/cop/qa/ambiguous_page_object_name_spec.rb b/spec/rubocop/cop/qa/ambiguous_page_object_name_spec.rb
index 9335b8d01ee..ab270090c7d 100644
--- a/spec/rubocop/cop/qa/ambiguous_page_object_name_spec.rb
+++ b/spec/rubocop/cop/qa/ambiguous_page_object_name_spec.rb
@@ -1,14 +1,12 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/qa/ambiguous_page_object_name'
RSpec.describe RuboCop::Cop::QA::AmbiguousPageObjectName do
let(:source_file) { 'qa/page.rb' }
- subject(:cop) { described_class.new }
-
context 'in a QA file' do
before do
allow(cop).to receive(:in_qa_file?).and_return(true)
diff --git a/spec/rubocop/cop/qa/element_with_pattern_spec.rb b/spec/rubocop/cop/qa/element_with_pattern_spec.rb
index d3e79525c62..1febdaf9c3b 100644
--- a/spec/rubocop/cop/qa/element_with_pattern_spec.rb
+++ b/spec/rubocop/cop/qa/element_with_pattern_spec.rb
@@ -1,14 +1,12 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/qa/element_with_pattern'
RSpec.describe RuboCop::Cop::QA::ElementWithPattern do
let(:source_file) { 'qa/page.rb' }
- subject(:cop) { described_class.new }
-
context 'in a QA file' do
before do
allow(cop).to receive(:in_qa_file?).and_return(true)
diff --git a/spec/rubocop/cop/qa/selector_usage_spec.rb b/spec/rubocop/cop/qa/selector_usage_spec.rb
index b40c57f8991..0ec289c1da6 100644
--- a/spec/rubocop/cop/qa/selector_usage_spec.rb
+++ b/spec/rubocop/cop/qa/selector_usage_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/qa/selector_usage'
RSpec.describe RuboCop::Cop::QA::SelectorUsage do
- subject(:cop) { described_class.new }
-
shared_examples 'non-qa file usage' do
it 'reports an offense' do
expect_offense(<<-RUBY)
diff --git a/spec/rubocop/cop/rspec/any_instance_of_spec.rb b/spec/rubocop/cop/rspec/any_instance_of_spec.rb
index e7675ded25e..f9675e17842 100644
--- a/spec/rubocop/cop/rspec/any_instance_of_spec.rb
+++ b/spec/rubocop/cop/rspec/any_instance_of_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/rspec/any_instance_of'
RSpec.describe RuboCop::Cop::RSpec::AnyInstanceOf do
- subject(:cop) { described_class.new }
-
context 'when calling allow_any_instance_of' do
let(:source) do
<<~SRC
diff --git a/spec/rubocop/cop/rspec/be_success_matcher_spec.rb b/spec/rubocop/cop/rspec/be_success_matcher_spec.rb
index 678e62048b8..c26fa32db8e 100644
--- a/spec/rubocop/cop/rspec/be_success_matcher_spec.rb
+++ b/spec/rubocop/cop/rspec/be_success_matcher_spec.rb
@@ -1,13 +1,11 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/rspec/be_success_matcher'
RSpec.describe RuboCop::Cop::RSpec::BeSuccessMatcher do
let(:source_file) { 'spec/foo_spec.rb' }
- subject(:cop) { described_class.new }
-
shared_examples 'cop' do |good:, bad:|
context "using #{bad} call" do
it 'registers an offense and corrects', :aggregate_failures do
diff --git a/spec/rubocop/cop/rspec/env_assignment_spec.rb b/spec/rubocop/cop/rspec/env_assignment_spec.rb
index 0fd09eeae11..6212cda0b88 100644
--- a/spec/rubocop/cop/rspec/env_assignment_spec.rb
+++ b/spec/rubocop/cop/rspec/env_assignment_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/rspec/env_assignment'
@@ -10,8 +10,6 @@ RSpec.describe RuboCop::Cop::RSpec::EnvAssignment do
let(:source_file) { 'spec/foo_spec.rb' }
- subject(:cop) { described_class.new }
-
shared_examples 'an offensive and correction ENV#[]= call' do |content, autocorrected_content|
it "registers an offense for `#{content}` and corrects", :aggregate_failures do
expect_offense(<<~CODE)
diff --git a/spec/rubocop/cop/rspec/expect_gitlab_tracking_spec.rb b/spec/rubocop/cop/rspec/expect_gitlab_tracking_spec.rb
index e36feecdd66..a07cf472ef0 100644
--- a/spec/rubocop/cop/rspec/expect_gitlab_tracking_spec.rb
+++ b/spec/rubocop/cop/rspec/expect_gitlab_tracking_spec.rb
@@ -1,13 +1,11 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/rspec/expect_gitlab_tracking'
RSpec.describe RuboCop::Cop::RSpec::ExpectGitlabTracking do
let(:source_file) { 'spec/foo_spec.rb' }
- subject(:cop) { described_class.new }
-
good_samples = [
'expect_snowplow_event(category: nil, action: nil)',
'expect_snowplow_event(category: "EventCategory", action: "event_action")',
diff --git a/spec/rubocop/cop/rspec/factories_in_migration_specs_spec.rb b/spec/rubocop/cop/rspec/factories_in_migration_specs_spec.rb
index 74c1521fa0e..e41dd338387 100644
--- a/spec/rubocop/cop/rspec/factories_in_migration_specs_spec.rb
+++ b/spec/rubocop/cop/rspec/factories_in_migration_specs_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/rspec/factories_in_migration_specs'
RSpec.describe RuboCop::Cop::RSpec::FactoriesInMigrationSpecs do
- subject(:cop) { described_class.new }
-
shared_examples 'an offensive factory call' do |namespace|
%i[build build_list create create_list attributes_for].each do |forbidden_method|
namespaced_forbidden_method = "#{namespace}#{forbidden_method}(:user)"
diff --git a/spec/rubocop/cop/rspec/factory_bot/inline_association_spec.rb b/spec/rubocop/cop/rspec/factory_bot/inline_association_spec.rb
index 194e2436ff2..008af734a99 100644
--- a/spec/rubocop/cop/rspec/factory_bot/inline_association_spec.rb
+++ b/spec/rubocop/cop/rspec/factory_bot/inline_association_spec.rb
@@ -1,13 +1,11 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require 'rspec-parameterized'
require_relative '../../../../../rubocop/cop/rspec/factory_bot/inline_association'
RSpec.describe RuboCop::Cop::RSpec::FactoryBot::InlineAssociation do
- subject(:cop) { described_class.new }
-
shared_examples 'offense' do |code_snippet, autocorrected|
# We allow `create` or `FactoryBot.create` or `::FactoryBot.create`
let(:type) { code_snippet[/^(?:::)?(?:FactoryBot\.)?(\w+)/, 1] }
diff --git a/spec/rubocop/cop/rspec/have_gitlab_http_status_spec.rb b/spec/rubocop/cop/rspec/have_gitlab_http_status_spec.rb
index 9bdbe145f4c..e8a60b9cad7 100644
--- a/spec/rubocop/cop/rspec/have_gitlab_http_status_spec.rb
+++ b/spec/rubocop/cop/rspec/have_gitlab_http_status_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require 'rspec-parameterized'
require_relative '../../../../rubocop/cop/rspec/have_gitlab_http_status'
@@ -10,8 +10,6 @@ RSpec.describe RuboCop::Cop::RSpec::HaveGitlabHttpStatus do
let(:source_file) { 'spec/foo_spec.rb' }
- subject(:cop) { described_class.new }
-
shared_examples 'offense' do |bad, good|
it 'registers an offense', :aggregate_failures do
expect_offense(<<~CODE, node: bad)
diff --git a/spec/rubocop/cop/rspec/htt_party_basic_auth_spec.rb b/spec/rubocop/cop/rspec/htt_party_basic_auth_spec.rb
index eac6ceb3ddf..537a7a9a7e9 100644
--- a/spec/rubocop/cop/rspec/htt_party_basic_auth_spec.rb
+++ b/spec/rubocop/cop/rspec/htt_party_basic_auth_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/rspec/httparty_basic_auth'
RSpec.describe RuboCop::Cop::RSpec::HTTPartyBasicAuth do
- subject(:cop) { described_class.new }
-
context 'when passing `basic_auth: { user: ... }`' do
it 'registers an offense and corrects', :aggregate_failures do
expect_offense(<<~SOURCE, 'spec/foo.rb')
diff --git a/spec/rubocop/cop/rspec/modify_sidekiq_middleware_spec.rb b/spec/rubocop/cop/rspec/modify_sidekiq_middleware_spec.rb
index 7a2b7c92bd1..3227b075758 100644
--- a/spec/rubocop/cop/rspec/modify_sidekiq_middleware_spec.rb
+++ b/spec/rubocop/cop/rspec/modify_sidekiq_middleware_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/rspec/modify_sidekiq_middleware'
RSpec.describe RuboCop::Cop::RSpec::ModifySidekiqMiddleware do
- subject(:cop) { described_class.new }
-
it 'registers an offense and corrects', :aggregate_failures do
expect_offense(<<~CODE)
Sidekiq::Testing.server_middleware do |chain|
diff --git a/spec/rubocop/cop/rspec/timecop_freeze_spec.rb b/spec/rubocop/cop/rspec/timecop_freeze_spec.rb
index b8d16d58d9e..4361f587da3 100644
--- a/spec/rubocop/cop/rspec/timecop_freeze_spec.rb
+++ b/spec/rubocop/cop/rspec/timecop_freeze_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/rspec/timecop_freeze'
RSpec.describe RuboCop::Cop::RSpec::TimecopFreeze do
- subject(:cop) { described_class.new }
-
context 'when calling Timecop.freeze' do
it 'registers an offense and corrects', :aggregate_failures do
expect_offense(<<~CODE)
diff --git a/spec/rubocop/cop/rspec/timecop_travel_spec.rb b/spec/rubocop/cop/rspec/timecop_travel_spec.rb
index 16e09fb8c45..89c46ff6c59 100644
--- a/spec/rubocop/cop/rspec/timecop_travel_spec.rb
+++ b/spec/rubocop/cop/rspec/timecop_travel_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/rspec/timecop_travel'
RSpec.describe RuboCop::Cop::RSpec::TimecopTravel do
- subject(:cop) { described_class.new }
-
context 'when calling Timecop.travel' do
it 'registers an offense and corrects', :aggregate_failures do
expect_offense(<<~CODE)
diff --git a/spec/rubocop/cop/rspec/top_level_describe_path_spec.rb b/spec/rubocop/cop/rspec/top_level_describe_path_spec.rb
index 78e6bec51d4..90101e09023 100644
--- a/spec/rubocop/cop/rspec/top_level_describe_path_spec.rb
+++ b/spec/rubocop/cop/rspec/top_level_describe_path_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/rspec/top_level_describe_path'
RSpec.describe RuboCop::Cop::RSpec::TopLevelDescribePath do
- subject(:cop) { described_class.new }
-
context 'when the file ends in _spec.rb' do
it 'registers no offenses' do
expect_no_offenses(<<~SOURCE, 'spec/foo_spec.rb')
diff --git a/spec/rubocop/cop/rspec/web_mock_enable_spec.rb b/spec/rubocop/cop/rspec/web_mock_enable_spec.rb
index 61a85064a61..63ffc06f1ca 100644
--- a/spec/rubocop/cop/rspec/web_mock_enable_spec.rb
+++ b/spec/rubocop/cop/rspec/web_mock_enable_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/rspec/web_mock_enable'
RSpec.describe RuboCop::Cop::RSpec::WebMockEnable do
- subject(:cop) { described_class.new }
-
context 'when calling WebMock.disable_net_connect!' do
it 'registers an offence and autocorrects it' do
expect_offense(<<~RUBY)
diff --git a/spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb b/spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb
index c21999be917..b687e91601c 100644
--- a/spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb
+++ b/spec/rubocop/cop/ruby_interpolation_in_translation_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/ruby_interpolation_in_translation'
@@ -9,8 +9,6 @@ require_relative '../../../rubocop/cop/ruby_interpolation_in_translation'
RSpec.describe RuboCop::Cop::RubyInterpolationInTranslation do
let(:msg) { "Don't use ruby interpolation \#{} inside translated strings, instead use %{}" }
- subject(:cop) { described_class.new }
-
it 'does not add an offense for a regular messages' do
expect_no_offenses('_("Hello world")')
end
diff --git a/spec/rubocop/cop/safe_params_spec.rb b/spec/rubocop/cop/safe_params_spec.rb
index 9a064b93b16..e6d86019d18 100644
--- a/spec/rubocop/cop/safe_params_spec.rb
+++ b/spec/rubocop/cop/safe_params_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/safe_params'
RSpec.describe RuboCop::Cop::SafeParams do
- subject(:cop) { described_class.new }
-
it 'flags the params as an argument of url_for' do
expect_offense(<<~SOURCE)
url_for(params)
diff --git a/spec/rubocop/cop/scalability/bulk_perform_with_context_spec.rb b/spec/rubocop/cop/scalability/bulk_perform_with_context_spec.rb
index 74912b53d37..bd248cd028a 100644
--- a/spec/rubocop/cop/scalability/bulk_perform_with_context_spec.rb
+++ b/spec/rubocop/cop/scalability/bulk_perform_with_context_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/scalability/bulk_perform_with_context'
RSpec.describe RuboCop::Cop::Scalability::BulkPerformWithContext do
- subject(:cop) { described_class.new }
-
it "adds an offense when calling bulk_perform_async" do
expect_offense(<<~CODE)
Worker.bulk_perform_async(args)
diff --git a/spec/rubocop/cop/scalability/cron_worker_context_spec.rb b/spec/rubocop/cop/scalability/cron_worker_context_spec.rb
index 28db12fd075..bcf93b04d6a 100644
--- a/spec/rubocop/cop/scalability/cron_worker_context_spec.rb
+++ b/spec/rubocop/cop/scalability/cron_worker_context_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/scalability/cron_worker_context'
RSpec.describe RuboCop::Cop::Scalability::CronWorkerContext do
- subject(:cop) { described_class.new }
-
it 'adds an offense when including CronjobQueue' do
expect_offense(<<~CODE)
class SomeWorker
diff --git a/spec/rubocop/cop/scalability/file_uploads_spec.rb b/spec/rubocop/cop/scalability/file_uploads_spec.rb
index ca25b0246f0..1395615479f 100644
--- a/spec/rubocop/cop/scalability/file_uploads_spec.rb
+++ b/spec/rubocop/cop/scalability/file_uploads_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/scalability/file_uploads'
RSpec.describe RuboCop::Cop::Scalability::FileUploads do
- subject(:cop) { described_class.new }
-
let(:message) { 'Do not upload files without workhorse acceleration. Please refer to https://docs.gitlab.com/ee/development/uploads.html' }
context 'with required params' do
diff --git a/spec/rubocop/cop/scalability/idempotent_worker_spec.rb b/spec/rubocop/cop/scalability/idempotent_worker_spec.rb
index 53c0c06f6c9..b1984721803 100644
--- a/spec/rubocop/cop/scalability/idempotent_worker_spec.rb
+++ b/spec/rubocop/cop/scalability/idempotent_worker_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/scalability/idempotent_worker'
RSpec.describe RuboCop::Cop::Scalability::IdempotentWorker do
- subject(:cop) { described_class.new }
-
before do
allow(cop)
.to receive(:in_worker?)
diff --git a/spec/rubocop/cop/sidekiq_load_balancing/worker_data_consistency_spec.rb b/spec/rubocop/cop/sidekiq_load_balancing/worker_data_consistency_spec.rb
index cf8d0d1b66f..7b6578a0744 100644
--- a/spec/rubocop/cop/sidekiq_load_balancing/worker_data_consistency_spec.rb
+++ b/spec/rubocop/cop/sidekiq_load_balancing/worker_data_consistency_spec.rb
@@ -1,11 +1,9 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/sidekiq_load_balancing/worker_data_consistency'
RSpec.describe RuboCop::Cop::SidekiqLoadBalancing::WorkerDataConsistency do
- subject(:cop) { described_class.new }
-
before do
allow(cop)
.to receive(:in_worker?)
diff --git a/spec/rubocop/cop/sidekiq_options_queue_spec.rb b/spec/rubocop/cop/sidekiq_options_queue_spec.rb
index 346a8d82475..da126090a81 100644
--- a/spec/rubocop/cop/sidekiq_options_queue_spec.rb
+++ b/spec/rubocop/cop/sidekiq_options_queue_spec.rb
@@ -1,12 +1,10 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../rubocop/cop/sidekiq_options_queue'
RSpec.describe RuboCop::Cop::SidekiqOptionsQueue do
- subject(:cop) { described_class.new }
-
it 'registers an offense when `sidekiq_options` is used with the `queue` option' do
expect_offense(<<~CODE)
sidekiq_options queue: "some_queue"
diff --git a/spec/rubocop/cop/static_translation_definition_spec.rb b/spec/rubocop/cop/static_translation_definition_spec.rb
index 372fc194c56..10b4f162504 100644
--- a/spec/rubocop/cop/static_translation_definition_spec.rb
+++ b/spec/rubocop/cop/static_translation_definition_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require 'rspec-parameterized'
@@ -11,8 +11,6 @@ RSpec.describe RuboCop::Cop::StaticTranslationDefinition do
let(:msg) { described_class::MSG }
- subject(:cop) { described_class.new }
-
shared_examples 'offense' do |code|
it 'registers an offense' do
expect_offense(code)
diff --git a/spec/rubocop/cop/style/regexp_literal_mixed_preserve_spec.rb b/spec/rubocop/cop/style/regexp_literal_mixed_preserve_spec.rb
index 384a834a512..1d1c0852db2 100644
--- a/spec/rubocop/cop/style/regexp_literal_mixed_preserve_spec.rb
+++ b/spec/rubocop/cop/style/regexp_literal_mixed_preserve_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/style/regexp_literal_mixed_preserve'
diff --git a/spec/rubocop/cop/usage_data/distinct_count_by_large_foreign_key_spec.rb b/spec/rubocop/cop/usage_data/distinct_count_by_large_foreign_key_spec.rb
index f377dfe36d8..b4d113a9bcc 100644
--- a/spec/rubocop/cop/usage_data/distinct_count_by_large_foreign_key_spec.rb
+++ b/spec/rubocop/cop/usage_data/distinct_count_by_large_foreign_key_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/usage_data/distinct_count_by_large_foreign_key'
@@ -13,8 +13,6 @@ RSpec.describe RuboCop::Cop::UsageData::DistinctCountByLargeForeignKey do
})
end
- subject(:cop) { described_class.new(config) }
-
context 'when counting by disallowed key' do
it 'registers an offense' do
expect_offense(<<~CODE)
diff --git a/spec/rubocop/cop/usage_data/histogram_with_large_table_spec.rb b/spec/rubocop/cop/usage_data/histogram_with_large_table_spec.rb
index 56aecc3ec4e..efa4e27dc9c 100644
--- a/spec/rubocop/cop/usage_data/histogram_with_large_table_spec.rb
+++ b/spec/rubocop/cop/usage_data/histogram_with_large_table_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/usage_data/histogram_with_large_table'
@@ -14,8 +14,6 @@ RSpec.describe RuboCop::Cop::UsageData::HistogramWithLargeTable do
})
end
- subject(:cop) { described_class.new(config) }
-
context 'with large tables' do
context 'with one-level constants' do
context 'when calling histogram(Issue)' do
diff --git a/spec/rubocop/cop/usage_data/instrumentation_superclass_spec.rb b/spec/rubocop/cop/usage_data/instrumentation_superclass_spec.rb
index 31324331e61..a55f0852f35 100644
--- a/spec/rubocop/cop/usage_data/instrumentation_superclass_spec.rb
+++ b/spec/rubocop/cop/usage_data/instrumentation_superclass_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/usage_data/instrumentation_superclass'
@@ -14,8 +14,6 @@ RSpec.describe RuboCop::Cop::UsageData::InstrumentationSuperclass do
})
end
- subject(:cop) { described_class.new(config) }
-
context 'with class definition' do
context 'when inheriting from allowed superclass' do
it 'does not register an offense' do
diff --git a/spec/rubocop/cop/usage_data/large_table_spec.rb b/spec/rubocop/cop/usage_data/large_table_spec.rb
index a6b22fd7f0d..fa94f878cea 100644
--- a/spec/rubocop/cop/usage_data/large_table_spec.rb
+++ b/spec/rubocop/cop/usage_data/large_table_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/usage_data/large_table'
@@ -18,8 +18,6 @@ RSpec.describe RuboCop::Cop::UsageData::LargeTable do
})
end
- subject(:cop) { described_class.new(config) }
-
context 'when in usage_data files' do
before do
allow(cop).to receive(:usage_data_files?).and_return(true)
diff --git a/spec/rubocop/cop/user_admin_spec.rb b/spec/rubocop/cop/user_admin_spec.rb
index 3bf458348f3..99e87d619c0 100644
--- a/spec/rubocop/cop/user_admin_spec.rb
+++ b/spec/rubocop/cop/user_admin_spec.rb
@@ -1,13 +1,11 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require 'rubocop'
require_relative '../../../rubocop/cop/user_admin'
RSpec.describe RuboCop::Cop::UserAdmin do
- subject(:cop) { described_class.new }
-
it 'flags a method call' do
expect_offense(<<~SOURCE)
user.admin?
diff --git a/spec/rubocop/cop_todo_spec.rb b/spec/rubocop/cop_todo_spec.rb
index 978df2c01ee..3f9c378b303 100644
--- a/spec/rubocop/cop_todo_spec.rb
+++ b/spec/rubocop/cop_todo_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require_relative '../../rubocop/cop_todo'
RSpec.describe RuboCop::CopTodo do
@@ -14,7 +14,8 @@ RSpec.describe RuboCop::CopTodo do
cop_name: cop_name,
files: be_empty,
offense_count: 0,
- previously_disabled: false
+ previously_disabled: false,
+ grace_period: false
)
end
end
@@ -102,6 +103,23 @@ RSpec.describe RuboCop::CopTodo do
end
end
+ context 'with grace period' do
+ specify do
+ cop_todo.record('a.rb', 1)
+ cop_todo.record('b.rb', 2)
+ cop_todo.grace_period = true
+
+ expect(yaml).to eq(<<~YAML)
+ ---
+ #{cop_name}:
+ Details: grace period
+ Exclude:
+ - 'a.rb'
+ - 'b.rb'
+ YAML
+ end
+ end
+
context 'with multiple files' do
before do
cop_todo.record('a.rb', 0)
diff --git a/spec/rubocop/formatter/graceful_formatter_spec.rb b/spec/rubocop/formatter/graceful_formatter_spec.rb
new file mode 100644
index 00000000000..0e0c1d52067
--- /dev/null
+++ b/spec/rubocop/formatter/graceful_formatter_spec.rb
@@ -0,0 +1,239 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'rspec-parameterized'
+require 'rubocop/rspec/shared_contexts'
+require 'stringio'
+
+require_relative '../../../rubocop/formatter/graceful_formatter'
+require_relative '../../../rubocop/todo_dir'
+
+RSpec.describe RuboCop::Formatter::GracefulFormatter, :isolated_environment do
+ # Set by :isolated_environment
+ let(:todo_dir) { RuboCop::TodoDir.new("#{Dir.pwd}/.rubocop_todo") }
+ let(:stdout) { StringIO.new }
+
+ subject(:formatter) { described_class.new(stdout) }
+
+ shared_examples 'summary reporting' do |inspected:, offenses: 0, silenced: 0|
+ it "reports summary with #{inspected} inspected, #{offenses} offenses, #{silenced} silenced" do
+ expect(stdout.string)
+ .to match(/Inspecting #{inspected} files/)
+ .and match(/#{inspected} files inspected/)
+
+ if offenses > 0
+ expect(stdout.string).to match(/Offenses:/)
+ expect(stdout.string).to match(/#{offenses} offenses detected/)
+ else
+ expect(stdout.string).not_to match(/Offenses:/)
+ expect(stdout.string).to match(/no offenses detected/)
+ end
+
+ if silenced > 0
+ expect(stdout.string).to match(/Silenced offenses:/)
+ expect(stdout.string).to match(/#{silenced} offenses silenced/)
+ else
+ expect(stdout.string).not_to match(/Silenced offenses:/)
+ expect(stdout.string).not_to match(/offenses silenced/)
+ end
+ end
+ end
+
+ context 'with offenses' do
+ let(:offense1) { fake_offense('Cop1') }
+ let(:offense2) { fake_offense('Cop2') }
+
+ before do
+ FileUtils.touch('.rubocop_todo.yml')
+
+ File.write('.rubocop.yml', <<~YAML)
+ inherit_from:
+ <% Dir.glob('.rubocop_todo/**/*.yml').each do |rubocop_todo_yaml| %>
+ - '<%= rubocop_todo_yaml %>'
+ <% end %>
+ - '.rubocop_todo.yml'
+
+ AllCops:
+ NewCops: enable # Avoiding RuboCop warnings
+ YAML
+
+ # These cops are unknown and would raise an validation error
+ allow(RuboCop::Cop::Registry.global).to receive(:contains_cop_matching?)
+ .and_return(true)
+ end
+
+ context 'with active only' do
+ before do
+ formatter.started(%w[a.rb b.rb])
+ formatter.file_finished('a.rb', [offense1])
+ formatter.file_finished('b.rb', [offense2])
+ formatter.finished(%w[a.rb b.rb])
+ end
+
+ it_behaves_like 'summary reporting', inspected: 2, offenses: 2
+ end
+
+ context 'with silenced only' do
+ before do
+ todo_dir.write('Cop1', <<~YAML)
+ ---
+ Cop1:
+ Details: grace period
+ YAML
+
+ File.write('.rubocop_todo.yml', <<~YAML)
+ ---
+ Cop2:
+ Details: grace period
+ YAML
+
+ formatter.started(%w[a.rb b.rb])
+ formatter.file_finished('a.rb', [offense1])
+ formatter.file_finished('b.rb', [offense2])
+ formatter.finished(%w[a.rb b.rb])
+ end
+
+ it_behaves_like 'summary reporting', inspected: 2, silenced: 2
+ end
+
+ context 'with active and silenced' do
+ before do
+ todo_dir.write('Cop1', <<~YAML)
+ ---
+ Cop1:
+ Details: grace period
+ YAML
+
+ formatter.started(%w[a.rb b.rb])
+ formatter.file_finished('a.rb', [offense1, offense2])
+ formatter.file_finished('b.rb', [offense2, offense1, offense1])
+ formatter.finished(%w[a.rb b.rb])
+ end
+
+ it_behaves_like 'summary reporting', inspected: 2, offenses: 2, silenced: 3
+ end
+ end
+
+ context 'without offenses' do
+ before do
+ formatter.started(%w[a.rb b.rb])
+ formatter.file_finished('a.rb', [])
+ formatter.file_finished('b.rb', [])
+ formatter.finished(%w[a.rb b.rb])
+ end
+
+ it_behaves_like 'summary reporting', inspected: 2
+ end
+
+ context 'without files to inspect' do
+ before do
+ formatter.started([])
+ formatter.finished([])
+ end
+
+ it_behaves_like 'summary reporting', inspected: 0
+ end
+
+ context 'with missing @total_offense_count' do
+ it 'raises an error' do
+ formatter.started(%w[a.rb])
+
+ if formatter.instance_variable_defined?(:@total_offense_count)
+ formatter.remove_instance_variable(:@total_offense_count)
+ end
+
+ expect do
+ formatter.finished(%w[a.rb])
+ end.to raise_error(/RuboCop has changed its internals/)
+ end
+ end
+
+ describe '.adjusted_exit_status' do
+ using RSpec::Parameterized::TableSyntax
+
+ success = RuboCop::CLI::STATUS_SUCCESS
+ offenses = RuboCop::CLI::STATUS_OFFENSES
+ error = RuboCop::CLI::STATUS_ERROR
+
+ subject { described_class.adjusted_exit_status(status) }
+
+ where(:active_offenses, :status, :adjusted_status) do
+ 0 | success | success
+ 0 | offenses | success
+ 1 | offenses | offenses
+ 0 | error | error
+ 1 | error | error
+ # impossible cases
+ 1 | success | success
+ end
+
+ with_them do
+ around do |example|
+ described_class.active_offenses = active_offenses
+ example.run
+ ensure
+ described_class.active_offenses = 0
+ end
+
+ it { is_expected.to eq(adjusted_status) }
+ end
+ end
+
+ describe '.grace_period?' do
+ let(:cop_name) { 'Cop/Name' }
+
+ subject { described_class.grace_period?(cop_name, config) }
+
+ context 'with Details in config' do
+ let(:config) { { 'Details' => 'grace period' } }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'with unknown value for Details in config' do
+ let(:config) { { 'Details' => 'unknown' } }
+
+ specify do
+ expect { is_expected.to eq(false) }
+ .to output(/#{cop_name}: Unhandled value "unknown" for `Details` key./)
+ .to_stderr
+ end
+ end
+
+ context 'with empty config' do
+ let(:config) { {} }
+
+ it { is_expected.to eq(false) }
+ end
+
+ context 'without Details in config' do
+ let(:config) { { 'Exclude' => false } }
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ describe '.grace_period_key_value' do
+ subject { described_class.grace_period_key_value }
+
+ it { is_expected.to eq('Details: grace period') }
+ end
+
+ def fake_offense(cop_name)
+ # rubocop:disable RSpec/VerifiedDoubles
+ double(:offense,
+ cop_name: cop_name,
+ corrected?: false,
+ correctable?: false,
+ severity: double(:severity, name: 'convention', code: :C),
+ line: 5,
+ column: 23,
+ real_column: 23,
+ corrected_with_todo?: false,
+ message: "#{cop_name} message",
+ location: double(:location, source_line: 'line', first_line: 1, last_line: 2),
+ highlighted_area: double(:highlighted_area, begin_pos: 1, size: 2)
+ )
+ # rubocop:enable RSpec/VerifiedDoubles
+ end
+end
diff --git a/spec/rubocop/formatter/todo_formatter_spec.rb b/spec/rubocop/formatter/todo_formatter_spec.rb
index df56ee45931..edd84632409 100644
--- a/spec/rubocop/formatter/todo_formatter_spec.rb
+++ b/spec/rubocop/formatter/todo_formatter_spec.rb
@@ -2,8 +2,10 @@
# rubocop:disable RSpec/VerifiedDoubles
require 'fast_spec_helper'
-require 'stringio'
+
require 'fileutils'
+require 'stringio'
+require 'tmpdir'
require_relative '../../../rubocop/formatter/todo_formatter'
require_relative '../../../rubocop/todo_dir'
@@ -174,6 +176,98 @@ RSpec.describe RuboCop::Formatter::TodoFormatter do
end
end
+ context 'with grace period' do
+ let(:yaml) do
+ <<~YAML
+ ---
+ B/TooManyOffenses:
+ Details: grace period
+ Exclude:
+ - 'x.rb'
+ YAML
+ end
+
+ shared_examples 'keeps grace period' do
+ it 'keeps Details: grace period' do
+ run_formatter
+
+ expect(todo_yml('B/TooManyOffenses')).to eq(<<~YAML)
+ ---
+ B/TooManyOffenses:
+ Details: grace period
+ Exclude:
+ - 'a.rb'
+ - 'c.rb'
+ YAML
+ end
+ end
+
+ context 'in rubocop_todo/' do
+ before do
+ todo_dir.write('B/TooManyOffenses', yaml)
+ todo_dir.inspect_all
+ end
+
+ it_behaves_like 'keeps grace period'
+ end
+
+ context 'in rubocop_todo.yml' do
+ before do
+ File.write('.rubocop_todo.yml', yaml)
+ end
+
+ it_behaves_like 'keeps grace period'
+ end
+
+ context 'with invalid details value' do
+ let(:yaml) do
+ <<~YAML
+ ---
+ B/TooManyOffenses:
+ Details: something unknown
+ Exclude:
+ - 'x.rb'
+ YAML
+ end
+
+ it 'ignores the details and warns' do
+ File.write('.rubocop_todo.yml', yaml)
+
+ expect { run_formatter }
+ .to output(%r{B/TooManyOffenses: Unhandled value "something unknown" for `Details` key.})
+ .to_stderr
+
+ expect(todo_yml('B/TooManyOffenses')).to eq(<<~YAML)
+ ---
+ B/TooManyOffenses:
+ Exclude:
+ - 'a.rb'
+ - 'c.rb'
+ YAML
+ end
+ end
+
+ context 'and previously disabled' do
+ let(:yaml) do
+ <<~YAML
+ ---
+ B/TooManyOffenses:
+ Enabled: false
+ Details: grace period
+ Exclude:
+ - 'x.rb'
+ YAML
+ end
+
+ it 'raises an exception' do
+ File.write('.rubocop_todo.yml', yaml)
+
+ expect { run_formatter }
+ .to raise_error(RuntimeError, 'B/TooManyOffenses: Cop must be enabled to use `Details: grace period`.')
+ end
+ end
+ end
+
context 'with cop configuration in both .rubocop_todo/ and .rubocop_todo.yml' do
before do
todo_dir.write('B/TooManyOffenses', <<~YAML)
diff --git a/spec/rubocop/qa_helpers_spec.rb b/spec/rubocop/qa_helpers_spec.rb
index 4b5566609e3..a50c8307733 100644
--- a/spec/rubocop/qa_helpers_spec.rb
+++ b/spec/rubocop/qa_helpers_spec.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'fast_spec_helper'
+require 'rubocop_spec_helper'
require 'parser/current'
require_relative '../../rubocop/qa_helpers'
diff --git a/spec/rubocop/todo_dir_spec.rb b/spec/rubocop/todo_dir_spec.rb
index a5c12e23896..e014c4c2c37 100644
--- a/spec/rubocop/todo_dir_spec.rb
+++ b/spec/rubocop/todo_dir_spec.rb
@@ -1,8 +1,10 @@
# frozen_string_literal: true
require 'fast_spec_helper'
-require 'fileutils'
+
require 'active_support/inflector/inflections'
+require 'fileutils'
+require 'tmpdir'
require_relative '../../rubocop/todo_dir'
diff --git a/spec/rubocop_spec_helper.rb b/spec/rubocop_spec_helper.rb
new file mode 100644
index 00000000000..a37415a25de
--- /dev/null
+++ b/spec/rubocop_spec_helper.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+# All RuboCop specs may use fast_spec_helper.
+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'
+
+RSpec.configure do |config|
+ config.include RuboCop::RSpec::ExpectOffense, type: :rubocop
+
+ config.define_derived_metadata(file_path: %r{spec/rubocop}) do |metadata|
+ metadata[:type] = :rubocop
+ end
+
+ # Include config shared context for all cop specs.
+ config.include_context 'config', type: :rubocop
+end
diff --git a/spec/scripts/changed-feature-flags_spec.rb b/spec/scripts/changed-feature-flags_spec.rb
index f4058614d85..f1e381b0656 100644
--- a/spec/scripts/changed-feature-flags_spec.rb
+++ b/spec/scripts/changed-feature-flags_spec.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'fast_spec_helper'
+require 'tmpdir'
load File.expand_path('../../scripts/changed-feature-flags', __dir__)
diff --git a/spec/scripts/determine-qa-tests_spec.rb b/spec/scripts/determine-qa-tests_spec.rb
deleted file mode 100644
index 043eb7f2dc9..00000000000
--- a/spec/scripts/determine-qa-tests_spec.rb
+++ /dev/null
@@ -1,109 +0,0 @@
-# frozen_string_literal: true
-require 'fast_spec_helper'
-
-load File.expand_path('../../scripts/determine-qa-tests', __dir__)
-
-RSpec.describe 'scripts/determine-qa-tests' do
- describe DetermineQATests do
- describe '.execute' do
- let(:qa_spec_files) do
- %w[qa/qa/specs/features/browser_ui/1_manage/test1.rb
- qa/qa/specs/features/browser_ui/1_manage/user/test2.rb]
- end
-
- let(:qa_spec_and_non_spec_files) do
- %w[qa/qa/specs/features/browser_ui/1_manage/test1.rb
- qa/qa/page/admin/menu.rb]
- end
-
- let(:non_qa_files) do
- %w[rubocop/code_reuse_helpers.rb
- app/components/diffs/overflow_warning_component.rb]
- end
-
- let(:non_qa_and_feature_flag_files) do
- %w[rubocop/code_reuse_helpers.rb
- app/components/diffs/overflow_warning_component.rb
- config/feature_flags/development/access_token_ajax.yml]
- end
-
- let(:qa_spec_and_non_qa_files) do
- %w[rubocop/code_reuse_helpers.rb
- app/components/diffs/overflow_warning_component.rb
- qa/qa/specs/features/browser_ui/1_manage/test1.rb]
- end
-
- let(:qa_non_spec_and_non_qa_files) do
- %w[rubocop/code_reuse_helpers.rb
- app/components/diffs/overflow_warning_component.rb
- qa/qa/page/admin/menu.rb]
- end
-
- shared_examples 'determine qa tests' do
- context 'when only qa spec files have changed' do
- it 'returns only the changed qa specs' do
- subject = described_class.new({ changed_files: qa_spec_files }.merge(labels))
-
- expect(subject.execute).to eql qa_spec_files.map { |path| path.delete_prefix("qa/") }.join(' ')
- end
- end
-
- context 'when qa spec and non spec files have changed' do
- it 'does not return any specs' do
- subject = described_class.new({ changed_files: qa_spec_and_non_spec_files }.merge(labels))
- expect(subject.execute).to be_nil
- end
- end
-
- context 'when non-qa and feature flag files have changed' do
- it 'does not return any specs' do
- subject = described_class.new({ changed_files: non_qa_and_feature_flag_files }.merge(labels))
- expect(subject.execute).to be_nil
- end
- end
-
- context 'when qa spec and non-qa files have changed' do
- it 'does not return any specs' do
- subject = described_class.new({ changed_files: qa_spec_and_non_qa_files }.merge(labels))
- expect(subject.execute).to be_nil
- end
- end
-
- context 'when qa non-spec and non-qa files have changed' do
- it 'does not return any specs' do
- subject = described_class.new({ changed_files: qa_non_spec_and_non_qa_files }.merge(labels))
- expect(subject.execute).to be_nil
- end
- end
- end
-
- context 'when a devops label is not specified' do
- let(:labels) { { mr_labels: ['type::feature'] } }
-
- it_behaves_like 'determine qa tests'
-
- context 'when only non-qa files have changed' do
- it 'does not return any specs' do
- subject = described_class.new({ changed_files: non_qa_files })
- expect(subject.execute).to be_nil
- end
- end
- end
-
- context 'when a devops label is specified' do
- let(:labels) { { mr_labels: %w[devops::manage type::feature] } }
-
- it_behaves_like 'determine qa tests'
-
- context 'when only non-qa files have changed' do
- it 'returns the specs for the devops label' do
- subject = described_class.new({ changed_files: non_qa_files }.merge(labels))
- allow(subject).to receive(:qa_spec_directories_for_devops_stage)
- .and_return(['qa/qa/specs/features/browser_ui/1_manage/'])
- expect(subject.execute).to eql 'qa/specs/features/browser_ui/1_manage/'
- end
- end
- end
- end
- end
-end
diff --git a/spec/scripts/lib/glfm/parse_examples_spec.rb b/spec/scripts/lib/glfm/parse_examples_spec.rb
new file mode 100644
index 00000000000..a1ee6b3f440
--- /dev/null
+++ b/spec/scripts/lib/glfm/parse_examples_spec.rb
@@ -0,0 +1,331 @@
+# frozen_string_literal: true
+require 'fast_spec_helper'
+require_relative '../../../../scripts/lib/glfm/parse_examples'
+
+RSpec.describe Glfm::ParseExamples, '#parse_examples' do
+ subject do
+ described_module = described_class
+ Class.new { include described_module }.new
+ end
+
+ let(:spec_txt_contents) do
+ <<~MARKDOWN
+ ---
+ title: Spec
+ ...
+
+ # Introduction
+
+ intro
+
+ # 1.0.0 H1
+
+ ## 1.1.0 H2
+
+ no extension
+
+ ```````````````````````````````` example
+ example 1 md
+ .
+ html
+ ````````````````````````````````
+
+ one extension
+
+ ```````````````````````````````` example extension_1.1.0-1
+ example 2 md
+ .
+ html
+ ````````````````````````````````
+
+ ### 1.1.1 H3 with no examples
+
+ text
+
+ ### 1.1.2 Consecutive H3 with example
+
+ text
+
+ ```````````````````````````````` example disabled
+ example 3 md
+ .
+ html
+ ````````````````````````````````
+
+ ## 1.2.0 H2 with all disabled examples
+
+
+ ```````````````````````````````` example disabled
+ example 4 md
+ .
+ html
+ ````````````````````````````````
+
+ ## 1.2.0 New H2
+
+
+ ```````````````````````````````` example extension_1.2.0-1
+ example 5 md
+ .
+ html
+ ````````````````````````````````
+
+ # 2.0.0 New H1
+
+ ## 2.1.0 H2
+
+ ```````````````````````````````` example gitlab
+ example 6 md
+ .
+ html
+ ````````````````````````````````
+
+ ## 2.2.0 H2 which contains an H3
+
+ No example here, just text
+
+ ### 2.2.1 H3
+
+ The CommonMark and GHFM specifications don't have any examples inside an H3, but it is
+ supported for the GLFM specification.
+
+ ```````````````````````````````` example extension_2.2.1-1
+ example 7 md
+ .
+ html
+ ````````````````````````````````
+
+ # 3.0.0 New H1
+
+ ## 3.1.0 H2
+
+ ```````````````````````````````` example
+ example 8 md
+ .
+ html
+ ````````````````````````````````
+
+ ### 3.1.1 H3
+
+ ```````````````````````````````` example
+ example 9 md
+ .
+ html
+ ````````````````````````````````
+
+ ### 3.1.1 Consecutive H3
+
+ ```````````````````````````````` example
+ example 10 md
+ .
+ html
+ ````````````````````````````````
+
+ ## 3.2.0 Another H2
+
+ ```````````````````````````````` example
+ example 11 md
+ .
+ html
+ ````````````````````````````````
+
+ <!-- END TESTS -->
+
+ # Appendix
+
+ Appendix text.
+ MARKDOWN
+ end
+
+ let(:spec_txt_lines) { spec_txt_contents.split("\n") }
+
+ describe "parsing" do
+ it 'correctly parses' do
+ examples = subject.parse_examples(spec_txt_lines)
+
+ expected =
+ [
+ {
+ disabled: false,
+ end_line: 19,
+ example: 1,
+ extensions: [],
+ headers: [
+ '1.0.0 H1',
+ '1.1.0 H2'
+ ],
+ html: 'html',
+ markdown: 'example 1 md',
+ section: '1.1.0 H2',
+ start_line: 15
+ },
+ {
+ disabled: false,
+ end_line: 27,
+ example: 2,
+ extensions: %w[extension_1.1.0-1],
+ headers: [
+ '1.0.0 H1',
+ '1.1.0 H2'
+ ],
+ html: 'html',
+ markdown: 'example 2 md',
+ section: '1.1.0 H2',
+ start_line: 23
+ },
+ {
+ disabled: true,
+ end_line: 41,
+ example: 3,
+ extensions: %w[disabled],
+ headers: [
+ '1.0.0 H1',
+ '1.1.0 H2',
+ '1.1.2 Consecutive H3 with example'
+ ],
+ html: 'html',
+ markdown: 'example 3 md',
+ section: '1.1.2 Consecutive H3 with example',
+ start_line: 37
+ },
+ {
+ disabled: true,
+ end_line: 50,
+ example: 4,
+ extensions: %w[disabled],
+ headers: [
+ '1.0.0 H1',
+ '1.2.0 H2 with all disabled examples'
+ ],
+ html: 'html',
+ markdown: 'example 4 md',
+ section: '1.2.0 H2 with all disabled examples',
+ start_line: 46
+ },
+ {
+ disabled: false,
+ end_line: 59,
+ example: 5,
+ extensions: %w[extension_1.2.0-1],
+ headers: [
+ '1.0.0 H1',
+ '1.2.0 New H2'
+ ],
+ html: 'html',
+ markdown: 'example 5 md',
+ section: '1.2.0 New H2',
+ start_line: 55
+ },
+ {
+ disabled: false,
+ end_line: 69,
+ example: 6,
+ extensions: %w[gitlab],
+ headers: [
+ '2.0.0 New H1',
+ '2.1.0 H2'
+ ],
+ html: 'html',
+ markdown: 'example 6 md',
+ section: '2.1.0 H2',
+ start_line: 65
+ },
+ {
+ disabled: false,
+ end_line: 84,
+ example: 7,
+ extensions: %w[extension_2.2.1-1],
+ headers: [
+ '2.0.0 New H1',
+ '2.2.0 H2 which contains an H3',
+ '2.2.1 H3'
+ ],
+ html: 'html',
+ markdown: 'example 7 md',
+ section: '2.2.1 H3',
+ start_line: 80
+ },
+ {
+ disabled: false,
+ end_line: 94,
+ example: 8,
+ extensions: [],
+ headers: [
+ '3.0.0 New H1',
+ '3.1.0 H2'
+ ],
+ html: 'html',
+ markdown: 'example 8 md',
+ section: '3.1.0 H2',
+ start_line: 90
+ },
+ {
+ disabled: false,
+ end_line: 102,
+ example: 9,
+ extensions: [],
+ headers: [
+ '3.0.0 New H1',
+ '3.1.0 H2',
+ '3.1.1 H3'
+ ],
+ html: 'html',
+ markdown: 'example 9 md',
+ section: '3.1.1 H3',
+ start_line: 98
+ },
+ {
+ disabled: false,
+ end_line: 110,
+ example: 10,
+ extensions: [],
+ headers: [
+ '3.0.0 New H1',
+ '3.1.0 H2',
+ '3.1.1 Consecutive H3'
+ ],
+ html: 'html',
+ markdown: 'example 10 md',
+ section: '3.1.1 Consecutive H3',
+ start_line: 106
+ },
+ {
+ disabled: false,
+ end_line: 118,
+ example: 11,
+ extensions: [],
+ headers: [
+ '3.0.0 New H1',
+ '3.2.0 Another H2'
+ ],
+ html: 'html',
+ markdown: 'example 11 md',
+ section: '3.2.0 Another H2',
+ start_line: 114
+ }
+ ]
+
+ expect(examples).to eq(expected)
+ end
+ end
+
+ describe "with incorrect header nesting" do
+ let(:spec_txt_contents) do
+ <<~MARKDOWN
+ ---
+ title: Spec
+ ...
+
+ # H1
+
+ ### H3
+
+ MARKDOWN
+ end
+
+ it "raises if H3 is nested directly in H1" do
+ expect { subject.parse_examples(spec_txt_lines) }
+ .to raise_error(/The H3 'H3' may not be nested directly within the H1 'H1'/)
+ end
+ end
+end
diff --git a/spec/scripts/lib/glfm/shared_spec.rb b/spec/scripts/lib/glfm/shared_spec.rb
index f6792b93718..3ce9d44ba3d 100644
--- a/spec/scripts/lib/glfm/shared_spec.rb
+++ b/spec/scripts/lib/glfm/shared_spec.rb
@@ -9,6 +9,16 @@ RSpec.describe Glfm::Shared do
end.new
end
+ describe '#write_file' do
+ it 'works' do
+ filename = Dir::Tmpname.create('basename') do |path|
+ instance.write_file(path, 'test')
+ end
+
+ expect(File.read(filename)).to eq 'test'
+ end
+ end
+
describe '#run_external_cmd' do
it 'works' do
expect(instance.run_external_cmd('echo "hello"')).to eq("hello\n")
@@ -24,6 +34,14 @@ RSpec.describe Glfm::Shared do
end
end
+ describe '#dump_yaml_with_formatting' do
+ it 'works' do
+ hash = { a: 'b' }
+ yaml = instance.dump_yaml_with_formatting(hash, literal_scalars: true)
+ expect(yaml).to eq("---\na: |-\n b\n")
+ end
+ end
+
describe '#output' do
# NOTE: The #output method is normally always mocked, to prevent output while the specs are
# running. However, in order to provide code coverage for the method, we have to invoke
diff --git a/spec/scripts/lib/glfm/update_example_snapshots_spec.rb b/spec/scripts/lib/glfm/update_example_snapshots_spec.rb
index fe815aa6f1e..f96936c0a6f 100644
--- a/spec/scripts/lib/glfm/update_example_snapshots_spec.rb
+++ b/spec/scripts/lib/glfm/update_example_snapshots_spec.rb
@@ -35,6 +35,8 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
let(:glfm_spec_txt_local_io) { StringIO.new(glfm_spec_txt_contents) }
let(:glfm_example_status_yml_path) { described_class::GLFM_EXAMPLE_STATUS_YML_PATH }
let(:glfm_example_status_yml_io) { StringIO.new(glfm_example_status_yml_contents) }
+ let(:glfm_example_metadata_yml_path) { described_class::GLFM_EXAMPLE_METADATA_YML_PATH }
+ let(:glfm_example_metadata_yml_io) { StringIO.new(glfm_example_metadata_yml_contents) }
# Example Snapshot (ES) output files
let(:es_examples_index_yml_path) { described_class::ES_EXAMPLES_INDEX_YML_PATH }
@@ -52,7 +54,7 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
let(:static_html_tempfile_path) { Tempfile.new.path }
let(:glfm_spec_txt_contents) do
- <<~GLFM_SPEC_TXT_CONTENTS
+ <<~MARKDOWN
---
title: GitLab Flavored Markdown Spec
...
@@ -128,12 +130,25 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
## Strong but with two asterisks
- ```````````````````````````````` example gitlab strong
+ ```````````````````````````````` example gitlab
**bold**
.
<p><strong>bold</strong></p>
````````````````````````````````
+ ## H2 which contains an H3
+
+ ### Example in an H3
+
+ The CommonMark and GHFM specifications don't have any examples inside an H3, but it is
+ supported for the GLFM specification.
+
+ ```````````````````````````````` example gitlab
+ Example in an H3
+ .
+ <p>Example in an H3</p>
+ ````````````````````````````````
+
# Second GitLab-Specific Section with Examples
## Strong but with HTML
@@ -142,7 +157,7 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
`source_specification` will be `gitlab`.
- ```````````````````````````````` example gitlab strong
+ ```````````````````````````````` example gitlab
<strong>
bold
</strong>
@@ -156,7 +171,7 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
## Strong but skipped
- ```````````````````````````````` example gitlab strong
+ ```````````````````````````````` example gitlab
**this example will be skipped**
.
<p><strong>this example will be skipped</strong></p>
@@ -164,80 +179,151 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
## Strong but manually modified and skipped
- ```````````````````````````````` example gitlab strong
+ ```````````````````````````````` example gitlab
**This example will have its manually modified static HTML, WYSIWYG HTML, and ProseMirror JSON preserved**
.
<p><strong>This example will have its manually modified static HTML, WYSIWYG HTML, and ProseMirror JSON preserved</strong></p>
````````````````````````````````
+ # API Request Overrides
+
+ This section contains examples which verify that all of the fixture models which are set up
+ in `render_static_html.rb` are correctly configured. They exercise various `preview_markdown`
+ endpoints via `glfm_example_metadata.yml`.
+
+ ## Group Upload Link
+
+ `preview_markdown` exercising `groups` API endpoint and `UploadLinkFilter`:
+
+ ```````````````````````````````` example gitlab
+ [groups-test-file](/uploads/groups-test-file)
+ .
+ <p><a href="groups-test-file">groups-test-file</a></p>
+ ````````````````````````````````
+
+ ## Project Repo Link
+
+ `preview_markdown` exercising `projects` API endpoint and `RepositoryLinkFilter`:
+
+ ```````````````````````````````` example gitlab
+ [projects-test-file](projects-test-file)
+ .
+ <p><a href="projects-test-file">projects-test-file</a></p>
+ ````````````````````````````````
+
+ ## Project Snippet Ref
+
+ `preview_markdown` exercising `projects` API endpoint and `SnippetReferenceFilter`:
+
+ ```````````````````````````````` example gitlab
+ This project snippet ID reference IS filtered: $88888
+ .
+ <p>This project snippet ID reference IS filtered: <a href="/glfm_group/glfm_project/-/snippets/88888">$88888</a>
+ ````````````````````````````````
+
+ ## Personal Snippet Ref
+
+ `preview_markdown` exercising personal (non-project) `snippets` 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
+ `SnippetReferenceFilter`, even if the ID is valid.
+
+ ```````````````````````````````` example gitlab
+ This personal snippet ID reference is NOT filtered: $99999
+ .
+ <p>This personal snippet ID reference is NOT filtered: $99999</p>
+ ````````````````````````````````
+
+ ## Project Wiki Link
+
+ `preview_markdown` exercising project `wikis` API endpoint and `WikiLinkFilter`:
+
+ ```````````````````````````````` example gitlab
+ [project-wikis-test-file](project-wikis-test-file)
+ .
+ <p><a href="project-wikis-test-file">project-wikis-test-file</a></p>
+ ````````````````````````````````
+
<!-- END TESTS -->
# Appendix
Appendix text.
- GLFM_SPEC_TXT_CONTENTS
+ MARKDOWN
end
let(:glfm_example_status_yml_contents) do
- # language=YAML
- <<~GLFM_EXAMPLE_STATUS_YML_CONTENTS
+ <<~YAML
---
- 02_01__inlines__strong__001:
+ 02_01_00__inlines__strong__001:
# The skip_update_example_snapshots key is present, but false, so this example is not skipped
skip_update_example_snapshots: false
- 02_01__inlines__strong__002:
+ 02_01_00__inlines__strong__002:
# It is OK to have an empty (nil) value for an example statuses entry, it means they will all be false.
- 05_01__third_gitlab_specific_section_with_skipped_examples__strong_but_skipped__001:
+ 05_01_00__third_gitlab_specific_section_with_skipped_examples__strong_but_skipped__001:
# Always skip this example
skip_update_example_snapshots: 'skipping this example because it is very bad'
- 05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001:
+ 05_02_00__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001:
# Always skip this example, but preserve the existing manual modifications
skip_update_example_snapshots: 'skipping this example because we have manually modified it'
- GLFM_EXAMPLE_STATUS_YML_CONTENTS
+ YAML
+ end
+
+ let(:glfm_example_metadata_yml_contents) do
+ <<~YAML
+ ---
+ 06_01_00__api_request_overrides__group_upload_link__001:
+ api_request_override_path: /groups/glfm_group/preview_markdown
+ 06_02_00__api_request_overrides__project_repo_link__001:
+ api_request_override_path: /glfm_group/glfm_project/preview_markdown
+ 06_03_00__api_request_overrides__project_snippet_ref__001:
+ api_request_override_path: /glfm_group/glfm_project/preview_markdown
+ 06_04_00__api_request_overrides__personal_snippet_ref__001:
+ api_request_override_path: /-/snippets/preview_markdown
+ 06_05_00__api_request_overrides__project_wiki_link__001:
+ api_request_override_path: /glfm_group/glfm_project/-/wikis/new_page/preview_markdown
+ YAML
end
let(:es_html_yml_io_existing_contents) do
- # language=YAML
- <<~ES_HTML_YML_IO_EXISTING_CONTENTS
+ <<~YAML
---
- 00_00__obsolete_entry_to_be_deleted__001:
+ 00_00_00__obsolete_entry_to_be_deleted__001:
canonical: |
- This entry is no longer exists in the spec.txt, and is not skipped, so it will be deleted.
+ This entry is no longer exists in the spec.txt, so it will be deleted.
static: |-
- This entry is no longer exists in the spec.txt, and is not skipped, so it will be deleted.
+ This entry is no longer exists in the spec.txt, so it will be deleted.
wysiwyg: |-
- This entry is no longer exists in the spec.txt, and is not skipped, so it will be deleted.
- 02_01__inlines__strong__001:
+ This entry is no longer exists in the spec.txt, so it will be deleted.
+ 02_01_00__inlines__strong__001:
canonical: |
This entry is existing, but not skipped, so it will be overwritten.
static: |-
This entry is existing, but not skipped, so it will be overwritten.
wysiwyg: |-
This entry is existing, but not skipped, so it will be overwritten.
- 05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001:
+ 05_02_00__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001:
canonical: |
<p><strong>This example will have its manually modified static HTML, WYSIWYG HTML, and ProseMirror JSON preserved</strong></p>
static: |-
<p>This is the manually modified static HTML which will be preserved</p>
wysiwyg: |-
<p>This is the manually modified WYSIWYG HTML which will be preserved</p>
- ES_HTML_YML_IO_EXISTING_CONTENTS
+ YAML
end
let(:es_prosemirror_json_yml_io_existing_contents) do
- # language=YAML
- <<~ES_PROSEMIRROR_JSON_YML_IO_EXISTING_CONTENTS
+ <<~YAML
---
- 00_00__obsolete_entry_to_be_deleted__001:
+ 00_00_00__obsolete_entry_to_be_deleted__001: |-
{
"obsolete": "This entry is no longer exists in the spec.txt, and is not skipped, so it will be deleted."
}
- 02_01__inlines__strong__001: |-
+ 02_01_00__inlines__strong__001: |-
{
"existing": "This entry is existing, but not skipped, so it will be overwritten."
}
- # 02_01__inlines__strong__002: is omitted from the existing file and skipped, to test that scenario.
- 02_03__inlines__strikethrough_extension__001: |-
+ 02_03_00__inlines__strikethrough_extension__001: |-
{
"type": "doc",
"content": [
@@ -252,15 +338,15 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
}
]
}
- 04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__001: |-
+ 04_01_00__second_gitlab_specific_section_with_examples__strong_but_with_html__001: |-
{
"existing": "This entry is manually modified and preserved because skip_update_example_snapshot_prosemirror_json will be truthy"
}
- 05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001: |-
+ 05_02_00__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001: |-
{
"existing": "This entry is manually modified and preserved because skip_update_example_snapshots will be truthy"
}
- ES_PROSEMIRROR_JSON_YML_IO_EXISTING_CONTENTS
+ YAML
end
before do
@@ -271,6 +357,9 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
# input files
allow(File).to receive(:open).with(glfm_spec_txt_path) { glfm_spec_txt_local_io }
allow(File).to receive(:open).with(glfm_example_status_yml_path) { glfm_example_status_yml_io }
+ allow(File).to receive(:open).with(glfm_example_metadata_yml_path) do
+ glfm_example_metadata_yml_io
+ end
# output files
allow(File).to receive(:open).with(es_examples_index_yml_path, 'w') { es_examples_index_yml_io }
@@ -286,6 +375,7 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
# Allow normal opening of Tempfile files created during script execution.
tempfile_basenames = [
described_class::MARKDOWN_TEMPFILE_BASENAME[0],
+ described_class::METADATA_TEMPFILE_BASENAME[0],
described_class::STATIC_HTML_TEMPFILE_BASENAME[0],
described_class::WYSIWYG_HTML_AND_JSON_TEMPFILE_BASENAME[0]
].join('|')
@@ -299,40 +389,41 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
allow(subject).to receive(:output)
end
- describe 'when skip_update_example_snapshots is truthy' do
- let(:es_examples_index_yml_contents) { reread_io(es_examples_index_yml_io) }
- let(:es_markdown_yml_contents) { reread_io(es_markdown_yml_io) }
- let(:expected_unskipped_example) do
- /05_01__third_gitlab_specific_section_with_skipped_examples__strong_but_skipped__001/
- end
-
- it 'still writes the example to examples_index.yml' do
- subject.process(skip_static_and_wysiwyg: true)
+ describe 'glfm_example_status.yml' do
+ describe 'when skip_update_example_snapshots entry is truthy' do
+ let(:es_examples_index_yml_contents) { reread_io(es_examples_index_yml_io) }
+ let(:es_markdown_yml_contents) { reread_io(es_markdown_yml_io) }
+ let(:expected_unskipped_example) do
+ /05_01_00__third_gitlab_specific_section_with_skipped_examples__strong_but_skipped__001/
+ end
- expect(es_examples_index_yml_contents).to match(expected_unskipped_example)
- end
+ it 'still writes the example to examples_index.yml' do
+ subject.process(skip_static_and_wysiwyg: true)
- it 'still writes the example to markdown.yml' do
- subject.process(skip_static_and_wysiwyg: true)
+ expect(es_examples_index_yml_contents).to match(expected_unskipped_example)
+ end
- expect(es_markdown_yml_contents).to match(expected_unskipped_example)
- end
+ it 'still writes the example to markdown.yml' do
+ subject.process(skip_static_and_wysiwyg: true)
- describe 'when any other skip_update_example_* is also truthy' do
- let(:glfm_example_status_yml_contents) do
- # language=YAML
- <<~GLFM_EXAMPLE_STATUS_YML_CONTENTS
- ---
- 02_01__inlines__strong__001:
- skip_update_example_snapshots: 'if the skip_update_example_snapshots key is truthy...'
- skip_update_example_snapshot_html_static: '...then no other skip_update_example_* keys can be truthy'
- GLFM_EXAMPLE_STATUS_YML_CONTENTS
+ expect(es_markdown_yml_contents).to match(expected_unskipped_example)
end
- it 'raises an error' do
- expected_msg = "Error: '02_01__inlines__strong__001' must not have any 'skip_update_example_snapshot_*' " \
+ describe 'when any other skip_update_example_snapshot_* is also truthy' do
+ let(:glfm_example_status_yml_contents) do
+ <<~YAML
+ ---
+ 02_01_00__inlines__strong__001:
+ skip_update_example_snapshots: 'if the skip_update_example_snapshots key is truthy...'
+ skip_update_example_snapshot_html_static: '...then no other skip_update_example_* keys can be truthy'
+ YAML
+ end
+
+ it 'raises an error' do
+ expected_msg = "Error: '02_01_00__inlines__strong__001' must not have any 'skip_update_example_snapshot_*' " \
"values specified if 'skip_update_example_snapshots' is truthy"
- expect { subject.process }.to raise_error(/#{Regexp.escape(expected_msg)}/)
+ expect { subject.process }.to raise_error(/#{Regexp.escape(expected_msg)}/)
+ end
end
end
end
@@ -340,31 +431,48 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
describe 'writing examples_index.yml' do
let(:es_examples_index_yml_contents) { reread_io(es_examples_index_yml_io) }
let(:expected_examples_index_yml_contents) do
- # language=YAML
- <<~ES_EXAMPLES_INDEX_YML_CONTENTS
+ <<~YAML
---
- 02_01__inlines__strong__001:
+ 02_01_00__inlines__strong__001:
spec_txt_example_position: 1
source_specification: commonmark
- 02_01__inlines__strong__002:
+ 02_01_00__inlines__strong__002:
spec_txt_example_position: 2
source_specification: github
- 02_03__inlines__strikethrough_extension__001:
+ 02_03_00__inlines__strikethrough_extension__001:
spec_txt_example_position: 4
source_specification: github
- 03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001:
+ 03_01_00__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001:
spec_txt_example_position: 5
source_specification: gitlab
- 04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__001:
+ 03_02_01__first_gitlab_specific_section_with_examples__h2_which_contains_an_h3__example_in_an_h3__001:
spec_txt_example_position: 6
source_specification: gitlab
- 05_01__third_gitlab_specific_section_with_skipped_examples__strong_but_skipped__001:
+ 04_01_00__second_gitlab_specific_section_with_examples__strong_but_with_html__001:
spec_txt_example_position: 7
source_specification: gitlab
- 05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001:
+ 05_01_00__third_gitlab_specific_section_with_skipped_examples__strong_but_skipped__001:
spec_txt_example_position: 8
source_specification: gitlab
- ES_EXAMPLES_INDEX_YML_CONTENTS
+ 05_02_00__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001:
+ spec_txt_example_position: 9
+ source_specification: gitlab
+ 06_01_00__api_request_overrides__group_upload_link__001:
+ spec_txt_example_position: 10
+ source_specification: gitlab
+ 06_02_00__api_request_overrides__project_repo_link__001:
+ spec_txt_example_position: 11
+ source_specification: gitlab
+ 06_03_00__api_request_overrides__project_snippet_ref__001:
+ spec_txt_example_position: 12
+ source_specification: gitlab
+ 06_04_00__api_request_overrides__personal_snippet_ref__001:
+ spec_txt_example_position: 13
+ source_specification: gitlab
+ 06_05_00__api_request_overrides__project_wiki_link__001:
+ spec_txt_example_position: 14
+ source_specification: gitlab
+ YAML
end
it 'writes the correct content' do
@@ -377,26 +485,37 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
describe 'writing markdown.yml' do
let(:es_markdown_yml_contents) { reread_io(es_markdown_yml_io) }
let(:expected_markdown_yml_contents) do
- # language=YAML
- <<~ES_MARKDOWN_YML_CONTENTS
+ <<~YAML
---
- 02_01__inlines__strong__001: |
+ 02_01_00__inlines__strong__001: |
__bold__
- 02_01__inlines__strong__002: |
+ 02_01_00__inlines__strong__002: |
__bold with more text__
- 02_03__inlines__strikethrough_extension__001: |
+ 02_03_00__inlines__strikethrough_extension__001: |
~~Hi~~ Hello, world!
- 03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001: |
+ 03_01_00__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001: |
**bold**
- 04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__001: |
+ 03_02_01__first_gitlab_specific_section_with_examples__h2_which_contains_an_h3__example_in_an_h3__001: |
+ Example in an H3
+ 04_01_00__second_gitlab_specific_section_with_examples__strong_but_with_html__001: |
<strong>
bold
</strong>
- 05_01__third_gitlab_specific_section_with_skipped_examples__strong_but_skipped__001: |
+ 05_01_00__third_gitlab_specific_section_with_skipped_examples__strong_but_skipped__001: |
**this example will be skipped**
- 05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001: |
+ 05_02_00__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001: |
**This example will have its manually modified static HTML, WYSIWYG HTML, and ProseMirror JSON preserved**
- ES_MARKDOWN_YML_CONTENTS
+ 06_01_00__api_request_overrides__group_upload_link__001: |
+ [groups-test-file](/uploads/groups-test-file)
+ 06_02_00__api_request_overrides__project_repo_link__001: |
+ [projects-test-file](projects-test-file)
+ 06_03_00__api_request_overrides__project_snippet_ref__001: |
+ This project snippet ID reference IS filtered: $88888
+ 06_04_00__api_request_overrides__personal_snippet_ref__001: |
+ This personal snippet ID reference is NOT filtered: $99999
+ 06_05_00__api_request_overrides__project_wiki_link__001: |
+ [project-wikis-test-file](project-wikis-test-file)
+ YAML
end
it 'writes the correct content' do
@@ -406,6 +525,7 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
end
end
+ # rubocop:disable RSpec/MultipleMemoizedHelpers
describe 'writing html.yml and prosemirror_json.yml' do
let(:es_html_yml_contents) { reread_io(es_html_yml_io) }
let(:es_prosemirror_json_yml_contents) { reread_io(es_prosemirror_json_yml_io) }
@@ -413,54 +533,64 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
# NOTE: This example_status.yml is crafted in conjunction with expected_html_yml_contents
# to test the behavior of the `skip_update_*` flags
let(:glfm_example_status_yml_contents) do
- # language=YAML
- <<~GLFM_EXAMPLE_STATUS_YML_CONTENTS
+ <<~YAML
---
- 02_01__inlines__strong__002:
+ 02_01_00__inlines__strong__002:
+ # NOTE: 02_01_00__inlines__strong__002: is omitted from the existing prosemirror_json.yml file, and is also
+ # skipped here, to show that an example does not need to exist in order to be skipped.
+ # TODO: This should be changed to raise an error instead, to enforce that there cannot be orphaned
+ # entries in glfm_example_status.yml. This task is captured in
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/361241#other-cleanup-tasks
skip_update_example_snapshot_prosemirror_json: "skipping because JSON isn't cool enough"
- 03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001:
+ 03_01_00__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001:
skip_update_example_snapshot_html_static: "skipping because there's too much static"
- 04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__001:
+ 04_01_00__second_gitlab_specific_section_with_examples__strong_but_with_html__001:
skip_update_example_snapshot_html_wysiwyg: 'skipping because what you see is NOT what you get'
skip_update_example_snapshot_prosemirror_json: "skipping because JSON still isn't cool enough"
- 05_01__third_gitlab_specific_section_with_skipped_examples__strong_but_skipped__001:
+ 05_01_00__third_gitlab_specific_section_with_skipped_examples__strong_but_skipped__001:
skip_update_example_snapshots: 'skipping this example because it is very bad'
- 05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001:
+ 05_02_00__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001:
skip_update_example_snapshots: 'skipping this example because we have manually modified it'
- GLFM_EXAMPLE_STATUS_YML_CONTENTS
+ YAML
end
let(:expected_html_yml_contents) do
- # language=YAML
- <<~ES_HTML_YML_CONTENTS
+ <<~YAML
---
- 02_01__inlines__strong__001:
+ 02_01_00__inlines__strong__001:
canonical: |
<p><strong>bold</strong></p>
static: |-
<p data-sourcepos="1:1-1:8" dir="auto"><strong>bold</strong></p>
wysiwyg: |-
<p><strong>bold</strong></p>
- 02_01__inlines__strong__002:
+ 02_01_00__inlines__strong__002:
canonical: |
<p><strong>bold with more text</strong></p>
static: |-
<p data-sourcepos="1:1-1:23" dir="auto"><strong>bold with more text</strong></p>
wysiwyg: |-
<p><strong>bold with more text</strong></p>
- 02_03__inlines__strikethrough_extension__001:
+ 02_03_00__inlines__strikethrough_extension__001:
canonical: |
<p><del>Hi</del> Hello, world!</p>
static: |-
<p data-sourcepos="1:1-1:20" dir="auto"><del>Hi</del> Hello, world!</p>
wysiwyg: |-
<p><s>Hi</s> Hello, world!</p>
- 03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001:
+ 03_01_00__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001:
canonical: |
<p><strong>bold</strong></p>
wysiwyg: |-
<p><strong>bold</strong></p>
- 04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__001:
+ 03_02_01__first_gitlab_specific_section_with_examples__h2_which_contains_an_h3__example_in_an_h3__001:
+ canonical: |
+ <p>Example in an H3</p>
+ static: |-
+ <p data-sourcepos="1:1-1:16" dir="auto">Example in an H3</p>
+ wysiwyg: |-
+ <p>Example in an H3</p>
+ 04_01_00__second_gitlab_specific_section_with_examples__strong_but_with_html__001:
canonical: |
<p><strong>
bold
@@ -469,21 +599,55 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
<strong>
bold
</strong>
- 05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001:
+ 05_02_00__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001:
canonical: |
<p><strong>This example will have its manually modified static HTML, WYSIWYG HTML, and ProseMirror JSON preserved</strong></p>
static: |-
<p>This is the manually modified static HTML which will be preserved</p>
wysiwyg: |-
<p>This is the manually modified WYSIWYG HTML which will be preserved</p>
- ES_HTML_YML_CONTENTS
+ 06_01_00__api_request_overrides__group_upload_link__001:
+ canonical: |
+ <p><a href="groups-test-file">groups-test-file</a></p>
+ static: |-
+ <p data-sourcepos="1:1-1:45" dir="auto"><a href="/groups/glfm_group/-/uploads/groups-test-file" data-canonical-src="/uploads/groups-test-file" data-link="true" class="gfm">groups-test-file</a></p>
+ wysiwyg: |-
+ <p><a target="_blank" rel="noopener noreferrer nofollow" href="/uploads/groups-test-file">groups-test-file</a></p>
+ 06_02_00__api_request_overrides__project_repo_link__001:
+ canonical: |
+ <p><a href="projects-test-file">projects-test-file</a></p>
+ static: |-
+ <p data-sourcepos="1:1-1:40" dir="auto"><a href="/glfm_group/glfm_project/-/blob/master/projects-test-file">projects-test-file</a></p>
+ wysiwyg: |-
+ <p><a target="_blank" rel="noopener noreferrer nofollow" href="projects-test-file">projects-test-file</a></p>
+ 06_03_00__api_request_overrides__project_snippet_ref__001:
+ canonical: |
+ <p>This project snippet ID reference IS filtered: <a href="/glfm_group/glfm_project/-/snippets/88888">$88888</a>
+ static: |-
+ <p data-sourcepos="1:1-1:53" dir="auto">This project snippet ID reference IS filtered: <a href="/glfm_group/glfm_project/-/snippets/88888" data-reference-type="snippet" data-original="$88888" data-link="false" data-link-reference="false" data-project="77777" data-snippet="88888" data-container="body" data-placement="top" title="glfm_project_snippet" class="gfm gfm-snippet has-tooltip">$88888</a></p>
+ wysiwyg: |-
+ <p>This project snippet ID reference IS filtered: $88888</p>
+ 06_04_00__api_request_overrides__personal_snippet_ref__001:
+ canonical: |
+ <p>This personal snippet ID reference is NOT filtered: $99999</p>
+ static: |-
+ <p data-sourcepos="1:1-1:58" dir="auto">This personal snippet ID reference is NOT filtered: $99999</p>
+ wysiwyg: |-
+ <p>This personal snippet ID reference is NOT filtered: $99999</p>
+ 06_05_00__api_request_overrides__project_wiki_link__001:
+ canonical: |
+ <p><a href="project-wikis-test-file">project-wikis-test-file</a></p>
+ static: |-
+ <p data-sourcepos="1:1-1:50" dir="auto"><a href="/glfm_group/glfm_project/-/wikis/project-wikis-test-file" data-canonical-src="project-wikis-test-file">project-wikis-test-file</a></p>
+ wysiwyg: |-
+ <p><a target="_blank" rel="noopener noreferrer nofollow" href="project-wikis-test-file">project-wikis-test-file</a></p>
+ YAML
end
let(:expected_prosemirror_json_contents) do
- # language=YAML
- <<~ES_PROSEMIRROR_JSON_YML_CONTENTS
+ <<~YAML
---
- 02_01__inlines__strong__001: |-
+ 02_01_00__inlines__strong__001: |-
{
"type": "doc",
"content": [
@@ -503,7 +667,7 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
}
]
}
- 02_03__inlines__strikethrough_extension__001: |-
+ 02_03_00__inlines__strikethrough_extension__001: |-
{
"type": "doc",
"content": [
@@ -527,7 +691,7 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
}
]
}
- 03_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001: |-
+ 03_01_00__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001: |-
{
"type": "doc",
"content": [
@@ -547,15 +711,144 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
}
]
}
- 04_01__second_gitlab_specific_section_with_examples__strong_but_with_html__001: |-
+ 03_02_01__first_gitlab_specific_section_with_examples__h2_which_contains_an_h3__example_in_an_h3__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "Example in an H3"
+ }
+ ]
+ }
+ ]
+ }
+ 04_01_00__second_gitlab_specific_section_with_examples__strong_but_with_html__001: |-
{
"existing": "This entry is manually modified and preserved because skip_update_example_snapshot_prosemirror_json will be truthy"
}
- 05_02__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001: |-
+ 05_02_00__third_gitlab_specific_section_with_skipped_examples__strong_but_manually_modified_and_skipped__001: |-
{
"existing": "This entry is manually modified and preserved because skip_update_example_snapshots will be truthy"
}
- ES_PROSEMIRROR_JSON_YML_CONTENTS
+ 06_01_00__api_request_overrides__group_upload_link__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "link",
+ "attrs": {
+ "href": "/uploads/groups-test-file",
+ "target": "_blank",
+ "class": null,
+ "title": null,
+ "canonicalSrc": "/uploads/groups-test-file",
+ "isReference": false
+ }
+ }
+ ],
+ "text": "groups-test-file"
+ }
+ ]
+ }
+ ]
+ }
+ 06_02_00__api_request_overrides__project_repo_link__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "link",
+ "attrs": {
+ "href": "projects-test-file",
+ "target": "_blank",
+ "class": null,
+ "title": null,
+ "canonicalSrc": "projects-test-file",
+ "isReference": false
+ }
+ }
+ ],
+ "text": "projects-test-file"
+ }
+ ]
+ }
+ ]
+ }
+ 06_03_00__api_request_overrides__project_snippet_ref__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "This project snippet ID reference IS filtered: $88888"
+ }
+ ]
+ }
+ ]
+ }
+ 06_04_00__api_request_overrides__personal_snippet_ref__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "text": "This personal snippet ID reference is NOT filtered: $99999"
+ }
+ ]
+ }
+ ]
+ }
+ 06_05_00__api_request_overrides__project_wiki_link__001: |-
+ {
+ "type": "doc",
+ "content": [
+ {
+ "type": "paragraph",
+ "content": [
+ {
+ "type": "text",
+ "marks": [
+ {
+ "type": "link",
+ "attrs": {
+ "href": "project-wikis-test-file",
+ "target": "_blank",
+ "class": null,
+ "title": null,
+ "canonicalSrc": "project-wikis-test-file",
+ "isReference": false
+ }
+ }
+ ],
+ "text": "project-wikis-test-file"
+ }
+ ]
+ }
+ ]
+ }
+ YAML
end
before do
@@ -581,6 +874,7 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
expect(es_prosemirror_json_yml_contents).to eq(expected_prosemirror_json_contents)
end
end
+ # rubocop:enable RSpec/MultipleMemoizedHelpers
def reread_io(io)
# Reset the io StringIO to the beginning position of the buffer
diff --git a/spec/scripts/lib/glfm/update_specification_spec.rb b/spec/scripts/lib/glfm/update_specification_spec.rb
index e8d34b13efa..9fb671e0016 100644
--- a/spec/scripts/lib/glfm/update_specification_spec.rb
+++ b/spec/scripts/lib/glfm/update_specification_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
let(:glfm_spec_txt_io) { StringIO.new }
let(:ghfm_spec_txt_contents) do
- <<~GHFM_SPEC_TXT_CONTENTS
+ <<~MARKDOWN
---
title: GitHub Flavored Markdown Spec
version: 0.29
@@ -49,25 +49,26 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
# Appendix
Appendix text.
- GHFM_SPEC_TXT_CONTENTS
+ MARKDOWN
end
let(:glfm_intro_txt_contents) do
- <<~GLFM_INTRO_TXT_CONTENTS
+ # language=Markdown
+ <<~MARKDOWN
# Introduction
## What is GitLab Flavored Markdown?
Intro text about GitLab Flavored Markdown.
- GLFM_INTRO_TXT_CONTENTS
+ MARKDOWN
end
let(:glfm_examples_txt_contents) do
- <<~GLFM_EXAMPLES_TXT_CONTENTS
+ <<~MARKDOWN
# GitLab-Specific Section with Examples
Some examples.
- GLFM_EXAMPLES_TXT_CONTENTS
+ MARKDOWN
end
before do
@@ -118,12 +119,12 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
context 'with error handling' do
context 'with a version mismatch' do
let(:ghfm_spec_txt_contents) do
- <<~GHFM_SPEC_TXT_CONTENTS
+ <<~MARKDOWN
---
title: GitHub Flavored Markdown Spec
version: 0.30
...
- GHFM_SPEC_TXT_CONTENTS
+ MARKDOWN
end
it 'raises an error' do
@@ -173,7 +174,7 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
end
it 'inserts the GitLab examples sections before the appendix section' do
- expected = <<~GHFM_SPEC_TXT_CONTENTS
+ expected = <<~MARKDOWN
End of last GitHub examples section.
# GitLab-Specific Section with Examples
@@ -183,7 +184,7 @@ RSpec.describe Glfm::UpdateSpecification, '#process' do
<!-- END TESTS -->
# Appendix
- GHFM_SPEC_TXT_CONTENTS
+ MARKDOWN
expect(glfm_contents).to match(/#{Regexp.escape(expected)}/m)
end
end
diff --git a/spec/scripts/trigger-build_spec.rb b/spec/scripts/trigger-build_spec.rb
index d0f1d3dc41b..46023d5823d 100644
--- a/spec/scripts/trigger-build_spec.rb
+++ b/spec/scripts/trigger-build_spec.rb
@@ -195,33 +195,13 @@ RSpec.describe Trigger do
end
end
- context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is set' do
+ context 'when CI_COMMIT_SHA is set' do
before do
- stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', 'ci_merge_request_source_branch_sha')
- end
-
- it 'sets TOP_UPSTREAM_SOURCE_SHA to ci_merge_request_source_branch_sha' do
- expect(subject.variables['TOP_UPSTREAM_SOURCE_SHA']).to eq('ci_merge_request_source_branch_sha')
- end
- end
-
- context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is set as empty' do
- before do
- stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', '')
- end
-
- it 'sets TOP_UPSTREAM_SOURCE_SHA to CI_COMMIT_SHA' do
- expect(subject.variables['TOP_UPSTREAM_SOURCE_SHA']).to eq(env['CI_COMMIT_SHA'])
- end
- end
-
- context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is not set' do
- before do
- stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', nil)
+ stub_env('CI_COMMIT_SHA', 'ci_commit_sha')
end
it 'sets TOP_UPSTREAM_SOURCE_SHA to CI_COMMIT_SHA' do
- expect(subject.variables['TOP_UPSTREAM_SOURCE_SHA']).to eq(env['CI_COMMIT_SHA'])
+ expect(subject.variables['TOP_UPSTREAM_SOURCE_SHA']).to eq('ci_commit_sha')
end
end
end
@@ -264,195 +244,6 @@ RSpec.describe Trigger do
end
end
- describe Trigger::Omnibus do
- describe '#variables' do
- it 'invokes the trigger with expected variables' do
- expect(subject.variables).to include(
- 'QA_IMAGE' => env['QA_IMAGE'],
- 'SKIP_QA_DOCKER' => 'true',
- 'ALTERNATIVE_SOURCES' => 'true',
- 'CACHE_UPDATE' => env['OMNIBUS_GITLAB_CACHE_UPDATE'],
- 'GITLAB_QA_OPTIONS' => env['GITLAB_QA_OPTIONS'],
- 'QA_TESTS' => env['QA_TESTS'],
- 'ALLURE_JOB_NAME' => env['ALLURE_JOB_NAME']
- )
- end
-
- context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is set' do
- before do
- stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', 'ci_merge_request_source_branch_sha')
- end
-
- it 'sets GITLAB_VERSION & IMAGE_TAG to ci_merge_request_source_branch_sha' do
- expect(subject.variables).to include(
- 'GITLAB_VERSION' => 'ci_merge_request_source_branch_sha',
- 'IMAGE_TAG' => 'ci_merge_request_source_branch_sha'
- )
- end
- end
-
- context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is set as empty' do
- before do
- stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', '')
- end
-
- it 'sets GITLAB_VERSION & IMAGE_TAG to CI_COMMIT_SHA' do
- expect(subject.variables).to include(
- 'GITLAB_VERSION' => env['CI_COMMIT_SHA'],
- 'IMAGE_TAG' => env['CI_COMMIT_SHA']
- )
- end
- end
-
- context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is not set' do
- before do
- stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', nil)
- end
-
- it 'sets GITLAB_VERSION & IMAGE_TAG to CI_COMMIT_SHA' do
- expect(subject.variables).to include(
- 'GITLAB_VERSION' => env['CI_COMMIT_SHA'],
- 'IMAGE_TAG' => env['CI_COMMIT_SHA']
- )
- end
- end
-
- context 'when Trigger.security? is true' do
- before do
- allow(Trigger).to receive(:security?).and_return(true)
- end
-
- it 'sets SECURITY_SOURCES to true' do
- expect(subject.variables['SECURITY_SOURCES']).to eq('true')
- end
- end
-
- context 'when Trigger.security? is false' do
- before do
- allow(Trigger).to receive(:security?).and_return(false)
- end
-
- it 'sets SECURITY_SOURCES to false' do
- expect(subject.variables['SECURITY_SOURCES']).to eq('false')
- end
- end
-
- context 'when Trigger.ee? is true' do
- before do
- allow(Trigger).to receive(:ee?).and_return(true)
- end
-
- it 'sets ee to true' do
- expect(subject.variables['ee']).to eq('true')
- end
- end
-
- context 'when Trigger.ee? is false' do
- before do
- allow(Trigger).to receive(:ee?).and_return(false)
- end
-
- it 'sets ee to false' do
- expect(subject.variables['ee']).to eq('false')
- end
- end
-
- context 'when QA_BRANCH is set' do
- before do
- stub_env('QA_BRANCH', 'qa_branch')
- end
-
- it 'sets QA_BRANCH to qa_branch' do
- expect(subject.variables['QA_BRANCH']).to eq('qa_branch')
- end
- end
- end
-
- describe '.access_token' do
- context 'when OMNIBUS_GITLAB_PROJECT_ACCESS_TOKEN is set' do
- let(:omnibus_gitlab_project_access_token) { 'omnibus_gitlab_project_access_token' }
-
- before do
- stub_env('OMNIBUS_GITLAB_PROJECT_ACCESS_TOKEN', omnibus_gitlab_project_access_token)
- end
-
- it 'returns the omnibus-specific access token' do
- expect(described_class.access_token).to eq(omnibus_gitlab_project_access_token)
- end
- end
-
- context 'when OMNIBUS_GITLAB_PROJECT_ACCESS_TOKEN is not set' do
- before do
- stub_env('OMNIBUS_GITLAB_PROJECT_ACCESS_TOKEN', nil)
- end
-
- it 'returns the default access token' do
- expect(described_class.access_token).to eq(Trigger::Base.access_token)
- end
- end
- end
-
- describe '#invoke!' do
- let(:downstream_project_path) { 'gitlab-org/build/omnibus-gitlab-mirror' }
- let(:ref) { 'master' }
-
- let(:env) do
- super().merge(
- 'QA_IMAGE' => 'qa_image',
- 'GITLAB_QA_OPTIONS' => 'gitlab_qa_options',
- 'QA_TESTS' => 'qa_tests',
- 'ALLURE_JOB_NAME' => 'allure_job_name'
- )
- end
-
- describe '#downstream_project_path' do
- context 'when OMNIBUS_PROJECT_PATH is set' do
- let(:downstream_project_path) { 'omnibus_project_path' }
-
- before do
- stub_env('OMNIBUS_PROJECT_PATH', downstream_project_path)
- end
-
- it 'triggers the pipeline on the correct project' do
- expect_run_trigger_with_params
-
- subject.invoke!
- end
- end
- end
-
- describe '#ref' do
- context 'when OMNIBUS_BRANCH is set' do
- let(:ref) { 'omnibus_branch' }
-
- before do
- stub_env('OMNIBUS_BRANCH', ref)
- end
-
- it 'triggers the pipeline on the correct ref' do
- expect_run_trigger_with_params
-
- subject.invoke!
- end
- end
- end
-
- context 'when CI_COMMIT_REF_NAME is a stable branch' do
- let(:ref) { '14-10-stable' }
-
- before do
- stub_env('CI_COMMIT_REF_NAME', "#{ref}-ee")
- end
-
- it 'triggers the pipeline on the correct ref' do
- expect_run_trigger_with_params
-
- subject.invoke!
- end
- end
- end
- end
-
describe Trigger::CNG do
describe '#variables' do
it 'does not include redundant variables' do
@@ -496,33 +287,13 @@ RSpec.describe Trigger do
end
describe "GITLAB_VERSION" do
- context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is set' do
- before do
- stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', 'ci_merge_request_source_branch_sha')
- end
-
- it 'sets GITLAB_VERSION to CI_MERGE_REQUEST_SOURCE_BRANCH_SHA' do
- expect(subject.variables['GITLAB_VERSION']).to eq('ci_merge_request_source_branch_sha')
- end
- end
-
- context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is set as empty' do
+ context 'when CI_COMMIT_SHA is set' do
before do
- stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', '')
+ stub_env('CI_COMMIT_SHA', 'ci_commit_sha')
end
it 'sets GITLAB_VERSION to CI_COMMIT_SHA' do
- expect(subject.variables['GITLAB_VERSION']).to eq(env['CI_COMMIT_SHA'])
- end
- end
-
- context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is not set' do
- before do
- stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', nil)
- end
-
- it 'sets GITLAB_VERSION to CI_COMMIT_SHA' do
- expect(subject.variables['GITLAB_VERSION']).to eq(env['CI_COMMIT_SHA'])
+ expect(subject.variables['GITLAB_VERSION']).to eq('ci_commit_sha')
end
end
end
@@ -560,10 +331,9 @@ RSpec.describe Trigger do
end
end
- context 'when CI_COMMIT_TAG and CI_MERGE_REQUEST_SOURCE_BRANCH_SHA are nil' do
+ context 'when CI_COMMIT_TAG is nil' do
before do
stub_env('CI_COMMIT_TAG', nil)
- stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', nil)
end
it 'sets GITLAB_ASSETS_TAG to CI_COMMIT_SHA' do
@@ -841,35 +611,33 @@ RSpec.describe Trigger do
expect(subject.variables).to include('TRIGGERED_USER_LOGIN' => env['GITLAB_USER_LOGIN'])
end
- describe "GITLAB_COMMIT_SHA" do
- context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is set' do
- before do
- stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', 'ci_merge_request_source_branch_sha')
- end
+ context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is set' do
+ before do
+ stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', 'ci_merge_request_source_branch_sha')
+ end
- it 'sets GITLAB_COMMIT_SHA to ci_merge_request_source_branch_sha' do
- expect(subject.variables['GITLAB_COMMIT_SHA']).to eq('ci_merge_request_source_branch_sha')
- end
+ it 'sets TOP_UPSTREAM_SOURCE_SHA to ci_merge_request_source_branch_sha' do
+ expect(subject.variables['TOP_UPSTREAM_SOURCE_SHA']).to eq('ci_merge_request_source_branch_sha')
end
+ end
- context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is set as empty' do
- before do
- stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', '')
- end
+ context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is set as empty' do
+ before do
+ stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', '')
+ end
- it 'sets GITLAB_COMMIT_SHA to CI_COMMIT_SHA' do
- expect(subject.variables['GITLAB_COMMIT_SHA']).to eq(env['CI_COMMIT_SHA'])
- end
+ it 'sets TOP_UPSTREAM_SOURCE_SHA to CI_COMMIT_SHA' do
+ expect(subject.variables['TOP_UPSTREAM_SOURCE_SHA']).to eq(env['CI_COMMIT_SHA'])
end
+ end
- context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is not set' do
- before do
- stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', nil)
- end
+ context 'when CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is not set' do
+ before do
+ stub_env('CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', nil)
+ end
- it 'sets GITLAB_COMMIT_SHA to CI_COMMIT_SHA' do
- expect(subject.variables['GITLAB_COMMIT_SHA']).to eq(env['CI_COMMIT_SHA'])
- end
+ it 'sets TOP_UPSTREAM_SOURCE_SHA to CI_COMMIT_SHA' do
+ expect(subject.variables['TOP_UPSTREAM_SOURCE_SHA']).to eq(env['CI_COMMIT_SHA'])
end
end
end
diff --git a/spec/serializers/access_token_entity_base_spec.rb b/spec/serializers/access_token_entity_base_spec.rb
new file mode 100644
index 00000000000..e14a07a346a
--- /dev/null
+++ b/spec/serializers/access_token_entity_base_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe AccessTokenEntityBase do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:token) { create(:personal_access_token, user: user, expires_at: nil) }
+
+ subject(:json) { described_class.new(token).as_json }
+
+ it 'has the correct attributes' do
+ expect(json).to(
+ include(
+ id: token.id,
+ name: token.name,
+ revoked: false,
+ created_at: token.created_at,
+ scopes: token.scopes,
+ expires_at: nil,
+ expired: false,
+ expires_soon: false
+ ))
+
+ expect(json).not_to include(:token)
+ end
+end
diff --git a/spec/serializers/ci/dag_pipeline_entity_spec.rb b/spec/serializers/ci/dag_pipeline_entity_spec.rb
index 548fd247743..a8ac76d800f 100644
--- a/spec/serializers/ci/dag_pipeline_entity_spec.rb
+++ b/spec/serializers/ci/dag_pipeline_entity_spec.rb
@@ -5,7 +5,8 @@ require 'spec_helper'
RSpec.describe Ci::DagPipelineEntity do
let_it_be(:request) { double(:request) }
- let(:pipeline) { create(:ci_pipeline) }
+ let_it_be(:pipeline) { create(:ci_pipeline) }
+
let(:entity) { described_class.new(pipeline, request: request) }
describe '#as_json' do
@@ -28,9 +29,13 @@ RSpec.describe Ci::DagPipelineEntity do
end
context 'when pipeline has jobs' do
- let!(:build_job) { create(:ci_build, stage: 'build', pipeline: pipeline) }
- let!(:test_job) { create(:ci_build, stage: 'test', pipeline: pipeline) }
- let!(:deploy_job) { create(:ci_build, stage: 'deploy', pipeline: pipeline) }
+ let_it_be(:build_stage) { create(:ci_stage, name: 'build', pipeline: pipeline) }
+ let_it_be(:test_stage) { create(:ci_stage, name: 'test', pipeline: pipeline) }
+ let_it_be(:deploy_stage) { create(:ci_stage, name: 'deploy', pipeline: pipeline) }
+
+ let!(:build_job) { create(:ci_build, ci_stage: build_stage, pipeline: pipeline) }
+ let!(:test_job) { create(:ci_build, ci_stage: test_stage, pipeline: pipeline) }
+ let!(:deploy_job) { create(:ci_build, ci_stage: deploy_stage, pipeline: pipeline) }
it 'contains 3 stages' do
stages = subject[:stages]
@@ -47,28 +52,31 @@ RSpec.describe Ci::DagPipelineEntity do
let!(:stage_test) { create(:ci_stage, name: 'test', position: 2, pipeline: pipeline) }
let!(:stage_deploy) { create(:ci_stage, name: 'deploy', position: 3, pipeline: pipeline) }
- let!(:job_build_1) { create(:ci_build, name: 'build 1', stage: 'build', pipeline: pipeline) }
- let!(:job_build_2) { create(:ci_build, name: 'build 2', stage: 'build', pipeline: pipeline) }
- let!(:commit_status) { create(:generic_commit_status, stage: 'build', pipeline: pipeline) }
+ let!(:job_build_1) { create(:ci_build, name: 'build 1', ci_stage: stage_build, pipeline: pipeline) }
+ let!(:job_build_2) { create(:ci_build, name: 'build 2', ci_stage: stage_build, pipeline: pipeline) }
+ let!(:commit_status) { create(:generic_commit_status, ci_stage: stage_build, pipeline: pipeline) }
- let!(:job_rspec_1) { create(:ci_build, name: 'rspec 1/2', stage: 'test', pipeline: pipeline) }
- let!(:job_rspec_2) { create(:ci_build, name: 'rspec 2/2', stage: 'test', pipeline: pipeline) }
+ let!(:job_rspec_1) { create(:ci_build, name: 'rspec 1/2', ci_stage: stage_test, pipeline: pipeline) }
+ let!(:job_rspec_2) { create(:ci_build, name: 'rspec 2/2', ci_stage: stage_test, pipeline: pipeline) }
let!(:job_jest) do
- create(:ci_build, name: 'jest', stage: 'test', scheduling_type: 'dag', pipeline: pipeline).tap do |job|
+ create(:ci_build, name: 'jest', ci_stage: stage_test, scheduling_type: 'dag', pipeline: pipeline)
+ .tap do |job|
create(:ci_build_need, name: 'build 1', build: job)
end
end
let!(:job_deploy_ruby) do
- create(:ci_build, name: 'deploy_ruby', stage: 'deploy', scheduling_type: 'dag', pipeline: pipeline).tap do |job|
+ create(:ci_build, name: 'deploy_ruby', ci_stage: stage_deploy, scheduling_type: 'dag', pipeline: pipeline)
+ .tap do |job|
create(:ci_build_need, name: 'rspec 1/2', build: job)
create(:ci_build_need, name: 'rspec 2/2', build: job)
end
end
let!(:job_deploy_js) do
- create(:ci_build, name: 'deploy_js', stage: 'deploy', scheduling_type: 'dag', pipeline: pipeline).tap do |job|
+ create(:ci_build, name: 'deploy_js', ci_stage: stage_deploy, scheduling_type: 'dag', pipeline: pipeline)
+ .tap do |job|
create(:ci_build_need, name: 'jest', build: job)
end
end
diff --git a/spec/serializers/ci/lint/job_entity_spec.rb b/spec/serializers/ci/lint/job_entity_spec.rb
index 2ef86cfd004..e1477612ad5 100644
--- a/spec/serializers/ci/lint/job_entity_spec.rb
+++ b/spec/serializers/ci/lint/job_entity_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe Ci::Lint::JobEntity, :aggregate_failures do
stage: 'test',
before_script: ['bundle install', 'bundle exec rake db:create'],
script: ["rake spec"],
- after_script: ["rake spec"],
+ after_script: ["rake spec"],
tag_list: %w[ruby postgres],
environment: { name: 'hello', url: 'world' },
when: 'on_success',
diff --git a/spec/serializers/cluster_entity_spec.rb b/spec/serializers/cluster_entity_spec.rb
index 7c4c146575d..2de27deeffe 100644
--- a/spec/serializers/cluster_entity_spec.rb
+++ b/spec/serializers/cluster_entity_spec.rb
@@ -45,7 +45,7 @@ RSpec.describe ClusterEntity do
context 'when no application has been installed' do
let(:cluster) { create(:cluster, :instance) }
- subject { described_class.new(cluster, request: request).as_json[:applications]}
+ subject { described_class.new(cluster, request: request).as_json[:applications] }
it 'contains helm as not_installable' do
expect(subject).not_to be_empty
diff --git a/spec/serializers/container_repository_entity_spec.rb b/spec/serializers/container_repository_entity_spec.rb
index 9ea00bc79e1..00e6a26d0be 100644
--- a/spec/serializers/container_repository_entity_spec.rb
+++ b/spec/serializers/container_repository_entity_spec.rb
@@ -14,8 +14,7 @@ RSpec.describe ContainerRepositoryEntity do
before do
stub_container_registry_config(enabled: true)
- stub_container_registry_tags(repository: :any,
- tags: %w[stable latest])
+ stub_container_registry_tags(repository: :any, tags: %w[stable latest])
allow(request).to receive(:project).and_return(project)
allow(request).to receive(:current_user).and_return(user)
end
diff --git a/spec/serializers/deployment_entity_spec.rb b/spec/serializers/deployment_entity_spec.rb
index a017f7523e9..433ce344680 100644
--- a/spec/serializers/deployment_entity_spec.rb
+++ b/spec/serializers/deployment_entity_spec.rb
@@ -61,8 +61,7 @@ RSpec.describe DeploymentEntity do
context 'when the pipeline has another manual action' do
let!(:other_build) do
- create(:ci_build, :manual, name: 'another deploy',
- pipeline: pipeline, environment: build.environment)
+ create(:ci_build, :manual, name: 'another deploy', pipeline: pipeline, environment: build.environment)
end
let!(:other_deployment) { create(:deployment, deployable: build) }
diff --git a/spec/serializers/group_access_token_entity_spec.rb b/spec/serializers/group_access_token_entity_spec.rb
index 39b587c7df7..05609dc3c7a 100644
--- a/spec/serializers/group_access_token_entity_spec.rb
+++ b/spec/serializers/group_access_token_entity_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe GroupAccessTokenEntity do
scopes: token.scopes,
user_id: token.user_id,
revoke_path: expected_revoke_path,
- access_level: ::Gitlab::Access::DEVELOPER
+ role: 'Developer'
))
expect(json).not_to include(:token)
@@ -48,7 +48,7 @@ RSpec.describe GroupAccessTokenEntity do
scopes: token.scopes,
user_id: token.user_id,
revoke_path: expected_revoke_path,
- access_level: nil
+ role: nil
))
expect(json).not_to include(:token)
diff --git a/spec/serializers/impersonation_access_token_entity_spec.rb b/spec/serializers/impersonation_access_token_entity_spec.rb
new file mode 100644
index 00000000000..e8517779c0d
--- /dev/null
+++ b/spec/serializers/impersonation_access_token_entity_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe ImpersonationAccessTokenEntity do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:token) { create(:personal_access_token, :impersonation, user: user) }
+
+ subject(:json) { described_class.new(token).as_json }
+
+ it 'has the correct attributes' do
+ expected_revoke_path = Gitlab::Routing.url_helpers
+ .revoke_admin_user_impersonation_token_path(
+ { user_id: user, id: token })
+
+ expect(json).to(
+ include(
+ id: token.id,
+ name: token.name,
+ scopes: token.scopes,
+ user_id: token.user_id,
+ revoke_path: expected_revoke_path
+ ))
+
+ expect(json).not_to include(:token)
+ end
+end
diff --git a/spec/serializers/impersonation_access_token_serializer_spec.rb b/spec/serializers/impersonation_access_token_serializer_spec.rb
new file mode 100644
index 00000000000..0c8cebb94b1
--- /dev/null
+++ b/spec/serializers/impersonation_access_token_serializer_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe ImpersonationAccessTokenSerializer do
+ subject(:serializer) { described_class.new }
+
+ describe '#represent' do
+ it 'can render a single token' do
+ token = create(:personal_access_token)
+
+ expect(serializer.represent(token)).to be_kind_of(Hash)
+ end
+
+ it 'can render a collection of tokens' do
+ tokens = create_list(:personal_access_token, 2)
+
+ expect(serializer.represent(tokens)).to be_kind_of(Array)
+ end
+ end
+end
diff --git a/spec/serializers/import/provider_repo_serializer_spec.rb b/spec/serializers/import/provider_repo_serializer_spec.rb
index 430bad151d3..905685c75e3 100644
--- a/spec/serializers/import/provider_repo_serializer_spec.rb
+++ b/spec/serializers/import/provider_repo_serializer_spec.rb
@@ -23,7 +23,7 @@ RSpec.describe Import::ProviderRepoSerializer do
end
it 'raises an error if invalid provider supplied' do
- expect { described_class.new.represent({}, { provider: :invalid })}.to raise_error { NotImplementedError }
+ expect { described_class.new.represent({}, { provider: :invalid }) }.to raise_error { NotImplementedError }
end
end
end
diff --git a/spec/serializers/member_user_entity_spec.rb b/spec/serializers/member_user_entity_spec.rb
index 4dd6848c47b..638347738f2 100644
--- a/spec/serializers/member_user_entity_spec.rb
+++ b/spec/serializers/member_user_entity_spec.rb
@@ -27,6 +27,12 @@ RSpec.describe MemberUserEntity do
expect(entity_hash[:blocked]).to be(true)
end
+ it 'correctly exposes `is_bot`' do
+ allow(user).to receive(:bot?).and_return(true)
+
+ expect(entity_hash[:is_bot]).to be(true)
+ end
+
it 'does not expose `two_factor_enabled` by default' do
expect(entity_hash[:two_factor_enabled]).to be(nil)
end
diff --git a/spec/serializers/merge_request_metrics_helper_spec.rb b/spec/serializers/merge_request_metrics_helper_spec.rb
index 8f683df1faa..ec764bf7853 100644
--- a/spec/serializers/merge_request_metrics_helper_spec.rb
+++ b/spec/serializers/merge_request_metrics_helper_spec.rb
@@ -57,9 +57,9 @@ RSpec.describe MergeRequestMetricsHelper do
expect(MergeRequest::Metrics).to receive(:new)
.with(latest_closed_at: closed_event&.updated_at,
- latest_closed_by: closed_event&.author,
- merged_at: merge_event&.updated_at,
- merged_by: merge_event&.author)
+ latest_closed_by: closed_event&.author,
+ merged_at: merge_event&.updated_at,
+ merged_by: merge_event&.author)
.and_call_original
subject
diff --git a/spec/serializers/pipeline_details_entity_spec.rb b/spec/serializers/pipeline_details_entity_spec.rb
index 67f8860ed4a..ca38721cc7f 100644
--- a/spec/serializers/pipeline_details_entity_spec.rb
+++ b/spec/serializers/pipeline_details_entity_spec.rb
@@ -104,7 +104,7 @@ RSpec.describe PipelineDetailsEntity do
let(:pipeline) { create(:ci_empty_pipeline) }
before do
- create(:generic_commit_status, pipeline: pipeline)
+ create(:commit_status, pipeline: pipeline)
end
it 'contains stages' do
diff --git a/spec/serializers/project_access_token_entity_spec.rb b/spec/serializers/project_access_token_entity_spec.rb
index 616aa45e9d5..4b5b4d4d77d 100644
--- a/spec/serializers/project_access_token_entity_spec.rb
+++ b/spec/serializers/project_access_token_entity_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe ProjectAccessTokenEntity do
scopes: token.scopes,
user_id: token.user_id,
revoke_path: expected_revoke_path,
- access_level: ::Gitlab::Access::DEVELOPER
+ role: 'Developer'
))
expect(json).not_to include(:token)
@@ -52,7 +52,7 @@ RSpec.describe ProjectAccessTokenEntity do
scopes: token.scopes,
user_id: token.user_id,
revoke_path: expected_revoke_path,
- access_level: nil
+ role: nil
))
expect(json).not_to include(:token)
diff --git a/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb b/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb
index 6f28f892f00..73d185283b6 100644
--- a/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb
+++ b/spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb
@@ -217,18 +217,20 @@ RSpec.describe AutoMerge::MergeWhenPipelineSucceedsService do
let(:ref) { mr_merge_if_green_enabled.source_branch }
let(:sha) { project.commit(ref).id }
+ let(:build_stage) { create(:ci_stage, name: 'build', pipeline: pipeline) }
+
let(:pipeline) do
create(:ci_empty_pipeline, ref: ref, sha: sha, project: project)
end
let!(:build) do
create(:ci_build, :created, pipeline: pipeline, ref: ref,
- name: 'build', stage: 'build')
+ name: 'build', ci_stage: build_stage )
end
let!(:test) do
create(:ci_build, :created, pipeline: pipeline, ref: ref,
- name: 'test', stage: 'test')
+ name: 'test')
end
before do
diff --git a/spec/services/bulk_imports/file_download_service_spec.rb b/spec/services/bulk_imports/file_download_service_spec.rb
index 81229cc8431..ec9cc719e24 100644
--- a/spec/services/bulk_imports/file_download_service_spec.rb
+++ b/spec/services/bulk_imports/file_download_service_spec.rb
@@ -277,7 +277,7 @@ RSpec.describe BulkImports::FileDownloadService do
let_it_be(:content_disposition) { 'filename="../../xxx.b"' }
before do
- stub_const("#{described_class}::FILENAME_SIZE_LIMIT", 1)
+ stub_const('BulkImports::FileDownloads::FilenameFetch::FILENAME_SIZE_LIMIT', 1)
end
it 'raises an error when the filename is not provided in the request header' do
diff --git a/spec/services/bulk_imports/tree_export_service_spec.rb b/spec/services/bulk_imports/tree_export_service_spec.rb
index ffb81fe2b5f..6e26cb6dc2b 100644
--- a/spec/services/bulk_imports/tree_export_service_spec.rb
+++ b/spec/services/bulk_imports/tree_export_service_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe BulkImports::TreeExportService do
let(:relation) { 'issues' }
- subject(:service) { described_class.new(project, export_path, relation) }
+ subject(:service) { described_class.new(project, export_path, relation, project.owner) }
describe '#execute' do
it 'executes export service and archives exported data' do
@@ -21,7 +21,7 @@ RSpec.describe BulkImports::TreeExportService do
context 'when unsupported relation is passed' do
it 'raises an error' do
- service = described_class.new(project, export_path, 'unsupported')
+ service = described_class.new(project, export_path, 'unsupported', project.owner)
expect { service.execute }.to raise_error(BulkImports::Error, 'Unsupported relation export type')
end
diff --git a/spec/services/ci/after_requeue_job_service_spec.rb b/spec/services/ci/after_requeue_job_service_spec.rb
index fb67ee18fb2..1f692bdb71a 100644
--- a/spec/services/ci/after_requeue_job_service_spec.rb
+++ b/spec/services/ci/after_requeue_job_service_spec.rb
@@ -112,7 +112,7 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
check_jobs_statuses(
a1: 'pending',
a2: 'created',
- a3: 'skipped',
+ a3: 'created',
b1: 'success',
b2: 'created',
c1: 'created',
@@ -120,6 +120,26 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
)
end
+ context 'when the FF ci_requeue_with_dag_object_hierarchy is disabled' do
+ before do
+ stub_feature_flags(ci_requeue_with_dag_object_hierarchy: false)
+ end
+
+ it 'marks subsequent skipped jobs as processable but leaves a3 created' do
+ execute_after_requeue_service(a1)
+
+ check_jobs_statuses(
+ a1: 'pending',
+ a2: 'created',
+ a3: 'skipped',
+ b1: 'success',
+ b2: 'created',
+ c1: 'created',
+ c2: 'created'
+ )
+ end
+ 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) }
@@ -140,7 +160,7 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
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' => 'skipped', 'user_id' => user.id, 'needs' => ['a2'] },
+ { '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'] },
@@ -237,6 +257,79 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
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
+
+ context 'when the FF ci_requeue_with_dag_object_hierarchy is disabled' do
+ before do
+ stub_feature_flags(ci_requeue_with_dag_object_hierarchy: false)
+ end
+
+ it 'marks the next subsequent skipped job as processable but leaves c skipped' do
+ execute_after_requeue_service(a)
+
+ check_jobs_statuses(
+ a: 'pending',
+ b: 'created',
+ c: 'skipped'
+ )
+ end
+ end
+ end
+
private
def find_job(name)
diff --git a/spec/services/ci/archive_trace_service_spec.rb b/spec/services/ci/archive_trace_service_spec.rb
index bf2e5302d2e..359ea0699e4 100644
--- a/spec/services/ci/archive_trace_service_spec.rb
+++ b/spec/services/ci/archive_trace_service_spec.rb
@@ -17,21 +17,12 @@ RSpec.describe Ci::ArchiveTraceService, '#execute' do
context 'integration hooks' do
it do
- stub_feature_flags(datadog_integration_logs_collection: [job.project])
-
expect(job.project).to receive(:execute_integrations) do |data, hook_type|
expect(data).to eq Gitlab::DataBuilder::ArchiveTrace.build(job)
expect(hook_type).to eq :archive_trace_hooks
end
expect { subject }.not_to raise_error
end
-
- it 'with feature flag disabled' do
- stub_feature_flags(datadog_integration_logs_collection: false)
-
- expect(job.project).not_to receive(:execute_integrations)
- expect { subject }.not_to raise_error
- end
end
context 'when trace is already archived' do
diff --git a/spec/services/ci/build_erase_service_spec.rb b/spec/services/ci/build_erase_service_spec.rb
new file mode 100644
index 00000000000..e750a163621
--- /dev/null
+++ b/spec/services/ci/build_erase_service_spec.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::BuildEraseService do
+ let_it_be(:user) { user }
+
+ let(:build) { create(:ci_build, :artifacts, :trace_artifact, artifacts_expire_at: 100.days.from_now) }
+
+ subject(:service) { described_class.new(build, user) }
+
+ describe '#execute' do
+ context 'when build is erasable' do
+ before do
+ allow(build).to receive(:erasable?).and_return(true)
+ end
+
+ it 'is successful' do
+ result = service.execute
+
+ expect(result).to be_success
+ end
+
+ it 'erases artifacts' do
+ service.execute
+
+ expect(build.artifacts_file).not_to be_present
+ expect(build.artifacts_metadata).not_to be_present
+ end
+
+ it 'erases trace' do
+ service.execute
+
+ expect(build.trace).not_to exist
+ end
+
+ it 'records erasure detail' do
+ freeze_time do
+ service.execute
+
+ expect(build.erased_by).to eq(user)
+ expect(build.erased_at).to eq(Time.current)
+ expect(build.artifacts_expire_at).to be_nil
+ end
+ end
+
+ context 'when project is undergoing statistics refresh' do
+ before do
+ allow(build.project).to receive(:refreshing_build_artifacts_size?).and_return(true)
+ end
+
+ it 'logs a warning' do
+ expect(Gitlab::ProjectStatsRefreshConflictsLogger)
+ .to receive(:warn_artifact_deletion_during_stats_refresh)
+ .with(method: 'Ci::BuildEraseService#execute', project_id: build.project_id)
+
+ service.execute
+ end
+ end
+ end
+
+ context 'when build is not erasable' do
+ before do
+ allow(build).to receive(:erasable?).and_return(false)
+ end
+
+ it 'is not successful' do
+ result = service.execute
+
+ expect(result).to be_error
+ expect(result.http_status).to eq(:unprocessable_entity)
+ end
+
+ it 'does not erase artifacts' do
+ service.execute
+
+ expect(build.artifacts_file).to be_present
+ expect(build.artifacts_metadata).to be_present
+ end
+
+ it 'does not erase trace' do
+ service.execute
+
+ expect(build.trace).to exist
+ end
+ end
+ end
+end
diff --git a/spec/services/ci/compare_reports_base_service_spec.rb b/spec/services/ci/compare_reports_base_service_spec.rb
index 9ce58c4972d..20d8cd37553 100644
--- a/spec/services/ci/compare_reports_base_service_spec.rb
+++ b/spec/services/ci/compare_reports_base_service_spec.rb
@@ -6,13 +6,13 @@ RSpec.describe Ci::CompareReportsBaseService do
let(:service) { described_class.new(project) }
let(:project) { create(:project, :repository) }
+ let!(:base_pipeline) { nil }
+ let!(:head_pipeline) { create(:ci_pipeline, :with_test_reports, project: project) }
+ let!(:key) { service.send(:key, base_pipeline, head_pipeline) }
+
describe '#latest?' do
subject { service.latest?(base_pipeline, head_pipeline, data) }
- let!(:base_pipeline) { nil }
- let!(:head_pipeline) { create(:ci_pipeline, :with_test_reports, project: project) }
- let!(:key) { service.send(:key, base_pipeline, head_pipeline) }
-
context 'when cache key is latest' do
let(:data) { { key: key } }
@@ -35,4 +35,14 @@ RSpec.describe Ci::CompareReportsBaseService do
it { is_expected.to be_falsy }
end
end
+
+ describe '#execute' do
+ context 'when base_pipeline is running' do
+ let!(:base_pipeline) { create(:ci_pipeline, :running, project: project) }
+
+ subject { service.execute(base_pipeline, head_pipeline) }
+
+ it { is_expected.to eq(status: :parsing, key: key) }
+ 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 11fb564b843..9c02c5218f1 100644
--- a/spec/services/ci/create_downstream_pipeline_service_spec.rb
+++ b/spec/services/ci/create_downstream_pipeline_service_spec.rb
@@ -5,9 +5,12 @@ require 'spec_helper'
RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
include Ci::SourcePipelineHelpers
- let_it_be(:user) { create(:user) }
+ # Using let_it_be on user and projects for these specs can cause
+ # spec-ordering failures due to the project-based permissions
+ # associating them. They should be recreated every time.
+ let(:user) { create(:user) }
let(:upstream_project) { create(:project, :repository) }
- let_it_be(:downstream_project, refind: true) { create(:project, :repository) }
+ let(:downstream_project) { create(:project, :repository) }
let!(:upstream_pipeline) do
create(:ci_pipeline, :running, project: upstream_project)
@@ -440,10 +443,7 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
let!(:trigger_project_bridge) do
create(
- :ci_bridge, status: :pending,
- user: user,
- options: trigger_downstream_project,
- pipeline: child_pipeline
+ :ci_bridge, status: :pending, user: user, options: trigger_downstream_project, pipeline: child_pipeline
)
end
@@ -819,5 +819,60 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do
end
end
end
+
+ context 'when a downstream pipeline has sibling pipelines' do
+ it_behaves_like 'logs downstream pipeline creation' do
+ let(:expected_root_pipeline) { upstream_pipeline }
+ let(:expected_downstream_relationship) { :multi_project }
+
+ # New downstream, plus upstream, plus two children of upstream created below
+ let(:expected_hierarchy_size) { 4 }
+
+ before do
+ create_list(:ci_pipeline, 2, child_of: upstream_pipeline)
+ end
+ end
+ end
+
+ context 'when the pipeline tree is too large' do
+ let_it_be(:parent) { create(:ci_pipeline) }
+ 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(:bridge) do
+ create(:ci_bridge, status: :pending, user: user, options: trigger, pipeline: child)
+ 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
+
+ context 'with :ci_limit_complete_hierarchy_size disabled' do
+ before do
+ stub_feature_flags(ci_limit_complete_hierarchy_size: false)
+ end
+
+ it 'creates a new pipeline' do
+ expect { subject }.to change { Ci::Pipeline.count }.by(1)
+ end
+
+ it 'marks the bridge job as successful' do
+ subject
+
+ expect(bridge.reload).to be_success
+ end
+ end
+ end
end
end
diff --git a/spec/services/ci/create_pipeline_service/artifacts_spec.rb b/spec/services/ci/create_pipeline_service/artifacts_spec.rb
index 1ec30d68666..e5e405492a0 100644
--- a/spec/services/ci/create_pipeline_service/artifacts_spec.rb
+++ b/spec/services/ci/create_pipeline_service/artifacts_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/cache_spec.rb b/spec/services/ci/create_pipeline_service/cache_spec.rb
index fe777bc50d9..82c3d374636 100644
--- a/spec/services/ci/create_pipeline_service/cache_spec.rb
+++ b/spec/services/ci/create_pipeline_service/cache_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
context 'cache' do
let(:project) { create(:project, :custom_repo, files: files) }
let(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb b/spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb
index a920b90b97d..0ebcecdd6e6 100644
--- a/spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb
+++ b/spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
describe 'creation errors and warnings' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
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 e1d60ed57ef..74d3534eb45 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
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, '#execute' do
+RSpec.describe Ci::CreatePipelineService, '#execute', :yaml_processor_feature_flag_corectness do
let_it_be(:group) { create(:group, name: 'my-organization') }
let(:upstream_project) { create(:project, :repository, name: 'upstream', group: group) }
diff --git a/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb b/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb
index a0cbf14d936..dafa227c4c8 100644
--- a/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb
+++ b/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/custom_yaml_tags_spec.rb b/spec/services/ci/create_pipeline_service/custom_yaml_tags_spec.rb
index 716a929830e..3b042f05fc0 100644
--- a/spec/services/ci/create_pipeline_service/custom_yaml_tags_spec.rb
+++ b/spec/services/ci/create_pipeline_service/custom_yaml_tags_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
describe '!reference tags' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/dry_run_spec.rb b/spec/services/ci/create_pipeline_service/dry_run_spec.rb
index 9a7e97fb12b..de1ed251c82 100644
--- a/spec/services/ci/create_pipeline_service/dry_run_spec.rb
+++ b/spec/services/ci/create_pipeline_service/dry_run_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/environment_spec.rb b/spec/services/ci/create_pipeline_service/environment_spec.rb
index 43b5220334c..438cb6ac895 100644
--- a/spec/services/ci/create_pipeline_service/environment_spec.rb
+++ b/spec/services/ci/create_pipeline_service/environment_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:developer) { create(:user) }
@@ -45,5 +45,51 @@ RSpec.describe Ci::CreatePipelineService do
end
end
end
+
+ context 'when variables are dependent on stage name' do
+ let(:config) do
+ <<~YAML
+ deploy-review-app-1:
+ stage: deploy
+ environment: 'test/$CI_JOB_STAGE/1'
+ script:
+ - echo $SCOPED_VARIABLE
+ rules:
+ - if: $SCOPED_VARIABLE == 'my-value-1'
+
+ deploy-review-app-2:
+ stage: deploy
+ script:
+ - echo $SCOPED_VARIABLE
+ environment: 'test/$CI_JOB_STAGE/2'
+ rules:
+ - if: $SCOPED_VARIABLE == 'my-value-2'
+ YAML
+ end
+
+ before do
+ create(:ci_variable, key: 'SCOPED_VARIABLE', value: 'my-value-1', environment_scope: '*', project: project)
+ create(:ci_variable,
+ key: 'SCOPED_VARIABLE',
+ value: 'my-value-2',
+ environment_scope: 'test/deploy/*',
+ project: project
+ )
+ stub_ci_pipeline_yaml_file(config)
+ end
+
+ it 'creates the pipeline successfully', :aggregate_failures do
+ pipeline = subject
+ build = pipeline.builds.first
+
+ expect(pipeline).to be_created_successfully
+ expect(Environment.find_by_name('test/deploy/2')).to be_persisted
+ expect(pipeline.builds.size).to eq(1)
+ expect(build.persisted_environment.name).to eq('test/deploy/2')
+ expect(build.name).to eq('deploy-review-app-2')
+ expect(build.environment).to eq('test/$CI_JOB_STAGE/2')
+ expect(build.variables.to_hash['SCOPED_VARIABLE']).to eq('my-value-2')
+ end
+ end
end
end
diff --git a/spec/services/ci/create_pipeline_service/evaluate_runner_tags_spec.rb b/spec/services/ci/create_pipeline_service/evaluate_runner_tags_spec.rb
index 7c698242921..e84726d31f6 100644
--- a/spec/services/ci/create_pipeline_service/evaluate_runner_tags_spec.rb
+++ b/spec/services/ci/create_pipeline_service/evaluate_runner_tags_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
let_it_be(:group) { create(:group, :private) }
let_it_be(:group_variable) { create(:ci_group_variable, group: group, key: 'RUNNER_TAG', value: 'group') }
let_it_be(:project) { create(:project, :repository, group: group) }
diff --git a/spec/services/ci/create_pipeline_service/include_spec.rb b/spec/services/ci/create_pipeline_service/include_spec.rb
index 849eb5885f6..67d8530525a 100644
--- a/spec/services/ci/create_pipeline_service/include_spec.rb
+++ b/spec/services/ci/create_pipeline_service/include_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
context 'include:' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/logger_spec.rb b/spec/services/ci/create_pipeline_service/logger_spec.rb
index 53e5f0dd7f2..2be23802757 100644
--- a/spec/services/ci/create_pipeline_service/logger_spec.rb
+++ b/spec/services/ci/create_pipeline_service/logger_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
context 'pipeline logger' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
@@ -19,9 +19,9 @@ RSpec.describe Ci::CreatePipelineService do
let(:counters) do
{
'count' => a_kind_of(Numeric),
- 'avg' => a_kind_of(Numeric),
- 'max' => a_kind_of(Numeric),
- 'min' => a_kind_of(Numeric)
+ 'avg' => a_kind_of(Numeric),
+ 'max' => a_kind_of(Numeric),
+ 'min' => a_kind_of(Numeric)
}
end
diff --git a/spec/services/ci/create_pipeline_service/merge_requests_spec.rb b/spec/services/ci/create_pipeline_service/merge_requests_spec.rb
index de19ef363fb..80f48451e5c 100644
--- a/spec/services/ci/create_pipeline_service/merge_requests_spec.rb
+++ b/spec/services/ci/create_pipeline_service/merge_requests_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
context 'merge requests handling' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/needs_spec.rb b/spec/services/ci/create_pipeline_service/needs_spec.rb
index abd17ccdd6a..38e330316ea 100644
--- a/spec/services/ci/create_pipeline_service/needs_spec.rb
+++ b/spec/services/ci/create_pipeline_service/needs_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
context 'needs' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/parallel_spec.rb b/spec/services/ci/create_pipeline_service/parallel_spec.rb
index ae28b74fef5..5ee378a9719 100644
--- a/spec/services/ci/create_pipeline_service/parallel_spec.rb
+++ b/spec/services/ci/create_pipeline_service/parallel_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/parameter_content_spec.rb b/spec/services/ci/create_pipeline_service/parameter_content_spec.rb
index f593707f460..cae88bb67cf 100644
--- a/spec/services/ci/create_pipeline_service/parameter_content_spec.rb
+++ b/spec/services/ci/create_pipeline_service/parameter_content_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
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 4326fa5533f..513cbbed6cd 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
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, '#execute' do
+RSpec.describe Ci::CreatePipelineService, '#execute', :yaml_processor_feature_flag_corectness do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { create(:user) }
@@ -36,7 +36,7 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do
expect(pipeline.statuses).to match_array [test, bridge]
expect(bridge.options).to eq(expected_bridge_options)
expect(bridge.yaml_variables)
- .to include(key: 'CROSS', value: 'downstream', public: true)
+ .to include(key: 'CROSS', value: 'downstream')
end
end
diff --git a/spec/services/ci/create_pipeline_service/partitioning_spec.rb b/spec/services/ci/create_pipeline_service/partitioning_spec.rb
new file mode 100644
index 00000000000..43fbb74ede4
--- /dev/null
+++ b/spec/services/ci/create_pipeline_service/partitioning_spec.rb
@@ -0,0 +1,146 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness, :aggregate_failures 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(:config) do
+ <<-YAML
+ stages:
+ - build
+ - test
+ - deploy
+
+ build:
+ stage: build
+ script: make build
+
+ test:
+ stage: test
+ trigger:
+ include: child.yml
+
+ deploy:
+ stage: deploy
+ script: make deploy
+ environment: review/$CI_JOB_NAME
+ YAML
+ end
+
+ let(:pipeline) { service.execute(:push).payload }
+ let(:current_partition_id) { 123 }
+
+ before do
+ stub_ci_pipeline_yaml_file(config)
+ allow(Ci::Pipeline).to receive(:current_partition_value) { current_partition_id }
+ end
+
+ it 'assigns partition_id to pipeline' do
+ expect(pipeline).to be_created_successfully
+ expect(pipeline.partition_id).to eq(current_partition_id)
+ end
+
+ it 'assigns partition_id to stages' do
+ stage_partition_ids = pipeline.stages.map(&:partition_id).uniq
+
+ expect(stage_partition_ids).to eq([current_partition_id])
+ end
+
+ it 'assigns partition_id to processables' do
+ processables_partition_ids = pipeline.processables.map(&:partition_id).uniq
+
+ expect(processables_partition_ids).to eq([current_partition_id])
+ end
+
+ it 'assigns partition_id to metadata' do
+ metadata_partition_ids = pipeline.processables.map { |job| job.metadata.partition_id }.uniq
+
+ expect(metadata_partition_ids).to eq([current_partition_id])
+ end
+
+ it 'correctly assigns partition and environment' do
+ metadata = find_metadata('deploy')
+
+ expect(metadata.partition_id).to eq(current_partition_id)
+ expect(metadata.expanded_environment_name).to eq('review/deploy')
+ end
+
+ context 'with pipeline variables' do
+ let(:variables_attributes) do
+ [
+ { key: 'SOME_VARIABLE', secret_value: 'SOME_VAL' },
+ { key: 'OTHER_VARIABLE', secret_value: 'OTHER_VAL' }
+ ]
+ end
+
+ let(:service) do
+ described_class.new(
+ project,
+ user,
+ { ref: 'master', variables_attributes: variables_attributes })
+ end
+
+ it 'assigns partition_id to pipeline' do
+ expect(pipeline).to be_created_successfully
+ expect(pipeline.partition_id).to eq(current_partition_id)
+ end
+
+ it 'assigns partition_id to variables' do
+ variables_partition_ids = pipeline.variables.map(&:partition_id).uniq
+
+ expect(pipeline.variables.size).to eq(2)
+ expect(variables_partition_ids).to eq([current_partition_id])
+ end
+ end
+
+ context 'with parent child pipelines' do
+ before do
+ allow(Ci::Pipeline)
+ .to receive(:current_partition_value)
+ .and_return(current_partition_id, 301, 302)
+
+ allow_next_found_instance_of(Ci::Bridge) do |bridge|
+ allow(bridge).to receive(:yaml_for_downstream).and_return(child_config)
+ end
+ end
+
+ let(:config) do
+ <<-YAML
+ test:
+ trigger:
+ include: child.yml
+ YAML
+ end
+
+ let(:child_config) do
+ <<-YAML
+ test:
+ script: make test
+ YAML
+ end
+
+ it 'assigns partition values to child pipelines', :aggregate_failures, :sidekiq_inline do
+ expect(pipeline).to be_created_successfully
+ expect(pipeline.child_pipelines).to all be_created_successfully
+
+ child_partition_ids = pipeline.child_pipelines.map(&:partition_id).uniq
+ child_jobs = CommitStatus.where(commit_id: pipeline.child_pipelines)
+
+ expect(pipeline.partition_id).to eq(current_partition_id)
+ expect(child_partition_ids).to eq([current_partition_id])
+
+ expect(child_jobs).to all be_a(Ci::Build)
+ expect(child_jobs.pluck(:partition_id).uniq).to eq([current_partition_id])
+ end
+ end
+
+ def find_metadata(name)
+ pipeline
+ .processables
+ .find { |job| job.name == name }
+ .metadata
+ end
+end
diff --git a/spec/services/ci/create_pipeline_service/pre_post_stages_spec.rb b/spec/services/ci/create_pipeline_service/pre_post_stages_spec.rb
index c6e69862422..db110bdc608 100644
--- a/spec/services/ci/create_pipeline_service/pre_post_stages_spec.rb
+++ b/spec/services/ci/create_pipeline_service/pre_post_stages_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
describe '.pre/.post stages' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/rate_limit_spec.rb b/spec/services/ci/create_pipeline_service/rate_limit_spec.rb
index 0000296230f..dfa74870341 100644
--- a/spec/services/ci/create_pipeline_service/rate_limit_spec.rb
+++ b/spec/services/ci/create_pipeline_service/rate_limit_spec.rb
@@ -1,7 +1,9 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService, :freeze_time, :clean_gitlab_redis_rate_limiting do
+RSpec.describe Ci::CreatePipelineService, :freeze_time,
+ :clean_gitlab_redis_rate_limiting,
+ :yaml_processor_feature_flag_corectness do
describe 'rate limiting' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
diff --git a/spec/services/ci/create_pipeline_service/rules_spec.rb b/spec/services/ci/create_pipeline_service/rules_spec.rb
index 6e48141226d..fc57ca66d3a 100644
--- a/spec/services/ci/create_pipeline_service/rules_spec.rb
+++ b/spec/services/ci/create_pipeline_service/rules_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
let(:project) { create(:project, :repository) }
let(:user) { project.first_owner }
let(:ref) { 'refs/heads/master' }
diff --git a/spec/services/ci/create_pipeline_service/tags_spec.rb b/spec/services/ci/create_pipeline_service/tags_spec.rb
index 0774f9fff2a..7450df11eac 100644
--- a/spec/services/ci/create_pipeline_service/tags_spec.rb
+++ b/spec/services/ci/create_pipeline_service/tags_spec.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness do
describe 'tags:' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:user) { project.first_owner }
@@ -37,7 +37,7 @@ RSpec.describe Ci::CreatePipelineService do
context 'tags persistence' do
let(:config) do
{
- build: {
+ build: {
script: 'ls',
stage: 'build',
tags: build_tag_list(label: 'build')
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index a9442b0dc68..c2e80316d26 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::CreatePipelineService do
+RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectness, :clean_gitlab_redis_cache do
include ProjectForksHelper
let_it_be_with_refind(:project) { create(:project, :repository) }
@@ -463,7 +463,7 @@ RSpec.describe Ci::CreatePipelineService do
it 'pull it from Auto-DevOps' do
pipeline = execute_service.payload
expect(pipeline).to be_auto_devops_source
- expect(pipeline.builds.map(&:name)).to match_array(%w[brakeman-sast build code_quality container_scanning eslint-sast secret_detection semgrep-sast test])
+ expect(pipeline.builds.map(&:name)).to match_array(%w[brakeman-sast build code_quality container_scanning secret_detection semgrep-sast test])
end
end
diff --git a/spec/services/ci/job_artifacts/create_service_spec.rb b/spec/services/ci/job_artifacts/create_service_spec.rb
index 7b3f67b192f..a2259f9813b 100644
--- a/spec/services/ci/job_artifacts/create_service_spec.rb
+++ b/spec/services/ci/job_artifacts/create_service_spec.rb
@@ -151,9 +151,8 @@ RSpec.describe Ci::JobArtifacts::CreateService do
expect { subject }.not_to change { Ci::JobArtifact.count }
expect(subject).to match(
- a_hash_including(http_status: :bad_request,
- message: 'another artifact of the same type already exists',
- status: :error))
+ a_hash_including(
+ http_status: :bad_request, message: 'another artifact of the same type already exists', status: :error))
end
end
end
@@ -182,6 +181,18 @@ RSpec.describe Ci::JobArtifacts::CreateService do
end
end
+ context 'with job partitioning' do
+ let(:job) { create(:ci_build, project: project, partition_id: 123) }
+
+ it 'sets partition_id on artifacts' do
+ expect { subject }.to change { Ci::JobArtifact.count }
+
+ artifacts_partitions = job.job_artifacts.map(&:partition_id).uniq
+
+ expect(artifacts_partitions).to eq([123])
+ end
+ end
+
shared_examples 'rescues object storage error' do |klass, message, expected_message|
it "handles #{klass}" do
allow_next_instance_of(JobArtifactUploader) do |uploader|
diff --git a/spec/services/ci/job_artifacts/delete_service_spec.rb b/spec/services/ci/job_artifacts/delete_service_spec.rb
new file mode 100644
index 00000000000..62a755eb44a
--- /dev/null
+++ b/spec/services/ci/job_artifacts/delete_service_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::JobArtifacts::DeleteService do
+ let_it_be(:build, reload: true) do
+ create(:ci_build, :artifacts, :trace_artifact, artifacts_expire_at: 100.days.from_now)
+ end
+
+ subject(:service) { described_class.new(build) }
+
+ describe '#execute' do
+ it 'is successful' do
+ result = service.execute
+
+ expect(result).to be_success
+ end
+
+ it 'deletes erasable artifacts' do
+ expect { service.execute }.to change { build.job_artifacts.erasable.count }.from(2).to(0)
+ end
+
+ it 'does not delete trace' do
+ expect { service.execute }.not_to change { build.has_trace? }.from(true)
+ end
+
+ context 'when project is undergoing statistics refresh' do
+ before do
+ allow(build.project).to receive(:refreshing_build_artifacts_size?).and_return(true)
+ end
+
+ it 'logs a warning' do
+ expect(Gitlab::ProjectStatsRefreshConflictsLogger)
+ .to receive(:warn_artifact_deletion_during_stats_refresh)
+ .with(method: 'Ci::JobArtifacts::DeleteService#execute', project_id: build.project_id)
+
+ service.execute
+ end
+ end
+ end
+end
diff --git a/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb b/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb
index 9ca39d4d32e..54d1cacc068 100644
--- a/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb
+++ b/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb
@@ -221,6 +221,15 @@ RSpec.describe Ci::JobArtifacts::DestroyBatchService do
end
context 'with update_stats: false' do
+ let_it_be(:extra_artifact_with_file) do
+ create(:ci_job_artifact, :zip, project: artifact_with_file.project)
+ end
+
+ let(:artifacts) do
+ Ci::JobArtifact.where(id: [artifact_with_file.id, extra_artifact_with_file.id,
+ artifact_without_file.id, trace_artifact.id])
+ end
+
it 'does not update project statistics' do
expect(ProjectStatistics).not_to receive(:increment_statistic)
@@ -230,7 +239,7 @@ RSpec.describe Ci::JobArtifacts::DestroyBatchService do
it 'returns size statistics' do
expected_updates = {
statistics_updates: {
- artifact_with_file.project => -artifact_with_file.file.size,
+ artifact_with_file.project => -(artifact_with_file.file.size + extra_artifact_with_file.file.size),
artifact_without_file.project => 0
}
}
diff --git a/spec/services/ci/job_artifacts/track_artifact_report_service_spec.rb b/spec/services/ci/job_artifacts/track_artifact_report_service_spec.rb
new file mode 100644
index 00000000000..6d9fc4c8e34
--- /dev/null
+++ b/spec/services/ci/job_artifacts/track_artifact_report_service_spec.rb
@@ -0,0 +1,122 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::JobArtifacts::TrackArtifactReportService do
+ describe '#execute', :clean_gitlab_redis_shared_state do
+ let_it_be(:group) { create(:group, :private) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:user1) { create(:user) }
+ let_it_be(:user2) { create(:user) }
+
+ let(:test_event_name) { 'i_testing_test_report_uploaded' }
+ let(:counter) { Gitlab::UsageDataCounters::HLLRedisCounter }
+ let(:start_time) { 1.week.ago }
+ let(:end_time) { 1.week.from_now }
+
+ subject(:track_artifact_report) { described_class.new.execute(pipeline) }
+
+ context 'when pipeline has test reports' do
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project, user: user1) }
+
+ before do
+ 2.times do
+ pipeline.builds << build(:ci_build, :test_reports, pipeline: pipeline, project: pipeline.project)
+ end
+ end
+
+ it 'tracks the event using HLLRedisCounter' do
+ allow(Gitlab::UsageDataCounters::HLLRedisCounter)
+ .to receive(:track_event)
+ .with(test_event_name, values: user1.id)
+ .and_call_original
+
+ expect { track_artifact_report }
+ .to change {
+ counter.unique_events(event_names: test_event_name,
+ start_date: start_time,
+ end_date: end_time)
+ }
+ .by 1
+ end
+ end
+
+ context 'when pipeline does not have test reports' do
+ let_it_be(:pipeline) { create(:ci_empty_pipeline) }
+
+ it 'does not track the event' do
+ track_artifact_report
+
+ expect(Gitlab::UsageDataCounters::HLLRedisCounter)
+ .not_to receive(:track_event)
+ .with(anything, test_event_name)
+ end
+ end
+
+ context 'when a single user started multiple pipelines with test reports' do
+ let_it_be(:pipeline1) { create(:ci_pipeline, :with_test_reports, project: project, user: user1) }
+ let_it_be(:pipeline2) { create(:ci_pipeline, :with_test_reports, project: project, user: user1) }
+
+ it 'tracks all pipelines using HLLRedisCounter by one user_id' do
+ allow(Gitlab::UsageDataCounters::HLLRedisCounter)
+ .to receive(:track_event)
+ .with(test_event_name, values: user1.id)
+ .and_call_original
+
+ allow(Gitlab::UsageDataCounters::HLLRedisCounter)
+ .to receive(:track_event)
+ .with(test_event_name, values: user1.id)
+ .and_call_original
+
+ expect do
+ described_class.new.execute(pipeline1)
+ described_class.new.execute(pipeline2)
+ end
+ .to change {
+ counter.unique_events(event_names: test_event_name,
+ start_date: start_time,
+ end_date: end_time)
+ }
+ .by 1
+ end
+ end
+
+ context 'when multiple users started multiple pipelines with test reports' do
+ let_it_be(:pipeline1) { create(:ci_pipeline, :with_test_reports, project: project, user: user1) }
+ let_it_be(:pipeline2) { create(:ci_pipeline, :with_test_reports, project: project, user: user2) }
+
+ it 'tracks all pipelines using HLLRedisCounter by multiple users' do
+ allow(Gitlab::UsageDataCounters::HLLRedisCounter)
+ .to receive(:track_event)
+ .with(test_event_name, values: user1.id)
+ .and_call_original
+
+ allow(Gitlab::UsageDataCounters::HLLRedisCounter)
+ .to receive(:track_event)
+ .with(test_event_name, values: user1.id)
+ .and_call_original
+
+ allow(Gitlab::UsageDataCounters::HLLRedisCounter)
+ .to receive(:track_event)
+ .with(test_event_name, values: user2.id)
+ .and_call_original
+
+ allow(Gitlab::UsageDataCounters::HLLRedisCounter)
+ .to receive(:track_event)
+ .with(test_event_name, values: user2.id)
+ .and_call_original
+
+ expect do
+ described_class.new.execute(pipeline1)
+ described_class.new.execute(pipeline2)
+ end
+ .to change {
+ counter.unique_events(event_names: test_event_name,
+ start_date: start_time,
+ end_date: end_time)
+ }
+ .by 2
+ end
+ end
+ end
+end
diff --git a/spec/services/ci/job_token_scope/add_project_service_spec.rb b/spec/services/ci/job_token_scope/add_project_service_spec.rb
index ba889465fac..bb6df4268dd 100644
--- a/spec/services/ci/job_token_scope/add_project_service_spec.rb
+++ b/spec/services/ci/job_token_scope/add_project_service_spec.rb
@@ -8,6 +8,14 @@ RSpec.describe Ci::JobTokenScope::AddProjectService do
let_it_be(:target_project) { create(:project) }
let_it_be(:current_user) { create(:user) }
+ shared_examples 'adds project' do |context|
+ it 'adds the project to the scope' do
+ expect do
+ expect(result).to be_success
+ end.to change { Ci::JobToken::ProjectScopeLink.count }.by(1)
+ end
+ end
+
describe '#execute' do
subject(:result) { service.execute(target_project) }
@@ -18,10 +26,14 @@ RSpec.describe Ci::JobTokenScope::AddProjectService do
target_project.add_developer(current_user)
end
- it 'adds the project to the scope' do
- expect do
- expect(result).to be_success
- end.to change { Ci::JobToken::ProjectScopeLink.count }.by(1)
+ it_behaves_like 'adds project'
+
+ context 'when token scope is disabled' do
+ before do
+ project.ci_cd_settings.update!(job_token_scope_enabled: false)
+ end
+
+ it_behaves_like 'adds project'
end
end
diff --git a/spec/services/ci/job_token_scope/remove_project_service_spec.rb b/spec/services/ci/job_token_scope/remove_project_service_spec.rb
index 238fc879f54..155e60ac48e 100644
--- a/spec/services/ci/job_token_scope/remove_project_service_spec.rb
+++ b/spec/services/ci/job_token_scope/remove_project_service_spec.rb
@@ -14,6 +14,14 @@ RSpec.describe Ci::JobTokenScope::RemoveProjectService do
target_project: target_project)
end
+ shared_examples 'removes project' do |context|
+ it 'removes the project from the scope' do
+ expect do
+ expect(result).to be_success
+ end.to change { Ci::JobToken::ProjectScopeLink.count }.by(-1)
+ end
+ end
+
describe '#execute' do
subject(:result) { service.execute(target_project) }
@@ -24,10 +32,14 @@ RSpec.describe Ci::JobTokenScope::RemoveProjectService do
target_project.add_developer(current_user)
end
- it 'removes the project from the scope' do
- expect do
- expect(result).to be_success
- end.to change { Ci::JobToken::ProjectScopeLink.count }.by(-1)
+ it_behaves_like 'removes project'
+
+ context 'when token scope is disabled' do
+ before do
+ project.ci_cd_settings.update!(job_token_scope_enabled: false)
+ end
+
+ it_behaves_like 'removes project'
end
end
diff --git a/spec/services/ci/list_config_variables_service_spec.rb b/spec/services/ci/list_config_variables_service_spec.rb
index 1735f4cfc97..4953b18bfcc 100644
--- a/spec/services/ci/list_config_variables_service_spec.rb
+++ b/spec/services/ci/list_config_variables_service_spec.rb
@@ -40,8 +40,8 @@ RSpec.describe Ci::ListConfigVariablesService, :use_clean_rails_memory_store_cac
it 'returns variable list' do
expect(subject['KEY1']).to eq({ value: 'val 1', description: 'description 1' })
expect(subject['KEY2']).to eq({ value: 'val 2', description: '' })
- expect(subject['KEY3']).to eq({ value: 'val 3', description: nil })
- expect(subject['KEY4']).to eq({ value: 'val 4', description: nil })
+ expect(subject['KEY3']).to eq({ value: 'val 3' })
+ expect(subject['KEY4']).to eq({ value: 'val 4' })
end
end
diff --git a/spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb b/spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb
index 31548793bac..6d4dcf28108 100644
--- a/spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb
+++ b/spec/services/ci/pipeline_artifacts/coverage_report_service_spec.rb
@@ -51,6 +51,30 @@ RSpec.describe Ci::PipelineArtifacts::CoverageReportService do
let!(:pipeline) { create(:ci_pipeline, :with_coverage_reports, project: project) }
it_behaves_like 'creating or updating a pipeline coverage report'
+
+ context 'when ci_update_unlocked_pipeline_artifacts feature flag is enabled' do
+ it "artifact has pipeline's locked status" do
+ subject
+
+ artifact = Ci::PipelineArtifact.first
+
+ expect(artifact.locked).to eq(pipeline.locked)
+ end
+ end
+
+ context 'when ci_update_unlocked_pipeline_artifacts is disabled' do
+ before do
+ stub_feature_flags(ci_update_unlocked_pipeline_artifacts: false)
+ end
+
+ it 'artifact has unknown locked status' do
+ subject
+
+ artifact = Ci::PipelineArtifact.first
+
+ expect(artifact.locked).to eq('unknown')
+ end
+ end
end
context 'when pipeline has coverage report from child pipeline' do
diff --git a/spec/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service_spec.rb b/spec/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service_spec.rb
index 5568052e346..75233248113 100644
--- a/spec/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service_spec.rb
+++ b/spec/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service_spec.rb
@@ -51,6 +51,30 @@ RSpec.describe ::Ci::PipelineArtifacts::CreateCodeQualityMrDiffReportService do
end
end
+ context 'when ci_update_unlocked_pipeline_artifacts feature flag is enabled' do
+ it "artifact has pipeline's locked status" do
+ subject
+
+ artifact = Ci::PipelineArtifact.first
+
+ expect(artifact.locked).to eq(head_pipeline.locked)
+ end
+ end
+
+ context 'when ci_update_unlocked_pipeline_artifacts is disabled' do
+ before do
+ stub_feature_flags(ci_update_unlocked_pipeline_artifacts: false)
+ end
+
+ it 'artifact has unknown locked status' do
+ subject
+
+ artifact = Ci::PipelineArtifact.first
+
+ expect(artifact.locked).to eq('unknown')
+ end
+ end
+
it 'does not persist the same artifact twice' do
2.times { described_class.new(head_pipeline).execute }
diff --git a/spec/services/ci/pipeline_processing/atomic_processing_service/status_collection_spec.rb b/spec/services/ci/pipeline_processing/atomic_processing_service/status_collection_spec.rb
index 289e004fcce..7578afa7c50 100644
--- a/spec/services/ci/pipeline_processing/atomic_processing_service/status_collection_spec.rb
+++ b/spec/services/ci/pipeline_processing/atomic_processing_service/status_collection_spec.rb
@@ -6,11 +6,28 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService::StatusCollection
using RSpec::Parameterized::TableSyntax
let_it_be(:pipeline) { create(:ci_pipeline) }
- let_it_be(:build_a) { create(:ci_build, :success, name: 'build-a', stage: 'build', stage_idx: 0, pipeline: pipeline) }
- let_it_be(:build_b) { create(:ci_build, :failed, name: 'build-b', stage: 'build', stage_idx: 0, pipeline: pipeline) }
- let_it_be(:test_a) { create(:ci_build, :running, name: 'test-a', stage: 'test', stage_idx: 1, pipeline: pipeline) }
- let_it_be(:test_b) { create(:ci_build, :pending, name: 'test-b', stage: 'test', stage_idx: 1, pipeline: pipeline) }
- let_it_be(:deploy) { create(:ci_build, :created, name: 'deploy', stage: 'deploy', stage_idx: 2, pipeline: pipeline) }
+ let_it_be(:build_stage) { create(:ci_stage, name: 'build', pipeline: pipeline) }
+ let_it_be(:test_stage) { create(:ci_stage, name: 'test', pipeline: pipeline) }
+ let_it_be(:deploy_stage) { create(:ci_stage, name: 'deploy', pipeline: pipeline) }
+ let_it_be(:build_a) do
+ create(:ci_build, :success, name: 'build-a', ci_stage: build_stage, stage_idx: 0, pipeline: pipeline)
+ end
+
+ let_it_be(:build_b) do
+ create(:ci_build, :failed, name: 'build-b', ci_stage: build_stage, stage_idx: 0, pipeline: pipeline)
+ end
+
+ let_it_be(:test_a) do
+ create(:ci_build, :running, name: 'test-a', ci_stage: test_stage, stage_idx: 1, pipeline: pipeline)
+ end
+
+ let_it_be(:test_b) do
+ create(:ci_build, :pending, name: 'test-b', ci_stage: test_stage, stage_idx: 1, pipeline: pipeline)
+ end
+
+ let_it_be(:deploy) do
+ create(:ci_build, :created, name: 'deploy', ci_stage: deploy_stage, stage_idx: 2, pipeline: pipeline)
+ end
let(:collection) { described_class.new(pipeline) }
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 5bc508447c1..06bb6d39fe5 100644
--- a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb
+++ b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb
@@ -55,6 +55,8 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService do
statuses.each do |status|
if event == 'play'
status.play(user)
+ elsif event == 'retry'
+ ::Ci::RetryJobService.new(project, user).execute(status)
else
status.public_send("#{event}!")
end
diff --git a/spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_fail_and_retry_1.yml b/spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_fail_and_retry_1.yml
new file mode 100644
index 00000000000..b9b8eb2f532
--- /dev/null
+++ b/spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_fail_and_retry_1.yml
@@ -0,0 +1,55 @@
+config:
+ build:
+ script: exit $(($RANDOM % 2))
+
+ test:
+ script: exit 0
+ needs: [build]
+
+ deploy:
+ script: exit 0
+ needs: [test]
+
+init:
+ expect:
+ pipeline: pending
+ stages:
+ test: pending
+ jobs:
+ build: pending
+ test: created
+ deploy: created
+
+transitions:
+ - event: drop
+ jobs: [build]
+ expect:
+ pipeline: failed
+ stages:
+ test: failed
+ jobs:
+ build: failed
+ test: skipped
+ deploy: skipped
+
+ - event: retry
+ jobs: [build]
+ expect:
+ pipeline: running
+ stages:
+ test: pending
+ jobs:
+ build: pending
+ test: created
+ deploy: created
+
+ - event: success
+ jobs: [build]
+ expect:
+ pipeline: running
+ stages:
+ test: running
+ jobs:
+ build: success
+ test: pending
+ deploy: created
diff --git a/spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_fail_and_retry_2.yml b/spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_fail_and_retry_2.yml
new file mode 100644
index 00000000000..c875ebab3c9
--- /dev/null
+++ b/spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_fail_and_retry_2.yml
@@ -0,0 +1,63 @@
+config:
+ build:
+ script: exit $(($RANDOM % 2))
+
+ test1:
+ script: exit 0
+ needs: [build]
+
+ test2:
+ script: exit 0
+ when: manual
+
+ deploy:
+ script: exit 0
+ needs: [test1, test2]
+
+init:
+ expect:
+ pipeline: pending
+ stages:
+ test: pending
+ jobs:
+ build: pending
+ test1: created
+ test2: manual
+ deploy: skipped
+
+transitions:
+ - event: drop
+ jobs: [build]
+ expect:
+ pipeline: failed
+ stages:
+ test: failed
+ jobs:
+ build: failed
+ test1: skipped
+ test2: manual
+ deploy: skipped
+
+ - event: retry
+ jobs: [build]
+ expect:
+ pipeline: running
+ stages:
+ test: pending
+ jobs:
+ build: pending
+ test1: created
+ test2: manual
+ deploy: skipped
+
+ - event: success
+ jobs: [build]
+ expect:
+ pipeline: running
+ stages:
+ test: running
+ jobs:
+ build: success
+ test1: pending
+ test2: manual
+ deploy: skipped
diff --git a/spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_subsequent_manual_jobs.yml b/spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_subsequent_manual_jobs.yml
new file mode 100644
index 00000000000..03ffda1caab
--- /dev/null
+++ b/spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_subsequent_manual_jobs.yml
@@ -0,0 +1,65 @@
+config:
+ job1:
+ script: exit 0
+ when: manual
+
+ job2:
+ script: exit 0
+ needs: [job1]
+
+ job3:
+ script: exit 0
+ when: manual
+ needs: [job2]
+
+ job4:
+ script: exit 0
+ needs: [job3]
+
+init:
+ expect:
+ pipeline: skipped
+ stages:
+ test: skipped
+ jobs:
+ job1: manual
+ job2: skipped
+ job3: skipped
+ job4: skipped
+
+transitions:
+ - event: play
+ jobs: [job1]
+ expect:
+ pipeline: pending
+ stages:
+ test: pending
+ jobs:
+ job1: pending
+ job2: created
+ job3: created
+ job4: created
+
+ - event: success
+ jobs: [job1]
+ expect:
+ pipeline: running
+ stages:
+ test: running
+ jobs:
+ job1: success
+ job2: pending
+ job3: created
+ job4: created
+
+ - event: success
+ jobs: [job2]
+ expect:
+ pipeline: success
+ stages:
+ test: success
+ jobs:
+ job1: success
+ job2: success
+ job3: manual
+ job4: skipped
diff --git a/spec/services/ci/pipeline_schedule_service_spec.rb b/spec/services/ci/pipeline_schedule_service_spec.rb
index b8e4fb19f5d..2f094583f1a 100644
--- a/spec/services/ci/pipeline_schedule_service_spec.rb
+++ b/spec/services/ci/pipeline_schedule_service_spec.rb
@@ -3,14 +3,15 @@
require 'spec_helper'
RSpec.describe Ci::PipelineScheduleService do
- let(:project) { create(:project) }
- let(:user) { create(:user) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project) }
+
let(:service) { described_class.new(project, user) }
describe '#execute' do
subject { service.execute(schedule) }
- let(:schedule) { create(:ci_pipeline_schedule, project: project, owner: user) }
+ let_it_be(:schedule) { create(:ci_pipeline_schedule, project: project, owner: user) }
it 'schedules next run' do
expect(schedule).to receive(:schedule_next_run!)
@@ -34,9 +35,7 @@ RSpec.describe Ci::PipelineScheduleService do
end
context 'when the project is missing' do
- before do
- project.delete
- end
+ let(:project) { create(:project).tap(&:delete) }
it 'does not raise an exception' do
expect { subject }.not_to raise_error
diff --git a/spec/services/ci/pipelines/add_job_service_spec.rb b/spec/services/ci/pipelines/add_job_service_spec.rb
index 560724a1c6a..e735b2752d9 100644
--- a/spec/services/ci/pipelines/add_job_service_spec.rb
+++ b/spec/services/ci/pipelines/add_job_service_spec.rb
@@ -34,6 +34,14 @@ RSpec.describe Ci::Pipelines::AddJobService do
).and change { job.metadata.project }.to(pipeline.project)
end
+ it 'assigns partition_id to job and metadata' do
+ pipeline.partition_id = 123
+
+ expect { execute }
+ .to change(job, :partition_id).to(pipeline.partition_id)
+ .and change { job.metadata.partition_id }.to(pipeline.partition_id)
+ end
+
it 'returns a service response with the job as payload' do
expect(execute).to be_success
expect(execute.payload[:job]).to eq(job)
diff --git a/spec/services/ci/pipelines/hook_service_spec.rb b/spec/services/ci/pipelines/hook_service_spec.rb
index 0e1ef6afd0d..8d138a3d957 100644
--- a/spec/services/ci/pipelines/hook_service_spec.rb
+++ b/spec/services/ci/pipelines/hook_service_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Ci::Pipelines::HookService do
describe '#execute_hooks' do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:project) { create(:project, :repository, namespace: namespace) }
- let_it_be(:pipeline) { create(:ci_empty_pipeline, :created, project: project) }
+ let_it_be(:pipeline, reload: true) { create(:ci_empty_pipeline, :created, project: project) }
let(:hook_enabled) { true }
let!(:hook) { create(:project_hook, project: project, pipeline_events: hook_enabled) }
diff --git a/spec/services/ci/play_manual_stage_service_spec.rb b/spec/services/ci/play_manual_stage_service_spec.rb
index b3ae92aa787..24f0a21f3dd 100644
--- a/spec/services/ci/play_manual_stage_service_spec.rb
+++ b/spec/services/ci/play_manual_stage_service_spec.rb
@@ -75,7 +75,6 @@ RSpec.describe Ci::PlayManualStageService, '#execute' do
options.merge!({
when: 'manual',
pipeline: pipeline,
- stage: stage.name,
stage_id: stage.id,
user: pipeline.user
})
@@ -87,7 +86,6 @@ RSpec.describe Ci::PlayManualStageService, '#execute' do
options.merge!({
when: 'manual',
pipeline: pipeline,
- stage: stage.name,
stage_id: stage.id,
user: pipeline.user,
downstream: downstream_project
diff --git a/spec/services/ci/process_sync_events_service_spec.rb b/spec/services/ci/process_sync_events_service_spec.rb
index 241ac4995ff..7ab7911e578 100644
--- a/spec/services/ci/process_sync_events_service_spec.rb
+++ b/spec/services/ci/process_sync_events_service_spec.rb
@@ -120,13 +120,15 @@ RSpec.describe Ci::ProcessSyncEventsService do
before do
Namespaces::SyncEvent.delete_all
+ # Creates a sync event for group, and the ProjectNamespace of project1 & project2: 3 in total
group.update!(parent: parent_group_2)
+ # Creates a sync event for parent_group2 and all the children: 4 in total
parent_group_2.update!(parent: parent_group_1)
end
shared_examples 'event consuming' do
it 'consumes events' do
- expect { execute }.to change(Namespaces::SyncEvent, :count).from(2).to(0)
+ expect { execute }.to change(Namespaces::SyncEvent, :count).from(7).to(0)
expect(group.reload.ci_namespace_mirror).to have_attributes(
traversal_ids: [parent_group_1.id, parent_group_2.id, group.id]
@@ -134,6 +136,12 @@ RSpec.describe Ci::ProcessSyncEventsService do
expect(parent_group_2.reload.ci_namespace_mirror).to have_attributes(
traversal_ids: [parent_group_1.id, parent_group_2.id]
)
+ expect(project1.reload.project_namespace).to have_attributes(
+ traversal_ids: [parent_group_1.id, parent_group_2.id, group.id, project1.project_namespace.id]
+ )
+ expect(project2.reload.project_namespace).to have_attributes(
+ traversal_ids: [parent_group_1.id, parent_group_2.id, group.id, project2.project_namespace.id]
+ )
end
end
@@ -157,7 +165,7 @@ RSpec.describe Ci::ProcessSyncEventsService do
end
it 'does not enqueue Namespaces::ProcessSyncEventsWorker if no left' do
- stub_const("#{described_class}::BATCH_SIZE", 2)
+ stub_const("#{described_class}::BATCH_SIZE", 7)
expect(Namespaces::ProcessSyncEventsWorker).not_to receive(:perform_async)
diff --git a/spec/services/ci/queue/pending_builds_strategy_spec.rb b/spec/services/ci/queue/pending_builds_strategy_spec.rb
new file mode 100644
index 00000000000..6f22c256c17
--- /dev/null
+++ b/spec/services/ci/queue/pending_builds_strategy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::Queue::PendingBuildsStrategy do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:group_runner) { create(:ci_runner, :group, groups: [group]) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:pipeline) { create(:ci_pipeline, project: project) }
+
+ let!(:build_1) { create(:ci_build, :created, pipeline: pipeline) }
+ let!(:build_2) { create(:ci_build, :created, pipeline: pipeline) }
+ let!(:build_3) { create(:ci_build, :created, pipeline: pipeline) }
+ let!(:pending_build_1) { create(:ci_pending_build, build: build_2, project: project) }
+ let!(:pending_build_2) { create(:ci_pending_build, build: build_3, project: project) }
+ let!(:pending_build_3) { create(:ci_pending_build, build: build_1, project: project) }
+
+ describe 'builds_for_group_runner' do
+ it 'returns builds ordered by build ID' do
+ strategy = described_class.new(group_runner)
+ expect(strategy.builds_for_group_runner).to eq([pending_build_3, pending_build_1, pending_build_2])
+ end
+ end
+end
diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb
index cabd60a22d1..e2e760b9812 100644
--- a/spec/services/ci/register_job_service_spec.rb
+++ b/spec/services/ci/register_job_service_spec.rb
@@ -571,10 +571,6 @@ module Ci
context 'when artifacts of depended job has been erased' do
let!(:pre_stage_job) { create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0, erased_at: 1.minute.ago) }
- before do
- pre_stage_job.erase
- end
-
it_behaves_like 'not pick'
end
@@ -612,10 +608,6 @@ module Ci
context 'when artifacts of depended job has been erased' do
let!(:pre_stage_job) { create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0, erased_at: 1.minute.ago) }
- before do
- pre_stage_job.erase
- end
-
it { expect(subject).to eq(pending_job) }
end
end
diff --git a/spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb b/spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb
index 194203a422c..3d1abe290bc 100644
--- a/spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb
+++ b/spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb
@@ -3,6 +3,8 @@
require 'spec_helper'
RSpec.describe Ci::ResourceGroups::AssignResourceFromResourceGroupService do
+ include ConcurrentHelpers
+
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
@@ -134,6 +136,19 @@ RSpec.describe Ci::ResourceGroups::AssignResourceFromResourceGroupService do
end
end
end
+
+ context 'when parallel services are running' do
+ it 'can run the same command in parallel' do
+ parallel_num = 4
+
+ blocks = Array.new(parallel_num).map do
+ -> { subject }
+ end
+
+ run_parallel(blocks)
+ expect(build.reload).to be_pending
+ end
+ end
end
context 'when there are no available resources' do
diff --git a/spec/services/ci/retry_job_service_spec.rb b/spec/services/ci/retry_job_service_spec.rb
index b14e4187c7a..69f19c5acf2 100644
--- a/spec/services/ci/retry_job_service_spec.rb
+++ b/spec/services/ci/retry_job_service_spec.rb
@@ -7,14 +7,13 @@ RSpec.describe Ci::RetryJobService do
let_it_be(:developer) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
let_it_be(:pipeline) do
- create(:ci_pipeline, project: project,
- sha: 'b83d6e391c22777fca1ed3012fce84f633d7fed0')
+ create(:ci_pipeline, project: project, sha: 'b83d6e391c22777fca1ed3012fce84f633d7fed0')
end
let_it_be(:stage) do
create(:ci_stage, project: project,
- pipeline: pipeline,
- name: 'test')
+ pipeline: pipeline,
+ name: 'test')
end
let(:job_variables_attributes) { [{ key: 'MANUAL_VAR', value: 'manual test var' }] }
@@ -31,9 +30,8 @@ RSpec.describe Ci::RetryJobService do
let_it_be(:downstream_project) { create(:project, :repository) }
let_it_be_with_refind(:job) do
- create(
- :ci_bridge, :success, pipeline: pipeline, downstream: downstream_project,
- description: 'a trigger job', stage_id: stage.id
+ create(:ci_bridge, :success,
+ pipeline: pipeline, downstream: downstream_project, description: 'a trigger job', ci_stage: stage
)
end
@@ -45,13 +43,13 @@ RSpec.describe Ci::RetryJobService do
end
shared_context 'retryable build' do
- let_it_be_with_refind(:job) { create(:ci_build, :success, pipeline: pipeline, stage_id: stage.id) }
+ let_it_be_with_refind(:job) { create(:ci_build, :success, pipeline: pipeline, ci_stage: stage) }
let_it_be(:another_pipeline) { create(:ci_empty_pipeline, project: project) }
let_it_be(:job_to_clone) do
create(:ci_build, :failed, :picked, :expired, :erased, :queued, :coverage, :tags,
:allowed_to_fail, :on_tag, :triggered, :teardown_environment, :resource_group,
- description: 'my-job', stage: 'test', stage_id: stage.id,
+ description: 'my-job', ci_stage: stage,
pipeline: pipeline, auto_canceled_by: another_pipeline,
scheduled_at: 10.seconds.since)
end
@@ -66,8 +64,7 @@ RSpec.describe Ci::RetryJobService do
let(:job) { job_to_clone }
before_all do
- # Make sure that job has both `stage_id` and `stage`
- job_to_clone.update!(stage: 'test', stage_id: stage.id)
+ job_to_clone.update!(ci_stage: stage)
create(:ci_build_need, build: job_to_clone)
end
@@ -126,16 +123,16 @@ RSpec.describe Ci::RetryJobService do
end
context 'when there are subsequent processables that are skipped' do
+ let_it_be(:stage) { create(:ci_stage, pipeline: pipeline, name: 'deploy') }
+
let!(:subsequent_build) do
create(:ci_build, :skipped, stage_idx: 2,
pipeline: pipeline,
- stage: 'deploy')
+ ci_stage: stage)
end
let!(:subsequent_bridge) do
- create(:ci_bridge, :skipped, stage_idx: 2,
- pipeline: pipeline,
- stage: 'deploy')
+ create(:ci_bridge, :skipped, stage_idx: 2, pipeline: pipeline, ci_stage: stage)
end
it 'resumes pipeline processing in the subsequent stage' do
@@ -156,8 +153,8 @@ RSpec.describe Ci::RetryJobService do
context 'when the pipeline has other jobs' do
let!(:stage2) { create(:ci_stage, project: project, pipeline: pipeline, name: 'deploy') }
- let!(:build2) { create(:ci_build, pipeline: pipeline, stage_id: stage.id ) }
- let!(:deploy) { create(:ci_build, pipeline: pipeline, stage_id: stage2.id) }
+ let!(:build2) { create(:ci_build, pipeline: pipeline, ci_stage: stage ) }
+ let!(:deploy) { create(:ci_build, pipeline: pipeline, ci_stage: stage2) }
let!(:deploy_needs_build2) { create(:ci_build_need, build: deploy, name: build2.name) }
context 'when job has a nil scheduling_type' do
@@ -227,7 +224,7 @@ RSpec.describe Ci::RetryJobService do
context 'when a build with a deployment is retried' do
let!(:job) do
create(:ci_build, :with_deployment, :deploy_to_production,
- pipeline: pipeline, stage_id: stage.id, project: project)
+ pipeline: pipeline, ci_stage: stage, project: project)
end
it 'creates a new deployment' do
@@ -245,10 +242,13 @@ RSpec.describe Ci::RetryJobService do
let(:environment_name) { 'review/$CI_COMMIT_REF_SLUG-$GITLAB_USER_ID' }
let!(:job) do
- create(:ci_build, :with_deployment, environment: environment_name,
- options: { environment: { name: environment_name } },
- pipeline: pipeline, stage_id: stage.id, project: project,
- user: other_developer)
+ create(:ci_build, :with_deployment,
+ environment: environment_name,
+ options: { environment: { name: environment_name } },
+ pipeline: pipeline,
+ ci_stage: stage,
+ project: project,
+ user: other_developer)
end
it 'creates a new deployment' do
@@ -307,22 +307,24 @@ RSpec.describe Ci::RetryJobService do
it_behaves_like 'retries the job'
context 'when there are subsequent jobs that are skipped' do
+ let_it_be(:stage) { create(:ci_stage, pipeline: pipeline, name: 'deploy') }
+
let!(:subsequent_build) do
create(:ci_build, :skipped, stage_idx: 2,
pipeline: pipeline,
- stage: 'deploy')
+ stage_id: stage.id)
end
let!(:subsequent_bridge) do
create(:ci_bridge, :skipped, stage_idx: 2,
pipeline: pipeline,
- stage: 'deploy')
+ stage_id: stage.id)
end
it 'does not cause an N+1 when updating the job ownership' do
control_count = ActiveRecord::QueryRecorder.new(skip_cached: false) { service.execute(job) }.count
- create_list(:ci_build, 2, :skipped, stage_idx: job.stage_idx + 1, pipeline: pipeline, stage: 'deploy')
+ create_list(:ci_build, 2, :skipped, stage_idx: job.stage_idx + 1, pipeline: pipeline, stage_id: stage.id)
expect { service.execute(job) }.not_to exceed_all_query_limit(control_count)
end
diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb
index 24272801480..0a1e767539d 100644
--- a/spec/services/ci/retry_pipeline_service_spec.rb
+++ b/spec/services/ci/retry_pipeline_service_spec.rb
@@ -9,6 +9,9 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
let(:project) { create(:project) }
let(:pipeline) { create(:ci_pipeline, project: project) }
let(:service) { described_class.new(project, user) }
+ let(:build_stage) { create(:ci_stage, name: 'build', position: 0, pipeline: pipeline) }
+ let(:test_stage) { create(:ci_stage, name: 'test', position: 1, pipeline: pipeline) }
+ let(:deploy_stage) { create(:ci_stage, name: 'deploy', position: 2, pipeline: pipeline) }
context 'when user has full ability to modify pipeline' do
before do
@@ -20,8 +23,8 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
context 'when there are already retried jobs present' do
before do
- create_build('rspec', :canceled, 0, retried: true)
- create_build('rspec', :failed, 0)
+ create_build('rspec', :canceled, build_stage, retried: true)
+ create_build('rspec', :failed, build_stage)
end
it 'does not retry jobs that has already been retried' do
@@ -33,9 +36,9 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
context 'when there are failed builds in the last stage' do
before do
- create_build('rspec 1', :success, 0)
- create_build('rspec 2', :failed, 1)
- create_build('rspec 3', :canceled, 1)
+ create_build('rspec 1', :success, build_stage)
+ create_build('rspec 2', :failed, test_stage)
+ create_build('rspec 3', :canceled, test_stage)
end
it 'enqueues all builds in the last stage' do
@@ -49,10 +52,10 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
context 'when there are failed or canceled builds in the first stage' do
before do
- create_build('rspec 1', :failed, 0)
- create_build('rspec 2', :canceled, 0)
- create_build('rspec 3', :canceled, 1)
- create_build('spinach 1', :canceled, 2)
+ create_build('rspec 1', :failed, build_stage)
+ create_build('rspec 2', :canceled, build_stage)
+ create_build('rspec 3', :canceled, test_stage)
+ create_build('spinach 1', :canceled, deploy_stage)
end
it 'retries builds failed builds and marks subsequent for processing' do
@@ -80,10 +83,10 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
context 'when there is failed build present which was run on failure' do
before do
- create_build('rspec 1', :failed, 0)
- create_build('rspec 2', :canceled, 0)
- create_build('rspec 3', :canceled, 1)
- create_build('report 1', :failed, 2)
+ create_build('rspec 1', :failed, build_stage)
+ create_build('rspec 2', :canceled, build_stage)
+ create_build('rspec 3', :canceled, test_stage)
+ create_build('report 1', :failed, deploy_stage)
end
it 'retries builds only in the first stage' do
@@ -105,9 +108,9 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
context 'when there is a failed test in a DAG' do
before do
- create_build('build', :success, 0)
- create_build('build2', :success, 0)
- test_build = create_build('test', :failed, 1, scheduling_type: :dag)
+ create_build('build', :success, build_stage)
+ create_build('build2', :success, build_stage)
+ test_build = create_build('test', :failed, test_stage, scheduling_type: :dag)
create(:ci_build_need, build: test_build, name: 'build')
create(:ci_build_need, build: test_build, name: 'build2')
end
@@ -123,7 +126,7 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
context 'when there is a failed DAG test without needs' do
before do
- create_build('deploy', :failed, 2, scheduling_type: :dag)
+ create_build('deploy', :failed, deploy_stage, scheduling_type: :dag)
end
it 'retries the test' do
@@ -139,10 +142,10 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
context 'when the last stage was skipped' do
before do
- create_build('build 1', :success, 0)
- create_build('test 2', :failed, 1)
- create_build('report 3', :skipped, 2)
- create_build('report 4', :skipped, 2)
+ create_build('build 1', :success, build_stage)
+ create_build('test 2', :failed, test_stage)
+ create_build('report 3', :skipped, deploy_stage)
+ create_build('report 4', :skipped, deploy_stage)
end
it 'retries builds only in the first stage' do
@@ -160,9 +163,9 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
context 'when there are optional manual actions only' do
context 'when there is a canceled manual action in first stage' do
before do
- create_build('rspec 1', :failed, 0)
- create_build('staging', :canceled, 0, when: :manual, allow_failure: true)
- create_build('rspec 2', :canceled, 1)
+ create_build('rspec 1', :failed, build_stage)
+ create_build('staging', :canceled, build_stage, when: :manual, allow_failure: true)
+ create_build('rspec 2', :canceled, test_stage)
end
it 'retries failed builds and marks subsequent for processing' do
@@ -189,9 +192,9 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
context 'when pipeline has blocking manual actions defined' do
context 'when pipeline retry should enqueue builds' do
before do
- create_build('test', :failed, 0)
- create_build('deploy', :canceled, 0, when: :manual, allow_failure: false)
- create_build('verify', :canceled, 1)
+ create_build('test', :failed, build_stage)
+ create_build('deploy', :canceled, build_stage, when: :manual, allow_failure: false)
+ create_build('verify', :canceled, test_stage)
end
it 'retries failed builds' do
@@ -206,10 +209,10 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
context 'when pipeline retry should block pipeline immediately' do
before do
- create_build('test', :success, 0)
- create_build('deploy:1', :success, 1, when: :manual, allow_failure: false)
- create_build('deploy:2', :failed, 1, when: :manual, allow_failure: false)
- create_build('verify', :canceled, 2)
+ create_build('test', :success, build_stage)
+ create_build('deploy:1', :success, test_stage, when: :manual, allow_failure: false)
+ create_build('deploy:2', :failed, test_stage, when: :manual, allow_failure: false)
+ create_build('verify', :canceled, deploy_stage)
end
it 'reprocesses blocking manual action and blocks pipeline' do
@@ -225,9 +228,9 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
context 'when there is a skipped manual action in last stage' do
before do
- create_build('rspec 1', :canceled, 0)
- create_build('rspec 2', :skipped, 0, when: :manual, allow_failure: true)
- create_build('staging', :skipped, 1, when: :manual, allow_failure: true)
+ create_build('rspec 1', :canceled, build_stage)
+ create_build('rspec 2', :skipped, build_stage, when: :manual, allow_failure: true)
+ create_build('staging', :skipped, test_stage, when: :manual, allow_failure: true)
end
it 'retries canceled job and reprocesses manual actions' do
@@ -242,8 +245,8 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
context 'when there is a created manual action in the last stage' do
before do
- create_build('rspec 1', :canceled, 0)
- create_build('staging', :created, 1, when: :manual, allow_failure: true)
+ create_build('rspec 1', :canceled, build_stage)
+ create_build('staging', :created, test_stage, when: :manual, allow_failure: true)
end
it 'retries canceled job and does not update the manual action' do
@@ -257,8 +260,8 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
context 'when there is a created manual action in the first stage' do
before do
- create_build('rspec 1', :canceled, 0)
- create_build('staging', :created, 0, when: :manual, allow_failure: true)
+ create_build('rspec 1', :canceled, build_stage)
+ create_build('staging', :created, build_stage, when: :manual, allow_failure: true)
end
it 'retries canceled job and processes the manual action' do
@@ -285,9 +288,9 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
end
context 'when pipeline has processables with nil scheduling_type' do
- let!(:build1) { create_build('build1', :success, 0) }
- let!(:build2) { create_build('build2', :failed, 0) }
- let!(:build3) { create_build('build3', :failed, 1) }
+ let!(:build1) { create_build('build1', :success, build_stage) }
+ let!(:build2) { create_build('build2', :failed, build_stage) }
+ let!(:build3) { create_build('build3', :failed, test_stage) }
let!(:build3_needs_build1) { create(:ci_build_need, build: build3, name: build1.name) }
before do
@@ -319,10 +322,10 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
context 'when there are skipped jobs in later stages' do
before do
- create_build('build 1', :success, 0)
- create_build('test 2', :failed, 1)
- create_build('report 3', :skipped, 2)
- create_bridge('deploy 4', :skipped, 2)
+ create_build('build 1', :success, build_stage)
+ create_build('test 2', :failed, test_stage)
+ create_build('report 3', :skipped, deploy_stage)
+ create_bridge('deploy 4', :skipped, deploy_stage)
end
it 'retries failed jobs and processes skipped jobs' do
@@ -374,9 +377,9 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
context 'when there is a failed manual action present' do
before do
- create_build('test', :failed, 0)
- create_build('deploy', :failed, 0, when: :manual)
- create_build('verify', :canceled, 1)
+ create_build('test', :failed, build_stage)
+ create_build('deploy', :failed, build_stage, when: :manual)
+ create_build('verify', :canceled, test_stage)
end
it 'returns an error' do
@@ -390,9 +393,9 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
context 'when there is a failed manual action in later stage' do
before do
- create_build('test', :failed, 0)
- create_build('deploy', :failed, 1, when: :manual)
- create_build('verify', :canceled, 2)
+ create_build('test', :failed, build_stage)
+ create_build('deploy', :failed, test_stage, when: :manual)
+ create_build('verify', :canceled, deploy_stage)
end
it 'returns an error' do
@@ -418,7 +421,7 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
target_project: project,
source_branch: 'fixes',
allow_collaboration: true)
- create_build('rspec 1', :failed, 1)
+ create_build('rspec 1', :failed, test_stage)
end
it 'allows to retry failed pipeline' do
@@ -441,19 +444,19 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
statuses.latest.find_by(name: name)
end
- def create_build(name, status, stage_num, **opts)
- create_processable(:ci_build, name, status, stage_num, **opts)
+ def create_build(name, status, stage, **opts)
+ create_processable(:ci_build, name, status, stage, **opts)
end
- def create_bridge(name, status, stage_num, **opts)
- create_processable(:ci_bridge, name, status, stage_num, **opts)
+ def create_bridge(name, status, stage, **opts)
+ create_processable(:ci_bridge, name, status, stage, **opts)
end
- def create_processable(type, name, status, stage_num, **opts)
+ def create_processable(type, name, status, stage, **opts)
create(type, name: name,
status: status,
- stage: "stage_#{stage_num}",
- stage_idx: stage_num,
+ ci_stage: stage,
+ stage_idx: stage.position,
pipeline: pipeline, **opts) do |_job|
::Ci::ProcessPipelineService.new(pipeline).execute
end
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
new file mode 100644
index 00000000000..0d2e237c87b
--- /dev/null
+++ b/spec/services/ci/runners/set_runner_associated_projects_service_spec.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::Ci::Runners::SetRunnerAssociatedProjectsService, '#execute' do
+ subject(:execute) { described_class.new(runner: runner, current_user: user, project_ids: project_ids).execute }
+
+ let_it_be(:owner_project) { create(:project) }
+ let_it_be(:project2) { create(:project) }
+ let_it_be(:original_projects) { [owner_project, project2] }
+
+ let(:runner) { create(:ci_runner, :project, projects: original_projects) }
+
+ context 'without user' do
+ let(:user) { nil }
+ let(:project_ids) { [project2.id] }
+
+ it 'does not call assign_to on runner and returns error response', :aggregate_failures do
+ expect(runner).not_to receive(:assign_to)
+
+ expect(execute).to be_error
+ expect(execute.message).to eq('user not allowed to assign runner')
+ end
+ end
+
+ context 'with unauthorized user' do
+ let(:user) { build(:user) }
+ let(:project_ids) { [project2.id] }
+
+ it 'does not call assign_to on runner and returns error message' do
+ expect(runner).not_to receive(:assign_to)
+
+ expect(execute).to be_error
+ expect(execute.message).to eq('user not allowed to assign runner')
+ end
+ end
+
+ context 'with admin user', :enable_admin_mode do
+ let_it_be(:user) { create(:user, :admin) }
+
+ let(:project3) { create(:project) }
+ let(:project4) { create(:project) }
+
+ context 'with successful requests' do
+ context 'when disassociating a project' do
+ let(:project_ids) { [project3.id, project4.id] }
+
+ it 'reassigns associated projects and returns success response' do
+ expect(execute).to be_success
+ expect(runner.reload.projects.ids).to eq([owner_project.id] + project_ids)
+ end
+ end
+
+ context 'when disassociating no projects' do
+ let(:project_ids) { [project2.id, project3.id] }
+
+ it 'reassigns associated projects and returns success response' do
+ expect(execute).to be_success
+ expect(runner.reload.projects.ids).to eq([owner_project.id] + project_ids)
+ end
+ end
+ end
+
+ context 'with failing assign_to requests' do
+ let(:project_ids) { [project3.id, project4.id] }
+
+ it 'returns error response and rolls back transaction' do
+ expect(runner).to receive(:assign_to).with(project4, user).once.and_return(false)
+
+ expect(execute).to be_error
+ expect(runner.reload.projects).to eq(original_projects)
+ end
+ end
+
+ context 'with failing destroy calls' do
+ let(:project_ids) { [project3.id, project4.id] }
+
+ it 'returns error response and rolls back transaction' do
+ allow_next_found_instance_of(Ci::RunnerProject) do |runner_project|
+ allow(runner_project).to receive(:destroy).and_return(false)
+ end
+
+ expect(execute).to be_error
+ expect(runner.reload.projects).to eq(original_projects)
+ end
+ end
+ end
+end
diff --git a/spec/services/ci/runners/update_runner_service_spec.rb b/spec/services/ci/runners/update_runner_service_spec.rb
index e008fde9982..1f953ac4cbb 100644
--- a/spec/services/ci/runners/update_runner_service_spec.rb
+++ b/spec/services/ci/runners/update_runner_service_spec.rb
@@ -2,69 +2,65 @@
require 'spec_helper'
-RSpec.describe Ci::Runners::UpdateRunnerService do
+RSpec.describe Ci::Runners::UpdateRunnerService, '#execute' do
+ subject(:execute) { described_class.new(runner).execute(params) }
+
let(:runner) { create(:ci_runner) }
- describe '#update' do
- before do
- allow(runner).to receive(:tick_runner_queue)
- end
+ before do
+ allow(runner).to receive(:tick_runner_queue)
+ end
- context 'with description params' do
- let(:params) { { description: 'new runner' } }
+ context 'with description params' do
+ let(:params) { { description: 'new runner' } }
- it 'updates the runner and ticking the queue' do
- expect(update).to be_truthy
+ it 'updates the runner and ticking the queue' do
+ expect(execute).to be_success
- runner.reload
+ runner.reload
- expect(runner).to have_received(:tick_runner_queue)
- expect(runner.description).to eq('new runner')
- end
+ expect(runner).to have_received(:tick_runner_queue)
+ expect(runner.description).to eq('new runner')
end
+ end
- context 'with paused param' do
- let(:params) { { paused: true } }
+ context 'with paused param' do
+ let(:params) { { paused: true } }
- it 'updates the runner and ticking the queue' do
- expect(runner.active).to be_truthy
- expect(update).to be_truthy
+ it 'updates the runner and ticking the queue' do
+ expect(runner.active).to be_truthy
+ expect(execute).to be_success
- runner.reload
+ runner.reload
- expect(runner).to have_received(:tick_runner_queue)
- expect(runner.active).to be_falsey
- end
+ expect(runner).to have_received(:tick_runner_queue)
+ expect(runner.active).to be_falsey
end
+ end
- context 'with cost factor params' do
- let(:params) { { public_projects_minutes_cost_factor: 1.1, private_projects_minutes_cost_factor: 2.2 } }
+ context 'with cost factor params' do
+ let(:params) { { public_projects_minutes_cost_factor: 1.1, private_projects_minutes_cost_factor: 2.2 } }
- it 'updates the runner cost factors' do
- expect(update).to be_truthy
+ it 'updates the runner cost factors' do
+ expect(execute).to be_success
- runner.reload
+ runner.reload
- expect(runner.public_projects_minutes_cost_factor).to eq(1.1)
- expect(runner.private_projects_minutes_cost_factor).to eq(2.2)
- end
+ expect(runner.public_projects_minutes_cost_factor).to eq(1.1)
+ expect(runner.private_projects_minutes_cost_factor).to eq(2.2)
end
+ end
- context 'when params are not valid' do
- let(:params) { { run_untagged: false } }
-
- it 'does not update and give false because it is not valid' do
- expect(update).to be_falsey
+ context 'when params are not valid' do
+ let(:params) { { run_untagged: false } }
- runner.reload
+ it 'does not update and returns error because it is not valid' do
+ expect(execute).to be_error
- expect(runner).not_to have_received(:tick_runner_queue)
- expect(runner.run_untagged).to be_truthy
- end
- end
+ runner.reload
- def update
- described_class.new(runner).update(params) # rubocop: disable Rails/SaveBang
+ expect(runner).not_to have_received(:tick_runner_queue)
+ expect(runner.run_untagged).to be_truthy
end
end
end
diff --git a/spec/services/ci/unlock_artifacts_service_spec.rb b/spec/services/ci/unlock_artifacts_service_spec.rb
index 94d39fc9f14..776019f03f8 100644
--- a/spec/services/ci/unlock_artifacts_service_spec.rb
+++ b/spec/services/ci/unlock_artifacts_service_spec.rb
@@ -5,11 +5,15 @@ 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, :ci_update_unlocked_job_artifacts, :ci_update_unlocked_pipeline_artifacts) do
+ false | false | false
+ false | true | false
+ true | false | false
+ true | true | false
+ false | false | true
+ false | true | true
+ true | false | true
+ true | true | true
end
with_them do
@@ -22,6 +26,7 @@ RSpec.describe Ci::UnlockArtifactsService do
let!(:old_unlocked_pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: ref, tag: tag, project: project, locked: :unlocked) }
let!(:older_pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: ref, tag: tag, project: project, locked: :artifacts_locked) }
let!(:older_ambiguous_pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: ref, tag: !tag, project: project, locked: :artifacts_locked) }
+ let!(:code_coverage_pipeline) { create(:ci_pipeline, :with_coverage_report_artifact, ref: ref, tag: tag, project: project, locked: :artifacts_locked) }
let!(:pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: ref, tag: tag, project: project, locked: :artifacts_locked) }
let!(:child_pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: ref, tag: tag, project: project, locked: :artifacts_locked) }
let!(:newer_pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: ref, tag: tag, project: project, locked: :artifacts_locked) }
@@ -30,7 +35,8 @@ 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)
+ stub_feature_flags(ci_update_unlocked_job_artifacts: ci_update_unlocked_job_artifacts,
+ ci_update_unlocked_pipeline_artifacts: ci_update_unlocked_pipeline_artifacts)
end
describe '#execute' do
@@ -72,6 +78,14 @@ RSpec.describe Ci::UnlockArtifactsService do
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 && ci_update_unlocked_pipeline_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
+ end
end
context 'when running on just the ref' do
@@ -106,6 +120,14 @@ RSpec.describe Ci::UnlockArtifactsService do
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 && ci_update_unlocked_pipeline_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
+ end
end
end
diff --git a/spec/services/container_expiration_policies/cleanup_service_spec.rb b/spec/services/container_expiration_policies/cleanup_service_spec.rb
index c265ce74d14..6e1be7271e1 100644
--- a/spec/services/container_expiration_policies/cleanup_service_spec.rb
+++ b/spec/services/container_expiration_policies/cleanup_service_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe ContainerExpirationPolicies::CleanupService do
it 'completely clean up the repository' do
expect(Projects::ContainerRepository::CleanupTagsService)
- .to receive(:new).with(repository, nil, cleanup_tags_service_params).and_return(cleanup_tags_service)
+ .to receive(:new).with(container_repository: repository, params: cleanup_tags_service_params).and_return(cleanup_tags_service)
expect(cleanup_tags_service).to receive(:execute).and_return(status: :success, deleted_size: 1)
response = subject
diff --git a/spec/services/deployments/link_merge_requests_service_spec.rb b/spec/services/deployments/link_merge_requests_service_spec.rb
index 62adc834733..a653cd2b48b 100644
--- a/spec/services/deployments/link_merge_requests_service_spec.rb
+++ b/spec/services/deployments/link_merge_requests_service_spec.rb
@@ -159,10 +159,10 @@ RSpec.describe Deployments::LinkMergeRequestsService do
it "doesn't link the same merge_request twice" do
create(:merge_request, :merged, merge_commit_sha: mr1_merge_commit_sha,
- source_project: project)
+ source_project: project)
picked_mr = create(:merge_request, :merged, merge_commit_sha: '123abc',
- source_project: project)
+ source_project: project)
# the first MR includes c1c67abba which is a cherry-pick of the fake picked_mr merge request
create(:track_mr_picking_note, noteable: picked_mr, project: project, commit_id: 'c1c67abbaf91f624347bb3ae96eabe3a1b742478')
diff --git a/spec/services/deployments/update_environment_service_spec.rb b/spec/services/deployments/update_environment_service_spec.rb
index 4485ce585bb..c952bcddd9a 100644
--- a/spec/services/deployments/update_environment_service_spec.rb
+++ b/spec/services/deployments/update_environment_service_spec.rb
@@ -159,14 +159,37 @@ RSpec.describe Deployments::UpdateEnvironmentService do
{ name: 'production', auto_stop_in: '1 day' }
end
+ before do
+ environment.update_attribute(:auto_stop_at, nil)
+ end
+
it 'renews auto stop at' do
freeze_time do
- environment.update!(auto_stop_at: nil)
-
expect { subject.execute }
.to change { environment.reset.auto_stop_at&.round }.from(nil).to(1.day.since.round)
end
end
+
+ context 'when value is a variable' do
+ let(:options) { { name: 'production', auto_stop_in: '$TTL' } }
+
+ let(:yaml_variables) do
+ [
+ { key: "TTL", value: '2 days', public: true }
+ ]
+ end
+
+ before do
+ job.update_attribute(:yaml_variables, yaml_variables)
+ end
+
+ it 'renews auto stop at with expanded variable value' do
+ freeze_time do
+ expect { subject.execute }
+ .to change { environment.reset.auto_stop_at&.round }.from(nil).to(2.days.since.round)
+ end
+ end
+ end
end
context 'when deployment tier is specified' do
diff --git a/spec/services/design_management/delete_designs_service_spec.rb b/spec/services/design_management/delete_designs_service_spec.rb
index a0e049ea42a..48e53a92758 100644
--- a/spec/services/design_management/delete_designs_service_spec.rb
+++ b/spec/services/design_management/delete_designs_service_spec.rb
@@ -126,7 +126,8 @@ RSpec.describe DesignManagement::DeleteDesignsService do
end
it 'updates UsageData for removed designs' do
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_designs_removed_action).with(author: user)
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_designs_removed_action)
+ .with(author: user, project: project)
run_service
end
@@ -171,6 +172,11 @@ RSpec.describe DesignManagement::DeleteDesignsService do
run_service
end
+
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_DESIGNS_REMOVED }
+ subject(:service_action) { run_service }
+ end
end
context 'more than one design is passed' do
diff --git a/spec/services/design_management/save_designs_service_spec.rb b/spec/services/design_management/save_designs_service_spec.rb
index b76c91fbac9..c69df5f2eb9 100644
--- a/spec/services/design_management/save_designs_service_spec.rb
+++ b/spec/services/design_management/save_designs_service_spec.rb
@@ -106,7 +106,8 @@ RSpec.describe DesignManagement::SaveDesignsService do
it 'creates a commit, an event in the activity stream and updates the creation count', :aggregate_failures do
counter = Gitlab::UsageDataCounters::DesignsCounter
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_designs_added_action).with(author: user)
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_designs_added_action)
+ .with(author: user, project: project)
expect { run_service }
.to change { Event.count }.by(1)
@@ -119,6 +120,11 @@ RSpec.describe DesignManagement::SaveDesignsService do
)
end
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_DESIGNS_ADDED }
+ subject(:service_action) { run_service }
+ end
+
it 'can run the same command in parallel' do
parellism = 4
@@ -206,11 +212,17 @@ RSpec.describe DesignManagement::SaveDesignsService do
end
it 'updates UsageData for changed designs' do
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_designs_modified_action).with(author: user)
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_designs_modified_action)
+ .with(author: user, project: project)
run_service
end
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_DESIGNS_MODIFIED }
+ subject(:service_action) { run_service }
+ end
+
it 'records the correct events' do
counter = Gitlab::UsageDataCounters::DesignsCounter
expect { run_service }
diff --git a/spec/services/discussions/capture_diff_note_positions_service_spec.rb b/spec/services/discussions/capture_diff_note_positions_service_spec.rb
index 25e5f549bee..8ba54495d4c 100644
--- a/spec/services/discussions/capture_diff_note_positions_service_spec.rb
+++ b/spec/services/discussions/capture_diff_note_positions_service_spec.rb
@@ -18,8 +18,8 @@ RSpec.describe Discussions::CaptureDiffNotePositionsService do
def build_position(diff_refs, new_line: nil, old_line: nil)
path = 'files/markdown/ruby-style-guide.md'
- Gitlab::Diff::Position.new(old_path: path, new_path: path,
- new_line: new_line, old_line: old_line, diff_refs: diff_refs)
+ Gitlab::Diff::Position.new(
+ old_path: path, new_path: path, new_line: new_line, old_line: old_line, diff_refs: diff_refs)
end
def note_for(new_line: nil, old_line: nil)
diff --git a/spec/services/environments/stop_service_spec.rb b/spec/services/environments/stop_service_spec.rb
index 3ed8a0b1da0..4f766b73710 100644
--- a/spec/services/environments/stop_service_spec.rb
+++ b/spec/services/environments/stop_service_spec.rb
@@ -193,7 +193,7 @@ RSpec.describe Environments::StopService do
end
it 'has active environment at first' do
- expect(pipeline.environments_in_self_and_descendants.first).to be_available
+ expect(pipeline.environments_in_self_and_project_descendants.first).to be_available
end
context 'when user is a developer' do
@@ -201,21 +201,43 @@ RSpec.describe Environments::StopService do
project.add_developer(user)
end
+ 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) }
+
+ before do
+ subject
+ end
+
+ it 'stops the associated created_environments' do
+ expect(environment1.reload).to be_stopped
+ expect(environment2.reload).to be_stopped
+ 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
+ end
+ end
+
it 'stops the active environment' do
subject
- expect(pipeline.environments_in_self_and_descendants.first).to be_stopping
+ expect(pipeline.environments_in_self_and_project_descendants.first).to be_stopping
end
context 'when pipeline is a branch pipeline for merge request' do
let(:pipeline) do
- create(:ci_pipeline, source: :push, project: project, sha: merge_request.diff_head_sha,
- merge_requests_as_head_pipeline: [merge_request])
+ create(:ci_pipeline,
+ source: :push,
+ project: project,
+ sha: merge_request.diff_head_sha,
+ merge_requests_as_head_pipeline: [merge_request])
end
it 'does not stop the active environment' do
subject
- expect(pipeline.environments_in_self_and_descendants.first).to be_available
+ expect(pipeline.environments_in_self_and_project_descendants.first).to be_available
end
end
@@ -241,7 +263,7 @@ RSpec.describe Environments::StopService do
it 'does not stop the active environment' do
subject
- expect(pipeline.environments_in_self_and_descendants.first).to be_available
+ expect(pipeline.environments_in_self_and_project_descendants.first).to be_available
end
end
@@ -265,7 +287,7 @@ RSpec.describe Environments::StopService do
it 'does not stop the active environment' do
subject
- expect(pipeline.environments_in_self_and_descendants.first).to be_available
+ expect(pipeline.environments_in_self_and_project_descendants.first).to be_available
end
end
end
diff --git a/spec/services/event_create_service_spec.rb b/spec/services/event_create_service_spec.rb
index e66b413a5c9..06f0eb1efbc 100644
--- a/spec/services/event_create_service_spec.rb
+++ b/spec/services/event_create_service_spec.rb
@@ -420,9 +420,9 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
service.save_designs(author, create: [design])
expect_snowplow_event(
- category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
+ category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
action: 'create',
- namespace: design.project.namespace,
+ namespace: design.project.namespace,
user: author,
project: design.project,
label: 'design_users'
@@ -433,9 +433,9 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
service.save_designs(author, update: [design])
expect_snowplow_event(
- category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
+ category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
action: 'update',
- namespace: design.project.namespace,
+ namespace: design.project.namespace,
user: author,
project: design.project,
label: 'design_users'
@@ -481,9 +481,9 @@ RSpec.describe EventCreateService, :clean_gitlab_redis_cache, :clean_gitlab_redi
service.destroy_designs([design], author)
expect_snowplow_event(
- category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
+ category: Gitlab::UsageDataCounters::TrackUniqueEvents::DESIGN_ACTION.to_s,
action: 'destroy',
- namespace: design.project.namespace,
+ namespace: design.project.namespace,
user: author,
project: design.project,
label: 'design_users'
diff --git a/spec/services/git/branch_hooks_service_spec.rb b/spec/services/git/branch_hooks_service_spec.rb
index 5de1c0e27be..973ead28462 100644
--- a/spec/services/git/branch_hooks_service_spec.rb
+++ b/spec/services/git/branch_hooks_service_spec.rb
@@ -596,7 +596,7 @@ RSpec.describe Git::BranchHooksService, :clean_gitlab_redis_shared_state do
end
end
- project.repository.multi_action(
+ project.repository.commit_files(
user, message: 'message', branch_name: branch, actions: actions
)
end
diff --git a/spec/services/git/branch_push_service_spec.rb b/spec/services/git/branch_push_service_spec.rb
index 6280f1263c3..a9f5b07fef4 100644
--- a/spec/services/git/branch_push_service_spec.rb
+++ b/spec/services/git/branch_push_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Git::BranchPushService, services: true do
+RSpec.describe Git::BranchPushService, :use_clean_rails_redis_caching, services: true do
include RepoHelpers
let_it_be(:user) { create(:user) }
@@ -285,7 +285,7 @@ RSpec.describe Git::BranchPushService, services: true do
author_email: commit_author.email
)
- allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit)
+ allow(Commit).to receive(:build_from_sidekiq_hash)
.and_return(commit)
allow(project.repository).to receive(:commits_between).and_return([commit])
@@ -348,7 +348,7 @@ RSpec.describe Git::BranchPushService, services: true do
committed_date: commit_time
)
- allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit)
+ allow(Commit).to receive(:build_from_sidekiq_hash)
.and_return(commit)
allow(project.repository).to receive(:commits_between).and_return([commit])
@@ -387,7 +387,7 @@ RSpec.describe Git::BranchPushService, services: true do
allow(project.repository).to receive(:commits_between)
.and_return([closing_commit])
- allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit)
+ allow(Commit).to receive(:build_from_sidekiq_hash)
.and_return(closing_commit)
project.add_maintainer(commit_author)
diff --git a/spec/services/git/wiki_push_service_spec.rb b/spec/services/git/wiki_push_service_spec.rb
index 7e5d7066e89..878a5c4ccf0 100644
--- a/spec/services/git/wiki_push_service_spec.rb
+++ b/spec/services/git/wiki_push_service_spec.rb
@@ -9,9 +9,13 @@ RSpec.describe Git::WikiPushService, services: true do
let_it_be(:key_id) { create(:key, user: current_user).shell_id }
let(:wiki) { create(:project_wiki, user: current_user) }
- let(:git_wiki) { wiki.wiki }
+ let(:default_branch) { wiki.default_branch }
let(:repository) { wiki.repository }
+ before do
+ repository.create_if_not_exists(default_branch)
+ end
+
describe '#execute' do
it 'executes model-specific callbacks' do
expect(wiki).to receive(:after_post_receive)
@@ -351,7 +355,12 @@ RSpec.describe Git::WikiPushService, services: true do
# that have not gone through our services.
def write_new_page
- generate(:wiki_page_title).tap { |t| git_wiki.write_page(t, 'markdown', 'Hello', commit_details) }
+ generate(:wiki_page_title).tap do |t|
+ repository.create_file(
+ current_user, ::Wiki.sluggified_full_path(t, 'md'), 'Hello',
+ **commit_details
+ )
+ end
end
# We write something to the wiki-repo that is not a page - as, for example, an
@@ -368,15 +377,26 @@ RSpec.describe Git::WikiPushService, services: true do
def update_page(title, new_title = nil)
new_title = title unless new_title.present?
- page = git_wiki.page(title: title)
- git_wiki.update_page(page.path, new_title, 'markdown', 'Hey', commit_details)
+
+ old_path = ::Wiki.sluggified_full_path(title, 'md')
+ new_path = ::Wiki.sluggified_full_path(new_title, 'md')
+
+ repository.update_file(
+ current_user, new_path, 'Hey',
+ **commit_details.merge(previous_path: old_path)
+ )
end
def delete_page(page)
- wiki.delete_page(page, 'commit message')
+ repository.delete_file(current_user, page.path, **commit_details)
end
def commit_details
- create(:git_wiki_commit_details, author: current_user)
+ {
+ branch_name: default_branch,
+ message: "commit message",
+ author_email: current_user.email,
+ author_name: current_user.name
+ }
end
end
diff --git a/spec/services/google_cloud/enable_cloudsql_service_spec.rb b/spec/services/google_cloud/enable_cloudsql_service_spec.rb
index e54e5a8d446..f267f6d3bc2 100644
--- a/spec/services/google_cloud/enable_cloudsql_service_spec.rb
+++ b/spec/services/google_cloud/enable_cloudsql_service_spec.rb
@@ -23,6 +23,11 @@ RSpec.describe GoogleCloud::EnableCloudsqlService do
project.save!
end
+ after do
+ project.variables.destroy_all # rubocop:disable Cop/DestroyAll
+ project.save!
+ end
+
it 'enables cloudsql, compute and service networking Google APIs', :aggregate_failures do
expect_next_instance_of(GoogleApi::CloudPlatform::Client) do |instance|
expect(instance).to receive(:enable_cloud_sql_admin).with('prj-prod')
@@ -35,5 +40,22 @@ RSpec.describe GoogleCloud::EnableCloudsqlService do
expect(result[:status]).to eq(:success)
end
+
+ context 'when Google APIs raise an error' do
+ it 'returns error result' do
+ allow_next_instance_of(GoogleApi::CloudPlatform::Client) do |instance|
+ allow(instance).to receive(:enable_cloud_sql_admin).with('prj-prod')
+ allow(instance).to receive(:enable_compute).with('prj-prod')
+ allow(instance).to receive(:enable_service_networking).with('prj-prod')
+ allow(instance).to receive(:enable_cloud_sql_admin).with('prj-staging')
+ allow(instance).to receive(:enable_compute).with('prj-staging')
+ allow(instance).to receive(:enable_service_networking).with('prj-staging')
+ .and_raise(Google::Apis::Error.new('error'))
+ end
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:message]).to eq('error')
+ end
+ end
end
end
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
new file mode 100644
index 00000000000..b83037f80cd
--- /dev/null
+++ b/spec/services/google_cloud/fetch_google_ip_list_service_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GoogleCloud::FetchGoogleIpListService,
+ :use_clean_rails_memory_store_caching, :clean_gitlab_redis_rate_limiting do
+ include StubRequests
+
+ let(:google_cloud_ips) { File.read(Rails.root.join('spec/fixtures/cdn/google_cloud.json')) }
+ let(:headers) { { 'Content-Type' => 'application/json' } }
+
+ subject { described_class.new.execute }
+
+ before do
+ WebMock.stub_request(:get, described_class::GOOGLE_IP_RANGES_URL)
+ .to_return(status: 200, body: google_cloud_ips, headers: headers)
+ end
+
+ describe '#execute' do
+ it 'returns a list of IPAddr subnets and caches the result' do
+ expect(::ObjectStorage::CDN::GoogleIpCache).to receive(:update!).and_call_original
+ expect(subject[:subnets]).to be_an(Array)
+ expect(subject[:subnets]).to all(be_an(IPAddr))
+ end
+
+ shared_examples 'IP range retrieval failure' do
+ it 'does not cache the result and logs an error' do
+ expect(Gitlab::ErrorTracking).to receive(:log_exception).and_call_original
+ expect(::ObjectStorage::CDN::GoogleIpCache).not_to receive(:update!)
+ expect(subject[:subnets]).to be_nil
+ end
+ end
+
+ context 'with rate limit in effect' do
+ before do
+ 10.times { described_class.new.execute }
+ end
+
+ it 'returns rate limit error' do
+ expect(subject[:status]).to eq(:error)
+ expect(subject[:message]).to eq("#{described_class} was rate limited")
+ end
+ end
+
+ context 'when the URL returns a 404' do
+ before do
+ WebMock.stub_request(:get, described_class::GOOGLE_IP_RANGES_URL).to_return(status: 404)
+ end
+
+ it_behaves_like 'IP range retrieval failure'
+ end
+
+ context 'when the URL returns too large of a payload' do
+ before do
+ stub_const("#{described_class}::RESPONSE_BODY_LIMIT", 300)
+ end
+
+ it_behaves_like 'IP range retrieval failure'
+ end
+
+ context 'when the URL returns HTML' do
+ let(:headers) { { 'Content-Type' => 'text/html' } }
+
+ it_behaves_like 'IP range retrieval failure'
+ end
+
+ context 'when the URL returns empty results' do
+ let(:google_cloud_ips) { '{}' }
+
+ it_behaves_like 'IP range retrieval failure'
+ end
+ end
+end
diff --git a/spec/services/groups/create_service_spec.rb b/spec/services/groups/create_service_spec.rb
index 0cfde9ef434..0a8164c9ca3 100644
--- a/spec/services/groups/create_service_spec.rb
+++ b/spec/services/groups/create_service_spec.rb
@@ -79,7 +79,7 @@ RSpec.describe Groups::CreateService, '#execute' do
it { is_expected.to be_persisted }
it 'adds an onboarding progress record' do
- expect { subject }.to change(OnboardingProgress, :count).from(0).to(1)
+ expect { subject }.to change(Onboarding::Progress, :count).from(0).to(1)
end
context 'with before_commit callback' do
@@ -108,7 +108,7 @@ RSpec.describe Groups::CreateService, '#execute' do
it { is_expected.to be_persisted }
it 'does not add an onboarding progress record' do
- expect { subject }.not_to change(OnboardingProgress, :count).from(0)
+ expect { subject }.not_to change(Onboarding::Progress, :count).from(0)
end
it_behaves_like 'has sync-ed traversal_ids'
diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb
index 0d699dd447b..9288793cc7a 100644
--- a/spec/services/groups/destroy_service_spec.rb
+++ b/spec/services/groups/destroy_service_spec.rb
@@ -35,17 +35,38 @@ RSpec.describe Groups::DestroyService do
it { expect(NotificationSetting.unscoped.all).not_to include(notification_setting) }
end
- context 'bot tokens', :sidekiq_might_not_need_inline do
- it 'removes group bot', :aggregate_failures do
- bot = create(:user, :project_bot)
- group.add_developer(bot)
- token = create(:personal_access_token, user: bot)
+ context 'bot tokens', :sidekiq_inline do
+ context 'when user_destroy_with_limited_execution_time_worker is enabled' do
+ it 'initiates group bot removal', :aggregate_failures do
+ bot = create(:user, :project_bot)
+ group.add_developer(bot)
+ create(:personal_access_token, user: bot)
+
+ destroy_group(group, user, async)
+
+ expect(
+ Users::GhostUserMigration.where(user: bot,
+ initiator_user: user)
+ ).to be_exists
+ end
+ end
- destroy_group(group, user, async)
+ context 'when user_destroy_with_limited_execution_time_worker is disabled' do
+ before do
+ stub_feature_flags(user_destroy_with_limited_execution_time_worker: false)
+ end
+
+ it 'removes group bot', :aggregate_failures do
+ bot = create(:user, :project_bot)
+ group.add_developer(bot)
+ token = create(:personal_access_token, user: bot)
- expect(PersonalAccessToken.find_by(id: token.id)).to be_nil
- expect(User.find_by(id: bot.id)).to be_nil
- expect(User.find_by(id: user.id)).not_to be_nil
+ destroy_group(group, user, async)
+
+ expect(PersonalAccessToken.find_by(id: token.id)).to be_nil
+ expect(User.find_by(id: bot.id)).to be_nil
+ expect(User.find_by(id: user.id)).not_to be_nil
+ 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 292f2e2b86b..a4dfec4723a 100644
--- a/spec/services/groups/import_export/import_service_spec.rb
+++ b/spec/services/groups/import_export/import_service_spec.rb
@@ -149,9 +149,9 @@ RSpec.describe Groups::ImportExport::ImportService do
it 'logs the import success' do
expect(import_logger).to receive(:info).with(
- group_id: group.id,
+ group_id: group.id,
group_name: group.name,
- message: 'Group Import/Export: Import succeeded'
+ message: 'Group Import/Export: Import succeeded'
).once
subject
@@ -161,9 +161,9 @@ RSpec.describe Groups::ImportExport::ImportService do
context 'when user does not have correct permissions' do
it 'logs the error and raises an exception' do
expect(import_logger).to receive(:error).with(
- group_id: group.id,
+ group_id: group.id,
group_name: group.name,
- message: a_string_including('Errors occurred')
+ message: a_string_including('Errors occurred')
)
expect { subject }.to raise_error(Gitlab::ImportExport::Error)
@@ -186,9 +186,9 @@ RSpec.describe Groups::ImportExport::ImportService do
it 'logs the error and raises an exception' do
expect(import_logger).to receive(:error).with(
- group_id: group.id,
+ group_id: group.id,
group_name: group.name,
- message: a_string_including('Errors occurred')
+ message: a_string_including('Errors occurred')
).once
expect { subject }.to raise_error(Gitlab::ImportExport::Error)
@@ -267,9 +267,9 @@ RSpec.describe Groups::ImportExport::ImportService do
it 'logs the import success' do
expect(import_logger).to receive(:info).with(
- group_id: group.id,
+ group_id: group.id,
group_name: group.name,
- message: 'Group Import/Export: Import succeeded'
+ message: 'Group Import/Export: Import succeeded'
).once
subject
@@ -279,9 +279,9 @@ RSpec.describe Groups::ImportExport::ImportService do
context 'when user does not have correct permissions' do
it 'logs the error and raises an exception' do
expect(import_logger).to receive(:error).with(
- group_id: group.id,
+ group_id: group.id,
group_name: group.name,
- message: a_string_including('Errors occurred')
+ message: a_string_including('Errors occurred')
)
expect { subject }.to raise_error(Gitlab::ImportExport::Error)
@@ -304,9 +304,9 @@ RSpec.describe Groups::ImportExport::ImportService do
it 'logs the error and raises an exception' do
expect(import_logger).to receive(:error).with(
- group_id: group.id,
+ group_id: group.id,
group_name: group.name,
- message: a_string_including('Errors occurred')
+ message: a_string_including('Errors occurred')
).once
expect { subject }.to raise_error(Gitlab::ImportExport::Error)
@@ -328,9 +328,9 @@ RSpec.describe Groups::ImportExport::ImportService do
allow(Gitlab::Import::Logger).to receive(:build).and_return(import_logger)
expect(import_logger).to receive(:info).with(
- group_id: group.id,
+ group_id: group.id,
group_name: group.name,
- message: 'Group Import/Export: Import succeeded'
+ message: 'Group Import/Export: Import succeeded'
)
subject
diff --git a/spec/services/import/github_service_spec.rb b/spec/services/import/github_service_spec.rb
index 1c26677cfa5..67a2c237e43 100644
--- a/spec/services/import/github_service_spec.rb
+++ b/spec/services/import/github_service_spec.rb
@@ -56,7 +56,7 @@ RSpec.describe Import::GithubService do
end
context 'repository size validation' do
- let(:repository_double) { double(name: 'repository', size: 99) }
+ let(:repository_double) { { name: 'repository', size: 99 } }
before do
expect(client).to receive(:repository).and_return(repository_double)
@@ -84,7 +84,7 @@ RSpec.describe Import::GithubService do
end
it 'returns error when the repository is larger than the limit' do
- allow(repository_double).to receive(:size).and_return(101)
+ repository_double[:size] = 101
expect(subject.execute(access_params, :github)).to include(size_limit_error)
end
@@ -103,7 +103,7 @@ RSpec.describe Import::GithubService do
end
it 'returns error when the repository is larger than the limit' do
- allow(repository_double).to receive(:size).and_return(101)
+ repository_double[:size] = 101
expect(subject.execute(access_params, :github)).to include(size_limit_error)
end
@@ -113,14 +113,14 @@ RSpec.describe Import::GithubService do
context 'when import source is disabled' do
let(:repository_double) do
- double({
+ {
name: 'vim',
description: 'test',
full_name: 'test/vim',
clone_url: 'http://repo.com/repo/repo.git',
private: false,
has_wiki?: false
- })
+ }
end
before do
diff --git a/spec/services/issuable/bulk_update_service_spec.rb b/spec/services/issuable/bulk_update_service_spec.rb
index 55e0e799c19..dc72cf04776 100644
--- a/spec/services/issuable/bulk_update_service_spec.rb
+++ b/spec/services/issuable/bulk_update_service_spec.rb
@@ -47,7 +47,7 @@ RSpec.describe Issuable::BulkUpdateService do
let(:bulk_update_params) do
{
- add_label_ids: add_labels.map(&:id),
+ add_label_ids: add_labels.map(&:id),
remove_label_ids: remove_labels.map(&:id)
}
end
diff --git a/spec/services/issues/build_service_spec.rb b/spec/services/issues/build_service_spec.rb
index 304e4bb3ebb..838e0675372 100644
--- a/spec/services/issues/build_service_spec.rb
+++ b/spec/services/issues/build_service_spec.rb
@@ -63,12 +63,14 @@ RSpec.describe Issues::BuildService do
it 'wraps the note in a blockquote' do
note_text = "This is a string\n"\
+ "\n"\
">>>\n"\
"with a blockquote\n"\
"> That has a quote\n"\
">>>\n"
note_result = " > This is a string\n"\
" > \n"\
+ " > \n"\
" > > with a blockquote\n"\
" > > > That has a quote\n"\
" > \n"
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index 80c455e72b0..4a84862b9d5 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -416,7 +416,7 @@ RSpec.describe Issues::CreateService do
context "when issuable feature is private" do
before do
project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE,
- merge_requests_access_level: ProjectFeature::PRIVATE)
+ merge_requests_access_level: ProjectFeature::PRIVATE)
end
levels = [Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC]
@@ -555,24 +555,29 @@ RSpec.describe Issues::CreateService do
expect(reloaded_discussion.last_note.system).to eq(true)
end
- it 'assigns the title and description for the issue' do
- issue = described_class.new(project: project, current_user: user, params: opts, spam_params: spam_params).execute
+ it 'sets default title and description values if not provided' do
+ issue = described_class.new(
+ project: project, current_user: user,
+ params: opts,
+ spam_params: spam_params
+ ).execute
- expect(issue.title).not_to be_nil
- expect(issue.description).not_to be_nil
+ expect(issue).to be_persisted
+ expect(issue.title).to eq("Follow-up from \"#{merge_request.title}\"")
+ expect(issue.description).to include("The following discussion from #{merge_request.to_reference} should be addressed")
end
- it 'can set nil explicitly to the title and description' do
+ it 'takes params from the request over the default values' do
issue = described_class.new(project: project, current_user: user,
- params: {
- merge_request_to_resolve_discussions_of: merge_request,
- description: nil,
- title: nil
- },
+ params: opts.merge(
+ description: 'Custom issue description',
+ title: 'My new issue'
+ ),
spam_params: spam_params).execute
- expect(issue.description).to be_nil
- expect(issue.title).to be_nil
+ expect(issue).to be_persisted
+ expect(issue.description).to eq('Custom issue description')
+ expect(issue.title).to eq('My new issue')
end
end
@@ -594,24 +599,29 @@ RSpec.describe Issues::CreateService do
expect(reloaded_discussion.last_note.system).to eq(true)
end
- it 'assigns the title and description for the issue' do
- issue = described_class.new(project: project, current_user: user, params: opts, spam_params: spam_params).execute
+ it 'sets default title and description values if not provided' do
+ issue = described_class.new(
+ project: project, current_user: user,
+ params: opts,
+ spam_params: spam_params
+ ).execute
- expect(issue.title).not_to be_nil
- expect(issue.description).not_to be_nil
+ expect(issue).to be_persisted
+ expect(issue.title).to eq("Follow-up from \"#{merge_request.title}\"")
+ expect(issue.description).to include("The following discussion from #{merge_request.to_reference} should be addressed")
end
- it 'can set nil explicitly to the title and description' do
+ it 'takes params from the request over the default values' do
issue = described_class.new(project: project, current_user: user,
- params: {
- merge_request_to_resolve_discussions_of: merge_request,
- description: nil,
- title: nil
- },
+ params: opts.merge(
+ description: 'Custom issue description',
+ title: 'My new issue'
+ ),
spam_params: spam_params).execute
- expect(issue.description).to be_nil
- expect(issue.title).to be_nil
+ expect(issue).to be_persisted
+ expect(issue.description).to eq('Custom issue description')
+ expect(issue.title).to eq('My new issue')
end
end
end
diff --git a/spec/services/issues/relative_position_rebalancing_service_spec.rb b/spec/services/issues/relative_position_rebalancing_service_spec.rb
index 20064bd7e4b..37a94e1d6a2 100644
--- a/spec/services/issues/relative_position_rebalancing_service_spec.rb
+++ b/spec/services/issues/relative_position_rebalancing_service_spec.rb
@@ -72,22 +72,8 @@ RSpec.describe Issues::RelativePositionRebalancingService, :clean_gitlab_redis_s
end.not_to change { issues_in_position_order.map(&:id) }
end
- it 'does nothing if the feature flag is disabled' do
- stub_feature_flags(rebalance_issues: false)
- issue = project.issues.first
- issue.project
- issue.project.group
- old_pos = issue.relative_position
-
- # fetching root namespace in the initializer triggers 2 queries:
- # for fetching a random project from collection and fetching the root namespace.
- expect { service.execute }.not_to exceed_query_limit(2)
- expect(old_pos).to eq(issue.reload.relative_position)
- end
-
it 'acts if the flag is enabled for the root namespace' do
issue = create(:issue, project: project, author: user, relative_position: max_pos)
- stub_feature_flags(rebalance_issues: project.root_namespace)
expect { service.execute }.to change { issue.reload.relative_position }
end
@@ -95,7 +81,6 @@ RSpec.describe Issues::RelativePositionRebalancingService, :clean_gitlab_redis_s
it 'acts if the flag is enabled for the group' do
issue = create(:issue, project: project, author: user, relative_position: max_pos)
project.update!(group: create(:group))
- stub_feature_flags(rebalance_issues: issue.project.group)
expect { service.execute }.to change { issue.reload.relative_position }
end
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index aef3608831c..8a2e9ed74f7 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -48,6 +48,11 @@ RSpec.describe Issues::UpdateService, :mailer do
described_class.new(project: project, current_user: user, params: opts).execute(issue)
end
+ it_behaves_like 'issuable update service updating last_edited_at values' do
+ let(:issuable) { issue }
+ subject(:update_issuable) { update_issue(update_params) }
+ end
+
context 'valid params' do
let(:opts) do
{
@@ -299,38 +304,6 @@ RSpec.describe Issues::UpdateService, :mailer do
end
end
- it 'does not rebalance even if needed if the flag is disabled' do
- stub_feature_flags(rebalance_issues: false)
-
- range = described_class::NO_REBALANCING_NEEDED
- issue1 = create(:issue, project: project, relative_position: range.first - 100)
- issue2 = create(:issue, project: project, relative_position: range.first)
- issue.update!(relative_position: RelativePositioning::START_POSITION)
-
- opts[:move_between_ids] = [issue1.id, issue2.id]
-
- expect(Issues::RebalancingWorker).not_to receive(:perform_async)
-
- update_issue(opts)
- expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position)
- end
-
- it 'rebalances if needed if the flag is enabled for the project' do
- stub_feature_flags(rebalance_issues: project)
-
- range = described_class::NO_REBALANCING_NEEDED
- issue1 = create(:issue, project: project, relative_position: range.first - 100)
- issue2 = create(:issue, project: project, relative_position: range.first)
- issue.update!(relative_position: RelativePositioning::START_POSITION)
-
- opts[:move_between_ids] = [issue1.id, issue2.id]
-
- expect(Issues::RebalancingWorker).to receive(:perform_async).with(nil, nil, project.root_namespace.id)
-
- update_issue(opts)
- expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position)
- end
-
it 'rebalances if needed on the left' do
range = described_class::NO_REBALANCING_NEEDED
issue1 = create(:issue, project: project, relative_position: range.first - 100)
diff --git a/spec/services/members/create_service_spec.rb b/spec/services/members/create_service_spec.rb
index fe9f3ddc14d..25696ca209e 100644
--- a/spec/services/members/create_service_spec.rb
+++ b/spec/services/members/create_service_spec.rb
@@ -20,10 +20,10 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_
case source
when Project
source.add_maintainer(user)
- OnboardingProgress.onboard(source.namespace)
+ Onboarding::Progress.onboard(source.namespace)
when Group
source.add_owner(user)
- OnboardingProgress.onboard(source)
+ Onboarding::Progress.onboard(source)
end
end
@@ -59,7 +59,7 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_
it 'adds a user to members' do
expect(execute_service[:status]).to eq(:success)
expect(source.users).to include member
- expect(OnboardingProgress.completed?(source.namespace, :user_added)).to be(true)
+ expect(Onboarding::Progress.completed?(source.namespace, :user_added)).to be(true)
end
context 'when user_id is passed as an integer' do
@@ -68,7 +68,7 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_
it 'successfully creates member' do
expect(execute_service[:status]).to eq(:success)
expect(source.users).to include member
- expect(OnboardingProgress.completed?(source.namespace, :user_added)).to be(true)
+ expect(Onboarding::Progress.completed?(source.namespace, :user_added)).to be(true)
end
end
@@ -78,7 +78,7 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_
it 'successfully creates members' do
expect(execute_service[:status]).to eq(:success)
expect(source.users).to include(member, user_invited_by_id)
- expect(OnboardingProgress.completed?(source.namespace, :user_added)).to be(true)
+ expect(Onboarding::Progress.completed?(source.namespace, :user_added)).to be(true)
end
end
@@ -88,7 +88,7 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_
it 'successfully creates members' do
expect(execute_service[:status]).to eq(:success)
expect(source.users).to include(member, user_invited_by_id)
- expect(OnboardingProgress.completed?(source.namespace, :user_added)).to be(true)
+ expect(Onboarding::Progress.completed?(source.namespace, :user_added)).to be(true)
end
end
@@ -98,7 +98,7 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_
it 'adds a user to members' do
expect(execute_service[:status]).to eq(:success)
expect(source.users).to include member
- expect(OnboardingProgress.completed?(source, :user_added)).to be(true)
+ expect(Onboarding::Progress.completed?(source, :user_added)).to be(true)
end
it 'triggers a members added event' do
@@ -119,7 +119,7 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_
expect(execute_service[:status]).to eq(:error)
expect(execute_service[:message]).to be_present
expect(source.users).not_to include member
- expect(OnboardingProgress.completed?(source.namespace, :user_added)).to be(false)
+ expect(Onboarding::Progress.completed?(source.namespace, :user_added)).to be(false)
end
end
@@ -130,7 +130,7 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_
expect(execute_service[:status]).to eq(:error)
expect(execute_service[:message]).to be_present
expect(source.users).not_to include member
- expect(OnboardingProgress.completed?(source.namespace, :user_added)).to be(false)
+ expect(Onboarding::Progress.completed?(source.namespace, :user_added)).to be(false)
end
end
@@ -141,7 +141,7 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_
expect(execute_service[:status]).to eq(:error)
expect(execute_service[:message]).to include("#{member.username}: Access level is not included in the list")
expect(source.users).not_to include member
- expect(OnboardingProgress.completed?(source.namespace, :user_added)).to be(false)
+ expect(Onboarding::Progress.completed?(source.namespace, :user_added)).to be(false)
end
end
@@ -153,7 +153,7 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_
it 'allows already invited members to be re-invited by email and updates the member access' do
expect(execute_service[:status]).to eq(:success)
expect(invited_member.reset.access_level).to eq ProjectMember::MAINTAINER
- expect(OnboardingProgress.completed?(source.namespace, :user_added)).to be(true)
+ expect(Onboarding::Progress.completed?(source.namespace, :user_added)).to be(true)
end
end
@@ -170,7 +170,7 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_
it 'does not update the member' do
expect(execute_service[:status]).to eq(:error)
expect(execute_service[:message]).to eq("#{project_bot.username}: not authorized to update member")
- expect(OnboardingProgress.completed?(source.namespace, :user_added)).to be(false)
+ expect(Onboarding::Progress.completed?(source.namespace, :user_added)).to be(false)
end
end
@@ -178,7 +178,7 @@ RSpec.describe Members::CreateService, :aggregate_failures, :clean_gitlab_redis_
it 'adds the member' do
expect(execute_service[:status]).to eq(:success)
expect(source.users).to include project_bot
- expect(OnboardingProgress.completed?(source.namespace, :user_added)).to be(true)
+ expect(Onboarding::Progress.completed?(source.namespace, :user_added)).to be(true)
end
end
end
diff --git a/spec/services/merge_requests/approval_service_spec.rb b/spec/services/merge_requests/approval_service_spec.rb
index ab98fad5d73..0846ec7f50e 100644
--- a/spec/services/merge_requests/approval_service_spec.rb
+++ b/spec/services/merge_requests/approval_service_spec.rb
@@ -36,29 +36,15 @@ RSpec.describe MergeRequests::ApprovalService do
it 'does not publish MergeRequests::ApprovedEvent' do
expect { service.execute(merge_request) }.not_to publish_event(MergeRequests::ApprovedEvent)
end
+ end
- context 'async_after_approval feature flag is disabled' do
- before do
- stub_feature_flags(async_after_approval: false)
- end
-
- it 'does not create approve MR event' do
- expect(EventCreateService).not_to receive(:new)
-
- service.execute(merge_request)
- end
-
- it 'does not create an approval note' do
- expect(SystemNoteService).not_to receive(:approve_mr)
-
- service.execute(merge_request)
- end
-
- it 'does not mark pending todos as done' do
- service.execute(merge_request)
+ context 'with an already approved MR' do
+ before do
+ merge_request.approvals.create!(user: user)
+ end
- expect(todo.reload).to be_pending
- end
+ it 'does not create an approval' do
+ expect { service.execute(merge_request) }.not_to change { merge_request.approvals.size }
end
end
@@ -81,51 +67,6 @@ RSpec.describe MergeRequests::ApprovalService do
.to publish_event(MergeRequests::ApprovedEvent)
.with(current_user_id: user.id, merge_request_id: merge_request.id)
end
-
- context 'async_after_approval feature flag is disabled' do
- let(:notification_service) { NotificationService.new }
-
- before do
- stub_feature_flags(async_after_approval: false)
- allow(service).to receive(:notification_service).and_return(notification_service)
- end
-
- it 'creates approve MR event' do
- expect_next_instance_of(EventCreateService) do |instance|
- expect(instance).to receive(:approve_mr)
- .with(merge_request, user)
- end
-
- service.execute(merge_request)
- end
-
- it 'creates an approval note' do
- expect(SystemNoteService).to receive(:approve_mr).with(merge_request, user)
-
- service.execute(merge_request)
- end
-
- it 'marks pending todos as done' do
- service.execute(merge_request)
-
- expect(todo.reload).to be_done
- end
-
- it 'sends a notification when approving' do
- expect(notification_service).to receive_message_chain(:async, :approve_mr)
- .with(merge_request, user)
-
- service.execute(merge_request)
- end
-
- context 'with remaining approvals' do
- it 'fires an approval webhook' do
- expect(service).to receive(:execute_hooks).with(merge_request, 'approved')
-
- service.execute(merge_request)
- end
- end
- end
end
context 'user cannot update the merge request' do
diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb
index 3c9d2271ddc..6a6f01e6a95 100644
--- a/spec/services/merge_requests/build_service_spec.rb
+++ b/spec/services/merge_requests/build_service_spec.rb
@@ -20,18 +20,30 @@ RSpec.describe MergeRequests::BuildService do
let(:merge_request) { service.execute }
let(:compare) { double(:compare, commits: commits) }
let(:commit_1) do
- double(:commit_1, sha: 'f00ba6', safe_message: 'Initial commit',
- gitaly_commit?: false, id: 'f00ba6', parent_ids: ['f00ba5'])
+ double(:commit_1,
+ sha: 'f00ba6',
+ safe_message: 'Initial commit',
+ gitaly_commit?: false,
+ id: 'f00ba6',
+ parent_ids: ['f00ba5'])
end
let(:commit_2) do
- double(:commit_2, sha: 'f00ba7', safe_message: "Closes #1234 Second commit\n\nCreate the app",
- gitaly_commit?: false, id: 'f00ba7', parent_ids: ['f00ba6'])
+ double(:commit_2,
+ sha: 'f00ba7',
+ safe_message: "Closes #1234 Second commit\n\nCreate the app",
+ gitaly_commit?: false,
+ id: 'f00ba7',
+ parent_ids: ['f00ba6'])
end
let(:commit_3) do
- double(:commit_3, sha: 'f00ba8', safe_message: 'This is a bad commit message!',
- gitaly_commit?: false, id: 'f00ba8', parent_ids: ['f00ba7'])
+ double(:commit_3,
+ sha: 'f00ba8',
+ safe_message: 'This is a bad commit message!',
+ gitaly_commit?: false,
+ id: 'f00ba8',
+ parent_ids: ['f00ba7'])
end
let(:commits) { nil }
diff --git a/spec/services/merge_requests/create_pipeline_service_spec.rb b/spec/services/merge_requests/create_pipeline_service_spec.rb
index c443d758a77..dc96b5c0e5e 100644
--- a/spec/services/merge_requests/create_pipeline_service_spec.rb
+++ b/spec/services/merge_requests/create_pipeline_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe MergeRequests::CreatePipelineService do
+RSpec.describe MergeRequests::CreatePipelineService, :clean_gitlab_redis_cache do
include ProjectForksHelper
let_it_be(:project, reload: true) { create(:project, :repository) }
diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb
index 9c9bcb79990..4102cdc101e 100644
--- a/spec/services/merge_requests/create_service_spec.rb
+++ b/spec/services/merge_requests/create_service_spec.rb
@@ -434,7 +434,7 @@ RSpec.describe MergeRequests::CreateService, :clean_gitlab_redis_shared_state do
context "when issuable feature is private" do
before do
project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE,
- merge_requests_access_level: ProjectFeature::PRIVATE)
+ merge_requests_access_level: ProjectFeature::PRIVATE)
end
levels = [Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC]
diff --git a/spec/services/merge_requests/ff_merge_service_spec.rb b/spec/services/merge_requests/ff_merge_service_spec.rb
index 24a1a8b3113..aa5d6dcd1fb 100644
--- a/spec/services/merge_requests/ff_merge_service_spec.rb
+++ b/spec/services/merge_requests/ff_merge_service_spec.rb
@@ -75,6 +75,7 @@ RSpec.describe MergeRequests::FfMergeService do
expect(merge_request).to receive(:update_and_mark_in_progress_merge_commit_sha).twice.and_call_original
expect { execute_ff_merge }.not_to change { merge_request.squash_commit_sha }
+ expect(merge_request.merge_commit_sha).to be_nil
expect(merge_request.in_progress_merge_commit_sha).to be_nil
end
@@ -87,6 +88,7 @@ RSpec.describe MergeRequests::FfMergeService do
.to change { merge_request.squash_commit_sha }
.from(nil)
+ expect(merge_request.merge_commit_sha).to be_nil
expect(merge_request.in_progress_merge_commit_sha).to be_nil
end
end
@@ -106,7 +108,6 @@ RSpec.describe MergeRequests::FfMergeService do
service.execute(merge_request)
- expect(merge_request.merge_error).to include(error_message)
expect(Gitlab::AppLogger).to have_received(:error).with(a_string_matching(error_message))
end
@@ -117,11 +118,6 @@ RSpec.describe MergeRequests::FfMergeService do
pre_receive_error = Gitlab::Git::PreReceiveError.new(raw_message, fallback_message: error_message)
allow(service).to receive(:repository).and_raise(pre_receive_error)
allow(service).to receive(:execute_hooks)
- expect(Gitlab::ErrorTracking).to receive(:track_exception).with(
- pre_receive_error,
- pre_receive_message: raw_message,
- merge_request_id: merge_request.id
- )
service.execute(merge_request)
diff --git a/spec/services/merge_requests/handle_assignees_change_service_spec.rb b/spec/services/merge_requests/handle_assignees_change_service_spec.rb
index c43f5db6059..3db3efedb84 100644
--- a/spec/services/merge_requests/handle_assignees_change_service_spec.rb
+++ b/spec/services/merge_requests/handle_assignees_change_service_spec.rb
@@ -102,7 +102,7 @@ RSpec.describe MergeRequests::HandleAssigneesChangeService do
end
context 'when execute_hooks option is set to true' do
- let(:options) { { execute_hooks: true } }
+ let(:options) { { 'execute_hooks' => true } }
it 'executes hooks and integrations' do
expect(merge_request.project).to receive(:execute_hooks).with(anything, :merge_request_hooks)
diff --git a/spec/services/merge_requests/mergeability/detailed_merge_status_service_spec.rb b/spec/services/merge_requests/mergeability/detailed_merge_status_service_spec.rb
new file mode 100644
index 00000000000..5722bb79cc5
--- /dev/null
+++ b/spec/services/merge_requests/mergeability/detailed_merge_status_service_spec.rb
@@ -0,0 +1,97 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ::MergeRequests::Mergeability::DetailedMergeStatusService do
+ subject(:detailed_merge_status) { described_class.new(merge_request: merge_request).execute }
+
+ context 'when merge status is cannot_be_merged_rechecking' do
+ let(:merge_request) { create(:merge_request, merge_status: :cannot_be_merged_rechecking) }
+
+ it 'returns :checking' do
+ expect(detailed_merge_status).to eq(:checking)
+ end
+ end
+
+ context 'when merge status is preparing' do
+ let(:merge_request) { create(:merge_request, merge_status: :preparing) }
+
+ it 'returns :checking' do
+ expect(detailed_merge_status).to eq(:checking)
+ end
+ end
+
+ context 'when merge status is checking' do
+ let(:merge_request) { create(:merge_request, merge_status: :checking) }
+
+ it 'returns :checking' do
+ expect(detailed_merge_status).to eq(:checking)
+ end
+ end
+
+ context 'when merge status is unchecked' do
+ let(:merge_request) { create(:merge_request, merge_status: :unchecked) }
+
+ it 'returns :unchecked' do
+ expect(detailed_merge_status).to eq(:unchecked)
+ end
+ end
+
+ context 'when merge checks are a success' do
+ let(:merge_request) { create(:merge_request) }
+
+ it 'returns :mergeable' do
+ expect(detailed_merge_status).to eq(:mergeable)
+ end
+ end
+
+ context 'when merge status have a failure' do
+ let(:merge_request) { create(:merge_request) }
+
+ before do
+ merge_request.close!
+ end
+
+ it 'returns the failure reason' do
+ expect(detailed_merge_status).to eq(:not_open)
+ end
+ end
+
+ context 'when all but the ci check fails' do
+ let(:merge_request) { create(:merge_request) }
+
+ before do
+ merge_request.project.update!(only_allow_merge_if_pipeline_succeeds: true)
+ end
+
+ context 'when pipeline does not exist' do
+ it 'returns the failure reason' do
+ expect(detailed_merge_status).to eq(:ci_must_pass)
+ end
+ end
+
+ context 'when pipeline exists' do
+ before do
+ create(:ci_pipeline, ci_status, merge_request: merge_request,
+ project: merge_request.project, sha: merge_request.source_branch_sha,
+ head_pipeline_of: merge_request)
+ end
+
+ context 'when the pipeline is running' do
+ let(:ci_status) { :running }
+
+ it 'returns the failure reason' do
+ expect(detailed_merge_status).to eq(:ci_still_running)
+ end
+ end
+
+ context 'when the pipeline is not running' do
+ let(:ci_status) { :failed }
+
+ it 'returns the failure reason' do
+ expect(detailed_merge_status).to eq(:ci_must_pass)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/merge_requests/mergeability/logger_spec.rb b/spec/services/merge_requests/mergeability/logger_spec.rb
new file mode 100644
index 00000000000..a4d544884b9
--- /dev/null
+++ b/spec/services/merge_requests/mergeability/logger_spec.rb
@@ -0,0 +1,121 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe MergeRequests::Mergeability::Logger, :request_store do
+ let_it_be(:merge_request) { create(:merge_request) }
+
+ subject(:logger) { described_class.new(merge_request: merge_request) }
+
+ let(:caller_id) { 'a' }
+
+ before do
+ allow(Gitlab::ApplicationContext).to receive(:current_context_attribute).with(:caller_id).and_return(caller_id)
+ end
+
+ def loggable_data(**extras)
+ {
+ 'mergeability.expensive_operation.duration_s.values' => a_kind_of(Array),
+ "mergeability_merge_request_id" => merge_request.id,
+ "correlation_id" => a_kind_of(String),
+ "mergeability_project_id" => merge_request.project.id
+ }.merge(extras)
+ end
+
+ describe '#instrument' do
+ let(:operation_count) { 1 }
+
+ context 'when enabled' do
+ it "returns the block's value" do
+ expect(logger.instrument(mergeability_name: :expensive_operation) { 123 }).to eq(123)
+ end
+
+ it 'records durations of instrumented operations' do
+ expect_next_instance_of(Gitlab::AppJsonLogger) do |app_logger|
+ expect(app_logger).to receive(:info).with(match(a_hash_including(loggable_data)))
+ end
+
+ expect(logger.instrument(mergeability_name: :expensive_operation) { 123 }).to eq(123)
+
+ logger.commit
+ end
+
+ context 'with multiple observations' do
+ let(:operation_count) { 2 }
+
+ it 'records durations of instrumented operations' do
+ expect_next_instance_of(Gitlab::AppJsonLogger) do |app_logger|
+ expect(app_logger).to receive(:info).with(match(a_hash_including(loggable_data)))
+ end
+
+ 2.times do
+ expect(logger.instrument(mergeability_name: :expensive_operation) { 123 }).to eq(123)
+ end
+
+ logger.commit
+ end
+ end
+
+ context 'when its a query' do
+ let(:extra_data) do
+ {
+ 'mergeability.expensive_operation.db_count.values' => a_kind_of(Array),
+ 'mergeability.expensive_operation.db_main_count.values' => a_kind_of(Array),
+ 'mergeability.expensive_operation.db_main_duration_s.values' => a_kind_of(Array),
+ 'mergeability.expensive_operation.db_primary_count.values' => a_kind_of(Array),
+ 'mergeability.expensive_operation.db_primary_duration_s.values' => a_kind_of(Array)
+ }
+ end
+
+ context 'with a single query' do
+ it 'includes SQL metrics' do
+ expect_next_instance_of(Gitlab::AppJsonLogger) do |app_logger|
+ expect(app_logger).to receive(:info).with(match(a_hash_including(loggable_data(**extra_data))))
+ end
+
+ expect(logger.instrument(mergeability_name: :expensive_operation) { MergeRequest.count }).to eq(1)
+
+ logger.commit
+ end
+ end
+
+ context 'with multiple queries' do
+ it 'includes SQL metrics' do
+ expect_next_instance_of(Gitlab::AppJsonLogger) do |app_logger|
+ expect(app_logger).to receive(:info).with(match(a_hash_including(loggable_data(**extra_data))))
+ end
+
+ expect(logger.instrument(mergeability_name: :expensive_operation) { Project.count + MergeRequest.count })
+ .to eq(2)
+
+ logger.commit
+ end
+ end
+ end
+ end
+
+ context 'when disabled' do
+ before do
+ stub_feature_flags(mergeability_checks_logger: false)
+ end
+
+ it "returns the block's value" do
+ expect(logger.instrument(mergeability_name: :expensive_operation) { 123 }).to eq(123)
+ end
+
+ it 'does not call the logger' do
+ expect(Gitlab::AppJsonLogger).not_to receive(:new)
+
+ expect(logger.instrument(mergeability_name: :expensive_operation) { Project.count + MergeRequest.count })
+ .to eq(2)
+
+ logger.commit
+ end
+ end
+
+ it 'raises an error when block is not provided' do
+ expect { logger.instrument(mergeability_name: :expensive_operation) }
+ .to raise_error(ArgumentError, 'block not given')
+ end
+ end
+end
diff --git a/spec/services/merge_requests/mergeability/run_checks_service_spec.rb b/spec/services/merge_requests/mergeability/run_checks_service_spec.rb
index afea3e952a1..cf34923795e 100644
--- a/spec/services/merge_requests/mergeability/run_checks_service_spec.rb
+++ b/spec/services/merge_requests/mergeability/run_checks_service_spec.rb
@@ -69,6 +69,11 @@ RSpec.describe MergeRequests::Mergeability::RunChecksService do
expect(service).to receive(:read).with(merge_check: merge_check).and_return(success_result)
end
+ expect_next_instance_of(MergeRequests::Mergeability::Logger, merge_request: merge_request) do |logger|
+ expect(logger).to receive(:instrument).with(mergeability_name: 'check_ci_status_service').and_call_original
+ expect(logger).to receive(:commit)
+ end
+
expect(execute.success?).to eq(true)
end
end
@@ -80,6 +85,11 @@ RSpec.describe MergeRequests::Mergeability::RunChecksService do
expect(service).to receive(:write).with(merge_check: merge_check, result_hash: success_result.to_hash).and_return(true)
end
+ expect_next_instance_of(MergeRequests::Mergeability::Logger, merge_request: merge_request) do |logger|
+ expect(logger).to receive(:instrument).with(mergeability_name: 'check_ci_status_service').and_call_original
+ expect(logger).to receive(:commit)
+ end
+
expect(execute.success?).to eq(true)
end
end
diff --git a/spec/services/merge_requests/update_assignees_service_spec.rb b/spec/services/merge_requests/update_assignees_service_spec.rb
index f5f6f0ca301..3a0b17c2768 100644
--- a/spec/services/merge_requests/update_assignees_service_spec.rb
+++ b/spec/services/merge_requests/update_assignees_service_spec.rb
@@ -113,49 +113,6 @@ RSpec.describe MergeRequests::UpdateAssigneesService do
expect { service.execute(merge_request) }
.to issue_fewer_queries_than { update_service.execute(other_mr) }
end
-
- context 'setting state of assignees' do
- before do
- stub_feature_flags(mr_attention_requests: false)
- end
-
- it 'does not set state as attention_requested if feature flag is disabled' do
- update_merge_request
-
- expect(merge_request.merge_request_assignees[0].state).not_to eq('attention_requested')
- end
-
- context 'feature flag is enabled for current_user' do
- before do
- stub_feature_flags(mr_attention_requests: user)
- end
-
- it 'sets state as attention_requested' do
- update_merge_request
-
- expect(merge_request.merge_request_assignees[0].state).to eq('attention_requested')
- expect(merge_request.merge_request_assignees[0].updated_state_by).to eq(user)
- end
-
- it 'uses reviewers state if it is same user as new assignee' do
- merge_request.reviewers << user2
-
- update_merge_request
-
- expect(merge_request.merge_request_assignees[0].state).to eq('unreviewed')
- end
-
- context 'when assignee_ids matches existing assignee' do
- let(:opts) { { assignee_ids: [user3.id] } }
-
- it 'keeps original assignees state' do
- update_merge_request
-
- expect(merge_request.find_assignee(user3).state).to eq('unreviewed')
- end
- end
- end
- end
end
end
end
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index b7fb48718d8..8ebabd64d8a 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -47,6 +47,11 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
@merge_request.reload
end
+ it_behaves_like 'issuable update service updating last_edited_at values' do
+ let(:issuable) { merge_request }
+ subject(:update_issuable) { update_merge_request(update_params) }
+ end
+
context 'valid params' do
let(:locked) { true }
@@ -215,14 +220,6 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
MergeRequests::UpdateService.new(project: project, current_user: user, params: opts).execute(merge_request)
end
-
- it 'updates attention requested by of reviewer' do
- opts[:reviewers] = [user2]
-
- MergeRequests::UpdateService.new(project: project, current_user: user, params: opts).execute(merge_request)
-
- expect(merge_request.find_reviewer(user2).updated_state_by).to eq(user)
- end
end
context 'when reviewers did not change' do
@@ -328,49 +325,6 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
update_merge_request(reviewer_ids: [user.id])
end
-
- context 'setting state of reviewers' do
- before do
- stub_feature_flags(mr_attention_requests: false)
- end
-
- it 'does not set state as attention_requested if feature flag is disabled' do
- update_merge_request(reviewer_ids: [user.id])
-
- expect(merge_request.merge_request_reviewers[0].state).not_to eq('attention_requested')
- end
-
- context 'feature flag is enabled for current_user' do
- before do
- stub_feature_flags(mr_attention_requests: user)
- end
-
- it 'sets state as attention_requested' do
- update_merge_request(reviewer_ids: [user2.id])
-
- expect(merge_request.merge_request_reviewers[0].state).to eq('attention_requested')
- expect(merge_request.merge_request_reviewers[0].updated_state_by).to eq(user)
- end
-
- it 'keeps original reviewers state' do
- merge_request.find_reviewer(user2).update!(state: :unreviewed)
-
- update_merge_request({
- reviewer_ids: [user2.id]
- })
-
- expect(merge_request.find_reviewer(user2).state).to eq('unreviewed')
- end
-
- it 'uses reviewers state if it is same user as new assignee' do
- merge_request.assignees << user
-
- update_merge_request(reviewer_ids: [user.id])
-
- expect(merge_request.merge_request_reviewers[0].state).to eq('unreviewed')
- end
- end
- end
end
it 'creates a resource label event' do
@@ -561,9 +515,9 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
before do
create(:ci_pipeline,
project: project,
- ref: merge_request.source_branch,
- sha: merge_request.diff_head_sha,
- status: :success)
+ ref: merge_request.source_branch,
+ sha: merge_request.diff_head_sha,
+ status: :success)
perform_enqueued_jobs do
@merge_request = service.execute(merge_request)
@@ -895,7 +849,7 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
@merge_request = described_class.new(project: project, current_user: user, params: opts).execute(merge_request)
end
- should_not_email(subscriber)
+ should_email(subscriber)
should_not_email(non_subscriber)
end
@@ -1133,53 +1087,6 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
end
end
end
-
- context 'setting state of assignees' do
- before do
- stub_feature_flags(mr_attention_requests: false)
- end
-
- it 'does not set state as attention_requested if feature flag is disabled' do
- update_merge_request({
- assignee_ids: [user2.id]
- })
-
- expect(merge_request.merge_request_assignees[0].state).not_to eq('attention_requested')
- end
-
- context 'feature flag is enabled for current_user' do
- before do
- stub_feature_flags(mr_attention_requests: user)
- end
-
- it 'sets state as attention_requested' do
- update_merge_request({
- assignee_ids: [user2.id]
- })
-
- expect(merge_request.merge_request_assignees[0].state).to eq('attention_requested')
- expect(merge_request.merge_request_assignees[0].updated_state_by).to eq(user)
- end
-
- it 'keeps original assignees state' do
- update_merge_request({
- assignee_ids: [user3.id]
- })
-
- expect(merge_request.find_assignee(user3).state).to eq('unreviewed')
- end
-
- it 'uses reviewers state if it is same user as new assignee' do
- merge_request.reviewers << user2
-
- update_merge_request({
- assignee_ids: [user2.id]
- })
-
- expect(merge_request.merge_request_assignees[0].state).to eq('unreviewed')
- end
- end
- end
end
context 'when adding time spent' do
diff --git a/spec/services/metrics/dashboard/clone_dashboard_service_spec.rb b/spec/services/metrics/dashboard/clone_dashboard_service_spec.rb
index b326fc1726d..47e5557105b 100644
--- a/spec/services/metrics/dashboard/clone_dashboard_service_spec.rb
+++ b/spec/services/metrics/dashboard/clone_dashboard_service_spec.rb
@@ -62,7 +62,7 @@ RSpec.describe Metrics::Dashboard::CloneDashboardService, :use_clean_rails_memor
start_branch: project.default_branch,
encoding: 'text',
file_path: ".gitlab/dashboards/custom_dashboard.yml",
- file_content: file_content_hash.to_yaml
+ file_content: file_content_hash.to_yaml
}
end
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index 37318d76586..4922e72b7a4 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -134,8 +134,7 @@ RSpec.describe Notes::CreateService do
context 'in a merge request' do
let_it_be(:project_with_repo) { create(:project, :repository) }
let_it_be(:merge_request) do
- create(:merge_request, source_project: project_with_repo,
- target_project: project_with_repo)
+ create(:merge_request, source_project: project_with_repo, target_project: project_with_repo)
end
context 'noteable highlight cache clearing' do
@@ -181,8 +180,7 @@ RSpec.describe Notes::CreateService do
it 'does not clear cache when note is not the first of the discussion' do
prev_note =
- create(:diff_note_on_merge_request, noteable: merge_request,
- project: project_with_repo)
+ create(:diff_note_on_merge_request, noteable: merge_request, project: project_with_repo)
reply_opts =
opts.merge(in_reply_to_discussion_id: prev_note.discussion_id,
type: 'DiffNote',
@@ -408,9 +406,9 @@ RSpec.describe Notes::CreateService do
expect(issuable.draft?).to eq(can_use_quick_action)
}
),
- # Remove draft status
+ # Remove draft (set ready) status
QuickAction.new(
- action_text: "/draft",
+ action_text: "/ready",
before_action: -> {
issuable.reload.update!(title: "Draft: title")
},
diff --git a/spec/services/notes/destroy_service_spec.rb b/spec/services/notes/destroy_service_spec.rb
index be95a4bb181..82caec52aee 100644
--- a/spec/services/notes/destroy_service_spec.rb
+++ b/spec/services/notes/destroy_service_spec.rb
@@ -57,13 +57,11 @@ RSpec.describe Notes::DestroyService do
context 'in a merge request' do
let_it_be(:repo_project) { create(:project, :repository) }
let_it_be(:merge_request) do
- create(:merge_request, source_project: repo_project,
- target_project: repo_project)
+ create(:merge_request, source_project: repo_project, target_project: repo_project)
end
let_it_be(:note) do
- create(:diff_note_on_merge_request, project: repo_project,
- noteable: merge_request)
+ create(:diff_note_on_merge_request, project: repo_project, noteable: merge_request)
end
it 'does not track issue comment removal usage data' do
@@ -84,9 +82,8 @@ RSpec.describe Notes::DestroyService do
end
it 'does not clear cache when note is not the first of the discussion' do
- reply_note = create(:diff_note_on_merge_request, in_reply_to: note,
- project: repo_project,
- noteable: merge_request)
+ reply_note = create(:diff_note_on_merge_request,
+ in_reply_to: note, project: repo_project, noteable: merge_request)
expect(merge_request).not_to receive(:diffs)
diff --git a/spec/services/onboarding/progress_service_spec.rb b/spec/services/onboarding/progress_service_spec.rb
new file mode 100644
index 00000000000..e9b8ea2e859
--- /dev/null
+++ b/spec/services/onboarding/progress_service_spec.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Onboarding::ProgressService do
+ describe '.async' do
+ let_it_be(:namespace) { create(:namespace) }
+ let_it_be(:action) { :git_pull }
+
+ subject(:execute_service) { described_class.async(namespace.id).execute(action: action) }
+
+ context 'when not onboarded' do
+ it 'does not schedule a worker' do
+ expect(Namespaces::OnboardingProgressWorker).not_to receive(:perform_async)
+
+ execute_service
+ end
+ end
+
+ context 'when onboarded' do
+ before do
+ Onboarding::Progress.onboard(namespace)
+ end
+
+ context 'when action is already completed' do
+ before do
+ Onboarding::Progress.register(namespace, action)
+ end
+
+ it 'does not schedule a worker' do
+ expect(Namespaces::OnboardingProgressWorker).not_to receive(:perform_async)
+
+ execute_service
+ end
+ end
+
+ context 'when action is not yet completed' do
+ it 'schedules a worker' do
+ expect(Namespaces::OnboardingProgressWorker).to receive(:perform_async)
+
+ execute_service
+ end
+ end
+ end
+ end
+
+ describe '#execute' do
+ let(:namespace) { create(:namespace) }
+ let(:action) { :namespace_action }
+
+ subject(:execute_service) { described_class.new(namespace).execute(action: :subscription_created) }
+
+ context 'when the namespace is a root' do
+ before do
+ Onboarding::Progress.onboard(namespace)
+ end
+
+ it 'registers a namespace onboarding progress action for the given namespace' do
+ execute_service
+
+ expect(Onboarding::Progress.completed?(namespace, :subscription_created)).to eq(true)
+ end
+ end
+
+ context 'when the namespace is not the root' do
+ let(:group) { create(:group, :nested) }
+
+ before do
+ Onboarding::Progress.onboard(group)
+ end
+
+ it 'does not register a namespace onboarding progress action' do
+ execute_service
+
+ expect(Onboarding::Progress.completed?(group, :subscription_created)).to be(nil)
+ end
+ end
+
+ context 'when no namespace is passed' do
+ let(:namespace) { nil }
+
+ it 'does not register a namespace onboarding progress action' do
+ execute_service
+
+ expect(Onboarding::Progress.completed?(namespace, :subscription_created)).to be(nil)
+ end
+ end
+ end
+end
diff --git a/spec/services/onboarding_progress_service_spec.rb b/spec/services/onboarding_progress_service_spec.rb
deleted file mode 100644
index ef4f4f0d822..00000000000
--- a/spec/services/onboarding_progress_service_spec.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe OnboardingProgressService do
- describe '.async' do
- let_it_be(:namespace) { create(:namespace) }
- let_it_be(:action) { :git_pull }
-
- subject(:execute_service) { described_class.async(namespace.id).execute(action: action) }
-
- context 'when not onboarded' do
- it 'does not schedule a worker' do
- expect(Namespaces::OnboardingProgressWorker).not_to receive(:perform_async)
-
- execute_service
- end
- end
-
- context 'when onboarded' do
- before do
- OnboardingProgress.onboard(namespace)
- end
-
- context 'when action is already completed' do
- before do
- OnboardingProgress.register(namespace, action)
- end
-
- it 'does not schedule a worker' do
- expect(Namespaces::OnboardingProgressWorker).not_to receive(:perform_async)
-
- execute_service
- end
- end
-
- context 'when action is not yet completed' do
- it 'schedules a worker' do
- expect(Namespaces::OnboardingProgressWorker).to receive(:perform_async)
-
- execute_service
- end
- end
- end
- end
-
- describe '#execute' do
- let(:namespace) { create(:namespace) }
- let(:action) { :namespace_action }
-
- subject(:execute_service) { described_class.new(namespace).execute(action: :subscription_created) }
-
- context 'when the namespace is a root' do
- before do
- OnboardingProgress.onboard(namespace)
- end
-
- it 'registers a namespace onboarding progress action for the given namespace' do
- execute_service
-
- expect(OnboardingProgress.completed?(namespace, :subscription_created)).to eq(true)
- end
- end
-
- context 'when the namespace is not the root' do
- let(:group) { create(:group, :nested) }
-
- before do
- OnboardingProgress.onboard(group)
- end
-
- it 'does not register a namespace onboarding progress action' do
- execute_service
-
- expect(OnboardingProgress.completed?(group, :subscription_created)).to be(nil)
- end
- end
-
- context 'when no namespace is passed' do
- let(:namespace) { nil }
-
- it 'does not register a namespace onboarding progress action' do
- execute_service
-
- expect(OnboardingProgress.completed?(namespace, :subscription_created)).to be(nil)
- end
- end
- end
-end
diff --git a/spec/services/packages/debian/parse_debian822_service_spec.rb b/spec/services/packages/debian/parse_debian822_service_spec.rb
index ff146fda250..a2731816459 100644
--- a/spec/services/packages/debian/parse_debian822_service_spec.rb
+++ b/spec/services/packages/debian/parse_debian822_service_spec.rb
@@ -77,7 +77,7 @@ RSpec.describe Packages::Debian::ParseDebian822Service do
'Depends' => '${shlibs:Depends}, ${misc:Depends}',
'Description' => "Some mostly empty lib\nUsed in GitLab tests.\n\nTesting another paragraph."
},
- 'Package: sample-udeb' => {
+ 'Package: sample-udeb' => {
'Package' => 'sample-udeb',
'Package-Type' => 'udeb',
'Architecture' => 'any',
diff --git a/spec/services/packages/debian/process_changes_service_spec.rb b/spec/services/packages/debian/process_changes_service_spec.rb
index 52bcddb6f5e..a45dd68cd6e 100644
--- a/spec/services/packages/debian/process_changes_service_spec.rb
+++ b/spec/services/packages/debian/process_changes_service_spec.rb
@@ -5,7 +5,8 @@ RSpec.describe Packages::Debian::ProcessChangesService 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_it_be(:incoming) { create(:debian_incoming, project: distribution.project) }
+
+ let!(:incoming) { create(:debian_incoming, project: distribution.project) }
let(:package_file) { incoming.package_files.last }
@@ -33,10 +34,11 @@ RSpec.describe Packages::Debian::ProcessChangesService do
existing_package.update!(debian_distribution: distribution)
end
- it 'does not create a package' do
+ it 'does not create a package and assigns the package_file to the existing package' do
expect { subject.execute }
.to not_change { Packages::Package.count }
.and not_change { Packages::PackageFile.count }
+ .and change(package_file, :package).to(existing_package)
end
context 'marked as pending_destruction' do
diff --git a/spec/services/packages/rpm/repository_metadata/base_builder_spec.rb b/spec/services/packages/rpm/repository_metadata/base_builder_spec.rb
new file mode 100644
index 00000000000..0fb58cc27d5
--- /dev/null
+++ b/spec/services/packages/rpm/repository_metadata/base_builder_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Packages::Rpm::RepositoryMetadata::BaseBuilder do
+ describe '#execute' do
+ subject { described_class.new.execute }
+
+ before do
+ stub_const("#{described_class}::ROOT_TAG", 'test')
+ stub_const("#{described_class}::ROOT_ATTRIBUTES", { foo1: 'bar1', foo2: 'bar2' })
+ end
+
+ it 'generate valid xml' do
+ result = Nokogiri::XML::Document.parse(subject)
+
+ expect(result.children.count).to eq(1)
+ expect(result.children.first.attributes.count).to eq(2)
+ expect(result.children.first.attributes['foo1'].value).to eq('bar1')
+ expect(result.children.first.attributes['foo2'].value).to eq('bar2')
+ end
+ end
+end
diff --git a/spec/services/packages/rpm/repository_metadata/build_filelist_xml_spec.rb b/spec/services/packages/rpm/repository_metadata/build_filelist_xml_spec.rb
new file mode 100644
index 00000000000..2feb44c7c1b
--- /dev/null
+++ b/spec/services/packages/rpm/repository_metadata/build_filelist_xml_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Packages::Rpm::RepositoryMetadata::BuildFilelistXml do
+ describe '#execute' do
+ subject { described_class.new.execute }
+
+ context "when generate empty xml" do
+ let(:expected_xml) do
+ <<~XML
+ <?xml version="1.0" encoding="UTF-8"?>
+ <filelists xmlns="http://linux.duke.edu/metadata/filelists" packages="0"/>
+ XML
+ end
+
+ it 'generate expected xml' do
+ expect(subject).to eq(expected_xml)
+ end
+ end
+ end
+end
diff --git a/spec/services/packages/rpm/repository_metadata/build_other_xml_spec.rb b/spec/services/packages/rpm/repository_metadata/build_other_xml_spec.rb
new file mode 100644
index 00000000000..823aa18808a
--- /dev/null
+++ b/spec/services/packages/rpm/repository_metadata/build_other_xml_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Packages::Rpm::RepositoryMetadata::BuildOtherXml do
+ describe '#execute' do
+ subject { described_class.new.execute }
+
+ context "when generate empty xml" do
+ let(:expected_xml) do
+ <<~XML
+ <?xml version="1.0" encoding="UTF-8"?>
+ <otherdata xmlns="http://linux.duke.edu/metadata/other" packages="0"/>
+ XML
+ end
+
+ it 'generate expected xml' do
+ expect(subject).to eq(expected_xml)
+ end
+ end
+ end
+end
diff --git a/spec/services/packages/rpm/repository_metadata/build_primary_xml_spec.rb b/spec/services/packages/rpm/repository_metadata/build_primary_xml_spec.rb
new file mode 100644
index 00000000000..f5294d6f7f7
--- /dev/null
+++ b/spec/services/packages/rpm/repository_metadata/build_primary_xml_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Packages::Rpm::RepositoryMetadata::BuildPrimaryXml do
+ describe '#execute' do
+ subject { described_class.new.execute }
+
+ context "when generate empty xml" do
+ let(:expected_xml) do
+ <<~XML
+ <?xml version="1.0" encoding="UTF-8"?>
+ <metadata xmlns="http://linux.duke.edu/metadata/common" xmlns:rpm="http://linux.duke.edu/metadata/rpm" packages="0"/>
+ XML
+ end
+
+ it 'generate expected xml' do
+ expect(subject).to eq(expected_xml)
+ end
+ end
+ end
+end
diff --git a/spec/services/packages/rpm/repository_metadata/build_repomd_xml_spec.rb b/spec/services/packages/rpm/repository_metadata/build_repomd_xml_spec.rb
new file mode 100644
index 00000000000..29b0f73e3c1
--- /dev/null
+++ b/spec/services/packages/rpm/repository_metadata/build_repomd_xml_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Packages::Rpm::RepositoryMetadata::BuildRepomdXml do
+ describe '#execute' do
+ subject { described_class.new(data).execute }
+
+ let(:data) do
+ {
+ filelists: {
+ checksum: { type: "sha256", value: "123" },
+ 'open-checksum': { type: "sha256", value: "123" },
+ location: { href: "repodata/123-filelists.xml.gz" },
+ timestamp: { value: 1644602784 },
+ size: { value: 11111 },
+ 'open-size': { value: 11111 }
+ },
+ primary: {
+ checksum: { type: "sha256", value: "234" },
+ 'open-checksum': { type: "sha256", value: "234" },
+ location: { href: "repodata/234-primary.xml.gz" },
+ timestamp: { value: 1644602784 },
+ size: { value: 22222 },
+ 'open-size': { value: 22222 }
+ },
+ other: {
+ checksum: { type: "sha256", value: "345" },
+ 'open-checksum': { type: "sha256", value: "345" },
+ location: { href: "repodata/345-other.xml.gz" },
+ timestamp: { value: 1644602784 },
+ size: { value: 33333 },
+ 'open-size': { value: 33333 }
+ }
+ }
+ end
+
+ let(:creation_timestamp) { 111111 }
+
+ before do
+ allow(Time).to receive(:now).and_return(creation_timestamp)
+ end
+
+ it 'generate valid xml' do
+ # Have one root attribute
+ result = Nokogiri::XML::Document.parse(subject)
+ expect(result.children.count).to eq(1)
+
+ # Root attribute name is 'repomd'
+ root = result.children.first
+ expect(root.name).to eq('repomd')
+
+ # Have the same count of 'data' tags as count of keys in 'data'
+ expect(result.css('data').count).to eq(data.count)
+ end
+
+ it 'has all data info' do
+ result = Nokogiri::XML::Document.parse(subject).remove_namespaces!
+
+ data.each do |tag_name, tag_attributes|
+ tag_attributes.each_key do |key|
+ expect(result.at("//repomd/data[@type=\"#{tag_name}\"]/#{key}")).not_to be_nil
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/post_receive_service_spec.rb b/spec/services/post_receive_service_spec.rb
index 3f4d37e5ddc..aa955b3445b 100644
--- a/spec/services/post_receive_service_spec.rb
+++ b/spec/services/post_receive_service_spec.rb
@@ -3,6 +3,7 @@
require 'spec_helper'
RSpec.describe PostReceiveService do
+ include GitlabShellHelpers
include Gitlab::Routing
let_it_be(:user) { create(:user) }
@@ -13,7 +14,6 @@ RSpec.describe PostReceiveService do
let(:identifier) { 'key-123' }
let(:gl_repository) { "project-#{project.id}" }
let(:branch_name) { 'feature' }
- let(:secret_token) { Gitlab::Shell.secret_token }
let(:reference_counter) { double('ReferenceCounter') }
let(:push_options) { ['ci.skip', 'another push option'] }
let(:repository) { project.repository }
@@ -25,7 +25,6 @@ RSpec.describe PostReceiveService do
let(:params) do
{
gl_repository: gl_repository,
- secret_token: secret_token,
identifier: identifier,
changes: changes,
push_options: push_options
diff --git a/spec/services/projects/blame_service_spec.rb b/spec/services/projects/blame_service_spec.rb
index 54c4315d242..52b0ed3412d 100644
--- a/spec/services/projects/blame_service_spec.rb
+++ b/spec/services/projects/blame_service_spec.rb
@@ -54,6 +54,12 @@ RSpec.describe Projects::BlameService, :aggregate_failures do
it { is_expected.to eq(1..2) }
end
+ context 'when user disabled the pagination' do
+ let(:params) { super().merge(no_pagination: 1) }
+
+ it { is_expected.to be_nil }
+ end
+
context 'when feature flag disabled' do
before do
stub_feature_flags(blame_page_pagination: false)
@@ -75,6 +81,12 @@ RSpec.describe Projects::BlameService, :aggregate_failures do
expect(subject.total_count).to eq(4)
end
+ context 'when user disabled the pagination' do
+ let(:params) { super().merge(no_pagination: 1) }
+
+ it { is_expected.to be_nil }
+ end
+
context 'when feature flag disabled' do
before do
stub_feature_flags(blame_page_pagination: false)
diff --git a/spec/services/projects/container_repository/cleanup_tags_service_spec.rb b/spec/services/projects/container_repository/cleanup_tags_service_spec.rb
index 79904e2bf72..2008de195ab 100644
--- a/spec/services/projects/container_repository/cleanup_tags_service_spec.rb
+++ b/spec/services/projects/container_repository/cleanup_tags_service_spec.rb
@@ -5,11 +5,13 @@ require 'spec_helper'
RSpec.describe Projects::ContainerRepository::CleanupTagsService, :clean_gitlab_redis_cache do
using RSpec::Parameterized::TableSyntax
+ include_context 'for a cleanup tags service'
+
let_it_be(:user) { create(:user) }
let_it_be(:project, reload: true) { create(:project, :private) }
let(:repository) { create(:container_repository, :root, project: project) }
- let(:service) { described_class.new(repository, user, params) }
+ let(:service) { described_class.new(container_repository: repository, current_user: user, params: params) }
let(:tags) { %w[latest A Ba Bb C D E] }
before do
@@ -39,268 +41,141 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService, :clean_gitlab_
describe '#execute' do
subject { service.execute }
- context 'when no params are specified' do
- let(:params) { {} }
-
- it 'does not remove anything' do
- expect_any_instance_of(Projects::ContainerRepository::DeleteTagsService)
- .not_to receive(:execute)
- expect_no_caching
-
- is_expected.to eq(expected_service_response(before_truncate_size: 0, after_truncate_size: 0, before_delete_size: 0))
- end
- end
-
- context 'when regex matching everything is specified' do
- shared_examples 'removes all matches' do
- it 'does remove all tags except latest' do
- expect_no_caching
-
- expect_delete(%w(A Ba Bb C D E))
-
- is_expected.to eq(expected_service_response(deleted: %w(A Ba Bb C D E)))
- end
- end
-
- let(:params) do
- { 'name_regex_delete' => '.*' }
- end
-
- it_behaves_like 'removes all matches'
-
- context 'with deprecated name_regex param' do
- let(:params) do
- { 'name_regex' => '.*' }
- end
-
- it_behaves_like 'removes all matches'
- end
- end
-
- context 'with invalid regular expressions' do
- shared_examples 'handling an invalid regex' do
- it 'keeps all tags' do
- expect_no_caching
-
- expect(Projects::ContainerRepository::DeleteTagsService)
- .not_to receive(:new)
-
- subject
- end
-
- it { is_expected.to eq(status: :error, message: 'invalid regex') }
-
- it 'calls error tracking service' do
- expect(Gitlab::ErrorTracking).to receive(:log_exception).and_call_original
-
- subject
- end
- end
-
- context 'when name_regex_delete is invalid' do
- let(:params) { { 'name_regex_delete' => '*test*' } }
-
- it_behaves_like 'handling an invalid regex'
- end
-
- context 'when name_regex is invalid' do
- let(:params) { { 'name_regex' => '*test*' } }
-
- it_behaves_like 'handling an invalid regex'
- end
-
- context 'when name_regex_keep is invalid' do
- let(:params) { { 'name_regex_keep' => '*test*' } }
-
- it_behaves_like 'handling an invalid regex'
- end
- end
-
- context 'when delete regex matching specific tags is used' do
- let(:params) do
- { 'name_regex_delete' => 'C|D' }
- end
-
- it 'does remove C and D' do
- expect_delete(%w(C D))
-
- expect_no_caching
-
- is_expected.to eq(expected_service_response(deleted: %w(C D), before_truncate_size: 2, after_truncate_size: 2, before_delete_size: 2))
- end
-
- context 'with overriding allow regex' do
- let(:params) do
- { 'name_regex_delete' => 'C|D',
- 'name_regex_keep' => 'C' }
- end
-
- it 'does not remove C' do
- expect_delete(%w(D))
-
- expect_no_caching
-
- is_expected.to eq(expected_service_response(deleted: %w(D), before_truncate_size: 1, after_truncate_size: 1, before_delete_size: 1))
- end
- end
-
- context 'with name_regex_delete overriding deprecated name_regex' do
- let(:params) do
- { 'name_regex' => 'C|D',
- 'name_regex_delete' => 'D' }
- end
-
- it 'does not remove C' do
- expect_delete(%w(D))
-
- expect_no_caching
-
- is_expected.to eq(expected_service_response(deleted: %w(D), before_truncate_size: 1, after_truncate_size: 1, before_delete_size: 1))
- end
- end
- end
-
- context 'with allow regex value' do
- let(:params) do
- { 'name_regex_delete' => '.*',
- 'name_regex_keep' => 'B.*' }
- end
-
- it 'does not remove B*' do
- expect_delete(%w(A C D E))
-
- expect_no_caching
-
- is_expected.to eq(expected_service_response(deleted: %w(A C D E), before_truncate_size: 4, after_truncate_size: 4, before_delete_size: 4))
- end
- end
-
- context 'when keeping only N tags' do
- let(:params) do
- { 'name_regex' => 'A|B.*|C',
- 'keep_n' => 1 }
- end
-
- it 'sorts tags by date' do
- expect_delete(%w(Bb Ba C))
-
- expect_no_caching
-
- expect(service).to receive(:order_by_date).and_call_original
-
- is_expected.to eq(expected_service_response(deleted: %w(Bb Ba C), before_truncate_size: 4, after_truncate_size: 4, before_delete_size: 3))
- end
- end
-
- context 'when not keeping N tags' do
- let(:params) do
- { 'name_regex' => 'A|B.*|C' }
- end
-
- it 'does not sort tags by date' do
- expect_delete(%w(A Ba Bb C))
-
- expect_no_caching
-
- expect(service).not_to receive(:order_by_date)
-
- is_expected.to eq(expected_service_response(deleted: %w(A Ba Bb C), before_truncate_size: 4, after_truncate_size: 4, before_delete_size: 4))
- end
- end
-
- context 'when removing keeping only 3' do
- let(:params) do
- { 'name_regex_delete' => '.*',
- 'keep_n' => 3 }
- end
-
- it 'does remove B* and C as they are the oldest' do
- expect_delete(%w(Bb Ba C))
-
- expect_no_caching
-
- is_expected.to eq(expected_service_response(deleted: %w(Bb Ba C), before_delete_size: 3))
- end
- end
-
- context 'when removing older than 1 day' do
- let(:params) do
- { 'name_regex_delete' => '.*',
- 'older_than' => '1 day' }
- end
-
- it 'does remove B* and C as they are older than 1 day' do
- expect_delete(%w(Ba Bb C))
-
- expect_no_caching
-
- is_expected.to eq(expected_service_response(deleted: %w(Ba Bb C), before_delete_size: 3))
- end
- end
-
- context 'when combining all parameters' do
+ it_behaves_like 'handling invalid params',
+ service_response_extra: {
+ before_truncate_size: 0,
+ after_truncate_size: 0,
+ before_delete_size: 0,
+ cached_tags_count: 0
+ },
+ supports_caching: true
+
+ it_behaves_like 'when regex matching everything is specified',
+ delete_expectations: [%w(A Ba Bb C D E)],
+ service_response_extra: {
+ before_truncate_size: 6,
+ after_truncate_size: 6,
+ before_delete_size: 6,
+ 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,
+ after_truncate_size: 2,
+ before_delete_size: 2,
+ cached_tags_count: 0
+ },
+ supports_caching: true
+
+ it_behaves_like 'when delete regex matching specific tags is used with overriding allow regex',
+ service_response_extra: {
+ before_truncate_size: 1,
+ after_truncate_size: 1,
+ before_delete_size: 1,
+ cached_tags_count: 0
+ },
+ supports_caching: true
+
+ it_behaves_like 'with allow regex value',
+ delete_expectations: [%w(A C D E)],
+ service_response_extra: {
+ before_truncate_size: 4,
+ after_truncate_size: 4,
+ before_delete_size: 4,
+ cached_tags_count: 0
+ },
+ supports_caching: true
+
+ it_behaves_like 'when keeping only N tags',
+ delete_expectations: [%w(Bb Ba C)],
+ service_response_extra: {
+ before_truncate_size: 4,
+ after_truncate_size: 4,
+ before_delete_size: 3,
+ cached_tags_count: 0
+ },
+ supports_caching: true
+
+ it_behaves_like 'when not keeping N tags',
+ delete_expectations: [%w(A Ba Bb C)],
+ service_response_extra: {
+ before_truncate_size: 4,
+ after_truncate_size: 4,
+ before_delete_size: 4,
+ cached_tags_count: 0
+ },
+ supports_caching: true
+
+ it_behaves_like 'when removing keeping only 3',
+ delete_expectations: [%w(Bb Ba C)],
+ service_response_extra: {
+ before_truncate_size: 6,
+ after_truncate_size: 6,
+ before_delete_size: 3,
+ cached_tags_count: 0
+ },
+ supports_caching: true
+
+ it_behaves_like 'when removing older than 1 day',
+ delete_expectations: [%w(Ba Bb C)],
+ service_response_extra: {
+ before_truncate_size: 6,
+ after_truncate_size: 6,
+ before_delete_size: 3,
+ cached_tags_count: 0
+ },
+ supports_caching: true
+
+ it_behaves_like 'when combining all parameters',
+ delete_expectations: [%w(Bb Ba C)],
+ service_response_extra: {
+ before_truncate_size: 6,
+ after_truncate_size: 6,
+ before_delete_size: 3,
+ cached_tags_count: 0
+ },
+ supports_caching: true
+
+ it_behaves_like 'when running a container_expiration_policy',
+ delete_expectations: [%w(Bb Ba C)],
+ service_response_extra: {
+ before_truncate_size: 6,
+ after_truncate_size: 6,
+ before_delete_size: 3,
+ cached_tags_count: 0
+ },
+ supports_caching: true
+
+ context 'when running a container_expiration_policy with caching' do
+ let(:user) { nil }
let(:params) do
- { 'name_regex_delete' => '.*',
+ {
+ 'name_regex_delete' => '.*',
'keep_n' => 1,
- 'older_than' => '1 day' }
+ 'older_than' => '1 day',
+ 'container_expiration_policy' => true
+ }
end
- it 'does remove B* and C' do
- expect_delete(%w(Bb Ba C))
-
- expect_no_caching
+ it 'expects caching to be used' do
+ expect_delete(%w(Bb Ba C), container_expiration_policy: true)
+ expect_caching
- is_expected.to eq(expected_service_response(deleted: %w(Bb Ba C), before_delete_size: 3))
+ subject
end
- end
-
- context 'when running a container_expiration_policy' do
- let(:user) { nil }
-
- context 'with valid container_expiration_policy param' do
- let(:params) do
- { 'name_regex_delete' => '.*',
- 'keep_n' => 1,
- 'older_than' => '1 day',
- 'container_expiration_policy' => true }
- end
+ context 'when setting set to false' do
before do
- expect_delete(%w(Bb Ba C), container_expiration_policy: true)
- end
-
- it { is_expected.to eq(expected_service_response(deleted: %w(Bb Ba C), before_delete_size: 3)) }
-
- context 'caching' do
- it 'expects caching to be used' do
- expect_caching
-
- subject
- end
-
- context 'when setting set to false' do
- before do
- stub_application_setting(container_registry_expiration_policies_caching: false)
- end
-
- it 'does not use caching' do
- expect_no_caching
-
- subject
- end
- end
+ stub_application_setting(container_registry_expiration_policies_caching: false)
end
- end
- context 'without container_expiration_policy param' do
- let(:params) do
- { 'name_regex_delete' => '.*',
- 'keep_n' => 1,
- 'older_than' => '1 day' }
- end
+ it 'does not use caching' do
+ expect_delete(%w(Bb Ba C), container_expiration_policy: true)
+ expect_no_caching
- it 'fails' do
- is_expected.to eq(status: :error, message: 'access denied')
+ subject
end
end
end
@@ -322,10 +197,12 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService, :clean_gitlab_
service_response = expected_service_response(
status: status,
original_size: original_size,
+ deleted: nil
+ ).merge(
before_truncate_size: before_truncate_size,
after_truncate_size: after_truncate_size,
before_delete_size: before_delete_size,
- deleted: nil
+ cached_tags_count: 0
)
expect(result).to eq(service_response)
@@ -395,11 +272,8 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService, :clean_gitlab_
end
it 'caches the created_at values' do
- ::Gitlab::Redis::Cache.with do |redis|
- expect_mget(redis, tags_and_created_ats.keys)
-
- expect_set(redis, cacheable_tags)
- end
+ expect_mget(tags_and_created_ats.keys)
+ expect_set(cacheable_tags)
expect(subject).to include(cached_tags_count: 0)
end
@@ -412,12 +286,10 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService, :clean_gitlab_
end
it 'uses them' do
- ::Gitlab::Redis::Cache.with do |redis|
- expect_mget(redis, tags_and_created_ats.keys)
+ expect_mget(tags_and_created_ats.keys)
- # because C is already in cache, it should not be cached again
- expect_set(redis, cacheable_tags.except('C'))
- end
+ # because C is already in cache, it should not be cached again
+ expect_set(cacheable_tags.except('C'))
# We will ping the container registry for all tags *except* for C because it's cached
expect(ContainerRegistry::Blob).to receive(:new).with(repository, { "digest" => "sha256:configA" }).and_call_original
@@ -429,15 +301,27 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService, :clean_gitlab_
end
end
- def expect_mget(redis, keys)
- expect(redis).to receive(:mget).with(keys.map(&method(:cache_key))).and_call_original
+ def expect_mget(keys)
+ Gitlab::Redis::Cache.with do |redis|
+ expect(redis).to receive(:mget).with(keys.map(&method(:cache_key))).and_call_original
+ end
end
- def expect_set(redis, tags)
- tags.each do |tag_name, created_at|
+ def expect_set(tags)
+ selected_tags = tags.map do |tag_name, created_at|
ex = 1.day.seconds - (Time.zone.now - created_at).seconds
- if ex > 0
- expect(redis).to receive(:set).with(cache_key(tag_name), rfc3339(created_at), ex: ex.to_i)
+ [tag_name, created_at, ex.to_i] if ex.positive?
+ end.compact
+
+ return if selected_tags.count.zero?
+
+ Gitlab::Redis::Cache.with do |redis|
+ expect(redis).to receive(:pipelined).and_call_original
+
+ expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
+ selected_tags.each do |tag_name, created_at, ex|
+ expect(pipeline).to receive(:set).with(cache_key(tag_name), rfc3339(created_at), ex: ex)
+ end
end
end
end
@@ -476,38 +360,14 @@ RSpec.describe Projects::ContainerRepository::CleanupTagsService, :clean_gitlab_
end
end
- def expect_delete(tags, container_expiration_policy: nil)
- expect(Projects::ContainerRepository::DeleteTagsService)
- .to receive(:new)
- .with(repository.project, user, tags: tags, container_expiration_policy: container_expiration_policy)
- .and_call_original
-
- expect_any_instance_of(Projects::ContainerRepository::DeleteTagsService)
- .to receive(:execute)
- .with(repository) { { status: :success, deleted: tags } }
- end
-
- # all those -1 because the default tags on L13 have a "latest" that will be filtered out
- def expected_service_response(status: :success, deleted: [], original_size: tags.size, before_truncate_size: tags.size - 1, after_truncate_size: tags.size - 1, before_delete_size: tags.size - 1)
- {
- status: status,
- deleted: deleted,
- original_size: original_size,
- before_truncate_size: before_truncate_size,
- after_truncate_size: after_truncate_size,
- before_delete_size: before_delete_size,
- cached_tags_count: 0
- }.compact.merge(deleted_size: deleted&.size)
- end
-
- def expect_no_caching
- expect(::Gitlab::Redis::Cache).not_to receive(:with)
- end
-
def expect_caching
::Gitlab::Redis::Cache.with do |redis|
expect(redis).to receive(:mget).and_call_original
- expect(redis).to receive(:set).and_call_original
+ expect(redis).to receive(:pipelined).and_call_original
+
+ expect_next_instance_of(Redis::PipelinedConnection) do |pipeline|
+ expect(pipeline).to receive(:set).and_call_original
+ 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
new file mode 100644
index 00000000000..d2cdb667659
--- /dev/null
+++ b/spec/services/projects/container_repository/gitlab/cleanup_tags_service_spec.rb
@@ -0,0 +1,183 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::ContainerRepository::Gitlab::CleanupTagsService do
+ using RSpec::Parameterized::TableSyntax
+
+ include_context 'for a cleanup tags service'
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project, reload: true) { create(:project, :private) }
+
+ let(:repository) { create(:container_repository, :root, :import_done, project: project) }
+ let(:service) { described_class.new(container_repository: repository, current_user: user, params: params) }
+ let(:tags) { %w[latest A Ba Bb C D E] }
+
+ before do
+ project.add_maintainer(user) if user
+
+ stub_container_registry_config(enabled: true)
+
+ stub_const("#{described_class}::TAGS_PAGE_SIZE", tags_page_size)
+
+ one_hour_ago = 1.hour.ago
+ five_days_ago = 5.days.ago
+ six_days_ago = 6.days.ago
+ one_month_ago = 1.month.ago
+
+ stub_tags(
+ {
+ 'latest' => one_hour_ago,
+ 'A' => one_hour_ago,
+ 'Ba' => five_days_ago,
+ 'Bb' => six_days_ago,
+ 'C' => one_month_ago,
+ 'D' => nil,
+ 'E' => nil
+ }
+ )
+ end
+
+ describe '#execute' do
+ subject { service.execute }
+
+ context 'with several tags pages' do
+ let(:tags_page_size) { 2 }
+
+ it_behaves_like 'handling invalid params'
+
+ 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 delete regex matching specific tags is used'
+
+ it_behaves_like 'when delete regex matching specific tags is used with overriding allow regex'
+
+ it_behaves_like 'with allow regex value',
+ delete_expectations: [%w[A], %w[C D], %w[E]]
+
+ it_behaves_like 'when keeping only N tags',
+ delete_expectations: [%w[Bb]]
+
+ it_behaves_like 'when not keeping N tags',
+ delete_expectations: [%w[A], %w[Ba Bb], %w[C]]
+
+ context 'when removing keeping only 3' do
+ let(:params) do
+ {
+ 'name_regex_delete' => '.*',
+ 'keep_n' => 3
+ }
+ end
+
+ it_behaves_like 'not removing anything'
+ end
+
+ it_behaves_like 'when removing older than 1 day',
+ delete_expectations: [%w[Ba Bb], %w[C]]
+
+ it_behaves_like 'when combining all parameters',
+ delete_expectations: [%w[Bb], %w[C]]
+
+ it_behaves_like 'when running a container_expiration_policy',
+ delete_expectations: [%w[Bb], %w[C]]
+
+ context 'with a timeout' do
+ let(:params) do
+ { 'name_regex_delete' => '.*' }
+ end
+
+ it 'removes the first few pages' do
+ expect(service).to receive(:timeout?).and_return(false, true)
+
+ expect_delete(%w[A])
+ expect_delete(%w[Ba Bb])
+
+ response = expected_service_response(status: :error, deleted: %w[A Ba Bb], original_size: 4)
+
+ is_expected.to eq(response)
+ end
+ end
+ end
+
+ context 'with a single tags page' do
+ let(:tags_page_size) { 1000 }
+
+ it_behaves_like 'handling invalid params'
+
+ it_behaves_like 'when regex matching everything is specified',
+ delete_expectations: [%w[A Ba Bb C D 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'
+
+ it_behaves_like 'with allow regex value',
+ delete_expectations: [%w[A C D E]]
+
+ it_behaves_like 'when keeping only N tags',
+ delete_expectations: [%w[Ba Bb C]]
+
+ it_behaves_like 'when not keeping N tags',
+ delete_expectations: [%w[A Ba Bb C]]
+
+ it_behaves_like 'when removing keeping only 3',
+ delete_expectations: [%w[Ba Bb C]]
+
+ it_behaves_like 'when removing older than 1 day',
+ delete_expectations: [%w[Ba Bb C]]
+
+ it_behaves_like 'when combining all parameters',
+ delete_expectations: [%w[Ba Bb C]]
+
+ it_behaves_like 'when running a container_expiration_policy',
+ delete_expectations: [%w[Ba Bb C]]
+ end
+ end
+
+ private
+
+ def stub_tags(tags)
+ chunked = tags_page_size < tags.size
+ previous_last = nil
+ max_chunk_index = tags.size / tags_page_size
+
+ tags.keys.in_groups_of(tags_page_size, false).each_with_index do |chunked_tag_names, index|
+ last = index == max_chunk_index
+ pagination_needed = chunked && !last
+
+ response = {
+ pagination: pagination_needed ? pagination_with(last: chunked_tag_names.last) : {},
+ response_body: chunked_tag_names.map do |name|
+ tag_raw_response(name, tags[name])
+ end
+ }
+
+ allow(repository.gitlab_api_client)
+ .to receive(:tags)
+ .with(repository.path, page_size: described_class::TAGS_PAGE_SIZE, last: previous_last)
+ .and_return(response)
+ previous_last = chunked_tag_names.last
+ end
+ end
+
+ def pagination_with(last:)
+ {
+ next: {
+ uri: URI("http://test.org?last=#{last}")
+ }
+ }
+ end
+
+ def tag_raw_response(name, timestamp)
+ timestamp_field = name.start_with?('B') ? 'updated_at' : 'created_at'
+ {
+ 'name' => name,
+ 'digest' => 'sha256:1234567890',
+ 'media_type' => 'application/vnd.oci.image.manifest.v1+json',
+ timestamp_field => timestamp&.iso8601
+ }
+ end
+end
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index e112c1e2497..9c8aeb5cf7b 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -125,6 +125,26 @@ RSpec.describe Projects::CreateService, '#execute' do
expect(project.namespace).to eq(user.namespace)
expect(project.project_namespace).to be_in_sync_with_project(project)
end
+
+ context 'project_authorizations record creation' do
+ context 'when the project_authrizations records are not created via the callback' do
+ it 'still creates project_authrizations record for the user' do
+ # stub out the callback that creates project_authorizations records on the `ProjectMember` model.
+ expect_next_instance_of(ProjectMember) do |member|
+ expect(member).to receive(:refresh_member_authorized_projects).and_return(nil)
+ end
+
+ project = create_project(user, opts)
+
+ expected_record = project.project_authorizations.where(
+ user: user,
+ access_level: ProjectMember::OWNER
+ )
+
+ expect(expected_record).to exist
+ end
+ end
+ end
end
describe 'after create actions' do
@@ -417,10 +437,10 @@ RSpec.describe Projects::CreateService, '#execute' do
expect(imported_project.import_url).to eq('http://import-url')
end
- it 'tracks for the combined_registration experiment', :experiment do
- expect(experiment(:combined_registration)).to track(:import_project).on_next_instance
-
+ it 'tracks for imported project' do
imported_project
+
+ expect_snowplow_event(category: described_class.name, action: 'import_project', user: user)
end
describe 'import scheduling' do
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index 955384e518c..8269dbebccb 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -135,6 +135,33 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
end
end
+ context 'deleting a project with merge request diffs' do
+ let!(:merge_request) { create(:merge_request, source_project: project) }
+ let!(:another_project_mr) { create(:merge_request, source_project: create(:project)) }
+
+ it 'deletes merge request diffs' do
+ merge_request_diffs = merge_request.merge_request_diffs
+ expect(merge_request_diffs.size).to eq(1)
+
+ expect { destroy_project(project, user, {}) }.to change(MergeRequestDiff, :count).by(-1)
+ expect { another_project_mr.reload }.not_to raise_error
+ end
+
+ context 'when extract_mr_diff_deletions feature flag is disabled' do
+ before do
+ stub_feature_flags(extract_mr_diff_deletions: false)
+ end
+
+ it 'also deletes merge request diffs' do
+ merge_request_diffs = merge_request.merge_request_diffs
+ expect(merge_request_diffs.size).to eq(1)
+
+ expect { destroy_project(project, user, {}) }.to change(MergeRequestDiff, :count).by(-1)
+ expect { another_project_mr.reload }.not_to raise_error
+ end
+ end
+ end
+
it_behaves_like 'deleting the project'
it 'invalidates personal_project_count cache' do
@@ -312,7 +339,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
before do
stub_container_registry_tags(repository: project.full_path + '/image',
- tags: ['tag'])
+ tags: ['tag'])
project.container_repositories << container_repository
end
@@ -350,7 +377,7 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
context 'when there are tags for legacy root repository' do
before do
stub_container_registry_tags(repository: project.full_path,
- tags: ['tag'])
+ tags: ['tag'])
end
context 'when image repository tags deletion succeeds' do
@@ -423,11 +450,11 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
destroy_project(project, user)
end
- it 'calls the bulk snippet destroy service with the hard_delete param set to true' do
+ it 'calls the bulk snippet destroy service with the skip_authorization param set to true' do
expect(project.snippets.count).to eq 2
expect_next_instance_of(Snippets::BulkDestroyService, user, project.snippets) do |instance|
- expect(instance).to receive(:execute).with(hard_delete: true).and_call_original
+ expect(instance).to receive(:execute).with(skip_authorization: true).and_call_original
end
expect do
@@ -485,9 +512,11 @@ RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publi
let!(:project_bot) { create(:user, :project_bot).tap { |user| project.add_maintainer(user) } }
it 'deletes bot user as well' do
- expect do
- destroy_project(project, user)
- end.to change { User.find_by(id: project_bot.id) }.to(nil)
+ expect_next_instance_of(Users::DestroyService, user) do |instance|
+ expect(instance).to receive(:execute).with(project_bot, skip_authorization: true).and_call_original
+ end
+
+ destroy_project(project, user)
end
end
diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb
index 24b5e35e422..eea2ea3271f 100644
--- a/spec/services/projects/update_pages_service_spec.rb
+++ b/spec/services/projects/update_pages_service_spec.rb
@@ -19,6 +19,25 @@ RSpec.describe Projects::UpdatePagesService do
subject { described_class.new(project, build) }
+ context 'when a deploy stage already exists' do
+ let!(:stage) { create(:ci_stage, name: 'deploy', pipeline: pipeline) }
+
+ it 'assigns the deploy stage' do
+ subject.execute
+
+ expect(GenericCommitStatus.last.ci_stage).to eq(stage)
+ expect(GenericCommitStatus.last.ci_stage.name).to eq('deploy')
+ end
+ end
+
+ context 'when a deploy stage does not exists' do
+ it 'assigns the deploy stage' do
+ subject.execute
+
+ expect(GenericCommitStatus.last.ci_stage.name).to eq('deploy')
+ end
+ end
+
context 'for new artifacts' do
context "for a valid job" do
let!(:artifacts_archive) { create(:ci_job_artifact, :correct_checksum, file: file, job: build) }
diff --git a/spec/services/protected_branches/cache_service_spec.rb b/spec/services/protected_branches/cache_service_spec.rb
index 4fa7553c23d..00d1e8b5457 100644
--- a/spec/services/protected_branches/cache_service_spec.rb
+++ b/spec/services/protected_branches/cache_service_spec.rb
@@ -75,10 +75,12 @@ RSpec.describe ProtectedBranches::CacheService, :clean_gitlab_redis_cache 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/,
- 'project_id' => project.id,
- 'project_path' => project.full_path
+ {
+ 'class' => described_class.name,
+ 'message' => /Cache mismatch/,
+ 'project_id' => project.id,
+ 'project_path' => project.full_path
+ }
)
expect(service.fetch('main', dry_run: true) { false }).to eq(false)
diff --git a/spec/services/protected_branches/destroy_service_spec.rb b/spec/services/protected_branches/destroy_service_spec.rb
index 9fa07820148..123deeea005 100644
--- a/spec/services/protected_branches/destroy_service_spec.rb
+++ b/spec/services/protected_branches/destroy_service_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe ProtectedBranches::DestroyService do
let_it_be_with_reload(:project) { create(:project) }
- let(:protected_branch) { create(:protected_branch, project: project) }
+ let!(:protected_branch) { create(:protected_branch, project: project) }
let(:user) { project.first_owner }
subject(:service) { described_class.new(project, user) }
diff --git a/spec/services/protected_branches/update_service_spec.rb b/spec/services/protected_branches/update_service_spec.rb
index c4fe4d78070..2ff6c3c489a 100644
--- a/spec/services/protected_branches/update_service_spec.rb
+++ b/spec/services/protected_branches/update_service_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe ProtectedBranches::UpdateService do
let_it_be_with_reload(:project) { create(:project) }
- let(:protected_branch) { create(:protected_branch, project: project) }
+ let!(:protected_branch) { create(:protected_branch, project: project) }
let(:user) { project.first_owner }
let(:params) { { name: new_name } }
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index 2d38d968ce4..a43f3bc55bf 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -1393,14 +1393,41 @@ RSpec.describe QuickActions::InterpretService do
let(:issuable) { issue }
end
+ # /draft is a toggle (ff disabled)
it_behaves_like 'draft command' do
let(:content) { '/draft' }
let(:issuable) { merge_request }
+
+ before do
+ stub_feature_flags(draft_quick_action_non_toggle: false)
+ end
end
+ # /draft is a toggle (ff disabled)
it_behaves_like 'ready command' do
let(:content) { '/draft' }
let(:issuable) { merge_request }
+
+ before do
+ stub_feature_flags(draft_quick_action_non_toggle: false)
+ issuable.update!(title: issuable.draft_title)
+ end
+ end
+
+ # /draft is one way (ff enabled)
+ it_behaves_like 'draft command' do
+ let(:content) { '/draft' }
+ let(:issuable) { merge_request }
+ end
+
+ # /draft is one way (ff enabled)
+ it_behaves_like 'draft/ready command no action' do
+ let(:content) { '/draft' }
+ let(:issuable) { merge_request }
+
+ before do
+ issuable.update!(title: issuable.draft_title)
+ end
end
it_behaves_like 'draft/ready command no action' do
@@ -2646,7 +2673,28 @@ RSpec.describe QuickActions::InterpretService do
end
end
- describe 'draft command' do
+ describe 'draft command toggle (deprecated)' do
+ let(:content) { '/draft' }
+
+ before do
+ stub_feature_flags(draft_quick_action_non_toggle: false)
+ end
+
+ it 'includes the new status' do
+ _, explanations = service.explain(content, merge_request)
+
+ expect(explanations).to match_array(['Marks this merge request as a draft.'])
+ end
+
+ it 'sets the ready status on a draft' do
+ merge_request.update!(title: merge_request.draft_title)
+ _, explanations = service.explain(content, merge_request)
+
+ expect(explanations).to match_array(["Marks this merge request as ready."])
+ end
+ end
+
+ describe 'draft command set' do
let(:content) { '/draft' }
it 'includes the new status' do
@@ -2654,6 +2702,13 @@ RSpec.describe QuickActions::InterpretService do
expect(explanations).to match_array(['Marks this merge request as a draft.'])
end
+
+ it 'includes the no change message when status unchanged' do
+ merge_request.update!(title: merge_request.draft_title)
+ _, explanations = service.explain(content, merge_request)
+
+ expect(explanations).to match_array(["No change to this merge request's draft status."])
+ end
end
describe 'ready command' do
diff --git a/spec/services/releases/create_service_spec.rb b/spec/services/releases/create_service_spec.rb
index 2421fab0eec..5f49eed3e77 100644
--- a/spec/services/releases/create_service_spec.rb
+++ b/spec/services/releases/create_service_spec.rb
@@ -70,6 +70,28 @@ RSpec.describe Releases::CreateService do
expect(result[:release]).not_to be_nil
end
+ context 'and the tag would be protected' do
+ let!(:protected_tag) { create(:protected_tag, project: project, name: tag_name) }
+
+ context 'and the user does not have permissions' do
+ let(:user) { create(:user) }
+
+ before do
+ project.add_developer(user)
+ end
+
+ it 'raises an error' do
+ result = service.execute
+
+ expect(result[:status]).to eq(:error)
+ end
+ end
+
+ context 'and the user has permissions' do
+ it_behaves_like 'a successful release creation'
+ end
+ end
+
context 'and tag_message is provided' do
let(:ref) { 'master' }
let(:tag_name) { 'foobar' }
diff --git a/spec/services/resource_access_tokens/revoke_service_spec.rb b/spec/services/resource_access_tokens/revoke_service_spec.rb
index 3d724a79fef..8f89696cc55 100644
--- a/spec/services/resource_access_tokens/revoke_service_spec.rb
+++ b/spec/services/resource_access_tokens/revoke_service_spec.rb
@@ -29,18 +29,35 @@ RSpec.describe ResourceAccessTokens::RevokeService do
expect(resource.reload.users).not_to include(resource_bot)
end
- it 'transfer issuables of bot user to ghost user' do
- issue = create(:issue, author: resource_bot)
+ context 'when user_destroy_with_limited_execution_time_worker is enabled' do
+ it 'initiates user removal' do
+ subject
+
+ expect(
+ Users::GhostUserMigration.where(user: resource_bot,
+ initiator_user: user)
+ ).to be_exists
+ end
+ end
- subject
+ context 'when user_destroy_with_limited_execution_time_worker is disabled' do
+ before do
+ stub_feature_flags(user_destroy_with_limited_execution_time_worker: false)
+ end
- expect(issue.reload.author.ghost?).to be true
- end
+ it 'transfer issuables of bot user to ghost user' do
+ issue = create(:issue, author: resource_bot)
- it 'deletes project bot user' do
- subject
+ subject
+
+ expect(issue.reload.author.ghost?).to be true
+ end
+
+ it 'deletes project bot user' do
+ subject
- expect(User.exists?(resource_bot.id)).to be_falsy
+ expect(User.exists?(resource_bot.id)).to be_falsy
+ end
end
it 'logs the event' do
diff --git a/spec/services/resource_events/change_labels_service_spec.rb b/spec/services/resource_events/change_labels_service_spec.rb
index 8dc7b07e397..9b0ca54a394 100644
--- a/spec/services/resource_events/change_labels_service_spec.rb
+++ b/spec/services/resource_events/change_labels_service_spec.rb
@@ -98,20 +98,33 @@ RSpec.describe ResourceEvents::ChangeLabelsService do
let(:added) { [labels[0]] }
let(:removed) { [labels[1]] }
+ subject(:counter_class) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter }
+
context 'when resource is an issue' do
it 'tracks changed labels' do
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_label_changed_action)
+ expect(counter_class).to receive(:track_issue_label_changed_action)
change_labels
end
+
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_LABEL_CHANGED }
+ let(:user) { author }
+ subject(:service_action) { change_labels }
+ end
end
context 'when resource is a merge request' do
let(:resource) { create(:merge_request, source_project: project) }
it 'does not track changed labels' do
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter)
- .not_to receive(:track_issue_label_changed_action)
+ expect(counter_class).not_to receive(:track_issue_label_changed_action)
+
+ change_labels
+ end
+
+ it 'does not emit snowplow event', :snowplow do
+ expect_no_snowplow_event
change_labels
end
diff --git a/spec/services/security/ci_configuration/sast_parser_service_spec.rb b/spec/services/security/ci_configuration/sast_parser_service_spec.rb
index 1fd196cdcee..7a004e2915c 100644
--- a/spec/services/security/ci_configuration/sast_parser_service_spec.rb
+++ b/spec/services/security/ci_configuration/sast_parser_service_spec.rb
@@ -3,8 +3,6 @@
require 'spec_helper'
RSpec.describe Security::CiConfiguration::SastParserService do
- include Ci::TemplateHelpers
-
describe '#configuration' do
include_context 'read ci configuration for sast enabled project'
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 b863b2a46b0..5dbf5edb776 100644
--- a/spec/services/service_ping/submit_service_ping_service_spec.rb
+++ b/spec/services/service_ping/submit_service_ping_service_spec.rb
@@ -54,11 +54,13 @@ RSpec.describe ServicePing::SubmitService do
let(:service_ping_payload_url) { File.join(described_class::STAGING_BASE_URL, described_class::USAGE_DATA_PATH) }
let(:service_ping_errors_url) { File.join(described_class::STAGING_BASE_URL, described_class::ERROR_PATH) }
let(:service_ping_metadata_url) { File.join(described_class::STAGING_BASE_URL, described_class::METADATA_PATH) }
+ let!(:usage_data) { { uuid: 'uuid', recorded_at: Time.current } }
+
+ let(:subject) { described_class.new(payload: usage_data) }
shared_examples 'does not run' do
it do
expect(Gitlab::HTTP).not_to receive(:post)
- expect(Gitlab::Usage::ServicePingReport).not_to receive(:for)
subject.execute
end
@@ -69,7 +71,7 @@ RSpec.describe ServicePing::SubmitService do
expect(Gitlab::HTTP).not_to receive(:post).with(service_ping_payload_url, any_args)
expect { subject.execute }.to raise_error(described_class::SubmissionError) do |error|
- expect(error.message).to include('Usage data is blank')
+ expect(error.message).to include('Usage data payload is blank')
end
end
end
@@ -118,13 +120,18 @@ RSpec.describe ServicePing::SubmitService do
allow(ServicePing::ServicePingSettings).to receive(:product_intelligence_enabled?).and_return(true)
end
- it 'generates service ping' do
- stub_response(body: with_dev_ops_score_params)
- stub_response(body: nil, url: service_ping_metadata_url, status: 201)
+ it 'submits a service ping payload without errors', :aggregate_failures do
+ response = stub_response(body: with_dev_ops_score_params)
+ error_response = stub_response(body: nil, url: service_ping_errors_url, status: 201)
+ metadata_response = stub_response(body: nil, url: service_ping_metadata_url, status: 201)
- expect(Gitlab::Usage::ServicePingReport).to receive(:for).with(output: :all_metrics_values).and_call_original
+ expect(Gitlab::HTTP).to receive(:post).twice.and_call_original
subject.execute
+
+ expect(response).to have_been_requested
+ expect(error_response).not_to have_been_requested
+ expect(metadata_response).to have_been_requested
end
end
@@ -155,15 +162,9 @@ RSpec.describe ServicePing::SubmitService do
expect(response).to have_been_requested
end
- it 'forces a refresh of usage data statistics before submitting' do
- stub_response(body: with_dev_ops_score_params)
-
- expect(Gitlab::Usage::ServicePingReport).to receive(:for).with(output: :all_metrics_values).and_call_original
-
- subject.execute
- end
-
context 'when conv_index data is passed' do
+ let(:usage_data) { { uuid: 'uuid', recorded_at: Time.current } }
+
before do
stub_response(body: with_conv_index_params)
end
@@ -171,21 +172,17 @@ RSpec.describe ServicePing::SubmitService do
it_behaves_like 'saves DevOps report data from the response'
it 'saves usage_data_id to version_usage_data_id_value' do
- recorded_at = Time.current
- usage_data = { uuid: 'uuid', recorded_at: recorded_at }
-
- expect(Gitlab::Usage::ServicePingReport).to receive(:for).with(output: :all_metrics_values)
- .and_return(usage_data)
-
subject.execute
- raw_usage_data = RawUsageData.find_by(recorded_at: recorded_at)
+ raw_usage_data = RawUsageData.find_by(recorded_at: usage_data[:recorded_at])
expect(raw_usage_data.version_usage_data_id_value).to eq(31643)
end
end
context 'when only usage_data_id is passed in response' do
+ let(:usage_data) { { uuid: 'uuid', recorded_at: Time.current } }
+
before do
stub_response(body: with_usage_data_id_params)
end
@@ -195,15 +192,9 @@ RSpec.describe ServicePing::SubmitService do
end
it 'saves usage_data_id to version_usage_data_id_value' do
- recorded_at = Time.current
- usage_data = { uuid: 'uuid', recorded_at: recorded_at }
-
- expect(Gitlab::Usage::ServicePingReport).to receive(:for).with(output: :all_metrics_values)
- .and_return(usage_data)
-
subject.execute
- raw_usage_data = RawUsageData.find_by(recorded_at: recorded_at)
+ raw_usage_data = RawUsageData.find_by(recorded_at: usage_data[:recorded_at])
expect(raw_usage_data.version_usage_data_id_value).to eq(31643)
end
@@ -232,6 +223,8 @@ RSpec.describe ServicePing::SubmitService do
end
context 'with saving raw_usage_data' do
+ let(:usage_data) { { uuid: 'uuid', recorded_at: Time.current } }
+
before do
stub_response(body: with_dev_ops_score_params)
end
@@ -241,17 +234,10 @@ RSpec.describe ServicePing::SubmitService do
end
it 'saves the correct payload' do
- recorded_at = Time.current
- usage_data = { uuid: 'uuid', recorded_at: recorded_at }
-
- expect(Gitlab::Usage::ServicePingReport).to receive(:for).with(output: :all_metrics_values)
- .and_return(usage_data)
-
subject.execute
- raw_usage_data = RawUsageData.find_by(recorded_at: recorded_at)
+ raw_usage_data = RawUsageData.find_by(recorded_at: usage_data[:recorded_at])
- expect(raw_usage_data.recorded_at).to be_like_time(recorded_at)
expect(raw_usage_data.payload.to_json).to eq(usage_data.to_json)
end
end
@@ -269,90 +255,30 @@ RSpec.describe ServicePing::SubmitService do
end
context 'and usage data is empty string' do
- before do
- allow(Gitlab::Usage::ServicePingReport).to receive(:for).with(output: :all_metrics_values).and_return({})
- end
+ let(:usage_data) { {} }
it_behaves_like 'does not send a blank usage ping payload'
end
context 'and usage data is nil' do
- before do
- allow(ServicePing::BuildPayload).to receive(:execute).and_return(nil)
- allow(Gitlab::Usage::ServicePingReport).to receive(:for).with(output: :all_metrics_values).and_return(nil)
- end
+ let(:usage_data) { nil }
it_behaves_like 'does not send a blank usage ping payload'
end
- context 'if payload service fails' do
- before do
- stub_response(body: with_dev_ops_score_params)
-
- allow(ServicePing::BuildPayload).to receive_message_chain(:new, :execute)
- .and_raise(described_class::SubmissionError, 'SubmissionError')
- end
-
- it 'calls Gitlab::Usage::ServicePingReport .for method' do
- usage_data = build_usage_data
-
- expect(Gitlab::Usage::ServicePingReport).to receive(:for).with(output: :all_metrics_values)
- .and_return(usage_data)
-
- subject.execute
- end
-
- it 'submits error' do
- expect(Gitlab::HTTP).to receive(:post).with(URI.join(service_ping_payload_url), any_args)
- .and_call_original
- expect(Gitlab::HTTP).to receive(:post).with(URI.join(service_ping_errors_url), any_args)
- .and_call_original
- expect(Gitlab::HTTP).to receive(:post).with(URI.join(service_ping_metadata_url), any_args)
- .and_call_original
-
- subject.execute
- end
- end
-
- context 'calls BuildPayload first' do
- before do
- stub_response(body: with_dev_ops_score_params)
- end
-
- it 'returns usage data' do
- usage_data = build_usage_data
-
- expect_next_instance_of(ServicePing::BuildPayload) do |service|
- expect(service).to receive(:execute).and_return(usage_data)
- end
-
- subject.execute
- end
- end
-
context 'if version app response fails' do
before do
stub_response(body: with_dev_ops_score_params, status: 404)
-
- usage_data = build_usage_data
- allow_next_instance_of(ServicePing::BuildPayload) do |service|
- allow(service).to receive(:execute).and_return(usage_data)
- end
end
- it 'calls Gitlab::Usage::ServicePingReport .for method' do
- usage_data = build_usage_data
-
- expect(Gitlab::Usage::ServicePingReport).to receive(:for).with(output: :all_metrics_values)
- .and_return(usage_data)
-
+ it 'raises SubmissionError' do
# SubmissionError is raised as a result of 404 in response from HTTP Request
expect { subject.execute }.to raise_error(described_class::SubmissionError)
end
end
context 'when skip_db_write passed to service' do
- let(:subject) { ServicePing::SubmitService.new(skip_db_write: true) }
+ let(:subject) { described_class.new(payload: usage_data, skip_db_write: true) }
before do
stub_response(body: with_dev_ops_score_params)
@@ -377,21 +303,18 @@ RSpec.describe ServicePing::SubmitService do
stub_database_flavor_check
stub_application_setting(usage_ping_enabled: true)
stub_response(body: with_conv_index_params)
- allow_next_instance_of(ServicePing::BuildPayload) do |service|
- allow(service).to receive(:execute).and_return(payload)
- end
end
let(:metric_double) { instance_double(Gitlab::Usage::ServicePing::LegacyMetricTimingDecorator, duration: 123) }
- let(:payload) do
+ let(:usage_data) do
{
uuid: 'uuid',
- metric_a: metric_double,
- metric_group: {
+ metric_a: metric_double,
+ metric_group: {
metric_b: metric_double
},
- metric_without_timing: "value",
- recorded_at: Time.current
+ metric_without_timing: "value",
+ recorded_at: Time.current
}
end
@@ -399,10 +322,10 @@ RSpec.describe ServicePing::SubmitService do
{
metadata: {
uuid: 'uuid',
- metrics: [
- { name: 'metric_a', time_elapsed: 123 },
- { name: 'metric_group.metric_b', time_elapsed: 123 }
- ]
+ metrics: [
+ { name: 'metric_a', time_elapsed: 123 },
+ { name: 'metric_group.metric_b', time_elapsed: 123 }
+ ]
}
}
end
@@ -425,8 +348,4 @@ RSpec.describe ServicePing::SubmitService do
status: status
)
end
-
- def build_usage_data
- { uuid: 'uuid', recorded_at: Time.current }
- end
end
diff --git a/spec/services/service_response_spec.rb b/spec/services/service_response_spec.rb
index 3ede90fbc44..2d70979dd3a 100644
--- a/spec/services/service_response_spec.rb
+++ b/spec/services/service_response_spec.rb
@@ -43,14 +43,14 @@ RSpec.describe ServiceResponse do
end
describe '.error' do
- it 'creates a failed response without HTTP status' do
+ it 'creates an error response without HTTP status' do
response = described_class.error(message: 'Bad apple')
expect(response).to be_error
expect(response.message).to eq('Bad apple')
end
- it 'creates a failed response with HTTP status' do
+ it 'creates an error response with HTTP status' do
response = described_class.error(message: 'Bad apple', http_status: 400)
expect(response).to be_error
@@ -58,7 +58,7 @@ RSpec.describe ServiceResponse do
expect(response.http_status).to eq(400)
end
- it 'creates a failed response with payload' do
+ it 'creates an error response with payload' do
response = described_class.error(message: 'Bad apple',
payload: { bad: 'apple' })
@@ -66,6 +66,15 @@ RSpec.describe ServiceResponse do
expect(response.message).to eq('Bad apple')
expect(response.payload).to eq(bad: 'apple')
end
+
+ it 'creates an error response with a reason' do
+ response = described_class.error(message: 'Bad apple',
+ reason: :permission_denied)
+
+ expect(response).to be_error
+ expect(response.message).to eq('Bad apple')
+ expect(response.reason).to eq(:permission_denied)
+ end
end
describe '#success?' do
diff --git a/spec/services/snippets/bulk_destroy_service_spec.rb b/spec/services/snippets/bulk_destroy_service_spec.rb
index 2d2bdd116d1..4142aa349e4 100644
--- a/spec/services/snippets/bulk_destroy_service_spec.rb
+++ b/spec/services/snippets/bulk_destroy_service_spec.rb
@@ -71,8 +71,8 @@ RSpec.describe Snippets::BulkDestroyService do
let(:error_message) { "You don't have access to delete these snippets." }
end
- context 'when hard_delete option is passed' do
- subject { described_class.new(service_user, snippets).execute(hard_delete: true) }
+ context 'when skip_authorization option is passed' do
+ subject { described_class.new(service_user, snippets).execute(skip_authorization: true) }
it 'returns a ServiceResponse success response' do
expect(subject).to be_success
diff --git a/spec/services/spam/spam_action_service_spec.rb b/spec/services/spam/spam_action_service_spec.rb
index bd8418d7092..4dfec9735ba 100644
--- a/spec/services/spam/spam_action_service_spec.rb
+++ b/spec/services/spam/spam_action_service_spec.rb
@@ -6,6 +6,8 @@ RSpec.describe Spam::SpamActionService do
include_context 'includes Spam constants'
let(:issue) { create(:issue, project: project, author: author) }
+ let(:personal_snippet) { create(:personal_snippet, :public, author: author) }
+ let(:project_snippet) { create(:project_snippet, :public, author: author) }
let(:fake_ip) { '1.2.3.4' }
let(:fake_user_agent) { 'fake-user-agent' }
let(:fake_referer) { 'fake-http-referer' }
@@ -27,6 +29,7 @@ RSpec.describe Spam::SpamActionService do
before do
issue.spam = false
+ personal_snippet.spam = false
end
describe 'constructor argument validation' do
@@ -50,24 +53,24 @@ RSpec.describe Spam::SpamActionService do
end
end
- shared_examples 'creates a spam log' do
+ shared_examples 'creates a spam log' do |target_type|
it do
expect { subject }
- .to log_spam(title: issue.title, description: issue.description, noteable_type: 'Issue')
+ .to log_spam(title: target.title, description: target.description, noteable_type: target_type)
# TODO: These checks should be incorporated into the `log_spam` RSpec matcher above
new_spam_log = SpamLog.last
expect(new_spam_log.user_id).to eq(user.id)
- expect(new_spam_log.title).to eq(issue.title)
- expect(new_spam_log.description).to eq(issue.description)
+ expect(new_spam_log.title).to eq(target.title)
+ expect(new_spam_log.description).to eq(target.spam_description)
expect(new_spam_log.source_ip).to eq(fake_ip)
expect(new_spam_log.user_agent).to eq(fake_user_agent)
- expect(new_spam_log.noteable_type).to eq('Issue')
+ expect(new_spam_log.noteable_type).to eq(target_type)
expect(new_spam_log.via_api).to eq(true)
end
end
- describe '#execute' do
+ shared_examples 'execute spam action service' do |target_type|
let(:fake_captcha_verification_service) { double(:captcha_verification_service) }
let(:fake_verdict_service) { double(:spam_verdict_service) }
let(:allowlisted) { false }
@@ -82,20 +85,22 @@ RSpec.describe Spam::SpamActionService do
let(:verdict_service_args) do
{
- target: issue,
+ target: target,
user: user,
options: verdict_service_opts,
context: {
action: :create,
- target_type: 'Issue'
- }
+ target_type: target_type
+ },
+ extra_features: extra_features
}
end
let_it_be(:existing_spam_log) { create(:spam_log, user: user, recaptcha_verified: false) }
subject do
- described_service = described_class.new(spammable: issue, spam_params: spam_params, user: user, action: :create)
+ described_service = described_class.new(spammable: target, spam_params: spam_params, extra_features:
+ extra_features, user: user, action: :create)
allow(described_service).to receive(:allowlisted?).and_return(allowlisted)
described_service.execute
end
@@ -136,7 +141,7 @@ RSpec.describe Spam::SpamActionService do
context 'when spammable attributes have not changed' do
before do
- issue.closed_at = Time.zone.now
+ allow(target).to receive(:has_changes_to_save?).and_return(true)
end
it 'does not create a spam log' do
@@ -146,11 +151,11 @@ RSpec.describe Spam::SpamActionService do
context 'when spammable attributes have changed' do
let(:expected_service_check_response_message) do
- /Check Issue spammable model for any errors or CAPTCHA requirement/
+ /Check #{target_type} spammable model for any errors or CAPTCHA requirement/
end
before do
- issue.description = 'Lovely Spam! Wonderful Spam!'
+ target.description = 'Lovely Spam! Wonderful Spam!'
end
context 'when allowlisted' do
@@ -170,13 +175,13 @@ RSpec.describe Spam::SpamActionService do
allow(fake_verdict_service).to receive(:execute).and_return(DISALLOW)
end
- it_behaves_like 'creates a spam log'
+ it_behaves_like 'creates a spam log', target_type
it 'marks as spam' do
response = subject
expect(response.message).to match(expected_service_check_response_message)
- expect(issue).to be_spam
+ expect(target).to be_spam
end
end
@@ -185,13 +190,13 @@ RSpec.describe Spam::SpamActionService do
allow(fake_verdict_service).to receive(:execute).and_return(BLOCK_USER)
end
- it_behaves_like 'creates a spam log'
+ it_behaves_like 'creates a spam log', target_type
it 'marks as spam' do
response = subject
expect(response.message).to match(expected_service_check_response_message)
- expect(issue).to be_spam
+ expect(target).to be_spam
end
end
@@ -200,20 +205,20 @@ RSpec.describe Spam::SpamActionService do
allow(fake_verdict_service).to receive(:execute).and_return(CONDITIONAL_ALLOW)
end
- it_behaves_like 'creates a spam log'
+ it_behaves_like 'creates a spam log', target_type
it 'does not mark as spam' do
response = subject
expect(response.message).to match(expected_service_check_response_message)
- expect(issue).not_to be_spam
+ expect(target).not_to be_spam
end
it 'marks as needing reCAPTCHA' do
response = subject
expect(response.message).to match(expected_service_check_response_message)
- expect(issue).to be_needs_recaptcha
+ expect(target).to be_needs_recaptcha
end
end
@@ -222,20 +227,20 @@ RSpec.describe Spam::SpamActionService do
allow(fake_verdict_service).to receive(:execute).and_return(OVERRIDE_VIA_ALLOW_POSSIBLE_SPAM)
end
- it_behaves_like 'creates a spam log'
+ it_behaves_like 'creates a spam log', target_type
it 'does not mark as spam' do
response = subject
expect(response.message).to match(expected_service_check_response_message)
- expect(issue).not_to be_spam
+ expect(target).not_to be_spam
end
it 'does not mark as needing CAPTCHA' do
response = subject
expect(response.message).to match(expected_service_check_response_message)
- expect(issue).not_to be_needs_recaptcha
+ expect(target).not_to be_needs_recaptcha
end
end
@@ -249,7 +254,7 @@ RSpec.describe Spam::SpamActionService do
end
it 'clears spam flags' do
- expect(issue).to receive(:clear_spam_flags!)
+ expect(target).to receive(:clear_spam_flags!)
subject
end
@@ -265,7 +270,7 @@ RSpec.describe Spam::SpamActionService do
end
it 'clears spam flags' do
- expect(issue).to receive(:clear_spam_flags!)
+ expect(target).to receive(:clear_spam_flags!)
subject
end
@@ -285,4 +290,27 @@ RSpec.describe Spam::SpamActionService do
end
end
end
+
+ describe '#execute' do
+ describe 'issue' do
+ let(:target) { issue }
+ let(:extra_features) { {} }
+
+ it_behaves_like 'execute spam action service', 'Issue'
+ end
+
+ describe 'project snippet' do
+ let(:target) { project_snippet }
+ let(:extra_features) { { files: [{ path: 'project.rb' }] } }
+
+ it_behaves_like 'execute spam action service', 'ProjectSnippet'
+ end
+
+ describe 'personal snippet' do
+ let(:target) { personal_snippet }
+ let(:extra_features) { { files: [{ path: 'personal.rb' }] } }
+
+ it_behaves_like 'execute spam action service', 'PersonalSnippet'
+ end
+ end
end
diff --git a/spec/services/spam/spam_verdict_service_spec.rb b/spec/services/spam/spam_verdict_service_spec.rb
index 082b8f909f9..b89c96129c2 100644
--- a/spec/services/spam/spam_verdict_service_spec.rb
+++ b/spec/services/spam/spam_verdict_service_spec.rb
@@ -17,9 +17,10 @@ RSpec.describe Spam::SpamVerdictService do
let(:check_for_spam) { true }
let_it_be(:user) { create(:user) }
let_it_be(:issue) { create(:issue, author: user) }
+ let_it_be(:snippet) { create(:personal_snippet, :public, author: user) }
let(:service) do
- described_class.new(user: user, target: issue, options: {})
+ described_class.new(user: user, target: target, options: {})
end
let(:attribs) do
@@ -31,7 +32,7 @@ RSpec.describe Spam::SpamVerdictService do
stub_feature_flags(allow_possible_spam: false)
end
- describe '#execute' do
+ shared_examples 'execute spam verdict service' do
subject { service.execute }
before do
@@ -172,7 +173,8 @@ RSpec.describe Spam::SpamVerdictService do
end
end
- describe '#akismet_verdict' do
+ shared_examples 'akismet verdict' do
+ let(:target) { issue }
subject { service.send(:akismet_verdict) }
context 'if Akismet is enabled' do
@@ -227,7 +229,7 @@ RSpec.describe Spam::SpamVerdictService do
end
end
- describe '#spamcheck_verdict' do
+ shared_examples 'spamcheck verdict' do
subject { service.send(:spamcheck_verdict) }
context 'if a Spam Check endpoint enabled and set to a URL' do
@@ -254,7 +256,7 @@ RSpec.describe Spam::SpamVerdictService do
before do
allow(service).to receive(:spamcheck_client).and_return(spam_client)
- allow(spam_client).to receive(:issue_spam?).and_return([verdict, attribs, error])
+ allow(spam_client).to receive(:spam?).and_return([verdict, attribs, error])
end
context 'if the result is a NOOP verdict' do
@@ -365,10 +367,13 @@ RSpec.describe Spam::SpamVerdictService do
let(:attribs) { nil }
before do
- allow(spam_client).to receive(:issue_spam?).and_raise(GRPC::Aborted)
+ allow(spam_client).to receive(:spam?).and_raise(GRPC::Aborted)
end
it 'returns nil' do
+ expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
+ an_instance_of(GRPC::Aborted), error: ::Spam::SpamConstants::ERROR_TYPE
+ )
expect(subject).to eq([ALLOW, attribs, true])
end
end
@@ -381,17 +386,20 @@ RSpec.describe Spam::SpamVerdictService do
expect(subject).to eq [DISALLOW, attribs]
end
end
- end
- context 'if the endpoint times out' do
- let(:attribs) { nil }
+ context 'if the endpoint times out' do
+ let(:attribs) { nil }
- before do
- allow(spam_client).to receive(:issue_spam?).and_raise(GRPC::DeadlineExceeded)
- end
+ before do
+ allow(spam_client).to receive(:spam?).and_raise(GRPC::DeadlineExceeded)
+ end
- it 'returns nil' do
- expect(subject).to eq([ALLOW, attribs, true])
+ it 'returns nil' do
+ expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
+ an_instance_of(GRPC::DeadlineExceeded), error: ::Spam::SpamConstants::ERROR_TYPE
+ )
+ expect(subject).to eq([ALLOW, attribs, true])
+ end
end
end
end
@@ -416,4 +424,46 @@ RSpec.describe Spam::SpamVerdictService do
end
end
end
+
+ describe '#execute' do
+ describe 'issue' do
+ let(:target) { issue }
+
+ it_behaves_like 'execute spam verdict service'
+ end
+
+ describe 'snippet' do
+ let(:target) { snippet }
+
+ it_behaves_like 'execute spam verdict service'
+ end
+ end
+
+ describe '#akismet_verdict' do
+ describe 'issue' do
+ let(:target) { issue }
+
+ it_behaves_like 'akismet verdict'
+ end
+
+ describe 'snippet' do
+ let(:target) { snippet }
+
+ it_behaves_like 'akismet verdict'
+ end
+ end
+
+ describe '#spamcheck_verdict' do
+ describe 'issue' do
+ let(:target) { issue }
+
+ it_behaves_like 'spamcheck verdict'
+ end
+
+ describe 'snippet' do
+ let(:target) { snippet }
+
+ it_behaves_like 'spamcheck verdict'
+ end
+ end
end
diff --git a/spec/services/suggestions/apply_service_spec.rb b/spec/services/suggestions/apply_service_spec.rb
index e34324d5fe2..41ccd8523fa 100644
--- a/spec/services/suggestions/apply_service_spec.rb
+++ b/spec/services/suggestions/apply_service_spec.rb
@@ -35,7 +35,7 @@ RSpec.describe Suggestions::ApplyService do
def apply(suggestions, custom_message = nil)
result = apply_service.new(user, *suggestions, message: custom_message).execute
- suggestions.map { |suggestion| suggestion.reload }
+ suggestions.map(&:reload)
expect(result[:status]).to eq(:success)
end
@@ -136,21 +136,20 @@ RSpec.describe Suggestions::ApplyService do
end
let(:merge_request) do
- create(:merge_request, source_project: project,
- target_project: project,
- source_branch: 'master')
+ create(:merge_request,
+ source_project: project, target_project: project, source_branch: 'master')
end
let(:position) { build_position }
let(:diff_note) do
- create(:diff_note_on_merge_request, noteable: merge_request,
- position: position, project: project)
+ create(:diff_note_on_merge_request,
+ noteable: merge_request, position: position, project: project)
end
let(:suggestion) do
- create(:suggestion, :content_from_repo, note: diff_note,
- to_content: " raise RuntimeError, 'Explosion'\n # explosion?\n")
+ create(:suggestion, :content_from_repo,
+ note: diff_note, to_content: " raise RuntimeError, 'Explosion'\n # explosion?\n")
end
let(:suggestion2) do
@@ -311,9 +310,9 @@ RSpec.describe Suggestions::ApplyService do
context 'when HEAD from position is different from source branch HEAD on repo' do
it 'returns error message' do
- allow(suggestion).to receive(:appliable?) { true }
- allow(suggestion.position).to receive(:head_sha) { 'old-sha' }
- allow(suggestion.noteable).to receive(:source_branch_sha) { 'new-sha' }
+ allow(suggestion).to receive(:appliable?).and_return(true)
+ allow(suggestion.position).to receive(:head_sha).and_return('old-sha')
+ allow(suggestion.noteable).to receive(:source_branch_sha).and_return('new-sha')
result = apply_service.new(user, suggestion).execute
@@ -430,7 +429,6 @@ RSpec.describe Suggestions::ApplyService do
suggestion1_diff = fetch_raw_diff(suggestion1)
suggestion2_diff = fetch_raw_diff(suggestion2)
- # rubocop: disable Layout/TrailingWhitespace
expected_suggestion1_diff = <<-CONTENT.strip_heredoc
@@ -10,7 +10,7 @@ module Popen
end
@@ -442,9 +440,6 @@ RSpec.describe Suggestions::ApplyService do
"PWD" => path
}
CONTENT
- # rubocop: enable Layout/TrailingWhitespace
-
- # rubocop: disable Layout/TrailingWhitespace
expected_suggestion2_diff = <<-CONTENT.strip_heredoc
@@ -28,7 +28,7 @@ module Popen
@@ -455,8 +450,6 @@ RSpec.describe Suggestions::ApplyService do
@cmd_status = wait_thr.value.exitstatus
end
CONTENT
- # rubocop: enable Layout/TrailingWhitespace
-
expect(suggestion1_diff.strip).to eq(expected_suggestion1_diff.strip)
expect(suggestion2_diff.strip).to eq(expected_suggestion2_diff.strip)
end
@@ -508,10 +501,8 @@ RSpec.describe Suggestions::ApplyService do
end
let(:suggestion) do
- create(:suggestion, :content_from_repo, note: diff_note,
- lines_above: 2,
- lines_below: 3,
- to_content: "# multi\n# line\n")
+ create(:suggestion, :content_from_repo,
+ note: diff_note, lines_above: 2, lines_below: 3, to_content: "# multi\n# line\n")
end
let(:suggestions) { [suggestion] }
@@ -568,7 +559,7 @@ RSpec.describe Suggestions::ApplyService do
end
let(:suggestion) do
- create_suggestion( to_content: "", new_line: 13)
+ create_suggestion(to_content: "", new_line: 13)
end
let(:suggestions) { [suggestion] }
@@ -616,14 +607,12 @@ RSpec.describe Suggestions::ApplyService do
context 'no permission' do
let(:merge_request) do
- create(:merge_request, source_project: project,
- target_project: project)
+ create(:merge_request, source_project: project, target_project: project)
end
let(:diff_note) do
- create(:diff_note_on_merge_request, noteable: merge_request,
- position: position,
- project: project)
+ create(:diff_note_on_merge_request,
+ noteable: merge_request, position: position, project: project)
end
context 'user cannot write in project repo' do
@@ -642,14 +631,12 @@ RSpec.describe Suggestions::ApplyService do
context 'patch is not appliable' do
let(:merge_request) do
- create(:merge_request, source_project: project,
- target_project: project)
+ create(:merge_request, source_project: project, target_project: project)
end
let(:diff_note) do
- create(:diff_note_on_merge_request, noteable: merge_request,
- position: position,
- project: project)
+ create(:diff_note_on_merge_request,
+ noteable: merge_request, position: position, project: project)
end
before do
@@ -669,7 +656,7 @@ RSpec.describe Suggestions::ApplyService do
let(:result) { apply_service.new(user, suggestion).execute }
before do
- expect(suggestion.note).to receive(:latest_diff_file) { nil }
+ expect(suggestion.note).to receive(:latest_diff_file).and_return(nil)
end
it 'returns error message' do
diff --git a/spec/services/system_notes/time_tracking_service_spec.rb b/spec/services/system_notes/time_tracking_service_spec.rb
index 33608deaa64..c856caa3f3e 100644
--- a/spec/services/system_notes/time_tracking_service_spec.rb
+++ b/spec/services/system_notes/time_tracking_service_spec.rb
@@ -48,12 +48,6 @@ RSpec.describe ::SystemNotes::TimeTrackingService do
expect(note.note).to eq("changed due date to #{due_date.to_s(:long)}")
end
- it 'tracks the issue event in usage ping' do
- expect(activity_counter_class).to receive(activity_counter_method).with(author: author)
-
- subject
- end
-
context 'and start date removed' do
let(:changed_dates) { { 'due_date' => [nil, due_date], 'start_date' => [start_date, nil] } }
@@ -66,12 +60,18 @@ RSpec.describe ::SystemNotes::TimeTrackingService do
context 'when start_date is added' do
let(:changed_dates) { { 'start_date' => [nil, start_date] } }
- it 'does not track the issue event in usage ping' do
+ it 'does not track the issue event' do
expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).not_to receive(:track_issue_due_date_changed_action)
subject
end
+ it 'does not emit snowplow event', :snowplow do
+ expect_no_snowplow_event
+
+ subject
+ end
+
it 'sets the correct note message' do
expect(note.note).to eq("changed start date to #{start_date.to_s(:long)}")
end
@@ -111,12 +111,19 @@ RSpec.describe ::SystemNotes::TimeTrackingService do
subject
end
- it 'tracks the issue event in usage ping' do
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_due_date_changed_action).with(author: author)
+ it 'tracks the issue event' do
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_due_date_changed_action)
+ .with(author: author, project: project)
subject
end
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_DUE_DATE_CHANGED }
+ let(:user) { author }
+ subject(:service_action) { note }
+ end
+
context 'when only start_date is added' do
let(:changed_dates) { { 'start_date' => [nil, start_date] } }
@@ -135,12 +142,18 @@ RSpec.describe ::SystemNotes::TimeTrackingService do
it_behaves_like 'issuable getting date change notes'
- it 'does not track the issue event in usage ping' do
+ it 'does not track the issue event' do
expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).not_to receive(:track_issue_due_date_changed_action)
subject
end
+ it 'does not emit snowplow event', :snowplow do
+ expect_no_snowplow_event
+
+ subject
+ end
+
context 'when only start_date is added' do
let(:changed_dates) { { 'start_date' => [nil, start_date] } }
@@ -155,12 +168,23 @@ RSpec.describe ::SystemNotes::TimeTrackingService do
context 'when noteable is a merge request' do
let(:noteable) { create(:merge_request, source_project: project) }
- it 'does not track the issue event in usage ping' do
+ it 'does not track the issue event' do
expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).not_to receive(:track_issue_due_date_changed_action)
+
+ subject
+ end
+
+ it 'does not track the work item event in usage ping' do
expect(Gitlab::UsageDataCounters::WorkItemActivityUniqueCounter).not_to receive(:track_work_item_date_changed_action)
subject
end
+
+ it 'does not emit snowplow event', :snowplow do
+ expect_no_snowplow_event
+
+ subject
+ end
end
end
@@ -201,17 +225,31 @@ RSpec.describe ::SystemNotes::TimeTrackingService do
end
it 'tracks the issue event in usage ping' do
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_time_estimate_changed_action).with(author: author)
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_time_estimate_changed_action)
+ .with(author: author, project: project)
subject
end
+
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_TIME_ESTIMATE_CHANGED }
+ let(:user) { author }
+ let(:service_action) { subject }
+ end
end
context 'when noteable is a merge request' do
let_it_be(:noteable) { create(:merge_request, source_project: project) }
- it 'does not track the issue event in usage ping' do
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).not_to receive(:track_issue_time_estimate_changed_action).with(author: author)
+ it 'does not track the issue event' do
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).not_to receive(:track_issue_time_estimate_changed_action)
+ .with(author: author, project: project)
+
+ subject
+ end
+
+ it 'does not emit snowplow event', :snowplow do
+ expect_no_snowplow_event
subject
end
@@ -316,25 +354,42 @@ RSpec.describe ::SystemNotes::TimeTrackingService do
end
end
- it 'tracks the issue event in usage ping' do
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_time_spent_changed_action).with(author: author)
+ it 'tracks the issue event' do
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_time_spent_changed_action)
+ .with(author: author, project: project)
spend_time!(277200)
subject
end
+
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_TIME_SPENT_CHANGED }
+ let(:user) { author }
+ let(:service_action) do
+ spend_time!(277200)
+ subject
+ end
+ end
end
context 'when noteable is a merge request' do
let_it_be(:noteable) { create(:merge_request, source_project: project) }
- it 'does not track the issue event in usage ping' do
- expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).not_to receive(:track_issue_time_estimate_changed_action).with(author: author)
+ it 'does not track the issue event' do
+ expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).not_to receive(:track_issue_time_estimate_changed_action)
+ .with(author: author, project: project)
spend_time!(277200)
subject
end
+
+ it 'does not emit snowplow event', :snowplow do
+ expect_no_snowplow_event
+
+ subject
+ end
end
def spend_time!(seconds)
diff --git a/spec/services/topics/merge_service_spec.rb b/spec/services/topics/merge_service_spec.rb
index 971917eb8e9..eef31817aa8 100644
--- a/spec/services/topics/merge_service_spec.rb
+++ b/spec/services/topics/merge_service_spec.rb
@@ -30,7 +30,9 @@ RSpec.describe Topics::MergeService do
it 'reverts previous changes' do
allow(source_topic.reload).to receive(:destroy!).and_raise(ActiveRecord::RecordNotDestroyed)
- expect { subject }.to raise_error(ActiveRecord::RecordNotDestroyed)
+ response = subject
+ expect(response).to be_error
+ expect(response.message).to eq('Topics could not be merged!')
expect(source_topic.projects).to contain_exactly(project_1, project_2, project_4)
expect(target_topic.projects).to contain_exactly(project_3, project_4)
@@ -50,9 +52,9 @@ RSpec.describe Topics::MergeService do
with_them do
it 'raises correct error' do
- expect { subject }.to raise_error(ArgumentError) do |error|
- expect(error.message).to eq(expected_message)
- end
+ response = subject
+ expect(response).to be_error
+ expect(response.message).to eq(expected_message)
end
end
end
diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb
index 90c4f70d749..b32599d4af8 100644
--- a/spec/services/users/destroy_service_spec.rb
+++ b/spec/services/users/destroy_service_spec.rb
@@ -10,371 +10,475 @@ RSpec.describe Users::DestroyService do
let(:service) { described_class.new(admin) }
let(:gitlab_shell) { Gitlab::Shell.new }
- describe "Deletes a user and all their personal projects", :enable_admin_mode do
- context 'no options are given' do
- it 'deletes the user' do
- user_data = service.execute(user)
-
- expect(user_data['email']).to eq(user.email)
- expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound)
- expect { Namespace.find(namespace.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ shared_examples 'pre-migrate clean-up' do
+ describe "Deletes a user and all their personal projects", :enable_admin_mode do
+ context 'no options are given' do
+ it 'will delete the personal project' do
+ expect_next_instance_of(Projects::DestroyService) do |destroy_service|
+ expect(destroy_service).to receive(:execute).once.and_return(true)
+ end
+
+ service.execute(user)
+ end
end
- it 'deletes user associations in batches' do
- expect(user).to receive(:destroy_dependent_associations_in_batches)
+ context 'personal projects in pending_delete' do
+ before do
+ project.pending_delete = true
+ project.save!
+ end
+
+ it 'destroys a personal project in pending_delete' do
+ expect_next_instance_of(Projects::DestroyService) do |destroy_service|
+ expect(destroy_service).to receive(:execute).once.and_return(true)
+ end
- service.execute(user)
+ service.execute(user)
+ end
end
- it 'does not include snippets when deleting in batches' do
- expect(user).to receive(:destroy_dependent_associations_in_batches).with({ exclude: [:snippets] })
+ context "solo owned groups present" do
+ let(:solo_owned) { create(:group) }
+ let(:member) { create(:group_member) }
+ let(:user) { member.user }
- service.execute(user)
- end
+ before do
+ solo_owned.group_members = [member]
+ end
- it 'will delete the project' do
- expect_next_instance_of(Projects::DestroyService) do |destroy_service|
- expect(destroy_service).to receive(:execute).once.and_return(true)
+ it 'returns the user with attached errors' do
+ expect(service.execute(user)).to be(user)
+ expect(user.errors.full_messages).to(
+ contain_exactly('You must transfer ownership or delete groups before you can remove user'))
end
- service.execute(user)
+ it 'does not delete the user, nor the group' do
+ service.execute(user)
+
+ expect(User.find(user.id)).to eq user
+ expect(Group.find(solo_owned.id)).to eq solo_owned
+ end
end
- it 'calls the bulk snippet destroy service for the user personal snippets' do
- repo1 = create(:personal_snippet, :repository, author: user).snippet_repository
- repo2 = create(:project_snippet, :repository, project: project, author: user).snippet_repository
+ context "deletions with solo owned groups" do
+ let(:solo_owned) { create(:group) }
+ let(:member) { create(:group_member) }
+ let(:user) { member.user }
- aggregate_failures do
- expect(gitlab_shell.repository_exists?(repo1.shard_name, repo1.disk_path + '.git')).to be_truthy
- expect(gitlab_shell.repository_exists?(repo2.shard_name, repo2.disk_path + '.git')).to be_truthy
+ before do
+ solo_owned.group_members = [member]
+ service.execute(user, delete_solo_owned_groups: true)
end
- # Call made when destroying user personal projects
- expect(Snippets::BulkDestroyService).to receive(:new)
- .with(admin, project.snippets).and_call_original
+ it 'deletes solo owned groups' do
+ expect { Group.find(solo_owned.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
- # Call to remove user personal snippets and for
- # project snippets where projects are not user personal
- # ones
- expect(Snippets::BulkDestroyService).to receive(:new)
- .with(admin, user.snippets.only_personal_snippets).and_call_original
+ context 'deletions with inherited group owners' do
+ let(:group) { create(:group, :nested) }
+ let(:user) { create(:user) }
+ let(:inherited_owner) { create(:user) }
+
+ before do
+ group.parent.add_owner(inherited_owner)
+ group.add_owner(user)
- service.execute(user)
+ service.execute(user, delete_solo_owned_groups: true)
+ end
- aggregate_failures do
- expect(gitlab_shell.repository_exists?(repo1.shard_name, repo1.disk_path + '.git')).to be_falsey
- expect(gitlab_shell.repository_exists?(repo2.shard_name, repo2.disk_path + '.git')).to be_falsey
+ it 'does not delete the group' do
+ expect(Group.exists?(id: group)).to be_truthy
end
end
- it 'calls the bulk snippet destroy service with hard delete option if it is present' do
- # this avoids getting into Projects::DestroyService as it would
- # call Snippets::BulkDestroyService first!
- allow(user).to receive(:personal_projects).and_return([])
+ describe "user personal's repository removal" do
+ context 'storages' do
+ before do
+ perform_enqueued_jobs { service.execute(user) }
+ end
- expect_next_instance_of(Snippets::BulkDestroyService) do |bulk_destroy_service|
- expect(bulk_destroy_service).to receive(:execute).with({ hard_delete: true }).and_call_original
- end
+ context 'legacy storage' do
+ let!(:project) { create(:project, :empty_repo, :legacy_storage, namespace: user.namespace) }
- service.execute(user, { hard_delete: true })
- end
+ it 'removes repository' do
+ expect(
+ gitlab_shell.repository_exists?(project.repository_storage,
+ "#{project.disk_path}.git")
+ ).to be_falsey
+ end
+ end
- it 'does not delete project snippets that the user is the author of' do
- repo = create(:project_snippet, :repository, author: user).snippet_repository
- service.execute(user)
- expect(gitlab_shell.repository_exists?(repo.shard_name, repo.disk_path + '.git')).to be_truthy
- expect(User.ghost.snippets).to include(repo.snippet)
- end
+ context 'hashed storage' do
+ let!(:project) { create(:project, :empty_repo, namespace: user.namespace) }
- context 'when an error is raised deleting snippets' do
- it 'does not delete user' do
- snippet = create(:personal_snippet, :repository, author: user)
+ it 'removes repository' do
+ expect(
+ gitlab_shell.repository_exists?(project.repository_storage,
+ "#{project.disk_path}.git")
+ ).to be_falsey
+ end
+ end
+ end
- bulk_service = double
- allow(Snippets::BulkDestroyService).to receive(:new).and_call_original
- allow(Snippets::BulkDestroyService).to receive(:new).with(admin, user.snippets).and_return(bulk_service)
- allow(bulk_service).to receive(:execute).and_return(ServiceResponse.error(message: 'foo'))
+ context 'repository removal status is taken into account' do
+ it 'raises exception' do
+ expect_next_instance_of(::Projects::DestroyService) do |destroy_service|
+ expect(destroy_service).to receive(:execute).and_return(false)
+ end
- aggregate_failures do
expect { service.execute(user) }
- .to raise_error(Users::DestroyService::DestroyError, 'foo' )
- expect(snippet.reload).not_to be_nil
- expect(gitlab_shell.repository_exists?(snippet.repository_storage, snippet.disk_path + '.git')).to be_truthy
+ .to raise_error(Users::DestroyService::DestroyError,
+ "Project #{project.id} can't be deleted" )
end
end
end
- end
- context 'projects in pending_delete' do
- before do
- project.pending_delete = true
- project.save!
- end
+ describe "calls the before/after callbacks" do
+ it 'of project_members' do
+ expect_any_instance_of(ProjectMember).to receive(:run_callbacks).with(:find).once
+ expect_any_instance_of(ProjectMember).to receive(:run_callbacks).with(:initialize).once
+ expect_any_instance_of(ProjectMember).to receive(:run_callbacks).with(:destroy).once
- it 'destroys a project in pending_delete' do
- expect_next_instance_of(Projects::DestroyService) do |destroy_service|
- expect(destroy_service).to receive(:execute).once.and_return(true)
+ service.execute(user)
end
- service.execute(user)
+ it 'of group_members' do
+ group_member = create(:group_member)
+ group_member.group.group_members.create!(user: user, access_level: 40)
- expect { Project.find(project.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ expect_any_instance_of(GroupMember).to receive(:run_callbacks).with(:find).once
+ expect_any_instance_of(GroupMember).to receive(:run_callbacks).with(:initialize).once
+ expect_any_instance_of(GroupMember).to receive(:run_callbacks).with(:destroy).once
+
+ service.execute(user)
+ end
end
end
+ end
- context "a deleted user's issues" do
- let(:project) { create(:project) }
+ context 'when user_destroy_with_limited_execution_time_worker is disabled' do
+ before do
+ stub_feature_flags(user_destroy_with_limited_execution_time_worker: false)
+ end
- before do
- project.add_developer(user)
- end
+ include_examples 'pre-migrate clean-up'
- context "for an issue the user was assigned to" do
- let!(:issue) { create(:issue, project: project, assignees: [user]) }
+ describe "Deletes a user and all their personal projects", :enable_admin_mode do
+ context 'no options are given' do
+ it 'deletes the user' do
+ user_data = service.execute(user)
- before do
- service.execute(user)
+ expect(user_data['email']).to eq(user.email)
+ expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { Namespace.find(namespace.id) }.to raise_error(ActiveRecord::RecordNotFound)
end
- it 'does not delete issues the user is assigned to' do
- expect(Issue.find_by_id(issue.id)).to be_present
+ it 'deletes user associations in batches' do
+ expect(user).to receive(:destroy_dependent_associations_in_batches)
+
+ service.execute(user)
end
- it 'migrates the issue so that it is "Unassigned"' do
- migrated_issue = Issue.find_by_id(issue.id)
+ it 'does not include snippets when deleting in batches' do
+ expect(user).to receive(:destroy_dependent_associations_in_batches).with({ exclude: [:snippets] })
- expect(migrated_issue.assignees).to be_empty
+ service.execute(user)
end
- end
- end
- context "a deleted user's merge_requests" do
- let(:project) { create(:project, :repository) }
+ it 'calls the bulk snippet destroy service for the user personal snippets' do
+ repo1 = create(:personal_snippet, :repository, author: user).snippet_repository
+ repo2 = create(:project_snippet, :repository, project: project, author: user).snippet_repository
- before do
- project.add_developer(user)
- end
+ aggregate_failures do
+ expect(gitlab_shell.repository_exists?(repo1.shard_name, repo1.disk_path + '.git')).to be_truthy
+ expect(gitlab_shell.repository_exists?(repo2.shard_name, repo2.disk_path + '.git')).to be_truthy
+ end
- context "for an merge request the user was assigned to" do
- let!(:merge_request) { create(:merge_request, source_project: project, assignees: [user]) }
+ # Call made when destroying user personal projects
+ expect(Snippets::BulkDestroyService).to receive(:new)
+ .with(admin, project.snippets).and_call_original
+
+ # Call to remove user personal snippets and for
+ # project snippets where projects are not user personal
+ # ones
+ expect(Snippets::BulkDestroyService).to receive(:new)
+ .with(admin, user.snippets.only_personal_snippets).and_call_original
- before do
service.execute(user)
+
+ aggregate_failures do
+ expect(gitlab_shell.repository_exists?(repo1.shard_name, repo1.disk_path + '.git')).to be_falsey
+ expect(gitlab_shell.repository_exists?(repo2.shard_name, repo2.disk_path + '.git')).to be_falsey
+ end
end
- it 'does not delete merge requests the user is assigned to' do
- expect(MergeRequest.find_by_id(merge_request.id)).to be_present
+ it 'calls the bulk snippet destroy service with hard delete option if it is present' do
+ # this avoids getting into Projects::DestroyService as it would
+ # call Snippets::BulkDestroyService first!
+ allow(user).to receive(:personal_projects).and_return([])
+
+ expect_next_instance_of(Snippets::BulkDestroyService) do |bulk_destroy_service|
+ expect(bulk_destroy_service).to receive(:execute).with({ skip_authorization: true }).and_call_original
+ end
+
+ service.execute(user, { hard_delete: true })
end
- it 'migrates the merge request so that it is "Unassigned"' do
- migrated_merge_request = MergeRequest.find_by_id(merge_request.id)
+ it 'does not delete project snippets that the user is the author of' do
+ repo = create(:project_snippet, :repository, author: user).snippet_repository
+ service.execute(user)
+ expect(gitlab_shell.repository_exists?(repo.shard_name, repo.disk_path + '.git')).to be_truthy
+ expect(User.ghost.snippets).to include(repo.snippet)
+ end
- expect(migrated_merge_request.assignees).to be_empty
+ context 'when an error is raised deleting snippets' do
+ it 'does not delete user' do
+ snippet = create(:personal_snippet, :repository, author: user)
+
+ bulk_service = double
+ allow(Snippets::BulkDestroyService).to receive(:new).and_call_original
+ allow(Snippets::BulkDestroyService).to receive(:new).with(admin, user.snippets).and_return(bulk_service)
+ allow(bulk_service).to receive(:execute).and_return(ServiceResponse.error(message: 'foo'))
+
+ aggregate_failures do
+ expect { service.execute(user) }
+ .to raise_error(Users::DestroyService::DestroyError, 'foo' )
+ expect(snippet.reload).not_to be_nil
+ expect(
+ gitlab_shell.repository_exists?(snippet.repository_storage,
+ snippet.disk_path + '.git')
+ ).to be_truthy
+ end
+ end
end
end
- end
- context "solo owned groups present" do
- let(:solo_owned) { create(:group) }
- let(:member) { create(:group_member) }
- let(:user) { member.user }
+ context 'projects in pending_delete' do
+ before do
+ project.pending_delete = true
+ project.save!
+ end
- before do
- solo_owned.group_members = [member]
- end
+ it 'destroys a project in pending_delete' do
+ expect_next_instance_of(Projects::DestroyService) do |destroy_service|
+ expect(destroy_service).to receive(:execute).once.and_return(true)
+ end
- it 'returns the user with attached errors' do
- expect(service.execute(user)).to be(user)
- expect(user.errors.full_messages).to eq([
- 'You must transfer ownership or delete groups before you can remove user'
- ])
- end
+ service.execute(user)
- it 'does not delete the user' do
- service.execute(user)
- expect(User.find(user.id)).to eq user
+ expect { Project.find(project.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
end
- end
- context "deletions with solo owned groups" do
- let(:solo_owned) { create(:group) }
- let(:member) { create(:group_member) }
- let(:user) { member.user }
+ context "a deleted user's issues" do
+ let(:project) { create(:project) }
- before do
- solo_owned.group_members = [member]
- service.execute(user, delete_solo_owned_groups: true)
- end
+ before do
+ project.add_developer(user)
+ end
- it 'deletes solo owned groups' do
- expect { Project.find(solo_owned.id) }.to raise_error(ActiveRecord::RecordNotFound)
- end
+ context "for an issue the user was assigned to" do
+ let!(:issue) { create(:issue, project: project, assignees: [user]) }
- it 'deletes the user' do
- expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound)
- end
- end
+ before do
+ service.execute(user)
+ end
- context 'deletions with inherited group owners' do
- let(:group) { create(:group, :nested) }
- let(:user) { create(:user) }
- let(:inherited_owner) { create(:user) }
+ it 'does not delete issues the user is assigned to' do
+ expect(Issue.find_by_id(issue.id)).to be_present
+ end
- before do
- group.parent.add_owner(inherited_owner)
- group.add_owner(user)
+ it 'migrates the issue so that it is "Unassigned"' do
+ migrated_issue = Issue.find_by_id(issue.id)
- service.execute(user, delete_solo_owned_groups: true)
+ expect(migrated_issue.assignees).to be_empty
+ end
+ end
end
- it 'does not delete the group' do
- expect(Group.exists?(id: group)).to be_truthy
- end
+ context "a deleted user's merge_requests" do
+ let(:project) { create(:project, :repository) }
- it 'deletes the user' do
- expect(User.exists?(id: user)).to be_falsey
- end
- end
+ before do
+ project.add_developer(user)
+ end
- context 'migrating associated records' do
- let!(:issue) { create(:issue, author: user) }
+ context "for an merge request the user was assigned to" do
+ let!(:merge_request) { create(:merge_request, source_project: project, assignees: [user]) }
+
+ before do
+ service.execute(user)
+ end
- it 'delegates to the `MigrateToGhostUser` service to move associated records to the ghost user' do
- expect_any_instance_of(Users::MigrateToGhostUserService).to receive(:execute).once.and_call_original
+ it 'does not delete merge requests the user is assigned to' do
+ expect(MergeRequest.find_by_id(merge_request.id)).to be_present
+ end
- service.execute(user)
+ it 'migrates the merge request so that it is "Unassigned"' do
+ migrated_merge_request = MergeRequest.find_by_id(merge_request.id)
- expect(issue.reload.author).to be_ghost
+ expect(migrated_merge_request.assignees).to be_empty
+ end
+ end
end
- context 'when hard_delete option is given' do
- it 'will not ghost certain records' do
+ context 'migrating associated records' do
+ let!(:issue) { create(:issue, author: user) }
+
+ it 'delegates to the `MigrateToGhostUser` service to move associated records to the ghost user' do
expect_any_instance_of(Users::MigrateToGhostUserService).to receive(:execute).once.and_call_original
- service.execute(user, hard_delete: true)
+ service.execute(user)
- expect(Issue.exists?(issue.id)).to be_falsy
+ expect(issue.reload.author).to be_ghost
end
- end
- end
- describe "user personal's repository removal" do
- context 'storages' do
- before do
- perform_enqueued_jobs { service.execute(user) }
- end
+ context 'when hard_delete option is given' do
+ it 'will not ghost certain records' do
+ expect_any_instance_of(Users::MigrateToGhostUserService).to receive(:execute).once.and_call_original
- context 'legacy storage' do
- let!(:project) { create(:project, :empty_repo, :legacy_storage, namespace: user.namespace) }
+ service.execute(user, hard_delete: true)
- it 'removes repository' do
- expect(gitlab_shell.repository_exists?(project.repository_storage, "#{project.disk_path}.git")).to be_falsey
+ expect(Issue.exists?(issue.id)).to be_falsy
end
end
+ end
+ end
- context 'hashed storage' do
- let!(:project) { create(:project, :empty_repo, namespace: user.namespace) }
+ describe "Deletion permission checks" do
+ it 'does not delete the user when user is not an admin' do
+ other_user = create(:user)
- it 'removes repository' do
- expect(gitlab_shell.repository_exists?(project.repository_storage, "#{project.disk_path}.git")).to be_falsey
- end
+ expect { described_class.new(other_user).execute(user) }.to raise_error(Gitlab::Access::AccessDeniedError)
+ expect(User.exists?(user.id)).to be(true)
+ end
+
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it 'allows admins to delete anyone' do
+ described_class.new(admin).execute(user)
+
+ expect(User.exists?(user.id)).to be(false)
end
end
- context 'repository removal status is taken into account' do
- it 'raises exception' do
- expect_next_instance_of(::Projects::DestroyService) do |destroy_service|
- expect(destroy_service).to receive(:execute).and_return(false)
- end
+ context 'when admin mode is disabled' do
+ it 'disallows admins to delete anyone' do
+ expect { described_class.new(admin).execute(user) }.to raise_error(Gitlab::Access::AccessDeniedError)
- expect { service.execute(user) }
- .to raise_error(Users::DestroyService::DestroyError, "Project #{project.id} can't be deleted" )
+ expect(User.exists?(user.id)).to be(true)
end
end
- end
- describe "calls the before/after callbacks" do
- it 'of project_members' do
- expect_any_instance_of(ProjectMember).to receive(:run_callbacks).with(:find).once
- expect_any_instance_of(ProjectMember).to receive(:run_callbacks).with(:initialize).once
- expect_any_instance_of(ProjectMember).to receive(:run_callbacks).with(:destroy).once
+ it 'allows users to delete their own account' do
+ described_class.new(user).execute(user)
- service.execute(user)
+ expect(User.exists?(user.id)).to be(false)
end
- it 'of group_members' do
- group_member = create(:group_member)
- group_member.group.group_members.create!(user: user, access_level: 40)
+ it 'allows user to be deleted if skip_authorization: true' do
+ other_user = create(:user)
- expect_any_instance_of(GroupMember).to receive(:run_callbacks).with(:find).once
- expect_any_instance_of(GroupMember).to receive(:run_callbacks).with(:initialize).once
- expect_any_instance_of(GroupMember).to receive(:run_callbacks).with(:destroy).once
+ described_class.new(user).execute(other_user, skip_authorization: true)
- service.execute(user)
+ expect(User.exists?(other_user.id)).to be(false)
end
end
- end
-
- describe "Deletion permission checks" do
- it 'does not delete the user when user is not an admin' do
- other_user = create(:user)
- expect { described_class.new(other_user).execute(user) }.to raise_error(Gitlab::Access::AccessDeniedError)
- expect(User.exists?(user.id)).to be(true)
- end
+ context 'batched nullify' do
+ let(:other_user) { create(:user) }
- context 'when admin mode is enabled', :enable_admin_mode do
- it 'allows admins to delete anyone' do
- described_class.new(admin).execute(user)
+ it 'nullifies related associations in batches' do
+ expect(other_user).to receive(:nullify_dependent_associations_in_batches).and_call_original
- expect(User.exists?(user.id)).to be(false)
+ described_class.new(user).execute(other_user, skip_authorization: true)
end
- end
- context 'when admin mode is disabled' do
- it 'disallows admins to delete anyone' do
- expect { described_class.new(admin).execute(user) }.to raise_error(Gitlab::Access::AccessDeniedError)
+ it 'nullifies last_updated_issues, closed_issues, resource_label_events' do
+ issue = create(:issue, closed_by: other_user, updated_by: other_user)
+ resource_label_event = create(:resource_label_event, user: other_user)
- expect(User.exists?(user.id)).to be(true)
- end
- end
+ described_class.new(user).execute(other_user, skip_authorization: true)
- it 'allows users to delete their own account' do
- described_class.new(user).execute(user)
+ issue.reload
+ resource_label_event.reload
- expect(User.exists?(user.id)).to be(false)
+ expect(issue.closed_by).to be_nil
+ expect(issue.updated_by).to be_nil
+ expect(resource_label_event.user).to be_nil
+ end
end
+ end
- it 'allows user to be deleted if skip_authorization: true' do
- other_user = create(:user)
-
- described_class.new(user).execute(other_user, skip_authorization: true)
+ context 'when user_destroy_with_limited_execution_time_worker is enabled' do
+ include_examples 'pre-migrate clean-up'
- expect(User.exists?(other_user.id)).to be(false)
+ describe "Deletes a user and all their personal projects", :enable_admin_mode do
+ context 'no options are given' do
+ it 'creates GhostUserMigration record to handle migration in a worker' do
+ expect { service.execute(user) }
+ .to(
+ change do
+ Users::GhostUserMigration.where(user: user,
+ initiator_user: admin)
+ .exists?
+ end.from(false).to(true))
+ end
+ end
end
- end
- context 'batched nullify' do
- let(:other_user) { create(:user) }
+ describe "Deletion permission checks" do
+ it 'does not delete the user when user is not an admin' do
+ other_user = create(:user)
- it 'nullifies related associations in batches' do
- expect(other_user).to receive(:nullify_dependent_associations_in_batches).and_call_original
+ expect { described_class.new(other_user).execute(user) }.to raise_error(Gitlab::Access::AccessDeniedError)
- described_class.new(user).execute(other_user, skip_authorization: true)
- end
+ expect(Users::GhostUserMigration).not_to be_exists
+ end
+
+ context 'when admin mode is enabled', :enable_admin_mode do
+ it 'allows admins to delete anyone' do
+ expect { described_class.new(admin).execute(user) }
+ .to(
+ change do
+ Users::GhostUserMigration.where(user: user,
+ initiator_user: admin)
+ .exists?
+ end.from(false).to(true))
+ end
+ end
- it 'nullifies last_updated_issues, closed_issues, resource_label_events' do
- issue = create(:issue, closed_by: other_user, updated_by: other_user)
- resource_label_event = create(:resource_label_event, user: other_user)
+ context 'when admin mode is disabled' do
+ it 'disallows admins to delete anyone' do
+ expect { described_class.new(admin).execute(user) }.to raise_error(Gitlab::Access::AccessDeniedError)
- described_class.new(user).execute(other_user, skip_authorization: true)
+ expect(Users::GhostUserMigration).not_to be_exists
+ end
+ end
- issue.reload
- resource_label_event.reload
+ it 'allows users to delete their own account' do
+ expect { described_class.new(user).execute(user) }
+ .to(
+ change do
+ Users::GhostUserMigration.where(user: user,
+ initiator_user: user)
+ .exists?
+ end.from(false).to(true))
+ end
- expect(issue.closed_by).to be_nil
- expect(issue.updated_by).to be_nil
- expect(resource_label_event.user).to be_nil
+ it 'allows user to be deleted if skip_authorization: true' do
+ other_user = create(:user)
+
+ expect do
+ described_class.new(user)
+ .execute(other_user, skip_authorization: true)
+ end.to(
+ change do
+ Users::GhostUserMigration.where(user: other_user,
+ initiator_user: user )
+ .exists?
+ end.from(false).to(true))
+ end
end
end
end
diff --git a/spec/services/users/email_verification/generate_token_service_spec.rb b/spec/services/users/email_verification/generate_token_service_spec.rb
new file mode 100644
index 00000000000..e7aa1bf8306
--- /dev/null
+++ b/spec/services/users/email_verification/generate_token_service_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Users::EmailVerification::GenerateTokenService do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:service) { described_class.new(attr: attr) }
+ let(:token) { 'token' }
+ let(:digest) { Devise.token_generator.digest(User, attr, token) }
+
+ describe '#execute' do
+ context 'with a valid attribute' do
+ where(:attr) { [:unlock_token, :confirmation_token] }
+
+ with_them do
+ before do
+ allow_next_instance_of(described_class) do |service|
+ allow(service).to receive(:generate_token).and_return(token)
+ end
+ end
+
+ it "returns a token and it's digest" do
+ expect(service.execute).to eq([token, digest])
+ end
+ end
+ end
+
+ context 'with an invalid attribute' do
+ let(:attr) { :xxx }
+
+ it 'raises an error' do
+ expect { service.execute }.to raise_error(ArgumentError, 'Invalid attribute')
+ end
+ end
+ end
+end
diff --git a/spec/services/users/email_verification/validate_token_service_spec.rb b/spec/services/users/email_verification/validate_token_service_spec.rb
new file mode 100644
index 00000000000..44af4a4d36f
--- /dev/null
+++ b/spec/services/users/email_verification/validate_token_service_spec.rb
@@ -0,0 +1,97 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Users::EmailVerification::ValidateTokenService, :clean_gitlab_redis_rate_limiting do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:service) { described_class.new(attr: attr, user: user, token: token) }
+ let(:token) { 'token' }
+ let(:encrypted_token) { Devise.token_generator.digest(User, attr, token) }
+ let(:generated_at_attr) { attr == :unlock_token ? :locked_at : :confirmation_sent_at }
+ let(:token_generated_at) { 1.minute.ago }
+ let(:user) { build(:user, attr => encrypted_token, generated_at_attr => token_generated_at) }
+
+ describe '#execute' do
+ context 'with a valid attribute' do
+ where(:attr) { [:unlock_token, :confirmation_token] }
+
+ with_them do
+ context 'when successful' do
+ it 'returns a success status' do
+ expect(service.execute).to eq(status: :success)
+ end
+ end
+
+ context 'when rate limited' do
+ before do
+ allow(Gitlab::ApplicationRateLimiter).to receive(:throttled?)
+ .with(:email_verification, scope: encrypted_token).and_return(true)
+ end
+
+ it 'returns a failure status' do
+ expect(service.execute).to eq(
+ {
+ status: :failure,
+ reason: :rate_limited,
+ message: format(s_("IdentityVerification|You've reached the maximum amount of tries. "\
+ 'Wait %{interval} or send a new code and try again.'), interval: '10 minutes')
+ }
+ )
+ end
+ end
+
+ context 'when expired' do
+ let(:token_generated_at) { 2.hours.ago }
+
+ it 'returns a failure status' do
+ expect(service.execute).to eq(
+ {
+ status: :failure,
+ reason: :expired,
+ message: s_('IdentityVerification|The code has expired. Send a new code and try again.')
+ }
+ )
+ end
+ end
+
+ context 'when invalid' do
+ let(:encrypted_token) { 'xxx' }
+
+ it 'returns a failure status' do
+ expect(service.execute).to eq(
+ {
+ status: :failure,
+ reason: :invalid,
+ message: s_('IdentityVerification|The code is incorrect. Enter it again, or send a new code.')
+ }
+ )
+ end
+ end
+
+ context 'when encrypted token was not set and a blank token is provided' do
+ let(:encrypted_token) { nil }
+ let(:token) { '' }
+
+ it 'returns a failure status' do
+ expect(service.execute).to eq(
+ {
+ status: :failure,
+ reason: :invalid,
+ message: s_('IdentityVerification|The code is incorrect. Enter it again, or send a new code.')
+ }
+ )
+ end
+ end
+ end
+ end
+
+ context 'with an invalid attribute' do
+ let(:attr) { :username }
+
+ it 'raises an error' do
+ expect { service.execute }.to raise_error(ArgumentError, 'Invalid attribute')
+ end
+ end
+ end
+end
diff --git a/spec/services/users/migrate_records_to_ghost_user_in_batches_service_spec.rb b/spec/services/users/migrate_records_to_ghost_user_in_batches_service_spec.rb
new file mode 100644
index 00000000000..7366b1646b9
--- /dev/null
+++ b/spec/services/users/migrate_records_to_ghost_user_in_batches_service_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Users::MigrateRecordsToGhostUserInBatchesService do
+ let(:service) { described_class.new }
+
+ let_it_be(:ghost_user_migration) { create(:ghost_user_migration) }
+
+ describe '#execute' do
+ it 'stops when execution time limit reached' do
+ expect_next_instance_of(::Gitlab::Utils::ExecutionTracker) do |tracker|
+ expect(tracker).to receive(:over_limit?).and_return(true)
+ end
+
+ expect(Users::MigrateRecordsToGhostUserService).not_to receive(:new)
+
+ service.execute
+ end
+
+ it 'calls Users::MigrateRecordsToGhostUserService' do
+ expect_next_instance_of(Users::MigrateRecordsToGhostUserService) do |service|
+ expect(service).to(
+ receive(:execute)
+ .with(hard_delete: ghost_user_migration.hard_delete))
+ end
+
+ service.execute
+ end
+ end
+end
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
new file mode 100644
index 00000000000..766be51ae13
--- /dev/null
+++ b/spec/services/users/migrate_records_to_ghost_user_service_spec.rb
@@ -0,0 +1,259 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Users::MigrateRecordsToGhostUserService do
+ let!(:user) { create(:user) }
+ let(:service) { described_class.new(user, admin, execution_tracker) }
+ let(:execution_tracker) { instance_double(::Gitlab::Utils::ExecutionTracker, over_limit?: false) }
+
+ let_it_be(:admin) { create(:admin) }
+ let_it_be(:project) { create(:project, :repository) }
+
+ context "when migrating a user's associated records to the ghost user" do
+ context 'for issues' do
+ context 'when deleted user is present as both author and edited_user' do
+ include_examples 'migrating records to the ghost user', Issue, [:author, :last_edited_by] do
+ let(:created_record) do
+ create(:issue, project: project, author: user, last_edited_by: user)
+ end
+ end
+ end
+
+ context 'when deleted user is present only as edited_user' do
+ include_examples 'migrating records to the ghost user', Issue, [:last_edited_by] do
+ let(:created_record) { create(:issue, project: project, author: create(:user), last_edited_by: user) }
+ end
+ end
+
+ context "when deleted user is the assignee" do
+ let!(:issue) { create(:issue, project: project, assignees: [user]) }
+
+ it 'migrates the issue so that it is "Unassigned"' do
+ service.execute
+
+ migrated_issue = Issue.find_by_id(issue.id)
+ expect(migrated_issue).to be_present
+ expect(migrated_issue.assignees).to be_empty
+ end
+ end
+ end
+
+ context 'for merge requests' do
+ context 'when deleted user is present as both author and merge_user' do
+ include_examples 'migrating records to the ghost user', MergeRequest, [:author, :merge_user] do
+ let(:created_record) do
+ create(:merge_request, source_project: project,
+ author: user,
+ merge_user: user,
+ target_branch: "first")
+ end
+ end
+ end
+
+ context 'when deleted user is present only as both merge_user' do
+ include_examples 'migrating records to the ghost user', MergeRequest, [:merge_user] do
+ let(:created_record) do
+ create(:merge_request, source_project: project,
+ merge_user: user,
+ target_branch: "first")
+ end
+ end
+ end
+
+ context "when deleted user is the assignee" do
+ let!(:merge_request) { create(:merge_request, source_project: project, assignees: [user]) }
+
+ it 'migrates the merge request so that it is "Unassigned"' do
+ service.execute
+
+ migrated_merge_request = MergeRequest.find_by_id(merge_request.id)
+ expect(migrated_merge_request).to be_present
+ expect(migrated_merge_request.assignees).to be_empty
+ end
+ end
+ end
+
+ context 'for notes' do
+ include_examples 'migrating records to the ghost user', Note do
+ let(:created_record) { create(:note, project: project, author: user) }
+ end
+ end
+
+ context 'for abuse reports' do
+ include_examples 'migrating records to the ghost user', AbuseReport do
+ let(:created_record) { create(:abuse_report, reporter: user, user: create(:user)) }
+ end
+ end
+
+ context 'for award emoji' do
+ include_examples 'migrating records to the ghost user', AwardEmoji, [:user] do
+ let(:created_record) { create(:award_emoji, user: user) }
+
+ context "when the awardable already has an award emoji of the same name assigned to the ghost user" do
+ let(:awardable) { create(:issue) }
+ let!(:existing_award_emoji) { create(:award_emoji, user: User.ghost, name: "thumbsup", awardable: awardable) }
+ let!(:award_emoji) { create(:award_emoji, user: user, name: "thumbsup", awardable: awardable) }
+
+ it "migrates the award emoji regardless" do
+ service.execute
+
+ migrated_record = AwardEmoji.find_by_id(award_emoji.id)
+
+ expect(migrated_record.user).to eq(User.ghost)
+ end
+
+ it "does not leave the migrated award emoji in an invalid state" do
+ service.execute
+
+ migrated_record = AwardEmoji.find_by_id(award_emoji.id)
+
+ expect(migrated_record).to be_valid
+ end
+ end
+ end
+ end
+
+ context 'for snippets' do
+ include_examples 'migrating records to the ghost user', Snippet do
+ let(:created_record) { create(:snippet, project: project, author: user) }
+ end
+ end
+
+ context 'for reviews' do
+ include_examples 'migrating records to the ghost user', Review, [:author] do
+ let(:created_record) { create(:review, author: user) }
+ end
+ end
+ end
+
+ context 'on post-migrate cleanups' do
+ it 'destroys the user and personal namespace' do
+ namespace = user.namespace
+
+ allow(user).to receive(:destroy).and_call_original
+
+ service.execute
+
+ expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ expect { Namespace.find(namespace.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'deletes user associations in batches' do
+ expect(user).to receive(:destroy_dependent_associations_in_batches)
+
+ service.execute
+ end
+
+ context 'for batched nullify' do
+ it 'nullifies related associations in batches' do
+ expect(user).to receive(:nullify_dependent_associations_in_batches).and_call_original
+
+ service.execute
+ end
+
+ it 'nullifies last_updated_issues, closed_issues, resource_label_events' do
+ issue = create(:issue, closed_by: user, updated_by: user)
+ resource_label_event = create(:resource_label_event, user: user)
+
+ service.execute
+
+ issue.reload
+ resource_label_event.reload
+
+ expect(issue.closed_by).to be_nil
+ expect(issue.updated_by).to be_nil
+ expect(resource_label_event.user).to be_nil
+ end
+ end
+
+ context 'for snippets' do
+ let(:gitlab_shell) { Gitlab::Shell.new }
+
+ it 'does not include snippets when deleting in batches' do
+ expect(user).to receive(:destroy_dependent_associations_in_batches).with({ exclude: [:snippets] })
+
+ service.execute
+ end
+
+ it 'calls the bulk snippet destroy service for the user personal snippets' do
+ repo1 = create(:personal_snippet, :repository, author: user).snippet_repository
+ repo2 = create(:project_snippet, :repository, project: project, author: user).snippet_repository
+
+ aggregate_failures do
+ expect(gitlab_shell.repository_exists?(repo1.shard_name, "#{repo1.disk_path}.git")).to be(true)
+ expect(gitlab_shell.repository_exists?(repo2.shard_name, "#{repo2.disk_path}.git")).to be(true)
+ end
+
+ # Call made when destroying user personal projects
+ expect(Snippets::BulkDestroyService).not_to(
+ receive(:new).with(admin, project.snippets).and_call_original)
+
+ # Call to remove user personal snippets and for
+ # project snippets where projects are not user personal
+ # ones
+ expect(Snippets::BulkDestroyService).to(
+ receive(:new).with(admin, user.snippets.only_personal_snippets).and_call_original)
+
+ service.execute
+
+ aggregate_failures do
+ expect(gitlab_shell.repository_exists?(repo1.shard_name, "#{repo1.disk_path}.git")).to be(false)
+ expect(gitlab_shell.repository_exists?(repo2.shard_name, "#{repo2.disk_path}.git")).to be(true)
+ end
+ end
+
+ it 'calls the bulk snippet destroy service with hard delete option if it is present' do
+ # this avoids getting into Projects::DestroyService as it would
+ # call Snippets::BulkDestroyService first!
+ allow(user).to receive(:personal_projects).and_return([])
+
+ expect_next_instance_of(Snippets::BulkDestroyService) do |bulk_destroy_service|
+ expect(bulk_destroy_service).to receive(:execute).with({ skip_authorization: true }).and_call_original
+ end
+
+ service.execute(hard_delete: true)
+ end
+
+ it 'does not delete project snippets that the user is the author of' do
+ repo = create(:project_snippet, :repository, author: user).snippet_repository
+
+ service.execute
+
+ expect(gitlab_shell.repository_exists?(repo.shard_name, "#{repo.disk_path}.git")).to be(true)
+ expect(User.ghost.snippets).to include(repo.snippet)
+ end
+
+ context 'when an error is raised deleting snippets' do
+ it 'does not delete user' do
+ snippet = create(:personal_snippet, :repository, author: user)
+
+ bulk_service = double
+ allow(Snippets::BulkDestroyService).to receive(:new).and_call_original
+ allow(Snippets::BulkDestroyService).to receive(:new).with(admin, user.snippets).and_return(bulk_service)
+ allow(bulk_service).to receive(:execute).and_return(ServiceResponse.error(message: 'foo'))
+
+ aggregate_failures do
+ expect { service.execute }.to(
+ raise_error(Users::MigrateRecordsToGhostUserService::DestroyError, 'foo' ))
+ expect(snippet.reload).not_to be_nil
+ expect(
+ gitlab_shell.repository_exists?(snippet.repository_storage,
+ "#{snippet.disk_path}.git")
+ ).to be(true)
+ end
+ end
+ end
+ end
+
+ context 'when hard_delete option is given' do
+ it 'will not ghost certain records' do
+ issue = create(:issue, author: user)
+
+ service.execute(hard_delete: true)
+
+ expect(Issue).not_to exist(issue.id)
+ end
+ end
+ end
+end
diff --git a/spec/services/users/reject_service_spec.rb b/spec/services/users/reject_service_spec.rb
index 5a243e876ac..abff6b1e023 100644
--- a/spec/services/users/reject_service_spec.rb
+++ b/spec/services/users/reject_service_spec.rb
@@ -35,11 +35,29 @@ RSpec.describe Users::RejectService do
context 'success' do
context 'when the executor user is an admin in admin mode', :enable_admin_mode do
- it 'deletes the user', :sidekiq_inline do
- subject
+ context 'when user_destroy_with_limited_execution_time_worker is enabled' do
+ it 'initiates user removal', :sidekiq_inline do
+ subject
+
+ expect(subject[:status]).to eq(:success)
+ expect(
+ Users::GhostUserMigration.where(user: user,
+ initiator_user: current_user)
+ ).to be_exists
+ end
+ end
- expect(subject[:status]).to eq(:success)
- expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ context 'when user_destroy_with_limited_execution_time_worker is disabled' do
+ before do
+ stub_feature_flags(user_destroy_with_limited_execution_time_worker: false)
+ end
+
+ it 'deletes the user', :sidekiq_inline do
+ subject
+
+ expect(subject[:status]).to eq(:success)
+ expect { User.find(user.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
end
it 'emails the user on rejection' do
diff --git a/spec/services/work_items/update_service_spec.rb b/spec/services/work_items/update_service_spec.rb
index 2e0b0051495..e8b82b0b4f2 100644
--- a/spec/services/work_items/update_service_spec.rb
+++ b/spec/services/work_items/update_service_spec.rb
@@ -54,6 +54,12 @@ RSpec.describe WorkItems::UpdateService do
expect(Gitlab::UsageDataCounters::IssueActivityUniqueCounter).to receive(:track_issue_title_changed_action)
expect(update_work_item[:status]).to eq(:success)
end
+
+ it_behaves_like 'issue_edit snowplow tracking' do
+ let(:property) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_TITLE_CHANGED }
+ let(:user) { current_user }
+ subject(:service_action) { update_work_item[:status] }
+ end
end
context 'when title is not changed' do
@@ -64,6 +70,12 @@ RSpec.describe WorkItems::UpdateService do
expect(Gitlab::UsageDataCounters::WorkItemActivityUniqueCounter).not_to receive(:track_work_item_title_changed_action)
expect(update_work_item[:status]).to eq(:success)
end
+
+ it 'does not emit Snowplow event', :snowplow do
+ expect_no_snowplow_event
+
+ update_work_item
+ end
end
context 'when dates are changed' do
diff --git a/spec/services/work_items/widgets/description_service/update_service_spec.rb b/spec/services/work_items/widgets/description_service/update_service_spec.rb
index 582d9dc85f7..4275950e720 100644
--- a/spec/services/work_items/widgets/description_service/update_service_spec.rb
+++ b/spec/services/work_items/widgets/description_service/update_service_spec.rb
@@ -13,7 +13,7 @@ RSpec.describe WorkItems::Widgets::DescriptionService::UpdateService do
let(:current_user) { author }
let(:work_item) do
create(:work_item, author: author, project: project, description: 'old description',
- last_edited_at: Date.yesterday, last_edited_by: random_user
+ last_edited_at: Date.yesterday, last_edited_by: random_user
)
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 8acf3bcf9c0..c75f651fb92 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -36,6 +36,7 @@ require 'rspec-parameterized'
require 'shoulda/matchers'
require 'test_prof/recipes/rspec/let_it_be'
require 'test_prof/factory_default'
+require 'test_prof/factory_prof/nate_heckler'
require 'parslet/rig/rspec'
rspec_profiling_is_configured =
@@ -53,6 +54,9 @@ end
require 'rainbow/ext/string'
Rainbow.enabled = false
+# Enable zero monkey patching mode before loading any other RSpec code.
+RSpec.configure(&:disable_monkey_patching!)
+
require_relative('../ee/spec/spec_helper') if Gitlab.ee?
require_relative('../jh/spec/spec_helper') if Gitlab.jh?
@@ -89,30 +93,6 @@ RSpec.configure do |config|
config.full_backtrace = true
end
- # Attempt to troubleshoot https://gitlab.com/gitlab-org/gitlab/-/issues/297359
- if ENV['CI']
- config.after do |example|
- if example.exception.is_a?(GRPC::Unavailable)
- warn "=== gRPC unavailable detected, process list:"
- processes = `ps -ef | grep toml`
- warn processes
- warn "=== free memory"
- warn `free -m`
- warn "=== uptime"
- warn `uptime`
- warn "=== Prometheus metrics:"
- warn `curl -s -o log/gitaly-metrics.log http://localhost:9236/metrics`
- warn "=== Taking goroutine dump in log/goroutines.log..."
- warn `curl -s -o log/goroutines.log http://localhost:9236/debug/pprof/goroutine?debug=2`
- end
- end
- else
- # Allow running `:focus` examples locally,
- # falling back to all tests when there is no `:focus` example.
- config.filter_run focus: true
- config.run_all_when_everything_filtered = true
- end
-
# Attempt to troubleshoot https://gitlab.com/gitlab-org/gitlab/-/issues/351531
config.after do |example|
if example.exception.is_a?(Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification::CrossDatabaseModificationAcrossUnsupportedTablesError)
@@ -122,9 +102,6 @@ RSpec.configure do |config|
end
end
- # Re-run failures locally with `--only-failures`
- config.example_status_persistence_file_path = ENV.fetch('RSPEC_LAST_RUN_RESULTS_FILE', './spec/examples.txt')
-
config.define_derived_metadata(file_path: %r{(ee)?/spec/.+_spec\.rb\z}) do |metadata|
location = metadata[:location]
@@ -170,6 +147,7 @@ RSpec.configure do |config|
config.include TestEnv
config.include FileReadHelpers
config.include Database::MultipleDatabases
+ config.include Database::WithoutCheckConstraint
config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::ControllerHelpers, type: :view
config.include Devise::Test::IntegrationHelpers, type: :feature
@@ -397,6 +375,12 @@ RSpec.configure do |config|
example.run unless GitalySetup.praefect_with_db?
end
+ config.around(:example, :yaml_processor_feature_flag_corectness) do |example|
+ ::Gitlab::Ci::YamlProcessor::FeatureFlags.ensure_correct_usage do
+ example.run
+ end
+ end
+
# previous test runs may have left some resources throttled
config.before do
::Gitlab::ExclusiveLease.reset_all!("el:throttle:*")
@@ -478,8 +462,6 @@ RSpec.configure do |config|
config.before(:each, :js) do
allow_any_instance_of(VersionCheck).to receive(:response).and_return({ "severity" => "success" })
end
-
- config.disable_monkey_patching!
end
ActiveRecord::Migration.maintain_test_schema!
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
index 14ef0f1b7e0..a5d845f5177 100644
--- a/spec/support/capybara.rb
+++ b/spec/support/capybara.rb
@@ -72,6 +72,12 @@ Capybara.register_driver :chrome do |app|
# Explicitly set user-data-dir to prevent crashes. See https://gitlab.com/gitlab-org/gitlab-foss/issues/58882#note_179811508
options.add_argument("user-data-dir=/tmp/chrome") if ENV['CI'] || ENV['CI_SERVER']
+ # Set chrome default download path
+ if ENV['DEFAULT_CHROME_DOWNLOAD_PATH']
+ options.add_preference("download.default_directory", ENV['DEFAULT_CHROME_DOWNLOAD_PATH'])
+ options.add_preference("download.prompt_for_download", false)
+ end
+
# Chrome 75 defaults to W3C mode which doesn't allow console log access
options.add_option(:w3c, false)
diff --git a/spec/support/database/without_check_constraint.rb b/spec/support/database/without_check_constraint.rb
new file mode 100644
index 00000000000..b361f4374b8
--- /dev/null
+++ b/spec/support/database/without_check_constraint.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+# Temporarily disable the named constraint on the table within the block.
+#
+# without_constraint('members', 'check_1234') do
+# create_invalid_data
+# end
+module Database
+ module WithoutCheckConstraint
+ def without_check_constraint(table, name, connection:)
+ saved_constraint = constraint(table, name, connection)
+
+ constraint_error!(table, name, connection) if saved_constraint.nil?
+
+ begin
+ connection.remove_check_constraint(table, name: name)
+ connection.transaction do
+ yield
+ raise ActiveRecord::Rollback
+ end
+ ensure
+ restore_constraint(saved_constraint, connection)
+ end
+ end
+
+ private
+
+ def constraint_error!(table, name, connection)
+ msg = if connection.table_exists?(table)
+ "'#{table}' table does not contain constraint called '#{name}'"
+ else
+ "'#{table}' does not exist"
+ end
+
+ raise msg
+ end
+
+ def constraint(table, name, connection)
+ connection
+ .check_constraints(table)
+ .find { |constraint| constraint.options[:name] == name }
+ end
+
+ def restore_constraint(constraint, connection)
+ connection.add_check_constraint(
+ constraint.table_name,
+ constraint.expression,
+ **constraint.options
+ )
+ end
+ end
+end
diff --git a/spec/support/gitlab_stubs/gitlab_ci.yml b/spec/support/gitlab_stubs/gitlab_ci.yml
index b1533879e32..b6a66cfa2c6 100644
--- a/spec/support/gitlab_stubs/gitlab_ci.yml
+++ b/spec/support/gitlab_stubs/gitlab_ci.yml
@@ -8,6 +8,9 @@ before_script:
variables:
DB_NAME: postgres
+ ENVIRONMENT_VAR:
+ value: 'env var value'
+ description: 'env var description'
stages:
- test
diff --git a/spec/support/helpers/api_internal_base_helpers.rb b/spec/support/helpers/api_internal_base_helpers.rb
index 94996f7480e..e89716571f9 100644
--- a/spec/support/helpers/api_internal_base_helpers.rb
+++ b/spec/support/helpers/api_internal_base_helpers.rb
@@ -1,6 +1,10 @@
# frozen_string_literal: true
+require_relative 'gitlab_shell_helpers'
+
module APIInternalBaseHelpers
+ include GitlabShellHelpers
+
def gl_repository_for(container)
case container
when ProjectWiki
@@ -33,9 +37,9 @@ module APIInternalBaseHelpers
project: full_path_for(container),
gl_repository: gl_repository_for(container),
action: 'git-upload-pack',
- secret_token: secret_token,
protocol: protocol
- }
+ },
+ headers: gitlab_shell_internal_api_request_header
)
end
@@ -56,7 +60,6 @@ module APIInternalBaseHelpers
key_id: key.id,
project: full_path,
action: 'git-receive-pack',
- secret_token: secret_token,
protocol: protocol,
env: env
}
@@ -64,7 +67,8 @@ module APIInternalBaseHelpers
post(
api("/internal/allowed"),
- params: params
+ params: params,
+ headers: gitlab_shell_internal_api_request_header
)
end
@@ -77,9 +81,9 @@ module APIInternalBaseHelpers
project: full_path_for(container),
gl_repository: gl_repository_for(container),
action: 'git-upload-archive',
- secret_token: secret_token,
protocol: 'ssh'
- }
+ },
+ headers: gitlab_shell_internal_api_request_header
)
end
end
diff --git a/spec/support/helpers/ci/template_helpers.rb b/spec/support/helpers/ci/template_helpers.rb
index 119f8d001a1..2e9b6f748cd 100644
--- a/spec/support/helpers/ci/template_helpers.rb
+++ b/spec/support/helpers/ci/template_helpers.rb
@@ -2,10 +2,6 @@
module Ci
module TemplateHelpers
- def secure_analyzers_prefix
- 'registry.gitlab.com/security-products'
- end
-
def template_registry_host
'registry.gitlab.com'
end
diff --git a/spec/support/helpers/create_environments_helpers.rb b/spec/support/helpers/create_environments_helpers.rb
index be105f5862b..361d365dc5b 100644
--- a/spec/support/helpers/create_environments_helpers.rb
+++ b/spec/support/helpers/create_environments_helpers.rb
@@ -7,7 +7,7 @@ module CreateEnvironmentsHelpers
start_review = create(:ci_build, :start_review_app, :success, **common, pipeline: pipeline)
stop_review = create(:ci_build, :stop_review_app, :manual, **common, pipeline: pipeline)
environment = create(:environment, :auto_stoppable, project: project, name: ref)
- create(:deployment, :success, **common, on_stop: stop_review.name,
- deployable: start_review, environment: environment)
+ create(:deployment, :success, **common,
+ on_stop: stop_review.name, deployable: start_review, environment: environment)
end
end
diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb
index 05e9a099a2b..6d41d7b7414 100644
--- a/spec/support/helpers/cycle_analytics_helpers.rb
+++ b/spec/support/helpers/cycle_analytics_helpers.rb
@@ -7,7 +7,7 @@ module CycleAnalyticsHelpers
def path_nav_stage_names_without_median
# Returns the path names with the median value stripped out
- page.all('.gl-path-button').collect(&:text).map {|name_with_median| name_with_median.split("\n")[0] }
+ page.all('.gl-path-button').collect(&:text).map { |name_with_median| name_with_median.split("\n")[0] }
end
def fill_in_custom_stage_fields
diff --git a/spec/support/helpers/database/partitioning_helpers.rb b/spec/support/helpers/database/partitioning_helpers.rb
index 80b31fe0603..889652a9252 100644
--- a/spec/support/helpers/database/partitioning_helpers.rb
+++ b/spec/support/helpers/database/partitioning_helpers.rb
@@ -79,8 +79,8 @@ module Database
SQL
end
- def find_partitions(partition, schema: Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA)
- connection.select_rows(<<~SQL)
+ def find_partitions(partition, schema: Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA, conn: connection)
+ conn.select_rows(<<~SQL)
select
pg_class.relname
from pg_class
diff --git a/spec/support/helpers/gitlab_shell_helpers.rb b/spec/support/helpers/gitlab_shell_helpers.rb
new file mode 100644
index 00000000000..aa0cec22727
--- /dev/null
+++ b/spec/support/helpers/gitlab_shell_helpers.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module GitlabShellHelpers
+ extend self
+
+ def gitlab_shell_internal_api_request_header(
+ issuer: API::Helpers::GITLAB_SHELL_JWT_ISSUER, secret_token: Gitlab::Shell.secret_token)
+ jwt_token = JSONWebToken::HMACToken.new(secret_token).tap do |token|
+ token.issuer = issuer
+ end
+
+ { API::Helpers::GITLAB_SHELL_API_HEADER => jwt_token.encoded }
+ end
+end
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index d78c523decd..9d745f2cb70 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -130,11 +130,12 @@ module GraphqlHelpers
current_user: :not_given, # The current user (specified explicitly, overrides ctx[:current_user])
schema: GitlabSchema, # A specific schema instance
object_type: described_class, # The `BaseObject` type this field belongs to
- arg_style: :internal_prepared # Args are in internal format, but should use more rigorous processing
+ arg_style: :internal_prepared, # Args are in internal format, but should use more rigorous processing
+ query: nil # Query to evaluate the field
)
field = to_base_field(field, object_type)
ctx[:current_user] = current_user unless current_user == :not_given
- query = GraphQL::Query.new(schema, context: ctx.to_h)
+ query ||= GraphQL::Query.new(schema, context: ctx.to_h)
extras[:lookahead] = negative_lookahead if extras[:lookahead] == :not_given && field.extras.include?(:lookahead)
query_ctx = query.context
@@ -857,6 +858,8 @@ module GraphqlHelpers
def positive_lookahead
double(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))
end
end
diff --git a/spec/support/helpers/html_escaped_helpers.rb b/spec/support/helpers/html_escaped_helpers.rb
new file mode 100644
index 00000000000..7f6825e9598
--- /dev/null
+++ b/spec/support/helpers/html_escaped_helpers.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module HtmlEscapedHelpers
+ extend self
+
+ # Checks if +content+ contains HTML escaped tags and returns its match.
+ #
+ # It matches escaped opening and closing tags `&lt;<name>` and
+ # `&lt;/<name>`. The match is discarded if the tag is inside a quoted
+ # attribute value.
+ # Foor example, `<div title="We allow # &lt;b&gt;bold&lt;/b&gt;">`.
+ #
+ # @return [MatchData, nil] Returns the match or +nil+ if no match was found.
+ def match_html_escaped_tags(content)
+ match_data = %r{&lt;\s*(?:/\s*)?\w+}.match(content)
+ return unless match_data
+
+ # Escaped HTML tags are allowed inside quoted attribute values like:
+ # `title="Press &lt;back&gt;"`
+ return if %r{=\s*["'][^>]*\z}.match?(match_data.pre_match)
+
+ match_data
+ end
+end
diff --git a/spec/support/helpers/javascript_form_helper.rb b/spec/support/helpers/javascript_form_helper.rb
new file mode 100644
index 00000000000..41c5ba4373b
--- /dev/null
+++ b/spec/support/helpers/javascript_form_helper.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module JavascriptFormHelper
+ def prevent_submit_for(query_selector)
+ execute_script("document.querySelector('#{query_selector}').addEventListener('submit', e => e.preventDefault())")
+ end
+end
diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb
index dd210f02ae7..912e7d24b25 100644
--- a/spec/support/helpers/kubernetes_helpers.rb
+++ b/spec/support/helpers/kubernetes_helpers.rb
@@ -880,7 +880,7 @@ module KubernetesHelpers
containers.map do |container|
terminal = {
selectors: { pod: pod_name, container: container['name'] },
- url: container_exec_url(service.api_url, pod_namespace, pod_name, container['name']),
+ url: container_exec_url(service.api_url, pod_namespace, pod_name, container['name']),
subprotocols: ['channel.k8s.io'],
headers: { 'Authorization' => ["Bearer #{service.token}"] },
created_at: DateTime.parse(pod['metadata']['creationTimestamp']),
diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb
index d966fd13dca..87a1f5459ec 100644
--- a/spec/support/helpers/login_helpers.rb
+++ b/spec/support/helpers/login_helpers.rb
@@ -122,7 +122,7 @@ module LoginHelpers
def register_via(provider, uid, email, additional_info: {})
mock_auth_hash(provider, uid, email, additional_info: additional_info)
visit new_user_registration_path
- expect(page).to have_content('Create an account using')
+ expect(page).to have_content('Create an account using').or(have_content('Register with'))
click_link_or_button "oauth-login-#{provider}"
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 59b1f1b1305..b05caf265ee 100644
--- a/spec/support/helpers/migrations_helpers/work_item_types_helper.rb
+++ b/spec/support/helpers/migrations_helpers/work_item_types_helper.rb
@@ -3,11 +3,11 @@
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 },
+ 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 }
+ task: { name: 'Task', icon_name: 'issue-type-task', enum_value: 4 }
}.freeze
def reset_work_item_types
diff --git a/spec/support/helpers/navbar_structure_helper.rb b/spec/support/helpers/navbar_structure_helper.rb
index e11548d0b75..b44552d6479 100644
--- a/spec/support/helpers/navbar_structure_helper.rb
+++ b/spec/support/helpers/navbar_structure_helper.rb
@@ -34,7 +34,7 @@ module NavbarStructureHelper
insert_after_nav_item(
within,
new_nav_item: {
- nav_item: _('Packages & Registries'),
+ nav_item: _('Packages and registries'),
nav_sub_items: [_('Package Registry')]
}
)
@@ -56,7 +56,7 @@ module NavbarStructureHelper
def insert_container_nav
insert_after_sub_nav_item(
_('Package Registry'),
- within: _('Packages & Registries'),
+ within: _('Packages and registries'),
new_sub_nav_item_name: _('Container Registry')
)
end
@@ -64,7 +64,7 @@ module NavbarStructureHelper
def insert_dependency_proxy_nav
insert_after_sub_nav_item(
_('Package Registry'),
- within: _('Packages & Registries'),
+ within: _('Packages and registries'),
new_sub_nav_item_name: _('Dependency Proxy')
)
end
@@ -72,7 +72,7 @@ module NavbarStructureHelper
def insert_infrastructure_registry_nav
insert_after_sub_nav_item(
_('Package Registry'),
- within: _('Packages & Registries'),
+ within: _('Packages and registries'),
new_sub_nav_item_name: _('Infrastructure Registry')
)
end
@@ -80,11 +80,21 @@ module NavbarStructureHelper
def insert_harbor_registry_nav(within)
insert_after_sub_nav_item(
within,
- within: _('Packages & Registries'),
+ within: _('Packages and registries'),
new_sub_nav_item_name: _('Harbor Registry')
)
end
+ def insert_observability_nav
+ insert_after_nav_item(
+ _('Kubernetes'),
+ new_nav_item: {
+ nav_item: _('Observability'),
+ nav_sub_items: []
+ }
+ )
+ end
+
def insert_infrastructure_google_cloud_nav
insert_after_sub_nav_item(
_('Terraform'),
diff --git a/spec/support/helpers/seed_helper.rb b/spec/support/helpers/seed_helper.rb
index 59723583cbc..9628762d46a 100644
--- a/spec/support/helpers/seed_helper.rb
+++ b/spec/support/helpers/seed_helper.rb
@@ -29,8 +29,8 @@ module SeedHelper
def create_bare_seeds
system(git_env, *%W(#{Gitlab.config.git.bin_path} clone --bare #{GITLAB_GIT_TEST_REPO_URL}),
chdir: SEED_STORAGE_PATH,
- out: '/dev/null',
- err: '/dev/null')
+ out: '/dev/null',
+ err: '/dev/null')
end
def create_normal_seeds
diff --git a/spec/support/helpers/snowplow_helpers.rb b/spec/support/helpers/snowplow_helpers.rb
index c8b194919ed..265e1c38b09 100644
--- a/spec/support/helpers/snowplow_helpers.rb
+++ b/spec/support/helpers/snowplow_helpers.rb
@@ -7,7 +7,7 @@ module SnowplowHelpers
#
# Examples:
#
- # describe '#show', :snowplow do
+ # describe '#show' do
# it 'tracks snowplow events' do
# get :show
#
@@ -15,7 +15,7 @@ module SnowplowHelpers
# end
# end
#
- # describe '#create', :snowplow do
+ # describe '#create' do
# it 'tracks snowplow events' do
# post :create
#
diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb
index 20f46396424..c08e35912c3 100644
--- a/spec/support/helpers/stub_configuration.rb
+++ b/spec/support/helpers/stub_configuration.rb
@@ -104,6 +104,10 @@ module StubConfiguration
.to receive(:sentry_clientside_dsn) { clientside_dsn }
end
+ def stub_microsoft_graph_mailer_setting(messages)
+ allow(Gitlab.config.microsoft_graph_mailer).to receive_messages(to_settings(messages))
+ end
+
def stub_kerberos_setting(messages)
allow(Gitlab.config.kerberos).to receive_messages(to_settings(messages))
end
diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb
index 024f06cae1b..661c1c683b0 100644
--- a/spec/support/helpers/stub_object_storage.rb
+++ b/spec/support/helpers/stub_object_storage.rb
@@ -3,8 +3,8 @@
module StubObjectStorage
def stub_dependency_proxy_object_storage(**params)
stub_object_storage_uploader(config: ::Gitlab.config.dependency_proxy.object_store,
- uploader: ::DependencyProxy::FileUploader,
- **params)
+ uploader: ::DependencyProxy::FileUploader,
+ **params)
end
def stub_object_storage_uploader(
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index 03e9ad1a08e..691f978550a 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -10,85 +10,86 @@ module TestEnv
# When developing the seed repository, comment out the branch you will modify.
BRANCH_SHA = {
- 'signed-commits' => 'c7794c1',
- 'not-merged-branch' => 'b83d6e3',
- 'branch-merged' => '498214d',
- 'empty-branch' => '7efb185',
- 'ends-with.json' => '98b0d8b',
- 'flatten-dir' => 'e56497b',
- 'feature' => '0b4bc9a',
- 'feature_conflict' => 'bb5206f',
- 'fix' => '48f0be4',
- 'improve/awesome' => '5937ac0',
- 'merged-target' => '21751bf',
- 'markdown' => '0ed8c6c',
- 'lfs' => '55bc176',
- 'master' => 'b83d6e3',
- 'merge-test' => '5937ac0',
- "'test'" => 'e56497b',
- 'orphaned-branch' => '45127a9',
- 'binary-encoding' => '7b1cf43',
- 'gitattributes' => '5a62481',
- 'expand-collapse-diffs' => '4842455',
- 'symlink-expand-diff' => '81e6355',
- 'diff-files-symlink-to-image' => '8cfca84',
- 'diff-files-image-to-symlink' => '3e94fda',
- 'diff-files-symlink-to-text' => '689815e',
- 'diff-files-text-to-symlink' => '5e2c270',
- 'expand-collapse-files' => '025db92',
- 'expand-collapse-lines' => '238e82d',
- 'pages-deploy' => '7897d5b',
- 'pages-deploy-target' => '7975be0',
- 'audio' => 'c3c21fd',
- 'video' => '8879059',
- 'crlf-diff' => '5938907',
- 'conflict-start' => '824be60',
- 'conflict-resolvable' => '1450cd6',
- 'conflict-binary-file' => '259a6fb',
+ 'signed-commits' => 'c7794c1',
+ 'not-merged-branch' => 'b83d6e3',
+ 'branch-merged' => '498214d',
+ 'empty-branch' => '7efb185',
+ 'ends-with.json' => '98b0d8b',
+ 'flatten-dir' => 'e56497b',
+ 'feature' => '0b4bc9a',
+ 'feature_conflict' => 'bb5206f',
+ 'fix' => '48f0be4',
+ 'improve/awesome' => '5937ac0',
+ 'merged-target' => '21751bf',
+ 'markdown' => '0ed8c6c',
+ 'lfs' => '55bc176',
+ 'master' => 'b83d6e391c22777fca1ed3012fce84f633d7fed0',
+ 'merge-test' => '5937ac0',
+ "'test'" => 'e56497b',
+ 'orphaned-branch' => '45127a9',
+ 'binary-encoding' => '7b1cf43',
+ 'gitattributes' => '5a62481',
+ 'expand-collapse-diffs' => '4842455',
+ 'symlink-expand-diff' => '81e6355',
+ 'diff-files-symlink-to-image' => '8cfca84',
+ 'diff-files-image-to-symlink' => '3e94fda',
+ 'diff-files-symlink-to-text' => '689815e',
+ 'diff-files-text-to-symlink' => '5e2c270',
+ 'expand-collapse-files' => '025db92',
+ 'expand-collapse-lines' => '238e82d',
+ 'pages-deploy' => '7897d5b',
+ 'pages-deploy-target' => '7975be0',
+ 'audio' => 'c3c21fd',
+ 'video' => '8879059',
+ 'crlf-diff' => '5938907',
+ 'conflict-start' => '824be60',
+ 'conflict-resolvable' => '1450cd6',
+ 'conflict-binary-file' => '259a6fb',
'conflict-contains-conflict-markers' => '78a3086',
- 'conflict-missing-side' => 'eb227b3',
- 'conflict-non-utf8' => 'd0a293c',
- 'conflict-too-large' => '39fa04f',
- 'deleted-image-test' => '6c17798',
- 'wip' => 'b9238ee',
- 'csv' => '3dd0896',
- 'v1.1.0' => 'b83d6e3',
- 'add-ipython-files' => '4963fef',
- 'add-pdf-file' => 'e774ebd',
- 'squash-large-files' => '54cec52',
- 'add-pdf-text-binary' => '79faa7b',
- 'add_images_and_changes' => '010d106',
- 'update-gitlab-shell-v-6-0-1' => '2f61d70',
- 'update-gitlab-shell-v-6-0-3' => 'de78448',
- 'merge-commit-analyze-before' => '1adbdef',
- 'merge-commit-analyze-side-branch' => '8a99451',
- 'merge-commit-analyze-after' => '646ece5',
- 'snippet/single-file' => '43e4080aaa14fc7d4b77ee1f5c9d067d5a7df10e',
- 'snippet/multiple-files' => '40232f7eb98b3f221886432def6e8bab2432add9',
- 'snippet/rename-and-edit-file' => '220a1e4b4dff37feea0625a7947a4c60fbe78365',
- 'snippet/edit-file' => 'c2f074f4f26929c92795a75775af79a6ed6d8430',
- 'snippet/no-files' => '671aaa842a4875e5f30082d1ab6feda345fdb94d',
- '2-mb-file' => 'bf12d25',
- 'before-create-delete-modify-move' => '845009f',
- 'between-create-delete-modify-move' => '3f5f443',
- 'after-create-delete-modify-move' => 'ba3faa7',
- 'with-codeowners' => '219560e',
- 'submodule_inside_folder' => 'b491b92',
- 'png-lfs' => 'fe42f41',
- 'sha-starting-with-large-number' => '8426165',
- 'invalid-utf8-diff-paths' => '99e4853',
- 'compare-with-merge-head-source' => 'f20a03d',
- 'compare-with-merge-head-target' => '2f1e176',
- 'trailers' => 'f0a5ed6',
- 'add_commit_with_5mb_subject' => '8cf8e80',
- 'blame-on-renamed' => '32c33da',
- 'with-executables' => '6b8dc4a',
- 'spooky-stuff' => 'ba3343b',
- 'few-commits' => '0031876',
- 'two-commits' => '304d257',
- 'utf-16' => 'f05a987',
- 'gitaly-rename-test' => '94bb47c',
- 'smime-signed-commits' => 'ed775cc'
+ 'conflict-missing-side' => 'eb227b3',
+ 'conflict-non-utf8' => 'd0a293c',
+ 'conflict-too-large' => '39fa04f',
+ 'deleted-image-test' => '6c17798',
+ 'wip' => 'b9238ee',
+ 'csv' => '3dd0896',
+ 'v1.1.0' => 'b83d6e3',
+ 'add-ipython-files' => '4963fef',
+ 'add-pdf-file' => 'e774ebd',
+ 'squash-large-files' => '54cec52',
+ 'add-pdf-text-binary' => '79faa7b',
+ 'add_images_and_changes' => '010d106',
+ 'update-gitlab-shell-v-6-0-1' => '2f61d70',
+ 'update-gitlab-shell-v-6-0-3' => 'de78448',
+ 'merge-commit-analyze-before' => '1adbdef',
+ 'merge-commit-analyze-side-branch' => '8a99451',
+ 'merge-commit-analyze-after' => '646ece5',
+ 'snippet/single-file' => '43e4080aaa14fc7d4b77ee1f5c9d067d5a7df10e',
+ 'snippet/multiple-files' => '40232f7eb98b3f221886432def6e8bab2432add9',
+ 'snippet/rename-and-edit-file' => '220a1e4b4dff37feea0625a7947a4c60fbe78365',
+ 'snippet/edit-file' => 'c2f074f4f26929c92795a75775af79a6ed6d8430',
+ 'snippet/no-files' => '671aaa842a4875e5f30082d1ab6feda345fdb94d',
+ '2-mb-file' => 'bf12d25',
+ 'before-create-delete-modify-move' => '845009f',
+ 'between-create-delete-modify-move' => '3f5f443',
+ 'after-create-delete-modify-move' => 'ba3faa7',
+ 'with-codeowners' => '219560e',
+ 'submodule_inside_folder' => 'b491b92',
+ 'png-lfs' => 'fe42f41',
+ 'sha-starting-with-large-number' => '8426165',
+ 'invalid-utf8-diff-paths' => '99e4853',
+ 'compare-with-merge-head-source' => 'f20a03d',
+ 'compare-with-merge-head-target' => '2f1e176',
+ 'trailers' => 'f0a5ed6',
+ 'add_commit_with_5mb_subject' => '8cf8e80',
+ 'blame-on-renamed' => '32c33da',
+ 'with-executables' => '6b8dc4a',
+ 'spooky-stuff' => 'ba3343b',
+ 'few-commits' => '0031876',
+ 'two-commits' => '304d257',
+ 'utf-16' => 'f05a987',
+ 'gitaly-rename-test' => '94bb47c',
+ 'smime-signed-commits' => 'ed775cc',
+ 'Ääh-test-utf-8' => '7975be0'
}.freeze
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
@@ -96,9 +97,9 @@ module TestEnv
# We currently only need a subset of the branches
FORKED_BRANCH_SHA = {
'add-submodule-version-bump' => '3f547c0',
- 'master' => '5937ac0',
- 'remove-submodule' => '2a33e0c',
- 'conflict-resolvable-fork' => '404fa3f'
+ 'master' => '5937ac0',
+ 'remove-submodule' => '2a33e0c',
+ 'conflict-resolvable-fork' => '404fa3f'
}.freeze
TMP_TEST_PATH = Rails.root.join('tmp', 'tests').freeze
diff --git a/spec/support/helpers/usage_data_helpers.rb b/spec/support/helpers/usage_data_helpers.rb
index 2a9144614d0..1aea3545ae0 100644
--- a/spec/support/helpers/usage_data_helpers.rb
+++ b/spec/support/helpers/usage_data_helpers.rb
@@ -6,22 +6,14 @@ module UsageDataHelpers
snippet_update
snippet_comment
merge_request_comment
- merge_request_create
commit_comment
wiki_pages_create
wiki_pages_update
wiki_pages_delete
- web_ide_views
- web_ide_commits
- web_ide_merge_requests
- web_ide_previews
navbar_searches
cycle_analytics_views
productivity_analytics_views
source_code_pushes
- design_management_designs_create
- design_management_designs_update
- design_management_designs_delete
).freeze
COUNTS_KEYS = %i(
@@ -126,7 +118,6 @@ module UsageDataHelpers
uploads
web_hooks
user_preferences_user_gitpod_enabled
- service_usage_data_download_payload_click
).push(*SMAU_KEYS)
USAGE_DATA_KEYS = %i(
@@ -193,11 +184,11 @@ module UsageDataHelpers
allow(Settings).to receive(:[]).with('artifacts')
.and_return(
{ 'enabled' => true,
- 'object_store' =>
+ 'object_store' =>
{ 'enabled' => true,
- 'remote_directory' => 'artifacts',
- 'direct_upload' => true,
- 'connection' =>
+ 'remote_directory' => 'artifacts',
+ '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 } }
@@ -208,11 +199,11 @@ module UsageDataHelpers
allow(Settings).to receive(:[]).with('lfs')
.and_return(
{ 'enabled' => true,
- 'object_store' =>
+ 'object_store' =>
{ 'enabled' => false,
- 'remote_directory' => 'lfs-objects',
- 'direct_upload' => true,
- 'connection' =>
+ 'remote_directory' => 'lfs-objects',
+ '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 } }
@@ -221,21 +212,21 @@ module UsageDataHelpers
.and_return(
{ 'object_store' =>
{ 'enabled' => false,
- 'remote_directory' => 'uploads',
- 'direct_upload' => true,
- 'connection' =>
+ 'remote_directory' => 'uploads',
+ '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 } }
+ 'background_upload' => false,
+ 'proxy_download' => false } }
)
allow(Settings).to receive(:[]).with('packages')
.and_return(
{ 'enabled' => true,
- 'object_store' =>
+ 'object_store' =>
{ 'enabled' => false,
- 'remote_directory' => 'packages',
- 'direct_upload' => false,
- 'connection' =>
+ 'remote_directory' => 'packages',
+ '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 } }
diff --git a/spec/support/matchers/abort_matcher.rb b/spec/support/matchers/abort_matcher.rb
index 64fed2ca069..140953cdc42 100644
--- a/spec/support/matchers/abort_matcher.rb
+++ b/spec/support/matchers/abort_matcher.rb
@@ -13,17 +13,16 @@ RSpec::Matchers.define :abort_execution do
captured = @captured_stderr.string.chomp
@actual_exit_code = e.status
break false unless e.status == 1
-
- if @message
- if @message.is_a? String
- @message == captured
- elsif @message.is_a? Regexp
- @message.match?(captured)
- else
- raise ArgumentError, 'with_message must be either a String or a Regular Expression'
- end
+ break true unless @message
+
+ case @message
+ when String
+ @message == captured
+ when Regexp
+ @message.match?(captured)
+ else
+ raise ArgumentError, 'with_message must be either a String or a Regular Expression'
end
-
ensure
$stderr = original_stderr
end
diff --git a/spec/support/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb
index e6d820104be..db7d4269945 100644
--- a/spec/support/matchers/graphql_matchers.rb
+++ b/spec/support/matchers/graphql_matchers.rb
@@ -230,3 +230,61 @@ RSpec::Matchers.define :expose_permissions_using do |expected|
expect(permission_field.type.of_type.graphql_name).to eq(expected.graphql_name)
end
end
+
+RSpec::Matchers.define :have_graphql_name do |expected|
+ def graphql_name(object)
+ object.graphql_name if object.respond_to?(:graphql_name)
+ end
+
+ match do |object|
+ name = graphql_name(object)
+
+ begin
+ if expected.present?
+ expect(name).to eq(expected)
+ else
+ expect(expected).to be_present
+ end
+ rescue RSpec::Expectations::ExpectationNotMetError => error
+ @error = error
+ raise
+ end
+ end
+
+ failure_message do |object|
+ if expected.present?
+ @error
+ else
+ 'Expected graphql_name value cannot be blank'
+ end
+ end
+end
+
+RSpec::Matchers.define :have_graphql_description do |expected|
+ def graphql_description(object)
+ object.description if object.respond_to?(:description)
+ end
+
+ match do |object|
+ description = graphql_description(object)
+
+ begin
+ if expected.present?
+ expect(description).to eq(expected)
+ else
+ expect(description).to be_present
+ end
+ rescue RSpec::Expectations::ExpectationNotMetError => error
+ @error = error
+ raise
+ end
+ end
+
+ failure_message do |object|
+ if expected.present?
+ @error
+ else
+ "have_graphql_description expected value cannot be blank"
+ end
+ end
+end
diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb
index 8bec3be2535..a80c269f915 100644
--- a/spec/support/matchers/markdown_matchers.rb
+++ b/spec/support/matchers/markdown_matchers.rb
@@ -134,7 +134,7 @@ module MarkdownMatchers
set_default_markdown_messages
match do |actual|
- expect(actual).to have_selector('a.gfm.gfm-snippet', count: 5)
+ expect(actual).to have_selector('a.gfm.gfm-snippet', count: 9)
end
end
@@ -196,6 +196,16 @@ module MarkdownMatchers
end
end
+ # MathFilter
+ matcher :parse_math do
+ set_default_markdown_messages
+
+ match do |actual|
+ expect(actual).to have_selector('[data-math-style="inline"]', count: 4)
+ expect(actual).to have_selector('[data-math-style="display"]', count: 4)
+ end
+ end
+
# InlineDiffFilter
matcher :parse_inline_diffs do
set_default_markdown_messages
diff --git a/spec/support/migrations_helpers/vulnerabilities_findings_helper.rb b/spec/support/migrations_helpers/vulnerabilities_findings_helper.rb
index 922f49ba84a..a3cccc3a75d 100644
--- a/spec/support/migrations_helpers/vulnerabilities_findings_helper.rb
+++ b/spec/support/migrations_helpers/vulnerabilities_findings_helper.rb
@@ -31,14 +31,14 @@ module MigrationHelpers
"links" => [
{
"name" => "Cipher does not check for integrity first?",
- "url" => "https://crypto.stackexchange.com/questions/31428/pbewithmd5anddes-cipher-does-not-check-for-integrity-first"
+ "url" => "https://crypto.stackexchange.com/questions/31428/pbewithmd5anddes-cipher-does-not-check-for-integrity-first"
}
],
"assets" => [
{
"type" => "postman",
"name" => "Test Postman Collection",
- "url" => "http://localhost/test.collection"
+ "url" => "http://localhost/test.collection"
}
],
"evidence" => {
@@ -50,7 +50,7 @@ module MigrationHelpers
"headers" => [
{
"name" => "Accept",
- "value" => "*/*"
+ "value" => "*/*"
}
]
},
@@ -61,7 +61,7 @@ module MigrationHelpers
"headers" => [
{
"name" => "Content-Length",
- "value" => "0"
+ "value" => "0"
}
]
},
diff --git a/spec/support/redis.rb b/spec/support/redis.rb
index 421079af8e0..d00d6562966 100644
--- a/spec/support/redis.rb
+++ b/spec/support/redis.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+require 'gitlab/redis'
RSpec.configure do |config|
config.after(:each, :redis) do
@@ -7,51 +8,15 @@ RSpec.configure do |config|
end
end
- config.around(:each, :clean_gitlab_redis_cache) do |example|
- redis_cache_cleanup!
+ Gitlab::Redis::ALL_CLASSES.each do |instance_class|
+ underscored_name = instance_class.store_name.underscore
- example.run
+ config.around(:each, :"clean_gitlab_redis_#{underscored_name}") do |example|
+ public_send("redis_#{underscored_name}_cleanup!")
- redis_cache_cleanup!
- end
-
- config.around(:each, :clean_gitlab_redis_shared_state) do |example|
- redis_shared_state_cleanup!
-
- example.run
-
- redis_shared_state_cleanup!
- end
-
- config.around(:each, :clean_gitlab_redis_queues) do |example|
- redis_queues_cleanup!
-
- example.run
-
- redis_queues_cleanup!
- end
-
- config.around(:each, :clean_gitlab_redis_trace_chunks) do |example|
- redis_trace_chunks_cleanup!
+ example.run
- example.run
-
- redis_trace_chunks_cleanup!
- end
-
- config.around(:each, :clean_gitlab_redis_rate_limiting) do |example|
- redis_rate_limiting_cleanup!
-
- example.run
-
- redis_rate_limiting_cleanup!
- end
-
- config.around(:each, :clean_gitlab_redis_sessions) do |example|
- redis_sessions_cleanup!
-
- example.run
-
- redis_sessions_cleanup!
+ public_send("redis_#{underscored_name}_cleanup!")
+ end
end
end
diff --git a/spec/support/redis/redis_helpers.rb b/spec/support/redis/redis_helpers.rb
index 90c15dea1f8..34ac69236ee 100644
--- a/spec/support/redis/redis_helpers.rb
+++ b/spec/support/redis/redis_helpers.rb
@@ -1,36 +1,10 @@
# frozen_string_literal: true
module RedisHelpers
- # config/README.md
-
- # Usage: performance enhancement
- def redis_cache_cleanup!
- Gitlab::Redis::Cache.with(&:flushdb)
- end
-
- # Usage: SideKiq, Mailroom, CI Runner, Workhorse, push services
- def redis_queues_cleanup!
- Gitlab::Redis::Queues.with(&:flushdb)
- end
-
- # Usage: session state, rate limiting
- def redis_shared_state_cleanup!
- Gitlab::Redis::SharedState.with(&:flushdb)
- end
-
- # Usage: CI trace chunks
- def redis_trace_chunks_cleanup!
- Gitlab::Redis::TraceChunks.with(&:flushdb)
- end
-
- # Usage: rate limiting state (for Rack::Attack)
- def redis_rate_limiting_cleanup!
- Gitlab::Redis::RateLimiting.with(&:flushdb)
- end
-
- # Usage: session state
- def redis_sessions_cleanup!
- Gitlab::Redis::Sessions.with(&:flushdb)
+ Gitlab::Redis::ALL_CLASSES.each do |instance_class|
+ define_method("redis_#{instance_class.store_name.underscore}_cleanup!") do
+ instance_class.with(&:flushdb)
+ end
end
# Usage: reset cached instance config
diff --git a/spec/support/redis/redis_shared_examples.rb b/spec/support/redis/redis_shared_examples.rb
index d4c8682ec71..33945509675 100644
--- a/spec/support/redis/redis_shared_examples.rb
+++ b/spec/support/redis/redis_shared_examples.rb
@@ -3,19 +3,19 @@
RSpec.shared_examples "redis_shared_examples" do
include StubENV
- let(:test_redis_url) { "redis://redishost:#{redis_port}"}
+ let(:test_redis_url) { "redis://redishost:#{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" }
- let(:old_socket_path) {"/path/to/old/redis.sock" }
- let(:new_socket_path) {"/path/to/redis.sock" }
+ let(:old_socket_path) { "/path/to/old/redis.sock" }
+ 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(:redis_port) { 6379 }
let(:redis_database) { 99 }
let(:sentinel_port) { 26379 }
- let(:config_with_environment_variable_inside) { "spec/fixtures/config/redis_config_with_env.yml"}
- let(:config_env_variable_url) {"TEST_GITLAB_REDIS_URL"}
+ let(:config_with_environment_variable_inside) { "spec/fixtures/config/redis_config_with_env.yml" }
+ let(:config_env_variable_url) { "TEST_GITLAB_REDIS_URL" }
let(:rails_root) { Dir.mktmpdir('redis_shared_examples') }
before do
diff --git a/spec/support/rspec.rb b/spec/support/rspec.rb
index 30e48b3baf1..6795d2f6d2a 100644
--- a/spec/support/rspec.rb
+++ b/spec/support/rspec.rb
@@ -1,22 +1,26 @@
# frozen_string_literal: true
+require_relative "rspec_order"
+require_relative "system_exit_detected"
require_relative "helpers/stub_configuration"
require_relative "helpers/stub_metrics"
require_relative "helpers/stub_object_storage"
require_relative "helpers/stub_env"
require_relative "helpers/fast_rails_root"
-# so we need to load rubocop here due to the rubocop support file loading cop_helper
-# which monkey patches class Cop
-# if cop helper is loaded before rubocop (where class Cop is defined as class Cop < Base)
-# we get a `superclass mismatch for class Cop` error when running a rspec for a locally defined
-# rubocop cop - therefore we need rubocop required first since it had an inheritance added to the Cop class
-require 'rubocop'
-require 'rubocop/rspec/support'
-
RSpec::Expectations.configuration.on_potential_false_positives = :raise
RSpec.configure do |config|
+ # Re-run failures locally with `--only-failures`
+ config.example_status_persistence_file_path = ENV.fetch('RSPEC_LAST_RUN_RESULTS_FILE', './spec/examples.txt')
+
+ unless ENV['CI']
+ # Allow running `:focus` examples locally,
+ # falling back to all tests when there is no `:focus` example.
+ config.filter_run focus: true
+ config.run_all_when_everything_filtered = true
+ end
+
config.mock_with :rspec do |mocks|
mocks.verify_doubled_constant_names = true
end
@@ -28,10 +32,4 @@ RSpec.configure do |config|
config.include StubObjectStorage
config.include StubENV
config.include FastRailsRoot
-
- config.include RuboCop::RSpec::ExpectOffense, type: :rubocop
-
- config.define_derived_metadata(file_path: %r{spec/rubocop}) do |metadata|
- metadata[:type] = :rubocop
- end
end
diff --git a/spec/support/rspec_order.rb b/spec/support/rspec_order.rb
new file mode 100644
index 00000000000..c128e18b38e
--- /dev/null
+++ b/spec/support/rspec_order.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+
+module Support
+ module RspecOrder
+ TODO_YAML = File.join(__dir__, 'rspec_order_todo.yml')
+
+ module_function
+
+ def order_for(example_group)
+ order_from_env || random_order(example_group)
+ end
+
+ def order_from_env
+ return @order_from_env if defined?(@order_from_env)
+
+ # Passing custom defined order via `--order NAME` is not supported.
+ # For example, `--order reverse` does not work so we are passing it via
+ # environment variable RSPEC_ORDER.
+ @order_from_env = ENV['RSPEC_ORDER']
+ end
+
+ def random_order(example_group)
+ path = example_group.metadata.fetch(:file_path)
+
+ :random unless potential_order_dependent?(path)
+ end
+
+ def potential_order_dependent?(path)
+ @todo ||= YAML.load_file(TODO_YAML).to_set # rubocop:disable Gitlab/PredicateMemoization
+
+ @todo.include?(path)
+ end
+
+ # Adds '# order <ORDER>` below the example group description if the order
+ # has been set to help debugging in case of failure.
+ #
+ # Previously, we've modified metadata[:description] directly but that led
+ # to bugs. See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96137
+ module DocumentationFormatterPatch
+ # See https://github.com/rspec/rspec-core/blob/v3.11.0/lib/rspec/core/formatters/documentation_formatter.rb#L24-L29
+ def example_group_started(notification)
+ super
+
+ order = notification.group.metadata[:order]
+ return unless order
+
+ output.puts "#{current_indentation}# order #{order}"
+ end
+ end
+ end
+end
+
+RSpec::Core::Formatters::DocumentationFormatter.prepend Support::RspecOrder::DocumentationFormatterPatch
+
+RSpec.configure do |config|
+ # Useful to find order-dependent specs.
+ config.register_ordering(:reverse, &:reverse)
+
+ # Randomization can be reproduced across test runs.
+ Kernel.srand config.seed
+
+ config.on_example_group_definition do |example_group|
+ order = Support::RspecOrder.order_for(example_group)
+
+ example_group.metadata[:order] = order.to_sym if order
+ end
+end
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
new file mode 100644
index 00000000000..b5e3d707d50
--- /dev/null
+++ b/spec/support/rspec_order_todo.yml
@@ -0,0 +1,11150 @@
+# The following specs are excluded from running in random order.
+# They are run in defined order.
+#
+# See https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#test-order.
+#
+---
+- './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'
+- './ee/spec/config/metrics/every_metric_definition_spec.rb'
+- './ee/spec/controllers/admin/applications_controller_spec.rb'
+- './ee/spec/controllers/admin/application_settings_controller_spec.rb'
+- './ee/spec/controllers/admin/audit_log_reports_controller_spec.rb'
+- './ee/spec/controllers/admin/audit_logs_controller_spec.rb'
+- './ee/spec/controllers/admin/clusters_controller_spec.rb'
+- './ee/spec/controllers/admin/dashboard_controller_spec.rb'
+- './ee/spec/controllers/admin/dev_ops_report_controller_spec.rb'
+- './ee/spec/controllers/admin/elasticsearch_controller_spec.rb'
+- './ee/spec/controllers/admin/emails_controller_spec.rb'
+- './ee/spec/controllers/admin/geo/nodes_controller_spec.rb'
+- './ee/spec/controllers/admin/geo/projects_controller_spec.rb'
+- './ee/spec/controllers/admin/geo/settings_controller_spec.rb'
+- './ee/spec/controllers/admin/groups_controller_spec.rb'
+- './ee/spec/controllers/admin/impersonations_controller_spec.rb'
+- './ee/spec/controllers/admin/licenses_controller_spec.rb'
+- './ee/spec/controllers/admin/licenses/usage_exports_controller_spec.rb'
+- './ee/spec/controllers/admin/projects_controller_spec.rb'
+- './ee/spec/controllers/admin/push_rules_controller_spec.rb'
+- './ee/spec/controllers/admin/runners_controller_spec.rb'
+- './ee/spec/controllers/admin/users_controller_spec.rb'
+- './ee/spec/controllers/autocomplete_controller_spec.rb'
+- './ee/spec/controllers/boards/issues_controller_spec.rb'
+- './ee/spec/controllers/boards/lists_controller_spec.rb'
+- './ee/spec/controllers/boards/milestones_controller_spec.rb'
+- './ee/spec/controllers/boards/users_controller_spec.rb'
+- './ee/spec/controllers/concerns/boards_responses_spec.rb'
+- './ee/spec/controllers/concerns/ee/routable_actions/sso_enforcement_redirect_spec.rb'
+- './ee/spec/controllers/concerns/geo_instrumentation_spec.rb'
+- './ee/spec/controllers/concerns/gitlab_subscriptions/seat_count_alert_spec.rb'
+- './ee/spec/controllers/concerns/internal_redirect_spec.rb'
+- './ee/spec/controllers/concerns/registrations/verification_spec.rb'
+- './ee/spec/controllers/concerns/routable_actions_spec.rb'
+- './ee/spec/controllers/countries_controller_spec.rb'
+- './ee/spec/controllers/country_states_controller_spec.rb'
+- './ee/spec/controllers/dashboard_controller_spec.rb'
+- './ee/spec/controllers/ee/admin/sessions_controller_spec.rb'
+- './ee/spec/controllers/ee/dashboard/projects_controller_spec.rb'
+- './ee/spec/controllers/ee/groups_controller_spec.rb'
+- './ee/spec/controllers/ee/groups/variables_controller_spec.rb'
+- './ee/spec/controllers/ee/omniauth_callbacks_controller_spec.rb'
+- './ee/spec/controllers/ee/profiles/preferences_controller_spec.rb'
+- './ee/spec/controllers/ee/projects/autocomplete_sources_controller_spec.rb'
+- './ee/spec/controllers/ee/projects/blob_controller_spec.rb'
+- './ee/spec/controllers/ee/projects/jobs_controller_spec.rb'
+- './ee/spec/controllers/ee/projects/merge_requests/content_controller_spec.rb'
+- './ee/spec/controllers/ee/projects/protected_branches_controller_spec.rb'
+- './ee/spec/controllers/ee/projects/variables_controller_spec.rb'
+- './ee/spec/controllers/ee/registrations_controller_spec.rb'
+- './ee/spec/controllers/ee/root_controller_spec.rb'
+- './ee/spec/controllers/ee/search_controller_spec.rb'
+- './ee/spec/controllers/ee/sent_notifications_controller_spec.rb'
+- './ee/spec/controllers/ee/sessions_controller_spec.rb'
+- './ee/spec/controllers/ee/uploads_controller_spec.rb'
+- './ee/spec/controllers/groups/analytics/ci_cd_analytics_controller_spec.rb'
+- './ee/spec/controllers/groups/analytics/coverage_reports_controller_spec.rb'
+- './ee/spec/controllers/groups/analytics/cycle_analytics_controller_spec.rb'
+- './ee/spec/controllers/groups/analytics/cycle_analytics/stages_controller_spec.rb'
+- './ee/spec/controllers/groups/analytics/cycle_analytics/summary_controller_spec.rb'
+- './ee/spec/controllers/groups/analytics/cycle_analytics/value_streams_controller_spec.rb'
+- './ee/spec/controllers/groups/analytics/productivity_analytics_controller_spec.rb'
+- './ee/spec/controllers/groups/analytics/repository_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/billings_controller_spec.rb'
+- './ee/spec/controllers/groups/boards_controller_spec.rb'
+- './ee/spec/controllers/groups/clusters_controller_spec.rb'
+- './ee/spec/controllers/groups/contribution_analytics_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/epic_issues_controller_spec.rb'
+- './ee/spec/controllers/groups/epics_controller_spec.rb'
+- './ee/spec/controllers/groups/epics/notes_controller_spec.rb'
+- './ee/spec/controllers/groups/group_members_controller_spec.rb'
+- './ee/spec/controllers/groups/groups_controller_spec.rb'
+- './ee/spec/controllers/groups/hooks_controller_spec.rb'
+- './ee/spec/controllers/groups/insights_controller_spec.rb'
+- './ee/spec/controllers/groups/issues_analytics_controller_spec.rb'
+- './ee/spec/controllers/groups/issues_controller_spec.rb'
+- './ee/spec/controllers/groups/iteration_cadences_controller_spec.rb'
+- './ee/spec/controllers/groups/iterations_controller_spec.rb'
+- './ee/spec/controllers/groups/ldaps_controller_spec.rb'
+- './ee/spec/controllers/groups/ldap_settings_controller_spec.rb'
+- './ee/spec/controllers/groups/merge_requests_controller_spec.rb'
+- './ee/spec/controllers/groups/omniauth_callbacks_controller_spec.rb'
+- './ee/spec/controllers/groups/push_rules_controller_spec.rb'
+- './ee/spec/controllers/groups/roadmap_controller_spec.rb'
+- './ee/spec/controllers/groups/runners_controller_spec.rb'
+- './ee/spec/controllers/groups/saml_group_links_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/seat_usage_controller_spec.rb'
+- './ee/spec/controllers/groups/security/compliance_dashboards_controller_spec.rb'
+- './ee/spec/controllers/groups/security/dashboard_controller_spec.rb'
+- './ee/spec/controllers/groups/security/merge_commit_reports_controller_spec.rb'
+- './ee/spec/controllers/groups/security/policies_controller_spec.rb'
+- './ee/spec/controllers/groups/security/vulnerabilities_controller_spec.rb'
+- './ee/spec/controllers/groups/sso_controller_spec.rb'
+- './ee/spec/controllers/groups/todos_controller_spec.rb'
+- './ee/spec/controllers/groups/usage_quotas_controller_spec.rb'
+- './ee/spec/controllers/groups/wikis_controller_spec.rb'
+- './ee/spec/controllers/ldap/omniauth_callbacks_controller_spec.rb'
+- './ee/spec/controllers/oauth/applications_controller_spec.rb'
+- './ee/spec/controllers/oauth/geo_auth_controller_spec.rb'
+- './ee/spec/controllers/operations_controller_spec.rb'
+- './ee/spec/controllers/passwords_controller_spec.rb'
+- './ee/spec/controllers/profiles/billings_controller_spec.rb'
+- './ee/spec/controllers/profiles_controller_spec.rb'
+- './ee/spec/controllers/profiles/keys_controller_spec.rb'
+- './ee/spec/controllers/profiles/slacks_controller_spec.rb'
+- './ee/spec/controllers/profiles/usage_quotas_controller_spec.rb'
+- './ee/spec/controllers/projects/analytics/cycle_analytics/summary_controller_spec.rb'
+- './ee/spec/controllers/projects/analytics/issues_analytics_controller_spec.rb'
+- './ee/spec/controllers/projects/analytics/merge_request_analytics_controller_spec.rb'
+- './ee/spec/controllers/projects/approver_groups_controller_spec.rb'
+- './ee/spec/controllers/projects/approvers_controller_spec.rb'
+- './ee/spec/controllers/projects/audit_events_controller_spec.rb'
+- './ee/spec/controllers/projects/boards_controller_spec.rb'
+- './ee/spec/controllers/projects/branches_controller_spec.rb'
+- './ee/spec/controllers/projects/clusters_controller_spec.rb'
+- './ee/spec/controllers/projects_controller_spec.rb'
+- './ee/spec/controllers/projects/dependencies_controller_spec.rb'
+- './ee/spec/controllers/projects/deploy_keys_controller_spec.rb'
+- './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'
+- './ee/spec/controllers/projects/issue_links_controller_spec.rb'
+- './ee/spec/controllers/projects/issues_controller_spec.rb'
+- './ee/spec/controllers/projects/iteration_cadences_controller_spec.rb'
+- './ee/spec/controllers/projects/iterations_controller_spec.rb'
+- './ee/spec/controllers/projects/legacy_pipelines_controller_spec.rb'
+- './ee/spec/controllers/projects/licenses_controller_spec.rb'
+- './ee/spec/controllers/projects/merge_requests_controller_spec.rb'
+- './ee/spec/controllers/projects/merge_requests/creations_controller_spec.rb'
+- './ee/spec/controllers/projects/mirrors_controller_spec.rb'
+- './ee/spec/controllers/projects/pages_controller_spec.rb'
+- './ee/spec/controllers/projects/path_locks_controller_spec.rb'
+- './ee/spec/controllers/projects/pipelines_controller_spec.rb'
+- './ee/spec/controllers/projects/protected_environments_controller_spec.rb'
+- './ee/spec/controllers/projects/push_rules_controller_spec.rb'
+- './ee/spec/controllers/projects/quality/test_cases_controller_spec.rb'
+- './ee/spec/controllers/projects/repositories_controller_spec.rb'
+- './ee/spec/controllers/projects/requirements_management/requirements_controller_spec.rb'
+- './ee/spec/controllers/projects/runners_controller_spec.rb'
+- './ee/spec/controllers/projects/security/api_fuzzing_configuration_controller_spec.rb'
+- './ee/spec/controllers/projects/security/configuration_controller_spec.rb'
+- './ee/spec/controllers/projects/security/dashboard_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/security/vulnerabilities_controller_spec.rb'
+- './ee/spec/controllers/projects/security/vulnerabilities/notes_controller_spec.rb'
+- './ee/spec/controllers/projects/security/vulnerability_report_controller_spec.rb'
+- './ee/spec/controllers/projects/settings/ci_cd_controller_spec.rb'
+- './ee/spec/controllers/projects/settings/integrations_controller_spec.rb'
+- './ee/spec/controllers/projects/settings/operations_controller_spec.rb'
+- './ee/spec/controllers/projects/settings/repository_controller_spec.rb'
+- './ee/spec/controllers/projects/settings/slacks_controller_spec.rb'
+- './ee/spec/controllers/projects/subscriptions_controller_spec.rb'
+- './ee/spec/controllers/projects/vulnerability_feedback_controller_spec.rb'
+- './ee/spec/controllers/registrations/company_controller_spec.rb'
+- './ee/spec/controllers/registrations/groups_controller_spec.rb'
+- './ee/spec/controllers/registrations/groups_projects_controller_spec.rb'
+- './ee/spec/controllers/registrations/projects_controller_spec.rb'
+- './ee/spec/controllers/registrations/verification_controller_spec.rb'
+- './ee/spec/controllers/registrations/welcome_controller_spec.rb'
+- './ee/spec/controllers/repositories/git_http_controller_spec.rb'
+- './ee/spec/controllers/security/dashboard_controller_spec.rb'
+- './ee/spec/controllers/security/projects_controller_spec.rb'
+- './ee/spec/controllers/security/vulnerabilities_controller_spec.rb'
+- './ee/spec/controllers/sitemap_controller_spec.rb'
+- './ee/spec/controllers/subscriptions_controller_spec.rb'
+- './ee/spec/controllers/subscriptions/groups_controller_spec.rb'
+- './ee/spec/controllers/trial_registrations_controller_spec.rb'
+- './ee/spec/controllers/trials_controller_spec.rb'
+- './ee/spec/controllers/users_controller_spec.rb'
+- './ee/spec/db/production/license_spec.rb'
+- './ee/spec/elastic_integration/global_search_spec.rb'
+- './ee/spec/elastic_integration/repository_index_spec.rb'
+- './ee/spec/elastic/migrate/20201105181100_apply_max_analyzed_offset_spec.rb'
+- './ee/spec/elastic/migrate/20201116142400_add_new_data_to_issues_documents_spec.rb'
+- './ee/spec/elastic/migrate/20201123123400_migrate_issues_to_separate_index_spec.rb'
+- './ee/spec/elastic/migrate/20210112165500_delete_issues_from_original_index_spec.rb'
+- './ee/spec/elastic/migrate/20210127154600_remove_permissions_data_from_notes_documents_spec.rb'
+- './ee/spec/elastic/migrate/20210128163600_add_permissions_data_to_notes_documents_spec.rb'
+- './ee/spec/elastic/migrate/20210201104800_migrate_notes_to_separate_index_spec.rb'
+- './ee/spec/elastic/migrate/20210421140400_add_new_data_to_merge_requests_documents_spec.rb'
+- './ee/spec/elastic/migrate/20210429154500_migrate_merge_requests_to_separate_index_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/elastic/migrate/20210623081800_add_upvotes_to_issues_spec.rb'
+- './ee/spec/elastic/migrate/20210722112500_add_upvotes_mappings_to_merge_requests_spec.rb'
+- './ee/spec/elastic/migrate/20210813134600_add_namespace_ancestry_to_issues_mapping_spec.rb'
+- './ee/spec/elastic/migrate/20210825110300_backfill_namespace_ancestry_for_issues_spec.rb'
+- './ee/spec/elastic/migrate/20210910094600_add_namespace_ancestry_ids_to_issues_mapping_spec.rb'
+- './ee/spec/elastic/migrate/20210910100000_redo_backfill_namespace_ancestry_ids_for_issues_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/20220512150000_pause_indexing_for_unsupported_es_versions_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/factories/lfs_object_spec.rb'
+- './ee/spec/features/account_recovery_regular_check_spec.rb'
+- './ee/spec/features/admin/admin_credentials_inventory_spec.rb'
+- './ee/spec/features/admin/admin_dashboard_spec.rb'
+- './ee/spec/features/admin/admin_dev_ops_reports_spec.rb'
+- './ee/spec/features/admin/admin_emails_spec.rb'
+- './ee/spec/features/admin/admin_groups_spec.rb'
+- './ee/spec/features/admin/admin_interacts_with_push_rules_spec.rb'
+- './ee/spec/features/admin/admin_merge_requests_approvals_spec.rb'
+- './ee/spec/features/admin/admin_reset_pipeline_minutes_spec.rb'
+- './ee/spec/features/admin/admin_runners_spec.rb'
+- './ee/spec/features/admin/admin_sends_notification_spec.rb'
+- './ee/spec/features/admin/admin_settings_spec.rb'
+- './ee/spec/features/admin/admin_show_new_user_signups_cap_alert_spec.rb'
+- './ee/spec/features/admin/admin_users_spec.rb'
+- './ee/spec/features/admin/geo/admin_geo_nodes_spec.rb'
+- './ee/spec/features/admin/geo/admin_geo_projects_spec.rb'
+- './ee/spec/features/admin/geo/admin_geo_replication_nav_spec.rb'
+- './ee/spec/features/admin/geo/admin_geo_sidebar_spec.rb'
+- './ee/spec/features/admin/groups/admin_changes_plan_spec.rb'
+- './ee/spec/features/admin/groups/admin_subscription_alerts_spec.rb'
+- './ee/spec/features/admin/licenses/admin_adds_license_spec.rb'
+- './ee/spec/features/admin/licenses/show_user_count_threshold_spec.rb'
+- './ee/spec/features/admin/subscriptions/admin_views_subscription_spec.rb'
+- './ee/spec/features/admin/users/users_spec.rb'
+- './ee/spec/features/analytics/code_analytics_spec.rb'
+- './ee/spec/features/analytics/group_analytics_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/boards/board_filters_spec.rb'
+- './ee/spec/features/boards/boards_licensed_features_spec.rb'
+- './ee/spec/features/boards/boards_spec.rb'
+- './ee/spec/features/boards/group_boards/board_deletion_spec.rb'
+- './ee/spec/features/boards/group_boards/multiple_boards_spec.rb'
+- './ee/spec/features/boards/new_issue_spec.rb'
+- './ee/spec/features/boards/scoped_issue_board_spec.rb'
+- './ee/spec/features/boards/sidebar_spec.rb'
+- './ee/spec/features/boards/swimlanes/epics_swimlanes_drag_drop_spec.rb'
+- './ee/spec/features/boards/swimlanes/epics_swimlanes_filtering_spec.rb'
+- './ee/spec/features/boards/swimlanes/epics_swimlanes_sidebar_labels_spec.rb'
+- './ee/spec/features/boards/swimlanes/epics_swimlanes_sidebar_spec.rb'
+- './ee/spec/features/boards/swimlanes/epics_swimlanes_spec.rb'
+- './ee/spec/features/boards/user_adds_lists_to_board_spec.rb'
+- './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'
+- './ee/spec/features/contextual_sidebar_spec.rb'
+- './ee/spec/features/dashboards/activity_spec.rb'
+- './ee/spec/features/dashboards/groups_spec.rb'
+- './ee/spec/features/dashboards/issues_spec.rb'
+- './ee/spec/features/dashboards/merge_requests_spec.rb'
+- './ee/spec/features/dashboards/operations_spec.rb'
+- './ee/spec/features/dashboards/projects_spec.rb'
+- './ee/spec/features/dashboards/todos_spec.rb'
+- './ee/spec/features/discussion_comments/epic_quick_actions_spec.rb'
+- './ee/spec/features/discussion_comments/epic_spec.rb'
+- './ee/spec/features/epic_boards/epic_boards_sidebar_spec.rb'
+- './ee/spec/features/epic_boards/epic_boards_spec.rb'
+- './ee/spec/features/epic_boards/multiple_epic_boards_spec.rb'
+- './ee/spec/features/epic_boards/new_epic_spec.rb'
+- './ee/spec/features/epics/delete_epic_spec.rb'
+- './ee/spec/features/epics/epic_issues_spec.rb'
+- './ee/spec/features/epics/epic_labels_spec.rb'
+- './ee/spec/features/epics/epic_related_epics_spec.rb'
+- './ee/spec/features/epics/epic_show_spec.rb'
+- './ee/spec/features/epics/epics_list_spec.rb'
+- './ee/spec/features/epics/gfm_autocomplete_spec.rb'
+- './ee/spec/features/epics/issue_promotion_spec.rb'
+- './ee/spec/features/epics/referencing_epics_spec.rb'
+- './ee/spec/features/epics/shortcuts_epic_spec.rb'
+- './ee/spec/features/epics/todo_spec.rb'
+- './ee/spec/features/epics/update_epic_spec.rb'
+- './ee/spec/features/epics/user_uses_quick_actions_spec.rb'
+- './ee/spec/features/geo_node_spec.rb'
+- './ee/spec/features/gitlab_subscriptions/seat_count_alert_spec.rb'
+- './ee/spec/features/google_analytics_datalayer_spec.rb'
+- './ee/spec/features/groups/active_tabs_spec.rb'
+- './ee/spec/features/groups/analytics/ci_cd_analytics_spec.rb'
+- './ee/spec/features/groups/analytics/cycle_analytics/charts_spec.rb'
+- './ee/spec/features/groups/analytics/cycle_analytics/filters_and_data_spec.rb'
+- './ee/spec/features/groups/analytics/cycle_analytics/multiple_value_streams_spec.rb'
+- './ee/spec/features/groups/analytics/productivity_analytics_spec.rb'
+- './ee/spec/features/groups/audit_events_spec.rb'
+- './ee/spec/features/groups/billing_spec.rb'
+- './ee/spec/features/groups/contribution_analytics_spec.rb'
+- './ee/spec/features/groups/feature_discovery_moments_spec.rb'
+- './ee/spec/features/groups/group_overview_spec.rb'
+- './ee/spec/features/groups/group_page_with_external_authorization_service_spec.rb'
+- './ee/spec/features/groups/group_projects_spec.rb'
+- './ee/spec/features/groups/group_roadmap_spec.rb'
+- './ee/spec/features/groups/group_settings_spec.rb'
+- './ee/spec/features/groups/groups_security_credentials_spec.rb'
+- './ee/spec/features/groups/hooks/user_adds_hook_spec.rb'
+- './ee/spec/features/groups/hooks/user_edits_hooks_spec.rb'
+- './ee/spec/features/groups/hooks/user_tests_hooks_spec.rb'
+- './ee/spec/features/groups/hooks/user_views_hooks_spec.rb'
+- './ee/spec/features/groups/insights_spec.rb'
+- './ee/spec/features/groups/issues_spec.rb'
+- './ee/spec/features/groups/iterations/iterations_list_spec.rb'
+- './ee/spec/features/groups/iteration_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'
+- './ee/spec/features/groups/iterations/user_views_iteration_cadence_spec.rb'
+- './ee/spec/features/groups/iterations/user_views_iteration_spec.rb'
+- './ee/spec/features/groups/ldap_group_links_spec.rb'
+- './ee/spec/features/groups/ldap_settings_spec.rb'
+- './ee/spec/features/groups/members/leave_group_spec.rb'
+- './ee/spec/features/groups/members/list_members_spec.rb'
+- './ee/spec/features/groups/members/manage_groups_spec.rb'
+- './ee/spec/features/groups/members/manage_members_spec.rb'
+- './ee/spec/features/groups/members/override_ldap_memberships_spec.rb'
+- './ee/spec/features/groups/navbar_spec.rb'
+- './ee/spec/features/groups/new_spec.rb'
+- './ee/spec/features/groups/push_rules_spec.rb'
+- './ee/spec/features/groups/saml_enforcement_spec.rb'
+- './ee/spec/features/groups/saml_group_links_spec.rb'
+- './ee/spec/features/groups/saml_providers_spec.rb'
+- './ee/spec/features/groups/scim_token_spec.rb'
+- './ee/spec/features/groups/seat_usage/seat_usage_spec.rb'
+- './ee/spec/features/groups/security/compliance_dashboards_spec.rb'
+- './ee/spec/features/groups/settings/ci_cd_spec.rb'
+- './ee/spec/features/groups/settings/protected_environments_spec.rb'
+- './ee/spec/features/groups/settings/reporting_spec.rb'
+- './ee/spec/features/groups/settings/user_configures_insights_spec.rb'
+- './ee/spec/features/groups/settings/user_searches_in_settings_spec.rb'
+- './ee/spec/features/groups_spec.rb'
+- './ee/spec/features/groups/sso_spec.rb'
+- './ee/spec/features/groups/usage_quotas_spec.rb'
+- './ee/spec/features/groups/wikis_spec.rb'
+- './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'
+- './ee/spec/features/issues/epic_in_issue_sidebar_spec.rb'
+- './ee/spec/features/issues/filtered_search/filter_issues_by_iteration_spec.rb'
+- './ee/spec/features/issues/filtered_search/filter_issues_by_multiple_assignees_spec.rb'
+- './ee/spec/features/issues/filtered_search/filter_issues_epic_spec.rb'
+- './ee/spec/features/issues/filtered_search/filter_issues_weight_spec.rb'
+- './ee/spec/features/issues/form_spec.rb'
+- './ee/spec/features/issues/gfm_autocomplete_ee_spec.rb'
+- './ee/spec/features/issues/issue_actions_spec.rb'
+- './ee/spec/features/issues/issue_sidebar_spec.rb'
+- './ee/spec/features/issues/move_issue_resource_weight_events_spec.rb'
+- './ee/spec/features/issues/related_issues_spec.rb'
+- './ee/spec/features/issues/resource_weight_events_spec.rb'
+- './ee/spec/features/issues/sub_nav_ee_spec.rb'
+- './ee/spec/features/issues/user_bulk_edits_issues_spec.rb'
+- './ee/spec/features/issues/user_edits_issue_spec.rb'
+- './ee/spec/features/issues/user_sees_empty_state_spec.rb'
+- './ee/spec/features/issues/user_uses_quick_actions_spec.rb'
+- './ee/spec/features/issues/user_views_issues_spec.rb'
+- './ee/spec/features/labels_hierarchy_spec.rb'
+- './ee/spec/features/markdown/markdown_spec.rb'
+- './ee/spec/features/markdown/metrics_spec.rb'
+- './ee/spec/features/merge_request/merge_request_widget_blocking_mrs_spec.rb'
+- './ee/spec/features/merge_request/sidebar_spec.rb'
+- './ee/spec/features/merge_requests/user_filters_by_approvers_spec.rb'
+- './ee/spec/features/merge_requests/user_resets_approvers_spec.rb'
+- './ee/spec/features/merge_requests/user_views_all_merge_requests_spec.rb'
+- './ee/spec/features/merge_request/user_approves_spec.rb'
+- './ee/spec/features/merge_request/user_approves_with_password_spec.rb'
+- './ee/spec/features/merge_request/user_comments_on_merge_request_spec.rb'
+- './ee/spec/features/merge_request/user_creates_merge_request_spec.rb'
+- './ee/spec/features/merge_request/user_creates_merge_request_with_blocking_mrs_spec.rb'
+- './ee/spec/features/merge_request/user_creates_multiple_assignees_mr_spec.rb'
+- './ee/spec/features/merge_request/user_creates_multiple_reviewers_mr_spec.rb'
+- './ee/spec/features/merge_request/user_edits_approval_rules_mr_spec.rb'
+- './ee/spec/features/merge_request/user_edits_merge_request_blocking_mrs_spec.rb'
+- './ee/spec/features/merge_request/user_edits_multiple_assignees_mr_spec.rb'
+- './ee/spec/features/merge_request/user_edits_multiple_reviewers_mr_spec.rb'
+- './ee/spec/features/merge_request/user_merges_immediately_spec.rb'
+- './ee/spec/features/merge_request/user_merges_with_namespace_storage_limits_spec.rb'
+- './ee/spec/features/merge_request/user_merges_with_push_rules_spec.rb'
+- './ee/spec/features/merge_request/user_sees_approval_widget_spec.rb'
+- './ee/spec/features/merge_request/user_sees_closing_issues_message_spec.rb'
+- './ee/spec/features/merge_request/user_sees_merge_widget_spec.rb'
+- './ee/spec/features/merge_request/user_sees_mr_approvals_promo_spec.rb'
+- './ee/spec/features/merge_request/user_sees_status_checks_widget_spec.rb'
+- './ee/spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb'
+- './ee/spec/features/merge_request/user_sets_approval_rules_spec.rb'
+- './ee/spec/features/merge_request/user_sets_approvers_spec.rb'
+- './ee/spec/features/merge_request/user_uses_slash_commands_spec.rb'
+- './ee/spec/features/merge_request/user_views_blocked_merge_request_spec.rb'
+- './ee/spec/features/merge_trains/two_merge_requests_on_train_spec.rb'
+- './ee/spec/features/merge_trains/user_adds_merge_request_to_merge_train_spec.rb'
+- './ee/spec/features/merge_trains/user_adds_to_merge_train_when_pipeline_succeeds_spec.rb'
+- './ee/spec/features/milestones/user_views_milestone_spec.rb'
+- './ee/spec/features/namespace_user_cap_reached_alert_spec.rb'
+- './ee/spec/features/oncall_schedules/user_creates_schedule_spec.rb'
+- './ee/spec/features/operations_nav_link_spec.rb'
+- './ee/spec/features/password_reset_spec.rb'
+- './ee/spec/features/pending_group_memberships_spec.rb'
+- './ee/spec/features/pending_project_memberships_spec.rb'
+- './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'
+- './ee/spec/features/projects/custom_projects_template_spec.rb'
+- './ee/spec/features/projects/environments/environments_spec.rb'
+- './ee/spec/features/projects/feature_flags/feature_flag_issues_spec.rb'
+- './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/feature_flags/user_sees_feature_flag_list_spec.rb'
+- './ee/spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb'
+- './ee/spec/features/projects/insights_spec.rb'
+- './ee/spec/features/projects/integrations/jira_issues_list_spec.rb'
+- './ee/spec/features/projects/integrations/project_integrations_spec.rb'
+- './ee/spec/features/projects/integrations/prometheus_custom_metrics_spec.rb'
+- './ee/spec/features/projects/integrations/user_activates_github_spec.rb'
+- './ee/spec/features/projects/integrations/user_activates_jira_spec.rb'
+- './ee/spec/features/projects/issues/user_creates_issue_spec.rb'
+- './ee/spec/features/projects/issues/viewing_relocated_issues_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/jobs/blocked_deployment_job_page_spec.rb'
+- './ee/spec/features/projects/jobs_spec.rb'
+- './ee/spec/features/projects/kerberos_clone_instructions_spec.rb'
+- './ee/spec/features/projects/licenses/maintainer_views_policies_spec.rb'
+- './ee/spec/features/projects/members/invite_group_and_members_spec.rb'
+- './ee/spec/features/projects/members/manage_groups_spec.rb'
+- './ee/spec/features/projects/members/member_is_removed_from_project_spec.rb'
+- './ee/spec/features/projects/members/member_leaves_project_spec.rb'
+- './ee/spec/features/projects/merge_requests/user_approves_merge_request_spec.rb'
+- './ee/spec/features/projects/merge_requests/user_edits_merge_request_spec.rb'
+- './ee/spec/features/projects/milestones/milestone_spec.rb'
+- './ee/spec/features/projects/mirror_spec.rb'
+- './ee/spec/features/projects/navbar_spec.rb'
+- './ee/spec/features/projects/new_project_from_template_spec.rb'
+- './ee/spec/features/projects/new_project_spec.rb'
+- './ee/spec/features/projects/path_locks_spec.rb'
+- './ee/spec/features/projects/pipelines/legacy_pipeline_spec.rb'
+- './ee/spec/features/projects/pipelines/pipeline_csp_spec.rb'
+- './ee/spec/features/projects/pipelines/pipeline_spec.rb'
+- './ee/spec/features/projects/pipelines/pipelines_spec.rb'
+- './ee/spec/features/projects/push_rules_spec.rb'
+- './ee/spec/features/projects/quality/test_case_create_spec.rb'
+- './ee/spec/features/projects/quality/test_case_list_spec.rb'
+- './ee/spec/features/projects/quality/test_case_show_spec.rb'
+- './ee/spec/features/projects/releases/user_views_release_spec.rb'
+- './ee/spec/features/projects/requirements_management/requirements_list_spec.rb'
+- './ee/spec/features/projects/security/dast_scanner_profiles_spec.rb'
+- './ee/spec/features/projects/security/dast_site_profiles_spec.rb'
+- './ee/spec/features/projects/security/user_creates_on_demand_scan_spec.rb'
+- './ee/spec/features/projects/security/user_edits_on_demand_scan_spec.rb'
+- './ee/spec/features/projects/security/user_views_security_configuration_spec.rb'
+- './ee/spec/features/projects/settings/auto_rollback_spec.rb'
+- './ee/spec/features/projects/settings/disable_merge_trains_setting_spec.rb'
+- './ee/spec/features/projects/settings/ee/repository_mirrors_settings_spec.rb'
+- './ee/spec/features/projects/settings/ee/service_desk_setting_spec.rb'
+- './ee/spec/features/projects/settings/issues_settings_spec.rb'
+- './ee/spec/features/projects/settings/merge_request_approvals_settings_spec.rb'
+- './ee/spec/features/projects/settings/merge_requests_settings_spec.rb'
+- './ee/spec/features/projects/settings/pipeline_subscriptions_spec.rb'
+- './ee/spec/features/projects/settings/protected_environments_spec.rb'
+- './ee/spec/features/projects/settings/push_rules_settings_spec.rb'
+- './ee/spec/features/projects/settings/slack_application_spec.rb'
+- './ee/spec/features/projects/settings/user_manages_approval_settings_spec.rb'
+- './ee/spec/features/projects/settings/user_manages_issues_template_spec.rb'
+- './ee/spec/features/projects/settings/user_manages_members_spec.rb'
+- './ee/spec/features/projects/settings/user_manages_merge_pipelines_spec.rb'
+- './ee/spec/features/projects/settings/user_manages_merge_requests_template_spec.rb'
+- './ee/spec/features/projects/settings/user_manages_merge_trains_spec.rb'
+- './ee/spec/features/projects/show/developer_views_empty_project_instructions_spec.rb'
+- './ee/spec/features/projects/show_project_spec.rb'
+- './ee/spec/features/projects_spec.rb'
+- './ee/spec/features/projects/user_applies_custom_file_template_spec.rb'
+- './ee/spec/features/projects/view_blob_with_code_owners_spec.rb'
+- './ee/spec/features/projects/wiki/user_views_wiki_empty_spec.rb'
+- './ee/spec/features/promotion_spec.rb'
+- './ee/spec/features/protected_branches_spec.rb'
+- './ee/spec/features/protected_tags_spec.rb'
+- './ee/spec/features/read_only_spec.rb'
+- './ee/spec/features/registrations/combined_registration_spec.rb'
+- './ee/spec/features/registrations/one_trust_spec.rb'
+- './ee/spec/features/registrations/saas_user_registration_spec.rb'
+- './ee/spec/features/registrations/trial_during_signup_flow_spec.rb'
+- './ee/spec/features/registrations/user_sees_new_onboarding_flow_spec.rb'
+- './ee/spec/features/registrations/welcome_spec.rb'
+- './ee/spec/features/search/elastic/global_search_spec.rb'
+- './ee/spec/features/search/elastic/group_search_spec.rb'
+- './ee/spec/features/search/elastic/project_search_spec.rb'
+- './ee/spec/features/search/elastic/snippet_search_spec.rb'
+- './ee/spec/features/search/user_searches_for_epics_spec.rb'
+- './ee/spec/features/security/admin_access_spec.rb'
+- './ee/spec/features/security/dashboard_access_spec.rb'
+- './ee/spec/features/security/group/internal_access_spec.rb'
+- './ee/spec/features/security/group/private_access_spec.rb'
+- './ee/spec/features/security/group/public_access_spec.rb'
+- './ee/spec/features/security/profile_access_spec.rb'
+- './ee/spec/features/security/project/discover_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/security/project/snippet/internal_access_spec.rb'
+- './ee/spec/features/security/project/snippet/private_access_spec.rb'
+- './ee/spec/features/security/project/snippet/public_access_spec.rb'
+- './ee/spec/features/signup_spec.rb'
+- './ee/spec/features/subscriptions/expiring_subscription_message_spec.rb'
+- './ee/spec/features/subscriptions/groups/edit_spec.rb'
+- './ee/spec/features/subscriptions_spec.rb'
+- './ee/spec/features/trial_registrations/company_information_spec.rb'
+- './ee/spec/features/trial_registrations/signin_spec.rb'
+- './ee/spec/features/trial_registrations/signup_spec.rb'
+- './ee/spec/features/trials/capture_lead_spec.rb'
+- './ee/spec/features/trials/select_namespace_spec.rb'
+- './ee/spec/features/trials/show_trial_banner_spec.rb'
+- './ee/spec/features/users/arkose_labs_csp_spec.rb'
+- './ee/spec/features/users/login_spec.rb'
+- './ee/spec/features/users/signup_spec.rb'
+- './ee/spec/features/user_unsubscribes_from_admin_notifications_spec.rb'
+- './ee/spec/finders/analytics/cycle_analytics/stage_finder_spec.rb'
+- './ee/spec/finders/analytics/devops_adoption/enabled_namespaces_finder_spec.rb'
+- './ee/spec/finders/analytics/devops_adoption/snapshots_finder_spec.rb'
+- './ee/spec/finders/approval_rules/group_finder_spec.rb'
+- './ee/spec/finders/app_sec/fuzzing/coverage/corpuses_finder_spec.rb'
+- './ee/spec/finders/audit_event_finder_spec.rb'
+- './ee/spec/finders/auth/group_saml_identity_finder_spec.rb'
+- './ee/spec/finders/auth/provisioned_users_finder_spec.rb'
+- './ee/spec/finders/autocomplete/group_subgroups_finder_spec.rb'
+- './ee/spec/finders/autocomplete/project_invited_groups_finder_spec.rb'
+- './ee/spec/finders/autocomplete/vulnerabilities_autocomplete_finder_spec.rb'
+- './ee/spec/finders/billed_users_finder_spec.rb'
+- './ee/spec/finders/boards/boards_finder_spec.rb'
+- './ee/spec/finders/boards/epic_boards_finder_spec.rb'
+- './ee/spec/finders/boards/milestones_finder_spec.rb'
+- './ee/spec/finders/boards/users_finder_spec.rb'
+- './ee/spec/finders/clusters/environments_finder_spec.rb'
+- './ee/spec/finders/compliance_management/merge_requests/compliance_violations_finder_spec.rb'
+- './ee/spec/finders/custom_project_templates_finder_spec.rb'
+- './ee/spec/finders/dast/profiles_finder_spec.rb'
+- './ee/spec/finders/dast_scanner_profiles_finder_spec.rb'
+- './ee/spec/finders/dast_site_profiles_finder_spec.rb'
+- './ee/spec/finders/dast_site_validations_finder_spec.rb'
+- './ee/spec/finders/ee/alert_management/http_integrations_finder_spec.rb'
+- './ee/spec/finders/ee/autocomplete/users_finder_spec.rb'
+- './ee/spec/finders/ee/ci/daily_build_group_report_results_finder_spec.rb'
+- './ee/spec/finders/ee/clusters/agent_authorizations_finder_spec.rb'
+- './ee/spec/finders/ee/clusters/agents_finder_spec.rb'
+- './ee/spec/finders/ee/fork_targets_finder_spec.rb'
+- './ee/spec/finders/ee/group_members_finder_spec.rb'
+- './ee/spec/finders/ee/namespaces/projects_finder_spec.rb'
+- './ee/spec/finders/ee/projects_finder_spec.rb'
+- './ee/spec/finders/ee/user_recent_events_finder_spec.rb'
+- './ee/spec/finders/epics_finder_spec.rb'
+- './ee/spec/finders/geo/ci_secure_file_registry_finder_spec.rb'
+- './ee/spec/finders/geo/container_repository_registry_finder_spec.rb'
+- './ee/spec/finders/geo/design_registry_finder_spec.rb'
+- './ee/spec/finders/geo/group_wiki_repository_registry_finder_spec.rb'
+- './ee/spec/finders/geo/lfs_object_registry_finder_spec.rb'
+- './ee/spec/finders/geo/merge_request_diff_registry_finder_spec.rb'
+- './ee/spec/finders/geo_node_finder_spec.rb'
+- './ee/spec/finders/geo/package_file_registry_finder_spec.rb'
+- './ee/spec/finders/geo/pages_deployment_registry_finder_spec.rb'
+- './ee/spec/finders/geo/pipeline_artifact_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/geo/repository_verification_finder_spec.rb'
+- './ee/spec/finders/geo/snippet_repository_registry_finder_spec.rb'
+- './ee/spec/finders/geo/terraform_state_version_registry_finder_spec.rb'
+- './ee/spec/finders/geo/upload_registry_finder_spec.rb'
+- './ee/spec/finders/gpg_keys_finder_spec.rb'
+- './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'
+- './ee/spec/finders/licenses_finder_spec.rb'
+- './ee/spec/finders/license_template_finder_spec.rb'
+- './ee/spec/finders/merge_requests/by_approvers_finder_spec.rb'
+- './ee/spec/finders/merge_requests_finder_spec.rb'
+- './ee/spec/finders/merge_trains_finder_spec.rb'
+- './ee/spec/finders/notes_finder_spec.rb'
+- './ee/spec/finders/productivity_analytics_finder_spec.rb'
+- './ee/spec/finders/projects/integrations/jira/by_ids_finder_spec.rb'
+- './ee/spec/finders/projects/integrations/jira/issues_finder_spec.rb'
+- './ee/spec/finders/requirements_management/requirements_finder_spec.rb'
+- './ee/spec/finders/scim_finder_spec.rb'
+- './ee/spec/finders/security/findings_finder_spec.rb'
+- './ee/spec/finders/security/pipeline_vulnerabilities_finder_spec.rb'
+- './ee/spec/finders/security/scan_execution_policies_finder_spec.rb'
+- './ee/spec/finders/security/training_providers/base_url_finder_spec.rb'
+- './ee/spec/finders/security/training_providers/kontra_url_finder_spec.rb'
+- './ee/spec/finders/security/training_providers/secure_code_warrior_url_finder_spec.rb'
+- './ee/spec/finders/security/training_urls_finder_spec.rb'
+- './ee/spec/finders/security/vulnerabilities_finder_spec.rb'
+- './ee/spec/finders/security/vulnerability_feedbacks_finder_spec.rb'
+- './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'
+- './ee/spec/frontend/fixtures/analytics/devops_reports/devops_adoption/enabled_namespaces.rb'
+- './ee/spec/frontend/fixtures/analytics/metrics.rb'
+- './ee/spec/frontend/fixtures/analytics/value_streams_code_stage.rb'
+- './ee/spec/frontend/fixtures/analytics/value_streams_issue_stage.rb'
+- './ee/spec/frontend/fixtures/analytics/value_streams_plan_stage.rb'
+- './ee/spec/frontend/fixtures/analytics/value_streams.rb'
+- './ee/spec/frontend/fixtures/analytics/value_streams_review_stage.rb'
+- './ee/spec/frontend/fixtures/analytics/value_streams_staging_stage.rb'
+- './ee/spec/frontend/fixtures/analytics/value_streams_test_stage.rb'
+- './ee/spec/frontend/fixtures/codequality_report.rb'
+- './ee/spec/frontend/fixtures/dast_profiles.rb'
+- './ee/spec/frontend/fixtures/dora/metrics.rb'
+- './ee/spec/frontend/fixtures/epic.rb'
+- './ee/spec/frontend/fixtures/issues.rb'
+- './ee/spec/frontend/fixtures/merge_requests.rb'
+- './ee/spec/frontend/fixtures/on_demand_dast_scans.rb'
+- './ee/spec/frontend/fixtures/project_quality_summary.rb'
+- './ee/spec/frontend/fixtures/projects.rb'
+- './ee/spec/frontend/fixtures/runner.rb'
+- './ee/spec/frontend/fixtures/saml_providers.rb'
+- './ee/spec/frontend/fixtures/search.rb'
+- './ee/spec/graphql/api/vulnerabilities_spec.rb'
+- './ee/spec/graphql/ee/mutations/boards/issues/issue_move_list_spec.rb'
+- './ee/spec/graphql/ee/mutations/boards/lists/create_spec.rb'
+- './ee/spec/graphql/ee/mutations/ci/project_ci_cd_settings_update_spec.rb'
+- './ee/spec/graphql/ee/mutations/ci/runner/update_spec.rb'
+- './ee/spec/graphql/ee/mutations/concerns/mutations/resolves_issuable_spec.rb'
+- './ee/spec/graphql/ee/resolvers/board_list_issues_resolver_spec.rb'
+- './ee/spec/graphql/ee/resolvers/board_lists_resolver_spec.rb'
+- './ee/spec/graphql/ee/resolvers/issues_resolver_spec.rb'
+- './ee/spec/graphql/ee/resolvers/namespace_projects_resolver_spec.rb'
+- './ee/spec/graphql/ee/types/alert_management/http_integration_type_spec.rb'
+- './ee/spec/graphql/ee/types/board_list_type_spec.rb'
+- './ee/spec/graphql/ee/types/boards/board_issue_input_type_spec.rb'
+- './ee/spec/graphql/ee/types/board_type_spec.rb'
+- './ee/spec/graphql/ee/types/ci/pipeline_merge_request_type_enum_spec.rb'
+- './ee/spec/graphql/ee/types/compliance_management/compliance_framework_type_spec.rb'
+- './ee/spec/graphql/ee/types/group_type_spec.rb'
+- './ee/spec/graphql/ee/types/issuable_type_spec.rb'
+- './ee/spec/graphql/ee/types/issue_sort_enum_spec.rb'
+- './ee/spec/graphql/ee/types/merge_request_type_spec.rb'
+- './ee/spec/graphql/ee/types/milestone_type_spec.rb'
+- './ee/spec/graphql/ee/types/mutation_type_spec.rb'
+- './ee/spec/graphql/ee/types/namespace_type_spec.rb'
+- './ee/spec/graphql/ee/types/notes/noteable_interface_spec.rb'
+- './ee/spec/graphql/ee/types/projects/service_type_enum_spec.rb'
+- './ee/spec/graphql/ee/types/repository/blob_type_spec.rb'
+- './ee/spec/graphql/ee/types/todoable_interface_spec.rb'
+- './ee/spec/graphql/ee/types/user_merge_request_interaction_type_spec.rb'
+- './ee/spec/graphql/mutations/app_sec/fuzzing/api/ci_configuration/create_spec.rb'
+- './ee/spec/graphql/mutations/app_sec/fuzzing/coverage/corpus/create_spec.rb'
+- './ee/spec/graphql/mutations/audit_events/streaming/headers/create_spec.rb'
+- './ee/spec/graphql/mutations/audit_events/streaming/headers/destroy_spec.rb'
+- './ee/spec/graphql/mutations/boards/epic_boards/create_spec.rb'
+- './ee/spec/graphql/mutations/boards/epic_boards/destroy_spec.rb'
+- './ee/spec/graphql/mutations/boards/epic_boards/epic_move_list_spec.rb'
+- './ee/spec/graphql/mutations/boards/epic_boards/update_spec.rb'
+- './ee/spec/graphql/mutations/boards/epic_lists/create_spec.rb'
+- './ee/spec/graphql/mutations/boards/epic_lists/update_spec.rb'
+- './ee/spec/graphql/mutations/boards/epics/create_spec.rb'
+- './ee/spec/graphql/mutations/boards/lists/update_limit_metrics_spec.rb'
+- './ee/spec/graphql/mutations/boards/update_epic_user_preferences_spec.rb'
+- './ee/spec/graphql/mutations/boards/update_spec.rb'
+- './ee/spec/graphql/mutations/compliance_management/frameworks/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_on_demand_scans/create_spec.rb'
+- './ee/spec/graphql/mutations/dast/profiles/create_spec.rb'
+- './ee/spec/graphql/mutations/dast/profiles/delete_spec.rb'
+- './ee/spec/graphql/mutations/dast/profiles/run_spec.rb'
+- './ee/spec/graphql/mutations/dast/profiles/update_spec.rb'
+- './ee/spec/graphql/mutations/dast_scanner_profiles/create_spec.rb'
+- './ee/spec/graphql/mutations/dast_scanner_profiles/delete_spec.rb'
+- './ee/spec/graphql/mutations/dast_scanner_profiles/update_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/dast_site_tokens/create_spec.rb'
+- './ee/spec/graphql/mutations/dast_site_validations/create_spec.rb'
+- './ee/spec/graphql/mutations/dast_site_validations/revoke_spec.rb'
+- './ee/spec/graphql/mutations/epics/add_issue_spec.rb'
+- './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'
+- './ee/spec/graphql/mutations/issues/promote_to_epic_spec.rb'
+- './ee/spec/graphql/mutations/issues/set_assignees_spec.rb'
+- './ee/spec/graphql/mutations/issues/set_epic_spec.rb'
+- './ee/spec/graphql/mutations/issues/set_escalation_policy_spec.rb'
+- './ee/spec/graphql/mutations/issues/set_iteration_spec.rb'
+- './ee/spec/graphql/mutations/issues/set_weight_spec.rb'
+- './ee/spec/graphql/mutations/issues/update_spec.rb'
+- './ee/spec/graphql/mutations/merge_requests/accept_spec.rb'
+- './ee/spec/graphql/mutations/merge_requests/set_assignees_spec.rb'
+- './ee/spec/graphql/mutations/merge_requests/set_reviewers_spec.rb'
+- './ee/spec/graphql/mutations/namespaces/increase_storage_temporarily_spec.rb'
+- './ee/spec/graphql/mutations/projects/set_compliance_framework_spec.rb'
+- './ee/spec/graphql/mutations/projects/set_locked_spec.rb'
+- './ee/spec/graphql/mutations/releases/update_spec.rb'
+- './ee/spec/graphql/mutations/requirements_management/create_requirement_spec.rb'
+- './ee/spec/graphql/mutations/requirements_management/export_requirements_spec.rb'
+- './ee/spec/graphql/mutations/requirements_management/update_requirement_spec.rb'
+- './ee/spec/graphql/mutations/security/ci_configuration/configure_container_scanning_spec.rb'
+- './ee/spec/graphql/mutations/security/ci_configuration/configure_dependency_scanning_spec.rb'
+- './ee/spec/graphql/mutations/security_finding/dismiss_spec.rb'
+- './ee/spec/graphql/mutations/security_policy/assign_security_policy_project_spec.rb'
+- './ee/spec/graphql/mutations/security_policy/commit_scan_execution_policy_spec.rb'
+- './ee/spec/graphql/mutations/security_policy/create_security_policy_project_spec.rb'
+- './ee/spec/graphql/mutations/security_policy/unassign_security_policy_project_spec.rb'
+- './ee/spec/graphql/mutations/security/training_provider_update_spec.rb'
+- './ee/spec/graphql/mutations/todos/create_spec.rb'
+- './ee/spec/graphql/mutations/vulnerabilities/confirm_spec.rb'
+- './ee/spec/graphql/mutations/vulnerabilities/create_external_issue_link_spec.rb'
+- './ee/spec/graphql/mutations/vulnerabilities/create_spec.rb'
+- './ee/spec/graphql/mutations/vulnerabilities/destroy_external_issue_link_spec.rb'
+- './ee/spec/graphql/mutations/vulnerabilities/dismiss_spec.rb'
+- './ee/spec/graphql/mutations/vulnerabilities/resolve_spec.rb'
+- './ee/spec/graphql/mutations/vulnerabilities/revert_to_detected_spec.rb'
+- './ee/spec/graphql/representation/vulnerability_scanner_entry_spec.rb'
+- './ee/spec/graphql/resolvers/admin/cloud_licenses/current_license_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/admin/cloud_licenses/license_history_entries_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/admin/cloud_licenses/subscription_future_entries_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/analytics/devops_adoption/enabled_namespaces_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/app_sec/dast/profile_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/app_sec/fuzzing/coverage/corpuses_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/board_groupings/epics_resolvers_spec.rb'
+- './ee/spec/graphql/resolvers/boards/board_list_epics_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/boards/epic_boards_resolvers_spec.rb'
+- './ee/spec/graphql/resolvers/boards/epic_list_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/boards/epic_lists_resolvers_spec.rb'
+- './ee/spec/graphql/resolvers/ci/code_coverage_activities_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/ci/code_coverage_summary_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/clusters/agents_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/compliance_management/merge_requests/compliance_violation_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/dast_site_profile_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/dast_site_validation_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/dora_metrics_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/epic_ancestors_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/epic_issues_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/epics_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/external_issue_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/geo/ci_secure_file_registries_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/geo/geo_node_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/geo/group_wiki_repository_registries_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/geo/job_artifact_registries_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/geo/lfs_object_registries_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/geo/merge_request_diff_registries_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/geo/package_file_registries_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/geo/pages_deployment_registries_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/geo/pipeline_artifact_registries_resolver_spec.rb'
+- './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'
+- './ee/spec/graphql/resolvers/iterations_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/network_policy_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/path_locks_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/pipeline_security_report_findings_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/requirements_management/requirements_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/requirements_management/test_reports_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/security_orchestration/scan_execution_policy_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/security_orchestration/scan_result_policy_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/security_report_summary_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/security_training_urls_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/timebox_report_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/user_discussions_count_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/user_notes_count_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/vulnerabilities/container_images_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/vulnerabilities_count_per_day_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/vulnerabilities/details_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/vulnerabilities_grade_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/vulnerabilities/issue_links_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/vulnerabilities_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/vulnerabilities/scanners_resolver_spec.rb'
+- './ee/spec/graphql/resolvers/vulnerability_severities_count_resolver_spec.rb'
+- './ee/spec/graphql/types/admin/cloud_licenses/current_license_type_spec.rb'
+- './ee/spec/graphql/types/admin/cloud_licenses/license_history_entry_type_spec.rb'
+- './ee/spec/graphql/types/admin/cloud_licenses/subscription_future_entry_type_spec.rb'
+- './ee/spec/graphql/types/alert_management/payload_alert_field_name_enum_spec.rb'
+- './ee/spec/graphql/types/alert_management/payload_alert_field_path_segment_type_spec.rb'
+- './ee/spec/graphql/types/alert_management/payload_alert_field_type_enum_spec.rb'
+- './ee/spec/graphql/types/approval_rule_type_enum_spec.rb'
+- './ee/spec/graphql/types/approval_rule_type_spec.rb'
+- './ee/spec/graphql/types/app_sec/fuzzing/api/ci_configuration_type_spec.rb'
+- './ee/spec/graphql/types/app_sec/fuzzing/api/scan_mode_enum_spec.rb'
+- './ee/spec/graphql/types/app_sec/fuzzing/api/scan_profile_type_spec.rb'
+- './ee/spec/graphql/types/app_sec/fuzzing/coverage/corpus_type_spec.rb'
+- './ee/spec/graphql/types/asset_type_spec.rb'
+- './ee/spec/graphql/types/audit_events/exterrnal_audit_event_destination_type_spec.rb'
+- './ee/spec/graphql/types/audit_events/streaming/header_type_spec.rb'
+- './ee/spec/graphql/types/boards/board_epic_type_spec.rb'
+- './ee/spec/graphql/types/boards/epic_board_type_spec.rb'
+- './ee/spec/graphql/types/boards/epic_list_metadata_type_spec.rb'
+- './ee/spec/graphql/types/boards/epic_list_type_spec.rb'
+- './ee/spec/graphql/types/boards/epic_user_preferences_type_spec.rb'
+- './ee/spec/graphql/types/burnup_chart_daily_totals_type_spec.rb'
+- './ee/spec/graphql/types/ci/code_coverage_activity_type_spec.rb'
+- './ee/spec/graphql/types/ci/code_coverage_summary_spec.rb'
+- './ee/spec/graphql/types/ci/code_quality_degradation_severity_enum_spec.rb'
+- './ee/spec/graphql/types/ci/code_quality_degradation_type_spec.rb'
+- './ee/spec/graphql/types/ci/minutes/namespace_monthly_usage_type_spec.rb'
+- './ee/spec/graphql/types/ci/minutes/project_monthly_usage_type_spec.rb'
+- './ee/spec/graphql/types/ci/pipeline_type_spec.rb'
+- './ee/spec/graphql/types/ci/runner_type_spec.rb'
+- './ee/spec/graphql/types/compliance_management/merge_requests/compliance_violation_input_type_spec.rb'
+- './ee/spec/graphql/types/compliance_management/merge_requests/compliance_violation_reason_enum_spec.rb'
+- './ee/spec/graphql/types/compliance_management/merge_requests/compliance_violation_severity_enum_spec.rb'
+- './ee/spec/graphql/types/compliance_management/merge_requests/compliance_violation_sort_enum_spec.rb'
+- './ee/spec/graphql/types/compliance_management/merge_requests/compliance_violation_type_spec.rb'
+- './ee/spec/graphql/types/dast/profile_branch_type_spec.rb'
+- './ee/spec/graphql/types/dast/profile_cadence_enum_spec.rb'
+- './ee/spec/graphql/types/dast/profile_cadence_input_type_spec.rb'
+- './ee/spec/graphql/types/dast/profile_cadence_type_spec.rb'
+- './ee/spec/graphql/types/dast/profile_schedule_input_type_spec.rb'
+- './ee/spec/graphql/types/dast/profile_schedule_type_spec.rb'
+- './ee/spec/graphql/types/dast/profile_type_spec.rb'
+- './ee/spec/graphql/types/dast/scan_method_type_enum_spec.rb'
+- './ee/spec/graphql/types/dast_scanner_profile_type_spec.rb'
+- './ee/spec/graphql/types/dast/site_profile_auth_input_type_spec.rb'
+- './ee/spec/graphql/types/dast/site_profile_auth_type_spec.rb'
+- './ee/spec/graphql/types/dast_site_profile_type_spec.rb'
+- './ee/spec/graphql/types/dast_site_validation_type_spec.rb'
+- './ee/spec/graphql/types/dora_metric_bucketing_interval_enum_spec.rb'
+- './ee/spec/graphql/types/dora_metric_type_enum_spec.rb'
+- './ee/spec/graphql/types/dora_metric_type_spec.rb'
+- './ee/spec/graphql/types/dora_type_spec.rb'
+- './ee/spec/graphql/types/epic_descendant_count_type_spec.rb'
+- './ee/spec/graphql/types/epic_descendant_weight_sum_type_spec.rb'
+- './ee/spec/graphql/types/epic_issue_type_spec.rb'
+- './ee/spec/graphql/types/epic_sort_enum_spec.rb'
+- './ee/spec/graphql/types/epic_state_enum_spec.rb'
+- './ee/spec/graphql/types/epic_type_spec.rb'
+- './ee/spec/graphql/types/external_issue_type_spec.rb'
+- './ee/spec/graphql/types/geo/ci_secure_file_registry_type_spec.rb'
+- './ee/spec/graphql/types/geo/geo_node_type_spec.rb'
+- './ee/spec/graphql/types/geo/job_artifact_registry_type_spec.rb'
+- './ee/spec/graphql/types/geo/lfs_object_registry_type_spec.rb'
+- './ee/spec/graphql/types/geo/merge_request_diff_registry_type_spec.rb'
+- './ee/spec/graphql/types/geo/package_file_registry_type_spec.rb'
+- './ee/spec/graphql/types/geo/pages_deployment_registry_type_spec.rb'
+- './ee/spec/graphql/types/geo/pipeline_artifact_registry_type_spec.rb'
+- './ee/spec/graphql/types/geo/registry_state_enum_spec.rb'
+- './ee/spec/graphql/types/geo/terraform_state_version_registry_type_spec.rb'
+- './ee/spec/graphql/types/geo/upload_registry_type_spec.rb'
+- './ee/spec/graphql/types/global_id_type_spec.rb'
+- './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'
+- './ee/spec/graphql/types/iterations/cadence_type_spec.rb'
+- './ee/spec/graphql/types/iteration_type_spec.rb'
+- './ee/spec/graphql/types/json_string_type_spec.rb'
+- './ee/spec/graphql/types/merge_requests/approval_state_type_spec.rb'
+- './ee/spec/graphql/types/metric_image_type_spec.rb'
+- './ee/spec/graphql/types/move_type_enum_spec.rb'
+- './ee/spec/graphql/types/network_policy_kind_enum_spec.rb'
+- './ee/spec/graphql/types/network_policy_type_spec.rb'
+- './ee/spec/graphql/types/path_lock_type_spec.rb'
+- './ee/spec/graphql/types/permission_types/epic_spec.rb'
+- './ee/spec/graphql/types/permission_types/project_spec.rb'
+- './ee/spec/graphql/types/permission_types/vulnerability_spec.rb'
+- './ee/spec/graphql/types/pipeline_security_report_finding_type_spec.rb'
+- './ee/spec/graphql/types/projects/services_enum_spec.rb'
+- './ee/spec/graphql/types/project_type_spec.rb'
+- './ee/spec/graphql/types/push_rules_type_spec.rb'
+- './ee/spec/graphql/types/query_type_spec.rb'
+- './ee/spec/graphql/types/requirements_management/requirement_state_enum_spec.rb'
+- './ee/spec/graphql/types/requirements_management/requirement_states_count_type_spec.rb'
+- './ee/spec/graphql/types/requirements_management/requirement_type_spec.rb'
+- './ee/spec/graphql/types/requirements_management/test_report_state_enum_spec.rb'
+- './ee/spec/graphql/types/requirements_management/test_report_type_spec.rb'
+- './ee/spec/graphql/types/scanned_resource_type_spec.rb'
+- './ee/spec/graphql/types/scan_type_spec.rb'
+- './ee/spec/graphql/types/security_orchestration/group_security_policy_source_type_spec.rb'
+- './ee/spec/graphql/types/security_orchestration/project_security_policy_source_type_spec.rb'
+- './ee/spec/graphql/types/security_orchestration/security_policy_relation_type_enum_spec.rb'
+- './ee/spec/graphql/types/security_orchestration/security_policy_source_type_spec.rb'
+- './ee/spec/graphql/types/security_report_summary_section_type_spec.rb'
+- './ee/spec/graphql/types/security_report_summary_type_spec.rb'
+- './ee/spec/graphql/types/security_scanners_spec.rb'
+- './ee/spec/graphql/types/security_scanner_type_enum_spec.rb'
+- './ee/spec/graphql/types/security/training_type_spec.rb'
+- './ee/spec/graphql/types/security/training_url_request_status_enum_spec.rb'
+- './ee/spec/graphql/types/security/training_url_type_spec.rb'
+- './ee/spec/graphql/types/timebox_report_type_spec.rb'
+- './ee/spec/graphql/types/vulnerabilities/container_image_type_spec.rb'
+- './ee/spec/graphql/types/vulnerabilities_count_by_day_type_spec.rb'
+- './ee/spec/graphql/types/vulnerabilities/link_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_confidence_enum_spec.rb'
+- './ee/spec/graphql/types/vulnerability_details/base_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_details/boolean_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_details/code_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_details/commit_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_details/diff_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_details/file_location_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_details/int_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_details/list_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_details/markdown_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_details/module_location_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_details/table_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_details/text_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_details/url_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_detail_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/external_issue_link_external_tracker_enum_spec.rb'
+- './ee/spec/graphql/types/vulnerability/external_issue_link_type_enum_spec.rb'
+- './ee/spec/graphql/types/vulnerability/external_issue_link_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_grade_enum_spec.rb'
+- './ee/spec/graphql/types/vulnerability_identifier_input_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_identifier_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability/issue_link_type_enum_spec.rb'
+- './ee/spec/graphql/types/vulnerability/issue_link_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_location/cluster_image_scanning_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_location/container_scanning_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_location/coverage_fuzzing_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_location/dast_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_location/dependency_scanning_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_location/generic_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_location/sast_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_location/secret_detection_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_location_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_report_type_enum_spec.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/graphql/types/vulnerability_scanner_input_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_scanner_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_scanner_vendor_input_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_severities_count_type_spec.rb'
+- './ee/spec/graphql/types/vulnerability_severity_enum_spec.rb'
+- './ee/spec/graphql/types/vulnerability_sort_enum_spec.rb'
+- './ee/spec/graphql/types/vulnerability_state_enum_spec.rb'
+- './ee/spec/graphql/types/vulnerability_type_spec.rb'
+- './ee/spec/graphql/types/vulnerable_dependency_type_spec.rb'
+- './ee/spec/graphql/types/vulnerable_kubernetes_resource_type_spec.rb'
+- './ee/spec/graphql/types/vulnerable_package_type_spec.rb'
+- './ee/spec/graphql/types/vulnerable_projects_by_grade_type_spec.rb'
+- './ee/spec/graphql/types/work_items/type_spec.rb'
+- './ee/spec/graphql/types/work_items/widget_interface_spec.rb'
+- './ee/spec/graphql/types/work_items/widgets/verification_status_type_spec.rb'
+- './ee/spec/helpers/admin/emails_helper_spec.rb'
+- './ee/spec/helpers/admin/ip_restriction_helper_spec.rb'
+- './ee/spec/helpers/admin/repo_size_limit_helper_spec.rb'
+- './ee/spec/helpers/analytics/code_review_helper_spec.rb'
+- './ee/spec/helpers/application_helper_spec.rb'
+- './ee/spec/helpers/audit_events_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/credentials_inventory_helper_spec.rb'
+- './ee/spec/helpers/ee/access_tokens_helper_spec.rb'
+- './ee/spec/helpers/ee/admin/identities_helper_spec.rb'
+- './ee/spec/helpers/ee/application_settings_helper_spec.rb'
+- './ee/spec/helpers/ee/auth_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/pipelines_helper_spec.rb'
+- './ee/spec/helpers/ee/ci/runners_helper_spec.rb'
+- './ee/spec/helpers/ee/dashboard_helper_spec.rb'
+- './ee/spec/helpers/ee/emails_helper_spec.rb'
+- './ee/spec/helpers/ee/environments_helper_spec.rb'
+- './ee/spec/helpers/ee/events_helper_spec.rb'
+- './ee/spec/helpers/ee/export_helper_spec.rb'
+- './ee/spec/helpers/ee/feature_flags_helper_spec.rb'
+- './ee/spec/helpers/ee/geo_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/groups/settings_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/namespaces_helper_spec.rb'
+- './ee/spec/helpers/ee/namespace_user_cap_reached_alert_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/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'
+- './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/registrations_helper_spec.rb'
+- './ee/spec/helpers/ee/releases_helper_spec.rb'
+- './ee/spec/helpers/ee/security_orchestration_helper_spec.rb'
+- './ee/spec/helpers/ee/sorting_helper_spec.rb'
+- './ee/spec/helpers/ee/subscribable_banner_helper_spec.rb'
+- './ee/spec/helpers/ee/system_note_helper_spec.rb'
+- './ee/spec/helpers/ee/todos_helper_spec.rb'
+- './ee/spec/helpers/ee/trial_helper_spec.rb'
+- './ee/spec/helpers/ee/trial_registration_helper_spec.rb'
+- './ee/spec/helpers/ee/users/callouts_helper_spec.rb'
+- './ee/spec/helpers/ee/version_check_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/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'
+- './ee/spec/helpers/manual_quarterly_co_term_banner_helper_spec.rb'
+- './ee/spec/helpers/markup_helper_spec.rb'
+- './ee/spec/helpers/merge_requests_helper_spec.rb'
+- './ee/spec/helpers/nav/new_dropdown_helper_spec.rb'
+- './ee/spec/helpers/nav/top_nav_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/preferences_helper_spec.rb'
+- './ee/spec/helpers/prevent_forking_helper_spec.rb'
+- './ee/spec/helpers/projects_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/push_rules_helper_spec.rb'
+- './ee/spec/helpers/roadmaps_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_registrations/reassurances_helper_spec.rb'
+- './ee/spec/helpers/trial_status_widget_helper_spec.rb'
+- './ee/spec/helpers/users_helper_spec.rb'
+- './ee/spec/helpers/vulnerabilities_helper_spec.rb'
+- './ee/spec/initializers/1_settings_spec.rb'
+- './ee/spec/initializers/database_config_spec.rb'
+- './ee/spec/initializers/fog_google_https_private_urls_spec.rb'
+- './ee/spec/initializers/session_store_spec.rb'
+- './ee/spec/lib/analytics/devops_adoption/snapshot_calculator_spec.rb'
+- './ee/spec/lib/analytics/group_activity_calculator_spec.rb'
+- './ee/spec/lib/analytics/merge_request_metrics_calculator_spec.rb'
+- './ee/spec/lib/analytics/merge_request_metrics_refresh_spec.rb'
+- './ee/spec/lib/analytics/productivity_analytics_request_params_spec.rb'
+- './ee/spec/lib/analytics/refresh_approvals_data_spec.rb'
+- './ee/spec/lib/analytics/refresh_comments_data_spec.rb'
+- './ee/spec/lib/analytics/refresh_reassign_data_spec.rb'
+- './ee/spec/lib/api/entities/deployments/approval_spec.rb'
+- './ee/spec/lib/api/entities/deployments/approval_summary_spec.rb'
+- './ee/spec/lib/api/entities/merge_request_approval_setting_spec.rb'
+- './ee/spec/lib/api/entities/pending_member_spec.rb'
+- './ee/spec/lib/api/entities/protected_environments/approval_rule_for_summary_spec.rb'
+- './ee/spec/lib/api/entities/protected_environments/approval_rule_spec.rb'
+- './ee/spec/lib/api/entities/protected_environments/deploy_access_level_spec.rb'
+- './ee/spec/lib/arkose/settings_spec.rb'
+- './ee/spec/lib/arkose/verify_response_spec.rb'
+- './ee/spec/lib/audit/changes_spec.rb'
+- './ee/spec/lib/audit/details_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/banzai/filter/cross_project_issuable_information_filter_spec.rb'
+- './ee/spec/lib/banzai/filter/issuable_reference_expansion_filter_spec.rb'
+- './ee/spec/lib/banzai/filter/jira_private_image_link_filter_spec.rb'
+- './ee/spec/lib/banzai/filter/references/epic_reference_filter_spec.rb'
+- './ee/spec/lib/banzai/filter/references/iteration_reference_filter_spec.rb'
+- './ee/spec/lib/banzai/filter/references/label_reference_filter_spec.rb'
+- './ee/spec/lib/banzai/filter/references/vulnerability_reference_filters_spec.rb'
+- './ee/spec/lib/banzai/issuable_extractor_spec.rb'
+- './ee/spec/lib/banzai/reference_parser/epic_parser_spec.rb'
+- './ee/spec/lib/banzai/reference_parser/iteration_parser_spec.rb'
+- './ee/spec/lib/banzai/reference_parser/vulnerability_parser_spec.rb'
+- './ee/spec/lib/bulk_imports/common/pipelines/boards_pipeline_spec.rb'
+- './ee/spec/lib/bulk_imports/common/pipelines/wiki_pipeline_spec.rb'
+- './ee/spec/lib/bulk_imports/groups/graphql/get_iterations_query_spec.rb'
+- './ee/spec/lib/bulk_imports/groups/pipelines/epics_pipeline_spec.rb'
+- './ee/spec/lib/bulk_imports/groups/pipelines/iterations_pipeline_spec.rb'
+- './ee/spec/lib/bulk_imports/projects/pipelines/issues_pipeline_spec.rb'
+- './ee/spec/lib/bulk_imports/projects/pipelines/protected_branches_pipeline_spec.rb'
+- './ee/spec/lib/bulk_imports/projects/pipelines/push_rule_pipeline_spec.rb'
+- './ee/spec/lib/compliance_management/merge_request_approval_settings/resolver_spec.rb'
+- './ee/spec/lib/container_registry/client_spec.rb'
+- './ee/spec/lib/ee/api/entities/analytics/code_review/merge_request_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/ci/minutes/additional_pack_spec.rb'
+- './ee/spec/lib/ee/api/entities/deployment_extended_spec.rb'
+- './ee/spec/lib/ee/api/entities/experiment_spec.rb'
+- './ee/spec/lib/ee/api/entities/geo_node_status_spec.rb'
+- './ee/spec/lib/ee/api/entities/group_detail_spec.rb'
+- './ee/spec/lib/ee/api/entities/groups/repository_storage_move_spec.rb'
+- './ee/spec/lib/ee/api/entities/member_spec.rb'
+- './ee/spec/lib/ee/api/entities/project_spec.rb'
+- './ee/spec/lib/ee/api/entities/scim/conflict_spec.rb'
+- './ee/spec/lib/ee/api/entities/scim/emails_spec.rb'
+- './ee/spec/lib/ee/api/entities/scim/error_spec.rb'
+- './ee/spec/lib/ee/api/entities/scim/not_found_spec.rb'
+- './ee/spec/lib/ee/api/entities/scim/user_name_spec.rb'
+- './ee/spec/lib/ee/api/entities/scim/user_spec.rb'
+- './ee/spec/lib/ee/api/entities/scim/users_spec.rb'
+- './ee/spec/lib/ee/api/entities/user_with_admin_spec.rb'
+- './ee/spec/lib/ee/api/entities/vulnerability_export_spec.rb'
+- './ee/spec/lib/ee/api/entities/vulnerability_spec.rb'
+- './ee/spec/lib/ee/api/helpers/issues_helpers_spec.rb'
+- './ee/spec/lib/ee/api/helpers/members_helpers_spec.rb'
+- './ee/spec/lib/ee/api/helpers/notes_helpers_spec.rb'
+- './ee/spec/lib/ee/api/helpers/scim_pagination_spec.rb'
+- './ee/spec/lib/ee/api/helpers_spec.rb'
+- './ee/spec/lib/ee/api/helpers/variables_helpers_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/backup/repositories_spec.rb'
+- './ee/spec/lib/ee/banzai/filter/sanitization_filter_spec.rb'
+- './ee/spec/lib/ee/bulk_imports/groups/stage_spec.rb'
+- './ee/spec/lib/ee/bulk_imports/projects/stage_spec.rb'
+- './ee/spec/lib/ee/event_filter_spec.rb'
+- './ee/spec/lib/ee/feature_spec.rb'
+- './ee/spec/lib/ee/gitlab/alert_management/payload/generic_spec.rb'
+- './ee/spec/lib/ee/gitlab/analytics/cycle_analytics/aggregated/base_query_builder_spec.rb'
+- './ee/spec/lib/ee/gitlab/analytics/cycle_analytics/base_query_builder_spec.rb'
+- './ee/spec/lib/ee/gitlab/application_context_spec.rb'
+- './ee/spec/lib/ee/gitlab/application_rate_limiter_spec.rb'
+- './ee/spec/lib/ee/gitlab/auth/auth_finders_spec.rb'
+- './ee/spec/lib/ee/gitlab/auth/ldap/access_levels_spec.rb'
+- './ee/spec/lib/ee/gitlab/auth/ldap/config_spec.rb'
+- './ee/spec/lib/ee/gitlab/auth/ldap/group_spec.rb'
+- './ee/spec/lib/ee/gitlab/auth/ldap/sync/admin_users_spec.rb'
+- './ee/spec/lib/ee/gitlab/auth/ldap/sync/external_users_spec.rb'
+- './ee/spec/lib/ee/gitlab/auth/ldap/sync/group_spec.rb'
+- './ee/spec/lib/ee/gitlab/auth/ldap/sync/groups_spec.rb'
+- './ee/spec/lib/ee/gitlab/auth/ldap/sync/proxy_spec.rb'
+- './ee/spec/lib/ee/gitlab/auth/request_authenticator_spec.rb'
+- './ee/spec/lib/ee/gitlab/auth/saml/identity_linker_spec.rb'
+- './ee/spec/lib/ee/gitlab/background_migration/backfill_iteration_cadence_id_for_boards_spec.rb'
+- './ee/spec/lib/ee/gitlab/background_migration/backfill_project_statistics_container_repository_size_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/fix_incorrect_max_seats_used_spec.rb'
+- './ee/spec/lib/ee/gitlab/background_migration/migrate_approver_to_approval_rules_check_progress_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/background_migration/migrate_job_artifact_registry_to_ssf_spec.rb'
+- './ee/spec/lib/ee/gitlab/background_migration/migrate_shared_vulnerability_scanners_spec.rb'
+- './ee/spec/lib/ee/gitlab/background_migration/populate_latest_pipeline_ids_spec.rb'
+- './ee/spec/lib/ee/gitlab/background_migration/populate_namespace_statistics_spec.rb'
+- './ee/spec/lib/ee/gitlab/background_migration/populate_resolved_on_default_branch_column_spec.rb'
+- './ee/spec/lib/ee/gitlab/background_migration/populate_uuids_for_security_findings_spec.rb'
+- './ee/spec/lib/ee/gitlab/background_migration/purge_stale_security_scans_spec.rb'
+- './ee/spec/lib/ee/gitlab/background_migration/recalculate_vulnerability_finding_signatures_for_findings_spec.rb'
+- './ee/spec/lib/ee/gitlab/background_migration/update_vulnerability_occurrences_location_spec.rb'
+- './ee/spec/lib/ee/gitlab/checks/push_rule_check_spec.rb'
+- './ee/spec/lib/ee/gitlab/checks/push_rules/branch_check_spec.rb'
+- './ee/spec/lib/ee/gitlab/checks/push_rules/commit_check_spec.rb'
+- './ee/spec/lib/ee/gitlab/checks/push_rules/file_size_check_spec.rb'
+- './ee/spec/lib/ee/gitlab/checks/push_rules/tag_check_spec.rb'
+- './ee/spec/lib/ee/gitlab/ci/config/entry/bridge_spec.rb'
+- './ee/spec/lib/ee/gitlab/ci/config/entry/need_spec.rb'
+- './ee/spec/lib/ee/gitlab/ci/config/entry/needs_spec.rb'
+- './ee/spec/lib/ee/gitlab/ci/config_spec.rb'
+- './ee/spec/lib/ee/gitlab/ci/jwt_spec.rb'
+- './ee/spec/lib/ee/gitlab/ci/matching/runner_matcher_spec.rb'
+- './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/abilities_spec.rb'
+- './ee/spec/lib/ee/gitlab/ci/pipeline/chain/validate/after_config_spec.rb'
+- './ee/spec/lib/ee/gitlab/ci/pipeline/chain/validate/external_spec.rb'
+- './ee/spec/lib/ee/gitlab/ci/pipeline/chain/validate/security_orchestration_policy_spec.rb'
+- './ee/spec/lib/ee/gitlab/ci/pipeline/quota/activity_spec.rb'
+- './ee/spec/lib/ee/gitlab/ci/pipeline/quota/job_activity_spec.rb'
+- './ee/spec/lib/ee/gitlab/ci/pipeline/quota/size_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/ci/templates/templates_spec.rb'
+- './ee/spec/lib/ee/gitlab/cleanup/orphan_job_artifact_files_batch_spec.rb'
+- './ee/spec/lib/ee/gitlab/cleanup/orphan_job_artifact_files_spec.rb'
+- './ee/spec/lib/ee/gitlab/database/gitlab_schema_spec.rb'
+- './ee/spec/lib/ee/gitlab/database_spec.rb'
+- './ee/spec/lib/ee/gitlab/elastic/helper_spec.rb'
+- './ee/spec/lib/ee/gitlab/email/handler/service_desk_handler_spec.rb'
+- './ee/spec/lib/ee/gitlab/etag_caching/router/rails_spec.rb'
+- './ee/spec/lib/ee/gitlab/event_store_spec.rb'
+- './ee/spec/lib/ee/gitlab/git_access_design_spec.rb'
+- './ee/spec/lib/ee/gitlab/git_access_project_spec.rb'
+- './ee/spec/lib/ee/gitlab/git_access_snippet_spec.rb'
+- './ee/spec/lib/ee/gitlab/gon_helper_spec.rb'
+- './ee/spec/lib/ee/gitlab/group_search_results_spec.rb'
+- './ee/spec/lib/ee/gitlab/hook_data/group_member_builder_spec.rb'
+- './ee/spec/lib/ee/gitlab/hook_data/issue_builder_spec.rb'
+- './ee/spec/lib/ee/gitlab/hook_data/user_builder_spec.rb'
+- './ee/spec/lib/ee/gitlab/import_export/after_export_strategies/custom_template_export_import_strategy_spec.rb'
+- './ee/spec/lib/ee/gitlab/import_export/group/legacy_tree_restorer_spec.rb'
+- './ee/spec/lib/ee/gitlab/import_export/group/legacy_tree_saver_spec.rb'
+- './ee/spec/lib/ee/gitlab/import_export/group/tree_restorer_spec.rb'
+- './ee/spec/lib/ee/gitlab/import_export/group/tree_saver_spec.rb'
+- './ee/spec/lib/ee/gitlab/import_export/project/tree_restorer_spec.rb'
+- './ee/spec/lib/ee/gitlab/import_export/project/tree_saver_spec.rb'
+- './ee/spec/lib/ee/gitlab/import_export/repo_restorer_spec.rb'
+- './ee/spec/lib/ee/gitlab/import_export/wiki_repo_saver_spec.rb'
+- './ee/spec/lib/ee/gitlab/ip_restriction/enforcer_spec.rb'
+- './ee/spec/lib/ee/gitlab/issuable/clone/copy_resource_events_service_spec.rb'
+- './ee/spec/lib/ee/gitlab/issuable_metadata_spec.rb'
+- './ee/spec/lib/ee/gitlab/metrics/samplers/database_sampler_spec.rb'
+- './ee/spec/lib/ee/gitlab/middleware/read_only_spec.rb'
+- './ee/spec/lib/ee/gitlab/namespaces/storage/enforcement_spec.rb'
+- './ee/spec/lib/ee/gitlab/namespace_storage_size_error_message_spec.rb'
+- './ee/spec/lib/ee/gitlab/omniauth_initializer_spec.rb'
+- './ee/spec/lib/ee/gitlab/pages/deployment_update_spec.rb'
+- './ee/spec/lib/ee/gitlab/prometheus/metric_group_spec.rb'
+- './ee/spec/lib/ee/gitlab/rack_attack/request_spec.rb'
+- './ee/spec/lib/ee/gitlab/repo_path_spec.rb'
+- './ee/spec/lib/ee/gitlab/repository_size_checker_spec.rb'
+- './ee/spec/lib/ee/gitlab/scim/attribute_transform_spec.rb'
+- './ee/spec/lib/ee/gitlab/scim/deprovision_service_spec.rb'
+- './ee/spec/lib/ee/gitlab/scim/filter_parser_spec.rb'
+- './ee/spec/lib/ee/gitlab/scim/params_parser_spec.rb'
+- './ee/spec/lib/ee/gitlab/scim/provisioning_service_spec.rb'
+- './ee/spec/lib/ee/gitlab/scim/reprovision_service_spec.rb'
+- './ee/spec/lib/ee/gitlab/scim/value_parser_spec.rb'
+- './ee/spec/lib/ee/gitlab/search_results_spec.rb'
+- './ee/spec/lib/ee/gitlab/security/scan_configuration_spec.rb'
+- './ee/spec/lib/ee/gitlab/snippet_search_results_spec.rb'
+- './ee/spec/lib/ee/gitlab/template/gitlab_ci_yml_template_spec.rb'
+- './ee/spec/lib/ee/gitlab/tracking_spec.rb'
+- './ee/spec/lib/ee/gitlab/url_builder_spec.rb'
+- './ee/spec/lib/ee/gitlab/usage_data_counters/hll_redis_counter_spec.rb'
+- './ee/spec/lib/ee/gitlab/usage_data_counters/issue_activity_unique_counter_spec.rb'
+- './ee/spec/lib/ee/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb'
+- './ee/spec/lib/ee/gitlab/usage_data_counters/work_item_activity_unique_counter_spec.rb'
+- './ee/spec/lib/ee/gitlab/usage_data_non_sql_metrics_spec.rb'
+- './ee/spec/lib/ee/gitlab/usage_data_spec.rb'
+- './ee/spec/lib/ee/gitlab/usage/service_ping/payload_keys_processor_spec.rb'
+- './ee/spec/lib/ee/gitlab/usage/service_ping_report_spec.rb'
+- './ee/spec/lib/ee/gitlab/verify/lfs_objects_spec.rb'
+- './ee/spec/lib/ee/gitlab/verify/uploads_spec.rb'
+- './ee/spec/lib/ee/gitlab/web_hooks/rate_limiter_spec.rb'
+- './ee/spec/lib/ee/gitlab/web_ide/config/entry/global_spec.rb'
+- './ee/spec/lib/ee/service_ping/build_payload_spec.rb'
+- './ee/spec/lib/ee/service_ping/permit_data_categories_spec.rb'
+- './ee/spec/lib/ee/service_ping/service_ping_settings_spec.rb'
+- './ee/spec/lib/ee/sidebars/groups/menus/issues_menu_spec.rb'
+- './ee/spec/lib/ee/sidebars/groups/menus/settings_menu_spec.rb'
+- './ee/spec/lib/ee/sidebars/projects/menus/analytics_menu_spec.rb'
+- './ee/spec/lib/ee/sidebars/projects/menus/ci_cd_menu_spec.rb'
+- './ee/spec/lib/ee/sidebars/projects/menus/issues_menu_spec.rb'
+- './ee/spec/lib/ee/sidebars/projects/menus/monitor_menu_spec.rb'
+- './ee/spec/lib/ee/sidebars/projects/menus/repository_menu_spec.rb'
+- './ee/spec/lib/ee/sidebars/projects/menus/security_compliance_menu_spec.rb'
+- './ee/spec/lib/ee/sidebars/projects/panel_spec.rb'
+- './ee/spec/lib/elastic/latest/commit_config_spec.rb'
+- './ee/spec/lib/elastic/latest/config_spec.rb'
+- './ee/spec/lib/elastic/latest/custom_language_analyzers_spec.rb'
+- './ee/spec/lib/elastic/latest/git_class_proxy_spec.rb'
+- './ee/spec/lib/elastic/latest/git_instance_proxy_spec.rb'
+- './ee/spec/lib/elastic/latest/issue_config_spec.rb'
+- './ee/spec/lib/elastic/latest/merge_request_config_spec.rb'
+- './ee/spec/lib/elastic/latest/note_config_spec.rb'
+- './ee/spec/lib/elastic/latest/project_instance_proxy_spec.rb'
+- './ee/spec/lib/elastic/latest/project_wiki_class_proxy_spec.rb'
+- './ee/spec/lib/elastic/latest/project_wiki_instance_proxy_spec.rb'
+- './ee/spec/lib/elastic/latest/routing_spec.rb'
+- './ee/spec/lib/elastic/latest/snippet_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/gem_extensions/elasticsearch/model/adapter/active_record/records_spec.rb'
+- './ee/spec/lib/gem_extensions/elasticsearch/model/indexing/instance_methods_spec.rb'
+- './ee/spec/lib/gitlab/alert_management/alert_payload_field_extractor_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/aggregated/data_for_duration_chart_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/data_collector_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/data_for_duration_chart_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/distinct_stage_loader_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/request_params_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_closed_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_added_to_board_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_associated_with_milestone_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_label_added_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_label_removed_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_last_edited_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_closed_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_first_commit_at_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_label_added_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_label_removed_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_edited_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/summary/base_dora_summary_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/summary/change_failure_rate_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/summary/group/stage_summary_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/summary/lead_time_for_changes_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/summary/lead_time_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/summary/stage_time_summary_spec.rb'
+- './ee/spec/lib/gitlab/analytics/cycle_analytics/summary/time_to_restore_service_spec.rb'
+- './ee/spec/lib/gitlab/analytics/type_of_work/tasks_by_type_spec.rb'
+- './ee/spec/lib/gitlab/audit/auditor_spec.rb'
+- './ee/spec/lib/gitlab/audit/events/preloader_spec.rb'
+- './ee/spec/lib/gitlab/audit/levels/group_spec.rb'
+- './ee/spec/lib/gitlab/audit/levels/instance_spec.rb'
+- './ee/spec/lib/gitlab/audit/levels/project_spec.rb'
+- './ee/spec/lib/gitlab/auth/group_saml/auth_hash_spec.rb'
+- './ee/spec/lib/gitlab/auth/group_saml/dynamic_settings_spec.rb'
+- './ee/spec/lib/gitlab/auth/group_saml/failure_handler_spec.rb'
+- './ee/spec/lib/gitlab/auth/group_saml/gma_membership_enforcer_spec.rb'
+- './ee/spec/lib/gitlab/auth/group_saml/group_lookup_spec.rb'
+- './ee/spec/lib/gitlab/auth/group_saml/identity_linker_spec.rb'
+- './ee/spec/lib/gitlab/auth/group_saml/membership_enforcer_spec.rb'
+- './ee/spec/lib/gitlab/auth/group_saml/membership_updater_spec.rb'
+- './ee/spec/lib/gitlab/auth/group_saml/response_check_spec.rb'
+- './ee/spec/lib/gitlab/auth/group_saml/response_store_spec.rb'
+- './ee/spec/lib/gitlab/auth/group_saml/session_enforcer_spec.rb'
+- './ee/spec/lib/gitlab/auth/group_saml/sso_enforcer_spec.rb'
+- './ee/spec/lib/gitlab/auth/group_saml/sso_state_spec.rb'
+- './ee/spec/lib/gitlab/auth/group_saml/token_actor_spec.rb'
+- './ee/spec/lib/gitlab/auth/group_saml/user_spec.rb'
+- './ee/spec/lib/gitlab/auth/group_saml/xml_response_spec.rb'
+- './ee/spec/lib/gitlab/auth/ldap/access_spec.rb'
+- './ee/spec/lib/gitlab/auth/ldap/adapter_spec.rb'
+- './ee/spec/lib/gitlab/auth/ldap/person_spec.rb'
+- './ee/spec/lib/gitlab/auth/ldap/user_spec.rb'
+- './ee/spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb'
+- './ee/spec/lib/gitlab/auth/o_auth/user_spec.rb'
+- './ee/spec/lib/gitlab/authority_analyzer_spec.rb'
+- './ee/spec/lib/gitlab/auth/otp/session_enforcer_spec.rb'
+- './ee/spec/lib/gitlab/auth/saml/config_spec.rb'
+- './ee/spec/lib/gitlab/auth/saml/membership_updater_spec.rb'
+- './ee/spec/lib/gitlab/auth/saml/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/auth/smartcard/san_extension_spec.rb'
+- './ee/spec/lib/gitlab/auth/smartcard/session_enforcer_spec.rb'
+- './ee/spec/lib/gitlab/auth/smartcard/session_spec.rb'
+- './ee/spec/lib/gitlab/background_migration/migrate_requirements_to_work_items_spec.rb'
+- './ee/spec/lib/gitlab/background_migration/populate_test_reports_issue_id_spec.rb'
+- './ee/spec/lib/gitlab/background_migration/remove_all_trace_expiration_dates_spec.rb'
+- './ee/spec/lib/gitlab/bullet/exclusions_spec.rb'
+- './ee/spec/lib/gitlab/cache_spec.rb'
+- './ee/spec/lib/gitlab/checks/changes_access_spec.rb'
+- './ee/spec/lib/gitlab/checks/diff_check_spec.rb'
+- './ee/spec/lib/gitlab/ci/config/entry/dast_configuration_spec.rb'
+- './ee/spec/lib/gitlab/ci/config/entry/job_spec.rb'
+- './ee/spec/lib/gitlab/ci/config/entry/secret_spec.rb'
+- './ee/spec/lib/gitlab/ci/config/entry/vault/engine_spec.rb'
+- './ee/spec/lib/gitlab/ci/config/entry/vault/secret_spec.rb'
+- './ee/spec/lib/gitlab/ci/config/required/processor_spec.rb'
+- './ee/spec/lib/gitlab/ci/config/security_orchestration_policies/processor_spec.rb'
+- './ee/spec/lib/gitlab/cidr_spec.rb'
+- './ee/spec/lib/gitlab/ci/minutes/build_consumption_spec.rb'
+- './ee/spec/lib/gitlab/ci/minutes/cached_quota_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/minutes/runners_availability_spec.rb'
+- './ee/spec/lib/gitlab/ci/parsers/license_compliance/license_scanning_spec.rb'
+- './ee/spec/lib/gitlab/ci/parsers/metrics/generic_spec.rb'
+- './ee/spec/lib/gitlab/ci/parsers/security/cluster_image_scanning_spec.rb'
+- './ee/spec/lib/gitlab/ci/parsers/security/container_scanning_spec.rb'
+- './ee/spec/lib/gitlab/ci/parsers/security/coverage_fuzzing_spec.rb'
+- './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/parsers/security/formatters/dependency_list_spec.rb'
+- './ee/spec/lib/gitlab/ci/parsers/security/validators/default_branch_image_validator_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/pipeline/chain/limit/activity_spec.rb'
+- './ee/spec/lib/gitlab/ci/pipeline/chain/limit/job_activity_spec.rb'
+- './ee/spec/lib/gitlab/ci/pipeline/chain/limit/size_spec.rb'
+- './ee/spec/lib/gitlab/ci/reports/coverage_fuzzing/report_spec.rb'
+- './ee/spec/lib/gitlab/ci/reports/dependency_list/dependency_spec.rb'
+- './ee/spec/lib/gitlab/ci/reports/dependency_list/report_spec.rb'
+- './ee/spec/lib/gitlab/ci/reports/license_scanning/dependency_spec.rb'
+- './ee/spec/lib/gitlab/ci/reports/license_scanning/license_spec.rb'
+- './ee/spec/lib/gitlab/ci/reports/license_scanning/reports_comparer_spec.rb'
+- './ee/spec/lib/gitlab/ci/reports/license_scanning/report_spec.rb'
+- './ee/spec/lib/gitlab/ci/reports/metrics/reports_comparer_spec.rb'
+- './ee/spec/lib/gitlab/ci/reports/metrics/report_spec.rb'
+- './ee/spec/lib/gitlab/ci/reports/security/finding_spec.rb'
+- './ee/spec/lib/gitlab/ci/reports/security/locations/cluster_image_scanning_spec.rb'
+- './ee/spec/lib/gitlab/ci/reports/security/locations/container_scanning_spec.rb'
+- './ee/spec/lib/gitlab/ci/reports/security/locations/dast_spec.rb'
+- './ee/spec/lib/gitlab/ci/reports/security/locations/dependency_scanning_spec.rb'
+- './ee/spec/lib/gitlab/ci/reports/security/remediation_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_gitlab_ci_yaml_spec.rb'
+- './ee/spec/lib/gitlab/ci/templates/dast_latest_gitlab_ci_yaml_spec.rb'
+- './ee/spec/lib/gitlab/ci/templates/dast_on_demand_api_scan_gitlab_ci_yaml_spec.rb'
+- './ee/spec/lib/gitlab/ci/templates/dast_runner_validation_gitlab_ci_yaml_spec.rb'
+- './ee/spec/lib/gitlab/ci/templates/dependency_scanning_gitlab_ci_yaml_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/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_latest_gitlab_ci_yaml_spec.rb'
+- './ee/spec/lib/gitlab/ci/templates/secret_detection_gitlab_ci_yaml_spec.rb'
+- './ee/spec/lib/gitlab/ci/templates/secret_detection_latest_gitlab_ci_yaml_spec.rb'
+- './ee/spec/lib/gitlab/ci/templates/secure_binaries_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/yaml_processor_spec.rb'
+- './ee/spec/lib/gitlab/code_owners/entry_spec.rb'
+- './ee/spec/lib/gitlab/code_owners/file_spec.rb'
+- './ee/spec/lib/gitlab/code_owners/groups_loader_spec.rb'
+- './ee/spec/lib/gitlab/code_owners/loader_spec.rb'
+- './ee/spec/lib/gitlab/code_owners/reference_extractor_spec.rb'
+- './ee/spec/lib/gitlab/code_owners_spec.rb'
+- './ee/spec/lib/gitlab/code_owners/users_loader_spec.rb'
+- './ee/spec/lib/gitlab/code_owners/validator_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/com_spec.rb'
+- './ee/spec/lib/gitlab/console_spec.rb'
+- './ee/spec/lib/gitlab/contribution_analytics/data_collector_spec.rb'
+- './ee/spec/lib/gitlab/customers_dot/jwt_spec.rb'
+- './ee/spec/lib/gitlab/custom_file_templates_spec.rb'
+- './ee/spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb'
+- './ee/spec/lib/gitlab/data_builder/vulnerability_spec.rb'
+- './ee/spec/lib/gitlab/elastic/bulk_indexer_spec.rb'
+- './ee/spec/lib/gitlab/elastic/client_spec.rb'
+- './ee/spec/lib/gitlab/elastic/document_reference_spec.rb'
+- './ee/spec/lib/gitlab/elastic/elasticsearch_enabled_cache_spec.rb'
+- './ee/spec/lib/gitlab/elastic/group_search_results_spec.rb'
+- './ee/spec/lib/gitlab/elastic/indexer_spec.rb'
+- './ee/spec/lib/gitlab/elastic/project_search_results_spec.rb'
+- './ee/spec/lib/gitlab/elastic/search_results_spec.rb'
+- './ee/spec/lib/gitlab/elastic/snippet_search_results_spec.rb'
+- './ee/spec/lib/gitlab/email/handler/create_note_handler_spec.rb'
+- './ee/spec/lib/gitlab/email/message/account_validation_spec.rb'
+- './ee/spec/lib/gitlab/exclusive_lease_spec.rb'
+- './ee/spec/lib/gitlab/expiring_subscription_message_spec.rb'
+- './ee/spec/lib/gitlab/favicon_spec.rb'
+- './ee/spec/lib/gitlab/geo/base_request_spec.rb'
+- './ee/spec/lib/gitlab/geo/cron_manager_spec.rb'
+- './ee/spec/lib/gitlab/geo/event_gap_tracking_spec.rb'
+- './ee/spec/lib/gitlab/geo/geo_node_status_check_spec.rb'
+- './ee/spec/lib/gitlab/geo/geo_tasks_spec.rb'
+- './ee/spec/lib/gitlab/geo/git_push_http_spec.rb'
+- './ee/spec/lib/gitlab/geo/git_ssh_proxy_spec.rb'
+- './ee/spec/lib/gitlab/geo/health_check_spec.rb'
+- './ee/spec/lib/gitlab/geo/json_request_spec.rb'
+- './ee/spec/lib/gitlab/geo/jwt_request_decoder_spec.rb'
+- './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'
+- './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/lease_spec.rb'
+- './ee/spec/lib/gitlab/geo/log_cursor/logger_spec.rb'
+- './ee/spec/lib/gitlab/geo/logger_spec.rb'
+- './ee/spec/lib/gitlab/geo/log_helpers_spec.rb'
+- './ee/spec/lib/gitlab/geo/oauth/login_state_spec.rb'
+- './ee/spec/lib/gitlab/geo/oauth/logout_state_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/registry_batcher_spec.rb'
+- './ee/spec/lib/gitlab/geo/replication/blob_downloader_spec.rb'
+- './ee/spec/lib/gitlab/geo/replication/blob_retriever_spec.rb'
+- './ee/spec/lib/gitlab/geo/replicator_spec.rb'
+- './ee/spec/lib/gitlab/geo/signed_data_spec.rb'
+- './ee/spec/lib/gitlab/geo_spec.rb'
+- './ee/spec/lib/gitlab/git_access_spec.rb'
+- './ee/spec/lib/gitlab/git_access_wiki_spec.rb'
+- './ee/spec/lib/gitlab/gl_repository/identifier_spec.rb'
+- './ee/spec/lib/gitlab/gl_repository/repo_type_spec.rb'
+- './ee/spec/lib/gitlab/gl_repository_spec.rb'
+- './ee/spec/lib/gitlab/graphql/aggregations/epics/epic_node_spec.rb'
+- './ee/spec/lib/gitlab/graphql/aggregations/epics/lazy_epic_aggregate_spec.rb'
+- './ee/spec/lib/gitlab/graphql/aggregations/epics/lazy_links_aggregate_spec.rb'
+- './ee/spec/lib/gitlab/graphql/aggregations/issuables/lazy_links_aggregate_spec.rb'
+- './ee/spec/lib/gitlab/graphql/aggregations/issues/lazy_links_aggregate_spec.rb'
+- './ee/spec/lib/gitlab/graphql/aggregations/security_orchestration_policies/lazy_dast_profile_aggregate_spec.rb'
+- './ee/spec/lib/gitlab/graphql/aggregations/vulnerabilities/lazy_user_notes_count_aggregate_spec.rb'
+- './ee/spec/lib/gitlab/graphql/aggregations/vulnerability_statistics/lazy_aggregate_spec.rb'
+- './ee/spec/lib/gitlab/graphql/loaders/bulk_epic_aggregate_loader_spec.rb'
+- './ee/spec/lib/gitlab/graphql/loaders/oncall_participant_loader_spec.rb'
+- './ee/spec/lib/gitlab/group_plans_preloader_spec.rb'
+- './ee/spec/lib/gitlab/import_export/attributes_permitter_spec.rb'
+- './ee/spec/lib/gitlab/import_export/group/group_and_descendants_repo_restorer_spec.rb'
+- './ee/spec/lib/gitlab/import_export/group/group_and_descendants_repo_saver_spec.rb'
+- './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'
+- './ee/spec/lib/gitlab/insights/finders/issuable_finder_spec.rb'
+- './ee/spec/lib/gitlab/insights/finders/projects_finder_spec.rb'
+- './ee/spec/lib/gitlab/insights/loader_spec.rb'
+- './ee/spec/lib/gitlab/insights/project_insights_config_spec.rb'
+- './ee/spec/lib/gitlab/insights/reducers/base_reducer_spec.rb'
+- './ee/spec/lib/gitlab/insights/reducers/count_per_label_reducer_spec.rb'
+- './ee/spec/lib/gitlab/insights/reducers/count_per_period_reducer_spec.rb'
+- './ee/spec/lib/gitlab/insights/reducers/dora_reducer_spec.rb'
+- './ee/spec/lib/gitlab/insights/reducers/label_count_per_period_reducer_spec.rb'
+- './ee/spec/lib/gitlab/insights/serializers/chartjs/bar_serializer_spec.rb'
+- './ee/spec/lib/gitlab/insights/serializers/chartjs/bar_time_series_serializer_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/lib/gitlab/insights/validators/params_validator_spec.rb'
+- './ee/spec/lib/gitlab/instrumentation/elasticsearch_transport_spec.rb'
+- './ee/spec/lib/gitlab/instrumentation_helper_spec.rb'
+- './ee/spec/lib/gitlab/ip_address_state_spec.rb'
+- './ee/spec/lib/gitlab/items_collection_spec.rb'
+- './ee/spec/lib/gitlab/kerberos/authentication_spec.rb'
+- './ee/spec/lib/gitlab/legacy_github_import/project_creator_spec.rb'
+- './ee/spec/lib/gitlab/licenses/submit_license_usage_data_banner_spec.rb'
+- './ee/spec/lib/gitlab/manual_quarterly_co_term_banner_spec.rb'
+- './ee/spec/lib/gitlab/metrics/samplers/global_search_sampler_spec.rb'
+- './ee/spec/lib/gitlab/middleware/ip_restrictor_spec.rb'
+- './ee/spec/lib/gitlab/mirror_spec.rb'
+- './ee/spec/lib/gitlab/object_hierarchy_spec.rb'
+- './ee/spec/lib/gitlab/pagination_delegate_spec.rb'
+- './ee/spec/lib/gitlab/pagination/keyset/simple_order_builder_spec.rb'
+- './ee/spec/lib/gitlab/patch/database_config_spec.rb'
+- './ee/spec/lib/gitlab/patch/draw_route_spec.rb'
+- './ee/spec/lib/gitlab/patch/geo_database_tasks_spec.rb'
+- './ee/spec/lib/gitlab/path_locks_finder_spec.rb'
+- './ee/spec/lib/gitlab/project_template_spec.rb'
+- './ee/spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb'
+- './ee/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb'
+- './ee/spec/lib/gitlab/prometheus/queries/cluster_query_spec.rb'
+- './ee/spec/lib/gitlab/proxy_spec.rb'
+- './ee/spec/lib/gitlab/quick_actions/users_extractor_spec.rb'
+- './ee/spec/lib/gitlab/rack_attack_spec.rb'
+- './ee/spec/lib/gitlab/reference_extractor_spec.rb'
+- './ee/spec/lib/gitlab/regex_spec.rb'
+- './ee/spec/lib/gitlab/return_to_location_spec.rb'
+- './ee/spec/lib/gitlab/search/aggregation_parser_spec.rb'
+- './ee/spec/lib/gitlab/search/aggregation_spec.rb'
+- './ee/spec/lib/gitlab/search_context/builder_spec.rb'
+- './ee/spec/lib/gitlab/search/recent_epics_spec.rb'
+- './ee/spec/lib/gitlab/sidekiq_config_spec.rb'
+- './ee/spec/lib/gitlab/sitemaps/generator_spec.rb'
+- './ee/spec/lib/gitlab/sitemaps/sitemap_file_spec.rb'
+- './ee/spec/lib/gitlab/sitemaps/url_extractor_spec.rb'
+- './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'
+- './ee/spec/lib/gitlab_subscriptions/upcoming_reconciliation_entity_spec.rb'
+- './ee/spec/lib/gitlab/template/custom_templates_spec.rb'
+- './ee/spec/lib/gitlab/tracking/snowplow_schema_validation_spec.rb'
+- './ee/spec/lib/gitlab/tracking/standard_context_spec.rb'
+- './ee/spec/lib/gitlab/tree_summary_spec.rb'
+- './ee/spec/lib/gitlab/usage_data_counters/epic_activity_unique_counter_spec.rb'
+- './ee/spec/lib/gitlab/usage_data_counters/licenses_list_spec.rb'
+- './ee/spec/lib/gitlab/usage_data_metrics_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/aggregates/aggregate_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/advanced_search/build_type_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/advanced_search/distribution_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/advanced_search/lucene_version_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/advanced_search/version_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/approval_project_rules_with_user_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/count_ci_builds_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/count_ci_environments_approval_required_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/count_deployment_approvals_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/count_distinct_merged_merge_requests_using_approval_rules_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/count_event_streaming_destinations_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/count_external_status_checks_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/count_groups_with_event_streaming_destinations_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/count_projects_with_external_status_checks_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/count_saml_group_links_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/count_slack_app_installations_gbp_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/count_slack_app_installations_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/count_users_associating_group_milestones_to_releases_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/count_users_creating_ci_builds_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/count_users_deployment_approvals_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/historical_max_users_metrics_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/licensee_metrics_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/license_management_jobs_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/license_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/protected_environment_approval_rules_required_approvals_average_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/protected_environments_required_approvals_average_metric_spec.rb'
+- './ee/spec/lib/gitlab/usage/metrics/instrumentations/user_cap_setting_enabled_metric_spec.rb'
+- './ee/spec/lib/gitlab/user_access_spec.rb'
+- './ee/spec/lib/gitlab/visibility_level_spec.rb'
+- './ee/spec/lib/gitlab/vulnerabilities/base_vulnerability_spec.rb'
+- './ee/spec/lib/gitlab/vulnerabilities/container_scanning_vulnerability_spec.rb'
+- './ee/spec/lib/gitlab/vulnerabilities/findings_preloader_spec.rb'
+- './ee/spec/lib/gitlab/vulnerabilities/parser_spec.rb'
+- './ee/spec/lib/gitlab/vulnerabilities/standard_vulnerability_spec.rb'
+- './ee/spec/lib/gitlab/web_ide/config/entry/schema/match_spec.rb'
+- './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'
+- './ee/spec/lib/sidebars/groups/menus/administration_menu_spec.rb'
+- './ee/spec/lib/sidebars/groups/menus/analytics_menu_spec.rb'
+- './ee/spec/lib/sidebars/groups/menus/epics_menu_spec.rb'
+- './ee/spec/lib/sidebars/groups/menus/security_compliance_menu_spec.rb'
+- './ee/spec/lib/sidebars/groups/menus/trial_experiment_menu_spec.rb'
+- './ee/spec/lib/sidebars/groups/menus/wiki_menu_spec.rb'
+- './ee/spec/lib/sidebars/projects/menus/trial_experiment_menu_spec.rb'
+- './ee/spec/lib/system_check/app/search_check_spec.rb'
+- './ee/spec/lib/system_check/geo/authorized_keys_check_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/geo_database_configured_check_spec.rb'
+- './ee/spec/lib/system_check/geo/http_clone_enabled_check_spec.rb'
+- './ee/spec/lib/system_check/geo/http_connection_check_spec.rb'
+- './ee/spec/lib/system_check/geo/license_check_spec.rb'
+- './ee/spec/lib/system_check/rake_task/geo_task_spec.rb'
+- './ee/spec/lib/world_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/epics_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/migrations/20220411173544_cleanup_orphans_approval_project_rules_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/migrations/async_build_trace_expire_at_index_spec.rb'
+- './ee/spec/migrations/backfill_delayed_group_deletion_spec.rb'
+- './ee/spec/migrations/backfill_namespace_statistics_with_wiki_size_spec.rb'
+- './ee/spec/migrations/drop_invalid_remediations_spec.rb'
+- './ee/spec/migrations/geo/fix_state_column_in_file_registry_spec.rb'
+- './ee/spec/migrations/geo/fix_state_column_in_lfs_object_registry_spec.rb'
+- './ee/spec/migrations/geo/migrate_ci_job_artifacts_to_separate_registry_spec.rb'
+- './ee/spec/migrations/geo/migrate_job_artifact_registry_spec.rb'
+- './ee/spec/migrations/geo/migrate_lfs_objects_to_separate_registry_spec.rb'
+- './ee/spec/migrations/geo/set_resync_flag_for_retried_projects_spec.rb'
+- './ee/spec/migrations/remove_schedule_and_status_null_constraints_from_pending_escalations_alert_spec.rb'
+- './ee/spec/migrations/schedule_delete_invalid_epic_issues_revised_spec.rb'
+- './ee/spec/migrations/schedule_populate_test_reports_issue_id_spec.rb'
+- './ee/spec/migrations/schedule_requirements_migration_spec.rb'
+- './ee/spec/migrations/schedule_trace_expiry_removal_spec.rb'
+- './ee/spec/migrations/update_gitlab_subscriptions_start_at_post_eoa_spec.rb'
+- './ee/spec/migrations/update_vulnerability_occurrences_location_spec.rb'
+- './ee/spec/models/alert_management/alert_payload_field_spec.rb'
+- './ee/spec/models/allowed_email_domain_spec.rb'
+- './ee/spec/models/analytics/cycle_analytics/aggregation_context_spec.rb'
+- './ee/spec/models/analytics/cycle_analytics/group_level_spec.rb'
+- './ee/spec/models/analytics/cycle_analytics/group_stage_spec.rb'
+- './ee/spec/models/analytics/cycle_analytics/group_value_stream_spec.rb'
+- './ee/spec/models/analytics/cycle_analytics/project_stage_spec.rb'
+- './ee/spec/models/analytics/cycle_analytics/runtime_limiter_spec.rb'
+- './ee/spec/models/analytics/devops_adoption/enabled_namespace_spec.rb'
+- './ee/spec/models/analytics/devops_adoption/snapshot_spec.rb'
+- './ee/spec/models/analytics/issues_analytics_spec.rb'
+- './ee/spec/models/analytics/language_trend/repository_language_spec.rb'
+- './ee/spec/models/application_setting_spec.rb'
+- './ee/spec/models/approvable_spec.rb'
+- './ee/spec/models/approval_merge_request_rule_spec.rb'
+- './ee/spec/models/approval_project_rule_spec.rb'
+- './ee/spec/models/approvals/scan_finding_wrapped_rule_set_spec.rb'
+- './ee/spec/models/approval_state_spec.rb'
+- './ee/spec/models/approvals/wrapped_rule_set_spec.rb'
+- './ee/spec/models/approval_wrapped_any_approver_rule_spec.rb'
+- './ee/spec/models/approval_wrapped_code_owner_rule_spec.rb'
+- './ee/spec/models/approval_wrapped_rule_spec.rb'
+- './ee/spec/models/approver_group_spec.rb'
+- './ee/spec/models/app_sec/fuzzing/api/ci_configuration_spec.rb'
+- './ee/spec/models/app_sec/fuzzing/coverage/corpus_spec.rb'
+- './ee/spec/models/audit_events/external_audit_event_destination_spec.rb'
+- './ee/spec/models/audit_events/streaming/header_spec.rb'
+- './ee/spec/models/board_assignee_spec.rb'
+- './ee/spec/models/board_label_spec.rb'
+- './ee/spec/models/boards/epic_board_label_spec.rb'
+- './ee/spec/models/boards/epic_board_position_spec.rb'
+- './ee/spec/models/boards/epic_board_recent_visit_spec.rb'
+- './ee/spec/models/boards/epic_board_spec.rb'
+- './ee/spec/models/boards/epic_list_spec.rb'
+- './ee/spec/models/boards/epic_list_user_preference_spec.rb'
+- './ee/spec/models/boards/epic_user_preference_spec.rb'
+- './ee/spec/models/board_spec.rb'
+- './ee/spec/models/board_user_preference_spec.rb'
+- './ee/spec/models/broadcast_message_spec.rb'
+- './ee/spec/models/burndown_spec.rb'
+- './ee/spec/models/ci/bridge_spec.rb'
+- './ee/spec/models/ci/build_spec.rb'
+- './ee/spec/models/ci/daily_build_group_report_result_spec.rb'
+- './ee/spec/models/ci/minutes/additional_pack_spec.rb'
+- './ee/spec/models/ci/minutes/context_spec.rb'
+- './ee/spec/models/ci/minutes/limit_spec.rb'
+- './ee/spec/models/ci/minutes/namespace_monthly_usage_spec.rb'
+- './ee/spec/models/ci/minutes/notification_spec.rb'
+- './ee/spec/models/ci/minutes/project_monthly_usage_spec.rb'
+- './ee/spec/models/ci/minutes/usage_spec.rb'
+- './ee/spec/models/ci/pipeline_spec.rb'
+- './ee/spec/models/ci/processable_spec.rb'
+- './ee/spec/models/ci/sources/project_spec.rb'
+- './ee/spec/models/ci/subscriptions/project_spec.rb'
+- './ee/spec/models/commit_spec.rb'
+- './ee/spec/models/compliance_management/compliance_framework/project_settings_spec.rb'
+- './ee/spec/models/compliance_management/framework_spec.rb'
+- './ee/spec/models/concerns/approval_rule_like_spec.rb'
+- './ee/spec/models/concerns/approver_migrate_hook_spec.rb'
+- './ee/spec/models/concerns/auditable_spec.rb'
+- './ee/spec/models/concerns/deprecated_approvals_before_merge_spec.rb'
+- './ee/spec/models/concerns/ee/clusters/agents/authorization_config_scopes_spec.rb'
+- './ee/spec/models/concerns/ee/issuable_spec.rb'
+- './ee/spec/models/concerns/ee/mentionable_spec.rb'
+- './ee/spec/models/concerns/ee/milestoneable_spec.rb'
+- './ee/spec/models/concerns/ee/noteable_spec.rb'
+- './ee/spec/models/concerns/ee/participable_spec.rb'
+- './ee/spec/models/concerns/ee/project_security_scanners_information_spec.rb'
+- './ee/spec/models/concerns/ee/weight_eventable_spec.rb'
+- './ee/spec/models/concerns/elastic/application_versioned_search_spec.rb'
+- './ee/spec/models/concerns/elastic/issue_spec.rb'
+- './ee/spec/models/concerns/elastic/merge_request_spec.rb'
+- './ee/spec/models/concerns/elastic/milestone_spec.rb'
+- './ee/spec/models/concerns/elastic/note_spec.rb'
+- './ee/spec/models/concerns/elastic/project_spec.rb'
+- './ee/spec/models/concerns/elastic/projects_search_spec.rb'
+- './ee/spec/models/concerns/elastic/project_wiki_spec.rb'
+- './ee/spec/models/concerns/elastic/repository_spec.rb'
+- './ee/spec/models/concerns/elastic/snippet_spec.rb'
+- './ee/spec/models/concerns/epic_tree_sorting_spec.rb'
+- './ee/spec/models/concerns/geo/eventable_spec.rb'
+- './ee/spec/models/concerns/geo/has_replicator_spec.rb'
+- './ee/spec/models/concerns/geo/replicable_model_spec.rb'
+- './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'
+- './ee/spec/models/container_repository_spec.rb'
+- './ee/spec/models/dast/branch_spec.rb'
+- './ee/spec/models/dast/profile_schedule_spec.rb'
+- './ee/spec/models/dast/profile_spec.rb'
+- './ee/spec/models/dast/profiles_pipeline_spec.rb'
+- './ee/spec/models/dast/scanner_profiles_build_spec.rb'
+- './ee/spec/models/dast_scanner_profile_spec.rb'
+- './ee/spec/models/dast/site_profiles_build_spec.rb'
+- './ee/spec/models/dast/site_profile_secret_variable_spec.rb'
+- './ee/spec/models/dast_site_profile_spec.rb'
+- './ee/spec/models/dast_site_spec.rb'
+- './ee/spec/models/dast_site_token_spec.rb'
+- './ee/spec/models/dast_site_validation_spec.rb'
+- './ee/spec/models/deployments/approval_spec.rb'
+- './ee/spec/models/deployment_spec.rb'
+- './ee/spec/models/dora/base_metric_spec.rb'
+- './ee/spec/models/dora/change_failure_rate_metric_spec.rb'
+- './ee/spec/models/dora/daily_metrics_spec.rb'
+- './ee/spec/models/dora/deployment_frequency_metric_spec.rb'
+- './ee/spec/models/dora/lead_time_for_changes_metric_spec.rb'
+- './ee/spec/models/dora/time_to_restore_service_metric_spec.rb'
+- './ee/spec/models/ee/alert_management/alert_spec.rb'
+- './ee/spec/models/ee/analytics/cycle_analytics/stage_event_hash_spec.rb'
+- './ee/spec/models/ee/analytics/usage_trends/measurement_spec.rb'
+- './ee/spec/models/ee/appearance_spec.rb'
+- './ee/spec/models/ee/audit_event_spec.rb'
+- './ee/spec/models/ee/award_emoji_spec.rb'
+- './ee/spec/models/ee/ci/build_dependencies_spec.rb'
+- './ee/spec/models/ee/ci/job_artifact_spec.rb'
+- './ee/spec/models/ee/ci/pending_build_spec.rb'
+- './ee/spec/models/ee/ci/pipeline_artifact_spec.rb'
+- './ee/spec/models/ee/ci/runner_spec.rb'
+- './ee/spec/models/ee/ci/secure_file_spec.rb'
+- './ee/spec/models/ee/clusters/agent_spec.rb'
+- './ee/spec/models/ee/description_version_spec.rb'
+- './ee/spec/models/ee/event_collection_spec.rb'
+- './ee/spec/models/ee/event_spec.rb'
+- './ee/spec/models/ee/gpg_key_spec.rb'
+- './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'
+- './ee/spec/models/ee/iteration_spec.rb'
+- './ee/spec/models/ee/key_spec.rb'
+- './ee/spec/models/ee/label_spec.rb'
+- './ee/spec/models/ee/lfs_object_spec.rb'
+- './ee/spec/models/ee/list_spec.rb'
+- './ee/spec/models/ee/members_preloader_spec.rb'
+- './ee/spec/models/ee/merge_request_diff_spec.rb'
+- './ee/spec/models/ee/merge_request/metrics_spec.rb'
+- './ee/spec/models/ee/namespace_ci_cd_setting_spec.rb'
+- './ee/spec/models/ee/namespace/root_storage_statistics_spec.rb'
+- './ee/spec/models/ee/namespaces/namespace_ban_spec.rb'
+- './ee/spec/models/ee/namespace_spec.rb'
+- './ee/spec/models/ee/namespace_statistics_spec.rb'
+- './ee/spec/models/ee/namespace/storage/notification_spec.rb'
+- './ee/spec/models/ee/notification_setting_spec.rb'
+- './ee/spec/models/ee/pages_deployment_spec.rb'
+- './ee/spec/models/ee/personal_access_token_spec.rb'
+- './ee/spec/models/ee/preloaders/group_policy_preloader_spec.rb'
+- './ee/spec/models/ee/project_authorization_spec.rb'
+- './ee/spec/models/ee/project_group_link_spec.rb'
+- './ee/spec/models/ee/project_setting_spec.rb'
+- './ee/spec/models/ee/project_wiki_spec.rb'
+- './ee/spec/models/ee/protected_branch_spec.rb'
+- './ee/spec/models/ee/protected_ref_access_spec.rb'
+- './ee/spec/models/ee/protected_ref_spec.rb'
+- './ee/spec/models/ee/release_spec.rb'
+- './ee/spec/models/ee/resource_label_event_spec.rb'
+- './ee/spec/models/ee/resource_state_event_spec.rb'
+- './ee/spec/models/ee/service_desk_setting_spec.rb'
+- './ee/spec/models/ee/system_note_metadata_spec.rb'
+- './ee/spec/models/ee/terraform/state_version_spec.rb'
+- './ee/spec/models/ee/user_highest_role_spec.rb'
+- './ee/spec/models/ee/users/merge_request_interaction_spec.rb'
+- './ee/spec/models/ee/user_spec.rb'
+- './ee/spec/models/ee/users_statistics_spec.rb'
+- './ee/spec/models/ee/vulnerability_spec.rb'
+- './ee/spec/models/ee/work_items/type_spec.rb'
+- './ee/spec/models/elastic/index_setting_spec.rb'
+- './ee/spec/models/elastic/migration_record_spec.rb'
+- './ee/spec/models/elastic/reindexing_slice_spec.rb'
+- './ee/spec/models/elastic/reindexing_subtask_spec.rb'
+- './ee/spec/models/elastic/reindexing_task_spec.rb'
+- './ee/spec/models/elasticsearch_indexed_namespace_spec.rb'
+- './ee/spec/models/elasticsearch_indexed_project_spec.rb'
+- './ee/spec/models/environment_spec.rb'
+- './ee/spec/models/epic_issue_spec.rb'
+- './ee/spec/models/epic/related_epic_link_spec.rb'
+- './ee/spec/models/epic_spec.rb'
+- './ee/spec/models/epic_user_mention_spec.rb'
+- './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'
+- './ee/spec/models/geo/event_log_state_spec.rb'
+- './ee/spec/models/geo/every_geo_event_spec.rb'
+- './ee/spec/models/geo/group_wiki_repository_registry_spec.rb'
+- './ee/spec/models/geo/hashed_storage_migrated_event_spec.rb'
+- './ee/spec/models/geo/job_artifact_registry_spec.rb'
+- './ee/spec/models/geo/lfs_object_registry_spec.rb'
+- './ee/spec/models/geo/merge_request_diff_registry_spec.rb'
+- './ee/spec/models/geo_node_namespace_link_spec.rb'
+- './ee/spec/models/geo_node_spec.rb'
+- './ee/spec/models/geo_node_status_spec.rb'
+- './ee/spec/models/geo/package_file_registry_spec.rb'
+- './ee/spec/models/geo/pages_deployment_registry_spec.rb'
+- './ee/spec/models/geo/pipeline_artifact_registry_spec.rb'
+- './ee/spec/models/geo/project_registry_spec.rb'
+- './ee/spec/models/geo/push_user_spec.rb'
+- './ee/spec/models/geo/repositories_changed_event_spec.rb'
+- './ee/spec/models/geo/repository_created_event_spec.rb'
+- './ee/spec/models/geo/repository_renamed_event_spec.rb'
+- './ee/spec/models/geo/repository_updated_event_spec.rb'
+- './ee/spec/models/geo/reset_checksum_event_spec.rb'
+- './ee/spec/models/geo/secondary_usage_data_spec.rb'
+- './ee/spec/models/geo/snippet_repository_registry_spec.rb'
+- './ee/spec/models/geo/terraform_state_version_registry_spec.rb'
+- './ee/spec/models/geo/tracking_base_spec.rb'
+- './ee/spec/models/geo/upload_registry_spec.rb'
+- './ee/spec/models/geo/upload_state_spec.rb'
+- './ee/spec/models/gitlab/seat_link_data_spec.rb'
+- './ee/spec/models/gitlab_subscription_history_spec.rb'
+- './ee/spec/models/gitlab_subscriptions/features_spec.rb'
+- './ee/spec/models/gitlab_subscription_spec.rb'
+- './ee/spec/models/gitlab_subscriptions/upcoming_reconciliation_spec.rb'
+- './ee/spec/models/group_deletion_schedule_spec.rb'
+- './ee/spec/models/group_member_spec.rb'
+- './ee/spec/models/group_merge_request_approval_setting_spec.rb'
+- './ee/spec/models/groups/repository_storage_move_spec.rb'
+- './ee/spec/models/group_wiki_repository_spec.rb'
+- './ee/spec/models/group_wiki_spec.rb'
+- './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'
+- './ee/spec/models/integrations/github_spec.rb'
+- './ee/spec/models/integrations/github/status_message_spec.rb'
+- './ee/spec/models/integrations/github/status_notifier_spec.rb'
+- './ee/spec/models/integrations/gitlab_slack_application_spec.rb'
+- './ee/spec/models/ip_restriction_spec.rb'
+- './ee/spec/models/issuable_metric_image_spec.rb'
+- './ee/spec/models/issuables_analytics_spec.rb'
+- './ee/spec/models/issuable_sla_spec.rb'
+- './ee/spec/models/issue_link_spec.rb'
+- './ee/spec/models/issue_spec.rb'
+- './ee/spec/models/iteration_note_spec.rb'
+- './ee/spec/models/label_note_spec.rb'
+- './ee/spec/models/ldap_group_link_spec.rb'
+- './ee/spec/models/license_spec.rb'
+- './ee/spec/models/member_spec.rb'
+- './ee/spec/models/merge_request/blocking_spec.rb'
+- './ee/spec/models/merge_request_block_spec.rb'
+- './ee/spec/models/merge_requests/compliance_violation_spec.rb'
+- './ee/spec/models/merge_requests/external_status_check_spec.rb'
+- './ee/spec/models/merge_request_spec.rb'
+- './ee/spec/models/merge_requests/status_check_response_spec.rb'
+- './ee/spec/models/merge_train_spec.rb'
+- './ee/spec/models/milestone_release_spec.rb'
+- './ee/spec/models/milestone_spec.rb'
+- './ee/spec/models/namespace_limit_spec.rb'
+- './ee/spec/models/namespace_setting_spec.rb'
+- './ee/spec/models/namespaces/free_user_cap/preview_spec.rb'
+- './ee/spec/models/namespaces/free_user_cap_spec.rb'
+- './ee/spec/models/namespaces/free_user_cap/standard_spec.rb'
+- './ee/spec/models/namespaces/storage/root_excess_size_spec.rb'
+- './ee/spec/models/namespaces/storage/root_size_spec.rb'
+- './ee/spec/models/note_spec.rb'
+- './ee/spec/models/packages/package_file_spec.rb'
+- './ee/spec/models/path_lock_spec.rb'
+- './ee/spec/models/plan_spec.rb'
+- './ee/spec/models/preloaders/environments/protected_environment_preloader_spec.rb'
+- './ee/spec/models/productivity_analytics_spec.rb'
+- './ee/spec/models/project_alias_spec.rb'
+- './ee/spec/models/project_ci_cd_setting_spec.rb'
+- './ee/spec/models/project_feature_spec.rb'
+- './ee/spec/models/project_import_data_spec.rb'
+- './ee/spec/models/project_import_state_spec.rb'
+- './ee/spec/models/project_member_spec.rb'
+- './ee/spec/models/project_repository_state_spec.rb'
+- './ee/spec/models/project_security_setting_spec.rb'
+- './ee/spec/models/project_spec.rb'
+- './ee/spec/models/project_team_spec.rb'
+- './ee/spec/models/protected_branch/required_code_owners_section_spec.rb'
+- './ee/spec/models/protected_branch/unprotect_access_level_spec.rb'
+- './ee/spec/models/protected_environment/deploy_access_level_spec.rb'
+- './ee/spec/models/protected_environments/approval_rule_spec.rb'
+- './ee/spec/models/protected_environments/approval_summary_spec.rb'
+- './ee/spec/models/protected_environment_spec.rb'
+- './ee/spec/models/push_rule_spec.rb'
+- './ee/spec/models/release_highlight_spec.rb'
+- './ee/spec/models/remote_mirror_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/resource_iteration_event_spec.rb'
+- './ee/spec/models/resource_weight_event_spec.rb'
+- './ee/spec/models/saml_group_link_spec.rb'
+- './ee/spec/models/saml_provider_spec.rb'
+- './ee/spec/models/sbom/component_spec.rb'
+- './ee/spec/models/sbom/component_version_spec.rb'
+- './ee/spec/models/sbom/occurrence_spec.rb'
+- './ee/spec/models/sbom/source_spec.rb'
+- './ee/spec/models/sca/license_compliance_spec.rb'
+- './ee/spec/models/sca/license_policy_spec.rb'
+- './ee/spec/models/scim_identity_spec.rb'
+- './ee/spec/models/scim_oauth_access_token_spec.rb'
+- './ee/spec/models/scoped_label_set_spec.rb'
+- './ee/spec/models/security/finding_spec.rb'
+- './ee/spec/models/security/orchestration_policy_configuration_spec.rb'
+- './ee/spec/models/security/orchestration_policy_rule_schedule_spec.rb'
+- './ee/spec/models/security/scan_spec.rb'
+- './ee/spec/models/security/training_provider_spec.rb'
+- './ee/spec/models/security/training_spec.rb'
+- './ee/spec/models/slack_integration_spec.rb'
+- './ee/spec/models/snippet_repository_spec.rb'
+- './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'
+- './ee/spec/models/user_detail_spec.rb'
+- './ee/spec/models/user_permission_export_upload_spec.rb'
+- './ee/spec/models/user_preference_spec.rb'
+- './ee/spec/models/users_security_dashboard_project_spec.rb'
+- './ee/spec/models/visible_approvable_spec.rb'
+- './ee/spec/models/vulnerabilities/export_spec.rb'
+- './ee/spec/models/vulnerabilities/external_issue_link_spec.rb'
+- './ee/spec/models/vulnerabilities/feedback_spec.rb'
+- './ee/spec/models/vulnerabilities/finding/evidence_spec.rb'
+- './ee/spec/models/vulnerabilities/finding_identifier_spec.rb'
+- './ee/spec/models/vulnerabilities/finding_link_spec.rb'
+- './ee/spec/models/vulnerabilities/finding_pipeline_spec.rb'
+- './ee/spec/models/vulnerabilities/finding_remediation_spec.rb'
+- './ee/spec/models/vulnerabilities/finding_signature_spec.rb'
+- './ee/spec/models/vulnerabilities/finding_spec.rb'
+- './ee/spec/models/vulnerabilities/flag_spec.rb'
+- './ee/spec/models/vulnerabilities/historical_statistic_spec.rb'
+- './ee/spec/models/vulnerabilities/identifier_spec.rb'
+- './ee/spec/models/vulnerabilities/issue_link_spec.rb'
+- './ee/spec/models/vulnerabilities/merge_request_link_spec.rb'
+- './ee/spec/models/vulnerabilities/projects_grade_spec.rb'
+- './ee/spec/models/vulnerabilities/read_spec.rb'
+- './ee/spec/models/vulnerabilities/remediation_spec.rb'
+- './ee/spec/models/vulnerabilities/scanner_spec.rb'
+- './ee/spec/models/vulnerabilities/stat_diff_spec.rb'
+- './ee/spec/models/vulnerabilities/state_transition_spec.rb'
+- './ee/spec/models/vulnerabilities/statistic_spec.rb'
+- './ee/spec/models/vulnerability_user_mention_spec.rb'
+- './ee/spec/models/weight_note_spec.rb'
+- './ee/spec/models/work_item_spec.rb'
+- './ee/spec/models/work_items/widgets/verification_status_spec.rb'
+- './ee/spec/policies/approval_merge_request_rule_policy_spec.rb'
+- './ee/spec/policies/approval_project_rule_policy_spec.rb'
+- './ee/spec/policies/approval_state_policy_spec.rb'
+- './ee/spec/policies/app_sec/fuzzing/coverage/corpus_policy_spec.rb'
+- './ee/spec/policies/award_emoji_policy_spec.rb'
+- './ee/spec/policies/base_policy_spec.rb'
+- './ee/spec/policies/ci/build_policy_spec.rb'
+- './ee/spec/policies/ci/minutes/namespace_monthly_usage_policy_spec.rb'
+- './ee/spec/policies/clusters/instance_policy_spec.rb'
+- './ee/spec/policies/compliance_management/framework_policy_spec.rb'
+- './ee/spec/policies/dast/branch_policy_spec.rb'
+- './ee/spec/policies/dast/profile_policy_spec.rb'
+- './ee/spec/policies/dast/profile_schedule_policy_spec.rb'
+- './ee/spec/policies/dast_scanner_profile_policy_spec.rb'
+- './ee/spec/policies/dast_site_profile_policy_spec.rb'
+- './ee/spec/policies/dast_site_validation_policy_spec.rb'
+- './ee/spec/policies/ee/ci/runner_policy_spec.rb'
+- './ee/spec/policies/ee/namespaces/user_namespace_policy_spec.rb'
+- './ee/spec/policies/ee/readonly_abilities_spec.rb'
+- './ee/spec/policies/environment_policy_spec.rb'
+- './ee/spec/policies/epic_policy_spec.rb'
+- './ee/spec/policies/event_policy_spec.rb'
+- './ee/spec/policies/geo_node_policy_spec.rb'
+- './ee/spec/policies/geo/registry_policy_spec.rb'
+- './ee/spec/policies/global_policy_spec.rb'
+- './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'
+- './ee/spec/policies/merge_request_policy_spec.rb'
+- './ee/spec/policies/note_policy_spec.rb'
+- './ee/spec/policies/path_lock_policy_spec.rb'
+- './ee/spec/policies/project_policy_spec.rb'
+- './ee/spec/policies/project_snippet_policy_spec.rb'
+- './ee/spec/policies/protected_branch_policy_spec.rb'
+- './ee/spec/policies/requirements_management/requirement_policy_spec.rb'
+- './ee/spec/policies/saml_provider_policy_spec.rb'
+- './ee/spec/policies/security/scan_policy_spec.rb'
+- './ee/spec/policies/user_policy_spec.rb'
+- './ee/spec/policies/vulnerabilities/export_policy_spec.rb'
+- './ee/spec/policies/vulnerabilities/external_issue_link_policy_spec.rb'
+- './ee/spec/policies/vulnerabilities/feedback_policy_spec.rb'
+- './ee/spec/policies/vulnerabilities/issue_link_policy_spec.rb'
+- './ee/spec/policies/vulnerabilities/scanner_policy_spec.rb'
+- './ee/spec/policies/vulnerability_policy_spec.rb'
+- './ee/spec/presenters/analytics/cycle_analytics/stage_presenter_spec.rb'
+- './ee/spec/presenters/approval_rule_presenter_spec.rb'
+- './ee/spec/presenters/audit_event_presenter_spec.rb'
+- './ee/spec/presenters/ci/build_presenter_spec.rb'
+- './ee/spec/presenters/ci/build_runner_presenter_spec.rb'
+- './ee/spec/presenters/ci/minutes/usage_presenter_spec.rb'
+- './ee/spec/presenters/ci/pipeline_presenter_spec.rb'
+- './ee/spec/presenters/dast/site_profile_presenter_spec.rb'
+- './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/group_member_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'
+- './ee/spec/presenters/project_member_presenter_spec.rb'
+- './ee/spec/presenters/security/scan_presenter_spec.rb'
+- './ee/spec/presenters/subscription_presenter_spec.rb'
+- './ee/spec/presenters/subscriptions/new_plan_presenter_spec.rb'
+- './ee/spec/presenters/vulnerabilities/finding_presenter_spec.rb'
+- './ee/spec/presenters/vulnerability_presenter_spec.rb'
+- './ee/spec/presenters/web_hooks/group/hook_presenter_spec.rb'
+- './ee/spec/replicators/geo/ci_secure_file_replicator_spec.rb'
+- './ee/spec/replicators/geo/group_wiki_repository_replicator_spec.rb'
+- './ee/spec/replicators/geo/job_artifact_replicator_spec.rb'
+- './ee/spec/replicators/geo/lfs_object_replicator_spec.rb'
+- './ee/spec/replicators/geo/merge_request_diff_replicator_spec.rb'
+- './ee/spec/replicators/geo/package_file_replicator_spec.rb'
+- './ee/spec/replicators/geo/pages_deployment_replicator_spec.rb'
+- './ee/spec/replicators/geo/pipeline_artifact_replicator_spec.rb'
+- './ee/spec/replicators/geo/pipeline_replicator_spec.rb'
+- './ee/spec/replicators/geo/snippet_repository_replicator_spec.rb'
+- './ee/spec/replicators/geo/terraform_state_version_replicator_spec.rb'
+- './ee/spec/replicators/geo/upload_replicator_spec.rb'
+- './ee/spec/requests/admin/audit_events_spec.rb'
+- './ee/spec/requests/admin/credentials_controller_spec.rb'
+- './ee/spec/requests/admin/geo/nodes_controller_spec.rb'
+- './ee/spec/requests/admin/geo/replicables_controller_spec.rb'
+- './ee/spec/requests/admin/subscriptions_controller_spec.rb'
+- './ee/spec/requests/admin/user_permission_exports_controller_spec.rb'
+- './ee/spec/requests/admin/users_controller_spec.rb'
+- './ee/spec/requests/api/analytics/code_review_analytics_spec.rb'
+- './ee/spec/requests/api/analytics/group_activity_analytics_spec.rb'
+- './ee/spec/requests/api/analytics/project_deployment_frequency_spec.rb'
+- './ee/spec/requests/api/api_spec.rb'
+- './ee/spec/requests/api/audit_events_spec.rb'
+- './ee/spec/requests/api/award_emoji_spec.rb'
+- './ee/spec/requests/api/boards_spec.rb'
+- './ee/spec/requests/api/branches_spec.rb'
+- './ee/spec/requests/api/captcha_check_spec.rb'
+- './ee/spec/requests/api/ci/jobs_spec.rb'
+- './ee/spec/requests/api/ci/minutes_spec.rb'
+- './ee/spec/requests/api/ci/pipelines_spec.rb'
+- './ee/spec/requests/api/ci/runner/jobs_put_spec.rb'
+- './ee/spec/requests/api/ci/runner/jobs_trace_spec.rb'
+- './ee/spec/requests/api/ci/runner_spec.rb'
+- './ee/spec/requests/api/ci/triggers_spec.rb'
+- './ee/spec/requests/api/ci/variables_spec.rb'
+- './ee/spec/requests/api/commits_spec.rb'
+- './ee/spec/requests/api/dependencies_spec.rb'
+- './ee/spec/requests/api/deployments_spec.rb'
+- './ee/spec/requests/api/discussions_spec.rb'
+- './ee/spec/requests/api/dora/metrics_spec.rb'
+- './ee/spec/requests/api/elasticsearch_indexed_namespaces_spec.rb'
+- './ee/spec/requests/api/epic_issues_spec.rb'
+- './ee/spec/requests/api/epic_links_spec.rb'
+- './ee/spec/requests/api/epics_spec.rb'
+- './ee/spec/requests/api/experiments_spec.rb'
+- './ee/spec/requests/api/features_spec.rb'
+- './ee/spec/requests/api/files_spec.rb'
+- './ee/spec/requests/api/geo_nodes_spec.rb'
+- './ee/spec/requests/api/geo_replication_spec.rb'
+- './ee/spec/requests/api/geo_spec.rb'
+- './ee/spec/requests/api/graphql/analytics/devops_adoption/enabled_namespaces_spec.rb'
+- './ee/spec/requests/api/graphql/app_sec/fuzzing/api/ci_configuration_type_spec.rb'
+- './ee/spec/requests/api/graphql/app_sec/fuzzing/coverage/corpus_type_spec.rb'
+- './ee/spec/requests/api/graphql/audit_events/streaming/headers/create_spec.rb'
+- './ee/spec/requests/api/graphql/audit_events/streaming/headers/destroy_spec.rb'
+- './ee/spec/requests/api/graphql/audit_events/streaming/headers/update_spec.rb'
+- './ee/spec/requests/api/graphql/boards/board_list_query_spec.rb'
+- './ee/spec/requests/api/graphql/boards/board_lists_query_spec.rb'
+- './ee/spec/requests/api/graphql/boards/boards_query_spec.rb'
+- './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_list_query_spec.rb'
+- './ee/spec/requests/api/graphql/boards/epic_lists_query_spec.rb'
+- './ee/spec/requests/api/graphql/boards/epic_lists/update_spec.rb'
+- './ee/spec/requests/api/graphql/ci/minutes/usage_spec.rb'
+- './ee/spec/requests/api/graphql/ci/runner_spec.rb'
+- './ee/spec/requests/api/graphql/ci/runners_spec.rb'
+- './ee/spec/requests/api/graphql/compliance_management/merge_requests/compliance_violations_spec.rb'
+- './ee/spec/requests/api/graphql/current_user/groups_query_spec.rb'
+- './ee/spec/requests/api/graphql/current_user/todos_query_spec.rb'
+- './ee/spec/requests/api/graphql/dora/dora_spec.rb'
+- './ee/spec/requests/api/graphql/epics/epic_resolver_spec.rb'
+- './ee/spec/requests/api/graphql/geo/geo_node_spec.rb'
+- './ee/spec/requests/api/graphql/geo/registries_spec.rb'
+- './ee/spec/requests/api/graphql/group/ci_cd_settings_spec.rb'
+- './ee/spec/requests/api/graphql/group/dast_profile_schedule_spec.rb'
+- './ee/spec/requests/api/graphql/group/epic/epic_aggregate_query_spec.rb'
+- './ee/spec/requests/api/graphql/group/epic/epic_ancestors_spec.rb'
+- './ee/spec/requests/api/graphql/group/epic/epic_issues_spec.rb'
+- './ee/spec/requests/api/graphql/group/epic/notes_spec.rb'
+- './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'
+- './ee/spec/requests/api/graphql/iteration_spec.rb'
+- './ee/spec/requests/api/graphql/merge_request_reviewer_spec.rb'
+- './ee/spec/requests/api/graphql/merge_requests/approval_state_spec.rb'
+- './ee/spec/requests/api/graphql/milestone_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/analytics/devops_adoption/enabled_namespaces/bulk_enable_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/analytics/devops_adoption/enabled_namespaces/disable_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/analytics/devops_adoption/enabled_namespaces/enable_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/app_sec/fuzzing/api/ci_configuration/create_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/create_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/boards/epic_boards/create_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/boards/epic_boards/destroy_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/boards/epic_boards/epic_move_list_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/boards/epic_boards/update_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/boards/epic_lists/create_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/boards/epic_lists/destroy_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/boards/epics/create_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/boards/issues/issue_move_list_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/boards/lists/create_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/boards/lists/update_limit_metrics_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/boards/update_epic_user_preferences_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/ci/namespace_ci_cd_settings_update_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/compliance_management/frameworks/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_on_demand_scans/create_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/profiles/run_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/dast/profiles/update_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/dast_scanner_profiles/create_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/dast_scanner_profiles/delete_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/dast_scanner_profiles/update_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/dast_site_profiles/create_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/dast_site_profiles/delete_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/dast_site_profiles/update_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/environments/canary_ingress/update_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/epics/add_issue_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/epics/create_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/epics/set_subscription_spec.rb'
+- './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'
+- './ee/spec/requests/api/graphql/mutations/issues/set_escalation_policy_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/issues/set_weight_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/issues/update_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/merge_requests/set_assignees_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/notes/create/note_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/projects/lock_path_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/projects/set_compliance_framework_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/quality_management/test_cases/create_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/mutations/requirements_management/create_requirement_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/requirements_management/export_requirements_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/requirements_management/update_requirement_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/security_finding/create_issue_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/security_policy/assign_security_policy_project_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/security_policy/commit_scan_execution_policy_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/requests/api/graphql/mutations/timelogs/create_spec.rb'
+- './ee/spec/requests/api/graphql/mutations/users/abuse/namespace_bans/destroy_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/graphql/namespace/compliance_frameworks_spec.rb'
+- './ee/spec/requests/api/graphql/namespace/projects_spec.rb'
+- './ee/spec/requests/api/graphql/project/alert_management/http_integrations_spec.rb'
+- './ee/spec/requests/api/graphql/project/alert_management/integrations_spec.rb'
+- './ee/spec/requests/api/graphql/project/alert_management/payload_fields_spec.rb'
+- './ee/spec/requests/api/graphql/project/code_coverage_summary_spec.rb'
+- './ee/spec/requests/api/graphql/project/compliance_frameworks_spec.rb'
+- './ee/spec/requests/api/graphql/project/dast_profile_schedule_spec.rb'
+- './ee/spec/requests/api/graphql/project/dast_profile_spec.rb'
+- './ee/spec/requests/api/graphql/project/dast_profiles_spec.rb'
+- './ee/spec/requests/api/graphql/project/dast_scanner_profiles_spec.rb'
+- './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'
+- './ee/spec/requests/api/graphql/project/pipeline/code_quality_reports_spec.rb'
+- './ee/spec/requests/api/graphql/project/pipeline/dast_profile_spec.rb'
+- './ee/spec/requests/api/graphql/project/pipelines/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/push_rules_spec.rb'
+- './ee/spec/requests/api/graphql/project/requirements_management/requirement_counts_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/projects/compliance_frameworks_spec.rb'
+- './ee/spec/requests/api/graphql/project/security_orchestration/scan_result_policy_spec.rb'
+- './ee/spec/requests/api/graphql/project/vulnerability_severities_count_spec.rb'
+- './ee/spec/requests/api/graphql/project/work_items_spec.rb'
+- './ee/spec/requests/api/graphql/vulnerabilities/description_spec.rb'
+- './ee/spec/requests/api/graphql/vulnerabilities/details_spec.rb'
+- './ee/spec/requests/api/graphql/vulnerabilities/external_issue_links_spec.rb'
+- './ee/spec/requests/api/graphql/vulnerabilities/identifiers_spec.rb'
+- './ee/spec/requests/api/graphql/vulnerabilities/issue_links_spec.rb'
+- './ee/spec/requests/api/graphql/vulnerabilities/location_spec.rb'
+- './ee/spec/requests/api/graphql/vulnerabilities/primary_identifier_spec.rb'
+- './ee/spec/requests/api/graphql/vulnerabilities/scanner_spec.rb'
+- './ee/spec/requests/api/graphql/vulnerabilities/sort_spec.rb'
+- './ee/spec/requests/api/graphql/work_item_spec.rb'
+- './ee/spec/requests/api/group_boards_spec.rb'
+- './ee/spec/requests/api/group_clusters_spec.rb'
+- './ee/spec/requests/api/group_hooks_spec.rb'
+- './ee/spec/requests/api/group_milestones_spec.rb'
+- './ee/spec/requests/api/group_push_rule_spec.rb'
+- './ee/spec/requests/api/group_repository_storage_moves_spec.rb'
+- './ee/spec/requests/api/groups_spec.rb'
+- './ee/spec/requests/api/group_variables_spec.rb'
+- './ee/spec/requests/api/integrations_spec.rb'
+- './ee/spec/requests/api/internal/app_sec/dast/site_validations_spec.rb'
+- './ee/spec/requests/api/internal/base_spec.rb'
+- './ee/spec/requests/api/internal/kubernetes_spec.rb'
+- './ee/spec/requests/api/internal/upcoming_reconciliations_spec.rb'
+- './ee/spec/requests/api/invitations_spec.rb'
+- './ee/spec/requests/api/issue_links_spec.rb'
+- './ee/spec/requests/api/issues_spec.rb'
+- './ee/spec/requests/api/iterations_spec.rb'
+- './ee/spec/requests/api/ldap_group_links_spec.rb'
+- './ee/spec/requests/api/ldap_spec.rb'
+- './ee/spec/requests/api/license_spec.rb'
+- './ee/spec/requests/api/managed_licenses_spec.rb'
+- './ee/spec/requests/api/markdown_golden_master_spec.rb'
+- './ee/spec/requests/api/members_spec.rb'
+- './ee/spec/requests/api/merge_request_approval_rules_spec.rb'
+- './ee/spec/requests/api/merge_request_approval_settings_spec.rb'
+- './ee/spec/requests/api/merge_request_approvals_spec.rb'
+- './ee/spec/requests/api/merge_requests_spec.rb'
+- './ee/spec/requests/api/merge_trains_spec.rb'
+- './ee/spec/requests/api/namespaces_spec.rb'
+- './ee/spec/requests/api/notes_spec.rb'
+- './ee/spec/requests/api/project_aliases_spec.rb'
+- './ee/spec/requests/api/project_approval_rules_spec.rb'
+- './ee/spec/requests/api/project_approval_settings_spec.rb'
+- './ee/spec/requests/api/project_approvals_spec.rb'
+- './ee/spec/requests/api/project_clusters_spec.rb'
+- './ee/spec/requests/api/project_import_spec.rb'
+- './ee/spec/requests/api/project_milestones_spec.rb'
+- './ee/spec/requests/api/project_mirror_spec.rb'
+- './ee/spec/requests/api/project_push_rule_spec.rb'
+- './ee/spec/requests/api/project_snapshots_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/related_epic_links_spec.rb'
+- './ee/spec/requests/api/releases_spec.rb'
+- './ee/spec/requests/api/repositories_spec.rb'
+- './ee/spec/requests/api/resource_iteration_events_spec.rb'
+- './ee/spec/requests/api/resource_label_events_spec.rb'
+- './ee/spec/requests/api/resource_weight_events_spec.rb'
+- './ee/spec/requests/api/saml_group_links_spec.rb'
+- './ee/spec/requests/api/scim_spec.rb'
+- './ee/spec/requests/api/search_spec.rb'
+- './ee/spec/requests/api/settings_spec.rb'
+- './ee/spec/requests/api/status_checks_spec.rb'
+- './ee/spec/requests/api/submodules_spec.rb'
+- './ee/spec/requests/api/templates_spec.rb'
+- './ee/spec/requests/api/todos_spec.rb'
+- './ee/spec/requests/api/usage_data_spec.rb'
+- './ee/spec/requests/api/users_spec.rb'
+- './ee/spec/requests/api/v3/github_spec.rb'
+- './ee/spec/requests/api/visual_review_discussions_spec.rb'
+- './ee/spec/requests/api/vulnerabilities_spec.rb'
+- './ee/spec/requests/api/vulnerability_exports_spec.rb'
+- './ee/spec/requests/api/vulnerability_findings_spec.rb'
+- './ee/spec/requests/api/vulnerability_issue_links_spec.rb'
+- './ee/spec/requests/api/wikis_spec.rb'
+- './ee/spec/requests/callout_spec.rb'
+- './ee/spec/requests/customers_dot/proxy_controller_spec.rb'
+- './ee/spec/requests/ee/groups/autocomplete_sources_spec.rb'
+- './ee/spec/requests/ee/groups/settings/repository_controller_spec.rb'
+- './ee/spec/requests/ee/projects/deploy_tokens_controller_spec.rb'
+- './ee/spec/requests/ee/projects/environments_controller_spec.rb'
+- './ee/spec/requests/ee/projects/service_desk_controller_spec.rb'
+- './ee/spec/requests/git_http_geo_spec.rb'
+- './ee/spec/requests/git_http_spec.rb'
+- './ee/spec/requests/groups/analytics/devops_adoption_controller_spec.rb'
+- './ee/spec/requests/groups/audit_events_spec.rb'
+- './ee/spec/requests/groups/clusters_controller_spec.rb'
+- './ee/spec/requests/groups/compliance_frameworks_spec.rb'
+- './ee/spec/requests/groups/contribution_analytics_spec.rb'
+- './ee/spec/requests/groups_controller_spec.rb'
+- './ee/spec/requests/groups/epics/epic_links_controller_spec.rb'
+- './ee/spec/requests/groups/epics/related_epic_links_controller_spec.rb'
+- './ee/spec/requests/groups/feature_discovery_moments_spec.rb'
+- './ee/spec/requests/groups/group_members_controller_spec.rb'
+- './ee/spec/requests/groups/hook_logs_controller_spec.rb'
+- './ee/spec/requests/groups/labels_spec.rb'
+- './ee/spec/requests/groups/protected_environments_controller_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/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'
+- './ee/spec/requests/projects/mirrors_controller_spec.rb'
+- './ee/spec/requests/projects/on_demand_scans_controller_spec.rb'
+- './ee/spec/requests/projects/pipelines_controller_spec.rb'
+- './ee/spec/requests/projects/pipelines/email_campaigns_controller_spec.rb'
+- './ee/spec/requests/projects/requirements_management/requirements_controller_spec.rb'
+- './ee/spec/requests/projects/security/corpus_management_controller_spec.rb'
+- './ee/spec/requests/projects/security/dast_configuration_controller_spec.rb'
+- './ee/spec/requests/projects/security/dast_profiles_controller_spec.rb'
+- './ee/spec/requests/projects/security/dast_scanner_profiles_controller_spec.rb'
+- './ee/spec/requests/projects/security/dast_site_profiles_controller_spec.rb'
+- './ee/spec/requests/projects/security/policies_controller_spec.rb'
+- './ee/spec/requests/projects/security/scanned_resources_controller_spec.rb'
+- './ee/spec/requests/projects/settings/access_tokens_controller_spec.rb'
+- './ee/spec/requests/rack_attack_global_spec.rb'
+- './ee/spec/requests/rack_attack_spec.rb'
+- './ee/spec/requests/repositories/git_http_controller_spec.rb'
+- './ee/spec/requests/search_controller_spec.rb'
+- './ee/spec/requests/smartcard_controller_spec.rb'
+- './ee/spec/requests/trial_registrations_controller_spec.rb'
+- './ee/spec/requests/user_activity_spec.rb'
+- './ee/spec/routing/admin_routing_spec.rb'
+- './ee/spec/routing/git_http_routing_spec.rb'
+- './ee/spec/routing/group_routing_spec.rb'
+- './ee/spec/routing/groups/cadences_routing_spec.rb'
+- './ee/spec/routing/operations_routing_spec.rb'
+- './ee/spec/routing/project_routing_spec.rb'
+- './ee/spec/routing/projects/security/configuration_controller_routing_spec.rb'
+- './ee/spec/routing/security_routing_spec.rb'
+- './ee/spec/routing/uploads_routing_spec.rb'
+- './ee/spec/routing/user_routing_spec.rb'
+- './ee/spec/routing/webhook_routes_spec.rb'
+- './ee/spec/serializers/analytics/cycle_analytics/event_entity_spec.rb'
+- './ee/spec/serializers/analytics/cycle_analytics/stage_entity_spec.rb'
+- './ee/spec/serializers/analytics/cycle_analytics/value_stream_errors_serializer_spec.rb'
+- './ee/spec/serializers/audit_event_entity_spec.rb'
+- './ee/spec/serializers/audit_event_serializer_spec.rb'
+- './ee/spec/serializers/autocomplete/group_entity_spec.rb'
+- './ee/spec/serializers/autocomplete/group_serializer_spec.rb'
+- './ee/spec/serializers/blocking_merge_request_entity_spec.rb'
+- './ee/spec/serializers/board_serializer_spec.rb'
+- './ee/spec/serializers/clusters/deployment_entity_spec.rb'
+- './ee/spec/serializers/clusters/environment_entity_spec.rb'
+- './ee/spec/serializers/clusters/environment_serializer_spec.rb'
+- './ee/spec/serializers/dashboard_environment_entity_spec.rb'
+- './ee/spec/serializers/dashboard_environments_project_entity_spec.rb'
+- './ee/spec/serializers/dashboard_environments_serializer_spec.rb'
+- './ee/spec/serializers/dashboard_operations_project_entity_spec.rb'
+- './ee/spec/serializers/dependency_entity_spec.rb'
+- './ee/spec/serializers/dependency_list_entity_spec.rb'
+- './ee/spec/serializers/dependency_list_serializer_spec.rb'
+- './ee/spec/serializers/ee/admin/user_entity_spec.rb'
+- './ee/spec/serializers/ee/blob_entity_spec.rb'
+- './ee/spec/serializers/ee/board_simple_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/deployment_entity_spec.rb'
+- './ee/spec/serializers/ee/environment_serializer_spec.rb'
+- './ee/spec/serializers/ee/evidences/release_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_basic_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/ee/user_serializer_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/epics/related_epic_entity_spec.rb'
+- './ee/spec/serializers/evidences/build_artifact_entity_spec.rb'
+- './ee/spec/serializers/evidences/evidence_entity_spec.rb'
+- './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'
+- './ee/spec/serializers/integrations/jira_serializers/issue_serializer_spec.rb'
+- './ee/spec/serializers/integrations/zentao_serializers/issue_entity_spec.rb'
+- './ee/spec/serializers/issuable_sidebar_extras_entity_spec.rb'
+- './ee/spec/serializers/issue_serializer_spec.rb'
+- './ee/spec/serializers/issues/linked_issue_feature_flag_entity_spec.rb'
+- './ee/spec/serializers/license_compliance/collapsed_comparer_entity_spec.rb'
+- './ee/spec/serializers/license_compliance/comparer_entity_spec.rb'
+- './ee/spec/serializers/license_entity_spec.rb'
+- './ee/spec/serializers/licenses_list_entity_spec.rb'
+- './ee/spec/serializers/licenses_list_serializer_spec.rb'
+- './ee/spec/serializers/linked_feature_flag_issue_entity_spec.rb'
+- './ee/spec/serializers/member_entity_spec.rb'
+- './ee/spec/serializers/member_user_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/metrics_report_metric_entity_spec.rb'
+- './ee/spec/serializers/metrics_reports_comparer_entity_spec.rb'
+- './ee/spec/serializers/pipeline_serializer_spec.rb'
+- './ee/spec/serializers/productivity_analytics_merge_request_entity_spec.rb'
+- './ee/spec/serializers/project_mirror_entity_spec.rb'
+- './ee/spec/serializers/protected_environments/deploy_access_level_entity_spec.rb'
+- './ee/spec/serializers/protected_environments/entity_spec.rb'
+- './ee/spec/serializers/scim_oauth_access_token_entity_spec.rb'
+- './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'
+- './ee/spec/serializers/test_suite_comparer_entity_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/vulnerabilities/identifier_entity_spec.rb'
+- './ee/spec/serializers/vulnerabilities/request_entity_spec.rb'
+- './ee/spec/serializers/vulnerabilities/response_entity_spec.rb'
+- './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/services/admin/email_service_spec.rb'
+- './ee/spec/services/alert_management/extract_alert_payload_fields_service_spec.rb'
+- './ee/spec/services/alert_management/process_prometheus_alert_service_spec.rb'
+- './ee/spec/services/analytics/cycle_analytics/aggregator_service_spec.rb'
+- './ee/spec/services/analytics/cycle_analytics/consistency_check_service_spec.rb'
+- './ee/spec/services/analytics/cycle_analytics/data_loader_service_spec.rb'
+- './ee/spec/services/analytics/cycle_analytics/stages/create_service_spec.rb'
+- './ee/spec/services/analytics/cycle_analytics/stages/delete_service_spec.rb'
+- './ee/spec/services/analytics/cycle_analytics/stages/list_service_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/analytics/devops_adoption/enabled_namespaces/bulk_delete_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/create_service_spec.rb'
+- './ee/spec/services/analytics/devops_adoption/enabled_namespaces/delete_service_spec.rb'
+- './ee/spec/services/analytics/devops_adoption/enabled_namespaces/find_or_create_service_spec.rb'
+- './ee/spec/services/analytics/devops_adoption/snapshots/calculate_and_save_service_spec.rb'
+- './ee/spec/services/analytics/devops_adoption/snapshots/create_service_spec.rb'
+- './ee/spec/services/analytics/devops_adoption/snapshots/update_service_spec.rb'
+- './ee/spec/services/applications/create_service_spec.rb'
+- './ee/spec/services/application_settings/update_service_spec.rb'
+- './ee/spec/services/approval_rules/create_service_spec.rb'
+- './ee/spec/services/approval_rules/finalize_service_spec.rb'
+- './ee/spec/services/approval_rules/merge_request_rule_destroy_service_spec.rb'
+- './ee/spec/services/approval_rules/params_filtering_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/services/app_sec/dast/builds/associate_service_spec.rb'
+- './ee/spec/services/app_sec/dast/pipelines/find_latest_service_spec.rb'
+- './ee/spec/services/app_sec/dast/profiles/audit/update_service_spec.rb'
+- './ee/spec/services/app_sec/dast/profiles/build_config_service_spec.rb'
+- './ee/spec/services/app_sec/dast/profile_schedules/audit/update_service_spec.rb'
+- './ee/spec/services/app_sec/dast/profiles/create_associations_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/scan_configs/build_service_spec.rb'
+- './ee/spec/services/app_sec/dast/scan_configs/fetch_service_spec.rb'
+- './ee/spec/services/app_sec/dast/scanner_profiles/create_service_spec.rb'
+- './ee/spec/services/app_sec/dast/scanner_profiles/destroy_service_spec.rb'
+- './ee/spec/services/app_sec/dast/scanner_profiles/update_service_spec.rb'
+- './ee/spec/services/app_sec/dast/scans/create_service_spec.rb'
+- './ee/spec/services/app_sec/dast/scans/run_service_spec.rb'
+- './ee/spec/services/app_sec/dast/site_profiles/audit/update_service_spec.rb'
+- './ee/spec/services/app_sec/dast/site_profiles/create_service_spec.rb'
+- './ee/spec/services/app_sec/dast/site_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/update_service_spec.rb'
+- './ee/spec/services/app_sec/dast/sites/find_or_create_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/api/ci_configuration_create_service_spec.rb'
+- './ee/spec/services/app_sec/fuzzing/coverage/corpuses/create_service_spec.rb'
+- './ee/spec/services/arkose/blocked_users_report_service_spec.rb'
+- './ee/spec/services/arkose/user_verification_service_spec.rb'
+- './ee/spec/services/audit_events/build_service_spec.rb'
+- './ee/spec/services/audit_events/custom_audit_event_service_spec.rb'
+- './ee/spec/services/audit_event_service_spec.rb'
+- './ee/spec/services/audit_events/export_csv_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/register_runner_audit_event_service_spec.rb'
+- './ee/spec/services/audit_events/release_artifacts_downloaded_audit_event_service_spec.rb'
+- './ee/spec/services/audit_events/release_associate_milestone_audit_event_service_spec.rb'
+- './ee/spec/services/audit_events/release_created_audit_event_service_spec.rb'
+- './ee/spec/services/audit_events/release_updated_audit_event_service_spec.rb'
+- './ee/spec/services/audit_events/repository_download_started_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/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/audit_events/unregister_runner_audit_event_service_spec.rb'
+- './ee/spec/services/audit_events/user_impersonation_group_audit_event_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/award_emojis/add_service_spec.rb'
+- './ee/spec/services/award_emojis/destroy_service_spec.rb'
+- './ee/spec/services/base_count_service_spec.rb'
+- './ee/spec/services/billable_members/destroy_service_spec.rb'
+- './ee/spec/services/boards/create_service_spec.rb'
+- './ee/spec/services/boards/epic_boards/create_service_spec.rb'
+- './ee/spec/services/boards/epic_boards/destroy_service_spec.rb'
+- './ee/spec/services/boards/epic_boards/update_service_spec.rb'
+- './ee/spec/services/boards/epic_boards/visits/create_service_spec.rb'
+- './ee/spec/services/boards/epic_lists/create_service_spec.rb'
+- './ee/spec/services/boards/epic_lists/destroy_service_spec.rb'
+- './ee/spec/services/boards/epic_lists/list_service_spec.rb'
+- './ee/spec/services/boards/epic_lists/update_service_spec.rb'
+- './ee/spec/services/boards/epics/create_service_spec.rb'
+- './ee/spec/services/boards/epics/list_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/boards/epic_user_preferences/update_service_spec.rb'
+- './ee/spec/services/boards/lists/update_service_spec.rb'
+- './ee/spec/services/boards/update_service_spec.rb'
+- './ee/spec/services/boards/user_preferences/update_service_spec.rb'
+- './ee/spec/services/branches/delete_service_spec.rb'
+- './ee/spec/services/ci/audit_variable_change_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/ci_cd/setup_project_spec.rb'
+- './ee/spec/services/ci/compare_license_scanning_reports_collapsed_service_spec.rb'
+- './ee/spec/services/ci/compare_license_scanning_reports_service_spec.rb'
+- './ee/spec/services/ci/compare_metrics_reports_service_spec.rb'
+- './ee/spec/services/ci/compare_security_reports_service_spec.rb'
+- './ee/spec/services/ci/copy_cross_database_associations_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/dast_configuration_spec.rb'
+- './ee/spec/services/ci/create_pipeline_service/needs_spec.rb'
+- './ee/spec/services/ci/create_pipeline_service/runnable_builds_spec.rb'
+- './ee/spec/services/ci/create_pipeline_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/additional_packs/change_namespace_service_spec.rb'
+- './ee/spec/services/ci/minutes/additional_packs/create_service_spec.rb'
+- './ee/spec/services/ci/minutes/batch_reset_service_spec.rb'
+- './ee/spec/services/ci/minutes/email_notification_service_spec.rb'
+- './ee/spec/services/ci/minutes/refresh_cached_data_service_spec.rb'
+- './ee/spec/services/ci/minutes/reset_usage_service_spec.rb'
+- './ee/spec/services/ci/minutes/track_live_consumption_service_spec.rb'
+- './ee/spec/services/ci/minutes/update_build_minutes_service_spec.rb'
+- './ee/spec/services/ci/minutes/update_project_and_namespace_usage_service_spec.rb'
+- './ee/spec/services/ci/pipeline_bridge_status_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/ci/pipeline_trigger_service_spec.rb'
+- './ee/spec/services/ci/play_bridge_service_spec.rb'
+- './ee/spec/services/ci/play_build_service_spec.rb'
+- './ee/spec/services/ci/process_build_service_spec.rb'
+- './ee/spec/services/ci/process_pipeline_service_spec.rb'
+- './ee/spec/services/ci/register_job_service_spec.rb'
+- './ee/spec/services/ci/retry_job_service_spec.rb'
+- './ee/spec/services/ci/retry_pipeline_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/stale_group_runners_prune_service_spec.rb'
+- './ee/spec/services/ci/runners/unassign_runner_service_spec.rb'
+- './ee/spec/services/ci/runners/unregister_runner_service_spec.rb'
+- './ee/spec/services/ci/subscribe_bridge_service_spec.rb'
+- './ee/spec/services/ci/sync_reports_to_approval_rules_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/compliance_management/merge_requests/create_compliance_violations_service_spec.rb'
+- './ee/spec/services/concerns/epics/related_epic_links/usage_data_helper_spec.rb'
+- './ee/spec/services/dashboard/environments/list_service_spec.rb'
+- './ee/spec/services/dashboard/operations/list_service_spec.rb'
+- './ee/spec/services/dashboard/projects/create_service_spec.rb'
+- './ee/spec/services/dashboard/projects/list_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/dora/aggregate_metrics_service_spec.rb'
+- './ee/spec/services/ee/alert_management/alerts/update_service_spec.rb'
+- './ee/spec/services/ee/alert_management/create_alert_issue_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/allowed_email_domains/update_service_spec.rb'
+- './ee/spec/services/ee/auth/container_registry_authentication_service_spec.rb'
+- './ee/spec/services/ee/auto_merge_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/issues/move_service_spec.rb'
+- './ee/spec/services/ee/boards/lists/create_service_spec.rb'
+- './ee/spec/services/ee/boards/lists/list_service_spec.rb'
+- './ee/spec/services/ee/boards/lists/max_limits_spec.rb'
+- './ee/spec/services/ee/ci/change_variable_service_spec.rb'
+- './ee/spec/services/ee/ci/change_variables_service_spec.rb'
+- './ee/spec/services/ee/ci/job_artifacts/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/ci/pipeline_processing/atomic_processing_service_spec.rb'
+- './ee/spec/services/ee/commits/create_service_spec.rb'
+- './ee/spec/services/ee/deployments/update_environment_service_spec.rb'
+- './ee/spec/services/ee/design_management/delete_designs_service_spec.rb'
+- './ee/spec/services/ee/design_management/save_designs_service_spec.rb'
+- './ee/spec/services/ee/event_create_service_spec.rb'
+- './ee/spec/services/ee/git/branch_push_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/autocomplete_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/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'
+- './ee/spec/services/ee/issuable/common_system_notes_service_spec.rb'
+- './ee/spec/services/ee/issuable/destroy_service_spec.rb'
+- './ee/spec/services/ee/issue_links/create_service_spec.rb'
+- './ee/spec/services/ee/issues/after_create_service_spec.rb'
+- './ee/spec/services/ee/issues/build_from_vulnerability_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_from_vulnerability_data_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/labels/create_service_spec.rb'
+- './ee/spec/services/ee/labels/promote_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/import_project_team_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_request_metrics_service_spec.rb'
+- './ee/spec/services/ee/merge_requests/after_create_service_spec.rb'
+- './ee/spec/services/ee/merge_requests/base_service_spec.rb'
+- './ee/spec/services/ee/merge_requests/create_approval_event_service_spec.rb'
+- './ee/spec/services/ee/merge_requests/create_from_vulnerability_data_service_spec.rb'
+- './ee/spec/services/ee/merge_requests/create_pipeline_service_spec.rb'
+- './ee/spec/services/ee/merge_requests/create_service_spec.rb'
+- './ee/spec/services/ee/merge_requests/execute_approval_hooks_service_spec.rb'
+- './ee/spec/services/ee/merge_requests/handle_assignees_change_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/merge_requests/update_assignees_service_spec.rb'
+- './ee/spec/services/ee/merge_requests/update_reviewers_service_spec.rb'
+- './ee/spec/services/ee/merge_requests/update_service_spec.rb'
+- './ee/spec/services/ee/namespace_settings/update_service_spec.rb'
+- './ee/spec/services/ee/notes/create_service_spec.rb'
+- './ee/spec/services/ee/notes/destroy_service_spec.rb'
+- './ee/spec/services/ee/notes/post_process_service_spec.rb'
+- './ee/spec/services/ee/notes/quick_actions_service_spec.rb'
+- './ee/spec/services/ee/notes/update_service_spec.rb'
+- './ee/spec/services/ee/notification_service_spec.rb'
+- './ee/spec/services/ee/null_notification_service_spec.rb'
+- './ee/spec/services/ee/personal_access_tokens/revoke_service_spec.rb'
+- './ee/spec/services/ee/post_receive_service_spec.rb'
+- './ee/spec/services/ee/preview_markdown_service_spec.rb'
+- './ee/spec/services/ee/projects/autocomplete_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/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/quick_actions/target_service_spec.rb'
+- './ee/spec/services/ee/releases/create_evidence_service_spec.rb'
+- './ee/spec/services/ee/resource_events/change_iteration_service_spec.rb'
+- './ee/spec/services/ee/resource_events/change_labels_service_spec.rb'
+- './ee/spec/services/ee/resource_events/merge_into_notes_service_spec.rb'
+- './ee/spec/services/ee/resource_events/synthetic_iteration_notes_builder_service_spec.rb'
+- './ee/spec/services/ee/resource_events/synthetic_weight_notes_builder_service_spec.rb'
+- './ee/spec/services/ee/system_notes/issuables_service_spec.rb'
+- './ee/spec/services/ee/terraform/states/destroy_service_spec.rb'
+- './ee/spec/services/ee/todos/destroy/entity_leave_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/authorized_build_service_spec.rb'
+- './ee/spec/services/ee/users/block_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'
+- './ee/spec/services/elastic/cluster_reindexing_service_spec.rb'
+- './ee/spec/services/elastic/data_migration_service_spec.rb'
+- './ee/spec/services/elastic/indexing_control_service_spec.rb'
+- './ee/spec/services/elastic/index_projects_by_id_service_spec.rb'
+- './ee/spec/services/elastic/index_projects_by_range_service_spec.rb'
+- './ee/spec/services/elastic/index_projects_service_spec.rb'
+- './ee/spec/services/elastic/metrics_update_service_spec.rb'
+- './ee/spec/services/elastic/process_bookkeeping_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/epic_issues/create_service_spec.rb'
+- './ee/spec/services/epic_issues/destroy_service_spec.rb'
+- './ee/spec/services/epic_issues/list_service_spec.rb'
+- './ee/spec/services/epic_issues/update_service_spec.rb'
+- './ee/spec/services/epics/close_service_spec.rb'
+- './ee/spec/services/epics/create_service_spec.rb'
+- './ee/spec/services/epics/descendant_count_service_spec.rb'
+- './ee/spec/services/epics/epic_links/create_service_spec.rb'
+- './ee/spec/services/epics/epic_links/destroy_service_spec.rb'
+- './ee/spec/services/epics/epic_links/list_service_spec.rb'
+- './ee/spec/services/epics/epic_links/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/related_epic_links/destroy_service_spec.rb'
+- './ee/spec/services/epics/related_epic_links/list_service_spec.rb'
+- './ee/spec/services/epics/reopen_service_spec.rb'
+- './ee/spec/services/epics/transfer_service_spec.rb'
+- './ee/spec/services/epics/tree_reorder_service_spec.rb'
+- './ee/spec/services/epics/update_dates_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/dispatch_service_spec.rb'
+- './ee/spec/services/external_status_checks/update_service_spec.rb'
+- './ee/spec/services/feature_flag_issues/destroy_service_spec.rb'
+- './ee/spec/services/geo/base_file_service_spec.rb'
+- './ee/spec/services/geo/blob_download_service_spec.rb'
+- './ee/spec/services/geo/blob_upload_service_spec.rb'
+- './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'
+- './ee/spec/services/geo/files_expire_service_spec.rb'
+- './ee/spec/services/geo/framework_repository_sync_service_spec.rb'
+- './ee/spec/services/geo/graphql_request_service_spec.rb'
+- './ee/spec/services/geo/hashed_storage_attachments_event_store_spec.rb'
+- './ee/spec/services/geo/hashed_storage_attachments_migration_service_spec.rb'
+- './ee/spec/services/geo/hashed_storage_migrated_event_store_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/node_create_service_spec.rb'
+- './ee/spec/services/geo/node_status_request_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/rename_repository_service_spec.rb'
+- './ee/spec/services/geo/replication_toggle_request_service_spec.rb'
+- './ee/spec/services/geo/repositories_changed_event_store_spec.rb'
+- './ee/spec/services/geo/repository_base_sync_service_spec.rb'
+- './ee/spec/services/geo/repository_created_event_store_spec.rb'
+- './ee/spec/services/geo/repository_deleted_event_store_spec.rb'
+- './ee/spec/services/geo/repository_destroy_service_spec.rb'
+- './ee/spec/services/geo/repository_registry_removal_service_spec.rb'
+- './ee/spec/services/geo/repository_renamed_event_store_spec.rb'
+- './ee/spec/services/geo/repository_sync_service_spec.rb'
+- './ee/spec/services/geo/repository_updated_event_store_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_reset_spec.rb'
+- './ee/spec/services/geo/repository_verification_secondary_service_spec.rb'
+- './ee/spec/services/geo/reset_checksum_event_store_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/activate_service_spec.rb'
+- './ee/spec/services/gitlab_subscriptions/apply_trial_service_spec.rb'
+- './ee/spec/services/gitlab_subscriptions/check_future_renewal_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/create_trial_or_lead_service_spec.rb'
+- './ee/spec/services/gitlab_subscriptions/extend_reactivate_trial_service_spec.rb'
+- './ee/spec/services/gitlab_subscriptions/fetch_purchase_eligible_namespaces_service_spec.rb'
+- './ee/spec/services/gitlab_subscriptions/fetch_subscription_plans_service_spec.rb'
+- './ee/spec/services/gitlab_subscriptions/notify_seats_exceeded_service_spec.rb'
+- './ee/spec/services/gitlab_subscriptions/plan_upgrade_service_spec.rb'
+- './ee/spec/services/gitlab_subscriptions/preview_billable_user_change_service_spec.rb'
+- './ee/spec/services/gitlab_subscriptions/reconciliations/calculate_seat_count_data_service_spec.rb'
+- './ee/spec/services/gitlab_subscriptions/reconciliations/check_seat_usage_alerts_eligibility_service_spec.rb'
+- './ee/spec/services/group_saml/group_managed_accounts/clean_up_members_service_spec.rb'
+- './ee/spec/services/group_saml/group_managed_accounts/transfer_membership_service_spec.rb'
+- './ee/spec/services/group_saml/identity/destroy_service_spec.rb'
+- './ee/spec/services/group_saml/saml_group_links/create_service_spec.rb'
+- './ee/spec/services/group_saml/saml_group_links/destroy_service_spec.rb'
+- './ee/spec/services/group_saml/saml_provider/create_service_spec.rb'
+- './ee/spec/services/group_saml/saml_provider/update_service_spec.rb'
+- './ee/spec/services/group_saml/sign_up_service_spec.rb'
+- './ee/spec/services/groups/create_service_spec.rb'
+- './ee/spec/services/groups/destroy_service_spec.rb'
+- './ee/spec/services/groups/epics_count_service_spec.rb'
+- './ee/spec/services/groups/mark_for_deletion_service_spec.rb'
+- './ee/spec/services/groups/memberships/export_service_spec.rb'
+- './ee/spec/services/groups/participants_service_spec.rb'
+- './ee/spec/services/groups/restore_service_spec.rb'
+- './ee/spec/services/groups/schedule_bulk_repository_shard_moves_service_spec.rb'
+- './ee/spec/services/groups/seat_usage_export_service_spec.rb'
+- './ee/spec/services/groups/sync_service_spec.rb'
+- './ee/spec/services/groups/transfer_service_spec.rb'
+- './ee/spec/services/groups/update_repository_storage_service_spec.rb'
+- './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'
+- './ee/spec/services/issues/duplicate_service_spec.rb'
+- './ee/spec/services/issues/export_csv_service_spec.rb'
+- './ee/spec/services/iterations/cadences/create_iterations_in_advance_service_spec.rb'
+- './ee/spec/services/iterations/cadences/create_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/iterations/roll_over_issues_service_spec.rb'
+- './ee/spec/services/iterations/update_service_spec.rb'
+- './ee/spec/services/jira/jql_builder_service_spec.rb'
+- './ee/spec/services/jira/requests/issues/list_service_spec.rb'
+- './ee/spec/services/keys/create_service_spec.rb'
+- './ee/spec/services/keys/last_used_service_spec.rb'
+- './ee/spec/services/ldap_group_reset_service_spec.rb'
+- './ee/spec/services/lfs/lock_file_service_spec.rb'
+- './ee/spec/services/lfs/unlock_file_service_spec.rb'
+- './ee/spec/services/licenses/destroy_service_spec.rb'
+- './ee/spec/services/members/activate_service_spec.rb'
+- './ee/spec/services/members/await_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/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/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/reload_merge_head_diff_service_spec.rb'
+- './ee/spec/services/merge_requests/remove_approval_service_spec.rb'
+- './ee/spec/services/merge_requests/reset_approvals_service_spec.rb'
+- './ee/spec/services/merge_requests/stream_approval_audit_event_service_spec.rb'
+- './ee/spec/services/merge_requests/sync_code_owner_approval_rules_spec.rb'
+- './ee/spec/services/merge_requests/sync_report_approver_approval_rules_spec.rb'
+- './ee/spec/services/merge_requests/update_blocks_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/milestones/destroy_service_spec.rb'
+- './ee/spec/services/milestones/promote_service_spec.rb'
+- './ee/spec/services/milestones/update_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/namespaces/free_user_cap/remove_project_group_links_outside_hierarchy_service_spec.rb'
+- './ee/spec/services/namespaces/free_user_cap/update_prevent_sharing_outside_hierarchy_service_spec.rb'
+- './ee/spec/services/namespaces/in_product_marketing_emails_service_spec.rb'
+- './ee/spec/services/namespaces/storage/email_notification_service_spec.rb'
+- './ee/spec/services/path_locks/lock_service_spec.rb'
+- './ee/spec/services/path_locks/unlock_service_spec.rb'
+- './ee/spec/services/personal_access_tokens/create_service_audit_log_spec.rb'
+- './ee/spec/services/personal_access_tokens/groups/update_lifetime_service_spec.rb'
+- './ee/spec/services/personal_access_tokens/instance/update_lifetime_service_spec.rb'
+- './ee/spec/services/personal_access_tokens/revoke_invalid_tokens_spec.rb'
+- './ee/spec/services/personal_access_tokens/revoke_service_audit_log_spec.rb'
+- './ee/spec/services/personal_access_tokens/rotation_verifier_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/cleanup_service_spec.rb'
+- './ee/spec/services/projects/create_from_template_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/disable_legacy_inactive_projects_service_spec.rb'
+- './ee/spec/services/projects/enable_deploy_key_service_spec.rb'
+- './ee/spec/services/projects/fork_service_spec.rb'
+- './ee/spec/services/projects/gitlab_projects_import_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/group_links/update_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_export/export_service_spec.rb'
+- './ee/spec/services/projects/import_service_spec.rb'
+- './ee/spec/services/projects/licenses/create_policy_service_spec.rb'
+- './ee/spec/services/projects/licenses/update_policy_service_spec.rb'
+- './ee/spec/services/projects/mark_for_deletion_service_spec.rb'
+- './ee/spec/services/projects/open_issues_count_service_spec.rb'
+- './ee/spec/services/projects/operations/update_service_spec.rb'
+- './ee/spec/services/projects/prometheus/alerts/notify_service_spec.rb'
+- './ee/spec/services/projects/protect_default_branch_service_spec.rb'
+- './ee/spec/services/projects/restore_service_spec.rb'
+- './ee/spec/services/projects/setup_ci_cd_spec.rb'
+- './ee/spec/services/projects/slack_application_install_service_spec.rb'
+- './ee/spec/services/projects/transfer_service_spec.rb'
+- './ee/spec/services/projects/update_mirror_service_spec.rb'
+- './ee/spec/services/projects/update_service_spec.rb'
+- './ee/spec/services/protected_environments/base_service_spec.rb'
+- './ee/spec/services/protected_environments/create_service_spec.rb'
+- './ee/spec/services/protected_environments/destroy_service_spec.rb'
+- './ee/spec/services/protected_environments/environment_dropdown_service_spec.rb'
+- './ee/spec/services/protected_environments/search_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/quality_management/test_cases/create_service_spec.rb'
+- './ee/spec/services/quick_actions/interpret_service_spec.rb'
+- './ee/spec/services/releases/create_service_spec.rb'
+- './ee/spec/services/releases/update_service_spec.rb'
+- './ee/spec/services/repositories/housekeeping_service_spec.rb'
+- './ee/spec/services/requirements_management/export_csv_service_spec.rb'
+- './ee/spec/services/requirements_management/import_csv_service_spec.rb'
+- './ee/spec/services/requirements_management/map_export_fields_service_spec.rb'
+- './ee/spec/services/requirements_management/prepare_import_csv_service_spec.rb'
+- './ee/spec/services/requirements_management/process_test_reports_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/resource_events/change_weight_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_service_spec.rb'
+- './ee/spec/services/search/snippet_service_spec.rb'
+- './ee/spec/services/security/auto_fix_label_service_spec.rb'
+- './ee/spec/services/security/auto_fix_service_spec.rb'
+- './ee/spec/services/security/configuration/save_auto_fix_service_spec.rb'
+- './ee/spec/services/security/dependency_list_service_spec.rb'
+- './ee/spec/services/security/findings/cleanup_service_spec.rb'
+- './ee/spec/services/security/ingestion/finding_map_collection_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/ingest_report_slice_service_spec.rb'
+- './ee/spec/services/security/ingestion/ingest_reports_service_spec.rb'
+- './ee/spec/services/security/ingestion/mark_as_resolved_service_spec.rb'
+- './ee/spec/services/security/ingestion/tasks/attach_findings_to_vulnerabilities_spec.rb'
+- './ee/spec/services/security/ingestion/tasks/hooks_execution_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_pipelines_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_issue_links_spec.rb'
+- './ee/spec/services/security/ingestion/tasks/ingest_remediations_spec.rb'
+- './ee/spec/services/security/ingestion/tasks/ingest_vulnerabilities/create_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/ingestion/tasks/ingest_vulnerability_flags_spec.rb'
+- './ee/spec/services/security/ingestion/tasks/ingest_vulnerability_statistics_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/security/orchestration/assign_service_spec.rb'
+- './ee/spec/services/security/orchestration/unassign_service_spec.rb'
+- './ee/spec/services/security/override_uuids_service_spec.rb'
+- './ee/spec/services/security/report_fetch_service_spec.rb'
+- './ee/spec/services/security/report_summary_service_spec.rb'
+- './ee/spec/services/security/scanned_resources_counting_service_spec.rb'
+- './ee/spec/services/security/scanned_resources_service_spec.rb'
+- './ee/spec/services/security/security_orchestration_policies/ci_configuration_service_spec.rb'
+- './ee/spec/services/security/security_orchestration_policies/create_pipeline_service_spec.rb'
+- './ee/spec/services/security/security_orchestration_policies/fetch_policy_approvers_service_spec.rb'
+- './ee/spec/services/security/security_orchestration_policies/fetch_policy_service_spec.rb'
+- './ee/spec/services/security/security_orchestration_policies/on_demand_scan_pipeline_configuration_service_spec.rb'
+- './ee/spec/services/security/security_orchestration_policies/operational_vulnerabilities_configuration_service_spec.rb'
+- './ee/spec/services/security/security_orchestration_policies/policy_commit_service_spec.rb'
+- './ee/spec/services/security/security_orchestration_policies/policy_configuration_validation_service_spec.rb'
+- './ee/spec/services/security/security_orchestration_policies/process_policy_service_spec.rb'
+- './ee/spec/services/security/security_orchestration_policies/process_rule_service_spec.rb'
+- './ee/spec/services/security/security_orchestration_policies/process_scan_result_policy_service_spec.rb'
+- './ee/spec/services/security/security_orchestration_policies/project_create_service_spec.rb'
+- './ee/spec/services/security/security_orchestration_policies/rule_schedule_service_spec.rb'
+- './ee/spec/services/security/security_orchestration_policies/scan_pipeline_service_spec.rb'
+- './ee/spec/services/security/security_orchestration_policies/sync_opened_merge_requests_service_spec.rb'
+- './ee/spec/services/security/security_orchestration_policies/sync_open_merge_requests_head_pipeline_service_spec.rb'
+- './ee/spec/services/security/security_orchestration_policies/validate_policy_service_spec.rb'
+- './ee/spec/services/security/store_findings_metadata_service_spec.rb'
+- './ee/spec/services/security/store_grouped_scans_service_spec.rb'
+- './ee/spec/services/security/store_scan_service_spec.rb'
+- './ee/spec/services/security/store_scans_service_spec.rb'
+- './ee/spec/services/security/token_revocation_service_spec.rb'
+- './ee/spec/services/security/track_scan_service_spec.rb'
+- './ee/spec/services/security/update_training_service_spec.rb'
+- './ee/spec/services/security/vulnerability_counting_service_spec.rb'
+- './ee/spec/services/sitemap/create_service_spec.rb'
+- './ee/spec/services/slash_commands/global_slack_handler_spec.rb'
+- './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'
+- './ee/spec/services/system_notes/merge_train_service_spec.rb'
+- './ee/spec/services/system_notes/vulnerabilities_service_spec.rb'
+- './ee/spec/services/timebox_report_service_spec.rb'
+- './ee/spec/services/timelogs/create_service_spec.rb'
+- './ee/spec/services/todos/allowed_target_filter_service_spec.rb'
+- './ee/spec/services/todos/destroy/confidential_epic_service_spec.rb'
+- './ee/spec/services/todo_service_spec.rb'
+- './ee/spec/services/upcoming_reconciliations/update_service_spec.rb'
+- './ee/spec/services/user_permissions/export_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/services/users/abuse/namespace_bans/create_service_spec.rb'
+- './ee/spec/services/users/abuse/namespace_bans/destroy_service_spec.rb'
+- './ee/spec/services/users/captcha_challenge_service_spec.rb'
+- './ee/spec/services/users_ops_dashboard_projects/destroy_service_spec.rb'
+- './ee/spec/services/users/update_highest_member_role_service_spec.rb'
+- './ee/spec/services/vulnerabilities/confirm_service_spec.rb'
+- './ee/spec/services/vulnerabilities/create_from_security_finding_service_spec.rb'
+- './ee/spec/services/vulnerabilities/create_service_spec.rb'
+- './ee/spec/services/vulnerabilities/destroy_dismissal_feedback_service_spec.rb'
+- './ee/spec/services/vulnerabilities/dismiss_service_spec.rb'
+- './ee/spec/services/vulnerabilities/finding_dismiss_service_spec.rb'
+- './ee/spec/services/vulnerabilities/findings/create_from_security_finding_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/manually_create_service_spec.rb'
+- './ee/spec/services/vulnerabilities/resolve_service_spec.rb'
+- './ee/spec/services/vulnerabilities/revert_to_detected_service_spec.rb'
+- './ee/spec/services/vulnerabilities/security_finding/create_issue_service_spec.rb'
+- './ee/spec/services/vulnerabilities/starboard_vulnerability_create_service_spec.rb'
+- './ee/spec/services/vulnerabilities/starboard_vulnerability_resolve_service_spec.rb'
+- './ee/spec/services/vulnerabilities/statistics/adjustment_service_spec.rb'
+- './ee/spec/services/vulnerabilities/statistics/update_service_spec.rb'
+- './ee/spec/services/vulnerabilities/update_service_spec.rb'
+- './ee/spec/services/vulnerabilities/user_notes_count_service_spec.rb'
+- './ee/spec/services/vulnerability_exports/create_service_spec.rb'
+- './ee/spec/services/vulnerability_exports/exporters/csv_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/services/vulnerability_scanners/list_service_spec.rb'
+- './ee/spec/services/web_hook_service_spec.rb'
+- './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/services/wikis/create_attachment_service_spec.rb'
+- './ee/spec/services/work_items/update_service_spec.rb'
+- './ee/spec/services/work_items/widgets/weight_service/update_service_spec.rb'
+- './ee/spec/tasks/geo/git_rake_spec.rb'
+- './ee/spec/tasks/geo_rake_spec.rb'
+- './ee/spec/tasks/gitlab/check_rake_spec.rb'
+- './ee/spec/tasks/gitlab/elastic_rake_spec.rb'
+- './ee/spec/tasks/gitlab/geo_rake_spec.rb'
+- './ee/spec/tasks/gitlab/license_rake_spec.rb'
+- './ee/spec/tasks/gitlab/seed/group_seed_rake_spec.rb'
+- './ee/spec/tasks/gitlab/spdx_rake_spec.rb'
+- './ee/spec/tasks/gitlab/uploads/migrate_rake_spec.rb'
+- './ee/spec/validators/json_schema_validator_spec.rb'
+- './ee/spec/validators/ldap_filter_validator_spec.rb'
+- './ee/spec/validators/password/complexity_validator_spec.rb'
+- './ee/spec/validators/user_existence_validator_spec.rb'
+- './ee/spec/views/admin/application_settings/_deletion_protection_settings.html.haml_spec.rb'
+- './ee/spec/views/admin/application_settings/_elasticsearch_form.html.haml_spec.rb'
+- './ee/spec/views/admin/application_settings/general.html.haml_spec.rb'
+- './ee/spec/views/admin/application_settings/_git_abuse_rate_limit.html.haml_spec.rb'
+- './ee/spec/views/admin/dashboard/index.html.haml_spec.rb'
+- './ee/spec/views/admin/dev_ops_report/show.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/push_rules/_merge_request_approvals.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/_compliance_framework_badge.html.haml_spec.rb'
+- './ee/spec/views/compliance_management/compliance_framework/_project_settings.html.haml_spec.rb'
+- './ee/spec/views/devise/sessions/new.html.haml_spec.rb'
+- './ee/spec/views/groups/billings/index.html.haml_spec.rb'
+- './ee/spec/views/groups/compliance_frameworks/edit.html.haml_spec.rb'
+- './ee/spec/views/groups/_compliance_frameworks.html.haml_spec.rb'
+- './ee/spec/views/groups/compliance_frameworks/new.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/groups/show.html.haml_spec.rb'
+- './ee/spec/views/groups/usage_quotas/index.html.haml_spec.rb'
+- './ee/spec/views/layouts/application.html.haml_spec.rb'
+- './ee/spec/views/layouts/checkout.html.haml_spec.rb'
+- './ee/spec/views/layouts/header/_current_user_dropdown.html.haml_spec.rb'
+- './ee/spec/views/layouts/header/_ee_subscribable_banner.html.haml_spec.rb'
+- './ee/spec/views/layouts/header/help_dropdown/_cross_stage_fdm.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/nav/sidebar/_admin.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/nav/sidebar/_push_rules_link.html.haml_spec.rb'
+- './ee/spec/views/layouts/_search.html.haml_spec.rb'
+- './ee/spec/views/operations/environments.html.haml_spec.rb'
+- './ee/spec/views/operations/index.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/empty.html.haml_spec.rb'
+- './ee/spec/views/projects/issues/show.html.haml_spec.rb'
+- './ee/spec/views/projects/merge_requests/_merge_request_approvals.html.haml_spec.rb'
+- './ee/spec/views/projects/_merge_request_status_checks_settings.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/projects/show.html.haml_spec.rb'
+- './ee/spec/views/registrations/groups/new.html.haml_spec.rb'
+- './ee/spec/views/registrations/groups_projects/new.html.haml_spec.rb'
+- './ee/spec/views/registrations/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/access_tokens/_table.html.haml_spec.rb'
+- './ee/spec/views/shared/billings/_billing_plan_actions.html.haml_spec.rb'
+- './ee/spec/views/shared/billings/_billing_plan.html.haml_spec.rb'
+- './ee/spec/views/shared/billings/_billing_plans.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/_clone_panel.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/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/_approver_suggestion.html.haml_spec.rb'
+- './ee/spec/views/shared/issuable/_epic_dropdown.html.haml_spec.rb'
+- './ee/spec/views/shared/issuable/_health_status_dropdown.html.haml_spec.rb'
+- './ee/spec/views/shared/issuable/_iterations_dropdown.html.haml_spec.rb'
+- './ee/spec/views/shared/issuable/_sidebar.html.haml_spec.rb'
+- './ee/spec/views/shared/_kerberos_clone_button.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/_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/promotions/_promotion_link_project.html.haml_spec.rb'
+- './ee/spec/views/subscriptions/buy_minutes.html.haml_spec.rb'
+- './ee/spec/views/subscriptions/buy_storage.html.haml_spec.rb'
+- './ee/spec/views/subscriptions/groups/edit.html.haml_spec.rb'
+- './ee/spec/views/subscriptions/new.html.haml_spec.rb'
+- './ee/spec/views/trial_registrations/new.html.haml_spec.rb'
+- './ee/spec/views/trials/_skip_trial.html.haml_spec.rb'
+- './ee/spec/workers/active_user_count_threshold_worker_spec.rb'
+- './ee/spec/workers/adjourned_group_deletion_worker_spec.rb'
+- './ee/spec/workers/adjourned_project_deletion_worker_spec.rb'
+- './ee/spec/workers/adjourned_projects_deletion_cron_worker_spec.rb'
+- './ee/spec/workers/admin_emails_worker_spec.rb'
+- './ee/spec/workers/analytics/code_review_metrics_worker_spec.rb'
+- './ee/spec/workers/analytics/cycle_analytics/consistency_worker_spec.rb'
+- './ee/spec/workers/analytics/cycle_analytics/incremental_worker_spec.rb'
+- './ee/spec/workers/analytics/cycle_analytics/reaggregation_worker_spec.rb'
+- './ee/spec/workers/analytics/devops_adoption/create_all_snapshots_worker_spec.rb'
+- './ee/spec/workers/analytics/devops_adoption/create_snapshot_worker_spec.rb'
+- './ee/spec/workers/approval_rules/external_approval_rule_payload_worker_spec.rb'
+- './ee/spec/workers/app_sec/dast/profile_schedule_worker_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/audit_events/audit_event_streaming_worker_spec.rb'
+- './ee/spec/workers/audit_events/user_impersonation_event_create_worker_spec.rb'
+- './ee/spec/workers/auth/saml_group_sync_worker_spec.rb'
+- './ee/spec/workers/ci/batch_reset_minutes_worker_spec.rb'
+- './ee/spec/workers/ci/initial_pipeline_process_worker_spec.rb'
+- './ee/spec/workers/ci/minutes/refresh_cached_data_worker_spec.rb'
+- './ee/spec/workers/ci/minutes/update_project_and_namespace_usage_worker_spec.rb'
+- './ee/spec/workers/ci/runners/stale_group_runners_prune_cron_worker_spec.rb'
+- './ee/spec/workers/ci/sync_reports_to_report_approval_rules_worker_spec.rb'
+- './ee/spec/workers/ci/trigger_downstream_subscriptions_worker_spec.rb'
+- './ee/spec/workers/ci/upstream_projects_subscriptions_cleanup_worker_spec.rb'
+- './ee/spec/workers/clear_shared_runners_minutes_worker_spec.rb'
+- './ee/spec/workers/compliance_management/chain_of_custody_report_worker_spec.rb'
+- './ee/spec/workers/compliance_management/merge_requests/compliance_violations_worker_spec.rb'
+- './ee/spec/workers/concerns/elastic/indexing_control_spec.rb'
+- './ee/spec/workers/concerns/elastic/migration_obsolete_spec.rb'
+- './ee/spec/workers/concerns/elastic/migration_options_spec.rb'
+- './ee/spec/workers/concerns/update_orchestration_policy_configuration_spec.rb'
+- './ee/spec/workers/create_github_webhook_worker_spec.rb'
+- './ee/spec/workers/deployments/auto_rollback_worker_spec.rb'
+- './ee/spec/workers/dora/daily_metrics/refresh_worker_spec.rb'
+- './ee/spec/workers/ee/arkose/blocked_users_report_worker_spec.rb'
+- './ee/spec/workers/ee/ci/build_finished_worker_spec.rb'
+- './ee/spec/workers/ee/issuable_export_csv_worker_spec.rb'
+- './ee/spec/workers/ee/namespaces/in_product_marketing_emails_worker_spec.rb'
+- './ee/spec/workers/ee/namespaces/root_statistics_worker_spec.rb'
+- './ee/spec/workers/ee/projects/inactive_projects_deletion_cron_worker_spec.rb'
+- './ee/spec/workers/ee/repository_check/batch_worker_spec.rb'
+- './ee/spec/workers/ee/repository_check/single_repository_worker_spec.rb'
+- './ee/spec/workers/elastic_association_indexer_worker_spec.rb'
+- './ee/spec/workers/elastic_cluster_reindexing_cron_worker_spec.rb'
+- './ee/spec/workers/elastic_commit_indexer_worker_spec.rb'
+- './ee/spec/workers/elastic_delete_project_worker_spec.rb'
+- './ee/spec/workers/elastic_full_index_worker_spec.rb'
+- './ee/spec/workers/elastic_index_bulk_cron_worker_spec.rb'
+- './ee/spec/workers/elastic_indexing_control_worker_spec.rb'
+- './ee/spec/workers/elastic_index_initial_bulk_cron_worker_spec.rb'
+- './ee/spec/workers/elastic/migration_worker_spec.rb'
+- './ee/spec/workers/elastic_namespace_indexer_worker_spec.rb'
+- './ee/spec/workers/elastic_namespace_rollout_worker_spec.rb'
+- './ee/spec/workers/elastic/project_transfer_worker_spec.rb'
+- './ee/spec/workers/elastic_remove_expired_namespace_subscriptions_from_index_cron_worker_spec.rb'
+- './ee/spec/workers/epics/new_epic_issue_worker_spec.rb'
+- './ee/spec/workers/geo/batch_event_create_worker_spec.rb'
+- './ee/spec/workers/geo/batch/project_registry_scheduler_worker_spec.rb'
+- './ee/spec/workers/geo/batch/project_registry_worker_spec.rb'
+- './ee/spec/workers/geo/container_repository_sync_dispatch_worker_spec.rb'
+- './ee/spec/workers/geo/container_repository_sync_worker_spec.rb'
+- './ee/spec/workers/geo/create_repository_updated_event_worker_spec.rb'
+- './ee/spec/workers/geo/design_repository_shard_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/project_sync_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/repositories_clean_up_worker_spec.rb'
+- './ee/spec/workers/geo/repository_cleanup_worker_spec.rb'
+- './ee/spec/workers/geo_repository_destroy_worker_spec.rb'
+- './ee/spec/workers/geo/repository_shard_sync_worker_spec.rb'
+- './ee/spec/workers/geo/repository_sync_worker_spec.rb'
+- './ee/spec/workers/geo/repository_verification/primary/batch_worker_spec.rb'
+- './ee/spec/workers/geo/repository_verification/primary/shard_worker_spec.rb'
+- './ee/spec/workers/geo/repository_verification/primary/single_worker_spec.rb'
+- './ee/spec/workers/geo/repository_verification/secondary/scheduler_worker_spec.rb'
+- './ee/spec/workers/geo/repository_verification/secondary/shard_worker_spec.rb'
+- './ee/spec/workers/geo/repository_verification/secondary/single_worker_spec.rb'
+- './ee/spec/workers/geo/reverification_batch_worker_spec.rb'
+- './ee/spec/workers/geo/scheduler/per_shard_scheduler_worker_spec.rb'
+- './ee/spec/workers/geo/scheduler/scheduler_worker_spec.rb'
+- './ee/spec/workers/geo/secondary/registry_consistency_worker_spec.rb'
+- './ee/spec/workers/geo/secondary_usage_data_cron_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_state_backfill_service_spec.rb'
+- './ee/spec/workers/geo/verification_state_backfill_worker_spec.rb'
+- './ee/spec/workers/geo/verification_timeout_worker_spec.rb'
+- './ee/spec/workers/geo/verification_worker_spec.rb'
+- './ee/spec/workers/gitlab_subscriptions/notify_seats_exceeded_worker_spec.rb'
+- './ee/spec/workers/group_saml_group_sync_worker_spec.rb'
+- './ee/spec/workers/groups/create_event_worker_spec.rb'
+- './ee/spec/workers/groups/export_memberships_worker_spec.rb'
+- './ee/spec/workers/groups/schedule_bulk_repository_shard_moves_worker_spec.rb'
+- './ee/spec/workers/groups/update_repository_storage_worker_spec.rb'
+- './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'
+- './ee/spec/workers/iterations_update_status_worker_spec.rb'
+- './ee/spec/workers/ldap_all_groups_sync_worker_spec.rb'
+- './ee/spec/workers/ldap_group_sync_worker_spec.rb'
+- './ee/spec/workers/ldap_sync_worker_spec.rb'
+- './ee/spec/workers/licenses/reset_submit_license_usage_data_banner_worker_spec.rb'
+- './ee/spec/workers/merge_request_reset_approvals_worker_spec.rb'
+- './ee/spec/workers/merge_requests/stream_approval_audit_event_worker_spec.rb'
+- './ee/spec/workers/merge_requests/sync_code_owner_approval_rules_worker_spec.rb'
+- './ee/spec/workers/namespaces/free_user_cap/remediation_worker_spec.rb'
+- './ee/spec/workers/namespaces/sync_namespace_name_worker_spec.rb'
+- './ee/spec/workers/new_epic_worker_spec.rb'
+- './ee/spec/workers/personal_access_tokens/groups/policy_worker_spec.rb'
+- './ee/spec/workers/personal_access_tokens/instance/policy_worker_spec.rb'
+- './ee/spec/workers/post_receive_spec.rb'
+- './ee/spec/workers/project_cache_worker_spec.rb'
+- './ee/spec/workers/project_import_schedule_worker_spec.rb'
+- './ee/spec/workers/projects/disable_legacy_open_source_license_for_inactive_projects_worker_spec.rb'
+- './ee/spec/workers/project_template_export_worker_spec.rb'
+- './ee/spec/workers/refresh_license_compliance_checks_worker_spec.rb'
+- './ee/spec/workers/repository_import_worker_spec.rb'
+- './ee/spec/workers/repository_update_mirror_worker_spec.rb'
+- './ee/spec/workers/requirements_management/import_requirements_csv_worker_spec.rb'
+- './ee/spec/workers/requirements_management/process_requirements_reports_worker_spec.rb'
+- './ee/spec/workers/sbom/ingest_reports_worker_spec.rb'
+- './ee/spec/workers/scan_security_report_secrets_worker_spec.rb'
+- './ee/spec/workers/security/auto_fix_worker_spec.rb'
+- './ee/spec/workers/security/create_orchestration_policy_worker_spec.rb'
+- './ee/spec/workers/security/findings/cleanup_worker_spec.rb'
+- './ee/spec/workers/security/findings/delete_by_job_id_worker_spec.rb'
+- './ee/spec/workers/security/orchestration_policy_rule_schedule_namespace_worker_spec.rb'
+- './ee/spec/workers/security/orchestration_policy_rule_schedule_worker_spec.rb'
+- './ee/spec/workers/security/store_scans_worker_spec.rb'
+- './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'
+- './ee/spec/workers/todos_destroyer/confidential_epic_worker_spec.rb'
+- './ee/spec/workers/update_all_mirrors_worker_spec.rb'
+- './ee/spec/workers/update_max_seats_used_for_gitlab_com_subscriptions_worker_spec.rb'
+- './ee/spec/workers/vulnerabilities/historical_statistics/deletion_worker_spec.rb'
+- './ee/spec/workers/vulnerabilities/statistics/adjustment_worker_spec.rb'
+- './ee/spec/workers/vulnerabilities/statistics/schedule_worker_spec.rb'
+- './ee/spec/workers/vulnerability_exports/export_deletion_worker_spec.rb'
+- './ee/spec/workers/vulnerability_exports/export_worker_spec.rb'
+- './spec/bin/feature_flag_spec.rb'
+- './spec/bin/sidekiq_cluster_spec.rb'
+- './spec/channels/application_cable/connection_spec.rb'
+- './spec/channels/awareness_channel_spec.rb'
+- './spec/commands/metrics_server/metrics_server_spec.rb'
+- './spec/commands/sidekiq_cluster/cli_spec.rb'
+- './spec/components/diffs/overflow_warning_component_spec.rb'
+- './spec/components/diffs/stats_component_spec.rb'
+- './spec/components/layouts/horizontal_section_component_spec.rb'
+- './spec/components/pajamas/alert_component_spec.rb'
+- './spec/components/pajamas/avatar_component_spec.rb'
+- './spec/components/pajamas/banner_component_spec.rb'
+- './spec/components/pajamas/button_component_spec.rb'
+- './spec/components/pajamas/card_component_spec.rb'
+- './spec/components/pajamas/checkbox_component_spec.rb'
+- './spec/components/pajamas/checkbox_tag_component_spec.rb'
+- './spec/components/pajamas/component_spec.rb'
+- './spec/components/pajamas/concerns/checkbox_radio_label_with_help_text_spec.rb'
+- './spec/components/pajamas/concerns/checkbox_radio_options_spec.rb'
+- './spec/components/pajamas/radio_component_spec.rb'
+- './spec/components/pajamas/spinner_component_spec.rb'
+- './spec/components/pajamas/toggle_component_spec.rb'
+- './spec/config/application_spec.rb'
+- './spec/config/inject_enterprise_edition_module_spec.rb'
+- './spec/config/mail_room_spec.rb'
+- './spec/config/metrics/aggregates/aggregated_metrics_spec.rb'
+- './spec/config/object_store_settings_spec.rb'
+- './spec/config/settings_spec.rb'
+- './spec/config/smime_signature_settings_spec.rb'
+- './spec/controllers/acme_challenges_controller_spec.rb'
+- './spec/controllers/admin/applications_controller_spec.rb'
+- './spec/controllers/admin/application_settings/appearances_controller_spec.rb'
+- './spec/controllers/admin/application_settings_controller_spec.rb'
+- './spec/controllers/admin/ci/variables_controller_spec.rb'
+- './spec/controllers/admin/clusters_controller_spec.rb'
+- './spec/controllers/admin/cohorts_controller_spec.rb'
+- './spec/controllers/admin/dashboard_controller_spec.rb'
+- './spec/controllers/admin/dev_ops_report_controller_spec.rb'
+- './spec/controllers/admin/gitaly_servers_controller_spec.rb'
+- './spec/controllers/admin/groups_controller_spec.rb'
+- './spec/controllers/admin/health_check_controller_spec.rb'
+- './spec/controllers/admin/hooks_controller_spec.rb'
+- './spec/controllers/admin/identities_controller_spec.rb'
+- './spec/controllers/admin/impersonations_controller_spec.rb'
+- './spec/controllers/admin/instance_review_controller_spec.rb'
+- './spec/controllers/admin/integrations_controller_spec.rb'
+- './spec/controllers/admin/jobs_controller_spec.rb'
+- './spec/controllers/admin/plan_limits_controller_spec.rb'
+- './spec/controllers/admin/projects_controller_spec.rb'
+- './spec/controllers/admin/runner_projects_controller_spec.rb'
+- './spec/controllers/admin/runners_controller_spec.rb'
+- './spec/controllers/admin/sessions_controller_spec.rb'
+- './spec/controllers/admin/spam_logs_controller_spec.rb'
+- './spec/controllers/admin/topics/avatars_controller_spec.rb'
+- './spec/controllers/admin/topics_controller_spec.rb'
+- './spec/controllers/admin/usage_trends_controller_spec.rb'
+- './spec/controllers/admin/users_controller_spec.rb'
+- './spec/controllers/application_controller_spec.rb'
+- './spec/controllers/autocomplete_controller_spec.rb'
+- './spec/controllers/boards/issues_controller_spec.rb'
+- './spec/controllers/boards/lists_controller_spec.rb'
+- './spec/controllers/chaos_controller_spec.rb'
+- './spec/controllers/concerns/boards_responses_spec.rb'
+- './spec/controllers/concerns/check_rate_limit_spec.rb'
+- './spec/controllers/concerns/checks_collaboration_spec.rb'
+- './spec/controllers/concerns/confirm_email_warning_spec.rb'
+- './spec/controllers/concerns/continue_params_spec.rb'
+- './spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb'
+- './spec/controllers/concerns/enforces_admin_authentication_spec.rb'
+- './spec/controllers/concerns/graceful_timeout_handling_spec.rb'
+- './spec/controllers/concerns/group_tree_spec.rb'
+- './spec/controllers/concerns/harbor/artifact_spec.rb'
+- './spec/controllers/concerns/harbor/repository_spec.rb'
+- './spec/controllers/concerns/harbor/tag_spec.rb'
+- './spec/controllers/concerns/import_url_params_spec.rb'
+- './spec/controllers/concerns/internal_redirect_spec.rb'
+- './spec/controllers/concerns/issuable_actions_spec.rb'
+- './spec/controllers/concerns/issuable_collections_spec.rb'
+- './spec/controllers/concerns/metrics_dashboard_spec.rb'
+- './spec/controllers/concerns/page_limiter_spec.rb'
+- './spec/controllers/concerns/product_analytics_tracking_spec.rb'
+- './spec/controllers/concerns/project_unauthorized_spec.rb'
+- './spec/controllers/concerns/redirects_for_missing_path_on_tree_spec.rb'
+- './spec/controllers/concerns/redis_tracking_spec.rb'
+- './spec/controllers/concerns/renders_commits_spec.rb'
+- './spec/controllers/concerns/routable_actions_spec.rb'
+- './spec/controllers/concerns/send_file_upload_spec.rb'
+- './spec/controllers/concerns/sorting_preference_spec.rb'
+- './spec/controllers/concerns/sourcegraph_decorator_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/concerns/spammable_actions/captcha_check/rest_api_actions_support_spec.rb'
+- './spec/controllers/concerns/static_object_external_storage_spec.rb'
+- './spec/controllers/confirmations_controller_spec.rb'
+- './spec/controllers/dashboard_controller_spec.rb'
+- './spec/controllers/dashboard/groups_controller_spec.rb'
+- './spec/controllers/dashboard/labels_controller_spec.rb'
+- './spec/controllers/dashboard/milestones_controller_spec.rb'
+- './spec/controllers/dashboard/projects_controller_spec.rb'
+- './spec/controllers/dashboard/snippets_controller_spec.rb'
+- './spec/controllers/dashboard/todos_controller_spec.rb'
+- './spec/controllers/every_controller_spec.rb'
+- './spec/controllers/explore/groups_controller_spec.rb'
+- './spec/controllers/explore/projects_controller_spec.rb'
+- './spec/controllers/explore/snippets_controller_spec.rb'
+- './spec/controllers/google_api/authorizations_controller_spec.rb'
+- './spec/controllers/graphql_controller_spec.rb'
+- './spec/controllers/groups/avatars_controller_spec.rb'
+- './spec/controllers/groups/boards_controller_spec.rb'
+- './spec/controllers/groups/children_controller_spec.rb'
+- './spec/controllers/groups/clusters_controller_spec.rb'
+- './spec/controllers/groups_controller_spec.rb'
+- './spec/controllers/groups/dependency_proxies_controller_spec.rb'
+- './spec/controllers/groups/dependency_proxy_auth_controller_spec.rb'
+- './spec/controllers/groups/dependency_proxy_for_containers_controller_spec.rb'
+- './spec/controllers/groups/group_links_controller_spec.rb'
+- './spec/controllers/groups/group_members_controller_spec.rb'
+- './spec/controllers/groups/imports_controller_spec.rb'
+- './spec/controllers/groups/labels_controller_spec.rb'
+- './spec/controllers/groups/milestones_controller_spec.rb'
+- './spec/controllers/groups/packages_controller_spec.rb'
+- './spec/controllers/groups/registry/repositories_controller_spec.rb'
+- './spec/controllers/groups/releases_controller_spec.rb'
+- './spec/controllers/groups/runners_controller_spec.rb'
+- './spec/controllers/groups/settings/applications_controller_spec.rb'
+- './spec/controllers/groups/settings/ci_cd_controller_spec.rb'
+- './spec/controllers/groups/settings/integrations_controller_spec.rb'
+- './spec/controllers/groups/settings/repository_controller_spec.rb'
+- './spec/controllers/groups/shared_projects_controller_spec.rb'
+- './spec/controllers/groups/uploads_controller_spec.rb'
+- './spec/controllers/groups/variables_controller_spec.rb'
+- './spec/controllers/health_check_controller_spec.rb'
+- './spec/controllers/help_controller_spec.rb'
+- './spec/controllers/import/available_namespaces_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/import/manifest_controller_spec.rb'
+- './spec/controllers/import/phabricator_controller_spec.rb'
+- './spec/controllers/invites_controller_spec.rb'
+- './spec/controllers/jira_connect/app_descriptor_controller_spec.rb'
+- './spec/controllers/jira_connect/branches_controller_spec.rb'
+- './spec/controllers/jira_connect/events_controller_spec.rb'
+- './spec/controllers/jira_connect/subscriptions_controller_spec.rb'
+- './spec/controllers/ldap/omniauth_callbacks_controller_spec.rb'
+- './spec/controllers/metrics_controller_spec.rb'
+- './spec/controllers/oauth/applications_controller_spec.rb'
+- './spec/controllers/oauth/authorizations_controller_spec.rb'
+- './spec/controllers/oauth/authorized_applications_controller_spec.rb'
+- './spec/controllers/oauth/jira_dvcs/authorizations_controller_spec.rb'
+- './spec/controllers/oauth/token_info_controller_spec.rb'
+- './spec/controllers/oauth/tokens_controller_spec.rb'
+- './spec/controllers/omniauth_callbacks_controller_spec.rb'
+- './spec/controllers/passwords_controller_spec.rb'
+- './spec/controllers/profiles/accounts_controller_spec.rb'
+- './spec/controllers/profiles/active_sessions_controller_spec.rb'
+- './spec/controllers/profiles/avatars_controller_spec.rb'
+- './spec/controllers/profiles_controller_spec.rb'
+- './spec/controllers/profiles/emails_controller_spec.rb'
+- './spec/controllers/profiles/gpg_keys_controller_spec.rb'
+- './spec/controllers/profiles/keys_controller_spec.rb'
+- './spec/controllers/profiles/notifications_controller_spec.rb'
+- './spec/controllers/profiles/personal_access_tokens_controller_spec.rb'
+- './spec/controllers/profiles/preferences_controller_spec.rb'
+- './spec/controllers/profiles/two_factor_auths_controller_spec.rb'
+- './spec/controllers/profiles/webauthn_registrations_controller_spec.rb'
+- './spec/controllers/projects/alerting/notifications_controller_spec.rb'
+- './spec/controllers/projects/alert_management_controller_spec.rb'
+- './spec/controllers/projects/analytics/cycle_analytics/stages_controller_spec.rb'
+- './spec/controllers/projects/analytics/cycle_analytics/summary_controller_spec.rb'
+- './spec/controllers/projects/analytics/cycle_analytics/value_streams_controller_spec.rb'
+- './spec/controllers/projects/artifacts_controller_spec.rb'
+- './spec/controllers/projects/autocomplete_sources_controller_spec.rb'
+- './spec/controllers/projects/avatars_controller_spec.rb'
+- './spec/controllers/projects/badges_controller_spec.rb'
+- './spec/controllers/projects/blame_controller_spec.rb'
+- './spec/controllers/projects/blob_controller_spec.rb'
+- './spec/controllers/projects/boards_controller_spec.rb'
+- './spec/controllers/projects/branches_controller_spec.rb'
+- './spec/controllers/projects/ci/daily_build_group_report_results_controller_spec.rb'
+- './spec/controllers/projects/ci/lints_controller_spec.rb'
+- './spec/controllers/projects/ci/pipeline_editor_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/compare_controller_spec.rb'
+- './spec/controllers/projects_controller_spec.rb'
+- './spec/controllers/projects/cycle_analytics_controller_spec.rb'
+- './spec/controllers/projects/cycle_analytics/events_controller_spec.rb'
+- './spec/controllers/projects/deploy_keys_controller_spec.rb'
+- './spec/controllers/projects/deployments_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/discussions_controller_spec.rb'
+- './spec/controllers/projects/environments_controller_spec.rb'
+- './spec/controllers/projects/environments/prometheus_api_controller_spec.rb'
+- './spec/controllers/projects/environments/sample_metrics_controller_spec.rb'
+- './spec/controllers/projects/error_tracking_controller_spec.rb'
+- './spec/controllers/projects/error_tracking/projects_controller_spec.rb'
+- './spec/controllers/projects/error_tracking/stack_traces_controller_spec.rb'
+- './spec/controllers/projects/feature_flags_clients_controller_spec.rb'
+- './spec/controllers/projects/feature_flags_controller_spec.rb'
+- './spec/controllers/projects/feature_flags_user_lists_controller_spec.rb'
+- './spec/controllers/projects/find_file_controller_spec.rb'
+- './spec/controllers/projects/forks_controller_spec.rb'
+- './spec/controllers/projects/grafana_api_controller_spec.rb'
+- './spec/controllers/projects/graphs_controller_spec.rb'
+- './spec/controllers/projects/group_links_controller_spec.rb'
+- './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'
+- './spec/controllers/projects/labels_controller_spec.rb'
+- './spec/controllers/projects/learn_gitlab_controller_spec.rb'
+- './spec/controllers/projects/mattermosts_controller_spec.rb'
+- './spec/controllers/projects/merge_requests/conflicts_controller_spec.rb'
+- './spec/controllers/projects/merge_requests/content_controller_spec.rb'
+- './spec/controllers/projects/merge_requests_controller_spec.rb'
+- './spec/controllers/projects/merge_requests/creations_controller_spec.rb'
+- './spec/controllers/projects/merge_requests/diffs_controller_spec.rb'
+- './spec/controllers/projects/merge_requests/drafts_controller_spec.rb'
+- './spec/controllers/projects/milestones_controller_spec.rb'
+- './spec/controllers/projects/mirrors_controller_spec.rb'
+- './spec/controllers/projects/notes_controller_spec.rb'
+- './spec/controllers/projects/packages/infrastructure_registry_controller_spec.rb'
+- './spec/controllers/projects/packages/packages_controller_spec.rb'
+- './spec/controllers/projects/pages_controller_spec.rb'
+- './spec/controllers/projects/pages_domains_controller_spec.rb'
+- './spec/controllers/projects/performance_monitoring/dashboards_controller_spec.rb'
+- './spec/controllers/projects/pipeline_schedules_controller_spec.rb'
+- './spec/controllers/projects/pipelines_controller_spec.rb'
+- './spec/controllers/projects/pipelines_settings_controller_spec.rb'
+- './spec/controllers/projects/pipelines/stages_controller_spec.rb'
+- './spec/controllers/projects/pipelines/tests_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'
+- './spec/controllers/projects/protected_branches_controller_spec.rb'
+- './spec/controllers/projects/protected_tags_controller_spec.rb'
+- './spec/controllers/projects/raw_controller_spec.rb'
+- './spec/controllers/projects/refs_controller_spec.rb'
+- './spec/controllers/projects/registry/repositories_controller_spec.rb'
+- './spec/controllers/projects/registry/tags_controller_spec.rb'
+- './spec/controllers/projects/releases_controller_spec.rb'
+- './spec/controllers/projects/releases/evidences_controller_spec.rb'
+- './spec/controllers/projects/repositories_controller_spec.rb'
+- './spec/controllers/projects/runners_controller_spec.rb'
+- './spec/controllers/projects/security/configuration_controller_spec.rb'
+- './spec/controllers/projects/service_desk_controller_spec.rb'
+- './spec/controllers/projects/service_ping_controller_spec.rb'
+- './spec/controllers/projects/settings/ci_cd_controller_spec.rb'
+- './spec/controllers/projects/settings/integration_hook_logs_controller_spec.rb'
+- './spec/controllers/projects/settings/integrations_controller_spec.rb'
+- './spec/controllers/projects/settings/operations_controller_spec.rb'
+- './spec/controllers/projects/settings/repository_controller_spec.rb'
+- './spec/controllers/projects/snippets/blobs_controller_spec.rb'
+- './spec/controllers/projects/snippets_controller_spec.rb'
+- './spec/controllers/projects/starrers_controller_spec.rb'
+- './spec/controllers/projects/tags_controller_spec.rb'
+- './spec/controllers/projects/templates_controller_spec.rb'
+- './spec/controllers/projects/terraform_controller_spec.rb'
+- './spec/controllers/projects/todos_controller_spec.rb'
+- './spec/controllers/projects/tree_controller_spec.rb'
+- './spec/controllers/projects/uploads_controller_spec.rb'
+- './spec/controllers/projects/usage_quotas_controller_spec.rb'
+- './spec/controllers/projects/variables_controller_spec.rb'
+- './spec/controllers/projects/web_ide_schemas_controller_spec.rb'
+- './spec/controllers/projects/web_ide_terminals_controller_spec.rb'
+- './spec/controllers/projects/wikis_controller_spec.rb'
+- './spec/controllers/registrations_controller_spec.rb'
+- './spec/controllers/registrations/welcome_controller_spec.rb'
+- './spec/controllers/repositories/git_http_controller_spec.rb'
+- './spec/controllers/repositories/lfs_storage_controller_spec.rb'
+- './spec/controllers/root_controller_spec.rb'
+- './spec/controllers/search_controller_spec.rb'
+- './spec/controllers/sent_notifications_controller_spec.rb'
+- './spec/controllers/sessions_controller_spec.rb'
+- './spec/controllers/snippets/blobs_controller_spec.rb'
+- './spec/controllers/snippets_controller_spec.rb'
+- './spec/controllers/snippets/notes_controller_spec.rb'
+- './spec/controllers/uploads_controller_spec.rb'
+- './spec/controllers/users/callouts_controller_spec.rb'
+- './spec/controllers/users/terms_controller_spec.rb'
+- './spec/controllers/users/unsubscribes_controller_spec.rb'
+- './spec/db/development/create_base_work_item_types_spec.rb'
+- './spec/db/development/import_common_metrics_spec.rb'
+- './spec/db/docs_spec.rb'
+- './spec/db/migration_spec.rb'
+- './spec/db/production/create_base_work_item_types_spec.rb'
+- './spec/db/production/import_common_metrics_spec.rb'
+- './spec/db/production/settings_spec.rb'
+- './spec/db/schema_spec.rb'
+- './spec/dependencies/omniauth_saml_spec.rb'
+- './spec/experiments/application_experiment_spec.rb'
+- './spec/experiments/concerns/project_commit_count_spec.rb'
+- './spec/experiments/force_company_trial_experiment_spec.rb'
+- './spec/experiments/in_product_guidance_environments_webide_experiment_spec.rb'
+- './spec/experiments/ios_specific_templates_experiment_spec.rb'
+- './spec/experiments/require_verification_for_namespace_creation_experiment_spec.rb'
+- './spec/experiments/security_reports_mr_widget_prompt_experiment_spec.rb'
+- './spec/experiments/video_tutorials_continuous_onboarding_experiment_spec.rb'
+- './spec/features/abuse_report_spec.rb'
+- './spec/features/action_cable_logging_spec.rb'
+- './spec/features/admin/admin_abuse_reports_spec.rb'
+- './spec/features/admin/admin_appearance_spec.rb'
+- './spec/features/admin/admin_broadcast_messages_spec.rb'
+- './spec/features/admin/admin_browse_spam_logs_spec.rb'
+- './spec/features/admin/admin_deploy_keys_spec.rb'
+- './spec/features/admin/admin_dev_ops_reports_spec.rb'
+- './spec/features/admin/admin_disables_git_access_protocol_spec.rb'
+- './spec/features/admin/admin_disables_two_factor_spec.rb'
+- './spec/features/admin/admin_groups_spec.rb'
+- './spec/features/admin/admin_health_check_spec.rb'
+- './spec/features/admin/admin_hook_logs_spec.rb'
+- './spec/features/admin/admin_hooks_spec.rb'
+- './spec/features/admin/admin_jobs_spec.rb'
+- './spec/features/admin/admin_labels_spec.rb'
+- './spec/features/admin/admin_manage_applications_spec.rb'
+- './spec/features/admin/admin_mode/login_spec.rb'
+- './spec/features/admin/admin_mode/logout_spec.rb'
+- './spec/features/admin/admin_mode_spec.rb'
+- './spec/features/admin/admin_mode/workers_spec.rb'
+- './spec/features/admin/admin_projects_spec.rb'
+- './spec/features/admin/admin_runners_spec.rb'
+- './spec/features/admin/admin_search_settings_spec.rb'
+- './spec/features/admin/admin_sees_background_migrations_spec.rb'
+- './spec/features/admin/admin_sees_projects_statistics_spec.rb'
+- './spec/features/admin/admin_sees_project_statistics_spec.rb'
+- './spec/features/admin/admin_settings_spec.rb'
+- './spec/features/admin/admin_system_info_spec.rb'
+- './spec/features/admin/admin_users_impersonation_tokens_spec.rb'
+- './spec/features/admin/admin_users_spec.rb'
+- './spec/features/admin/admin_uses_repository_checks_spec.rb'
+- './spec/features/admin/dashboard_spec.rb'
+- './spec/features/admin/integrations/instance_integrations_spec.rb'
+- './spec/features/admin/integrations/user_activates_mattermost_slash_command_spec.rb'
+- './spec/features/admin/users/user_spec.rb'
+- './spec/features/admin/users/users_spec.rb'
+- './spec/features/admin_variables_spec.rb'
+- './spec/features/alert_management/alert_details_spec.rb'
+- './spec/features/alert_management/alert_management_list_spec.rb'
+- './spec/features/alert_management_spec.rb'
+- './spec/features/alert_management/user_filters_alerts_by_status_spec.rb'
+- './spec/features/alert_management/user_searches_alerts_spec.rb'
+- './spec/features/alert_management/user_updates_alert_status_spec.rb'
+- './spec/features/alerts_settings/user_views_alerts_settings_spec.rb'
+- './spec/features/atom/dashboard_issues_spec.rb'
+- './spec/features/atom/dashboard_spec.rb'
+- './spec/features/atom/issues_spec.rb'
+- './spec/features/atom/merge_requests_spec.rb'
+- './spec/features/atom/users_spec.rb'
+- './spec/features/boards/board_filters_spec.rb'
+- './spec/features/boards/boards_spec.rb'
+- './spec/features/boards/focus_mode_spec.rb'
+- './spec/features/boards/issue_ordering_spec.rb'
+- './spec/features/boards/keyboard_shortcut_spec.rb'
+- './spec/features/boards/multiple_boards_spec.rb'
+- './spec/features/boards/multi_select_spec.rb'
+- './spec/features/boards/new_issue_spec.rb'
+- './spec/features/boards/reload_boards_on_browser_back_spec.rb'
+- './spec/features/boards/sidebar_assignee_spec.rb'
+- './spec/features/boards/sidebar_labels_in_namespaces_spec.rb'
+- './spec/features/boards/sidebar_labels_spec.rb'
+- './spec/features/boards/sidebar_spec.rb'
+- './spec/features/boards/user_adds_lists_to_board_spec.rb'
+- './spec/features/boards/user_visits_board_spec.rb'
+- './spec/features/breadcrumbs_schema_markup_spec.rb'
+- './spec/features/broadcast_messages_spec.rb'
+- './spec/features/calendar_spec.rb'
+- './spec/features/callouts/registration_enabled_spec.rb'
+- './spec/features/canonical_link_spec.rb'
+- './spec/features/clusters/cluster_detail_page_spec.rb'
+- './spec/features/clusters/cluster_health_dashboard_spec.rb'
+- './spec/features/clusters/create_agent_spec.rb'
+- './spec/features/commit_spec.rb'
+- './spec/features/commits_spec.rb'
+- './spec/features/commits/user_uses_quick_actions_spec.rb'
+- './spec/features/commits/user_view_commits_spec.rb'
+- './spec/features/contextual_sidebar_spec.rb'
+- './spec/features/cycle_analytics_spec.rb'
+- './spec/features/dashboard/activity_spec.rb'
+- './spec/features/dashboard/archived_projects_spec.rb'
+- './spec/features/dashboard/datetime_on_tooltips_spec.rb'
+- './spec/features/dashboard/group_dashboard_with_external_authorization_service_spec.rb'
+- './spec/features/dashboard/groups_list_spec.rb'
+- './spec/features/dashboard/group_spec.rb'
+- './spec/features/dashboard/issuables_counter_spec.rb'
+- './spec/features/dashboard/issues_filter_spec.rb'
+- './spec/features/dashboard/issues_spec.rb'
+- './spec/features/dashboard/label_filter_spec.rb'
+- './spec/features/dashboard/merge_requests_spec.rb'
+- './spec/features/dashboard/milestones_spec.rb'
+- './spec/features/dashboard/project_member_activity_index_spec.rb'
+- './spec/features/dashboard/projects_spec.rb'
+- './spec/features/dashboard/root_explore_spec.rb'
+- './spec/features/dashboard/shortcuts_spec.rb'
+- './spec/features/dashboard/snippets_spec.rb'
+- './spec/features/dashboard/todos/target_state_spec.rb'
+- './spec/features/dashboard/todos/todos_filtering_spec.rb'
+- './spec/features/dashboard/todos/todos_sorting_spec.rb'
+- './spec/features/dashboard/todos/todos_spec.rb'
+- './spec/features/dashboard/user_filters_projects_spec.rb'
+- './spec/features/discussion_comments/commit_spec.rb'
+- './spec/features/discussion_comments/issue_spec.rb'
+- './spec/features/discussion_comments/merge_request_spec.rb'
+- './spec/features/discussion_comments/snippets_spec.rb'
+- './spec/features/display_system_header_and_footer_bar_spec.rb'
+- './spec/features/error_pages_spec.rb'
+- './spec/features/error_tracking/user_filters_errors_by_status_spec.rb'
+- './spec/features/error_tracking/user_searches_sentry_errors_spec.rb'
+- './spec/features/error_tracking/user_sees_error_details_spec.rb'
+- './spec/features/error_tracking/user_sees_error_index_spec.rb'
+- './spec/features/expand_collapse_diffs_spec.rb'
+- './spec/features/explore/groups_list_spec.rb'
+- './spec/features/explore/groups_spec.rb'
+- './spec/features/explore/topics_spec.rb'
+- './spec/features/explore/user_explores_projects_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/multipart_invalid_uploads_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/file_uploads/user_avatar_spec.rb'
+- './spec/features/frequently_visited_projects_and_groups_spec.rb'
+- './spec/features/gitlab_experiments_spec.rb'
+- './spec/features/global_search_spec.rb'
+- './spec/features/graphiql_spec.rb'
+- './spec/features/graphql_known_operations_spec.rb'
+- './spec/features/groups/activity_spec.rb'
+- './spec/features/groups/board_sidebar_spec.rb'
+- './spec/features/groups/board_spec.rb'
+- './spec/features/groups/clusters/user_spec.rb'
+- './spec/features/groups/container_registry_spec.rb'
+- './spec/features/groups/crm/contacts/create_spec.rb'
+- './spec/features/groups/dependency_proxy_for_containers_spec.rb'
+- './spec/features/groups/dependency_proxy_spec.rb'
+- './spec/features/groups/empty_states_spec.rb'
+- './spec/features/groups/group_page_with_external_authorization_service_spec.rb'
+- './spec/features/groups/group_runners_spec.rb'
+- './spec/features/groups/group_settings_spec.rb'
+- './spec/features/groups/import_export/connect_instance_spec.rb'
+- './spec/features/groups/import_export/export_file_spec.rb'
+- './spec/features/groups/import_export/import_file_spec.rb'
+- './spec/features/groups/import_export/migration_history_spec.rb'
+- './spec/features/groups/integrations/group_integrations_spec.rb'
+- './spec/features/groups/integrations/user_activates_mattermost_slash_command_spec.rb'
+- './spec/features/groups/issues_spec.rb'
+- './spec/features/groups/labels/create_spec.rb'
+- './spec/features/groups/labels/edit_spec.rb'
+- './spec/features/groups/labels/index_spec.rb'
+- './spec/features/groups/labels/search_labels_spec.rb'
+- './spec/features/groups/labels/sort_labels_spec.rb'
+- './spec/features/groups/labels/subscription_spec.rb'
+- './spec/features/groups/labels/user_sees_links_to_issuables_spec.rb'
+- './spec/features/groups/members/filter_members_spec.rb'
+- './spec/features/groups/members/leave_group_spec.rb'
+- './spec/features/groups/members/list_members_spec.rb'
+- './spec/features/groups/members/manage_groups_spec.rb'
+- './spec/features/groups/members/manage_members_spec.rb'
+- './spec/features/groups/members/master_adds_member_with_expiration_date_spec.rb'
+- './spec/features/groups/members/master_manages_access_requests_spec.rb'
+- './spec/features/groups/members/request_access_spec.rb'
+- './spec/features/groups/members/search_members_spec.rb'
+- './spec/features/groups/members/sort_members_spec.rb'
+- './spec/features/groups/members/tabs_spec.rb'
+- './spec/features/groups/merge_requests_spec.rb'
+- './spec/features/groups/milestones/gfm_autocomplete_spec.rb'
+- './spec/features/groups/milestone_spec.rb'
+- './spec/features/groups/milestones_sorting_spec.rb'
+- './spec/features/groups/navbar_spec.rb'
+- './spec/features/groups/packages_spec.rb'
+- './spec/features/groups/settings/access_tokens_spec.rb'
+- './spec/features/groups/settings/ci_cd_spec.rb'
+- './spec/features/groups/settings/group_badges_spec.rb'
+- './spec/features/groups/settings/manage_applications_spec.rb'
+- './spec/features/groups/settings/packages_and_registries_spec.rb'
+- './spec/features/groups/settings/repository_spec.rb'
+- './spec/features/groups/settings/user_searches_in_settings_spec.rb'
+- './spec/features/groups/share_lock_spec.rb'
+- './spec/features/groups/show_spec.rb'
+- './spec/features/groups_spec.rb'
+- './spec/features/groups/user_browse_projects_group_page_spec.rb'
+- './spec/features/groups/user_sees_package_sidebar_spec.rb'
+- './spec/features/groups/user_sees_users_dropdowns_in_issuables_list_spec.rb'
+- './spec/features/group_variables_spec.rb'
+- './spec/features/help_dropdown_spec.rb'
+- './spec/features/help_pages_spec.rb'
+- './spec/features/ics/dashboard_issues_spec.rb'
+- './spec/features/ics/group_issues_spec.rb'
+- './spec/features/ics/project_issues_spec.rb'
+- './spec/features/ide/clientside_preview_csp_spec.rb'
+- './spec/features/ide_spec.rb'
+- './spec/features/ide/static_object_external_storage_csp_spec.rb'
+- './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/incidents/user_views_incident_spec.rb'
+- './spec/features/invites_spec.rb'
+- './spec/features/issuables/issuable_list_spec.rb'
+- './spec/features/issuables/markdown_references/internal_references_spec.rb'
+- './spec/features/issuables/markdown_references/jira_spec.rb'
+- './spec/features/issuables/shortcuts_issuable_spec.rb'
+- './spec/features/issuables/sorting_list_spec.rb'
+- './spec/features/issuables/user_sees_sidebar_spec.rb'
+- './spec/features/issue_rebalancing_spec.rb'
+- './spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb'
+- './spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb'
+- './spec/features/issues/csv_spec.rb'
+- './spec/features/issues/discussion_lock_spec.rb'
+- './spec/features/issues/filtered_search/dropdown_assignee_spec.rb'
+- './spec/features/issues/filtered_search/dropdown_author_spec.rb'
+- './spec/features/issues/filtered_search/dropdown_base_spec.rb'
+- './spec/features/issues/filtered_search/dropdown_emoji_spec.rb'
+- './spec/features/issues/filtered_search/dropdown_hint_spec.rb'
+- './spec/features/issues/filtered_search/dropdown_label_spec.rb'
+- './spec/features/issues/filtered_search/dropdown_milestone_spec.rb'
+- './spec/features/issues/filtered_search/dropdown_release_spec.rb'
+- './spec/features/issues/filtered_search/filter_issues_spec.rb'
+- './spec/features/issues/filtered_search/recent_searches_spec.rb'
+- './spec/features/issues/filtered_search/search_bar_spec.rb'
+- './spec/features/issues/filtered_search/visual_tokens_spec.rb'
+- './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'
+- './spec/features/issues/issue_state_spec.rb'
+- './spec/features/issues/keyboard_shortcut_spec.rb'
+- './spec/features/issues/markdown_toolbar_spec.rb'
+- './spec/features/issues/move_spec.rb'
+- './spec/features/issues/note_polling_spec.rb'
+- './spec/features/issues/notes_on_issues_spec.rb'
+- './spec/features/issues/related_issues_spec.rb'
+- './spec/features/issues/resource_label_events_spec.rb'
+- './spec/features/issues/rss_spec.rb'
+- './spec/features/issues/service_desk_spec.rb'
+- './spec/features/issues/spam_akismet_issue_creation_spec.rb'
+- './spec/features/issues/todo_spec.rb'
+- './spec/features/issues/user_bulk_edits_issues_labels_spec.rb'
+- './spec/features/issues/user_bulk_edits_issues_spec.rb'
+- './spec/features/issues/user_comments_on_issue_spec.rb'
+- './spec/features/issues/user_creates_branch_and_merge_request_spec.rb'
+- './spec/features/issues/user_creates_confidential_merge_request_spec.rb'
+- './spec/features/issues/user_creates_issue_by_email_spec.rb'
+- './spec/features/issues/user_creates_issue_spec.rb'
+- './spec/features/issues/user_edits_issue_spec.rb'
+- './spec/features/issues/user_filters_issues_spec.rb'
+- './spec/features/issues/user_interacts_with_awards_spec.rb'
+- './spec/features/issues/user_resets_their_incoming_email_token_spec.rb'
+- './spec/features/issues/user_scrolls_to_deeplinked_note_spec.rb'
+- './spec/features/issues/user_sees_breadcrumb_links_spec.rb'
+- './spec/features/issues/user_sees_empty_state_spec.rb'
+- './spec/features/issues/user_sees_live_update_spec.rb'
+- './spec/features/issues/user_sees_sidebar_updates_in_realtime_spec.rb'
+- './spec/features/issues/user_sorts_issue_comments_spec.rb'
+- './spec/features/issues/user_sorts_issues_spec.rb'
+- './spec/features/issues/user_toggles_subscription_spec.rb'
+- './spec/features/issues/user_uses_quick_actions_spec.rb'
+- './spec/features/issues/user_views_issue_spec.rb'
+- './spec/features/issues/user_views_issues_spec.rb'
+- './spec/features/jira_connect/branches_spec.rb'
+- './spec/features/jira_connect/subscriptions_spec.rb'
+- './spec/features/jira_oauth_provider_authorize_spec.rb'
+- './spec/features/labels_hierarchy_spec.rb'
+- './spec/features/markdown/copy_as_gfm_spec.rb'
+- './spec/features/markdown/gitlab_flavored_markdown_spec.rb'
+- './spec/features/markdown/json_table_spec.rb'
+- './spec/features/markdown/keyboard_shortcuts_spec.rb'
+- './spec/features/markdown/kroki_spec.rb'
+- './spec/features/markdown/markdown_spec.rb'
+- './spec/features/markdown/math_spec.rb'
+- './spec/features/markdown/metrics_spec.rb'
+- './spec/features/markdown/sandboxed_mermaid_spec.rb'
+- './spec/features/merge_request/batch_comments_spec.rb'
+- './spec/features/merge_request/close_reopen_report_toggle_spec.rb'
+- './spec/features/merge_request/maintainer_edits_fork_spec.rb'
+- './spec/features/merge_request/merge_request_discussion_lock_spec.rb'
+- './spec/features/merge_requests/filters_generic_behavior_spec.rb'
+- './spec/features/merge_requests/rss_spec.rb'
+- './spec/features/merge_requests/user_exports_as_csv_spec.rb'
+- './spec/features/merge_requests/user_filters_by_approvals_spec.rb'
+- './spec/features/merge_requests/user_filters_by_assignees_spec.rb'
+- './spec/features/merge_requests/user_filters_by_deployments_spec.rb'
+- './spec/features/merge_requests/user_filters_by_draft_spec.rb'
+- './spec/features/merge_requests/user_filters_by_labels_spec.rb'
+- './spec/features/merge_requests/user_filters_by_milestones_spec.rb'
+- './spec/features/merge_requests/user_filters_by_multiple_criteria_spec.rb'
+- './spec/features/merge_requests/user_filters_by_target_branch_spec.rb'
+- './spec/features/merge_requests/user_lists_merge_requests_spec.rb'
+- './spec/features/merge_requests/user_mass_updates_spec.rb'
+- './spec/features/merge_requests/user_sees_empty_state_spec.rb'
+- './spec/features/merge_requests/user_sorts_merge_requests_spec.rb'
+- './spec/features/merge_requests/user_views_all_merge_requests_spec.rb'
+- './spec/features/merge_requests/user_views_closed_merge_requests_spec.rb'
+- './spec/features/merge_requests/user_views_merged_merge_requests_spec.rb'
+- './spec/features/merge_requests/user_views_open_merge_requests_spec.rb'
+- './spec/features/merge_request/user_accepts_merge_request_spec.rb'
+- './spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb'
+- './spec/features/merge_request/user_approves_spec.rb'
+- './spec/features/merge_request/user_assigns_themselves_spec.rb'
+- './spec/features/merge_request/user_awards_emoji_spec.rb'
+- './spec/features/merge_request/user_clicks_merge_request_tabs_spec.rb'
+- './spec/features/merge_request/user_closes_reopens_merge_request_state_spec.rb'
+- './spec/features/merge_request/user_comments_on_commit_spec.rb'
+- './spec/features/merge_request/user_comments_on_diff_spec.rb'
+- './spec/features/merge_request/user_comments_on_merge_request_spec.rb'
+- './spec/features/merge_request/user_creates_image_diff_notes_spec.rb'
+- './spec/features/merge_request/user_creates_merge_request_spec.rb'
+- './spec/features/merge_request/user_creates_mr_spec.rb'
+- './spec/features/merge_request/user_customizes_merge_commit_message_spec.rb'
+- './spec/features/merge_request/user_edits_assignees_sidebar_spec.rb'
+- './spec/features/merge_request/user_edits_merge_request_spec.rb'
+- './spec/features/merge_request/user_edits_mr_spec.rb'
+- './spec/features/merge_request/user_edits_reviewers_sidebar_spec.rb'
+- './spec/features/merge_request/user_expands_diff_spec.rb'
+- './spec/features/merge_request/user_interacts_with_batched_mr_diffs_spec.rb'
+- './spec/features/merge_request/user_jumps_to_discussion_spec.rb'
+- './spec/features/merge_request/user_locks_discussion_spec.rb'
+- './spec/features/merge_request/user_manages_subscription_spec.rb'
+- './spec/features/merge_request/user_marks_merge_request_as_draft_spec.rb'
+- './spec/features/merge_request/user_merges_immediately_spec.rb'
+- './spec/features/merge_request/user_merges_merge_request_spec.rb'
+- './spec/features/merge_request/user_merges_only_if_pipeline_succeeds_spec.rb'
+- './spec/features/merge_request/user_merges_when_pipeline_succeeds_spec.rb'
+- './spec/features/merge_request/user_opens_checkout_branch_modal_spec.rb'
+- './spec/features/merge_request/user_opens_context_commits_modal_spec.rb'
+- './spec/features/merge_request/user_posts_diff_notes_spec.rb'
+- './spec/features/merge_request/user_posts_notes_spec.rb'
+- './spec/features/merge_request/user_rebases_merge_request_spec.rb'
+- './spec/features/merge_request/user_resolves_conflicts_spec.rb'
+- './spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb'
+- './spec/features/merge_request/user_resolves_outdated_diff_discussions_spec.rb'
+- './spec/features/merge_request/user_resolves_wip_mr_spec.rb'
+- './spec/features/merge_request/user_reverts_merge_request_spec.rb'
+- './spec/features/merge_request/user_reviews_image_spec.rb'
+- './spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb'
+- './spec/features/merge_request/user_sees_avatar_on_diff_notes_spec.rb'
+- './spec/features/merge_request/user_sees_breadcrumb_links_spec.rb'
+- './spec/features/merge_request/user_sees_check_out_branch_modal_spec.rb'
+- './spec/features/merge_request/user_sees_cherry_pick_modal_spec.rb'
+- './spec/features/merge_request/user_sees_closing_issues_message_spec.rb'
+- './spec/features/merge_request/user_sees_deleted_target_branch_spec.rb'
+- './spec/features/merge_request/user_sees_deployment_widget_spec.rb'
+- './spec/features/merge_request/user_sees_diff_spec.rb'
+- './spec/features/merge_request/user_sees_discussions_spec.rb'
+- './spec/features/merge_request/user_sees_merge_button_depending_on_unresolved_discussions_spec.rb'
+- './spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb'
+- './spec/features/merge_request/user_sees_merge_widget_spec.rb'
+- './spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb'
+- './spec/features/merge_request/user_sees_mr_from_deleted_forked_project_spec.rb'
+- './spec/features/merge_request/user_sees_mr_with_deleted_source_branch_spec.rb'
+- './spec/features/merge_request/user_sees_notes_from_forked_project_spec.rb'
+- './spec/features/merge_request/user_sees_page_metadata_spec.rb'
+- './spec/features/merge_request/user_sees_pipelines_from_forked_project_spec.rb'
+- './spec/features/merge_request/user_sees_pipelines_spec.rb'
+- './spec/features/merge_request/user_sees_suggest_pipeline_spec.rb'
+- './spec/features/merge_request/user_sees_system_notes_spec.rb'
+- './spec/features/merge_request/user_sees_versions_spec.rb'
+- './spec/features/merge_request/user_sees_wip_help_message_spec.rb'
+- './spec/features/merge_request/user_selects_branches_for_new_mr_spec.rb'
+- './spec/features/merge_request/user_squashes_merge_request_spec.rb'
+- './spec/features/merge_request/user_suggests_changes_on_diff_spec.rb'
+- './spec/features/merge_request/user_toggles_whitespace_changes_spec.rb'
+- './spec/features/merge_request/user_tries_to_access_private_project_info_through_new_mr_spec.rb'
+- './spec/features/merge_request/user_uses_quick_actions_spec.rb'
+- './spec/features/merge_request/user_views_auto_expanding_diff_spec.rb'
+- './spec/features/merge_request/user_views_diffs_commit_spec.rb'
+- './spec/features/merge_request/user_views_diffs_file_by_file_spec.rb'
+- './spec/features/merge_request/user_views_diffs_spec.rb'
+- './spec/features/merge_request/user_views_merge_request_from_deleted_fork_spec.rb'
+- './spec/features/merge_request/user_views_open_merge_request_spec.rb'
+- './spec/features/merge_request/user_views_user_status_on_merge_request_spec.rb'
+- './spec/features/milestone_spec.rb'
+- './spec/features/milestones/user_creates_milestone_spec.rb'
+- './spec/features/milestones/user_deletes_milestone_spec.rb'
+- './spec/features/milestones/user_edits_milestone_spec.rb'
+- './spec/features/milestones/user_promotes_milestone_spec.rb'
+- './spec/features/milestones/user_sees_breadcrumb_links_spec.rb'
+- './spec/features/milestones/user_views_milestone_spec.rb'
+- './spec/features/milestones/user_views_milestones_spec.rb'
+- './spec/features/monitor_sidebar_link_spec.rb'
+- './spec/features/nav/top_nav_responsive_spec.rb'
+- './spec/features/nav/top_nav_tooltip_spec.rb'
+- './spec/features/oauth_login_spec.rb'
+- './spec/features/oauth_provider_authorize_spec.rb'
+- './spec/features/oauth_registration_spec.rb'
+- './spec/features/one_trust_spec.rb'
+- './spec/features/participants_autocomplete_spec.rb'
+- './spec/features/password_reset_spec.rb'
+- './spec/features/populate_new_pipeline_vars_with_params_spec.rb'
+- './spec/features/profiles/account_spec.rb'
+- './spec/features/profiles/active_sessions_spec.rb'
+- './spec/features/profiles/chat_names_spec.rb'
+- './spec/features/profiles/emails_spec.rb'
+- './spec/features/profiles/gpg_keys_spec.rb'
+- './spec/features/profiles/keys_spec.rb'
+- './spec/features/profiles/oauth_applications_spec.rb'
+- './spec/features/profiles/password_spec.rb'
+- './spec/features/profile_spec.rb'
+- './spec/features/profiles/personal_access_tokens_spec.rb'
+- './spec/features/profiles/two_factor_auths_spec.rb'
+- './spec/features/profiles/user_changes_notified_of_own_activity_spec.rb'
+- './spec/features/profiles/user_edit_preferences_spec.rb'
+- './spec/features/profiles/user_edit_profile_spec.rb'
+- './spec/features/profiles/user_manages_applications_spec.rb'
+- './spec/features/profiles/user_manages_emails_spec.rb'
+- './spec/features/profiles/user_search_settings_spec.rb'
+- './spec/features/profiles/user_visits_notifications_tab_spec.rb'
+- './spec/features/profiles/user_visits_profile_account_page_spec.rb'
+- './spec/features/profiles/user_visits_profile_authentication_log_spec.rb'
+- './spec/features/profiles/user_visits_profile_preferences_page_spec.rb'
+- './spec/features/profiles/user_visits_profile_spec.rb'
+- './spec/features/profiles/user_visits_profile_ssh_keys_page_spec.rb'
+- './spec/features/project_group_variables_spec.rb'
+- './spec/features/projects/active_tabs_spec.rb'
+- './spec/features/projects/activity/rss_spec.rb'
+- './spec/features/projects/activity/user_sees_activity_spec.rb'
+- './spec/features/projects/activity/user_sees_design_activity_spec.rb'
+- './spec/features/projects/activity/user_sees_design_comment_spec.rb'
+- './spec/features/projects/activity/user_sees_private_activity_spec.rb'
+- './spec/features/projects/artifacts/file_spec.rb'
+- './spec/features/projects/artifacts/raw_spec.rb'
+- './spec/features/projects/artifacts/user_browses_artifacts_spec.rb'
+- './spec/features/projects/artifacts/user_downloads_artifacts_spec.rb'
+- './spec/features/projects/badges/coverage_spec.rb'
+- './spec/features/projects/badges/list_spec.rb'
+- './spec/features/projects/badges/pipeline_badge_spec.rb'
+- './spec/features/projects/blobs/blame_spec.rb'
+- './spec/features/projects/blobs/blob_line_permalink_updater_spec.rb'
+- './spec/features/projects/blobs/blob_show_spec.rb'
+- './spec/features/projects/blobs/edit_spec.rb'
+- './spec/features/projects/blobs/shortcuts_blob_spec.rb'
+- './spec/features/projects/blobs/user_creates_new_blob_in_new_project_spec.rb'
+- './spec/features/projects/blobs/user_follows_pipeline_suggest_nudge_spec.rb'
+- './spec/features/projects/blobs/user_views_pipeline_editor_button_spec.rb'
+- './spec/features/projects/branches/download_buttons_spec.rb'
+- './spec/features/projects/branches/new_branch_ref_dropdown_spec.rb'
+- './spec/features/projects/branches_spec.rb'
+- './spec/features/projects/branches/user_creates_branch_spec.rb'
+- './spec/features/projects/branches/user_deletes_branch_spec.rb'
+- './spec/features/projects/branches/user_views_branches_spec.rb'
+- './spec/features/projects/ci/editor_spec.rb'
+- './spec/features/projects/ci/lint_spec.rb'
+- './spec/features/projects/classification_label_on_project_pages_spec.rb'
+- './spec/features/projects/cluster_agents_spec.rb'
+- './spec/features/projects/clusters/gcp_spec.rb'
+- './spec/features/projects/clusters_spec.rb'
+- './spec/features/projects/clusters/user_spec.rb'
+- './spec/features/projects/commit/builds_spec.rb'
+- './spec/features/projects/commit/cherry_pick_spec.rb'
+- './spec/features/projects/commit/comments/user_adds_comment_spec.rb'
+- './spec/features/projects/commit/comments/user_deletes_comments_spec.rb'
+- './spec/features/projects/commit/comments/user_edits_comments_spec.rb'
+- './spec/features/projects/commit/diff_notes_spec.rb'
+- './spec/features/projects/commit/mini_pipeline_graph_spec.rb'
+- './spec/features/projects/commits/multi_view_diff_spec.rb'
+- './spec/features/projects/commits/rss_spec.rb'
+- './spec/features/projects/commits/user_browses_commits_spec.rb'
+- './spec/features/projects/commit/user_comments_on_commit_spec.rb'
+- './spec/features/projects/commit/user_reverts_commit_spec.rb'
+- './spec/features/projects/commit/user_views_user_status_on_commit_spec.rb'
+- './spec/features/projects/compare_spec.rb'
+- './spec/features/projects/confluence/user_views_confluence_page_spec.rb'
+- './spec/features/projects/container_registry_spec.rb'
+- './spec/features/projects/deploy_keys_spec.rb'
+- './spec/features/projects/diffs/diff_show_spec.rb'
+- './spec/features/projects/environments/environment_metrics_spec.rb'
+- './spec/features/projects/environments/environment_spec.rb'
+- './spec/features/projects/environments/environments_spec.rb'
+- './spec/features/projects/feature_flags/user_creates_feature_flag_spec.rb'
+- './spec/features/projects/feature_flags/user_deletes_feature_flag_spec.rb'
+- './spec/features/projects/feature_flags/user_sees_feature_flag_list_spec.rb'
+- './spec/features/projects/feature_flags/user_updates_feature_flag_spec.rb'
+- './spec/features/projects/feature_flag_user_lists/user_deletes_feature_flag_user_list_spec.rb'
+- './spec/features/projects/feature_flag_user_lists/user_edits_feature_flag_user_list_spec.rb'
+- './spec/features/projects/feature_flag_user_lists/user_sees_feature_flag_user_list_details_spec.rb'
+- './spec/features/projects/features_visibility_spec.rb'
+- './spec/features/projects/files/dockerfile_dropdown_spec.rb'
+- './spec/features/projects/files/download_buttons_spec.rb'
+- './spec/features/projects/files/edit_file_soft_wrap_spec.rb'
+- './spec/features/projects/files/editing_a_file_spec.rb'
+- './spec/features/projects/files/files_sort_submodules_with_folders_spec.rb'
+- './spec/features/projects/files/find_file_keyboard_spec.rb'
+- './spec/features/projects/files/gitignore_dropdown_spec.rb'
+- './spec/features/projects/files/gitlab_ci_yml_dropdown_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/template_selector_menu_spec.rb'
+- './spec/features/projects/files/template_type_dropdown_spec.rb'
+- './spec/features/projects/files/undo_template_spec.rb'
+- './spec/features/projects/files/user_browses_a_tree_with_a_folder_containing_only_a_folder_spec.rb'
+- './spec/features/projects/files/user_browses_files_spec.rb'
+- './spec/features/projects/files/user_browses_lfs_files_spec.rb'
+- './spec/features/projects/files/user_creates_directory_spec.rb'
+- './spec/features/projects/files/user_creates_files_spec.rb'
+- './spec/features/projects/files/user_deletes_files_spec.rb'
+- './spec/features/projects/files/user_edits_files_spec.rb'
+- './spec/features/projects/files/user_find_file_spec.rb'
+- './spec/features/projects/files/user_reads_pipeline_status_spec.rb'
+- './spec/features/projects/files/user_replaces_files_spec.rb'
+- './spec/features/projects/files/user_searches_for_files_spec.rb'
+- './spec/features/projects/files/user_uploads_files_spec.rb'
+- './spec/features/projects/forks/fork_list_spec.rb'
+- './spec/features/projects/fork_spec.rb'
+- './spec/features/projects/gfm_autocomplete_load_spec.rb'
+- './spec/features/projects/graph_spec.rb'
+- './spec/features/projects/hook_logs/user_reads_log_spec.rb'
+- './spec/features/projects/import_export/export_file_spec.rb'
+- './spec/features/projects/import_export/import_file_spec.rb'
+- './spec/features/projects/infrastructure_registry_spec.rb'
+- './spec/features/projects/integrations/disable_triggers_spec.rb'
+- './spec/features/projects/integrations/project_integrations_spec.rb'
+- './spec/features/projects/integrations/user_activates_asana_spec.rb'
+- './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'
+- './spec/features/projects/integrations/user_activates_jira_spec.rb'
+- './spec/features/projects/integrations/user_activates_mattermost_slash_command_spec.rb'
+- './spec/features/projects/integrations/user_activates_packagist_spec.rb'
+- './spec/features/projects/integrations/user_activates_pivotaltracker_spec.rb'
+- './spec/features/projects/integrations/user_activates_prometheus_spec.rb'
+- './spec/features/projects/integrations/user_activates_pushover_spec.rb'
+- './spec/features/projects/integrations/user_activates_slack_notifications_spec.rb'
+- './spec/features/projects/integrations/user_activates_slack_slash_command_spec.rb'
+- './spec/features/projects/integrations/user_uses_inherited_settings_spec.rb'
+- './spec/features/projects/integrations/user_views_services_spec.rb'
+- './spec/features/projects/issuable_templates_spec.rb'
+- './spec/features/projects/issues/design_management/user_links_to_designs_in_issue_spec.rb'
+- './spec/features/projects/issues/design_management/user_paginates_designs_spec.rb'
+- './spec/features/projects/issues/design_management/user_permissions_upload_spec.rb'
+- './spec/features/projects/issues/design_management/user_uploads_designs_spec.rb'
+- './spec/features/projects/issues/design_management/user_views_design_images_spec.rb'
+- './spec/features/projects/issues/design_management/user_views_design_spec.rb'
+- './spec/features/projects/issues/design_management/user_views_designs_spec.rb'
+- './spec/features/projects/issues/design_management/user_views_designs_with_svg_xss_spec.rb'
+- './spec/features/projects/issues/email_participants_spec.rb'
+- './spec/features/projects/issues/viewing_issues_with_external_authorization_enabled_spec.rb'
+- './spec/features/projects/issues/viewing_relocated_issues_spec.rb'
+- './spec/features/projects/jobs/permissions_spec.rb'
+- './spec/features/projects/jobs_spec.rb'
+- './spec/features/projects/jobs/user_browses_job_spec.rb'
+- './spec/features/projects/jobs/user_browses_jobs_spec.rb'
+- './spec/features/projects/jobs/user_triggers_manual_job_with_variables_spec.rb'
+- './spec/features/projects/labels/issues_sorted_by_priority_spec.rb'
+- './spec/features/projects/labels/search_labels_spec.rb'
+- './spec/features/projects/labels/sort_labels_spec.rb'
+- './spec/features/projects/labels/subscription_spec.rb'
+- './spec/features/projects/labels/update_prioritization_spec.rb'
+- './spec/features/projects/labels/user_creates_labels_spec.rb'
+- './spec/features/projects/labels/user_edits_labels_spec.rb'
+- './spec/features/projects/labels/user_promotes_label_spec.rb'
+- './spec/features/projects/labels/user_removes_labels_spec.rb'
+- './spec/features/projects/labels/user_sees_breadcrumb_links_spec.rb'
+- './spec/features/projects/labels/user_sees_links_to_issuables_spec.rb'
+- './spec/features/projects/labels/user_views_labels_spec.rb'
+- './spec/features/projects/members/anonymous_user_sees_members_spec.rb'
+- './spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb'
+- './spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb'
+- './spec/features/projects/members/group_members_spec.rb'
+- './spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb'
+- './spec/features/projects/members/groups_with_access_list_spec.rb'
+- './spec/features/projects/members/manage_groups_spec.rb'
+- './spec/features/projects/members/manage_members_spec.rb'
+- './spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb'
+- './spec/features/projects/members/master_manages_access_requests_spec.rb'
+- './spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb'
+- './spec/features/projects/members/member_leaves_project_spec.rb'
+- './spec/features/projects/members/owner_cannot_leave_project_spec.rb'
+- './spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb'
+- './spec/features/projects/members/sorting_spec.rb'
+- './spec/features/projects/members/tabs_spec.rb'
+- './spec/features/projects/members/user_requests_access_spec.rb'
+- './spec/features/projects/merge_request_button_spec.rb'
+- './spec/features/projects/milestones/gfm_autocomplete_spec.rb'
+- './spec/features/projects/milestones/milestone_spec.rb'
+- './spec/features/projects/milestones/milestones_sorting_spec.rb'
+- './spec/features/projects/milestones/new_spec.rb'
+- './spec/features/projects/milestones/user_interacts_with_labels_spec.rb'
+- './spec/features/projects/navbar_spec.rb'
+- './spec/features/projects/network_graph_spec.rb'
+- './spec/features/projects/new_project_from_template_spec.rb'
+- './spec/features/projects/new_project_spec.rb'
+- './spec/features/projects/package_files_spec.rb'
+- './spec/features/projects/packages_spec.rb'
+- './spec/features/projects/pages/user_adds_domain_spec.rb'
+- './spec/features/projects/pages/user_configures_pages_pipeline_spec.rb'
+- './spec/features/projects/pages/user_edits_lets_encrypt_settings_spec.rb'
+- './spec/features/projects/pages/user_edits_settings_spec.rb'
+- './spec/features/projects/pipeline_schedules_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/projects/product_analytics/events_spec.rb'
+- './spec/features/projects/product_analytics/graphs_spec.rb'
+- './spec/features/projects/product_analytics/setup_spec.rb'
+- './spec/features/projects/product_analytics/test_spec.rb'
+- './spec/features/projects/raw/user_interacts_with_raw_endpoint_spec.rb'
+- './spec/features/projects/releases/user_creates_release_spec.rb'
+- './spec/features/projects/releases/user_views_edit_release_spec.rb'
+- './spec/features/projects/releases/user_views_release_spec.rb'
+- './spec/features/projects/releases/user_views_releases_spec.rb'
+- './spec/features/projects/remote_mirror_spec.rb'
+- './spec/features/projects/settings/access_tokens_spec.rb'
+- './spec/features/projects/settings/branch_rules_settings_spec.rb'
+- './spec/features/projects/settings/external_authorization_service_settings_spec.rb'
+- './spec/features/projects/settings/forked_project_settings_spec.rb'
+- './spec/features/projects/settings/lfs_settings_spec.rb'
+- './spec/features/projects/settings/monitor_settings_spec.rb'
+- './spec/features/projects/settings/packages_settings_spec.rb'
+- './spec/features/projects/settings/pipelines_settings_spec.rb'
+- './spec/features/projects/settings/project_badges_spec.rb'
+- './spec/features/projects/settings/project_settings_spec.rb'
+- './spec/features/projects/settings/registry_settings_cleanup_tags_spec.rb'
+- './spec/features/projects/settings/registry_settings_spec.rb'
+- './spec/features/projects/settings/repository_settings_spec.rb'
+- './spec/features/projects/settings/secure_files_spec.rb'
+- './spec/features/projects/settings/service_desk_setting_spec.rb'
+- './spec/features/projects/settings/user_archives_project_spec.rb'
+- './spec/features/projects/settings/user_changes_avatar_spec.rb'
+- './spec/features/projects/settings/user_changes_default_branch_spec.rb'
+- './spec/features/projects/settings/user_interacts_with_deploy_keys_spec.rb'
+- './spec/features/projects/settings/user_manages_merge_requests_settings_spec.rb'
+- './spec/features/projects/settings/user_manages_project_members_spec.rb'
+- './spec/features/projects/settings/user_renames_a_project_spec.rb'
+- './spec/features/projects/settings/user_searches_in_settings_spec.rb'
+- './spec/features/projects/settings/user_sees_revoke_deploy_token_modal_spec.rb'
+- './spec/features/projects/settings/user_tags_project_spec.rb'
+- './spec/features/projects/settings/user_transfers_a_project_spec.rb'
+- './spec/features/projects/settings/visibility_settings_spec.rb'
+- './spec/features/projects/settings/webhooks_settings_spec.rb'
+- './spec/features/projects/show/download_buttons_spec.rb'
+- './spec/features/projects/show/no_password_spec.rb'
+- './spec/features/projects/show/redirects_spec.rb'
+- './spec/features/projects/show/rss_spec.rb'
+- './spec/features/projects/show/schema_markup_spec.rb'
+- './spec/features/projects/show/user_interacts_with_auto_devops_banner_spec.rb'
+- './spec/features/projects/show/user_interacts_with_stars_spec.rb'
+- './spec/features/projects/show/user_manages_notifications_spec.rb'
+- './spec/features/projects/show/user_sees_collaboration_links_spec.rb'
+- './spec/features/projects/show/user_sees_deletion_failure_message_spec.rb'
+- './spec/features/projects/show/user_sees_git_instructions_spec.rb'
+- './spec/features/projects/show/user_sees_last_commit_ci_status_spec.rb'
+- './spec/features/projects/show/user_sees_readme_spec.rb'
+- './spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb'
+- './spec/features/projects/show/user_uploads_files_spec.rb'
+- './spec/features/projects/snippets/create_snippet_spec.rb'
+- './spec/features/projects/snippets/show_spec.rb'
+- './spec/features/projects/snippets/user_comments_on_snippet_spec.rb'
+- './spec/features/projects/snippets/user_deletes_snippet_spec.rb'
+- './spec/features/projects/snippets/user_updates_snippet_spec.rb'
+- './spec/features/projects/snippets/user_views_snippets_spec.rb'
+- './spec/features/projects/sourcegraph_csp_spec.rb'
+- './spec/features/projects_spec.rb'
+- './spec/features/projects/sub_group_issuables_spec.rb'
+- './spec/features/projects/tags/download_buttons_spec.rb'
+- './spec/features/projects/tags/user_edits_tags_spec.rb'
+- './spec/features/projects/tags/user_views_tag_spec.rb'
+- './spec/features/projects/tags/user_views_tags_spec.rb'
+- './spec/features/projects/terraform_spec.rb'
+- './spec/features/projects/tree/create_directory_spec.rb'
+- './spec/features/projects/tree/create_file_spec.rb'
+- './spec/features/projects/tree/rss_spec.rb'
+- './spec/features/projects/tree/tree_show_spec.rb'
+- './spec/features/projects/tree/upload_file_spec.rb'
+- './spec/features/projects/user_changes_project_visibility_spec.rb'
+- './spec/features/projects/user_creates_project_spec.rb'
+- './spec/features/projects/user_sees_sidebar_spec.rb'
+- './spec/features/projects/user_sees_user_popover_spec.rb'
+- './spec/features/projects/user_sorts_projects_spec.rb'
+- './spec/features/projects/user_uses_shortcuts_spec.rb'
+- './spec/features/projects/user_views_empty_project_spec.rb'
+- './spec/features/projects/view_on_env_spec.rb'
+- './spec/features/projects/wikis_spec.rb'
+- './spec/features/projects/wiki/user_views_wiki_empty_spec.rb'
+- './spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb'
+- './spec/features/project_variables_spec.rb'
+- './spec/features/promotion_spec.rb'
+- './spec/features/protected_branches_spec.rb'
+- './spec/features/protected_tags_spec.rb'
+- './spec/features/read_only_spec.rb'
+- './spec/features/reportable_note/commit_spec.rb'
+- './spec/features/reportable_note/issue_spec.rb'
+- './spec/features/reportable_note/merge_request_spec.rb'
+- './spec/features/reportable_note/snippets_spec.rb'
+- './spec/features/runners_spec.rb'
+- './spec/features/search/user_searches_for_code_spec.rb'
+- './spec/features/search/user_searches_for_comments_spec.rb'
+- './spec/features/search/user_searches_for_commits_spec.rb'
+- './spec/features/search/user_searches_for_issues_spec.rb'
+- './spec/features/search/user_searches_for_merge_requests_spec.rb'
+- './spec/features/search/user_searches_for_milestones_spec.rb'
+- './spec/features/search/user_searches_for_projects_spec.rb'
+- './spec/features/search/user_searches_for_users_spec.rb'
+- './spec/features/search/user_searches_for_wiki_pages_spec.rb'
+- './spec/features/search/user_uses_header_search_field_spec.rb'
+- './spec/features/search/user_uses_search_filters_spec.rb'
+- './spec/features/security/admin_access_spec.rb'
+- './spec/features/security/dashboard_access_spec.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/features/security/profile_access_spec.rb'
+- './spec/features/security/project/internal_access_spec.rb'
+- './spec/features/security/project/private_access_spec.rb'
+- './spec/features/security/project/public_access_spec.rb'
+- './spec/features/security/project/snippet/internal_access_spec.rb'
+- './spec/features/security/project/snippet/private_access_spec.rb'
+- './spec/features/security/project/snippet/public_access_spec.rb'
+- './spec/features/sentry_js_spec.rb'
+- './spec/features/signed_commits_spec.rb'
+- './spec/features/snippets/embedded_snippet_spec.rb'
+- './spec/features/snippets/explore_spec.rb'
+- './spec/features/snippets/internal_snippet_spec.rb'
+- './spec/features/snippets/notes_on_personal_snippets_spec.rb'
+- './spec/features/snippets/private_snippets_spec.rb'
+- './spec/features/snippets/public_snippets_spec.rb'
+- './spec/features/snippets/search_snippets_spec.rb'
+- './spec/features/snippets/show_spec.rb'
+- './spec/features/snippets/spam_snippets_spec.rb'
+- './spec/features/snippets_spec.rb'
+- './spec/features/snippets/user_creates_snippet_spec.rb'
+- './spec/features/snippets/user_deletes_snippet_spec.rb'
+- './spec/features/snippets/user_edits_snippet_spec.rb'
+- './spec/features/snippets/user_snippets_spec.rb'
+- './spec/features/tags/developer_creates_tag_spec.rb'
+- './spec/features/tags/developer_deletes_tag_spec.rb'
+- './spec/features/tags/developer_views_tags_spec.rb'
+- './spec/features/tags/maintainer_deletes_protected_tag_spec.rb'
+- './spec/features/task_lists_spec.rb'
+- './spec/features/topic_show_spec.rb'
+- './spec/features/triggers_spec.rb'
+- './spec/features/u2f_spec.rb'
+- './spec/features/unsubscribe_links_spec.rb'
+- './spec/features/uploads/user_uploads_avatar_to_group_spec.rb'
+- './spec/features/uploads/user_uploads_avatar_to_profile_spec.rb'
+- './spec/features/uploads/user_uploads_file_to_note_spec.rb'
+- './spec/features/usage_stats_consent_spec.rb'
+- './spec/features/user_can_display_performance_bar_spec.rb'
+- './spec/features/user_opens_link_to_comment_spec.rb'
+- './spec/features/users/active_sessions_spec.rb'
+- './spec/features/users/add_email_to_existing_account_spec.rb'
+- './spec/features/users/anonymous_sessions_spec.rb'
+- './spec/features/users/bizible_csp_spec.rb'
+- './spec/features/users/confirmation_spec.rb'
+- './spec/features/user_sees_marketing_header_spec.rb'
+- './spec/features/user_sees_revert_modal_spec.rb'
+- './spec/features/users/email_verification_on_login_spec.rb'
+- './spec/features/users/google_analytics_csp_spec.rb'
+- './spec/features/users/login_spec.rb'
+- './spec/features/users/logout_spec.rb'
+- './spec/features/users/one_trust_csp_spec.rb'
+- './spec/features/user_sorts_things_spec.rb'
+- './spec/features/users/overview_spec.rb'
+- './spec/features/users/password_spec.rb'
+- './spec/features/users/rss_spec.rb'
+- './spec/features/users/show_spec.rb'
+- './spec/features/users/signup_spec.rb'
+- './spec/features/users/snippets_spec.rb'
+- './spec/features/users/terms_spec.rb'
+- './spec/features/users/user_browses_projects_on_user_page_spec.rb'
+- './spec/features/users/zuora_csp_spec.rb'
+- './spec/features/webauthn_spec.rb'
+- './spec/features/whats_new_spec.rb'
+- './spec/features/work_items/work_item_children_spec.rb'
+- './spec/finders/abuse_reports_finder_spec.rb'
+- './spec/finders/access_requests_finder_spec.rb'
+- './spec/finders/admin/plans_finder_spec.rb'
+- './spec/finders/admin/projects_finder_spec.rb'
+- './spec/finders/alert_management/alerts_finder_spec.rb'
+- './spec/finders/alert_management/http_integrations_finder_spec.rb'
+- './spec/finders/analytics/cycle_analytics/stage_finder_spec.rb'
+- './spec/finders/applications_finder_spec.rb'
+- './spec/finders/autocomplete/acts_as_taggable_on/tags_finder_spec.rb'
+- './spec/finders/autocomplete/deploy_keys_with_write_access_finder_spec.rb'
+- './spec/finders/autocomplete/group_finder_spec.rb'
+- './spec/finders/autocomplete/move_to_project_finder_spec.rb'
+- './spec/finders/autocomplete/project_finder_spec.rb'
+- './spec/finders/autocomplete/routes_finder_spec.rb'
+- './spec/finders/autocomplete/users_finder_spec.rb'
+- './spec/finders/award_emojis_finder_spec.rb'
+- './spec/finders/boards/boards_finder_spec.rb'
+- './spec/finders/boards/visits_finder_spec.rb'
+- './spec/finders/branches_finder_spec.rb'
+- './spec/finders/bulk_imports/entities_finder_spec.rb'
+- './spec/finders/bulk_imports/imports_finder_spec.rb'
+- './spec/finders/ci/auth_job_finder_spec.rb'
+- './spec/finders/ci/commit_statuses_finder_spec.rb'
+- './spec/finders/ci/daily_build_group_report_results_finder_spec.rb'
+- './spec/finders/ci/job_artifacts_finder_spec.rb'
+- './spec/finders/ci/jobs_finder_spec.rb'
+- './spec/finders/ci/pipeline_schedules_finder_spec.rb'
+- './spec/finders/ci/pipelines_finder_spec.rb'
+- './spec/finders/ci/pipelines_for_merge_request_finder_spec.rb'
+- './spec/finders/ci/runner_jobs_finder_spec.rb'
+- './spec/finders/ci/runners_finder_spec.rb'
+- './spec/finders/ci/variables_finder_spec.rb'
+- './spec/finders/cluster_ancestors_finder_spec.rb'
+- './spec/finders/clusters/agent_authorizations_finder_spec.rb'
+- './spec/finders/clusters/agents_finder_spec.rb'
+- './spec/finders/clusters_finder_spec.rb'
+- './spec/finders/clusters/knative_services_finder_spec.rb'
+- './spec/finders/clusters/kubernetes_namespace_finder_spec.rb'
+- './spec/finders/concerns/finder_methods_spec.rb'
+- './spec/finders/concerns/finder_with_cross_project_access_spec.rb'
+- './spec/finders/concerns/finder_with_group_hierarchy_spec.rb'
+- './spec/finders/concerns/packages/finder_helper_spec.rb'
+- './spec/finders/container_repositories_finder_spec.rb'
+- './spec/finders/context_commits_finder_spec.rb'
+- './spec/finders/contributed_projects_finder_spec.rb'
+- './spec/finders/crm/contacts_finder_spec.rb'
+- './spec/finders/crm/organizations_finder_spec.rb'
+- './spec/finders/database/batched_background_migrations_finder_spec.rb'
+- './spec/finders/deployments_finder_spec.rb'
+- './spec/finders/deploy_tokens/tokens_finder_spec.rb'
+- './spec/finders/design_management/designs_finder_spec.rb'
+- './spec/finders/design_management/versions_finder_spec.rb'
+- './spec/finders/environments/environment_names_finder_spec.rb'
+- './spec/finders/environments/environments_by_deployments_finder_spec.rb'
+- './spec/finders/environments/environments_finder_spec.rb'
+- './spec/finders/events_finder_spec.rb'
+- './spec/finders/feature_flags_finder_spec.rb'
+- './spec/finders/feature_flags_user_lists_finder_spec.rb'
+- './spec/finders/fork_projects_finder_spec.rb'
+- './spec/finders/fork_targets_finder_spec.rb'
+- './spec/finders/freeze_periods_finder_spec.rb'
+- './spec/finders/group_descendants_finder_spec.rb'
+- './spec/finders/group_members_finder_spec.rb'
+- './spec/finders/group_projects_finder_spec.rb'
+- './spec/finders/groups/accepting_project_transfers_finder_spec.rb'
+- './spec/finders/groups_finder_spec.rb'
+- './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'
+- './spec/finders/joined_groups_finder_spec.rb'
+- './spec/finders/keys_finder_spec.rb'
+- './spec/finders/labels_finder_spec.rb'
+- './spec/finders/lfs_pointers_finder_spec.rb'
+- './spec/finders/license_template_finder_spec.rb'
+- './spec/finders/members_finder_spec.rb'
+- './spec/finders/merge_request/metrics_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/merge_requests/oldest_per_commit_finder_spec.rb'
+- './spec/finders/merge_request_target_project_finder_spec.rb'
+- './spec/finders/metrics/dashboards/annotations_finder_spec.rb'
+- './spec/finders/metrics/users_starred_dashboards_finder_spec.rb'
+- './spec/finders/milestones_finder_spec.rb'
+- './spec/finders/namespaces/projects_finder_spec.rb'
+- './spec/finders/notes_finder_spec.rb'
+- './spec/finders/packages/build_infos_finder_spec.rb'
+- './spec/finders/packages/composer/packages_finder_spec.rb'
+- './spec/finders/packages/conan/package_file_finder_spec.rb'
+- './spec/finders/packages/conan/package_finder_spec.rb'
+- './spec/finders/packages/debian/distributions_finder_spec.rb'
+- './spec/finders/packages/generic/package_finder_spec.rb'
+- './spec/finders/packages/go/module_finder_spec.rb'
+- './spec/finders/packages/go/package_finder_spec.rb'
+- './spec/finders/packages/go/version_finder_spec.rb'
+- './spec/finders/packages/group_or_project_package_finder_spec.rb'
+- './spec/finders/packages/group_packages_finder_spec.rb'
+- './spec/finders/packages/helm/package_files_finder_spec.rb'
+- './spec/finders/packages/helm/packages_finder_spec.rb'
+- './spec/finders/packages/maven/package_finder_spec.rb'
+- './spec/finders/packages/npm/package_finder_spec.rb'
+- './spec/finders/packages/nuget/package_finder_spec.rb'
+- './spec/finders/packages/package_file_finder_spec.rb'
+- './spec/finders/packages/package_finder_spec.rb'
+- './spec/finders/packages/packages_finder_spec.rb'
+- './spec/finders/packages/pypi/package_finder_spec.rb'
+- './spec/finders/packages/pypi/packages_finder_spec.rb'
+- './spec/finders/packages/tags_finder_spec.rb'
+- './spec/finders/pending_todos_finder_spec.rb'
+- './spec/finders/personal_access_tokens_finder_spec.rb'
+- './spec/finders/personal_projects_finder_spec.rb'
+- './spec/finders/projects/export_job_finder_spec.rb'
+- './spec/finders/projects_finder_spec.rb'
+- './spec/finders/projects/groups_finder_spec.rb'
+- './spec/finders/projects/members/effective_access_level_finder_spec.rb'
+- './spec/finders/projects/members/effective_access_level_per_user_finder_spec.rb'
+- './spec/finders/projects/prometheus/alerts_finder_spec.rb'
+- './spec/finders/projects/topics_finder_spec.rb'
+- './spec/finders/prometheus_metrics_finder_spec.rb'
+- './spec/finders/protected_branches_finder_spec.rb'
+- './spec/finders/releases/evidence_pipeline_finder_spec.rb'
+- './spec/finders/releases_finder_spec.rb'
+- './spec/finders/releases/group_releases_finder_spec.rb'
+- './spec/finders/repositories/branch_names_finder_spec.rb'
+- './spec/finders/repositories/changelog_commits_finder_spec.rb'
+- './spec/finders/repositories/changelog_tag_finder_spec.rb'
+- './spec/finders/repositories/tree_finder_spec.rb'
+- './spec/finders/resource_milestone_event_finder_spec.rb'
+- './spec/finders/resource_state_event_finder_spec.rb'
+- './spec/finders/security/jobs_finder_spec.rb'
+- './spec/finders/security/license_compliance_jobs_finder_spec.rb'
+- './spec/finders/security/security_jobs_finder_spec.rb'
+- './spec/finders/sentry_issue_finder_spec.rb'
+- './spec/finders/serverless_domain_finder_spec.rb'
+- './spec/finders/snippets_finder_spec.rb'
+- './spec/finders/starred_projects_finder_spec.rb'
+- './spec/finders/tags_finder_spec.rb'
+- './spec/finders/template_finder_spec.rb'
+- './spec/finders/terraform/states_finder_spec.rb'
+- './spec/finders/todos_finder_spec.rb'
+- './spec/finders/uploader_finder_spec.rb'
+- './spec/finders/user_finder_spec.rb'
+- './spec/finders/user_group_notification_settings_finder_spec.rb'
+- './spec/finders/user_groups_counter_spec.rb'
+- './spec/finders/user_recent_events_finder_spec.rb'
+- './spec/finders/users_finder_spec.rb'
+- './spec/finders/users_star_projects_finder_spec.rb'
+- './spec/finders/work_items/work_items_finder_spec.rb'
+- './spec/frontend/fixtures/abuse_reports.rb'
+- './spec/frontend/fixtures/admin_users.rb'
+- './spec/frontend/fixtures/analytics.rb'
+- './spec/frontend/fixtures/api_deploy_keys.rb'
+- './spec/frontend/fixtures/api_merge_requests.rb'
+- './spec/frontend/fixtures/api_projects.rb'
+- './spec/frontend/fixtures/application_settings.rb'
+- './spec/frontend/fixtures/autocomplete.rb'
+- './spec/frontend/fixtures/autocomplete_sources.rb'
+- './spec/frontend/fixtures/blob.rb'
+- './spec/frontend/fixtures/branches.rb'
+- './spec/frontend/fixtures/clusters.rb'
+- './spec/frontend/fixtures/commit.rb'
+- './spec/frontend/fixtures/deploy_keys.rb'
+- './spec/frontend/fixtures/freeze_period.rb'
+- './spec/frontend/fixtures/groups.rb'
+- './spec/frontend/fixtures/integrations.rb'
+- './spec/frontend/fixtures/issues.rb'
+- './spec/frontend/fixtures/jobs.rb'
+- './spec/frontend/fixtures/labels.rb'
+- './spec/frontend/fixtures/listbox.rb'
+- './spec/frontend/fixtures/merge_requests_diffs.rb'
+- './spec/frontend/fixtures/merge_requests.rb'
+- './spec/frontend/fixtures/metrics_dashboard.rb'
+- './spec/frontend/fixtures/namespaces.rb'
+- './spec/frontend/fixtures/pipeline_schedules.rb'
+- './spec/frontend/fixtures/pipelines.rb'
+- './spec/frontend/fixtures/projects_json.rb'
+- './spec/frontend/fixtures/projects.rb'
+- './spec/frontend/fixtures/prometheus_integration.rb'
+- './spec/frontend/fixtures/raw.rb'
+- './spec/frontend/fixtures/releases.rb'
+- './spec/frontend/fixtures/runner.rb'
+- './spec/frontend/fixtures/search.rb'
+- './spec/frontend/fixtures/sessions.rb'
+- './spec/frontend/fixtures/snippet.rb'
+- './spec/frontend/fixtures/startup_css.rb'
+- './spec/frontend/fixtures/tabs.rb'
+- './spec/frontend/fixtures/tags.rb'
+- './spec/frontend/fixtures/timezones.rb'
+- './spec/frontend/fixtures/todos.rb'
+- './spec/frontend/fixtures/u2f.rb'
+- './spec/frontend/fixtures/webauthn.rb'
+- './spec/graphql/features/authorization_spec.rb'
+- './spec/graphql/features/feature_flag_spec.rb'
+- './spec/graphql/gitlab_schema_spec.rb'
+- './spec/graphql/graphql_triggers_spec.rb'
+- './spec/graphql/mutations/alert_management/alerts/set_assignees_spec.rb'
+- './spec/graphql/mutations/alert_management/alerts/todo/create_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/graphql/mutations/alert_management/update_alert_status_spec.rb'
+- './spec/graphql/mutations/base_mutation_spec.rb'
+- './spec/graphql/mutations/boards/issues/issue_move_list_spec.rb'
+- './spec/graphql/mutations/boards/lists/create_spec.rb'
+- './spec/graphql/mutations/boards/lists/update_spec.rb'
+- './spec/graphql/mutations/boards/update_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/ci/runner/bulk_delete_spec.rb'
+- './spec/graphql/mutations/ci/runner/delete_spec.rb'
+- './spec/graphql/mutations/ci/runner/update_spec.rb'
+- './spec/graphql/mutations/clusters/agents/create_spec.rb'
+- './spec/graphql/mutations/clusters/agents/delete_spec.rb'
+- './spec/graphql/mutations/clusters/agent_tokens/create_spec.rb'
+- './spec/graphql/mutations/clusters/agent_tokens/revoke_spec.rb'
+- './spec/graphql/mutations/commits/create_spec.rb'
+- './spec/graphql/mutations/concerns/mutations/finds_by_gid_spec.rb'
+- './spec/graphql/mutations/concerns/mutations/resolves_group_spec.rb'
+- './spec/graphql/mutations/concerns/mutations/resolves_issuable_spec.rb'
+- './spec/graphql/mutations/container_expiration_policies/update_spec.rb'
+- './spec/graphql/mutations/container_repositories/destroy_spec.rb'
+- './spec/graphql/mutations/container_repositories/destroy_tags_spec.rb'
+- './spec/graphql/mutations/custom_emoji/create_spec.rb'
+- './spec/graphql/mutations/custom_emoji/destroy_spec.rb'
+- './spec/graphql/mutations/customer_relations/contacts/create_spec.rb'
+- './spec/graphql/mutations/customer_relations/contacts/update_spec.rb'
+- './spec/graphql/mutations/customer_relations/organizations/create_spec.rb'
+- './spec/graphql/mutations/customer_relations/organizations/update_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/design_management/delete_spec.rb'
+- './spec/graphql/mutations/design_management/move_spec.rb'
+- './spec/graphql/mutations/design_management/upload_spec.rb'
+- './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'
+- './spec/graphql/mutations/issues/set_confidential_spec.rb'
+- './spec/graphql/mutations/issues/set_due_date_spec.rb'
+- './spec/graphql/mutations/issues/set_escalation_status_spec.rb'
+- './spec/graphql/mutations/issues/set_locked_spec.rb'
+- './spec/graphql/mutations/issues/set_severity_spec.rb'
+- './spec/graphql/mutations/issues/set_subscription_spec.rb'
+- './spec/graphql/mutations/issues/update_spec.rb'
+- './spec/graphql/mutations/labels/create_spec.rb'
+- './spec/graphql/mutations/merge_requests/accept_spec.rb'
+- './spec/graphql/mutations/merge_requests/create_spec.rb'
+- './spec/graphql/mutations/merge_requests/set_assignees_spec.rb'
+- './spec/graphql/mutations/merge_requests/set_draft_spec.rb'
+- './spec/graphql/mutations/merge_requests/set_labels_spec.rb'
+- './spec/graphql/mutations/merge_requests/set_locked_spec.rb'
+- './spec/graphql/mutations/merge_requests/set_milestone_spec.rb'
+- './spec/graphql/mutations/merge_requests/set_reviewers_spec.rb'
+- './spec/graphql/mutations/merge_requests/set_subscription_spec.rb'
+- './spec/graphql/mutations/merge_requests/update_spec.rb'
+- './spec/graphql/mutations/namespace/package_settings/update_spec.rb'
+- './spec/graphql/mutations/notes/reposition_image_diff_note_spec.rb'
+- './spec/graphql/mutations/pages/mark_onboarding_complete_spec.rb'
+- './spec/graphql/mutations/release_asset_links/create_spec.rb'
+- './spec/graphql/mutations/release_asset_links/delete_spec.rb'
+- './spec/graphql/mutations/release_asset_links/update_spec.rb'
+- './spec/graphql/mutations/releases/create_spec.rb'
+- './spec/graphql/mutations/releases/delete_spec.rb'
+- './spec/graphql/mutations/releases/update_spec.rb'
+- './spec/graphql/mutations/saved_replies/create_spec.rb'
+- './spec/graphql/mutations/saved_replies/destroy_spec.rb'
+- './spec/graphql/mutations/saved_replies/update_spec.rb'
+- './spec/graphql/mutations/security/ci_configuration/base_security_analyzer_spec.rb'
+- './spec/graphql/mutations/security/ci_configuration/configure_sast_iac_spec.rb'
+- './spec/graphql/mutations/security/ci_configuration/configure_sast_spec.rb'
+- './spec/graphql/mutations/security/ci_configuration/configure_secret_detection_spec.rb'
+- './spec/graphql/mutations/terraform/state/delete_spec.rb'
+- './spec/graphql/mutations/terraform/state/lock_spec.rb'
+- './spec/graphql/mutations/terraform/state/unlock_spec.rb'
+- './spec/graphql/mutations/timelogs/delete_spec.rb'
+- './spec/graphql/mutations/todos/create_spec.rb'
+- './spec/graphql/mutations/todos/mark_all_done_spec.rb'
+- './spec/graphql/mutations/todos/mark_done_spec.rb'
+- './spec/graphql/mutations/todos/restore_many_spec.rb'
+- './spec/graphql/mutations/todos/restore_spec.rb'
+- './spec/graphql/mutations/user_callouts/create_spec.rb'
+- './spec/graphql/mutations/work_items/update_task_spec.rb'
+- './spec/graphql/mutations/work_items/update_widgets_spec.rb'
+- './spec/graphql/resolvers/admin/analytics/usage_trends/measurements_resolver_spec.rb'
+- './spec/graphql/resolvers/alert_management/alert_resolver_spec.rb'
+- './spec/graphql/resolvers/alert_management/alert_status_counts_resolver_spec.rb'
+- './spec/graphql/resolvers/alert_management/http_integrations_resolver_spec.rb'
+- './spec/graphql/resolvers/alert_management/integrations_resolver_spec.rb'
+- './spec/graphql/resolvers/base_resolver_spec.rb'
+- './spec/graphql/resolvers/blobs_resolver_spec.rb'
+- './spec/graphql/resolvers/board_list_issues_resolver_spec.rb'
+- './spec/graphql/resolvers/board_list_resolver_spec.rb'
+- './spec/graphql/resolvers/board_lists_resolver_spec.rb'
+- './spec/graphql/resolvers/board_resolver_spec.rb'
+- './spec/graphql/resolvers/boards_resolver_spec.rb'
+- './spec/graphql/resolvers/branch_commit_resolver_spec.rb'
+- './spec/graphql/resolvers/ci/config_resolver_spec.rb'
+- './spec/graphql/resolvers/ci/group_runners_resolver_spec.rb'
+- './spec/graphql/resolvers/ci/jobs_resolver_spec.rb'
+- './spec/graphql/resolvers/ci/job_token_scope_resolver_spec.rb'
+- './spec/graphql/resolvers/ci/project_pipeline_counts_resolver_spec.rb'
+- './spec/graphql/resolvers/ci/runner_jobs_resolver_spec.rb'
+- './spec/graphql/resolvers/ci/runner_platforms_resolver_spec.rb'
+- './spec/graphql/resolvers/ci/runner_setup_resolver_spec.rb'
+- './spec/graphql/resolvers/ci/runners_resolver_spec.rb'
+- './spec/graphql/resolvers/ci/runner_status_resolver_spec.rb'
+- './spec/graphql/resolvers/ci/template_resolver_spec.rb'
+- './spec/graphql/resolvers/ci/test_report_summary_resolver_spec.rb'
+- './spec/graphql/resolvers/ci/test_suite_resolver_spec.rb'
+- './spec/graphql/resolvers/clusters/agent_activity_events_resolver_spec.rb'
+- './spec/graphql/resolvers/clusters/agents_resolver_spec.rb'
+- './spec/graphql/resolvers/clusters/agent_tokens_resolver_spec.rb'
+- './spec/graphql/resolvers/commit_pipelines_resolver_spec.rb'
+- './spec/graphql/resolvers/concerns/caching_array_resolver_spec.rb'
+- './spec/graphql/resolvers/concerns/looks_ahead_spec.rb'
+- './spec/graphql/resolvers/concerns/resolves_groups_spec.rb'
+- './spec/graphql/resolvers/concerns/resolves_ids_spec.rb'
+- './spec/graphql/resolvers/concerns/resolves_pipelines_spec.rb'
+- './spec/graphql/resolvers/concerns/resolves_project_spec.rb'
+- './spec/graphql/resolvers/container_repositories_resolver_spec.rb'
+- './spec/graphql/resolvers/container_repository_tags_resolver_spec.rb'
+- './spec/graphql/resolvers/crm/contacts_resolver_spec.rb'
+- './spec/graphql/resolvers/crm/contact_state_counts_resolver_spec.rb'
+- './spec/graphql/resolvers/crm/organizations_resolver_spec.rb'
+- './spec/graphql/resolvers/crm/organization_state_counts_resolver_spec.rb'
+- './spec/graphql/resolvers/design_management/design_at_version_resolver_spec.rb'
+- './spec/graphql/resolvers/design_management/design_resolver_spec.rb'
+- './spec/graphql/resolvers/design_management/designs_resolver_spec.rb'
+- './spec/graphql/resolvers/design_management/version/design_at_version_resolver_spec.rb'
+- './spec/graphql/resolvers/design_management/version/designs_at_version_resolver_spec.rb'
+- './spec/graphql/resolvers/design_management/version_in_collection_resolver_spec.rb'
+- './spec/graphql/resolvers/design_management/version_resolver_spec.rb'
+- './spec/graphql/resolvers/design_management/versions_resolver_spec.rb'
+- './spec/graphql/resolvers/echo_resolver_spec.rb'
+- './spec/graphql/resolvers/environments_resolver_spec.rb'
+- './spec/graphql/resolvers/error_tracking/sentry_detailed_error_resolver_spec.rb'
+- './spec/graphql/resolvers/error_tracking/sentry_error_collection_resolver_spec.rb'
+- './spec/graphql/resolvers/error_tracking/sentry_errors_resolver_spec.rb'
+- './spec/graphql/resolvers/group_issues_resolver_spec.rb'
+- './spec/graphql/resolvers/group_labels_resolver_spec.rb'
+- './spec/graphql/resolvers/group_members/notification_email_resolver_spec.rb'
+- './spec/graphql/resolvers/group_members_resolver_spec.rb'
+- './spec/graphql/resolvers/group_milestones_resolver_spec.rb'
+- './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'
+- './spec/graphql/resolvers/kas/agent_connections_resolver_spec.rb'
+- './spec/graphql/resolvers/labels_resolver_spec.rb'
+- './spec/graphql/resolvers/last_commit_resolver_spec.rb'
+- './spec/graphql/resolvers/merge_request_pipelines_resolver_spec.rb'
+- './spec/graphql/resolvers/merge_requests_count_resolver_spec.rb'
+- './spec/graphql/resolvers/merge_requests_resolver_spec.rb'
+- './spec/graphql/resolvers/metadata_resolver_spec.rb'
+- './spec/graphql/resolvers/metrics/dashboard_resolver_spec.rb'
+- './spec/graphql/resolvers/metrics/dashboards/annotation_resolver_spec.rb'
+- './spec/graphql/resolvers/namespace_projects_resolver_spec.rb'
+- './spec/graphql/resolvers/package_details_resolver_spec.rb'
+- './spec/graphql/resolvers/package_pipelines_resolver_spec.rb'
+- './spec/graphql/resolvers/packages_base_resolver_spec.rb'
+- './spec/graphql/resolvers/paginated_tree_resolver_spec.rb'
+- './spec/graphql/resolvers/project_jobs_resolver_spec.rb'
+- './spec/graphql/resolvers/project_members_resolver_spec.rb'
+- './spec/graphql/resolvers/project_merge_requests_resolver_spec.rb'
+- './spec/graphql/resolvers/project_milestones_resolver_spec.rb'
+- './spec/graphql/resolvers/project_packages_resolver_spec.rb'
+- './spec/graphql/resolvers/project_pipeline_resolver_spec.rb'
+- './spec/graphql/resolvers/project_pipelines_resolver_spec.rb'
+- './spec/graphql/resolvers/project_pipeline_statistics_resolver_spec.rb'
+- './spec/graphql/resolvers/project_resolver_spec.rb'
+- './spec/graphql/resolvers/projects/fork_targets_resolver_spec.rb'
+- './spec/graphql/resolvers/projects/grafana_integration_resolver_spec.rb'
+- './spec/graphql/resolvers/projects/jira_projects_resolver_spec.rb'
+- './spec/graphql/resolvers/projects_resolver_spec.rb'
+- './spec/graphql/resolvers/projects/services_resolver_spec.rb'
+- './spec/graphql/resolvers/projects/snippets_resolver_spec.rb'
+- './spec/graphql/resolvers/recent_boards_resolver_spec.rb'
+- './spec/graphql/resolvers/release_milestones_resolver_spec.rb'
+- './spec/graphql/resolvers/release_resolver_spec.rb'
+- './spec/graphql/resolvers/releases_resolver_spec.rb'
+- './spec/graphql/resolvers/repository_branch_names_resolver_spec.rb'
+- './spec/graphql/resolvers/snippets/blobs_resolver_spec.rb'
+- './spec/graphql/resolvers/snippets_resolver_spec.rb'
+- './spec/graphql/resolvers/terraform/states_resolver_spec.rb'
+- './spec/graphql/resolvers/timelog_resolver_spec.rb'
+- './spec/graphql/resolvers/todos_resolver_spec.rb'
+- './spec/graphql/resolvers/topics_resolver_spec.rb'
+- './spec/graphql/resolvers/tree_resolver_spec.rb'
+- './spec/graphql/resolvers/user_discussions_count_resolver_spec.rb'
+- './spec/graphql/resolvers/user_notes_count_resolver_spec.rb'
+- './spec/graphql/resolvers/user_resolver_spec.rb'
+- './spec/graphql/resolvers/users/group_count_resolver_spec.rb'
+- './spec/graphql/resolvers/users/groups_resolver_spec.rb'
+- './spec/graphql/resolvers/users/participants_resolver_spec.rb'
+- './spec/graphql/resolvers/users_resolver_spec.rb'
+- './spec/graphql/resolvers/users/snippets_resolver_spec.rb'
+- './spec/graphql/resolvers/work_item_resolver_spec.rb'
+- './spec/graphql/resolvers/work_items_resolver_spec.rb'
+- './spec/graphql/resolvers/work_items/types_resolver_spec.rb'
+- './spec/graphql/subscriptions/issuable_updated_spec.rb'
+- './spec/graphql/types/access_level_enum_spec.rb'
+- './spec/graphql/types/access_level_type_spec.rb'
+- './spec/graphql/types/admin/analytics/usage_trends/measurement_identifier_enum_spec.rb'
+- './spec/graphql/types/admin/analytics/usage_trends/measurement_type_spec.rb'
+- './spec/graphql/types/alert_management/alert_status_count_type_spec.rb'
+- './spec/graphql/types/alert_management/alert_type_spec.rb'
+- './spec/graphql/types/alert_management/domain_filter_enum_spec.rb'
+- './spec/graphql/types/alert_management/http_integration_type_spec.rb'
+- './spec/graphql/types/alert_management/integration_type_enum_spec.rb'
+- './spec/graphql/types/alert_management/integration_type_spec.rb'
+- './spec/graphql/types/alert_management/prometheus_integration_type_spec.rb'
+- './spec/graphql/types/alert_management/severity_enum_spec.rb'
+- './spec/graphql/types/alert_management/status_enum_spec.rb'
+- './spec/graphql/types/availability_enum_spec.rb'
+- './spec/graphql/types/award_emojis/award_emoji_type_spec.rb'
+- './spec/graphql/types/base_argument_spec.rb'
+- './spec/graphql/types/base_edge_spec.rb'
+- './spec/graphql/types/base_enum_spec.rb'
+- './spec/graphql/types/base_field_spec.rb'
+- './spec/graphql/types/base_object_spec.rb'
+- './spec/graphql/types/blob_viewers/type_enum_spec.rb'
+- './spec/graphql/types/blob_viewer_type_spec.rb'
+- './spec/graphql/types/board_list_type_spec.rb'
+- './spec/graphql/types/boards/board_issue_input_type_spec.rb'
+- './spec/graphql/types/board_type_spec.rb'
+- './spec/graphql/types/branch_type_spec.rb'
+- './spec/graphql/types/ci/analytics_type_spec.rb'
+- './spec/graphql/types/ci/config/config_type_spec.rb'
+- './spec/graphql/types/ci/config/group_type_spec.rb'
+- './spec/graphql/types/ci/config/include_type_enum_spec.rb'
+- './spec/graphql/types/ci/config/include_type_spec.rb'
+- './spec/graphql/types/ci/config/job_restriction_type_spec.rb'
+- './spec/graphql/types/ci/config/job_type_spec.rb'
+- './spec/graphql/types/ci/config/need_type_spec.rb'
+- './spec/graphql/types/ci/config/stage_type_spec.rb'
+- './spec/graphql/types/ci_configuration/sast/analyzers_entity_input_type_spec.rb'
+- './spec/graphql/types/ci_configuration/sast/analyzers_entity_type_spec.rb'
+- './spec/graphql/types/ci_configuration/sast/entity_input_type_spec.rb'
+- './spec/graphql/types/ci_configuration/sast/entity_type_spec.rb'
+- './spec/graphql/types/ci_configuration/sast/input_type_spec.rb'
+- './spec/graphql/types/ci_configuration/sast/options_entity_spec.rb'
+- './spec/graphql/types/ci_configuration/sast/type_spec.rb'
+- './spec/graphql/types/ci_configuration/sast/ui_component_size_enum_spec.rb'
+- './spec/graphql/types/ci/detailed_status_type_spec.rb'
+- './spec/graphql/types/ci/group_type_spec.rb'
+- './spec/graphql/types/ci/group_variable_type_spec.rb'
+- './spec/graphql/types/ci/instance_variable_type_spec.rb'
+- './spec/graphql/types/ci/job_artifact_file_type_enum_spec.rb'
+- './spec/graphql/types/ci/job_artifact_type_spec.rb'
+- './spec/graphql/types/ci/job_kind_enum_spec.rb'
+- './spec/graphql/types/ci/job_need_union_spec.rb'
+- './spec/graphql/types/ci/job_status_enum_spec.rb'
+- './spec/graphql/types/ci/job_token_scope_type_spec.rb'
+- './spec/graphql/types/ci/job_type_spec.rb'
+- './spec/graphql/types/ci/manual_variable_type_spec.rb'
+- './spec/graphql/types/ci/pipeline_counts_type_spec.rb'
+- './spec/graphql/types/ci/pipeline_merge_request_event_type_enum_spec.rb'
+- './spec/graphql/types/ci/pipeline_message_type_spec.rb'
+- './spec/graphql/types/ci/pipeline_scope_enum_spec.rb'
+- './spec/graphql/types/ci/pipeline_status_enum_spec.rb'
+- './spec/graphql/types/ci/pipeline_type_spec.rb'
+- './spec/graphql/types/ci/project_variable_type_spec.rb'
+- './spec/graphql/types/ci/recent_failures_type_spec.rb'
+- './spec/graphql/types/ci/runner_architecture_type_spec.rb'
+- './spec/graphql/types/ci/runner_platform_type_spec.rb'
+- './spec/graphql/types/ci/runner_setup_type_spec.rb'
+- './spec/graphql/types/ci/runner_type_spec.rb'
+- './spec/graphql/types/ci/runner_upgrade_status_enum_spec.rb'
+- './spec/graphql/types/ci/runner_web_url_edge_spec.rb'
+- './spec/graphql/types/ci/stage_type_spec.rb'
+- './spec/graphql/types/ci/status_action_type_spec.rb'
+- './spec/graphql/types/ci/template_type_spec.rb'
+- './spec/graphql/types/ci/test_case_status_enum_spec.rb'
+- './spec/graphql/types/ci/test_case_type_spec.rb'
+- './spec/graphql/types/ci/test_report_summary_type_spec.rb'
+- './spec/graphql/types/ci/test_report_total_type_spec.rb'
+- './spec/graphql/types/ci/test_suite_summary_type_spec.rb'
+- './spec/graphql/types/ci/test_suite_type_spec.rb'
+- './spec/graphql/types/ci/variable_input_type_spec.rb'
+- './spec/graphql/types/ci/variable_interface_spec.rb'
+- './spec/graphql/types/ci/variable_type_enum_spec.rb'
+- './spec/graphql/types/clusters/agent_activity_event_type_spec.rb'
+- './spec/graphql/types/clusters/agent_token_status_enum_spec.rb'
+- './spec/graphql/types/clusters/agent_token_type_spec.rb'
+- './spec/graphql/types/clusters/agent_type_spec.rb'
+- './spec/graphql/types/color_type_spec.rb'
+- './spec/graphql/types/commit_action_mode_enum_spec.rb'
+- './spec/graphql/types/commit_encoding_enum_spec.rb'
+- './spec/graphql/types/commit_type_spec.rb'
+- './spec/graphql/types/container_expiration_policy_cadence_enum_spec.rb'
+- './spec/graphql/types/container_expiration_policy_keep_enum_spec.rb'
+- './spec/graphql/types/container_expiration_policy_older_than_enum_spec.rb'
+- './spec/graphql/types/container_expiration_policy_type_spec.rb'
+- './spec/graphql/types/container_repository_cleanup_status_enum_spec.rb'
+- './spec/graphql/types/container_repository_details_type_spec.rb'
+- './spec/graphql/types/container_repository_sort_enum_spec.rb'
+- './spec/graphql/types/container_repository_status_enum_spec.rb'
+- './spec/graphql/types/container_repository_tag_type_spec.rb'
+- './spec/graphql/types/container_repository_type_spec.rb'
+- './spec/graphql/types/container_respository_tags_sort_enum_spec.rb'
+- './spec/graphql/types/countable_connection_type_spec.rb'
+- './spec/graphql/types/current_user_todos_type_spec.rb'
+- './spec/graphql/types/custom_emoji_type_spec.rb'
+- './spec/graphql/types/customer_relations/contact_sort_enum_spec.rb'
+- './spec/graphql/types/customer_relations/contact_state_counts_type_spec.rb'
+- './spec/graphql/types/customer_relations/contact_type_spec.rb'
+- './spec/graphql/types/customer_relations/organization_sort_enum_spec.rb'
+- './spec/graphql/types/customer_relations/organization_state_counts_type_spec.rb'
+- './spec/graphql/types/customer_relations/organization_type_spec.rb'
+- './spec/graphql/types/dependency_proxy/blob_type_spec.rb'
+- './spec/graphql/types/dependency_proxy/group_setting_type_spec.rb'
+- './spec/graphql/types/dependency_proxy/image_ttl_group_policy_type_spec.rb'
+- './spec/graphql/types/dependency_proxy/manifest_type_spec.rb'
+- './spec/graphql/types/deployment_tier_enum_spec.rb'
+- './spec/graphql/types/design_management/design_at_version_type_spec.rb'
+- './spec/graphql/types/design_management/design_collection_copy_state_enum_spec.rb'
+- './spec/graphql/types/design_management/design_collection_type_spec.rb'
+- './spec/graphql/types/design_management/design_type_spec.rb'
+- './spec/graphql/types/design_management/design_version_event_enum_spec.rb'
+- './spec/graphql/types/design_management_type_spec.rb'
+- './spec/graphql/types/design_management/version_type_spec.rb'
+- './spec/graphql/types/diff_refs_type_spec.rb'
+- './spec/graphql/types/duration_type_spec.rb'
+- './spec/graphql/types/environment_type_spec.rb'
+- './spec/graphql/types/error_tracking/sentry_detailed_error_type_spec.rb'
+- './spec/graphql/types/error_tracking/sentry_error_collection_type_spec.rb'
+- './spec/graphql/types/error_tracking/sentry_error_stack_trace_entry_type_spec.rb'
+- './spec/graphql/types/error_tracking/sentry_error_stack_trace_type_spec.rb'
+- './spec/graphql/types/error_tracking/sentry_error_type_spec.rb'
+- './spec/graphql/types/eventable_type_spec.rb'
+- './spec/graphql/types/event_type_spec.rb'
+- './spec/graphql/types/evidence_type_spec.rb'
+- './spec/graphql/types/global_id_type_spec.rb'
+- './spec/graphql/types/grafana_integration_type_spec.rb'
+- './spec/graphql/types/group_invitation_type_spec.rb'
+- './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'
+- './spec/graphql/types/issuable_sort_enum_spec.rb'
+- './spec/graphql/types/issuable_state_enum_spec.rb'
+- './spec/graphql/types/issuable_type_spec.rb'
+- './spec/graphql/types/issue_sort_enum_spec.rb'
+- './spec/graphql/types/issue_state_enum_spec.rb'
+- './spec/graphql/types/issue_status_count_type_spec.rb'
+- './spec/graphql/types/issue_type_enum_spec.rb'
+- './spec/graphql/types/issue_type_spec.rb'
+- './spec/graphql/types/jira_import_type_spec.rb'
+- './spec/graphql/types/jira_user_type_spec.rb'
+- './spec/graphql/types/kas/agent_configuration_type_spec.rb'
+- './spec/graphql/types/kas/agent_connection_type_spec.rb'
+- './spec/graphql/types/kas/agent_metadata_type_spec.rb'
+- './spec/graphql/types/label_type_spec.rb'
+- './spec/graphql/types/limited_countable_connection_type_spec.rb'
+- './spec/graphql/types/member_interface_spec.rb'
+- './spec/graphql/types/merge_request_connection_type_spec.rb'
+- './spec/graphql/types/merge_request_review_state_enum_spec.rb'
+- './spec/graphql/types/merge_requests/assignee_type_spec.rb'
+- './spec/graphql/types/merge_requests/author_type_spec.rb'
+- './spec/graphql/types/merge_request_sort_enum_spec.rb'
+- './spec/graphql/types/merge_requests/participant_type_spec.rb'
+- './spec/graphql/types/merge_requests/reviewer_type_spec.rb'
+- './spec/graphql/types/merge_request_state_enum_spec.rb'
+- './spec/graphql/types/merge_request_state_event_enum_spec.rb'
+- './spec/graphql/types/merge_request_type_spec.rb'
+- './spec/graphql/types/metadata/kas_type_spec.rb'
+- './spec/graphql/types/metadata_type_spec.rb'
+- './spec/graphql/types/metrics/dashboards/annotation_type_spec.rb'
+- './spec/graphql/types/metrics/dashboard_type_spec.rb'
+- './spec/graphql/types/milestone_stats_type_spec.rb'
+- './spec/graphql/types/milestone_type_spec.rb'
+- './spec/graphql/types/mutation_type_spec.rb'
+- './spec/graphql/types/namespace/package_settings_type_spec.rb'
+- './spec/graphql/types/namespace_type_spec.rb'
+- './spec/graphql/types/notes/diff_position_type_spec.rb'
+- './spec/graphql/types/notes/discussion_type_spec.rb'
+- './spec/graphql/types/notes/noteable_interface_spec.rb'
+- './spec/graphql/types/notes/note_type_spec.rb'
+- './spec/graphql/types/packages/cleanup/keep_duplicated_package_files_enum_spec.rb'
+- './spec/graphql/types/packages/cleanup/policy_type_spec.rb'
+- './spec/graphql/types/packages/composer/json_type_spec.rb'
+- './spec/graphql/types/packages/composer/metadatum_type_spec.rb'
+- './spec/graphql/types/packages/conan/file_metadatum_type_spec.rb'
+- './spec/graphql/types/packages/conan/metadatum_file_type_enum_spec.rb'
+- './spec/graphql/types/packages/conan/metadatum_type_spec.rb'
+- './spec/graphql/types/packages/helm/dependency_type_spec.rb'
+- './spec/graphql/types/packages/helm/file_metadatum_type_spec.rb'
+- './spec/graphql/types/packages/helm/maintainer_type_spec.rb'
+- './spec/graphql/types/packages/helm/metadata_type_spec.rb'
+- './spec/graphql/types/packages/maven/metadatum_type_spec.rb'
+- './spec/graphql/types/packages/nuget/dependency_link_metdatum_type_spec.rb'
+- './spec/graphql/types/packages/nuget/metadatum_type_spec.rb'
+- './spec/graphql/types/packages/package_base_type_spec.rb'
+- './spec/graphql/types/packages/package_dependency_link_type_spec.rb'
+- './spec/graphql/types/packages/package_dependency_type_enum_spec.rb'
+- './spec/graphql/types/packages/package_dependency_type_spec.rb'
+- './spec/graphql/types/packages/package_details_type_spec.rb'
+- './spec/graphql/types/packages/package_file_type_spec.rb'
+- './spec/graphql/types/packages/package_group_sort_enum_spec.rb'
+- './spec/graphql/types/packages/package_sort_enum_spec.rb'
+- './spec/graphql/types/packages/package_status_enum_spec.rb'
+- './spec/graphql/types/packages/package_type_enum_spec.rb'
+- './spec/graphql/types/packages/package_type_spec.rb'
+- './spec/graphql/types/packages/pypi/metadatum_type_spec.rb'
+- './spec/graphql/types/packages/tag_type_spec.rb'
+- './spec/graphql/types/permission_types/base_permission_type_spec.rb'
+- './spec/graphql/types/permission_types/ci/job_spec.rb'
+- './spec/graphql/types/permission_types/ci/runner_spec.rb'
+- './spec/graphql/types/permission_types/issue_spec.rb'
+- './spec/graphql/types/permission_types/merge_request_spec.rb'
+- './spec/graphql/types/permission_types/merge_request_type_spec.rb'
+- './spec/graphql/types/permission_types/note_spec.rb'
+- './spec/graphql/types/permission_types/project_spec.rb'
+- './spec/graphql/types/permission_types/snippet_spec.rb'
+- './spec/graphql/types/permission_types/user_spec.rb'
+- './spec/graphql/types/permission_types/work_item_spec.rb'
+- './spec/graphql/types/project_invitation_type_spec.rb'
+- './spec/graphql/types/project_member_relation_enum_spec.rb'
+- './spec/graphql/types/project_member_type_spec.rb'
+- './spec/graphql/types/projects/base_service_type_spec.rb'
+- './spec/graphql/types/projects/jira_project_type_spec.rb'
+- './spec/graphql/types/projects/jira_service_type_spec.rb'
+- './spec/graphql/types/projects/service_type_enum_spec.rb'
+- './spec/graphql/types/projects/service_type_spec.rb'
+- './spec/graphql/types/project_statistics_type_spec.rb'
+- './spec/graphql/types/projects/topic_type_spec.rb'
+- './spec/graphql/types/project_type_spec.rb'
+- './spec/graphql/types/prometheus_alert_type_spec.rb'
+- './spec/graphql/types/query_complexity_type_spec.rb'
+- './spec/graphql/types/query_type_spec.rb'
+- './spec/graphql/types/range_input_type_spec.rb'
+- './spec/graphql/types/release_asset_link_input_type_spec.rb'
+- './spec/graphql/types/release_asset_link_type_spec.rb'
+- './spec/graphql/types/release_assets_input_type_spec.rb'
+- './spec/graphql/types/release_assets_type_spec.rb'
+- './spec/graphql/types/release_links_type_spec.rb'
+- './spec/graphql/types/release_source_type_spec.rb'
+- './spec/graphql/types/release_type_spec.rb'
+- './spec/graphql/types/repository/blob_type_spec.rb'
+- './spec/graphql/types/repository_type_spec.rb'
+- './spec/graphql/types/resolvable_interface_spec.rb'
+- './spec/graphql/types/root_storage_statistics_type_spec.rb'
+- './spec/graphql/types/saved_reply_type_spec.rb'
+- './spec/graphql/types/security/report_types_enum_spec.rb'
+- './spec/graphql/types/snippets/blob_action_enum_spec.rb'
+- './spec/graphql/types/snippets/blob_action_input_type_spec.rb'
+- './spec/graphql/types/snippets/blob_type_spec.rb'
+- './spec/graphql/types/snippets/blob_viewer_type_spec.rb'
+- './spec/graphql/types/snippet_type_spec.rb'
+- './spec/graphql/types/subscription_type_spec.rb'
+- './spec/graphql/types/terraform/state_type_spec.rb'
+- './spec/graphql/types/terraform/state_version_type_spec.rb'
+- './spec/graphql/types/timeframe_type_spec.rb'
+- './spec/graphql/types/timelog_type_spec.rb'
+- './spec/graphql/types/time_tracking/timelog_category_type_spec.rb'
+- './spec/graphql/types/time_type_spec.rb'
+- './spec/graphql/types/todoable_interface_spec.rb'
+- './spec/graphql/types/todo_type_spec.rb'
+- './spec/graphql/types/tree/blob_type_spec.rb'
+- './spec/graphql/types/tree/submodule_type_spec.rb'
+- './spec/graphql/types/tree/tree_entry_type_spec.rb'
+- './spec/graphql/types/tree/tree_type_spec.rb'
+- './spec/graphql/types/tree/type_enum_spec.rb'
+- './spec/graphql/types/untrusted_regexp_spec.rb'
+- './spec/graphql/types/upload_type_spec.rb'
+- './spec/graphql/types/user_callout_feature_name_enum_spec.rb'
+- './spec/graphql/types/user_callout_type_spec.rb'
+- './spec/graphql/types/user_merge_request_interaction_type_spec.rb'
+- './spec/graphql/types/user_preferences_type_spec.rb'
+- './spec/graphql/types/user_status_type_spec.rb'
+- './spec/graphql/types/user_type_spec.rb'
+- './spec/graphql/types/work_item_id_type_spec.rb'
+- './spec/graphql/types/work_items/widget_interface_spec.rb'
+- './spec/graphql/types/work_items/widgets/assignees_input_type_spec.rb'
+- './spec/graphql/types/work_items/widgets/assignees_type_spec.rb'
+- './spec/graphql/types/work_items/widgets/description_input_type_spec.rb'
+- './spec/graphql/types/work_items/widgets/description_type_spec.rb'
+- './spec/graphql/types/work_items/widgets/hierarchy_type_spec.rb'
+- './spec/graphql/types/work_items/widgets/hierarchy_update_input_type_spec.rb'
+- './spec/graphql/types/work_items/widgets/labels_type_spec.rb'
+- './spec/graphql/types/work_items/widgets/start_and_due_date_type_spec.rb'
+- './spec/graphql/types/work_items/widgets/start_and_due_date_update_input_type_spec.rb'
+- './spec/graphql/types/work_items/widget_type_enum_spec.rb'
+- './spec/graphql/types/work_item_type_spec.rb'
+- './spec/haml_lint/linter/documentation_links_spec.rb'
+- './spec/haml_lint/linter/inline_javascript_spec.rb'
+- './spec/haml_lint/linter/no_plain_nodes_spec.rb'
+- './spec/helpers/access_tokens_helper_spec.rb'
+- './spec/helpers/admin/application_settings/settings_helper_spec.rb'
+- './spec/helpers/admin/background_migrations_helper_spec.rb'
+- './spec/helpers/admin/deploy_key_helper_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/badges_helper_spec.rb'
+- './spec/helpers/bizible_helper_spec.rb'
+- './spec/helpers/blame_helper_spec.rb'
+- './spec/helpers/blob_helper_spec.rb'
+- './spec/helpers/boards_helper_spec.rb'
+- './spec/helpers/branches_helper_spec.rb'
+- './spec/helpers/breadcrumbs_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/ci/status_helper_spec.rb'
+- './spec/helpers/ci/triggers_helper_spec.rb'
+- './spec/helpers/clusters_helper_spec.rb'
+- './spec/helpers/colors_helper_spec.rb'
+- './spec/helpers/commits_helper_spec.rb'
+- './spec/helpers/components_helper_spec.rb'
+- './spec/helpers/container_expiration_policies_helper_spec.rb'
+- './spec/helpers/container_registry_helper_spec.rb'
+- './spec/helpers/cookies_helper_spec.rb'
+- './spec/helpers/dashboard_helper_spec.rb'
+- './spec/helpers/deploy_tokens_helper_spec.rb'
+- './spec/helpers/dev_ops_report_helper_spec.rb'
+- './spec/helpers/diff_helper_spec.rb'
+- './spec/helpers/dropdowns_helper_spec.rb'
+- './spec/helpers/emails_helper_spec.rb'
+- './spec/helpers/emoji_helper_spec.rb'
+- './spec/helpers/enable_search_settings_helper_spec.rb'
+- './spec/helpers/environment_helper_spec.rb'
+- './spec/helpers/environments_helper_spec.rb'
+- './spec/helpers/events_helper_spec.rb'
+- './spec/helpers/explore_helper_spec.rb'
+- './spec/helpers/export_helper_spec.rb'
+- './spec/helpers/external_link_helper_spec.rb'
+- './spec/helpers/feature_flags_helper_spec.rb'
+- './spec/helpers/form_helper_spec.rb'
+- './spec/helpers/git_helper_spec.rb'
+- './spec/helpers/gitlab_routing_helper_spec.rb'
+- './spec/helpers/gitlab_script_tag_helper_spec.rb'
+- './spec/helpers/graph_helper_spec.rb'
+- './spec/helpers/groups/group_members_helper_spec.rb'
+- './spec/helpers/groups_helper_spec.rb'
+- './spec/helpers/groups/settings_helper_spec.rb'
+- './spec/helpers/hooks_helper_spec.rb'
+- './spec/helpers/icons_helper_spec.rb'
+- './spec/helpers/ide_helper_spec.rb'
+- './spec/helpers/import_helper_spec.rb'
+- './spec/helpers/instance_configuration_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/listbox_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_helper_spec.rb'
+- './spec/helpers/nav/new_dropdown_helper_spec.rb'
+- './spec/helpers/nav/top_nav_helper_spec.rb'
+- './spec/helpers/notes_helper_spec.rb'
+- './spec/helpers/notifications_helper_spec.rb'
+- './spec/helpers/notify_helper_spec.rb'
+- './spec/helpers/numbers_helper_spec.rb'
+- './spec/helpers/one_trust_helper_spec.rb'
+- './spec/helpers/operations_helper_spec.rb'
+- './spec/helpers/packages_helper_spec.rb'
+- './spec/helpers/page_layout_helper_spec.rb'
+- './spec/helpers/pagination_helper_spec.rb'
+- './spec/helpers/preferences_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/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'
+- './spec/helpers/projects/terraform_helper_spec.rb'
+- './spec/helpers/recaptcha_helper_spec.rb'
+- './spec/helpers/registrations_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/sessions_helper_spec.rb'
+- './spec/helpers/sidebars_helper_spec.rb'
+- './spec/helpers/sidekiq_helper_spec.rb'
+- './spec/helpers/snippets_helper_spec.rb'
+- './spec/helpers/sorting_helper_spec.rb'
+- './spec/helpers/sourcegraph_helper_spec.rb'
+- './spec/helpers/ssh_keys_helper_spec.rb'
+- './spec/helpers/startupjs_helper_spec.rb'
+- './spec/helpers/stat_anchors_helper_spec.rb'
+- './spec/helpers/storage_helper_spec.rb'
+- './spec/helpers/submodule_helper_spec.rb'
+- './spec/helpers/subscribable_banner_helper_spec.rb'
+- './spec/helpers/tab_helper_spec.rb'
+- './spec/helpers/terms_helper_spec.rb'
+- './spec/helpers/timeboxes_helper_spec.rb'
+- './spec/helpers/timeboxes_routing_helper_spec.rb'
+- './spec/helpers/time_helper_spec.rb'
+- './spec/helpers/time_zone_helper_spec.rb'
+- './spec/helpers/todos_helper_spec.rb'
+- './spec/helpers/tooling/visual_review_helper_spec.rb'
+- './spec/helpers/tracking_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/web_ide_button_helper_spec.rb'
+- './spec/helpers/webpack_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/helpers/x509_helper_spec.rb'
+- './spec/initializers/00_deprecations_spec.rb'
+- './spec/initializers/00_rails_disable_joins_spec.rb'
+- './spec/initializers/0_log_deprecations_spec.rb'
+- './spec/initializers/0_postgresql_types_spec.rb'
+- './spec/initializers/100_patch_omniauth_oauth2_spec.rb'
+- './spec/initializers/100_patch_omniauth_saml_spec.rb'
+- './spec/initializers/1_acts_as_taggable_spec.rb'
+- './spec/initializers/6_validations_spec.rb'
+- './spec/initializers/action_cable_subscription_adapter_identifier_spec.rb'
+- './spec/initializers/action_mailer_hooks_spec.rb'
+- './spec/initializers/active_record_locking_spec.rb'
+- './spec/initializers/asset_proxy_setting_spec.rb'
+- './spec/initializers/attr_encrypted_no_db_connection_spec.rb'
+- './spec/initializers/attr_encrypted_thread_safe_spec.rb'
+- './spec/initializers/carrierwave_patch_spec.rb'
+- './spec/initializers/cookies_serializer_spec.rb'
+- './spec/initializers/database_config_spec.rb'
+- './spec/initializers/diagnostic_reports_spec.rb'
+- './spec/initializers/direct_upload_support_spec.rb'
+- './spec/initializers/doorkeeper_spec.rb'
+- './spec/initializers/enumerator_next_patch_spec.rb'
+- './spec/initializers/fog_google_https_private_urls_spec.rb'
+- './spec/initializers/forbid_sidekiq_in_transactions_spec.rb'
+- './spec/initializers/global_id_spec.rb'
+- './spec/initializers/google_api_client_spec.rb'
+- './spec/initializers/hangouts_chat_http_override_spec.rb'
+- './spec/initializers/lograge_spec.rb'
+- './spec/initializers/mail_encoding_patch_spec.rb'
+- './spec/initializers/mailer_retries_spec.rb'
+- './spec/initializers/memory_watchdog_spec.rb'
+- './spec/initializers/net_http_patch_spec.rb'
+- './spec/initializers/net_http_response_patch_spec.rb'
+- './spec/initializers/omniauth_spec.rb'
+- './spec/initializers/pages_storage_check_spec.rb'
+- './spec/initializers/rack_multipart_patch_spec.rb'
+- './spec/initializers/rails_asset_host_spec.rb'
+- './spec/initializers/rdoc_segfault_patch_spec.rb'
+- './spec/initializers/remove_active_job_execute_callback_spec.rb'
+- './spec/initializers/rest-client-hostname_override_spec.rb'
+- './spec/initializers/secret_token_spec.rb'
+- './spec/initializers/session_store_spec.rb'
+- './spec/initializers/settings_spec.rb'
+- './spec/initializers/sidekiq_spec.rb'
+- './spec/initializers/trusted_proxies_spec.rb'
+- './spec/initializers/validate_database_config_spec.rb'
+- './spec/initializers/validate_puma_spec.rb'
+- './spec/lib/api/api_spec.rb'
+- './spec/lib/api/base_spec.rb'
+- './spec/lib/api/ci/helpers/runner_helpers_spec.rb'
+- './spec/lib/api/ci/helpers/runner_spec.rb'
+- './spec/lib/api/entities/application_setting_spec.rb'
+- './spec/lib/api/entities/basic_project_details_spec.rb'
+- './spec/lib/api/entities/branch_spec.rb'
+- './spec/lib/api/entities/bulk_imports/entity_failure_spec.rb'
+- './spec/lib/api/entities/bulk_imports/entity_spec.rb'
+- './spec/lib/api/entities/bulk_imports/export_status_spec.rb'
+- './spec/lib/api/entities/bulk_import_spec.rb'
+- './spec/lib/api/entities/changelog_spec.rb'
+- './spec/lib/api/entities/ci/job_artifact_file_spec.rb'
+- './spec/lib/api/entities/ci/job_request/dependency_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/entities/ci/job_request/service_spec.rb'
+- './spec/lib/api/entities/ci/pipeline_spec.rb'
+- './spec/lib/api/entities/clusters/agent_authorization_spec.rb'
+- './spec/lib/api/entities/clusters/agent_spec.rb'
+- './spec/lib/api/entities/deploy_key_spec.rb'
+- './spec/lib/api/entities/deploy_keys_project_spec.rb'
+- './spec/lib/api/entities/deployment_extended_spec.rb'
+- './spec/lib/api/entities/design_management/design_spec.rb'
+- './spec/lib/api/entities/group_detail_spec.rb'
+- './spec/lib/api/entities/merge_request_approvals_spec.rb'
+- './spec/lib/api/entities/merge_request_basic_spec.rb'
+- './spec/lib/api/entities/merge_request_changes_spec.rb'
+- './spec/lib/api/entities/nuget/dependency_group_spec.rb'
+- './spec/lib/api/entities/nuget/dependency_spec.rb'
+- './spec/lib/api/entities/nuget/metadatum_spec.rb'
+- './spec/lib/api/entities/nuget/package_metadata_catalog_entry_spec.rb'
+- './spec/lib/api/entities/nuget/search_result_spec.rb'
+- './spec/lib/api/entities/package_spec.rb'
+- './spec/lib/api/entities/personal_access_token_spec.rb'
+- './spec/lib/api/entities/personal_access_token_with_details_spec.rb'
+- './spec/lib/api/entities/plan_limit_spec.rb'
+- './spec/lib/api/entities/project_import_failed_relation_spec.rb'
+- './spec/lib/api/entities/project_import_status_spec.rb'
+- './spec/lib/api/entities/project_spec.rb'
+- './spec/lib/api/entities/projects/repository_storage_move_spec.rb'
+- './spec/lib/api/entities/projects/topic_spec.rb'
+- './spec/lib/api/entities/public_group_details_spec.rb'
+- './spec/lib/api/entities/release_spec.rb'
+- './spec/lib/api/entities/snippet_spec.rb'
+- './spec/lib/api/entities/snippets/repository_storage_move_spec.rb'
+- './spec/lib/api/entities/ssh_key_spec.rb'
+- './spec/lib/api/entities/user_spec.rb'
+- './spec/lib/api/entities/wiki_page_spec.rb'
+- './spec/lib/api/every_api_endpoint_spec.rb'
+- './spec/lib/api/github/entities_spec.rb'
+- './spec/lib/api/helpers/authentication_spec.rb'
+- './spec/lib/api/helpers/caching_spec.rb'
+- './spec/lib/api/helpers/common_helpers_spec.rb'
+- './spec/lib/api/helpers/graphql_helpers_spec.rb'
+- './spec/lib/api/helpers/label_helpers_spec.rb'
+- './spec/lib/api/helpers/merge_requests_helpers_spec.rb'
+- './spec/lib/api/helpers/packages/dependency_proxy_helpers_spec.rb'
+- './spec/lib/api/helpers/packages_helpers_spec.rb'
+- './spec/lib/api/helpers/packages_manager_clients_helpers_spec.rb'
+- './spec/lib/api/helpers/pagination_spec.rb'
+- './spec/lib/api/helpers/pagination_strategies_spec.rb'
+- './spec/lib/api/helpers/project_stats_refresh_conflicts_helpers_spec.rb'
+- './spec/lib/api/helpers/rate_limiter_spec.rb'
+- './spec/lib/api/helpers/related_resources_helpers_spec.rb'
+- './spec/lib/api/helpers_spec.rb'
+- './spec/lib/api/helpers/variables_helpers_spec.rb'
+- './spec/lib/api/helpers/version_spec.rb'
+- './spec/lib/api/integrations/slack/events/url_verification_spec.rb'
+- './spec/lib/api/support/git_access_actor_spec.rb'
+- './spec/lib/api/validations/validators/absence_spec.rb'
+- './spec/lib/api/validations/validators/array_none_any_spec.rb'
+- './spec/lib/api/validations/validators/email_or_email_list_spec.rb'
+- './spec/lib/api/validations/validators/file_path_spec.rb'
+- './spec/lib/api/validations/validators/git_ref_spec.rb'
+- './spec/lib/api/validations/validators/git_sha_spec.rb'
+- './spec/lib/api/validations/validators/integer_none_any_spec.rb'
+- './spec/lib/api/validations/validators/integer_or_custom_value_spec.rb'
+- './spec/lib/api/validations/validators/limit_spec.rb'
+- './spec/lib/api/validations/validators/project_portable_spec.rb'
+- './spec/lib/api/validations/validators/untrusted_regexp_spec.rb'
+- './spec/lib/atlassian/jira_connect/client_spec.rb'
+- './spec/lib/atlassian/jira_connect/jwt/asymmetric_spec.rb'
+- './spec/lib/atlassian/jira_connect/jwt/symmetric_spec.rb'
+- './spec/lib/atlassian/jira_connect/serializers/author_entity_spec.rb'
+- './spec/lib/atlassian/jira_connect/serializers/base_entity_spec.rb'
+- './spec/lib/atlassian/jira_connect/serializers/branch_entity_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/pull_request_entity_spec.rb'
+- './spec/lib/atlassian/jira_connect/serializers/repository_entity_spec.rb'
+- './spec/lib/atlassian/jira_connect_spec.rb'
+- './spec/lib/atlassian/jira_issue_key_extractor_spec.rb'
+- './spec/lib/backup/database_backup_error_spec.rb'
+- './spec/lib/backup/database_spec.rb'
+- './spec/lib/backup/file_backup_error_spec.rb'
+- './spec/lib/backup/files_spec.rb'
+- './spec/lib/backup/gitaly_backup_spec.rb'
+- './spec/lib/backup/manager_spec.rb'
+- './spec/lib/backup/repositories_spec.rb'
+- './spec/lib/backup/task_spec.rb'
+- './spec/lib/banzai/color_parser_spec.rb'
+- './spec/lib/banzai/commit_renderer_spec.rb'
+- './spec/lib/banzai/cross_project_reference_spec.rb'
+- './spec/lib/banzai/filter/absolute_link_filter_spec.rb'
+- './spec/lib/banzai/filter_array_spec.rb'
+- './spec/lib/banzai/filter/ascii_doc_post_processing_filter_spec.rb'
+- './spec/lib/banzai/filter/ascii_doc_sanitization_filter_spec.rb'
+- './spec/lib/banzai/filter/asset_proxy_filter_spec.rb'
+- './spec/lib/banzai/filter/audio_link_filter_spec.rb'
+- './spec/lib/banzai/filter/autolink_filter_spec.rb'
+- './spec/lib/banzai/filter/blockquote_fence_filter_spec.rb'
+- './spec/lib/banzai/filter/broadcast_message_placeholders_filter_spec.rb'
+- './spec/lib/banzai/filter/broadcast_message_sanitization_filter_spec.rb'
+- './spec/lib/banzai/filter/color_filter_spec.rb'
+- './spec/lib/banzai/filter/commit_trailers_filter_spec.rb'
+- './spec/lib/banzai/filter/custom_emoji_filter_spec.rb'
+- './spec/lib/banzai/filter/emoji_filter_spec.rb'
+- './spec/lib/banzai/filter/external_link_filter_spec.rb'
+- './spec/lib/banzai/filter/footnote_filter_spec.rb'
+- './spec/lib/banzai/filter/front_matter_filter_spec.rb'
+- './spec/lib/banzai/filter/gollum_tags_filter_spec.rb'
+- './spec/lib/banzai/filter/html_entity_filter_spec.rb'
+- './spec/lib/banzai/filter/image_lazy_load_filter_spec.rb'
+- './spec/lib/banzai/filter/image_link_filter_spec.rb'
+- './spec/lib/banzai/filter/inline_alert_metrics_filter_spec.rb'
+- './spec/lib/banzai/filter/inline_cluster_metrics_filter_spec.rb'
+- './spec/lib/banzai/filter/inline_diff_filter_spec.rb'
+- './spec/lib/banzai/filter/inline_grafana_metrics_filter_spec.rb'
+- './spec/lib/banzai/filter/inline_metrics_filter_spec.rb'
+- './spec/lib/banzai/filter/inline_metrics_redactor_filter_spec.rb'
+- './spec/lib/banzai/filter/issuable_reference_expansion_filter_spec.rb'
+- './spec/lib/banzai/filter/jira_import/adf_to_commonmark_filter_spec.rb'
+- './spec/lib/banzai/filter/kroki_filter_spec.rb'
+- './spec/lib/banzai/filter/markdown_filter_spec.rb'
+- './spec/lib/banzai/filter/math_filter_spec.rb'
+- './spec/lib/banzai/filter/mermaid_filter_spec.rb'
+- './spec/lib/banzai/filter/normalize_source_filter_spec.rb'
+- './spec/lib/banzai/filter/output_safety_spec.rb'
+- './spec/lib/banzai/filter/plantuml_filter_spec.rb'
+- './spec/lib/banzai/filter/reference_redactor_filter_spec.rb'
+- './spec/lib/banzai/filter/references/abstract_reference_filter_spec.rb'
+- './spec/lib/banzai/filter/references/alert_reference_filter_spec.rb'
+- './spec/lib/banzai/filter/references/commit_range_reference_filter_spec.rb'
+- './spec/lib/banzai/filter/references/commit_reference_filter_spec.rb'
+- './spec/lib/banzai/filter/references/design_reference_filter_spec.rb'
+- './spec/lib/banzai/filter/references/external_issue_reference_filter_spec.rb'
+- './spec/lib/banzai/filter/references/feature_flag_reference_filter_spec.rb'
+- './spec/lib/banzai/filter/references/issue_reference_filter_spec.rb'
+- './spec/lib/banzai/filter/references/label_reference_filter_spec.rb'
+- './spec/lib/banzai/filter/references/merge_request_reference_filter_spec.rb'
+- './spec/lib/banzai/filter/references/milestone_reference_filter_spec.rb'
+- './spec/lib/banzai/filter/references/project_reference_filter_spec.rb'
+- './spec/lib/banzai/filter/references/reference_cache_spec.rb'
+- './spec/lib/banzai/filter/references/reference_filter_spec.rb'
+- './spec/lib/banzai/filter/references/snippet_reference_filter_spec.rb'
+- './spec/lib/banzai/filter/references/user_reference_filter_spec.rb'
+- './spec/lib/banzai/filter/repository_link_filter_spec.rb'
+- './spec/lib/banzai/filter/sanitization_filter_spec.rb'
+- './spec/lib/banzai/filter/spaced_link_filter_spec.rb'
+- './spec/lib/banzai/filter/suggestion_filter_spec.rb'
+- './spec/lib/banzai/filter/syntax_highlight_filter_spec.rb'
+- './spec/lib/banzai/filter/table_of_contents_filter_spec.rb'
+- './spec/lib/banzai/filter/table_of_contents_tag_filter_spec.rb'
+- './spec/lib/banzai/filter/task_list_filter_spec.rb'
+- './spec/lib/banzai/filter/truncate_source_filter_spec.rb'
+- './spec/lib/banzai/filter/upload_link_filter_spec.rb'
+- './spec/lib/banzai/filter/video_link_filter_spec.rb'
+- './spec/lib/banzai/filter/wiki_link_filter_spec.rb'
+- './spec/lib/banzai/issuable_extractor_spec.rb'
+- './spec/lib/banzai/object_renderer_spec.rb'
+- './spec/lib/banzai/pipeline/broadcast_message_pipeline_spec.rb'
+- './spec/lib/banzai/pipeline/description_pipeline_spec.rb'
+- './spec/lib/banzai/pipeline/email_pipeline_spec.rb'
+- './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'
+- './spec/lib/banzai/pipeline/pre_process_pipeline_spec.rb'
+- './spec/lib/banzai/pipeline_spec.rb'
+- './spec/lib/banzai/pipeline/wiki_pipeline_spec.rb'
+- './spec/lib/banzai/querying_spec.rb'
+- './spec/lib/banzai/reference_parser/alert_parser_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/reference_parser/design_parser_spec.rb'
+- './spec/lib/banzai/reference_parser/external_issue_parser_spec.rb'
+- './spec/lib/banzai/reference_parser/feature_flag_parser_spec.rb'
+- './spec/lib/banzai/reference_parser/issue_parser_spec.rb'
+- './spec/lib/banzai/reference_parser/label_parser_spec.rb'
+- './spec/lib/banzai/reference_parser/mentioned_group_parser_spec.rb'
+- './spec/lib/banzai/reference_parser/mentioned_project_parser_spec.rb'
+- './spec/lib/banzai/reference_parser/mentioned_user_parser_spec.rb'
+- './spec/lib/banzai/reference_parser/merge_request_parser_spec.rb'
+- './spec/lib/banzai/reference_parser/milestone_parser_spec.rb'
+- './spec/lib/banzai/reference_parser/project_parser_spec.rb'
+- './spec/lib/banzai/reference_parser/snippet_parser_spec.rb'
+- './spec/lib/banzai/reference_parser/user_parser_spec.rb'
+- './spec/lib/banzai/reference_redactor_spec.rb'
+- './spec/lib/banzai/render_context_spec.rb'
+- './spec/lib/banzai/renderer_spec.rb'
+- './spec/lib/bitbucket/collection_spec.rb'
+- './spec/lib/bitbucket/connection_spec.rb'
+- './spec/lib/bitbucket/page_spec.rb'
+- './spec/lib/bitbucket/paginator_spec.rb'
+- './spec/lib/bitbucket/representation/comment_spec.rb'
+- './spec/lib/bitbucket/representation/issue_spec.rb'
+- './spec/lib/bitbucket/representation/pull_request_comment_spec.rb'
+- './spec/lib/bitbucket/representation/pull_request_spec.rb'
+- './spec/lib/bitbucket/representation/repo_spec.rb'
+- './spec/lib/bitbucket/representation/user_spec.rb'
+- './spec/lib/bitbucket_server/client_spec.rb'
+- './spec/lib/bitbucket_server/collection_spec.rb'
+- './spec/lib/bitbucket_server/connection_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/comment_spec.rb'
+- './spec/lib/bitbucket_server/representation/pull_request_comment_spec.rb'
+- './spec/lib/bitbucket_server/representation/pull_request_spec.rb'
+- './spec/lib/bitbucket_server/representation/repo_spec.rb'
+- './spec/lib/bulk_imports/clients/graphql_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/json_extractor_spec.rb'
+- './spec/lib/bulk_imports/common/extractors/ndjson_extractor_spec.rb'
+- './spec/lib/bulk_imports/common/extractors/rest_extractor_spec.rb'
+- './spec/lib/bulk_imports/common/graphql/get_members_query_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/lfs_objects_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/common/pipelines/uploads_pipeline_spec.rb'
+- './spec/lib/bulk_imports/common/pipelines/wiki_pipeline_spec.rb'
+- './spec/lib/bulk_imports/common/rest/get_badges_query_spec.rb'
+- './spec/lib/bulk_imports/common/transformers/prohibited_attributes_transformer_spec.rb'
+- './spec/lib/bulk_imports/common/transformers/user_reference_transformer_spec.rb'
+- './spec/lib/bulk_imports/groups/extractors/subgroups_extractor_spec.rb'
+- './spec/lib/bulk_imports/groups/graphql/get_group_query_spec.rb'
+- './spec/lib/bulk_imports/groups/graphql/get_projects_query_spec.rb'
+- './spec/lib/bulk_imports/groups/loaders/group_loader_spec.rb'
+- './spec/lib/bulk_imports/groups/pipelines/group_attributes_pipeline_spec.rb'
+- './spec/lib/bulk_imports/groups/pipelines/group_pipeline_spec.rb'
+- './spec/lib/bulk_imports/groups/pipelines/namespace_settings_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/groups/stage_spec.rb'
+- './spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb'
+- './spec/lib/bulk_imports/groups/transformers/member_attributes_transformer_spec.rb'
+- './spec/lib/bulk_imports/groups/transformers/subgroup_to_entity_transformer_spec.rb'
+- './spec/lib/bulk_imports/ndjson_pipeline_spec.rb'
+- './spec/lib/bulk_imports/network_error_spec.rb'
+- './spec/lib/bulk_imports/pipeline/context_spec.rb'
+- './spec/lib/bulk_imports/pipeline/extracted_data_spec.rb'
+- './spec/lib/bulk_imports/pipeline/runner_spec.rb'
+- './spec/lib/bulk_imports/pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/graphql/get_project_query_spec.rb'
+- './spec/lib/bulk_imports/projects/graphql/get_repository_query_spec.rb'
+- './spec/lib/bulk_imports/projects/graphql/get_snippet_repository_query_spec.rb'
+- './spec/lib/bulk_imports/projects/pipelines/auto_devops_pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/pipelines/ci_pipelines_pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/pipelines/container_expiration_policy_pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/pipelines/design_bundle_pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/pipelines/external_pull_requests_pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/pipelines/issues_pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/pipelines/merge_requests_pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/pipelines/pipeline_schedules_pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/pipelines/project_attributes_pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/pipelines/project_feature_pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/pipelines/project_pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/pipelines/protected_branches_pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/pipelines/releases_pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/pipelines/repository_bundle_pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/pipelines/repository_pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/pipelines/service_desk_setting_pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/pipelines/snippets_pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/pipelines/snippets_repository_pipeline_spec.rb'
+- './spec/lib/bulk_imports/projects/stage_spec.rb'
+- './spec/lib/bulk_imports/projects/transformers/project_attributes_transformer_spec.rb'
+- './spec/lib/bulk_imports/retry_pipeline_error_spec.rb'
+- './spec/lib/bulk_imports/users_mapper_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/container_registry/blob_spec.rb'
+- './spec/lib/container_registry/client_spec.rb'
+- './spec/lib/container_registry/gitlab_api_client_spec.rb'
+- './spec/lib/container_registry/migration_spec.rb'
+- './spec/lib/container_registry/path_spec.rb'
+- './spec/lib/container_registry/registry_spec.rb'
+- './spec/lib/container_registry/tag_spec.rb'
+- './spec/lib/csv_builder_spec.rb'
+- './spec/lib/csv_builders/stream_spec.rb'
+- './spec/lib/declarative_enum_spec.rb'
+- './spec/lib/error_tracking/collector/payload_validator_spec.rb'
+- './spec/lib/error_tracking/collector/sentry_auth_parser_spec.rb'
+- './spec/lib/error_tracking/collector/sentry_request_parser_spec.rb'
+- './spec/lib/error_tracking/sentry_client/api_urls_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/issue_spec.rb'
+- './spec/lib/error_tracking/sentry_client/pagination_parser_spec.rb'
+- './spec/lib/error_tracking/sentry_client/projects_spec.rb'
+- './spec/lib/error_tracking/sentry_client/repo_spec.rb'
+- './spec/lib/error_tracking/sentry_client_spec.rb'
+- './spec/lib/error_tracking/stacktrace_builder_spec.rb'
+- './spec/lib/event_filter_spec.rb'
+- './spec/lib/expand_variables_spec.rb'
+- './spec/lib/extracts_path_spec.rb'
+- './spec/lib/extracts_ref_spec.rb'
+- './spec/lib/feature/definition_spec.rb'
+- './spec/lib/feature/gitaly_spec.rb'
+- './spec/lib/feature_spec.rb'
+- './spec/lib/file_size_validator_spec.rb'
+- './spec/lib/forever_spec.rb'
+- './spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb'
+- './spec/lib/generators/gitlab/usage_metric_definition_generator_spec.rb'
+- './spec/lib/generators/gitlab/usage_metric_definition/redis_hll_generator_spec.rb'
+- './spec/lib/generators/gitlab/usage_metric_generator_spec.rb'
+- './spec/lib/generators/model/model_generator_spec.rb'
+- './spec/lib/gitaly/server_spec.rb'
+- './spec/lib/gitlab/access/branch_protection_spec.rb'
+- './spec/lib/gitlab/action_cable/request_store_callbacks_spec.rb'
+- './spec/lib/gitlab/alert_management/alert_status_counts_spec.rb'
+- './spec/lib/gitlab/alert_management/fingerprint_spec.rb'
+- './spec/lib/gitlab/alert_management/payload/base_spec.rb'
+- './spec/lib/gitlab/alert_management/payload/generic_spec.rb'
+- './spec/lib/gitlab/alert_management/payload/managed_prometheus_spec.rb'
+- './spec/lib/gitlab/alert_management/payload/prometheus_spec.rb'
+- './spec/lib/gitlab/alert_management/payload_spec.rb'
+- './spec/lib/gitlab/allowable_spec.rb'
+- './spec/lib/gitlab/analytics/cycle_analytics/aggregated/base_query_builder_spec.rb'
+- './spec/lib/gitlab/analytics/cycle_analytics/aggregated/records_fetcher_spec.rb'
+- './spec/lib/gitlab/analytics/cycle_analytics/average_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/analytics/cycle_analytics/sorting_spec.rb'
+- './spec/lib/gitlab/analytics/cycle_analytics/stage_events/code_stage_start_spec.rb'
+- './spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_created_spec.rb'
+- './spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_deployed_to_production_spec.rb'
+- './spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_first_mentioned_in_commit_spec.rb'
+- './spec/lib/gitlab/analytics/cycle_analytics/stage_events/issue_stage_end_spec.rb'
+- './spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_created_spec.rb'
+- './spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_first_deployed_to_production_spec.rb'
+- './spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_finished_spec.rb'
+- './spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_last_build_started_spec.rb'
+- './spec/lib/gitlab/analytics/cycle_analytics/stage_events/merge_request_merged_spec.rb'
+- './spec/lib/gitlab/analytics/cycle_analytics/stage_events/plan_stage_start_spec.rb'
+- './spec/lib/gitlab/analytics/cycle_analytics/stage_events/stage_event_spec.rb'
+- './spec/lib/gitlab/analytics/usage_trends/workers_argument_builder_spec.rb'
+- './spec/lib/gitlab/anonymous_session_spec.rb'
+- './spec/lib/gitlab/api_authentication/builder_spec.rb'
+- './spec/lib/gitlab/api_authentication/sent_through_builder_spec.rb'
+- './spec/lib/gitlab/api_authentication/token_locator_spec.rb'
+- './spec/lib/gitlab/api_authentication/token_resolver_spec.rb'
+- './spec/lib/gitlab/api_authentication/token_type_builder_spec.rb'
+- './spec/lib/gitlab/app_json_logger_spec.rb'
+- './spec/lib/gitlab/application_context_spec.rb'
+- './spec/lib/gitlab/application_rate_limiter/base_strategy_spec.rb'
+- './spec/lib/gitlab/application_rate_limiter/increment_per_actioned_resource_spec.rb'
+- './spec/lib/gitlab/application_rate_limiter/increment_per_action_spec.rb'
+- './spec/lib/gitlab/application_rate_limiter_spec.rb'
+- './spec/lib/gitlab/app_logger_spec.rb'
+- './spec/lib/gitlab/app_text_logger_spec.rb'
+- './spec/lib/gitlab/asciidoc/html5_converter_spec.rb'
+- './spec/lib/gitlab/asciidoc/include_processor_spec.rb'
+- './spec/lib/gitlab/asciidoc_spec.rb'
+- './spec/lib/gitlab/asset_proxy_spec.rb'
+- './spec/lib/gitlab/audit/auditor_spec.rb'
+- './spec/lib/gitlab/audit/ci_runner_token_author_spec.rb'
+- './spec/lib/gitlab/audit/deploy_key_author_spec.rb'
+- './spec/lib/gitlab/audit/deploy_token_author_spec.rb'
+- './spec/lib/gitlab/audit/null_author_spec.rb'
+- './spec/lib/gitlab/audit/null_target_spec.rb'
+- './spec/lib/gitlab/audit/target_spec.rb'
+- './spec/lib/gitlab/audit/unauthenticated_author_spec.rb'
+- './spec/lib/gitlab/auth/activity_spec.rb'
+- './spec/lib/gitlab/auth/atlassian/auth_hash_spec.rb'
+- './spec/lib/gitlab/auth/atlassian/identity_linker_spec.rb'
+- './spec/lib/gitlab/auth/atlassian/user_spec.rb'
+- './spec/lib/gitlab/auth/auth_finders_spec.rb'
+- './spec/lib/gitlab/auth/blocked_user_tracker_spec.rb'
+- './spec/lib/gitlab/auth/crowd/authentication_spec.rb'
+- './spec/lib/gitlab/auth/current_user_mode_spec.rb'
+- './spec/lib/gitlab/auth/ip_rate_limiter_spec.rb'
+- './spec/lib/gitlab/auth/key_status_checker_spec.rb'
+- './spec/lib/gitlab/auth/ldap/access_spec.rb'
+- './spec/lib/gitlab/auth/ldap/adapter_spec.rb'
+- './spec/lib/gitlab/auth/ldap/authentication_spec.rb'
+- './spec/lib/gitlab/auth/ldap/auth_hash_spec.rb'
+- './spec/lib/gitlab/auth/ldap/config_spec.rb'
+- './spec/lib/gitlab/auth/ldap/dn_spec.rb'
+- './spec/lib/gitlab/auth/ldap/person_spec.rb'
+- './spec/lib/gitlab/auth/ldap/user_spec.rb'
+- './spec/lib/gitlab/auth/o_auth/auth_hash_spec.rb'
+- './spec/lib/gitlab/auth/o_auth/identity_linker_spec.rb'
+- './spec/lib/gitlab/auth/o_auth/provider_spec.rb'
+- './spec/lib/gitlab/auth/o_auth/user_spec.rb'
+- './spec/lib/gitlab/authorized_keys_spec.rb'
+- './spec/lib/gitlab/auth/otp/strategies/devise_spec.rb'
+- './spec/lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp_spec.rb'
+- './spec/lib/gitlab/auth/otp/strategies/forti_authenticator/push_otp_spec.rb'
+- './spec/lib/gitlab/auth/otp/strategies/forti_token_cloud_spec.rb'
+- './spec/lib/gitlab/auth/request_authenticator_spec.rb'
+- './spec/lib/gitlab/auth/result_spec.rb'
+- './spec/lib/gitlab/auth/saml/auth_hash_spec.rb'
+- './spec/lib/gitlab/auth/saml/config_spec.rb'
+- './spec/lib/gitlab/auth/saml/identity_linker_spec.rb'
+- './spec/lib/gitlab/auth/saml/origin_validator_spec.rb'
+- './spec/lib/gitlab/auth/saml/user_spec.rb'
+- './spec/lib/gitlab/auth_spec.rb'
+- './spec/lib/gitlab/auth/two_factor_auth_verifier_spec.rb'
+- './spec/lib/gitlab/auth/u2f_webauthn_converter_spec.rb'
+- './spec/lib/gitlab/auth/unique_ips_limiter_spec.rb'
+- './spec/lib/gitlab/auth/user_access_denied_reason_spec.rb'
+- './spec/lib/gitlab/avatar_cache_spec.rb'
+- './spec/lib/gitlab/background_migration/add_primary_email_to_emails_if_user_confirmed_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_ci_queuing_tables_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_draft_status_on_merge_requests_with_corrected_regex_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_integrations_enable_ssl_verification_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_integrations_type_new_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_issue_search_data_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_jira_tracker_deployment_type2_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_member_namespace_for_group_members_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_namespace_id_for_namespace_route_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_namespace_id_for_project_route_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_namespace_id_of_vulnerability_reads_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_namespace_traversal_ids_children_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_namespace_traversal_ids_roots_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_note_discussion_id_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_project_feature_package_registry_access_level_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_project_import_level_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_project_member_namespace_id_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_project_repositories_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_project_settings_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_projects_with_coverage_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_snippet_repositories_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_topics_title_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_upvotes_count_on_issues_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_user_namespace_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_vulnerability_reads_cluster_agent_spec.rb'
+- './spec/lib/gitlab/background_migration/backfill_work_item_type_id_for_issues_spec.rb'
+- './spec/lib/gitlab/background_migration/base_job_spec.rb'
+- './spec/lib/gitlab/background_migration/batched_migration_job_spec.rb'
+- './spec/lib/gitlab/background_migration/batching_strategies/backfill_issue_work_item_type_batching_strategy_spec.rb'
+- './spec/lib/gitlab/background_migration/batching_strategies/backfill_project_namespace_per_group_batching_strategy_spec.rb'
+- './spec/lib/gitlab/background_migration/batching_strategies/backfill_project_statistics_with_container_registry_size_batching_strategy_spec.rb'
+- './spec/lib/gitlab/background_migration/batching_strategies/base_strategy_spec.rb'
+- './spec/lib/gitlab/background_migration/batching_strategies/dismissed_vulnerabilities_strategy_spec.rb'
+- './spec/lib/gitlab/background_migration/batching_strategies/loose_index_scan_batching_strategy_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/cleanup_orphaned_lfs_objects_projects_spec.rb'
+- './spec/lib/gitlab/background_migration/cleanup_orphaned_routes_spec.rb'
+- './spec/lib/gitlab/background_migration/copy_column_using_background_migration_job_spec.rb'
+- './spec/lib/gitlab/background_migration/delete_orphaned_deployments_spec.rb'
+- './spec/lib/gitlab/background_migration/destroy_invalid_group_members_spec.rb'
+- './spec/lib/gitlab/background_migration/disable_expiration_policies_linked_to_no_container_images_spec.rb'
+- './spec/lib/gitlab/background_migration/disable_legacy_open_source_licence_for_recent_public_projects_spec.rb'
+- './spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_inactive_public_projects_spec.rb'
+- './spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_no_issues_no_repo_projects_spec.rb'
+- './spec/lib/gitlab/background_migration/disable_legacy_open_source_license_for_one_member_no_repo_projects_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/encrypt_integration_properties_spec.rb'
+- './spec/lib/gitlab/background_migration/encrypt_static_object_token_spec.rb'
+- './spec/lib/gitlab/background_migration/expire_o_auth_tokens_spec.rb'
+- './spec/lib/gitlab/background_migration/extract_project_topics_into_separate_table_spec.rb'
+- './spec/lib/gitlab/background_migration/fix_duplicate_project_name_and_path_spec.rb'
+- './spec/lib/gitlab/background_migration/fix_first_mentioned_in_commit_at_spec.rb'
+- './spec/lib/gitlab/background_migration/fix_merge_request_diff_commit_users_spec.rb'
+- './spec/lib/gitlab/background_migration/fix_vulnerability_occurrences_with_hashes_as_raw_metadata_spec.rb'
+- './spec/lib/gitlab/background_migration/job_coordinator_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/mailers/unconfirm_mailer_spec.rb'
+- './spec/lib/gitlab/background_migration/merge_topics_with_same_name_spec.rb'
+- './spec/lib/gitlab/background_migration/migrate_merge_request_diff_commit_users_spec.rb'
+- './spec/lib/gitlab/background_migration/migrate_personal_namespace_project_maintainer_to_owner_spec.rb'
+- './spec/lib/gitlab/background_migration/migrate_project_taggings_context_from_tags_to_topics_spec.rb'
+- './spec/lib/gitlab/background_migration/migrate_shimo_confluence_integration_category_spec.rb'
+- './spec/lib/gitlab/background_migration/migrate_u2f_webauthn_spec.rb'
+- './spec/lib/gitlab/background_migration/move_container_registry_enabled_to_project_feature_spec.rb'
+- './spec/lib/gitlab/background_migration/nullify_orphan_runner_id_on_ci_builds_spec.rb'
+- './spec/lib/gitlab/background_migration/populate_container_repository_migration_plan_spec.rb'
+- './spec/lib/gitlab/background_migration/populate_namespace_statistics_spec.rb'
+- './spec/lib/gitlab/background_migration/populate_operation_visibility_permissions_from_operations_spec.rb'
+- './spec/lib/gitlab/background_migration/populate_topics_non_private_projects_count_spec.rb'
+- './spec/lib/gitlab/background_migration/populate_topics_total_projects_count_cache_spec.rb'
+- './spec/lib/gitlab/background_migration/populate_vulnerability_reads_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_all_trace_expiration_dates_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/background_migration/remove_self_managed_wiki_notes_spec.rb'
+- './spec/lib/gitlab/background_migration/remove_vulnerability_finding_links_spec.rb'
+- './spec/lib/gitlab/background_migration/reset_duplicate_ci_runners_token_encrypted_values_on_projects_spec.rb'
+- './spec/lib/gitlab/background_migration/reset_duplicate_ci_runners_token_values_on_projects_spec.rb'
+- './spec/lib/gitlab/background_migration/reset_too_many_tags_skipped_registry_imports_spec.rb'
+- './spec/lib/gitlab/background_migration/set_correct_vulnerability_state_spec.rb'
+- './spec/lib/gitlab/background_migration/set_legacy_open_source_license_available_for_non_public_projects_spec.rb'
+- './spec/lib/gitlab/background_migration_spec.rb'
+- './spec/lib/gitlab/background_migration/steal_migrate_merge_request_diff_commit_users_spec.rb'
+- './spec/lib/gitlab/background_migration/update_delayed_project_removal_to_null_for_user_namespaces_spec.rb'
+- './spec/lib/gitlab/background_migration/update_jira_tracker_data_deployment_type_based_on_url_spec.rb'
+- './spec/lib/gitlab/background_migration/update_timelogs_null_spent_at_spec.rb'
+- './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/background_task_spec.rb'
+- './spec/lib/gitlab/backtrace_cleaner_spec.rb'
+- './spec/lib/gitlab/bare_repository_import/importer_spec.rb'
+- './spec/lib/gitlab/bare_repository_import/repository_spec.rb'
+- './spec/lib/gitlab/batch_pop_queueing_spec.rb'
+- './spec/lib/gitlab/batch_worker_context_spec.rb'
+- './spec/lib/gitlab/bitbucket_import/importer_spec.rb'
+- './spec/lib/gitlab/bitbucket_import/project_creator_spec.rb'
+- './spec/lib/gitlab/bitbucket_import/wiki_formatter_spec.rb'
+- './spec/lib/gitlab/bitbucket_server_import/importer_spec.rb'
+- './spec/lib/gitlab/blame_spec.rb'
+- './spec/lib/gitlab/blob_helper_spec.rb'
+- './spec/lib/gitlab/branch_push_merge_commit_analyzer_spec.rb'
+- './spec/lib/gitlab/buffered_io_spec.rb'
+- './spec/lib/gitlab/build_access_spec.rb'
+- './spec/lib/gitlab/bullet/exclusions_spec.rb'
+- './spec/lib/gitlab/bullet_spec.rb'
+- './spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb'
+- './spec/lib/gitlab/cache/helpers_spec.rb'
+- './spec/lib/gitlab/cache/import/caching_spec.rb'
+- './spec/lib/gitlab/cache/request_cache_spec.rb'
+- './spec/lib/gitlab/cache_spec.rb'
+- './spec/lib/gitlab/changelog/committer_spec.rb'
+- './spec/lib/gitlab/changelog/config_spec.rb'
+- './spec/lib/gitlab/changelog/generator_spec.rb'
+- './spec/lib/gitlab/changelog/release_spec.rb'
+- './spec/lib/gitlab/changes_list_spec.rb'
+- './spec/lib/gitlab/chat/command_spec.rb'
+- './spec/lib/gitlab/chat_name_token_spec.rb'
+- './spec/lib/gitlab/chat/output_spec.rb'
+- './spec/lib/gitlab/chat/responder/base_spec.rb'
+- './spec/lib/gitlab/chat/responder/mattermost_spec.rb'
+- './spec/lib/gitlab/chat/responder/slack_spec.rb'
+- './spec/lib/gitlab/chat/responder_spec.rb'
+- './spec/lib/gitlab/chat_spec.rb'
+- './spec/lib/gitlab/checks/branch_check_spec.rb'
+- './spec/lib/gitlab/checks/changes_access_spec.rb'
+- './spec/lib/gitlab/checks/container_moved_spec.rb'
+- './spec/lib/gitlab/checks/diff_check_spec.rb'
+- './spec/lib/gitlab/checks/force_push_spec.rb'
+- './spec/lib/gitlab/checks/lfs_check_spec.rb'
+- './spec/lib/gitlab/checks/lfs_integrity_spec.rb'
+- './spec/lib/gitlab/checks/matching_merge_request_spec.rb'
+- './spec/lib/gitlab/checks/project_created_spec.rb'
+- './spec/lib/gitlab/checks/push_check_spec.rb'
+- './spec/lib/gitlab/checks/push_file_count_check_spec.rb'
+- './spec/lib/gitlab/checks/single_change_access_spec.rb'
+- './spec/lib/gitlab/checks/snippet_check_spec.rb'
+- './spec/lib/gitlab/checks/tag_check_spec.rb'
+- './spec/lib/gitlab/checks/timed_logger_spec.rb'
+- './spec/lib/gitlab/ci_access_spec.rb'
+- './spec/lib/gitlab/ci/ansi2html_spec.rb'
+- './spec/lib/gitlab/ci/ansi2json/line_spec.rb'
+- './spec/lib/gitlab/ci/ansi2json/parser_spec.rb'
+- './spec/lib/gitlab/ci/ansi2json/result_spec.rb'
+- './spec/lib/gitlab/ci/ansi2json_spec.rb'
+- './spec/lib/gitlab/ci/ansi2json/style_spec.rb'
+- './spec/lib/gitlab/ci/artifact_file_reader_spec.rb'
+- './spec/lib/gitlab/ci/artifacts/logger_spec.rb'
+- './spec/lib/gitlab/ci/artifacts/metrics_spec.rb'
+- './spec/lib/gitlab/ci/badge/coverage/metadata_spec.rb'
+- './spec/lib/gitlab/ci/badge/coverage/report_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/status_spec.rb'
+- './spec/lib/gitlab/ci/badge/pipeline/template_spec.rb'
+- './spec/lib/gitlab/ci/badge/release/latest_release_spec.rb'
+- './spec/lib/gitlab/ci/badge/release/metadata_spec.rb'
+- './spec/lib/gitlab/ci/badge/release/template_spec.rb'
+- './spec/lib/gitlab/ci/build/artifacts/adapters/gzip_stream_spec.rb'
+- './spec/lib/gitlab/ci/build/artifacts/adapters/raw_stream_spec.rb'
+- './spec/lib/gitlab/ci/build/artifacts/adapters/zip_stream_spec.rb'
+- './spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb'
+- './spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb'
+- './spec/lib/gitlab/ci/build/artifacts/path_spec.rb'
+- './spec/lib/gitlab/ci/build/auto_retry_spec.rb'
+- './spec/lib/gitlab/ci/build/cache_spec.rb'
+- './spec/lib/gitlab/ci/build/context/build_spec.rb'
+- './spec/lib/gitlab/ci/build/context/global_spec.rb'
+- './spec/lib/gitlab/ci/build/credentials/factory_spec.rb'
+- './spec/lib/gitlab/ci/build/credentials/registry/dependency_proxy_spec.rb'
+- './spec/lib/gitlab/ci/build/credentials/registry/gitlab_registry_spec.rb'
+- './spec/lib/gitlab/ci/build/duration_parser_spec.rb'
+- './spec/lib/gitlab/ci/build/image_spec.rb'
+- './spec/lib/gitlab/ci/build/policy/changes_spec.rb'
+- './spec/lib/gitlab/ci/build/policy/kubernetes_spec.rb'
+- './spec/lib/gitlab/ci/build/policy/refs_spec.rb'
+- './spec/lib/gitlab/ci/build/policy_spec.rb'
+- './spec/lib/gitlab/ci/build/policy/variables_spec.rb'
+- './spec/lib/gitlab/ci/build/port_spec.rb'
+- './spec/lib/gitlab/ci/build/prerequisite/factory_spec.rb'
+- './spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb'
+- './spec/lib/gitlab/ci/build/releaser_spec.rb'
+- './spec/lib/gitlab/ci/build/rules/rule/clause/changes_spec.rb'
+- './spec/lib/gitlab/ci/build/rules/rule/clause/exists_spec.rb'
+- './spec/lib/gitlab/ci/build/rules/rule/clause/if_spec.rb'
+- './spec/lib/gitlab/ci/build/rules/rule/clause_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/build/step_spec.rb'
+- './spec/lib/gitlab/ci/charts_spec.rb'
+- './spec/lib/gitlab/ci/config/edge_stages_injector_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/allow_failure_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/artifacts_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/bridge_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/cache_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/caches_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/commands_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/coverage_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/default_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/environment_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/files_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/hidden_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/imageable_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/image_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/include/rules/rule_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/include/rules_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/include_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/inherit/default_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/inherit/variables_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/job_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/jobs_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/key_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/kubernetes_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/need_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/needs_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/paths_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/policy_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/port_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/ports_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/prefix_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/processable_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/product/matrix_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/product/parallel_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/product/variables_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/pull_policy_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/release/assets/link_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/release/assets/links_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/release/assets_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/release_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/reports/coverage_report_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/reports_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/retry_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/root_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/rules/rule/changes_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/rules/rule_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/rules_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/service_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/services_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/stage_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/stages_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/tags_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/trigger/forward_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/trigger_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/variables_spec.rb'
+- './spec/lib/gitlab/ci/config/entry/workflow_spec.rb'
+- './spec/lib/gitlab/ci/config/extendable/entry_spec.rb'
+- './spec/lib/gitlab/ci/config/extendable_spec.rb'
+- './spec/lib/gitlab/ci/config/external/context_spec.rb'
+- './spec/lib/gitlab/ci/config/external/file/artifact_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/file/project_spec.rb'
+- './spec/lib/gitlab/ci/config/external/file/remote_spec.rb'
+- './spec/lib/gitlab/ci/config/external/file/template_spec.rb'
+- './spec/lib/gitlab/ci/config/external/mapper_spec.rb'
+- './spec/lib/gitlab/ci/config/external/processor_spec.rb'
+- './spec/lib/gitlab/ci/config/external/rules_spec.rb'
+- './spec/lib/gitlab/ci/config/normalizer/factory_spec.rb'
+- './spec/lib/gitlab/ci/config/normalizer/matrix_strategy_spec.rb'
+- './spec/lib/gitlab/ci/config/normalizer/number_strategy_spec.rb'
+- './spec/lib/gitlab/ci/config/normalizer_spec.rb'
+- './spec/lib/gitlab/ci/config_spec.rb'
+- './spec/lib/gitlab/ci/config/yaml/tags/reference_spec.rb'
+- './spec/lib/gitlab/ci/config/yaml/tags/resolver_spec.rb'
+- './spec/lib/gitlab/ci/cron_parser_spec.rb'
+- './spec/lib/gitlab/ci/jwt_spec.rb'
+- './spec/lib/gitlab/ci/jwt_v2_spec.rb'
+- './spec/lib/gitlab/ci/lint_spec.rb'
+- './spec/lib/gitlab/ci/mask_secret_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/accessibility/pa11y_spec.rb'
+- './spec/lib/gitlab/ci/parsers/codequality/code_climate_spec.rb'
+- './spec/lib/gitlab/ci/parsers/coverage/cobertura_spec.rb'
+- './spec/lib/gitlab/ci/parsers/coverage/sax_document_spec.rb'
+- './spec/lib/gitlab/ci/parsers/instrumentation_spec.rb'
+- './spec/lib/gitlab/ci/parsers/sbom/cyclonedx_properties_spec.rb'
+- './spec/lib/gitlab/ci/parsers/sbom/cyclonedx_spec.rb'
+- './spec/lib/gitlab/ci/parsers/sbom/source/dependency_scanning_spec.rb'
+- './spec/lib/gitlab/ci/parsers/sbom/validators/cyclonedx_schema_validator_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/secret_detection_spec.rb'
+- './spec/lib/gitlab/ci/parsers/security/validators/schema_validator_spec.rb'
+- './spec/lib/gitlab/ci/parsers_spec.rb'
+- './spec/lib/gitlab/ci/parsers/terraform/tfplan_spec.rb'
+- './spec/lib/gitlab/ci/parsers/test/junit_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/chain/build/associations_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/chain/build_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/chain/command_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/chain/config/content_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/chain/create_deployments_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/chain/create_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/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/limit/rate_limit_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/chain/pipeline/process_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/chain/seed_block_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/chain/seed_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/chain/validate/abilities_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/chain/validate/external_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/chain/validate/repository_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/duration_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/null_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/expression/lexeme/or_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/expression/lexeme/pattern_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/expression/lexeme/string_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/expression/lexeme/variable_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/expression/lexer_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/expression/parser_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/expression/statement_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/expression/token_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/logger_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/metrics_spec.rb'
+- './spec/lib/gitlab/ci/pipeline_object_hierarchy_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/cache_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/seed/build_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/seed/deployment_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/seed/pipeline_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/seed/processable/resource_group_spec.rb'
+- './spec/lib/gitlab/ci/pipeline/seed/stage_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/codequality_mr_diff_spec.rb'
+- './spec/lib/gitlab/ci/reports/codequality_reports_comparer_spec.rb'
+- './spec/lib/gitlab/ci/reports/codequality_reports_spec.rb'
+- './spec/lib/gitlab/ci/reports/coverage_report_generator_spec.rb'
+- './spec/lib/gitlab/ci/reports/coverage_report_spec.rb'
+- './spec/lib/gitlab/ci/reports/reports_comparer_spec.rb'
+- './spec/lib/gitlab/ci/reports/sbom/component_spec.rb'
+- './spec/lib/gitlab/ci/reports/sbom/report_spec.rb'
+- './spec/lib/gitlab/ci/reports/sbom/reports_spec.rb'
+- './spec/lib/gitlab/ci/reports/sbom/source_spec.rb'
+- './spec/lib/gitlab/ci/reports/security/aggregated_report_spec.rb'
+- './spec/lib/gitlab/ci/reports/security/finding_key_spec.rb'
+- './spec/lib/gitlab/ci/reports/security/finding_signature_spec.rb'
+- './spec/lib/gitlab/ci/reports/security/flag_spec.rb'
+- './spec/lib/gitlab/ci/reports/security/identifier_spec.rb'
+- './spec/lib/gitlab/ci/reports/security/link_spec.rb'
+- './spec/lib/gitlab/ci/reports/security/locations/sast_spec.rb'
+- './spec/lib/gitlab/ci/reports/security/locations/secret_detection_spec.rb'
+- './spec/lib/gitlab/ci/reports/security/report_spec.rb'
+- './spec/lib/gitlab/ci/reports/security/reports_spec.rb'
+- './spec/lib/gitlab/ci/reports/security/scanned_resource_spec.rb'
+- './spec/lib/gitlab/ci/reports/security/scanner_spec.rb'
+- './spec/lib/gitlab/ci/reports/security/scan_spec.rb'
+- './spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb'
+- './spec/lib/gitlab/ci/reports/terraform_reports_spec.rb'
+- './spec/lib/gitlab/ci/reports/test_case_spec.rb'
+- './spec/lib/gitlab/ci/reports/test_failure_history_spec.rb'
+- './spec/lib/gitlab/ci/reports/test_reports_comparer_spec.rb'
+- './spec/lib/gitlab/ci/reports/test_report_spec.rb'
+- './spec/lib/gitlab/ci/reports/test_report_summary_spec.rb'
+- './spec/lib/gitlab/ci/reports/test_suite_comparer_spec.rb'
+- './spec/lib/gitlab/ci/reports/test_suite_spec.rb'
+- './spec/lib/gitlab/ci/reports/test_suite_summary_spec.rb'
+- './spec/lib/gitlab/ci/runner/backoff_spec.rb'
+- './spec/lib/gitlab/ci/runner_instructions_spec.rb'
+- './spec/lib/gitlab/ci/runner/metrics_spec.rb'
+- './spec/lib/gitlab/ci/runner_releases_spec.rb'
+- './spec/lib/gitlab/ci/runner_upgrade_check_spec.rb'
+- './spec/lib/gitlab/ci/status/bridge/common_spec.rb'
+- './spec/lib/gitlab/ci/status/bridge/factory_spec.rb'
+- './spec/lib/gitlab/ci/status/bridge/waiting_for_resource_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/common_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/factory_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/failed_unmet_prerequisites_spec.rb'
+- './spec/lib/gitlab/ci/status/build/manual_spec.rb'
+- './spec/lib/gitlab/ci/status/build/pending_spec.rb'
+- './spec/lib/gitlab/ci/status/build/play_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/scheduled_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/build/waiting_for_approval_spec.rb'
+- './spec/lib/gitlab/ci/status/build/waiting_for_resource_spec.rb'
+- './spec/lib/gitlab/ci/status/canceled_spec.rb'
+- './spec/lib/gitlab/ci/status/composite_spec.rb'
+- './spec/lib/gitlab/ci/status/core_spec.rb'
+- './spec/lib/gitlab/ci/status/created_spec.rb'
+- './spec/lib/gitlab/ci/status/extended_spec.rb'
+- './spec/lib/gitlab/ci/status/external/common_spec.rb'
+- './spec/lib/gitlab/ci/status/external/factory_spec.rb'
+- './spec/lib/gitlab/ci/status/factory_spec.rb'
+- './spec/lib/gitlab/ci/status/failed_spec.rb'
+- './spec/lib/gitlab/ci/status/group/common_spec.rb'
+- './spec/lib/gitlab/ci/status/group/factory_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/common_spec.rb'
+- './spec/lib/gitlab/ci/status/pipeline/delayed_spec.rb'
+- './spec/lib/gitlab/ci/status/pipeline/factory_spec.rb'
+- './spec/lib/gitlab/ci/status/preparing_spec.rb'
+- './spec/lib/gitlab/ci/status/processable/waiting_for_resource_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/common_spec.rb'
+- './spec/lib/gitlab/ci/status/stage/factory_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/tags/bulk_insert_spec.rb'
+- './spec/lib/gitlab/ci/templates/5_minute_production_app_ci_yaml_spec.rb'
+- './spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb'
+- './spec/lib/gitlab/ci/templates/AWS/deploy_ecs_gitlab_ci_yaml_spec.rb'
+- './spec/lib/gitlab/ci/templates/flutter_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/sast_iac_gitlab_ci_yaml_spec.rb'
+- './spec/lib/gitlab/ci/templates/Jobs/sast_iac_latest_gitlab_ci_yaml_spec.rb'
+- './spec/lib/gitlab/ci/templates/Jobs/test_gitlab_ci_yaml_spec.rb'
+- './spec/lib/gitlab/ci/templates/kaniko_gitlab_ci_yaml_spec.rb'
+- './spec/lib/gitlab/ci/templates/MATLAB_spec.rb'
+- './spec/lib/gitlab/ci/templates/npm_spec.rb'
+- './spec/lib/gitlab/ci/templates/templates_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/terraform_gitlab_ci_yaml_spec.rb'
+- './spec/lib/gitlab/ci/templates/terraform_latest_gitlab_ci_yaml_spec.rb'
+- './spec/lib/gitlab/ci/templates/themekit_gitlab_ci_yaml_spec.rb'
+- './spec/lib/gitlab/ci/templates/Verify/load_performance_testing_gitlab_ci_yaml_spec.rb'
+- './spec/lib/gitlab/ci/trace/archive_spec.rb'
+- './spec/lib/gitlab/ci/trace/backoff_spec.rb'
+- './spec/lib/gitlab/ci/trace/checksum_spec.rb'
+- './spec/lib/gitlab/ci/trace/chunked_io_spec.rb'
+- './spec/lib/gitlab/ci/trace/metrics_spec.rb'
+- './spec/lib/gitlab/ci/trace/remote_checksum_spec.rb'
+- './spec/lib/gitlab/ci/trace/section_parser_spec.rb'
+- './spec/lib/gitlab/ci/trace_spec.rb'
+- './spec/lib/gitlab/ci/trace/stream_spec.rb'
+- './spec/lib/gitlab/ci/variables/builder/group_spec.rb'
+- './spec/lib/gitlab/ci/variables/builder/instance_spec.rb'
+- './spec/lib/gitlab/ci/variables/builder/project_spec.rb'
+- './spec/lib/gitlab/ci/variables/builder_spec.rb'
+- './spec/lib/gitlab/ci/variables/collection/item_spec.rb'
+- './spec/lib/gitlab/ci/variables/collection/sort_spec.rb'
+- './spec/lib/gitlab/ci/variables/collection_spec.rb'
+- './spec/lib/gitlab/ci/variables/helpers_spec.rb'
+- './spec/lib/gitlab/ci/yaml_processor/dag_spec.rb'
+- './spec/lib/gitlab/ci/yaml_processor/feature_flags_spec.rb'
+- './spec/lib/gitlab/ci/yaml_processor/result_spec.rb'
+- './spec/lib/gitlab/ci/yaml_processor_spec.rb'
+- './spec/lib/gitlab/class_attributes_spec.rb'
+- './spec/lib/gitlab/cleanup/orphan_job_artifact_files_batch_spec.rb'
+- './spec/lib/gitlab/cleanup/orphan_job_artifact_files_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/closing_issue_extractor_spec.rb'
+- './spec/lib/gitlab/cluster/lifecycle_events_spec.rb'
+- './spec/lib/gitlab/cluster/mixins/puma_cluster_spec.rb'
+- './spec/lib/gitlab/cluster/puma_worker_killer_observer_spec.rb'
+- './spec/lib/gitlab/cluster/rack_timeout_observer_spec.rb'
+- './spec/lib/gitlab/code_navigation_path_spec.rb'
+- './spec/lib/gitlab/color_schemes_spec.rb'
+- './spec/lib/gitlab/color_spec.rb'
+- './spec/lib/gitlab/composer/cache_spec.rb'
+- './spec/lib/gitlab/composer/version_index_spec.rb'
+- './spec/lib/gitlab/conan_token_spec.rb'
+- './spec/lib/gitlab/config_checker/external_database_checker_spec.rb'
+- './spec/lib/gitlab/config_checker/puma_rugged_checker_spec.rb'
+- './spec/lib/gitlab/config/entry/attributable_spec.rb'
+- './spec/lib/gitlab/config/entry/boolean_spec.rb'
+- './spec/lib/gitlab/config/entry/composable_array_spec.rb'
+- './spec/lib/gitlab/config/entry/composable_hash_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/undefined_spec.rb'
+- './spec/lib/gitlab/config/entry/unspecified_spec.rb'
+- './spec/lib/gitlab/config/entry/validatable_spec.rb'
+- './spec/lib/gitlab/config/entry/validators/nested_array_helpers_spec.rb'
+- './spec/lib/gitlab/config/entry/validator_spec.rb'
+- './spec/lib/gitlab/config/entry/validators_spec.rb'
+- './spec/lib/gitlab/config/loader/yaml_spec.rb'
+- './spec/lib/gitlab/conflict/file_collection_spec.rb'
+- './spec/lib/gitlab/conflict/file_spec.rb'
+- './spec/lib/gitlab/console_spec.rb'
+- './spec/lib/gitlab/consul/internal_spec.rb'
+- './spec/lib/gitlab/container_repository/tags/cache_spec.rb'
+- './spec/lib/gitlab/content_security_policy/config_loader_spec.rb'
+- './spec/lib/gitlab/contributions_calendar_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/cross_project_access/class_methods_spec.rb'
+- './spec/lib/gitlab/cross_project_access_spec.rb'
+- './spec/lib/gitlab/crypto_helper_spec.rb'
+- './spec/lib/gitlab/current_settings_spec.rb'
+- './spec/lib/gitlab/cycle_analytics/permissions_spec.rb'
+- './spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb'
+- './spec/lib/gitlab/cycle_analytics/summary/value_spec.rb'
+- './spec/lib/gitlab/cycle_analytics/updater_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/gitlab/database/async_indexes/migration_helpers_spec.rb'
+- './spec/lib/gitlab/database/async_indexes/postgres_async_index_spec.rb'
+- './spec/lib/gitlab/database/async_indexes_spec.rb'
+- './spec/lib/gitlab/database/background_migration/batched_job_spec.rb'
+- './spec/lib/gitlab/database/background_migration/batched_job_transition_log_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/background_migration/batch_metrics_spec.rb'
+- './spec/lib/gitlab/database/background_migration/batch_optimizer_spec.rb'
+- './spec/lib/gitlab/database/background_migration/health_status/indicators/autovacuum_active_on_table_spec.rb'
+- './spec/lib/gitlab/database/background_migration/health_status/indicators/write_ahead_log_spec.rb'
+- './spec/lib/gitlab/database/background_migration/health_status_spec.rb'
+- './spec/lib/gitlab/database/background_migration_job_spec.rb'
+- './spec/lib/gitlab/database/background_migration/prometheus_metrics_spec.rb'
+- './spec/lib/gitlab/database/batch_count_spec.rb'
+- './spec/lib/gitlab/database/bulk_update_spec.rb'
+- './spec/lib/gitlab/database/connection_timer_spec.rb'
+- './spec/lib/gitlab/database/consistency_checker_spec.rb'
+- './spec/lib/gitlab/database/consistency_spec.rb'
+- './spec/lib/gitlab/database/count/exact_count_strategy_spec.rb'
+- './spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb'
+- './spec/lib/gitlab/database/count_spec.rb'
+- './spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb'
+- './spec/lib/gitlab/database/dynamic_model_helpers_spec.rb'
+- './spec/lib/gitlab/database/each_database_spec.rb'
+- './spec/lib/gitlab/database/gitlab_schema_spec.rb'
+- './spec/lib/gitlab/database/grant_spec.rb'
+- './spec/lib/gitlab/database_importers/common_metrics/importer_spec.rb'
+- './spec/lib/gitlab/database_importers/common_metrics/prometheus_metric_spec.rb'
+- './spec/lib/gitlab/database_importers/instance_administrators/create_group_spec.rb'
+- './spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb'
+- './spec/lib/gitlab/database_importers/self_monitoring/project/delete_service_spec.rb'
+- './spec/lib/gitlab/database_importers/work_items/base_type_importer_spec.rb'
+- './spec/lib/gitlab/database/load_balancing/action_cable_callbacks_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/primary_host_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/session_spec.rb'
+- './spec/lib/gitlab/database/load_balancing/setup_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/load_balancing_spec.rb'
+- './spec/lib/gitlab/database/load_balancing/srv_resolver_spec.rb'
+- './spec/lib/gitlab/database/load_balancing/sticking_spec.rb'
+- './spec/lib/gitlab/database/load_balancing/transaction_leaking_spec.rb'
+- './spec/lib/gitlab/database/lock_writes_manager_spec.rb'
+- './spec/lib/gitlab/database/loose_foreign_keys_spec.rb'
+- './spec/lib/gitlab/database/migration_helpers/announce_database_spec.rb'
+- './spec/lib/gitlab/database/migration_helpers/cascading_namespace_settings_spec.rb'
+- './spec/lib/gitlab/database/migration_helpers/loose_foreign_key_helpers_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/migration_helpers/v2_spec.rb'
+- './spec/lib/gitlab/database/migrations/background_migration_helpers_spec.rb'
+- './spec/lib/gitlab/database/migrations/base_background_runner_spec.rb'
+- './spec/lib/gitlab/database/migrations/batched_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_details_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/migrations/observers/transaction_duration_spec.rb'
+- './spec/lib/gitlab/database/migration_spec.rb'
+- './spec/lib/gitlab/database/migrations/reestablished_connection_stack_spec.rb'
+- './spec/lib/gitlab/database/migrations/runner_spec.rb'
+- './spec/lib/gitlab/database/migrations/test_background_runner_spec.rb'
+- './spec/lib/gitlab/database/migrations/test_batched_background_runner_spec.rb'
+- './spec/lib/gitlab/database/no_cross_db_foreign_keys_spec.rb'
+- './spec/lib/gitlab/database/obsolete_ignored_columns_spec.rb'
+- './spec/lib/gitlab/database/partitioning/detached_partition_dropper_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/partitioning_migration_helpers/table_management_helpers_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/replace_table_spec.rb'
+- './spec/lib/gitlab/database/partitioning/single_numeric_list_partition_spec.rb'
+- './spec/lib/gitlab/database/partitioning/sliding_list_strategy_spec.rb'
+- './spec/lib/gitlab/database/partitioning_spec.rb'
+- './spec/lib/gitlab/database/partitioning/time_partition_spec.rb'
+- './spec/lib/gitlab/database/pg_class_spec.rb'
+- './spec/lib/gitlab/database/postgres_autovacuum_activity_spec.rb'
+- './spec/lib/gitlab/database/postgres_foreign_key_spec.rb'
+- './spec/lib/gitlab/database/postgres_hll/batch_distinct_counter_spec.rb'
+- './spec/lib/gitlab/database/postgres_hll/buckets_spec.rb'
+- './spec/lib/gitlab/database/postgres_index_bloat_estimate_spec.rb'
+- './spec/lib/gitlab/database/postgres_index_spec.rb'
+- './spec/lib/gitlab/database/postgres_partitioned_table_spec.rb'
+- './spec/lib/gitlab/database/postgres_partition_spec.rb'
+- './spec/lib/gitlab/database/postgresql_adapter/dump_schema_versions_mixin_spec.rb'
+- './spec/lib/gitlab/database/postgresql_adapter/empty_query_ping_spec.rb'
+- './spec/lib/gitlab/database/postgresql_adapter/force_disconnectable_mixin_spec.rb'
+- './spec/lib/gitlab/database/postgresql_adapter/type_map_cache_spec.rb'
+- './spec/lib/gitlab/database/postgresql_database_tasks/load_schema_versions_mixin_spec.rb'
+- './spec/lib/gitlab/database/query_analyzers/gitlab_schemas_metrics_spec.rb'
+- './spec/lib/gitlab/database/query_analyzers/gitlab_schemas_validate_connection_spec.rb'
+- './spec/lib/gitlab/database/query_analyzer_spec.rb'
+- './spec/lib/gitlab/database/query_analyzers/prevent_cross_database_modification_spec.rb'
+- './spec/lib/gitlab/database/query_analyzers/restrict_allowed_schemas_spec.rb'
+- './spec/lib/gitlab/database/reflection_spec.rb'
+- './spec/lib/gitlab/database/reindexing/coordinator_spec.rb'
+- './spec/lib/gitlab/database/reindexing/grafana_notifier_spec.rb'
+- './spec/lib/gitlab/database/reindexing/index_selection_spec.rb'
+- './spec/lib/gitlab/database/reindexing/reindex_action_spec.rb'
+- './spec/lib/gitlab/database/reindexing/reindex_concurrently_spec.rb'
+- './spec/lib/gitlab/database/reindexing_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/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb'
+- './spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb'
+- './spec/lib/gitlab/database/schema_cache_with_renamed_table_spec.rb'
+- './spec/lib/gitlab/database/schema_cleaner_spec.rb'
+- './spec/lib/gitlab/database/schema_migrations/context_spec.rb'
+- './spec/lib/gitlab/database/schema_migrations/migrations_spec.rb'
+- './spec/lib/gitlab/database/sha_attribute_spec.rb'
+- './spec/lib/gitlab/database/shared_model_spec.rb'
+- './spec/lib/gitlab/database/similarity_score_spec.rb'
+- './spec/lib/gitlab/database_spec.rb'
+- './spec/lib/gitlab/database/transaction/context_spec.rb'
+- './spec/lib/gitlab/database/transaction/observer_spec.rb'
+- './spec/lib/gitlab/database/type/color_spec.rb'
+- './spec/lib/gitlab/database/type/json_pg_safe_spec.rb'
+- './spec/lib/gitlab/database/unidirectional_copy_trigger_spec.rb'
+- './spec/lib/gitlab/database/with_lock_retries_outside_transaction_spec.rb'
+- './spec/lib/gitlab/database/with_lock_retries_spec.rb'
+- './spec/lib/gitlab/data_builder/alert_spec.rb'
+- './spec/lib/gitlab/data_builder/archive_trace_spec.rb'
+- './spec/lib/gitlab/data_builder/build_spec.rb'
+- './spec/lib/gitlab/data_builder/deployment_spec.rb'
+- './spec/lib/gitlab/data_builder/feature_flag_spec.rb'
+- './spec/lib/gitlab/data_builder/issuable_spec.rb'
+- './spec/lib/gitlab/data_builder/note_spec.rb'
+- './spec/lib/gitlab/data_builder/pipeline_spec.rb'
+- './spec/lib/gitlab/data_builder/push_spec.rb'
+- './spec/lib/gitlab/data_builder/wiki_page_spec.rb'
+- './spec/lib/gitlab/default_branch_spec.rb'
+- './spec/lib/gitlab/dependency_linker/base_linker_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/godeps_json_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/package_json_linker_spec.rb'
+- './spec/lib/gitlab/dependency_linker/parser/gemfile_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/dependency_linker_spec.rb'
+- './spec/lib/gitlab/deploy_key_access_spec.rb'
+- './spec/lib/gitlab/diff/char_diff_spec.rb'
+- './spec/lib/gitlab/diff/diff_refs_spec.rb'
+- './spec/lib/gitlab/diff/file_collection/base_spec.rb'
+- './spec/lib/gitlab/diff/file_collection/commit_spec.rb'
+- './spec/lib/gitlab/diff/file_collection/compare_spec.rb'
+- './spec/lib/gitlab/diff/file_collection/merge_request_diff_base_spec.rb'
+- './spec/lib/gitlab/diff/file_collection/merge_request_diff_batch_spec.rb'
+- './spec/lib/gitlab/diff/file_collection/merge_request_diff_spec.rb'
+- './spec/lib/gitlab/diff/file_collection_sorter_spec.rb'
+- './spec/lib/gitlab/diff/file_spec.rb'
+- './spec/lib/gitlab/diff/formatters/image_formatter_spec.rb'
+- './spec/lib/gitlab/diff/formatters/text_formatter_spec.rb'
+- './spec/lib/gitlab/diff/highlight_cache_spec.rb'
+- './spec/lib/gitlab/diff/highlight_spec.rb'
+- './spec/lib/gitlab/diff/inline_diff_markdown_marker_spec.rb'
+- './spec/lib/gitlab/diff/inline_diff_marker_spec.rb'
+- './spec/lib/gitlab/diff/inline_diff_spec.rb'
+- './spec/lib/gitlab/diff/line_mapper_spec.rb'
+- './spec/lib/gitlab/diff/line_spec.rb'
+- './spec/lib/gitlab/diff/lines_unfolder_spec.rb'
+- './spec/lib/gitlab/diff/pair_selector_spec.rb'
+- './spec/lib/gitlab/diff/parallel_diff_spec.rb'
+- './spec/lib/gitlab/diff/parser_spec.rb'
+- './spec/lib/gitlab/diff/position_collection_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/diff/position_tracer_spec.rb'
+- './spec/lib/gitlab/diff/rendered/notebook/diff_file_helper_spec.rb'
+- './spec/lib/gitlab/diff/rendered/notebook/diff_file_spec.rb'
+- './spec/lib/gitlab/diff/stats_cache_spec.rb'
+- './spec/lib/gitlab/diff/suggestion_diff_spec.rb'
+- './spec/lib/gitlab/diff/suggestions_parser_spec.rb'
+- './spec/lib/gitlab/diff/suggestion_spec.rb'
+- './spec/lib/gitlab/discussions_diff/file_collection_spec.rb'
+- './spec/lib/gitlab/discussions_diff/highlight_cache_spec.rb'
+- './spec/lib/gitlab/doctor/secrets_spec.rb'
+- './spec/lib/gitlab/doorkeeper_secret_storing/pbkdf2_sha512_spec.rb'
+- './spec/lib/gitlab_edition_spec.rb'
+- './spec/lib/gitlab/email/attachment_uploader_spec.rb'
+- './spec/lib/gitlab/email/failure_handler_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_spec.rb'
+- './spec/lib/gitlab/email/handler/unsubscribe_handler_spec.rb'
+- './spec/lib/gitlab/email/hook/additional_headers_interceptor_spec.rb'
+- './spec/lib/gitlab/email/hook/delivery_metrics_observer_spec.rb'
+- './spec/lib/gitlab/email/hook/disable_email_interceptor_spec.rb'
+- './spec/lib/gitlab/email/hook/smime_signature_interceptor_spec.rb'
+- './spec/lib/gitlab/email/hook/validate_addresses_interceptor_spec.rb'
+- './spec/lib/gitlab/email/message/build_ios_app_guide_spec.rb'
+- './spec/lib/gitlab/email/message/in_product_marketing/admin_verify_spec.rb'
+- './spec/lib/gitlab/email/message/in_product_marketing/base_spec.rb'
+- './spec/lib/gitlab/email/message/in_product_marketing/create_spec.rb'
+- './spec/lib/gitlab/email/message/in_product_marketing/helper_spec.rb'
+- './spec/lib/gitlab/email/message/in_product_marketing_spec.rb'
+- './spec/lib/gitlab/email/message/in_product_marketing/team_short_spec.rb'
+- './spec/lib/gitlab/email/message/in_product_marketing/team_spec.rb'
+- './spec/lib/gitlab/email/message/in_product_marketing/trial_short_spec.rb'
+- './spec/lib/gitlab/email/message/in_product_marketing/trial_spec.rb'
+- './spec/lib/gitlab/email/message/in_product_marketing/verify_spec.rb'
+- './spec/lib/gitlab/email/message/repository_push_spec.rb'
+- './spec/lib/gitlab/email/receiver_spec.rb'
+- './spec/lib/gitlab/email/reply_parser_spec.rb'
+- './spec/lib/gitlab/email/service_desk_receiver_spec.rb'
+- './spec/lib/gitlab/email/smime/signer_spec.rb'
+- './spec/lib/gitlab/emoji_spec.rb'
+- './spec/lib/gitlab/empty_search_results_spec.rb'
+- './spec/lib/gitlab/encoding_helper_spec.rb'
+- './spec/lib/gitlab/encrypted_configuration_spec.rb'
+- './spec/lib/gitlab/endpoint_attributes_spec.rb'
+- './spec/lib/gitlab/error_tracking/context_payload_generator_spec.rb'
+- './spec/lib/gitlab/error_tracking/error_repository/open_api_strategy_spec.rb'
+- './spec/lib/gitlab/error_tracking/log_formatter_spec.rb'
+- './spec/lib/gitlab/error_tracking/logger_spec.rb'
+- './spec/lib/gitlab/error_tracking/processor/context_payload_processor_spec.rb'
+- './spec/lib/gitlab/error_tracking/processor/grpc_error_processor_spec.rb'
+- './spec/lib/gitlab/error_tracking/processor/sanitize_error_message_processor_spec.rb'
+- './spec/lib/gitlab/error_tracking/processor/sanitizer_processor_spec.rb'
+- './spec/lib/gitlab/error_tracking/processor/sidekiq_processor_spec.rb'
+- './spec/lib/gitlab/error_tracking_spec.rb'
+- './spec/lib/gitlab/error_tracking/stack_trace_highlight_decorator_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/etag_caching/store_spec.rb'
+- './spec/lib/gitlab/event_store/event_spec.rb'
+- './spec/lib/gitlab/event_store/store_spec.rb'
+- './spec/lib/gitlab/exception_log_formatter_spec.rb'
+- './spec/lib/gitlab/exceptions_app_spec.rb'
+- './spec/lib/gitlab/exclusive_lease_helpers/sleeping_lock_spec.rb'
+- './spec/lib/gitlab/exclusive_lease_helpers_spec.rb'
+- './spec/lib/gitlab/exclusive_lease_spec.rb'
+- './spec/lib/gitlab/experimentation/controller_concern_spec.rb'
+- './spec/lib/gitlab/experimentation/experiment_spec.rb'
+- './spec/lib/gitlab/experimentation/group_types_spec.rb'
+- './spec/lib/gitlab/experimentation_spec.rb'
+- './spec/lib/gitlab/experiment/rollout/feature_spec.rb'
+- './spec/lib/gitlab/external_authorization/access_spec.rb'
+- './spec/lib/gitlab/external_authorization/cache_spec.rb'
+- './spec/lib/gitlab/external_authorization/client_spec.rb'
+- './spec/lib/gitlab/external_authorization/logger_spec.rb'
+- './spec/lib/gitlab/external_authorization/response_spec.rb'
+- './spec/lib/gitlab/external_authorization_spec.rb'
+- './spec/lib/gitlab/fake_application_settings_spec.rb'
+- './spec/lib/gitlab/faraday/error_callback_spec.rb'
+- './spec/lib/gitlab/favicon_spec.rb'
+- './spec/lib/gitlab/feature_categories_spec.rb'
+- './spec/lib/gitlab/file_detector_spec.rb'
+- './spec/lib/gitlab/file_finder_spec.rb'
+- './spec/lib/gitlab/file_hook_spec.rb'
+- './spec/lib/gitlab/file_markdown_link_builder_spec.rb'
+- './spec/lib/gitlab/file_type_detection_spec.rb'
+- './spec/lib/gitlab/fips_spec.rb'
+- './spec/lib/gitlab/fogbugz_import/client_spec.rb'
+- './spec/lib/gitlab/fogbugz_import/importer_spec.rb'
+- './spec/lib/gitlab/fogbugz_import/project_creator_spec.rb'
+- './spec/lib/gitlab/form_builders/gitlab_ui_form_builder_spec.rb'
+- './spec/lib/gitlab/gfm/reference_rewriter_spec.rb'
+- './spec/lib/gitlab/gfm/uploads_rewriter_spec.rb'
+- './spec/lib/gitlab/git_access_design_spec.rb'
+- './spec/lib/gitlab/git_access_project_spec.rb'
+- './spec/lib/gitlab/git_access_snippet_spec.rb'
+- './spec/lib/gitlab/git_access_spec.rb'
+- './spec/lib/gitlab/git_access_wiki_spec.rb'
+- './spec/lib/gitlab/gitaly_client/blob_service_spec.rb'
+- './spec/lib/gitlab/gitaly_client/blobs_stitcher_spec.rb'
+- './spec/lib/gitlab/gitaly_client/call_spec.rb'
+- './spec/lib/gitlab/gitaly_client/cleanup_service_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/diff_spec.rb'
+- './spec/lib/gitlab/gitaly_client/diff_stitcher_spec.rb'
+- './spec/lib/gitlab/gitaly_client/health_check_service_spec.rb'
+- './spec/lib/gitlab/gitaly_client/object_pool_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/server_service_spec.rb'
+- './spec/lib/gitlab/gitaly_client_spec.rb'
+- './spec/lib/gitlab/gitaly_client/storage_settings_spec.rb'
+- './spec/lib/gitlab/gitaly_client/util_spec.rb'
+- './spec/lib/gitlab/gitaly_client/wiki_service_spec.rb'
+- './spec/lib/gitlab/git/attributes_at_ref_parser_spec.rb'
+- './spec/lib/gitlab/git/attributes_parser_spec.rb'
+- './spec/lib/gitlab/git/base_error_spec.rb'
+- './spec/lib/gitlab/git/blame_spec.rb'
+- './spec/lib/gitlab/git/blob_spec.rb'
+- './spec/lib/gitlab/git/branch_spec.rb'
+- './spec/lib/gitlab/git/bundle_file_spec.rb'
+- './spec/lib/gitlab/git/changed_path_spec.rb'
+- './spec/lib/gitlab/git/changes_spec.rb'
+- './spec/lib/gitlab/git/commit_spec.rb'
+- './spec/lib/gitlab/git/commit_stats_spec.rb'
+- './spec/lib/gitlab/git/compare_spec.rb'
+- './spec/lib/gitlab/git/conflict/file_spec.rb'
+- './spec/lib/gitlab/git/conflict/parser_spec.rb'
+- './spec/lib/gitlab/git/conflict/resolver_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/diff_stats_collection_spec.rb'
+- './spec/lib/gitlab/git/gitmodules_parser_spec.rb'
+- './spec/lib/gitlab/git/hook_env_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/events/base_importer_spec.rb'
+- './spec/lib/gitlab/github_import/importer/events/changed_assignee_spec.rb'
+- './spec/lib/gitlab/github_import/importer/events/changed_label_spec.rb'
+- './spec/lib/gitlab/github_import/importer/events/changed_milestone_spec.rb'
+- './spec/lib/gitlab/github_import/importer/events/closed_spec.rb'
+- './spec/lib/gitlab/github_import/importer/events/cross_referenced_spec.rb'
+- './spec/lib/gitlab/github_import/importer/events/renamed_spec.rb'
+- './spec/lib/gitlab/github_import/importer/events/reopened_spec.rb'
+- './spec/lib/gitlab/github_import/importer/issue_and_label_links_importer_spec.rb'
+- './spec/lib/gitlab/github_import/importer/issue_event_importer_spec.rb'
+- './spec/lib/gitlab/github_import/importer/issue_events_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_object_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_events_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/label_finder_spec.rb'
+- './spec/lib/gitlab/github_import/logger_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/diff_notes/suggestion_formatter_spec.rb'
+- './spec/lib/gitlab/github_import/representation/expose_attribute_spec.rb'
+- './spec/lib/gitlab/github_import/representation/issue_event_spec.rb'
+- './spec/lib/gitlab/github_import/representation/issue_spec.rb'
+- './spec/lib/gitlab/github_import/representation/lfs_object_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_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/single_endpoint_notes_importing_spec.rb'
+- './spec/lib/gitlab/github_import_spec.rb'
+- './spec/lib/gitlab/github_import/user_finder_spec.rb'
+- './spec/lib/gitlab/git/keep_around_spec.rb'
+- './spec/lib/gitlab/gitlab_import/client_spec.rb'
+- './spec/lib/gitlab/gitlab_import/importer_spec.rb'
+- './spec/lib/gitlab/gitlab_import/project_creator_spec.rb'
+- './spec/lib/gitlab/git/lfs_changes_spec.rb'
+- './spec/lib/gitlab/git/lfs_pointer_file_spec.rb'
+- './spec/lib/gitlab/git/merge_base_spec.rb'
+- './spec/lib/gitlab/git/object_pool_spec.rb'
+- './spec/lib/gitlab/git/patches/collection_spec.rb'
+- './spec/lib/gitlab/git/patches/commit_patches_spec.rb'
+- './spec/lib/gitlab/git/patches/patch_spec.rb'
+- './spec/lib/gitlab/git_post_receive_spec.rb'
+- './spec/lib/gitlab/git/pre_receive_error_spec.rb'
+- './spec/lib/gitlab/git/push_spec.rb'
+- './spec/lib/gitlab/git/raw_diff_change_spec.rb'
+- './spec/lib/gitlab/git_ref_validator_spec.rb'
+- './spec/lib/gitlab/git/remote_mirror_spec.rb'
+- './spec/lib/gitlab/git/repository_cleaner_spec.rb'
+- './spec/lib/gitlab/git/repository_spec.rb'
+- './spec/lib/gitlab/git/rugged_impl/use_rugged_spec.rb'
+- './spec/lib/gitlab/git_spec.rb'
+- './spec/lib/gitlab/git/tag_spec.rb'
+- './spec/lib/gitlab/git/tree_spec.rb'
+- './spec/lib/gitlab/git/user_spec.rb'
+- './spec/lib/gitlab/git/util_spec.rb'
+- './spec/lib/gitlab/git/wiki_page_version_spec.rb'
+- './spec/lib/gitlab/git/wiki_spec.rb'
+- './spec/lib/gitlab/git/wraps_gitaly_errors_spec.rb'
+- './spec/lib/gitlab/global_id/deprecations_spec.rb'
+- './spec/lib/gitlab/global_id_spec.rb'
+- './spec/lib/gitlab/gl_repository/identifier_spec.rb'
+- './spec/lib/gitlab/gl_repository/repo_type_spec.rb'
+- './spec/lib/gitlab/gl_repository_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/formatters/lograge_with_timestamp_spec.rb'
+- './spec/lib/gitlab/grape_logging/loggers/cloudflare_logger_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/response_logger_spec.rb'
+- './spec/lib/gitlab/grape_logging/loggers/token_logger_spec.rb'
+- './spec/lib/gitlab/grape_logging/loggers/urgency_logger_spec.rb'
+- './spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb'
+- './spec/lib/gitlab/graphql/authorize/object_authorization_spec.rb'
+- './spec/lib/gitlab/graphql/batch_key_spec.rb'
+- './spec/lib/gitlab/graphql/calls_gitaly/field_extension_spec.rb'
+- './spec/lib/gitlab/graphql/copy_field_description_spec.rb'
+- './spec/lib/gitlab/graphql/deprecation_spec.rb'
+- './spec/lib/gitlab/graphql/generic_tracing_spec.rb'
+- './spec/lib/gitlab/graphql/known_operations_spec.rb'
+- './spec/lib/gitlab/graphql/lazy_spec.rb'
+- './spec/lib/gitlab/graphql/loaders/batch_commit_loader_spec.rb'
+- './spec/lib/gitlab/graphql/loaders/batch_lfs_oid_loader_spec.rb'
+- './spec/lib/gitlab/graphql/loaders/batch_model_loader_spec.rb'
+- './spec/lib/gitlab/graphql/loaders/batch_project_statistics_loader_spec.rb'
+- './spec/lib/gitlab/graphql/loaders/batch_root_storage_statistics_loader_spec.rb'
+- './spec/lib/gitlab/graphql/loaders/issuable_loader_spec.rb'
+- './spec/lib/gitlab/graphql_logger_spec.rb'
+- './spec/lib/gitlab/graphql/markdown_field_spec.rb'
+- './spec/lib/gitlab/graphql/mount_mutation_spec.rb'
+- './spec/lib/gitlab/graphql/negatable_arguments_spec.rb'
+- './spec/lib/gitlab/graphql/pagination/active_record_array_connection_spec.rb'
+- './spec/lib/gitlab/graphql/pagination/array_connection_spec.rb'
+- './spec/lib/gitlab/graphql/pagination/connections_spec.rb'
+- './spec/lib/gitlab/graphql/pagination/externally_paginated_array_connection_spec.rb'
+- './spec/lib/gitlab/graphql/pagination/keyset/connection_spec.rb'
+- './spec/lib/gitlab/graphql/pagination/keyset/last_items_spec.rb'
+- './spec/lib/gitlab/graphql/pagination/offset_active_record_relation_connection_spec.rb'
+- './spec/lib/gitlab/graphql/present/field_extension_spec.rb'
+- './spec/lib/gitlab/graphql/queries_spec.rb'
+- './spec/lib/gitlab/graphql/query_analyzers/ast/logger_analyzer_spec.rb'
+- './spec/lib/gitlab/graphql/query_analyzers/ast/recursion_analyzer_spec.rb'
+- './spec/lib/gitlab/graphql/representation/submodule_tree_entry_spec.rb'
+- './spec/lib/gitlab/graphql/representation/tree_entry_spec.rb'
+- './spec/lib/gitlab/graphql/timeout_spec.rb'
+- './spec/lib/gitlab/graphql/tracers/application_context_tracer_spec.rb'
+- './spec/lib/gitlab/graphql/tracers/logger_tracer_spec.rb'
+- './spec/lib/gitlab/graphql/tracers/metrics_tracer_spec.rb'
+- './spec/lib/gitlab/graphql/tracers/timer_tracer_spec.rb'
+- './spec/lib/gitlab/graphql/type_name_deprecations_spec.rb'
+- './spec/lib/gitlab/graphs/commits_spec.rb'
+- './spec/lib/gitlab/group_search_results_spec.rb'
+- './spec/lib/gitlab/harbor/client_spec.rb'
+- './spec/lib/gitlab/harbor/query_spec.rb'
+- './spec/lib/gitlab/hashed_path_spec.rb'
+- './spec/lib/gitlab/hashed_storage/migrator_spec.rb'
+- './spec/lib/gitlab/health_checks/db_check_spec.rb'
+- './spec/lib/gitlab/health_checks/gitaly_check_spec.rb'
+- './spec/lib/gitlab/health_checks/master_check_spec.rb'
+- './spec/lib/gitlab/health_checks/middleware_spec.rb'
+- './spec/lib/gitlab/health_checks/probes/collection_spec.rb'
+- './spec/lib/gitlab/health_checks/puma_check_spec.rb'
+- './spec/lib/gitlab/health_checks/redis/cache_check_spec.rb'
+- './spec/lib/gitlab/health_checks/redis/queues_check_spec.rb'
+- './spec/lib/gitlab/health_checks/redis/rate_limiting_check_spec.rb'
+- './spec/lib/gitlab/health_checks/redis/redis_check_spec.rb'
+- './spec/lib/gitlab/health_checks/redis/sessions_check_spec.rb'
+- './spec/lib/gitlab/health_checks/redis/shared_state_check_spec.rb'
+- './spec/lib/gitlab/health_checks/redis/trace_chunks_check_spec.rb'
+- './spec/lib/gitlab/health_checks/server_spec.rb'
+- './spec/lib/gitlab/highlight_spec.rb'
+- './spec/lib/gitlab/hook_data/base_builder_spec.rb'
+- './spec/lib/gitlab/hook_data/group_builder_spec.rb'
+- './spec/lib/gitlab/hook_data/group_member_builder_spec.rb'
+- './spec/lib/gitlab/hook_data/issue_builder_spec.rb'
+- './spec/lib/gitlab/hook_data/key_builder_spec.rb'
+- './spec/lib/gitlab/hook_data/merge_request_builder_spec.rb'
+- './spec/lib/gitlab/hook_data/project_builder_spec.rb'
+- './spec/lib/gitlab/hook_data/project_member_builder_spec.rb'
+- './spec/lib/gitlab/hook_data/release_builder_spec.rb'
+- './spec/lib/gitlab/hook_data/subgroup_builder_spec.rb'
+- './spec/lib/gitlab/hook_data/user_builder_spec.rb'
+- './spec/lib/gitlab/hotlinking_detector_spec.rb'
+- './spec/lib/gitlab/http_connection_adapter_spec.rb'
+- './spec/lib/gitlab/http_io_spec.rb'
+- './spec/lib/gitlab/http_spec.rb'
+- './spec/lib/gitlab/i18n/metadata_entry_spec.rb'
+- './spec/lib/gitlab/i18n/po_linter_spec.rb'
+- './spec/lib/gitlab/i18n_spec.rb'
+- './spec/lib/gitlab/i18n/translation_entry_spec.rb'
+- './spec/lib/gitlab/identifier_spec.rb'
+- './spec/lib/gitlab/import/database_helpers_spec.rb'
+- './spec/lib/gitlab/import_export/after_export_strategies/base_after_export_strategy_spec.rb'
+- './spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb'
+- './spec/lib/gitlab/import_export/after_export_strategy_builder_spec.rb'
+- './spec/lib/gitlab/import_export/attribute_cleaner_spec.rb'
+- './spec/lib/gitlab/import_export/attribute_configuration_spec.rb'
+- './spec/lib/gitlab/import_export/attributes_finder_spec.rb'
+- './spec/lib/gitlab/import_export/attributes_permitter_spec.rb'
+- './spec/lib/gitlab/import_export/avatar_restorer_spec.rb'
+- './spec/lib/gitlab/import_export/avatar_saver_spec.rb'
+- './spec/lib/gitlab/import_export/base/object_builder_spec.rb'
+- './spec/lib/gitlab/import_export/base/relation_factory_spec.rb'
+- './spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb'
+- './spec/lib/gitlab/import_export/command_line_util_spec.rb'
+- './spec/lib/gitlab/import_export/config_spec.rb'
+- './spec/lib/gitlab/import_export/decompressed_archive_size_validator_spec.rb'
+- './spec/lib/gitlab/import_export/design_repo_restorer_spec.rb'
+- './spec/lib/gitlab/import_export/design_repo_saver_spec.rb'
+- './spec/lib/gitlab/import_export/duration_measuring_spec.rb'
+- './spec/lib/gitlab/import_export/error_spec.rb'
+- './spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb'
+- './spec/lib/gitlab/import_export/file_importer_spec.rb'
+- './spec/lib/gitlab/import_export/fork_spec.rb'
+- './spec/lib/gitlab/import_export/group/legacy_tree_restorer_spec.rb'
+- './spec/lib/gitlab/import_export/group/legacy_tree_saver_spec.rb'
+- './spec/lib/gitlab/import_export/group/object_builder_spec.rb'
+- './spec/lib/gitlab/import_export/group/relation_factory_spec.rb'
+- './spec/lib/gitlab/import_export/group/relation_tree_restorer_spec.rb'
+- './spec/lib/gitlab/import_export/group/tree_restorer_spec.rb'
+- './spec/lib/gitlab/import_export/group/tree_saver_spec.rb'
+- './spec/lib/gitlab/import_export/hash_util_spec.rb'
+- './spec/lib/gitlab/import_export/importer_spec.rb'
+- './spec/lib/gitlab/import_export/import_export_equivalence_spec.rb'
+- './spec/lib/gitlab/import_export/import_export_spec.rb'
+- './spec/lib/gitlab/import_export/import_failure_service_spec.rb'
+- './spec/lib/gitlab/import_export/import_test_coverage_spec.rb'
+- './spec/lib/gitlab/import_export/json/legacy_reader/file_spec.rb'
+- './spec/lib/gitlab/import_export/json/legacy_reader/hash_spec.rb'
+- './spec/lib/gitlab/import_export/json/legacy_writer_spec.rb'
+- './spec/lib/gitlab/import_export/json/ndjson_reader_spec.rb'
+- './spec/lib/gitlab/import_export/json/ndjson_writer_spec.rb'
+- './spec/lib/gitlab/import_export/json/streaming_serializer_spec.rb'
+- './spec/lib/gitlab/import_export/legacy_relation_tree_saver_spec.rb'
+- './spec/lib/gitlab/import_export/lfs_restorer_spec.rb'
+- './spec/lib/gitlab/import_export/lfs_saver_spec.rb'
+- './spec/lib/gitlab/import_export/log_util_spec.rb'
+- './spec/lib/gitlab/import_export/members_mapper_spec.rb'
+- './spec/lib/gitlab/import_export/merge_request_parser_spec.rb'
+- './spec/lib/gitlab/import_export/model_configuration_spec.rb'
+- './spec/lib/gitlab/import_export/project/export_task_spec.rb'
+- './spec/lib/gitlab/import_export/project/import_task_spec.rb'
+- './spec/lib/gitlab/import_export/project/object_builder_spec.rb'
+- './spec/lib/gitlab/import_export/project/relation_factory_spec.rb'
+- './spec/lib/gitlab/import_export/project/relation_saver_spec.rb'
+- './spec/lib/gitlab/import_export/project/relation_tree_restorer_spec.rb'
+- './spec/lib/gitlab/import_export/project/sample/date_calculator_spec.rb'
+- './spec/lib/gitlab/import_export/project/sample/relation_factory_spec.rb'
+- './spec/lib/gitlab/import_export/project/sample/relation_tree_restorer_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/import_export/reader_spec.rb'
+- './spec/lib/gitlab/import_export/references_configuration_spec.rb'
+- './spec/lib/gitlab/import_export/remote_stream_upload_spec.rb'
+- './spec/lib/gitlab/import_export/repo_restorer_spec.rb'
+- './spec/lib/gitlab/import_export/repo_saver_spec.rb'
+- './spec/lib/gitlab/import_export/saver_spec.rb'
+- './spec/lib/gitlab/import_export/shared_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_restorer_spec.rb'
+- './spec/lib/gitlab/import_export/snippets_repo_saver_spec.rb'
+- './spec/lib/gitlab/import_export/uploads_manager_spec.rb'
+- './spec/lib/gitlab/import_export/uploads_restorer_spec.rb'
+- './spec/lib/gitlab/import_export/uploads_saver_spec.rb'
+- './spec/lib/gitlab/import_export/version_checker_spec.rb'
+- './spec/lib/gitlab/import_export/wiki_repo_saver_spec.rb'
+- './spec/lib/gitlab/import_formatter_spec.rb'
+- './spec/lib/gitlab/import/import_failure_service_spec.rb'
+- './spec/lib/gitlab/import/logger_spec.rb'
+- './spec/lib/gitlab/import/merge_request_creator_spec.rb'
+- './spec/lib/gitlab/import/merge_request_helpers_spec.rb'
+- './spec/lib/gitlab/import/metrics_spec.rb'
+- './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'
+- './spec/lib/gitlab/instrumentation/rate_limiting_gates_spec.rb'
+- './spec/lib/gitlab/instrumentation/redis_base_spec.rb'
+- './spec/lib/gitlab/instrumentation/redis_cluster_validator_spec.rb'
+- './spec/lib/gitlab/instrumentation/redis_interceptor_spec.rb'
+- './spec/lib/gitlab/instrumentation/redis_spec.rb'
+- './spec/lib/gitlab/internal_post_receive/response_spec.rb'
+- './spec/lib/gitlab/issuable/clone/attributes_rewriter_spec.rb'
+- './spec/lib/gitlab/issuable/clone/copy_resource_events_service_spec.rb'
+- './spec/lib/gitlab/issuable_metadata_spec.rb'
+- './spec/lib/gitlab/issuables_count_for_state_spec.rb'
+- './spec/lib/gitlab/issuable_sorter_spec.rb'
+- './spec/lib/gitlab/issues/rebalancing/state_spec.rb'
+- './spec/lib/gitlab/jira/dvcs_spec.rb'
+- './spec/lib/gitlab/jira_import/base_importer_spec.rb'
+- './spec/lib/gitlab/jira_import/handle_labels_service_spec.rb'
+- './spec/lib/gitlab/jira_import/issue_serializer_spec.rb'
+- './spec/lib/gitlab/jira_import/issues_importer_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/jira/middleware_spec.rb'
+- './spec/lib/gitlab/job_waiter_spec.rb'
+- './spec/lib/gitlab/json_cache_spec.rb'
+- './spec/lib/gitlab/json_logger_spec.rb'
+- './spec/lib/gitlab/json_spec.rb'
+- './spec/lib/gitlab/jwt_authenticatable_spec.rb'
+- './spec/lib/gitlab/jwt_token_spec.rb'
+- './spec/lib/gitlab/kas/client_spec.rb'
+- './spec/lib/gitlab/kas_spec.rb'
+- './spec/lib/gitlab/kroki_spec.rb'
+- './spec/lib/gitlab/kubernetes/cluster_role_binding_spec.rb'
+- './spec/lib/gitlab/kubernetes/config_maps/aws_node_auth_spec.rb'
+- './spec/lib/gitlab/kubernetes/config_map_spec.rb'
+- './spec/lib/gitlab/kubernetes/default_namespace_spec.rb'
+- './spec/lib/gitlab/kubernetes/deployment_spec.rb'
+- './spec/lib/gitlab/kubernetes/generic_secret_spec.rb'
+- './spec/lib/gitlab/kubernetes/helm/api_spec.rb'
+- './spec/lib/gitlab/kubernetes/helm/pod_spec.rb'
+- './spec/lib/gitlab/kubernetes/helm/v2/base_command_spec.rb'
+- './spec/lib/gitlab/kubernetes/helm/v2/certificate_spec.rb'
+- './spec/lib/gitlab/kubernetes/helm/v2/delete_command_spec.rb'
+- './spec/lib/gitlab/kubernetes/helm/v2/init_command_spec.rb'
+- './spec/lib/gitlab/kubernetes/helm/v2/install_command_spec.rb'
+- './spec/lib/gitlab/kubernetes/helm/v2/patch_command_spec.rb'
+- './spec/lib/gitlab/kubernetes/helm/v2/reset_command_spec.rb'
+- './spec/lib/gitlab/kubernetes/helm/v3/base_command_spec.rb'
+- './spec/lib/gitlab/kubernetes/helm/v3/delete_command_spec.rb'
+- './spec/lib/gitlab/kubernetes/helm/v3/install_command_spec.rb'
+- './spec/lib/gitlab/kubernetes/helm/v3/patch_command_spec.rb'
+- './spec/lib/gitlab/kubernetes/ingress_spec.rb'
+- './spec/lib/gitlab/kubernetes/kube_client_spec.rb'
+- './spec/lib/gitlab/kubernetes/kubeconfig/entry/cluster_spec.rb'
+- './spec/lib/gitlab/kubernetes/kubeconfig/entry/context_spec.rb'
+- './spec/lib/gitlab/kubernetes/kubeconfig/entry/user_spec.rb'
+- './spec/lib/gitlab/kubernetes/kubeconfig/template_spec.rb'
+- './spec/lib/gitlab/kubernetes/kubectl_cmd_spec.rb'
+- './spec/lib/gitlab/kubernetes/namespace_spec.rb'
+- './spec/lib/gitlab/kubernetes/node_spec.rb'
+- './spec/lib/gitlab/kubernetes/pod_cmd_spec.rb'
+- './spec/lib/gitlab/kubernetes/role_binding_spec.rb'
+- './spec/lib/gitlab/kubernetes/role_spec.rb'
+- './spec/lib/gitlab/kubernetes/rollout_instances_spec.rb'
+- './spec/lib/gitlab/kubernetes/rollout_status_spec.rb'
+- './spec/lib/gitlab/kubernetes/service_account_spec.rb'
+- './spec/lib/gitlab/kubernetes/service_account_token_spec.rb'
+- './spec/lib/gitlab/kubernetes_spec.rb'
+- './spec/lib/gitlab/kubernetes/tls_secret_spec.rb'
+- './spec/lib/gitlab/language_data_spec.rb'
+- './spec/lib/gitlab/language_detection_spec.rb'
+- './spec/lib/gitlab/lazy_spec.rb'
+- './spec/lib/gitlab/legacy_github_import/branch_formatter_spec.rb'
+- './spec/lib/gitlab/legacy_github_import/client_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/project_creator_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/legacy_github_import/wiki_formatter_spec.rb'
+- './spec/lib/gitlab/lets_encrypt/challenge_spec.rb'
+- './spec/lib/gitlab/lets_encrypt/client_spec.rb'
+- './spec/lib/gitlab/lets_encrypt/order_spec.rb'
+- './spec/lib/gitlab/lets_encrypt_spec.rb'
+- './spec/lib/gitlab/lfs/client_spec.rb'
+- './spec/lib/gitlab/lfs_token_spec.rb'
+- './spec/lib/gitlab/local_and_remote_storage_migration/artifact_migrater_spec.rb'
+- './spec/lib/gitlab/local_and_remote_storage_migration/pages_deployment_migrater_spec.rb'
+- './spec/lib/gitlab/logger_spec.rb'
+- './spec/lib/gitlab/logging/cloudflare_helper_spec.rb'
+- './spec/lib/gitlab/lograge/custom_options_spec.rb'
+- './spec/lib/gitlab/log_timestamp_formatter_spec.rb'
+- './spec/lib/gitlab/loop_helpers_spec.rb'
+- './spec/lib/gitlab/mailgun/webhook_processors/failure_logger_spec.rb'
+- './spec/lib/gitlab/mailgun/webhook_processors/member_invites_spec.rb'
+- './spec/lib/gitlab/mail_room/authenticator_spec.rb'
+- './spec/lib/gitlab/mail_room/mail_room_spec.rb'
+- './spec/lib/gitlab/manifest_import/manifest_spec.rb'
+- './spec/lib/gitlab/manifest_import/metadata_spec.rb'
+- './spec/lib/gitlab/manifest_import/project_creator_spec.rb'
+- './spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb'
+- './spec/lib/gitlab/markdown_cache/field_data_spec.rb'
+- './spec/lib/gitlab/markdown_cache/redis/extension_spec.rb'
+- './spec/lib/gitlab/markdown_cache/redis/store_spec.rb'
+- './spec/lib/gitlab/marker_range_spec.rb'
+- './spec/lib/gitlab/markup_helper_spec.rb'
+- './spec/lib/gitlab/memory/instrumentation_spec.rb'
+- './spec/lib/gitlab/memory/jemalloc_spec.rb'
+- './spec/lib/gitlab/memory/reports_daemon_spec.rb'
+- './spec/lib/gitlab/memory/reports/jemalloc_stats_spec.rb'
+- './spec/lib/gitlab/memory/watchdog_spec.rb'
+- './spec/lib/gitlab/merge_requests/commit_message_generator_spec.rb'
+- './spec/lib/gitlab/merge_requests/mergeability/check_result_spec.rb'
+- './spec/lib/gitlab/merge_requests/mergeability/redis_interface_spec.rb'
+- './spec/lib/gitlab/merge_requests/mergeability/results_store_spec.rb'
+- './spec/lib/gitlab/metrics/background_transaction_spec.rb'
+- './spec/lib/gitlab/metrics/boot_time_tracker_spec.rb'
+- './spec/lib/gitlab/metrics/dashboard/cache_spec.rb'
+- './spec/lib/gitlab/metrics/dashboard/defaults_spec.rb'
+- './spec/lib/gitlab/metrics/dashboard/finder_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/processor_spec.rb'
+- './spec/lib/gitlab/metrics/dashboard/repo_dashboard_finder_spec.rb'
+- './spec/lib/gitlab/metrics/dashboard/service_selector_spec.rb'
+- './spec/lib/gitlab/metrics/dashboard/stages/grafana_formatter_spec.rb'
+- './spec/lib/gitlab/metrics/dashboard/stages/metric_endpoint_inserter_spec.rb'
+- './spec/lib/gitlab/metrics/dashboard/stages/panel_ids_inserter_spec.rb'
+- './spec/lib/gitlab/metrics/dashboard/stages/track_panel_type_spec.rb'
+- './spec/lib/gitlab/metrics/dashboard/stages/url_validator_spec.rb'
+- './spec/lib/gitlab/metrics/dashboard/stages/variable_endpoint_inserter_spec.rb'
+- './spec/lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics_spec.rb'
+- './spec/lib/gitlab/metrics/dashboard/url_spec.rb'
+- './spec/lib/gitlab/metrics/dashboard/validator/client_spec.rb'
+- './spec/lib/gitlab/metrics/dashboard/validator/custom_formats_spec.rb'
+- './spec/lib/gitlab/metrics/dashboard/validator/errors_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/delta_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/memory_spec.rb'
+- './spec/lib/gitlab/metrics/method_call_spec.rb'
+- './spec/lib/gitlab/metrics/methods_spec.rb'
+- './spec/lib/gitlab/metrics/prometheus_spec.rb'
+- './spec/lib/gitlab/metrics/rack_middleware_spec.rb'
+- './spec/lib/gitlab/metrics/rails_slis_spec.rb'
+- './spec/lib/gitlab/metrics/requests_rack_middleware_spec.rb'
+- './spec/lib/gitlab/metrics/samplers/action_cable_sampler_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/samplers/threads_sampler_spec.rb'
+- './spec/lib/gitlab/metrics/sli_spec.rb'
+- './spec/lib/gitlab/metrics_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/system_spec.rb'
+- './spec/lib/gitlab/metrics/transaction_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_ip_spoof_attack_error_spec.rb'
+- './spec/lib/gitlab/middleware/handle_malformed_strings_spec.rb'
+- './spec/lib/gitlab/middleware/memory_report_spec.rb'
+- './spec/lib/gitlab/middleware/multipart/handler_spec.rb'
+- './spec/lib/gitlab/middleware/multipart_spec.rb'
+- './spec/lib/gitlab/middleware/query_analyzer_spec.rb'
+- './spec/lib/gitlab/middleware/rack_multipart_tempfile_factory_spec.rb'
+- './spec/lib/gitlab/middleware/rails_queue_duration_spec.rb'
+- './spec/lib/gitlab/middleware/read_only_spec.rb'
+- './spec/lib/gitlab/middleware/release_env_spec.rb'
+- './spec/lib/gitlab/middleware/request_context_spec.rb'
+- './spec/lib/gitlab/middleware/same_site_cookies_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/monitor/demo_projects_spec.rb'
+- './spec/lib/gitlab/multi_collection_paginator_spec.rb'
+- './spec/lib/gitlab/multi_destination_logger_spec.rb'
+- './spec/lib/gitlab/namespaced_session_store_spec.rb'
+- './spec/lib/gitlab/nav/top_nav_menu_item_spec.rb'
+- './spec/lib/gitlab/net_http_adapter_spec.rb'
+- './spec/lib/gitlab/no_cache_headers_spec.rb'
+- './spec/lib/gitlab/noteable_metadata_spec.rb'
+- './spec/lib/gitlab/null_request_store_spec.rb'
+- './spec/lib/gitlab/object_hierarchy_spec.rb'
+- './spec/lib/gitlab/octokit/middleware_spec.rb'
+- './spec/lib/gitlab/omniauth_initializer_spec.rb'
+- './spec/lib/gitlab/optimistic_locking_spec.rb'
+- './spec/lib/gitlab/other_markup_spec.rb'
+- './spec/lib/gitlab/otp_key_rotator_spec.rb'
+- './spec/lib/gitlab/pages/cache_control_spec.rb'
+- './spec/lib/gitlab/pages/deployment_update_spec.rb'
+- './spec/lib/gitlab/pages/settings_spec.rb'
+- './spec/lib/gitlab/pages_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/column_order_definition_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/in_operator_optimization/array_scope_columns_spec.rb'
+- './spec/lib/gitlab/pagination/keyset/in_operator_optimization/column_data_spec.rb'
+- './spec/lib/gitlab/pagination/keyset/in_operator_optimization/order_by_column_data_spec.rb'
+- './spec/lib/gitlab/pagination/keyset/in_operator_optimization/order_by_columns_spec.rb'
+- './spec/lib/gitlab/pagination/keyset/in_operator_optimization/query_builder_spec.rb'
+- './spec/lib/gitlab/pagination/keyset/in_operator_optimization/strategies/order_values_loader_strategy_spec.rb'
+- './spec/lib/gitlab/pagination/keyset/in_operator_optimization/strategies/record_loader_strategy_spec.rb'
+- './spec/lib/gitlab/pagination/keyset/iterator_spec.rb'
+- './spec/lib/gitlab/pagination/keyset/order_spec.rb'
+- './spec/lib/gitlab/pagination/keyset/pager_spec.rb'
+- './spec/lib/gitlab/pagination/keyset/page_spec.rb'
+- './spec/lib/gitlab/pagination/keyset/paginator_spec.rb'
+- './spec/lib/gitlab/pagination/keyset/request_context_spec.rb'
+- './spec/lib/gitlab/pagination/keyset/simple_order_builder_spec.rb'
+- './spec/lib/gitlab/pagination/keyset_spec.rb'
+- './spec/lib/gitlab/pagination/offset_header_builder_spec.rb'
+- './spec/lib/gitlab/pagination/offset_header_builder_with_controller_spec.rb'
+- './spec/lib/gitlab/pagination/offset_pagination_spec.rb'
+- './spec/lib/gitlab/patch/action_cable_redis_listener_spec.rb'
+- './spec/lib/gitlab/patch/database_config_spec.rb'
+- './spec/lib/gitlab/patch/draw_route_spec.rb'
+- './spec/lib/gitlab/patch/prependable_spec.rb'
+- './spec/lib/gitlab/path_regex_spec.rb'
+- './spec/lib/gitlab/performance_bar/redis_adapter_when_peek_enabled_spec.rb'
+- './spec/lib/gitlab/performance_bar_spec.rb'
+- './spec/lib/gitlab/performance_bar/stats_spec.rb'
+- './spec/lib/gitlab/performance_bar/with_top_level_warnings_spec.rb'
+- './spec/lib/gitlab/phabricator_import/cache/map_spec.rb'
+- './spec/lib/gitlab/phabricator_import/conduit/client_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/tasks_response_spec.rb'
+- './spec/lib/gitlab/phabricator_import/conduit/user_spec.rb'
+- './spec/lib/gitlab/phabricator_import/conduit/users_response_spec.rb'
+- './spec/lib/gitlab/phabricator_import/importer_spec.rb'
+- './spec/lib/gitlab/phabricator_import/issues/importer_spec.rb'
+- './spec/lib/gitlab/phabricator_import/issues/task_importer_spec.rb'
+- './spec/lib/gitlab/phabricator_import/project_creator_spec.rb'
+- './spec/lib/gitlab/phabricator_import/representation/task_spec.rb'
+- './spec/lib/gitlab/phabricator_import/representation/user_spec.rb'
+- './spec/lib/gitlab/phabricator_import/user_finder_spec.rb'
+- './spec/lib/gitlab/phabricator_import/worker_state_spec.rb'
+- './spec/lib/gitlab/pipeline_scope_counts_spec.rb'
+- './spec/lib/gitlab/polling_interval_spec.rb'
+- './spec/lib/gitlab/popen/runner_spec.rb'
+- './spec/lib/gitlab/popen_spec.rb'
+- './spec/lib/gitlab/private_commit_email_spec.rb'
+- './spec/lib/gitlab/process_management_spec.rb'
+- './spec/lib/gitlab/process_memory_cache/helper_spec.rb'
+- './spec/lib/gitlab/process_supervisor_spec.rb'
+- './spec/lib/gitlab/profiler_spec.rb'
+- './spec/lib/gitlab/project_authorizations_spec.rb'
+- './spec/lib/gitlab/project_search_results_spec.rb'
+- './spec/lib/gitlab/project_stats_refresh_conflicts_logger_spec.rb'
+- './spec/lib/gitlab/project_template_spec.rb'
+- './spec/lib/gitlab/project_transfer_spec.rb'
+- './spec/lib/gitlab/prometheus/adapter_spec.rb'
+- './spec/lib/gitlab/prometheus/additional_metrics_parser_spec.rb'
+- './spec/lib/gitlab/prometheus_client_spec.rb'
+- './spec/lib/gitlab/prometheus/internal_spec.rb'
+- './spec/lib/gitlab/prometheus/metric_group_spec.rb'
+- './spec/lib/gitlab/prometheus/queries/additional_metrics_deployment_query_spec.rb'
+- './spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_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/prometheus/queries/validate_query_spec.rb'
+- './spec/lib/gitlab/prometheus/query_variables_spec.rb'
+- './spec/lib/gitlab/protocol_access_spec.rb'
+- './spec/lib/gitlab/puma_logging/json_formatter_spec.rb'
+- './spec/lib/gitlab/push_options_spec.rb'
+- './spec/lib/gitlab/query_limiting/active_support_subscriber_spec.rb'
+- './spec/lib/gitlab/query_limiting/middleware_spec.rb'
+- './spec/lib/gitlab/query_limiting_spec.rb'
+- './spec/lib/gitlab/query_limiting/transaction_spec.rb'
+- './spec/lib/gitlab/quick_actions/command_definition_spec.rb'
+- './spec/lib/gitlab/quick_actions/dsl_spec.rb'
+- './spec/lib/gitlab/quick_actions/extractor_spec.rb'
+- './spec/lib/gitlab/quick_actions/spend_time_and_date_separator_spec.rb'
+- './spec/lib/gitlab/quick_actions/substitution_definition_spec.rb'
+- './spec/lib/gitlab/quick_actions/users_extractor_spec.rb'
+- './spec/lib/gitlab/rack_attack/instrumented_cache_store_spec.rb'
+- './spec/lib/gitlab/rack_attack/request_spec.rb'
+- './spec/lib/gitlab/rack_attack_spec.rb'
+- './spec/lib/gitlab/rack_attack/user_allowlist_spec.rb'
+- './spec/lib/gitlab/reactive_cache_set_cache_spec.rb'
+- './spec/lib/gitlab/redis/boolean_spec.rb'
+- './spec/lib/gitlab/redis/cache_spec.rb'
+- './spec/lib/gitlab/redis/duplicate_jobs_spec.rb'
+- './spec/lib/gitlab/redis/hll_spec.rb'
+- './spec/lib/gitlab/redis/multi_store_spec.rb'
+- './spec/lib/gitlab/redis/queues_spec.rb'
+- './spec/lib/gitlab/redis/rate_limiting_spec.rb'
+- './spec/lib/gitlab/redis/sessions_spec.rb'
+- './spec/lib/gitlab/redis/shared_state_spec.rb'
+- './spec/lib/gitlab/redis/sidekiq_status_spec.rb'
+- './spec/lib/gitlab/redis/trace_chunks_spec.rb'
+- './spec/lib/gitlab/redis/wrapper_spec.rb'
+- './spec/lib/gitlab/reference_counter_spec.rb'
+- './spec/lib/gitlab/reference_extractor_spec.rb'
+- './spec/lib/gitlab/regex_requires_app_spec.rb'
+- './spec/lib/gitlab/regex_spec.rb'
+- './spec/lib/gitlab/relative_positioning/item_context_spec.rb'
+- './spec/lib/gitlab/relative_positioning/mover_spec.rb'
+- './spec/lib/gitlab/relative_positioning/range_spec.rb'
+- './spec/lib/gitlab/render_timeout_spec.rb'
+- './spec/lib/gitlab/repo_path_spec.rb'
+- './spec/lib/gitlab/repository_archive_rate_limiter_spec.rb'
+- './spec/lib/gitlab/repository_cache_adapter_spec.rb'
+- './spec/lib/gitlab/repository_cache/preloader_spec.rb'
+- './spec/lib/gitlab/repository_cache_spec.rb'
+- './spec/lib/gitlab/repository_hash_cache_spec.rb'
+- './spec/lib/gitlab/repository_set_cache_spec.rb'
+- './spec/lib/gitlab/repository_size_checker_spec.rb'
+- './spec/lib/gitlab/repository_size_error_message_spec.rb'
+- './spec/lib/gitlab/repository_url_builder_spec.rb'
+- './spec/lib/gitlab/request_context_spec.rb'
+- './spec/lib/gitlab/request_endpoints_spec.rb'
+- './spec/lib/gitlab/request_forgery_protection_spec.rb'
+- './spec/lib/gitlab/robots_txt/parser_spec.rb'
+- './spec/lib/gitlab/route_map_spec.rb'
+- './spec/lib/gitlab/routing_spec.rb'
+- './spec/lib/gitlab/rugged_instrumentation_spec.rb'
+- './spec/lib/gitlab/runtime_spec.rb'
+- './spec/lib/gitlab/saas_spec.rb'
+- './spec/lib/gitlab/safe_request_loader_spec.rb'
+- './spec/lib/gitlab/safe_request_purger_spec.rb'
+- './spec/lib/gitlab/safe_request_store_spec.rb'
+- './spec/lib/gitlab/sample_data_template_spec.rb'
+- './spec/lib/gitlab/sanitizers/exception_message_spec.rb'
+- './spec/lib/gitlab/sanitizers/exif_spec.rb'
+- './spec/lib/gitlab/sanitizers/svg_spec.rb'
+- './spec/lib/gitlab/search/abuse_detection_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/search_context/builder_spec.rb'
+- './spec/lib/gitlab/search_context/controller_concern_spec.rb'
+- './spec/lib/gitlab/search/found_blob_spec.rb'
+- './spec/lib/gitlab/search/found_wiki_page_spec.rb'
+- './spec/lib/gitlab/search/params_spec.rb'
+- './spec/lib/gitlab/search/query_spec.rb'
+- './spec/lib/gitlab/search/recent_issues_spec.rb'
+- './spec/lib/gitlab/search/recent_merge_requests_spec.rb'
+- './spec/lib/gitlab/search_results_spec.rb'
+- './spec/lib/gitlab/search/sort_options_spec.rb'
+- './spec/lib/gitlab/security/scan_configuration_spec.rb'
+- './spec/lib/gitlab/seeders/ci/daily_build_group_report_result_spec.rb'
+- './spec/lib/gitlab/seeder_spec.rb'
+- './spec/lib/gitlab/serializer/ci/variables_spec.rb'
+- './spec/lib/gitlab/serializer/pagination_spec.rb'
+- './spec/lib/gitlab/serverless/service_spec.rb'
+- './spec/lib/gitlab/service_desk_email_spec.rb'
+- './spec/lib/gitlab/service_desk_spec.rb'
+- './spec/lib/gitlab/session_spec.rb'
+- './spec/lib/gitlab/setup_helper/praefect_spec.rb'
+- './spec/lib/gitlab/setup_helper/workhorse_spec.rb'
+- './spec/lib/gitlab/shard_health_cache_spec.rb'
+- './spec/lib/gitlab/shell_spec.rb'
+- './spec/lib/gitlab/sidekiq_config/cli_methods_spec.rb'
+- './spec/lib/gitlab/sidekiq_config_spec.rb'
+- './spec/lib/gitlab/sidekiq_config/worker_matcher_spec.rb'
+- './spec/lib/gitlab/sidekiq_config/worker_router_spec.rb'
+- './spec/lib/gitlab/sidekiq_config/worker_spec.rb'
+- './spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb'
+- './spec/lib/gitlab/sidekiq_daemon/monitor_spec.rb'
+- './spec/lib/gitlab/sidekiq_death_handler_spec.rb'
+- './spec/lib/gitlab/sidekiq_logging/deduplication_logger_spec.rb'
+- './spec/lib/gitlab/sidekiq_logging/json_formatter_spec.rb'
+- './spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb'
+- './spec/lib/gitlab/sidekiq_middleware/admin_mode/client_spec.rb'
+- './spec/lib/gitlab/sidekiq_middleware/admin_mode/server_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/duplicate_job_spec.rb'
+- './spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/server_spec.rb'
+- './spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/none_spec.rb'
+- './spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies_spec.rb'
+- './spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executed_spec.rb'
+- './spec/lib/gitlab/sidekiq_middleware/duplicate_jobs/strategies/until_executing_spec.rb'
+- './spec/lib/gitlab/sidekiq_middleware/extra_done_log_metadata_spec.rb'
+- './spec/lib/gitlab/sidekiq_middleware/instrumentation_logger_spec.rb'
+- './spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb'
+- './spec/lib/gitlab/sidekiq_middleware/monitor_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/size_limiter/client_spec.rb'
+- './spec/lib/gitlab/sidekiq_middleware/size_limiter/compressor_spec.rb'
+- './spec/lib/gitlab/sidekiq_middleware/size_limiter/exceed_limit_error_spec.rb'
+- './spec/lib/gitlab/sidekiq_middleware/size_limiter/server_spec.rb'
+- './spec/lib/gitlab/sidekiq_middleware/size_limiter/validator_spec.rb'
+- './spec/lib/gitlab/sidekiq_middleware_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/sidekiq_migrate_jobs_spec.rb'
+- './spec/lib/gitlab/sidekiq_queue_spec.rb'
+- './spec/lib/gitlab/sidekiq_signals_spec.rb'
+- './spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb'
+- './spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb'
+- './spec/lib/gitlab/sidekiq_status_spec.rb'
+- './spec/lib/gitlab/sidekiq_versioning/middleware_spec.rb'
+- './spec/lib/gitlab/sidekiq_versioning_spec.rb'
+- './spec/lib/gitlab/sidekiq_versioning/worker_spec.rb'
+- './spec/lib/gitlab/slash_commands/application_help_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_move_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/access_spec.rb'
+- './spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb'
+- './spec/lib/gitlab/slash_commands/presenters/error_spec.rb'
+- './spec/lib/gitlab/slash_commands/presenters/issue_close_spec.rb'
+- './spec/lib/gitlab/slash_commands/presenters/issue_comment_spec.rb'
+- './spec/lib/gitlab/slash_commands/presenters/issue_move_spec.rb'
+- './spec/lib/gitlab/slash_commands/presenters/issue_new_spec.rb'
+- './spec/lib/gitlab/slash_commands/presenters/issue_search_spec.rb'
+- './spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb'
+- './spec/lib/gitlab/slash_commands/presenters/run_spec.rb'
+- './spec/lib/gitlab/slash_commands/run_spec.rb'
+- './spec/lib/gitlab/slug/environment_spec.rb'
+- './spec/lib/gitlab/snippet_search_results_spec.rb'
+- './spec/lib/gitlab/sourcegraph_spec.rb'
+- './spec/lib/gitlab/spamcheck/client_spec.rb'
+- './spec/lib/gitlab_spec.rb'
+- './spec/lib/gitlab/sql/cte_spec.rb'
+- './spec/lib/gitlab/sql/except_spec.rb'
+- './spec/lib/gitlab/sql/glob_spec.rb'
+- './spec/lib/gitlab/sql/intersect_spec.rb'
+- './spec/lib/gitlab/sql/pattern_spec.rb'
+- './spec/lib/gitlab/sql/recursive_cte_spec.rb'
+- './spec/lib/gitlab/sql/union_spec.rb'
+- './spec/lib/gitlab/ssh/commit_spec.rb'
+- './spec/lib/gitlab/ssh_public_key_spec.rb'
+- './spec/lib/gitlab/ssh/signature_spec.rb'
+- './spec/lib/gitlab/string_placeholder_replacer_spec.rb'
+- './spec/lib/gitlab/string_range_marker_spec.rb'
+- './spec/lib/gitlab/string_regex_marker_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/suggestions/file_suggestion_spec.rb'
+- './spec/lib/gitlab/suggestions/suggestion_set_spec.rb'
+- './spec/lib/gitlab/tab_width_spec.rb'
+- './spec/lib/gitlab/tcp_checker_spec.rb'
+- './spec/lib/gitlab/template/finders/global_template_finder_spec.rb'
+- './spec/lib/gitlab/template/finders/repo_template_finders_spec.rb'
+- './spec/lib/gitlab/template/gitignore_template_spec.rb'
+- './spec/lib/gitlab/template/gitlab_ci_yml_template_spec.rb'
+- './spec/lib/gitlab/template/issue_template_spec.rb'
+- './spec/lib/gitlab/template/merge_request_template_spec.rb'
+- './spec/lib/gitlab/template/metrics_dashboard_template_spec.rb'
+- './spec/lib/gitlab/template_parser/ast_spec.rb'
+- './spec/lib/gitlab/template_parser/parser_spec.rb'
+- './spec/lib/gitlab/terraform_registry_token_spec.rb'
+- './spec/lib/gitlab/terraform/state_migration_helper_spec.rb'
+- './spec/lib/gitlab/themes_spec.rb'
+- './spec/lib/gitlab/throttle_spec.rb'
+- './spec/lib/gitlab/time_tracking_formatter_spec.rb'
+- './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'
+- './spec/lib/gitlab/tree_summary_spec.rb'
+- './spec/lib/gitlab/unicode_spec.rb'
+- './spec/lib/gitlab/untrusted_regexp/ruby_syntax_spec.rb'
+- './spec/lib/gitlab/untrusted_regexp_spec.rb'
+- './spec/lib/gitlab/uploads_transfer_spec.rb'
+- './spec/lib/gitlab/url_blockers/domain_allowlist_entry_spec.rb'
+- './spec/lib/gitlab/url_blockers/ip_allowlist_entry_spec.rb'
+- './spec/lib/gitlab/url_blocker_spec.rb'
+- './spec/lib/gitlab/url_blockers/url_allowlist_spec.rb'
+- './spec/lib/gitlab/url_builder_spec.rb'
+- './spec/lib/gitlab/url_sanitizer_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/base_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/ci_template_unique_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/code_review_events_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/cycle_analytics_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/designs_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/editor_unique_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/gitlab_cli_activity_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/issue_activity_unique_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/jetbrains_plugin_activity_unique_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/kubernetes_agent_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/merge_request_activity_unique_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/merge_request_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/note_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/package_event_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/productivity_analytics_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/redis_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/search_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/service_usage_data_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/snippet_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/source_code_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/track_unique_events_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/vscode_extension_activity_unique_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/web_ide_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/wiki_page_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_counters/work_item_activity_unique_counter_spec.rb'
+- './spec/lib/gitlab/usage_data_metrics_spec.rb'
+- './spec/lib/gitlab/usage_data_non_sql_metrics_spec.rb'
+- './spec/lib/gitlab/usage_data_queries_spec.rb'
+- './spec/lib/gitlab/usage_data_spec.rb'
+- './spec/lib/gitlab/usage_data/topology_spec.rb'
+- './spec/lib/gitlab/usage/metric_definition_spec.rb'
+- './spec/lib/gitlab/usage/metrics/aggregates/aggregate_spec.rb'
+- './spec/lib/gitlab/usage/metrics/aggregates/sources/calculations/intersection_spec.rb'
+- './spec/lib/gitlab/usage/metrics/aggregates/sources/postgres_hll_spec.rb'
+- './spec/lib/gitlab/usage/metrics/aggregates/sources/redis_hll_spec.rb'
+- './spec/lib/gitlab/usage/metrics/instrumentations/active_user_count_metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/instrumentations/cert_based_clusters_ff_metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/instrumentations/collected_data_categories_metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/instrumentations/count_boards_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/count_imported_projects_total_metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/instrumentations/count_issues_metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/instrumentations/count_users_associating_milestones_to_releases_metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/instrumentations/count_users_creating_issues_metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/instrumentations/database_metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/instrumentations/generic_metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/instrumentations/hostname_metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/instrumentations/jira_imports_total_imported_issues_count_metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/instrumentations/numbers_metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/instrumentations/redis_hll_metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/instrumentations/redis_metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/instrumentations/service_ping_features_metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/instrumentations/snowplow_configured_to_gitlab_collector_metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/instrumentations/snowplow_enabled_metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/instrumentations/uuid_metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/key_path_processor_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/name_suggestion_spec.rb'
+- './spec/lib/gitlab/usage/metric_spec.rb'
+- './spec/lib/gitlab/usage/metrics/query_spec.rb'
+- './spec/lib/gitlab/usage/service_ping/instrumented_payload_spec.rb'
+- './spec/lib/gitlab/usage/service_ping/legacy_metric_timing_decorator_spec.rb'
+- './spec/lib/gitlab/usage/service_ping/payload_keys_processor_spec.rb'
+- './spec/lib/gitlab/usage/service_ping_report_spec.rb'
+- './spec/lib/gitlab/user_access_snippet_spec.rb'
+- './spec/lib/gitlab/user_access_spec.rb'
+- './spec/lib/gitlab/uuid_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/version_info_spec.rb'
+- './spec/lib/gitlab/view/presenter/base_spec.rb'
+- './spec/lib/gitlab/view/presenter/delegated_spec.rb'
+- './spec/lib/gitlab/view/presenter/factory_spec.rb'
+- './spec/lib/gitlab/view/presenter/simple_spec.rb'
+- './spec/lib/gitlab/visibility_level_checker_spec.rb'
+- './spec/lib/gitlab/visibility_level_spec.rb'
+- './spec/lib/gitlab/web_hooks/rate_limiter_spec.rb'
+- './spec/lib/gitlab/web_hooks/recursion_detection_spec.rb'
+- './spec/lib/gitlab/web_ide/config/entry/global_spec.rb'
+- './spec/lib/gitlab/web_ide/config/entry/terminal_spec.rb'
+- './spec/lib/gitlab/web_ide/config_spec.rb'
+- './spec/lib/gitlab/webpack/file_loader_spec.rb'
+- './spec/lib/gitlab/webpack/graphql_known_operations_spec.rb'
+- './spec/lib/gitlab/webpack/manifest_spec.rb'
+- './spec/lib/gitlab/wiki_file_finder_spec.rb'
+- './spec/lib/gitlab/wiki_pages/front_matter_parser_spec.rb'
+- './spec/lib/gitlab/with_request_store_spec.rb'
+- './spec/lib/gitlab/word_diff/chunk_collection_spec.rb'
+- './spec/lib/gitlab/word_diff/line_processor_spec.rb'
+- './spec/lib/gitlab/word_diff/parser_spec.rb'
+- './spec/lib/gitlab/word_diff/positions_counter_spec.rb'
+- './spec/lib/gitlab/word_diff/segments/chunk_spec.rb'
+- './spec/lib/gitlab/word_diff/segments/diff_hunk_spec.rb'
+- './spec/lib/gitlab/word_diff/segments/newline_spec.rb'
+- './spec/lib/gitlab/workhorse_spec.rb'
+- './spec/lib/gitlab/x509/certificate_spec.rb'
+- './spec/lib/gitlab/x509/commit_spec.rb'
+- './spec/lib/gitlab/x509/signature_spec.rb'
+- './spec/lib/gitlab/x509/tag_spec.rb'
+- './spec/lib/gitlab/zentao/client_spec.rb'
+- './spec/lib/gitlab/zentao/query_spec.rb'
+- './spec/lib/gitlab/zoom_link_extractor_spec.rb'
+- './spec/lib/google_api/auth_spec.rb'
+- './spec/lib/google_api/cloud_platform/client_spec.rb'
+- './spec/lib/grafana/client_spec.rb'
+- './spec/lib/grafana/time_window_spec.rb'
+- './spec/lib/grafana/validator_spec.rb'
+- './spec/lib/initializer_connections_spec.rb'
+- './spec/lib/json_web_token/hmac_token_spec.rb'
+- './spec/lib/json_web_token/rsa_token_spec.rb'
+- './spec/lib/json_web_token/token_spec.rb'
+- './spec/lib/kramdown/kramdown_spec.rb'
+- './spec/lib/kramdown/parser/atlassian_document_format_spec.rb'
+- './spec/lib/marginalia_spec.rb'
+- './spec/lib/mattermost/client_spec.rb'
+- './spec/lib/mattermost/command_spec.rb'
+- './spec/lib/mattermost/session_spec.rb'
+- './spec/lib/mattermost/team_spec.rb'
+- './spec/lib/microsoft_teams/activity_spec.rb'
+- './spec/lib/microsoft_teams/notifier_spec.rb'
+- './spec/lib/object_storage/config_spec.rb'
+- './spec/lib/object_storage/direct_upload_spec.rb'
+- './spec/lib/omni_auth/strategies/jwt_spec.rb'
+- './spec/lib/pager_duty/webhook_payload_parser_spec.rb'
+- './spec/lib/peek/views/active_record_spec.rb'
+- './spec/lib/peek/views/bullet_detailed_spec.rb'
+- './spec/lib/peek/views/detailed_view_spec.rb'
+- './spec/lib/peek/views/external_http_spec.rb'
+- './spec/lib/peek/views/memory_spec.rb'
+- './spec/lib/peek/views/redis_detailed_spec.rb'
+- './spec/lib/peek/views/rugged_spec.rb'
+- './spec/lib/product_analytics/event_params_spec.rb'
+- './spec/lib/product_analytics/tracker_spec.rb'
+- './spec/lib/prometheus/cleanup_multiproc_dir_service_spec.rb'
+- './spec/lib/prometheus/pid_provider_spec.rb'
+- './spec/lib/quality/seeders/issues_spec.rb'
+- './spec/lib/release_highlights/validator/entry_spec.rb'
+- './spec/lib/release_highlights/validator_spec.rb'
+- './spec/lib/rouge/formatters/html_gitlab_spec.rb'
+- './spec/lib/safe_zip/entry_spec.rb'
+- './spec/lib/safe_zip/extract_params_spec.rb'
+- './spec/lib/safe_zip/extract_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'
+- './spec/lib/security/ci_configuration/secret_detection_build_action_spec.rb'
+- './spec/lib/security/report_schema_version_matcher_spec.rb'
+- './spec/lib/serializers/json_spec.rb'
+- './spec/lib/serializers/symbolized_json_spec.rb'
+- './spec/lib/serializers/unsafe_json_spec.rb'
+- './spec/lib/service_ping/build_payload_spec.rb'
+- './spec/lib/service_ping/devops_report_spec.rb'
+- './spec/lib/service_ping/permit_data_categories_spec.rb'
+- './spec/lib/service_ping/service_ping_settings_spec.rb'
+- './spec/lib/sidebars/concerns/container_with_html_options_spec.rb'
+- './spec/lib/sidebars/concerns/link_with_html_options_spec.rb'
+- './spec/lib/sidebars/groups/menus/ci_cd_menu_spec.rb'
+- './spec/lib/sidebars/groups/menus/group_information_menu_spec.rb'
+- './spec/lib/sidebars/groups/menus/invite_team_members_menu_spec.rb'
+- './spec/lib/sidebars/groups/menus/issues_menu_spec.rb'
+- './spec/lib/sidebars/groups/menus/kubernetes_menu_spec.rb'
+- './spec/lib/sidebars/groups/menus/merge_requests_menu_spec.rb'
+- './spec/lib/sidebars/groups/menus/packages_registries_menu_spec.rb'
+- './spec/lib/sidebars/groups/menus/scope_menu_spec.rb'
+- './spec/lib/sidebars/groups/menus/settings_menu_spec.rb'
+- './spec/lib/sidebars/menu_item_spec.rb'
+- './spec/lib/sidebars/menu_spec.rb'
+- './spec/lib/sidebars/panel_spec.rb'
+- './spec/lib/sidebars/projects/context_spec.rb'
+- './spec/lib/sidebars/projects/menus/analytics_menu_spec.rb'
+- './spec/lib/sidebars/projects/menus/ci_cd_menu_spec.rb'
+- './spec/lib/sidebars/projects/menus/confluence_menu_spec.rb'
+- './spec/lib/sidebars/projects/menus/deployments_menu_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/hidden_menu_spec.rb'
+- './spec/lib/sidebars/projects/menus/infrastructure_menu_spec.rb'
+- './spec/lib/sidebars/projects/menus/invite_team_members_menu_spec.rb'
+- './spec/lib/sidebars/projects/menus/issues_menu_spec.rb'
+- './spec/lib/sidebars/projects/menus/merge_requests_menu_spec.rb'
+- './spec/lib/sidebars/projects/menus/monitor_menu_spec.rb'
+- './spec/lib/sidebars/projects/menus/packages_registries_menu_spec.rb'
+- './spec/lib/sidebars/projects/menus/project_information_menu_spec.rb'
+- './spec/lib/sidebars/projects/menus/repository_menu_spec.rb'
+- './spec/lib/sidebars/projects/menus/scope_menu_spec.rb'
+- './spec/lib/sidebars/projects/menus/security_compliance_menu_spec.rb'
+- './spec/lib/sidebars/projects/menus/settings_menu_spec.rb'
+- './spec/lib/sidebars/projects/menus/shimo_menu_spec.rb'
+- './spec/lib/sidebars/projects/menus/snippets_menu_spec.rb'
+- './spec/lib/sidebars/projects/menus/wiki_menu_spec.rb'
+- './spec/lib/sidebars/projects/menus/zentao_menu_spec.rb'
+- './spec/lib/sidebars/projects/panel_spec.rb'
+- './spec/lib/system_check/app/authorized_keys_permission_check_spec.rb'
+- './spec/lib/system_check/app/git_user_default_ssh_config_check_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/lib/system_check/base_check_spec.rb'
+- './spec/lib/system_check/incoming_email_check_spec.rb'
+- './spec/lib/system_check/incoming_email/imap_authentication_check_spec.rb'
+- './spec/lib/system_check/orphans/namespace_check_spec.rb'
+- './spec/lib/system_check/orphans/repository_check_spec.rb'
+- './spec/lib/system_check/sidekiq_check_spec.rb'
+- './spec/lib/system_check/simple_executor_spec.rb'
+- './spec/lib/system_check_spec.rb'
+- './spec/lib/tasks/gitlab/metrics_exporter_task_spec.rb'
+- './spec/lib/unnested_in_filters/dsl_spec.rb'
+- './spec/lib/unnested_in_filters/rewriter_spec.rb'
+- './spec/lib/uploaded_file_spec.rb'
+- './spec/lib/version_check_spec.rb'
+- './spec/mailers/abuse_report_mailer_spec.rb'
+- './spec/mailers/devise_mailer_spec.rb'
+- './spec/mailers/email_rejection_mailer_spec.rb'
+- './spec/mailers/emails/admin_notification_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/repository_check_mailer_spec.rb'
+- './spec/metrics_server/metrics_server_spec.rb'
+- './spec/migrations/20210406144743_backfill_total_tuple_count_for_batched_migrations_spec.rb'
+- './spec/migrations/20210423160427_schedule_drop_invalid_vulnerabilities_spec.rb'
+- './spec/migrations/20210430134202_copy_adoption_snapshot_namespace_spec.rb'
+- './spec/migrations/20210430135954_copy_adoption_segments_namespace_spec.rb'
+- './spec/migrations/20210503105845_add_project_value_stream_id_to_project_stages_spec.rb'
+- './spec/migrations/20210511142748_schedule_drop_invalid_vulnerabilities2_spec.rb'
+- './spec/migrations/20210514063252_schedule_cleanup_orphaned_lfs_objects_projects_spec.rb'
+- './spec/migrations/20210601073400_fix_total_stage_in_vsa_spec.rb'
+- './spec/migrations/20210601080039_group_protected_environments_add_index_and_constraint_spec.rb'
+- './spec/migrations/20210603222333_remove_builds_email_service_from_services_spec.rb'
+- './spec/migrations/20210610153556_delete_legacy_operations_feature_flags_spec.rb'
+- './spec/migrations/2021061716138_cascade_delete_freeze_periods_spec.rb'
+- './spec/migrations/20210708130419_reschedule_merge_request_diff_users_background_migration_spec.rb'
+- './spec/migrations/20210713042000_fix_ci_sources_pipelines_index_names_spec.rb'
+- './spec/migrations/20210722042939_update_issuable_slas_where_issue_closed_spec.rb'
+- './spec/migrations/20210722150102_operations_feature_flags_correct_flexible_rollout_values_spec.rb'
+- './spec/migrations/20210804150320_create_base_work_item_types_spec.rb'
+- './spec/migrations/20210805192450_update_trial_plans_ci_daily_pipeline_schedule_triggers_spec.rb'
+- './spec/migrations/20210811122206_update_external_project_bots_spec.rb'
+- './spec/migrations/20210812013042_remove_duplicate_project_authorizations_spec.rb'
+- './spec/migrations/20210818185845_backfill_projects_with_coverage_spec.rb'
+- './spec/migrations/20210819145000_drop_temporary_columns_and_triggers_for_ci_builds_runner_session_spec.rb'
+- './spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb'
+- './spec/migrations/20210902144144_drop_temporary_columns_and_triggers_for_ci_build_needs_spec.rb'
+- './spec/migrations/20210906100316_drop_temporary_columns_and_triggers_for_ci_build_trace_chunks_spec.rb'
+- './spec/migrations/20210906130643_drop_temporary_columns_and_triggers_for_taggings_spec.rb'
+- './spec/migrations/20210907013944_cleanup_bigint_conversion_for_ci_builds_metadata_spec.rb'
+- './spec/migrations/20210907211557_finalize_ci_builds_bigint_conversion_spec.rb'
+- './spec/migrations/20210910194952_update_report_type_for_existing_approval_project_rules_spec.rb'
+- './spec/migrations/20210914095310_cleanup_orphan_project_access_tokens_spec.rb'
+- './spec/migrations/20210915022415_cleanup_bigint_conversion_for_ci_builds_spec.rb'
+- './spec/migrations/20210918201050_remove_old_pending_jobs_for_recalculate_vulnerabilities_occurrences_uuid_spec.rb'
+- './spec/migrations/20210922021816_drop_int4_columns_for_ci_job_artifacts_spec.rb'
+- './spec/migrations/20210922025631_drop_int4_column_for_ci_sources_pipelines_spec.rb'
+- './spec/migrations/20210922082019_drop_int4_column_for_events_spec.rb'
+- './spec/migrations/20210922091402_drop_int4_column_for_push_event_payloads_spec.rb'
+- './spec/migrations/20211006060436_schedule_populate_topics_total_projects_count_cache_spec.rb'
+- './spec/migrations/20211012134316_clean_up_migrate_merge_request_diff_commit_users_spec.rb'
+- './spec/migrations/20211018152654_schedule_remove_duplicate_vulnerabilities_findings3_spec.rb'
+- './spec/migrations/20211028155449_schedule_fix_merge_request_diff_commit_users_migration_spec.rb'
+- './spec/migrations/20211101222614_consume_remaining_user_namespace_jobs_spec.rb'
+- './spec/migrations/20211110143306_add_not_null_constraint_to_security_findings_uuid_spec.rb'
+- './spec/migrations/20211110151350_schedule_drop_invalid_security_findings_spec.rb'
+- './spec/migrations/20211116091751_change_namespace_type_default_to_user_spec.rb'
+- './spec/migrations/20211116111644_schedule_remove_occurrence_pipelines_and_duplicate_vulnerabilities_findings_spec.rb'
+- './spec/migrations/20211117084814_migrate_remaining_u2f_registrations_spec.rb'
+- './spec/migrations/20211126115449_encrypt_static_objects_external_storage_auth_token_spec.rb'
+- './spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb'
+- './spec/migrations/20211130165043_backfill_sequence_column_for_sprints_table_spec.rb'
+- './spec/migrations/20211203091642_add_index_to_projects_on_marked_for_deletion_at_spec.rb'
+- './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'
+- './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/20220120094340_drop_position_from_security_findings_spec.rb'
+- './spec/migrations/20220124130028_dedup_runner_projects_spec.rb'
+- './spec/migrations/20220128155251_remove_dangling_running_builds_spec.rb'
+- './spec/migrations/20220128155814_fix_approval_rules_code_owners_rule_type_index_spec.rb'
+- './spec/migrations/20220202105733_delete_service_template_records_spec.rb'
+- './spec/migrations/20220204095121_backfill_namespace_statistics_with_dependency_proxy_size_spec.rb'
+- './spec/migrations/20220204194347_encrypt_integration_properties_spec.rb'
+- './spec/migrations/20220208080921_schedule_migrate_personal_namespace_project_maintainer_to_owner_spec.rb'
+- './spec/migrations/20220211214605_update_integrations_trigger_type_new_on_insert_null_safe_spec.rb'
+- './spec/migrations/20220213103859_remove_integrations_type_spec.rb'
+- './spec/migrations/20220222192524_create_not_null_constraint_releases_tag_spec.rb'
+- './spec/migrations/20220222192525_remove_null_releases_spec.rb'
+- './spec/migrations/20220223124428_schedule_merge_topics_with_same_name_spec.rb'
+- './spec/migrations/20220305223212_add_security_training_providers_spec.rb'
+- './spec/migrations/20220307192610_remove_duplicate_project_tag_releases_spec.rb'
+- './spec/migrations/20220309084954_remove_leftover_external_pull_request_deletions_spec.rb'
+- './spec/migrations/20220310141349_remove_dependency_list_usage_data_from_redis_spec.rb'
+- './spec/migrations/20220315171129_cleanup_draft_data_from_faulty_regex_spec.rb'
+- './spec/migrations/20220316202640_populate_container_repositories_migration_plan_spec.rb'
+- './spec/migrations/20220321234317_remove_all_issuable_escalation_statuses_spec.rb'
+- './spec/migrations/20220322132242_update_pages_onboarding_state_spec.rb'
+- './spec/migrations/20220324032250_migrate_shimo_confluence_service_category_spec.rb'
+- './spec/migrations/20220324165436_schedule_backfill_project_settings_spec.rb'
+- './spec/migrations/20220329175119_remove_leftover_ci_job_artifact_deletions_spec.rb'
+- './spec/migrations/20220331133802_schedule_backfill_topics_title_spec.rb'
+- './spec/migrations/20220412143552_consume_remaining_encrypt_integration_property_jobs_spec.rb'
+- './spec/migrations/20220416054011_schedule_backfill_project_member_namespace_id_spec.rb'
+- './spec/migrations/20220420135946_update_batched_background_migration_arguments_spec.rb'
+- './spec/migrations/20220426185933_backfill_deployments_finished_at_spec.rb'
+- './spec/migrations/20220502015011_clean_up_fix_merge_request_diff_commit_users_spec.rb'
+- './spec/migrations/20220502173045_reset_too_many_tags_skipped_registry_imports_spec.rb'
+- './spec/migrations/20220503035221_add_gitlab_schema_to_batched_background_migrations_spec.rb'
+- './spec/migrations/20220505044348_fix_automatic_iterations_cadences_start_date_spec.rb'
+- './spec/migrations/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints_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/20220513043344_reschedule_expire_o_auth_tokens_spec.rb'
+- './spec/migrations/20220523171107_drop_deploy_tokens_token_column_spec.rb'
+- './spec/migrations/20220524074947_finalize_backfill_null_note_discussion_ids_spec.rb'
+- './spec/migrations/20220524184149_create_sync_project_namespace_details_trigger_spec.rb'
+- './spec/migrations/20220525221133_schedule_backfill_vulnerability_reads_cluster_agent_spec.rb'
+- './spec/migrations/20220601110011_schedule_remove_self_managed_wiki_notes_spec.rb'
+- './spec/migrations/20220601152916_add_user_id_and_ip_address_success_index_to_authentication_events_spec.rb'
+- './spec/migrations/20220606082910_add_tmp_index_for_potentially_misassociated_vulnerability_occurrences_spec.rb'
+- './spec/migrations/20220607082910_add_sync_tmp_index_for_potentially_misassociated_vulnerability_occurrences_spec.rb'
+- './spec/migrations/20220620132300_update_last_run_date_for_iterations_cadences_spec.rb'
+- './spec/migrations/20220622080547_backfill_project_statistics_with_container_registry_size_spec.rb'
+- './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'
+- './spec/migrations/20220722110026_reschedule_set_legacy_open_source_license_available_for_non_public_projects_spec.rb'
+- './spec/migrations/20220725150127_update_jira_tracker_data_deployment_type_based_on_url_spec.rb'
+- './spec/migrations/20220801155858_schedule_disable_legacy_open_source_licence_for_recent_public_projects_spec.rb'
+- './spec/migrations/20220802114351_reschedule_backfill_container_registry_size_into_project_statistics_spec.rb'
+- './spec/migrations/20220802204737_remove_deactivated_user_highest_role_stats_spec.rb'
+- './spec/migrations/20220809002011_schedule_destroy_invalid_group_members_spec.rb'
+- './spec/migrations/active_record/schema_spec.rb'
+- './spec/migrations/add_default_project_approval_rules_vuln_allowed_spec.rb'
+- './spec/migrations/add_epics_relative_position_spec.rb'
+- './spec/migrations/add_new_trail_plans_spec.rb'
+- './spec/migrations/add_open_source_plan_spec.rb'
+- './spec/migrations/add_premium_and_ultimate_plan_limits_spec.rb'
+- './spec/migrations/add_triggers_to_integrations_type_new_spec.rb'
+- './spec/migrations/add_upvotes_count_index_to_issues_spec.rb'
+- './spec/migrations/add_web_hook_calls_to_plan_limits_paid_tiers_spec.rb'
+- './spec/migrations/associate_existing_dast_builds_with_variables_spec.rb'
+- './spec/migrations/backfill_all_project_namespaces_spec.rb'
+- './spec/migrations/backfill_cadence_id_for_boards_scoped_to_iteration_spec.rb'
+- './spec/migrations/backfill_clusters_integration_prometheus_enabled_spec.rb'
+- './spec/migrations/backfill_cycle_analytics_aggregations_spec.rb'
+- './spec/migrations/backfill_escalation_policies_for_oncall_schedules_spec.rb'
+- './spec/migrations/backfill_group_features_spec.rb'
+- './spec/migrations/backfill_integrations_enable_ssl_verification_spec.rb'
+- './spec/migrations/backfill_integrations_type_new_spec.rb'
+- './spec/migrations/backfill_issues_upvotes_count_spec.rb'
+- './spec/migrations/backfill_member_namespace_id_for_group_members_spec.rb'
+- './spec/migrations/backfill_namespace_id_for_namespace_routes_spec.rb'
+- './spec/migrations/backfill_namespace_id_for_project_routes_spec.rb'
+- './spec/migrations/backfill_nuget_temporary_packages_to_processing_status_spec.rb'
+- './spec/migrations/backfill_project_import_level_spec.rb'
+- './spec/migrations/backfill_project_namespaces_for_group_spec.rb'
+- './spec/migrations/backfill_stage_event_hash_spec.rb'
+- './spec/migrations/backfill_user_namespace_spec.rb'
+- './spec/migrations/bulk_insert_cluster_enabled_grants_spec.rb'
+- './spec/migrations/change_public_projects_cost_factor_spec.rb'
+- './spec/migrations/change_web_hook_events_default_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/cleanup_backfill_integrations_enable_ssl_verification_spec.rb'
+- './spec/migrations/cleanup_move_container_registry_enabled_to_project_feature_spec.rb'
+- './spec/migrations/cleanup_mr_attention_request_todos_spec.rb'
+- './spec/migrations/cleanup_orphaned_routes_spec.rb'
+- './spec/migrations/clean_up_pending_builds_table_spec.rb'
+- './spec/migrations/cleanup_remaining_orphan_invites_spec.rb'
+- './spec/migrations/confirm_security_bot_spec.rb'
+- './spec/migrations/confirm_support_bot_user_spec.rb'
+- './spec/migrations/delete_security_findings_without_uuid_spec.rb'
+- './spec/migrations/disable_expiration_policies_linked_to_no_container_images_spec.rb'
+- './spec/migrations/disable_job_token_scope_when_unused_spec.rb'
+- './spec/migrations/finalize_orphaned_routes_cleanup_spec.rb'
+- './spec/migrations/finalize_project_namespaces_backfill_spec.rb'
+- './spec/migrations/finalize_routes_backfilling_for_projects_spec.rb'
+- './spec/migrations/finalize_traversal_ids_background_migrations_spec.rb'
+- './spec/migrations/fix_and_backfill_project_namespaces_for_projects_with_duplicate_name_spec.rb'
+- './spec/migrations/fix_batched_migrations_old_format_job_arguments_spec.rb'
+- './spec/migrations/generate_customers_dot_jwt_signing_key_spec.rb'
+- './spec/migrations/insert_ci_daily_pipeline_schedule_triggers_plan_limits_spec.rb'
+- './spec/migrations/migrate_elastic_index_settings_spec.rb'
+- './spec/migrations/migrate_protected_attribute_to_pending_builds_spec.rb'
+- './spec/migrations/move_container_registry_enabled_to_project_features3_spec.rb'
+- './spec/migrations/orphaned_invite_tokens_cleanup_spec.rb'
+- './spec/migrations/populate_audit_event_streaming_verification_token_spec.rb'
+- './spec/migrations/populate_dismissal_information_for_vulnerabilities_spec.rb'
+- './spec/migrations/populate_operation_visibility_permissions_spec.rb'
+- './spec/migrations/queue_backfill_project_feature_package_registry_access_level_spec.rb'
+- './spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_features_spec.rb'
+- './spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_with_new_features_spec.rb'
+- './spec/migrations/remove_duplicate_dast_site_tokens_spec.rb'
+- './spec/migrations/remove_duplicate_dast_site_tokens_with_same_token_spec.rb'
+- './spec/migrations/remove_hipchat_service_records_spec.rb'
+- './spec/migrations/remove_invalid_integrations_spec.rb'
+- './spec/migrations/remove_not_null_contraint_on_title_from_sprints_spec.rb'
+- './spec/migrations/remove_records_without_group_from_webhooks_table_spec.rb'
+- './spec/migrations/remove_schedule_and_status_from_pending_alert_escalations_spec.rb'
+- './spec/migrations/remove_wiki_notes_spec.rb'
+- './spec/migrations/rename_services_to_integrations_spec.rb'
+- './spec/migrations/replace_external_wiki_triggers_spec.rb'
+- './spec/migrations/reschedule_backfill_imported_issue_search_data_spec.rb'
+- './spec/migrations/reschedule_delete_orphaned_deployments_spec.rb'
+- './spec/migrations/re_schedule_latest_pipeline_id_population_with_all_security_related_artifact_types_spec.rb'
+- './spec/migrations/reset_job_token_scope_enabled_again_spec.rb'
+- './spec/migrations/reset_job_token_scope_enabled_spec.rb'
+- './spec/migrations/reset_severity_levels_to_new_default_spec.rb'
+- './spec/migrations/retry_backfill_traversal_ids_spec.rb'
+- './spec/migrations/schedule_add_primary_email_to_emails_if_user_confirmed_spec.rb'
+- './spec/migrations/schedule_backfill_draft_status_on_merge_requests_corrected_regex_spec.rb'
+- './spec/migrations/schedule_backfilling_the_namespace_id_for_vulnerability_reads_spec.rb'
+- './spec/migrations/schedule_copy_ci_builds_columns_to_security_scans2_spec.rb'
+- './spec/migrations/schedule_disable_expiration_policies_linked_to_no_container_images_spec.rb'
+- './spec/migrations/schedule_fix_incorrect_max_seats_used2_spec.rb'
+- './spec/migrations/schedule_fix_incorrect_max_seats_used_spec.rb'
+- './spec/migrations/schedule_migrate_shared_vulnerability_scanners_spec.rb'
+- './spec/migrations/schedule_populate_requirements_issue_id_spec.rb'
+- './spec/migrations/schedule_purging_stale_security_scans_spec.rb'
+- './spec/migrations/schedule_recalculate_vulnerability_finding_signatures_for_findings_spec.rb'
+- './spec/migrations/schedule_security_setting_creation_spec.rb'
+- './spec/migrations/schedule_set_correct_vulnerability_state_spec.rb'
+- './spec/migrations/schedule_update_timelogs_null_spent_at_spec.rb'
+- './spec/migrations/schedule_update_timelogs_project_id_spec.rb'
+- './spec/migrations/schedule_update_users_where_two_factor_auth_required_from_group_spec.rb'
+- './spec/migrations/set_default_job_token_scope_true_spec.rb'
+- './spec/migrations/slice_merge_request_diff_commit_migrations_spec.rb'
+- './spec/migrations/start_backfill_ci_queuing_tables_spec.rb'
+- './spec/migrations/steal_merge_request_diff_commit_users_migration_spec.rb'
+- './spec/migrations/toggle_vsa_aggregations_enable_spec.rb'
+- './spec/migrations/update_application_settings_container_registry_exp_pol_worker_capacity_default_spec.rb'
+- './spec/migrations/update_application_settings_protected_paths_spec.rb'
+- './spec/migrations/update_default_scan_method_of_dast_site_profile_spec.rb'
+- './spec/migrations/update_integrations_trigger_type_new_on_insert_spec.rb'
+- './spec/migrations/update_invalid_member_states_spec.rb'
+- './spec/migrations/update_invalid_web_hooks_spec.rb'
+- './spec/models/ability_spec.rb'
+- './spec/models/abuse_report_spec.rb'
+- './spec/models/active_session_spec.rb'
+- './spec/models/acts_as_taggable_on/tagging_spec.rb'
+- './spec/models/acts_as_taggable_on/tag_spec.rb'
+- './spec/models/alerting/project_alerting_setting_spec.rb'
+- './spec/models/alert_management/alert_assignee_spec.rb'
+- './spec/models/alert_management/alert_spec.rb'
+- './spec/models/alert_management/alert_user_mention_spec.rb'
+- './spec/models/alert_management/http_integration_spec.rb'
+- './spec/models/alert_management/metric_image_spec.rb'
+- './spec/models/analytics/cycle_analytics/aggregation_spec.rb'
+- './spec/models/analytics/cycle_analytics/issue_stage_event_spec.rb'
+- './spec/models/analytics/cycle_analytics/merge_request_stage_event_spec.rb'
+- './spec/models/analytics/cycle_analytics/project_stage_spec.rb'
+- './spec/models/analytics/cycle_analytics/project_value_stream_spec.rb'
+- './spec/models/analytics/cycle_analytics/stage_event_hash_spec.rb'
+- './spec/models/analytics/usage_trends/measurement_spec.rb'
+- './spec/models/appearance_spec.rb'
+- './spec/models/application_record_spec.rb'
+- './spec/models/application_setting_spec.rb'
+- './spec/models/application_setting/term_spec.rb'
+- './spec/models/approval_spec.rb'
+- './spec/models/atlassian/identity_spec.rb'
+- './spec/models/audit_event_spec.rb'
+- './spec/models/authentication_event_spec.rb'
+- './spec/models/award_emoji_spec.rb'
+- './spec/models/awareness_session_spec.rb'
+- './spec/models/aws/role_spec.rb'
+- './spec/models/badges/group_badge_spec.rb'
+- './spec/models/badge_spec.rb'
+- './spec/models/badges/project_badge_spec.rb'
+- './spec/models/blob_spec.rb'
+- './spec/models/blob_viewer/base_spec.rb'
+- './spec/models/blob_viewer/changelog_spec.rb'
+- './spec/models/blob_viewer/composer_json_spec.rb'
+- './spec/models/blob_viewer/gemspec_spec.rb'
+- './spec/models/blob_viewer/gitlab_ci_yml_spec.rb'
+- './spec/models/blob_viewer/go_mod_spec.rb'
+- './spec/models/blob_viewer/license_spec.rb'
+- './spec/models/blob_viewer/markup_spec.rb'
+- './spec/models/blob_viewer/metrics_dashboard_yml_spec.rb'
+- './spec/models/blob_viewer/package_json_spec.rb'
+- './spec/models/blob_viewer/podspec_json_spec.rb'
+- './spec/models/blob_viewer/podspec_spec.rb'
+- './spec/models/blob_viewer/readme_spec.rb'
+- './spec/models/blob_viewer/route_map_spec.rb'
+- './spec/models/blob_viewer/server_side_spec.rb'
+- './spec/models/board_group_recent_visit_spec.rb'
+- './spec/models/board_project_recent_visit_spec.rb'
+- './spec/models/board_spec.rb'
+- './spec/models/broadcast_message_spec.rb'
+- './spec/models/bulk_imports/configuration_spec.rb'
+- './spec/models/bulk_imports/entity_spec.rb'
+- './spec/models/bulk_imports/export_spec.rb'
+- './spec/models/bulk_imports/export_status_spec.rb'
+- './spec/models/bulk_imports/export_upload_spec.rb'
+- './spec/models/bulk_imports/failure_spec.rb'
+- './spec/models/bulk_imports/file_transfer/group_config_spec.rb'
+- './spec/models/bulk_imports/file_transfer/project_config_spec.rb'
+- './spec/models/bulk_imports/file_transfer_spec.rb'
+- './spec/models/bulk_import_spec.rb'
+- './spec/models/bulk_imports/tracker_spec.rb'
+- './spec/models/chat_name_spec.rb'
+- './spec/models/chat_team_spec.rb'
+- './spec/models/ci/artifact_blob_spec.rb'
+- './spec/models/ci/bridge_spec.rb'
+- './spec/models/ci/build_dependencies_spec.rb'
+- './spec/models/ci/build_metadata_spec.rb'
+- './spec/models/ci/build_need_spec.rb'
+- './spec/models/ci/build_pending_state_spec.rb'
+- './spec/models/ci/build_report_result_spec.rb'
+- './spec/models/ci/build_runner_session_spec.rb'
+- './spec/models/ci/build_spec.rb'
+- './spec/models/ci/build_trace_chunks/database_spec.rb'
+- './spec/models/ci/build_trace_chunks/fog_spec.rb'
+- './spec/models/ci/build_trace_chunk_spec.rb'
+- './spec/models/ci/build_trace_chunks/redis_spec.rb'
+- './spec/models/ci/build_trace_metadata_spec.rb'
+- './spec/models/ci/build_trace_spec.rb'
+- './spec/models/ci/commit_with_pipeline_spec.rb'
+- './spec/models/ci/daily_build_group_report_result_spec.rb'
+- './spec/models/ci/deleted_object_spec.rb'
+- './spec/models/ci/freeze_period_spec.rb'
+- './spec/models/ci/freeze_period_status_spec.rb'
+- './spec/models/ci/group_spec.rb'
+- './spec/models/ci/group_variable_spec.rb'
+- './spec/models/ci/instance_variable_spec.rb'
+- './spec/models/ci/job_artifact_spec.rb'
+- './spec/models/ci/job_token/project_scope_link_spec.rb'
+- './spec/models/ci/job_token/scope_spec.rb'
+- './spec/models/ci/job_variable_spec.rb'
+- './spec/models/ci/namespace_mirror_spec.rb'
+- './spec/models/ci/pending_build_spec.rb'
+- './spec/models/ci/persistent_ref_spec.rb'
+- './spec/models/ci/pipeline_artifact_spec.rb'
+- './spec/models/ci/pipeline_config_spec.rb'
+- './spec/models/ci/pipeline_message_spec.rb'
+- './spec/models/ci/pipeline_schedule_spec.rb'
+- './spec/models/ci/pipeline_schedule_variable_spec.rb'
+- './spec/models/ci/pipeline_spec.rb'
+- './spec/models/ci/pipeline_variable_spec.rb'
+- './spec/models/ci_platform_metric_spec.rb'
+- './spec/models/ci/processable_spec.rb'
+- './spec/models/ci/project_mirror_spec.rb'
+- './spec/models/ci/ref_spec.rb'
+- './spec/models/ci/resource_group_spec.rb'
+- './spec/models/ci/resource_spec.rb'
+- './spec/models/ci/runner_namespace_spec.rb'
+- './spec/models/ci/runner_project_spec.rb'
+- './spec/models/ci/runner_spec.rb'
+- './spec/models/ci/runner_version_spec.rb'
+- './spec/models/ci/running_build_spec.rb'
+- './spec/models/ci/secure_file_spec.rb'
+- './spec/models/ci/sources/pipeline_spec.rb'
+- './spec/models/ci/stage_spec.rb'
+- './spec/models/ci/trigger_request_spec.rb'
+- './spec/models/ci/trigger_spec.rb'
+- './spec/models/ci/unit_test_failure_spec.rb'
+- './spec/models/ci/unit_test_spec.rb'
+- './spec/models/ci/variable_spec.rb'
+- './spec/models/clusters/agents/activity_event_spec.rb'
+- './spec/models/clusters/agents/group_authorization_spec.rb'
+- './spec/models/clusters/agents/implicit_authorization_spec.rb'
+- './spec/models/clusters/agent_spec.rb'
+- './spec/models/clusters/agents/project_authorization_spec.rb'
+- './spec/models/clusters/agent_token_spec.rb'
+- './spec/models/clusters/applications/cert_manager_spec.rb'
+- './spec/models/clusters/applications/cilium_spec.rb'
+- './spec/models/clusters/applications/crossplane_spec.rb'
+- './spec/models/clusters/applications/helm_spec.rb'
+- './spec/models/clusters/applications/ingress_spec.rb'
+- './spec/models/clusters/applications/jupyter_spec.rb'
+- './spec/models/clusters/applications/knative_spec.rb'
+- './spec/models/clusters/applications/prometheus_spec.rb'
+- './spec/models/clusters/applications/runner_spec.rb'
+- './spec/models/clusters/cluster_enabled_grant_spec.rb'
+- './spec/models/clusters/clusters_hierarchy_spec.rb'
+- './spec/models/clusters/cluster_spec.rb'
+- './spec/models/clusters/group_spec.rb'
+- './spec/models/clusters/integrations/prometheus_spec.rb'
+- './spec/models/clusters/kubernetes_namespace_spec.rb'
+- './spec/models/clusters/platforms/kubernetes_spec.rb'
+- './spec/models/clusters/project_spec.rb'
+- './spec/models/clusters/providers/aws_spec.rb'
+- './spec/models/clusters/providers/gcp_spec.rb'
+- './spec/models/commit_collection_spec.rb'
+- './spec/models/commit_range_spec.rb'
+- './spec/models/commit_signatures/gpg_signature_spec.rb'
+- './spec/models/commit_signatures/ssh_signature_spec.rb'
+- './spec/models/commit_signatures/x509_commit_signature_spec.rb'
+- './spec/models/commit_spec.rb'
+- './spec/models/commit_status_spec.rb'
+- './spec/models/compare_spec.rb'
+- './spec/models/concerns/access_requestable_spec.rb'
+- './spec/models/concerns/after_commit_queue_spec.rb'
+- './spec/models/concerns/approvable_base_spec.rb'
+- './spec/models/concerns/as_cte_spec.rb'
+- './spec/models/concerns/atomic_internal_id_spec.rb'
+- './spec/models/concerns/avatarable_spec.rb'
+- './spec/models/concerns/awardable_spec.rb'
+- './spec/models/concerns/awareness_spec.rb'
+- './spec/models/concerns/batch_destroy_dependent_associations_spec.rb'
+- './spec/models/concerns/batch_nullify_dependent_associations_spec.rb'
+- './spec/models/concerns/blob_language_from_git_attributes_spec.rb'
+- './spec/models/concerns/blocks_unsafe_serialization_spec.rb'
+- './spec/models/concerns/bulk_insertable_associations_spec.rb'
+- './spec/models/concerns/bulk_insert_safe_spec.rb'
+- './spec/models/concerns/cacheable_attributes_spec.rb'
+- './spec/models/concerns/cache_markdown_field_spec.rb'
+- './spec/models/concerns/cascading_namespace_setting_attribute_spec.rb'
+- './spec/models/concerns/case_sensitivity_spec.rb'
+- './spec/models/concerns/checksummable_spec.rb'
+- './spec/models/concerns/chronic_duration_attribute_spec.rb'
+- './spec/models/concerns/ci/artifactable_spec.rb'
+- './spec/models/concerns/ci/bulk_insertable_tags_spec.rb'
+- './spec/models/concerns/ci/has_deployment_name_spec.rb'
+- './spec/models/concerns/ci/has_ref_spec.rb'
+- './spec/models/concerns/ci/has_status_spec.rb'
+- './spec/models/concerns/ci/has_variable_spec.rb'
+- './spec/models/concerns/ci/maskable_spec.rb'
+- './spec/models/concerns/clusters/agents/authorization_config_scopes_spec.rb'
+- './spec/models/concerns/counter_attribute_spec.rb'
+- './spec/models/concerns/cron_schedulable_spec.rb'
+- './spec/models/concerns/cross_database_modification_spec.rb'
+- './spec/models/concerns/database_event_tracking_spec.rb'
+- './spec/models/concerns/database_reflection_spec.rb'
+- './spec/models/concerns/delete_with_limit_spec.rb'
+- './spec/models/concerns/deployment_platform_spec.rb'
+- './spec/models/concerns/deprecated_assignee_spec.rb'
+- './spec/models/concerns/discussion_on_diff_spec.rb'
+- './spec/models/concerns/each_batch_spec.rb'
+- './spec/models/concerns/editable_spec.rb'
+- './spec/models/concerns/expirable_spec.rb'
+- './spec/models/concerns/faster_cache_keys_spec.rb'
+- './spec/models/concerns/featurable_spec.rb'
+- './spec/models/concerns/feature_gate_spec.rb'
+- './spec/models/concerns/from_except_spec.rb'
+- './spec/models/concerns/from_intersect_spec.rb'
+- './spec/models/concerns/from_set_operator_spec.rb'
+- './spec/models/concerns/from_union_spec.rb'
+- './spec/models/concerns/group_descendant_spec.rb'
+- './spec/models/concerns/has_environment_scope_spec.rb'
+- './spec/models/concerns/has_user_type_spec.rb'
+- './spec/models/concerns/id_in_ordered_spec.rb'
+- './spec/models/concerns/ignorable_columns_spec.rb'
+- './spec/models/concerns/integrations/enable_ssl_verification_spec.rb'
+- './spec/models/concerns/integrations/has_data_fields_spec.rb'
+- './spec/models/concerns/integrations/reset_secret_fields_spec.rb'
+- './spec/models/concerns/issuable_link_spec.rb'
+- './spec/models/concerns/issuable_spec.rb'
+- './spec/models/concerns/legacy_bulk_insert_spec.rb'
+- './spec/models/concerns/limitable_spec.rb'
+- './spec/models/concerns/loaded_in_group_list_spec.rb'
+- './spec/models/concerns/loose_index_scan_spec.rb'
+- './spec/models/concerns/manual_inverse_association_spec.rb'
+- './spec/models/concerns/mentionable_spec.rb'
+- './spec/models/concerns/milestoneable_spec.rb'
+- './spec/models/concerns/milestoneish_spec.rb'
+- './spec/models/concerns/noteable_spec.rb'
+- './spec/models/concerns/nullify_if_blank_spec.rb'
+- './spec/models/concerns/optionally_search_spec.rb'
+- './spec/models/concerns/participable_spec.rb'
+- './spec/models/concerns/partitioned_table_spec.rb'
+- './spec/models/concerns/pg_full_text_searchable_spec.rb'
+- './spec/models/concerns/presentable_spec.rb'
+- './spec/models/concerns/project_api_compatibility_spec.rb'
+- './spec/models/concerns/project_features_compatibility_spec.rb'
+- './spec/models/concerns/prometheus_adapter_spec.rb'
+- './spec/models/concerns/protected_ref_access_spec.rb'
+- './spec/models/concerns/reactive_caching_spec.rb'
+- './spec/models/concerns/redactable_spec.rb'
+- './spec/models/concerns/redis_cacheable_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/runners_token_prefixable_spec.rb'
+- './spec/models/concerns/safe_url_spec.rb'
+- './spec/models/concerns/sanitizable_spec.rb'
+- './spec/models/concerns/schedulable_spec.rb'
+- './spec/models/concerns/sensitive_serializable_hash_spec.rb'
+- './spec/models/concerns/sha_attribute_spec.rb'
+- './spec/models/concerns/sortable_spec.rb'
+- './spec/models/concerns/spammable_spec.rb'
+- './spec/models/concerns/stepable_spec.rb'
+- './spec/models/concerns/strip_attribute_spec.rb'
+- './spec/models/concerns/subscribable_spec.rb'
+- './spec/models/concerns/taggable_queries_spec.rb'
+- './spec/models/concerns/taskable_spec.rb'
+- './spec/models/concerns/token_authenticatable_spec.rb'
+- './spec/models/concerns/token_authenticatable_strategies/base_spec.rb'
+- './spec/models/concerns/token_authenticatable_strategies/digest_spec.rb'
+- './spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb'
+- './spec/models/concerns/token_authenticatable_strategies/encryption_helper_spec.rb'
+- './spec/models/concerns/transactions_spec.rb'
+- './spec/models/concerns/triggerable_hooks_spec.rb'
+- './spec/models/concerns/uniquify_spec.rb'
+- './spec/models/concerns/usage_statistics_spec.rb'
+- './spec/models/concerns/vulnerability_finding_helpers_spec.rb'
+- './spec/models/concerns/vulnerability_finding_signature_helpers_spec.rb'
+- './spec/models/concerns/where_composite_spec.rb'
+- './spec/models/concerns/x509_serial_number_attribute_spec.rb'
+- './spec/models/container_expiration_policy_spec.rb'
+- './spec/models/container_registry/event_spec.rb'
+- './spec/models/container_repository_spec.rb'
+- './spec/models/context_commits_diff_spec.rb'
+- './spec/models/custom_emoji_spec.rb'
+- './spec/models/customer_relations/contact_spec.rb'
+- './spec/models/customer_relations/contact_state_counts_spec.rb'
+- './spec/models/customer_relations/issue_contact_spec.rb'
+- './spec/models/customer_relations/organization_spec.rb'
+- './spec/models/cycle_analytics/project_level_stage_adapter_spec.rb'
+- './spec/models/data_list_spec.rb'
+- './spec/models/dependency_proxy/blob_spec.rb'
+- './spec/models/dependency_proxy/group_setting_spec.rb'
+- './spec/models/dependency_proxy/image_ttl_group_policy_spec.rb'
+- './spec/models/dependency_proxy/manifest_spec.rb'
+- './spec/models/dependency_proxy/registry_spec.rb'
+- './spec/models/deploy_key_spec.rb'
+- './spec/models/deploy_keys_project_spec.rb'
+- './spec/models/deployment_cluster_spec.rb'
+- './spec/models/deployment_merge_request_spec.rb'
+- './spec/models/deployment_metrics_spec.rb'
+- './spec/models/deployment_spec.rb'
+- './spec/models/deploy_token_spec.rb'
+- './spec/models/description_version_spec.rb'
+- './spec/models/design_management/action_spec.rb'
+- './spec/models/design_management/design_action_spec.rb'
+- './spec/models/design_management/design_at_version_spec.rb'
+- './spec/models/design_management/design_collection_spec.rb'
+- './spec/models/design_management/design_spec.rb'
+- './spec/models/design_management/repository_spec.rb'
+- './spec/models/design_management/version_spec.rb'
+- './spec/models/design_user_mention_spec.rb'
+- './spec/models/dev_ops_report/metric_spec.rb'
+- './spec/models/diff_discussion_spec.rb'
+- './spec/models/diff_note_position_spec.rb'
+- './spec/models/diff_note_spec.rb'
+- './spec/models/diff_viewer/base_spec.rb'
+- './spec/models/diff_viewer/image_spec.rb'
+- './spec/models/diff_viewer/server_side_spec.rb'
+- './spec/models/discussion_note_spec.rb'
+- './spec/models/discussion_spec.rb'
+- './spec/models/draft_note_spec.rb'
+- './spec/models/email_spec.rb'
+- './spec/models/environment_spec.rb'
+- './spec/models/environment_status_spec.rb'
+- './spec/models/error_tracking/client_key_spec.rb'
+- './spec/models/error_tracking/error_event_spec.rb'
+- './spec/models/error_tracking/error_spec.rb'
+- './spec/models/error_tracking/project_error_tracking_setting_spec.rb'
+- './spec/models/event_collection_spec.rb'
+- './spec/models/event_spec.rb'
+- './spec/models/experiment_spec.rb'
+- './spec/models/experiment_subject_spec.rb'
+- './spec/models/experiment_user_spec.rb'
+- './spec/models/exported_protected_branch_spec.rb'
+- './spec/models/external_issue_spec.rb'
+- './spec/models/external_pull_request_spec.rb'
+- './spec/models/factories_spec.rb'
+- './spec/models/fork_network_member_spec.rb'
+- './spec/models/fork_network_spec.rb'
+- './spec/models/generic_commit_status_spec.rb'
+- './spec/models/gpg_key_spec.rb'
+- './spec/models/gpg_key_subkey_spec.rb'
+- './spec/models/grafana_integration_spec.rb'
+- './spec/models/group/crm_settings_spec.rb'
+- './spec/models/group_custom_attribute_spec.rb'
+- './spec/models/group_deploy_keys_group_spec.rb'
+- './spec/models/group_deploy_key_spec.rb'
+- './spec/models/group_deploy_token_spec.rb'
+- './spec/models/group_group_link_spec.rb'
+- './spec/models/group_import_state_spec.rb'
+- './spec/models/group_label_spec.rb'
+- './spec/models/groups/feature_setting_spec.rb'
+- './spec/models/group_spec.rb'
+- './spec/models/guest_spec.rb'
+- './spec/models/hooks/active_hook_filter_spec.rb'
+- './spec/models/hooks/project_hook_spec.rb'
+- './spec/models/hooks/service_hook_spec.rb'
+- './spec/models/hooks/system_hook_spec.rb'
+- './spec/models/hooks/web_hook_log_spec.rb'
+- './spec/models/hooks/web_hook_spec.rb'
+- './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'
+- './spec/models/integrations/asana_spec.rb'
+- './spec/models/integrations/assembla_spec.rb'
+- './spec/models/integrations/bamboo_spec.rb'
+- './spec/models/integrations/base_chat_notification_spec.rb'
+- './spec/models/integrations/base_issue_tracker_spec.rb'
+- './spec/models/integrations/base_third_party_wiki_spec.rb'
+- './spec/models/integrations/bugzilla_spec.rb'
+- './spec/models/integrations/buildkite_spec.rb'
+- './spec/models/integrations/campfire_spec.rb'
+- './spec/models/integrations/chat_message/alert_message_spec.rb'
+- './spec/models/integrations/chat_message/base_message_spec.rb'
+- './spec/models/integrations/chat_message/deployment_message_spec.rb'
+- './spec/models/integrations/chat_message/issue_message_spec.rb'
+- './spec/models/integrations/chat_message/merge_message_spec.rb'
+- './spec/models/integrations/chat_message/note_message_spec.rb'
+- './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/confluence_spec.rb'
+- './spec/models/integrations/custom_issue_tracker_spec.rb'
+- './spec/models/integrations/datadog_spec.rb'
+- './spec/models/integrations/discord_spec.rb'
+- './spec/models/integrations/drone_ci_spec.rb'
+- './spec/models/integrations/emails_on_push_spec.rb'
+- './spec/models/integrations/every_integration_spec.rb'
+- './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'
+- './spec/models/integrations/issue_tracker_data_spec.rb'
+- './spec/models/integrations/jenkins_spec.rb'
+- './spec/models/integrations/jira_spec.rb'
+- './spec/models/integrations/jira_tracker_data_spec.rb'
+- './spec/models/integrations/mattermost_slash_commands_spec.rb'
+- './spec/models/integrations/mattermost_spec.rb'
+- './spec/models/integrations/microsoft_teams_spec.rb'
+- './spec/models/integrations/mock_ci_spec.rb'
+- './spec/models/integrations/packagist_spec.rb'
+- './spec/models/integration_spec.rb'
+- './spec/models/integrations/pipelines_email_spec.rb'
+- './spec/models/integrations/pivotaltracker_spec.rb'
+- './spec/models/integrations/prometheus_spec.rb'
+- './spec/models/integrations/pumble_spec.rb'
+- './spec/models/integrations/pushover_spec.rb'
+- './spec/models/integrations/redmine_spec.rb'
+- './spec/models/integrations/shimo_spec.rb'
+- './spec/models/integrations/slack_slash_commands_spec.rb'
+- './spec/models/integrations/slack_spec.rb'
+- './spec/models/integrations/teamcity_spec.rb'
+- './spec/models/integrations/unify_circuit_spec.rb'
+- './spec/models/integrations/webex_teams_spec.rb'
+- './spec/models/integrations/youtrack_spec.rb'
+- './spec/models/integrations/zentao_spec.rb'
+- './spec/models/integrations/zentao_tracker_data_spec.rb'
+- './spec/models/internal_id_spec.rb'
+- './spec/models/issuable_severity_spec.rb'
+- './spec/models/issue_assignee_spec.rb'
+- './spec/models/issue_collection_spec.rb'
+- './spec/models/issue_email_participant_spec.rb'
+- './spec/models/issue/email_spec.rb'
+- './spec/models/issue_link_spec.rb'
+- './spec/models/issue/metrics_spec.rb'
+- './spec/models/issues/csv_import_spec.rb'
+- './spec/models/issue_spec.rb'
+- './spec/models/jira_connect_installation_spec.rb'
+- './spec/models/jira_connect_subscription_spec.rb'
+- './spec/models/jira_import_state_spec.rb'
+- './spec/models/key_spec.rb'
+- './spec/models/label_link_spec.rb'
+- './spec/models/label_note_spec.rb'
+- './spec/models/label_priority_spec.rb'
+- './spec/models/label_spec.rb'
+- './spec/models/legacy_diff_discussion_spec.rb'
+- './spec/models/legacy_diff_note_spec.rb'
+- './spec/models/lfs_download_object_spec.rb'
+- './spec/models/lfs_file_lock_spec.rb'
+- './spec/models/lfs_object_spec.rb'
+- './spec/models/lfs_objects_project_spec.rb'
+- './spec/models/license_template_spec.rb'
+- './spec/models/list_spec.rb'
+- './spec/models/list_user_preference_spec.rb'
+- './spec/models/loose_foreign_keys/deleted_record_spec.rb'
+- './spec/models/loose_foreign_keys/modification_tracker_spec.rb'
+- './spec/models/members/group_member_spec.rb'
+- './spec/models/members/last_group_owner_assigner_spec.rb'
+- './spec/models/members/member_role_spec.rb'
+- './spec/models/members/member_task_spec.rb'
+- './spec/models/member_spec.rb'
+- './spec/models/members/project_member_spec.rb'
+- './spec/models/merge_request/approval_removal_settings_spec.rb'
+- './spec/models/merge_request_assignee_spec.rb'
+- './spec/models/merge_request/cleanup_schedule_spec.rb'
+- './spec/models/merge_request_context_commit_diff_file_spec.rb'
+- './spec/models/merge_request_context_commit_spec.rb'
+- './spec/models/merge_request_diff_commit_spec.rb'
+- './spec/models/merge_request/diff_commit_user_spec.rb'
+- './spec/models/merge_request_diff_file_spec.rb'
+- './spec/models/merge_request_diff_spec.rb'
+- './spec/models/merge_request/metrics_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'
+- './spec/models/milestone_note_spec.rb'
+- './spec/models/milestone_release_spec.rb'
+- './spec/models/milestone_spec.rb'
+- './spec/models/ml/candidate_metric_spec.rb'
+- './spec/models/ml/candidate_param_spec.rb'
+- './spec/models/ml/candidate_spec.rb'
+- './spec/models/ml/experiment_spec.rb'
+- './spec/models/namespace/admin_note_spec.rb'
+- './spec/models/namespace/aggregation_schedule_spec.rb'
+- './spec/models/namespace_ci_cd_setting_spec.rb'
+- './spec/models/namespace/detail_spec.rb'
+- './spec/models/namespace/package_setting_spec.rb'
+- './spec/models/namespace/root_storage_statistics_spec.rb'
+- './spec/models/namespace_setting_spec.rb'
+- './spec/models/namespace_spec.rb'
+- './spec/models/namespaces/project_namespace_spec.rb'
+- './spec/models/namespace_statistics_spec.rb'
+- './spec/models/namespaces/user_namespace_spec.rb'
+- './spec/models/namespace/traversal_hierarchy_spec.rb'
+- './spec/models/network/graph_spec.rb'
+- './spec/models/note_diff_file_spec.rb'
+- './spec/models/note_spec.rb'
+- './spec/models/notification_recipient_spec.rb'
+- './spec/models/notification_setting_spec.rb'
+- './spec/models/oauth_access_grant_spec.rb'
+- './spec/models/oauth_access_token_spec.rb'
+- './spec/models/onboarding_progress_spec.rb'
+- './spec/models/operations/feature_flags_client_spec.rb'
+- './spec/models/operations/feature_flag_spec.rb'
+- './spec/models/operations/feature_flags/strategy_spec.rb'
+- './spec/models/operations/feature_flags/user_list_spec.rb'
+- './spec/models/packages/build_info_spec.rb'
+- './spec/models/packages/cleanup/policy_spec.rb'
+- './spec/models/packages/composer/cache_file_spec.rb'
+- './spec/models/packages/composer/metadatum_spec.rb'
+- './spec/models/packages/conan/file_metadatum_spec.rb'
+- './spec/models/packages/conan/metadatum_spec.rb'
+- './spec/models/packages/debian/file_entry_spec.rb'
+- './spec/models/packages/debian/file_metadatum_spec.rb'
+- './spec/models/packages/debian/group_architecture_spec.rb'
+- './spec/models/packages/debian/group_component_file_spec.rb'
+- './spec/models/packages/debian/group_component_spec.rb'
+- './spec/models/packages/debian/group_distribution_key_spec.rb'
+- './spec/models/packages/debian/group_distribution_spec.rb'
+- './spec/models/packages/debian/project_architecture_spec.rb'
+- './spec/models/packages/debian/project_component_file_spec.rb'
+- './spec/models/packages/debian/project_component_spec.rb'
+- './spec/models/packages/debian/project_distribution_key_spec.rb'
+- './spec/models/packages/debian/project_distribution_spec.rb'
+- './spec/models/packages/debian/publication_spec.rb'
+- './spec/models/packages/dependency_link_spec.rb'
+- './spec/models/packages/dependency_spec.rb'
+- './spec/models/packages/go/module_spec.rb'
+- './spec/models/packages/go/module_version_spec.rb'
+- './spec/models/packages/helm/file_metadatum_spec.rb'
+- './spec/models/packages/maven/metadatum_spec.rb'
+- './spec/models/packages/npm/metadatum_spec.rb'
+- './spec/models/packages/npm_spec.rb'
+- './spec/models/packages/nuget/dependency_link_metadatum_spec.rb'
+- './spec/models/packages/nuget/metadatum_spec.rb'
+- './spec/models/packages/package_file_build_info_spec.rb'
+- './spec/models/packages/package_file_spec.rb'
+- './spec/models/packages/package_spec.rb'
+- './spec/models/packages/pypi/metadatum_spec.rb'
+- './spec/models/packages/rubygems/metadatum_spec.rb'
+- './spec/models/packages/sem_ver_spec.rb'
+- './spec/models/packages/tag_spec.rb'
+- './spec/models/pages_deployment_spec.rb'
+- './spec/models/pages_domain_acme_order_spec.rb'
+- './spec/models/pages_domain_spec.rb'
+- './spec/models/pages/lookup_path_spec.rb'
+- './spec/models/pages/virtual_domain_spec.rb'
+- './spec/models/performance_monitoring/prometheus_dashboard_spec.rb'
+- './spec/models/performance_monitoring/prometheus_metric_spec.rb'
+- './spec/models/performance_monitoring/prometheus_panel_group_spec.rb'
+- './spec/models/performance_monitoring/prometheus_panel_spec.rb'
+- './spec/models/personal_access_token_spec.rb'
+- './spec/models/personal_snippet_spec.rb'
+- './spec/models/plan_limits_spec.rb'
+- './spec/models/plan_spec.rb'
+- './spec/models/pool_repository_spec.rb'
+- './spec/models/postgresql/detached_partition_spec.rb'
+- './spec/models/postgresql/replication_slot_spec.rb'
+- './spec/models/preloaders/commit_status_preloader_spec.rb'
+- './spec/models/preloaders/environments/deployment_preloader_spec.rb'
+- './spec/models/preloaders/group_policy_preloader_spec.rb'
+- './spec/models/preloaders/group_root_ancestor_preloader_spec.rb'
+- './spec/models/preloaders/labels_preloader_spec.rb'
+- './spec/models/preloaders/merge_request_diff_preloader_spec.rb'
+- './spec/models/preloaders/user_max_access_level_in_groups_preloader_spec.rb'
+- './spec/models/preloaders/user_max_access_level_in_projects_preloader_spec.rb'
+- './spec/models/preloaders/users_max_access_level_in_projects_preloader_spec.rb'
+- './spec/models/product_analytics_event_spec.rb'
+- './spec/models/programming_language_spec.rb'
+- './spec/models/project_authorization_spec.rb'
+- './spec/models/project_auto_devops_spec.rb'
+- './spec/models/project_ci_cd_setting_spec.rb'
+- './spec/models/project_custom_attribute_spec.rb'
+- './spec/models/project_daily_statistic_spec.rb'
+- './spec/models/project_deploy_token_spec.rb'
+- './spec/models/project_export_job_spec.rb'
+- './spec/models/project_feature_spec.rb'
+- './spec/models/project_feature_usage_spec.rb'
+- './spec/models/project_group_link_spec.rb'
+- './spec/models/project_import_data_spec.rb'
+- './spec/models/project_import_state_spec.rb'
+- './spec/models/project_label_spec.rb'
+- './spec/models/project_metrics_setting_spec.rb'
+- './spec/models/project_pages_metadatum_spec.rb'
+- './spec/models/project_repository_spec.rb'
+- './spec/models/projects/build_artifacts_size_refresh_spec.rb'
+- './spec/models/projects/ci_feature_usage_spec.rb'
+- './spec/models/project_setting_spec.rb'
+- './spec/models/projects/import_export/relation_export_spec.rb'
+- './spec/models/projects/import_export/relation_export_upload_spec.rb'
+- './spec/models/project_snippet_spec.rb'
+- './spec/models/project_spec.rb'
+- './spec/models/projects/project_topic_spec.rb'
+- './spec/models/projects/repository_storage_move_spec.rb'
+- './spec/models/project_statistics_spec.rb'
+- './spec/models/projects/topic_spec.rb'
+- './spec/models/projects/triggered_hooks_spec.rb'
+- './spec/models/project_team_spec.rb'
+- './spec/models/project_wiki_spec.rb'
+- './spec/models/prometheus_alert_event_spec.rb'
+- './spec/models/prometheus_alert_spec.rb'
+- './spec/models/prometheus_metric_spec.rb'
+- './spec/models/protectable_dropdown_spec.rb'
+- './spec/models/protected_branch/merge_access_level_spec.rb'
+- './spec/models/protected_branch/push_access_level_spec.rb'
+- './spec/models/protected_branch_spec.rb'
+- './spec/models/protected_tag_spec.rb'
+- './spec/models/push_event_payload_spec.rb'
+- './spec/models/push_event_spec.rb'
+- './spec/models/raw_usage_data_spec.rb'
+- './spec/models/redirect_route_spec.rb'
+- './spec/models/ref_matcher_spec.rb'
+- './spec/models/release_highlight_spec.rb'
+- './spec/models/releases/evidence_spec.rb'
+- './spec/models/releases/link_spec.rb'
+- './spec/models/release_spec.rb'
+- './spec/models/releases/source_spec.rb'
+- './spec/models/remote_mirror_spec.rb'
+- './spec/models/repository_language_spec.rb'
+- './spec/models/repository_spec.rb'
+- './spec/models/resource_label_event_spec.rb'
+- './spec/models/resource_milestone_event_spec.rb'
+- './spec/models/resource_state_event_spec.rb'
+- './spec/models/review_spec.rb'
+- './spec/models/route_spec.rb'
+- './spec/models/sent_notification_spec.rb'
+- './spec/models/sentry_issue_spec.rb'
+- './spec/models/serverless/domain_cluster_spec.rb'
+- './spec/models/serverless/domain_spec.rb'
+- './spec/models/serverless/function_spec.rb'
+- './spec/models/service_desk_setting_spec.rb'
+- './spec/models/shard_spec.rb'
+- './spec/models/snippet_blob_spec.rb'
+- './spec/models/snippet_input_action_collection_spec.rb'
+- './spec/models/snippet_input_action_spec.rb'
+- './spec/models/snippet_repository_spec.rb'
+- './spec/models/snippet_spec.rb'
+- './spec/models/snippets/repository_storage_move_spec.rb'
+- './spec/models/snippet_statistics_spec.rb'
+- './spec/models/spam_log_spec.rb'
+- './spec/models/ssh_host_key_spec.rb'
+- './spec/models/state_note_spec.rb'
+- './spec/models/subscription_spec.rb'
+- './spec/models/suggestion_spec.rb'
+- './spec/models/synthetic_note_spec.rb'
+- './spec/models/system_note_metadata_spec.rb'
+- './spec/models/term_agreement_spec.rb'
+- './spec/models/terraform/state_spec.rb'
+- './spec/models/terraform/state_version_spec.rb'
+- './spec/models/timelog_spec.rb'
+- './spec/models/time_tracking/timelog_category_spec.rb'
+- './spec/models/todo_spec.rb'
+- './spec/models/token_with_iv_spec.rb'
+- './spec/models/tree_spec.rb'
+- './spec/models/trending_project_spec.rb'
+- './spec/models/u2f_registration_spec.rb'
+- './spec/models/uploads/fog_spec.rb'
+- './spec/models/uploads/local_spec.rb'
+- './spec/models/upload_spec.rb'
+- './spec/models/user_agent_detail_spec.rb'
+- './spec/models/user_canonical_email_spec.rb'
+- './spec/models/user_custom_attribute_spec.rb'
+- './spec/models/user_detail_spec.rb'
+- './spec/models/user_highest_role_spec.rb'
+- './spec/models/user_interacted_project_spec.rb'
+- './spec/models/user_mentions/commit_user_mention_spec.rb'
+- './spec/models/user_mentions/issue_user_mention_spec.rb'
+- './spec/models/user_mentions/merge_request_user_mention_spec.rb'
+- './spec/models/user_mentions/snippet_user_mention_spec.rb'
+- './spec/models/user_preference_spec.rb'
+- './spec/models/users/banned_user_spec.rb'
+- './spec/models/users/calloutable_spec.rb'
+- './spec/models/users/callout_spec.rb'
+- './spec/models/users/credit_card_validation_spec.rb'
+- './spec/models/users/group_callout_spec.rb'
+- './spec/models/users/in_product_marketing_email_spec.rb'
+- './spec/models/users/merge_request_interaction_spec.rb'
+- './spec/models/users/namespace_callout_spec.rb'
+- './spec/models/user_spec.rb'
+- './spec/models/users/project_callout_spec.rb'
+- './spec/models/users/saved_reply_spec.rb'
+- './spec/models/users_star_project_spec.rb'
+- './spec/models/users_statistics_spec.rb'
+- './spec/models/user_status_spec.rb'
+- './spec/models/webauthn_registration_spec.rb'
+- './spec/models/web_ide_terminal_spec.rb'
+- './spec/models/wiki_directory_spec.rb'
+- './spec/models/wiki_page/meta_spec.rb'
+- './spec/models/wiki_page/slug_spec.rb'
+- './spec/models/wiki_page_spec.rb'
+- './spec/models/work_items/parent_link_spec.rb'
+- './spec/models/work_item_spec.rb'
+- './spec/models/work_items/type_spec.rb'
+- './spec/models/work_items/widgets/assignees_spec.rb'
+- './spec/models/work_items/widgets/base_spec.rb'
+- './spec/models/work_items/widgets/description_spec.rb'
+- './spec/models/work_items/widgets/hierarchy_spec.rb'
+- './spec/models/work_items/widgets/labels_spec.rb'
+- './spec/models/work_items/widgets/start_and_due_date_spec.rb'
+- './spec/models/x509_certificate_spec.rb'
+- './spec/models/x509_issuer_spec.rb'
+- './spec/models/zoom_meeting_spec.rb'
+- './spec/policies/alert_management/alert_policy_spec.rb'
+- './spec/policies/alert_management/http_integration_policy_spec.rb'
+- './spec/policies/application_setting_policy_spec.rb'
+- './spec/policies/application_setting/term_policy_spec.rb'
+- './spec/policies/award_emoji_policy_spec.rb'
+- './spec/policies/base_policy_spec.rb'
+- './spec/policies/blob_policy_spec.rb'
+- './spec/policies/board_policy_spec.rb'
+- './spec/policies/ci/bridge_policy_spec.rb'
+- './spec/policies/ci/build_policy_spec.rb'
+- './spec/policies/ci/pipeline_policy_spec.rb'
+- './spec/policies/ci/pipeline_schedule_policy_spec.rb'
+- './spec/policies/ci/trigger_policy_spec.rb'
+- './spec/policies/clusters/agent_policy_spec.rb'
+- './spec/policies/clusters/agents/activity_event_policy_spec.rb'
+- './spec/policies/clusters/agent_token_policy_spec.rb'
+- './spec/policies/clusters/cluster_policy_spec.rb'
+- './spec/policies/clusters/instance_policy_spec.rb'
+- './spec/policies/commit_policy_spec.rb'
+- './spec/policies/concerns/crud_policy_helpers_spec.rb'
+- './spec/policies/concerns/policy_actor_spec.rb'
+- './spec/policies/concerns/readonly_abilities_spec.rb'
+- './spec/policies/container_expiration_policy_policy_spec.rb'
+- './spec/policies/custom_emoji_policy_spec.rb'
+- './spec/policies/deploy_key_policy_spec.rb'
+- './spec/policies/deploy_keys_project_policy_spec.rb'
+- './spec/policies/deploy_token_policy_spec.rb'
+- './spec/policies/design_management/design_policy_spec.rb'
+- './spec/policies/environment_policy_spec.rb'
+- './spec/policies/global_policy_spec.rb'
+- './spec/policies/group_deploy_key_policy_spec.rb'
+- './spec/policies/group_deploy_keys_group_policy_spec.rb'
+- './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'
+- './spec/policies/issue_policy_spec.rb'
+- './spec/policies/merge_request_policy_spec.rb'
+- './spec/policies/metrics/dashboard/annotation_policy_spec.rb'
+- './spec/policies/namespace/root_storage_statistics_policy_spec.rb'
+- './spec/policies/namespaces/project_namespace_policy_spec.rb'
+- './spec/policies/namespaces/user_namespace_policy_spec.rb'
+- './spec/policies/note_policy_spec.rb'
+- './spec/policies/packages/package_policy_spec.rb'
+- './spec/policies/packages/policies/group_policy_spec.rb'
+- './spec/policies/packages/policies/project_policy_spec.rb'
+- './spec/policies/personal_access_token_policy_spec.rb'
+- './spec/policies/personal_snippet_policy_spec.rb'
+- './spec/policies/project_hook_policy_spec.rb'
+- './spec/policies/project_member_policy_spec.rb'
+- './spec/policies/project_policy_spec.rb'
+- './spec/policies/project_snippet_policy_spec.rb'
+- './spec/policies/project_statistics_policy_spec.rb'
+- './spec/policies/protected_branch_policy_spec.rb'
+- './spec/policies/release_policy_spec.rb'
+- './spec/policies/resource_label_event_policy_spec.rb'
+- './spec/policies/system_hook_policy_spec.rb'
+- './spec/policies/terraform/state_policy_spec.rb'
+- './spec/policies/terraform/state_version_policy_spec.rb'
+- './spec/policies/timelog_policy_spec.rb'
+- './spec/policies/todo_policy_spec.rb'
+- './spec/policies/upload_policy_spec.rb'
+- './spec/policies/user_policy_spec.rb'
+- './spec/policies/wiki_page_policy_spec.rb'
+- './spec/policies/work_item_policy_spec.rb'
+- './spec/presenters/alert_management/alert_presenter_spec.rb'
+- './spec/presenters/award_emoji_presenter_spec.rb'
+- './spec/presenters/blob_presenter_spec.rb'
+- './spec/presenters/blobs/notebook_presenter_spec.rb'
+- './spec/presenters/blobs/unfold_presenter_spec.rb'
+- './spec/presenters/ci/bridge_presenter_spec.rb'
+- './spec/presenters/ci/build_presenter_spec.rb'
+- './spec/presenters/ci/build_runner_presenter_spec.rb'
+- './spec/presenters/ci/group_variable_presenter_spec.rb'
+- './spec/presenters/ci/pipeline_artifacts/code_coverage_presenter_spec.rb'
+- './spec/presenters/ci/pipeline_artifacts/code_quality_mr_diff_presenter_spec.rb'
+- './spec/presenters/ci/pipeline_presenter_spec.rb'
+- './spec/presenters/ci/stage_presenter_spec.rb'
+- './spec/presenters/ci/trigger_presenter_spec.rb'
+- './spec/presenters/ci/variable_presenter_spec.rb'
+- './spec/presenters/clusterable_presenter_spec.rb'
+- './spec/presenters/clusters/cluster_presenter_spec.rb'
+- './spec/presenters/commit_presenter_spec.rb'
+- './spec/presenters/commit_status_presenter_spec.rb'
+- './spec/presenters/dev_ops_report/metric_presenter_spec.rb'
+- './spec/presenters/event_presenter_spec.rb'
+- './spec/presenters/gitlab/blame_presenter_spec.rb'
+- './spec/presenters/group_clusterable_presenter_spec.rb'
+- './spec/presenters/group_member_presenter_spec.rb'
+- './spec/presenters/instance_clusterable_presenter_spec.rb'
+- './spec/presenters/issue_presenter_spec.rb'
+- './spec/presenters/label_presenter_spec.rb'
+- './spec/presenters/merge_request_presenter_spec.rb'
+- './spec/presenters/milestone_presenter_spec.rb'
+- './spec/presenters/packages/composer/packages_presenter_spec.rb'
+- './spec/presenters/packages/conan/package_presenter_spec.rb'
+- './spec/presenters/packages/detail/package_presenter_spec.rb'
+- './spec/presenters/packages/helm/index_presenter_spec.rb'
+- './spec/presenters/packages/npm/package_presenter_spec.rb'
+- './spec/presenters/packages/nuget/package_metadata_presenter_spec.rb'
+- './spec/presenters/packages/nuget/packages_metadata_presenter_spec.rb'
+- './spec/presenters/packages/nuget/packages_versions_presenter_spec.rb'
+- './spec/presenters/packages/nuget/search_results_presenter_spec.rb'
+- './spec/presenters/packages/nuget/service_index_presenter_spec.rb'
+- './spec/presenters/packages/pypi/simple_index_presenter_spec.rb'
+- './spec/presenters/packages/pypi/simple_package_versions_presenter_spec.rb'
+- './spec/presenters/pages_domain_presenter_spec.rb'
+- './spec/presenters/project_clusterable_presenter_spec.rb'
+- './spec/presenters/project_hook_presenter_spec.rb'
+- './spec/presenters/project_member_presenter_spec.rb'
+- './spec/presenters/project_presenter_spec.rb'
+- './spec/presenters/projects/import_export/project_export_presenter_spec.rb'
+- './spec/presenters/projects/security/configuration_presenter_spec.rb'
+- './spec/presenters/projects/settings/deploy_keys_presenter_spec.rb'
+- './spec/presenters/prometheus_alert_presenter_spec.rb'
+- './spec/presenters/release_presenter_spec.rb'
+- './spec/presenters/releases/link_presenter_spec.rb'
+- './spec/presenters/search_service_presenter_spec.rb'
+- './spec/presenters/sentry_error_presenter_spec.rb'
+- './spec/presenters/service_hook_presenter_spec.rb'
+- './spec/presenters/snippet_blob_presenter_spec.rb'
+- './spec/presenters/snippet_presenter_spec.rb'
+- './spec/presenters/terraform/modules_presenter_spec.rb'
+- './spec/presenters/tree_entry_presenter_spec.rb'
+- './spec/presenters/user_presenter_spec.rb'
+- './spec/presenters/web_hook_log_presenter_spec.rb'
+- './spec/rack_servers/puma_spec.rb'
+- './spec/requests/abuse_reports_controller_spec.rb'
+- './spec/requests/admin/applications_controller_spec.rb'
+- './spec/requests/admin/background_migrations_controller_spec.rb'
+- './spec/requests/admin/batched_jobs_controller_spec.rb'
+- './spec/requests/admin/broadcast_messages_controller_spec.rb'
+- './spec/requests/admin/clusters/integrations_controller_spec.rb'
+- './spec/requests/admin/impersonation_tokens_controller_spec.rb'
+- './spec/requests/admin/integrations_controller_spec.rb'
+- './spec/requests/admin/version_check_controller_spec.rb'
+- './spec/requests/api/access_requests_spec.rb'
+- './spec/requests/api/admin/batched_background_migrations_spec.rb'
+- './spec/requests/api/admin/ci/variables_spec.rb'
+- './spec/requests/api/admin/instance_clusters_spec.rb'
+- './spec/requests/api/admin/plan_limits_spec.rb'
+- './spec/requests/api/admin/sidekiq_spec.rb'
+- './spec/requests/api/alert_management_alerts_spec.rb'
+- './spec/requests/api/api_guard/admin_mode_middleware_spec.rb'
+- './spec/requests/api/api_guard/response_coercer_middleware_spec.rb'
+- './spec/requests/api/api_spec.rb'
+- './spec/requests/api/appearance_spec.rb'
+- './spec/requests/api/applications_spec.rb'
+- './spec/requests/api/avatar_spec.rb'
+- './spec/requests/api/award_emoji_spec.rb'
+- './spec/requests/api/badges_spec.rb'
+- './spec/requests/api/boards_spec.rb'
+- './spec/requests/api/branches_spec.rb'
+- './spec/requests/api/broadcast_messages_spec.rb'
+- './spec/requests/api/bulk_imports_spec.rb'
+- './spec/requests/api/ci/job_artifacts_spec.rb'
+- './spec/requests/api/ci/jobs_spec.rb'
+- './spec/requests/api/ci/pipeline_schedules_spec.rb'
+- './spec/requests/api/ci/pipelines_spec.rb'
+- './spec/requests/api/ci/resource_groups_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_reset_spec.rb'
+- './spec/requests/api/ci/runner/runners_verify_post_spec.rb'
+- './spec/requests/api/ci/runners_reset_registration_token_spec.rb'
+- './spec/requests/api/ci/runners_spec.rb'
+- './spec/requests/api/ci/secure_files_spec.rb'
+- './spec/requests/api/ci/triggers_spec.rb'
+- './spec/requests/api/ci/variables_spec.rb'
+- './spec/requests/api/clusters/agents_spec.rb'
+- './spec/requests/api/clusters/agent_tokens_spec.rb'
+- './spec/requests/api/commits_spec.rb'
+- './spec/requests/api/commit_statuses_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/container_repositories_spec.rb'
+- './spec/requests/api/debian_group_packages_spec.rb'
+- './spec/requests/api/debian_project_packages_spec.rb'
+- './spec/requests/api/dependency_proxy_spec.rb'
+- './spec/requests/api/deploy_keys_spec.rb'
+- './spec/requests/api/deployments_spec.rb'
+- './spec/requests/api/deploy_tokens_spec.rb'
+- './spec/requests/api/discussions_spec.rb'
+- './spec/requests/api/doorkeeper_access_spec.rb'
+- './spec/requests/api/environments_spec.rb'
+- './spec/requests/api/error_tracking/client_keys_spec.rb'
+- './spec/requests/api/error_tracking/collector_spec.rb'
+- './spec/requests/api/error_tracking/project_settings_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/features_spec.rb'
+- './spec/requests/api/files_spec.rb'
+- './spec/requests/api/freeze_periods_spec.rb'
+- './spec/requests/api/generic_packages_spec.rb'
+- './spec/requests/api/geo_spec.rb'
+- './spec/requests/api/go_proxy_spec.rb'
+- './spec/requests/api/graphql/boards/board_list_issues_query_spec.rb'
+- './spec/requests/api/graphql/boards/board_list_query_spec.rb'
+- './spec/requests/api/graphql/boards/board_lists_query_spec.rb'
+- './spec/requests/api/graphql/boards/boards_query_spec.rb'
+- './spec/requests/api/graphql/ci/application_setting_spec.rb'
+- './spec/requests/api/graphql/ci/ci_cd_setting_spec.rb'
+- './spec/requests/api/graphql/ci/config_spec.rb'
+- './spec/requests/api/graphql/ci/groups_spec.rb'
+- './spec/requests/api/graphql/ci/group_variables_spec.rb'
+- './spec/requests/api/graphql/ci/instance_variables_spec.rb'
+- './spec/requests/api/graphql/ci/job_artifacts_spec.rb'
+- './spec/requests/api/graphql/ci/job_spec.rb'
+- './spec/requests/api/graphql/ci/jobs_spec.rb'
+- './spec/requests/api/graphql/ci/manual_variables_spec.rb'
+- './spec/requests/api/graphql/ci/pipelines_spec.rb'
+- './spec/requests/api/graphql/ci/project_variables_spec.rb'
+- './spec/requests/api/graphql/ci/runner_spec.rb'
+- './spec/requests/api/graphql/ci/runners_spec.rb'
+- './spec/requests/api/graphql/ci/runner_web_url_edge_spec.rb'
+- './spec/requests/api/graphql/ci/stages_spec.rb'
+- './spec/requests/api/graphql/ci/template_spec.rb'
+- './spec/requests/api/graphql/container_repository/container_repository_details_spec.rb'
+- './spec/requests/api/graphql/crm/contacts_spec.rb'
+- './spec/requests/api/graphql/current_user/groups_query_spec.rb'
+- './spec/requests/api/graphql/current_user_query_spec.rb'
+- './spec/requests/api/graphql/current_user/todos_query_spec.rb'
+- './spec/requests/api/graphql/current_user_todos_spec.rb'
+- './spec/requests/api/graphql/custom_emoji_query_spec.rb'
+- './spec/requests/api/graphql/gitlab_schema_spec.rb'
+- './spec/requests/api/graphql/group/container_repositories_spec.rb'
+- './spec/requests/api/graphql/group/dependency_proxy_blobs_spec.rb'
+- './spec/requests/api/graphql/group/dependency_proxy_group_setting_spec.rb'
+- './spec/requests/api/graphql/group/dependency_proxy_image_ttl_policy_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/issues_spec.rb'
+- './spec/requests/api/graphql/group/labels_query_spec.rb'
+- './spec/requests/api/graphql/group/merge_requests_spec.rb'
+- './spec/requests/api/graphql/group/milestones_spec.rb'
+- './spec/requests/api/graphql/group/packages_spec.rb'
+- './spec/requests/api/graphql/group_query_spec.rb'
+- './spec/requests/api/graphql/group/recent_issue_boards_query_spec.rb'
+- './spec/requests/api/graphql/group/timelogs_spec.rb'
+- './spec/requests/api/graphql/group/work_item_types_spec.rb'
+- './spec/requests/api/graphql/issue/issue_spec.rb'
+- './spec/requests/api/graphql/issue_status_counts_spec.rb'
+- './spec/requests/api/graphql/merge_request/merge_request_spec.rb'
+- './spec/requests/api/graphql/metadata_query_spec.rb'
+- './spec/requests/api/graphql/metrics/dashboard/annotations_spec.rb'
+- './spec/requests/api/graphql/metrics/dashboard_query_spec.rb'
+- './spec/requests/api/graphql/milestone_spec.rb'
+- './spec/requests/api/graphql/multiplexed_queries_spec.rb'
+- './spec/requests/api/graphql/mutations/admin/sidekiq_queues/delete_jobs_spec.rb'
+- './spec/requests/api/graphql/mutations/alert_management/alerts/create_alert_issue_spec.rb'
+- './spec/requests/api/graphql/mutations/alert_management/alerts/set_assignees_spec.rb'
+- './spec/requests/api/graphql/mutations/alert_management/alerts/todo/create_spec.rb'
+- './spec/requests/api/graphql/mutations/alert_management/alerts/update_alert_status_spec.rb'
+- './spec/requests/api/graphql/mutations/alert_management/http_integration/create_spec.rb'
+- './spec/requests/api/graphql/mutations/alert_management/http_integration/destroy_spec.rb'
+- './spec/requests/api/graphql/mutations/alert_management/http_integration/reset_token_spec.rb'
+- './spec/requests/api/graphql/mutations/alert_management/http_integration/update_spec.rb'
+- './spec/requests/api/graphql/mutations/alert_management/prometheus_integration/create_spec.rb'
+- './spec/requests/api/graphql/mutations/alert_management/prometheus_integration/reset_token_spec.rb'
+- './spec/requests/api/graphql/mutations/alert_management/prometheus_integration/update_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/create_spec.rb'
+- './spec/requests/api/graphql/mutations/boards/destroy_spec.rb'
+- './spec/requests/api/graphql/mutations/boards/issues/issue_move_list_spec.rb'
+- './spec/requests/api/graphql/mutations/boards/lists/create_spec.rb'
+- './spec/requests/api/graphql/mutations/boards/lists/destroy_spec.rb'
+- './spec/requests/api/graphql/mutations/boards/lists/update_spec.rb'
+- './spec/requests/api/graphql/mutations/branches/create_spec.rb'
+- './spec/requests/api/graphql/mutations/ci/job_cancel_spec.rb'
+- './spec/requests/api/graphql/mutations/ci/job_play_spec.rb'
+- './spec/requests/api/graphql/mutations/ci/job_retry_spec.rb'
+- './spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb'
+- './spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb'
+- './spec/requests/api/graphql/mutations/ci/job_unschedule_spec.rb'
+- './spec/requests/api/graphql/mutations/ci/pipeline_cancel_spec.rb'
+- './spec/requests/api/graphql/mutations/ci/pipeline_destroy_spec.rb'
+- './spec/requests/api/graphql/mutations/ci/pipeline_retry_spec.rb'
+- './spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb'
+- './spec/requests/api/graphql/mutations/ci/runners_registration_token/reset_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/clusters/agent_tokens/agent_tokens/create_spec.rb'
+- './spec/requests/api/graphql/mutations/commits/create_spec.rb'
+- './spec/requests/api/graphql/mutations/container_expiration_policy/update_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/custom_emoji/create_spec.rb'
+- './spec/requests/api/graphql/mutations/custom_emoji/destroy_spec.rb'
+- './spec/requests/api/graphql/mutations/dependency_proxy/group_settings/update_spec.rb'
+- './spec/requests/api/graphql/mutations/dependency_proxy/image_ttl_group_policy/update_spec.rb'
+- './spec/requests/api/graphql/mutations/design_management/delete_spec.rb'
+- './spec/requests/api/graphql/mutations/design_management/move_spec.rb'
+- './spec/requests/api/graphql/mutations/design_management/upload_spec.rb'
+- './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'
+- './spec/requests/api/graphql/mutations/issues/set_crm_contacts_spec.rb'
+- './spec/requests/api/graphql/mutations/issues/set_due_date_spec.rb'
+- './spec/requests/api/graphql/mutations/issues/set_escalation_status_spec.rb'
+- './spec/requests/api/graphql/mutations/issues/set_locked_spec.rb'
+- './spec/requests/api/graphql/mutations/issues/set_severity_spec.rb'
+- './spec/requests/api/graphql/mutations/issues/set_subscription_spec.rb'
+- './spec/requests/api/graphql/mutations/issues/update_spec.rb'
+- './spec/requests/api/graphql/mutations/jira_import/import_users_spec.rb'
+- './spec/requests/api/graphql/mutations/jira_import/start_spec.rb'
+- './spec/requests/api/graphql/mutations/labels/create_spec.rb'
+- './spec/requests/api/graphql/mutations/merge_requests/accept_spec.rb'
+- './spec/requests/api/graphql/mutations/merge_requests/create_spec.rb'
+- './spec/requests/api/graphql/mutations/merge_requests/reviewer_rereview_spec.rb'
+- './spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb'
+- './spec/requests/api/graphql/mutations/merge_requests/set_draft_spec.rb'
+- './spec/requests/api/graphql/mutations/merge_requests/set_labels_spec.rb'
+- './spec/requests/api/graphql/mutations/merge_requests/set_locked_spec.rb'
+- './spec/requests/api/graphql/mutations/merge_requests/set_milestone_spec.rb'
+- './spec/requests/api/graphql/mutations/merge_requests/set_reviewers_spec.rb'
+- './spec/requests/api/graphql/mutations/merge_requests/set_subscription_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/namespace/package_settings/update_spec.rb'
+- './spec/requests/api/graphql/mutations/notes/create/diff_note_spec.rb'
+- './spec/requests/api/graphql/mutations/notes/create/image_diff_note_spec.rb'
+- './spec/requests/api/graphql/mutations/notes/create/note_spec.rb'
+- './spec/requests/api/graphql/mutations/notes/destroy_spec.rb'
+- './spec/requests/api/graphql/mutations/notes/reposition_image_diff_note_spec.rb'
+- './spec/requests/api/graphql/mutations/notes/update/image_diff_note_spec.rb'
+- './spec/requests/api/graphql/mutations/notes/update/note_spec.rb'
+- './spec/requests/api/graphql/mutations/packages/cleanup/policy/update_spec.rb'
+- './spec/requests/api/graphql/mutations/packages/destroy_file_spec.rb'
+- './spec/requests/api/graphql/mutations/packages/destroy_files_spec.rb'
+- './spec/requests/api/graphql/mutations/packages/destroy_spec.rb'
+- './spec/requests/api/graphql/mutations/release_asset_links/create_spec.rb'
+- './spec/requests/api/graphql/mutations/release_asset_links/delete_spec.rb'
+- './spec/requests/api/graphql/mutations/release_asset_links/update_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/security/ci_configuration/configure_sast_iac_spec.rb'
+- './spec/requests/api/graphql/mutations/security/ci_configuration/configure_secret_detection_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/mark_as_spam_spec.rb'
+- './spec/requests/api/graphql/mutations/snippets/update_spec.rb'
+- './spec/requests/api/graphql/mutations/timelogs/create_spec.rb'
+- './spec/requests/api/graphql/mutations/timelogs/delete_spec.rb'
+- './spec/requests/api/graphql/mutations/todos/create_spec.rb'
+- './spec/requests/api/graphql/mutations/todos/mark_all_done_spec.rb'
+- './spec/requests/api/graphql/mutations/todos/mark_done_spec.rb'
+- './spec/requests/api/graphql/mutations/todos/restore_many_spec.rb'
+- './spec/requests/api/graphql/mutations/todos/restore_spec.rb'
+- './spec/requests/api/graphql/mutations/uploads/delete_spec.rb'
+- './spec/requests/api/graphql/mutations/user_callouts/create_spec.rb'
+- './spec/requests/api/graphql/mutations/user_preferences/update_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/graphql/mutations/work_items/update_widgets_spec.rb'
+- './spec/requests/api/graphql/namespace/package_settings_spec.rb'
+- './spec/requests/api/graphql/namespace/projects_spec.rb'
+- './spec/requests/api/graphql/namespace_query_spec.rb'
+- './spec/requests/api/graphql/namespace/root_storage_statistics_spec.rb'
+- './spec/requests/api/graphql/packages/composer_spec.rb'
+- './spec/requests/api/graphql/packages/conan_spec.rb'
+- './spec/requests/api/graphql/packages/helm_spec.rb'
+- './spec/requests/api/graphql/packages/maven_spec.rb'
+- './spec/requests/api/graphql/packages/nuget_spec.rb'
+- './spec/requests/api/graphql/packages/package_spec.rb'
+- './spec/requests/api/graphql/packages/pypi_spec.rb'
+- './spec/requests/api/graphql/project/alert_management/alert/assignees_spec.rb'
+- './spec/requests/api/graphql/project/alert_management/alert/issue_spec.rb'
+- './spec/requests/api/graphql/project/alert_management/alert/metrics_dashboard_url_spec.rb'
+- './spec/requests/api/graphql/project/alert_management/alert/notes_spec.rb'
+- './spec/requests/api/graphql/project/alert_management/alerts_spec.rb'
+- './spec/requests/api/graphql/project/alert_management/alert_status_counts_spec.rb'
+- './spec/requests/api/graphql/project/alert_management/alert/todos_spec.rb'
+- './spec/requests/api/graphql/project/alert_management/integrations_spec.rb'
+- './spec/requests/api/graphql/project/base_service_spec.rb'
+- './spec/requests/api/graphql/project/cluster_agents_spec.rb'
+- './spec/requests/api/graphql/project/container_expiration_policy_spec.rb'
+- './spec/requests/api/graphql/project/container_repositories_spec.rb'
+- './spec/requests/api/graphql/project/error_tracking/sentry_detailed_error_request_spec.rb'
+- './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'
+- './spec/requests/api/graphql/project/issue/designs/notes_spec.rb'
+- './spec/requests/api/graphql/project/issue/notes_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/jira_projects_spec.rb'
+- './spec/requests/api/graphql/project/jira_service_spec.rb'
+- './spec/requests/api/graphql/project/jobs_spec.rb'
+- './spec/requests/api/graphql/project/labels_query_spec.rb'
+- './spec/requests/api/graphql/project/merge_request/diff_notes_spec.rb'
+- './spec/requests/api/graphql/project/merge_request/pipelines_spec.rb'
+- './spec/requests/api/graphql/project/merge_request_spec.rb'
+- './spec/requests/api/graphql/project/merge_requests_spec.rb'
+- './spec/requests/api/graphql/project/milestones_spec.rb'
+- './spec/requests/api/graphql/project/packages_cleanup_policy_spec.rb'
+- './spec/requests/api/graphql/project/packages_spec.rb'
+- './spec/requests/api/graphql/project/pipeline_spec.rb'
+- './spec/requests/api/graphql/project/project_members_spec.rb'
+- './spec/requests/api/graphql/project/project_pipeline_statistics_spec.rb'
+- './spec/requests/api/graphql/project/project_statistics_spec.rb'
+- './spec/requests/api/graphql/project_query_spec.rb'
+- './spec/requests/api/graphql/project/recent_issue_boards_query_spec.rb'
+- './spec/requests/api/graphql/project/release_spec.rb'
+- './spec/requests/api/graphql/project/releases_spec.rb'
+- './spec/requests/api/graphql/project/repository/blobs_spec.rb'
+- './spec/requests/api/graphql/project/repository_spec.rb'
+- './spec/requests/api/graphql/project/terraform/state_spec.rb'
+- './spec/requests/api/graphql/project/terraform/states_spec.rb'
+- './spec/requests/api/graphql/project/tree/tree_spec.rb'
+- './spec/requests/api/graphql/project/work_items_spec.rb'
+- './spec/requests/api/graphql/project/work_item_types_spec.rb'
+- './spec/requests/api/graphql/query_spec.rb'
+- './spec/requests/api/graphql/read_only_spec.rb'
+- './spec/requests/api/graphql/snippets_spec.rb'
+- './spec/requests/api/graphql_spec.rb'
+- './spec/requests/api/graphql/tasks/task_completion_status_spec.rb'
+- './spec/requests/api/graphql/terraform/state/delete_spec.rb'
+- './spec/requests/api/graphql/terraform/state/lock_spec.rb'
+- './spec/requests/api/graphql/terraform/state/unlock_spec.rb'
+- './spec/requests/api/graphql/todo_query_spec.rb'
+- './spec/requests/api/graphql/usage_trends_measurements_spec.rb'
+- './spec/requests/api/graphql/user/group_member_query_spec.rb'
+- './spec/requests/api/graphql/user/project_member_query_spec.rb'
+- './spec/requests/api/graphql/user_query_spec.rb'
+- './spec/requests/api/graphql/user_spec.rb'
+- './spec/requests/api/graphql/users_spec.rb'
+- './spec/requests/api/graphql/user/starred_projects_query_spec.rb'
+- './spec/requests/api/graphql/work_item_spec.rb'
+- './spec/requests/api/group_avatar_spec.rb'
+- './spec/requests/api/group_boards_spec.rb'
+- './spec/requests/api/group_clusters_spec.rb'
+- './spec/requests/api/group_container_repositories_spec.rb'
+- './spec/requests/api/group_debian_distributions_spec.rb'
+- './spec/requests/api/group_export_spec.rb'
+- './spec/requests/api/group_import_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/group_variables_spec.rb'
+- './spec/requests/api/helm_packages_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/integrations/jira_connect/subscriptions_spec.rb'
+- './spec/requests/api/integrations/slack/events_spec.rb'
+- './spec/requests/api/integrations_spec.rb'
+- './spec/requests/api/internal/base_spec.rb'
+- './spec/requests/api/internal/container_registry/migration_spec.rb'
+- './spec/requests/api/internal/error_tracking_spec.rb'
+- './spec/requests/api/internal/kubernetes_spec.rb'
+- './spec/requests/api/internal/lfs_spec.rb'
+- './spec/requests/api/internal/mail_room_spec.rb'
+- './spec/requests/api/internal/pages_spec.rb'
+- './spec/requests/api/internal/workhorse_spec.rb'
+- './spec/requests/api/invitations_spec.rb'
+- './spec/requests/api/issue_links_spec.rb'
+- './spec/requests/api/issues/get_group_issues_spec.rb'
+- './spec/requests/api/issues/get_project_issues_spec.rb'
+- './spec/requests/api/issues/issues_spec.rb'
+- './spec/requests/api/issues/post_projects_issues_spec.rb'
+- './spec/requests/api/issues/put_projects_issues_spec.rb'
+- './spec/requests/api/keys_spec.rb'
+- './spec/requests/api/labels_spec.rb'
+- './spec/requests/api/lint_spec.rb'
+- './spec/requests/api/markdown_golden_master_spec.rb'
+- './spec/requests/api/markdown_snapshot_spec.rb'
+- './spec/requests/api/markdown_spec.rb'
+- './spec/requests/api/maven_packages_spec.rb'
+- './spec/requests/api/members_spec.rb'
+- './spec/requests/api/merge_request_approvals_spec.rb'
+- './spec/requests/api/merge_request_diffs_spec.rb'
+- './spec/requests/api/merge_requests_spec.rb'
+- './spec/requests/api/metadata_spec.rb'
+- './spec/requests/api/metrics/dashboard/annotations_spec.rb'
+- './spec/requests/api/metrics/user_starred_dashboards_spec.rb'
+- './spec/requests/api/namespaces_spec.rb'
+- './spec/requests/api/notes_spec.rb'
+- './spec/requests/api/notification_settings_spec.rb'
+- './spec/requests/api/npm_instance_packages_spec.rb'
+- './spec/requests/api/npm_project_packages_spec.rb'
+- './spec/requests/api/nuget_group_packages_spec.rb'
+- './spec/requests/api/nuget_project_packages_spec.rb'
+- './spec/requests/api/oauth_tokens_spec.rb'
+- './spec/requests/api/package_files_spec.rb'
+- './spec/requests/api/pages_domains_spec.rb'
+- './spec/requests/api/pages/internal_access_spec.rb'
+- './spec/requests/api/pages/pages_spec.rb'
+- './spec/requests/api/pages/private_access_spec.rb'
+- './spec/requests/api/pages/public_access_spec.rb'
+- './spec/requests/api/performance_bar_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_debian_distributions_spec.rb'
+- './spec/requests/api/project_events_spec.rb'
+- './spec/requests/api/project_export_spec.rb'
+- './spec/requests/api/project_hooks_spec.rb'
+- './spec/requests/api/project_import_spec.rb'
+- './spec/requests/api/project_milestones_spec.rb'
+- './spec/requests/api/project_packages_spec.rb'
+- './spec/requests/api/project_repository_storage_moves_spec.rb'
+- './spec/requests/api/project_snapshots_spec.rb'
+- './spec/requests/api/project_snippets_spec.rb'
+- './spec/requests/api/projects_spec.rb'
+- './spec/requests/api/project_statistics_spec.rb'
+- './spec/requests/api/project_templates_spec.rb'
+- './spec/requests/api/protected_branches_spec.rb'
+- './spec/requests/api/protected_tags_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/remote_mirrors_spec.rb'
+- './spec/requests/api/repositories_spec.rb'
+- './spec/requests/api/resource_access_tokens_spec.rb'
+- './spec/requests/api/resource_label_events_spec.rb'
+- './spec/requests/api/resource_milestone_events_spec.rb'
+- './spec/requests/api/resource_state_events_spec.rb'
+- './spec/requests/api/rubygem_packages_spec.rb'
+- './spec/requests/api/search_spec.rb'
+- './spec/requests/api/settings_spec.rb'
+- './spec/requests/api/sidekiq_metrics_spec.rb'
+- './spec/requests/api/snippet_repository_storage_moves_spec.rb'
+- './spec/requests/api/snippets_spec.rb'
+- './spec/requests/api/statistics_spec.rb'
+- './spec/requests/api/submodules_spec.rb'
+- './spec/requests/api/suggestions_spec.rb'
+- './spec/requests/api/system_hooks_spec.rb'
+- './spec/requests/api/tags_spec.rb'
+- './spec/requests/api/task_completion_status_spec.rb'
+- './spec/requests/api/templates_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/todos_spec.rb'
+- './spec/requests/api/topics_spec.rb'
+- './spec/requests/api/unleash_spec.rb'
+- './spec/requests/api/usage_data_non_sql_metrics_spec.rb'
+- './spec/requests/api/usage_data_queries_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'
+- './spec/requests/api/version_spec.rb'
+- './spec/requests/api/wikis_spec.rb'
+- './spec/requests/boards/lists_controller_spec.rb'
+- './spec/requests/concerns/planning_hierarchy_spec.rb'
+- './spec/requests/content_security_policy_spec.rb'
+- './spec/requests/dashboard_controller_spec.rb'
+- './spec/requests/dashboard/projects_controller_spec.rb'
+- './spec/requests/git_http_spec.rb'
+- './spec/requests/groups/autocomplete_sources_spec.rb'
+- './spec/requests/groups/clusters/integrations_controller_spec.rb'
+- './spec/requests/groups_controller_spec.rb'
+- './spec/requests/groups/crm/contacts_controller_spec.rb'
+- './spec/requests/groups/crm/organizations_controller_spec.rb'
+- './spec/requests/groups/deploy_tokens_controller_spec.rb'
+- './spec/requests/groups/email_campaigns_controller_spec.rb'
+- './spec/requests/groups/harbor/artifacts_controller_spec.rb'
+- './spec/requests/groups/harbor/repositories_controller_spec.rb'
+- './spec/requests/groups/harbor/tags_controller_spec.rb'
+- './spec/requests/groups/milestones_controller_spec.rb'
+- './spec/requests/groups/registry/repositories_controller_spec.rb'
+- './spec/requests/groups/settings/access_tokens_controller_spec.rb'
+- './spec/requests/groups/settings/applications_controller_spec.rb'
+- './spec/requests/health_controller_spec.rb'
+- './spec/requests/ide_controller_spec.rb'
+- './spec/requests/import/gitlab_groups_controller_spec.rb'
+- './spec/requests/import/gitlab_projects_controller_spec.rb'
+- './spec/requests/import/url_controller_spec.rb'
+- './spec/requests/jira_authorizations_spec.rb'
+- './spec/requests/jira_connect/installations_controller_spec.rb'
+- './spec/requests/jira_connect/oauth_application_ids_controller_spec.rb'
+- './spec/requests/jira_connect/oauth_callbacks_controller_spec.rb'
+- './spec/requests/jira_connect/subscriptions_controller_spec.rb'
+- './spec/requests/jira_connect/users_controller_spec.rb'
+- './spec/requests/jira_routing_spec.rb'
+- './spec/requests/jwks_controller_spec.rb'
+- './spec/requests/jwt_controller_spec.rb'
+- './spec/requests/lfs_http_spec.rb'
+- './spec/requests/lfs_locks_api_spec.rb'
+- './spec/requests/mailgun/webhooks_controller_spec.rb'
+- './spec/requests/oauth/applications_controller_spec.rb'
+- './spec/requests/oauth/authorizations_controller_spec.rb'
+- './spec/requests/oauth/tokens_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/profiles/notifications_controller_spec.rb'
+- './spec/requests/projects/ci/promeheus_metrics/histograms_controller_spec.rb'
+- './spec/requests/projects/cluster_agents_controller_spec.rb'
+- './spec/requests/projects/clusters/integrations_controller_spec.rb'
+- './spec/requests/projects/commits_controller_spec.rb'
+- './spec/requests/projects_controller_spec.rb'
+- './spec/requests/projects/cycle_analytics_events_spec.rb'
+- './spec/requests/projects/environments_controller_spec.rb'
+- './spec/requests/projects/google_cloud/configuration_controller_spec.rb'
+- './spec/requests/projects/google_cloud/databases_controller_spec.rb'
+- './spec/requests/projects/google_cloud/deployments_controller_spec.rb'
+- './spec/requests/projects/google_cloud/gcp_regions_controller_spec.rb'
+- './spec/requests/projects/google_cloud/revoke_oauth_controller_spec.rb'
+- './spec/requests/projects/google_cloud/service_accounts_controller_spec.rb'
+- './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'
+- './spec/requests/projects/issues/discussions_spec.rb'
+- './spec/requests/projects/merge_requests/content_spec.rb'
+- './spec/requests/projects/merge_requests/context_commit_diffs_spec.rb'
+- './spec/requests/projects/merge_requests_controller_spec.rb'
+- './spec/requests/projects/merge_requests/creations_spec.rb'
+- './spec/requests/projects/merge_requests/diffs_spec.rb'
+- './spec/requests/projects/merge_requests_discussions_spec.rb'
+- './spec/requests/projects/merge_requests_spec.rb'
+- './spec/requests/projects/metrics/dashboards/builder_spec.rb'
+- './spec/requests/projects/metrics_dashboard_spec.rb'
+- './spec/requests/projects/noteable_notes_spec.rb'
+- './spec/requests/projects/pipelines_controller_spec.rb'
+- './spec/requests/projects/redirect_controller_spec.rb'
+- './spec/requests/projects/releases_controller_spec.rb'
+- './spec/requests/projects/settings/access_tokens_controller_spec.rb'
+- './spec/requests/projects/settings/packages_and_registries_controller_spec.rb'
+- './spec/requests/projects/tags_controller_spec.rb'
+- './spec/requests/projects/uploads_spec.rb'
+- './spec/requests/projects/usage_quotas_spec.rb'
+- './spec/requests/projects/work_items_spec.rb'
+- './spec/requests/pwa_controller_spec.rb'
+- './spec/requests/rack_attack_global_spec.rb'
+- './spec/requests/recursive_webhook_detection_spec.rb'
+- './spec/requests/robots_txt_spec.rb'
+- './spec/requests/runner_setup_controller_spec.rb'
+- './spec/requests/sandbox_controller_spec.rb'
+- './spec/requests/search_controller_spec.rb'
+- './spec/requests/self_monitoring_project_spec.rb'
+- './spec/requests/sessions_spec.rb'
+- './spec/requests/terraform/services_controller_spec.rb'
+- './spec/requests/user_activity_spec.rb'
+- './spec/requests/user_avatar_spec.rb'
+- './spec/requests/users_controller_spec.rb'
+- './spec/requests/user_sends_malformed_strings_spec.rb'
+- './spec/requests/users/group_callouts_spec.rb'
+- './spec/requests/users/namespace_callouts_spec.rb'
+- './spec/requests/user_spoofs_ip_spec.rb'
+- './spec/requests/users/project_callouts_spec.rb'
+- './spec/requests/verifies_with_email_spec.rb'
+- './spec/requests/whats_new_controller_spec.rb'
+- './spec/routing/admin_routing_spec.rb'
+- './spec/routing/environments_spec.rb'
+- './spec/routing/git_http_routing_spec.rb'
+- './spec/routing/group_routing_spec.rb'
+- './spec/routing/import_routing_spec.rb'
+- './spec/routing/notifications_routing_spec.rb'
+- './spec/routing/openid_connect_spec.rb'
+- './spec/routing/project_routing_spec.rb'
+- './spec/routing/projects/security/configuration_controller_routing_spec.rb'
+- './spec/routing/routing_spec.rb'
+- './spec/routing/uploads_routing_spec.rb'
+- './spec/scripts/changed-feature-flags_spec.rb'
+- './spec/scripts/determine-qa-tests_spec.rb'
+- './spec/scripts/failed_tests_spec.rb'
+- './spec/scripts/lib/glfm/parse_examples_spec.rb'
+- './spec/scripts/lib/glfm/shared_spec.rb'
+- './spec/scripts/lib/glfm/update_example_snapshots_spec.rb'
+- './spec/scripts/lib/glfm/update_specification_spec.rb'
+- './spec/scripts/pipeline_test_report_builder_spec.rb'
+- './spec/scripts/setup/find_jh_branch_spec.rb'
+- './spec/scripts/trigger-build_spec.rb'
+- './spec/serializers/accessibility_error_entity_spec.rb'
+- './spec/serializers/accessibility_reports_comparer_entity_spec.rb'
+- './spec/serializers/accessibility_reports_comparer_serializer_spec.rb'
+- './spec/serializers/admin/user_entity_spec.rb'
+- './spec/serializers/admin/user_serializer_spec.rb'
+- './spec/serializers/analytics_build_entity_spec.rb'
+- './spec/serializers/analytics_build_serializer_spec.rb'
+- './spec/serializers/analytics/cycle_analytics/stage_entity_spec.rb'
+- './spec/serializers/analytics_issue_entity_spec.rb'
+- './spec/serializers/analytics_issue_serializer_spec.rb'
+- './spec/serializers/analytics_merge_request_serializer_spec.rb'
+- './spec/serializers/analytics_summary_serializer_spec.rb'
+- './spec/serializers/base_discussion_entity_spec.rb'
+- './spec/serializers/blob_entity_spec.rb'
+- './spec/serializers/board_serializer_spec.rb'
+- './spec/serializers/board_simple_entity_spec.rb'
+- './spec/serializers/build_action_entity_spec.rb'
+- './spec/serializers/build_artifact_entity_spec.rb'
+- './spec/serializers/build_details_entity_spec.rb'
+- './spec/serializers/build_trace_entity_spec.rb'
+- './spec/serializers/ci/codequality_mr_diff_entity_spec.rb'
+- './spec/serializers/ci/codequality_mr_diff_report_serializer_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_entity_spec.rb'
+- './spec/serializers/ci/daily_build_group_report_result_serializer_spec.rb'
+- './spec/serializers/ci/downloadable_artifact_entity_spec.rb'
+- './spec/serializers/ci/downloadable_artifact_serializer_spec.rb'
+- './spec/serializers/ci/group_variable_entity_spec.rb'
+- './spec/serializers/ci/job_entity_spec.rb'
+- './spec/serializers/ci/job_serializer_spec.rb'
+- './spec/serializers/ci/lint/job_entity_spec.rb'
+- './spec/serializers/ci/lint/result_entity_spec.rb'
+- './spec/serializers/ci/lint/result_serializer_spec.rb'
+- './spec/serializers/ci/pipeline_entity_spec.rb'
+- './spec/serializers/ci/trigger_entity_spec.rb'
+- './spec/serializers/ci/trigger_serializer_spec.rb'
+- './spec/serializers/ci/variable_entity_spec.rb'
+- './spec/serializers/cluster_application_entity_spec.rb'
+- './spec/serializers/cluster_entity_spec.rb'
+- './spec/serializers/cluster_serializer_spec.rb'
+- './spec/serializers/clusters/kubernetes_error_entity_spec.rb'
+- './spec/serializers/codequality_degradation_entity_spec.rb'
+- './spec/serializers/codequality_reports_comparer_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/context_commits_diff_entity_spec.rb'
+- './spec/serializers/deploy_keys/basic_deploy_key_entity_spec.rb'
+- './spec/serializers/deploy_keys/deploy_key_entity_spec.rb'
+- './spec/serializers/deployment_cluster_entity_spec.rb'
+- './spec/serializers/deployment_entity_spec.rb'
+- './spec/serializers/deployment_serializer_spec.rb'
+- './spec/serializers/detailed_status_entity_spec.rb'
+- './spec/serializers/diff_file_base_entity_spec.rb'
+- './spec/serializers/diff_file_entity_spec.rb'
+- './spec/serializers/diff_file_metadata_entity_spec.rb'
+- './spec/serializers/diff_line_entity_spec.rb'
+- './spec/serializers/diff_line_serializer_spec.rb'
+- './spec/serializers/diffs_entity_spec.rb'
+- './spec/serializers/diffs_metadata_entity_spec.rb'
+- './spec/serializers/diff_viewer_entity_spec.rb'
+- './spec/serializers/discussion_diff_file_entity_spec.rb'
+- './spec/serializers/discussion_entity_spec.rb'
+- './spec/serializers/entity_date_helper_spec.rb'
+- './spec/serializers/entity_request_spec.rb'
+- './spec/serializers/environment_entity_spec.rb'
+- './spec/serializers/environment_serializer_spec.rb'
+- './spec/serializers/environment_status_entity_spec.rb'
+- './spec/serializers/evidences/evidence_entity_spec.rb'
+- './spec/serializers/evidences/evidence_serializer_spec.rb'
+- './spec/serializers/evidences/issue_entity_spec.rb'
+- './spec/serializers/evidences/milestone_entity_spec.rb'
+- './spec/serializers/evidences/project_entity_spec.rb'
+- './spec/serializers/evidences/release_entity_spec.rb'
+- './spec/serializers/evidences/release_serializer_spec.rb'
+- './spec/serializers/feature_flag_entity_spec.rb'
+- './spec/serializers/feature_flags_client_serializer_spec.rb'
+- './spec/serializers/feature_flag_serializer_spec.rb'
+- './spec/serializers/feature_flag_summary_entity_spec.rb'
+- './spec/serializers/feature_flag_summary_serializer_spec.rb'
+- './spec/serializers/fork_namespace_entity_spec.rb'
+- './spec/serializers/fork_namespace_serializer_spec.rb'
+- './spec/serializers/group_access_token_entity_spec.rb'
+- './spec/serializers/group_access_token_serializer_spec.rb'
+- './spec/serializers/group_child_entity_spec.rb'
+- './spec/serializers/group_child_serializer_spec.rb'
+- './spec/serializers/group_deploy_key_entity_spec.rb'
+- './spec/serializers/group_issuable_autocomplete_entity_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/import/bitbucket_provider_repo_entity_spec.rb'
+- './spec/serializers/import/bitbucket_server_provider_repo_entity_spec.rb'
+- './spec/serializers/import/bulk_import_entity_spec.rb'
+- './spec/serializers/import/fogbugz_provider_repo_entity_spec.rb'
+- './spec/serializers/import/githubish_provider_repo_entity_spec.rb'
+- './spec/serializers/import/gitlab_provider_repo_entity_spec.rb'
+- './spec/serializers/import/manifest_provider_repo_entity_spec.rb'
+- './spec/serializers/import/provider_repo_serializer_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/artifact_serializer_spec.rb'
+- './spec/serializers/integrations/harbor_serializers/repository_entity_spec.rb'
+- './spec/serializers/integrations/harbor_serializers/repository_serializer_spec.rb'
+- './spec/serializers/integrations/harbor_serializers/tag_entity_spec.rb'
+- './spec/serializers/integrations/harbor_serializers/tag_serializer_spec.rb'
+- './spec/serializers/integrations/project_entity_spec.rb'
+- './spec/serializers/integrations/project_serializer_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/issue_serializer_spec.rb'
+- './spec/serializers/issue_sidebar_basic_entity_spec.rb'
+- './spec/serializers/jira_connect/app_data_serializer_spec.rb'
+- './spec/serializers/jira_connect/group_entity_spec.rb'
+- './spec/serializers/jira_connect/subscription_entity_spec.rb'
+- './spec/serializers/job_artifact_report_entity_spec.rb'
+- './spec/serializers/label_serializer_spec.rb'
+- './spec/serializers/lfs_file_lock_entity_spec.rb'
+- './spec/serializers/linked_project_issue_entity_spec.rb'
+- './spec/serializers/member_entity_spec.rb'
+- './spec/serializers/member_serializer_spec.rb'
+- './spec/serializers/member_user_entity_spec.rb'
+- './spec/serializers/merge_request_basic_entity_spec.rb'
+- './spec/serializers/merge_request_current_user_entity_spec.rb'
+- './spec/serializers/merge_request_diff_entity_spec.rb'
+- './spec/serializers/merge_request_for_pipeline_entity_spec.rb'
+- './spec/serializers/merge_request_metrics_helper_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_serializer_spec.rb'
+- './spec/serializers/merge_request_sidebar_basic_entity_spec.rb'
+- './spec/serializers/merge_request_sidebar_extras_entity_spec.rb'
+- './spec/serializers/merge_requests/pipeline_entity_spec.rb'
+- './spec/serializers/merge_request_user_entity_spec.rb'
+- './spec/serializers/merge_request_widget_commit_entity_spec.rb'
+- './spec/serializers/merge_request_widget_entity_spec.rb'
+- './spec/serializers/move_to_project_entity_spec.rb'
+- './spec/serializers/move_to_project_serializer_spec.rb'
+- './spec/serializers/namespace_basic_entity_spec.rb'
+- './spec/serializers/namespace_serializer_spec.rb'
+- './spec/serializers/note_entity_spec.rb'
+- './spec/serializers/paginated_diff_entity_spec.rb'
+- './spec/serializers/personal_access_token_entity_spec.rb'
+- './spec/serializers/personal_access_token_serializer_spec.rb'
+- './spec/serializers/pipeline_details_entity_spec.rb'
+- './spec/serializers/pipeline_serializer_spec.rb'
+- './spec/serializers/project_access_token_entity_spec.rb'
+- './spec/serializers/project_access_token_serializer_spec.rb'
+- './spec/serializers/project_import_entity_spec.rb'
+- './spec/serializers/project_mirror_entity_spec.rb'
+- './spec/serializers/project_mirror_serializer_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/remote_mirror_entity_spec.rb'
+- './spec/serializers/request_aware_entity_spec.rb'
+- './spec/serializers/review_app_setup_entity_spec.rb'
+- './spec/serializers/rollout_status_entity_spec.rb'
+- './spec/serializers/rollout_statuses/ingress_entity_spec.rb'
+- './spec/serializers/runner_entity_spec.rb'
+- './spec/serializers/serverless/domain_entity_spec.rb'
+- './spec/serializers/stage_entity_spec.rb'
+- './spec/serializers/stage_serializer_spec.rb'
+- './spec/serializers/suggestion_entity_spec.rb'
+- './spec/serializers/test_case_entity_spec.rb'
+- './spec/serializers/test_report_entity_spec.rb'
+- './spec/serializers/test_reports_comparer_entity_spec.rb'
+- './spec/serializers/test_reports_comparer_serializer_spec.rb'
+- './spec/serializers/test_report_summary_entity_spec.rb'
+- './spec/serializers/test_suite_comparer_entity_spec.rb'
+- './spec/serializers/test_suite_entity_spec.rb'
+- './spec/serializers/test_suite_summary_entity_spec.rb'
+- './spec/serializers/trigger_variable_entity_spec.rb'
+- './spec/serializers/user_entity_spec.rb'
+- './spec/serializers/user_serializer_spec.rb'
+- './spec/serializers/web_ide_terminal_entity_spec.rb'
+- './spec/serializers/web_ide_terminal_serializer_spec.rb'
+- './spec/services/access_token_validation_service_spec.rb'
+- './spec/services/alert_management/alerts/todo/create_service_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/http_integrations/create_service_spec.rb'
+- './spec/services/alert_management/http_integrations/destroy_service_spec.rb'
+- './spec/services/alert_management/http_integrations/update_service_spec.rb'
+- './spec/services/alert_management/metric_images/upload_service_spec.rb'
+- './spec/services/alert_management/process_prometheus_alert_service_spec.rb'
+- './spec/services/analytics/cycle_analytics/stages/list_service_spec.rb'
+- './spec/services/applications/create_service_spec.rb'
+- './spec/services/application_settings/update_service_spec.rb'
+- './spec/services/audit_events/build_service_spec.rb'
+- './spec/services/audit_event_service_spec.rb'
+- './spec/services/auth/container_registry_authentication_service_spec.rb'
+- './spec/services/auth/dependency_proxy_authentication_service_spec.rb'
+- './spec/services/authorized_project_update/find_records_due_for_refresh_service_spec.rb'
+- './spec/services/authorized_project_update/periodic_recalculate_service_spec.rb'
+- './spec/services/authorized_project_update/project_access_changed_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/auto_merge/base_service_spec.rb'
+- './spec/services/auto_merge/merge_when_pipeline_succeeds_service_spec.rb'
+- './spec/services/auto_merge_service_spec.rb'
+- './spec/services/award_emojis/add_service_spec.rb'
+- './spec/services/award_emojis/base_service_spec.rb'
+- './spec/services/award_emojis/collect_user_emoji_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/base_container_service_spec.rb'
+- './spec/services/base_count_service_spec.rb'
+- './spec/services/boards/create_service_spec.rb'
+- './spec/services/boards/destroy_service_spec.rb'
+- './spec/services/boards/issues/create_service_spec.rb'
+- './spec/services/boards/issues/list_service_spec.rb'
+- './spec/services/boards/issues/move_service_spec.rb'
+- './spec/services/boards/lists/create_service_spec.rb'
+- './spec/services/boards/lists/destroy_service_spec.rb'
+- './spec/services/boards/lists/generate_service_spec.rb'
+- './spec/services/boards/lists/list_service_spec.rb'
+- './spec/services/boards/lists/move_service_spec.rb'
+- './spec/services/boards/lists/update_service_spec.rb'
+- './spec/services/boards/visits/create_service_spec.rb'
+- './spec/services/branches/create_service_spec.rb'
+- './spec/services/branches/delete_merged_service_spec.rb'
+- './spec/services/branches/delete_service_spec.rb'
+- './spec/services/branches/diverging_commit_counts_service_spec.rb'
+- './spec/services/branches/validate_new_service_spec.rb'
+- './spec/services/bulk_create_integration_service_spec.rb'
+- './spec/services/bulk_imports/archive_extraction_service_spec.rb'
+- './spec/services/bulk_imports/create_pipeline_trackers_service_spec.rb'
+- './spec/services/bulk_imports/create_service_spec.rb'
+- './spec/services/bulk_imports/export_service_spec.rb'
+- './spec/services/bulk_imports/file_decompression_service_spec.rb'
+- './spec/services/bulk_imports/file_download_service_spec.rb'
+- './spec/services/bulk_imports/file_export_service_spec.rb'
+- './spec/services/bulk_imports/get_importable_data_service_spec.rb'
+- './spec/services/bulk_imports/lfs_objects_export_service_spec.rb'
+- './spec/services/bulk_imports/relation_export_service_spec.rb'
+- './spec/services/bulk_imports/repository_bundle_export_service_spec.rb'
+- './spec/services/bulk_imports/tree_export_service_spec.rb'
+- './spec/services/bulk_imports/uploads_export_service_spec.rb'
+- './spec/services/bulk_push_event_payload_service_spec.rb'
+- './spec/services/bulk_update_integration_service_spec.rb'
+- './spec/services/captcha/captcha_verification_service_spec.rb'
+- './spec/services/chat_names/authorize_user_service_spec.rb'
+- './spec/services/chat_names/find_user_service_spec.rb'
+- './spec/services/ci/abort_pipelines_service_spec.rb'
+- './spec/services/ci/after_requeue_job_service_spec.rb'
+- './spec/services/ci/append_build_trace_service_spec.rb'
+- './spec/services/ci/archive_trace_service_spec.rb'
+- './spec/services/ci/build_cancel_service_spec.rb'
+- './spec/services/ci/build_report_result_service_spec.rb'
+- './spec/services/ci/build_unschedule_service_spec.rb'
+- './spec/services/ci/change_variable_service_spec.rb'
+- './spec/services/ci/change_variables_service_spec.rb'
+- './spec/services/ci/compare_accessibility_reports_service_spec.rb'
+- './spec/services/ci/compare_codequality_reports_service_spec.rb'
+- './spec/services/ci/compare_reports_base_service_spec.rb'
+- './spec/services/ci/compare_test_reports_service_spec.rb'
+- './spec/services/ci/copy_cross_database_associations_service_spec.rb'
+- './spec/services/ci/create_downstream_pipeline_service_spec.rb'
+- './spec/services/ci/create_pipeline_service/artifacts_spec.rb'
+- './spec/services/ci/create_pipeline_service/cache_spec.rb'
+- './spec/services/ci/create_pipeline_service/creation_errors_and_warnings_spec.rb'
+- './spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb'
+- './spec/services/ci/create_pipeline_service/custom_config_content_spec.rb'
+- './spec/services/ci/create_pipeline_service/custom_yaml_tags_spec.rb'
+- './spec/services/ci/create_pipeline_service/dry_run_spec.rb'
+- './spec/services/ci/create_pipeline_service/environment_spec.rb'
+- './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/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'
+- './spec/services/ci/create_pipeline_service/parameter_content_spec.rb'
+- './spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb'
+- './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_spec.rb'
+- './spec/services/ci/create_pipeline_service/tags_spec.rb'
+- './spec/services/ci/create_web_ide_terminal_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/delete_unit_tests_service_spec.rb'
+- './spec/services/ci/deployments/destroy_service_spec.rb'
+- './spec/services/ci/destroy_pipeline_service_spec.rb'
+- './spec/services/ci/destroy_secure_file_service_spec.rb'
+- './spec/services/ci/disable_user_pipeline_schedules_service_spec.rb'
+- './spec/services/ci/drop_pipeline_service_spec.rb'
+- './spec/services/ci/ensure_stage_service_spec.rb'
+- './spec/services/ci/expire_pipeline_cache_service_spec.rb'
+- './spec/services/ci/external_pull_requests/create_pipeline_service_spec.rb'
+- './spec/services/ci/find_exposed_artifacts_service_spec.rb'
+- './spec/services/ci/generate_codequality_mr_diff_report_service_spec.rb'
+- './spec/services/ci/generate_coverage_reports_service_spec.rb'
+- './spec/services/ci/generate_kubeconfig_service_spec.rb'
+- './spec/services/ci/generate_terraform_reports_service_spec.rb'
+- './spec/services/ci/job_artifacts/create_service_spec.rb'
+- './spec/services/ci/job_artifacts/delete_project_artifacts_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/expire_project_build_artifacts_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/list_config_variables_service_spec.rb'
+- './spec/services/ci/parse_dotenv_artifact_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_bridge_status_service_spec.rb'
+- './spec/services/ci/pipeline_creation/start_pipeline_service_spec.rb'
+- './spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb'
+- './spec/services/ci/pipeline_processing/atomic_processing_service/status_collection_spec.rb'
+- './spec/services/ci/pipelines/add_job_service_spec.rb'
+- './spec/services/ci/pipeline_schedule_service_spec.rb'
+- './spec/services/ci/pipelines/hook_service_spec.rb'
+- './spec/services/ci/pipeline_trigger_service_spec.rb'
+- './spec/services/ci/play_bridge_service_spec.rb'
+- './spec/services/ci/play_build_service_spec.rb'
+- './spec/services/ci/play_manual_stage_service_spec.rb'
+- './spec/services/ci/prepare_build_service_spec.rb'
+- './spec/services/ci/process_build_service_spec.rb'
+- './spec/services/ci/process_pipeline_service_spec.rb'
+- './spec/services/ci/process_sync_events_service_spec.rb'
+- './spec/services/ci/prometheus_metrics/observe_histograms_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_job_service_spec.rb'
+- './spec/services/ci/retry_pipeline_service_spec.rb'
+- './spec/services/ci/runners/assign_runner_service_spec.rb'
+- './spec/services/ci/runners/bulk_delete_runners_service_spec.rb'
+- './spec/services/ci/runners/process_runner_version_update_service_spec.rb'
+- './spec/services/ci/runners/reconcile_existing_runner_versions_service_spec.rb'
+- './spec/services/ci/runners/register_runner_service_spec.rb'
+- './spec/services/ci/runners/reset_registration_token_service_spec.rb'
+- './spec/services/ci/runners/unassign_runner_service_spec.rb'
+- './spec/services/ci/runners/unregister_runner_service_spec.rb'
+- './spec/services/ci/runners/update_runner_service_spec.rb'
+- './spec/services/ci/run_scheduled_build_service_spec.rb'
+- './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/ci/test_failure_history_service_spec.rb'
+- './spec/services/ci/track_failed_build_service_spec.rb'
+- './spec/services/ci/unlock_artifacts_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/update_pending_build_service_spec.rb'
+- './spec/services/clusters/agents/create_activity_event_service_spec.rb'
+- './spec/services/clusters/agents/create_service_spec.rb'
+- './spec/services/clusters/agents/delete_expired_events_service_spec.rb'
+- './spec/services/clusters/agents/delete_service_spec.rb'
+- './spec/services/clusters/agents/refresh_authorization_service_spec.rb'
+- './spec/services/clusters/agent_tokens/create_service_spec.rb'
+- './spec/services/clusters/agent_tokens/track_usage_service_spec.rb'
+- './spec/services/clusters/applications/check_ingress_ip_address_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/check_upgrade_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/prometheus_config_service_spec.rb'
+- './spec/services/clusters/applications/prometheus_update_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'
+- './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/build_service_spec.rb'
+- './spec/services/clusters/cleanup/project_namespace_service_spec.rb'
+- './spec/services/clusters/cleanup/service_account_service_spec.rb'
+- './spec/services/clusters/create_service_spec.rb'
+- './spec/services/clusters/destroy_service_spec.rb'
+- './spec/services/clusters/gcp/fetch_operation_service_spec.rb'
+- './spec/services/clusters/gcp/finalize_creation_service_spec.rb'
+- './spec/services/clusters/gcp/provision_service_spec.rb'
+- './spec/services/clusters/gcp/verify_provision_status_service_spec.rb'
+- './spec/services/clusters/integrations/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/kubernetes/fetch_kubernetes_token_service_spec.rb'
+- './spec/services/clusters/kubernetes_spec.rb'
+- './spec/services/clusters/management/validate_management_project_permissions_service_spec.rb'
+- './spec/services/clusters/update_service_spec.rb'
+- './spec/services/cohorts_service_spec.rb'
+- './spec/services/commits/cherry_pick_service_spec.rb'
+- './spec/services/commits/commit_patch_service_spec.rb'
+- './spec/services/commits/tag_service_spec.rb'
+- './spec/services/compare_service_spec.rb'
+- './spec/services/concerns/audit_event_save_type_spec.rb'
+- './spec/services/concerns/exclusive_lease_guard_spec.rb'
+- './spec/services/concerns/merge_requests/assigns_merge_params_spec.rb'
+- './spec/services/concerns/rate_limited_service_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/create_service_spec.rb'
+- './spec/services/customer_relations/contacts/update_service_spec.rb'
+- './spec/services/customer_relations/organizations/create_service_spec.rb'
+- './spec/services/customer_relations/organizations/update_service_spec.rb'
+- './spec/services/database/consistency_check_service_spec.rb'
+- './spec/services/database/consistency_fix_service_spec.rb'
+- './spec/services/dependency_proxy/auth_token_service_spec.rb'
+- './spec/services/dependency_proxy/find_cached_manifest_service_spec.rb'
+- './spec/services/dependency_proxy/group_settings/update_service_spec.rb'
+- './spec/services/dependency_proxy/head_manifest_service_spec.rb'
+- './spec/services/dependency_proxy/image_ttl_group_policies/update_service_spec.rb'
+- './spec/services/dependency_proxy/request_token_service_spec.rb'
+- './spec/services/deploy_keys/create_service_spec.rb'
+- './spec/services/deployments/archive_in_project_service_spec.rb'
+- './spec/services/deployments/create_for_build_service_spec.rb'
+- './spec/services/deployments/create_service_spec.rb'
+- './spec/services/deployments/link_merge_requests_service_spec.rb'
+- './spec/services/deployments/older_deployments_drop_service_spec.rb'
+- './spec/services/deployments/update_environment_service_spec.rb'
+- './spec/services/deployments/update_service_spec.rb'
+- './spec/services/design_management/copy_design_collection/copy_service_spec.rb'
+- './spec/services/design_management/copy_design_collection/queue_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/design_management/generate_image_versions_service_spec.rb'
+- './spec/services/design_management/move_designs_service_spec.rb'
+- './spec/services/design_management/save_designs_service_spec.rb'
+- './spec/services/discussions/capture_diff_note_position_service_spec.rb'
+- './spec/services/discussions/capture_diff_note_positions_service_spec.rb'
+- './spec/services/discussions/resolve_service_spec.rb'
+- './spec/services/discussions/unresolve_service_spec.rb'
+- './spec/services/discussions/update_diff_position_service_spec.rb'
+- './spec/services/draft_notes/create_service_spec.rb'
+- './spec/services/draft_notes/destroy_service_spec.rb'
+- './spec/services/draft_notes/publish_service_spec.rb'
+- './spec/services/emails/confirm_service_spec.rb'
+- './spec/services/emails/create_service_spec.rb'
+- './spec/services/emails/destroy_service_spec.rb'
+- './spec/services/environments/auto_stop_service_spec.rb'
+- './spec/services/environments/canary_ingress/update_service_spec.rb'
+- './spec/services/environments/reset_auto_stop_service_spec.rb'
+- './spec/services/environments/schedule_to_delete_review_apps_service_spec.rb'
+- './spec/services/environments/stop_service_spec.rb'
+- './spec/services/error_tracking/base_service_spec.rb'
+- './spec/services/error_tracking/collect_error_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/issue_update_service_spec.rb'
+- './spec/services/error_tracking/list_issues_service_spec.rb'
+- './spec/services/error_tracking/list_projects_service_spec.rb'
+- './spec/services/event_create_service_spec.rb'
+- './spec/services/events/destroy_service_spec.rb'
+- './spec/services/events/render_service_spec.rb'
+- './spec/services/feature_flags/create_service_spec.rb'
+- './spec/services/feature_flags/destroy_service_spec.rb'
+- './spec/services/feature_flags/hook_service_spec.rb'
+- './spec/services/feature_flags/update_service_spec.rb'
+- './spec/services/files/create_service_spec.rb'
+- './spec/services/files/delete_service_spec.rb'
+- './spec/services/files/multi_service_spec.rb'
+- './spec/services/files/update_service_spec.rb'
+- './spec/services/git/base_hooks_service_spec.rb'
+- './spec/services/git/branch_hooks_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/tag_push_service_spec.rb'
+- './spec/services/git/wiki_push_service/change_spec.rb'
+- './spec/services/git/wiki_push_service_spec.rb'
+- './spec/services/google_cloud/create_cloudsql_instance_service_spec.rb'
+- './spec/services/google_cloud/create_service_accounts_service_spec.rb'
+- './spec/services/google_cloud/enable_cloud_run_service_spec.rb'
+- './spec/services/google_cloud/enable_cloudsql_service_spec.rb'
+- './spec/services/google_cloud/gcp_region_add_or_replace_service_spec.rb'
+- './spec/services/google_cloud/generate_pipeline_service_spec.rb'
+- './spec/services/google_cloud/get_cloudsql_instances_service_spec.rb'
+- './spec/services/google_cloud/service_accounts_service_spec.rb'
+- './spec/services/google_cloud/setup_cloudsql_instance_service_spec.rb'
+- './spec/services/gpg_keys/create_service_spec.rb'
+- './spec/services/gpg_keys/destroy_service_spec.rb'
+- './spec/services/grafana/proxy_service_spec.rb'
+- './spec/services/gravatar_service_spec.rb'
+- './spec/services/groups/autocomplete_service_spec.rb'
+- './spec/services/groups/auto_devops_service_spec.rb'
+- './spec/services/groups/create_service_spec.rb'
+- './spec/services/groups/deploy_tokens/create_service_spec.rb'
+- './spec/services/groups/deploy_tokens/destroy_service_spec.rb'
+- './spec/services/groups/deploy_tokens/revoke_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/group_links/update_service_spec.rb'
+- './spec/services/groups/import_export/export_service_spec.rb'
+- './spec/services/groups/import_export/import_service_spec.rb'
+- './spec/services/groups/merge_requests_count_service_spec.rb'
+- './spec/services/groups/nested_create_service_spec.rb'
+- './spec/services/groups/open_issues_count_service_spec.rb'
+- './spec/services/groups/participants_service_spec.rb'
+- './spec/services/groups/transfer_service_spec.rb'
+- './spec/services/groups/update_service_spec.rb'
+- './spec/services/groups/update_shared_runners_service_spec.rb'
+- './spec/services/groups/update_statistics_service_spec.rb'
+- './spec/services/ide/base_config_service_spec.rb'
+- './spec/services/ide/schemas_config_service_spec.rb'
+- './spec/services/ide/terminal_config_service_spec.rb'
+- './spec/services/import/bitbucket_server_service_spec.rb'
+- './spec/services/import_export_clean_up_service_spec.rb'
+- './spec/services/import/fogbugz_service_spec.rb'
+- './spec/services/import/github/notes/create_service_spec.rb'
+- './spec/services/import/github_service_spec.rb'
+- './spec/services/import/gitlab_projects/create_project_service_spec.rb'
+- './spec/services/import/gitlab_projects/file_acquisition_strategies/file_upload_spec.rb'
+- './spec/services/import/gitlab_projects/file_acquisition_strategies/remote_file_s3_spec.rb'
+- './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/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/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'
+- './spec/services/issuable/common_system_notes_service_spec.rb'
+- './spec/services/issuable/destroy_label_links_service_spec.rb'
+- './spec/services/issuable/destroy_service_spec.rb'
+- './spec/services/issuable/process_assignees_spec.rb'
+- './spec/services/issue_links/create_service_spec.rb'
+- './spec/services/issue_links/destroy_service_spec.rb'
+- './spec/services/issue_links/list_service_spec.rb'
+- './spec/services/issues/after_create_service_spec.rb'
+- './spec/services/issues/build_service_spec.rb'
+- './spec/services/issues/clone_service_spec.rb'
+- './spec/services/issues/close_service_spec.rb'
+- './spec/services/issues/create_service_spec.rb'
+- './spec/services/issues/duplicate_service_spec.rb'
+- './spec/services/issues/export_csv_service_spec.rb'
+- './spec/services/issues/import_csv_service_spec.rb'
+- './spec/services/issues/move_service_spec.rb'
+- './spec/services/issues/prepare_import_csv_service_spec.rb'
+- './spec/services/issues/referenced_merge_requests_service_spec.rb'
+- './spec/services/issues/related_branches_service_spec.rb'
+- './spec/services/issues/relative_position_rebalancing_service_spec.rb'
+- './spec/services/issues/reopen_service_spec.rb'
+- './spec/services/issues/reorder_service_spec.rb'
+- './spec/services/issues/resolve_discussions_spec.rb'
+- './spec/services/issues/set_crm_contacts_service_spec.rb'
+- './spec/services/issues/update_service_spec.rb'
+- './spec/services/issues/zoom_link_service_spec.rb'
+- './spec/services/jira_connect_installations/destroy_service_spec.rb'
+- './spec/services/jira_connect_subscriptions/create_service_spec.rb'
+- './spec/services/jira_connect/sync_service_spec.rb'
+- './spec/services/jira_import/cloud_users_mapper_service_spec.rb'
+- './spec/services/jira_import/server_users_mapper_service_spec.rb'
+- './spec/services/jira_import/start_import_service_spec.rb'
+- './spec/services/jira_import/users_importer_spec.rb'
+- './spec/services/jira/requests/projects/list_service_spec.rb'
+- './spec/services/keys/create_service_spec.rb'
+- './spec/services/keys/destroy_service_spec.rb'
+- './spec/services/keys/expiry_notification_service_spec.rb'
+- './spec/services/keys/last_used_service_spec.rb'
+- './spec/services/labels/available_labels_service_spec.rb'
+- './spec/services/labels/create_service_spec.rb'
+- './spec/services/labels/find_or_create_service_spec.rb'
+- './spec/services/labels/promote_service_spec.rb'
+- './spec/services/labels/transfer_service_spec.rb'
+- './spec/services/labels/update_service_spec.rb'
+- './spec/services/lfs/file_transformer_spec.rb'
+- './spec/services/lfs/lock_file_service_spec.rb'
+- './spec/services/lfs/locks_finder_service_spec.rb'
+- './spec/services/lfs/push_service_spec.rb'
+- './spec/services/lfs/unlock_file_service_spec.rb'
+- './spec/services/loose_foreign_keys/batch_cleaner_service_spec.rb'
+- './spec/services/loose_foreign_keys/cleaner_service_spec.rb'
+- './spec/services/markdown_content_rewriter_service_spec.rb'
+- './spec/services/members/approve_access_request_service_spec.rb'
+- './spec/services/members/create_service_spec.rb'
+- './spec/services/members/creator_service_spec.rb'
+- './spec/services/members/destroy_service_spec.rb'
+- './spec/services/members/groups/creator_service_spec.rb'
+- './spec/services/members/import_project_team_service_spec.rb'
+- './spec/services/members/invitation_reminder_email_service_spec.rb'
+- './spec/services/members/invite_member_builder_spec.rb'
+- './spec/services/members/invite_service_spec.rb'
+- './spec/services/members/projects/creator_service_spec.rb'
+- './spec/services/members/request_access_service_spec.rb'
+- './spec/services/members/standard_member_builder_spec.rb'
+- './spec/services/members/unassign_issuables_service_spec.rb'
+- './spec/services/members/update_service_spec.rb'
+- './spec/services/merge_requests/add_context_service_spec.rb'
+- './spec/services/merge_requests/add_spent_time_service_spec.rb'
+- './spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb'
+- './spec/services/merge_requests/after_create_service_spec.rb'
+- './spec/services/merge_requests/approval_service_spec.rb'
+- './spec/services/merge_requests/assign_issues_service_spec.rb'
+- './spec/services/merge_requests/base_service_spec.rb'
+- './spec/services/merge_requests/build_service_spec.rb'
+- './spec/services/merge_requests/cleanup_refs_service_spec.rb'
+- './spec/services/merge_requests/close_service_spec.rb'
+- './spec/services/merge_requests/conflicts/list_service_spec.rb'
+- './spec/services/merge_requests/conflicts/resolve_service_spec.rb'
+- './spec/services/merge_requests/create_approval_event_service_spec.rb'
+- './spec/services/merge_requests/create_from_issue_service_spec.rb'
+- './spec/services/merge_requests/create_pipeline_service_spec.rb'
+- './spec/services/merge_requests/create_service_spec.rb'
+- './spec/services/merge_requests/delete_non_latest_diffs_service_spec.rb'
+- './spec/services/merge_requests/execute_approval_hooks_service_spec.rb'
+- './spec/services/merge_requests/export_csv_service_spec.rb'
+- './spec/services/merge_requests/ff_merge_service_spec.rb'
+- './spec/services/merge_requests/get_urls_service_spec.rb'
+- './spec/services/merge_requests/handle_assignees_change_service_spec.rb'
+- './spec/services/merge_requests/link_lfs_objects_service_spec.rb'
+- './spec/services/merge_requests/mark_reviewer_reviewed_service_spec.rb'
+- './spec/services/merge_requests/mergeability/check_base_service_spec.rb'
+- './spec/services/merge_requests/mergeability/check_broken_status_service_spec.rb'
+- './spec/services/merge_requests/mergeability/check_ci_status_service_spec.rb'
+- './spec/services/merge_requests/mergeability/check_discussions_status_service_spec.rb'
+- './spec/services/merge_requests/mergeability/check_draft_status_service_spec.rb'
+- './spec/services/merge_requests/mergeability/check_open_status_service_spec.rb'
+- './spec/services/merge_requests/mergeability_check_service_spec.rb'
+- './spec/services/merge_requests/mergeability/run_checks_service_spec.rb'
+- './spec/services/merge_requests/merge_orchestration_service_spec.rb'
+- './spec/services/merge_requests/merge_service_spec.rb'
+- './spec/services/merge_requests/merge_to_ref_service_spec.rb'
+- './spec/services/merge_requests/migrate_external_diffs_service_spec.rb'
+- './spec/services/merge_requests/post_merge_service_spec.rb'
+- './spec/services/merge_requests/pushed_branches_service_spec.rb'
+- './spec/services/merge_requests/push_options_handler_service_spec.rb'
+- './spec/services/merge_requests/rebase_service_spec.rb'
+- './spec/services/merge_requests/refresh_service_spec.rb'
+- './spec/services/merge_requests/reload_diffs_service_spec.rb'
+- './spec/services/merge_requests/reload_merge_head_diff_service_spec.rb'
+- './spec/services/merge_requests/remove_approval_service_spec.rb'
+- './spec/services/merge_requests/reopen_service_spec.rb'
+- './spec/services/merge_requests/request_review_service_spec.rb'
+- './spec/services/merge_requests/resolved_discussion_notification_service_spec.rb'
+- './spec/services/merge_requests/resolve_todos_service_spec.rb'
+- './spec/services/merge_requests/retarget_chain_service_spec.rb'
+- './spec/services/merge_requests/squash_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/metrics/dashboard/annotations/create_service_spec.rb'
+- './spec/services/metrics/dashboard/annotations/delete_service_spec.rb'
+- './spec/services/metrics/dashboard/clone_dashboard_service_spec.rb'
+- './spec/services/metrics/dashboard/cluster_dashboard_service_spec.rb'
+- './spec/services/metrics/dashboard/cluster_metrics_embed_service_spec.rb'
+- './spec/services/metrics/dashboard/custom_dashboard_service_spec.rb'
+- './spec/services/metrics/dashboard/custom_metric_embed_service_spec.rb'
+- './spec/services/metrics/dashboard/default_embed_service_spec.rb'
+- './spec/services/metrics/dashboard/dynamic_embed_service_spec.rb'
+- './spec/services/metrics/dashboard/gitlab_alert_embed_service_spec.rb'
+- './spec/services/metrics/dashboard/grafana_metric_embed_service_spec.rb'
+- './spec/services/metrics/dashboard/panel_preview_service_spec.rb'
+- './spec/services/metrics/dashboard/pod_dashboard_service_spec.rb'
+- './spec/services/metrics/dashboard/self_monitoring_dashboard_service_spec.rb'
+- './spec/services/metrics/dashboard/system_dashboard_service_spec.rb'
+- './spec/services/metrics/dashboard/transient_embed_service_spec.rb'
+- './spec/services/metrics/dashboard/update_dashboard_service_spec.rb'
+- './spec/services/metrics/sample_metrics_service_spec.rb'
+- './spec/services/metrics/users_starred_dashboards/create_service_spec.rb'
+- './spec/services/metrics/users_starred_dashboards/delete_service_spec.rb'
+- './spec/services/milestones/closed_issues_count_service_spec.rb'
+- './spec/services/milestones/close_service_spec.rb'
+- './spec/services/milestones/create_service_spec.rb'
+- './spec/services/milestones/destroy_service_spec.rb'
+- './spec/services/milestones/find_or_create_service_spec.rb'
+- './spec/services/milestones/issues_count_service_spec.rb'
+- './spec/services/milestones/merge_requests_count_service_spec.rb'
+- './spec/services/milestones/promote_service_spec.rb'
+- './spec/services/milestones/transfer_service_spec.rb'
+- './spec/services/milestones/update_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/namespaces/statistics_refresher_service_spec.rb'
+- './spec/services/notes/build_service_spec.rb'
+- './spec/services/notes/copy_service_spec.rb'
+- './spec/services/notes/create_service_spec.rb'
+- './spec/services/notes/destroy_service_spec.rb'
+- './spec/services/notes/post_process_service_spec.rb'
+- './spec/services/notes/quick_actions_service_spec.rb'
+- './spec/services/notes/render_service_spec.rb'
+- './spec/services/notes/resolve_service_spec.rb'
+- './spec/services/note_summary_spec.rb'
+- './spec/services/notes/update_service_spec.rb'
+- './spec/services/notification_recipients/builder/default_spec.rb'
+- './spec/services/notification_recipients/builder/new_note_spec.rb'
+- './spec/services/notification_recipients/build_service_spec.rb'
+- './spec/services/notification_service_spec.rb'
+- './spec/services/packages/cleanup/execute_policy_service_spec.rb'
+- './spec/services/packages/cleanup/update_policy_service_spec.rb'
+- './spec/services/packages/composer/composer_json_service_spec.rb'
+- './spec/services/packages/composer/create_package_service_spec.rb'
+- './spec/services/packages/composer/version_parser_service_spec.rb'
+- './spec/services/packages/conan/create_package_file_service_spec.rb'
+- './spec/services/packages/conan/create_package_service_spec.rb'
+- './spec/services/packages/conan/search_service_spec.rb'
+- './spec/services/packages/create_dependency_service_spec.rb'
+- './spec/services/packages/create_event_service_spec.rb'
+- './spec/services/packages/create_package_file_service_spec.rb'
+- './spec/services/packages/create_temporary_package_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_changes_metadata_service_spec.rb'
+- './spec/services/packages/debian/extract_deb_metadata_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'
+- './spec/services/packages/debian/generate_distribution_key_service_spec.rb'
+- './spec/services/packages/debian/generate_distribution_service_spec.rb'
+- './spec/services/packages/debian/parse_debian822_service_spec.rb'
+- './spec/services/packages/debian/process_changes_service_spec.rb'
+- './spec/services/packages/debian/sign_distribution_service_spec.rb'
+- './spec/services/packages/debian/update_distribution_service_spec.rb'
+- './spec/services/packages/generic/create_package_file_service_spec.rb'
+- './spec/services/packages/generic/find_or_create_package_service_spec.rb'
+- './spec/services/packages/go/create_package_service_spec.rb'
+- './spec/services/packages/go/sync_packages_service_spec.rb'
+- './spec/services/packages/helm/extract_file_metadata_service_spec.rb'
+- './spec/services/packages/helm/process_file_service_spec.rb'
+- './spec/services/packages/mark_package_files_for_destruction_service_spec.rb'
+- './spec/services/packages/mark_package_for_destruction_service_spec.rb'
+- './spec/services/packages/maven/create_package_service_spec.rb'
+- './spec/services/packages/maven/find_or_create_package_service_spec.rb'
+- './spec/services/packages/maven/metadata/append_package_file_service_spec.rb'
+- './spec/services/packages/maven/metadata/create_plugins_xml_service_spec.rb'
+- './spec/services/packages/maven/metadata/create_versions_xml_service_spec.rb'
+- './spec/services/packages/maven/metadata/sync_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/metadata_extraction_service_spec.rb'
+- './spec/services/packages/nuget/search_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/create_gemspec_service_spec.rb'
+- './spec/services/packages/rubygems/dependency_resolver_service_spec.rb'
+- './spec/services/packages/rubygems/metadata_extraction_service_spec.rb'
+- './spec/services/packages/rubygems/process_gem_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/create_acme_order_service_spec.rb'
+- './spec/services/pages_domains/obtain_lets_encrypt_certificate_service_spec.rb'
+- './spec/services/pages_domains/retry_acme_order_service_spec.rb'
+- './spec/services/pages/migrate_from_legacy_storage_service_spec.rb'
+- './spec/services/pages/migrate_legacy_storage_to_deployment_service_spec.rb'
+- './spec/services/pages/zip_directory_service_spec.rb'
+- './spec/services/personal_access_tokens/create_service_spec.rb'
+- './spec/services/personal_access_tokens/last_used_service_spec.rb'
+- './spec/services/personal_access_tokens/revoke_service_spec.rb'
+- './spec/services/post_receive_service_spec.rb'
+- './spec/services/preview_markdown_service_spec.rb'
+- './spec/services/product_analytics/build_activity_graph_service_spec.rb'
+- './spec/services/product_analytics/build_graph_service_spec.rb'
+- './spec/services/projects/after_rename_service_spec.rb'
+- './spec/services/projects/alerting/notify_service_spec.rb'
+- './spec/services/projects/all_issues_count_service_spec.rb'
+- './spec/services/projects/all_merge_requests_count_service_spec.rb'
+- './spec/services/projects/android_target_platform_detector_service_spec.rb'
+- './spec/services/projects/apple_target_platform_detector_service_spec.rb'
+- './spec/services/projects/autocomplete_service_spec.rb'
+- './spec/services/projects/auto_devops/disable_service_spec.rb'
+- './spec/services/projects/batch_open_issues_count_service_spec.rb'
+- './spec/services/projects/blame_service_spec.rb'
+- './spec/services/projects/branches_by_mode_service_spec.rb'
+- './spec/services/projects/cleanup_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/destroy_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/count_service_spec.rb'
+- './spec/services/projects/create_from_template_service_spec.rb'
+- './spec/services/projects/create_service_spec.rb'
+- './spec/services/projects/deploy_tokens/create_service_spec.rb'
+- './spec/services/projects/deploy_tokens/destroy_service_spec.rb'
+- './spec/services/projects/destroy_service_spec.rb'
+- './spec/services/projects/detect_repository_languages_service_spec.rb'
+- './spec/services/projects/download_service_spec.rb'
+- './spec/services/projects/enable_deploy_key_service_spec.rb'
+- './spec/services/projects/fetch_statistics_increment_service_spec.rb'
+- './spec/services/projects/forks_count_service_spec.rb'
+- './spec/services/projects/fork_service_spec.rb'
+- './spec/services/projects/git_deduplication_service_spec.rb'
+- './spec/services/projects/gitlab_projects_import_service_spec.rb'
+- './spec/services/projects/group_links/create_service_spec.rb'
+- './spec/services/projects/group_links/destroy_service_spec.rb'
+- './spec/services/projects/group_links/update_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/migration_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/hashed_storage/rollback_service_spec.rb'
+- './spec/services/projects/import_error_filter_spec.rb'
+- './spec/services/projects/import_export/export_service_spec.rb'
+- './spec/services/projects/import_export/relation_export_service_spec.rb'
+- './spec/services/projects/import_service_spec.rb'
+- './spec/services/projects/in_product_marketing_campaign_emails_service_spec.rb'
+- './spec/services/projects/lfs_pointers/lfs_download_link_list_service_spec.rb'
+- './spec/services/projects/lfs_pointers/lfs_download_service_spec.rb'
+- './spec/services/projects/lfs_pointers/lfs_import_service_spec.rb'
+- './spec/services/projects/lfs_pointers/lfs_link_service_spec.rb'
+- './spec/services/projects/lfs_pointers/lfs_object_download_list_service_spec.rb'
+- './spec/services/projects/move_access_service_spec.rb'
+- './spec/services/projects/move_deploy_keys_projects_service_spec.rb'
+- './spec/services/projects/move_forks_service_spec.rb'
+- './spec/services/projects/move_lfs_objects_projects_service_spec.rb'
+- './spec/services/projects/move_notification_settings_service_spec.rb'
+- './spec/services/projects/move_project_authorizations_service_spec.rb'
+- './spec/services/projects/move_project_group_links_service_spec.rb'
+- './spec/services/projects/move_project_members_service_spec.rb'
+- './spec/services/projects/move_users_star_projects_service_spec.rb'
+- './spec/services/projects/open_issues_count_service_spec.rb'
+- './spec/services/projects/open_merge_requests_count_service_spec.rb'
+- './spec/services/projects/operations/update_service_spec.rb'
+- './spec/services/projects/overwrite_project_service_spec.rb'
+- './spec/services/projects/participants_service_spec.rb'
+- './spec/services/projects/prometheus/alerts/notify_service_spec.rb'
+- './spec/services/projects/prometheus/metrics/destroy_service_spec.rb'
+- './spec/services/projects/protect_default_branch_service_spec.rb'
+- './spec/services/projects/readme_renderer_service_spec.rb'
+- './spec/services/projects/record_target_platforms_service_spec.rb'
+- './spec/services/projects/refresh_build_artifacts_size_statistics_service_spec.rb'
+- './spec/services/projects/repository_languages_service_spec.rb'
+- './spec/services/projects/schedule_bulk_repository_shard_moves_service_spec.rb'
+- './spec/services/projects/transfer_service_spec.rb'
+- './spec/services/projects/unlink_fork_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/projects/update_statistics_service_spec.rb'
+- './spec/services/prometheus/proxy_service_spec.rb'
+- './spec/services/prometheus/proxy_variable_substitution_service_spec.rb'
+- './spec/services/protected_branches/cache_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/protected_tags/create_service_spec.rb'
+- './spec/services/protected_tags/destroy_service_spec.rb'
+- './spec/services/protected_tags/update_service_spec.rb'
+- './spec/services/push_event_payload_service_spec.rb'
+- './spec/services/quick_actions/interpret_service_spec.rb'
+- './spec/services/quick_actions/target_service_spec.rb'
+- './spec/services/releases/create_evidence_service_spec.rb'
+- './spec/services/releases/create_service_spec.rb'
+- './spec/services/releases/destroy_service_spec.rb'
+- './spec/services/releases/update_service_spec.rb'
+- './spec/services/repositories/changelog_service_spec.rb'
+- './spec/services/repositories/destroy_service_spec.rb'
+- './spec/services/repositories/housekeeping_service_spec.rb'
+- './spec/services/repository_archive_clean_up_service_spec.rb'
+- './spec/services/reset_project_cache_service_spec.rb'
+- './spec/services/resource_access_tokens/create_service_spec.rb'
+- './spec/services/resource_access_tokens/revoke_service_spec.rb'
+- './spec/services/resource_events/change_labels_service_spec.rb'
+- './spec/services/resource_events/change_milestone_service_spec.rb'
+- './spec/services/resource_events/change_state_service_spec.rb'
+- './spec/services/resource_events/merge_into_notes_service_spec.rb'
+- './spec/services/resource_events/synthetic_label_notes_builder_service_spec.rb'
+- './spec/services/resource_events/synthetic_milestone_notes_builder_service_spec.rb'
+- './spec/services/resource_events/synthetic_state_notes_builder_service_spec.rb'
+- './spec/services/search/global_service_spec.rb'
+- './spec/services/search/group_service_spec.rb'
+- './spec/services/search_service_spec.rb'
+- './spec/services/search/snippet_service_spec.rb'
+- './spec/services/security/ci_configuration/container_scanning_create_service_spec.rb'
+- './spec/services/security/ci_configuration/sast_create_service_spec.rb'
+- './spec/services/security/ci_configuration/sast_iac_create_service_spec.rb'
+- './spec/services/security/ci_configuration/sast_parser_service_spec.rb'
+- './spec/services/security/ci_configuration/secret_detection_create_service_spec.rb'
+- './spec/services/security/merge_reports_service_spec.rb'
+- './spec/services/serverless/associate_domain_service_spec.rb'
+- './spec/services/service_desk_settings/update_service_spec.rb'
+- './spec/services/service_ping/submit_service_ping_service_spec.rb'
+- './spec/services/service_response_spec.rb'
+- './spec/services/snippets/bulk_destroy_service_spec.rb'
+- './spec/services/snippets/count_service_spec.rb'
+- './spec/services/snippets/create_service_spec.rb'
+- './spec/services/snippets/destroy_service_spec.rb'
+- './spec/services/snippets/repository_validation_service_spec.rb'
+- './spec/services/snippets/schedule_bulk_repository_shard_moves_service_spec.rb'
+- './spec/services/snippets/update_repository_storage_service_spec.rb'
+- './spec/services/snippets/update_service_spec.rb'
+- './spec/services/snippets/update_statistics_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/spam/spam_verdict_service_spec.rb'
+- './spec/services/submodules/update_service_spec.rb'
+- './spec/services/suggestions/apply_service_spec.rb'
+- './spec/services/suggestions/create_service_spec.rb'
+- './spec/services/suggestions/outdate_service_spec.rb'
+- './spec/services/system_hooks_service_spec.rb'
+- './spec/services/system_notes/alert_management_service_spec.rb'
+- './spec/services/system_notes/base_service_spec.rb'
+- './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'
+- './spec/services/system_notes/zoom_service_spec.rb'
+- './spec/services/tags/create_service_spec.rb'
+- './spec/services/tags/destroy_service_spec.rb'
+- './spec/services/task_list_toggle_service_spec.rb'
+- './spec/services/tasks_to_be_done/base_service_spec.rb'
+- './spec/services/terraform/remote_state_handler_spec.rb'
+- './spec/services/terraform/states/destroy_service_spec.rb'
+- './spec/services/terraform/states/trigger_destroy_service_spec.rb'
+- './spec/services/test_hooks/project_service_spec.rb'
+- './spec/services/test_hooks/system_service_spec.rb'
+- './spec/services/timelogs/create_service_spec.rb'
+- './spec/services/timelogs/delete_service_spec.rb'
+- './spec/services/todos/allowed_target_filter_service_spec.rb'
+- './spec/services/todos/destroy/confidential_issue_service_spec.rb'
+- './spec/services/todos/destroy/design_service_spec.rb'
+- './spec/services/todos/destroy/destroyed_issuable_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/todo_service_spec.rb'
+- './spec/services/topics/merge_service_spec.rb'
+- './spec/services/two_factor/destroy_service_spec.rb'
+- './spec/services/update_container_registry_info_service_spec.rb'
+- './spec/services/update_merge_request_metrics_service_spec.rb'
+- './spec/services/uploads/destroy_service_spec.rb'
+- './spec/services/upload_service_spec.rb'
+- './spec/services/user_preferences/update_service_spec.rb'
+- './spec/services/user_project_access_changed_service_spec.rb'
+- './spec/services/users/activity_service_spec.rb'
+- './spec/services/users/approve_service_spec.rb'
+- './spec/services/users/authorized_build_service_spec.rb'
+- './spec/services/users/banned_user_base_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/build_service_spec.rb'
+- './spec/services/users/create_service_spec.rb'
+- './spec/services/users/destroy_service_spec.rb'
+- './spec/services/users/dismiss_callout_service_spec.rb'
+- './spec/services/users/dismiss_group_callout_service_spec.rb'
+- './spec/services/users/dismiss_namespace_callout_service_spec.rb'
+- './spec/services/users/dismiss_project_callout_service_spec.rb'
+- './spec/services/users/email_verification/generate_token_service_spec.rb'
+- './spec/services/users/email_verification/validate_token_service_spec.rb'
+- './spec/services/users/in_product_marketing_email_records_spec.rb'
+- './spec/services/users/keys_count_service_spec.rb'
+- './spec/services/users/last_push_event_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/registrations_build_service_spec.rb'
+- './spec/services/users/reject_service_spec.rb'
+- './spec/services/users/repair_ldap_blocked_service_spec.rb'
+- './spec/services/users/respond_to_terms_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/set_status_service_spec.rb'
+- './spec/services/users/signup_service_spec.rb'
+- './spec/services/users/unban_service_spec.rb'
+- './spec/services/users/update_canonical_email_service_spec.rb'
+- './spec/services/users/update_highest_member_role_service_spec.rb'
+- './spec/services/users/update_service_spec.rb'
+- './spec/services/users/update_todo_count_cache_service_spec.rb'
+- './spec/services/users/upsert_credit_card_validation_service_spec.rb'
+- './spec/services/users/validate_manual_otp_service_spec.rb'
+- './spec/services/users/validate_push_otp_service_spec.rb'
+- './spec/services/verify_pages_domain_service_spec.rb'
+- './spec/services/webauthn/authenticate_service_spec.rb'
+- './spec/services/webauthn/register_service_spec.rb'
+- './spec/services/web_hooks/destroy_service_spec.rb'
+- './spec/services/web_hook_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/base_service_spec.rb'
+- './spec/services/wiki_pages/create_service_spec.rb'
+- './spec/services/wiki_pages/destroy_service_spec.rb'
+- './spec/services/wiki_pages/event_create_service_spec.rb'
+- './spec/services/wiki_pages/update_service_spec.rb'
+- './spec/services/wikis/create_attachment_service_spec.rb'
+- './spec/services/work_items/build_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_service_spec.rb'
+- './spec/services/work_items/delete_task_service_spec.rb'
+- './spec/services/work_items/parent_links/create_service_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/assignees_service/update_service_spec.rb'
+- './spec/services/work_items/widgets/description_service/update_service_spec.rb'
+- './spec/services/work_items/widgets/hierarchy_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'
+- './spec/sidekiq_cluster/sidekiq_cluster_spec.rb'
+- './spec/sidekiq/cron/job_gem_dependency_spec.rb'
+- './spec/spam/concerns/has_spam_action_response_fields_spec.rb'
+- './spec/support_specs/database/multiple_databases_spec.rb'
+- './spec/support_specs/database/prevent_cross_joins_spec.rb'
+- './spec/support_specs/graphql/arguments_spec.rb'
+- './spec/support_specs/graphql/field_selection_spec.rb'
+- './spec/support_specs/graphql/var_spec.rb'
+- './spec/support_specs/helpers/active_record/query_recorder_spec.rb'
+- './spec/support_specs/helpers/graphql_helpers_spec.rb'
+- './spec/support_specs/helpers/migrations_helpers_spec.rb'
+- './spec/support_specs/helpers/redis_commands/recorder_spec.rb'
+- './spec/support_specs/helpers/stub_feature_flags_spec.rb'
+- './spec/support_specs/helpers/stub_method_calls_spec.rb'
+- './spec/support_specs/matchers/be_sorted_spec.rb'
+- './spec/support_specs/matchers/exceed_query_limit_helpers_spec.rb'
+- './spec/support_specs/time_travel_spec.rb'
+- './spec/tasks/admin_mode_spec.rb'
+- './spec/tasks/cache/clear/redis_spec.rb'
+- './spec/tasks/config_lint_spec.rb'
+- './spec/tasks/dev_rake_spec.rb'
+- './spec/tasks/gettext_rake_spec.rb'
+- './spec/tasks/gitlab/artifacts/check_rake_spec.rb'
+- './spec/tasks/gitlab/artifacts/migrate_rake_spec.rb'
+- './spec/tasks/gitlab/background_migrations_rake_spec.rb'
+- './spec/tasks/gitlab/backup_rake_spec.rb'
+- './spec/tasks/gitlab/check_rake_spec.rb'
+- './spec/tasks/gitlab/cleanup_rake_spec.rb'
+- './spec/tasks/gitlab/container_registry_rake_spec.rb'
+- './spec/tasks/gitlab/db/decomposition/rollback/bump_ci_sequences_rake_spec.rb'
+- './spec/tasks/gitlab/db/lock_writes_rake_spec.rb'
+- './spec/tasks/gitlab/db_rake_spec.rb'
+- './spec/tasks/gitlab/db/validate_config_rake_spec.rb'
+- './spec/tasks/gitlab/dependency_proxy/migrate_rake_spec.rb'
+- './spec/tasks/gitlab/external_diffs_rake_spec.rb'
+- './spec/tasks/gitlab/generate_sample_prometheus_data_spec.rb'
+- './spec/tasks/gitlab/gitaly_rake_spec.rb'
+- './spec/tasks/gitlab/git_rake_spec.rb'
+- './spec/tasks/gitlab/ldap_rake_spec.rb'
+- './spec/tasks/gitlab/lfs/check_rake_spec.rb'
+- './spec/tasks/gitlab/lfs/migrate_rake_spec.rb'
+- './spec/tasks/gitlab/packages/events_rake_spec.rb'
+- './spec/tasks/gitlab/packages/migrate_rake_spec.rb'
+- './spec/tasks/gitlab/pages_rake_spec.rb'
+- './spec/tasks/gitlab/password_rake_spec.rb'
+- './spec/tasks/gitlab/praefect_rake_spec.rb'
+- './spec/tasks/gitlab/refresh_project_statistics_build_artifacts_size_rake_spec.rb'
+- './spec/tasks/gitlab/seed/group_seed_rake_spec.rb'
+- './spec/tasks/gitlab/setup_rake_spec.rb'
+- './spec/tasks/gitlab/shell_rake_spec.rb'
+- './spec/tasks/gitlab/sidekiq_rake_spec.rb'
+- './spec/tasks/gitlab/smtp_rake_spec.rb'
+- './spec/tasks/gitlab/snippets_rake_spec.rb'
+- './spec/tasks/gitlab/storage_rake_spec.rb'
+- './spec/tasks/gitlab/task_helpers_spec.rb'
+- './spec/tasks/gitlab/terraform/migrate_rake_spec.rb'
+- './spec/tasks/gitlab/update_templates_rake_spec.rb'
+- './spec/tasks/gitlab/uploads/check_rake_spec.rb'
+- './spec/tasks/gitlab/uploads/migrate_rake_spec.rb'
+- './spec/tasks/gitlab/usage_data_rake_spec.rb'
+- './spec/tasks/gitlab/user_management_rake_spec.rb'
+- './spec/tasks/gitlab/web_hook_rake_spec.rb'
+- './spec/tasks/gitlab/workhorse_rake_spec.rb'
+- './spec/tasks/gitlab/x509/update_rake_spec.rb'
+- './spec/tasks/migrate/schema_check_rake_spec.rb'
+- './spec/tasks/rubocop_rake_spec.rb'
+- './spec/tasks/tokens_spec.rb'
+- './spec/tooling/danger/customer_success_spec.rb'
+- './spec/tooling/danger/datateam_spec.rb'
+- './spec/tooling/danger/feature_flag_spec.rb'
+- './spec/tooling/danger/product_intelligence_spec.rb'
+- './spec/tooling/danger/project_helper_spec.rb'
+- './spec/tooling/danger/sidekiq_queues_spec.rb'
+- './spec/tooling/danger/specs_spec.rb'
+- './spec/tooling/docs/deprecation_handling_spec.rb'
+- './spec/tooling/graphql/docs/renderer_spec.rb'
+- './spec/tooling/lib/tooling/crystalball/coverage_lines_execution_detector_spec.rb'
+- './spec/tooling/lib/tooling/crystalball/coverage_lines_strategy_spec.rb'
+- './spec/tooling/lib/tooling/find_codeowners_spec.rb'
+- './spec/tooling/lib/tooling/helm3_client_spec.rb'
+- './spec/tooling/lib/tooling/kubernetes_client_spec.rb'
+- './spec/tooling/lib/tooling/parallel_rspec_runner_spec.rb'
+- './spec/tooling/lib/tooling/test_map_generator_spec.rb'
+- './spec/tooling/lib/tooling/test_map_packer_spec.rb'
+- './spec/tooling/merge_request_spec.rb'
+- './spec/tooling/quality/test_level_spec.rb'
+- './spec/tooling/rspec_flaky/config_spec.rb'
+- './spec/tooling/rspec_flaky/example_spec.rb'
+- './spec/tooling/rspec_flaky/flaky_examples_collection_spec.rb'
+- './spec/tooling/rspec_flaky/flaky_example_spec.rb'
+- './spec/tooling/rspec_flaky/listener_spec.rb'
+- './spec/tooling/rspec_flaky/report_spec.rb'
+- './spec/uploaders/attachment_uploader_spec.rb'
+- './spec/uploaders/avatar_uploader_spec.rb'
+- './spec/uploaders/ci/pipeline_artifact_uploader_spec.rb'
+- './spec/uploaders/ci/secure_file_uploader_spec.rb'
+- './spec/uploaders/content_type_whitelist_spec.rb'
+- './spec/uploaders/dependency_proxy/file_uploader_spec.rb'
+- './spec/uploaders/design_management/design_v432x230_uploader_spec.rb'
+- './spec/uploaders/external_diff_uploader_spec.rb'
+- './spec/uploaders/favicon_uploader_spec.rb'
+- './spec/uploaders/file_mover_spec.rb'
+- './spec/uploaders/file_uploader_spec.rb'
+- './spec/uploaders/gitlab_uploader_spec.rb'
+- './spec/uploaders/import_export_uploader_spec.rb'
+- './spec/uploaders/job_artifact_uploader_spec.rb'
+- './spec/uploaders/lfs_object_uploader_spec.rb'
+- './spec/uploaders/metric_image_uploader_spec.rb'
+- './spec/uploaders/namespace_file_uploader_spec.rb'
+- './spec/uploaders/object_storage_spec.rb'
+- './spec/uploaders/packages/composer/cache_uploader_spec.rb'
+- './spec/uploaders/packages/debian/component_file_uploader_spec.rb'
+- './spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb'
+- './spec/uploaders/packages/package_file_uploader_spec.rb'
+- './spec/uploaders/pages/deployment_uploader_spec.rb'
+- './spec/uploaders/personal_file_uploader_spec.rb'
+- './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'
+- './spec/validators/array_members_validator_spec.rb'
+- './spec/validators/branch_filter_validator_spec.rb'
+- './spec/validators/color_validator_spec.rb'
+- './spec/validators/cron_freeze_period_timezone_validator_spec.rb'
+- './spec/validators/cron_validator_spec.rb'
+- './spec/validators/devise_email_validator_spec.rb'
+- './spec/validators/future_date_validator_spec.rb'
+- './spec/validators/gitlab/zoom_url_validator_spec.rb'
+- './spec/validators/html_safety_validator_spec.rb'
+- './spec/validators/import/gitlab_projects/remote_file_validator_spec.rb'
+- './spec/validators/ip_address_validator_spec.rb'
+- './spec/validators/json_schema_validator_spec.rb'
+- './spec/validators/js_regex_validator_spec.rb'
+- './spec/validators/named_ecdsa_key_validator_spec.rb'
+- './spec/validators/namespace_path_validator_spec.rb'
+- './spec/validators/nested_attributes_duplicates_validator_spec.rb'
+- './spec/validators/project_path_validator_spec.rb'
+- './spec/validators/public_url_validator_spec.rb'
+- './spec/validators/qualified_domain_array_validator_spec.rb'
+- './spec/validators/rsa_key_validator_spec.rb'
+- './spec/validators/sha_validator_spec.rb'
+- './spec/validators/system_hook_url_validator_spec.rb'
+- './spec/validators/x509_certificate_credentials_validator_spec.rb'
+- './spec/views/admin/application_settings/_ci_cd.html.haml_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/general.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/repository.html.haml_spec.rb'
+- './spec/views/admin/application_settings/_repository_storage.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/milestones/index.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/dashboard/projects/index.html.haml_spec.rb'
+- './spec/views/dashboard/projects/_nav.html.haml_spec.rb'
+- './spec/views/devise/sessions/new.html.haml_spec.rb'
+- './spec/views/devise/shared/_signin_box.html.haml_spec.rb'
+- './spec/views/devise/shared/_signup_box.html.haml_spec.rb'
+- './spec/views/errors/access_denied.html.haml_spec.rb'
+- './spec/views/errors/omniauth_error.html.haml_spec.rb'
+- './spec/views/events/event/_push.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/_home_panel.html.haml_spec.rb'
+- './spec/views/groups/milestones/index.html.haml_spec.rb'
+- './spec/views/groups/new.html.haml_spec.rb'
+- './spec/views/groups/settings/_remove.html.haml_spec.rb'
+- './spec/views/help/index.html.haml_spec.rb'
+- './spec/views/help/instance_configuration.html.haml_spec.rb'
+- './spec/views/help/show.html.haml_spec.rb'
+- './spec/views/import/gitlab_projects/new.html.haml_spec.rb'
+- './spec/views/layouts/application.html.haml_spec.rb'
+- './spec/views/layouts/devise_empty.html.haml_spec.rb'
+- './spec/views/layouts/devise.html.haml_spec.rb'
+- './spec/views/layouts/_flash.html.haml_spec.rb'
+- './spec/views/layouts/fullscreen.html.haml_spec.rb'
+- './spec/views/layouts/header/_gitlab_version.html.haml_spec.rb'
+- './spec/views/layouts/header/_new_dropdown.haml_spec.rb'
+- './spec/views/layouts/_header_search.html.haml_spec.rb'
+- './spec/views/layouts/_head.html.haml_spec.rb'
+- './spec/views/layouts/nav/sidebar/_admin.html.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/_published_experiments.html.haml_spec.rb'
+- './spec/views/layouts/_search.html.haml_spec.rb'
+- './spec/views/layouts/signup_onboarding.html.haml_spec.rb'
+- './spec/views/layouts/simple_registration.html.haml_spec.rb'
+- './spec/views/layouts/terms.html.haml_spec.rb'
+- './spec/views/notify/autodevops_disabled_email.text.erb_spec.rb'
+- './spec/views/notify/changed_milestone_email.html.haml_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/pipeline_failed_email.html.haml_spec.rb'
+- './spec/views/notify/pipeline_failed_email.text.erb_spec.rb'
+- './spec/views/notify/pipeline_fixed_email.html.haml_spec.rb'
+- './spec/views/notify/pipeline_fixed_email.text.erb_spec.rb'
+- './spec/views/notify/pipeline_success_email.html.haml_spec.rb'
+- './spec/views/notify/pipeline_success_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/_form.html.haml_spec.rb'
+- './spec/views/profiles/keys/_key.html.haml_spec.rb'
+- './spec/views/profiles/notifications/show.html.haml_spec.rb'
+- './spec/views/profiles/preferences/show.html.haml_spec.rb'
+- './spec/views/profiles/show.html.haml_spec.rb'
+- './spec/views/projects/artifacts/_artifact.html.haml_spec.rb'
+- './spec/views/projects/blob/_viewer.html.haml_spec.rb'
+- './spec/views/projects/branches/index.html.haml_spec.rb'
+- './spec/views/projects/commit/branches.html.haml_spec.rb'
+- './spec/views/projects/commit/_commit_box.html.haml_spec.rb'
+- './spec/views/projects/commits/_commit.html.haml_spec.rb'
+- './spec/views/projects/commit/show.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/_flash_messages.html.haml_spec.rb'
+- './spec/views/projects/_home_panel.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/_related_branches.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/_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/merge_requests/show.html.haml_spec.rb'
+- './spec/views/projects/milestones/index.html.haml_spec.rb'
+- './spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb'
+- './spec/views/projects/pages_domains/show.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/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/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/search/show.html.haml_spec.rb'
+- './spec/views/shared/access_tokens/_table.html.haml_spec.rb'
+- './spec/views/shared/deploy_tokens/_form.html.haml_spec.rb'
+- './spec/views/shared/groups/_dropdown.html.haml_spec.rb'
+- './spec/views/shared/issuable/_sidebar.html.haml_spec.rb'
+- './spec/views/shared/_label_row.html.haml_spec.rb'
+- './spec/views/shared/milestones/_issuable.html.haml_spec.rb'
+- './spec/views/shared/milestones/_issuables.html.haml_spec.rb'
+- './spec/views/shared/_milestones_sort_dropdown.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/ssh_keys/_key_details.html.haml_spec.rb'
+- './spec/views/shared/wikis/_sidebar.html.haml_spec.rb'
+- './spec/workers/admin_email_worker_spec.rb'
+- './spec/workers/analytics/usage_trends/counter_job_worker_spec.rb'
+- './spec/workers/analytics/usage_trends/count_job_trigger_worker_spec.rb'
+- './spec/workers/approve_blocked_pending_approval_users_worker_spec.rb'
+- './spec/workers/authorized_keys_worker_spec.rb'
+- './spec/workers/authorized_projects_worker_spec.rb'
+- './spec/workers/authorized_project_update/periodic_recalculate_worker_spec.rb'
+- './spec/workers/authorized_project_update/project_recalculate_per_user_worker_spec.rb'
+- './spec/workers/authorized_project_update/project_recalculate_worker_spec.rb'
+- './spec/workers/authorized_project_update/user_refresh_from_replica_worker_spec.rb'
+- './spec/workers/authorized_project_update/user_refresh_over_user_range_worker_spec.rb'
+- './spec/workers/authorized_project_update/user_refresh_with_low_urgency_worker_spec.rb'
+- './spec/workers/auto_devops/disable_worker_spec.rb'
+- './spec/workers/auto_merge_process_worker_spec.rb'
+- './spec/workers/background_migration/ci_database_worker_spec.rb'
+- './spec/workers/background_migration_worker_spec.rb'
+- './spec/workers/build_hooks_worker_spec.rb'
+- './spec/workers/build_queue_worker_spec.rb'
+- './spec/workers/build_success_worker_spec.rb'
+- './spec/workers/bulk_imports/entity_worker_spec.rb'
+- './spec/workers/bulk_imports/export_request_worker_spec.rb'
+- './spec/workers/bulk_imports/pipeline_worker_spec.rb'
+- './spec/workers/bulk_imports/relation_export_worker_spec.rb'
+- './spec/workers/bulk_imports/stuck_import_worker_spec.rb'
+- './spec/workers/bulk_import_worker_spec.rb'
+- './spec/workers/chat_notification_worker_spec.rb'
+- './spec/workers/ci/archive_traces_cron_worker_spec.rb'
+- './spec/workers/ci/archive_trace_worker_spec.rb'
+- './spec/workers/ci/build_finished_worker_spec.rb'
+- './spec/workers/ci/build_prepare_worker_spec.rb'
+- './spec/workers/ci/build_schedule_worker_spec.rb'
+- './spec/workers/ci/build_trace_chunk_flush_worker_spec.rb'
+- './spec/workers/ci/cancel_pipeline_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/daily_build_group_report_results_worker_spec.rb'
+- './spec/workers/ci/delete_objects_worker_spec.rb'
+- './spec/workers/ci/delete_unit_tests_worker_spec.rb'
+- './spec/workers/ci/drop_pipeline_worker_spec.rb'
+- './spec/workers/ci/external_pull_requests/create_pipeline_worker_spec.rb'
+- './spec/workers/ci/initial_pipeline_process_worker_spec.rb'
+- './spec/workers/ci/job_artifacts/expire_project_build_artifacts_worker_spec.rb'
+- './spec/workers/ci/merge_requests/add_todo_when_build_fails_worker_spec.rb'
+- './spec/workers/ci/pending_builds/update_group_worker_spec.rb'
+- './spec/workers/ci/pending_builds/update_project_worker_spec.rb'
+- './spec/workers/ci/pipeline_artifacts/coverage_report_worker_spec.rb'
+- './spec/workers/ci/pipeline_artifacts/create_quality_report_worker_spec.rb'
+- './spec/workers/ci/pipeline_artifacts/expire_artifacts_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_platform_metrics_update_cron_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/ci/retry_pipeline_worker_spec.rb'
+- './spec/workers/ci/runners/process_runner_version_update_worker_spec.rb'
+- './spec/workers/ci/runners/reconcile_existing_runner_versions_cron_worker_spec.rb'
+- './spec/workers/ci/schedule_delete_objects_cron_worker_spec.rb'
+- './spec/workers/ci/stuck_builds/drop_running_worker_spec.rb'
+- './spec/workers/ci/stuck_builds/drop_scheduled_worker_spec.rb'
+- './spec/workers/ci/test_failure_history_worker_spec.rb'
+- './spec/workers/ci/track_failed_build_worker_spec.rb'
+- './spec/workers/ci/update_locked_unknown_artifacts_worker_spec.rb'
+- './spec/workers/cleanup_container_repository_worker_spec.rb'
+- './spec/workers/cluster_configure_istio_worker_spec.rb'
+- './spec/workers/cluster_provision_worker_spec.rb'
+- './spec/workers/clusters/agents/delete_expired_events_worker_spec.rb'
+- './spec/workers/clusters/applications/activate_integration_worker_spec.rb'
+- './spec/workers/clusters/applications/deactivate_integration_worker_spec.rb'
+- './spec/workers/clusters/applications/wait_for_uninstall_app_worker_spec.rb'
+- './spec/workers/clusters/cleanup/project_namespace_worker_spec.rb'
+- './spec/workers/clusters/cleanup/service_account_worker_spec.rb'
+- './spec/workers/clusters/integrations/check_prometheus_health_worker_spec.rb'
+- './spec/workers/cluster_update_app_worker_spec.rb'
+- './spec/workers/cluster_wait_for_app_update_worker_spec.rb'
+- './spec/workers/cluster_wait_for_ingress_ip_address_worker_spec.rb'
+- './spec/workers/concerns/application_worker_spec.rb'
+- './spec/workers/concerns/cluster_agent_queue_spec.rb'
+- './spec/workers/concerns/cronjob_queue_spec.rb'
+- './spec/workers/concerns/gitlab/github_import/object_importer_spec.rb'
+- './spec/workers/concerns/gitlab/github_import/rescheduling_methods_spec.rb'
+- './spec/workers/concerns/gitlab/github_import/stage_methods_spec.rb'
+- './spec/workers/concerns/gitlab/notify_upon_death_spec.rb'
+- './spec/workers/concerns/limited_capacity/job_tracker_spec.rb'
+- './spec/workers/concerns/limited_capacity/worker_spec.rb'
+- './spec/workers/concerns/packages/cleanup_artifact_worker_spec.rb'
+- './spec/workers/concerns/project_import_options_spec.rb'
+- './spec/workers/concerns/reenqueuer_spec.rb'
+- './spec/workers/concerns/repository_check_queue_spec.rb'
+- './spec/workers/concerns/waitable_worker_spec.rb'
+- './spec/workers/concerns/worker_attributes_spec.rb'
+- './spec/workers/concerns/worker_context_spec.rb'
+- './spec/workers/container_expiration_policies/cleanup_container_repository_worker_spec.rb'
+- './spec/workers/container_expiration_policy_worker_spec.rb'
+- './spec/workers/container_registry/migration/enqueuer_worker_spec.rb'
+- './spec/workers/container_registry/migration/guard_worker_spec.rb'
+- './spec/workers/container_registry/migration/observer_worker_spec.rb'
+- './spec/workers/create_commit_signature_worker_spec.rb'
+- './spec/workers/create_note_diff_file_worker_spec.rb'
+- './spec/workers/create_pipeline_worker_spec.rb'
+- './spec/workers/database/batched_background_migration/ci_database_worker_spec.rb'
+- './spec/workers/database/batched_background_migration_worker_spec.rb'
+- './spec/workers/database/ci_namespace_mirrors_consistency_check_worker_spec.rb'
+- './spec/workers/database/ci_project_mirrors_consistency_check_worker_spec.rb'
+- './spec/workers/database/drop_detached_partitions_worker_spec.rb'
+- './spec/workers/database/partition_management_worker_spec.rb'
+- './spec/workers/delete_container_repository_worker_spec.rb'
+- './spec/workers/delete_diff_files_worker_spec.rb'
+- './spec/workers/delete_merged_branches_worker_spec.rb'
+- './spec/workers/delete_user_worker_spec.rb'
+- './spec/workers/dependency_proxy/cleanup_blob_worker_spec.rb'
+- './spec/workers/dependency_proxy/cleanup_dependency_proxy_worker_spec.rb'
+- './spec/workers/dependency_proxy/cleanup_manifest_worker_spec.rb'
+- './spec/workers/dependency_proxy/image_ttl_group_policy_worker_spec.rb'
+- './spec/workers/deployments/archive_in_project_worker_spec.rb'
+- './spec/workers/deployments/drop_older_deployments_worker_spec.rb'
+- './spec/workers/deployments/hooks_worker_spec.rb'
+- './spec/workers/deployments/link_merge_request_worker_spec.rb'
+- './spec/workers/deployments/update_environment_worker_spec.rb'
+- './spec/workers/design_management/copy_design_collection_worker_spec.rb'
+- './spec/workers/design_management/new_version_worker_spec.rb'
+- './spec/workers/destroy_pages_deployments_worker_spec.rb'
+- './spec/workers/detect_repository_languages_worker_spec.rb'
+- './spec/workers/disallow_two_factor_for_group_worker_spec.rb'
+- './spec/workers/disallow_two_factor_for_subgroups_worker_spec.rb'
+- './spec/workers/email_receiver_worker_spec.rb'
+- './spec/workers/emails_on_push_worker_spec.rb'
+- './spec/workers/environments/auto_delete_cron_worker_spec.rb'
+- './spec/workers/environments/auto_stop_cron_worker_spec.rb'
+- './spec/workers/environments/auto_stop_worker_spec.rb'
+- './spec/workers/environments/canary_ingress/update_worker_spec.rb'
+- './spec/workers/error_tracking_issue_link_worker_spec.rb'
+- './spec/workers/every_sidekiq_worker_spec.rb'
+- './spec/workers/experiments/record_conversion_event_worker_spec.rb'
+- './spec/workers/expire_build_artifacts_worker_spec.rb'
+- './spec/workers/export_csv_worker_spec.rb'
+- './spec/workers/external_service_reactive_caching_worker_spec.rb'
+- './spec/workers/file_hook_worker_spec.rb'
+- './spec/workers/flush_counter_increments_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_event_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_merged_by_worker_spec.rb'
+- './spec/workers/gitlab/github_import/import_pull_request_review_worker_spec.rb'
+- './spec/workers/gitlab/github_import/import_pull_request_worker_spec.rb'
+- './spec/workers/gitlab/github_import/refresh_import_jid_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_issue_events_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/import/stuck_import_job_spec.rb'
+- './spec/workers/gitlab/import/stuck_project_import_jobs_worker_spec.rb'
+- './spec/workers/gitlab/jira_import/import_issue_worker_spec.rb'
+- './spec/workers/gitlab/jira_import/stage/finish_import_worker_spec.rb'
+- './spec/workers/gitlab/jira_import/stage/import_attachments_worker_spec.rb'
+- './spec/workers/gitlab/jira_import/stage/import_issues_worker_spec.rb'
+- './spec/workers/gitlab/jira_import/stage/import_labels_worker_spec.rb'
+- './spec/workers/gitlab/jira_import/stage/import_notes_worker_spec.rb'
+- './spec/workers/gitlab/jira_import/stage/start_import_worker_spec.rb'
+- './spec/workers/gitlab/jira_import/stuck_jira_import_jobs_worker_spec.rb'
+- './spec/workers/gitlab_performance_bar_stats_worker_spec.rb'
+- './spec/workers/gitlab/phabricator_import/base_worker_spec.rb'
+- './spec/workers/gitlab/phabricator_import/import_tasks_worker_spec.rb'
+- './spec/workers/gitlab_service_ping_worker_spec.rb'
+- './spec/workers/gitlab_shell_worker_spec.rb'
+- './spec/workers/google_cloud/create_cloudsql_instance_worker_spec.rb'
+- './spec/workers/group_destroy_worker_spec.rb'
+- './spec/workers/group_export_worker_spec.rb'
+- './spec/workers/group_import_worker_spec.rb'
+- './spec/workers/groups/update_statistics_worker_spec.rb'
+- './spec/workers/hashed_storage/migrator_worker_spec.rb'
+- './spec/workers/hashed_storage/project_migrate_worker_spec.rb'
+- './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'
+- './spec/workers/invalid_gpg_signature_update_worker_spec.rb'
+- './spec/workers/issuable_export_csv_worker_spec.rb'
+- './spec/workers/issuable/label_links_destroy_worker_spec.rb'
+- './spec/workers/issuables/clear_groups_issue_counter_worker_spec.rb'
+- './spec/workers/issue_due_scheduler_worker_spec.rb'
+- './spec/workers/issues/placement_worker_spec.rb'
+- './spec/workers/issues/rebalancing_worker_spec.rb'
+- './spec/workers/issues/reschedule_stuck_issue_rebalances_worker_spec.rb'
+- './spec/workers/jira_connect/forward_event_worker_spec.rb'
+- './spec/workers/jira_connect/retry_request_worker_spec.rb'
+- './spec/workers/jira_connect/sync_branch_worker_spec.rb'
+- './spec/workers/jira_connect/sync_builds_worker_spec.rb'
+- './spec/workers/jira_connect/sync_deployments_worker_spec.rb'
+- './spec/workers/jira_connect/sync_feature_flags_worker_spec.rb'
+- './spec/workers/jira_connect/sync_merge_request_worker_spec.rb'
+- './spec/workers/jira_connect/sync_project_worker_spec.rb'
+- './spec/workers/loose_foreign_keys/cleanup_worker_spec.rb'
+- './spec/workers/mail_scheduler/issue_due_worker_spec.rb'
+- './spec/workers/mail_scheduler/notification_service_worker_spec.rb'
+- './spec/workers/member_invitation_reminder_emails_worker_spec.rb'
+- './spec/workers/members_destroyer/unassign_issuables_worker_spec.rb'
+- './spec/workers/merge_request_cleanup_refs_worker_spec.rb'
+- './spec/workers/merge_request_mergeability_check_worker_spec.rb'
+- './spec/workers/merge_requests/close_issue_worker_spec.rb'
+- './spec/workers/merge_requests/create_approval_event_worker_spec.rb'
+- './spec/workers/merge_requests/create_approval_note_worker_spec.rb'
+- './spec/workers/merge_requests/create_pipeline_worker_spec.rb'
+- './spec/workers/merge_requests/delete_source_branch_worker_spec.rb'
+- './spec/workers/merge_requests/execute_approval_hooks_worker_spec.rb'
+- './spec/workers/merge_requests/handle_assignees_change_worker_spec.rb'
+- './spec/workers/merge_requests/resolve_todos_after_approval_worker_spec.rb'
+- './spec/workers/merge_requests/resolve_todos_worker_spec.rb'
+- './spec/workers/merge_requests/update_head_pipeline_worker_spec.rb'
+- './spec/workers/merge_worker_spec.rb'
+- './spec/workers/metrics/dashboard/prune_old_annotations_worker_spec.rb'
+- './spec/workers/metrics/dashboard/schedule_annotations_prune_worker_spec.rb'
+- './spec/workers/metrics/dashboard/sync_dashboards_worker_spec.rb'
+- './spec/workers/migrate_external_diffs_worker_spec.rb'
+- './spec/workers/namespaces/in_product_marketing_emails_worker_spec.rb'
+- './spec/workers/namespaces/onboarding_issue_created_worker_spec.rb'
+- './spec/workers/namespaces/onboarding_pipeline_created_worker_spec.rb'
+- './spec/workers/namespaces/onboarding_progress_worker_spec.rb'
+- './spec/workers/namespaces/onboarding_user_added_worker_spec.rb'
+- './spec/workers/namespaces/process_sync_events_worker_spec.rb'
+- './spec/workers/namespaces/prune_aggregation_schedules_worker_spec.rb'
+- './spec/workers/namespaces/root_statistics_worker_spec.rb'
+- './spec/workers/namespaces/schedule_aggregation_worker_spec.rb'
+- './spec/workers/namespaces/update_root_statistics_worker_spec.rb'
+- './spec/workers/new_issue_worker_spec.rb'
+- './spec/workers/new_merge_request_worker_spec.rb'
+- './spec/workers/new_note_worker_spec.rb'
+- './spec/workers/object_pool/create_worker_spec.rb'
+- './spec/workers/object_pool/destroy_worker_spec.rb'
+- './spec/workers/object_pool/join_worker_spec.rb'
+- './spec/workers/packages/cleanup/execute_policy_worker_spec.rb'
+- './spec/workers/packages/cleanup_package_file_worker_spec.rb'
+- './spec/workers/packages/cleanup_package_registry_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/generate_distribution_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/mark_package_files_for_destruction_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/pages_domain_ssl_renewal_cron_worker_spec.rb'
+- './spec/workers/pages_domain_ssl_renewal_worker_spec.rb'
+- './spec/workers/pages_domain_verification_cron_worker_spec.rb'
+- './spec/workers/pages_domain_verification_worker_spec.rb'
+- './spec/workers/pages/invalidate_domain_cache_worker_spec.rb'
+- './spec/workers/pages_worker_spec.rb'
+- './spec/workers/partition_creation_worker_spec.rb'
+- './spec/workers/personal_access_tokens/expired_notification_worker_spec.rb'
+- './spec/workers/personal_access_tokens/expiring_worker_spec.rb'
+- './spec/workers/pipeline_hooks_worker_spec.rb'
+- './spec/workers/pipeline_metrics_worker_spec.rb'
+- './spec/workers/pipeline_notification_worker_spec.rb'
+- './spec/workers/pipeline_process_worker_spec.rb'
+- './spec/workers/pipeline_schedule_worker_spec.rb'
+- './spec/workers/post_receive_spec.rb'
+- './spec/workers/process_commit_worker_spec.rb'
+- './spec/workers/project_cache_worker_spec.rb'
+- './spec/workers/project_destroy_worker_spec.rb'
+- './spec/workers/project_export_worker_spec.rb'
+- './spec/workers/projects/after_import_worker_spec.rb'
+- './spec/workers/projects/git_garbage_collect_worker_spec.rb'
+- './spec/workers/projects/import_export/relation_export_worker_spec.rb'
+- './spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb'
+- './spec/workers/projects/inactive_projects_deletion_notification_worker_spec.rb'
+- './spec/workers/projects/post_creation_worker_spec.rb'
+- './spec/workers/projects/process_sync_events_worker_spec.rb'
+- './spec/workers/projects/record_target_platforms_worker_spec.rb'
+- './spec/workers/projects/refresh_build_artifacts_size_statistics_worker_spec.rb'
+- './spec/workers/projects/schedule_bulk_repository_shard_moves_worker_spec.rb'
+- './spec/workers/projects/schedule_refresh_build_artifacts_size_statistics_worker_spec.rb'
+- './spec/workers/projects/update_repository_storage_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/propagate_integration_worker_spec.rb'
+- './spec/workers/prune_old_events_worker_spec.rb'
+- './spec/workers/purge_dependency_proxy_cache_worker_spec.rb'
+- './spec/workers/reactive_caching_worker_spec.rb'
+- './spec/workers/rebase_worker_spec.rb'
+- './spec/workers/releases/create_evidence_worker_spec.rb'
+- './spec/workers/releases/manage_evidence_worker_spec.rb'
+- './spec/workers/remote_mirror_notification_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/remove_unreferenced_lfs_objects_worker_spec.rb'
+- './spec/workers/repository_check/batch_worker_spec.rb'
+- './spec/workers/repository_check/clear_worker_spec.rb'
+- './spec/workers/repository_check/dispatch_worker_spec.rb'
+- './spec/workers/repository_check/single_repository_worker_spec.rb'
+- './spec/workers/repository_cleanup_worker_spec.rb'
+- './spec/workers/repository_fork_worker_spec.rb'
+- './spec/workers/repository_import_worker_spec.rb'
+- './spec/workers/repository_update_remote_mirror_worker_spec.rb'
+- './spec/workers/run_pipeline_schedule_worker_spec.rb'
+- './spec/workers/schedule_merge_request_cleanup_refs_worker_spec.rb'
+- './spec/workers/schedule_migrate_external_diffs_worker_spec.rb'
+- './spec/workers/self_monitoring_project_create_worker_spec.rb'
+- './spec/workers/self_monitoring_project_delete_worker_spec.rb'
+- './spec/workers/service_desk_email_receiver_worker_spec.rb'
+- './spec/workers/snippets/schedule_bulk_repository_shard_moves_worker_spec.rb'
+- './spec/workers/snippets/update_repository_storage_worker_spec.rb'
+- './spec/workers/ssh_keys/expired_notification_worker_spec.rb'
+- './spec/workers/ssh_keys/expiring_soon_notification_worker_spec.rb'
+- './spec/workers/stage_update_worker_spec.rb'
+- './spec/workers/stuck_ci_jobs_worker_spec.rb'
+- './spec/workers/stuck_export_jobs_worker_spec.rb'
+- './spec/workers/stuck_merge_jobs_worker_spec.rb'
+- './spec/workers/system_hook_push_worker_spec.rb'
+- './spec/workers/tasks_to_be_done/create_worker_spec.rb'
+- './spec/workers/terraform/states/destroy_worker_spec.rb'
+- './spec/workers/todos_destroyer/confidential_issue_worker_spec.rb'
+- './spec/workers/todos_destroyer/destroyed_designs_worker_spec.rb'
+- './spec/workers/todos_destroyer/destroyed_issuable_worker_spec.rb'
+- './spec/workers/todos_destroyer/entity_leave_worker_spec.rb'
+- './spec/workers/todos_destroyer/group_private_worker_spec.rb'
+- './spec/workers/todos_destroyer/private_features_worker_spec.rb'
+- './spec/workers/todos_destroyer/project_private_worker_spec.rb'
+- './spec/workers/trending_projects_worker_spec.rb'
+- './spec/workers/update_container_registry_info_worker_spec.rb'
+- './spec/workers/update_external_pull_requests_worker_spec.rb'
+- './spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb'
+- './spec/workers/update_highest_role_worker_spec.rb'
+- './spec/workers/update_merge_requests_worker_spec.rb'
+- './spec/workers/update_project_statistics_worker_spec.rb'
+- './spec/workers/upload_checksum_worker_spec.rb'
+- './spec/workers/users/create_statistics_worker_spec.rb'
+- './spec/workers/users/deactivate_dormant_users_worker_spec.rb'
+- './spec/workers/user_status_cleanup/batch_worker_spec.rb'
+- './spec/workers/wait_for_cluster_creation_worker_spec.rb'
+- './spec/workers/web_hooks/log_destroy_worker_spec.rb'
+- './spec/workers/web_hook_worker_spec.rb'
+- './spec/workers/wikis/git_garbage_collect_worker_spec.rb'
+- './spec/workers/x509_certificate_revoke_worker_spec.rb'
+- './spec/workers/x509_issuer_crl_check_worker_spec.rb'
diff --git a/spec/support/seed.rb b/spec/support/seed.rb
deleted file mode 100644
index 36cb819763b..00000000000
--- a/spec/support/seed.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-RSpec.configure do |config|
- config.include SeedHelper, :seed_helper
-
- config.before(:all, :seed_helper) do
- ensure_seeds
- end
-end
diff --git a/spec/support/shared_contexts/bulk_imports_requests_shared_context.rb b/spec/support/shared_contexts/bulk_imports_requests_shared_context.rb
index 5fcb14e075a..2f74d3131ab 100644
--- a/spec/support/shared_contexts/bulk_imports_requests_shared_context.rb
+++ b/spec/support/shared_contexts/bulk_imports_requests_shared_context.rb
@@ -25,15 +25,15 @@ RSpec.shared_context 'bulk imports requests context' do |url|
stub_request(:get, "https://gitlab.example.com/api/v4/groups?min_access_level=50&page=1&per_page=20&private_token=demo-pat&search=test&top_level_only=true")
.with(headers: request_headers)
.to_return(status: 200,
- body: [{
- id: 2595440,
- web_url: 'https://gitlab.com/groups/test',
- name: 'Test',
- path: 'stub-test-group',
- full_name: 'Test',
- full_path: 'stub-test-group'
- }].to_json,
- headers: page_response_headers)
+ body: [{
+ id: 2595440,
+ web_url: 'https://gitlab.com/groups/test',
+ name: 'Test',
+ path: 'stub-test-group',
+ full_name: 'Test',
+ full_path: 'stub-test-group'
+ }].to_json,
+ headers: page_response_headers)
stub_request(:get, "%{url}/api/v4/groups?min_access_level=50&page=1&per_page=20&private_token=demo-pat&search=&top_level_only=true" % { url: url })
.to_return(
diff --git a/spec/support/shared_contexts/design_management_shared_contexts.rb b/spec/support/shared_contexts/design_management_shared_contexts.rb
index e6ae7e03664..d89bcada1df 100644
--- a/spec/support/shared_contexts/design_management_shared_contexts.rb
+++ b/spec/support/shared_contexts/design_management_shared_contexts.rb
@@ -14,23 +14,23 @@ RSpec.shared_context 'four designs in three versions' do
let_it_be(:first_version) do
create(:design_version, issue: issue,
- created_designs: [design_a],
- modified_designs: [],
- deleted_designs: [])
+ created_designs: [design_a],
+ modified_designs: [],
+ deleted_designs: [])
end
let_it_be(:second_version) do
create(:design_version, issue: issue,
- created_designs: [design_b, design_c, design_d],
- modified_designs: [design_a],
- deleted_designs: [])
+ created_designs: [design_b, design_c, design_d],
+ modified_designs: [design_a],
+ deleted_designs: [])
end
let_it_be(:third_version) do
create(:design_version, issue: issue,
- created_designs: [],
- modified_designs: [design_a],
- deleted_designs: [design_d])
+ created_designs: [],
+ modified_designs: [design_a],
+ deleted_designs: [design_d])
end
before do
diff --git a/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb
index 4c003dff947..91b6baac610 100644
--- a/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb
+++ b/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb
@@ -55,33 +55,33 @@ RSpec.shared_context 'MergeRequestsFinder multiple projects with merge requests
let!(:merge_request1) do
create(:merge_request, assignees: [user], author: user, reviewers: [user2],
- source_project: project2, target_project: project1,
- target_branch: 'merged-target')
+ source_project: project2, target_project: project1,
+ target_branch: 'merged-target')
end
let!(:merge_request2) do
create(:merge_request, :conflict, assignees: [user], author: user, reviewers: [user2],
- source_project: project2, target_project: project1,
- state: 'closed')
+ source_project: project2, target_project: project1,
+ state: 'closed')
end
let!(:merge_request3) do
create(:merge_request, :simple, author: user, assignees: [user2], reviewers: [user],
- source_project: project2, target_project: project2,
- state: 'locked',
- title: 'thing WIP thing')
+ source_project: project2, target_project: project2,
+ state: 'locked',
+ title: 'thing WIP thing')
end
let!(:merge_request4) do
create(:merge_request, :simple, author: user,
- source_project: project3, target_project: project3,
- title: 'WIP thing')
+ source_project: project3, target_project: project3,
+ title: 'WIP thing')
end
let_it_be(:merge_request5) do
create(:merge_request, :simple, author: user,
- source_project: project4, target_project: project4,
- title: '[WIP]')
+ source_project: project4, target_project: project4,
+ title: '[WIP]')
end
let!(:label_link) { create(:label_link, label: label, target: merge_request2) }
diff --git a/spec/support/shared_contexts/glfm/api_markdown_snapshot_shared_context.rb b/spec/support/shared_contexts/glfm/api_markdown_snapshot_shared_context.rb
new file mode 100644
index 00000000000..3623fa0850d
--- /dev/null
+++ b/spec/support/shared_contexts/glfm/api_markdown_snapshot_shared_context.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require_relative '../../../../scripts/lib/glfm/constants'
+
+# See https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#markdown-snapshot-testing
+# for documentation on this spec.
+RSpec.shared_context 'with API::Markdown Snapshot shared context' do |ee_only: false|
+ include_context 'with GLFM example snapshot fixtures'
+
+ include ApiHelpers
+
+ markdown_examples, html_examples, normalizations_by_example_name, metadata_by_example_name = [
+ Glfm::Constants::ES_MARKDOWN_YML_PATH,
+ Glfm::Constants::ES_HTML_YML_PATH,
+ Glfm::Constants::GLFM_EXAMPLE_NORMALIZATIONS_YML_PATH,
+ Glfm::Constants::GLFM_EXAMPLE_METADATA_YML_PATH
+ ].map { |path| YAML.safe_load(File.open(path), symbolize_names: true, aliases: true) }
+
+ if (focused_markdown_examples_string = ENV['FOCUSED_MARKDOWN_EXAMPLES'])
+ focused_markdown_examples = focused_markdown_examples_string.split(',').map(&:strip).map(&:to_sym)
+ markdown_examples.select! { |example_name| focused_markdown_examples.include?(example_name) }
+ end
+
+ markdown_examples.select! { |example_name| !!metadata_by_example_name&.dig(example_name, :ee) == ee_only }
+
+ markdown_examples.each do |name, markdown|
+ context "for #{name}" do
+ let(:html) { html_examples.fetch(name).fetch(:static) }
+ let(:normalizations) { normalizations_by_example_name.dig(name, :html, :static, :snapshot) }
+
+ it "verifies conversion of GLFM to HTML", :unlimited_max_formatted_output_length do
+ # noinspection RubyResolve
+ normalized_html = normalize_html(html, normalizations)
+ api_url = metadata_by_example_name&.dig(name, :api_request_override_path) || (api "/markdown")
+
+ post api_url, params: { text: markdown, gfm: true }
+ expect(response).to be_successful
+ parsed_response = Gitlab::Json.parse(response.body, symbolize_names: true)
+ # Some responses have the HTML in the `html` key, others in the `body` key.
+ response_html = parsed_response[:body] || parsed_response[:html]
+ normalized_response_html = normalize_html(response_html, normalizations)
+
+ expect(normalized_response_html).to eq(normalized_html)
+ end
+
+ def normalize_html(html, normalizations)
+ return html unless normalizations
+
+ normalized_html = html.dup
+ normalizations.each_value do |normalization_entry|
+ normalization_entry.each do |normalization|
+ regex = normalization.fetch(:regex)
+ replacement = normalization.fetch(:replacement)
+ normalized_html.gsub!(%r{#{regex}}, replacement)
+ end
+ end
+
+ normalized_html
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_contexts/glfm/example_snapshot_fixtures.rb b/spec/support/shared_contexts/glfm/example_snapshot_fixtures.rb
new file mode 100644
index 00000000000..22b401bc841
--- /dev/null
+++ b/spec/support/shared_contexts/glfm/example_snapshot_fixtures.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'with GLFM example snapshot fixtures' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:group) { create(:group, name: 'glfm_group').tap { |group| group.add_owner(user) } }
+
+ let_it_be(:project) do
+ # NOTE: We hardcode the IDs on all fixtures to prevent variability in the
+ # rendered HTML/Prosemirror JSON, and to minimize the need for normalization:
+ # https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#normalization
+ create(:project, :repository, creator: user, group: group, name: 'glfm_project', id: 77777)
+ end
+
+ let_it_be(:project_snippet) { create(:project_snippet, title: 'glfm_project_snippet', id: 88888, project: project) }
+ let_it_be(:personal_snippet) { create(:snippet, id: 99999) }
+
+ before do
+ # Set 'GITLAB_TEST_FOOTNOTE_ID' in order to override random number generation in
+ # Banzai::Filter::FootnoteFilter#random_number, and thus avoid the need to
+ # perform normalization on the value. See:
+ # https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#normalization
+ stub_env('GITLAB_TEST_FOOTNOTE_ID', 42)
+
+ stub_licensed_features(group_wikis: true)
+ sign_in(user)
+ end
+end
diff --git a/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb b/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb
index b29a231f3a6..d7cfdc09732 100644
--- a/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb
+++ b/spec/support/shared_contexts/graphql/requests/packages_shared_context.rb
@@ -16,8 +16,8 @@ RSpec.shared_context 'package details setup' do
let(:metadata_response) { graphql_data_at(:package, :metadata) }
let(:first_file) { package.package_files.find { |f| a_graphql_entity_for(f).matches?(first_file_response) } }
let(:package_files_response) { graphql_data_at(:package, :package_files, :nodes) }
- let(:first_file_response) { graphql_data_at(:package, :package_files, :nodes, 0)}
- let(:first_file_response_metadata) { graphql_data_at(:package, :package_files, :nodes, 0, :file_metadata)}
+ let(:first_file_response) { graphql_data_at(:package, :package_files, :nodes, 0) }
+ let(:first_file_response_metadata) { graphql_data_at(:package, :package_files, :nodes, 0, :file_metadata) }
let(:query) do
graphql_query_for(:package, { id: package_global_id }, <<~FIELDS)
diff --git a/spec/support/shared_contexts/markdown_golden_master_shared_examples.rb b/spec/support/shared_contexts/markdown_golden_master_shared_examples.rb
index dea03af2248..168aef0f174 100644
--- a/spec/support/shared_contexts/markdown_golden_master_shared_examples.rb
+++ b/spec/support/shared_contexts/markdown_golden_master_shared_examples.rb
@@ -42,7 +42,7 @@ RSpec.shared_context 'API::Markdown Golden Master shared context' do |markdown_y
if focused_markdown_examples_string = ENV['FOCUSED_MARKDOWN_EXAMPLES']
focused_markdown_examples = focused_markdown_examples_string.split(',').map(&:strip) || []
- markdown_examples.reject! {|markdown_example| !focused_markdown_examples.include?(markdown_example.fetch(:name)) }
+ markdown_examples.reject! { |markdown_example| !focused_markdown_examples.include?(markdown_example.fetch(:name)) }
end
markdown_examples.each do |markdown_example|
diff --git a/spec/support/shared_contexts/markdown_snapshot_shared_examples.rb b/spec/support/shared_contexts/markdown_snapshot_shared_examples.rb
deleted file mode 100644
index 040b2da9f37..00000000000
--- a/spec/support/shared_contexts/markdown_snapshot_shared_examples.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-# See https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#markdown-snapshot-testing
-# for documentation on this spec.
-# rubocop:disable Layout/LineLength
-RSpec.shared_context 'with API::Markdown Snapshot shared context' do |glfm_specification_dir|
- # rubocop:enable Layout/LineLength
- include ApiHelpers
-
- let_it_be(:user) { create(:user) }
- let_it_be(:api_url) { api('/markdown', user) }
-
- markdown_examples, html_examples = %w[markdown.yml html.yml].map do |file_name|
- yaml = File.read("#{glfm_specification_dir}/example_snapshots/#{file_name}")
- YAML.safe_load(yaml, symbolize_names: true, aliases: true)
- end
-
- normalizations_yaml = File.read(
- "#{glfm_specification_dir}/input/gitlab_flavored_markdown/glfm_example_normalizations.yml")
- normalizations_by_example_name = YAML.safe_load(normalizations_yaml, symbolize_names: true, aliases: true)
-
- if (focused_markdown_examples_string = ENV['FOCUSED_MARKDOWN_EXAMPLES'])
- focused_markdown_examples = focused_markdown_examples_string.split(',').map(&:strip).map(&:to_sym)
- markdown_examples.select! { |example_name| focused_markdown_examples.include?(example_name) }
- end
-
- markdown_examples.each do |name, markdown|
- context "for #{name}" do
- let(:html) { html_examples.fetch(name).fetch(:static) }
- let(:normalizations) { normalizations_by_example_name.dig(name, :html, :static, :snapshot) }
-
- it "verifies conversion of GLFM to HTML", :unlimited_max_formatted_output_length do
- # noinspection RubyResolve
- normalized_html = normalize_html(html, normalizations)
-
- post api_url, params: { text: markdown, gfm: true }
- expect(response).to be_successful
- response_body = Gitlab::Json.parse(response.body)
- # Some requests have the HTML in the `html` key, others in the `body` key.
- response_html = response_body['body'] ? response_body.fetch('body') : response_body.fetch('html')
- # noinspection RubyResolve
- normalized_response_html = normalize_html(response_html, normalizations)
-
- expect(normalized_response_html).to eq(normalized_html)
- end
-
- def normalize_html(html, normalizations)
- return html unless normalizations
-
- normalized_html = html.dup
- normalizations.each_value do |normalization_entry|
- normalization_entry.each do |normalization|
- regex = normalization.fetch(:regex)
- replacement = normalization.fetch(:replacement)
- normalized_html.gsub!(%r{#{regex}}, replacement)
- end
- end
-
- normalized_html
- end
- end
- end
-end
diff --git a/spec/support/shared_contexts/navbar_structure_context.rb b/spec/support/shared_contexts/navbar_structure_context.rb
index 6c2ed79b343..064e40287be 100644
--- a/spec/support/shared_contexts/navbar_structure_context.rb
+++ b/spec/support/shared_contexts/navbar_structure_context.rb
@@ -109,8 +109,9 @@ RSpec.shared_context 'project navbar structure' do
_('Webhooks'),
_('Access Tokens'),
_('Repository'),
+ _('Merge requests'),
_('CI/CD'),
- _('Packages & Registries'),
+ _('Packages and registries'),
_('Monitor'),
s_('UsageQuota|Usage Quotas')
]
@@ -139,7 +140,17 @@ RSpec.shared_context 'group navbar structure' do
_('Repository'),
_('CI/CD'),
_('Applications'),
- _('Packages & Registries')
+ _('Packages and registries'),
+ _('Domain Verification')
+ ]
+ }
+ end
+
+ let(:settings_for_maintainer_nav_item) do
+ {
+ nav_item: _('Settings'),
+ nav_sub_items: [
+ _('Repository')
]
}
end
@@ -162,13 +173,6 @@ RSpec.shared_context 'group navbar structure' do
}
end
- let(:push_rules_nav_item) do
- {
- nav_item: _('Push Rules'),
- nav_sub_items: []
- }
- end
-
let(:ci_cd_nav_item) do
{
nav_item: _('CI/CD'),
@@ -210,7 +214,6 @@ RSpec.shared_context 'group navbar structure' do
nav_sub_items: []
},
(security_and_compliance_nav_item if Gitlab.ee?),
- (push_rules_nav_item if Gitlab.ee?),
{
nav_item: _('Kubernetes'),
nav_sub_items: []
diff --git a/spec/support/shared_contexts/policies/project_policy_shared_context.rb b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
index 1d4731d9b39..fc7255a4a20 100644
--- a/spec/support/shared_contexts/policies/project_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
@@ -6,13 +6,19 @@ RSpec.shared_context 'ProjectPolicy context' do
let_it_be(:reporter) { create(:user) }
let_it_be(:developer) { create(:user) }
let_it_be(:maintainer) { create(:user) }
+ let_it_be(:inherited_guest) { create(:user) }
+ let_it_be(:inherited_reporter) { create(:user) }
+ let_it_be(:inherited_developer) { create(:user) }
+ let_it_be(:inherited_maintainer) { create(:user) }
let_it_be(:owner) { create(:user) }
let_it_be(:admin) { create(:admin) }
let_it_be(:non_member) { create(:user) }
+ let_it_be_with_refind(:group) { create(:group, :public) }
let_it_be_with_refind(:private_project) { create(:project, :private, namespace: owner.namespace) }
let_it_be_with_refind(:internal_project) { create(:project, :internal, namespace: owner.namespace) }
let_it_be_with_refind(:public_project) { create(:project, :public, namespace: owner.namespace) }
- let_it_be_with_refind(:public_project_in_group) { create(:project, :public, namespace: create(:group, :public)) }
+ let_it_be_with_refind(:public_project_in_group) { create(:project, :public, namespace: group) }
+ let_it_be_with_refind(:private_project_in_group) { create(:project, :private, namespace: group) }
let(:base_guest_permissions) do
%i[
@@ -95,6 +101,11 @@ RSpec.shared_context 'ProjectPolicy context' do
let(:owner_permissions) { base_owner_permissions + additional_owner_permissions }
before_all do
+ group.add_guest(inherited_guest)
+ group.add_reporter(inherited_reporter)
+ group.add_developer(inherited_developer)
+ group.add_maintainer(inherited_maintainer)
+
[private_project, internal_project, public_project, public_project_in_group].each do |project|
project.add_guest(guest)
project.add_reporter(reporter)
diff --git a/spec/support/shared_contexts/projects/container_repository/cleanup_tags_service_shared_context.rb b/spec/support/shared_contexts/projects/container_repository/cleanup_tags_service_shared_context.rb
new file mode 100644
index 00000000000..c976bbe9212
--- /dev/null
+++ b/spec/support/shared_contexts/projects/container_repository/cleanup_tags_service_shared_context.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'for a cleanup tags service' do
+ def expected_service_response(status: :success, deleted: [], original_size: tags.size)
+ {
+ status: status,
+ deleted: deleted,
+ original_size: original_size,
+ before_delete_size: deleted&.size
+ }.compact.merge(deleted_size: deleted&.size)
+ end
+
+ def expect_delete(tags, container_expiration_policy: nil)
+ service = instance_double('Projects::ContainerRepository::DeleteTagsService')
+
+ expect(Projects::ContainerRepository::DeleteTagsService)
+ .to receive(:new)
+ .with(repository.project, user, tags: tags, container_expiration_policy: container_expiration_policy)
+ .and_return(service)
+
+ expect(service).to receive(:execute)
+ .with(repository) { { status: :success, deleted: tags } }
+ end
+
+ def expect_no_caching
+ expect(::Gitlab::Redis::Cache).not_to receive(:with)
+ end
+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 b90270356f8..3974338238a 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
@@ -64,5 +64,5 @@ RSpec.shared_context 'conan file upload endpoints' do
let(:jwt) { build_jwt(personal_access_token) }
let(:headers_with_token) { build_token_auth_header(jwt.encoded).merge(workhorse_headers) }
- let(:recipe_path) { "foo/bar/#{project.full_path.tr('/', '+')}/baz"}
+ let(:recipe_path) { "foo/bar/#{project.full_path.tr('/', '+')}/baz" }
end
diff --git a/spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb b/spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb
index 95b8b7ed9f8..cf090c7a185 100644
--- a/spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb
+++ b/spec/support/shared_contexts/requests/api/debian_repository_shared_context.rb
@@ -18,6 +18,11 @@ RSpec.shared_context 'Debian repository shared context' do |container_type, can_
let_it_be(:private_architecture_all, freeze: true) { create("debian_#{container_type}_architecture", distribution: private_distribution, name: 'all') }
let_it_be(:private_architecture, freeze: true) { create("debian_#{container_type}_architecture", distribution: private_distribution, name: 'existing-arch') }
let_it_be(:private_component_file) { create("debian_#{container_type}_component_file", component: private_component, architecture: private_architecture) }
+ let_it_be(:private_component_file_sources) { create("debian_#{container_type}_component_file", :sources, component: private_component) }
+ let_it_be(:private_component_file_di) { create("debian_#{container_type}_component_file", :di_packages, component: private_component, architecture: private_architecture) }
+ let_it_be(:private_component_file_older_sha256) { create("debian_#{container_type}_component_file", :older_sha256, component: private_component, architecture: private_architecture) }
+ let_it_be(:private_component_file_sources_older_sha256) { create("debian_#{container_type}_component_file", :sources, :older_sha256, component: private_component) }
+ let_it_be(:private_component_file_di_older_sha256) { create("debian_#{container_type}_component_file", :di_packages, :older_sha256, component: private_component, architecture: private_architecture) }
let_it_be(:public_distribution, freeze: true) { create("debian_#{container_type}_distribution", :with_file, container: public_container, codename: 'existing-codename') }
let_it_be(:public_distribution_key, freeze: true) { create("debian_#{container_type}_distribution_key", distribution: public_distribution) }
@@ -25,6 +30,11 @@ RSpec.shared_context 'Debian repository shared context' do |container_type, can_
let_it_be(:public_architecture_all, freeze: true) { create("debian_#{container_type}_architecture", distribution: public_distribution, name: 'all') }
let_it_be(:public_architecture, freeze: true) { create("debian_#{container_type}_architecture", distribution: public_distribution, name: 'existing-arch') }
let_it_be(:public_component_file) { create("debian_#{container_type}_component_file", component: public_component, architecture: public_architecture) }
+ let_it_be(:public_component_file_sources) { create("debian_#{container_type}_component_file", :sources, component: public_component) }
+ let_it_be(:public_component_file_di) { create("debian_#{container_type}_component_file", :di_packages, component: public_component, architecture: public_architecture) }
+ let_it_be(:public_component_file_older_sha256) { create("debian_#{container_type}_component_file", :older_sha256, component: public_component, architecture: public_architecture) }
+ let_it_be(:public_component_file_sources_older_sha256) { create("debian_#{container_type}_component_file", :sources, :older_sha256, component: public_component) }
+ let_it_be(:public_component_file_di_older_sha256) { create("debian_#{container_type}_component_file", :di_packages, :older_sha256, component: public_component, architecture: public_architecture) }
if container_type == :group
let_it_be(:private_project) { create(:project, :private, group: private_container) }
@@ -48,7 +58,9 @@ RSpec.shared_context 'Debian repository shared context' do |container_type, can_
let(:distribution) { { private: private_distribution, public: public_distribution }[visibility_level] }
let(:architecture) { { private: private_architecture, public: public_architecture }[visibility_level] }
let(:component) { { private: private_component, public: public_component }[visibility_level] }
- let(:component_file) { { private: private_component_file, public: public_component_file }[visibility_level] }
+ let(:component_file_older_sha256) { { private: private_component_file_older_sha256, public: public_component_file_older_sha256 }[visibility_level] }
+ let(:component_file_sources_older_sha256) { { private: private_component_file_sources_older_sha256, public: public_component_file_sources_older_sha256 }[visibility_level] }
+ let(:component_file_di_older_sha256) { { private: private_component_file_di_older_sha256, public: public_component_file_di_older_sha256 }[visibility_level] }
let(:package) { { private: private_package, public: public_package }[visibility_level] }
let(:letter) { package.name[0..2] == 'lib' ? package.name[0..3] : package.name[0] }
diff --git a/spec/support/shared_contexts/views/html_safe_render_shared_context.rb b/spec/support/shared_contexts/views/html_safe_render_shared_context.rb
new file mode 100644
index 00000000000..3acca60c901
--- /dev/null
+++ b/spec/support/shared_contexts/views/html_safe_render_shared_context.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'when rendered view has no HTML escapes', type: :view do
+ # Check once per example if `rendered` contains HTML escapes.
+ let(:rendered) do |example|
+ super().tap do |rendered|
+ next if example.metadata[:skip_html_escaped_tags_check]
+
+ ensure_no_html_escaped_tags!(rendered, example)
+ end
+ end
+
+ def ensure_no_html_escaped_tags!(content, example)
+ match_data = HtmlEscapedHelpers.match_html_escaped_tags(content)
+ return unless match_data
+
+ # Truncate
+ pre_match = match_data.pre_match.last(50)
+ match = match_data[0]
+ post_match = match_data.post_match.first(50)
+
+ string = "#{pre_match}«#{match}»#{post_match}"
+
+ raise <<~MESSAGE
+ The following string contains HTML escaped tags:
+
+ #{string}
+
+ Please consider using `.html_safe`.
+
+ This check can be disabled via:
+
+ it #{example.description.inspect}, :skip_html_escaped_tags_check do
+ ...
+ end
+
+ MESSAGE
+ end
+end
diff --git a/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb b/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb
index 0e6f6f12c3f..fa048b76e18 100644
--- a/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb
+++ b/spec/support/shared_examples/boards/multiple_issue_boards_shared_examples.rb
@@ -125,6 +125,31 @@ RSpec.shared_examples 'multiple issue boards' do
wait_for_requests
end
+ it 'shows current board name' do
+ page.within('.boards-switcher') do
+ expect(page).to have_content(board.name)
+ end
+ end
+
+ it 'shows a list of boards' do
+ in_boards_switcher_dropdown do
+ expect(page).to have_content(board.name)
+ expect(page).to have_content(board2.name)
+ end
+ end
+
+ it 'switches current board' do
+ in_boards_switcher_dropdown do
+ click_button board2.name
+ end
+
+ wait_for_requests
+
+ page.within('.boards-switcher') do
+ expect(page).to have_content(board2.name)
+ end
+ end
+
it 'does not show action links' do
in_boards_switcher_dropdown do
expect(page).not_to have_content('Create new board')
diff --git a/spec/support/shared_examples/ci/edit_job_token_scope_shared_examples.rb b/spec/support/shared_examples/ci/edit_job_token_scope_shared_examples.rb
index 05b2b5f5de1..d8333ae25ad 100644
--- a/spec/support/shared_examples/ci/edit_job_token_scope_shared_examples.rb
+++ b/spec/support/shared_examples/ci/edit_job_token_scope_shared_examples.rb
@@ -8,14 +8,6 @@ RSpec.shared_examples 'editable job token scope' do
end
end
- context 'when job token scope is disabled for the given project' do
- before do
- allow(project).to receive(:ci_job_token_scope_enabled?).and_return(false)
- end
-
- it_behaves_like 'returns error', 'Job token scope is disabled for this project'
- end
-
context 'when user does not have permissions to edit the job token scope' do
it_behaves_like 'returns error', 'Insufficient permissions to modify the job token scope'
end
diff --git a/spec/support/shared_examples/controllers/concerns/web_hooks/integrations_hook_log_actions_shared_examples.rb b/spec/support/shared_examples/controllers/concerns/web_hooks/integrations_hook_log_actions_shared_examples.rb
new file mode 100644
index 00000000000..62c9c3508a8
--- /dev/null
+++ b/spec/support/shared_examples/controllers/concerns/web_hooks/integrations_hook_log_actions_shared_examples.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples WebHooks::HookLogActions do
+ let!(:show_path) { web_hook_log.present.details_path }
+ let!(:retry_path) { web_hook_log.present.retry_path }
+
+ before do
+ sign_in(user)
+ end
+
+ describe 'GET #show' do
+ it 'renders a 200 if the hook exists' do
+ get show_path
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to render_template('hook_logs/show')
+ end
+
+ it 'renders a 404 if the hook does not exist' do
+ web_hook.destroy!
+ get show_path
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ describe 'POST #retry' do
+ it 'executes the hook and redirects to the service form' do
+ stub_request(:post, web_hook.url)
+
+ expect_next_found_instance_of(web_hook.class) do |hook|
+ expect(hook).to receive(:execute).and_call_original
+ end
+
+ post retry_path
+
+ expect(response).to redirect_to(edit_hook_path)
+ end
+
+ it 'renders a 404 if the hook does not exist' do
+ web_hook.destroy!
+ post retry_path
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/controllers/error_tracking_shared_examples.rb b/spec/support/shared_examples/controllers/error_tracking_shared_examples.rb
index 08e5efcf63c..1bf2f158504 100644
--- a/spec/support/shared_examples/controllers/error_tracking_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/error_tracking_shared_examples.rb
@@ -3,5 +3,5 @@
RSpec.shared_examples 'sets the polling header' do
subject { response.headers[Gitlab::PollingInterval::HEADER_NAME] }
- it { is_expected.to eq '1000'}
+ it { is_expected.to eq '1000' }
end
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 5faf462c23c..bbbe93a644f 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
@@ -241,12 +241,11 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
let(:provider_user) { double('user', login: provider_username) }
let(:project) { create(:project, import_type: provider, import_status: :finished, import_source: "#{provider_username}/vim") }
let(:provider_repo) do
- double(
- 'provider',
+ {
name: 'vim',
full_name: "#{provider_username}/vim",
owner: double('owner', login: provider_username)
- )
+ }
end
before do
@@ -256,7 +255,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
it 'returns 200 response when the project is imported successfully' do
allow(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, type: provider, **access_params)
+ .to receive(:new).with(provider_repo, provider_repo[:name], user.namespace, user, type: provider, **access_params)
.and_return(double(execute: project))
post :create, format: :json
@@ -270,7 +269,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
project.errors.add(:path, 'is old')
allow(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, type: provider, **access_params)
+ .to receive(:new).with(provider_repo, provider_repo[:name], user.namespace, user, type: provider, **access_params)
.and_return(double(execute: project))
post :create, format: :json
@@ -281,7 +280,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
it "touches the etag cache store" do
allow(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, type: provider, **access_params)
+ .to receive(:new).with(provider_repo, provider_repo[:name], user.namespace, user, type: provider, **access_params)
.and_return(double(execute: project))
expect_next_instance_of(Gitlab::EtagCaching::Store) do |store|
expect(store).to receive(:touch) { "realtime_changes_import_#{provider}_path" }
@@ -294,7 +293,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
context "when the provider user and GitLab user's usernames match" do
it "takes the current user's namespace" do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, type: provider, **access_params)
+ .to receive(:new).with(provider_repo, provider_repo[:name], user.namespace, user, type: provider, **access_params)
.and_return(double(execute: project))
post :create, format: :json
@@ -306,7 +305,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
it "takes the current user's namespace" do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, type: provider, **access_params)
+ .to receive(:new).with(provider_repo, provider_repo[:name], user.namespace, user, type: provider, **access_params)
.and_return(double(execute: project))
post :create, format: :json
@@ -331,7 +330,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
it "takes the existing namespace" do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, provider_repo.name, existing_namespace, user, type: provider, **access_params)
+ .to receive(:new).with(provider_repo, provider_repo[:name], existing_namespace, user, type: provider, **access_params)
.and_return(double(execute: project))
post :create, format: :json
@@ -343,7 +342,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
create(:user, username: provider_username)
expect(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, type: provider, **access_params)
+ .to receive(:new).with(provider_repo, provider_repo[:name], user.namespace, user, type: provider, **access_params)
.and_return(double(execute: project))
post :create, format: :json
@@ -357,15 +356,15 @@ 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
expect(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, provider_repo.name, an_instance_of(Group), user, type: provider, **access_params)
+ .to receive(:new).with(provider_repo, provider_repo[:name], an_instance_of(Group), user, type: provider, **access_params)
.and_return(double(execute: project))
- post :create, params: { target_namespace: provider_repo.name }, format: :json
+ post :create, params: { target_namespace: provider_repo[:name] }, format: :json
end
end
@@ -383,7 +382,7 @@ RSpec.shared_examples 'a GitHub-ish import controller: POST create' do
it "takes the current user's namespace" do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, type: provider, **access_params)
+ .to receive(:new).with(provider_repo, provider_repo[:name], user.namespace, user, type: provider, **access_params)
.and_return(double(execute: project))
post :create, format: :json
diff --git a/spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb b/spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb
index aa4d78b23f4..112b9cbb204 100644
--- a/spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/snippets_sort_order_shared_examples.rb
@@ -3,7 +3,7 @@
RSpec.shared_examples 'snippets sort order' do
let(:params) { {} }
let(:sort_argument) { {} }
- let(:sort_params) { params.merge(sort_argument)}
+ let(:sort_params) { params.merge(sort_argument) }
before do
sign_in(user)
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 2e691d1b36f..4af3c0cc6cc 100644
--- a/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb
+++ b/spec/support/shared_examples/controllers/snowplow_event_tracking_examples.rb
@@ -13,7 +13,7 @@
# - label
# - **extra
-shared_examples 'Snowplow event tracking' do |overrides: {}|
+RSpec.shared_examples 'Snowplow event tracking' do |overrides: {}|
let(:extra) { {} }
it 'is not emitted if FF is disabled' do
diff --git a/spec/support/shared_examples/features/board_sidebar_labels_examples.rb b/spec/support/shared_examples/features/board_sidebar_labels_examples.rb
index 520980c2615..4e5b371c18d 100644
--- a/spec/support/shared_examples/features/board_sidebar_labels_examples.rb
+++ b/spec/support/shared_examples/features/board_sidebar_labels_examples.rb
@@ -17,7 +17,7 @@ RSpec.shared_context 'labels from nested groups and projects' do
let_it_be(:maintainer) { create(:user) }
let(:labels_select) { find("[data-testid='sidebar-labels']") }
- let(:labels_dropdown) { labels_select.find('[data-testid="dropdown-content"]')}
+ let(:labels_dropdown) { labels_select.find('[data-testid="dropdown-content"]') }
before do
group.add_maintainer(maintainer)
diff --git a/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb b/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb
index 8a07e52019c..f7cdc4c61ec 100644
--- a/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb
+++ b/spec/support/shared_examples/features/comments_on_merge_request_files_shared_examples.rb
@@ -1,6 +1,10 @@
# frozen_string_literal: true
RSpec.shared_examples 'comment on merge request file' do
+ before do
+ stub_feature_flags(remove_user_attributes_projects: false)
+ end
+
it 'adds a comment' do
click_diff_line(find_by_scrolling("[id='#{sample_commit.line_code}']"))
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 3fa7beea97e..21f264a8b6a 100644
--- a/spec/support/shared_examples/features/content_editor_shared_examples.rb
+++ b/spec/support/shared_examples/features/content_editor_shared_examples.rb
@@ -1,22 +1,87 @@
# frozen_string_literal: true
RSpec.shared_examples 'edits content using the content editor' do
- content_editor_testid = '[data-testid="content-editor"] [contenteditable].ProseMirror'
+ let(:content_editor_testid) { '[data-testid="content-editor"] [contenteditable].ProseMirror' }
+
+ def switch_to_content_editor
+ find('[data-testid="toggle-editing-mode-button"] label', text: 'Rich text').click
+ end
+
+ def type_in_content_editor(keys)
+ find(content_editor_testid).send_keys keys
+ end
+
+ def open_insert_media_dropdown
+ page.find('svg[data-testid="media-icon"]').click
+ end
+
+ def set_source_editor_content(content)
+ find('.js-gfm-input').set content
+ end
+
+ def expect_formatting_menu_to_be_visible
+ expect(page).to have_css('[data-testid="formatting-bubble-menu"]')
+ end
+
+ def expect_formatting_menu_to_be_hidden
+ expect(page).not_to have_css('[data-testid="formatting-bubble-menu"]')
+ end
+
+ def expect_media_bubble_menu_to_be_visible
+ expect(page).to have_css('[data-testid="media-bubble-menu"]')
+ end
+
+ def upload_asset(fixture_name)
+ attach_file('content_editor_image', Rails.root.join('spec', 'fixtures', fixture_name), make_visible: true)
+ end
describe 'formatting bubble menu' do
- it 'shows a formatting bubble menu for a regular paragraph' do
+ it 'shows a formatting bubble menu for a regular paragraph and headings' do
+ switch_to_content_editor
+
expect(page).to have_css(content_editor_testid)
- find(content_editor_testid).send_keys 'Typing text in the content editor'
- find(content_editor_testid).send_keys [:shift, :left]
+ type_in_content_editor 'Typing text in the content editor'
+ type_in_content_editor [:shift, :left]
+
+ expect_formatting_menu_to_be_visible
+
+ type_in_content_editor [:right, :right, :enter, '## Heading']
- expect(page).to have_css('[data-testid="formatting-bubble-menu"]')
+ expect_formatting_menu_to_be_hidden
+
+ type_in_content_editor [:shift, :left]
+
+ expect_formatting_menu_to_be_visible
+ end
+ end
+
+ describe 'media elements bubble menu' do
+ before do
+ switch_to_content_editor
+
+ open_insert_media_dropdown
+ end
+
+ def test_displays_media_bubble_menu(media_element_selector, fixture_file)
+ upload_asset fixture_file
+
+ wait_for_requests
+
+ expect(page).to have_css(media_element_selector)
+
+ page.find(media_element_selector).click
+
+ expect_formatting_menu_to_be_hidden
+ expect_media_bubble_menu_to_be_visible
end
- it 'does not show a formatting bubble menu for code blocks' do
- find(content_editor_testid).send_keys '```js '
+ it 'displays correct media bubble menu for images', :js do
+ test_displays_media_bubble_menu '[data-testid="content_editor_editablebox"] img[src]', 'dk.png'
+ end
- expect(page).not_to have_css('[data-testid="formatting-bubble-menu"]')
+ it 'displays correct media bubble menu for video', :js do
+ test_displays_media_bubble_menu '[data-testid="content_editor_editablebox"] video', 'video_sample.mp4'
end
end
@@ -30,45 +95,50 @@ RSpec.shared_examples 'edits content using the content editor' do
page.go_back
refresh
+ switch_to_content_editor
end
it 'applies theme classes to code blocks' do
expect(page).not_to have_css('.content-editor-code-block.code.highlight.dark')
- find(content_editor_testid).send_keys [:enter, :enter]
- find(content_editor_testid).send_keys '```js ' # trigger input rule
- find(content_editor_testid).send_keys 'var a = 0'
+ type_in_content_editor [:enter, :enter]
+ type_in_content_editor '```js ' # trigger input rule
+ type_in_content_editor 'var a = 0'
expect(page).to have_css('.content-editor-code-block.code.highlight.dark')
end
end
describe 'code block bubble menu' do
+ before do
+ switch_to_content_editor
+ end
+
it 'shows a code block bubble menu for a code block' do
- find(content_editor_testid).send_keys [:enter, :enter]
+ type_in_content_editor [:enter, :enter]
- find(content_editor_testid).send_keys '```js ' # trigger input rule
- find(content_editor_testid).send_keys 'var a = 0'
- find(content_editor_testid).send_keys [:shift, :left]
+ type_in_content_editor '```js ' # trigger input rule
+ type_in_content_editor 'var a = 0'
+ type_in_content_editor [:shift, :left]
- expect(page).not_to have_css('[data-testid="formatting-bubble-menu"]')
+ expect_formatting_menu_to_be_hidden
expect(page).to have_css('[data-testid="code-block-bubble-menu"]')
end
it 'sets code block type to "javascript" for `js`' do
- find(content_editor_testid).send_keys [:enter, :enter]
+ type_in_content_editor [:enter, :enter]
- find(content_editor_testid).send_keys '```js '
- find(content_editor_testid).send_keys 'var a = 0'
+ type_in_content_editor '```js '
+ type_in_content_editor 'var a = 0'
expect(find('[data-testid="code-block-bubble-menu"]')).to have_text('Javascript')
end
it 'sets code block type to "Custom (nomnoml)" for `nomnoml`' do
- find(content_editor_testid).send_keys [:enter, :enter]
+ type_in_content_editor [:enter, :enter]
- find(content_editor_testid).send_keys '```nomnoml '
- find(content_editor_testid).send_keys 'test'
+ type_in_content_editor '```nomnoml '
+ type_in_content_editor 'test'
expect(find('[data-testid="code-block-bubble-menu"]')).to have_text('Custom (nomnoml)')
end
@@ -76,10 +146,11 @@ RSpec.shared_examples 'edits content using the content editor' do
describe 'mermaid diagram' do
before do
- find(content_editor_testid).send_keys [:enter, :enter]
+ switch_to_content_editor
- find(content_editor_testid).send_keys '```mermaid '
- find(content_editor_testid).send_keys ['graph TD;', :enter, ' JohnDoe12 --> HelloWorld34']
+ type_in_content_editor [:enter, :enter]
+ type_in_content_editor '```mermaid '
+ type_in_content_editor ['graph TD;', :enter, ' JohnDoe12 --> HelloWorld34']
end
it 'renders and updates the diagram correctly in a sandboxed iframe' do
diff --git a/spec/support/shared_examples/features/deploy_token_shared_examples.rb b/spec/support/shared_examples/features/deploy_token_shared_examples.rb
index 25dfe089f51..79ad5bd6c7f 100644
--- a/spec/support/shared_examples/features/deploy_token_shared_examples.rb
+++ b/spec/support/shared_examples/features/deploy_token_shared_examples.rb
@@ -30,6 +30,27 @@ RSpec.shared_examples 'a deploy token in settings' do
expect(page).to have_selector("input[name='deploy-token-user'][value='deployer']")
expect(page).to have_selector("input[name='deploy-token'][readonly='readonly']")
end
+
+ expect(find("input#deploy_token_name").value).to eq nil
+ expect(find("input#deploy_token_read_repository").checked?).to eq false
+ end
+
+ context "with form errors" do
+ before do
+ visit page_path
+ fill_in "deploy_token_name", with: "new_deploy_key"
+ fill_in "deploy_token_username", with: "deployer"
+ click_button "Create deploy token"
+ end
+
+ it "shows form errors" do
+ expect(page).to have_text("Scopes can't be blank")
+ end
+
+ it "keeps form inputs" do
+ expect(find("input#deploy_token_name").value).to eq "new_deploy_key"
+ expect(find("input#deploy_token_username").value).to eq "deployer"
+ end
end
context 'when User#time_display_relative is false', :js do
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 24dc4bcfc59..f209070d82a 100644
--- a/spec/support/shared_examples/features/discussion_comments_shared_example.rb
+++ b/spec/support/shared_examples/features/discussion_comments_shared_example.rb
@@ -304,7 +304,7 @@ RSpec.shared_examples 'thread comments for issue, epic and merge request' do |re
let(:reply_id) { find("#{comments_selector} .note:last-of-type", match: :first)['data-note-id'] }
it 'can be replied to after resolving' do
- find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage
+ find('button[data-testid="resolve-discussion-button"]').click
wait_for_requests
refresh
@@ -316,7 +316,7 @@ RSpec.shared_examples 'thread comments for issue, epic and merge request' do |re
it 'shows resolved thread when toggled' do
submit_reply('a')
- find('button[data-qa-selector="resolve_discussion_button"]').click # rubocop:disable QA/SelectorUsage
+ find('button[data-testid="resolve-discussion-button"]').click
wait_for_requests
expect(page).to have_selector(".note-row-#{note_id}", visible: true)
diff --git a/spec/support/shared_examples/features/manage_applications_shared_examples.rb b/spec/support/shared_examples/features/manage_applications_shared_examples.rb
index 442264e7ae4..b59f3f1e27b 100644
--- a/spec/support/shared_examples/features/manage_applications_shared_examples.rb
+++ b/spec/support/shared_examples/features/manage_applications_shared_examples.rb
@@ -5,39 +5,87 @@ RSpec.shared_examples 'manage applications' do
let_it_be(:application_name_changed) { "#{application_name} changed" }
let_it_be(:application_redirect_uri) { 'https://foo.bar' }
- it 'allows user to manage applications', :js do
- visit new_application_path
+ context 'when hash_oauth_secrets flag set' do
+ before do
+ stub_feature_flags(hash_oauth_secrets: true)
+ end
+
+ it 'allows user to manage applications', :js do
+ visit new_application_path
- expect(page).to have_content 'Add new application'
+ expect(page).to have_content 'Add new application'
- fill_in :doorkeeper_application_name, with: application_name
- fill_in :doorkeeper_application_redirect_uri, with: application_redirect_uri
- check :doorkeeper_application_scopes_read_user
- click_on 'Save application'
+ fill_in :doorkeeper_application_name, with: application_name
+ fill_in :doorkeeper_application_redirect_uri, with: application_redirect_uri
+ check :doorkeeper_application_scopes_read_user
+ click_on 'Save application'
- validate_application(application_name, 'Yes')
- expect(page).to have_link('Continue', href: index_path)
+ validate_application(application_name, 'Yes')
+ expect(page).to have_content _('This is the only time the secret is accessible. Copy the secret and store it securely')
+ expect(page).to have_link('Continue', href: index_path)
- application = Doorkeeper::Application.find_by(name: application_name)
- expect(page).to have_css("button[title=\"Copy secret\"][data-clipboard-text=\"#{application.secret}\"]", text: 'Copy')
+ expect(page).to have_css("button[title=\"Copy secret\"]", text: 'Copy')
- click_on 'Edit'
+ click_on 'Edit'
- application_name_changed = "#{application_name} changed"
+ application_name_changed = "#{application_name} changed"
- fill_in :doorkeeper_application_name, with: application_name_changed
- uncheck :doorkeeper_application_confidential
- click_on 'Save application'
+ fill_in :doorkeeper_application_name, with: application_name_changed
+ uncheck :doorkeeper_application_confidential
+ click_on 'Save application'
+
+ validate_application(application_name_changed, 'No')
+ expect(page).not_to have_link('Continue')
+ expect(page).to have_content _('The secret is only available when you first create the application')
+
+ visit_applications_path
+
+ page.within '.oauth-applications' do
+ click_on 'Destroy'
+ end
+ expect(page.find('.oauth-applications')).not_to have_content 'test_changed'
+ end
+ end
+
+ context 'when hash_oauth_secrets flag not set' do
+ before do
+ stub_feature_flags(hash_oauth_secrets: false)
+ end
+
+ it 'allows user to manage applications', :js do
+ visit new_application_path
+
+ expect(page).to have_content 'Add new application'
+
+ fill_in :doorkeeper_application_name, with: application_name
+ fill_in :doorkeeper_application_redirect_uri, with: application_redirect_uri
+ check :doorkeeper_application_scopes_read_user
+ click_on 'Save application'
+
+ validate_application(application_name, 'Yes')
+ expect(page).to have_link('Continue', href: index_path)
+
+ application = Doorkeeper::Application.find_by(name: application_name)
+ expect(page).to have_css("button[title=\"Copy secret\"][data-clipboard-text=\"#{application.secret}\"]", text: 'Copy')
+
+ click_on 'Edit'
+
+ application_name_changed = "#{application_name} changed"
+
+ fill_in :doorkeeper_application_name, with: application_name_changed
+ uncheck :doorkeeper_application_confidential
+ click_on 'Save application'
- validate_application(application_name_changed, 'No')
- expect(page).not_to have_link('Continue')
+ validate_application(application_name_changed, 'No')
+ expect(page).not_to have_link('Continue')
- visit_applications_path
+ visit_applications_path
- page.within '.oauth-applications' do
- click_on 'Destroy'
+ page.within '.oauth-applications' do
+ click_on 'Destroy'
+ end
+ expect(page.find('.oauth-applications')).not_to have_content 'test_changed'
end
- expect(page.find('.oauth-applications')).not_to have_content 'test_changed'
end
context 'when scopes are blank' do
diff --git a/spec/support/shared_examples/features/packages_shared_examples.rb b/spec/support/shared_examples/features/packages_shared_examples.rb
index 323bd4f5171..7aad5e2de80 100644
--- a/spec/support/shared_examples/features/packages_shared_examples.rb
+++ b/spec/support/shared_examples/features/packages_shared_examples.rb
@@ -14,7 +14,7 @@ RSpec.shared_examples 'packages list' do |check_project_name: false|
end
def package_table_row(index)
- page.all("#{packages_table_selector} > [data-qa-selector=\"package_row\"]")[index].text # rubocop:disable QA/SelectorUsage
+ page.all("#{packages_table_selector} > [data-testid=\"package-row\"]")[index].text
end
end
@@ -84,7 +84,7 @@ RSpec.shared_examples 'shared package sorting' do
end
def packages_table_selector
- '[data-qa-selector="packages-table"]' # rubocop:disable QA/SelectorUsage
+ '[data-testid="packages-table"]'
end
def click_sort_option(option, ascending)
diff --git a/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb b/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb
index 8212f14d6be..81d548e000a 100644
--- a/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb
+++ b/spec/support/shared_examples/features/protected_branches_access_control_ce_shared_examples.rb
@@ -8,7 +8,7 @@ RSpec.shared_examples "protected branches > access control > CE" do
set_protected_branch_name('master')
find(".js-allowed-to-merge").click
- within('.rspec-allowed-to-merge-dropdown') do
+ within('[data-testid="allowed-to-merge-dropdown"]') do
expect(first("li")).to have_content("Roles")
find(:link, 'No one').click
end
@@ -35,13 +35,13 @@ RSpec.shared_examples "protected branches > access control > CE" do
set_protected_branch_name('master')
find(".js-allowed-to-merge").click
- within('.rspec-allowed-to-merge-dropdown') do
+ within('[data-testid="allowed-to-merge-dropdown"]') do
expect(first("li")).to have_content("Roles")
find(:link, 'No one').click
end
find(".js-allowed-to-push").click
- within('.rspec-allowed-to-push-dropdown') do
+ within('[data-testid="allowed-to-push-dropdown"]') do
expect(first("li")).to have_content("Roles")
find(:link, 'No one').click
end
@@ -83,7 +83,7 @@ RSpec.shared_examples "protected branches > access control > CE" do
end
find(".js-allowed-to-push").click
- within('.rspec-allowed-to-push-dropdown') do
+ within('[data-testid="allowed-to-push-dropdown"]') do
expect(first("li")).to have_content("Roles")
find(:link, 'No one').click
end
@@ -100,13 +100,13 @@ RSpec.shared_examples "protected branches > access control > CE" do
set_protected_branch_name('master')
find(".js-allowed-to-merge").click
- within('.rspec-allowed-to-merge-dropdown') do
+ within('[data-testid="allowed-to-merge-dropdown"]') do
expect(first("li")).to have_content("Roles")
find(:link, 'No one').click
end
find(".js-allowed-to-push").click
- within('.rspec-allowed-to-push-dropdown') do
+ within('[data-testid="allowed-to-push-dropdown"]') do
expect(first("li")).to have_content("Roles")
find(:link, 'No one').click
end
diff --git a/spec/support/shared_examples/features/protected_branches_with_deploy_keys_examples.rb b/spec/support/shared_examples/features/protected_branches_with_deploy_keys_examples.rb
index 14142793a0d..90b0e600228 100644
--- a/spec/support/shared_examples/features/protected_branches_with_deploy_keys_examples.rb
+++ b/spec/support/shared_examples/features/protected_branches_with_deploy_keys_examples.rb
@@ -23,7 +23,7 @@ RSpec.shared_examples 'Deploy keys with protected branches' do
find(".js-allowed-to-push").click
wait_for_requests
- within('.qa-allowed-to-push-dropdown') do # rubocop:disable QA/SelectorUsage
+ within('[data-testid="allowed-to-push-dropdown"]') do
dropdown_headers = page.all('.dropdown-header').map(&:text)
expect(dropdown_headers).to contain_exactly(*all_dropdown_sections)
@@ -38,7 +38,7 @@ RSpec.shared_examples 'Deploy keys with protected branches' do
find(".js-allowed-to-merge").click
wait_for_requests
- within('.qa-allowed-to-merge-dropdown') do # rubocop:disable QA/SelectorUsage
+ within('[data-testid="allowed-to-merge-dropdown"]') do
dropdown_headers = page.all('.dropdown-header').map(&:text)
expect(dropdown_headers).to contain_exactly(*dropdown_sections_minus_deploy_keys)
@@ -68,7 +68,7 @@ RSpec.shared_examples 'Deploy keys with protected branches' do
find(".js-allowed-to-push").click
wait_for_requests
- within('.qa-allowed-to-push-dropdown') do # rubocop:disable QA/SelectorUsage
+ within('[data-testid="allowed-to-push-dropdown"]') do
dropdown_headers = page.all('.dropdown-header').map(&:text)
expect(dropdown_headers).to contain_exactly(*dropdown_sections_minus_deploy_keys)
diff --git a/spec/support/shared_examples/features/rss_shared_examples.rb b/spec/support/shared_examples/features/rss_shared_examples.rb
index 0991de21d8d..ad865b084e1 100644
--- a/spec/support/shared_examples/features/rss_shared_examples.rb
+++ b/spec/support/shared_examples/features/rss_shared_examples.rb
@@ -9,7 +9,7 @@ end
RSpec.shared_examples "it has an RSS button with current_user's feed token" do
it "shows the RSS button with current_user's feed token" do
expect(page)
- .to have_css("a:has(.qa-rss-icon)[href*='feed_token=#{user.feed_token}']") # rubocop:disable QA/SelectorUsage
+ .to have_css("a:has([data-testid='rss-icon'])[href*='feed_token=#{user.feed_token}']")
end
end
@@ -22,7 +22,7 @@ end
RSpec.shared_examples "it has an RSS button without a feed token" do
it "shows the RSS button without a feed token" do
expect(page)
- .to have_css("a:has(.qa-rss-icon):not([href*='feed_token'])") # rubocop:disable QA/SelectorUsage
+ .to have_css("a:has([data-testid='rss-icon']):not([href*='feed_token'])")
end
end
diff --git a/spec/support/shared_examples/features/runners_shared_examples.rb b/spec/support/shared_examples/features/runners_shared_examples.rb
index 52f3fd60c07..31ee08ea9db 100644
--- a/spec/support/shared_examples/features/runners_shared_examples.rb
+++ b/spec/support/shared_examples/features/runners_shared_examples.rb
@@ -64,9 +64,9 @@ end
RSpec.shared_examples 'shows no runners registered' do
it 'shows counts with 0' do
- expect(page).to have_text "Online runners 0"
- expect(page).to have_text "Offline runners 0"
- expect(page).to have_text "Stale runners 0"
+ 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"
end
it 'shows "no runners" message' do
@@ -101,7 +101,7 @@ RSpec.shared_examples 'pauses, resumes and deletes a runner' do
within_runner_row(runner.id) do
click_button "Pause"
- expect(page).to have_text 'paused'
+ expect(page).to have_text s_('Runners|Paused')
expect(page).to have_button 'Resume'
expect(page).not_to have_button 'Pause'
@@ -145,3 +145,39 @@ RSpec.shared_examples 'pauses, resumes and deletes a runner' do
end
end
end
+
+RSpec.shared_examples 'submits edit runner form' do
+ it 'breadcrumb contains runner id and token' do
+ page.within '[data-testid="breadcrumb-links"]' do
+ expect(page).to have_link("##{runner.id} (#{runner.short_sha})")
+ expect(page.find('[data-testid="breadcrumb-current-link"]')).to have_content("Edit")
+ end
+ end
+
+ describe 'runner header', :js do
+ it 'contains the runner id' do
+ expect(page).to have_content("Runner ##{runner.id} created")
+ end
+ end
+
+ context 'when a runner is updated', :js do
+ before do
+ find('[data-testid="runner-field-description"] input').set('new-runner-description')
+
+ click_on _('Save changes')
+ wait_for_requests
+ end
+
+ it 'redirects to runner page' do
+ expect(current_url).to match(runner_page_path)
+ end
+
+ it 'show success alert' do
+ expect(page.find('[data-testid="alert-success"]')).to have_content('saved')
+ end
+
+ it 'shows updated information' do
+ expect(page).to have_content("#{s_('Runners|Description')} new-runner-description")
+ end
+ end
+end
diff --git a/spec/support/shared_examples/features/snippets_shared_examples.rb b/spec/support/shared_examples/features/snippets_shared_examples.rb
index c402333107c..bf870b3ce66 100644
--- a/spec/support/shared_examples/features/snippets_shared_examples.rb
+++ b/spec/support/shared_examples/features/snippets_shared_examples.rb
@@ -194,7 +194,7 @@ end
RSpec.shared_examples 'personal snippet with references' do
let_it_be(:project) { create(:project, :repository) }
let_it_be(:merge_request) { create(:merge_request, source_project: project) }
- let_it_be(:project_snippet) { create(:project_snippet, :repository, project: project)}
+ let_it_be(:project_snippet) { create(:project_snippet, :repository, project: project) }
let_it_be(:issue) { create(:issue, project: project) }
let_it_be(:commit) { project.commit }
diff --git a/spec/support/shared_examples/features/variable_list_shared_examples.rb b/spec/support/shared_examples/features/variable_list_shared_examples.rb
index 9d81c0e9a3e..d1e5046a39e 100644
--- a/spec/support/shared_examples/features/variable_list_shared_examples.rb
+++ b/spec/support/shared_examples/features/variable_list_shared_examples.rb
@@ -91,7 +91,7 @@ RSpec.shared_examples 'variable list' do |is_admin|
end
page.within('#add-ci-variable') do
- find('[data-qa-selector="ci_variable_key_field"] input').set('new_key') # rubocop:disable QA/SelectorUsage
+ find('[data-testid="pipeline-form-ci-variable-key"] input').set('new_key')
click_button('Update variable')
end
@@ -173,7 +173,7 @@ RSpec.shared_examples 'variable list' do |is_admin|
click_button('Add variable')
page.within('#add-ci-variable') do
- find('[data-qa-selector="ci_variable_key_field"] input').set('empty_mask_key') # rubocop:disable QA/SelectorUsage
+ find('[data-testid="pipeline-form-ci-variable-key"] input').set('empty_mask_key')
find('[data-testid="ci-variable-protected-checkbox"]').click
find('[data-testid="ci-variable-masked-checkbox"]').click
@@ -290,8 +290,8 @@ RSpec.shared_examples 'variable list' do |is_admin|
wait_for_requests
page.within('#add-ci-variable') do
- find('[data-qa-selector="ci_variable_key_field"] input').set(key) # rubocop:disable QA/SelectorUsage
- find('[data-qa-selector="ci_variable_value_field"]').set(value) if value.present? # rubocop:disable QA/SelectorUsage
+ find('[data-testid="pipeline-form-ci-variable-key"] input').set(key)
+ find('[data-testid="pipeline-form-ci-variable-value"]').set(value) if value.present?
find('[data-testid="ci-variable-protected-checkbox"]').click if protected
find('[data-testid="ci-variable-masked-checkbox"]').click if masked
diff --git a/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb
index 2285d9a17e2..3e285bb8ad7 100644
--- a/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/user_previews_wiki_changes_shared_examples.rb
@@ -64,7 +64,7 @@ RSpec.shared_examples 'User previews wiki changes' do
end
it_behaves_like 'relative links' do
- let(:element) { page.find('[data-testid="wiki_page_content"]') }
+ let(:element) { page.find('[data-testid="wiki-page-content"]') }
end
end
diff --git a/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb
index 87067336a36..5c63d6a973d 100644
--- a/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/user_updates_wiki_page_shared_examples.rb
@@ -137,16 +137,7 @@ RSpec.shared_examples 'User updates wiki page' do
end
end
- context 'when using the content editor' do
- context 'with feature flag on' do
- before do
- find('[data-testid="toggle-editing-mode-button"] label', text: 'Rich text').click
- end
-
- it_behaves_like 'edits content using the content editor'
- end
- end
-
+ it_behaves_like 'edits content using the content editor'
it_behaves_like 'autocompletes items'
end
diff --git a/spec/support/shared_examples/features/wiki/user_views_asciidoc_page_with_includes_shared_examples.rb b/spec/support/shared_examples/features/wiki/user_views_asciidoc_page_with_includes_shared_examples.rb
index 6fdc5ecae73..fde38df558f 100644
--- a/spec/support/shared_examples/features/wiki/user_views_asciidoc_page_with_includes_shared_examples.rb
+++ b/spec/support/shared_examples/features/wiki/user_views_asciidoc_page_with_includes_shared_examples.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
RSpec.shared_examples 'User views AsciiDoc page with includes' do
- let_it_be(:wiki_content_selector) { '[data-qa-selector=wiki_page_content]' } # rubocop:disable QA/SelectorUsage
- let!(:included_wiki_page) { create_wiki_page('included_page', content: 'Content from the included page')}
+ let_it_be(:wiki_content_selector) { '[data-testid=wiki-page-content]' }
+ let!(:included_wiki_page) { create_wiki_page('included_page', content: 'Content from the included page') }
let!(:wiki_page) { create_wiki_page('home', content: "Content from the main page.\ninclude::included_page.asciidoc[]") }
def create_wiki_page(title, content:)
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 049ead9fb89..f62c9c00006 100644
--- a/spec/support/shared_examples/finders/issues_finder_shared_examples.rb
+++ b/spec/support/shared_examples/finders/issues_finder_shared_examples.rb
@@ -365,7 +365,7 @@ RSpec.shared_examples 'issues or work items finder' do |factory, execute_context
let!(:created_items) do
milestones.map do |milestone|
create(factory, project: milestone.project || project_in_group,
- milestone: milestone, author: user, assignees: [user])
+ milestone: milestone, author: user, assignees: [user])
end
end
diff --git a/spec/support/shared_examples/graphql/members_shared_examples.rb b/spec/support/shared_examples/graphql/members_shared_examples.rb
index 110706c730b..5cba8baa829 100644
--- a/spec/support/shared_examples/graphql/members_shared_examples.rb
+++ b/spec/support/shared_examples/graphql/members_shared_examples.rb
@@ -40,7 +40,7 @@ RSpec.shared_examples 'querying members with a group' do
subject do
resolve(described_class, obj: resource, args: base_args.merge(args),
- ctx: { current_user: user_4 }, arg_style: :internal)
+ ctx: { current_user: user_4 }, arg_style: :internal)
end
describe '#resolve' do
@@ -52,6 +52,15 @@ RSpec.shared_examples 'querying members with a group' do
expect(subject).to contain_exactly(resource_member, group_1_member, root_group_member)
end
+ context 'with sort options' do
+ let(:args) { { sort: 'name_asc' } }
+
+ it 'searches users by user name' do
+ # the order is important here
+ expect(subject.items).to eq([root_group_member, resource_member, group_1_member])
+ end
+ end
+
context 'with search' do
context 'when the search term matches a user' do
let(:args) { { search: 'test' } }
@@ -75,7 +84,7 @@ RSpec.shared_examples 'querying members with a group' do
subject do
resolve(described_class, obj: resource, args: base_args.merge(args),
- ctx: { current_user: other_user }, arg_style: :internal)
+ ctx: { current_user: other_user }, arg_style: :internal)
end
it 'generates an error' do
diff --git a/spec/support/shared_examples/graphql/n_plus_one_query_examples.rb b/spec/support/shared_examples/graphql/n_plus_one_query_examples.rb
index 738edd43c92..faf1bb204c9 100644
--- a/spec/support/shared_examples/graphql/n_plus_one_query_examples.rb
+++ b/spec/support/shared_examples/graphql/n_plus_one_query_examples.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
-shared_examples 'N+1 query check' do
+RSpec.shared_examples 'N+1 query check' do
it 'prevents N+1 queries' do
execute_query # "warm up" to prevent undeterministic counts
expect(graphql_errors).to be_blank # Sanity check - ex falso quodlibet!
diff --git a/spec/support/shared_examples/graphql/resolvers/issuable_resolvers_shared_examples.rb b/spec/support/shared_examples/graphql/resolvers/issuable_resolvers_shared_examples.rb
new file mode 100644
index 00000000000..25008bca619
--- /dev/null
+++ b/spec/support/shared_examples/graphql/resolvers/issuable_resolvers_shared_examples.rb
@@ -0,0 +1,99 @@
+# frozen_string_literal: true
+
+# Requires `parent`, issuable1`, `issuable2`, `issuable3`, `issuable4`,
+# `finder_class` and `optimization_param` bindings.
+RSpec.shared_examples 'graphql query for searching issuables' do
+ it 'uses search optimization' do
+ expected_arguments = a_hash_including(
+ search: 'text',
+ optimization_param => true
+ )
+ expect(finder_class).to receive(:new).with(anything, expected_arguments).and_call_original
+
+ resolve_issuables(search: 'text')
+ end
+
+ it 'filters issuables by title' do
+ issuables = resolve_issuables(search: 'created')
+
+ expect(issuables).to contain_exactly(issuable1, issuable2)
+ end
+
+ it 'filters issuables by description' do
+ issuables = resolve_issuables(search: 'text')
+
+ expect(issuables).to contain_exactly(issuable2, issuable3)
+ end
+
+ context 'with in param' do
+ it 'generates an error if param search is missing' do
+ error_message = "`search` should be present when including the `in` argument"
+
+ expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, error_message) do
+ resolve_issuables(in: ['title'])
+ end
+ end
+
+ it 'filters issuables by title and description' do
+ issuable4.update!(title: 'fourth text')
+ issuables = resolve_issuables(search: 'text', in: %w[title description])
+
+ expect(issuables).to contain_exactly(issuable2, issuable3, issuable4)
+ end
+
+ it 'filters issuables by description only' do
+ with_text = resolve_issuables(search: 'text', in: ['description'])
+ with_created = resolve_issuables(search: 'created', in: ['description'])
+
+ expect(with_created).to be_empty
+ expect(with_text).to contain_exactly(issuable2, issuable3)
+ end
+
+ it 'filters issuables by title only' do
+ with_text = resolve_issuables(search: 'text', in: ['title'])
+ with_created = resolve_issuables(search: 'created', in: ['title'])
+
+ expect(with_created).to contain_exactly(issuable1, issuable2)
+ expect(with_text).to be_empty
+ end
+ end
+
+ context 'with anonymous user' do
+ let_it_be(:current_user) { nil }
+
+ context 'with disable_anonymous_search as `true`' do
+ before do
+ stub_feature_flags(disable_anonymous_search: true)
+ end
+
+ it 'returns an error' do
+ error_message = "User must be authenticated to include the `search` argument."
+
+ expect_graphql_error_to_be_created(Gitlab::Graphql::Errors::ArgumentError, error_message) do
+ resolve_issuables(search: 'created')
+ end
+ end
+
+ it 'does not return error if search term is not present' do
+ expect(resolve_issuables).not_to be_instance_of(Gitlab::Graphql::Errors::ArgumentError)
+ end
+ end
+
+ context 'with disable_anonymous_search as `false`' do
+ before do
+ stub_feature_flags(disable_anonymous_search: false)
+ parent.update!(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
+ end
+
+ it 'filters issuables by search term' do
+ issuables = resolve_issuables(search: 'created')
+
+ expect(issuables).to contain_exactly(issuable1, issuable2)
+ end
+ end
+ end
+
+ def resolve_issuables(args = {}, obj = parent, context = { current_user: current_user })
+ resolve(described_class, obj: obj, args: args, ctx: context, arg_style: :internal)
+ end
+end
diff --git a/spec/support/shared_examples/lib/banzai/filters/sanitization_filter_shared_examples.rb b/spec/support/shared_examples/lib/banzai/filters/sanitization_filter_shared_examples.rb
index b5c07f45d59..47655f86558 100644
--- a/spec/support/shared_examples/lib/banzai/filters/sanitization_filter_shared_examples.rb
+++ b/spec/support/shared_examples/lib/banzai/filters/sanitization_filter_shared_examples.rb
@@ -45,62 +45,62 @@ RSpec.shared_examples 'XSS prevention' do
# Adapted from the Sanitize test suite: http://git.io/vczrM
protocols = {
'protocol-based JS injection: simple, no spaces' => {
- input: '<a href="javascript:alert(\'XSS\');">foo</a>',
+ input: '<a href="javascript:alert(\'XSS\');">foo</a>',
output: '<a>foo</a>'
},
'protocol-based JS injection: simple, spaces before' => {
- input: '<a href="javascript :alert(\'XSS\');">foo</a>',
+ input: '<a href="javascript :alert(\'XSS\');">foo</a>',
output: '<a>foo</a>'
},
'protocol-based JS injection: simple, spaces after' => {
- input: '<a href="javascript: alert(\'XSS\');">foo</a>',
+ input: '<a href="javascript: alert(\'XSS\');">foo</a>',
output: '<a>foo</a>'
},
'protocol-based JS injection: simple, spaces before and after' => {
- input: '<a href="javascript : alert(\'XSS\');">foo</a>',
+ input: '<a href="javascript : alert(\'XSS\');">foo</a>',
output: '<a>foo</a>'
},
'protocol-based JS injection: preceding colon' => {
- input: '<a href=":javascript:alert(\'XSS\');">foo</a>',
+ input: '<a href=":javascript:alert(\'XSS\');">foo</a>',
output: '<a>foo</a>'
},
'protocol-based JS injection: UTF-8 encoding' => {
- input: '<a href="javascript&#58;">foo</a>',
+ input: '<a href="javascript&#58;">foo</a>',
output: '<a>foo</a>'
},
'protocol-based JS injection: long UTF-8 encoding' => {
- input: '<a href="javascript&#0058;">foo</a>',
+ input: '<a href="javascript&#0058;">foo</a>',
output: '<a>foo</a>'
},
'protocol-based JS injection: long UTF-8 encoding without semicolons' => {
- input: '<a href=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>foo</a>',
+ input: '<a href=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>foo</a>',
output: '<a>foo</a>'
},
'protocol-based JS injection: hex encoding' => {
- input: '<a href="javascript&#x3A;">foo</a>',
+ input: '<a href="javascript&#x3A;">foo</a>',
output: '<a>foo</a>'
},
'protocol-based JS injection: long hex encoding' => {
- input: '<a href="javascript&#x003A;">foo</a>',
+ input: '<a href="javascript&#x003A;">foo</a>',
output: '<a>foo</a>'
},
'protocol-based JS injection: hex encoding without semicolons' => {
- input: '<a href=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>foo</a>',
+ input: '<a href=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>foo</a>',
output: '<a>foo</a>'
},
'protocol-based JS injection: null char' => {
- input: "<a href=java\0script:alert(\"XSS\")>foo</a>",
+ input: "<a href=java\0script:alert(\"XSS\")>foo</a>",
output: '<a href="java"></a>'
},
@@ -115,7 +115,7 @@ RSpec.shared_examples 'XSS prevention' do
},
'protocol-based JS injection: spaces and entities' => {
- input: '<a href=" &#14; javascript:alert(\'XSS\');">foo</a>',
+ input: '<a href=" &#14; javascript:alert(\'XSS\');">foo</a>',
output: '<a href="">foo</a>'
},
diff --git a/spec/support/shared_examples/lib/cache_helpers_shared_examples.rb b/spec/support/shared_examples/lib/cache_helpers_shared_examples.rb
index 845fa78a827..82a9e8130f7 100644
--- a/spec/support/shared_examples/lib/cache_helpers_shared_examples.rb
+++ b/spec/support/shared_examples/lib/cache_helpers_shared_examples.rb
@@ -43,6 +43,54 @@ RSpec.shared_examples_for 'object cache helper' do
subject
end
end
+
+ context 'when a caller id is present' do
+ let(:transaction) { Gitlab::Metrics::WebTransaction.new({}) }
+ let(:caller_id) { 'caller_id' }
+
+ before do
+ allow(::Gitlab::Metrics::WebTransaction).to receive(:current).and_return(transaction)
+ allow(transaction).to receive(:increment)
+ allow(Gitlab::ApplicationContext).to receive(:current_context_attribute).with(:caller_id).and_return(caller_id)
+ end
+
+ context 'when feature flag is off' do
+ before do
+ stub_feature_flags(add_timing_to_certain_cache_actions: false)
+ end
+
+ it 'does not call increment' do
+ expect(transaction).not_to receive(:increment).with(:cached_object_operations_total, any_args)
+
+ subject
+ end
+
+ it 'does not call histogram' do
+ expect(Gitlab::Metrics).not_to receive(:histogram)
+
+ subject
+ end
+
+ it "is valid JSON" do
+ parsed = Gitlab::Json.parse(subject.to_s)
+
+ expect(parsed).to be_a(Hash)
+ expect(parsed["id"]).to eq(presentable.id)
+ end
+ end
+
+ it 'increments the counter' do
+ expect(transaction)
+ .to receive(:increment)
+ .with(:cached_object_operations_total, 1, { caller_id: caller_id, render_type: :object, cache_hit: false }).once
+
+ expect(transaction)
+ .to receive(:increment)
+ .with(:cached_object_operations_total, 0, { caller_id: caller_id, render_type: :object, cache_hit: true }).once
+
+ subject
+ end
+ end
end
RSpec.shared_examples_for 'collection cache helper' do
@@ -98,4 +146,95 @@ RSpec.shared_examples_for 'collection cache helper' do
subject
end
end
+
+ context 'when a caller id is present' do
+ let(:transaction) { Gitlab::Metrics::WebTransaction.new({}) }
+ let(:caller_id) { 'caller_id' }
+
+ before do
+ allow(::Gitlab::Metrics::WebTransaction).to receive(:current).and_return(transaction)
+ allow(transaction).to receive(:increment)
+ allow(Gitlab::ApplicationContext).to receive(:current_context_attribute).with(:caller_id).and_return(caller_id)
+ end
+
+ context 'when feature flag is off' do
+ before do
+ stub_feature_flags(add_timing_to_certain_cache_actions: false)
+ end
+
+ it 'does not call increment' do
+ expect(transaction).not_to receive(:increment).with(:cached_object_operations_total, any_args)
+
+ subject
+ end
+
+ it 'does not call histogram' do
+ expect(Gitlab::Metrics).not_to receive(:histogram)
+
+ subject
+ end
+
+ it "is valid JSON" do
+ parsed = Gitlab::Json.parse(subject.to_s)
+
+ expect(parsed).to be_an(Array)
+
+ presentable.each_with_index do |item, i|
+ expect(parsed[i]["id"]).to eq(item.id)
+ end
+ end
+ end
+
+ context 'when presentable has a group by clause' do
+ let(:presentable) { MergeRequest.group(:id) }
+
+ it "returns the presentables" do
+ expect(transaction)
+ .to receive(:increment)
+ .with(:cached_object_operations_total, 0, { caller_id: caller_id, render_type: :collection, cache_hit: true }).once
+
+ expect(transaction)
+ .to receive(:increment)
+ .with(:cached_object_operations_total, MergeRequest.count, { caller_id: caller_id, render_type: :collection, cache_hit: false }).once
+
+ parsed = Gitlab::Json.parse(subject.to_s)
+
+ expect(parsed).to be_an(Array)
+
+ presentable.each_with_index do |item, i|
+ expect(parsed[i]["id"]).to eq(item.id)
+ end
+ end
+ end
+
+ context 'when the presentables all miss' do
+ it 'increments the counters' do
+ expect(transaction)
+ .to receive(:increment)
+ .with(:cached_object_operations_total, 0, { caller_id: caller_id, render_type: :collection, cache_hit: true }).once
+
+ expect(transaction)
+ .to receive(:increment)
+ .with(:cached_object_operations_total, presentable.size, { caller_id: caller_id, render_type: :collection, cache_hit: false }).once
+
+ subject
+ end
+ end
+
+ context 'when the presents hit' do
+ it 'increments the counters' do
+ subject
+
+ expect(transaction)
+ .to receive(:increment)
+ .with(:cached_object_operations_total, presentable.size, { caller_id: caller_id, render_type: :collection, cache_hit: true }).once
+
+ expect(transaction)
+ .to receive(:increment)
+ .with(:cached_object_operations_total, 0, { caller_id: caller_id, render_type: :collection, cache_hit: false }).once
+
+ instance.public_send(method, presentable, **kwargs)
+ end
+ end
+ end
end
diff --git a/spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb
index b786d7e5527..10f58748698 100644
--- a/spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/ci/ci_trace_shared_examples.rb
@@ -131,7 +131,7 @@ RSpec.shared_examples 'common trace features' do
end
context 'logs contains "section_start"' do
- let(:log) { "section_start:1506417476:a_section\r\033[0Klooks like a section_start:invalid\nsection_end:1506417477:a_section\r\033[0K"}
+ let(:log) { "section_start:1506417476:a_section\r\033[0Klooks like a section_start:invalid\nsection_end:1506417477:a_section\r\033[0K" }
it "returns only one section" do
expect(sections).not_to be_empty
@@ -144,7 +144,7 @@ RSpec.shared_examples 'common trace features' do
end
context 'missing section_end' do
- let(:log) { "section_start:1506417476:a_section\r\033[0KSome logs\nNo section_end\n"}
+ let(:log) { "section_start:1506417476:a_section\r\033[0KSome logs\nNo section_end\n" }
it "returns no sections" do
expect(sections).to be_empty
@@ -152,7 +152,7 @@ RSpec.shared_examples 'common trace features' do
end
context 'missing section_start' do
- let(:log) { "Some logs\nNo section_start\nsection_end:1506417476:a_section\r\033[0K"}
+ let(:log) { "Some logs\nNo section_start\nsection_end:1506417476:a_section\r\033[0K" }
it "returns no sections" do
expect(sections).to be_empty
@@ -160,7 +160,7 @@ RSpec.shared_examples 'common trace features' do
end
context 'inverted section_start section_end' do
- let(:log) { "section_end:1506417476:a_section\r\033[0Klooks like a section_start:invalid\nsection_start:1506417477:a_section\r\033[0K"}
+ let(:log) { "section_end:1506417476:a_section\r\033[0Klooks like a section_start:invalid\nsection_start:1506417477:a_section\r\033[0K" }
it "returns no sections" do
expect(sections).to be_empty
@@ -169,7 +169,7 @@ RSpec.shared_examples 'common trace features' do
end
describe '#write' do
- subject { trace.send(:write, mode) { } }
+ subject { trace.send(:write, mode) {} }
let(:mode) { 'wb' }
@@ -370,15 +370,6 @@ RSpec.shared_examples 'trace with disabled live trace feature' do
end
end
- shared_examples 'read successfully with StringIO' do
- it 'yields with source' do
- trace.read do |stream|
- expect(stream).to be_a(Gitlab::Ci::Trace::Stream)
- expect(stream.stream).to be_a(StringIO)
- end
- end
- end
-
shared_examples 'failed to read' do
it 'yields without source' do
trace.read do |stream|
@@ -404,14 +395,6 @@ RSpec.shared_examples 'trace with disabled live trace feature' do
it_behaves_like 'read successfully with IO'
end
- context 'when db trace exists' do
- before do
- build.send(:write_attribute, :trace, "data")
- end
-
- it_behaves_like 'read successfully with StringIO'
- end
-
context 'when no sources exist' do
it_behaves_like 'failed to read'
end
@@ -462,25 +445,6 @@ RSpec.shared_examples 'trace with disabled live trace feature' do
expect(trace.exist?).to be(false)
end
end
-
- context 'stored in database' do
- before do
- build.send(:write_attribute, :trace, "data")
- end
-
- it "trace exist" do
- expect(trace.exist?).to be(true)
- end
-
- it "can be erased" do
- trace.erase!
- expect(trace.exist?).to be(false)
- end
-
- it "returns database data" do
- expect(trace.raw).to eq("data")
- end
- end
end
describe '#archive!' do
@@ -520,24 +484,12 @@ RSpec.shared_examples 'trace with disabled live trace feature' do
expect(build.trace.exist?).to be_truthy
expect(build.job_artifacts_trace.file.exists?).to be_truthy
expect(build.job_artifacts_trace.file.filename).to eq('job.log')
- expect(build.old_trace).to be_nil
expect(src_checksum)
.to eq(described_class.sha256_hexdigest(build.job_artifacts_trace.file.path))
expect(build.job_artifacts_trace.file_sha256).to eq(src_checksum)
end
end
- shared_examples 'source trace in database stays intact' do |error:|
- it do
- expect { subject }.to raise_error(error)
-
- build.reload
- expect(build.trace.exist?).to be_truthy
- expect(build.job_artifacts_trace).to be_nil
- expect(build.old_trace).to eq(trace_content)
- end
- end
-
context 'when job does not have trace artifact' do
context 'when trace file stored in default path' do
let!(:build) { create(:ci_build, :success, :trace_live) }
@@ -564,58 +516,6 @@ RSpec.shared_examples 'trace with disabled live trace feature' do
it_behaves_like 'source trace file stays intact', error: ActiveRecord::RecordInvalid
end
end
-
- context 'when trace is stored in database' do
- let(:build) { create(:ci_build, :success) }
- let(:trace_content) { 'Sample trace' }
- let(:src_checksum) { Digest::SHA256.hexdigest(trace_content) }
-
- before do
- build.update_column(:trace, trace_content)
- end
-
- it_behaves_like 'archive trace in database'
-
- context 'when failed to create clone file' do
- before do
- allow(IO).to receive(:copy_stream).and_return(0)
- end
-
- it_behaves_like 'source trace in database stays intact', error: Gitlab::Ci::Trace::ArchiveError
- end
-
- context 'when failed to create job artifact record' do
- before do
- allow_any_instance_of(Ci::JobArtifact).to receive(:save).and_return(false)
- allow_any_instance_of(Ci::JobArtifact).to receive_message_chain(:errors, :full_messages)
- .and_return(%w[Error Error])
- end
-
- it_behaves_like 'source trace in database stays intact', error: ActiveRecord::RecordInvalid
- end
-
- context 'when there is a validation error on Ci::Build' do
- before do
- allow_any_instance_of(Ci::Build).to receive(:save).and_return(false)
- allow_any_instance_of(Ci::Build).to receive_message_chain(:errors, :full_messages)
- .and_return(%w[Error Error])
- end
-
- context "when erase old trace with 'save'" do
- before do
- build.send(:write_attribute, :trace, nil)
- build.save # rubocop:disable Rails/SaveBang
- end
-
- it 'old trace is not deleted' do
- build.reload
- expect(build.trace.raw).to eq(trace_content)
- end
- end
-
- it_behaves_like 'archive trace in database'
- end
- end
end
context 'when job has trace artifact' do
@@ -645,22 +545,6 @@ RSpec.shared_examples 'trace with disabled live trace feature' do
subject { trace.erase! }
context 'when it is a live trace' do
- context 'when trace is stored in database' do
- let(:build) { create(:ci_build) }
-
- before do
- build.update_column(:trace, 'sample trace')
- end
-
- it { expect(trace.raw).not_to be_nil }
-
- it "removes trace" do
- subject
-
- expect(trace.raw).to be_nil
- end
- end
-
context 'when trace is stored in file storage' do
let(:build) { create(:ci_build, :trace_live) }
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 beec072e474..9ffc55f7e7e 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
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-shared_examples 'deployment metrics examples' do
+RSpec.shared_examples 'deployment metrics examples' do
def create_deployment(args)
project = args[:project]
environment = project.environments.production.first || create(:environment, :production, project: project)
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 771ab89972c..a28fefcfc58 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
@@ -3,7 +3,7 @@
RSpec.shared_examples 'marks background migration job records' do
it 'marks each job record as succeeded after processing' do
create(:background_migration_job, class_name: "::#{described_class.name.demodulize}",
- arguments: arguments)
+ arguments: arguments)
expect(::Gitlab::Database::BackgroundMigrationJob).to receive(:mark_all_as_succeeded).and_call_original
@@ -14,7 +14,7 @@ RSpec.shared_examples 'marks background migration job records' do
it 'returns the number of job records marked as succeeded' do
create(:background_migration_job, class_name: "::#{described_class.name.demodulize}",
- arguments: arguments)
+ arguments: arguments)
jobs_updated = subject.perform(*arguments)
diff --git a/spec/support/shared_examples/lib/gitlab/diff_file_collections_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/diff_file_collections_shared_examples.rb
index 1f7325df11a..243dc1d195b 100644
--- a/spec/support/shared_examples/lib/gitlab/diff_file_collections_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/diff_file_collections_shared_examples.rb
@@ -144,7 +144,7 @@ RSpec.shared_examples 'cacheable diff collection' do
end
end
-shared_examples_for 'sortable diff files' do
+RSpec.shared_examples_for 'sortable diff files' do
subject { described_class.new(diffable, **collection_default_args) }
describe '#raw_diff_files' do
@@ -170,7 +170,7 @@ shared_examples_for 'sortable diff files' do
end
end
-shared_examples_for 'unsortable diff files' do
+RSpec.shared_examples_for 'unsortable diff files' do
subject { described_class.new(diffable, **collection_default_args) }
describe '#raw_diff_files' do
diff --git a/spec/support/shared_examples/lib/gitlab/sql/set_operator_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/sql/set_operator_shared_examples.rb
index ead8b174d46..ec7b2794703 100644
--- a/spec/support/shared_examples/lib/gitlab/sql/set_operator_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/sql/set_operator_shared_examples.rb
@@ -25,7 +25,7 @@ RSpec.shared_examples 'SQL set operator' do |operator_keyword|
empty_relation = User.none.select(:id)
set_operator = described_class.new([empty_relation, relation_1, relation_2])
- expect {User.where("users.id IN (#{set_operator.to_sql})").to_a}.not_to raise_error
+ expect { User.where("users.id IN (#{set_operator.to_sql})").to_a }.not_to raise_error
expect(set_operator.to_sql).to eq("(#{to_sql(relation_1)})\n#{operator_keyword}\n(#{to_sql(relation_2)})")
end
diff --git a/spec/support/shared_examples/lib/sentry/client_shared_examples.rb b/spec/support/shared_examples/lib/sentry/client_shared_examples.rb
index d73c7b6848d..1c0e0061385 100644
--- a/spec/support/shared_examples/lib/sentry/client_shared_examples.rb
+++ b/spec/support/shared_examples/lib/sentry/client_shared_examples.rb
@@ -43,7 +43,7 @@ RSpec.shared_examples 'maps Sentry exceptions' do |http_method|
}
exceptions.each do |exception, message|
- context "#{exception}" do
+ context exception do
before do
stub_request(
http_method || :get,
@@ -58,3 +58,50 @@ RSpec.shared_examples 'maps Sentry exceptions' do |http_method|
end
end
end
+
+# Expects to following variables:
+# - subject
+# - sentry_api_response
+# - sentry_url, token - only if enabled_by_default: false
+RSpec.shared_examples 'Sentry API response size limit' do |enabled_by_default: false|
+ let(:invalid_deep_size) { instance_double(Gitlab::Utils::DeepSize, valid?: false) }
+
+ before do
+ allow(Gitlab::Utils::DeepSize)
+ .to receive(:new)
+ .with(sentry_api_response, any_args)
+ .and_return(invalid_deep_size)
+ end
+
+ if enabled_by_default
+ it 'raises an exception when response is too large' do
+ expect { subject }.to raise_error(ErrorTracking::SentryClient::ResponseInvalidSizeError,
+ 'Sentry API response is too big. Limit is 1 MB.')
+ end
+ else
+ context 'when guarded by feature flag' do
+ let(:client) do
+ ErrorTracking::SentryClient.new(sentry_url, token, validate_size_guarded_by_feature_flag: feature_flag)
+ end
+
+ context 'with feature flag enabled' do
+ let(:feature_flag) { true }
+
+ it 'raises an exception when response is too large' do
+ expect { subject }.to raise_error(ErrorTracking::SentryClient::ResponseInvalidSizeError,
+ 'Sentry API response is too big. Limit is 1 MB.')
+ end
+ end
+
+ context 'with feature flag disabled' do
+ let(:feature_flag) { false }
+
+ it 'does not check the limit and thus not raise' do
+ expect { subject }.not_to raise_error
+
+ expect(Gitlab::Utils::DeepSize).not_to have_received(:new)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/models/chat_integration_shared_examples.rb b/spec/support/shared_examples/models/chat_integration_shared_examples.rb
index fb08784f34f..6cfeeabc952 100644
--- a/spec/support/shared_examples/models/chat_integration_shared_examples.rb
+++ b/spec/support/shared_examples/models/chat_integration_shared_examples.rb
@@ -32,9 +32,11 @@ RSpec.shared_examples "chat integration" do |integration_name|
end
describe "#execute" do
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
+ let_it_be_with_reload(:project) { create(:project, :repository) }
+
let(:webhook_url) { "https://example.gitlab.com/" }
+ let(:webhook_url_regex) { /\A#{webhook_url}.*/ }
before do
allow(subject).to receive_messages(
@@ -44,7 +46,7 @@ RSpec.shared_examples "chat integration" do |integration_name|
webhook: webhook_url
)
- WebMock.stub_request(:post, webhook_url)
+ WebMock.stub_request(:post, webhook_url_regex)
end
shared_examples "triggered #{integration_name} integration" do |branches_to_be_notified: nil|
@@ -56,7 +58,7 @@ RSpec.shared_examples "chat integration" do |integration_name|
result = subject.execute(sample_data)
expect(result).to be(true)
- expect(WebMock).to have_requested(:post, webhook_url).once.with { |req|
+ expect(WebMock).to have_requested(:post, webhook_url_regex).once.with { |req|
json_body = Gitlab::Json.parse(req.body).with_indifferent_access
expect(json_body).to include(payload)
}
@@ -72,7 +74,7 @@ RSpec.shared_examples "chat integration" do |integration_name|
result = subject.execute(sample_data)
expect(result).to be_falsy
- expect(WebMock).not_to have_requested(:post, webhook_url)
+ expect(WebMock).not_to have_requested(:post, webhook_url_regex)
end
end
@@ -112,14 +114,14 @@ RSpec.shared_examples "chat integration" do |integration_name|
end
context "with protected branch" do
- before do
- create(:protected_branch, :create_branch_on_repository, project: project, name: "a-protected-branch")
- end
-
let(:sample_data) do
Gitlab::DataBuilder::Push.build(project: project, user: user, ref: "a-protected-branch")
end
+ before_all do
+ create(:protected_branch, :create_branch_on_repository, project: project, name: "a-protected-branch")
+ end
+
context "when only default branch are to be notified" do
it_behaves_like "untriggered #{integration_name} integration", branches_to_be_notified: "default"
end
@@ -214,7 +216,7 @@ RSpec.shared_examples "chat integration" do |integration_name|
let(:sample_data) { Gitlab::DataBuilder::Note.build(note, user) }
context "with commit comment" do
- let(:note) do
+ let_it_be(:note) do
create(:note_on_commit,
author: user,
project: project,
@@ -226,7 +228,7 @@ RSpec.shared_examples "chat integration" do |integration_name|
end
context "with merge request comment" do
- let(:note) do
+ let_it_be(:note) do
create(:note_on_merge_request, project: project, note: "merge request note")
end
@@ -234,7 +236,7 @@ RSpec.shared_examples "chat integration" do |integration_name|
end
context "with issue comment" do
- let(:note) do
+ let_it_be(:note) do
create(:note_on_issue, project: project, note: "issue note")
end
@@ -242,7 +244,7 @@ RSpec.shared_examples "chat integration" do |integration_name|
end
context "with snippet comment" do
- let(:note) do
+ let_it_be(:note) do
create(:note_on_project_snippet, project: project, note: "snippet note")
end
@@ -251,22 +253,24 @@ RSpec.shared_examples "chat integration" do |integration_name|
end
context "with pipeline events" do
- let(:pipeline) do
- create(:ci_pipeline,
- project: project, status: status,
- sha: project.commit.sha, ref: project.default_branch)
- end
-
let(:sample_data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
context "with failed pipeline" do
- let(:status) { "failed" }
+ let_it_be(:pipeline) do
+ create(:ci_pipeline,
+ project: project, status: "failed",
+ sha: project.commit.sha, ref: project.default_branch)
+ end
it_behaves_like "triggered #{integration_name} integration"
end
context "with succeeded pipeline" do
- let(:status) { "success" }
+ let_it_be(:pipeline) do
+ create(:ci_pipeline,
+ project: project, status: "success",
+ sha: project.commit.sha, ref: project.default_branch)
+ end
context "with default notify_only_broken_pipelines" do
it "does not call #{integration_name} API" do
@@ -308,7 +312,7 @@ RSpec.shared_examples "chat integration" do |integration_name|
end
context "with protected branch" do
- before do
+ before_all do
create(:protected_branch, :create_branch_on_repository, project: project, name: "a-protected-branch")
end
@@ -357,7 +361,8 @@ RSpec.shared_examples "chat integration" do |integration_name|
end
context 'deployment events' do
- let(:deployment) { create(:deployment) }
+ let_it_be(:deployment) { create(:deployment) }
+
let(:sample_data) { Gitlab::DataBuilder::Deployment.build(deployment, deployment.status, Time.now) }
it_behaves_like "untriggered #{integration_name} integration"
diff --git a/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb b/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb
index 51071ae47c3..ca9122bf61f 100644
--- a/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb
+++ b/spec/support/shared_examples/models/cluster_application_core_shared_examples.rb
@@ -37,7 +37,7 @@ RSpec.shared_examples 'cluster application core specs' do |application_name|
with_them do
subject { described_class.new(cluster: cluster).helm_command_module }
- let(:cluster) { build(:cluster, helm_major_version: helm_major_version)}
+ let(:cluster) { build(:cluster, helm_major_version: helm_major_version) }
it { is_expected.to eq(expected_helm_command_module) }
end
diff --git a/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb b/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb
index 8ff30021d6e..6f104f400bc 100644
--- a/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb
+++ b/spec/support/shared_examples/models/concerns/analytics/cycle_analytics/stage_event_model_examples.rb
@@ -89,7 +89,7 @@ RSpec.shared_examples 'StageEventModel' do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:user) }
let_it_be(:milestone) { create(:milestone) }
- let_it_be(:issuable_with_assignee) { create(issuable_factory, assignees: [user])}
+ let_it_be(:issuable_with_assignee) { create(issuable_factory, assignees: [user]) }
let_it_be(:record) { create(stage_event_factory, start_event_timestamp: 3.years.ago.to_date, end_event_timestamp: 2.years.ago.to_date) }
let_it_be(:record_with_author) { create(stage_event_factory, author_id: user.id) }
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 f4d5ab3d5c6..f3a12578912 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
@@ -75,9 +75,9 @@ RSpec.shared_examples_for CounterAttribute do |counter_attributes|
end
context 'when attribute is not a counter attribute' do
- it 'delegates to ActiveRecord update!' do
+ it 'raises ArgumentError' do
expect { model.delayed_increment_counter(:unknown_attribute, 10) }
- .to raise_error(ActiveModel::MissingAttributeError)
+ .to raise_error(ArgumentError, 'unknown_attribute is not a counter attribute')
end
end
end
diff --git a/spec/support/shared_examples/models/concerns/has_repository_shared_examples.rb b/spec/support/shared_examples/models/concerns/has_repository_shared_examples.rb
index a403a27adef..0a07c9d677b 100644
--- a/spec/support/shared_examples/models/concerns/has_repository_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/has_repository_shared_examples.rb
@@ -125,17 +125,17 @@ RSpec.shared_examples 'model with repository' do
end
describe '#valid_repo?' do
- it { expect(stubbed_container.valid_repo?).to be(false)}
+ it { expect(stubbed_container.valid_repo?).to be(false) }
it { expect(container.valid_repo?).to be(true) }
end
describe '#repository_exists?' do
- it { expect(stubbed_container.repository_exists?).to be(false)}
+ it { expect(stubbed_container.repository_exists?).to be(false) }
it { expect(container.repository_exists?).to be(true) }
end
describe '#repo_exists?' do
- it { expect(stubbed_container.repo_exists?).to be(false)}
+ it { expect(stubbed_container.repo_exists?).to be(false) }
it { expect(container.repo_exists?).to be(true) }
end
diff --git a/spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb b/spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb
index 8ee76efc896..a5970f134d9 100644
--- a/spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/incident_management/escalatable_shared_examples.rb
@@ -77,7 +77,7 @@ RSpec.shared_examples 'a model including Escalatable' do
end
context 'scopes' do
- let(:all_escalatables) { described_class.where(id: [triggered_escalatable, acknowledged_escalatable, ignored_escalatable, resolved_escalatable])}
+ let(:all_escalatables) { described_class.where(id: [triggered_escalatable, acknowledged_escalatable, ignored_escalatable, resolved_escalatable]) }
describe '.order_status' do
subject { all_escalatables.order_status(order) }
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 73066fb631a..f61007f57fd 100644
--- a/spec/support/shared_examples/models/label_note_shared_examples.rb
+++ b/spec/support/shared_examples/models/label_note_shared_examples.rb
@@ -12,7 +12,7 @@ RSpec.shared_examples 'label note created from events' do
def label_refs(events)
labels = events.map(&:label).compact
- labels.map { |l| l.to_reference}.sort.join(' ')
+ labels.map { |l| l.to_reference }.sort.join(' ')
end
let(:time) { Time.now }
diff --git a/spec/support/shared_examples/models/members_notifications_shared_example.rb b/spec/support/shared_examples/models/members_notifications_shared_example.rb
index 75eed0203a7..e74aab95e46 100644
--- a/spec/support/shared_examples/models/members_notifications_shared_example.rb
+++ b/spec/support/shared_examples/models/members_notifications_shared_example.rb
@@ -8,7 +8,7 @@ RSpec.shared_examples 'members notifications' do |entity_type|
end
describe "#after_create" do
- let(:member) { build(:"#{entity_type}_member") }
+ let(:member) { build(:"#{entity_type}_member", "#{entity_type}": create(entity_type.to_s)) }
it "sends email to user" do
expect(notification_service).to receive(:"new_#{entity_type}_member").with(member)
@@ -35,7 +35,7 @@ RSpec.shared_examples 'members notifications' do |entity_type|
describe '#after_commit' do
context 'on creation of a member requesting access' do
- let(:member) { build(:"#{entity_type}_member", :access_request) }
+ let(:member) { build(:"#{entity_type}_member", :access_request, "#{entity_type}": create(entity_type.to_s)) }
it "calls NotificationService.new_access_request" do
expect(notification_service).to receive(:new_access_request).with(member)
diff --git a/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
index 6b0ae589efb..3d7d97bbeae 100644
--- a/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
+++ b/spec/support/shared_examples/models/packages/debian/distribution_shared_examples.rb
@@ -202,17 +202,17 @@ RSpec.shared_examples 'Debian Distribution' do |factory, container, can_freeze|
end
else
describe 'group distribution specifics' do
- let_it_be(:public_project) { create(:project, :public, group: distribution_with_suite.container)}
+ let_it_be(:public_project) { create(:project, :public, group: distribution_with_suite.container) }
let_it_be(:public_distribution_with_same_codename) { create(:debian_project_distribution, container: public_project, codename: distribution_with_suite.codename) }
- let_it_be(:public_package_with_same_codename) { create(:debian_package, project: public_project, published_in: public_distribution_with_same_codename)}
+ let_it_be(:public_package_with_same_codename) { create(:debian_package, project: public_project, published_in: public_distribution_with_same_codename) }
let_it_be(:public_distribution_with_same_suite) { create(:debian_project_distribution, container: public_project, suite: distribution_with_suite.suite) }
- let_it_be(:public_package_with_same_suite) { create(:debian_package, project: public_project, published_in: public_distribution_with_same_suite)}
+ let_it_be(:public_package_with_same_suite) { create(:debian_package, project: public_project, published_in: public_distribution_with_same_suite) }
- let_it_be(:private_project) { create(:project, :private, group: distribution_with_suite.container)}
+ let_it_be(:private_project) { create(:project, :private, group: distribution_with_suite.container) }
let_it_be(:private_distribution_with_same_codename) { create(:debian_project_distribution, container: private_project, codename: distribution_with_suite.codename) }
- let_it_be(:private_package_with_same_codename) { create(:debian_package, project: private_project, published_in: private_distribution_with_same_codename)}
+ let_it_be(:private_package_with_same_codename) { create(:debian_package, project: private_project, published_in: private_distribution_with_same_codename) }
let_it_be(:private_distribution_with_same_suite) { create(:debian_project_distribution, container: private_project, suite: distribution_with_suite.suite) }
- let_it_be(:private_package_with_same_suite) { create(:debian_package, project: private_project, published_in: private_distribution_with_same_codename)}
+ let_it_be(:private_package_with_same_suite) { create(:debian_package, project: private_project, published_in: private_distribution_with_same_codename) }
describe '#packages' do
subject { distribution_with_suite.packages }
diff --git a/spec/support/shared_examples/models/project_latest_successful_build_for_shared_examples.rb b/spec/support/shared_examples/models/project_latest_successful_build_for_shared_examples.rb
index 66cd8d1df12..9093b386a5d 100644
--- a/spec/support/shared_examples/models/project_latest_successful_build_for_shared_examples.rb
+++ b/spec/support/shared_examples/models/project_latest_successful_build_for_shared_examples.rb
@@ -64,7 +64,7 @@ RSpec.shared_examples 'latest successful build for sha or ref' do
context 'with build belonging to a child pipeline' do
let(:child_pipeline) { create_pipeline(project) }
let(:parent_bridge) { create(:ci_bridge, pipeline: pipeline, project: pipeline.project) }
- let!(:pipeline_source) { create(:ci_sources_pipeline, source_job: parent_bridge, pipeline: child_pipeline)}
+ let!(:pipeline_source) { create(:ci_sources_pipeline, source_job: parent_bridge, pipeline: child_pipeline) }
let!(:child_build) { create_build(child_pipeline, 'child-build') }
let(:build_name) { child_build.name }
diff --git a/spec/support/shared_examples/models/synthetic_note_shared_examples.rb b/spec/support/shared_examples/models/synthetic_note_shared_examples.rb
index a41ade2950a..12e865b1312 100644
--- a/spec/support/shared_examples/models/synthetic_note_shared_examples.rb
+++ b/spec/support/shared_examples/models/synthetic_note_shared_examples.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.shared_examples 'a synthetic note' do |action|
- it_behaves_like 'a system note', exclude_project: true do
+ it_behaves_like 'a system note', exclude_project: true, skip_persistence_check: true do
let(:action) { action }
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 ad0bbc0aeff..b81bd514d0a 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
@@ -26,9 +26,6 @@ RSpec.shared_examples 'UpdateProjectStatistics' do |with_counter_attribute|
expect(FlushCounterIncrementsWorker)
.to receive(:perform_in)
.with(CounterAttribute::WORKER_DELAY, project.statistics.class.name, project.statistics.id, project_statistics_name)
- expect(FlushCounterIncrementsWorker)
- .to receive(:perform_in)
- .with(CounterAttribute::WORKER_DELAY, project.statistics.class.name, project.statistics.id, :storage_size)
yield
diff --git a/spec/support/shared_examples/models/wiki_shared_examples.rb b/spec/support/shared_examples/models/wiki_shared_examples.rb
index 604c57768fe..5f6a10bd754 100644
--- a/spec/support/shared_examples/models/wiki_shared_examples.rb
+++ b/spec/support/shared_examples/models/wiki_shared_examples.rb
@@ -286,67 +286,134 @@ RSpec.shared_examples 'wiki model' do
end
describe '#find_page' do
- before do
- subject.create_page('index page', 'This is an awesome Gollum Wiki')
- end
+ shared_examples 'wiki model #find_page' do
+ before do
+ subject.create_page('index page', 'This is an awesome Gollum Wiki')
+ end
- it 'returns the latest version of the page if it exists' do
- page = subject.find_page('index page')
+ it 'returns the latest version of the page if it exists' do
+ page = subject.find_page('index page')
- expect(page.title).to eq('index page')
- end
+ expect(page.title).to eq('index page')
+ end
- it 'returns nil if the page or version does not exist' do
- expect(subject.find_page('non-existent')).to be_nil
- expect(subject.find_page('index page', 'non-existent')).to be_nil
- end
+ it 'returns nil if the page or version does not exist' do
+ expect(subject.find_page('non-existent')).to be_nil
+ expect(subject.find_page('index page', 'non-existent')).to be_nil
+ end
- it 'can find a page by slug' do
- page = subject.find_page('index-page')
+ it 'can find a page by slug' do
+ page = subject.find_page('index-page')
- expect(page.title).to eq('index page')
- end
+ expect(page.title).to eq('index page')
+ end
- it 'returns a WikiPage instance' do
- page = subject.find_page('index page')
+ it 'returns a WikiPage instance' do
+ page = subject.find_page('index page')
- expect(page).to be_a WikiPage
- end
+ expect(page).to be_a WikiPage
+ end
- context 'pages with multibyte-character title' do
- before do
- subject.create_page('autre pagé', "C'est un génial Gollum Wiki")
+ context 'pages with multibyte-character title' do
+ before do
+ subject.create_page('autre pagé', "C'est un génial Gollum Wiki")
+ end
+
+ it 'can find a page by slug' do
+ page = subject.find_page('autre pagé')
+
+ expect(page.title).to eq('autre pagé')
+ end
end
- it 'can find a page by slug' do
- page = subject.find_page('autre pagé')
+ context 'pages with invalidly-encoded content' do
+ before do
+ subject.create_page('encoding is fun', "f\xFCr".b)
+ end
+
+ it 'can find the page' do
+ page = subject.find_page('encoding is fun')
+
+ expect(page.content).to eq('fr')
+ end
+ end
+
+ context 'pages with different file extensions' do
+ where(:extension, :path, :title) do
+ [
+ [:md, "wiki-markdown.md", "wiki markdown"],
+ [:markdown, "wiki-markdown-2.md", "wiki markdown 2"],
+ [:rdoc, "wiki-rdoc.rdoc", "wiki rdoc"],
+ [:asciidoc, "wiki-asciidoc.asciidoc", "wiki asciidoc"],
+ [:adoc, "wiki-asciidoc-2.adoc", "wiki asciidoc 2"],
+ [:org, "wiki-org.org", "wiki org"],
+ [:textile, "wiki-textile.textile", "wiki textile"],
+ [:creole, "wiki-creole.creole", "wiki creole"],
+ [:rest, "wiki-rest.rest", "wiki rest"],
+ [:rst, "wiki-rest-2.rst", "wiki rest 2"],
+ [:mediawiki, "wiki-mediawiki.mediawiki", "wiki mediawiki"],
+ [:wiki, "wiki-mediawiki-2.wiki", "wiki mediawiki 2"],
+ [:pod, "wiki-pod.pod", "wiki pod"],
+ [:text, "wiki-text.txt", "wiki text"]
+ ]
+ end
- expect(page.title).to eq('autre pagé')
+ with_them do
+ before do
+ wiki.repository.create_file(
+ user, path, "content of wiki file",
+ branch_name: wiki.default_branch,
+ message: "created page #{path}",
+ author_email: user.email,
+ author_name: user.name
+ )
+ end
+
+ it "can find page with #{params[:extension]} extension" do
+ page = subject.find_page(title)
+
+ expect(page.content).to eq("content of wiki file")
+ end
+ end
end
end
- context 'pages with invalidly-encoded content' do
+ context 'find page with legacy wiki service' do
before do
- subject.create_page('encoding is fun', "f\xFCr".b)
+ stub_feature_flags(wiki_find_page_with_normal_repository_rpcs: false)
end
- it 'can find the page' do
- page = subject.find_page('encoding is fun')
+ it_behaves_like 'wiki model #find_page'
+ end
- expect(page.content).to eq('fr')
- end
+ context 'find page with normal repository RPCs' do
+ it_behaves_like 'wiki model #find_page'
end
end
describe '#find_sidebar' do
- before do
- subject.create_page(described_class::SIDEBAR, 'This is an awesome Sidebar')
+ shared_examples 'wiki model #find_sidebar' do
+ before do
+ subject.create_page(described_class::SIDEBAR, 'This is an awesome Sidebar')
+ end
+
+ it 'finds the page defined as _sidebar' do
+ page = subject.find_sidebar
+
+ expect(page.content).to eq('This is an awesome Sidebar')
+ end
end
- it 'finds the page defined as _sidebar' do
- page = subject.find_sidebar
+ context 'find sidebar with legacy wiki service' do
+ before do
+ stub_feature_flags(wiki_find_page_with_normal_repository_rpcs: false)
+ end
- expect(page.content).to eq('This is an awesome Sidebar')
+ it_behaves_like 'wiki model #find_sidebar'
+ end
+
+ context 'find sidebar with normal repository RPCs' do
+ it_behaves_like 'wiki model #find_sidebar'
end
end
@@ -450,9 +517,7 @@ RSpec.shared_examples 'wiki model' do
expect(subject.error_message).to match(/Duplicate page:/)
end
- end
- it_behaves_like 'create_page tests' do
it 'returns false if a page exists already in the repository', :aggregate_failures do
subject.create_page('test page', 'content')
@@ -540,6 +605,16 @@ RSpec.shared_examples 'wiki model' do
end
end
end
+
+ it_behaves_like 'create_page tests'
+
+ context 'create page with legacy find_page wiki service' do
+ it_behaves_like 'create_page tests' do
+ before do
+ stub_feature_flags(wiki_find_page_with_normal_repository_rpcs: false)
+ end
+ end
+ end
end
describe '#update_page' do
@@ -636,6 +711,17 @@ RSpec.shared_examples 'wiki model' do
include_context 'extended examples'
end
+ context 'update page with legacy find_page wiki service' do
+ it_behaves_like 'update_page tests' do
+ before do
+ stub_feature_flags(wiki_find_page_with_normal_repository_rpcs: false)
+ end
+
+ include_context 'common examples'
+ include_context 'extended examples'
+ end
+ end
+
context 'when format is invalid' do
let!(:page) { create(:wiki_page, wiki: subject, title: 'test page') }
diff --git a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb
index 807295f8442..4afed5139d8 100644
--- a/spec/support/shared_examples/namespaces/traversal_scope_examples.rb
+++ b/spec/support/shared_examples/namespaces/traversal_scope_examples.rb
@@ -265,14 +265,6 @@ RSpec.shared_examples 'namespace traversal scopes' do
describe '.self_and_descendants' do
include_examples '.self_and_descendants'
-
- context 'with traversal_ids_btree feature flag disabled' do
- before do
- stub_feature_flags(traversal_ids_btree: false)
- end
-
- include_examples '.self_and_descendants'
- end
end
shared_examples '.self_and_descendant_ids' do
@@ -308,14 +300,6 @@ RSpec.shared_examples 'namespace traversal scopes' do
describe '.self_and_descendant_ids' do
include_examples '.self_and_descendant_ids'
-
- context 'with traversal_ids_btree feature flag disabled' do
- before do
- stub_feature_flags(traversal_ids_btree: false)
- end
-
- include_examples '.self_and_descendant_ids'
- end
end
shared_examples '.self_and_hierarchy' do
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 c4083df47e2..cfcc3615e13 100644
--- a/spec/support/shared_examples/policies/project_policy_shared_examples.rb
+++ b/spec/support/shared_examples/policies/project_policy_shared_examples.rb
@@ -107,70 +107,88 @@ RSpec.shared_examples 'deploy token does not get confused with user' do
end
RSpec.shared_examples 'project policies as guest' do
- context 'abilities for public projects' do
- let(:project) { public_project }
- let(:current_user) { guest }
-
- it do
- expect_allowed(*guest_permissions)
- expect_allowed(*public_permissions)
- expect_disallowed(*developer_permissions)
- expect_disallowed(*maintainer_permissions)
- expect_disallowed(*owner_permissions)
- end
+ let(:reporter_public_build_permissions) do
+ reporter_permissions - [:read_build, :read_pipeline]
end
- context 'abilities for non-public projects' do
- let(:project) { private_project }
- let(:current_user) { guest }
+ context 'as a direct project member' do
+ context 'abilities for public projects' do
+ let(:project) { public_project }
+ let(:current_user) { guest }
- let(:reporter_public_build_permissions) do
- reporter_permissions - [:read_build, :read_pipeline]
+ specify do
+ expect_allowed(*guest_permissions)
+ expect_allowed(*public_permissions)
+ expect_disallowed(*developer_permissions)
+ expect_disallowed(*maintainer_permissions)
+ expect_disallowed(*owner_permissions)
+ end
end
- it do
- expect_allowed(*guest_permissions)
- expect_disallowed(*reporter_public_build_permissions)
- expect_disallowed(*team_member_reporter_permissions)
- expect_disallowed(*developer_permissions)
- expect_disallowed(*maintainer_permissions)
- expect_disallowed(*owner_permissions)
- end
+ context 'abilities for non-public projects' do
+ let(:project) { private_project }
+ let(:current_user) { guest }
- it_behaves_like 'deploy token does not get confused with user' do
- let(:user_id) { guest.id }
- end
+ specify do
+ expect_allowed(*guest_permissions)
+ expect_disallowed(*reporter_public_build_permissions)
+ expect_disallowed(*team_member_reporter_permissions)
+ expect_disallowed(*developer_permissions)
+ expect_disallowed(*maintainer_permissions)
+ expect_disallowed(*owner_permissions)
+ end
- it_behaves_like 'archived project policies' do
- let(:regular_abilities) { guest_permissions }
- end
+ it_behaves_like 'deploy token does not get confused with user' do
+ let(:user_id) { guest.id }
+ end
- context 'public builds enabled' do
- it do
- expect_allowed(*guest_permissions)
- expect_allowed(:read_build, :read_pipeline)
+ it_behaves_like 'archived project policies' do
+ let(:regular_abilities) { guest_permissions }
end
- end
- context 'when public builds disabled' do
- before do
- project.update!(public_builds: false)
+ context 'public builds enabled' do
+ specify do
+ expect_allowed(*guest_permissions)
+ expect_allowed(:read_build, :read_pipeline)
+ end
end
- it do
- expect_allowed(*guest_permissions)
- expect_disallowed(:read_build, :read_pipeline)
+ context 'when public builds disabled' do
+ before do
+ project.update!(public_builds: false)
+ end
+
+ specify do
+ expect_allowed(*guest_permissions)
+ expect_disallowed(:read_build, :read_pipeline)
+ end
end
- end
- context 'when builds are disabled' do
- before do
- project.project_feature.update!(builds_access_level: ProjectFeature::DISABLED)
+ context 'when builds are disabled' do
+ before do
+ project.project_feature.update!(builds_access_level: ProjectFeature::DISABLED)
+ end
+
+ specify do
+ expect_disallowed(:read_build)
+ expect_allowed(:read_pipeline)
+ end
end
+ end
+ end
- it do
- expect_disallowed(:read_build)
- expect_allowed(:read_pipeline)
+ context 'as an inherited member from the group' do
+ context 'abilities for private projects' do
+ let(:project) { private_project_in_group }
+ let(:current_user) { inherited_guest }
+
+ specify do
+ expect_allowed(*guest_permissions)
+ expect_disallowed(*reporter_public_build_permissions)
+ expect_disallowed(*team_member_reporter_permissions)
+ expect_disallowed(*developer_permissions)
+ expect_disallowed(*maintainer_permissions)
+ expect_disallowed(*owner_permissions)
end
end
end
@@ -181,7 +199,7 @@ RSpec.shared_examples 'project policies as reporter' do
let(:project) { private_project }
let(:current_user) { reporter }
- it do
+ specify do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*team_member_reporter_permissions)
@@ -198,6 +216,22 @@ RSpec.shared_examples 'project policies as reporter' do
let(:regular_abilities) { reporter_permissions }
end
end
+
+ context 'as an inherited member from the group' do
+ context 'abilities for private projects' do
+ let(:project) { private_project_in_group }
+ let(:current_user) { inherited_reporter }
+
+ specify do
+ expect_allowed(*guest_permissions)
+ expect_allowed(*reporter_permissions)
+ expect_allowed(*team_member_reporter_permissions)
+ expect_disallowed(*developer_permissions)
+ expect_disallowed(*maintainer_permissions)
+ expect_disallowed(*owner_permissions)
+ end
+ end
+ end
end
RSpec.shared_examples 'project policies as developer' do
@@ -205,7 +239,7 @@ RSpec.shared_examples 'project policies as developer' do
let(:project) { private_project }
let(:current_user) { developer }
- it do
+ specify do
expect_allowed(*guest_permissions)
expect_allowed(*reporter_permissions)
expect_allowed(*team_member_reporter_permissions)
@@ -222,6 +256,22 @@ RSpec.shared_examples 'project policies as developer' do
let(:regular_abilities) { developer_permissions }
end
end
+
+ context 'as an inherited member from the group' do
+ context 'abilities for private projects' do
+ let(:project) { private_project_in_group }
+ let(:current_user) { inherited_developer }
+
+ specify do
+ expect_allowed(*guest_permissions)
+ expect_allowed(*reporter_permissions)
+ expect_allowed(*team_member_reporter_permissions)
+ expect_allowed(*developer_permissions)
+ expect_disallowed(*maintainer_permissions)
+ expect_disallowed(*owner_permissions)
+ end
+ end
+ end
end
RSpec.shared_examples 'project policies as maintainer' 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
new file mode 100644
index 00000000000..9c2d30a9c8c
--- /dev/null
+++ b/spec/support/shared_examples/projects/container_repository/cleanup_tags_service_shared_examples.rb
@@ -0,0 +1,263 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'handling invalid params' do |service_response_extra: {}, supports_caching: false|
+ context 'when no params are specified' do
+ let(:params) { {} }
+
+ it_behaves_like 'not removing anything',
+ service_response_extra: service_response_extra,
+ supports_caching: supports_caching
+ end
+
+ context 'with invalid regular expressions' do
+ shared_examples 'handling an invalid regex' do
+ it 'keeps all tags' do
+ expect(Projects::ContainerRepository::DeleteTagsService)
+ .not_to receive(:new)
+ expect_no_caching unless supports_caching
+
+ subject
+ end
+
+ it { is_expected.to eq(status: :error, message: 'invalid regex') }
+
+ it 'calls error tracking service' do
+ expect(Gitlab::ErrorTracking).to receive(:log_exception).and_call_original
+
+ subject
+ end
+ end
+
+ context 'when name_regex_delete is invalid' do
+ let(:params) { { 'name_regex_delete' => '*test*' } }
+
+ it_behaves_like 'handling an invalid regex'
+ end
+
+ context 'when name_regex is invalid' do
+ let(:params) { { 'name_regex' => '*test*' } }
+
+ it_behaves_like 'handling an invalid regex'
+ end
+
+ context 'when name_regex_keep is invalid' do
+ let(:params) { { 'name_regex_keep' => '*test*' } }
+
+ it_behaves_like 'handling an invalid regex'
+ end
+ end
+end
+
+RSpec.shared_examples 'when regex matching everything is specified' do
+ |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ let(:params) do
+ { 'name_regex_delete' => '.*' }
+ end
+
+ it_behaves_like 'removing the expected tags',
+ service_response_extra: service_response_extra,
+ supports_caching: supports_caching,
+ delete_expectations: delete_expectations
+
+ context 'with deprecated name_regex param' do
+ let(:params) do
+ { 'name_regex' => '.*' }
+ end
+
+ it_behaves_like 'removing the expected tags',
+ service_response_extra: service_response_extra,
+ supports_caching: supports_caching,
+ delete_expectations: delete_expectations
+ end
+end
+
+RSpec.shared_examples 'when delete regex matching specific tags is used' do
+ |service_response_extra: {}, supports_caching: false|
+ let(:params) do
+ { 'name_regex_delete' => 'C|D' }
+ end
+
+ it_behaves_like 'removing the expected tags',
+ service_response_extra: service_response_extra,
+ supports_caching: supports_caching,
+ delete_expectations: [%w[C D]]
+end
+
+RSpec.shared_examples 'when delete regex matching specific tags is used with overriding allow regex' do
+ |service_response_extra: {}, supports_caching: false|
+ let(:params) do
+ {
+ 'name_regex_delete' => 'C|D',
+ 'name_regex_keep' => 'C'
+ }
+ end
+
+ it_behaves_like 'removing the expected tags',
+ service_response_extra: service_response_extra,
+ supports_caching: supports_caching,
+ delete_expectations: [%w[D]]
+
+ context 'with name_regex_delete overriding deprecated name_regex' do
+ let(:params) do
+ {
+ 'name_regex' => 'C|D',
+ 'name_regex_delete' => 'D'
+ }
+ end
+
+ it_behaves_like 'removing the expected tags',
+ service_response_extra: service_response_extra,
+ supports_caching: supports_caching,
+ delete_expectations: [%w[D]]
+ end
+end
+
+RSpec.shared_examples 'with allow regex value' do
+ |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ let(:params) do
+ {
+ 'name_regex_delete' => '.*',
+ 'name_regex_keep' => 'B.*'
+ }
+ 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 keeping only N tags' do
+ |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ let(:params) do
+ {
+ 'name_regex' => 'A|B.*|C',
+ 'keep_n' => 1
+ }
+ end
+
+ it 'sorts tags by date' do
+ delete_expectations.each { |expectation| expect_delete(expectation) }
+ expect_no_caching unless supports_caching
+
+ expect(service).to receive(:order_by_date_desc).at_least(:once).and_call_original
+
+ is_expected.to eq(expected_service_response(deleted: delete_expectations.flatten).merge(service_response_extra))
+ end
+end
+
+RSpec.shared_examples 'when not keeping N tags' do
+ |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ let(:params) do
+ { 'name_regex' => 'A|B.*|C' }
+ end
+
+ it 'does not sort tags by date' do
+ delete_expectations.each { |expectation| expect_delete(expectation) }
+ expect_no_caching unless supports_caching
+
+ expect(service).not_to receive(:order_by_date_desc)
+
+ is_expected.to eq(expected_service_response(deleted: delete_expectations.flatten).merge(service_response_extra))
+ end
+end
+
+RSpec.shared_examples 'when removing keeping only 3' do
+ |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ let(:params) do
+ { 'name_regex_delete' => '.*',
+ 'keep_n' => 3 }
+ 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 removing older than 1 day' do
+ |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ let(:params) do
+ {
+ 'name_regex_delete' => '.*',
+ 'older_than' => '1 day'
+ }
+ 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 combining all parameters' do
+ |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ let(:params) do
+ {
+ 'name_regex_delete' => '.*',
+ 'keep_n' => 1,
+ 'older_than' => '1 day'
+ }
+ 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 running a container_expiration_policy' do
+ |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ let(:user) { nil }
+
+ context 'with valid container_expiration_policy param' do
+ let(:params) do
+ {
+ 'name_regex_delete' => '.*',
+ 'keep_n' => 1,
+ 'older_than' => '1 day',
+ 'container_expiration_policy' => true
+ }
+ end
+
+ it 'removes the expected tags' do
+ delete_expectations.each { |expectation| expect_delete(expectation, container_expiration_policy: true) }
+ expect_no_caching unless supports_caching
+
+ is_expected.to eq(expected_service_response(deleted: delete_expectations.flatten).merge(service_response_extra))
+ end
+ end
+
+ context 'without container_expiration_policy param' do
+ let(:params) do
+ {
+ 'name_regex_delete' => '.*',
+ 'keep_n' => 1,
+ 'older_than' => '1 day'
+ }
+ end
+
+ it 'fails' do
+ is_expected.to eq(status: :error, message: 'access denied')
+ end
+ end
+end
+
+RSpec.shared_examples 'not removing anything' do |service_response_extra: {}, supports_caching: false|
+ it 'does not remove anything' do
+ expect(Projects::ContainerRepository::DeleteTagsService).not_to receive(:new)
+ expect_no_caching unless supports_caching
+
+ is_expected.to eq(expected_service_response(deleted: []).merge(service_response_extra))
+ end
+end
+
+RSpec.shared_examples 'removing the expected tags' do
+ |service_response_extra: {}, supports_caching: false, delete_expectations:|
+ it 'removes the expected tags' do
+ delete_expectations.each { |expectation| expect_delete(expectation) }
+ expect_no_caching unless supports_caching
+
+ is_expected.to eq(expected_service_response(deleted: delete_expectations.flatten).merge(service_response_extra))
+ end
+end
diff --git a/spec/support/shared_examples/quick_actions/incident/timeline_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/incident/timeline_quick_action_shared_examples.rb
new file mode 100644
index 00000000000..ae7e511a739
--- /dev/null
+++ b/spec/support/shared_examples/quick_actions/incident/timeline_quick_action_shared_examples.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'timeline quick action' do
+ describe '/timeline' do
+ context 'with valid args' do
+ where(:timeline_text, :date_time_arg) do
+ [
+ ['timeline comment', '2022-09-09 09:30'],
+ ['new timeline comment', '09:30'],
+ ['another timeline comment', ' 2022-09-09 09:15']
+ ]
+ end
+
+ with_them do
+ it 'adds a timeline event' do
+ add_note("/timeline #{timeline_text} | #{date_time_arg}")
+
+ expect(page).to have_content('Timeline event added successfully.')
+ expect(issue.incident_management_timeline_events.first.note).to eq(timeline_text)
+ expect(issue.incident_management_timeline_events.first.occurred_at).to eq(DateTime.parse(date_time_arg))
+ end
+ end
+
+ it 'adds a timeline event when no date is passed' do
+ freeze_time do
+ add_note('/timeline timeline event with not date')
+
+ expect(page).to have_content('Timeline event added successfully.')
+ expect(issue.incident_management_timeline_events.first.note).to eq('timeline event with not date')
+ expect(issue.incident_management_timeline_events.first.occurred_at).to eq(DateTime
+ .current.strftime("%Y-%m-%d %H:%M:00 UTC"))
+ end
+ end
+
+ it 'adds a timeline event when only date is passed' do
+ freeze_time do
+ add_note('/timeline timeline event with not date | 2022-10-11')
+
+ expect(page).to have_content('Timeline event added successfully.')
+ expect(issue.incident_management_timeline_events.first.note).to eq('timeline event with not date')
+ expect(issue.incident_management_timeline_events.first.occurred_at).to eq(DateTime
+ .current.strftime("%Y-%m-%d %H:%M:00 UTC"))
+ end
+ end
+ end
+
+ context 'with invalid args' do
+ where(:timeline_text, :date_time_arg) do
+ [
+ ['timeline comment', '2022-13-13 09:30'],
+ ['timeline comment 2', '2022-09-06 24:30']
+ ]
+ end
+
+ with_them do
+ it 'does not add a timeline event' do
+ add_note("/timeline #{timeline_text} | #{date_time_arg}")
+
+ expect(page).to have_content('Failed to apply commands.')
+ expect(issue.incident_management_timeline_events.length).to eq(0)
+ end
+ end
+ end
+
+ context 'when create service fails' do
+ before do
+ allow_next_instance_of(::IncidentManagement::TimelineEvents::CreateService) do |service|
+ allow(service).to receive(:execute).and_return(
+ ServiceResponse.error(payload: { timeline_event: nil }, message: 'Some error')
+ )
+ end
+ end
+
+ it 'does not add a timeline event' do
+ add_note('/timeline text | 2022-09-10 09:30')
+
+ expect(page).to have_content('Something went wrong while adding timeline event.')
+ expect(issue.incident_management_timeline_events.length).to eq(0)
+ end
+ end
+ end
+end
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 f414500f202..18304951e41 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
@@ -1,6 +1,8 @@
# frozen_string_literal: true
RSpec.shared_examples 'issuable time tracker' do |issuable_type|
+ let_it_be(:time_tracker_selector) { '[data-testid="time-tracker"]' }
+
before do
project.add_maintainer(maintainer)
gitlab_sign_in(maintainer)
@@ -12,6 +14,14 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type|
wait_for_requests
end
+ def open_time_tracking_report
+ page.within time_tracker_selector do
+ click_link 'Time tracking report'
+
+ wait_for_requests
+ 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'
@@ -50,7 +60,7 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type|
submit_time('/estimate 3w 1d 1h')
submit_time('/remove_estimate')
- page.within '.time-tracking-component-wrap' do
+ page.within time_tracker_selector do
expect(page).to have_content 'No estimate or time spent'
end
end
@@ -59,13 +69,13 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type|
submit_time('/spend 3w 1d 1h')
submit_time('/remove_time_spent')
- page.within '.time-tracking-component-wrap' do
+ page.within time_tracker_selector do
expect(page).to have_content 'No estimate or time spent'
end
end
it 'shows the help state when icon is clicked' do
- page.within '.time-tracking-component-wrap' 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'
@@ -78,11 +88,7 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type|
wait_for_requests
- page.within '.time-tracking-component-wrap' do
- click_link 'Time tracking report'
-
- wait_for_requests
- end
+ open_time_tracking_report
page.within '#time-tracking-report' do
expect(find('tbody')).to have_content maintainer.name
@@ -90,8 +96,36 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type|
end
end
+ it 'removes time log when delete is clicked in time tracking report' do
+ submit_time('/estimate 1w')
+ submit_time('/spend 1d')
+ submit_time('/spend 3d')
+
+ wait_for_requests
+
+ open_time_tracking_report
+
+ page.within '#time-tracking-report tbody tr:nth-child(2)' do
+ click_button test_id: 'deleteButton'
+
+ wait_for_requests
+ end
+
+ # Assert that 2nd row was removed
+ expect(all('#time-tracking-report tbody tr').length).to eq(1)
+ expect(find('#time-tracking-report tbody')).not_to have_content('3d')
+
+ # Assert that summary line was updated
+ expect(find('#time-tracking-report tfoot')).to have_content('1d', exact: true)
+
+ # Assert that the time tracking widget was reactively updated
+ page.within '[data-testid="timeTrackingComparisonPane"]' do
+ expect(page).to have_content '1d'
+ end
+ end
+
it 'hides the help state when close icon is clicked' do
- page.within '.time-tracking-component-wrap' do
+ page.within time_tracker_selector do
find('[data-testid="helpButton"]').click
find('[data-testid="closeHelpButton"]').click
@@ -101,7 +135,7 @@ RSpec.shared_examples 'issuable time tracker' do |issuable_type|
end
it 'displays the correct help url' do
- page.within '.time-tracking-component-wrap' 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')
diff --git a/spec/support/shared_examples/quick_actions/merge_request/rebase_quick_action_shared_examples.rb b/spec/support/shared_examples/quick_actions/merge_request/rebase_quick_action_shared_examples.rb
index 2258bdd2c79..92705fc1b4d 100644
--- a/spec/support/shared_examples/quick_actions/merge_request/rebase_quick_action_shared_examples.rb
+++ b/spec/support/shared_examples/quick_actions/merge_request/rebase_quick_action_shared_examples.rb
@@ -75,7 +75,16 @@ RSpec.shared_examples 'rebase quick action' do
end
context 'when the merge request branch is protected from force push' do
- let!(:protected_branch) { create(:protected_branch, project: project, name: merge_request.source_branch, allow_force_push: false) }
+ let!(:protected_branch) do
+ ProtectedBranches::CreateService.new(
+ project,
+ user,
+ name: merge_request.source_branch,
+ allow_force_push: false,
+ push_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }],
+ merge_access_levels_attributes: [{ access_level: Gitlab::Access::DEVELOPER }]
+ ).execute
+ end
it 'does not rebase the MR' do
add_note("/rebase")
diff --git a/spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb b/spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb
index 6cd871d354c..017e6274cb0 100644
--- a/spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb
+++ b/spec/support/shared_examples/requests/access_tokens_controller_shared_examples.rb
@@ -108,18 +108,31 @@ RSpec.shared_examples 'PUT resource access tokens available' do
expect(resource.reload.bots).not_to include(bot_user)
end
- it 'converts issuables of the bot user to ghost user' do
- issue = create(:issue, author: bot_user)
+ context 'when user_destroy_with_limited_execution_time_worker is enabled' do
+ it 'creates GhostUserMigration records to handle migration in a worker' do
+ expect { subject }.to(
+ change { Users::GhostUserMigration.count }.from(0).to(1))
+ end
+ end
- subject
+ context 'when user_destroy_with_limited_execution_time_worker is disabled' do
+ before do
+ stub_feature_flags(user_destroy_with_limited_execution_time_worker: false)
+ end
- expect(issue.reload.author.ghost?).to be true
- end
+ it 'converts issuables of the bot user to ghost user' do
+ issue = create(:issue, author: bot_user)
- it 'deletes project bot user' do
- subject
+ subject
+
+ expect(issue.reload.author.ghost?).to be true
+ end
- expect(User.exists?(bot_user.id)).to be_falsy
+ it 'deletes project bot user' do
+ subject
+
+ expect(User.exists?(bot_user.id)).to be_falsy
+ end
end
context 'when unsuccessful' do
diff --git a/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb
index dc2c4f890b1..6a77de4266f 100644
--- a/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb
@@ -108,6 +108,7 @@ RSpec.shared_examples 'process Composer api request' do |user_type, status, add_
end
it_behaves_like 'returning response status', status
+ it_behaves_like 'bumping the package last downloaded at field' if status == :success
end
end
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 bb2f8965294..629d93676eb 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
@@ -355,7 +355,7 @@ RSpec.shared_examples 'recipe download_urls' do
it 'returns the download_urls for the recipe files' do
expected_response = {
- 'conanfile.py' => "#{url_prefix}/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conanfile.py",
+ 'conanfile.py' => "#{url_prefix}/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conanfile.py",
'conanmanifest.txt' => "#{url_prefix}/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conanmanifest.txt"
}
@@ -372,7 +372,7 @@ RSpec.shared_examples 'package download_urls' do
it 'returns the download_urls for the package files' do
expected_response = {
- 'conaninfo.txt' => "#{url_prefix}/packages/conan/v1/files/#{package.conan_recipe_path}/0/package/123456789/0/conaninfo.txt",
+ 'conaninfo.txt' => "#{url_prefix}/packages/conan/v1/files/#{package.conan_recipe_path}/0/package/123456789/0/conaninfo.txt",
'conanmanifest.txt' => "#{url_prefix}/packages/conan/v1/files/#{package.conan_recipe_path}/0/package/123456789/0/conanmanifest.txt",
'conan_package.tgz' => "#{url_prefix}/packages/conan/v1/files/#{package.conan_recipe_path}/0/package/123456789/0/conan_package.tgz"
}
@@ -412,7 +412,7 @@ RSpec.shared_examples 'recipe snapshot endpoint' do
conan_manifest_file = package.package_files.find_by(file_name: 'conanmanifest.txt')
expected_response = {
- 'conanfile.py' => conan_file_file.file_md5,
+ 'conanfile.py' => conan_file_file.file_md5,
'conanmanifest.txt' => conan_manifest_file.file_md5
}
@@ -435,7 +435,7 @@ RSpec.shared_examples 'package snapshot endpoint' do
context 'with existing package' do
it 'returns a hash of md5 values for the files' do
expected_response = {
- 'conaninfo.txt' => "12345abcde",
+ 'conaninfo.txt' => "12345abcde",
'conanmanifest.txt' => "12345abcde",
'conan_package.tgz' => "12345abcde"
}
@@ -486,7 +486,7 @@ RSpec.shared_examples 'recipe upload_urls endpoint' do
subject
expected_response = {
- 'conanfile.py': "#{url_prefix}/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conanfile.py",
+ 'conanfile.py': "#{url_prefix}/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conanfile.py",
'conanmanifest.txt': "#{url_prefix}/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conanmanifest.txt"
}
@@ -505,7 +505,7 @@ RSpec.shared_examples 'recipe upload_urls endpoint' do
expected_response = {
'conan_sources.tgz': "#{url_prefix}/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conan_sources.tgz",
- 'conan_export.tgz': "#{url_prefix}/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conan_export.tgz",
+ 'conan_export.tgz': "#{url_prefix}/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conan_export.tgz",
'conanmanifest.txt': "#{url_prefix}/packages/conan/v1/files/#{package.conan_recipe_path}/0/export/conanmanifest.txt"
}
@@ -547,7 +547,7 @@ RSpec.shared_examples 'package upload_urls endpoint' do
it 'returns a set of upload urls for the files requested' do
expected_response = {
- 'conaninfo.txt': "#{url_prefix}/packages/conan/v1/files/#{package.conan_recipe_path}/0/package/123456789/0/conaninfo.txt",
+ 'conaninfo.txt': "#{url_prefix}/packages/conan/v1/files/#{package.conan_recipe_path}/0/package/123456789/0/conaninfo.txt",
'conanmanifest.txt': "#{url_prefix}/packages/conan/v1/files/#{package.conan_recipe_path}/0/package/123456789/0/conanmanifest.txt",
'conan_package.tgz': "#{url_prefix}/packages/conan/v1/files/#{package.conan_recipe_path}/0/package/123456789/0/conan_package.tgz"
}
@@ -631,6 +631,7 @@ RSpec.shared_examples 'a public project with packages' do
end
it_behaves_like 'allows download with no token'
+ it_behaves_like 'bumping the package last downloaded at field'
it 'returns the file' do
subject
@@ -647,6 +648,7 @@ RSpec.shared_examples 'an internal project with packages' do
end
it_behaves_like 'denies download with no token'
+ it_behaves_like 'bumping the package last downloaded at field'
it 'returns the file' do
subject
@@ -662,6 +664,7 @@ RSpec.shared_examples 'a private project with packages' do
end
it_behaves_like 'denies download with no token'
+ it_behaves_like 'bumping the package last downloaded at field'
it 'returns the file' do
subject
diff --git a/spec/support/shared_examples/requests/api/graphql/issuable_search_shared_examples.rb b/spec/support/shared_examples/requests/api/graphql/issuable_search_shared_examples.rb
new file mode 100644
index 00000000000..22805cf7aed
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/graphql/issuable_search_shared_examples.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+# Requires `query(params)` , `user`, `issuable_data` and `issuable` bindings
+RSpec.shared_examples 'query with a search term' do
+ it 'returns only matching issuables' do
+ filter_params = { search: 'bar', in: [:DESCRIPTION] }
+ graphql_query = query(filter_params)
+
+ post_graphql(graphql_query, current_user: user)
+ ids = graphql_dig_at(issuable_data, :node, :id)
+
+ expect(ids).to contain_exactly(issuable.to_global_id.to_s)
+ 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 9f7ec6e90e9..1b609915f32 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
@@ -9,9 +9,10 @@ RSpec.shared_examples 'group and project packages query' do
let_it_be(:composer_package) { create(:composer_package, project: project2, name: 'dab', version: '4.0.0', created_at: 3.days.ago) }
let_it_be(:debian_package) { create(:debian_package, project: project2, name: 'aab', version: '5.0.0', created_at: 2.days.ago) }
let_it_be(:composer_metadatum) do
- create(:composer_metadatum, package: composer_package,
- target_sha: 'afdeh',
- composer_json: { name: 'x', type: 'y', license: 'z', version: 1 })
+ create(:composer_metadatum,
+ package: composer_package,
+ target_sha: 'afdeh',
+ composer_json: { name: 'x', type: 'y', license: 'z', version: 1 })
end
let(:package_names) { graphql_data_at(resource_type, :packages, :nodes, :name) }
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 acbcf4f7f3d..06ed0448b50 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
@@ -191,14 +191,15 @@ RSpec.shared_examples 'process helm download content request' do |user_type, sta
end
end
- it_behaves_like 'a package tracking event', 'API::HelmPackages', 'pull_package'
-
it 'returns expected status and a valid package archive' do
subject
expect(response).to have_gitlab_http_status(status)
expect(response.media_type).to eq('application/octet-stream')
end
+
+ it_behaves_like 'a package tracking event', 'API::HelmPackages', 'pull_package'
+ it_behaves_like 'bumping the package last downloaded at field'
end
end
@@ -278,7 +279,6 @@ RSpec.shared_examples 'handling helm chart index requests' do
end
it_behaves_like 'deploy token for package GET requests'
-
it_behaves_like 'rejects helm access with unknown project id' do
subject { get api(url) }
end
diff --git a/spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb b/spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb
index 971b21b5b32..8c4ff120471 100644
--- a/spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/issues/merge_requests_count_shared_examples.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
def get_issue
- json_response.is_a?(Array) ? json_response.detect {|issue| issue['id'] == target_issue.id} : json_response
+ json_response.is_a?(Array) ? json_response.detect { |issue| issue['id'] == target_issue.id } : json_response
end
RSpec.shared_examples 'accessible merge requests count' do
diff --git a/spec/support/shared_examples/requests/api/labels_api_shared_examples.rb b/spec/support/shared_examples/requests/api/labels_api_shared_examples.rb
index 02e50b789cc..41d21490343 100644
--- a/spec/support/shared_examples/requests/api/labels_api_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/labels_api_shared_examples.rb
@@ -9,6 +9,6 @@ RSpec.shared_examples 'fetches labels' do
expect(json_response).to be_an Array
expect(json_response).to all(match_schema('public_api/v4/labels/label'))
expect(json_response.size).to eq(expected_labels.size)
- expect(json_response.map {|r| r['name'] }).to match_array(expected_labels)
+ expect(json_response.map { |r| r['name'] }).to match_array(expected_labels)
end
end
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 6568d51b90e..fdd55893deb 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
@@ -293,6 +293,8 @@ RSpec.shared_examples 'process nuget download content request' do |user_type, st
it_behaves_like 'a package tracking event', 'API::NugetPackages', 'pull_package'
+ it_behaves_like 'bumping the package last downloaded at field'
+
it 'returns a valid package archive' do
subject
@@ -315,6 +317,8 @@ RSpec.shared_examples 'process nuget download content request' do |user_type, st
end
it_behaves_like 'a package tracking event', 'API::NugetPackages', 'pull_symbol_package'
+
+ it_behaves_like 'bumping the package last downloaded at field'
end
context 'with lower case package name' do
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 eb650b7a09f..860cb1b1d86 100644
--- a/spec/support/shared_examples/requests/api/packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/packages_shared_examples.rb
@@ -165,3 +165,10 @@ RSpec.shared_examples 'not a package tracking event' do
expect_no_snowplow_event
end
end
+
+RSpec.shared_examples 'bumping the package last downloaded at field' do
+ it 'bumps last_downloaded_at' do
+ expect { subject }
+ .to change { package.reload.last_downloaded_at }.from(nil).to(instance_of(ActiveSupport::TimeWithZone))
+ 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 ba8311bf0be..f411b5699a9 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
@@ -167,6 +167,7 @@ RSpec.shared_examples 'PyPI package download' do |user_type, status, add_member
it_behaves_like 'returning response status', status
it_behaves_like 'a package tracking event', described_class.name, 'pull_package'
+ it_behaves_like 'bumping the package last downloaded at field'
end
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 3ca2b9fa6de..2d036cb2aa3 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
@@ -70,7 +70,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'] }
+ json_ids = json_response.map { |storage_move| storage_move['id'] }
expect(json_ids).to eq([
storage_move.id,
storage_move_middle.id,
diff --git a/spec/support/shared_examples/requests/api/resource_state_events_api_shared_examples.rb b/spec/support/shared_examples/requests/api/resource_state_events_api_shared_examples.rb
new file mode 100644
index 00000000000..c1850a0d0c9
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/resource_state_events_api_shared_examples.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'resource_state_events API' do |parent_type, eventable_type, id_name|
+ let(:base_path) { "/#{parent_type}/#{parent.id}/#{eventable_type}/#{eventable[id_name]}" }
+
+ describe "GET /#{parent_type}/:id/#{eventable_type}/:noteable_id/resource_state_events" do
+ let!(:event) { create_event }
+
+ it "returns an array of resource state events" do
+ url = "#{base_path}/resource_state_events"
+ get api(url, user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.first['id']).to eq(event.id)
+ expect(json_response.first['state']).to eq(event.state.to_s)
+ end
+
+ it "returns a 404 error when eventable id not found" do
+ get api("/#{parent_type}/#{parent.id}/#{eventable_type}/#{non_existing_record_id}/resource_state_events", user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it "returns 404 when not authorized" do
+ parent.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ private_user = create(:user)
+
+ get api("#{base_path}/resource_state_events", private_user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ describe "GET /#{parent_type}/:id/#{eventable_type}/:noteable_id/resource_state_events/:event_id" do
+ let!(:event) { create_event }
+
+ it "returns a resource state event by id" do
+ get api("#{base_path}/resource_state_events/#{event.id}", user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['id']).to eq(event.id)
+ expect(json_response['state']).to eq(event.state.to_s)
+ end
+
+ it "returns 404 when not authorized" do
+ parent.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ private_user = create(:user)
+
+ get api("#{base_path}/resource_state_events/#{event.id}", private_user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+
+ it "returns a 404 error if resource state event not found" do
+ get api("#{base_path}/resource_state_events/#{non_existing_record_id}", user)
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ describe 'pagination' do
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/220192
+ it 'returns the second page' do
+ create_event
+ event2 = create_event
+
+ get api("#{base_path}/resource_state_events?page=2&per_page=1", user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to include_pagination_headers
+ expect(response.headers['X-Total']).to eq '2'
+ expect(json_response.count).to eq(1)
+ expect(json_response.first['id']).to eq(event2.id)
+ end
+ end
+
+ def create_event(state: :opened)
+ create(:resource_state_event, eventable.class.name.underscore => eventable, state: state)
+ end
+end
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 abdb468353a..f075927e7bf 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
@@ -203,5 +203,6 @@ RSpec.shared_examples 'Rubygems gem download' do |user_type, status, add_member
end
it_behaves_like 'a package tracking event', described_class.name, 'pull_package'
+ it_behaves_like 'bumping the package last downloaded at field'
end
end
diff --git a/spec/support/shared_examples/requests/api/snippets_shared_examples.rb b/spec/support/shared_examples/requests/api/snippets_shared_examples.rb
index 2b72c69cb37..1b92eb56f54 100644
--- a/spec/support/shared_examples/requests/api/snippets_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/snippets_shared_examples.rb
@@ -133,7 +133,7 @@ RSpec.shared_examples 'snippet file updates' do
context 'when save fails due to a repository commit error' do
before do
allow_next_instance_of(Repository) do |instance|
- allow(instance).to receive(:multi_action).and_raise(Gitlab::Git::CommitError)
+ allow(instance).to receive(:commit_files).and_raise(Gitlab::Git::CommitError)
end
update_snippet(params: { files: [create_action] })
diff --git a/spec/support/shared_examples/requests/applications_controller_shared_examples.rb b/spec/support/shared_examples/requests/applications_controller_shared_examples.rb
index 8f852d42c2c..642930dd982 100644
--- a/spec/support/shared_examples/requests/applications_controller_shared_examples.rb
+++ b/spec/support/shared_examples/requests/applications_controller_shared_examples.rb
@@ -11,6 +11,7 @@ RSpec.shared_examples 'applications controller - GET #show' do
context 'when application is viewed after being created' do
before do
create_application
+ stub_feature_flags(hash_oauth_secrets: false)
end
it 'sets `@created` instance variable to `true`' do
@@ -21,6 +22,10 @@ RSpec.shared_examples 'applications controller - GET #show' do
end
context 'when application is reviewed' do
+ before do
+ stub_feature_flags(hash_oauth_secrets: false)
+ end
+
it 'sets `@created` instance variable to `false`' do
get show_path
@@ -32,6 +37,7 @@ end
RSpec.shared_examples 'applications controller - POST #create' do
it "sets `#{OauthApplications::CREATED_SESSION_KEY}` session key to `true`" do
+ stub_feature_flags(hash_oauth_secrets: false)
create_application
expect(session[OauthApplications::CREATED_SESSION_KEY]).to eq(true)
diff --git a/spec/support/shared_examples/requests/lfs_http_shared_examples.rb b/spec/support/shared_examples/requests/lfs_http_shared_examples.rb
index 294ceffd77b..83be0cc1fe3 100644
--- a/spec/support/shared_examples/requests/lfs_http_shared_examples.rb
+++ b/spec/support/shared_examples/requests/lfs_http_shared_examples.rb
@@ -49,7 +49,7 @@ RSpec.shared_examples 'LFS http 404 response' do
end
RSpec.shared_examples 'LFS http expected response code and message' do
- let(:response_code) { }
+ let(:response_code) {}
let(:response_headers) { {} }
let(:content_type) { LfsRequest::CONTENT_TYPE }
let(:message) {}
diff --git a/spec/support/shared_examples/requests/projects/google_cloud/google_cloud_ff_examples.rb b/spec/support/shared_examples/requests/projects/google_cloud/google_cloud_ff_examples.rb
new file mode 100644
index 00000000000..d49fe517c60
--- /dev/null
+++ b/spec/support/shared_examples/requests/projects/google_cloud/google_cloud_ff_examples.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'requires feature flag `incubation_5mp_google_cloud` enabled' do
+ context 'when feature flag is disabled' do
+ before do
+ project.add_maintainer(user)
+ stub_feature_flags(incubation_5mp_google_cloud: false)
+ end
+
+ it 'renders not found' do
+ sign_in(user)
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/projects/google_cloud/google_cloud_role_examples.rb b/spec/support/shared_examples/requests/projects/google_cloud/google_cloud_role_examples.rb
new file mode 100644
index 00000000000..4c616b59be0
--- /dev/null
+++ b/spec/support/shared_examples/requests/projects/google_cloud/google_cloud_role_examples.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'requires `admin_project_google_cloud` role' do
+ shared_examples 'returns not_found' do
+ it 'returns not found' do
+ sign_in(user)
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:not_found)
+ end
+ end
+
+ shared_examples 'redirects to authorize url' do
+ it 'redirects to authorize url' do
+ sign_in(user)
+
+ subject
+
+ expect(response).to redirect_to(assigns(:authorize_url))
+ end
+ end
+
+ context 'when requested by users with different roles' do
+ let_it_be(:guest) { create(:user) }
+ let_it_be(:developer) { create(:user) }
+ let_it_be(:maintainer) { create(:user) }
+
+ before do
+ project.add_guest(guest)
+ project.add_developer(developer)
+ project.add_maintainer(maintainer)
+ end
+
+ context 'for unauthorized users' do
+ include_examples 'returns not_found' do
+ let(:user) { guest }
+ end
+
+ include_examples 'returns not_found' do
+ let(:user) { developer }
+ end
+ end
+
+ context 'for authorized users' do
+ include_examples 'redirects to authorize url' do
+ let(:user) { maintainer }
+ end
+
+ include_examples 'redirects to authorize url' do
+ let(:user) { project.owner }
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/projects/google_cloud/google_oauth2_config_examples.rb b/spec/support/shared_examples/requests/projects/google_cloud/google_oauth2_config_examples.rb
new file mode 100644
index 00000000000..63f6cffb3a0
--- /dev/null
+++ b/spec/support/shared_examples/requests/projects/google_cloud/google_oauth2_config_examples.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'requires valid Google OAuth2 configuration' do
+ context 'when GitLab instance does not have valid Google OAuth2 configuration ' do
+ before do
+ project.add_maintainer(user)
+ unconfigured_google_oauth2 = Struct.new(:app_id, :app_secret)
+ .new('', '')
+ allow(Gitlab::Auth::OAuth::Provider).to receive(:config_for)
+ .with('google_oauth2')
+ .and_return(unconfigured_google_oauth2)
+ end
+
+ it 'renders forbidden' do
+ sign_in(user)
+
+ subject
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/projects/google_cloud/google_oauth2_token_examples.rb b/spec/support/shared_examples/requests/projects/google_cloud/google_oauth2_token_examples.rb
new file mode 100644
index 00000000000..379327be0db
--- /dev/null
+++ b/spec/support/shared_examples/requests/projects/google_cloud/google_oauth2_token_examples.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'requires valid Google Oauth2 token' do
+ context 'when a valid Google OAuth2 token does not exist' do
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+
+ it 'triggers Google OAuth2 flow on request' do
+ subject
+
+ expect(response).to redirect_to(assigns(:authorize_url))
+ end
+
+ context 'and a valid Google OAuth2 token gets created' do
+ before do
+ allow_next_instance_of(GoogleApi::CloudPlatform::Client) do |client|
+ allow(client).to receive(:validate_token).and_return(true)
+ allow(client).to receive(:list_projects).and_return(mock_gcp_projects) if mock_gcp_projects
+ end
+
+ allow_next_instance_of(BranchesFinder) do |finder|
+ allow(finder).to receive(:execute).and_return(mock_branches) if mock_branches
+ end
+
+ allow_next_instance_of(TagsFinder) do |finder|
+ allow(finder).to receive(:execute).and_return(mock_branches) if mock_branches
+ end
+ end
+
+ it 'renders template as expected' do
+ if renders_template
+ subject
+ expect(response).to render_template(renders_template)
+ end
+ end
+
+ it 'redirects as expected' do
+ if redirects_to
+ subject
+ expect(response).to redirect_to(redirects_to)
+ end
+ end
+ 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 d4417b23a5f..11759b6671f 100644
--- a/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
+++ b/spec/support/shared_examples/requests/rack_attack_shared_examples.rb
@@ -518,7 +518,7 @@ RSpec.shared_examples 'rate-limited unauthenticated requests' do
context 'when the request is to the api internal endpoints' do
it 'allows requests over the rate limit' do
(1 + requests_per_period).times do
- get '/api/v4/internal/check', params: { secret_token: Gitlab::Shell.secret_token }
+ get '/api/v4/internal/check', headers: GitlabShellHelpers.gitlab_shell_internal_api_request_header
expect(response).to have_gitlab_http_status(:ok)
end
end
diff --git a/spec/support/shared_examples/routing/resource_routing_shared_examples.rb b/spec/support/shared_examples/routing/resource_routing_shared_examples.rb
index b98901a57ea..d7a674f3522 100644
--- a/spec/support/shared_examples/routing/resource_routing_shared_examples.rb
+++ b/spec/support/shared_examples/routing/resource_routing_shared_examples.rb
@@ -43,12 +43,12 @@ RSpec.shared_examples 'resource routing' do
let(:default_actions) do
{
- index: [:get, ''],
- show: [:get, '/:id'],
- new: [:get, '/new'],
- create: [:post, ''],
- edit: [:get, '/:id/edit'],
- update: [:put, '/:id'],
+ index: [:get, ''],
+ show: [:get, '/:id'],
+ new: [:get, '/new'],
+ create: [:post, ''],
+ edit: [:get, '/:id/edit'],
+ update: [:put, '/:id'],
destroy: [:delete, '/:id']
}
end
diff --git a/spec/support/shared_examples/routing/wiki_routing_shared_examples.rb b/spec/support/shared_examples/routing/wiki_routing_shared_examples.rb
index 9289934677e..64f237f0d4d 100644
--- a/spec/support/shared_examples/routing/wiki_routing_shared_examples.rb
+++ b/spec/support/shared_examples/routing/wiki_routing_shared_examples.rb
@@ -6,9 +6,9 @@ RSpec.shared_examples 'wiki routing' do
let(:actions) { %i[show new create edit update destroy] }
let(:additional_actions) do
{
- pages: [:get, '/pages'],
- history: [:get, '/:id/history'],
- git_access: [:get, '/git_access'],
+ pages: [:get, '/pages'],
+ history: [:get, '/:id/history'],
+ git_access: [:get, '/git_access'],
preview_markdown: [:post, '/:id/preview_markdown']
}
end
diff --git a/spec/support/shared_examples/security_training_providers_importer.rb b/spec/support/shared_examples/security_training_providers_importer.rb
new file mode 100644
index 00000000000..568e3e1a4f2
--- /dev/null
+++ b/spec/support/shared_examples/security_training_providers_importer.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'security training providers importer' do
+ let(:security_training_providers) do
+ Class.new(ApplicationRecord) do
+ self.table_name = 'security_training_providers'
+ end
+ end
+
+ it 'upserts security training providers' do
+ 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/serializers/environment_serializer_shared_examples.rb b/spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb
index 6d59943d91c..9eaad541df7 100644
--- a/spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb
+++ b/spec/support/shared_examples/serializers/environment_serializer_shared_examples.rb
@@ -9,7 +9,8 @@ RSpec.shared_examples 'avoid N+1 on environments serialization' do
create_environment_with_associations(project)
# See issue: https://gitlab.com/gitlab-org/gitlab/-/issues/363317
- relax_count = 1
+ # See also: https://gitlab.com/gitlab-org/gitlab/-/issues/373151
+ relax_count = 4
expect { serialize(grouping: true) }.not_to exceed_query_limit(control.count + relax_count)
end
@@ -23,7 +24,8 @@ RSpec.shared_examples 'avoid N+1 on environments serialization' do
create_environment_with_associations(project)
# See issue: https://gitlab.com/gitlab-org/gitlab/-/issues/363317
- relax_count = 1
+ # See also: https://gitlab.com/gitlab-org/gitlab/-/issues/373151
+ relax_count = 5
expect { serialize(grouping: false) }.not_to exceed_query_limit(control.count + relax_count)
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 6cae7d8e00f..0db9519f760 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
@@ -23,12 +23,10 @@ RSpec.shared_examples 'creates an alert management alert or errors' do
end
context 'and fails to save' do
- let(:errors) { double(messages: { hosts: ['hosts array is over 255 chars'] }, '[]': [] )}
-
before do
- allow(service).to receive(:alert).and_call_original
- allow(service).to receive_message_chain(:alert, :save).and_return(false)
- allow(service).to receive_message_chain(:alert, :errors).and_return(errors)
+ allow(AlertManagement::Alert).to receive(:new).and_wrap_original do |m, **args|
+ m.call(**args, hosts: ['a' * 256]) # hosts should be 255
+ end
end
it_behaves_like 'alerts service responds with an error', :bad_request
diff --git a/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb b/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb
index a46c2f0ac5c..162be24fe8f 100644
--- a/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/boards/issues_move_service_shared_examples.rb
@@ -3,8 +3,8 @@
RSpec.shared_examples 'issues move service' do |group|
shared_examples 'updating timestamps' do
it 'updates updated_at' do
- expect {described_class.new(parent, user, params).execute(issue)}
- .to change {issue.reload.updated_at}
+ expect { described_class.new(parent, user, params).execute(issue) }
+ .to change { issue.reload.updated_at }
end
end
@@ -140,6 +140,40 @@ RSpec.shared_examples 'issues move service' do |group|
expect(issue2.reload.updated_at.change(usec: 0)).to eq updated_at2.change(usec: 0)
end
+ context 'when moving to a specific list position' do
+ before do
+ [issue1, issue2, issue].each do |issue|
+ issue.move_to_end && issue.save!
+ end
+ end
+
+ it 'moves issue to the top of the list' do
+ described_class.new(parent, user, params.merge({ position_in_list: 0 })).execute(issue)
+
+ expect(issue.relative_position).to be < issue1.relative_position
+ end
+
+ it 'moves issue to a position in the middle of the list' do
+ described_class.new(parent, user, params.merge({ position_in_list: 1 })).execute(issue)
+
+ expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position)
+ end
+
+ it 'moves issue to the bottom of the list' do
+ described_class.new(parent, user, params.merge({ position_in_list: -1 })).execute(issue1)
+
+ expect(issue1.relative_position).to be > issue.relative_position
+ end
+
+ context 'when given position is greater than number of issues in the list' do
+ it 'moves the issue to the bottom of the list' do
+ described_class.new(parent, user, params.merge({ position_in_list: 5 })).execute(issue1)
+
+ expect(issue1.relative_position).to be > issue.relative_position
+ end
+ end
+ end
+
def reorder_issues(params, issues: [])
issues.each do |issue|
issue.move_to_end && issue.save!
diff --git a/spec/support/shared_examples/services/common_system_notes_shared_examples.rb b/spec/support/shared_examples/services/common_system_notes_shared_examples.rb
index ce412ef55de..1887b38b50e 100644
--- a/spec/support/shared_examples/services/common_system_notes_shared_examples.rb
+++ b/spec/support/shared_examples/services/common_system_notes_shared_examples.rb
@@ -42,6 +42,7 @@ RSpec.shared_examples 'a system note' do |params|
it 'has the correct attributes', :aggregate_failures do
exclude_project = !params.nil? && params[:exclude_project]
+ skip_persistence_check = !params.nil? && params[:skip_persistence_check]
expect(subject).to be_valid
expect(subject).to be_system
@@ -50,6 +51,7 @@ RSpec.shared_examples 'a system note' do |params|
expect(subject.project).to eq project unless exclude_project
expect(subject.author).to eq author
+ expect(subject.system_note_metadata).to be_persisted unless skip_persistence_check
expect(subject.system_note_metadata.action).to eq(action)
expect(subject.system_note_metadata.commit_count).to eq(commit_count)
end
diff --git a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
index 3be59af6a37..58659775d8c 100644
--- a/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/container_registry_auth_service_shared_examples.rb
@@ -54,6 +54,12 @@ RSpec.shared_examples 'a valid token' do
end
end
+RSpec.shared_examples 'with auth_type' do
+ let(:current_params) { super().merge(auth_type: :foo) }
+
+ it { expect(payload['auth_type']).to eq('foo') }
+end
+
RSpec.shared_examples 'a browsable' do
let(:access) do
[{ 'type' => 'registry',
@@ -199,8 +205,8 @@ RSpec.shared_examples 'a container registry auth service' do
describe '.import_access_token' do
let(:access) do
[{ 'type' => 'registry',
- 'name' => 'import',
- 'actions' => ['*'] }]
+ 'name' => 'import',
+ 'actions' => ['*'] }]
end
let(:token) { described_class.import_access_token }
@@ -286,6 +292,7 @@ RSpec.shared_examples 'a container registry auth service' do
shared_examples 'private project' do
context 'allow to use scope-less authentication' do
it_behaves_like 'a valid token'
+ it_behaves_like 'with auth_type'
end
context 'allow developer to push images' do
@@ -299,6 +306,7 @@ RSpec.shared_examples 'a container registry auth service' do
it_behaves_like 'a pushable'
it_behaves_like 'container repository factory'
+ it_behaves_like 'with auth_type'
end
context 'disallow developer to delete images' do
@@ -341,6 +349,7 @@ RSpec.shared_examples 'a container registry auth service' do
it_behaves_like 'a pullable'
it_behaves_like 'not a container repository factory'
+ it_behaves_like 'with auth_type'
end
end
@@ -381,6 +390,7 @@ RSpec.shared_examples 'a container registry auth service' do
it_behaves_like 'a pullable'
it_behaves_like 'not a container repository factory'
+ it_behaves_like 'with auth_type'
end
context 'disallow guest to pull or push images' do
@@ -445,6 +455,7 @@ RSpec.shared_examples 'a container registry auth service' do
it_behaves_like 'a pullable'
it_behaves_like 'not a container repository factory'
+ it_behaves_like 'with auth_type'
end
context 'disallow anyone to push images' do
@@ -495,6 +506,7 @@ RSpec.shared_examples 'a container registry auth service' do
it_behaves_like 'a pullable'
it_behaves_like 'not a container repository factory'
+ it_behaves_like 'with auth_type'
end
context 'disallow anyone to push images' do
@@ -600,6 +612,7 @@ RSpec.shared_examples 'a container registry auth service' do
end
it_behaves_like 'a valid token'
+ it_behaves_like 'with auth_type'
context 'allow to pull and push images' do
let(:current_params) do
@@ -944,10 +957,11 @@ RSpec.shared_examples 'a container registry auth service' do
shared_examples 'able to login' do
context 'registry provides read_container_image authentication_abilities' do
- let(:current_params) { { deploy_token: deploy_token } }
+ let(:current_params) { { deploy_token: deploy_token, auth_type: :deploy_token } }
let(:authentication_abilities) { [:read_container_image] }
it_behaves_like 'an authenticated'
+ it { expect(payload['auth_type']).to eq('deploy_token') }
end
end
diff --git a/spec/support/shared_examples/services/feature_flags/client_shared_examples.rb b/spec/support/shared_examples/services/feature_flags/client_shared_examples.rb
index a62cffc0e1b..73a02905914 100644
--- a/spec/support/shared_examples/services/feature_flags/client_shared_examples.rb
+++ b/spec/support/shared_examples/services/feature_flags/client_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-shared_examples_for 'update feature flag client' do
+RSpec.shared_examples_for 'update feature flag client' do
let!(:client) { create(:operations_feature_flags_client, project: project) }
it 'updates last feature flag updated at' do
@@ -10,7 +10,7 @@ shared_examples_for 'update feature flag client' do
end
end
-shared_examples_for 'does not update feature flag client' do
+RSpec.shared_examples_for 'does not update feature flag client' do
let!(:client) { create(:operations_feature_flags_client, project: project) }
it 'does not update last feature flag updated at' do
diff --git a/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb b/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb
index 2aac7e328f0..366fa4763e1 100644
--- a/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/gitlab_projects_import_service_shared_examples.rb
@@ -33,7 +33,7 @@ RSpec.shared_examples 'gitlab projects import validations' do
context 'when there is a project with the same path' do
let(:existing_project) { create(:project, namespace: namespace) }
- let(:path) { existing_project.path}
+ let(:path) { existing_project.path }
it 'does not create the project' do
project = subject.execute
diff --git a/spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb b/spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb
index 31571b1ffb9..92681b8ba79 100644
--- a/spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/issuable/destroy_service_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-shared_examples_for 'service scheduling async deletes' do
+RSpec.shared_examples_for 'service scheduling async deletes' do
it 'destroys associated todos asynchronously' do
expect(worker_class)
.to receive(:perform_async)
@@ -20,13 +20,13 @@ shared_examples_for 'service scheduling async deletes' do
end
end
-shared_examples_for 'service deleting todos' do
+RSpec.shared_examples_for 'service deleting todos' do
it_behaves_like 'service scheduling async deletes' do
let(:worker_class) { TodosDestroyer::DestroyedIssuableWorker }
end
end
-shared_examples_for 'service deleting label links' do
+RSpec.shared_examples_for 'service deleting label links' do
it_behaves_like 'service scheduling async deletes' do
let(:worker_class) { Issuable::LabelLinksDestroyWorker }
end
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
new file mode 100644
index 00000000000..3d90885dd6f
--- /dev/null
+++ b/spec/support/shared_examples/services/issuable/update_service_shared_examples.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples_for 'issuable update service updating last_edited_at values' do
+ context 'when updating the title of the issuable' do
+ 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(
+ not_change(issuable, :last_edited_at)
+ ).and(
+ not_change(issuable, :last_edited_by)
+ )
+ end
+ end
+
+ context 'when updating the description of the issuable' do
+ let(:update_params) { { description: 'updated description' } }
+
+ 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)
+ ).and(
+ change(issuable, :last_edited_by)
+ )
+ end
+ 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 9610cdd18a3..65351ac94ab 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
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-shared_examples 'issuable link creation' do
+RSpec.shared_examples 'issuable link creation' do
describe '#execute' do
subject { described_class.new(issuable, user, params).execute }
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 53d637a9094..5e80014da1d 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
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-shared_examples 'a destroyable issuable link' do
+RSpec.shared_examples 'a destroyable issuable link' do
context 'when successfully removes an issuable link' do
before do
issuable_link.source.resource_parent.add_reporter(user)
diff --git a/spec/support/shared_examples/services/merge_request_shared_examples.rb b/spec/support/shared_examples/services/merge_request_shared_examples.rb
index d2595b92cbc..b3ba0a1be93 100644
--- a/spec/support/shared_examples/services/merge_request_shared_examples.rb
+++ b/spec/support/shared_examples/services/merge_request_shared_examples.rb
@@ -123,7 +123,7 @@ end
RSpec.shared_examples 'with an existing branch that has a merge request open' do |count|
let(:changes) { existing_branch_changes }
- let!(:merge_request) { create(:merge_request, source_project: project, source_branch: source_branch)}
+ let!(:merge_request) { create(:merge_request, source_project: project, source_branch: source_branch) }
it_behaves_like 'a service that does not create a merge request'
it_behaves_like 'a service that can change assignees of a merge request', count
diff --git a/spec/support/shared_examples/services/onboarding_progress_shared_examples.rb b/spec/support/shared_examples/services/onboarding_progress_shared_examples.rb
index 8c6c2271af3..07025dac689 100644
--- a/spec/support/shared_examples/services/onboarding_progress_shared_examples.rb
+++ b/spec/support/shared_examples/services/onboarding_progress_shared_examples.rb
@@ -4,7 +4,7 @@ RSpec.shared_examples 'records an onboarding progress action' do |action|
include AfterNextHelpers
it do
- expect_next(OnboardingProgressService, namespace)
+ expect_next(Onboarding::ProgressService, namespace)
.to receive(:execute).with(action: action).and_call_original
subject
@@ -13,7 +13,7 @@ end
RSpec.shared_examples 'does not record an onboarding progress action' do
it do
- expect(OnboardingProgressService).not_to receive(:new)
+ expect(Onboarding::ProgressService).not_to receive(:new)
subject
end
diff --git a/spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb b/spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb
index 7fd20fc3909..ea79dc674a1 100644
--- a/spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb
+++ b/spec/support/shared_examples/services/packages/debian/generate_distribution_shared_examples.rb
@@ -190,6 +190,7 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
Codename: unstable
Date: Sat, 25 Jan 2020 15:17:18 +0000
Valid-Until: Mon, 27 Jan 2020 15:17:18 +0000
+ Acquire-By-Hash: yes
Architectures: all amd64 arm64
Components: contrib main
MD5Sum:
@@ -249,6 +250,7 @@ RSpec.shared_examples 'Generate Debian Distribution and component files' do
Codename: unstable
Date: Sat, 25 Jan 2020 15:17:18 +0000
Valid-Until: Mon, 27 Jan 2020 15:17:18 +0000
+ Acquire-By-Hash: yes
MD5Sum:
SHA256:
EOF
diff --git a/spec/support/shared_examples/services/packages_shared_examples.rb b/spec/support/shared_examples/services/packages_shared_examples.rb
index 704a4bbe0b8..ca4dea90c55 100644
--- a/spec/support/shared_examples/services/packages_shared_examples.rb
+++ b/spec/support/shared_examples/services/packages_shared_examples.rb
@@ -227,6 +227,7 @@ RSpec.shared_examples 'filters on each package_type' do |is_project: false|
let_it_be(:package10) { create(:rubygems_package, project: project) }
let_it_be(:package11) { create(:helm_package, project: project) }
let_it_be(:package12) { create(:terraform_module_package, project: project) }
+ let_it_be(:package13) { create(:rpm_package, project: project) }
Packages::Package.package_types.keys.each do |package_type|
context "for package type #{package_type}" do
diff --git a/spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb b/spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb
index 716bee39fca..a7e51408032 100644
--- a/spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb
+++ b/spec/support/shared_examples/services/resource_events/synthetic_notes_builder_shared_examples.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
RSpec.shared_examples 'filters by paginated notes' do |event_type|
- let(:event) { create(event_type) } # rubocop:disable Rails/SaveBang
+ let(:event) { create(event_type, issue: create(:issue)) }
before do
create(event_type, issue: event.issue)
diff --git a/spec/support/shared_examples/services/snippets_shared_examples.rb b/spec/support/shared_examples/services/snippets_shared_examples.rb
index 5a44f739b27..65893d84798 100644
--- a/spec/support/shared_examples/services/snippets_shared_examples.rb
+++ b/spec/support/shared_examples/services/snippets_shared_examples.rb
@@ -14,7 +14,8 @@ RSpec.shared_examples 'checking spam' do
spammable: kind_of(Snippet),
spam_params: spam_params,
user: an_instance_of(User),
- action: action
+ action: action,
+ extra_features: { files: an_instance_of(Array) }
}
) do |instance|
expect(instance).to receive(:execute)
@@ -24,7 +25,7 @@ RSpec.shared_examples 'checking spam' do
end
end
-shared_examples 'invalid params error response' do
+RSpec.shared_examples 'invalid params error response' do
before do
allow_next_instance_of(described_class) do |service|
allow(service).to receive(:valid_params?).and_return false
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 0687be6f429..31919a4263d 100644
--- a/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb
+++ b/spec/support/shared_examples/services/snowplow_tracking_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-shared_examples 'issue_edit snowplow tracking' do
+RSpec.shared_examples 'issue_edit snowplow tracking' do
let(:category) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_CATEGORY }
let(:action) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_ACTION }
let(:label) { Gitlab::UsageDataCounters::IssueActivityUniqueCounter::ISSUE_LABEL }
diff --git a/spec/support/shared_examples/tasks/gitlab/uploads/migration_shared_examples.rb b/spec/support/shared_examples/tasks/gitlab/uploads/migration_shared_examples.rb
deleted file mode 100644
index b37a8059574..00000000000
--- a/spec/support/shared_examples/tasks/gitlab/uploads/migration_shared_examples.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-# frozen_string_literal: true
-
-# Expects the calling spec to define:
-# - uploader_class
-# - model_class
-# - mounted_as
-RSpec.shared_examples 'enqueue upload migration jobs in batch' do |batch:|
- def run(task)
- args = [uploader_class.to_s, model_class.to_s, mounted_as].compact
- run_rake_task(task, *args)
- end
-
- it 'migrates local storage to remote object storage' do
- expect(ObjectStorage::MigrateUploadsWorker)
- .to receive(:perform_async).exactly(batch).times
- .and_return("A fake job.")
-
- run('gitlab:uploads:migrate')
- end
-
- it 'migrates remote object storage to local storage' do
- expect(Upload).to receive(:where).exactly(batch + 1).times { Upload.all }
- expect(ObjectStorage::MigrateUploadsWorker)
- .to receive(:perform_async)
- .with(anything, model_class.name, mounted_as, ObjectStorage::Store::LOCAL)
- .exactly(batch).times
- .and_return("A fake job.")
-
- run('gitlab:uploads:migrate_to_local')
- end
-end
diff --git a/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb b/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb
index f8b00d1e4c0..3c977e62a10 100644
--- a/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb
+++ b/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb
@@ -56,8 +56,8 @@ RSpec.shared_examples "migrates" do |to_store:, from_store: nil|
it 'can access to the original file during migration' do
file = subject.file
- allow(subject).to receive(:delete_migrated_file) { } # Remove as a callback of :migrate
- allow(subject).to receive(:record_upload) { } # Remove as a callback of :store (:record_upload)
+ allow(subject).to receive(:delete_migrated_file) {} # Remove as a callback of :migrate
+ allow(subject).to receive(:record_upload) {} # Remove as a callback of :store (:record_upload)
expect(file.exists?).to be_truthy
expect { migrate(to) }.not_to change { file.exists? }
diff --git a/spec/support/shared_examples/users/migrate_records_to_ghost_user_service_shared_examples.rb b/spec/support/shared_examples/users/migrate_records_to_ghost_user_service_shared_examples.rb
new file mode 100644
index 00000000000..eb03f0888b9
--- /dev/null
+++ b/spec/support/shared_examples/users/migrate_records_to_ghost_user_service_shared_examples.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'migrating records to the ghost user' do |record_class, fields|
+ record_class_name = record_class.to_s.titleize.downcase
+
+ let(:project) do
+ case record_class
+ when MergeRequest
+ create(:project, :repository)
+ else
+ create(:project)
+ end
+ end
+
+ before do
+ project.add_developer(user)
+ end
+
+ context "for a #{record_class_name} the user has created" do
+ let!(:record) { created_record }
+ let(:migrated_fields) { fields || [:author] }
+
+ it "does not delete the #{record_class_name}" do
+ service.execute
+
+ expect(record_class.find_by_id(record.id)).to be_present
+ end
+
+ it 'migrates all associated fields to the "Ghost user"' do
+ service.execute
+
+ migrated_record = record_class.find_by_id(record.id)
+
+ migrated_fields.each do |field|
+ expect(migrated_record.public_send(field)).to eq(User.ghost)
+ end
+ end
+ end
+end
diff --git a/spec/support_specs/database/without_check_constraint_spec.rb b/spec/support_specs/database/without_check_constraint_spec.rb
new file mode 100644
index 00000000000..d78eafd4a32
--- /dev/null
+++ b/spec/support_specs/database/without_check_constraint_spec.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Database::WithoutCheckConstraint' do
+ include MigrationsHelpers
+
+ describe '.without_check_constraint' do
+ let(:connection) { ApplicationRecord.connection }
+ let(:table_name) { '_test_table' }
+ let(:constraint_name) { 'check_1234' }
+ let(:model) { table(table_name) }
+
+ before do
+ # Drop test table in case it's left from a previous execution.
+ connection.exec_query("DROP TABLE IF EXISTS #{table_name}")
+ # Model has an attribute called 'name' that can't be NULL.
+ connection.exec_query(<<-SQL)
+ CREATE TABLE #{table_name} (
+ name text
+ CONSTRAINT #{constraint_name} CHECK (name IS NOT NULL)
+ );
+ SQL
+ end
+
+ context 'with invalid table' do
+ subject do
+ without_check_constraint('no_such_table', constraint_name, connection: connection) {}
+ end
+
+ it 'raises exception' do
+ msg = "'no_such_table' does not exist"
+ expect { subject }.to raise_error(msg)
+ end
+ end
+
+ context 'with invalid constraint name' do
+ subject do
+ without_check_constraint(table_name, 'no_such_constraint', connection: connection) {}
+ end
+
+ it 'raises exception' do
+ msg = "'#{table_name}' table does not contain constraint called 'no_such_constraint'"
+ expect { subject }.to raise_error(msg)
+ end
+ end
+
+ context 'with constraint' do
+ subject { connection.check_constraints(table_name) }
+
+ it 'removes inside block' do
+ without_check_constraint(table_name, constraint_name, connection: connection) do
+ expect(subject).to be_empty
+ end
+ end
+
+ it 'restores outside block' do
+ saved_constraints = subject
+
+ without_check_constraint(table_name, constraint_name, connection: connection) do
+ end
+
+ expect(subject).to eq(saved_constraints)
+ end
+ end
+
+ context 'when creating an invalid record' do
+ subject(:invalid_record) { model.create!(name: nil) }
+
+ it 'enables invalid record creation inside block' do
+ without_check_constraint(table_name, constraint_name, connection: connection) do
+ expect(invalid_record).to be_persisted
+ expect(invalid_record.name).to be_nil
+ end
+ end
+
+ it 'rolls back changes made within the block' do
+ without_check_constraint(table_name, constraint_name, connection: connection) do
+ invalid_record
+ end
+ expect(model.all).to be_empty
+ end
+ end
+ end
+end
diff --git a/spec/support_specs/helpers/html_escaped_helpers_spec.rb b/spec/support_specs/helpers/html_escaped_helpers_spec.rb
new file mode 100644
index 00000000000..337f7ecc659
--- /dev/null
+++ b/spec/support_specs/helpers/html_escaped_helpers_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'rspec-parameterized'
+
+require_relative '../../support/helpers/html_escaped_helpers'
+
+RSpec.describe HtmlEscapedHelpers do
+ using RSpec::Parameterized::TableSyntax
+
+ describe '#match_html_escaped_tags' do
+ let(:actual_match) { actual_match_data && actual_match_data[0] }
+
+ subject(:actual_match_data) { described_class.match_html_escaped_tags(content) }
+
+ where(:content, :expected_match) do
+ nil | nil
+ '' | nil
+ '<a href' | nil
+ '<span href' | nil
+ '</a>' | nil
+ '&lt;a href' | '&lt;a'
+ '&lt;span href' | '&lt;span'
+ '&lt; span' | '&lt; span'
+ 'some text &lt;a href' | '&lt;a'
+ 'some text "&lt;a href' | '&lt;a'
+ '&lt;/a&glt;' | '&lt;/a'
+ '&lt;/span&gt;' | '&lt;/span'
+ '&lt; / span&gt;' | '&lt; / span'
+ 'title="&lt;a href' | nil
+ 'title= "&lt;a href' | nil
+ "title= '&lt;a href" | nil
+ "title= '&lt;/a" | nil
+ "title= '&lt;/span" | nil
+ 'title="foo">&lt;a' | '&lt;a'
+ "title='foo'>\n&lt;a" | '&lt;a'
+ end
+
+ with_them do
+ specify { expect(actual_match).to eq(expected_match) }
+ end
+ end
+end
diff --git a/spec/tasks/gitlab/db/truncate_legacy_tables_rake_spec.rb b/spec/tasks/gitlab/db/truncate_legacy_tables_rake_spec.rb
new file mode 100644
index 00000000000..f9ebb985255
--- /dev/null
+++ b/spec/tasks/gitlab/db/truncate_legacy_tables_rake_spec.rb
@@ -0,0 +1,157 @@
+# frozen_string_literal: true
+
+require 'rake_helper'
+
+RSpec.describe 'gitlab:db:truncate_legacy_tables', :silence_stdout, :reestablished_active_record_base,
+ :suppress_gitlab_schemas_validate_connection do
+ 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' }
+
+ before :all do
+ Rake.application.rake_require 'active_record/railties/databases'
+ Rake.application.rake_require 'tasks/seed_fu'
+ Rake.application.rake_require 'tasks/gitlab/db/validate_config'
+ Rake.application.rake_require 'tasks/gitlab/db/truncate_legacy_tables'
+
+ # empty task as env is already loaded
+ Rake::Task.define_task :environment
+ end
+
+ before do
+ skip_if_multiple_databases_not_setup
+
+ # Filling the table on both databases main and ci
+ Gitlab::Database.database_base_models.each_value do |base_model|
+ base_model.connection.execute(<<~SQL)
+ CREATE TABLE #{test_gitlab_main_table} (id integer NOT NULL);
+ INSERT INTO #{test_gitlab_main_table} VALUES(generate_series(1, 50));
+ SQL
+ base_model.connection.execute(<<~SQL)
+ CREATE TABLE #{test_gitlab_ci_table} (id integer NOT NULL);
+ INSERT INTO #{test_gitlab_ci_table} VALUES(generate_series(1, 50));
+ SQL
+ end
+
+ allow(Gitlab::Database::GitlabSchema).to receive(:tables_to_schema).and_return(
+ {
+ test_gitlab_main_table => :gitlab_main,
+ test_gitlab_ci_table => :gitlab_ci
+ }
+ )
+ end
+
+ shared_examples 'truncating legacy tables' do
+ before do
+ allow(ENV).to receive(:[]).and_return(nil)
+ end
+
+ context 'when tables are not locked for writes' do
+ it 'raises an error when trying to truncate the tables' do
+ error_message = /is not locked for writes. Run the rake task gitlab:db:lock_writes first/
+ expect { truncate_legacy_tables }.to raise_error(error_message)
+ end
+ end
+
+ context 'when tables are locked for writes' do
+ before do
+ # Locking ci table on the main database
+ Gitlab::Database::LockWritesManager.new(
+ table_name: test_gitlab_ci_table,
+ connection: main_connection,
+ database_name: "main"
+ ).lock_writes
+
+ # Locking main table on the ci database
+ Gitlab::Database::LockWritesManager.new(
+ table_name: test_gitlab_main_table,
+ connection: ci_connection,
+ database_name: "ci"
+ ).lock_writes
+ end
+
+ it 'calls TablesTruncate with the correct parameters and default minimum batch size' do
+ expect(Gitlab::Database::TablesTruncate).to receive(:new).with(
+ database_name: database_name,
+ min_batch_size: 5,
+ logger: anything,
+ dry_run: false,
+ until_table: nil
+ ).and_call_original
+
+ truncate_legacy_tables
+ end
+
+ it 'truncates the legacy table' do
+ expect do
+ truncate_legacy_tables
+ end.to change { connection.select_value("SELECT count(*) from #{legacy_table}") }.from(50).to(0)
+ end
+
+ it 'does not truncate the table that belongs to the connection schema' do
+ expect do
+ truncate_legacy_tables
+ end.not_to change { connection.select_value("SELECT count(*) from #{active_table}") }
+ end
+
+ context 'when running in dry_run mode' do
+ before do
+ allow(ENV).to receive(:[]).with("DRY_RUN").and_return("true")
+ end
+
+ it 'does not truncate any tables' do
+ expect do
+ truncate_legacy_tables
+ end.not_to change { connection.select_value("SELECT count(*) from #{legacy_table}") }
+ end
+
+ it 'prints the truncation sql statement to the output' do
+ expect do
+ truncate_legacy_tables
+ end.to output(/TRUNCATE TABLE #{legacy_table} RESTRICT/).to_stdout
+ end
+ end
+
+ context 'when passing until_table parameter via environment variable' do
+ before do
+ allow(ENV).to receive(:[]).with("UNTIL_TABLE").and_return(legacy_table)
+ end
+
+ it 'sends the table name to TablesTruncate' do
+ expect(Gitlab::Database::TablesTruncate).to receive(:new).with(
+ database_name: database_name,
+ min_batch_size: 5,
+ logger: anything,
+ dry_run: false,
+ until_table: legacy_table
+ ).and_call_original
+
+ truncate_legacy_tables
+ end
+ end
+ end
+ end
+
+ context 'when truncating ci tables on the main database' do
+ subject(:truncate_legacy_tables) { run_rake_task('gitlab:db:truncate_legacy_tables:main') }
+
+ let(:connection) { ApplicationRecord.connection }
+ let(:database_name) { 'main' }
+ let(:active_table) { test_gitlab_main_table }
+ let(:legacy_table) { test_gitlab_ci_table }
+
+ it_behaves_like 'truncating legacy tables'
+ end
+
+ context 'when truncating main tables on the ci database' do
+ subject(:truncate_legacy_tables) { run_rake_task('gitlab:db:truncate_legacy_tables:ci') }
+
+ let(:connection) { Ci::ApplicationRecord.connection }
+ let(:database_name) { 'ci' }
+ let(:active_table) { test_gitlab_ci_table }
+ let(:legacy_table) { test_gitlab_main_table }
+
+ it_behaves_like 'truncating legacy tables'
+ end
+end
diff --git a/spec/tasks/gitlab/db/validate_config_rake_spec.rb b/spec/tasks/gitlab/db/validate_config_rake_spec.rb
index ad15c7f0d1c..1d47c94aa77 100644
--- a/spec/tasks/gitlab/db/validate_config_rake_spec.rb
+++ b/spec/tasks/gitlab/db/validate_config_rake_spec.rb
@@ -216,7 +216,7 @@ RSpec.describe 'gitlab:db:validate_config', :silence_stdout, :suppress_gitlab_sc
let(:exception) { ActiveRecord::StatementInvalid.new("READONLY") }
before do
- allow(exception).to receive(:cause).and_return(PG::ReadOnlySqlTransaction.new("cannot execute INSERT in a read-only transaction"))
+ allow(exception).to receive(:cause).and_return(PG::ReadOnlySqlTransaction.new("cannot execute UPSERT in a read-only transaction"))
allow(ActiveRecord::InternalMetadata).to receive(:upsert).at_least(:once).and_raise(exception)
end
diff --git a/spec/tasks/gitlab/snippets_rake_spec.rb b/spec/tasks/gitlab/snippets_rake_spec.rb
index c55bded1d5a..c50b04b4600 100644
--- a/spec/tasks/gitlab/snippets_rake_spec.rb
+++ b/spec/tasks/gitlab/snippets_rake_spec.rb
@@ -3,7 +3,7 @@
require 'rake_helper'
RSpec.describe 'gitlab:snippets namespace rake task', :silence_stdout do
- let_it_be(:user) { create(:user)}
+ let_it_be(:user) { create(:user) }
let_it_be(:migrated) { create(:personal_snippet, :repository, author: user) }
let(:non_migrated) { create_list(:personal_snippet, 3, author: user) }
diff --git a/spec/tasks/gitlab/uploads/migrate_rake_spec.rb b/spec/tasks/gitlab/uploads/migrate_rake_spec.rb
index e293271ca67..3a368a5011b 100644
--- a/spec/tasks/gitlab/uploads/migrate_rake_spec.rb
+++ b/spec/tasks/gitlab/uploads/migrate_rake_spec.rb
@@ -2,133 +2,93 @@
require 'rake_helper'
-RSpec.describe 'gitlab:uploads:migrate and migrate_to_local rake tasks', :silence_stdout do
- let(:model_class) { nil }
- let(:uploader_class) { nil }
- let(:mounted_as) { nil }
- let(:batch_size) { 3 }
-
+RSpec.describe 'gitlab:uploads:migrate and migrate_to_local rake tasks', :sidekiq_inline, :silence_stdout do
before do
- stub_env('MIGRATION_BATCH_SIZE', batch_size.to_s)
- stub_uploads_object_storage(uploader_class)
+ stub_env('MIGRATION_BATCH_SIZE', 3.to_s)
+ stub_uploads_object_storage(AvatarUploader)
+ stub_uploads_object_storage(FileUploader)
Rake.application.rake_require 'tasks/gitlab/uploads/migrate'
- allow(ObjectStorage::MigrateUploadsWorker).to receive(:perform_async)
+ create_list(:project, 2, :with_avatar)
+ create_list(:group, 2, :with_avatar)
+ create_list(:project, 2) do |model|
+ FileUploader.new(model).store!(fixture_file_upload('spec/fixtures/doc_sample.txt'))
+ end
end
- context "for AvatarUploader" do
- let(:uploader_class) { AvatarUploader }
- let(:mounted_as) { :avatar }
+ let(:total_uploads_count) { 6 }
- context "for Project" do
- let(:model_class) { Project }
- let!(:projects) { create_list(:project, 10, :with_avatar) }
+ it 'migrates all uploads to object storage in batches' do
+ expect(ObjectStorage::MigrateUploadsWorker)
+ .to receive(:perform_async).twice.and_call_original
- it_behaves_like 'enqueue upload migration jobs in batch', batch: 4
- end
+ run_rake_task('gitlab:uploads:migrate:all')
- context "for Group" do
- let(:model_class) { Group }
+ expect(Upload.with_files_stored_locally.count).to eq(0)
+ expect(Upload.with_files_stored_remotely.count).to eq(total_uploads_count)
+ end
- before do
- create_list(:group, 10, :with_avatar)
- end
+ it 'migrates all uploads to local storage in batches' do
+ run_rake_task('gitlab:uploads:migrate')
+ expect(Upload.with_files_stored_remotely.count).to eq(total_uploads_count)
- it_behaves_like 'enqueue upload migration jobs in batch', batch: 4
- end
+ expect(ObjectStorage::MigrateUploadsWorker)
+ .to receive(:perform_async).twice.and_call_original
- context "for User" do
- let(:model_class) { User }
+ run_rake_task('gitlab:uploads:migrate_to_local:all')
- before do
- create_list(:user, 10, :with_avatar)
- end
-
- it_behaves_like 'enqueue upload migration jobs in batch', batch: 4
- end
+ expect(Upload.with_files_stored_remotely.count).to eq(0)
+ expect(Upload.with_files_stored_locally.count).to eq(total_uploads_count)
end
- context "for AttachmentUploader" do
- let(:uploader_class) { AttachmentUploader }
+ shared_examples 'migrate task with filters' do
+ it 'migrates matching uploads to object storage' do
+ run_rake_task('gitlab:uploads:migrate', task_arguments)
- context "for Note" do
- let(:model_class) { Note }
- let(:mounted_as) { :attachment }
+ migrated_count = matching_uploads.with_files_stored_remotely.count
- before do
- create_list(:note, 10, :with_attachment)
- end
-
- it_behaves_like 'enqueue upload migration jobs in batch', batch: 4
+ expect(migrated_count).to eq(matching_uploads.count)
+ expect(Upload.with_files_stored_locally.count).to eq(total_uploads_count - migrated_count)
end
- context "for Appearance" do
- let(:model_class) { Appearance }
- let(:mounted_as) { :logo }
+ it 'migrates matching uploads to local storage' do
+ run_rake_task('gitlab:uploads:migrate')
+ expect(Upload.with_files_stored_remotely.count).to eq(total_uploads_count)
+
+ run_rake_task('gitlab:uploads:migrate_to_local', task_arguments)
- before do
- create(:appearance, :with_logos)
- end
+ migrated_count = matching_uploads.with_files_stored_locally.count
- %i(logo header_logo).each do |mount|
- it_behaves_like 'enqueue upload migration jobs in batch', batch: 1 do
- let(:mounted_as) { mount }
- end
- end
+ expect(migrated_count).to eq(matching_uploads.count)
+ expect(Upload.with_files_stored_remotely.count).to eq(total_uploads_count - migrated_count)
end
end
- context "for FileUploader" do
- let(:uploader_class) { FileUploader }
- let(:model_class) { Project }
+ context 'when uploader_class is given' do
+ let(:task_arguments) { ['FileUploader'] }
+ let(:matching_uploads) { Upload.where(uploader: 'FileUploader') }
- before do
- create_list(:project, 10) do |model|
- uploader_class.new(model)
- .store!(fixture_file_upload('spec/fixtures/doc_sample.txt'))
- end
- end
-
- it_behaves_like 'enqueue upload migration jobs in batch', batch: 4
+ it_behaves_like 'migrate task with filters'
end
- context "for PersonalFileUploader" do
- let(:uploader_class) { PersonalFileUploader }
- let(:model_class) { PersonalSnippet }
-
- before do
- create_list(:personal_snippet, 10) do |model|
- uploader_class.new(model)
- .store!(fixture_file_upload('spec/fixtures/doc_sample.txt'))
- end
- end
+ context 'when model_class is given' do
+ let(:task_arguments) { [nil, 'Project'] }
+ let(:matching_uploads) { Upload.where(model_type: 'Project') }
- it_behaves_like 'enqueue upload migration jobs in batch', batch: 4
+ it_behaves_like 'migrate task with filters'
end
- context "for NamespaceFileUploader" do
- let(:uploader_class) { NamespaceFileUploader }
- let(:model_class) { Snippet }
+ context 'when mounted_as is given' do
+ let(:task_arguments) { [nil, nil, :avatar] }
+ let(:matching_uploads) { Upload.where(mount_point: :avatar) }
- before do
- create_list(:snippet, 10) do |model|
- uploader_class.new(model)
- .store!(fixture_file_upload('spec/fixtures/doc_sample.txt'))
- end
- end
-
- it_behaves_like 'enqueue upload migration jobs in batch', batch: 4
+ it_behaves_like 'migrate task with filters'
end
- context 'for DesignManagement::DesignV432x230Uploader' do
- let(:uploader_class) { DesignManagement::DesignV432x230Uploader }
- let(:model_class) { DesignManagement::Action }
- let(:mounted_as) { :image_v432x230 }
-
- before do
- create_list(:design_action, 10, :with_image_v432x230)
- end
+ context 'when multiple filters are given' do
+ let(:task_arguments) { %w[AvatarUploader Project] }
+ let(:matching_uploads) { Upload.where(uploader: 'AvatarUploader', model_type: 'Project') }
- it_behaves_like 'enqueue upload migration jobs in batch', batch: 4
+ it_behaves_like 'migrate task with filters'
end
end
diff --git a/spec/tasks/gitlab/usage_data_rake_spec.rb b/spec/tasks/gitlab/usage_data_rake_spec.rb
index 442b884b313..f54d06f406f 100644
--- a/spec/tasks/gitlab/usage_data_rake_spec.rb
+++ b/spec/tasks/gitlab/usage_data_rake_spec.rb
@@ -3,15 +3,23 @@
require 'rake_helper'
RSpec.describe 'gitlab:usage data take tasks', :silence_stdout do
+ include StubRequests
include UsageDataHelpers
+ let(:metrics_file) { Rails.root.join('tmp', 'test', 'sql_metrics_queries.json') }
+
before do
Rake.application.rake_require 'tasks/gitlab/usage_data'
+
# stub prometheus external http calls https://gitlab.com/gitlab-org/gitlab/-/issues/245277
stub_prometheus_queries
stub_database_flavor_check
end
+ after do
+ FileUtils.rm_rf(metrics_file)
+ end
+
describe 'dump_sql_in_yaml' do
it 'dumps SQL queries in yaml format' do
expect { run_rake_task('gitlab:usage_data:dump_sql_in_yaml') }.to output(/.*recorded_at:.*/).to_stdout
@@ -23,4 +31,53 @@ RSpec.describe 'gitlab:usage data take tasks', :silence_stdout do
expect { run_rake_task('gitlab:usage_data:dump_sql_in_json') }.to output(/.*"recorded_at":.*/).to_stdout
end
end
+
+ describe 'dump_non_sql_in_json' do
+ it 'dumps non SQL data in json format' do
+ expect { run_rake_task('gitlab:usage_data:dump_non_sql_in_json') }.to output(/.*"recorded_at":.*/).to_stdout
+ end
+ end
+
+ describe 'generate_sql_metrics_fixture' do
+ it 'generates fixture file correctly' do
+ run_rake_task('gitlab:usage_data:generate_sql_metrics_queries')
+
+ expect(Pathname.new(metrics_file)).to exist
+ end
+ end
+
+ describe 'generate_and_send' do
+ let(:service_ping_payload_url) do
+ File.join(ServicePing::SubmitService::STAGING_BASE_URL, ServicePing::SubmitService::USAGE_DATA_PATH)
+ end
+
+ let(:service_ping_metadata_url) do
+ File.join(ServicePing::SubmitService::STAGING_BASE_URL, ServicePing::SubmitService::METADATA_PATH)
+ end
+
+ let(:payload) { { recorded_at: Time.current } }
+
+ before do
+ allow_next_instance_of(ServicePing::BuildPayload) do |service|
+ allow(service).to receive(:execute).and_return(payload)
+ end
+ stub_response(body: payload.merge(conv_index: { usage_data_id: 123 }))
+ stub_response(body: nil, url: service_ping_metadata_url, status: 201)
+ end
+
+ it 'generates and sends Service Ping payload' do
+ expect { run_rake_task('gitlab:usage_data:generate_and_send') }.to output(/.*201.*/).to_stdout
+ end
+
+ private
+
+ def stub_response(url: service_ping_payload_url, body:, status: 201)
+ stub_full_request(url, method: :post)
+ .to_return(
+ headers: { 'Content-Type' => 'application/json' },
+ body: body.to_json,
+ status: status
+ )
+ end
+ end
end
diff --git a/spec/tasks/rubocop_rake_spec.rb b/spec/tasks/rubocop_rake_spec.rb
index a92d7dc2e52..eb360cdff93 100644
--- a/spec/tasks/rubocop_rake_spec.rb
+++ b/spec/tasks/rubocop_rake_spec.rb
@@ -3,16 +3,20 @@
require 'fast_spec_helper'
require 'rake'
+require 'tmpdir'
require 'fileutils'
require_relative '../support/silence_stdout'
require_relative '../support/helpers/next_instance_of'
require_relative '../support/helpers/rake_helpers'
+require_relative '../support/matchers/abort_matcher'
require_relative '../../rubocop/formatter/todo_formatter'
require_relative '../../rubocop/todo_dir'
+require_relative '../../rubocop/check_graceful_task'
RSpec.describe 'rubocop rake tasks', :silence_stdout do
include RakeHelpers
+ include NextInstanceOf
before do
stub_const('Rails', double(:rails_env))
@@ -23,6 +27,41 @@ RSpec.describe 'rubocop rake tasks', :silence_stdout do
Rake.application.rake_require 'tasks/rubocop'
end
+ describe 'check:graceful' do
+ let(:options) { %w[file.rb Cop/Name] }
+
+ subject(:run_task) { run_rake_task('rubocop:check:graceful', *options) }
+
+ before do
+ allow_next_instance_of(RuboCop::CheckGracefulTask, $stdout) do |task|
+ allow(task).to receive(:run).with(options).and_return(task_result)
+ end
+ end
+
+ context 'with successful task result' do
+ let(:task_result) { 0 }
+
+ # We cannot use `abort_execution` because it's ignoring exit status `0`.
+ # Rely on SystemExitDetected here.
+ specify { run_task }
+
+ it 'modifies ENV and deletes REVEAL_RUBOCOP_TODO key' do
+ # There's ENV backup in before block.
+ ENV['REVEAL_RUBOCOP_TODO'] = '0' # rubocop:disable RSpec/EnvAssignment
+
+ run_task
+
+ expect(ENV.key?('REVEAL_RUBOCOP_TODO')).to eq(false)
+ end
+ end
+
+ context 'with non-successful task result' do
+ let(:task_result) { 1 }
+
+ specify { expect { run_task }.to abort_execution }
+ end
+ end
+
describe 'todo:generate', :aggregate_failures do
let(:tmp_dir) { Dir.mktmpdir }
let(:rubocop_todo_dir) { File.join(tmp_dir, '.rubocop_todo') }
diff --git a/spec/tooling/danger/config_files_spec.rb b/spec/tooling/danger/config_files_spec.rb
new file mode 100644
index 00000000000..0e01908a1dd
--- /dev/null
+++ b/spec/tooling/danger/config_files_spec.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+require 'gitlab-dangerfiles'
+require 'danger'
+require 'danger/plugins/internal/helper'
+require 'gitlab/dangerfiles/spec_helper'
+
+require_relative '../../../tooling/danger/config_files'
+require_relative '../../../tooling/danger/project_helper'
+
+RSpec.describe Tooling::Danger::ConfigFiles do
+ include_context "with dangerfile"
+
+ let(:fake_danger) { DangerSpecHelper.fake_danger.include(described_class) }
+ let(:fake_project_helper) { instance_double(Tooling::Danger::ProjectHelper) }
+ let(:matching_line) { "+ introduced_by_url:" }
+
+ subject(:config_file) { fake_danger.new(helper: fake_helper) }
+
+ before do
+ allow(config_file).to receive(:project_helper).and_return(fake_project_helper)
+ end
+
+ describe '#add_suggestion_for_missing_introduced_by_url' do
+ let(:file_lines) do
+ [
+ "---",
+ "name: about_your_company_registration_flow",
+ "introduced_by_url: #{url}",
+ "rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/355909",
+ "milestone: '14.10'"
+ ]
+ end
+
+ let(:filename) { 'config/feature_flags/new_ff.yml' }
+
+ before do
+ allow(config_file.project_helper).to receive(:file_lines).and_return(file_lines)
+ allow(config_file.helper).to receive(:added_files).and_return([filename])
+ allow(config_file.helper).to receive(:mr_web_url).and_return(url)
+ end
+
+ context 'when config file has an empty introduced_by_url line' do
+ let(:url) { '' }
+
+ it 'adds suggestions at the correct line' do
+ expected_format = format(described_class::SUGGEST_INTRODUCED_BY_COMMENT, url: url)
+ expect(config_file).to receive(:markdown).with(expected_format, file: filename, line: 3)
+
+ config_file.add_suggestion_for_missing_introduced_by_url
+ end
+ end
+
+ context 'when config file has an introduced_by_url line with value' do
+ let(:url) { 'https://gitlab.com/gitlab-org/gitlab/-/issues/1' }
+
+ it 'does not add suggestion' do
+ expect(config_file).not_to receive(:markdown)
+
+ config_file.add_suggestion_for_missing_introduced_by_url
+ end
+ end
+ end
+
+ describe '#new_config_files' do
+ let(:expected_files) do
+ %w[
+ config/feature_flags/first.yml
+ config/events/1234_new_event.yml
+ config/metrics/count_7d/new_metric.yml
+ ]
+ end
+
+ before do
+ all_new_files = %w[
+ app/workers/a.rb
+ doc/events/new_event.md
+ config/feature_flags/first.yml
+ config/events/1234_new_event.yml
+ config/metrics/count_7d/new_metric.yml
+ app/assets/index.js
+ ]
+
+ allow(config_file.helper).to receive(:added_files).and_return(all_new_files)
+ end
+
+ it 'returns added, modified, and renamed_after files by default' do
+ expect(config_file.new_config_files).to match_array(expected_files)
+ end
+ end
+end
diff --git a/spec/tooling/danger/datateam_spec.rb b/spec/tooling/danger/datateam_spec.rb
index e4ab3a6f4b1..de8a93baa27 100644
--- a/spec/tooling/danger/datateam_spec.rb
+++ b/spec/tooling/danger/datateam_spec.rb
@@ -24,7 +24,7 @@ RSpec.describe Tooling::Danger::Datateam do
impacted: true,
impacted_files: %w(db/structure.sql)
},
- 'with structure.sql changes and Data Warehouse::Impact Check label' => {
+ 'with structure.sql changes and Data Warehouse::Impact Check label' => {
modified_files: %w(db/structure.sql),
changed_lines: ['+group_id bigint NOT NULL)'],
mr_labels: ['Data Warehouse::Impact Check'],
diff --git a/spec/tooling/danger/project_helper_spec.rb b/spec/tooling/danger/project_helper_spec.rb
index 2f52c0fd36c..4cc5df385a5 100644
--- a/spec/tooling/danger/project_helper_spec.rb
+++ b/spec/tooling/danger/project_helper_spec.rb
@@ -31,6 +31,8 @@ RSpec.describe Tooling::Danger::ProjectHelper do
end
where(:path, :expected_categories) do
+ 'glfm_specification/example_snapshots/prosemirror_json.yml' | [:frontend]
+ 'glfm_specification/input/glfm_anything.yml' | [:frontend, :backend]
'usage_data.rb' | [:database, :backend, :product_intelligence]
'doc/foo.md' | [:docs]
'CONTRIBUTING.md' | [:docs]
diff --git a/spec/uploaders/object_storage/cdn/google_cdn_spec.rb b/spec/uploaders/object_storage/cdn/google_cdn_spec.rb
new file mode 100644
index 00000000000..b72f6d66d69
--- /dev/null
+++ b/spec/uploaders/object_storage/cdn/google_cdn_spec.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ObjectStorage::CDN::GoogleCDN,
+ :use_clean_rails_memory_store_caching, :use_clean_rails_redis_caching, :sidekiq_inline do
+ include StubRequests
+
+ let(:key) { SecureRandom.hex }
+ let(:key_name) { 'test-key' }
+ let(:options) { { url: 'https://cdn.gitlab.example.com', key_name: key_name, key: Base64.urlsafe_encode64(key) } }
+ let(:google_cloud_ips) { File.read(Rails.root.join('spec/fixtures/cdn/google_cloud.json')) }
+ let(:headers) { { 'Content-Type' => 'application/json' } }
+ let(:public_ip) { '18.245.0.42' }
+
+ subject { described_class.new(options) }
+
+ before do
+ WebMock.stub_request(:get, GoogleCloud::FetchGoogleIpListService::GOOGLE_IP_RANGES_URL)
+ .to_return(status: 200, body: google_cloud_ips, headers: headers)
+ end
+
+ describe '#use_cdn?' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:ip_address, :expected) do
+ '34.80.0.1' | false
+ '18.245.0.42' | true
+ '2500:1900:4180:0000:0000:0000:0000:0000' | true
+ '2600:1900:4180:0000:0000:0000:0000:0000' | false
+ '10.10.1.5' | false
+ 'fc00:0000:0000:0000:0000:0000:0000:0000' | false
+ end
+
+ with_them do
+ it { expect(subject.use_cdn?(ip_address)).to eq(expected) }
+ end
+
+ context 'when the key name is missing' do
+ let(:options) { { url: 'https://cdn.gitlab.example.com', key: Base64.urlsafe_encode64(SecureRandom.hex) } }
+
+ it 'returns false' do
+ expect(subject.use_cdn?(public_ip)).to be false
+ end
+ end
+
+ context 'when the key is missing' do
+ let(:options) { { url: 'https://invalid.example.com' } }
+
+ it 'returns false' do
+ expect(subject.use_cdn?(public_ip)).to be false
+ end
+ end
+
+ context 'when the key is invalid' do
+ let(:options) { { key_name: key_name, key: '\0x1' } }
+
+ it 'returns false' do
+ expect(Gitlab::ErrorTracking).to receive(:log_exception).and_call_original
+ expect(subject.use_cdn?(public_ip)).to be false
+ end
+ end
+
+ context 'when the URL is missing' do
+ let(:options) { { key: Base64.urlsafe_encode64(SecureRandom.hex) } }
+
+ it 'returns false' do
+ expect(subject.use_cdn?(public_ip)).to be false
+ end
+ end
+ end
+
+ describe '#signed_url' do
+ let(:path) { '/path/to/file.txt' }
+
+ it 'returns a valid signed URL' do
+ url = subject.signed_url(path)
+
+ expect(url).to start_with("#{options[:url]}#{path}")
+
+ uri = Addressable::URI.parse(url)
+ parsed_query = Rack::Utils.parse_nested_query(uri.query)
+ signature = parsed_query.delete('Signature')
+
+ signed_url = "#{options[:url]}#{path}?Expires=#{parsed_query['Expires']}&KeyName=#{key_name}"
+ computed_signature = OpenSSL::HMAC.digest('SHA1', key, signed_url)
+
+ aggregate_failures do
+ expect(parsed_query['Expires'].to_i).to be > 0
+ expect(parsed_query['KeyName']).to eq(key_name)
+ expect(signature).to eq(Base64.urlsafe_encode64(computed_signature))
+ end
+ end
+ end
+end
diff --git a/spec/uploaders/object_storage/cdn/google_ip_cache_spec.rb b/spec/uploaders/object_storage/cdn/google_ip_cache_spec.rb
new file mode 100644
index 00000000000..d6568636bc0
--- /dev/null
+++ b/spec/uploaders/object_storage/cdn/google_ip_cache_spec.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ObjectStorage::CDN::GoogleIpCache,
+ :use_clean_rails_memory_store_caching, :use_clean_rails_redis_caching do
+ include StubRequests
+
+ let(:subnets) { [IPAddr.new("34.80.0.0/15"), IPAddr.new("2600:1900:4180::/44")] }
+ let(:public_ip) { '18.245.0.42' }
+
+ describe '.update!' do
+ it 'caches to both L1 and L2 caches' do
+ expect(Gitlab::ProcessMemoryCache.cache_backend.exist?(described_class::GOOGLE_CDN_LIST_KEY)).to be false
+ expect(Rails.cache.exist?(described_class::GOOGLE_CDN_LIST_KEY)).to be false
+
+ described_class.update!(subnets)
+
+ expect(Gitlab::ProcessMemoryCache.cache_backend.fetch(described_class::GOOGLE_CDN_LIST_KEY)).to eq(subnets)
+ expect(Rails.cache.fetch(described_class::GOOGLE_CDN_LIST_KEY)).to eq(subnets)
+ end
+ end
+
+ describe '.ready?' do
+ it 'returns false' do
+ expect(described_class.ready?).to be false
+ end
+
+ it 'returns true' do
+ described_class.update!(subnets)
+
+ expect(described_class.ready?).to be true
+ end
+ end
+
+ describe '.google_ip?' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:ip_address, :expected) do
+ '34.80.0.1' | true
+ '18.245.0.42' | false
+ '2500:1900:4180:0000:0000:0000:0000:0000' | false
+ '2600:1900:4180:0000:0000:0000:0000:0000' | true
+ '10.10.1.5' | false
+ 'fc00:0000:0000:0000:0000:0000:0000:0000' | false
+ end
+
+ before do
+ described_class.update!(subnets)
+ end
+
+ with_them do
+ it { expect(described_class.google_ip?(ip_address)).to eq(expected) }
+ end
+
+ it 'uses the L2 cache and updates the L1 cache when L1 is missing' do
+ Gitlab::ProcessMemoryCache.cache_backend.delete(described_class::GOOGLE_CDN_LIST_KEY)
+ expect(Rails.cache.fetch(described_class::GOOGLE_CDN_LIST_KEY)).to eq(subnets)
+
+ expect(described_class.google_ip?(public_ip)).to be false
+
+ expect(Gitlab::ProcessMemoryCache.cache_backend.fetch(described_class::GOOGLE_CDN_LIST_KEY)).to eq(subnets)
+ expect(Rails.cache.fetch(described_class::GOOGLE_CDN_LIST_KEY)).to eq(subnets)
+ end
+
+ it 'avoids populating L1 cache if L2 is missing' do
+ Gitlab::ProcessMemoryCache.cache_backend.delete(described_class::GOOGLE_CDN_LIST_KEY)
+ Rails.cache.delete(described_class::GOOGLE_CDN_LIST_KEY)
+
+ expect(described_class.google_ip?(public_ip)).to be false
+
+ expect(Gitlab::ProcessMemoryCache.cache_backend.exist?(described_class::GOOGLE_CDN_LIST_KEY)).to be false
+ expect(Rails.cache.exist?(described_class::GOOGLE_CDN_LIST_KEY)).to be false
+ end
+ end
+
+ describe '.async_refresh' do
+ it 'schedules the worker' do
+ expect(::GoogleCloud::FetchGoogleIpListWorker).to receive(:perform_async)
+
+ described_class.async_refresh
+ end
+ end
+end
diff --git a/spec/uploaders/object_storage/cdn_spec.rb b/spec/uploaders/object_storage/cdn_spec.rb
new file mode 100644
index 00000000000..246cb1bf349
--- /dev/null
+++ b/spec/uploaders/object_storage/cdn_spec.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ObjectStorage::CDN do
+ let(:cdn_options) do
+ {
+ 'object_store' => {
+ 'cdn' => {
+ 'provider' => 'google',
+ 'url' => 'https://gitlab.example.com',
+ 'key_name' => 'test-key',
+ 'key' => '12345'
+ }
+ }
+ }.freeze
+ end
+
+ let(:uploader_class) do
+ Class.new(GitlabUploader) do
+ include ObjectStorage::Concern
+ include ObjectStorage::CDN::Concern
+
+ private
+
+ # user/:id
+ def dynamic_segment
+ File.join(model.class.underscore, model.id.to_s)
+ end
+ end
+ end
+
+ let(:object) { build_stubbed(:user) }
+
+ subject { uploader_class.new(object, :file) }
+
+ context 'with CDN config' do
+ before do
+ uploader_class.options = Settingslogic.new(Gitlab.config.uploads.deep_merge(cdn_options))
+ end
+
+ describe '#use_cdn?' do
+ it 'returns true' do
+ expect_next_instance_of(ObjectStorage::CDN::GoogleCDN) do |cdn|
+ expect(cdn).to receive(:use_cdn?).and_return(true)
+ end
+
+ expect(subject.use_cdn?('18.245.0.1')).to be true
+ end
+ end
+
+ describe '#cdn_signed_url' do
+ it 'returns a URL' do
+ expect_next_instance_of(ObjectStorage::CDN::GoogleCDN) do |cdn|
+ expect(cdn).to receive(:signed_url).and_return("https://cdn.example.com/path")
+ end
+
+ expect(subject.cdn_signed_url).to eq("https://cdn.example.com/path")
+ end
+ end
+ end
+
+ context 'without CDN config' do
+ before do
+ uploader_class.options = Gitlab.config.uploads
+ end
+
+ describe '#use_cdn?' do
+ it 'returns false' do
+ expect(subject.use_cdn?('18.245.0.1')).to be false
+ end
+ end
+ end
+
+ context 'with an unknown CDN provider' do
+ before do
+ cdn_options['object_store']['cdn']['provider'] = 'amazon'
+ uploader_class.options = Settingslogic.new(Gitlab.config.uploads.deep_merge(cdn_options))
+ end
+
+ it 'raises an error' do
+ expect { subject.use_cdn?('18.245.0.1') }.to raise_error("Unknown CDN provider: amazon")
+ end
+ end
+end
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 203a453bcdd..dbbf69e3c8d 100644
--- a/spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb
+++ b/spec/uploaders/packages/debian/distribution_release_file_uploader_spec.rb
@@ -49,12 +49,12 @@ RSpec.describe Packages::Debian::DistributionReleaseFileUploader do
end
describe '#filename' do
- it { expect(subject.filename).to eq('Release')}
+ it { expect(subject.filename).to eq('Release') }
context 'with signed_file' do
let(:uploader) { described_class.new(distribution, :signed_file) }
- it { expect(subject.filename).to eq('InRelease')}
+ it { expect(subject.filename).to eq('InRelease') }
end
end
end
diff --git a/spec/uploaders/packages/package_file_uploader_spec.rb b/spec/uploaders/packages/package_file_uploader_spec.rb
index e8f4cae7b04..0c7bf6432cb 100644
--- a/spec/uploaders/packages/package_file_uploader_spec.rb
+++ b/spec/uploaders/packages/package_file_uploader_spec.rb
@@ -2,50 +2,43 @@
require 'spec_helper'
RSpec.describe Packages::PackageFileUploader do
- {
- package_file: %r[^\h{2}/\h{2}/\h{64}/packages/\d+/files/\d+$],
- debian_package_file: %r[^\h{2}/\h{2}/\h{64}/packages/debian/files/\d+$]
- }.each do |factory, store_dir_regex|
- context factory.to_s do
- let(:package_file) { create(factory) } # rubocop:disable Rails/SaveBang
- let(:uploader) { described_class.new(package_file, :file) }
- let(:path) { Gitlab.config.packages.storage_path }
-
- subject { uploader }
-
- it_behaves_like "builds correct paths",
- store_dir: store_dir_regex,
- cache_dir: %r[/packages/tmp/cache],
- work_dir: %r[/packages/tmp/work]
-
- context 'object store is remote' do
- before do
- stub_package_file_object_storage
- end
-
- include_context 'with storage', described_class::Store::REMOTE
-
- it_behaves_like "builds correct paths",
- store_dir: store_dir_regex
- end
+ let(:package_file) { create(:package_file) }
+ let(:uploader) { described_class.new(package_file, :file) }
+ let(:path) { Gitlab.config.packages.storage_path }
+
+ subject { uploader }
+
+ it_behaves_like "builds correct paths",
+ store_dir: %r[^\h{2}/\h{2}/\h{64}/packages/\d+/files/\d+$],
+ cache_dir: %r[/packages/tmp/cache],
+ work_dir: %r[/packages/tmp/work]
+
+ context 'object store is remote' do
+ before do
+ stub_package_file_object_storage
+ end
- describe 'remote file' do
- let(:package_file) { create(factory, :object_storage) }
+ include_context 'with storage', described_class::Store::REMOTE
- context 'with object storage enabled' do
- before do
- stub_package_file_object_storage
- end
+ it_behaves_like "builds correct paths",
+ store_dir: %r[^\h{2}/\h{2}/\h{64}/packages/\d+/files/\d+$]
+ end
+
+ describe 'remote file' do
+ let(:package_file) { create(:package_file, :object_storage) }
+
+ context 'with object storage enabled' do
+ before do
+ stub_package_file_object_storage
+ end
- it 'can store file remotely' do
- allow(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async)
+ it 'can store file remotely' do
+ allow(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async)
- package_file
+ package_file
- expect(package_file.file_store).to eq(described_class::Store::REMOTE)
- expect(package_file.file.path).not_to be_blank
- end
- end
+ expect(package_file.file_store).to eq(described_class::Store::REMOTE)
+ expect(package_file.file.path).not_to be_blank
end
end
end
diff --git a/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb b/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb
index fd01a18e810..1746f480c9b 100644
--- a/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb
+++ b/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb
@@ -3,120 +3,62 @@
require 'spec_helper'
RSpec.describe ObjectStorage::MigrateUploadsWorker do
- let(:model_class) { Project }
+ let(:project) { create(:project, :with_avatar) }
let(:uploads) { Upload.all }
- let(:to_store) { ObjectStorage::Store::REMOTE }
- def perform(uploads, store = nil)
- described_class.new.perform(uploads.ids, model_class.to_s, mounted_as, store || to_store)
+ def perform(uploads, store = ObjectStorage::Store::REMOTE)
+ described_class.new.perform(uploads.ids, store)
rescue ObjectStorage::MigrateUploadsWorker::Report::MigrationFailures
# swallow
end
- # Expects the calling spec to define:
- # - model_class
- # - mounted_as
- # - to_store
- RSpec.shared_examples 'uploads migration worker' do
- describe '.enqueue!' do
- def enqueue!
- described_class.enqueue!(uploads, model_class, mounted_as, to_store)
- end
-
- it 'is guarded by .sanity_check!' do
- expect(described_class).to receive(:perform_async)
- expect(described_class).to receive(:sanity_check!)
+ before do
+ stub_uploads_object_storage(AvatarUploader)
+ stub_uploads_object_storage(FileUploader)
- enqueue!
- end
+ FileUploader.new(project).store!(fixture_file_upload('spec/fixtures/doc_sample.txt'))
+ end
- context 'sanity_check! fails' do
- before do
- expect(described_class).to receive(:sanity_check!).and_raise(described_class::SanityCheckError)
- end
+ describe '#perform' do
+ it 'migrates files to remote storage' do
+ expect(Gitlab::AppLogger).to receive(:info).with(%r{Migrated 2/2 files})
- it 'does not enqueue a job' do
- expect(described_class).not_to receive(:perform_async)
+ perform(uploads)
- expect { enqueue! }.to raise_error(described_class::SanityCheckError)
- end
- end
+ expect(Upload.where(store: ObjectStorage::Store::LOCAL).count).to eq(0)
+ expect(Upload.where(store: ObjectStorage::Store::REMOTE).count).to eq(2)
end
- describe '.sanity_check!' do
- shared_examples 'raises a SanityCheckError' do |expected_message|
- let(:mount_point) { nil }
-
- it do
- expect { described_class.sanity_check!(uploads, model_class, mount_point) }
- .to raise_error(described_class::SanityCheckError).with_message(expected_message)
- end
+ context 'reversed' do
+ before do
+ perform(uploads)
end
- context 'uploader types mismatch' do
- let!(:outlier) { create(:upload, uploader: 'GitlabUploader') }
+ it 'migrates files to local storage' do
+ expect(Upload.where(store: ObjectStorage::Store::REMOTE).count).to eq(2)
- include_examples 'raises a SanityCheckError', /Multiple uploaders found/
- end
+ perform(uploads, ObjectStorage::Store::LOCAL)
- context 'mount point not found' do
- include_examples 'raises a SanityCheckError', /Mount point [a-z:]+ not found in/ do
- let(:mount_point) { :potato }
- end
+ expect(Upload.where(store: ObjectStorage::Store::LOCAL).count).to eq(2)
+ expect(Upload.where(store: ObjectStorage::Store::REMOTE).count).to eq(0)
end
end
- describe '#perform' do
- it 'migrates files to remote storage' do
- expect(Gitlab::AppLogger).to receive(:info).with(%r{Migrated 1/1 files})
-
- perform(uploads)
-
- expect(Upload.where(store: ObjectStorage::Store::LOCAL).count).to eq(0)
- end
-
- context 'reversed' do
- let(:to_store) { ObjectStorage::Store::LOCAL }
-
- before do
- perform(uploads, ObjectStorage::Store::REMOTE)
- end
-
- it 'migrates files to local storage' do
- expect(Upload.where(store: ObjectStorage::Store::REMOTE).count).to eq(1)
-
- perform(uploads)
-
- expect(Upload.where(store: ObjectStorage::Store::LOCAL).count).to eq(1)
- end
+ context 'migration is unsuccessful' do
+ before do
+ allow_any_instance_of(ObjectStorage::Concern)
+ .to receive(:migrate!).and_raise(CarrierWave::UploadError, 'I am a teapot.')
end
- context 'migration is unsuccessful' do
- before do
- allow_any_instance_of(ObjectStorage::Concern)
- .to receive(:migrate!).and_raise(CarrierWave::UploadError, 'I am a teapot.')
- end
-
- it 'does not migrate files to remote storage' do
- expect(Gitlab::AppLogger).to receive(:warn).with(/Error .* I am a teapot/)
+ it 'does not migrate files to remote storage' do
+ expect(Gitlab::AppLogger).to receive(:warn).with(/Error .* I am a teapot/)
- perform(uploads)
+ perform(uploads)
- expect(Upload.where(store: ObjectStorage::Store::LOCAL).count).to eq(1)
- end
+ expect(Upload.where(store: ObjectStorage::Store::LOCAL).count).to eq(2)
+ expect(Upload.where(store: ObjectStorage::Store::REMOTE).count).to eq(0)
end
end
- end
-
- context "for AvatarUploader" do
- let!(:project_with_avatar) { create(:project, :with_avatar) }
- let(:mounted_as) { :avatar }
-
- before do
- stub_uploads_object_storage(AvatarUploader)
- end
-
- it_behaves_like "uploads migration worker"
describe "limits N+1 queries" do
it "to N*5" do
@@ -127,46 +69,18 @@ RSpec.describe ObjectStorage::MigrateUploadsWorker do
expect { perform(Upload.all) }.not_to exceed_query_limit(query_count).with_threshold(5)
end
end
- end
-
- context "for FileUploader" do
- let!(:project_with_file) { create(:project) }
- let(:secret) { SecureRandom.hex }
- let(:mounted_as) { nil }
-
- def upload_file(project)
- uploader = FileUploader.new(project)
- uploader.store!(fixture_file_upload('spec/fixtures/doc_sample.txt'))
- end
-
- before do
- stub_uploads_object_storage(FileUploader)
-
- upload_file(project_with_file)
- end
-
- it_behaves_like "uploads migration worker"
-
- describe "limits N+1 queries" do
- it "to N*5" do
- query_count = ActiveRecord::QueryRecorder.new { perform(uploads) }
- upload_file(create(:project))
+ it 'handles legacy argument format' do
+ described_class.new.perform(uploads.ids, 'Project', :avatar, ObjectStorage::Store::REMOTE)
- expect { perform(Upload.all) }.not_to exceed_query_limit(query_count).with_threshold(5)
- end
+ expect(Upload.where(store: ObjectStorage::Store::LOCAL).count).to eq(0)
+ expect(Upload.where(store: ObjectStorage::Store::REMOTE).count).to eq(2)
end
- end
- context 'for DesignManagement::DesignV432x230Uploader' do
- let(:model_class) { DesignManagement::Action }
- let!(:design_action) { create(:design_action, :with_image_v432x230) }
- let(:mounted_as) { :image_v432x230 }
+ it 'logs an error when number of arguments is incorrect' do
+ expect(Gitlab::AppLogger).to receive(:warn).with(/Job has wrong arguments format/)
- before do
- stub_uploads_object_storage(DesignManagement::DesignV432x230Uploader)
+ described_class.new.perform(uploads.ids, 'Project', ObjectStorage::Store::REMOTE)
end
-
- it_behaves_like 'uploads migration worker'
end
end
diff --git a/spec/validators/addressable_url_validator_spec.rb b/spec/validators/addressable_url_validator_spec.rb
index b3a4459db30..9109a899881 100644
--- a/spec/validators/addressable_url_validator_spec.rb
+++ b/spec/validators/addressable_url_validator_spec.rb
@@ -245,7 +245,7 @@ RSpec.describe AddressableUrlValidator do
end
context 'when enforce_user is' do
- let(:url) { 'http://$user@example.com'}
+ let(:url) { 'http://$user@example.com' }
let(:validator) { described_class.new(attributes: [:link_url], enforce_user: enforce_user) }
context 'true' do
@@ -274,7 +274,7 @@ RSpec.describe AddressableUrlValidator do
end
context 'when ascii_only is' do
- let(:url) { 'https://ð•˜itⅼαƄ.com/foo/foo.bar'}
+ let(:url) { 'https://ð•˜itⅼαƄ.com/foo/foo.bar' }
let(:validator) { described_class.new(attributes: [:link_url], ascii_only: ascii_only) }
context 'true' do
diff --git a/spec/views/admin/sessions/new.html.haml_spec.rb b/spec/views/admin/sessions/new.html.haml_spec.rb
index 97528b6e782..ac35bbef5b4 100644
--- a/spec/views/admin/sessions/new.html.haml_spec.rb
+++ b/spec/views/admin/sessions/new.html.haml_spec.rb
@@ -19,9 +19,9 @@ RSpec.describe 'admin/sessions/new.html.haml' do
it 'shows enter password form' do
render
- expect(rendered).to have_selector('[data-qa-selector="sign_in_tab"]') # rubocop:disable QA/SelectorUsage
+ expect(rendered).to have_selector('[data-testid="sign-in-tab"]')
expect(rendered).to have_css('#login-pane.active')
- expect(rendered).to have_selector('[data-qa-selector="password_field"]') # rubocop:disable QA/SelectorUsage
+ expect(rendered).to have_selector('[data-testid="password-field"]')
end
it 'warns authentication not possible if password not set' do
@@ -60,7 +60,7 @@ RSpec.describe 'admin/sessions/new.html.haml' do
it 'is shown when enabled' do
render
- expect(rendered).to have_selector('[data-qa-selector="ldap_tab"]') # rubocop:disable QA/SelectorUsage
+ expect(rendered).to have_selector('[data-testid="ldap-tab"]')
expect(rendered).to have_css('.login-box#ldapmain')
expect(rendered).to have_field('LDAP Username')
expect(rendered).not_to have_content('No authentication methods configured')
@@ -71,7 +71,7 @@ RSpec.describe 'admin/sessions/new.html.haml' do
render
- expect(rendered).not_to have_selector('[data-qa-selector="ldap_tab"]') # rubocop:disable QA/SelectorUsage
+ expect(rendered).not_to have_selector('[data-testid="ldap-tab"]')
expect(rendered).not_to have_field('LDAP Username')
expect(rendered).to have_content('No authentication methods configured')
end
diff --git a/spec/views/dashboard/projects/_blank_state_welcome.html.haml_spec.rb b/spec/views/dashboard/projects/_blank_state_welcome.html.haml_spec.rb
index edec46ad0a3..6f6596caabb 100644
--- a/spec/views/dashboard/projects/_blank_state_welcome.html.haml_spec.rb
+++ b/spec/views/dashboard/projects/_blank_state_welcome.html.haml_spec.rb
@@ -3,15 +3,65 @@
require 'spec_helper'
RSpec.describe 'dashboard/projects/_blank_state_welcome.html.haml' do
- let_it_be(:user) { create(:user) }
+ context 'with regular user' do
+ context 'with project creation enabled' do
+ let_it_be(:user) { create(:user) }
- before do
- allow(view).to receive(:current_user).and_return(user)
+ before do
+ allow(view).to receive(:current_user).and_return(user)
+ end
+
+ it 'has a doc_url' do
+ render
+
+ expect(rendered).to have_link(href: Gitlab::Saas.doc_url)
+ end
+
+ it "shows create project panel" do
+ render
+
+ expect(rendered).to include(_('Create a project'))
+ end
+ end
+
+ context 'with project creation disabled' do
+ let_it_be(:user_projects_limit) { create(:user, projects_limit: 0 ) }
+
+ before do
+ allow(view).to receive(:current_user).and_return(user_projects_limit)
+ end
+
+ it "doesn't show create project panel" do
+ render
+
+ expect(rendered).not_to include(_('Create a project'))
+ end
+
+ it 'shows an alert' do
+ render
+
+ expect(rendered).to include(_("You see projects here when you're added to a group or project."))
+ end
+ end
end
- it 'has a doc_url' do
- render
+ context 'with external user' do
+ let_it_be(:external_user) { create(:user, :external) }
+
+ before do
+ allow(view).to receive(:current_user).and_return(external_user)
+ end
+
+ it "doesn't show create project panel" do
+ render
+
+ expect(rendered).not_to include(_('Create a project'))
+ end
+
+ it 'shows an alert' do
+ render
- expect(rendered).to have_link(href: Gitlab::Saas.doc_url)
+ expect(rendered).to include(_("You see projects here when you're added to a group or project."))
+ end
end
end
diff --git a/spec/views/devise/sessions/new.html.haml_spec.rb b/spec/views/devise/sessions/new.html.haml_spec.rb
index c8e9aa15287..798c891e75c 100644
--- a/spec/views/devise/sessions/new.html.haml_spec.rb
+++ b/spec/views/devise/sessions/new.html.haml_spec.rb
@@ -55,7 +55,7 @@ RSpec.describe 'devise/sessions/new' do
render
expect(rendered).to have_selector('.new-session-tabs')
- expect(rendered).to have_selector('[data-qa-selector="ldap_tab"]') # rubocop:disable QA/SelectorUsage
+ expect(rendered).to have_selector('[data-testid="ldap-tab"]')
expect(rendered).to have_field('LDAP Username')
end
@@ -65,7 +65,7 @@ RSpec.describe 'devise/sessions/new' do
render
expect(rendered).to have_content('No authentication methods configured')
- expect(rendered).not_to have_selector('[data-qa-selector="ldap_tab"]') # rubocop:disable QA/SelectorUsage
+ expect(rendered).not_to have_selector('[data-testid="ldap-tab"]')
expect(rendered).not_to have_field('LDAP Username')
end
end
diff --git a/spec/views/devise/shared/_signup_box.html.haml_spec.rb b/spec/views/devise/shared/_signup_box.html.haml_spec.rb
index b0730e6fc54..ee9ccbf6ff5 100644
--- a/spec/views/devise/shared/_signup_box.html.haml_spec.rb
+++ b/spec/views/devise/shared/_signup_box.html.haml_spec.rb
@@ -7,13 +7,15 @@ RSpec.describe 'devise/shared/_signup_box' do
let(:terms_path) { '_terms_path_' }
let(:translation_com) do
- s_("SignUp|By clicking %{button_text}, I agree that I have read and accepted "\
- "the GitLab %{link_start}Terms of Use and Privacy Policy%{link_end}")
+ s_("SignUp|By clicking %{button_text} or registering through a third party you "\
+ "accept the GitLab%{link_start} Terms of Use and acknowledge the Privacy Policy "\
+ "and Cookie Policy%{link_end}")
end
let(:translation_non_com) do
- s_("SignUp|By clicking %{button_text}, I agree that I have read and accepted "\
- "the %{link_start}Terms of Use and Privacy Policy%{link_end}")
+ s_("SignUp|By clicking %{button_text} or registering through a third party you "\
+ "accept the%{link_start} Terms of Use and acknowledge the Privacy Policy and "\
+ "Cookie Policy%{link_end}")
end
before do
diff --git a/spec/views/groups/new.html.haml_spec.rb b/spec/views/groups/new.html.haml_spec.rb
index 8b12cc42a88..5c7378e8dc7 100644
--- a/spec/views/groups/new.html.haml_spec.rb
+++ b/spec/views/groups/new.html.haml_spec.rb
@@ -25,4 +25,15 @@ RSpec.describe 'groups/new.html.haml' do
expect(rendered).not_to have_checked_field('Just me')
end
end
+
+ context 'when a subgroup' do
+ let_it_be(:group) { create(:group, :nested) }
+
+ it 'renders the visibility level section' do
+ expect(rendered).to have_content('Visibility level')
+ expect(rendered).to have_field('Private')
+ expect(rendered).to have_field('Internal')
+ expect(rendered).to have_field('Public')
+ end
+ end
end
diff --git a/spec/views/groups/observability.html.haml_spec.rb b/spec/views/groups/observability.html.haml_spec.rb
new file mode 100644
index 00000000000..db280d5a2ba
--- /dev/null
+++ b/spec/views/groups/observability.html.haml_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'groups/observability/index' do
+ let_it_be(:iframe_url) { "foo.test" }
+
+ before do
+ assign(:observability_iframe_src, iframe_url)
+ end
+
+ it 'renders as expected' do
+ render
+ page = Capybara.string(rendered)
+ iframe = page.find('iframe#observability-ui-iframe')
+ expect(iframe['src']).to eq(iframe_url)
+ end
+end
diff --git a/spec/views/help/drawers.html.haml_spec.rb b/spec/views/help/drawers.html.haml_spec.rb
new file mode 100644
index 00000000000..b250d00bb20
--- /dev/null
+++ b/spec/views/help/drawers.html.haml_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'help/drawers' do
+ describe 'Markdown rendering' do
+ before do
+ allow(view).to receive(:get_markdown_without_frontmatter).and_return('[GitLab](https://about.gitlab.com/)')
+ assign(:clean_path, 'user/ssh')
+ end
+
+ it 'renders Markdown' do
+ render
+
+ expect(rendered).to have_link('GitLab', href: 'https://about.gitlab.com/')
+ end
+ end
+end
diff --git a/spec/views/help/instance_configuration.html.haml_spec.rb b/spec/views/help/instance_configuration.html.haml_spec.rb
index fbf84a5d272..4461eadf1a3 100644
--- a/spec/views/help/instance_configuration.html.haml_spec.rb
+++ b/spec/views/help/instance_configuration.html.haml_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe 'help/instance_configuration' do
describe 'General Sections:' do
- let(:instance_configuration) { build(:instance_configuration)}
+ let(:instance_configuration) { build(:instance_configuration) }
let(:settings) { instance_configuration.settings }
let(:ssh_settings) { settings[:ssh_algorithms_hashes] }
diff --git a/spec/views/layouts/_header_search.html.haml_spec.rb b/spec/views/layouts/_header_search.html.haml_spec.rb
index 3ab4ae6a483..3a21bb3a92c 100644
--- a/spec/views/layouts/_header_search.html.haml_spec.rb
+++ b/spec/views/layouts/_header_search.html.haml_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe 'layouts/_header_search' do
let(:scope) { nil }
let(:ref) { nil }
let(:code_search) { false }
- let(:for_snippets) { false}
+ let(:for_snippets) { false }
let(:header_search_context) do
{
diff --git a/spec/views/layouts/_published_experiments.html.haml_spec.rb b/spec/views/layouts/_published_experiments.html.haml_spec.rb
index 84894554bd9..072e4f2074e 100644
--- a/spec/views/layouts/_published_experiments.html.haml_spec.rb
+++ b/spec/views/layouts/_published_experiments.html.haml_spec.rb
@@ -13,10 +13,10 @@ RSpec.describe 'layouts/_published_experiments', :experiment do
test_variant: :variant_name
)
- experiment(:test_control) { }
+ experiment(:test_control) {}
experiment(:test_excluded) { |e| e.exclude! }
- experiment(:test_candidate) { |e| e.candidate { } }
- experiment(:test_variant) { |e| e.variant(:variant_name) { } }
+ experiment(:test_candidate) { |e| e.candidate {} }
+ experiment(:test_variant) { |e| e.variant(:variant_name) {} }
experiment(:test_published_only).publish
render
diff --git a/spec/views/layouts/fullscreen.html.haml_spec.rb b/spec/views/layouts/fullscreen.html.haml_spec.rb
index 0ae2c76ebcb..14b382bc238 100644
--- a/spec/views/layouts/fullscreen.html.haml_spec.rb
+++ b/spec/views/layouts/fullscreen.html.haml_spec.rb
@@ -9,5 +9,46 @@ RSpec.describe 'layouts/fullscreen' do
allow(view).to receive(:current_user_mode).and_return(Gitlab::Auth::CurrentUserMode.new(user))
end
+ it 'renders a flex container' do
+ render
+
+ expect(rendered).to have_selector(".gl--flex-full.gl-h-full")
+ expect(rendered).to have_selector(".gl--flex-full.gl-w-full")
+ end
+
it_behaves_like 'a layout which reflects the application theme setting'
+
+ describe 'sidebar' do
+ context 'when nav is set' do
+ before do
+ allow(view).to receive(:nav).and_return("admin")
+ render
+ end
+
+ it 'renders the sidebar' do
+ expect(rendered).to render_template("layouts/nav/sidebar/_admin")
+ expect(rendered).to have_selector("aside.nav-sidebar")
+ end
+
+ it 'adds the proper classes' do
+ expect(rendered).to have_selector(".layout-page.gl-mt-0\\!")
+ end
+ end
+
+ describe 'when nav is not set' do
+ before do
+ allow(view).to receive(:nav).and_return(nil)
+ render
+ end
+
+ it 'does not render the sidebar' do
+ expect(rendered).not_to render_template("layouts/nav/sidebar/_admin")
+ expect(rendered).not_to have_selector("aside.nav-sidebar")
+ end
+
+ it 'not add classes' do
+ expect(rendered).not_to have_selector(".layout-page.gl-mt-0\\!")
+ end
+ end
+ end
end
diff --git a/spec/views/layouts/header/_new_dropdown.haml_spec.rb b/spec/views/layouts/header/_new_dropdown.haml_spec.rb
index 79c22871b44..17251049c57 100644
--- a/spec/views/layouts/header/_new_dropdown.haml_spec.rb
+++ b/spec/views/layouts/header/_new_dropdown.haml_spec.rb
@@ -166,7 +166,7 @@ RSpec.describe 'layouts/header/_new_dropdown' do
let(:user) { create(:user, :external) }
it 'is nil' do
- # We have to us `view.render` because `render` causes issues
+ # We have to use `view.render` because `render` causes issues
# https://github.com/rails/rails/issues/41320
expect(view.render("layouts/header/new_dropdown")).to be_nil
end
diff --git a/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb
index 428e9cc8490..472a2f3cb34 100644
--- a/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb
+++ b/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb
@@ -109,7 +109,7 @@ RSpec.describe 'layouts/nav/sidebar/_group' do
end
end
- describe 'Packages & Registries' do
+ describe 'Packages and registries' do
it 'has a link to the package registry page' do
stub_config(packages: { enabled: true })
@@ -178,10 +178,10 @@ RSpec.describe 'layouts/nav/sidebar/_group' do
expect(rendered).to have_link('Applications', href: group_settings_applications_path(group))
end
- it 'has a link to the Package & Registries settings page' do
+ it 'has a link to the Package and registry settings page' do
render
- expect(rendered).to have_link('Packages & Registries', href: group_settings_packages_and_registries_path(group))
+ expect(rendered).to have_link('Packages and registries', href: group_settings_packages_and_registries_path(group))
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 9ae3f814679..e7d9a8a4708 100644
--- a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
+++ b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
@@ -70,8 +70,8 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
describe 'Learn GitLab' do
it 'has a link to the learn GitLab' do
allow(view).to receive(:learn_gitlab_enabled?).and_return(true)
- allow_next_instance_of(LearnGitlab::Onboarding) do |onboarding|
- expect(onboarding).to receive(:completed_percentage).and_return(20)
+ allow_next_instance_of(Onboarding::Completion) do |onboarding|
+ expect(onboarding).to receive(:percentage).and_return(20)
end
render
@@ -559,7 +559,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
it 'top level navigation link is visible and points to package registry page' do
render
- expect(rendered).to have_link('Packages & Registries', href: project_packages_path(project))
+ expect(rendered).to have_link('Packages and registries', href: project_packages_path(project))
end
describe 'Packages Registry' do
@@ -908,7 +908,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end
end
- describe 'Packages & Registries' do
+ describe 'Packages and registries' do
let(:packages_enabled) { false }
before do
@@ -919,20 +919,20 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
context 'when registry is enabled' do
let(:registry_enabled) { true }
- it 'has a link to the Packages & Registries settings' do
+ it 'has a link to the Package and registry settings' do
render
- expect(rendered).to have_link('Packages & Registries', href: project_settings_packages_and_registries_path(project))
+ expect(rendered).to have_link('Packages and registries', href: project_settings_packages_and_registries_path(project))
end
end
context 'when registry is not enabled' do
let(:registry_enabled) { false }
- it 'does not have a link to the Packages & Registries settings' do
+ it 'does not have a link to the Package and registry settings' do
render
- expect(rendered).not_to have_link('Packages & Registries', href: project_settings_packages_and_registries_path(project))
+ expect(rendered).not_to have_link('Packages and registries', href: project_settings_packages_and_registries_path(project))
end
end
@@ -940,10 +940,10 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
let(:registry_enabled) { false }
let(:packages_enabled) { true }
- it 'has a link to the Packages & Registries settings' do
+ it 'has a link to the Package and registry settings' do
render
- expect(rendered).to have_link('Packages & Registries', href: project_settings_packages_and_registries_path(project))
+ expect(rendered).to have_link('Packages and registries', href: project_settings_packages_and_registries_path(project))
end
end
end
diff --git a/spec/views/notify/approved_merge_request_email.html.haml_spec.rb b/spec/views/notify/approved_merge_request_email.html.haml_spec.rb
new file mode 100644
index 00000000000..7d19e628eb8
--- /dev/null
+++ b/spec/views/notify/approved_merge_request_email.html.haml_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'email_spec'
+
+RSpec.describe 'notify/approved_merge_request_email.html.haml' do
+ let(:user) { create(:user) }
+ let(:merge_request) { create(:merge_request) }
+ let(:group) { create(:group) }
+ let(:project) { create(:project, group: group) }
+
+ before do
+ allow(view).to receive(:message) { instance_double(Mail::Message, subject: 'Subject') }
+ assign(:project, project)
+ assign(:approved_by, user)
+ assign(:merge_request, merge_request)
+ end
+
+ it 'contains approval information' do
+ render
+
+ expect(rendered).to have_content(merge_request.to_reference.to_s)
+ expect(rendered).to have_content("was approved by")
+ expect(rendered).to have_content(user.name.to_s)
+ end
+end
diff --git a/spec/views/notify/autodevops_disabled_email.text.erb_spec.rb b/spec/views/notify/autodevops_disabled_email.text.erb_spec.rb
index c3cb0c83f35..d8299d637e1 100644
--- a/spec/views/notify/autodevops_disabled_email.text.erb_spec.rb
+++ b/spec/views/notify/autodevops_disabled_email.text.erb_spec.rb
@@ -30,7 +30,7 @@ RSpec.describe 'notify/autodevops_disabled_email.text.erb' do
expect(rendered).to have_content("Auto DevOps pipeline was disabled for #{project.name}")
expect(rendered).to match(/Pipeline ##{pipeline.id} .* triggered by #{pipeline.user.name}/)
- expect(rendered).to have_content("Stage: #{build.stage}")
+ expect(rendered).to have_content("Stage: #{build.stage_name}")
expect(rendered).to have_content("Name: #{build.name}")
expect(rendered).not_to have_content("Trace:")
end
diff --git a/spec/views/notify/change_in_merge_request_draft_status_email.html.haml_spec.rb b/spec/views/notify/change_in_merge_request_draft_status_email.html.haml_spec.rb
index 6d56145144f..deef13fec99 100644
--- a/spec/views/notify/change_in_merge_request_draft_status_email.html.haml_spec.rb
+++ b/spec/views/notify/change_in_merge_request_draft_status_email.html.haml_spec.rb
@@ -12,10 +12,25 @@ RSpec.describe 'notify/change_in_merge_request_draft_status_email.html.haml' do
assign(:merge_request, merge_request)
end
+ it 'shows user added draft status on email' do
+ merge_request.update!(title: merge_request.draft_title)
+
+ render
+
+ expect(merge_request.draft).to be_truthy
+ expect(rendered).to have_content("#{user.name} marked merge request #{merge_request.to_reference} as draft")
+ end
+
+ it 'shows user removed draft status on email' do
+ render
+
+ expect(merge_request.draft).to be_falsy
+ expect(rendered).to have_content("#{user.name} marked merge request #{merge_request.to_reference} as ready")
+ end
+
it 'renders the email correctly' do
render
- expect(rendered).to have_content("#{user.name} changed the draft status of merge request #{merge_request.to_reference}")
expect(rendered).to have_link(user.name, href: user_url(user))
expect(rendered).to have_link(merge_request.to_reference, href: merge_request_link)
end
diff --git a/spec/views/notify/change_in_merge_request_draft_status_email.text.erb_spec.rb b/spec/views/notify/change_in_merge_request_draft_status_email.text.erb_spec.rb
index a05c20fd8c4..3faba483516 100644
--- a/spec/views/notify/change_in_merge_request_draft_status_email.text.erb_spec.rb
+++ b/spec/views/notify/change_in_merge_request_draft_status_email.text.erb_spec.rb
@@ -12,9 +12,19 @@ RSpec.describe 'notify/change_in_merge_request_draft_status_email.text.erb' do
it_behaves_like 'renders plain text email correctly'
- it 'renders the email correctly' do
+ it 'shows user added draft status on email' do
+ merge_request.update!(title: merge_request.draft_title)
+
+ render
+
+ expect(merge_request.draft).to be_truthy
+ expect(rendered).to have_content("#{user.name} marked merge request #{merge_request.to_reference} as draft")
+ end
+
+ it 'shows user removed draft status on email' do
render
- expect(rendered).to have_content("#{user.name} changed the draft status of merge request #{merge_request.to_reference}")
+ expect(merge_request.draft).to be_falsy
+ expect(rendered).to have_content("#{user.name} marked merge request #{merge_request.to_reference} as ready")
end
end
diff --git a/spec/views/notify/import_issues_csv_email.html.haml_spec.rb b/spec/views/notify/import_issues_csv_email.html.haml_spec.rb
new file mode 100644
index 00000000000..43dfab87ac9
--- /dev/null
+++ b/spec/views/notify/import_issues_csv_email.html.haml_spec.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'notify/import_issues_csv_email.html.haml' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:correct_results) { { success: 3, valid_file: true } }
+ let(:errored_results) { { success: 3, error_lines: [5, 6, 7], valid_file: true } }
+ let(:parse_error_results) { { success: 0, parse_error: true } }
+
+ before do
+ assign(:user, user)
+ assign(:project, project)
+ end
+
+ context 'when no errors found while importing' do
+ before do
+ assign(:results, correct_results)
+ end
+
+ it 'renders correctly' do
+ render
+
+ expect(rendered).to have_link(project.full_name, href: project_url(project))
+ expect(rendered).to have_content("3 issues imported")
+ expect(rendered).not_to have_content("Errors found on line")
+ expect(rendered).not_to have_content(
+ "Error parsing CSV file. Please make sure it has the correct format: \
+a delimited text file that uses a comma to separate values.")
+ end
+ end
+
+ context 'when import errors reported' do
+ before do
+ assign(:results, errored_results)
+ end
+
+ it 'renders correctly' do
+ render
+
+ expect(rendered).to have_content("Errors found on lines: #{errored_results[:error_lines].join(", ")}. \
+Please check if these lines have an issue title.")
+ expect(rendered).not_to have_content("Error parsing CSV file. Please make sure it has the correct format: \
+a delimited text file that uses a comma to separate values.")
+ end
+ end
+
+ context 'when parse error reported while importing' do
+ before do
+ assign(:results, parse_error_results)
+ end
+
+ it 'renders with parse error' do
+ render
+
+ expect(rendered).to have_content("Error parsing CSV file. \
+Please make sure it has the correct format: a delimited text file that uses a comma to separate values.")
+ 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 c807512a11a..3c61afb21c5 100644
--- a/spec/views/profiles/keys/_form.html.haml_spec.rb
+++ b/spec/views/profiles/keys/_form.html.haml_spec.rb
@@ -33,9 +33,9 @@ RSpec.describe 'profiles/keys/_form.html.haml' do
end
it 'has the expires at field', :aggregate_failures do
- expect(rendered).to have_field('Expiration date', type: 'date')
+ 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"))
- expect(rendered).to have_text('Key becomes invalid on this date')
+ expect(rendered).to have_text( s_('Profiles|Optional but recommended. If set, key becomes invalid on the specified date.'))
end
it 'has the validation warning', :aggregate_failures do
diff --git a/spec/views/profiles/preferences/show.html.haml_spec.rb b/spec/views/profiles/preferences/show.html.haml_spec.rb
index 2fe941b9f14..4e4499c3252 100644
--- a/spec/views/profiles/preferences/show.html.haml_spec.rb
+++ b/spec/views/profiles/preferences/show.html.haml_spec.rb
@@ -54,8 +54,8 @@ RSpec.describe 'profiles/preferences/show' do
end
it 'has helpful homepage setup guidance' do
- expect(rendered).to have_field('Homepage content')
- expect(rendered).to have_content('Choose what content you want to see on your homepage.')
+ expect(rendered).to have_field('Dashboard')
+ expect(rendered).to have_content('Choose what content you want to see by default on your dashboard.')
end
end
diff --git a/spec/views/profiles/show.html.haml_spec.rb b/spec/views/profiles/show.html.haml_spec.rb
index daa1d20e6b1..5751d47ee97 100644
--- a/spec/views/profiles/show.html.haml_spec.rb
+++ b/spec/views/profiles/show.html.haml_spec.rb
@@ -17,6 +17,11 @@ RSpec.describe 'profiles/show' do
expect(rendered).to have_field('user_name', with: user.name)
expect(rendered).to have_field('user_id', with: user.id)
+
+ expectd_link = help_page_path('user/profile/index', anchor: 'change-the-email-displayed-on-your-commits')
+ expected_link_html = "<a href=\"#{expectd_link}\" target=\"_blank\" " \
+ "rel=\"noopener noreferrer\">#{_('Learn more.')}</a>"
+ expect(rendered.include?(expected_link_html)).to eq(true)
end
end
end
diff --git a/spec/views/projects/edit.html.haml_spec.rb b/spec/views/projects/edit.html.haml_spec.rb
index a85ddf7a005..2935e4395ba 100644
--- a/spec/views/projects/edit.html.haml_spec.rb
+++ b/spec/views/projects/edit.html.haml_spec.rb
@@ -28,62 +28,6 @@ RSpec.describe 'projects/edit' do
end
end
- context 'merge suggestions settings' do
- it 'displays a placeholder if none is set' do
- render
-
- expect(rendered).to have_field('project[suggestion_commit_message]', placeholder: "Apply %{suggestions_count} suggestion(s) to %{files_count} file(s)")
- end
-
- it 'displays the user entered value' do
- project.update!(suggestion_commit_message: 'refactor: changed %{file_paths}')
-
- render
-
- expect(rendered).to have_field('project[suggestion_commit_message]', with: 'refactor: changed %{file_paths}')
- end
- end
-
- context 'merge commit template' do
- it 'displays default template if none is set' do
- render
-
- expect(rendered).to have_field('project[merge_commit_template_or_default]', with: <<~MSG.rstrip)
- Merge branch '%{source_branch}' into '%{target_branch}'
-
- %{title}
-
- %{issues}
-
- See merge request %{reference}
- MSG
- end
-
- it 'displays the user entered value' do
- project.update!(merge_commit_template: '%{title}')
-
- render
-
- expect(rendered).to have_field('project[merge_commit_template_or_default]', with: '%{title}')
- end
- end
-
- context 'squash template' do
- it 'displays default template if none is set' do
- render
-
- expect(rendered).to have_field('project[squash_commit_template_or_default]', with: '%{title}')
- end
-
- it 'displays the user entered value' do
- project.update!(squash_commit_template: '%{first_multiline_commit}')
-
- render
-
- expect(rendered).to have_field('project[squash_commit_template_or_default]', with: '%{first_multiline_commit}')
- end
- end
-
context 'forking' do
before do
assign(:project, project)
diff --git a/spec/views/projects/imports/new.html.haml_spec.rb b/spec/views/projects/imports/new.html.haml_spec.rb
index 7c171ee65b9..7f537022445 100644
--- a/spec/views/projects/imports/new.html.haml_spec.rb
+++ b/spec/views/projects/imports/new.html.haml_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe "projects/imports/new.html.haml" do
project.add_maintainer(user)
end
- it "escapes HTML in import errors" do
+ it "escapes HTML in import errors", :skip_html_escaped_tags_check do
assign(:project, project)
render
diff --git a/spec/views/projects/settings/merge_requests/show.html.haml_spec.rb b/spec/views/projects/settings/merge_requests/show.html.haml_spec.rb
new file mode 100644
index 00000000000..821f430eb10
--- /dev/null
+++ b/spec/views/projects/settings/merge_requests/show.html.haml_spec.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'projects/settings/merge_requests/show' do
+ include Devise::Test::ControllerHelpers
+ include ProjectForksHelper
+
+ let(:project) { create(:project) }
+ let(:user) { create(:admin) }
+
+ before do
+ assign(:project, project)
+
+ allow(controller).to receive(:current_user).and_return(user)
+ allow(view).to receive_messages(current_user: user,
+ can?: true,
+ current_application_settings: Gitlab::CurrentSettings.current_application_settings)
+ end
+
+ describe 'merge suggestions settings' do
+ it 'displays a placeholder if none is set' do
+ render
+
+ placeholder = "Apply %{suggestions_count} suggestion(s) to %{files_count} file(s)"
+
+ expect(rendered).to have_field('project[suggestion_commit_message]', placeholder: placeholder)
+ end
+
+ it 'displays the user entered value' do
+ project.update!(suggestion_commit_message: 'refactor: changed %{file_paths}')
+
+ render
+
+ expect(rendered).to have_field('project[suggestion_commit_message]', with: 'refactor: changed %{file_paths}')
+ end
+ end
+
+ describe 'merge commit template' do
+ it 'displays default template if none is set' do
+ render
+
+ expect(rendered).to have_field('project[merge_commit_template_or_default]', with: <<~MSG.rstrip)
+ Merge branch '%{source_branch}' into '%{target_branch}'
+
+ %{title}
+
+ %{issues}
+
+ See merge request %{reference}
+ MSG
+ end
+
+ it 'displays the user entered value' do
+ project.update!(merge_commit_template: '%{title}')
+
+ render
+
+ expect(rendered).to have_field('project[merge_commit_template_or_default]', with: '%{title}')
+ end
+ end
+
+ describe 'squash template' do
+ it 'displays default template if none is set' do
+ render
+
+ expect(rendered).to have_field('project[squash_commit_template_or_default]', with: '%{title}')
+ end
+
+ it 'displays the user entered value' do
+ project.update!(squash_commit_template: '%{first_multiline_commit}')
+
+ render
+
+ expect(rendered).to have_field('project[squash_commit_template_or_default]', with: '%{first_multiline_commit}')
+ end
+ end
+end
diff --git a/spec/views/projects/tags/index.html.haml_spec.rb b/spec/views/projects/tags/index.html.haml_spec.rb
index aff233b697f..99db5d9e2a8 100644
--- a/spec/views/projects/tags/index.html.haml_spec.rb
+++ b/spec/views/projects/tags/index.html.haml_spec.rb
@@ -7,8 +7,8 @@ RSpec.describe 'projects/tags/index.html.haml' do
let_it_be(:git_tag) { project.repository.tags.last }
let_it_be(:release) do
create(:release, project: project,
- sha: git_tag.target_commit.sha,
- tag: 'v1.1.0')
+ sha: git_tag.target_commit.sha,
+ tag: 'v1.1.0')
end
let(:pipeline) { create(:ci_pipeline, :success, project: project, ref: git_tag.name, sha: release.sha) }
diff --git a/spec/views/shared/runners/_runner_details.html.haml_spec.rb b/spec/views/shared/runners/_runner_details.html.haml_spec.rb
index cdf5ec563d0..978750c8435 100644
--- a/spec/views/shared/runners/_runner_details.html.haml_spec.rb
+++ b/spec/views/shared/runners/_runner_details.html.haml_spec.rb
@@ -113,14 +113,14 @@ RSpec.describe 'shared/runners/_runner_details.html.haml' do
describe 'Tags value' do
context 'when runner does not have tags' do
it { is_expected.to have_content('Tags') }
- it { is_expected.not_to have_selector('span.gl-badge.badge.badge-info')}
+ it { is_expected.not_to have_selector('span.gl-badge.badge.badge-info') }
end
context 'when runner have tags' do
let(:runner) { create(:ci_runner, tag_list: %w(tag2 tag3 tag1)) }
it { is_expected.to have_content('Tags tag1 tag2 tag3') }
- it { is_expected.to have_selector('span.gl-badge.badge.badge-info')}
+ it { is_expected.to have_selector('span.gl-badge.badge.badge-info') }
end
end
diff --git a/spec/views/shared/web_hooks/_web_hook_disabled_alert.html.haml_spec.rb b/spec/views/shared/web_hooks/_web_hook_disabled_alert.html.haml_spec.rb
new file mode 100644
index 00000000000..22ed8bb262c
--- /dev/null
+++ b/spec/views/shared/web_hooks/_web_hook_disabled_alert.html.haml_spec.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'shared/web_hooks/_web_hook_disabled_alert' do
+ let_it_be(:project) { create(:project) }
+
+ let(:show_project_hook_failed_callout?) { false }
+
+ def after_flash_content
+ view.content_for(:after_flash_content)
+ end
+
+ before do
+ assign(:project, project)
+ allow(view).to receive(:show_project_hook_failed_callout?).and_return(show_project_hook_failed_callout?)
+ end
+
+ context 'when show_project_hook_failed_callout? is true' do
+ let(:show_project_hook_failed_callout?) { true }
+
+ it 'adds alert to `:after_flash_content`' do
+ render
+
+ expect(after_flash_content).to have_content('Webhook disabled')
+ end
+ end
+
+ context 'when show_project_hook_failed_callout? is false' do
+ it 'does not add alert to `:after_flash_content`' do
+ # We have to use `view.render` because `render` causes issues
+ # https://github.com/rails/rails/issues/41320
+ view.render('shared/web_hooks/web_hook_disabled_alert')
+
+ expect(after_flash_content).to be_nil
+ end
+ end
+end
diff --git a/spec/workers/analytics/usage_trends/counter_job_worker_spec.rb b/spec/workers/analytics/usage_trends/counter_job_worker_spec.rb
index c45ec20fe5a..ee1bbafa9b5 100644
--- a/spec/workers/analytics/usage_trends/counter_job_worker_spec.rb
+++ b/spec/workers/analytics/usage_trends/counter_job_worker_spec.rb
@@ -48,11 +48,43 @@ RSpec.describe Analytics::UsageTrends::CounterJobWorker do
end
it 'does not insert anything when BatchCount returns error' do
- allow(Gitlab::Database::BatchCount).to receive(:batch_count).and_return(Gitlab::Database::BatchCounter::FALLBACK)
+ allow(Gitlab::Database::BatchCount).to receive(:batch_count_with_timeout)
+ .and_return({ status: :canceled })
expect { subject }.not_to change { Analytics::UsageTrends::Measurement.count }
end
+ context 'when the timeout elapses' do
+ let(:min_id) { 1 }
+ let(:max_id) { 12345 }
+ let(:continue_from) { 321 }
+ let(:partial_results) { 42 }
+ let(:final_count) { 123 }
+
+ subject { described_class.new.perform(users_measurement_identifier, min_id, max_id, recorded_at) }
+
+ it 'continues counting later when the timeout elapses' do
+ expect(Gitlab::Database::BatchCount).to receive(:batch_count_with_timeout)
+ .with(anything, start: min_id, finish: max_id, timeout: 250.seconds, partial_results: nil)
+ .and_return({ status: :timeout, partial_results: partial_results, continue_from: continue_from })
+
+ expect(described_class).to receive(:perform_async).with(anything, continue_from, max_id, recorded_at, partial_results) do |*args|
+ described_class.new.perform(*args)
+ end
+
+ expect(Gitlab::Database::BatchCount).to receive(:batch_count_with_timeout)
+ .with(anything, start: continue_from, finish: max_id, timeout: 250.seconds, partial_results: partial_results)
+ .and_return({ status: :completed, count: final_count })
+
+ expect { subject }.to change { Analytics::UsageTrends::Measurement.count }
+
+ measurement = Analytics::UsageTrends::Measurement.users.last
+ expect(measurement.recorded_at).to be_like_time(recorded_at)
+ expect(measurement.identifier).to eq('users')
+ expect(measurement.count).to eq(final_count)
+ end
+ end
+
context 'when pipelines_succeeded identifier is passed' do
let_it_be(:pipeline) { create(:ci_pipeline, :success) }
diff --git a/spec/workers/bulk_imports/export_request_worker_spec.rb b/spec/workers/bulk_imports/export_request_worker_spec.rb
index 846df63a4d7..a7f7aaa7dba 100644
--- a/spec/workers/bulk_imports/export_request_worker_spec.rb
+++ b/spec/workers/bulk_imports/export_request_worker_spec.rb
@@ -60,7 +60,7 @@ RSpec.describe BulkImports::ExportRequestWorker do
context 'when entity is group' do
let(:entity) { create(:bulk_import_entity, :group_entity, source_full_path: 'foo/bar', bulk_import: bulk_import) }
- let(:expected) { '/groups/foo%2Fbar/export_relations'}
+ let(:expected) { '/groups/foo%2Fbar/export_relations' }
include_examples 'requests relations export for api resource'
end
diff --git a/spec/workers/ci/build_finished_worker_spec.rb b/spec/workers/ci/build_finished_worker_spec.rb
index 5ddaabc3938..e8bb3988001 100644
--- a/spec/workers/ci/build_finished_worker_spec.rb
+++ b/spec/workers/ci/build_finished_worker_spec.rb
@@ -27,19 +27,6 @@ RSpec.describe Ci::BuildFinishedWorker do
subject
end
- context 'when the execute_build_hooks_inline feature flag is disabled' do
- before do
- stub_feature_flags(execute_build_hooks_inline: false)
- end
-
- it 'uses the BuildHooksWorker' do
- expect(build).not_to receive(:execute_hooks)
- expect(BuildHooksWorker).to receive(:perform_async).with(build)
-
- subject
- end
- end
-
context 'when build is failed' do
before do
build.update!(status: :failed)
diff --git a/spec/workers/ci/job_artifacts/track_artifact_report_worker_spec.rb b/spec/workers/ci/job_artifacts/track_artifact_report_worker_spec.rb
new file mode 100644
index 00000000000..e18539cc6e3
--- /dev/null
+++ b/spec/workers/ci/job_artifacts/track_artifact_report_worker_spec.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::JobArtifacts::TrackArtifactReportWorker do
+ describe '#perform', :clean_gitlab_redis_shared_state do
+ let_it_be(:group) { create(:group, :private) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:user) { create(:user) }
+
+ let_it_be(:pipeline) { create(:ci_pipeline, :with_test_reports, project: project, user: user) }
+
+ subject(:perform) { described_class.new.perform(pipeline_id) }
+
+ context 'when pipeline is found' do
+ let(:pipeline_id) { pipeline.id }
+
+ it 'executed service' do
+ expect_next_instance_of(Ci::JobArtifacts::TrackArtifactReportService) do |instance|
+ expect(instance).to receive(:execute).with(pipeline)
+ end
+
+ perform
+ end
+
+ it_behaves_like 'an idempotent worker' do
+ let(:job_args) { pipeline_id }
+ let(:test_event_name) { 'i_testing_test_report_uploaded' }
+ let(:start_time) { 1.week.ago }
+ let(:end_time) { 1.week.from_now }
+
+ subject(:idempotent_perform) { perform_multiple(pipeline_id, exec_times: 2) }
+
+ it 'does not try to increment again' do
+ idempotent_perform
+
+ unique_pipeline_pass = Gitlab::UsageDataCounters::HLLRedisCounter.unique_events(
+ event_names: test_event_name,
+ start_date: start_time,
+ end_date: end_time
+ )
+ expect(unique_pipeline_pass).to eq(1)
+ end
+ end
+ end
+
+ context 'when pipeline is not found' do
+ let(:pipeline_id) { non_existing_record_id }
+
+ it 'does not execute service' do
+ allow_next_instance_of(Ci::JobArtifacts::TrackArtifactReportService) do |instance|
+ expect(instance).not_to receive(:execute)
+ end
+
+ expect { perform }
+ .not_to raise_error
+ end
+ end
+ end
+end
diff --git a/spec/workers/cleanup_container_repository_worker_spec.rb b/spec/workers/cleanup_container_repository_worker_spec.rb
index edb815f426d..817b71c8cc6 100644
--- a/spec/workers/cleanup_container_repository_worker_spec.rb
+++ b/spec/workers/cleanup_container_repository_worker_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe CleanupContainerRepositoryWorker, :clean_gitlab_redis_shared_stat
it 'executes the destroy service' do
expect(Projects::ContainerRepository::CleanupTagsService).to receive(:new)
- .with(repository, user, params)
+ .with(container_repository: repository, current_user: user, params: params)
.and_return(service)
expect(service).to receive(:execute)
diff --git a/spec/workers/clusters/cleanup/project_namespace_worker_spec.rb b/spec/workers/clusters/cleanup/project_namespace_worker_spec.rb
index b9219586a0b..c24ca71eb35 100644
--- a/spec/workers/clusters/cleanup/project_namespace_worker_spec.rb
+++ b/spec/workers/clusters/cleanup/project_namespace_worker_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe Clusters::Cleanup::ProjectNamespaceWorker do
end
context 'when exceeded the execution limit' do
- subject { worker_instance.perform(cluster.id, worker_instance.send(:execution_limit))}
+ subject { worker_instance.perform(cluster.id, worker_instance.send(:execution_limit)) }
it 'logs the error' do
expect(logger).to receive(:error)
diff --git a/spec/workers/concerns/application_worker_spec.rb b/spec/workers/concerns/application_worker_spec.rb
index 707fa0c9c78..5fde54b98f0 100644
--- a/spec/workers/concerns/application_worker_spec.rb
+++ b/spec/workers/concerns/application_worker_spec.rb
@@ -289,7 +289,6 @@ RSpec.describe ApplicationWorker do
perform_action
expect(worker.jobs.count).to eq args.count
- expect(worker.jobs).to all(include('enqueued_at'))
end
end
@@ -302,7 +301,6 @@ RSpec.describe ApplicationWorker do
perform_action
expect(worker.jobs.count).to eq args.count
- expect(worker.jobs).to all(include('enqueued_at'))
end
end
diff --git a/spec/workers/concerns/cluster_agent_queue_spec.rb b/spec/workers/concerns/cluster_agent_queue_spec.rb
index b5189cbd8c8..4f67102a0be 100644
--- a/spec/workers/concerns/cluster_agent_queue_spec.rb
+++ b/spec/workers/concerns/cluster_agent_queue_spec.rb
@@ -14,6 +14,5 @@ RSpec.describe ClusterAgentQueue do
end
end
- it { expect(worker.queue).to eq('cluster_agent:example') }
it { expect(worker.get_feature_category).to eq(:kubernetes_management) }
end
diff --git a/spec/workers/concerns/cluster_queue_spec.rb b/spec/workers/concerns/cluster_queue_spec.rb
deleted file mode 100644
index c03ca9cea48..00000000000
--- a/spec/workers/concerns/cluster_queue_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe ClusterQueue do
- let(:worker) do
- Class.new do
- def self.name
- 'DummyWorker'
- end
-
- include ApplicationWorker
- include ClusterQueue
- end
- end
-
- it 'sets a default pipelines queue automatically' do
- expect(worker.sidekiq_options['queue'])
- .to eq 'gcp_cluster:dummy'
- end
-end
diff --git a/spec/workers/concerns/cronjob_queue_spec.rb b/spec/workers/concerns/cronjob_queue_spec.rb
index 0244535051f..7dd016fc78a 100644
--- a/spec/workers/concerns/cronjob_queue_spec.rb
+++ b/spec/workers/concerns/cronjob_queue_spec.rb
@@ -40,10 +40,6 @@ RSpec.describe CronjobQueue do
stub_const("AnotherWorker", another_worker)
end
- it 'sets the queue name of a worker' do
- expect(worker.sidekiq_options['queue'].to_s).to eq('cronjob:dummy')
- end
-
it 'disables retrying of failed jobs' do
expect(worker.sidekiq_options['retry']).to eq(false)
end
diff --git a/spec/workers/concerns/gitlab/github_import/queue_spec.rb b/spec/workers/concerns/gitlab/github_import/queue_spec.rb
deleted file mode 100644
index beca221b593..00000000000
--- a/spec/workers/concerns/gitlab/github_import/queue_spec.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Gitlab::GithubImport::Queue do
- it 'sets the Sidekiq options for the worker' do
- worker = Class.new do
- def self.name
- 'DummyWorker'
- end
-
- include ApplicationWorker
- include Gitlab::GithubImport::Queue
- end
-
- expect(worker.sidekiq_options['queue']).to eq('github_importer:dummy')
- end
-end
diff --git a/spec/workers/concerns/pipeline_background_queue_spec.rb b/spec/workers/concerns/pipeline_background_queue_spec.rb
deleted file mode 100644
index 77c7e7440c5..00000000000
--- a/spec/workers/concerns/pipeline_background_queue_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe PipelineBackgroundQueue do
- let(:worker) do
- Class.new do
- def self.name
- 'DummyWorker'
- end
-
- include ApplicationWorker
- include PipelineBackgroundQueue
- end
- end
-
- it 'sets a default object storage queue automatically' do
- expect(worker.sidekiq_options['queue'])
- .to eq 'pipeline_background:dummy'
- end
-end
diff --git a/spec/workers/concerns/pipeline_queue_spec.rb b/spec/workers/concerns/pipeline_queue_spec.rb
deleted file mode 100644
index 6c1ac2052e4..00000000000
--- a/spec/workers/concerns/pipeline_queue_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe PipelineQueue do
- let(:worker) do
- Class.new do
- def self.name
- 'DummyWorker'
- end
-
- include ApplicationWorker
- include PipelineQueue
- end
- end
-
- it 'sets a default pipelines queue automatically' do
- expect(worker.sidekiq_options['queue'])
- .to eq 'pipeline_default:dummy'
- end
-end
diff --git a/spec/workers/concerns/repository_check_queue_spec.rb b/spec/workers/concerns/repository_check_queue_spec.rb
index ae377c09b37..08ac73aac7b 100644
--- a/spec/workers/concerns/repository_check_queue_spec.rb
+++ b/spec/workers/concerns/repository_check_queue_spec.rb
@@ -14,10 +14,6 @@ RSpec.describe RepositoryCheckQueue do
end
end
- it 'sets the queue name of a worker' do
- expect(worker.sidekiq_options['queue'].to_s).to eq('repository_check:dummy')
- end
-
it 'disables retrying of failed jobs' do
expect(worker.sidekiq_options['retry']).to eq(false)
end
diff --git a/spec/workers/concerns/waitable_worker_spec.rb b/spec/workers/concerns/waitable_worker_spec.rb
index bf156c3b8cb..2df5b60deaf 100644
--- a/spec/workers/concerns/waitable_worker_spec.rb
+++ b/spec/workers/concerns/waitable_worker_spec.rb
@@ -49,8 +49,7 @@ RSpec.describe WaitableWorker do
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'))
+ 'job_status' => 'running'))
.once)
worker.bulk_perform_and_wait(args_list)
diff --git a/spec/workers/disallow_two_factor_for_group_worker_spec.rb b/spec/workers/disallow_two_factor_for_group_worker_spec.rb
index f30b12dd7f4..3a875727cce 100644
--- a/spec/workers/disallow_two_factor_for_group_worker_spec.rb
+++ b/spec/workers/disallow_two_factor_for_group_worker_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe DisallowTwoFactorForGroupWorker do
expect(group.reload.require_two_factor_authentication).to eq(false)
end
- it "updates group members" do
+ it "updates group members", :sidekiq_inline do
group.add_member(user, GroupMember::DEVELOPER)
described_class.new.perform(group.id)
diff --git a/spec/workers/emails_on_push_worker_spec.rb b/spec/workers/emails_on_push_worker_spec.rb
index 3e313610054..7d11957e2df 100644
--- a/spec/workers/emails_on_push_worker_spec.rb
+++ b/spec/workers/emails_on_push_worker_spec.rb
@@ -51,7 +51,7 @@ RSpec.describe EmailsOnPushWorker, :mailer do
context "when push is a force push to delete commits" do
before do
data_force_push = data.stringify_keys.merge(
- "after" => data[:before],
+ "after" => data[:before],
"before" => data[:after]
)
diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb
index 4a1bf7dbbf9..6b67c2b474c 100644
--- a/spec/workers/every_sidekiq_worker_spec.rb
+++ b/spec/workers/every_sidekiq_worker_spec.rb
@@ -257,11 +257,13 @@ RSpec.describe 'Every Sidekiq worker' do
'GeoRepositoryDestroyWorker' => 3,
'GitGarbageCollectWorker' => false,
'Gitlab::GithubImport::AdvanceStageWorker' => 3,
+ 'Gitlab::GithubImport::ImportReleaseAttachmentsWorker' => 5,
'Gitlab::GithubImport::ImportDiffNoteWorker' => 5,
'Gitlab::GithubImport::ImportIssueWorker' => 5,
'Gitlab::GithubImport::ImportIssueEventWorker' => 5,
'Gitlab::GithubImport::ImportLfsObjectWorker' => 5,
'Gitlab::GithubImport::ImportNoteWorker' => 5,
+ 'Gitlab::GithubImport::ImportProtectedBranchWorker' => 5,
'Gitlab::GithubImport::ImportPullRequestMergedByWorker' => 5,
'Gitlab::GithubImport::ImportPullRequestReviewWorker' => 5,
'Gitlab::GithubImport::ImportPullRequestWorker' => 5,
@@ -271,6 +273,8 @@ RSpec.describe 'Every Sidekiq worker' do
'Gitlab::GithubImport::Stage::ImportIssuesAndDiffNotesWorker' => 5,
'Gitlab::GithubImport::Stage::ImportIssueEventsWorker' => 5,
'Gitlab::GithubImport::Stage::ImportLfsObjectsWorker' => 5,
+ 'Gitlab::GithubImport::Stage::ImportAttachmentsWorker' => 5,
+ 'Gitlab::GithubImport::Stage::ImportProtectedBranchesWorker' => 5,
'Gitlab::GithubImport::Stage::ImportNotesWorker' => 5,
'Gitlab::GithubImport::Stage::ImportPullRequestsMergedByWorker' => 5,
'Gitlab::GithubImport::Stage::ImportPullRequestsReviewsWorker' => 5,
@@ -311,6 +315,7 @@ RSpec.describe 'Every Sidekiq worker' do
'Integrations::IrkerWorker' => 3,
'InvalidGpgSignatureUpdateWorker' => 3,
'IssuableExportCsvWorker' => 3,
+ 'Issues::CloseWorker' => 3,
'Issues::PlacementWorker' => 3,
'Issues::RebalancingWorker' => 3,
'IterationsUpdateStatusWorker' => 3,
@@ -356,6 +361,7 @@ RSpec.describe 'Every Sidekiq worker' do
'ObjectPool::ScheduleJoinWorker' => 3,
'ObjectStorage::BackgroundMoveWorker' => 5,
'ObjectStorage::MigrateUploadsWorker' => 3,
+ 'Onboarding::CreateLearnGitlabWorker' => 3,
'Packages::CleanupPackageFileWorker' => 0,
'Packages::Cleanup::ExecutePolicyWorker' => 0,
'Packages::Composer::CacheUpdateWorker' => false,
diff --git a/spec/workers/gitlab/github_import/import_protected_branch_worker_spec.rb b/spec/workers/gitlab/github_import/import_protected_branch_worker_spec.rb
new file mode 100644
index 00000000000..4a3ef2bf560
--- /dev/null
+++ b/spec/workers/gitlab/github_import/import_protected_branch_worker_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::ImportProtectedBranchWorker do
+ let(:worker) { described_class.new }
+
+ let(:import_state) { build_stubbed(:import_state, :started) }
+ let(:project) { instance_double('Project', full_path: 'foo/bar', id: 1, import_state: import_state) }
+ let(:client) { instance_double('Gitlab::GithubImport::Client') }
+ let(:importer) { instance_double('Gitlab::GithubImport::Importer::ProtectedBranchImporter') }
+
+ describe '#import' do
+ let(:json_hash) do
+ {
+ id: 'main',
+ allow_force_pushes: true
+ }
+ end
+
+ it 'imports protected branch rule' do
+ expect(Gitlab::GithubImport::Importer::ProtectedBranchImporter)
+ .to receive(:new)
+ .with(
+ an_instance_of(Gitlab::GithubImport::Representation::ProtectedBranch),
+ project,
+ client
+ )
+ .and_return(importer)
+
+ expect(importer).to receive(:execute)
+
+ expect(Gitlab::GithubImport::ObjectCounter)
+ .to receive(:increment)
+ .with(project, :protected_branch, :imported)
+
+ worker.import(project, client, json_hash)
+ end
+ end
+end
diff --git a/spec/workers/gitlab/github_import/import_release_attachments_worker_spec.rb b/spec/workers/gitlab/github_import/import_release_attachments_worker_spec.rb
new file mode 100644
index 00000000000..cd53c6ee9c0
--- /dev/null
+++ b/spec/workers/gitlab/github_import/import_release_attachments_worker_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::ImportReleaseAttachmentsWorker do
+ subject(:worker) { described_class.new }
+
+ describe '#import' do
+ let(:import_state) { create(:import_state, :started) }
+
+ let(:project) do
+ instance_double('Project', full_path: 'foo/bar', id: 1, import_state: import_state)
+ end
+
+ let(:client) { instance_double('Gitlab::GithubImport::Client') }
+ let(:importer) { instance_double('Gitlab::GithubImport::Importer::ReleaseAttachmentsImporter') }
+
+ let(:release_hash) do
+ {
+ 'release_db_id' => rand(100),
+ 'description' => <<-TEXT
+ Some text...
+
+ ![special-image](https://user-images.githubusercontent.com...)
+ TEXT
+ }
+ end
+
+ it 'imports an issue event' do
+ expect(Gitlab::GithubImport::Importer::ReleaseAttachmentsImporter)
+ .to receive(:new)
+ .with(
+ an_instance_of(Gitlab::GithubImport::Representation::ReleaseAttachments),
+ project,
+ client
+ )
+ .and_return(importer)
+
+ expect(importer).to receive(:execute)
+
+ expect(Gitlab::GithubImport::ObjectCounter)
+ .to receive(:increment)
+ .and_call_original
+
+ worker.import(project, client, release_hash)
+ end
+ end
+end
diff --git a/spec/workers/gitlab/github_import/stage/import_attachments_worker_spec.rb b/spec/workers/gitlab/github_import/stage/import_attachments_worker_spec.rb
new file mode 100644
index 00000000000..c2c5e1dbf4e
--- /dev/null
+++ b/spec/workers/gitlab/github_import/stage/import_attachments_worker_spec.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Stage::ImportAttachmentsWorker do
+ subject(:worker) { described_class.new }
+
+ let(:project) { create(:project) }
+ let!(:group) { create(:group, projects: [project]) }
+ let(:feature_flag_state) { [group] }
+
+ describe '#import' do
+ let(:importer) { instance_double('Gitlab::GithubImport::Importer::ReleasesAttachmentsImporter') }
+ let(:client) { instance_double('Gitlab::GithubImport::Client') }
+
+ before do
+ stub_feature_flags(github_importer_attachments_import: feature_flag_state)
+ end
+
+ it 'imports release attachments' do
+ waiter = Gitlab::JobWaiter.new(2, '123')
+
+ expect(Gitlab::GithubImport::Importer::ReleasesAttachmentsImporter)
+ .to receive(:new)
+ .with(project, client)
+ .and_return(importer)
+
+ expect(importer).to receive(:execute).and_return(waiter)
+
+ expect(Gitlab::GithubImport::AdvanceStageWorker)
+ .to receive(:perform_async)
+ .with(project.id, { '123' => 2 }, :protected_branches)
+
+ worker.import(client, project)
+ end
+
+ context 'when feature flag is disabled' do
+ let(:feature_flag_state) { false }
+
+ it 'skips release attachments import and calls next stage' do
+ expect(Gitlab::GithubImport::Importer::ReleasesAttachmentsImporter).not_to receive(:new)
+ expect(Gitlab::GithubImport::AdvanceStageWorker)
+ .to receive(:perform_async).with(project.id, {}, :protected_branches)
+
+ worker.import(client, project)
+ end
+ end
+ end
+end
diff --git a/spec/workers/gitlab/github_import/stage/import_notes_worker_spec.rb b/spec/workers/gitlab/github_import/stage/import_notes_worker_spec.rb
index f9f21e4dfa2..adf20d24a7e 100644
--- a/spec/workers/gitlab/github_import/stage/import_notes_worker_spec.rb
+++ b/spec/workers/gitlab/github_import/stage/import_notes_worker_spec.rb
@@ -26,7 +26,7 @@ RSpec.describe Gitlab::GithubImport::Stage::ImportNotesWorker do
expect(Gitlab::GithubImport::AdvanceStageWorker)
.to receive(:perform_async)
- .with(project.id, { '123' => 2 }, :lfs_objects)
+ .with(project.id, { '123' => 2 }, :attachments)
worker.import(client, project)
end
diff --git a/spec/workers/gitlab/github_import/stage/import_protected_branches_worker_spec.rb b/spec/workers/gitlab/github_import/stage/import_protected_branches_worker_spec.rb
new file mode 100644
index 00000000000..0770af524a1
--- /dev/null
+++ b/spec/workers/gitlab/github_import/stage/import_protected_branches_worker_spec.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::GithubImport::Stage::ImportProtectedBranchesWorker do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:import_state) { create(:import_state, project: project) }
+
+ let(:worker) { described_class.new }
+ let(:importer) { instance_double('Gitlab::GithubImport::Importer::ProtectedBranchImporter') }
+ let(:client) { instance_double('Gitlab::GithubImport::Client') }
+
+ describe '#import' do
+ it 'imports all the pull requests' do
+ waiter = Gitlab::JobWaiter.new(2, '123')
+
+ expect(Gitlab::GithubImport::Importer::ProtectedBranchesImporter)
+ .to receive(:new)
+ .with(project, client)
+ .and_return(importer)
+
+ expect(importer)
+ .to receive(:execute)
+ .and_return(waiter)
+
+ expect(import_state)
+ .to receive(:refresh_jid_expiration)
+
+ expect(Gitlab::GithubImport::AdvanceStageWorker)
+ .to receive(:perform_async)
+ .with(project.id, { '123' => 2 }, :lfs_objects)
+
+ worker.import(client, project)
+ end
+
+ context 'when an error raised' do
+ let(:exception) { StandardError.new('_some_error_') }
+
+ before do
+ allow_next_instance_of(Gitlab::GithubImport::Importer::ProtectedBranchesImporter) do |importer|
+ allow(importer).to receive(:execute).and_raise(exception)
+ end
+ end
+
+ it 'raises an error' do
+ expect(Gitlab::Import::ImportFailureService).to receive(:track)
+ .with(
+ project_id: project.id,
+ exception: exception,
+ error_source: described_class.name,
+ metrics: true
+ ).and_call_original
+
+ expect { worker.import(client, project) }.to raise_error(StandardError)
+ end
+ end
+ end
+end
diff --git a/spec/workers/gitlab_service_ping_worker_spec.rb b/spec/workers/gitlab_service_ping_worker_spec.rb
index c88708dc50a..f17847a7b33 100644
--- a/spec/workers/gitlab_service_ping_worker_spec.rb
+++ b/spec/workers/gitlab_service_ping_worker_spec.rb
@@ -14,21 +14,36 @@ RSpec.describe GitlabServicePingWorker, :clean_gitlab_redis_shared_state do
allow(subject).to receive(:sleep)
end
- it 'does not run for GitLab.com' do
+ it 'does not run for GitLab.com when triggered from cron' do
allow(Gitlab).to receive(:com?).and_return(true)
expect(ServicePing::SubmitService).not_to receive(:new)
subject.perform
end
+ it 'runs for GitLab.com when triggered manually' do
+ allow(Gitlab).to receive(:com?).and_return(true)
+ expect(ServicePing::SubmitService).to receive(:new)
+
+ subject.perform('triggered_from_cron' => false)
+ end
+
it 'delegates to ServicePing::SubmitService' do
- expect_next_instance_of(ServicePing::SubmitService, payload: payload) do |service|
+ expect_next_instance_of(ServicePing::SubmitService, payload: payload, skip_db_write: false) do |service|
expect(service).to receive(:execute)
end
subject.perform
end
+ it 'passes Hash arguments to ServicePing::SubmitService' do
+ expect_next_instance_of(ServicePing::SubmitService, payload: payload, skip_db_write: true) do |service|
+ expect(service).to receive(:execute)
+ end
+
+ subject.perform('skip_db_write' => true)
+ end
+
context 'payload computation' do
it 'creates RawUsageData entry when there is NO entry with the same recorded_at timestamp' do
expect { subject.perform }.to change { RawUsageData.count }.by(1)
@@ -46,7 +61,7 @@ RSpec.describe GitlabServicePingWorker, :clean_gitlab_redis_shared_state do
allow(::ServicePing::BuildPayload).to receive(:new).and_raise(error)
expect(::Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception).with(error)
- expect_next_instance_of(::ServicePing::SubmitService, payload: nil) do |service|
+ expect_next_instance_of(::ServicePing::SubmitService, payload: nil, skip_db_write: false) do |service|
expect(service).to receive(:execute)
end
diff --git a/spec/workers/google_cloud/fetch_google_ip_list_worker_spec.rb b/spec/workers/google_cloud/fetch_google_ip_list_worker_spec.rb
new file mode 100644
index 00000000000..c0b32515d15
--- /dev/null
+++ b/spec/workers/google_cloud/fetch_google_ip_list_worker_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GoogleCloud::FetchGoogleIpListWorker do
+ describe '#perform' do
+ it 'returns success' do
+ allow_next_instance_of(GoogleCloud::FetchGoogleIpListService) do |service|
+ expect(service).to receive(:execute).and_return({ status: :success })
+ end
+
+ expect(described_class.new.perform).to eq({ status: :success })
+ end
+ end
+end
diff --git a/spec/workers/groups/update_two_factor_requirement_for_members_worker_spec.rb b/spec/workers/groups/update_two_factor_requirement_for_members_worker_spec.rb
new file mode 100644
index 00000000000..9d202b9452f
--- /dev/null
+++ b/spec/workers/groups/update_two_factor_requirement_for_members_worker_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Groups::UpdateTwoFactorRequirementForMembersWorker do
+ let_it_be(:group) { create(:group) }
+
+ let(:worker) { described_class.new }
+
+ describe '#perform' do
+ it 'calls #update_two_factor_requirement_for_members' do
+ allow(Group).to receive(:find_by_id).with(group.id).and_return(group)
+ expect(group).to receive(:update_two_factor_requirement_for_members)
+
+ worker.perform(group.id)
+ end
+
+ context 'when group not found' do
+ it 'returns nil' do
+ expect(worker.perform(non_existing_record_id)).to be_nil
+ end
+ end
+
+ include_examples 'an idempotent worker' do
+ let(:subject) { described_class.new.perform(group.id) }
+
+ it 'requires 2fa for group members correctly' do
+ group.update!(require_two_factor_authentication: true)
+ user = create(:user, require_two_factor_authentication_from_group: false)
+ group.add_member(user, GroupMember::OWNER)
+
+ # Using subject inside this block will process the job multiple times
+ subject
+
+ expect(user.reload.require_two_factor_authentication_from_group).to be true
+ end
+ end
+ end
+end
diff --git a/spec/workers/issues/close_worker_spec.rb b/spec/workers/issues/close_worker_spec.rb
new file mode 100644
index 00000000000..41611447db1
--- /dev/null
+++ b/spec/workers/issues/close_worker_spec.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe Issues::CloseWorker do
+ describe "#perform" do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :public, :repository) }
+ let_it_be(:issue) { create(:issue, project: project, author: user) }
+
+ let(:commit) { project.commit }
+ let(:opts) do
+ { "closed_by" => user&.id, "commit_hash" => commit.to_hash }
+ end
+
+ subject(:worker) { described_class.new }
+
+ describe "#perform" do
+ context "when the user can update the issues" do
+ it "closes the issues" do
+ worker.perform(project.id, issue.id, issue.class.to_s, opts)
+
+ issue.reload
+
+ expect(issue.closed?).to eq(true)
+ end
+
+ it "closes external issues" do
+ external_issue = ExternalIssue.new("foo", project)
+ closer = instance_double(Issues::CloseService, execute: true)
+
+ expect(Issues::CloseService).to receive(:new).with(project: project, current_user: user).and_return(closer)
+ expect(closer).to receive(:execute).with(external_issue, commit: commit)
+
+ worker.perform(project.id, external_issue.id, external_issue.class.to_s, opts)
+ end
+ end
+
+ context "when the user can not update the issues" do
+ it "does not close the issues" do
+ other_user = create(:user)
+ opts = { "closed_by" => other_user.id, "commit_hash" => commit.to_hash }
+
+ worker.perform(project.id, issue.id, issue.class.to_s, opts)
+
+ issue.reload
+
+ expect(issue.closed?).to eq(false)
+ end
+ end
+ end
+
+ shared_examples "when object does not exist" do
+ it "does not call the close issue service" do
+ expect(Issues::CloseService).not_to receive(:new)
+
+ expect { worker.perform(project.id, issue.id, issue.class.to_s, opts) }
+ .not_to raise_exception
+ end
+ end
+
+ context "when the project does not exist" do
+ before do
+ allow(Project).to receive(:find_by_id).with(project.id).and_return(nil)
+ end
+
+ it_behaves_like "when object does not exist"
+ end
+
+ context "when the user does not exist" do
+ before do
+ allow(User).to receive(:find_by_id).with(user.id).and_return(nil)
+ end
+
+ it_behaves_like "when object does not exist"
+ end
+
+ context "when the issue does not exist" do
+ before do
+ allow(Issue).to receive(:find_by_id).with(issue.id).and_return(nil)
+ end
+
+ it_behaves_like "when object does not exist"
+ end
+ end
+end
diff --git a/spec/workers/namespaces/onboarding_issue_created_worker_spec.rb b/spec/workers/namespaces/onboarding_issue_created_worker_spec.rb
index 53116815ce7..0a896d864b7 100644
--- a/spec/workers/namespaces/onboarding_issue_created_worker_spec.rb
+++ b/spec/workers/namespaces/onboarding_issue_created_worker_spec.rb
@@ -19,11 +19,11 @@ RSpec.describe Namespaces::OnboardingIssueCreatedWorker, '#perform' do
let(:job_args) { [namespace.id] }
it 'sets the onboarding progress action' do
- OnboardingProgress.onboard(namespace)
+ Onboarding::Progress.onboard(namespace)
subject
- expect(OnboardingProgress.completed?(namespace, :issue_created)).to eq(true)
+ expect(Onboarding::Progress.completed?(namespace, :issue_created)).to eq(true)
end
end
end
diff --git a/spec/workers/namespaces/process_sync_events_worker_spec.rb b/spec/workers/namespaces/process_sync_events_worker_spec.rb
index c15a74a2934..5e5179eab62 100644
--- a/spec/workers/namespaces/process_sync_events_worker_spec.rb
+++ b/spec/workers/namespaces/process_sync_events_worker_spec.rb
@@ -11,6 +11,30 @@ RSpec.describe Namespaces::ProcessSyncEventsWorker do
include_examples 'an idempotent worker'
+ describe 'deduplication' do
+ before do
+ stub_const("Ci::ProcessSyncEventsService::BATCH_SIZE", 2)
+ end
+
+ it 'has the `until_executed` deduplicate strategy' do
+ expect(described_class.get_deduplicate_strategy).to eq(:until_executed)
+ end
+
+ it 'has an option to reschedule once if deduplicated' do
+ expect(described_class.get_deduplication_options).to include({ if_deduplicated: :reschedule_once })
+ end
+
+ it 'expect the job to enqueue itself again if there was more items to be processed', :sidekiq_inline do
+ Namespaces::SyncEvent.delete_all # delete the sync_events that have been created by triggers of previous groups
+ create_list(:sync_event, 3, namespace_id: group1.id)
+ # It's called more than twice, because the job deduplication and rescheduling calls the perform_async again
+ 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
+ end
+
describe '#perform' do
subject(:perform) { worker.perform }
diff --git a/spec/workers/packages/helm/extraction_worker_spec.rb b/spec/workers/packages/helm/extraction_worker_spec.rb
index daebbda3077..70a090d6989 100644
--- a/spec/workers/packages/helm/extraction_worker_spec.rb
+++ b/spec/workers/packages/helm/extraction_worker_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Packages::Helm::ExtractionWorker, type: :worker do
describe '#perform' do
- let_it_be(:package) { create(:helm_package, without_package_files: true, status: 'processing')}
+ let_it_be(:package) { create(:helm_package, without_package_files: true, status: 'processing') }
let!(:package_file) { create(:helm_package_file, without_loaded_metadatum: true, package: package) }
let(:package_file_id) { package_file.id }
diff --git a/spec/workers/pages_domain_ssl_renewal_cron_worker_spec.rb b/spec/workers/pages_domain_ssl_renewal_cron_worker_spec.rb
index 563bbdef1be..70ffef5342e 100644
--- a/spec/workers/pages_domain_ssl_renewal_cron_worker_spec.rb
+++ b/spec/workers/pages_domain_ssl_renewal_cron_worker_spec.rb
@@ -25,8 +25,8 @@ RSpec.describe PagesDomainSslRenewalCronWorker do
end
let!(:domain_with_failed_auto_ssl) do
- create(:pages_domain, :without_certificate, :without_key, project: project,
- auto_ssl_enabled: true, auto_ssl_failed: true)
+ create(:pages_domain, :without_certificate, :without_key,
+ project: project, auto_ssl_enabled: true, auto_ssl_failed: true)
end
let!(:domain_with_expired_auto_ssl) do
diff --git a/spec/workers/pages_worker_spec.rb b/spec/workers/pages_worker_spec.rb
index 5ddfd5b43b9..ad714d8d11e 100644
--- a/spec/workers/pages_worker_spec.rb
+++ b/spec/workers/pages_worker_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe PagesWorker, :sidekiq_inline do
let(:project) { create(:project) }
- let(:ci_build) { create(:ci_build, project: project)}
+ let(:ci_build) { create(:ci_build, project: project) }
it 'calls UpdatePagesService' do
expect_next_instance_of(Projects::UpdatePagesService, project, ci_build) do |service|
diff --git a/spec/workers/process_commit_worker_spec.rb b/spec/workers/process_commit_worker_spec.rb
index 3df26c774ba..a445db3a276 100644
--- a/spec/workers/process_commit_worker_spec.rb
+++ b/spec/workers/process_commit_worker_spec.rb
@@ -115,25 +115,37 @@ RSpec.describe ProcessCommitWorker do
end
describe '#close_issues' do
- context 'when the user can update the issues' do
- it 'closes the issues' 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
+
+ context 'when process_issue_closure_in_background flag is disabled' do
+ before do
+ stub_feature_flags(process_issue_closure_in_background: false)
+ end
- issue.reload
+ context 'when the user can update the issues' do
+ it 'closes the issues' do
+ worker.close_issues(project, user, user, commit, [issue])
- expect(issue.closed?).to eq(true)
+ issue.reload
+
+ expect(issue.closed?).to eq(true)
+ end
end
- end
- context 'when the user can not update the issues' do
- it 'does not close the issues' do
- other_user = create(:user)
+ context 'when the user can not update the issues' do
+ it 'does not close the issues' do
+ other_user = create(:user)
- worker.close_issues(project, other_user, other_user, commit, [issue])
+ worker.close_issues(project, other_user, other_user, commit, [issue])
- issue.reload
+ issue.reload
- expect(issue.closed?).to eq(false)
+ expect(issue.closed?).to eq(false)
+ end
end
end
end
@@ -189,20 +201,4 @@ RSpec.describe ProcessCommitWorker do
end
end
end
-
- describe '#build_commit' do
- it 'returns a Commit' do
- commit = worker.build_commit(project, id: '123')
-
- expect(commit).to be_an_instance_of(Commit)
- end
-
- it 'parses date strings into Time instances' do
- commit = worker.build_commit(project,
- id: '123',
- authored_date: Time.current.to_s)
-
- expect(commit.authored_date).to be_a_kind_of(Time)
- 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 ec10c66968d..50b5b0a6e7b 100644
--- a/spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb
+++ b/spec/workers/projects/inactive_projects_deletion_cron_worker_spec.rb
@@ -85,86 +85,58 @@ RSpec.describe Projects::InactiveProjectsDeletionCronWorker do
end
end
- context 'when delete inactive projects feature is enabled' do
+ context 'when delete inactive projects feature is enabled', :clean_gitlab_redis_shared_state, :sidekiq_inline do
before do
stub_application_setting(delete_inactive_projects: true)
end
- context 'when feature flag is disabled' do
- before do
- stub_feature_flags(inactive_projects_deletion: false)
- end
-
- it 'does not invoke Projects::InactiveProjectsDeletionNotificationWorker' do
- expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_async)
- expect(::Projects::DestroyService).not_to receive(:new)
-
- worker.perform
- end
-
- it 'does not delete the inactive projects' do
- worker.perform
-
- expect(inactive_large_project.reload.pending_delete).to eq(false)
+ it 'invokes Projects::InactiveProjectsDeletionNotificationWorker for inactive projects' do
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis).to receive(:hset).with('inactive_projects_deletion_warning_email_notified',
+ "project:#{inactive_large_project.id}", Date.current)
end
+ expect(::Projects::InactiveProjectsDeletionNotificationWorker).to receive(:perform_async).with(
+ inactive_large_project.id, deletion_date).and_call_original
+ expect(::Projects::DestroyService).not_to receive(:new)
- it_behaves_like 'worker is running for more than 4 minutes'
- it_behaves_like 'worker finishes processing in less than 4 minutes'
+ worker.perform
end
- context 'when feature flag is enabled', :clean_gitlab_redis_shared_state, :sidekiq_inline do
- before do
- stub_feature_flags(inactive_projects_deletion: true)
- end
-
- it 'invokes Projects::InactiveProjectsDeletionNotificationWorker for inactive projects' do
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis).to receive(:hset).with('inactive_projects_deletion_warning_email_notified',
- "project:#{inactive_large_project.id}", Date.current)
- end
- expect(::Projects::InactiveProjectsDeletionNotificationWorker).to receive(:perform_async).with(
- inactive_large_project.id, deletion_date).and_call_original
- expect(::Projects::DestroyService).not_to receive(:new)
-
- worker.perform
+ it 'does not invoke InactiveProjectsDeletionNotificationWorker for already notified inactive projects' do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.hset('inactive_projects_deletion_warning_email_notified', "project:#{inactive_large_project.id}",
+ Date.current.to_s)
end
- it 'does not invoke InactiveProjectsDeletionNotificationWorker for already notified inactive projects' do
- Gitlab::Redis::SharedState.with do |redis|
- redis.hset('inactive_projects_deletion_warning_email_notified', "project:#{inactive_large_project.id}",
- Date.current.to_s)
- end
+ expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_async)
+ expect(::Projects::DestroyService).not_to receive(:new)
- expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_async)
- expect(::Projects::DestroyService).not_to receive(:new)
+ worker.perform
+ end
- worker.perform
+ it 'invokes Projects::DestroyService for projects that are inactive even after being notified' do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.hset('inactive_projects_deletion_warning_email_notified', "project:#{inactive_large_project.id}",
+ 15.months.ago.to_date.to_s)
end
- it 'invokes Projects::DestroyService for projects that are inactive even after being notified' do
- Gitlab::Redis::SharedState.with do |redis|
- redis.hset('inactive_projects_deletion_warning_email_notified', "project:#{inactive_large_project.id}",
- 15.months.ago.to_date.to_s)
- end
-
- expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_async)
- expect(::Projects::DestroyService).to receive(:new).with(inactive_large_project, admin_user, {})
- .at_least(:once).and_call_original
+ expect(::Projects::InactiveProjectsDeletionNotificationWorker).not_to receive(:perform_async)
+ expect(::Projects::DestroyService).to receive(:new).with(inactive_large_project, admin_user, {})
+ .at_least(:once).and_call_original
- worker.perform
+ worker.perform
- expect(inactive_large_project.reload.pending_delete).to eq(true)
+ expect(inactive_large_project.reload.pending_delete).to eq(true)
- Gitlab::Redis::SharedState.with do |redis|
- expect(redis.hget('inactive_projects_deletion_warning_email_notified',
- "project:#{inactive_large_project.id}")).to be_nil
- end
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.hget('inactive_projects_deletion_warning_email_notified',
+ "project:#{inactive_large_project.id}")).to be_nil
end
-
- it_behaves_like 'worker is running for more than 4 minutes'
- it_behaves_like 'worker finishes processing in less than 4 minutes'
end
+ it_behaves_like 'worker is running for more than 4 minutes'
+ it_behaves_like 'worker finishes processing in less than 4 minutes'
+
it_behaves_like 'an idempotent worker'
end
end
diff --git a/spec/workers/projects/process_sync_events_worker_spec.rb b/spec/workers/projects/process_sync_events_worker_spec.rb
index 963e0ad1028..202942ce905 100644
--- a/spec/workers/projects/process_sync_events_worker_spec.rb
+++ b/spec/workers/projects/process_sync_events_worker_spec.rb
@@ -10,6 +10,14 @@ RSpec.describe Projects::ProcessSyncEventsWorker do
include_examples 'an idempotent worker'
+ it 'has the `until_executed` deduplicate strategy' do
+ expect(described_class.get_deduplicate_strategy).to eq(:until_executed)
+ end
+
+ it 'has an option to reschedule once if deduplicated' do
+ expect(described_class.get_deduplication_options).to include({ if_deduplicated: :reschedule_once })
+ end
+
describe '#perform' do
subject(:perform) { worker.perform }
diff --git a/spec/workers/purge_dependency_proxy_cache_worker_spec.rb b/spec/workers/purge_dependency_proxy_cache_worker_spec.rb
index 3de59670f8d..84315fd6ee9 100644
--- a/spec/workers/purge_dependency_proxy_cache_worker_spec.rb
+++ b/spec/workers/purge_dependency_proxy_cache_worker_spec.rb
@@ -4,9 +4,9 @@ require 'spec_helper'
RSpec.describe PurgeDependencyProxyCacheWorker do
let_it_be(:user) { create(:admin) }
- let_it_be_with_refind(:blob) { create(:dependency_proxy_blob )}
+ let_it_be_with_refind(:blob) { create(:dependency_proxy_blob ) }
let_it_be_with_reload(:group) { blob.group }
- let_it_be_with_refind(:manifest) { create(:dependency_proxy_manifest, group: group )}
+ let_it_be_with_refind(:manifest) { create(:dependency_proxy_manifest, group: group ) }
let_it_be(:group_id) { group.id }
subject { described_class.new.perform(user.id, group_id) }
diff --git a/spec/workers/releases/manage_evidence_worker_spec.rb b/spec/workers/releases/manage_evidence_worker_spec.rb
index 2fbfb6c9dc1..886fcd346eb 100644
--- a/spec/workers/releases/manage_evidence_worker_spec.rb
+++ b/spec/workers/releases/manage_evidence_worker_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe Releases::ManageEvidenceWorker do
context 'when evidence has already been created' do
let(:release) { create(:release, project: project, released_at: 1.hour.since) }
- let!(:evidence) { create(:evidence, release: release )}
+ let!(:evidence) { create(:evidence, release: release ) }
it_behaves_like 'does not create a new Evidence record'
end
diff --git a/spec/workers/remove_expired_members_worker_spec.rb b/spec/workers/remove_expired_members_worker_spec.rb
index 8d7d488094f..44b8fa21be4 100644
--- a/spec/workers/remove_expired_members_worker_spec.rb
+++ b/spec/workers/remove_expired_members_worker_spec.rb
@@ -56,10 +56,27 @@ RSpec.describe RemoveExpiredMembersWorker do
expect(Member.find_by(user_id: expired_project_bot.id)).to be_nil
end
- it 'deletes expired project bot' do
- worker.perform
+ context 'when user_destroy_with_limited_execution_time_worker is enabled' do
+ it 'initiates project bot removal' do
+ worker.perform
+
+ expect(
+ Users::GhostUserMigration.where(user: expired_project_bot,
+ initiator_user: nil)
+ ).to be_exists
+ end
+ end
+
+ context 'when user_destroy_with_limited_execution_time_worker is disabled' do
+ before do
+ stub_feature_flags(user_destroy_with_limited_execution_time_worker: false)
+ end
+
+ it 'deletes expired project bot' do
+ worker.perform
- expect(User.exists?(expired_project_bot.id)).to be(false)
+ expect(User.exists?(expired_project_bot.id)).to be(false)
+ end
end
end
diff --git a/spec/workers/repository_check/dispatch_worker_spec.rb b/spec/workers/repository_check/dispatch_worker_spec.rb
index 829abc7d895..146228c0852 100644
--- a/spec/workers/repository_check/dispatch_worker_spec.rb
+++ b/spec/workers/repository_check/dispatch_worker_spec.rb
@@ -22,6 +22,10 @@ RSpec.describe RepositoryCheck::DispatchWorker do
end
it 'dispatches work to RepositoryCheck::BatchWorker' do
+ expect_next_instance_of(Gitlab::GitalyClient::ServerService) do |service|
+ expect(service).to receive(:readiness_check).and_return({ success: true })
+ end
+
expect(RepositoryCheck::BatchWorker).to receive(:perform_async).at_least(:once)
subject.perform
diff --git a/spec/workers/ssh_keys/expired_notification_worker_spec.rb b/spec/workers/ssh_keys/expired_notification_worker_spec.rb
index 26d9460d73e..f93d02e86c0 100644
--- a/spec/workers/ssh_keys/expired_notification_worker_spec.rb
+++ b/spec/workers/ssh_keys/expired_notification_worker_spec.rb
@@ -7,7 +7,6 @@ RSpec.describe SshKeys::ExpiredNotificationWorker, type: :worker do
it 'uses a cronjob queue' do
expect(worker.sidekiq_options_hash).to include(
- 'queue' => 'cronjob:ssh_keys_expired_notification',
'queue_namespace' => :cronjob
)
end
diff --git a/spec/workers/ssh_keys/expiring_soon_notification_worker_spec.rb b/spec/workers/ssh_keys/expiring_soon_notification_worker_spec.rb
index e907d035020..ed6701532a5 100644
--- a/spec/workers/ssh_keys/expiring_soon_notification_worker_spec.rb
+++ b/spec/workers/ssh_keys/expiring_soon_notification_worker_spec.rb
@@ -7,7 +7,6 @@ RSpec.describe SshKeys::ExpiringSoonNotificationWorker, type: :worker do
it 'uses a cronjob queue' do
expect(worker.sidekiq_options_hash).to include(
- 'queue' => 'cronjob:ssh_keys_expiring_soon_notification',
'queue_namespace' => :cronjob
)
end
diff --git a/spec/workers/users/deactivate_dormant_users_worker_spec.rb b/spec/workers/users/deactivate_dormant_users_worker_spec.rb
index 263ca31e0a0..a8318de669b 100644
--- a/spec/workers/users/deactivate_dormant_users_worker_spec.rb
+++ b/spec/workers/users/deactivate_dormant_users_worker_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe Users::DeactivateDormantUsersWorker do
using RSpec::Parameterized::TableSyntax
describe '#perform' do
- let_it_be(:dormant) { create(:user, last_activity_on: User::MINIMUM_INACTIVE_DAYS.days.ago.to_date) }
+ let_it_be(:dormant) { create(:user, last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_date) }
let_it_be(:inactive) { create(:user, last_activity_on: nil, created_at: User::MINIMUM_DAYS_CREATED.days.ago.to_date) }
let_it_be(:inactive_recently_created) { create(:user, last_activity_on: nil, created_at: (User::MINIMUM_DAYS_CREATED - 1).days.ago.to_date) }
@@ -14,7 +14,7 @@ RSpec.describe Users::DeactivateDormantUsersWorker do
it 'does not run for GitLab.com' do
expect(Gitlab).to receive(:com?).and_return(true)
- expect(Gitlab::CurrentSettings).not_to receive(:current_application_settings)
+ # Now makes a call to current settings to determine period of dormancy
worker.perform
@@ -48,7 +48,7 @@ RSpec.describe Users::DeactivateDormantUsersWorker do
end
with_them do
it 'deactivates certain user types' do
- user = create(:user, user_type: user_type, state: :active, last_activity_on: User::MINIMUM_INACTIVE_DAYS.days.ago.to_date)
+ user = create(:user, user_type: user_type, state: :active, last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_date)
worker.perform
@@ -57,8 +57,8 @@ RSpec.describe Users::DeactivateDormantUsersWorker do
end
it 'does not deactivate non-active users' do
- human_user = create(:user, user_type: :human, state: :blocked, last_activity_on: User::MINIMUM_INACTIVE_DAYS.days.ago.to_date)
- service_user = create(:user, user_type: :service_user, state: :blocked, last_activity_on: User::MINIMUM_INACTIVE_DAYS.days.ago.to_date)
+ human_user = create(:user, user_type: :human, state: :blocked, last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_date)
+ service_user = create(:user, user_type: :service_user, state: :blocked, last_activity_on: Gitlab::CurrentSettings.deactivate_dormant_users_period.days.ago.to_date)
worker.perform
diff --git a/spec/workers/users/migrate_records_to_ghost_user_in_batches_worker_spec.rb b/spec/workers/users/migrate_records_to_ghost_user_in_batches_worker_spec.rb
new file mode 100644
index 00000000000..f42033fdb9c
--- /dev/null
+++ b/spec/workers/users/migrate_records_to_ghost_user_in_batches_worker_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Users::MigrateRecordsToGhostUserInBatchesWorker do
+ include ExclusiveLeaseHelpers
+
+ let(:worker) { described_class.new }
+
+ describe '#perform', :clean_gitlab_redis_shared_state do
+ it 'executes service with lease' do
+ lease_key = described_class.name.underscore
+
+ expect_to_obtain_exclusive_lease(lease_key, 'uuid')
+ expect_next_instance_of(Users::MigrateRecordsToGhostUserInBatchesService) do |service|
+ expect(service).to receive(:execute).and_return(true)
+ end
+
+ worker.perform
+ end
+ end
+
+ include_examples 'an idempotent worker' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, namespace: create(:group)) }
+ let_it_be(:issue) { create(:issue, project: project, author: user, last_edited_by: user) }
+
+ subject { worker.perform }
+
+ before do
+ create(:ghost_user_migration, user: user, initiator_user: user)
+ end
+
+ it 'migrates issue to ghost user' do
+ subject
+
+ expect(issue.reload.author).to eq(User.ghost)
+ expect(issue.last_edited_by).to eq(User.ghost)
+ end
+ end
+
+ context 'when user_destroy_with_limited_execution_time_worker is disabled' do
+ before do
+ stub_feature_flags(user_destroy_with_limited_execution_time_worker: false)
+ end
+
+ it 'does not execute the service' do
+ expect(Users::MigrateRecordsToGhostUserInBatchesService).not_to receive(:new)
+
+ worker.perform
+ end
+ end
+end
diff --git a/storybook/config/preview.js b/storybook/config/preview.js
index a55d0d52a0c..6f3b8190742 100644
--- a/storybook/config/preview.js
+++ b/storybook/config/preview.js
@@ -6,13 +6,16 @@ import translateMixin from '~/vue_shared/translate';
const stylesheetsRequireCtx = require.context(
'../../app/assets/stylesheets',
true,
- /(application|application_utilities)\.scss$/,
+ /(application|application_utilities|highlight\/themes\/white)\.scss$/,
);
-window.gon = {};
+window.gon = {
+ user_color_scheme: 'white',
+};
translateMixin(Vue);
stylesheetsRequireCtx('./application.scss');
stylesheetsRequireCtx('./application_utilities.scss');
+stylesheetsRequireCtx('./highlight/themes/white.scss');
export const decorators = [withServer(createMockServer)];
diff --git a/storybook/package.json b/storybook/package.json
index f47b16b2eab..c6e47bf6b5f 100644
--- a/storybook/package.json
+++ b/storybook/package.json
@@ -9,15 +9,18 @@
"vue": "https://gitlab.com/gitlab-org/gitlab/-/issues/340511"
},
"devDependencies": {
- "@storybook/addon-a11y": "^6.2.9",
- "@storybook/addon-actions": "^6.2.9",
- "@storybook/addon-controls": "^6.2.9",
- "@storybook/addon-essentials": "^6.2.9",
- "@storybook/vue": "6.2.9",
+ "@storybook/addon-a11y": "^6.5.10",
+ "@storybook/addon-actions": "^6.5.10",
+ "@storybook/addon-controls": "^6.5.10",
+ "@storybook/addon-essentials": "^6.5.10",
+ "@storybook/vue": "6.5.10",
"graphql-tag": "^2.12.5",
"postcss-loader": "3.0.0",
"sass": "^1.49.9",
"sass-loader": "^7.1.0",
"storybook-mirage": "^0.0.4"
+ },
+ "resolutions": {
+ "chokidar": "^3.5.2"
}
}
diff --git a/storybook/yarn.lock b/storybook/yarn.lock
index 8b925a05ba1..f960b7e1de9 100644
--- a/storybook/yarn.lock
+++ b/storybook/yarn.lock
@@ -2,19 +2,12 @@
# yarn lockfile v1
-"@babel/code-frame@7.10.4":
- version "7.10.4"
- resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
- integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==
- dependencies:
- "@babel/highlight" "^7.10.4"
-
-"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3":
- version "7.12.13"
- resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658"
- integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3":
+ 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.12.13"
+ "@babel/highlight" "^7.18.6"
"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.14.4":
version "7.14.4"
@@ -64,21 +57,21 @@
semver "^6.3.0"
source-map "^0.5.0"
-"@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.14.2", "@babel/generator@^7.14.3":
- version "7.14.3"
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.3.tgz#0c2652d91f7bddab7cccc6ba8157e4f40dcedb91"
- integrity sha512-bn0S6flG/j0xtQdz3hsjJ624h3W0r3llttBMfyHX3YrZ/KtLYr15bjA0FXkgW7FpvrDuTuElXeVjiKlYRpnOFA==
+"@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.14.3", "@babel/generator@^7.18.10":
+ version "7.18.12"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.12.tgz#fa58daa303757bd6f5e4bbca91b342040463d9f4"
+ integrity sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==
dependencies:
- "@babel/types" "^7.14.2"
+ "@babel/types" "^7.18.10"
+ "@jridgewell/gen-mapping" "^0.3.2"
jsesc "^2.5.1"
- source-map "^0.5.0"
-"@babel/helper-annotate-as-pure@^7.10.4", "@babel/helper-annotate-as-pure@^7.12.13":
- version "7.12.13"
- resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz#0f58e86dfc4bb3b1fcd7db806570e177d439b6ab"
- integrity sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw==
+"@babel/helper-annotate-as-pure@^7.10.4", "@babel/helper-annotate-as-pure@^7.12.13", "@babel/helper-annotate-as-pure@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb"
+ integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==
dependencies:
- "@babel/types" "^7.12.13"
+ "@babel/types" "^7.18.6"
"@babel/helper-builder-binary-assignment-operator-visitor@^7.12.13":
version "7.12.13"
@@ -98,17 +91,18 @@
browserslist "^4.16.6"
semver "^6.3.0"
-"@babel/helper-create-class-features-plugin@^7.13.0", "@babel/helper-create-class-features-plugin@^7.14.0", "@babel/helper-create-class-features-plugin@^7.14.2", "@babel/helper-create-class-features-plugin@^7.14.3", "@babel/helper-create-class-features-plugin@^7.14.4":
- version "7.14.4"
- resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.4.tgz#abf888d836a441abee783c75229279748705dc42"
- integrity sha512-idr3pthFlDCpV+p/rMgGLGYIVtazeatrSOQk8YzO2pAepIjQhCN3myeihVg58ax2bbbGK9PUE1reFi7axOYIOw==
+"@babel/helper-create-class-features-plugin@^7.13.0", "@babel/helper-create-class-features-plugin@^7.14.2", "@babel/helper-create-class-features-plugin@^7.14.3", "@babel/helper-create-class-features-plugin@^7.14.4", "@babel/helper-create-class-features-plugin@^7.18.6":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz#d802ee16a64a9e824fcbf0a2ffc92f19d58550ce"
+ integrity sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw==
dependencies:
- "@babel/helper-annotate-as-pure" "^7.12.13"
- "@babel/helper-function-name" "^7.14.2"
- "@babel/helper-member-expression-to-functions" "^7.13.12"
- "@babel/helper-optimise-call-expression" "^7.12.13"
- "@babel/helper-replace-supers" "^7.14.4"
- "@babel/helper-split-export-declaration" "^7.12.13"
+ "@babel/helper-annotate-as-pure" "^7.18.6"
+ "@babel/helper-environment-visitor" "^7.18.9"
+ "@babel/helper-function-name" "^7.18.9"
+ "@babel/helper-member-expression-to-functions" "^7.18.9"
+ "@babel/helper-optimise-call-expression" "^7.18.6"
+ "@babel/helper-replace-supers" "^7.18.9"
+ "@babel/helper-split-export-declaration" "^7.18.6"
"@babel/helper-create-regexp-features-plugin@^7.12.13":
version "7.14.3"
@@ -146,6 +140,11 @@
resolve "^1.14.2"
semver "^6.1.2"
+"@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.12.13":
version "7.13.0"
resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz#17b5c59ff473d9f956f40ef570cf3a76ca12657f"
@@ -153,38 +152,29 @@
dependencies:
"@babel/types" "^7.13.0"
-"@babel/helper-function-name@^7.12.13", "@babel/helper-function-name@^7.14.2":
- version "7.14.2"
- resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz#397688b590760b6ef7725b5f0860c82427ebaac2"
- integrity sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ==
- dependencies:
- "@babel/helper-get-function-arity" "^7.12.13"
- "@babel/template" "^7.12.13"
- "@babel/types" "^7.14.2"
-
-"@babel/helper-get-function-arity@^7.12.13":
- version "7.12.13"
- resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583"
- integrity sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==
+"@babel/helper-function-name@^7.12.13", "@babel/helper-function-name@^7.14.2", "@babel/helper-function-name@^7.18.9":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0"
+ integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==
dependencies:
- "@babel/types" "^7.12.13"
+ "@babel/template" "^7.18.6"
+ "@babel/types" "^7.18.9"
-"@babel/helper-hoist-variables@^7.13.0":
- version "7.13.16"
- resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.16.tgz#1b1651249e94b51f8f0d33439843e33e39775b30"
- integrity sha512-1eMtTrXtrwscjcAeO4BVK+vvkxaLJSPFz1w1KLawz6HLNi9bPFGBNwwDyVfiu1Tv/vRRFYfoGaKhmAQPGPn5Wg==
+"@babel/helper-hoist-variables@^7.13.0", "@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/traverse" "^7.13.15"
- "@babel/types" "^7.13.16"
+ "@babel/types" "^7.18.6"
-"@babel/helper-member-expression-to-functions@^7.13.12":
- version "7.13.12"
- resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.12.tgz#dfe368f26d426a07299d8d6513821768216e6d72"
- integrity sha512-48ql1CLL59aKbU94Y88Xgb2VFy7a95ykGRbJJaaVv+LX5U8wFpLfiGXJJGUozsmA1oEh/o5Bp60Voq7ACyA/Sw==
+"@babel/helper-member-expression-to-functions@^7.18.9":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815"
+ integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==
dependencies:
- "@babel/types" "^7.13.12"
+ "@babel/types" "^7.18.9"
-"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.13.12":
+"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.13.12":
version "7.13.12"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz#c6a369a6f3621cb25da014078684da9196b61977"
integrity sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==
@@ -205,22 +195,22 @@
"@babel/traverse" "^7.14.2"
"@babel/types" "^7.14.2"
-"@babel/helper-optimise-call-expression@^7.12.13":
- version "7.12.13"
- resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea"
- integrity sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA==
+"@babel/helper-optimise-call-expression@^7.12.13", "@babel/helper-optimise-call-expression@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe"
+ integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==
dependencies:
- "@babel/types" "^7.12.13"
+ "@babel/types" "^7.18.6"
"@babel/helper-plugin-utils@7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375"
integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==
-"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
- version "7.13.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz#806526ce125aed03373bc416a828321e3a6a33af"
- integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==
+"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f"
+ integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==
"@babel/helper-remap-async-to-generator@^7.13.0":
version "7.13.0"
@@ -231,15 +221,16 @@
"@babel/helper-wrap-function" "^7.13.0"
"@babel/types" "^7.13.0"
-"@babel/helper-replace-supers@^7.12.13", "@babel/helper-replace-supers@^7.13.12", "@babel/helper-replace-supers@^7.14.4":
- version "7.14.4"
- resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.4.tgz#b2ab16875deecfff3ddfcd539bc315f72998d836"
- integrity sha512-zZ7uHCWlxfEAAOVDYQpEf/uyi1dmeC7fX4nCf2iz9drnCwi1zvwXL3HwWWNXUQEJ1k23yVn3VbddiI9iJEXaTQ==
+"@babel/helper-replace-supers@^7.12.13", "@babel/helper-replace-supers@^7.13.12", "@babel/helper-replace-supers@^7.14.4", "@babel/helper-replace-supers@^7.18.9":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz#1092e002feca980fbbb0bd4d51b74a65c6a500e6"
+ integrity sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==
dependencies:
- "@babel/helper-member-expression-to-functions" "^7.13.12"
- "@babel/helper-optimise-call-expression" "^7.12.13"
- "@babel/traverse" "^7.14.2"
- "@babel/types" "^7.14.4"
+ "@babel/helper-environment-visitor" "^7.18.9"
+ "@babel/helper-member-expression-to-functions" "^7.18.9"
+ "@babel/helper-optimise-call-expression" "^7.18.6"
+ "@babel/traverse" "^7.18.9"
+ "@babel/types" "^7.18.9"
"@babel/helper-simple-access@^7.13.12":
version "7.13.12"
@@ -255,17 +246,22 @@
dependencies:
"@babel/types" "^7.12.1"
-"@babel/helper-split-export-declaration@^7.12.13":
- version "7.12.13"
- resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz#e9430be00baf3e88b0e13e6f9d4eaf2136372b05"
- integrity sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==
+"@babel/helper-split-export-declaration@^7.12.13", "@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.12.13"
+ "@babel/types" "^7.18.6"
-"@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.14.0":
- version "7.14.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288"
- integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==
+"@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.12.11", "@babel/helper-validator-identifier@^7.14.0", "@babel/helper-validator-identifier@^7.18.6":
+ 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.12.17":
version "7.12.17"
@@ -291,19 +287,19 @@
"@babel/traverse" "^7.14.0"
"@babel/types" "^7.14.0"
-"@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13":
- version "7.14.0"
- resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.0.tgz#3197e375711ef6bf834e67d0daec88e4f46113cf"
- integrity sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==
+"@babel/highlight@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
+ integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==
dependencies:
- "@babel/helper-validator-identifier" "^7.14.0"
+ "@babel/helper-validator-identifier" "^7.18.6"
chalk "^2.0.0"
js-tokens "^4.0.0"
-"@babel/parser@^7.1.6", "@babel/parser@^7.12.0", "@babel/parser@^7.12.11", "@babel/parser@^7.12.13", "@babel/parser@^7.12.7", "@babel/parser@^7.13.12", "@babel/parser@^7.13.9", "@babel/parser@^7.14.2", "@babel/parser@^7.14.3", "@babel/parser@^7.6.0", "@babel/parser@^7.9.6":
- version "7.14.4"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.4.tgz#a5c560d6db6cd8e6ed342368dea8039232cbab18"
- integrity sha512-ArliyUsWDUqEGfWcmzpGUzNfLxTdTp6WU4IuP6QFSp9gGfWS6boxFCkJSJ/L4+RG8z/FnIU3WxCk6hPL9SSWeA==
+"@babel/parser@^7.1.6", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.13.12", "@babel/parser@^7.14.3", "@babel/parser@^7.16.4", "@babel/parser@^7.18.10", "@babel/parser@^7.18.11", "@babel/parser@^7.6.0", "@babel/parser@^7.9.6":
+ version "7.18.11"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9"
+ integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.13.12":
version "7.13.12"
@@ -450,15 +446,15 @@
"@babel/helper-create-class-features-plugin" "^7.13.0"
"@babel/helper-plugin-utils" "^7.13.0"
-"@babel/plugin-proposal-private-property-in-object@^7.14.0":
- version "7.14.0"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.14.0.tgz#b1a1f2030586b9d3489cc26179d2eb5883277636"
- integrity sha512-59ANdmEwwRUkLjB7CRtwJxxwtjESw+X2IePItA+RGQh+oy5RmpCh/EvVVvh5XQc3yxsm5gtv0+i9oBZhaDNVTg==
+"@babel/plugin-proposal-private-property-in-object@^7.12.1", "@babel/plugin-proposal-private-property-in-object@^7.14.0":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz#a64137b232f0aca3733a67eb1a144c192389c503"
+ integrity sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==
dependencies:
- "@babel/helper-annotate-as-pure" "^7.12.13"
- "@babel/helper-create-class-features-plugin" "^7.14.0"
- "@babel/helper-plugin-utils" "^7.13.0"
- "@babel/plugin-syntax-private-property-in-object" "^7.14.0"
+ "@babel/helper-annotate-as-pure" "^7.18.6"
+ "@babel/helper-create-class-features-plugin" "^7.18.6"
+ "@babel/helper-plugin-utils" "^7.18.6"
+ "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
"@babel/plugin-proposal-unicode-property-regex@^7.12.13", "@babel/plugin-proposal-unicode-property-regex@^7.4.4":
version "7.12.13"
@@ -587,12 +583,12 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
-"@babel/plugin-syntax-private-property-in-object@^7.14.0":
- version "7.14.0"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.0.tgz#762a4babec61176fec6c88480dec40372b140c0b"
- integrity sha512-bda3xF8wGl5/5btF794utNOL0Jw+9jE5C1sLZcoK7c4uonE/y3iQiyG+KbkF3WBV/paX58VCpjhxLPkdj5Fe4w==
+"@babel/plugin-syntax-private-property-in-object@^7.14.0", "@babel/plugin-syntax-private-property-in-object@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad"
+ integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==
dependencies:
- "@babel/helper-plugin-utils" "^7.13.0"
+ "@babel/helper-plugin-utils" "^7.14.5"
"@babel/plugin-syntax-top-level-await@^7.12.13":
version "7.12.13"
@@ -1037,49 +1033,47 @@
pirates "^4.0.0"
source-map-support "^0.5.16"
-"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4":
- version "7.14.0"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6"
- integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==
+"@babel/runtime@^7.0.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.8", "@babel/runtime@^7.5.0", "@babel/runtime@^7.8.4":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a"
+ integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==
dependencies:
regenerator-runtime "^0.13.4"
-"@babel/template@^7.12.13", "@babel/template@^7.12.7":
- version "7.12.13"
- resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327"
- integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==
- dependencies:
- "@babel/code-frame" "^7.12.13"
- "@babel/parser" "^7.12.13"
- "@babel/types" "^7.12.13"
-
-"@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.13.15", "@babel/traverse@^7.14.0", "@babel/traverse@^7.14.2":
- version "7.14.2"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.2.tgz#9201a8d912723a831c2679c7ebbf2fe1416d765b"
- integrity sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA==
- dependencies:
- "@babel/code-frame" "^7.12.13"
- "@babel/generator" "^7.14.2"
- "@babel/helper-function-name" "^7.14.2"
- "@babel/helper-split-export-declaration" "^7.12.13"
- "@babel/parser" "^7.14.2"
- "@babel/types" "^7.14.2"
+"@babel/template@^7.12.13", "@babel/template@^7.12.7", "@babel/template@^7.18.6":
+ 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.18.6"
+ "@babel/parser" "^7.18.10"
+ "@babel/types" "^7.18.10"
+
+"@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.14.0", "@babel/traverse@^7.14.2", "@babel/traverse@^7.18.9":
+ version "7.18.11"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.11.tgz#3d51f2afbd83ecf9912bcbb5c4d94e3d2ddaa16f"
+ integrity sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==
+ dependencies:
+ "@babel/code-frame" "^7.18.6"
+ "@babel/generator" "^7.18.10"
+ "@babel/helper-environment-visitor" "^7.18.9"
+ "@babel/helper-function-name" "^7.18.9"
+ "@babel/helper-hoist-variables" "^7.18.6"
+ "@babel/helper-split-export-declaration" "^7.18.6"
+ "@babel/parser" "^7.18.11"
+ "@babel/types" "^7.18.10"
debug "^4.1.0"
globals "^11.1.0"
-"@babel/types@^7.12.0", "@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.12.7", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.13.16", "@babel/types@^7.14.0", "@babel/types@^7.14.2", "@babel/types@^7.14.4", "@babel/types@^7.4.4", "@babel/types@^7.6.1", "@babel/types@^7.9.6":
- version "7.14.4"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.4.tgz#bfd6980108168593b38b3eb48a24aa026b919bc0"
- integrity sha512-lCj4aIs0xUefJFQnwwQv2Bxg7Omd6bgquZ6LGC+gGMh6/s5qDVfjuCMlDmYQ15SLsWHd9n+X3E75lKIhl5Lkiw==
+"@babel/types@^7.12.1", "@babel/types@^7.12.11", "@babel/types@^7.12.13", "@babel/types@^7.12.7", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.14.0", "@babel/types@^7.14.2", "@babel/types@^7.14.4", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.8", "@babel/types@^7.18.9", "@babel/types@^7.4.4", "@babel/types@^7.6.1", "@babel/types@^7.9.6":
+ version "7.18.10"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.10.tgz#4908e81b6b339ca7c6b7a555a5fc29446f26dde6"
+ integrity sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==
dependencies:
- "@babel/helper-validator-identifier" "^7.14.0"
+ "@babel/helper-string-parser" "^7.18.10"
+ "@babel/helper-validator-identifier" "^7.18.6"
to-fast-properties "^2.0.0"
-"@base2/pretty-print-object@1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.0.tgz#860ce718b0b73f4009e153541faff2cb6b85d047"
- integrity sha512-4Th98KlMHr5+JkxfcoDT//6vY8vM+iSPrLNpHhRyLx2CFYi8e2RfqPLdpbnpo0Q5lQC5hNB79yes07zb02fvCw==
-
"@cnakazawa/watch@^1.0.3":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a"
@@ -1088,107 +1082,15 @@
exec-sh "^0.3.2"
minimist "^1.2.0"
-"@emotion/cache@^10.0.27":
- version "10.0.29"
- resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-10.0.29.tgz#87e7e64f412c060102d589fe7c6dc042e6f9d1e0"
- integrity sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ==
- dependencies:
- "@emotion/sheet" "0.9.4"
- "@emotion/stylis" "0.8.5"
- "@emotion/utils" "0.11.3"
- "@emotion/weak-memoize" "0.2.5"
-
-"@emotion/core@^10.1.1":
- version "10.1.1"
- resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.1.1.tgz#c956c1365f2f2481960064bcb8c4732e5fb612c3"
- integrity sha512-ZMLG6qpXR8x031NXD8HJqugy/AZSkAuMxxqB46pmAR7ze47MhNJ56cdoX243QPZdGctrdfo+s08yZTiwaUcRKA==
- dependencies:
- "@babel/runtime" "^7.5.5"
- "@emotion/cache" "^10.0.27"
- "@emotion/css" "^10.0.27"
- "@emotion/serialize" "^0.11.15"
- "@emotion/sheet" "0.9.4"
- "@emotion/utils" "0.11.3"
-
-"@emotion/css@^10.0.27":
- version "10.0.27"
- resolved "https://registry.yarnpkg.com/@emotion/css/-/css-10.0.27.tgz#3a7458198fbbebb53b01b2b87f64e5e21241e14c"
- integrity sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw==
- dependencies:
- "@emotion/serialize" "^0.11.15"
- "@emotion/utils" "0.11.3"
- babel-plugin-emotion "^10.0.27"
-
-"@emotion/hash@0.8.0":
- version "0.8.0"
- resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
- integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
-
-"@emotion/is-prop-valid@0.8.8", "@emotion/is-prop-valid@^0.8.6":
- version "0.8.8"
- resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a"
- integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==
- dependencies:
- "@emotion/memoize" "0.7.4"
-
-"@emotion/memoize@0.7.4":
- version "0.7.4"
- resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb"
- integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
-
-"@emotion/serialize@^0.11.15", "@emotion/serialize@^0.11.16":
- version "0.11.16"
- resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-0.11.16.tgz#dee05f9e96ad2fb25a5206b6d759b2d1ed3379ad"
- integrity sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg==
- dependencies:
- "@emotion/hash" "0.8.0"
- "@emotion/memoize" "0.7.4"
- "@emotion/unitless" "0.7.5"
- "@emotion/utils" "0.11.3"
- csstype "^2.5.7"
-
-"@emotion/sheet@0.9.4":
- version "0.9.4"
- resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-0.9.4.tgz#894374bea39ec30f489bbfc3438192b9774d32e5"
- integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==
-
-"@emotion/styled-base@^10.0.27":
- version "10.0.31"
- resolved "https://registry.yarnpkg.com/@emotion/styled-base/-/styled-base-10.0.31.tgz#940957ee0aa15c6974adc7d494ff19765a2f742a"
- integrity sha512-wTOE1NcXmqMWlyrtwdkqg87Mu6Rj1MaukEoEmEkHirO5IoHDJ8LgCQL4MjJODgxWxXibGR3opGp1p7YvkNEdXQ==
- dependencies:
- "@babel/runtime" "^7.5.5"
- "@emotion/is-prop-valid" "0.8.8"
- "@emotion/serialize" "^0.11.15"
- "@emotion/utils" "0.11.3"
-
-"@emotion/styled@^10.0.27":
- version "10.0.27"
- resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-10.0.27.tgz#12cb67e91f7ad7431e1875b1d83a94b814133eaf"
- integrity sha512-iK/8Sh7+NLJzyp9a5+vIQIXTYxfT4yB/OJbjzQanB2RZpvmzBQOHZWhpAMZWYEKRNNbsD6WfBw5sVWkb6WzS/Q==
- dependencies:
- "@emotion/styled-base" "^10.0.27"
- babel-plugin-emotion "^10.0.27"
-
-"@emotion/stylis@0.8.5":
- version "0.8.5"
- resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04"
- integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==
-
-"@emotion/unitless@0.7.5":
- version "0.7.5"
- resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
- integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
-
-"@emotion/utils@0.11.3":
- version "0.11.3"
- resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-0.11.3.tgz#a759863867befa7e583400d322652a3f44820924"
- integrity sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw==
-
-"@emotion/weak-memoize@0.2.5":
- version "0.2.5"
- resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
- integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
+"@colors/colors@1.5.0":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
+ integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
+
+"@discoveryjs/json-ext@^0.5.3":
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
+ integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
"@istanbuljs/load-nyc-config@^1.0.0":
version "1.1.0"
@@ -1238,16 +1140,47 @@
"@types/yargs" "^15.0.0"
chalk "^4.0.0"
-"@mdx-js/loader@^1.6.22":
- version "1.6.22"
- resolved "https://registry.yarnpkg.com/@mdx-js/loader/-/loader-1.6.22.tgz#d9e8fe7f8185ff13c9c8639c048b123e30d322c4"
- integrity sha512-9CjGwy595NaxAYp0hF9B/A0lH6C8Rms97e2JS9d3jVUtILn6pT5i5IV965ra3lIWc7Rs1GG1tBdVF7dCowYe6Q==
+"@jridgewell/gen-mapping@^0.3.0", "@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:
- "@mdx-js/mdx" "1.6.22"
- "@mdx-js/react" "1.6.22"
- loader-utils "2.0.0"
+ "@jridgewell/set-array" "^1.0.1"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+ "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/resolve-uri@^3.0.3":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
+ integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
+
+"@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==
-"@mdx-js/mdx@1.6.22", "@mdx-js/mdx@^1.6.22":
+"@jridgewell/source-map@^0.3.2":
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb"
+ integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==
+ dependencies:
+ "@jridgewell/gen-mapping" "^0.3.0"
+ "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/sourcemap-codec@^1.4.10":
+ version "1.4.14"
+ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
+ integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
+
+"@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"
+
+"@mdx-js/mdx@^1.6.22":
version "1.6.22"
resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba"
integrity sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==
@@ -1272,7 +1205,7 @@
unist-builder "2.0.3"
unist-util-visit "2.0.3"
-"@mdx-js/react@1.6.22", "@mdx-js/react@^1.6.22":
+"@mdx-js/react@^1.6.22":
version "1.6.22"
resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-1.6.22.tgz#ae09b4744fddc74714ee9f9d6f17a66e77c43573"
integrity sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==
@@ -1324,77 +1257,65 @@
mkdirp "^1.0.4"
rimraf "^3.0.2"
-"@popperjs/core@^2.5.4", "@popperjs/core@^2.6.0":
- version "2.9.2"
- resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.9.2.tgz#adea7b6953cbb34651766b0548468e743c6a2353"
- integrity sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==
-
-"@reach/router@^1.3.4":
- version "1.3.4"
- resolved "https://registry.yarnpkg.com/@reach/router/-/router-1.3.4.tgz#d2574b19370a70c80480ed91f3da840136d10f8c"
- integrity sha512-+mtn9wjlB9NN2CNnnC/BRYtwdKBfSyyasPYraNAyvaV1occr/5NnB4CVzjEZipNHwYebQwcndGUmpFzxAUoqSA==
- dependencies:
- create-react-context "0.3.0"
- invariant "^2.2.3"
- prop-types "^15.6.1"
- react-lifecycles-compat "^3.0.4"
-
-"@storybook/addon-a11y@^6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/addon-a11y/-/addon-a11y-6.2.9.tgz#8386d73343db03c15d07f6bf927a4030d441d1ff"
- integrity sha512-wo7nFpEqEeiHDsRKnhqe2gIHZ9Z7/Aefw570kBgReU5tKlmrb5rFAfTVBWGBZlLHWeJMsFsRsWrWrmkf1B52OQ==
- dependencies:
- "@storybook/addons" "6.2.9"
- "@storybook/api" "6.2.9"
- "@storybook/channels" "6.2.9"
- "@storybook/client-api" "6.2.9"
- "@storybook/client-logger" "6.2.9"
- "@storybook/components" "6.2.9"
- "@storybook/core-events" "6.2.9"
- "@storybook/theming" "6.2.9"
- axe-core "^4.1.1"
+"@storybook/addon-a11y@^6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/addon-a11y/-/addon-a11y-6.5.10.tgz#a907cbd3f1889ba367828435a58ecc9dac42f6ba"
+ integrity sha512-BnDbLg7YEAX1aEyiB+gDFYMXIbiSFH/M0CdwPCq7T7o8cqULKOHtQkndMja1soMxsqHAVH8AGvVVZ8VlaxaJ3Q==
+ dependencies:
+ "@storybook/addons" "6.5.10"
+ "@storybook/api" "6.5.10"
+ "@storybook/channels" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/components" "6.5.10"
+ "@storybook/core-events" "6.5.10"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
+ "@storybook/theming" "6.5.10"
+ axe-core "^4.2.0"
core-js "^3.8.2"
global "^4.4.0"
- lodash "^4.17.20"
+ lodash "^4.17.21"
react-sizeme "^3.0.1"
regenerator-runtime "^0.13.7"
ts-dedent "^2.0.0"
util-deprecate "^1.0.2"
-"@storybook/addon-actions@6.2.9", "@storybook/addon-actions@^6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.2.9.tgz#688413ac77410690755a5da3c277bfa0ff1a10b0"
- integrity sha512-CkUYSMt+fvuHfWvtDzlhhaeQBCWlUo99xdL88JTsTml05P43bIHZNIRv2QJ8DwhHuxdIPeHKLmz9y/ymOagOnw==
- dependencies:
- "@storybook/addons" "6.2.9"
- "@storybook/api" "6.2.9"
- "@storybook/client-api" "6.2.9"
- "@storybook/components" "6.2.9"
- "@storybook/core-events" "6.2.9"
- "@storybook/theming" "6.2.9"
+"@storybook/addon-actions@6.5.10", "@storybook/addon-actions@^6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.5.10.tgz#83ec807a899e0412cf98037647f256c45cc32bf5"
+ integrity sha512-vpCnEu81fmtYzOf0QsRYoDuf9wXgVVl2VysE1dWRebRhIUDU0JurrthTnw322e38D4FzaoNGqZE7wnBYBohzZA==
+ dependencies:
+ "@storybook/addons" "6.5.10"
+ "@storybook/api" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/components" "6.5.10"
+ "@storybook/core-events" "6.5.10"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
+ "@storybook/theming" "6.5.10"
core-js "^3.8.2"
fast-deep-equal "^3.1.3"
global "^4.4.0"
- lodash "^4.17.20"
- polished "^4.0.5"
+ lodash "^4.17.21"
+ polished "^4.2.2"
prop-types "^15.7.2"
react-inspector "^5.1.0"
regenerator-runtime "^0.13.7"
+ telejson "^6.0.8"
ts-dedent "^2.0.0"
util-deprecate "^1.0.2"
uuid-browser "^3.1.0"
-"@storybook/addon-backgrounds@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-6.2.9.tgz#4f75aa58b262f461d9f8713d65d11407f4e53537"
- integrity sha512-oPSdeoUuvaXshY5sQRagbYXpr6ZEVUuLhGYBnZTlvm19QMeNCXQE+rdlgzcgyafq4mc1FI/udE2MpJ1dhfS6pQ==
- dependencies:
- "@storybook/addons" "6.2.9"
- "@storybook/api" "6.2.9"
- "@storybook/client-logger" "6.2.9"
- "@storybook/components" "6.2.9"
- "@storybook/core-events" "6.2.9"
- "@storybook/theming" "6.2.9"
+"@storybook/addon-backgrounds@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-6.5.10.tgz#9ab2d2165fe35d265d9d6013fc174fa8528a272f"
+ integrity sha512-5uzQda3dh891h7BL8e9Ymk7BI+QgkkzDJXuA4mHjOXfIiD3S3efhJI8amXuBC2ZpIr6zmVit0MqZVyoVve46cQ==
+ dependencies:
+ "@storybook/addons" "6.5.10"
+ "@storybook/api" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/components" "6.5.10"
+ "@storybook/core-events" "6.5.10"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
+ "@storybook/theming" "6.5.10"
core-js "^3.8.2"
global "^4.4.0"
memoizerific "^1.11.3"
@@ -1402,341 +1323,343 @@
ts-dedent "^2.0.0"
util-deprecate "^1.0.2"
-"@storybook/addon-controls@6.2.9", "@storybook/addon-controls@^6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-6.2.9.tgz#eeec14b2946f1fb5326115f2205ed72c7f44ccea"
- integrity sha512-NvXAJ7I5U4CLxv4wL3/Ne9rehJlgnSmQlLIG/z6dg5zm7JIb48LT4IY6GzjlUP5LkjmO9KJ8gJC249uRt2iPBQ==
- dependencies:
- "@storybook/addons" "6.2.9"
- "@storybook/api" "6.2.9"
- "@storybook/client-api" "6.2.9"
- "@storybook/components" "6.2.9"
- "@storybook/node-logger" "6.2.9"
- "@storybook/theming" "6.2.9"
+"@storybook/addon-controls@6.5.10", "@storybook/addon-controls@^6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-6.5.10.tgz#275ddcd0f4dc1a107777b425417a8f252f52a91e"
+ integrity sha512-lC2y3XcolmQAJwFurIyGrynAHPWmfNtTCdu3rQBTVGwyxCoNwdOOeC2jV0BRqX2+CW6OHzJr9frNWXPSaZ8c4w==
+ dependencies:
+ "@storybook/addons" "6.5.10"
+ "@storybook/api" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/components" "6.5.10"
+ "@storybook/core-common" "6.5.10"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
+ "@storybook/node-logger" "6.5.10"
+ "@storybook/store" "6.5.10"
+ "@storybook/theming" "6.5.10"
core-js "^3.8.2"
+ lodash "^4.17.21"
ts-dedent "^2.0.0"
-"@storybook/addon-docs@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-6.2.9.tgz#61271e54ff4ea490409e4873ed022e62577366c1"
- integrity sha512-qOtwgiqI3LMqT0eXYNV6ykp7qSu0LQGeXxy3wOBGuDDqAizfgnAjomYEWGFcyKp5ahV7HCRCjxbixAklFPUmyw==
+"@storybook/addon-docs@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-6.5.10.tgz#dde18b5659e8033651e139a231a7f69306433b92"
+ integrity sha512-1kgjo3f0vL6GN8fTwLL05M/q/kDdzvuqwhxPY/v5hubFb3aQZGr2yk9pRBaLAbs4bez0yG0ASXcwhYnrEZUppg==
dependencies:
- "@babel/core" "^7.12.10"
- "@babel/generator" "^7.12.11"
- "@babel/parser" "^7.12.11"
"@babel/plugin-transform-react-jsx" "^7.12.12"
"@babel/preset-env" "^7.12.11"
"@jest/transform" "^26.6.2"
- "@mdx-js/loader" "^1.6.22"
- "@mdx-js/mdx" "^1.6.22"
"@mdx-js/react" "^1.6.22"
- "@storybook/addons" "6.2.9"
- "@storybook/api" "6.2.9"
- "@storybook/builder-webpack4" "6.2.9"
- "@storybook/client-api" "6.2.9"
- "@storybook/client-logger" "6.2.9"
- "@storybook/components" "6.2.9"
- "@storybook/core" "6.2.9"
- "@storybook/core-events" "6.2.9"
- "@storybook/csf" "0.0.1"
- "@storybook/node-logger" "6.2.9"
- "@storybook/postinstall" "6.2.9"
- "@storybook/source-loader" "6.2.9"
- "@storybook/theming" "6.2.9"
- acorn "^7.4.1"
- acorn-jsx "^5.3.1"
- acorn-walk "^7.2.0"
+ "@storybook/addons" "6.5.10"
+ "@storybook/api" "6.5.10"
+ "@storybook/components" "6.5.10"
+ "@storybook/core-common" "6.5.10"
+ "@storybook/core-events" "6.5.10"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
+ "@storybook/docs-tools" "6.5.10"
+ "@storybook/mdx1-csf" "^0.0.1"
+ "@storybook/node-logger" "6.5.10"
+ "@storybook/postinstall" "6.5.10"
+ "@storybook/preview-web" "6.5.10"
+ "@storybook/source-loader" "6.5.10"
+ "@storybook/store" "6.5.10"
+ "@storybook/theming" "6.5.10"
+ babel-loader "^8.0.0"
core-js "^3.8.2"
- doctrine "^3.0.0"
- escodegen "^2.0.0"
fast-deep-equal "^3.1.3"
global "^4.4.0"
- html-tags "^3.1.0"
- js-string-escape "^1.0.1"
- loader-utils "^2.0.0"
- lodash "^4.17.20"
- prettier "~2.2.1"
- prop-types "^15.7.2"
- react-element-to-jsx-string "^14.3.2"
+ lodash "^4.17.21"
regenerator-runtime "^0.13.7"
remark-external-links "^8.0.0"
remark-slug "^6.0.0"
ts-dedent "^2.0.0"
util-deprecate "^1.0.2"
-"@storybook/addon-essentials@^6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-6.2.9.tgz#cd946b024804c4d9bfec4e232b74ffdf936b25ef"
- integrity sha512-zXsV4e1TCkHyDwi7hew4h9eJfDW++f2BNKzTif+DAcjPUVFDp7yC17gLjS5IhOjcQk+db0UUlFSx/OrTxhy7Xw==
- dependencies:
- "@storybook/addon-actions" "6.2.9"
- "@storybook/addon-backgrounds" "6.2.9"
- "@storybook/addon-controls" "6.2.9"
- "@storybook/addon-docs" "6.2.9"
- "@storybook/addon-toolbars" "6.2.9"
- "@storybook/addon-viewport" "6.2.9"
- "@storybook/addons" "6.2.9"
- "@storybook/api" "6.2.9"
- "@storybook/node-logger" "6.2.9"
+"@storybook/addon-essentials@^6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-6.5.10.tgz#d56f0f972e3bd5eae6c79b2126f510c5c020b62d"
+ integrity sha512-PT2aiR4vgAyB0pl3HNBUa4/a7NDRxASxAazz7zt9ZDirkipDKfxwdcLeRoJzwSngVDWEhuz5/paN5x4eNp4Hww==
+ dependencies:
+ "@storybook/addon-actions" "6.5.10"
+ "@storybook/addon-backgrounds" "6.5.10"
+ "@storybook/addon-controls" "6.5.10"
+ "@storybook/addon-docs" "6.5.10"
+ "@storybook/addon-measure" "6.5.10"
+ "@storybook/addon-outline" "6.5.10"
+ "@storybook/addon-toolbars" "6.5.10"
+ "@storybook/addon-viewport" "6.5.10"
+ "@storybook/addons" "6.5.10"
+ "@storybook/api" "6.5.10"
+ "@storybook/core-common" "6.5.10"
+ "@storybook/node-logger" "6.5.10"
+ core-js "^3.8.2"
+ regenerator-runtime "^0.13.7"
+ ts-dedent "^2.0.0"
+
+"@storybook/addon-measure@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/addon-measure/-/addon-measure-6.5.10.tgz#afac72a15d927f9f2119e2218017d757a8c8c6a4"
+ integrity sha512-ss7L1H5K5hXygDIoVwj+QyVXbve5V67x7CofLiLCgQYuJzfO16+sPGjiTGWMpTb4ijox2uKWnTkpilt5bCjXgw==
+ dependencies:
+ "@storybook/addons" "6.5.10"
+ "@storybook/api" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/components" "6.5.10"
+ "@storybook/core-events" "6.5.10"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
+ core-js "^3.8.2"
+ global "^4.4.0"
+
+"@storybook/addon-outline@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/addon-outline/-/addon-outline-6.5.10.tgz#a49164697344de1bd11d35a5ce21e59afc0dd19c"
+ integrity sha512-AjdaeQ+/iBKmGrAqRW4niwMB6AkgGnYmSzVs5Cf6F/Sb4Dp+vzgLNOwLABD9qs8Ri8dvHl5J4QpVwQKUhYZaOQ==
+ dependencies:
+ "@storybook/addons" "6.5.10"
+ "@storybook/api" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/components" "6.5.10"
+ "@storybook/core-events" "6.5.10"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
core-js "^3.8.2"
+ global "^4.4.0"
regenerator-runtime "^0.13.7"
ts-dedent "^2.0.0"
-"@storybook/addon-toolbars@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-6.2.9.tgz#29f2f4cba0bfbcff9424958eb573e537f7e2d5af"
- integrity sha512-4WjIofN5npBPNZ8v1UhzPeATB9RnAWRH/y1AVS1HB+zl6Ku92o7aOMqVxs8zR1oSSmtkHh/rcUcpATFKjuofdw==
+"@storybook/addon-toolbars@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-6.5.10.tgz#750e6c7fa50a54dac7fe5df7b7c239fb02a4456c"
+ integrity sha512-S0Ljc6Wv+bPbx2e0iTveJ6bBDqjsemu+FZD4qDLsHreoI7DAcqyrF5Def1l8xNohixIVpx8dQpYXRtyzNlXekg==
dependencies:
- "@storybook/addons" "6.2.9"
- "@storybook/api" "6.2.9"
- "@storybook/client-api" "6.2.9"
- "@storybook/components" "6.2.9"
+ "@storybook/addons" "6.5.10"
+ "@storybook/api" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/components" "6.5.10"
+ "@storybook/theming" "6.5.10"
core-js "^3.8.2"
+ regenerator-runtime "^0.13.7"
-"@storybook/addon-viewport@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-6.2.9.tgz#e380de567cea6c24c4e933efa009e80428d5b49e"
- integrity sha512-IK2mu5njmfcAT967SJtBOY2B6NPMikySZga9QuaLdSpQxPd3vXKNMVG1CjnduMLeDaAoUlvlJISeEPbYGuE+1A==
- dependencies:
- "@storybook/addons" "6.2.9"
- "@storybook/api" "6.2.9"
- "@storybook/client-logger" "6.2.9"
- "@storybook/components" "6.2.9"
- "@storybook/core-events" "6.2.9"
- "@storybook/theming" "6.2.9"
+"@storybook/addon-viewport@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-6.5.10.tgz#4c6151d7e8177b07df8dcb4c61e842dac949215b"
+ integrity sha512-RFMd+4kZljyuJjR9OJ2bFXHrSG7VTi5FDZYWEU+4W1sBxzC+JhnVnUP+HJH3gUxEFIRQC5neRzwWRE9RUUoALQ==
+ dependencies:
+ "@storybook/addons" "6.5.10"
+ "@storybook/api" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/components" "6.5.10"
+ "@storybook/core-events" "6.5.10"
+ "@storybook/theming" "6.5.10"
core-js "^3.8.2"
global "^4.4.0"
memoizerific "^1.11.3"
prop-types "^15.7.2"
regenerator-runtime "^0.13.7"
-"@storybook/addons@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.2.9.tgz#b7ba2b9f0e15b852c7d6b57d04fb0a493c57477c"
- integrity sha512-GnmEKbJwiN1jncN9NSA8CuR1i2XAlasPcl/Zn0jkfV9WitQeczVcJCPw86SGH84AD+tTBCyF2i9UC0KaOV1YBQ==
- dependencies:
- "@storybook/api" "6.2.9"
- "@storybook/channels" "6.2.9"
- "@storybook/client-logger" "6.2.9"
- "@storybook/core-events" "6.2.9"
- "@storybook/router" "6.2.9"
- "@storybook/theming" "6.2.9"
+"@storybook/addons@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.5.10.tgz#bff2f8fb8453e9df04fa6dbc41341fd05f4cdeba"
+ integrity sha512-VD4tBCQ23PkSeDoxuHcKy0RfhIs3oMYjBacOZx7d0bvOzK9WjPyvE2ysDAh7r/ceqnwmWHAScIpE+I1RU7gl+g==
+ dependencies:
+ "@storybook/api" "6.5.10"
+ "@storybook/channels" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/core-events" "6.5.10"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
+ "@storybook/router" "6.5.10"
+ "@storybook/theming" "6.5.10"
+ "@types/webpack-env" "^1.16.0"
core-js "^3.8.2"
global "^4.4.0"
regenerator-runtime "^0.13.7"
-"@storybook/api@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.2.9.tgz#a9b46569192ad5d8da6435c9d63dc4b0c8463b51"
- integrity sha512-okkA3HAScE9tGnYBrjTOcgzT+L1lRHNoEh3ZfGgh1u/XNEyHGNkj4grvkd6nX7BzRcYQ/l2VkcKCqmOjUnSkVQ==
- dependencies:
- "@reach/router" "^1.3.4"
- "@storybook/channels" "6.2.9"
- "@storybook/client-logger" "6.2.9"
- "@storybook/core-events" "6.2.9"
- "@storybook/csf" "0.0.1"
- "@storybook/router" "6.2.9"
+"@storybook/api@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.5.10.tgz#215623844648f0da2ac646fdcdd1345c2e1a8490"
+ integrity sha512-AkmgSPNEGdKp4oZA4KQ+RJsacw7GwfvjsVDnCkcXqS9zmSr/RNL0fhpcd60KKkmx/hGKPTDFpK3ZayxDrJ/h4A==
+ dependencies:
+ "@storybook/channels" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/core-events" "6.5.10"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
+ "@storybook/router" "6.5.10"
"@storybook/semver" "^7.3.2"
- "@storybook/theming" "6.2.9"
- "@types/reach__router" "^1.3.7"
+ "@storybook/theming" "6.5.10"
core-js "^3.8.2"
fast-deep-equal "^3.1.3"
global "^4.4.0"
- lodash "^4.17.20"
+ lodash "^4.17.21"
memoizerific "^1.11.3"
- qs "^6.10.0"
regenerator-runtime "^0.13.7"
store2 "^2.12.0"
- telejson "^5.1.0"
+ telejson "^6.0.8"
ts-dedent "^2.0.0"
util-deprecate "^1.0.2"
-"@storybook/builder-webpack4@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/builder-webpack4/-/builder-webpack4-6.2.9.tgz#dddff0b1b4590a7ba088ce13e7cc42e482f6455d"
- integrity sha512-swECic1huVdj+B+iRJIQ8ds59HuPVE4fmhI+j/nhw0CQCsgAEKqDlOQVYEimW6nZX8GO4WxNm6tiiRzxixejbw==
+"@storybook/builder-webpack4@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/builder-webpack4/-/builder-webpack4-6.5.10.tgz#79e95323577a37349ab3c81193fa249ac5c50173"
+ integrity sha512-AoKjsCNoQQoZXYwBDxO8s+yVEd5FjBJAaysEuUTHq2fb81jwLrGcEOo6hjw4jqfugZQIzYUEjPazlvubS78zpw==
dependencies:
"@babel/core" "^7.12.10"
- "@babel/plugin-proposal-class-properties" "^7.12.1"
- "@babel/plugin-proposal-decorators" "^7.12.12"
- "@babel/plugin-proposal-export-default-from" "^7.12.1"
- "@babel/plugin-proposal-nullish-coalescing-operator" "^7.12.1"
- "@babel/plugin-proposal-object-rest-spread" "^7.12.1"
- "@babel/plugin-proposal-optional-chaining" "^7.12.7"
- "@babel/plugin-proposal-private-methods" "^7.12.1"
- "@babel/plugin-syntax-dynamic-import" "^7.8.3"
- "@babel/plugin-transform-arrow-functions" "^7.12.1"
- "@babel/plugin-transform-block-scoping" "^7.12.12"
- "@babel/plugin-transform-classes" "^7.12.1"
- "@babel/plugin-transform-destructuring" "^7.12.1"
- "@babel/plugin-transform-for-of" "^7.12.1"
- "@babel/plugin-transform-parameters" "^7.12.1"
- "@babel/plugin-transform-shorthand-properties" "^7.12.1"
- "@babel/plugin-transform-spread" "^7.12.1"
- "@babel/plugin-transform-template-literals" "^7.12.1"
- "@babel/preset-env" "^7.12.11"
- "@babel/preset-react" "^7.12.10"
- "@babel/preset-typescript" "^7.12.7"
- "@storybook/addons" "6.2.9"
- "@storybook/api" "6.2.9"
- "@storybook/channel-postmessage" "6.2.9"
- "@storybook/channels" "6.2.9"
- "@storybook/client-api" "6.2.9"
- "@storybook/client-logger" "6.2.9"
- "@storybook/components" "6.2.9"
- "@storybook/core-common" "6.2.9"
- "@storybook/core-events" "6.2.9"
- "@storybook/node-logger" "6.2.9"
- "@storybook/router" "6.2.9"
+ "@storybook/addons" "6.5.10"
+ "@storybook/api" "6.5.10"
+ "@storybook/channel-postmessage" "6.5.10"
+ "@storybook/channels" "6.5.10"
+ "@storybook/client-api" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/components" "6.5.10"
+ "@storybook/core-common" "6.5.10"
+ "@storybook/core-events" "6.5.10"
+ "@storybook/node-logger" "6.5.10"
+ "@storybook/preview-web" "6.5.10"
+ "@storybook/router" "6.5.10"
"@storybook/semver" "^7.3.2"
- "@storybook/theming" "6.2.9"
- "@storybook/ui" "6.2.9"
- "@types/node" "^14.0.10"
+ "@storybook/store" "6.5.10"
+ "@storybook/theming" "6.5.10"
+ "@storybook/ui" "6.5.10"
+ "@types/node" "^14.0.10 || ^16.0.0"
"@types/webpack" "^4.41.26"
autoprefixer "^9.8.6"
- babel-loader "^8.2.2"
- babel-plugin-macros "^2.8.0"
- babel-plugin-polyfill-corejs3 "^0.1.0"
+ babel-loader "^8.0.0"
case-sensitive-paths-webpack-plugin "^2.3.0"
core-js "^3.8.2"
css-loader "^3.6.0"
- dotenv-webpack "^1.8.0"
file-loader "^6.2.0"
find-up "^5.0.0"
fork-ts-checker-webpack-plugin "^4.1.6"
- fs-extra "^9.0.1"
glob "^7.1.6"
glob-promise "^3.4.0"
global "^4.4.0"
html-webpack-plugin "^4.0.0"
pnp-webpack-plugin "1.6.4"
- postcss "^7.0.35"
+ postcss "^7.0.36"
postcss-flexbugs-fixes "^4.2.1"
postcss-loader "^4.2.0"
raw-loader "^4.0.2"
- react-dev-utils "^11.0.3"
stable "^0.1.8"
style-loader "^1.3.0"
- terser-webpack-plugin "^3.1.0"
+ terser-webpack-plugin "^4.2.3"
ts-dedent "^2.0.0"
url-loader "^4.1.1"
util-deprecate "^1.0.2"
webpack "4"
webpack-dev-middleware "^3.7.3"
webpack-filter-warnings-plugin "^1.2.1"
- webpack-hot-middleware "^2.25.0"
+ webpack-hot-middleware "^2.25.1"
webpack-virtual-modules "^0.2.2"
-"@storybook/channel-postmessage@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-6.2.9.tgz#ad85573e0a5d6f0cde3504f168d87a73cb0b6269"
- integrity sha512-OqV+gLeeCHR0KExsIz0B7gD17Cjd9D+I75qnBsLWM9inWO5kc/WZ5svw8Bvjlcm6snWpvxUaT8L+svuqcPSmww==
+"@storybook/channel-postmessage@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-6.5.10.tgz#be8971b4b7f91b664bb2c6965fdfb073d541a03e"
+ integrity sha512-t9PTA0UzFvYa3IlOfpBOolfrRMPTjUMIeCQ6FNyM0aj5GqLKSvoQzP8NeoRpIrvyf6ljFKKdaMaZ3fiCvh45ag==
dependencies:
- "@storybook/channels" "6.2.9"
- "@storybook/client-logger" "6.2.9"
- "@storybook/core-events" "6.2.9"
+ "@storybook/channels" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/core-events" "6.5.10"
core-js "^3.8.2"
global "^4.4.0"
qs "^6.10.0"
- telejson "^5.1.0"
+ telejson "^6.0.8"
-"@storybook/channels@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.2.9.tgz#a9fd7f25102cbec15fb56f76abf891b7b214e9de"
- integrity sha512-6dC8Fb2ipNyOQXnUZMDeEUaJGH5DMLzyHlGLhVyDtrO5WR6bO8mQdkzf4+5dSKXgCBNX0BSkssXth4pDjn18rg==
+"@storybook/channel-websocket@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/channel-websocket/-/channel-websocket-6.5.10.tgz#bd1316a9b555229b215e5054a76b57c503dd8adc"
+ integrity sha512-RTXMZbMWCS3xU+4GVIdfnUXsKcwg/WTozy88/5OxaKjGw6KgRedqLAQJKJ6Y5XlnwIcWelirkHj/COwTTXhbPg==
+ dependencies:
+ "@storybook/channels" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ core-js "^3.8.2"
+ global "^4.4.0"
+ telejson "^6.0.8"
+
+"@storybook/channels@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.5.10.tgz#fca5b0d1ea8d30b022e805301ed436407c867ac4"
+ integrity sha512-lo26YZ6kWpHXLhuHJF4P/bICY7jD/rXEZqReKtGOSk1Lv99/xvG6pqmcy3hWLf3v3Dy/8otjRPSR7izFVIIZgQ==
dependencies:
core-js "^3.8.2"
ts-dedent "^2.0.0"
util-deprecate "^1.0.2"
-"@storybook/client-api@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.2.9.tgz#f0bb44e9b2692adfbf30d7ff751c6dd44bcfe1ce"
- integrity sha512-aLvEUVkbvv6Qo/2mF4rFCecdqi2CGOUDdsV1a6EFIVS/9gXFdpirsOwKHo9qNjacGdWPlBYGCUcbrw+DvNaSFA==
- dependencies:
- "@storybook/addons" "6.2.9"
- "@storybook/channel-postmessage" "6.2.9"
- "@storybook/channels" "6.2.9"
- "@storybook/client-logger" "6.2.9"
- "@storybook/core-events" "6.2.9"
- "@storybook/csf" "0.0.1"
+"@storybook/client-api@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.5.10.tgz#0bc3f68ce014ce1ffd560472a893ba04be370f09"
+ integrity sha512-3wBWZl3NvMFgMovgEh+euiARAT2FXzpvTF4Q1gerGMNNDlrGxHnFvSuy4FHg/irtOGLa4yLz43ULFbYtpKw0Lg==
+ dependencies:
+ "@storybook/addons" "6.5.10"
+ "@storybook/channel-postmessage" "6.5.10"
+ "@storybook/channels" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/core-events" "6.5.10"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
+ "@storybook/store" "6.5.10"
"@types/qs" "^6.9.5"
"@types/webpack-env" "^1.16.0"
core-js "^3.8.2"
+ fast-deep-equal "^3.1.3"
global "^4.4.0"
- lodash "^4.17.20"
+ lodash "^4.17.21"
memoizerific "^1.11.3"
qs "^6.10.0"
regenerator-runtime "^0.13.7"
- stable "^0.1.8"
store2 "^2.12.0"
+ synchronous-promise "^2.0.15"
ts-dedent "^2.0.0"
util-deprecate "^1.0.2"
-"@storybook/client-logger@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.2.9.tgz#77c1ea39684ad2a2cf6836051b381fc5b354e132"
- integrity sha512-IfOQZuvpjh66qBInQCJOb9S0dTGpzZ/Cxlcvokp+PYt95KztaWN3mPm+HaDQCeRsrWNe0Bpm1zuickcJ6dBOXg==
+"@storybook/client-logger@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.5.10.tgz#cfea823a5b8444409daa74f854c5d05367986b34"
+ integrity sha512-/xA0MHOevXev68hyLMQw8Qo8KczSIdXOxliAgrycMTkDmw5eKeA8TP7B8zP3wGuq/e3MrdD9/8MWhb/IQBNC3w==
dependencies:
core-js "^3.8.2"
global "^4.4.0"
-"@storybook/components@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.2.9.tgz#7189f9715b05720fe083ae8ad014849f14e98e73"
- integrity sha512-hnV1MI2aB2g1sJ7NJphpxi7TwrMZQ/tpCJeHnkjmzyC6ez1MXqcBXGrEEdSXzRfAxjQTOEpu6H1mnns0xMP0Ag==
- dependencies:
- "@popperjs/core" "^2.6.0"
- "@storybook/client-logger" "6.2.9"
- "@storybook/csf" "0.0.1"
- "@storybook/theming" "6.2.9"
- "@types/color-convert" "^2.0.0"
- "@types/overlayscrollbars" "^1.12.0"
- "@types/react-syntax-highlighter" "11.0.5"
- color-convert "^2.0.1"
+"@storybook/components@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.5.10.tgz#268e1269bc3d262f7dcec13f96c3b844919687b8"
+ integrity sha512-9OhgB8YQfGwOKjo/N96N5mrtJ6qDVVoEM1zuhea32tJUd2eYf0aSWpryA9VnOM0V1q/8DAoCg5rPBMYWMBU5uw==
+ dependencies:
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
+ "@storybook/theming" "6.5.10"
core-js "^3.8.2"
- fast-deep-equal "^3.1.3"
- global "^4.4.0"
- lodash "^4.17.20"
- markdown-to-jsx "^7.1.0"
memoizerific "^1.11.3"
- overlayscrollbars "^1.13.1"
- polished "^4.0.5"
- prop-types "^15.7.2"
- react-colorful "^5.0.1"
- react-popper-tooltip "^3.1.1"
- react-syntax-highlighter "^13.5.3"
- react-textarea-autosize "^8.3.0"
+ qs "^6.10.0"
regenerator-runtime "^0.13.7"
- ts-dedent "^2.0.0"
util-deprecate "^1.0.2"
-"@storybook/core-client@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/core-client/-/core-client-6.2.9.tgz#3f611947e64dee0a297e512ff974087bc52c1877"
- integrity sha512-jW841J5lCe1Ub5ZMtzYPgCy/OUddFxxVYeHLZyuNxlH5RoiQQxbDpuFlzuZMYGuIzD6eZw+ANE4w5vW/y5oBfA==
- dependencies:
- "@storybook/addons" "6.2.9"
- "@storybook/channel-postmessage" "6.2.9"
- "@storybook/client-api" "6.2.9"
- "@storybook/client-logger" "6.2.9"
- "@storybook/core-events" "6.2.9"
- "@storybook/csf" "0.0.1"
- "@storybook/ui" "6.2.9"
+"@storybook/core-client@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/core-client/-/core-client-6.5.10.tgz#90c86923236c8efff33d454a0dc552f6df4346b1"
+ integrity sha512-THsIjNrOrampTl0Lgfjvfjk1JnktKb4CQLOM80KpQb4cjDqorBjJmErzUkUQ2y3fXvrDmQ/kUREkShET4XEdtA==
+ dependencies:
+ "@storybook/addons" "6.5.10"
+ "@storybook/channel-postmessage" "6.5.10"
+ "@storybook/channel-websocket" "6.5.10"
+ "@storybook/client-api" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/core-events" "6.5.10"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
+ "@storybook/preview-web" "6.5.10"
+ "@storybook/store" "6.5.10"
+ "@storybook/ui" "6.5.10"
+ airbnb-js-shims "^2.2.1"
ansi-to-html "^0.6.11"
core-js "^3.8.2"
global "^4.4.0"
- lodash "^4.17.20"
+ lodash "^4.17.21"
qs "^6.10.0"
regenerator-runtime "^0.13.7"
ts-dedent "^2.0.0"
unfetch "^4.2.0"
util-deprecate "^1.0.2"
-"@storybook/core-common@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/core-common/-/core-common-6.2.9.tgz#54f8e005733d39c4cb90eec7c17f9ca4dcbeec5f"
- integrity sha512-ve0Qb4EMit8jGibfZBprmaU2i4LtpB4vSMIzD9nB1YeBmw2cGhHubtmayZ0TwcV3fPQhtYH9wwRWuWyzzHyQyw==
+"@storybook/core-common@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/core-common/-/core-common-6.5.10.tgz#6b93449548b0890f5c68d89f0ca78e092026182c"
+ integrity sha512-Bx+VKkfWdrAmD8T51Sjq/mMhRaiapBHcpG4cU5bc3DMbg+LF2/yrgqv/cjVu+m5gHAzYCac5D7gqzBgvG7Myww==
dependencies:
"@babel/core" "^7.12.10"
"@babel/plugin-proposal-class-properties" "^7.12.1"
@@ -1746,6 +1669,7 @@
"@babel/plugin-proposal-object-rest-spread" "^7.12.1"
"@babel/plugin-proposal-optional-chaining" "^7.12.7"
"@babel/plugin-proposal-private-methods" "^7.12.1"
+ "@babel/plugin-proposal-private-property-in-object" "^7.12.1"
"@babel/plugin-syntax-dynamic-import" "^7.8.3"
"@babel/plugin-transform-arrow-functions" "^7.12.1"
"@babel/plugin-transform-block-scoping" "^7.12.12"
@@ -1759,13 +1683,11 @@
"@babel/preset-react" "^7.12.10"
"@babel/preset-typescript" "^7.12.7"
"@babel/register" "^7.12.1"
- "@storybook/node-logger" "6.2.9"
+ "@storybook/node-logger" "6.5.10"
"@storybook/semver" "^7.3.2"
- "@types/glob-base" "^0.3.0"
- "@types/micromatch" "^4.0.1"
- "@types/node" "^14.0.10"
+ "@types/node" "^14.0.10 || ^16.0.0"
"@types/pretty-hrtime" "^1.0.0"
- babel-loader "^8.2.2"
+ babel-loader "^8.0.0"
babel-plugin-macros "^3.0.1"
babel-plugin-polyfill-corejs3 "^0.1.0"
chalk "^4.1.0"
@@ -1774,78 +1696,162 @@
file-system-cache "^1.0.5"
find-up "^5.0.0"
fork-ts-checker-webpack-plugin "^6.0.4"
+ fs-extra "^9.0.1"
glob "^7.1.6"
- glob-base "^0.3.0"
+ handlebars "^4.7.7"
interpret "^2.2.0"
json5 "^2.1.3"
lazy-universal-dotenv "^3.0.1"
- micromatch "^4.0.2"
+ picomatch "^2.3.0"
pkg-dir "^5.0.0"
pretty-hrtime "^1.0.3"
resolve-from "^5.0.0"
+ slash "^3.0.0"
+ telejson "^6.0.8"
ts-dedent "^2.0.0"
util-deprecate "^1.0.2"
webpack "4"
-"@storybook/core-events@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.2.9.tgz#4f12947cd15d1eb3c4109923657c012feef521cd"
- integrity sha512-xQmbX/oYQK1QsAGN8hriXX5SUKOoTUe3L4dVaVHxJqy7MReRWJpprJmCpbAPJzWS6WCbDFfCM5kVEexHLOzJlQ==
+"@storybook/core-events@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.5.10.tgz#66d87c8ea18db8e448018a16a3d0198ddbcbc683"
+ integrity sha512-EVb1gO1172klVIAABLOoigFMx0V88uctY0K/qVCO8n6v+wd2+0Ccn63kl+gTxsAC3WZ8XhXh9q2w5ImHklVECw==
dependencies:
core-js "^3.8.2"
-"@storybook/core-server@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-6.2.9.tgz#da8b7f043ff59ee6cd2e8631ba8d0f954fdc265a"
- integrity sha512-DzihO73pj1Ro0Y4tq9hjw2mLMUYeSRPrx7CndCOBxcTHCKQ8Kd7Dee3wJ49t5/19V7TW1+4lYR59GAy73FeOAQ==
- dependencies:
- "@babel/core" "^7.12.10"
- "@babel/plugin-transform-template-literals" "^7.12.1"
- "@babel/preset-react" "^7.12.10"
- "@storybook/addons" "6.2.9"
- "@storybook/builder-webpack4" "6.2.9"
- "@storybook/core-client" "6.2.9"
- "@storybook/core-common" "6.2.9"
- "@storybook/node-logger" "6.2.9"
+"@storybook/core-server@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-6.5.10.tgz#ada3d647833c02cb8c742281c1f314ff866f96f8"
+ integrity sha512-jqwpA0ccA8X5ck4esWBid04+cEIVqirdAcqJeNb9IZAD+bRreO4Im8ilzr7jc5AmQ9fkqHs2NByFKh9TITp8NQ==
+ dependencies:
+ "@discoveryjs/json-ext" "^0.5.3"
+ "@storybook/builder-webpack4" "6.5.10"
+ "@storybook/core-client" "6.5.10"
+ "@storybook/core-common" "6.5.10"
+ "@storybook/core-events" "6.5.10"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
+ "@storybook/csf-tools" "6.5.10"
+ "@storybook/manager-webpack4" "6.5.10"
+ "@storybook/node-logger" "6.5.10"
"@storybook/semver" "^7.3.2"
- "@storybook/theming" "6.2.9"
- "@storybook/ui" "6.2.9"
- "@types/node" "^14.0.10"
+ "@storybook/store" "6.5.10"
+ "@storybook/telemetry" "6.5.10"
+ "@types/node" "^14.0.10 || ^16.0.0"
"@types/node-fetch" "^2.5.7"
"@types/pretty-hrtime" "^1.0.0"
"@types/webpack" "^4.41.26"
- airbnb-js-shims "^2.2.1"
- babel-loader "^8.2.2"
better-opn "^2.1.1"
- boxen "^4.2.0"
- case-sensitive-paths-webpack-plugin "^2.3.0"
+ boxen "^5.1.2"
chalk "^4.1.0"
- cli-table3 "0.6.0"
+ cli-table3 "^0.6.1"
commander "^6.2.1"
+ compression "^1.7.4"
core-js "^3.8.2"
- cpy "^8.1.1"
- css-loader "^3.6.0"
+ cpy "^8.1.2"
detect-port "^1.3.0"
- dotenv-webpack "^1.8.0"
+ express "^4.17.1"
+ fs-extra "^9.0.1"
+ global "^4.4.0"
+ globby "^11.0.2"
+ ip "^2.0.0"
+ lodash "^4.17.21"
+ node-fetch "^2.6.7"
+ open "^8.4.0"
+ pretty-hrtime "^1.0.3"
+ prompts "^2.4.0"
+ regenerator-runtime "^0.13.7"
+ serve-favicon "^2.5.0"
+ slash "^3.0.0"
+ telejson "^6.0.8"
+ ts-dedent "^2.0.0"
+ util-deprecate "^1.0.2"
+ watchpack "^2.2.0"
+ webpack "4"
+ ws "^8.2.3"
+ x-default-browser "^0.4.0"
+
+"@storybook/core@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/core/-/core-6.5.10.tgz#15ec8be85943251e25c2c24e80e20dcacc4fed65"
+ integrity sha512-K86yYa0tYlMxADlwQTculYvPROokQau09SCVqpsLg3wJCTvYFL4+SIqcYoyBSbFmHOdnYbJgPydjN33MYLiOZQ==
+ dependencies:
+ "@storybook/core-client" "6.5.10"
+ "@storybook/core-server" "6.5.10"
+
+"@storybook/csf-tools@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/csf-tools/-/csf-tools-6.5.10.tgz#ae6f1ebd4951e8978c8fe3e08ddd2bd269bf922b"
+ integrity sha512-H77kZQEisu7+skzeIbNZwmE09OqLjwJTeFhLN1pcjxKVa30LEI3pBHcNBxVKqgxl+Yg3KkB7W/ArLO2N+i2ohw==
+ dependencies:
+ "@babel/core" "^7.12.10"
+ "@babel/generator" "^7.12.11"
+ "@babel/parser" "^7.12.11"
+ "@babel/plugin-transform-react-jsx" "^7.12.12"
+ "@babel/preset-env" "^7.12.11"
+ "@babel/traverse" "^7.12.11"
+ "@babel/types" "^7.12.11"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
+ "@storybook/mdx1-csf" "^0.0.1"
+ core-js "^3.8.2"
+ fs-extra "^9.0.1"
+ global "^4.4.0"
+ regenerator-runtime "^0.13.7"
+ ts-dedent "^2.0.0"
+
+"@storybook/csf@0.0.2--canary.4566f4d.1":
+ version "0.0.2--canary.4566f4d.1"
+ resolved "https://registry.yarnpkg.com/@storybook/csf/-/csf-0.0.2--canary.4566f4d.1.tgz#dac52a21c40ef198554e71fe4d20d61e17f65327"
+ integrity sha512-9OVvMVh3t9znYZwb0Svf/YQoxX2gVOeQTGe2bses2yj+a3+OJnCrUF3/hGv6Em7KujtOdL2LL+JnG49oMVGFgQ==
+ dependencies:
+ lodash "^4.17.15"
+
+"@storybook/docs-tools@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/docs-tools/-/docs-tools-6.5.10.tgz#30baa62c1ca3a18b13625b6b305e23e39f404416"
+ integrity sha512-/bvYgOO+CxMEcHifkjJg0A60OTGOhcjGxnsB1h0gJuxMrqA/7Qwc108bFmPiX0eiD1BovFkZLJV4O6OY7zP5Vw==
+ dependencies:
+ "@babel/core" "^7.12.10"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
+ "@storybook/store" "6.5.10"
+ core-js "^3.8.2"
+ doctrine "^3.0.0"
+ lodash "^4.17.21"
+ regenerator-runtime "^0.13.7"
+
+"@storybook/manager-webpack4@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/manager-webpack4/-/manager-webpack4-6.5.10.tgz#41bae252b863484f293954ef2d2dc80bf3e028f1"
+ integrity sha512-N/TlNDhuhARuFipR/ZJ/xEVESz23iIbCsZ4VNehLHm8PpiGlQUehk+jMjWmz5XV0bJItwjRclY+CU3GjZKblfQ==
+ dependencies:
+ "@babel/core" "^7.12.10"
+ "@babel/plugin-transform-template-literals" "^7.12.1"
+ "@babel/preset-react" "^7.12.10"
+ "@storybook/addons" "6.5.10"
+ "@storybook/core-client" "6.5.10"
+ "@storybook/core-common" "6.5.10"
+ "@storybook/node-logger" "6.5.10"
+ "@storybook/theming" "6.5.10"
+ "@storybook/ui" "6.5.10"
+ "@types/node" "^14.0.10 || ^16.0.0"
+ "@types/webpack" "^4.41.26"
+ babel-loader "^8.0.0"
+ case-sensitive-paths-webpack-plugin "^2.3.0"
+ chalk "^4.1.0"
+ core-js "^3.8.2"
+ css-loader "^3.6.0"
express "^4.17.1"
file-loader "^6.2.0"
- file-system-cache "^1.0.5"
find-up "^5.0.0"
fs-extra "^9.0.1"
- global "^4.4.0"
html-webpack-plugin "^4.0.0"
- ip "^1.1.5"
- node-fetch "^2.6.1"
+ node-fetch "^2.6.7"
pnp-webpack-plugin "1.6.4"
- pretty-hrtime "^1.0.3"
- prompts "^2.4.0"
read-pkg-up "^7.0.1"
regenerator-runtime "^0.13.7"
resolve-from "^5.0.0"
- serve-favicon "^2.5.0"
style-loader "^1.3.0"
- telejson "^5.1.0"
- terser-webpack-plugin "^3.1.0"
+ telejson "^6.0.8"
+ terser-webpack-plugin "^4.2.3"
ts-dedent "^2.0.0"
url-loader "^4.1.1"
util-deprecate "^1.0.2"
@@ -1853,54 +1859,73 @@
webpack-dev-middleware "^3.7.3"
webpack-virtual-modules "^0.2.2"
-"@storybook/core@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/core/-/core-6.2.9.tgz#e32e72b3bdb44384f5f0ff93ad1a483acd033b4b"
- integrity sha512-pzbyjWvj0t8m0kR2pC9GQne4sZn7Y/zfcbm6/31CL+yhzOQjfJEj3n4ZFUlxikXqQJPg1aWfypfyaeaLL0QyuA==
- dependencies:
- "@storybook/core-client" "6.2.9"
- "@storybook/core-server" "6.2.9"
-
-"@storybook/csf@0.0.1":
+"@storybook/mdx1-csf@^0.0.1":
version "0.0.1"
- resolved "https://registry.yarnpkg.com/@storybook/csf/-/csf-0.0.1.tgz#95901507dc02f0bc6f9ac8ee1983e2fc5bb98ce6"
- integrity sha512-USTLkZze5gkel8MYCujSRBVIrUQ3YPBrLOx7GNk/0wttvVtlzWXAq9eLbQ4p/NicGxP+3T7KPEMVV//g+yubpw==
+ resolved "https://registry.yarnpkg.com/@storybook/mdx1-csf/-/mdx1-csf-0.0.1.tgz#d4184e3f6486fade9f7a6bfaf934d9bc07718d5b"
+ integrity sha512-4biZIWWzoWlCarMZmTpqcJNgo/RBesYZwGFbQeXiGYsswuvfWARZnW9RE9aUEMZ4XPn7B1N3EKkWcdcWe/K2tg==
dependencies:
- lodash "^4.17.15"
+ "@babel/generator" "^7.12.11"
+ "@babel/parser" "^7.12.11"
+ "@babel/preset-env" "^7.12.11"
+ "@babel/types" "^7.12.11"
+ "@mdx-js/mdx" "^1.6.22"
+ "@types/lodash" "^4.14.167"
+ js-string-escape "^1.0.1"
+ loader-utils "^2.0.0"
+ lodash "^4.17.21"
+ prettier ">=2.2.1 <=2.3.0"
+ ts-dedent "^2.0.0"
-"@storybook/node-logger@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.2.9.tgz#c67d8d7684514b8d00207502e8a9adda0ee750e5"
- integrity sha512-ryRBChWZf1A5hOVONErJZosS25IdMweoMVFAUAcj91iC0ynoSA6YL2jmoE71jQchxEXEgkDeRkX9lR/GlqFGZQ==
+"@storybook/node-logger@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.5.10.tgz#bce4c04009c4b62d6d2fb617176d7ef0084e9e89"
+ integrity sha512-bYswXIKV7Stru8vYfkjUMNN8UhF7Qg7NRsUvG5Djt5lLIae1XmUIgnH40mU/nW4X4BSfcR9MKxsSsngvn2WmQg==
dependencies:
"@types/npmlog" "^4.1.2"
chalk "^4.1.0"
core-js "^3.8.2"
- npmlog "^4.1.2"
+ npmlog "^5.0.1"
pretty-hrtime "^1.0.3"
-"@storybook/postinstall@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-6.2.9.tgz#3573ca86a27e9628defdd3a2c64721ee9db359ce"
- integrity sha512-HjAjXZV+WItonC7lVrfrUsQuRFZNz1g1lE0GgsEK2LdC5rAcD/JwJxjiWREwY+RGxKL9rpWgqyxVQajpIJRjhA==
+"@storybook/postinstall@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-6.5.10.tgz#b25378da036bce7b318c6732733aa5ad43449f37"
+ integrity sha512-xqUdpnFHYkn8MgtV+QztvIsRWa6jQUk7QT1Mu17Y0S7PbslNGsuskRPHenHhACXBJF+TM86R+4BaAhnVYTmElw==
dependencies:
core-js "^3.8.2"
-"@storybook/router@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.2.9.tgz#547543031dd8330870bb6b473dcf7e51982e841c"
- integrity sha512-7Bn1OFoItCl8whXRT8N1qp1Lky7kzXJ3aslWp5E8HcM8rxh4OYXfbaeiyJEJxBTGC5zxgY+tAEXHFjsAviFROg==
- dependencies:
- "@reach/router" "^1.3.4"
- "@storybook/client-logger" "6.2.9"
- "@types/reach__router" "^1.3.7"
+"@storybook/preview-web@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/preview-web/-/preview-web-6.5.10.tgz#81bf5d3f5fca9e26099c057206bd8e684225989b"
+ integrity sha512-sTC/o5gkvALOtcNgtApGKGN9EavvSxRHBeBh+5BQjV2qQ8ap+26RsfUizNBECAa2Jrn4osaDYn9HRhJLFL69WA==
+ dependencies:
+ "@storybook/addons" "6.5.10"
+ "@storybook/channel-postmessage" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/core-events" "6.5.10"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
+ "@storybook/store" "6.5.10"
+ ansi-to-html "^0.6.11"
core-js "^3.8.2"
- fast-deep-equal "^3.1.3"
global "^4.4.0"
- lodash "^4.17.20"
- memoizerific "^1.11.3"
+ lodash "^4.17.21"
qs "^6.10.0"
+ regenerator-runtime "^0.13.7"
+ synchronous-promise "^2.0.15"
ts-dedent "^2.0.0"
+ unfetch "^4.2.0"
+ util-deprecate "^1.0.2"
+
+"@storybook/router@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.5.10.tgz#b0c342e080c1d2b5344603bc43a6c75734a4a879"
+ integrity sha512-O+vNW/eEpYFF8eCg5jZjNQ6q2DKQVxqDRPCy9pJdEbvavMDZn6AFYgVK+VJe5F4211WW2yncOu922xObCxXJYg==
+ dependencies:
+ "@storybook/client-logger" "6.5.10"
+ core-js "^3.8.2"
+ memoizerific "^1.11.3"
+ qs "^6.10.0"
+ regenerator-runtime "^0.13.7"
"@storybook/semver@^7.3.2":
version "7.3.2"
@@ -1910,83 +1935,104 @@
core-js "^3.6.5"
find-up "^4.1.0"
-"@storybook/source-loader@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/source-loader/-/source-loader-6.2.9.tgz#ac6b314e48044acad5318d237275b24e684edb9f"
- integrity sha512-cx499g7BG2oeXvRFx45r0W0p2gKEy/e88WsUFnqqfMKZBJ8K0R/lx5DI0l1hq+TzSrE6uGe0/uPlaLkJNIro7g==
+"@storybook/source-loader@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/source-loader/-/source-loader-6.5.10.tgz#f62b4c7b1933976a20913ddc149d55026ef4c872"
+ integrity sha512-1RxxRumpjs8VUUwES9LId+cuNQnixhZAcwCxd6jaKkTZbjiQCtAhXX6DBTjJGV1u/JnCsqEp5b1wB8j/EioNHw==
dependencies:
- "@storybook/addons" "6.2.9"
- "@storybook/client-logger" "6.2.9"
- "@storybook/csf" "0.0.1"
+ "@storybook/addons" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
core-js "^3.8.2"
estraverse "^5.2.0"
global "^4.4.0"
loader-utils "^2.0.0"
- lodash "^4.17.20"
- prettier "~2.2.1"
+ lodash "^4.17.21"
+ prettier ">=2.2.1 <=2.3.0"
regenerator-runtime "^0.13.7"
-"@storybook/theming@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.2.9.tgz#16bf40180861f222c7ed1d80abd5d1e3cb315660"
- integrity sha512-183oJW7AD7Fhqg5NT4ct3GJntwteAb9jZnQ6yhf9JSdY+fk8OhxRbPf7ov0au2gYACcGrWDd9K5pYQsvWlP5gA==
+"@storybook/store@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/store/-/store-6.5.10.tgz#85df17a8d57af0cba3934b3c6046537e2bca9abd"
+ integrity sha512-RswrSYh2IiKkytFPxP9AvP+hekjrvHK2ILvyDk2ZgduCN4n5ivsekOb+N3M2t+dq1eLuW9or5n2T4OWwAwjxxQ==
dependencies:
- "@emotion/core" "^10.1.1"
- "@emotion/is-prop-valid" "^0.8.6"
- "@emotion/styled" "^10.0.27"
- "@storybook/client-logger" "6.2.9"
+ "@storybook/addons" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/core-events" "6.5.10"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
core-js "^3.8.2"
- deep-object-diff "^1.1.0"
- emotion-theming "^10.0.27"
+ fast-deep-equal "^3.1.3"
global "^4.4.0"
+ lodash "^4.17.21"
memoizerific "^1.11.3"
- polished "^4.0.5"
- resolve-from "^5.0.0"
+ regenerator-runtime "^0.13.7"
+ slash "^3.0.0"
+ stable "^0.1.8"
+ synchronous-promise "^2.0.15"
ts-dedent "^2.0.0"
+ util-deprecate "^1.0.2"
-"@storybook/ui@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.2.9.tgz#25cdf7ae2ef38ab337570c2377fda1da999792e7"
- integrity sha512-jq2xmw3reIqik/6ibUSbNKGR+Xvr9wkAEwexiOl+5WQ5BeYJpw4dmDmsFQf+SQuWaSEUUPolbzkakRQM778Kdg==
- dependencies:
- "@emotion/core" "^10.1.1"
- "@storybook/addons" "6.2.9"
- "@storybook/api" "6.2.9"
- "@storybook/channels" "6.2.9"
- "@storybook/client-logger" "6.2.9"
- "@storybook/components" "6.2.9"
- "@storybook/core-events" "6.2.9"
- "@storybook/router" "6.2.9"
- "@storybook/semver" "^7.3.2"
- "@storybook/theming" "6.2.9"
- "@types/markdown-to-jsx" "^6.11.3"
- copy-to-clipboard "^3.3.1"
+"@storybook/telemetry@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/telemetry/-/telemetry-6.5.10.tgz#742b05a55dfe8470ce4cb371f3f3f2c02f96e816"
+ integrity sha512-+M5HILDFS8nDumLxeSeAwi1MTzIuV6UWzV4yB2wcsEXOBTdplcl9oYqFKtlst78oOIdGtpPYxYfivDlqxC2K4g==
+ dependencies:
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/core-common" "6.5.10"
+ chalk "^4.1.0"
core-js "^3.8.2"
- core-js-pure "^3.8.2"
- downshift "^6.0.15"
- emotion-theming "^10.0.27"
- fuse.js "^3.6.1"
+ detect-package-manager "^2.0.1"
+ fetch-retry "^5.0.2"
+ fs-extra "^9.0.1"
global "^4.4.0"
- lodash "^4.17.20"
- markdown-to-jsx "^6.11.4"
+ isomorphic-unfetch "^3.1.0"
+ nanoid "^3.3.1"
+ read-pkg-up "^7.0.1"
+ regenerator-runtime "^0.13.7"
+
+"@storybook/theming@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.5.10.tgz#052100979c1270fc8f60653c1a13a6f047318109"
+ integrity sha512-BvTQBBcSEwKKcsVmF+Ol6v0RIQUr+bxP7gb10wtfBd23mZTEFA0C1N5FnZr/dDeiBKG1pvf1UKvoYA731y0BsA==
+ dependencies:
+ "@storybook/client-logger" "6.5.10"
+ core-js "^3.8.2"
+ memoizerific "^1.11.3"
+ regenerator-runtime "^0.13.7"
+
+"@storybook/ui@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.5.10.tgz#f56095a1a39ae5a203f2ac7f3dba86341a5927d5"
+ integrity sha512-6iaoaRAiTqB1inTw35vao+5hjcDE0Qa0A3a9ZIeNa6yHvpB1k0lO/N/0PMrRdVvySYpXVD1iry4z4QYdo1rU+w==
+ dependencies:
+ "@storybook/addons" "6.5.10"
+ "@storybook/api" "6.5.10"
+ "@storybook/channels" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/components" "6.5.10"
+ "@storybook/core-events" "6.5.10"
+ "@storybook/router" "6.5.10"
+ "@storybook/semver" "^7.3.2"
+ "@storybook/theming" "6.5.10"
+ core-js "^3.8.2"
memoizerific "^1.11.3"
- polished "^4.0.5"
qs "^6.10.0"
- react-draggable "^4.4.3"
- react-helmet-async "^1.0.7"
- react-sizeme "^3.0.1"
regenerator-runtime "^0.13.7"
resolve-from "^5.0.0"
- store2 "^2.12.0"
-"@storybook/vue@6.2.9":
- version "6.2.9"
- resolved "https://registry.yarnpkg.com/@storybook/vue/-/vue-6.2.9.tgz#f93476e564dd96e7992e3fc7ee9b58a5854a5b98"
- integrity sha512-mAXY1+/zn5gR+qDr6OzEj5AZgLlsw1qdarvQLf7fFH078oTPTQ7c/WzFUp3TaHd2M+ijGD8lb/HHBZqg37NoJA==
- dependencies:
- "@storybook/addons" "6.2.9"
- "@storybook/core" "6.2.9"
- "@storybook/core-common" "6.2.9"
+"@storybook/vue@6.5.10":
+ version "6.5.10"
+ resolved "https://registry.yarnpkg.com/@storybook/vue/-/vue-6.5.10.tgz#9074ba517d9dbf7587319b9d3c600c2fbd6c087a"
+ integrity sha512-4MYYvRPkqTBqQUjCNXiTM/PJ6qfzKaECFtEe0H7TG+WP+TuKCCfTY2u1q4ru2qjf8BcSXUfpIWPlfEpZh7wdaQ==
+ dependencies:
+ "@storybook/addons" "6.5.10"
+ "@storybook/client-logger" "6.5.10"
+ "@storybook/core" "6.5.10"
+ "@storybook/core-common" "6.5.10"
+ "@storybook/csf" "0.0.2--canary.4566f4d.1"
+ "@storybook/docs-tools" "6.5.10"
+ "@storybook/store" "6.5.10"
+ "@types/node" "^14.14.20 || ^16.0.0"
"@types/webpack-env" "^1.16.0"
core-js "^3.8.2"
global "^4.4.0"
@@ -1996,31 +2042,9 @@
regenerator-runtime "^0.13.7"
ts-dedent "^2.0.0"
ts-loader "^8.0.14"
- vue-docgen-api "^4.34.2"
+ vue-docgen-api "^4.44.15"
vue-docgen-loader "^1.5.0"
- webpack "4"
-
-"@types/braces@*":
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/@types/braces/-/braces-3.0.0.tgz#7da1c0d44ff1c7eb660a36ec078ea61ba7eb42cb"
- integrity sha512-TbH79tcyi9FHwbyboOKeRachRq63mSuWYXOflsNO9ZyE5ClQ/JaozNKl+aWUq87qPNsXasXxi2AbgfwIJ+8GQw==
-
-"@types/color-convert@^2.0.0":
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/@types/color-convert/-/color-convert-2.0.0.tgz#8f5ee6b9e863dcbee5703f5a517ffb13d3ea4e22"
- integrity sha512-m7GG7IKKGuJUXvkZ1qqG3ChccdIM/qBBo913z+Xft0nKCX4hAU/IxKwZBU4cpRZ7GS5kV4vOblUkILtSShCPXQ==
- dependencies:
- "@types/color-name" "*"
-
-"@types/color-name@*":
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
- integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
-
-"@types/glob-base@^0.3.0":
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/@types/glob-base/-/glob-base-0.3.0.tgz#a581d688347e10e50dd7c17d6f2880a10354319d"
- integrity sha1-pYHWiDR+EOUN18F9byiAoQNUMZ0=
+ webpack ">=4.0.0 <6.0.0"
"@types/glob@*", "@types/glob@^7.1.1":
version "7.1.3"
@@ -2073,17 +2097,15 @@
dependencies:
"@types/istanbul-lib-report" "*"
-"@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6":
- version "7.0.7"
- resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
- integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
+"@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8":
+ version "7.0.11"
+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
+ integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
-"@types/markdown-to-jsx@^6.11.3":
- version "6.11.3"
- resolved "https://registry.yarnpkg.com/@types/markdown-to-jsx/-/markdown-to-jsx-6.11.3.tgz#cdd1619308fecbc8be7e6a26f3751260249b020e"
- integrity sha512-30nFYpceM/ZEvhGiqWjm5quLUxNeld0HCzJEXMZZDpq53FPkS85mTwkWtCXzCqq8s5JYLgM5W392a02xn8Bdaw==
- dependencies:
- "@types/react" "*"
+"@types/lodash@^4.14.167":
+ version "4.14.183"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.183.tgz#1173e843e858cff5b997c234df2789a4a54c2374"
+ integrity sha512-UXavyuxzXKMqJPEpFPri6Ku5F9af6ZJXUneHhvQJxavrEjuHkFp2YnDWHcxJiG7hk8ZkWqjcyNeW1s/smZv5cw==
"@types/mdast@^3.0.0":
version "3.0.3"
@@ -2092,13 +2114,6 @@
dependencies:
"@types/unist" "*"
-"@types/micromatch@^4.0.1":
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/@types/micromatch/-/micromatch-4.0.1.tgz#9381449dd659fc3823fd2a4190ceacc985083bc7"
- integrity sha512-my6fLBvpY70KattTNzYOK6KU1oR1+UCz9ug/JbcF5UrEmeCt9P7DV2t7L8+t18mMPINqGQCE4O8PLOPbI84gxw==
- dependencies:
- "@types/braces" "*"
-
"@types/minimatch@*":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21"
@@ -2112,15 +2127,10 @@
"@types/node" "*"
form-data "^3.0.0"
-"@types/node@*":
- version "15.6.1"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-15.6.1.tgz#32d43390d5c62c5b6ec486a9bc9c59544de39a08"
- integrity sha512-7EIraBEyRHEe7CH+Fm1XvgqU6uwZN8Q7jppJGcqjROMT29qhAuuOxYB1uEY5UMYQKEmA5D+5tBnhdaPXSsLONA==
-
-"@types/node@^14.0.10":
- version "14.17.1"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.1.tgz#5e07e0cb2ff793aa7a1b41deae76221e6166049f"
- integrity sha512-/tpUyFD7meeooTRwl3sYlihx2BrJE7q9XF71EguPFIySj9B7qgnRtHsHTho+0AUm4m1SvWGm6uSncrR94q6Vtw==
+"@types/node@*", "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0":
+ version "16.11.49"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.49.tgz#560b1ea774b61e19a89c3fc72d2dcaa3863f38b2"
+ integrity sha512-Abq9fBviLV93OiXMu+f6r0elxCzRwc0RC5f99cU892uBITL44pTvgvEqlRlPRi8EGcO1z7Cp8A4d0s/p3J/+Nw==
"@types/normalize-package-data@^2.4.0":
version "2.4.0"
@@ -2132,11 +2142,6 @@
resolved "https://registry.yarnpkg.com/@types/npmlog/-/npmlog-4.1.2.tgz#d070fe6a6b78755d1092a3dc492d34c3d8f871c4"
integrity sha512-4QQmOF5KlwfxJ5IGXFIudkeLCdMABz03RcUXu+LCb24zmln8QW6aDjuGl4d4XPVLf2j+FnjelHTP7dvceAFbhA==
-"@types/overlayscrollbars@^1.12.0":
- version "1.12.0"
- resolved "https://registry.yarnpkg.com/@types/overlayscrollbars/-/overlayscrollbars-1.12.0.tgz#98456caceca8ad73bd5bb572632a585074e70764"
- integrity sha512-h/pScHNKi4mb+TrJGDon8Yb06ujFG0mSg12wIO0sWMUF3dQIe2ExRRdNRviaNt9IjxIiOfnRr7FsQAdHwK4sMg==
-
"@types/parse-json@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
@@ -2152,44 +2157,11 @@
resolved "https://registry.yarnpkg.com/@types/pretty-hrtime/-/pretty-hrtime-1.0.0.tgz#c5a2d644a135e988b2932f99737e67b3c62528d0"
integrity sha512-xl+5r2rcrxdLViAYkkiLMYsoUs3qEyrAnHFyEzYysgRxdVp3WbhysxIvJIxZp9FvZ2CYezh0TaHZorivH+voOQ==
-"@types/prop-types@*":
- version "15.7.3"
- resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
- integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
-
"@types/qs@^6.9.5":
version "6.9.6"
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.6.tgz#df9c3c8b31a247ec315e6996566be3171df4b3b1"
integrity sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==
-"@types/reach__router@^1.3.7":
- version "1.3.7"
- resolved "https://registry.yarnpkg.com/@types/reach__router/-/reach__router-1.3.7.tgz#de8ab374259ae7f7499fc1373b9697a5f3cd6428"
- integrity sha512-cyBEb8Ef3SJNH5NYEIDGPoMMmYUxROatuxbICusVRQIqZUB85UCt6R2Ok60tKS/TABJsJYaHyNTW3kqbpxlMjg==
- dependencies:
- "@types/react" "*"
-
-"@types/react-syntax-highlighter@11.0.5":
- version "11.0.5"
- resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.5.tgz#0d546261b4021e1f9d85b50401c0a42acb106087"
- integrity sha512-VIOi9i2Oj5XsmWWoB72p3KlZoEbdRAcechJa8Ztebw7bDl2YmR+odxIqhtJGp1q2EozHs02US+gzxJ9nuf56qg==
- dependencies:
- "@types/react" "*"
-
-"@types/react@*":
- version "17.0.8"
- resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.8.tgz#fe76e3ba0fbb5602704110fd1e3035cf394778e3"
- integrity sha512-3sx4c0PbXujrYAKwXxNONXUtRp9C+hE2di0IuxFyf5BELD+B+AXL8G7QrmSKhVwKZDbv0igiAjQAMhXj8Yg3aw==
- dependencies:
- "@types/prop-types" "*"
- "@types/scheduler" "*"
- csstype "^3.0.2"
-
-"@types/scheduler@*":
- version "0.16.1"
- resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275"
- integrity sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==
-
"@types/source-list-map@*":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
@@ -2250,59 +2222,63 @@
dependencies:
"@types/yargs-parser" "*"
-"@vue/compiler-core@3.0.11":
- version "3.0.11"
- resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.0.11.tgz#5ef579e46d7b336b8735228758d1c2c505aae69a"
- integrity sha512-6sFj6TBac1y2cWCvYCA8YzHJEbsVkX7zdRs/3yK/n1ilvRqcn983XvpBbnN3v4mZ1UiQycTvOiajJmOgN9EVgw==
+"@vue/compiler-core@3.2.37":
+ version "3.2.37"
+ resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.37.tgz#b3c42e04c0e0f2c496ff1784e543fbefe91e215a"
+ integrity sha512-81KhEjo7YAOh0vQJoSmAD68wLfYqJvoiD4ulyedzF+OEk/bk6/hx3fTNVfuzugIIaTrOx4PGx6pAiBRe5e9Zmg==
dependencies:
- "@babel/parser" "^7.12.0"
- "@babel/types" "^7.12.0"
- "@vue/shared" "3.0.11"
- estree-walker "^2.0.1"
+ "@babel/parser" "^7.16.4"
+ "@vue/shared" "3.2.37"
+ estree-walker "^2.0.2"
source-map "^0.6.1"
-"@vue/compiler-dom@3.0.11", "@vue/compiler-dom@^3.0.7":
- version "3.0.11"
- resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.0.11.tgz#b15fc1c909371fd671746020ba55b5dab4a730ee"
- integrity sha512-+3xB50uGeY5Fv9eMKVJs2WSRULfgwaTJsy23OIltKgMrynnIj8hTYY2UL97HCoz78aDw1VDXdrBQ4qepWjnQcw==
- dependencies:
- "@vue/compiler-core" "3.0.11"
- "@vue/shared" "3.0.11"
-
-"@vue/compiler-sfc@^3.0.7":
- version "3.0.11"
- resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.0.11.tgz#cd8ca2154b88967b521f5ad3b10f5f8b6b665679"
- integrity sha512-7fNiZuCecRleiyVGUWNa6pn8fB2fnuJU+3AGjbjl7r1P5wBivfl02H4pG+2aJP5gh2u+0wXov1W38tfWOphsXw==
- dependencies:
- "@babel/parser" "^7.13.9"
- "@babel/types" "^7.13.0"
- "@vue/compiler-core" "3.0.11"
- "@vue/compiler-dom" "3.0.11"
- "@vue/compiler-ssr" "3.0.11"
- "@vue/shared" "3.0.11"
- consolidate "^0.16.0"
- estree-walker "^2.0.1"
- hash-sum "^2.0.0"
- lru-cache "^5.1.1"
+"@vue/compiler-dom@3.2.37", "@vue/compiler-dom@^3.2.0":
+ version "3.2.37"
+ resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz#10d2427a789e7c707c872da9d678c82a0c6582b5"
+ integrity sha512-yxJLH167fucHKxaqXpYk7x8z7mMEnXOw3G2q62FTkmsvNxu4FQSu5+3UMb+L7fjKa26DEzhrmCxAgFLLIzVfqQ==
+ dependencies:
+ "@vue/compiler-core" "3.2.37"
+ "@vue/shared" "3.2.37"
+
+"@vue/compiler-sfc@^3.2.0":
+ version "3.2.37"
+ resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz#3103af3da2f40286edcd85ea495dcb35bc7f5ff4"
+ integrity sha512-+7i/2+9LYlpqDv+KTtWhOZH+pa8/HnX/905MdVmAcI/mPQOBwkHHIzrsEsucyOIZQYMkXUiTkmZq5am/NyXKkg==
+ dependencies:
+ "@babel/parser" "^7.16.4"
+ "@vue/compiler-core" "3.2.37"
+ "@vue/compiler-dom" "3.2.37"
+ "@vue/compiler-ssr" "3.2.37"
+ "@vue/reactivity-transform" "3.2.37"
+ "@vue/shared" "3.2.37"
+ estree-walker "^2.0.2"
magic-string "^0.25.7"
- merge-source-map "^1.1.0"
postcss "^8.1.10"
- postcss-modules "^4.0.0"
- postcss-selector-parser "^6.0.4"
source-map "^0.6.1"
-"@vue/compiler-ssr@3.0.11":
- version "3.0.11"
- resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.0.11.tgz#ac5a05fd1257412fa66079c823d8203b6a889a13"
- integrity sha512-66yUGI8SGOpNvOcrQybRIhl2M03PJ+OrDPm78i7tvVln86MHTKhM3ERbALK26F7tXl0RkjX4sZpucCpiKs3MnA==
+"@vue/compiler-ssr@3.2.37":
+ version "3.2.37"
+ resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz#4899d19f3a5fafd61524a9d1aee8eb0505313cff"
+ integrity sha512-7mQJD7HdXxQjktmsWp/J67lThEIcxLemz1Vb5I6rYJHR5vI+lON3nPGOH3ubmbvYGt8xEUaAr1j7/tIFWiEOqw==
+ dependencies:
+ "@vue/compiler-dom" "3.2.37"
+ "@vue/shared" "3.2.37"
+
+"@vue/reactivity-transform@3.2.37":
+ version "3.2.37"
+ resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.37.tgz#0caa47c4344df4ae59f5a05dde2a8758829f8eca"
+ integrity sha512-IWopkKEb+8qpu/1eMKVeXrK0NLw9HicGviJzhJDEyfxTR9e1WtpnnbYkJWurX6WwoFP0sz10xQg8yL8lgskAZg==
dependencies:
- "@vue/compiler-dom" "3.0.11"
- "@vue/shared" "3.0.11"
+ "@babel/parser" "^7.16.4"
+ "@vue/compiler-core" "3.2.37"
+ "@vue/shared" "3.2.37"
+ estree-walker "^2.0.2"
+ magic-string "^0.25.7"
-"@vue/shared@3.0.11":
- version "3.0.11"
- resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.0.11.tgz#20d22dd0da7d358bb21c17f9bde8628152642c77"
- integrity sha512-b+zB8A2so8eCE0JsxjL24J7vdGl8rzPQ09hZNhystm+KqSbKcAej1A+Hbva1rCMmTTqA+hFnUSDc5kouEo0JzA==
+"@vue/shared@3.2.37":
+ version "3.2.37"
+ resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.37.tgz#8e6adc3f2759af52f0e85863dfb0b711ecc5c702"
+ integrity sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw==
"@webassemblyjs/ast@1.9.0":
version "1.9.0"
@@ -2459,35 +2435,30 @@
resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
-accepts@~1.3.7:
- version "1.3.7"
- resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd"
- integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==
+accepts@~1.3.5, accepts@~1.3.7:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
+ integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
dependencies:
- mime-types "~2.1.24"
- negotiator "0.6.2"
-
-acorn-jsx@^5.3.1:
- version "5.3.1"
- resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b"
- integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==
-
-acorn-walk@^7.2.0:
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
- integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
+ mime-types "~2.1.34"
+ negotiator "0.6.3"
acorn@^6.4.1:
version "6.4.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==
-acorn@^7.1.1, acorn@^7.4.1:
+acorn@^7.1.1:
version "7.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
-address@1.1.2, address@^1.0.1:
+acorn@^8.5.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==
+
+address@^1.0.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6"
integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA==
@@ -2533,7 +2504,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2:
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
-ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5:
+ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.5:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -2555,30 +2526,25 @@ ansi-colors@^3.0.0:
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf"
integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==
-ansi-html@0.0.7:
- version "0.0.7"
- resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e"
- integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4=
+ansi-html-community@0.0.8:
+ version "0.0.8"
+ resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41"
+ integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
-ansi-regex@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
- integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
-
ansi-regex@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
-ansi-regex@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
- integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
+ansi-regex@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+ integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
ansi-styles@^3.2.1:
version "3.2.1"
@@ -2587,7 +2553,7 @@ ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
-ansi-styles@^4.1.0:
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
@@ -2609,7 +2575,7 @@ anymatch@^2.0.0:
micromatch "^3.1.4"
normalize-path "^2.1.1"
-anymatch@^3.0.0, anymatch@^3.0.3, anymatch@~3.1.1, anymatch@~3.1.2:
+anymatch@^3.0.0, anymatch@^3.0.3, anymatch@~3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
@@ -2622,18 +2588,18 @@ app-root-dir@^1.0.2:
resolved "https://registry.yarnpkg.com/app-root-dir/-/app-root-dir-1.0.2.tgz#38187ec2dea7577fff033ffcb12172692ff6e118"
integrity sha1-OBh+wt6nV3//Az/8sSFyaS/24Rg=
-aproba@^1.0.3, aproba@^1.1.1:
+"aproba@^1.0.3 || ^2.0.0", aproba@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
-are-we-there-yet@~1.1.2:
- version "1.1.5"
- resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
- integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==
+are-we-there-yet@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c"
+ integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==
dependencies:
delegates "^1.0.0"
- readable-stream "^2.0.6"
+ readable-stream "^3.6.0"
argparse@^1.0.7:
version "1.0.10"
@@ -2657,6 +2623,11 @@ arr-union@^3.1.0:
resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
+array-find-index@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
+ integrity sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==
+
array-flatten@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
@@ -2775,10 +2746,12 @@ ast-types@0.14.2:
dependencies:
tslib "^2.0.1"
-async-each@^1.0.1:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
- integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
+ast-types@0.15.2:
+ version "0.15.2"
+ resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.15.2.tgz#39ae4809393c4b16df751ee563411423e85fb49d"
+ integrity sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==
+ dependencies:
+ tslib "^2.0.1"
asynckit@^0.4.0:
version "0.4.0"
@@ -2808,23 +2781,23 @@ autoprefixer@^9.8.6:
postcss "^7.0.32"
postcss-value-parser "^4.1.0"
-axe-core@^4.1.1:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.2.1.tgz#2e50bcf10ee5b819014f6e342e41e45096239e34"
- integrity sha512-evY7DN8qSIbsW2H/TWQ1bX3sXN1d4MNb5Vb4n7BzPuCwRHdkZ1H2eNLuSh73EoQqkGKUtju2G2HCcjCfhvZIAA==
+axe-core@^4.2.0:
+ version "4.4.3"
+ resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.3.tgz#11c74d23d5013c0fa5d183796729bc3482bd2f6f"
+ integrity sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==
babel-core@^7.0.0-bridge.0:
version "7.0.0-bridge.0"
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece"
integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==
-babel-loader@^8.2.2:
- version "8.2.2"
- resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.2.tgz#9363ce84c10c9a40e6c753748e1441b60c8a0b81"
- integrity sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==
+babel-loader@^8.0.0:
+ version "8.2.5"
+ resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.5.tgz#d45f585e654d5a5d90f5350a779d7647c5ed512e"
+ integrity sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==
dependencies:
find-cache-dir "^3.3.1"
- loader-utils "^1.4.0"
+ loader-utils "^2.0.0"
make-dir "^3.1.0"
schema-utils "^2.6.5"
@@ -2843,22 +2816,6 @@ babel-plugin-dynamic-import-node@^2.3.3:
dependencies:
object.assign "^4.1.0"
-babel-plugin-emotion@^10.0.27:
- version "10.2.2"
- resolved "https://registry.yarnpkg.com/babel-plugin-emotion/-/babel-plugin-emotion-10.2.2.tgz#a1fe3503cff80abfd0bdda14abd2e8e57a79d17d"
- integrity sha512-SMSkGoqTbTyUTDeuVuPIWifPdUGkTk1Kf9BWRiXIOIcuyMfsdp2EjeiiFvOzX8NOBvEh/ypKYvUh2rkgAJMCLA==
- dependencies:
- "@babel/helper-module-imports" "^7.0.0"
- "@emotion/hash" "0.8.0"
- "@emotion/memoize" "0.7.4"
- "@emotion/serialize" "^0.11.16"
- babel-plugin-macros "^2.0.0"
- babel-plugin-syntax-jsx "^6.18.0"
- convert-source-map "^1.5.0"
- escape-string-regexp "^1.0.5"
- find-root "^1.1.0"
- source-map "^0.5.7"
-
babel-plugin-extract-import-names@1.6.22:
version "1.6.22"
resolved "https://registry.yarnpkg.com/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz#de5f9a28eb12f3eb2578bf74472204e66d1a13dc"
@@ -2877,15 +2834,6 @@ babel-plugin-istanbul@^6.0.0:
istanbul-lib-instrument "^4.0.0"
test-exclude "^6.0.0"
-babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.8.0:
- version "2.8.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138"
- integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==
- dependencies:
- "@babel/runtime" "^7.7.2"
- cosmiconfig "^6.0.0"
- resolve "^1.12.0"
-
babel-plugin-macros@^3.0.1:
version "3.1.0"
resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1"
@@ -2927,11 +2875,6 @@ babel-plugin-polyfill-regenerator@^0.2.0:
dependencies:
"@babel/helper-define-polyfill-provider" "^0.2.2"
-babel-plugin-syntax-jsx@^6.18.0:
- version "6.18.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946"
- integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=
-
babel-walk@3.0.0-canary-5:
version "3.0.0-canary-5"
resolved "https://registry.yarnpkg.com/babel-walk/-/babel-walk-3.0.0-canary-5.tgz#f66ecd7298357aee44955f235a6ef54219104b11"
@@ -2979,29 +2922,22 @@ better-opn@^2.1.1:
dependencies:
open "^7.0.3"
+big-integer@^1.6.7:
+ version "1.6.51"
+ resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686"
+ integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==
+
big.js@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
-binary-extensions@^1.0.0:
- version "1.13.1"
- resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
- integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==
-
binary-extensions@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
-bindings@^1.5.0:
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
- integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
- dependencies:
- file-uri-to-path "1.0.0"
-
-bluebird@^3.3.5, bluebird@^3.5.5, bluebird@^3.7.2:
+bluebird@^3.3.5, bluebird@^3.5.5:
version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
@@ -3037,19 +2973,26 @@ boolbase@^1.0.0, boolbase@~1.0.0:
resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
-boxen@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64"
- integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==
+boxen@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50"
+ integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==
dependencies:
ansi-align "^3.0.0"
- camelcase "^5.3.1"
- chalk "^3.0.0"
- cli-boxes "^2.2.0"
- string-width "^4.1.0"
- term-size "^2.1.0"
- type-fest "^0.8.1"
+ camelcase "^6.2.0"
+ chalk "^4.1.0"
+ cli-boxes "^2.2.1"
+ string-width "^4.2.2"
+ type-fest "^0.20.2"
widest-line "^3.1.0"
+ wrap-ansi "^7.0.0"
+
+bplist-parser@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.1.1.tgz#d60d5dcc20cba6dc7e1f299b35d3e1f95dafbae6"
+ integrity sha512-2AEM0FXy8ZxVLBuqX0hqt1gDwcnz2zygEkQ6zaD5Wko/sB9paUNwlpawrFtKeHUAQUOzjVy9AO4oeonqIHKA9Q==
+ dependencies:
+ big-integer "^1.6.7"
brace-expansion@^1.1.7:
version "1.1.11"
@@ -3059,7 +3002,7 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
-braces@^2.3.1, braces@^2.3.2:
+braces@^2.3.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
@@ -3075,7 +3018,7 @@ braces@^2.3.1, braces@^2.3.2:
split-string "^3.0.2"
to-regex "^3.0.1"
-braces@^3.0.1, braces@~3.0.2:
+braces@^3.0.2, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
@@ -3148,26 +3091,15 @@ browserify-zlib@^0.2.0:
dependencies:
pako "~1.0.5"
-browserslist@4.14.2:
- version "4.14.2"
- resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.14.2.tgz#1b3cec458a1ba87588cc5e9be62f19b6d48813ce"
- integrity sha512-HI4lPveGKUR0x2StIz+2FXfDk9SfVMrxn6PLh1JeGUwcuoDkdKZebWiyLRJ68iIPDpMI4JLVDf7S7XzslgWOhw==
- dependencies:
- caniuse-lite "^1.0.30001125"
- electron-to-chromium "^1.3.564"
- escalade "^3.0.2"
- node-releases "^1.1.61"
-
browserslist@^4.12.0, browserslist@^4.16.6:
- version "4.16.6"
- resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2"
- integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==
+ version "4.21.3"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a"
+ integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==
dependencies:
- caniuse-lite "^1.0.30001219"
- colorette "^1.2.2"
- electron-to-chromium "^1.3.723"
- escalade "^3.1.1"
- node-releases "^1.1.71"
+ caniuse-lite "^1.0.30001370"
+ electron-to-chromium "^1.4.202"
+ node-releases "^2.0.6"
+ update-browserslist-db "^1.0.5"
bser@2.1.1:
version "2.1.1"
@@ -3200,6 +3132,11 @@ builtin-status-codes@^3.0.0:
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
+bytes@3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
+ integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==
+
bytes@3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
@@ -3314,15 +3251,33 @@ camelcase-css@2.0.1:
resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
+camelcase-keys@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
+ integrity sha512-bA/Z/DERHKqoEOrp+qeGKw1QlvEQkGZSc0XaY6VnTxZr+Kv1G5zFwttpjv8qxZ/sBPT4nthwZaAcsAZTJlSKXQ==
+ dependencies:
+ camelcase "^2.0.0"
+ map-obj "^1.0.0"
+
+camelcase@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
+ integrity sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw==
+
camelcase@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
-caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001219:
- version "1.0.30001230"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz#8135c57459854b2240b57a4a6786044bdc5a9f71"
- integrity sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==
+camelcase@^6.2.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
+ integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
+
+caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001370:
+ version "1.0.30001378"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001378.tgz#3d2159bf5a8f9ca093275b0d3ecc717b00f27b67"
+ integrity sha512-JVQnfoO7FK7WvU4ZkBRbPjaot4+YqxogSDosHv0Hv5mWpUESmN+UubMU6L/hGz8QlQ2aY5U0vR6MOs6j/CXpNA==
capture-exit@^2.0.0:
version "2.0.0"
@@ -3341,7 +3296,7 @@ ccount@^1.0.0:
resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043"
integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==
-chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2:
+chalk@^2.0.0, chalk@^2.4.1:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -3350,14 +3305,6 @@ chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
-chalk@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
- integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
- dependencies:
- ansi-styles "^4.1.0"
- supports-color "^7.1.0"
-
chalk@^4.0.0, chalk@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad"
@@ -3388,7 +3335,7 @@ character-reference-invalid@^1.0.0:
resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560"
integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==
-"chokidar@>=3.0.0 <4.0.0":
+"chokidar@>=3.0.0 <4.0.0", chokidar@^2.1.8, chokidar@^3.4.1, chokidar@^3.4.2, chokidar@^3.5.2:
version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
@@ -3403,40 +3350,6 @@ character-reference-invalid@^1.0.0:
optionalDependencies:
fsevents "~2.3.2"
-chokidar@^2.1.8:
- version "2.1.8"
- resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
- integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==
- dependencies:
- anymatch "^2.0.0"
- async-each "^1.0.1"
- braces "^2.3.2"
- glob-parent "^3.1.0"
- inherits "^2.0.3"
- is-binary-path "^1.0.0"
- is-glob "^4.0.0"
- normalize-path "^3.0.0"
- path-is-absolute "^1.0.0"
- readdirp "^2.2.1"
- upath "^1.1.1"
- optionalDependencies:
- fsevents "^1.2.7"
-
-chokidar@^3.4.1, chokidar@^3.4.2:
- version "3.5.1"
- resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a"
- integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==
- dependencies:
- anymatch "~3.1.1"
- braces "~3.0.2"
- glob-parent "~5.1.0"
- is-binary-path "~2.1.0"
- is-glob "~4.0.1"
- normalize-path "~3.0.0"
- readdirp "~3.5.0"
- optionalDependencies:
- fsevents "~2.3.1"
-
chownr@^1.1.1:
version "1.1.4"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
@@ -3475,11 +3388,6 @@ class-utils@^0.3.5:
isobject "^3.0.0"
static-extend "^0.1.1"
-classnames@^2.2.5:
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
- integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==
-
clean-css@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78"
@@ -3492,29 +3400,19 @@ clean-stack@^2.0.0:
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
-cli-boxes@^2.2.0:
+cli-boxes@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f"
integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==
-cli-table3@0.6.0:
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.0.tgz#b7b1bc65ca8e7b5cef9124e13dc2b21e2ce4faee"
- integrity sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ==
+cli-table3@^0.6.1:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.2.tgz#aaf5df9d8b5bf12634dc8b3040806a0c07120d2a"
+ integrity sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw==
dependencies:
- object-assign "^4.1.0"
string-width "^4.2.0"
optionalDependencies:
- colors "^1.1.2"
-
-clipboard@^2.0.0:
- version "2.0.8"
- resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.8.tgz#ffc6c103dd2967a83005f3f61976aa4655a4cdba"
- integrity sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==
- dependencies:
- good-listener "^1.2.2"
- select "^1.1.2"
- tiny-emitter "^2.0.0"
+ "@colors/colors" "1.5.0"
clone-deep@^4.0.1:
version "4.0.1"
@@ -3530,11 +3428,6 @@ clone@^2.1.2:
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=
-code-point-at@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
- integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
-
collapse-white-space@^1.0.2:
version "1.0.6"
resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287"
@@ -3572,6 +3465,11 @@ color-name@~1.1.4:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+color-support@^1.1.2:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
+ integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
+
colorette@^1.2.1, colorette@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
@@ -3619,10 +3517,25 @@ component-emitter@^1.2.1:
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
-compute-scroll-into-view@^1.0.17:
- version "1.0.17"
- resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz#6a88f18acd9d42e9cf4baa6bec7e0522607ab7ab"
- integrity sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg==
+compressible@~2.0.16:
+ version "2.0.18"
+ resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
+ integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==
+ dependencies:
+ mime-db ">= 1.43.0 < 2"
+
+compression@^1.7.4:
+ version "1.7.4"
+ resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
+ integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==
+ dependencies:
+ accepts "~1.3.5"
+ bytes "3.0.0"
+ compressible "~2.0.16"
+ debug "2.6.9"
+ on-headers "~1.0.2"
+ safe-buffer "5.1.2"
+ vary "~1.1.2"
concat-map@0.0.1:
version "0.0.1"
@@ -3644,17 +3557,10 @@ console-browserify@^1.1.0:
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==
-console-control-strings@^1.0.0, console-control-strings@~1.1.0:
+console-control-strings@^1.0.0, console-control-strings@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
- integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
-
-consolidate@^0.16.0:
- version "0.16.0"
- resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.16.0.tgz#a11864768930f2f19431660a65906668f5fbdc16"
- integrity sha512-Nhl1wzCslqXYTJVDyJCu3ODohy9OfBMB5uD2BiBTzd7w+QY0lBzafkR8y8755yMYHAaMD4NuzbAw03/xzfw+eQ==
- dependencies:
- bluebird "^3.7.2"
+ integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
constantinople@^4.0.1:
version "4.0.1"
@@ -3681,7 +3587,7 @@ content-type@~1.0.4:
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
-convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.7.0:
+convert-source-map@^1.4.0, convert-source-map@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
@@ -3715,13 +3621,6 @@ copy-descriptor@^0.1.0:
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
-copy-to-clipboard@^3.3.1:
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz#115aa1a9998ffab6196f93076ad6da3b913662ae"
- integrity sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==
- dependencies:
- toggle-selection "^1.0.6"
-
core-js-compat@^3.8.1, core-js-compat@^3.9.0, core-js-compat@^3.9.1:
version "3.13.1"
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.13.1.tgz#05444caa8f153be0c67db03cf8adb8ec0964e58e"
@@ -3730,11 +3629,6 @@ core-js-compat@^3.8.1, core-js-compat@^3.9.0, core-js-compat@^3.9.1:
browserslist "^4.16.6"
semver "7.0.0"
-core-js-pure@^3.8.2:
- version "3.13.1"
- resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.13.1.tgz#5d139d346780f015f67225f45ee2362a6bed6ba1"
- integrity sha512-wVlh0IAi2t1iOEh16y4u1TRk6ubd4KvLE8dlMi+3QUI6SfKphQUh7tAwihGGSQ8affxEXpVIPpOdf9kjR4v4Pw==
-
core-js@^3.0.4, core-js@^3.6.5, core-js@^3.8.2:
version "3.13.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.13.1.tgz#30303fabd53638892062d8b4e802cac7599e9fb7"
@@ -3787,7 +3681,7 @@ cp-file@^7.0.0:
nested-error-stacks "^2.0.0"
p-event "^4.1.0"
-cpy@^8.1.1:
+cpy@^8.1.2:
version "8.1.2"
resolved "https://registry.yarnpkg.com/cpy/-/cpy-8.1.2.tgz#e339ea54797ad23f8e3919a5cffd37bfc3f25935"
integrity sha512-dmC4mUesv0OYH2kNFEidtf/skUwv4zePmGeepjyyJ0qTo5+8KhA1o99oIAwVVLzQMAeDJml74d6wPPKb6EZUTg==
@@ -3833,23 +3727,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
-create-react-context@0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.3.0.tgz#546dede9dc422def0d3fc2fe03afe0bc0f4f7d8c"
- integrity sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==
- dependencies:
- gud "^1.0.0"
- warning "^4.0.3"
-
-cross-spawn@7.0.3:
- version "7.0.3"
- resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
- integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
- dependencies:
- path-key "^3.1.0"
- shebang-command "^2.0.0"
- which "^2.0.1"
-
cross-spawn@^6.0.0:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
@@ -3861,6 +3738,15 @@ cross-spawn@^6.0.0:
shebang-command "^1.2.0"
which "^1.2.9"
+cross-spawn@^7.0.3:
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
+ integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
+ dependencies:
+ path-key "^3.1.0"
+ shebang-command "^2.0.0"
+ which "^2.0.1"
+
crypto-browserify@^3.11.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
@@ -3917,15 +3803,12 @@ cssesc@^3.0.0:
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
-csstype@^2.5.7:
- version "2.6.17"
- resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.17.tgz#4cf30eb87e1d1a005d8b6510f95292413f6a1c0e"
- integrity sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A==
-
-csstype@^3.0.2:
- version "3.0.8"
- resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340"
- integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==
+currently-unhandled@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
+ integrity sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==
+ dependencies:
+ array-find-index "^1.0.1"
cyclist@^1.0.1:
version "1.0.1"
@@ -3953,26 +3836,35 @@ debug@^4.1.0, debug@^4.1.1:
dependencies:
ms "2.1.2"
+decamelize@^1.1.2:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+ integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==
+
decode-uri-component@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
-deep-is@~0.1.3:
- version "0.1.3"
- resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
- integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
-
-deep-object-diff@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/deep-object-diff/-/deep-object-diff-1.1.0.tgz#d6fabf476c2ed1751fc94d5ca693d2ed8c18bc5a"
- integrity sha512-b+QLs5vHgS+IoSNcUE4n9HP2NwcHj7aqnJWsjPtuG75Rh5TOaGt0OjAYInh77d5T16V5cRDC+Pw/6ZZZiETBGw==
-
deepmerge@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
+default-browser-id@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-1.0.4.tgz#e59d09a5d157b828b876c26816e61c3d2a2c203a"
+ integrity sha512-qPy925qewwul9Hifs+3sx1ZYn14obHxpkX+mPD369w4Rzg+YkJBgi3SOvwUq81nWSjqGUegIgEPwD8u+HUnxlw==
+ dependencies:
+ bplist-parser "^0.1.0"
+ meow "^3.1.0"
+ untildify "^2.0.0"
+
+define-lazy-prop@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f"
+ integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==
+
define-properties@^1.1.2, define-properties@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
@@ -4007,11 +3899,6 @@ delayed-stream@~1.0.0:
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
-delegate@^3.1.2:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166"
- integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==
-
delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
@@ -4042,13 +3929,12 @@ detab@2.0.4:
dependencies:
repeat-string "^1.5.4"
-detect-port-alt@1.1.6:
- version "1.1.6"
- resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275"
- integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==
+detect-package-manager@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/detect-package-manager/-/detect-package-manager-2.0.1.tgz#6b182e3ae5e1826752bfef1de9a7b828cffa50d8"
+ integrity sha512-j/lJHyoLlWi6G1LDdLgvUtz60Zo5GEj+sVYtTVXnYLDPuzgC3llMxonXym9zIwhhUII8vjdw0LXxavpLqTbl1A==
dependencies:
- address "^1.0.1"
- debug "^2.6.0"
+ execa "^5.1.1"
detect-port@^1.3.0:
version "1.3.0"
@@ -4151,50 +4037,16 @@ dot-case@^3.0.4:
no-case "^3.0.4"
tslib "^2.0.3"
-dotenv-defaults@^1.0.2:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/dotenv-defaults/-/dotenv-defaults-1.1.1.tgz#032c024f4b5906d9990eb06d722dc74cc60ec1bd"
- integrity sha512-6fPRo9o/3MxKvmRZBD3oNFdxODdhJtIy1zcJeUSCs6HCy4tarUpd+G67UTU9tF6OWXeSPqsm4fPAB+2eY9Rt9Q==
- dependencies:
- dotenv "^6.2.0"
-
dotenv-expand@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==
-dotenv-webpack@^1.8.0:
- version "1.8.0"
- resolved "https://registry.yarnpkg.com/dotenv-webpack/-/dotenv-webpack-1.8.0.tgz#7ca79cef2497dd4079d43e81e0796bc9d0f68a5e"
- integrity sha512-o8pq6NLBehtrqA8Jv8jFQNtG9nhRtVqmoD4yWbgUyoU3+9WBlPe+c2EAiaJok9RB28QvrWvdWLZGeTT5aATDMg==
- dependencies:
- dotenv-defaults "^1.0.2"
-
-dotenv@^6.2.0:
- version "6.2.0"
- resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064"
- integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==
-
dotenv@^8.0.0:
version "8.6.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b"
integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==
-downshift@^6.0.15:
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/downshift/-/downshift-6.1.3.tgz#e794b7805d24810968f21e81ad6bdd9f3fdc40da"
- integrity sha512-RA1MuaNcTbt0j+sVLhSs8R2oZbBXYAtdQP/V+uHhT3DoDteZzJPjlC+LQVm9T07Wpvo84QXaZtUCePLDTDwGXg==
- dependencies:
- "@babel/runtime" "^7.13.10"
- compute-scroll-into-view "^1.0.17"
- prop-types "^15.7.2"
- react-is "^17.0.2"
-
-duplexer@^0.1.1:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
- integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==
-
duplexify@^3.4.2, duplexify@^3.6.0:
version "3.7.1"
resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309"
@@ -4210,10 +4062,10 @@ ee-first@1.1.1:
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
-electron-to-chromium@^1.3.564, electron-to-chromium@^1.3.723:
- version "1.3.742"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.742.tgz#7223215acbbd3a5284962ebcb6df85d88b95f200"
- integrity sha512-ihL14knI9FikJmH2XUIDdZFWJxvr14rPSdOhJ7PpS27xbz8qmaRwCwyg/bmFwjWKmWK9QyamiCZVCvXm5CH//Q==
+electron-to-chromium@^1.4.202:
+ version "1.4.224"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.224.tgz#ecf2eed395cfedcbbe634658ccc4b457f7b254c3"
+ integrity sha512-dOujC5Yzj0nOVE23iD5HKqrRSDj2SD7RazpZS/b/WX85MtO6/LzKDF4TlYZTBteB+7fvSg5JpWh0sN7fImNF8w==
element-resize-detector@^1.2.2:
version "1.2.2"
@@ -4255,15 +4107,6 @@ emojis-list@^3.0.0:
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
-emotion-theming@^10.0.27:
- version "10.0.27"
- resolved "https://registry.yarnpkg.com/emotion-theming/-/emotion-theming-10.0.27.tgz#1887baaec15199862c89b1b984b79806f2b9ab10"
- integrity sha512-MlF1yu/gYh8u+sLUqA0YuA9JX0P4Hb69WlKc/9OLo+WCXuX6sy/KoIa+qJimgmr2dWqnypYKYPX37esjDBbhdw==
- dependencies:
- "@babel/runtime" "^7.5.5"
- "@emotion/weak-memoize" "0.2.5"
- hoist-non-react-statics "^3.3.0"
-
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
@@ -4302,7 +4145,7 @@ errno@^0.1.3, errno@~0.1.7:
dependencies:
prr "~1.0.1"
-error-ex@^1.3.1:
+error-ex@^1.2.0, error-ex@^1.3.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
@@ -4369,7 +4212,7 @@ es6-shim@^0.35.5:
resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.6.tgz#d10578301a83af2de58b9eadb7c2c9945f7388a0"
integrity sha512-EmTr31wppcaIAgblChZiuN/l9Y7DPyw8Xtbg7fIVngn6zMW+IEBJDJngeKC3x6wr0V/vcA2wqeFnaw1bFJbDdA==
-escalade@^3.0.2, escalade@^3.1.1:
+escalade@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
@@ -4379,28 +4222,11 @@ escape-html@~1.0.3:
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
-escape-string-regexp@2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
- integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
-
escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
-escodegen@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd"
- integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==
- dependencies:
- esprima "^4.0.1"
- estraverse "^5.2.0"
- esutils "^2.0.2"
- optionator "^0.8.1"
- optionalDependencies:
- source-map "~0.6.1"
-
eslint-scope@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848"
@@ -4409,7 +4235,7 @@ eslint-scope@^4.0.3:
esrecurse "^4.1.0"
estraverse "^4.1.1"
-esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0:
+esprima@^4.0.0, esprima@~4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
@@ -4431,7 +4257,7 @@ estraverse@^5.2.0:
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880"
integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==
-estree-walker@^2.0.1:
+estree-walker@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
@@ -4477,6 +4303,21 @@ execa@^1.0.0:
signal-exit "^3.0.0"
strip-eof "^1.0.0"
+execa@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
+ integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
+ dependencies:
+ cross-spawn "^7.0.3"
+ get-stream "^6.0.0"
+ human-signals "^2.1.0"
+ is-stream "^2.0.0"
+ merge-stream "^2.0.0"
+ npm-run-path "^4.0.1"
+ onetime "^5.1.2"
+ signal-exit "^3.0.3"
+ strip-final-newline "^2.0.0"
+
expand-brackets@^2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
@@ -4577,28 +4418,22 @@ fast-glob@^2.2.6:
merge2 "^1.2.3"
micromatch "^3.1.10"
-fast-glob@^3.1.1:
- version "3.2.5"
- resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661"
- integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==
+fast-glob@^3.2.9:
+ version "3.2.11"
+ resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
+ integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==
dependencies:
"@nodelib/fs.stat" "^2.0.2"
"@nodelib/fs.walk" "^1.2.3"
- glob-parent "^5.1.0"
+ glob-parent "^5.1.2"
merge2 "^1.3.0"
- micromatch "^4.0.2"
- picomatch "^2.2.1"
+ micromatch "^4.0.4"
fast-json-stable-stringify@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
-fast-levenshtein@~2.0.6:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
- integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
-
fastq@^1.6.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858"
@@ -4606,13 +4441,6 @@ fastq@^1.6.0:
dependencies:
reusify "^1.0.4"
-fault@^1.0.0:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/fault/-/fault-1.0.4.tgz#eafcfc0a6d214fc94601e170df29954a4f842f13"
- integrity sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==
- dependencies:
- format "^0.2.0"
-
fb-watchman@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85"
@@ -4620,6 +4448,11 @@ fb-watchman@^2.0.0:
dependencies:
bser "2.1.1"
+fetch-retry@^5.0.2:
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/fetch-retry/-/fetch-retry-5.0.3.tgz#edfa3641892995f9afee94f25b168827aa97fe3d"
+ integrity sha512-uJQyMrX5IJZkhoEUBQ3EjxkeiZkppBd5jS/fMTJmfZxLSiaQjv2zD0kTvuvkSH89uFvgSlB6ueGpjD3HWN7Bxw==
+
figgy-pudding@^3.5.1:
version "3.5.2"
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e"
@@ -4642,16 +4475,6 @@ file-system-cache@^1.0.5:
fs-extra "^0.30.0"
ramda "^0.21.0"
-file-uri-to-path@1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
- integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
-
-filesize@6.1.0:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.1.0.tgz#e81bdaa780e2451d714d71c0d7a4f3238d37ad00"
- integrity sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg==
-
fill-range@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
@@ -4700,18 +4523,13 @@ find-cache-dir@^3.3.1:
make-dir "^3.0.2"
pkg-dir "^4.1.0"
-find-root@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
- integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
-
-find-up@4.1.0, find-up@^4.0.0, find-up@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
- integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
+find-up@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
+ integrity sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==
dependencies:
- locate-path "^5.0.0"
- path-exists "^4.0.0"
+ path-exists "^2.0.0"
+ pinkie-promise "^2.0.0"
find-up@^3.0.0:
version "3.0.0"
@@ -4720,6 +4538,14 @@ find-up@^3.0.0:
dependencies:
locate-path "^3.0.0"
+find-up@^4.0.0, find-up@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
+ integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
+ dependencies:
+ locate-path "^5.0.0"
+ path-exists "^4.0.0"
+
find-up@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
@@ -4746,7 +4572,7 @@ for-in@^1.0.2:
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
-fork-ts-checker-webpack-plugin@4.1.6, fork-ts-checker-webpack-plugin@^4.1.6:
+fork-ts-checker-webpack-plugin@^4.1.6:
version "4.1.6"
resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz#5055c703febcf37fa06405d400c122b905167fc5"
integrity sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw==
@@ -4787,11 +4613,6 @@ form-data@^3.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
-format@^0.2.0:
- version "0.2.2"
- resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b"
- integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=
-
forwarded@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
@@ -4865,15 +4686,7 @@ fs.realpath@^1.0.0:
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
-fsevents@^1.2.7:
- version "1.2.13"
- resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38"
- integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==
- dependencies:
- bindings "^1.5.0"
- nan "^2.12.1"
-
-fsevents@^2.1.2, fsevents@~2.3.1, fsevents@~2.3.2:
+fsevents@^2.1.2, fsevents@~2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
@@ -4898,31 +4711,20 @@ functions-have-names@^1.2.2:
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.2.tgz#98d93991c39da9361f8e50b337c4f6e41f120e21"
integrity sha512-bLgc3asbWdwPbx2mNk2S49kmJCuQeu0nfmaOgbs8WIyzzkw3r4htszdIi9Q9EMezDPTYuJx2wvjZ/EwgAthpnA==
-fuse.js@^3.6.1:
- version "3.6.1"
- resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-3.6.1.tgz#7de85fdd6e1b3377c23ce010892656385fd9b10c"
- integrity sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw==
-
-gauge@~2.7.3:
- version "2.7.4"
- resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
- integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=
+gauge@^3.0.0:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395"
+ integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==
dependencies:
- aproba "^1.0.3"
+ aproba "^1.0.3 || ^2.0.0"
+ color-support "^1.1.2"
console-control-strings "^1.0.0"
- has-unicode "^2.0.0"
- object-assign "^4.1.0"
+ has-unicode "^2.0.1"
+ object-assign "^4.1.1"
signal-exit "^3.0.0"
- string-width "^1.0.1"
- strip-ansi "^3.0.1"
- wide-align "^1.1.0"
-
-generic-names@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/generic-names/-/generic-names-2.0.1.tgz#f8a378ead2ccaa7a34f0317b05554832ae41b872"
- integrity sha512-kPCHWa1m9wGG/OwQpeweTwM/PYiQLrUIxXbt/P4Nic3LbGjCP0YwrALHW1uNLKZ0LIMg+RF+XRlj2ekT9ZlZAQ==
- dependencies:
- loader-utils "^1.1.0"
+ string-width "^4.2.3"
+ strip-ansi "^6.0.1"
+ wide-align "^1.1.2"
gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2:
version "1.0.0-beta.2"
@@ -4943,6 +4745,11 @@ get-package-type@^0.1.0:
resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
+get-stdin@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
+ integrity sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==
+
get-stream@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
@@ -4950,6 +4757,11 @@ get-stream@^4.0.0:
dependencies:
pump "^3.0.0"
+get-stream@^6.0.0:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
+ integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
+
get-value@^2.0.3, get-value@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
@@ -4962,21 +4774,6 @@ github-slugger@^1.0.0:
dependencies:
emoji-regex ">=6.0.0 <=6.1.1"
-glob-base@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
- integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=
- dependencies:
- glob-parent "^2.0.0"
- is-glob "^2.0.0"
-
-glob-parent@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28"
- integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=
- dependencies:
- is-glob "^2.0.0"
-
glob-parent@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
@@ -4985,7 +4782,7 @@ glob-parent@^3.1.0:
is-glob "^3.1.0"
path-dirname "^1.0.0"
-glob-parent@^5.1.0, glob-parent@~5.1.0, glob-parent@~5.1.2:
+glob-parent@^5.1.2, glob-parent@~5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
@@ -5004,6 +4801,11 @@ glob-to-regexp@^0.3.0:
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
+glob-to-regexp@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
+ integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
+
glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
version "7.1.7"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90"
@@ -5016,22 +4818,6 @@ glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
once "^1.3.0"
path-is-absolute "^1.0.0"
-global-modules@2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780"
- integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==
- dependencies:
- global-prefix "^3.0.0"
-
-global-prefix@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97"
- integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==
- dependencies:
- ini "^1.3.5"
- kind-of "^6.0.2"
- which "^1.3.1"
-
global@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406"
@@ -5052,16 +4838,16 @@ globalthis@^1.0.0:
dependencies:
define-properties "^1.1.3"
-globby@11.0.1:
- version "11.0.1"
- resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357"
- integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==
+globby@^11.0.2:
+ version "11.1.0"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
+ integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
dependencies:
array-union "^2.1.0"
dir-glob "^3.0.1"
- fast-glob "^3.1.1"
- ignore "^5.1.4"
- merge2 "^1.3.0"
+ fast-glob "^3.2.9"
+ ignore "^5.2.0"
+ merge2 "^1.4.1"
slash "^3.0.0"
globby@^9.2.0:
@@ -5078,17 +4864,10 @@ globby@^9.2.0:
pify "^4.0.1"
slash "^2.0.0"
-good-listener@^1.2.2:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
- integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=
- dependencies:
- delegate "^3.1.2"
-
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.4:
- version "4.2.6"
- resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
- integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
+ version "4.2.10"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
+ integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
graphql-tag@^2.12.5:
version "2.12.5"
@@ -5097,18 +4876,17 @@ graphql-tag@^2.12.5:
dependencies:
tslib "^2.1.0"
-gud@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0"
- integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==
-
-gzip-size@5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274"
- integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==
+handlebars@^4.7.7:
+ version "4.7.7"
+ resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1"
+ integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==
dependencies:
- duplexer "^0.1.1"
- pify "^4.0.1"
+ minimist "^1.2.5"
+ neo-async "^2.6.0"
+ source-map "^0.6.1"
+ wordwrap "^1.0.0"
+ optionalDependencies:
+ uglify-js "^3.1.4"
has-bigints@^1.0.1:
version "1.0.1"
@@ -5137,10 +4915,10 @@ has-symbols@^1.0.1, has-symbols@^1.0.2:
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423"
integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==
-has-unicode@^2.0.0:
+has-unicode@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
- integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
+ integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==
has-value@^0.3.1:
version "0.3.1"
@@ -5194,11 +4972,6 @@ hash-sum@^1.0.2:
resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04"
integrity sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=
-hash-sum@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-2.0.0.tgz#81d01bb5de8ea4a214ad5d6ead1b523460b0b45a"
- integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==
-
hash.js@^1.0.0, hash.js@^1.0.3:
version "1.1.7"
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
@@ -5280,11 +5053,6 @@ he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
-highlight.js@^10.1.1, highlight.js@~10.7.0:
- version "10.7.2"
- resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.2.tgz#89319b861edc66c48854ed1e6da21ea89f847360"
- integrity sha512-oFLl873u4usRM9K63j4ME9u3etNF0PLiJhSQ8rdfuL51Wn3zkD6drf9ZW0dOzjnZI22YYG24z30JcmfCZjMgYg==
-
hmac-drbg@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
@@ -5294,22 +5062,15 @@ hmac-drbg@^1.0.1:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
-hoist-non-react-statics@^3.3.0:
- version "3.3.2"
- resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
- integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
- dependencies:
- react-is "^16.7.0"
-
hosted-git-info@^2.1.4:
version "2.8.9"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
-html-entities@^1.2.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.4.0.tgz#cfbd1b01d2afaf9adca1b10ae7dffab98c71d2dc"
- integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==
+html-entities@^2.1.0:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46"
+ integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==
html-minifier-terser@^5.0.1:
version "5.1.1"
@@ -5324,11 +5085,6 @@ html-minifier-terser@^5.0.1:
relateurl "^0.2.7"
terser "^4.6.3"
-html-tags@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140"
- integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==
-
html-void-elements@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483"
@@ -5361,7 +5117,7 @@ htmlparser2@^3.10.1:
inherits "^2.0.1"
readable-stream "^3.1.1"
-http-errors@1.7.2:
+http-errors@1.7.2, http-errors@~1.7.2:
version "1.7.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f"
integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==
@@ -5372,22 +5128,16 @@ http-errors@1.7.2:
statuses ">= 1.5.0 < 2"
toidentifier "1.0.0"
-http-errors@~1.7.2:
- version "1.7.3"
- resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
- integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
- dependencies:
- depd "~1.1.2"
- inherits "2.0.4"
- setprototypeof "1.1.1"
- statuses ">= 1.5.0 < 2"
- toidentifier "1.0.0"
-
https-browserify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
+human-signals@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
+ integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
+
iconv-lite@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@@ -5395,11 +5145,6 @@ iconv-lite@0.4.24:
dependencies:
safer-buffer ">= 2.1.2 < 3"
-icss-replace-symbols@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
- integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=
-
icss-utils@^4.0.0, icss-utils@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467"
@@ -5407,11 +5152,6 @@ icss-utils@^4.0.0, icss-utils@^4.1.1:
dependencies:
postcss "^7.0.14"
-icss-utils@^5.0.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
- integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
-
ieee754@^1.1.4:
version "1.2.1"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
@@ -5427,15 +5167,10 @@ ignore@^4.0.3:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
-ignore@^5.1.4:
- version "5.1.8"
- resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
- integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
-
-immer@8.0.1:
- version "8.0.1"
- resolved "https://registry.yarnpkg.com/immer/-/immer-8.0.1.tgz#9c73db683e2b3975c424fb0572af5889877ae656"
- integrity sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA==
+ignore@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
+ integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
immutable@^4.0.0:
version "4.0.0"
@@ -5477,6 +5212,13 @@ imurmurhash@^0.1.4:
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
+indent-string@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80"
+ integrity sha512-aqwDFWSgSgfRaEwao5lg5KEcVd/2a+D1rvoG7NdilmYz0NwRk6StWpWdz/Hpk34MKPpx7s8XxUqimfcQK6gGlg==
+ dependencies:
+ repeating "^2.0.0"
+
indent-string@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
@@ -5495,7 +5237,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
-inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3:
+inherits@2, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -5510,11 +5252,6 @@ inherits@2.0.3:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
-ini@^1.3.5:
- version "1.3.8"
- resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
- integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
-
inline-style-parser@0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1"
@@ -5534,17 +5271,17 @@ interpret@^2.2.0:
resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==
-invariant@^2.2.3, invariant@^2.2.4:
+invariant@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
dependencies:
loose-envify "^1.0.0"
-ip@^1.1.5:
- version "1.1.5"
- resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
- integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=
+ip@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da"
+ integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==
ipaddr.js@1.9.1:
version "1.9.1"
@@ -5600,13 +5337,6 @@ is-bigint@^1.0.1:
resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a"
integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==
-is-binary-path@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
- integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=
- dependencies:
- binary-extensions "^1.0.0"
-
is-binary-path@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
@@ -5697,7 +5427,7 @@ is-directory@^0.3.1:
resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1"
integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=
-is-docker@^2.0.0:
+is-docker@^2.0.0, is-docker@^2.1.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
@@ -5730,22 +5460,15 @@ is-extendable@^1.0.1:
dependencies:
is-plain-object "^2.0.4"
-is-extglob@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0"
- integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=
-
is-extglob@^2.1.0, is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
-is-fullwidth-code-point@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
- integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
- dependencies:
- number-is-nan "^1.0.0"
+is-finite@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3"
+ integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==
is-fullwidth-code-point@^2.0.0:
version "2.0.0"
@@ -5762,13 +5485,6 @@ is-function@^1.0.2:
resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08"
integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==
-is-glob@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863"
- integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=
- dependencies:
- is-extglob "^1.0.0"
-
is-glob@^3.0.0, is-glob@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a"
@@ -5825,11 +5541,6 @@ is-plain-obj@^2.0.0:
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
-is-plain-object@3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.1.tgz#662d92d24c0aa4302407b0d45d21f2251c85f85b"
- integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==
-
is-plain-object@^2.0.3, is-plain-object@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
@@ -5850,11 +5561,6 @@ is-regex@^1.0.3, is-regex@^1.1.2, is-regex@^1.1.3:
call-bind "^1.0.2"
has-symbols "^1.0.2"
-is-root@2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c"
- integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==
-
is-set@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec"
@@ -5865,6 +5571,11 @@ is-stream@^1.1.0:
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
+is-stream@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
+ integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
+
is-string@^1.0.5, is-string@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f"
@@ -5882,6 +5593,11 @@ is-typedarray@^1.0.0:
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
+is-utf8@^0.2.0:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
+ integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==
+
is-whitespace-character@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7"
@@ -5907,7 +5623,7 @@ is-wsl@^1.1.0:
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=
-is-wsl@^2.1.1:
+is-wsl@^2.1.1, is-wsl@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271"
integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==
@@ -5946,6 +5662,14 @@ isobject@^4.0.0:
resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0"
integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==
+isomorphic-unfetch@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz#87341d5f4f7b63843d468438128cb087b7c3e98f"
+ integrity sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==
+ dependencies:
+ node-fetch "^2.6.1"
+ unfetch "^4.2.0"
+
istanbul-lib-coverage@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec"
@@ -6020,7 +5744,7 @@ jest-util@^26.6.2:
is-ci "^2.0.0"
micromatch "^4.0.2"
-jest-worker@^26.2.1, jest-worker@^26.6.2:
+jest-worker@^26.5.0, jest-worker@^26.6.2:
version "26.6.2"
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed"
integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==
@@ -6196,34 +5920,28 @@ lazy-universal-dotenv@^3.0.1:
dotenv "^8.0.0"
dotenv-expand "^5.1.0"
-levn@~0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
- integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=
- dependencies:
- prelude-ls "~1.1.2"
- type-check "~0.3.2"
-
lines-and-columns@^1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
+load-json-file@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
+ integrity sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==
+ dependencies:
+ graceful-fs "^4.1.2"
+ parse-json "^2.2.0"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+ strip-bom "^2.0.0"
+
loader-runner@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
-loader-utils@2.0.0, loader-utils@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
- integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
- dependencies:
- big.js "^5.2.2"
- emojis-list "^3.0.0"
- json5 "^2.1.2"
-
-loader-utils@^1.0.1, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0:
+loader-utils@^1.0.1, loader-utils@^1.1.0, loader-utils@^1.2.3:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613"
integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==
@@ -6232,6 +5950,15 @@ loader-utils@^1.0.1, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4
emojis-list "^3.0.0"
json5 "^1.0.1"
+loader-utils@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
+ integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
+ dependencies:
+ big.js "^5.2.2"
+ emojis-list "^3.0.0"
+ json5 "^2.1.2"
+
locate-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
@@ -6254,11 +5981,6 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
-lodash.camelcase@^4.3.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
- integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
-
lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
@@ -6281,6 +6003,14 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
+loud-rejection@^1.0.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
+ integrity sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==
+ dependencies:
+ currently-unhandled "^0.4.1"
+ signal-exit "^3.0.0"
+
lower-case@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
@@ -6288,14 +6018,6 @@ lower-case@^2.0.2:
dependencies:
tslib "^2.0.3"
-lowlight@^1.14.0:
- version "1.20.0"
- resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.20.0.tgz#ddb197d33462ad0d93bf19d17b6c301aa3941888"
- integrity sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==
- dependencies:
- fault "^1.0.0"
- highlight.js "~10.7.0"
-
lru-cache@^4.1.5:
version "4.1.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
@@ -6352,6 +6074,11 @@ map-cache@^0.2.2:
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
+map-obj@^1.0.0, map-obj@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
+ integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==
+
map-or-similar@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/map-or-similar/-/map-or-similar-1.5.0.tgz#6de2653174adfb5d9edc33c69d3e92a1b76faf08"
@@ -6369,19 +6096,6 @@ markdown-escapes@^1.0.0:
resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535"
integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==
-markdown-to-jsx@^6.11.4:
- version "6.11.4"
- resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-6.11.4.tgz#b4528b1ab668aef7fe61c1535c27e837819392c5"
- integrity sha512-3lRCD5Sh+tfA52iGgfs/XZiw33f7fFX9Bn55aNnVNUd2GzLDkOWyKYYD8Yju2B1Vn+feiEdgJs8T6Tg0xNokPw==
- dependencies:
- prop-types "^15.6.2"
- unquote "^1.1.0"
-
-markdown-to-jsx@^7.1.0:
- version "7.1.3"
- resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-7.1.3.tgz#f00bae66c0abe7dd2d274123f84cb6bd2a2c7c6a"
- integrity sha512-jtQ6VyT7rMT5tPV0g2EJakEnXLiPksnvlYtwQsVVZ611JsWGN8bQ1tVSDX4s6JllfEH6wmsYxNjTUAMrPmNA8w==
-
md5.js@^1.3.4:
version "1.3.5"
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
@@ -6464,24 +6178,33 @@ memory-fs@^0.5.0:
errno "^0.1.3"
readable-stream "^2.0.1"
+meow@^3.1.0:
+ version "3.7.0"
+ resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
+ integrity sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA==
+ dependencies:
+ camelcase-keys "^2.0.0"
+ decamelize "^1.1.2"
+ loud-rejection "^1.0.0"
+ map-obj "^1.0.1"
+ minimist "^1.1.3"
+ normalize-package-data "^2.3.4"
+ object-assign "^4.0.1"
+ read-pkg-up "^1.0.1"
+ redent "^1.0.0"
+ trim-newlines "^1.0.0"
+
merge-descriptors@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
-merge-source-map@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646"
- integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==
- dependencies:
- source-map "^0.6.1"
-
merge-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
-merge2@^1.2.3, merge2@^1.3.0:
+merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
@@ -6515,13 +6238,13 @@ micromatch@^3.1.10, micromatch@^3.1.4:
snapdragon "^0.8.1"
to-regex "^3.0.2"
-micromatch@^4.0.0, micromatch@^4.0.2:
- version "4.0.4"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9"
- integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==
+micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
+ integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
dependencies:
- braces "^3.0.1"
- picomatch "^2.2.3"
+ braces "^3.0.2"
+ picomatch "^2.3.1"
miller-rabin@^4.0.0:
version "4.0.1"
@@ -6531,17 +6254,17 @@ miller-rabin@^4.0.0:
bn.js "^4.0.0"
brorand "^1.0.1"
-mime-db@1.47.0:
- version "1.47.0"
- resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.47.0.tgz#8cb313e59965d3c05cfbf898915a267af46a335c"
- integrity sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==
+mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
+ version "1.52.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+ integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
-mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.24:
- version "2.1.30"
- resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.30.tgz#6e7be8b4c479825f85ed6326695db73f9305d62d"
- integrity sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==
+mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34:
+ version "2.1.35"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
- mime-db "1.47.0"
+ mime-db "1.52.0"
mime@1.6.0:
version "1.6.0"
@@ -6553,6 +6276,11 @@ mime@^2.4.4:
resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe"
integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==
+mimic-fn@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
+ integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+
min-document@^2.19.0:
version "2.19.0"
resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
@@ -6570,17 +6298,17 @@ minimalistic-crypto-utils@^1.0.1:
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
-minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4:
+minimatch@^3.0.2, minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
dependencies:
brace-expansion "^1.1.7"
-minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5:
- version "1.2.5"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
- integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
+minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5:
+ version "1.2.6"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
+ integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
minipass-collect@^1.0.2:
version "1.0.2"
@@ -6676,25 +6404,15 @@ ms@2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
-ms@2.1.2:
+ms@2.1.2, ms@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
-ms@^2.1.1:
- version "2.1.3"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
- integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
-
-nan@^2.12.1:
- version "2.14.2"
- resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
- integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
-
-nanoid@^3.1.23:
- version "3.1.23"
- resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81"
- integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==
+nanoid@^3.1.23, nanoid@^3.3.1:
+ version "3.3.4"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
+ integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
nanomatch@^1.2.9:
version "1.2.13"
@@ -6713,12 +6431,12 @@ nanomatch@^1.2.9:
snapdragon "^0.8.1"
to-regex "^3.0.1"
-negotiator@0.6.2:
- version "0.6.2"
- resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
- integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
+negotiator@0.6.3:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
+ integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
-neo-async@^2.5.0, neo-async@^2.6.1:
+neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1:
version "2.6.2"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
@@ -6748,10 +6466,12 @@ node-dir@^0.1.17:
dependencies:
minimatch "^3.0.2"
-node-fetch@^2.6.1:
- version "2.6.1"
- resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
- integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
+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==
+ dependencies:
+ whatwg-url "^5.0.0"
node-int64@^0.4.0:
version "0.4.0"
@@ -6792,12 +6512,12 @@ node-modules-regexp@^1.0.0:
resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40"
integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=
-node-releases@^1.1.61, node-releases@^1.1.71:
- version "1.1.72"
- resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe"
- integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==
+node-releases@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
+ integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==
-normalize-package-data@^2.5.0:
+normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
@@ -6831,15 +6551,22 @@ npm-run-path@^2.0.0:
dependencies:
path-key "^2.0.0"
-npmlog@^4.1.2:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
- integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
+npm-run-path@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
+ integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
+ dependencies:
+ path-key "^3.0.0"
+
+npmlog@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0"
+ integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==
dependencies:
- are-we-there-yet "~1.1.2"
- console-control-strings "~1.1.0"
- gauge "~2.7.3"
- set-blocking "~2.0.0"
+ are-we-there-yet "^2.0.0"
+ console-control-strings "^1.1.0"
+ gauge "^3.0.0"
+ set-blocking "^2.0.0"
nth-check@^1.0.2:
version "1.0.2"
@@ -6853,12 +6580,7 @@ num2fraction@^1.2.2:
resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=
-number-is-nan@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
- integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
-
-object-assign@^4.1.0, object-assign@^4.1.1:
+object-assign@^4.0.1, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
@@ -6950,6 +6672,11 @@ on-finished@~2.3.0:
dependencies:
ee-first "1.1.1"
+on-headers@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
+ integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
+
once@^1.3.0, once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@@ -6957,7 +6684,14 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
dependencies:
wrappy "1"
-open@^7.0.2, open@^7.0.3:
+onetime@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
+ integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
+ dependencies:
+ mimic-fn "^2.1.0"
+
+open@^7.0.3:
version "7.4.2"
resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321"
integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==
@@ -6965,27 +6699,24 @@ open@^7.0.2, open@^7.0.3:
is-docker "^2.0.0"
is-wsl "^2.1.1"
-optionator@^0.8.1:
- version "0.8.3"
- resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
- integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==
+open@^8.4.0:
+ version "8.4.0"
+ resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8"
+ integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==
dependencies:
- deep-is "~0.1.3"
- fast-levenshtein "~2.0.6"
- levn "~0.3.0"
- prelude-ls "~1.1.2"
- type-check "~0.3.2"
- word-wrap "~1.2.3"
+ define-lazy-prop "^2.0.0"
+ is-docker "^2.1.1"
+ is-wsl "^2.2.0"
os-browserify@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=
-overlayscrollbars@^1.13.1:
- version "1.13.1"
- resolved "https://registry.yarnpkg.com/overlayscrollbars/-/overlayscrollbars-1.13.1.tgz#0b840a88737f43a946b9d87875a2f9e421d0338a"
- integrity sha512-gIQfzgGgu1wy80EB4/6DaJGHMEGmizq27xHIESrzXq0Y/J0Ay1P3DWk6tuVmEPIZH15zaBlxeEJOqdJKmowHCQ==
+os-homedir@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
+ integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==
p-all@^2.1.0:
version "2.1.0"
@@ -7131,6 +6862,13 @@ parse-entities@^2.0.0:
is-decimal "^1.0.0"
is-hexadecimal "^1.0.0"
+parse-json@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9"
+ integrity sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==
+ dependencies:
+ error-ex "^1.2.0"
+
parse-json@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
@@ -7182,6 +6920,13 @@ path-dirname@^1.0.0:
resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=
+path-exists@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
+ integrity sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==
+ dependencies:
+ pinkie-promise "^2.0.0"
+
path-exists@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
@@ -7202,7 +6947,7 @@ path-key@^2.0.0, path-key@^2.0.1:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
-path-key@^3.1.0:
+path-key@^3.0.0, path-key@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
@@ -7217,6 +6962,15 @@ path-to-regexp@0.1.7:
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
+path-type@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
+ integrity sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==
+ dependencies:
+ graceful-fs "^4.1.2"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+
path-type@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
@@ -7240,10 +6994,25 @@ pbkdf2@^3.0.3:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
-picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3:
+picocolors@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f"
+ integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==
+
+picocolors@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+ integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.0, picomatch@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+ integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+pify@^2.0.0:
version "2.3.0"
- resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972"
- integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==
+ resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+ integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==
pify@^3.0.0:
version "3.0.0"
@@ -7255,6 +7024,18 @@ pify@^4.0.1:
resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
+pinkie-promise@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
+ integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==
+ dependencies:
+ pinkie "^2.0.0"
+
+pinkie@^2.0.0:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
+ integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==
+
pirates@^4.0.0, pirates@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87"
@@ -7283,13 +7064,6 @@ pkg-dir@^5.0.0:
dependencies:
find-up "^5.0.0"
-pkg-up@3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
- integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
- dependencies:
- find-up "^3.0.0"
-
pnp-webpack-plugin@1.6.4:
version "1.6.4"
resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149"
@@ -7297,12 +7071,12 @@ pnp-webpack-plugin@1.6.4:
dependencies:
ts-pnp "^1.1.6"
-polished@^4.0.5:
- version "4.1.3"
- resolved "https://registry.yarnpkg.com/polished/-/polished-4.1.3.tgz#7a3abf2972364e7d97770b827eec9a9e64002cfc"
- integrity sha512-ocPAcVBUOryJEKe0z2KLd1l9EBa1r5mSwlKpExmrLzsnIzJo4axsoU9O2BjOTkDGDT4mZ0WFE5XKTlR3nLnZOA==
+polished@^4.2.2:
+ version "4.2.2"
+ resolved "https://registry.yarnpkg.com/polished/-/polished-4.2.2.tgz#2529bb7c3198945373c52e34618c8fe7b1aa84d1"
+ integrity sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==
dependencies:
- "@babel/runtime" "^7.14.0"
+ "@babel/runtime" "^7.17.8"
posix-character-classes@^0.1.0:
version "0.1.1"
@@ -7352,11 +7126,6 @@ postcss-modules-extract-imports@^2.0.0:
dependencies:
postcss "^7.0.5"
-postcss-modules-extract-imports@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d"
- integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==
-
postcss-modules-local-by-default@^3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0"
@@ -7367,15 +7136,6 @@ postcss-modules-local-by-default@^3.0.2:
postcss-selector-parser "^6.0.2"
postcss-value-parser "^4.1.0"
-postcss-modules-local-by-default@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c"
- integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==
- dependencies:
- icss-utils "^5.0.0"
- postcss-selector-parser "^6.0.2"
- postcss-value-parser "^4.1.0"
-
postcss-modules-scope@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee"
@@ -7384,13 +7144,6 @@ postcss-modules-scope@^2.2.0:
postcss "^7.0.6"
postcss-selector-parser "^6.0.0"
-postcss-modules-scope@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06"
- integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==
- dependencies:
- postcss-selector-parser "^6.0.4"
-
postcss-modules-values@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10"
@@ -7399,28 +7152,7 @@ postcss-modules-values@^3.0.0:
icss-utils "^4.0.0"
postcss "^7.0.6"
-postcss-modules-values@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c"
- integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==
- dependencies:
- icss-utils "^5.0.0"
-
-postcss-modules@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules/-/postcss-modules-4.0.0.tgz#2bc7f276ab88f3f1b0fadf6cbd7772d43b5f3b9b"
- integrity sha512-ghS/ovDzDqARm4Zj6L2ntadjyQMoyJmi0JkLlYtH2QFLrvNlxH5OAVRPWPeKilB0pY7SbuhO173KOWkPAxRJcw==
- dependencies:
- generic-names "^2.0.1"
- icss-replace-symbols "^1.1.0"
- lodash.camelcase "^4.3.0"
- postcss-modules-extract-imports "^3.0.0"
- postcss-modules-local-by-default "^4.0.0"
- postcss-modules-scope "^3.0.0"
- postcss-modules-values "^4.0.0"
- string-hash "^1.1.1"
-
-postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4:
+postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2:
version "6.0.6"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.6.tgz#2c5bba8174ac2f6981ab631a42ab0ee54af332ea"
integrity sha512-9LXrvaaX3+mcv5xkg5kFwqSzSH1JIObIx51PrndZwlmznwXRfxMddDvo9gve3gVR8ZTKgoFDdWkbRFmEhT4PMg==
@@ -7433,14 +7165,13 @@ postcss-value-parser@^4.1.0:
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb"
integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==
-postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.35, postcss@^7.0.5, postcss@^7.0.6:
- version "7.0.35"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24"
- integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==
+postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.36, postcss@^7.0.5, postcss@^7.0.6:
+ version "7.0.39"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309"
+ integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==
dependencies:
- chalk "^2.4.2"
+ picocolors "^0.2.1"
source-map "^0.6.1"
- supports-color "^6.1.0"
postcss@^8.1.10:
version "8.3.0"
@@ -7451,15 +7182,10 @@ postcss@^8.1.10:
nanoid "^3.1.23"
source-map-js "^0.6.2"
-prelude-ls@~1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
- integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
-
-prettier@~2.2.1:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5"
- integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==
+"prettier@>=2.2.1 <=2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18"
+ integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==
pretty-error@^2.1.1:
version "2.1.2"
@@ -7474,13 +7200,6 @@ pretty-hrtime@^1.0.3:
resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1"
integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=
-prismjs@^1.21.0, prismjs@~1.23.0:
- version "1.23.0"
- resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.23.0.tgz#d3b3967f7d72440690497652a9d40ff046067f33"
- integrity sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA==
- optionalDependencies:
- clipboard "^2.0.0"
-
private@^0.1.8:
version "0.1.8"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
@@ -7529,14 +7248,6 @@ promise@^7.0.1:
dependencies:
asap "~2.0.3"
-prompts@2.4.0:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.0.tgz#4aa5de0723a231d1ee9121c40fdf663df73f61d7"
- integrity sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==
- dependencies:
- kleur "^3.0.3"
- sisteransi "^1.0.5"
-
prompts@^2.4.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.1.tgz#befd3b1195ba052f9fd2fde8a486c4e82ee77f61"
@@ -7545,7 +7256,7 @@ prompts@^2.4.0:
kleur "^3.0.3"
sisteransi "^1.0.5"
-prop-types@^15.0.0, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
+prop-types@^15.0.0, prop-types@^15.6.2, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -7719,16 +7430,11 @@ pumpify@^1.3.3:
inherits "^2.0.3"
pump "^2.0.0"
-punycode@1.3.2:
+punycode@1.3.2, punycode@^1.2.4:
version "1.3.2"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
-punycode@^1.2.4:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
- integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
-
punycode@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
@@ -7751,16 +7457,11 @@ querystring-es3@^0.2.0:
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=
-querystring@0.2.0:
+querystring@0.2.0, querystring@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
-querystring@^0.2.0:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd"
- integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==
-
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
@@ -7809,41 +7510,6 @@ raw-loader@^4.0.2:
loader-utils "^2.0.0"
schema-utils "^3.0.0"
-react-colorful@^5.0.1:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/react-colorful/-/react-colorful-5.2.0.tgz#1cd24ca7deff73a70cf34261813a4ed6c45129c2"
- integrity sha512-SJXywyc9oew0rOp7xjmtStiJ5N4Mk2RYc/1OptlaEnbuCz9PvILmRotoHfowdmO142HASUSgKdngL4WOHo/TnQ==
-
-react-dev-utils@^11.0.3:
- version "11.0.4"
- resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-11.0.4.tgz#a7ccb60257a1ca2e0efe7a83e38e6700d17aa37a"
- integrity sha512-dx0LvIGHcOPtKbeiSUM4jqpBl3TcY7CDjZdfOIcKeznE7BWr9dg0iPG90G5yfVQ+p/rGNMXdbfStvzQZEVEi4A==
- dependencies:
- "@babel/code-frame" "7.10.4"
- address "1.1.2"
- browserslist "4.14.2"
- chalk "2.4.2"
- cross-spawn "7.0.3"
- detect-port-alt "1.1.6"
- escape-string-regexp "2.0.0"
- filesize "6.1.0"
- find-up "4.1.0"
- fork-ts-checker-webpack-plugin "4.1.6"
- global-modules "2.0.0"
- globby "11.0.1"
- gzip-size "5.1.1"
- immer "8.0.1"
- is-root "2.1.0"
- loader-utils "2.0.0"
- open "^7.0.2"
- pkg-up "3.1.0"
- prompts "2.4.0"
- react-error-overlay "^6.0.9"
- recursive-readdir "2.2.2"
- shell-quote "1.7.2"
- strip-ansi "6.0.0"
- text-table "0.2.0"
-
react-dom@16.14.0:
version "16.14.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89"
@@ -7854,43 +7520,6 @@ react-dom@16.14.0:
prop-types "^15.6.2"
scheduler "^0.19.1"
-react-draggable@^4.4.3:
- version "4.4.3"
- resolved "https://registry.yarnpkg.com/react-draggable/-/react-draggable-4.4.3.tgz#0727f2cae5813e36b0e4962bf11b2f9ef2b406f3"
- integrity sha512-jV4TE59MBuWm7gb6Ns3Q1mxX8Azffb7oTtDtBgFkxRvhDp38YAARmRplrj0+XGkhOJB5XziArX+4HUUABtyZ0w==
- dependencies:
- classnames "^2.2.5"
- prop-types "^15.6.0"
-
-react-element-to-jsx-string@^14.3.2:
- version "14.3.2"
- resolved "https://registry.yarnpkg.com/react-element-to-jsx-string/-/react-element-to-jsx-string-14.3.2.tgz#c0000ed54d1f8b4371731b669613f2d4e0f63d5c"
- integrity sha512-WZbvG72cjLXAxV7VOuSzuHEaI3RHj10DZu8EcKQpkKcAj7+qAkG5XUeSdX5FXrA0vPrlx0QsnAzZEBJwzV0e+w==
- dependencies:
- "@base2/pretty-print-object" "1.0.0"
- is-plain-object "3.0.1"
-
-react-error-overlay@^6.0.9:
- version "6.0.9"
- resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
- integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==
-
-react-fast-compare@^3.0.1, react-fast-compare@^3.2.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
- integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==
-
-react-helmet-async@^1.0.7:
- version "1.0.9"
- resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.0.9.tgz#5b9ed2059de6b4aab47f769532f9fbcbce16c5ca"
- integrity sha512-N+iUlo9WR3/u9qGMmP4jiYfaD6pe9IvDTapZLFJz2D3xlTlCM1Bzy4Ab3g72Nbajo/0ZyW+W9hdz8Hbe4l97pQ==
- dependencies:
- "@babel/runtime" "^7.12.5"
- invariant "^2.2.4"
- prop-types "^15.7.2"
- react-fast-compare "^3.2.0"
- shallowequal "^1.1.0"
-
react-inspector@^5.1.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-5.1.1.tgz#58476c78fde05d5055646ed8ec02030af42953c8"
@@ -7900,38 +7529,11 @@ react-inspector@^5.1.0:
is-dom "^1.0.0"
prop-types "^15.0.0"
-react-is@^16.7.0, react-is@^16.8.1:
+react-is@^16.8.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
-react-is@^17.0.2:
- version "17.0.2"
- resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
- integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
-
-react-lifecycles-compat@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
- integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
-
-react-popper-tooltip@^3.1.1:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/react-popper-tooltip/-/react-popper-tooltip-3.1.1.tgz#329569eb7b287008f04fcbddb6370452ad3f9eac"
- integrity sha512-EnERAnnKRptQBJyaee5GJScWNUKQPDD2ywvzZyUjst/wj5U64C8/CnSYLNEmP2hG0IJ3ZhtDxE8oDN+KOyavXQ==
- dependencies:
- "@babel/runtime" "^7.12.5"
- "@popperjs/core" "^2.5.4"
- react-popper "^2.2.4"
-
-react-popper@^2.2.4:
- version "2.2.5"
- resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.2.5.tgz#1214ef3cec86330a171671a4fbcbeeb65ee58e96"
- integrity sha512-kxGkS80eQGtLl18+uig1UIf9MKixFSyPxglsgLBxlYnyDf65BiY9B3nZSc6C9XUNDgStROB0fMQlTEz1KxGddw==
- dependencies:
- react-fast-compare "^3.0.1"
- warning "^4.0.2"
-
react-sizeme@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/react-sizeme/-/react-sizeme-3.0.1.tgz#4d12f4244e0e6a0fb97253e7af0314dc7c83a5a0"
@@ -7942,26 +7544,6 @@ react-sizeme@^3.0.1:
shallowequal "^1.1.0"
throttle-debounce "^3.0.1"
-react-syntax-highlighter@^13.5.3:
- version "13.5.3"
- resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-13.5.3.tgz#9712850f883a3e19eb858cf93fad7bb357eea9c6"
- integrity sha512-crPaF+QGPeHNIblxxCdf2Lg936NAHKhNhuMzRL3F9ct6aYXL3NcZtCL0Rms9+qVo6Y1EQLdXGypBNSbPL/r+qg==
- dependencies:
- "@babel/runtime" "^7.3.1"
- highlight.js "^10.1.1"
- lowlight "^1.14.0"
- prismjs "^1.21.0"
- refractor "^3.1.0"
-
-react-textarea-autosize@^8.3.0:
- version "8.3.2"
- resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.3.2.tgz#4f9374d357b0a6f6469956726722549124a1b2db"
- integrity sha512-JrMWVgQSaExQByP3ggI1eA8zF4mF0+ddVuX7acUeK2V7bmrpjVOY72vmLz2IXFJSAXoY3D80nEzrn0GWajWK3Q==
- dependencies:
- "@babel/runtime" "^7.10.2"
- use-composed-ref "^1.0.0"
- use-latest "^1.0.0"
-
react@16.14.0:
version "16.14.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d"
@@ -7971,6 +7553,14 @@ react@16.14.0:
object-assign "^4.1.1"
prop-types "^15.6.2"
+read-pkg-up@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
+ integrity sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==
+ dependencies:
+ find-up "^1.0.0"
+ read-pkg "^1.0.0"
+
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"
@@ -7980,6 +7570,15 @@ read-pkg-up@^7.0.1:
read-pkg "^5.2.0"
type-fest "^0.8.1"
+read-pkg@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
+ integrity sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==
+ dependencies:
+ load-json-file "^1.0.0"
+ normalize-package-data "^2.3.2"
+ path-type "^1.0.0"
+
read-pkg@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc"
@@ -7990,7 +7589,7 @@ read-pkg@^5.2.0:
parse-json "^5.0.0"
type-fest "^0.6.0"
-"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6:
+"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@@ -8012,22 +7611,6 @@ readable-stream@^3.1.1, readable-stream@^3.6.0:
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
-readdirp@^2.2.1:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525"
- integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==
- dependencies:
- graceful-fs "^4.1.11"
- micromatch "^3.1.10"
- readable-stream "^2.0.2"
-
-readdirp@~3.5.0:
- version "3.5.0"
- resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e"
- integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==
- dependencies:
- picomatch "^2.2.1"
-
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
@@ -8035,12 +7618,12 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
-recast@0.20.4:
- version "0.20.4"
- resolved "https://registry.yarnpkg.com/recast/-/recast-0.20.4.tgz#db55983eac70c46b3fff96c8e467d65ffb4a7abc"
- integrity sha512-6qLIBGGRcwjrTZGIiBpJVC/NeuXpogXNyRQpqU1zWPUigCphvApoCs9KIwDYh1eDuJ6dAFlQoi/QUyE5KQ6RBQ==
+recast@0.21.1:
+ version "0.21.1"
+ resolved "https://registry.yarnpkg.com/recast/-/recast-0.21.1.tgz#9b3f4f68c1fe9c1513a1c02ff572fdef02231de2"
+ integrity sha512-PF61BHLaOGF5oIKTpSrDM6Qfy2d7DIx5qblgqG+wjqHuFH97OgAqBYFIJwEuHTrM6pQGT17IJ8D0C/jVu/0tig==
dependencies:
- ast-types "0.14.2"
+ ast-types "0.15.2"
esprima "~4.0.0"
source-map "~0.6.1"
tslib "^2.0.1"
@@ -8055,21 +7638,13 @@ recast@^0.18.1:
private "^0.1.8"
source-map "~0.6.1"
-recursive-readdir@2.2.2:
- version "2.2.2"
- resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f"
- integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==
- dependencies:
- minimatch "3.0.4"
-
-refractor@^3.1.0:
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/refractor/-/refractor-3.3.1.tgz#ebbc04b427ea81dc25ad333f7f67a0b5f4f0be3a"
- integrity sha512-vaN6R56kLMuBszHSWlwTpcZ8KTMG6aUCok4GrxYDT20UIOXxOc5o6oDc8tNTzSlH3m2sI+Eu9Jo2kVdDcUTWYw==
+redent@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde"
+ integrity sha512-qtW5hKzGQZqKoh6JNSD+4lfitfPKGz42e6QwiRmPM5mmKtR0N41AbJRYu0xJi7nhOJ4WDgRkKvAk6tw4WIwR4g==
dependencies:
- hastscript "^6.0.0"
- parse-entities "^2.0.0"
- prismjs "~1.23.0"
+ indent-string "^2.1.0"
+ strip-indent "^1.0.1"
regenerate-unicode-properties@^8.2.0:
version "8.2.0"
@@ -8234,6 +7809,13 @@ repeat-string@^1.5.4, repeat-string@^1.6.1:
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
+repeating@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda"
+ integrity sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A==
+ dependencies:
+ is-finite "^1.0.0"
+
resolve-from@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
@@ -8254,7 +7836,7 @@ resolve-url@^0.2.1:
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
-resolve@^1.10.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.15.1, resolve@^1.19.0, resolve@^1.3.2:
+resolve@^1.10.0, resolve@^1.14.2, resolve@^1.15.1, resolve@^1.19.0, resolve@^1.3.2:
version "1.20.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975"
integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==
@@ -8272,10 +7854,10 @@ reusify@^1.0.4:
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
-rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.3:
- version "2.7.1"
- resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
- integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
+rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.3, rimraf@~2.6.2:
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
+ integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
dependencies:
glob "^7.1.3"
@@ -8286,13 +7868,6 @@ rimraf@^3.0.2:
dependencies:
glob "^7.1.3"
-rimraf@~2.6.2:
- version "2.6.3"
- resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
- integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
- dependencies:
- glob "^7.1.3"
-
ripemd160@^2.0.0, ripemd160@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
@@ -8325,12 +7900,12 @@ safe-buffer@5.1.1:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
integrity sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==
-safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
-safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0:
+safe-buffer@^5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@@ -8390,7 +7965,7 @@ scheduler@^0.19.1:
loose-envify "^1.1.0"
object-assign "^4.1.1"
-schema-utils@2.7.0:
+schema-utils@2.7.0, schema-utils@^2.6.5, schema-utils@^2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7"
integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==
@@ -8408,29 +7983,15 @@ schema-utils@^1.0.0:
ajv-errors "^1.0.0"
ajv-keywords "^3.1.0"
-schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7.0:
- version "2.7.1"
- resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7"
- integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==
- dependencies:
- "@types/json-schema" "^7.0.5"
- ajv "^6.12.4"
- ajv-keywords "^3.5.2"
-
schema-utils@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef"
- integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281"
+ integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==
dependencies:
- "@types/json-schema" "^7.0.6"
+ "@types/json-schema" "^7.0.8"
ajv "^6.12.5"
ajv-keywords "^3.5.2"
-select@^1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
- integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=
-
"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
@@ -8479,6 +8040,13 @@ serialize-javascript@^4.0.0:
dependencies:
randombytes "^2.1.0"
+serialize-javascript@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4"
+ integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==
+ dependencies:
+ randombytes "^2.1.0"
+
serve-favicon@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/serve-favicon/-/serve-favicon-2.5.0.tgz#935d240cdfe0f5805307fdfe967d88942a2cbcf0"
@@ -8500,10 +8068,10 @@ serve-static@1.14.1:
parseurl "~1.3.3"
send "0.17.1"
-set-blocking@~2.0.0:
+set-blocking@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
- integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
+ integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
set-value@^2.0.0, set-value@^2.0.1:
version "2.0.1"
@@ -8569,11 +8137,6 @@ shebang-regex@^3.0.0:
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
-shell-quote@1.7.2:
- version "1.7.2"
- resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2"
- integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==
-
side-channel@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
@@ -8583,10 +8146,10 @@ side-channel@^1.0.4:
get-intrinsic "^1.0.2"
object-inspect "^1.9.0"
-signal-exit@^3.0.0, signal-exit@^3.0.2:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
- integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
+signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3:
+ 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==
sisteransi@^1.0.5:
version "1.0.5"
@@ -8638,12 +8201,7 @@ source-list-map@^2.0.0:
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
-"source-map-js@>=0.6.2 <2.0.0":
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
- integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
-
-source-map-js@^0.6.2:
+"source-map-js@>=0.6.2 <2.0.0", source-map-js@^0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==
@@ -8659,10 +8217,10 @@ source-map-resolve@^0.5.0:
source-map-url "^0.4.0"
urix "^0.1.0"
-source-map-support@^0.5.16, source-map-support@~0.5.12:
- version "0.5.19"
- resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
- integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
+source-map-support@^0.5.16, source-map-support@~0.5.12, source-map-support@~0.5.20:
+ version "0.5.21"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
+ integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"
@@ -8672,7 +8230,7 @@ source-map-url@^0.4.0:
resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56"
integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==
-source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7:
+source-map@^0.5.0, source-map@^0.5.6:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
@@ -8814,27 +8372,14 @@ stream-shift@^1.0.0:
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==
-string-hash@^1.1.1:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b"
- integrity sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=
-
-string-width@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
- integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
- dependencies:
- code-point-at "^1.0.0"
- is-fullwidth-code-point "^1.0.0"
- strip-ansi "^3.0.0"
-
-"string-width@^1.0.2 || 2":
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
- integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
+"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
- is-fullwidth-code-point "^2.0.0"
- strip-ansi "^4.0.0"
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
string-width@^3.0.0:
version "3.1.0"
@@ -8845,15 +8390,6 @@ string-width@^3.0.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^5.1.0"
-string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0:
- version "4.2.2"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5"
- integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==
- dependencies:
- emoji-regex "^8.0.0"
- is-fullwidth-code-point "^3.0.0"
- strip-ansi "^6.0.0"
-
"string.prototype.matchall@^4.0.0 || ^3.0.1":
version "4.0.5"
resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.5.tgz#59370644e1db7e4c0c045277690cf7b01203c4da"
@@ -8902,41 +8438,20 @@ string.prototype.trimstart@^1.0.4:
call-bind "^1.0.2"
define-properties "^1.1.3"
-string_decoder@^1.0.0, string_decoder@^1.1.1:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
- integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
- dependencies:
- safe-buffer "~5.2.0"
-
-string_decoder@~1.1.1:
+string_decoder@^1.0.0, string_decoder@^1.1.1, string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
-strip-ansi@6.0.0, strip-ansi@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
- integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==
- dependencies:
- ansi-regex "^5.0.0"
-
-strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+strip-ansi@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
dependencies:
ansi-regex "^2.0.0"
-strip-ansi@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
- integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
- dependencies:
- ansi-regex "^3.0.0"
-
strip-ansi@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
@@ -8944,11 +8459,37 @@ strip-ansi@^5.1.0:
dependencies:
ansi-regex "^4.1.0"
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-bom@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
+ integrity sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==
+ dependencies:
+ is-utf8 "^0.2.0"
+
strip-eof@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
+strip-final-newline@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
+ integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
+
+strip-indent@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2"
+ integrity sha512-I5iQq6aFMM62fBEAIB/hXzwJD6EEZ0xEGCX2t7oXqaKPIRgt4WruAQ285BISgdkP+HLGWyeGmNJcpIwFeRYRUA==
+ dependencies:
+ get-stdin "^4.0.1"
+
style-loader@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.3.0.tgz#828b4a3b3b7e7aa5847ce7bae9e874512114249e"
@@ -8971,13 +8512,6 @@ supports-color@^5.3.0:
dependencies:
has-flag "^3.0.0"
-supports-color@^6.1.0:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
- integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
- dependencies:
- has-flag "^3.0.0"
-
supports-color@^7.0.0, supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
@@ -8995,6 +8529,11 @@ symbol.prototype.description@^1.0.0:
has-symbols "^1.0.1"
object.getownpropertydescriptors "^2.1.2"
+synchronous-promise@^2.0.15:
+ version "2.0.15"
+ resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.15.tgz#07ca1822b9de0001f5ff73595f3d08c4f720eb8e"
+ integrity sha512-k8uzYIkIVwmT+TcglpdN50pS2y1BDcUnBPK9iJeGu0Pl1lOI8pD6wtzgw91Pjpe+RxtTncw32tLxs/R0yNL2Mg==
+
tapable@^1.0.0, tapable@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
@@ -9012,10 +8551,10 @@ tar@^6.0.2:
mkdirp "^1.0.3"
yallist "^4.0.0"
-telejson@^5.1.0:
- version "5.3.3"
- resolved "https://registry.yarnpkg.com/telejson/-/telejson-5.3.3.tgz#fa8ca84543e336576d8734123876a9f02bf41d2e"
- integrity sha512-PjqkJZpzEggA9TBpVtJi1LVptP7tYtXB6rEubwlHap76AMjzvOdKX41CxyaW7ahhzDU1aftXnMCx5kAPDZTQBA==
+telejson@^6.0.8:
+ version "6.0.8"
+ resolved "https://registry.yarnpkg.com/telejson/-/telejson-6.0.8.tgz#1c432db7e7a9212c1fbd941c3e5174ec385148f7"
+ integrity sha512-nerNXi+j8NK1QEfBHtZUN/aLdDcyupA//9kAboYLrtzZlPLpUfqbVGWb9zz91f/mIjRbAYhbgtnJHY8I1b5MBg==
dependencies:
"@types/is-function" "^1.0.0"
global "^4.4.0"
@@ -9033,11 +8572,6 @@ temp@^0.8.1:
dependencies:
rimraf "~2.6.2"
-term-size@^2.1.0:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54"
- integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==
-
terser-webpack-plugin@^1.4.3:
version "1.4.5"
resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b"
@@ -9053,22 +8587,22 @@ terser-webpack-plugin@^1.4.3:
webpack-sources "^1.4.0"
worker-farm "^1.7.0"
-terser-webpack-plugin@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-3.1.0.tgz#91e6d39571460ed240c0cf69d295bcf30ebf98cb"
- integrity sha512-cjdZte66fYkZ65rQ2oJfrdCAkkhJA7YLYk5eGOcGCSGlq0ieZupRdjedSQXYknMPo2IveQL+tPdrxUkERENCFA==
+terser-webpack-plugin@^4.2.3:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz#28daef4a83bd17c1db0297070adc07fc8cfc6a9a"
+ integrity sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==
dependencies:
cacache "^15.0.5"
find-cache-dir "^3.3.1"
- jest-worker "^26.2.1"
+ jest-worker "^26.5.0"
p-limit "^3.0.2"
- schema-utils "^2.6.6"
- serialize-javascript "^4.0.0"
+ schema-utils "^3.0.0"
+ serialize-javascript "^5.0.1"
source-map "^0.6.1"
- terser "^4.8.0"
+ terser "^5.3.4"
webpack-sources "^1.4.3"
-terser@^4.1.2, terser@^4.6.3, terser@^4.8.0:
+terser@^4.1.2, terser@^4.6.3:
version "4.8.0"
resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17"
integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==
@@ -9077,6 +8611,16 @@ terser@^4.1.2, terser@^4.6.3, terser@^4.8.0:
source-map "~0.6.1"
source-map-support "~0.5.12"
+terser@^5.3.4:
+ version "5.14.2"
+ resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10"
+ integrity sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==
+ dependencies:
+ "@jridgewell/source-map" "^0.3.2"
+ acorn "^8.5.0"
+ commander "^2.20.0"
+ source-map-support "~0.5.20"
+
test-exclude@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"
@@ -9086,11 +8630,6 @@ test-exclude@^6.0.0:
glob "^7.1.4"
minimatch "^3.0.4"
-text-table@0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
- integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
-
throttle-debounce@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb"
@@ -9111,11 +8650,6 @@ timers-browserify@^2.0.4:
dependencies:
setimmediate "^1.0.4"
-tiny-emitter@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
- integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
-
tmpl@1.0.x:
version "1.0.4"
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1"
@@ -9163,11 +8697,6 @@ to-regex@^3.0.1, to-regex@^3.0.2:
regex-not "^1.0.2"
safe-regex "^1.1.0"
-toggle-selection@^1.0.6:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
- integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI=
-
toidentifier@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
@@ -9178,6 +8707,16 @@ token-stream@1.0.0:
resolved "https://registry.yarnpkg.com/token-stream/-/token-stream-1.0.0.tgz#cc200eab2613f4166d27ff9afc7ca56d49df6eb4"
integrity sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=
+tr46@~0.0.3:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
+ integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
+
+trim-newlines@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
+ integrity sha512-Nm4cF79FhSTzrLKGDMi3I4utBtFv8qKy4sq1enftf2gMdpqI8oVQTAfySkTz5r49giVzDj88SVZXP4CeYQwjaw==
+
trim-trailing-lines@^1.0.0:
version "1.1.4"
resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0"
@@ -9198,11 +8737,6 @@ ts-dedent@^2.0.0:
resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.1.1.tgz#6dd56870bb5493895171334fa5d7e929107e5bbc"
integrity sha512-riHuwnzAUCfdIeTBNUq7+Yj+ANnrMXo/7+Z74dIdudS7ys2k8aSGMzpJRMFDF7CLwUTbtvi1ZZff/Wl+XxmqIA==
-ts-essentials@^2.0.3:
- version "2.0.12"
- resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-2.0.12.tgz#c9303f3d74f75fa7528c3d49b80e089ab09d8745"
- integrity sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w==
-
ts-loader@^8.0.14:
version "8.3.0"
resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-8.3.0.tgz#83360496d6f8004fab35825279132c93412edf33"
@@ -9224,12 +8758,7 @@ ts-pnp@^1.1.6:
resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92"
integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==
-tslib@^2.0.1, tslib@^2.0.3:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
- integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==
-
-tslib@^2.1.0:
+tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
@@ -9239,12 +8768,10 @@ tty-browserify@0.0.0:
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=
-type-check@~0.3.2:
- version "0.3.2"
- resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
- integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=
- dependencies:
- prelude-ls "~1.1.2"
+type-fest@^0.20.2:
+ version "0.20.2"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+ integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
type-fest@^0.6.0:
version "0.6.0"
@@ -9276,6 +8803,11 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
+uglify-js@^3.1.4:
+ version "3.17.0"
+ resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.0.tgz#55bd6e9d19ce5eef0d5ad17cd1f587d85b180a85"
+ integrity sha512-aTeNPVmgIMPpm1cxXr2Q/nEbvkmV8yq66F3om7X3P/cvOXQ0TMQ64Wk63iyT1gPlmdmGzjGpyLh1f3y8MZWXGg==
+
unbox-primitive@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471"
@@ -9426,11 +8958,6 @@ unpipe@1.0.0, unpipe@~1.0.0:
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
-unquote@^1.1.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544"
- integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=
-
unset-value@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"
@@ -9439,10 +8966,20 @@ unset-value@^1.0.0:
has-value "^0.3.1"
isobject "^3.0.0"
-upath@^1.1.1:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
- integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==
+untildify@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/untildify/-/untildify-2.1.0.tgz#17eb2807987f76952e9c0485fc311d06a826a2e0"
+ integrity sha512-sJjbDp2GodvkB0FZZcn7k6afVisqX5BZD7Yq3xp4nN2O15BBK0cLm3Vwn2vQaF7UDS0UUsrQMkkplmDI5fskig==
+ dependencies:
+ os-homedir "^1.0.0"
+
+update-browserslist-db@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38"
+ integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==
+ dependencies:
+ escalade "^3.1.1"
+ picocolors "^1.0.0"
uri-js@^4.2.2:
version "4.4.1"
@@ -9473,25 +9010,6 @@ url@^0.11.0:
punycode "1.3.2"
querystring "0.2.0"
-use-composed-ref@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.1.0.tgz#9220e4e94a97b7b02d7d27eaeab0b37034438bbc"
- integrity sha512-my1lNHGWsSDAhhVAT4MKs6IjBUtG6ZG11uUqexPH9PptiIZDQOzaF4f5tEbJ2+7qvNbtXNBbU3SfmN+fXlWDhg==
- dependencies:
- ts-essentials "^2.0.3"
-
-use-isomorphic-layout-effect@^1.0.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.1.tgz#7bb6589170cd2987a152042f9084f9effb75c225"
- integrity sha512-L7Evj8FGcwo/wpbv/qvSfrkHFtOpCzvM5yl2KVyDJoylVuSvzphiiasmjgQPttIGBAy2WKiBNR98q8w7PiNgKQ==
-
-use-latest@^1.0.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/use-latest/-/use-latest-1.2.0.tgz#a44f6572b8288e0972ec411bdd0840ada366f232"
- integrity sha512-d2TEuG6nSLKQLAfW3By8mKr8HurOlTkul0sOpxbClIv4SQ4iOd7BYr7VIzdbktUCnv7dua/60xzd8igMU6jmyw==
- dependencies:
- use-isomorphic-layout-effect "^1.0.0"
-
use@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
@@ -9590,22 +9108,22 @@ void-elements@^3.1.0:
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
integrity sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=
-vue-docgen-api@^4.34.2:
- version "4.39.0"
- resolved "https://registry.yarnpkg.com/vue-docgen-api/-/vue-docgen-api-4.39.0.tgz#a27d223fe7400117160355f5a2b46f5f5ccdc2cf"
- integrity sha512-Sd3zVHwR/hqoLZQyc9OeGWIXfa/+dtvYpfTTJL7GciMbs4GlDWijXLngneOOp6ObW+It3a/eXM6gtlhI/wgPhA==
+vue-docgen-api@^4.44.15:
+ version "4.52.0"
+ resolved "https://registry.yarnpkg.com/vue-docgen-api/-/vue-docgen-api-4.52.0.tgz#83d38e23f640913ec4fd2107c2a9dff1b5af5d0f"
+ integrity sha512-2HSt9KLQ/ehJiwV4+6LgOjRqzh37vtUv6sKhSIp3CAPlwSd4/Bq2uvbs0+GJxdfrMnVquwY5DOavgNSEHsPA2w==
dependencies:
"@babel/parser" "^7.13.12"
- "@babel/types" "^7.13.12"
- "@vue/compiler-dom" "^3.0.7"
- "@vue/compiler-sfc" "^3.0.7"
+ "@babel/types" "^7.18.8"
+ "@vue/compiler-dom" "^3.2.0"
+ "@vue/compiler-sfc" "^3.2.0"
ast-types "0.14.2"
hash-sum "^1.0.2"
lru-cache "^4.1.5"
pug "^3.0.2"
- recast "0.20.4"
+ recast "0.21.1"
ts-map "^1.0.3"
- vue-inbrowser-compiler-utils "^4.39.0"
+ vue-inbrowser-compiler-independent-utils "^4.52.0"
vue-docgen-loader@^1.5.0:
version "1.5.0"
@@ -9617,12 +9135,10 @@ vue-docgen-loader@^1.5.0:
loader-utils "^1.2.3"
querystring "^0.2.0"
-vue-inbrowser-compiler-utils@^4.39.0:
- version "4.39.0"
- resolved "https://registry.yarnpkg.com/vue-inbrowser-compiler-utils/-/vue-inbrowser-compiler-utils-4.39.0.tgz#0e9a2b5d8d3a8a96c08d98cf0a09ffe546d51941"
- integrity sha512-dtdqNELGa3snFgN6eXMhCN/EDn0f+B6Kl+QOgzyKPnpXSUjxvsT4lmw/ZhQq08HCic3DsuXW48pqlq/iBC6wNw==
- dependencies:
- camelcase "^5.3.1"
+vue-inbrowser-compiler-independent-utils@^4.52.0:
+ version "4.52.0"
+ resolved "https://registry.yarnpkg.com/vue-inbrowser-compiler-independent-utils/-/vue-inbrowser-compiler-independent-utils-4.52.0.tgz#c40aefe12eef48d22157fd22cd2bf9b9f9ee9e9c"
+ integrity sha512-r09DtBbHbCO/5Ns0UofB/orwX7pYaAJW12+c/cy4brTF06oeng6QbxX0XziiepMyf9E9q/lLa4gk8hlhmG4Zww==
walker@^1.0.7, walker@~1.0.5:
version "1.0.7"
@@ -9631,13 +9147,6 @@ walker@^1.0.7, walker@~1.0.5:
dependencies:
makeerror "1.0.x"
-warning@^4.0.2, warning@^4.0.3:
- version "4.0.3"
- resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
- integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
- dependencies:
- loose-envify "^1.0.0"
-
watchpack-chokidar2@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957"
@@ -9656,11 +9165,24 @@ watchpack@^1.7.4:
chokidar "^3.4.1"
watchpack-chokidar2 "^2.0.1"
+watchpack@^2.2.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
+ integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
+ dependencies:
+ glob-to-regexp "^0.4.1"
+ graceful-fs "^4.1.2"
+
web-namespaces@^1.0.0:
version "1.1.4"
resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec"
integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==
+webidl-conversions@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
+ integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
+
webpack-dev-middleware@^3.7.3:
version "3.7.3"
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5"
@@ -9677,15 +9199,14 @@ webpack-filter-warnings-plugin@^1.2.1:
resolved "https://registry.yarnpkg.com/webpack-filter-warnings-plugin/-/webpack-filter-warnings-plugin-1.2.1.tgz#dc61521cf4f9b4a336fbc89108a75ae1da951cdb"
integrity sha512-Ez6ytc9IseDMLPo0qCuNNYzgtUl8NovOqjIq4uAU8LTD4uoa1w1KpZyyzFtLTEMZpkkOkLfL9eN+KGYdk1Qtwg==
-webpack-hot-middleware@^2.25.0:
- version "2.25.0"
- resolved "https://registry.yarnpkg.com/webpack-hot-middleware/-/webpack-hot-middleware-2.25.0.tgz#4528a0a63ec37f8f8ef565cf9e534d57d09fe706"
- integrity sha512-xs5dPOrGPCzuRXNi8F6rwhawWvQQkeli5Ro48PRuQh8pYPCPmNnltP9itiUPT4xI8oW+y0m59lyyeQk54s5VgA==
+webpack-hot-middleware@^2.25.1:
+ version "2.25.2"
+ resolved "https://registry.yarnpkg.com/webpack-hot-middleware/-/webpack-hot-middleware-2.25.2.tgz#f7f936f3871d8c4eb95ecdf23a34e9cefe9806e8"
+ integrity sha512-CVgm3NAQyfdIonRvXisRwPTUYuSbyZ6BY7782tMeUzWOO7RmVI2NaBYuCp41qyD4gYCkJyTneAJdK69A13B0+A==
dependencies:
- ansi-html "0.0.7"
- html-entities "^1.2.0"
- querystring "^0.2.0"
- strip-ansi "^3.0.0"
+ ansi-html-community "0.0.8"
+ html-entities "^2.1.0"
+ strip-ansi "^6.0.0"
webpack-log@^2.0.0:
version "2.0.0"
@@ -9710,7 +9231,7 @@ webpack-virtual-modules@^0.2.2:
dependencies:
debug "^3.0.0"
-webpack@4:
+webpack@4, "webpack@>=4.0.0 <6.0.0":
version "4.46.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542"
integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==
@@ -9739,6 +9260,14 @@ webpack@4:
watchpack "^1.7.4"
webpack-sources "^1.4.1"
+whatwg-url@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
+ integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
+ dependencies:
+ tr46 "~0.0.3"
+ webidl-conversions "^3.0.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"
@@ -9750,7 +9279,7 @@ which-boxed-primitive@^1.0.2:
is-string "^1.0.5"
is-symbol "^1.0.3"
-which@^1.2.9, which@^1.3.1:
+which@^1.2.9:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
@@ -9764,12 +9293,12 @@ which@^2.0.1:
dependencies:
isexe "^2.0.0"
-wide-align@^1.1.0:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457"
- integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==
+wide-align@^1.1.2:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3"
+ integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==
dependencies:
- string-width "^1.0.2 || 2"
+ string-width "^1.0.2 || 2 || 3 || 4"
widest-line@^3.1.0:
version "3.1.0"
@@ -9788,10 +9317,10 @@ with@^7.0.0:
assert-never "^1.2.1"
babel-walk "3.0.0-canary-5"
-word-wrap@~1.2.3:
- version "1.2.3"
- resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
- integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+wordwrap@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
+ integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==
worker-farm@^1.7.0:
version "1.7.0"
@@ -9807,6 +9336,15 @@ worker-rpc@^0.1.0:
dependencies:
microevent.ts "~0.1.1"
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
@@ -9831,6 +9369,18 @@ write-file-atomic@^3.0.0:
signal-exit "^3.0.2"
typedarray-to-buffer "^3.1.5"
+ws@^8.2.3:
+ version "8.8.1"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0"
+ integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==
+
+x-default-browser@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/x-default-browser/-/x-default-browser-0.4.0.tgz#70cf0da85da7c0ab5cb0f15a897f2322a6bdd481"
+ integrity sha512-7LKo7RtWfoFN/rHx1UELv/2zHGMx8MkZKDq1xENmOCTkfIqZJ0zZ26NEJX8czhnPXVcqS0ARjjfJB+eJ0/5Cvw==
+ optionalDependencies:
+ default-browser-id "^1.0.4"
+
xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
diff --git a/tests.yml b/tests.yml
index 3b1ac26a195..c530ec30416 100644
--- a/tests.yml
+++ b/tests.yml
@@ -60,3 +60,18 @@ mapping:
# Whats New should map to its respective spec
- source: data/whats_new/\w*.yml
test: spec/lib/release_highlights/validator_spec.rb
+
+ - source: (ee/)?app/workers/.+\.rb
+ test: spec/workers/every_sidekiq_worker_spec.rb
+
+ - source: lib/gitlab/usage_data_counters/known_events/.+\.yml
+ test: spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
+ - source: lib/gitlab/usage_data_counters/known_events/.+\.yml
+ test: spec/lib/gitlab/usage_data_spec.rb
+
+ # Mailer previews
+ - source: (ee/)?app/mailers/previews/.+\.rb
+ test: spec/mailers/previews_spec.rb
+ - source: ee/app/mailers/ee/preview/.+\.rb
+ test: spec/mailers/previews_spec.rb
+
diff --git a/tooling/bin/find_changes b/tooling/bin/find_changes
index 8ad5011459b..38e1f363dd9 100755
--- a/tooling/bin/find_changes
+++ b/tooling/bin/find_changes
@@ -46,12 +46,16 @@ class FindChanges # rubocop:disable Gitlab/NamespacedClass
config.private_token = gitlab_token
end
- mr_changes = Gitlab.merge_request_changes(mr_project_path, mr_iid)
-
- mr_changes.changes.map { |change| change['new_path'] unless change['deleted_file'] }.compact
+ mr_changes.changes.flat_map do |change|
+ change.to_h.values_at('old_path', 'new_path')
+ end.uniq
end
end
+ def mr_changes
+ @mr_changes ||= Gitlab.merge_request_changes(mr_project_path, mr_iid)
+ end
+
def test_files
return [] if !matched_tests_file || !File.exist?(matched_tests_file)
diff --git a/tooling/bin/qa/run_qa_check b/tooling/bin/qa/run_qa_check
deleted file mode 100755
index 5b8844ec4fd..00000000000
--- a/tooling/bin/qa/run_qa_check
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-require 'pathname'
-
-# This script checks if the code changes justify running the QA suite.
-#
-# It assumes the first argument is a directory of files containing diffs of changes from an MR
-# (e.g., created by tooling/bin/find_change_diffs). It exits with a success code if there are no diffs, or if the diffs
-# are suitable to run QA tests.
-#
-# The script will abort (exit code 1) if the argument is missing.
-#
-# The following condition will result in a failure code (2), indicating that QA tests should not run:
-#
-# - If the changes only include tests being put in quarantine
-
-abort("ERROR: Please specify the directory containing MR diffs.") if ARGV.empty?
-diffs_dir = Pathname.new(ARGV.shift).expand_path
-
-# Run QA tests if there are no diffs. E.g., in scheduled pipelines
-exit 0 if diffs_dir.glob('**/*').empty?
-
-files_count = 0
-specs_count = 0
-quarantine_specs_count = 0
-
-diffs_dir.glob('**/*').each do |path|
- next if path.directory?
-
- files_count += 1
- next unless path.to_s.end_with?('_spec.rb.diff')
-
- specs_count += 1
- quarantine_specs_count += 1 if path.read.match?(/^\+.*,? quarantine:/)
-end
-
-# Run QA tests if there are no specs. E.g., when the MR changes QA framework files.
-exit 0 if specs_count == 0
-
-# Skip QA tests if there are only specs being put in quarantine.
-exit 2 if quarantine_specs_count == specs_count && quarantine_specs_count == files_count
-
-# Run QA tests under any other circumstances. E.g., if there are specs being put in quarantine but there are also
-# other changes that might need to be tested.
diff --git a/tooling/config/CODEOWNERS.yml b/tooling/config/CODEOWNERS.yml
index 71818b67ab1..6b24134ea17 100644
--- a/tooling/config/CODEOWNERS.yml
+++ b/tooling/config/CODEOWNERS.yml
@@ -47,7 +47,7 @@
- 'jira_connect/'
- 'kubernetes/'
- 'protected_environments/'
- - '/config/feature_flags/development/jira_connect_*'
+ - '/config/feature_flags/**/*'
- '/config/metrics/'
- '/app/controllers/groups/dependency_proxy_auth_controller.rb'
- '/app/finders/ci/auth_job_finder.rb'
@@ -65,17 +65,19 @@
keywords:
- audit
patterns:
- - '**%{keyword}**'
+ - '/{,ee/}app/**/*%{keyword}*'
+ - '/{,ee/}config/**/*%{keyword}*'
+ - '/{,ee/}lib/**/*%{keyword}*'
deny:
keywords:
- '*.png'
- '*bundler-audit*'
- '**/merge_requests/**'
- - '/ee/app/services/audit_events/*'
+ - '/config/feature_flags/**/*'
+ - '/ee/app/services/audit_events/**/*'
- '/ee/config/feature_flags/development/auditor_group_runner_access.yml'
- - '/ee/spec/services/audit_events/*'
+ - '/ee/spec/services/audit_events/**/*'
- '/ee/spec/services/ci/*'
- '/ee/spec/services/personal_access_tokens/*'
- - '/qa/**/*'
patterns:
- '%{keyword}'
diff --git a/tooling/danger/config_files.rb b/tooling/danger/config_files.rb
new file mode 100644
index 00000000000..436335bfc06
--- /dev/null
+++ b/tooling/danger/config_files.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'yaml'
+
+module Tooling
+ module Danger
+ module ConfigFiles
+ SUGGEST_INTRODUCED_BY_COMMENT = <<~SUGGEST_COMMENT
+ ```suggestion
+ introduced_by_url: "%<url>s"
+ ```
+ SUGGEST_COMMENT
+
+ CONFIG_DIRS = %w[
+ config/feature_flags
+ config/metrics
+ config/events
+ ].freeze
+
+ def add_suggestion_for_missing_introduced_by_url
+ new_config_files.each do |file_name|
+ config_file_lines = project_helper.file_lines(file_name)
+
+ config_file_lines.each_with_index do |added_line, i|
+ next unless added_line =~ /^introduced_by_url:\s?$/
+
+ markdown(format(SUGGEST_INTRODUCED_BY_COMMENT, url: helper.mr_web_url), file: file_name, line: i + 1)
+ end
+ end
+ end
+
+ def new_config_files
+ helper.added_files.select { |f| in_config_dir?(f) && f.end_with?('yml') }
+ end
+
+ private
+
+ def in_config_dir?(path)
+ CONFIG_DIRS.any? { |d| path.start_with?(d) }
+ end
+ end
+ end
+end
diff --git a/tooling/danger/project_helper.rb b/tooling/danger/project_helper.rb
index d8c7d617927..1d052bf6bbd 100644
--- a/tooling/danger/project_helper.rb
+++ b/tooling/danger/project_helper.rb
@@ -17,6 +17,10 @@ module Tooling
# First-match win, so be sure to put more specific regex at the top...
CATEGORIES = {
+ # GitLab Flavored Markdown Specification files. See more context at: https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#specification-files
+ %r{\Aglfm_specification/.+prosemirror_json\.yml} => [:frontend],
+ %r{\Aglfm_specification/.+\.yml} => [:frontend, :backend],
+
[%r{usage_data\.rb}, %r{^(\+|-).*\s+(count|distinct_count|estimate_batch_distinct_count)\(.*\)(.*)$}] => [:database, :backend, :product_intelligence],
%r{\A((ee|jh)/)?config/feature_flags/} => :feature_flag,
diff --git a/vendor/gems/bundler-checksum/.gitlab-ci.yml b/vendor/gems/bundler-checksum/.gitlab-ci.yml
new file mode 100644
index 00000000000..f6bdb73a039
--- /dev/null
+++ b/vendor/gems/bundler-checksum/.gitlab-ci.yml
@@ -0,0 +1,28 @@
+workflow:
+ rules:
+ - if: $CI_MERGE_REQUEST_ID
+
+.test:
+ cache:
+ key: bundler-checksum
+ paths:
+ - vendor/gems/bundler-checksum/vendor/ruby
+ before_script:
+ - cd vendor/gems/bundler-checksum
+ - ruby -v # Print out ruby version for debugging
+ - gem install bundler --no-document # Bundler is not installed with the image
+ - bundle config set --local path 'vendor' # Install dependencies into ./vendor/ruby
+ - bundle config set with 'development'
+ - bundle config set --local frozen 'true' # Disallow Gemfile.lock changes on CI
+ - bundle config # Show bundler configuration
+ - bundle install -j $(nproc)
+ script:
+ - pushd test/project_with_checksum_lock && scripts/test
+
+test-2.7:
+ image: "ruby:2.7"
+ extends: .test
+
+test-3.0:
+ image: "ruby:3.0"
+ extends: .test
diff --git a/vendor/gems/bundler-checksum/Gemfile b/vendor/gems/bundler-checksum/Gemfile
new file mode 100644
index 00000000000..9a78debe9a3
--- /dev/null
+++ b/vendor/gems/bundler-checksum/Gemfile
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+source 'https://rubygems.org/'
+
+gemspec
diff --git a/vendor/gems/bundler-checksum/Gemfile.lock b/vendor/gems/bundler-checksum/Gemfile.lock
new file mode 100644
index 00000000000..8ae053f0105
--- /dev/null
+++ b/vendor/gems/bundler-checksum/Gemfile.lock
@@ -0,0 +1,18 @@
+PATH
+ remote: .
+ specs:
+ bundler-checksum (0.1.0)
+ bundler
+
+GEM
+ remote: https://rubygems.org/
+ specs:
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ bundler-checksum!
+
+BUNDLED WITH
+ 2.3.17
diff --git a/vendor/gems/bundler-checksum/LICENSE b/vendor/gems/bundler-checksum/LICENSE
new file mode 100644
index 00000000000..7f53e1576a1
--- /dev/null
+++ b/vendor/gems/bundler-checksum/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2022-present GitLab B.V.
+
+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/bundler-checksum/README.md b/vendor/gems/bundler-checksum/README.md
new file mode 100644
index 00000000000..1420dc49b94
--- /dev/null
+++ b/vendor/gems/bundler-checksum/README.md
@@ -0,0 +1,32 @@
+# bundler-checksum
+
+Bundler patch for verifying local gem checksums
+
+## Install
+
+Add the following to your Gemfile:
+
+```
+if ENV['BUNDLER_CHECKSUM_VERIFICATION_OPT_IN'] # this verification is still experimental
+ require 'bundler-checksum'
+ Bundler::Checksum.patch!
+end
+```
+
+## Usage
+
+Once the gem is installed, bundler-checksum will verify gems before
+installation.
+
+If a new or updated gem is to be installed, the remote checksum of that gem is stored in `Gemfile.checksum`.
+Checksum entries for other versions of the gem are removed from `Gemfile.checksum`.
+
+If a version of a gem is to be installed that is already present in `Gemfile.checksum`, the remote and local
+checksums are compared and an error is prompted if they do not match.
+
+Gem checksums for all platforms are stored in `Gemfile.checksum`.
+When `bundler-checksum` runs it will only verify the checksum for the platform that `bundle` wants to download.
+
+
+## Development
+
diff --git a/vendor/gems/bundler-checksum/bin/bundler-checksum b/vendor/gems/bundler-checksum/bin/bundler-checksum
new file mode 100755
index 00000000000..2d0aea827bc
--- /dev/null
+++ b/vendor/gems/bundler-checksum/bin/bundler-checksum
@@ -0,0 +1,6 @@
+#!/usr/bin/env ruby
+
+require 'bundler-checksum'
+require 'bundler/checksum/command'
+
+Bundler::Checksum::Command.execute(ARGV)
diff --git a/vendor/gems/bundler-checksum/bundler-checksum.gemspec b/vendor/gems/bundler-checksum/bundler-checksum.gemspec
new file mode 100644
index 00000000000..c04312480b6
--- /dev/null
+++ b/vendor/gems/bundler-checksum/bundler-checksum.gemspec
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require_relative 'lib/bundler/checksum/version'
+
+Gem::Specification.new do |spec|
+ spec.name = 'bundler-checksum'
+ spec.version = Bundler::Checksum::VERSION
+ spec.authors = ['dustinmm80']
+ spec.email = ['dcollins@gitlab.com']
+
+ spec.summary = 'Track checksums locally with Bundler'
+ spec.description = 'Track checksums locally with Bundler'
+ spec.homepage = 'https://gitlab.com/gitlab-org/gitlab/-/tree/master/vendor/gems/bundler-checksum'
+ spec.license = 'MIT'
+
+ spec.files = Dir['bin/*', 'lib/**/*.rb']
+ spec.bindir = 'bin'
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
+ spec.require_paths = ['lib']
+
+ spec.add_dependency 'bundler'
+end
diff --git a/vendor/gems/bundler-checksum/lib/bundler-checksum.rb b/vendor/gems/bundler-checksum/lib/bundler-checksum.rb
new file mode 100644
index 00000000000..600cd4f7107
--- /dev/null
+++ b/vendor/gems/bundler-checksum/lib/bundler-checksum.rb
@@ -0,0 +1 @@
+require 'bundler/checksum'
diff --git a/vendor/gems/bundler-checksum/lib/bundler/checksum.rb b/vendor/gems/bundler-checksum/lib/bundler/checksum.rb
new file mode 100644
index 00000000000..c8d78eba111
--- /dev/null
+++ b/vendor/gems/bundler-checksum/lib/bundler/checksum.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+require 'bundler'
+require 'bundler/checksum/version'
+require 'json'
+
+module Bundler
+ module Patches
+ # This module monkey-patches Bundler to check Gemfile.checksum
+ # when installing gems that are from RubyGems
+ module RubyGemsInstallerPatch
+ def pre_install_checks
+ super && validate_local_package_checksum
+ end
+
+ private
+
+ def validate_local_package_checksum
+ cached_checksum = fetch_checksum_from_file(spec)
+
+ if cached_checksum.nil?
+ raise SecurityError, "Cached checksum for #{spec.full_name} not found. Please (re-)generate Gemfile.checksum"
+ end
+
+ validate_file_checksum(cached_checksum)
+ end
+
+ def fetch_checksum_from_file(spec)
+ ::Bundler::Checksum.checksum_for(spec.name, spec.version.to_s, spec.platform.to_s)
+ end
+
+ # Modified from
+ # https://github.com/rubygems/rubygems/blob/243173279e79a38f03e318eea8825d1c8824e119/bundler/lib/bundler/rubygems_gem_installer.rb#L116
+ def validate_file_checksum(checksum)
+ return true if Bundler.settings[:disable_checksum_validation]
+
+ source = @package.instance_variable_get(:@gem)
+
+ # Contary to upstream, we raise instead of silently returning
+ raise "#{@package.inspect} does not have :@gem" unless source
+ raise "#{source.inspect} does not respond to :with_read_io" unless source.respond_to?(:with_read_io)
+
+ digest = source.with_read_io do |io|
+ digest = SharedHelpers.digest(:SHA256).new
+ digest << io.read(16_384) until io.eof?
+ io.rewind
+ send(checksum_type(checksum), digest)
+ end
+ unless digest == checksum
+ raise SecurityError, <<-MESSAGE
+ Bundler cannot continue installing #{spec.name} (#{spec.version}).
+ The checksum for the downloaded `#{spec.full_name}.gem` does not match \
+ the checksum from the checksum file. This means the contents of the downloaded \
+ gem is different from what was recorded in the checksum file, and could be potential security issue.
+ gem is different from what was uploaded to the server, and could be a potential security issue.
+
+ To resolve this issue:
+ 1. delete the downloaded gem located at: `#{spec.gem_dir}/#{spec.full_name}.gem`
+ 2. run `bundle install`
+
+ If you wish to continue installing the downloaded gem, and are certain it does not pose a \
+ security issue despite the mismatching checksum, do the following:
+ 1. run `bundle config set --local disable_checksum_validation true` to turn off checksum verification
+ 2. run `bundle install`
+
+ (More info: The expected SHA256 checksum was #{checksum.inspect}, but the \
+ checksum for the downloaded gem was #{digest.inspect}.)
+ MESSAGE
+ end
+ true
+ end
+ end
+ end
+end
+
+module Bundler
+ module Checksum
+ class << self
+ def checksum_file
+ @checksum_file ||= File.join(File.dirname(Bundler.default_gemfile), 'Gemfile.checksum')
+ end
+
+ def checksums_from_file
+ @checksums_from_file ||= JSON.parse(File.open(checksum_file).read, symbolize_names: true)
+ rescue JSON::ParserError => e
+ raise "Invalid checksum file: #{e.message}"
+ end
+
+ def checksum_for(gem_name, gem_version, gem_platform)
+ item = checksums_from_file.detect do |item|
+ item[:name] == gem_name &&
+ item[:platform] == gem_platform &&
+ item[:version] == gem_version
+ end
+
+ item&.fetch(:checksum)
+ end
+
+ def patch!
+ return if defined?(@patched) && @patched
+ @patched = true
+
+ Bundler.ui.info "Patching bundler with bundler-checksum..."
+ require 'bundler/rubygems_gem_installer'
+ ::Bundler::RubyGemsGemInstaller.prepend(Bundler::Patches::RubyGemsInstallerPatch)
+ end
+ end
+ end
+end
diff --git a/vendor/gems/bundler-checksum/lib/bundler/checksum/command.rb b/vendor/gems/bundler-checksum/lib/bundler/checksum/command.rb
new file mode 100644
index 00000000000..438f41f6e69
--- /dev/null
+++ b/vendor/gems/bundler-checksum/lib/bundler/checksum/command.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Bundler::Checksum
+ module Command
+ autoload :Init, File.expand_path("command/init", __dir__)
+ autoload :Verify, File.expand_path("command/verify", __dir__)
+ autoload :Helper, File.expand_path("command/helper", __dir__)
+
+ def self.execute(args)
+ if args.empty?
+ $stderr.puts 'A command must be given [init,update,verify]'
+ end
+
+ if args.first == 'init'
+ Init.execute
+ elsif args.first == 'update'
+ $stderr.puts 'Not implemented, please use init'
+ elsif args.first == 'verify'
+ verified = Verify.execute
+
+ unless verified
+ exit 1
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/gems/bundler-checksum/lib/bundler/checksum/command/helper.rb b/vendor/gems/bundler-checksum/lib/bundler/checksum/command/helper.rb
new file mode 100644
index 00000000000..fa06bfe3da4
--- /dev/null
+++ b/vendor/gems/bundler-checksum/lib/bundler/checksum/command/helper.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'json'
+require 'net/http'
+
+module Bundler::Checksum::Command
+ module Helper
+ extend self
+
+ def remote_checksums_for_gem(gem_name, gem_version)
+ response = Net::HTTP.get_response(URI(
+ "https://rubygems.org/api/v1/versions/#{gem_name}.json"
+ ))
+
+ return [] unless response.code == '200'
+
+ gem_candidates = JSON.parse(response.body, symbolize_names: true)
+ gem_candidates.select! { |g| g[:number] == gem_version.to_s }
+
+ gem_candidates.map {
+ |g| {:name => gem_name, :version => gem_version, :platform => g[:platform], :checksum => g[:sha]}
+ }
+
+ rescue JSON::ParserError
+ []
+ end
+ end
+end
diff --git a/vendor/gems/bundler-checksum/lib/bundler/checksum/command/init.rb b/vendor/gems/bundler-checksum/lib/bundler/checksum/command/init.rb
new file mode 100644
index 00000000000..fed0e11080f
--- /dev/null
+++ b/vendor/gems/bundler-checksum/lib/bundler/checksum/command/init.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'openssl'
+
+module Bundler::Checksum::Command
+ module Init
+ extend self
+
+ def execute
+ $stderr.puts "Initializing checksum file #{checksum_file}"
+
+ checksums = []
+
+ compact_index_cache = Bundler::Fetcher::CompactIndex
+ .new(nil, Bundler::Source::Rubygems::Remote.new(Bundler::URI("https://rubygems.org")), nil)
+ .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)
+
+ next if seen.include?(spec.name)
+ seen << spec.name
+
+ $stderr.puts "Adding #{spec.name}==#{spec.version}"
+
+ compact_index_dependencies = compact_index_cache.dependencies(spec.name).select { |item| item.first == spec.version.to_s }
+
+ if !compact_index_dependencies.empty?
+ compact_index_checksums = compact_index_dependencies.map do |version, platform, dependencies, requirements|
+ {
+ name: spec.name,
+ version: spec.version.to_s,
+ platform: Gem::Platform.new(platform).to_s,
+ checksum: requirements.detect { |requirement| requirement.first == 'checksum' }.flatten[1]
+ }
+ end
+
+ checksums += compact_index_checksums.sort_by { |hash| hash.values }
+ else
+ remote_checksum = Helper.remote_checksums_for_gem(spec.name, spec.version)
+
+ if remote_checksum.empty?
+ raise "#{spec.name} #{spec.version} not found on Rubygems!"
+ end
+
+ checksums += remote_checksum.sort_by { |hash| hash.values }
+ end
+ end
+
+ File.write(checksum_file, JSON.generate(checksums, array_nl: "\n") + "\n")
+ end
+
+ private
+
+ def checksum_file
+ ::Bundler::Checksum.checksum_file
+ end
+
+ def lockfile
+ lockfile_path = Bundler.default_lockfile
+ lockfile = Bundler::LockfileParser.new(Bundler.read_file(lockfile_path))
+ end
+ end
+end
diff --git a/vendor/gems/bundler-checksum/lib/bundler/checksum/command/verify.rb b/vendor/gems/bundler-checksum/lib/bundler/checksum/command/verify.rb
new file mode 100644
index 00000000000..ba2eea6ea0c
--- /dev/null
+++ b/vendor/gems/bundler-checksum/lib/bundler/checksum/command/verify.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module Bundler::Checksum::Command
+ module Verify
+ extend self
+
+ def execute
+ $stderr.puts 'Verifying bundle checksums'
+
+ verified = true
+
+ local_checksums.each do |gem|
+ name = gem.fetch(:name)
+ version = gem.fetch(:version)
+ platform = gem.fetch(:platform)
+ checksum = gem.fetch(:checksum)
+
+ $stderr.puts "Verifying #{name}==#{version} #{platform}"
+ unless validate_gem_checksum(name, version, platform, checksum)
+ verified = false
+ end
+ end
+
+ verified
+ end
+
+ private
+
+ def local_checksums
+ ::Bundler::Checksum.checksums_from_file
+ end
+
+ def validate_gem_checksum(gem_name, gem_version, gem_platform, local_checksum)
+ remote_checksums = Helper.remote_checksums_for_gem(gem_name, gem_version)
+ if remote_checksums.empty?
+ $stderr.puts "#{gem_name} #{gem_version} not found on Rubygems, skipping"
+ return false
+ end
+
+ remote_platform_checksum = remote_checksums.find { |g| g[:name] == gem_name && g[:platform] == gem_platform.to_s }
+
+ if local_checksum == remote_platform_checksum[:checksum]
+ true
+ else
+ $stderr.puts "Gem #{gem_name} #{gem_version} #{gem_platform} failed checksum verification"
+ $stderr.puts "LOCAL: #{local_checksum}"
+ $stderr.puts "REMOTE: #{remote_platform_checksum[:checksum]}"
+ return false
+ end
+ end
+ end
+end
diff --git a/vendor/gems/bundler-checksum/lib/bundler/checksum/version.rb b/vendor/gems/bundler-checksum/lib/bundler/checksum/version.rb
new file mode 100644
index 00000000000..41e958b2db9
--- /dev/null
+++ b/vendor/gems/bundler-checksum/lib/bundler/checksum/version.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+module Bundler
+ module Checksum
+ # bundler-checksum version
+ VERSION = '0.1.0'
+ end
+end
diff --git a/vendor/gems/bundler-checksum/test/project_with_checksum_lock/Gemfile b/vendor/gems/bundler-checksum/test/project_with_checksum_lock/Gemfile
new file mode 100644
index 00000000000..238bd09669f
--- /dev/null
+++ b/vendor/gems/bundler-checksum/test/project_with_checksum_lock/Gemfile
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+source 'https://rubygems.org'
+
+if ENV['BUNDLER_CHECKSUM_VERIFICATION_OPT_IN'] # this verification is still experimental
+ $:.unshift(File.expand_path('../../lib', __dir__))
+ require 'bundler-checksum'
+ Bundler::Checksum.patch!
+end
+
+gem 'rails', '~> 6.1.6.1'
diff --git a/vendor/gems/bundler-checksum/test/project_with_checksum_lock/Gemfile.checksum b/vendor/gems/bundler-checksum/test/project_with_checksum_lock/Gemfile.checksum
new file mode 100644
index 00000000000..cf70611c97a
--- /dev/null
+++ b/vendor/gems/bundler-checksum/test/project_with_checksum_lock/Gemfile.checksum
@@ -0,0 +1,54 @@
+[
+{"name":"actioncable","version":"6.1.6.1","platform":"ruby","checksum":"11f079141cf032026881e4a79ae0cc93753351089c1b6ca1ed30a8a6a21f961b"},
+{"name":"actionmailbox","version":"6.1.6.1","platform":"ruby","checksum":"a4cc16fe634c9de4e22669fc4bf20d5b84f65039c7e3d7308c804b82726d03d2"},
+{"name":"actionmailer","version":"6.1.6.1","platform":"ruby","checksum":"13964bff4a75efd705304cb7aeb71380a4b11d404c7304b67f3bc3208cde12a7"},
+{"name":"actionpack","version":"6.1.6.1","platform":"ruby","checksum":"f3e0a82a62aa36fecadbacbb266e38338da032f18aaf97674f335671b420bdd4"},
+{"name":"actiontext","version":"6.1.6.1","platform":"ruby","checksum":"ff26b96769b6f4bdf3c0e74f613b232b2cdab7e46f1433c9cfa4fdcd081afac0"},
+{"name":"actionview","version":"6.1.6.1","platform":"ruby","checksum":"a87fc7d2c4fe9b6357492a3ee361be8169f3f319f47bf70fda1b1718b944d06b"},
+{"name":"activejob","version":"6.1.6.1","platform":"ruby","checksum":"9efee4499d31aaaab73b843a09564d4a2aabcd51c2088361a92e08766ab0db65"},
+{"name":"activemodel","version":"6.1.6.1","platform":"ruby","checksum":"239953365a7da4bcb9a3819b8ac2557a58a3ba89ddd36bee9bb3eca818e4a3e2"},
+{"name":"activerecord","version":"6.1.6.1","platform":"ruby","checksum":"82f74804ab34ea549fd593e5ced68c32426564786127d2de9b933ba78467d0b0"},
+{"name":"activestorage","version":"6.1.6.1","platform":"ruby","checksum":"3fbf4c355a69a46e14676004ad8e06245bdce7f96858e72782715218326aafc5"},
+{"name":"activesupport","version":"6.1.6.1","platform":"ruby","checksum":"5fc9fd6fe6f755e7523bb3aaf4370fb91a8416b39e3202939fd8bded4fec606d"},
+{"name":"builder","version":"3.2.4","platform":"ruby","checksum":"99caf08af60c8d7f3a6b004029c4c3c0bdaebced6c949165fe98f1db27fbbc10"},
+{"name":"concurrent-ruby","version":"1.1.10","platform":"ruby","checksum":"244cb1ca0d91ec2c15ca2209507c39fb163336994428e16fbd3f465c87bd8e68"},
+{"name":"crass","version":"1.0.6","platform":"ruby","checksum":"dc516022a56e7b3b156099abc81b6d2b08ea1ed12676ac7a5657617f012bd45d"},
+{"name":"erubi","version":"1.11.0","platform":"ruby","checksum":"fda72d577feaf3bdcd646d33fa630be5f92f48e179a9278e4175a9cec20e7f85"},
+{"name":"globalid","version":"1.0.0","platform":"ruby","checksum":"1253641b1dc3392721c964351773755d75135d3d3c5cc65d88b0a3880a60bed8"},
+{"name":"i18n","version":"1.12.0","platform":"ruby","checksum":"91e3cc1b97616d308707eedee413d82ee021d751c918661fb82152793e64aced"},
+{"name":"loofah","version":"2.18.0","platform":"ruby","checksum":"61975a247a6aeb8f09ac5a3430305451efc4525c0b9b79c05feaec35a8b9d5a3"},
+{"name":"mail","version":"2.7.1","platform":"ruby","checksum":"ec2a3d489f7510b90d8eaa3f6abaad7038cf1d663cdf8ee66d0214a0bdf99c03"},
+{"name":"marcel","version":"1.0.2","platform":"ruby","checksum":"a013b677ef46cbcb49fd5c59b3d35803d2ee04dd75d8bfdc43533fc5a31f7e4e"},
+{"name":"method_source","version":"1.0.0","platform":"ruby","checksum":"d779455a2b5666a079ce58577bfad8534f571af7cec8107f4dce328f0981dede"},
+{"name":"mini_mime","version":"1.1.2","platform":"ruby","checksum":"a54aec0cc7438a03a850adb00daca2bdb60747f839e28186994df057cea87151"},
+{"name":"minitest","version":"5.16.2","platform":"ruby","checksum":"c1be0c6b57fab451faa08e74ffa71e7d6a259b90f4bacb881c7f4808ec8b4991"},
+{"name":"nio4r","version":"2.5.8","platform":"java","checksum":"b2b1800f6bf7ce4b797ca8b639ad278a99c9c904fb087a91d944f38e4bd71401"},
+{"name":"nio4r","version":"2.5.8","platform":"ruby","checksum":"3becb4ad95ab8ac0a9bd2e1b16466869402be62848082bf6329ae9091f276676"},
+{"name":"nokogiri","version":"1.13.8","platform":"aarch64-linux","checksum":"d6b2c45a57738f12fe27783939fe1394e7049246288c7770d3b1fee7f49432a6"},
+{"name":"nokogiri","version":"1.13.8","platform":"arm64-darwin","checksum":"00217e48a6995e81dd83014325c0ea0b015023a8922c7bdb2ef1416aa87c1f43"},
+{"name":"nokogiri","version":"1.13.8","platform":"java","checksum":"9d04c616900e2b5118e501436ebb9bc48520d08f3695d012a314006e28082f72"},
+{"name":"nokogiri","version":"1.13.8","platform":"ruby","checksum":"79c279298b2f22fd4e760f49990c7930436bac1b1cfeff7bacff192f30edea3c"},
+{"name":"nokogiri","version":"1.13.8","platform":"x64-mingw-ucrt","checksum":"98f7dac7583f07a84ec3fcc01dc03a66fce10f412cd363fce7de749acdb2a42d"},
+{"name":"nokogiri","version":"1.13.8","platform":"x64-mingw32","checksum":"117a71b37f2e1d774a9f031d393e72d5d04b92af8036e0c1a8dd509c247b2013"},
+{"name":"nokogiri","version":"1.13.8","platform":"x86-linux","checksum":"6d04342456edfb8fbc041d0c2cf5a59baaa7aacdda414b2333100b02f85d441d"},
+{"name":"nokogiri","version":"1.13.8","platform":"x86-mingw32","checksum":"0529d558b4280a55bc7af500d3d4d590b7c059c814a0cea52e4e18cb30c25d15"},
+{"name":"nokogiri","version":"1.13.8","platform":"x86_64-darwin","checksum":"8966d79e687b271df87a4b240456597c43cd98584e3f783fc35de4f066486421"},
+{"name":"nokogiri","version":"1.13.8","platform":"x86_64-linux","checksum":"344f1bc66feac787e5b2053c6e9095d1f33605083e58ddf2b8d4eef257bccc5f"},
+{"name":"racc","version":"1.6.0","platform":"java","checksum":"d449a3c279026451b9fd5f34e829dc5f6e0ef6b9b472b7ff89fd3877fe8fe8cf"},
+{"name":"racc","version":"1.6.0","platform":"ruby","checksum":"2dede3b136eeabd0f7b8c9356b958b3d743c00158e2615acab431af141354551"},
+{"name":"rack","version":"2.2.4","platform":"ruby","checksum":"ea2232b638cbd919129c8c8ad8012ecaccc09f848152a7e705d2139d0137ac2b"},
+{"name":"rack-test","version":"2.0.2","platform":"ruby","checksum":"adadd0e957f63a34199a9fdf905a920a0b0a50795735095b4ac4bd3c13385466"},
+{"name":"rails","version":"6.1.6.1","platform":"ruby","checksum":"17024921a3913fb341f584542b06adf6bb12977a8b92d5fce093c3996c963686"},
+{"name":"rails-dom-testing","version":"2.0.3","platform":"ruby","checksum":"b140c4f39f6e609c8113137b9a60dfc2ecb89864e496f87f23a68b3b8f12d8d1"},
+{"name":"rails-html-sanitizer","version":"1.4.3","platform":"ruby","checksum":"2ebba6ad9a0b100f79fda853a46851e7664febe1728223f9734281e0d55940d6"},
+{"name":"railties","version":"6.1.6.1","platform":"ruby","checksum":"bafecdf2dcbe4ea44e1ab7081fd797aa87ae9bbcd0f3a4372b662a1b93949733"},
+{"name":"rake","version":"13.0.6","platform":"ruby","checksum":"5ce4bf5037b4196c24ac62834d8db1ce175470391026bd9e557d669beeb19097"},
+{"name":"sprockets","version":"4.1.1","platform":"ruby","checksum":"68b10b0e574fc2a080e4779d025bf39bc7a20bc8659e32f827cccce9581348e2"},
+{"name":"sprockets-rails","version":"3.4.2","platform":"ruby","checksum":"36d6327757ccf7460a00d1d52b2d5ef0019a4670503046a129fa1fb1300931ad"},
+{"name":"thor","version":"1.2.1","platform":"ruby","checksum":"b1752153dc9c6b8d3fcaa665e9e1a00a3e73f28da5e238b81c404502e539d446"},
+{"name":"tzinfo","version":"2.0.5","platform":"ruby","checksum":"c5352fd901544d396745d013f46a04ae2ed081ce806d942099825b7c2b09a167"},
+{"name":"websocket-driver","version":"0.7.5","platform":"java","checksum":"fffa83aa188e9ac90e32a385832ec9d26acdf019538e1c7d703f2c8a323b39c8"},
+{"name":"websocket-driver","version":"0.7.5","platform":"ruby","checksum":"a280c3f44dcbb0323d58bc78dc49350c05d589ab7d13267fcff08d9d5ae76b28"},
+{"name":"websocket-extensions","version":"0.1.5","platform":"ruby","checksum":"1c6ba63092cda343eb53fc657110c71c754c56484aad42578495227d717a8241"},
+{"name":"zeitwerk","version":"2.6.0","platform":"ruby","checksum":"6cb2ee4645c6e597640d6f2d8cc91a59a6699ab38896a5c3fac3eefeb5c84d76"}
+]
diff --git a/vendor/gems/bundler-checksum/test/project_with_checksum_lock/Gemfile.lock b/vendor/gems/bundler-checksum/test/project_with_checksum_lock/Gemfile.lock
new file mode 100644
index 00000000000..8f4bb5fa40d
--- /dev/null
+++ b/vendor/gems/bundler-checksum/test/project_with_checksum_lock/Gemfile.lock
@@ -0,0 +1,139 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ actioncable (6.1.6.1)
+ actionpack (= 6.1.6.1)
+ activesupport (= 6.1.6.1)
+ nio4r (~> 2.0)
+ websocket-driver (>= 0.6.1)
+ actionmailbox (6.1.6.1)
+ actionpack (= 6.1.6.1)
+ activejob (= 6.1.6.1)
+ activerecord (= 6.1.6.1)
+ activestorage (= 6.1.6.1)
+ activesupport (= 6.1.6.1)
+ mail (>= 2.7.1)
+ actionmailer (6.1.6.1)
+ actionpack (= 6.1.6.1)
+ actionview (= 6.1.6.1)
+ activejob (= 6.1.6.1)
+ activesupport (= 6.1.6.1)
+ mail (~> 2.5, >= 2.5.4)
+ rails-dom-testing (~> 2.0)
+ actionpack (6.1.6.1)
+ actionview (= 6.1.6.1)
+ activesupport (= 6.1.6.1)
+ rack (~> 2.0, >= 2.0.9)
+ rack-test (>= 0.6.3)
+ rails-dom-testing (~> 2.0)
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
+ actiontext (6.1.6.1)
+ actionpack (= 6.1.6.1)
+ activerecord (= 6.1.6.1)
+ activestorage (= 6.1.6.1)
+ activesupport (= 6.1.6.1)
+ nokogiri (>= 1.8.5)
+ actionview (6.1.6.1)
+ activesupport (= 6.1.6.1)
+ builder (~> 3.1)
+ erubi (~> 1.4)
+ rails-dom-testing (~> 2.0)
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
+ activejob (6.1.6.1)
+ activesupport (= 6.1.6.1)
+ globalid (>= 0.3.6)
+ activemodel (6.1.6.1)
+ activesupport (= 6.1.6.1)
+ activerecord (6.1.6.1)
+ activemodel (= 6.1.6.1)
+ activesupport (= 6.1.6.1)
+ activestorage (6.1.6.1)
+ actionpack (= 6.1.6.1)
+ activejob (= 6.1.6.1)
+ activerecord (= 6.1.6.1)
+ activesupport (= 6.1.6.1)
+ marcel (~> 1.0)
+ mini_mime (>= 1.1.0)
+ activesupport (6.1.6.1)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (>= 1.6, < 2)
+ minitest (>= 5.1)
+ tzinfo (~> 2.0)
+ zeitwerk (~> 2.3)
+ builder (3.2.4)
+ concurrent-ruby (1.1.10)
+ crass (1.0.6)
+ erubi (1.11.0)
+ globalid (1.0.0)
+ activesupport (>= 5.0)
+ i18n (1.12.0)
+ concurrent-ruby (~> 1.0)
+ loofah (2.18.0)
+ crass (~> 1.0.2)
+ nokogiri (>= 1.5.9)
+ mail (2.7.1)
+ mini_mime (>= 0.1.1)
+ marcel (1.0.2)
+ method_source (1.0.0)
+ mini_mime (1.1.2)
+ minitest (5.16.2)
+ nio4r (2.5.8)
+ nokogiri (1.13.8-arm64-darwin)
+ racc (~> 1.4)
+ nokogiri (1.13.8-x86_64-linux)
+ racc (~> 1.4)
+ racc (1.6.0)
+ rack (2.2.4)
+ rack-test (2.0.2)
+ rack (>= 1.3)
+ rails (6.1.6.1)
+ actioncable (= 6.1.6.1)
+ actionmailbox (= 6.1.6.1)
+ actionmailer (= 6.1.6.1)
+ actionpack (= 6.1.6.1)
+ actiontext (= 6.1.6.1)
+ actionview (= 6.1.6.1)
+ activejob (= 6.1.6.1)
+ activemodel (= 6.1.6.1)
+ activerecord (= 6.1.6.1)
+ activestorage (= 6.1.6.1)
+ activesupport (= 6.1.6.1)
+ bundler (>= 1.15.0)
+ railties (= 6.1.6.1)
+ sprockets-rails (>= 2.0.0)
+ rails-dom-testing (2.0.3)
+ activesupport (>= 4.2.0)
+ nokogiri (>= 1.6)
+ rails-html-sanitizer (1.4.3)
+ loofah (~> 2.3)
+ railties (6.1.6.1)
+ actionpack (= 6.1.6.1)
+ activesupport (= 6.1.6.1)
+ method_source
+ rake (>= 12.2)
+ thor (~> 1.0)
+ rake (13.0.6)
+ sprockets (4.1.1)
+ concurrent-ruby (~> 1.0)
+ rack (> 1, < 3)
+ sprockets-rails (3.4.2)
+ actionpack (>= 5.2)
+ activesupport (>= 5.2)
+ sprockets (>= 3.0.0)
+ thor (1.2.1)
+ tzinfo (2.0.5)
+ concurrent-ruby (~> 1.0)
+ websocket-driver (0.7.5)
+ websocket-extensions (>= 0.1.0)
+ websocket-extensions (0.1.5)
+ zeitwerk (2.6.0)
+
+PLATFORMS
+ arm64-darwin-21
+ x86_64-linux
+
+DEPENDENCIES
+ rails (~> 6.1.6.1)
+
+BUNDLED WITH
+ 2.3.19
diff --git a/vendor/gems/bundler-checksum/test/project_with_checksum_lock/scripts/test b/vendor/gems/bundler-checksum/test/project_with_checksum_lock/scripts/test
new file mode 100755
index 00000000000..bb256449226
--- /dev/null
+++ b/vendor/gems/bundler-checksum/test/project_with_checksum_lock/scripts/test
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+set -x
+set -e
+
+# Check there's no differences after re-initialising
+ruby -I ../../lib ../../bin/bundler-checksum init
+git diff --exit-code Gemfile.checksum
+
+# Verify against rubygems.org
+ruby -I ../../lib ../../bin/bundler-checksum verify
+
+# Test installing with bundler-checksum
+export BUNDLER_CHECKSUM_VERIFICATION_OPT_IN=1
+bundle install
diff --git a/vendor/gems/ipynbdiff/Gemfile.lock b/vendor/gems/ipynbdiff/Gemfile.lock
index 1c2fd81e3b4..6a8d3750602 100644
--- a/vendor/gems/ipynbdiff/Gemfile.lock
+++ b/vendor/gems/ipynbdiff/Gemfile.lock
@@ -2,7 +2,7 @@ PATH
remote: .
specs:
ipynbdiff (0.4.7)
- diffy (~> 3.3)
+ diffy (~> 3.4)
oj (~> 3.13.16)
GEM
diff --git a/vendor/gems/ipynbdiff/ipynbdiff.gemspec b/vendor/gems/ipynbdiff/ipynbdiff.gemspec
index 3054118ea47..014005029ef 100644
--- a/vendor/gems/ipynbdiff/ipynbdiff.gemspec
+++ b/vendor/gems/ipynbdiff/ipynbdiff.gemspec
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
s.require_paths = ['lib']
- s.add_runtime_dependency 'diffy', '~> 3.3'
+ s.add_runtime_dependency 'diffy', '~> 3.4'
s.add_runtime_dependency 'oj', '~> 3.13.16'
s.add_development_dependency 'bundler', '~> 2.2'
diff --git a/vendor/gems/microsoft_graph_mailer/.gitlab-ci.yml b/vendor/gems/microsoft_graph_mailer/.gitlab-ci.yml
new file mode 100644
index 00000000000..1b10debb1b9
--- /dev/null
+++ b/vendor/gems/microsoft_graph_mailer/.gitlab-ci.yml
@@ -0,0 +1,32 @@
+workflow:
+ rules:
+ - if: $CI_MERGE_REQUEST_ID
+
+.rspec:
+ cache:
+ key: microsoft_graph_mailer-ruby
+ paths:
+ - vendor/gems/microsoft_graph_mailer/vendor/ruby
+ before_script:
+ - cd vendor/gems/microsoft_graph_mailer
+ - ruby -v # Print out ruby version for debugging
+ - gem install bundler --no-document # Bundler is not installed with the image
+ - bundle config set --local path 'vendor' # Install dependencies into ./vendor/ruby
+ - bundle config set with 'development'
+ - bundle config set --local frozen 'true' # Disallow Gemfile.lock changes on CI
+ - bundle config # Show bundler configuration
+ - bundle install -j $(nproc)
+ script:
+ - bundle exec rspec
+
+rspec-2.7:
+ image: "ruby:2.7"
+ extends: .rspec
+
+rspec-3.0:
+ image: "ruby:3.0"
+ extends: .rspec
+
+rspec-3.1:
+ image: "ruby:3.1"
+ extends: .rspec
diff --git a/vendor/gems/microsoft_graph_mailer/Gemfile b/vendor/gems/microsoft_graph_mailer/Gemfile
new file mode 100644
index 00000000000..be173b205f7
--- /dev/null
+++ b/vendor/gems/microsoft_graph_mailer/Gemfile
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+source "https://rubygems.org"
+
+gemspec
diff --git a/vendor/gems/microsoft_graph_mailer/Gemfile.lock b/vendor/gems/microsoft_graph_mailer/Gemfile.lock
new file mode 100644
index 00000000000..d6bb01eba73
--- /dev/null
+++ b/vendor/gems/microsoft_graph_mailer/Gemfile.lock
@@ -0,0 +1,217 @@
+PATH
+ remote: .
+ specs:
+ microsoft_graph_mailer (0.1.0)
+ mail (~> 2.7)
+ oauth2 (>= 1.4.4, < 3)
+
+GEM
+ remote: https://rubygems.org/
+ specs:
+ actioncable (7.0.4)
+ actionpack (= 7.0.4)
+ activesupport (= 7.0.4)
+ nio4r (~> 2.0)
+ websocket-driver (>= 0.6.1)
+ actionmailbox (7.0.4)
+ actionpack (= 7.0.4)
+ activejob (= 7.0.4)
+ activerecord (= 7.0.4)
+ activestorage (= 7.0.4)
+ activesupport (= 7.0.4)
+ mail (>= 2.7.1)
+ net-imap
+ net-pop
+ net-smtp
+ actionmailer (7.0.4)
+ actionpack (= 7.0.4)
+ actionview (= 7.0.4)
+ activejob (= 7.0.4)
+ activesupport (= 7.0.4)
+ mail (~> 2.5, >= 2.5.4)
+ net-imap
+ net-pop
+ net-smtp
+ rails-dom-testing (~> 2.0)
+ actionpack (7.0.4)
+ actionview (= 7.0.4)
+ activesupport (= 7.0.4)
+ rack (~> 2.0, >= 2.2.0)
+ rack-test (>= 0.6.3)
+ rails-dom-testing (~> 2.0)
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
+ actiontext (7.0.4)
+ actionpack (= 7.0.4)
+ activerecord (= 7.0.4)
+ activestorage (= 7.0.4)
+ activesupport (= 7.0.4)
+ globalid (>= 0.6.0)
+ nokogiri (>= 1.8.5)
+ actionview (7.0.4)
+ activesupport (= 7.0.4)
+ builder (~> 3.1)
+ erubi (~> 1.4)
+ rails-dom-testing (~> 2.0)
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
+ activejob (7.0.4)
+ activesupport (= 7.0.4)
+ globalid (>= 0.3.6)
+ activemodel (7.0.4)
+ activesupport (= 7.0.4)
+ activerecord (7.0.4)
+ activemodel (= 7.0.4)
+ activesupport (= 7.0.4)
+ activestorage (7.0.4)
+ actionpack (= 7.0.4)
+ activejob (= 7.0.4)
+ activerecord (= 7.0.4)
+ activesupport (= 7.0.4)
+ marcel (~> 1.0)
+ mini_mime (>= 1.1.0)
+ activesupport (7.0.4)
+ concurrent-ruby (~> 1.0, >= 1.0.2)
+ i18n (>= 1.6, < 2)
+ minitest (>= 5.1)
+ tzinfo (~> 2.0)
+ addressable (2.8.1)
+ public_suffix (>= 2.0.2, < 6.0)
+ builder (3.2.4)
+ concurrent-ruby (1.1.10)
+ crack (0.4.5)
+ rexml
+ crass (1.0.6)
+ debug (1.6.2)
+ irb (>= 1.3.6)
+ reline (>= 0.3.1)
+ diff-lcs (1.5.0)
+ digest (3.1.0)
+ erubi (1.11.0)
+ faraday (2.5.2)
+ faraday-net_http (>= 2.0, < 3.1)
+ ruby2_keywords (>= 0.0.4)
+ faraday-net_http (3.0.0)
+ globalid (1.0.0)
+ activesupport (>= 5.0)
+ hashdiff (1.0.1)
+ hashie (5.0.0)
+ i18n (1.12.0)
+ concurrent-ruby (~> 1.0)
+ io-console (0.5.11)
+ irb (1.4.1)
+ reline (>= 0.3.0)
+ jwt (2.5.0)
+ loofah (2.19.0)
+ crass (~> 1.0.2)
+ nokogiri (>= 1.5.9)
+ mail (2.7.1)
+ mini_mime (>= 0.1.1)
+ marcel (1.0.2)
+ method_source (1.0.0)
+ mini_mime (1.1.2)
+ mini_portile2 (2.8.0)
+ minitest (5.16.3)
+ multi_xml (0.6.0)
+ net-imap (0.2.3)
+ digest
+ net-protocol
+ strscan
+ net-pop (0.1.1)
+ digest
+ net-protocol
+ timeout
+ net-protocol (0.1.3)
+ timeout
+ net-smtp (0.3.1)
+ digest
+ net-protocol
+ timeout
+ nio4r (2.5.8)
+ nokogiri (1.13.8)
+ mini_portile2 (~> 2.8.0)
+ racc (~> 1.4)
+ oauth2 (2.0.8)
+ faraday (>= 0.17.3, < 3.0)
+ jwt (>= 1.0, < 3.0)
+ multi_xml (~> 0.5)
+ rack (>= 1.2, < 3)
+ snaky_hash (~> 2.0)
+ version_gem (~> 1.1)
+ public_suffix (5.0.0)
+ racc (1.6.0)
+ rack (2.2.4)
+ rack-test (2.0.2)
+ rack (>= 1.3)
+ rails (7.0.4)
+ actioncable (= 7.0.4)
+ actionmailbox (= 7.0.4)
+ actionmailer (= 7.0.4)
+ actionpack (= 7.0.4)
+ actiontext (= 7.0.4)
+ actionview (= 7.0.4)
+ activejob (= 7.0.4)
+ activemodel (= 7.0.4)
+ activerecord (= 7.0.4)
+ activestorage (= 7.0.4)
+ activesupport (= 7.0.4)
+ bundler (>= 1.15.0)
+ railties (= 7.0.4)
+ rails-dom-testing (2.0.3)
+ activesupport (>= 4.2.0)
+ nokogiri (>= 1.6)
+ rails-html-sanitizer (1.4.3)
+ loofah (~> 2.3)
+ railties (7.0.4)
+ actionpack (= 7.0.4)
+ activesupport (= 7.0.4)
+ method_source
+ rake (>= 12.2)
+ thor (~> 1.0)
+ zeitwerk (~> 2.5)
+ rake (13.0.6)
+ reline (0.3.1)
+ io-console (~> 0.5)
+ rexml (3.2.5)
+ rspec (3.11.0)
+ rspec-core (~> 3.11.0)
+ rspec-expectations (~> 3.11.0)
+ rspec-mocks (~> 3.11.0)
+ rspec-core (3.11.0)
+ rspec-support (~> 3.11.0)
+ rspec-expectations (3.11.1)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.11.0)
+ rspec-mocks (3.11.1)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.11.0)
+ rspec-support (3.11.1)
+ ruby2_keywords (0.0.5)
+ snaky_hash (2.0.0)
+ hashie
+ version_gem (~> 1.1)
+ strscan (3.0.4)
+ thor (1.2.1)
+ timeout (0.3.0)
+ tzinfo (2.0.5)
+ concurrent-ruby (~> 1.0)
+ version_gem (1.1.0)
+ webmock (3.18.1)
+ addressable (>= 2.8.0)
+ crack (>= 0.3.2)
+ hashdiff (>= 0.4.0, < 2.0.0)
+ websocket-driver (0.7.5)
+ websocket-extensions (>= 0.1.0)
+ websocket-extensions (0.1.5)
+ zeitwerk (2.6.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ debug (>= 1.0.0)
+ microsoft_graph_mailer!
+ rails
+ rspec (~> 3.11.0)
+ webmock (~> 3.18.1)
+
+BUNDLED WITH
+ 2.3.22
diff --git a/vendor/gems/microsoft_graph_mailer/LICENSE.txt b/vendor/gems/microsoft_graph_mailer/LICENSE.txt
new file mode 100644
index 00000000000..7ed79aa0423
--- /dev/null
+++ b/vendor/gems/microsoft_graph_mailer/LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2022 GitLab B.V.
+
+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/microsoft_graph_mailer/README.md b/vendor/gems/microsoft_graph_mailer/README.md
new file mode 100644
index 00000000000..dd9dfecfc56
--- /dev/null
+++ b/vendor/gems/microsoft_graph_mailer/README.md
@@ -0,0 +1,104 @@
+# microsoft_graph_mailer
+
+This gem allows delivery of emails using [Microsoft Graph API](https://docs.microsoft.com/en-us/graph/api/user-sendmail) with [OAuth 2.0 client credentials flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow).
+
+## The reason for this gem
+
+See [https://gitlab.com/groups/gitlab-org/-/epics/8259](https://gitlab.com/groups/gitlab-org/-/epics/8259).
+
+## Installation
+
+Add this line to your application's Gemfile:
+
+```ruby
+gem 'microsoft_graph_mailer'
+```
+
+And then execute:
+
+```shell
+bundle
+```
+
+Or install it yourself as:
+
+```shell
+gem install microsoft_graph_mailer
+```
+
+## Settings
+
+To use the Microsoft Graph API to send mails, you will
+need to create an application in the Azure Active Directory. See the
+[Microsoft instructions](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app) for more details:
+
+1. Sign in to the [Azure portal](https://portal.azure.com).
+1. Search for and select `Azure Active Directory`.
+1. Under `Manage`, select `App registrations` > `New registration`.
+1. Enter a `Name` for your application, such as `MicrosoftGraphMailer`. Users of your app might see this name, and you can change it later.
+1. If `Supported account types` is listed, select the appropriate option.
+1. Leave `Redirect URI` blank. This is not needed.
+1. Select `Register`.
+1. Under `Manage`, select `Certificates & secrets`.
+1. Under `Client secrets`, select `New client secret`, and enter a name.
+1. Under `Expires`, select `Never`, unless you plan on updating the credentials every time it expires.
+1. Select `Add`. Record the secret value in a safe location for use in a later step.
+1. Under `Manage`, select `API Permissions` > `Add a permission`. Select `Microsoft Graph`.
+1. Select `Application permissions`.
+1. Under the `Mail` node, select `Mail.Send`. Then select Add permissions.
+1. If `User.Read` is listed in the permission list, you can delete this.
+1. Click `Grant admin consent` for these permissions.
+
+- `user_id` - The unique identifier for the user. To use Microsoft Graph on behalf of the user.
+- `tenant` - The directory tenant the application plans to operate against, in GUID or domain-name format.
+- `client_id` - The application ID that's assigned to your app. You can find this information in the portal where you registered your app.
+- `client_secret` - The client secret that you generated for your app in the app registration portal.
+
+## Usage
+
+```ruby
+require "microsoft_graph_mailer"
+
+microsoft_graph_mailer = MicrosoftGraphMailer::Delivery.new(
+ {
+ user_id: "YOUR-USER-ID",
+ tenant: "YOUR-TENANT-ID",
+ client_id: "YOUR-CLIENT-ID",
+ client_secret: "YOUR-CLIENT-SECRET-ID"
+ # Defaults to "https://login.microsoftonline.com".
+ azure_ad_endpoint: "https://login.microsoftonline.us",
+ # Defaults to "https://graph.microsoft.com".
+ graph_endpoint: "https://graph.microsoft.us"
+ }
+)
+
+message = Mail.new do
+ from "about@gitlab.com"
+ to "to@example.com"
+ subject "GitLab Mission"
+
+ html_part do
+ content_type "text/html; charset=UTF-8"
+ body "It is GitLab's mission to make it so that <strong>everyone can contribute</strong>."
+ end
+end
+
+microsoft_graph_mailer.deliver!(message)
+```
+
+## Usage with ActionMailer
+
+```ruby
+ActionMailer::Base.delivery_method = :microsoft_graph
+
+ActionMailer::Base.microsoft_graph_settings = {
+ user_id: "YOUR-USER-ID",
+ tenant: "YOUR-TENANT-ID",
+ client_id: "YOUR-CLIENT-ID",
+ client_secret: "YOUR-CLIENT-SECRET-ID"
+ # Defaults to "https://login.microsoftonline.com".
+ azure_ad_endpoint: "https://login.microsoftonline.us",
+ # Defaults to "https://graph.microsoft.com".
+ graph_endpoint: "https://graph.microsoft.us"
+}
+```
diff --git a/vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer.rb b/vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer.rb
new file mode 100644
index 00000000000..8bd252aac94
--- /dev/null
+++ b/vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require_relative "microsoft_graph_mailer/delivery"
+require_relative "microsoft_graph_mailer/railtie" if defined?(Rails::Railtie)
+require_relative "microsoft_graph_mailer/version"
+
+module MicrosoftGraphMailer
+ class Error < StandardError
+ end
+
+ class ConfigurationError < Error
+ end
+
+ class DeliveryError < Error
+ end
+end
diff --git a/vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer/client.rb b/vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer/client.rb
new file mode 100644
index 00000000000..b779bb28c39
--- /dev/null
+++ b/vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer/client.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require "oauth2"
+
+module MicrosoftGraphMailer
+ class Client
+ attr_accessor :user_id, :tenant, :client_id, :client_secret, :azure_ad_endpoint, :graph_endpoint
+
+ def initialize(user_id:, tenant:, client_id:, client_secret:, azure_ad_endpoint:, graph_endpoint:)
+ @user_id = user_id
+ @tenant = tenant
+ @client_id = client_id
+ @client_secret = client_secret
+ @azure_ad_endpoint = azure_ad_endpoint
+ @graph_endpoint = graph_endpoint
+ end
+
+ def send_mail(message_in_mime_format)
+ # https://docs.microsoft.com/en-us/graph/api/user-sendmail
+ token.post(
+ send_mail_url,
+ headers: { "Content-type" => "text/plain" },
+ body: Base64.encode64(message_in_mime_format)
+ )
+ end
+
+ private
+
+ def token
+ # https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow
+ OAuth2::Client.new(
+ client_id,
+ client_secret,
+ site: azure_ad_endpoint,
+ token_url: "/#{tenant}/oauth2/v2.0/token"
+ ).client_credentials.get_token({ scope: scope })
+ end
+
+ def scope
+ "#{graph_endpoint}/.default"
+ end
+
+ def base_url
+ "#{graph_endpoint}/v1.0/users/#{user_id}"
+ end
+
+ def send_mail_url
+ "#{base_url}/sendMail"
+ end
+ end
+end
diff --git a/vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer/delivery.rb b/vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer/delivery.rb
new file mode 100644
index 00000000000..c807ee4319e
--- /dev/null
+++ b/vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer/delivery.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+require_relative "client"
+
+module MicrosoftGraphMailer
+ class Delivery
+ attr_reader :microsoft_graph_settings
+
+ def initialize(microsoft_graph_settings)
+ @microsoft_graph_settings = microsoft_graph_settings
+
+ [:user_id, :tenant, :client_id, :client_secret].each do |setting|
+ unless microsoft_graph_settings[setting]
+ raise MicrosoftGraphMailer::ConfigurationError, "'#{setting}' is missing"
+ end
+ end
+
+ @microsoft_graph_settings[:azure_ad_endpoint] ||= "https://login.microsoftonline.com"
+ @microsoft_graph_settings[:graph_endpoint] ||= "https://graph.microsoft.com"
+ end
+
+ def deliver!(message)
+ # https://github.com/mikel/mail/pull/872
+ if message[:bcc]
+ previous_message_bcc_include_in_headers = message[:bcc].include_in_headers
+ message[:bcc].include_in_headers = true
+ end
+
+ message_in_mime_format = message.encoded
+
+ client = MicrosoftGraphMailer::Client.new(
+ user_id: microsoft_graph_settings[:user_id],
+ tenant: microsoft_graph_settings[:tenant],
+ client_id: microsoft_graph_settings[:client_id],
+ client_secret: microsoft_graph_settings[:client_secret],
+ azure_ad_endpoint: microsoft_graph_settings[:azure_ad_endpoint],
+ graph_endpoint: microsoft_graph_settings[:graph_endpoint]
+ )
+
+ response = client.send_mail(message_in_mime_format)
+
+ raise MicrosoftGraphMailer::DeliveryError unless response.status == 202
+
+ response
+ ensure
+ message[:bcc].include_in_headers = previous_message_bcc_include_in_headers if message[:bcc]
+ end
+ end
+end
diff --git a/vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer/railtie.rb b/vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer/railtie.rb
new file mode 100644
index 00000000000..607d68cc454
--- /dev/null
+++ b/vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer/railtie.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+require_relative "delivery"
+
+module MicrosoftGraphMailer
+ class Railtie < Rails::Railtie
+ ActiveSupport.on_load(:action_mailer) do
+ add_delivery_method :microsoft_graph, MicrosoftGraphMailer::Delivery
+ end
+ end
+end
diff --git a/vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer/version.rb b/vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer/version.rb
new file mode 100644
index 00000000000..057aebdad2b
--- /dev/null
+++ b/vendor/gems/microsoft_graph_mailer/lib/microsoft_graph_mailer/version.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+module MicrosoftGraphMailer
+ VERSION = "0.1.0"
+end
diff --git a/vendor/gems/microsoft_graph_mailer/microsoft_graph_mailer.gemspec b/vendor/gems/microsoft_graph_mailer/microsoft_graph_mailer.gemspec
new file mode 100644
index 00000000000..af9fb5987ca
--- /dev/null
+++ b/vendor/gems/microsoft_graph_mailer/microsoft_graph_mailer.gemspec
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require_relative "lib/microsoft_graph_mailer/version"
+
+Gem::Specification.new do |spec|
+ spec.name = "microsoft_graph_mailer"
+ spec.version = MicrosoftGraphMailer::VERSION
+ spec.authors = ["Bogdan Denkovych"]
+ spec.email = ["bdenkovych@gitlab.com"]
+
+ spec.summary = "Allows delivery of emails using Microsoft Graph API with OAuth 2.0 client credentials flow"
+ spec.homepage = "https://gitlab.com/gitlab-org/gitlab/-/tree/master/vendor/gems/microsoft_graph_mailer"
+ spec.license = "MIT"
+ spec.required_ruby_version = ">= 2.7.0"
+
+ spec.metadata["homepage_uri"] = spec.homepage
+ spec.metadata["source_code_uri"] = "https://gitlab.com/gitlab-org/gitlab/-/tree/master/vendor/gems/microsoft_graph_mailer"
+
+ spec.files = Dir["lib/**/*.rb"] + ["LICENSE.txt", "README.md"]
+ spec.require_paths = ["lib"]
+
+ spec.add_runtime_dependency "mail", "~> 2.7"
+ spec.add_runtime_dependency "oauth2", [">= 1.4.4", "< 3"]
+
+ spec.add_development_dependency "debug", ">= 1.0.0"
+ spec.add_development_dependency "rails"
+ spec.add_development_dependency "rspec", "~> 3.11.0"
+ spec.add_development_dependency "webmock", "~> 3.18.1"
+end
diff --git a/vendor/gems/microsoft_graph_mailer/spec/fixtures/attachments/gitlab.txt b/vendor/gems/microsoft_graph_mailer/spec/fixtures/attachments/gitlab.txt
new file mode 100644
index 00000000000..1df29200042
--- /dev/null
+++ b/vendor/gems/microsoft_graph_mailer/spec/fixtures/attachments/gitlab.txt
@@ -0,0 +1 @@
+GitLab
diff --git a/vendor/gems/microsoft_graph_mailer/spec/fixtures/attachments/gitlab_logo.png b/vendor/gems/microsoft_graph_mailer/spec/fixtures/attachments/gitlab_logo.png
new file mode 100644
index 00000000000..12525056939
--- /dev/null
+++ b/vendor/gems/microsoft_graph_mailer/spec/fixtures/attachments/gitlab_logo.png
Binary files differ
diff --git a/vendor/gems/microsoft_graph_mailer/spec/lib/microsoft_graph_mailer/delivery_spec.rb b/vendor/gems/microsoft_graph_mailer/spec/lib/microsoft_graph_mailer/delivery_spec.rb
new file mode 100644
index 00000000000..23096e75b76
--- /dev/null
+++ b/vendor/gems/microsoft_graph_mailer/spec/lib/microsoft_graph_mailer/delivery_spec.rb
@@ -0,0 +1,128 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+require "securerandom"
+
+RSpec.describe MicrosoftGraphMailer::Delivery do
+ let(:microsoft_graph_settings) do
+ {
+ user_id: SecureRandom.hex,
+ tenant: SecureRandom.hex,
+ client_id: SecureRandom.hex,
+ client_secret: SecureRandom.hex,
+ azure_ad_endpoint: "https://test-azure_ad_endpoint",
+ graph_endpoint: "https://test-graph_endpoint"
+ }
+ end
+
+ subject { described_class.new(microsoft_graph_settings) }
+
+ describe ".new" do
+ it "sets #microsoft_graph_settings" do
+ expect(subject.microsoft_graph_settings).to eq(microsoft_graph_settings)
+ end
+
+ [:user_id, :tenant, :client_id, :client_secret].each do |setting|
+ it "raises MicrosoftGraphMailer::ConfigurationError when '#{setting}' is missing" do
+ microsoft_graph_settings[setting] = nil
+
+ expect { subject }
+ .to raise_error(MicrosoftGraphMailer::ConfigurationError, "'#{setting}' is missing")
+ end
+ end
+
+ it "sets azure_ad_endpoint setting to 'https://login.microsoftonline.com' when it is missing" do
+ microsoft_graph_settings[:azure_ad_endpoint] = nil
+
+ expect(subject.microsoft_graph_settings[:azure_ad_endpoint]).to eq("https://login.microsoftonline.com")
+ end
+
+ it "sets graph_endpoint setting to 'https://graph.microsoft.com' when it is missing" do
+ microsoft_graph_settings[:graph_endpoint] = nil
+
+ expect(subject.microsoft_graph_settings[:graph_endpoint]).to eq("https://graph.microsoft.com")
+ end
+ end
+
+ describe "#deliver!" do
+ let(:access_token) { SecureRandom.hex }
+
+ let(:message) do
+ Mail.new do
+ from "about@gitlab.com"
+
+ to "to@example.com"
+
+ cc "cc@example.com"
+
+ subject "GitLab Mission"
+
+ text_part do
+ body "It is GitLab's mission to make it so that everyone can contribute."
+ end
+
+ html_part do
+ content_type "text/html; charset=UTF-8"
+ body "It is GitLab's mission to make it so that <strong>everyone can contribute</strong>."
+ end
+
+ add_file fixture_path("attachments", "gitlab.txt")
+
+ add_file fixture_path("attachments", "gitlab_logo.png")
+ end
+ end
+
+ context "when token request is successful" do
+ before do
+ stub_token_request(microsoft_graph_settings: subject.microsoft_graph_settings, access_token: access_token, response_status: 200)
+ end
+
+ context "when send mail request returns response status 202" do
+ it "sends mail and returns an instance of OAuth2::Response" do
+ stub_send_mail_request(microsoft_graph_settings: subject.microsoft_graph_settings, access_token: access_token, message: message, response_status: 202)
+
+ expect(subject.deliver!(message)).to be_an_instance_of(OAuth2::Response)
+ end
+
+ it "sends mail including bcc field" do
+ message.bcc = "bcc@example.com"
+
+ stub_send_mail_request(microsoft_graph_settings: subject.microsoft_graph_settings, access_token: access_token, message: message, response_status: 202)
+
+ subject.deliver!(message)
+ end
+
+ it "does not change message[:bcc].include_in_headers" do
+ message.bcc = "bcc@example.com"
+ expected_message_bcc_include_in_headers = "42"
+ message[:bcc].include_in_headers = expected_message_bcc_include_in_headers
+
+ stub_send_mail_request(microsoft_graph_settings: subject.microsoft_graph_settings, access_token: access_token, message: message, response_status: 202)
+
+ subject.deliver!(message)
+
+ expect(message[:bcc].include_in_headers).to eq(expected_message_bcc_include_in_headers)
+ end
+ end
+
+ context "when send mail request returns response status other than 202" do
+ it "raises MicrosoftGraphMailer::DeliveryError" do
+ stub_send_mail_request(microsoft_graph_settings: subject.microsoft_graph_settings, access_token: access_token, message: message, response_status: 200)
+
+ expect { subject.deliver!(message) }.to raise_error(MicrosoftGraphMailer::DeliveryError)
+ end
+ end
+ end
+
+ context "when token request is not successful" do
+ before do
+ stub_token_request(microsoft_graph_settings: subject.microsoft_graph_settings, access_token: access_token, response_status: 503)
+ end
+
+ it "raises OAuth2::Error" do
+ expect { subject.deliver!(message) }.to raise_error(OAuth2::Error)
+ end
+ end
+ end
+end
diff --git a/vendor/gems/microsoft_graph_mailer/spec/lib/microsoft_graph_mailer/railtie_spec.rb b/vendor/gems/microsoft_graph_mailer/spec/lib/microsoft_graph_mailer/railtie_spec.rb
new file mode 100644
index 00000000000..d1a60522cdf
--- /dev/null
+++ b/vendor/gems/microsoft_graph_mailer/spec/lib/microsoft_graph_mailer/railtie_spec.rb
@@ -0,0 +1,140 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+require "securerandom"
+
+class TestMailer < ActionMailer::Base
+ def gitlab_mission(to:, cc: [])
+ mail(from: "about@gitlab.com", to: to, cc: cc, subject: "GitLab Mission") do |format|
+ format.text { render plain: "It is GitLab's mission to make it so that everyone can contribute." }
+ format.html { render html: "It is GitLab's mission to make it so that <strong>everyone can contribute</strong>.".html_safe }
+ end
+
+ mail.attachments["gitlab.txt"] = File.read(fixture_path("attachments", "gitlab.txt"))
+
+ mail.attachments["gitlab_logo.png"] = File.read(fixture_path("attachments", "gitlab_logo.png"))
+ end
+end
+
+RSpec.describe MicrosoftGraphMailer::Railtie do
+ let(:microsoft_graph_settings) do
+ {
+ user_id: SecureRandom.hex,
+ tenant: SecureRandom.hex,
+ client_id: SecureRandom.hex,
+ client_secret: SecureRandom.hex,
+ azure_ad_endpoint: "https://test-azure_ad_endpoint",
+ graph_endpoint: "https://test-graph_endpoint"
+ }
+ end
+
+ let(:message) { TestMailer.gitlab_mission(to: "to@example.com", cc: "cc@example.com") }
+
+ before do
+ ActionMailer::Base.delivery_method = :microsoft_graph
+ ActionMailer::Base.microsoft_graph_settings = microsoft_graph_settings
+ end
+
+ it "its superclass is Rails::Railtie" do
+ expect(MicrosoftGraphMailer::Railtie.superclass).to eq(Rails::Railtie)
+ end
+
+ describe "settings" do
+ describe "ActionMailer::Base.delivery_methods[:microsoft_graph]" do
+ it "returns MicrosoftGraphMailer::Delivery" do
+ expect(ActionMailer::Base.delivery_methods[:microsoft_graph]).to eq(MicrosoftGraphMailer::Delivery)
+ end
+ end
+
+ describe "ActionMailer::Base.microsoft_graph_settings" do
+ it "returns microsoft_graph_settings" do
+ expect(ActionMailer::Base.microsoft_graph_settings).to eq(microsoft_graph_settings)
+ end
+ end
+
+ it "sets #microsoft_graph_settings" do
+ expect(message.delivery_method.microsoft_graph_settings).to eq(microsoft_graph_settings)
+ end
+
+ [:user_id, :tenant, :client_id, :client_secret].each do |setting|
+ it "raises MicrosoftGraphMailer::ConfigurationError when '#{setting}' is missing" do
+ microsoft_graph_settings[setting] = nil
+ ActionMailer::Base.microsoft_graph_settings = microsoft_graph_settings
+
+ expect { message.delivery_method }
+ .to raise_error(MicrosoftGraphMailer::ConfigurationError, "'#{setting}' is missing")
+ end
+ end
+
+ it "sets azure_ad_endpoint setting to 'https://login.microsoftonline.com' when it is missing" do
+ microsoft_graph_settings[:azure_ad_endpoint] = nil
+ ActionMailer::Base.microsoft_graph_settings = microsoft_graph_settings
+
+ expect(message.delivery_method.microsoft_graph_settings[:azure_ad_endpoint]).to eq("https://login.microsoftonline.com")
+ end
+
+ it "sets graph_endpoint setting to 'https://graph.microsoft.com' when it is missing" do
+ microsoft_graph_settings[:graph_endpoint] = nil
+ ActionMailer::Base.microsoft_graph_settings = microsoft_graph_settings
+
+ expect(message.delivery_method.microsoft_graph_settings[:graph_endpoint]).to eq("https://graph.microsoft.com")
+ end
+ end
+
+ describe "ActionMailer::MessageDelivery#deliver_now" do
+ let(:access_token) { SecureRandom.hex }
+
+ context "when token request is successful" do
+ before do
+ stub_token_request(microsoft_graph_settings: microsoft_graph_settings, access_token: access_token, response_status: 200)
+ end
+
+ context "when send mail request returns response status 202" do
+ it "sends and returns mail" do
+ stub_send_mail_request(microsoft_graph_settings: microsoft_graph_settings, access_token: access_token, message: message, response_status: 202)
+
+ expect(message.deliver_now).to eq(message)
+ end
+
+ it "sends mail including bcc field" do
+ message.bcc = "bcc@example.com"
+
+ stub_send_mail_request(microsoft_graph_settings: microsoft_graph_settings, access_token: access_token, message: message, response_status: 202)
+
+ message.deliver_now
+ end
+
+ it "does not change message[:bcc].include_in_headers" do
+ message.bcc = "bcc@example.com"
+ expected_message_bcc_include_in_headers = "42"
+ message[:bcc].include_in_headers = expected_message_bcc_include_in_headers
+
+ stub_send_mail_request(microsoft_graph_settings: microsoft_graph_settings, access_token: access_token, message: message, response_status: 202)
+
+ message.deliver_now
+
+ expect(message[:bcc].include_in_headers).to eq(expected_message_bcc_include_in_headers)
+ end
+ end
+
+ context "when send mail request returns response status other than 202" do
+ it "raises MicrosoftGraphMailer::DeliveryError" do
+ stub_send_mail_request(microsoft_graph_settings: microsoft_graph_settings, access_token: access_token, message: message, response_status: 200)
+
+ expect { message.deliver_now }.to raise_error(MicrosoftGraphMailer::DeliveryError)
+ end
+ end
+ end
+
+ context "when token request is not successful" do
+ before do
+ stub_token_request(microsoft_graph_settings: microsoft_graph_settings, access_token: access_token, response_status: 503)
+ end
+
+ it "raises OAuth2::Error" do
+ expect { message.deliver_now }.to raise_error(OAuth2::Error)
+ end
+ end
+ end
+end
diff --git a/vendor/gems/microsoft_graph_mailer/spec/lib/microsoft_graph_mailer_spec.rb b/vendor/gems/microsoft_graph_mailer/spec/lib/microsoft_graph_mailer_spec.rb
new file mode 100644
index 00000000000..3306e91db38
--- /dev/null
+++ b/vendor/gems/microsoft_graph_mailer/spec/lib/microsoft_graph_mailer_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+RSpec.describe MicrosoftGraphMailer do
+ describe "::VERSION" do
+ it "returns a version number" do
+ expect(MicrosoftGraphMailer::VERSION).to eq("0.1.0")
+ end
+ end
+
+ describe "::Error" do
+ it "its superclass is StandardError" do
+ expect(MicrosoftGraphMailer::Error.superclass).to eq(StandardError)
+ end
+ end
+
+ describe "::ConfigurationError" do
+ it "its superclass is MicrosoftGraphMailer::Error" do
+ expect(MicrosoftGraphMailer::ConfigurationError.superclass).to eq(MicrosoftGraphMailer::Error)
+ end
+ end
+
+ describe "::DeliveryError" do
+ it "its superclass is MicrosoftGraphMailer::Error" do
+ expect(MicrosoftGraphMailer::DeliveryError.superclass).to eq(MicrosoftGraphMailer::Error)
+ end
+ end
+end
diff --git a/vendor/gems/microsoft_graph_mailer/spec/spec_helper.rb b/vendor/gems/microsoft_graph_mailer/spec/spec_helper.rb
new file mode 100644
index 00000000000..ac5dd817904
--- /dev/null
+++ b/vendor/gems/microsoft_graph_mailer/spec/spec_helper.rb
@@ -0,0 +1,60 @@
+# frozen_string_literal: true
+
+require "rails"
+require "action_mailer/railtie"
+
+require "microsoft_graph_mailer"
+
+require "mail"
+
+require "webmock/rspec"
+
+RSpec.configure do |config|
+end
+
+def fixture_path(*path)
+ File.join(__dir__, "fixtures", path)
+end
+
+def stub_token_request(microsoft_graph_settings:, access_token:, response_status:)
+ stub_request(
+ :post,
+ "#{microsoft_graph_settings[:azure_ad_endpoint]}/#{microsoft_graph_settings[:tenant]}/oauth2/v2.0/token"
+ ).with(
+ body: {
+ "grant_type" => "client_credentials",
+ "scope" => "#{microsoft_graph_settings[:graph_endpoint]}/.default"
+ }
+ ).to_return(
+ body: {
+ "token_type" => "Bearer",
+ "expires_in" => "3599",
+ "access_token" => access_token
+ }.to_json,
+ status: response_status,
+ headers: { "content-type" => "application/json; charset=utf-8" }
+ )
+end
+
+def stub_send_mail_request(microsoft_graph_settings:, access_token:, message:, response_status:)
+ if message[:bcc]
+ previous_message_bcc_include_in_headers = message[:bcc].include_in_headers
+ message[:bcc].include_in_headers = true
+ end
+
+ stub_request(
+ :post,
+ "#{microsoft_graph_settings[:graph_endpoint]}/v1.0/users/#{microsoft_graph_settings[:user_id]}/sendMail"
+ ).with(
+ body: Base64.encode64(message.encoded),
+ headers: {
+ "Authorization" => "Bearer #{access_token}",
+ "Content-Type" => "text/plain"
+ }
+ ).to_return(
+ body: "",
+ status: response_status
+ )
+ensure
+ message[:bcc].include_in_headers = previous_message_bcc_include_in_headers if message[:bcc]
+end
diff --git a/vendor/gems/omniauth-azure-oauth2/Gemfile.lock b/vendor/gems/omniauth-azure-oauth2/Gemfile.lock
index 0bd5d401175..d2bbe3e8d2f 100644
--- a/vendor/gems/omniauth-azure-oauth2/Gemfile.lock
+++ b/vendor/gems/omniauth-azure-oauth2/Gemfile.lock
@@ -3,7 +3,7 @@ PATH
specs:
omniauth-azure-oauth2 (0.0.10)
jwt (>= 1.0, < 3.0)
- omniauth (~> 1.0, < 3)
+ omniauth (~> 2.0)
omniauth-oauth2 (~> 1.4)
GEM
@@ -19,16 +19,17 @@ GEM
multi_xml (0.6.0)
mustermann (2.0.2)
ruby2_keywords (~> 0.0.1)
- oauth2 (2.0.6)
+ oauth2 (2.0.3)
faraday (>= 0.17.3, < 3.0)
jwt (>= 1.0, < 3.0)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
rash_alt (>= 0.4, < 1)
- version_gem (~> 1.1)
- omniauth (1.9.1)
+ version_gem (~> 1.0)
+ omniauth (2.1.0)
hashie (>= 3.4.6)
- rack (>= 1.6.2, < 3)
+ rack (>= 2.2.3)
+ rack-protection
omniauth-oauth2 (1.7.3)
oauth2 (>= 1.4, < 3)
omniauth (>= 1.9, < 3)
@@ -70,4 +71,4 @@ DEPENDENCIES
sinatra
BUNDLED WITH
- 2.3.20
+ 2.3.21
diff --git a/vendor/gems/omniauth-azure-oauth2/lib/omniauth/strategies/azure_oauth2.rb b/vendor/gems/omniauth-azure-oauth2/lib/omniauth/strategies/azure_oauth2.rb
index f18babc0619..d71911b9876 100644
--- a/vendor/gems/omniauth-azure-oauth2/lib/omniauth/strategies/azure_oauth2.rb
+++ b/vendor/gems/omniauth-azure-oauth2/lib/omniauth/strategies/azure_oauth2.rb
@@ -59,8 +59,10 @@ module OmniAuth
super.merge(resource: azure_resource || options.resource)
end
+ # for compatibility with OmniAuth 2.0
+ # see https://github.com/RIPAGlobal/omniauth-azure-activedirectory-v2/pull/6
def callback_url
- full_host + script_name + callback_path
+ full_host + callback_path
end
def raw_info
diff --git a/vendor/gems/omniauth-azure-oauth2/omniauth-azure-oauth2.gemspec b/vendor/gems/omniauth-azure-oauth2/omniauth-azure-oauth2.gemspec
index 6e1bc583881..1299285b945 100644
--- a/vendor/gems/omniauth-azure-oauth2/omniauth-azure-oauth2.gemspec
+++ b/vendor/gems/omniauth-azure-oauth2/omniauth-azure-oauth2.gemspec
@@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
gem.version = OmniAuth::AzureOauth2::VERSION
gem.license = "MIT"
- gem.add_runtime_dependency 'omniauth', '~> 1.0', '< 3'
+ gem.add_runtime_dependency 'omniauth', '~> 2.0'
gem.add_dependency 'jwt', ['>= 1.0', '< 3.0']
gem.add_runtime_dependency 'omniauth-oauth2', '~> 1.4'
diff --git a/vendor/gems/omniauth-cas3/Gemfile.lock b/vendor/gems/omniauth-cas3/Gemfile.lock
index 4c59eb05d50..a856e78f00f 100644
--- a/vendor/gems/omniauth-cas3/Gemfile.lock
+++ b/vendor/gems/omniauth-cas3/Gemfile.lock
@@ -4,29 +4,30 @@ PATH
omniauth-cas3 (1.1.4)
addressable (~> 2.3)
nokogiri (~> 1.7, >= 1.7.1)
- omniauth (~> 1.2, < 3)
+ omniauth (~> 2.0)
GEM
remote: https://rubygems.org/
specs:
- addressable (2.8.0)
- public_suffix (>= 2.0.2, < 5.0)
+ addressable (2.8.1)
+ public_suffix (>= 2.0.2, < 6.0)
awesome_print (1.9.2)
crack (0.4.5)
rexml
diff-lcs (1.5.0)
hashdiff (1.0.1)
hashie (5.0.0)
- mini_portile2 (2.8.0)
nokogiri (1.13.7)
- mini_portile2 (~> 2.8.0)
racc (~> 1.4)
- omniauth (1.9.1)
+ omniauth (2.1.0)
hashie (>= 3.4.6)
- rack (>= 1.6.2, < 3)
- public_suffix (4.0.7)
+ rack (>= 2.2.3)
+ rack-protection
+ public_suffix (5.0.0)
racc (1.6.0)
rack (2.2.4)
+ rack-protection (2.2.2)
+ rack
rack-test (0.8.3)
rack (>= 1.0, < 3)
rake (10.5.0)
@@ -44,7 +45,7 @@ GEM
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.11.0)
rspec-support (3.11.0)
- webmock (3.14.0)
+ webmock (3.18.1)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
@@ -61,4 +62,4 @@ DEPENDENCIES
webmock
BUNDLED WITH
- 2.3.18
+ 2.3.21
diff --git a/vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3.rb b/vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3.rb
index 7271621c564..441529b67d8 100644
--- a/vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3.rb
+++ b/vendor/gems/omniauth-cas3/lib/omniauth/strategies/cas3.rb
@@ -62,9 +62,14 @@ module OmniAuth
end
extra do
- prune!(
- raw_info.delete_if{ |k,v| AuthHashSchemaKeys.include?(k) }
- )
+ hash = {}
+
+ unless skip_info?
+ hash = raw_info.dup
+ hash.delete_if { |k, _v| AuthHashSchemaKeys.include?(k) }
+ end
+
+ prune! hash
end
uid do
diff --git a/vendor/gems/omniauth-cas3/omniauth-cas3.gemspec b/vendor/gems/omniauth-cas3/omniauth-cas3.gemspec
index abbcaa268d0..c976d85df99 100644
--- a/vendor/gems/omniauth-cas3/omniauth-cas3.gemspec
+++ b/vendor/gems/omniauth-cas3/omniauth-cas3.gemspec
@@ -14,7 +14,7 @@ Gem::Specification.new do |gem|
gem.require_paths = ["lib"]
gem.version = Omniauth::Cas3::VERSION
- gem.add_dependency 'omniauth', '~> 1.2', '< 3'
+ gem.add_dependency 'omniauth', '~> 2.0'
gem.add_dependency 'nokogiri', '~> 1.7', '>= 1.7.1'
gem.add_dependency 'addressable', '~> 2.3'
diff --git a/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3_spec.rb b/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3_spec.rb
index fd61fc79580..f434d711f02 100644
--- a/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3_spec.rb
+++ b/vendor/gems/omniauth-cas3/spec/omniauth/strategies/cas3_spec.rb
@@ -1,4 +1,5 @@
require 'spec_helper'
+require 'securerandom'
describe OmniAuth::Strategies::CAS3, type: :strategy do
include Rack::Test::Methods
@@ -15,17 +16,32 @@ describe OmniAuth::Strategies::CAS3, type: :strategy do
}.to_app
end
+ let(:csrf_token) { SecureRandom.base64(32) }
+ let(:base_env) { { 'rack.session' => { csrf: csrf_token }, 'rack.input' => StringIO.new("authenticity_token=#{escaped_token}") } }
+ let(:post_env) { make_env('/auth/cas3', base_env.merge(request_env)) }
+ let(:escaped_token) { URI.encode_www_form_component(csrf_token, Encoding::UTF_8) }
+
+ def make_env(path = '/auth/cas3', props = {})
+ {
+ 'REQUEST_METHOD' => 'POST',
+ 'PATH_INFO' => path,
+ 'rack.session' => {},
+ 'rack.input' => StringIO.new('test=true')
+ }.merge(props)
+ end
+
# TODO: Verify that these are even useful tests
shared_examples_for 'a CAS redirect response' do
let(:redirect_params) { 'service=' + Rack::Utils.escape("http://example.org/auth/cas3/callback?url=#{Rack::Utils.escape(return_url)}") }
- before { get url, nil, request_env }
+ before { post url, nil, post_env }
subject { last_response }
it { should be_redirect }
it 'redirects to the CAS server' do
+ expect(subject.status).to eq(302)
expect(subject.headers).to include 'Location' => "http://cas.example.org:8080/login?#{redirect_params}"
end
end
@@ -78,7 +94,7 @@ describe OmniAuth::Strategies::CAS3, type: :strategy do
it { should include('ssl' => true) }
end
- describe 'GET /auth/cas3' do
+ describe 'POST /auth/cas3' do
let(:return_url) { 'http://myapp.com/admin/foo' }
context 'with a referer' do
diff --git a/vendor/gems/omniauth-gitlab/Gemfile.lock b/vendor/gems/omniauth-gitlab/Gemfile.lock
index b5979104080..b59ba59b95b 100644
--- a/vendor/gems/omniauth-gitlab/Gemfile.lock
+++ b/vendor/gems/omniauth-gitlab/Gemfile.lock
@@ -2,7 +2,7 @@ PATH
remote: .
specs:
omniauth-gitlab (4.0.0)
- omniauth (~> 1.0)
+ omniauth (~> 2.0)
omniauth-oauth2 (~> 1.7.1)
GEM
@@ -24,13 +24,16 @@ GEM
rack (>= 1.2, < 3)
rash_alt (>= 0.4, < 1)
version_gem (~> 1.0)
- omniauth (1.9.1)
+ omniauth (2.1.0)
hashie (>= 3.4.6)
- rack (>= 1.6.2, < 3)
+ rack (>= 2.2.3)
+ rack-protection
omniauth-oauth2 (1.7.3)
oauth2 (>= 1.4, < 3)
omniauth (>= 1.9, < 3)
- rack (2.2.3.1)
+ rack (2.2.4)
+ rack-protection (2.2.2)
+ rack
rake (13.0.6)
rash_alt (0.4.12)
hashie (>= 3.4)
diff --git a/vendor/gems/omniauth-gitlab/lib/omniauth/strategies/gitlab.rb b/vendor/gems/omniauth-gitlab/lib/omniauth/strategies/gitlab.rb
index 19ee02e78c4..3c23d8e2b19 100644
--- a/vendor/gems/omniauth-gitlab/lib/omniauth/strategies/gitlab.rb
+++ b/vendor/gems/omniauth-gitlab/lib/omniauth/strategies/gitlab.rb
@@ -30,15 +30,15 @@ module OmniAuth
@raw_info ||= access_token.get(user_endpoint_url).parsed
end
+ def callback_url
+ options.redirect_url || (full_host + callback_path)
+ end
+
private
def user_endpoint_url
options.client_options.site.match(API_SUFFIX_REGEX) ? 'user' : 'api/v4/user'
end
-
- def callback_url
- options.redirect_url || (full_host + script_name + callback_path)
- end
end
end
end
diff --git a/vendor/gems/omniauth-gitlab/omniauth-gitlab.gemspec b/vendor/gems/omniauth-gitlab/omniauth-gitlab.gemspec
index be25cb50af6..ca4b8d904f0 100644
--- a/vendor/gems/omniauth-gitlab/omniauth-gitlab.gemspec
+++ b/vendor/gems/omniauth-gitlab/omniauth-gitlab.gemspec
@@ -16,7 +16,7 @@ Gem::Specification.new do |gem|
gem.test_files = Dir['spec/**/*.rb']
gem.require_paths = ['lib']
- gem.add_dependency 'omniauth', '~> 1.0'
+ gem.add_dependency 'omniauth', '~> 2.0'
gem.add_dependency 'omniauth-oauth2', '~> 1.7.1'
gem.add_development_dependency 'rspec', '~> 3.1'
gem.add_development_dependency 'rspec-its', '~> 1.0'
diff --git a/vendor/gems/omniauth-gitlab/spec/omniauth/strategies/gitlab_spec.rb b/vendor/gems/omniauth-gitlab/spec/omniauth/strategies/gitlab_spec.rb
index a599386b26c..e9fd38f474b 100644
--- a/vendor/gems/omniauth-gitlab/spec/omniauth/strategies/gitlab_spec.rb
+++ b/vendor/gems/omniauth-gitlab/spec/omniauth/strategies/gitlab_spec.rb
@@ -77,4 +77,26 @@ describe OmniAuth::Strategies::GitLab do
end
end
end
+
+ describe '#callback_url' do
+ let(:base_url) { 'https://example.com' }
+
+ context 'no script name present' do
+ it 'has the correct default callback path' do
+ allow(subject).to receive(:full_host) { base_url }
+ allow(subject).to receive(:script_name) { '' }
+ allow(subject).to receive(:query_string) { '' }
+ expect(subject.callback_url).to eq(base_url + '/auth/gitlab/callback')
+ end
+ end
+
+ context 'script name' do
+ it 'should set the callback path with script_name' do
+ allow(subject).to receive(:full_host) { base_url }
+ allow(subject).to receive(:script_name) { '/v1' }
+ allow(subject).to receive(:query_string) { '' }
+ expect(subject.callback_url).to eq(base_url + '/v1/auth/gitlab/callback')
+ end
+ end
+ end
end
diff --git a/vendor/gems/omniauth-google-oauth2/.gitlab-ci.yml b/vendor/gems/omniauth-google-oauth2/.gitlab-ci.yml
new file mode 100644
index 00000000000..dbc4a7c16b1
--- /dev/null
+++ b/vendor/gems/omniauth-google-oauth2/.gitlab-ci.yml
@@ -0,0 +1,28 @@
+workflow:
+ rules:
+ - if: $CI_MERGE_REQUEST_ID
+
+.rspec:
+ cache:
+ key: omniauth-google-oauth2
+ paths:
+ - vendor/gems/omniauth-google-oauth2/vendor/ruby
+ before_script:
+ - cd vendor/gems/omniauth-google-oauth2
+ - ruby -v # Print out ruby version for debugging
+ - gem install bundler --no-document # Bundler is not installed with the image
+ - bundle config set --local path 'vendor' # Install dependencies into ./vendor/ruby
+ - bundle config set with 'development'
+ - bundle config set --local frozen 'true' # Disallow Gemfile.lock changes on CI
+ - bundle config # Show bundler configuration
+ - bundle install -j $(nproc)
+ script:
+ - bundle exec rspec
+
+rspec-2.7:
+ image: "ruby:2.7"
+ extends: .rspec
+
+rspec-3.0:
+ image: "ruby:3.0"
+ extends: .rspec
diff --git a/vendor/gems/omniauth-google-oauth2/CHANGELOG.md b/vendor/gems/omniauth-google-oauth2/CHANGELOG.md
new file mode 100644
index 00000000000..5b252048fd6
--- /dev/null
+++ b/vendor/gems/omniauth-google-oauth2/CHANGELOG.md
@@ -0,0 +1,354 @@
+# Changelog
+All notable changes to this project will be documented in this file.
+
+## 1.0.1 - 2022-03-10
+
+### Added
+- Output granted scopes in credentials block of the auth hash.
+- Migrated to GitHub actions.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Nothing.
+
+### Fixed
+- Overriding the `redirect_uri` via params or JSON request body.
+
+## 1.0.0 - 2021-03-14
+
+### Added
+- Support for Omniauth 2.x!
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Support for Omniauth 1.x.
+
+### Fixed
+- Nothing.
+
+## 0.8.2 - 2021-03-14
+
+### Added
+- Constrains the version to Omniauth 1.x.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Nothing.
+
+### Fixed
+- Nothing.
+
+## 0.8.1 - 2020-12-12
+
+### Added
+- Support reading the access token from a json request body.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- No longer verify the iat claim for JWT.
+
+### Fixed
+- A few minor issues with .rubocop.yml.
+- Issues with image resizing code when the image came with size information from Google.
+
+## 0.8.0 - 2019-08-21
+
+### Added
+- Updated omniauth-oauth2 to v1.6.0 for security fixes.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Ruby 2.1 support.
+
+### Fixed
+- Nothing.
+
+## 0.7.0 - 2019-06-03
+
+### Added
+- Ensure `info[:email]` is always verified, and include `unverified_email`
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Nothing.
+
+### Fixed
+- Nothing.
+
+## 0.6.1 - 2019-03-07
+
+### Added
+- Return `email` and `email_verified` keys in response.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Nothing.
+
+### Fixed
+- Nothing.
+
+## 0.6.0 - 2018-12-28
+
+### Added
+- Support for JWT 2.x.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Support for JWT 1.x.
+- Support for `raw_friend_info` and `raw_image_info`.
+- Stop using Google+ API endpoints.
+
+### Fixed
+- Nothing.
+
+## 0.5.4 - 2018-12-07
+
+### Added
+- New recommended endpoints for Google OAuth.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Nothing.
+
+### Fixed
+- Nothing.
+
+## 0.5.3 - 2018-01-25
+
+### Added
+- Added support for the JWT 2.x gem.
+- Now fully qualifies the `JWT` class to prevent conflicts with the `Omniauth::JWT` strategy.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Removed the `multijson` dependency.
+- Support for versions of `omniauth-oauth2` < 1.5.
+
+### Fixed
+- Nothing.
+
+## 0.5.2 - 2017-07-30
+
+### Added
+- Nothing.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- New `authorize_url` and `token_url` endpoints are reverted until JWT 2.0 ships.
+
+### Fixed
+- Nothing.
+
+## 0.5.1 - 2017-07-19
+
+### Added
+- *Breaking* JWT iss verification can be enabled/disabled with the `verify_iss` flag - see the README for more details.
+- Authorize options now includes `device_id` and `device_name` for private ip ranges.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Nothing.
+
+### Fixed
+- Updated `authorize_url` and `token_url` to new endpoints.
+
+## 0.5.0 - 2017-05-29
+
+### Added
+- Rubocop checks to specs.
+- Defaulted dev environment to ruby 2.3.4.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Testing support for older versions of ruby not supported by OmniAuth 1.5.
+- Key `[:urls]['Google']` no longer exists, it has been renamed to `[:urls][:google]`.
+
+### Fixed
+- Updated all code to rubocop conventions. This includes the Ruby 1.9 hash syntax when appropriate.
+- Example javascript flow now picks up ENV vars for google key and secret.
+
+## 0.4.1 - 2016-03-14
+
+### Added
+- Nothing.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Nothing.
+
+### Fixed
+- Fixed JWT iat leeway by requiring ruby-jwt 1.5.2
+
+## 0.4.0 - 2016-03-11
+
+### Added
+- Addedd ability to specify multiple hosted domains.
+- Added a default leeway of 1 minute to JWT token validation.
+- Now requires ruby-jwt 1.5.x.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Removed support for ruby 1.9.3 as ruby-jwt 1.5.x does not support it.
+
+### Fixed
+- Nothing.
+
+## 0.3.1 - 2016-01-28
+
+### Added
+- Verify Hosted Domain if hd is set in options.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Dependency on addressable.
+
+### Fixed
+- Nothing.
+
+## 0.3.0 - 2016-01-09
+
+### Added
+- Updated verify_token to use the v3 tokeninfo endpoint.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Nothing.
+
+### Fixed
+- Compatibility with omniauth-oauth2 1.4.0
+
+## 0.2.10 - 2015-11-05
+
+### Added
+- Nothing.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Removed some checks on the id_token. Now only parses the id_token in the JWT processing.
+
+### Fixed
+- Nothing.
+
+## 0.2.9 - 2015-10-29
+
+### Added
+- Nothing.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Nothing.
+
+### Fixed
+- Issue with omniauth-oauth2 where redirect_uri was handled improperly. We now lock the dependency to ~> 1.3.1
+
+## 0.2.8 - 2015-10-01
+
+### Added
+- Added skip_jwt option to bypass JWT decoding in case you get decoding errors.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Nothing.
+
+### Fixed
+- Resolved JWT::InvalidIatError. https://github.com/zquestz/omniauth-google-oauth2/issues/195
+
+## 0.2.7 - 2015-09-25
+
+### Added
+- Now strips out the 'sz' parameter from profile image urls.
+- Now uses 'addressable' gem for URI actions.
+- Added image data to extras hash.
+- Override validation on JWT token for open_id token.
+- Handle authorization codes coming from an installed applications.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Nothing.
+
+### Fixed
+- Fixes double slashes in google image urls.
+
+## 0.2.6 - 2014-10-26
+
+### Added
+- Nothing.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Nothing.
+
+### Fixed
+- Hybrid authorization issues due to bad method alias.
+
+## 0.2.5 - 2014-07-09
+
+### Added
+- Support for versions of omniauth past 1.0.x.
+
+### Deprecated
+- Nothing.
+
+### Removed
+- Nothing.
+
+### Fixed
+- Nothing.
+
+## 0.2.4 - 2014-04-25
+
+### Added
+- Now requiring the "Contacts API" and "Google+ API" to be enabled in your Google API console.
+
+### Deprecated
+- The old Google OAuth API support was removed without deprecation.
+
+### Removed
+- Support for the old Google OAuth API. `OAuth2::Error` will be thrown and state that access is not configured when you attempt to authenticate using the old API. See Added section for this release.
+
+### Fixed
+- Nothing.
diff --git a/vendor/gems/omniauth-google-oauth2/Gemfile b/vendor/gems/omniauth-google-oauth2/Gemfile
new file mode 100644
index 00000000000..7f4f5e950d1
--- /dev/null
+++ b/vendor/gems/omniauth-google-oauth2/Gemfile
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+source 'https://rubygems.org'
+
+gemspec
diff --git a/vendor/gems/omniauth-google-oauth2/Gemfile.lock b/vendor/gems/omniauth-google-oauth2/Gemfile.lock
new file mode 100644
index 00000000000..6c3f5ff3f68
--- /dev/null
+++ b/vendor/gems/omniauth-google-oauth2/Gemfile.lock
@@ -0,0 +1,87 @@
+PATH
+ remote: .
+ specs:
+ omniauth-google-oauth2 (1.0.1)
+ jwt (>= 2.0)
+ oauth2 (~> 2.0)
+ omniauth (~> 2.0)
+ omniauth-oauth2 (~> 1.7.1)
+
+GEM
+ remote: https://rubygems.org/
+ specs:
+ ast (2.4.2)
+ diff-lcs (1.5.0)
+ faraday (2.5.2)
+ faraday-net_http (>= 2.0, < 3.1)
+ ruby2_keywords (>= 0.0.4)
+ faraday-net_http (3.0.0)
+ hashie (5.0.0)
+ jwt (2.4.1)
+ multi_xml (0.6.0)
+ oauth2 (2.0.3)
+ faraday (>= 0.17.3, < 3.0)
+ jwt (>= 1.0, < 3.0)
+ multi_xml (~> 0.5)
+ rack (>= 1.2, < 3)
+ rash_alt (>= 0.4, < 1)
+ version_gem (~> 1.0)
+ omniauth (2.1.0)
+ hashie (>= 3.4.6)
+ rack (>= 2.2.3)
+ rack-protection
+ omniauth-oauth2 (1.7.3)
+ oauth2 (>= 1.4, < 3)
+ omniauth (>= 1.9, < 3)
+ parallel (1.22.1)
+ parser (3.1.2.0)
+ ast (~> 2.4.1)
+ rack (2.2.4)
+ rack-protection (2.2.2)
+ rack
+ rainbow (3.1.1)
+ rake (12.3.3)
+ rash_alt (0.4.12)
+ hashie (>= 3.4)
+ regexp_parser (2.5.0)
+ rexml (3.2.5)
+ rspec (3.11.0)
+ rspec-core (~> 3.11.0)
+ rspec-expectations (~> 3.11.0)
+ rspec-mocks (~> 3.11.0)
+ rspec-core (3.11.0)
+ rspec-support (~> 3.11.0)
+ rspec-expectations (3.11.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.11.0)
+ rspec-mocks (3.11.1)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.11.0)
+ rspec-support (3.11.0)
+ rubocop (0.93.1)
+ parallel (~> 1.10)
+ parser (>= 2.7.1.5)
+ rainbow (>= 2.2.2, < 4.0)
+ regexp_parser (>= 1.8)
+ rexml
+ rubocop-ast (>= 0.6.0)
+ ruby-progressbar (~> 1.7)
+ unicode-display_width (>= 1.4.0, < 2.0)
+ rubocop-ast (1.19.1)
+ parser (>= 3.1.1.0)
+ ruby-progressbar (1.11.0)
+ ruby2_keywords (0.0.5)
+ unicode-display_width (1.8.0)
+ version_gem (1.1.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ omniauth-google-oauth2!
+ rake (~> 12.0)
+ rspec (~> 3.6)
+ rubocop (~> 0.49)
+
+BUNDLED WITH
+ 2.3.21
diff --git a/vendor/gems/omniauth-google-oauth2/README.md b/vendor/gems/omniauth-google-oauth2/README.md
new file mode 100644
index 00000000000..80c611392ca
--- /dev/null
+++ b/vendor/gems/omniauth-google-oauth2/README.md
@@ -0,0 +1,368 @@
+[![Gem Version](https://badge.fury.io/rb/omniauth-google-oauth2.svg)](https://badge.fury.io/rb/omniauth-google-oauth2)
+
+# OmniAuth Google OAuth2 Strategy
+
+Strategy to authenticate with Google via OAuth2 in OmniAuth.
+
+Get your API key at: https://code.google.com/apis/console/ Note the Client ID and the Client Secret.
+
+For more details, read the Google docs: https://developers.google.com/accounts/docs/OAuth2
+
+## Installation
+
+Add to your `Gemfile`:
+
+```ruby
+gem 'omniauth-google-oauth2'
+```
+
+Then `bundle install`.
+
+## Google API Setup
+
+* Go to 'https://console.developers.google.com'
+* Select your project.
+* Go to Credentials, then select the "OAuth consent screen" tab on top, and provide an 'EMAIL ADDRESS' and a 'PRODUCT NAME'
+* Wait 10 minutes for changes to take effect.
+
+## Usage
+
+Here's an example for adding the middleware to a Rails app in `config/initializers/omniauth.rb`:
+
+```ruby
+Rails.application.config.middleware.use OmniAuth::Builder do
+ provider :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET']
+end
+OmniAuth.config.allowed_request_methods = %i[get]
+```
+
+You can now access the OmniAuth Google OAuth2 URL: `/auth/google_oauth2`
+
+For more examples please check out `examples/omni_auth.rb`
+
+NOTE: While developing your application, if you change the scope in the initializer you will need to restart your app server. Remember that either the 'email' or 'profile' scope is required!
+
+## Configuration
+
+You can configure several options, which you pass in to the `provider` method via a hash:
+
+* `scope`: A comma-separated list of permissions you want to request from the user. See the [Google OAuth 2.0 Playground](https://developers.google.com/oauthplayground/) for a full list of available permissions. Caveats:
+ * The `email` and `profile` scopes are used by default. By defining your own `scope`, you override these defaults, but Google requires at least one of `email` or `profile`, so make sure to add at least one of them to your scope!
+ * Scopes starting with `https://www.googleapis.com/auth/` do not need that prefix specified. So while you can use the smaller scope `books` since that permission starts with the mentioned prefix, you should use the full scope URL `https://docs.google.com/feeds/` to access a user's docs, for example.
+
+* `redirect_uri`: Override the redirect_uri used by the gem.
+
+* `prompt`: A space-delimited list of string values that determines whether the user is re-prompted for authentication and/or consent. Possible values are:
+ * `none`: No authentication or consent pages will be displayed; it will return an error if the user is not already authenticated and has not pre-configured consent for the requested scopes. This can be used as a method to check for existing authentication and/or consent.
+ * `consent`: The user will always be prompted for consent, even if they have previously allowed access a given set of scopes.
+ * `select_account`: The user will always be prompted to select a user account. This allows a user who has multiple current account sessions to select one amongst them.
+
+ If no value is specified, the user only sees the authentication page if they are not logged in and only sees the consent page the first time they authorize a given set of scopes.
+
+* `image_aspect_ratio`: The shape of the user's profile picture. Possible values are:
+ * `original`: Picture maintains its original aspect ratio.
+ * `square`: Picture presents equal width and height.
+
+ Defaults to `original`.
+
+* `image_size`: The size of the user's profile picture. The image returned will have width equal to the given value and variable height, according to the `image_aspect_ratio` chosen. Additionally, a picture with specific width and height can be requested by setting this option to a hash with `width` and `height` as keys. If only `width` or `height` is specified, a picture whose width or height is closest to the requested size and requested aspect ratio will be returned. Defaults to the original width and height of the picture.
+
+* `name`: The name of the strategy. The default name is `google_oauth2` but it can be changed to any value, for example `google`. The OmniAuth URL will thus change to `/auth/google` and the `provider` key in the auth hash will then return `google`.
+
+* `access_type`: Defaults to `offline`, so a refresh token is sent to be used when the user is not present at the browser. Can be set to `online`. More about [offline access](https://developers.google.com/identity/protocols/OAuth2WebServer#offline)
+
+* `hd`: (Optional) Limit sign-in to a particular Google Apps hosted domain. This can be simply string `'domain.com'` or an array `%w(domain.com domain.co)`. More information at: https://developers.google.com/accounts/docs/OpenIDConnect#hd-param
+
+* `jwt_leeway`: Number of seconds passed to the JWT library as leeway. Defaults to 60 seconds. Note this only works if you use jwt 2.1, as the leeway option was removed in later versions.
+
+* `skip_jwt`: Skip JWT processing. This is for users who are seeing JWT decoding errors with the `iat` field. Always try adjusting the leeway before disabling JWT processing.
+
+* `login_hint`: When your app knows which user it is trying to authenticate, it can provide this parameter as a hint to the authentication server. Passing this hint suppresses the account chooser and either pre-fill the email box on the sign-in form, or select the proper session (if the user is using multiple sign-in), which can help you avoid problems that occur if your app logs in the wrong user account. The value can be either an email address or the sub string, which is equivalent to the user's Google+ ID.
+
+* `include_granted_scopes`: If this is provided with the value true, and the authorization request is granted, the authorization will include any previous authorizations granted to this user/application combination for other scopes. See Google's [Incremental Authorization](https://developers.google.com/accounts/docs/OAuth2WebServer#incrementalAuth) for additional details.
+
+* `openid_realm`: Set the OpenID realm value, to allow upgrading from OpenID based authentication to OAuth 2 based authentication. When this is set correctly an `openid_id` value will be set in `['extra']['id_info']` in the authentication hash with the value of the user's OpenID ID URL.
+
+* `provider_ignores_state`: You will need to set this to `true` when using the `One-time Code Flow` below. In this flow there is no server side redirect that would set the state.
+
+Here's an example of a possible configuration where the strategy name is changed, the user is asked for extra permissions, the user is always prompted to select their account when logging in and the user's profile picture is returned as a thumbnail:
+
+```ruby
+Rails.application.config.middleware.use OmniAuth::Builder do
+ provider :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_CLIENT_SECRET'],
+ {
+ scope: 'userinfo.email, userinfo.profile, http://gdata.youtube.com',
+ prompt: 'select_account',
+ image_aspect_ratio: 'square',
+ image_size: 50
+ }
+end
+```
+
+## Auth Hash
+
+Here's an example of an authentication hash available in the callback by accessing `request.env['omniauth.auth']`:
+
+```ruby
+{
+ "provider" => "google_oauth2",
+ "uid" => "100000000000000000000",
+ "info" => {
+ "name" => "John Smith",
+ "email" => "john@example.com",
+ "first_name" => "John",
+ "last_name" => "Smith",
+ "image" => "https://lh4.googleusercontent.com/photo.jpg",
+ "urls" => {
+ "google" => "https://plus.google.com/+JohnSmith"
+ }
+ },
+ "credentials" => {
+ "token" => "TOKEN",
+ "refresh_token" => "REFRESH_TOKEN",
+ "expires_at" => 1496120719,
+ "expires" => true
+ },
+ "extra" => {
+ "id_token" => "ID_TOKEN",
+ "id_info" => {
+ "azp" => "APP_ID",
+ "aud" => "APP_ID",
+ "sub" => "100000000000000000000",
+ "email" => "john@example.com",
+ "email_verified" => true,
+ "at_hash" => "HK6E_P6Dh8Y93mRNtsDB1Q",
+ "iss" => "accounts.google.com",
+ "iat" => 1496117119,
+ "exp" => 1496120719
+ },
+ "raw_info" => {
+ "sub" => "100000000000000000000",
+ "name" => "John Smith",
+ "given_name" => "John",
+ "family_name" => "Smith",
+ "profile" => "https://plus.google.com/+JohnSmith",
+ "picture" => "https://lh4.googleusercontent.com/photo.jpg?sz=50",
+ "email" => "john@example.com",
+ "email_verified" => "true",
+ "locale" => "en",
+ "hd" => "company.com"
+ }
+ }
+}
+```
+
+### Devise
+
+First define your application id and secret in `config/initializers/devise.rb`. Do not use the snippet mentioned in the [Usage](https://github.com/zquestz/omniauth-google-oauth2#usage) section.
+
+Configuration options can be passed as the last parameter here as key/value pairs.
+
+```ruby
+config.omniauth :google_oauth2, 'GOOGLE_CLIENT_ID', 'GOOGLE_CLIENT_SECRET', {}
+```
+NOTE: If you are using this gem with devise with above snippet in `config/initializers/devise.rb` then do not create `config/initializers/omniauth.rb` which will conflict with devise configurations.
+
+Then add the following to 'config/routes.rb' so the callback routes are defined.
+
+```ruby
+devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }
+```
+
+Make sure your model is omniauthable. Generally this is "/app/models/user.rb"
+
+```ruby
+devise :omniauthable, omniauth_providers: [:google_oauth2]
+```
+
+Then make sure your callbacks controller is setup.
+
+```ruby
+# app/controllers/users/omniauth_callbacks_controller.rb:
+
+class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
+ def google_oauth2
+ # You need to implement the method below in your model (e.g. app/models/user.rb)
+ @user = User.from_omniauth(request.env['omniauth.auth'])
+
+ if @user.persisted?
+ flash[:notice] = I18n.t 'devise.omniauth_callbacks.success', kind: 'Google'
+ sign_in_and_redirect @user, event: :authentication
+ else
+ session['devise.google_data'] = request.env['omniauth.auth'].except('extra') # Removing extra as it can overflow some session stores
+ redirect_to new_user_registration_url, alert: @user.errors.full_messages.join("\n")
+ end
+ end
+end
+```
+
+and bind to or create the user
+
+```ruby
+def self.from_omniauth(access_token)
+ data = access_token.info
+ user = User.where(email: data['email']).first
+
+ # Uncomment the section below if you want users to be created if they don't exist
+ # unless user
+ # user = User.create(name: data['name'],
+ # email: data['email'],
+ # password: Devise.friendly_token[0,20]
+ # )
+ # end
+ user
+end
+```
+
+For your views you can login using:
+
+```erb
+<%# omniauth-google-oauth2 1.0.x uses OmniAuth 2 and requires using HTTP Post to initiate authentication: %>
+<%= link_to "Sign in with Google", user_google_oauth2_omniauth_authorize_path, method: :post %>
+
+<%# omniauth-google-oauth2 prior 1.0.0: %>
+<%= link_to "Sign in with Google", user_google_oauth2_omniauth_authorize_path %>
+
+<%# Devise prior 4.1.0: %>
+<%= link_to "Sign in with Google", user_omniauth_authorize_path(:google_oauth2) %>
+```
+
+An overview is available at https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview
+
+### One-time Code Flow (Hybrid Authentication)
+
+Google describes the One-time Code Flow [here](https://developers.google.com/identity/sign-in/web/server-side-flow). This hybrid authentication flow has significant functional and security advantages over a pure server-side or pure client-side flow. The following steps occur in this flow:
+
+1. The client (web browser) authenticates the user directly via Google's JS API. During this process assorted modals may be rendered by Google.
+2. On successful authentication, Google returns a one-time use code, which requires the Google client secret (which is only available server-side).
+3. Using a AJAX request, the code is POSTed to the Omniauth Google OAuth2 callback.
+4. The Omniauth Google OAuth2 gem will validate the code via a server-side request to Google. If the code is valid, then Google will return an access token and, if this is the first time this user is authenticating against this application, a refresh token. Both of these should be stored on the server. The response to the AJAX request indicates the success or failure of this process.
+
+This flow is immune to replay attacks, and conveys no useful information to a man in the middle.
+
+The omniauth-google-oauth2 gem supports this mode of operation when `provider_ignores_state` is set to `true`. Implementors simply need to add the appropriate JavaScript to their web page, and they can take advantage of this flow. An example JavaScript snippet follows.
+
+```javascript
+// Basic hybrid auth example following the pattern at:
+// https://developers.google.com/identity/sign-in/web/reference
+
+<script src="https://apis.google.com/js/platform.js?onload=init" async defer></script>
+
+...
+
+function init() {
+ gapi.load('auth2', function() {
+ // Ready.
+ $('.google-login-button').click(function(e) {
+ e.preventDefault();
+
+ gapi.auth2.authorize({
+ client_id: 'YOUR_CLIENT_ID',
+ cookie_policy: 'single_host_origin',
+ scope: 'email profile',
+ response_type: 'code'
+ }, function(response) {
+ if (response && !response.error) {
+ // google authentication succeed, now post data to server.
+ jQuery.ajax({type: 'POST', url: '/auth/google_oauth2/callback', data: response,
+ success: function(data) {
+ // response from server
+ }
+ });
+ } else {
+ // google authentication failed
+ }
+ });
+ });
+ });
+};
+```
+
+#### Note about mobile clients (iOS, Android)
+
+The documentation at https://developers.google.com/identity/sign-in/ios/offline-access specifies the _REDIRECT_URI_ to be either a set value or an EMPTY string for mobile logins to work. Else, you will run into _redirect_uri_mismatch_ errors.
+
+In that case, ensure to send an additional parameter `redirect_uri=` (empty string) to the `/auth/google_oauth2/callback` URL from your mobile device.
+
+#### Note about CORS
+
+If you're making POST requests to `/auth/google_oauth2/callback` from another domain, then you need to make sure `'X-Requested-With': 'XMLHttpRequest'` header is included with your request, otherwise your server might respond with `OAuth2::Error, : Invalid Value` error.
+
+#### Getting around the `redirect_uri_mismatch` error (See [Issue #365](https://github.com/zquestz/omniauth-google-oauth2/issues/365))
+
+If you are struggling with a persistent `redirect_uri_mismatch`, you can instead pass the `access_token` from [`getAuthResponse`](https://developers.google.com/identity/sign-in/web/reference#googleusergetauthresponseincludeauthorizationdata) directly to the `auth/google_oauth2/callback` endpoint, like so:
+
+```javascript
+// Initialize the GoogleAuth object
+let googleAuth;
+gapi.load('client:auth2', async () => {
+ await gapi.client.init({ scope: '...', client_id: '...' });
+ googleAuth = gapi.auth2.getAuthInstance();
+});
+
+// Call this when the Google Sign In button is clicked
+async function signInGoogle() {
+ const googleUser = await googleAuth.signIn(); // wait for the user to authorize through the modal
+ const { access_token } = googleUser.getAuthResponse();
+
+ const data = new FormData();
+ data.append('access_token', access_token);
+
+ const response = await api.post('/auth/google_oauth2/callback', data)
+ console.log(response);
+}
+```
+
+#### Using Axios
+If you're making a GET resquests from another domain using `access_token`.
+```
+axios
+ .get(
+ 'url(path to your callback}',
+ { params: { access_token: 'token' } },
+ headers....
+ )
+```
+
+If you're making a POST resquests from another domain using `access_token`.
+```
+axios
+ .post(
+ 'url(path to your callback}',
+ { access_token: 'token' },
+ headers....
+ )
+
+--OR--
+
+axios
+ .post(
+ 'url(path to your callback}',
+ null,
+ {
+ params: {
+ access_token: 'token'
+ },
+ headers....
+ }
+ )
+```
+
+## Fixing Protocol Mismatch for `redirect_uri` in Rails
+
+Just set the `full_host` in OmniAuth based on the Rails.env.
+
+```
+# config/initializers/omniauth.rb
+OmniAuth.config.full_host = Rails.env.production? ? 'https://domain.com' : 'http://localhost:3000'
+```
+
+## License
+
+Copyright (c) 2018 by Josh Ellithorpe
+
+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/omniauth-google-oauth2/Rakefile b/vendor/gems/omniauth-google-oauth2/Rakefile
new file mode 100644
index 00000000000..db36369bc12
--- /dev/null
+++ b/vendor/gems/omniauth-google-oauth2/Rakefile
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+require File.join('bundler', 'gem_tasks')
+require File.join('rspec', 'core', 'rake_task')
+
+RSpec::Core::RakeTask.new(:spec)
+
+task default: :spec
diff --git a/vendor/gems/omniauth-google-oauth2/examples/Gemfile b/vendor/gems/omniauth-google-oauth2/examples/Gemfile
new file mode 100644
index 00000000000..ba019344a67
--- /dev/null
+++ b/vendor/gems/omniauth-google-oauth2/examples/Gemfile
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+source 'https://rubygems.org'
+
+gem 'omniauth-google-oauth2', '~> 0.8.1'
+gem 'rubocop'
+gem 'sinatra', '~> 1.4'
+gem 'webrick'
diff --git a/vendor/gems/omniauth-google-oauth2/examples/config.ru b/vendor/gems/omniauth-google-oauth2/examples/config.ru
new file mode 100644
index 00000000000..ee17929094c
--- /dev/null
+++ b/vendor/gems/omniauth-google-oauth2/examples/config.ru
@@ -0,0 +1,120 @@
+# frozen_string_literal: true
+
+# Sample app for Google OAuth2 Strategy
+# Make sure to setup the ENV variables GOOGLE_KEY and GOOGLE_SECRET
+# Run with "bundle exec rackup"
+
+require 'rubygems'
+require 'bundler'
+require 'sinatra'
+require 'omniauth'
+require 'omniauth-google-oauth2'
+
+# Do not use for production code.
+# This is only to make setup easier when running through the sample.
+#
+# If you do have issues with certs in production code, this could help:
+# http://railsapps.github.io/openssl-certificate-verify-failed.html
+OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
+
+# Main example app for omniauth-google-oauth2
+class App < Sinatra::Base
+ get '/' do
+ <<-HTML
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <title>Google OAuth2 Example</title>
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
+ <script>
+ jQuery(function() {
+ return $.ajax({
+ url: 'https://apis.google.com/js/client:plus.js?onload=gpAsyncInit',
+ dataType: 'script',
+ cache: true
+ });
+ });
+
+ window.gpAsyncInit = function() {
+ gapi.auth.authorize({
+ immediate: true,
+ response_type: 'code',
+ cookie_policy: 'single_host_origin',
+ client_id: '#{ENV['GOOGLE_KEY']}',
+ scope: 'email profile'
+ }, function(response) {
+ return;
+ });
+ $('.googleplus-login').click(function(e) {
+ e.preventDefault();
+ gapi.auth.authorize({
+ immediate: false,
+ response_type: 'code',
+ cookie_policy: 'single_host_origin',
+ client_id: '#{ENV['GOOGLE_KEY']}',
+ scope: 'email profile'
+ }, function(response) {
+ if (response && !response.error) {
+ // google authentication succeed, now post data to server.
+ jQuery.ajax({type: 'POST', url: "/auth/google_oauth2/callback", data: response,
+ success: function(data) {
+ // Log the data returning from google.
+ console.log(data)
+ }
+ });
+ } else {
+ // google authentication failed.
+ console.log("FAILED")
+ }
+ });
+ });
+ };
+ </script>
+ </head>
+ <body>
+ <ul>
+ <li><a href='/auth/google_oauth2'>Sign in with Google</a></li>
+ <li><a href='#' class="googleplus-login">Sign in with Google via AJAX</a></li>
+ </ul>
+ </body>
+ </html>
+ HTML
+ end
+
+ post '/auth/:provider/callback' do
+ content_type 'text/plain'
+ begin
+ request.env['omniauth.auth'].to_hash.inspect
+ rescue StandardError
+ 'No Data'
+ end
+ end
+
+ get '/auth/:provider/callback' do
+ content_type 'text/plain'
+ begin
+ request.env['omniauth.auth'].to_hash.inspect
+ rescue StandardError
+ 'No Data'
+ end
+ end
+
+ get '/auth/failure' do
+ content_type 'text/plain'
+ begin
+ request.env['omniauth.auth'].to_hash.inspect
+ rescue StandardError
+ 'No Data'
+ end
+ end
+end
+
+use Rack::Session::Cookie, secret: ENV['RACK_COOKIE_SECRET']
+
+use OmniAuth::Builder do
+ # For additional provider examples please look at 'omni_auth.rb'
+ # The key provider_ignores_state is only for AJAX flows. It is not recommended for normal logins.
+ provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], access_type: 'offline', prompt: 'consent', provider_ignores_state: true, scope: 'email,profile,calendar'
+end
+
+run App.new
diff --git a/vendor/gems/omniauth-google-oauth2/examples/omni_auth.rb b/vendor/gems/omniauth-google-oauth2/examples/omni_auth.rb
new file mode 100644
index 00000000000..0a94164d766
--- /dev/null
+++ b/vendor/gems/omniauth-google-oauth2/examples/omni_auth.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+# Google's OAuth2 docs. Make sure you are familiar with all the options
+# before attempting to configure this gem.
+# https://developers.google.com/accounts/docs/OAuth2Login
+
+Rails.application.config.middleware.use OmniAuth::Builder do
+ # Default usage, this will give you offline access and a refresh token
+ # using default scopes 'email' and 'profile'
+ #
+ provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], scope: 'email,profile'
+
+ # Custom redirect_uri
+ #
+ # provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], scope: 'email,profile', redirect_uri: 'https://localhost:3000/redirect'
+
+ # Manual setup for offline access with a refresh token.
+ #
+ # provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], access_type: 'offline'
+
+ # Custom scope supporting youtube. If you are customizing scopes, remember
+ # to include the default scopes 'email' and 'profile'
+ #
+ # provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], scope: 'http://gdata.youtube.com,email,profile,plus.me'
+
+ # Custom scope for users only using Google for account creation/auth and do not require a refresh token.
+ #
+ # provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], access_type: 'online', prompt: ''
+
+ # To include information about people in your circles you must include the 'plus.login' scope.
+ #
+ # provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], skip_friends: false, scope: 'email,profile,plus.login'
+
+ # If you need to acquire whether user picture is a default one or uploaded by user.
+ #
+ # provider :google_oauth2, ENV['GOOGLE_KEY'], ENV['GOOGLE_SECRET'], skip_image_info: false
+end
diff --git a/vendor/gems/omniauth-google-oauth2/lib/omniauth-google-oauth2.rb b/vendor/gems/omniauth-google-oauth2/lib/omniauth-google-oauth2.rb
new file mode 100644
index 00000000000..a5cc57ab4c9
--- /dev/null
+++ b/vendor/gems/omniauth-google-oauth2/lib/omniauth-google-oauth2.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+require 'omniauth/google_oauth2'
diff --git a/vendor/gems/omniauth-google-oauth2/lib/omniauth/google_oauth2.rb b/vendor/gems/omniauth-google-oauth2/lib/omniauth/google_oauth2.rb
new file mode 100644
index 00000000000..594c9a4eed9
--- /dev/null
+++ b/vendor/gems/omniauth-google-oauth2/lib/omniauth/google_oauth2.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+require 'omniauth/strategies/google_oauth2'
diff --git a/vendor/gems/omniauth-google-oauth2/lib/omniauth/google_oauth2/version.rb b/vendor/gems/omniauth-google-oauth2/lib/omniauth/google_oauth2/version.rb
new file mode 100644
index 00000000000..93ae5e9e990
--- /dev/null
+++ b/vendor/gems/omniauth-google-oauth2/lib/omniauth/google_oauth2/version.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+module OmniAuth
+ module GoogleOauth2
+ VERSION = '1.0.1'
+ end
+end
diff --git a/vendor/gems/omniauth-google-oauth2/lib/omniauth/strategies/google_oauth2.rb b/vendor/gems/omniauth-google-oauth2/lib/omniauth/strategies/google_oauth2.rb
new file mode 100644
index 00000000000..4ce32eb80d1
--- /dev/null
+++ b/vendor/gems/omniauth-google-oauth2/lib/omniauth/strategies/google_oauth2.rb
@@ -0,0 +1,254 @@
+# frozen_string_literal: true
+
+require 'jwt'
+require 'oauth2'
+require 'omniauth/strategies/oauth2'
+require 'uri'
+
+module OmniAuth
+ module Strategies
+ # Main class for Google OAuth2 strategy.
+ class GoogleOauth2 < OmniAuth::Strategies::OAuth2
+ ALLOWED_ISSUERS = ['accounts.google.com', 'https://accounts.google.com'].freeze
+ BASE_SCOPE_URL = 'https://www.googleapis.com/auth/'
+ BASE_SCOPES = %w[profile email openid].freeze
+ DEFAULT_SCOPE = 'email,profile'
+ USER_INFO_URL = 'https://www.googleapis.com/oauth2/v3/userinfo'
+ IMAGE_SIZE_REGEXP = /(s\d+(-c)?)|(w\d+-h\d+(-c)?)|(w\d+(-c)?)|(h\d+(-c)?)|c/
+
+ option :name, 'google_oauth2'
+ option :skip_friends, true
+ option :skip_image_info, true
+ option :skip_jwt, false
+ option :jwt_leeway, 60
+ option :authorize_options, %i[access_type hd login_hint prompt request_visible_actions scope state redirect_uri include_granted_scopes openid_realm device_id device_name]
+ option :authorized_client_ids, []
+
+ option :client_options,
+ site: 'https://oauth2.googleapis.com',
+ authorize_url: 'https://accounts.google.com/o/oauth2/auth',
+ token_url: '/token'
+
+ def authorize_params
+ super.tap do |params|
+ options[:authorize_options].each do |k|
+ params[k] = request.params[k.to_s] unless [nil, ''].include?(request.params[k.to_s])
+ end
+
+ params[:scope] = get_scope(params)
+ params[:access_type] = 'offline' if params[:access_type].nil?
+ params['openid.realm'] = params.delete(:openid_realm) unless params[:openid_realm].nil?
+
+ session['omniauth.state'] = params[:state] if params[:state]
+ end
+ end
+
+ uid { raw_info['sub'] }
+
+ info do
+ prune!(
+ name: raw_info['name'],
+ email: verified_email,
+ unverified_email: raw_info['email'],
+ email_verified: raw_info['email_verified'],
+ first_name: raw_info['given_name'],
+ last_name: raw_info['family_name'],
+ image: image_url,
+ urls: {
+ google: raw_info['profile']
+ }
+ )
+ end
+
+ credentials do
+ # Tokens and expiration will be used from OAuth2 strategy credentials block
+ prune!({ 'scope' => token_info(access_token.token)['scope'] })
+ end
+
+ extra do
+ hash = {}
+ hash[:id_token] = access_token['id_token']
+ if !options[:skip_jwt] && !access_token['id_token'].nil?
+ decoded = ::JWT.decode(access_token['id_token'], nil, false).first
+
+ # We have to manually verify the claims because the third parameter to
+ # JWT.decode is false since no verification key is provided.
+ ::JWT::Verify.verify_claims(decoded,
+ verify_iss: true,
+ iss: ALLOWED_ISSUERS,
+ verify_aud: true,
+ aud: options.client_id,
+ verify_sub: false,
+ verify_expiration: true,
+ verify_not_before: true,
+ verify_iat: false,
+ verify_jti: false,
+ leeway: options[:jwt_leeway])
+
+ hash[:id_info] = decoded
+ end
+ hash[:raw_info] = raw_info unless skip_info?
+ prune! hash
+ end
+
+ def raw_info
+ @raw_info ||= access_token.get(USER_INFO_URL).parsed
+ end
+
+ def custom_build_access_token
+ access_token = get_access_token(request)
+
+ verify_hd(access_token)
+ access_token
+ end
+
+ alias build_access_token custom_build_access_token
+
+ private
+
+ def callback_url
+ options[:redirect_uri] || (full_host + callback_path)
+ end
+
+ def get_access_token(request)
+ verifier = request.params['code']
+ redirect_uri = request.params['redirect_uri']
+ access_token = request.params['access_token']
+ if verifier && request.xhr?
+ client_get_token(verifier, redirect_uri || 'postmessage')
+ elsif verifier
+ client_get_token(verifier, redirect_uri || callback_url)
+ elsif access_token && verify_token(access_token)
+ ::OAuth2::AccessToken.from_hash(client, request.params.dup)
+ elsif request.content_type =~ /json/i
+ begin
+ body = JSON.parse(request.body.read)
+ request.body.rewind # rewind request body for downstream middlewares
+ verifier = body && body['code']
+ access_token = body && body['access_token']
+ redirect_uri ||= body && body['redirect_uri']
+ if verifier
+ client_get_token(verifier, redirect_uri || 'postmessage')
+ elsif verify_token(access_token)
+ ::OAuth2::AccessToken.from_hash(client, body.dup)
+ end
+ rescue JSON::ParserError => e
+ warn "[omniauth google-oauth2] JSON parse error=#{e}"
+ end
+ end
+ end
+
+ def client_get_token(verifier, redirect_uri)
+ client.auth_code.get_token(verifier, get_token_options(redirect_uri), get_token_params)
+ end
+
+ def get_token_params
+ deep_symbolize(options.auth_token_params || {})
+ end
+
+ def get_scope(params)
+ raw_scope = params[:scope] || DEFAULT_SCOPE
+ scope_list = raw_scope.split(' ').map { |item| item.split(',') }.flatten
+ scope_list.map! { |s| s =~ %r{^https?://} || BASE_SCOPES.include?(s) ? s : "#{BASE_SCOPE_URL}#{s}" }
+ scope_list.join(' ')
+ end
+
+ def verified_email
+ raw_info['email_verified'] ? raw_info['email'] : nil
+ end
+
+ def get_token_options(redirect_uri = '')
+ { redirect_uri: redirect_uri }.merge(token_params.to_hash(symbolize_keys: true))
+ end
+
+ def prune!(hash)
+ hash.delete_if do |_, v|
+ prune!(v) if v.is_a?(Hash)
+ v.nil? || (v.respond_to?(:empty?) && v.empty?)
+ end
+ end
+
+ def image_url
+ return nil unless raw_info['picture']
+
+ u = URI.parse(raw_info['picture'].gsub('https:https', 'https'))
+
+ path_index = u.path.to_s.index('/photo.jpg')
+
+ if path_index && image_size_opts_passed?
+ u.path.insert(path_index, image_params)
+ u.path = u.path.gsub('//', '/')
+
+ # Check if the image is already sized!
+ split_path = u.path.split('/')
+ u.path = u.path.sub("/#{split_path[-3]}", '') if split_path[-3] =~ IMAGE_SIZE_REGEXP
+ end
+
+ u.query = strip_unnecessary_query_parameters(u.query)
+
+ u.to_s
+ end
+
+ def image_size_opts_passed?
+ options[:image_size] || options[:image_aspect_ratio]
+ end
+
+ def image_params
+ image_params = []
+ if options[:image_size].is_a?(Integer)
+ image_params << "s#{options[:image_size]}"
+ elsif options[:image_size].is_a?(Hash)
+ image_params << "w#{options[:image_size][:width]}" if options[:image_size][:width]
+ image_params << "h#{options[:image_size][:height]}" if options[:image_size][:height]
+ end
+ image_params << 'c' if options[:image_aspect_ratio] == 'square'
+
+ '/' + image_params.join('-')
+ end
+
+ def strip_unnecessary_query_parameters(query_parameters)
+ # strip `sz` parameter (defaults to sz=50) which overrides `image_size` options
+ return nil if query_parameters.nil?
+
+ params = CGI.parse(query_parameters)
+ stripped_params = params.delete_if { |key| key == 'sz' }
+
+ # don't return an empty Hash since that would result
+ # in URLs with a trailing ? character: http://image.url?
+ return nil if stripped_params.empty?
+
+ URI.encode_www_form(stripped_params)
+ end
+
+ def token_info(access_token)
+ return nil unless access_token
+
+ @token_info ||= Hash.new do |h, k|
+ h[k] = client.request(:get, 'https://www.googleapis.com/oauth2/v3/tokeninfo', params: { access_token: access_token }).parsed
+ end
+
+ @token_info[access_token]
+ end
+
+ def verify_token(access_token)
+ return false unless access_token
+
+ token_info = token_info(access_token)
+ token_info['aud'] == options.client_id || options.authorized_client_ids.include?(token_info['aud'])
+ end
+
+ def verify_hd(access_token)
+ return true unless options.hd
+
+ @raw_info ||= access_token.get(USER_INFO_URL).parsed
+
+ options.hd = options.hd.call if options.hd.is_a? Proc
+ allowed_hosted_domains = Array(options.hd)
+
+ raise CallbackError.new(:invalid_hd, 'Invalid Hosted Domain') unless allowed_hosted_domains.include?(@raw_info['hd']) || options.hd == '*'
+
+ true
+ end
+ end
+ end
+end
diff --git a/vendor/gems/omniauth-google-oauth2/omniauth-google-oauth2.gemspec b/vendor/gems/omniauth-google-oauth2/omniauth-google-oauth2.gemspec
new file mode 100644
index 00000000000..a50d67bc9a0
--- /dev/null
+++ b/vendor/gems/omniauth-google-oauth2/omniauth-google-oauth2.gemspec
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require File.expand_path(
+ File.join('..', 'lib', 'omniauth', 'google_oauth2', 'version'),
+ __FILE__
+)
+
+Gem::Specification.new do |gem|
+ gem.name = 'omniauth-google-oauth2'
+ gem.version = OmniAuth::GoogleOauth2::VERSION
+ gem.license = 'MIT'
+ gem.summary = %(A Google OAuth2 strategy for OmniAuth 1.x)
+ gem.description = %(A Google OAuth2 strategy for OmniAuth 1.x. This allows you to login to Google with your ruby app.)
+ gem.authors = ['Josh Ellithorpe', 'Yury Korolev']
+ gem.email = ['quest@mac.com']
+ gem.homepage = 'https://github.com/zquestz/omniauth-google-oauth2'
+
+ gem.files = Dir.glob("lib/**/*.*")
+ gem.require_paths = ['lib']
+
+ gem.required_ruby_version = '>= 2.2'
+
+ gem.add_runtime_dependency 'jwt', '>= 2.0'
+ gem.add_runtime_dependency 'oauth2', '~> 2.0'
+ gem.add_runtime_dependency 'omniauth', '~> 2.0'
+ gem.add_runtime_dependency 'omniauth-oauth2', '~> 1.7.1'
+
+ gem.add_development_dependency 'rake', '~> 12.0'
+ gem.add_development_dependency 'rspec', '~> 3.6'
+ gem.add_development_dependency 'rubocop', '~> 0.49'
+end
diff --git a/vendor/gems/omniauth-google-oauth2/spec/omniauth/strategies/google_oauth2_spec.rb b/vendor/gems/omniauth-google-oauth2/spec/omniauth/strategies/google_oauth2_spec.rb
new file mode 100644
index 00000000000..3a2bcf07e54
--- /dev/null
+++ b/vendor/gems/omniauth-google-oauth2/spec/omniauth/strategies/google_oauth2_spec.rb
@@ -0,0 +1,850 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require 'json'
+require 'omniauth-google-oauth2'
+require 'stringio'
+
+describe OmniAuth::Strategies::GoogleOauth2 do
+ let(:request) { double('Request', params: {}, cookies: {}, env: {}) }
+ let(:app) do
+ lambda do
+ [200, {}, ['Hello.']]
+ end
+ end
+
+ subject do
+ OmniAuth::Strategies::GoogleOauth2.new(app, 'appid', 'secret', @options || {}).tap do |strategy|
+ allow(strategy).to receive(:request) do
+ request
+ end
+ end
+ end
+
+ before do
+ OmniAuth.config.test_mode = true
+ end
+
+ after do
+ OmniAuth.config.test_mode = false
+ end
+
+ describe '#client_options' do
+ it 'has correct site' do
+ expect(subject.client.site).to eq('https://oauth2.googleapis.com')
+ end
+
+ it 'has correct authorize_url' do
+ expect(subject.client.options[:authorize_url]).to eq('https://accounts.google.com/o/oauth2/auth')
+ end
+
+ it 'has correct token_url' do
+ expect(subject.client.options[:token_url]).to eq('/token')
+ end
+
+ describe 'overrides' do
+ context 'as strings' do
+ it 'should allow overriding the site' do
+ @options = { client_options: { 'site' => 'https://example.com' } }
+ expect(subject.client.site).to eq('https://example.com')
+ end
+
+ it 'should allow overriding the authorize_url' do
+ @options = { client_options: { 'authorize_url' => 'https://example.com' } }
+ expect(subject.client.options[:authorize_url]).to eq('https://example.com')
+ end
+
+ it 'should allow overriding the token_url' do
+ @options = { client_options: { 'token_url' => 'https://example.com' } }
+ expect(subject.client.options[:token_url]).to eq('https://example.com')
+ end
+ end
+
+ context 'as symbols' do
+ it 'should allow overriding the site' do
+ @options = { client_options: { site: 'https://example.com' } }
+ expect(subject.client.site).to eq('https://example.com')
+ end
+
+ it 'should allow overriding the authorize_url' do
+ @options = { client_options: { authorize_url: 'https://example.com' } }
+ expect(subject.client.options[:authorize_url]).to eq('https://example.com')
+ end
+
+ it 'should allow overriding the token_url' do
+ @options = { client_options: { token_url: 'https://example.com' } }
+ expect(subject.client.options[:token_url]).to eq('https://example.com')
+ end
+ end
+ end
+ end
+
+ describe '#authorize_options' do
+ %i[access_type hd login_hint prompt scope state device_id device_name].each do |k|
+ it "should support #{k}" do
+ @options = { k => 'http://someval' }
+ expect(subject.authorize_params[k.to_s]).to eq('http://someval')
+ end
+ end
+
+ describe 'redirect_uri' do
+ it 'should default to nil' do
+ @options = {}
+ expect(subject.authorize_params['redirect_uri']).to eq(nil)
+ end
+
+ it 'should set the redirect_uri parameter if present' do
+ @options = { redirect_uri: 'https://example.com' }
+ expect(subject.authorize_params['redirect_uri']).to eq('https://example.com')
+ end
+ end
+
+ describe 'access_type' do
+ it 'should default to "offline"' do
+ @options = {}
+ expect(subject.authorize_params['access_type']).to eq('offline')
+ end
+
+ it 'should set the access_type parameter if present' do
+ @options = { access_type: 'online' }
+ expect(subject.authorize_params['access_type']).to eq('online')
+ end
+ end
+
+ describe 'hd' do
+ it 'should default to nil' do
+ expect(subject.authorize_params['hd']).to eq(nil)
+ end
+
+ it 'should set the hd (hosted domain) parameter if present' do
+ @options = { hd: 'example.com' }
+ expect(subject.authorize_params['hd']).to eq('example.com')
+ end
+
+ it 'should set the hd parameter and work with nil hd (gmail)' do
+ @options = { hd: nil }
+ expect(subject.authorize_params['hd']).to eq(nil)
+ end
+
+ it 'should set the hd parameter to * if set (only allows G Suite emails)' do
+ @options = { hd: '*' }
+ expect(subject.authorize_params['hd']).to eq('*')
+ end
+ end
+
+ describe 'login_hint' do
+ it 'should default to nil' do
+ expect(subject.authorize_params['login_hint']).to eq(nil)
+ end
+
+ it 'should set the login_hint parameter if present' do
+ @options = { login_hint: 'john@example.com' }
+ expect(subject.authorize_params['login_hint']).to eq('john@example.com')
+ end
+ end
+
+ describe 'prompt' do
+ it 'should default to nil' do
+ expect(subject.authorize_params['prompt']).to eq(nil)
+ end
+
+ it 'should set the prompt parameter if present' do
+ @options = { prompt: 'consent select_account' }
+ expect(subject.authorize_params['prompt']).to eq('consent select_account')
+ end
+ end
+
+ describe 'request_visible_actions' do
+ it 'should default to nil' do
+ expect(subject.authorize_params['request_visible_actions']).to eq(nil)
+ end
+
+ it 'should set the request_visible_actions parameter if present' do
+ @options = { request_visible_actions: 'something' }
+ expect(subject.authorize_params['request_visible_actions']).to eq('something')
+ end
+ end
+
+ describe 'include_granted_scopes' do
+ it 'should default to nil' do
+ expect(subject.authorize_params['include_granted_scopes']).to eq(nil)
+ end
+
+ it 'should set the include_granted_scopes parameter if present' do
+ @options = { include_granted_scopes: 'true' }
+ expect(subject.authorize_params['include_granted_scopes']).to eq('true')
+ end
+ end
+
+ describe 'scope' do
+ it 'should expand scope shortcuts' do
+ @options = { scope: 'calendar' }
+ expect(subject.authorize_params['scope']).to eq('https://www.googleapis.com/auth/calendar')
+ end
+
+ it 'should leave base scopes as is' do
+ @options = { scope: 'profile' }
+ expect(subject.authorize_params['scope']).to eq('profile')
+ end
+
+ it 'should join scopes' do
+ @options = { scope: 'profile,email' }
+ expect(subject.authorize_params['scope']).to eq('profile email')
+ end
+
+ it 'should deal with whitespace when joining scopes' do
+ @options = { scope: 'profile, email' }
+ expect(subject.authorize_params['scope']).to eq('profile email')
+ end
+
+ it 'should set default scope to email,profile' do
+ expect(subject.authorize_params['scope']).to eq('email profile')
+ end
+
+ it 'should support space delimited scopes' do
+ @options = { scope: 'profile email' }
+ expect(subject.authorize_params['scope']).to eq('profile email')
+ end
+
+ it 'should support extremely badly formed scopes' do
+ @options = { scope: 'profile email,foo,steve yeah http://example.com' }
+ expect(subject.authorize_params['scope']).to eq('profile email https://www.googleapis.com/auth/foo https://www.googleapis.com/auth/steve https://www.googleapis.com/auth/yeah http://example.com')
+ end
+ end
+
+ describe 'state' do
+ it 'should set the state parameter' do
+ @options = { state: 'some_state' }
+ expect(subject.authorize_params['state']).to eq('some_state')
+ expect(subject.authorize_params[:state]).to eq('some_state')
+ expect(subject.session['omniauth.state']).to eq('some_state')
+ end
+
+ it 'should set the omniauth.state dynamically' do
+ allow(subject).to receive(:request) { double('Request', params: { 'state' => 'some_state' }, env: {}) }
+ expect(subject.authorize_params['state']).to eq('some_state')
+ expect(subject.authorize_params[:state]).to eq('some_state')
+ expect(subject.session['omniauth.state']).to eq('some_state')
+ end
+ end
+
+ describe 'overrides' do
+ it 'should include top-level options that are marked as :authorize_options' do
+ @options = { authorize_options: %i[scope foo request_visible_actions], scope: 'http://bar', foo: 'baz', hd: 'wow', request_visible_actions: 'something' }
+ expect(subject.authorize_params['scope']).to eq('http://bar')
+ expect(subject.authorize_params['foo']).to eq('baz')
+ expect(subject.authorize_params['hd']).to eq(nil)
+ expect(subject.authorize_params['request_visible_actions']).to eq('something')
+ end
+
+ describe 'request overrides' do
+ %i[access_type hd login_hint prompt scope state].each do |k|
+ context "authorize option #{k}" do
+ let(:request) { double('Request', params: { k.to_s => 'http://example.com' }, cookies: {}, env: {}) }
+
+ it "should set the #{k} authorize option dynamically in the request" do
+ @options = { k: '' }
+ expect(subject.authorize_params[k.to_s]).to eq('http://example.com')
+ end
+ end
+ end
+
+ describe 'custom authorize_options' do
+ let(:request) { double('Request', params: { 'foo' => 'something' }, cookies: {}, env: {}) }
+
+ it 'should support request overrides from custom authorize_options' do
+ @options = { authorize_options: [:foo], foo: '' }
+ expect(subject.authorize_params['foo']).to eq('something')
+ end
+ end
+ end
+ end
+ end
+
+ describe '#authorize_params' do
+ it 'should include any authorize params passed in the :authorize_params option' do
+ @options = { authorize_params: { request_visible_actions: 'something', foo: 'bar', baz: 'zip' }, hd: 'wow', bad: 'not_included' }
+ expect(subject.authorize_params['request_visible_actions']).to eq('something')
+ expect(subject.authorize_params['foo']).to eq('bar')
+ expect(subject.authorize_params['baz']).to eq('zip')
+ expect(subject.authorize_params['hd']).to eq('wow')
+ expect(subject.authorize_params['bad']).to eq(nil)
+ end
+ end
+
+ describe '#token_params' do
+ it 'should include any token params passed in the :token_params option' do
+ @options = { token_params: { foo: 'bar', baz: 'zip' } }
+ expect(subject.token_params['foo']).to eq('bar')
+ expect(subject.token_params['baz']).to eq('zip')
+ end
+ end
+
+ describe '#token_options' do
+ it 'should include top-level options that are marked as :token_options' do
+ @options = { token_options: %i[scope foo], scope: 'bar', foo: 'baz', bad: 'not_included' }
+ expect(subject.token_params['scope']).to eq('bar')
+ expect(subject.token_params['foo']).to eq('baz')
+ expect(subject.token_params['bad']).to eq(nil)
+ end
+ end
+
+ describe '#callback_url' do
+ let(:base_url) { 'https://example.com' }
+
+ it 'has the correct default callback path' do
+ allow(subject).to receive(:full_host) { base_url }
+ allow(subject).to receive(:script_name) { '' }
+ expect(subject.send(:callback_url)).to eq(base_url + '/auth/google_oauth2/callback')
+ end
+
+ it 'should set the callback path with script_name if present' do
+ allow(subject).to receive(:full_host) { base_url }
+ allow(subject).to receive(:script_name) { '/v1' }
+ expect(subject.send(:callback_url)).to eq(base_url + '/v1/auth/google_oauth2/callback')
+ end
+
+ it 'should set the callback_path parameter if present' do
+ @options = { callback_path: '/auth/foo/callback' }
+ allow(subject).to receive(:full_host) { base_url }
+ allow(subject).to receive(:script_name) { '' }
+ expect(subject.send(:callback_url)).to eq(base_url + '/auth/foo/callback')
+ end
+ end
+
+ describe '#info' do
+ let(:client) do
+ OAuth2::Client.new('abc', 'def') do |builder|
+ builder.request :url_encoded
+ builder.adapter :test do |stub|
+ stub.get('/oauth2/v3/userinfo') { [200, { 'content-type' => 'application/json' }, response_hash.to_json] }
+ end
+ end
+ end
+ let(:access_token) { OAuth2::AccessToken.from_hash(client, {}) }
+ before { allow(subject).to receive(:access_token).and_return(access_token) }
+
+ context 'with verified email' do
+ let(:response_hash) do
+ { email: 'something@domain.invalid', email_verified: true }
+ end
+
+ it 'should return equal email and unverified_email' do
+ expect(subject.info[:email]).to eq('something@domain.invalid')
+ expect(subject.info[:unverified_email]).to eq('something@domain.invalid')
+ end
+ end
+
+ context 'with unverified email' do
+ let(:response_hash) do
+ { email: 'something@domain.invalid', email_verified: false }
+ end
+
+ it 'should return nil email, and correct unverified email' do
+ expect(subject.info[:email]).to eq(nil)
+ expect(subject.info[:unverified_email]).to eq('something@domain.invalid')
+ end
+ end
+ end
+
+ describe '#credentials' do
+ let(:client) { OAuth2::Client.new('abc', 'def') }
+ let(:access_token) { OAuth2::AccessToken.from_hash(client, access_token: 'valid_access_token', expires_at: 123_456_789, refresh_token: 'valid_refresh_token') }
+ before(:each) do
+ allow(subject).to receive(:access_token).and_return(access_token)
+ subject.options.client_options[:connection_build] = proc do |builder|
+ builder.request :url_encoded
+ builder.adapter :test do |stub|
+ stub.get('/oauth2/v3/tokeninfo?access_token=valid_access_token') do
+ [200, { 'Content-Type' => 'application/json; charset=UTF-8' }, JSON.dump(
+ aud: '000000000000.apps.googleusercontent.com',
+ sub: '123456789',
+ scope: 'profile email'
+ )]
+ end
+ end
+ end
+ end
+
+ it 'should return access token and (optionally) refresh token' do
+ expect(subject.credentials.to_h).to \
+ match(hash_including(
+ 'token' => 'valid_access_token',
+ 'refresh_token' => 'valid_refresh_token',
+ 'scope' => 'profile email',
+ 'expires_at' => 123_456_789,
+ 'expires' => true
+ ))
+ end
+ end
+
+ describe '#extra' do
+ let(:client) do
+ OAuth2::Client.new('abc', 'def') do |builder|
+ builder.request :url_encoded
+ builder.adapter :test do |stub|
+ stub.get('/oauth2/v3/userinfo') { [200, { 'content-type' => 'application/json' }, '{"sub": "12345"}'] }
+ end
+ end
+ end
+ let(:access_token) { OAuth2::AccessToken.from_hash(client, {}) }
+
+ before { allow(subject).to receive(:access_token).and_return(access_token) }
+
+ describe 'id_token' do
+ shared_examples 'id_token issued by valid issuer' do |issuer|
+ context 'when the id_token is passed into the access token' do
+ let(:token_info) do
+ {
+ 'abc' => 'xyz',
+ 'exp' => Time.now.to_i + 3600,
+ 'nbf' => Time.now.to_i - 60,
+ 'iat' => Time.now.to_i,
+ 'aud' => 'appid',
+ 'iss' => issuer
+ }
+ end
+ let(:id_token) { JWT.encode(token_info, 'secret') }
+ let(:access_token) { OAuth2::AccessToken.from_hash(client, 'id_token' => id_token) }
+
+ it 'should include id_token when set on the access_token' do
+ expect(subject.extra).to include(id_token: id_token)
+ end
+
+ it 'should include id_info when id_token is set on the access_token and skip_jwt is false' do
+ subject.options[:skip_jwt] = false
+ expect(subject.extra).to include(id_info: token_info)
+ end
+
+ it 'should not include id_info when id_token is set on the access_token and skip_jwt is true' do
+ subject.options[:skip_jwt] = true
+ expect(subject.extra).not_to have_key(:id_info)
+ end
+
+ it 'should include id_info when id_token is set on the access_token by default' do
+ expect(subject.extra).to include(id_info: token_info)
+ end
+ end
+ end
+
+ it_behaves_like 'id_token issued by valid issuer', 'accounts.google.com'
+ it_behaves_like 'id_token issued by valid issuer', 'https://accounts.google.com'
+
+ context 'when the id_token is issued by an invalid issuer' do
+ let(:token_info) do
+ {
+ 'abc' => 'xyz',
+ 'exp' => Time.now.to_i + 3600,
+ 'nbf' => Time.now.to_i - 60,
+ 'iat' => Time.now.to_i,
+ 'aud' => 'appid',
+ 'iss' => 'fake.google.com'
+ }
+ end
+ let(:id_token) { JWT.encode(token_info, 'secret') }
+ let(:access_token) { OAuth2::AccessToken.from_hash(client, 'id_token' => id_token) }
+
+ it 'raises JWT::InvalidIssuerError' do
+ expect { subject.extra }.to raise_error(JWT::InvalidIssuerError)
+ end
+ end
+
+ context 'when the id_token is missing' do
+ it 'should not include id_token' do
+ expect(subject.extra).not_to have_key(:id_token)
+ end
+
+ it 'should not include id_info' do
+ expect(subject.extra).not_to have_key(:id_info)
+ end
+ end
+ end
+
+ describe 'raw_info' do
+ context 'when skip_info is true' do
+ before { subject.options[:skip_info] = true }
+
+ it 'should not include raw_info' do
+ expect(subject.extra).not_to have_key(:raw_info)
+ end
+ end
+
+ context 'when skip_info is false' do
+ before { subject.options[:skip_info] = false }
+
+ it 'should include raw_info' do
+ expect(subject.extra[:raw_info]).to eq('sub' => '12345')
+ end
+ end
+ end
+ end
+
+ describe 'populate auth hash urls' do
+ it 'should populate url map in auth hash if link present in raw_info' do
+ allow(subject).to receive(:raw_info) { { 'name' => 'Foo', 'profile' => 'https://plus.google.com/123456' } }
+ expect(subject.info[:urls][:google]).to eq('https://plus.google.com/123456')
+ end
+
+ it 'should not populate url map in auth hash if no link present in raw_info' do
+ allow(subject).to receive(:raw_info) { { 'name' => 'Foo' } }
+ expect(subject.info).not_to have_key(:urls)
+ end
+ end
+
+ describe 'image options' do
+ it 'should have no image if a picture is not present' do
+ @options = { image_aspect_ratio: 'square' }
+ allow(subject).to receive(:raw_info) { { 'name' => 'User Without Pic' } }
+ expect(subject.info[:image]).to be_nil
+ end
+
+ describe 'when a picture is returned from google' do
+ it 'should return the image with size specified in the `image_size` option' do
+ @options = { image_size: 50 }
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/photo.jpg' } }
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/s50/photo.jpg')
+ end
+
+ it 'should return the image with size specified in the `image_size` option when sizing is in the picture' do
+ @options = { image_size: 50 }
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh4.googleusercontent.com/url/s96-c/photo.jpg' } }
+ expect(subject.info[:image]).to eq('https://lh4.googleusercontent.com/url/s50/photo.jpg')
+ end
+
+ it 'should handle a picture with too many slashes correctly' do
+ @options = { image_size: 50 }
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url//photo.jpg' } }
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/s50/photo.jpg')
+ end
+
+ it 'should handle a picture with a size query parameter correctly' do
+ @options = { image_size: 50 }
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/photo.jpg?sz=50' } }
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/s50/photo.jpg')
+ end
+
+ it 'should handle a picture with a size query parameter and other valid query parameters correctly' do
+ @options = { image_size: 50 }
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/photo.jpg?sz=50&hello=true&life=42' } }
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/s50/photo.jpg?hello=true&life=42')
+ end
+
+ it 'should handle a picture with other valid query parameters correctly' do
+ @options = { image_size: 50 }
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/photo.jpg?hello=true&life=42' } }
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/s50/photo.jpg?hello=true&life=42')
+ end
+
+ it 'should return the image with width and height specified in the `image_size` option' do
+ @options = { image_size: { width: 50, height: 40 } }
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/photo.jpg' } }
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/w50-h40/photo.jpg')
+ end
+
+ it 'should return the image with width and height specified in the `image_size` option when sizing is in the picture' do
+ @options = { image_size: { width: 50, height: 40 } }
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/w100-h80-c/photo.jpg' } }
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/w50-h40/photo.jpg')
+ end
+
+ it 'should return square image when `image_aspect_ratio` is specified' do
+ @options = { image_aspect_ratio: 'square' }
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/photo.jpg' } }
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/c/photo.jpg')
+ end
+
+ it 'should return square image when `image_aspect_ratio` is specified and sizing is in the picture' do
+ @options = { image_aspect_ratio: 'square' }
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/c/photo.jpg' } }
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/c/photo.jpg')
+ end
+
+ it 'should return square sized image when `image_aspect_ratio` and `image_size` is set' do
+ @options = { image_aspect_ratio: 'square', image_size: 50 }
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/photo.jpg' } }
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/s50-c/photo.jpg')
+ end
+
+ it 'should return square sized image when `image_aspect_ratio` and `image_size` is set and sizing is in the picture' do
+ @options = { image_aspect_ratio: 'square', image_size: 50 }
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/s90/photo.jpg' } }
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/s50-c/photo.jpg')
+ end
+
+ it 'should return square sized image when `image_aspect_ratio` and `image_size` has height and width' do
+ @options = { image_aspect_ratio: 'square', image_size: { width: 50, height: 40 } }
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/photo.jpg' } }
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/w50-h40-c/photo.jpg')
+ end
+
+ it 'should return square sized image when `image_aspect_ratio` and `image_size` has height and width and sizing is in the picture' do
+ @options = { image_aspect_ratio: 'square', image_size: { width: 50, height: 40 } }
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/w100-h80/photo.jpg' } }
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/w50-h40-c/photo.jpg')
+ end
+
+ it 'should return original image if image url does not end in `photo.jpg`' do
+ @options = { image_size: 50 }
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/photograph.jpg' } }
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/photograph.jpg')
+ end
+ end
+
+ it 'should return original image if no options are provided' do
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https://lh3.googleusercontent.com/url/photo.jpg' } }
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/photo.jpg')
+ end
+
+ it 'should return correct image if google image url has double https' do
+ allow(subject).to receive(:raw_info) { { 'picture' => 'https:https://lh3.googleusercontent.com/url/photo.jpg' } }
+ expect(subject.info[:image]).to eq('https://lh3.googleusercontent.com/url/photo.jpg')
+ end
+ end
+
+ describe 'build_access_token' do
+ it 'should use a hybrid authorization request_uri if this is an AJAX request with a code parameter' do
+ allow(request).to receive(:xhr?).and_return(true)
+ allow(request).to receive(:params).and_return('code' => 'valid_code')
+
+ client = double(:client)
+ auth_code = double(:auth_code)
+ allow(client).to receive(:auth_code).and_return(auth_code)
+ expect(subject).to receive(:client).and_return(client)
+ expect(auth_code).to receive(:get_token).with('valid_code', { redirect_uri: 'postmessage' }, {})
+
+ expect(subject).not_to receive(:orig_build_access_token)
+ subject.build_access_token
+ end
+
+ it 'should use a hybrid authorization request_uri if this is an AJAX request (mobile) with a code parameter' do
+ allow(request).to receive(:xhr?).and_return(true)
+ allow(request).to receive(:params).and_return('code' => 'valid_code', 'redirect_uri' => '')
+
+ client = double(:client)
+ auth_code = double(:auth_code)
+ allow(client).to receive(:auth_code).and_return(auth_code)
+ expect(subject).to receive(:client).and_return(client)
+ expect(auth_code).to receive(:get_token).with('valid_code', { redirect_uri: '' }, {})
+
+ expect(subject).not_to receive(:orig_build_access_token)
+ subject.build_access_token
+ end
+
+ it 'should use the request_uri from params if this not an AJAX request (request from installed app) with a code parameter' do
+ allow(request).to receive(:xhr?).and_return(false)
+ allow(request).to receive(:params).and_return('code' => 'valid_code', 'redirect_uri' => 'redirect_uri')
+
+ client = double(:client)
+ auth_code = double(:auth_code)
+ allow(client).to receive(:auth_code).and_return(auth_code)
+ expect(subject).to receive(:client).and_return(client)
+ expect(auth_code).to receive(:get_token).with('valid_code', { redirect_uri: 'redirect_uri' }, {})
+
+ expect(subject).not_to receive(:orig_build_access_token)
+ subject.build_access_token
+ end
+
+ it 'should read access_token from hash if this is not an AJAX request with a code parameter' do
+ allow(request).to receive(:xhr?).and_return(false)
+ allow(request).to receive(:params).and_return('access_token' => 'valid_access_token')
+ expect(subject).to receive(:verify_token).with('valid_access_token').and_return true
+ expect(subject).to receive(:client).and_return(:client)
+
+ token = subject.build_access_token
+ expect(token).to be_instance_of(::OAuth2::AccessToken)
+ expect(token.token).to eq('valid_access_token')
+ expect(token.client).to eq(:client)
+ end
+
+ it 'reads the code from a json request body' do
+ body = StringIO.new(%({"code":"json_access_token"}))
+ client = double(:client)
+ auth_code = double(:auth_code)
+
+ allow(request).to receive(:xhr?).and_return(false)
+ allow(request).to receive(:content_type).and_return('application/json')
+ allow(request).to receive(:body).and_return(body)
+ allow(client).to receive(:auth_code).and_return(auth_code)
+ expect(subject).to receive(:client).and_return(client)
+
+ expect(auth_code).to receive(:get_token).with('json_access_token', { redirect_uri: 'postmessage' }, {})
+
+ subject.build_access_token
+ end
+
+ it 'reads the redirect uri from a json request body' do
+ body = StringIO.new(%({"code":"json_access_token", "redirect_uri":"sample"}))
+ client = double(:client)
+ auth_code = double(:auth_code)
+
+ allow(request).to receive(:xhr?).and_return(false)
+ allow(request).to receive(:content_type).and_return('application/json')
+ allow(request).to receive(:body).and_return(body)
+ allow(client).to receive(:auth_code).and_return(auth_code)
+ expect(subject).to receive(:client).and_return(client)
+
+ expect(auth_code).to receive(:get_token).with('json_access_token', { redirect_uri: 'sample' }, {})
+
+ subject.build_access_token
+ end
+
+ it 'reads the access token from a json request body' do
+ body = StringIO.new(%({"access_token":"valid_access_token"}))
+
+ allow(request).to receive(:xhr?).and_return(false)
+ allow(request).to receive(:content_type).and_return('application/json')
+ allow(request).to receive(:body).and_return(body)
+ expect(subject).to receive(:client).and_return(:client)
+
+ expect(subject).to receive(:verify_token).with('valid_access_token').and_return true
+
+ token = subject.build_access_token
+ expect(token).to be_instance_of(::OAuth2::AccessToken)
+ expect(token.token).to eq('valid_access_token')
+ expect(token.client).to eq(:client)
+ end
+
+ it 'should use callback_url without query_string if this is not an AJAX request' do
+ allow(request).to receive(:xhr?).and_return(false)
+ allow(request).to receive(:params).and_return('code' => 'valid_code')
+ allow(request).to receive(:content_type).and_return('application/x-www-form-urlencoded')
+
+ client = double(:client)
+ auth_code = double(:auth_code)
+ allow(client).to receive(:auth_code).and_return(auth_code)
+ allow(subject).to receive(:callback_url).and_return('redirect_uri_without_query_string')
+
+ expect(subject).to receive(:client).and_return(client)
+ expect(auth_code).to receive(:get_token).with('valid_code', { redirect_uri: 'redirect_uri_without_query_string' }, {})
+ subject.build_access_token
+ end
+ end
+
+ describe 'verify_token' do
+ before(:each) do
+ subject.options.client_options[:connection_build] = proc do |builder|
+ builder.request :url_encoded
+ builder.adapter :test do |stub|
+ stub.get('/oauth2/v3/tokeninfo?access_token=valid_access_token') do
+ [200, { 'Content-Type' => 'application/json; charset=UTF-8' }, JSON.dump(
+ aud: '000000000000.apps.googleusercontent.com',
+ sub: '123456789',
+ email_verified: 'true',
+ email: 'example@example.com',
+ access_type: 'offline',
+ scope: 'profile email',
+ expires_in: 436
+ )]
+ end
+ stub.get('/oauth2/v3/tokeninfo?access_token=invalid_access_token') do
+ [400, { 'Content-Type' => 'application/json; charset=UTF-8' }, JSON.dump(error_description: 'Invalid Value')]
+ end
+ end
+ end
+ end
+
+ it 'should verify token if access_token is valid and app_id equals' do
+ subject.options.client_id = '000000000000.apps.googleusercontent.com'
+ expect(subject.send(:verify_token, 'valid_access_token')).to eq(true)
+ end
+
+ it 'should verify token if access_token is valid and app_id authorized' do
+ subject.options.authorized_client_ids = ['000000000000.apps.googleusercontent.com']
+ expect(subject.send(:verify_token, 'valid_access_token')).to eq(true)
+ end
+
+ it 'should not verify token if access_token is valid but app_id is false' do
+ expect(subject.send(:verify_token, 'valid_access_token')).to eq(false)
+ end
+
+ it 'should raise error if access_token is invalid' do
+ expect do
+ subject.send(:verify_token, 'invalid_access_token')
+ end.to raise_error(OAuth2::Error)
+ end
+ end
+
+ describe 'verify_hd' do
+ let(:client) do
+ OAuth2::Client.new('abc', 'def') do |builder|
+ builder.request :url_encoded
+ builder.adapter :test do |stub|
+ stub.get('/oauth2/v3/userinfo') do
+ [200, { 'Content-Type' => 'application/json; charset=UTF-8' }, JSON.dump(
+ hd: 'example.com'
+ )]
+ end
+ end
+ end
+ end
+ let(:access_token) { OAuth2::AccessToken.from_hash(client, {}) }
+
+ context 'when domain is nil' do
+ let(:client) do
+ OAuth2::Client.new('abc', 'def') do |builder|
+ builder.request :url_encoded
+ builder.adapter :test do |stub|
+ stub.get('/oauth2/v3/userinfo') do
+ [200, { 'Content-Type' => 'application/json; charset=UTF-8' }, JSON.dump({})]
+ end
+ end
+ end
+ end
+
+ it 'should verify hd if options hd is set and correct' do
+ subject.options.hd = nil
+ expect(subject.send(:verify_hd, access_token)).to eq(true)
+ end
+
+ it 'should verify hd if options hd is set as an array and is correct' do
+ subject.options.hd = ['example.com', 'example.co', nil]
+ expect(subject.send(:verify_hd, access_token)).to eq(true)
+ end
+
+ it 'should raise an exception if nil is not included' do
+ subject.options.hd = ['example.com', 'example.co']
+ expect do
+ subject.send(:verify_hd, access_token)
+ end.to raise_error(OmniAuth::Strategies::OAuth2::CallbackError)
+ end
+ end
+
+ it 'should verify hd if options hd is not set' do
+ expect(subject.send(:verify_hd, access_token)).to eq(true)
+ end
+
+ it 'should verify hd if options hd is set and correct' do
+ subject.options.hd = 'example.com'
+ expect(subject.send(:verify_hd, access_token)).to eq(true)
+ end
+
+ it 'should verify hd if options hd is set as an array and is correct' do
+ subject.options.hd = ['example.com', 'example.co', nil]
+ expect(subject.send(:verify_hd, access_token)).to eq(true)
+ end
+
+ it 'should verify hd if options hd is set as an Proc and is correct' do
+ subject.options.hd = proc { 'example.com' }
+ expect(subject.send(:verify_hd, access_token)).to eq(true)
+ end
+
+ it 'should verify hd if options hd is set as an Proc returning an array and is correct' do
+ subject.options.hd = proc { ['example.com', 'example.co'] }
+ expect(subject.send(:verify_hd, access_token)).to eq(true)
+ end
+
+ it 'should raise error if options hd is set and wrong' do
+ subject.options.hd = 'invalid.com'
+ expect do
+ subject.send(:verify_hd, access_token)
+ end.to raise_error(OmniAuth::Strategies::GoogleOauth2::CallbackError)
+ end
+
+ it 'should raise error if options hd is set as an array and is not correct' do
+ subject.options.hd = ['invalid.com', 'invalid.co']
+ expect do
+ subject.send(:verify_hd, access_token)
+ end.to raise_error(OmniAuth::Strategies::GoogleOauth2::CallbackError)
+ end
+ end
+end
diff --git a/vendor/gems/omniauth-google-oauth2/spec/spec_helper.rb b/vendor/gems/omniauth-google-oauth2/spec/spec_helper.rb
new file mode 100644
index 00000000000..3c2325389c2
--- /dev/null
+++ b/vendor/gems/omniauth-google-oauth2/spec/spec_helper.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+require File.join('bundler', 'setup')
+require 'rspec'
diff --git a/vendor/gems/omniauth-salesforce/.gitlab-ci.yml b/vendor/gems/omniauth-salesforce/.gitlab-ci.yml
new file mode 100644
index 00000000000..4e18980d01c
--- /dev/null
+++ b/vendor/gems/omniauth-salesforce/.gitlab-ci.yml
@@ -0,0 +1,28 @@
+workflow:
+ rules:
+ - if: $CI_MERGE_REQUEST_ID
+
+.rspec:
+ cache:
+ key: omniauth-salesforce
+ paths:
+ - vendor/gems/omniauth-salesforce/vendor/ruby
+ before_script:
+ - cd vendor/gems/omniauth-salesforce
+ - ruby -v # Print out ruby version for debugging
+ - gem install bundler --no-document # Bundler is not installed with the image
+ - bundle config set --local path 'vendor' # Install dependencies into ./vendor/ruby
+ - bundle config set with 'development'
+ - bundle config set --local frozen 'true' # Disallow Gemfile.lock changes on CI
+ - bundle config # Show bundler configuration
+ - bundle install -j $(nproc)
+ script:
+ - bundle exec rspec
+
+rspec-2.7:
+ image: "ruby:2.7"
+ extends: .rspec
+
+rspec-3.0:
+ image: "ruby:3.0"
+ extends: .rspec
diff --git a/vendor/gems/omniauth-salesforce/Gemfile b/vendor/gems/omniauth-salesforce/Gemfile
new file mode 100755
index 00000000000..0ac6c2141a7
--- /dev/null
+++ b/vendor/gems/omniauth-salesforce/Gemfile
@@ -0,0 +1,12 @@
+source 'https://rubygems.org'
+
+# Specify your gem's dependencies in omniauth-salesforce.gemspec
+gemspec
+
+group :development, :test do
+ gem 'guard'
+ gem 'guard-rspec'
+ gem 'guard-bundler'
+ gem 'rb-fsevent'
+ gem 'growl'
+end
diff --git a/vendor/gems/omniauth-salesforce/Gemfile.lock b/vendor/gems/omniauth-salesforce/Gemfile.lock
new file mode 100644
index 00000000000..0317b16dd9f
--- /dev/null
+++ b/vendor/gems/omniauth-salesforce/Gemfile.lock
@@ -0,0 +1,124 @@
+PATH
+ remote: .
+ specs:
+ omniauth-salesforce (1.0.5)
+ omniauth (~> 2.0)
+ omniauth-oauth2 (~> 1.0)
+
+GEM
+ remote: https://rubygems.org/
+ specs:
+ addressable (2.8.1)
+ public_suffix (>= 2.0.2, < 6.0)
+ coderay (1.1.3)
+ crack (0.4.5)
+ rexml
+ diff-lcs (1.5.0)
+ docile (1.4.0)
+ faraday (2.5.2)
+ faraday-net_http (>= 2.0, < 3.1)
+ ruby2_keywords (>= 0.0.4)
+ faraday-net_http (3.0.0)
+ ffi (1.15.5)
+ formatador (1.1.0)
+ growl (1.0.3)
+ guard (2.18.0)
+ formatador (>= 0.2.4)
+ listen (>= 2.7, < 4.0)
+ lumberjack (>= 1.0.12, < 2.0)
+ nenv (~> 0.1)
+ notiffany (~> 0.0)
+ pry (>= 0.13.0)
+ shellany (~> 0.0)
+ thor (>= 0.18.1)
+ guard-bundler (3.0.0)
+ bundler (>= 2.1, < 3)
+ guard (~> 2.2)
+ guard-compat (~> 1.1)
+ guard-compat (1.2.1)
+ guard-rspec (4.7.3)
+ guard (~> 2.1)
+ guard-compat (~> 1.1)
+ rspec (>= 2.99.0, < 4.0)
+ hashdiff (1.0.1)
+ hashie (5.0.0)
+ jwt (2.4.1)
+ listen (3.7.1)
+ rb-fsevent (~> 0.10, >= 0.10.3)
+ rb-inotify (~> 0.9, >= 0.9.10)
+ lumberjack (1.2.8)
+ method_source (1.0.0)
+ multi_xml (0.6.0)
+ nenv (0.3.0)
+ notiffany (0.1.3)
+ nenv (~> 0.1)
+ shellany (~> 0.0)
+ oauth2 (2.0.3)
+ faraday (>= 0.17.3, < 3.0)
+ jwt (>= 1.0, < 3.0)
+ multi_xml (~> 0.5)
+ rack (>= 1.2, < 3)
+ rash_alt (>= 0.4, < 1)
+ version_gem (~> 1.0)
+ omniauth (2.1.0)
+ hashie (>= 3.4.6)
+ rack (>= 2.2.3)
+ rack-protection
+ omniauth-oauth2 (1.7.3)
+ oauth2 (>= 1.4, < 3)
+ omniauth (>= 1.9, < 3)
+ pry (0.14.1)
+ coderay (~> 1.1)
+ method_source (~> 1.0)
+ public_suffix (5.0.0)
+ rack (2.2.4)
+ rack-protection (2.2.2)
+ rack
+ rack-test (2.0.2)
+ rack (>= 1.3)
+ rash_alt (0.4.12)
+ hashie (>= 3.4)
+ rb-fsevent (0.11.1)
+ rb-inotify (0.10.1)
+ ffi (~> 1.0)
+ rexml (3.2.5)
+ rspec (2.99.0)
+ rspec-core (~> 2.99.0)
+ rspec-expectations (~> 2.99.0)
+ rspec-mocks (~> 2.99.0)
+ rspec-core (2.99.2)
+ rspec-expectations (2.99.2)
+ diff-lcs (>= 1.1.3, < 2.0)
+ rspec-mocks (2.99.4)
+ ruby2_keywords (0.0.5)
+ shellany (0.0.1)
+ simplecov (0.21.2)
+ docile (~> 1.1)
+ simplecov-html (~> 0.11)
+ simplecov_json_formatter (~> 0.1)
+ simplecov-html (0.12.3)
+ simplecov_json_formatter (0.1.4)
+ thor (1.2.1)
+ version_gem (1.1.0)
+ webmock (3.18.1)
+ addressable (>= 2.8.0)
+ crack (>= 0.3.2)
+ hashdiff (>= 0.4.0, < 2.0.0)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ growl
+ guard
+ guard-bundler
+ guard-rspec
+ omniauth-salesforce!
+ rack-test
+ rb-fsevent
+ rspec (~> 2.7)
+ simplecov
+ webmock
+
+BUNDLED WITH
+ 2.3.21
diff --git a/vendor/gems/omniauth-salesforce/Guardfile b/vendor/gems/omniauth-salesforce/Guardfile
new file mode 100755
index 00000000000..2aaba26cc43
--- /dev/null
+++ b/vendor/gems/omniauth-salesforce/Guardfile
@@ -0,0 +1,10 @@
+guard 'rspec', :version => 2 do
+ watch(%r{^spec/.+_spec\.rb$})
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
+ watch('spec/spec_helper.rb') { "spec" }
+end
+
+guard 'bundler' do
+ watch('Gemfile')
+ watch('omniauth-salesforce.gemspec')
+end
diff --git a/vendor/gems/omniauth-salesforce/LICENSE.md b/vendor/gems/omniauth-salesforce/LICENSE.md
new file mode 100644
index 00000000000..e662c786216
--- /dev/null
+++ b/vendor/gems/omniauth-salesforce/LICENSE.md
@@ -0,0 +1,5 @@
+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. \ No newline at end of file
diff --git a/vendor/gems/omniauth-salesforce/README.md b/vendor/gems/omniauth-salesforce/README.md
new file mode 100755
index 00000000000..df59e99bc55
--- /dev/null
+++ b/vendor/gems/omniauth-salesforce/README.md
@@ -0,0 +1,60 @@
+# omniauth-salesforce
+
+This is fork of [omniauth-salesforce](https://github.com/realdoug/omniauth-salesforce) to support:
+
+1. OmniAuth v1 and v2. OmniAuth v2 disables GET requests by default
+ and defaults to POST. GitLab already has patched v1 to use POST,
+ but other dependencies need to be updated:
+ https://gitlab.com/gitlab-org/gitlab/-/issues/30073.
+
+There is active discussion with the gem owner (via email) about adding some GitLab employees as gem
+authors so tha they can push changes. If that happens, the updated/canonical gem
+should be used in favor of this vendored fork.
+
+[OmniAuth](https://github.com/intridea/omniauth) Strategy for [salesforce.com](salesforce.com).
+
+Note: This is a fork of the [original](https://github.com/richardvanhook/omniauth-salesforce) project and is now the main repository for the omniauth-salesforce gem.
+
+## See it in action
+
+[http://omniauth-salesforce-example.herokuapp.com](http://omniauth-salesforce-example.herokuapp.com)
+
+[Source for above app](https://github.com/richardvanhook/omniauth-salesforce-example)
+
+## Basic Usage
+
+```ruby
+require "sinatra"
+require "omniauth"
+require "omniauth-salesforce"
+
+class MyApplication < Sinatra::Base
+ use Rack::Session
+ use OmniAuth::Builder do
+ provider :salesforce, ENV['SALESFORCE_KEY'], ENV['SALESFORCE_SECRET']
+ end
+end
+```
+
+## Including other sites
+
+```ruby
+use OmniAuth::Builder do
+ provider :salesforce,
+ ENV['SALESFORCE_KEY'],
+ ENV['SALESFORCE_SECRET']
+ provider OmniAuth::Strategies::SalesforceSandbox,
+ ENV['SALESFORCE_SANDBOX_KEY'],
+ ENV['SALESFORCE_SANDBOX_SECRET']
+ provider OmniAuth::Strategies::SalesforcePreRelease,
+ ENV['SALESFORCE_PRERELEASE_KEY'],
+ ENV['SALESFORCE_PRERELEASE_SECRET']
+ provider OmniAuth::Strategies::DatabaseDotCom,
+ ENV['DATABASE_DOT_COM_KEY'],
+ ENV['DATABASE_DOT_COM_SECRET']
+end
+```
+
+## Resources
+
+* [Article: Digging Deeper into OAuth 2.0 on Force.com](http://wiki.developerforce.com/index.php/Digging_Deeper_into_OAuth_2.0_on_Force.com)
diff --git a/vendor/gems/omniauth-salesforce/Rakefile b/vendor/gems/omniauth-salesforce/Rakefile
new file mode 100755
index 00000000000..ca978a9cf0d
--- /dev/null
+++ b/vendor/gems/omniauth-salesforce/Rakefile
@@ -0,0 +1,12 @@
+#!/usr/bin/env rake
+require "bundler/gem_tasks"
+require 'rspec/core/rake_task'
+
+desc 'Default: run specs.'
+task :default => :spec
+
+desc "Run specs"
+RSpec::Core::RakeTask.new
+
+desc 'Run specs'
+task :default => :spec
diff --git a/vendor/gems/omniauth-salesforce/lib/omniauth-salesforce.rb b/vendor/gems/omniauth-salesforce/lib/omniauth-salesforce.rb
new file mode 100755
index 00000000000..20a5937d762
--- /dev/null
+++ b/vendor/gems/omniauth-salesforce/lib/omniauth-salesforce.rb
@@ -0,0 +1,2 @@
+require "omniauth-salesforce/version"
+require 'omniauth/strategies/salesforce'
diff --git a/vendor/gems/omniauth-salesforce/lib/omniauth-salesforce/version.rb b/vendor/gems/omniauth-salesforce/lib/omniauth-salesforce/version.rb
new file mode 100755
index 00000000000..51f0084840e
--- /dev/null
+++ b/vendor/gems/omniauth-salesforce/lib/omniauth-salesforce/version.rb
@@ -0,0 +1,5 @@
+module OmniAuth
+ module Salesforce
+ VERSION = "1.0.5"
+ end
+end
diff --git a/vendor/gems/omniauth-salesforce/lib/omniauth/strategies/salesforce.rb b/vendor/gems/omniauth-salesforce/lib/omniauth/strategies/salesforce.rb
new file mode 100755
index 00000000000..f2d811aeb9e
--- /dev/null
+++ b/vendor/gems/omniauth-salesforce/lib/omniauth/strategies/salesforce.rb
@@ -0,0 +1,97 @@
+require 'omniauth-oauth2'
+require 'openssl'
+require 'base64'
+
+module OmniAuth
+ module Strategies
+ class Salesforce < OmniAuth::Strategies::OAuth2
+
+ MOBILE_USER_AGENTS = 'webos|ipod|iphone|ipad|android|blackberry|mobile'
+
+ option :client_options, {
+ :site => 'https://login.salesforce.com',
+ :authorize_url => '/services/oauth2/authorize',
+ :token_url => '/services/oauth2/token'
+ }
+ option :authorize_options, [
+ :scope,
+ :display,
+ :immediate,
+ :state,
+ :prompt
+ ]
+
+ def request_phase
+ req = Rack::Request.new(@env)
+ options.update(req.params)
+ ua = req.user_agent.to_s
+ if !options.has_key?(:display)
+ mobile_request = ua.downcase =~ Regexp.new(MOBILE_USER_AGENTS)
+ options[:display] = mobile_request ? 'touch' : 'page'
+ end
+ super
+ end
+
+ def auth_hash
+ signed_value = access_token.params['id'] + access_token.params['issued_at']
+ raw_expected_signature = OpenSSL::HMAC.digest('sha256', options.client_secret.to_s, signed_value)
+ expected_signature = Base64.strict_encode64 raw_expected_signature
+ signature = access_token.params['signature']
+ fail! "Salesforce user id did not match signature!" unless signature == expected_signature
+ super
+ end
+
+ uid { raw_info['id'] }
+
+ info do
+ {
+ 'name' => raw_info['display_name'],
+ 'email' => raw_info['email'],
+ 'nickname' => raw_info['nick_name'],
+ 'first_name' => raw_info['first_name'],
+ 'last_name' => raw_info['last_name'],
+ 'location' => '',
+ 'description' => '',
+ 'image' => raw_info['photos']['thumbnail'] + "?oauth_token=#{access_token.token}",
+ 'phone' => '',
+ 'urls' => raw_info['urls']
+ }
+ end
+
+ credentials do
+ hash = {'token' => access_token.token}
+ hash.merge!('instance_url' => access_token.params["instance_url"])
+ hash.merge!('refresh_token' => access_token.refresh_token) if access_token.refresh_token
+ hash
+ end
+
+ def raw_info
+ access_token.options[:mode] = :header
+ @raw_info ||= access_token.post(access_token['id']).parsed
+ end
+
+ extra do
+ raw_info.merge({
+ 'instance_url' => access_token.params['instance_url'],
+ 'pod' => access_token.params['instance_url'],
+ 'signature' => access_token.params['signature'],
+ 'issued_at' => access_token.params['issued_at']
+ })
+ end
+
+ end
+
+ class SalesforceSandbox < OmniAuth::Strategies::Salesforce
+ default_options[:client_options][:site] = 'https://test.salesforce.com'
+ end
+
+ class DatabaseDotCom < OmniAuth::Strategies::Salesforce
+ default_options[:client_options][:site] = 'https://login.database.com'
+ end
+
+ class SalesforcePreRelease < OmniAuth::Strategies::Salesforce
+ default_options[:client_options][:site] = 'https://prerellogin.pre.salesforce.com/'
+ end
+
+ end
+end
diff --git a/vendor/gems/omniauth-salesforce/omniauth-salesforce.gemspec b/vendor/gems/omniauth-salesforce/omniauth-salesforce.gemspec
new file mode 100755
index 00000000000..9c87746e9fd
--- /dev/null
+++ b/vendor/gems/omniauth-salesforce/omniauth-salesforce.gemspec
@@ -0,0 +1,24 @@
+# -*- encoding: utf-8 -*-
+require File.expand_path('../lib/omniauth-salesforce/version', __FILE__)
+
+Gem::Specification.new do |gem|
+ gem.authors = ["Richard Vanhook"]
+ gem.email = ["rvanhook@salesforce.com"]
+ gem.description = %q{OmniAuth strategy for salesforce.com.}
+ gem.summary = %q{OmniAuth strategy for salesforce.com.}
+ gem.homepage = "https://github.com/realdoug/omniauth-salesforce"
+
+ gem.files = Dir.glob("lib/**/*.*")
+ gem.test_files = Dir.glob("spec/**/**/*.*")
+ gem.name = "omniauth-salesforce"
+ gem.require_paths = ["lib"]
+ gem.version = OmniAuth::Salesforce::VERSION
+ gem.license = "MIT"
+
+ gem.add_dependency 'omniauth', '~> 2.0'
+ gem.add_dependency 'omniauth-oauth2', '~> 1.0'
+ gem.add_development_dependency 'rspec', '~> 2.7'
+ gem.add_development_dependency 'rack-test'
+ gem.add_development_dependency 'simplecov'
+ gem.add_development_dependency 'webmock'
+end
diff --git a/vendor/gems/omniauth-salesforce/spec/omniauth/strategies/salesforce_spec.rb b/vendor/gems/omniauth-salesforce/spec/omniauth/strategies/salesforce_spec.rb
new file mode 100755
index 00000000000..9b5afbdbe25
--- /dev/null
+++ b/vendor/gems/omniauth-salesforce/spec/omniauth/strategies/salesforce_spec.rb
@@ -0,0 +1,219 @@
+require 'spec_helper'
+
+describe OmniAuth::Strategies::Salesforce do
+ strategy = nil
+ before do
+ OmniAuth.config.test_mode = true
+ rack_app = []
+ rack_app.stub :call
+ strategy = OmniAuth::Strategies::Salesforce.new rack_app, 'Consumer Key', 'Consumer Secret'
+ end
+ describe "request_phase" do
+ env = nil
+ before do
+ env = {
+ 'rack.session' => {},
+ 'HTTP_USER_AGENT' => 'unknown',
+ 'REQUEST_METHOD' => 'GET',
+ 'rack.input' => '',
+ 'rack.url_scheme' => 'http',
+ 'SERVER_NAME' => 'server.example',
+ 'QUERY_STRING' => 'code=xxxx',
+ 'SCRIPT_NAME' => '',
+ 'SERVER_PORT' => 80
+ }
+ end
+ context "when using a mobile browser" do
+ user_agents = {
+ :Pre => "Mozilla/5.0 (webOS/1.4.0; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pre/1.1",
+ :iPod => "Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1 (KHTML, like Gecko) Version/3.0 Mobile/4A93 Safari/419.3",
+ :iPhone => "Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543 Safari/419.3",
+ :iPad => "Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) Version/4.0.4 Mobile/7B334b Safari/531.21.10",
+ :Nexus => "Mozilla/5.0 (Linux; U; Android 2.2; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
+ :myTouch => "Mozilla/5.0 (Linux; U; Android 1.6; en-us; WOWMobile myTouch 3G Build/unknown) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1",
+ :Storm => "BlackBerry9530/4.7.0.148 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/105",
+ :Torch => "Mozilla/5.0 (BlackBerry; U; BlackBerry 9810; en-US) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.0.0 Mobile Safari/534.11+",
+ :generic_mobile => "some mobile device"
+ }
+ user_agents.each_pair do |name, agent|
+ context "with the user agent from a #{name.to_s}" do
+ before do
+ env['HTTP_USER_AGENT'] = agent
+ strategy.call!(env)
+ strategy.request_phase
+ end
+ subject {strategy.options}
+ it "sets the :display option to 'touch'" do
+ subject[:display].should == 'touch'
+ end
+ end
+ end
+ end
+ context "when using a desktop browser" do
+ user_agents = {
+ :Chrome => "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.21 (KHTML, like Gecko) Chrome/19.0.1042.0 Safari/535.21",
+ :Safari => "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1",
+ :IE => "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
+ :anything_else => "unknown"
+ }
+ user_agents.each_pair do |name, agent|
+ context "with the user agent from #{name.to_s}" do
+ before do
+ env['HTTP_USER_AGENT'] = agent
+ strategy.call!(env)
+ strategy.request_phase
+ end
+ subject {strategy.options}
+ it "sets the :display option to 'page'" do
+ subject[:display].should == 'page'
+ end
+ end
+ end
+ end
+ end
+ describe "callback phase" do
+ raw_info = nil
+ before do
+ raw_info = {
+ 'id' => 'salesforce id',
+ 'display_name' => 'display name',
+ 'email' => 'email',
+ 'nick_name' => 'nick name',
+ 'first_name' => 'first name',
+ 'last_name' => 'last name',
+ 'photos' => {'thumbnail' => '/thumbnail/url'},
+ 'urls'=> {
+ "enterprise" => "https://salesforce.example/services",
+ "metadata" => "https://salesforce.example/services"
+ }
+ }
+ client = OAuth2::Client.new 'id', 'secret', {:site => 'example.com'}
+ access_token = OAuth2::AccessToken.from_hash client, {
+ 'access_token' => 'token',
+ 'instance_url' => 'http://instance.salesforce.example',
+ 'signature' => 'invalid',
+ 'issued_at' => '1296458209517'
+ }
+ strategy.stub(:raw_info) { raw_info }
+ strategy.stub(:access_token) { access_token }
+ end
+ describe "uid" do
+ it "sets the id" do
+ strategy.uid.should == raw_info['id']
+ end
+ end
+ describe "info" do
+ subject { strategy.info }
+ it "returns an info hash" do
+ subject.should_not be_nil
+ end
+ it "sets name" do
+ subject['name'].should == raw_info['display_name']
+ end
+ it "sets email" do
+ subject['email'].should == raw_info['email']
+ end
+ it "sets nickname" do
+ subject['nickname'].should == raw_info['nick_name']
+ end
+ it "sets first_name" do
+ subject['first_name'].should == raw_info['first_name']
+ end
+ it "sets last_name" do
+ subject['last_name'].should == raw_info['last_name']
+ end
+ it "sets location" do
+ subject['location'].should == ''
+ end
+ it "sets description" do
+ subject['description'].should == ''
+ end
+ it "sets image" do
+ subject['image'].should == raw_info['photos']['thumbnail'] + "?oauth_token=#{strategy.access_token.token}"
+ end
+ it "sets phone" do
+ subject['phone'].should == ''
+ end
+ it "sets urls" do
+ subject['urls'].should == raw_info['urls']
+ end
+ end
+ describe "credentials" do
+ subject { strategy.credentials }
+ it "sets token" do
+ subject['token'].should == strategy.access_token.token
+ end
+ it "sets instance_url" do
+ subject['instance_url'].should == strategy.access_token.params["instance_url"]
+ end
+ context "given a refresh token" do
+ it "sets refresh_token" do
+ subject['refresh_token'].should == strategy.access_token.refresh_token
+ end
+ end
+ context "when not given a refresh token" do
+ it "does not set a refresh token" do
+ subject['refresh_token'].should be_nil
+ end
+ end
+ end
+ describe "extra" do
+ subject { strategy.extra }
+ it "sets instance_url" do
+ subject['instance_url'].should == strategy.access_token.params['instance_url']
+ end
+ it "sets pod" do
+ subject['pod'].should == strategy.access_token.params['instance_url']
+ end
+ it "sets signature" do
+ subject['signature'].should == strategy.access_token.params['signature']
+ end
+ it "sets issued_at" do
+ subject['issued_at'].should == strategy.access_token.params['issued_at']
+ end
+ end
+ describe "user id validation" do
+ client_id = nil
+ issued_at = nil
+ signature = nil
+ instance_url = 'http://instance.salesforce.example'
+ before do
+ client_id = "https://login.salesforce.com/id/00Dd0000000d45TEBQ/005d0000000fyGPCCY"
+ issued_at = "1331142541514"
+ signature = Base64.strict_encode64(OpenSSL::HMAC.digest('sha256', strategy.options.client_secret.to_s, client_id + issued_at))
+ end
+ context "when the signature does not match" do
+ before do
+ access_token = OAuth2::AccessToken.from_hash strategy.access_token.client, {
+ 'id' => 'forged client id',
+ 'refresh_token' => 'anything',
+ 'issued_at' => issued_at,
+ 'instance_url' => 'http://instance.salesforce.example',
+ 'signature' => signature
+ }
+ strategy.stub(:access_token) { access_token }
+ end
+ it "should call fail!" do
+ strategy.should_receive(:fail!)
+ strategy.auth_hash
+ end
+ end
+ context "when the signature does match" do
+ before do
+ access_token = OAuth2::AccessToken.from_hash strategy.access_token.client, {
+ 'id' => client_id,
+ 'refresh_token' => 'anything',
+ 'issued_at' => issued_at,
+ 'instance_url' => 'http://instance.salesforce.example',
+ 'signature' => signature
+ }
+ strategy.stub(:access_token) { access_token }
+ end
+ it "should not fail" do
+ strategy.should_not_receive(:fail!)
+ strategy.auth_hash
+ end
+ end
+ end
+ end
+end
diff --git a/vendor/gems/omniauth-salesforce/spec/spec_helper.rb b/vendor/gems/omniauth-salesforce/spec/spec_helper.rb
new file mode 100755
index 00000000000..0b00358fa05
--- /dev/null
+++ b/vendor/gems/omniauth-salesforce/spec/spec_helper.rb
@@ -0,0 +1,16 @@
+$:.unshift File.expand_path('..', __FILE__)
+$:.unshift File.expand_path('../../lib', __FILE__)
+require 'simplecov'
+SimpleCov.start
+require 'rspec'
+require 'rack/test'
+require 'webmock/rspec'
+require 'omniauth'
+require 'omniauth-salesforce'
+
+RSpec.configure do |config|
+ config.include WebMock::API
+ config.include Rack::Test::Methods
+ config.extend OmniAuth::Test::StrategyMacros, :type => :strategy
+end
+
diff --git a/vendor/gems/omniauth_crowd/Gemfile.lock b/vendor/gems/omniauth_crowd/Gemfile.lock
index 56c9bd4cc7e..43518582535 100644
--- a/vendor/gems/omniauth_crowd/Gemfile.lock
+++ b/vendor/gems/omniauth_crowd/Gemfile.lock
@@ -4,7 +4,7 @@ PATH
omniauth_crowd (2.4.0)
activesupport
nokogiri (>= 1.4.4)
- omniauth (~> 1.0, < 3)
+ omniauth (~> 2.0)
GEM
remote: http://rubygems.org/
@@ -29,12 +29,15 @@ GEM
nokogiri (1.13.8)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
- omniauth (1.9.1)
+ omniauth (2.1.0)
hashie (>= 3.4.6)
- rack (>= 1.6.2, < 3)
+ rack (>= 2.2.3)
+ rack-protection
public_suffix (4.0.7)
racc (1.6.0)
rack (2.2.4)
+ rack-protection (2.2.2)
+ rack
rack-test (2.0.2)
rack (>= 1.3)
rake (13.0.6)
diff --git a/vendor/gems/omniauth_crowd/lib/omniauth/strategies/crowd.rb b/vendor/gems/omniauth_crowd/lib/omniauth/strategies/crowd.rb
index 7e3829b9b95..7126d8328e0 100755
--- a/vendor/gems/omniauth_crowd/lib/omniauth/strategies/crowd.rb
+++ b/vendor/gems/omniauth_crowd/lib/omniauth/strategies/crowd.rb
@@ -17,15 +17,7 @@ module OmniAuth
protected
def request_phase
- if env['REQUEST_METHOD'] == 'GET'
-
- if @configuration.use_sessions? && request.cookies[@configuration.session_cookie]
- redirect callback_url
- else
- get_credentials
- end
-
- elsif (env['REQUEST_METHOD'] == 'POST') && (not request.params['username'])
+ if (env['REQUEST_METHOD'] == 'POST') && (not request.params['username'])
get_credentials
else
session['omniauth.crowd'] = {'username' => request['username'], 'password' => request['password']}
diff --git a/vendor/gems/omniauth_crowd/omniauth_crowd.gemspec b/vendor/gems/omniauth_crowd/omniauth_crowd.gemspec
index 1707c7f3f10..dcbf403419f 100644
--- a/vendor/gems/omniauth_crowd/omniauth_crowd.gemspec
+++ b/vendor/gems/omniauth_crowd/omniauth_crowd.gemspec
@@ -15,7 +15,7 @@ Gem::Specification.new do |gem|
gem.require_paths = ["lib"]
gem.version = OmniAuth::Crowd::VERSION
- gem.add_runtime_dependency 'omniauth', '~> 1.0', '< 3'
+ gem.add_runtime_dependency 'omniauth', '~> 2.0'
gem.add_runtime_dependency 'nokogiri', '>= 1.4.4'
gem.add_runtime_dependency 'activesupport', '>= 0'
gem.add_development_dependency(%q<rack>, [">= 0"])
diff --git a/vendor/gems/omniauth_crowd/spec/omniauth/strategies/crowd_spec.rb b/vendor/gems/omniauth_crowd/spec/omniauth/strategies/crowd_spec.rb
index f234ef82e76..000b3901f86 100755
--- a/vendor/gems/omniauth_crowd/spec/omniauth/strategies/crowd_spec.rb
+++ b/vendor/gems/omniauth_crowd/spec/omniauth/strategies/crowd_spec.rb
@@ -20,9 +20,21 @@ describe OmniAuth::Strategies::Crowd, :type=>:strategy do
@sso_url_image = nil
let(:config) { OmniAuth::Strategies::Crowd::Configuration.new(strategy[1]) }
let(:validator) { OmniAuth::Strategies::Crowd::CrowdValidator.new(config, 'foo', 'bar', nil, nil) }
+ let(:csrf_token) { SecureRandom.base64(32) }
+ let(:base_env) { { 'rack.session' => { csrf: csrf_token }, 'rack.input' => StringIO.new("authenticity_token=#{escaped_token}") } }
+ let(:post_env) { make_env('/auth/crowd', base_env) }
+ let(:escaped_token) { URI.encode_www_form_component(csrf_token, Encoding::UTF_8) }
+
+ def make_env(path = '/auth/crowd', props = {})
+ {
+ 'REQUEST_METHOD' => 'POST',
+ 'PATH_INFO' => path,
+ 'rack.session' => {},
+ 'rack.input' => StringIO.new('test=true')
+ }.merge(props)
+ end
describe 'Authentication Request Body' do
-
it 'should send password in session request' do
body = <<-BODY.strip
<password>
@@ -42,21 +54,13 @@ BODY
end
end
- describe 'GET /auth/crowd' do
+ describe 'POST /auth/crowd' do
it 'should show the login form' do
- get '/auth/crowd'
+ post '/auth/crowd', nil, post_env
expect(last_response).to be_ok
end
end
- describe 'POST /auth/crowd' do
- it 'should redirect to callback' do
- post '/auth/crowd', :username=>'foo', :password=>'bar'
- expect(last_response).to be_redirect
- expect(last_response.headers['Location']).to eq('http://example.org/auth/crowd/callback')
- end
- end
-
describe 'GET /auth/crowd/callback without any credentials' do
it 'should fail' do
get '/auth/crowd/callback'
@@ -79,13 +83,16 @@ BODY
to_return(:status => [415, "Unsupported Media Type"])
get '/auth/crowd/callback', nil, 'rack.session'=>{'omniauth.crowd'=> {"username"=>"foo", "password"=>"ba"}}
end
+
it 'should call through to the master app' do
expect(last_response.body).to eq('true')
end
+
it 'should have an auth hash' do
auth = last_request.env['omniauth.auth']
expect(auth).to be_kind_of(Hash)
end
+
it 'should have good data' do
auth = last_request.env['omniauth.auth']
expect(auth['provider']).to eq(:crowd)
@@ -142,8 +149,7 @@ BODY
end
end
- describe 'GET /auth/crowd without credentials will redirect to login form' do
-
+ describe 'POST /auth/crowd without credentials will redirect to login form' do
sso_url = 'https://foo.bar'
before do
@@ -152,10 +158,9 @@ BODY
end
it 'should have the SSO button in the response body' do
-
found_legend = found_anchor = nil
- get '/auth/crowd'
+ post '/auth/crowd', nil, post_env
Nokogiri::HTML(last_response.body).xpath('//html/body/form/fieldset/*').each do |element|
@@ -163,26 +168,23 @@ BODY
found_legend = true
elsif element.name === 'a' && element.attr('href') === "#{sso_url}/users/auth/crowd/callback"
found_anchor = true
- end
+ end
end
expect(found_legend).to(be(true))
expect(found_anchor).to(be(true))
-
end
after do
@using_sessions = false
@sso_url = nil
end
-
end
-
- describe 'GET /auth/crowd without credentials will redirect to login form which has custom image in the SSO link' do
-
+
+ describe 'POST /auth/crowd without credentials will redirect to login form which has custom image in the SSO link' do
sso_url = 'https://foo.bar'
sso_url_image = 'https://foo.bar/image.png'
-
+
before do
@using_sessions = true
@sso_url = sso_url
@@ -190,10 +192,9 @@ BODY
end
it 'should have the SSO button with a custom image in the response body' do
-
found_legend = found_anchor = found_image = false
- get '/auth/crowd'
+ post '/auth/crowd', nil, post_env
Nokogiri::HTML(last_response.body).xpath('//html/body/form/fieldset/*').each do |element|
@@ -206,14 +207,12 @@ BODY
if element.children.length === 1 && element.children.first.name === 'img' && element.children.first.attr('src') === sso_url_image
found_image = true
end
-
end
end
expect(found_legend).to(be(true))
expect(found_anchor).to(be(true))
expect(found_image).to(be(true))
-
end
after do
@@ -221,46 +220,13 @@ BODY
@sso_url = nil
@sso_url_image = nil
end
-
end
- describe 'GET /auth/crowd without credentials but with SSO cookie will redirect to callback' do
-
- sso_url = 'https://foo.bar'
-
- before do
-
- @using_sessions = true
- @sso_url = sso_url
-
- set_cookie('crowd.token_key=foobar')
-
- end
-
- it 'should redirect to callback' do
- get '/auth/crowd'
- expect(last_response).to be_redirect
- expect(last_response.headers['Location']).to eq('http://example.org/auth/crowd/callback')
- end
-
- after do
-
- @using_sessions = false
- @sso_url = nil
-
- clear_cookies()
-
- end
-
- end
-
describe 'POST /auth/crowd/callback without credentials but with SSO cookie will redirect to login form because session is invalid' do
-
sso_url = 'https://foo.bar'
token = 'foobar'
-
+
before do
-
@using_sessions = true
@sso_url = sso_url
@@ -268,7 +234,6 @@ BODY
to_return(:status => [404])
set_cookie("crowd.token_key=#{token}")
-
end
it 'should redirect to login form' do
@@ -360,7 +325,6 @@ BODY
end
it 'should return user data' do
-
auth = nil
get '/auth/crowd/callback'
diff --git a/vendor/project_templates/cluster_management.tar.gz b/vendor/project_templates/cluster_management.tar.gz
index 93eba37a57b..b7c44a4770d 100644
--- a/vendor/project_templates/cluster_management.tar.gz
+++ b/vendor/project_templates/cluster_management.tar.gz
Binary files differ
diff --git a/workhorse/.gitignore b/workhorse/.gitignore
index 97a27630a9c..3cd5f522336 100644
--- a/workhorse/.gitignore
+++ b/workhorse/.gitignore
@@ -8,5 +8,6 @@ testdata/alt-public
/gitlab-zip-metadata
/_build
coverage.html
+cover.out
/*.toml
/gitaly.pid
diff --git a/workhorse/Makefile b/workhorse/Makefile
index fe9bf639753..a0412f5e2e1 100644
--- a/workhorse/Makefile
+++ b/workhorse/Makefile
@@ -84,8 +84,8 @@ test: prepare-tests
else \
$(MAKE) run-gitaly ; \
fi
- @go test -tags "$(BUILD_TAGS)" ./... ;\
- status="$$?" ;\
+ go test ${TEST_OPTIONS} -tags "$(BUILD_TAGS)" ./...
+ @status="$$?" ;\
if [ -f "$(GITALY_PID_FILE)" ] ; then \
echo "Clean up Gitaly server for workhorse integration test" ;\
kill -9 $$(cat $(GITALY_PID_FILE)) ;\
@@ -96,10 +96,21 @@ test: prepare-tests
exit "$$status"
@echo SUCCESS
+.PHONY: test-race
+test-race: TEST_OPTIONS = -race
+test-race: test
+
+.PHONY: test-coverage
+test-coverage: TEST_OPTIONS = -cover -coverprofile=cover.out
+test-coverage: test
+ $(call message, "Calculating the coverage")
+ [ -f cover.out ] && go tool cover -html cover.out -o coverage.html
+ [ -f cover.out ] && go tool cover -func cover.out
+
.PHONY: clean
clean: clean-workhorse clean-build
$(call message,$@)
- rm -rf testdata/data testdata/scratch
+ rm -rf testdata/data testdata/scratch cover.out coverage.html
.PHONY: clean-workhorse
clean-workhorse:
@@ -112,8 +123,7 @@ clean-build:
rm -rf $(TARGET_DIR)
.PHONY: prepare-tests
-prepare-tests: testdata/data/group/test.git $(EXE_ALL)
-prepare-tests: testdata/scratch
+prepare-tests: testdata/scratch $(EXE_ALL)
.PHONY: run-gitaly
run-gitaly: $(GITALY_PID_FILE)
@@ -130,10 +140,6 @@ gitaly.toml: ../tmp/tests/gitaly/config.toml
$(call message, "Building a complete test environment")
cd .. ; ./scripts/setup-test-env
-testdata/data/group/test.git:
- $(call message,$@)
- git clone --quiet --bare https://gitlab.com/gitlab-org/gitlab-test.git $@
-
testdata/scratch:
mkdir -p testdata/scratch
diff --git a/workhorse/gitaly_integration_test.go b/workhorse/gitaly_integration_test.go
index d578ae50765..a2826c3edc4 100644
--- a/workhorse/gitaly_integration_test.go
+++ b/workhorse/gitaly_integration_test.go
@@ -18,6 +18,7 @@ import (
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitaly/v15/proto/go/gitalypb"
+ "gitlab.com/gitlab-org/gitaly/v15/streamio"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/api"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/gitaly"
@@ -78,13 +79,28 @@ func ensureGitalyRepository(t *testing.T, apiResponse *api.Response) error {
return err
}
- createReq := &gitalypb.CreateRepositoryFromURLRequest{
- Repository: &apiResponse.Repository,
- Url: "https://gitlab.com/gitlab-org/gitlab-test.git",
+ stream, err := repository.CreateRepositoryFromBundle(ctx)
+ if err != nil {
+ return fmt.Errorf("initiate stream: %w", err)
+ }
+
+ if err := stream.Send(&gitalypb.CreateRepositoryFromBundleRequest{Repository: &apiResponse.Repository}); err != nil {
+ return err
+ }
+
+ gitBundle := exec.Command("git", "-C", path.Join(testRepoRoot, testRepo), "bundle", "create", "-", "--all")
+ gitBundle.Stdout = streamio.NewWriter(func(p []byte) error {
+ return stream.Send(&gitalypb.CreateRepositoryFromBundleRequest{Data: p})
+ })
+
+ if err := gitBundle.Run(); err != nil {
+ return fmt.Errorf("run git bundle --create: %w", err)
+ }
+ if _, err := stream.CloseAndRecv(); err != nil {
+ return fmt.Errorf("finish CreateRepositoryFromBundle: %w", err)
}
- _, err = repository.CreateRepositoryFromURL(ctx, createReq)
- return err
+ return nil
}
func TestAllowedClone(t *testing.T) {
@@ -282,24 +298,22 @@ func TestAllowedGetGitDiff(t *testing.T) {
apiResponse := realGitalyOkBody(t)
require.NoError(t, ensureGitalyRepository(t, apiResponse))
- leftCommit := "8a0f2ee90d940bfb0ba1e14e8214b0649056e4ab"
- rightCommit := "e395f646b1499e8e0279445fc99a0596a65fab7e"
- expectedBody := "diff --git a/README.md b/README.md"
-
msg := serializedMessage("RawDiffRequest", &gitalypb.RawDiffRequest{
Repository: &apiResponse.Repository,
- LeftCommitId: leftCommit,
- RightCommitId: rightCommit,
+ LeftCommitId: "b0e52af38d7ea43cf41d8a6f2471351ac036d6c9",
+ RightCommitId: "732401c65e924df81435deb12891ef570167d2e2",
})
jsonParams := buildGitalyRPCParams(gitalyAddress, msg)
resp, body, err := doSendDataRequest("/something", "git-diff", jsonParams)
require.NoError(t, err)
- shortBody := string(body[:len(expectedBody)])
require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resp.Request.URL)
- require.Equal(t, expectedBody, shortBody, "GET %q: response body", resp.Request.URL)
requireNginxResponseBuffering(t, "no", resp, "GET %q: nginx response buffering", resp.Request.URL)
+
+ expectedBody := "diff --git a/LICENSE b/LICENSE\n"
+ require.Equal(t, expectedBody, string(body[:len(expectedBody)]),
+ "GET %q: response body", resp.Request.URL)
}
func TestAllowedGetGitFormatPatch(t *testing.T) {
@@ -309,12 +323,10 @@ func TestAllowedGetGitFormatPatch(t *testing.T) {
apiResponse := realGitalyOkBody(t)
require.NoError(t, ensureGitalyRepository(t, apiResponse))
- leftCommit := "8a0f2ee90d940bfb0ba1e14e8214b0649056e4ab"
- rightCommit := "e395f646b1499e8e0279445fc99a0596a65fab7e"
msg := serializedMessage("RawPatchRequest", &gitalypb.RawPatchRequest{
Repository: &apiResponse.Repository,
- LeftCommitId: leftCommit,
- RightCommitId: rightCommit,
+ LeftCommitId: "b0e52af38d7ea43cf41d8a6f2471351ac036d6c9",
+ RightCommitId: "0e1b353b348f8477bdbec1ef47087171c5032cd9",
})
jsonParams := buildGitalyRPCParams(gitalyAddress, msg)
@@ -324,14 +336,10 @@ func TestAllowedGetGitFormatPatch(t *testing.T) {
require.Equal(t, 200, resp.StatusCode, "GET %q: status code", resp.Request.URL)
requireNginxResponseBuffering(t, "no", resp, "GET %q: nginx response buffering", resp.Request.URL)
- requirePatchSeries(
- t,
- body,
- "372ab6950519549b14d220271ee2322caa44d4eb",
- "57290e673a4c87f51294f5216672cbc58d485d25",
- "41ae11ba5d091d73d5de671f6fa7d1a4539e979e",
- "742518b2be68fc750bb4c357c0df821a88113286",
- rightCommit,
+ requirePatchSeries(t, body,
+ "732401c65e924df81435deb12891ef570167d2e2",
+ "33bcff41c232a11727ac6d660bd4b0c2ba86d63d",
+ "0e1b353b348f8477bdbec1ef47087171c5032cd9",
)
}
diff --git a/workhorse/go.mod b/workhorse/go.mod
index e2191c1a749..500753bbf97 100644
--- a/workhorse/go.mod
+++ b/workhorse/go.mod
@@ -26,15 +26,15 @@ require (
github.com/sirupsen/logrus v1.9.0
github.com/smartystreets/goconvey v1.7.2
github.com/stretchr/testify v1.8.0
- gitlab.com/gitlab-org/gitaly/v15 v15.2.2
+ gitlab.com/gitlab-org/gitaly/v15 v15.4.0-rc2
gitlab.com/gitlab-org/golang-archive-zip v0.1.1
gitlab.com/gitlab-org/labkit v1.16.0
- gocloud.dev v0.25.0
- golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8
+ gocloud.dev v0.26.0
+ golang.org/x/image v0.0.0-20220722155232-062f8c9fd539
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616
- golang.org/x/net v0.0.0-20220531201128-c960675eff93
- golang.org/x/tools v0.1.11
- google.golang.org/grpc v1.48.0
+ golang.org/x/net v0.0.0-20220722155237-a158d28d115b
+ golang.org/x/tools v0.1.12
+ google.golang.org/grpc v1.49.0
google.golang.org/protobuf v1.28.1
honnef.co/go/tools v0.3.3
)
@@ -74,7 +74,7 @@ require (
github.com/google/wire v0.5.0 // indirect
github.com/googleapis/gax-go/v2 v2.2.0 // indirect
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect
- github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect
+ github.com/hashicorp/yamux v0.1.1 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/kr/text v0.2.0 // indirect
@@ -102,12 +102,12 @@ 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-20220525230936-793ad666bf5e // indirect
+ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // 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/oauth2 v0.0.0-20220309155454-6242fa91716a // indirect
- golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect
- golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
+ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
+ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
diff --git a/workhorse/go.sum b/workhorse/go.sum
index d143f62f812..30c7fec32d2 100644
--- a/workhorse/go.sum
+++ b/workhorse/go.sum
@@ -1,10 +1,7 @@
-bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
-bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg=
cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
@@ -49,7 +46,7 @@ cloud.google.com/go/compute v1.5.0 h1:b1zWmYuuHz7gO9kDcM/EpHGr06UgsYNRpNJzI2kFiL
cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
-cloud.google.com/go/firestore v1.5.0/go.mod h1:c4nNYR1qdq7eaZ+jSc5fonrQN2k3M7sWATcYTiakjEo=
+cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c=
cloud.google.com/go/iam v0.1.1/go.mod h1:CKqrcnI/suGpybEHxZ7BMehL0oA4LpdyJdUlTl9jVMw=
@@ -66,7 +63,6 @@ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2k
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
-cloud.google.com/go/pubsub v1.10.3/go.mod h1:FUcc28GpGxxACoklPsE1sCtbkY4Ix+ro7yvw+h82Jn4=
cloud.google.com/go/pubsub v1.19.0/go.mod h1:/O9kmSe9bb9KRnIAWkzmqhPjHo6LtzGOBYd/kr06XSs=
cloud.google.com/go/secretmanager v1.3.0/go.mod h1:+oLTkouyiYiabAQNugCeTS3PAArGiMJuBqvJnJsyH+U=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
@@ -74,7 +70,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
-cloud.google.com/go/storage v1.15.0/go.mod h1:mjjQMoxxyGH7Jr8K5qrx6N2O0AHsczI61sMNn03GIZI=
cloud.google.com/go/storage v1.21.0 h1:HwnT2u2D309SFDHQII6m18HlrCi3jAXhUMTLOWXYH14=
cloud.google.com/go/storage v1.21.0/go.mod h1:XmRlxkgPjlBONznT2dDUU/5XlpU2OjMnKuqnZI01LAA=
cloud.google.com/go/trace v0.1.0/go.mod h1:wxEwsoeRVPbeSkt7ZC9nWCgmoKQRAoySN7XHW2AmI7g=
@@ -82,63 +77,47 @@ cloud.google.com/go/trace v1.0.0/go.mod h1:4iErSByzxkyHWzzlAj63/Gmjz0NH1ASqhJguH
cloud.google.com/go/trace v1.2.0 h1:oIaB4KahkIUOpLSAAjEJ8y2desbjY/x/RfP4O3KAtTI=
cloud.google.com/go/trace v1.2.0/go.mod h1:Wc8y/uYyOhPy12KEnXG9XGrvfMz5F5SrYecQlbW1rwM=
contrib.go.opencensus.io/exporter/aws v0.0.0-20200617204711-c478e41e60e9/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA=
-contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EUaVTwzvYETVNZk10Pu26tevFKLUc=
contrib.go.opencensus.io/exporter/stackdriver v0.13.8/go.mod h1:huNtlWx75MwO7qMs0KrMxPZXzNNWebav1Sq/pm02JdQ=
contrib.go.opencensus.io/exporter/stackdriver v0.13.10 h1:a9+GZPUe+ONKUwULjlEOucMMG0qfSCCenlji0Nhqbys=
contrib.go.opencensus.io/exporter/stackdriver v0.13.10/go.mod h1:I5htMbyta491eUxufwwZPQdcKvvgzMB4O9ni41YnIM8=
contrib.go.opencensus.io/integrations/ocsql v0.1.7/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
-github.com/Azure/azure-amqp-common-go/v3 v3.1.0/go.mod h1:PBIGdzcO1teYoufTKMcGibdKaYZv4avS+O6LNIp8bq0=
github.com/Azure/azure-amqp-common-go/v3 v3.2.1/go.mod h1:O6X1iYHP7s2x7NjUKsXVhkwWrQhxrd+d8/3rRadj4CI=
github.com/Azure/azure-amqp-common-go/v3 v3.2.2/go.mod h1:O6X1iYHP7s2x7NjUKsXVhkwWrQhxrd+d8/3rRadj4CI=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-sdk-for-go v51.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/azure-sdk-for-go v54.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v59.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
-github.com/Azure/azure-service-bus-go v0.10.11/go.mod h1:AWw9eTTWZVZyvgpPahD1ybz3a8/vT3GsJDS8KYex55U=
github.com/Azure/azure-service-bus-go v0.11.5/go.mod h1:MI6ge2CuQWBVq+ly456MY7XqNLJip5LO1iSFodbNLbU=
-github.com/Azure/azure-storage-blob-go v0.13.0/go.mod h1:pA9kNqtjUeQF2zOSu4s//nUdBD+e64lEuc4sVnuOfNs=
github.com/Azure/azure-storage-blob-go v0.14.0 h1:1BCg74AmVdYwO3dlKwtFU1V0wU2PZdREkXvAmZJRUlM=
github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck=
-github.com/Azure/go-amqp v0.13.0/go.mod h1:qj+o8xPCz9tMSbQ83Vp8boHahuRDl5mkNHyt1xlxUTs=
-github.com/Azure/go-amqp v0.13.4/go.mod h1:wbpCKA8tR5MLgRyIu+bb+S6ECdIDdYJ0NlpFE9xsBPI=
-github.com/Azure/go-amqp v0.13.7/go.mod h1:wbpCKA8tR5MLgRyIu+bb+S6ECdIDdYJ0NlpFE9xsBPI=
github.com/Azure/go-amqp v0.16.0/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fwmw9Zlg=
github.com/Azure/go-amqp v0.16.4/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fwmw9Zlg=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
-github.com/Azure/go-autorest/autorest v0.11.3/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
-github.com/Azure/go-autorest/autorest v0.11.17/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
github.com/Azure/go-autorest/autorest v0.11.19/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
github.com/Azure/go-autorest/autorest v0.11.22 h1:bXiQwDjrRmBQOE67bwlvUKAC1EU1yZTPQ38c+bstZws=
github.com/Azure/go-autorest/autorest v0.11.22/go.mod h1:BAWYUWGPEtKPzjVkp0Q6an0MJcJDsoh5Z1BFAEFs4Xs=
-github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
-github.com/Azure/go-autorest/autorest/adal v0.9.2/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE=
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
-github.com/Azure/go-autorest/autorest/adal v0.9.11/go.mod h1:nBKAnTomx8gDtl+3ZCJv2v0KACFHWTB2drffI1B68Pk=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.14/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/adal v0.9.17 h1:esOPl2dhcz9P3jqBSJ8tPGEj2EqzPPT6zfyuloiogKY=
github.com/Azure/go-autorest/autorest/adal v0.9.17/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
-github.com/Azure/go-autorest/autorest/azure/auth v0.5.7/go.mod h1:AkzUsqkrdmNhfP2i54HqINVQopw0CLDnvHpJ88Zz1eI=
github.com/Azure/go-autorest/autorest/azure/auth v0.5.9 h1:Y2CgdzitFDsdMwYMzf9LIZWrrTFysqbRc7b94XVVJ78=
github.com/Azure/go-autorest/autorest/azure/auth v0.5.9/go.mod h1:hg3/1yw0Bq87O3KvvnJoAh34/0zbP7SFizX/qN5JvjU=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 h1:dMOmEJfkLKW/7JsokJqkyoYSgmR08hi9KrhjZb+JALY=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2/go.mod h1:7qkJkT+j6b+hIpzMOwPChJhTqS8VbsqqgULzMNRugoM=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
-github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
-github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
@@ -148,9 +127,7 @@ github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw=
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
-github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w=
github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
github.com/DataDog/datadog-go v4.4.0+incompatible h1:R7WqXWP4fIOAqWJtUKmSfuc7eDsBT58k9AY5WSHVosk=
github.com/DataDog/datadog-go v4.4.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
@@ -159,28 +136,23 @@ github.com/DataDog/sketches-go v1.0.0 h1:chm5KSXO7kO+ywGWJ0Zs6tdmWU8PBXSbywFVciL
github.com/DataDog/sketches-go v1.0.0/go.mod h1:O+XkJHWk9w4hDwY2ZUDU31ZC9sNYlYo8DiFsxjYeo1k=
github.com/FZambia/sentinel v1.1.0 h1:qrCBfxc8SvJihYNjBWgwUI93ZCvFe/PJIPTHKmlp8a8=
github.com/FZambia/sentinel v1.1.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI=
-github.com/GoogleCloudPlatform/cloudsql-proxy v1.22.0/go.mod h1:mAm5O/zik2RFmcpigNjg6nMotDL8ZXJaxKzgGVcSMFA=
github.com/GoogleCloudPlatform/cloudsql-proxy v1.29.0/go.mod h1:spvB9eLJH9dutlbPSRmHvSXXHOwGRyeXh1jVdquA2G8=
-github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/HdrHistogram/hdrhistogram-go v1.1.1 h1:cJXY5VLMHgejurPjZH6Fo9rIwRGLefBGdiaENZALqrg=
github.com/HdrHistogram/hdrhistogram-go v1.1.1/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
-github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM=
-github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
+github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
+github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
-github.com/Microsoft/go-winio v0.4.19/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU=
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/ProtonMail/go-crypto v0.0.0-20220810064516-de89276ce0f3/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8=
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
-github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
-github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
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/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
@@ -193,7 +165,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
-github.com/alexbrainman/sspi v0.0.0-20180125232955-4729b3d4d858/go.mod h1:976q2ETgjT2snVCf2ZaBnyBbVoPERGjUz+0sofzEfro=
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
@@ -202,19 +173,13 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
-github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/avast/retry-go v2.4.2+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
-github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/aws/aws-sdk-go v1.17.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
-github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.43.31 h1:yJZIr8nMV1hXjAvvOLUFqZRJcHV7udPQBfhJqawDzI0=
github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
-github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/aws/aws-sdk-go-v2 v1.16.2 h1:fqlCk6Iy3bnCumtrLz9r3mJ/2gUT0pJ0wLFVIdWh+JA=
github.com/aws/aws-sdk-go-v2 v1.16.2/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.1 h1:SdK4Ppk5IzLs64ZMvr6MrSficMtjY2oS0WOORXTlxwU=
@@ -263,13 +228,12 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
-github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
-github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
+github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0 h1:t/LhUZLVitR1Ow2YOnduCsavhwFUklBMoGVYUCqmCqk=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/certifi/gocertifi v0.0.0-20180905225744-ee1a9a0726d2/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV47EEXL8mWmRdEfGscSJ+7EgePNgt0s=
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
@@ -280,14 +244,11 @@ github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc=
github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
-github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/client9/reopen v1.0.0 h1:8tpLVR74DLpLObrn2KvsyxJY++2iORGR17WLUdSzUws=
github.com/client9/reopen v1.0.0/go.mod h1:caXVCEr+lUtoN1FlsRiOWdfQtdRHIYfcb0ai8qKWtkQ=
-github.com/cloudflare/tableflip v0.0.0-20190329062924-8392f1641731/go.mod h1:erh4dYezoMVbIa52pi7i1Du7+cXOgqNuTamt10qvMoA=
-github.com/cloudflare/tableflip v1.2.2/go.mod h1:P4gRehmV6Z2bY5ao5ml9Pd8u6kuEnlB37pUFMmv7j2E=
+github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
github.com/cloudflare/tableflip v1.2.3 h1:8I+B99QnnEWPHOY3fWipwVKxS70LGgUsslG7CSfmHMw=
github.com/cloudflare/tableflip v1.2.3/go.mod h1:P4gRehmV6Z2bY5ao5ml9Pd8u6kuEnlB37pUFMmv7j2E=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
@@ -300,35 +261,26 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
-github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
-github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
-github.com/containerd/cgroups v0.0.0-20201118023556-2819c83ced99/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
-github.com/coreos/go-systemd/v22 v22.3.1/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU=
github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
github.com/dgryski/go-minhash v0.0.0-20170608043002-7fe510aff544/go.mod h1:VBi0XHpFy0xiMySf6YpVbRqrupW4RprJ5QTyN+XvGSM=
@@ -342,19 +294,12 @@ github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/dpotapov/go-spnego v0.0.0-20190506202455-c2c609116ad0/go.mod h1:P4f4MSk7h52F2PK0lCapn5+fu47Uf8aRdxDSqgezxZE=
github.com/dpotapov/go-spnego v0.0.0-20210315154721-298b63a54430/go.mod h1:AVSs/gZKt1bOd2AhkhbS7Qh56Hv7klde22yXVbwYJhc=
github.com/dpotapov/go-spnego v0.0.0-20220426193508-b7f82e4507db/go.mod h1:AVSs/gZKt1bOd2AhkhbS7Qh56Hv7klde22yXVbwYJhc=
-github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
-github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
-github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
-github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
github.com/ekzhu/minhash-lsh v0.0.0-20171225071031-5c06ee8586a1/go.mod h1:yEtCVi+QamvzjEH4U/m6ZGkALIkF2xfQnFp0BcKmIOk=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
-github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -366,16 +311,12 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
-github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
-github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
-github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
-github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/fsnotify/fsnotify v1.4.3-0.20170329110642-4da3e2cfbabc/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@@ -383,26 +324,17 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/garyburd/redigo v1.1.1-0.20170914051019-70e1b1943d4f/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
-github.com/getsentry/raven-go v0.1.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
-github.com/getsentry/raven-go v0.1.2/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
-github.com/getsentry/sentry-go v0.5.1/go.mod h1:B8H7x8TYDPkeWPRzGpIiFO97LZP6rL8A3hEt8lUItMw=
-github.com/getsentry/sentry-go v0.7.0/go.mod h1:pLFpD2Y5RHIKF9Bw3KH6/68DeN2K/XBJd8awjdPnUwg=
-github.com/getsentry/sentry-go v0.10.0/go.mod h1:kELm/9iCblqUYh+ZRML7PNdCvEuw24wBvJPYyi86cws=
github.com/getsentry/sentry-go v0.13.0/go.mod h1:EOsfu5ZdvKPfeHYV6pTVQnsjfp30+XA7//UooKNumH0=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.7.3/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
-github.com/git-lfs/git-lfs v1.5.1-0.20210304194248-2e1d981afbe3/go.mod h1:8Xqs4mqL7o6xEnaXckIgELARTeK7RYtm3pBab7S79Js=
github.com/git-lfs/git-lfs/v3 v3.2.0/go.mod h1:GZZO3jw2Yn3/1KFV4nRoXUzH+yPzGypIdTeQpkzxEvQ=
-github.com/git-lfs/gitobj/v2 v2.0.1/go.mod h1:q6aqxl6Uu3gWsip5GEKpw+7459F97er8COmU45ncAxw=
github.com/git-lfs/gitobj/v2 v2.1.0/go.mod h1:q6aqxl6Uu3gWsip5GEKpw+7459F97er8COmU45ncAxw=
-github.com/git-lfs/go-netrc v0.0.0-20180525200031-e0e9ca483a18/go.mod h1:70O4NAtvWn1jW8V8V+OKrJJYcxDLTmIozfi2fmSz5SI=
github.com/git-lfs/go-netrc v0.0.0-20210914205454-f0c862dd687a/go.mod h1:70O4NAtvWn1jW8V8V+OKrJJYcxDLTmIozfi2fmSz5SI=
github.com/git-lfs/pktline v0.0.0-20210330133718-06e9096e2825/go.mod h1:fenKRzpXDjNpsIBhuhUzvjCKlDjKam0boRAenTE0Q6A=
-github.com/git-lfs/wildmatch v1.0.4/go.mod h1:SdHAGnApDpnFYQ0vAxbniWR0sn7yLJ3QXo9RRfhn2ew=
github.com/git-lfs/wildmatch/v2 v2.0.1/go.mod h1:EVqonpk9mXbREP3N8UkwoWdrF249uHpCUo5CPXY81gw=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
@@ -420,10 +352,10 @@ github.com/go-git/go-git/v5 v5.3.0/go.mod h1:xdX4bWJ48aOrdhnl2XqHYstHbbp6+LFS4r4
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY=
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
@@ -437,27 +369,21 @@ github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvSc
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
-github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
-github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
-github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
-github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
-github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc=
+github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs=
+github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT85L4+VY=
+github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
-github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
-github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
-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.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
@@ -472,14 +398,12 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGw
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f h1:16RtHeWGkJMc80Etb8RPCcKevXGldr57+LOyZt8zOlg=
github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f/go.mod h1:ijRvpgDJDI262hYq/IQVYgf8hd8IHUs93Ol0kvMBAx4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/lint v0.0.0-20170918230701-e5d664eb928e/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
-github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
@@ -509,7 +433,6 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/gomodule/redigo v1.8.8/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
@@ -534,10 +457,8 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
-github.com/google/go-replayers/grpcreplay v1.0.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
github.com/google/go-replayers/grpcreplay v1.1.0 h1:S5+I3zYyZ+GQz68OfbURDdt/+cSMqCK1wrvNx7WBzTE=
github.com/google/go-replayers/grpcreplay v1.1.0/go.mod h1:qzAvJ8/wi57zq7gWqaE6AwLM6miiXUQwP1S+I9icmhk=
-github.com/google/go-replayers/httpreplay v0.1.2/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no=
github.com/google/go-replayers/httpreplay v1.1.1 h1:H91sIMlt1NZzN7R+/ASswyouLJfW0WLW7fhyUFvDEkY=
github.com/google/go-replayers/httpreplay v1.1.1/go.mod h1:gN9GeLIs7l6NUoVaSSnv2RiqK1NiwAmD0MrKeC9IIks=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -573,8 +494,6 @@ github.com/google/pprof v0.0.0-20210804190019-f964ff605595/go.mod h1:kpwsk12EmLe
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
-github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
@@ -591,30 +510,21 @@ github.com/googleapis/gax-go/v2 v2.2.0 h1:s7jOdKSaksJVOxE0Y/S32otcfiP+UQ0cL8/GTK
github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
-github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
-github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
-github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
-github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20170920190843-316c5e0ff04e/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-middleware v1.2.3-0.20210213123510-be4c235f9d1c/go.mod h1:RXwzibsL7UhPcEmGyGvXKJ8kyJsOCOEaLgGce4igMFs=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok=
github.com/hanwen/go-fuse/v2 v2.1.0/go.mod h1:oRyA5eK+pvJyv5otpO/DgccS8y/RvYMaO00GgRLGryc=
-github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
-github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
+github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
@@ -638,24 +548,22 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
-github.com/hashicorp/yamux v0.0.0-20210316155119-a95892c5f864/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
-github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I=
-github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
+github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
+github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/hhatto/gorst v0.0.0-20181029133204-ca9f730cac5b/go.mod h1:HmaZGXHdSwQh1jnUlBGN2BeEYOHACLVGzYOXCbsLvxY=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
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/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
+github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
github.com/inconshreveable/log15 v0.0.0-20170622235902-74a0988b5f80/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
-github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI=
github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk=
github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g=
github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
@@ -668,9 +576,8 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
-github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
-github.com/jackc/pgconn v1.12.1/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono=
+github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
@@ -684,31 +591,27 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
-github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
-github.com/jackc/pgtype v1.9.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
-github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
+github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
-github.com/jackc/pgx/v4 v4.14.1/go.mod h1:RgDuE4Z34o7XE92RpLsvFiOEfrAUT0Xt2KxvX73W06M=
github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw=
-github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ=
+github.com/jackc/pgx/v4 v4.17.0/go.mod h1:Gd6RmOhtFLTu8cp/Fhq4kP195KrshxYJH3oW8AWJ1pw=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
-github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
-github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc=
@@ -725,12 +628,9 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC
github.com/johannesboyne/gofakes3 v0.0.0-20220627085814-c3ac35da23b2 h1:V5q1Mx2WTE5coXLG2QpkRZ7LsJvgkedm6Ib4AwC1Lfg=
github.com/johannesboyne/gofakes3 v0.0.0-20220627085814-c3ac35da23b2/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/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -740,40 +640,30 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
github.com/jteeuwen/go-bindata v3.0.8-0.20180305030458-6025e8de665b+incompatible/go.mod h1:JVvhzYOiGBnFSYRyV00iY8q7/0PThjIYav1p9h5dmKs=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
-github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
-github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
-github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
-github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
-github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk=
+github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8=
-github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U=
github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE=
-github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw=
github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE=
-github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0=
github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro=
github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8=
-github.com/kelseyhightower/envconfig v1.3.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
-github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
-github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
-github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kortschak/utter v1.0.1/go.mod h1:vSmSjbyrlKjjsL71193LmzBOKgwePk9DH6uFaWHIInc=
+github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@@ -785,7 +675,6 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
-github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
@@ -794,24 +683,19 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/libgit2/git2go v0.0.0-20190104134018-ecaeb7a21d47/go.mod h1:4bKN42efkbNYMZlvDfxGDxzl066GhpvIircZDsm8Y+Y=
-github.com/libgit2/git2go/v31 v31.4.12/go.mod h1:c/rkJcBcUFx6wHaT++UwNpKvIsmPNqCeQ/vzO4DrEec=
github.com/libgit2/git2go/v33 v33.0.9/go.mod h1:KdpqkU+6+++4oHna/MIOgx4GCQ92IPCdpVRMRI80J+4=
-github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
-github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20200305213919-a88bf8de3718/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20210210170715-a8dfcb80d3a7 h1:YjW+hUb8Fh2S58z4av4t/0cBMK/Q0aP48RocCFsC8yI=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20210210170715-a8dfcb80d3a7/go.mod h1:Spd59icnvRxSKuyijbbwe5AemzvcyXAUBgApa7VybMw=
-github.com/lightstep/lightstep-tracer-go v0.15.6/go.mod h1:6AMpwZpsyCFwSovxzM78e+AsYxE8sGwiM6C3TytaWeI=
-github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
-github.com/lightstep/lightstep-tracer-go v0.24.0/go.mod h1:RnONwHKg89zYPmF+Uig5PpHMUcYCFgml8+r4SS53y7A=
github.com/lightstep/lightstep-tracer-go v0.25.0 h1:sGVnz8h3jTQuHKMbUe2949nXm3Sg09N1UcR3VoQNN5E=
github.com/lightstep/lightstep-tracer-go v0.25.0/go.mod h1:G1ZAEaqTHFPWpWunnbUn1ADEY/Jvzz7jIOaXwAfD6A8=
-github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/magiconair/properties v1.7.4-0.20170902060319-8d7837e64d3c/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
+github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc=
+github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI=
+github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
@@ -832,21 +716,18 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
-github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
-github.com/mattn/go-shellwords v0.0.0-20190425161501-2444a32a19f4/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
-github.com/mattn/go-shellwords v1.0.11/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
-github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg=
-github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
-github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a/go.mod h1:v8eSC2SMp9/7FTKUncp7fH9IwPfw+ysMObcEz5FWheQ=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4=
+github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@@ -861,6 +742,7 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -873,20 +755,13 @@ github.com/montanaflynn/stats v0.0.0-20151014174947-eeaced052adb/go.mod h1:wL8QJ
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
-github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/neurosnap/sentences v1.0.6/go.mod h1:pg1IapvYpWCJJm/Etxeh0+gtMf1rI1STY9S7eUCPbDc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
-github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/ulid/v2 v2.0.2 h1:r4fFzBm+bv0wNKNh5eXTwU7i85y5x+uwkxCUTNVQqLc=
github.com/oklog/ulid/v2 v2.0.2/go.mod h1:mtBL0Qe/0HAx6/a4Z30qxVIAL1eQDweXq5lxOEiwQ68=
-github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
-github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
-github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -896,83 +771,53 @@ github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
-github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
-github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
-github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
-github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
-github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
-github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
-github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
-github.com/otiai10/copy v1.0.1/go.mod h1:8bMCJrAqOtN/d9oyh5HR7HhLQMvcGMpGdwRDYsfOCHc=
-github.com/otiai10/copy v1.4.2/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E=
-github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
-github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
-github.com/otiai10/mint v1.2.3/go.mod h1:YnfyPNhBvnY8bW4SGQHCs/aAFhkgySlMZbrF5U0bOVw=
-github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
-github.com/otiai10/mint v1.3.2/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
-github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
-github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.0.1-0.20170904195809-1d6b12b7cb29/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
-github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
-github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
-github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
+github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
+github.com/pelletier/go-toml/v2 v2.0.3/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ=
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
-github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
-github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
-github.com/pires/go-proxyproto v0.5.0/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
-github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
+github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.0.0-20170505043639-c605e284fe17/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
+github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/poy/onpar v0.0.0-20190519213022-ee068f8ea4d1/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
-github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
-github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
-github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
@@ -980,17 +825,13 @@ github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/rafaeljusto/redigomock/v3 v3.1.1 h1:SdWE9v+SPy3x6G5hS3aofIJgHJY3OdBJ0BdUTk4dYbA=
github.com/rafaeljusto/redigomock/v3 v3.1.1/go.mod h1:F9zPqz8rMriScZkPtUiLJoLruYcpGo/XXREpeyasREM=
-github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
-github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
-github.com/rubenv/sql-migrate v0.0.0-20191213152630-06338513c237/go.mod h1:rtQlpHw+eR6UrqaS3kX1VYeaCxzCVdimDS7g5Ln4pPc=
+github.com/rubenv/sql-migrate v1.1.2/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMHQPT4FWdnbQ=
github.com/rubyist/tracerx v0.0.0-20170927163412-787959303086/go.mod h1:YpdgDXpumPB/+EGmGTYHeiW/0QVFRzBYTNFaxWfPDk4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -998,11 +839,9 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8=
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
-github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
-github.com/sebest/xff v0.0.0-20160910043805-6c115e0ffa35/go.mod h1:wozgYq9WEBQBaIJe4YZ0qTSFAMxmcwBhQH0fO0R34Z0=
github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a h1:iLcLb5Fwwz7g/DLK89F+uQBDeAhHhwdzB5fSlVdhGcM=
github.com/sebest/xff v0.0.0-20210106013422-671bd2870b3a/go.mod h1:wozgYq9WEBQBaIJe4YZ0qTSFAMxmcwBhQH0fO0R34Z0=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
@@ -1010,8 +849,6 @@ github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNX
github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63/go.mod h1:n+VKSARF5y/tS9XFSP7vWDfS+GUC5vs/YT7M5XDTUEM=
github.com/shabbyrobe/gocovmerge v0.0.0-20190829150210-3e036491d500 h1:WnNuhiq+FOY3jNj6JXFT+eLN3CQ/oPIsDPRanvwsmbI=
github.com/shabbyrobe/gocovmerge v0.0.0-20190829150210-3e036491d500/go.mod h1:+njLrG5wSeoG4Ds61rFgEzKvenR2UHbjMoDHsczxly0=
-github.com/shirou/gopsutil v2.20.1+incompatible h1:oIq9Cq4i84Hk8uQAUOG3eNdI/29hBawGrD5YRl6JRDY=
-github.com/shirou/gopsutil v2.20.1+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil/v3 v3.21.2 h1:fIOk3hyqV1oGKogfGNjUZa0lUbtlkx3+ZT0IoJth2uM=
github.com/shirou/gopsutil/v3 v3.21.2/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw=
github.com/shogo82148/go-shuffle v0.0.0-20170808115208-59829097ff3b/go.mod h1:2htx6lmL0NGLHlO8ZCf+lQBGBHIbEujyywxJArf+2Yc=
@@ -1020,7 +857,6 @@ github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFR
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
@@ -1034,28 +870,27 @@ github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYl
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=
-github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
-github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v0.0.0-20170901052352-ee1bd8ee15a1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.1.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
+github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/jwalterweatherman v0.0.0-20170901151539-12bd96e66386/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.1-0.20170901120850-7aff26db30c1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/ssgelm/cookiejarparser v1.0.1/go.mod h1:DUfC0mpjIzlDN7DzKjXpHj0qMI5m9VrZuz3wSlI+OEI=
-github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
-github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
-github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
@@ -1071,30 +906,21 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
-github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
+github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ=
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tklauser/go-sysconf v0.3.4 h1:HT8SVixZd3IzLdfs/xlpq0jeSfTX57g1v6wB1EuzV7M=
github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek=
github.com/tklauser/numcpus v0.2.1 h1:ct88eFm+Q7m2ZfXJdan1xYoXKlmwsfP+k88q05KvlZc=
github.com/tklauser/numcpus v0.2.1/go.mod h1:9aU+wOc6WjUIZEwWMP62PL/41d65P+iks1gBkr4QyP8=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
-github.com/uber/jaeger-client-go v2.15.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
-github.com/uber/jaeger-client-go v2.27.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-client-go v2.29.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
-github.com/uber/jaeger-lib v1.5.0/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
-github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
-github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
-github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
@@ -1107,46 +933,31 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v0.0.0-20170210233622-6b67b3fab74d/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
-github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
-gitlab.com/gitlab-org/gitaly v1.68.0 h1:VlcJs1+PrhW7lqJUU7Fh1q8FMJujmbbivdfde/cwB98=
-gitlab.com/gitlab-org/gitaly v1.68.0/go.mod h1:/pCsB918Zu5wFchZ9hLYin9WkJ2yQqdVNz0zlv5HbXg=
-gitlab.com/gitlab-org/gitaly/v14 v14.0.0-rc1/go.mod h1:4Cz8tOAyueSZX5o6gYum1F/unupaOclxqETPcg4ODvQ=
-gitlab.com/gitlab-org/gitaly/v14 v14.9.0-rc5.0.20220329111719-51da8bc17059/go.mod h1:uX1qhFKBDuPqATlpMcFL2dKDiX8D/tbUg7CYWx7OXt4=
-gitlab.com/gitlab-org/gitaly/v15 v15.2.2 h1:/hSbAhBqRrT6Epc35k83qFwwVbKottNY6wDFr+5DYQo=
-gitlab.com/gitlab-org/gitaly/v15 v15.2.2/go.mod h1:WjitFL44l9ovitGC4OvSuGwfeq0VpHUbHS6sDw13LV8=
-gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20201117050822-3f9890ef73dc/go.mod h1:5QSTbpAHY2v0iIH5uHh2KA9w7sPUqPmnLjDApI/sv1U=
-gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20210720163109-50da611814d2/go.mod h1:QWDYBwuy24qGMandtCngLRPzFgnGPg6LSNoJWPKmJMc=
-gitlab.com/gitlab-org/gitlab-shell/v14 v14.8.0/go.mod h1:Z1S5MihpEmtA7GDXGsU0kUf1nzm7zr8w6bP+uXRnxaw=
+gitlab.com/gitlab-org/gitaly/v15 v15.4.0-rc2 h1:ngV/NWEMz4TRlnJFzBpatdXkEIDMHsU3YUzGdZU63qY=
+gitlab.com/gitlab-org/gitaly/v15 v15.4.0-rc2/go.mod h1:Rv/LoDU5I3cOP6KLUWFjC1eigcay2u8b8ZcsW86A0Xo=
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 v0.0.0-20190221122536-0c3fc7cdd57c/go.mod h1:rYhLgfrbEcyfinG+R3EvKu6bZSsmwQqcXzLfHWSfUKM=
-gitlab.com/gitlab-org/labkit v0.0.0-20200908084045-45895e129029/go.mod h1:SNfxkfUwVNECgtmluVayv0GWFgEjjBs5AzgsowPQuo0=
-gitlab.com/gitlab-org/labkit v1.0.0/go.mod h1:nohrYTSLDnZix0ebXZrbZJjymRar8HeV2roWL5/jw2U=
-gitlab.com/gitlab-org/labkit v1.4.1/go.mod h1:x5JO5uvdX4t6e/TZXLXZnFL5AcKz2uLLd3uKXZcuO4k=
-gitlab.com/gitlab-org/labkit v1.5.0/go.mod h1:1ZuVZpjSpCKUgjLx8P6jzkkQFxJI1thUKr6yKV3p0vY=
-gitlab.com/gitlab-org/labkit v1.14.0/go.mod h1:bcxc4ZpAC+WyACgyKl7FcvT2XXAbl8CrzN6UY+w8cMc=
-gitlab.com/gitlab-org/labkit v1.15.0/go.mod h1:bcxc4ZpAC+WyACgyKl7FcvT2XXAbl8CrzN6UY+w8cMc=
gitlab.com/gitlab-org/labkit v1.16.0 h1:Vm3NAMZ8RqAunXlvPWby3GJ2R35vsYGP6Uu0YjyMIlY=
gitlab.com/gitlab-org/labkit v1.16.0/go.mod h1:bcxc4ZpAC+WyACgyKl7FcvT2XXAbl8CrzN6UY+w8cMc=
-go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
-go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
+go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
+go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
+go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0=
-go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
-go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -1164,7 +975,6 @@ go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
@@ -1177,11 +987,10 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
-go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
+go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
-gocloud.dev v0.23.0/go.mod h1:zklCCIIo1N9ELkU2S2E7tW8P8eeMU7oGLeQCXdDwx9Q=
-gocloud.dev v0.25.0 h1:Y7vDq8xj7SyM848KXf32Krda2e6jQ4CLh/mTeCSqXtk=
-gocloud.dev v0.25.0/go.mod h1:7HegHVCYZrMiU3IE1qtnzf/vRrDwLYnRNR3EhWX8x9Y=
+gocloud.dev v0.26.0 h1:4rM/SVL0lLs+rhC0Gmc+gt/82DBpb7nbpIZKXXnfMXg=
+gocloud.dev v0.26.0/go.mod h1:mkUgejbnbLotorqDyvedJO20XcZNTynmSeVSQS9btVg=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -1189,33 +998,28 @@ golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
-golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
-golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211115234514-b4de73f9ece8/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
-golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/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/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1236,9 +1040,9 @@ golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 h1:/eM0PCrQI2xd471rI+snWuu251/+/jpBpZqir2mPdnU=
+golang.org/x/image v0.0.0-20220722155232-062f8c9fd539/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -1270,12 +1074,9 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190310074541-c10a0554eabf/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -1290,7 +1091,6 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191112182307-2180aed22343/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -1300,7 +1100,6 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
@@ -1318,11 +1117,10 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20210420210106-798c2154c571/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210505214959-0714010a04ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -1332,8 +1130,9 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220401154927-543a649e0bdd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220531201128-c960675eff93 h1:MYimHLfoXEpOhqd/zgoA/uoXzHB86AEky4LAx5ij9xA=
golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1347,7 +1146,7 @@ golang.org/x/oauth2 v0.0.0-20210126194326-f9ce19ea3013/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
@@ -1370,16 +1169,15 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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 h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
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/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=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1390,33 +1188,28 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1435,20 +1228,14 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210223095934-7937bea0104d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210412220455-f1c623a9e750/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1457,13 +1244,13 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211102192858-4dd72447c267/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1477,9 +1264,9 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -1494,21 +1281,16 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/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 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U=
golang.org/x/time v0.0.0-20220609170525-579cf78fd858/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-20180525024113-a5b4c53f6e8b/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=
-golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1531,12 +1313,10 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190829051458-42f498d34c4d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -1578,9 +1358,10 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
-golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
-golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
+golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1594,13 +1375,10 @@ gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
google.golang.org/api v0.0.0-20170921000349-586095a6e407/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
-google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
@@ -1619,7 +1397,7 @@ google.golang.org/api v0.37.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
-google.golang.org/api v0.45.0/go.mod h1:ISLIJCedJolbZvDfAk+Ctuq5hf+aJ33WgtUsfyFoLXA=
+google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
google.golang.org/api v0.46.0/go.mod h1:ceL4oozhkAiTID8XMmJBsIxID/9wMXJVVFXPg4ylg3I=
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
@@ -1643,23 +1421,19 @@ google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc
google.golang.org/api v0.74.0 h1:ExR2D+5TYIrMphWgs5JCgwRhEDlPDXXrLwHHMgPHTXE=
google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20170918111702-1e559d0a00ee/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
@@ -1699,11 +1473,7 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
-google.golang.org/genproto v0.0.0-20210413151531-c14fb6ef47c3/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
-google.golang.org/genproto v0.0.0-20210420162539-3c870d7478d2/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
-google.golang.org/genproto v0.0.0-20210423144448-3a41ef94ed2b/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210429181445-86c259c2b4ab/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
-google.golang.org/genproto v0.0.0-20210506142907-4a47615972c2/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210517163617-5e0236093d7a/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
@@ -1747,17 +1517,11 @@ google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2I
google.golang.org/genproto v0.0.0-20220401170504-314d38edb7de h1:9Ti5SG2U4cAcluryUo/sFay3TQKoxiFMfaT0pbizU7k=
google.golang.org/genproto v0.0.0-20220401170504-314d38edb7de/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
-google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
-google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
@@ -1782,9 +1546,9 @@ google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
-google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
+google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw=
+google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
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=
@@ -1802,8 +1566,6 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-gopkg.in/DataDog/dd-trace-go.v1 v1.7.0/go.mod h1:DVp8HmDh8PuTu2Z0fVVlBsyWaC++fzwVCaGWylTe3tg=
-gopkg.in/DataDog/dd-trace-go.v1 v1.30.0/go.mod h1:SnKViq44dv/0gjl9RpkP0Y2G3BJSRkp6eYdCSu39iI8=
gopkg.in/DataDog/dd-trace-go.v1 v1.32.0 h1:DkD0plWEVUB8v/Ru6kRBW30Hy/fRNBC8hPdcExuBZMc=
gopkg.in/DataDog/dd-trace-go.v1 v1.32.0/go.mod h1:wRKMf/tRASHwH/UOfPQ3IQmVFhTz2/1a1/mpXoIjF54=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
@@ -1813,26 +1575,17 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
-gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
-gopkg.in/gorp.v1 v1.7.2/go.mod h1:Wo3h+DBQZIxATwftsglhdD/62zRFPhGhTiu5jUJmCaw=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo=
-gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q=
-gopkg.in/jcmturner/goidentity.v2 v2.0.0/go.mod h1:vCwK9HeXksMeUmQ4SxDd1tRz4LejrKh3KRVjQWhjvZI=
-gopkg.in/jcmturner/gokrb5.v5 v5.3.0/go.mod h1:oQz8Wc5GsctOTgCVyKad1Vw4TCWz5G6gfIQr88RPv4k=
-gopkg.in/jcmturner/rpc.v0 v0.0.2/go.mod h1:NzMq6cRzR9lipgw7WxRBHNx5N8SifBuaCQsOT1kWY/E=
+gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/neurosnap/sentences.v1 v1.0.6/go.mod h1:YlK+SN+fLQZj+kY3r8DkGDhDr91+S3JmTb5LSxFRQo0=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -1847,7 +1600,6 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -1857,11 +1609,8 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.3.3 h1:oDx7VAwstgpYpb3wv0oxiZlxY+foCpRAwY7Vk6XpAgA=
honnef.co/go/tools v0.3.3/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw=
-nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
-sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
-sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
diff --git a/workhorse/internal/git/pktline.go b/workhorse/internal/git/pktline.go
deleted file mode 100644
index e970f60182d..00000000000
--- a/workhorse/internal/git/pktline.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package git
-
-import (
- "bufio"
- "bytes"
- "fmt"
- "io"
- "strconv"
-)
-
-func scanDeepen(body io.Reader) bool {
- scanner := bufio.NewScanner(body)
- scanner.Split(pktLineSplitter)
- for scanner.Scan() {
- if bytes.HasPrefix(scanner.Bytes(), []byte("deepen")) && scanner.Err() == nil {
- return true
- }
- }
-
- return false
-}
-
-func pktLineSplitter(data []byte, atEOF bool) (advance int, token []byte, err error) {
- if len(data) < 4 {
- if atEOF && len(data) > 0 {
- return 0, nil, fmt.Errorf("pktLineSplitter: incomplete length prefix on %q", data)
- }
- return 0, nil, nil // want more data
- }
-
- if bytes.HasPrefix(data, []byte("0000")) {
- // special case: "0000" terminator packet: return empty token
- return 4, data[:0], nil
- }
-
- // We have at least 4 bytes available so we can decode the 4-hex digit
- // length prefix of the packet line.
- pktLength64, err := strconv.ParseInt(string(data[:4]), 16, 0)
- if err != nil {
- return 0, nil, fmt.Errorf("pktLineSplitter: decode length: %v", err)
- }
-
- // Cast is safe because we requested an int-size number from strconv.ParseInt
- pktLength := int(pktLength64)
-
- if pktLength < 0 {
- return 0, nil, fmt.Errorf("pktLineSplitter: invalid length: %d", pktLength)
- }
-
- if len(data) < pktLength {
- if atEOF {
- return 0, nil, fmt.Errorf("pktLineSplitter: less than %d bytes in input %q", pktLength, data)
- }
- return 0, nil, nil // want more data
- }
-
- // return "pkt" token without length prefix
- return pktLength, data[4:pktLength], nil
-}
diff --git a/workhorse/internal/git/pktline_test.go b/workhorse/internal/git/pktline_test.go
deleted file mode 100644
index d4be8634538..00000000000
--- a/workhorse/internal/git/pktline_test.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package git
-
-import (
- "bytes"
- "testing"
-)
-
-func TestSuccessfulScanDeepen(t *testing.T) {
- examples := []struct {
- input string
- output bool
- }{
- {"000dsomething000cdeepen 10000", true},
- {"000dsomething0000000cdeepen 1", true},
- {"000dsomething0000", false},
- }
-
- for _, example := range examples {
- hasDeepen := scanDeepen(bytes.NewReader([]byte(example.input)))
-
- if hasDeepen != example.output {
- t.Fatalf("scanDeepen %q: expected %v, got %v", example.input, example.output, hasDeepen)
- }
- }
-}
-
-func TestFailedScanDeepen(t *testing.T) {
- examples := []string{
- "invalid data",
- "deepen",
- "000cdeepen",
- }
-
- for _, example := range examples {
- if scanDeepen(bytes.NewReader([]byte(example))) {
- t.Fatalf("scanDeepen %q: expected result to be false, got true", example)
- }
- }
-}
diff --git a/workhorse/internal/headers/content_headers.go b/workhorse/internal/headers/content_headers.go
index 8cca3d97e82..854cc8abddd 100644
--- a/workhorse/internal/headers/content_headers.go
+++ b/workhorse/internal/headers/content_headers.go
@@ -43,6 +43,16 @@ const (
func SafeContentHeaders(data []byte, contentDisposition string) (string, string) {
contentType := safeContentType(data)
contentDisposition = safeContentDisposition(contentType, contentDisposition)
+
+ // Set attachments to application/octet-stream since browsers can do
+ // a better job distinguishing certain types (for example: ZIP files
+ // vs. Microsoft .docx files). However, browsers may safely render SVGs even
+ // when Content-Disposition is an attachment but only if the SVG
+ // Content-Type is set. Note that scripts in an SVG file will only be executed
+ // if the file is downloaded separately with an inline Content-Disposition.
+ if attachmentRegex.MatchString(contentDisposition) && !isType(contentType, svgMimeTypeRegex) {
+ contentType = "application/octet-stream"
+ }
return contentType, contentDisposition
}
diff --git a/workhorse/internal/lsif_transformer/parser/code_hover.go b/workhorse/internal/lsif_transformer/parser/code_hover.go
index 25550cce29e..ab3ab291432 100644
--- a/workhorse/internal/lsif_transformer/parser/code_hover.go
+++ b/workhorse/internal/lsif_transformer/parser/code_hover.go
@@ -28,6 +28,16 @@ type truncatableString struct {
Truncated bool
}
+// supportedLexerLanguages is used for a fast lookup to ensure the language
+// is supported by the lexer library.
+var supportedLexerLanguages = map[string]struct{}{}
+
+func init() {
+ for _, name := range lexers.Names(true) {
+ supportedLexerLanguages[name] = struct{}{}
+ }
+}
+
func (ts *truncatableString) UnmarshalText(b []byte) error {
s := 0
for i := 0; s < len(b); i++ {
@@ -93,6 +103,24 @@ func newCodeHover(content json.RawMessage) (*codeHover, error) {
}
func (c *codeHover) setTokens() {
+ // fastpath: bail early if no language specified
+ if c.Language == "" {
+ return
+ }
+
+ // fastpath: lexer.Get() will first match against indexed languages by
+ // name and alias, and then fallback to a very slow filepath match. We
+ // avoid this slow path by first checking against languages we know to
+ // be within the index, and bailing if not found.
+ //
+ // Not case-folding immediately is done intentionally. These two lookups
+ // mirror the behaviour of lexer.Get().
+ if _, ok := supportedLexerLanguages[c.Language]; !ok {
+ if _, ok := supportedLexerLanguages[strings.ToLower(c.Language)]; !ok {
+ return
+ }
+ }
+
lexer := lexers.Get(c.Language)
if lexer == nil {
return
diff --git a/workhorse/internal/lsif_transformer/parser/code_hover_test.go b/workhorse/internal/lsif_transformer/parser/code_hover_test.go
index c09636b2f76..7dc9e126ae7 100644
--- a/workhorse/internal/lsif_transformer/parser/code_hover_test.go
+++ b/workhorse/internal/lsif_transformer/parser/code_hover_test.go
@@ -56,6 +56,14 @@ func TestHighlight(t *testing.T) {
},
},
{
+ name: "ruby by file extension",
+ language: "rb",
+ value: `print hello`,
+ want: [][]token{
+ {{Value: "print hello"}},
+ },
+ },
+ {
name: "unknown/malicious language is passed",
language: "<lang> alert(1); </lang>",
value: `def a;\nend`,
@@ -116,3 +124,43 @@ func TestTruncatingMultiByteChars(t *testing.T) {
symbolSize := 3
require.Equal(t, value[0:maxValueSize*symbolSize], c.TruncatedValue.Value)
}
+
+func BenchmarkHighlight(b *testing.B) {
+ type entry struct {
+ Language string `json:"language"`
+ Value string `json:"value"`
+ }
+
+ tests := []entry{
+ {
+ Language: "go",
+ Value: "func main()",
+ },
+ {
+ Language: "ruby",
+ Value: "def read(line)",
+ },
+ {
+ Language: "",
+ Value: "<html><head>foobar</head></html>",
+ },
+ {
+ Language: "zzz",
+ Value: "def read(line)",
+ },
+ }
+
+ for _, tc := range tests {
+ b.Run("lang:"+tc.Language, func(b *testing.B) {
+ raw, err := json.Marshal(tc)
+ require.NoError(b, err)
+
+ b.ResetTimer()
+
+ for n := 0; n < b.N; n++ {
+ _, err := newCodeHovers(raw)
+ require.NoError(b, err)
+ }
+ })
+ }
+}
diff --git a/workhorse/internal/redis/keywatcher.go b/workhorse/internal/redis/keywatcher.go
index 82cb082f5f0..cdf6ccd7e83 100644
--- a/workhorse/internal/redis/keywatcher.go
+++ b/workhorse/internal/redis/keywatcher.go
@@ -15,61 +15,99 @@ import (
"gitlab.com/gitlab-org/gitlab/workhorse/internal/log"
)
-var (
- keyWatcher = make(map[string][]chan string)
- keyWatcherMutex sync.Mutex
- shutdown = make(chan struct{})
- redisReconnectTimeout = backoff.Backoff{
- //These are the defaults
- Min: 100 * time.Millisecond,
- Max: 60 * time.Second,
- Factor: 2,
- Jitter: true,
+type KeyWatcher struct {
+ mu sync.Mutex
+ subscribers map[string][]chan string
+ shutdown chan struct{}
+ reconnectBackoff backoff.Backoff
+ conn *redis.PubSubConn
+}
+
+func NewKeyWatcher() *KeyWatcher {
+ return &KeyWatcher{
+ shutdown: make(chan struct{}),
+ reconnectBackoff: backoff.Backoff{
+ Min: 100 * time.Millisecond,
+ Max: 60 * time.Second,
+ Factor: 2,
+ Jitter: true,
+ },
}
+}
+
+var (
keyWatchers = promauto.NewGauge(
prometheus.GaugeOpts{
Name: "gitlab_workhorse_keywatcher_keywatchers",
Help: "The number of keys that is being watched by gitlab-workhorse",
},
)
+ redisSubscriptions = promauto.NewGauge(
+ prometheus.GaugeOpts{
+ Name: "gitlab_workhorse_keywatcher_redis_subscriptions",
+ Help: "Current number of keywatcher Redis pubsub subscriptions",
+ },
+ )
totalMessages = promauto.NewCounter(
prometheus.CounterOpts{
Name: "gitlab_workhorse_keywatcher_total_messages",
Help: "How many messages gitlab-workhorse has received in total on pubsub.",
},
)
+ totalActions = promauto.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_keywatcher_actions_total",
+ Help: "Counts of various keywatcher actions",
+ },
+ []string{"action"},
+ )
+ receivedBytes = promauto.NewCounter(
+ prometheus.CounterOpts{
+ Name: "gitlab_workhorse_keywatcher_received_bytes_total",
+ Help: "How many bytes of messages gitlab-workhorse has received in total on pubsub.",
+ },
+ )
)
-const (
- keySubChannel = "workhorse:notifications"
-)
+const channelPrefix = "workhorse:notifications:"
-// KeyChan holds a key and a channel
-type KeyChan struct {
- Key string
- Chan chan string
-}
+func countAction(action string) { totalActions.WithLabelValues(action).Add(1) }
-func processInner(conn redis.Conn) error {
- defer conn.Close()
- psc := redis.PubSubConn{Conn: conn}
- if err := psc.Subscribe(keySubChannel); err != nil {
- return err
- }
- defer psc.Unsubscribe(keySubChannel)
+func (kw *KeyWatcher) receivePubSubStream(conn redis.Conn) error {
+ kw.mu.Lock()
+ // We must share kw.conn with the goroutines that call SUBSCRIBE and
+ // UNSUBSCRIBE because Redis pubsub subscriptions are tied to the
+ // connection.
+ kw.conn = &redis.PubSubConn{Conn: conn}
+ kw.mu.Unlock()
+
+ defer func() {
+ kw.mu.Lock()
+ defer kw.mu.Unlock()
+ kw.conn.Close()
+ kw.conn = nil
+
+ // Reset kw.subscribers because it is tied to Redis server side state of
+ // kw.conn and we just closed that connection.
+ for _, chans := range kw.subscribers {
+ for _, ch := range chans {
+ close(ch)
+ keyWatchers.Dec()
+ }
+ }
+ kw.subscribers = nil
+ }()
for {
- switch v := psc.Receive().(type) {
+ switch v := kw.conn.Receive().(type) {
case redis.Message:
totalMessages.Inc()
- dataStr := string(v.Data)
- msg := strings.SplitN(dataStr, "=", 2)
- if len(msg) != 2 {
- log.WithError(fmt.Errorf("keywatcher: invalid notification: %q", dataStr)).Error()
- continue
+ receivedBytes.Add(float64(len(v.Data)))
+ if strings.HasPrefix(v.Channel, channelPrefix) {
+ kw.notifySubscribers(v.Channel[len(channelPrefix):], string(v.Data))
}
- key, value := msg[0], msg[1]
- notifyChanWatchers(key, value)
+ case redis.Subscription:
+ redisSubscriptions.Set(float64(v.Count))
case error:
log.WithError(fmt.Errorf("keywatcher: pubsub receive: %v", v)).Error()
// Intermittent error, return nil so that it doesn't wait before reconnect
@@ -94,72 +132,106 @@ func dialPubSub(dialer redisDialerFunc) (redis.Conn, error) {
return conn, nil
}
-// Process redis subscriptions
-//
-// NOTE: There Can Only Be One!
-func Process() {
+func (kw *KeyWatcher) Process() {
log.Info("keywatcher: starting process loop")
for {
conn, err := dialPubSub(workerDialFunc)
if err != nil {
log.WithError(fmt.Errorf("keywatcher: %v", err)).Error()
- time.Sleep(redisReconnectTimeout.Duration())
+ time.Sleep(kw.reconnectBackoff.Duration())
continue
}
- redisReconnectTimeout.Reset()
+ kw.reconnectBackoff.Reset()
- if err = processInner(conn); err != nil {
- log.WithError(fmt.Errorf("keywatcher: process loop: %v", err)).Error()
+ if err = kw.receivePubSubStream(conn); err != nil {
+ log.WithError(fmt.Errorf("keywatcher: receivePubSubStream: %v", err)).Error()
}
}
}
-func Shutdown() {
+func (kw *KeyWatcher) Shutdown() {
log.Info("keywatcher: shutting down")
- keyWatcherMutex.Lock()
- defer keyWatcherMutex.Unlock()
+ kw.mu.Lock()
+ defer kw.mu.Unlock()
select {
- case <-shutdown:
+ case <-kw.shutdown:
// already closed
default:
- close(shutdown)
+ close(kw.shutdown)
}
}
-func notifyChanWatchers(key, value string) {
- keyWatcherMutex.Lock()
- defer keyWatcherMutex.Unlock()
- if chanList, ok := keyWatcher[key]; ok {
- for _, c := range chanList {
- c <- value
- keyWatchers.Dec()
+func (kw *KeyWatcher) notifySubscribers(key, value string) {
+ kw.mu.Lock()
+ defer kw.mu.Unlock()
+
+ chanList, ok := kw.subscribers[key]
+ if !ok {
+ countAction("drop-message")
+ return
+ }
+
+ countAction("deliver-message")
+ for _, c := range chanList {
+ select {
+ case c <- value:
+ default:
}
- delete(keyWatcher, key)
}
}
-func addKeyChan(kc *KeyChan) {
- keyWatcherMutex.Lock()
- defer keyWatcherMutex.Unlock()
- keyWatcher[kc.Key] = append(keyWatcher[kc.Key], kc.Chan)
+func (kw *KeyWatcher) addSubscription(key string, notify chan string) error {
+ kw.mu.Lock()
+ defer kw.mu.Unlock()
+
+ if kw.conn == nil {
+ // This can happen because CI long polling is disabled in this Workhorse
+ // process. It can also be that we are waiting for the pubsub connection
+ // to be established. Either way it is OK to fail fast.
+ return errors.New("no redis connection")
+ }
+
+ if len(kw.subscribers[key]) == 0 {
+ countAction("create-subscription")
+ if err := kw.conn.Subscribe(channelPrefix + key); err != nil {
+ return err
+ }
+ }
+
+ if kw.subscribers == nil {
+ kw.subscribers = make(map[string][]chan string)
+ }
+ kw.subscribers[key] = append(kw.subscribers[key], notify)
keyWatchers.Inc()
+
+ return nil
}
-func delKeyChan(kc *KeyChan) {
- keyWatcherMutex.Lock()
- defer keyWatcherMutex.Unlock()
- if chans, ok := keyWatcher[kc.Key]; ok {
- for i, c := range chans {
- if kc.Chan == c {
- keyWatcher[kc.Key] = append(chans[:i], chans[i+1:]...)
- keyWatchers.Dec()
- break
- }
+func (kw *KeyWatcher) delSubscription(key string, notify chan string) {
+ kw.mu.Lock()
+ defer kw.mu.Unlock()
+
+ chans, ok := kw.subscribers[key]
+ if !ok {
+ // This can happen if the pubsub connection dropped while we were
+ // waiting.
+ return
+ }
+
+ for i, c := range chans {
+ if notify == c {
+ kw.subscribers[key] = append(chans[:i], chans[i+1:]...)
+ keyWatchers.Dec()
+ break
}
- if len(keyWatcher[kc.Key]) == 0 {
- delete(keyWatcher, kc.Key)
+ }
+ if len(kw.subscribers[key]) == 0 {
+ delete(kw.subscribers, key)
+ countAction("delete-subscription")
+ if kw.conn != nil {
+ kw.conn.Unsubscribe(channelPrefix + key)
}
}
}
@@ -179,15 +251,12 @@ const (
WatchKeyStatusNoChange
)
-// WatchKey waits for a key to be updated or expired
-func WatchKey(key, value string, timeout time.Duration) (WatchKeyStatus, error) {
- kw := &KeyChan{
- Key: key,
- Chan: make(chan string, 1),
+func (kw *KeyWatcher) WatchKey(key, value string, timeout time.Duration) (WatchKeyStatus, error) {
+ notify := make(chan string, 1)
+ if err := kw.addSubscription(key, notify); err != nil {
+ return WatchKeyStatusNoChange, err
}
-
- addKeyChan(kw)
- defer delKeyChan(kw)
+ defer kw.delSubscription(key, notify)
currentValue, err := GetString(key)
if errors.Is(err, redis.ErrNil) {
@@ -200,10 +269,10 @@ func WatchKey(key, value string, timeout time.Duration) (WatchKeyStatus, error)
}
select {
- case <-shutdown:
+ case <-kw.shutdown:
log.WithFields(log.Fields{"key": key}).Info("stopping watch due to shutdown")
return WatchKeyStatusNoChange, nil
- case currentValue := <-kw.Chan:
+ case currentValue := <-notify:
if currentValue == "" {
return WatchKeyStatusNoChange, fmt.Errorf("keywatcher: redis GET failed")
}
@@ -211,7 +280,6 @@ func WatchKey(key, value string, timeout time.Duration) (WatchKeyStatus, error)
return WatchKeyStatusNoChange, nil
}
return WatchKeyStatusSeenChange, nil
-
case <-time.After(timeout):
return WatchKeyStatusTimeout, nil
}
diff --git a/workhorse/internal/redis/keywatcher_test.go b/workhorse/internal/redis/keywatcher_test.go
index 29041226b14..bae49d81bb1 100644
--- a/workhorse/internal/redis/keywatcher_test.go
+++ b/workhorse/internal/redis/keywatcher_test.go
@@ -1,7 +1,6 @@
package redis
import (
- "errors"
"sync"
"testing"
"time"
@@ -38,33 +37,38 @@ func createUnsubscribeMessage(key string) []interface{} {
}
}
-func countWatchers(key string) int {
- keyWatcherMutex.Lock()
- defer keyWatcherMutex.Unlock()
- return len(keyWatcher[key])
-}
-
-func deleteWatchers(key string) {
- keyWatcherMutex.Lock()
- defer keyWatcherMutex.Unlock()
- delete(keyWatcher, key)
+func (kw *KeyWatcher) countSubscribers(key string) int {
+ kw.mu.Lock()
+ defer kw.mu.Unlock()
+ return len(kw.subscribers[key])
}
// Forces a run of the `Process` loop against a mock PubSubConn.
-func processMessages(numWatchers int, value string) {
+func (kw *KeyWatcher) processMessages(t *testing.T, numWatchers int, value string, ready chan<- struct{}) {
psc := redigomock.NewConn()
+ psc.ReceiveWait = true
- // Setup the initial subscription message
- psc.Command("SUBSCRIBE", keySubChannel).Expect(createSubscribeMessage(keySubChannel))
- psc.Command("UNSUBSCRIBE", keySubChannel).Expect(createUnsubscribeMessage(keySubChannel))
- psc.AddSubscriptionMessage(createSubscriptionMessage(keySubChannel, runnerKey+"="+value))
+ channel := channelPrefix + runnerKey
+ psc.Command("SUBSCRIBE", channel).Expect(createSubscribeMessage(channel))
+ psc.Command("UNSUBSCRIBE", channel).Expect(createUnsubscribeMessage(channel))
+ psc.AddSubscriptionMessage(createSubscriptionMessage(channel, value))
- // Wait for all the `WatchKey` calls to be registered
- for countWatchers(runnerKey) != numWatchers {
- time.Sleep(time.Millisecond)
- }
+ errC := make(chan error)
+ go func() { errC <- kw.receivePubSubStream(psc) }()
+
+ require.Eventually(t, func() bool {
+ kw.mu.Lock()
+ defer kw.mu.Unlock()
+ return kw.conn != nil
+ }, time.Second, time.Millisecond)
+ close(ready)
+
+ require.Eventually(t, func() bool {
+ return kw.countSubscribers(runnerKey) == numWatchers
+ }, time.Second, time.Millisecond)
+ close(psc.ReceiveNow)
- processInner(psc)
+ require.NoError(t, <-errC)
}
type keyChangeTestCase struct {
@@ -77,18 +81,6 @@ type keyChangeTestCase struct {
timeout time.Duration
}
-func TestKeyChangesBubblesUpError(t *testing.T) {
- conn, td := setupMockPool()
- defer td()
-
- conn.Command("GET", runnerKey).ExpectError(errors.New("test error"))
-
- _, err := WatchKey(runnerKey, "something", time.Second)
- require.Error(t, err, "Expected error")
-
- deleteWatchers(runnerKey)
-}
-
func TestKeyChangesInstantReturn(t *testing.T) {
testCases := []keyChangeTestCase{
// WatchKeyStatusAlreadyChanged
@@ -135,12 +127,14 @@ func TestKeyChangesInstantReturn(t *testing.T) {
conn.Command("GET", runnerKey).Expect(tc.returnValue)
}
- val, err := WatchKey(runnerKey, tc.watchValue, tc.timeout)
+ kw := NewKeyWatcher()
+ defer kw.Shutdown()
+ kw.conn = &redis.PubSubConn{Conn: redigomock.NewConn()}
+
+ val, err := kw.WatchKey(runnerKey, tc.watchValue, tc.timeout)
require.NoError(t, err, "Expected no error")
require.Equal(t, tc.expectedStatus, val, "Expected value")
-
- deleteWatchers(runnerKey)
})
}
}
@@ -183,18 +177,23 @@ func TestKeyChangesWhenWatching(t *testing.T) {
conn.Command("GET", runnerKey).Expect(tc.returnValue)
}
+ kw := NewKeyWatcher()
+ defer kw.Shutdown()
+
wg := &sync.WaitGroup{}
wg.Add(1)
+ ready := make(chan struct{})
go func() {
defer wg.Done()
- val, err := WatchKey(runnerKey, tc.watchValue, time.Second)
+ <-ready
+ val, err := kw.WatchKey(runnerKey, tc.watchValue, time.Second)
require.NoError(t, err, "Expected no error")
require.Equal(t, tc.expectedStatus, val, "Expected value")
}()
- processMessages(1, tc.processedValue)
+ kw.processMessages(t, 1, tc.processedValue, ready)
wg.Wait()
})
}
@@ -237,18 +236,23 @@ func TestKeyChangesParallel(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(runTimes)
+ ready := make(chan struct{})
+
+ kw := NewKeyWatcher()
+ defer kw.Shutdown()
for i := 0; i < runTimes; i++ {
go func() {
defer wg.Done()
- val, err := WatchKey(runnerKey, tc.watchValue, time.Second)
+ <-ready
+ val, err := kw.WatchKey(runnerKey, tc.watchValue, time.Second)
require.NoError(t, err, "Expected no error")
require.Equal(t, tc.expectedStatus, val, "Expected value")
}()
}
- processMessages(runTimes, tc.processedValue)
+ kw.processMessages(t, runTimes, tc.processedValue, ready)
wg.Wait()
})
}
@@ -257,7 +261,10 @@ func TestKeyChangesParallel(t *testing.T) {
func TestShutdown(t *testing.T) {
conn, td := setupMockPool()
defer td()
- defer func() { shutdown = make(chan struct{}) }()
+
+ kw := NewKeyWatcher()
+ kw.conn = &redis.PubSubConn{Conn: redigomock.NewConn()}
+ defer kw.Shutdown()
conn.Command("GET", runnerKey).Expect("something")
@@ -265,30 +272,30 @@ func TestShutdown(t *testing.T) {
wg.Add(2)
go func() {
- val, err := WatchKey(runnerKey, "something", 10*time.Second)
+ defer wg.Done()
+ val, err := kw.WatchKey(runnerKey, "something", 10*time.Second)
require.NoError(t, err, "Expected no error")
require.Equal(t, WatchKeyStatusNoChange, val, "Expected value not to change")
- wg.Done()
}()
go func() {
- require.Eventually(t, func() bool { return countWatchers(runnerKey) == 1 }, 10*time.Second, time.Millisecond)
+ defer wg.Done()
+ require.Eventually(t, func() bool { return kw.countSubscribers(runnerKey) == 1 }, 10*time.Second, time.Millisecond)
- Shutdown()
- wg.Done()
+ kw.Shutdown()
}()
wg.Wait()
- require.Eventually(t, func() bool { return countWatchers(runnerKey) == 0 }, 10*time.Second, time.Millisecond)
+ require.Eventually(t, func() bool { return kw.countSubscribers(runnerKey) == 0 }, 10*time.Second, time.Millisecond)
// Adding a key after the shutdown should result in an immediate response
var val WatchKeyStatus
var err error
done := make(chan struct{})
go func() {
- val, err = WatchKey(runnerKey, "something", 10*time.Second)
+ val, err = kw.WatchKey(runnerKey, "something", 10*time.Second)
close(done)
}()
diff --git a/workhorse/internal/senddata/contentprocessor/contentprocessor_test.go b/workhorse/internal/senddata/contentprocessor/contentprocessor_test.go
index ce7f7921589..b04263de6b9 100644
--- a/workhorse/internal/senddata/contentprocessor/contentprocessor_test.go
+++ b/workhorse/internal/senddata/contentprocessor/contentprocessor_test.go
@@ -86,7 +86,7 @@ func TestSetProperContentTypeAndDisposition(t *testing.T) {
},
{
desc: "Application type",
- contentType: "application/pdf",
+ contentType: "application/octet-stream",
contentDisposition: "attachment",
body: testhelper.LoadFile(t, "testdata/file.pdf"),
},
@@ -110,7 +110,7 @@ func TestSetProperContentTypeAndDisposition(t *testing.T) {
},
{
desc: "Audio type",
- contentType: "audio/mpeg",
+ contentType: "application/octet-stream",
contentDisposition: "attachment",
body: testhelper.LoadFile(t, "testdata/audio.mp3"),
},
@@ -152,16 +152,22 @@ func TestSetProperContentTypeAndDisposition(t *testing.T) {
},
{
desc: "Sketch file",
- contentType: "application/zip",
+ contentType: "application/octet-stream",
contentDisposition: "attachment",
body: testhelper.LoadFile(t, "testdata/file.sketch"),
},
{
desc: "PDF file with non-ASCII characters in filename",
- contentType: "application/pdf",
+ contentType: "application/octet-stream",
contentDisposition: `attachment; filename="file-ä.pdf"; filename*=UTF-8''file-%c3.pdf`,
body: testhelper.LoadFile(t, "testdata/file-ä.pdf"),
},
+ {
+ desc: "Microsoft Word file",
+ contentType: "application/octet-stream",
+ contentDisposition: `attachment`,
+ body: testhelper.LoadFile(t, "testdata/file.docx"),
+ },
}
for _, tc := range testCases {
diff --git a/workhorse/internal/upload/destination/objectstore/uploader.go b/workhorse/internal/upload/destination/objectstore/uploader.go
index aedfbe55ead..43e573872ee 100644
--- a/workhorse/internal/upload/destination/objectstore/uploader.go
+++ b/workhorse/internal/upload/destination/objectstore/uploader.go
@@ -80,13 +80,13 @@ func (u *uploader) Consume(outerCtx context.Context, reader io.Reader, deadline
cr := &countReader{r: reader}
if err := u.strategy.Upload(uploadCtx, cr); err != nil {
- return cr.n, err
+ return 0, err
}
if u.checkETag {
if err := compareMD5(hexString(hasher), u.strategy.ETag()); err != nil {
log.ContextLogger(uploadCtx).WithError(err).Error("error comparing MD5 checksum")
- return cr.n, err
+ return 0, err
}
}
diff --git a/workhorse/internal/upstream/metrics_test.go b/workhorse/internal/upstream/metrics_test.go
index 29a9e09777c..dff849ac214 100644
--- a/workhorse/internal/upstream/metrics_test.go
+++ b/workhorse/internal/upstream/metrics_test.go
@@ -26,7 +26,7 @@ func TestInstrumentGeoProxyRoute(t *testing.T) {
handleRouteWithMatchers(u, local),
handleRouteWithMatchers(u, main),
}
- })
+ }, nil)
ts := httptest.NewServer(u)
defer ts.Close()
diff --git a/workhorse/internal/upstream/routes.go b/workhorse/internal/upstream/routes.go
index c889f87ed96..40cd012a890 100644
--- a/workhorse/internal/upstream/routes.go
+++ b/workhorse/internal/upstream/routes.go
@@ -21,7 +21,6 @@ import (
"gitlab.com/gitlab-org/gitlab/workhorse/internal/imageresizer"
proxypkg "gitlab.com/gitlab-org/gitlab/workhorse/internal/proxy"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/queueing"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/redis"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/secret"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/senddata"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/sendfile"
@@ -51,7 +50,7 @@ const (
gitProjectPattern = `^/.+\.git/`
geoGitProjectPattern = `^/[^-].+\.git/` // Prevent matching routes like /-/push_from_secondary
projectPattern = `^/([^/]+/){1,}[^/]+/`
- apiProjectPattern = apiPattern + `v4/projects/[^/]+/` // API: Projects can be encoded via group%2Fsubgroup%2Fproject
+ apiProjectPattern = apiPattern + `v4/projects/[^/]+` // API: Projects can be encoded via group%2Fsubgroup%2Fproject
apiTopicPattern = apiPattern + `v4/topics`
snippetUploadPattern = `^/uploads/personal_snippet`
userUploadPattern = `^/uploads/user`
@@ -223,7 +222,7 @@ func configureRoutes(u *upstream) {
tempfileMultipartProxy := upload.FixedPreAuthMultipart(api, proxy, preparer)
ciAPIProxyQueue := queueing.QueueRequests("ci_api_job_requests", tempfileMultipartProxy, u.APILimit, u.APIQueueLimit, u.APIQueueTimeout)
- ciAPILongPolling := builds.RegisterHandler(ciAPIProxyQueue, redis.WatchKey, u.APICILongPollingDuration)
+ ciAPILongPolling := builds.RegisterHandler(ciAPIProxyQueue, u.watchKeyHandler, u.APICILongPollingDuration)
dependencyProxyInjector.SetUploadHandler(requestBodyUploader)
@@ -269,37 +268,40 @@ func configureRoutes(u *upstream) {
// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56731.
// Maven Artifact Repository
- u.route("PUT", apiProjectPattern+`packages/maven/`, requestBodyUploader),
+ u.route("PUT", apiProjectPattern+`/packages/maven/`, requestBodyUploader),
// Conan Artifact Repository
u.route("PUT", apiPattern+`v4/packages/conan/`, requestBodyUploader),
- u.route("PUT", apiProjectPattern+`packages/conan/`, requestBodyUploader),
+ u.route("PUT", apiProjectPattern+`/packages/conan/`, requestBodyUploader),
// Generic Packages Repository
- u.route("PUT", apiProjectPattern+`packages/generic/`, requestBodyUploader),
+ u.route("PUT", apiProjectPattern+`/packages/generic/`, requestBodyUploader),
// NuGet Artifact Repository
- u.route("PUT", apiProjectPattern+`packages/nuget/`, mimeMultipartUploader),
+ u.route("PUT", apiProjectPattern+`/packages/nuget/`, mimeMultipartUploader),
// PyPI Artifact Repository
- u.route("POST", apiProjectPattern+`packages/pypi`, mimeMultipartUploader),
+ u.route("POST", apiProjectPattern+`/packages/pypi`, mimeMultipartUploader),
// Debian Artifact Repository
- u.route("PUT", apiProjectPattern+`packages/debian/`, requestBodyUploader),
+ u.route("PUT", apiProjectPattern+`/packages/debian/`, requestBodyUploader),
+
+ // RPM Artifact Repository
+ u.route("POST", apiProjectPattern+`/packages/rpm/`, requestBodyUploader),
// Gem Artifact Repository
- u.route("POST", apiProjectPattern+`packages/rubygems/`, requestBodyUploader),
+ u.route("POST", apiProjectPattern+`/packages/rubygems/`, requestBodyUploader),
// Terraform Module Package Repository
- u.route("PUT", apiProjectPattern+`packages/terraform/modules/`, requestBodyUploader),
+ u.route("PUT", apiProjectPattern+`/packages/terraform/modules/`, requestBodyUploader),
// Helm Artifact Repository
- u.route("POST", apiProjectPattern+`packages/helm/api/[^/]+/charts\z`, mimeMultipartUploader),
+ u.route("POST", apiProjectPattern+`/packages/helm/api/[^/]+/charts\z`, mimeMultipartUploader),
// We are porting API to disk acceleration
// we need to declare each routes until we have fixed all the routes on the rails codebase.
// Overall status can be seen at https://gitlab.com/groups/gitlab-org/-/epics/1802#current-status
- u.route("POST", apiProjectPattern+`wikis/attachments\z`, tempfileMultipartProxy),
+ u.route("POST", apiProjectPattern+`/wikis/attachments\z`, tempfileMultipartProxy),
u.route("POST", apiPattern+`graphql\z`, tempfileMultipartProxy),
u.route("POST", apiTopicPattern, tempfileMultipartProxy),
u.route("PUT", apiTopicPattern, tempfileMultipartProxy),
@@ -312,16 +314,28 @@ func configureRoutes(u *upstream) {
u.route("POST", importPattern+`gitlab_group`, mimeMultipartUploader),
// Issuable Metric image upload
- u.route("POST", apiProjectPattern+`issues/[0-9]+/metric_images\z`, mimeMultipartUploader),
+ u.route("POST", apiProjectPattern+`/issues/[0-9]+/metric_images\z`, mimeMultipartUploader),
// Alert Metric image upload
- u.route("POST", apiProjectPattern+`alert_management_alerts/[0-9]+/metric_images\z`, mimeMultipartUploader),
+ u.route("POST", apiProjectPattern+`/alert_management_alerts/[0-9]+/metric_images\z`, mimeMultipartUploader),
// Requirements Import via UI upload acceleration
u.route("POST", projectPattern+`requirements_management/requirements/import_csv`, mimeMultipartUploader),
// Uploads via API
- u.route("POST", apiProjectPattern+`uploads\z`, mimeMultipartUploader),
+ u.route("POST", apiProjectPattern+`/uploads\z`, mimeMultipartUploader),
+
+ // Project Avatar
+ u.route("POST", apiPattern+`v4/projects\z`, tempfileMultipartProxy),
+ u.route("PUT", apiProjectPattern+`\z`, tempfileMultipartProxy),
+
+ // Group Avatar
+ u.route("POST", apiPattern+`v4/groups\z`, tempfileMultipartProxy),
+ u.route("PUT", apiPattern+`v4/groups/[^/]+\z`, tempfileMultipartProxy),
+
+ // User Avatar
+ u.route("POST", apiPattern+`v4/users\z`, tempfileMultipartProxy),
+ u.route("PUT", apiPattern+`v4/users/[0-9]+\z`, tempfileMultipartProxy),
// Explicitly proxy API requests
u.route("", apiPattern, proxy),
diff --git a/workhorse/internal/upstream/upstream.go b/workhorse/internal/upstream/upstream.go
index 43b470b568f..248f190e316 100644
--- a/workhorse/internal/upstream/upstream.go
+++ b/workhorse/internal/upstream/upstream.go
@@ -21,6 +21,7 @@ import (
"gitlab.com/gitlab-org/labkit/correlation"
apipkg "gitlab.com/gitlab-org/gitlab/workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/builds"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/config"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
proxypkg "gitlab.com/gitlab-org/gitlab/workhorse/internal/proxy"
@@ -55,19 +56,21 @@ type upstream struct {
accessLogger *logrus.Logger
enableGeoProxyFeature bool
mu sync.RWMutex
+ watchKeyHandler builds.WatchKeyHandler
}
-func NewUpstream(cfg config.Config, accessLogger *logrus.Logger) http.Handler {
- return newUpstream(cfg, accessLogger, configureRoutes)
+func NewUpstream(cfg config.Config, accessLogger *logrus.Logger, watchKeyHandler builds.WatchKeyHandler) http.Handler {
+ return newUpstream(cfg, accessLogger, configureRoutes, watchKeyHandler)
}
-func newUpstream(cfg config.Config, accessLogger *logrus.Logger, routesCallback func(*upstream)) http.Handler {
+func newUpstream(cfg config.Config, accessLogger *logrus.Logger, routesCallback func(*upstream), watchKeyHandler builds.WatchKeyHandler) http.Handler {
up := upstream{
Config: cfg,
accessLogger: accessLogger,
// Kind of a feature flag. See https://gitlab.com/groups/gitlab-org/-/epics/5914#note_564974130
enableGeoProxyFeature: os.Getenv("GEO_SECONDARY_PROXY") != "0",
geoProxyBackend: &url.URL{},
+ watchKeyHandler: watchKeyHandler,
}
if up.geoProxyPollSleep == nil {
up.geoProxyPollSleep = time.Sleep
diff --git a/workhorse/internal/upstream/upstream_test.go b/workhorse/internal/upstream/upstream_test.go
index 21fa7b81fdb..7ab3e67116f 100644
--- a/workhorse/internal/upstream/upstream_test.go
+++ b/workhorse/internal/upstream/upstream_test.go
@@ -58,7 +58,7 @@ func TestRouting(t *testing.T) {
handle(u, quxbaz),
handle(u, main),
}
- })
+ }, nil)
ts := httptest.NewServer(u)
defer ts.Close()
@@ -415,7 +415,7 @@ func startWorkhorseServer(railsServerURL string, enableGeoProxyFeature bool) (*h
configureRoutes(u)
}
cfg := newUpstreamConfig(railsServerURL)
- upstreamHandler := newUpstream(*cfg, logrus.StandardLogger(), myConfigureRoutes)
+ upstreamHandler := newUpstream(*cfg, logrus.StandardLogger(), myConfigureRoutes, nil)
ws := httptest.NewServer(upstreamHandler)
waitForNextApiPoll := func() {}
diff --git a/workhorse/main.go b/workhorse/main.go
index 91008e16961..b0f9760b0d5 100644
--- a/workhorse/main.go
+++ b/workhorse/main.go
@@ -220,9 +220,10 @@ func run(boot bootConfig, cfg config.Config) error {
secret.SetPath(boot.secretPath)
+ keyWatcher := redis.NewKeyWatcher()
if cfg.Redis != nil {
redis.Configure(cfg.Redis, redis.DefaultDialFunc)
- go redis.Process()
+ go keyWatcher.Process()
}
if err := cfg.RegisterGoCloudURLOpeners(); err != nil {
@@ -237,7 +238,7 @@ func run(boot bootConfig, cfg config.Config) error {
gitaly.InitializeSidechannelRegistry(accessLogger)
- up := wrapRaven(upstream.NewUpstream(cfg, accessLogger))
+ up := wrapRaven(upstream.NewUpstream(cfg, accessLogger, keyWatcher.WatchKey))
done := make(chan os.Signal, 1)
signal.Notify(done, syscall.SIGINT, syscall.SIGTERM)
@@ -271,7 +272,7 @@ func run(boot bootConfig, cfg config.Config) error {
ctx, cancel := context.WithTimeout(context.Background(), cfg.ShutdownTimeout.Duration) // lint:allow context.Background
defer cancel()
- redis.Shutdown()
+ keyWatcher.Shutdown()
return srv.Shutdown(ctx)
}
}
diff --git a/workhorse/main_test.go b/workhorse/main_test.go
index 195b1166c75..5ebc26c7ac7 100644
--- a/workhorse/main_test.go
+++ b/workhorse/main_test.go
@@ -35,7 +35,7 @@ import (
)
const scratchDir = "testdata/scratch"
-const testRepoRoot = "testdata/data"
+const testRepoRoot = "testdata/repo"
const testDocumentRoot = "testdata/public"
const testAltDocumentRoot = "testdata/alt-public"
@@ -799,7 +799,7 @@ func startWorkhorseServer(authBackend string) *httptest.Server {
func startWorkhorseServerWithConfig(cfg *config.Config) *httptest.Server {
testhelper.ConfigureSecret()
- u := upstream.NewUpstream(*cfg, logrus.StandardLogger())
+ u := upstream.NewUpstream(*cfg, logrus.StandardLogger(), nil)
return httptest.NewServer(u)
}
diff --git a/workhorse/testdata/file.docx b/workhorse/testdata/file.docx
new file mode 100644
index 00000000000..7976343a631
--- /dev/null
+++ b/workhorse/testdata/file.docx
Binary files differ
diff --git a/workhorse/testdata/repo/group/test.git b/workhorse/testdata/repo/group/test.git
new file mode 120000
index 00000000000..5dcbd509206
--- /dev/null
+++ b/workhorse/testdata/repo/group/test.git
@@ -0,0 +1 @@
+../../../../spec/support/gitlab-git-test.git \ No newline at end of file
diff --git a/workhorse/upload_test.go b/workhorse/upload_test.go
index 9947059770f..c7e5888cec9 100644
--- a/workhorse/upload_test.go
+++ b/workhorse/upload_test.go
@@ -122,6 +122,10 @@ func TestAcceleratedUpload(t *testing.T) {
{"POST", `/example`, false},
{"POST", `/uploads/personal_snippet`, true},
{"POST", `/uploads/user`, true},
+ {"POST", `/api/v4/projects`, false},
+ {"PUT", `/api/v4/projects/group%2Fproject`, false},
+ {"PUT", `/api/v4/projects/group%2Fsubgroup%2Fproject`, false},
+ {"PUT", `/api/v4/projects/39`, false},
{"POST", `/api/v4/projects/1/uploads`, true},
{"POST", `/api/v4/projects/group%2Fproject/uploads`, true},
{"POST", `/api/v4/projects/group%2Fsubgroup%2Fproject/uploads`, true},
@@ -131,6 +135,11 @@ func TestAcceleratedUpload(t *testing.T) {
{"POST", `/api/graphql`, false},
{"POST", `/api/v4/topics`, false},
{"PUT", `/api/v4/topics`, false},
+ {"POST", `/api/v4/groups`, false},
+ {"PUT", `/api/v4/groups/5`, false},
+ {"PUT", `/api/v4/groups/group%2Fsubgroup`, false},
+ {"POST", `/api/v4/users`, false},
+ {"PUT", `/api/v4/users/42`, false},
{"PUT", "/api/v4/projects/9001/packages/nuget/v1/files", true},
{"PUT", "/api/v4/projects/group%2Fproject/packages/nuget/v1/files", true},
{"PUT", "/api/v4/projects/group%2Fsubgroup%2Fproject/packages/nuget/v1/files", true},
@@ -536,11 +545,13 @@ func TestPackageFilesUpload(t *testing.T) {
{"PUT", "/api/v4/projects/2412/packages/generic/mypackage/0.0.1/myfile.tar.gz"},
{"PUT", "/api/v4/projects/2412/packages/debian/libsample0_1.2.3~alpha2-1_amd64.deb"},
{"POST", "/api/v4/projects/2412/packages/rubygems/api/v1/gems/sample.gem"},
+ {"POST", "/api/v4/projects/2412/packages/rpm/sample-4.23.fc21.x86_64.rpm"},
{"PUT", "/api/v4/projects/group%2Fproject/packages/conan/v1/files"},
{"PUT", "/api/v4/projects/group%2Fproject/packages/maven/v1/files"},
{"PUT", "/api/v4/projects/group%2Fproject/packages/generic/mypackage/0.0.1/myfile.tar.gz"},
{"PUT", "/api/v4/projects/group%2Fproject/packages/debian/libsample0_1.2.3~alpha2-1_amd64.deb"},
{"POST", "/api/v4/projects/group%2Fproject/packages/rubygems/api/v1/gems/sample.gem"},
+ {"POST", "/api/v4/projects/group%2Fproject/packages/rpm/sample-4.23.fc21.x86_64.rpm"},
{"PUT", "/api/v4/projects/group%2Fproject/packages/terraform/modules/mymodule/mysystem/0.0.1/file"},
}
diff --git a/yarn.lock b/yarn.lock
index 1195c22adb5..a0e22985f97 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -303,9 +303,9 @@
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.5"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.5.tgz#337062363436a893a2d22faa60be5bb37091c83c"
- integrity sha512-YZWVaglMiplo7v8f1oMQ5ZPQr0vn7HPeZXxXWsxXJRjGVrzUFn9OxFQl1sb5wzfootjA/yChhW84BV+383FSOw==
+ version "7.18.11"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9"
+ integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.17.12":
version "7.17.12"
@@ -1007,14 +1007,14 @@
ts-node "^9"
tslib "^2"
-"@eslint/eslintrc@^1.3.0":
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f"
- integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==
+"@eslint/eslintrc@^1.3.2":
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.2.tgz#58b69582f3b7271d8fa67fe5251767a5b38ea356"
+ integrity sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==
dependencies:
ajv "^6.12.4"
debug "^4.3.2"
- espree "^9.3.2"
+ espree "^9.4.0"
globals "^13.15.0"
ignore "^5.2.0"
import-fresh "^3.2.1"
@@ -1027,10 +1027,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/at.js/-/at.js-1.5.7.tgz#1ee6f838cc4410a1d797770934df91d90df8179e"
integrity sha512-c6ySRK/Ma7lxwpIVbSAF3P+xiTLrNTGTLRx4/pHK111AdFxwgUwrYF6aVZFXvmG65jHOJHoa0eQQ21RW6rm0Rg==
-"@gitlab/eslint-plugin@16.0.0":
- version "16.0.0"
- resolved "https://registry.yarnpkg.com/@gitlab/eslint-plugin/-/eslint-plugin-16.0.0.tgz#83b71bb3f749c6e52138d2c1c17ac623e7b2e3db"
- integrity sha512-2n7geoRPkeMAq4GCqyvFzcTgcSrTM7pdCOxfcqIeuTmh/PFGhh+m7YC+YC4enhGOCN8lo08buLZhXkSgWiHSqA==
+"@gitlab/eslint-plugin@17.0.0":
+ version "17.0.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/eslint-plugin/-/eslint-plugin-17.0.0.tgz#5451fbbad96b09d812af2afb247f6602fe0be6c6"
+ integrity sha512-c+sJtjzYl+KGPtZScU8Mji9seJw7dSEn31APyYEYTyWp72yMsFvXmg46txT2QCz+ueZlqk0/C2IQmgfe6fLcBw==
dependencies:
"@babel/core" "^7.17.0"
"@babel/eslint-parser" "^7.17.0"
@@ -1059,19 +1059,19 @@
stylelint-declaration-strict-value "1.8.0"
stylelint-scss "4.2.0"
-"@gitlab/svgs@3.1.0":
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.1.0.tgz#0108498a17e2f79d16158015db0be764b406cc09"
- integrity sha512-kZ45VTQOgLdwQCLRSj7+aohF+6AUnAaoucR1CFY/6DPDLnNNGeflwsCLN0sFBKwx42HLxFfNwvDmKOMLdSQg5A==
+"@gitlab/svgs@3.3.0":
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.3.0.tgz#99b044484fcf3d5a6431281e320e2405540ff5a9"
+ integrity sha512-S8Hqf+ms8aNrSgmci9SVoIyj/0qQnizU5uV5vUPAOwiufMDFDyI5qfcgn4EYZ6mnju3LiO+ReSL/PPTD4qNgHA==
-"@gitlab/ui@43.6.0":
- version "43.6.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-43.6.0.tgz#95f85f405455aa95ee5b68b5072bcbe1bd22f375"
- integrity sha512-d0ebKUU7BBBCVoblEA6iWbD3BfR4t0YsDbC7UlN7nyR++MhojCmaML+L9MuDeJGpd0DWR0HLxAGWawtcdeZErw==
+"@gitlab/ui@43.16.0":
+ version "43.16.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-43.16.0.tgz#cb383b5ca95b7b3d3fbb5d134a71676050c0aa20"
+ integrity sha512-wdB2Mjto7UgLaeeW6mSmUiWW2xxhl98msB18kcDFqth2cJ59JHvA6eUydmt8Es6pJ9kKj7MLUiC64wEaNgpQBA==
dependencies:
"@popperjs/core" "^2.11.2"
bootstrap-vue "2.20.1"
- dompurify "^2.3.10"
+ dompurify "^2.4.0"
echarts "^5.3.2"
iframe-resizer "^4.3.2"
lodash "^4.17.20"
@@ -1083,6 +1083,13 @@
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-20220815034418":
+ version "0.0.1-dev-20220815034418"
+ resolved "https://registry.yarnpkg.com/@gitlab/web-ide/-/web-ide-0.0.1-dev-20220815034418.tgz#c4c4f72d6ffe1ba18fdfc452b30d10ff7cde0550"
+ integrity sha512-cYTMAXfc+B549euU+IDHgFK0Rjwa5SNjs4coBJOD4eoZVvPd3YtHbArqqzmdO71SYjCqV1Bl4v0Jy4UnOJgcjQ==
+ dependencies:
+ mustache "^4.2.0"
+
"@graphql-eslint/eslint-plugin@3.10.7":
version "3.10.7"
resolved "https://registry.yarnpkg.com/@graphql-eslint/eslint-plugin/-/eslint-plugin-3.10.7.tgz#9a203e2084371eca933d88b73ce7a6bebbbb9872"
@@ -1270,6 +1277,11 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz#316b0a63b91c10e53f242efb4ace5c3b34e8728d"
integrity sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==
+"@humanwhocodes/module-importer@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
+ integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
+
"@humanwhocodes/object-schema@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
@@ -2216,19 +2228,20 @@
tsutils "^3.17.1"
"@vue/component-compiler-utils@^3.1.0":
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-3.1.1.tgz#d4ef8f80292674044ad6211e336a302e4d2a6575"
- integrity sha512-+lN3nsfJJDGMNz7fCpcoYIORrXo0K3OTsdr8jCM7FuqdI4+70TY6gxY6viJ2Xi1clqyPg7LpeOWwjF31vSMmUw==
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz#f9f5fb53464b0c37b2c8d2f3fbfe44df60f61dc9"
+ integrity sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==
dependencies:
consolidate "^0.15.1"
hash-sum "^1.0.2"
lru-cache "^4.1.2"
merge-source-map "^1.1.0"
- postcss "^7.0.14"
+ postcss "^7.0.36"
postcss-selector-parser "^6.0.2"
- prettier "^1.18.2"
source-map "~0.6.1"
vue-template-es2015-compiler "^1.9.0"
+ optionalDependencies:
+ prettier "^1.18.2 || ^2.0.0"
"@vue/test-utils@1.3.0":
version "1.3.0"
@@ -2949,9 +2962,9 @@ binaryextensions@2:
integrity sha512-nAihlQsYGyc5Bwq6+EsubvANYGExeJKHDO3RjnvwU042fawQTQfM3Kxn7IHUXQOz4bzfwsGYYHGSvXyW4zOGLg==
bluebird@^3.1.1, bluebird@^3.5.5:
- version "3.5.5"
- resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f"
- integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==
+ version "3.7.2"
+ resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
+ integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.9:
version "4.11.9"
@@ -3013,6 +3026,13 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
+brace-expansion@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+ integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+ dependencies:
+ balanced-match "^1.0.0"
+
braces@^2.3.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
@@ -3420,7 +3440,7 @@ clean-stack@^2.0.0:
resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
-clipboard@^2.0.0, clipboard@^2.0.8:
+clipboard@^2.0.8:
version "2.0.8"
resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.8.tgz#ffc6c103dd2967a83005f3f61976aa4655a4cdba"
integrity sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==
@@ -3557,16 +3577,11 @@ commander@^6.0.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
-commander@^9.4.0:
+commander@^9.4.0, commander@~9.4.0:
version "9.4.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.0.tgz#bc4a40918fefe52e22450c111ecd6b7acce6f11c"
integrity sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==
-commander@~9.0.0:
- version "9.0.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-9.0.0.tgz#86d58f24ee98126568936bd1d3574e0308a99a40"
- integrity sha512-JJfP2saEKbQqvW+FI93OYUB4ByV5cizMpFMiiJI8xDbBvQvSkIk0VvQdn1CZ8mqAO8Loq2h0gYTYtDFUZUeERw==
-
commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -3750,20 +3765,15 @@ 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.24.1:
- version "3.24.1"
- resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.24.1.tgz#cf7724d41724154010a6576b7b57d94c5d66e64f"
- integrity sha512-0QTBSYSUZ6Gq21utGzkfITDylE8jWC9Ne1D2MrhvlsZBI1x39OdDIVbzSqtgMndIy6BlHxBXpMGqzZmnztg2rg==
-
-core-js@~2.3.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.3.0.tgz#fab83fbb0b2d8dc85fa636c4b9d34c75420c6d65"
- integrity sha1-+rg/uwstjchfpjbEudNMdUIMbWU=
+core-js@^3.25.1:
+ version "3.25.1"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.25.1.tgz#5818e09de0db8956e16bf10e2a7141e931b7c69c"
+ integrity sha512-sr0FY4lnO1hkQ4gLDr24K0DGnweGO1QwSj5BpfQjpSJPdqWalja4cTps29Y/PJVG/P7FYlPDkH3hO+Tr0CvDgQ==
core-util-is@~1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
- integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
+ integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
cosmiconfig-toml-loader@1.0.0:
version "1.0.0"
@@ -4831,10 +4841,10 @@ dompurify@2.3.8:
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.8.tgz#224fe9ae57d7ebd9a1ae1ac18c1c1ca3f532226f"
integrity sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw==
-dompurify@^2.3.10:
- version "2.3.10"
- resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.10.tgz#901f7390ffe16a91a5a556b94043314cd4850385"
- integrity sha512-o7Fg/AgC7p/XpKjf/+RC3Ok6k4St5F7Q6q6+Nnm3p2zGWioAY6dh0CbbuwOhH2UcSzKsdniE/YnE2/92JcsA+g==
+dompurify@^2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.0.tgz#c9c88390f024c2823332615c9e20a453cf3825dd"
+ integrity sha512-Be9tbQMZds4a3C6xTmz68NlMfeONA//4dOavl/1rNw50E+/QO0KVpbcU0PcaW0nsQxurXls9ZocqFxk8R2mWEA==
domutils@^2.5.2, domutils@^2.6.0:
version "2.6.0"
@@ -4971,7 +4981,7 @@ enhanced-resolve@^4.5.0:
memory-fs "^0.5.0"
tapable "^1.0.0"
-entities@^2.0.0, entities@~2.1.0:
+entities@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
@@ -5045,11 +5055,6 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1"
is-symbol "^1.0.2"
-es6-promise@~3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6"
- integrity sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=
-
escalade@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
@@ -5188,6 +5193,11 @@ 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-promise@^4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a"
@@ -5284,14 +5294,15 @@ 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.22.0:
- version "8.22.0"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.22.0.tgz#78fcb044196dfa7eef30a9d65944f6f980402c48"
- integrity sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==
+eslint@8.23.1:
+ version "8.23.1"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.23.1.tgz#cfd7b3f7fdd07db8d16b4ac0516a29c8d8dca5dc"
+ integrity sha512-w7C1IXCc6fNqjpuYd0yPlcTKKmHlHHktRkzmBPZ+7cvNBQuiNjx0xaMTjAJGCafJhQkrFJooREv0CtrVzmHwqg==
dependencies:
- "@eslint/eslintrc" "^1.3.0"
+ "@eslint/eslintrc" "^1.3.2"
"@humanwhocodes/config-array" "^0.10.4"
"@humanwhocodes/gitignore-to-minimatch" "^1.0.2"
+ "@humanwhocodes/module-importer" "^1.0.1"
ajv "^6.10.0"
chalk "^4.0.0"
cross-spawn "^7.0.2"
@@ -5301,13 +5312,12 @@ eslint@8.22.0:
eslint-scope "^7.1.1"
eslint-utils "^3.0.0"
eslint-visitor-keys "^3.3.0"
- espree "^9.3.3"
+ espree "^9.4.0"
esquery "^1.4.0"
esutils "^2.0.2"
fast-deep-equal "^3.1.3"
file-entry-cache "^6.0.1"
find-up "^5.0.0"
- functional-red-black-tree "^1.0.1"
glob-parent "^6.0.1"
globals "^13.15.0"
globby "^11.1.0"
@@ -5316,6 +5326,7 @@ eslint@8.22.0:
import-fresh "^3.0.0"
imurmurhash "^0.1.4"
is-glob "^4.0.0"
+ js-sdsl "^4.1.4"
js-yaml "^4.1.0"
json-stable-stringify-without-jsonify "^1.0.1"
levn "^0.4.1"
@@ -5327,12 +5338,11 @@ eslint@8.22.0:
strip-ansi "^6.0.1"
strip-json-comments "^3.1.0"
text-table "^0.2.0"
- v8-compile-cache "^2.0.3"
-espree@^9.3.1, espree@^9.3.2, espree@^9.3.3:
- version "9.3.3"
- resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.3.tgz#2dd37c4162bb05f433ad3c1a52ddf8a49dc08e9d"
- integrity sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==
+espree@^9.3.1, espree@^9.4.0:
+ version "9.4.0"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.0.tgz#cd4bc3d6e9336c433265fc0aa016fc1aaf182f8a"
+ integrity sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==
dependencies:
acorn "^8.8.0"
acorn-jsx "^5.3.2"
@@ -5858,11 +5868,6 @@ function.prototype.name@^1.1.5:
es-abstract "^1.19.0"
functions-have-names "^1.2.2"
-functional-red-black-tree@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
- integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
-
functions-have-names@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
@@ -5964,7 +5969,7 @@ glob-parent@^6.0.1:
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@~7.2.0:
+"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:
version "7.2.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
@@ -5976,6 +5981,17 @@ glob-parent@^6.0.1:
once "^1.3.0"
path-is-absolute "^1.0.0"
+glob@~8.0.3:
+ version "8.0.3"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-8.0.3.tgz#415c6eb2deed9e502c68fa44a272e6da6eeca42e"
+ integrity sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^5.0.1"
+ once "^1.3.0"
+
global-modules@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780"
@@ -6211,7 +6227,7 @@ hash-base@^3.0.0:
hash-sum@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04"
- integrity sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=
+ integrity sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==
hash.js@^1.0.0, hash.js@^1.0.3:
version "1.1.3"
@@ -6512,7 +6528,7 @@ ignore@^5.2.0, ignore@~5.2.0:
immediate@~3.0.5:
version "3.0.6"
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
- integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
+ integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
immer@^9.0.15:
version "9.0.15"
@@ -6593,10 +6609,10 @@ ini@^1.3.4, ini@^1.3.5:
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
-ini@~2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5"
- integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==
+ini@~3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/ini/-/ini-3.0.1.tgz#c76ec81007875bc44d544ff7a11a55d12294102d"
+ integrity sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==
inline-style-parser@0.1.1:
version "0.1.1"
@@ -7466,6 +7482,11 @@ js-cookie@^3.0.0:
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.1.tgz#9e39b4c6c2f56563708d7d31f6f5f21873a92414"
integrity sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==
+js-sdsl@^4.1.4:
+ version "4.1.4"
+ resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.1.4.tgz#78793c90f80e8430b7d8dc94515b6c77d98a26a6"
+ integrity sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw==
+
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@@ -7566,10 +7587,10 @@ json5@^2.1.2, json5@^2.2.1:
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
-jsonc-parser@~3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22"
- integrity sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==
+jsonc-parser@~3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.1.0.tgz#73b8f0e5c940b83d03476bc2e51a20ef0932615d"
+ integrity sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==
jsonfile@^4.0.0:
version "4.0.0"
@@ -7578,21 +7599,15 @@ jsonfile@^4.0.0:
optionalDependencies:
graceful-fs "^4.1.6"
-jszip-utils@^0.0.2:
- version "0.0.2"
- resolved "https://registry.yarnpkg.com/jszip-utils/-/jszip-utils-0.0.2.tgz#457d5cbca60a1c2e0706e9da2b544e8e7bc50bf8"
- integrity sha1-RX1cvKYKHC4HBunaK1ROjnvFC/g=
-
jszip@^3.1.3:
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.1.3.tgz#8a920403b2b1651c0fc126be90192d9080957c37"
- integrity sha1-ipIEA7KxZRwPwSa+kBktkICVfDc=
+ version "3.10.1"
+ resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2"
+ integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==
dependencies:
- core-js "~2.3.0"
- es6-promise "~3.0.2"
- lie "~3.1.0"
+ lie "~3.3.0"
pako "~1.0.2"
- readable-stream "~2.0.6"
+ readable-stream "~2.3.6"
+ setimmediate "^1.0.5"
katex@^0.13.2:
version "0.13.2"
@@ -7673,10 +7688,10 @@ levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
-lie@~3.1.0:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
- integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=
+lie@~3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
+ integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==
dependencies:
immediate "~3.0.5"
@@ -7685,13 +7700,6 @@ lines-and-columns@^1.1.6:
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
-linkify-it@^3.0.1:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.2.tgz#f55eeb8bc1d3ae754049e124ab3bb56d97797fb8"
- integrity sha512-gDBO4aHNZS6coiZCKVhSNh43F9ioIL4JwRjLZPkoLIY4yZFwg264Y5lu2x6rb1Js42Gh6Yqm2f6L2AJcnkzinQ==
- dependencies:
- uc.micro "^1.0.1"
-
linkify-it@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-4.0.1.tgz#01f1d5e508190d06669982ba31a7d9f56a5751ec"
@@ -7994,18 +8002,7 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"
-markdown-it@12.3.2:
- version "12.3.2"
- resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.3.2.tgz#bf92ac92283fe983fe4de8ff8abfb5ad72cd0c90"
- integrity sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==
- dependencies:
- argparse "^2.0.1"
- entities "~2.1.0"
- linkify-it "^3.0.1"
- mdurl "^1.0.1"
- uc.micro "^1.0.5"
-
-markdown-it@^13.0.1:
+markdown-it@13.0.1, markdown-it@^13.0.1:
version "13.0.1"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.1.tgz#c6ecc431cacf1a5da531423fc6a42807814af430"
integrity sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==
@@ -8021,33 +8018,33 @@ markdown-table@^3.0.0:
resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.2.tgz#9b59eb2c1b22fe71954a65ff512887065a7bb57c"
integrity sha512-y8j3a5/DkJCmS5x4dMCQL+OR0+2EAq3DOtio1COSHsmW2BGXnNCK3v12hJt1LrUz5iZH5g0LmuYOjDdI+czghA==
-markdownlint-cli@0.31.0:
- version "0.31.0"
- resolved "https://registry.yarnpkg.com/markdownlint-cli/-/markdownlint-cli-0.31.0.tgz#a44264a71066475228292b7af19d3d18b827676d"
- integrity sha512-UCNA10I2evrEqGWUGM4I6ae6LubLeySkKegP1GQaZSES516BYBgOn8Ai8MXU+5rSIeCvMyKi91alqHyRDuUnYA==
+markdownlint-cli@0.32.2:
+ version "0.32.2"
+ resolved "https://registry.yarnpkg.com/markdownlint-cli/-/markdownlint-cli-0.32.2.tgz#b7b5c5808039aef4022aef603efaa607caf8e0de"
+ integrity sha512-xmJT1rGueUgT4yGNwk6D0oqQr90UJ7nMyakXtqjgswAkEhYYqjHew9RY8wDbOmh2R270IWjuKSeZzHDEGPAUkQ==
dependencies:
- commander "~9.0.0"
+ commander "~9.4.0"
get-stdin "~9.0.0"
- glob "~7.2.0"
+ glob "~8.0.3"
ignore "~5.2.0"
js-yaml "^4.1.0"
- jsonc-parser "~3.0.0"
- markdownlint "~0.25.1"
- markdownlint-rule-helpers "~0.16.0"
- minimatch "~3.0.4"
- run-con "~1.2.10"
+ jsonc-parser "~3.1.0"
+ markdownlint "~0.26.2"
+ markdownlint-rule-helpers "~0.17.2"
+ minimatch "~5.1.0"
+ run-con "~1.2.11"
-markdownlint-rule-helpers@~0.16.0:
- version "0.16.0"
- resolved "https://registry.yarnpkg.com/markdownlint-rule-helpers/-/markdownlint-rule-helpers-0.16.0.tgz#c327f72782bd2b9475127a240508231f0413a25e"
- integrity sha512-oEacRUVeTJ5D5hW1UYd2qExYI0oELdYK72k1TKGvIeYJIbqQWAz476NAc7LNixSySUhcNl++d02DvX0ccDk9/w==
+markdownlint-rule-helpers@~0.17.2:
+ version "0.17.2"
+ resolved "https://registry.yarnpkg.com/markdownlint-rule-helpers/-/markdownlint-rule-helpers-0.17.2.tgz#64d6e8c66e497e631b0e40cf1cef7ca622a0b654"
+ integrity sha512-XaeoW2NYSlWxMCZM2B3H7YTG6nlaLfkEZWMBhr4hSPlq9MuY2sy83+Xr89jXOqZMZYjvi5nBCGoFh7hHoPKZmA==
-markdownlint@~0.25.1:
- version "0.25.1"
- resolved "https://registry.yarnpkg.com/markdownlint/-/markdownlint-0.25.1.tgz#df04536607ebeeda5ccd5e4f38138823ed623788"
- integrity sha512-AG7UkLzNa1fxiOv5B+owPsPhtM4D6DoODhsJgiaNg1xowXovrYgOnLqAgOOFQpWOlHFVQUzjMY5ypNNTeov92g==
+markdownlint@~0.26.2:
+ version "0.26.2"
+ resolved "https://registry.yarnpkg.com/markdownlint/-/markdownlint-0.26.2.tgz#11d3d03e7f0dd3c2e239753ee8fd064a861d9237"
+ integrity sha512-2Am42YX2Ex5SQhRq35HxYWDfz1NLEOZWWN25nqd2h3AHRKsGRE+Qg1gt1++exW792eXTrR4jCNHfShfWk9Nz8w==
dependencies:
- markdown-it "12.3.2"
+ markdown-it "13.0.1"
marked@^4.0.18:
version "4.0.18"
@@ -8696,12 +8693,12 @@ minimatch@^3.0.4, minimatch@^3.1.2:
dependencies:
brace-expansion "^1.1.7"
-minimatch@~3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
- integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+minimatch@^5.0.1, minimatch@~5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7"
+ integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==
dependencies:
- brace-expansion "^1.1.7"
+ brace-expansion "^2.0.1"
minimist-options@4.1.0:
version "4.1.0"
@@ -8897,6 +8894,11 @@ multicast-dns@^7.2.4:
dns-packet "^5.2.2"
thunky "^1.0.2"
+mustache@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64"
+ integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==
+
nanoid@^3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
@@ -9336,9 +9338,9 @@ p-try@^2.0.0:
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
pako@~1.0.2, pako@~1.0.5:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258"
- integrity sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
+ integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
papaparse@^5.3.1:
version "5.3.1"
@@ -9642,7 +9644,7 @@ postcss@8.4.14, postcss@^8.2.1, postcss@^8.4.14:
picocolors "^1.0.0"
source-map-js "^1.0.2"
-postcss@^7.0.14, postcss@^7.0.5, postcss@^7.0.6:
+postcss@^7.0.14, postcss@^7.0.36, postcss@^7.0.5, postcss@^7.0.6:
version "7.0.39"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309"
integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==
@@ -9668,16 +9670,11 @@ pretender@^3.4.3:
fake-xml-http-request "^2.1.1"
route-recognizer "^0.3.3"
-prettier@2.2.1:
+prettier@2.2.1, "prettier@^1.18.2 || ^2.0.0":
version "2.2.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5"
integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==
-prettier@^1.18.2:
- version "1.18.2"
- resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea"
- integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==
-
pretty-format@^26.4.2:
version "26.6.2"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93"
@@ -9706,18 +9703,6 @@ pretty@^2.0.0:
extend-shallow "^2.0.1"
js-beautify "^1.6.12"
-prismjs@^1.21.0:
- version "1.21.0"
- resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.21.0.tgz#36c086ec36b45319ec4218ee164c110f9fc015a3"
- integrity sha512-uGdSIu1nk3kej2iZsLyDoJ7e9bnPzIgY0naW/HdknGj61zScaprVEVGHrPoXqI+M9sP0NDnTK2jpkvmldpuqDw==
- optionalDependencies:
- clipboard "^2.0.0"
-
-process-nextick-args@~1.0.6:
- version "1.0.7"
- resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
- integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=
-
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
@@ -9885,7 +9870,7 @@ prr@~1.0.1:
pseudomap@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
- integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
+ integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==
psl@^1.1.33:
version "1.8.0"
@@ -10082,18 +10067,6 @@ readable-stream@^3.0.6:
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
-readable-stream@~2.0.6:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e"
- integrity sha1-j5A0HmilPMySh4jaz80Rs265t44=
- dependencies:
- core-util-is "~1.0.0"
- inherits "~2.0.1"
- isarray "~1.0.0"
- process-nextick-args "~1.0.6"
- string_decoder "~0.10.x"
- util-deprecate "~1.0.1"
-
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
@@ -10362,14 +10335,14 @@ route-recognizer@^0.3.3:
resolved "https://registry.yarnpkg.com/route-recognizer/-/route-recognizer-0.3.4.tgz#39ab1ffbce1c59e6d2bdca416f0932611e4f3ca3"
integrity sha512-2+MhsfPhvauN1O8KaXpXAOfR/fwe8dnUXVM+xw7yt40lJRfPVQxV6yryZm0cgRvAj5fMF/mdRZbL2ptwbs5i2g==
-run-con@~1.2.10:
- version "1.2.10"
- resolved "https://registry.yarnpkg.com/run-con/-/run-con-1.2.10.tgz#90de9d43d20274d00478f4c000495bd72f417d22"
- integrity sha512-n7PZpYmMM26ZO21dd8y3Yw1TRtGABjRtgPSgFS/nhzfvbJMXFtJhJVyEgayMiP+w/23craJjsnfDvx4W4ue/HQ==
+run-con@~1.2.11:
+ version "1.2.11"
+ resolved "https://registry.yarnpkg.com/run-con/-/run-con-1.2.11.tgz#0014ed430bad034a60568dfe7de2235f32e3f3c4"
+ integrity sha512-NEMGsUT+cglWkzEr4IFK21P4Jca45HqiAbIIZIBdX5+UZTB24Mb/21iNGgz9xZa8tL6vbW7CXmq7MFN42+VjNQ==
dependencies:
deep-extend "^0.6.0"
- ini "~2.0.0"
- minimist "^1.2.5"
+ ini "~3.0.0"
+ minimist "^1.2.6"
strip-json-comments "~3.1.1"
run-parallel@^1.1.9:
@@ -10608,7 +10581,7 @@ set-value@^2.0.0, set-value@^2.0.1:
is-plain-object "^2.0.3"
split-string "^3.0.1"
-setimmediate@^1.0.4:
+setimmediate@^1.0.4, setimmediate@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
@@ -11005,11 +10978,6 @@ string_decoder@^1.0.0, string_decoder@^1.1.1, string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
-string_decoder@~0.10.x:
- version "0.10.31"
- resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
- integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=
-
strip-ansi@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
@@ -11303,20 +11271,10 @@ textextensions@2:
resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.6.0.tgz#d7e4ab13fe54e32e08873be40d51b74229b00fc4"
integrity sha512-49WtAWS+tcsy93dRt6P0P3AMD2m5PvXRhuEA0kaXos5ZLlujtYmpmFsB+QvWUSxE1ZsstmYXfQ7L40+EcQgpAQ==
-three-orbit-controls@^82.1.0:
- version "82.1.0"
- resolved "https://registry.yarnpkg.com/three-orbit-controls/-/three-orbit-controls-82.1.0.tgz#11a7f33d0a20ecec98f098b37780f6537374fab4"
- integrity sha1-EafzPQog7OyY8Jizd4D2U3N0+rQ=
-
-three-stl-loader@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/three-stl-loader/-/three-stl-loader-1.0.4.tgz#6b3319a31e3b910aab1883d19b00c81a663c3e03"
- integrity sha1-azMZox47kQqrGIPRmwDIGmY8PgM=
-
-three@^0.84.0:
- version "0.84.0"
- resolved "https://registry.yarnpkg.com/three/-/three-0.84.0.tgz#95be85a55a0fa002aa625ed559130957dcffd918"
- integrity sha1-lb6FpVoPoAKqYl7VWRMJV9z/2Rg=
+three@^0.143.0:
+ version "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"
@@ -11809,7 +11767,7 @@ use@^3.1.0:
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
- integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
+ integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
util@0.10.3:
version "0.10.3"
@@ -11855,7 +11813,7 @@ uvu@^0.5.0:
kleur "^4.0.3"
sade "^1.7.3"
-v8-compile-cache@^2.0.3, v8-compile-cache@^2.3.0:
+v8-compile-cache@^2.3.0:
version "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==
@@ -11951,11 +11909,11 @@ vue-functional-data-merge@^3.1.0:
integrity sha512-leT4kdJVQyeZNY1kmnS1xiUlQ9z1B/kdBFCILIjYYQDqZgLqCLa0UhjSSeRX6c3mUe6U5qYeM8LrEqkHJ1B4LA==
vue-hot-reload-api@^2.3.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.0.tgz#97976142405d13d8efae154749e88c4e358cf926"
- integrity sha512-2j/t+wIbyVMP5NvctQoSUvLkYKoWAAk2QlQiilrM2a6/ulzFgdcLUJfTvs4XQ/3eZhHiBmmEojbjmM4AzZj8JA==
+ version "2.3.4"
+ resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"
+ integrity sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==
-vue-loader@^15.9.6:
+vue-loader@15.9.6:
version "15.9.6"
resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.9.6.tgz#f4bb9ae20c3a8370af3ecf09b8126d38ffdb6b8b"
integrity sha512-j0cqiLzwbeImIC6nVIby2o/ABAWhlppyL/m5oJ67R5MloP0hj/DtFgb0Zmq3J9CG7AJ+AXIvHVnJAPBvrLyuDg==
@@ -11989,17 +11947,17 @@ vue-runtime-helpers@^1.1.2:
integrity sha512-pZfGp+PW/IXEOyETE09xQHR1CKkR9HfHZdnMD/FVLUNI+HxYTa82evx5WrF6Kz4s82qtqHvMZ8MZpbk2zT2E1Q==
vue-style-loader@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.0.tgz#7588bd778e2c9f8d87bfc3c5a4a039638da7a863"
- integrity sha512-IsSiXDrLW2QIjyBsCqa35e45l5AceMbJ2jO8DxoEQv75xu/UmtXkSC0ybESq/LpbmmIW47MAWDQvErUw+Hrz/A==
+ version "4.1.3"
+ resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz#6d55863a51fa757ab24e89d9371465072aa7bc35"
+ integrity sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==
dependencies:
hash-sum "^1.0.2"
loader-utils "^1.0.2"
-vue-template-compiler@^2.6.12:
- version "2.6.12"
- resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.12.tgz#947ed7196744c8a5285ebe1233fe960437fcc57e"
- integrity sha512-OzzZ52zS41YUbkCBfdXShQTe69j1gQDZ9HIX8miuC9C3rBCk9wIRjLiZZLrmX9V+Ftq/YEyv1JaVr5Y/hNtByg==
+vue-template-compiler@2.6.14:
+ version "2.6.14"
+ resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz#a2f0e7d985670d42c9c9ee0d044fed7690f4f763"
+ integrity sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==
dependencies:
de-indent "^1.0.2"
he "^1.1.0"
@@ -12014,10 +11972,10 @@ vue-virtual-scroll-list@^1.4.7:
resolved "https://registry.yarnpkg.com/vue-virtual-scroll-list/-/vue-virtual-scroll-list-1.4.7.tgz#12ee26833885f5bb4d37dc058085ccf3ce5b5a74"
integrity sha512-R8bk+k7WMGGoFQ9xF0krGCAlZhQjbJOkDUX+YZD2J+sHQWTzDtmTLS6kiIJToOHK1d/8QPGiD8fd9w0lDP4arg==
-vue@^2.6.12:
- version "2.6.12"
- resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.12.tgz#f5ebd4fa6bd2869403e29a896aed4904456c9123"
- integrity sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg==
+vue@2.6.14:
+ version "2.6.14"
+ resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235"
+ integrity sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ==
vuedraggable@^2.23.0:
version "2.23.0"
@@ -12026,10 +11984,10 @@ vuedraggable@^2.23.0:
dependencies:
sortablejs "^1.9.0"
-vuex@^3.6.0:
- version "3.6.0"
- resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.6.0.tgz#95efa56a58f7607c135b053350833a09e01aa813"
- integrity sha512-W74OO2vCJPs9/YjNjW8lLbj+jzT24waTo2KShI8jLvJW8OaIkgb3wuAMA7D+ZiUxDOx3ubwSZTaJBip9G8a3aQ==
+vuex@^3.6.2:
+ version "3.6.2"
+ resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.6.2.tgz#236bc086a870c3ae79946f107f16de59d5895e71"
+ integrity sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==
w3c-hr-time@^1.0.2:
version "1.0.2"
@@ -12117,10 +12075,10 @@ webidl-conversions@^6.1.0:
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514"
integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
-webpack-bundle-analyzer@^4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz#1b0eea2947e73528754a6f9af3e91b2b6e0f79d5"
- integrity sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ==
+webpack-bundle-analyzer@^4.6.1:
+ version "4.6.1"
+ resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.6.1.tgz#bee2ee05f4ba4ed430e4831a319126bb4ed9f5a6"
+ integrity sha512-oKz9Oz9j3rUciLNfpGFjOb49/jEpXNmWdVH8Ls//zNcnLlQdTGXQQMsBbb/gR7Zl8WNLxVCq+0Hqbx3zv6twBw==
dependencies:
acorn "^8.0.4"
acorn-walk "^8.0.0"
@@ -12161,10 +12119,10 @@ webpack-dev-middleware@^5.3.1:
range-parser "^1.2.1"
schema-utils "^4.0.0"
-webpack-dev-server@4.10.0:
- version "4.10.0"
- resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.10.0.tgz#de270d0009eba050546912be90116e7fd740a9ca"
- integrity sha512-7dezwAs+k6yXVFZ+MaL8VnE+APobiO3zvpp3rBHe/HmWQ+avwh0Q3d0xxacOiBybZZ3syTZw9HXzpa3YNbAZDQ==
+webpack-dev-server@4.11.0:
+ version "4.11.0"
+ resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.11.0.tgz#290ee594765cd8260adfe83b2d18115ea04484e7"
+ integrity sha512-L5S4Q2zT57SK7tazgzjMiSMBdsw+rGYIX27MgPgx7LDhWO0lViPrHKoLS7jo5In06PWYAhlYu3PbyoC6yAThbw==
dependencies:
"@types/bonjour" "^3.5.9"
"@types/connect-history-api-fallback" "^1.3.5"
@@ -12432,7 +12390,7 @@ y18n@^5.0.5:
yallist@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
- integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
+ integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==
yallist@^3.0.2:
version "3.0.3"
@@ -12484,10 +12442,10 @@ yarn-check-webpack-plugin@^1.2.0:
dependencies:
chalk "^2.4.2"
-yarn-deduplicate@^5.0.2:
- version "5.0.2"
- resolved "https://registry.yarnpkg.com/yarn-deduplicate/-/yarn-deduplicate-5.0.2.tgz#b56484c94d8f1163a828bf20516607f89c078675"
- integrity sha512-pxKa+dM7DMQ4X2vYLKqGCUgtEoTtdMVk9gNoIsxsMSP0rOV51IWFcKHfRIcZjAPNgHTrxz46sKB4xr7Nte7jdw==
+yarn-deduplicate@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/yarn-deduplicate/-/yarn-deduplicate-6.0.0.tgz#91bc0b7b374efe24796606df2c6b00eabb5aab62"
+ integrity sha512-HjGVvuy10hetOuXeexXXT77V+6FfgS+NiW3FsmQD88yfF2kBqTpChvMglyKUlQ0xXEcI77VJazll5qKKBl3ssw==
dependencies:
"@yarnpkg/lockfile" "^1.1.0"
commander "^9.4.0"